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        if (cpuid == -1)
  56                return false;
  57
  58        return true;
  59}
  60
  61static void acpi_set_pdc_bits(u32 *buf)
  62{
  63        buf[0] = ACPI_PDC_REVISION_ID;
  64        buf[1] = 1;
  65
  66        /* Enable coordination with firmware's _TSD info */
  67        buf[2] = ACPI_PDC_SMP_T_SWCOORD;
  68
  69        /* Twiddle arch-specific bits needed for _PDC */
  70        arch_acpi_set_pdc_bits(buf);
  71}
  72
  73static struct acpi_object_list *acpi_processor_alloc_pdc(void)
  74{
  75        struct acpi_object_list *obj_list;
  76        union acpi_object *obj;
  77        u32 *buf;
  78
  79        /* allocate and initialize pdc. It will be used later. */
  80        obj_list = kmalloc(sizeof(struct acpi_object_list), GFP_KERNEL);
  81        if (!obj_list)
  82                goto out;
  83
  84        obj = kmalloc(sizeof(union acpi_object), GFP_KERNEL);
  85        if (!obj) {
  86                kfree(obj_list);
  87                goto out;
  88        }
  89
  90        buf = kmalloc(12, GFP_KERNEL);
  91        if (!buf) {
  92                kfree(obj);
  93                kfree(obj_list);
  94                goto out;
  95        }
  96
  97        acpi_set_pdc_bits(buf);
  98
  99        obj->type = ACPI_TYPE_BUFFER;
 100        obj->buffer.length = 12;
 101        obj->buffer.pointer = (u8 *) buf;
 102        obj_list->count = 1;
 103        obj_list->pointer = obj;
 104
 105        return obj_list;
 106out:
 107        pr_err("Memory allocation error\n");
 108        return NULL;
 109}
 110
 111/*
 112 * _PDC is required for a BIOS-OS handshake for most of the newer
 113 * ACPI processor features.
 114 */
 115static acpi_status
 116acpi_processor_eval_pdc(acpi_handle handle, struct acpi_object_list *pdc_in)
 117{
 118        acpi_status status = AE_OK;
 119
 120        if (boot_option_idle_override == IDLE_NOMWAIT) {
 121                /*
 122                 * If mwait is disabled for CPU C-states, the C2C3_FFH access
 123                 * mode will be disabled in the parameter of _PDC object.
 124                 * Of course C1_FFH access mode will also be disabled.
 125                 */
 126                union acpi_object *obj;
 127                u32 *buffer = NULL;
 128
 129                obj = pdc_in->pointer;
 130                buffer = (u32 *)(obj->buffer.pointer);
 131                buffer[2] &= ~(ACPI_PDC_C_C2C3_FFH | ACPI_PDC_C_C1_FFH);
 132
 133        }
 134        status = acpi_evaluate_object(handle, "_PDC", pdc_in, NULL);
 135
 136        if (ACPI_FAILURE(status))
 137                ACPI_DEBUG_PRINT((ACPI_DB_INFO,
 138                    "Could not evaluate _PDC, using legacy perf. control.\n"));
 139
 140        return status;
 141}
 142
 143void acpi_processor_set_pdc(acpi_handle handle)
 144{
 145        struct acpi_object_list *obj_list;
 146
 147        if (arch_has_acpi_pdc() == false)
 148                return;
 149
 150        obj_list = acpi_processor_alloc_pdc();
 151        if (!obj_list)
 152                return;
 153
 154        acpi_processor_eval_pdc(handle, obj_list);
 155
 156        kfree(obj_list->pointer->buffer.pointer);
 157        kfree(obj_list->pointer);
 158        kfree(obj_list);
 159}
 160
 161static acpi_status __init
 162early_init_pdc(acpi_handle handle, u32 lvl, void *context, void **rv)
 163{
 164        if (processor_physically_present(handle) == false)
 165                return AE_OK;
 166
 167        acpi_processor_set_pdc(handle);
 168        return AE_OK;
 169}
 170
 171static int __init set_no_mwait(const struct dmi_system_id *id)
 172{
 173        pr_notice("%s detected - disabling mwait for CPU C-states\n",
 174                  id->ident);
 175        boot_option_idle_override = IDLE_NOMWAIT;
 176        return 0;
 177}
 178
 179static struct dmi_system_id processor_idle_dmi_table[] __initdata = {
 180        {
 181        set_no_mwait, "Extensa 5220", {
 182        DMI_MATCH(DMI_BIOS_VENDOR, "Phoenix Technologies LTD"),
 183        DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
 184        DMI_MATCH(DMI_PRODUCT_VERSION, "0100"),
 185        DMI_MATCH(DMI_BOARD_NAME, "Columbia") }, NULL},
 186        {},
 187};
 188
 189static void __init processor_dmi_check(void)
 190{
 191        /*
 192         * Check whether the system is DMI table. If yes, OSPM
 193         * should not use mwait for CPU-states.
 194         */
 195        dmi_check_system(processor_idle_dmi_table);
 196}
 197
 198void __init acpi_early_processor_set_pdc(void)
 199{
 200        processor_dmi_check();
 201
 202        acpi_walk_namespace(ACPI_TYPE_PROCESSOR, ACPI_ROOT_OBJECT,
 203                            ACPI_UINT32_MAX,
 204                            early_init_pdc, NULL, NULL, NULL);
 205        acpi_get_devices(ACPI_PROCESSOR_DEVICE_HID, early_init_pdc, NULL, NULL);
 206}
 207