Skip to content

Commit

Permalink
---
Browse files Browse the repository at this point in the history
yaml
---
r: 174767
b: refs/heads/master
c: 204d49a
h: refs/heads/master
i:
  174765: 8726ddb
  174763: 96ee37b
  174759: 40edb70
  174751: 11e9743
v: v3
  • Loading branch information
Bjorn Helgaas authored and Jesse Barnes committed Nov 4, 2009
1 parent b68d42f commit c8341a4
Show file tree
Hide file tree
Showing 6 changed files with 137 additions and 197 deletions.
2 changes: 1 addition & 1 deletion [refs]
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
---
refs/heads/master: 476f644edf7c22b47e6a118e4a1e138112a5ef14
refs/heads/master: 204d49a5613a06eb2fa5c3b842a29b1336cc7995
7 changes: 7 additions & 0 deletions trunk/drivers/pci/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -69,3 +69,10 @@ config PCI_IOV
physical resources.

If unsure, say N.

config PCI_IOAPIC
bool
depends on PCI
depends on ACPI
depends on HOTPLUG
default y
2 changes: 2 additions & 0 deletions trunk/drivers/pci/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ CFLAGS_legacy.o += -Wno-deprecated-declarations
# Build PCI Express stuff if needed
obj-$(CONFIG_PCIEPORTBUS) += pcie/

obj-$(CONFIG_PCI_IOAPIC) += ioapic.o

obj-$(CONFIG_HOTPLUG) += hotplug.o

# Build the PCI Hotplug drivers if we were asked to
Expand Down
6 changes: 0 additions & 6 deletions trunk/drivers/pci/hotplug/acpiphp.h
Original file line number Diff line number Diff line change
Expand Up @@ -146,12 +146,6 @@ struct acpiphp_attention_info
struct module *owner;
};

struct acpiphp_ioapic {
struct pci_dev *dev;
u32 gsi_base;
struct list_head list;
};

/* PCI bus bridge HID */
#define ACPI_PCI_HOST_HID "PNP0A03"

Expand Down
190 changes: 0 additions & 190 deletions trunk/drivers/pci/hotplug/acpiphp_glue.c
Original file line number Diff line number Diff line change
Expand Up @@ -52,8 +52,6 @@
#include "acpiphp.h"

static LIST_HEAD(bridge_list);
static LIST_HEAD(ioapic_list);
static DEFINE_SPINLOCK(ioapic_list_lock);

#define MY_NAME "acpiphp_glue"

Expand Down Expand Up @@ -606,190 +604,6 @@ static void remove_bridge(acpi_handle handle)
handle_hotplug_event_bridge);
}

static struct pci_dev * get_apic_pci_info(acpi_handle handle)
{
struct pci_dev *dev;

dev = acpi_get_pci_dev(handle);
if (!dev)
return NULL;

if ((dev->class != PCI_CLASS_SYSTEM_PIC_IOAPIC) &&
(dev->class != PCI_CLASS_SYSTEM_PIC_IOXAPIC))
{
pci_dev_put(dev);
return NULL;
}

return dev;
}

static int get_gsi_base(acpi_handle handle, u32 *gsi_base)
{
acpi_status status;
int result = -1;
unsigned long long gsb;
struct acpi_buffer buffer = {ACPI_ALLOCATE_BUFFER, NULL};
union acpi_object *obj;
void *table;

status = acpi_evaluate_integer(handle, "_GSB", NULL, &gsb);
if (ACPI_SUCCESS(status)) {
*gsi_base = (u32)gsb;
return 0;
}

status = acpi_evaluate_object(handle, "_MAT", NULL, &buffer);
if (ACPI_FAILURE(status) || !buffer.length || !buffer.pointer)
return -1;

obj = buffer.pointer;
if (obj->type != ACPI_TYPE_BUFFER)
goto out;

table = obj->buffer.pointer;
switch (((struct acpi_subtable_header *)table)->type) {
case ACPI_MADT_TYPE_IO_SAPIC:
*gsi_base = ((struct acpi_madt_io_sapic *)table)->global_irq_base;
result = 0;
break;
case ACPI_MADT_TYPE_IO_APIC:
*gsi_base = ((struct acpi_madt_io_apic *)table)->global_irq_base;
result = 0;
break;
default:
break;
}
out:
kfree(buffer.pointer);
return result;
}

