pm-imx5.c 4.5 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
#include <asm/tlbflush.h>
19 20

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

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
/*
 * set cpu low power mode before WFI instruction. This function is called
F
Fabio Estevam 已提交
37
 * mx5 because it can be used for mx51, and mx53.
38
 */
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
{
	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);
F
Fabio Estevam 已提交
88
	__raw_writel(arm_srpgcr, MXC_SRPG_NEON_SRPGCR);
89 90 91 92 93 94 95 96 97

	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);
	}
}
98 99 100 101 102 103 104 105

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:
106
		/* DEFAULT_IDLE_STATE already configured */
107 108 109 110 111 112 113 114 115 116 117 118 119 120 121
		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();

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

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 已提交
137
static inline int imx5_cpu_do_idle(void)
138
{
R
Robert Lee 已提交
139 140 141
	int ret = tzic_enable_wake();

	if (likely(!ret))
142
		cpu_do_idle();
R
Robert Lee 已提交
143 144 145 146 147 148 149 150 151

	return ret;
}

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

152 153 154 155 156 157 158
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);
159

160 161 162
	ret = clk_prepare_enable(gpc_dvfs_clk);
	if (ret)
		return ret;
163

164 165 166 167
	arm_pm_idle = imx5_pm_idle;

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

169
	return imx5_cpuidle_init();
170
}
171 172 173 174 175 176 177

void __init imx51_pm_init(void)
{
	int ret = imx5_pm_common_init();
	if (!ret)
		suspend_set_ops(&mx5_suspend_ops);
}
178 179 180 181 182

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