1 /* 2 * linux/arch/arm/mach-omap2/cpuidle34xx.c 3 * 4 * OMAP3 CPU IDLE Routines 5 * 6 * Copyright (C) 2008 Texas Instruments, Inc. 7 * Rajendra Nayak <rnayak@ti.com> 8 * 9 * Copyright (C) 2007 Texas Instruments, Inc. 10 * Karthik Dasu <karthik-dp@ti.com> 11 * 12 * Copyright (C) 2006 Nokia Corporation 13 * Tony Lindgren <tony@atomide.com> 14 * 15 * Copyright (C) 2005 Texas Instruments, Inc. 16 * Richard Woodruff <r-woodruff2@ti.com> 17 * 18 * Based on pm.c for omap2 19 * 20 * This program is free software; you can redistribute it and/or modify 21 * it under the terms of the GNU General Public License version 2 as 22 * published by the Free Software Foundation. 23 */ 24 25 #include <linux/sched.h> 26 #include <linux/cpuidle.h> 27 #include <linux/export.h> 28 #include <linux/cpu_pm.h> 29 30 #include <plat/prcm.h> 31 #include <plat/irqs.h> 32 #include "powerdomain.h" 33 #include "clockdomain.h" 34 35 #include "pm.h" 36 #include "control.h" 37 #include "common.h" 38 39 /* Mach specific information to be recorded in the C-state driver_data */ 40 struct omap3_idle_statedata { 41 u32 mpu_state; 42 u32 core_state; 43 }; 44 45 static struct omap3_idle_statedata omap3_idle_data[] = { 46 { 47 .mpu_state = PWRDM_POWER_ON, 48 .core_state = PWRDM_POWER_ON, 49 }, 50 { 51 .mpu_state = PWRDM_POWER_ON, 52 .core_state = PWRDM_POWER_ON, 53 }, 54 { 55 .mpu_state = PWRDM_POWER_RET, 56 .core_state = PWRDM_POWER_ON, 57 }, 58 { 59 .mpu_state = PWRDM_POWER_OFF, 60 .core_state = PWRDM_POWER_ON, 61 }, 62 { 63 .mpu_state = PWRDM_POWER_RET, 64 .core_state = PWRDM_POWER_RET, 65 }, 66 { 67 .mpu_state = PWRDM_POWER_OFF, 68 .core_state = PWRDM_POWER_RET, 69 }, 70 { 71 .mpu_state = PWRDM_POWER_OFF, 72 .core_state = PWRDM_POWER_OFF, 73 }, 74 }; 75 76 static struct powerdomain *mpu_pd, *core_pd, *per_pd, *cam_pd; 77 78 static int __omap3_enter_idle(struct cpuidle_device *dev, 79 struct cpuidle_driver *drv, 80 int index) 81 { 82 struct omap3_idle_statedata *cx = &omap3_idle_data[index]; 83 u32 mpu_state = cx->mpu_state, core_state = cx->core_state; 84 85 local_fiq_disable(); 86 87 pwrdm_set_next_pwrst(mpu_pd, mpu_state); 88 pwrdm_set_next_pwrst(core_pd, core_state); 89 90 if (omap_irq_pending() || need_resched()) 91 goto return_sleep_time; 92 93 /* Deny idle for C1 */ 94 if (index == 0) { 95 clkdm_deny_idle(mpu_pd->pwrdm_clkdms[0]); 96 clkdm_deny_idle(core_pd->pwrdm_clkdms[0]); 97 } 98 99 /* 100 * Call idle CPU PM enter notifier chain so that 101 * VFP context is saved. 102 */ 103 if (mpu_state == PWRDM_POWER_OFF) 104 cpu_pm_enter(); 105 106 /* Execute ARM wfi */ 107 omap_sram_idle(); 108 109 /* 110 * Call idle CPU PM enter notifier chain to restore 111 * VFP context. 112 */ 113 if (pwrdm_read_prev_pwrst(mpu_pd) == PWRDM_POWER_OFF) 114 cpu_pm_exit(); 115 116 /* Re-allow idle for C1 */ 117 if (index == 0) { 118 clkdm_allow_idle(mpu_pd->pwrdm_clkdms[0]); 119 clkdm_allow_idle(core_pd->pwrdm_clkdms[0]); 120 } 121 122 return_sleep_time: 123 124 local_fiq_enable(); 125 126 return index; 127 } 128 129 /** 130 * omap3_enter_idle - Programs OMAP3 to enter the specified state 131 * @dev: cpuidle device 132 * @drv: cpuidle driver 133 * @index: the index of state to be entered 134 * 135 * Called from the CPUidle framework to program the device to the 136 * specified target state selected by the governor. 137 */ 138 static inline int omap3_enter_idle(struct cpuidle_device *dev, 139 struct cpuidle_driver *drv, 140 int index) 141 { 142 return cpuidle_wrap_enter(dev, drv, index, __omap3_enter_idle); 143 } 144 145 /** 146 * next_valid_state - Find next valid C-state 147 * @dev: cpuidle device 148 * @drv: cpuidle driver 149 * @index: Index of currently selected c-state 150 * 151 * If the state corresponding to index is valid, index is returned back 152 * to the caller. Else, this function searches for a lower c-state which is 153 * still valid (as defined in omap3_power_states[]) and returns its index. 154 * 155 * A state is valid if the 'valid' field is enabled and 156 * if it satisfies the enable_off_mode condition. 157 */ 158 static int next_valid_state(struct cpuidle_device *dev, 159 struct cpuidle_driver *drv, int index) 160 { 161 struct omap3_idle_statedata *cx = &omap3_idle_data[index]; 162 u32 mpu_deepest_state = PWRDM_POWER_RET; 163 u32 core_deepest_state = PWRDM_POWER_RET; 164 int idx; 165 int next_index = 0; /* C1 is the default value */ 166 167 if (enable_off_mode) { 168 mpu_deepest_state = PWRDM_POWER_OFF; 169 /* 170 * Erratum i583: valable for ES rev < Es1.2 on 3630. 171 * CORE OFF mode is not supported in a stable form, restrict 172 * instead the CORE state to RET. 173 */ 174 if (!IS_PM34XX_ERRATUM(PM_SDRC_WAKEUP_ERRATUM_i583)) 175 core_deepest_state = PWRDM_POWER_OFF; 176 } 177 178 /* Check if current state is valid */ 179 if ((cx->mpu_state >= mpu_deepest_state) && 180 (cx->core_state >= core_deepest_state)) 181 return index; 182 183 /* 184 * Drop to next valid state. 185 * Start search from the next (lower) state. 186 */ 187 for (idx = index - 1; idx >= 0; idx--) { 188 cx = &omap3_idle_data[idx]; 189 if ((cx->mpu_state >= mpu_deepest_state) && 190 (cx->core_state >= core_deepest_state)) { 191 next_index = idx; 192 break; 193 } 194 } 195 196 return next_index; 197 } 198 199 /** 200 * omap3_enter_idle_bm - Checks for any bus activity 201 * @dev: cpuidle device 202 * @drv: cpuidle driver 203 * @index: array index of target state to be programmed 204 * 205 * This function checks for any pending activity and then programs 206 * the device to the specified or a safer state. 207 */ 208 static int omap3_enter_idle_bm(struct cpuidle_device *dev, 209 struct cpuidle_driver *drv, 210 int index) 211 { 212 int new_state_idx; 213 u32 core_next_state, per_next_state = 0, per_saved_state = 0; 214 struct omap3_idle_statedata *cx; 215 int ret; 216 217 /* 218 * Use only C1 if CAM is active. 219 * CAM does not have wakeup capability in OMAP3. 220 */ 221 if (pwrdm_read_pwrst(cam_pd) == PWRDM_POWER_ON) 222 new_state_idx = drv->safe_state_index; 223 else 224 new_state_idx = next_valid_state(dev, drv, index); 225 226 /* 227 * FIXME: we currently manage device-specific idle states 228 * for PER and CORE in combination with CPU-specific 229 * idle states. This is wrong, and device-specific 230 * idle management needs to be separated out into 231 * its own code. 232 */ 233 234 /* Program PER state */ 235 cx = &omap3_idle_data[new_state_idx]; 236 core_next_state = cx->core_state; 237 per_next_state = per_saved_state = pwrdm_read_next_pwrst(per_pd); 238 if (new_state_idx == 0) { 239 /* In C1 do not allow PER state lower than CORE state */ 240 if (per_next_state < core_next_state) 241 per_next_state = core_next_state; 242 } else { 243 /* 244 * Prevent PER OFF if CORE is not in RETention or OFF as this 245 * would disable PER wakeups completely. 246 */ 247 if ((per_next_state == PWRDM_POWER_OFF) && 248 (core_next_state > PWRDM_POWER_RET)) 249 per_next_state = PWRDM_POWER_RET; 250 } 251 252 /* Are we changing PER target state? */ 253 if (per_next_state != per_saved_state) 254 pwrdm_set_next_pwrst(per_pd, per_next_state); 255 256 ret = omap3_enter_idle(dev, drv, new_state_idx); 257 258 /* Restore original PER state if it was modified */ 259 if (per_next_state != per_saved_state) 260 pwrdm_set_next_pwrst(per_pd, per_saved_state); 261 262 return ret; 263 } 264 265 DEFINE_PER_CPU(struct cpuidle_device, omap3_idle_dev); 266 267 struct cpuidle_driver omap3_idle_driver = { 268 .name = "omap3_idle", 269 .owner = THIS_MODULE, 270 .states = { 271 { 272 .enter = omap3_enter_idle_bm, 273 .exit_latency = 2 + 2, 274 .target_residency = 5, 275 .flags = CPUIDLE_FLAG_TIME_VALID, 276 .name = "C1", 277 .desc = "MPU ON + CORE ON", 278 }, 279 { 280 .enter = omap3_enter_idle_bm, 281 .exit_latency = 10 + 10, 282 .target_residency = 30, 283 .flags = CPUIDLE_FLAG_TIME_VALID, 284 .name = "C2", 285 .desc = "MPU ON + CORE ON", 286 }, 287 { 288 .enter = omap3_enter_idle_bm, 289 .exit_latency = 50 + 50, 290 .target_residency = 300, 291 .flags = CPUIDLE_FLAG_TIME_VALID, 292 .name = "C3", 293 .desc = "MPU RET + CORE ON", 294 }, 295 { 296 .enter = omap3_enter_idle_bm, 297 .exit_latency = 1500 + 1800, 298 .target_residency = 4000, 299 .flags = CPUIDLE_FLAG_TIME_VALID, 300 .name = "C4", 301 .desc = "MPU OFF + CORE ON", 302 }, 303 { 304 .enter = omap3_enter_idle_bm, 305 .exit_latency = 2500 + 7500, 306 .target_residency = 12000, 307 .flags = CPUIDLE_FLAG_TIME_VALID, 308 .name = "C5", 309 .desc = "MPU RET + CORE RET", 310 }, 311 { 312 .enter = omap3_enter_idle_bm, 313 .exit_latency = 3000 + 8500, 314 .target_residency = 15000, 315 .flags = CPUIDLE_FLAG_TIME_VALID, 316 .name = "C6", 317 .desc = "MPU OFF + CORE RET", 318 }, 319 { 320 .enter = omap3_enter_idle_bm, 321 .exit_latency = 10000 + 30000, 322 .target_residency = 30000, 323 .flags = CPUIDLE_FLAG_TIME_VALID, 324 .name = "C7", 325 .desc = "MPU OFF + CORE OFF", 326 }, 327 }, 328 .state_count = ARRAY_SIZE(omap3_idle_data), 329 .safe_state_index = 0, 330 }; 331 332 /** 333 * omap3_idle_init - Init routine for OMAP3 idle 334 * 335 * Registers the OMAP3 specific cpuidle driver to the cpuidle 336 * framework with the valid set of states. 337 */ 338 int __init omap3_idle_init(void) 339 { 340 struct cpuidle_device *dev; 341 342 mpu_pd = pwrdm_lookup("mpu_pwrdm"); 343 core_pd = pwrdm_lookup("core_pwrdm"); 344 per_pd = pwrdm_lookup("per_pwrdm"); 345 cam_pd = pwrdm_lookup("cam_pwrdm"); 346 347 if (!mpu_pd || !core_pd || !per_pd || !cam_pd) 348 return -ENODEV; 349 350 cpuidle_register_driver(&omap3_idle_driver); 351 352 dev = &per_cpu(omap3_idle_dev, smp_processor_id()); 353 dev->cpu = 0; 354 355 if (cpuidle_register_device(dev)) { 356 printk(KERN_ERR "%s: CPUidle register device failed\n", 357 __func__); 358 return -EIO; 359 } 360 361 return 0; 362 } 363