linux/drivers/acpi/processor_pdc.c
<<
>>
Prefs
   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}
 202