aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorYinghai Lu <yinghai@kernel.org>2012-09-17 22:22:32 -0700
committerYinghai Lu <yinghai@kernel.org>2012-09-17 22:22:32 -0700
commitc186df98f84336422088740ef8cedc1313ca2485 (patch)
treee22dbc466ebf72740239c77221ab8156a6a15e8b
parent537d28686b2bbe82ba1d12b75570510ae6511f2e (diff)
downloadlinux-yinghai-c186df98f84336422088740ef8cedc1313ca2485.tar.gz
PCI, x86: Use acpi_pci_driver instead of pci_driver for ioapic hotplug
We need to have that setup before other really pci drivers. otherwise other pci driver can not enable ioapic routing. Signed-off-by: Yinghai Lu <yinghai@kernel.org>
-rw-r--r--drivers/pci/ioapic.c225
1 files changed, 169 insertions, 56 deletions
diff --git a/drivers/pci/ioapic.c b/drivers/pci/ioapic.c
index 0b2c210f0050a9..504ca93ac69264 100644
--- a/drivers/pci/ioapic.c
+++ b/drivers/pci/ioapic.c
@@ -22,70 +22,128 @@
#include <linux/slab.h>
#include <acpi/acpi_bus.h>
-struct ioapic {
- acpi_handle handle;
+struct acpi_pci_ioapic {
+ acpi_handle root_handle;
u32 gsi_base;
+ struct pci_dev *pdev;
+ struct list_head list;
};
-static int __devinit ioapic_probe(struct pci_dev *dev, const struct pci_device_id *ent)
+static LIST_HEAD(ioapic_list);
+static DEFINE_MUTEX(ioapic_list_lock);
+
+static acpi_status setup_res(struct acpi_resource *acpi_res, void *data)
+{
+ struct resource *res;
+ struct acpi_resource_address64 addr;
+ acpi_status status;
+ unsigned long flags;
+ u64 start, end;
+
+ status = acpi_resource_to_address64(acpi_res, &addr);
+ if (!ACPI_SUCCESS(status))
+ return AE_OK;
+
+ if (addr.resource_type == ACPI_MEMORY_RANGE) {
+ if (addr.info.mem.caching == ACPI_PREFETCHABLE_MEMORY)
+ return AE_OK;
+ flags = IORESOURCE_MEM;
+ } else
+ return AE_OK;
+
+ start = addr.minimum + addr.translation_offset;
+ end = addr.maximum + addr.translation_offset;
+
+ res = data;
+ res->flags = flags;
+ res->start = start;
+ res->end = end;
+
+ return AE_OK;
+}
+
+static void handle_ioapic_add(acpi_handle handle, struct pci_dev **pdev,
+ u32 *pgsi_base)
{
- acpi_handle handle;
acpi_status status;
unsigned long long gsb;
- struct ioapic *ioapic;
+ struct pci_dev *dev;
+ u32 gsi_base;
int ret;
char *type;
- struct resource *res;
+ struct resource r;
+ struct resource *res = &r;
+ char objname[64];
+ struct acpi_buffer buffer = {sizeof(objname), objname};
- handle = DEVICE_ACPI_HANDLE(&dev->dev);
- if (!handle)
- return -EINVAL;
+ *pdev = NULL;
+ *pgsi_base = 0;
status = acpi_evaluate_integer(handle, "_GSB", NULL, &gsb);
- if (ACPI_FAILURE(status))
- return -EINVAL;
+ if (ACPI_FAILURE(status) || !gsb)
+ return;
+
+ dev = acpi_get_pci_dev(handle);
+ if (!dev) {
+ struct acpi_device_info *info;
+ char *hid = NULL;
+
+ status = acpi_get_object_info(handle, &info);
+ if (ACPI_FAILURE(status))
+ return;
+ if (info->valid & ACPI_VALID_HID)
+ hid = info->hardware_id.string;
+ if (!hid || strcmp(hid, "ACPI0009")) {
+ kfree(info);
+ return;
+ }
+ kfree(info);
+ memset(res, 0, sizeof(*res));
+ acpi_walk_resources(handle, METHOD_NAME__CRS, setup_res, res);
+ if (!res->flags)
+ return;
+ }
- /*
- * 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.
- */
+ acpi_get_name(handle, ACPI_FULL_PATHNAME, &buffer);
- ioapic = kzalloc(sizeof(*ioapic), GFP_KERNEL);
- if (!ioapic)
- return -ENOMEM;
+ gsi_base = gsb;
+ type = "IOxAPIC";
+ if (dev) {
+ ret = pci_enable_device(dev);
+ if (ret < 0)
+ goto exit_put;
- ioapic->handle = handle;
- ioapic->gsi_base = (u32) gsb;
+ pci_set_master(dev);
- if (dev->class == PCI_CLASS_SYSTEM_PIC_IOAPIC)
- type = "IOAPIC";
- else
- type = "IOxAPIC";
+ if (dev->class == PCI_CLASS_SYSTEM_PIC_IOAPIC)
+ type = "IOAPIC";
- ret = pci_enable_device(dev);
- if (ret < 0)
- goto exit_free;
+ if (pci_request_region(dev, 0, type))
+ goto exit_disable;
- pci_set_master(dev);
+ res = &dev->resource[0];
+ }
- if (pci_request_region(dev, 0, type))
- goto exit_disable;
+ if (acpi_register_ioapic(handle, res->start, gsi_base)) {
+ if (dev)
+ goto exit_release;
+ return;
+ }
- res = &dev->resource[0];
- if (acpi_register_ioapic(ioapic->handle, res->start, ioapic->gsi_base))
- goto exit_release;
+ printk(KERN_INFO "%s %s %s at %pR, GSI %u\n",
+ dev ? dev_name(&dev->dev) : "", objname, type,
+ res, gsi_base);
- pci_set_drvdata(dev, ioapic);
- dev_info(&dev->dev, "%s at %pR, GSI %u\n", type, res, ioapic->gsi_base);
- return 0;
+ *pdev = dev;
+ *pgsi_base = gsi_base;
+ return;
exit_release:
pci_release_region(dev, 0);
exit_disable:
pci_disable_device(dev);
-exit_free:
- kfree(ioapic);
- return -ENODEV;
+exit_put:
+ pci_dev_put(dev);
}
static void pci_disable_device_mem(struct pci_dev *dev)
@@ -99,36 +157,91 @@ static void pci_disable_device_mem(struct pci_dev *dev)
}
}
-static void __devexit ioapic_remove(struct pci_dev *dev)
+static void handle_ioapic_remove(acpi_handle handle, struct pci_dev *dev,
+ u32 gsi_base)
{
- struct ioapic *ioapic = pci_get_drvdata(dev);
+ acpi_unregister_ioapic(handle, gsi_base);
+
+ if (!dev)
+ return;
- acpi_unregister_ioapic(ioapic->handle, ioapic->gsi_base);
pci_release_region(dev, 0);
pci_disable_device(dev);
/* need to disable it, otherwise remove/rescan will not work */
pci_disable_device_mem(dev);
- kfree(ioapic);
+ pci_dev_put(dev);
+}
+
+static acpi_status register_ioapic(acpi_handle handle, u32 lvl,
+ void *context, void **rv)
+{
+ acpi_handle root_handle = context;
+ struct pci_dev *pdev;
+ u32 gsi_base;
+ struct acpi_pci_ioapic *ioapic;
+
+ handle_ioapic_add(handle, &pdev, &gsi_base);
+ if (!gsi_base)
+ return AE_OK;
+
+ ioapic = kzalloc(sizeof(*ioapic), GFP_KERNEL);
+ if (!ioapic) {
+ printk(KERN_ERR "%s: cannot allocate memory\n", __func__);
+ handle_ioapic_remove(root_handle, pdev, gsi_base);
+ return AE_OK;
+ }
+ ioapic->root_handle = root_handle;
+ ioapic->pdev = pdev;
+ ioapic->gsi_base = gsi_base;
+
+ mutex_lock(&ioapic_list_lock);
+ list_add(&ioapic->list, &ioapic_list);
+ mutex_unlock(&ioapic_list_lock);
+
+ return AE_OK;
}
+static int acpi_pci_ioapic_add(struct acpi_pci_root *root)
+{
+ acpi_status status;
+
+ status = acpi_walk_namespace(ACPI_TYPE_DEVICE, root->device->handle,
+ (u32)1, register_ioapic, NULL,
+ root->device->handle,
+ NULL);
+ if (ACPI_FAILURE(status))
+ printk(KERN_ERR "%s: register_ioapic failure - %d", __func__,
+ status);
-static DEFINE_PCI_DEVICE_TABLE(ioapic_devices) = {
- { PCI_DEVICE_CLASS(PCI_CLASS_SYSTEM_PIC_IOAPIC, ~0) },
- { PCI_DEVICE_CLASS(PCI_CLASS_SYSTEM_PIC_IOXAPIC, ~0) },
- { }
-};
-MODULE_DEVICE_TABLE(pci, ioapic_devices);
+ return status;
+}
+
+static void acpi_pci_ioapic_remove(struct acpi_pci_root *root)
+{
+ struct acpi_pci_ioapic *ioapic, *tmp;
+
+ mutex_lock(&ioapic_list_lock);
+ list_for_each_entry_safe(ioapic, tmp, &ioapic_list, list) {
+ if (root->device->handle != ioapic->root_handle)
+ continue;
+ list_del(&ioapic->list);
+ handle_ioapic_remove(ioapic->root_handle, ioapic->pdev,
+ ioapic->gsi_base);
+ kfree(ioapic);
+ }
+ mutex_unlock(&ioapic_list_lock);
+}
-static struct pci_driver ioapic_driver = {
- .name = "ioapic",
- .id_table = ioapic_devices,
- .probe = ioapic_probe,
- .remove = __devexit_p(ioapic_remove),
+static struct acpi_pci_driver acpi_pci_ioapic_driver = {
+ .add = acpi_pci_ioapic_add,
+ .remove = acpi_pci_ioapic_remove,
};
static int __init ioapic_init(void)
{
- return pci_register_driver(&ioapic_driver);
+ acpi_pci_register_driver(&acpi_pci_ioapic_driver);
+
+ return 0;
}
-module_init(ioapic_init);
+subsys_initcall(ioapic_init);