xref: /illumos-gate/usr/src/uts/common/io/ppm/ppm_subr.c (revision 201ceb75ab95f9bf1f42ea1dc9ab363b43ba47cf)
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
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
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
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 *
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 *
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
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 *
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 *
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
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
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 *
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 *
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 *
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
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
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
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
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
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
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
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
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
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
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 *
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 *
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 *
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
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