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 */