18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0+
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Common ACPI functions for hot plug platforms
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Copyright (C) 2006 Intel Corporation
68c2ecf20Sopenharmony_ci *
78c2ecf20Sopenharmony_ci * All rights reserved.
88c2ecf20Sopenharmony_ci *
98c2ecf20Sopenharmony_ci * Send feedback to <kristen.c.accardi@intel.com>
108c2ecf20Sopenharmony_ci */
118c2ecf20Sopenharmony_ci
128c2ecf20Sopenharmony_ci#include <linux/module.h>
138c2ecf20Sopenharmony_ci#include <linux/moduleparam.h>
148c2ecf20Sopenharmony_ci#include <linux/kernel.h>
158c2ecf20Sopenharmony_ci#include <linux/types.h>
168c2ecf20Sopenharmony_ci#include <linux/pci.h>
178c2ecf20Sopenharmony_ci#include <linux/pci_hotplug.h>
188c2ecf20Sopenharmony_ci#include <linux/acpi.h>
198c2ecf20Sopenharmony_ci#include <linux/pci-acpi.h>
208c2ecf20Sopenharmony_ci#include <linux/slab.h>
218c2ecf20Sopenharmony_ci
228c2ecf20Sopenharmony_ci#define MY_NAME	"acpi_pcihp"
238c2ecf20Sopenharmony_ci
248c2ecf20Sopenharmony_ci#define dbg(fmt, arg...) do { if (debug_acpi) printk(KERN_DEBUG "%s: %s: " fmt, MY_NAME, __func__, ## arg); } while (0)
258c2ecf20Sopenharmony_ci#define err(format, arg...) printk(KERN_ERR "%s: " format, MY_NAME, ## arg)
268c2ecf20Sopenharmony_ci#define info(format, arg...) printk(KERN_INFO "%s: " format, MY_NAME, ## arg)
278c2ecf20Sopenharmony_ci#define warn(format, arg...) printk(KERN_WARNING "%s: " format, MY_NAME, ## arg)
288c2ecf20Sopenharmony_ci
298c2ecf20Sopenharmony_ci#define	METHOD_NAME__SUN	"_SUN"
308c2ecf20Sopenharmony_ci#define	METHOD_NAME_OSHP	"OSHP"
318c2ecf20Sopenharmony_ci
328c2ecf20Sopenharmony_cistatic bool debug_acpi;
338c2ecf20Sopenharmony_ci
348c2ecf20Sopenharmony_ci/* acpi_run_oshp - get control of hotplug from the firmware
358c2ecf20Sopenharmony_ci *
368c2ecf20Sopenharmony_ci * @handle - the handle of the hotplug controller.
378c2ecf20Sopenharmony_ci */
388c2ecf20Sopenharmony_cistatic acpi_status acpi_run_oshp(acpi_handle handle)
398c2ecf20Sopenharmony_ci{
408c2ecf20Sopenharmony_ci	acpi_status		status;
418c2ecf20Sopenharmony_ci	struct acpi_buffer	string = { ACPI_ALLOCATE_BUFFER, NULL };
428c2ecf20Sopenharmony_ci
438c2ecf20Sopenharmony_ci	acpi_get_name(handle, ACPI_FULL_PATHNAME, &string);
448c2ecf20Sopenharmony_ci
458c2ecf20Sopenharmony_ci	/* run OSHP */
468c2ecf20Sopenharmony_ci	status = acpi_evaluate_object(handle, METHOD_NAME_OSHP, NULL, NULL);
478c2ecf20Sopenharmony_ci	if (ACPI_FAILURE(status))
488c2ecf20Sopenharmony_ci		if (status != AE_NOT_FOUND)
498c2ecf20Sopenharmony_ci			printk(KERN_ERR "%s:%s OSHP fails=0x%x\n",
508c2ecf20Sopenharmony_ci			       __func__, (char *)string.pointer, status);
518c2ecf20Sopenharmony_ci		else
528c2ecf20Sopenharmony_ci			dbg("%s:%s OSHP not found\n",
538c2ecf20Sopenharmony_ci			    __func__, (char *)string.pointer);
548c2ecf20Sopenharmony_ci	else
558c2ecf20Sopenharmony_ci		pr_debug("%s:%s OSHP passes\n", __func__,
568c2ecf20Sopenharmony_ci			(char *)string.pointer);
578c2ecf20Sopenharmony_ci
588c2ecf20Sopenharmony_ci	kfree(string.pointer);
598c2ecf20Sopenharmony_ci	return status;
608c2ecf20Sopenharmony_ci}
618c2ecf20Sopenharmony_ci
628c2ecf20Sopenharmony_ci/**
638c2ecf20Sopenharmony_ci * acpi_get_hp_hw_control_from_firmware
648c2ecf20Sopenharmony_ci * @pdev: the pci_dev of the bridge that has a hotplug controller
658c2ecf20Sopenharmony_ci *
668c2ecf20Sopenharmony_ci * Attempt to take hotplug control from firmware.
678c2ecf20Sopenharmony_ci */
688c2ecf20Sopenharmony_ciint acpi_get_hp_hw_control_from_firmware(struct pci_dev *pdev)
698c2ecf20Sopenharmony_ci{
708c2ecf20Sopenharmony_ci	const struct pci_host_bridge *host;
718c2ecf20Sopenharmony_ci	const struct acpi_pci_root *root;
728c2ecf20Sopenharmony_ci	acpi_status status;
738c2ecf20Sopenharmony_ci	acpi_handle chandle, handle;
748c2ecf20Sopenharmony_ci	struct acpi_buffer string = { ACPI_ALLOCATE_BUFFER, NULL };
758c2ecf20Sopenharmony_ci
768c2ecf20Sopenharmony_ci	/*
778c2ecf20Sopenharmony_ci	 * If there's no ACPI host bridge (i.e., ACPI support is compiled
788c2ecf20Sopenharmony_ci	 * into the kernel but the hardware platform doesn't support ACPI),
798c2ecf20Sopenharmony_ci	 * there's nothing to do here.
808c2ecf20Sopenharmony_ci	 */
818c2ecf20Sopenharmony_ci	host = pci_find_host_bridge(pdev->bus);
828c2ecf20Sopenharmony_ci	root = acpi_pci_find_root(ACPI_HANDLE(&host->dev));
838c2ecf20Sopenharmony_ci	if (!root)
848c2ecf20Sopenharmony_ci		return 0;
858c2ecf20Sopenharmony_ci
868c2ecf20Sopenharmony_ci	/*
878c2ecf20Sopenharmony_ci	 * If _OSC exists, it determines whether we're allowed to manage
888c2ecf20Sopenharmony_ci	 * the SHPC.  We executed it while enumerating the host bridge.
898c2ecf20Sopenharmony_ci	 */
908c2ecf20Sopenharmony_ci	if (root->osc_support_set) {
918c2ecf20Sopenharmony_ci		if (host->native_shpc_hotplug)
928c2ecf20Sopenharmony_ci			return 0;
938c2ecf20Sopenharmony_ci		return -ENODEV;
948c2ecf20Sopenharmony_ci	}
958c2ecf20Sopenharmony_ci
968c2ecf20Sopenharmony_ci	/*
978c2ecf20Sopenharmony_ci	 * In the absence of _OSC, we're always allowed to manage the SHPC.
988c2ecf20Sopenharmony_ci	 * However, if an OSHP method is present, we must execute it so the
998c2ecf20Sopenharmony_ci	 * firmware can transfer control to the OS, e.g., direct interrupts
1008c2ecf20Sopenharmony_ci	 * to the OS instead of to the firmware.
1018c2ecf20Sopenharmony_ci	 *
1028c2ecf20Sopenharmony_ci	 * N.B. The PCI Firmware Spec (r3.2, sec 4.8) does not endorse
1038c2ecf20Sopenharmony_ci	 * searching up the ACPI hierarchy, so the loops below are suspect.
1048c2ecf20Sopenharmony_ci	 */
1058c2ecf20Sopenharmony_ci	handle = ACPI_HANDLE(&pdev->dev);
1068c2ecf20Sopenharmony_ci	if (!handle) {
1078c2ecf20Sopenharmony_ci		/*
1088c2ecf20Sopenharmony_ci		 * This hotplug controller was not listed in the ACPI name
1098c2ecf20Sopenharmony_ci		 * space at all. Try to get ACPI handle of parent PCI bus.
1108c2ecf20Sopenharmony_ci		 */
1118c2ecf20Sopenharmony_ci		struct pci_bus *pbus;
1128c2ecf20Sopenharmony_ci		for (pbus = pdev->bus; pbus; pbus = pbus->parent) {
1138c2ecf20Sopenharmony_ci			handle = acpi_pci_get_bridge_handle(pbus);
1148c2ecf20Sopenharmony_ci			if (handle)
1158c2ecf20Sopenharmony_ci				break;
1168c2ecf20Sopenharmony_ci		}
1178c2ecf20Sopenharmony_ci	}
1188c2ecf20Sopenharmony_ci
1198c2ecf20Sopenharmony_ci	while (handle) {
1208c2ecf20Sopenharmony_ci		acpi_get_name(handle, ACPI_FULL_PATHNAME, &string);
1218c2ecf20Sopenharmony_ci		pci_info(pdev, "Requesting control of SHPC hotplug via OSHP (%s)\n",
1228c2ecf20Sopenharmony_ci			 (char *)string.pointer);
1238c2ecf20Sopenharmony_ci		status = acpi_run_oshp(handle);
1248c2ecf20Sopenharmony_ci		if (ACPI_SUCCESS(status))
1258c2ecf20Sopenharmony_ci			goto got_one;
1268c2ecf20Sopenharmony_ci		if (acpi_is_root_bridge(handle))
1278c2ecf20Sopenharmony_ci			break;
1288c2ecf20Sopenharmony_ci		chandle = handle;
1298c2ecf20Sopenharmony_ci		status = acpi_get_parent(chandle, &handle);
1308c2ecf20Sopenharmony_ci		if (ACPI_FAILURE(status))
1318c2ecf20Sopenharmony_ci			break;
1328c2ecf20Sopenharmony_ci	}
1338c2ecf20Sopenharmony_ci
1348c2ecf20Sopenharmony_ci	pci_info(pdev, "Cannot get control of SHPC hotplug\n");
1358c2ecf20Sopenharmony_ci	kfree(string.pointer);
1368c2ecf20Sopenharmony_ci	return -ENODEV;
1378c2ecf20Sopenharmony_cigot_one:
1388c2ecf20Sopenharmony_ci	pci_info(pdev, "Gained control of SHPC hotplug (%s)\n",
1398c2ecf20Sopenharmony_ci		 (char *)string.pointer);
1408c2ecf20Sopenharmony_ci	kfree(string.pointer);
1418c2ecf20Sopenharmony_ci	return 0;
1428c2ecf20Sopenharmony_ci}
1438c2ecf20Sopenharmony_ciEXPORT_SYMBOL(acpi_get_hp_hw_control_from_firmware);
1448c2ecf20Sopenharmony_ci
1458c2ecf20Sopenharmony_cistatic int pcihp_is_ejectable(acpi_handle handle)
1468c2ecf20Sopenharmony_ci{
1478c2ecf20Sopenharmony_ci	acpi_status status;
1488c2ecf20Sopenharmony_ci	unsigned long long removable;
1498c2ecf20Sopenharmony_ci	if (!acpi_has_method(handle, "_ADR"))
1508c2ecf20Sopenharmony_ci		return 0;
1518c2ecf20Sopenharmony_ci	if (acpi_has_method(handle, "_EJ0"))
1528c2ecf20Sopenharmony_ci		return 1;
1538c2ecf20Sopenharmony_ci	status = acpi_evaluate_integer(handle, "_RMV", NULL, &removable);
1548c2ecf20Sopenharmony_ci	if (ACPI_SUCCESS(status) && removable)
1558c2ecf20Sopenharmony_ci		return 1;
1568c2ecf20Sopenharmony_ci	return 0;
1578c2ecf20Sopenharmony_ci}
1588c2ecf20Sopenharmony_ci
1598c2ecf20Sopenharmony_ci/**
1608c2ecf20Sopenharmony_ci * acpi_pcihp_check_ejectable - check if handle is ejectable ACPI PCI slot
1618c2ecf20Sopenharmony_ci * @pbus: the PCI bus of the PCI slot corresponding to 'handle'
1628c2ecf20Sopenharmony_ci * @handle: ACPI handle to check
1638c2ecf20Sopenharmony_ci *
1648c2ecf20Sopenharmony_ci * Return 1 if handle is ejectable PCI slot, 0 otherwise.
1658c2ecf20Sopenharmony_ci */
1668c2ecf20Sopenharmony_ciint acpi_pci_check_ejectable(struct pci_bus *pbus, acpi_handle handle)
1678c2ecf20Sopenharmony_ci{
1688c2ecf20Sopenharmony_ci	acpi_handle bridge_handle, parent_handle;
1698c2ecf20Sopenharmony_ci
1708c2ecf20Sopenharmony_ci	bridge_handle = acpi_pci_get_bridge_handle(pbus);
1718c2ecf20Sopenharmony_ci	if (!bridge_handle)
1728c2ecf20Sopenharmony_ci		return 0;
1738c2ecf20Sopenharmony_ci	if ((ACPI_FAILURE(acpi_get_parent(handle, &parent_handle))))
1748c2ecf20Sopenharmony_ci		return 0;
1758c2ecf20Sopenharmony_ci	if (bridge_handle != parent_handle)
1768c2ecf20Sopenharmony_ci		return 0;
1778c2ecf20Sopenharmony_ci	return pcihp_is_ejectable(handle);
1788c2ecf20Sopenharmony_ci}
1798c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(acpi_pci_check_ejectable);
1808c2ecf20Sopenharmony_ci
1818c2ecf20Sopenharmony_cistatic acpi_status
1828c2ecf20Sopenharmony_cicheck_hotplug(acpi_handle handle, u32 lvl, void *context, void **rv)
1838c2ecf20Sopenharmony_ci{
1848c2ecf20Sopenharmony_ci	int *found = (int *)context;
1858c2ecf20Sopenharmony_ci	if (pcihp_is_ejectable(handle)) {
1868c2ecf20Sopenharmony_ci		*found = 1;
1878c2ecf20Sopenharmony_ci		return AE_CTRL_TERMINATE;
1888c2ecf20Sopenharmony_ci	}
1898c2ecf20Sopenharmony_ci	return AE_OK;
1908c2ecf20Sopenharmony_ci}
1918c2ecf20Sopenharmony_ci
1928c2ecf20Sopenharmony_ci/**
1938c2ecf20Sopenharmony_ci * acpi_pci_detect_ejectable - check if the PCI bus has ejectable slots
1948c2ecf20Sopenharmony_ci * @handle: handle of the PCI bus to scan
1958c2ecf20Sopenharmony_ci *
1968c2ecf20Sopenharmony_ci * Returns 1 if the PCI bus has ACPI based ejectable slots, 0 otherwise.
1978c2ecf20Sopenharmony_ci */
1988c2ecf20Sopenharmony_ciint acpi_pci_detect_ejectable(acpi_handle handle)
1998c2ecf20Sopenharmony_ci{
2008c2ecf20Sopenharmony_ci	int found = 0;
2018c2ecf20Sopenharmony_ci
2028c2ecf20Sopenharmony_ci	if (!handle)
2038c2ecf20Sopenharmony_ci		return found;
2048c2ecf20Sopenharmony_ci
2058c2ecf20Sopenharmony_ci	acpi_walk_namespace(ACPI_TYPE_DEVICE, handle, 1,
2068c2ecf20Sopenharmony_ci			    check_hotplug, NULL, (void *)&found, NULL);
2078c2ecf20Sopenharmony_ci	return found;
2088c2ecf20Sopenharmony_ci}
2098c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(acpi_pci_detect_ejectable);
2108c2ecf20Sopenharmony_ci
2118c2ecf20Sopenharmony_cimodule_param(debug_acpi, bool, 0644);
2128c2ecf20Sopenharmony_ciMODULE_PARM_DESC(debug_acpi, "Debugging mode for ACPI enabled or not");
213