diff options
author | Yinghai Lu <yinghai@kernel.org> | 2012-09-17 22:22:32 -0700 |
---|---|---|
committer | Yinghai Lu <yinghai@kernel.org> | 2012-09-17 22:22:32 -0700 |
commit | c186df98f84336422088740ef8cedc1313ca2485 (patch) | |
tree | e22dbc466ebf72740239c77221ab8156a6a15e8b | |
parent | 537d28686b2bbe82ba1d12b75570510ae6511f2e (diff) | |
download | linux-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.c | 225 |
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); |