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