xref: /netbsd-src/sys/dev/acpi/acpi_cppc.c (revision 3772555307d774424243e6d1cdc66aa160602c26)
1*37725553Sthorpej /* $NetBSD: acpi_cppc.c,v 1.2 2021/01/29 15:49:55 thorpej Exp $ */
2ad3a4041Sjmcneill 
3ad3a4041Sjmcneill /*-
4ad3a4041Sjmcneill  * Copyright (c) 2020 Jared McNeill <jmcneill@invisible.ca>
5ad3a4041Sjmcneill  * All rights reserved.
6ad3a4041Sjmcneill  *
7ad3a4041Sjmcneill  * Redistribution and use in source and binary forms, with or without
8ad3a4041Sjmcneill  * modification, are permitted provided that the following conditions
9ad3a4041Sjmcneill  * are met:
10ad3a4041Sjmcneill  * 1. Redistributions of source code must retain the above copyright
11ad3a4041Sjmcneill  *    notice, this list of conditions and the following disclaimer.
12ad3a4041Sjmcneill  * 2. Redistributions in binary form must reproduce the above copyright
13ad3a4041Sjmcneill  *    notice, this list of conditions and the following disclaimer in the
14ad3a4041Sjmcneill  *    documentation and/or other materials provided with the distribution.
15ad3a4041Sjmcneill  *
16ad3a4041Sjmcneill  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
17ad3a4041Sjmcneill  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
18ad3a4041Sjmcneill  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
19ad3a4041Sjmcneill  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
20ad3a4041Sjmcneill  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21ad3a4041Sjmcneill  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22ad3a4041Sjmcneill  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23ad3a4041Sjmcneill  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24ad3a4041Sjmcneill  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25ad3a4041Sjmcneill  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26ad3a4041Sjmcneill  * POSSIBILITY OF SUCH DAMAGE.
27ad3a4041Sjmcneill  */
28ad3a4041Sjmcneill 
29ad3a4041Sjmcneill /*
30ad3a4041Sjmcneill  * ACPI Collaborative Processor Performance Control support.
31ad3a4041Sjmcneill  */
32ad3a4041Sjmcneill 
33ad3a4041Sjmcneill #include <sys/cdefs.h>
34*37725553Sthorpej __KERNEL_RCSID(0, "$NetBSD: acpi_cppc.c,v 1.2 2021/01/29 15:49:55 thorpej Exp $");
35ad3a4041Sjmcneill 
36ad3a4041Sjmcneill #include <sys/param.h>
37ad3a4041Sjmcneill #include <sys/bus.h>
38ad3a4041Sjmcneill #include <sys/cpu.h>
39ad3a4041Sjmcneill #include <sys/device.h>
40ad3a4041Sjmcneill #include <sys/kmem.h>
41ad3a4041Sjmcneill #include <sys/sysctl.h>
42ad3a4041Sjmcneill 
43ad3a4041Sjmcneill #include <dev/acpi/acpireg.h>
44ad3a4041Sjmcneill #include <dev/acpi/acpivar.h>
45ad3a4041Sjmcneill #include <dev/acpi/acpi_pcc.h>
46ad3a4041Sjmcneill 
47ad3a4041Sjmcneill #include <external/bsd/acpica/dist/include/amlresrc.h>
48ad3a4041Sjmcneill 
49ad3a4041Sjmcneill /* _CPC package elements */
50ad3a4041Sjmcneill typedef enum CPCPackageElement {
51ad3a4041Sjmcneill 	CPCNumEntries,
52ad3a4041Sjmcneill 	CPCRevision,
53ad3a4041Sjmcneill 	CPCHighestPerformance,
54ad3a4041Sjmcneill 	CPCNominalPerformance,
55ad3a4041Sjmcneill 	CPCLowestNonlinearPerformance,
56ad3a4041Sjmcneill 	CPCLowestPerformance,
57ad3a4041Sjmcneill 	CPCGuaranteedPerformanceReg,
58ad3a4041Sjmcneill 	CPCDesiredPerformanceReg,
59ad3a4041Sjmcneill 	CPCMinimumPerformanceReg,
60ad3a4041Sjmcneill 	CPCMaximumPerformanceReg,
61ad3a4041Sjmcneill 	CPCPerformanceReductionToleranceReg,
62ad3a4041Sjmcneill 	CPCTimeWindowReg,
63ad3a4041Sjmcneill 	CPCCounterWraparoundTime,
64ad3a4041Sjmcneill 	CPCReferencePerformanceCounterReg,
65ad3a4041Sjmcneill 	CPCDeliveredPerformanceCounterReg,
66ad3a4041Sjmcneill 	CPCPerformanceLimitedReg,
67ad3a4041Sjmcneill 	CPCCPPCEnableReg,
68ad3a4041Sjmcneill 	CPCAutonomousSelectionEnable,
69ad3a4041Sjmcneill 	CPCAutonomousActivityWindowReg,
70ad3a4041Sjmcneill 	CPCEnergyPerformancePreferenceReg,
71ad3a4041Sjmcneill 	CPCReferencePerformance,
72ad3a4041Sjmcneill 	CPCLowestFrequency,
73ad3a4041Sjmcneill 	CPCNominalFrequency,
74ad3a4041Sjmcneill } CPCPackageElement;
75ad3a4041Sjmcneill 
76ad3a4041Sjmcneill /* PCC command numbers */
77ad3a4041Sjmcneill #define	CPPC_PCC_READ	0x00
78ad3a4041Sjmcneill #define	CPPC_PCC_WRITE	0x01
79ad3a4041Sjmcneill 
80ad3a4041Sjmcneill struct cppc_softc {
81ad3a4041Sjmcneill 	device_t		sc_dev;
82ad3a4041Sjmcneill 	struct cpu_info	*	sc_cpuinfo;
83ad3a4041Sjmcneill 	ACPI_HANDLE		sc_handle;
84ad3a4041Sjmcneill 	ACPI_OBJECT *		sc_cpc;
85ad3a4041Sjmcneill 	u_int			sc_ncpc;
86ad3a4041Sjmcneill 
87ad3a4041Sjmcneill 	char *			sc_available;
88ad3a4041Sjmcneill 	int			sc_node_target;
89ad3a4041Sjmcneill 	int			sc_node_current;
90ad3a4041Sjmcneill 	ACPI_INTEGER		sc_max_target;
91ad3a4041Sjmcneill 	ACPI_INTEGER		sc_min_target;
92ad3a4041Sjmcneill };
93ad3a4041Sjmcneill 
94ad3a4041Sjmcneill static int		cppc_match(device_t, cfdata_t, void *);
95ad3a4041Sjmcneill static void		cppc_attach(device_t, device_t, void *);
96ad3a4041Sjmcneill 
97ad3a4041Sjmcneill static ACPI_STATUS 	cppc_parse_cpc(struct cppc_softc *);
98ad3a4041Sjmcneill static ACPI_STATUS 	cppc_cpufreq_init(struct cppc_softc *);
99ad3a4041Sjmcneill static ACPI_STATUS	cppc_read(struct cppc_softc *, CPCPackageElement,
100ad3a4041Sjmcneill 				  ACPI_INTEGER *);
101ad3a4041Sjmcneill static ACPI_STATUS	cppc_write(struct cppc_softc *, CPCPackageElement,
102ad3a4041Sjmcneill 				   ACPI_INTEGER);
103ad3a4041Sjmcneill 
104ad3a4041Sjmcneill CFATTACH_DECL_NEW(acpicppc, sizeof(struct cppc_softc),
105ad3a4041Sjmcneill     cppc_match, cppc_attach, NULL, NULL);
106ad3a4041Sjmcneill 
107*37725553Sthorpej static const struct device_compatible_entry compat_data[] = {
108*37725553Sthorpej 	{ .compat = "ACPI0007" },	/* ACPI Processor Device */
109*37725553Sthorpej 	DEVICE_COMPAT_EOL
110ad3a4041Sjmcneill };
111ad3a4041Sjmcneill 
112ad3a4041Sjmcneill static int
cppc_match(device_t parent,cfdata_t cf,void * aux)113ad3a4041Sjmcneill cppc_match(device_t parent, cfdata_t cf, void *aux)
114ad3a4041Sjmcneill {
115ad3a4041Sjmcneill 	struct acpi_attach_args * const aa = aux;
116ad3a4041Sjmcneill 	ACPI_HANDLE handle;
117ad3a4041Sjmcneill 	ACPI_STATUS rv;
118ad3a4041Sjmcneill 
119*37725553Sthorpej 	if (acpi_compatible_match(aa, compat_data) == 0)
120ad3a4041Sjmcneill 		return 0;
121ad3a4041Sjmcneill 
122ad3a4041Sjmcneill 	rv = AcpiGetHandle(aa->aa_node->ad_handle, "_CPC", &handle);
123ad3a4041Sjmcneill 	if (ACPI_FAILURE(rv)) {
124ad3a4041Sjmcneill 		return 0;
125ad3a4041Sjmcneill 	}
126ad3a4041Sjmcneill 
127ad3a4041Sjmcneill 	if (acpi_match_cpu_handle(aa->aa_node->ad_handle) == NULL) {
128ad3a4041Sjmcneill 		return 0;
129ad3a4041Sjmcneill 	}
130ad3a4041Sjmcneill 
131ad3a4041Sjmcneill 	/* When CPPC and P-states/T-states are both available, prefer CPPC */
132*37725553Sthorpej 	return ACPI_MATCHSCORE_CID_MAX + 1;
133ad3a4041Sjmcneill }
134ad3a4041Sjmcneill 
135ad3a4041Sjmcneill static void
cppc_attach(device_t parent,device_t self,void * aux)136ad3a4041Sjmcneill cppc_attach(device_t parent, device_t self, void *aux)
137ad3a4041Sjmcneill {
138ad3a4041Sjmcneill 	struct cppc_softc * const sc = device_private(self);
139ad3a4041Sjmcneill 	struct acpi_attach_args * const aa = aux;
140ad3a4041Sjmcneill 	ACPI_HANDLE handle = aa->aa_node->ad_handle;
141ad3a4041Sjmcneill 	struct cpu_info *ci;
142ad3a4041Sjmcneill 	ACPI_STATUS rv;
143ad3a4041Sjmcneill 
144ad3a4041Sjmcneill 	ci = acpi_match_cpu_handle(handle);
145ad3a4041Sjmcneill 	KASSERT(ci != NULL);
146ad3a4041Sjmcneill 
147ad3a4041Sjmcneill 	aprint_naive("\n");
148ad3a4041Sjmcneill 	aprint_normal(": Processor Performance Control (%s)\n", cpu_name(ci));
149ad3a4041Sjmcneill 
150ad3a4041Sjmcneill 	sc->sc_dev = self;
151ad3a4041Sjmcneill 	sc->sc_cpuinfo = ci;
152ad3a4041Sjmcneill 	sc->sc_handle = handle;
153ad3a4041Sjmcneill 
154ad3a4041Sjmcneill 	rv = cppc_parse_cpc(sc);
155ad3a4041Sjmcneill 	if (ACPI_FAILURE(rv)) {
156ad3a4041Sjmcneill 		aprint_error_dev(self, "failed to parse CPC package: %s\n",
157ad3a4041Sjmcneill 		    AcpiFormatException(rv));
158ad3a4041Sjmcneill 		return;
159ad3a4041Sjmcneill 	}
160ad3a4041Sjmcneill 
161ad3a4041Sjmcneill 	cppc_cpufreq_init(sc);
162ad3a4041Sjmcneill }
163ad3a4041Sjmcneill 
164ad3a4041Sjmcneill /*
165ad3a4041Sjmcneill  * cppc_parse_cpc --
166ad3a4041Sjmcneill  *
167ad3a4041Sjmcneill  *	Read and verify the contents of the _CPC package.
168ad3a4041Sjmcneill  */
169ad3a4041Sjmcneill static ACPI_STATUS
cppc_parse_cpc(struct cppc_softc * sc)170ad3a4041Sjmcneill cppc_parse_cpc(struct cppc_softc *sc)
171ad3a4041Sjmcneill {
172ad3a4041Sjmcneill 	ACPI_BUFFER buf;
173ad3a4041Sjmcneill 	ACPI_STATUS rv;
174ad3a4041Sjmcneill 
175ad3a4041Sjmcneill 	buf.Pointer = NULL;
176ad3a4041Sjmcneill 	buf.Length = ACPI_ALLOCATE_BUFFER;
177ad3a4041Sjmcneill 	rv = AcpiEvaluateObjectTyped(sc->sc_handle, "_CPC", NULL, &buf,
178ad3a4041Sjmcneill 	    ACPI_TYPE_PACKAGE);
179ad3a4041Sjmcneill 	if (ACPI_FAILURE(rv)) {
180ad3a4041Sjmcneill 		return rv;
181ad3a4041Sjmcneill 	}
182ad3a4041Sjmcneill 
183ad3a4041Sjmcneill 	sc->sc_cpc = (ACPI_OBJECT *)buf.Pointer;
184ad3a4041Sjmcneill 	if (sc->sc_cpc->Package.Count == 0) {
185ad3a4041Sjmcneill 		return AE_NOT_EXIST;
186ad3a4041Sjmcneill 	}
187ad3a4041Sjmcneill 	if (sc->sc_cpc->Package.Elements[CPCNumEntries].Type !=
188ad3a4041Sjmcneill 	    ACPI_TYPE_INTEGER) {
189ad3a4041Sjmcneill 		return AE_TYPE;
190ad3a4041Sjmcneill 	}
191ad3a4041Sjmcneill 	sc->sc_ncpc =
192ad3a4041Sjmcneill 	    sc->sc_cpc->Package.Elements[CPCNumEntries].Integer.Value;
193ad3a4041Sjmcneill 
194ad3a4041Sjmcneill 	return AE_OK;
195ad3a4041Sjmcneill }
196ad3a4041Sjmcneill 
197ad3a4041Sjmcneill /*
198ad3a4041Sjmcneill  * cppc_cpufreq_sysctl --
199ad3a4041Sjmcneill  *
200ad3a4041Sjmcneill  *	sysctl helper function for machdep.cpu.cpuN.{target,current}
201ad3a4041Sjmcneill  *	nodes.
202ad3a4041Sjmcneill  */
203ad3a4041Sjmcneill static int
cppc_cpufreq_sysctl(SYSCTLFN_ARGS)204ad3a4041Sjmcneill cppc_cpufreq_sysctl(SYSCTLFN_ARGS)
205ad3a4041Sjmcneill {
206ad3a4041Sjmcneill 	struct cppc_softc * const sc = rnode->sysctl_data;
207ad3a4041Sjmcneill 	struct sysctlnode node;
208ad3a4041Sjmcneill 	u_int fq, oldfq = 0;
209ad3a4041Sjmcneill 	ACPI_INTEGER val;
210ad3a4041Sjmcneill 	ACPI_STATUS rv;
211ad3a4041Sjmcneill 	int error;
212ad3a4041Sjmcneill 
213ad3a4041Sjmcneill 	node = *rnode;
214ad3a4041Sjmcneill 	node.sysctl_data = &fq;
215ad3a4041Sjmcneill 
216ad3a4041Sjmcneill 	if (rnode->sysctl_num == sc->sc_node_target) {
217ad3a4041Sjmcneill 		rv = cppc_read(sc, CPCDesiredPerformanceReg, &val);
218ad3a4041Sjmcneill 	} else {
219ad3a4041Sjmcneill 		/*
220ad3a4041Sjmcneill 		 * XXX We should measure the delivered performance and
221ad3a4041Sjmcneill 		 *     report it here. For now, just report the desired
222ad3a4041Sjmcneill 		 *     performance level.
223ad3a4041Sjmcneill 		 */
224ad3a4041Sjmcneill 		rv = cppc_read(sc, CPCDesiredPerformanceReg, &val);
225ad3a4041Sjmcneill 	}
226ad3a4041Sjmcneill 	if (ACPI_FAILURE(rv)) {
227ad3a4041Sjmcneill 		return EIO;
228ad3a4041Sjmcneill 	}
229ad3a4041Sjmcneill 	if (val > UINT32_MAX) {
230ad3a4041Sjmcneill 		return ERANGE;
231ad3a4041Sjmcneill 	}
232ad3a4041Sjmcneill 	fq = (u_int)val;
233ad3a4041Sjmcneill 
234ad3a4041Sjmcneill 	if (rnode->sysctl_num == sc->sc_node_target) {
235ad3a4041Sjmcneill 		oldfq = fq;
236ad3a4041Sjmcneill 	}
237ad3a4041Sjmcneill 
238ad3a4041Sjmcneill 	error = sysctl_lookup(SYSCTLFN_CALL(&node));
239ad3a4041Sjmcneill 	if (error != 0 || newp == NULL) {
240ad3a4041Sjmcneill 		return error;
241ad3a4041Sjmcneill 	}
242ad3a4041Sjmcneill 
243ad3a4041Sjmcneill 	if (fq == oldfq || rnode->sysctl_num != sc->sc_node_target) {
244ad3a4041Sjmcneill 		return 0;
245ad3a4041Sjmcneill 	}
246ad3a4041Sjmcneill 
247ad3a4041Sjmcneill 	if (fq < sc->sc_min_target || fq > sc->sc_max_target) {
248ad3a4041Sjmcneill 		return EINVAL;
249ad3a4041Sjmcneill 	}
250ad3a4041Sjmcneill 
251ad3a4041Sjmcneill 	rv = cppc_write(sc, CPCDesiredPerformanceReg, fq);
252ad3a4041Sjmcneill 	if (ACPI_FAILURE(rv)) {
253ad3a4041Sjmcneill 		return EIO;
254ad3a4041Sjmcneill 	}
255ad3a4041Sjmcneill 
256ad3a4041Sjmcneill 	return 0;
257ad3a4041Sjmcneill }
258ad3a4041Sjmcneill 
259ad3a4041Sjmcneill /*
260ad3a4041Sjmcneill  * cppc_cpufreq_init --
261ad3a4041Sjmcneill  *
262ad3a4041Sjmcneill  *	Create sysctl machdep.cpu.cpuN.* sysctl tree.
263ad3a4041Sjmcneill  */
264ad3a4041Sjmcneill static ACPI_STATUS
cppc_cpufreq_init(struct cppc_softc * sc)265ad3a4041Sjmcneill cppc_cpufreq_init(struct cppc_softc *sc)
266ad3a4041Sjmcneill {
267ad3a4041Sjmcneill 	static CPCPackageElement perf_regs[4] = {
268ad3a4041Sjmcneill 		CPCHighestPerformance,
269ad3a4041Sjmcneill 		CPCNominalPerformance,
270ad3a4041Sjmcneill 		CPCLowestNonlinearPerformance,
271ad3a4041Sjmcneill 		CPCLowestPerformance
272ad3a4041Sjmcneill 	};
273ad3a4041Sjmcneill 	ACPI_INTEGER perf[4], last;
274ad3a4041Sjmcneill 	const struct sysctlnode *node, *cpunode;
275ad3a4041Sjmcneill 	struct sysctllog *log = NULL;
276ad3a4041Sjmcneill 	struct cpu_info *ci = sc->sc_cpuinfo;
277ad3a4041Sjmcneill 	ACPI_STATUS rv;
278ad3a4041Sjmcneill 	int error, i, n;
279ad3a4041Sjmcneill 
280ad3a4041Sjmcneill 	/*
281ad3a4041Sjmcneill 	 * Read highest, nominal, lowest nonlinear, and lowest performance
282ad3a4041Sjmcneill 	 * levels and advertise this list of performance levels in the
283ad3a4041Sjmcneill 	 * machdep.cpufreq.cpuN.available sysctl.
284ad3a4041Sjmcneill 	 */
285ad3a4041Sjmcneill 	sc->sc_available = kmem_zalloc(
286ad3a4041Sjmcneill 	    strlen("########## ") * __arraycount(perf_regs), KM_SLEEP);
287ad3a4041Sjmcneill 	last = 0;
288ad3a4041Sjmcneill 	for (i = 0, n = 0; i < __arraycount(perf_regs); i++) {
289ad3a4041Sjmcneill 		rv = cppc_read(sc, perf_regs[i], &perf[i]);
290ad3a4041Sjmcneill 		if (ACPI_FAILURE(rv)) {
291ad3a4041Sjmcneill 			return rv;
292ad3a4041Sjmcneill 		}
293ad3a4041Sjmcneill 		if (perf[i] != last) {
294ad3a4041Sjmcneill 			char buf[12];
295ad3a4041Sjmcneill 			snprintf(buf, sizeof(buf), n ? " %u" : "%u",
296ad3a4041Sjmcneill 			    (u_int)perf[i]);
297ad3a4041Sjmcneill 			strcat(sc->sc_available, buf);
298ad3a4041Sjmcneill 			last = perf[i];
299ad3a4041Sjmcneill 			n++;
300ad3a4041Sjmcneill 		}
301ad3a4041Sjmcneill 	}
302ad3a4041Sjmcneill 	sc->sc_max_target = perf[0];
303ad3a4041Sjmcneill 	sc->sc_min_target = perf[3];
304ad3a4041Sjmcneill 
305ad3a4041Sjmcneill 	error = sysctl_createv(&log, 0, NULL, &node,
306ad3a4041Sjmcneill 	    CTLFLAG_PERMANENT, CTLTYPE_NODE, "machdep", NULL,
307ad3a4041Sjmcneill 	    NULL, 0, NULL, 0, CTL_MACHDEP, CTL_EOL);
308ad3a4041Sjmcneill 	if (error != 0) {
309ad3a4041Sjmcneill 		goto sysctl_failed;
310ad3a4041Sjmcneill 	}
311ad3a4041Sjmcneill 
312ad3a4041Sjmcneill 	error = sysctl_createv(&log, 0, &node, &node,
313ad3a4041Sjmcneill 	    0, CTLTYPE_NODE, "cpufreq", NULL,
314ad3a4041Sjmcneill 	    NULL, 0, NULL, 0, CTL_CREATE, CTL_EOL);
315ad3a4041Sjmcneill 	if (error != 0) {
316ad3a4041Sjmcneill 		goto sysctl_failed;
317ad3a4041Sjmcneill 	}
318ad3a4041Sjmcneill 
319ad3a4041Sjmcneill 	error = sysctl_createv(&log, 0, &node, &cpunode,
320ad3a4041Sjmcneill 	    0, CTLTYPE_NODE, cpu_name(ci), NULL,
321ad3a4041Sjmcneill 	    NULL, 0, NULL, 0, CTL_CREATE, CTL_EOL);
322ad3a4041Sjmcneill 	if (error != 0) {
323ad3a4041Sjmcneill 		goto sysctl_failed;
324ad3a4041Sjmcneill 	}
325ad3a4041Sjmcneill 
326ad3a4041Sjmcneill 	error = sysctl_createv(&log, 0, &cpunode, &node,
327ad3a4041Sjmcneill 	    CTLFLAG_READWRITE, CTLTYPE_INT, "target", NULL,
328ad3a4041Sjmcneill 	    cppc_cpufreq_sysctl, 0, (void *)sc, 0,
329ad3a4041Sjmcneill 	    CTL_CREATE, CTL_EOL);
330ad3a4041Sjmcneill 	if (error != 0) {
331ad3a4041Sjmcneill 		goto sysctl_failed;
332ad3a4041Sjmcneill 	}
333ad3a4041Sjmcneill 	sc->sc_node_target = node->sysctl_num;
334ad3a4041Sjmcneill 
335ad3a4041Sjmcneill 	error = sysctl_createv(&log, 0, &cpunode, &node,
336ad3a4041Sjmcneill 	    CTLFLAG_READONLY, CTLTYPE_INT, "current", NULL,
337ad3a4041Sjmcneill 	    cppc_cpufreq_sysctl, 0, (void *)sc, 0,
338ad3a4041Sjmcneill 	    CTL_CREATE, CTL_EOL);
339ad3a4041Sjmcneill 	if (error != 0) {
340ad3a4041Sjmcneill 		goto sysctl_failed;
341ad3a4041Sjmcneill 	}
342ad3a4041Sjmcneill 	sc->sc_node_current = node->sysctl_num;
343ad3a4041Sjmcneill 
344ad3a4041Sjmcneill 	error = sysctl_createv(&log, 0, &cpunode, &node,
345ad3a4041Sjmcneill 	    CTLFLAG_READONLY, CTLTYPE_STRING, "available", NULL,
346ad3a4041Sjmcneill 	    NULL, 0, sc->sc_available, 0,
347ad3a4041Sjmcneill 	    CTL_CREATE, CTL_EOL);
348ad3a4041Sjmcneill 	if (error != 0) {
349ad3a4041Sjmcneill 		goto sysctl_failed;
350ad3a4041Sjmcneill 	}
351ad3a4041Sjmcneill 
352ad3a4041Sjmcneill 	return AE_OK;
353ad3a4041Sjmcneill 
354ad3a4041Sjmcneill sysctl_failed:
355ad3a4041Sjmcneill 	aprint_error_dev(sc->sc_dev, "couldn't create sysctl nodes: %d\n",
356ad3a4041Sjmcneill 	    error);
357ad3a4041Sjmcneill 	sysctl_teardown(&log);
358ad3a4041Sjmcneill 
359ad3a4041Sjmcneill 	return AE_ERROR;
360ad3a4041Sjmcneill }
361ad3a4041Sjmcneill 
362ad3a4041Sjmcneill /*
363ad3a4041Sjmcneill  * cppc_read --
364ad3a4041Sjmcneill  *
365ad3a4041Sjmcneill  *	Read a value from the CPC package that contains either an integer
366ad3a4041Sjmcneill  *	or indirect register reference.
367ad3a4041Sjmcneill  */
368ad3a4041Sjmcneill static ACPI_STATUS
cppc_read(struct cppc_softc * sc,CPCPackageElement index,ACPI_INTEGER * val)369ad3a4041Sjmcneill cppc_read(struct cppc_softc *sc, CPCPackageElement index, ACPI_INTEGER *val)
370ad3a4041Sjmcneill {
371ad3a4041Sjmcneill 	ACPI_OBJECT *obj;
372ad3a4041Sjmcneill 	ACPI_GENERIC_ADDRESS addr;
373ad3a4041Sjmcneill 	ACPI_STATUS rv;
374ad3a4041Sjmcneill 
375ad3a4041Sjmcneill 	if (index >= sc->sc_ncpc) {
376ad3a4041Sjmcneill 		return AE_NOT_EXIST;
377ad3a4041Sjmcneill 	}
378ad3a4041Sjmcneill 
379ad3a4041Sjmcneill 	obj = &sc->sc_cpc->Package.Elements[index];
380ad3a4041Sjmcneill 	switch (obj->Type) {
381ad3a4041Sjmcneill 	case ACPI_TYPE_INTEGER:
382ad3a4041Sjmcneill 		*val = obj->Integer.Value;
383ad3a4041Sjmcneill 		return AE_OK;
384ad3a4041Sjmcneill 
385ad3a4041Sjmcneill 	case ACPI_TYPE_BUFFER:
386ad3a4041Sjmcneill 		if (obj->Buffer.Length <
387ad3a4041Sjmcneill 		    sizeof(AML_RESOURCE_GENERIC_REGISTER)) {
388ad3a4041Sjmcneill 			return AE_TYPE;
389ad3a4041Sjmcneill 		}
390ad3a4041Sjmcneill 		memcpy(&addr, obj->Buffer.Pointer +
391ad3a4041Sjmcneill 		    sizeof(AML_RESOURCE_LARGE_HEADER), sizeof(addr));
392ad3a4041Sjmcneill 		if (addr.SpaceId == ACPI_ADR_SPACE_PLATFORM_COMM) {
393ad3a4041Sjmcneill 			rv = pcc_message(&addr, CPPC_PCC_READ, PCC_READ, val);
394ad3a4041Sjmcneill 		} else {
395ad3a4041Sjmcneill 			rv = AcpiRead(val, &addr);
396ad3a4041Sjmcneill 		}
397ad3a4041Sjmcneill 		return rv;
398ad3a4041Sjmcneill 
399ad3a4041Sjmcneill 	default:
400ad3a4041Sjmcneill 		return AE_SUPPORT;
401ad3a4041Sjmcneill 	}
402ad3a4041Sjmcneill }
403ad3a4041Sjmcneill 
404ad3a4041Sjmcneill /*
405ad3a4041Sjmcneill  * cppc_write --
406ad3a4041Sjmcneill  *
407ad3a4041Sjmcneill  *	Write a value based on the CPC package to the specified register.
408ad3a4041Sjmcneill  */
409ad3a4041Sjmcneill static ACPI_STATUS
cppc_write(struct cppc_softc * sc,CPCPackageElement index,ACPI_INTEGER val)410ad3a4041Sjmcneill cppc_write(struct cppc_softc *sc, CPCPackageElement index, ACPI_INTEGER val)
411ad3a4041Sjmcneill {
412ad3a4041Sjmcneill 	ACPI_OBJECT *obj;
413ad3a4041Sjmcneill 	ACPI_GENERIC_ADDRESS addr;
414ad3a4041Sjmcneill 	ACPI_STATUS rv;
415ad3a4041Sjmcneill 
416ad3a4041Sjmcneill 	if (index >= sc->sc_ncpc) {
417ad3a4041Sjmcneill 		return AE_NOT_EXIST;
418ad3a4041Sjmcneill 	}
419ad3a4041Sjmcneill 
420ad3a4041Sjmcneill 	obj = &sc->sc_cpc->Package.Elements[index];
421ad3a4041Sjmcneill 	if (obj->Type != ACPI_TYPE_BUFFER ||
422ad3a4041Sjmcneill 	    obj->Buffer.Length < sizeof(AML_RESOURCE_GENERIC_REGISTER)) {
423ad3a4041Sjmcneill 		return AE_TYPE;
424ad3a4041Sjmcneill 	}
425ad3a4041Sjmcneill 
426ad3a4041Sjmcneill 	memcpy(&addr, obj->Buffer.Pointer +
427ad3a4041Sjmcneill 	    sizeof(AML_RESOURCE_LARGE_HEADER), sizeof(addr));
428ad3a4041Sjmcneill 	if (addr.SpaceId == ACPI_ADR_SPACE_PLATFORM_COMM) {
429ad3a4041Sjmcneill 		rv = pcc_message(&addr, CPPC_PCC_WRITE, PCC_WRITE, &val);
430ad3a4041Sjmcneill 	} else {
431ad3a4041Sjmcneill 		rv = AcpiWrite(val, &addr);
432ad3a4041Sjmcneill 	}
433ad3a4041Sjmcneill 
434ad3a4041Sjmcneill 	return rv;
435ad3a4041Sjmcneill }
436