1510 lines · 41.0 KB · C
RawDownload
// SPDX-License-Identifier: GPL-2.0-or-later/* *  acpi_bus.c - ACPI Bus Driver ($Revision: 80 $) * *  Copyright (C) 2001, 2002 Paul Diefenbaugh <paul.s.diefenbaugh@intel.com> */ #define pr_fmt(fmt) "ACPI: " fmt #include <linux/module.h>#include <linux/init.h>#include <linux/ioport.h>#include <linux/kernel.h>#include <linux/list.h>#include <linux/sched.h>#include <linux/pm.h>#include <linux/device.h>#include <linux/proc_fs.h>#include <linux/acpi.h>#include <linux/slab.h>#include <linux/regulator/machine.h>#include <linux/workqueue.h>#include <linux/reboot.h>#include <linux/delay.h>#ifdef CONFIG_X86#include <asm/mpspec.h>#include <linux/dmi.h>#endif#include <linux/acpi_viot.h>#include <linux/pci.h>#include <acpi/apei.h>#include <linux/suspend.h>#include <linux/prmt.h> #include "internal.h" struct acpi_device *acpi_root;struct proc_dir_entry *acpi_root_dir;EXPORT_SYMBOL(acpi_root_dir); #ifdef CONFIG_X86#ifdef CONFIG_ACPI_CUSTOM_DSDTstatic inline int set_copy_dsdt(const struct dmi_system_id *id){	return 0;}#elsestatic int set_copy_dsdt(const struct dmi_system_id *id){	pr_notice("%s detected - force copy of DSDT to local memory\n", id->ident);	acpi_gbl_copy_dsdt_locally = 1;	return 0;}#endif static const struct dmi_system_id dsdt_dmi_table[] __initconst = {	/*	 * Invoke DSDT corruption work-around on all Toshiba Satellite.	 * https://bugzilla.kernel.org/show_bug.cgi?id=14679	 */	{	 .callback = set_copy_dsdt,	 .ident = "TOSHIBA Satellite",	 .matches = {		DMI_MATCH(DMI_SYS_VENDOR, "TOSHIBA"),		DMI_MATCH(DMI_PRODUCT_NAME, "Satellite"),		},	},	{}};#endif /* --------------------------------------------------------------------------                                Device Management   -------------------------------------------------------------------------- */ acpi_status acpi_bus_get_status_handle(acpi_handle handle,				       unsigned long long *sta){	acpi_status status; 	status = acpi_evaluate_integer(handle, "_STA", NULL, sta);	if (ACPI_SUCCESS(status))		return AE_OK; 	if (status == AE_NOT_FOUND) {		*sta = ACPI_STA_DEVICE_PRESENT | ACPI_STA_DEVICE_ENABLED |		       ACPI_STA_DEVICE_UI      | ACPI_STA_DEVICE_FUNCTIONING;		return AE_OK;	}	return status;}EXPORT_SYMBOL_GPL(acpi_bus_get_status_handle); int acpi_bus_get_status(struct acpi_device *device){	acpi_status status;	unsigned long long sta; 	if (acpi_device_override_status(device, &sta)) {		acpi_set_device_status(device, sta);		return 0;	} 	/* Battery devices must have their deps met before calling _STA */	if (acpi_device_is_battery(device) && device->dep_unmet) {		acpi_set_device_status(device, 0);		return 0;	} 	status = acpi_bus_get_status_handle(device->handle, &sta);	if (ACPI_FAILURE(status))		return -ENODEV; 	if (!device->status.present && device->status.enabled) {		pr_info(FW_BUG "Device [%s] status [%08x]: not present and enabled\n",			device->pnp.bus_id, (u32)sta);		device->status.enabled = 0;		/*		 * The status is clearly invalid, so clear the functional bit as		 * well to avoid attempting to use the device.		 */		device->status.functional = 0;	} 	acpi_set_device_status(device, sta); 	if (device->status.functional && !device->status.present) {		pr_debug("Device [%s] status [%08x]: functional but not present\n",			 device->pnp.bus_id, (u32)sta);	} 	pr_debug("Device [%s] status [%08x]\n", device->pnp.bus_id, (u32)sta);	return 0;}EXPORT_SYMBOL(acpi_bus_get_status); void acpi_bus_private_data_handler(acpi_handle handle,				   void *context){	return;}EXPORT_SYMBOL(acpi_bus_private_data_handler); int acpi_bus_attach_private_data(acpi_handle handle, void *data){	acpi_status status; 	status = acpi_attach_data(handle,			acpi_bus_private_data_handler, data);	if (ACPI_FAILURE(status)) {		acpi_handle_debug(handle, "Error attaching device data\n");		return -ENODEV;	} 	return 0;}EXPORT_SYMBOL_GPL(acpi_bus_attach_private_data); int acpi_bus_get_private_data(acpi_handle handle, void **data){	acpi_status status; 	if (!data)		return -EINVAL; 	status = acpi_get_data(handle, acpi_bus_private_data_handler, data);	if (ACPI_FAILURE(status)) {		acpi_handle_debug(handle, "No context for object\n");		return -ENODEV;	} 	return 0;}EXPORT_SYMBOL_GPL(acpi_bus_get_private_data); void acpi_bus_detach_private_data(acpi_handle handle){	acpi_detach_data(handle, acpi_bus_private_data_handler);}EXPORT_SYMBOL_GPL(acpi_bus_detach_private_data); static void acpi_dump_osc_data(acpi_handle handle, const guid_t *guid, int rev,			       struct acpi_buffer *cap){	u32 *capbuf = cap->pointer;	int i; 	acpi_handle_debug(handle, "_OSC: UUID: %pUL, rev: %d\n", guid, rev);	for (i = 0; i < cap->length / sizeof(u32); i++)		acpi_handle_debug(handle, "_OSC: capabilities DWORD %i: [%08x]\n",				  i, capbuf[i]);} #define OSC_ERROR_MASK 	(OSC_REQUEST_ERROR | OSC_INVALID_UUID_ERROR | \			 OSC_INVALID_REVISION_ERROR | \			 OSC_CAPABILITIES_MASK_ERROR) static int acpi_eval_osc(acpi_handle handle, guid_t *guid, int rev,			 struct acpi_buffer *cap,			 union acpi_object in_params[at_least 4],			 struct acpi_buffer *output){	struct acpi_object_list input;	union acpi_object *out_obj;	acpi_status status; 	in_params[0].type = ACPI_TYPE_BUFFER;	in_params[0].buffer.length = sizeof(*guid);	in_params[0].buffer.pointer = (u8 *)guid;	in_params[1].type = ACPI_TYPE_INTEGER;	in_params[1].integer.value = rev;	in_params[2].type = ACPI_TYPE_INTEGER;	in_params[2].integer.value = cap->length / sizeof(u32);	in_params[3].type = ACPI_TYPE_BUFFER;	in_params[3].buffer.length = cap->length;	in_params[3].buffer.pointer = cap->pointer;	input.pointer = in_params;	input.count = 4; 	output->length = ACPI_ALLOCATE_BUFFER;	output->pointer = NULL; 	status = acpi_evaluate_object(handle, "_OSC", &input, output);	if (ACPI_FAILURE(status) || !output->length)		return -ENODATA; 	out_obj = output->pointer;	if (out_obj->type != ACPI_TYPE_BUFFER ||	    out_obj->buffer.length != cap->length) {		acpi_handle_debug(handle, "Invalid _OSC return buffer\n");		acpi_dump_osc_data(handle, guid, rev, cap);		ACPI_FREE(out_obj);		return -ENODATA;	} 	return 0;} static bool acpi_osc_error_check(acpi_handle handle, guid_t *guid, int rev,				 struct acpi_buffer *cap, u32 *retbuf){	/* Only take defined error bits into account. */	u32 errors = retbuf[OSC_QUERY_DWORD] & OSC_ERROR_MASK;	u32 *capbuf = cap->pointer;	bool fail; 	/*	 * If OSC_QUERY_ENABLE is set, ignore the "capabilities masked"	 * bit because it merely means that some features have not been	 * acknowledged which is not unexpected.	 */	if (capbuf[OSC_QUERY_DWORD] & OSC_QUERY_ENABLE)		errors &= ~OSC_CAPABILITIES_MASK_ERROR; 	if (!errors)		return false; 	acpi_dump_osc_data(handle, guid, rev, cap);	/*	 * As a rule, fail only if OSC_QUERY_ENABLE is set because otherwise the	 * acknowledged features need to be controlled.	 */	fail = !!(capbuf[OSC_QUERY_DWORD] & OSC_QUERY_ENABLE); 	if (errors & OSC_REQUEST_ERROR)		acpi_handle_debug(handle, "_OSC: request failed\n"); 	if (errors & OSC_INVALID_UUID_ERROR) {		acpi_handle_debug(handle, "_OSC: invalid UUID\n");		/*		 * Always fail if this bit is set because it means that the		 * request could not be processed.		 */		fail = true;	} 	if (errors & OSC_INVALID_REVISION_ERROR)		acpi_handle_debug(handle, "_OSC: invalid revision\n"); 	if (errors & OSC_CAPABILITIES_MASK_ERROR)		acpi_handle_debug(handle, "_OSC: capability bits masked\n"); 	return fail;} acpi_status acpi_run_osc(acpi_handle handle, struct acpi_osc_context *context){	union acpi_object in_params[4], *out_obj;	struct acpi_buffer output;	acpi_status status = AE_OK;	guid_t guid;	u32 *retbuf;	int ret; 	if (!context || !context->cap.pointer ||	    context->cap.length < 2 * sizeof(u32) ||	    guid_parse(context->uuid_str, &guid))		return AE_BAD_PARAMETER; 	ret = acpi_eval_osc(handle, &guid, context->rev, &context->cap,			    in_params, &output);	if (ret)		return AE_ERROR; 	out_obj = output.pointer;	retbuf = (u32 *)out_obj->buffer.pointer; 	if (acpi_osc_error_check(handle, &guid, context->rev, &context->cap, retbuf)) {		status = AE_ERROR;		goto out;	} 	context->ret.length = out_obj->buffer.length;	context->ret.pointer = kmemdup(retbuf, context->ret.length, GFP_KERNEL);	if (!context->ret.pointer) {		status =  AE_NO_MEMORY;		goto out;	}	status =  AE_OK; out:	ACPI_FREE(out_obj);	return status;}EXPORT_SYMBOL(acpi_run_osc); static int acpi_osc_handshake(acpi_handle handle, const char *uuid_str,			      int rev, u32 *capbuf, size_t bufsize){	union acpi_object in_params[4], *out_obj;	struct acpi_object_list input;	struct acpi_buffer cap = {		.pointer = capbuf,		.length = bufsize * sizeof(u32),	};	struct acpi_buffer output;	u32 *retbuf, test, errors;	guid_t guid;	int ret, i; 	if (!capbuf || bufsize < 2 || guid_parse(uuid_str, &guid))		return -EINVAL; 	/* First evaluate _OSC with OSC_QUERY_ENABLE set. */	capbuf[OSC_QUERY_DWORD] = OSC_QUERY_ENABLE; 	ret = acpi_eval_osc(handle, &guid, rev, &cap, in_params, &output);	if (ret)		return ret; 	out_obj = output.pointer;	retbuf = (u32 *)out_obj->buffer.pointer; 	if (acpi_osc_error_check(handle, &guid, rev, &cap, retbuf)) {		ret = -ENODATA;		goto out;	} 	/*	 * Clear the feature bits in the capabilities buffer that have not been	 * acknowledged and clear the return buffer.	 */	for (i = OSC_QUERY_DWORD + 1, test = 0; i < bufsize; i++) {		capbuf[i] &= retbuf[i];		test |= capbuf[i];		retbuf[i] = 0;	}	/*	 * If none of the feature bits have been acknowledged, there's nothing	 * more to do.  capbuf[] contains a feature mask of all zeros.	 */	if (!test)		goto out; 	retbuf[OSC_QUERY_DWORD] = 0;	/*	 * Now evaluate _OSC again (directly) with OSC_QUERY_ENABLE clear and	 * the updated input and output buffers used before.  Since the feature	 * bits that were clear in the return buffer from the previous _OSC	 * evaluation are also clear in the capabilities buffer now, this _OSC	 * evaluation is not expected to fail.	 */	capbuf[OSC_QUERY_DWORD] = 0;	/* Reuse in_params[] populated by acpi_eval_osc(). */	input.pointer = in_params;	input.count = 4; 	if (ACPI_FAILURE(acpi_evaluate_object(handle, "_OSC", &input, &output))) {		ret = -ENODATA;		goto out;	} 	/*	 * Clear the feature bits in capbuf[] that have not been acknowledged.	 * After that, capbuf[] contains the resultant feature mask.	 */	for (i = OSC_QUERY_DWORD + 1, test = 0; i < bufsize; i++) {		test |= capbuf[i] & ~retbuf[i];		capbuf[i] &= retbuf[i];	} 	errors = retbuf[OSC_QUERY_DWORD] & OSC_ERROR_MASK;	/*	 * Some platforms set OSC_CAPABILITIES_MASK_ERROR even though they	 * acknowledge all of the requested features, so avoid complaining in	 * those cases unless any other error bits are also set.	 */	if (errors && (test || errors != OSC_CAPABILITIES_MASK_ERROR)) {		/*		 * Complain about the unexpected errors and print diagnostic		 * information related to them.		 */		acpi_handle_err(handle, "_OSC: errors while processing control request\n");		acpi_handle_err(handle, "_OSC: some features may be missing\n");		acpi_osc_error_check(handle, &guid, rev, &cap, retbuf);	} out:	ACPI_FREE(out_obj);	return ret;} bool osc_sb_apei_support_acked; /* * ACPI 6.0 Section 8.4.4.2 Idle State Coordination * OSPM supports platform coordinated low power idle(LPI) states */bool osc_pc_lpi_support_confirmed;EXPORT_SYMBOL_GPL(osc_pc_lpi_support_confirmed); /* * ACPI 6.2 Section 6.2.11.2 'Platform-Wide OSPM Capabilities': *   Starting with ACPI Specification 6.2, all _CPC registers can be in *   PCC, System Memory, System IO, or Functional Fixed Hardware address *   spaces. OSPM support for this more flexible register space scheme is *   indicated by the “Flexible Address Space for CPPC Registers” _OSC bit. * * Otherwise (cf ACPI 6.1, s8.4.7.1.1.X), _CPC registers must be in: * - PCC or Functional Fixed Hardware address space if defined * - SystemMemory address space (NULL register) if not defined */bool osc_cpc_flexible_adr_space_confirmed;EXPORT_SYMBOL_GPL(osc_cpc_flexible_adr_space_confirmed); /* * ACPI 6.4 Operating System Capabilities for USB. */bool osc_sb_native_usb4_support_confirmed;EXPORT_SYMBOL_GPL(osc_sb_native_usb4_support_confirmed); bool osc_sb_cppc2_support_acked; static void acpi_bus_osc_negotiate_platform_control(void){	static const u8 sb_uuid_str[] = "0811B06E-4A27-44F9-8D60-3CBBC22E7B48";	u32 capbuf[2], feature_mask;	acpi_handle handle; 	feature_mask = OSC_SB_PR3_SUPPORT | OSC_SB_HOTPLUG_OST_SUPPORT |			OSC_SB_PCLPI_SUPPORT | OSC_SB_OVER_16_PSTATES_SUPPORT |			OSC_SB_GED_SUPPORT | OSC_SB_IRQ_RESOURCE_SOURCE_SUPPORT; 	if (IS_ENABLED(CONFIG_ARM64) || IS_ENABLED(CONFIG_X86))		feature_mask |= OSC_SB_GENERIC_INITIATOR_SUPPORT; 	if (IS_ENABLED(CONFIG_ACPI_CPPC_LIB)) {		feature_mask |= OSC_SB_CPC_SUPPORT | OSC_SB_CPCV2_SUPPORT |				OSC_SB_CPC_FLEXIBLE_ADR_SPACE;		if (IS_ENABLED(CONFIG_SCHED_MC_PRIO))			feature_mask |= OSC_SB_CPC_DIVERSE_HIGH_SUPPORT;	} 	if (IS_ENABLED(CONFIG_ACPI_PROCESSOR_AGGREGATOR))		feature_mask |= OSC_SB_PAD_SUPPORT; 	if (IS_ENABLED(CONFIG_ACPI_PROCESSOR))		feature_mask |= OSC_SB_PPC_OST_SUPPORT; 	if (IS_ENABLED(CONFIG_ACPI_THERMAL))		feature_mask |= OSC_SB_FAST_THERMAL_SAMPLING_SUPPORT; 	if (IS_ENABLED(CONFIG_ACPI_BATTERY))		feature_mask |= OSC_SB_BATTERY_CHARGE_LIMITING_SUPPORT; 	if (IS_ENABLED(CONFIG_ACPI_PRMT))		feature_mask |= OSC_SB_PRM_SUPPORT; 	if (IS_ENABLED(CONFIG_ACPI_FFH))		feature_mask |= OSC_SB_FFH_OPR_SUPPORT; 	if (IS_ENABLED(CONFIG_USB4))		feature_mask |= OSC_SB_NATIVE_USB4_SUPPORT; 	if (!ghes_disable)		feature_mask |= OSC_SB_APEI_SUPPORT; 	if (ACPI_FAILURE(acpi_get_handle(NULL, "\\_SB", &handle)))		return;

Lines 1–500 of 1510.

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t