root/arch/arm/mach-omap2/cpuidle34xx.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. omap3_enter_idle
  2. next_valid_state
  3. omap3_enter_idle_bm
  4. omap3_idle_init

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

/* [<][>][^][v][top][bottom][index][help] */