0001
0002
0003
0004
0005
0006
0007
0008
0009
0010
0011
0012
0013
0014
0015
0016
0017
0018
0019
0020
0021
0022 #include <linux/sched.h>
0023 #include <linux/cpuidle.h>
0024 #include <linux/export.h>
0025 #include <linux/cpu_pm.h>
0026 #include <asm/cpuidle.h>
0027
0028 #include "powerdomain.h"
0029 #include "clockdomain.h"
0030
0031 #include "pm.h"
0032 #include "control.h"
0033 #include "common.h"
0034 #include "soc.h"
0035
0036
0037 struct omap3_idle_statedata {
0038 u8 mpu_state;
0039 u8 core_state;
0040 u8 per_min_state;
0041 u8 flags;
0042 };
0043
0044 static struct powerdomain *mpu_pd, *core_pd, *per_pd, *cam_pd;
0045
0046
0047
0048
0049
0050
0051
0052
0053
0054
0055 #define OMAP_CPUIDLE_CX_NO_CLKDM_IDLE BIT(0)
0056
0057
0058
0059
0060
0061 static struct omap3_idle_statedata omap3_idle_data[] = {
0062 {
0063 .mpu_state = PWRDM_POWER_ON,
0064 .core_state = PWRDM_POWER_ON,
0065
0066 .per_min_state = PWRDM_POWER_ON,
0067 .flags = OMAP_CPUIDLE_CX_NO_CLKDM_IDLE,
0068 },
0069 {
0070 .mpu_state = PWRDM_POWER_ON,
0071 .core_state = PWRDM_POWER_ON,
0072 .per_min_state = PWRDM_POWER_RET,
0073 },
0074 {
0075 .mpu_state = PWRDM_POWER_RET,
0076 .core_state = PWRDM_POWER_ON,
0077 .per_min_state = PWRDM_POWER_RET,
0078 },
0079 {
0080 .mpu_state = PWRDM_POWER_OFF,
0081 .core_state = PWRDM_POWER_ON,
0082 .per_min_state = PWRDM_POWER_RET,
0083 },
0084 {
0085 .mpu_state = PWRDM_POWER_RET,
0086 .core_state = PWRDM_POWER_RET,
0087 .per_min_state = PWRDM_POWER_OFF,
0088 },
0089 {
0090 .mpu_state = PWRDM_POWER_OFF,
0091 .core_state = PWRDM_POWER_RET,
0092 .per_min_state = PWRDM_POWER_OFF,
0093 },
0094 {
0095 .mpu_state = PWRDM_POWER_OFF,
0096 .core_state = PWRDM_POWER_OFF,
0097 .per_min_state = PWRDM_POWER_OFF,
0098 },
0099 };
0100
0101
0102
0103
0104
0105
0106
0107 static int omap3_enter_idle(struct cpuidle_device *dev,
0108 struct cpuidle_driver *drv,
0109 int index)
0110 {
0111 struct omap3_idle_statedata *cx = &omap3_idle_data[index];
0112 int error;
0113
0114 if (omap_irq_pending() || need_resched())
0115 goto return_sleep_time;
0116
0117
0118 if (cx->flags & OMAP_CPUIDLE_CX_NO_CLKDM_IDLE) {
0119 clkdm_deny_idle(mpu_pd->pwrdm_clkdms[0]);
0120 } else {
0121 pwrdm_set_next_pwrst(mpu_pd, cx->mpu_state);
0122 pwrdm_set_next_pwrst(core_pd, cx->core_state);
0123 }
0124
0125
0126
0127
0128
0129 if (cx->mpu_state == PWRDM_POWER_OFF) {
0130 error = cpu_pm_enter();
0131 if (error)
0132 goto out_clkdm_set;
0133 }
0134
0135
0136 omap_sram_idle();
0137
0138
0139
0140
0141
0142 if (cx->mpu_state == PWRDM_POWER_OFF &&
0143 pwrdm_read_prev_pwrst(mpu_pd) == PWRDM_POWER_OFF)
0144 cpu_pm_exit();
0145
0146 out_clkdm_set:
0147
0148 if (cx->flags & OMAP_CPUIDLE_CX_NO_CLKDM_IDLE)
0149 clkdm_allow_idle(mpu_pd->pwrdm_clkdms[0]);
0150
0151 return_sleep_time:
0152
0153 return index;
0154 }
0155
0156
0157
0158
0159
0160
0161
0162
0163
0164
0165
0166
0167
0168
0169 static int next_valid_state(struct cpuidle_device *dev,
0170 struct cpuidle_driver *drv, int index)
0171 {
0172 struct omap3_idle_statedata *cx = &omap3_idle_data[index];
0173 u32 mpu_deepest_state = PWRDM_POWER_RET;
0174 u32 core_deepest_state = PWRDM_POWER_RET;
0175 int idx;
0176 int next_index = 0;
0177
0178 if (enable_off_mode) {
0179 mpu_deepest_state = PWRDM_POWER_OFF;
0180
0181
0182
0183
0184
0185 if (!IS_PM34XX_ERRATUM(PM_SDRC_WAKEUP_ERRATUM_i583))
0186 core_deepest_state = PWRDM_POWER_OFF;
0187 }
0188
0189
0190 if ((cx->mpu_state >= mpu_deepest_state) &&
0191 (cx->core_state >= core_deepest_state))
0192 return index;
0193
0194
0195
0196
0197
0198 for (idx = index - 1; idx >= 0; idx--) {
0199 cx = &omap3_idle_data[idx];
0200 if ((cx->mpu_state >= mpu_deepest_state) &&
0201 (cx->core_state >= core_deepest_state)) {
0202 next_index = idx;
0203 break;
0204 }
0205 }
0206
0207 return next_index;
0208 }
0209
0210
0211
0212
0213
0214
0215
0216
0217
0218
0219 static int omap3_enter_idle_bm(struct cpuidle_device *dev,
0220 struct cpuidle_driver *drv,
0221 int index)
0222 {
0223 int new_state_idx, ret;
0224 u8 per_next_state, per_saved_state;
0225 struct omap3_idle_statedata *cx;
0226
0227
0228
0229
0230
0231 if (pwrdm_read_pwrst(cam_pd) == PWRDM_POWER_ON)
0232 new_state_idx = drv->safe_state_index;
0233 else
0234 new_state_idx = next_valid_state(dev, drv, index);
0235
0236
0237
0238
0239
0240
0241
0242
0243
0244
0245 cx = &omap3_idle_data[new_state_idx];
0246
0247 per_next_state = pwrdm_read_next_pwrst(per_pd);
0248 per_saved_state = per_next_state;
0249 if (per_next_state < cx->per_min_state) {
0250 per_next_state = cx->per_min_state;
0251 pwrdm_set_next_pwrst(per_pd, per_next_state);
0252 }
0253
0254 ret = omap3_enter_idle(dev, drv, new_state_idx);
0255
0256
0257 if (per_next_state != per_saved_state)
0258 pwrdm_set_next_pwrst(per_pd, per_saved_state);
0259
0260 return ret;
0261 }
0262
0263 static struct cpuidle_driver omap3_idle_driver = {
0264 .name = "omap3_idle",
0265 .owner = THIS_MODULE,
0266 .states = {
0267 {
0268 .enter = omap3_enter_idle_bm,
0269 .exit_latency = 2 + 2,
0270 .target_residency = 5,
0271 .name = "C1",
0272 .desc = "MPU ON + CORE ON",
0273 },
0274 {
0275 .enter = omap3_enter_idle_bm,
0276 .exit_latency = 10 + 10,
0277 .target_residency = 30,
0278 .name = "C2",
0279 .desc = "MPU ON + CORE ON",
0280 },
0281 {
0282 .enter = omap3_enter_idle_bm,
0283 .exit_latency = 50 + 50,
0284 .target_residency = 300,
0285 .name = "C3",
0286 .desc = "MPU RET + CORE ON",
0287 },
0288 {
0289 .enter = omap3_enter_idle_bm,
0290 .exit_latency = 1500 + 1800,
0291 .target_residency = 4000,
0292 .name = "C4",
0293 .desc = "MPU OFF + CORE ON",
0294 },
0295 {
0296 .enter = omap3_enter_idle_bm,
0297 .exit_latency = 2500 + 7500,
0298 .target_residency = 12000,
0299 .name = "C5",
0300 .desc = "MPU RET + CORE RET",
0301 },
0302 {
0303 .enter = omap3_enter_idle_bm,
0304 .exit_latency = 3000 + 8500,
0305 .target_residency = 15000,
0306 .name = "C6",
0307 .desc = "MPU OFF + CORE RET",
0308 },
0309 {
0310 .enter = omap3_enter_idle_bm,
0311 .exit_latency = 10000 + 30000,
0312 .target_residency = 30000,
0313 .name = "C7",
0314 .desc = "MPU OFF + CORE OFF",
0315 },
0316 },
0317 .state_count = ARRAY_SIZE(omap3_idle_data),
0318 .safe_state_index = 0,
0319 };
0320
0321
0322
0323
0324
0325
0326 static struct cpuidle_driver omap3430_idle_driver = {
0327 .name = "omap3430_idle",
0328 .owner = THIS_MODULE,
0329 .states = {
0330 {
0331 .enter = omap3_enter_idle_bm,
0332 .exit_latency = 110 + 162,
0333 .target_residency = 5,
0334 .name = "C1",
0335 .desc = "MPU ON + CORE ON",
0336 },
0337 {
0338 .enter = omap3_enter_idle_bm,
0339 .exit_latency = 106 + 180,
0340 .target_residency = 309,
0341 .name = "C2",
0342 .desc = "MPU ON + CORE ON",
0343 },
0344 {
0345 .enter = omap3_enter_idle_bm,
0346 .exit_latency = 107 + 410,
0347 .target_residency = 46057,
0348 .name = "C3",
0349 .desc = "MPU RET + CORE ON",
0350 },
0351 {
0352 .enter = omap3_enter_idle_bm,
0353 .exit_latency = 121 + 3374,
0354 .target_residency = 46057,
0355 .name = "C4",
0356 .desc = "MPU OFF + CORE ON",
0357 },
0358 {
0359 .enter = omap3_enter_idle_bm,
0360 .exit_latency = 855 + 1146,
0361 .target_residency = 46057,
0362 .name = "C5",
0363 .desc = "MPU RET + CORE RET",
0364 },
0365 {
0366 .enter = omap3_enter_idle_bm,
0367 .exit_latency = 7580 + 4134,
0368 .target_residency = 484329,
0369 .name = "C6",
0370 .desc = "MPU OFF + CORE RET",
0371 },
0372 {
0373 .enter = omap3_enter_idle_bm,
0374 .exit_latency = 7505 + 15274,
0375 .target_residency = 484329,
0376 .name = "C7",
0377 .desc = "MPU OFF + CORE OFF",
0378 },
0379 },
0380 .state_count = ARRAY_SIZE(omap3_idle_data),
0381 .safe_state_index = 0,
0382 };
0383
0384
0385
0386
0387
0388
0389
0390
0391
0392 int __init omap3_idle_init(void)
0393 {
0394 mpu_pd = pwrdm_lookup("mpu_pwrdm");
0395 core_pd = pwrdm_lookup("core_pwrdm");
0396 per_pd = pwrdm_lookup("per_pwrdm");
0397 cam_pd = pwrdm_lookup("cam_pwrdm");
0398
0399 if (!mpu_pd || !core_pd || !per_pd || !cam_pd)
0400 return -ENODEV;
0401
0402 if (cpu_is_omap3430())
0403 return cpuidle_register(&omap3430_idle_driver, NULL);
0404 else
0405 return cpuidle_register(&omap3_idle_driver, NULL);
0406 }