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