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

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

DEFINITIONS

This source file includes following definitions.
  1. omap_enter_idle_simple
  2. omap_enter_idle_smp
  3. omap_enter_idle_coupled
  4. omap4_idle_init

   1 // SPDX-License-Identifier: GPL-2.0-only
   2 /*
   3  * OMAP4+ CPU idle Routines
   4  *
   5  * Copyright (C) 2011-2013 Texas Instruments, Inc.
   6  * Santosh Shilimkar <santosh.shilimkar@ti.com>
   7  * Rajendra Nayak <rnayak@ti.com>
   8  */
   9 
  10 #include <linux/sched.h>
  11 #include <linux/cpuidle.h>
  12 #include <linux/cpu_pm.h>
  13 #include <linux/export.h>
  14 #include <linux/tick.h>
  15 
  16 #include <asm/cpuidle.h>
  17 
  18 #include "common.h"
  19 #include "pm.h"
  20 #include "prm.h"
  21 #include "soc.h"
  22 #include "clockdomain.h"
  23 
  24 #define MAX_CPUS        2
  25 
  26 /* Machine specific information */
  27 struct idle_statedata {
  28         u32 cpu_state;
  29         u32 mpu_logic_state;
  30         u32 mpu_state;
  31         u32 mpu_state_vote;
  32 };
  33 
  34 static struct idle_statedata omap4_idle_data[] = {
  35         {
  36                 .cpu_state = PWRDM_POWER_ON,
  37                 .mpu_state = PWRDM_POWER_ON,
  38                 .mpu_logic_state = PWRDM_POWER_RET,
  39         },
  40         {
  41                 .cpu_state = PWRDM_POWER_OFF,
  42                 .mpu_state = PWRDM_POWER_RET,
  43                 .mpu_logic_state = PWRDM_POWER_RET,
  44         },
  45         {
  46                 .cpu_state = PWRDM_POWER_OFF,
  47                 .mpu_state = PWRDM_POWER_RET,
  48                 .mpu_logic_state = PWRDM_POWER_OFF,
  49         },
  50 };
  51 
  52 static struct idle_statedata omap5_idle_data[] = {
  53         {
  54                 .cpu_state = PWRDM_POWER_ON,
  55                 .mpu_state = PWRDM_POWER_ON,
  56                 .mpu_logic_state = PWRDM_POWER_ON,
  57         },
  58         {
  59                 .cpu_state = PWRDM_POWER_RET,
  60                 .mpu_state = PWRDM_POWER_RET,
  61                 .mpu_logic_state = PWRDM_POWER_RET,
  62         },
  63 };
  64 
  65 static struct powerdomain *mpu_pd, *cpu_pd[MAX_CPUS];
  66 static struct clockdomain *cpu_clkdm[MAX_CPUS];
  67 
  68 static atomic_t abort_barrier;
  69 static bool cpu_done[MAX_CPUS];
  70 static struct idle_statedata *state_ptr = &omap4_idle_data[0];
  71 static DEFINE_RAW_SPINLOCK(mpu_lock);
  72 
  73 /* Private functions */
  74 
  75 /**
  76  * omap_enter_idle_[simple/coupled] - OMAP4PLUS cpuidle entry functions
  77  * @dev: cpuidle device
  78  * @drv: cpuidle driver
  79  * @index: the index of state to be entered
  80  *
  81  * Called from the CPUidle framework to program the device to the
  82  * specified low power state selected by the governor.
  83  * Returns the amount of time spent in the low power state.
  84  */
  85 static int omap_enter_idle_simple(struct cpuidle_device *dev,
  86                         struct cpuidle_driver *drv,
  87                         int index)
  88 {
  89         omap_do_wfi();
  90         return index;
  91 }
  92 
  93 static int omap_enter_idle_smp(struct cpuidle_device *dev,
  94                                struct cpuidle_driver *drv,
  95                                int index)
  96 {
  97         struct idle_statedata *cx = state_ptr + index;
  98         unsigned long flag;
  99 
 100         raw_spin_lock_irqsave(&mpu_lock, flag);
 101         cx->mpu_state_vote++;
 102         if (cx->mpu_state_vote == num_online_cpus()) {
 103                 pwrdm_set_logic_retst(mpu_pd, cx->mpu_logic_state);
 104                 omap_set_pwrdm_state(mpu_pd, cx->mpu_state);
 105         }
 106         raw_spin_unlock_irqrestore(&mpu_lock, flag);
 107 
 108         omap4_enter_lowpower(dev->cpu, cx->cpu_state);
 109 
 110         raw_spin_lock_irqsave(&mpu_lock, flag);
 111         if (cx->mpu_state_vote == num_online_cpus())
 112                 omap_set_pwrdm_state(mpu_pd, PWRDM_POWER_ON);
 113         cx->mpu_state_vote--;
 114         raw_spin_unlock_irqrestore(&mpu_lock, flag);
 115 
 116         return index;
 117 }
 118 
 119 static int omap_enter_idle_coupled(struct cpuidle_device *dev,
 120                         struct cpuidle_driver *drv,
 121                         int index)
 122 {
 123         struct idle_statedata *cx = state_ptr + index;
 124         u32 mpuss_can_lose_context = 0;
 125 
 126         /*
 127          * CPU0 has to wait and stay ON until CPU1 is OFF state.
 128          * This is necessary to honour hardware recommondation
 129          * of triggeing all the possible low power modes once CPU1 is
 130          * out of coherency and in OFF mode.
 131          */
 132         if (dev->cpu == 0 && cpumask_test_cpu(1, cpu_online_mask)) {
 133                 while (pwrdm_read_pwrst(cpu_pd[1]) != PWRDM_POWER_OFF) {
 134                         cpu_relax();
 135 
 136                         /*
 137                          * CPU1 could have already entered & exited idle
 138                          * without hitting off because of a wakeup
 139                          * or a failed attempt to hit off mode.  Check for
 140                          * that here, otherwise we could spin forever
 141                          * waiting for CPU1 off.
 142                          */
 143                         if (cpu_done[1])
 144                             goto fail;
 145 
 146                 }
 147         }
 148 
 149         mpuss_can_lose_context = (cx->mpu_state == PWRDM_POWER_RET) &&
 150                                  (cx->mpu_logic_state == PWRDM_POWER_OFF);
 151 
 152         /* Enter broadcast mode for periodic timers */
 153         tick_broadcast_enable();
 154 
 155         /* Enter broadcast mode for one-shot timers */
 156         tick_broadcast_enter();
 157 
 158         /*
 159          * Call idle CPU PM enter notifier chain so that
 160          * VFP and per CPU interrupt context is saved.
 161          */
 162         cpu_pm_enter();
 163 
 164         if (dev->cpu == 0) {
 165                 pwrdm_set_logic_retst(mpu_pd, cx->mpu_logic_state);
 166                 omap_set_pwrdm_state(mpu_pd, cx->mpu_state);
 167 
 168                 /*
 169                  * Call idle CPU cluster PM enter notifier chain
 170                  * to save GIC and wakeupgen context.
 171                  */
 172                 if (mpuss_can_lose_context)
 173                         cpu_cluster_pm_enter();
 174         }
 175 
 176         omap4_enter_lowpower(dev->cpu, cx->cpu_state);
 177         cpu_done[dev->cpu] = true;
 178 
 179         /* Wakeup CPU1 only if it is not offlined */
 180         if (dev->cpu == 0 && cpumask_test_cpu(1, cpu_online_mask)) {
 181 
 182                 if (IS_PM44XX_ERRATUM(PM_OMAP4_ROM_SMP_BOOT_ERRATUM_GICD) &&
 183                     mpuss_can_lose_context)
 184                         gic_dist_disable();
 185 
 186                 clkdm_deny_idle(cpu_clkdm[1]);
 187                 omap_set_pwrdm_state(cpu_pd[1], PWRDM_POWER_ON);
 188                 clkdm_allow_idle(cpu_clkdm[1]);
 189 
 190                 if (IS_PM44XX_ERRATUM(PM_OMAP4_ROM_SMP_BOOT_ERRATUM_GICD) &&
 191                     mpuss_can_lose_context) {
 192                         while (gic_dist_disabled()) {
 193                                 udelay(1);
 194                                 cpu_relax();
 195                         }
 196                         gic_timer_retrigger();
 197                 }
 198         }
 199 
 200         /*
 201          * Call idle CPU PM exit notifier chain to restore
 202          * VFP and per CPU IRQ context.
 203          */
 204         cpu_pm_exit();
 205 
 206         /*
 207          * Call idle CPU cluster PM exit notifier chain
 208          * to restore GIC and wakeupgen context.
 209          */
 210         if (dev->cpu == 0 && mpuss_can_lose_context)
 211                 cpu_cluster_pm_exit();
 212 
 213         tick_broadcast_exit();
 214 
 215 fail:
 216         cpuidle_coupled_parallel_barrier(dev, &abort_barrier);
 217         cpu_done[dev->cpu] = false;
 218 
 219         return index;
 220 }
 221 
 222 static struct cpuidle_driver omap4_idle_driver = {
 223         .name                           = "omap4_idle",
 224         .owner                          = THIS_MODULE,
 225         .states = {
 226                 {
 227                         /* C1 - CPU0 ON + CPU1 ON + MPU ON */
 228                         .exit_latency = 2 + 2,
 229                         .target_residency = 5,
 230                         .enter = omap_enter_idle_simple,
 231                         .name = "C1",
 232                         .desc = "CPUx ON, MPUSS ON"
 233                 },
 234                 {
 235                         /* C2 - CPU0 OFF + CPU1 OFF + MPU CSWR */
 236                         .exit_latency = 328 + 440,
 237                         .target_residency = 960,
 238                         .flags = CPUIDLE_FLAG_COUPLED,
 239                         .enter = omap_enter_idle_coupled,
 240                         .name = "C2",
 241                         .desc = "CPUx OFF, MPUSS CSWR",
 242                 },
 243                 {
 244                         /* C3 - CPU0 OFF + CPU1 OFF + MPU OSWR */
 245                         .exit_latency = 460 + 518,
 246                         .target_residency = 1100,
 247                         .flags = CPUIDLE_FLAG_COUPLED,
 248                         .enter = omap_enter_idle_coupled,
 249                         .name = "C3",
 250                         .desc = "CPUx OFF, MPUSS OSWR",
 251                 },
 252         },
 253         .state_count = ARRAY_SIZE(omap4_idle_data),
 254         .safe_state_index = 0,
 255 };
 256 
 257 static struct cpuidle_driver omap5_idle_driver = {
 258         .name                           = "omap5_idle",
 259         .owner                          = THIS_MODULE,
 260         .states = {
 261                 {
 262                         /* C1 - CPU0 ON + CPU1 ON + MPU ON */
 263                         .exit_latency = 2 + 2,
 264                         .target_residency = 5,
 265                         .enter = omap_enter_idle_simple,
 266                         .name = "C1",
 267                         .desc = "CPUx WFI, MPUSS ON"
 268                 },
 269                 {
 270                         /* C2 - CPU0 RET + CPU1 RET + MPU CSWR */
 271                         .exit_latency = 48 + 60,
 272                         .target_residency = 100,
 273                         .flags = CPUIDLE_FLAG_TIMER_STOP,
 274                         .enter = omap_enter_idle_smp,
 275                         .name = "C2",
 276                         .desc = "CPUx CSWR, MPUSS CSWR",
 277                 },
 278         },
 279         .state_count = ARRAY_SIZE(omap5_idle_data),
 280         .safe_state_index = 0,
 281 };
 282 
 283 /* Public functions */
 284 
 285 /**
 286  * omap4_idle_init - Init routine for OMAP4+ idle
 287  *
 288  * Registers the OMAP4+ specific cpuidle driver to the cpuidle
 289  * framework with the valid set of states.
 290  */
 291 int __init omap4_idle_init(void)
 292 {
 293         struct cpuidle_driver *idle_driver;
 294 
 295         if (soc_is_omap54xx()) {
 296                 state_ptr = &omap5_idle_data[0];
 297                 idle_driver = &omap5_idle_driver;
 298         } else {
 299                 state_ptr = &omap4_idle_data[0];
 300                 idle_driver = &omap4_idle_driver;
 301         }
 302 
 303         mpu_pd = pwrdm_lookup("mpu_pwrdm");
 304         cpu_pd[0] = pwrdm_lookup("cpu0_pwrdm");
 305         cpu_pd[1] = pwrdm_lookup("cpu1_pwrdm");
 306         if ((!mpu_pd) || (!cpu_pd[0]) || (!cpu_pd[1]))
 307                 return -ENODEV;
 308 
 309         cpu_clkdm[0] = clkdm_lookup("mpu0_clkdm");
 310         cpu_clkdm[1] = clkdm_lookup("mpu1_clkdm");
 311         if (!cpu_clkdm[0] || !cpu_clkdm[1])
 312                 return -ENODEV;
 313 
 314         return cpuidle_register(idle_driver, cpu_online_mask);
 315 }

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