xref: /openbmc/linux/arch/arm/mach-exynos/pm.c (revision 318fd20b)
1c9347101SJongpill Lee /*
2c9347101SJongpill Lee  * Copyright (c) 2011-2012 Samsung Electronics Co., Ltd.
383014579SKukjin Kim  *		http://www.samsung.com
483014579SKukjin Kim  *
5c9347101SJongpill Lee  * EXYNOS - Power Management support
683014579SKukjin Kim  *
783014579SKukjin Kim  * Based on arch/arm/mach-s3c2410/pm.c
883014579SKukjin Kim  * Copyright (c) 2006 Simtec Electronics
983014579SKukjin Kim  *	Ben Dooks <ben@simtec.co.uk>
1083014579SKukjin Kim  *
1183014579SKukjin Kim  * This program is free software; you can redistribute it and/or modify
1283014579SKukjin Kim  * it under the terms of the GNU General Public License version 2 as
1383014579SKukjin Kim  * published by the Free Software Foundation.
1483014579SKukjin Kim */
1583014579SKukjin Kim 
1683014579SKukjin Kim #include <linux/init.h>
1783014579SKukjin Kim #include <linux/suspend.h>
1883014579SKukjin Kim #include <linux/syscore_ops.h>
1985f9f908SDaniel Lezcano #include <linux/cpu_pm.h>
2083014579SKukjin Kim #include <linux/io.h>
21dd8ac696STomasz Figa #include <linux/irqchip/arm-gic.h>
2283014579SKukjin Kim #include <linux/err.h>
2383014579SKukjin Kim #include <linux/clk.h>
2483014579SKukjin Kim 
2583014579SKukjin Kim #include <asm/cacheflush.h>
2683014579SKukjin Kim #include <asm/hardware/cache-l2x0.h>
2763b870f1SShawn Guo #include <asm/smp_scu.h>
28d710aa31STomasz Figa #include <asm/suspend.h>
2983014579SKukjin Kim 
30d710aa31STomasz Figa #include <plat/pm-common.h>
3183014579SKukjin Kim #include <plat/pll.h>
3283014579SKukjin Kim #include <plat/regs-srom.h>
3383014579SKukjin Kim 
349c9239afSKukjin Kim #include <mach/map.h>
35ccd458c1SKukjin Kim 
36ccd458c1SKukjin Kim #include "common.h"
3765c9a853SKukjin Kim #include "regs-pmu.h"
38318fd20bSPankaj Dubey #include "regs-sys.h"
3983014579SKukjin Kim 
40dd8ac696STomasz Figa /**
41dd8ac696STomasz Figa  * struct exynos_wkup_irq - Exynos GIC to PMU IRQ mapping
42dd8ac696STomasz Figa  * @hwirq: Hardware IRQ signal of the GIC
43dd8ac696STomasz Figa  * @mask: Mask in PMU wake-up mask register
44dd8ac696STomasz Figa  */
45dd8ac696STomasz Figa struct exynos_wkup_irq {
46dd8ac696STomasz Figa 	unsigned int hwirq;
47dd8ac696STomasz Figa 	u32 mask;
48dd8ac696STomasz Figa };
49dd8ac696STomasz Figa 
5086ffb0e8SAbhilash Kesavan static struct sleep_save exynos5_sys_save[] = {
5186ffb0e8SAbhilash Kesavan 	SAVE_ITEM(EXYNOS5_SYS_I2C_CFG),
5286ffb0e8SAbhilash Kesavan };
5386ffb0e8SAbhilash Kesavan 
54c9347101SJongpill Lee static struct sleep_save exynos_core_save[] = {
5583014579SKukjin Kim 	/* SROM side */
5683014579SKukjin Kim 	SAVE_ITEM(S5P_SROM_BW),
5783014579SKukjin Kim 	SAVE_ITEM(S5P_SROM_BC0),
5883014579SKukjin Kim 	SAVE_ITEM(S5P_SROM_BC1),
5983014579SKukjin Kim 	SAVE_ITEM(S5P_SROM_BC2),
6083014579SKukjin Kim 	SAVE_ITEM(S5P_SROM_BC3),
6183014579SKukjin Kim };
6283014579SKukjin Kim 
63dd8ac696STomasz Figa /*
64dd8ac696STomasz Figa  * GIC wake-up support
65dd8ac696STomasz Figa  */
66dd8ac696STomasz Figa 
67d710aa31STomasz Figa static u32 exynos_irqwake_intmask = 0xffffffff;
6883014579SKukjin Kim 
69dd8ac696STomasz Figa static const struct exynos_wkup_irq exynos4_wkup_irq[] = {
70dd8ac696STomasz Figa 	{ 76, BIT(1) }, /* RTC alarm */
71dd8ac696STomasz Figa 	{ 77, BIT(2) }, /* RTC tick */
72dd8ac696STomasz Figa 	{ /* sentinel */ },
73dd8ac696STomasz Figa };
74dd8ac696STomasz Figa 
75dd8ac696STomasz Figa static const struct exynos_wkup_irq exynos5250_wkup_irq[] = {
76dd8ac696STomasz Figa 	{ 75, BIT(1) }, /* RTC alarm */
77dd8ac696STomasz Figa 	{ 76, BIT(2) }, /* RTC tick */
78dd8ac696STomasz Figa 	{ /* sentinel */ },
79dd8ac696STomasz Figa };
80dd8ac696STomasz Figa 
81dd8ac696STomasz Figa static int exynos_irq_set_wake(struct irq_data *data, unsigned int state)
82dd8ac696STomasz Figa {
83dd8ac696STomasz Figa 	const struct exynos_wkup_irq *wkup_irq;
84dd8ac696STomasz Figa 
85dd8ac696STomasz Figa 	if (soc_is_exynos5250())
86dd8ac696STomasz Figa 		wkup_irq = exynos5250_wkup_irq;
87dd8ac696STomasz Figa 	else
88dd8ac696STomasz Figa 		wkup_irq = exynos4_wkup_irq;
89dd8ac696STomasz Figa 
90dd8ac696STomasz Figa 	while (wkup_irq->mask) {
91dd8ac696STomasz Figa 		if (wkup_irq->hwirq == data->hwirq) {
92dd8ac696STomasz Figa 			if (!state)
93dd8ac696STomasz Figa 				exynos_irqwake_intmask |= wkup_irq->mask;
94dd8ac696STomasz Figa 			else
95dd8ac696STomasz Figa 				exynos_irqwake_intmask &= ~wkup_irq->mask;
96dd8ac696STomasz Figa 			return 0;
97dd8ac696STomasz Figa 		}
98dd8ac696STomasz Figa 		++wkup_irq;
99dd8ac696STomasz Figa 	}
100dd8ac696STomasz Figa 
101dd8ac696STomasz Figa 	return -ENOENT;
102dd8ac696STomasz Figa }
103dd8ac696STomasz Figa 
104d3af6976SLeela Krishna Amudala /**
105d3af6976SLeela Krishna Amudala  * exynos_core_power_down : power down the specified cpu
106d3af6976SLeela Krishna Amudala  * @cpu : the cpu to power down
107d3af6976SLeela Krishna Amudala  *
108d3af6976SLeela Krishna Amudala  * Power down the specified cpu. The sequence must be finished by a
109d3af6976SLeela Krishna Amudala  * call to cpu_do_idle()
110d3af6976SLeela Krishna Amudala  *
111d3af6976SLeela Krishna Amudala  */
112d3af6976SLeela Krishna Amudala void exynos_cpu_power_down(int cpu)
113d3af6976SLeela Krishna Amudala {
114d3af6976SLeela Krishna Amudala 	__raw_writel(0, EXYNOS_ARM_CORE_CONFIGURATION(cpu));
115d3af6976SLeela Krishna Amudala }
116d3af6976SLeela Krishna Amudala 
117d3af6976SLeela Krishna Amudala /**
118d3af6976SLeela Krishna Amudala  * exynos_cpu_power_up : power up the specified cpu
119d3af6976SLeela Krishna Amudala  * @cpu : the cpu to power up
120d3af6976SLeela Krishna Amudala  *
121d3af6976SLeela Krishna Amudala  * Power up the specified cpu
122d3af6976SLeela Krishna Amudala  */
123d3af6976SLeela Krishna Amudala void exynos_cpu_power_up(int cpu)
124d3af6976SLeela Krishna Amudala {
125d3af6976SLeela Krishna Amudala 	__raw_writel(S5P_CORE_LOCAL_PWR_EN,
126d3af6976SLeela Krishna Amudala 		     EXYNOS_ARM_CORE_CONFIGURATION(cpu));
127d3af6976SLeela Krishna Amudala }
128d3af6976SLeela Krishna Amudala 
129d3af6976SLeela Krishna Amudala /**
130d3af6976SLeela Krishna Amudala  * exynos_cpu_power_state : returns the power state of the cpu
131d3af6976SLeela Krishna Amudala  * @cpu : the cpu to retrieve the power state from
132d3af6976SLeela Krishna Amudala  *
133d3af6976SLeela Krishna Amudala  */
134d3af6976SLeela Krishna Amudala int exynos_cpu_power_state(int cpu)
135d3af6976SLeela Krishna Amudala {
136d3af6976SLeela Krishna Amudala 	return (__raw_readl(EXYNOS_ARM_CORE_STATUS(cpu)) &
137d3af6976SLeela Krishna Amudala 			S5P_CORE_LOCAL_PWR_EN);
138d3af6976SLeela Krishna Amudala }
139d3af6976SLeela Krishna Amudala 
140096d21c6SAbhilash Kesavan /**
141096d21c6SAbhilash Kesavan  * exynos_cluster_power_down : power down the specified cluster
142096d21c6SAbhilash Kesavan  * @cluster : the cluster to power down
143096d21c6SAbhilash Kesavan  */
144096d21c6SAbhilash Kesavan void exynos_cluster_power_down(int cluster)
145096d21c6SAbhilash Kesavan {
146096d21c6SAbhilash Kesavan 	__raw_writel(0, EXYNOS_COMMON_CONFIGURATION(cluster));
147096d21c6SAbhilash Kesavan }
148096d21c6SAbhilash Kesavan 
149096d21c6SAbhilash Kesavan /**
150096d21c6SAbhilash Kesavan  * exynos_cluster_power_up : power up the specified cluster
151096d21c6SAbhilash Kesavan  * @cluster : the cluster to power up
152096d21c6SAbhilash Kesavan  */
153096d21c6SAbhilash Kesavan void exynos_cluster_power_up(int cluster)
154096d21c6SAbhilash Kesavan {
155096d21c6SAbhilash Kesavan 	__raw_writel(S5P_CORE_LOCAL_PWR_EN,
156096d21c6SAbhilash Kesavan 		     EXYNOS_COMMON_CONFIGURATION(cluster));
157096d21c6SAbhilash Kesavan }
158096d21c6SAbhilash Kesavan 
159096d21c6SAbhilash Kesavan /**
160096d21c6SAbhilash Kesavan  * exynos_cluster_power_state : returns the power state of the cluster
161096d21c6SAbhilash Kesavan  * @cluster : the cluster to retrieve the power state from
162096d21c6SAbhilash Kesavan  *
163096d21c6SAbhilash Kesavan  */
164096d21c6SAbhilash Kesavan int exynos_cluster_power_state(int cluster)
165096d21c6SAbhilash Kesavan {
166096d21c6SAbhilash Kesavan 	return (__raw_readl(EXYNOS_COMMON_STATUS(cluster)) &
167096d21c6SAbhilash Kesavan 			S5P_CORE_LOCAL_PWR_EN);
168096d21c6SAbhilash Kesavan }
169096d21c6SAbhilash Kesavan 
1703681bafeSDaniel Lezcano #define EXYNOS_BOOT_VECTOR_ADDR	(samsung_rev() == EXYNOS4210_REV_1_1 ? \
1713681bafeSDaniel Lezcano 			S5P_INFORM7 : (samsung_rev() == EXYNOS4210_REV_1_0 ? \
172cd245f59SDaniel Lezcano 			(sysram_base_addr + 0x24) : S5P_INFORM0))
1733681bafeSDaniel Lezcano #define EXYNOS_BOOT_VECTOR_FLAG	(samsung_rev() == EXYNOS4210_REV_1_1 ? \
1743681bafeSDaniel Lezcano 			S5P_INFORM6 : (samsung_rev() == EXYNOS4210_REV_1_0 ? \
175cd245f59SDaniel Lezcano 			(sysram_base_addr + 0x20) : S5P_INFORM1))
1763681bafeSDaniel Lezcano 
1773681bafeSDaniel Lezcano #define S5P_CHECK_AFTR  0xFCBA0D10
178e30b154bSDaniel Lezcano #define S5P_CHECK_SLEEP 0x00000BAD
1793681bafeSDaniel Lezcano 
1803681bafeSDaniel Lezcano /* Ext-GIC nIRQ/nFIQ is the only wakeup source in AFTR */
1813681bafeSDaniel Lezcano static void exynos_set_wakeupmask(long mask)
1823681bafeSDaniel Lezcano {
1833681bafeSDaniel Lezcano 	__raw_writel(mask, S5P_WAKEUP_MASK);
1843681bafeSDaniel Lezcano }
1853681bafeSDaniel Lezcano 
1863681bafeSDaniel Lezcano static void exynos_cpu_set_boot_vector(long flags)
1873681bafeSDaniel Lezcano {
1883681bafeSDaniel Lezcano 	__raw_writel(virt_to_phys(exynos_cpu_resume), EXYNOS_BOOT_VECTOR_ADDR);
1893681bafeSDaniel Lezcano 	__raw_writel(flags, EXYNOS_BOOT_VECTOR_FLAG);
1903681bafeSDaniel Lezcano }
1913681bafeSDaniel Lezcano 
1923681bafeSDaniel Lezcano void exynos_enter_aftr(void)
1933681bafeSDaniel Lezcano {
1943681bafeSDaniel Lezcano 	exynos_set_wakeupmask(0x0000ff3e);
1953681bafeSDaniel Lezcano 	exynos_cpu_set_boot_vector(S5P_CHECK_AFTR);
1963681bafeSDaniel Lezcano 	/* Set value of power down register for aftr mode */
1973681bafeSDaniel Lezcano 	exynos_sys_powerdown_conf(SYS_AFTR);
1983681bafeSDaniel Lezcano }
1993681bafeSDaniel Lezcano 
20083014579SKukjin Kim /* For Cortex-A9 Diagnostic and Power control register */
20183014579SKukjin Kim static unsigned int save_arm_register[2];
20283014579SKukjin Kim 
203309e08c4SDaniel Lezcano static void exynos_cpu_save_register(void)
204309e08c4SDaniel Lezcano {
205309e08c4SDaniel Lezcano 	unsigned long tmp;
206309e08c4SDaniel Lezcano 
207309e08c4SDaniel Lezcano 	/* Save Power control register */
208309e08c4SDaniel Lezcano 	asm ("mrc p15, 0, %0, c15, c0, 0"
209309e08c4SDaniel Lezcano 	     : "=r" (tmp) : : "cc");
210309e08c4SDaniel Lezcano 
211309e08c4SDaniel Lezcano 	save_arm_register[0] = tmp;
212309e08c4SDaniel Lezcano 
213309e08c4SDaniel Lezcano 	/* Save Diagnostic register */
214309e08c4SDaniel Lezcano 	asm ("mrc p15, 0, %0, c15, c0, 1"
215309e08c4SDaniel Lezcano 	     : "=r" (tmp) : : "cc");
216309e08c4SDaniel Lezcano 
217309e08c4SDaniel Lezcano 	save_arm_register[1] = tmp;
218309e08c4SDaniel Lezcano }
219309e08c4SDaniel Lezcano 
220309e08c4SDaniel Lezcano static void exynos_cpu_restore_register(void)
221309e08c4SDaniel Lezcano {
222309e08c4SDaniel Lezcano 	unsigned long tmp;
223309e08c4SDaniel Lezcano 
224309e08c4SDaniel Lezcano 	/* Restore Power control register */
225309e08c4SDaniel Lezcano 	tmp = save_arm_register[0];
226309e08c4SDaniel Lezcano 
227309e08c4SDaniel Lezcano 	asm volatile ("mcr p15, 0, %0, c15, c0, 0"
228309e08c4SDaniel Lezcano 		      : : "r" (tmp)
229309e08c4SDaniel Lezcano 		      : "cc");
230309e08c4SDaniel Lezcano 
231309e08c4SDaniel Lezcano 	/* Restore Diagnostic register */
232309e08c4SDaniel Lezcano 	tmp = save_arm_register[1];
233309e08c4SDaniel Lezcano 
234309e08c4SDaniel Lezcano 	asm volatile ("mcr p15, 0, %0, c15, c0, 1"
235309e08c4SDaniel Lezcano 		      : : "r" (tmp)
236309e08c4SDaniel Lezcano 		      : "cc");
237309e08c4SDaniel Lezcano }
238309e08c4SDaniel Lezcano 
239c9347101SJongpill Lee static int exynos_cpu_suspend(unsigned long arg)
24083014579SKukjin Kim {
24160e49ca6SJongpill Lee #ifdef CONFIG_CACHE_L2X0
24283014579SKukjin Kim 	outer_flush_all();
24360e49ca6SJongpill Lee #endif
24483014579SKukjin Kim 
245573e5bbeSAbhilash Kesavan 	if (soc_is_exynos5250())
246573e5bbeSAbhilash Kesavan 		flush_cache_all();
247573e5bbeSAbhilash Kesavan 
24883014579SKukjin Kim 	/* issue the standby signal into the pm unit. */
24983014579SKukjin Kim 	cpu_do_idle();
25083014579SKukjin Kim 
251d3fcacf5SAbhilash Kesavan 	pr_info("Failed to suspend the system\n");
252d3fcacf5SAbhilash Kesavan 	return 1; /* Aborting suspend */
25383014579SKukjin Kim }
25483014579SKukjin Kim 
255c9347101SJongpill Lee static void exynos_pm_prepare(void)
25683014579SKukjin Kim {
25760e49ca6SJongpill Lee 	unsigned int tmp;
25883014579SKukjin Kim 
259d710aa31STomasz Figa 	/* Set wake-up mask registers */
260d710aa31STomasz Figa 	__raw_writel(exynos_get_eint_wake_mask(), S5P_EINT_WAKEUP_MASK);
261d710aa31STomasz Figa 	__raw_writel(exynos_irqwake_intmask & ~(1 << 31), S5P_WAKEUP_MASK);
262d710aa31STomasz Figa 
263c9347101SJongpill Lee 	s3c_pm_do_save(exynos_core_save, ARRAY_SIZE(exynos_core_save));
26460e49ca6SJongpill Lee 
265e11d919eSTomasz Figa 	if (soc_is_exynos5250()) {
26686ffb0e8SAbhilash Kesavan 		s3c_pm_do_save(exynos5_sys_save, ARRAY_SIZE(exynos5_sys_save));
26760e49ca6SJongpill Lee 		/* Disable USE_RETENTION of JPEG_MEM_OPTION */
26860e49ca6SJongpill Lee 		tmp = __raw_readl(EXYNOS5_JPEG_MEM_OPTION);
26960e49ca6SJongpill Lee 		tmp &= ~EXYNOS5_OPTION_USE_RETENTION;
27060e49ca6SJongpill Lee 		__raw_writel(tmp, EXYNOS5_JPEG_MEM_OPTION);
27160e49ca6SJongpill Lee 	}
27283014579SKukjin Kim 
27383014579SKukjin Kim 	/* Set value of power down register for sleep mode */
27483014579SKukjin Kim 
2757d44d2baSJongpill Lee 	exynos_sys_powerdown_conf(SYS_SLEEP);
27683014579SKukjin Kim 	__raw_writel(S5P_CHECK_SLEEP, S5P_INFORM1);
27783014579SKukjin Kim 
27883014579SKukjin Kim 	/* ensure at least INFORM0 has the resume address */
27983014579SKukjin Kim 
280d710aa31STomasz Figa 	__raw_writel(virt_to_phys(exynos_cpu_resume), S5P_INFORM0);
28183014579SKukjin Kim }
28283014579SKukjin Kim 
2830ebc13e2SDaniel Lezcano static void exynos_pm_central_suspend(void)
28483014579SKukjin Kim {
28583014579SKukjin Kim 	unsigned long tmp;
28683014579SKukjin Kim 
28783014579SKukjin Kim 	/* Setting Central Sequence Register for power down mode */
28883014579SKukjin Kim 	tmp = __raw_readl(S5P_CENTRAL_SEQ_CONFIGURATION);
28983014579SKukjin Kim 	tmp &= ~S5P_CENTRAL_LOWPWR_CFG;
29083014579SKukjin Kim 	__raw_writel(tmp, S5P_CENTRAL_SEQ_CONFIGURATION);
2910ebc13e2SDaniel Lezcano }
2920ebc13e2SDaniel Lezcano 
2930ebc13e2SDaniel Lezcano static int exynos_pm_suspend(void)
2940ebc13e2SDaniel Lezcano {
2950ebc13e2SDaniel Lezcano 	unsigned long tmp;
2960ebc13e2SDaniel Lezcano 
2970ebc13e2SDaniel Lezcano 	exynos_pm_central_suspend();
29883014579SKukjin Kim 
29960e49ca6SJongpill Lee 	/* Setting SEQ_OPTION register */
30083014579SKukjin Kim 
30160e49ca6SJongpill Lee 	tmp = (S5P_USE_STANDBY_WFI0 | S5P_USE_STANDBY_WFE0);
30260e49ca6SJongpill Lee 	__raw_writel(tmp, S5P_CENTRAL_SEQ_OPTION);
30360e49ca6SJongpill Lee 
304c0c3c359SAbhilash Kesavan 	if (read_cpuid_part_number() == ARM_CPU_PART_CORTEX_A9)
305309e08c4SDaniel Lezcano 		exynos_cpu_save_register();
30683014579SKukjin Kim 
30783014579SKukjin Kim 	return 0;
30883014579SKukjin Kim }
30983014579SKukjin Kim 
3100ebc13e2SDaniel Lezcano static int exynos_pm_central_resume(void)
31183014579SKukjin Kim {
31283014579SKukjin Kim 	unsigned long tmp;
31383014579SKukjin Kim 
31483014579SKukjin Kim 	/*
31583014579SKukjin Kim 	 * If PMU failed while entering sleep mode, WFI will be
31683014579SKukjin Kim 	 * ignored by PMU and then exiting cpu_do_idle().
31783014579SKukjin Kim 	 * S5P_CENTRAL_LOWPWR_CFG bit will not be set automatically
31883014579SKukjin Kim 	 * in this situation.
31983014579SKukjin Kim 	 */
32083014579SKukjin Kim 	tmp = __raw_readl(S5P_CENTRAL_SEQ_CONFIGURATION);
32183014579SKukjin Kim 	if (!(tmp & S5P_CENTRAL_LOWPWR_CFG)) {
32283014579SKukjin Kim 		tmp |= S5P_CENTRAL_LOWPWR_CFG;
32383014579SKukjin Kim 		__raw_writel(tmp, S5P_CENTRAL_SEQ_CONFIGURATION);
324d3fcacf5SAbhilash Kesavan 		/* clear the wakeup state register */
325d3fcacf5SAbhilash Kesavan 		__raw_writel(0x0, S5P_WAKEUP_STAT);
32683014579SKukjin Kim 		/* No need to perform below restore code */
3270ebc13e2SDaniel Lezcano 		return -1;
32883014579SKukjin Kim 	}
32983014579SKukjin Kim 
3300ebc13e2SDaniel Lezcano 	return 0;
3310ebc13e2SDaniel Lezcano }
3320ebc13e2SDaniel Lezcano 
3330ebc13e2SDaniel Lezcano static void exynos_pm_resume(void)
3340ebc13e2SDaniel Lezcano {
3350ebc13e2SDaniel Lezcano 	if (exynos_pm_central_resume())
3360ebc13e2SDaniel Lezcano 		goto early_wakeup;
3370ebc13e2SDaniel Lezcano 
338c0c3c359SAbhilash Kesavan 	if (read_cpuid_part_number() == ARM_CPU_PART_CORTEX_A9)
339309e08c4SDaniel Lezcano 		exynos_cpu_restore_register();
34083014579SKukjin Kim 
34183014579SKukjin Kim 	/* For release retention */
34283014579SKukjin Kim 
34383014579SKukjin Kim 	__raw_writel((1 << 28), S5P_PAD_RET_MAUDIO_OPTION);
34483014579SKukjin Kim 	__raw_writel((1 << 28), S5P_PAD_RET_GPIO_OPTION);
34583014579SKukjin Kim 	__raw_writel((1 << 28), S5P_PAD_RET_UART_OPTION);
34683014579SKukjin Kim 	__raw_writel((1 << 28), S5P_PAD_RET_MMCA_OPTION);
34783014579SKukjin Kim 	__raw_writel((1 << 28), S5P_PAD_RET_MMCB_OPTION);
34883014579SKukjin Kim 	__raw_writel((1 << 28), S5P_PAD_RET_EBIA_OPTION);
34983014579SKukjin Kim 	__raw_writel((1 << 28), S5P_PAD_RET_EBIB_OPTION);
35083014579SKukjin Kim 
35186ffb0e8SAbhilash Kesavan 	if (soc_is_exynos5250())
35286ffb0e8SAbhilash Kesavan 		s3c_pm_do_restore(exynos5_sys_save,
35386ffb0e8SAbhilash Kesavan 			ARRAY_SIZE(exynos5_sys_save));
35486ffb0e8SAbhilash Kesavan 
355c9347101SJongpill Lee 	s3c_pm_do_restore_core(exynos_core_save, ARRAY_SIZE(exynos_core_save));
35683014579SKukjin Kim 
357c0c3c359SAbhilash Kesavan 	if (read_cpuid_part_number() == ARM_CPU_PART_CORTEX_A9)
35863b870f1SShawn Guo 		scu_enable(S5P_VA_SCU);
35983014579SKukjin Kim 
36083014579SKukjin Kim early_wakeup:
361ebee8541SInderpal Singh 
362ebee8541SInderpal Singh 	/* Clear SLEEP mode set in INFORM1 */
363ebee8541SInderpal Singh 	__raw_writel(0x0, S5P_INFORM1);
364ebee8541SInderpal Singh 
36583014579SKukjin Kim 	return;
36683014579SKukjin Kim }
36783014579SKukjin Kim 
368c9347101SJongpill Lee static struct syscore_ops exynos_pm_syscore_ops = {
369c9347101SJongpill Lee 	.suspend	= exynos_pm_suspend,
370c9347101SJongpill Lee 	.resume		= exynos_pm_resume,
37183014579SKukjin Kim };
37283014579SKukjin Kim 
373d710aa31STomasz Figa /*
374d710aa31STomasz Figa  * Suspend Ops
375d710aa31STomasz Figa  */
376d710aa31STomasz Figa 
377d710aa31STomasz Figa static int exynos_suspend_enter(suspend_state_t state)
378d710aa31STomasz Figa {
379d710aa31STomasz Figa 	int ret;
380d710aa31STomasz Figa 
381d710aa31STomasz Figa 	s3c_pm_debug_init();
382d710aa31STomasz Figa 
383d710aa31STomasz Figa 	S3C_PMDBG("%s: suspending the system...\n", __func__);
384d710aa31STomasz Figa 
385d710aa31STomasz Figa 	S3C_PMDBG("%s: wakeup masks: %08x,%08x\n", __func__,
386d710aa31STomasz Figa 			exynos_irqwake_intmask, exynos_get_eint_wake_mask());
387d710aa31STomasz Figa 
388d710aa31STomasz Figa 	if (exynos_irqwake_intmask == -1U
389d710aa31STomasz Figa 	    && exynos_get_eint_wake_mask() == -1U) {
390d710aa31STomasz Figa 		pr_err("%s: No wake-up sources!\n", __func__);
391d710aa31STomasz Figa 		pr_err("%s: Aborting sleep\n", __func__);
392d710aa31STomasz Figa 		return -EINVAL;
393d710aa31STomasz Figa 	}
394d710aa31STomasz Figa 
395d710aa31STomasz Figa 	s3c_pm_save_uarts();
396d710aa31STomasz Figa 	exynos_pm_prepare();
397d710aa31STomasz Figa 	flush_cache_all();
398d710aa31STomasz Figa 	s3c_pm_check_store();
399d710aa31STomasz Figa 
400d710aa31STomasz Figa 	ret = cpu_suspend(0, exynos_cpu_suspend);
401d710aa31STomasz Figa 	if (ret)
402d710aa31STomasz Figa 		return ret;
403d710aa31STomasz Figa 
404d710aa31STomasz Figa 	s3c_pm_restore_uarts();
405d710aa31STomasz Figa 
406d710aa31STomasz Figa 	S3C_PMDBG("%s: wakeup stat: %08x\n", __func__,
407d710aa31STomasz Figa 			__raw_readl(S5P_WAKEUP_STAT));
408d710aa31STomasz Figa 
409d710aa31STomasz Figa 	s3c_pm_check_restore();
410d710aa31STomasz Figa 
411d710aa31STomasz Figa 	S3C_PMDBG("%s: resuming the system...\n", __func__);
412d710aa31STomasz Figa 
413d710aa31STomasz Figa 	return 0;
414d710aa31STomasz Figa }
415d710aa31STomasz Figa 
416d710aa31STomasz Figa static int exynos_suspend_prepare(void)
417d710aa31STomasz Figa {
418d710aa31STomasz Figa 	s3c_pm_check_prepare();
419d710aa31STomasz Figa 
420d710aa31STomasz Figa 	return 0;
421d710aa31STomasz Figa }
422d710aa31STomasz Figa 
423d710aa31STomasz Figa static void exynos_suspend_finish(void)
424d710aa31STomasz Figa {
425d710aa31STomasz Figa 	s3c_pm_check_cleanup();
426d710aa31STomasz Figa }
427d710aa31STomasz Figa 
428d710aa31STomasz Figa static const struct platform_suspend_ops exynos_suspend_ops = {
429d710aa31STomasz Figa 	.enter		= exynos_suspend_enter,
430d710aa31STomasz Figa 	.prepare	= exynos_suspend_prepare,
431d710aa31STomasz Figa 	.finish		= exynos_suspend_finish,
432d710aa31STomasz Figa 	.valid		= suspend_valid_only_mem,
433d710aa31STomasz Figa };
434d710aa31STomasz Figa 
43585f9f908SDaniel Lezcano static int exynos_cpu_pm_notifier(struct notifier_block *self,
43685f9f908SDaniel Lezcano 				  unsigned long cmd, void *v)
43785f9f908SDaniel Lezcano {
43885f9f908SDaniel Lezcano 	int cpu = smp_processor_id();
43985f9f908SDaniel Lezcano 
44085f9f908SDaniel Lezcano 	switch (cmd) {
44185f9f908SDaniel Lezcano 	case CPU_PM_ENTER:
4420ebc13e2SDaniel Lezcano 		if (cpu == 0) {
4430ebc13e2SDaniel Lezcano 			exynos_pm_central_suspend();
444c0c3c359SAbhilash Kesavan 			if (read_cpuid_part_number() == ARM_CPU_PART_CORTEX_A9)
44585f9f908SDaniel Lezcano 				exynos_cpu_save_register();
4460ebc13e2SDaniel Lezcano 		}
44785f9f908SDaniel Lezcano 		break;
44885f9f908SDaniel Lezcano 
44985f9f908SDaniel Lezcano 	case CPU_PM_EXIT:
450795537daSDaniel Lezcano 		if (cpu == 0) {
451c0c3c359SAbhilash Kesavan 			if (read_cpuid_part_number() ==
452c0c3c359SAbhilash Kesavan 					ARM_CPU_PART_CORTEX_A9) {
453795537daSDaniel Lezcano 				scu_enable(S5P_VA_SCU);
45485f9f908SDaniel Lezcano 				exynos_cpu_restore_register();
455c0c3c359SAbhilash Kesavan 			}
4560ebc13e2SDaniel Lezcano 			exynos_pm_central_resume();
457795537daSDaniel Lezcano 		}
45885f9f908SDaniel Lezcano 		break;
45985f9f908SDaniel Lezcano 	}
46085f9f908SDaniel Lezcano 
46185f9f908SDaniel Lezcano 	return NOTIFY_OK;
46285f9f908SDaniel Lezcano }
46385f9f908SDaniel Lezcano 
46485f9f908SDaniel Lezcano static struct notifier_block exynos_cpu_pm_notifier_block = {
46585f9f908SDaniel Lezcano 	.notifier_call = exynos_cpu_pm_notifier,
46685f9f908SDaniel Lezcano };
46785f9f908SDaniel Lezcano 
468559ba237STomasz Figa void __init exynos_pm_init(void)
46983014579SKukjin Kim {
470559ba237STomasz Figa 	u32 tmp;
471559ba237STomasz Figa 
47285f9f908SDaniel Lezcano 	cpu_pm_register_notifier(&exynos_cpu_pm_notifier_block);
47385f9f908SDaniel Lezcano 
474dd8ac696STomasz Figa 	/* Platform-specific GIC callback */
475dd8ac696STomasz Figa 	gic_arch_extn.irq_set_wake = exynos_irq_set_wake;
476dd8ac696STomasz Figa 
477559ba237STomasz Figa 	/* All wakeup disable */
478559ba237STomasz Figa 	tmp = __raw_readl(S5P_WAKEUP_MASK);
479559ba237STomasz Figa 	tmp |= ((0xFF << 8) | (0x1F << 1));
480559ba237STomasz Figa 	__raw_writel(tmp, S5P_WAKEUP_MASK);
481e085cad6SKukjin Kim 
482c9347101SJongpill Lee 	register_syscore_ops(&exynos_pm_syscore_ops);
483d710aa31STomasz Figa 	suspend_set_ops(&exynos_suspend_ops);
48483014579SKukjin Kim }
485