Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-only
0002 /*
0003  * linux/arch/arm/mach-omap2/cpuidle34xx.c
0004  *
0005  * OMAP3 CPU IDLE Routines
0006  *
0007  * Copyright (C) 2008 Texas Instruments, Inc.
0008  * Rajendra Nayak <rnayak@ti.com>
0009  *
0010  * Copyright (C) 2007 Texas Instruments, Inc.
0011  * Karthik Dasu <karthik-dp@ti.com>
0012  *
0013  * Copyright (C) 2006 Nokia Corporation
0014  * Tony Lindgren <tony@atomide.com>
0015  *
0016  * Copyright (C) 2005 Texas Instruments, Inc.
0017  * Richard Woodruff <r-woodruff2@ti.com>
0018  *
0019  * Based on pm.c for omap2
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 /* Mach specific information to be recorded in the C-state driver_data */
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  * Possible flag bits for struct omap3_idle_statedata.flags:
0048  *
0049  * OMAP_CPUIDLE_CX_NO_CLKDM_IDLE: don't allow the MPU clockdomain to go
0050  *    inactive.  This in turn prevents the MPU DPLL from entering autoidle
0051  *    mode, so wakeup latency is greatly reduced, at the cost of additional
0052  *    energy consumption.  This also prevents the CORE clockdomain from
0053  *    entering idle.
0054  */
0055 #define OMAP_CPUIDLE_CX_NO_CLKDM_IDLE       BIT(0)
0056 
0057 /*
0058  * Prevent PER OFF if CORE is not in RETention or OFF as this would
0059  * disable PER wakeups completely.
0060  */
0061 static struct omap3_idle_statedata omap3_idle_data[] = {
0062     {
0063         .mpu_state = PWRDM_POWER_ON,
0064         .core_state = PWRDM_POWER_ON,
0065         /* In C1 do not allow PER state lower than CORE state */
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  * omap3_enter_idle - Programs OMAP3 to enter the specified state
0103  * @dev: cpuidle device
0104  * @drv: cpuidle driver
0105  * @index: the index of state to be entered
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     /* Deny idle for C1 */
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      * Call idle CPU PM enter notifier chain so that
0127      * VFP context is saved.
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     /* Execute ARM wfi */
0136     omap_sram_idle();
0137 
0138     /*
0139      * Call idle CPU PM enter notifier chain to restore
0140      * VFP context.
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     /* Re-allow idle for C1 */
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  * next_valid_state - Find next valid C-state
0158  * @dev: cpuidle device
0159  * @drv: cpuidle driver
0160  * @index: Index of currently selected c-state
0161  *
0162  * If the state corresponding to index is valid, index is returned back
0163  * to the caller. Else, this function searches for a lower c-state which is
0164  * still valid (as defined in omap3_power_states[]) and returns its index.
0165  *
0166  * A state is valid if the 'valid' field is enabled and
0167  * if it satisfies the enable_off_mode condition.
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; /* C1 is the default value */
0177 
0178     if (enable_off_mode) {
0179         mpu_deepest_state = PWRDM_POWER_OFF;
0180         /*
0181          * Erratum i583: valable for ES rev < Es1.2 on 3630.
0182          * CORE OFF mode is not supported in a stable form, restrict
0183          * instead the CORE state to RET.
0184          */
0185         if (!IS_PM34XX_ERRATUM(PM_SDRC_WAKEUP_ERRATUM_i583))
0186             core_deepest_state = PWRDM_POWER_OFF;
0187     }
0188 
0189     /* Check if current state is valid */
0190     if ((cx->mpu_state >= mpu_deepest_state) &&
0191         (cx->core_state >= core_deepest_state))
0192         return index;
0193 
0194     /*
0195      * Drop to next valid state.
0196      * Start search from the next (lower) state.
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  * omap3_enter_idle_bm - Checks for any bus activity
0212  * @dev: cpuidle device
0213  * @drv: cpuidle driver
0214  * @index: array index of target state to be programmed
0215  *
0216  * This function checks for any pending activity and then programs
0217  * the device to the specified or a safer state.
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      * Use only C1 if CAM is active.
0229      * CAM does not have wakeup capability in OMAP3.
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      * FIXME: we currently manage device-specific idle states
0238      *        for PER and CORE in combination with CPU-specific
0239      *        idle states.  This is wrong, and device-specific
0240      *        idle management needs to be separated out into
0241      *        its own code.
0242      */
0243 
0244     /* Program PER state */
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     /* Restore original PER state if it was modified */
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  * Numbers based on measurements made in October 2009 for PM optimized kernel
0323  * with CPU freq enabled on device Nokia N900. Assumes OPP2 (main idle OPP,
0324  * and worst case latencies).
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 /* Public functions */
0385 
0386 /**
0387  * omap3_idle_init - Init routine for OMAP3 idle
0388  *
0389  * Registers the OMAP3 specific cpuidle driver to the cpuidle
0390  * framework with the valid set of states.
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 }