18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Copyright (C) 2005 Intel Corporation
48c2ecf20Sopenharmony_ci * Copyright (C) 2009 Hewlett-Packard Development Company, L.P.
58c2ecf20Sopenharmony_ci *
68c2ecf20Sopenharmony_ci *      Venkatesh Pallipadi <venkatesh.pallipadi@intel.com>
78c2ecf20Sopenharmony_ci *      - Added _PDC for platforms with Intel CPUs
88c2ecf20Sopenharmony_ci */
98c2ecf20Sopenharmony_ci
108c2ecf20Sopenharmony_ci#define pr_fmt(fmt) "ACPI: " fmt
118c2ecf20Sopenharmony_ci
128c2ecf20Sopenharmony_ci#include <linux/dmi.h>
138c2ecf20Sopenharmony_ci#include <linux/slab.h>
148c2ecf20Sopenharmony_ci#include <linux/acpi.h>
158c2ecf20Sopenharmony_ci#include <acpi/processor.h>
168c2ecf20Sopenharmony_ci
178c2ecf20Sopenharmony_ci#include <xen/xen.h>
188c2ecf20Sopenharmony_ci
198c2ecf20Sopenharmony_ci#include "internal.h"
208c2ecf20Sopenharmony_ci
218c2ecf20Sopenharmony_ci#define _COMPONENT              ACPI_PROCESSOR_COMPONENT
228c2ecf20Sopenharmony_ciACPI_MODULE_NAME("processor_pdc");
238c2ecf20Sopenharmony_ci
248c2ecf20Sopenharmony_cistatic bool __init processor_physically_present(acpi_handle handle)
258c2ecf20Sopenharmony_ci{
268c2ecf20Sopenharmony_ci	int cpuid, type;
278c2ecf20Sopenharmony_ci	u32 acpi_id;
288c2ecf20Sopenharmony_ci	acpi_status status;
298c2ecf20Sopenharmony_ci	acpi_object_type acpi_type;
308c2ecf20Sopenharmony_ci	unsigned long long tmp;
318c2ecf20Sopenharmony_ci	union acpi_object object = { 0 };
328c2ecf20Sopenharmony_ci	struct acpi_buffer buffer = { sizeof(union acpi_object), &object };
338c2ecf20Sopenharmony_ci
348c2ecf20Sopenharmony_ci	status = acpi_get_type(handle, &acpi_type);
358c2ecf20Sopenharmony_ci	if (ACPI_FAILURE(status))
368c2ecf20Sopenharmony_ci		return false;
378c2ecf20Sopenharmony_ci
388c2ecf20Sopenharmony_ci	switch (acpi_type) {
398c2ecf20Sopenharmony_ci	case ACPI_TYPE_PROCESSOR:
408c2ecf20Sopenharmony_ci		status = acpi_evaluate_object(handle, NULL, NULL, &buffer);
418c2ecf20Sopenharmony_ci		if (ACPI_FAILURE(status))
428c2ecf20Sopenharmony_ci			return false;
438c2ecf20Sopenharmony_ci		acpi_id = object.processor.proc_id;
448c2ecf20Sopenharmony_ci		break;
458c2ecf20Sopenharmony_ci	case ACPI_TYPE_DEVICE:
468c2ecf20Sopenharmony_ci		status = acpi_evaluate_integer(handle, "_UID", NULL, &tmp);
478c2ecf20Sopenharmony_ci		if (ACPI_FAILURE(status))
488c2ecf20Sopenharmony_ci			return false;
498c2ecf20Sopenharmony_ci		acpi_id = tmp;
508c2ecf20Sopenharmony_ci		break;
518c2ecf20Sopenharmony_ci	default:
528c2ecf20Sopenharmony_ci		return false;
538c2ecf20Sopenharmony_ci	}
548c2ecf20Sopenharmony_ci
558c2ecf20Sopenharmony_ci	if (xen_initial_domain())
568c2ecf20Sopenharmony_ci		/*
578c2ecf20Sopenharmony_ci		 * When running as a Xen dom0 the number of processors Linux
588c2ecf20Sopenharmony_ci		 * sees can be different from the real number of processors on
598c2ecf20Sopenharmony_ci		 * the system, and we still need to execute _PDC for all of
608c2ecf20Sopenharmony_ci		 * them.
618c2ecf20Sopenharmony_ci		 */
628c2ecf20Sopenharmony_ci		return xen_processor_present(acpi_id);
638c2ecf20Sopenharmony_ci
648c2ecf20Sopenharmony_ci	type = (acpi_type == ACPI_TYPE_DEVICE) ? 1 : 0;
658c2ecf20Sopenharmony_ci	cpuid = acpi_get_cpuid(handle, type, acpi_id);
668c2ecf20Sopenharmony_ci
678c2ecf20Sopenharmony_ci	return !invalid_logical_cpuid(cpuid);
688c2ecf20Sopenharmony_ci}
698c2ecf20Sopenharmony_ci
708c2ecf20Sopenharmony_cistatic void acpi_set_pdc_bits(u32 *buf)
718c2ecf20Sopenharmony_ci{
728c2ecf20Sopenharmony_ci	buf[0] = ACPI_PDC_REVISION_ID;
738c2ecf20Sopenharmony_ci	buf[1] = 1;
748c2ecf20Sopenharmony_ci
758c2ecf20Sopenharmony_ci	/* Enable coordination with firmware's _TSD info */
768c2ecf20Sopenharmony_ci	buf[2] = ACPI_PDC_SMP_T_SWCOORD;
778c2ecf20Sopenharmony_ci
788c2ecf20Sopenharmony_ci	/* Twiddle arch-specific bits needed for _PDC */
798c2ecf20Sopenharmony_ci	arch_acpi_set_pdc_bits(buf);
808c2ecf20Sopenharmony_ci}
818c2ecf20Sopenharmony_ci
828c2ecf20Sopenharmony_cistatic struct acpi_object_list *acpi_processor_alloc_pdc(void)
838c2ecf20Sopenharmony_ci{
848c2ecf20Sopenharmony_ci	struct acpi_object_list *obj_list;
858c2ecf20Sopenharmony_ci	union acpi_object *obj;
868c2ecf20Sopenharmony_ci	u32 *buf;
878c2ecf20Sopenharmony_ci
888c2ecf20Sopenharmony_ci	/* allocate and initialize pdc. It will be used later. */
898c2ecf20Sopenharmony_ci	obj_list = kmalloc(sizeof(struct acpi_object_list), GFP_KERNEL);
908c2ecf20Sopenharmony_ci	if (!obj_list)
918c2ecf20Sopenharmony_ci		goto out;
928c2ecf20Sopenharmony_ci
938c2ecf20Sopenharmony_ci	obj = kmalloc(sizeof(union acpi_object), GFP_KERNEL);
948c2ecf20Sopenharmony_ci	if (!obj) {
958c2ecf20Sopenharmony_ci		kfree(obj_list);
968c2ecf20Sopenharmony_ci		goto out;
978c2ecf20Sopenharmony_ci	}
988c2ecf20Sopenharmony_ci
998c2ecf20Sopenharmony_ci	buf = kmalloc(12, GFP_KERNEL);
1008c2ecf20Sopenharmony_ci	if (!buf) {
1018c2ecf20Sopenharmony_ci		kfree(obj);
1028c2ecf20Sopenharmony_ci		kfree(obj_list);
1038c2ecf20Sopenharmony_ci		goto out;
1048c2ecf20Sopenharmony_ci	}
1058c2ecf20Sopenharmony_ci
1068c2ecf20Sopenharmony_ci	acpi_set_pdc_bits(buf);
1078c2ecf20Sopenharmony_ci
1088c2ecf20Sopenharmony_ci	obj->type = ACPI_TYPE_BUFFER;
1098c2ecf20Sopenharmony_ci	obj->buffer.length = 12;
1108c2ecf20Sopenharmony_ci	obj->buffer.pointer = (u8 *) buf;
1118c2ecf20Sopenharmony_ci	obj_list->count = 1;
1128c2ecf20Sopenharmony_ci	obj_list->pointer = obj;
1138c2ecf20Sopenharmony_ci
1148c2ecf20Sopenharmony_ci	return obj_list;
1158c2ecf20Sopenharmony_ciout:
1168c2ecf20Sopenharmony_ci	pr_err("Memory allocation error\n");
1178c2ecf20Sopenharmony_ci	return NULL;
1188c2ecf20Sopenharmony_ci}
1198c2ecf20Sopenharmony_ci
1208c2ecf20Sopenharmony_ci/*
1218c2ecf20Sopenharmony_ci * _PDC is required for a BIOS-OS handshake for most of the newer
1228c2ecf20Sopenharmony_ci * ACPI processor features.
1238c2ecf20Sopenharmony_ci */
1248c2ecf20Sopenharmony_cistatic acpi_status
1258c2ecf20Sopenharmony_ciacpi_processor_eval_pdc(acpi_handle handle, struct acpi_object_list *pdc_in)
1268c2ecf20Sopenharmony_ci{
1278c2ecf20Sopenharmony_ci	acpi_status status = AE_OK;
1288c2ecf20Sopenharmony_ci
1298c2ecf20Sopenharmony_ci	if (boot_option_idle_override == IDLE_NOMWAIT) {
1308c2ecf20Sopenharmony_ci		/*
1318c2ecf20Sopenharmony_ci		 * If mwait is disabled for CPU C-states, the C2C3_FFH access
1328c2ecf20Sopenharmony_ci		 * mode will be disabled in the parameter of _PDC object.
1338c2ecf20Sopenharmony_ci		 * Of course C1_FFH access mode will also be disabled.
1348c2ecf20Sopenharmony_ci		 */
1358c2ecf20Sopenharmony_ci		union acpi_object *obj;
1368c2ecf20Sopenharmony_ci		u32 *buffer = NULL;
1378c2ecf20Sopenharmony_ci
1388c2ecf20Sopenharmony_ci		obj = pdc_in->pointer;
1398c2ecf20Sopenharmony_ci		buffer = (u32 *)(obj->buffer.pointer);
1408c2ecf20Sopenharmony_ci		buffer[2] &= ~(ACPI_PDC_C_C2C3_FFH | ACPI_PDC_C_C1_FFH);
1418c2ecf20Sopenharmony_ci
1428c2ecf20Sopenharmony_ci	}
1438c2ecf20Sopenharmony_ci	status = acpi_evaluate_object(handle, "_PDC", pdc_in, NULL);
1448c2ecf20Sopenharmony_ci
1458c2ecf20Sopenharmony_ci	if (ACPI_FAILURE(status))
1468c2ecf20Sopenharmony_ci		ACPI_DEBUG_PRINT((ACPI_DB_INFO,
1478c2ecf20Sopenharmony_ci		    "Could not evaluate _PDC, using legacy perf. control.\n"));
1488c2ecf20Sopenharmony_ci
1498c2ecf20Sopenharmony_ci	return status;
1508c2ecf20Sopenharmony_ci}
1518c2ecf20Sopenharmony_ci
1528c2ecf20Sopenharmony_civoid acpi_processor_set_pdc(acpi_handle handle)
1538c2ecf20Sopenharmony_ci{
1548c2ecf20Sopenharmony_ci	struct acpi_object_list *obj_list;
1558c2ecf20Sopenharmony_ci
1568c2ecf20Sopenharmony_ci	if (arch_has_acpi_pdc() == false)
1578c2ecf20Sopenharmony_ci		return;
1588c2ecf20Sopenharmony_ci
1598c2ecf20Sopenharmony_ci	obj_list = acpi_processor_alloc_pdc();
1608c2ecf20Sopenharmony_ci	if (!obj_list)
1618c2ecf20Sopenharmony_ci		return;
1628c2ecf20Sopenharmony_ci
1638c2ecf20Sopenharmony_ci	acpi_processor_eval_pdc(handle, obj_list);
1648c2ecf20Sopenharmony_ci
1658c2ecf20Sopenharmony_ci	kfree(obj_list->pointer->buffer.pointer);
1668c2ecf20Sopenharmony_ci	kfree(obj_list->pointer);
1678c2ecf20Sopenharmony_ci	kfree(obj_list);
1688c2ecf20Sopenharmony_ci}
1698c2ecf20Sopenharmony_ci
1708c2ecf20Sopenharmony_cistatic acpi_status __init
1718c2ecf20Sopenharmony_ciearly_init_pdc(acpi_handle handle, u32 lvl, void *context, void **rv)
1728c2ecf20Sopenharmony_ci{
1738c2ecf20Sopenharmony_ci	if (processor_physically_present(handle) == false)
1748c2ecf20Sopenharmony_ci		return AE_OK;
1758c2ecf20Sopenharmony_ci
1768c2ecf20Sopenharmony_ci	acpi_processor_set_pdc(handle);
1778c2ecf20Sopenharmony_ci	return AE_OK;
1788c2ecf20Sopenharmony_ci}
1798c2ecf20Sopenharmony_ci
1808c2ecf20Sopenharmony_cistatic int __init set_no_mwait(const struct dmi_system_id *id)
1818c2ecf20Sopenharmony_ci{
1828c2ecf20Sopenharmony_ci	pr_notice("%s detected - disabling mwait for CPU C-states\n",
1838c2ecf20Sopenharmony_ci		  id->ident);
1848c2ecf20Sopenharmony_ci	boot_option_idle_override = IDLE_NOMWAIT;
1858c2ecf20Sopenharmony_ci	return 0;
1868c2ecf20Sopenharmony_ci}
1878c2ecf20Sopenharmony_ci
1888c2ecf20Sopenharmony_cistatic const struct dmi_system_id processor_idle_dmi_table[] __initconst = {
1898c2ecf20Sopenharmony_ci	{
1908c2ecf20Sopenharmony_ci	set_no_mwait, "Extensa 5220", {
1918c2ecf20Sopenharmony_ci	DMI_MATCH(DMI_BIOS_VENDOR, "Phoenix Technologies LTD"),
1928c2ecf20Sopenharmony_ci	DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
1938c2ecf20Sopenharmony_ci	DMI_MATCH(DMI_PRODUCT_VERSION, "0100"),
1948c2ecf20Sopenharmony_ci	DMI_MATCH(DMI_BOARD_NAME, "Columbia") }, NULL},
1958c2ecf20Sopenharmony_ci	{},
1968c2ecf20Sopenharmony_ci};
1978c2ecf20Sopenharmony_ci
1988c2ecf20Sopenharmony_cistatic void __init processor_dmi_check(void)
1998c2ecf20Sopenharmony_ci{
2008c2ecf20Sopenharmony_ci	/*
2018c2ecf20Sopenharmony_ci	 * Check whether the system is DMI table. If yes, OSPM
2028c2ecf20Sopenharmony_ci	 * should not use mwait for CPU-states.
2038c2ecf20Sopenharmony_ci	 */
2048c2ecf20Sopenharmony_ci	dmi_check_system(processor_idle_dmi_table);
2058c2ecf20Sopenharmony_ci}
2068c2ecf20Sopenharmony_ci
2078c2ecf20Sopenharmony_civoid __init acpi_early_processor_set_pdc(void)
2088c2ecf20Sopenharmony_ci{
2098c2ecf20Sopenharmony_ci	processor_dmi_check();
2108c2ecf20Sopenharmony_ci
2118c2ecf20Sopenharmony_ci	acpi_walk_namespace(ACPI_TYPE_PROCESSOR, ACPI_ROOT_OBJECT,
2128c2ecf20Sopenharmony_ci			    ACPI_UINT32_MAX,
2138c2ecf20Sopenharmony_ci			    early_init_pdc, NULL, NULL, NULL);
2148c2ecf20Sopenharmony_ci	acpi_get_devices(ACPI_PROCESSOR_DEVICE_HID, early_init_pdc, NULL, NULL);
2158c2ecf20Sopenharmony_ci}
216