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