Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-only
0002 /*
0003  *  pci_slot.c - ACPI PCI Slot Driver
0004  *
0005  *  The code here is heavily leveraged from the acpiphp module.
0006  *  Thanks to Matthew Wilcox <matthew@wil.cx> for much guidance.
0007  *  Thanks to Kenji Kaneshige <kaneshige.kenji@jp.fujitsu.com> for code
0008  *  review and fixes.
0009  *
0010  *  Copyright (C) 2007-2008 Hewlett-Packard Development Company, L.P.
0011  *      Alex Chiang <achiang@hp.com>
0012  *
0013  *  Copyright (C) 2013 Huawei Tech. Co., Ltd.
0014  *  Jiang Liu <jiang.liu@huawei.com>
0015  */
0016 
0017 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
0018 
0019 #include <linux/kernel.h>
0020 #include <linux/init.h>
0021 #include <linux/slab.h>
0022 #include <linux/types.h>
0023 #include <linux/list.h>
0024 #include <linux/pci.h>
0025 #include <linux/acpi.h>
0026 #include <linux/dmi.h>
0027 #include <linux/pci-acpi.h>
0028 
0029 static int check_sta_before_sun;
0030 
0031 #define SLOT_NAME_SIZE 21       /* Inspired by #define in acpiphp.h */
0032 
0033 struct acpi_pci_slot {
0034     struct pci_slot *pci_slot;  /* corresponding pci_slot */
0035     struct list_head list;      /* node in the list of slots */
0036 };
0037 
0038 static LIST_HEAD(slot_list);
0039 static DEFINE_MUTEX(slot_list_lock);
0040 
0041 static int
0042 check_slot(acpi_handle handle, unsigned long long *sun)
0043 {
0044     int device = -1;
0045     unsigned long long adr, sta;
0046     acpi_status status;
0047     struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
0048 
0049     acpi_get_name(handle, ACPI_FULL_PATHNAME, &buffer);
0050     pr_debug("Checking slot on path: %s\n", (char *)buffer.pointer);
0051 
0052     if (check_sta_before_sun) {
0053         /* If SxFy doesn't have _STA, we just assume it's there */
0054         status = acpi_evaluate_integer(handle, "_STA", NULL, &sta);
0055         if (ACPI_SUCCESS(status) && !(sta & ACPI_STA_DEVICE_PRESENT))
0056             goto out;
0057     }
0058 
0059     status = acpi_evaluate_integer(handle, "_ADR", NULL, &adr);
0060     if (ACPI_FAILURE(status)) {
0061         pr_debug("_ADR returned %d on %s\n",
0062              status, (char *)buffer.pointer);
0063         goto out;
0064     }
0065 
0066     /* No _SUN == not a slot == bail */
0067     status = acpi_evaluate_integer(handle, "_SUN", NULL, sun);
0068     if (ACPI_FAILURE(status)) {
0069         pr_debug("_SUN returned %d on %s\n",
0070              status, (char *)buffer.pointer);
0071         goto out;
0072     }
0073 
0074     device = (adr >> 16) & 0xffff;
0075 out:
0076     kfree(buffer.pointer);
0077     return device;
0078 }
0079 
0080 /*
0081  * Check whether handle has an associated slot and create PCI slot if it has.
0082  */
0083 static acpi_status
0084 register_slot(acpi_handle handle, u32 lvl, void *context, void **rv)
0085 {
0086     int device;
0087     unsigned long long sun;
0088     char name[SLOT_NAME_SIZE];
0089     struct acpi_pci_slot *slot;
0090     struct pci_slot *pci_slot;
0091     struct pci_bus *pci_bus = context;
0092 
0093     device = check_slot(handle, &sun);
0094     if (device < 0)
0095         return AE_OK;
0096 
0097     /*
0098      * There may be multiple PCI functions associated with the same slot.
0099      * Check whether PCI slot has already been created for this PCI device.
0100      */
0101     list_for_each_entry(slot, &slot_list, list) {
0102         pci_slot = slot->pci_slot;
0103         if (pci_slot->bus == pci_bus && pci_slot->number == device)
0104             return AE_OK;
0105     }
0106 
0107     slot = kmalloc(sizeof(*slot), GFP_KERNEL);
0108     if (!slot)
0109         return AE_OK;
0110 
0111     snprintf(name, sizeof(name), "%llu", sun);
0112     pci_slot = pci_create_slot(pci_bus, device, name, NULL);
0113     if (IS_ERR(pci_slot)) {
0114         pr_err("pci_create_slot returned %ld\n", PTR_ERR(pci_slot));
0115         kfree(slot);
0116         return AE_OK;
0117     }
0118 
0119     slot->pci_slot = pci_slot;
0120     list_add(&slot->list, &slot_list);
0121 
0122     get_device(&pci_bus->dev);
0123 
0124     pr_debug("%p, pci_bus: %x, device: %d, name: %s\n",
0125          pci_slot, pci_bus->number, device, name);
0126 
0127     return AE_OK;
0128 }
0129 
0130 void acpi_pci_slot_enumerate(struct pci_bus *bus)
0131 {
0132     acpi_handle handle = ACPI_HANDLE(bus->bridge);
0133 
0134     if (handle) {
0135         mutex_lock(&slot_list_lock);
0136         acpi_walk_namespace(ACPI_TYPE_DEVICE, handle, 1,
0137                     register_slot, NULL, bus, NULL);
0138         mutex_unlock(&slot_list_lock);
0139     }
0140 }
0141 
0142 void acpi_pci_slot_remove(struct pci_bus *bus)
0143 {
0144     struct acpi_pci_slot *slot, *tmp;
0145 
0146     mutex_lock(&slot_list_lock);
0147     list_for_each_entry_safe(slot, tmp, &slot_list, list) {
0148         if (slot->pci_slot->bus == bus) {
0149             list_del(&slot->list);
0150             pci_destroy_slot(slot->pci_slot);
0151             put_device(&bus->dev);
0152             kfree(slot);
0153         }
0154     }
0155     mutex_unlock(&slot_list_lock);
0156 }
0157 
0158 static int do_sta_before_sun(const struct dmi_system_id *d)
0159 {
0160     pr_info("%s detected: will evaluate _STA before calling _SUN\n",
0161         d->ident);
0162     check_sta_before_sun = 1;
0163     return 0;
0164 }
0165 
0166 static const struct dmi_system_id acpi_pci_slot_dmi_table[] __initconst = {
0167     /*
0168      * Fujitsu Primequest machines will return 1023 to indicate an
0169      * error if the _SUN method is evaluated on SxFy objects that
0170      * are not present (as indicated by _STA), so for those machines,
0171      * we want to check _STA before evaluating _SUN.
0172      */
0173     {
0174      .callback = do_sta_before_sun,
0175      .ident = "Fujitsu PRIMEQUEST",
0176      .matches = {
0177         DMI_MATCH(DMI_BIOS_VENDOR, "FUJITSU LIMITED"),
0178         DMI_MATCH(DMI_BIOS_VERSION, "PRIMEQUEST"),
0179         },
0180     },
0181     {}
0182 };
0183 
0184 void __init acpi_pci_slot_init(void)
0185 {
0186     dmi_check_system(acpi_pci_slot_dmi_table);
0187 }