pm.c 6.2 KB
Newer Older
1 2
/*
 * Copyright (c) 2011-2012 Samsung Electronics Co., Ltd.
3 4
 *		http://www.samsung.com
 *
5
 * EXYNOS - Power Management support
6 7 8 9 10 11 12 13 14 15 16 17
 *
 * Based on arch/arm/mach-s3c2410/pm.c
 * Copyright (c) 2006 Simtec Electronics
 *	Ben Dooks <ben@simtec.co.uk>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 2 as
 * published by the Free Software Foundation.
*/

#include <linux/init.h>
#include <linux/suspend.h>
18
#include <linux/syscore_ops.h>
19
#include <linux/io.h>
20 21
#include <linux/err.h>
#include <linux/clk.h>
22 23 24

#include <asm/cacheflush.h>
#include <asm/hardware/cache-l2x0.h>
25
#include <asm/smp_scu.h>
26
#include <asm/suspend.h>
27 28

#include <plat/cpu.h>
29
#include <plat/pm-common.h>
30
#include <plat/pll.h>
31
#include <plat/regs-srom.h>
32

33
#include <mach/map.h>
34 35

#include "common.h"
36
#include "regs-pmu.h"
37

38 39 40 41
static struct sleep_save exynos5_sys_save[] = {
	SAVE_ITEM(EXYNOS5_SYS_I2C_CFG),
};

42
static struct sleep_save exynos_core_save[] = {
43 44 45 46 47 48
	/* SROM side */
	SAVE_ITEM(S5P_SROM_BW),
	SAVE_ITEM(S5P_SROM_BC0),
	SAVE_ITEM(S5P_SROM_BC1),
	SAVE_ITEM(S5P_SROM_BC2),
	SAVE_ITEM(S5P_SROM_BC3),
49 50
};

51
static u32 exynos_irqwake_intmask = 0xffffffff;
52

53 54 55
/* For Cortex-A9 Diagnostic and Power control register */
static unsigned int save_arm_register[2];

56
static int exynos_cpu_suspend(unsigned long arg)
57
{
58
#ifdef CONFIG_CACHE_L2X0
59
	outer_flush_all();
60
#endif
61

62 63 64
	if (soc_is_exynos5250())
		flush_cache_all();

65 66 67
	/* issue the standby signal into the pm unit. */
	cpu_do_idle();

68 69
	pr_info("Failed to suspend the system\n");
	return 1; /* Aborting suspend */
70 71
}

72
static void exynos_pm_prepare(void)
73
{
74
	unsigned int tmp;
75

76 77 78 79
	/* Set wake-up mask registers */
	__raw_writel(exynos_get_eint_wake_mask(), S5P_EINT_WAKEUP_MASK);
	__raw_writel(exynos_irqwake_intmask & ~(1 << 31), S5P_WAKEUP_MASK);

80
	s3c_pm_do_save(exynos_core_save, ARRAY_SIZE(exynos_core_save));
81

82
	if (soc_is_exynos5250()) {
83
		s3c_pm_do_save(exynos5_sys_save, ARRAY_SIZE(exynos5_sys_save));
84 85 86 87 88
		/* Disable USE_RETENTION of JPEG_MEM_OPTION */
		tmp = __raw_readl(EXYNOS5_JPEG_MEM_OPTION);
		tmp &= ~EXYNOS5_OPTION_USE_RETENTION;
		__raw_writel(tmp, EXYNOS5_JPEG_MEM_OPTION);
	}
89 90 91

	/* Set value of power down register for sleep mode */

92
	exynos_sys_powerdown_conf(SYS_SLEEP);
93 94 95 96
	__raw_writel(S5P_CHECK_SLEEP, S5P_INFORM1);

	/* ensure at least INFORM0 has the resume address */

97
	__raw_writel(virt_to_phys(exynos_cpu_resume), S5P_INFORM0);
98 99
}

100
static int exynos_pm_suspend(void)
101 102 103 104 105 106 107 108 109
{
	unsigned long tmp;

	/* Setting Central Sequence Register for power down mode */

	tmp = __raw_readl(S5P_CENTRAL_SEQ_CONFIGURATION);
	tmp &= ~S5P_CENTRAL_LOWPWR_CFG;
	__raw_writel(tmp, S5P_CENTRAL_SEQ_CONFIGURATION);

110
	/* Setting SEQ_OPTION register */
111

112 113
	tmp = (S5P_USE_STANDBY_WFI0 | S5P_USE_STANDBY_WFE0);
	__raw_writel(tmp, S5P_CENTRAL_SEQ_OPTION);
114

115 116 117 118 119 120 121 122 123 124 125
	if (!soc_is_exynos5250()) {
		/* Save Power control register */
		asm ("mrc p15, 0, %0, c15, c0, 0"
		     : "=r" (tmp) : : "cc");
		save_arm_register[0] = tmp;

		/* Save Diagnostic register */
		asm ("mrc p15, 0, %0, c15, c0, 1"
		     : "=r" (tmp) : : "cc");
		save_arm_register[1] = tmp;
	}
126

127 128 129
	return 0;
}

