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
22 static DEFINE_MUTEX(xen_cpu_lock);
23 
xen_acpi_pad_idle_cpus(unsigned int idle_nums)24 static 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 
xen_acpi_pad_idle_cpus_num(void)35 static 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  */
acpi_pad_pur(acpi_handle handle)50 static 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 
acpi_pad_handle_notify(acpi_handle handle)73 static 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 
acpi_pad_notify(acpi_handle handle,u32 event,void * data)97 static 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 
acpi_pad_add(struct acpi_device * device)110 static 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 
acpi_pad_remove(struct acpi_device * device)125 static void 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 }
134 
135 static const struct acpi_device_id pad_device_ids[] = {
136 	{"ACPI000C", 0},
137 	{"", 0},
138 };
139 
140 static struct acpi_driver acpi_pad_driver = {
141 	.name = "processor_aggregator",
142 	.class = ACPI_PROCESSOR_AGGREGATOR_CLASS,
143 	.ids = pad_device_ids,
144 	.ops = {
145 		.add = acpi_pad_add,
146 		.remove = acpi_pad_remove,
147 	},
148 };
149 
xen_acpi_pad_init(void)150 static int __init xen_acpi_pad_init(void)
151 {
152 	/* Only DOM0 is responsible for Xen acpi pad */
153 	if (!xen_initial_domain())
154 		return -ENODEV;
155 
156 	/* Only Xen4.2 or later support Xen acpi pad */
157 	if (!xen_running_on_version_or_later(4, 2))
158 		return -ENODEV;
159 
160 	return acpi_bus_register_driver(&acpi_pad_driver);
161 }
162 subsys_initcall(xen_acpi_pad_init);
163