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 /* 28 * Platform Power Management master pseudo driver - 29 * - attaches only when ppm.conf file is present, indicating a 30 * workstation (since Excalibur era ) that is designed to 31 * be MOU-3 EPA compliant and which uses platform-specific 32 * hardware to do so; 33 * - this pseudo driver uses a set of simple satellite 34 * device drivers responsible for accessing platform 35 * specific devices to modify the registers they own. 36 * ppm drivers tells these satellite drivers what to do 37 * according to using command values taken from ppm.conf. 38 */ 39 #include <sys/conf.h> 40 #include <sys/stat.h> 41 #include <sys/file.h> 42 #include <sys/types.h> 43 #include <sys/param.h> 44 #include <sys/open.h> 45 #include <sys/callb.h> 46 #include <sys/va_list.h> 47 #include <sys/errno.h> 48 #include <sys/modctl.h> 49 #include <sys/sysmacros.h> 50 #include <sys/ddi_impldefs.h> 51 #include <sys/promif.h> 52 #include <sys/epm.h> 53 #include <sys/sunpm.h> 54 #include <sys/ppmio.h> 55 #include <sys/sunldi.h> 56 #include <sys/ppmvar.h> 57 #include <sys/ddi.h> 58 #include <sys/sunddi.h> 59 #include <sys/ppm_plat.h> 60 61 /* 62 * Note: When pm_power() is called (directly or indirectly) to change the 63 * power level of a device and the call returns failure, DO NOT assume the 64 * level is unchanged. Doublecheck it against ppmd->level. 65 */ 66 67 /* 68 * cb_ops 69 */ 70 static int ppm_open(dev_t *, int, int, cred_t *); 71 static int ppm_close(dev_t, int, int, cred_t *); 72 static int ppm_ioctl(dev_t, int, intptr_t, int, cred_t *, int *); 73 74 static struct cb_ops ppm_cb_ops = { 75 ppm_open, /* open */ 76 ppm_close, /* close */ 77 nodev, /* strategy */ 78 nodev, /* print */ 79 nodev, /* dump */ 80 nodev, /* read */ 81 nodev, /* write */ 82 ppm_ioctl, /* ioctl */ 83 nodev, /* devmap */ 84 nodev, /* mmap */ 85 nodev, /* segmap */ 86 nochpoll, /* poll */ 87 ddi_prop_op, /* prop_op */ 88 NULL, /* streamtab */ 89 D_MP | D_NEW, /* driver compatibility flag */ 90 CB_REV, /* cb_ops revision */ 91 nodev, /* async read */ 92 nodev /* async write */ 93 }; 94 95 /* 96 * bus_ops 97 */ 98 static int ppm_ctlops(dev_info_t *, dev_info_t *, ddi_ctl_enum_t, void *, 99 void *); 100 101 static struct bus_ops ppm_bus_ops = { 102 BUSO_REV, /* busops_rev */ 103 0, /* bus_map */ 104 0, /* bus_get_intrspec */ 105 0, /* bus_add_intrspec */ 106 0, /* bus_remove_intrspec */ 107 0, /* bus_map_fault */ 108 ddi_no_dma_map, /* bus_dma_map */ 109 ddi_no_dma_allochdl, /* bus_dma_allochdl */ 110 NULL, /* bus_dma_freehdl */ 111 NULL, /* bus_dma_bindhdl */ 112 NULL, /* bus_dma_unbindhdl */ 113 NULL, /* bus_dma_flush */ 114 NULL, /* bus_dma_win */ 115 NULL, /* bus_dma_ctl */ 116 ppm_ctlops, /* bus_ctl */ 117 0, /* bus_prop_op */ 118 0, /* bus_get_eventcookie */ 119 0, /* bus_add_eventcall */ 120 0, /* bus_remove_eventcall */ 121 0, /* bus_post_event */ 122 0 /* bus_intr_ctl */ 123 }; 124 125 /* 126 * dev_ops 127 */ 128 static int ppm_getinfo(dev_info_t *, ddi_info_cmd_t, void *, void **); 129 static int ppm_attach(dev_info_t *, ddi_attach_cmd_t); 130 static int ppm_detach(dev_info_t *, ddi_detach_cmd_t); 131 132 static struct dev_ops ppm_ops = { 133 DEVO_REV, /* devo_rev */ 134 0, /* refcnt */ 135 ppm_getinfo, /* info */ 136 nulldev, /* identify */ 137 nulldev, /* probe */ 138 ppm_attach, /* attach */ 139 ppm_detach, /* detach */ 140 nodev, /* reset */ 141 &ppm_cb_ops, /* cb_ops */ 142 &ppm_bus_ops, /* bus_ops */ 143 nulldev, /* power */ 144 ddi_quiesce_not_needed, /* quiesce */ 145 }; 146 147 extern struct mod_ops mod_driverops; 148 149 static struct modldrv modldrv = { 150 &mod_driverops, 151 "platform pm driver", 152 &ppm_ops 153 }; 154 155 static struct modlinkage modlinkage = { 156 MODREV_1, 157 &modldrv, 158 NULL 159 }; 160 161 /* 162 * Global data structure and variables 163 */ 164 int ppm_inst = -1; 165 void *ppm_statep; 166 ppm_domain_t *ppm_domain_p; 167 callb_id_t *ppm_cprcb_id; 168 static kmutex_t ppm_cpr_window_lock; /* guard ppm_cpr_window_flag */ 169 static boolean_t ppm_cpr_window_flag; /* set indicating chpt-resume period */ 170 171 /* LED actions */ 172 #define PPM_LED_SOLIDON 0 173 #define PPM_LED_BLINKING 1 174 175 /* 176 * Debug 177 */ 178 #ifdef DEBUG 179 uint_t ppm_debug = 0; 180 #endif 181 182 /* 183 * Local function prototypes and data 184 */ 185 static boolean_t ppm_cpr_callb(void *, int); 186 static int ppm_fetset(ppm_domain_t *, uint8_t); 187 static int ppm_fetget(ppm_domain_t *, uint8_t *); 188 static int ppm_gpioset(ppm_domain_t *, int); 189 static int ppm_manage_cpus(dev_info_t *, power_req_t *, int *); 190 static int ppm_manage_pci(dev_info_t *, power_req_t *, int *); 191 static int ppm_manage_pcie(dev_info_t *, power_req_t *, int *); 192 static int ppm_manage_fet(dev_info_t *, power_req_t *, int *); 193 static void ppm_manage_led(int); 194 static void ppm_set_led(ppm_domain_t *, int); 195 static void ppm_blink_led(void *); 196 static void ppm_svc_resume_ctlop(dev_info_t *, power_req_t *); 197 static int ppm_set_level(ppm_dev_t *, int, int, boolean_t); 198 static int ppm_change_power_level(ppm_dev_t *, int, int); 199 static int ppm_record_level_change(ppm_dev_t *, int, int); 200 static int ppm_switch_clock(ppm_domain_t *, int); 201 static int ppm_pcie_pwr(ppm_domain_t *, int); 202 static int ppm_power_up_domain(dev_info_t *dip); 203 static int ppm_power_down_domain(dev_info_t *dip); 204 205 int 206 _init(void) 207 { 208 if (ddi_soft_state_init( 209 &ppm_statep, sizeof (ppm_unit_t), 1) != DDI_SUCCESS) { 210 PPMD(D_INIT, ("ppm: soft state init\n")) 211 return (DDI_FAILURE); 212 } 213 214 if (mod_install(&modlinkage) != DDI_SUCCESS) { 215 ddi_soft_state_fini(&ppm_statep); 216 return (DDI_FAILURE); 217 } 218 return (DDI_SUCCESS); 219 } 220 221 222 int 223 _fini(void) 224 { 225 int error; 226 227 if ((error = mod_remove(&modlinkage)) == DDI_SUCCESS) 228 ddi_soft_state_fini(&ppm_statep); 229 230 return (error); 231 } 232 233 234 int 235 _info(struct modinfo *modinfop) 236 { 237 return (mod_info(&modlinkage, modinfop)); 238 } 239 240 241 /* ARGSUSED */ 242 int 243 ppm_getinfo(dev_info_t *dip, ddi_info_cmd_t cmd, void *arg, void **resultp) 244 { 245 struct ppm_unit *unitp; 246 dev_t dev; 247 int instance; 248 int rval; 249 250 if (ppm_inst == -1) 251 return (DDI_FAILURE); 252 253 switch (cmd) { 254 case DDI_INFO_DEVT2DEVINFO: 255 if (unitp = ddi_get_soft_state(ppm_statep, (dev_t)arg)) { 256 *resultp = unitp->dip; 257 rval = DDI_SUCCESS; 258 } else 259 rval = DDI_FAILURE; 260 261 return (rval); 262 263 case DDI_INFO_DEVT2INSTANCE: 264 dev = (dev_t)arg; 265 instance = getminor(dev); 266 *resultp = (void *)(uintptr_t)instance; 267 return (DDI_SUCCESS); 268 269 default: 270 return (DDI_FAILURE); 271 } 272 } 273 274 275 /* 276 * attach(9E) 277 */ 278 static int 279 ppm_attach(dev_info_t *dip, ddi_attach_cmd_t cmd) 280 { 281 ppm_unit_t *unitp; 282 int ret; 283 #ifdef DEBUG 284 char *str = "ppm_attach"; 285 #endif 286 287 288 switch (cmd) { 289 case DDI_ATTACH: 290 PPMD(D_ATTACH, ("%s: attaching ...\n", str)) 291 break; 292 293 case DDI_RESUME: 294 PPMD(D_ATTACH, ("%s: Resuming ...\n", str)) 295 unitp = ddi_get_soft_state(ppm_statep, ppm_inst); 296 mutex_enter(&unitp->lock); 297 unitp->states &= ~PPM_STATE_SUSPENDED; 298 mutex_exit(&unitp->lock); 299 return (DDI_SUCCESS); 300 301 default: 302 cmn_err(CE_WARN, "ppm_attach: unknown command %d, dip(0x%p)", 303 cmd, (void *)dip); 304 return (DDI_FAILURE); 305 } 306 307 if (ppm_inst != -1) { 308 PPMD(D_ATTACH, ("%s: Already attached !", str)) 309 return (DDI_FAILURE); 310 } 311 312 ppm_inst = ddi_get_instance(dip); 313 if (ddi_soft_state_zalloc(ppm_statep, ppm_inst) != DDI_SUCCESS) { 314 PPMD(D_ATTACH, ("%s: soft states alloc error!\n", str)) 315 return (DDI_FAILURE); 316 } 317 unitp = ddi_get_soft_state(ppm_statep, ppm_inst); 318 319 ret = ddi_create_minor_node(dip, "ppm", S_IFCHR, ppm_inst, 320 "ddi_ppm", 0); 321 if (ret != DDI_SUCCESS) { 322 PPMD(D_ATTACH, ("%s: can't create minor node!\n", str)) 323 goto fail1; 324 } 325 326 unitp->dip = dip; 327 mutex_init(&unitp->lock, NULL, MUTEX_DRIVER, NULL); 328 329 /* 330 * read ppm.conf, construct ppm_domain data structure and 331 * their sub data structure. 332 */ 333 if ((ret = ppm_create_db(dip)) != DDI_SUCCESS) 334 goto fail2; 335 336 /* 337 * walk down ppm domain control from each domain, initialize 338 * domain control orthogonal function call handle 339 */ 340 ppm_init_cb(dip); 341 342 if ((ret = pm_register_ppm(ppm_claim_dev, dip)) != DDI_SUCCESS) { 343 cmn_err(CE_WARN, "ppm_attach: can't register ppm handler!"); 344 goto fail2; 345 } 346 347 mutex_init(&ppm_cpr_window_lock, NULL, MUTEX_DRIVER, NULL); 348 ppm_cpr_window_flag = B_FALSE; 349 ppm_cprcb_id = callb_add(ppm_cpr_callb, (void *)NULL, 350 CB_CL_CPR_PM, "ppm_cpr"); 351 352 #if defined(__x86) 353 /* 354 * Register callback so that once CPUs have been added to 355 * the device tree, ppm can rebuild CPU domains using ACPI 356 * data. 357 */ 358 cpupm_rebuild_cpu_domains = ppm_rebuild_cpu_domains; 359 360 /* 361 * Register callback so that the ppm can initialize the 362 * topspeed for all CPUs in all domains. 363 */ 364 cpupm_init_topspeed = ppm_init_topspeed; 365 366 /* 367 * Register callback so that whenever max speed throttle requests 368 * are received, ppm can redefine the high power level for 369 * all CPUs in the domain. 370 */ 371 cpupm_redefine_topspeed = ppm_redefine_topspeed; 372 #endif 373 374 ddi_report_dev(dip); 375 return (DDI_SUCCESS); 376 377 fail2: 378 ddi_remove_minor_node(dip, "ddi_ppm"); 379 mutex_destroy(&unitp->lock); 380 fail1: 381 ddi_soft_state_free(ppm_statep, ppm_inst); 382 ppm_inst = -1; 383 return (DDI_FAILURE); 384 } 385 386 387 /* ARGSUSED */ 388 static int 389 ppm_detach(dev_info_t *dip, ddi_detach_cmd_t cmd) 390 { 391 ppm_unit_t *unitp; 392 #ifdef DEBUG 393 char *str = "ppm_detach"; 394 #endif 395 396 switch (cmd) { 397 case DDI_DETACH: 398 PPMD(D_DETACH, ("%s: detach not allowed.\n", str)) 399 return (DDI_FAILURE); 400 401 case DDI_SUSPEND: 402 PPMD(D_DETACH, ("%s: suspending ...\n", str)) 403 unitp = ddi_get_soft_state(ppm_statep, ppm_inst); 404 mutex_enter(&unitp->lock); 405 unitp->states |= PPM_STATE_SUSPENDED; 406 mutex_exit(&unitp->lock); 407 408 /* 409 * Suspend requires that timeout callouts to be canceled. 410 * Turning off the LED blinking will cancel the timeout. 411 */ 412 ppm_manage_led(PPM_LED_SOLIDON); 413 return (DDI_SUCCESS); 414 415 default: 416 cmn_err(CE_WARN, "ppm_detach: unsupported command %d, dip(%p)", 417 cmd, (void *)dip); 418 return (DDI_FAILURE); 419 } 420 } 421 422 423 /* ARGSUSED */ 424 int 425 ppm_open(dev_t *devp, int flag, int otyp, cred_t *cred_p) 426 { 427 if (otyp != OTYP_CHR) 428 return (EINVAL); 429 PPMD(D_OPEN, ("ppm_open: devp 0x%p, flag 0x%x, otyp %d\n", 430 (void *)devp, flag, otyp)) 431 return (0); 432 } 433 434 435 /* ARGSUSED */ 436 int 437 ppm_close(dev_t dev, int flag, int otyp, cred_t *credp) 438 { 439 PPMD(D_CLOSE, ("ppm_close: dev 0x%lx, flag 0x%x, otyp %d\n", 440 dev, flag, otyp)) 441 return (0); 442 } 443 444 445 /* ARGSUSED */ 446 int 447 ppm_ioctl(dev_t dev, int cmd, intptr_t arg, int mode, cred_t *cred_p, 448 int *rval_p) 449 { 450 #ifdef DEBUG 451 char *str = "ppm_ioctl"; 452 #endif 453 ppm_domain_t *domp = NULL; 454 uint8_t level, lvl; 455 int ret = 0; 456 457 PPMD(D_IOCTL, ("%s: dev 0x%lx, cmd 0x%x, mode 0x%x\n", 458 str, dev, cmd, mode)) 459 460 switch (cmd) { 461 case PPMGET_DPWR: 462 { 463 STRUCT_DECL(ppm_dpwr, dpwr); 464 struct ppm_unit *unitp; 465 char *domain; 466 467 STRUCT_INIT(dpwr, mode); 468 ret = ddi_copyin((caddr_t)arg, STRUCT_BUF(dpwr), 469 STRUCT_SIZE(dpwr), mode); 470 if (ret != 0) 471 return (EFAULT); 472 473 /* copyin domain name */ 474 domain = kmem_zalloc(MAXNAMELEN, KM_SLEEP); 475 ret = copyinstr( 476 STRUCT_FGETP(dpwr, domain), domain, MAXNAMELEN, NULL); 477 if (ret != 0) { 478 PPMD(D_IOCTL, ("%s: can't copyin domain, line(%d)\n", 479 str, __LINE__)) 480 ret = EFAULT; 481 goto err_dpwr; 482 } 483 484 /* locate domain */ 485 if ((domp = ppm_lookup_domain(domain)) == NULL) { 486 PPMD(D_IOCTL, ("%s: no such domain %s\n", str, domain)) 487 ret = ENODEV; 488 goto err_dpwr; 489 } 490 491 switch (domp->model) { 492 case PPMD_FET: /* report power fet ON or OFF */ 493 if ((ret = ppm_fetget(domp, &lvl)) != 0) { 494 ret = EIO; 495 goto err_dpwr; 496 } 497 level = (lvl == PPMD_ON) ? 498 PPMIO_POWER_ON : PPMIO_POWER_OFF; 499 break; 500 501 case PPMD_PCI: /* report pci slot clock ON or OFF */ 502 case PPMD_PCI_PROP: 503 case PPMD_PCIE: 504 level = (domp->status == PPMD_ON) ? 505 PPMIO_POWER_ON : PPMIO_POWER_OFF; 506 break; 507 508 case PPMD_LED: /* report LED blinking or solid on */ 509 510 unitp = ddi_get_soft_state(ppm_statep, ppm_inst); 511 if (unitp->led_tid == 0) 512 level = PPMIO_LED_SOLIDON; 513 else 514 level = PPMIO_LED_BLINKING; 515 break; 516 517 case PPMD_CPU: /* report cpu speed divisor */ 518 level = domp->devlist->level; 519 break; 520 521 default: 522 ret = EINVAL; 523 goto err_dpwr; 524 } 525 526 STRUCT_FSET(dpwr, level, level); 527 ret = ddi_copyout(STRUCT_BUF(dpwr), (caddr_t)arg, 528 STRUCT_SIZE(dpwr), mode); 529 if (ret != 0) { 530 PPMD(D_IOCTL, ("%s: can't copyout, line(%d)\n", 531 str, __LINE__)) 532 ret = EFAULT; 533 } 534 err_dpwr: 535 kmem_free(domain, MAXNAMELEN); 536 537 break; 538 } 539 540 case PPMGET_DOMBYDEV: 541 { 542 STRUCT_DECL(ppm_bydev, bydev); 543 char *path = NULL; 544 size_t size, l; 545 546 STRUCT_INIT(bydev, mode); 547 ret = ddi_copyin((caddr_t)arg, STRUCT_BUF(bydev), 548 STRUCT_SIZE(bydev), mode); 549 if (ret != 0) 550 return (EFAULT); 551 552 /* copyin .path */ 553 path = kmem_zalloc(MAXPATHLEN, KM_SLEEP); 554 ret = copyinstr( 555 STRUCT_FGETP(bydev, path), path, MAXPATHLEN, NULL); 556 if (ret != 0) { 557 PPMD(D_IOCTL, ("%s: can't copyin path, line(%d)\n", 558 str, __LINE__)) 559 kmem_free(path, MAXPATHLEN); 560 return (EFAULT); 561 } 562 563 /* so far we have up to one domain for a given device */ 564 size = STRUCT_FGET(bydev, size); 565 domp = ppm_get_domain_by_dev(path); 566 kmem_free(path, MAXPATHLEN); 567 if (domp != NULL) { 568 l = strlen(domp->name) + 1; 569 if (l > size) { 570 PPMD(D_IOCTL, ("%s: buffer too small\n", str)) 571 return ((size == 0) ? EINVAL : EFAULT); 572 } 573 } else /* no domain found to be associated with given device */ 574 return (ENODEV); 575 576 ret = copyoutstr( 577 domp->name, STRUCT_FGETP(bydev, domlist), l, &l); 578 if (ret != 0) { 579 PPMD(D_IOCTL, ("%s: can't copyout domlist, line(%d)" 580 " \n", str, __LINE__)) 581 return (EFAULT); 582 } 583 584 break; 585 } 586 587 588 case PPMGET_DEVBYDOM: 589 { 590 STRUCT_DECL(ppm_bydom, bydom); 591 char *domain = NULL; 592 char *devlist = NULL; 593 ppm_dev_t *ppmd; 594 dev_info_t *odip = NULL; 595 char *s, *d; 596 size_t size, l; 597 598 STRUCT_INIT(bydom, mode); 599 ret = ddi_copyin((caddr_t)arg, STRUCT_BUF(bydom), 600 STRUCT_SIZE(bydom), mode); 601 if (ret != 0) 602 return (EFAULT); 603 604 /* copyin .domain */ 605 domain = kmem_zalloc(MAXNAMELEN, KM_SLEEP); 606 ret = copyinstr(STRUCT_FGETP(bydom, domain), domain, 607 MAXNAMELEN, NULL); 608 if (ret != 0) { 609 PPMD(D_IOCTL, ("%s: can't copyin domain, line(%d)\n", 610 str, __LINE__)) 611 ret = EFAULT; 612 goto err_bydom; 613 } 614 615 /* locate domain */ 616 if ((domp = ppm_lookup_domain(domain)) == NULL) { 617 ret = ENODEV; 618 goto err_bydom; 619 } 620 621 l = 0; 622 if ((size = STRUCT_FGET(bydom, size)) == 0) 623 ret = EINVAL; 624 else 625 if ((d = devlist = kmem_zalloc(size, KM_SLEEP)) == NULL) 626 ret = EFAULT; 627 if (ret != 0) 628 goto err_bydom; 629 630 for (ppmd = domp->devlist; ppmd; 631 odip = ppmd->dip, ppmd = ppmd->next) { 632 633 if (ppmd->dip == odip) 634 continue; 635 if (ppmd != domp->devlist) 636 *d++ = ' '; 637 638 l += strlen(ppmd->path) + 1; 639 if (l > size) { 640 PPMD(D_IOCTL, ("%s: buffer overflow\n", str)) 641 ret = EFAULT; 642 goto err_bydom; 643 } 644 645 for (s = ppmd->path; *s != 0; ) 646 *d++ = *s++; 647 } 648 *d = 0; 649 650 if (*devlist == 0) 651 goto err_bydom; 652 653 ret = copyoutstr( 654 devlist, STRUCT_FGETP(bydom, devlist), l, &l); 655 if (ret != 0) { 656 PPMD(D_IOCTL, ("%s: can't copyout devlist, line(%d)" 657 " \n", str, __LINE__)) 658 ret = EFAULT; 659 } 660 661 err_bydom: 662 if (devlist) 663 kmem_free(devlist, size); 664 if (domain) 665 kmem_free(domain, MAXNAMELEN); 666 667 break; 668 } 669 670 #if defined(__x86) 671 /* 672 * Note that these two ioctls exist for test purposes only. 673 * Unfortunately, there really isn't any other good way of 674 * unit testing the dynamic redefinition of the top speed as it 675 * usually occurs due to environmental conditions. 676 */ 677 case PPMGET_NORMAL: 678 case PPMSET_NORMAL: 679 { 680 STRUCT_DECL(ppm_norm, norm); 681 char *path = NULL; 682 struct pm_component *dcomps; 683 struct pm_comp *pm_comp; 684 ppm_dev_t *ppmd; 685 int i; 686 687 STRUCT_INIT(norm, mode); 688 ret = ddi_copyin((caddr_t)arg, STRUCT_BUF(norm), 689 STRUCT_SIZE(norm), mode); 690 if (ret != 0) 691 return (EFAULT); 692 693 /* copyin .path */ 694 path = kmem_zalloc(MAXPATHLEN, KM_SLEEP); 695 ret = copyinstr( 696 STRUCT_FGETP(norm, path), path, MAXPATHLEN, NULL); 697 if (ret != 0) { 698 PPMD(D_IOCTL, ("%s: can't copyin path, line(%d)\n", 699 str, __LINE__)) 700 kmem_free(path, MAXPATHLEN); 701 return (EFAULT); 702 } 703 704 domp = ppm_get_domain_by_dev(path); 705 kmem_free(path, MAXPATHLEN); 706 707 if (domp == NULL) 708 return (ENODEV); 709 710 ppmd = domp->devlist; 711 if (cmd == PPMSET_NORMAL) { 712 if (domp->model != PPMD_CPU) 713 return (EINVAL); 714 level = STRUCT_FGET(norm, norm); 715 dcomps = DEVI(ppmd->dip)->devi_pm_components; 716 pm_comp = &dcomps[ppmd->cmpt].pmc_comp; 717 for (i = pm_comp->pmc_numlevels; i > 0; i--) { 718 if (pm_comp->pmc_lvals[i-1] == level) 719 break; 720 } 721 if (i == 0) 722 return (EINVAL); 723 724 ppm_set_topspeed(ppmd, pm_comp->pmc_numlevels - i); 725 } 726 727 level = pm_get_normal_power(ppmd->dip, 0); 728 729 STRUCT_FSET(norm, norm, level); 730 ret = ddi_copyout(STRUCT_BUF(norm), (caddr_t)arg, 731 STRUCT_SIZE(norm), mode); 732 if (ret != 0) { 733 PPMD(D_IOCTL, ("%s: can't copyout, line(%d)\n", 734 str, __LINE__)) 735 ret = EFAULT; 736 } 737 break; 738 } 739 #endif 740 default: 741 PPMD(D_IOCTL, ("%s: unsupported ioctl command(%d)\n", str, cmd)) 742 return (EINVAL); 743 } 744 745 return (ret); 746 } 747 748 749 static int ppm_manage_sx(s3a_t *, int); 750 static int ppm_search_list(pm_searchargs_t *); 751 752 /* 753 * interface between pm framework and ppm driver 754 */ 755 /* ARGSUSED */ 756 static int 757 ppm_ctlops(dev_info_t *dip, dev_info_t *rdip, 758 ddi_ctl_enum_t ctlop, void *arg, void *result) 759 { 760 power_req_t *reqp = (power_req_t *)arg; 761 ppm_unit_t *unitp; 762 ppm_domain_t *domp; 763 ppm_dev_t *ppmd; 764 char path[MAXNAMELEN]; 765 ppm_owned_t *owned; 766 int mode; 767 int ret = DDI_SUCCESS; 768 int *res = (int *)result; 769 s3a_t s3args; 770 771 #ifdef DEBUG 772 char *str = "ppm_ctlops"; 773 int mask = ppm_debug & (D_CTLOPS1 | D_CTLOPS2); 774 char *ctlstr = ppm_get_ctlstr(reqp->request_type, mask); 775 if (mask && ctlstr) 776 PPMD(mask, ("%s: %s, %s\n", 777 str, ddi_binding_name(rdip), ctlstr)) 778 #endif 779 780 if (ctlop != DDI_CTLOPS_POWER) { 781 return (DDI_FAILURE); 782 } 783 784 unitp = (ppm_unit_t *)ddi_get_soft_state(ppm_statep, ppm_inst); 785 786 switch (reqp->request_type) { 787 788 /* attempt to blink led if indeed all at lowest */ 789 case PMR_PPM_ALL_LOWEST: 790 mode = (reqp->req.ppm_all_lowest_req.mode == PM_ALL_LOWEST); 791 if (!(unitp->states & PPM_STATE_SUSPENDED) && mode) 792 ppm_manage_led(PPM_LED_BLINKING); 793 else 794 ppm_manage_led(PPM_LED_SOLIDON); 795 return (DDI_SUCCESS); 796 797 /* undo the claiming of 'rdip' at attach time */ 798 case PMR_PPM_POST_DETACH: 799 ASSERT(reqp->req.ppm_set_power_req.who == rdip); 800 mutex_enter(&unitp->lock); 801 if (reqp->req.ppm_config_req.result != DDI_SUCCESS || 802 (PPM_GET_PRIVATE(rdip) == NULL)) { 803 mutex_exit(&unitp->lock); 804 return (DDI_FAILURE); 805 } 806 mutex_exit(&unitp->lock); 807 ppm_rem_dev(rdip); 808 return (DDI_SUCCESS); 809 810 /* chance to adjust pwr_cnt if resume is about to power up rdip */ 811 case PMR_PPM_PRE_RESUME: 812 ppm_svc_resume_ctlop(rdip, reqp); 813 return (DDI_SUCCESS); 814 815 /* 816 * synchronizing, so that only the owner of the power lock is 817 * permitted to change device and component's power level. 818 */ 819 case PMR_PPM_UNLOCK_POWER: 820 case PMR_PPM_TRY_LOCK_POWER: 821 case PMR_PPM_LOCK_POWER: 822 ppmd = PPM_GET_PRIVATE(rdip); 823 if (ppmd) 824 domp = ppmd->domp; 825 else if (reqp->request_type != PMR_PPM_UNLOCK_POWER) { 826 domp = ppm_lookup_dev(rdip); 827 ASSERT(domp); 828 ppmd = ppm_get_dev(rdip, domp); 829 } 830 831 PPMD(D_LOCKS, ("ppm_lock_%s: %s, %s\n", 832 (domp->dflags & PPMD_LOCK_ALL) ? "all" : "one", 833 ppmd->path, ppm_get_ctlstr(reqp->request_type, D_LOCKS))) 834 835 if (domp->dflags & PPMD_LOCK_ALL) 836 ppm_lock_all(domp, reqp, result); 837 else 838 ppm_lock_one(ppmd, reqp, result); 839 return (DDI_SUCCESS); 840 841 case PMR_PPM_POWER_LOCK_OWNER: 842 ASSERT(reqp->req.ppm_power_lock_owner_req.who == rdip); 843 ppmd = PPM_GET_PRIVATE(rdip); 844 if (ppmd) 845 domp = ppmd->domp; 846 else { 847 domp = ppm_lookup_dev(rdip); 848 ASSERT(domp); 849 ppmd = ppm_get_dev(rdip, domp); 850 } 851 852 /* 853 * In case of LOCK_ALL, effective owner of the power lock 854 * is the owner of the domain lock. otherwise, it is the owner 855 * of the power lock. 856 */ 857 if (domp->dflags & PPMD_LOCK_ALL) 858 reqp->req.ppm_power_lock_owner_req.owner = 859 mutex_owner(&domp->lock); 860 else { 861 reqp->req.ppm_power_lock_owner_req.owner = 862 DEVI(rdip)->devi_busy_thread; 863 } 864 return (DDI_SUCCESS); 865 866 case PMR_PPM_INIT_CHILD: 867 ASSERT(reqp->req.ppm_lock_power_req.who == rdip); 868 if ((domp = ppm_lookup_dev(rdip)) == NULL) 869 return (DDI_SUCCESS); 870 871 /* 872 * We keep track of power-manageable devices starting with 873 * initialization process. The initializing flag remains 874 * set until it is cleared by ppm_add_dev(). Power management 875 * policy for some domains are affected even during device 876 * initialization. For example, PCI domains should leave 877 * their clock running meanwhile a device in that domain 878 * is initializing. 879 */ 880 mutex_enter(&domp->lock); 881 owned = ppm_add_owned(rdip, domp); 882 ASSERT(owned->initializing == 0); 883 owned->initializing = 1; 884 885 if (PPMD_IS_PCI(domp->model) && domp->status == PPMD_OFF) { 886 ret = ppm_switch_clock(domp, PPMD_ON); 887 if (ret == DDI_SUCCESS) 888 domp->dflags |= PPMD_INITCHILD_CLKON; 889 } 890 mutex_exit(&domp->lock); 891 return (ret); 892 893 case PMR_PPM_POST_ATTACH: 894 ASSERT(reqp->req.ppm_config_req.who == rdip); 895 domp = ppm_lookup_dev(rdip); 896 ASSERT(domp); 897 ASSERT(domp->status == PPMD_ON); 898 if (reqp->req.ppm_config_req.result == DDI_SUCCESS) { 899 /* 900 * call ppm_get_dev, which will increment the 901 * domain power count by the right number. 902 * Undo the power count increment, done in PRE_PROBE. 903 */ 904 if (PM_GET_PM_INFO(rdip)) 905 ppmd = ppm_get_dev(rdip, domp); 906 mutex_enter(&domp->lock); 907 ASSERT(domp->pwr_cnt > 0); 908 domp->pwr_cnt--; 909 mutex_exit(&domp->lock); 910 return (DDI_SUCCESS); 911 } 912 913 ret = ppm_power_down_domain(rdip); 914 /* FALLTHROUGH */ 915 case PMR_PPM_UNINIT_CHILD: 916 ASSERT(reqp->req.ppm_lock_power_req.who == rdip); 917 if ((domp = ppm_lookup_dev(rdip)) == NULL) 918 return (DDI_SUCCESS); 919 920 (void) ddi_pathname(rdip, path); 921 mutex_enter(&domp->lock); 922 for (owned = domp->owned; owned; owned = owned->next) 923 if (strcmp(owned->path, path) == 0) 924 break; 925 926 /* 927 * In case we didn't go through a complete attach and detach, 928 * the initializing flag will still be set, so clear it. 929 */ 930 if ((owned != NULL) && (owned->initializing)) 931 owned->initializing = 0; 932 933 if (PPMD_IS_PCI(domp->model) && 934 domp->status == PPMD_ON && domp->pwr_cnt == 0 && 935 (domp->dflags & PPMD_INITCHILD_CLKON) && 936 ppm_none_else_holds_power(domp)) { 937 ret = ppm_switch_clock(domp, PPMD_OFF); 938 if (ret == DDI_SUCCESS) 939 domp->dflags &= ~PPMD_INITCHILD_CLKON; 940 } 941 mutex_exit(&domp->lock); 942 return (ret); 943 944 /* place holders */ 945 case PMR_PPM_UNMANAGE: 946 case PMR_PPM_PRE_DETACH: 947 return (DDI_SUCCESS); 948 949 case PMR_PPM_PRE_PROBE: 950 ASSERT(reqp->req.ppm_config_req.who == rdip); 951 return (ppm_power_up_domain(rdip)); 952 953 case PMR_PPM_POST_PROBE: 954 ASSERT(reqp->req.ppm_config_req.who == rdip); 955 if (reqp->req.ppm_config_req.result == DDI_PROBE_SUCCESS || 956 reqp->req.ppm_config_req.result == DDI_PROBE_DONTCARE) 957 return (DDI_SUCCESS); 958 959 /* Probe failed */ 960 PPMD(D_CTLOPS1 | D_CTLOPS2, ("%s: probe failed for %s@%s " 961 "rv %d\n", str, PM_NAME(rdip), PM_ADDR(rdip), 962 reqp->req.ppm_config_req.result)) 963 return (ppm_power_down_domain(rdip)); 964 965 case PMR_PPM_PRE_ATTACH: 966 ASSERT(reqp->req.ppm_config_req.who == rdip); 967 /* Domain has already been powered up in PRE_PROBE */ 968 domp = ppm_lookup_dev(rdip); 969 ASSERT(domp); 970 ASSERT(domp->status == PPMD_ON); 971 return (DDI_SUCCESS); 972 973 /* ppm intercepts power change process to the claimed devices */ 974 case PMR_PPM_SET_POWER: 975 case PMR_PPM_POWER_CHANGE_NOTIFY: 976 if ((ppmd = PPM_GET_PRIVATE(rdip)) == NULL) { 977 domp = ppm_lookup_dev(rdip); 978 ASSERT(domp); 979 ppmd = ppm_get_dev(rdip, domp); 980 } 981 switch (ppmd->domp->model) { 982 case PPMD_CPU: 983 return (ppm_manage_cpus(rdip, reqp, result)); 984 case PPMD_FET: 985 return (ppm_manage_fet(rdip, reqp, result)); 986 case PPMD_PCI: 987 case PPMD_PCI_PROP: 988 return (ppm_manage_pci(rdip, reqp, result)); 989 case PPMD_PCIE: 990 return (ppm_manage_pcie(rdip, reqp, result)); 991 default: 992 cmn_err(CE_WARN, "ppm_ctlops: domain model %d does" 993 " not support PMR_PPM_SET_POWER ctlop", 994 ppmd->domp->model); 995 return (DDI_FAILURE); 996 } 997 998 case PMR_PPM_ENTER_SX: 999 case PMR_PPM_EXIT_SX: 1000 s3args.s3a_state = reqp->req.ppm_power_enter_sx_req.sx_state; 1001 s3args.s3a_test_point = 1002 reqp->req.ppm_power_enter_sx_req.test_point; 1003 s3args.s3a_wakephys = reqp->req.ppm_power_enter_sx_req.wakephys; 1004 s3args.s3a_psr = reqp->req.ppm_power_enter_sx_req.psr; 1005 ret = ppm_manage_sx(&s3args, 1006 reqp->request_type == PMR_PPM_ENTER_SX); 1007 if (ret) { 1008 PPMD(D_CPR, ("ppm_manage_sx returns %d\n", ret)) 1009 return (DDI_FAILURE); 1010 } else { 1011 return (DDI_SUCCESS); 1012 } 1013 1014 case PMR_PPM_SEARCH_LIST: 1015 ret = ppm_search_list(reqp->req.ppm_search_list_req.searchlist); 1016 reqp->req.ppm_search_list_req.result = ret; 1017 *res = ret; 1018 if (ret) { 1019 PPMD(D_CPR, ("ppm_search_list returns %d\n", ret)) 1020 return (DDI_FAILURE); 1021 } else { 1022 PPMD(D_CPR, ("ppm_search_list returns %d\n", ret)) 1023 return (DDI_SUCCESS); 1024 } 1025 1026 default: 1027 cmn_err(CE_WARN, "ppm_ctlops: unrecognized ctlops req(%d)", 1028 reqp->request_type); 1029 return (DDI_FAILURE); 1030 } 1031 } 1032 1033 1034 /* 1035 * Raise the power level of a subrange of cpus. Used when cpu driver 1036 * failed an attempt to lower the power of a cpu (probably because 1037 * it got busy). Need to revert the ones we already changed. 1038 * 1039 * ecpup = the ppm_dev_t for the cpu which failed to lower power 1040 * level = power level to reset prior cpus to 1041 */ 1042 int 1043 ppm_revert_cpu_power(ppm_dev_t *ecpup, int level) 1044 { 1045 ppm_dev_t *cpup; 1046 int ret = DDI_SUCCESS; 1047 1048 for (cpup = ecpup->domp->devlist; cpup != ecpup; cpup = cpup->next) { 1049 PPMD(D_CPU, ("ppm_revert_cpu_power: \"%s\", revert to " 1050 "level %d\n", cpup->path, level)) 1051 1052 ret = pm_power(cpup->dip, 0, level); 1053 if (ret == DDI_SUCCESS) { 1054 cpup->level = level; 1055 cpup->rplvl = PM_LEVEL_UNKNOWN; 1056 } 1057 } 1058 return (ret); 1059 } 1060 1061 1062 /* 1063 * ppm_manage_cpus - Process a request to change the power level of a cpu. 1064 * If not all cpus want to be at the same level, OR if we are currently 1065 * refusing slowdown requests due to thermal stress, we cache the request. 1066 * Otherwise, set all cpus to the new power level. 1067 */ 1068 /* ARGSUSED */ 1069 static int 1070 ppm_manage_cpus(dev_info_t *dip, power_req_t *reqp, int *result) 1071 { 1072 #ifdef DEBUG 1073 char *str = "ppm_manage_cpus"; 1074 #endif 1075 int old, new, ret, kmflag; 1076 ppm_dev_t *ppmd, *cpup; 1077 int change_notify = 0; 1078 pm_ppm_devlist_t *devlist = NULL, *p; 1079 int do_rescan = 0; 1080 1081 *result = DDI_SUCCESS; 1082 1083 switch (reqp->request_type) { 1084 case PMR_PPM_SET_POWER: 1085 break; 1086 1087 case PMR_PPM_POWER_CHANGE_NOTIFY: 1088 change_notify = 1; 1089 break; 1090 1091 default: 1092 return (DDI_FAILURE); 1093 } 1094 1095 ppmd = PPM_GET_PRIVATE(dip); 1096 ASSERT(MUTEX_HELD(&ppmd->domp->lock)); 1097 old = reqp->req.ppm_set_power_req.old_level; 1098 new = reqp->req.ppm_set_power_req.new_level; 1099 1100 if (change_notify) { 1101 ppmd->level = new; 1102 ppmd->rplvl = PM_LEVEL_UNKNOWN; 1103 1104 PPMD(D_CPU, ("%s: Notify cpu dip %p power level has changed " 1105 "from %d to %d", str, (void *)dip, old, new)) 1106 return (DDI_SUCCESS); 1107 } 1108 1109 if (ppm_manage_early_cpus(dip, new, result)) 1110 return (*result); 1111 1112 if (new == ppmd->level) { 1113 PPMD(D_CPU, ("%s: already at power level %d\n", str, new)) 1114 return (DDI_SUCCESS); 1115 } 1116 1117 /* 1118 * A request from lower to higher level transition is granted and 1119 * made effective on all cpus. A request from higher to lower must 1120 * be agreed upon by all cpus. 1121 */ 1122 ppmd->rplvl = new; 1123 for (cpup = ppmd->domp->devlist; cpup; cpup = cpup->next) { 1124 if (cpup->rplvl == new) 1125 continue; 1126 1127 if (new < old) { 1128 PPMD(D_SOME, ("%s: not all cpus wants to be at new " 1129 "level %d yet.\n", str, new)) 1130 return (DDI_SUCCESS); 1131 } 1132 1133 /* 1134 * If a single cpu requests power up, honor the request 1135 * powering up all cpus. 1136 */ 1137 if (new > old) { 1138 PPMD(D_SOME, ("%s: powering up device(%s@%s, %p) " 1139 "because of request from dip(%s@%s, %p), " 1140 "need pm_rescan\n", str, PM_NAME(cpup->dip), 1141 PM_ADDR(cpup->dip), (void *)cpup->dip, 1142 PM_NAME(dip), PM_ADDR(dip), (void *)dip)) 1143 do_rescan++; 1144 } 1145 } 1146 1147 PPMD(D_SETLVL, ("%s: \"%s\" set power level old %d, new %d \n", 1148 str, ppmd->path, ppmd->level, new)) 1149 ret = ppm_change_cpu_power(ppmd, new); 1150 *result = ret; 1151 1152 if (ret == DDI_SUCCESS) { 1153 if (reqp->req.ppm_set_power_req.canblock == PM_CANBLOCK_BLOCK) 1154 kmflag = KM_SLEEP; 1155 else 1156 kmflag = KM_NOSLEEP; 1157 1158 for (cpup = ppmd->domp->devlist; cpup; cpup = cpup->next) { 1159 if (cpup->dip == dip) 1160 continue; 1161 1162 if ((p = kmem_zalloc(sizeof (pm_ppm_devlist_t), 1163 kmflag)) == NULL) { 1164 break; 1165 } 1166 p->ppd_who = cpup->dip; 1167 p->ppd_cmpt = cpup->cmpt; 1168 p->ppd_old_level = old; 1169 p->ppd_new_level = new; 1170 p->ppd_next = devlist; 1171 1172 PPMD(D_SETLVL, ("%s: devlist entry[\"%s\"] %d -> %d\n", 1173 str, cpup->path, old, new)) 1174 1175 devlist = p; 1176 } 1177 reqp->req.ppm_set_power_req.cookie = (void *) devlist; 1178 1179 if (do_rescan > 0) { 1180 for (cpup = ppmd->domp->devlist; cpup; 1181 cpup = cpup->next) { 1182 if (cpup->dip == dip) 1183 continue; 1184 pm_rescan(cpup->dip); 1185 } 1186 } 1187 } 1188 1189 return (ret); 1190 } 1191 1192 1193 /* 1194 * ppm_svc_resume_ctlop - this is a small bookkeeping ppm does - 1195 * increments its FET domain power count, in anticipation of that 1196 * the indicated device(dip) would be powered up by its driver as 1197 * a result of cpr resuming. 1198 */ 1199 /* ARGSUSED */ 1200 static void 1201 ppm_svc_resume_ctlop(dev_info_t *dip, power_req_t *reqp) 1202 { 1203 ppm_domain_t *domp; 1204 ppm_dev_t *ppmd; 1205 int powered; /* power up count per dip */ 1206 1207 ppmd = PPM_GET_PRIVATE(dip); 1208 if (ppmd == NULL) 1209 return; 1210 1211 /* 1212 * Maintain correct powered count for domain which cares 1213 */ 1214 powered = 0; 1215 domp = ppmd->domp; 1216 mutex_enter(&domp->lock); 1217 if ((domp->model == PPMD_FET) || PPMD_IS_PCI(domp->model) || 1218 (domp->model == PPMD_PCIE)) { 1219 for (ppmd = domp->devlist; ppmd; ppmd = ppmd->next) { 1220 if (ppmd->dip == dip && ppmd->level) 1221 powered++; 1222 } 1223 1224 /* 1225 * All fets and clocks are held on during suspend - 1226 * resume window regardless their domain devices' power 1227 * level. 1228 */ 1229 ASSERT(domp->status == PPMD_ON); 1230 1231 /* 1232 * The difference indicates the number of components 1233 * being off prior to suspend operation, that is the 1234 * amount needs to be compensated in order to sync up 1235 * bookkeeping with reality, for PROM reset would have 1236 * brought up all devices. 1237 */ 1238 if (powered < PM_NUMCMPTS(dip)) 1239 domp->pwr_cnt += PM_NUMCMPTS(dip) - powered; 1240 } 1241 for (ppmd = domp->devlist; ppmd; ppmd = ppmd->next) { 1242 if (ppmd->dip == dip) 1243 ppmd->level = ppmd->rplvl = PM_LEVEL_UNKNOWN; 1244 } 1245 mutex_exit(&domp->lock); 1246 } 1247 1248 #ifdef DEBUG 1249 static int ppmbringup = 0; 1250 #endif 1251 1252 int 1253 ppm_bringup_domains() 1254 { 1255 #ifdef DEBUG 1256 char *str = "ppm_bringup_domains"; 1257 #endif 1258 ppm_domain_t *domp; 1259 int ret = DDI_SUCCESS; 1260 1261 PPMD(D_CPR, ("%s[%d]: enter\n", str, ++ppmbringup)) 1262 for (domp = ppm_domain_p; domp; domp = domp->next) { 1263 if ((!PPMD_IS_PCI(domp->model) && (domp->model != PPMD_FET) && 1264 (domp->model != PPMD_PCIE)) || (domp->devlist == NULL)) 1265 continue; 1266 1267 mutex_enter(&domp->lock); 1268 if (domp->status == PPMD_ON) { 1269 mutex_exit(&domp->lock); 1270 continue; 1271 } 1272 switch (domp->model) { 1273 case PPMD_FET: 1274 ret = ppm_fetset(domp, PPMD_ON); 1275 break; 1276 case PPMD_PCI: 1277 case PPMD_PCI_PROP: 1278 ret = ppm_switch_clock(domp, PPMD_ON); 1279 break; 1280 case PPMD_PCIE: 1281 ret = ppm_pcie_pwr(domp, PPMD_ON); 1282 break; 1283 default: 1284 break; 1285 } 1286 mutex_exit(&domp->lock); 1287 } 1288 PPMD(D_CPR, ("%s[%d]: exit\n", str, ppmbringup)) 1289 1290 return (ret); 1291 } 1292 1293 #ifdef DEBUG 1294 static int ppmsyncbp = 0; 1295 #endif 1296 1297 int 1298 ppm_sync_bookkeeping() 1299 { 1300 #ifdef DEBUG 1301 char *str = "ppm_sync_bookkeeping"; 1302 #endif 1303 ppm_domain_t *domp; 1304 int ret = DDI_SUCCESS; 1305 1306 PPMD(D_CPR, ("%s[%d]: enter\n", str, ++ppmsyncbp)) 1307 for (domp = ppm_domain_p; domp; domp = domp->next) { 1308 if ((!PPMD_IS_PCI(domp->model) && (domp->model != PPMD_FET) && 1309 (domp->model != PPMD_PCIE)) || (domp->devlist == NULL)) 1310 continue; 1311 1312 mutex_enter(&domp->lock); 1313 if ((domp->pwr_cnt != 0) || !ppm_none_else_holds_power(domp)) { 1314 mutex_exit(&domp->lock); 1315 continue; 1316 } 1317 1318 /* 1319 * skip NULL .devlist slot, for some may host pci device 1320 * that can not tolerate clock off or not even participate 1321 * in PM. 1322 */ 1323 if (domp->devlist == NULL) 1324 continue; 1325 1326 switch (domp->model) { 1327 case PPMD_FET: 1328 ret = ppm_fetset(domp, PPMD_OFF); 1329 break; 1330 case PPMD_PCI: 1331 case PPMD_PCI_PROP: 1332 ret = ppm_switch_clock(domp, PPMD_OFF); 1333 break; 1334 case PPMD_PCIE: 1335 ret = ppm_pcie_pwr(domp, PPMD_OFF); 1336 break; 1337 default: 1338 break; 1339 } 1340 mutex_exit(&domp->lock); 1341 } 1342 PPMD(D_CPR, ("%s[%d]: exit\n", str, ppmsyncbp)) 1343 1344 return (ret); 1345 } 1346 1347 1348 1349 /* 1350 * pre-suspend window; 1351 * 1352 * power up every FET and PCI clock that are off; 1353 * 1354 * set ppm_cpr_window global flag to indicate 1355 * that even though all pm_scan requested power transitions 1356 * will be honored as usual but that until we're out 1357 * of this window, no FET or clock will be turned off 1358 * for domains with pwr_cnt decremented down to 0. 1359 * Such is to avoid accessing the orthogonal drivers that own 1360 * the FET and clock registers that may not be resumed yet. 1361 * 1362 * at post-resume window, walk through each FET and PCI domains, 1363 * bring pwr_cnt and domp->status to sense: if pwr-cnt == 0, 1364 * and noinvol check okays, power down the FET or PCI. At last, 1365 * clear the global flag ppm_cpr_window. 1366 * 1367 * ASSERT case 1, during cpr window, checks pwr_cnt against power 1368 * transitions; 1369 * ASSERT case 2, out of cpr window, checks four things: 1370 * pwr_cnt <> power transition in/out of 0 1371 * <> status <> record of noinvol device detached 1372 * 1373 */ 1374 /* ARGSUSED */ 1375 static boolean_t 1376 ppm_cpr_callb(void *arg, int code) 1377 { 1378 int ret; 1379 1380 switch (code) { 1381 case CB_CODE_CPR_CHKPT: 1382 1383 /* pre-suspend: start of cpr window */ 1384 mutex_enter(&ppm_cpr_window_lock); 1385 ASSERT(ppm_cpr_window_flag == B_FALSE); 1386 ppm_cpr_window_flag = B_TRUE; 1387 mutex_exit(&ppm_cpr_window_lock); 1388 1389 ret = ppm_bringup_domains(); 1390 1391 break; 1392 1393 case CB_CODE_CPR_RESUME: 1394 1395 /* post-resume: end of cpr window */ 1396 ret = ppm_sync_bookkeeping(); 1397 1398 mutex_enter(&ppm_cpr_window_lock); 1399 ASSERT(ppm_cpr_window_flag == B_TRUE); 1400 ppm_cpr_window_flag = B_FALSE; 1401 mutex_exit(&ppm_cpr_window_lock); 1402 1403 break; 1404 } 1405 1406 return (ret == DDI_SUCCESS); 1407 } 1408 1409 1410 /* 1411 * Initialize our private version of real power level 1412 * as well as lowest and highest levels the device supports; 1413 * relate to ppm_add_dev 1414 */ 1415 void 1416 ppm_dev_init(ppm_dev_t *ppmd) 1417 { 1418 struct pm_component *dcomps; 1419 struct pm_comp *pm_comp; 1420 dev_info_t *dip; 1421 int maxi, i; 1422 1423 ASSERT(MUTEX_HELD(&ppmd->domp->lock)); 1424 ppmd->level = PM_LEVEL_UNKNOWN; 1425 ppmd->rplvl = PM_LEVEL_UNKNOWN; 1426 1427 /* increment pwr_cnt per component */ 1428 if ((ppmd->domp->model == PPMD_FET) || 1429 PPMD_IS_PCI(ppmd->domp->model) || 1430 (ppmd->domp->model == PPMD_PCIE)) 1431 ppmd->domp->pwr_cnt++; 1432 1433 dip = ppmd->dip; 1434 1435 /* 1436 * ppm exists to handle power-manageable devices which require 1437 * special handling on the current platform. However, a 1438 * driver for such a device may choose not to support power 1439 * management on a particular load/attach. In this case we 1440 * we create a structure to represent a single-component device 1441 * for which "level" = PM_LEVEL_UNKNOWN and "lowest" = 0 1442 * are effectively constant. 1443 */ 1444 if (PM_GET_PM_INFO(dip)) { 1445 dcomps = DEVI(dip)->devi_pm_components; 1446 pm_comp = &dcomps[ppmd->cmpt].pmc_comp; 1447 1448 ppmd->lowest = pm_comp->pmc_lvals[0]; 1449 ASSERT(ppmd->lowest >= 0); 1450 maxi = pm_comp->pmc_numlevels - 1; 1451 ppmd->highest = pm_comp->pmc_lvals[maxi]; 1452 1453 /* 1454 * If 66mhz PCI device on pci 66mhz bus supports D2 state 1455 * (config reg PMC bit 10 set), ppm could turn off its bus 1456 * clock once it is at D3hot. 1457 */ 1458 if (ppmd->domp->dflags & PPMD_PCI66MHZ) { 1459 for (i = 0; i < maxi; i++) 1460 if (pm_comp->pmc_lvals[i] == PM_LEVEL_D2) { 1461 ppmd->flags |= PPMDEV_PCI66_D2; 1462 break; 1463 } 1464 } 1465 } 1466 1467 /* 1468 * If device is in PCI_PROP domain and has exported the 1469 * property listed in ppm.conf, its clock will be turned 1470 * off when all pm'able devices in that domain are at D3. 1471 */ 1472 if ((ppmd->domp->model == PPMD_PCI_PROP) && 1473 (ppmd->domp->propname != NULL) && 1474 ddi_prop_exists(DDI_DEV_T_ANY, dip, DDI_PROP_DONTPASS, 1475 ppmd->domp->propname)) 1476 ppmd->flags |= PPMDEV_PCI_PROP_CLKPM; 1477 } 1478 1479 1480 /* 1481 * relate to ppm_rem_dev 1482 */ 1483 void 1484 ppm_dev_fini(ppm_dev_t *ppmd) 1485 { 1486 ASSERT(MUTEX_HELD(&ppmd->domp->lock)); 1487 1488 /* decrement pwr_cnt per component */ 1489 if ((ppmd->domp->model == PPMD_FET) || 1490 PPMD_IS_PCI(ppmd->domp->model) || 1491 (ppmd->domp->model == PPMD_PCIE)) 1492 if (ppmd->level != ppmd->lowest) 1493 ppmd->domp->pwr_cnt--; 1494 } 1495 1496 /* 1497 * Each power fet controls the power of one or more platform 1498 * device(s) within their domain. Hence domain devices' power 1499 * level change has been monitored, such that once all devices 1500 * are powered off, the fet is turned off to save more power. 1501 * 1502 * To power on any domain device, the domain power fet 1503 * needs to be turned on first. always one fet per domain. 1504 */ 1505 static int 1506 ppm_manage_fet(dev_info_t *dip, power_req_t *reqp, int *result) 1507 { 1508 #ifdef DEBUG 1509 char *str = "ppm_manage_fet"; 1510 #endif 1511 int (*pwr_func)(ppm_dev_t *, int, int); 1512 int new, old, cmpt; 1513 ppm_dev_t *ppmd; 1514 ppm_domain_t *domp; 1515 int incr = 0; 1516 int dummy_ret; 1517 1518 1519 *result = DDI_SUCCESS; 1520 switch (reqp->request_type) { 1521 case PMR_PPM_SET_POWER: 1522 pwr_func = ppm_change_power_level; 1523 old = reqp->req.ppm_set_power_req.old_level; 1524 new = reqp->req.ppm_set_power_req.new_level; 1525 cmpt = reqp->req.ppm_set_power_req.cmpt; 1526 break; 1527 case PMR_PPM_POWER_CHANGE_NOTIFY: 1528 pwr_func = ppm_record_level_change; 1529 old = reqp->req.ppm_notify_level_req.old_level; 1530 new = reqp->req.ppm_notify_level_req.new_level; 1531 cmpt = reqp->req.ppm_notify_level_req.cmpt; 1532 break; 1533 default: 1534 *result = DDI_FAILURE; 1535 PPMD(D_FET, ("%s: unknown request type %d for %s@%s\n", 1536 str, reqp->request_type, PM_NAME(dip), PM_ADDR(dip))) 1537 return (DDI_FAILURE); 1538 } 1539 1540 for (ppmd = PPM_GET_PRIVATE(dip); ppmd; ppmd = ppmd->next) 1541 if (cmpt == ppmd->cmpt) 1542 break; 1543 if (!ppmd) { 1544 PPMD(D_FET, ("%s: dip(%p): old(%d)->new(%d): no ppm_dev" 1545 " found for cmpt(%d)", str, (void *)dip, old, new, cmpt)) 1546 *result = DDI_FAILURE; 1547 return (DDI_FAILURE); 1548 } 1549 domp = ppmd->domp; 1550 PPMD(D_FET, ("%s: %s@%s %s old %d, new %d, c%d, level %d, " 1551 "status %s\n", str, PM_NAME(dip), PM_ADDR(dip), 1552 ppm_get_ctlstr(reqp->request_type, ~0), old, new, cmpt, 1553 ppmd->level, (domp->status == PPMD_OFF ? "off" : "on"))) 1554 1555 1556 ASSERT(old == ppmd->level); 1557 1558 if (new == ppmd->level) { 1559 PPMD(D_FET, ("nop\n")) 1560 return (DDI_SUCCESS); 1561 } 1562 1563 PPM_LOCK_DOMAIN(domp); 1564 1565 /* 1566 * In general, a device's published lowest power level does not 1567 * have to be 0 if power-off is not tolerated. i.e. a device 1568 * instance may export its lowest level > 0. It is reasonable to 1569 * assume that level 0 indicates off state, positive level values 1570 * indicate power states above off, include full power state. 1571 */ 1572 if (new > 0) { /* device powering up or to different positive level */ 1573 if (domp->status == PPMD_OFF) { 1574 1575 /* can not be in (chpt, resume) window */ 1576 ASSERT(ppm_cpr_window_flag == B_FALSE); 1577 1578 ASSERT(old == 0 && domp->pwr_cnt == 0); 1579 1580 PPMD(D_FET, ("About to turn fet on for %s@%s c%d\n", 1581 PM_NAME(dip), PM_ADDR(dip), cmpt)) 1582 1583 *result = ppm_fetset(domp, PPMD_ON); 1584 if (*result != DDI_SUCCESS) { 1585 PPMD(D_FET, ("\tCan't turn on power FET: " 1586 "ret(%d)\n", *result)) 1587 PPM_UNLOCK_DOMAIN(domp); 1588 return (DDI_FAILURE); 1589 } 1590 } 1591 1592 /* 1593 * If powering up, pre-increment the count before 1594 * calling pwr_func, because we are going to release 1595 * the domain lock and another thread might turn off 1596 * domain power otherwise. 1597 */ 1598 if (old == 0) { 1599 domp->pwr_cnt++; 1600 incr = 1; 1601 } 1602 1603 PPMD(D_FET, ("\t%s domain power count: %d\n", 1604 domp->name, domp->pwr_cnt)) 1605 } 1606 1607 1608 PPM_UNLOCK_DOMAIN(domp); 1609 1610 ASSERT(domp->pwr_cnt > 0); 1611 1612 if ((*result = (*pwr_func)(ppmd, cmpt, new)) != DDI_SUCCESS) { 1613 PPMD(D_FET, ("\t%s power change failed: ret(%d)\n", 1614 ppmd->path, *result)) 1615 } 1616 1617 PPM_LOCK_DOMAIN(domp); 1618 1619 /* 1620 * Decr the power count in two cases: 1621 * 1622 * 1) request was to power device down and was successful 1623 * 2) request was to power up (we pre-incremented count), but failed. 1624 */ 1625 if ((*result == DDI_SUCCESS && ppmd->level == 0) || 1626 (*result != DDI_SUCCESS && incr)) { 1627 ASSERT(domp->pwr_cnt > 0); 1628 domp->pwr_cnt--; 1629 } 1630 1631 PPMD(D_FET, ("\t%s domain power count: %d\n", 1632 domp->name, domp->pwr_cnt)) 1633 1634 /* 1635 * call to pwr_func will update ppm data structures, if it 1636 * succeeds. ppm should return whatever is the return value 1637 * from call to pwr_func. This way pm and ppm data structures 1638 * always in sync. Use dummy_ret from here for any further 1639 * return values. 1640 */ 1641 if ((domp->pwr_cnt == 0) && 1642 (ppm_cpr_window_flag == B_FALSE) && 1643 ppm_none_else_holds_power(domp)) { 1644 1645 PPMD(D_FET, ("About to turn FET off for %s@%s c%d\n", 1646 PM_NAME(dip), PM_ADDR(dip), cmpt)) 1647 1648 dummy_ret = ppm_fetset(domp, PPMD_OFF); 1649 if (dummy_ret != DDI_SUCCESS) { 1650 PPMD(D_FET, ("\tCan't turn off FET: ret(%d)\n", 1651 dummy_ret)) 1652 } 1653 } 1654 1655 PPM_UNLOCK_DOMAIN(domp); 1656 ASSERT(domp->pwr_cnt >= 0); 1657 return (*result); 1658 } 1659 1660 1661 /* 1662 * the actual code that turn on or off domain power fet and 1663 * update domain status 1664 */ 1665 static int 1666 ppm_fetset(ppm_domain_t *domp, uint8_t value) 1667 { 1668 char *str = "ppm_fetset"; 1669 int key; 1670 ppm_dc_t *dc; 1671 int ret; 1672 clock_t temp; 1673 clock_t delay = 0; 1674 1675 key = (value == PPMD_ON) ? PPMDC_FET_ON : PPMDC_FET_OFF; 1676 for (dc = domp->dc; dc; dc = dc->next) 1677 if (dc->cmd == key) 1678 break; 1679 if (!dc || !dc->lh) { 1680 PPMD(D_FET, ("%s: %s domain: NULL ppm_dc handle\n", 1681 str, domp->name)) 1682 return (DDI_FAILURE); 1683 } 1684 1685 if (key == PPMDC_FET_ON) { 1686 PPM_GET_IO_DELAY(dc, delay); 1687 if (delay > 0 && domp->last_off_time > 0) { 1688 /* 1689 * provide any delay required before turning on. 1690 * some devices e.g. Samsung DVD require minimum 1691 * of 1 sec between OFF->ON. no delay is required 1692 * for the first time. 1693 */ 1694 temp = ddi_get_lbolt(); 1695 temp -= domp->last_off_time; 1696 temp = drv_hztousec(temp); 1697 1698 if (temp < delay) { 1699 /* 1700 * busy wait untill we meet the 1701 * required delay. Since we maintain 1702 * time stamps in terms of clock ticks 1703 * we might wait for longer than required 1704 */ 1705 PPMD(D_FET, ("%s : waiting %lu micro seconds " 1706 "before on\n", domp->name, 1707 delay - temp)); 1708 drv_usecwait(delay - temp); 1709 } 1710 } 1711 } 1712 switch (dc->method) { 1713 #ifdef sun4u 1714 case PPMDC_I2CKIO: { 1715 i2c_gpio_t i2c_req; 1716 i2c_req.reg_mask = dc->m_un.i2c.mask; 1717 i2c_req.reg_val = dc->m_un.i2c.val; 1718 ret = ldi_ioctl(dc->lh, dc->m_un.i2c.iowr, 1719 (intptr_t)&i2c_req, FWRITE | FKIOCTL, kcred, NULL); 1720 break; 1721 } 1722 #endif 1723 1724 case PPMDC_KIO: 1725 ret = ldi_ioctl(dc->lh, dc->m_un.kio.iowr, 1726 (intptr_t)&(dc->m_un.kio.val), FWRITE | FKIOCTL, kcred, 1727 NULL); 1728 break; 1729 1730 default: 1731 PPMD(D_FET, ("\t%s: unsupported domain control method %d\n", 1732 str, domp->dc->method)) 1733 return (DDI_FAILURE); 1734 } 1735 1736 PPMD(D_FET, ("%s: %s domain(%s) FET from %s to %s\n", str, 1737 (ret == 0) ? "turned" : "failed to turn", 1738 domp->name, 1739 (domp->status == PPMD_ON) ? "ON" : "OFF", 1740 (value == PPMD_ON) ? "ON" : "OFF")) 1741 1742 if (ret == DDI_SUCCESS) { 1743 domp->status = value; 1744 1745 if (key == PPMDC_FET_OFF) 1746 /* 1747 * record the time, when it is off. time is recorded 1748 * in clock ticks 1749 */ 1750 domp->last_off_time = ddi_get_lbolt(); 1751 1752 /* implement any post op delay. */ 1753 if (key == PPMDC_FET_ON) { 1754 PPM_GET_IO_POST_DELAY(dc, delay); 1755 PPMD(D_FET, ("%s : waiting %lu micro seconds " 1756 "after on\n", domp->name, delay)) 1757 if (delay > 0) 1758 drv_usecwait(delay); 1759 } 1760 } 1761 1762 return (ret); 1763 } 1764 1765 1766 /* 1767 * read power fet status 1768 */ 1769 static int 1770 ppm_fetget(ppm_domain_t *domp, uint8_t *lvl) 1771 { 1772 char *str = "ppm_fetget"; 1773 ppm_dc_t *dc = domp->dc; 1774 uint_t kio_val; 1775 int off_val; 1776 int ret; 1777 1778 if (!dc->lh) { 1779 PPMD(D_FET, ("%s: %s domain NULL ppm_dc layered handle\n", 1780 str, domp->name)) 1781 return (DDI_FAILURE); 1782 } 1783 if (!dc->next) { 1784 cmn_err(CE_WARN, "%s: expect both fet on and fet off ops " 1785 "defined, found only one in domain(%s)", str, domp->name); 1786 return (DDI_FAILURE); 1787 } 1788 1789 switch (dc->method) { 1790 #ifdef sun4u 1791 case PPMDC_I2CKIO: { 1792 i2c_gpio_t i2c_req; 1793 i2c_req.reg_mask = dc->m_un.i2c.mask; 1794 ret = ldi_ioctl(dc->lh, dc->m_un.i2c.iord, 1795 (intptr_t)&i2c_req, FWRITE | FKIOCTL, kcred, NULL); 1796 1797 if (ret) { 1798 PPMD(D_FET, ("%s: PPMDC_I2CKIO failed: ret(%d)\n", 1799 str, ret)) 1800 return (ret); 1801 } 1802 1803 off_val = (dc->cmd == PPMDC_FET_OFF) ? dc->m_un.i2c.val : 1804 dc->next->m_un.i2c.val; 1805 *lvl = (i2c_req.reg_val == off_val) ? PPMD_OFF : PPMD_ON; 1806 1807 PPMD(D_FET, ("%s: %s domain FET %s\n", str, domp->name, 1808 (i2c_req.reg_val == off_val) ? "OFF" : "ON")) 1809 1810 break; 1811 } 1812 #endif 1813 1814 case PPMDC_KIO: 1815 ret = ldi_ioctl(dc->lh, dc->m_un.kio.iord, 1816 (intptr_t)&kio_val, FWRITE | FKIOCTL, kcred, NULL); 1817 if (ret) { 1818 PPMD(D_FET, ("%s: PPMDC_KIO failed: ret(%d)\n", 1819 str, ret)) 1820 return (ret); 1821 } 1822 1823 off_val = (dc->cmd == PPMDC_FET_OFF) ? dc->m_un.kio.val : 1824 dc->next->m_un.kio.val; 1825 *lvl = (kio_val == off_val) ? PPMD_OFF : PPMD_ON; 1826 1827 PPMD(D_FET, ("%s: %s domain FET %s\n", str, domp->name, 1828 (kio_val == off_val) ? "OFF" : "ON")) 1829 1830 break; 1831 1832 default: 1833 PPMD(D_FET, ("%s: unsupported domain control method %d\n", 1834 str, domp->dc->method)) 1835 return (DDI_FAILURE); 1836 } 1837 1838 return (DDI_SUCCESS); 1839 } 1840 1841 1842 /* 1843 * the actual code that switches pci clock and update domain status 1844 */ 1845 static int 1846 ppm_switch_clock(ppm_domain_t *domp, int onoff) 1847 { 1848 #ifdef DEBUG 1849 char *str = "ppm_switch_clock"; 1850 #endif 1851 int cmd, pio_save; 1852 ppm_dc_t *dc; 1853 int ret; 1854 extern int do_polled_io; 1855 extern uint_t cfb_inuse; 1856 ppm_dev_t *pdev; 1857 1858 cmd = (onoff == PPMD_ON) ? PPMDC_CLK_ON : PPMDC_CLK_OFF; 1859 dc = ppm_lookup_dc(domp, cmd); 1860 if (!dc) { 1861 PPMD(D_PCI, ("%s: no ppm_dc found for domain (%s)\n", 1862 str, domp->name)) 1863 return (DDI_FAILURE); 1864 } 1865 1866 switch (dc->method) { 1867 case PPMDC_KIO: 1868 /* 1869 * If we're powering up cfb on a Stop-A, we only 1870 * want to do polled i/o to turn ON the clock 1871 */ 1872 pio_save = do_polled_io; 1873 if ((cfb_inuse) && (cmd == PPMDC_CLK_ON)) { 1874 for (pdev = domp->devlist; pdev; pdev = pdev->next) { 1875 if (pm_is_cfb(pdev->dip)) { 1876 do_polled_io = 1; 1877 break; 1878 } 1879 } 1880 } 1881 1882 ret = ldi_ioctl(dc->lh, dc->m_un.kio.iowr, 1883 (intptr_t)&(dc->m_un.kio.val), FWRITE | FKIOCTL, 1884 kcred, NULL); 1885 1886 do_polled_io = pio_save; 1887 1888 if (ret == 0) { 1889 if (cmd == PPMDC_CLK_ON) { 1890 domp->status = PPMD_ON; 1891 1892 /* 1893 * PCI PM spec requires 50ms delay 1894 */ 1895 drv_usecwait(50000); 1896 } else 1897 domp->status = PPMD_OFF; 1898 } 1899 1900 PPMD(D_PCI, ("%s: %s pci clock %s for domain (%s)\n", str, 1901 (ret == 0) ? "turned" : "failed to turn", 1902 (cmd == PPMDC_CLK_OFF) ? "OFF" : "ON", 1903 domp->name)) 1904 1905 break; 1906 1907 default: 1908 PPMD(D_PCI, ("%s: unsupported domain control method %d\n", 1909 str, dc->method)) 1910 return (DDI_FAILURE); 1911 } 1912 1913 return (DDI_SUCCESS); 1914 } 1915 1916 1917 /* 1918 * pci slot domain is formed of pci device(s) reside in a pci slot. 1919 * This function monitors domain device's power level change, such 1920 * that, 1921 * when all domain power count has gone to 0, it attempts to turn off 1922 * the pci slot's clock; 1923 * if any domain device is powering up, it'll turn on the pci slot's 1924 * clock as the first thing. 1925 */ 1926 /* ARGUSED */ 1927 static int 1928 ppm_manage_pci(dev_info_t *dip, power_req_t *reqp, int *result) 1929 { 1930 #ifdef DEBUG 1931 char *str = "ppm_manage_pci"; 1932 #endif 1933 int (*pwr_func)(ppm_dev_t *, int, int); 1934 int old, new, cmpt; 1935 ppm_dev_t *ppmd; 1936 ppm_domain_t *domp; 1937 int incr = 0; 1938 int dummy_ret; 1939 1940 *result = DDI_SUCCESS; 1941 switch (reqp->request_type) { 1942 case PMR_PPM_SET_POWER: 1943 pwr_func = ppm_change_power_level; 1944 old = reqp->req.ppm_set_power_req.old_level; 1945 new = reqp->req.ppm_set_power_req.new_level; 1946 cmpt = reqp->req.ppm_set_power_req.cmpt; 1947 break; 1948 1949 case PMR_PPM_POWER_CHANGE_NOTIFY: 1950 pwr_func = ppm_record_level_change; 1951 old = reqp->req.ppm_notify_level_req.old_level; 1952 new = reqp->req.ppm_notify_level_req.new_level; 1953 cmpt = reqp->req.ppm_notify_level_req.cmpt; 1954 break; 1955 1956 default: 1957 *result = DDI_FAILURE; 1958 return (DDI_FAILURE); 1959 } 1960 1961 for (ppmd = PPM_GET_PRIVATE(dip); ppmd; ppmd = ppmd->next) 1962 if (cmpt == ppmd->cmpt) 1963 break; 1964 if (!ppmd) { 1965 PPMD(D_PCI, ("%s: dip(%p): old(%d), new(%d): no ppm_dev" 1966 " found for cmpt(%d)", str, (void *)dip, old, new, cmpt)) 1967 *result = DDI_FAILURE; 1968 return (DDI_FAILURE); 1969 } 1970 domp = ppmd->domp; 1971 PPMD(D_PCI, ("%s: %s, dev(%s), c%d, old %d, new %d\n", str, 1972 ppm_get_ctlstr(reqp->request_type, ~0), 1973 ppmd->path, cmpt, old, new)) 1974 1975 ASSERT(old == ppmd->level); 1976 if (new == ppmd->level) 1977 return (DDI_SUCCESS); 1978 1979 PPM_LOCK_DOMAIN(domp); 1980 1981 if (new > 0) { /* device powering up */ 1982 if (domp->status == PPMD_OFF) { 1983 1984 /* cannot be off during (chpt, resume) window */ 1985 ASSERT(ppm_cpr_window_flag == B_FALSE); 1986 1987 /* either both OFF or both ON */ 1988 ASSERT(!((old == 0) ^ (domp->pwr_cnt == 0))); 1989 1990 PPMD(D_PCI, ("About to turn clock on for %s@%s c%d\n", 1991 PM_NAME(dip), PM_ADDR(dip), cmpt)) 1992 1993 *result = ppm_switch_clock(domp, PPMD_ON); 1994 if (*result != DDI_SUCCESS) { 1995 PPMD(D_PCI, ("\tcan't switch on pci clock: " 1996 "ret(%d)\n", *result)) 1997 PPM_UNLOCK_DOMAIN(domp); 1998 return (DDI_FAILURE); 1999 } 2000 } 2001 2002 if (old == 0) { 2003 domp->pwr_cnt++; 2004 incr = 1; 2005 } 2006 2007 PPMD(D_PCI, ("\t%s domain power count: %d\n", 2008 domp->name, domp->pwr_cnt)) 2009 } 2010 2011 PPM_UNLOCK_DOMAIN(domp); 2012 2013 ASSERT(domp->pwr_cnt > 0); 2014 2015 if ((*result = (*pwr_func)(ppmd, cmpt, new)) != DDI_SUCCESS) { 2016 PPMD(D_PCI, ("\t%s power change failed: ret(%d)\n", 2017 ppmd->path, *result)) 2018 } 2019 2020 PPM_LOCK_DOMAIN(domp); 2021 2022 /* 2023 * Decr the power count in two cases: 2024 * 2025 * 1) request was to power device down and was successful 2026 * 2) request was to power up (we pre-incremented count), but failed. 2027 */ 2028 if ((*result == DDI_SUCCESS && ppmd->level == 0) || 2029 (*result != DDI_SUCCESS && incr)) { 2030 ASSERT(domp->pwr_cnt > 0); 2031 domp->pwr_cnt--; 2032 } 2033 2034 PPMD(D_PCI, ("\t%s domain power count: %d\n", 2035 domp->name, domp->pwr_cnt)) 2036 2037 /* 2038 * call to pwr_func will update ppm data structures, if it 2039 * succeeds. ppm should return whatever is the return value 2040 * from call to pwr_func. This way pm and ppm data structures 2041 * always in sync. Use dummy_ret from here for any further 2042 * return values. 2043 */ 2044 if ((domp->pwr_cnt == 0) && 2045 (ppm_cpr_window_flag == B_FALSE) && 2046 ppm_none_else_holds_power(domp)) { 2047 2048 PPMD(D_PCI, ("About to turn clock off for %s@%s c%d\n", 2049 PM_NAME(dip), PM_ADDR(dip), cmpt)) 2050 2051 dummy_ret = ppm_switch_clock(domp, PPMD_OFF); 2052 if (dummy_ret != DDI_SUCCESS) { 2053 PPMD(D_PCI, ("\tCan't switch clock off: " 2054 "ret(%d)\n", dummy_ret)) 2055 } 2056 } 2057 2058 PPM_UNLOCK_DOMAIN(domp); 2059 ASSERT(domp->pwr_cnt >= 0); 2060 return (*result); 2061 } 2062 2063 /* 2064 * When the driver for the primary PCI-Express child has set the device to 2065 * lowest power (D3hot), we come here to save even more power by transitioning 2066 * the slot to D3cold. Similarly, if the slot is in D3cold and we need to 2067 * power up the child, we come here first to power up the slot. 2068 */ 2069 /* ARGUSED */ 2070 static int 2071 ppm_manage_pcie(dev_info_t *dip, power_req_t *reqp, int *result) 2072 { 2073 #ifdef DEBUG 2074 char *str = "ppm_manage_pcie"; 2075 #endif 2076 int (*pwr_func)(ppm_dev_t *, int, int); 2077 int old, new, cmpt; 2078 ppm_dev_t *ppmd; 2079 ppm_domain_t *domp; 2080 int incr = 0; 2081 int dummy_ret; 2082 2083 *result = DDI_SUCCESS; 2084 switch (reqp->request_type) { 2085 case PMR_PPM_SET_POWER: 2086 pwr_func = ppm_change_power_level; 2087 old = reqp->req.ppm_set_power_req.old_level; 2088 new = reqp->req.ppm_set_power_req.new_level; 2089 cmpt = reqp->req.ppm_set_power_req.cmpt; 2090 break; 2091 2092 case PMR_PPM_POWER_CHANGE_NOTIFY: 2093 pwr_func = ppm_record_level_change; 2094 old = reqp->req.ppm_notify_level_req.old_level; 2095 new = reqp->req.ppm_notify_level_req.new_level; 2096 cmpt = reqp->req.ppm_notify_level_req.cmpt; 2097 break; 2098 2099 default: 2100 *result = DDI_FAILURE; 2101 return (DDI_FAILURE); 2102 } 2103 2104 for (ppmd = PPM_GET_PRIVATE(dip); ppmd; ppmd = ppmd->next) 2105 if (cmpt == ppmd->cmpt) 2106 break; 2107 if (!ppmd) { 2108 PPMD(D_PCI, ("%s: dip(%p): old(%d), new(%d): no ppm_dev" 2109 " found for cmpt(%d)", str, (void *)dip, old, new, cmpt)) 2110 *result = DDI_FAILURE; 2111 return (DDI_FAILURE); 2112 } 2113 domp = ppmd->domp; 2114 PPMD(D_PCI, ("%s: %s, dev(%s), c%d, old %d, new %d\n", str, 2115 ppm_get_ctlstr(reqp->request_type, ~0), 2116 ppmd->path, cmpt, old, new)) 2117 2118 ASSERT(old == ppmd->level); 2119 if (new == ppmd->level) 2120 return (DDI_SUCCESS); 2121 2122 PPM_LOCK_DOMAIN(domp); 2123 2124 if (new > 0) { /* device powering up */ 2125 if (domp->status == PPMD_OFF) { 2126 2127 /* cannot be off during (chpt, resume) window */ 2128 ASSERT(ppm_cpr_window_flag == B_FALSE); 2129 2130 /* either both OFF or both ON */ 2131 ASSERT(!((old == 0) ^ (domp->pwr_cnt == 0))); 2132 2133 PPMD(D_PCI, ("About to turn on pcie slot for " 2134 "%s@%s c%d\n", PM_NAME(dip), PM_ADDR(dip), cmpt)) 2135 2136 *result = ppm_pcie_pwr(domp, PPMD_ON); 2137 if (*result != DDI_SUCCESS) { 2138 PPMD(D_PCI, ("\tcan't switch on pcie slot: " 2139 "ret(%d)\n", *result)) 2140 PPM_UNLOCK_DOMAIN(domp); 2141 return (DDI_FAILURE); 2142 } 2143 } 2144 2145 if (old == 0) { 2146 domp->pwr_cnt++; 2147 incr = 1; 2148 } 2149 2150 PPMD(D_PCI, ("\t%s domain power count: %d\n", 2151 domp->name, domp->pwr_cnt)) 2152 } 2153 2154 PPM_UNLOCK_DOMAIN(domp); 2155 2156 ASSERT(domp->pwr_cnt > 0); 2157 2158 if ((*result = (*pwr_func)(ppmd, cmpt, new)) != DDI_SUCCESS) { 2159 PPMD(D_PCI, ("\t%s power change failed: ret(%d)\n", 2160 ppmd->path, *result)) 2161 } 2162 2163 PPM_LOCK_DOMAIN(domp); 2164 2165 /* 2166 * Decr the power count in two cases: 2167 * 2168 * 1) request was to power device down and was successful 2169 * 2) request was to power up (we pre-incremented count), but failed. 2170 */ 2171 if ((*result == DDI_SUCCESS && ppmd->level == 0) || 2172 (*result != DDI_SUCCESS && incr)) { 2173 ASSERT(domp->pwr_cnt > 0); 2174 domp->pwr_cnt--; 2175 } 2176 2177 PPMD(D_PCI, ("\t%s domain power count: %d\n", 2178 domp->name, domp->pwr_cnt)) 2179 2180 /* 2181 * call to pwr_func will update ppm data structures, if it 2182 * succeeds. ppm should return whatever is the return value 2183 * from call to pwr_func. This way pm and ppm data structures 2184 * always in sync. Use dummy_ret from here for any further 2185 * return values. 2186 */ 2187 if ((domp->pwr_cnt == 0) && 2188 (ppm_cpr_window_flag == B_FALSE) && 2189 ppm_none_else_holds_power(domp)) { 2190 2191 PPMD(D_PCI, ("About to turn off pcie slot for %s@%s c%d\n", 2192 PM_NAME(dip), PM_ADDR(dip), cmpt)) 2193 2194 dummy_ret = ppm_pcie_pwr(domp, PPMD_OFF); 2195 if (dummy_ret != DDI_SUCCESS) { 2196 PPMD(D_PCI, ("\tCan't switch pcie slot off: " 2197 "ret(%d)\n", dummy_ret)) 2198 } 2199 } 2200 2201 PPM_UNLOCK_DOMAIN(domp); 2202 ASSERT(domp->pwr_cnt >= 0); 2203 return (*result); 2204 2205 } 2206 2207 /* 2208 * Set or clear a bit on a GPIO device. These bits are used for various device- 2209 * specific purposes. 2210 */ 2211 static int 2212 ppm_gpioset(ppm_domain_t *domp, int key) 2213 { 2214 #ifdef DEBUG 2215 char *str = "ppm_gpioset"; 2216 #endif 2217 ppm_dc_t *dc; 2218 int ret; 2219 clock_t delay = 0; 2220 2221 for (dc = domp->dc; dc; dc = dc->next) 2222 if (dc->cmd == key) 2223 break; 2224 if (!dc || !dc->lh) { 2225 PPMD(D_GPIO, ("%s: %s domain: NULL ppm_dc handle\n", 2226 str, domp->name)) 2227 return (DDI_FAILURE); 2228 } 2229 2230 PPM_GET_IO_DELAY(dc, delay); 2231 if (delay > 0) { 2232 PPMD(D_GPIO, ("%s : waiting %lu micro seconds " 2233 "before change\n", domp->name, delay)) 2234 drv_usecwait(delay); 2235 } 2236 2237 switch (dc->method) { 2238 #ifdef sun4u 2239 case PPMDC_I2CKIO: { 2240 i2c_gpio_t i2c_req; 2241 ppm_dev_t *pdev; 2242 int pio_save; 2243 extern int do_polled_io; 2244 extern uint_t cfb_inuse; 2245 i2c_req.reg_mask = dc->m_un.i2c.mask; 2246 i2c_req.reg_val = dc->m_un.i2c.val; 2247 2248 pio_save = do_polled_io; 2249 if (cfb_inuse) { 2250 for (pdev = domp->devlist; pdev; pdev = pdev->next) { 2251 if (pm_is_cfb(pdev->dip)) { 2252 do_polled_io = 1; 2253 PPMD(D_GPIO, ("%s: cfb is in use, " 2254 "i2c transaction is done in " 2255 "poll-mode.\n", str)) 2256 break; 2257 } 2258 } 2259 } 2260 ret = ldi_ioctl(dc->lh, dc->m_un.i2c.iowr, 2261 (intptr_t)&i2c_req, FWRITE | FKIOCTL, kcred, NULL); 2262 do_polled_io = pio_save; 2263 2264 PPMD(D_GPIO, ("%s: %s domain(%s) from %s by writing %x " 2265 "to gpio\n", 2266 str, (ret == 0) ? "turned" : "FAILed to turn", 2267 domp->name, 2268 (domp->status == PPMD_ON) ? "ON" : "OFF", 2269 dc->m_un.i2c.val)) 2270 2271 break; 2272 } 2273 #endif 2274 2275 case PPMDC_KIO: 2276 ret = ldi_ioctl(dc->lh, dc->m_un.kio.iowr, 2277 (intptr_t)&(dc->m_un.kio.val), FWRITE | FKIOCTL, kcred, 2278 NULL); 2279 2280 PPMD(D_GPIO, ("%s: %s domain(%s) from %s by writing %x " 2281 "to gpio\n", 2282 str, (ret == 0) ? "turned" : "FAILed to turn", 2283 domp->name, 2284 (domp->status == PPMD_ON) ? "ON" : "OFF", 2285 dc->m_un.kio.val)) 2286 2287 break; 2288 2289 default: 2290 PPMD(D_GPIO, ("\t%s: unsupported domain control method %d\n", 2291 str, domp->dc->method)) 2292 return (DDI_FAILURE); 2293 } 2294 2295 /* implement any post op delay. */ 2296 PPM_GET_IO_POST_DELAY(dc, delay); 2297 if (delay > 0) { 2298 PPMD(D_GPIO, ("%s : waiting %lu micro seconds " 2299 "after change\n", domp->name, delay)) 2300 drv_usecwait(delay); 2301 } 2302 2303 return (ret); 2304 } 2305 2306 static int 2307 ppm_pcie_pwr(ppm_domain_t *domp, int onoff) 2308 { 2309 #ifdef DEBUG 2310 char *str = "ppm_pcie_pwr"; 2311 #endif 2312 int ret = DDI_FAILURE; 2313 ppm_dc_t *dc; 2314 clock_t delay; 2315 2316 ASSERT(onoff == PPMD_OFF || onoff == PPMD_ON); 2317 2318 dc = ppm_lookup_dc(domp, 2319 onoff == PPMD_ON ? PPMDC_PRE_PWR_ON : PPMDC_PRE_PWR_OFF); 2320 if (dc) { 2321 2322 /* 2323 * Invoke layered ioctl for pcie root complex nexus to 2324 * transition the link 2325 */ 2326 ASSERT(dc->method == PPMDC_KIO); 2327 delay = dc->m_un.kio.delay; 2328 if (delay > 0) { 2329 PPMD(D_GPIO, ("%s : waiting %lu micro seconds " 2330 "before change\n", domp->name, delay)) 2331 drv_usecwait(delay); 2332 } 2333 ret = ldi_ioctl(dc->lh, dc->m_un.kio.iowr, 2334 (intptr_t)&(dc->m_un.kio.val), 2335 FWRITE | FKIOCTL, kcred, NULL); 2336 if (ret == DDI_SUCCESS) { 2337 delay = dc->m_un.kio.post_delay; 2338 if (delay > 0) { 2339 PPMD(D_GPIO, ("%s : waiting %lu micro seconds " 2340 "after change\n", domp->name, delay)) 2341 drv_usecwait(delay); 2342 } 2343 } else { 2344 PPMD(D_PCI, ("%s: ldi_ioctl FAILED for domain(%s)\n", 2345 str, domp->name)) 2346 return (ret); 2347 } 2348 } 2349 2350 switch (onoff) { 2351 case PPMD_OFF: 2352 /* Turn off the clock for this slot. */ 2353 if ((ret = ppm_gpioset(domp, PPMDC_CLK_OFF)) != DDI_SUCCESS) { 2354 PPMD(D_GPIO, 2355 ("%s: failed to turn off domain(%s) clock\n", 2356 str, domp->name)) 2357 return (ret); 2358 } 2359 2360 /* Turn off the power to this slot */ 2361 if ((ret = ppm_gpioset(domp, PPMDC_PWR_OFF)) != DDI_SUCCESS) { 2362 PPMD(D_GPIO, 2363 ("%s: failed to turn off domain(%s) power\n", 2364 str, domp->name)) 2365 return (ret); 2366 } 2367 break; 2368 case PPMD_ON: 2369 /* Assert RESET for this slot. */ 2370 if ((ret = ppm_gpioset(domp, PPMDC_RESET_ON)) != DDI_SUCCESS) { 2371 PPMD(D_GPIO, 2372 ("%s: failed to assert reset for domain(%s)\n", 2373 str, domp->name)) 2374 return (ret); 2375 } 2376 2377 /* Turn on the power to this slot */ 2378 if ((ret = ppm_gpioset(domp, PPMDC_PWR_ON)) != DDI_SUCCESS) { 2379 PPMD(D_GPIO, 2380 ("%s: failed to turn on domain(%s) power\n", 2381 str, domp->name)) 2382 return (ret); 2383 } 2384 2385 /* Turn on the clock for this slot */ 2386 if ((ret = ppm_gpioset(domp, PPMDC_CLK_ON)) != DDI_SUCCESS) { 2387 PPMD(D_GPIO, 2388 ("%s: failed to turn on domain(%s) clock\n", 2389 str, domp->name)) 2390 return (ret); 2391 } 2392 2393 /* De-assert RESET for this slot. */ 2394 if ((ret = ppm_gpioset(domp, PPMDC_RESET_OFF)) != DDI_SUCCESS) { 2395 PPMD(D_GPIO, 2396 ("%s: failed to de-assert reset for domain(%s)\n", 2397 str, domp->name)) 2398 return (ret); 2399 } 2400 2401 dc = ppm_lookup_dc(domp, PPMDC_POST_PWR_ON); 2402 if (dc) { 2403 /* 2404 * Invoke layered ioctl to PCIe root complex nexus 2405 * to transition the link. 2406 */ 2407 ASSERT(dc->method == PPMDC_KIO); 2408 delay = dc->m_un.kio.delay; 2409 if (delay > 0) { 2410 PPMD(D_GPIO, ("%s: waiting %lu micro seconds " 2411 "before change\n", domp->name, delay)) 2412 drv_usecwait(delay); 2413 } 2414 ret = ldi_ioctl(dc->lh, dc->m_un.kio.iowr, 2415 (intptr_t)&(dc->m_un.kio.val), 2416 FWRITE | FKIOCTL, kcred, NULL); 2417 2418 if (ret != DDI_SUCCESS) { 2419 PPMD(D_PCI, ("%s: layered ioctl to PCIe" 2420 "root complex nexus FAILed\n", str)) 2421 return (ret); 2422 } 2423 2424 delay = dc->m_un.kio.post_delay; 2425 if (delay > 0) { 2426 PPMD(D_GPIO, ("%s: waiting %lu micro " 2427 "seconds after change\n", 2428 domp->name, delay)) 2429 drv_usecwait(delay); 2430 } 2431 } 2432 break; 2433 default: 2434 ASSERT(0); 2435 } 2436 2437 PPMD(D_PCI, ("%s: turned domain(%s) PCIe slot power from %s to %s\n", 2438 str, domp->name, (domp->status == PPMD_ON) ? "ON" : "OFF", 2439 onoff == PPMD_ON ? "ON" : "OFF")) 2440 2441 domp->status = onoff; 2442 return (ret); 2443 } 2444 2445 2446 /* 2447 * Change the power level for a component of a device. If the change 2448 * arg is true, we call the framework to actually change the device's 2449 * power; otherwise, we just update our own copy of the power level. 2450 */ 2451 static int 2452 ppm_set_level(ppm_dev_t *ppmd, int cmpt, int level, boolean_t change) 2453 { 2454 #ifdef DEBUG 2455 char *str = "ppm_set_level"; 2456 #endif 2457 int ret; 2458 2459 ret = DDI_SUCCESS; 2460 if (change) 2461 ret = pm_power(ppmd->dip, cmpt, level); 2462 2463 PPMD(D_SETLVL, ("%s: %s change=%d, old %d, new %d, ret %d\n", 2464 str, ppmd->path, change, ppmd->level, level, ret)) 2465 2466 if (ret == DDI_SUCCESS) { 2467 ppmd->level = level; 2468 ppmd->rplvl = PM_LEVEL_UNKNOWN; 2469 } 2470 2471 return (ret); 2472 } 2473 2474 2475 static int 2476 ppm_change_power_level(ppm_dev_t *ppmd, int cmpt, int level) 2477 { 2478 return (ppm_set_level(ppmd, cmpt, level, B_TRUE)); 2479 } 2480 2481 2482 static int 2483 ppm_record_level_change(ppm_dev_t *ppmd, int cmpt, int level) 2484 { 2485 return (ppm_set_level(ppmd, cmpt, level, B_FALSE)); 2486 } 2487 2488 2489 static void 2490 ppm_manage_led(int action) 2491 { 2492 ppm_domain_t *domp; 2493 ppm_unit_t *unitp; 2494 timeout_id_t tid; 2495 2496 2497 PPMD(D_LED, ("ppm_manage_led: action: %s\n", 2498 (action == PPM_LED_BLINKING) ? "PPM_LED_BLINKING" : 2499 "PPM_LED_SOLIDON")) 2500 2501 /* 2502 * test whether led operation is practically supported, 2503 * if not, we waive without pressing for reasons 2504 */ 2505 if (!ppm_lookup_dc(NULL, PPMDC_LED_ON)) 2506 return; 2507 2508 unitp = ddi_get_soft_state(ppm_statep, ppm_inst); 2509 for (domp = ppm_domain_p; (domp && (domp->model != PPMD_LED)); ) 2510 domp = domp->next; 2511 2512 mutex_enter(&unitp->lock); 2513 if (action == PPM_LED_BLINKING) { 2514 ppm_set_led(domp, PPMD_OFF); 2515 unitp->led_tid = timeout( 2516 ppm_blink_led, domp, PPM_LEDOFF_INTERVAL); 2517 2518 } else { /* PPM_LED_SOLIDON */ 2519 ASSERT(action == PPM_LED_SOLIDON); 2520 tid = unitp->led_tid; 2521 unitp->led_tid = 0; 2522 2523 mutex_exit(&unitp->lock); 2524 (void) untimeout(tid); 2525 2526 mutex_enter(&unitp->lock); 2527 ppm_set_led(domp, PPMD_ON); 2528 } 2529 mutex_exit(&unitp->lock); 2530 } 2531 2532 2533 static void 2534 ppm_set_led(ppm_domain_t *domp, int val) 2535 { 2536 int ret; 2537 2538 ret = ppm_gpioset(domp, 2539 (val == PPMD_ON) ? PPMDC_LED_ON : PPMDC_LED_OFF); 2540 2541 PPMD(D_LED, ("ppm_set_led: %s LED from %s\n", 2542 (ret == 0) ? "turned" : "FAILed to turn", 2543 (domp->status == PPMD_ON) ? "ON to OFF" : "OFF to ON")) 2544 2545 if (ret == DDI_SUCCESS) 2546 domp->status = val; 2547 } 2548 2549 2550 static void 2551 ppm_blink_led(void *arg) 2552 { 2553 ppm_unit_t *unitp; 2554 clock_t intvl; 2555 ppm_domain_t *domp = arg; 2556 2557 unitp = ddi_get_soft_state(ppm_statep, ppm_inst); 2558 2559 mutex_enter(&unitp->lock); 2560 if (unitp->led_tid == 0) { 2561 mutex_exit(&unitp->lock); 2562 return; 2563 } 2564 2565 if (domp->status == PPMD_ON) { 2566 ppm_set_led(domp, PPMD_OFF); 2567 intvl = PPM_LEDOFF_INTERVAL; 2568 } else { 2569 ppm_set_led(domp, PPMD_ON); 2570 intvl = PPM_LEDON_INTERVAL; 2571 } 2572 2573 unitp->led_tid = timeout(ppm_blink_led, domp, intvl); 2574 mutex_exit(&unitp->lock); 2575 } 2576 2577 /* 2578 * Function to power up a domain, if required. It also increments the 2579 * domain pwr_cnt to prevent it from going down. 2580 */ 2581 static int 2582 ppm_power_up_domain(dev_info_t *dip) 2583 { 2584 int ret = DDI_SUCCESS; 2585 ppm_domain_t *domp; 2586 char *str = "ppm_power_up_domain"; 2587 2588 domp = ppm_lookup_dev(dip); 2589 ASSERT(domp); 2590 mutex_enter(&domp->lock); 2591 switch (domp->model) { 2592 case PPMD_FET: 2593 if (domp->status == PPMD_OFF) { 2594 if ((ret = ppm_fetset(domp, PPMD_ON)) == 2595 DDI_SUCCESS) { 2596 PPMD(D_FET, ("%s: turned on fet for %s@%s\n", 2597 str, PM_NAME(dip), PM_ADDR(dip))) 2598 } else { 2599 PPMD(D_FET, ("%s: couldn't turn on fet " 2600 "for %s@%s\n", str, PM_NAME(dip), 2601 PM_ADDR(dip))) 2602 } 2603 } 2604 break; 2605 2606 case PPMD_PCI: 2607 case PPMD_PCI_PROP: 2608 if (domp->status == PPMD_OFF) { 2609 if ((ret = ppm_switch_clock(domp, PPMD_ON)) == 2610 DDI_SUCCESS) { 2611 PPMD(D_PCI, ("%s: turned on clock for " 2612 "%s@%s\n", str, PM_NAME(dip), 2613 PM_ADDR(dip))) 2614 } else { 2615 PPMD(D_PCI, ("%s: couldn't turn on clock " 2616 "for %s@%s\n", str, PM_NAME(dip), 2617 PM_ADDR(dip))) 2618 } 2619 } 2620 break; 2621 2622 case PPMD_PCIE: 2623 if (domp->status == PPMD_OFF) { 2624 if ((ret = ppm_pcie_pwr(domp, PPMD_ON)) == 2625 DDI_SUCCESS) { 2626 PPMD(D_PCI, ("%s: turned on link for " 2627 "%s@%s\n", str, PM_NAME(dip), 2628 PM_ADDR(dip))) 2629 } else { 2630 PPMD(D_PCI, ("%s: couldn't turn on link " 2631 "for %s@%s\n", str, PM_NAME(dip), 2632 PM_ADDR(dip))) 2633 } 2634 } 2635 break; 2636 2637 default: 2638 break; 2639 } 2640 if (ret == DDI_SUCCESS) 2641 domp->pwr_cnt++; 2642 mutex_exit(&domp->lock); 2643 return (ret); 2644 } 2645 2646 /* 2647 * Decrements the domain pwr_cnt. if conditions to power down the domain 2648 * are met, powers down the domain,. 2649 */ 2650 static int 2651 ppm_power_down_domain(dev_info_t *dip) 2652 { 2653 int ret = DDI_SUCCESS; 2654 char *str = "ppm_power_down_domain"; 2655 ppm_domain_t *domp; 2656 2657 domp = ppm_lookup_dev(dip); 2658 ASSERT(domp); 2659 mutex_enter(&domp->lock); 2660 ASSERT(domp->pwr_cnt > 0); 2661 domp->pwr_cnt--; 2662 switch (domp->model) { 2663 case PPMD_FET: 2664 if ((domp->pwr_cnt == 0) && 2665 (ppm_cpr_window_flag == B_FALSE) && 2666 ppm_none_else_holds_power(domp)) { 2667 if ((ret = ppm_fetset(domp, PPMD_OFF)) == 2668 DDI_SUCCESS) { 2669 PPMD(D_FET, ("%s: turned off FET for %s@%s \n", 2670 str, PM_NAME(dip), PM_ADDR(dip))) 2671 } else { 2672 PPMD(D_FET, ("%s: couldn't turn off FET for " 2673 " %s@%s\n", str, PM_NAME(dip), 2674 PM_ADDR(dip))) 2675 } 2676 } 2677 break; 2678 2679 case PPMD_PCI: 2680 case PPMD_PCI_PROP: 2681 if ((domp->pwr_cnt == 0) && 2682 (ppm_cpr_window_flag == B_FALSE) && 2683 ppm_none_else_holds_power(domp)) { 2684 if ((ret = ppm_switch_clock(domp, PPMD_OFF)) == 2685 DDI_SUCCESS) { 2686 PPMD(D_PCI, ("%s: turned off clock for %s@%s\n", 2687 str, PM_NAME(dip), PM_ADDR(dip))) 2688 } else { 2689 PPMD(D_PCI, ("%s: couldn't turn off clock " 2690 "for %s@%s\n", str, PM_NAME(dip), 2691 PM_ADDR(dip))) 2692 } 2693 } 2694 break; 2695 2696 case PPMD_PCIE: 2697 if ((domp->pwr_cnt == 0) && 2698 (ppm_cpr_window_flag == B_FALSE) && 2699 ppm_none_else_holds_power(domp)) { 2700 if ((ret = ppm_pcie_pwr(domp, PPMD_OFF)) == 2701 DDI_SUCCESS) { 2702 PPMD(D_PCI, ("%s: turned off link for %s@%s\n", 2703 str, PM_NAME(dip), PM_ADDR(dip))) 2704 } else { 2705 PPMD(D_PCI, ("%s: couldn't turn off link " 2706 "for %s@%s\n", str, PM_NAME(dip), 2707 PM_ADDR(dip))) 2708 } 2709 } 2710 break; 2711 2712 default: 2713 break; 2714 } 2715 mutex_exit(&domp->lock); 2716 return (ret); 2717 } 2718 2719 static int 2720 ppm_manage_sx(s3a_t *s3ap, int enter) 2721 { 2722 ppm_domain_t *domp = ppm_lookup_domain("domain_estar"); 2723 ppm_dc_t *dc; 2724 int ret = 0; 2725 2726 if (domp == NULL) { 2727 PPMD(D_CPR, ("ppm_manage_sx: can't find estar domain\n")) 2728 return (ENODEV); 2729 } 2730 PPMD(D_CPR, ("ppm_manage_sx %x, enter %d\n", s3ap->s3a_state, 2731 enter)) 2732 switch (s3ap->s3a_state) { 2733 case S3: 2734 if (enter) { 2735 dc = ppm_lookup_dc(domp, PPMDC_ENTER_S3); 2736 } else { 2737 dc = ppm_lookup_dc(domp, PPMDC_EXIT_S3); 2738 } 2739 ASSERT(dc && dc->method == PPMDC_KIO); 2740 PPMD(D_CPR, 2741 ("ppm_manage_sx: calling acpi driver (handle %p)" 2742 " with %x\n", (void *)dc->lh, dc->m_un.kio.iowr)) 2743 ret = ldi_ioctl(dc->lh, dc->m_un.kio.iowr, 2744 (intptr_t)s3ap, FWRITE | FKIOCTL, kcred, NULL); 2745 break; 2746 2747 case S4: 2748 /* S4 is not supported yet */ 2749 return (EINVAL); 2750 default: 2751 ASSERT(0); 2752 } 2753 return (ret); 2754 } 2755 2756 /* 2757 * Search enable/disable lists, which are encoded in ppm.conf as an array 2758 * of char strings. 2759 */ 2760 static int 2761 ppm_search_list(pm_searchargs_t *sl) 2762 { 2763 int i; 2764 int flags = DDI_PROP_DONTPASS; 2765 ppm_unit_t *unitp = ddi_get_soft_state(ppm_statep, ppm_inst); 2766 char **pp; 2767 char *starp; 2768 uint_t nelements; 2769 char *manuf = sl->pms_manufacturer; 2770 char *prod = sl->pms_product; 2771 2772 if (ddi_prop_lookup_string_array(DDI_DEV_T_ANY, unitp->dip, flags, 2773 sl->pms_listname, &pp, &nelements) != DDI_PROP_SUCCESS) { 2774 PPMD(D_CPR, ("ppm_search_list prop lookup %s failed--EINVAL\n", 2775 sl->pms_listname)) 2776 return (EINVAL); 2777 } 2778 ASSERT((nelements & 1) == 0); /* must be even */ 2779 2780 PPMD(D_CPR, ("ppm_search_list looking for %s, %s\n", manuf, prod)) 2781 2782 for (i = 0; i < nelements; i += 2) { 2783 PPMD(D_CPR, ("checking %s, %s", pp[i], pp[i+1])) 2784 /* we support only a trailing '*' pattern match */ 2785 if ((starp = strchr(pp[i], '*')) != NULL && *(starp + 1) == 0) { 2786 /* LINTED - ptrdiff overflow */ 2787 if (strncmp(manuf, pp[i], (starp - pp[i])) != 0) { 2788 PPMD(D_CPR, (" no match %s with %s\n", 2789 manuf, pp[i + 1])) 2790 continue; 2791 } 2792 } 2793 if ((starp = strchr(pp[i + 1], '*')) != NULL && 2794 *(starp + 1) == 0) { 2795 if (strncmp(prod, 2796 /* LINTED - ptrdiff overflow */ 2797 pp[i + 1], (starp - pp[i + 1])) != 0) { 2798 PPMD(D_CPR, (" no match %s with %s\n", 2799 prod, pp[i + 1])) 2800 continue; 2801 } 2802 } 2803 if (strcmp(manuf, pp[i]) == 0 && 2804 (strcmp(prod, pp[i + 1]) == 0)) { 2805 PPMD(D_CPR, (" match\n")) 2806 ddi_prop_free(pp); 2807 return (0); 2808 } 2809 PPMD(D_CPR, (" no match %s with %s or %s with %s\n", 2810 manuf, pp[i], prod, pp[i + 1])) 2811 } 2812 ddi_prop_free(pp); 2813 return (ENODEV); 2814 } 2815