xref: /freebsd/sys/dev/xen/cpu/xen_acpi_cpu.c (revision b93f47eaeef75b2b99fc1c501fb7cffac34068c7)
1*b93f47eaSRoger Pau Monné /*-
2*b93f47eaSRoger Pau Monné  * Copyright (c) 2022 Citrix Systems R&D
3*b93f47eaSRoger Pau Monné  * Copyright (c) 2003-2005 Nate Lawson (SDG)
4*b93f47eaSRoger Pau Monné  * Copyright (c) 2001 Michael Smith
5*b93f47eaSRoger Pau Monné  * All rights reserved.
6*b93f47eaSRoger Pau Monné  *
7*b93f47eaSRoger Pau Monné  * Redistribution and use in source and binary forms, with or without
8*b93f47eaSRoger Pau Monné  * modification, are permitted provided that the following conditions
9*b93f47eaSRoger Pau Monné  * are met:
10*b93f47eaSRoger Pau Monné  * 1. Redistributions of source code must retain the above copyright
11*b93f47eaSRoger Pau Monné  *    notice, this list of conditions and the following disclaimer.
12*b93f47eaSRoger Pau Monné  * 2. Redistributions in binary form must reproduce the above copyright
13*b93f47eaSRoger Pau Monné  *    notice, this list of conditions and the following disclaimer in the
14*b93f47eaSRoger Pau Monné  *    documentation and/or other materials provided with the distribution.
15*b93f47eaSRoger Pau Monné  *
16*b93f47eaSRoger Pau Monné  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17*b93f47eaSRoger Pau Monné  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18*b93f47eaSRoger Pau Monné  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19*b93f47eaSRoger Pau Monné  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20*b93f47eaSRoger Pau Monné  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21*b93f47eaSRoger Pau Monné  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22*b93f47eaSRoger Pau Monné  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23*b93f47eaSRoger Pau Monné  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24*b93f47eaSRoger Pau Monné  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25*b93f47eaSRoger Pau Monné  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26*b93f47eaSRoger Pau Monné  * SUCH DAMAGE.
27*b93f47eaSRoger Pau Monné  */
28*b93f47eaSRoger Pau Monné 
29*b93f47eaSRoger Pau Monné #include <sys/cdefs.h>
30*b93f47eaSRoger Pau Monné __FBSDID("$FreeBSD$");
31*b93f47eaSRoger Pau Monné 
32*b93f47eaSRoger Pau Monné #include "opt_acpi.h"
33*b93f47eaSRoger Pau Monné #include <sys/param.h>
34*b93f47eaSRoger Pau Monné #include <sys/bus.h>
35*b93f47eaSRoger Pau Monné #include <sys/cpu.h>
36*b93f47eaSRoger Pau Monné #include <sys/kernel.h>
37*b93f47eaSRoger Pau Monné #include <sys/malloc.h>
38*b93f47eaSRoger Pau Monné #include <sys/module.h>
39*b93f47eaSRoger Pau Monné #include <sys/pcpu.h>
40*b93f47eaSRoger Pau Monné #include <sys/power.h>
41*b93f47eaSRoger Pau Monné #include <sys/proc.h>
42*b93f47eaSRoger Pau Monné #include <sys/sched.h>
43*b93f47eaSRoger Pau Monné 
44*b93f47eaSRoger Pau Monné #include <machine/_inttypes.h>
45*b93f47eaSRoger Pau Monné 
46*b93f47eaSRoger Pau Monné #include <contrib/dev/acpica/include/acpi.h>
47*b93f47eaSRoger Pau Monné #include <contrib/dev/acpica/include/accommon.h>
48*b93f47eaSRoger Pau Monné 
49*b93f47eaSRoger Pau Monné #include <dev/acpica/acpivar.h>
50*b93f47eaSRoger Pau Monné 
51*b93f47eaSRoger Pau Monné #include <xen/xen-os.h>
52*b93f47eaSRoger Pau Monné 
53*b93f47eaSRoger Pau Monné #define ACPI_DOMAIN_COORD_TYPE_SW_ALL 0xfc
54*b93f47eaSRoger Pau Monné #define ACPI_DOMAIN_COORD_TYPE_SW_ANY 0xfd
55*b93f47eaSRoger Pau Monné #define ACPI_DOMAIN_COORD_TYPE_HW_ALL 0xfe
56*b93f47eaSRoger Pau Monné 
57*b93f47eaSRoger Pau Monné #define ACPI_NOTIFY_PERF_STATES 0x80	/* _PSS changed. */
58*b93f47eaSRoger Pau Monné #define ACPI_NOTIFY_CX_STATES	0x81	/* _CST changed. */
59*b93f47eaSRoger Pau Monné 
60*b93f47eaSRoger Pau Monné static MALLOC_DEFINE(M_XENACPI, "xen_acpi", "Xen CPU ACPI forwarder");
61*b93f47eaSRoger Pau Monné 
62*b93f47eaSRoger Pau Monné /* Hooks for the ACPI CA debugging infrastructure */
63*b93f47eaSRoger Pau Monné #define _COMPONENT ACPI_PROCESSOR
64*b93f47eaSRoger Pau Monné ACPI_MODULE_NAME("PROCESSOR")
65*b93f47eaSRoger Pau Monné 
66*b93f47eaSRoger Pau Monné struct xen_acpi_cpu_softc {
67*b93f47eaSRoger Pau Monné 	device_t cpu_dev;
68*b93f47eaSRoger Pau Monné 	ACPI_HANDLE cpu_handle;
69*b93f47eaSRoger Pau Monné 	uint32_t cpu_acpi_id;
70*b93f47eaSRoger Pau Monné 	struct xen_processor_cx *cpu_cx_states;
71*b93f47eaSRoger Pau Monné 	unsigned int cpu_cx_count;
72*b93f47eaSRoger Pau Monné 	struct xen_processor_px *cpu_px_states;
73*b93f47eaSRoger Pau Monné 	unsigned int cpu_px_count;
74*b93f47eaSRoger Pau Monné 	struct xen_pct_register control_register;
75*b93f47eaSRoger Pau Monné 	struct xen_pct_register status_register;
76*b93f47eaSRoger Pau Monné 	struct xen_psd_package psd;
77*b93f47eaSRoger Pau Monné };
78*b93f47eaSRoger Pau Monné 
79*b93f47eaSRoger Pau Monné #define	CPUDEV_DEVICE_ID "ACPI0007"
80*b93f47eaSRoger Pau Monné 
81*b93f47eaSRoger Pau Monné ACPI_SERIAL_DECL(cpu, "ACPI CPU");
82*b93f47eaSRoger Pau Monné 
83*b93f47eaSRoger Pau Monné #define device_printf(dev,...) \
84*b93f47eaSRoger Pau Monné 	if (!device_is_quiet(dev)) \
85*b93f47eaSRoger Pau Monné 		device_printf((dev), __VA_ARGS__)
86*b93f47eaSRoger Pau Monné 
87*b93f47eaSRoger Pau Monné static int
88*b93f47eaSRoger Pau Monné acpi_get_gas(const ACPI_OBJECT *res, unsigned int idx,
89*b93f47eaSRoger Pau Monné     ACPI_GENERIC_ADDRESS *gas)
90*b93f47eaSRoger Pau Monné {
91*b93f47eaSRoger Pau Monné 	const ACPI_OBJECT *obj = &res->Package.Elements[idx];
92*b93f47eaSRoger Pau Monné 
93*b93f47eaSRoger Pau Monné 	if (obj == NULL || obj->Type != ACPI_TYPE_BUFFER ||
94*b93f47eaSRoger Pau Monné 	    obj->Buffer.Length < sizeof(ACPI_GENERIC_ADDRESS) + 3)
95*b93f47eaSRoger Pau Monné 		return (EINVAL);
96*b93f47eaSRoger Pau Monné 
97*b93f47eaSRoger Pau Monné 	memcpy(gas, obj->Buffer.Pointer + 3, sizeof(*gas));
98*b93f47eaSRoger Pau Monné 
99*b93f47eaSRoger Pau Monné 	return (0);
100*b93f47eaSRoger Pau Monné }
101*b93f47eaSRoger Pau Monné 
102*b93f47eaSRoger Pau Monné static int
103*b93f47eaSRoger Pau Monné acpi_get_pct(const ACPI_OBJECT *res, unsigned int idx,
104*b93f47eaSRoger Pau Monné     struct xen_pct_register *reg)
105*b93f47eaSRoger Pau Monné {
106*b93f47eaSRoger Pau Monné 	struct {
107*b93f47eaSRoger Pau Monné 		uint8_t descriptor;
108*b93f47eaSRoger Pau Monné 		uint16_t length;
109*b93f47eaSRoger Pau Monné 		ACPI_GENERIC_ADDRESS gas;
110*b93f47eaSRoger Pau Monné 	} __packed raw;
111*b93f47eaSRoger Pau Monné 	const ACPI_OBJECT *obj = &res->Package.Elements[idx];
112*b93f47eaSRoger Pau Monné 
113*b93f47eaSRoger Pau Monné 	if (obj == NULL || obj->Type != ACPI_TYPE_BUFFER ||
114*b93f47eaSRoger Pau Monné 	    obj->Buffer.Length < sizeof(raw))
115*b93f47eaSRoger Pau Monné 		return (EINVAL);
116*b93f47eaSRoger Pau Monné 
117*b93f47eaSRoger Pau Monné 	memcpy(&raw, obj->Buffer.Pointer, sizeof(raw));
118*b93f47eaSRoger Pau Monné 	reg->descriptor = raw.descriptor;
119*b93f47eaSRoger Pau Monné 	reg->length = raw.length;
120*b93f47eaSRoger Pau Monné 	reg->space_id = raw.gas.SpaceId;
121*b93f47eaSRoger Pau Monné 	reg->bit_width = raw.gas.BitWidth;
122*b93f47eaSRoger Pau Monné 	reg->bit_offset = raw.gas.BitOffset;
123*b93f47eaSRoger Pau Monné 	reg->reserved = raw.gas.AccessWidth;
124*b93f47eaSRoger Pau Monné 	reg->address = raw.gas.Address;
125*b93f47eaSRoger Pau Monné 
126*b93f47eaSRoger Pau Monné 	return (0);
127*b93f47eaSRoger Pau Monné }
128*b93f47eaSRoger Pau Monné 
129*b93f47eaSRoger Pau Monné static int
130*b93f47eaSRoger Pau Monné xen_upload_cx(struct xen_acpi_cpu_softc *sc)
131*b93f47eaSRoger Pau Monné {
132*b93f47eaSRoger Pau Monné 	struct xen_platform_op op = {
133*b93f47eaSRoger Pau Monné 		.cmd			= XENPF_set_processor_pminfo,
134*b93f47eaSRoger Pau Monné 		.interface_version	= XENPF_INTERFACE_VERSION,
135*b93f47eaSRoger Pau Monné 		.u.set_pminfo.id	= sc->cpu_acpi_id,
136*b93f47eaSRoger Pau Monné 		.u.set_pminfo.type	= XEN_PM_CX,
137*b93f47eaSRoger Pau Monné 		.u.set_pminfo.u.power.count = sc->cpu_cx_count,
138*b93f47eaSRoger Pau Monné 		.u.set_pminfo.u.power.flags.has_cst = 1,
139*b93f47eaSRoger Pau Monné 		/* Ignore bm_check and bm_control, Xen will set those. */
140*b93f47eaSRoger Pau Monné 	};
141*b93f47eaSRoger Pau Monné 	int error;
142*b93f47eaSRoger Pau Monné 
143*b93f47eaSRoger Pau Monné 	set_xen_guest_handle(op.u.set_pminfo.u.power.states, sc->cpu_cx_states);
144*b93f47eaSRoger Pau Monné 
145*b93f47eaSRoger Pau Monné 	error = HYPERVISOR_platform_op(&op);
146*b93f47eaSRoger Pau Monné 	if (error != 0)
147*b93f47eaSRoger Pau Monné 		device_printf(sc->cpu_dev,
148*b93f47eaSRoger Pau Monné 		    "ACPI ID %u Cx upload failed: %d\n", sc->cpu_acpi_id,
149*b93f47eaSRoger Pau Monné 		    error);
150*b93f47eaSRoger Pau Monné 	return (error);
151*b93f47eaSRoger Pau Monné }
152*b93f47eaSRoger Pau Monné 
153*b93f47eaSRoger Pau Monné static int
154*b93f47eaSRoger Pau Monné xen_upload_px(struct xen_acpi_cpu_softc *sc)
155*b93f47eaSRoger Pau Monné {
156*b93f47eaSRoger Pau Monné 	struct xen_platform_op op = {
157*b93f47eaSRoger Pau Monné 		.cmd = XENPF_set_processor_pminfo,
158*b93f47eaSRoger Pau Monné 		.interface_version = XENPF_INTERFACE_VERSION,
159*b93f47eaSRoger Pau Monné 		.u.set_pminfo.id = sc->cpu_acpi_id,
160*b93f47eaSRoger Pau Monné 		.u.set_pminfo.type = XEN_PM_PX,
161*b93f47eaSRoger Pau Monné 		.u.set_pminfo.u.perf.state_count = sc->cpu_px_count,
162*b93f47eaSRoger Pau Monné 		.u.set_pminfo.u.perf.control_register = sc->control_register,
163*b93f47eaSRoger Pau Monné 		.u.set_pminfo.u.perf.status_register = sc->status_register,
164*b93f47eaSRoger Pau Monné 		.u.set_pminfo.u.perf.domain_info = sc->psd,
165*b93f47eaSRoger Pau Monné 		.u.set_pminfo.u.perf.flags = XEN_PX_PPC | XEN_PX_PCT |
166*b93f47eaSRoger Pau Monné 		    XEN_PX_PSS | XEN_PX_PSD,
167*b93f47eaSRoger Pau Monné 	};
168*b93f47eaSRoger Pau Monné 	ACPI_STATUS status;
169*b93f47eaSRoger Pau Monné 	int error;
170*b93f47eaSRoger Pau Monné 
171*b93f47eaSRoger Pau Monné 	status = acpi_GetInteger(sc->cpu_handle, "_PPC",
172*b93f47eaSRoger Pau Monné 	    &op.u.set_pminfo.u.perf.platform_limit);
173*b93f47eaSRoger Pau Monné 	if (ACPI_FAILURE(status)) {
174*b93f47eaSRoger Pau Monné 		device_printf(sc->cpu_dev, "missing _PPC object\n");
175*b93f47eaSRoger Pau Monné 		return (ENXIO);
176*b93f47eaSRoger Pau Monné 	}
177*b93f47eaSRoger Pau Monné 
178*b93f47eaSRoger Pau Monné 	set_xen_guest_handle(op.u.set_pminfo.u.perf.states, sc->cpu_px_states);
179*b93f47eaSRoger Pau Monné 
180*b93f47eaSRoger Pau Monné 	/*
181*b93f47eaSRoger Pau Monné 	 * NB: it's unclear the exact purpose of the shared_type field, or why
182*b93f47eaSRoger Pau Monné 	 * it can't be calculated by Xen itself. Naively set it here to allow
183*b93f47eaSRoger Pau Monné 	 * the upload to succeed.
184*b93f47eaSRoger Pau Monné 	 */
185*b93f47eaSRoger Pau Monné 	switch (sc->psd.coord_type) {
186*b93f47eaSRoger Pau Monné 	case ACPI_DOMAIN_COORD_TYPE_SW_ALL:
187*b93f47eaSRoger Pau Monné 		op.u.set_pminfo.u.perf.shared_type =
188*b93f47eaSRoger Pau Monné 		    XEN_CPUPERF_SHARED_TYPE_ALL;
189*b93f47eaSRoger Pau Monné 		break;
190*b93f47eaSRoger Pau Monné 
191*b93f47eaSRoger Pau Monné 	case ACPI_DOMAIN_COORD_TYPE_HW_ALL:
192*b93f47eaSRoger Pau Monné 		op.u.set_pminfo.u.perf.shared_type =
193*b93f47eaSRoger Pau Monné 		    XEN_CPUPERF_SHARED_TYPE_HW;
194*b93f47eaSRoger Pau Monné 		break;
195*b93f47eaSRoger Pau Monné 
196*b93f47eaSRoger Pau Monné 	case ACPI_DOMAIN_COORD_TYPE_SW_ANY:
197*b93f47eaSRoger Pau Monné 		op.u.set_pminfo.u.perf.shared_type =
198*b93f47eaSRoger Pau Monné 		    XEN_CPUPERF_SHARED_TYPE_ANY;
199*b93f47eaSRoger Pau Monné 		break;
200*b93f47eaSRoger Pau Monné 	default:
201*b93f47eaSRoger Pau Monné 		device_printf(sc->cpu_dev,
202*b93f47eaSRoger Pau Monné 		    "unknown coordination type %#" PRIx64 "\n",
203*b93f47eaSRoger Pau Monné 		    sc->psd.coord_type);
204*b93f47eaSRoger Pau Monné 		return (EINVAL);
205*b93f47eaSRoger Pau Monné 	}
206*b93f47eaSRoger Pau Monné 
207*b93f47eaSRoger Pau Monné 	error = HYPERVISOR_platform_op(&op);
208*b93f47eaSRoger Pau Monné 	if (error != 0)
209*b93f47eaSRoger Pau Monné 	    device_printf(sc->cpu_dev,
210*b93f47eaSRoger Pau Monné 		"ACPI ID %u Px upload failed: %d\n", sc->cpu_acpi_id, error);
211*b93f47eaSRoger Pau Monné 	return (error);
212*b93f47eaSRoger Pau Monné }
213*b93f47eaSRoger Pau Monné 
214*b93f47eaSRoger Pau Monné static int
215*b93f47eaSRoger Pau Monné acpi_set_pdc(const struct xen_acpi_cpu_softc *sc)
216*b93f47eaSRoger Pau Monné {
217*b93f47eaSRoger Pau Monné 	struct xen_platform_op op = {
218*b93f47eaSRoger Pau Monné 		.cmd			= XENPF_set_processor_pminfo,
219*b93f47eaSRoger Pau Monné 		.interface_version	= XENPF_INTERFACE_VERSION,
220*b93f47eaSRoger Pau Monné 		.u.set_pminfo.id	= -1,
221*b93f47eaSRoger Pau Monné 		.u.set_pminfo.type	= XEN_PM_PDC,
222*b93f47eaSRoger Pau Monné 	};
223*b93f47eaSRoger Pau Monné 	uint32_t pdc[3] = {1, 1};
224*b93f47eaSRoger Pau Monné 	ACPI_OBJECT arg = {
225*b93f47eaSRoger Pau Monné 		.Buffer.Type = ACPI_TYPE_BUFFER,
226*b93f47eaSRoger Pau Monné 		.Buffer.Length = sizeof(pdc),
227*b93f47eaSRoger Pau Monné 		.Buffer.Pointer = (uint8_t *)pdc,
228*b93f47eaSRoger Pau Monné 	};
229*b93f47eaSRoger Pau Monné 	ACPI_OBJECT_LIST arglist = {
230*b93f47eaSRoger Pau Monné 		.Pointer = &arg,
231*b93f47eaSRoger Pau Monné 		.Count = 1,
232*b93f47eaSRoger Pau Monné 	};
233*b93f47eaSRoger Pau Monné 	ACPI_STATUS status;
234*b93f47eaSRoger Pau Monné 	int error;
235*b93f47eaSRoger Pau Monné 
236*b93f47eaSRoger Pau Monné 	set_xen_guest_handle(op.u.set_pminfo.u.pdc, pdc);
237*b93f47eaSRoger Pau Monné 	error = HYPERVISOR_platform_op(&op);
238*b93f47eaSRoger Pau Monné 	if (error != 0) {
239*b93f47eaSRoger Pau Monné 		device_printf(sc->cpu_dev,
240*b93f47eaSRoger Pau Monné 		    "unable to get _PDC features from Xen: %d\n", error);
241*b93f47eaSRoger Pau Monné 		return (error);
242*b93f47eaSRoger Pau Monné 	}
243*b93f47eaSRoger Pau Monné 
244*b93f47eaSRoger Pau Monné 	status = AcpiEvaluateObject(sc->cpu_handle, "_PDC", &arglist, NULL);
245*b93f47eaSRoger Pau Monné 	if (ACPI_FAILURE(status)) {
246*b93f47eaSRoger Pau Monné 		device_printf(sc->cpu_dev, "unable to execute _PDC - %s\n",
247*b93f47eaSRoger Pau Monné 		    AcpiFormatException(status));
248*b93f47eaSRoger Pau Monné 		return (ENXIO);
249*b93f47eaSRoger Pau Monné 	}
250*b93f47eaSRoger Pau Monné 
251*b93f47eaSRoger Pau Monné 	return (0);
252*b93f47eaSRoger Pau Monné }
253*b93f47eaSRoger Pau Monné 
254*b93f47eaSRoger Pau Monné /*
255*b93f47eaSRoger Pau Monné  * Parse a _CST package and set up its Cx states.  Since the _CST object
256*b93f47eaSRoger Pau Monné  * can change dynamically, our notify handler may call this function
257*b93f47eaSRoger Pau Monné  * to clean up and probe the new _CST package.
258*b93f47eaSRoger Pau Monné  */
259*b93f47eaSRoger Pau Monné static int
260*b93f47eaSRoger Pau Monné acpi_fetch_cx(struct xen_acpi_cpu_softc *sc)
261*b93f47eaSRoger Pau Monné {
262*b93f47eaSRoger Pau Monné 	ACPI_STATUS status;
263*b93f47eaSRoger Pau Monné 	ACPI_BUFFER buf = {
264*b93f47eaSRoger Pau Monné 		.Length = ACPI_ALLOCATE_BUFFER,
265*b93f47eaSRoger Pau Monné 	};
266*b93f47eaSRoger Pau Monné 	ACPI_OBJECT *top;
267*b93f47eaSRoger Pau Monné 	uint32_t count;
268*b93f47eaSRoger Pau Monné 	unsigned int i;
269*b93f47eaSRoger Pau Monné 
270*b93f47eaSRoger Pau Monné 	status = AcpiEvaluateObject(sc->cpu_handle, "_CST", NULL, &buf);
271*b93f47eaSRoger Pau Monné 	if (ACPI_FAILURE(status)) {
272*b93f47eaSRoger Pau Monné 		device_printf(sc->cpu_dev, "missing _CST object\n");
273*b93f47eaSRoger Pau Monné 		return (ENXIO);
274*b93f47eaSRoger Pau Monné 	}
275*b93f47eaSRoger Pau Monné 
276*b93f47eaSRoger Pau Monné 	/* _CST is a package with a count and at least one Cx package. */
277*b93f47eaSRoger Pau Monné 	top = (ACPI_OBJECT *)buf.Pointer;
278*b93f47eaSRoger Pau Monné 	if (!ACPI_PKG_VALID(top, 2) || acpi_PkgInt32(top, 0, &count) != 0) {
279*b93f47eaSRoger Pau Monné 		device_printf(sc->cpu_dev, "invalid _CST package\n");
280*b93f47eaSRoger Pau Monné 		AcpiOsFree(buf.Pointer);
281*b93f47eaSRoger Pau Monné 		return (ENXIO);
282*b93f47eaSRoger Pau Monné 	}
283*b93f47eaSRoger Pau Monné 	if (count != top->Package.Count - 1) {
284*b93f47eaSRoger Pau Monné 		device_printf(sc->cpu_dev,
285*b93f47eaSRoger Pau Monné 		    "invalid _CST state count (%u != %u)\n",
286*b93f47eaSRoger Pau Monné 		    count, top->Package.Count - 1);
287*b93f47eaSRoger Pau Monné 		count = top->Package.Count - 1;
288*b93f47eaSRoger Pau Monné 	}
289*b93f47eaSRoger Pau Monné 
290*b93f47eaSRoger Pau Monné 	sc->cpu_cx_states = mallocarray(count, sizeof(struct xen_processor_cx),
291*b93f47eaSRoger Pau Monné 	    M_XENACPI, M_WAITOK | M_ZERO);
292*b93f47eaSRoger Pau Monné 
293*b93f47eaSRoger Pau Monné 	sc->cpu_cx_count = 0;
294*b93f47eaSRoger Pau Monné 	for (i = 0; i < count; i++) {
295*b93f47eaSRoger Pau Monné 		uint32_t type;
296*b93f47eaSRoger Pau Monné 		ACPI_GENERIC_ADDRESS gas;
297*b93f47eaSRoger Pau Monné 		ACPI_OBJECT *pkg = &top->Package.Elements[i + 1];
298*b93f47eaSRoger Pau Monné 		struct xen_processor_cx *cx_ptr =
299*b93f47eaSRoger Pau Monné 		    &sc->cpu_cx_states[sc->cpu_cx_count];
300*b93f47eaSRoger Pau Monné 
301*b93f47eaSRoger Pau Monné 		if (!ACPI_PKG_VALID(pkg, 4) ||
302*b93f47eaSRoger Pau Monné 		    acpi_PkgInt32(pkg, 1, &type) != 0 ||
303*b93f47eaSRoger Pau Monné 		    acpi_PkgInt32(pkg, 2, &cx_ptr->latency) != 0 ||
304*b93f47eaSRoger Pau Monné 		    acpi_PkgInt32(pkg, 3, &cx_ptr->power) != 0 ||
305*b93f47eaSRoger Pau Monné 		    acpi_get_gas(pkg, 0, &gas) != 0) {
306*b93f47eaSRoger Pau Monné 			device_printf(sc->cpu_dev,
307*b93f47eaSRoger Pau Monné 			    "skipping invalid _CST %u package\n",
308*b93f47eaSRoger Pau Monné 			    i + 1);
309*b93f47eaSRoger Pau Monné 			continue;
310*b93f47eaSRoger Pau Monné 		}
311*b93f47eaSRoger Pau Monné 
312*b93f47eaSRoger Pau Monné 		cx_ptr->type = type;
313*b93f47eaSRoger Pau Monné 		cx_ptr->reg.space_id = gas.SpaceId;
314*b93f47eaSRoger Pau Monné 		cx_ptr->reg.bit_width = gas.BitWidth;
315*b93f47eaSRoger Pau Monné 		cx_ptr->reg.bit_offset = gas.BitOffset;
316*b93f47eaSRoger Pau Monné 		cx_ptr->reg.access_size = gas.AccessWidth;
317*b93f47eaSRoger Pau Monné 		cx_ptr->reg.address = gas.Address;
318*b93f47eaSRoger Pau Monné 		sc->cpu_cx_count++;
319*b93f47eaSRoger Pau Monné 	}
320*b93f47eaSRoger Pau Monné 	AcpiOsFree(buf.Pointer);
321*b93f47eaSRoger Pau Monné 
322*b93f47eaSRoger Pau Monné 	if (sc->cpu_cx_count == 0) {
323*b93f47eaSRoger Pau Monné 		device_printf(sc->cpu_dev, "no valid _CST package found\n");
324*b93f47eaSRoger Pau Monné 		free(sc->cpu_cx_states, M_XENACPI);
325*b93f47eaSRoger Pau Monné 		sc->cpu_cx_states = NULL;
326*b93f47eaSRoger Pau Monné 		return (ENXIO);
327*b93f47eaSRoger Pau Monné 	}
328*b93f47eaSRoger Pau Monné 
329*b93f47eaSRoger Pau Monné 	return (0);
330*b93f47eaSRoger Pau Monné }
331*b93f47eaSRoger Pau Monné 
332*b93f47eaSRoger Pau Monné /* Probe and setup any valid performance states (Px). */
333*b93f47eaSRoger Pau Monné static int
334*b93f47eaSRoger Pau Monné acpi_fetch_px(struct xen_acpi_cpu_softc *sc)
335*b93f47eaSRoger Pau Monné {
336*b93f47eaSRoger Pau Monné 	ACPI_BUFFER buf = {
337*b93f47eaSRoger Pau Monné 		.Length = ACPI_ALLOCATE_BUFFER,
338*b93f47eaSRoger Pau Monné 	};
339*b93f47eaSRoger Pau Monné 	ACPI_OBJECT *pkg, *res;
340*b93f47eaSRoger Pau Monné 	ACPI_STATUS status;
341*b93f47eaSRoger Pau Monné 	unsigned int count, i;
342*b93f47eaSRoger Pau Monné 	int error;
343*b93f47eaSRoger Pau Monné 	uint64_t *p;
344*b93f47eaSRoger Pau Monné 
345*b93f47eaSRoger Pau Monné 	/* _PSS */
346*b93f47eaSRoger Pau Monné 	status = AcpiEvaluateObject(sc->cpu_handle, "_PSS", NULL, &buf);
347*b93f47eaSRoger Pau Monné 	if (ACPI_FAILURE(status)) {
348*b93f47eaSRoger Pau Monné 		device_printf(sc->cpu_dev, "missing _PSS object\n");
349*b93f47eaSRoger Pau Monné 		return (ENXIO);
350*b93f47eaSRoger Pau Monné 	}
351*b93f47eaSRoger Pau Monné 
352*b93f47eaSRoger Pau Monné 	pkg = (ACPI_OBJECT *)buf.Pointer;
353*b93f47eaSRoger Pau Monné 	if (!ACPI_PKG_VALID(pkg, 1)) {
354*b93f47eaSRoger Pau Monné 		device_printf(sc->cpu_dev, "invalid top level _PSS package\n");
355*b93f47eaSRoger Pau Monné 		goto error;
356*b93f47eaSRoger Pau Monné 	}
357*b93f47eaSRoger Pau Monné 	count = pkg->Package.Count;
358*b93f47eaSRoger Pau Monné 
359*b93f47eaSRoger Pau Monné 	sc->cpu_px_states = mallocarray(count, sizeof(struct xen_processor_px),
360*b93f47eaSRoger Pau Monné 	    M_XENACPI, M_WAITOK | M_ZERO);
361*b93f47eaSRoger Pau Monné 
362*b93f47eaSRoger Pau Monné 	/*
363*b93f47eaSRoger Pau Monné 	 * Each state is a package of {CoreFreq, Power, TransitionLatency,
364*b93f47eaSRoger Pau Monné 	 * BusMasterLatency, ControlVal, StatusVal}, sorted from highest
365*b93f47eaSRoger Pau Monné 	 * performance to lowest.
366*b93f47eaSRoger Pau Monné 	 */
367*b93f47eaSRoger Pau Monné 	sc->cpu_px_count = 0;
368*b93f47eaSRoger Pau Monné 	for (i = 0; i < count; i++) {
369*b93f47eaSRoger Pau Monné 		unsigned int j;
370*b93f47eaSRoger Pau Monné 
371*b93f47eaSRoger Pau Monné 		res = &pkg->Package.Elements[i];
372*b93f47eaSRoger Pau Monné 		if (!ACPI_PKG_VALID(res, 6)) {
373*b93f47eaSRoger Pau Monné 			device_printf(sc->cpu_dev,
374*b93f47eaSRoger Pau Monné 			    "invalid _PSS package idx %u\n", i);
375*b93f47eaSRoger Pau Monné 			continue;
376*b93f47eaSRoger Pau Monné 		}
377*b93f47eaSRoger Pau Monné 
378*b93f47eaSRoger Pau Monné 		/* Parse the rest of the package into the struct. */
379*b93f47eaSRoger Pau Monné 		p = (uint64_t *)&sc->cpu_px_states[sc->cpu_px_count++];
380*b93f47eaSRoger Pau Monné 		for (j = 0; j < 6; j++, p++)
381*b93f47eaSRoger Pau Monné 			acpi_PkgInt(res, j, p);
382*b93f47eaSRoger Pau Monné 	}
383*b93f47eaSRoger Pau Monné 
384*b93f47eaSRoger Pau Monné 	/* No valid Px state found so give up. */
385*b93f47eaSRoger Pau Monné 	if (sc->cpu_px_count == 0) {
386*b93f47eaSRoger Pau Monné 		device_printf(sc->cpu_dev, "no valid _PSS package found\n");
387*b93f47eaSRoger Pau Monné 		goto error;
388*b93f47eaSRoger Pau Monné 	}
389*b93f47eaSRoger Pau Monné 	AcpiOsFree(buf.Pointer);
390*b93f47eaSRoger Pau Monné 
391*b93f47eaSRoger Pau Monné 	/* _PCT */
392*b93f47eaSRoger Pau Monné 	buf.Pointer = NULL;
393*b93f47eaSRoger Pau Monné 	buf.Length = ACPI_ALLOCATE_BUFFER;
394*b93f47eaSRoger Pau Monné 	status = AcpiEvaluateObject(sc->cpu_handle, "_PCT", NULL, &buf);
395*b93f47eaSRoger Pau Monné 	if (ACPI_FAILURE(status)) {
396*b93f47eaSRoger Pau Monné 		device_printf(sc->cpu_dev, "missing _PCT object\n");
397*b93f47eaSRoger Pau Monné 		goto error;
398*b93f47eaSRoger Pau Monné 	}
399*b93f47eaSRoger Pau Monné 
400*b93f47eaSRoger Pau Monné 	/* Check the package of two registers, each a Buffer in GAS format. */
401*b93f47eaSRoger Pau Monné 	pkg = (ACPI_OBJECT *)buf.Pointer;
402*b93f47eaSRoger Pau Monné 	if (!ACPI_PKG_VALID(pkg, 2)) {
403*b93f47eaSRoger Pau Monné 		device_printf(sc->cpu_dev, "invalid top level _PCT package\n");
404*b93f47eaSRoger Pau Monné 		goto error;
405*b93f47eaSRoger Pau Monné 	}
406*b93f47eaSRoger Pau Monné 
407*b93f47eaSRoger Pau Monné 	error = acpi_get_pct(pkg, 0, &sc->control_register);
408*b93f47eaSRoger Pau Monné 	if (error != 0) {
409*b93f47eaSRoger Pau Monné 		device_printf(sc->cpu_dev,
410*b93f47eaSRoger Pau Monné 		    "unable to fetch _PCT control register: %d\n", error);
411*b93f47eaSRoger Pau Monné 		goto error;
412*b93f47eaSRoger Pau Monné 	}
413*b93f47eaSRoger Pau Monné 	error = acpi_get_pct(pkg, 1, &sc->status_register);
414*b93f47eaSRoger Pau Monné 	if (error != 0) {
415*b93f47eaSRoger Pau Monné 		device_printf(sc->cpu_dev,
416*b93f47eaSRoger Pau Monné 		    "unable to fetch _PCT status register: %d\n", error);
417*b93f47eaSRoger Pau Monné 		goto error;
418*b93f47eaSRoger Pau Monné 	}
419*b93f47eaSRoger Pau Monné 	AcpiOsFree(buf.Pointer);
420*b93f47eaSRoger Pau Monné 
421*b93f47eaSRoger Pau Monné 	/* _PSD */
422*b93f47eaSRoger Pau Monné 	buf.Pointer = NULL;
423*b93f47eaSRoger Pau Monné 	buf.Length = ACPI_ALLOCATE_BUFFER;
424*b93f47eaSRoger Pau Monné 	status = AcpiEvaluateObject(sc->cpu_handle, "_PSD", NULL, &buf);
425*b93f47eaSRoger Pau Monné 	if (ACPI_FAILURE(status)) {
426*b93f47eaSRoger Pau Monné 		device_printf(sc->cpu_dev, "missing _PSD object\n");
427*b93f47eaSRoger Pau Monné 		goto error;
428*b93f47eaSRoger Pau Monné 	}
429*b93f47eaSRoger Pau Monné 
430*b93f47eaSRoger Pau Monné 	pkg = (ACPI_OBJECT *)buf.Pointer;
431*b93f47eaSRoger Pau Monné 	if (!ACPI_PKG_VALID(pkg, 1)) {
432*b93f47eaSRoger Pau Monné 		device_printf(sc->cpu_dev, "invalid top level _PSD package\n");
433*b93f47eaSRoger Pau Monné 		goto error;
434*b93f47eaSRoger Pau Monné 	}
435*b93f47eaSRoger Pau Monné 
436*b93f47eaSRoger Pau Monné 	res = &pkg->Package.Elements[0];
437*b93f47eaSRoger Pau Monné 	if (!ACPI_PKG_VALID(res, 5)) {
438*b93f47eaSRoger Pau Monné 		printf("invalid _PSD package\n");
439*b93f47eaSRoger Pau Monné 		goto error;
440*b93f47eaSRoger Pau Monné 	}
441*b93f47eaSRoger Pau Monné 
442*b93f47eaSRoger Pau Monné 	p = (uint64_t *)&sc->psd;
443*b93f47eaSRoger Pau Monné 	for (i = 0; i < 5; i++, p++)
444*b93f47eaSRoger Pau Monné 		acpi_PkgInt(res, i, p);
445*b93f47eaSRoger Pau Monné 	AcpiOsFree(buf.Pointer);
446*b93f47eaSRoger Pau Monné 
447*b93f47eaSRoger Pau Monné 	return (0);
448*b93f47eaSRoger Pau Monné 
449*b93f47eaSRoger Pau Monné error:
450*b93f47eaSRoger Pau Monné 	if (buf.Pointer != NULL)
451*b93f47eaSRoger Pau Monné 		AcpiOsFree(buf.Pointer);
452*b93f47eaSRoger Pau Monné 	if (sc->cpu_px_states != NULL) {
453*b93f47eaSRoger Pau Monné 		free(sc->cpu_px_states, M_XENACPI);
454*b93f47eaSRoger Pau Monné 		sc->cpu_px_states = NULL;
455*b93f47eaSRoger Pau Monné 	}
456*b93f47eaSRoger Pau Monné 	return (ENXIO);
457*b93f47eaSRoger Pau Monné }
458*b93f47eaSRoger Pau Monné 
459*b93f47eaSRoger Pau Monné static void
460*b93f47eaSRoger Pau Monné acpi_notify(ACPI_HANDLE h, UINT32 notify, void *context)
461*b93f47eaSRoger Pau Monné {
462*b93f47eaSRoger Pau Monné 	struct xen_acpi_cpu_softc *sc = context;
463*b93f47eaSRoger Pau Monné 
464*b93f47eaSRoger Pau Monné 	switch (notify) {
465*b93f47eaSRoger Pau Monné 	case ACPI_NOTIFY_PERF_STATES:
466*b93f47eaSRoger Pau Monné 		if (acpi_fetch_px(sc) != 0)
467*b93f47eaSRoger Pau Monné 			break;
468*b93f47eaSRoger Pau Monné 		xen_upload_px(sc);
469*b93f47eaSRoger Pau Monné 		free(sc->cpu_px_states, M_XENACPI);
470*b93f47eaSRoger Pau Monné 		sc->cpu_px_states = NULL;
471*b93f47eaSRoger Pau Monné 		break;
472*b93f47eaSRoger Pau Monné 
473*b93f47eaSRoger Pau Monné 	case ACPI_NOTIFY_CX_STATES:
474*b93f47eaSRoger Pau Monné 		if (acpi_fetch_cx(sc) != 0)
475*b93f47eaSRoger Pau Monné 			break;
476*b93f47eaSRoger Pau Monné 		xen_upload_cx(sc);
477*b93f47eaSRoger Pau Monné 		free(sc->cpu_cx_states, M_XENACPI);
478*b93f47eaSRoger Pau Monné 		sc->cpu_cx_states = NULL;
479*b93f47eaSRoger Pau Monné 		break;
480*b93f47eaSRoger Pau Monné 	}
481*b93f47eaSRoger Pau Monné }
482*b93f47eaSRoger Pau Monné 
483*b93f47eaSRoger Pau Monné static int
484*b93f47eaSRoger Pau Monné xen_acpi_cpu_probe(device_t dev)
485*b93f47eaSRoger Pau Monné {
486*b93f47eaSRoger Pau Monné 	static char *cpudev_ids[] = { CPUDEV_DEVICE_ID, NULL };
487*b93f47eaSRoger Pau Monné 	ACPI_OBJECT_TYPE type = acpi_get_type(dev);
488*b93f47eaSRoger Pau Monné 
489*b93f47eaSRoger Pau Monné 	if (!xen_initial_domain())
490*b93f47eaSRoger Pau Monné 		return (ENXIO);
491*b93f47eaSRoger Pau Monné 	if (type != ACPI_TYPE_PROCESSOR && type != ACPI_TYPE_DEVICE)
492*b93f47eaSRoger Pau Monné 		return (ENXIO);
493*b93f47eaSRoger Pau Monné 	if (type == ACPI_TYPE_DEVICE &&
494*b93f47eaSRoger Pau Monné 	    ACPI_ID_PROBE(device_get_parent(dev), dev, cpudev_ids, NULL) >= 0)
495*b93f47eaSRoger Pau Monné 		return (ENXIO);
496*b93f47eaSRoger Pau Monné 
497*b93f47eaSRoger Pau Monné 	device_set_desc(dev, "XEN ACPI CPU");
498*b93f47eaSRoger Pau Monné 	if (!bootverbose)
499*b93f47eaSRoger Pau Monné 		device_quiet(dev);
500*b93f47eaSRoger Pau Monné 
501*b93f47eaSRoger Pau Monné 	/*
502*b93f47eaSRoger Pau Monné 	 * Use SPECIFIC because when running as a Xen dom0 the ACPI PROCESSOR
503*b93f47eaSRoger Pau Monné 	 * data is the native one, and needs to be forwarded to Xen but not
504*b93f47eaSRoger Pau Monné 	 * used by FreeBSD itself.
505*b93f47eaSRoger Pau Monné 	 */
506*b93f47eaSRoger Pau Monné 	return (BUS_PROBE_SPECIFIC);
507*b93f47eaSRoger Pau Monné }
508*b93f47eaSRoger Pau Monné 
509*b93f47eaSRoger Pau Monné static int
510*b93f47eaSRoger Pau Monné xen_acpi_cpu_attach(device_t dev)
511*b93f47eaSRoger Pau Monné {
512*b93f47eaSRoger Pau Monné 	struct xen_acpi_cpu_softc *sc = device_get_softc(dev);
513*b93f47eaSRoger Pau Monné 	ACPI_STATUS status;
514*b93f47eaSRoger Pau Monné 	int error;
515*b93f47eaSRoger Pau Monné 
516*b93f47eaSRoger Pau Monné 	sc->cpu_dev = dev;
517*b93f47eaSRoger Pau Monné 	sc->cpu_handle = acpi_get_handle(dev);
518*b93f47eaSRoger Pau Monné 
519*b93f47eaSRoger Pau Monné 	if (acpi_get_type(dev) == ACPI_TYPE_PROCESSOR) {
520*b93f47eaSRoger Pau Monné 		ACPI_BUFFER buf = {
521*b93f47eaSRoger Pau Monné 			.Length = ACPI_ALLOCATE_BUFFER,
522*b93f47eaSRoger Pau Monné 		};
523*b93f47eaSRoger Pau Monné 		ACPI_OBJECT *obj;
524*b93f47eaSRoger Pau Monné 
525*b93f47eaSRoger Pau Monné 		status = AcpiEvaluateObject(sc->cpu_handle, NULL, NULL, &buf);
526*b93f47eaSRoger Pau Monné 		if (ACPI_FAILURE(status)) {
527*b93f47eaSRoger Pau Monné 			device_printf(dev,
528*b93f47eaSRoger Pau Monné 			    "attach failed to get Processor obj - %s\n",
529*b93f47eaSRoger Pau Monné 			    AcpiFormatException(status));
530*b93f47eaSRoger Pau Monné 			return (ENXIO);
531*b93f47eaSRoger Pau Monné 		}
532*b93f47eaSRoger Pau Monné 		obj = (ACPI_OBJECT *)buf.Pointer;
533*b93f47eaSRoger Pau Monné 		sc->cpu_acpi_id = obj->Processor.ProcId;
534*b93f47eaSRoger Pau Monné 		AcpiOsFree(obj);
535*b93f47eaSRoger Pau Monné 	} else {
536*b93f47eaSRoger Pau Monné 		KASSERT(acpi_get_type(dev) == ACPI_TYPE_DEVICE,
537*b93f47eaSRoger Pau Monné 		    ("Unexpected ACPI object"));
538*b93f47eaSRoger Pau Monné 		status = acpi_GetInteger(sc->cpu_handle, "_UID",
539*b93f47eaSRoger Pau Monné 		    &sc->cpu_acpi_id);
540*b93f47eaSRoger Pau Monné 		if (ACPI_FAILURE(status)) {
541*b93f47eaSRoger Pau Monné 			device_printf(dev, "device object has bad value - %s\n",
542*b93f47eaSRoger Pau Monné 			    AcpiFormatException(status));
543*b93f47eaSRoger Pau Monné 			return (ENXIO);
544*b93f47eaSRoger Pau Monné 		}
545*b93f47eaSRoger Pau Monné 	}
546*b93f47eaSRoger Pau Monné 
547*b93f47eaSRoger Pau Monné 	/*
548*b93f47eaSRoger Pau Monné 	 * Install the notify handler now: even if we fail to parse or upload
549*b93f47eaSRoger Pau Monné 	 * the states it shouldn't prevent us from attempting to parse further
550*b93f47eaSRoger Pau Monné 	 * updates.
551*b93f47eaSRoger Pau Monné 	 */
552*b93f47eaSRoger Pau Monné 	status = AcpiInstallNotifyHandler(sc->cpu_handle, ACPI_DEVICE_NOTIFY,
553*b93f47eaSRoger Pau Monné 	    acpi_notify, sc);
554*b93f47eaSRoger Pau Monné 	if (ACPI_FAILURE(status))
555*b93f47eaSRoger Pau Monné 		device_printf(dev, "failed to register notify handler - %s\n",
556*b93f47eaSRoger Pau Monné 		    AcpiFormatException(status));
557*b93f47eaSRoger Pau Monné 
558*b93f47eaSRoger Pau Monné 	/*
559*b93f47eaSRoger Pau Monné 	 * Don't report errors: it's likely there are processor objects
560*b93f47eaSRoger Pau Monné 	 * belonging to CPUs that are not online, but the MADT provided to
561*b93f47eaSRoger Pau Monné 	 * FreeBSD is crafted to report the number of CPUs available to dom0.
562*b93f47eaSRoger Pau Monné 	 *
563*b93f47eaSRoger Pau Monné 	 * Parsing or uploading those states could result in errors, just
564*b93f47eaSRoger Pau Monné 	 * ignore them in order to avoid pointless noise.
565*b93f47eaSRoger Pau Monné 	 */
566*b93f47eaSRoger Pau Monné 	error = acpi_set_pdc(sc);
567*b93f47eaSRoger Pau Monné 	if (error != 0)
568*b93f47eaSRoger Pau Monné 		return (0);
569*b93f47eaSRoger Pau Monné 
570*b93f47eaSRoger Pau Monné 	error = acpi_fetch_px(sc);
571*b93f47eaSRoger Pau Monné 	if (error != 0)
572*b93f47eaSRoger Pau Monné 		return (0);
573*b93f47eaSRoger Pau Monné 	error = xen_upload_px(sc);
574*b93f47eaSRoger Pau Monné 	free(sc->cpu_px_states, M_XENACPI);
575*b93f47eaSRoger Pau Monné 	sc->cpu_px_states = NULL;
576*b93f47eaSRoger Pau Monné 	if (error != 0)
577*b93f47eaSRoger Pau Monné 		return (0);
578*b93f47eaSRoger Pau Monné 
579*b93f47eaSRoger Pau Monné 	error = acpi_fetch_cx(sc);
580*b93f47eaSRoger Pau Monné 	if (error != 0)
581*b93f47eaSRoger Pau Monné 		return (0);
582*b93f47eaSRoger Pau Monné 	xen_upload_cx(sc);
583*b93f47eaSRoger Pau Monné 	free(sc->cpu_cx_states, M_XENACPI);
584*b93f47eaSRoger Pau Monné 	sc->cpu_cx_states = NULL;
585*b93f47eaSRoger Pau Monné 
586*b93f47eaSRoger Pau Monné 	return (0);
587*b93f47eaSRoger Pau Monné }
588*b93f47eaSRoger Pau Monné 
589*b93f47eaSRoger Pau Monné static device_method_t xen_acpi_cpu_methods[] = {
590*b93f47eaSRoger Pau Monné     /* Device interface */
591*b93f47eaSRoger Pau Monné     DEVMETHOD(device_probe, xen_acpi_cpu_probe),
592*b93f47eaSRoger Pau Monné     DEVMETHOD(device_attach, xen_acpi_cpu_attach),
593*b93f47eaSRoger Pau Monné 
594*b93f47eaSRoger Pau Monné     DEVMETHOD_END
595*b93f47eaSRoger Pau Monné };
596*b93f47eaSRoger Pau Monné 
597*b93f47eaSRoger Pau Monné static driver_t xen_acpi_cpu_driver = {
598*b93f47eaSRoger Pau Monné     "xen cpu",
599*b93f47eaSRoger Pau Monné     xen_acpi_cpu_methods,
600*b93f47eaSRoger Pau Monné     sizeof(struct xen_acpi_cpu_softc),
601*b93f47eaSRoger Pau Monné };
602*b93f47eaSRoger Pau Monné 
603*b93f47eaSRoger Pau Monné static devclass_t xen_acpi_cpu_devclass;
604*b93f47eaSRoger Pau Monné DRIVER_MODULE(xen_cpu, acpi, xen_acpi_cpu_driver, xen_acpi_cpu_devclass, 0, 0);
605*b93f47eaSRoger Pau Monné MODULE_DEPEND(xen_cpu, acpi, 1, 1, 1);
606