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