|  | /* | 
|  | * xen-acpi-pad.c - Xen pad interface | 
|  | * | 
|  | * Copyright (c) 2012, Intel Corporation. | 
|  | *    Author: Liu, Jinsong <jinsong.liu@intel.com> | 
|  | * | 
|  | * This program is free software; you can redistribute it and/or modify it | 
|  | * under the terms and conditions of the GNU General Public License, | 
|  | * version 2, as published by the Free Software Foundation. | 
|  | * | 
|  | * This program is distributed in the hope it will be useful, but WITHOUT | 
|  | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | 
|  | * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for | 
|  | * more details. | 
|  | */ | 
|  |  | 
|  | #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt | 
|  |  | 
|  | #include <linux/kernel.h> | 
|  | #include <linux/types.h> | 
|  | #include <linux/acpi.h> | 
|  | #include <xen/interface/version.h> | 
|  | #include <xen/xen-ops.h> | 
|  | #include <asm/xen/hypercall.h> | 
|  |  | 
|  | #define ACPI_PROCESSOR_AGGREGATOR_CLASS	"acpi_pad" | 
|  | #define ACPI_PROCESSOR_AGGREGATOR_DEVICE_NAME "Processor Aggregator" | 
|  | #define ACPI_PROCESSOR_AGGREGATOR_NOTIFY 0x80 | 
|  | static DEFINE_MUTEX(xen_cpu_lock); | 
|  |  | 
|  | static int xen_acpi_pad_idle_cpus(unsigned int idle_nums) | 
|  | { | 
|  | struct xen_platform_op op; | 
|  |  | 
|  | op.cmd = XENPF_core_parking; | 
|  | op.u.core_parking.type = XEN_CORE_PARKING_SET; | 
|  | op.u.core_parking.idle_nums = idle_nums; | 
|  |  | 
|  | return HYPERVISOR_platform_op(&op); | 
|  | } | 
|  |  | 
|  | static int xen_acpi_pad_idle_cpus_num(void) | 
|  | { | 
|  | struct xen_platform_op op; | 
|  |  | 
|  | op.cmd = XENPF_core_parking; | 
|  | op.u.core_parking.type = XEN_CORE_PARKING_GET; | 
|  |  | 
|  | return HYPERVISOR_platform_op(&op) | 
|  | ?: op.u.core_parking.idle_nums; | 
|  | } | 
|  |  | 
|  | /* | 
|  | * Query firmware how many CPUs should be idle | 
|  | * return -1 on failure | 
|  | */ | 
|  | static int acpi_pad_pur(acpi_handle handle) | 
|  | { | 
|  | struct acpi_buffer buffer = {ACPI_ALLOCATE_BUFFER, NULL}; | 
|  | union acpi_object *package; | 
|  | int num = -1; | 
|  |  | 
|  | if (ACPI_FAILURE(acpi_evaluate_object(handle, "_PUR", NULL, &buffer))) | 
|  | return num; | 
|  |  | 
|  | if (!buffer.length || !buffer.pointer) | 
|  | return num; | 
|  |  | 
|  | package = buffer.pointer; | 
|  |  | 
|  | if (package->type == ACPI_TYPE_PACKAGE && | 
|  | package->package.count == 2 && | 
|  | package->package.elements[0].integer.value == 1) /* rev 1 */ | 
|  | num = package->package.elements[1].integer.value; | 
|  |  | 
|  | kfree(buffer.pointer); | 
|  | return num; | 
|  | } | 
|  |  | 
|  | static void acpi_pad_handle_notify(acpi_handle handle) | 
|  | { | 
|  | int idle_nums; | 
|  | struct acpi_buffer param = { | 
|  | .length = 4, | 
|  | .pointer = (void *)&idle_nums, | 
|  | }; | 
|  |  | 
|  |  | 
|  | mutex_lock(&xen_cpu_lock); | 
|  | idle_nums = acpi_pad_pur(handle); | 
|  | if (idle_nums < 0) { | 
|  | mutex_unlock(&xen_cpu_lock); | 
|  | return; | 
|  | } | 
|  |  | 
|  | idle_nums = xen_acpi_pad_idle_cpus(idle_nums) | 
|  | ?: xen_acpi_pad_idle_cpus_num(); | 
|  | if (idle_nums >= 0) | 
|  | acpi_evaluate_ost(handle, ACPI_PROCESSOR_AGGREGATOR_NOTIFY, | 
|  | 0, ¶m); | 
|  | mutex_unlock(&xen_cpu_lock); | 
|  | } | 
|  |  | 
|  | static void acpi_pad_notify(acpi_handle handle, u32 event, | 
|  | void *data) | 
|  | { | 
|  | switch (event) { | 
|  | case ACPI_PROCESSOR_AGGREGATOR_NOTIFY: | 
|  | acpi_pad_handle_notify(handle); | 
|  | break; | 
|  | default: | 
|  | pr_warn("Unsupported event [0x%x]\n", event); | 
|  | break; | 
|  | } | 
|  | } | 
|  |  | 
|  | static int acpi_pad_add(struct acpi_device *device) | 
|  | { | 
|  | acpi_status status; | 
|  |  | 
|  | strcpy(acpi_device_name(device), ACPI_PROCESSOR_AGGREGATOR_DEVICE_NAME); | 
|  | strcpy(acpi_device_class(device), ACPI_PROCESSOR_AGGREGATOR_CLASS); | 
|  |  | 
|  | status = acpi_install_notify_handler(device->handle, | 
|  | ACPI_DEVICE_NOTIFY, acpi_pad_notify, device); | 
|  | if (ACPI_FAILURE(status)) | 
|  | return -ENODEV; | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | static int acpi_pad_remove(struct acpi_device *device) | 
|  | { | 
|  | mutex_lock(&xen_cpu_lock); | 
|  | xen_acpi_pad_idle_cpus(0); | 
|  | mutex_unlock(&xen_cpu_lock); | 
|  |  | 
|  | acpi_remove_notify_handler(device->handle, | 
|  | ACPI_DEVICE_NOTIFY, acpi_pad_notify); | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | static const struct acpi_device_id pad_device_ids[] = { | 
|  | {"ACPI000C", 0}, | 
|  | {"", 0}, | 
|  | }; | 
|  |  | 
|  | static struct acpi_driver acpi_pad_driver = { | 
|  | .name = "processor_aggregator", | 
|  | .class = ACPI_PROCESSOR_AGGREGATOR_CLASS, | 
|  | .ids = pad_device_ids, | 
|  | .ops = { | 
|  | .add = acpi_pad_add, | 
|  | .remove = acpi_pad_remove, | 
|  | }, | 
|  | }; | 
|  |  | 
|  | static int __init xen_acpi_pad_init(void) | 
|  | { | 
|  | /* Only DOM0 is responsible for Xen acpi pad */ | 
|  | if (!xen_initial_domain()) | 
|  | return -ENODEV; | 
|  |  | 
|  | /* Only Xen4.2 or later support Xen acpi pad */ | 
|  | if (!xen_running_on_version_or_later(4, 2)) | 
|  | return -ENODEV; | 
|  |  | 
|  | return acpi_bus_register_driver(&acpi_pad_driver); | 
|  | } | 
|  | subsys_initcall(xen_acpi_pad_init); |