linux/drivers/xen/xen-acpi-pad.c
<<
>>
Prefs
   1/*
   2 * xen-acpi-pad.c - Xen pad interface
   3 *
   4 * Copyright (c) 2012, Intel Corporation.
   5 *    Author: Liu, Jinsong <jinsong.liu@intel.com>
   6 *
   7 * This program is free software; you can redistribute it and/or modify it
   8 * under the terms and conditions of the GNU General Public License,
   9 * version 2, as published by the Free Software Foundation.
  10 *
  11 * This program is distributed in the hope it will be useful, but WITHOUT
  12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
  14 * more details.
  15 */
  16
  17#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
  18
  19#include <linux/kernel.h>
  20#include <linux/types.h>
  21#include <linux/acpi.h>
  22#include <xen/interface/version.h>
  23#include <xen/xen-ops.h>
  24#include <asm/xen/hypercall.h>
  25
  26#define ACPI_PROCESSOR_AGGREGATOR_CLASS "acpi_pad"
  27#define ACPI_PROCESSOR_AGGREGATOR_DEVICE_NAME "Processor Aggregator"
  28#define ACPI_PROCESSOR_AGGREGATOR_NOTIFY 0x80
  29static DEFINE_MUTEX(xen_cpu_lock);
  30
  31static int xen_acpi_pad_idle_cpus(unsigned int idle_nums)
  32{
  33        struct xen_platform_op op;
  34
  35        op.cmd = XENPF_core_parking;
  36        op.u.core_parking.type = XEN_CORE_PARKING_SET;
  37        op.u.core_parking.idle_nums = idle_nums;
  38
  39        return HYPERVISOR_platform_op(&op);
  40}
  41
  42static int xen_acpi_pad_idle_cpus_num(void)
  43{
  44        struct xen_platform_op op;
  45
  46        op.cmd = XENPF_core_parking;
  47        op.u.core_parking.type = XEN_CORE_PARKING_GET;
  48
  49        return HYPERVISOR_platform_op(&op)
  50               ?: op.u.core_parking.idle_nums;
  51}
  52
  53/*
  54 * Query firmware how many CPUs should be idle
  55 * return -1 on failure
  56 */
  57static int acpi_pad_pur(acpi_handle handle)
  58{
  59        struct acpi_buffer buffer = {ACPI_ALLOCATE_BUFFER, NULL};
  60        union acpi_object *package;
  61        int num = -1;
  62
  63        if (ACPI_FAILURE(acpi_evaluate_object(handle, "_PUR", NULL, &buffer)))
  64                return num;
  65
  66        if (!buffer.length || !buffer.pointer)
  67                return num;
  68
  69        package = buffer.pointer;
  70
  71        if (package->type == ACPI_TYPE_PACKAGE &&
  72                package->package.count == 2 &&
  73                package->package.elements[0].integer.value == 1) /* rev 1 */
  74                num = package->package.elements[1].integer.value;
  75
  76        kfree(buffer.pointer);
  77        return num;
  78}
  79
  80static void acpi_pad_handle_notify(acpi_handle handle)
  81{
  82        int idle_nums;
  83        struct acpi_buffer param = {
  84                .length = 4,
  85                .pointer = (void *)&idle_nums,
  86        };
  87
  88
  89        mutex_lock(&xen_cpu_lock);
  90        idle_nums = acpi_pad_pur(handle);
  91        if (idle_nums < 0) {
  92                mutex_unlock(&xen_cpu_lock);
  93                return;
  94        }
  95
  96        idle_nums = xen_acpi_pad_idle_cpus(idle_nums)
  97                    ?: xen_acpi_pad_idle_cpus_num();
  98        if (idle_nums >= 0)
  99                acpi_evaluate_ost(handle, ACPI_PROCESSOR_AGGREGATOR_NOTIFY,
 100                                  0, &param);
 101        mutex_unlock(&xen_cpu_lock);
 102}
 103
 104static void acpi_pad_notify(acpi_handle handle, u32 event,
 105        void *data)
 106{
 107        switch (event) {
 108        case ACPI_PROCESSOR_AGGREGATOR_NOTIFY:
 109                acpi_pad_handle_notify(handle);
 110                break;
 111        default:
 112                pr_warn("Unsupported event [0x%x]\n", event);
 113                break;
 114        }
 115}
 116
 117static int acpi_pad_add(struct acpi_device *device)
 118{
 119        acpi_status status;
 120
 121        strcpy(acpi_device_name(device), ACPI_PROCESSOR_AGGREGATOR_DEVICE_NAME);
 122        strcpy(acpi_device_class(device), ACPI_PROCESSOR_AGGREGATOR_CLASS);
 123
 124        status = acpi_install_notify_handler(device->handle,
 125                ACPI_DEVICE_NOTIFY, acpi_pad_notify, device);
 126        if (ACPI_FAILURE(status))
 127                return -ENODEV;
 128
 129        return 0;
 130}
 131
 132static int acpi_pad_remove(struct acpi_device *device)
 133{
 134        mutex_lock(&xen_cpu_lock);
 135        xen_acpi_pad_idle_cpus(0);
 136        mutex_unlock(&xen_cpu_lock);
 137
 138        acpi_remove_notify_handler(device->handle,
 139                ACPI_DEVICE_NOTIFY, acpi_pad_notify);
 140        return 0;
 141}
 142
 143static const struct acpi_device_id pad_device_ids[] = {
 144        {"ACPI000C", 0},
 145        {"", 0},
 146};
 147
 148static struct acpi_driver acpi_pad_driver = {
 149        .name = "processor_aggregator",
 150        .class = ACPI_PROCESSOR_AGGREGATOR_CLASS,
 151        .ids = pad_device_ids,
 152        .ops = {
 153                .add = acpi_pad_add,
 154                .remove = acpi_pad_remove,
 155        },
 156};
 157
 158static int __init xen_acpi_pad_init(void)
 159{
 160        /* Only DOM0 is responsible for Xen acpi pad */
 161        if (!xen_initial_domain())
 162                return -ENODEV;
 163
 164        /* Only Xen4.2 or later support Xen acpi pad */
 165        if (!xen_running_on_version_or_later(4, 2))
 166                return -ENODEV;
 167
 168        return acpi_bus_register_driver(&acpi_pad_driver);
 169}
 170subsys_initcall(xen_acpi_pad_init);
 171