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