xref: /openbmc/linux/drivers/acpi/pci_slot.c (revision cbecf716ca618fd44feda6bd9a64a8179d031fc5)
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