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