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