1 /* linux/arch/arm/mach-exynos4/cpuidle.c
3 * Copyright (c) 2011 Samsung Electronics Co., Ltd.
4 * http://www.samsung.com
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License version 2 as
8 * published by the Free Software Foundation.
11 #include <linux/kernel.h>
12 #include <linux/init.h>
13 #include <linux/cpuidle.h>
14 #include <linux/cpu_pm.h>
16 #include <linux/export.h>
17 #include <linux/module.h>
18 #include <linux/time.h>
19 #include <linux/platform_device.h>
21 #include <asm/proc-fns.h>
22 #include <asm/smp_scu.h>
23 #include <asm/suspend.h>
24 #include <asm/unified.h>
25 #include <asm/cpuidle.h>
35 #define REG_DIRECTGO_ADDR (samsung_rev() == EXYNOS4210_REV_1_1 ? \
36 S5P_INFORM7 : (samsung_rev() == EXYNOS4210_REV_1_0 ? \
37 (S5P_VA_SYSRAM + 0x24) : S5P_INFORM0))
38 #define REG_DIRECTGO_FLAG (samsung_rev() == EXYNOS4210_REV_1_1 ? \
39 S5P_INFORM6 : (samsung_rev() == EXYNOS4210_REV_1_0 ? \
40 (S5P_VA_SYSRAM + 0x20) : S5P_INFORM1))
42 #define S5P_CHECK_AFTR 0xFCBA0D10
44 /* Ext-GIC nIRQ/nFIQ is the only wakeup source in AFTR */
45 static void exynos4_set_wakeupmask(void)
47 __raw_writel(0x0000ff3e, S5P_WAKEUP_MASK
);
50 static unsigned int g_pwr_ctrl
, g_diag_reg
;
52 static void save_cpu_arch_register(void)
54 /*read power control register*/
55 asm("mrc p15, 0, %0, c15, c0, 0" : "=r"(g_pwr_ctrl
) : : "cc");
56 /*read diagnostic register*/
57 asm("mrc p15, 0, %0, c15, c0, 1" : "=r"(g_diag_reg
) : : "cc");
61 static void restore_cpu_arch_register(void)
63 /*write power control register*/
64 asm("mcr p15, 0, %0, c15, c0, 0" : : "r"(g_pwr_ctrl
) : "cc");
65 /*write diagnostic register*/
66 asm("mcr p15, 0, %0, c15, c0, 1" : : "r"(g_diag_reg
) : "cc");
70 static int idle_finisher(unsigned long flags
)
76 static int exynos4_enter_core0_aftr(struct cpuidle_device
*dev
,
77 struct cpuidle_driver
*drv
,
82 exynos4_set_wakeupmask();
84 /* Set value of power down register for aftr mode */
85 exynos_sys_powerdown_conf(SYS_AFTR
);
87 __raw_writel(virt_to_phys(exynos_cpu_resume
), REG_DIRECTGO_ADDR
);
88 __raw_writel(S5P_CHECK_AFTR
, REG_DIRECTGO_FLAG
);
90 save_cpu_arch_register();
92 /* Setting Central Sequence Register for power down mode */
93 tmp
= __raw_readl(S5P_CENTRAL_SEQ_CONFIGURATION
);
94 tmp
&= ~S5P_CENTRAL_LOWPWR_CFG
;
95 __raw_writel(tmp
, S5P_CENTRAL_SEQ_CONFIGURATION
);
98 cpu_suspend(0, idle_finisher
);
101 if (!soc_is_exynos5250())
102 scu_enable(S5P_VA_SCU
);
106 restore_cpu_arch_register();
109 * If PMU failed while entering sleep mode, WFI will be
110 * ignored by PMU and then exiting cpu_do_idle().
111 * S5P_CENTRAL_LOWPWR_CFG bit will not be set automatically
114 tmp
= __raw_readl(S5P_CENTRAL_SEQ_CONFIGURATION
);
115 if (!(tmp
& S5P_CENTRAL_LOWPWR_CFG
)) {
116 tmp
|= S5P_CENTRAL_LOWPWR_CFG
;
117 __raw_writel(tmp
, S5P_CENTRAL_SEQ_CONFIGURATION
);
120 /* Clear wakeup state register */
121 __raw_writel(0x0, S5P_WAKEUP_STAT
);
126 static int exynos4_enter_lowpower(struct cpuidle_device
*dev
,
127 struct cpuidle_driver
*drv
,
130 int new_index
= index
;
132 /* AFTR can only be entered when cores other than CPU0 are offline */
133 if (num_online_cpus() > 1 || dev
->cpu
!= 0)
134 new_index
= drv
->safe_state_index
;
137 return arm_cpuidle_simple_enter(dev
, drv
, new_index
);
139 return exynos4_enter_core0_aftr(dev
, drv
, new_index
);
142 static struct cpuidle_driver exynos4_idle_driver
= {
143 .name
= "exynos4_idle",
144 .owner
= THIS_MODULE
,
146 [0] = ARM_CPUIDLE_WFI_STATE
,
148 .enter
= exynos4_enter_lowpower
,
150 .target_residency
= 100000,
151 .flags
= CPUIDLE_FLAG_TIME_VALID
,
153 .desc
= "ARM power down",
157 .safe_state_index
= 0,
160 static int exynos_cpuidle_probe(struct platform_device
*pdev
)
164 if (soc_is_exynos5440())
165 exynos4_idle_driver
.state_count
= 1;
167 ret
= cpuidle_register(&exynos4_idle_driver
, NULL
);
169 dev_err(&pdev
->dev
, "failed to register cpuidle driver\n");
176 static struct platform_driver exynos_cpuidle_driver
= {
177 .probe
= exynos_cpuidle_probe
,
179 .name
= "exynos_cpuidle",
180 .owner
= THIS_MODULE
,
184 module_platform_driver(exynos_cpuidle_driver
);