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