diff options
author | Russell King <rmk@dyn-67.arm.linux.org.uk> | 2009-06-10 17:40:55 -0400 |
---|---|---|
committer | Russell King <rmk+kernel@arm.linux.org.uk> | 2009-06-10 17:41:06 -0400 |
commit | 547c32aeb5a24178ef20488ae0508efcc32ae8e4 (patch) | |
tree | fdc33154820cfddc845cef2f67380dace655b28f /arch/arm/plat-s3c64xx | |
parent | b0efb42478c81ed0ed9aa0dc444db200c32624f0 (diff) | |
parent | 99ae99533a059750c4b0f76295625b8ac5ea751a (diff) |
Merge branch for-rmk-devel of git://aeryn.fluff.org.uk/bjdooks/linux into devel
Diffstat (limited to 'arch/arm/plat-s3c64xx')
-rw-r--r-- | arch/arm/plat-s3c64xx/Kconfig | 10 | ||||
-rw-r--r-- | arch/arm/plat-s3c64xx/Makefile | 11 | ||||
-rw-r--r-- | arch/arm/plat-s3c64xx/clock.c | 19 | ||||
-rw-r--r-- | arch/arm/plat-s3c64xx/cpu.c | 32 | ||||
-rw-r--r-- | arch/arm/plat-s3c64xx/dma.c | 722 | ||||
-rw-r--r-- | arch/arm/plat-s3c64xx/gpiolib.c | 10 | ||||
-rw-r--r-- | arch/arm/plat-s3c64xx/include/plat/dma-plat.h | 70 | ||||
-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/include/plat/s3c6400.h | 3 | ||||
-rw-r--r-- | arch/arm/plat-s3c64xx/irq-eint.c | 3 | ||||
-rw-r--r-- | arch/arm/plat-s3c64xx/irq-pm.c | 111 | ||||
-rw-r--r-- | arch/arm/plat-s3c64xx/irq.c | 9 | ||||
-rw-r--r-- | arch/arm/plat-s3c64xx/pm.c | 175 | ||||
-rw-r--r-- | arch/arm/plat-s3c64xx/s3c6400-clock.c | 106 | ||||
-rw-r--r-- | arch/arm/plat-s3c64xx/setup-sdhci-gpio.c | 55 | ||||
-rw-r--r-- | arch/arm/plat-s3c64xx/sleep.S | 144 |
18 files changed, 1572 insertions, 8 deletions
diff --git a/arch/arm/plat-s3c64xx/Kconfig b/arch/arm/plat-s3c64xx/Kconfig index 54375a00a7d2..5ebd8b425a54 100644 --- a/arch/arm/plat-s3c64xx/Kconfig +++ b/arch/arm/plat-s3c64xx/Kconfig | |||
@@ -19,6 +19,7 @@ config PLAT_S3C64XX | |||
19 | select S3C_GPIO_PULL_UPDOWN | 19 | select S3C_GPIO_PULL_UPDOWN |
20 | select S3C_GPIO_CFG_S3C24XX | 20 | select S3C_GPIO_CFG_S3C24XX |
21 | select S3C_GPIO_CFG_S3C64XX | 21 | select S3C_GPIO_CFG_S3C64XX |
22 | select USB_ARCH_HAS_OHCI | ||
22 | help | 23 | help |
23 | Base platform code for any Samsung S3C64XX device | 24 | Base platform code for any Samsung S3C64XX device |
24 | 25 | ||
@@ -38,6 +39,10 @@ config CPU_S3C6400_CLOCK | |||
38 | Common clock support code for the S3C6400 that is shared | 39 | Common clock support code for the S3C6400 that is shared |
39 | by other CPUs in the series, such as the S3C6410. | 40 | by other CPUs in the series, such as the S3C6410. |
40 | 41 | ||
42 | config S3C64XX_DMA | ||
43 | bool "S3C64XX DMA" | ||
44 | select S3C_DMA | ||
45 | |||
41 | # platform specific device setup | 46 | # platform specific device setup |
42 | 47 | ||
43 | config S3C64XX_SETUP_I2C0 | 48 | config S3C64XX_SETUP_I2C0 |
@@ -59,4 +64,9 @@ config S3C64XX_SETUP_FB_24BPP | |||
59 | help | 64 | help |
60 | Common setup code for S3C64XX with an 24bpp RGB display helper. | 65 | Common setup code for S3C64XX with an 24bpp RGB display helper. |
61 | 66 | ||
67 | config S3C64XX_SETUP_SDHCI_GPIO | ||
68 | bool | ||
69 | help | ||
70 | Common setup code for S3C64XX SDHCI GPIO configurations | ||
71 | |||
62 | endif | 72 | endif |
diff --git a/arch/arm/plat-s3c64xx/Makefile b/arch/arm/plat-s3c64xx/Makefile index 2e6d79bf8f33..2ed5df34f9ea 100644 --- a/arch/arm/plat-s3c64xx/Makefile +++ b/arch/arm/plat-s3c64xx/Makefile | |||
@@ -24,8 +24,19 @@ 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 | obj-$(CONFIG_PM) += irq-pm.o | ||
32 | |||
33 | # DMA support | ||
34 | |||
35 | obj-$(CONFIG_S3C64XX_DMA) += dma.o | ||
36 | |||
27 | # Device setup | 37 | # Device setup |
28 | 38 | ||
29 | obj-$(CONFIG_S3C64XX_SETUP_I2C0) += setup-i2c0.o | 39 | obj-$(CONFIG_S3C64XX_SETUP_I2C0) += setup-i2c0.o |
30 | obj-$(CONFIG_S3C64XX_SETUP_I2C1) += setup-i2c1.o | 40 | obj-$(CONFIG_S3C64XX_SETUP_I2C1) += setup-i2c1.o |
31 | obj-$(CONFIG_S3C64XX_SETUP_FB_24BPP) += setup-fb-24bpp.o | 41 | obj-$(CONFIG_S3C64XX_SETUP_FB_24BPP) += setup-fb-24bpp.o |
42 | obj-$(CONFIG_S3C64XX_SETUP_SDHCI_GPIO) += setup-sdhci-gpio.o \ No newline at end of file | ||
diff --git a/arch/arm/plat-s3c64xx/clock.c b/arch/arm/plat-s3c64xx/clock.c index ad1b9682c9c3..0bc2fa1dfc40 100644 --- a/arch/arm/plat-s3c64xx/clock.c +++ b/arch/arm/plat-s3c64xx/clock.c | |||
@@ -27,6 +27,12 @@ | |||
27 | #include <plat/devs.h> | 27 | #include <plat/devs.h> |
28 | #include <plat/clock.h> | 28 | #include <plat/clock.h> |
29 | 29 | ||
30 | struct clk clk_h2 = { | ||
31 | .name = "hclk2", | ||
32 | .id = -1, | ||
33 | .rate = 0, | ||
34 | }; | ||
35 | |||
30 | struct clk clk_27m = { | 36 | struct clk clk_27m = { |
31 | .name = "clk_27m", | 37 | .name = "clk_27m", |
32 | .id = -1, | 38 | .id = -1, |
@@ -152,6 +158,18 @@ static struct clk init_clocks_disable[] = { | |||
152 | .parent = &clk_48m, | 158 | .parent = &clk_48m, |
153 | .enable = s3c64xx_sclk_ctrl, | 159 | .enable = s3c64xx_sclk_ctrl, |
154 | .ctrlbit = S3C_CLKCON_SCLK_MMC2_48, | 160 | .ctrlbit = S3C_CLKCON_SCLK_MMC2_48, |
161 | }, { | ||
162 | .name = "dma0", | ||
163 | .id = -1, | ||
164 | .parent = &clk_h, | ||
165 | .enable = s3c64xx_hclk_ctrl, | ||
166 | .ctrlbit = S3C_CLKCON_HCLK_DMA0, | ||
167 | }, { | ||
168 | .name = "dma1", | ||
169 | .id = -1, | ||
170 | .parent = &clk_h, | ||
171 | .enable = s3c64xx_hclk_ctrl, | ||
172 | .ctrlbit = S3C_CLKCON_HCLK_DMA1, | ||
155 | }, | 173 | }, |
156 | }; | 174 | }; |
157 | 175 | ||
@@ -246,6 +264,7 @@ static struct clk *clks[] __initdata = { | |||
246 | &clk_epll, | 264 | &clk_epll, |
247 | &clk_27m, | 265 | &clk_27m, |
248 | &clk_48m, | 266 | &clk_48m, |
267 | &clk_h2, | ||
249 | }; | 268 | }; |
250 | 269 | ||
251 | void __init s3c64xx_register_clocks(void) | 270 | void __init s3c64xx_register_clocks(void) |
diff --git a/arch/arm/plat-s3c64xx/cpu.c b/arch/arm/plat-s3c64xx/cpu.c index 91f49a3a665d..b1fdd83940a6 100644 --- a/arch/arm/plat-s3c64xx/cpu.c +++ b/arch/arm/plat-s3c64xx/cpu.c | |||
@@ -16,6 +16,7 @@ | |||
16 | #include <linux/module.h> | 16 | #include <linux/module.h> |
17 | #include <linux/interrupt.h> | 17 | #include <linux/interrupt.h> |
18 | #include <linux/ioport.h> | 18 | #include <linux/ioport.h> |
19 | #include <linux/sysdev.h> | ||
19 | #include <linux/serial_core.h> | 20 | #include <linux/serial_core.h> |
20 | #include <linux/platform_device.h> | 21 | #include <linux/platform_device.h> |
21 | #include <linux/io.h> | 22 | #include <linux/io.h> |
@@ -101,9 +102,24 @@ static struct map_desc s3c_iodesc[] __initdata = { | |||
101 | .pfn = __phys_to_pfn(S3C64XX_PA_MODEM), | 102 | .pfn = __phys_to_pfn(S3C64XX_PA_MODEM), |
102 | .length = SZ_4K, | 103 | .length = SZ_4K, |
103 | .type = MT_DEVICE, | 104 | .type = MT_DEVICE, |
105 | }, { | ||
106 | .virtual = (unsigned long)S3C_VA_WATCHDOG, | ||
107 | .pfn = __phys_to_pfn(S3C64XX_PA_WATCHDOG), | ||
108 | .length = SZ_4K, | ||
109 | .type = MT_DEVICE, | ||
104 | }, | 110 | }, |
105 | }; | 111 | }; |
106 | 112 | ||
113 | |||
114 | struct sysdev_class s3c64xx_sysclass = { | ||
115 | .name = "s3c64xx-core", | ||
116 | }; | ||
117 | |||
118 | static struct sys_device s3c64xx_sysdev = { | ||
119 | .cls = &s3c64xx_sysclass, | ||
120 | }; | ||
121 | |||
122 | |||
107 | /* read cpu identification code */ | 123 | /* read cpu identification code */ |
108 | 124 | ||
109 | void __init s3c64xx_init_io(struct map_desc *mach_desc, int size) | 125 | void __init s3c64xx_init_io(struct map_desc *mach_desc, int size) |
@@ -115,5 +131,21 @@ void __init s3c64xx_init_io(struct map_desc *mach_desc, int size) | |||
115 | iotable_init(mach_desc, size); | 131 | iotable_init(mach_desc, size); |
116 | 132 | ||
117 | idcode = __raw_readl(S3C_VA_SYS + 0x118); | 133 | idcode = __raw_readl(S3C_VA_SYS + 0x118); |
134 | if (!idcode) { | ||
135 | /* S3C6400 has the ID register in a different place, | ||
136 | * and needs a write before it can be read. */ | ||
137 | |||
138 | __raw_writel(0x0, S3C_VA_SYS + 0xA1C); | ||
139 | idcode = __raw_readl(S3C_VA_SYS + 0xA1C); | ||
140 | } | ||
141 | |||
118 | s3c_init_cpu(idcode, cpu_ids, ARRAY_SIZE(cpu_ids)); | 142 | s3c_init_cpu(idcode, cpu_ids, ARRAY_SIZE(cpu_ids)); |
119 | } | 143 | } |
144 | |||
145 | static __init int s3c64xx_sysdev_init(void) | ||
146 | { | ||
147 | sysdev_class_register(&s3c64xx_sysclass); | ||
148 | return sysdev_register(&s3c64xx_sysdev); | ||
149 | } | ||
150 | |||
151 | core_initcall(s3c64xx_sysdev_init); | ||
diff --git a/arch/arm/plat-s3c64xx/dma.c b/arch/arm/plat-s3c64xx/dma.c new file mode 100644 index 000000000000..67aa93dbb69e --- /dev/null +++ b/arch/arm/plat-s3c64xx/dma.c | |||
@@ -0,0 +1,722 @@ | |||
1 | /* linux/arch/arm/plat-s3c64xx/dma.c | ||
2 | * | ||
3 | * Copyright 2009 Openmoko, Inc. | ||
4 | * Copyright 2009 Simtec Electronics | ||
5 | * Ben Dooks <ben@simtec.co.uk> | ||
6 | * http://armlinux.simtec.co.uk/ | ||
7 | * | ||
8 | * S3C64XX DMA core | ||
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/kernel.h> | ||
16 | #include <linux/module.h> | ||
17 | #include <linux/interrupt.h> | ||
18 | #include <linux/dmapool.h> | ||
19 | #include <linux/sysdev.h> | ||
20 | #include <linux/errno.h> | ||
21 | #include <linux/delay.h> | ||
22 | #include <linux/clk.h> | ||
23 | #include <linux/err.h> | ||
24 | #include <linux/io.h> | ||
25 | |||
26 | #include <mach/dma.h> | ||
27 | #include <mach/map.h> | ||
28 | #include <mach/irqs.h> | ||
29 | |||
30 | #include <plat/dma-plat.h> | ||
31 | #include <plat/regs-sys.h> | ||
32 | |||
33 | #include <asm/hardware/pl080.h> | ||
34 | |||
35 | /* dma channel state information */ | ||
36 | |||
37 | struct s3c64xx_dmac { | ||
38 | struct sys_device sysdev; | ||
39 | struct clk *clk; | ||
40 | void __iomem *regs; | ||
41 | struct s3c2410_dma_chan *channels; | ||
42 | enum dma_ch chanbase; | ||
43 | }; | ||
44 | |||
45 | /* pool to provide LLI buffers */ | ||
46 | static struct dma_pool *dma_pool; | ||
47 | |||
48 | /* Debug configuration and code */ | ||
49 | |||
50 | static unsigned char debug_show_buffs = 0; | ||
51 | |||
52 | static void dbg_showchan(struct s3c2410_dma_chan *chan) | ||
53 | { | ||
54 | pr_debug("DMA%d: %08x->%08x L %08x C %08x,%08x S %08x\n", | ||
55 | chan->number, | ||
56 | readl(chan->regs + PL080_CH_SRC_ADDR), | ||
57 | readl(chan->regs + PL080_CH_DST_ADDR), | ||
58 | readl(chan->regs + PL080_CH_LLI), | ||
59 | readl(chan->regs + PL080_CH_CONTROL), | ||
60 | readl(chan->regs + PL080S_CH_CONTROL2), | ||
61 | readl(chan->regs + PL080S_CH_CONFIG)); | ||
62 | } | ||
63 | |||
64 | static void show_lli(struct pl080s_lli *lli) | ||
65 | { | ||
66 | pr_debug("LLI[%p] %08x->%08x, NL %08x C %08x,%08x\n", | ||
67 | lli, lli->src_addr, lli->dst_addr, lli->next_lli, | ||
68 | lli->control0, lli->control1); | ||
69 | } | ||
70 | |||
71 | static void dbg_showbuffs(struct s3c2410_dma_chan *chan) | ||
72 | { | ||
73 | struct s3c64xx_dma_buff *ptr; | ||
74 | struct s3c64xx_dma_buff *end; | ||
75 | |||
76 | pr_debug("DMA%d: buffs next %p, curr %p, end %p\n", | ||
77 | chan->number, chan->next, chan->curr, chan->end); | ||
78 | |||
79 | ptr = chan->next; | ||
80 | end = chan->end; | ||
81 | |||
82 | if (debug_show_buffs) { | ||
83 | for (; ptr != NULL; ptr = ptr->next) { | ||
84 | pr_debug("DMA%d: %08x ", | ||
85 | chan->number, ptr->lli_dma); | ||
86 | show_lli(ptr->lli); | ||
87 | } | ||
88 | } | ||
89 | } | ||
90 | |||
91 | /* End of Debug */ | ||
92 | |||
93 | static struct s3c2410_dma_chan *s3c64xx_dma_map_channel(unsigned int channel) | ||
94 | { | ||
95 | struct s3c2410_dma_chan *chan; | ||
96 | unsigned int start, offs; | ||
97 | |||
98 | start = 0; | ||
99 | |||
100 | if (channel >= DMACH_PCM1_TX) | ||
101 | start = 8; | ||
102 | |||
103 | for (offs = 0; offs < 8; offs++) { | ||
104 | chan = &s3c2410_chans[start + offs]; | ||
105 | if (!chan->in_use) | ||
106 | goto found; | ||
107 | } | ||
108 | |||
109 | return NULL; | ||
110 | |||
111 | found: | ||
112 | s3c_dma_chan_map[channel] = chan; | ||
113 | return chan; | ||
114 | } | ||
115 | |||
116 | int s3c2410_dma_config(unsigned int channel, int xferunit) | ||
117 | { | ||
118 | struct s3c2410_dma_chan *chan = s3c_dma_lookup_channel(channel); | ||
119 | |||
120 | if (chan == NULL) | ||
121 | return -EINVAL; | ||
122 | |||
123 | switch (xferunit) { | ||
124 | case 1: | ||
125 | chan->hw_width = 0; | ||
126 | break; | ||
127 | case 2: | ||
128 | chan->hw_width = 1; | ||
129 | break; | ||
130 | case 4: | ||
131 | chan->hw_width = 2; | ||
132 | break; | ||
133 | default: | ||
134 | printk(KERN_ERR "%s: illegal width %d\n", __func__, xferunit); | ||
135 | return -EINVAL; | ||
136 | } | ||
137 | |||
138 | return 0; | ||
139 | } | ||
140 | EXPORT_SYMBOL(s3c2410_dma_config); | ||
141 | |||
142 | static void s3c64xx_dma_fill_lli(struct s3c2410_dma_chan *chan, | ||
143 | struct pl080s_lli *lli, | ||
144 | dma_addr_t data, int size) | ||
145 | { | ||
146 | dma_addr_t src, dst; | ||
147 | u32 control0, control1; | ||
148 | |||
149 | switch (chan->source) { | ||
150 | case S3C2410_DMASRC_HW: | ||
151 | src = chan->dev_addr; | ||
152 | dst = data; | ||
153 | control0 = PL080_CONTROL_SRC_AHB2; | ||
154 | control0 |= (u32)chan->hw_width << PL080_CONTROL_SWIDTH_SHIFT; | ||
155 | control0 |= 2 << PL080_CONTROL_DWIDTH_SHIFT; | ||
156 | control0 |= PL080_CONTROL_DST_INCR; | ||
157 | break; | ||
158 | |||
159 | case S3C2410_DMASRC_MEM: | ||
160 | src = data; | ||
161 | dst = chan->dev_addr; | ||
162 | control0 = PL080_CONTROL_DST_AHB2; | ||
163 | control0 |= (u32)chan->hw_width << PL080_CONTROL_DWIDTH_SHIFT; | ||
164 | control0 |= 2 << PL080_CONTROL_SWIDTH_SHIFT; | ||
165 | control0 |= PL080_CONTROL_SRC_INCR; | ||
166 | break; | ||
167 | default: | ||
168 | BUG(); | ||
169 | } | ||
170 | |||
171 | /* note, we do not currently setup any of the burst controls */ | ||
172 | |||
173 | control1 = size >> chan->hw_width; /* size in no of xfers */ | ||
174 | control0 |= PL080_CONTROL_PROT_SYS; /* always in priv. mode */ | ||
175 | control0 |= PL080_CONTROL_TC_IRQ_EN; /* always fire IRQ */ | ||
176 | |||
177 | lli->src_addr = src; | ||
178 | lli->dst_addr = dst; | ||
179 | lli->next_lli = 0; | ||
180 | lli->control0 = control0; | ||
181 | lli->control1 = control1; | ||
182 | } | ||
183 | |||
184 | static void s3c64xx_lli_to_regs(struct s3c2410_dma_chan *chan, | ||
185 | struct pl080s_lli *lli) | ||
186 | { | ||
187 | void __iomem *regs = chan->regs; | ||
188 | |||
189 | pr_debug("%s: LLI %p => regs\n", __func__, lli); | ||
190 | show_lli(lli); | ||
191 | |||
192 | writel(lli->src_addr, regs + PL080_CH_SRC_ADDR); | ||
193 | writel(lli->dst_addr, regs + PL080_CH_DST_ADDR); | ||
194 | writel(lli->next_lli, regs + PL080_CH_LLI); | ||
195 | writel(lli->control0, regs + PL080_CH_CONTROL); | ||
196 | writel(lli->control1, regs + PL080S_CH_CONTROL2); | ||
197 | } | ||
198 | |||
199 | static int s3c64xx_dma_start(struct s3c2410_dma_chan *chan) | ||
200 | { | ||
201 | struct s3c64xx_dmac *dmac = chan->dmac; | ||
202 | u32 config; | ||
203 | u32 bit = chan->bit; | ||
204 | |||
205 | dbg_showchan(chan); | ||
206 | |||
207 | pr_debug("%s: clearing interrupts\n", __func__); | ||
208 | |||
209 | /* clear interrupts */ | ||
210 | writel(bit, dmac->regs + PL080_TC_CLEAR); | ||
211 | writel(bit, dmac->regs + PL080_ERR_CLEAR); | ||
212 | |||
213 | pr_debug("%s: starting channel\n", __func__); | ||
214 | |||
215 | config = readl(chan->regs + PL080S_CH_CONFIG); | ||
216 | config |= PL080_CONFIG_ENABLE; | ||
217 | |||
218 | pr_debug("%s: writing config %08x\n", __func__, config); | ||
219 | writel(config, chan->regs + PL080S_CH_CONFIG); | ||
220 | |||
221 | return 0; | ||
222 | } | ||
223 | |||
224 | static int s3c64xx_dma_stop(struct s3c2410_dma_chan *chan) | ||
225 | { | ||
226 | u32 config; | ||
227 | int timeout; | ||
228 | |||
229 | pr_debug("%s: stopping channel\n", __func__); | ||
230 | |||
231 | dbg_showchan(chan); | ||
232 | |||
233 | config = readl(chan->regs + PL080S_CH_CONFIG); | ||
234 | config |= PL080_CONFIG_HALT; | ||
235 | writel(config, chan->regs + PL080S_CH_CONFIG); | ||
236 | |||
237 | timeout = 1000; | ||
238 | do { | ||
239 | config = readl(chan->regs + PL080S_CH_CONFIG); | ||
240 | pr_debug("%s: %d - config %08x\n", __func__, timeout, config); | ||
241 | if (config & PL080_CONFIG_ACTIVE) | ||
242 | udelay(10); | ||
243 | else | ||
244 | break; | ||
245 | } while (--timeout > 0); | ||
246 | |||
247 | if (config & PL080_CONFIG_ACTIVE) { | ||
248 | printk(KERN_ERR "%s: channel still active\n", __func__); | ||
249 | return -EFAULT; | ||
250 | } | ||
251 | |||
252 | config = readl(chan->regs + PL080S_CH_CONFIG); | ||
253 | config &= ~PL080_CONFIG_ENABLE; | ||
254 | writel(config, chan->regs + PL080S_CH_CONFIG); | ||
255 | |||
256 | return 0; | ||
257 | } | ||
258 | |||
259 | static inline void s3c64xx_dma_bufffdone(struct s3c2410_dma_chan *chan, | ||
260 | struct s3c64xx_dma_buff *buf, | ||
261 | enum s3c2410_dma_buffresult result) | ||
262 | { | ||
263 | if (chan->callback_fn != NULL) | ||
264 | (chan->callback_fn)(chan, buf->pw, 0, result); | ||
265 | } | ||
266 | |||
267 | static void s3c64xx_dma_freebuff(struct s3c64xx_dma_buff *buff) | ||
268 | { | ||
269 | dma_pool_free(dma_pool, buff->lli, buff->lli_dma); | ||
270 | kfree(buff); | ||
271 | } | ||
272 | |||
273 | static int s3c64xx_dma_flush(struct s3c2410_dma_chan *chan) | ||
274 | { | ||
275 | struct s3c64xx_dma_buff *buff, *next; | ||
276 | u32 config; | ||
277 | |||
278 | dbg_showchan(chan); | ||
279 | |||
280 | pr_debug("%s: flushing channel\n", __func__); | ||
281 | |||
282 | config = readl(chan->regs + PL080S_CH_CONFIG); | ||
283 | config &= ~PL080_CONFIG_ENABLE; | ||
284 | writel(config, chan->regs + PL080S_CH_CONFIG); | ||
285 | |||
286 | /* dump all the buffers associated with this channel */ | ||
287 | |||
288 | for (buff = chan->curr; buff != NULL; buff = next) { | ||
289 | next = buff->next; | ||
290 | pr_debug("%s: buff %p (next %p)\n", __func__, buff, buff->next); | ||
291 | |||
292 | s3c64xx_dma_bufffdone(chan, buff, S3C2410_RES_ABORT); | ||
293 | s3c64xx_dma_freebuff(buff); | ||
294 | } | ||
295 | |||
296 | chan->curr = chan->next = chan->end = NULL; | ||
297 | |||
298 | return 0; | ||
299 | } | ||
300 | |||
301 | int s3c2410_dma_ctrl(unsigned int channel, enum s3c2410_chan_op op) | ||
302 | { | ||
303 | struct s3c2410_dma_chan *chan = s3c_dma_lookup_channel(channel); | ||
304 | |||
305 | WARN_ON(!chan); | ||
306 | if (!chan) | ||
307 | return -EINVAL; | ||
308 | |||
309 | switch (op) { | ||
310 | case S3C2410_DMAOP_START: | ||
311 | return s3c64xx_dma_start(chan); | ||
312 | |||
313 | case S3C2410_DMAOP_STOP: | ||
314 | return s3c64xx_dma_stop(chan); | ||
315 | |||
316 | case S3C2410_DMAOP_FLUSH: | ||
317 | return s3c64xx_dma_flush(chan); | ||
318 | |||
319 | /* belive PAUSE/RESUME are no-ops */ | ||
320 | case S3C2410_DMAOP_PAUSE: | ||
321 | case S3C2410_DMAOP_RESUME: | ||
322 | case S3C2410_DMAOP_STARTED: | ||
323 | case S3C2410_DMAOP_TIMEOUT: | ||
324 | return 0; | ||
325 | } | ||
326 | |||
327 | return -ENOENT; | ||
328 | } | ||
329 | EXPORT_SYMBOL(s3c2410_dma_ctrl); | ||
330 | |||
331 | /* s3c2410_dma_enque | ||
332 | * | ||
333 | */ | ||
334 | |||
335 | int s3c2410_dma_enqueue(unsigned int channel, void *id, | ||
336 | dma_addr_t data, int size) | ||
337 | { | ||
338 | struct s3c2410_dma_chan *chan = s3c_dma_lookup_channel(channel); | ||
339 | struct s3c64xx_dma_buff *next; | ||
340 | struct s3c64xx_dma_buff *buff; | ||
341 | struct pl080s_lli *lli; | ||
342 | int ret; | ||
343 | |||
344 | WARN_ON(!chan); | ||
345 | if (!chan) | ||
346 | return -EINVAL; | ||
347 | |||
348 | buff = kzalloc(sizeof(struct s3c64xx_dma_buff), GFP_KERNEL); | ||
349 | if (!buff) { | ||
350 | printk(KERN_ERR "%s: no memory for buffer\n", __func__); | ||
351 | return -ENOMEM; | ||
352 | } | ||
353 | |||
354 | lli = dma_pool_alloc(dma_pool, GFP_KERNEL, &buff->lli_dma); | ||
355 | if (!lli) { | ||
356 | printk(KERN_ERR "%s: no memory for lli\n", __func__); | ||
357 | ret = -ENOMEM; | ||
358 | goto err_buff; | ||
359 | } | ||
360 | |||
361 | pr_debug("%s: buff %p, dp %08x lli (%p, %08x) %d\n", | ||
362 | __func__, buff, data, lli, (u32)buff->lli_dma, size); | ||
363 | |||
364 | buff->lli = lli; | ||
365 | buff->pw = id; | ||
366 | |||
367 | s3c64xx_dma_fill_lli(chan, lli, data, size); | ||
368 | |||
369 | if ((next = chan->next) != NULL) { | ||
370 | struct s3c64xx_dma_buff *end = chan->end; | ||
371 | struct pl080s_lli *endlli = end->lli; | ||
372 | |||
373 | pr_debug("enquing onto channel\n"); | ||
374 | |||
375 | end->next = buff; | ||
376 | endlli->next_lli = buff->lli_dma; | ||
377 | |||
378 | if (chan->flags & S3C2410_DMAF_CIRCULAR) { | ||
379 | struct s3c64xx_dma_buff *curr = chan->curr; | ||
380 | lli->next_lli = curr->lli_dma; | ||
381 | } | ||
382 | |||
383 | if (next == chan->curr) { | ||
384 | writel(buff->lli_dma, chan->regs + PL080_CH_LLI); | ||
385 | chan->next = buff; | ||
386 | } | ||
387 | |||
388 | show_lli(endlli); | ||
389 | chan->end = buff; | ||
390 | } else { | ||
391 | pr_debug("enquing onto empty channel\n"); | ||
392 | |||
393 | chan->curr = buff; | ||
394 | chan->next = buff; | ||
395 | chan->end = buff; | ||
396 | |||
397 | s3c64xx_lli_to_regs(chan, lli); | ||
398 | } | ||
399 | |||
400 | show_lli(lli); | ||
401 | |||
402 | dbg_showchan(chan); | ||
403 | dbg_showbuffs(chan); | ||
404 | return 0; | ||
405 | |||
406 | err_buff: | ||
407 | kfree(buff); | ||
408 | return ret; | ||
409 | } | ||
410 | |||
411 | EXPORT_SYMBOL(s3c2410_dma_enqueue); | ||
412 | |||
413 | |||
414 | int s3c2410_dma_devconfig(int channel, | ||
415 | enum s3c2410_dmasrc source, | ||
416 | unsigned long devaddr) | ||
417 | { | ||
418 | struct s3c2410_dma_chan *chan = s3c_dma_lookup_channel(channel); | ||
419 | u32 peripheral; | ||
420 | u32 config = 0; | ||
421 | |||
422 | pr_debug("%s: channel %d, source %d, dev %08lx, chan %p\n", | ||
423 | __func__, channel, source, devaddr, chan); | ||
424 | |||
425 | WARN_ON(!chan); | ||
426 | if (!chan) | ||
427 | return -EINVAL; | ||
428 | |||
429 | peripheral = (chan->peripheral & 0xf); | ||
430 | chan->source = source; | ||
431 | chan->dev_addr = devaddr; | ||
432 | |||
433 | pr_debug("%s: peripheral %d\n", __func__, peripheral); | ||
434 | |||
435 | switch (source) { | ||
436 | case S3C2410_DMASRC_HW: | ||
437 | config = 2 << PL080_CONFIG_FLOW_CONTROL_SHIFT; | ||
438 | config |= peripheral << PL080_CONFIG_SRC_SEL_SHIFT; | ||
439 | break; | ||
440 | case S3C2410_DMASRC_MEM: | ||
441 | config = 1 << PL080_CONFIG_FLOW_CONTROL_SHIFT; | ||
442 | config |= peripheral << PL080_CONFIG_DST_SEL_SHIFT; | ||
443 | break; | ||
444 | default: | ||
445 | printk(KERN_ERR "%s: bad source\n", __func__); | ||
446 | return -EINVAL; | ||
447 | } | ||
448 | |||
449 | /* allow TC and ERR interrupts */ | ||
450 | config |= PL080_CONFIG_TC_IRQ_MASK; | ||
451 | config |= PL080_CONFIG_ERR_IRQ_MASK; | ||
452 | |||
453 | pr_debug("%s: config %08x\n", __func__, config); | ||
454 | |||
455 | writel(config, chan->regs + PL080S_CH_CONFIG); | ||
456 | |||
457 | return 0; | ||
458 | } | ||
459 | EXPORT_SYMBOL(s3c2410_dma_devconfig); | ||
460 | |||
461 | |||
462 | int s3c2410_dma_getposition(unsigned int channel, | ||
463 | dma_addr_t *src, dma_addr_t *dst) | ||
464 | { | ||
465 | struct s3c2410_dma_chan *chan = s3c_dma_lookup_channel(channel); | ||
466 | |||
467 | WARN_ON(!chan); | ||
468 | if (!chan) | ||
469 | return -EINVAL; | ||
470 | |||
471 | if (src != NULL) | ||
472 | *src = readl(chan->regs + PL080_CH_SRC_ADDR); | ||
473 | |||
474 | if (dst != NULL) | ||
475 | *dst = readl(chan->regs + PL080_CH_DST_ADDR); | ||
476 | |||
477 | return 0; | ||
478 | } | ||
479 | EXPORT_SYMBOL(s3c2410_dma_getposition); | ||
480 | |||
481 | /* s3c2410_request_dma | ||
482 | * | ||
483 | * get control of an dma channel | ||
484 | */ | ||
485 | |||
486 | int s3c2410_dma_request(unsigned int channel, | ||
487 | struct s3c2410_dma_client *client, | ||
488 | void *dev) | ||
489 | { | ||
490 | struct s3c2410_dma_chan *chan; | ||
491 | unsigned long flags; | ||
492 | |||
493 | pr_debug("dma%d: s3c2410_request_dma: client=%s, dev=%p\n", | ||
494 | channel, client->name, dev); | ||
495 | |||
496 | local_irq_save(flags); | ||
497 | |||
498 | chan = s3c64xx_dma_map_channel(channel); | ||
499 | if (chan == NULL) { | ||
500 | local_irq_restore(flags); | ||
501 | return -EBUSY; | ||
502 | } | ||
503 | |||
504 | dbg_showchan(chan); | ||
505 | |||
506 | chan->client = client; | ||
507 | chan->in_use = 1; | ||
508 | chan->peripheral = channel; | ||
509 | |||
510 | local_irq_restore(flags); | ||
511 | |||
512 | /* need to setup */ | ||
513 | |||
514 | pr_debug("%s: channel initialised, %p\n", __func__, chan); | ||
515 | |||
516 | return chan->number | DMACH_LOW_LEVEL; | ||
517 | } | ||
518 | |||
519 | EXPORT_SYMBOL(s3c2410_dma_request); | ||
520 | |||
521 | /* s3c2410_dma_free | ||
522 | * | ||
523 | * release the given channel back to the system, will stop and flush | ||
524 | * any outstanding transfers, and ensure the channel is ready for the | ||
525 | * next claimant. | ||
526 | * | ||
527 | * Note, although a warning is currently printed if the freeing client | ||
528 | * info is not the same as the registrant's client info, the free is still | ||
529 | * allowed to go through. | ||
530 | */ | ||
531 | |||
532 | int s3c2410_dma_free(unsigned int channel, struct s3c2410_dma_client *client) | ||
533 | { | ||
534 | struct s3c2410_dma_chan *chan = s3c_dma_lookup_channel(channel); | ||
535 | unsigned long flags; | ||
536 | |||
537 | if (chan == NULL) | ||
538 | return -EINVAL; | ||
539 | |||
540 | local_irq_save(flags); | ||
541 | |||
542 | if (chan->client != client) { | ||
543 | printk(KERN_WARNING "dma%d: possible free from different client (channel %p, passed %p)\n", | ||
544 | channel, chan->client, client); | ||
545 | } | ||
546 | |||
547 | /* sort out stopping and freeing the channel */ | ||
548 | |||
549 | |||
550 | chan->client = NULL; | ||
551 | chan->in_use = 0; | ||
552 | |||
553 | if (!(channel & DMACH_LOW_LEVEL)) | ||
554 | s3c_dma_chan_map[channel] = NULL; | ||
555 | |||
556 | local_irq_restore(flags); | ||
557 | |||
558 | return 0; | ||
559 | } | ||
560 | |||
561 | EXPORT_SYMBOL(s3c2410_dma_free); | ||
562 | |||
563 | |||
564 | static void s3c64xx_dma_tcirq(struct s3c64xx_dmac *dmac, int offs) | ||
565 | { | ||
566 | struct s3c2410_dma_chan *chan = dmac->channels + offs; | ||
567 | |||
568 | /* note, we currently do not bother to work out which buffer | ||
569 | * or buffers have been completed since the last tc-irq. */ | ||
570 | |||
571 | if (chan->callback_fn) | ||
572 | (chan->callback_fn)(chan, chan->curr->pw, 0, S3C2410_RES_OK); | ||
573 | } | ||
574 | |||
575 | static void s3c64xx_dma_errirq(struct s3c64xx_dmac *dmac, int offs) | ||
576 | { | ||
577 | printk(KERN_DEBUG "%s: offs %d\n", __func__, offs); | ||
578 | } | ||
579 | |||
580 | static irqreturn_t s3c64xx_dma_irq(int irq, void *pw) | ||
581 | { | ||
582 | struct s3c64xx_dmac *dmac = pw; | ||
583 | u32 tcstat, errstat; | ||
584 | u32 bit; | ||
585 | int offs; | ||
586 | |||
587 | tcstat = readl(dmac->regs + PL080_TC_STATUS); | ||
588 | errstat = readl(dmac->regs + PL080_ERR_STATUS); | ||
589 | |||
590 | for (offs = 0, bit = 1; offs < 8; offs++, bit <<= 1) { | ||
591 | if (tcstat & bit) { | ||
592 | writel(bit, dmac->regs + PL080_TC_CLEAR); | ||
593 | s3c64xx_dma_tcirq(dmac, offs); | ||
594 | } | ||
595 | |||
596 | if (errstat & bit) { | ||
597 | s3c64xx_dma_errirq(dmac, offs); | ||
598 | writel(bit, dmac->regs + PL080_ERR_CLEAR); | ||
599 | } | ||
600 | } | ||
601 | |||
602 | return IRQ_HANDLED; | ||
603 | } | ||
604 | |||
605 | static struct sysdev_class dma_sysclass = { | ||
606 | .name = "s3c64xx-dma", | ||
607 | }; | ||
608 | |||
609 | static int s3c64xx_dma_init1(int chno, enum dma_ch chbase, | ||
610 | int irq, unsigned int base) | ||
611 | { | ||
612 | struct s3c2410_dma_chan *chptr = &s3c2410_chans[chno]; | ||
613 | struct s3c64xx_dmac *dmac; | ||
614 | char clkname[16]; | ||
615 | void __iomem *regs; | ||
616 | void __iomem *regptr; | ||
617 | int err, ch; | ||
618 | |||
619 | dmac = kzalloc(sizeof(struct s3c64xx_dmac), GFP_KERNEL); | ||
620 | if (!dmac) { | ||
621 | printk(KERN_ERR "%s: failed to alloc mem\n", __func__); | ||
622 | return -ENOMEM; | ||
623 | } | ||
624 | |||
625 | dmac->sysdev.id = chno / 8; | ||
626 | dmac->sysdev.cls = &dma_sysclass; | ||
627 | |||
628 | err = sysdev_register(&dmac->sysdev); | ||
629 | if (err) { | ||
630 | printk(KERN_ERR "%s: failed to register sysdevice\n", __func__); | ||
631 | goto err_alloc; | ||
632 | } | ||
633 | |||
634 | regs = ioremap(base, 0x200); | ||
635 | if (!regs) { | ||
636 | printk(KERN_ERR "%s: failed to ioremap()\n", __func__); | ||
637 | err = -ENXIO; | ||
638 | goto err_dev; | ||
639 | } | ||
640 | |||
641 | snprintf(clkname, sizeof(clkname), "dma%d", dmac->sysdev.id); | ||
642 | |||
643 | dmac->clk = clk_get(NULL, clkname); | ||
644 | if (IS_ERR(dmac->clk)) { | ||
645 | printk(KERN_ERR "%s: failed to get clock %s\n", __func__, clkname); | ||
646 | err = PTR_ERR(dmac->clk); | ||
647 | goto err_map; | ||
648 | } | ||
649 | |||
650 | clk_enable(dmac->clk); | ||
651 | |||
652 | dmac->regs = regs; | ||
653 | dmac->chanbase = chbase; | ||
654 | dmac->channels = chptr; | ||
655 | |||
656 | err = request_irq(irq, s3c64xx_dma_irq, 0, "DMA", dmac); | ||
657 | if (err < 0) { | ||
658 | printk(KERN_ERR "%s: failed to get irq\n", __func__); | ||
659 | goto err_clk; | ||
660 | } | ||
661 | |||
662 | regptr = regs + PL080_Cx_BASE(0); | ||
663 | |||
664 | for (ch = 0; ch < 8; ch++, chno++, chptr++) { | ||
665 | printk(KERN_INFO "%s: registering DMA %d (%p)\n", | ||
666 | __func__, chno, regptr); | ||
667 | |||
668 | chptr->bit = 1 << ch; | ||
669 | chptr->number = chno; | ||
670 | chptr->dmac = dmac; | ||
671 | chptr->regs = regptr; | ||
672 | regptr += PL008_Cx_STRIDE; | ||
673 | } | ||
674 | |||
675 | /* for the moment, permanently enable the controller */ | ||
676 | writel(PL080_CONFIG_ENABLE, regs + PL080_CONFIG); | ||
677 | |||
678 | printk(KERN_INFO "PL080: IRQ %d, at %p\n", irq, regs); | ||
679 | |||
680 | return 0; | ||
681 | |||
682 | err_clk: | ||
683 | clk_disable(dmac->clk); | ||
684 | clk_put(dmac->clk); | ||
685 | err_map: | ||
686 | iounmap(regs); | ||
687 | err_dev: | ||
688 | sysdev_unregister(&dmac->sysdev); | ||
689 | err_alloc: | ||
690 | kfree(dmac); | ||
691 | return err; | ||
692 | } | ||
693 | |||
694 | static int __init s3c64xx_dma_init(void) | ||
695 | { | ||
696 | int ret; | ||
697 | |||
698 | printk(KERN_INFO "%s: Registering DMA channels\n", __func__); | ||
699 | |||
700 | dma_pool = dma_pool_create("DMA-LLI", NULL, 32, 16, 0); | ||
701 | if (!dma_pool) { | ||
702 | printk(KERN_ERR "%s: failed to create pool\n", __func__); | ||
703 | return -ENOMEM; | ||
704 | } | ||
705 | |||
706 | ret = sysdev_class_register(&dma_sysclass); | ||
707 | if (ret) { | ||
708 | printk(KERN_ERR "%s: failed to create sysclass\n", __func__); | ||
709 | return -ENOMEM; | ||
710 | } | ||
711 | |||
712 | /* Set all DMA configuration to be DMA, not SDMA */ | ||
713 | writel(0xffffff, S3C_SYSREG(0x110)); | ||
714 | |||
715 | /* Register standard DMA controlers */ | ||
716 | s3c64xx_dma_init1(0, DMACH_UART0, IRQ_DMA0, 0x75000000); | ||
717 | s3c64xx_dma_init1(8, DMACH_PCM1_TX, IRQ_DMA1, 0x75100000); | ||
718 | |||
719 | return 0; | ||
720 | } | ||
721 | |||
722 | arch_initcall(s3c64xx_dma_init); | ||
diff --git a/arch/arm/plat-s3c64xx/gpiolib.c b/arch/arm/plat-s3c64xx/gpiolib.c index 78ee52cffc9e..da7b60ee5e67 100644 --- a/arch/arm/plat-s3c64xx/gpiolib.c +++ b/arch/arm/plat-s3c64xx/gpiolib.c | |||
@@ -385,12 +385,19 @@ static __init void s3c64xx_gpiolib_add_4bit(struct s3c_gpio_chip *chip) | |||
385 | { | 385 | { |
386 | chip->chip.direction_input = s3c64xx_gpiolib_4bit_input; | 386 | chip->chip.direction_input = s3c64xx_gpiolib_4bit_input; |
387 | chip->chip.direction_output = s3c64xx_gpiolib_4bit_output; | 387 | chip->chip.direction_output = s3c64xx_gpiolib_4bit_output; |
388 | chip->pm = __gpio_pm(&s3c_gpio_pm_4bit); | ||
388 | } | 389 | } |
389 | 390 | ||
390 | static __init void s3c64xx_gpiolib_add_4bit2(struct s3c_gpio_chip *chip) | 391 | static __init void s3c64xx_gpiolib_add_4bit2(struct s3c_gpio_chip *chip) |
391 | { | 392 | { |
392 | chip->chip.direction_input = s3c64xx_gpiolib_4bit2_input; | 393 | chip->chip.direction_input = s3c64xx_gpiolib_4bit2_input; |
393 | chip->chip.direction_output = s3c64xx_gpiolib_4bit2_output; | 394 | chip->chip.direction_output = s3c64xx_gpiolib_4bit2_output; |
395 | chip->pm = __gpio_pm(&s3c_gpio_pm_4bit); | ||
396 | } | ||
397 | |||
398 | static __init void s3c64xx_gpiolib_add_2bit(struct s3c_gpio_chip *chip) | ||
399 | { | ||
400 | chip->pm = __gpio_pm(&s3c_gpio_pm_2bit); | ||
394 | } | 401 | } |
395 | 402 | ||
396 | static __init void s3c64xx_gpiolib_add(struct s3c_gpio_chip *chips, | 403 | static __init void s3c64xx_gpiolib_add(struct s3c_gpio_chip *chips, |
@@ -412,7 +419,8 @@ static __init int s3c64xx_gpiolib_init(void) | |||
412 | s3c64xx_gpiolib_add(gpio_4bit2, ARRAY_SIZE(gpio_4bit2), | 419 | s3c64xx_gpiolib_add(gpio_4bit2, ARRAY_SIZE(gpio_4bit2), |
413 | s3c64xx_gpiolib_add_4bit2); | 420 | s3c64xx_gpiolib_add_4bit2); |
414 | 421 | ||
415 | s3c64xx_gpiolib_add(gpio_2bit, ARRAY_SIZE(gpio_2bit), NULL); | 422 | s3c64xx_gpiolib_add(gpio_2bit, ARRAY_SIZE(gpio_2bit), |
423 | s3c64xx_gpiolib_add_2bit); | ||
416 | 424 | ||
417 | return 0; | 425 | return 0; |
418 | } | 426 | } |
diff --git a/arch/arm/plat-s3c64xx/include/plat/dma-plat.h b/arch/arm/plat-s3c64xx/include/plat/dma-plat.h new file mode 100644 index 000000000000..0c30dd986725 --- /dev/null +++ b/arch/arm/plat-s3c64xx/include/plat/dma-plat.h | |||
@@ -0,0 +1,70 @@ | |||
1 | /* linux/arch/arm/plat-s3c64xx/include/plat/dma-plat.h | ||
2 | * | ||
3 | * Copyright 2009 Openmoko, Inc. | ||
4 | * Copyright 2009 Simtec Electronics | ||
5 | * Ben Dooks <ben@simtec.co.uk> | ||
6 | * http://armlinux.simtec.co.uk/ | ||
7 | * | ||
8 | * S3C64XX DMA core | ||
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 | #define DMACH_LOW_LEVEL (1<<28) /* use this to specifiy hardware ch no */ | ||
16 | |||
17 | struct s3c64xx_dma_buff; | ||
18 | |||
19 | /** s3c64xx_dma_buff - S3C64XX DMA buffer descriptor | ||
20 | * @next: Pointer to next buffer in queue or ring. | ||
21 | * @pw: Client provided identifier | ||
22 | * @lli: Pointer to hardware descriptor this buffer is associated with. | ||
23 | * @lli_dma: Hardare address of the descriptor. | ||
24 | */ | ||
25 | struct s3c64xx_dma_buff { | ||
26 | struct s3c64xx_dma_buff *next; | ||
27 | |||
28 | void *pw; | ||
29 | struct pl080_lli *lli; | ||
30 | dma_addr_t lli_dma; | ||
31 | }; | ||
32 | |||
33 | struct s3c64xx_dmac; | ||
34 | |||
35 | struct s3c2410_dma_chan { | ||
36 | unsigned char number; /* number of this dma channel */ | ||
37 | unsigned char in_use; /* channel allocated */ | ||
38 | unsigned char bit; /* bit for enable/disable/etc */ | ||
39 | unsigned char hw_width; | ||
40 | unsigned char peripheral; | ||
41 | |||
42 | unsigned int flags; | ||
43 | enum s3c2410_dmasrc source; | ||
44 | |||
45 | |||
46 | dma_addr_t dev_addr; | ||
47 | |||
48 | struct s3c2410_dma_client *client; | ||
49 | struct s3c64xx_dmac *dmac; /* pointer to controller */ | ||
50 | |||
51 | void __iomem *regs; | ||
52 | |||
53 | /* cdriver callbacks */ | ||
54 | s3c2410_dma_cbfn_t callback_fn; /* buffer done callback */ | ||
55 | s3c2410_dma_opfn_t op_fn; /* channel op callback */ | ||
56 | |||
57 | /* buffer list and information */ | ||
58 | struct s3c64xx_dma_buff *curr; /* current dma buffer */ | ||
59 | struct s3c64xx_dma_buff *next; /* next buffer to load */ | ||
60 | struct s3c64xx_dma_buff *end; /* end of queue */ | ||
61 | |||
62 | /* note, when channel is running in circular mode, curr is the | ||
63 | * first buffer enqueued, end is the last and curr is where the | ||
64 | * last buffer-done event is set-at. The buffers are not freed | ||
65 | * and the last buffer hardware descriptor points back to the | ||
66 | * first. | ||
67 | */ | ||
68 | }; | ||
69 | |||
70 | #include <plat/dma-core.h> | ||
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/include/plat/s3c6400.h b/arch/arm/plat-s3c64xx/include/plat/s3c6400.h index 571eaa2e54f1..11f2e1e119b0 100644 --- a/arch/arm/plat-s3c64xx/include/plat/s3c6400.h +++ b/arch/arm/plat-s3c64xx/include/plat/s3c6400.h | |||
@@ -15,12 +15,13 @@ | |||
15 | /* Common init code for S3C6400 related SoCs */ | 15 | /* Common init code for S3C6400 related SoCs */ |
16 | 16 | ||
17 | extern void s3c6400_common_init_uarts(struct s3c2410_uartcfg *cfg, int no); | 17 | extern void s3c6400_common_init_uarts(struct s3c2410_uartcfg *cfg, int no); |
18 | extern void s3c6400_register_clocks(void); | 18 | extern void s3c6400_register_clocks(unsigned armclk_divlimit); |
19 | extern void s3c6400_setup_clocks(void); | 19 | extern void s3c6400_setup_clocks(void); |
20 | 20 | ||
21 | #ifdef CONFIG_CPU_S3C6400 | 21 | #ifdef CONFIG_CPU_S3C6400 |
22 | 22 | ||
23 | extern int s3c6400_init(void); | 23 | extern int s3c6400_init(void); |
24 | extern void s3c6400_init_irq(void); | ||
24 | extern void s3c6400_map_io(void); | 25 | extern void s3c6400_map_io(void); |
25 | extern void s3c6400_init_clocks(int xtal); | 26 | extern void s3c6400_init_clocks(int xtal); |
26 | 27 | ||
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/irq-pm.c b/arch/arm/plat-s3c64xx/irq-pm.c new file mode 100644 index 000000000000..ca523b5d4c17 --- /dev/null +++ b/arch/arm/plat-s3c64xx/irq-pm.c | |||
@@ -0,0 +1,111 @@ | |||
1 | /* arch/arm/plat-s3c64xx/irq-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 - Interrupt handling Power Management | ||
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/kernel.h> | ||
16 | #include <linux/sysdev.h> | ||
17 | #include <linux/interrupt.h> | ||
18 | #include <linux/serial_core.h> | ||
19 | #include <linux/irq.h> | ||
20 | #include <linux/io.h> | ||
21 | |||
22 | #include <mach/map.h> | ||
23 | |||
24 | #include <plat/regs-serial.h> | ||
25 | #include <plat/regs-timer.h> | ||
26 | #include <plat/regs-gpio.h> | ||
27 | #include <plat/cpu.h> | ||
28 | #include <plat/pm.h> | ||
29 | |||
30 | /* We handled all the IRQ types in this code, to save having to make several | ||
31 | * small files to handle each different type separately. Having the EINT_GRP | ||
32 | * code here shouldn't be as much bloat as the IRQ table space needed when | ||
33 | * they are enabled. The added benefit is we ensure that these registers are | ||
34 | * in the same state as we suspended. | ||
35 | */ | ||
36 | |||
37 | static struct sleep_save irq_save[] = { | ||
38 | SAVE_ITEM(S3C64XX_PRIORITY), | ||
39 | SAVE_ITEM(S3C64XX_EINT0CON0), | ||
40 | SAVE_ITEM(S3C64XX_EINT0CON1), | ||
41 | SAVE_ITEM(S3C64XX_EINT0FLTCON0), | ||
42 | SAVE_ITEM(S3C64XX_EINT0FLTCON1), | ||
43 | SAVE_ITEM(S3C64XX_EINT0FLTCON2), | ||
44 | SAVE_ITEM(S3C64XX_EINT0FLTCON3), | ||
45 | SAVE_ITEM(S3C64XX_EINT0MASK), | ||
46 | SAVE_ITEM(S3C64XX_TINT_CSTAT), | ||
47 | }; | ||
48 | |||
49 | static struct irq_grp_save { | ||
50 | u32 fltcon; | ||
51 | u32 con; | ||
52 | u32 mask; | ||
53 | } eint_grp_save[5]; | ||
54 | |||
55 | static u32 irq_uart_mask[CONFIG_SERIAL_SAMSUNG_UARTS]; | ||
56 | |||
57 | static int s3c64xx_irq_pm_suspend(struct sys_device *dev, pm_message_t state) | ||
58 | { | ||
59 | struct irq_grp_save *grp = eint_grp_save; | ||
60 | int i; | ||
61 | |||
62 | S3C_PMDBG("%s: suspending IRQs\n", __func__); | ||
63 | |||
64 | s3c_pm_do_save(irq_save, ARRAY_SIZE(irq_save)); | ||
65 | |||
66 | for (i = 0; i < CONFIG_SERIAL_SAMSUNG_UARTS; i++) | ||
67 | irq_uart_mask[i] = __raw_readl(S3C_VA_UARTx(i) + S3C64XX_UINTM); | ||
68 | |||
69 | for (i = 0; i < ARRAY_SIZE(eint_grp_save); i++, grp++) { | ||
70 | grp->con = __raw_readl(S3C64XX_EINT12CON + (i * 4)); | ||
71 | grp->mask = __raw_readl(S3C64XX_EINT12MASK + (i * 4)); | ||
72 | grp->fltcon = __raw_readl(S3C64XX_EINT12FLTCON + (i * 4)); | ||
73 | } | ||
74 | |||
75 | return 0; | ||
76 | } | ||
77 | |||
78 | static int s3c64xx_irq_pm_resume(struct sys_device *dev) | ||
79 | { | ||
80 | struct irq_grp_save *grp = eint_grp_save; | ||
81 | int i; | ||
82 | |||
83 | S3C_PMDBG("%s: resuming IRQs\n", __func__); | ||
84 | |||
85 | s3c_pm_do_restore(irq_save, ARRAY_SIZE(irq_save)); | ||
86 | |||
87 | for (i = 0; i < CONFIG_SERIAL_SAMSUNG_UARTS; i++) | ||
88 | __raw_writel(irq_uart_mask[i], S3C_VA_UARTx(i) + S3C64XX_UINTM); | ||
89 | |||
90 | for (i = 0; i < ARRAY_SIZE(eint_grp_save); i++, grp++) { | ||
91 | __raw_writel(grp->con, S3C64XX_EINT12CON + (i * 4)); | ||
92 | __raw_writel(grp->mask, S3C64XX_EINT12MASK + (i * 4)); | ||
93 | __raw_writel(grp->fltcon, S3C64XX_EINT12FLTCON + (i * 4)); | ||
94 | } | ||
95 | |||
96 | S3C_PMDBG("%s: IRQ configuration restored\n", __func__); | ||
97 | return 0; | ||
98 | } | ||
99 | |||
100 | static struct sysdev_driver s3c64xx_irq_driver = { | ||
101 | .suspend = s3c64xx_irq_pm_suspend, | ||
102 | .resume = s3c64xx_irq_pm_resume, | ||
103 | }; | ||
104 | |||
105 | static int __init s3c64xx_irq_pm_init(void) | ||
106 | { | ||
107 | return sysdev_driver_register(&s3c64xx_sysclass, &s3c64xx_irq_driver); | ||
108 | } | ||
109 | |||
110 | arch_initcall(s3c64xx_irq_pm_init); | ||
111 | |||
diff --git a/arch/arm/plat-s3c64xx/irq.c b/arch/arm/plat-s3c64xx/irq.c index f22edf7c2d2d..8dc5b6da9789 100644 --- a/arch/arm/plat-s3c64xx/irq.c +++ b/arch/arm/plat-s3c64xx/irq.c | |||
@@ -14,12 +14,14 @@ | |||
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/serial_core.h> | ||
17 | #include <linux/irq.h> | 18 | #include <linux/irq.h> |
18 | #include <linux/io.h> | 19 | #include <linux/io.h> |
19 | 20 | ||
20 | #include <asm/hardware/vic.h> | 21 | #include <asm/hardware/vic.h> |
21 | 22 | ||
22 | #include <mach/map.h> | 23 | #include <mach/map.h> |
24 | #include <plat/regs-serial.h> | ||
23 | #include <plat/regs-timer.h> | 25 | #include <plat/regs-timer.h> |
24 | #include <plat/cpu.h> | 26 | #include <plat/cpu.h> |
25 | 27 | ||
@@ -135,9 +137,6 @@ static inline unsigned int s3c_irq_uart_bit(unsigned int irq) | |||
135 | } | 137 | } |
136 | 138 | ||
137 | /* UART interrupt registers, not worth adding to seperate include header */ | 139 | /* UART interrupt registers, not worth adding to seperate include header */ |
138 | #define S3C64XX_UINTP 0x30 | ||
139 | #define S3C64XX_UINTSP 0x34 | ||
140 | #define S3C64XX_UINTM 0x38 | ||
141 | 140 | ||
142 | static void s3c_irq_uart_mask(unsigned int irq) | 141 | static void s3c_irq_uart_mask(unsigned int irq) |
143 | { | 142 | { |
@@ -233,8 +232,8 @@ void __init s3c64xx_init_irq(u32 vic0_valid, u32 vic1_valid) | |||
233 | printk(KERN_DEBUG "%s: initialising interrupts\n", __func__); | 232 | printk(KERN_DEBUG "%s: initialising interrupts\n", __func__); |
234 | 233 | ||
235 | /* initialise the pair of VICs */ | 234 | /* initialise the pair of VICs */ |
236 | vic_init(S3C_VA_VIC0, S3C_VIC0_BASE, vic0_valid); | 235 | vic_init(S3C_VA_VIC0, S3C_VIC0_BASE, vic0_valid, 0); |
237 | vic_init(S3C_VA_VIC1, S3C_VIC1_BASE, vic1_valid); | 236 | vic_init(S3C_VA_VIC1, S3C_VIC1_BASE, vic1_valid, 0); |
238 | 237 | ||
239 | /* add the timer sub-irqs */ | 238 | /* add the timer sub-irqs */ |
240 | 239 | ||
diff --git a/arch/arm/plat-s3c64xx/pm.c b/arch/arm/plat-s3c64xx/pm.c new file mode 100644 index 000000000000..07a6516a4f3c --- /dev/null +++ b/arch/arm/plat-s3c64xx/pm.c | |||
@@ -0,0 +1,175 @@ | |||
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_restore_core(void) | ||
100 | { | ||
101 | __raw_writel(0, S3C64XX_EINT_MASK); | ||
102 | |||
103 | s3c_pm_debug_smdkled(1 << 2, 0); | ||
104 | |||
105 | s3c_pm_do_restore_core(core_save, ARRAY_SIZE(core_save)); | ||
106 | s3c_pm_do_restore(misc_save, ARRAY_SIZE(misc_save)); | ||
107 | } | ||
108 | |||
109 | void s3c_pm_save_core(void) | ||
110 | { | ||
111 | s3c_pm_do_save(misc_save, ARRAY_SIZE(misc_save)); | ||
112 | s3c_pm_do_save(core_save, ARRAY_SIZE(core_save)); | ||
113 | } | ||
114 | |||
115 | /* since both s3c6400 and s3c6410 share the same sleep pm calls, we | ||
116 | * put the per-cpu code in here until any new cpu comes along and changes | ||
117 | * this. | ||
118 | */ | ||
119 | |||
120 | #include <plat/regs-gpio.h> | ||
121 | |||
122 | static void s3c64xx_cpu_suspend(void) | ||
123 | { | ||
124 | unsigned long tmp; | ||
125 | |||
126 | /* set our standby method to sleep */ | ||
127 | |||
128 | tmp = __raw_readl(S3C64XX_PWR_CFG); | ||
129 | tmp &= ~S3C64XX_PWRCFG_CFG_WFI_MASK; | ||
130 | tmp |= S3C64XX_PWRCFG_CFG_WFI_SLEEP; | ||
131 | __raw_writel(tmp, S3C64XX_PWR_CFG); | ||
132 | |||
133 | /* clear any old wakeup */ | ||
134 | |||
135 | __raw_writel(__raw_readl(S3C64XX_WAKEUP_STAT), | ||
136 | S3C64XX_WAKEUP_STAT); | ||
137 | |||
138 | /* set the LED state to 0110 over sleep */ | ||
139 | s3c_pm_debug_smdkled(3 << 1, 0xf); | ||
140 | |||
141 | /* issue the standby signal into the pm unit. Note, we | ||
142 | * issue a write-buffer drain just in case */ | ||
143 | |||
144 | tmp = 0; | ||
145 | |||
146 | asm("b 1f\n\t" | ||
147 | ".align 5\n\t" | ||
148 | "1:\n\t" | ||
149 | "mcr p15, 0, %0, c7, c10, 5\n\t" | ||
150 | "mcr p15, 0, %0, c7, c10, 4\n\t" | ||
151 | "mcr p15, 0, %0, c7, c0, 4" :: "r" (tmp)); | ||
152 | |||
153 | /* we should never get past here */ | ||
154 | |||
155 | panic("sleep resumed to originator?"); | ||
156 | } | ||
157 | |||
158 | static void s3c64xx_pm_prepare(void) | ||
159 | { | ||
160 | /* store address of resume. */ | ||
161 | __raw_writel(virt_to_phys(s3c_cpu_resume), S3C64XX_INFORM0); | ||
162 | |||
163 | /* ensure previous wakeup state is cleared before sleeping */ | ||
164 | __raw_writel(__raw_readl(S3C64XX_WAKEUP_STAT), S3C64XX_WAKEUP_STAT); | ||
165 | } | ||
166 | |||
167 | static int s3c64xx_pm_init(void) | ||
168 | { | ||
169 | pm_cpu_prep = s3c64xx_pm_prepare; | ||
170 | pm_cpu_sleep = s3c64xx_cpu_suspend; | ||
171 | pm_uart_udivslot = 1; | ||
172 | return 0; | ||
173 | } | ||
174 | |||
175 | arch_initcall(s3c64xx_pm_init); | ||
diff --git a/arch/arm/plat-s3c64xx/s3c6400-clock.c b/arch/arm/plat-s3c64xx/s3c6400-clock.c index 05b17528041e..1debc1f9f987 100644 --- a/arch/arm/plat-s3c64xx/s3c6400-clock.c +++ b/arch/arm/plat-s3c64xx/s3c6400-clock.c | |||
@@ -133,6 +133,65 @@ static struct clksrc_clk clk_mout_mpll = { | |||
133 | .sources = &clk_src_mpll, | 133 | .sources = &clk_src_mpll, |
134 | }; | 134 | }; |
135 | 135 | ||
136 | static unsigned int armclk_mask; | ||
137 | |||
138 | static unsigned long s3c64xx_clk_arm_get_rate(struct clk *clk) | ||
139 | { | ||
140 | unsigned long rate = clk_get_rate(clk->parent); | ||
141 | u32 clkdiv; | ||
142 | |||
143 | /* divisor mask starts at bit0, so no need to shift */ | ||
144 | clkdiv = __raw_readl(S3C_CLK_DIV0) & armclk_mask; | ||
145 | |||
146 | return rate / (clkdiv + 1); | ||
147 | } | ||
148 | |||
149 | static unsigned long s3c64xx_clk_arm_round_rate(struct clk *clk, | ||
150 | unsigned long rate) | ||
151 | { | ||
152 | unsigned long parent = clk_get_rate(clk->parent); | ||
153 | u32 div; | ||
154 | |||
155 | if (parent < rate) | ||
156 | return rate; | ||
157 | |||
158 | div = (parent / rate) - 1; | ||
159 | if (div > armclk_mask) | ||
160 | div = armclk_mask; | ||
161 | |||
162 | return parent / (div + 1); | ||
163 | } | ||
164 | |||
165 | static int s3c64xx_clk_arm_set_rate(struct clk *clk, unsigned long rate) | ||
166 | { | ||
167 | unsigned long parent = clk_get_rate(clk->parent); | ||
168 | u32 div; | ||
169 | u32 val; | ||
170 | |||
171 | if (rate < parent / (armclk_mask + 1)) | ||
172 | return -EINVAL; | ||
173 | |||
174 | rate = clk_round_rate(clk, rate); | ||
175 | div = clk_get_rate(clk->parent) / rate; | ||
176 | |||
177 | val = __raw_readl(S3C_CLK_DIV0); | ||
178 | val &= armclk_mask; | ||
179 | val |= (div - 1); | ||
180 | __raw_writel(val, S3C_CLK_DIV0); | ||
181 | |||
182 | return 0; | ||
183 | |||
184 | } | ||
185 | |||
186 | static struct clk clk_arm = { | ||
187 | .name = "armclk", | ||
188 | .id = -1, | ||
189 | .parent = &clk_mout_apll.clk, | ||
190 | .get_rate = s3c64xx_clk_arm_get_rate, | ||
191 | .set_rate = s3c64xx_clk_arm_set_rate, | ||
192 | .round_rate = s3c64xx_clk_arm_round_rate, | ||
193 | }; | ||
194 | |||
136 | static unsigned long s3c64xx_clk_doutmpll_get_rate(struct clk *clk) | 195 | static unsigned long s3c64xx_clk_doutmpll_get_rate(struct clk *clk) |
137 | { | 196 | { |
138 | unsigned long rate = clk_get_rate(clk->parent); | 197 | unsigned long rate = clk_get_rate(clk->parent); |
@@ -520,6 +579,33 @@ static struct clksrc_clk clk_irda = { | |||
520 | .reg_divider = S3C_CLK_DIV2, | 579 | .reg_divider = S3C_CLK_DIV2, |
521 | }; | 580 | }; |
522 | 581 | ||
582 | static struct clk *clkset_camif_list[] = { | ||
583 | &clk_h2, | ||
584 | }; | ||
585 | |||
586 | static struct clk_sources clkset_camif = { | ||
587 | .sources = clkset_camif_list, | ||
588 | .nr_sources = ARRAY_SIZE(clkset_camif_list), | ||
589 | }; | ||
590 | |||
591 | static struct clksrc_clk clk_camif = { | ||
592 | .clk = { | ||
593 | .name = "camera", | ||
594 | .id = -1, | ||
595 | .ctrlbit = S3C_CLKCON_SCLK_CAM, | ||
596 | .enable = s3c64xx_sclk_ctrl, | ||
597 | .set_parent = s3c64xx_setparent_clksrc, | ||
598 | .get_rate = s3c64xx_getrate_clksrc, | ||
599 | .set_rate = s3c64xx_setrate_clksrc, | ||
600 | .round_rate = s3c64xx_roundrate_clksrc, | ||
601 | }, | ||
602 | .shift = 0, | ||
603 | .mask = 0, | ||
604 | .sources = &clkset_camif, | ||
605 | .divider_shift = S3C6400_CLKDIV0_CAM_SHIFT, | ||
606 | .reg_divider = S3C_CLK_DIV0, | ||
607 | }; | ||
608 | |||
523 | /* Clock initialisation code */ | 609 | /* Clock initialisation code */ |
524 | 610 | ||
525 | static struct clksrc_clk *init_parents[] = { | 611 | static struct clksrc_clk *init_parents[] = { |
@@ -536,6 +622,7 @@ static struct clksrc_clk *init_parents[] = { | |||
536 | &clk_audio0, | 622 | &clk_audio0, |
537 | &clk_audio1, | 623 | &clk_audio1, |
538 | &clk_irda, | 624 | &clk_irda, |
625 | &clk_camif, | ||
539 | }; | 626 | }; |
540 | 627 | ||
541 | static void __init_or_cpufreq s3c6400_set_clksrc(struct clksrc_clk *clk) | 628 | static void __init_or_cpufreq s3c6400_set_clksrc(struct clksrc_clk *clk) |
@@ -608,6 +695,7 @@ void __init_or_cpufreq s3c6400_setup_clocks(void) | |||
608 | clk_fout_epll.rate = epll; | 695 | clk_fout_epll.rate = epll; |
609 | clk_fout_apll.rate = apll; | 696 | clk_fout_apll.rate = apll; |
610 | 697 | ||
698 | clk_h2.rate = hclk2; | ||
611 | clk_h.rate = hclk; | 699 | clk_h.rate = hclk; |
612 | clk_p.rate = pclk; | 700 | clk_p.rate = pclk; |
613 | clk_f.rate = fclk; | 701 | clk_f.rate = fclk; |
@@ -635,14 +723,30 @@ static struct clk *clks[] __initdata = { | |||
635 | &clk_audio0.clk, | 723 | &clk_audio0.clk, |
636 | &clk_audio1.clk, | 724 | &clk_audio1.clk, |
637 | &clk_irda.clk, | 725 | &clk_irda.clk, |
726 | &clk_camif.clk, | ||
727 | &clk_arm, | ||
638 | }; | 728 | }; |
639 | 729 | ||
640 | void __init s3c6400_register_clocks(void) | 730 | /** |
731 | * s3c6400_register_clocks - register clocks for s3c6400 and above | ||
732 | * @armclk_divlimit: Divisor mask for ARMCLK | ||
733 | * | ||
734 | * Register the clocks for the S3C6400 and above SoC range, such | ||
735 | * as ARMCLK and the clocks which have divider chains attached. | ||
736 | * | ||
737 | * This call does not setup the clocks, which is left to the | ||
738 | * s3c6400_setup_clocks() call which may be needed by the cpufreq | ||
739 | * or resume code to re-set the clocks if the bootloader has changed | ||
740 | * them. | ||
741 | */ | ||
742 | void __init s3c6400_register_clocks(unsigned armclk_divlimit) | ||
641 | { | 743 | { |
642 | struct clk *clkp; | 744 | struct clk *clkp; |
643 | int ret; | 745 | int ret; |
644 | int ptr; | 746 | int ptr; |
645 | 747 | ||
748 | armclk_mask = armclk_divlimit; | ||
749 | |||
646 | for (ptr = 0; ptr < ARRAY_SIZE(clks); ptr++) { | 750 | for (ptr = 0; ptr < ARRAY_SIZE(clks); ptr++) { |
647 | clkp = clks[ptr]; | 751 | clkp = clks[ptr]; |
648 | ret = s3c24xx_register_clock(clkp); | 752 | ret = s3c24xx_register_clock(clkp); |
diff --git a/arch/arm/plat-s3c64xx/setup-sdhci-gpio.c b/arch/arm/plat-s3c64xx/setup-sdhci-gpio.c new file mode 100644 index 000000000000..5417123b0ac1 --- /dev/null +++ b/arch/arm/plat-s3c64xx/setup-sdhci-gpio.c | |||
@@ -0,0 +1,55 @@ | |||
1 | /* linux/arch/arm/plat-s3c64xx/setup-sdhci-gpio.c | ||
2 | * | ||
3 | * Copyright 2008 Simtec Electronics | ||
4 | * Ben Dooks <ben@simtec.co.uk> | ||
5 | * http://armlinux.simtec.co.uk/ | ||
6 | * | ||
7 | * S3C64XX - Helper functions for setting up SDHCI device(s) GPIO (HSMMC) | ||
8 | * | ||
9 | * This program is free software; you can redistribute it and/or modify | ||
10 | * it under the terms of the GNU General Public License version 2 as | ||
11 | * published by the Free Software Foundation. | ||
12 | */ | ||
13 | |||
14 | #include <linux/kernel.h> | ||
15 | #include <linux/types.h> | ||
16 | #include <linux/interrupt.h> | ||
17 | #include <linux/platform_device.h> | ||
18 | #include <linux/io.h> | ||
19 | |||
20 | #include <mach/gpio.h> | ||
21 | #include <plat/gpio-cfg.h> | ||
22 | |||
23 | void s3c64xx_setup_sdhci0_cfg_gpio(struct platform_device *dev, int width) | ||
24 | { | ||
25 | unsigned int gpio; | ||
26 | unsigned int end; | ||
27 | |||
28 | end = S3C64XX_GPG(2 + width); | ||
29 | |||
30 | /* Set all the necessary GPG pins to special-function 0 */ | ||
31 | for (gpio = S3C64XX_GPG(0); gpio < end; gpio++) { | ||
32 | s3c_gpio_cfgpin(gpio, S3C_GPIO_SFN(2)); | ||
33 | s3c_gpio_setpull(gpio, S3C_GPIO_PULL_NONE); | ||
34 | } | ||
35 | |||
36 | s3c_gpio_setpull(S3C64XX_GPG(6), S3C_GPIO_PULL_UP); | ||
37 | s3c_gpio_cfgpin(S3C64XX_GPG(6), S3C_GPIO_SFN(2)); | ||
38 | } | ||
39 | |||
40 | void s3c64xx_setup_sdhci1_cfg_gpio(struct platform_device *dev, int width) | ||
41 | { | ||
42 | unsigned int gpio; | ||
43 | unsigned int end; | ||
44 | |||
45 | end = S3C64XX_GPH(2 + width); | ||
46 | |||
47 | /* Set all the necessary GPG pins to special-function 0 */ | ||
48 | for (gpio = S3C64XX_GPH(0); gpio < end; gpio++) { | ||
49 | s3c_gpio_cfgpin(gpio, S3C_GPIO_SFN(2)); | ||
50 | s3c_gpio_setpull(gpio, S3C_GPIO_PULL_NONE); | ||
51 | } | ||
52 | |||
53 | s3c_gpio_setpull(S3C64XX_GPG(6), S3C_GPIO_PULL_UP); | ||
54 | s3c_gpio_cfgpin(S3C64XX_GPG(6), S3C_GPIO_SFN(3)); | ||
55 | } | ||
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 | ||