diff options
| author | Jiang Liu <jiang.liu@linux.intel.com> | 2015-02-05 00:44:49 -0500 |
|---|---|---|
| committer | Rafael J. Wysocki <rafael.j.wysocki@intel.com> | 2015-02-05 09:09:26 -0500 |
| commit | c183619b63ec934110e3a173a34b414e26869f96 (patch) | |
| tree | 4793a52eeb88bf69aa39189fdf73642113440a90 | |
| parent | ecf5636dcd59cd5508641f995cc4c2bafedbb995 (diff) | |
x86/irq, ACPI: Implement ACPI driver to support IOAPIC hotplug
Enable support of IOAPIC hotplug by:
1) reintroducing ACPI based IOAPIC driver
2) enhance pci_root driver to hook hotplug events
The ACPI IOAPIC driver is always enabled if all of ACPI, PCI and IOAPIC
are enabled.
Signed-off-by: Jiang Liu <jiang.liu@linux.intel.com>
Cc: Konrad Rzeszutek Wilk <konrad.wilk@oracle.com>
Cc: Tony Luck <tony.luck@intel.com>
Cc: Joerg Roedel <joro@8bytes.org>
Cc: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Cc: Benjamin Herrenschmidt <benh@kernel.crashing.org>
Cc: Rafael J. Wysocki <rjw@rjwysocki.net>
Cc: Bjorn Helgaas <bhelgaas@google.com>
Cc: Randy Dunlap <rdunlap@infradead.org>
Cc: Yinghai Lu <yinghai@kernel.org>
Cc: Borislav Petkov <bp@alien8.de>
Cc: Len Brown <lenb@kernel.org>
Link: http://lkml.kernel.org/r/1414387308-27148-19-git-send-email-jiang.liu@linux.intel.com
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
| -rw-r--r-- | drivers/acpi/Kconfig | 6 | ||||
| -rw-r--r-- | drivers/acpi/Makefile | 1 | ||||
| -rw-r--r-- | drivers/acpi/internal.h | 7 | ||||
| -rw-r--r-- | drivers/acpi/ioapic.c | 229 | ||||
| -rw-r--r-- | drivers/acpi/pci_root.c | 3 |
5 files changed, 246 insertions, 0 deletions
diff --git a/drivers/acpi/Kconfig b/drivers/acpi/Kconfig index 8951cefb0a96..e6c3ddd92665 100644 --- a/drivers/acpi/Kconfig +++ b/drivers/acpi/Kconfig | |||
| @@ -315,6 +315,12 @@ config ACPI_HOTPLUG_MEMORY | |||
| 315 | To compile this driver as a module, choose M here: | 315 | To compile this driver as a module, choose M here: |
| 316 | the module will be called acpi_memhotplug. | 316 | the module will be called acpi_memhotplug. |
| 317 | 317 | ||
| 318 | config ACPI_HOTPLUG_IOAPIC | ||
| 319 | bool | ||
| 320 | depends on PCI | ||
| 321 | depends on X86_IO_APIC | ||
| 322 | default y | ||
| 323 | |||
| 318 | config ACPI_SBS | 324 | config ACPI_SBS |
| 319 | tristate "Smart Battery System" | 325 | tristate "Smart Battery System" |
| 320 | depends on X86 | 326 | depends on X86 |
diff --git a/drivers/acpi/Makefile b/drivers/acpi/Makefile index f74317cc1ca9..e59494c90402 100644 --- a/drivers/acpi/Makefile +++ b/drivers/acpi/Makefile | |||
| @@ -70,6 +70,7 @@ obj-$(CONFIG_ACPI_PROCESSOR) += processor.o | |||
| 70 | obj-y += container.o | 70 | obj-y += container.o |
| 71 | obj-$(CONFIG_ACPI_THERMAL) += thermal.o | 71 | obj-$(CONFIG_ACPI_THERMAL) += thermal.o |
| 72 | obj-y += acpi_memhotplug.o | 72 | obj-y += acpi_memhotplug.o |
| 73 | obj-$(CONFIG_ACPI_HOTPLUG_IOAPIC) += ioapic.o | ||
| 73 | obj-$(CONFIG_ACPI_BATTERY) += battery.o | 74 | obj-$(CONFIG_ACPI_BATTERY) += battery.o |
| 74 | obj-$(CONFIG_ACPI_SBS) += sbshc.o | 75 | obj-$(CONFIG_ACPI_SBS) += sbshc.o |
| 75 | obj-$(CONFIG_ACPI_SBS) += sbs.o | 76 | obj-$(CONFIG_ACPI_SBS) += sbs.o |
diff --git a/drivers/acpi/internal.h b/drivers/acpi/internal.h index 163e82f536fa..caca2805536d 100644 --- a/drivers/acpi/internal.h +++ b/drivers/acpi/internal.h | |||
| @@ -35,6 +35,13 @@ void acpi_int340x_thermal_init(void); | |||
| 35 | int acpi_sysfs_init(void); | 35 | int acpi_sysfs_init(void); |
| 36 | void acpi_container_init(void); | 36 | void acpi_container_init(void); |
| 37 | void acpi_memory_hotplug_init(void); | 37 | void acpi_memory_hotplug_init(void); |
| 38 | #ifdef CONFIG_ACPI_HOTPLUG_IOAPIC | ||
| 39 | int acpi_ioapic_add(struct acpi_pci_root *root); | ||
| 40 | int acpi_ioapic_remove(struct acpi_pci_root *root); | ||
| 41 | #else | ||
| 42 | static inline int acpi_ioapic_add(struct acpi_pci_root *root) { return 0; } | ||
| 43 | static inline int acpi_ioapic_remove(struct acpi_pci_root *root) { return 0; } | ||
| 44 | #endif | ||
| 38 | #ifdef CONFIG_ACPI_DOCK | 45 | #ifdef CONFIG_ACPI_DOCK |
| 39 | void register_dock_dependent_device(struct acpi_device *adev, | 46 | void register_dock_dependent_device(struct acpi_device *adev, |
| 40 | acpi_handle dshandle); | 47 | acpi_handle dshandle); |
diff --git a/drivers/acpi/ioapic.c b/drivers/acpi/ioapic.c new file mode 100644 index 000000000000..ccdc8db16bb8 --- /dev/null +++ b/drivers/acpi/ioapic.c | |||
| @@ -0,0 +1,229 @@ | |||
| 1 | /* | ||
| 2 | * IOAPIC/IOxAPIC/IOSAPIC driver | ||
| 3 | * | ||
| 4 | * Copyright (C) 2009 Fujitsu Limited. | ||
| 5 | * (c) Copyright 2009 Hewlett-Packard Development Company, L.P. | ||
| 6 | * | ||
| 7 | * Copyright (C) 2014 Intel Corporation | ||
| 8 | * | ||
| 9 | * This program is free software; you can redistribute it and/or modify | ||
| 10 | * it under the terms of the GNU General Public License version 2 as | ||
| 11 | * published by the Free Software Foundation. | ||
| 12 | * | ||
| 13 | * Based on original drivers/pci/ioapic.c | ||
| 14 | * Yinghai Lu <yinghai@kernel.org> | ||
| 15 | * Jiang Liu <jiang.liu@intel.com> | ||
| 16 | */ | ||
| 17 | |||
| 18 | /* | ||
| 19 | * This driver manages I/O APICs added by hotplug after boot. | ||
| 20 | * We try to claim all I/O APIC devices, but those present at boot were | ||
| 21 | * registered when we parsed the ACPI MADT. | ||
| 22 | */ | ||
| 23 | |||
| 24 | #define pr_fmt(fmt) "ACPI : IOAPIC: " fmt | ||
| 25 | |||
| 26 | #include <linux/slab.h> | ||
| 27 | #include <linux/acpi.h> | ||
| 28 | #include <linux/pci.h> | ||
| 29 | #include <acpi/acpi.h> | ||
| 30 | |||
| 31 | struct acpi_pci_ioapic { | ||
| 32 | acpi_handle root_handle; | ||
| 33 | acpi_handle handle; | ||
| 34 | u32 gsi_base; | ||
| 35 | struct resource res; | ||
| 36 | struct pci_dev *pdev; | ||
| 37 | struct list_head list; | ||
| 38 | }; | ||
| 39 | |||
| 40 | static LIST_HEAD(ioapic_list); | ||
| 41 | static DEFINE_MUTEX(ioapic_list_lock); | ||
| 42 | |||
| 43 | static acpi_status setup_res(struct acpi_resource *acpi_res, void *data) | ||
| 44 | { | ||
| 45 | struct resource *res = data; | ||
| 46 | struct resource_win win; | ||
| 47 | |||
| 48 | res->flags = 0; | ||
| 49 | if (acpi_dev_filter_resource_type(acpi_res, IORESOURCE_MEM) == 0) | ||
| 50 | return AE_OK; | ||
| 51 | |||
| 52 | if (!acpi_dev_resource_memory(acpi_res, res)) { | ||
| 53 | if (acpi_dev_resource_address_space(acpi_res, &win) || | ||
| 54 | acpi_dev_resource_ext_address_space(acpi_res, &win)) | ||
| 55 | *res = win.res; | ||
| 56 | } | ||
| 57 | if ((res->flags & IORESOURCE_PREFETCH) || | ||
| 58 | (res->flags & IORESOURCE_DISABLED)) | ||
| 59 | res->flags = 0; | ||
| 60 | |||
| 61 | return AE_CTRL_TERMINATE; | ||
| 62 | } | ||
| 63 | |||
| 64 | static bool acpi_is_ioapic(acpi_handle handle, char **type) | ||
| 65 | { | ||
| 66 | acpi_status status; | ||
| 67 | struct acpi_device_info *info; | ||
| 68 | char *hid = NULL; | ||
| 69 | bool match = false; | ||
| 70 | |||
| 71 | if (!acpi_has_method(handle, "_GSB")) | ||
| 72 | return false; | ||
| 73 | |||
| 74 | status = acpi_get_object_info(handle, &info); | ||
| 75 | if (ACPI_SUCCESS(status)) { | ||
| 76 | if (info->valid & ACPI_VALID_HID) | ||
| 77 | hid = info->hardware_id.string; | ||
| 78 | if (hid) { | ||
| 79 | if (strcmp(hid, "ACPI0009") == 0) { | ||
| 80 | *type = "IOxAPIC"; | ||
| 81 | match = true; | ||
| 82 | } else if (strcmp(hid, "ACPI000A") == 0) { | ||
| 83 | *type = "IOAPIC"; | ||
| 84 | match = true; | ||
| 85 | } | ||
| 86 | } | ||
| 87 | kfree(info); | ||
| 88 | } | ||
| 89 | |||
| 90 | return match; | ||
| 91 | } | ||
| 92 | |||
| 93 | static acpi_status handle_ioapic_add(acpi_handle handle, u32 lvl, | ||
| 94 | void *context, void **rv) | ||
| 95 | { | ||
| 96 | acpi_status status; | ||
| 97 | unsigned long long gsi_base; | ||
| 98 | struct acpi_pci_ioapic *ioapic; | ||
| 99 | struct pci_dev *dev = NULL; | ||
| 100 | struct resource *res = NULL; | ||
| 101 | char *type = NULL; | ||
| 102 | |||
| 103 | if (!acpi_is_ioapic(handle, &type)) | ||
| 104 | return AE_OK; | ||
| 105 | |||
| 106 | mutex_lock(&ioapic_list_lock); | ||
| 107 | list_for_each_entry(ioapic, &ioapic_list, list) | ||
| 108 | if (ioapic->handle == handle) { | ||
| 109 | mutex_unlock(&ioapic_list_lock); | ||
| 110 | return AE_OK; | ||
| 111 | } | ||
| 112 | |||
| 113 | status = acpi_evaluate_integer(handle, "_GSB", NULL, &gsi_base); | ||
| 114 | if (ACPI_FAILURE(status)) { | ||
| 115 | acpi_handle_warn(handle, "failed to evaluate _GSB method\n"); | ||
| 116 | goto exit; | ||
| 117 | } | ||
| 118 | |||
| 119 | ioapic = kzalloc(sizeof(*ioapic), GFP_KERNEL); | ||
| 120 | if (!ioapic) { | ||
| 121 | pr_err("cannot allocate memory for new IOAPIC\n"); | ||
| 122 | goto exit; | ||
| 123 | } else { | ||
| 124 | ioapic->root_handle = (acpi_handle)context; | ||
| 125 | ioapic->handle = handle; | ||
| 126 | ioapic->gsi_base = (u32)gsi_base; | ||
| 127 | INIT_LIST_HEAD(&ioapic->list); | ||
| 128 | } | ||
| 129 | |||
| 130 | if (acpi_ioapic_registered(handle, (u32)gsi_base)) | ||
| 131 | goto done; | ||
| 132 | |||
| 133 | dev = acpi_get_pci_dev(handle); | ||
| 134 | if (dev && pci_resource_len(dev, 0)) { | ||
| 135 | if (pci_enable_device(dev) < 0) | ||
| 136 | goto exit_put; | ||
| 137 | pci_set_master(dev); | ||
| 138 | if (pci_request_region(dev, 0, type)) | ||
| 139 | goto exit_disable; | ||
| 140 | res = &dev->resource[0]; | ||
| 141 | ioapic->pdev = dev; | ||
| 142 | } else { | ||
| 143 | pci_dev_put(dev); | ||
| 144 | dev = NULL; | ||
| 145 | |||
| 146 | res = &ioapic->res; | ||
| 147 | acpi_walk_resources(handle, METHOD_NAME__CRS, setup_res, res); | ||
| 148 | if (res->flags == 0) { | ||
| 149 | acpi_handle_warn(handle, "failed to get resource\n"); | ||
| 150 | goto exit_free; | ||
| 151 | } else if (request_resource(&iomem_resource, res)) { | ||
| 152 | acpi_handle_warn(handle, "failed to insert resource\n"); | ||
| 153 | goto exit_free; | ||
| 154 | } | ||
| 155 | } | ||
| 156 | |||
| 157 | if (acpi_register_ioapic(handle, res->start, (u32)gsi_base)) { | ||
| 158 | acpi_handle_warn(handle, "failed to register IOAPIC\n"); | ||
| 159 | goto exit_release; | ||
| 160 | } | ||
| 161 | done: | ||
| 162 | list_add(&ioapic->list, &ioapic_list); | ||
| 163 | mutex_unlock(&ioapic_list_lock); | ||
| 164 | |||
| 165 | if (dev) | ||
| 166 | dev_info(&dev->dev, "%s at %pR, GSI %u\n", | ||
| 167 | type, res, (u32)gsi_base); | ||
| 168 | else | ||
| 169 | acpi_handle_info(handle, "%s at %pR, GSI %u\n", | ||
| 170 | type, res, (u32)gsi_base); | ||
| 171 | |||
| 172 | return AE_OK; | ||
| 173 | |||
| 174 | exit_release: | ||
| 175 | if (dev) | ||
| 176 | pci_release_region(dev, 0); | ||
| 177 | else | ||
| 178 | release_resource(res); | ||
| 179 | exit_disable: | ||
| 180 | if (dev) | ||
| 181 | pci_disable_device(dev); | ||
| 182 | exit_put: | ||
| 183 | pci_dev_put(dev); | ||
| 184 | exit_free: | ||
| 185 | kfree(ioapic); | ||
| 186 | exit: | ||
| 187 | mutex_unlock(&ioapic_list_lock); | ||
| 188 | *(acpi_status *)rv = AE_ERROR; | ||
| 189 | return AE_OK; | ||
| 190 | } | ||
| 191 | |||
| 192 | int acpi_ioapic_add(struct acpi_pci_root *root) | ||
| 193 | { | ||
| 194 | acpi_status status, retval = AE_OK; | ||
| 195 | |||
| 196 | status = acpi_walk_namespace(ACPI_TYPE_DEVICE, root->device->handle, | ||
| 197 | UINT_MAX, handle_ioapic_add, NULL, | ||
| 198 | root->device->handle, (void **)&retval); | ||
| 199 | |||
| 200 | return ACPI_SUCCESS(status) && ACPI_SUCCESS(retval) ? 0 : -ENODEV; | ||
| 201 | } | ||
| 202 | |||
| 203 | int acpi_ioapic_remove(struct acpi_pci_root *root) | ||
| 204 | { | ||
| 205 | int retval = 0; | ||
| 206 | struct acpi_pci_ioapic *ioapic, *tmp; | ||
| 207 | |||
| 208 | mutex_lock(&ioapic_list_lock); | ||
| 209 | list_for_each_entry_safe(ioapic, tmp, &ioapic_list, list) { | ||
| 210 | if (root->device->handle != ioapic->root_handle) | ||
| 211 | continue; | ||
| 212 | |||
| 213 | if (acpi_unregister_ioapic(ioapic->handle, ioapic->gsi_base)) | ||
| 214 | retval = -EBUSY; | ||
| 215 | |||
| 216 | if (ioapic->pdev) { | ||
| 217 | pci_release_region(ioapic->pdev, 0); | ||
| 218 | pci_disable_device(ioapic->pdev); | ||
| 219 | pci_dev_put(ioapic->pdev); | ||
| 220 | } else if (ioapic->res.flags && ioapic->res.parent) { | ||
| 221 | release_resource(&ioapic->res); | ||
| 222 | } | ||
| 223 | list_del(&ioapic->list); | ||
| 224 | kfree(ioapic); | ||
| 225 | } | ||
| 226 | mutex_unlock(&ioapic_list_lock); | ||
| 227 | |||
| 228 | return retval; | ||
| 229 | } | ||
diff --git a/drivers/acpi/pci_root.c b/drivers/acpi/pci_root.c index e53e0f659204..68a5f712cd19 100644 --- a/drivers/acpi/pci_root.c +++ b/drivers/acpi/pci_root.c | |||
| @@ -621,6 +621,7 @@ static int acpi_pci_root_add(struct acpi_device *device, | |||
| 621 | if (hotadd) { | 621 | if (hotadd) { |
| 622 | pcibios_resource_survey_bus(root->bus); | 622 | pcibios_resource_survey_bus(root->bus); |
| 623 | pci_assign_unassigned_root_bus_resources(root->bus); | 623 | pci_assign_unassigned_root_bus_resources(root->bus); |
| 624 | acpi_ioapic_add(root); | ||
| 624 | } | 625 | } |
| 625 | 626 | ||
| 626 | pci_lock_rescan_remove(); | 627 | pci_lock_rescan_remove(); |
| @@ -644,6 +645,8 @@ static void acpi_pci_root_remove(struct acpi_device *device) | |||
| 644 | 645 | ||
| 645 | pci_stop_root_bus(root->bus); | 646 | pci_stop_root_bus(root->bus); |
| 646 | 647 | ||
| 648 | WARN_ON(acpi_ioapic_remove(root)); | ||
| 649 | |||
| 647 | device_set_run_wake(root->bus->bridge, false); | 650 | device_set_run_wake(root->bus->bridge, false); |
| 648 | pci_acpi_remove_bus_pm_notifier(device); | 651 | pci_acpi_remove_bus_pm_notifier(device); |
| 649 | 652 | ||
