Add the rt linux 4.1.3-rt3 as base
[kvmfornfv.git] / kernel / drivers / xen / xen-acpi-pad.c
diff --git a/kernel/drivers/xen/xen-acpi-pad.c b/kernel/drivers/xen/xen-acpi-pad.c
new file mode 100644 (file)
index 0000000..f83b754
--- /dev/null
@@ -0,0 +1,170 @@
+/*
+ * 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_dom0_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_dom0_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, &param);
+       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);