130
static void exynos_pm_resume(void)
131
{
132 133 134 135 136 137 138 139 140 141 142 143
	unsigned long tmp;

	/*
	 * If PMU failed while entering sleep mode, WFI will be
	 * ignored by PMU and then exiting cpu_do_idle().
	 * S5P_CENTRAL_LOWPWR_CFG bit will not be set automatically
	 * in this situation.
	 */
	tmp = __raw_readl(S5P_CENTRAL_SEQ_CONFIGURATION);
	if (!(tmp & S5P_CENTRAL_LOWPWR_CFG)) {
		tmp |= S5P_CENTRAL_LOWPWR_CFG;
		__raw_writel(tmp, S5P_CENTRAL_SEQ_CONFIGURATION);
144 145
		/* clear the wakeup state register */
		__raw_writel(0x0, S5P_WAKEUP_STAT);
146 147 148
		/* No need to perform below restore code */
		goto early_wakeup;
	}
149 150 151 152 153 154 155 156 157 158 159 160 161
	if (!soc_is_exynos5250()) {
		/* Restore Power control register */
		tmp = save_arm_register[0];
		asm volatile ("mcr p15, 0, %0, c15, c0, 0"
			      : : "r" (tmp)
			      : "cc");

		/* Restore Diagnostic register */
		tmp = save_arm_register[1];
		asm volatile ("mcr p15, 0, %0, c15, c0, 1"
			      : : "r" (tmp)
			      : "cc");
	}
162

163 164 165 166 167 168 169 170 171 172
	/* For release retention */

	__raw_writel((1 << 28), S5P_PAD_RET_MAUDIO_OPTION);
	__raw_writel((1 << 28), S5P_PAD_RET_GPIO_OPTION);
	__raw_writel((1 << 28), S5P_PAD_RET_UART_OPTION);
	__raw_writel((1 << 28), S5P_PAD_RET_MMCA_OPTION);
	__raw_writel((1 << 28), S5P_PAD_RET_MMCB_OPTION);
	__raw_writel((1 << 28), S5P_PAD_RET_EBIA_OPTION);
	__raw_writel((1 << 28), S5P_PAD_RET_EBIB_OPTION);

173 174 175 176
	if (soc_is_exynos5250())
		s3c_pm_do_restore(exynos5_sys_save,
			ARRAY_SIZE(exynos5_sys_save));

177
	s3c_pm_do_restore_core(exynos_core_save, ARRAY_SIZE(exynos_core_save));
178

179
	if (IS_ENABLED(CONFIG_SMP) && !soc_is_exynos5250())
180
		scu_enable(S5P_VA_SCU);
181

182
early_wakeup:
183 184 185 186

	/* Clear SLEEP mode set in INFORM1 */
	__raw_writel(0x0, S5P_INFORM1);

187
	return;
188 189
}

190 191 192
static struct syscore_ops exynos_pm_syscore_ops = {
	.suspend	= exynos_pm_suspend,
	.resume		= exynos_pm_resume,
193 194
};

195 196 197 198 199
/*
 * Suspend Ops
 */

static int exynos_suspend_enter(suspend_state_t state)
200
{
201 202 203 204 205 206 207 208
	int ret;

	s3c_pm_debug_init();

	S3C_PMDBG("%s: suspending the system...\n", __func__);

	S3C_PMDBG("%s: wakeup masks: %08x,%08x\n", __func__,
			exynos_irqwake_intmask, exynos_get_eint_wake_mask());
209

210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226
	if (exynos_irqwake_intmask == -1U
	    && exynos_get_eint_wake_mask() == -1U) {
		pr_err("%s: No wake-up sources!\n", __func__);
		pr_err("%s: Aborting sleep\n", __func__);
		return -EINVAL;
	}

	s3c_pm_save_uarts();
	exynos_pm_prepare();
	flush_cache_all();
	s3c_pm_check_store();

	ret = cpu_suspend(0, exynos_cpu_suspend);
	if (ret)
		return ret;

	s3c_pm_restore_uarts();
227

228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259
	S3C_PMDBG("%s: wakeup stat: %08x\n", __func__,
			__raw_readl(S5P_WAKEUP_STAT));

	s3c_pm_check_restore();

	S3C_PMDBG("%s: resuming the system...\n", __func__);

	return 0;
}

static int exynos_suspend_prepare(void)
{
	s3c_pm_check_prepare();

	return 0;
}

static void exynos_suspend_finish(void)
{
	s3c_pm_check_cleanup();
}

static const struct platform_suspend_ops exynos_suspend_ops = {
	.enter		= exynos_suspend_enter,
	.prepare	= exynos_suspend_prepare,
	.finish		= exynos_suspend_finish,
	.valid		= suspend_valid_only_mem,
};

void __init exynos_pm_init(void)
{
	u32 tmp;
260 261 262 263 264

	/* All wakeup disable */
	tmp = __raw_readl(S5P_WAKEUP_MASK);
	tmp |= ((0xFF << 8) | (0x1F << 1));
	__raw_writel(tmp, S5P_WAKEUP_MASK);
265

266
	register_syscore_ops(&exynos_pm_syscore_ops);
267
	suspend_set_ops(&exynos_suspend_ops);
268
}