aboutsummaryrefslogtreecommitdiffstats
path: root/arch/arm/plat-s3c64xx
diff options
context:
space:
mode:
Diffstat (limited to 'arch/arm/plat-s3c64xx')
-rw-r--r--arch/arm/plat-s3c64xx/Kconfig10
-rw-r--r--arch/arm/plat-s3c64xx/Makefile11
-rw-r--r--arch/arm/plat-s3c64xx/clock.c19
-rw-r--r--arch/arm/plat-s3c64xx/cpu.c32
-rw-r--r--arch/arm/plat-s3c64xx/dma.c722
-rw-r--r--arch/arm/plat-s3c64xx/gpiolib.c10
-rw-r--r--arch/arm/plat-s3c64xx/include/plat/dma-plat.h70
-rw-r--r--arch/arm/plat-s3c64xx/include/plat/irqs.h1
-rw-r--r--arch/arm/plat-s3c64xx/include/plat/pm-core.h98
-rw-r--r--arch/arm/plat-s3c64xx/include/plat/regs-clock.h1
-rw-r--r--arch/arm/plat-s3c64xx/include/plat/s3c6400.h3
-rw-r--r--arch/arm/plat-s3c64xx/irq-eint.c3
-rw-r--r--arch/arm/plat-s3c64xx/irq-pm.c111
-rw-r--r--arch/arm/plat-s3c64xx/irq.c9
-rw-r--r--arch/arm/plat-s3c64xx/pm.c175
-rw-r--r--arch/arm/plat-s3c64xx/s3c6400-clock.c106
-rw-r--r--arch/arm/plat-s3c64xx/setup-sdhci-gpio.c55
-rw-r--r--arch/arm/plat-s3c64xx/sleep.S144
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
42config S3C64XX_DMA
43 bool "S3C64XX DMA"
44 select S3C_DMA
45
41# platform specific device setup 46# platform specific device setup
42 47
43config S3C64XX_SETUP_I2C0 48config 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
67config S3C64XX_SETUP_SDHCI_GPIO
68 bool
69 help
70 Common setup code for S3C64XX SDHCI GPIO configurations
71
62endif 72endif
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
24obj-$(CONFIG_CPU_S3C6400_INIT) += s3c6400-init.o 24obj-$(CONFIG_CPU_S3C6400_INIT) += s3c6400-init.o
25obj-$(CONFIG_CPU_S3C6400_CLOCK) += s3c6400-clock.o 25obj-$(CONFIG_CPU_S3C6400_CLOCK) += s3c6400-clock.o
26 26
27# PM support
28
29obj-$(CONFIG_PM) += pm.o
30obj-$(CONFIG_PM) += sleep.o
31obj-$(CONFIG_PM) += irq-pm.o
32
33# DMA support
34
35obj-$(CONFIG_S3C64XX_DMA) += dma.o
36
27# Device setup 37# Device setup
28 38
29obj-$(CONFIG_S3C64XX_SETUP_I2C0) += setup-i2c0.o 39obj-$(CONFIG_S3C64XX_SETUP_I2C0) += setup-i2c0.o
30obj-$(CONFIG_S3C64XX_SETUP_I2C1) += setup-i2c1.o 40obj-$(CONFIG_S3C64XX_SETUP_I2C1) += setup-i2c1.o
31obj-$(CONFIG_S3C64XX_SETUP_FB_24BPP) += setup-fb-24bpp.o 41obj-$(CONFIG_S3C64XX_SETUP_FB_24BPP) += setup-fb-24bpp.o
42obj-$(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
30struct clk clk_h2 = {
31 .name = "hclk2",
32 .id = -1,
33 .rate = 0,
34};
35
30struct clk clk_27m = { 36struct 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
251void __init s3c64xx_register_clocks(void) 270void __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
114struct sysdev_class s3c64xx_sysclass = {
115 .name = "s3c64xx-core",
116};
117
118static struct sys_device s3c64xx_sysdev = {
119 .cls = &s3c64xx_sysclass,
120};
121
122
107/* read cpu identification code */ 123/* read cpu identification code */
108 124
109void __init s3c64xx_init_io(struct map_desc *mach_desc, int size) 125void __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
145static __init int s3c64xx_sysdev_init(void)
146{
147 sysdev_class_register(&s3c64xx_sysclass);
148 return sysdev_register(&s3c64xx_sysdev);
149}
150
151core_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
37struct 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 */
46static struct dma_pool *dma_pool;
47
48/* Debug configuration and code */
49
50static unsigned char debug_show_buffs = 0;
51
52static 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
64static 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
71static 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
93static 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
111found:
112 s3c_dma_chan_map[channel] = chan;
113 return chan;
114}
115
116int 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}
140EXPORT_SYMBOL(s3c2410_dma_config);
141
142static 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
184static 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
199static 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
224static 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
259static 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
267static 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
273static 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
301int 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}
329EXPORT_SYMBOL(s3c2410_dma_ctrl);
330
331/* s3c2410_dma_enque
332 *
333 */
334
335int 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
406err_buff:
407 kfree(buff);
408 return ret;
409}
410
411EXPORT_SYMBOL(s3c2410_dma_enqueue);
412
413
414int 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}
459EXPORT_SYMBOL(s3c2410_dma_devconfig);
460
461
462int 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}
479EXPORT_SYMBOL(s3c2410_dma_getposition);
480
481/* s3c2410_request_dma
482 *
483 * get control of an dma channel
484*/
485
486int 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
519EXPORT_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
532int 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
561EXPORT_SYMBOL(s3c2410_dma_free);
562
563
564static 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
575static void s3c64xx_dma_errirq(struct s3c64xx_dmac *dmac, int offs)
576{
577 printk(KERN_DEBUG "%s: offs %d\n", __func__, offs);
578}
579
580static 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
605static struct sysdev_class dma_sysclass = {
606 .name = "s3c64xx-dma",
607};
608
609static 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
682err_clk:
683 clk_disable(dmac->clk);
684 clk_put(dmac->clk);
685err_map:
686 iounmap(regs);
687err_dev:
688 sysdev_unregister(&dmac->sysdev);
689err_alloc:
690 kfree(dmac);
691 return err;
692}
693
694static 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
722arch_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
390static __init void s3c64xx_gpiolib_add_4bit2(struct s3c_gpio_chip *chip) 391static __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
398static __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
396static __init void s3c64xx_gpiolib_add(struct s3c_gpio_chip *chips, 403static __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
17struct 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 */
25struct 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
33struct s3c64xx_dmac;
34
35struct 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
17static 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
36static 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
44static inline void s3c_pm_arch_stop_clocks(void)
45{
46}
47
48static 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
58static 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
17extern void s3c6400_common_init_uarts(struct s3c2410_uartcfg *cfg, int no); 17extern void s3c6400_common_init_uarts(struct s3c2410_uartcfg *cfg, int no);
18extern void s3c6400_register_clocks(void); 18extern void s3c6400_register_clocks(unsigned armclk_divlimit);
19extern void s3c6400_setup_clocks(void); 19extern void s3c6400_setup_clocks(void);
20 20
21#ifdef CONFIG_CPU_S3C6400 21#ifdef CONFIG_CPU_S3C6400
22 22
23extern int s3c6400_init(void); 23extern int s3c6400_init(void);
24extern void s3c6400_init_irq(void);
24extern void s3c6400_map_io(void); 25extern void s3c6400_map_io(void);
25extern void s3c6400_init_clocks(int xtal); 26extern 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
37static 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
49static struct irq_grp_save {
50 u32 fltcon;
51 u32 con;
52 u32 mask;
53} eint_grp_save[5];
54
55static u32 irq_uart_mask[CONFIG_SERIAL_SAMSUNG_UARTS];
56
57static 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
78static 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
100static struct sysdev_driver s3c64xx_irq_driver = {
101 .suspend = s3c64xx_irq_pm_suspend,
102 .resume = s3c64xx_irq_pm_resume,
103};
104
105static int __init s3c64xx_irq_pm_init(void)
106{
107 return sysdev_driver_register(&s3c64xx_sysclass, &s3c64xx_irq_driver);
108}
109
110arch_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
142static void s3c_irq_uart_mask(unsigned int irq) 141static 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
32void 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
54static 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
80static 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
94void s3c_pm_configure_extint(void)
95{
96 __raw_writel(s3c_irqwake_eintmask, S3C64XX_EINT_MASK);
97}
98
99void 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
109void 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
122static 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
158static 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
167static 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
175arch_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
136static unsigned int armclk_mask;
137
138static 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
149static 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
165static 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
186static 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
136static unsigned long s3c64xx_clk_doutmpll_get_rate(struct clk *clk) 195static 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
582static struct clk *clkset_camif_list[] = {
583 &clk_h2,
584};
585
586static struct clk_sources clkset_camif = {
587 .sources = clkset_camif_list,
588 .nr_sources = ARRAY_SIZE(clkset_camif_list),
589};
590
591static 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
525static struct clksrc_clk *init_parents[] = { 611static 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
541static void __init_or_cpufreq s3c6400_set_clksrc(struct clksrc_clk *clk) 628static 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
640void __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 */
742void __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
23void 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
40void 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
38ENTRY(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.
61resume_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
73s3c_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
90ENTRY(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