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