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