18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * pci_slot.c - ACPI PCI Slot Driver 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * The code here is heavily leveraged from the acpiphp module. 68c2ecf20Sopenharmony_ci * Thanks to Matthew Wilcox <matthew@wil.cx> for much guidance. 78c2ecf20Sopenharmony_ci * Thanks to Kenji Kaneshige <kaneshige.kenji@jp.fujitsu.com> for code 88c2ecf20Sopenharmony_ci * review and fixes. 98c2ecf20Sopenharmony_ci * 108c2ecf20Sopenharmony_ci * Copyright (C) 2007-2008 Hewlett-Packard Development Company, L.P. 118c2ecf20Sopenharmony_ci * Alex Chiang <achiang@hp.com> 128c2ecf20Sopenharmony_ci * 138c2ecf20Sopenharmony_ci * Copyright (C) 2013 Huawei Tech. Co., Ltd. 148c2ecf20Sopenharmony_ci * Jiang Liu <jiang.liu@huawei.com> 158c2ecf20Sopenharmony_ci */ 168c2ecf20Sopenharmony_ci 178c2ecf20Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 188c2ecf20Sopenharmony_ci 198c2ecf20Sopenharmony_ci#include <linux/kernel.h> 208c2ecf20Sopenharmony_ci#include <linux/init.h> 218c2ecf20Sopenharmony_ci#include <linux/slab.h> 228c2ecf20Sopenharmony_ci#include <linux/types.h> 238c2ecf20Sopenharmony_ci#include <linux/list.h> 248c2ecf20Sopenharmony_ci#include <linux/pci.h> 258c2ecf20Sopenharmony_ci#include <linux/acpi.h> 268c2ecf20Sopenharmony_ci#include <linux/dmi.h> 278c2ecf20Sopenharmony_ci#include <linux/pci-acpi.h> 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_cistatic int check_sta_before_sun; 308c2ecf20Sopenharmony_ci 318c2ecf20Sopenharmony_ci#define SLOT_NAME_SIZE 21 /* Inspired by #define in acpiphp.h */ 328c2ecf20Sopenharmony_ci 338c2ecf20Sopenharmony_cistruct acpi_pci_slot { 348c2ecf20Sopenharmony_ci struct pci_slot *pci_slot; /* corresponding pci_slot */ 358c2ecf20Sopenharmony_ci struct list_head list; /* node in the list of slots */ 368c2ecf20Sopenharmony_ci}; 378c2ecf20Sopenharmony_ci 388c2ecf20Sopenharmony_cistatic LIST_HEAD(slot_list); 398c2ecf20Sopenharmony_cistatic DEFINE_MUTEX(slot_list_lock); 408c2ecf20Sopenharmony_ci 418c2ecf20Sopenharmony_cistatic int 428c2ecf20Sopenharmony_cicheck_slot(acpi_handle handle, unsigned long long *sun) 438c2ecf20Sopenharmony_ci{ 448c2ecf20Sopenharmony_ci int device = -1; 458c2ecf20Sopenharmony_ci unsigned long long adr, sta; 468c2ecf20Sopenharmony_ci acpi_status status; 478c2ecf20Sopenharmony_ci struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; 488c2ecf20Sopenharmony_ci 498c2ecf20Sopenharmony_ci acpi_get_name(handle, ACPI_FULL_PATHNAME, &buffer); 508c2ecf20Sopenharmony_ci pr_debug("Checking slot on path: %s\n", (char *)buffer.pointer); 518c2ecf20Sopenharmony_ci 528c2ecf20Sopenharmony_ci if (check_sta_before_sun) { 538c2ecf20Sopenharmony_ci /* If SxFy doesn't have _STA, we just assume it's there */ 548c2ecf20Sopenharmony_ci status = acpi_evaluate_integer(handle, "_STA", NULL, &sta); 558c2ecf20Sopenharmony_ci if (ACPI_SUCCESS(status) && !(sta & ACPI_STA_DEVICE_PRESENT)) 568c2ecf20Sopenharmony_ci goto out; 578c2ecf20Sopenharmony_ci } 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_ci status = acpi_evaluate_integer(handle, "_ADR", NULL, &adr); 608c2ecf20Sopenharmony_ci if (ACPI_FAILURE(status)) { 618c2ecf20Sopenharmony_ci pr_debug("_ADR returned %d on %s\n", 628c2ecf20Sopenharmony_ci status, (char *)buffer.pointer); 638c2ecf20Sopenharmony_ci goto out; 648c2ecf20Sopenharmony_ci } 658c2ecf20Sopenharmony_ci 668c2ecf20Sopenharmony_ci /* No _SUN == not a slot == bail */ 678c2ecf20Sopenharmony_ci status = acpi_evaluate_integer(handle, "_SUN", NULL, sun); 688c2ecf20Sopenharmony_ci if (ACPI_FAILURE(status)) { 698c2ecf20Sopenharmony_ci pr_debug("_SUN returned %d on %s\n", 708c2ecf20Sopenharmony_ci status, (char *)buffer.pointer); 718c2ecf20Sopenharmony_ci goto out; 728c2ecf20Sopenharmony_ci } 738c2ecf20Sopenharmony_ci 748c2ecf20Sopenharmony_ci device = (adr >> 16) & 0xffff; 758c2ecf20Sopenharmony_ciout: 768c2ecf20Sopenharmony_ci kfree(buffer.pointer); 778c2ecf20Sopenharmony_ci return device; 788c2ecf20Sopenharmony_ci} 798c2ecf20Sopenharmony_ci 808c2ecf20Sopenharmony_ci/* 818c2ecf20Sopenharmony_ci * Check whether handle has an associated slot and create PCI slot if it has. 828c2ecf20Sopenharmony_ci */ 838c2ecf20Sopenharmony_cistatic acpi_status 848c2ecf20Sopenharmony_ciregister_slot(acpi_handle handle, u32 lvl, void *context, void **rv) 858c2ecf20Sopenharmony_ci{ 868c2ecf20Sopenharmony_ci int device; 878c2ecf20Sopenharmony_ci unsigned long long sun; 888c2ecf20Sopenharmony_ci char name[SLOT_NAME_SIZE]; 898c2ecf20Sopenharmony_ci struct acpi_pci_slot *slot; 908c2ecf20Sopenharmony_ci struct pci_slot *pci_slot; 918c2ecf20Sopenharmony_ci struct pci_bus *pci_bus = context; 928c2ecf20Sopenharmony_ci 938c2ecf20Sopenharmony_ci device = check_slot(handle, &sun); 948c2ecf20Sopenharmony_ci if (device < 0) 958c2ecf20Sopenharmony_ci return AE_OK; 968c2ecf20Sopenharmony_ci 978c2ecf20Sopenharmony_ci /* 988c2ecf20Sopenharmony_ci * There may be multiple PCI functions associated with the same slot. 998c2ecf20Sopenharmony_ci * Check whether PCI slot has already been created for this PCI device. 1008c2ecf20Sopenharmony_ci */ 1018c2ecf20Sopenharmony_ci list_for_each_entry(slot, &slot_list, list) { 1028c2ecf20Sopenharmony_ci pci_slot = slot->pci_slot; 1038c2ecf20Sopenharmony_ci if (pci_slot->bus == pci_bus && pci_slot->number == device) 1048c2ecf20Sopenharmony_ci return AE_OK; 1058c2ecf20Sopenharmony_ci } 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_ci slot = kmalloc(sizeof(*slot), GFP_KERNEL); 1088c2ecf20Sopenharmony_ci if (!slot) 1098c2ecf20Sopenharmony_ci return AE_OK; 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_ci snprintf(name, sizeof(name), "%llu", sun); 1128c2ecf20Sopenharmony_ci pci_slot = pci_create_slot(pci_bus, device, name, NULL); 1138c2ecf20Sopenharmony_ci if (IS_ERR(pci_slot)) { 1148c2ecf20Sopenharmony_ci pr_err("pci_create_slot returned %ld\n", PTR_ERR(pci_slot)); 1158c2ecf20Sopenharmony_ci kfree(slot); 1168c2ecf20Sopenharmony_ci return AE_OK; 1178c2ecf20Sopenharmony_ci } 1188c2ecf20Sopenharmony_ci 1198c2ecf20Sopenharmony_ci slot->pci_slot = pci_slot; 1208c2ecf20Sopenharmony_ci list_add(&slot->list, &slot_list); 1218c2ecf20Sopenharmony_ci 1228c2ecf20Sopenharmony_ci get_device(&pci_bus->dev); 1238c2ecf20Sopenharmony_ci 1248c2ecf20Sopenharmony_ci pr_debug("%p, pci_bus: %x, device: %d, name: %s\n", 1258c2ecf20Sopenharmony_ci pci_slot, pci_bus->number, device, name); 1268c2ecf20Sopenharmony_ci 1278c2ecf20Sopenharmony_ci return AE_OK; 1288c2ecf20Sopenharmony_ci} 1298c2ecf20Sopenharmony_ci 1308c2ecf20Sopenharmony_civoid acpi_pci_slot_enumerate(struct pci_bus *bus) 1318c2ecf20Sopenharmony_ci{ 1328c2ecf20Sopenharmony_ci acpi_handle handle = ACPI_HANDLE(bus->bridge); 1338c2ecf20Sopenharmony_ci 1348c2ecf20Sopenharmony_ci if (handle) { 1358c2ecf20Sopenharmony_ci mutex_lock(&slot_list_lock); 1368c2ecf20Sopenharmony_ci acpi_walk_namespace(ACPI_TYPE_DEVICE, handle, 1, 1378c2ecf20Sopenharmony_ci register_slot, NULL, bus, NULL); 1388c2ecf20Sopenharmony_ci mutex_unlock(&slot_list_lock); 1398c2ecf20Sopenharmony_ci } 1408c2ecf20Sopenharmony_ci} 1418c2ecf20Sopenharmony_ci 1428c2ecf20Sopenharmony_civoid acpi_pci_slot_remove(struct pci_bus *bus) 1438c2ecf20Sopenharmony_ci{ 1448c2ecf20Sopenharmony_ci struct acpi_pci_slot *slot, *tmp; 1458c2ecf20Sopenharmony_ci 1468c2ecf20Sopenharmony_ci mutex_lock(&slot_list_lock); 1478c2ecf20Sopenharmony_ci list_for_each_entry_safe(slot, tmp, &slot_list, list) { 1488c2ecf20Sopenharmony_ci if (slot->pci_slot->bus == bus) { 1498c2ecf20Sopenharmony_ci list_del(&slot->list); 1508c2ecf20Sopenharmony_ci pci_destroy_slot(slot->pci_slot); 1518c2ecf20Sopenharmony_ci put_device(&bus->dev); 1528c2ecf20Sopenharmony_ci kfree(slot); 1538c2ecf20Sopenharmony_ci } 1548c2ecf20Sopenharmony_ci } 1558c2ecf20Sopenharmony_ci mutex_unlock(&slot_list_lock); 1568c2ecf20Sopenharmony_ci} 1578c2ecf20Sopenharmony_ci 1588c2ecf20Sopenharmony_cistatic int do_sta_before_sun(const struct dmi_system_id *d) 1598c2ecf20Sopenharmony_ci{ 1608c2ecf20Sopenharmony_ci pr_info("%s detected: will evaluate _STA before calling _SUN\n", 1618c2ecf20Sopenharmony_ci d->ident); 1628c2ecf20Sopenharmony_ci check_sta_before_sun = 1; 1638c2ecf20Sopenharmony_ci return 0; 1648c2ecf20Sopenharmony_ci} 1658c2ecf20Sopenharmony_ci 1668c2ecf20Sopenharmony_cistatic const struct dmi_system_id acpi_pci_slot_dmi_table[] __initconst = { 1678c2ecf20Sopenharmony_ci /* 1688c2ecf20Sopenharmony_ci * Fujitsu Primequest machines will return 1023 to indicate an 1698c2ecf20Sopenharmony_ci * error if the _SUN method is evaluated on SxFy objects that 1708c2ecf20Sopenharmony_ci * are not present (as indicated by _STA), so for those machines, 1718c2ecf20Sopenharmony_ci * we want to check _STA before evaluating _SUN. 1728c2ecf20Sopenharmony_ci */ 1738c2ecf20Sopenharmony_ci { 1748c2ecf20Sopenharmony_ci .callback = do_sta_before_sun, 1758c2ecf20Sopenharmony_ci .ident = "Fujitsu PRIMEQUEST", 1768c2ecf20Sopenharmony_ci .matches = { 1778c2ecf20Sopenharmony_ci DMI_MATCH(DMI_BIOS_VENDOR, "FUJITSU LIMITED"), 1788c2ecf20Sopenharmony_ci DMI_MATCH(DMI_BIOS_VERSION, "PRIMEQUEST"), 1798c2ecf20Sopenharmony_ci }, 1808c2ecf20Sopenharmony_ci }, 1818c2ecf20Sopenharmony_ci {} 1828c2ecf20Sopenharmony_ci}; 1838c2ecf20Sopenharmony_ci 1848c2ecf20Sopenharmony_civoid __init acpi_pci_slot_init(void) 1858c2ecf20Sopenharmony_ci{ 1868c2ecf20Sopenharmony_ci dmi_check_system(acpi_pci_slot_dmi_table); 1878c2ecf20Sopenharmony_ci} 188