We need to have ioapic setup before normal pci drivers.
otherwise other pci driver can not setup irq.

Make ioapic built-in, so can call add/remove during host-bridge add/remove
the same as the booting path.

Also need to make it depends on X86_IO_APIC.

Signed-off-by: <ying...@kernel.org>
---
 arch/x86/kernel/acpi/boot.c |   10 +-
 drivers/acpi/pci_root.c     |    4 +
 drivers/pci/Kconfig         |    3 +-
 drivers/pci/ioapic.c        |  216 ++++++++++++++++++++++++++++++-------------
 include/linux/pci-acpi.h    |    8 ++
 5 files changed, 172 insertions(+), 69 deletions(-)

diff --git a/arch/x86/kernel/acpi/boot.c b/arch/x86/kernel/acpi/boot.c
index 350879f..338163b 100644
--- a/arch/x86/kernel/acpi/boot.c
+++ b/arch/x86/kernel/acpi/boot.c
@@ -697,16 +697,18 @@ EXPORT_SYMBOL(acpi_unmap_lsapic);
 
 int acpi_register_ioapic(acpi_handle handle, u64 phys_addr, u32 gsi_base)
 {
-       /* TBD */
-       return -EINVAL;
+       unsigned long long id = 0;
+
+       acpi_evaluate_integer(handle, "_UID", NULL, &id);
+
+       return  __mp_register_ioapic(id, phys_addr, gsi_base, true);
 }
 
 EXPORT_SYMBOL(acpi_register_ioapic);
 
 int acpi_unregister_ioapic(acpi_handle handle, u32 gsi_base)
 {
-       /* TBD */
-       return -EINVAL;
+       return mp_unregister_ioapic(gsi_base);
 }
 
 EXPORT_SYMBOL(acpi_unregister_ioapic);
diff --git a/drivers/acpi/pci_root.c b/drivers/acpi/pci_root.c
index 417487a..54d61ce 100644
--- a/drivers/acpi/pci_root.c
+++ b/drivers/acpi/pci_root.c
@@ -587,6 +587,8 @@ static int acpi_pci_root_add(struct acpi_device *device)
                pci_assign_unassigned_bus_resources(root->bus);
        }
 
+       acpi_pci_ioapic_add(root);
+
        mutex_lock(&acpi_pci_root_lock);
        list_for_each_entry(driver, &acpi_pci_drivers, node)
                if (driver->add)
@@ -626,6 +628,8 @@ static int acpi_pci_root_remove(struct acpi_device *device, 
int type)
                        driver->remove(root);
        mutex_unlock(&acpi_pci_root_lock);
 
+       acpi_pci_ioapic_remove(root);
+
        device_set_run_wake(root->bus->bridge, false);
        pci_acpi_remove_bus_pm_notifier(device);
 
diff --git a/drivers/pci/Kconfig b/drivers/pci/Kconfig
index 6d51aa6..720989f 100644
--- a/drivers/pci/Kconfig
+++ b/drivers/pci/Kconfig
@@ -110,10 +110,11 @@ config PCI_PASID
          If unsure, say N.
 
 config PCI_IOAPIC
-       tristate "PCI IO-APIC hotplug support" if X86
+       bool "PCI IO-APIC hotplug support" if X86
        depends on PCI
        depends on ACPI
        depends on HOTPLUG
+       depends on X86_IO_APIC
        default !X86
 
 config PCI_LABEL
diff --git a/drivers/pci/ioapic.c b/drivers/pci/ioapic.c
index 8dacfd0..12e56c7 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 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;
+       pr_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,44 +157,74 @@ static void pci_disable_device_mem(struct pci_dev *dev)
        }
 }
 
-static void 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;
 
-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);
+       handle_ioapic_add(handle, &pdev, &gsi_base);
+       if (!gsi_base)
+               return AE_OK;
 
-static struct pci_driver ioapic_driver = {
-       .name           = "ioapic",
-       .id_table       = ioapic_devices,
-       .probe          = ioapic_probe,
-       .remove         = ioapic_remove,
-};
+       ioapic = kzalloc(sizeof(*ioapic), GFP_KERNEL);
+       if (!ioapic) {
+               pr_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;
 
-static int __init ioapic_init(void)
-{
-       return pci_register_driver(&ioapic_driver);
+       mutex_lock(&ioapic_list_lock);
+       list_add(&ioapic->list, &ioapic_list);
+       mutex_unlock(&ioapic_list_lock);
+
+       return AE_OK;
 }
 
-static void __exit ioapic_exit(void)
+void acpi_pci_ioapic_add(struct acpi_pci_root *root)
 {
-       pci_unregister_driver(&ioapic_driver);
-}
+       acpi_status status;
 
-module_init(ioapic_init);
-module_exit(ioapic_exit);
+       status = acpi_walk_namespace(ACPI_TYPE_DEVICE, root->device->handle,
+                                    (u32)1, register_ioapic, NULL,
+                                    root->device->handle,
+                                    NULL);
+       if (ACPI_FAILURE(status))
+               pr_err("%s: register_ioapic failure - %d", __func__, status);
+}
 
-MODULE_LICENSE("GPL");
+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);
+}
diff --git a/include/linux/pci-acpi.h b/include/linux/pci-acpi.h
index 9a22b5e..6f83039 100644
--- a/include/linux/pci-acpi.h
+++ b/include/linux/pci-acpi.h
@@ -49,4 +49,12 @@ extern bool aer_acpi_firmware_first(void);
 static inline bool aer_acpi_firmware_first(void) { return false; }
 #endif
 
+#ifdef CONFIG_PCI_IOAPIC
+void acpi_pci_ioapic_add(struct acpi_pci_root *root);
+void acpi_pci_ioapic_remove(struct acpi_pci_root *root);
+#else
+static inline void acpi_pci_ioapic_add(struct acpi_pci_root *root) { }
+static inline void acpi_pci_ioapic_remove(struct acpi_pci_root *root) { }
+#endif
+
 #endif /* _PCI_ACPI_H_ */
-- 
1.7.10.4

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

Reply via email to