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.
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.
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]
22 * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
27 * ppm driver subroutines
34 #include <sys/sunldi.h>
35 #include <sys/ppmvar.h>
36 #include <sys/ppmio.h>
37 #include <sys/promif.h>
38 #include <sys/ddi_impldefs.h>
40 #include <sys/sunddi.h>
42 * Append address to the device path, if it is set. Routine
43 * ddi_pathname does not look for device address if the node is in
44 * DS_INITIALIZED state.
46 #define PPM_GET_PATHNAME(dip, path) \
47 (void) ddi_pathname((dip), (path)); \
48 if ((i_ddi_node_state((dip)) < DS_INITIALIZED) && \
49 (ddi_get_name_addr((dip)) != NULL)) { \
50 (void) strcat((path), "@"); \
51 (void) strcat((path), ddi_get_name_addr((dip)));\
54 int ppm_parse_dc(char **, ppm_dc_t
*);
55 int ppm_match_devs(char *, ppm_db_t
*);
56 ppm_db_t
*ppm_parse_pattern(struct ppm_db
**, char *);
57 int ppm_count_char(char *, char);
58 int ppm_stoi(char *, uint_t
*);
59 int ppm_convert(char *, uint_t
*);
60 void ppm_prop_free(struct ppm_cdata
**);
63 * lookup string property from configuration file ppm.conf
66 ppm_get_confdata(struct ppm_cdata
**cdp
, dev_info_t
*dip
)
69 char *str
= "ppm_get_confdata";
71 struct ppm_cdata
*cinfo
;
74 for (; (cinfo
= *cdp
) != NULL
; cdp
++) {
75 err
= ddi_prop_lookup_string_array(
76 DDI_DEV_T_ANY
, dip
, DDI_PROP_DONTPASS
,
77 cinfo
->name
, &cinfo
->strings
, &cinfo
->cnt
);
78 if (err
!= DDI_PROP_SUCCESS
) {
79 PPMD(D_ERROR
, ("%s: no %s found, err(%d)\n",
80 str
, cinfo
->name
, err
))
88 ppm_prop_free(struct ppm_cdata
**cdp
)
93 kmem_free((*cdp
)->name
,
94 strlen((*cdp
)->name
) + 1);
97 if ((*cdp
)->strings
) {
98 ddi_prop_free((*cdp
)->strings
);
99 (*cdp
)->strings
= NULL
;
107 * free ddi prop strings. Under error condition, free ppm_db_t lists as well.
110 ppm_attach_err(struct ppm_cdata
**cdp
, int err
)
116 if (err
!= DDI_SUCCESS
) {
117 for (domp
= ppm_domain_p
; domp
; domp
= domp
->next
) {
118 for (db
= domp
->conflist
; (tmp
= db
) != NULL
; ) {
120 kmem_free(tmp
->name
, strlen(tmp
->name
) + 1);
121 kmem_free(tmp
, sizeof (*tmp
));
123 domp
->conflist
= NULL
;
133 ppm_lookup_domain(char *dname
)
137 for (domp
= ppm_domain_p
; domp
; domp
= domp
->next
) {
138 if (strcmp(dname
, domp
->name
) == 0)
146 * for the purpose of optimizing we search for identical dc->path
147 * that has been opened per previous visit here. If search results
148 * in a hit, copy the device handle, else open the device.
151 ppm_lookup_hndl(int model
, ppm_dc_t
*key_dc
)
154 char *str
= "ppm_lookup_hndl";
156 char *key_path
= key_dc
->path
;
160 /* search domain by domain.model */
161 for (domp
= ppm_domain_p
; domp
; domp
= domp
->next
) {
162 if (domp
->model
== model
)
166 /* lookup hndl from same domain model */
167 if (domp
&& PPM_DOMAIN_UP(domp
)) {
168 for (dc
= domp
->dc
; dc
; dc
= dc
->next
) {
169 if ((strcmp(dc
->path
, key_path
) == 0) &&
171 PPMD(D_PPMDC
, ("%s: Hit(dc_path:%s) from SAME "
172 "domain %s.\n", str
, key_path
, domp
->name
))
179 /* otherwise, check other domains */
180 for (domp
= ppm_domain_p
;
181 domp
&& (domp
->model
!= model
); domp
= domp
->next
) {
182 if (PPM_DOMAIN_UP(domp
)) {
183 for (dc
= domp
->dc
; dc
; dc
= dc
->next
) {
184 if ((strcmp(dc
->path
, key_path
) == 0) &&
186 PPMD(D_PPMDC
, ("%s: Hit(dc_path:%s) "
188 str
, key_path
, domp
->name
))
196 PPMD(D_PPMDC
, ("%s: Miss(dc_path:%s)\n", str
, key_path
))
201 #define PPM_DOMAIN_PROP "ppm-domains"
202 #define PPM_DEV_PROP_SUFFIX "-devices"
203 #define PPM_MODEL_PROP_SUFFIX "-model"
204 #define PPM_PROPNAME_PROP_SUFFIX "-propname"
205 #define PPM_CTRL_PROP_SUFFIX "-control"
207 struct ppm_domit ppm_domit_data
[] = {
208 "SX", PPMD_SX
, 0, PPMD_ON
,
209 "CPU", PPMD_CPU
, PPMD_LOCK_ALL
, PPMD_ON
,
210 "FET", PPMD_FET
, PPMD_LOCK_ONE
, PPMD_ON
,
211 "PCI", PPMD_PCI
, PPMD_LOCK_ONE
, PPMD_ON
,
212 "PCI_PROP", PPMD_PCI_PROP
, PPMD_LOCK_ONE
, PPMD_ON
,
213 "LED", PPMD_LED
, 0, PPMD_ON
,
214 "PCIE", PPMD_PCIE
, PPMD_LOCK_ONE
, PPMD_ON
,
219 * store up platform dependent information provided by ppm.conf file
220 * into private data base
223 ppm_create_db(dev_info_t
*dip
)
226 char *str
= "ppm_create_db";
231 struct ppm_cdata domdata
; /* hold "ppm-domains" property */
232 struct ppm_cdata modeldata
; /* hold "domain_xy-model" property */
233 struct ppm_cdata propnamedata
; /* hold "domain_xy-propname" property */
234 struct ppm_cdata devdata
; /* hold "domain_xy-devices" property */
235 struct ppm_cdata dcdata
; /* hold "domain_xy-control" property */
236 struct ppm_cdata
*cdata
[2];
237 char **dom_namep
, **model_namep
, **dev_namep
, **dc_namep
;
238 struct ppm_domit
*domit_p
;
242 * get "ppm-domains" property
244 bzero(&domdata
, sizeof (domdata
));
245 domdata
.name
= kmem_zalloc(strlen(PPM_DOMAIN_PROP
) + 1, KM_SLEEP
);
246 (void) strcpy(domdata
.name
, PPM_DOMAIN_PROP
);
249 if (err
= ppm_get_confdata(cdata
, dip
)) {
250 PPMD(D_CREATEDB
, ("%s: failed to get prop \"%s\"!\n",
251 str
, PPM_DOMAIN_PROP
))
252 return (ppm_attach_err(cdata
, err
));
255 for (dom_namep
= domdata
.strings
; *dom_namep
; dom_namep
++) {
256 domp
= kmem_zalloc(sizeof (*domp
), KM_SLEEP
);
257 domp
->name
= kmem_zalloc(strlen(*dom_namep
) + 1, KM_SLEEP
);
258 (void) strcpy(domp
->name
, *dom_namep
);
259 mutex_init(&domp
->lock
, NULL
, MUTEX_DRIVER
, NULL
);
260 if (ppm_domain_p
== NULL
)
263 domp
->next
= ppm_domain_p
;
267 ppm_prop_free(cdata
);
270 * more per domain property strings in ppm.conf file tell us
271 * what the nature of domain, how to performe domain control, etc.
272 * Even the property names of those per domain properties are
273 * formed consisting its domain name string.
274 * Here we walk through our domain list, and fullfill the details.
276 for (domp
= ppm_domain_p
; domp
; domp
= domp
->next
) {
280 * get "domain_xy-model" property
282 bzero(&modeldata
, sizeof (modeldata
));
283 plen
= strlen(domp
->name
) + strlen(PPM_MODEL_PROP_SUFFIX
) + 1;
284 modeldata
.name
= kmem_zalloc(plen
, KM_SLEEP
);
285 (void) sprintf(modeldata
.name
, "%s%s",
286 domp
->name
, PPM_MODEL_PROP_SUFFIX
);
288 cdata
[0] = &modeldata
;
290 if (err
= ppm_get_confdata(cdata
, dip
)) {
291 PPMD(D_CREATEDB
, ("%s: Can't read property %s!\n",
292 str
, modeldata
.name
))
293 return (ppm_attach_err(cdata
, err
));
296 model_namep
= modeldata
.strings
;
297 for (domit_p
= ppm_domit_data
; domit_p
->name
; domit_p
++) {
298 if (strcmp(domit_p
->name
, *model_namep
) == 0) {
299 domp
->model
= domit_p
->model
;
300 domp
->dflags
= domit_p
->dflags
;
301 domp
->status
= domit_p
->status
;
307 ppm_prop_free(cdata
);
310 /* get "domain_xy-propname" property */
311 bzero(&propnamedata
, sizeof (propnamedata
));
312 plen
= strlen(domp
->name
) +
313 strlen(PPM_PROPNAME_PROP_SUFFIX
) + 1;
314 propnamedata
.name
= kmem_zalloc(plen
, KM_SLEEP
);
315 (void) sprintf(propnamedata
.name
, "%s%s",
316 domp
->name
, PPM_PROPNAME_PROP_SUFFIX
);
318 cdata
[0] = &propnamedata
;
320 if (ppm_get_confdata(cdata
, dip
) == DDI_PROP_SUCCESS
) {
321 domp
->propname
= kmem_zalloc(
322 (strlen(*propnamedata
.strings
) + 1), KM_SLEEP
);
323 (void) strcpy(domp
->propname
, *propnamedata
.strings
);
324 PPMD(D_CREATEDB
, ("%s: %s has property name: %s\n",
325 str
, domp
->name
, domp
->propname
))
327 ppm_prop_free(cdata
);
330 /* get "domain_xy-devices" property */
331 bzero(&devdata
, sizeof (devdata
));
332 plen
= strlen(domp
->name
) + strlen(PPM_DEV_PROP_SUFFIX
) + 1;
333 devdata
.name
= kmem_zalloc(plen
, KM_SLEEP
);
334 (void) sprintf(devdata
.name
, "%s%s",
335 domp
->name
, PPM_DEV_PROP_SUFFIX
);
339 if (err
= ppm_get_confdata(cdata
, dip
)) {
340 PPMD(D_CREATEDB
, ("%s: Can't read property %s!\n",
342 return (ppm_attach_err(cdata
, err
));
345 for (dev_namep
= devdata
.strings
; *dev_namep
; dev_namep
++) {
346 if (!ppm_parse_pattern(&db
, *dev_namep
))
347 return (ppm_attach_err(cdata
, err
));
348 db
->next
= domp
->conflist
;
350 PPMD(D_CREATEDB
, ("%s: %s add pattern: %s \n",
351 str
, devdata
.name
, db
->name
))
353 PPMD(D_CREATEDB
, ("\n"))
354 ppm_prop_free(cdata
);
357 /* get "domain_xy-control" property */
358 bzero(&dcdata
, sizeof (dcdata
));
359 plen
= strlen(domp
->name
) + strlen(PPM_CTRL_PROP_SUFFIX
) + 1;
360 dcdata
.name
= kmem_zalloc(plen
, KM_SLEEP
);
361 (void) sprintf(dcdata
.name
, "%s%s",
362 domp
->name
, PPM_CTRL_PROP_SUFFIX
);
366 if (ppm_get_confdata(cdata
, dip
) == DDI_PROP_SUCCESS
) {
367 for (dc_namep
= dcdata
.strings
; *dc_namep
;
369 dc
= kmem_zalloc(sizeof (*dc
), KM_SLEEP
);
372 err
= ppm_parse_dc(dc_namep
, domp
->dc
);
373 if (err
!= DDI_SUCCESS
)
374 return (ppm_attach_err(cdata
, err
));
377 ppm_prop_free(cdata
);
387 return (DDI_SUCCESS
);
392 * scan conf devices within each domain for a matching device name
395 ppm_lookup_dev(dev_info_t
*dip
)
397 char path
[MAXNAMELEN
];
401 char *devtype
= NULL
;
404 PPM_GET_PATHNAME(dip
, path
);
405 for (domp
= ppm_domain_p
; domp
; domp
= domp
->next
) {
406 if (PPM_DOMAIN_UP(domp
)) {
407 for (dbp
= domp
->conflist
; dbp
; dbp
= dbp
->next
) {
409 * allow claiming root without knowing
412 if (dip
== ddi_root_node() &&
413 strcmp(dbp
->name
, "/") == 0)
418 * Special rule to catch all CPU devices on x86.
420 if (domp
->model
== PPMD_CPU
&&
421 strcmp(dbp
->name
, "/") == 0 &&
422 ddi_prop_lookup_string(DDI_DEV_T_ANY
, dip
,
423 DDI_PROP_DONTPASS
, "device_type",
424 &devtype
) == DDI_SUCCESS
) {
425 if (strcmp(devtype
, "cpu") == 0) {
426 ddi_prop_free(devtype
);
429 ddi_prop_free(devtype
);
434 if (ppm_match_devs(path
, dbp
) == 0)
445 * check ppm.conf file domain device pathname syntax, if correct,
446 * create device match pattern.
447 * return 1 for good, -1 for bad.
450 ppm_parse_pattern(struct ppm_db
**dbpp
, char *dev_path
)
452 char path
[MAXNAMELEN
];
459 (void) strcpy(path
, dev_path
);
460 if ((wccnt
= ppm_count_char(path
, '*')) > 2)
463 for (i
= 0, cp
= path
, pos
= 0; i
< wccnt
; i
++, cp
++, pos
++) {
464 for (; *cp
; cp
++, pos
++)
468 PPMD(D_CREATEDB
, (" wildcard #%d, pos %d\n",
473 /* first '*', if exists, don't go beyond the string */
475 ASSERT(wcpos
[0] < strlen(path
));
477 /* second '*', if exists, better be the last character */
479 ASSERT(wcpos
[1] == (strlen(path
) - 1));
483 * first '*', if followed by any char, must be immediately
484 * followed by '@' and the rest better be bound by
485 * ['0-9', 'a-f', A-F'] until ended '0' or second '*''0'.
487 if ((wccnt
> 0) && (wcpos
[0] < (strlen(path
) - 1))) {
488 cp
= path
+ wcpos
[0] + 1;
492 if (!(((*(++cp
) > '0') && (*cp
< '9')) ||
493 ((*cp
> 'a') && (*cp
< 'f')) ||
494 ((*cp
> 'A') && (*cp
< 'F'))))
498 dbp
= kmem_zalloc(sizeof (struct ppm_db
), KM_SLEEP
);
499 dbp
->name
= kmem_zalloc((strlen(path
) + 1), KM_SLEEP
);
500 (void) strcpy(dbp
->name
, path
);
502 dbp
->wcpos
[0] = (wccnt
> 0) ? wcpos
[0] : -1;
503 dbp
->wcpos
[1] = (wccnt
== 2) ? wcpos
[1] : -1;
505 return (*dbpp
= dbp
);
510 * match given device "path" to domain device pathname
511 * pattern dbp->name that contains one or two '*' character(s).
513 * 1). If one wildcard terminates match pattern, need exact match
514 * up to (but exclude) the wildcard;
515 * 2). If one wildcard does not terminate match pattern, it is to
516 * match driver name (terminates with '@') and must be followed
517 * by exact match of rest of pattern;
518 * 3). If two wildcards, first is to match driver name as in 2),
519 * second is to match fcnid (terminates with '/' or '\0') and
520 * must the last char of pattern.
522 * return 0 if match, and
526 ppm_match_devs(char *dev_path
, ppm_db_t
*dbp
)
528 char path
[MAXNAMELEN
];
529 char *cp
; /* points into "path", real device pathname */
530 char *np
; /* points into "dbp->name", the pattern */
534 return (strcmp(dev_path
, dbp
->name
));
536 (void) strcpy(path
, dev_path
);
538 /* match upto the first '*' regardless */
539 if (strncmp(path
, dbp
->name
, dbp
->wcpos
[0]) != 0)
543 /* "<exact match>*" */
544 if (dbp
->name
[dbp
->wcpos
[0] + 1] == 0) {
545 cp
= path
+ dbp
->wcpos
[0];
546 while (*cp
&& (*cp
++ != '/'))
548 return ((*cp
== 0) ? 0 : -1);
553 cp
= path
+ dbp
->wcpos
[0] + 1;
554 while (*cp
&& *cp
!= '@')
557 np
= dbp
->name
+ dbp
->wcpos
[0] + 1;
559 /* if one wildcard, match the rest in the pattern */
561 return ((strcmp(cp
, np
) == 0) ? 0 : (-1));
564 /* must have exact match after first wildcard up to second */
565 ASSERT(dbp
->wccnt
== 2);
566 len
= dbp
->wcpos
[1] - dbp
->wcpos
[0] - 1;
567 if (strncmp(cp
, np
, len
) != 0)
570 /* second wildcard match terminates with '/' or '\0' */
571 /* but only termination with '\0' is a successful match */
573 while (*cp
&& (*cp
!= '/'))
575 return ((*cp
== 0) ? 0 : -1);
580 * By claiming a device, ppm gets involved in its power change
581 * process: handles additional issues prior and/or post its
584 * If 'dip' is a PCI device, this is the time to ask its parent
585 * what PCI bus speed it is running.
587 * returns 1 (claimed), 0 (not claimed)
590 ppm_claim_dev(dev_info_t
*dip
)
597 domp
= ppm_lookup_dev(dip
);
601 if (domp
&& PPMD_IS_PCI(domp
->model
) &&
602 ! (domp
->dflags
& (PPMD_PCI33MHZ
| PPMD_PCI66MHZ
))) {
603 pdip
= ddi_get_parent(dip
);
605 pciclk
= ddi_prop_get_int(DDI_DEV_T_ANY
, pdip
,
606 DDI_PROP_DONTPASS
, "clock-frequency", -1);
610 domp
->dflags
|= PPMD_PCI33MHZ
;
614 domp
->dflags
|= PPMD_PCI66MHZ
;
623 if (domp
&& (claimed
== -1))
628 char path
[MAXNAMELEN
];
629 PPMD(D_CLAIMDEV
, ("ppm_claim_dev: %s into domain %s\n",
630 ddi_pathname(dip
, path
), domp
->name
))
639 * add a device to the list of domain's owned devices (if it is not already
643 ppm_add_owned(dev_info_t
*dip
, ppm_domain_t
*domp
)
645 char path
[MAXNAMELEN
];
646 ppm_owned_t
*owned
, *new_owned
;
648 ASSERT(MUTEX_HELD(&domp
->lock
));
649 PPM_GET_PATHNAME(dip
, path
);
650 for (owned
= domp
->owned
; owned
; owned
= owned
->next
)
651 if (strcmp(path
, owned
->path
) == 0)
654 new_owned
= kmem_zalloc(sizeof (*new_owned
), KM_SLEEP
);
655 new_owned
->path
= kmem_zalloc(strlen(path
) + 1, KM_SLEEP
);
656 (void) strcpy(new_owned
->path
, path
);
657 new_owned
->next
= domp
->owned
;
658 domp
->owned
= new_owned
;
660 return (domp
->owned
);
664 * create/init a new ppm device and link into the domain
667 ppm_add_dev(dev_info_t
*dip
, ppm_domain_t
*domp
)
669 char path
[MAXNAMELEN
];
670 ppm_dev_t
*new = NULL
;
674 ASSERT(MUTEX_HELD(&domp
->lock
));
675 (void) ddi_pathname(dip
, path
);
677 * For devs which have exported "pm-components" we want to create
678 * a data structure for each component. When a driver chooses not
679 * to export the prop we treat its device as having a single
680 * component and build a structure for it anyway. All other ppm
681 * logic will act as if this device were always up and can thus
682 * make correct decisions about it in relation to other devices
685 for (cmpt
= PM_GET_PM_INFO(dip
) ? PM_NUMCMPTS(dip
) : 1; cmpt
--; ) {
686 new = kmem_zalloc(sizeof (*new), KM_SLEEP
);
687 new->path
= kmem_zalloc(strlen(path
) + 1, KM_SLEEP
);
688 (void) strcpy(new->path
, path
);
693 new->next
= domp
->devlist
;
696 ("ppm_add_dev: %s to domain %s: ppm_dev(0x%p)\n",
697 new->path
, domp
->name
, (void *)new))
702 * devi_pm_ppm_private should be set only after all
703 * ppm_dev s related to all components have been
704 * initialized and domain's pwr_cnt is incremented
707 PPM_SET_PRIVATE(dip
, new);
709 /* remember this device forever */
710 owned
= ppm_add_owned(dip
, domp
);
713 * Initializing flag is set for devices which have gone through
714 * PPM_PMR_INIT_CHILD ctlop. By this point, these devices have
715 * been added to ppm structures and could participate in pm
716 * decision making, so clear the initializing flag.
718 if (owned
->initializing
) {
719 owned
->initializing
= 0;
720 PPMD(D_ADDDEV
, ("ppm_add_dev: cleared initializing flag "
721 "for %s@%s\n", PM_NAME(dip
),
722 (PM_ADDR(dip
) == NULL
) ? "" : PM_ADDR(dip
)))
730 * returns an existing or newly created ppm device reference
733 ppm_get_dev(dev_info_t
*dip
, ppm_domain_t
*domp
)
737 mutex_enter(&domp
->lock
);
738 pdp
= PPM_GET_PRIVATE(dip
);
740 pdp
= ppm_add_dev(dip
, domp
);
741 mutex_exit(&domp
->lock
);
748 * scan a domain's device list and remove those with .dip
749 * matching the arg *dip; we need to scan the entire list
750 * for the case of devices with multiple components
753 ppm_rem_dev(dev_info_t
*dip
)
755 ppm_dev_t
*pdp
, **devpp
;
758 pdp
= PPM_GET_PRIVATE(dip
);
763 mutex_enter(&domp
->lock
);
764 for (devpp
= &domp
->devlist
; (pdp
= *devpp
) != NULL
; ) {
765 if (pdp
->dip
!= dip
) {
770 PPMD(D_REMDEV
, ("ppm_rem_dev: path \"%s\", ppm_dev 0x%p\n",
771 pdp
->path
, (void *)pdp
))
773 PPM_SET_PRIVATE(dip
, NULL
);
776 kmem_free(pdp
->path
, strlen(pdp
->path
) + 1);
777 kmem_free(pdp
, sizeof (*pdp
));
779 mutex_exit(&domp
->lock
);
783 * prepare kernel ioctl calls:
786 ppm_init_cb(dev_info_t
*dip
)
788 char *str
= "ppm_init_cb";
792 for (domp
= ppm_domain_p
; domp
!= NULL
; domp
= domp
->next
) {
793 for (dc
= domp
->dc
; dc
; dc
= dc
->next
) {
795 * Warning: This code is rather confusing.
797 * It intends to ensure that ppm_init_lyr() is only
798 * called ONCE for a device that may be associated
799 * with more than one domain control.
800 * So, what it does is first to check to see if
801 * there is a handle, and then if not it goes on
802 * to call the init_lyr() routine.
804 * The non-obvious thing is that the ppm_init_lyr()
805 * routine, in addition to opening the device
806 * associated with the dc (domain control) in
807 * question, has the side-effect of creating the
808 * handle for that dc as well.
810 if (ppm_lookup_hndl(domp
->model
, dc
) != NULL
)
813 if (ppm_init_lyr(dc
, dip
) != DDI_SUCCESS
) {
814 domp
->dflags
|= PPMD_OFFLINE
;
815 cmn_err(CE_WARN
, "%s: ppm domain %s will "
816 "be offline.", str
, domp
->name
);
825 * ppm_init_lyr - initializing layered ioctl
827 * DDI_SUCCESS - succeeded
828 * DDI_FAILURE - failed
832 ppm_init_lyr(ppm_dc_t
*dc
, dev_info_t
*dip
)
834 char *str
= "ppm_init_lyr";
838 ASSERT(dc
&& dc
->path
);
840 if (err
= ldi_ident_from_dip(dip
, &li
)) {
841 cmn_err(CE_WARN
, "%s: get ldi identifier "
842 "failed (err=%d)", str
, err
);
845 err
= ldi_open_by_name(dc
->path
, FWRITE
|FREAD
, kcred
, &(dc
->lh
), li
);
847 (void) ldi_ident_release(li
);
850 cmn_err(CE_WARN
, "Failed to open device(%s), rv(%d)",
855 return (DDI_SUCCESS
);
859 * lock, unlock, or trylock for one power mutex
862 ppm_lock_one(ppm_dev_t
*ppmd
, power_req_t
*reqp
, int *iresp
)
864 switch (reqp
->request_type
) {
865 case PMR_PPM_LOCK_POWER
:
866 pm_lock_power_single(ppmd
->dip
,
867 reqp
->req
.ppm_lock_power_req
.circp
);
870 case PMR_PPM_UNLOCK_POWER
:
871 pm_unlock_power_single(ppmd
->dip
,
872 reqp
->req
.ppm_unlock_power_req
.circ
);
875 case PMR_PPM_TRY_LOCK_POWER
:
876 *iresp
= pm_try_locking_power_single(ppmd
->dip
,
877 reqp
->req
.ppm_lock_power_req
.circp
);
884 * lock, unlock, or trylock for all power mutexes within a domain
887 ppm_lock_all(ppm_domain_t
*domp
, power_req_t
*reqp
, int *iresp
)
890 * To simplify the implementation we let all the devices
891 * in the domain be represented by a single device (dip).
892 * We use the first device in the domain's devlist. This
893 * is safe because we return with the domain lock held
894 * which prevents the list from changing.
896 if (reqp
->request_type
== PMR_PPM_LOCK_POWER
) {
897 if (!MUTEX_HELD(&domp
->lock
))
898 mutex_enter(&domp
->lock
);
900 ASSERT(domp
->devlist
!= NULL
);
901 pm_lock_power_single(domp
->devlist
->dip
,
902 reqp
->req
.ppm_lock_power_req
.circp
);
903 /* domain lock remains held */
905 } else if (reqp
->request_type
== PMR_PPM_UNLOCK_POWER
) {
906 ASSERT(MUTEX_HELD(&domp
->lock
));
907 ASSERT(domp
->devlist
!= NULL
);
908 pm_unlock_power_single(domp
->devlist
->dip
,
909 reqp
->req
.ppm_unlock_power_req
.circ
);
910 if (--domp
->refcnt
== 0)
911 mutex_exit(&domp
->lock
);
915 ASSERT(reqp
->request_type
== PMR_PPM_TRY_LOCK_POWER
);
916 if (!MUTEX_HELD(&domp
->lock
))
917 if (!mutex_tryenter(&domp
->lock
)) {
921 *iresp
= pm_try_locking_power_single(domp
->devlist
->dip
,
922 reqp
->req
.ppm_lock_power_req
.circp
);
926 mutex_exit(&domp
->lock
);
931 * return FALSE: if any detached device during its previous life exported
932 * the "no-involuntary-power-cycles" property and detached with its
933 * power level not at its lowest, or there is a device in the process
934 * of being installed/attached; if a PCI domain has devices that have not
935 * exported a property that it can tolerate clock off while bus is not
936 * quiescent; if a 66mhz PCI domain has devices that do not support stopping
937 * clock at D3; either one would count as a power holder.
938 * return TRUE: otherwise.
941 ppm_none_else_holds_power(ppm_domain_t
*domp
)
947 if (PPMD_IS_PCI(domp
->model
)) {
948 for (ppmd
= domp
->devlist
; ppmd
; ppmd
= ppmd
->next
) {
949 if ((domp
->model
== PPMD_PCI_PROP
) &&
950 !(ppmd
->flags
& PPMDEV_PCI_PROP_CLKPM
))
952 if ((domp
->dflags
& PPMD_PCI66MHZ
) &&
953 !(ppmd
->flags
& PPMDEV_PCI66_D2
))
958 for (owned
= domp
->owned
; owned
; owned
= owned
->next
)
959 if (pm_noinvol_detached(owned
->path
) || owned
->initializing
)
966 * return the number of char 'c' occurrences in string s
969 ppm_count_char(char *s
, char c
)
985 * extract and convert a substring from input string "ss" in form of
986 * "name=value" into an hex or decimal integer
991 ppm_stoi(char *ss
, uint_t
*val
)
994 int hex_
= 0, base
= D_BASE
;
997 if ((cp
= strchr(ss
, '=')) == NULL
)
998 return (*val
= (uint_t
)-1);
1001 if ((*cp
== '0') && (*++cp
== 'x')) {
1007 for (digit
= 0; *cp
; cp
++) {
1008 if (hex_
&& ((*cp
>= 'A') && (*cp
<= 'F')))
1009 digit
= (digit
* base
) + ((*cp
- 'A') + D_BASE
);
1010 else if (hex_
&& ((*cp
>= 'a') && (*cp
<= 'f')))
1011 digit
= (digit
* base
) + ((*cp
- 'a') + D_BASE
);
1013 digit
= (digit
* base
) + (*cp
- '0');
1016 return (*val
= digit
);
1020 * ppm_convert - convert a #define symbol to its integer value,
1021 * only the #defines for ppm_dc.cmd and ppm_dc.method fields in
1022 * ppmvar.h file are recognized.
1024 struct ppm_confdefs
{
1027 } ppm_confdefs_table
[] = {
1028 "ENTER_S3", PPMDC_ENTER_S3
,
1029 "EXIT_S3", PPMDC_EXIT_S3
,
1030 "CPU_NEXT", PPMDC_CPU_NEXT
,
1031 "PRE_CHNG", PPMDC_PRE_CHNG
,
1032 "CPU_GO", PPMDC_CPU_GO
,
1033 "POST_CHNG", PPMDC_POST_CHNG
,
1034 "FET_ON", PPMDC_FET_ON
,
1035 "FET_OFF", PPMDC_FET_OFF
,
1036 "CLK_OFF", PPMDC_CLK_OFF
,
1037 "CLK_ON", PPMDC_CLK_ON
,
1038 "LED_ON", PPMDC_LED_ON
,
1039 "LED_OFF", PPMDC_LED_OFF
,
1041 "VCORE", PPMDC_VCORE
,
1043 "I2CKIO", PPMDC_I2CKIO
,
1045 "CPUSPEEDKIO", PPMDC_CPUSPEEDKIO
,
1046 "PRE_PWR_OFF", PPMDC_PRE_PWR_OFF
,
1047 "PRE_PWR_ON", PPMDC_PRE_PWR_ON
,
1048 "POST_PWR_ON", PPMDC_POST_PWR_ON
,
1049 "PWR_OFF", PPMDC_PWR_OFF
,
1050 "PWR_ON", PPMDC_PWR_ON
,
1051 "RESET_OFF", PPMDC_RESET_OFF
,
1052 "RESET_ON", PPMDC_RESET_ON
,
1058 * convert a #define'd symbol to its integer value where
1059 * input "symbol" is expected to be in form of "SYMBOL=value"
1062 ppm_convert(char *symbol
, uint_t
*val
)
1065 struct ppm_confdefs
*pcfp
;
1067 if ((s
= strchr(symbol
, '=')) == NULL
) {
1068 cmn_err(CE_WARN
, "ppm_convert: token \"%s\" syntax error in "
1069 "ppm.conf file", symbol
);
1070 return (*val
= (uint_t
)-1);
1074 for (pcfp
= ppm_confdefs_table
; (pcfp
->sym
!= NULL
); pcfp
++) {
1075 if (strcmp(s
, pcfp
->sym
) == 0)
1076 return (*val
= pcfp
->val
);
1079 cmn_err(CE_WARN
, "ppm_convert: Unrecognizable token \"%s\" "
1080 "in ppm.conf file", symbol
);
1081 return (*val
= (uint_t
)-1);
1086 * parse a domain control property string into data structure struct ppm_dc
1089 ppm_parse_dc(char **dc_namep
, ppm_dc_t
*dc
)
1091 char *str
= "ppm_parse_dc";
1094 char **dclist
; /* list of ppm_dc_t fields */
1095 int count
; /* the # of '=' indicates the # of items */
1096 size_t len
; /* length of line being parsed */
1101 len
= strlen(*dc_namep
);
1102 line
= kmem_alloc(len
+ 1, KM_SLEEP
);
1103 (void) strcpy(line
, *dc_namep
);
1105 count
= ppm_count_char(line
, '=');
1106 ASSERT((count
- ppm_count_char(line
, ' ')) == 1);
1109 kmem_zalloc((sizeof (char *) * (count
+ 1)), KM_SLEEP
);
1110 for (i
= 0, f
= b
= line
, done
= B_FALSE
; !done
; i
++, f
= ++b
) {
1111 while (*b
!= ' ' && *b
!= 0)
1120 for (i
= 0; i
< count
; i
++) {
1121 if (strstr(dclist
[i
], "cmd=")) {
1122 err
= ppm_convert(dclist
[i
], &dc
->cmd
);
1127 if ((f
= strstr(dclist
[i
], "path=")) != NULL
) {
1128 f
+= strlen("path=");
1129 dc
->path
= kmem_zalloc((strlen(f
) + 1), KM_SLEEP
);
1130 (void) strcpy(dc
->path
, f
);
1133 if (strstr(dclist
[i
], "method=")) {
1134 err
= ppm_convert(dclist
[i
], &dc
->method
);
1139 if (strstr(dclist
[i
], "iowr=")) {
1140 (void) ppm_stoi(dclist
[i
], &dc
->m_un
.kio
.iowr
);
1143 if (strstr(dclist
[i
], "iord=")) {
1144 (void) ppm_stoi(dclist
[i
], &dc
->m_un
.kio
.iord
);
1147 if (strstr(dclist
[i
], "val=")) {
1148 (void) ppm_stoi(dclist
[i
], &dc
->m_un
.kio
.val
);
1151 if (strstr(dclist
[i
], "speeds=")) {
1152 ASSERT(dc
->method
== PPMDC_CPUSPEEDKIO
);
1153 (void) ppm_stoi(dclist
[i
], &dc
->m_un
.cpu
.speeds
);
1157 if (strstr(dclist
[i
], "mask=")) {
1158 (void) ppm_stoi(dclist
[i
], &dc
->m_un
.i2c
.mask
);
1162 /* This must be before the if statement for delay */
1163 if (strstr(dclist
[i
], "post_delay=")) {
1165 ASSERT(dc
->method
== PPMDC_KIO
||
1166 dc
->method
== PPMDC_I2CKIO
);
1168 ASSERT(dc
->method
== PPMDC_KIO
);
1171 * all delays are uint_t type instead of clock_t.
1172 * If the delay is too long, it might get truncated.
1173 * But, we don't expect delay to be too long.
1175 switch (dc
->method
) {
1177 (void) ppm_stoi(dclist
[i
],
1178 &dc
->m_un
.kio
.post_delay
);
1183 (void) ppm_stoi(dclist
[i
],
1184 &dc
->m_un
.i2c
.post_delay
);
1193 if (strstr(dclist
[i
], "delay=")) {
1195 ASSERT(dc
->method
== PPMDC_VCORE
||
1196 dc
->method
== PPMDC_KIO
||
1197 dc
->method
== PPMDC_I2CKIO
);
1199 ASSERT(dc
->method
== PPMDC_VCORE
||
1200 dc
->method
== PPMDC_KIO
);
1204 * all delays are uint_t type instead of clock_t.
1205 * If the delay is too long, it might get truncated.
1206 * But, we don't expect delay to be too long.
1209 switch (dc
->method
) {
1211 (void) ppm_stoi(dclist
[i
], &dc
->m_un
.kio
.delay
);
1216 (void) ppm_stoi(dclist
[i
], &dc
->m_un
.i2c
.delay
);
1221 (void) ppm_stoi(dclist
[i
], &dc
->m_un
.cpu
.delay
);
1230 /* we encounted unrecognized field, flag error */
1231 cmn_err(CE_WARN
, "%s: Unrecognized token \"%s\" in ppm.conf "
1232 "file!", str
, dclist
[i
]);
1236 kmem_free(dclist
, sizeof (char *) * (count
+ 1));
1237 kmem_free(line
, len
+ 1);
1239 return (DDI_SUCCESS
);
1244 * search for domain control handle for a claimed device coupled with a
1245 * domain control command. NULL device may indicate LED domain.
1248 ppm_lookup_dc(ppm_domain_t
*domp
, int cmd
)
1251 char *str
= "ppm_lookup_dc";
1256 * For convenience, we accept 'domp' as NULL for searching
1257 * LED domain control operation.
1259 if ((cmd
== PPMDC_LED_OFF
) || (cmd
== PPMDC_LED_ON
)) {
1260 for (domp
= ppm_domain_p
; domp
; domp
= domp
->next
)
1261 if (domp
->model
== PPMD_LED
)
1263 if (!domp
|| !domp
->dc
|| !domp
->dc
->lh
|| !domp
->dc
->next
) {
1264 PPMD(D_LED
, ("\tinsufficient led domain control "
1268 if (cmd
== domp
->dc
->cmd
)
1271 return (domp
->dc
->next
);
1276 * for the rest of ppm domains, lookup ppm_dc starting from domp
1278 ASSERT(domp
!= NULL
);
1280 case PPMDC_CPU_NEXT
:
1281 case PPMDC_PRE_CHNG
:
1283 case PPMDC_POST_CHNG
:
1288 case PPMDC_PRE_PWR_OFF
:
1289 case PPMDC_PRE_PWR_ON
:
1290 case PPMDC_POST_PWR_ON
:
1293 case PPMDC_RESET_OFF
:
1294 case PPMDC_RESET_ON
:
1295 case PPMDC_ENTER_S3
:
1299 PPMD(D_PPMDC
, ("%s: cmd(%d) unrecognized\n", str
, cmd
))
1303 for (dc
= domp
->dc
; dc
; dc
= dc
->next
) {
1304 if (dc
->cmd
== cmd
) {
1312 #include <sys/esunddi.h>
1315 ppm_get_domain_by_dev(const char *p
)
1320 boolean_t found
= B_FALSE
;
1322 if ((dip
= e_ddi_hold_devi_by_path((char *)p
, 0)) == NULL
)
1325 for (domp
= ppm_domain_p
; domp
; domp
= domp
->next
) {
1326 for (pdev
= domp
->devlist
; pdev
; pdev
= pdev
->next
) {
1327 if (pdev
->dip
== dip
) {
1335 ddi_release_devi(dip
);
1341 #define FLINTSTR(flags, sym) { flags, sym, #sym }
1342 #define PMR_UNKNOWN -1
1344 * convert a ctlop integer to a char string. this helps printing
1345 * meaningful info when cltops are received from the pm framework.
1346 * since some ctlops are so frequent, we use mask to limit output:
1347 * a valid string is returned when ctlop is found and when
1348 * (cmd.flags & mask) is true; otherwise NULL is returned.
1351 ppm_get_ctlstr(int ctlop
, uint_t mask
)
1359 struct ctlop_cmd
*ccp
;
1360 static struct ctlop_cmd cmds
[] = {
1361 FLINTSTR(D_SETPWR
, PMR_SET_POWER
),
1362 FLINTSTR(D_CTLOPS2
, PMR_SUSPEND
),
1363 FLINTSTR(D_CTLOPS2
, PMR_RESUME
),
1364 FLINTSTR(D_CTLOPS2
, PMR_PRE_SET_POWER
),
1365 FLINTSTR(D_CTLOPS2
, PMR_POST_SET_POWER
),
1366 FLINTSTR(D_CTLOPS2
, PMR_PPM_SET_POWER
),
1367 FLINTSTR(0, PMR_PPM_ATTACH
),
1368 FLINTSTR(0, PMR_PPM_DETACH
),
1369 FLINTSTR(D_CTLOPS1
, PMR_PPM_POWER_CHANGE_NOTIFY
),
1370 FLINTSTR(D_CTLOPS1
, PMR_REPORT_PMCAP
),
1371 FLINTSTR(D_CTLOPS1
, PMR_CHANGED_POWER
),
1372 FLINTSTR(D_CTLOPS2
, PMR_PPM_INIT_CHILD
),
1373 FLINTSTR(D_CTLOPS2
, PMR_PPM_UNINIT_CHILD
),
1374 FLINTSTR(D_CTLOPS2
, PMR_PPM_PRE_PROBE
),
1375 FLINTSTR(D_CTLOPS2
, PMR_PPM_POST_PROBE
),
1376 FLINTSTR(D_CTLOPS2
, PMR_PPM_PRE_ATTACH
),
1377 FLINTSTR(D_CTLOPS2
, PMR_PPM_POST_ATTACH
),
1378 FLINTSTR(D_CTLOPS2
, PMR_PPM_PRE_DETACH
),
1379 FLINTSTR(D_CTLOPS2
, PMR_PPM_POST_DETACH
),
1380 FLINTSTR(D_CTLOPS1
, PMR_PPM_UNMANAGE
),
1381 FLINTSTR(D_CTLOPS2
, PMR_PPM_PRE_RESUME
),
1382 FLINTSTR(D_CTLOPS1
, PMR_PPM_ALL_LOWEST
),
1383 FLINTSTR(D_LOCKS
, PMR_PPM_LOCK_POWER
),
1384 FLINTSTR(D_LOCKS
, PMR_PPM_UNLOCK_POWER
),
1385 FLINTSTR(D_LOCKS
, PMR_PPM_TRY_LOCK_POWER
),
1386 FLINTSTR(D_LOCKS
, PMR_PPM_POWER_LOCK_OWNER
),
1387 FLINTSTR(D_CTLOPS1
| D_CTLOPS2
, PMR_PPM_ENTER_SX
),
1388 FLINTSTR(D_CTLOPS1
| D_CTLOPS2
, PMR_UNKNOWN
),
1391 for (ccp
= cmds
; ccp
->ctlop
!= PMR_UNKNOWN
; ccp
++)
1392 if (ctlop
== ccp
->ctlop
)
1395 if (ccp
->flags
& mask
)
1401 ppm_print_dc(ppm_dc_t
*dc
)
1405 PPMD(D_PPMDC
, ("\nAdds ppm_dc: path(%s),\n cmd(%x), "
1406 "method(%x), ", d
->path
, d
->cmd
, d
->method
))
1407 if (d
->method
== PPMDC_KIO
) {
1408 PPMD(D_PPMDC
, ("kio.iowr(%x), kio.val(0x%X)",
1409 d
->m_un
.kio
.iowr
, d
->m_un
.kio
.val
))
1411 } else if (d
->method
== PPMDC_I2CKIO
) {
1412 PPMD(D_PPMDC
, ("i2c.iowr(%x), i2c.val(0x%X), "
1413 "i2c.mask(0x%X)", d
->m_un
.i2c
.iowr
,
1414 d
->m_un
.i2c
.val
, d
->m_un
.i2c
.mask
))
1416 } else if (d
->method
== PPMDC_VCORE
) {
1417 PPMD(D_PPMDC
, ("cpu: .iord(%x), .iowr(%x), .val(0x%X), "
1419 d
->m_un
.cpu
.iord
, d
->m_un
.cpu
.iowr
, d
->m_un
.cpu
.val
,
1421 } else if (d
->method
== PPMDC_CPUSPEEDKIO
) {
1422 PPMD(D_PPMDC
, ("cpu.iowr(%x), cpu.speeds(0x%X)",
1423 d
->m_un
.cpu
.iowr
, d
->m_un
.cpu
.speeds
))
1425 PPMD(D_PPMDC
, ("\n"))