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