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> 2192e3229dSLiu, Jinsong #include <acpi/acpi_bus.h> 2292e3229dSLiu, Jinsong #include <acpi/acpi_drivers.h> 2392e3229dSLiu, Jinsong #include <asm/xen/hypercall.h> 2492e3229dSLiu, Jinsong #include <xen/interface/version.h> 25394b40f6SKonrad Rzeszutek Wilk #include <xen/xen-ops.h> 2692e3229dSLiu, Jinsong 2792e3229dSLiu, Jinsong #define ACPI_PROCESSOR_AGGREGATOR_CLASS "acpi_pad" 2892e3229dSLiu, Jinsong #define ACPI_PROCESSOR_AGGREGATOR_DEVICE_NAME "Processor Aggregator" 2992e3229dSLiu, Jinsong #define ACPI_PROCESSOR_AGGREGATOR_NOTIFY 0x80 3092e3229dSLiu, Jinsong static DEFINE_MUTEX(xen_cpu_lock); 3192e3229dSLiu, Jinsong 3292e3229dSLiu, Jinsong static int xen_acpi_pad_idle_cpus(unsigned int idle_nums) 3392e3229dSLiu, Jinsong { 3492e3229dSLiu, Jinsong struct xen_platform_op op; 3592e3229dSLiu, Jinsong 3692e3229dSLiu, Jinsong op.cmd = XENPF_core_parking; 3792e3229dSLiu, Jinsong op.u.core_parking.type = XEN_CORE_PARKING_SET; 3892e3229dSLiu, Jinsong op.u.core_parking.idle_nums = idle_nums; 3992e3229dSLiu, Jinsong 4092e3229dSLiu, Jinsong return HYPERVISOR_dom0_op(&op); 4192e3229dSLiu, Jinsong } 4292e3229dSLiu, Jinsong 4392e3229dSLiu, Jinsong static int xen_acpi_pad_idle_cpus_num(void) 4492e3229dSLiu, Jinsong { 4592e3229dSLiu, Jinsong struct xen_platform_op op; 4692e3229dSLiu, Jinsong 4792e3229dSLiu, Jinsong op.cmd = XENPF_core_parking; 4892e3229dSLiu, Jinsong op.u.core_parking.type = XEN_CORE_PARKING_GET; 4992e3229dSLiu, Jinsong 5092e3229dSLiu, Jinsong return HYPERVISOR_dom0_op(&op) 5192e3229dSLiu, Jinsong ?: op.u.core_parking.idle_nums; 5292e3229dSLiu, Jinsong } 5392e3229dSLiu, Jinsong 5492e3229dSLiu, Jinsong /* 5592e3229dSLiu, Jinsong * Query firmware how many CPUs should be idle 5692e3229dSLiu, Jinsong * return -1 on failure 5792e3229dSLiu, Jinsong */ 5892e3229dSLiu, Jinsong static int acpi_pad_pur(acpi_handle handle) 5992e3229dSLiu, Jinsong { 6092e3229dSLiu, Jinsong struct acpi_buffer buffer = {ACPI_ALLOCATE_BUFFER, NULL}; 6192e3229dSLiu, Jinsong union acpi_object *package; 6292e3229dSLiu, Jinsong int num = -1; 6392e3229dSLiu, Jinsong 6492e3229dSLiu, Jinsong if (ACPI_FAILURE(acpi_evaluate_object(handle, "_PUR", NULL, &buffer))) 6592e3229dSLiu, Jinsong return num; 6692e3229dSLiu, Jinsong 6792e3229dSLiu, Jinsong if (!buffer.length || !buffer.pointer) 6892e3229dSLiu, Jinsong return num; 6992e3229dSLiu, Jinsong 7092e3229dSLiu, Jinsong package = buffer.pointer; 7192e3229dSLiu, Jinsong 7292e3229dSLiu, Jinsong if (package->type == ACPI_TYPE_PACKAGE && 7392e3229dSLiu, Jinsong package->package.count == 2 && 7492e3229dSLiu, Jinsong package->package.elements[0].integer.value == 1) /* rev 1 */ 7592e3229dSLiu, Jinsong num = package->package.elements[1].integer.value; 7692e3229dSLiu, Jinsong 7792e3229dSLiu, Jinsong kfree(buffer.pointer); 7892e3229dSLiu, Jinsong return num; 7992e3229dSLiu, Jinsong } 8092e3229dSLiu, Jinsong 8192e3229dSLiu, Jinsong /* Notify firmware how many CPUs are idle */ 8292e3229dSLiu, Jinsong static void acpi_pad_ost(acpi_handle handle, int stat, 8392e3229dSLiu, Jinsong uint32_t idle_nums) 8492e3229dSLiu, Jinsong { 8592e3229dSLiu, Jinsong union acpi_object params[3] = { 8692e3229dSLiu, Jinsong {.type = ACPI_TYPE_INTEGER,}, 8792e3229dSLiu, Jinsong {.type = ACPI_TYPE_INTEGER,}, 8892e3229dSLiu, Jinsong {.type = ACPI_TYPE_BUFFER,}, 8992e3229dSLiu, Jinsong }; 9092e3229dSLiu, Jinsong struct acpi_object_list arg_list = {3, params}; 9192e3229dSLiu, Jinsong 9292e3229dSLiu, Jinsong params[0].integer.value = ACPI_PROCESSOR_AGGREGATOR_NOTIFY; 9392e3229dSLiu, Jinsong params[1].integer.value = stat; 9492e3229dSLiu, Jinsong params[2].buffer.length = 4; 9592e3229dSLiu, Jinsong params[2].buffer.pointer = (void *)&idle_nums; 9692e3229dSLiu, Jinsong acpi_evaluate_object(handle, "_OST", &arg_list, NULL); 9792e3229dSLiu, Jinsong } 9892e3229dSLiu, Jinsong 9992e3229dSLiu, Jinsong static void acpi_pad_handle_notify(acpi_handle handle) 10092e3229dSLiu, Jinsong { 10192e3229dSLiu, Jinsong int idle_nums; 10292e3229dSLiu, Jinsong 10392e3229dSLiu, Jinsong mutex_lock(&xen_cpu_lock); 10492e3229dSLiu, Jinsong idle_nums = acpi_pad_pur(handle); 10592e3229dSLiu, Jinsong if (idle_nums < 0) { 10692e3229dSLiu, Jinsong mutex_unlock(&xen_cpu_lock); 10792e3229dSLiu, Jinsong return; 10892e3229dSLiu, Jinsong } 10992e3229dSLiu, Jinsong 11092e3229dSLiu, Jinsong idle_nums = xen_acpi_pad_idle_cpus(idle_nums) 11192e3229dSLiu, Jinsong ?: xen_acpi_pad_idle_cpus_num(); 11292e3229dSLiu, Jinsong if (idle_nums >= 0) 11392e3229dSLiu, Jinsong acpi_pad_ost(handle, 0, idle_nums); 11492e3229dSLiu, Jinsong mutex_unlock(&xen_cpu_lock); 11592e3229dSLiu, Jinsong } 11692e3229dSLiu, Jinsong 11792e3229dSLiu, Jinsong static void acpi_pad_notify(acpi_handle handle, u32 event, 11892e3229dSLiu, Jinsong void *data) 11992e3229dSLiu, Jinsong { 12092e3229dSLiu, Jinsong switch (event) { 12192e3229dSLiu, Jinsong case ACPI_PROCESSOR_AGGREGATOR_NOTIFY: 12292e3229dSLiu, Jinsong acpi_pad_handle_notify(handle); 12392e3229dSLiu, Jinsong break; 12492e3229dSLiu, Jinsong default: 12592e3229dSLiu, Jinsong pr_warn("Unsupported event [0x%x]\n", event); 12692e3229dSLiu, Jinsong break; 12792e3229dSLiu, Jinsong } 12892e3229dSLiu, Jinsong } 12992e3229dSLiu, Jinsong 13092e3229dSLiu, Jinsong static int acpi_pad_add(struct acpi_device *device) 13192e3229dSLiu, Jinsong { 13292e3229dSLiu, Jinsong acpi_status status; 13392e3229dSLiu, Jinsong 13492e3229dSLiu, Jinsong strcpy(acpi_device_name(device), ACPI_PROCESSOR_AGGREGATOR_DEVICE_NAME); 13592e3229dSLiu, Jinsong strcpy(acpi_device_class(device), ACPI_PROCESSOR_AGGREGATOR_CLASS); 13692e3229dSLiu, Jinsong 13792e3229dSLiu, Jinsong status = acpi_install_notify_handler(device->handle, 13892e3229dSLiu, Jinsong ACPI_DEVICE_NOTIFY, acpi_pad_notify, device); 13992e3229dSLiu, Jinsong if (ACPI_FAILURE(status)) 14092e3229dSLiu, Jinsong return -ENODEV; 14192e3229dSLiu, Jinsong 14292e3229dSLiu, Jinsong return 0; 14392e3229dSLiu, Jinsong } 14492e3229dSLiu, Jinsong 14551fac838SRafael J. Wysocki static int acpi_pad_remove(struct acpi_device *device) 14692e3229dSLiu, Jinsong { 14792e3229dSLiu, Jinsong mutex_lock(&xen_cpu_lock); 14892e3229dSLiu, Jinsong xen_acpi_pad_idle_cpus(0); 14992e3229dSLiu, Jinsong mutex_unlock(&xen_cpu_lock); 15092e3229dSLiu, Jinsong 15192e3229dSLiu, Jinsong acpi_remove_notify_handler(device->handle, 15292e3229dSLiu, Jinsong ACPI_DEVICE_NOTIFY, acpi_pad_notify); 15392e3229dSLiu, Jinsong return 0; 15492e3229dSLiu, Jinsong } 15592e3229dSLiu, Jinsong 15692e3229dSLiu, Jinsong static const struct acpi_device_id pad_device_ids[] = { 15792e3229dSLiu, Jinsong {"ACPI000C", 0}, 15892e3229dSLiu, Jinsong {"", 0}, 15992e3229dSLiu, Jinsong }; 16092e3229dSLiu, Jinsong 16192e3229dSLiu, Jinsong static struct acpi_driver acpi_pad_driver = { 16292e3229dSLiu, Jinsong .name = "processor_aggregator", 16392e3229dSLiu, Jinsong .class = ACPI_PROCESSOR_AGGREGATOR_CLASS, 16492e3229dSLiu, Jinsong .ids = pad_device_ids, 16592e3229dSLiu, Jinsong .ops = { 16692e3229dSLiu, Jinsong .add = acpi_pad_add, 16792e3229dSLiu, Jinsong .remove = acpi_pad_remove, 16892e3229dSLiu, Jinsong }, 16992e3229dSLiu, Jinsong }; 17092e3229dSLiu, Jinsong 17192e3229dSLiu, Jinsong static int __init xen_acpi_pad_init(void) 17292e3229dSLiu, Jinsong { 17392e3229dSLiu, Jinsong /* Only DOM0 is responsible for Xen acpi pad */ 17492e3229dSLiu, Jinsong if (!xen_initial_domain()) 17592e3229dSLiu, Jinsong return -ENODEV; 17692e3229dSLiu, Jinsong 17792e3229dSLiu, Jinsong /* Only Xen4.2 or later support Xen acpi pad */ 17892e3229dSLiu, Jinsong if (!xen_running_on_version_or_later(4, 2)) 17992e3229dSLiu, Jinsong return -ENODEV; 18092e3229dSLiu, Jinsong 18192e3229dSLiu, Jinsong return acpi_bus_register_driver(&acpi_pad_driver); 18292e3229dSLiu, Jinsong } 18392e3229dSLiu, Jinsong subsys_initcall(xen_acpi_pad_init); 184