1 /*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21 /*
22 * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
24 */
25
26 /*
27 * Copyright 2023 Oxide Computer Company
28 */
29
30 /*
31 * ppm driver subroutines
32 */
33
34 #include <sys/open.h>
35 #include <sys/file.h>
36 #include <sys/conf.h>
37 #include <sys/epm.h>
38 #include <sys/sunldi.h>
39 #include <sys/ppmvar.h>
40 #include <sys/ppmio.h>
41 #include <sys/promif.h>
42 #include <sys/ddi_impldefs.h>
43 #include <sys/ddi.h>
44 #include <sys/sunddi.h>
45 /*
46 * Append address to the device path, if it is set. Routine
47 * ddi_pathname does not look for device address if the node is in
48 * DS_INITIALIZED state.
49 */
50 #define PPM_GET_PATHNAME(dip, path) \
51 (void) ddi_pathname((dip), (path)); \
52 if ((i_ddi_node_state((dip)) < DS_INITIALIZED) && \
53 (ddi_get_name_addr((dip)) != NULL)) { \
54 (void) strcat((path), "@"); \
55 (void) strcat((path), ddi_get_name_addr((dip)));\
56 }
57
58 int ppm_parse_dc(char **, ppm_dc_t *);
59 int ppm_match_devs(char *, ppm_db_t *);
60 ppm_db_t *ppm_parse_pattern(struct ppm_db **, char *);
61 int ppm_count_char(char *, char);
62 int ppm_stoi(char *, uint_t *);
63 int ppm_convert(char *, uint_t *);
64 void ppm_prop_free(struct ppm_cdata **);
65
66 /*
67 * lookup string property from configuration file ppm.conf
68 */
69 static int
ppm_get_confdata(struct ppm_cdata ** cdp,dev_info_t * dip)70 ppm_get_confdata(struct ppm_cdata **cdp, dev_info_t *dip)
71 {
72 #ifdef DEBUG
73 char *str = "ppm_get_confdata";
74 #endif
75 struct ppm_cdata *cinfo;
76 int err;
77
78 for (; (cinfo = *cdp) != NULL; cdp++) {
79 err = ddi_prop_lookup_string_array(
80 DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS,
81 cinfo->name, &cinfo->strings, &cinfo->cnt);
82 if (err != DDI_PROP_SUCCESS) {
83 PPMD(D_ERROR, ("%s: no %s found, err(%d)\n",
84 str, cinfo->name, err))
85 break;
86 }
87 }
88 return (err);
89 }
90
91 void
ppm_prop_free(struct ppm_cdata ** cdp)92 ppm_prop_free(struct ppm_cdata **cdp)
93 {
94 if (cdp) {
95 for (; *cdp; cdp++) {
96 if ((*cdp)->name) {
97 kmem_free((*cdp)->name,
98 strlen((*cdp)->name) + 1);
99 (*cdp)->name = NULL;
100 }
101 if ((*cdp)->strings) {
102 ddi_prop_free((*cdp)->strings);
103 (*cdp)->strings = NULL;
104 }
105 }
106 }
107 }
108
109
110 /*
111 * free ddi prop strings. Under error condition, free ppm_db_t lists as well.
112 */
113 static int
ppm_attach_err(struct ppm_cdata ** cdp,int err)114 ppm_attach_err(struct ppm_cdata **cdp, int err)
115 {
116 ppm_domain_t *domp;
117 ppm_db_t *db, *tmp;
118
119 ppm_prop_free(cdp);
120 if (err != DDI_SUCCESS) {
121 for (domp = ppm_domain_p; domp; domp = domp->next) {
122 for (db = domp->conflist; (tmp = db) != NULL; ) {
123 db = db->next;
124 kmem_free(tmp->name, strlen(tmp->name) + 1);
125 kmem_free(tmp, sizeof (*tmp));
126 }
127 domp->conflist = NULL;
128 }
129 err = DDI_FAILURE;
130 }
131
132 return (err);
133 }
134
135
136 ppm_domain_t *
ppm_lookup_domain(char * dname)137 ppm_lookup_domain(char *dname)
138 {
139 ppm_domain_t *domp;
140
141 for (domp = ppm_domain_p; domp; domp = domp->next) {
142 if (strcmp(dname, domp->name) == 0)
143 break;
144 }
145 return (domp);
146 }
147
148
149 /*
150 * for the purpose of optimizing we search for identical dc->path
151 * that has been opened per previous visit here. If search results
152 * in a hit, copy the device handle, else open the device.
153 */
154 ppm_dc_t *
ppm_lookup_hndl(int model,ppm_dc_t * key_dc)155 ppm_lookup_hndl(int model, ppm_dc_t *key_dc)
156 {
157 #ifdef DEBUG
158 char *str = "ppm_lookup_hndl";
159 #endif
160 char *key_path = key_dc->path;
161 ppm_domain_t *domp;
162 ppm_dc_t *dc;
163
164 /* search domain by domain.model */
165 for (domp = ppm_domain_p; domp; domp = domp->next) {
166 if (domp->model == model)
167 break;
168 }
169
170 /* lookup hndl from same domain model */
171 if (domp && PPM_DOMAIN_UP(domp)) {
172 for (dc = domp->dc; dc; dc = dc->next) {
173 if ((strcmp(dc->path, key_path) == 0) &&
174 (dc->lh != NULL)) {
175 PPMD(D_PPMDC, ("%s: Hit(dc_path:%s) from SAME "
176 "domain %s.\n", str, key_path, domp->name))
177 key_dc->lh = dc->lh;
178 return (key_dc);
179 }
180 }
181 }
182
183 /* otherwise, check other domains */
184 for (domp = ppm_domain_p;
185 domp && (domp->model != model); domp = domp->next) {
186 if (PPM_DOMAIN_UP(domp)) {
187 for (dc = domp->dc; dc; dc = dc->next) {
188 if ((strcmp(dc->path, key_path) == 0) &&
189 (dc->lh != NULL)) {
190 PPMD(D_PPMDC, ("%s: Hit(dc_path:%s) "
191 "from domain %s\n",
192 str, key_path, domp->name))
193 key_dc->lh = dc->lh;
194 return (key_dc);
195 }
196 }
197 }
198 }
199
200 PPMD(D_PPMDC, ("%s: Miss(dc_path:%s)\n", str, key_path))
201 return (NULL);
202 }
203
204
205 #define PPM_DOMAIN_PROP "ppm-domains"
206 #define PPM_DEV_PROP_SUFFIX "-devices"
207 #define PPM_MODEL_PROP_SUFFIX "-model"
208 #define PPM_PROPNAME_PROP_SUFFIX "-propname"
209 #define PPM_CTRL_PROP_SUFFIX "-control"
210
211 struct ppm_domit ppm_domit_data[] = {
212 "SX", PPMD_SX, 0, PPMD_ON,
213 "CPU", PPMD_CPU, PPMD_LOCK_ALL, PPMD_ON,
214 "FET", PPMD_FET, PPMD_LOCK_ONE, PPMD_ON,
215 "PCI", PPMD_PCI, PPMD_LOCK_ONE, PPMD_ON,
216 "PCI_PROP", PPMD_PCI_PROP, PPMD_LOCK_ONE, PPMD_ON,
217 "LED", PPMD_LED, 0, PPMD_ON,
218 "PCIE", PPMD_PCIE, PPMD_LOCK_ONE, PPMD_ON,
219 NULL
220 };
221
222 /*
223 * store up platform dependent information provided by ppm.conf file
224 * into private data base
225 */
226 int
ppm_create_db(dev_info_t * dip)227 ppm_create_db(dev_info_t *dip)
228 {
229 #ifdef DEBUG
230 char *str = "ppm_create_db";
231 #endif
232 ppm_domain_t *domp;
233 ppm_db_t *db;
234 ppm_dc_t *dc;
235 struct ppm_cdata domdata; /* hold "ppm-domains" property */
236 struct ppm_cdata modeldata; /* hold "domain_xy-model" property */
237 struct ppm_cdata propnamedata; /* hold "domain_xy-propname" property */
238 struct ppm_cdata devdata; /* hold "domain_xy-devices" property */
239 struct ppm_cdata dcdata; /* hold "domain_xy-control" property */
240 struct ppm_cdata *cdata[2];
241 char **dom_namep, **model_namep, **dev_namep, **dc_namep;
242 struct ppm_domit *domit_p;
243 int err;
244
245 /*
246 * get "ppm-domains" property
247 */
248 bzero(&domdata, sizeof (domdata));
249 domdata.name = kmem_zalloc(strlen(PPM_DOMAIN_PROP) + 1, KM_SLEEP);
250 (void) strcpy(domdata.name, PPM_DOMAIN_PROP);
251 cdata[0] = &domdata;
252 cdata[1] = NULL;
253 if (err = ppm_get_confdata(cdata, dip)) {
254 PPMD(D_CREATEDB, ("%s: failed to get prop \"%s\"!\n",
255 str, PPM_DOMAIN_PROP))
256 return (ppm_attach_err(cdata, err));
257 }
258
259 for (dom_namep = domdata.strings; *dom_namep; dom_namep++) {
260 domp = kmem_zalloc(sizeof (*domp), KM_SLEEP);
261 domp->name = kmem_zalloc(strlen(*dom_namep) + 1, KM_SLEEP);
262 (void) strcpy(domp->name, *dom_namep);
263 mutex_init(&domp->lock, NULL, MUTEX_DRIVER, NULL);
264 if (ppm_domain_p == NULL)
265 ppm_domain_p = domp;
266 else {
267 domp->next = ppm_domain_p;
268 ppm_domain_p = domp;
269 }
270 }
271 ppm_prop_free(cdata);
272
273 /*
274 * more per domain property strings in ppm.conf file tell us
275 * what the nature of domain, how to performe domain control, etc.
276 * Even the property names of those per domain properties are
277 * formed consisting its domain name string.
278 * Here we walk through our domain list, and fullfill the details.
279 */
280 for (domp = ppm_domain_p; domp; domp = domp->next) {
281 size_t plen;
282
283 /*
284 * get "domain_xy-model" property
285 */
286 bzero(&modeldata, sizeof (modeldata));
287 plen = strlen(domp->name) + strlen(PPM_MODEL_PROP_SUFFIX) + 1;
288 modeldata.name = kmem_zalloc(plen, KM_SLEEP);
289 (void) sprintf(modeldata.name, "%s%s",
290 domp->name, PPM_MODEL_PROP_SUFFIX);
291
292 cdata[0] = &modeldata;
293 cdata[1] = NULL;
294 if (err = ppm_get_confdata(cdata, dip)) {
295 PPMD(D_CREATEDB, ("%s: Can't read property %s!\n",
296 str, modeldata.name))
297 return (ppm_attach_err(cdata, err));
298 }
299
300 model_namep = modeldata.strings;
301 for (domit_p = ppm_domit_data; domit_p->name; domit_p++) {
302 if (strcmp(domit_p->name, *model_namep) == 0) {
303 domp->model = domit_p->model;
304 domp->dflags = domit_p->dflags;
305 domp->status = domit_p->status;
306 break;
307 }
308 }
309 ASSERT(domit_p);
310
311 ppm_prop_free(cdata);
312
313
314 /* get "domain_xy-propname" property */
315 bzero(&propnamedata, sizeof (propnamedata));
316 plen = strlen(domp->name) +
317 strlen(PPM_PROPNAME_PROP_SUFFIX) + 1;
318 propnamedata.name = kmem_zalloc(plen, KM_SLEEP);
319 (void) sprintf(propnamedata.name, "%s%s",
320 domp->name, PPM_PROPNAME_PROP_SUFFIX);
321
322 cdata[0] = &propnamedata;
323 cdata[1] = NULL;
324 if (ppm_get_confdata(cdata, dip) == DDI_PROP_SUCCESS) {
325 domp->propname = kmem_zalloc(
326 (strlen(*propnamedata.strings) + 1), KM_SLEEP);
327 (void) strcpy(domp->propname, *propnamedata.strings);
328 PPMD(D_CREATEDB, ("%s: %s has property name: %s\n",
329 str, domp->name, domp->propname))
330 }
331 ppm_prop_free(cdata);
332
333
334 /* get "domain_xy-devices" property */
335 bzero(&devdata, sizeof (devdata));
336 plen = strlen(domp->name) + strlen(PPM_DEV_PROP_SUFFIX) + 1;
337 devdata.name = kmem_zalloc(plen, KM_SLEEP);
338 (void) sprintf(devdata.name, "%s%s",
339 domp->name, PPM_DEV_PROP_SUFFIX);
340
341 cdata[0] = &devdata;
342 cdata[1] = NULL;
343 if (err = ppm_get_confdata(cdata, dip)) {
344 PPMD(D_CREATEDB, ("%s: Can't read property %s!\n",
345 str, devdata.name))
346 return (ppm_attach_err(cdata, err));
347 }
348
349 for (dev_namep = devdata.strings; *dev_namep; dev_namep++) {
350 if (!ppm_parse_pattern(&db, *dev_namep))
351 return (ppm_attach_err(cdata, err));
352 db->next = domp->conflist;
353 domp->conflist = db;
354 PPMD(D_CREATEDB, ("%s: %s add pattern: %s \n",
355 str, devdata.name, db->name))
356 }
357 PPMD(D_CREATEDB, ("\n"))
358 ppm_prop_free(cdata);
359
360
361 /* get "domain_xy-control" property */
362 bzero(&dcdata, sizeof (dcdata));
363 plen = strlen(domp->name) + strlen(PPM_CTRL_PROP_SUFFIX) + 1;
364 dcdata.name = kmem_zalloc(plen, KM_SLEEP);
365 (void) sprintf(dcdata.name, "%s%s",
366 domp->name, PPM_CTRL_PROP_SUFFIX);
367
368 cdata[0] = &dcdata;
369 cdata[1] = NULL;
370 if (ppm_get_confdata(cdata, dip) == DDI_PROP_SUCCESS) {
371 for (dc_namep = dcdata.strings; *dc_namep;
372 dc_namep++) {
373 dc = kmem_zalloc(sizeof (*dc), KM_SLEEP);
374 dc->next = domp->dc;
375 domp->dc = dc;
376 err = ppm_parse_dc(dc_namep, domp->dc);
377 if (err != DDI_SUCCESS)
378 return (ppm_attach_err(cdata, err));
379 }
380 }
381 ppm_prop_free(cdata);
382 #ifdef DEBUG
383 dc = domp->dc;
384 while (dc) {
385 ppm_print_dc(dc);
386 dc = dc->next;
387 }
388 #endif
389 }
390
391 return (DDI_SUCCESS);
392 }
393
394
395 /*
396 * scan conf devices within each domain for a matching device name
397 */
398 ppm_domain_t *
ppm_lookup_dev(dev_info_t * dip)399 ppm_lookup_dev(dev_info_t *dip)
400 {
401 char path[MAXNAMELEN];
402 ppm_domain_t *domp;
403 ppm_db_t *dbp;
404 #ifdef __x86
405 char *devtype = NULL;
406 #endif /* __x86 */
407
408 PPM_GET_PATHNAME(dip, path);
409 for (domp = ppm_domain_p; domp; domp = domp->next) {
410 if (PPM_DOMAIN_UP(domp)) {
411 for (dbp = domp->conflist; dbp; dbp = dbp->next) {
412 /*
413 * allow claiming root without knowing
414 * its full name
415 */
416 if (dip == ddi_root_node() &&
417 strcmp(dbp->name, "/") == 0)
418 return (domp);
419
420 #ifdef __x86
421 /*
422 * Special rule to catch all CPU devices on x86.
423 */
424 if (domp->model == PPMD_CPU &&
425 strcmp(dbp->name, "/") == 0 &&
426 ddi_prop_lookup_string(DDI_DEV_T_ANY, dip,
427 DDI_PROP_DONTPASS, "device_type",
428 &devtype) == DDI_SUCCESS) {
429 if (strcmp(devtype, "cpu") == 0) {
430 ddi_prop_free(devtype);
431 return (domp);
432 } else {
433 ddi_prop_free(devtype);
434 }
435 }
436 #endif /* __x86 */
437
438 if (ppm_match_devs(path, dbp) == 0)
439 return (domp);
440 }
441 }
442 }
443
444 return (NULL);
445 }
446
447
448 /*
449 * check ppm.conf file domain device pathname syntax, if correct,
450 * create device match pattern.
451 * return 1 for good, -1 for bad.
452 */
453 ppm_db_t *
ppm_parse_pattern(struct ppm_db ** dbpp,char * dev_path)454 ppm_parse_pattern(struct ppm_db **dbpp, char *dev_path)
455 {
456 char path[MAXNAMELEN];
457 int wccnt, i;
458 int wcpos[2];
459 int pos;
460 char *cp;
461 ppm_db_t *dbp;
462
463 (void) strcpy(path, dev_path);
464 if ((wccnt = ppm_count_char(path, '*')) > 2)
465 return (NULL);
466
467 for (i = 0, cp = path, pos = 0; i < wccnt; i++, cp++, pos++) {
468 for (; *cp; cp++, pos++)
469 if (*cp == '*')
470 break;
471 wcpos[i] = pos;
472 PPMD(D_CREATEDB, (" wildcard #%d, pos %d\n",
473 (i + 1), wcpos[i]))
474 }
475
476 #ifdef DEBUG
477 /* first '*', if exists, don't go beyond the string */
478 if (wccnt > 0)
479 ASSERT(wcpos[0] < strlen(path));
480
481 /* second '*', if exists, better be the last character */
482 if (wccnt == 2)
483 ASSERT(wcpos[1] == (strlen(path) - 1));
484 #endif
485
486 /*
487 * first '*', if followed by any char, must be immediately
488 * followed by '@' and the rest better be bound by
489 * ['0-9', 'a-f', A-F'] until ended '0' or second '*''0'.
490 */
491 if ((wccnt > 0) && (wcpos[0] < (strlen(path) - 1))) {
492 cp = path + wcpos[0] + 1;
493 if (*cp != '@')
494 return (NULL);
495
496 if (!(((*(++cp) > '0') && (*cp < '9')) ||
497 ((*cp > 'a') && (*cp < 'f')) ||
498 ((*cp > 'A') && (*cp < 'F'))))
499 return (NULL);
500 }
501
502 dbp = kmem_zalloc(sizeof (struct ppm_db), KM_SLEEP);
503 dbp->name = kmem_zalloc((strlen(path) + 1), KM_SLEEP);
504 (void) strcpy(dbp->name, path);
505 dbp->wccnt = wccnt;
506 dbp->wcpos[0] = (wccnt > 0) ? wcpos[0] : -1;
507 dbp->wcpos[1] = (wccnt == 2) ? wcpos[1] : -1;
508
509 return (*dbpp = dbp);
510 }
511
512
513 /*
514 * match given device "path" to domain device pathname
515 * pattern dbp->name that contains one or two '*' character(s).
516 * Matching policy:
517 * 1). If one wildcard terminates match pattern, need exact match
518 * up to (but exclude) the wildcard;
519 * 2). If one wildcard does not terminate match pattern, it is to
520 * match driver name (terminates with '@') and must be followed
521 * by exact match of rest of pattern;
522 * 3). If two wildcards, first is to match driver name as in 2),
523 * second is to match fcnid (terminates with '/' or '\0') and
524 * must the last char of pattern.
525 *
526 * return 0 if match, and
527 * non 0 if mismatch
528 */
529 int
ppm_match_devs(char * dev_path,ppm_db_t * dbp)530 ppm_match_devs(char *dev_path, ppm_db_t *dbp)
531 {
532 char path[MAXNAMELEN];
533 char *cp; /* points into "path", real device pathname */
534 char *np; /* points into "dbp->name", the pattern */
535 int len;
536
537 if (dbp->wccnt == 0)
538 return (strcmp(dev_path, dbp->name));
539
540 (void) strcpy(path, dev_path);
541
542 /* match upto the first '*' regardless */
543 if (strncmp(path, dbp->name, dbp->wcpos[0]) != 0)
544 return (-1);
545
546
547 /* "<exact match>*" */
548 if (dbp->name[dbp->wcpos[0] + 1] == 0) {
549 cp = path + dbp->wcpos[0];
550 while (*cp && (*cp++ != '/'))
551 ;
552 return ((*cp == 0) ? 0 : -1);
553 }
554
555
556 /* locate '@' */
557 cp = path + dbp->wcpos[0] + 1;
558 while (*cp && *cp != '@')
559 cp++;
560
561 np = dbp->name + dbp->wcpos[0] + 1;
562
563 /* if one wildcard, match the rest in the pattern */
564 if (dbp->wccnt == 1)
565 return ((strcmp(cp, np) == 0) ? 0 : (-1));
566
567
568 /* must have exact match after first wildcard up to second */
569 ASSERT(dbp->wccnt == 2);
570 len = dbp->wcpos[1] - dbp->wcpos[0] - 1;
571 if (strncmp(cp, np, len) != 0)
572 return (-1);
573
574 /* second wildcard match terminates with '/' or '\0' */
575 /* but only termination with '\0' is a successful match */
576 cp += len;
577 while (*cp && (*cp != '/'))
578 cp++;
579 return ((*cp == 0) ? 0 : -1);
580 }
581
582
583 /*
584 * By claiming a device, ppm gets involved in its power change
585 * process: handles additional issues prior and/or post its
586 * power(9e) call.
587 *
588 * If 'dip' is a PCI device, this is the time to ask its parent
589 * what PCI bus speed it is running.
590 *
591 * returns 1 (claimed), 0 (not claimed)
592 */
593 int
ppm_claim_dev(dev_info_t * dip)594 ppm_claim_dev(dev_info_t *dip)
595 {
596 ppm_domain_t *domp;
597 dev_info_t *pdip;
598 uint_t pciclk;
599 int claimed = -1;
600
601 domp = ppm_lookup_dev(dip);
602 if (!domp)
603 claimed = 0;
604
605 if (domp && PPMD_IS_PCI(domp->model) &&
606 ! (domp->dflags & (PPMD_PCI33MHZ | PPMD_PCI66MHZ))) {
607 pdip = ddi_get_parent(dip);
608 ASSERT(pdip);
609 pciclk = ddi_prop_get_int(DDI_DEV_T_ANY, pdip,
610 DDI_PROP_DONTPASS, "clock-frequency", -1);
611
612 switch (pciclk) {
613 case 33000000:
614 domp->dflags |= PPMD_PCI33MHZ;
615 claimed = 1;
616 break;
617 case 66000000:
618 domp->dflags |= PPMD_PCI66MHZ;
619 claimed = 1;
620 break;
621 default:
622 claimed = 0;
623 break;
624 }
625 }
626
627 if (domp && (claimed == -1))
628 claimed = 1;
629
630 #ifdef DEBUG
631 if (claimed) {
632 char path[MAXNAMELEN];
633 PPMD(D_CLAIMDEV, ("ppm_claim_dev: %s into domain %s\n",
634 ddi_pathname(dip, path), domp->name))
635 }
636
637 #endif
638
639 return (claimed);
640 }
641
642 /*
643 * add a device to the list of domain's owned devices (if it is not already
644 * on the list).
645 */
646 ppm_owned_t *
ppm_add_owned(dev_info_t * dip,ppm_domain_t * domp)647 ppm_add_owned(dev_info_t *dip, ppm_domain_t *domp)
648 {
649 char path[MAXNAMELEN];
650 ppm_owned_t *owned, *new_owned;
651
652 ASSERT(MUTEX_HELD(&domp->lock));
653 PPM_GET_PATHNAME(dip, path);
654 for (owned = domp->owned; owned; owned = owned->next)
655 if (strcmp(path, owned->path) == 0)
656 return (owned);
657
658 new_owned = kmem_zalloc(sizeof (*new_owned), KM_SLEEP);
659 new_owned->path = kmem_zalloc(strlen(path) + 1, KM_SLEEP);
660 (void) strcpy(new_owned->path, path);
661 new_owned->next = domp->owned;
662 domp->owned = new_owned;
663
664 return (domp->owned);
665 }
666
667 /*
668 * create/init a new ppm device and link into the domain
669 */
670 ppm_dev_t *
ppm_add_dev(dev_info_t * dip,ppm_domain_t * domp)671 ppm_add_dev(dev_info_t *dip, ppm_domain_t *domp)
672 {
673 char path[MAXNAMELEN];
674 ppm_dev_t *new = NULL;
675 int cmpt;
676 ppm_owned_t *owned;
677
678 ASSERT(MUTEX_HELD(&domp->lock));
679 (void) ddi_pathname(dip, path);
680 /*
681 * For devs which have exported "pm-components" we want to create
682 * a data structure for each component. When a driver chooses not
683 * to export the prop we treat its device as having a single
684 * component and build a structure for it anyway. All other ppm
685 * logic will act as if this device were always up and can thus
686 * make correct decisions about it in relation to other devices
687 * in its domain.
688 */
689 for (cmpt = PM_GET_PM_INFO(dip) ? PM_NUMCMPTS(dip) : 1; cmpt--; ) {
690 new = kmem_zalloc(sizeof (*new), KM_SLEEP);
691 new->path = kmem_zalloc(strlen(path) + 1, KM_SLEEP);
692 (void) strcpy(new->path, path);
693 new->domp = domp;
694 new->dip = dip;
695 new->cmpt = cmpt;
696 ppm_dev_init(new);
697 new->next = domp->devlist;
698 domp->devlist = new;
699 PPMD(D_ADDDEV,
700 ("ppm_add_dev: %s to domain %s: ppm_dev(0x%p)\n",
701 new->path, domp->name, (void *)new))
702 }
703
704 ASSERT(new != NULL);
705 /*
706 * devi_pm_ppm_private should be set only after all
707 * ppm_dev s related to all components have been
708 * initialized and domain's pwr_cnt is incremented
709 * for each of them.
710 */
711 PPM_SET_PRIVATE(dip, new);
712
713 /* remember this device forever */
714 owned = ppm_add_owned(dip, domp);
715
716 /*
717 * Initializing flag is set for devices which have gone through
718 * PPM_PMR_INIT_CHILD ctlop. By this point, these devices have
719 * been added to ppm structures and could participate in pm
720 * decision making, so clear the initializing flag.
721 */
722 if (owned->initializing) {
723 owned->initializing = 0;
724 PPMD(D_ADDDEV, ("ppm_add_dev: cleared initializing flag "
725 "for %s@%s\n", PM_NAME(dip),
726 (PM_ADDR(dip) == NULL) ? "" : PM_ADDR(dip)))
727 }
728
729 return (new);
730 }
731
732
733 /*
734 * returns an existing or newly created ppm device reference
735 */
736 ppm_dev_t *
ppm_get_dev(dev_info_t * dip,ppm_domain_t * domp)737 ppm_get_dev(dev_info_t *dip, ppm_domain_t *domp)
738 {
739 ppm_dev_t *pdp;
740
741 mutex_enter(&domp->lock);
742 pdp = PPM_GET_PRIVATE(dip);
743 if (pdp == NULL)
744 pdp = ppm_add_dev(dip, domp);
745 mutex_exit(&domp->lock);
746
747 return (pdp);
748 }
749
750
751 /*
752 * scan a domain's device list and remove those with .dip
753 * matching the arg *dip; we need to scan the entire list
754 * for the case of devices with multiple components
755 */
756 void
ppm_rem_dev(dev_info_t * dip)757 ppm_rem_dev(dev_info_t *dip)
758 {
759 ppm_dev_t *pdp, **devpp;
760 ppm_domain_t *domp;
761
762 pdp = PPM_GET_PRIVATE(dip);
763 ASSERT(pdp);
764 domp = pdp->domp;
765 ASSERT(domp);
766
767 mutex_enter(&domp->lock);
768 for (devpp = &domp->devlist; (pdp = *devpp) != NULL; ) {
769 if (pdp->dip != dip) {
770 devpp = &pdp->next;
771 continue;
772 }
773
774 PPMD(D_REMDEV, ("ppm_rem_dev: path \"%s\", ppm_dev 0x%p\n",
775 pdp->path, (void *)pdp))
776
777 PPM_SET_PRIVATE(dip, NULL);
778 *devpp = pdp->next;
779 ppm_dev_fini(pdp);
780 kmem_free(pdp->path, strlen(pdp->path) + 1);
781 kmem_free(pdp, sizeof (*pdp));
782 }
783 mutex_exit(&domp->lock);
784 }
785
786 /*
787 * prepare kernel ioctl calls:
788 */
789 void
ppm_init_cb(dev_info_t * dip)790 ppm_init_cb(dev_info_t *dip)
791 {
792 char *str = "ppm_init_cb";
793 ppm_domain_t *domp;
794 ppm_dc_t *dc;
795
796 for (domp = ppm_domain_p; domp != NULL; domp = domp->next) {
797 for (dc = domp->dc; dc; dc = dc->next) {
798 /*
799 * Warning: This code is rather confusing.
800 *
801 * It intends to ensure that ppm_init_lyr() is only
802 * called ONCE for a device that may be associated
803 * with more than one domain control.
804 * So, what it does is first to check to see if
805 * there is a handle, and then if not it goes on
806 * to call the init_lyr() routine.
807 *
808 * The non-obvious thing is that the ppm_init_lyr()
809 * routine, in addition to opening the device
810 * associated with the dc (domain control) in
811 * question, has the side-effect of creating the
812 * handle for that dc as well.
813 */
814 if (ppm_lookup_hndl(domp->model, dc) != NULL)
815 continue;
816
817 if (ppm_init_lyr(dc, dip) != DDI_SUCCESS) {
818 domp->dflags |= PPMD_OFFLINE;
819 cmn_err(CE_WARN, "%s: ppm domain %s will "
820 "be offline.", str, domp->name);
821 break;
822 }
823 }
824 }
825 }
826
827
828 /*
829 * ppm_init_lyr - initializing layered ioctl
830 * Return:
831 * DDI_SUCCESS - succeeded
832 * DDI_FAILURE - failed
833 *
834 */
835 int
ppm_init_lyr(ppm_dc_t * dc,dev_info_t * dip)836 ppm_init_lyr(ppm_dc_t *dc, dev_info_t *dip)
837 {
838 char *str = "ppm_init_lyr";
839 int err = 0;
840 ldi_ident_t li;
841
842 ASSERT(dc && dc->path);
843
844 if (err = ldi_ident_from_dip(dip, &li)) {
845 cmn_err(CE_WARN, "%s: get ldi identifier "
846 "failed (err=%d)", str, err);
847 }
848
849 err = ldi_open_by_name(dc->path, FWRITE|FREAD, kcred, &(dc->lh), li);
850
851 (void) ldi_ident_release(li);
852
853 if (err != 0) {
854 cmn_err(CE_WARN, "Failed to open device(%s), rv(%d)",
855 dc->path, err);
856 return (err);
857 }
858
859 return (DDI_SUCCESS);
860 }
861
862 /*
863 * lock, unlock, or trylock for one power mutex
864 */
865 void
ppm_lock_one(ppm_dev_t * ppmd,power_req_t * reqp,int * iresp)866 ppm_lock_one(ppm_dev_t *ppmd, power_req_t *reqp, int *iresp)
867 {
868 switch (reqp->request_type) {
869 case PMR_PPM_LOCK_POWER:
870 pm_lock_power_single(ppmd->dip);
871 break;
872
873 case PMR_PPM_UNLOCK_POWER:
874 pm_unlock_power_single(ppmd->dip);
875 break;
876
877 case PMR_PPM_TRY_LOCK_POWER:
878 *iresp = pm_try_locking_power_single(ppmd->dip);
879 break;
880 }
881 }
882
883
884 /*
885 * lock, unlock, or trylock for all power mutexes within a domain
886 */
887 void
ppm_lock_all(ppm_domain_t * domp,power_req_t * reqp,int * iresp)888 ppm_lock_all(ppm_domain_t *domp, power_req_t *reqp, int *iresp)
889 {
890 /*
891 * To simplify the implementation we let all the devices
892 * in the domain be represented by a single device (dip).
893 * We use the first device in the domain's devlist. This
894 * is safe because we return with the domain lock held
895 * which prevents the list from changing.
896 */
897 if (reqp->request_type == PMR_PPM_LOCK_POWER) {
898 if (!MUTEX_HELD(&domp->lock))
899 mutex_enter(&domp->lock);
900 domp->refcnt++;
901 ASSERT(domp->devlist != NULL);
902 pm_lock_power_single(domp->devlist->dip);
903 /* domain lock remains held */
904 return;
905 } else if (reqp->request_type == PMR_PPM_UNLOCK_POWER) {
906 ASSERT(MUTEX_HELD(&domp->lock));
907 ASSERT(domp->devlist != NULL);
908 pm_unlock_power_single(domp->devlist->dip);
909 if (--domp->refcnt == 0)
910 mutex_exit(&domp->lock);
911 return;
912 }
913
914 ASSERT(reqp->request_type == PMR_PPM_TRY_LOCK_POWER);
915 if (!MUTEX_HELD(&domp->lock))
916 if (!mutex_tryenter(&domp->lock)) {
917 *iresp = 0;
918 return;
919 }
920 *iresp = pm_try_locking_power_single(domp->devlist->dip);
921 if (*iresp)
922 domp->refcnt++;
923 else
924 mutex_exit(&domp->lock);
925 }
926
927
928 /*
929 * return FALSE: if any detached device during its previous life exported
930 * the "no-involuntary-power-cycles" property and detached with its
931 * power level not at its lowest, or there is a device in the process
932 * of being installed/attached; if a PCI domain has devices that have not
933 * exported a property that it can tolerate clock off while bus is not
934 * quiescent; if a 66mhz PCI domain has devices that do not support stopping
935 * clock at D3; either one would count as a power holder.
936 * return TRUE: otherwise.
937 */
938 boolean_t
ppm_none_else_holds_power(ppm_domain_t * domp)939 ppm_none_else_holds_power(ppm_domain_t *domp)
940 {
941 ppm_dev_t *ppmd;
942 ppm_owned_t *owned;
943 int i = 0;
944
945 if (PPMD_IS_PCI(domp->model)) {
946 for (ppmd = domp->devlist; ppmd; ppmd = ppmd->next) {
947 if ((domp->model == PPMD_PCI_PROP) &&
948 !(ppmd->flags & PPMDEV_PCI_PROP_CLKPM))
949 return (B_FALSE);
950 if ((domp->dflags & PPMD_PCI66MHZ) &&
951 !(ppmd->flags & PPMDEV_PCI66_D2))
952 return (B_FALSE);
953 }
954 }
955
956 for (owned = domp->owned; owned; owned = owned->next)
957 if (pm_noinvol_detached(owned->path) || owned->initializing)
958 i++;
959 return (i == 0);
960 }
961
962
963 /*
964 * return the number of char 'c' occurrences in string s
965 */
966 int
ppm_count_char(char * s,char c)967 ppm_count_char(char *s, char c)
968 {
969 int i = 0;
970 char *cp = s;
971
972 while (*cp) {
973 if (*cp == c)
974 i++;
975 cp++;
976 }
977
978 return (i);
979 }
980
981
982 /*
983 * extract and convert a substring from input string "ss" in form of
984 * "name=value" into an hex or decimal integer
985 */
986 #define X_BASE 16
987 #define D_BASE 10
988 int
ppm_stoi(char * ss,uint_t * val)989 ppm_stoi(char *ss, uint_t *val)
990 {
991 char *cp;
992 int hex_ = 0, base = D_BASE;
993 int digit;
994
995 if ((cp = strchr(ss, '=')) == NULL) {
996 *val = UINT_MAX;
997 return (-1);
998 }
999
1000 cp++;
1001 if ((*cp == '0') && (*++cp == 'x')) {
1002 hex_++;
1003 cp++;
1004 base = X_BASE;
1005 }
1006
1007 for (digit = 0; *cp; cp++) {
1008 if (hex_ && ((*cp >= 'A') && (*cp <= 'F')))
1009 digit = (digit * base) + ((*cp - 'A') + D_BASE);
1010 else if (hex_ && ((*cp >= 'a') && (*cp <= 'f')))
1011 digit = (digit * base) + ((*cp - 'a') + D_BASE);
1012 else
1013 digit = (digit * base) + (*cp - '0');
1014 }
1015
1016 return (*val = digit);
1017 }
1018
1019 /*
1020 * ppm_convert - convert a #define symbol to its integer value,
1021 * only the #defines for ppm_dc.cmd and ppm_dc.method fields in
1022 * ppmvar.h file are recognized.
1023 */
1024 struct ppm_confdefs {
1025 char *sym;
1026 int val;
1027 } ppm_confdefs_table[] = {
1028 "ENTER_S3", PPMDC_ENTER_S3,
1029 "EXIT_S3", PPMDC_EXIT_S3,
1030 "CPU_NEXT", PPMDC_CPU_NEXT,
1031 "PRE_CHNG", PPMDC_PRE_CHNG,
1032 "CPU_GO", PPMDC_CPU_GO,
1033 "POST_CHNG", PPMDC_POST_CHNG,
1034 "FET_ON", PPMDC_FET_ON,
1035 "FET_OFF", PPMDC_FET_OFF,
1036 "CLK_OFF", PPMDC_CLK_OFF,
1037 "CLK_ON", PPMDC_CLK_ON,
1038 "LED_ON", PPMDC_LED_ON,
1039 "LED_OFF", PPMDC_LED_OFF,
1040 "KIO", PPMDC_KIO,
1041 "VCORE", PPMDC_VCORE,
1042 #ifdef sun4u
1043 "I2CKIO", PPMDC_I2CKIO,
1044 #endif
1045 "CPUSPEEDKIO", PPMDC_CPUSPEEDKIO,
1046 "PRE_PWR_OFF", PPMDC_PRE_PWR_OFF,
1047 "PRE_PWR_ON", PPMDC_PRE_PWR_ON,
1048 "POST_PWR_ON", PPMDC_POST_PWR_ON,
1049 "PWR_OFF", PPMDC_PWR_OFF,
1050 "PWR_ON", PPMDC_PWR_ON,
1051 "RESET_OFF", PPMDC_RESET_OFF,
1052 "RESET_ON", PPMDC_RESET_ON,
1053 NULL
1054 };
1055
1056
1057 /*
1058 * convert a #define'd symbol to its integer value where
1059 * input "symbol" is expected to be in form of "SYMBOL=value"
1060 */
1061 int
ppm_convert(char * symbol,uint_t * val)1062 ppm_convert(char *symbol, uint_t *val)
1063 {
1064 char *s;
1065 struct ppm_confdefs *pcfp;
1066
1067 *val = UINT_MAX;
1068 if ((s = strchr(symbol, '=')) == NULL) {
1069 cmn_err(CE_WARN, "ppm_convert: token \"%s\" syntax error in "
1070 "ppm.conf file", symbol);
1071 return (-1);
1072 }
1073 s++;
1074
1075 for (pcfp = ppm_confdefs_table; (pcfp->sym != NULL); pcfp++) {
1076 if (strcmp(s, pcfp->sym) == 0)
1077 return (*val = pcfp->val);
1078 }
1079
1080 cmn_err(CE_WARN, "ppm_convert: Unrecognizable token \"%s\" "
1081 "in ppm.conf file", symbol);
1082 return (-1);
1083 }
1084
1085
1086 /*
1087 * parse a domain control property string into data structure struct ppm_dc
1088 */
1089 int
ppm_parse_dc(char ** dc_namep,ppm_dc_t * dc)1090 ppm_parse_dc(char **dc_namep, ppm_dc_t *dc)
1091 {
1092 char *str = "ppm_parse_dc";
1093 char *line;
1094 char *f, *b;
1095 char **dclist; /* list of ppm_dc_t fields */
1096 int count; /* the # of '=' indicates the # of items */
1097 size_t len; /* length of line being parsed */
1098 boolean_t done;
1099 int i;
1100 int err;
1101
1102 len = strlen(*dc_namep);
1103 line = kmem_alloc(len + 1, KM_SLEEP);
1104 (void) strcpy(line, *dc_namep);
1105
1106 count = ppm_count_char(line, '=');
1107 ASSERT((count - ppm_count_char(line, ' ')) == 1);
1108
1109 dclist = (char **)
1110 kmem_zalloc((sizeof (char *) * (count + 1)), KM_SLEEP);
1111 for (i = 0, f = b = line, done = B_FALSE; !done; i++, f = ++b) {
1112 while (*b != ' ' && *b != 0)
1113 b++;
1114 if (*b == 0)
1115 done = B_TRUE;
1116 else
1117 *b = 0;
1118 dclist[i] = f;
1119 }
1120
1121 for (i = 0; i < count; i++) {
1122 if (strstr(dclist[i], "cmd=")) {
1123 err = ppm_convert(dclist[i], &dc->cmd);
1124 if (err == -1)
1125 return (err);
1126 continue;
1127 }
1128 if ((f = strstr(dclist[i], "path=")) != NULL) {
1129 f += strlen("path=");
1130 dc->path = kmem_zalloc((strlen(f) + 1), KM_SLEEP);
1131 (void) strcpy(dc->path, f);
1132 continue;
1133 }
1134 if (strstr(dclist[i], "method=")) {
1135 err = ppm_convert(dclist[i], &dc->method);
1136 if (err == -1)
1137 return (err);
1138 continue;
1139 }
1140 if (strstr(dclist[i], "iowr=")) {
1141 (void) ppm_stoi(dclist[i], &dc->m_un.kio.iowr);
1142 continue;
1143 }
1144 if (strstr(dclist[i], "iord=")) {
1145 (void) ppm_stoi(dclist[i], &dc->m_un.kio.iord);
1146 continue;
1147 }
1148 if (strstr(dclist[i], "val=")) {
1149 (void) ppm_stoi(dclist[i], &dc->m_un.kio.val);
1150 continue;
1151 }
1152 if (strstr(dclist[i], "speeds=")) {
1153 ASSERT(dc->method == PPMDC_CPUSPEEDKIO);
1154 (void) ppm_stoi(dclist[i], &dc->m_un.cpu.speeds);
1155 continue;
1156 }
1157 #ifdef sun4u
1158 if (strstr(dclist[i], "mask=")) {
1159 (void) ppm_stoi(dclist[i], &dc->m_un.i2c.mask);
1160 continue;
1161 }
1162 #endif
1163 /* This must be before the if statement for delay */
1164 if (strstr(dclist[i], "post_delay=")) {
1165 #ifdef sun4u
1166 ASSERT(dc->method == PPMDC_KIO ||
1167 dc->method == PPMDC_I2CKIO);
1168 #else
1169 ASSERT(dc->method == PPMDC_KIO);
1170 #endif
1171 /*
1172 * all delays are uint_t type instead of clock_t.
1173 * If the delay is too long, it might get truncated.
1174 * But, we don't expect delay to be too long.
1175 */
1176 switch (dc->method) {
1177 case PPMDC_KIO:
1178 (void) ppm_stoi(dclist[i],
1179 &dc->m_un.kio.post_delay);
1180 break;
1181
1182 #ifdef sun4u
1183 case PPMDC_I2CKIO:
1184 (void) ppm_stoi(dclist[i],
1185 &dc->m_un.i2c.post_delay);
1186 break;
1187 #endif
1188
1189 default:
1190 break;
1191 }
1192 continue;
1193 }
1194 if (strstr(dclist[i], "delay=")) {
1195 #ifdef sun4u
1196 ASSERT(dc->method == PPMDC_VCORE ||
1197 dc->method == PPMDC_KIO ||
1198 dc->method == PPMDC_I2CKIO);
1199 #else
1200 ASSERT(dc->method == PPMDC_VCORE ||
1201 dc->method == PPMDC_KIO);
1202 #endif
1203
1204 /*
1205 * all delays are uint_t type instead of clock_t.
1206 * If the delay is too long, it might get truncated.
1207 * But, we don't expect delay to be too long.
1208 */
1209
1210 switch (dc->method) {
1211 case PPMDC_KIO:
1212 (void) ppm_stoi(dclist[i], &dc->m_un.kio.delay);
1213 break;
1214
1215 #ifdef sun4u
1216 case PPMDC_I2CKIO:
1217 (void) ppm_stoi(dclist[i], &dc->m_un.i2c.delay);
1218 break;
1219 #endif
1220
1221 case PPMDC_VCORE:
1222 (void) ppm_stoi(dclist[i], &dc->m_un.cpu.delay);
1223 break;
1224
1225 default:
1226 break;
1227 }
1228 continue;
1229 }
1230
1231 /* we encounted unrecognized field, flag error */
1232 cmn_err(CE_WARN, "%s: Unrecognized token \"%s\" in ppm.conf "
1233 "file!", str, dclist[i]);
1234 return (-1);
1235 }
1236
1237 kmem_free(dclist, sizeof (char *) * (count + 1));
1238 kmem_free(line, len + 1);
1239
1240 return (DDI_SUCCESS);
1241 }
1242
1243
1244 /*
1245 * search for domain control handle for a claimed device coupled with a
1246 * domain control command. NULL device may indicate LED domain.
1247 */
1248 ppm_dc_t *
ppm_lookup_dc(ppm_domain_t * domp,int cmd)1249 ppm_lookup_dc(ppm_domain_t *domp, int cmd)
1250 {
1251 #ifdef DEBUG
1252 char *str = "ppm_lookup_dc";
1253 #endif
1254 ppm_dc_t *dc;
1255
1256 /*
1257 * For convenience, we accept 'domp' as NULL for searching
1258 * LED domain control operation.
1259 */
1260 if ((cmd == PPMDC_LED_OFF) || (cmd == PPMDC_LED_ON)) {
1261 for (domp = ppm_domain_p; domp; domp = domp->next)
1262 if (domp->model == PPMD_LED)
1263 break;
1264 if (!domp || !domp->dc || !domp->dc->lh || !domp->dc->next) {
1265 PPMD(D_LED, ("\tinsufficient led domain control "
1266 "information.\n"))
1267 return (NULL);
1268 }
1269 if (cmd == domp->dc->cmd)
1270 return (domp->dc);
1271 else
1272 return (domp->dc->next);
1273 }
1274
1275
1276 /*
1277 * for the rest of ppm domains, lookup ppm_dc starting from domp
1278 */
1279 ASSERT(domp != NULL);
1280 switch (cmd) {
1281 case PPMDC_CPU_NEXT:
1282 case PPMDC_PRE_CHNG:
1283 case PPMDC_CPU_GO:
1284 case PPMDC_POST_CHNG:
1285 case PPMDC_FET_OFF:
1286 case PPMDC_FET_ON:
1287 case PPMDC_CLK_OFF:
1288 case PPMDC_CLK_ON:
1289 case PPMDC_PRE_PWR_OFF:
1290 case PPMDC_PRE_PWR_ON:
1291 case PPMDC_POST_PWR_ON:
1292 case PPMDC_PWR_OFF:
1293 case PPMDC_PWR_ON:
1294 case PPMDC_RESET_OFF:
1295 case PPMDC_RESET_ON:
1296 case PPMDC_ENTER_S3:
1297 case PPMDC_EXIT_S3:
1298 break;
1299 default:
1300 PPMD(D_PPMDC, ("%s: cmd(%d) unrecognized\n", str, cmd))
1301 return (NULL);
1302 }
1303
1304 for (dc = domp->dc; dc; dc = dc->next) {
1305 if (dc->cmd == cmd) {
1306 return (dc);
1307 }
1308 }
1309
1310 return (NULL);
1311 }
1312
1313 #include <sys/esunddi.h>
1314
1315 ppm_domain_t *
ppm_get_domain_by_dev(const char * p)1316 ppm_get_domain_by_dev(const char *p)
1317 {
1318 dev_info_t *dip;
1319 ppm_domain_t *domp;
1320 ppm_dev_t *pdev;
1321 boolean_t found = B_FALSE;
1322
1323 if ((dip = e_ddi_hold_devi_by_path((char *)p, 0)) == NULL)
1324 return (NULL);
1325
1326 for (domp = ppm_domain_p; domp; domp = domp->next) {
1327 for (pdev = domp->devlist; pdev; pdev = pdev->next) {
1328 if (pdev->dip == dip) {
1329 found = B_TRUE;
1330 break;
1331 }
1332 }
1333 if (found)
1334 break;
1335 }
1336 ddi_release_devi(dip);
1337 return (domp);
1338 }
1339
1340
1341 #ifdef DEBUG
1342 #define FLINTSTR(flags, sym) { flags, sym, #sym }
1343 #define PMR_UNKNOWN -1
1344 /*
1345 * convert a ctlop integer to a char string. this helps printing
1346 * meaningful info when cltops are received from the pm framework.
1347 * since some ctlops are so frequent, we use mask to limit output:
1348 * a valid string is returned when ctlop is found and when
1349 * (cmd.flags & mask) is true; otherwise NULL is returned.
1350 */
1351 char *
ppm_get_ctlstr(int ctlop,uint_t mask)1352 ppm_get_ctlstr(int ctlop, uint_t mask)
1353 {
1354 struct ctlop_cmd {
1355 uint_t flags;
1356 int ctlop;
1357 char *str;
1358 };
1359
1360 struct ctlop_cmd *ccp;
1361 static struct ctlop_cmd cmds[] = {
1362 FLINTSTR(D_SETPWR, PMR_SET_POWER),
1363 FLINTSTR(D_CTLOPS2, PMR_SUSPEND),
1364 FLINTSTR(D_CTLOPS2, PMR_RESUME),
1365 FLINTSTR(D_CTLOPS2, PMR_PRE_SET_POWER),
1366 FLINTSTR(D_CTLOPS2, PMR_POST_SET_POWER),
1367 FLINTSTR(D_CTLOPS2, PMR_PPM_SET_POWER),
1368 FLINTSTR(0, PMR_PPM_ATTACH),
1369 FLINTSTR(0, PMR_PPM_DETACH),
1370 FLINTSTR(D_CTLOPS1, PMR_PPM_POWER_CHANGE_NOTIFY),
1371 FLINTSTR(D_CTLOPS1, PMR_REPORT_PMCAP),
1372 FLINTSTR(D_CTLOPS1, PMR_CHANGED_POWER),
1373 FLINTSTR(D_CTLOPS2, PMR_PPM_INIT_CHILD),
1374 FLINTSTR(D_CTLOPS2, PMR_PPM_UNINIT_CHILD),
1375 FLINTSTR(D_CTLOPS2, PMR_PPM_PRE_PROBE),
1376 FLINTSTR(D_CTLOPS2, PMR_PPM_POST_PROBE),
1377 FLINTSTR(D_CTLOPS2, PMR_PPM_PRE_ATTACH),
1378 FLINTSTR(D_CTLOPS2, PMR_PPM_POST_ATTACH),
1379 FLINTSTR(D_CTLOPS2, PMR_PPM_PRE_DETACH),
1380 FLINTSTR(D_CTLOPS2, PMR_PPM_POST_DETACH),
1381 FLINTSTR(D_CTLOPS1, PMR_PPM_UNMANAGE),
1382 FLINTSTR(D_CTLOPS2, PMR_PPM_PRE_RESUME),
1383 FLINTSTR(D_CTLOPS1, PMR_PPM_ALL_LOWEST),
1384 FLINTSTR(D_LOCKS, PMR_PPM_LOCK_POWER),
1385 FLINTSTR(D_LOCKS, PMR_PPM_UNLOCK_POWER),
1386 FLINTSTR(D_LOCKS, PMR_PPM_TRY_LOCK_POWER),
1387 FLINTSTR(D_LOCKS, PMR_PPM_POWER_LOCK_OWNER),
1388 FLINTSTR(D_CTLOPS1 | D_CTLOPS2, PMR_PPM_ENTER_SX),
1389 FLINTSTR(D_CTLOPS1 | D_CTLOPS2, PMR_UNKNOWN),
1390 };
1391
1392 for (ccp = cmds; ccp->ctlop != PMR_UNKNOWN; ccp++)
1393 if (ctlop == ccp->ctlop)
1394 break;
1395
1396 if (ccp->flags & mask)
1397 return (ccp->str);
1398 return (NULL);
1399 }
1400
1401 void
ppm_print_dc(ppm_dc_t * dc)1402 ppm_print_dc(ppm_dc_t *dc)
1403 {
1404 ppm_dc_t *d = dc;
1405
1406 PPMD(D_PPMDC, ("\nAdds ppm_dc: path(%s),\n cmd(%x), "
1407 "method(%x), ", d->path, d->cmd, d->method))
1408 if (d->method == PPMDC_KIO) {
1409 PPMD(D_PPMDC, ("kio.iowr(%x), kio.val(0x%X)",
1410 d->m_un.kio.iowr, d->m_un.kio.val))
1411 #ifdef sun4u
1412 } else if (d->method == PPMDC_I2CKIO) {
1413 PPMD(D_PPMDC, ("i2c.iowr(%x), i2c.val(0x%X), "
1414 "i2c.mask(0x%X)", d->m_un.i2c.iowr,
1415 d->m_un.i2c.val, d->m_un.i2c.mask))
1416 #endif
1417 } else if (d->method == PPMDC_VCORE) {
1418 PPMD(D_PPMDC, ("cpu: .iord(%x), .iowr(%x), .val(0x%X), "
1419 ".delay(0x%x)",
1420 d->m_un.cpu.iord, d->m_un.cpu.iowr, d->m_un.cpu.val,
1421 d->m_un.cpu.delay))
1422 } else if (d->method == PPMDC_CPUSPEEDKIO) {
1423 PPMD(D_PPMDC, ("cpu.iowr(%x), cpu.speeds(0x%X)",
1424 d->m_un.cpu.iowr, d->m_un.cpu.speeds))
1425 }
1426 PPMD(D_PPMDC, ("\n"))
1427 }
1428 #endif /* DEBUG */
1429