acpi/pstate: Print coordination
[dragonfly.git] / sys / dev / acpica5 / acpi_cpu_pstate.c
CommitLineData
c3a2c0df
SZ
1/*
2 * Copyright (c) 2009 The DragonFly Project. All rights reserved.
3 *
4 * This code is derived from software contributed to The DragonFly Project
5 * by Sepherosa Ziehau <sepherosa@gmail.com>
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 *
11 * 1. Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in
15 * the documentation and/or other materials provided with the
16 * distribution.
17 * 3. Neither the name of The DragonFly Project nor the names of its
18 * contributors may be used to endorse or promote products derived
19 * from this software without specific, prior written permission.
20 *
21 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
24 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
25 * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
26 * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
27 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
28 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
29 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
30 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
31 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32 * SUCH DAMAGE.
33 */
34
35#include "opt_acpi.h"
36
37#include <sys/param.h>
38#include <sys/bus.h>
39#include <sys/kernel.h>
40#include <sys/malloc.h>
41#include <sys/queue.h>
0e276230 42#include <sys/rman.h>
c3a2c0df 43#include <sys/sysctl.h>
7b46b972
SZ
44#include <sys/msgport2.h>
45
46#include <net/netisr.h>
47#include <net/netmsg2.h>
48#include <net/if_var.h>
c3a2c0df
SZ
49
50#include "acpi.h"
51#include "acpivar.h"
52#include "acpi_cpu.h"
7b46b972 53#include "acpi_cpu_pstate.h"
c3a2c0df
SZ
54
55#define ACPI_NPSTATE_MAX 16
56
57#define ACPI_PSS_PX_NENTRY 6
58
59#define ACPI_PSD_COORD_SWALL 0xfc
60#define ACPI_PSD_COORD_SWANY 0xfd
61#define ACPI_PSD_COORD_HWALL 0xfe
62#define ACPI_PSD_COORD_VALID(coord) \
63 ((coord) == ACPI_PSD_COORD_SWALL || \
64 (coord) == ACPI_PSD_COORD_SWANY || \
65 (coord) == ACPI_PSD_COORD_HWALL)
66
67struct acpi_pst_softc;
68LIST_HEAD(acpi_pst_list, acpi_pst_softc);
69
7b46b972 70struct netmsg_acpi_pst {
002c1265 71 struct netmsg_base base;
76418417
SZ
72 const struct acpi_pst_res *ctrl;
73 const struct acpi_pst_res *status;
7b46b972
SZ
74};
75
c3a2c0df
SZ
76struct acpi_pst_domain {
77 uint32_t pd_dom;
78 uint32_t pd_coord;
79 uint32_t pd_nproc;
80 LIST_ENTRY(acpi_pst_domain) pd_link;
81
82 uint32_t pd_flags;
83
84 int pd_state;
85 int pd_sstart;
86 struct acpi_pst_list pd_pstlist;
87
88 struct sysctl_ctx_list pd_sysctl_ctx;
89 struct sysctl_oid *pd_sysctl_tree;
90};
91LIST_HEAD(acpi_pst_domlist, acpi_pst_domain);
92
93#define ACPI_PSTDOM_FLAG_STUB 0x1 /* stub domain, no _PSD */
7b46b972 94#define ACPI_PSTDOM_FLAG_DEAD 0x2 /* domain can't be started */
0cb292d1 95#define ACPI_PSTDOM_FLAG_INT 0x4 /* domain created from Integer _PSD */
c3a2c0df
SZ
96
97struct acpi_pst_softc {
7b46b972 98 device_t pst_dev;
c3a2c0df
SZ
99 struct acpi_cpux_softc *pst_parent;
100 struct acpi_pst_domain *pst_domain;
76418417
SZ
101 struct acpi_pst_res pst_creg;
102 struct acpi_pst_res pst_sreg;
c3a2c0df
SZ
103
104 int pst_state;
105 int pst_sstart;
106 int pst_cpuid;
107
108 ACPI_HANDLE pst_handle;
109
110 LIST_ENTRY(acpi_pst_softc) pst_link;
111};
112
c3a2c0df
SZ
113static int acpi_pst_probe(device_t dev);
114static int acpi_pst_attach(device_t dev);
115
116static void acpi_pst_postattach(void *);
117static struct acpi_pst_domain *
0cb292d1
SZ
118 acpi_pst_domain_create_int(device_t, uint32_t);
119static struct acpi_pst_domain *
120 acpi_pst_domain_create_pkg(device_t, ACPI_OBJECT *);
c3a2c0df
SZ
121static struct acpi_pst_domain *
122 acpi_pst_domain_find(uint32_t);
123static struct acpi_pst_domain *
124 acpi_pst_domain_alloc(uint32_t, uint32_t, uint32_t);
7b46b972 125static int acpi_pst_domain_set_pstate(struct acpi_pst_domain *, int);
446b8384 126static int acpi_pst_global_set_pstate(int);
7b46b972
SZ
127
128static int acpi_pst_check_csr(struct acpi_pst_softc *);
129static int acpi_pst_check_pstates(struct acpi_pst_softc *);
c5479e60 130static int acpi_pst_init(struct acpi_pst_softc *);
7b46b972
SZ
131static int acpi_pst_set_pstate(struct acpi_pst_softc *,
132 const struct acpi_pstate *);
133static const struct acpi_pstate *
134 acpi_pst_get_pstate(struct acpi_pst_softc *);
0e276230
SZ
135static int acpi_pst_alloc_resource(device_t, ACPI_OBJECT *, int,
136 struct acpi_pst_res *);
7b46b972 137
002c1265
MD
138static void acpi_pst_check_csr_handler(netmsg_t);
139static void acpi_pst_check_pstates_handler(netmsg_t);
140static void acpi_pst_init_handler(netmsg_t);
141static void acpi_pst_set_pstate_handler(netmsg_t);
142static void acpi_pst_get_pstate_handler(netmsg_t);
c3a2c0df
SZ
143
144static int acpi_pst_sysctl_freqs(SYSCTL_HANDLER_ARGS);
145static int acpi_pst_sysctl_members(SYSCTL_HANDLER_ARGS);
7b46b972
SZ
146static int acpi_pst_sysctl_select(SYSCTL_HANDLER_ARGS);
147static int acpi_pst_sysctl_global(SYSCTL_HANDLER_ARGS);
c3a2c0df
SZ
148
149static struct acpi_pst_domlist acpi_pst_domains =
150 LIST_HEAD_INITIALIZER(acpi_pst_domains);
381b8691 151static int acpi_pst_domain_id;
c3a2c0df 152
7b46b972
SZ
153static int acpi_pst_global_state;
154
c3a2c0df
SZ
155static int acpi_npstates;
156static struct acpi_pstate *acpi_pstates;
157
7b46b972
SZ
158static const struct acpi_pst_md *acpi_pst_md;
159
c3a2c0df
SZ
160static device_method_t acpi_pst_methods[] = {
161 /* Device interface */
162 DEVMETHOD(device_probe, acpi_pst_probe),
163 DEVMETHOD(device_attach, acpi_pst_attach),
164 DEVMETHOD(device_detach, bus_generic_detach),
165 DEVMETHOD(device_shutdown, bus_generic_shutdown),
166 DEVMETHOD(device_suspend, bus_generic_suspend),
167 DEVMETHOD(device_resume, bus_generic_resume),
168
169 /* Bus interface */
170 DEVMETHOD(bus_add_child, bus_generic_add_child),
171 DEVMETHOD(bus_print_child, bus_generic_print_child),
172 DEVMETHOD(bus_read_ivar, bus_generic_read_ivar),
173 DEVMETHOD(bus_write_ivar, bus_generic_write_ivar),
174 DEVMETHOD(bus_get_resource_list, bus_generic_get_resource_list),
175 DEVMETHOD(bus_set_resource, bus_generic_rl_set_resource),
176 DEVMETHOD(bus_get_resource, bus_generic_rl_get_resource),
177 DEVMETHOD(bus_alloc_resource, bus_generic_alloc_resource),
178 DEVMETHOD(bus_release_resource, bus_generic_release_resource),
179 DEVMETHOD(bus_driver_added, bus_generic_driver_added),
180 DEVMETHOD(bus_activate_resource, bus_generic_activate_resource),
181 DEVMETHOD(bus_deactivate_resource, bus_generic_deactivate_resource),
182 DEVMETHOD(bus_setup_intr, bus_generic_setup_intr),
183 DEVMETHOD(bus_teardown_intr, bus_generic_teardown_intr),
184
185 { 0, 0 }
186};
187
188static driver_t acpi_pst_driver = {
189 "cpu_pst",
190 acpi_pst_methods,
191 sizeof(struct acpi_pst_softc)
192};
193
194static devclass_t acpi_pst_devclass;
aa2b9d05 195DRIVER_MODULE(cpu_pst, cpu, acpi_pst_driver, acpi_pst_devclass, NULL, NULL);
c3a2c0df
SZ
196MODULE_DEPEND(cpu_pst, acpi, 1, 1, 1);
197
7b46b972
SZ
198static __inline int
199acpi_pst_freq2index(int freq)
200{
201 int i;
202
203 for (i = 0; i < acpi_npstates; ++i) {
204 if (acpi_pstates[i].st_freq == freq)
205 return i;
206 }
207 return -1;
208}
209
c3a2c0df
SZ
210static int
211acpi_pst_probe(device_t dev)
212{
213 ACPI_BUFFER buf;
214 ACPI_HANDLE handle;
215 ACPI_STATUS status;
216 ACPI_OBJECT *obj;
217
218 if (acpi_disabled("cpu_pst") ||
219 acpi_get_type(dev) != ACPI_TYPE_PROCESSOR)
220 return ENXIO;
221
7b46b972
SZ
222 if (acpi_pst_md == NULL)
223 acpi_pst_md = acpi_pst_md_probe();
224
c3a2c0df
SZ
225 handle = acpi_get_handle(dev);
226
227 /*
228 * Check _PCT package
229 */
230 buf.Pointer = NULL;
231 buf.Length = ACPI_ALLOCATE_BUFFER;
232 status = AcpiEvaluateObject(handle, "_PCT", NULL, &buf);
233 if (ACPI_FAILURE(status)) {
604bd1e4
SZ
234 if (bootverbose) {
235 device_printf(dev, "Can't get _PCT package - %s\n",
236 AcpiFormatException(status));
237 }
c3a2c0df
SZ
238 return ENXIO;
239 }
240
241 obj = (ACPI_OBJECT *)buf.Pointer;
242 if (!ACPI_PKG_VALID_EQ(obj, 2)) {
243 device_printf(dev, "Invalid _PCT package\n");
244 AcpiOsFree(obj);
245 return ENXIO;
246 }
247 AcpiOsFree(obj);
248
249 /*
250 * Check _PSS package
251 */
252 buf.Pointer = NULL;
253 buf.Length = ACPI_ALLOCATE_BUFFER;
254 status = AcpiEvaluateObject(handle, "_PSS", NULL, &buf);
255 if (ACPI_FAILURE(status)) {
256 device_printf(dev, "Can't get _PSS package - %s\n",
257 AcpiFormatException(status));
258 return ENXIO;
259 }
260
261 obj = (ACPI_OBJECT *)buf.Pointer;
262 if (!ACPI_PKG_VALID(obj, 1)) {
263 device_printf(dev, "Invalid _PSS package\n");
264 AcpiOsFree(obj);
265 return ENXIO;
266 }
267 AcpiOsFree(obj);
268
269 device_set_desc(dev, "ACPI CPU P-State");
270 return 0;
271}
272
273static int
274acpi_pst_attach(device_t dev)
275{
276 struct acpi_pst_softc *sc = device_get_softc(dev), *pst;
277 struct acpi_pst_domain *dom = NULL;
278 ACPI_BUFFER buf;
279 ACPI_STATUS status;
76418417 280 ACPI_OBJECT *obj;
c3a2c0df 281 struct acpi_pstate *pstate, *p;
76418417 282 int i, npstate, error;
c3a2c0df 283
7b46b972 284 sc->pst_dev = dev;
c3a2c0df
SZ
285 sc->pst_parent = device_get_softc(device_get_parent(dev));
286 sc->pst_handle = acpi_get_handle(dev);
287 sc->pst_cpuid = acpi_get_magic(dev);
288
289 /*
290 * If there is a _PSD, then we create procossor domain
291 * accordingly. If there is no _PSD, we just fake a
292 * default processor domain0.
293 */
294 buf.Pointer = NULL;
295 buf.Length = ACPI_ALLOCATE_BUFFER;
296 status = AcpiEvaluateObject(sc->pst_handle, "_PSD", NULL, &buf);
297 if (!ACPI_FAILURE(status)) {
6ec50a1c
SZ
298 obj = (ACPI_OBJECT *)buf.Pointer;
299
6f36cba1
SZ
300 if (acpi_pst_domain_id > 0) {
301 device_printf(dev, "Missing _PSD for certain CPUs\n");
302 AcpiOsFree(obj);
303 return ENXIO;
304 }
305 acpi_pst_domain_id = -1;
306
c3a2c0df 307 if (ACPI_PKG_VALID_EQ(obj, 1)) {
0cb292d1 308 dom = acpi_pst_domain_create_pkg(dev,
c3a2c0df
SZ
309 &obj->Package.Elements[0]);
310 if (dom == NULL) {
311 AcpiOsFree(obj);
312 return ENXIO;
313 }
314 } else {
0cb292d1
SZ
315 if (obj->Type != ACPI_TYPE_INTEGER) {
316 device_printf(dev,
317 "Invalid _PSD package, Type 0x%x\n",
318 obj->Type);
319 AcpiOsFree(obj);
320 return ENXIO;
321 } else {
322 device_printf(dev, "Integer _PSD %ju\n",
323 (uintmax_t)obj->Integer.Value);
324 dom = acpi_pst_domain_create_int(dev,
325 obj->Integer.Value);
326 if (dom == NULL) {
327 AcpiOsFree(obj);
328 return ENXIO;
329 }
330 }
c3a2c0df
SZ
331 }
332
333 /* Free _PSD */
334 AcpiOsFree(buf.Pointer);
335 } else {
6f36cba1
SZ
336 if (acpi_pst_domain_id < 0) {
337 device_printf(dev, "Missing _PSD for cpu%d\n",
338 sc->pst_cpuid);
339 return ENXIO;
340 }
341
381b8691
SZ
342 /*
343 * Create a stub one processor domain for each processor
344 */
345 dom = acpi_pst_domain_alloc(acpi_pst_domain_id,
346 ACPI_PSD_COORD_SWANY, 1);
c3a2c0df 347 dom->pd_flags |= ACPI_PSTDOM_FLAG_STUB;
381b8691
SZ
348
349 ++acpi_pst_domain_id;
c3a2c0df
SZ
350 }
351
352 /* Make sure that adding us will not overflow our domain */
353 i = 0;
354 LIST_FOREACH(pst, &dom->pd_pstlist, pst_link)
355 ++i;
356 if (i == dom->pd_nproc) {
357 device_printf(dev, "Domain%u already contains %d P-States, "
358 "invalid _PSD package\n",
359 dom->pd_dom, dom->pd_nproc);
360 return ENXIO;
361 }
2f336f65 362 KKASSERT(i < dom->pd_nproc);
c3a2c0df
SZ
363
364 /*
365 * Get control/status registers from _PCT
366 */
367 buf.Pointer = NULL;
368 buf.Length = ACPI_ALLOCATE_BUFFER;
369 status = AcpiEvaluateObject(sc->pst_handle, "_PCT", NULL, &buf);
370 if (ACPI_FAILURE(status)) {
371 device_printf(dev, "Can't get _PCT package - %s\n",
372 AcpiFormatException(status));
373 return ENXIO;
374 }
375
376 obj = (ACPI_OBJECT *)buf.Pointer;
377 if (!ACPI_PKG_VALID_EQ(obj, 2)) {
378 device_printf(dev, "Invalid _PCT package\n");
379 AcpiOsFree(obj);
380 return ENXIO;
381 }
382
0e276230
SZ
383 /* Save and try allocating control register */
384 error = acpi_pst_alloc_resource(dev, obj, 0, &sc->pst_creg);
76418417
SZ
385 if (error) {
386 AcpiOsFree(obj);
387 return error;
388 }
c3a2c0df 389 if (bootverbose) {
b6b64e7b 390 device_printf(dev, "control reg %d %jx\n",
10f97674 391 sc->pst_creg.pr_gas.SpaceId,
b6b64e7b 392 (uintmax_t)sc->pst_creg.pr_gas.Address);
c3a2c0df
SZ
393 }
394
0e276230
SZ
395 /* Save and try allocating status register */
396 error = acpi_pst_alloc_resource(dev, obj, 1, &sc->pst_sreg);
76418417
SZ
397 if (error) {
398 AcpiOsFree(obj);
399 return error;
400 }
c3a2c0df 401 if (bootverbose) {
b6b64e7b 402 device_printf(dev, "status reg %d %jx\n",
76418417 403 sc->pst_sreg.pr_gas.SpaceId,
b6b64e7b 404 (uintmax_t)sc->pst_sreg.pr_gas.Address);
c3a2c0df
SZ
405 }
406
407 /* Free _PCT */
408 AcpiOsFree(obj);
409
410 /*
411 * Create P-State table according to _PSS
412 */
413 buf.Pointer = NULL;
414 buf.Length = ACPI_ALLOCATE_BUFFER;
415 status = AcpiEvaluateObject(sc->pst_handle, "_PSS", NULL, &buf);
416 if (ACPI_FAILURE(status)) {
417 device_printf(dev, "Can't get _PSS package - %s\n",
418 AcpiFormatException(status));
419 return ENXIO;
420 }
421
422 obj = (ACPI_OBJECT *)buf.Pointer;
423 if (!ACPI_PKG_VALID(obj, 1)) {
424 device_printf(dev, "Invalid _PSS package\n");
425 AcpiOsFree(obj);
426 return ENXIO;
427 }
428
429 /* Don't create too many P-States */
430 npstate = obj->Package.Count;
431 if (npstate > ACPI_NPSTATE_MAX) {
432 device_printf(dev, "Too many P-States, %d->%d\n",
433 npstate, ACPI_NPSTATE_MAX);
434 npstate = ACPI_NPSTATE_MAX;
435 }
436
437 /*
438 * If we have already created P-State table,
439 * we must make sure that number of entries
440 * is consistent.
441 */
442 if (acpi_pstates != NULL && acpi_npstates != npstate) {
443 device_printf(dev, "Inconsistent # of P-States "
444 "cross Processor objects\n");
445 AcpiOsFree(obj);
446 return ENXIO;
447 }
448
449 /*
450 * Create a temporary P-State table
451 */
452 pstate = kmalloc(sizeof(*pstate) * npstate, M_TEMP, M_WAITOK);
453 for (i = 0, p = pstate; i < npstate; ++i, ++p) {
454 ACPI_OBJECT *pkg;
455 uint32_t *ptr[ACPI_PSS_PX_NENTRY] = {
456 &p->st_freq, &p->st_power, &p->st_xsit_lat,
457 &p->st_bm_lat, &p->st_cval, &p->st_sval
458 };
459 int j;
460
461 pkg = &obj->Package.Elements[i];
462 if (!ACPI_PKG_VALID(pkg, ACPI_PSS_PX_NENTRY)) {
463 device_printf(dev, "Invalud _PSS P%d\n", i);
464 AcpiOsFree(obj);
465 kfree(pstate, M_TEMP);
466 return ENXIO;
467 }
468 for (j = 0; j < ACPI_PSS_PX_NENTRY; ++j) {
469 if (acpi_PkgInt32(pkg, j, ptr[j]) != 0) {
470 device_printf(dev, "Can't extract "
471 "_PSS P%d %dth entry\n", i, j);
472 AcpiOsFree(obj);
473 kfree(pstate, M_TEMP);
474 return ENXIO;
475 }
476 }
477 }
478
479 /* Free _PSS */
480 AcpiOsFree(obj);
481
482 if (acpi_pstates == NULL) {
483 /*
484 * If no P-State table is created yet,
485 * save the temporary one we just created.
486 */
487 acpi_pstates = pstate;
488 acpi_npstates = npstate;
489 pstate = NULL;
490
491 if (bootverbose) {
492 for (i = 0; i < acpi_npstates; ++i) {
493 device_printf(dev,
494 "freq %u, pwr %u, xlat %u, blat %u, "
495 "cv %08x, sv %08x\n",
496 acpi_pstates[i].st_freq,
497 acpi_pstates[i].st_power,
498 acpi_pstates[i].st_xsit_lat,
499 acpi_pstates[i].st_bm_lat,
500 acpi_pstates[i].st_cval,
501 acpi_pstates[i].st_sval);
502 }
503 }
504 } else {
505 /*
506 * Make sure that P-State tables are same
507 * for all processors.
508 */
509 if (memcmp(pstate, acpi_pstates,
510 sizeof(*pstate) * npstate) != 0) {
511 device_printf(dev, "Inconsistent _PSS "
512 "cross Processor objects\n");
513 kfree(pstate, M_TEMP);
514 return ENXIO;
515 }
516 kfree(pstate, M_TEMP);
517 }
518
519 /* By default, we start from P-State table's first entry */
520 sc->pst_sstart = 0;
521
522 /*
523 * Adjust the usable first entry of P-State table,
524 * if there is _PPC object.
525 */
526 buf.Pointer = NULL;
527 buf.Length = ACPI_ALLOCATE_BUFFER;
528 status = AcpiEvaluateObject(sc->pst_handle, "_PPC", NULL, &buf);
529 if (!ACPI_FAILURE(status)) {
530 obj = (ACPI_OBJECT *)buf.Pointer;
531 if (obj->Type == ACPI_TYPE_INTEGER) {
532 if (obj->Integer.Value >= acpi_npstates) {
533 device_printf(dev, "Invalid _PPC value\n");
534 AcpiOsFree(obj);
535 return ENXIO;
536 }
537 sc->pst_sstart = obj->Integer.Value;
538 if (bootverbose)
539 device_printf(dev, "_PPC %d\n", sc->pst_sstart);
540
541 /* TODO: Install notifiy handler */
542 } else {
543 device_printf(dev, "Invalid _PPC object\n");
544 AcpiOsFree(obj);
545 return ENXIO;
546 }
547
548 /* Free _PPC */
549 AcpiOsFree(obj);
550 }
551
552 sc->pst_state = sc->pst_sstart;
553
554 /* Link us with the domain */
555 sc->pst_domain = dom;
556 LIST_INSERT_HEAD(&dom->pd_pstlist, sc, pst_link);
557
558 if (device_get_unit(dev) == 0)
559 AcpiOsExecute(OSL_NOTIFY_HANDLER, acpi_pst_postattach, NULL);
560
561 return 0;
562}
563
564static struct acpi_pst_domain *
0cb292d1 565acpi_pst_domain_create_pkg(device_t dev, ACPI_OBJECT *obj)
c3a2c0df
SZ
566{
567 struct acpi_pst_domain *dom;
568 uint32_t val, domain, coord, nproc;
569
570 if (!ACPI_PKG_VALID_EQ(obj, 5)) {
571 device_printf(dev, "Invalid _PSD package\n");
572 return NULL;
573 }
574
575 /* NumberOfEntries */
576 if (acpi_PkgInt32(obj, 0, &val) != 0 || val != 5) {
577 device_printf(dev, "Invalid _PSD NumberOfEntries\n");
578 return NULL;
579 }
580
581 /* Revision */
582 if (acpi_PkgInt32(obj, 1, &val) != 0 || val != 0) {
583 device_printf(dev, "Invalid _PSD Revision\n");
584 return NULL;
585 }
586
587 if (acpi_PkgInt32(obj, 2, &domain) != 0 ||
588 acpi_PkgInt32(obj, 3, &coord) != 0 ||
589 acpi_PkgInt32(obj, 4, &nproc) != 0) {
590 device_printf(dev, "Can't extract _PSD package\n");
591 return NULL;
592 }
593
2f336f65
SZ
594 if (!ACPI_PSD_COORD_VALID(coord)) {
595 device_printf(dev, "Invalid _PSD CoordType (%#x)\n", coord);
c3a2c0df
SZ
596 return NULL;
597 }
598
2f336f65
SZ
599 if (nproc > MAXCPU) {
600 /*
601 * If NumProcessors is greater than MAXCPU
602 * and domain's coordination is SWALL, then
603 * we will never be able to start all CPUs
604 * within this domain, and power state
605 * transition will never be completed, so we
606 * just bail out here.
607 */
608 if (coord == ACPI_PSD_COORD_SWALL) {
609 device_printf(dev, "Unsupported _PSD NumProcessors "
610 "(%d)\n", nproc);
611 return NULL;
612 }
613 } else if (nproc == 0) {
614 device_printf(dev, "_PSD NumProcessors are zero\n");
c3a2c0df
SZ
615 return NULL;
616 }
617
618 dom = acpi_pst_domain_find(domain);
619 if (dom != NULL) {
0cb292d1
SZ
620 if (dom->pd_flags & ACPI_PSTDOM_FLAG_INT) {
621 device_printf(dev, "Mixed Integer _PSD and "
622 "Package _PSD\n");
623 return NULL;
624 }
c3a2c0df
SZ
625 if (dom->pd_coord != coord || dom->pd_nproc != nproc) {
626 device_printf(dev, "Inconsistent _PSD information "
627 "cross Processor objects\n");
628 return NULL;
629 }
630 return dom;
631 }
632
633 dom = acpi_pst_domain_alloc(domain, coord, nproc);
95b6142f
SZ
634 if (bootverbose) {
635 device_printf(dev, "create pkg domain%u, coord %#x\n",
636 dom->pd_dom, dom->pd_coord);
637 }
0cb292d1
SZ
638
639 return dom;
640}
641
642static struct acpi_pst_domain *
643acpi_pst_domain_create_int(device_t dev, uint32_t domain)
644{
645 struct acpi_pst_domain *dom;
646
647 dom = acpi_pst_domain_find(domain);
648 if (dom != NULL) {
649 if ((dom->pd_flags & ACPI_PSTDOM_FLAG_INT) == 0) {
650 device_printf(dev, "Mixed Package _PSD and "
651 "Integer _PSD\n");
652 return NULL;
653 }
654 KKASSERT(dom->pd_coord == ACPI_PSD_COORD_SWALL);
655
656 dom->pd_nproc++;
657 return dom;
658 }
659
660 dom = acpi_pst_domain_alloc(domain, ACPI_PSD_COORD_SWALL, 1);
661 dom->pd_flags |= ACPI_PSTDOM_FLAG_INT;
662
663 if (bootverbose)
664 device_printf(dev, "create int domain%u\n", dom->pd_dom);
c3a2c0df
SZ
665
666 return dom;
667}
668
669static struct acpi_pst_domain *
670acpi_pst_domain_find(uint32_t domain)
671{
672 struct acpi_pst_domain *dom;
673
674 LIST_FOREACH(dom, &acpi_pst_domains, pd_link) {
675 if (dom->pd_flags & ACPI_PSTDOM_FLAG_STUB)
676 continue;
677 if (dom->pd_dom == domain)
678 return dom;
679 }
680 return NULL;
681}
682
683static struct acpi_pst_domain *
684acpi_pst_domain_alloc(uint32_t domain, uint32_t coord, uint32_t nproc)
685{
686 struct acpi_pst_domain *dom;
687
688 dom = kmalloc(sizeof(*dom), M_DEVBUF, M_WAITOK | M_ZERO);
689 dom->pd_dom = domain;
690 dom->pd_coord = coord;
691 dom->pd_nproc = nproc;
692 dom->pd_state = 0; /* XXX */
693 dom->pd_sstart = 0; /* XXX */
694 LIST_INIT(&dom->pd_pstlist);
695
696 LIST_INSERT_HEAD(&acpi_pst_domains, dom, pd_link);
697
698 return dom;
699}
700
7b46b972
SZ
701static int
702acpi_pst_domain_set_pstate(struct acpi_pst_domain *dom, int i)
703{
704 const struct acpi_pstate *pstate;
705 struct acpi_pst_softc *sc;
706 int done, error;
707
708 KKASSERT(i >= 0 && i < acpi_npstates);
709 pstate = &acpi_pstates[i];
710
711 done = 0;
712 LIST_FOREACH(sc, &dom->pd_pstlist, pst_link) {
713 if (!done) {
714 error = acpi_pst_set_pstate(sc, pstate);
715 if (error) {
716 device_printf(sc->pst_dev, "can't set "
717 "freq %d\n", pstate->st_freq);
718 /* XXX error cleanup? */
719 }
720 if (dom->pd_coord == ACPI_PSD_COORD_SWANY)
721 done = 1;
722 }
723 sc->pst_state = i;
724 }
725 dom->pd_state = i;
726
727 return 0;
728}
729
446b8384
SZ
730static int
731acpi_pst_global_set_pstate(int i)
732{
733 struct acpi_pst_domain *dom;
734
735 LIST_FOREACH(dom, &acpi_pst_domains, pd_link) {
736 /* Skip dead domain */
737 if (dom->pd_flags & ACPI_PSTDOM_FLAG_DEAD)
738 continue;
739 acpi_pst_domain_set_pstate(dom, i);
740 }
741 acpi_pst_global_state = i;
742
743 return 0;
744}
745
c3a2c0df
SZ
746static void
747acpi_pst_postattach(void *arg __unused)
748{
749 struct acpi_pst_domain *dom;
750 struct acpi_cpux_softc *cpux;
751 device_t *devices;
446b8384 752 int i, ndevices, error, has_domain, sstate;
c3a2c0df
SZ
753
754 devices = NULL;
755 ndevices = 0;
7b46b972
SZ
756 error = devclass_get_devices(acpi_pst_devclass, &devices, &ndevices);
757 if (error)
758 return;
759
c3a2c0df
SZ
760 if (ndevices == 0)
761 return;
762
763 cpux = NULL;
764 for (i = 0; i < ndevices; ++i) {
765 cpux = device_get_softc(device_get_parent(devices[i]));
766 if (cpux->glob_sysctl_tree != NULL)
767 break;
768 }
769 kfree(devices, M_TEMP);
770 KKASSERT(cpux != NULL);
771
7b46b972
SZ
772 if (acpi_pst_md == NULL)
773 kprintf("ACPI: no P-State CPU driver\n");
774
446b8384 775 sstate = 0x7fffffff;
7b46b972 776 has_domain = 0;
c3a2c0df
SZ
777 LIST_FOREACH(dom, &acpi_pst_domains, pd_link) {
778 struct acpi_pst_softc *sc;
779 char buf[32];
780
7b46b972
SZ
781 /*
782 * Make sure that all processors belonging to this
783 * domain are located.
784 */
c3a2c0df
SZ
785 i = 0;
786 LIST_FOREACH(sc, &dom->pd_pstlist, pst_link)
787 ++i;
788 if (i != dom->pd_nproc) {
2f336f65
SZ
789 KKASSERT(i < dom->pd_nproc);
790
7b46b972
SZ
791 kprintf("ACPI: domain%u misses processors, "
792 "should be %d, got %d\n", dom->pd_dom,
793 dom->pd_nproc, i);
2f336f65
SZ
794 if (dom->pd_coord == ACPI_PSD_COORD_SWALL) {
795 /*
796 * If this domain's coordination is
797 * SWALL and we don't see all of the
798 * member CPUs of this domain, then
799 * the P-State transition will never
800 * be completed, so just leave this
801 * domain out.
802 */
803 dom->pd_flags |= ACPI_PSTDOM_FLAG_DEAD;
804 continue;
805 }
806 dom->pd_nproc = i;
7b46b972
SZ
807 }
808
809 /*
810 * Validate P-State configurations for this domain
811 */
812 LIST_FOREACH(sc, &dom->pd_pstlist, pst_link) {
813 error = acpi_pst_check_csr(sc);
814 if (error)
815 break;
816
817 error = acpi_pst_check_pstates(sc);
818 if (error)
819 break;
820 }
821 if (sc != NULL) {
822 kprintf("ACPI: domain%u P-State configuration "
823 "check failed\n", dom->pd_dom);
824 dom->pd_flags |= ACPI_PSTDOM_FLAG_DEAD;
c3a2c0df
SZ
825 continue;
826 }
7b46b972 827
c5479e60
SZ
828 /*
829 * Do necssary P-State initialization
830 */
831 LIST_FOREACH(sc, &dom->pd_pstlist, pst_link) {
832 error = acpi_pst_init(sc);
833 if (error)
834 break;
835 }
836 if (sc != NULL) {
837 kprintf("ACPI: domain%u P-State initialization "
838 "check failed\n", dom->pd_dom);
839 dom->pd_flags |= ACPI_PSTDOM_FLAG_DEAD;
840 continue;
841 }
842
7b46b972
SZ
843 has_domain = 1;
844
c3a2c0df
SZ
845 ksnprintf(buf, sizeof(buf), "px_dom%u", dom->pd_dom);
846
847 sysctl_ctx_init(&dom->pd_sysctl_ctx);
848 dom->pd_sysctl_tree =
849 SYSCTL_ADD_NODE(&dom->pd_sysctl_ctx,
850 SYSCTL_CHILDREN(cpux->glob_sysctl_tree),
851 OID_AUTO, buf, CTLFLAG_RD, 0,
852 "P-State domain");
7b46b972
SZ
853 if (dom->pd_sysctl_tree == NULL) {
854 kprintf("ACPI: Can't create sysctl tree for domain%u",
855 dom->pd_dom);
c3a2c0df 856 continue;
7b46b972 857 }
c3a2c0df
SZ
858
859 SYSCTL_ADD_PROC(&dom->pd_sysctl_ctx,
860 SYSCTL_CHILDREN(dom->pd_sysctl_tree),
861 OID_AUTO, "available",
862 CTLTYPE_STRING | CTLFLAG_RD,
863 dom, 0, acpi_pst_sysctl_freqs, "A",
864 "available frequencies");
865
866 SYSCTL_ADD_PROC(&dom->pd_sysctl_ctx,
867 SYSCTL_CHILDREN(dom->pd_sysctl_tree),
868 OID_AUTO, "members",
869 CTLTYPE_STRING | CTLFLAG_RD,
870 dom, 0, acpi_pst_sysctl_members, "A",
871 "member cpus");
7b46b972
SZ
872
873 if (acpi_pst_md != NULL &&
874 acpi_pst_md->pmd_set_pstate != NULL) {
875 SYSCTL_ADD_PROC(&dom->pd_sysctl_ctx,
876 SYSCTL_CHILDREN(dom->pd_sysctl_tree),
877 OID_AUTO, "select",
878 CTLTYPE_UINT | CTLFLAG_RW,
879 dom, 0, acpi_pst_sysctl_select,
880 "IU", "select freq");
881 }
446b8384
SZ
882
883 if (dom->pd_state < sstate)
884 sstate = dom->pd_state;
7b46b972
SZ
885 }
886
887 if (has_domain && acpi_pst_md != NULL &&
888 acpi_pst_md->pmd_set_pstate != NULL) {
889 SYSCTL_ADD_PROC(&cpux->glob_sysctl_ctx,
890 SYSCTL_CHILDREN(cpux->glob_sysctl_tree),
891 OID_AUTO, "px_global",
892 CTLTYPE_UINT | CTLFLAG_RW,
893 NULL, 0, acpi_pst_sysctl_global,
894 "IU", "select freq for all domains");
446b8384
SZ
895
896 acpi_pst_global_set_pstate(sstate);
c3a2c0df
SZ
897 }
898}
899
900static int
901acpi_pst_sysctl_freqs(SYSCTL_HANDLER_ARGS)
902{
903 struct acpi_pst_domain *dom = arg1;
904 int i, error;
905
906 error = 0;
907 for (i = 0; i < acpi_npstates; ++i) {
908 if (error == 0 && i)
909 error = SYSCTL_OUT(req, " ", 1);
910 if (error == 0) {
911 const char *pat;
912 char buf[32];
913
914 if (i < dom->pd_sstart)
915 pat = "(%u)";
916 else
917 pat = "%u";
918
919 ksnprintf(buf, sizeof(buf), pat,
920 acpi_pstates[i].st_freq);
921 error = SYSCTL_OUT(req, buf, strlen(buf));
922 }
923 }
924 return error;
925}
926
927static int
928acpi_pst_sysctl_members(SYSCTL_HANDLER_ARGS)
929{
930 struct acpi_pst_domain *dom = arg1;
7b46b972 931 struct acpi_pst_softc *sc;
c3a2c0df
SZ
932 int loop, error;
933
934 loop = error = 0;
935 LIST_FOREACH(sc, &dom->pd_pstlist, pst_link) {
7b46b972
SZ
936 char buf[32];
937
c3a2c0df
SZ
938 if (error == 0 && loop)
939 error = SYSCTL_OUT(req, " ", 1);
940 if (error == 0) {
c3a2c0df
SZ
941 ksnprintf(buf, sizeof(buf), "cpu%d", sc->pst_cpuid);
942 error = SYSCTL_OUT(req, buf, strlen(buf));
943 }
7b46b972
SZ
944
945 if (error == 0 && acpi_pst_md && acpi_pst_md->pmd_get_pstate) {
946 const struct acpi_pstate *pstate;
947 const char *str;
948
949 pstate = acpi_pst_get_pstate(sc);
950 if (pstate == NULL) {
951 str = "(*)";
952 } else {
953 ksnprintf(buf, sizeof(buf), "(%d)",
954 pstate->st_freq);
955 str = buf;
956 }
957 error = SYSCTL_OUT(req, str, strlen(str));
958 }
c3a2c0df
SZ
959 ++loop;
960 }
961 return error;
962}
7b46b972
SZ
963
964static int
965acpi_pst_sysctl_select(SYSCTL_HANDLER_ARGS)
966{
967 struct acpi_pst_domain *dom = arg1;
968 int error, i, freq;
969
970 KKASSERT(dom->pd_state >= 0 && dom->pd_state < acpi_npstates);
971
972 freq = acpi_pstates[dom->pd_state].st_freq;
973
974 error = sysctl_handle_int(oidp, &freq, 0, req);
975 if (error || req->newptr == NULL)
976 return error;
977
978 i = acpi_pst_freq2index(freq);
979 if (i < 0)
980 return EINVAL;
981
982 acpi_pst_domain_set_pstate(dom, i);
983 return 0;
984}
985
986static int
987acpi_pst_sysctl_global(SYSCTL_HANDLER_ARGS)
988{
7b46b972
SZ
989 int error, i, freq;
990
991 KKASSERT(acpi_pst_global_state >= 0 &&
992 acpi_pst_global_state < acpi_npstates);
993
994 freq = acpi_pstates[acpi_pst_global_state].st_freq;
995
996 error = sysctl_handle_int(oidp, &freq, 0, req);
997 if (error || req->newptr == NULL)
998 return error;
999
1000 i = acpi_pst_freq2index(freq);
1001 if (i < 0)
1002 return EINVAL;
1003
446b8384 1004 acpi_pst_global_set_pstate(i);
7b46b972
SZ
1005
1006 return 0;
1007}
1008
1009static void
002c1265 1010acpi_pst_check_csr_handler(netmsg_t msg)
7b46b972 1011{
002c1265 1012 struct netmsg_acpi_pst *rmsg = (struct netmsg_acpi_pst *)msg;
7b46b972
SZ
1013 int error;
1014
002c1265
MD
1015 error = acpi_pst_md->pmd_check_csr(rmsg->ctrl, rmsg->status);
1016 lwkt_replymsg(&rmsg->base.lmsg, error);
7b46b972
SZ
1017}
1018
1019static int
1020acpi_pst_check_csr(struct acpi_pst_softc *sc)
1021{
1022 struct netmsg_acpi_pst msg;
1023
1024 if (acpi_pst_md == NULL)
1025 return 0;
1026
002c1265 1027 netmsg_init(&msg.base, NULL, &curthread->td_msgport,
c3c96e44 1028 MSGF_PRIORITY, acpi_pst_check_csr_handler);
7b46b972
SZ
1029 msg.ctrl = &sc->pst_creg;
1030 msg.status = &sc->pst_sreg;
1031
002c1265 1032 return lwkt_domsg(cpu_portfn(sc->pst_cpuid), &msg.base.lmsg, 0);
7b46b972
SZ
1033}
1034
1035static void
002c1265 1036acpi_pst_check_pstates_handler(netmsg_t msg)
7b46b972
SZ
1037{
1038 int error;
1039
1040 error = acpi_pst_md->pmd_check_pstates(acpi_pstates, acpi_npstates);
002c1265 1041 lwkt_replymsg(&msg->lmsg, error);
7b46b972
SZ
1042}
1043
1044static int
1045acpi_pst_check_pstates(struct acpi_pst_softc *sc)
1046{
002c1265 1047 struct netmsg_base msg;
7b46b972
SZ
1048
1049 if (acpi_pst_md == NULL)
1050 return 0;
1051
002c1265 1052 netmsg_init(&msg, NULL, &curthread->td_msgport,
c3c96e44 1053 MSGF_PRIORITY, acpi_pst_check_pstates_handler);
7b46b972 1054
002c1265 1055 return lwkt_domsg(cpu_portfn(sc->pst_cpuid), &msg.lmsg, 0);
7b46b972
SZ
1056}
1057
1058static void
002c1265 1059acpi_pst_init_handler(netmsg_t msg)
c5479e60 1060{
002c1265 1061 struct netmsg_acpi_pst *rmsg = (struct netmsg_acpi_pst *)msg;
c5479e60
SZ
1062 int error;
1063
002c1265
MD
1064 error = acpi_pst_md->pmd_init(rmsg->ctrl, rmsg->status);
1065 lwkt_replymsg(&rmsg->base.lmsg, error);
c5479e60
SZ
1066}
1067
1068static int
1069acpi_pst_init(struct acpi_pst_softc *sc)
1070{
1071 struct netmsg_acpi_pst msg;
1072
1073 if (acpi_pst_md == NULL)
1074 return 0;
1075
002c1265 1076 netmsg_init(&msg.base, NULL, &curthread->td_msgport,
c3c96e44 1077 MSGF_PRIORITY, acpi_pst_init_handler);
c5479e60
SZ
1078 msg.ctrl = &sc->pst_creg;
1079 msg.status = &sc->pst_sreg;
1080
002c1265 1081 return lwkt_domsg(cpu_portfn(sc->pst_cpuid), &msg.base.lmsg, 0);
c5479e60
SZ
1082}
1083
1084static void
002c1265 1085acpi_pst_set_pstate_handler(netmsg_t msg)
7b46b972 1086{
002c1265 1087 struct netmsg_acpi_pst *rmsg = (struct netmsg_acpi_pst *)msg;
7b46b972
SZ
1088 int error;
1089
002c1265
MD
1090 error = acpi_pst_md->pmd_set_pstate(rmsg->ctrl, rmsg->status,
1091 rmsg->base.lmsg.u.ms_resultp);
1092 lwkt_replymsg(&rmsg->base.lmsg, error);
7b46b972
SZ
1093}
1094
1095static int
1096acpi_pst_set_pstate(struct acpi_pst_softc *sc, const struct acpi_pstate *pstate)
1097{
1098 struct netmsg_acpi_pst msg;
1099
1100 KKASSERT(acpi_pst_md != NULL);
1101
002c1265 1102 netmsg_init(&msg.base, NULL, &curthread->td_msgport,
c3c96e44 1103 MSGF_PRIORITY, acpi_pst_set_pstate_handler);
002c1265 1104 msg.base.lmsg.u.ms_resultp = __DECONST(void *, pstate);
7b46b972
SZ
1105 msg.ctrl = &sc->pst_creg;
1106 msg.status = &sc->pst_sreg;
1107
002c1265 1108 return lwkt_domsg(cpu_portfn(sc->pst_cpuid), &msg.base.lmsg, 0);
7b46b972
SZ
1109}
1110
1111static void
002c1265 1112acpi_pst_get_pstate_handler(netmsg_t msg)
7b46b972 1113{
002c1265 1114 struct netmsg_acpi_pst *rmsg = (struct netmsg_acpi_pst *)msg;
7b46b972
SZ
1115 const struct acpi_pstate *pstate;
1116
002c1265 1117 pstate = acpi_pst_md->pmd_get_pstate(rmsg->status, acpi_pstates,
7b46b972 1118 acpi_npstates);
002c1265
MD
1119 rmsg->base.lmsg.u.ms_resultp = __DECONST(void *, pstate);
1120 lwkt_replymsg(&rmsg->base.lmsg, 0);
7b46b972
SZ
1121}
1122
1123static const struct acpi_pstate *
1124acpi_pst_get_pstate(struct acpi_pst_softc *sc)
1125{
1126 struct netmsg_acpi_pst msg;
1127
1128 if (acpi_pst_md == NULL)
1129 return 0;
1130
002c1265 1131 netmsg_init(&msg.base, NULL, &curthread->td_msgport,
c3c96e44 1132 MSGF_PRIORITY, acpi_pst_get_pstate_handler);
7b46b972
SZ
1133 msg.status = &sc->pst_sreg;
1134
002c1265
MD
1135 lwkt_domsg(cpu_portfn(sc->pst_cpuid), &msg.base.lmsg, 0);
1136 return msg.base.lmsg.u.ms_resultp;
7b46b972 1137}
0e276230
SZ
1138
1139static int
1140acpi_pst_alloc_resource(device_t dev, ACPI_OBJECT *obj, int idx,
1141 struct acpi_pst_res *res)
1142{
1143 struct acpi_pst_softc *sc = device_get_softc(dev);
10f97674 1144 int error, type;
0e276230
SZ
1145
1146 /* Save GAS */
1147 error = acpi_PkgRawGas(obj, idx, &res->pr_gas);
1148 if (error)
1149 return error;
1150
1151 /* Allocate resource, if possible */
1152 res->pr_rid = sc->pst_parent->cpux_next_rid;
10f97674 1153 acpi_bus_alloc_gas(dev, &type, &res->pr_rid, &res->pr_gas, &res->pr_res, 0);
0e276230
SZ
1154 if (res->pr_res != NULL) {
1155 sc->pst_parent->cpux_next_rid++;
1156 res->pr_bt = rman_get_bustag(res->pr_res);
1157 res->pr_bh = rman_get_bushandle(res->pr_res);
1158 } else {
1159 res->pr_rid = 0;
1160 }
1161 return 0;
1162}