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