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