diff options
-rw-r--r-- | arch/arm/mach-s3c2410/Makefile | 6 | ||||
-rw-r--r-- | arch/arm/mach-s3c2410/dma.c | 241 | ||||
-rw-r--r-- | arch/arm/mach-s3c2410/dma.h | 45 | ||||
-rw-r--r-- | include/asm-arm/arch-s3c2410/dma.h | 34 |
4 files changed, 257 insertions, 69 deletions
diff --git a/arch/arm/mach-s3c2410/Makefile b/arch/arm/mach-s3c2410/Makefile index 0eadec916214..e4c32f6ffb71 100644 --- a/arch/arm/mach-s3c2410/Makefile +++ b/arch/arm/mach-s3c2410/Makefile | |||
@@ -9,6 +9,8 @@ obj-y := cpu.o irq.o time.o gpio.o clock.o devs.o | |||
9 | obj-m := | 9 | obj-m := |
10 | obj-n := | 10 | obj-n := |
11 | obj- := | 11 | obj- := |
12 | obj-dma-y := | ||
13 | obj-dma-n := | ||
12 | 14 | ||
13 | # DMA | 15 | # DMA |
14 | obj-$(CONFIG_S3C2410_DMA) += dma.o | 16 | obj-$(CONFIG_S3C2410_DMA) += dma.o |
@@ -57,6 +59,10 @@ obj-$(CONFIG_CPU_S3C2442) += s3c2442-clock.o | |||
57 | 59 | ||
58 | obj-$(CONFIG_BAST_PC104_IRQ) += bast-irq.o | 60 | obj-$(CONFIG_BAST_PC104_IRQ) += bast-irq.o |
59 | 61 | ||
62 | # merge in dma objects | ||
63 | |||
64 | obj-y += $(obj-dma-y) | ||
65 | |||
60 | # machine specific support | 66 | # machine specific support |
61 | 67 | ||
62 | obj-$(CONFIG_MACH_ANUBIS) += mach-anubis.o | 68 | obj-$(CONFIG_MACH_ANUBIS) += mach-anubis.o |
diff --git a/arch/arm/mach-s3c2410/dma.c b/arch/arm/mach-s3c2410/dma.c index cc92a7b2db88..d264bbbd8bef 100644 --- a/arch/arm/mach-s3c2410/dma.c +++ b/arch/arm/mach-s3c2410/dma.c | |||
@@ -1,35 +1,16 @@ | |||
1 | /* linux/arch/arm/mach-bast/dma.c | 1 | /* linux/arch/arm/mach-s3c2410/dma.c |
2 | * | 2 | * |
3 | * (c) 2003-2005 Simtec Electronics | 3 | * (c) 2003-2005,2006 Simtec Electronics |
4 | * Ben Dooks <ben@simtec.co.uk> | 4 | * Ben Dooks <ben@simtec.co.uk> |
5 | * | 5 | * |
6 | * S3C2410 DMA core | 6 | * S3C2410 DMA core |
7 | * | 7 | * |
8 | * http://www.simtec.co.uk/products/EB2410ITX/ | 8 | * http://armlinux.simtec.co.uk/ |
9 | * | 9 | * |
10 | * This program is free software; you can redistribute it and/or modify | 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 | 11 | * it under the terms of the GNU General Public License version 2 as |
12 | * published by the Free Software Foundation. | 12 | * published by the Free Software Foundation. |
13 | * | 13 | */ |
14 | * Changelog: | ||
15 | * 27-Feb-2005 BJD Added kmem cache for dma descriptors | ||
16 | * 18-Nov-2004 BJD Removed error for loading onto stopped channel | ||
17 | * 10-Nov-2004 BJD Ensure all external symbols exported for modules | ||
18 | * 10-Nov-2004 BJD Use sys_device and sysdev_class for power management | ||
19 | * 08-Aug-2004 BJD Apply rmk's suggestions | ||
20 | * 21-Jul-2004 BJD Ported to linux 2.6 | ||
21 | * 12-Jul-2004 BJD Finished re-write and change of API | ||
22 | * 06-Jul-2004 BJD Rewrote dma code to try and cope with various problems | ||
23 | * 23-May-2003 BJD Created file | ||
24 | * 19-Aug-2003 BJD Cleanup, header fix, added URL | ||
25 | * | ||
26 | * This file is based on the Sangwook Lee/Samsung patches, re-written due | ||
27 | * to various ommisions from the code (such as flexible dma configuration) | ||
28 | * for use with the BAST system board. | ||
29 | * | ||
30 | * The re-write is pretty much complete, and should be good enough for any | ||
31 | * possible DMA function | ||
32 | */ | ||
33 | 14 | ||
34 | 15 | ||
35 | #ifdef CONFIG_S3C2410_DMA_DEBUG | 16 | #ifdef CONFIG_S3C2410_DMA_DEBUG |
@@ -55,10 +36,14 @@ | |||
55 | #include <asm/mach/dma.h> | 36 | #include <asm/mach/dma.h> |
56 | #include <asm/arch/map.h> | 37 | #include <asm/arch/map.h> |
57 | 38 | ||
39 | #include "dma.h" | ||
40 | |||
58 | /* io map for dma */ | 41 | /* io map for dma */ |
59 | static void __iomem *dma_base; | 42 | static void __iomem *dma_base; |
60 | static kmem_cache_t *dma_kmem; | 43 | static kmem_cache_t *dma_kmem; |
61 | 44 | ||
45 | struct s3c24xx_dma_selection dma_sel; | ||
46 | |||
62 | /* dma channel state information */ | 47 | /* dma channel state information */ |
63 | struct s3c2410_dma_chan s3c2410_chans[S3C2410_DMA_CHANNELS]; | 48 | struct s3c2410_dma_chan s3c2410_chans[S3C2410_DMA_CHANNELS]; |
64 | 49 | ||
@@ -79,7 +64,6 @@ dma_wrreg(struct s3c2410_dma_chan *chan, int reg, unsigned long val) | |||
79 | pr_debug("writing %08x to register %08x\n",(unsigned int)val,reg); | 64 | pr_debug("writing %08x to register %08x\n",(unsigned int)val,reg); |
80 | writel(val, dma_regaddr(chan, reg)); | 65 | writel(val, dma_regaddr(chan, reg)); |
81 | } | 66 | } |
82 | |||
83 | #endif | 67 | #endif |
84 | 68 | ||
85 | #define dma_rdreg(chan, reg) readl((chan)->regs + (reg)) | 69 | #define dma_rdreg(chan, reg) readl((chan)->regs + (reg)) |
@@ -151,12 +135,20 @@ dmadbg_showregs(const char *fname, int line, struct s3c2410_dma_chan *chan) | |||
151 | #define dbg_showchan(chan) do { } while(0) | 135 | #define dbg_showchan(chan) do { } while(0) |
152 | #endif /* CONFIG_S3C2410_DMA_DEBUG */ | 136 | #endif /* CONFIG_S3C2410_DMA_DEBUG */ |
153 | 137 | ||
154 | #define check_channel(chan) \ | 138 | static struct s3c2410_dma_chan *dma_chan_map[DMACH_MAX]; |
155 | do { if ((chan) >= S3C2410_DMA_CHANNELS) { \ | ||
156 | printk(KERN_ERR "%s: invalid channel %d\n", __FUNCTION__, (chan)); \ | ||
157 | return -EINVAL; \ | ||
158 | } } while(0) | ||
159 | 139 | ||
140 | /* lookup_dma_channel | ||
141 | * | ||
142 | * change the dma channel number given into a real dma channel id | ||
143 | */ | ||
144 | |||
145 | static struct s3c2410_dma_chan *lookup_dma_channel(unsigned int channel) | ||
146 | { | ||
147 | if (channel & DMACH_LOW_LEVEL) | ||
148 | return &s3c2410_chans[channel & ~DMACH_LOW_LEVEL]; | ||
149 | else | ||
150 | return dma_chan_map[channel]; | ||
151 | } | ||
160 | 152 | ||
161 | /* s3c2410_dma_stats_timeout | 153 | /* s3c2410_dma_stats_timeout |
162 | * | 154 | * |
@@ -321,8 +313,10 @@ static inline void | |||
321 | s3c2410_dma_buffdone(struct s3c2410_dma_chan *chan, struct s3c2410_dma_buf *buf, | 313 | s3c2410_dma_buffdone(struct s3c2410_dma_chan *chan, struct s3c2410_dma_buf *buf, |
322 | enum s3c2410_dma_buffresult result) | 314 | enum s3c2410_dma_buffresult result) |
323 | { | 315 | { |
316 | #if 0 | ||
324 | pr_debug("callback_fn=%p, buf=%p, id=%p, size=%d, result=%d\n", | 317 | pr_debug("callback_fn=%p, buf=%p, id=%p, size=%d, result=%d\n", |
325 | chan->callback_fn, buf, buf->id, buf->size, result); | 318 | chan->callback_fn, buf, buf->id, buf->size, result); |
319 | #endif | ||
326 | 320 | ||
327 | if (chan->callback_fn != NULL) { | 321 | if (chan->callback_fn != NULL) { |
328 | (chan->callback_fn)(chan, buf->id, buf->size, result); | 322 | (chan->callback_fn)(chan, buf->id, buf->size, result); |
@@ -439,7 +433,6 @@ s3c2410_dma_canload(struct s3c2410_dma_chan *chan) | |||
439 | return 0; | 433 | return 0; |
440 | } | 434 | } |
441 | 435 | ||
442 | |||
443 | /* s3c2410_dma_enqueue | 436 | /* s3c2410_dma_enqueue |
444 | * | 437 | * |
445 | * queue an given buffer for dma transfer. | 438 | * queue an given buffer for dma transfer. |
@@ -460,11 +453,12 @@ s3c2410_dma_canload(struct s3c2410_dma_chan *chan) | |||
460 | int s3c2410_dma_enqueue(unsigned int channel, void *id, | 453 | int s3c2410_dma_enqueue(unsigned int channel, void *id, |
461 | dma_addr_t data, int size) | 454 | dma_addr_t data, int size) |
462 | { | 455 | { |
463 | struct s3c2410_dma_chan *chan = &s3c2410_chans[channel]; | 456 | struct s3c2410_dma_chan *chan = lookup_dma_channel(channel); |
464 | struct s3c2410_dma_buf *buf; | 457 | struct s3c2410_dma_buf *buf; |
465 | unsigned long flags; | 458 | unsigned long flags; |
466 | 459 | ||
467 | check_channel(channel); | 460 | if (chan == NULL) |
461 | return -EINVAL; | ||
468 | 462 | ||
469 | pr_debug("%s: id=%p, data=%08x, size=%d\n", | 463 | pr_debug("%s: id=%p, data=%08x, size=%d\n", |
470 | __FUNCTION__, id, (unsigned int)data, size); | 464 | __FUNCTION__, id, (unsigned int)data, size); |
@@ -562,8 +556,10 @@ s3c2410_dma_freebuf(struct s3c2410_dma_buf *buf) | |||
562 | static inline void | 556 | static inline void |
563 | s3c2410_dma_lastxfer(struct s3c2410_dma_chan *chan) | 557 | s3c2410_dma_lastxfer(struct s3c2410_dma_chan *chan) |
564 | { | 558 | { |
559 | #if 0 | ||
565 | pr_debug("dma%d: s3c2410_dma_lastxfer: load_state %d\n", | 560 | pr_debug("dma%d: s3c2410_dma_lastxfer: load_state %d\n", |
566 | chan->number, chan->load_state); | 561 | chan->number, chan->load_state); |
562 | #endif | ||
567 | 563 | ||
568 | switch (chan->load_state) { | 564 | switch (chan->load_state) { |
569 | case S3C2410_DMALOAD_NONE: | 565 | case S3C2410_DMALOAD_NONE: |
@@ -718,7 +714,8 @@ s3c2410_dma_irq(int irq, void *devpw, struct pt_regs *regs) | |||
718 | if (chan->load_state == S3C2410_DMALOAD_NONE) { | 714 | if (chan->load_state == S3C2410_DMALOAD_NONE) { |
719 | pr_debug("dma%d: end of transfer, stopping channel (%ld)\n", | 715 | pr_debug("dma%d: end of transfer, stopping channel (%ld)\n", |
720 | chan->number, jiffies); | 716 | chan->number, jiffies); |
721 | s3c2410_dma_ctrl(chan->number, S3C2410_DMAOP_STOP); | 717 | s3c2410_dma_ctrl(chan->number | DMACH_LOW_LEVEL, |
718 | S3C2410_DMAOP_STOP); | ||
722 | } | 719 | } |
723 | } | 720 | } |
724 | 721 | ||
@@ -726,37 +723,34 @@ s3c2410_dma_irq(int irq, void *devpw, struct pt_regs *regs) | |||
726 | return IRQ_HANDLED; | 723 | return IRQ_HANDLED; |
727 | } | 724 | } |
728 | 725 | ||
726 | static struct s3c2410_dma_chan *s3c2410_dma_map_channel(int channel); | ||
727 | |||
729 | /* s3c2410_request_dma | 728 | /* s3c2410_request_dma |
730 | * | 729 | * |
731 | * get control of an dma channel | 730 | * get control of an dma channel |
732 | */ | 731 | */ |
733 | 732 | ||
734 | int s3c2410_dma_request(unsigned int channel, struct s3c2410_dma_client *client, | 733 | int s3c2410_dma_request(unsigned int channel, |
734 | struct s3c2410_dma_client *client, | ||
735 | void *dev) | 735 | void *dev) |
736 | { | 736 | { |
737 | struct s3c2410_dma_chan *chan = &s3c2410_chans[channel]; | 737 | struct s3c2410_dma_chan *chan; |
738 | unsigned long flags; | 738 | unsigned long flags; |
739 | int err; | 739 | int err; |
740 | 740 | ||
741 | pr_debug("dma%d: s3c2410_request_dma: client=%s, dev=%p\n", | 741 | pr_debug("dma%d: s3c2410_request_dma: client=%s, dev=%p\n", |
742 | channel, client->name, dev); | 742 | channel, client->name, dev); |
743 | 743 | ||
744 | check_channel(channel); | ||
745 | |||
746 | local_irq_save(flags); | 744 | local_irq_save(flags); |
747 | 745 | ||
748 | dbg_showchan(chan); | 746 | chan = s3c2410_dma_map_channel(channel); |
749 | 747 | if (chan == NULL) { | |
750 | if (chan->in_use) { | 748 | local_irq_restore(flags); |
751 | if (client != chan->client) { | 749 | return -EBUSY; |
752 | printk(KERN_ERR "dma%d: already in use\n", channel); | ||
753 | local_irq_restore(flags); | ||
754 | return -EBUSY; | ||
755 | } else { | ||
756 | printk(KERN_ERR "dma%d: client already has channel\n", channel); | ||
757 | } | ||
758 | } | 750 | } |
759 | 751 | ||
752 | dbg_showchan(chan); | ||
753 | |||
760 | chan->client = client; | 754 | chan->client = client; |
761 | chan->in_use = 1; | 755 | chan->in_use = 1; |
762 | 756 | ||
@@ -809,14 +803,14 @@ EXPORT_SYMBOL(s3c2410_dma_request); | |||
809 | 803 | ||
810 | int s3c2410_dma_free(dmach_t channel, struct s3c2410_dma_client *client) | 804 | int s3c2410_dma_free(dmach_t channel, struct s3c2410_dma_client *client) |
811 | { | 805 | { |
812 | struct s3c2410_dma_chan *chan = &s3c2410_chans[channel]; | 806 | struct s3c2410_dma_chan *chan = lookup_dma_channel(channel); |
813 | unsigned long flags; | 807 | unsigned long flags; |
814 | 808 | ||
815 | check_channel(channel); | 809 | if (chan == NULL) |
810 | return -EINVAL; | ||
816 | 811 | ||
817 | local_irq_save(flags); | 812 | local_irq_save(flags); |
818 | 813 | ||
819 | |||
820 | if (chan->client != client) { | 814 | if (chan->client != client) { |
821 | printk(KERN_WARNING "dma%d: possible free from different client (channel %p, passed %p)\n", | 815 | printk(KERN_WARNING "dma%d: possible free from different client (channel %p, passed %p)\n", |
822 | channel, chan->client, client); | 816 | channel, chan->client, client); |
@@ -837,8 +831,12 @@ int s3c2410_dma_free(dmach_t channel, struct s3c2410_dma_client *client) | |||
837 | 831 | ||
838 | if (chan->irq_claimed) | 832 | if (chan->irq_claimed) |
839 | free_irq(chan->irq, (void *)chan); | 833 | free_irq(chan->irq, (void *)chan); |
834 | |||
840 | chan->irq_claimed = 0; | 835 | chan->irq_claimed = 0; |
841 | 836 | ||
837 | if (!(channel & DMACH_LOW_LEVEL)) | ||
838 | dma_chan_map[channel] = NULL; | ||
839 | |||
842 | local_irq_restore(flags); | 840 | local_irq_restore(flags); |
843 | 841 | ||
844 | return 0; | 842 | return 0; |
@@ -848,8 +846,8 @@ EXPORT_SYMBOL(s3c2410_dma_free); | |||
848 | 846 | ||
849 | static int s3c2410_dma_dostop(struct s3c2410_dma_chan *chan) | 847 | static int s3c2410_dma_dostop(struct s3c2410_dma_chan *chan) |
850 | { | 848 | { |
851 | unsigned long tmp; | ||
852 | unsigned long flags; | 849 | unsigned long flags; |
850 | unsigned long tmp; | ||
853 | 851 | ||
854 | pr_debug("%s:\n", __FUNCTION__); | 852 | pr_debug("%s:\n", __FUNCTION__); |
855 | 853 | ||
@@ -997,9 +995,10 @@ s3c2410_dma_started(struct s3c2410_dma_chan *chan) | |||
997 | int | 995 | int |
998 | s3c2410_dma_ctrl(dmach_t channel, enum s3c2410_chan_op op) | 996 | s3c2410_dma_ctrl(dmach_t channel, enum s3c2410_chan_op op) |
999 | { | 997 | { |
1000 | struct s3c2410_dma_chan *chan = &s3c2410_chans[channel]; | 998 | struct s3c2410_dma_chan *chan = lookup_dma_channel(channel); |
1001 | 999 | ||
1002 | check_channel(channel); | 1000 | if (chan == NULL) |
1001 | return -EINVAL; | ||
1003 | 1002 | ||
1004 | switch (op) { | 1003 | switch (op) { |
1005 | case S3C2410_DMAOP_START: | 1004 | case S3C2410_DMAOP_START: |
@@ -1046,12 +1045,19 @@ int s3c2410_dma_config(dmach_t channel, | |||
1046 | int xferunit, | 1045 | int xferunit, |
1047 | int dcon) | 1046 | int dcon) |
1048 | { | 1047 | { |
1049 | struct s3c2410_dma_chan *chan = &s3c2410_chans[channel]; | 1048 | struct s3c2410_dma_chan *chan = lookup_dma_channel(channel); |
1050 | 1049 | ||
1051 | pr_debug("%s: chan=%d, xfer_unit=%d, dcon=%08x\n", | 1050 | pr_debug("%s: chan=%d, xfer_unit=%d, dcon=%08x\n", |
1052 | __FUNCTION__, channel, xferunit, dcon); | 1051 | __FUNCTION__, channel, xferunit, dcon); |
1053 | 1052 | ||
1054 | check_channel(channel); | 1053 | if (chan == NULL) |
1054 | return -EINVAL; | ||
1055 | |||
1056 | printk("Initial dcon is %08x\n", dcon); | ||
1057 | |||
1058 | dcon |= chan->dcon & dma_sel.dcon_mask; | ||
1059 | |||
1060 | printk("New dcon is %08x\n", dcon); | ||
1055 | 1061 | ||
1056 | switch (xferunit) { | 1062 | switch (xferunit) { |
1057 | case 1: | 1063 | case 1: |
@@ -1086,9 +1092,10 @@ EXPORT_SYMBOL(s3c2410_dma_config); | |||
1086 | 1092 | ||
1087 | int s3c2410_dma_setflags(dmach_t channel, unsigned int flags) | 1093 | int s3c2410_dma_setflags(dmach_t channel, unsigned int flags) |
1088 | { | 1094 | { |
1089 | struct s3c2410_dma_chan *chan = &s3c2410_chans[channel]; | 1095 | struct s3c2410_dma_chan *chan = lookup_dma_channel(channel); |
1090 | 1096 | ||
1091 | check_channel(channel); | 1097 | if (chan == NULL) |
1098 | return -EINVAL; | ||
1092 | 1099 | ||
1093 | pr_debug("%s: chan=%p, flags=%08x\n", __FUNCTION__, chan, flags); | 1100 | pr_debug("%s: chan=%p, flags=%08x\n", __FUNCTION__, chan, flags); |
1094 | 1101 | ||
@@ -1106,9 +1113,10 @@ EXPORT_SYMBOL(s3c2410_dma_setflags); | |||
1106 | 1113 | ||
1107 | int s3c2410_dma_set_opfn(dmach_t channel, s3c2410_dma_opfn_t rtn) | 1114 | int s3c2410_dma_set_opfn(dmach_t channel, s3c2410_dma_opfn_t rtn) |
1108 | { | 1115 | { |
1109 | struct s3c2410_dma_chan *chan = &s3c2410_chans[channel]; | 1116 | struct s3c2410_dma_chan *chan = lookup_dma_channel(channel); |
1110 | 1117 | ||
1111 | check_channel(channel); | 1118 | if (chan == NULL) |
1119 | return -EINVAL; | ||
1112 | 1120 | ||
1113 | pr_debug("%s: chan=%p, op rtn=%p\n", __FUNCTION__, chan, rtn); | 1121 | pr_debug("%s: chan=%p, op rtn=%p\n", __FUNCTION__, chan, rtn); |
1114 | 1122 | ||
@@ -1121,9 +1129,10 @@ EXPORT_SYMBOL(s3c2410_dma_set_opfn); | |||
1121 | 1129 | ||
1122 | int s3c2410_dma_set_buffdone_fn(dmach_t channel, s3c2410_dma_cbfn_t rtn) | 1130 | int s3c2410_dma_set_buffdone_fn(dmach_t channel, s3c2410_dma_cbfn_t rtn) |
1123 | { | 1131 | { |
1124 | struct s3c2410_dma_chan *chan = &s3c2410_chans[channel]; | 1132 | struct s3c2410_dma_chan *chan = lookup_dma_channel(channel); |
1125 | 1133 | ||
1126 | check_channel(channel); | 1134 | if (chan == NULL) |
1135 | return -EINVAL; | ||
1127 | 1136 | ||
1128 | pr_debug("%s: chan=%p, callback rtn=%p\n", __FUNCTION__, chan, rtn); | 1137 | pr_debug("%s: chan=%p, callback rtn=%p\n", __FUNCTION__, chan, rtn); |
1129 | 1138 | ||
@@ -1153,9 +1162,10 @@ int s3c2410_dma_devconfig(int channel, | |||
1153 | int hwcfg, | 1162 | int hwcfg, |
1154 | unsigned long devaddr) | 1163 | unsigned long devaddr) |
1155 | { | 1164 | { |
1156 | struct s3c2410_dma_chan *chan = &s3c2410_chans[channel]; | 1165 | struct s3c2410_dma_chan *chan = lookup_dma_channel(channel); |
1157 | 1166 | ||
1158 | check_channel(channel); | 1167 | if (chan == NULL) |
1168 | return -EINVAL; | ||
1159 | 1169 | ||
1160 | pr_debug("%s: source=%d, hwcfg=%08x, devaddr=%08lx\n", | 1170 | pr_debug("%s: source=%d, hwcfg=%08x, devaddr=%08lx\n", |
1161 | __FUNCTION__, (int)source, hwcfg, devaddr); | 1171 | __FUNCTION__, (int)source, hwcfg, devaddr); |
@@ -1200,9 +1210,10 @@ EXPORT_SYMBOL(s3c2410_dma_devconfig); | |||
1200 | 1210 | ||
1201 | int s3c2410_dma_getposition(dmach_t channel, dma_addr_t *src, dma_addr_t *dst) | 1211 | int s3c2410_dma_getposition(dmach_t channel, dma_addr_t *src, dma_addr_t *dst) |
1202 | { | 1212 | { |
1203 | struct s3c2410_dma_chan *chan = &s3c2410_chans[channel]; | 1213 | struct s3c2410_dma_chan *chan = lookup_dma_channel(channel); |
1204 | 1214 | ||
1205 | check_channel(channel); | 1215 | if (chan == NULL) |
1216 | return -EINVAL; | ||
1206 | 1217 | ||
1207 | if (src != NULL) | 1218 | if (src != NULL) |
1208 | *src = dma_rdreg(chan, S3C2410_DMA_DCSRC); | 1219 | *src = dma_rdreg(chan, S3C2410_DMA_DCSRC); |
@@ -1252,7 +1263,7 @@ static int s3c2410_dma_resume(struct sys_device *dev) | |||
1252 | #define s3c2410_dma_resume NULL | 1263 | #define s3c2410_dma_resume NULL |
1253 | #endif /* CONFIG_PM */ | 1264 | #endif /* CONFIG_PM */ |
1254 | 1265 | ||
1255 | static struct sysdev_class dma_sysclass = { | 1266 | struct sysdev_class dma_sysclass = { |
1256 | set_kset_name("s3c24xx-dma"), | 1267 | set_kset_name("s3c24xx-dma"), |
1257 | .suspend = s3c2410_dma_suspend, | 1268 | .suspend = s3c2410_dma_suspend, |
1258 | .resume = s3c2410_dma_resume, | 1269 | .resume = s3c2410_dma_resume, |
@@ -1265,7 +1276,6 @@ static void s3c2410_dma_cache_ctor(void *p, kmem_cache_t *c, unsigned long f) | |||
1265 | memset(p, 0, sizeof(struct s3c2410_dma_buf)); | 1276 | memset(p, 0, sizeof(struct s3c2410_dma_buf)); |
1266 | } | 1277 | } |
1267 | 1278 | ||
1268 | |||
1269 | /* initialisation code */ | 1279 | /* initialisation code */ |
1270 | 1280 | ||
1271 | static int __init s3c2410_init_dma(void) | 1281 | static int __init s3c2410_init_dma(void) |
@@ -1274,7 +1284,7 @@ static int __init s3c2410_init_dma(void) | |||
1274 | int channel; | 1284 | int channel; |
1275 | int ret; | 1285 | int ret; |
1276 | 1286 | ||
1277 | printk("S3C2410 DMA Driver, (c) 2003-2004 Simtec Electronics\n"); | 1287 | printk("S3C24XX DMA Driver, (c) 2003-2004,2006 Simtec Electronics\n"); |
1278 | 1288 | ||
1279 | dma_base = ioremap(S3C24XX_PA_DMA, 0x200); | 1289 | dma_base = ioremap(S3C24XX_PA_DMA, 0x200); |
1280 | if (dma_base == NULL) { | 1290 | if (dma_base == NULL) { |
@@ -1282,6 +1292,8 @@ static int __init s3c2410_init_dma(void) | |||
1282 | return -ENOMEM; | 1292 | return -ENOMEM; |
1283 | } | 1293 | } |
1284 | 1294 | ||
1295 | printk("Registering sysclass\n"); | ||
1296 | |||
1285 | ret = sysdev_class_register(&dma_sysclass); | 1297 | ret = sysdev_class_register(&dma_sysclass); |
1286 | if (ret != 0) { | 1298 | if (ret != 0) { |
1287 | printk(KERN_ERR "dma sysclass registration failed\n"); | 1299 | printk(KERN_ERR "dma sysclass registration failed\n"); |
@@ -1335,4 +1347,95 @@ static int __init s3c2410_init_dma(void) | |||
1335 | return ret; | 1347 | return ret; |
1336 | } | 1348 | } |
1337 | 1349 | ||
1338 | __initcall(s3c2410_init_dma); | 1350 | core_initcall(s3c2410_init_dma); |
1351 | |||
1352 | static inline int is_channel_valid(unsigned int channel) | ||
1353 | { | ||
1354 | return (channel & DMA_CH_VALID); | ||
1355 | } | ||
1356 | |||
1357 | /* s3c2410_dma_map_channel() | ||
1358 | * | ||
1359 | * turn the virtual channel number into a real, and un-used hardware | ||
1360 | * channel. | ||
1361 | * | ||
1362 | * currently this code uses first-free channel from the specified harware | ||
1363 | * map, not taking into account anything that the board setup code may | ||
1364 | * have to say about the likely peripheral set to be in use. | ||
1365 | */ | ||
1366 | |||
1367 | struct s3c2410_dma_chan *s3c2410_dma_map_channel(int channel) | ||
1368 | { | ||
1369 | struct s3c24xx_dma_map *ch_map; | ||
1370 | struct s3c2410_dma_chan *dmach; | ||
1371 | int ch; | ||
1372 | |||
1373 | if (dma_sel.map == NULL || channel > dma_sel.map_size) | ||
1374 | return NULL; | ||
1375 | |||
1376 | ch_map = dma_sel.map + channel; | ||
1377 | |||
1378 | for (ch = 0; ch < S3C2410_DMA_CHANNELS; ch++) { | ||
1379 | if (!is_channel_valid(ch_map->channels[ch])) | ||
1380 | continue; | ||
1381 | |||
1382 | if (s3c2410_chans[ch].in_use == 0) { | ||
1383 | printk("mapped channel %d to %d\n", channel, ch); | ||
1384 | break; | ||
1385 | } | ||
1386 | } | ||
1387 | |||
1388 | if (ch >= S3C2410_DMA_CHANNELS) | ||
1389 | return NULL; | ||
1390 | |||
1391 | /* update our channel mapping */ | ||
1392 | |||
1393 | dmach = &s3c2410_chans[ch]; | ||
1394 | dma_chan_map[channel] = dmach; | ||
1395 | |||
1396 | /* select the channel */ | ||
1397 | |||
1398 | (dma_sel.select)(dmach, ch_map); | ||
1399 | |||
1400 | return dmach; | ||
1401 | } | ||
1402 | |||
1403 | static void s3c24xx_dma_show_ch(struct s3c24xx_dma_map *map, int ch) | ||
1404 | { | ||
1405 | /* show the channel configuration */ | ||
1406 | |||
1407 | printk("%2d: %20s, channels %c%c%c%c\n", ch, map->name, | ||
1408 | (is_channel_valid(map->channels[0]) ? '0' : '-'), | ||
1409 | (is_channel_valid(map->channels[1]) ? '1' : '-'), | ||
1410 | (is_channel_valid(map->channels[2]) ? '2' : '-'), | ||
1411 | (is_channel_valid(map->channels[3]) ? '3' : '-')); | ||
1412 | } | ||
1413 | |||
1414 | static int s3c24xx_dma_check_entry(struct s3c24xx_dma_map *map, int ch) | ||
1415 | { | ||
1416 | if (1) | ||
1417 | s3c24xx_dma_show_ch(map, ch); | ||
1418 | |||
1419 | return 0; | ||
1420 | } | ||
1421 | |||
1422 | int __init s3c24xx_dma_init_map(struct s3c24xx_dma_selection *sel) | ||
1423 | { | ||
1424 | struct s3c24xx_dma_map *nmap; | ||
1425 | size_t map_sz = sizeof(*nmap) * sel->map_size; | ||
1426 | int ptr; | ||
1427 | |||
1428 | nmap = kmalloc(map_sz, GFP_KERNEL); | ||
1429 | if (nmap == NULL) | ||
1430 | return -ENOMEM; | ||
1431 | |||
1432 | memcpy(nmap, sel->map, map_sz); | ||
1433 | memcpy(&dma_sel, sel, sizeof(*sel)); | ||
1434 | |||
1435 | dma_sel.map = nmap; | ||
1436 | |||
1437 | for (ptr = 0; ptr < sel->map_size; ptr++) | ||
1438 | s3c24xx_dma_check_entry(nmap+ptr, ptr); | ||
1439 | |||
1440 | return 0; | ||
1441 | } | ||
diff --git a/arch/arm/mach-s3c2410/dma.h b/arch/arm/mach-s3c2410/dma.h new file mode 100644 index 000000000000..0ebfe0aab80b --- /dev/null +++ b/arch/arm/mach-s3c2410/dma.h | |||
@@ -0,0 +1,45 @@ | |||
1 | /* arch/arm/mach-s3c2410/dma.h | ||
2 | * | ||
3 | * Copyright (C) 2006 Simtec Electronics | ||
4 | * Ben Dooks <ben@simtec.co.uk> | ||
5 | * | ||
6 | * Samsung S3C24XX DMA support | ||
7 | * | ||
8 | * This program is free software; you can redistribute it and/or modify | ||
9 | * it under the terms of the GNU General Public License version 2 as | ||
10 | * published by the Free Software Foundation. | ||
11 | */ | ||
12 | |||
13 | extern struct sysdev_class dma_sysclass; | ||
14 | extern struct s3c2410_dma_chan s3c2410_chans[S3C2410_DMA_CHANNELS]; | ||
15 | |||
16 | #define DMA_CH_VALID (1<<31) | ||
17 | |||
18 | struct s3c24xx_dma_addr { | ||
19 | unsigned long from; | ||
20 | unsigned long to; | ||
21 | }; | ||
22 | |||
23 | /* struct s3c24xx_dma_map | ||
24 | * | ||
25 | * this holds the mapping information for the channel selected | ||
26 | * to be connected to the specified device | ||
27 | */ | ||
28 | |||
29 | struct s3c24xx_dma_map { | ||
30 | const char *name; | ||
31 | struct s3c24xx_dma_addr hw_addr; | ||
32 | |||
33 | unsigned long channels[S3C2410_DMA_CHANNELS]; | ||
34 | }; | ||
35 | |||
36 | struct s3c24xx_dma_selection { | ||
37 | struct s3c24xx_dma_map *map; | ||
38 | unsigned long map_size; | ||
39 | unsigned long dcon_mask; | ||
40 | |||
41 | void (*select)(struct s3c2410_dma_chan *chan, | ||
42 | struct s3c24xx_dma_map *map); | ||
43 | }; | ||
44 | |||
45 | extern int s3c24xx_dma_init_map(struct s3c24xx_dma_selection *sel); | ||
diff --git a/include/asm-arm/arch-s3c2410/dma.h b/include/asm-arm/arch-s3c2410/dma.h index 3661e465b0a5..166fc89d62d7 100644 --- a/include/asm-arm/arch-s3c2410/dma.h +++ b/include/asm-arm/arch-s3c2410/dma.h | |||
@@ -23,6 +23,36 @@ | |||
23 | #define MAX_DMA_ADDRESS 0x40000000 | 23 | #define MAX_DMA_ADDRESS 0x40000000 |
24 | #define MAX_DMA_TRANSFER_SIZE 0x100000 /* Data Unit is half word */ | 24 | #define MAX_DMA_TRANSFER_SIZE 0x100000 /* Data Unit is half word */ |
25 | 25 | ||
26 | /* We use `virtual` dma channels to hide the fact we have only a limited | ||
27 | * number of DMA channels, and not of all of them (dependant on the device) | ||
28 | * can be attached to any DMA source. We therefore let the DMA core handle | ||
29 | * the allocation of hardware channels to clients. | ||
30 | */ | ||
31 | |||
32 | enum dma_ch { | ||
33 | DMACH_XD0, | ||
34 | DMACH_XD1, | ||
35 | DMACH_SDI, | ||
36 | DMACH_SPI0, | ||
37 | DMACH_SPI1, | ||
38 | DMACH_UART0, | ||
39 | DMACH_UART1, | ||
40 | DMACH_UART2, | ||
41 | DMACH_TIMER, | ||
42 | DMACH_I2S_IN, | ||
43 | DMACH_I2S_OUT, | ||
44 | DMACH_PCM_IN, | ||
45 | DMACH_PCM_OUT, | ||
46 | DMACH_MIC_IN, | ||
47 | DMACH_USB_EP1, | ||
48 | DMACH_USB_EP2, | ||
49 | DMACH_USB_EP3, | ||
50 | DMACH_USB_EP4, | ||
51 | DMACH_MAX, /* the end entry */ | ||
52 | }; | ||
53 | |||
54 | #define DMACH_LOW_LEVEL (1<<28) /* use this to specifiy hardware ch no */ | ||
55 | |||
26 | /* we have 4 dma channels */ | 56 | /* we have 4 dma channels */ |
27 | #define S3C2410_DMA_CHANNELS (4) | 57 | #define S3C2410_DMA_CHANNELS (4) |
28 | 58 | ||
@@ -149,6 +179,8 @@ struct s3c2410_dma_stats { | |||
149 | unsigned long timeout_failed; | 179 | unsigned long timeout_failed; |
150 | }; | 180 | }; |
151 | 181 | ||
182 | struct s3c2410_dma_map; | ||
183 | |||
152 | /* struct s3c2410_dma_chan | 184 | /* struct s3c2410_dma_chan |
153 | * | 185 | * |
154 | * full state information for each DMA channel | 186 | * full state information for each DMA channel |
@@ -174,6 +206,8 @@ struct s3c2410_dma_chan { | |||
174 | unsigned long load_timeout; | 206 | unsigned long load_timeout; |
175 | unsigned int flags; /* channel flags */ | 207 | unsigned int flags; /* channel flags */ |
176 | 208 | ||
209 | struct s3c24xx_dma_map *map; /* channel hw maps */ | ||
210 | |||
177 | /* channel's hardware position and configuration */ | 211 | /* channel's hardware position and configuration */ |
178 | void __iomem *regs; /* channels registers */ | 212 | void __iomem *regs; /* channels registers */ |
179 | void __iomem *addr_reg; /* data address register */ | 213 | void __iomem *addr_reg; /* data address register */ |