ioapic.c (6070B)
1// SPDX-License-Identifier: GPL-2.0-only 2/* 3 * IOAPIC/IOxAPIC/IOSAPIC driver 4 * 5 * Copyright (C) 2009 Fujitsu Limited. 6 * (c) Copyright 2009 Hewlett-Packard Development Company, L.P. 7 * 8 * Copyright (C) 2014 Intel Corporation 9 * 10 * Based on original drivers/pci/ioapic.c 11 * Yinghai Lu <yinghai@kernel.org> 12 * Jiang Liu <jiang.liu@intel.com> 13 */ 14 15/* 16 * This driver manages I/O APICs added by hotplug after boot. 17 * We try to claim all I/O APIC devices, but those present at boot were 18 * registered when we parsed the ACPI MADT. 19 */ 20 21#define pr_fmt(fmt) "ACPI: IOAPIC: " fmt 22 23#include <linux/slab.h> 24#include <linux/acpi.h> 25#include <linux/pci.h> 26#include <acpi/acpi.h> 27 28struct acpi_pci_ioapic { 29 acpi_handle root_handle; 30 acpi_handle handle; 31 u32 gsi_base; 32 struct resource res; 33 struct pci_dev *pdev; 34 struct list_head list; 35}; 36 37static LIST_HEAD(ioapic_list); 38static DEFINE_MUTEX(ioapic_list_lock); 39 40static acpi_status setup_res(struct acpi_resource *acpi_res, void *data) 41{ 42 struct resource *res = data; 43 struct resource_win win; 44 45 /* 46 * We might assign this to 'res' later, make sure all pointers are 47 * cleared before the resource is added to the global list 48 */ 49 memset(&win, 0, sizeof(win)); 50 51 res->flags = 0; 52 if (acpi_dev_filter_resource_type(acpi_res, IORESOURCE_MEM)) 53 return AE_OK; 54 55 if (!acpi_dev_resource_memory(acpi_res, res)) { 56 if (acpi_dev_resource_address_space(acpi_res, &win) || 57 acpi_dev_resource_ext_address_space(acpi_res, &win)) 58 *res = win.res; 59 } 60 if ((res->flags & IORESOURCE_PREFETCH) || 61 (res->flags & IORESOURCE_DISABLED)) 62 res->flags = 0; 63 64 return AE_CTRL_TERMINATE; 65} 66 67static bool acpi_is_ioapic(acpi_handle handle, char **type) 68{ 69 acpi_status status; 70 struct acpi_device_info *info; 71 char *hid = NULL; 72 bool match = false; 73 74 if (!acpi_has_method(handle, "_GSB")) 75 return false; 76 77 status = acpi_get_object_info(handle, &info); 78 if (ACPI_SUCCESS(status)) { 79 if (info->valid & ACPI_VALID_HID) 80 hid = info->hardware_id.string; 81 if (hid) { 82 if (strcmp(hid, "ACPI0009") == 0) { 83 *type = "IOxAPIC"; 84 match = true; 85 } else if (strcmp(hid, "ACPI000A") == 0) { 86 *type = "IOAPIC"; 87 match = true; 88 } 89 } 90 kfree(info); 91 } 92 93 return match; 94} 95 96static acpi_status handle_ioapic_add(acpi_handle handle, u32 lvl, 97 void *context, void **rv) 98{ 99 acpi_status status; 100 unsigned long long gsi_base; 101 struct acpi_pci_ioapic *ioapic; 102 struct pci_dev *dev = NULL; 103 struct resource *res = NULL, *pci_res = NULL, *crs_res; 104 char *type = NULL; 105 106 if (!acpi_is_ioapic(handle, &type)) 107 return AE_OK; 108 109 mutex_lock(&ioapic_list_lock); 110 list_for_each_entry(ioapic, &ioapic_list, list) 111 if (ioapic->handle == handle) { 112 mutex_unlock(&ioapic_list_lock); 113 return AE_OK; 114 } 115 116 status = acpi_evaluate_integer(handle, "_GSB", NULL, &gsi_base); 117 if (ACPI_FAILURE(status)) { 118 acpi_handle_warn(handle, "failed to evaluate _GSB method\n"); 119 goto exit; 120 } 121 122 ioapic = kzalloc(sizeof(*ioapic), GFP_KERNEL); 123 if (!ioapic) { 124 pr_err("cannot allocate memory for new IOAPIC\n"); 125 goto exit; 126 } else { 127 ioapic->root_handle = (acpi_handle)context; 128 ioapic->handle = handle; 129 ioapic->gsi_base = (u32)gsi_base; 130 INIT_LIST_HEAD(&ioapic->list); 131 } 132 133 if (acpi_ioapic_registered(handle, (u32)gsi_base)) 134 goto done; 135 136 dev = acpi_get_pci_dev(handle); 137 if (dev && pci_resource_len(dev, 0)) { 138 if (pci_enable_device(dev) < 0) 139 goto exit_put; 140 pci_set_master(dev); 141 if (pci_request_region(dev, 0, type)) 142 goto exit_disable; 143 pci_res = &dev->resource[0]; 144 ioapic->pdev = dev; 145 } else { 146 pci_dev_put(dev); 147 dev = NULL; 148 } 149 150 crs_res = &ioapic->res; 151 acpi_walk_resources(handle, METHOD_NAME__CRS, setup_res, crs_res); 152 crs_res->name = type; 153 crs_res->flags |= IORESOURCE_BUSY; 154 if (crs_res->flags == 0) { 155 acpi_handle_warn(handle, "failed to get resource\n"); 156 goto exit_release; 157 } else if (insert_resource(&iomem_resource, crs_res)) { 158 acpi_handle_warn(handle, "failed to insert resource\n"); 159 goto exit_release; 160 } 161 162 /* try pci resource first, then "_CRS" resource */ 163 res = pci_res; 164 if (!res || !res->flags) 165 res = crs_res; 166 167 if (acpi_register_ioapic(handle, res->start, (u32)gsi_base)) { 168 acpi_handle_warn(handle, "failed to register IOAPIC\n"); 169 goto exit_release; 170 } 171done: 172 list_add(&ioapic->list, &ioapic_list); 173 mutex_unlock(&ioapic_list_lock); 174 175 if (dev) 176 dev_info(&dev->dev, "%s at %pR, GSI %u\n", 177 type, res, (u32)gsi_base); 178 else 179 acpi_handle_info(handle, "%s at %pR, GSI %u\n", 180 type, res, (u32)gsi_base); 181 182 return AE_OK; 183 184exit_release: 185 if (dev) 186 pci_release_region(dev, 0); 187 if (ioapic->res.flags && ioapic->res.parent) 188 release_resource(&ioapic->res); 189exit_disable: 190 if (dev) 191 pci_disable_device(dev); 192exit_put: 193 pci_dev_put(dev); 194 kfree(ioapic); 195exit: 196 mutex_unlock(&ioapic_list_lock); 197 *(acpi_status *)rv = AE_ERROR; 198 return AE_OK; 199} 200 201int acpi_ioapic_add(acpi_handle root_handle) 202{ 203 acpi_status status, retval = AE_OK; 204 205 status = acpi_walk_namespace(ACPI_TYPE_DEVICE, root_handle, 206 UINT_MAX, handle_ioapic_add, NULL, 207 root_handle, (void **)&retval); 208 209 return ACPI_SUCCESS(status) && ACPI_SUCCESS(retval) ? 0 : -ENODEV; 210} 211 212void pci_ioapic_remove(struct acpi_pci_root *root) 213{ 214 struct acpi_pci_ioapic *ioapic, *tmp; 215 216 mutex_lock(&ioapic_list_lock); 217 list_for_each_entry_safe(ioapic, tmp, &ioapic_list, list) { 218 if (root->device->handle != ioapic->root_handle) 219 continue; 220 if (ioapic->pdev) { 221 pci_release_region(ioapic->pdev, 0); 222 pci_disable_device(ioapic->pdev); 223 pci_dev_put(ioapic->pdev); 224 } 225 } 226 mutex_unlock(&ioapic_list_lock); 227} 228 229int acpi_ioapic_remove(struct acpi_pci_root *root) 230{ 231 int retval = 0; 232 struct acpi_pci_ioapic *ioapic, *tmp; 233 234 mutex_lock(&ioapic_list_lock); 235 list_for_each_entry_safe(ioapic, tmp, &ioapic_list, list) { 236 if (root->device->handle != ioapic->root_handle) 237 continue; 238 if (acpi_unregister_ioapic(ioapic->handle, ioapic->gsi_base)) 239 retval = -EBUSY; 240 if (ioapic->res.flags && ioapic->res.parent) 241 release_resource(&ioapic->res); 242 list_del(&ioapic->list); 243 kfree(ioapic); 244 } 245 mutex_unlock(&ioapic_list_lock); 246 247 return retval; 248}