1*4c3dd9cdSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
28344b568SAlex Chiang /*
38344b568SAlex Chiang * pci_slot.c - ACPI PCI Slot Driver
48344b568SAlex Chiang *
58344b568SAlex Chiang * The code here is heavily leveraged from the acpiphp module.
68344b568SAlex Chiang * Thanks to Matthew Wilcox <matthew@wil.cx> for much guidance.
78344b568SAlex Chiang * Thanks to Kenji Kaneshige <kaneshige.kenji@jp.fujitsu.com> for code
88344b568SAlex Chiang * review and fixes.
98344b568SAlex Chiang *
10e4268aadSAlex Chiang * Copyright (C) 2007-2008 Hewlett-Packard Development Company, L.P.
11e4268aadSAlex Chiang * Alex Chiang <achiang@hp.com>
128344b568SAlex Chiang *
135c0b04e3SJiang Liu * Copyright (C) 2013 Huawei Tech. Co., Ltd.
145c0b04e3SJiang Liu * Jiang Liu <jiang.liu@huawei.com>
158344b568SAlex Chiang */
168344b568SAlex Chiang
172cfd93ddSJoe Perches #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
182cfd93ddSJoe Perches
198344b568SAlex Chiang #include <linux/kernel.h>
208344b568SAlex Chiang #include <linux/init.h>
215a0e3ad6STejun Heo #include <linux/slab.h>
228344b568SAlex Chiang #include <linux/types.h>
235c0b04e3SJiang Liu #include <linux/list.h>
248344b568SAlex Chiang #include <linux/pci.h>
258344b568SAlex Chiang #include <linux/acpi.h>
26eb27cae8SLen Brown #include <linux/dmi.h>
27e22e58a4SRashika #include <linux/pci-acpi.h>
288344b568SAlex Chiang
298344b568SAlex Chiang static int check_sta_before_sun;
308344b568SAlex Chiang
317e24bc1cSAlex Chiang #define SLOT_NAME_SIZE 21 /* Inspired by #define in acpiphp.h */
328344b568SAlex Chiang
338344b568SAlex Chiang struct acpi_pci_slot {
348344b568SAlex Chiang struct pci_slot *pci_slot; /* corresponding pci_slot */
358344b568SAlex Chiang struct list_head list; /* node in the list of slots */
368344b568SAlex Chiang };
378344b568SAlex Chiang
388344b568SAlex Chiang static LIST_HEAD(slot_list);
398344b568SAlex Chiang static DEFINE_MUTEX(slot_list_lock);
408344b568SAlex Chiang
418344b568SAlex Chiang static int
check_slot(acpi_handle handle,unsigned long long * sun)4227663c58SMatthew Wilcox check_slot(acpi_handle handle, unsigned long long *sun)
438344b568SAlex Chiang {
44362b7077SMatthew Wilcox int device = -1;
4527663c58SMatthew Wilcox unsigned long long adr, sta;
468344b568SAlex Chiang acpi_status status;
478344b568SAlex Chiang struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
488344b568SAlex Chiang
498344b568SAlex Chiang acpi_get_name(handle, ACPI_FULL_PATHNAME, &buffer);
50409ffed2SJoe Perches pr_debug("Checking slot on path: %s\n", (char *)buffer.pointer);
518344b568SAlex Chiang
528344b568SAlex Chiang if (check_sta_before_sun) {
538344b568SAlex Chiang /* If SxFy doesn't have _STA, we just assume it's there */
548344b568SAlex Chiang status = acpi_evaluate_integer(handle, "_STA", NULL, &sta);
55362b7077SMatthew Wilcox if (ACPI_SUCCESS(status) && !(sta & ACPI_STA_DEVICE_PRESENT))
568344b568SAlex Chiang goto out;
578344b568SAlex Chiang }
588344b568SAlex Chiang
598344b568SAlex Chiang status = acpi_evaluate_integer(handle, "_ADR", NULL, &adr);
608344b568SAlex Chiang if (ACPI_FAILURE(status)) {
61409ffed2SJoe Perches pr_debug("_ADR returned %d on %s\n",
62409ffed2SJoe Perches status, (char *)buffer.pointer);
638344b568SAlex Chiang goto out;
648344b568SAlex Chiang }
658344b568SAlex Chiang
668344b568SAlex Chiang /* No _SUN == not a slot == bail */
678344b568SAlex Chiang status = acpi_evaluate_integer(handle, "_SUN", NULL, sun);
688344b568SAlex Chiang if (ACPI_FAILURE(status)) {
69409ffed2SJoe Perches pr_debug("_SUN returned %d on %s\n",
70409ffed2SJoe Perches status, (char *)buffer.pointer);
718344b568SAlex Chiang goto out;
728344b568SAlex Chiang }
738344b568SAlex Chiang
74362b7077SMatthew Wilcox device = (adr >> 16) & 0xffff;
758344b568SAlex Chiang out:
768344b568SAlex Chiang kfree(buffer.pointer);
77362b7077SMatthew Wilcox return device;
788344b568SAlex Chiang }
798344b568SAlex Chiang
808344b568SAlex Chiang /*
815c0b04e3SJiang Liu * Check whether handle has an associated slot and create PCI slot if it has.
828344b568SAlex Chiang */
838344b568SAlex Chiang static acpi_status
register_slot(acpi_handle handle,u32 lvl,void * context,void ** rv)848344b568SAlex Chiang register_slot(acpi_handle handle, u32 lvl, void *context, void **rv)
858344b568SAlex Chiang {
868344b568SAlex Chiang int device;
8727663c58SMatthew Wilcox unsigned long long sun;
888344b568SAlex Chiang char name[SLOT_NAME_SIZE];
898344b568SAlex Chiang struct acpi_pci_slot *slot;
908344b568SAlex Chiang struct pci_slot *pci_slot;
915c0b04e3SJiang Liu struct pci_bus *pci_bus = context;
928344b568SAlex Chiang
93362b7077SMatthew Wilcox device = check_slot(handle, &sun);
94362b7077SMatthew Wilcox if (device < 0)
958344b568SAlex Chiang return AE_OK;
968344b568SAlex Chiang
975c0b04e3SJiang Liu /*
985c0b04e3SJiang Liu * There may be multiple PCI functions associated with the same slot.
995c0b04e3SJiang Liu * Check whether PCI slot has already been created for this PCI device.
1005c0b04e3SJiang Liu */
1015c0b04e3SJiang Liu list_for_each_entry(slot, &slot_list, list) {
1025c0b04e3SJiang Liu pci_slot = slot->pci_slot;
1035c0b04e3SJiang Liu if (pci_slot->bus == pci_bus && pci_slot->number == device)
1045c0b04e3SJiang Liu return AE_OK;
1055c0b04e3SJiang Liu }
1065c0b04e3SJiang Liu
1078344b568SAlex Chiang slot = kmalloc(sizeof(*slot), GFP_KERNEL);
1082cfd93ddSJoe Perches if (!slot)
1098344b568SAlex Chiang return AE_OK;
1108344b568SAlex Chiang
1117e24bc1cSAlex Chiang snprintf(name, sizeof(name), "%llu", sun);
112828f3768SAlex Chiang pci_slot = pci_create_slot(pci_bus, device, name, NULL);
1138344b568SAlex Chiang if (IS_ERR(pci_slot)) {
1142cfd93ddSJoe Perches pr_err("pci_create_slot returned %ld\n", PTR_ERR(pci_slot));
1158344b568SAlex Chiang kfree(slot);
116e4268aadSAlex Chiang return AE_OK;
1178344b568SAlex Chiang }
1188344b568SAlex Chiang
1198344b568SAlex Chiang slot->pci_slot = pci_slot;
1208344b568SAlex Chiang list_add(&slot->list, &slot_list);
1218344b568SAlex Chiang
12272800360SAlex Chiang get_device(&pci_bus->dev);
12372800360SAlex Chiang
124409ffed2SJoe Perches pr_debug("%p, pci_bus: %x, device: %d, name: %s\n",
1258344b568SAlex Chiang pci_slot, pci_bus->number, device, name);
1268344b568SAlex Chiang
1278344b568SAlex Chiang return AE_OK;
1288344b568SAlex Chiang }
1298344b568SAlex Chiang
acpi_pci_slot_enumerate(struct pci_bus * bus)130be1c9de9SRafael J. Wysocki void acpi_pci_slot_enumerate(struct pci_bus *bus)
1318344b568SAlex Chiang {
132be1c9de9SRafael J. Wysocki acpi_handle handle = ACPI_HANDLE(bus->bridge);
133be1c9de9SRafael J. Wysocki
134be1c9de9SRafael J. Wysocki if (handle) {
1355c0b04e3SJiang Liu mutex_lock(&slot_list_lock);
1365c0b04e3SJiang Liu acpi_walk_namespace(ACPI_TYPE_DEVICE, handle, 1,
1375c0b04e3SJiang Liu register_slot, NULL, bus, NULL);
1385c0b04e3SJiang Liu mutex_unlock(&slot_list_lock);
1398344b568SAlex Chiang }
140be1c9de9SRafael J. Wysocki }
1418344b568SAlex Chiang
acpi_pci_slot_remove(struct pci_bus * bus)1425c0b04e3SJiang Liu void acpi_pci_slot_remove(struct pci_bus *bus)
1438344b568SAlex Chiang {
1448344b568SAlex Chiang struct acpi_pci_slot *slot, *tmp;
1458344b568SAlex Chiang
1468344b568SAlex Chiang mutex_lock(&slot_list_lock);
1478344b568SAlex Chiang list_for_each_entry_safe(slot, tmp, &slot_list, list) {
1485c0b04e3SJiang Liu if (slot->pci_slot->bus == bus) {
1498344b568SAlex Chiang list_del(&slot->list);
1508344b568SAlex Chiang pci_destroy_slot(slot->pci_slot);
1515c0b04e3SJiang Liu put_device(&bus->dev);
1528344b568SAlex Chiang kfree(slot);
1538344b568SAlex Chiang }
1548344b568SAlex Chiang }
1558344b568SAlex Chiang mutex_unlock(&slot_list_lock);
1568344b568SAlex Chiang }
1578344b568SAlex Chiang
do_sta_before_sun(const struct dmi_system_id * d)1588344b568SAlex Chiang static int do_sta_before_sun(const struct dmi_system_id *d)
1598344b568SAlex Chiang {
1602cfd93ddSJoe Perches pr_info("%s detected: will evaluate _STA before calling _SUN\n",
1612cfd93ddSJoe Perches d->ident);
1628344b568SAlex Chiang check_sta_before_sun = 1;
1638344b568SAlex Chiang return 0;
1648344b568SAlex Chiang }
1658344b568SAlex Chiang
1666faadbbbSChristoph Hellwig static const struct dmi_system_id acpi_pci_slot_dmi_table[] __initconst = {
1678344b568SAlex Chiang /*
1688344b568SAlex Chiang * Fujitsu Primequest machines will return 1023 to indicate an
1698344b568SAlex Chiang * error if the _SUN method is evaluated on SxFy objects that
1708344b568SAlex Chiang * are not present (as indicated by _STA), so for those machines,
1718344b568SAlex Chiang * we want to check _STA before evaluating _SUN.
1728344b568SAlex Chiang */
1738344b568SAlex Chiang {
1748344b568SAlex Chiang .callback = do_sta_before_sun,
1758344b568SAlex Chiang .ident = "Fujitsu PRIMEQUEST",
1768344b568SAlex Chiang .matches = {
1778344b568SAlex Chiang DMI_MATCH(DMI_BIOS_VENDOR, "FUJITSU LIMITED"),
1788344b568SAlex Chiang DMI_MATCH(DMI_BIOS_VERSION, "PRIMEQUEST"),
1798344b568SAlex Chiang },
1808344b568SAlex Chiang },
1818344b568SAlex Chiang {}
1828344b568SAlex Chiang };
1838344b568SAlex Chiang
acpi_pci_slot_init(void)184ab1a2e03SJiang Liu void __init acpi_pci_slot_init(void)
1858344b568SAlex Chiang {
1868344b568SAlex Chiang dmi_check_system(acpi_pci_slot_dmi_table);
1878344b568SAlex Chiang }
188