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#include <asm/cpuidle.h> 30 31#include "powerdomain.h" 32#include "clockdomain.h" 33 34#include "pm.h" 35#include "control.h" 36#include "common.h" 37 38/* Mach specific information to be recorded in the C-state driver_data */ 39struct omap3_idle_statedata { 40 u8 mpu_state; 41 u8 core_state; 42 u8 per_min_state; 43 u8 flags; 44}; 45 46static struct powerdomain *mpu_pd, *core_pd, *per_pd, *cam_pd; 47 48/* 49 * Possible flag bits for struct omap3_idle_statedata.flags: 50 * 51 * OMAP_CPUIDLE_CX_NO_CLKDM_IDLE: don't allow the MPU clockdomain to go 52 * inactive. This in turn prevents the MPU DPLL from entering autoidle 53 * mode, so wakeup latency is greatly reduced, at the cost of additional 54 * energy consumption. This also prevents the CORE clockdomain from 55 * entering idle. 56 */ 57#define OMAP_CPUIDLE_CX_NO_CLKDM_IDLE BIT(0) 58 59/* 60 * Prevent PER OFF if CORE is not in RETention or OFF as this would 61 * disable PER wakeups completely. 62 */ 63static struct omap3_idle_statedata omap3_idle_data[] = { 64 { 65 .mpu_state = PWRDM_POWER_ON, 66 .core_state = PWRDM_POWER_ON, 67 /* In C1 do not allow PER state lower than CORE state */ 68 .per_min_state = PWRDM_POWER_ON, 69 .flags = OMAP_CPUIDLE_CX_NO_CLKDM_IDLE, 70 }, 71 { 72 .mpu_state = PWRDM_POWER_ON, 73 .core_state = PWRDM_POWER_ON, 74 .per_min_state = PWRDM_POWER_RET, 75 }, 76 { 77 .mpu_state = PWRDM_POWER_RET, 78 .core_state = PWRDM_POWER_ON, 79 .per_min_state = PWRDM_POWER_RET, 80 }, 81 { 82 .mpu_state = PWRDM_POWER_OFF, 83 .core_state = PWRDM_POWER_ON, 84 .per_min_state = PWRDM_POWER_RET, 85 }, 86 { 87 .mpu_state = PWRDM_POWER_RET, 88 .core_state = PWRDM_POWER_RET, 89 .per_min_state = PWRDM_POWER_OFF, 90 }, 91 { 92 .mpu_state = PWRDM_POWER_OFF, 93 .core_state = PWRDM_POWER_RET, 94 .per_min_state = PWRDM_POWER_OFF, 95 }, 96 { 97 .mpu_state = PWRDM_POWER_OFF, 98 .core_state = PWRDM_POWER_OFF, 99 .per_min_state = PWRDM_POWER_OFF, 100 }, 101}; 102 103/** 104 * omap3_enter_idle - Programs OMAP3 to enter the specified state 105 * @dev: cpuidle device 106 * @drv: cpuidle driver 107 * @index: the index of state to be entered 108 */ 109static int omap3_enter_idle(struct cpuidle_device *dev, 110 struct cpuidle_driver *drv, 111 int index) 112{ 113 struct omap3_idle_statedata *cx = &omap3_idle_data[index]; 114 115 if (omap_irq_pending() || need_resched()) 116 goto return_sleep_time; 117 118 /* Deny idle for C1 */ 119 if (cx->flags & OMAP_CPUIDLE_CX_NO_CLKDM_IDLE) { 120 clkdm_deny_idle(mpu_pd->pwrdm_clkdms[0]); 121 } else { 122 pwrdm_set_next_pwrst(mpu_pd, cx->mpu_state); 123 pwrdm_set_next_pwrst(core_pd, cx->core_state); 124 } 125 126 /* 127 * Call idle CPU PM enter notifier chain so that 128 * VFP context is saved. 129 */ 130 if (cx->mpu_state == PWRDM_POWER_OFF) 131 cpu_pm_enter(); 132 133 /* Execute ARM wfi */ 134 omap_sram_idle(); 135 136 /* 137 * Call idle CPU PM enter notifier chain to restore 138 * VFP context. 139 */ 140 if (cx->mpu_state == PWRDM_POWER_OFF && 141 pwrdm_read_prev_pwrst(mpu_pd) == PWRDM_POWER_OFF) 142 cpu_pm_exit(); 143 144 /* Re-allow idle for C1 */ 145 if (cx->flags & OMAP_CPUIDLE_CX_NO_CLKDM_IDLE) 146 clkdm_allow_idle(mpu_pd->pwrdm_clkdms[0]); 147 148return_sleep_time: 149 150 return index; 151} 152 153/** 154 * next_valid_state - Find next valid C-state 155 * @dev: cpuidle device 156 * @drv: cpuidle driver 157 * @index: Index of currently selected c-state 158 * 159 * If the state corresponding to index is valid, index is returned back 160 * to the caller. Else, this function searches for a lower c-state which is 161 * still valid (as defined in omap3_power_states[]) and returns its index. 162 * 163 * A state is valid if the 'valid' field is enabled and 164 * if it satisfies the enable_off_mode condition. 165 */ 166static int next_valid_state(struct cpuidle_device *dev, 167 struct cpuidle_driver *drv, int index) 168{ 169 struct omap3_idle_statedata *cx = &omap3_idle_data[index]; 170 u32 mpu_deepest_state = PWRDM_POWER_RET; 171 u32 core_deepest_state = PWRDM_POWER_RET; 172 int idx; 173 int next_index = 0; /* C1 is the default value */ 174 175 if (enable_off_mode) { 176 mpu_deepest_state = PWRDM_POWER_OFF; 177 /* 178 * Erratum i583: valable for ES rev < Es1.2 on 3630. 179 * CORE OFF mode is not supported in a stable form, restrict 180 * instead the CORE state to RET. 181 */ 182 if (!IS_PM34XX_ERRATUM(PM_SDRC_WAKEUP_ERRATUM_i583)) 183 core_deepest_state = PWRDM_POWER_OFF; 184 } 185 186 /* Check if current state is valid */ 187 if ((cx->mpu_state >= mpu_deepest_state) && 188 (cx->core_state >= core_deepest_state)) 189 return index; 190 191 /* 192 * Drop to next valid state. 193 * Start search from the next (lower) state. 194 */ 195 for (idx = index - 1; idx >= 0; idx--) { 196 cx = &omap3_idle_data[idx]; 197 if ((cx->mpu_state >= mpu_deepest_state) && 198 (cx->core_state >= core_deepest_state)) { 199 next_index = idx; 200 break; 201 } 202 } 203 204 return next_index; 205} 206 207/** 208 * omap3_enter_idle_bm - Checks for any bus activity 209 * @dev: cpuidle device 210 * @drv: cpuidle driver 211 * @index: array index of target state to be programmed 212 * 213 * This function checks for any pending activity and then programs 214 * the device to the specified or a safer state. 215 */ 216static int omap3_enter_idle_bm(struct cpuidle_device *dev, 217 struct cpuidle_driver *drv, 218 int index) 219{ 220 int new_state_idx, ret; 221 u8 per_next_state, per_saved_state; 222 struct omap3_idle_statedata *cx; 223 224 /* 225 * Use only C1 if CAM is active. 226 * CAM does not have wakeup capability in OMAP3. 227 */ 228 if (pwrdm_read_pwrst(cam_pd) == PWRDM_POWER_ON) 229 new_state_idx = drv->safe_state_index; 230 else 231 new_state_idx = next_valid_state(dev, drv, index); 232 233 /* 234 * FIXME: we currently manage device-specific idle states 235 * for PER and CORE in combination with CPU-specific 236 * idle states. This is wrong, and device-specific 237 * idle management needs to be separated out into 238 * its own code. 239 */ 240 241 /* Program PER state */ 242 cx = &omap3_idle_data[new_state_idx]; 243 244 per_next_state = pwrdm_read_next_pwrst(per_pd); 245 per_saved_state = per_next_state; 246 if (per_next_state < cx->per_min_state) { 247 per_next_state = cx->per_min_state; 248 pwrdm_set_next_pwrst(per_pd, per_next_state); 249 } 250 251 ret = omap3_enter_idle(dev, drv, new_state_idx); 252 253 /* Restore original PER state if it was modified */ 254 if (per_next_state != per_saved_state) 255 pwrdm_set_next_pwrst(per_pd, per_saved_state); 256 257 return ret; 258} 259 260static struct cpuidle_driver omap3_idle_driver = { 261 .name = "omap3_idle", 262 .owner = THIS_MODULE, 263 .states = { 264 { 265 .enter = omap3_enter_idle_bm, 266 .exit_latency = 2 + 2, 267 .target_residency = 5, 268 .name = "C1", 269 .desc = "MPU ON + CORE ON", 270 }, 271 { 272 .enter = omap3_enter_idle_bm, 273 .exit_latency = 10 + 10, 274 .target_residency = 30, 275 .name = "C2", 276 .desc = "MPU ON + CORE ON", 277 }, 278 { 279 .enter = omap3_enter_idle_bm, 280 .exit_latency = 50 + 50, 281 .target_residency = 300, 282 .name = "C3", 283 .desc = "MPU RET + CORE ON", 284 }, 285 { 286 .enter = omap3_enter_idle_bm, 287 .exit_latency = 1500 + 1800, 288 .target_residency = 4000, 289 .name = "C4", 290 .desc = "MPU OFF + CORE ON", 291 }, 292 { 293 .enter = omap3_enter_idle_bm, 294 .exit_latency = 2500 + 7500, 295 .target_residency = 12000, 296 .name = "C5", 297 .desc = "MPU RET + CORE RET", 298 }, 299 { 300 .enter = omap3_enter_idle_bm, 301 .exit_latency = 3000 + 8500, 302 .target_residency = 15000, 303 .name = "C6", 304 .desc = "MPU OFF + CORE RET", 305 }, 306 { 307 .enter = omap3_enter_idle_bm, 308 .exit_latency = 10000 + 30000, 309 .target_residency = 30000, 310 .name = "C7", 311 .desc = "MPU OFF + CORE OFF", 312 }, 313 }, 314 .state_count = ARRAY_SIZE(omap3_idle_data), 315 .safe_state_index = 0, 316}; 317 318/* Public functions */ 319 320/** 321 * omap3_idle_init - Init routine for OMAP3 idle 322 * 323 * Registers the OMAP3 specific cpuidle driver to the cpuidle 324 * framework with the valid set of states. 325 */ 326int __init omap3_idle_init(void) 327{ 328 mpu_pd = pwrdm_lookup("mpu_pwrdm"); 329 core_pd = pwrdm_lookup("core_pwrdm"); 330 per_pd = pwrdm_lookup("per_pwrdm"); 331 cam_pd = pwrdm_lookup("cam_pwrdm"); 332 333 if (!mpu_pd || !core_pd || !per_pd || !cam_pd) 334 return -ENODEV; 335 336 return cpuidle_register(&omap3_idle_driver, NULL); 337} 338