1*5cff7825Smh27603 /* 2*5cff7825Smh27603 * CDDL HEADER START 3*5cff7825Smh27603 * 4*5cff7825Smh27603 * The contents of this file are subject to the terms of the 5*5cff7825Smh27603 * Common Development and Distribution License (the "License"). 6*5cff7825Smh27603 * You may not use this file except in compliance with the License. 7*5cff7825Smh27603 * 8*5cff7825Smh27603 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9*5cff7825Smh27603 * or http://www.opensolaris.org/os/licensing. 10*5cff7825Smh27603 * See the License for the specific language governing permissions 11*5cff7825Smh27603 * and limitations under the License. 12*5cff7825Smh27603 * 13*5cff7825Smh27603 * When distributing Covered Code, include this CDDL HEADER in each 14*5cff7825Smh27603 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15*5cff7825Smh27603 * If applicable, add the following below this CDDL HEADER, with the 16*5cff7825Smh27603 * fields enclosed by brackets "[]" replaced with your own identifying 17*5cff7825Smh27603 * information: Portions Copyright [yyyy] [name of copyright owner] 18*5cff7825Smh27603 * 19*5cff7825Smh27603 * CDDL HEADER END 20*5cff7825Smh27603 */ 21*5cff7825Smh27603 /* 22*5cff7825Smh27603 * Copyright 2007 Sun Microsystems, Inc. All rights reserved. 23*5cff7825Smh27603 * Use is subject to license terms. 24*5cff7825Smh27603 */ 25*5cff7825Smh27603 26*5cff7825Smh27603 #pragma ident "%Z%%M% %I% %E% SMI" 27*5cff7825Smh27603 28*5cff7825Smh27603 /* 29*5cff7825Smh27603 * Platform Power Management master pseudo driver platform support. 30*5cff7825Smh27603 */ 31*5cff7825Smh27603 32*5cff7825Smh27603 #include <sys/file.h> 33*5cff7825Smh27603 #include <sys/ddi.h> 34*5cff7825Smh27603 #include <sys/sunddi.h> 35*5cff7825Smh27603 #include <sys/ppmvar.h> 36*5cff7825Smh27603 37*5cff7825Smh27603 /* 38*5cff7825Smh27603 * This flag disables vcore/vid feature by default. 39*5cff7825Smh27603 */ 40*5cff7825Smh27603 uint_t ppm_do_vcore = 0; 41*5cff7825Smh27603 42*5cff7825Smh27603 /* 43*5cff7825Smh27603 * PPMDC_CPU_NEXT operation 44*5cff7825Smh27603 */ 45*5cff7825Smh27603 static int 46*5cff7825Smh27603 ppm_cpu_next(ppm_domain_t *domp, int level) 47*5cff7825Smh27603 { 48*5cff7825Smh27603 #ifdef DEBUG 49*5cff7825Smh27603 char *str = "ppm_cpu_next"; 50*5cff7825Smh27603 #endif 51*5cff7825Smh27603 ppm_dc_t *dc; 52*5cff7825Smh27603 int index = level - 1; 53*5cff7825Smh27603 int ret = 0; 54*5cff7825Smh27603 55*5cff7825Smh27603 dc = ppm_lookup_dc(domp, PPMDC_CPU_NEXT); 56*5cff7825Smh27603 for (; dc && (dc->cmd == PPMDC_CPU_NEXT); dc = dc->next) { 57*5cff7825Smh27603 switch (dc->method) { 58*5cff7825Smh27603 case PPMDC_CPUSPEEDKIO: 59*5cff7825Smh27603 ret = ldi_ioctl(dc->lh, dc->m_un.cpu.iowr, 60*5cff7825Smh27603 (intptr_t)index, FWRITE | FKIOCTL, kcred, NULL); 61*5cff7825Smh27603 if (ret) 62*5cff7825Smh27603 return (ret); 63*5cff7825Smh27603 break; 64*5cff7825Smh27603 65*5cff7825Smh27603 default: 66*5cff7825Smh27603 PPMD(D_CPU, ("%s: unsupported method(0x%x)\n", 67*5cff7825Smh27603 str, dc->method)) 68*5cff7825Smh27603 return (-1); 69*5cff7825Smh27603 } 70*5cff7825Smh27603 } 71*5cff7825Smh27603 return (ret); 72*5cff7825Smh27603 } 73*5cff7825Smh27603 74*5cff7825Smh27603 /* 75*5cff7825Smh27603 * PPMDC_PRE_CHNG operation 76*5cff7825Smh27603 */ 77*5cff7825Smh27603 static int 78*5cff7825Smh27603 ppm_cpu_pre_chng(ppm_domain_t *domp, int oldl, int speedup) 79*5cff7825Smh27603 { 80*5cff7825Smh27603 #ifdef DEBUG 81*5cff7825Smh27603 char *str = "ppm_cpu_pre_chng"; 82*5cff7825Smh27603 #endif 83*5cff7825Smh27603 ppm_dc_t *dc; 84*5cff7825Smh27603 int lowest; 85*5cff7825Smh27603 int ret = 0; 86*5cff7825Smh27603 87*5cff7825Smh27603 dc = ppm_lookup_dc(domp, PPMDC_PRE_CHNG); 88*5cff7825Smh27603 for (; dc && (dc->cmd == PPMDC_PRE_CHNG); dc = dc->next) { 89*5cff7825Smh27603 90*5cff7825Smh27603 switch (dc->method) { 91*5cff7825Smh27603 case PPMDC_VCORE: 92*5cff7825Smh27603 lowest = domp->devlist->lowest; 93*5cff7825Smh27603 if ((oldl != lowest) || (speedup != 1)) 94*5cff7825Smh27603 break; 95*5cff7825Smh27603 96*5cff7825Smh27603 /* raise core voltage */ 97*5cff7825Smh27603 if (ppm_do_vcore > 0) { 98*5cff7825Smh27603 ret = ldi_ioctl(dc->lh, 99*5cff7825Smh27603 dc->m_un.cpu.iowr, 100*5cff7825Smh27603 (intptr_t)&dc->m_un.cpu.val, 101*5cff7825Smh27603 FWRITE | FKIOCTL, kcred, NULL); 102*5cff7825Smh27603 if (ret != 0) 103*5cff7825Smh27603 return (ret); 104*5cff7825Smh27603 if (dc->m_un.cpu.delay > 0) 105*5cff7825Smh27603 drv_usecwait(dc->m_un.cpu.delay); 106*5cff7825Smh27603 } 107*5cff7825Smh27603 break; 108*5cff7825Smh27603 109*5cff7825Smh27603 default: 110*5cff7825Smh27603 PPMD(D_CPU, ("%s: unsupported method(0x%x)\n", 111*5cff7825Smh27603 str, dc->method)) 112*5cff7825Smh27603 return (-1); 113*5cff7825Smh27603 } 114*5cff7825Smh27603 } 115*5cff7825Smh27603 116*5cff7825Smh27603 return (ret); 117*5cff7825Smh27603 } 118*5cff7825Smh27603 119*5cff7825Smh27603 /* 120*5cff7825Smh27603 * PPMDC_CPU_GO operation 121*5cff7825Smh27603 */ 122*5cff7825Smh27603 /* ARGSUSED */ 123*5cff7825Smh27603 static int 124*5cff7825Smh27603 ppm_cpu_go(ppm_domain_t *domp, int level) 125*5cff7825Smh27603 { 126*5cff7825Smh27603 ppm_dc_t *dc; 127*5cff7825Smh27603 int ret = 0; 128*5cff7825Smh27603 129*5cff7825Smh27603 dc = ppm_lookup_dc(domp, PPMDC_CPU_GO); 130*5cff7825Smh27603 if (dc == NULL) { 131*5cff7825Smh27603 return (ret); 132*5cff7825Smh27603 } 133*5cff7825Smh27603 switch (dc->method) { 134*5cff7825Smh27603 case PPMDC_KIO: 135*5cff7825Smh27603 ret = ldi_ioctl(dc->lh, dc->m_un.kio.iowr, 136*5cff7825Smh27603 (intptr_t)dc->m_un.kio.val, FWRITE | FKIOCTL, 137*5cff7825Smh27603 kcred, NULL); 138*5cff7825Smh27603 break; 139*5cff7825Smh27603 default: 140*5cff7825Smh27603 return (-1); 141*5cff7825Smh27603 } 142*5cff7825Smh27603 143*5cff7825Smh27603 return (ret); 144*5cff7825Smh27603 } 145*5cff7825Smh27603 146*5cff7825Smh27603 /* 147*5cff7825Smh27603 * PPMDC_POST_CHNG operation 148*5cff7825Smh27603 */ 149*5cff7825Smh27603 static int 150*5cff7825Smh27603 ppm_cpu_post_chng(ppm_domain_t *domp, int newl, int speedup) 151*5cff7825Smh27603 { 152*5cff7825Smh27603 #ifdef DEBUG 153*5cff7825Smh27603 char *str = "ppm_cpu_post_chng"; 154*5cff7825Smh27603 #endif 155*5cff7825Smh27603 ppm_dc_t *dc; 156*5cff7825Smh27603 int lowest; 157*5cff7825Smh27603 int ret = 0; 158*5cff7825Smh27603 159*5cff7825Smh27603 dc = ppm_lookup_dc(domp, PPMDC_POST_CHNG); 160*5cff7825Smh27603 for (; dc && (dc->cmd == PPMDC_POST_CHNG); dc = dc->next) { 161*5cff7825Smh27603 162*5cff7825Smh27603 switch (dc->method) { 163*5cff7825Smh27603 case PPMDC_VCORE: 164*5cff7825Smh27603 lowest = domp->devlist->lowest; 165*5cff7825Smh27603 if ((newl != lowest) || (speedup != 0)) 166*5cff7825Smh27603 break; 167*5cff7825Smh27603 168*5cff7825Smh27603 /* lower core voltage */ 169*5cff7825Smh27603 if (ppm_do_vcore > 0) { 170*5cff7825Smh27603 ret = ldi_ioctl(dc->lh, 171*5cff7825Smh27603 dc->m_un.cpu.iowr, 172*5cff7825Smh27603 (intptr_t)&dc->m_un.cpu.val, 173*5cff7825Smh27603 FWRITE | FKIOCTL, kcred, NULL); 174*5cff7825Smh27603 if (ret != 0) 175*5cff7825Smh27603 return (ret); 176*5cff7825Smh27603 if (dc->m_un.cpu.delay > 0) 177*5cff7825Smh27603 drv_usecwait(dc->m_un.cpu.delay); 178*5cff7825Smh27603 } 179*5cff7825Smh27603 break; 180*5cff7825Smh27603 181*5cff7825Smh27603 default: 182*5cff7825Smh27603 PPMD(D_CPU, ("%s: unsupported method(0x%x)\n", 183*5cff7825Smh27603 str, dc->method)) 184*5cff7825Smh27603 return (-1); 185*5cff7825Smh27603 } 186*5cff7825Smh27603 } 187*5cff7825Smh27603 return (ret); 188*5cff7825Smh27603 } 189*5cff7825Smh27603 190*5cff7825Smh27603 /* 191*5cff7825Smh27603 * The effective cpu estar model is: program all cpus to be ready to go 192*5cff7825Smh27603 * the same next(or new) speed level, program all other system bus resident 193*5cff7825Smh27603 * devices to the same next speed level. At last, pull the trigger to 194*5cff7825Smh27603 * initiate the speed change for all system bus resident devices 195*5cff7825Smh27603 * simultaneously. 196*5cff7825Smh27603 * 197*5cff7825Smh27603 * On Excalibur, the Safari bus resident devices are Cheetah/Cheetah+ and 198*5cff7825Smh27603 * Schizo. On Enchilada, the JBus resident devides are Jalapeno(s) and 199*5cff7825Smh27603 * Tomatillo(s). 200*5cff7825Smh27603 */ 201*5cff7825Smh27603 int 202*5cff7825Smh27603 ppm_change_cpu_power(ppm_dev_t *ppmd, int newlevel) 203*5cff7825Smh27603 { 204*5cff7825Smh27603 #ifdef DEBUG 205*5cff7825Smh27603 char *str = "ppm_change_cpu_power"; 206*5cff7825Smh27603 #endif 207*5cff7825Smh27603 ppm_unit_t *unitp; 208*5cff7825Smh27603 ppm_domain_t *domp; 209*5cff7825Smh27603 ppm_dev_t *cpup; 210*5cff7825Smh27603 dev_info_t *dip; 211*5cff7825Smh27603 int level, oldlevel; 212*5cff7825Smh27603 int speedup, incr, lowest, highest; 213*5cff7825Smh27603 char *chstr; 214*5cff7825Smh27603 int ret; 215*5cff7825Smh27603 216*5cff7825Smh27603 unitp = ddi_get_soft_state(ppm_statep, ppm_inst); 217*5cff7825Smh27603 ASSERT(unitp); 218*5cff7825Smh27603 domp = ppmd->domp; 219*5cff7825Smh27603 cpup = domp->devlist; 220*5cff7825Smh27603 lowest = cpup->lowest; 221*5cff7825Smh27603 highest = cpup->highest; 222*5cff7825Smh27603 223*5cff7825Smh27603 /* 224*5cff7825Smh27603 * Not all cpus may have transitioned to a known level by this time 225*5cff7825Smh27603 */ 226*5cff7825Smh27603 oldlevel = (cpup->level == PM_LEVEL_UNKNOWN) ? highest : cpup->level; 227*5cff7825Smh27603 dip = cpup->dip; 228*5cff7825Smh27603 ASSERT(dip); 229*5cff7825Smh27603 230*5cff7825Smh27603 PPMD(D_CPU, ("%s: old %d, new %d, highest %d, lowest %d\n", 231*5cff7825Smh27603 str, oldlevel, newlevel, highest, lowest)) 232*5cff7825Smh27603 233*5cff7825Smh27603 if (newlevel > oldlevel) { 234*5cff7825Smh27603 chstr = "UP"; 235*5cff7825Smh27603 speedup = 1; 236*5cff7825Smh27603 incr = 1; 237*5cff7825Smh27603 } else if (newlevel < oldlevel) { 238*5cff7825Smh27603 chstr = "DOWN"; 239*5cff7825Smh27603 speedup = 0; 240*5cff7825Smh27603 incr = -1; 241*5cff7825Smh27603 } else 242*5cff7825Smh27603 return (DDI_SUCCESS); 243*5cff7825Smh27603 244*5cff7825Smh27603 /* 245*5cff7825Smh27603 * This loop will execute 1x or 2x depending on 246*5cff7825Smh27603 * number of times we need to change clock rates 247*5cff7825Smh27603 */ 248*5cff7825Smh27603 for (level = oldlevel+incr; level != newlevel+incr; level += incr) { 249*5cff7825Smh27603 /* bring each cpu to next level */ 250*5cff7825Smh27603 for (; cpup; cpup = cpup->next) { 251*5cff7825Smh27603 if (cpup->level == level) 252*5cff7825Smh27603 continue; 253*5cff7825Smh27603 254*5cff7825Smh27603 ret = pm_power(cpup->dip, 0, level); 255*5cff7825Smh27603 PPMD(D_CPU, ("%s: \"%s\", %s to level %d, ret %d\n", 256*5cff7825Smh27603 str, cpup->path, chstr, level, ret)) 257*5cff7825Smh27603 if (ret == DDI_SUCCESS) { 258*5cff7825Smh27603 cpup->level = level; 259*5cff7825Smh27603 cpup->rplvl = PM_LEVEL_UNKNOWN; 260*5cff7825Smh27603 continue; 261*5cff7825Smh27603 } 262*5cff7825Smh27603 263*5cff7825Smh27603 /* 264*5cff7825Smh27603 * if the driver was unable to lower cpu speed, 265*5cff7825Smh27603 * the cpu probably got busy; set the previous 266*5cff7825Smh27603 * cpus back to the original level 267*5cff7825Smh27603 */ 268*5cff7825Smh27603 if (speedup == 0) 269*5cff7825Smh27603 ret = ppm_revert_cpu_power(cpup, level - incr); 270*5cff7825Smh27603 return (ret); 271*5cff7825Smh27603 } 272*5cff7825Smh27603 cpup = domp->devlist; 273*5cff7825Smh27603 274*5cff7825Smh27603 /* 275*5cff7825Smh27603 * set bus resident devices at next speed level 276*5cff7825Smh27603 */ 277*5cff7825Smh27603 ret = ppm_cpu_next(domp, level); 278*5cff7825Smh27603 if (ret != 0) { 279*5cff7825Smh27603 (void) ppm_revert_cpu_power(cpup, level - incr); 280*5cff7825Smh27603 return (ret); 281*5cff7825Smh27603 } 282*5cff7825Smh27603 283*5cff7825Smh27603 /* 284*5cff7825Smh27603 * platform dependent various operations before 285*5cff7825Smh27603 * initiating cpu speed change 286*5cff7825Smh27603 */ 287*5cff7825Smh27603 ret = ppm_cpu_pre_chng(domp, level - incr, speedup); 288*5cff7825Smh27603 if (ret != 0) { 289*5cff7825Smh27603 (void) ppm_revert_cpu_power(cpup, level - incr); 290*5cff7825Smh27603 (void) ppm_cpu_next(domp, level - incr); 291*5cff7825Smh27603 return (ret); 292*5cff7825Smh27603 } 293*5cff7825Smh27603 294*5cff7825Smh27603 /* 295*5cff7825Smh27603 * the following 1us delay is actually required for us3i only. 296*5cff7825Smh27603 * on us3i system, entering estar mode from full requires 297*5cff7825Smh27603 * to set mcu to single fsm state followed by 1us delay 298*5cff7825Smh27603 * before trigger actual transition. The mcu part is 299*5cff7825Smh27603 * handled in us_drv, the delay is here. 300*5cff7825Smh27603 */ 301*5cff7825Smh27603 if ((oldlevel == highest) && (speedup == 0)) 302*5cff7825Smh27603 drv_usecwait(1); 303*5cff7825Smh27603 304*5cff7825Smh27603 /* 305*5cff7825Smh27603 * initiate cpu speed change 306*5cff7825Smh27603 */ 307*5cff7825Smh27603 ret = ppm_cpu_go(domp, level); 308*5cff7825Smh27603 if (ret != 0) { 309*5cff7825Smh27603 (void) ppm_revert_cpu_power(cpup, level - incr); 310*5cff7825Smh27603 (void) ppm_cpu_next(domp, level - incr); 311*5cff7825Smh27603 return (ret); 312*5cff7825Smh27603 } 313*5cff7825Smh27603 314*5cff7825Smh27603 /* 315*5cff7825Smh27603 * platform dependent operations post cpu speed change 316*5cff7825Smh27603 */ 317*5cff7825Smh27603 ret = ppm_cpu_post_chng(domp, level, speedup); 318*5cff7825Smh27603 if (ret != 0) 319*5cff7825Smh27603 return (ret); 320*5cff7825Smh27603 321*5cff7825Smh27603 } /* end of looping each level */ 322*5cff7825Smh27603 323*5cff7825Smh27603 return (DDI_SUCCESS); 324*5cff7825Smh27603 } 325*5cff7825Smh27603 326*5cff7825Smh27603 /* 327*5cff7825Smh27603 * This handles the power-on case where cpu power level is 328*5cff7825Smh27603 * PM_LEVEL_UNKNOWN. Per agreement with OBP, cpus always 329*5cff7825Smh27603 * boot up at full speed. In fact, we must not making calls 330*5cff7825Smh27603 * into tomtppm or schppm to trigger cpu speed change to a 331*5cff7825Smh27603 * different level at early boot time since some cpu may not 332*5cff7825Smh27603 * be ready, causing xc_one() to fail silently. 333*5cff7825Smh27603 * 334*5cff7825Smh27603 * Here we simply call pm_power() to get the power level updated 335*5cff7825Smh27603 * in pm and ppm. Had xc_one() failed silently inside us_power() 336*5cff7825Smh27603 * at this time we're unaffected. 337*5cff7825Smh27603 */ 338*5cff7825Smh27603 boolean_t 339*5cff7825Smh27603 ppm_manage_early_cpus(dev_info_t *dip, int new, int *result) 340*5cff7825Smh27603 { 341*5cff7825Smh27603 ppm_dev_t *ppmd = PPM_GET_PRIVATE(dip); 342*5cff7825Smh27603 int ret; 343*5cff7825Smh27603 344*5cff7825Smh27603 if (ppmd->level == PM_LEVEL_UNKNOWN && new == ppmd->highest) { 345*5cff7825Smh27603 ret = pm_power(dip, 0, new); 346*5cff7825Smh27603 if (ret != DDI_SUCCESS) { 347*5cff7825Smh27603 PPMD(D_CPU, ("ppm_manage_early_cpus: pm_power() " 348*5cff7825Smh27603 "failed to change power level to %d", new)) 349*5cff7825Smh27603 } else { 350*5cff7825Smh27603 ppmd->level = new; 351*5cff7825Smh27603 ppmd->rplvl = PM_LEVEL_UNKNOWN; 352*5cff7825Smh27603 } 353*5cff7825Smh27603 *result = ret; 354*5cff7825Smh27603 return (B_TRUE); 355*5cff7825Smh27603 } 356*5cff7825Smh27603 return (B_FALSE); 357*5cff7825Smh27603 } 358