diff options
Diffstat (limited to 'arch/arm/mach-s3c64xx')
-rw-r--r-- | arch/arm/mach-s3c64xx/Kconfig | 7 | ||||
-rw-r--r-- | arch/arm/mach-s3c64xx/Makefile | 2 | ||||
-rw-r--r-- | arch/arm/mach-s3c64xx/common.h | 5 | ||||
-rw-r--r-- | arch/arm/mach-s3c64xx/dma.c | 762 | ||||
-rw-r--r-- | arch/arm/mach-s3c64xx/include/mach/dma.h | 144 | ||||
-rw-r--r-- | arch/arm/mach-s3c64xx/mach-s3c64xx-dt.c | 11 | ||||
-rw-r--r-- | arch/arm/mach-s3c64xx/pl080.c | 244 |
7 files changed, 297 insertions, 878 deletions
diff --git a/arch/arm/mach-s3c64xx/Kconfig b/arch/arm/mach-s3c64xx/Kconfig index 2cb8dc55b50e..7094bccbae91 100644 --- a/arch/arm/mach-s3c64xx/Kconfig +++ b/arch/arm/mach-s3c64xx/Kconfig | |||
@@ -17,9 +17,10 @@ config CPU_S3C6410 | |||
17 | help | 17 | help |
18 | Enable S3C6410 CPU support | 18 | Enable S3C6410 CPU support |
19 | 19 | ||
20 | config S3C64XX_DMA | 20 | config S3C64XX_PL080 |
21 | bool "S3C64XX DMA" | 21 | bool "S3C64XX DMA using generic PL08x driver" |
22 | select S3C_DMA | 22 | select AMBA_PL08X |
23 | select SAMSUNG_DMADEV | ||
23 | 24 | ||
24 | config S3C64XX_SETUP_SDHCI | 25 | config S3C64XX_SETUP_SDHCI |
25 | bool | 26 | bool |
diff --git a/arch/arm/mach-s3c64xx/Makefile b/arch/arm/mach-s3c64xx/Makefile index 6faedcffce04..58069a702a43 100644 --- a/arch/arm/mach-s3c64xx/Makefile +++ b/arch/arm/mach-s3c64xx/Makefile | |||
@@ -26,7 +26,7 @@ obj-$(CONFIG_CPU_IDLE) += cpuidle.o | |||
26 | 26 | ||
27 | # DMA support | 27 | # DMA support |
28 | 28 | ||
29 | obj-$(CONFIG_S3C64XX_DMA) += dma.o | 29 | obj-$(CONFIG_S3C64XX_PL080) += pl080.o |
30 | 30 | ||
31 | # Device support | 31 | # Device support |
32 | 32 | ||
diff --git a/arch/arm/mach-s3c64xx/common.h b/arch/arm/mach-s3c64xx/common.h index bd3bd562011e..7043e7a3a67e 100644 --- a/arch/arm/mach-s3c64xx/common.h +++ b/arch/arm/mach-s3c64xx/common.h | |||
@@ -58,4 +58,9 @@ int __init s3c64xx_pm_late_initcall(void); | |||
58 | static inline int s3c64xx_pm_late_initcall(void) { return 0; } | 58 | static inline int s3c64xx_pm_late_initcall(void) { return 0; } |
59 | #endif | 59 | #endif |
60 | 60 | ||
61 | #ifdef CONFIG_S3C64XX_PL080 | ||
62 | extern struct pl08x_platform_data s3c64xx_dma0_plat_data; | ||
63 | extern struct pl08x_platform_data s3c64xx_dma1_plat_data; | ||
64 | #endif | ||
65 | |||
61 | #endif /* __ARCH_ARM_MACH_S3C64XX_COMMON_H */ | 66 | #endif /* __ARCH_ARM_MACH_S3C64XX_COMMON_H */ |
diff --git a/arch/arm/mach-s3c64xx/dma.c b/arch/arm/mach-s3c64xx/dma.c deleted file mode 100644 index 7e22c2113816..000000000000 --- a/arch/arm/mach-s3c64xx/dma.c +++ /dev/null | |||
@@ -1,762 +0,0 @@ | |||
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 | /* | ||
16 | * NOTE: Code in this file is not used when booting with Device Tree support. | ||
17 | */ | ||
18 | |||
19 | #include <linux/kernel.h> | ||
20 | #include <linux/module.h> | ||
21 | #include <linux/interrupt.h> | ||
22 | #include <linux/dmapool.h> | ||
23 | #include <linux/device.h> | ||
24 | #include <linux/errno.h> | ||
25 | #include <linux/slab.h> | ||
26 | #include <linux/delay.h> | ||
27 | #include <linux/clk.h> | ||
28 | #include <linux/err.h> | ||
29 | #include <linux/io.h> | ||
30 | #include <linux/amba/pl080.h> | ||
31 | #include <linux/of.h> | ||
32 | |||
33 | #include <mach/dma.h> | ||
34 | #include <mach/map.h> | ||
35 | #include <mach/irqs.h> | ||
36 | |||
37 | #include "regs-sys.h" | ||
38 | |||
39 | /* dma channel state information */ | ||
40 | |||
41 | struct s3c64xx_dmac { | ||
42 | struct device dev; | ||
43 | struct clk *clk; | ||
44 | void __iomem *regs; | ||
45 | struct s3c2410_dma_chan *channels; | ||
46 | enum dma_ch chanbase; | ||
47 | }; | ||
48 | |||
49 | /* pool to provide LLI buffers */ | ||
50 | static struct dma_pool *dma_pool; | ||
51 | |||
52 | /* Debug configuration and code */ | ||
53 | |||
54 | static unsigned char debug_show_buffs = 0; | ||
55 | |||
56 | static void dbg_showchan(struct s3c2410_dma_chan *chan) | ||
57 | { | ||
58 | pr_debug("DMA%d: %08x->%08x L %08x C %08x,%08x S %08x\n", | ||
59 | chan->number, | ||
60 | readl(chan->regs + PL080_CH_SRC_ADDR), | ||
61 | readl(chan->regs + PL080_CH_DST_ADDR), | ||
62 | readl(chan->regs + PL080_CH_LLI), | ||
63 | readl(chan->regs + PL080_CH_CONTROL), | ||
64 | readl(chan->regs + PL080S_CH_CONTROL2), | ||
65 | readl(chan->regs + PL080S_CH_CONFIG)); | ||
66 | } | ||
67 | |||
68 | static void show_lli(struct pl080s_lli *lli) | ||
69 | { | ||
70 | pr_debug("LLI[%p] %08x->%08x, NL %08x C %08x,%08x\n", | ||
71 | lli, lli->src_addr, lli->dst_addr, lli->next_lli, | ||
72 | lli->control0, lli->control1); | ||
73 | } | ||
74 | |||
75 | static void dbg_showbuffs(struct s3c2410_dma_chan *chan) | ||
76 | { | ||
77 | struct s3c64xx_dma_buff *ptr; | ||
78 | struct s3c64xx_dma_buff *end; | ||
79 | |||
80 | pr_debug("DMA%d: buffs next %p, curr %p, end %p\n", | ||
81 | chan->number, chan->next, chan->curr, chan->end); | ||
82 | |||
83 | ptr = chan->next; | ||
84 | end = chan->end; | ||
85 | |||
86 | if (debug_show_buffs) { | ||
87 | for (; ptr != NULL; ptr = ptr->next) { | ||
88 | pr_debug("DMA%d: %08x ", | ||
89 | chan->number, ptr->lli_dma); | ||
90 | show_lli(ptr->lli); | ||
91 | } | ||
92 | } | ||
93 | } | ||
94 | |||
95 | /* End of Debug */ | ||
96 | |||
97 | static struct s3c2410_dma_chan *s3c64xx_dma_map_channel(unsigned int channel) | ||
98 | { | ||
99 | struct s3c2410_dma_chan *chan; | ||
100 | unsigned int start, offs; | ||
101 | |||
102 | start = 0; | ||
103 | |||
104 | if (channel >= DMACH_PCM1_TX) | ||
105 | start = 8; | ||
106 | |||
107 | for (offs = 0; offs < 8; offs++) { | ||
108 | chan = &s3c2410_chans[start + offs]; | ||
109 | if (!chan->in_use) | ||
110 | goto found; | ||
111 | } | ||
112 | |||
113 | return NULL; | ||
114 | |||
115 | found: | ||
116 | s3c_dma_chan_map[channel] = chan; | ||
117 | return chan; | ||
118 | } | ||
119 | |||
120 | int s3c2410_dma_config(enum dma_ch channel, int xferunit) | ||
121 | { | ||
122 | struct s3c2410_dma_chan *chan = s3c_dma_lookup_channel(channel); | ||
123 | |||
124 | if (chan == NULL) | ||
125 | return -EINVAL; | ||
126 | |||
127 | switch (xferunit) { | ||
128 | case 1: | ||
129 | chan->hw_width = 0; | ||
130 | break; | ||
131 | case 2: | ||
132 | chan->hw_width = 1; | ||
133 | break; | ||
134 | case 4: | ||
135 | chan->hw_width = 2; | ||
136 | break; | ||
137 | default: | ||
138 | printk(KERN_ERR "%s: illegal width %d\n", __func__, xferunit); | ||
139 | return -EINVAL; | ||
140 | } | ||
141 | |||
142 | return 0; | ||
143 | } | ||
144 | EXPORT_SYMBOL(s3c2410_dma_config); | ||
145 | |||
146 | static void s3c64xx_dma_fill_lli(struct s3c2410_dma_chan *chan, | ||
147 | struct pl080s_lli *lli, | ||
148 | dma_addr_t data, int size) | ||
149 | { | ||
150 | dma_addr_t src, dst; | ||
151 | u32 control0, control1; | ||
152 | |||
153 | switch (chan->source) { | ||
154 | case DMA_FROM_DEVICE: | ||
155 | src = chan->dev_addr; | ||
156 | dst = data; | ||
157 | control0 = PL080_CONTROL_SRC_AHB2; | ||
158 | control0 |= PL080_CONTROL_DST_INCR; | ||
159 | break; | ||
160 | |||
161 | case DMA_TO_DEVICE: | ||
162 | src = data; | ||
163 | dst = chan->dev_addr; | ||
164 | control0 = PL080_CONTROL_DST_AHB2; | ||
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 | control0 |= (u32)chan->hw_width << PL080_CONTROL_DWIDTH_SHIFT; | ||
177 | control0 |= (u32)chan->hw_width << PL080_CONTROL_SWIDTH_SHIFT; | ||
178 | |||
179 | lli->src_addr = src; | ||
180 | lli->dst_addr = dst; | ||
181 | lli->next_lli = 0; | ||
182 | lli->control0 = control0; | ||
183 | lli->control1 = control1; | ||
184 | } | ||
185 | |||
186 | static void s3c64xx_lli_to_regs(struct s3c2410_dma_chan *chan, | ||
187 | struct pl080s_lli *lli) | ||
188 | { | ||
189 | void __iomem *regs = chan->regs; | ||
190 | |||
191 | pr_debug("%s: LLI %p => regs\n", __func__, lli); | ||
192 | show_lli(lli); | ||
193 | |||
194 | writel(lli->src_addr, regs + PL080_CH_SRC_ADDR); | ||
195 | writel(lli->dst_addr, regs + PL080_CH_DST_ADDR); | ||
196 | writel(lli->next_lli, regs + PL080_CH_LLI); | ||
197 | writel(lli->control0, regs + PL080_CH_CONTROL); | ||
198 | writel(lli->control1, regs + PL080S_CH_CONTROL2); | ||
199 | } | ||
200 | |||
201 | static int s3c64xx_dma_start(struct s3c2410_dma_chan *chan) | ||
202 | { | ||
203 | struct s3c64xx_dmac *dmac = chan->dmac; | ||
204 | u32 config; | ||
205 | u32 bit = chan->bit; | ||
206 | |||
207 | dbg_showchan(chan); | ||
208 | |||
209 | pr_debug("%s: clearing interrupts\n", __func__); | ||
210 | |||
211 | /* clear interrupts */ | ||
212 | writel(bit, dmac->regs + PL080_TC_CLEAR); | ||
213 | writel(bit, dmac->regs + PL080_ERR_CLEAR); | ||
214 | |||
215 | pr_debug("%s: starting channel\n", __func__); | ||
216 | |||
217 | config = readl(chan->regs + PL080S_CH_CONFIG); | ||
218 | config |= PL080_CONFIG_ENABLE; | ||
219 | config &= ~PL080_CONFIG_HALT; | ||
220 | |||
221 | pr_debug("%s: writing config %08x\n", __func__, config); | ||
222 | writel(config, chan->regs + PL080S_CH_CONFIG); | ||
223 | |||
224 | return 0; | ||
225 | } | ||
226 | |||
227 | static int s3c64xx_dma_stop(struct s3c2410_dma_chan *chan) | ||
228 | { | ||
229 | u32 config; | ||
230 | int timeout; | ||
231 | |||
232 | pr_debug("%s: stopping channel\n", __func__); | ||
233 | |||
234 | dbg_showchan(chan); | ||
235 | |||
236 | config = readl(chan->regs + PL080S_CH_CONFIG); | ||
237 | config |= PL080_CONFIG_HALT; | ||
238 | writel(config, chan->regs + PL080S_CH_CONFIG); | ||
239 | |||
240 | timeout = 1000; | ||
241 | do { | ||
242 | config = readl(chan->regs + PL080S_CH_CONFIG); | ||
243 | pr_debug("%s: %d - config %08x\n", __func__, timeout, config); | ||
244 | if (config & PL080_CONFIG_ACTIVE) | ||
245 | udelay(10); | ||
246 | else | ||
247 | break; | ||
248 | } while (--timeout > 0); | ||
249 | |||
250 | if (config & PL080_CONFIG_ACTIVE) { | ||
251 | printk(KERN_ERR "%s: channel still active\n", __func__); | ||
252 | return -EFAULT; | ||
253 | } | ||
254 | |||
255 | config = readl(chan->regs + PL080S_CH_CONFIG); | ||
256 | config &= ~PL080_CONFIG_ENABLE; | ||
257 | writel(config, chan->regs + PL080S_CH_CONFIG); | ||
258 | |||
259 | return 0; | ||
260 | } | ||
261 | |||
262 | static inline void s3c64xx_dma_bufffdone(struct s3c2410_dma_chan *chan, | ||
263 | struct s3c64xx_dma_buff *buf, | ||
264 | enum s3c2410_dma_buffresult result) | ||
265 | { | ||
266 | if (chan->callback_fn != NULL) | ||
267 | (chan->callback_fn)(chan, buf->pw, 0, result); | ||
268 | } | ||
269 | |||
270 | static void s3c64xx_dma_freebuff(struct s3c64xx_dma_buff *buff) | ||
271 | { | ||
272 | dma_pool_free(dma_pool, buff->lli, buff->lli_dma); | ||
273 | kfree(buff); | ||
274 | } | ||
275 | |||
276 | static int s3c64xx_dma_flush(struct s3c2410_dma_chan *chan) | ||
277 | { | ||
278 | struct s3c64xx_dma_buff *buff, *next; | ||
279 | u32 config; | ||
280 | |||
281 | dbg_showchan(chan); | ||
282 | |||
283 | pr_debug("%s: flushing channel\n", __func__); | ||
284 | |||
285 | config = readl(chan->regs + PL080S_CH_CONFIG); | ||
286 | config &= ~PL080_CONFIG_ENABLE; | ||
287 | writel(config, chan->regs + PL080S_CH_CONFIG); | ||
288 | |||
289 | /* dump all the buffers associated with this channel */ | ||
290 | |||
291 | for (buff = chan->curr; buff != NULL; buff = next) { | ||
292 | next = buff->next; | ||
293 | pr_debug("%s: buff %p (next %p)\n", __func__, buff, buff->next); | ||
294 | |||
295 | s3c64xx_dma_bufffdone(chan, buff, S3C2410_RES_ABORT); | ||
296 | s3c64xx_dma_freebuff(buff); | ||
297 | } | ||
298 | |||
299 | chan->curr = chan->next = chan->end = NULL; | ||
300 | |||
301 | return 0; | ||
302 | } | ||
303 | |||
304 | int s3c2410_dma_ctrl(enum dma_ch channel, enum s3c2410_chan_op op) | ||
305 | { | ||
306 | struct s3c2410_dma_chan *chan = s3c_dma_lookup_channel(channel); | ||
307 | |||
308 | WARN_ON(!chan); | ||
309 | if (!chan) | ||
310 | return -EINVAL; | ||
311 | |||
312 | switch (op) { | ||
313 | case S3C2410_DMAOP_START: | ||
314 | return s3c64xx_dma_start(chan); | ||
315 | |||
316 | case S3C2410_DMAOP_STOP: | ||
317 | return s3c64xx_dma_stop(chan); | ||
318 | |||
319 | case S3C2410_DMAOP_FLUSH: | ||
320 | return s3c64xx_dma_flush(chan); | ||
321 | |||
322 | /* believe PAUSE/RESUME are no-ops */ | ||
323 | case S3C2410_DMAOP_PAUSE: | ||
324 | case S3C2410_DMAOP_RESUME: | ||
325 | case S3C2410_DMAOP_STARTED: | ||
326 | case S3C2410_DMAOP_TIMEOUT: | ||
327 | return 0; | ||
328 | } | ||
329 | |||
330 | return -ENOENT; | ||
331 | } | ||
332 | EXPORT_SYMBOL(s3c2410_dma_ctrl); | ||
333 | |||
334 | /* s3c2410_dma_enque | ||
335 | * | ||
336 | */ | ||
337 | |||
338 | int s3c2410_dma_enqueue(enum dma_ch channel, void *id, | ||
339 | dma_addr_t data, int size) | ||
340 | { | ||
341 | struct s3c2410_dma_chan *chan = s3c_dma_lookup_channel(channel); | ||
342 | struct s3c64xx_dma_buff *next; | ||
343 | struct s3c64xx_dma_buff *buff; | ||
344 | struct pl080s_lli *lli; | ||
345 | unsigned long flags; | ||
346 | int ret; | ||
347 | |||
348 | WARN_ON(!chan); | ||
349 | if (!chan) | ||
350 | return -EINVAL; | ||
351 | |||
352 | buff = kzalloc(sizeof(struct s3c64xx_dma_buff), GFP_ATOMIC); | ||
353 | if (!buff) { | ||
354 | printk(KERN_ERR "%s: no memory for buffer\n", __func__); | ||
355 | return -ENOMEM; | ||
356 | } | ||
357 | |||
358 | lli = dma_pool_alloc(dma_pool, GFP_ATOMIC, &buff->lli_dma); | ||
359 | if (!lli) { | ||
360 | printk(KERN_ERR "%s: no memory for lli\n", __func__); | ||
361 | ret = -ENOMEM; | ||
362 | goto err_buff; | ||
363 | } | ||
364 | |||
365 | pr_debug("%s: buff %p, dp %08x lli (%p, %08x) %d\n", | ||
366 | __func__, buff, data, lli, (u32)buff->lli_dma, size); | ||
367 | |||
368 | buff->lli = lli; | ||
369 | buff->pw = id; | ||
370 | |||
371 | s3c64xx_dma_fill_lli(chan, lli, data, size); | ||
372 | |||
373 | local_irq_save(flags); | ||
374 | |||
375 | if ((next = chan->next) != NULL) { | ||
376 | struct s3c64xx_dma_buff *end = chan->end; | ||
377 | struct pl080s_lli *endlli = end->lli; | ||
378 | |||
379 | pr_debug("enquing onto channel\n"); | ||
380 | |||
381 | end->next = buff; | ||
382 | endlli->next_lli = buff->lli_dma; | ||
383 | |||
384 | if (chan->flags & S3C2410_DMAF_CIRCULAR) { | ||
385 | struct s3c64xx_dma_buff *curr = chan->curr; | ||
386 | lli->next_lli = curr->lli_dma; | ||
387 | } | ||
388 | |||
389 | if (next == chan->curr) { | ||
390 | writel(buff->lli_dma, chan->regs + PL080_CH_LLI); | ||
391 | chan->next = buff; | ||
392 | } | ||
393 | |||
394 | show_lli(endlli); | ||
395 | chan->end = buff; | ||
396 | } else { | ||
397 | pr_debug("enquing onto empty channel\n"); | ||
398 | |||
399 | chan->curr = buff; | ||
400 | chan->next = buff; | ||
401 | chan->end = buff; | ||
402 | |||
403 | s3c64xx_lli_to_regs(chan, lli); | ||
404 | } | ||
405 | |||
406 | local_irq_restore(flags); | ||
407 | |||
408 | show_lli(lli); | ||
409 | |||
410 | dbg_showchan(chan); | ||
411 | dbg_showbuffs(chan); | ||
412 | return 0; | ||
413 | |||
414 | err_buff: | ||
415 | kfree(buff); | ||
416 | return ret; | ||
417 | } | ||
418 | |||
419 | EXPORT_SYMBOL(s3c2410_dma_enqueue); | ||
420 | |||
421 | |||
422 | int s3c2410_dma_devconfig(enum dma_ch channel, | ||
423 | enum dma_data_direction source, | ||
424 | unsigned long devaddr) | ||
425 | { | ||
426 | struct s3c2410_dma_chan *chan = s3c_dma_lookup_channel(channel); | ||
427 | u32 peripheral; | ||
428 | u32 config = 0; | ||
429 | |||
430 | pr_debug("%s: channel %d, source %d, dev %08lx, chan %p\n", | ||
431 | __func__, channel, source, devaddr, chan); | ||
432 | |||
433 | WARN_ON(!chan); | ||
434 | if (!chan) | ||
435 | return -EINVAL; | ||
436 | |||
437 | peripheral = (chan->peripheral & 0xf); | ||
438 | chan->source = source; | ||
439 | chan->dev_addr = devaddr; | ||
440 | |||
441 | pr_debug("%s: peripheral %d\n", __func__, peripheral); | ||
442 | |||
443 | switch (source) { | ||
444 | case DMA_FROM_DEVICE: | ||
445 | config = 2 << PL080_CONFIG_FLOW_CONTROL_SHIFT; | ||
446 | config |= peripheral << PL080_CONFIG_SRC_SEL_SHIFT; | ||
447 | break; | ||
448 | case DMA_TO_DEVICE: | ||
449 | config = 1 << PL080_CONFIG_FLOW_CONTROL_SHIFT; | ||
450 | config |= peripheral << PL080_CONFIG_DST_SEL_SHIFT; | ||
451 | break; | ||
452 | default: | ||
453 | printk(KERN_ERR "%s: bad source\n", __func__); | ||
454 | return -EINVAL; | ||
455 | } | ||
456 | |||
457 | /* allow TC and ERR interrupts */ | ||
458 | config |= PL080_CONFIG_TC_IRQ_MASK; | ||
459 | config |= PL080_CONFIG_ERR_IRQ_MASK; | ||
460 | |||
461 | pr_debug("%s: config %08x\n", __func__, config); | ||
462 | |||
463 | writel(config, chan->regs + PL080S_CH_CONFIG); | ||
464 | |||
465 | return 0; | ||
466 | } | ||
467 | EXPORT_SYMBOL(s3c2410_dma_devconfig); | ||
468 | |||
469 | |||
470 | int s3c2410_dma_getposition(enum dma_ch channel, | ||
471 | dma_addr_t *src, dma_addr_t *dst) | ||
472 | { | ||
473 | struct s3c2410_dma_chan *chan = s3c_dma_lookup_channel(channel); | ||
474 | |||
475 | WARN_ON(!chan); | ||
476 | if (!chan) | ||
477 | return -EINVAL; | ||
478 | |||
479 | if (src != NULL) | ||
480 | *src = readl(chan->regs + PL080_CH_SRC_ADDR); | ||
481 | |||
482 | if (dst != NULL) | ||
483 | *dst = readl(chan->regs + PL080_CH_DST_ADDR); | ||
484 | |||
485 | return 0; | ||
486 | } | ||
487 | EXPORT_SYMBOL(s3c2410_dma_getposition); | ||
488 | |||
489 | /* s3c2410_request_dma | ||
490 | * | ||
491 | * get control of an dma channel | ||
492 | */ | ||
493 | |||
494 | int s3c2410_dma_request(enum dma_ch channel, | ||
495 | struct s3c2410_dma_client *client, | ||
496 | void *dev) | ||
497 | { | ||
498 | struct s3c2410_dma_chan *chan; | ||
499 | unsigned long flags; | ||
500 | |||
501 | pr_debug("dma%d: s3c2410_request_dma: client=%s, dev=%p\n", | ||
502 | channel, client->name, dev); | ||
503 | |||
504 | local_irq_save(flags); | ||
505 | |||
506 | chan = s3c64xx_dma_map_channel(channel); | ||
507 | if (chan == NULL) { | ||
508 | local_irq_restore(flags); | ||
509 | return -EBUSY; | ||
510 | } | ||
511 | |||
512 | dbg_showchan(chan); | ||
513 | |||
514 | chan->client = client; | ||
515 | chan->in_use = 1; | ||
516 | chan->peripheral = channel; | ||
517 | chan->flags = 0; | ||
518 | |||
519 | local_irq_restore(flags); | ||
520 | |||
521 | /* need to setup */ | ||
522 | |||
523 | pr_debug("%s: channel initialised, %p\n", __func__, chan); | ||
524 | |||
525 | return chan->number | DMACH_LOW_LEVEL; | ||
526 | } | ||
527 | |||
528 | EXPORT_SYMBOL(s3c2410_dma_request); | ||
529 | |||
530 | /* s3c2410_dma_free | ||
531 | * | ||
532 | * release the given channel back to the system, will stop and flush | ||
533 | * any outstanding transfers, and ensure the channel is ready for the | ||
534 | * next claimant. | ||
535 | * | ||
536 | * Note, although a warning is currently printed if the freeing client | ||
537 | * info is not the same as the registrant's client info, the free is still | ||
538 | * allowed to go through. | ||
539 | */ | ||
540 | |||
541 | int s3c2410_dma_free(enum dma_ch channel, struct s3c2410_dma_client *client) | ||
542 | { | ||
543 | struct s3c2410_dma_chan *chan = s3c_dma_lookup_channel(channel); | ||
544 | unsigned long flags; | ||
545 | |||
546 | if (chan == NULL) | ||
547 | return -EINVAL; | ||
548 | |||
549 | local_irq_save(flags); | ||
550 | |||
551 | if (chan->client != client) { | ||
552 | printk(KERN_WARNING "dma%d: possible free from different client (channel %p, passed %p)\n", | ||
553 | channel, chan->client, client); | ||
554 | } | ||
555 | |||
556 | /* sort out stopping and freeing the channel */ | ||
557 | |||
558 | |||
559 | chan->client = NULL; | ||
560 | chan->in_use = 0; | ||
561 | |||
562 | if (!(channel & DMACH_LOW_LEVEL)) | ||
563 | s3c_dma_chan_map[channel] = NULL; | ||
564 | |||
565 | local_irq_restore(flags); | ||
566 | |||
567 | return 0; | ||
568 | } | ||
569 | |||
570 | EXPORT_SYMBOL(s3c2410_dma_free); | ||
571 | |||
572 | static irqreturn_t s3c64xx_dma_irq(int irq, void *pw) | ||
573 | { | ||
574 | struct s3c64xx_dmac *dmac = pw; | ||
575 | struct s3c2410_dma_chan *chan; | ||
576 | enum s3c2410_dma_buffresult res; | ||
577 | u32 tcstat, errstat; | ||
578 | u32 bit; | ||
579 | int offs; | ||
580 | |||
581 | tcstat = readl(dmac->regs + PL080_TC_STATUS); | ||
582 | errstat = readl(dmac->regs + PL080_ERR_STATUS); | ||
583 | |||
584 | for (offs = 0, bit = 1; offs < 8; offs++, bit <<= 1) { | ||
585 | struct s3c64xx_dma_buff *buff; | ||
586 | |||
587 | if (!(errstat & bit) && !(tcstat & bit)) | ||
588 | continue; | ||
589 | |||
590 | chan = dmac->channels + offs; | ||
591 | res = S3C2410_RES_ERR; | ||
592 | |||
593 | if (tcstat & bit) { | ||
594 | writel(bit, dmac->regs + PL080_TC_CLEAR); | ||
595 | res = S3C2410_RES_OK; | ||
596 | } | ||
597 | |||
598 | if (errstat & bit) | ||
599 | writel(bit, dmac->regs + PL080_ERR_CLEAR); | ||
600 | |||
601 | /* 'next' points to the buffer that is next to the | ||
602 | * currently active buffer. | ||
603 | * For CIRCULAR queues, 'next' will be same as 'curr' | ||
604 | * when 'end' is the active buffer. | ||
605 | */ | ||
606 | buff = chan->curr; | ||
607 | while (buff && buff != chan->next | ||
608 | && buff->next != chan->next) | ||
609 | buff = buff->next; | ||
610 | |||
611 | if (!buff) | ||
612 | BUG(); | ||
613 | |||
614 | if (buff == chan->next) | ||
615 | buff = chan->end; | ||
616 | |||
617 | s3c64xx_dma_bufffdone(chan, buff, res); | ||
618 | |||
619 | /* Free the node and update curr, if non-circular queue */ | ||
620 | if (!(chan->flags & S3C2410_DMAF_CIRCULAR)) { | ||
621 | chan->curr = buff->next; | ||
622 | s3c64xx_dma_freebuff(buff); | ||
623 | } | ||
624 | |||
625 | /* Update 'next' */ | ||
626 | buff = chan->next; | ||
627 | if (chan->next == chan->end) { | ||
628 | chan->next = chan->curr; | ||
629 | if (!(chan->flags & S3C2410_DMAF_CIRCULAR)) | ||
630 | chan->end = NULL; | ||
631 | } else { | ||
632 | chan->next = buff->next; | ||
633 | } | ||
634 | } | ||
635 | |||
636 | return IRQ_HANDLED; | ||
637 | } | ||
638 | |||
639 | static struct bus_type dma_subsys = { | ||
640 | .name = "s3c64xx-dma", | ||
641 | .dev_name = "s3c64xx-dma", | ||
642 | }; | ||
643 | |||
644 | static int s3c64xx_dma_init1(int chno, enum dma_ch chbase, | ||
645 | int irq, unsigned int base) | ||
646 | { | ||
647 | struct s3c2410_dma_chan *chptr = &s3c2410_chans[chno]; | ||
648 | struct s3c64xx_dmac *dmac; | ||
649 | char clkname[16]; | ||
650 | void __iomem *regs; | ||
651 | void __iomem *regptr; | ||
652 | int err, ch; | ||
653 | |||
654 | dmac = kzalloc(sizeof(struct s3c64xx_dmac), GFP_KERNEL); | ||
655 | if (!dmac) { | ||
656 | printk(KERN_ERR "%s: failed to alloc mem\n", __func__); | ||
657 | return -ENOMEM; | ||
658 | } | ||
659 | |||
660 | dmac->dev.id = chno / 8; | ||
661 | dmac->dev.bus = &dma_subsys; | ||
662 | |||
663 | err = device_register(&dmac->dev); | ||
664 | if (err) { | ||
665 | printk(KERN_ERR "%s: failed to register device\n", __func__); | ||
666 | goto err_alloc; | ||
667 | } | ||
668 | |||
669 | regs = ioremap(base, 0x200); | ||
670 | if (!regs) { | ||
671 | printk(KERN_ERR "%s: failed to ioremap()\n", __func__); | ||
672 | err = -ENXIO; | ||
673 | goto err_dev; | ||
674 | } | ||
675 | |||
676 | snprintf(clkname, sizeof(clkname), "dma%d", dmac->dev.id); | ||
677 | |||
678 | dmac->clk = clk_get(NULL, clkname); | ||
679 | if (IS_ERR(dmac->clk)) { | ||
680 | printk(KERN_ERR "%s: failed to get clock %s\n", __func__, clkname); | ||
681 | err = PTR_ERR(dmac->clk); | ||
682 | goto err_map; | ||
683 | } | ||
684 | |||
685 | clk_prepare_enable(dmac->clk); | ||
686 | |||
687 | dmac->regs = regs; | ||
688 | dmac->chanbase = chbase; | ||
689 | dmac->channels = chptr; | ||
690 | |||
691 | err = request_irq(irq, s3c64xx_dma_irq, 0, "DMA", dmac); | ||
692 | if (err < 0) { | ||
693 | printk(KERN_ERR "%s: failed to get irq\n", __func__); | ||
694 | goto err_clk; | ||
695 | } | ||
696 | |||
697 | regptr = regs + PL080_Cx_BASE(0); | ||
698 | |||
699 | for (ch = 0; ch < 8; ch++, chptr++) { | ||
700 | pr_debug("%s: registering DMA %d (%p)\n", | ||
701 | __func__, chno + ch, regptr); | ||
702 | |||
703 | chptr->bit = 1 << ch; | ||
704 | chptr->number = chno + ch; | ||
705 | chptr->dmac = dmac; | ||
706 | chptr->regs = regptr; | ||
707 | regptr += PL080_Cx_STRIDE; | ||
708 | } | ||
709 | |||
710 | /* for the moment, permanently enable the controller */ | ||
711 | writel(PL080_CONFIG_ENABLE, regs + PL080_CONFIG); | ||
712 | |||
713 | printk(KERN_INFO "PL080: IRQ %d, at %p, channels %d..%d\n", | ||
714 | irq, regs, chno, chno+8); | ||
715 | |||
716 | return 0; | ||
717 | |||
718 | err_clk: | ||
719 | clk_disable_unprepare(dmac->clk); | ||
720 | clk_put(dmac->clk); | ||
721 | err_map: | ||
722 | iounmap(regs); | ||
723 | err_dev: | ||
724 | device_unregister(&dmac->dev); | ||
725 | err_alloc: | ||
726 | kfree(dmac); | ||
727 | return err; | ||
728 | } | ||
729 | |||
730 | static int __init s3c64xx_dma_init(void) | ||
731 | { | ||
732 | int ret; | ||
733 | |||
734 | /* This driver is not supported when booting with device tree. */ | ||
735 | if (of_have_populated_dt()) | ||
736 | return -ENODEV; | ||
737 | |||
738 | printk(KERN_INFO "%s: Registering DMA channels\n", __func__); | ||
739 | |||
740 | dma_pool = dma_pool_create("DMA-LLI", NULL, sizeof(struct pl080s_lli), 16, 0); | ||
741 | if (!dma_pool) { | ||
742 | printk(KERN_ERR "%s: failed to create pool\n", __func__); | ||
743 | return -ENOMEM; | ||
744 | } | ||
745 | |||
746 | ret = subsys_system_register(&dma_subsys, NULL); | ||
747 | if (ret) { | ||
748 | printk(KERN_ERR "%s: failed to create subsys\n", __func__); | ||
749 | return -ENOMEM; | ||
750 | } | ||
751 | |||
752 | /* Set all DMA configuration to be DMA, not SDMA */ | ||
753 | writel(0xffffff, S3C64XX_SDMA_SEL); | ||
754 | |||
755 | /* Register standard DMA controllers */ | ||
756 | s3c64xx_dma_init1(0, DMACH_UART0, IRQ_DMA0, 0x75000000); | ||
757 | s3c64xx_dma_init1(8, DMACH_PCM1_TX, IRQ_DMA1, 0x75100000); | ||
758 | |||
759 | return 0; | ||
760 | } | ||
761 | |||
762 | arch_initcall(s3c64xx_dma_init); | ||
diff --git a/arch/arm/mach-s3c64xx/include/mach/dma.h b/arch/arm/mach-s3c64xx/include/mach/dma.h index fe1a98cf0e4c..059b1fc85037 100644 --- a/arch/arm/mach-s3c64xx/include/mach/dma.h +++ b/arch/arm/mach-s3c64xx/include/mach/dma.h | |||
@@ -11,51 +11,48 @@ | |||
11 | #ifndef __ASM_ARCH_DMA_H | 11 | #ifndef __ASM_ARCH_DMA_H |
12 | #define __ASM_ARCH_DMA_H __FILE__ | 12 | #define __ASM_ARCH_DMA_H __FILE__ |
13 | 13 | ||
14 | #define S3C_DMA_CHANNELS (16) | 14 | #define S3C64XX_DMA_CHAN(name) ((unsigned long)(name)) |
15 | |||
16 | /* DMA0/SDMA0 */ | ||
17 | #define DMACH_UART0 S3C64XX_DMA_CHAN("uart0_tx") | ||
18 | #define DMACH_UART0_SRC2 S3C64XX_DMA_CHAN("uart0_rx") | ||
19 | #define DMACH_UART1 S3C64XX_DMA_CHAN("uart1_tx") | ||
20 | #define DMACH_UART1_SRC2 S3C64XX_DMA_CHAN("uart1_rx") | ||
21 | #define DMACH_UART2 S3C64XX_DMA_CHAN("uart2_tx") | ||
22 | #define DMACH_UART2_SRC2 S3C64XX_DMA_CHAN("uart2_rx") | ||
23 | #define DMACH_UART3 S3C64XX_DMA_CHAN("uart3_tx") | ||
24 | #define DMACH_UART3_SRC2 S3C64XX_DMA_CHAN("uart3_rx") | ||
25 | #define DMACH_PCM0_TX S3C64XX_DMA_CHAN("pcm0_tx") | ||
26 | #define DMACH_PCM0_RX S3C64XX_DMA_CHAN("pcm0_rx") | ||
27 | #define DMACH_I2S0_OUT S3C64XX_DMA_CHAN("i2s0_tx") | ||
28 | #define DMACH_I2S0_IN S3C64XX_DMA_CHAN("i2s0_rx") | ||
29 | #define DMACH_SPI0_TX S3C64XX_DMA_CHAN("spi0_tx") | ||
30 | #define DMACH_SPI0_RX S3C64XX_DMA_CHAN("spi0_rx") | ||
31 | #define DMACH_HSI_I2SV40_TX S3C64XX_DMA_CHAN("i2s2_tx") | ||
32 | #define DMACH_HSI_I2SV40_RX S3C64XX_DMA_CHAN("i2s2_rx") | ||
33 | |||
34 | /* DMA1/SDMA1 */ | ||
35 | #define DMACH_PCM1_TX S3C64XX_DMA_CHAN("pcm1_tx") | ||
36 | #define DMACH_PCM1_RX S3C64XX_DMA_CHAN("pcm1_rx") | ||
37 | #define DMACH_I2S1_OUT S3C64XX_DMA_CHAN("i2s1_tx") | ||
38 | #define DMACH_I2S1_IN S3C64XX_DMA_CHAN("i2s1_rx") | ||
39 | #define DMACH_SPI1_TX S3C64XX_DMA_CHAN("spi1_tx") | ||
40 | #define DMACH_SPI1_RX S3C64XX_DMA_CHAN("spi1_rx") | ||
41 | #define DMACH_AC97_PCMOUT S3C64XX_DMA_CHAN("ac97_out") | ||
42 | #define DMACH_AC97_PCMIN S3C64XX_DMA_CHAN("ac97_in") | ||
43 | #define DMACH_AC97_MICIN S3C64XX_DMA_CHAN("ac97_mic") | ||
44 | #define DMACH_PWM S3C64XX_DMA_CHAN("pwm") | ||
45 | #define DMACH_IRDA S3C64XX_DMA_CHAN("irda") | ||
46 | #define DMACH_EXTERNAL S3C64XX_DMA_CHAN("external") | ||
47 | #define DMACH_SECURITY_RX S3C64XX_DMA_CHAN("sec_rx") | ||
48 | #define DMACH_SECURITY_TX S3C64XX_DMA_CHAN("sec_tx") | ||
15 | 49 | ||
16 | /* see mach-s3c2410/dma.h for notes on dma channel numbers */ | ||
17 | |||
18 | /* Note, for the S3C64XX architecture we keep the DMACH_ | ||
19 | * defines in the order they are allocated to [S]DMA0/[S]DMA1 | ||
20 | * so that is easy to do DHACH_ -> DMA controller conversion | ||
21 | */ | ||
22 | enum dma_ch { | 50 | enum dma_ch { |
23 | /* DMA0/SDMA0 */ | 51 | DMACH_MAX = 32 |
24 | DMACH_UART0 = 0, | 52 | }; |
25 | DMACH_UART0_SRC2, | ||
26 | DMACH_UART1, | ||
27 | DMACH_UART1_SRC2, | ||
28 | DMACH_UART2, | ||
29 | DMACH_UART2_SRC2, | ||
30 | DMACH_UART3, | ||
31 | DMACH_UART3_SRC2, | ||
32 | DMACH_PCM0_TX, | ||
33 | DMACH_PCM0_RX, | ||
34 | DMACH_I2S0_OUT, | ||
35 | DMACH_I2S0_IN, | ||
36 | DMACH_SPI0_TX, | ||
37 | DMACH_SPI0_RX, | ||
38 | DMACH_HSI_I2SV40_TX, | ||
39 | DMACH_HSI_I2SV40_RX, | ||
40 | 53 | ||
41 | /* DMA1/SDMA1 */ | 54 | struct s3c2410_dma_client { |
42 | DMACH_PCM1_TX = 16, | 55 | char *name; |
43 | DMACH_PCM1_RX, | ||
44 | DMACH_I2S1_OUT, | ||
45 | DMACH_I2S1_IN, | ||
46 | DMACH_SPI1_TX, | ||
47 | DMACH_SPI1_RX, | ||
48 | DMACH_AC97_PCMOUT, | ||
49 | DMACH_AC97_PCMIN, | ||
50 | DMACH_AC97_MICIN, | ||
51 | DMACH_PWM, | ||
52 | DMACH_IRDA, | ||
53 | DMACH_EXTERNAL, | ||
54 | DMACH_RES1, | ||
55 | DMACH_RES2, | ||
56 | DMACH_SECURITY_RX, /* SDMA1 only */ | ||
57 | DMACH_SECURITY_TX, /* SDMA1 only */ | ||
58 | DMACH_MAX /* the end */ | ||
59 | }; | 56 | }; |
60 | 57 | ||
61 | static inline bool samsung_dma_has_circular(void) | 58 | static inline bool samsung_dma_has_circular(void) |
@@ -65,67 +62,10 @@ static inline bool samsung_dma_has_circular(void) | |||
65 | 62 | ||
66 | static inline bool samsung_dma_is_dmadev(void) | 63 | static inline bool samsung_dma_is_dmadev(void) |
67 | { | 64 | { |
68 | return false; | 65 | return true; |
69 | } | 66 | } |
70 | #define S3C2410_DMAF_CIRCULAR (1 << 0) | ||
71 | |||
72 | #include <plat/dma.h> | ||
73 | |||
74 | #define DMACH_LOW_LEVEL (1<<28) /* use this to specifiy hardware ch no */ | ||
75 | |||
76 | struct s3c64xx_dma_buff; | ||
77 | |||
78 | /** s3c64xx_dma_buff - S3C64XX DMA buffer descriptor | ||
79 | * @next: Pointer to next buffer in queue or ring. | ||
80 | * @pw: Client provided identifier | ||
81 | * @lli: Pointer to hardware descriptor this buffer is associated with. | ||
82 | * @lli_dma: Hardare address of the descriptor. | ||
83 | */ | ||
84 | struct s3c64xx_dma_buff { | ||
85 | struct s3c64xx_dma_buff *next; | ||
86 | |||
87 | void *pw; | ||
88 | struct pl080s_lli *lli; | ||
89 | dma_addr_t lli_dma; | ||
90 | }; | ||
91 | |||
92 | struct s3c64xx_dmac; | ||
93 | |||
94 | struct s3c2410_dma_chan { | ||
95 | unsigned char number; /* number of this dma channel */ | ||
96 | unsigned char in_use; /* channel allocated */ | ||
97 | unsigned char bit; /* bit for enable/disable/etc */ | ||
98 | unsigned char hw_width; | ||
99 | unsigned char peripheral; | ||
100 | |||
101 | unsigned int flags; | ||
102 | enum dma_data_direction source; | ||
103 | |||
104 | |||
105 | dma_addr_t dev_addr; | ||
106 | |||
107 | struct s3c2410_dma_client *client; | ||
108 | struct s3c64xx_dmac *dmac; /* pointer to controller */ | ||
109 | |||
110 | void __iomem *regs; | ||
111 | |||
112 | /* cdriver callbacks */ | ||
113 | s3c2410_dma_cbfn_t callback_fn; /* buffer done callback */ | ||
114 | s3c2410_dma_opfn_t op_fn; /* channel op callback */ | ||
115 | |||
116 | /* buffer list and information */ | ||
117 | struct s3c64xx_dma_buff *curr; /* current dma buffer */ | ||
118 | struct s3c64xx_dma_buff *next; /* next buffer to load */ | ||
119 | struct s3c64xx_dma_buff *end; /* end of queue */ | ||
120 | |||
121 | /* note, when channel is running in circular mode, curr is the | ||
122 | * first buffer enqueued, end is the last and curr is where the | ||
123 | * last buffer-done event is set-at. The buffers are not freed | ||
124 | * and the last buffer hardware descriptor points back to the | ||
125 | * first. | ||
126 | */ | ||
127 | }; | ||
128 | 67 | ||
129 | #include <plat/dma-core.h> | 68 | #include <linux/amba/pl08x.h> |
69 | #include <plat/dma-ops.h> | ||
130 | 70 | ||
131 | #endif /* __ASM_ARCH_IRQ_H */ | 71 | #endif /* __ASM_ARCH_IRQ_H */ |
diff --git a/arch/arm/mach-s3c64xx/mach-s3c64xx-dt.c b/arch/arm/mach-s3c64xx/mach-s3c64xx-dt.c index 7eb9a10fc1af..2fddf38192df 100644 --- a/arch/arm/mach-s3c64xx/mach-s3c64xx-dt.c +++ b/arch/arm/mach-s3c64xx/mach-s3c64xx-dt.c | |||
@@ -8,8 +8,6 @@ | |||
8 | * published by the Free Software Foundation. | 8 | * published by the Free Software Foundation. |
9 | */ | 9 | */ |
10 | 10 | ||
11 | #include <linux/clk-provider.h> | ||
12 | #include <linux/irqchip.h> | ||
13 | #include <linux/of_platform.h> | 11 | #include <linux/of_platform.h> |
14 | 12 | ||
15 | #include <asm/mach/arch.h> | 13 | #include <asm/mach/arch.h> |
@@ -48,15 +46,9 @@ static void __init s3c64xx_dt_map_io(void) | |||
48 | panic("SoC is not S3C64xx!"); | 46 | panic("SoC is not S3C64xx!"); |
49 | } | 47 | } |
50 | 48 | ||
51 | static void __init s3c64xx_dt_init_irq(void) | ||
52 | { | ||
53 | of_clk_init(NULL); | ||
54 | samsung_wdt_reset_of_init(); | ||
55 | irqchip_init(); | ||
56 | }; | ||
57 | |||
58 | static void __init s3c64xx_dt_init_machine(void) | 49 | static void __init s3c64xx_dt_init_machine(void) |
59 | { | 50 | { |
51 | samsung_wdt_reset_of_init(); | ||
60 | of_platform_populate(NULL, of_default_bus_match_table, NULL, NULL); | 52 | of_platform_populate(NULL, of_default_bus_match_table, NULL, NULL); |
61 | } | 53 | } |
62 | 54 | ||
@@ -79,7 +71,6 @@ DT_MACHINE_START(S3C6400_DT, "Samsung S3C64xx (Flattened Device Tree)") | |||
79 | /* Maintainer: Tomasz Figa <tomasz.figa@gmail.com> */ | 71 | /* Maintainer: Tomasz Figa <tomasz.figa@gmail.com> */ |
80 | .dt_compat = s3c64xx_dt_compat, | 72 | .dt_compat = s3c64xx_dt_compat, |
81 | .map_io = s3c64xx_dt_map_io, | 73 | .map_io = s3c64xx_dt_map_io, |
82 | .init_irq = s3c64xx_dt_init_irq, | ||
83 | .init_machine = s3c64xx_dt_init_machine, | 74 | .init_machine = s3c64xx_dt_init_machine, |
84 | .restart = s3c64xx_dt_restart, | 75 | .restart = s3c64xx_dt_restart, |
85 | MACHINE_END | 76 | MACHINE_END |
diff --git a/arch/arm/mach-s3c64xx/pl080.c b/arch/arm/mach-s3c64xx/pl080.c new file mode 100644 index 000000000000..901a984bddc2 --- /dev/null +++ b/arch/arm/mach-s3c64xx/pl080.c | |||
@@ -0,0 +1,244 @@ | |||
1 | /* | ||
2 | * Samsung's S3C64XX generic DMA support using amba-pl08x driver. | ||
3 | * | ||
4 | * Copyright (c) 2013 Tomasz Figa <tomasz.figa@gmail.com> | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify | ||
7 | * it under the terms of the GNU General Public License version 2 as | ||
8 | * published by the Free Software Foundation. | ||
9 | */ | ||
10 | |||
11 | #include <linux/kernel.h> | ||
12 | #include <linux/amba/bus.h> | ||
13 | #include <linux/amba/pl080.h> | ||
14 | #include <linux/amba/pl08x.h> | ||
15 | #include <linux/of.h> | ||
16 | |||
17 | #include <mach/irqs.h> | ||
18 | #include <mach/map.h> | ||
19 | |||
20 | #include "regs-sys.h" | ||
21 | |||
22 | static int pl08x_get_xfer_signal(const struct pl08x_channel_data *cd) | ||
23 | { | ||
24 | return cd->min_signal; | ||
25 | } | ||
26 | |||
27 | static void pl08x_put_xfer_signal(const struct pl08x_channel_data *cd, int ch) | ||
28 | { | ||
29 | } | ||
30 | |||
31 | /* | ||
32 | * DMA0 | ||
33 | */ | ||
34 | |||
35 | static struct pl08x_channel_data s3c64xx_dma0_info[] = { | ||
36 | { | ||
37 | .bus_id = "uart0_tx", | ||
38 | .min_signal = 0, | ||
39 | .max_signal = 0, | ||
40 | .periph_buses = PL08X_AHB2, | ||
41 | }, { | ||
42 | .bus_id = "uart0_rx", | ||
43 | .min_signal = 1, | ||
44 | .max_signal = 1, | ||
45 | .periph_buses = PL08X_AHB2, | ||
46 | }, { | ||
47 | .bus_id = "uart1_tx", | ||
48 | .min_signal = 2, | ||
49 | .max_signal = 2, | ||
50 | .periph_buses = PL08X_AHB2, | ||
51 | }, { | ||
52 | .bus_id = "uart1_rx", | ||
53 | .min_signal = 3, | ||
54 | .max_signal = 3, | ||
55 | .periph_buses = PL08X_AHB2, | ||
56 | }, { | ||
57 | .bus_id = "uart2_tx", | ||
58 | .min_signal = 4, | ||
59 | .max_signal = 4, | ||
60 | .periph_buses = PL08X_AHB2, | ||
61 | }, { | ||
62 | .bus_id = "uart2_rx", | ||
63 | .min_signal = 5, | ||
64 | .max_signal = 5, | ||
65 | .periph_buses = PL08X_AHB2, | ||
66 | }, { | ||
67 | .bus_id = "uart3_tx", | ||
68 | .min_signal = 6, | ||
69 | .max_signal = 6, | ||
70 | .periph_buses = PL08X_AHB2, | ||
71 | }, { | ||
72 | .bus_id = "uart3_rx", | ||
73 | .min_signal = 7, | ||
74 | .max_signal = 7, | ||
75 | .periph_buses = PL08X_AHB2, | ||
76 | }, { | ||
77 | .bus_id = "pcm0_tx", | ||
78 | .min_signal = 8, | ||
79 | .max_signal = 8, | ||
80 | .periph_buses = PL08X_AHB2, | ||
81 | }, { | ||
82 | .bus_id = "pcm0_rx", | ||
83 | .min_signal = 9, | ||
84 | .max_signal = 9, | ||
85 | .periph_buses = PL08X_AHB2, | ||
86 | }, { | ||
87 | .bus_id = "i2s0_tx", | ||
88 | .min_signal = 10, | ||
89 | .max_signal = 10, | ||
90 | .periph_buses = PL08X_AHB2, | ||
91 | }, { | ||
92 | .bus_id = "i2s0_rx", | ||
93 | .min_signal = 11, | ||
94 | .max_signal = 11, | ||
95 | .periph_buses = PL08X_AHB2, | ||
96 | }, { | ||
97 | .bus_id = "spi0_tx", | ||
98 | .min_signal = 12, | ||
99 | .max_signal = 12, | ||
100 | .periph_buses = PL08X_AHB2, | ||
101 | }, { | ||
102 | .bus_id = "spi0_rx", | ||
103 | .min_signal = 13, | ||
104 | .max_signal = 13, | ||
105 | .periph_buses = PL08X_AHB2, | ||
106 | }, { | ||
107 | .bus_id = "i2s2_tx", | ||
108 | .min_signal = 14, | ||
109 | .max_signal = 14, | ||
110 | .periph_buses = PL08X_AHB2, | ||
111 | }, { | ||
112 | .bus_id = "i2s2_rx", | ||
113 | .min_signal = 15, | ||
114 | .max_signal = 15, | ||
115 | .periph_buses = PL08X_AHB2, | ||
116 | } | ||
117 | }; | ||
118 | |||
119 | struct pl08x_platform_data s3c64xx_dma0_plat_data = { | ||
120 | .memcpy_channel = { | ||
121 | .bus_id = "memcpy", | ||
122 | .cctl_memcpy = | ||
123 | (PL080_BSIZE_4 << PL080_CONTROL_SB_SIZE_SHIFT | | ||
124 | PL080_BSIZE_4 << PL080_CONTROL_DB_SIZE_SHIFT | | ||
125 | PL080_WIDTH_32BIT << PL080_CONTROL_SWIDTH_SHIFT | | ||
126 | PL080_WIDTH_32BIT << PL080_CONTROL_DWIDTH_SHIFT | | ||
127 | PL080_CONTROL_PROT_BUFF | PL080_CONTROL_PROT_CACHE | | ||
128 | PL080_CONTROL_PROT_SYS), | ||
129 | }, | ||
130 | .lli_buses = PL08X_AHB1, | ||
131 | .mem_buses = PL08X_AHB1, | ||
132 | .get_xfer_signal = pl08x_get_xfer_signal, | ||
133 | .put_xfer_signal = pl08x_put_xfer_signal, | ||
134 | .slave_channels = s3c64xx_dma0_info, | ||
135 | .num_slave_channels = ARRAY_SIZE(s3c64xx_dma0_info), | ||
136 | }; | ||
137 | |||
138 | static AMBA_AHB_DEVICE(s3c64xx_dma0, "dma-pl080s.0", 0, | ||
139 | 0x75000000, {IRQ_DMA0}, &s3c64xx_dma0_plat_data); | ||
140 | |||
141 | /* | ||
142 | * DMA1 | ||
143 | */ | ||
144 | |||
145 | static struct pl08x_channel_data s3c64xx_dma1_info[] = { | ||
146 | { | ||
147 | .bus_id = "pcm1_tx", | ||
148 | .min_signal = 0, | ||
149 | .max_signal = 0, | ||
150 | .periph_buses = PL08X_AHB2, | ||
151 | }, { | ||
152 | .bus_id = "pcm1_rx", | ||
153 | .min_signal = 1, | ||
154 | .max_signal = 1, | ||
155 | .periph_buses = PL08X_AHB2, | ||
156 | }, { | ||
157 | .bus_id = "i2s1_tx", | ||
158 | .min_signal = 2, | ||
159 | .max_signal = 2, | ||
160 | .periph_buses = PL08X_AHB2, | ||
161 | }, { | ||
162 | .bus_id = "i2s1_rx", | ||
163 | .min_signal = 3, | ||
164 | .max_signal = 3, | ||
165 | .periph_buses = PL08X_AHB2, | ||
166 | }, { | ||
167 | .bus_id = "spi1_tx", | ||
168 | .min_signal = 4, | ||
169 | .max_signal = 4, | ||
170 | .periph_buses = PL08X_AHB2, | ||
171 | }, { | ||
172 | .bus_id = "spi1_rx", | ||
173 | .min_signal = 5, | ||
174 | .max_signal = 5, | ||
175 | .periph_buses = PL08X_AHB2, | ||
176 | }, { | ||
177 | .bus_id = "ac97_out", | ||
178 | .min_signal = 6, | ||
179 | .max_signal = 6, | ||
180 | .periph_buses = PL08X_AHB2, | ||
181 | }, { | ||
182 | .bus_id = "ac97_in", | ||
183 | .min_signal = 7, | ||
184 | .max_signal = 7, | ||
185 | .periph_buses = PL08X_AHB2, | ||
186 | }, { | ||
187 | .bus_id = "ac97_mic", | ||
188 | .min_signal = 8, | ||
189 | .max_signal = 8, | ||
190 | .periph_buses = PL08X_AHB2, | ||
191 | }, { | ||
192 | .bus_id = "pwm", | ||
193 | .min_signal = 9, | ||
194 | .max_signal = 9, | ||
195 | .periph_buses = PL08X_AHB2, | ||
196 | }, { | ||
197 | .bus_id = "irda", | ||
198 | .min_signal = 10, | ||
199 | .max_signal = 10, | ||
200 | .periph_buses = PL08X_AHB2, | ||
201 | }, { | ||
202 | .bus_id = "external", | ||
203 | .min_signal = 11, | ||
204 | .max_signal = 11, | ||
205 | .periph_buses = PL08X_AHB2, | ||
206 | }, | ||
207 | }; | ||
208 | |||
209 | struct pl08x_platform_data s3c64xx_dma1_plat_data = { | ||
210 | .memcpy_channel = { | ||
211 | .bus_id = "memcpy", | ||
212 | .cctl_memcpy = | ||
213 | (PL080_BSIZE_4 << PL080_CONTROL_SB_SIZE_SHIFT | | ||
214 | PL080_BSIZE_4 << PL080_CONTROL_DB_SIZE_SHIFT | | ||
215 | PL080_WIDTH_32BIT << PL080_CONTROL_SWIDTH_SHIFT | | ||
216 | PL080_WIDTH_32BIT << PL080_CONTROL_DWIDTH_SHIFT | | ||
217 | PL080_CONTROL_PROT_BUFF | PL080_CONTROL_PROT_CACHE | | ||
218 | PL080_CONTROL_PROT_SYS), | ||
219 | }, | ||
220 | .lli_buses = PL08X_AHB1, | ||
221 | .mem_buses = PL08X_AHB1, | ||
222 | .get_xfer_signal = pl08x_get_xfer_signal, | ||
223 | .put_xfer_signal = pl08x_put_xfer_signal, | ||
224 | .slave_channels = s3c64xx_dma1_info, | ||
225 | .num_slave_channels = ARRAY_SIZE(s3c64xx_dma1_info), | ||
226 | }; | ||
227 | |||
228 | static AMBA_AHB_DEVICE(s3c64xx_dma1, "dma-pl080s.1", 0, | ||
229 | 0x75100000, {IRQ_DMA1}, &s3c64xx_dma1_plat_data); | ||
230 | |||
231 | static int __init s3c64xx_pl080_init(void) | ||
232 | { | ||
233 | /* Set all DMA configuration to be DMA, not SDMA */ | ||
234 | writel(0xffffff, S3C64XX_SDMA_SEL); | ||
235 | |||
236 | if (of_have_populated_dt()) | ||
237 | return 0; | ||
238 | |||
239 | amba_device_register(&s3c64xx_dma0_device, &iomem_resource); | ||
240 | amba_device_register(&s3c64xx_dma1_device, &iomem_resource); | ||
241 | |||
242 | return 0; | ||
243 | } | ||
244 | arch_initcall(s3c64xx_pl080_init); | ||