processor_pdc.c (4700B)
1// SPDX-License-Identifier: GPL-2.0 2/* 3 * Copyright (C) 2005 Intel Corporation 4 * Copyright (C) 2009 Hewlett-Packard Development Company, L.P. 5 * 6 * Venkatesh Pallipadi <venkatesh.pallipadi@intel.com> 7 * - Added _PDC for platforms with Intel CPUs 8 */ 9 10#define pr_fmt(fmt) "ACPI: " fmt 11 12#include <linux/dmi.h> 13#include <linux/slab.h> 14#include <linux/acpi.h> 15#include <acpi/processor.h> 16 17#include "internal.h" 18 19static bool __init processor_physically_present(acpi_handle handle) 20{ 21 int cpuid, type; 22 u32 acpi_id; 23 acpi_status status; 24 acpi_object_type acpi_type; 25 unsigned long long tmp; 26 union acpi_object object = { 0 }; 27 struct acpi_buffer buffer = { sizeof(union acpi_object), &object }; 28 29 status = acpi_get_type(handle, &acpi_type); 30 if (ACPI_FAILURE(status)) 31 return false; 32 33 switch (acpi_type) { 34 case ACPI_TYPE_PROCESSOR: 35 status = acpi_evaluate_object(handle, NULL, NULL, &buffer); 36 if (ACPI_FAILURE(status)) 37 return false; 38 acpi_id = object.processor.proc_id; 39 break; 40 case ACPI_TYPE_DEVICE: 41 status = acpi_evaluate_integer(handle, "_UID", NULL, &tmp); 42 if (ACPI_FAILURE(status)) 43 return false; 44 acpi_id = tmp; 45 break; 46 default: 47 return false; 48 } 49 50 type = (acpi_type == ACPI_TYPE_DEVICE) ? 1 : 0; 51 cpuid = acpi_get_cpuid(handle, type, acpi_id); 52 53 return !invalid_logical_cpuid(cpuid); 54} 55 56static void acpi_set_pdc_bits(u32 *buf) 57{ 58 buf[0] = ACPI_PDC_REVISION_ID; 59 buf[1] = 1; 60 61 /* Enable coordination with firmware's _TSD info */ 62 buf[2] = ACPI_PDC_SMP_T_SWCOORD; 63 64 /* Twiddle arch-specific bits needed for _PDC */ 65 arch_acpi_set_pdc_bits(buf); 66} 67 68static struct acpi_object_list *acpi_processor_alloc_pdc(void) 69{ 70 struct acpi_object_list *obj_list; 71 union acpi_object *obj; 72 u32 *buf; 73 74 /* allocate and initialize pdc. It will be used later. */ 75 obj_list = kmalloc(sizeof(struct acpi_object_list), GFP_KERNEL); 76 if (!obj_list) 77 goto out; 78 79 obj = kmalloc(sizeof(union acpi_object), GFP_KERNEL); 80 if (!obj) { 81 kfree(obj_list); 82 goto out; 83 } 84 85 buf = kmalloc(12, GFP_KERNEL); 86 if (!buf) { 87 kfree(obj); 88 kfree(obj_list); 89 goto out; 90 } 91 92 acpi_set_pdc_bits(buf); 93 94 obj->type = ACPI_TYPE_BUFFER; 95 obj->buffer.length = 12; 96 obj->buffer.pointer = (u8 *) buf; 97 obj_list->count = 1; 98 obj_list->pointer = obj; 99 100 return obj_list; 101out: 102 pr_err("Memory allocation error\n"); 103 return NULL; 104} 105 106/* 107 * _PDC is required for a BIOS-OS handshake for most of the newer 108 * ACPI processor features. 109 */ 110static acpi_status 111acpi_processor_eval_pdc(acpi_handle handle, struct acpi_object_list *pdc_in) 112{ 113 acpi_status status = AE_OK; 114 115 if (boot_option_idle_override == IDLE_NOMWAIT) { 116 /* 117 * If mwait is disabled for CPU C-states, the C2C3_FFH access 118 * mode will be disabled in the parameter of _PDC object. 119 * Of course C1_FFH access mode will also be disabled. 120 */ 121 union acpi_object *obj; 122 u32 *buffer = NULL; 123 124 obj = pdc_in->pointer; 125 buffer = (u32 *)(obj->buffer.pointer); 126 buffer[2] &= ~(ACPI_PDC_C_C2C3_FFH | ACPI_PDC_C_C1_FFH); 127 128 } 129 status = acpi_evaluate_object(handle, "_PDC", pdc_in, NULL); 130 131 if (ACPI_FAILURE(status)) 132 acpi_handle_debug(handle, 133 "Could not evaluate _PDC, using legacy perf control\n"); 134 135 return status; 136} 137 138void acpi_processor_set_pdc(acpi_handle handle) 139{ 140 struct acpi_object_list *obj_list; 141 142 if (arch_has_acpi_pdc() == false) 143 return; 144 145 obj_list = acpi_processor_alloc_pdc(); 146 if (!obj_list) 147 return; 148 149 acpi_processor_eval_pdc(handle, obj_list); 150 151 kfree(obj_list->pointer->buffer.pointer); 152 kfree(obj_list->pointer); 153 kfree(obj_list); 154} 155 156static acpi_status __init 157early_init_pdc(acpi_handle handle, u32 lvl, void *context, void **rv) 158{ 159 if (processor_physically_present(handle) == false) 160 return AE_OK; 161 162 acpi_processor_set_pdc(handle); 163 return AE_OK; 164} 165 166static int __init set_no_mwait(const struct dmi_system_id *id) 167{ 168 pr_notice("%s detected - disabling mwait for CPU C-states\n", 169 id->ident); 170 boot_option_idle_override = IDLE_NOMWAIT; 171 return 0; 172} 173 174static const struct dmi_system_id processor_idle_dmi_table[] __initconst = { 175 { 176 set_no_mwait, "Extensa 5220", { 177 DMI_MATCH(DMI_BIOS_VENDOR, "Phoenix Technologies LTD"), 178 DMI_MATCH(DMI_SYS_VENDOR, "Acer"), 179 DMI_MATCH(DMI_PRODUCT_VERSION, "0100"), 180 DMI_MATCH(DMI_BOARD_NAME, "Columbia") }, NULL}, 181 {}, 182}; 183 184static void __init processor_dmi_check(void) 185{ 186 /* 187 * Check whether the system is DMI table. If yes, OSPM 188 * should not use mwait for CPU-states. 189 */ 190 dmi_check_system(processor_idle_dmi_table); 191} 192 193void __init acpi_early_processor_set_pdc(void) 194{ 195 processor_dmi_check(); 196 197 acpi_walk_namespace(ACPI_TYPE_PROCESSOR, ACPI_ROOT_OBJECT, 198 ACPI_UINT32_MAX, 199 early_init_pdc, NULL, NULL, NULL); 200 acpi_get_devices(ACPI_PROCESSOR_DEVICE_HID, early_init_pdc, NULL, NULL); 201}