pm-imx5.c 5.2 KB
Newer Older
1
/*
2 3
 *  Copyright (C) 2011 Freescale Semiconductor, Inc. All Rights Reserved.
 *
4 5 6 7 8 9 10
 * The code contained herein is licensed under the GNU General Public
 * License. You may obtain a copy of the GNU General Public License
 * Version 2 or later at the following locations:
 *
 * http://www.opensource.org/licenses/gpl-license.html
 * http://www.gnu.org/copyleft/gpl.html
 */
11 12
#include <linux/suspend.h>
#include <linux/clk.h>
13
#include <linux/io.h>
14
#include <linux/err.h>
R
Robert Lee 已提交
15
#include <linux/export.h>
16
#include <asm/cacheflush.h>
17
#include <asm/system_misc.h>
18 19
#include <asm/tlbflush.h>
#include <mach/hardware.h>
20 21

#include "common.h"
22
#include "cpuidle.h"
23 24
#include "crm-regs-imx5.h"

25 26 27 28 29 30 31 32 33
/*
 * The WAIT_UNCLOCKED_POWER_OFF state only requires <= 500ns to exit.
 * This is also the lowest power state possible without affecting
 * non-cpu parts of the system.  For these reasons, imx5 should default
 * to always using this state for cpu idling.  The PM_SUSPEND_STANDBY also
 * uses this state and needs to take no action when registers remain confgiured
 * for this state.
 */
#define IMX5_DEFAULT_CPU_IDLE_STATE WAIT_UNCLOCKED_POWER_OFF
34

35 36 37 38
/*
 * set cpu low power mode before WFI instruction. This function is called
 * mx5 because it can be used for mx50, mx51, and mx53.
 */
39
static void mx5_cpu_lp_set(enum mxc_cpu_pwr_mode mode)
40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100
{
	u32 plat_lpc, arm_srpgcr, ccm_clpcr;
	u32 empgc0, empgc1;
	int stop_mode = 0;

	/* always allow platform to issue a deep sleep mode request */
	plat_lpc = __raw_readl(MXC_CORTEXA8_PLAT_LPC) &
	    ~(MXC_CORTEXA8_PLAT_LPC_DSM);
	ccm_clpcr = __raw_readl(MXC_CCM_CLPCR) & ~(MXC_CCM_CLPCR_LPM_MASK);
	arm_srpgcr = __raw_readl(MXC_SRPG_ARM_SRPGCR) & ~(MXC_SRPGCR_PCR);
	empgc0 = __raw_readl(MXC_SRPG_EMPGC0_SRPGCR) & ~(MXC_SRPGCR_PCR);
	empgc1 = __raw_readl(MXC_SRPG_EMPGC1_SRPGCR) & ~(MXC_SRPGCR_PCR);

	switch (mode) {
	case WAIT_CLOCKED:
		break;
	case WAIT_UNCLOCKED:
		ccm_clpcr |= 0x1 << MXC_CCM_CLPCR_LPM_OFFSET;
		break;
	case WAIT_UNCLOCKED_POWER_OFF:
	case STOP_POWER_OFF:
		plat_lpc |= MXC_CORTEXA8_PLAT_LPC_DSM
			    | MXC_CORTEXA8_PLAT_LPC_DBG_DSM;
		if (mode == WAIT_UNCLOCKED_POWER_OFF) {
			ccm_clpcr |= 0x1 << MXC_CCM_CLPCR_LPM_OFFSET;
			ccm_clpcr &= ~MXC_CCM_CLPCR_VSTBY;
			ccm_clpcr &= ~MXC_CCM_CLPCR_SBYOS;
			stop_mode = 0;
		} else {
			ccm_clpcr |= 0x2 << MXC_CCM_CLPCR_LPM_OFFSET;
			ccm_clpcr |= 0x3 << MXC_CCM_CLPCR_STBY_COUNT_OFFSET;
			ccm_clpcr |= MXC_CCM_CLPCR_VSTBY;
			ccm_clpcr |= MXC_CCM_CLPCR_SBYOS;
			stop_mode = 1;
		}
		arm_srpgcr |= MXC_SRPGCR_PCR;
		break;
	case STOP_POWER_ON:
		ccm_clpcr |= 0x2 << MXC_CCM_CLPCR_LPM_OFFSET;
		break;
	default:
		printk(KERN_WARNING "UNKNOWN cpu power mode: %d\n", mode);
		return;
	}

	__raw_writel(plat_lpc, MXC_CORTEXA8_PLAT_LPC);
	__raw_writel(ccm_clpcr, MXC_CCM_CLPCR);
	__raw_writel(arm_srpgcr, MXC_SRPG_ARM_SRPGCR);

	/* Enable NEON SRPG for all but MX50TO1.0. */
	if (mx50_revision() != IMX_CHIP_REVISION_1_0)
		__raw_writel(arm_srpgcr, MXC_SRPG_NEON_SRPGCR);

	if (stop_mode) {
		empgc0 |= MXC_SRPGCR_PCR;
		empgc1 |= MXC_SRPGCR_PCR;

		__raw_writel(empgc0, MXC_SRPG_EMPGC0_SRPGCR);
		__raw_writel(empgc1, MXC_SRPG_EMPGC1_SRPGCR);
	}
}
101 102 103 104 105 106 107 108

