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