1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 /* 22 * Copyright 2009 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 */ 25 26 /* 27 * CPU power management driver support for i86pc. 28 */ 29 30 #include <sys/ddi.h> 31 #include <sys/sunddi.h> 32 #include <sys/cpupm.h> 33 #include <sys/cpudrv_mach.h> 34 #include <sys/machsystm.h> 35 #include <sys/cpu_pm.h> 36 #include <sys/cpuvar.h> 37 #include <sys/sdt.h> 38 #include <sys/cpu_idle.h> 39 40 /* 41 * Note that our driver numbers the power levels from lowest to 42 * highest starting at 1 (i.e., the lowest power level is 1 and 43 * the highest power level is cpupm->num_spd). The x86 modules get 44 * their power levels from ACPI which numbers power levels from 45 * highest to lowest starting at 0 (i.e., the lowest power level 46 * is (cpupm->num_spd - 1) and the highest power level is 0). So to 47 * map one of our driver power levels to one understood by ACPI we 48 * simply subtract our driver power level from cpupm->num_spd. Likewise, 49 * to map an ACPI power level to the proper driver power level, we 50 * subtract the ACPI power level from cpupm->num_spd. 51 */ 52 #define PM_2_PLAT_LEVEL(cpupm, pm_level) (cpupm->num_spd - pm_level) 53 #define PLAT_2_PM_LEVEL(cpupm, plat_level) (cpupm->num_spd - plat_level) 54 55 /* 56 * Change CPU speed using interface provided by module. 57 */ 58 int 59 cpudrv_change_speed(cpudrv_devstate_t *cpudsp, cpudrv_pm_spd_t *new_spd) 60 { 61 cpu_t *cp = cpudsp->cp; 62 cpupm_mach_state_t *mach_state = 63 (cpupm_mach_state_t *)cp->cpu_m.mcpu_pm_mach_state; 64 cpudrv_pm_t *cpupm; 65 cpuset_t set; 66 uint32_t plat_level; 67 68 if (!(mach_state->ms_caps & CPUPM_P_STATES)) 69 return (DDI_FAILURE); 70 ASSERT(mach_state->ms_pstate.cma_ops != NULL); 71 cpupm = &(cpudsp->cpudrv_pm); 72 plat_level = PM_2_PLAT_LEVEL(cpupm, new_spd->pm_level); 73 CPUSET_ONLY(set, cp->cpu_id); 74 mach_state->ms_pstate.cma_ops->cpus_change(set, plat_level); 75 76 return (DDI_SUCCESS); 77 } 78 79 /* 80 * Determine the cpu_id for the CPU device. 81 */ 82 boolean_t 83 cpudrv_get_cpu_id(dev_info_t *dip, processorid_t *cpu_id) 84 { 85 return ((*cpu_id = ddi_prop_get_int(DDI_DEV_T_ANY, dip, 86 DDI_PROP_DONTPASS, "reg", -1)) != -1); 87 88 } 89 90 boolean_t 91 cpudrv_is_enabled(cpudrv_devstate_t *cpudsp) 92 { 93 cpupm_mach_state_t *mach_state; 94 95 if (!cpupm_is_enabled(CPUPM_P_STATES) || !cpudrv_enabled) 96 return (B_FALSE); 97 98 /* 99 * Only check the instance specific setting it exists. 100 */ 101 if (cpudsp != NULL && cpudsp->cp != NULL && 102 cpudsp->cp->cpu_m.mcpu_pm_mach_state != NULL) { 103 mach_state = 104 (cpupm_mach_state_t *)cpudsp->cp->cpu_m.mcpu_pm_mach_state; 105 return (mach_state->ms_caps & CPUPM_P_STATES); 106 } 107 108 return (B_TRUE); 109 } 110 111 /* 112 * Is the current thread the thread that is handling the 113 * PPC change notification? 114 */ 115 boolean_t 116 cpudrv_is_governor_thread(cpudrv_pm_t *cpupm) 117 { 118 return (curthread == cpupm->pm_governor_thread); 119 } 120 121 /* 122 * This routine changes the top speed to which the CPUs can transition by: 123 * 124 * - Resetting the up_spd for all speeds lower than the new top speed 125 * to point to the new top speed. 126 * - Updating the framework with a new "normal" (maximum power) for this 127 * device. 128 */ 129 void 130 cpudrv_set_topspeed(void *ctx, int plat_level) 131 { 132 cpudrv_devstate_t *cpudsp; 133 cpudrv_pm_t *cpupm; 134 cpudrv_pm_spd_t *spd; 135 cpudrv_pm_spd_t *top_spd; 136 dev_info_t *dip; 137 int pm_level; 138 int instance; 139 int i; 140 141 dip = ctx; 142 instance = ddi_get_instance(dip); 143 cpudsp = ddi_get_soft_state(cpudrv_state, instance); 144 ASSERT(cpudsp != NULL); 145 146 mutex_enter(&cpudsp->lock); 147 cpupm = &(cpudsp->cpudrv_pm); 148 pm_level = PLAT_2_PM_LEVEL(cpupm, plat_level); 149 for (i = 0, spd = cpupm->head_spd; spd; i++, spd = spd->down_spd) { 150 /* 151 * Don't mess with speeds that are higher than the new 152 * top speed. They should be out of range anyway. 153 */ 154 if (spd->pm_level > pm_level) 155 continue; 156 /* 157 * This is the new top speed. 158 */ 159 if (spd->pm_level == pm_level) 160 top_spd = spd; 161 162 spd->up_spd = top_spd; 163 } 164 cpupm->top_spd = top_spd; 165 166 cpupm->pm_governor_thread = curthread; 167 168 mutex_exit(&cpudsp->lock); 169 170 (void) pm_update_maxpower(dip, 0, top_spd->pm_level); 171 } 172 173 /* 174 * This routine reads the ACPI _PPC object. It's accessed as a callback 175 * by the ppm driver whenever a _PPC change notification is received. 176 */ 177 int 178 cpudrv_get_topspeed(void *ctx) 179 { 180 cpu_t *cp; 181 cpudrv_devstate_t *cpudsp; 182 dev_info_t *dip; 183 int instance; 184 int plat_level; 185 186 dip = ctx; 187 instance = ddi_get_instance(dip); 188 cpudsp = ddi_get_soft_state(cpudrv_state, instance); 189 ASSERT(cpudsp != NULL); 190 cp = cpudsp->cp; 191 plat_level = cpupm_get_top_speed(cp); 192 193 return (plat_level); 194 } 195 196 197 /* 198 * This notification handler is called whenever the ACPI _PPC 199 * object changes. The _PPC is a sort of governor on power levels. 200 * It sets an upper threshold on which, _PSS defined, power levels 201 * are usuable. The _PPC value is dynamic and may change as properties 202 * (i.e., thermal or AC source) of the system change. 203 */ 204 /* ARGSUSED */ 205 static void 206 cpudrv_notify_handler(ACPI_HANDLE obj, UINT32 val, void *ctx) 207 { 208 cpu_t *cp; 209 cpupm_mach_state_t *mach_state; 210 cpudrv_devstate_t *cpudsp; 211 dev_info_t *dip; 212 int instance; 213 extern pm_cpupm_t cpupm; 214 215 dip = ctx; 216 instance = ddi_get_instance(dip); 217 cpudsp = ddi_get_soft_state(cpudrv_state, instance); 218 if (cpudsp == NULL) 219 return; 220 cp = cpudsp->cp; 221 mach_state = (cpupm_mach_state_t *)cp->cpu_m.mcpu_pm_mach_state; 222 if (mach_state == NULL) 223 return; 224 225 /* 226 * We only handle _PPC change notifications. 227 */ 228 if (!PM_EVENT_CPUPM && val == CPUPM_PPC_CHANGE_NOTIFICATION && 229 mach_state->ms_caps & CPUPM_P_STATES) 230 cpudrv_redefine_topspeed(ctx); 231 } 232 233 void 234 cpudrv_install_notify_handler(cpudrv_devstate_t *cpudsp) 235 { 236 cpu_t *cp = cpudsp->cp; 237 cpupm_add_notify_handler(cp, cpudrv_notify_handler, 238 cpudsp->dip); 239 } 240 241 void 242 cpudrv_redefine_topspeed(void *ctx) 243 { 244 /* 245 * This should never happen, unless ppm does not get loaded. 246 */ 247 if (cpupm_redefine_topspeed == NULL) { 248 cmn_err(CE_WARN, "cpudrv_redefine_topspeed: " 249 "cpupm_redefine_topspeed has not been initialized - " 250 "ignoring notification"); 251 return; 252 } 253 254 /* 255 * ppm callback needs to handle redefinition for all CPUs in 256 * the domain. 257 */ 258 (*cpupm_redefine_topspeed)(ctx); 259 } 260 261 boolean_t 262 cpudrv_mach_init(cpudrv_devstate_t *cpudsp) 263 { 264 cpupm_mach_state_t *mach_state; 265 266 mutex_enter(&cpu_lock); 267 cpudsp->cp = cpu_get(cpudsp->cpu_id); 268 mutex_exit(&cpu_lock); 269 if (cpudsp->cp == NULL) { 270 cmn_err(CE_WARN, "cpudrv_mach_pm_init: instance %d: " 271 "can't get cpu_t", ddi_get_instance(cpudsp->dip)); 272 return (B_FALSE); 273 } 274 275 mach_state = (cpupm_mach_state_t *) 276 (cpudsp->cp->cpu_m.mcpu_pm_mach_state); 277 mach_state->ms_dip = cpudsp->dip; 278 return (B_TRUE); 279 } 280 281 uint_t 282 cpudrv_get_speeds(cpudrv_devstate_t *cpudsp, int **speeds) 283 { 284 return (cpupm_get_speeds(cpudsp->cp, speeds)); 285 } 286 287 void 288 cpudrv_free_speeds(int *speeds, uint_t nspeeds) 289 { 290 cpupm_free_speeds(speeds, nspeeds); 291 } 292 293 boolean_t 294 cpudrv_power_ready(void) 295 { 296 return (cpupm_power_ready()); 297 } 298 299 /* ARGSUSED */ 300 void 301 cpudrv_set_supp_freqs(cpudrv_devstate_t *cpudsp) 302 { 303 } 304