static int mx5_suspend_enter(suspend_state_t state)
{
	switch (state) {
	case PM_SUSPEND_MEM:
		mx5_cpu_lp_set(STOP_POWER_OFF);
		break;
	case PM_SUSPEND_STANDBY:
109
		/* DEFAULT_IDLE_STATE already configured */
110 111 112 113 114 115 116 117 118 119 120 121 122 123 124
		break;
	default:
		return -EINVAL;
	}

	if (state == PM_SUSPEND_MEM) {
		local_flush_tlb_all();
		flush_cache_all();

		/*clear the EMPGC0/1 bits */
		__raw_writel(0, MXC_SRPG_EMPGC0_SRPGCR);
		__raw_writel(0, MXC_SRPG_EMPGC1_SRPGCR);
	}
	cpu_do_idle();

125 126 127
	/* return registers to default idle state */
	mx5_cpu_lp_set(IMX5_DEFAULT_CPU_IDLE_STATE);
	return 0;
128 129 130 131 132 133 134 135 136 137 138 139
}

static int mx5_pm_valid(suspend_state_t state)
{
	return (state > PM_SUSPEND_ON && state <= PM_SUSPEND_MAX);
}

static const struct platform_suspend_ops mx5_suspend_ops = {
	.valid = mx5_pm_valid,
	.enter = mx5_suspend_enter,
};

R
Robert Lee 已提交
140
static inline int imx5_cpu_do_idle(void)
141
{
R
Robert Lee 已提交
142 143 144
	int ret = tzic_enable_wake();

	if (likely(!ret))
145
		cpu_do_idle();
R
Robert Lee 已提交
146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164

	return ret;
}

static void imx5_pm_idle(void)
{
	imx5_cpu_do_idle();
}

static int imx5_cpuidle_enter(struct cpuidle_device *dev,
				struct cpuidle_driver *drv, int idx)
{
	int ret;

	ret = imx5_cpu_do_idle();
	if (ret < 0)
		return ret;

	return idx;
165 166
}

R
Robert Lee 已提交
167 168 169 170 171 172 173 174 175 176 177 178 179 180 181
static struct cpuidle_driver imx5_cpuidle_driver = {
	.name			= "imx5_cpuidle",
	.owner			= THIS_MODULE,
	.en_core_tk_irqen	= 1,
	.states[0]	= {
		.enter			= imx5_cpuidle_enter,
		.exit_latency		= 2,
		.target_residency	= 1,
		.flags			= CPUIDLE_FLAG_TIME_VALID,
		.name			= "IMX5 SRPG",
		.desc			= "CPU state retained,powered off",
	},
	.state_count		= 1,
};

182 183 184 185 186 187 188
static int __init imx5_pm_common_init(void)
{
	int ret;
	struct clk *gpc_dvfs_clk = clk_get(NULL, "gpc_dvfs");

	if (IS_ERR(gpc_dvfs_clk))
		return PTR_ERR(gpc_dvfs_clk);
189

190 191 192
	ret = clk_prepare_enable(gpc_dvfs_clk);
	if (ret)
		return ret;
193

194 195 196 197
	arm_pm_idle = imx5_pm_idle;

	/* Set the registers to the default cpu idle state. */
	mx5_cpu_lp_set(IMX5_DEFAULT_CPU_IDLE_STATE);
198

R
Robert Lee 已提交
199
	imx_cpuidle_init(&imx5_cpuidle_driver);
200 201
	return 0;
}
202 203 204 205 206 207 208

void __init imx51_pm_init(void)
{
	int ret = imx5_pm_common_init();
	if (!ret)
		suspend_set_ops(&mx5_suspend_ops);
}
209 210 211 212 213

void __init imx53_pm_init(void)
{
	imx5_pm_common_init();
}