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#include <linux/kernel.h>
  18#include <linux/types.h>
  19#include <acpi/acpi_bus.h>
  20#include <acpi/acpi_drivers.h>
  21#include <asm/xen/hypercall.h>
  22#include <xen/interface/version.h>
  23#include <xen/xen-ops.h>
  24
  25#define ACPI_PROCESSOR_AGGREGATOR_CLASS "acpi_pad"
  26#define ACPI_PROCESSOR_AGGREGATOR_DEVICE_NAME "Processor Aggregator"
  27#define ACPI_PROCESSOR_AGGREGATOR_NOTIFY 0x80
  28static DEFINE_MUTEX(xen_cpu_lock);
  29
  30static int xen_acpi_pad_idle_cpus(unsigned int idle_nums)
  31{
  32        struct xen_platform_op op;
  33
  34        op.cmd = XENPF_core_parking;
  35        op.u.core_parking.type = XEN_CORE_PARKING_SET;
  36        op.u.core_parking.idle_nums = idle_nums;
  37
  38        return HYPERVISOR_dom0_op(&op);
  39}
  40
  41static int xen_acpi_pad_idle_cpus_num(void)
  42{
  43        struct xen_platform_op op;
  44
  45        op.cmd = XENPF_core_parking;
  46        op.u.core_parking.type = XEN_CORE_PARKING_GET;
  47
  48        return HYPERVISOR_dom0_op(&op)
  49               ?: op.u.core_parking.idle_nums;
  50}
  51
  52/*
  53 * Query firmware how many CPUs should be idle
  54 * return -1 on failure
  55 */
  56static int acpi_pad_pur(acpi_handle handle)
  57{
  58        struct acpi_buffer buffer = {ACPI_ALLOCATE_BUFFER, NULL};
  59        union acpi_object *package;
  60        int num = -1;
  61
  62        if (ACPI_FAILURE(acpi_evaluate_object(handle, "_PUR", NULL, &buffer)))
  63                return num;
  64
  65        if (!buffer.length || !buffer.pointer)
  66                return num;
  67
  68        package = buffer.pointer;
  69
  70        if (package->type == ACPI_TYPE_PACKAGE &&
  71                package->package.count == 2 &&
  72                package->package.elements[0].integer.value == 1) /* rev 1 */
  73                num = package->package.elements[1].integer.value;
  74
  75        kfree(buffer.pointer);
  76        return num;
  77}
  78
  79/* Notify firmware how many CPUs are idle */
  80static void acpi_pad_ost(acpi_handle handle, int stat,
  81        uint32_t idle_nums)
  82{
  83        union acpi_object params[3] = {
  84                {.type = ACPI_TYPE_INTEGER,},
  85                {.type = ACPI_TYPE_INTEGER,},
  86                {.type = ACPI_TYPE_BUFFER,},
  87        };
  88        struct acpi_object_list arg_list = {3, params};
  89
  90        params[0].integer.value = ACPI_PROCESSOR_AGGREGATOR_NOTIFY;
  91        params[1].integer.value =  stat;
  92        params[2].buffer.length = 4;
  93        params[2].buffer.pointer = (void *)&idle_nums;
  94        acpi_evaluate_object(handle, "_OST", &arg_list, NULL);
  95}
  96
  97static void acpi_pad_handle_notify(acpi_handle handle)
  98{
  99        int idle_nums;
 100
 101        mutex_lock(&xen_cpu_lock);
 102        idle_nums = acpi_pad_pur(handle);
 103        if (idle_nums < 0) {
 104                mutex_unlock(&xen_cpu_lock);
 105                return;
 106        }
 107
 108        idle_nums = xen_acpi_pad_idle_cpus(idle_nums)
 109                    ?: xen_acpi_pad_idle_cpus_num();
 110        if (idle_nums >= 0)
 111                acpi_pad_ost(handle, 0, idle_nums);
 112        mutex_unlock(&xen_cpu_lock);
 113}
 114
 115static void acpi_pad_notify(acpi_handle handle, u32 event,
 116        void *data)
 117{
 118        switch (event) {
 119        case ACPI_PROCESSOR_AGGREGATOR_NOTIFY:
 120                acpi_pad_handle_notify(handle);
 121                break;
 122        default:
 123                pr_warn("Unsupported event [0x%x]\n", event);
 124                break;
 125        }
 126}
 127
 128static int acpi_pad_add(struct acpi_device *device)
 129{
 130        acpi_status status;
 131
 132        strcpy(acpi_device_name(device), ACPI_PROCESSOR_AGGREGATOR_DEVICE_NAME);
 133        strcpy(acpi_device_class(device), ACPI_PROCESSOR_AGGREGATOR_CLASS);
 134
 135        status = acpi_install_notify_handler(device->handle,
 136                ACPI_DEVICE_NOTIFY, acpi_pad_notify, device);
 137        if (ACPI_FAILURE(status))
 138                return -ENODEV;
 139
 140        return 0;
 141}
 142
 143static int acpi_pad_remove(struct acpi_device *device,
 144        int type)
 145{
 146        mutex_lock(&xen_cpu_lock);
 147        xen_acpi_pad_idle_cpus(0);
 148        mutex_unlock(&xen_cpu_lock);
 149
 150        acpi_remove_notify_handler(device->handle,
 151                ACPI_DEVICE_NOTIFY, acpi_pad_notify);
 152        return 0;
 153}
 154
 155static const struct acpi_device_id pad_device_ids[] = {
 156        {"ACPI000C", 0},
 157        {"", 0},
 158};
 159
 160static struct acpi_driver acpi_pad_driver = {
 161        .name = "processor_aggregator",
 162        .class = ACPI_PROCESSOR_AGGREGATOR_CLASS,
 163        .ids = pad_device_ids,
 164        .ops = {
 165                .add = acpi_pad_add,
 166                .remove = acpi_pad_remove,
 167        },
 168};
 169
 170static int __init xen_acpi_pad_init(void)
 171{
 172        /* Only DOM0 is responsible for Xen acpi pad */
 173        if (!xen_initial_domain())
 174                return -ENODEV;
 175
 176        /* Only Xen4.2 or later support Xen acpi pad */
 177        if (!xen_running_on_version_or_later(4, 2))
 178                return -ENODEV;
 179
 180        return acpi_bus_register_driver(&acpi_pad_driver);
 181}
 182subsys_initcall(xen_acpi_pad_init);
 183
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.