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