cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

cpuidle34xx.c (10240B)


      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 */
     37struct omap3_idle_statedata {
     38	u8 mpu_state;
     39	u8 core_state;
     40	u8 per_min_state;
     41	u8 flags;
     42};
     43
     44static 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 */
     61static 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 */
    107static 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	int error;
    113
    114	if (omap_irq_pending() || need_resched())
    115		goto return_sleep_time;
    116
    117	/* Deny idle for C1 */
    118	if (cx->flags & OMAP_CPUIDLE_CX_NO_CLKDM_IDLE) {
    119		clkdm_deny_idle(mpu_pd->pwrdm_clkdms[0]);
    120	} else {
    121		pwrdm_set_next_pwrst(mpu_pd, cx->mpu_state);
    122		pwrdm_set_next_pwrst(core_pd, cx->core_state);
    123	}
    124
    125	/*
    126	 * Call idle CPU PM enter notifier chain so that
    127	 * VFP context is saved.
    128	 */
    129	if (cx->mpu_state == PWRDM_POWER_OFF) {
    130		error = cpu_pm_enter();
    131		if (error)
    132			goto out_clkdm_set;
    133	}
    134
    135	/* Execute ARM wfi */
    136	omap_sram_idle();
    137
    138	/*
    139	 * Call idle CPU PM enter notifier chain to restore
    140	 * VFP context.
    141	 */
    142	if (cx->mpu_state == PWRDM_POWER_OFF &&
    143	    pwrdm_read_prev_pwrst(mpu_pd) == PWRDM_POWER_OFF)
    144		cpu_pm_exit();
    145
    146out_clkdm_set:
    147	/* Re-allow idle for C1 */
    148	if (cx->flags & OMAP_CPUIDLE_CX_NO_CLKDM_IDLE)
    149		clkdm_allow_idle(mpu_pd->pwrdm_clkdms[0]);
    150
    151return_sleep_time:
    152
    153	return index;
    154}
    155
    156/**
    157 * next_valid_state - Find next valid C-state
    158 * @dev: cpuidle device
    159 * @drv: cpuidle driver
    160 * @index: Index of currently selected c-state
    161 *
    162 * If the state corresponding to index is valid, index is returned back
    163 * to the caller. Else, this function searches for a lower c-state which is
    164 * still valid (as defined in omap3_power_states[]) and returns its index.
    165 *
    166 * A state is valid if the 'valid' field is enabled and
    167 * if it satisfies the enable_off_mode condition.
    168 */
    169static int next_valid_state(struct cpuidle_device *dev,
    170			    struct cpuidle_driver *drv, int index)
    171{
    172	struct omap3_idle_statedata *cx = &omap3_idle_data[index];
    173	u32 mpu_deepest_state = PWRDM_POWER_RET;
    174	u32 core_deepest_state = PWRDM_POWER_RET;
    175	int idx;
    176	int next_index = 0; /* C1 is the default value */
    177
    178	if (enable_off_mode) {
    179		mpu_deepest_state = PWRDM_POWER_OFF;
    180		/*
    181		 * Erratum i583: valable for ES rev < Es1.2 on 3630.
    182		 * CORE OFF mode is not supported in a stable form, restrict
    183		 * instead the CORE state to RET.
    184		 */
    185		if (!IS_PM34XX_ERRATUM(PM_SDRC_WAKEUP_ERRATUM_i583))
    186			core_deepest_state = PWRDM_POWER_OFF;
    187	}
    188
    189	/* Check if current state is valid */
    190	if ((cx->mpu_state >= mpu_deepest_state) &&
    191	    (cx->core_state >= core_deepest_state))
    192		return index;
    193
    194	/*
    195	 * Drop to next valid state.
    196	 * Start search from the next (lower) state.
    197	 */
    198	for (idx = index - 1; idx >= 0; idx--) {
    199		cx = &omap3_idle_data[idx];
    200		if ((cx->mpu_state >= mpu_deepest_state) &&
    201		    (cx->core_state >= core_deepest_state)) {
    202			next_index = idx;
    203			break;
    204		}
    205	}
    206
    207	return next_index;
    208}
    209
    210/**
    211 * omap3_enter_idle_bm - Checks for any bus activity
    212 * @dev: cpuidle device
    213 * @drv: cpuidle driver
    214 * @index: array index of target state to be programmed
    215 *
    216 * This function checks for any pending activity and then programs
    217 * the device to the specified or a safer state.
    218 */
    219static int omap3_enter_idle_bm(struct cpuidle_device *dev,
    220			       struct cpuidle_driver *drv,
    221			       int index)
    222{
    223	int new_state_idx, ret;
    224	u8 per_next_state, per_saved_state;
    225	struct omap3_idle_statedata *cx;
    226
    227	/*
    228	 * Use only C1 if CAM is active.
    229	 * CAM does not have wakeup capability in OMAP3.
    230	 */
    231	if (pwrdm_read_pwrst(cam_pd) == PWRDM_POWER_ON)
    232		new_state_idx = drv->safe_state_index;
    233	else
    234		new_state_idx = next_valid_state(dev, drv, index);
    235
    236	/*
    237	 * FIXME: we currently manage device-specific idle states
    238	 *        for PER and CORE in combination with CPU-specific
    239	 *        idle states.  This is wrong, and device-specific
    240	 *        idle management needs to be separated out into
    241	 *        its own code.
    242	 */
    243
    244	/* Program PER state */
    245	cx = &omap3_idle_data[new_state_idx];
    246
    247	per_next_state = pwrdm_read_next_pwrst(per_pd);
    248	per_saved_state = per_next_state;
    249	if (per_next_state < cx->per_min_state) {
    250		per_next_state = cx->per_min_state;
    251		pwrdm_set_next_pwrst(per_pd, per_next_state);
    252	}
    253
    254	ret = omap3_enter_idle(dev, drv, new_state_idx);
    255
    256	/* Restore original PER state if it was modified */
    257	if (per_next_state != per_saved_state)
    258		pwrdm_set_next_pwrst(per_pd, per_saved_state);
    259
    260	return ret;
    261}
    262
    263static struct cpuidle_driver omap3_idle_driver = {
    264	.name             = "omap3_idle",
    265	.owner            = THIS_MODULE,
    266	.states = {
    267		{
    268			.enter		  = omap3_enter_idle_bm,
    269			.exit_latency	  = 2 + 2,
    270			.target_residency = 5,
    271			.name		  = "C1",
    272			.desc		  = "MPU ON + CORE ON",
    273		},
    274		{
    275			.enter		  = omap3_enter_idle_bm,
    276			.exit_latency	  = 10 + 10,
    277			.target_residency = 30,
    278			.name		  = "C2",
    279			.desc		  = "MPU ON + CORE ON",
    280		},
    281		{
    282			.enter		  = omap3_enter_idle_bm,
    283			.exit_latency	  = 50 + 50,
    284			.target_residency = 300,
    285			.name		  = "C3",
    286			.desc		  = "MPU RET + CORE ON",
    287		},
    288		{
    289			.enter		  = omap3_enter_idle_bm,
    290			.exit_latency	  = 1500 + 1800,
    291			.target_residency = 4000,
    292			.name		  = "C4",
    293			.desc		  = "MPU OFF + CORE ON",
    294		},
    295		{
    296			.enter		  = omap3_enter_idle_bm,
    297			.exit_latency	  = 2500 + 7500,
    298			.target_residency = 12000,
    299			.name		  = "C5",
    300			.desc		  = "MPU RET + CORE RET",
    301		},
    302		{
    303			.enter		  = omap3_enter_idle_bm,
    304			.exit_latency	  = 3000 + 8500,
    305			.target_residency = 15000,
    306			.name		  = "C6",
    307			.desc		  = "MPU OFF + CORE RET",
    308		},
    309		{
    310			.enter		  = omap3_enter_idle_bm,
    311			.exit_latency	  = 10000 + 30000,
    312			.target_residency = 30000,
    313			.name		  = "C7",
    314			.desc		  = "MPU OFF + CORE OFF",
    315		},
    316	},
    317	.state_count = ARRAY_SIZE(omap3_idle_data),
    318	.safe_state_index = 0,
    319};
    320
    321/*
    322 * Numbers based on measurements made in October 2009 for PM optimized kernel
    323 * with CPU freq enabled on device Nokia N900. Assumes OPP2 (main idle OPP,
    324 * and worst case latencies).
    325 */
    326static struct cpuidle_driver omap3430_idle_driver = {
    327	.name             = "omap3430_idle",
    328	.owner            = THIS_MODULE,
    329	.states = {
    330		{
    331			.enter		  = omap3_enter_idle_bm,
    332			.exit_latency	  = 110 + 162,
    333			.target_residency = 5,
    334			.name		  = "C1",
    335			.desc		  = "MPU ON + CORE ON",
    336		},
    337		{
    338			.enter		  = omap3_enter_idle_bm,
    339			.exit_latency	  = 106 + 180,
    340			.target_residency = 309,
    341			.name		  = "C2",
    342			.desc		  = "MPU ON + CORE ON",
    343		},
    344		{
    345			.enter		  = omap3_enter_idle_bm,
    346			.exit_latency	  = 107 + 410,
    347			.target_residency = 46057,
    348			.name		  = "C3",
    349			.desc		  = "MPU RET + CORE ON",
    350		},
    351		{
    352			.enter		  = omap3_enter_idle_bm,
    353			.exit_latency	  = 121 + 3374,
    354			.target_residency = 46057,
    355			.name		  = "C4",
    356			.desc		  = "MPU OFF + CORE ON",
    357		},
    358		{
    359			.enter		  = omap3_enter_idle_bm,
    360			.exit_latency	  = 855 + 1146,
    361			.target_residency = 46057,
    362			.name		  = "C5",
    363			.desc		  = "MPU RET + CORE RET",
    364		},
    365		{
    366			.enter		  = omap3_enter_idle_bm,
    367			.exit_latency	  = 7580 + 4134,
    368			.target_residency = 484329,
    369			.name		  = "C6",
    370			.desc		  = "MPU OFF + CORE RET",
    371		},
    372		{
    373			.enter		  = omap3_enter_idle_bm,
    374			.exit_latency	  = 7505 + 15274,
    375			.target_residency = 484329,
    376			.name		  = "C7",
    377			.desc		  = "MPU OFF + CORE OFF",
    378		},
    379	},
    380	.state_count = ARRAY_SIZE(omap3_idle_data),
    381	.safe_state_index = 0,
    382};
    383
    384/* Public functions */
    385
    386/**
    387 * omap3_idle_init - Init routine for OMAP3 idle
    388 *
    389 * Registers the OMAP3 specific cpuidle driver to the cpuidle
    390 * framework with the valid set of states.
    391 */
    392int __init omap3_idle_init(void)
    393{
    394	mpu_pd = pwrdm_lookup("mpu_pwrdm");
    395	core_pd = pwrdm_lookup("core_pwrdm");
    396	per_pd = pwrdm_lookup("per_pwrdm");
    397	cam_pd = pwrdm_lookup("cam_pwrdm");
    398
    399	if (!mpu_pd || !core_pd || !per_pd || !cam_pd)
    400		return -ENODEV;
    401
    402	if (cpu_is_omap3430())
    403		return cpuidle_register(&omap3430_idle_driver, NULL);
    404	else
    405		return cpuidle_register(&omap3_idle_driver, NULL);
    406}