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