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