static acpi_status
ioapic_add(acpi_handle handle, u32 lvl, void *context, void **rv)
{
acpi_status status;
unsigned long long sta;
acpi_handle tmp;
struct pci_dev *pdev;
u32 gsi_base;
u64 phys_addr;
struct acpiphp_ioapic *ioapic;

/* Evaluate _STA if present */
status = acpi_evaluate_integer(handle, "_STA", NULL, &sta);
if (ACPI_SUCCESS(status) && sta != ACPI_STA_ALL)
return AE_CTRL_DEPTH;

/* Scan only PCI bus scope */
status = acpi_get_handle(handle, "_HID", &tmp);
if (ACPI_SUCCESS(status))
return AE_CTRL_DEPTH;

if (get_gsi_base(handle, &gsi_base))
return AE_OK;

ioapic = kmalloc(sizeof(*ioapic), GFP_KERNEL);
if (!ioapic)
return AE_NO_MEMORY;

pdev = get_apic_pci_info(handle);
if (!pdev)
goto exit_kfree;

if (pci_enable_device(pdev))
goto exit_pci_dev_put;

pci_set_master(pdev);

if (pci_request_region(pdev, 0, "I/O APIC(acpiphp)"))
goto exit_pci_disable_device;

phys_addr = pci_resource_start(pdev, 0);
if (acpi_register_ioapic(handle, phys_addr, gsi_base))
goto exit_pci_release_region;

ioapic->gsi_base = gsi_base;
ioapic->dev = pdev;
spin_lock(&ioapic_list_lock);
list_add_tail(&ioapic->list, &ioapic_list);
spin_unlock(&ioapic_list_lock);

return AE_OK;

exit_pci_release_region:
pci_release_region(pdev, 0);
exit_pci_disable_device:
pci_disable_device(pdev);
exit_pci_dev_put:
pci_dev_put(pdev);
exit_kfree:
kfree(ioapic);

return AE_OK;
}

static acpi_status
ioapic_remove(acpi_handle handle, u32 lvl, void *context, void **rv)
{
acpi_status status;
unsigned long long sta;
acpi_handle tmp;
u32 gsi_base;
struct acpiphp_ioapic *pos, *n, *ioapic = NULL;

/* Evaluate _STA if present */
status = acpi_evaluate_integer(handle, "_STA", NULL, &sta);
if (ACPI_SUCCESS(status) && sta != ACPI_STA_ALL)
return AE_CTRL_DEPTH;

/* Scan only PCI bus scope */
status = acpi_get_handle(handle, "_HID", &tmp);
if (ACPI_SUCCESS(status))
return AE_CTRL_DEPTH;

if (get_gsi_base(handle, &gsi_base))
return AE_OK;

acpi_unregister_ioapic(handle, gsi_base);

spin_lock(&ioapic_list_lock);
list_for_each_entry_safe(pos, n, &ioapic_list, list) {
if (pos->gsi_base != gsi_base)
continue;
ioapic = pos;
list_del(&ioapic->list);
break;
}
spin_unlock(&ioapic_list_lock);

if (!ioapic)
return AE_OK;

pci_release_region(ioapic->dev, 0);
pci_disable_device(ioapic->dev);
pci_dev_put(ioapic->dev);
kfree(ioapic);

return AE_OK;
}

static int acpiphp_configure_ioapics(acpi_handle handle)
{
ioapic_add(handle, 0, NULL, NULL);
acpi_walk_namespace(ACPI_TYPE_DEVICE, handle,
ACPI_UINT32_MAX, ioapic_add, NULL, NULL);
return 0;
}

static int acpiphp_unconfigure_ioapics(acpi_handle handle)
{
ioapic_remove(handle, 0, NULL, NULL);
acpi_walk_namespace(ACPI_TYPE_DEVICE, handle,
ACPI_UINT32_MAX, ioapic_remove, NULL, NULL);
return 0;
}

static int power_on_slot(struct acpiphp_slot *slot)
{
acpi_status status;
Expand Down Expand Up @@ -1014,8 +828,6 @@ static int __ref enable_device(struct acpiphp_slot *slot)
pci_bus_assign_resources(bus);
acpiphp_sanitize_bus(bus);
acpiphp_set_hpp_values(bus);
list_for_each_entry(func, &slot->funcs, sibling)
acpiphp_configure_ioapics(func->handle);
pci_enable_bridges(bus);
pci_bus_add_devices(bus);

Expand Down Expand Up @@ -1091,7 +903,6 @@ static int disable_device(struct acpiphp_slot *slot)
}

