Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-only
0002 /*
0003  * IOAPIC/IOxAPIC/IOSAPIC driver
0004  *
0005  * Copyright (C) 2009 Fujitsu Limited.
0006  * (c) Copyright 2009 Hewlett-Packard Development Company, L.P.
0007  *
0008  * Copyright (C) 2014 Intel Corporation
0009  *
0010  * Based on original drivers/pci/ioapic.c
0011  *  Yinghai Lu <yinghai@kernel.org>
0012  *  Jiang Liu <jiang.liu@intel.com>
0013  */
0014 
0015 /*
0016  * This driver manages I/O APICs added by hotplug after boot.
0017  * We try to claim all I/O APIC devices, but those present at boot were
0018  * registered when we parsed the ACPI MADT.
0019  */
0020 
0021 #define pr_fmt(fmt) "ACPI: IOAPIC: " fmt
0022 
0023 #include <linux/slab.h>
0024 #include <linux/acpi.h>
0025 #include <linux/pci.h>
0026 #include <acpi/acpi.h>
0027 
0028 struct acpi_pci_ioapic {
0029     acpi_handle root_handle;
0030     acpi_handle handle;
0031     u32     gsi_base;
0032     struct resource res;
0033     struct pci_dev  *pdev;
0034     struct list_head list;
0035 };
0036 
0037 static LIST_HEAD(ioapic_list);
0038 static DEFINE_MUTEX(ioapic_list_lock);
0039 
0040 static acpi_status setup_res(struct acpi_resource *acpi_res, void *data)
0041 {
0042     struct resource *res = data;
0043     struct resource_win win;
0044 
0045     /*
0046      * We might assign this to 'res' later, make sure all pointers are
0047      * cleared before the resource is added to the global list
0048      */
0049     memset(&win, 0, sizeof(win));
0050 
0051     res->flags = 0;
0052     if (acpi_dev_filter_resource_type(acpi_res, IORESOURCE_MEM))
0053         return AE_OK;
0054 
0055     if (!acpi_dev_resource_memory(acpi_res, res)) {
0056         if (acpi_dev_resource_address_space(acpi_res, &win) ||
0057             acpi_dev_resource_ext_address_space(acpi_res, &win))
0058             *res = win.res;
0059     }
0060     if ((res->flags & IORESOURCE_PREFETCH) ||
0061         (res->flags & IORESOURCE_DISABLED))
0062         res->flags = 0;
0063 
0064     return AE_CTRL_TERMINATE;
0065 }
0066 
0067 static bool acpi_is_ioapic(acpi_handle handle, char **type)
0068 {
0069     acpi_status status;
0070     struct acpi_device_info *info;
0071     char *hid = NULL;
0072     bool match = false;
0073 
0074     if (!acpi_has_method(handle, "_GSB"))
0075         return false;
0076 
0077     status = acpi_get_object_info(handle, &info);
0078     if (ACPI_SUCCESS(status)) {
0079         if (info->valid & ACPI_VALID_HID)
0080             hid = info->hardware_id.string;
0081         if (hid) {
0082             if (strcmp(hid, "ACPI0009") == 0) {
0083                 *type = "IOxAPIC";
0084                 match = true;
0085             } else if (strcmp(hid, "ACPI000A") == 0) {
0086                 *type = "IOAPIC";
0087                 match = true;
0088             }
0089         }
0090         kfree(info);
0091     }
0092 
0093     return match;
0094 }
0095 
0096 static acpi_status handle_ioapic_add(acpi_handle handle, u32 lvl,
0097                      void *context, void **rv)
0098 {
0099     acpi_status status;
0100     unsigned long long gsi_base;
0101     struct acpi_pci_ioapic *ioapic;
0102     struct pci_dev *dev = NULL;
0103     struct resource *res = NULL, *pci_res = NULL, *crs_res;
0104     char *type = NULL;
0105 
0106     if (!acpi_is_ioapic(handle, &type))
0107         return AE_OK;
0108 
0109     mutex_lock(&ioapic_list_lock);
0110     list_for_each_entry(ioapic, &ioapic_list, list)
0111         if (ioapic->handle == handle) {
0112             mutex_unlock(&ioapic_list_lock);
0113             return AE_OK;
0114         }
0115 
0116     status = acpi_evaluate_integer(handle, "_GSB", NULL, &gsi_base);
0117     if (ACPI_FAILURE(status)) {
0118         acpi_handle_warn(handle, "failed to evaluate _GSB method\n");
0119         goto exit;
0120     }
0121 
0122     ioapic = kzalloc(sizeof(*ioapic), GFP_KERNEL);
0123     if (!ioapic) {
0124         pr_err("cannot allocate memory for new IOAPIC\n");
0125         goto exit;
0126     } else {
0127         ioapic->root_handle = (acpi_handle)context;
0128         ioapic->handle = handle;
0129         ioapic->gsi_base = (u32)gsi_base;
0130         INIT_LIST_HEAD(&ioapic->list);
0131     }
0132 
0133     if (acpi_ioapic_registered(handle, (u32)gsi_base))
0134         goto done;
0135 
0136     dev = acpi_get_pci_dev(handle);
0137     if (dev && pci_resource_len(dev, 0)) {
0138         if (pci_enable_device(dev) < 0)
0139             goto exit_put;
0140         pci_set_master(dev);
0141         if (pci_request_region(dev, 0, type))
0142             goto exit_disable;
0143         pci_res = &dev->resource[0];
0144         ioapic->pdev = dev;
0145     } else {
0146         pci_dev_put(dev);
0147         dev = NULL;
0148     }
0149 
0150     crs_res = &ioapic->res;
0151     acpi_walk_resources(handle, METHOD_NAME__CRS, setup_res, crs_res);
0152     crs_res->name = type;
0153     crs_res->flags |= IORESOURCE_BUSY;
0154     if (crs_res->flags == 0) {
0155         acpi_handle_warn(handle, "failed to get resource\n");
0156         goto exit_release;
0157     } else if (insert_resource(&iomem_resource, crs_res)) {
0158         acpi_handle_warn(handle, "failed to insert resource\n");
0159         goto exit_release;
0160     }
0161 
0162     /* try pci resource first, then "_CRS" resource */
0163     res = pci_res;
0164     if (!res || !res->flags)
0165         res = crs_res;
0166 
0167     if (acpi_register_ioapic(handle, res->start, (u32)gsi_base)) {
0168         acpi_handle_warn(handle, "failed to register IOAPIC\n");
0169         goto exit_release;
0170     }
0171 done:
0172     list_add(&ioapic->list, &ioapic_list);
0173     mutex_unlock(&ioapic_list_lock);
0174 
0175     if (dev)
0176         dev_info(&dev->dev, "%s at %pR, GSI %u\n",
0177              type, res, (u32)gsi_base);
0178     else
0179         acpi_handle_info(handle, "%s at %pR, GSI %u\n",
0180                  type, res, (u32)gsi_base);
0181 
0182     return AE_OK;
0183 
0184 exit_release:
0185     if (dev)
0186         pci_release_region(dev, 0);
0187     if (ioapic->res.flags && ioapic->res.parent)
0188         release_resource(&ioapic->res);
0189 exit_disable:
0190     if (dev)
0191         pci_disable_device(dev);
0192 exit_put:
0193     pci_dev_put(dev);
0194     kfree(ioapic);
0195 exit:
0196     mutex_unlock(&ioapic_list_lock);
0197     *(acpi_status *)rv = AE_ERROR;
0198     return AE_OK;
0199 }
0200 
0201 int acpi_ioapic_add(acpi_handle root_handle)
0202 {
0203     acpi_status status, retval = AE_OK;
0204 
0205     status = acpi_walk_namespace(ACPI_TYPE_DEVICE, root_handle,
0206                      UINT_MAX, handle_ioapic_add, NULL,
0207                      root_handle, (void **)&retval);
0208 
0209     return ACPI_SUCCESS(status) && ACPI_SUCCESS(retval) ? 0 : -ENODEV;
0210 }
0211 
0212 void pci_ioapic_remove(struct acpi_pci_root *root)
0213 {
0214     struct acpi_pci_ioapic *ioapic, *tmp;
0215 
0216     mutex_lock(&ioapic_list_lock);
0217     list_for_each_entry_safe(ioapic, tmp, &ioapic_list, list) {
0218         if (root->device->handle != ioapic->root_handle)
0219             continue;
0220         if (ioapic->pdev) {
0221             pci_release_region(ioapic->pdev, 0);
0222             pci_disable_device(ioapic->pdev);
0223             pci_dev_put(ioapic->pdev);
0224         }
0225     }
0226     mutex_unlock(&ioapic_list_lock);
0227 }
0228 
0229 int acpi_ioapic_remove(struct acpi_pci_root *root)
0230 {
0231     int retval = 0;
0232     struct acpi_pci_ioapic *ioapic, *tmp;
0233 
0234     mutex_lock(&ioapic_list_lock);
0235     list_for_each_entry_safe(ioapic, tmp, &ioapic_list, list) {
0236         if (root->device->handle != ioapic->root_handle)
0237             continue;
0238         if (acpi_unregister_ioapic(ioapic->handle, ioapic->gsi_base))
0239             retval = -EBUSY;
0240         if (ioapic->res.flags && ioapic->res.parent)
0241             release_resource(&ioapic->res);
0242         list_del(&ioapic->list);
0243         kfree(ioapic);
0244     }
0245     mutex_unlock(&ioapic_list_lock);
0246 
0247     return retval;
0248 }