1 /*
   2  * CDDL HEADER START
   3  *
   4  * The contents of this file are subject to the terms of the
   5  * Common Development and Distribution License (the "License").
   6  * You may not use this file except in compliance with the License.
   7  *
   8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
   9  * or http://www.opensolaris.org/os/licensing.
  10  * See the License for the specific language governing permissions
  11  * and limitations under the License.
  12  *
  13  * When distributing Covered Code, include this CDDL HEADER in each
  14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
  15  * If applicable, add the following below this CDDL HEADER, with the
  16  * fields enclosed by brackets "[]" replaced with your own identifying
  17  * information: Portions Copyright [yyyy] [name of copyright owner]
  18  *
  19  * CDDL HEADER END
  20  */
  21 /*
  22  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
  23  * Use is subject to license terms.
  24  */
  25 
  26 /*
  27  * ppm driver subroutines
  28  */
  29 
  30 #include <sys/open.h>
  31 #include <sys/file.h>
  32 #include <sys/conf.h>
  33 #include <sys/epm.h>
  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>
  39 #include <sys/ddi.h>
  40 #include <sys/sunddi.h>
  41 /*
  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.
  45  */
  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)));\
  52         }
  53 
  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 **);
  61 
  62 /*
  63  * lookup string property from configuration file ppm.conf
  64  */
  65 static int
  66 ppm_get_confdata(struct ppm_cdata **cdp, dev_info_t *dip)
  67 {
  68 #ifdef  DEBUG
  69         char *str = "ppm_get_confdata";
  70 #endif
  71         struct ppm_cdata *cinfo;
  72         int err;
  73 
  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))
  81                         break;
  82                 }
  83         }
  84         return (err);
  85 }
  86 
  87 void
  88 ppm_prop_free(struct ppm_cdata **cdp)
  89 {
  90         if (cdp) {
  91                 for (; *cdp; cdp++) {
  92                         if ((*cdp)->name) {
  93                                 kmem_free((*cdp)->name,
  94                                     strlen((*cdp)->name) + 1);
  95                                 (*cdp)->name = NULL;
  96                         }
  97                         if ((*cdp)->strings) {
  98                                 ddi_prop_free((*cdp)->strings);
  99                                 (*cdp)->strings = NULL;
 100                         }
 101                 }
 102         }
 103 }
 104 
 105 
 106 /*
 107  * free ddi prop strings. Under error condition, free ppm_db_t lists as well.
 108  */
 109 static int
 110 ppm_attach_err(struct ppm_cdata **cdp, int err)
 111 {
 112         ppm_domain_t *domp;
 113         ppm_db_t *db, *tmp;
 114 
 115         ppm_prop_free(cdp);
 116         if (err != DDI_SUCCESS) {
 117                 for (domp = ppm_domain_p; domp; domp = domp->next) {
 118                         for (db = domp->conflist; (tmp = db) != NULL; ) {
 119                                 db = db->next;
 120                                 kmem_free(tmp->name, strlen(tmp->name) + 1);
 121                                 kmem_free(tmp, sizeof (*tmp));
 122                         }
 123                         domp->conflist = NULL;
 124                 }
 125                 err = DDI_FAILURE;
 126         }
 127 
 128         return (err);
 129 }
 130 
 131 
 132 ppm_domain_t *
 133 ppm_lookup_domain(char *dname)
 134 {
 135         ppm_domain_t    *domp;
 136 
 137         for (domp = ppm_domain_p; domp; domp = domp->next) {
 138                 if (strcmp(dname, domp->name) == 0)
 139                         break;
 140         }
 141         return (domp);
 142 }
 143 
 144 
 145 /*
 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.
 149  */
 150 ppm_dc_t *
 151 ppm_lookup_hndl(int model, ppm_dc_t *key_dc)
 152 {
 153 #ifdef  DEBUG
 154         char *str = "ppm_lookup_hndl";
 155 #endif
 156         char *key_path = key_dc->path;
 157         ppm_domain_t *domp;
 158         ppm_dc_t *dc;
 159 
 160         /* search domain by domain.model */
 161         for (domp = ppm_domain_p; domp; domp = domp->next) {
 162                 if (domp->model == model)
 163                         break;
 164         }
 165 
 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) &&
 170                             (dc->lh != NULL)) {
 171                                 PPMD(D_PPMDC, ("%s: Hit(dc_path:%s) from SAME "
 172                                     "domain %s.\n", str, key_path, domp->name))
 173                                 key_dc->lh = dc->lh;
 174                                 return (key_dc);
 175                         }
 176                 }
 177         }
 178 
 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) &&
 185                                     (dc->lh != NULL)) {
 186                                         PPMD(D_PPMDC, ("%s: Hit(dc_path:%s) "
 187                                             "from domain %s\n",
 188                                             str, key_path, domp->name))
 189                                         key_dc->lh = dc->lh;
 190                                         return (key_dc);
 191                                 }
 192                         }
 193                 }
 194         }
 195 
 196         PPMD(D_PPMDC, ("%s: Miss(dc_path:%s)\n", str, key_path))
 197         return (NULL);
 198 }
 199 
 200 
 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"
 206 
 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 },
 215         { NULL }
 216 };
 217 
 218 /*
 219  * store up platform dependent information provided by ppm.conf file
 220  * into private data base
 221  */
 222 int
 223 ppm_create_db(dev_info_t *dip)
 224 {
 225 #ifdef  DEBUG
 226         char *str = "ppm_create_db";
 227 #endif
 228         ppm_domain_t *domp;
 229         ppm_db_t *db;
 230         ppm_dc_t *dc;
 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;
 239         int err;
 240 
 241         /*
 242          * get "ppm-domains" property
 243          */
 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);
 247         cdata[0] = &domdata;
 248         cdata[1] = NULL;
 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));
 253         }
 254 
 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)
 261                         ppm_domain_p = domp;
 262                 else {
 263                         domp->next = ppm_domain_p;
 264                         ppm_domain_p = domp;
 265                 }
 266         }
 267         ppm_prop_free(cdata);
 268 
 269         /*
 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.
 275          */
 276         for (domp = ppm_domain_p; domp; domp = domp->next) {
 277                 size_t  plen;
 278 
 279                 /*
 280                  * get "domain_xy-model" property
 281                  */
 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);
 287 
 288                 cdata[0] = &modeldata;
 289                 cdata[1] = NULL;
 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));
 294                 }
 295 
 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;
 302                                 break;
 303                         }
 304                 }
 305                 ASSERT(domit_p);
 306 
 307                 ppm_prop_free(cdata);
 308 
 309 
 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);
 317 
 318                 cdata[0] = &propnamedata;
 319                 cdata[1] = NULL;
 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))
 326                 }
 327                 ppm_prop_free(cdata);
 328 
 329 
 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);
 336 
 337                 cdata[0] = &devdata;
 338                 cdata[1] = NULL;
 339                 if (err = ppm_get_confdata(cdata, dip)) {
 340                         PPMD(D_CREATEDB, ("%s: Can't read property %s!\n",
 341                             str, devdata.name))
 342                         return (ppm_attach_err(cdata, err));
 343                 }
 344 
 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;
 349                         domp->conflist = db;
 350                         PPMD(D_CREATEDB, ("%s: %s add pattern: %s \n",
 351                             str, devdata.name, db->name))
 352                 }
 353                 PPMD(D_CREATEDB, ("\n"))
 354                 ppm_prop_free(cdata);
 355 
 356 
 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);
 363 
 364                 cdata[0] = &dcdata;
 365                 cdata[1] = NULL;
 366                 if (ppm_get_confdata(cdata, dip) == DDI_PROP_SUCCESS) {
 367                         for (dc_namep = dcdata.strings; *dc_namep;
 368                             dc_namep++) {
 369                                 dc = kmem_zalloc(sizeof (*dc), KM_SLEEP);
 370                                 dc->next = domp->dc;
 371                                 domp->dc = dc;
 372                                 err = ppm_parse_dc(dc_namep, domp->dc);
 373                                 if (err != DDI_SUCCESS)
 374                                         return (ppm_attach_err(cdata, err));
 375                         }
 376                 }
 377                 ppm_prop_free(cdata);
 378 #ifdef  DEBUG
 379                 dc = domp->dc;
 380                 while (dc) {
 381                         ppm_print_dc(dc);
 382                         dc = dc->next;
 383                 }
 384 #endif
 385         }
 386 
 387         return (DDI_SUCCESS);
 388 }
 389 
 390 
 391 /*
 392  * scan conf devices within each domain for a matching device name
 393  */
 394 ppm_domain_t *
 395 ppm_lookup_dev(dev_info_t *dip)
 396 {
 397         char path[MAXNAMELEN];
 398         ppm_domain_t *domp;
 399         ppm_db_t *dbp;
 400 #ifdef  __x86
 401         char *devtype = NULL;
 402 #endif  /* __x86 */
 403 
 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) {
 408                                 /*
 409                                  * allow claiming root without knowing
 410                                  * its full name
 411                                  */
 412                                 if (dip == ddi_root_node() &&
 413                                     strcmp(dbp->name, "/") == 0)
 414                                         return (domp);
 415 
 416 #ifdef  __x86
 417                                 /*
 418                                  * Special rule to catch all CPU devices on x86.
 419                                  */
 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);
 427                                                 return (domp);
 428                                         } else {
 429                                                 ddi_prop_free(devtype);
 430                                         }
 431                                 }
 432 #endif  /* __x86 */
 433 
 434                                 if (ppm_match_devs(path, dbp) == 0)
 435                                         return (domp);
 436                         }
 437                 }
 438         }
 439 
 440         return (NULL);
 441 }
 442 
 443 
 444 /*
 445  * check ppm.conf file domain device pathname syntax, if correct,
 446  * create device match pattern.
 447  * return 1 for good, -1 for bad.
 448  */
 449 ppm_db_t *
 450 ppm_parse_pattern(struct ppm_db **dbpp, char *dev_path)
 451 {
 452         char path[MAXNAMELEN];
 453         int     wccnt, i;
 454         int     wcpos[2];
 455         int     pos;
 456         char    *cp;
 457         ppm_db_t *dbp;
 458 
 459         (void) strcpy(path, dev_path);
 460         if ((wccnt = ppm_count_char(path, '*')) > 2)
 461                 return (NULL);
 462 
 463         for (i = 0, cp = path, pos = 0; i < wccnt; i++, cp++, pos++) {
 464                 for (; *cp; cp++, pos++)
 465                         if (*cp == '*')
 466                                 break;
 467                 wcpos[i] = pos;
 468                 PPMD(D_CREATEDB, ("    wildcard #%d, pos %d\n",
 469                     (i + 1), wcpos[i]))
 470         }
 471 
 472 #ifdef  DEBUG
 473         /* first '*', if exists, don't go beyond the string */
 474         if (wccnt > 0)
 475                 ASSERT(wcpos[0] < strlen(path));
 476 
 477         /* second '*', if exists, better be the last character */
 478         if (wccnt == 2)
 479                 ASSERT(wcpos[1] == (strlen(path) - 1));
 480 #endif
 481 
 482         /*
 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'.
 486          */
 487         if ((wccnt > 0) && (wcpos[0] < (strlen(path) - 1))) {
 488                 cp = path + wcpos[0] + 1;
 489                 if (*cp != '@')
 490                         return (NULL);
 491 
 492                 if (!(((*(++cp) > '0') && (*cp < '9')) ||
 493                     ((*cp > 'a') && (*cp < 'f')) ||
 494                     ((*cp > 'A') && (*cp < 'F'))))
 495                         return (NULL);
 496         }
 497 
 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);
 501         dbp->wccnt = wccnt;
 502         dbp->wcpos[0] = (wccnt > 0) ? wcpos[0] : -1;
 503         dbp->wcpos[1] = (wccnt == 2) ? wcpos[1] : -1;
 504 
 505         return (*dbpp = dbp);
 506 }
 507 
 508 
 509 /*
 510  * match given device "path" to domain device pathname
 511  * pattern dbp->name that contains one or two '*' character(s).
 512  * Matching policy:
 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.
 521  *
 522  * return  0  if match, and
 523  *        non 0  if mismatch
 524  */
 525 int
 526 ppm_match_devs(char *dev_path, ppm_db_t *dbp)
 527 {
 528         char path[MAXNAMELEN];
 529         char *cp;       /* points into "path", real device pathname */
 530         char *np;       /* points into "dbp->name", the pattern */
 531         int  len;
 532 
 533         if (dbp->wccnt == 0)
 534                 return (strcmp(dev_path, dbp->name));
 535 
 536         (void) strcpy(path, dev_path);
 537 
 538         /* match upto the first '*' regardless */
 539         if (strncmp(path, dbp->name, dbp->wcpos[0]) != 0)
 540                 return (-1);
 541 
 542 
 543         /* "<exact match>*"       */
 544         if (dbp->name[dbp->wcpos[0] + 1] == 0) {
 545                 cp = path + dbp->wcpos[0];
 546                 while (*cp && (*cp++ != '/'))
 547                         ;
 548                 return ((*cp == 0) ? 0 : -1);
 549         }
 550 
 551 
 552         /* locate '@'   */
 553         cp = path + dbp->wcpos[0] + 1;
 554         while (*cp && *cp != '@')
 555                 cp++;
 556 
 557         np = dbp->name + dbp->wcpos[0] + 1;
 558 
 559         /* if one wildcard, match the rest in the pattern */
 560         if (dbp->wccnt == 1)
 561                 return ((strcmp(cp, np) == 0) ? 0 : (-1));
 562 
 563 
 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)
 568                 return (-1);
 569 
 570         /* second wildcard match terminates with '/' or '\0' */
 571         /* but only termination with '\0' is a successful match */
 572         cp += len;
 573         while (*cp && (*cp != '/'))
 574                 cp++;
 575         return ((*cp == 0) ? 0 : -1);
 576 }
 577 
 578 
 579 /*
 580  * By claiming a device, ppm gets involved in its power change
 581  * process: handles additional issues prior and/or post its
 582  * power(9e) call.
 583  *
 584  * If 'dip' is a PCI device, this is the time to ask its parent
 585  * what PCI bus speed it is running.
 586  *
 587  * returns 1 (claimed), 0 (not claimed)
 588  */
 589 int
 590 ppm_claim_dev(dev_info_t *dip)
 591 {
 592         ppm_domain_t    *domp;
 593         dev_info_t      *pdip;
 594         uint_t          pciclk;
 595         int             claimed = -1;
 596 
 597         domp = ppm_lookup_dev(dip);
 598         if (!domp)
 599                 claimed = 0;
 600 
 601         if (domp && PPMD_IS_PCI(domp->model) &&
 602             ! (domp->dflags & (PPMD_PCI33MHZ | PPMD_PCI66MHZ))) {
 603                 pdip = ddi_get_parent(dip);
 604                 ASSERT(pdip);
 605                 pciclk = ddi_prop_get_int(DDI_DEV_T_ANY, pdip,
 606                     DDI_PROP_DONTPASS, "clock-frequency", -1);
 607 
 608                 switch (pciclk) {
 609                 case 33000000:
 610                         domp->dflags |= PPMD_PCI33MHZ;
 611                         claimed = 1;
 612                         break;
 613                 case 66000000:
 614                         domp->dflags |= PPMD_PCI66MHZ;
 615                         claimed = 1;
 616                         break;
 617                 default:
 618                         claimed = 0;
 619                         break;
 620                 }
 621         }
 622 
 623         if (domp && (claimed == -1))
 624                 claimed = 1;
 625 
 626 #ifdef DEBUG
 627         if (claimed) {
 628                 char path[MAXNAMELEN];
 629                 PPMD(D_CLAIMDEV, ("ppm_claim_dev: %s into domain %s\n",
 630                     ddi_pathname(dip, path), domp->name))
 631         }
 632 
 633 #endif
 634 
 635         return (claimed);
 636 }
 637 
 638 /*
 639  * add a device to the list of domain's owned devices (if it is not already
 640  * on the list).
 641  */
 642 ppm_owned_t *
 643 ppm_add_owned(dev_info_t *dip, ppm_domain_t *domp)
 644 {
 645         char path[MAXNAMELEN];
 646         ppm_owned_t *owned, *new_owned;
 647 
 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)
 652                         return (owned);
 653 
 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;
 659 
 660         return (domp->owned);
 661 }
 662 
 663 /*
 664  * create/init a new ppm device and link into the domain
 665  */
 666 ppm_dev_t *
 667 ppm_add_dev(dev_info_t *dip, ppm_domain_t *domp)
 668 {
 669         char path[MAXNAMELEN];
 670         ppm_dev_t *new = NULL;
 671         int cmpt;
 672         ppm_owned_t *owned;
 673 
 674         ASSERT(MUTEX_HELD(&domp->lock));
 675         (void) ddi_pathname(dip, path);
 676         /*
 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
 683          * in its domain.
 684          */
 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);
 689                 new->domp = domp;
 690                 new->dip = dip;
 691                 new->cmpt = cmpt;
 692                 ppm_dev_init(new);
 693                 new->next = domp->devlist;
 694                 domp->devlist = new;
 695                 PPMD(D_ADDDEV,
 696                     ("ppm_add_dev: %s to domain %s: ppm_dev(0x%p)\n",
 697                     new->path, domp->name, (void *)new))
 698         }
 699 
 700         ASSERT(new != NULL);
 701         /*
 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
 705          * for each of them.
 706          */
 707         PPM_SET_PRIVATE(dip, new);
 708 
 709         /* remember this device forever */
 710         owned = ppm_add_owned(dip, domp);
 711 
 712         /*
 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.
 717          */
 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)))
 723         }
 724 
 725         return (new);
 726 }
 727 
 728 
 729 /*
 730  * returns an existing or newly created ppm device reference
 731  */
 732 ppm_dev_t *
 733 ppm_get_dev(dev_info_t *dip, ppm_domain_t *domp)
 734 {
 735         ppm_dev_t *pdp;
 736 
 737         mutex_enter(&domp->lock);
 738         pdp = PPM_GET_PRIVATE(dip);
 739         if (pdp == NULL)
 740                 pdp = ppm_add_dev(dip, domp);
 741         mutex_exit(&domp->lock);
 742 
 743         return (pdp);
 744 }
 745 
 746 
 747 /*
 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
 751  */
 752 void
 753 ppm_rem_dev(dev_info_t *dip)
 754 {
 755         ppm_dev_t *pdp, **devpp;
 756         ppm_domain_t *domp;
 757 
 758         pdp = PPM_GET_PRIVATE(dip);
 759         ASSERT(pdp);
 760         domp = pdp->domp;
 761         ASSERT(domp);
 762 
 763         mutex_enter(&domp->lock);
 764         for (devpp = &domp->devlist; (pdp = *devpp) != NULL; ) {
 765                 if (pdp->dip != dip) {
 766                         devpp = &pdp->next;
 767                         continue;
 768                 }
 769 
 770                 PPMD(D_REMDEV, ("ppm_rem_dev: path \"%s\", ppm_dev 0x%p\n",
 771                     pdp->path, (void *)pdp))
 772 
 773                 PPM_SET_PRIVATE(dip, NULL);
 774                 *devpp = pdp->next;
 775                 ppm_dev_fini(pdp);
 776                 kmem_free(pdp->path, strlen(pdp->path) + 1);
 777                 kmem_free(pdp, sizeof (*pdp));
 778         }
 779         mutex_exit(&domp->lock);
 780 }
 781 
 782 /*
 783  * prepare kernel ioctl calls:
 784  */
 785 void
 786 ppm_init_cb(dev_info_t *dip)
 787 {
 788         char            *str = "ppm_init_cb";
 789         ppm_domain_t    *domp;
 790         ppm_dc_t        *dc;
 791 
 792         for (domp = ppm_domain_p; domp != NULL; domp = domp->next) {
 793                 for (dc = domp->dc; dc; dc = dc->next) {
 794                         /*
 795                          * Warning: This code is rather confusing.
 796                          *
 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.
 803                          *
 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.
 809                          */
 810                         if (ppm_lookup_hndl(domp->model, dc) != NULL)
 811                                 continue;
 812 
 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);
 817                                 break;
 818                         }
 819                 }
 820         }
 821 }
 822 
 823 
 824 /*
 825  *  ppm_init_lyr - initializing layered ioctl
 826  * Return:
 827  *     DDI_SUCCESS  - succeeded
 828  *     DDI_FAILURE  - failed
 829  *
 830  */
 831 int
 832 ppm_init_lyr(ppm_dc_t   *dc, dev_info_t *dip)
 833 {
 834         char                    *str = "ppm_init_lyr";
 835         int                     err = 0;
 836         ldi_ident_t             li;
 837 
 838         ASSERT(dc && dc->path);
 839 
 840         if (err = ldi_ident_from_dip(dip, &li)) {
 841                 cmn_err(CE_WARN, "%s: get ldi identifier "
 842                     "failed (err=%d)", str, err);
 843         }
 844 
 845         err = ldi_open_by_name(dc->path, FWRITE|FREAD, kcred, &(dc->lh), li);
 846 
 847         (void) ldi_ident_release(li);
 848 
 849         if (err != 0) {
 850                 cmn_err(CE_WARN, "Failed to open device(%s), rv(%d)",
 851                     dc->path, err);
 852                 return (err);
 853         }
 854 
 855         return (DDI_SUCCESS);
 856 }
 857 
 858 /*
 859  * lock, unlock, or trylock for one power mutex
 860  */
 861 void
 862 ppm_lock_one(ppm_dev_t *ppmd, power_req_t *reqp, int *iresp)
 863 {
 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);
 868                 break;
 869 
 870         case PMR_PPM_UNLOCK_POWER:
 871                 pm_unlock_power_single(ppmd->dip,
 872                     reqp->req.ppm_unlock_power_req.circ);
 873                 break;
 874 
 875         case PMR_PPM_TRY_LOCK_POWER:
 876                 *iresp = pm_try_locking_power_single(ppmd->dip,
 877                     reqp->req.ppm_lock_power_req.circp);
 878                 break;
 879         }
 880 }
 881 
 882 
 883 /*
 884  * lock, unlock, or trylock for all power mutexes within a domain
 885  */
 886 void
 887 ppm_lock_all(ppm_domain_t *domp, power_req_t *reqp, int *iresp)
 888 {
 889         /*
 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.
 895          */
 896         if (reqp->request_type == PMR_PPM_LOCK_POWER) {
 897                 if (!MUTEX_HELD(&domp->lock))
 898                         mutex_enter(&domp->lock);
 899                 domp->refcnt++;
 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 */
 904                 return;
 905         } else if (reqp->request_type == PMR_PPM_UNLOCK_POWER) {
 906                 ASSERT(MUTEX_HELD(&domp->lock));
 907                 ASSERT(domp->devlist != NULL);
 908                 pm_unlock_power_single(domp->devlist->dip,
 909                     reqp->req.ppm_unlock_power_req.circ);
 910                 if (--domp->refcnt == 0)
 911                         mutex_exit(&domp->lock);
 912                 return;
 913         }
 914 
 915         ASSERT(reqp->request_type == PMR_PPM_TRY_LOCK_POWER);
 916         if (!MUTEX_HELD(&domp->lock))
 917                 if (!mutex_tryenter(&domp->lock)) {
 918                         *iresp = 0;
 919                         return;
 920                 }
 921         *iresp = pm_try_locking_power_single(domp->devlist->dip,
 922             reqp->req.ppm_lock_power_req.circp);
 923         if (*iresp)
 924                 domp->refcnt++;
 925         else
 926                 mutex_exit(&domp->lock);
 927 }
 928 
 929 
 930 /*
 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.
 939  */
 940 boolean_t
 941 ppm_none_else_holds_power(ppm_domain_t *domp)
 942 {
 943         ppm_dev_t  *ppmd;
 944         ppm_owned_t *owned;
 945         int     i = 0;
 946 
 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))
 951                                 return (B_FALSE);
 952                         if ((domp->dflags & PPMD_PCI66MHZ) &&
 953                             !(ppmd->flags & PPMDEV_PCI66_D2))
 954                                 return (B_FALSE);
 955                 }
 956         }
 957 
 958         for (owned = domp->owned; owned; owned = owned->next)
 959                 if (pm_noinvol_detached(owned->path) || owned->initializing)
 960                         i++;
 961         return (i == 0);
 962 }
 963 
 964 
 965 /*
 966  * return the number of char 'c' occurrences in string s
 967  */
 968 int
 969 ppm_count_char(char *s, char c)
 970 {
 971         int     i = 0;
 972         char    *cp = s;
 973 
 974         while (*cp) {
 975                 if (*cp == c)
 976                         i++;
 977                 cp++;
 978         }
 979 
 980         return (i);
 981 }
 982 
 983 
 984 /*
 985  * extract and convert a substring from input string "ss" in form of
 986  * "name=value" into an hex or decimal integer
 987  */
 988 #define X_BASE  16
 989 #define D_BASE  10
 990 int
 991 ppm_stoi(char *ss, uint_t *val)
 992 {
 993         char *cp;
 994         int  hex_ = 0, base = D_BASE;
 995         int  digit;
 996 
 997         if ((cp = strchr(ss, '=')) == NULL)
 998                 return (*val = (uint_t)-1);
 999 
1000         cp++;
1001         if ((*cp == '0') && (*++cp == 'x')) {
1002                 hex_++;
1003                 cp++;
1004                 base = X_BASE;
1005         }
1006 
1007         for (digit = 0; *cp; cp++) {
1008                 if (hex_ && ((*cp >= 'A') && (*cp <= 'F')))
1009                         digit = (digit * base) + ((*cp - 'A') + D_BASE);
1010                 else if (hex_ && ((*cp >= 'a') && (*cp <= 'f')))
1011                         digit = (digit * base) + ((*cp - 'a') + D_BASE);
1012                 else
1013                         digit = (digit * base) + (*cp - '0');
1014         }
1015 
1016         return (*val = digit);
1017 }
1018 
1019 /*
1020  * ppm_convert - convert a #define symbol to its integer value,
1021  * only the #defines for ppm_dc.cmd and ppm_dc.method fields in
1022  * ppmvar.h file are recognized.
1023  */
1024 struct ppm_confdefs {
1025         char    *sym;
1026         int     val;
1027 } ppm_confdefs_table[] = {
1028         { "ENTER_S3", PPMDC_ENTER_S3 },
1029         { "EXIT_S3", PPMDC_EXIT_S3 },
1030         { "CPU_NEXT", PPMDC_CPU_NEXT },
1031         { "PRE_CHNG", PPMDC_PRE_CHNG },
1032         { "CPU_GO", PPMDC_CPU_GO },
1033         { "POST_CHNG", PPMDC_POST_CHNG },
1034         { "FET_ON", PPMDC_FET_ON },
1035         { "FET_OFF", PPMDC_FET_OFF },
1036         { "CLK_OFF", PPMDC_CLK_OFF },
1037         { "CLK_ON", PPMDC_CLK_ON },
1038         { "LED_ON", PPMDC_LED_ON },
1039         { "LED_OFF", PPMDC_LED_OFF },
1040         { "KIO", PPMDC_KIO },
1041         { "VCORE", PPMDC_VCORE },
1042 #ifdef sun4u
1043         { "I2CKIO", PPMDC_I2CKIO },
1044 #endif
1045         { "CPUSPEEDKIO", PPMDC_CPUSPEEDKIO },
1046         { "PRE_PWR_OFF", PPMDC_PRE_PWR_OFF },
1047         { "PRE_PWR_ON", PPMDC_PRE_PWR_ON },
1048         { "POST_PWR_ON", PPMDC_POST_PWR_ON },
1049         { "PWR_OFF", PPMDC_PWR_OFF },
1050         { "PWR_ON", PPMDC_PWR_ON },
1051         { "RESET_OFF", PPMDC_RESET_OFF },
1052         { "RESET_ON", PPMDC_RESET_ON },
1053         { NULL }
1054 };
1055 
1056 
1057 /*
1058  * convert a #define'd symbol to its integer value where
1059  * input "symbol" is expected to be in form of "SYMBOL=value"
1060  */
1061 int
1062 ppm_convert(char *symbol, uint_t *val)
1063 {
1064         char *s;
1065         struct ppm_confdefs *pcfp;
1066 
1067         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);
1071         }
1072         s++;
1073 
1074         for (pcfp = ppm_confdefs_table; (pcfp->sym != NULL); pcfp++) {
1075                 if (strcmp(s, pcfp->sym) == 0)
1076                         return (*val = pcfp->val);
1077         }
1078 
1079         cmn_err(CE_WARN, "ppm_convert: Unrecognizable token \"%s\" "
1080             "in ppm.conf file", symbol);
1081         return (*val = (uint_t)-1);
1082 }
1083 
1084 
1085 /*
1086  * parse a domain control property string into data structure struct ppm_dc
1087  */
1088 int
1089 ppm_parse_dc(char **dc_namep, ppm_dc_t *dc)
1090 {
1091         char    *str = "ppm_parse_dc";
1092         char    *line;
1093         char    *f, *b;
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 */
1097         boolean_t done;
1098         int     i;
1099         int     err;
1100 
1101         len = strlen(*dc_namep);
1102         line = kmem_alloc(len + 1, KM_SLEEP);
1103         (void) strcpy(line, *dc_namep);
1104 
1105         count = ppm_count_char(line, '=');
1106         ASSERT((count - ppm_count_char(line, ' ')) == 1);
1107 
1108         dclist = (char **)
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)
1112                         b++;
1113                 if (*b == 0)
1114                         done = B_TRUE;
1115                 else
1116                         *b = 0;
1117                 dclist[i] = f;
1118         }
1119 
1120         for (i = 0; i < count; i++) {
1121                 if (strstr(dclist[i], "cmd=")) {
1122                         err = ppm_convert(dclist[i], &dc->cmd);
1123                         if (err == -1)
1124                                 return (err);
1125                         continue;
1126                 }
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);
1131                         continue;
1132                 }
1133                 if (strstr(dclist[i], "method=")) {
1134                         err = ppm_convert(dclist[i], &dc->method);
1135                         if (err == -1)
1136                                 return (err);
1137                         continue;
1138                 }
1139                 if (strstr(dclist[i], "iowr=")) {
1140                         (void) ppm_stoi(dclist[i], &dc->m_un.kio.iowr);
1141                         continue;
1142                 }
1143                 if (strstr(dclist[i], "iord=")) {
1144                         (void) ppm_stoi(dclist[i], &dc->m_un.kio.iord);
1145                         continue;
1146                 }
1147                 if (strstr(dclist[i], "val=")) {
1148                         (void) ppm_stoi(dclist[i], &dc->m_un.kio.val);
1149                         continue;
1150                 }
1151                 if (strstr(dclist[i], "speeds=")) {
1152                         ASSERT(dc->method == PPMDC_CPUSPEEDKIO);
1153                         (void) ppm_stoi(dclist[i], &dc->m_un.cpu.speeds);
1154                         continue;
1155                 }
1156 #ifdef sun4u
1157                 if (strstr(dclist[i], "mask=")) {
1158                         (void) ppm_stoi(dclist[i], &dc->m_un.i2c.mask);
1159                         continue;
1160                 }
1161 #endif
1162                 /* This must be before the if statement for delay */
1163                 if (strstr(dclist[i], "post_delay=")) {
1164 #ifdef sun4u
1165                         ASSERT(dc->method == PPMDC_KIO ||
1166                             dc->method == PPMDC_I2CKIO);
1167 #else
1168                         ASSERT(dc->method == PPMDC_KIO);
1169 #endif
1170                         /*
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.
1174                          */
1175                         switch (dc->method) {
1176                         case PPMDC_KIO:
1177                                 (void) ppm_stoi(dclist[i],
1178                                     &dc->m_un.kio.post_delay);
1179                                 break;
1180 
1181 #ifdef sun4u
1182                         case PPMDC_I2CKIO:
1183                                 (void) ppm_stoi(dclist[i],
1184                                     &dc->m_un.i2c.post_delay);
1185                                 break;
1186 #endif
1187 
1188                         default:
1189                                 break;
1190                         }
1191                         continue;
1192                 }
1193                 if (strstr(dclist[i], "delay=")) {
1194 #ifdef sun4u
1195                         ASSERT(dc->method == PPMDC_VCORE ||
1196                             dc->method == PPMDC_KIO ||
1197                             dc->method == PPMDC_I2CKIO);
1198 #else
1199                         ASSERT(dc->method == PPMDC_VCORE ||
1200                             dc->method == PPMDC_KIO);
1201 #endif
1202 
1203                         /*
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.
1207                          */
1208 
1209                         switch (dc->method) {
1210                         case PPMDC_KIO:
1211                                 (void) ppm_stoi(dclist[i], &dc->m_un.kio.delay);
1212                                 break;
1213 
1214 #ifdef sun4u
1215                         case PPMDC_I2CKIO:
1216                                 (void) ppm_stoi(dclist[i], &dc->m_un.i2c.delay);
1217                                 break;
1218 #endif
1219 
1220                         case PPMDC_VCORE:
1221                                 (void) ppm_stoi(dclist[i], &dc->m_un.cpu.delay);
1222                                 break;
1223 
1224                         default:
1225                                 break;
1226                         }
1227                         continue;
1228                 }
1229 
1230                 /* we encounted unrecognized field, flag error */
1231                 cmn_err(CE_WARN, "%s: Unrecognized token \"%s\" in ppm.conf "
1232                     "file!", str, dclist[i]);
1233                 return (-1);
1234         }
1235 
1236         kmem_free(dclist, sizeof (char *) * (count + 1));
1237         kmem_free(line, len + 1);
1238 
1239         return (DDI_SUCCESS);
1240 }
1241 
1242 
1243 /*
1244  * search for domain control handle for a claimed device coupled with a
1245  * domain control command.  NULL device may indicate LED domain.
1246  */
1247 ppm_dc_t *
1248 ppm_lookup_dc(ppm_domain_t *domp, int cmd)
1249 {
1250 #ifdef  DEBUG
1251         char *str = "ppm_lookup_dc";
1252 #endif
1253         ppm_dc_t        *dc;
1254 
1255         /*
1256          *  For convenience, we accept 'domp' as NULL for searching
1257          *  LED domain control operation.
1258          */
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)
1262                                 break;
1263                 if (!domp || !domp->dc || !domp->dc->lh || !domp->dc->next) {
1264                         PPMD(D_LED, ("\tinsufficient led domain control "
1265                             "information.\n"))
1266                         return (NULL);
1267                 }
1268                 if (cmd == domp->dc->cmd)
1269                         return (domp->dc);
1270                 else
1271                         return (domp->dc->next);
1272         }
1273 
1274 
1275         /*
1276          * for the rest of ppm domains, lookup ppm_dc starting from domp
1277          */
1278         ASSERT(domp != NULL);
1279         switch (cmd) {
1280         case PPMDC_CPU_NEXT:
1281         case PPMDC_PRE_CHNG:
1282         case PPMDC_CPU_GO:
1283         case PPMDC_POST_CHNG:
1284         case PPMDC_FET_OFF:
1285         case PPMDC_FET_ON:
1286         case PPMDC_CLK_OFF:
1287         case PPMDC_CLK_ON:
1288         case PPMDC_PRE_PWR_OFF:
1289         case PPMDC_PRE_PWR_ON:
1290         case PPMDC_POST_PWR_ON:
1291         case PPMDC_PWR_OFF:
1292         case PPMDC_PWR_ON:
1293         case PPMDC_RESET_OFF:
1294         case PPMDC_RESET_ON:
1295         case PPMDC_ENTER_S3:
1296         case PPMDC_EXIT_S3:
1297                 break;
1298         default:
1299                 PPMD(D_PPMDC, ("%s: cmd(%d) unrecognized\n", str, cmd))
1300                 return (NULL);
1301         }
1302 
1303         for (dc = domp->dc; dc; dc = dc->next) {
1304                 if (dc->cmd == cmd) {
1305                         return (dc);
1306                 }
1307         }
1308 
1309         return (NULL);
1310 }
1311 
1312 #include <sys/esunddi.h>
1313 
1314 ppm_domain_t *
1315 ppm_get_domain_by_dev(const char *p)
1316 {
1317         dev_info_t *dip;
1318         ppm_domain_t    *domp;
1319         ppm_dev_t       *pdev;
1320         boolean_t       found = B_FALSE;
1321 
1322         if ((dip = e_ddi_hold_devi_by_path((char *)p, 0)) == NULL)
1323                 return (NULL);
1324 
1325         for (domp = ppm_domain_p; domp; domp = domp->next) {
1326                 for (pdev = domp->devlist; pdev; pdev = pdev->next) {
1327                         if (pdev->dip == dip) {
1328                                 found = B_TRUE;
1329                                 break;
1330                         }
1331                 }
1332                 if (found)
1333                         break;
1334         }
1335         ddi_release_devi(dip);
1336         return (domp);
1337 }
1338 
1339 
1340 #ifdef DEBUG
1341 #define FLINTSTR(flags, sym) { flags, sym, #sym }
1342 #define PMR_UNKNOWN -1
1343 /*
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.
1349  */
1350 char *
1351 ppm_get_ctlstr(int ctlop, uint_t mask)
1352 {
1353         struct ctlop_cmd {
1354                 uint_t flags;
1355                 int ctlop;
1356                 char *str;
1357         };
1358 
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),
1389         };
1390 
1391         for (ccp = cmds; ccp->ctlop != PMR_UNKNOWN; ccp++)
1392                 if (ctlop == ccp->ctlop)
1393                         break;
1394 
1395         if (ccp->flags & mask)
1396                 return (ccp->str);
1397         return (NULL);
1398 }
1399 
1400 void
1401 ppm_print_dc(ppm_dc_t *dc)
1402 {
1403         ppm_dc_t        *d = dc;
1404 
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))
1410 #ifdef sun4u
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))
1415 #endif
1416         } else if (d->method == PPMDC_VCORE) {
1417                 PPMD(D_PPMDC, ("cpu: .iord(%x), .iowr(%x), .val(0x%X), "
1418                     ".delay(0x%x)",
1419                     d->m_un.cpu.iord, d->m_un.cpu.iowr, d->m_un.cpu.val,
1420                     d->m_un.cpu.delay))
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))
1424         }
1425         PPMD(D_PPMDC, ("\n"))
1426 }
1427 #endif  /* DEBUG */