list_for_each_entry(func, &slot->funcs, sibling) {
acpiphp_unconfigure_ioapics(func->handle);
acpiphp_bus_trim(func->handle);
}

Expand Down Expand Up @@ -1275,7 +1086,6 @@ static int acpiphp_configure_bridge (acpi_handle handle)
acpiphp_sanitize_bus(bus);
acpiphp_set_hpp_values(bus);
pci_enable_bridges(bus);
acpiphp_configure_ioapics(handle);
return 0;
}

Expand Down
127 changes: 127 additions & 0 deletions trunk/drivers/pci/ioapic.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
/*
* IOAPIC/IOxAPIC/IOSAPIC driver
*
* Copyright (C) 2009 Fujitsu Limited.
* (c) Copyright 2009 Hewlett-Packard Development Company, L.P.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/

/*
* This driver manages PCI I/O APICs added by hotplug after boot. We try to
* claim all I/O APIC PCI devices, but those present at boot were registered
* when we parsed the ACPI MADT, so we'll fail when we try to re-register
* them.
*/

#include <linux/pci.h>
#include <linux/acpi.h>
#include <acpi/acpi_bus.h>

struct ioapic {
acpi_handle handle;
u32 gsi_base;
};

static int ioapic_probe(struct pci_dev *dev, const struct pci_device_id *ent)
{
acpi_handle handle;
acpi_status status;
unsigned long long gsb;
struct ioapic *ioapic;
u64 addr;
int ret;
char *type;

handle = DEVICE_ACPI_HANDLE(&dev->dev);
if (!handle)
return -EINVAL;

status = acpi_evaluate_integer(handle, "_GSB", NULL, &gsb);
if (ACPI_FAILURE(status))
return -EINVAL;

/*
* The previous code in acpiphp evaluated _MAT if _GSB failed, but
* ACPI spec 4.0 sec 6.2.2 requires _GSB for hot-pluggable I/O APICs.
*/

ioapic = kzalloc(sizeof(*ioapic), GFP_KERNEL);
if (!ioapic)
return -ENOMEM;

ioapic->handle = handle;
ioapic->gsi_base = (u32) gsb;

if (dev->class == PCI_CLASS_SYSTEM_PIC_IOAPIC)
type = "IOAPIC";
else
type = "IOxAPIC";

ret = pci_enable_device(dev);
if (ret < 0)
goto exit_free;

pci_set_master(dev);

if (pci_request_region(dev, 0, type))
goto exit_disable;

addr = pci_resource_start(dev, 0);
if (acpi_register_ioapic(ioapic->handle, addr, ioapic->gsi_base))
goto exit_release;

pci_set_drvdata(dev, ioapic);
dev_info(&dev->dev, "%s at %#llx, GSI %u\n", type, addr,
ioapic->gsi_base);
return 0;

exit_release:
pci_release_region(dev, 0);
exit_disable:
pci_disable_device(dev);
exit_free:
kfree(ioapic);
return -ENODEV;
}

static void ioapic_remove(struct pci_dev *dev)
{
struct ioapic *ioapic = pci_get_drvdata(dev);

acpi_unregister_ioapic(ioapic->handle, ioapic->gsi_base);
pci_release_region(dev, 0);
pci_disable_device(dev);
kfree(ioapic);
}


static struct pci_device_id ioapic_devices[] = {
{ PCI_ANY_ID, PCI_ANY_ID, PCI_ANY_ID, PCI_ANY_ID,
PCI_CLASS_SYSTEM_PIC_IOAPIC << 8, 0xffff00, },
{ PCI_ANY_ID, PCI_ANY_ID, PCI_ANY_ID, PCI_ANY_ID,
PCI_CLASS_SYSTEM_PIC_IOXAPIC << 8, 0xffff00, },
{ }
};

static struct pci_driver ioapic_driver = {
.name = "ioapic",
.id_table = ioapic_devices,
.probe = ioapic_probe,
.remove = __devexit_p(ioapic_remove),
};

static int __init ioapic_init(void)
{
return pci_register_driver(&ioapic_driver);
}

static void __exit ioapic_exit(void)
{
pci_unregister_driver(&ioapic_driver);
}

module_init(ioapic_init);
module_exit(ioapic_exit);

0 comments on commit c8341a4

Please sign in to comment.