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 extern pm_cpupm_t cpupm; 209 210 /* 211 * We only handle _PPC change notifications. 212 */ 213 if (val == CPUPM_PPC_CHANGE_NOTIFICATION && !PM_EVENT_CPUPM) 214 cpudrv_redefine_topspeed(ctx); 215 } 216 217 void 218 cpudrv_install_notify_handler(cpudrv_devstate_t *cpudsp) 219 { 220 cpu_t *cp = cpudsp->cp; 221 cpupm_add_notify_handler(cp, cpudrv_notify_handler, 222 cpudsp->dip); 223 } 224 225 void 226 cpudrv_redefine_topspeed(void *ctx) 227 { 228 /* 229 * This should never happen, unless ppm does not get loaded. 230 */ 231 if (cpupm_redefine_topspeed == NULL) { 232 cmn_err(CE_WARN, "cpudrv_redefine_topspeed: " 233 "cpupm_redefine_topspeed has not been initialized - " 234 "ignoring notification"); 235 return; 236 } 237 238 /* 239 * ppm callback needs to handle redefinition for all CPUs in 240 * the domain. 241 */ 242 (*cpupm_redefine_topspeed)(ctx); 243 } 244 245 boolean_t 246 cpudrv_mach_init(cpudrv_devstate_t *cpudsp) 247 { 248 cpupm_mach_state_t *mach_state; 249 250 mutex_enter(&cpu_lock); 251 cpudsp->cp = cpu_get(cpudsp->cpu_id); 252 mutex_exit(&cpu_lock); 253 if (cpudsp->cp == NULL) { 254 cmn_err(CE_WARN, "cpudrv_mach_pm_init: instance %d: " 255 "can't get cpu_t", ddi_get_instance(cpudsp->dip)); 256 return (B_FALSE); 257 } 258 259 mach_state = (cpupm_mach_state_t *) 260 (cpudsp->cp->cpu_m.mcpu_pm_mach_state); 261 mach_state->ms_dip = cpudsp->dip; 262 return (B_TRUE); 263 } 264 265 uint_t 266 cpudrv_get_speeds(cpudrv_devstate_t *cpudsp, int **speeds) 267 { 268 return (cpupm_get_speeds(cpudsp->cp, speeds)); 269 } 270 271 void 272 cpudrv_free_speeds(int *speeds, uint_t nspeeds) 273 { 274 cpupm_free_speeds(speeds, nspeeds); 275 } 276 277 boolean_t 278 cpudrv_power_ready(void) 279 { 280 return (cpupm_power_ready()); 281 } 282 283 /* ARGSUSED */ 284 void 285 cpudrv_set_supp_freqs(cpudrv_devstate_t *cpudsp) 286 { 287 } 288