192e3229dSLiu, Jinsong /* 292e3229dSLiu, Jinsong * xen-acpi-pad.c - Xen pad interface 392e3229dSLiu, Jinsong * 492e3229dSLiu, Jinsong * Copyright (c) 2012, Intel Corporation. 592e3229dSLiu, Jinsong * Author: Liu, Jinsong <jinsong.liu@intel.com> 692e3229dSLiu, Jinsong * 792e3229dSLiu, Jinsong * This program is free software; you can redistribute it and/or modify it 892e3229dSLiu, Jinsong * under the terms and conditions of the GNU General Public License, 992e3229dSLiu, Jinsong * version 2, as published by the Free Software Foundation. 1092e3229dSLiu, Jinsong * 1192e3229dSLiu, Jinsong * This program is distributed in the hope it will be useful, but WITHOUT 1292e3229dSLiu, Jinsong * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 1392e3229dSLiu, Jinsong * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for 1492e3229dSLiu, Jinsong * more details. 1592e3229dSLiu, Jinsong */ 1692e3229dSLiu, Jinsong 17283c0972SJoe Perches #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 18283c0972SJoe Perches 1992e3229dSLiu, Jinsong #include <linux/kernel.h> 2092e3229dSLiu, Jinsong #include <linux/types.h> 218b48463fSLv Zheng #include <linux/acpi.h> 2292e3229dSLiu, Jinsong #include <xen/interface/version.h> 23394b40f6SKonrad Rzeszutek Wilk #include <xen/xen-ops.h> 248b48463fSLv Zheng #include <asm/xen/hypercall.h> 2592e3229dSLiu, Jinsong 2692e3229dSLiu, Jinsong #define ACPI_PROCESSOR_AGGREGATOR_CLASS "acpi_pad" 2792e3229dSLiu, Jinsong #define ACPI_PROCESSOR_AGGREGATOR_DEVICE_NAME "Processor Aggregator" 2892e3229dSLiu, Jinsong #define ACPI_PROCESSOR_AGGREGATOR_NOTIFY 0x80 2992e3229dSLiu, Jinsong static DEFINE_MUTEX(xen_cpu_lock); 3092e3229dSLiu, Jinsong 3192e3229dSLiu, Jinsong static int xen_acpi_pad_idle_cpus(unsigned int idle_nums) 3292e3229dSLiu, Jinsong { 3392e3229dSLiu, Jinsong struct xen_platform_op op; 3492e3229dSLiu, Jinsong 3592e3229dSLiu, Jinsong op.cmd = XENPF_core_parking; 3692e3229dSLiu, Jinsong op.u.core_parking.type = XEN_CORE_PARKING_SET; 3792e3229dSLiu, Jinsong op.u.core_parking.idle_nums = idle_nums; 3892e3229dSLiu, Jinsong 3992e3229dSLiu, Jinsong return HYPERVISOR_dom0_op(&op); 4092e3229dSLiu, Jinsong } 4192e3229dSLiu, Jinsong 4292e3229dSLiu, Jinsong static int xen_acpi_pad_idle_cpus_num(void) 4392e3229dSLiu, Jinsong { 4492e3229dSLiu, Jinsong struct xen_platform_op op; 4592e3229dSLiu, Jinsong 4692e3229dSLiu, Jinsong op.cmd = XENPF_core_parking; 4792e3229dSLiu, Jinsong op.u.core_parking.type = XEN_CORE_PARKING_GET; 4892e3229dSLiu, Jinsong 4992e3229dSLiu, Jinsong return HYPERVISOR_dom0_op(&op) 5092e3229dSLiu, Jinsong ?: op.u.core_parking.idle_nums; 5192e3229dSLiu, Jinsong } 5292e3229dSLiu, Jinsong 5392e3229dSLiu, Jinsong /* 5492e3229dSLiu, Jinsong * Query firmware how many CPUs should be idle 5592e3229dSLiu, Jinsong * return -1 on failure 5692e3229dSLiu, Jinsong */ 5792e3229dSLiu, Jinsong static int acpi_pad_pur(acpi_handle handle) 5892e3229dSLiu, Jinsong { 5992e3229dSLiu, Jinsong struct acpi_buffer buffer = {ACPI_ALLOCATE_BUFFER, NULL}; 6092e3229dSLiu, Jinsong union acpi_object *package; 6192e3229dSLiu, Jinsong int num = -1; 6292e3229dSLiu, Jinsong 6392e3229dSLiu, Jinsong if (ACPI_FAILURE(acpi_evaluate_object(handle, "_PUR", NULL, &buffer))) 6492e3229dSLiu, Jinsong return num; 6592e3229dSLiu, Jinsong 6692e3229dSLiu, Jinsong if (!buffer.length || !buffer.pointer) 6792e3229dSLiu, Jinsong return num; 6892e3229dSLiu, Jinsong 6992e3229dSLiu, Jinsong package = buffer.pointer; 7092e3229dSLiu, Jinsong 7192e3229dSLiu, Jinsong if (package->type == ACPI_TYPE_PACKAGE && 7292e3229dSLiu, Jinsong package->package.count == 2 && 7392e3229dSLiu, Jinsong package->package.elements[0].integer.value == 1) /* rev 1 */ 7492e3229dSLiu, Jinsong num = package->package.elements[1].integer.value; 7592e3229dSLiu, Jinsong 7692e3229dSLiu, Jinsong kfree(buffer.pointer); 7792e3229dSLiu, Jinsong return num; 7892e3229dSLiu, Jinsong } 7992e3229dSLiu, Jinsong 8092e3229dSLiu, Jinsong /* Notify firmware how many CPUs are idle */ 8192e3229dSLiu, Jinsong static void acpi_pad_ost(acpi_handle handle, int stat, 8292e3229dSLiu, Jinsong uint32_t idle_nums) 8392e3229dSLiu, Jinsong { 8492e3229dSLiu, Jinsong union acpi_object params[3] = { 8592e3229dSLiu, Jinsong {.type = ACPI_TYPE_INTEGER,}, 8692e3229dSLiu, Jinsong {.type = ACPI_TYPE_INTEGER,}, 8792e3229dSLiu, Jinsong {.type = ACPI_TYPE_BUFFER,}, 8892e3229dSLiu, Jinsong }; 8992e3229dSLiu, Jinsong struct acpi_object_list arg_list = {3, params}; 9092e3229dSLiu, Jinsong 9192e3229dSLiu, Jinsong params[0].integer.value = ACPI_PROCESSOR_AGGREGATOR_NOTIFY; 9292e3229dSLiu, Jinsong params[1].integer.value = stat; 9392e3229dSLiu, Jinsong params[2].buffer.length = 4; 9492e3229dSLiu, Jinsong params[2].buffer.pointer = (void *)&idle_nums; 9592e3229dSLiu, Jinsong acpi_evaluate_object(handle, "_OST", &arg_list, NULL); 9692e3229dSLiu, Jinsong } 9792e3229dSLiu, Jinsong 9892e3229dSLiu, Jinsong static void acpi_pad_handle_notify(acpi_handle handle) 9992e3229dSLiu, Jinsong { 10092e3229dSLiu, Jinsong int idle_nums; 10192e3229dSLiu, Jinsong 10292e3229dSLiu, Jinsong mutex_lock(&xen_cpu_lock); 10392e3229dSLiu, Jinsong idle_nums = acpi_pad_pur(handle); 10492e3229dSLiu, Jinsong if (idle_nums < 0) { 10592e3229dSLiu, Jinsong mutex_unlock(&xen_cpu_lock); 10692e3229dSLiu, Jinsong return; 10792e3229dSLiu, Jinsong } 10892e3229dSLiu, Jinsong 10992e3229dSLiu, Jinsong idle_nums = xen_acpi_pad_idle_cpus(idle_nums) 11092e3229dSLiu, Jinsong ?: xen_acpi_pad_idle_cpus_num(); 11192e3229dSLiu, Jinsong if (idle_nums >= 0) 11292e3229dSLiu, Jinsong acpi_pad_ost(handle, 0, idle_nums); 11392e3229dSLiu, Jinsong mutex_unlock(&xen_cpu_lock); 11492e3229dSLiu, Jinsong } 11592e3229dSLiu, Jinsong 11692e3229dSLiu, Jinsong static void acpi_pad_notify(acpi_handle handle, u32 event, 11792e3229dSLiu, Jinsong void *data) 11892e3229dSLiu, Jinsong { 11992e3229dSLiu, Jinsong switch (event) { 12092e3229dSLiu, Jinsong case ACPI_PROCESSOR_AGGREGATOR_NOTIFY: 12192e3229dSLiu, Jinsong acpi_pad_handle_notify(handle); 12292e3229dSLiu, Jinsong break; 12392e3229dSLiu, Jinsong default: 12492e3229dSLiu, Jinsong pr_warn("Unsupported event [0x%x]\n", event); 12592e3229dSLiu, Jinsong break; 12692e3229dSLiu, Jinsong } 12792e3229dSLiu, Jinsong } 12892e3229dSLiu, Jinsong 12992e3229dSLiu, Jinsong static int acpi_pad_add(struct acpi_device *device) 13092e3229dSLiu, Jinsong { 13192e3229dSLiu, Jinsong acpi_status status; 13292e3229dSLiu, Jinsong 13392e3229dSLiu, Jinsong strcpy(acpi_device_name(device), ACPI_PROCESSOR_AGGREGATOR_DEVICE_NAME); 13492e3229dSLiu, Jinsong strcpy(acpi_device_class(device), ACPI_PROCESSOR_AGGREGATOR_CLASS); 13592e3229dSLiu, Jinsong 13692e3229dSLiu, Jinsong status = acpi_install_notify_handler(device->handle, 13792e3229dSLiu, Jinsong ACPI_DEVICE_NOTIFY, acpi_pad_notify, device); 13892e3229dSLiu, Jinsong if (ACPI_FAILURE(status)) 13992e3229dSLiu, Jinsong return -ENODEV; 14092e3229dSLiu, Jinsong 14192e3229dSLiu, Jinsong return 0; 14292e3229dSLiu, Jinsong } 14392e3229dSLiu, Jinsong 14451fac838SRafael J. Wysocki static int acpi_pad_remove(struct acpi_device *device) 14592e3229dSLiu, Jinsong { 14692e3229dSLiu, Jinsong mutex_lock(&xen_cpu_lock); 14792e3229dSLiu, Jinsong xen_acpi_pad_idle_cpus(0); 14892e3229dSLiu, Jinsong mutex_unlock(&xen_cpu_lock); 14992e3229dSLiu, Jinsong 15092e3229dSLiu, Jinsong acpi_remove_notify_handler(device->handle, 15192e3229dSLiu, Jinsong ACPI_DEVICE_NOTIFY, acpi_pad_notify); 15292e3229dSLiu, Jinsong return 0; 15392e3229dSLiu, Jinsong } 15492e3229dSLiu, Jinsong 15592e3229dSLiu, Jinsong static const struct acpi_device_id pad_device_ids[] = { 15692e3229dSLiu, Jinsong {"ACPI000C", 0}, 15792e3229dSLiu, Jinsong {"", 0}, 15892e3229dSLiu, Jinsong }; 15992e3229dSLiu, Jinsong 16092e3229dSLiu, Jinsong static struct acpi_driver acpi_pad_driver = { 16192e3229dSLiu, Jinsong .name = "processor_aggregator", 16292e3229dSLiu, Jinsong .class = ACPI_PROCESSOR_AGGREGATOR_CLASS, 16392e3229dSLiu, Jinsong .ids = pad_device_ids, 16492e3229dSLiu, Jinsong .ops = { 16592e3229dSLiu, Jinsong .add = acpi_pad_add, 16692e3229dSLiu, Jinsong .remove = acpi_pad_remove, 16792e3229dSLiu, Jinsong }, 16892e3229dSLiu, Jinsong }; 16992e3229dSLiu, Jinsong 17092e3229dSLiu, Jinsong static int __init xen_acpi_pad_init(void) 17192e3229dSLiu, Jinsong { 17292e3229dSLiu, Jinsong /* Only DOM0 is responsible for Xen acpi pad */ 17392e3229dSLiu, Jinsong if (!xen_initial_domain()) 17492e3229dSLiu, Jinsong return -ENODEV; 17592e3229dSLiu, Jinsong 17692e3229dSLiu, Jinsong /* Only Xen4.2 or later support Xen acpi pad */ 17792e3229dSLiu, Jinsong if (!xen_running_on_version_or_later(4, 2)) 17892e3229dSLiu, Jinsong return -ENODEV; 17992e3229dSLiu, Jinsong 18092e3229dSLiu, Jinsong return acpi_bus_register_driver(&acpi_pad_driver); 18192e3229dSLiu, Jinsong } 18292e3229dSLiu, Jinsong subsys_initcall(xen_acpi_pad_init); 183