diff options
author | Ben Dooks <ben-linux@fluff.org> | 2009-03-10 14:19:35 -0400 |
---|---|---|
committer | Ben Dooks <ben-linux@fluff.org> | 2009-05-07 06:04:55 -0400 |
commit | bd117bd161ea99826494983aef8c8e236ac129bd (patch) | |
tree | 76929c5f9eb611b7428a69375b2e6ddf09638821 | |
parent | 4b637dc231a96a151ea70c27d86b35c7891e2a7c (diff) |
[ARM] S3C64XX: Initial support for PM (suspend to RAM)
Add the initial support for the S3C64XX based systems to use
suspend-to-RAM to sleep.
Includes basic debugging for use with the SMDK6410 usign the
LEDs on the baseboard.
Signed-off-by: Ben Dooks <ben-linux@fluff.org>
-rw-r--r-- | arch/arm/mach-s3c6400/include/mach/regs-clock.h | 16 | ||||
-rw-r--r-- | arch/arm/plat-s3c/Kconfig | 9 | ||||
-rw-r--r-- | arch/arm/plat-s3c/include/plat/pm.h | 12 | ||||
-rw-r--r-- | arch/arm/plat-s3c/pm.c | 6 | ||||
-rw-r--r-- | arch/arm/plat-s3c64xx/Makefile | 5 | ||||
-rw-r--r-- | arch/arm/plat-s3c64xx/include/plat/irqs.h | 1 | ||||
-rw-r--r-- | arch/arm/plat-s3c64xx/include/plat/pm-core.h | 98 | ||||
-rw-r--r-- | arch/arm/plat-s3c64xx/include/plat/regs-clock.h | 1 | ||||
-rw-r--r-- | arch/arm/plat-s3c64xx/irq-eint.c | 3 | ||||
-rw-r--r-- | arch/arm/plat-s3c64xx/pm.c | 186 | ||||
-rw-r--r-- | arch/arm/plat-s3c64xx/sleep.S | 144 |
11 files changed, 479 insertions, 2 deletions
diff --git a/arch/arm/mach-s3c6400/include/mach/regs-clock.h b/arch/arm/mach-s3c6400/include/mach/regs-clock.h new file mode 100644 index 000000000000..a6c7f4eb3a1b --- /dev/null +++ b/arch/arm/mach-s3c6400/include/mach/regs-clock.h | |||
@@ -0,0 +1,16 @@ | |||
1 | /* linux/arch/arm/mach-s3c6400/include/mach/regs-clock.h | ||
2 | * | ||
3 | * Copyright 2008 Openmoko, Inc. | ||
4 | * Copyright 2008 Simtec Electronics | ||
5 | * http://armlinux.simtec.co.uk/ | ||
6 | * Ben Dooks <ben@simtec.co.uk> | ||
7 | * | ||
8 | * S3C64XX - clock register compatibility with s3c24xx | ||
9 | * | ||
10 | * This program is free software; you can redistribute it and/or modify | ||
11 | * it under the terms of the GNU General Public License version 2 as | ||
12 | * published by the Free Software Foundation. | ||
13 | */ | ||
14 | |||
15 | #include <plat/regs-clock.h> | ||
16 | |||
diff --git a/arch/arm/plat-s3c/Kconfig b/arch/arm/plat-s3c/Kconfig index 042d34a6f815..a4c5027421d3 100644 --- a/arch/arm/plat-s3c/Kconfig +++ b/arch/arm/plat-s3c/Kconfig | |||
@@ -71,6 +71,15 @@ config S3C2410_PM_DEBUG | |||
71 | Resume code. See <file:Documentation/arm/Samsung-S3C24XX/Suspend.txt> | 71 | Resume code. See <file:Documentation/arm/Samsung-S3C24XX/Suspend.txt> |
72 | for more information. | 72 | for more information. |
73 | 73 | ||
74 | config S3C_PM_DEBUG_LED_SMDK | ||
75 | bool "SMDK LED suspend/resume debugging" | ||
76 | depends on PM && (MACH_SMDK6410) | ||
77 | help | ||
78 | Say Y here to enable the use of the SMDK LEDs on the baseboard | ||
79 | for debugging of the state of the suspend and resume process. | ||
80 | |||
81 | Note, this currently only works for S3C64XX based SMDK boards. | ||
82 | |||
74 | config S3C2410_PM_CHECK | 83 | config S3C2410_PM_CHECK |
75 | bool "S3C2410 PM Suspend Memory CRC" | 84 | bool "S3C2410 PM Suspend Memory CRC" |
76 | depends on PM && CRC32 | 85 | depends on PM && CRC32 |
diff --git a/arch/arm/plat-s3c/include/plat/pm.h b/arch/arm/plat-s3c/include/plat/pm.h index 5f8707e2a09c..7a797192fcf3 100644 --- a/arch/arm/plat-s3c/include/plat/pm.h +++ b/arch/arm/plat-s3c/include/plat/pm.h | |||
@@ -127,6 +127,18 @@ extern void s3c_pm_dbg(const char *msg, ...); | |||
127 | #define S3C_PMDBG(fmt...) printk(KERN_DEBUG fmt) | 127 | #define S3C_PMDBG(fmt...) printk(KERN_DEBUG fmt) |
128 | #endif | 128 | #endif |
129 | 129 | ||
130 | #ifdef CONFIG_S3C_PM_DEBUG_LED_SMDK | ||
131 | /** | ||
132 | * s3c_pm_debug_smdkled() - Debug PM suspend/resume via SMDK Board LEDs | ||
133 | * @set: set bits for the state of the LEDs | ||
134 | * @clear: clear bits for the state of the LEDs. | ||
135 | */ | ||
136 | extern void s3c_pm_debug_smdkled(u32 set, u32 clear); | ||
137 | |||
138 | #else | ||
139 | static inline void s3c_pm_debug_smdkled(u32 set, u32 clear) { } | ||
140 | #endif /* CONFIG_S3C_PM_DEBUG_LED_SMDK */ | ||
141 | |||
130 | /* suspend memory checking */ | 142 | /* suspend memory checking */ |
131 | 143 | ||
132 | #ifdef CONFIG_S3C2410_PM_CHECK | 144 | #ifdef CONFIG_S3C2410_PM_CHECK |
diff --git a/arch/arm/plat-s3c/pm.c b/arch/arm/plat-s3c/pm.c index 9957b539600f..8d97db2c7a0d 100644 --- a/arch/arm/plat-s3c/pm.c +++ b/arch/arm/plat-s3c/pm.c | |||
@@ -21,11 +21,10 @@ | |||
21 | 21 | ||
22 | #include <asm/cacheflush.h> | 22 | #include <asm/cacheflush.h> |
23 | #include <mach/hardware.h> | 23 | #include <mach/hardware.h> |
24 | #include <mach/map.h> | ||
24 | 25 | ||
25 | #include <plat/regs-serial.h> | 26 | #include <plat/regs-serial.h> |
26 | #include <mach/regs-clock.h> | 27 | #include <mach/regs-clock.h> |
27 | #include <mach/regs-gpio.h> | ||
28 | #include <mach/regs-mem.h> | ||
29 | #include <mach/regs-irq.h> | 28 | #include <mach/regs-irq.h> |
30 | #include <asm/irq.h> | 29 | #include <asm/irq.h> |
31 | 30 | ||
@@ -326,6 +325,9 @@ static int s3c_pm_enter(suspend_state_t state) | |||
326 | 325 | ||
327 | S3C_PMDBG("%s: post sleep, preparing to return\n", __func__); | 326 | S3C_PMDBG("%s: post sleep, preparing to return\n", __func__); |
328 | 327 | ||
328 | /* LEDs should now be 1110 */ | ||
329 | s3c_pm_debug_smdkled(1 << 1, 0); | ||
330 | |||
329 | s3c_pm_check_restore(); | 331 | s3c_pm_check_restore(); |
330 | 332 | ||
331 | /* ok, let's return from sleep */ | 333 | /* ok, let's return from sleep */ |
diff --git a/arch/arm/plat-s3c64xx/Makefile b/arch/arm/plat-s3c64xx/Makefile index 2e6d79bf8f33..b2bc2a9ed46b 100644 --- a/arch/arm/plat-s3c64xx/Makefile +++ b/arch/arm/plat-s3c64xx/Makefile | |||
@@ -24,6 +24,11 @@ obj-y += gpiolib.o | |||
24 | obj-$(CONFIG_CPU_S3C6400_INIT) += s3c6400-init.o | 24 | obj-$(CONFIG_CPU_S3C6400_INIT) += s3c6400-init.o |
25 | obj-$(CONFIG_CPU_S3C6400_CLOCK) += s3c6400-clock.o | 25 | obj-$(CONFIG_CPU_S3C6400_CLOCK) += s3c6400-clock.o |
26 | 26 | ||
27 | # PM support | ||
28 | |||
29 | obj-$(CONFIG_PM) += pm.o | ||
30 | obj-$(CONFIG_PM) += sleep.o | ||
31 | |||
27 | # Device setup | 32 | # Device setup |
28 | 33 | ||
29 | obj-$(CONFIG_S3C64XX_SETUP_I2C0) += setup-i2c0.o | 34 | obj-$(CONFIG_S3C64XX_SETUP_I2C0) += setup-i2c0.o |
diff --git a/arch/arm/plat-s3c64xx/include/plat/irqs.h b/arch/arm/plat-s3c64xx/include/plat/irqs.h index f865bf4d709e..743a70094d04 100644 --- a/arch/arm/plat-s3c64xx/include/plat/irqs.h +++ b/arch/arm/plat-s3c64xx/include/plat/irqs.h | |||
@@ -157,6 +157,7 @@ | |||
157 | 157 | ||
158 | #define S3C_EINT(x) ((x) + S3C_IRQ_EINT_BASE) | 158 | #define S3C_EINT(x) ((x) + S3C_IRQ_EINT_BASE) |
159 | #define IRQ_EINT(x) S3C_EINT(x) | 159 | #define IRQ_EINT(x) S3C_EINT(x) |
160 | #define IRQ_EINT_BIT(x) ((x) - S3C_EINT(0)) | ||
160 | 161 | ||
161 | /* Next the external interrupt groups. These are similar to the IRQ_EINT(x) | 162 | /* Next the external interrupt groups. These are similar to the IRQ_EINT(x) |
162 | * that they are sourced from the GPIO pins but with a different scheme for | 163 | * that they are sourced from the GPIO pins but with a different scheme for |
diff --git a/arch/arm/plat-s3c64xx/include/plat/pm-core.h b/arch/arm/plat-s3c64xx/include/plat/pm-core.h new file mode 100644 index 000000000000..d347de3ba0dc --- /dev/null +++ b/arch/arm/plat-s3c64xx/include/plat/pm-core.h | |||
@@ -0,0 +1,98 @@ | |||
1 | /* linux/arch/arm/plat-s3c64xx/include/plat/pm-core.h | ||
2 | * | ||
3 | * Copyright 2008 Openmoko, Inc. | ||
4 | * Copyright 2008 Simtec Electronics | ||
5 | * Ben Dooks <ben@simtec.co.uk> | ||
6 | * http://armlinux.simtec.co.uk/ | ||
7 | * | ||
8 | * S3C64XX - PM core support for arch/arm/plat-s3c/pm.c | ||
9 | * | ||
10 | * This program is free software; you can redistribute it and/or modify | ||
11 | * it under the terms of the GNU General Public License version 2 as | ||
12 | * published by the Free Software Foundation. | ||
13 | */ | ||
14 | |||
15 | #include <plat/regs-gpio.h> | ||
16 | |||
17 | static inline void s3c_pm_debug_init_uart(void) | ||
18 | { | ||
19 | u32 tmp = __raw_readl(S3C_PCLK_GATE); | ||
20 | |||
21 | /* As a note, since the S3C64XX UARTs generally have multiple | ||
22 | * clock sources, we simply enable PCLK at the moment and hope | ||
23 | * that the resume settings for the UART are suitable for the | ||
24 | * use with PCLK. | ||
25 | */ | ||
26 | |||
27 | tmp |= S3C_CLKCON_PCLK_UART0; | ||
28 | tmp |= S3C_CLKCON_PCLK_UART1; | ||
29 | tmp |= S3C_CLKCON_PCLK_UART2; | ||
30 | tmp |= S3C_CLKCON_PCLK_UART3; | ||
31 | |||
32 | __raw_writel(tmp, S3C_PCLK_GATE); | ||
33 | udelay(10); | ||
34 | } | ||
35 | |||
36 | static inline void s3c_pm_arch_prepare_irqs(void) | ||
37 | { | ||
38 | /* VIC should have already been taken care of */ | ||
39 | |||
40 | /* clear any pending EINT0 interrupts */ | ||
41 | __raw_writel(__raw_readl(S3C64XX_EINT0PEND), S3C64XX_EINT0PEND); | ||
42 | } | ||
43 | |||
44 | static inline void s3c_pm_arch_stop_clocks(void) | ||
45 | { | ||
46 | } | ||
47 | |||
48 | static inline void s3c_pm_arch_show_resume_irqs(void) | ||
49 | { | ||
50 | } | ||
51 | |||
52 | /* make these defines, we currently do not have any need to change | ||
53 | * the IRQ wake controls depending on the CPU we are running on */ | ||
54 | |||
55 | #define s3c_irqwake_eintallow ((1 << 28) - 1) | ||
56 | #define s3c_irqwake_intallow (0) | ||
57 | |||
58 | static inline void s3c_pm_arch_update_uart(void __iomem *regs, | ||
59 | struct pm_uart_save *save) | ||
60 | { | ||
61 | u32 ucon = __raw_readl(regs + S3C2410_UCON); | ||
62 | u32 ucon_clk = ucon & S3C6400_UCON_CLKMASK; | ||
63 | u32 save_clk = save->ucon & S3C6400_UCON_CLKMASK; | ||
64 | u32 new_ucon; | ||
65 | u32 delta; | ||
66 | |||
67 | /* S3C64XX UART blocks only support level interrupts, so ensure that | ||
68 | * when we restore unused UART blocks we force the level interrupt | ||
69 | * settigs. */ | ||
70 | save->ucon |= S3C2410_UCON_TXILEVEL | S3C2410_UCON_RXILEVEL; | ||
71 | |||
72 | /* We have a constraint on changing the clock type of the UART | ||
73 | * between UCLKx and PCLK, so ensure that when we restore UCON | ||
74 | * that the CLK field is correctly modified if the bootloader | ||
75 | * has changed anything. | ||
76 | */ | ||
77 | if (ucon_clk != save_clk) { | ||
78 | new_ucon = save->ucon; | ||
79 | delta = ucon_clk ^ save_clk; | ||
80 | |||
81 | /* change from UCLKx => wrong PCLK, | ||
82 | * either UCLK can be tested for by a bit-test | ||
83 | * with UCLK0 */ | ||
84 | if (ucon_clk & S3C6400_UCON_UCLK0 && | ||
85 | !(save_clk & S3C6400_UCON_UCLK0) && | ||
86 | delta & S3C6400_UCON_PCLK2) { | ||
87 | new_ucon &= ~S3C6400_UCON_UCLK0; | ||
88 | } else if (delta == S3C6400_UCON_PCLK2) { | ||
89 | /* as an precaution, don't change from | ||
90 | * PCLK2 => PCLK or vice-versa */ | ||
91 | new_ucon ^= S3C6400_UCON_PCLK2; | ||
92 | } | ||
93 | |||
94 | S3C_PMDBG("ucon change %04x => %04x (save=%04x)\n", | ||
95 | ucon, new_ucon, save->ucon); | ||
96 | save->ucon = new_ucon; | ||
97 | } | ||
98 | } | ||
diff --git a/arch/arm/plat-s3c64xx/include/plat/regs-clock.h b/arch/arm/plat-s3c64xx/include/plat/regs-clock.h index b1082c163247..52836d41e333 100644 --- a/arch/arm/plat-s3c64xx/include/plat/regs-clock.h +++ b/arch/arm/plat-s3c64xx/include/plat/regs-clock.h | |||
@@ -32,6 +32,7 @@ | |||
32 | #define S3C_HCLK_GATE S3C_CLKREG(0x30) | 32 | #define S3C_HCLK_GATE S3C_CLKREG(0x30) |
33 | #define S3C_PCLK_GATE S3C_CLKREG(0x34) | 33 | #define S3C_PCLK_GATE S3C_CLKREG(0x34) |
34 | #define S3C_SCLK_GATE S3C_CLKREG(0x38) | 34 | #define S3C_SCLK_GATE S3C_CLKREG(0x38) |
35 | #define S3C_MEM0_GATE S3C_CLKREG(0x3C) | ||
35 | 36 | ||
36 | /* CLKDIV0 */ | 37 | /* CLKDIV0 */ |
37 | #define S3C6400_CLKDIV0_MFC_MASK (0xf << 28) | 38 | #define S3C6400_CLKDIV0_MFC_MASK (0xf << 28) |
diff --git a/arch/arm/plat-s3c64xx/irq-eint.c b/arch/arm/plat-s3c64xx/irq-eint.c index 47e5155bb13e..f81b7b818ba0 100644 --- a/arch/arm/plat-s3c64xx/irq-eint.c +++ b/arch/arm/plat-s3c64xx/irq-eint.c | |||
@@ -14,6 +14,7 @@ | |||
14 | 14 | ||
15 | #include <linux/kernel.h> | 15 | #include <linux/kernel.h> |
16 | #include <linux/interrupt.h> | 16 | #include <linux/interrupt.h> |
17 | #include <linux/sysdev.h> | ||
17 | #include <linux/gpio.h> | 18 | #include <linux/gpio.h> |
18 | #include <linux/irq.h> | 19 | #include <linux/irq.h> |
19 | #include <linux/io.h> | 20 | #include <linux/io.h> |
@@ -26,6 +27,7 @@ | |||
26 | 27 | ||
27 | #include <mach/map.h> | 28 | #include <mach/map.h> |
28 | #include <plat/cpu.h> | 29 | #include <plat/cpu.h> |
30 | #include <plat/pm.h> | ||
29 | 31 | ||
30 | #define eint_offset(irq) ((irq) - IRQ_EINT(0)) | 32 | #define eint_offset(irq) ((irq) - IRQ_EINT(0)) |
31 | #define eint_irq_to_bit(irq) (1 << eint_offset(irq)) | 33 | #define eint_irq_to_bit(irq) (1 << eint_offset(irq)) |
@@ -134,6 +136,7 @@ static struct irq_chip s3c_irq_eint = { | |||
134 | .mask_ack = s3c_irq_eint_maskack, | 136 | .mask_ack = s3c_irq_eint_maskack, |
135 | .ack = s3c_irq_eint_ack, | 137 | .ack = s3c_irq_eint_ack, |
136 | .set_type = s3c_irq_eint_set_type, | 138 | .set_type = s3c_irq_eint_set_type, |
139 | .set_wake = s3c_irqext_wake, | ||
137 | }; | 140 | }; |
138 | 141 | ||
139 | /* s3c_irq_demux_eint | 142 | /* s3c_irq_demux_eint |
diff --git a/arch/arm/plat-s3c64xx/pm.c b/arch/arm/plat-s3c64xx/pm.c new file mode 100644 index 000000000000..98190aa364ae --- /dev/null +++ b/arch/arm/plat-s3c64xx/pm.c | |||
@@ -0,0 +1,186 @@ | |||
1 | /* linux/arch/arm/plat-s3c64xx/pm.c | ||
2 | * | ||
3 | * Copyright 2008 Openmoko, Inc. | ||
4 | * Copyright 2008 Simtec Electronics | ||
5 | * Ben Dooks <ben@simtec.co.uk> | ||
6 | * http://armlinux.simtec.co.uk/ | ||
7 | * | ||
8 | * S3C64XX CPU PM support. | ||
9 | * | ||
10 | * This program is free software; you can redistribute it and/or modify | ||
11 | * it under the terms of the GNU General Public License version 2 as | ||
12 | * published by the Free Software Foundation. | ||
13 | */ | ||
14 | |||
15 | #include <linux/init.h> | ||
16 | #include <linux/suspend.h> | ||
17 | #include <linux/serial_core.h> | ||
18 | #include <linux/io.h> | ||
19 | |||
20 | #include <mach/map.h> | ||
21 | |||
22 | #include <plat/pm.h> | ||
23 | #include <plat/regs-sys.h> | ||
24 | #include <plat/regs-gpio.h> | ||
25 | #include <plat/regs-clock.h> | ||
26 | #include <plat/regs-syscon-power.h> | ||
27 | #include <plat/regs-gpio-memport.h> | ||
28 | |||
29 | #ifdef CONFIG_S3C_PM_DEBUG_LED_SMDK | ||
30 | #include <plat/gpio-bank-n.h> | ||
31 | |||
32 | void s3c_pm_debug_smdkled(u32 set, u32 clear) | ||
33 | { | ||
34 | unsigned long flags; | ||
35 | u32 reg; | ||
36 | |||
37 | local_irq_save(flags); | ||
38 | reg = __raw_readl(S3C64XX_GPNCON); | ||
39 | reg &= ~(S3C64XX_GPN_CONMASK(12) | S3C64XX_GPN_CONMASK(13) | | ||
40 | S3C64XX_GPN_CONMASK(14) | S3C64XX_GPN_CONMASK(15)); | ||
41 | reg |= S3C64XX_GPN_OUTPUT(12) | S3C64XX_GPN_OUTPUT(13) | | ||
42 | S3C64XX_GPN_OUTPUT(14) | S3C64XX_GPN_OUTPUT(15); | ||
43 | __raw_writel(reg, S3C64XX_GPNCON); | ||
44 | |||
45 | reg = __raw_readl(S3C64XX_GPNDAT); | ||
46 | reg &= ~(clear << 12); | ||
47 | reg |= set << 12; | ||
48 | __raw_writel(reg, S3C64XX_GPNDAT); | ||
49 | |||
50 | local_irq_restore(flags); | ||
51 | } | ||
52 | #endif | ||
53 | |||
54 | static struct sleep_save core_save[] = { | ||
55 | SAVE_ITEM(S3C_APLL_LOCK), | ||
56 | SAVE_ITEM(S3C_MPLL_LOCK), | ||
57 | SAVE_ITEM(S3C_EPLL_LOCK), | ||
58 | SAVE_ITEM(S3C_CLK_SRC), | ||
59 | SAVE_ITEM(S3C_CLK_DIV0), | ||
60 | SAVE_ITEM(S3C_CLK_DIV1), | ||
61 | SAVE_ITEM(S3C_CLK_DIV2), | ||
62 | SAVE_ITEM(S3C_CLK_OUT), | ||
63 | SAVE_ITEM(S3C_HCLK_GATE), | ||
64 | SAVE_ITEM(S3C_PCLK_GATE), | ||
65 | SAVE_ITEM(S3C_SCLK_GATE), | ||
66 | SAVE_ITEM(S3C_MEM0_GATE), | ||
67 | |||
68 | SAVE_ITEM(S3C_EPLL_CON1), | ||
69 | SAVE_ITEM(S3C_EPLL_CON0), | ||
70 | |||
71 | SAVE_ITEM(S3C64XX_MEM0DRVCON), | ||
72 | SAVE_ITEM(S3C64XX_MEM1DRVCON), | ||
73 | |||
74 | #ifndef CONFIG_CPU_FREQ | ||
75 | SAVE_ITEM(S3C_APLL_CON), | ||
76 | SAVE_ITEM(S3C_MPLL_CON), | ||
77 | #endif | ||
78 | }; | ||
79 | |||
80 | static struct sleep_save misc_save[] = { | ||
81 | SAVE_ITEM(S3C64XX_AHB_CON0), | ||
82 | SAVE_ITEM(S3C64XX_AHB_CON1), | ||
83 | SAVE_ITEM(S3C64XX_AHB_CON2), | ||
84 | |||
85 | SAVE_ITEM(S3C64XX_SPCON), | ||
86 | |||
87 | SAVE_ITEM(S3C64XX_MEM0CONSTOP), | ||
88 | SAVE_ITEM(S3C64XX_MEM1CONSTOP), | ||
89 | SAVE_ITEM(S3C64XX_MEM0CONSLP0), | ||
90 | SAVE_ITEM(S3C64XX_MEM0CONSLP1), | ||
91 | SAVE_ITEM(S3C64XX_MEM1CONSLP), | ||
92 | }; | ||
93 | |||
94 | void s3c_pm_configure_extint(void) | ||
95 | { | ||
96 | __raw_writel(s3c_irqwake_eintmask, S3C64XX_EINT_MASK); | ||
97 | } | ||
98 | |||
99 | void s3c_pm_save_gpios(void) | ||
100 | { | ||
101 | /* currently, unless the bootloader does something really stupid | ||
102 | * the gpio blocks should be maintained over their sleep. | ||
103 | */ | ||
104 | } | ||
105 | |||
106 | void s3c_pm_restore_gpios(void) | ||
107 | { | ||
108 | } | ||
109 | |||
110 | void s3c_pm_restore_core(void) | ||
111 | { | ||
112 | __raw_writel(0, S3C64XX_EINT_MASK); | ||
113 | |||
114 | s3c_pm_debug_smdkled(1 << 2, 0); | ||
115 | |||
116 | s3c_pm_do_restore_core(core_save, ARRAY_SIZE(core_save)); | ||
117 | s3c_pm_do_restore(misc_save, ARRAY_SIZE(misc_save)); | ||
118 | } | ||
119 | |||
120 | void s3c_pm_save_core(void) | ||
121 | { | ||
122 | s3c_pm_do_save(misc_save, ARRAY_SIZE(misc_save)); | ||
123 | s3c_pm_do_save(core_save, ARRAY_SIZE(core_save)); | ||
124 | } | ||
125 | |||
126 | /* since both s3c6400 and s3c6410 share the same sleep pm calls, we | ||
127 | * put the per-cpu code in here until any new cpu comes along and changes | ||
128 | * this. | ||
129 | */ | ||
130 | |||
131 | #include <plat/regs-gpio.h> | ||
132 | |||
133 | static void s3c64xx_cpu_suspend(void) | ||
134 | { | ||
135 | unsigned long tmp; | ||
136 | |||
137 | /* set our standby method to sleep */ | ||
138 | |||
139 | tmp = __raw_readl(S3C64XX_PWR_CFG); | ||
140 | tmp &= ~S3C64XX_PWRCFG_CFG_WFI_MASK; | ||
141 | tmp |= S3C64XX_PWRCFG_CFG_WFI_SLEEP; | ||
142 | __raw_writel(tmp, S3C64XX_PWR_CFG); | ||
143 | |||
144 | /* clear any old wakeup */ | ||
145 | |||
146 | __raw_writel(__raw_readl(S3C64XX_WAKEUP_STAT), | ||
147 | S3C64XX_WAKEUP_STAT); | ||
148 | |||
149 | /* set the LED state to 0110 over sleep */ | ||
150 | s3c_pm_debug_smdkled(3 << 1, 0xf); | ||
151 | |||
152 | /* issue the standby signal into the pm unit. Note, we | ||
153 | * issue a write-buffer drain just in case */ | ||
154 | |||
155 | tmp = 0; | ||
156 | |||
157 | asm("b 1f\n\t" | ||
158 | ".align 5\n\t" | ||
159 | "1:\n\t" | ||
160 | "mcr p15, 0, %0, c7, c10, 5\n\t" | ||
161 | "mcr p15, 0, %0, c7, c10, 4\n\t" | ||
162 | "mcr p15, 0, %0, c7, c0, 4" :: "r" (tmp)); | ||
163 | |||
164 | /* we should never get past here */ | ||
165 | |||
166 | panic("sleep resumed to originator?"); | ||
167 | } | ||
168 | |||
169 | static void s3c64xx_pm_prepare(void) | ||
170 | { | ||
171 | /* store address of resume. */ | ||
172 | __raw_writel(virt_to_phys(s3c_cpu_resume), S3C64XX_INFORM0); | ||
173 | |||
174 | /* ensure previous wakeup state is cleared before sleeping */ | ||
175 | __raw_writel(__raw_readl(S3C64XX_WAKEUP_STAT), S3C64XX_WAKEUP_STAT); | ||
176 | } | ||
177 | |||
178 | static int s3c64xx_pm_init(void) | ||
179 | { | ||
180 | pm_cpu_prep = s3c64xx_pm_prepare; | ||
181 | pm_cpu_sleep = s3c64xx_cpu_suspend; | ||
182 | pm_uart_udivslot = 1; | ||
183 | return 0; | ||
184 | } | ||
185 | |||
186 | arch_initcall(s3c64xx_pm_init); | ||
diff --git a/arch/arm/plat-s3c64xx/sleep.S b/arch/arm/plat-s3c64xx/sleep.S new file mode 100644 index 000000000000..8e71fe90a373 --- /dev/null +++ b/arch/arm/plat-s3c64xx/sleep.S | |||
@@ -0,0 +1,144 @@ | |||
1 | /* linux/0arch/arm/plat-s3c64xx/sleep.S | ||
2 | * | ||
3 | * Copyright 2008 Openmoko, Inc. | ||
4 | * Copyright 2008 Simtec Electronics | ||
5 | * Ben Dooks <ben@simtec.co.uk> | ||
6 | * http://armlinux.simtec.co.uk/ | ||
7 | * | ||
8 | * S3C64XX CPU sleep code | ||
9 | * | ||
10 | * This program is free software; you can redistribute it and/or modify | ||
11 | * it under the terms of the GNU General Public License version 2 as | ||
12 | * published by the Free Software Foundation. | ||
13 | */ | ||
14 | |||
15 | #include <linux/linkage.h> | ||
16 | #include <asm/assembler.h> | ||
17 | #include <mach/map.h> | ||
18 | |||
19 | #undef S3C64XX_VA_GPIO | ||
20 | #define S3C64XX_VA_GPIO (0x0) | ||
21 | |||
22 | #include <plat/regs-gpio.h> | ||
23 | #include <plat/gpio-bank-n.h> | ||
24 | |||
25 | #define LL_UART (S3C_PA_UART + (0x400 * CONFIG_S3C_LOWLEVEL_UART_PORT)) | ||
26 | |||
27 | .text | ||
28 | |||
29 | /* s3c_cpu_save | ||
30 | * | ||
31 | * Save enough processor state to allow the restart of the pm.c | ||
32 | * code after resume. | ||
33 | * | ||
34 | * entry: | ||
35 | * r0 = pointer to the save block | ||
36 | */ | ||
37 | |||
38 | ENTRY(s3c_cpu_save) | ||
39 | stmfd sp!, { r4 - r12, lr } | ||
40 | |||
41 | mrc p15, 0, r4, c13, c0, 0 @ FCSE/PID | ||
42 | mrc p15, 0, r5, c3, c0, 0 @ Domain ID | ||
43 | mrc p15, 0, r6, c2, c0, 0 @ Translation Table BASE0 | ||
44 | mrc p15, 0, r7, c2, c0, 1 @ Translation Table BASE1 | ||
45 | mrc p15, 0, r8, c2, c0, 2 @ Translation Table Control | ||
46 | mrc p15, 0, r9, c1, c0, 0 @ Control register | ||
47 | mrc p15, 0, r10, c1, c0, 1 @ Auxiliary control register | ||
48 | mrc p15, 0, r11, c1, c0, 2 @ Co-processor access controls | ||
49 | |||
50 | stmia r0, { r4 - r13 } @ Save CP registers and SP | ||
51 | |||
52 | @@ save our state to ram | ||
53 | bl s3c_pm_cb_flushcache | ||
54 | |||
55 | @@ call final suspend code | ||
56 | ldr r0, =pm_cpu_sleep | ||
57 | ldr pc, [r0] | ||
58 | |||
59 | @@ return to the caller, after the MMU is turned on. | ||
60 | @@ restore the last bits of the stack and return. | ||
61 | resume_with_mmu: | ||
62 | ldmfd sp!, { r4 - r12, pc } @ return, from sp from s3c_cpu_save | ||
63 | |||
64 | .data | ||
65 | |||
66 | /* the next bit is code, but it requires easy access to the | ||
67 | * s3c_sleep_save_phys data before the MMU is switched on, so | ||
68 | * we store the code that needs this variable in the .data where | ||
69 | * the value can be written to (the .text segment is RO). | ||
70 | */ | ||
71 | |||
72 | .global s3c_sleep_save_phys | ||
73 | s3c_sleep_save_phys: | ||
74 | .word 0 | ||
75 | |||
76 | /* Sleep magic, the word before the resume entry point so that the | ||
77 | * bootloader can check for a resumeable image. */ | ||
78 | |||
79 | .word 0x2bedf00d | ||
80 | |||
81 | /* s3c_cpu_reusme | ||
82 | * | ||
83 | * This is the entry point, stored by whatever method the bootloader | ||
84 | * requires to get the kernel runnign again. This code expects to be | ||
85 | * entered with no caches live and the MMU disabled. It will then | ||
86 | * restore the MMU and other basic CP registers saved and restart | ||
87 | * the kernel C code to finish the resume code. | ||
88 | */ | ||
89 | |||
90 | ENTRY(s3c_cpu_resume) | ||
91 | msr cpsr_c, #PSR_I_BIT | PSR_F_BIT | SVC_MODE | ||
92 | ldr r2, =LL_UART /* for debug */ | ||
93 | |||
94 | #ifdef CONFIG_S3C_PM_DEBUG_LED_SMDK | ||
95 | /* Initialise the GPIO state if we are debugging via the SMDK LEDs, | ||
96 | * as the uboot version supplied resets these to inputs during the | ||
97 | * resume checks. | ||
98 | */ | ||
99 | |||
100 | ldr r3, =S3C64XX_PA_GPIO | ||
101 | ldr r0, [ r3, #S3C64XX_GPNCON ] | ||
102 | bic r0, r0, #(S3C64XX_GPN_CONMASK(12) | S3C64XX_GPN_CONMASK(13) | \ | ||
103 | S3C64XX_GPN_CONMASK(14) | S3C64XX_GPN_CONMASK(15)) | ||
104 | orr r0, r0, #(S3C64XX_GPN_OUTPUT(12) | S3C64XX_GPN_OUTPUT(13) | \ | ||
105 | S3C64XX_GPN_OUTPUT(14) | S3C64XX_GPN_OUTPUT(15)) | ||
106 | str r0, [ r3, #S3C64XX_GPNCON ] | ||
107 | |||
108 | ldr r0, [ r3, #S3C64XX_GPNDAT ] | ||
109 | bic r0, r0, #0xf << 12 @ GPN12..15 | ||
110 | orr r0, r0, #1 << 15 @ GPN15 | ||
111 | str r0, [ r3, #S3C64XX_GPNDAT ] | ||
112 | #endif | ||
113 | |||
114 | /* __v6_setup from arch/arm/mm/proc-v6.S, ensure that the caches | ||
115 | * are thoroughly cleaned just in case the bootloader didn't do it | ||
116 | * for us. */ | ||
117 | mov r0, #0 | ||
118 | mcr p15, 0, r0, c7, c14, 0 @ clean+invalidate D cache | ||
119 | mcr p15, 0, r0, c7, c5, 0 @ invalidate I cache | ||
120 | mcr p15, 0, r0, c7, c15, 0 @ clean+invalidate cache | ||
121 | mcr p15, 0, r0, c7, c10, 4 @ drain write buffer | ||
122 | @@mcr p15, 0, r0, c8, c7, 0 @ invalidate I + D TLBs | ||
123 | @@mcr p15, 0, r0, c7, c7, 0 @ Invalidate I + D caches | ||
124 | |||
125 | ldr r0, s3c_sleep_save_phys | ||
126 | ldmia r0, { r4 - r13 } | ||
127 | |||
128 | mcr p15, 0, r4, c13, c0, 0 @ FCSE/PID | ||
129 | mcr p15, 0, r5, c3, c0, 0 @ Domain ID | ||
130 | mcr p15, 0, r6, c2, c0, 0 @ Translation Table BASE0 | ||
131 | mcr p15, 0, r7, c2, c0, 1 @ Translation Table BASE1 | ||
132 | mcr p15, 0, r8, c2, c0, 2 @ Translation Table Control | ||
133 | mcr p15, 0, r10, c1, c0, 1 @ Auxiliary control register | ||
134 | |||
135 | mov r0, #0 @ restore copro access controls | ||
136 | mcr p15, 0, r11, c1, c0, 2 @ Co-processor access controls | ||
137 | mcr p15, 0, r0, c7, c5, 4 | ||
138 | |||
139 | ldr r2, =resume_with_mmu | ||
140 | mcr p15, 0, r9, c1, c0, 0 /* turn mmu back on */ | ||
141 | nop | ||
142 | mov pc, r2 /* jump back */ | ||
143 | |||
144 | .end | ||