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