diff options
author | Oskar Schirmer <os@emlix.com> | 2009-06-10 15:58:45 -0400 |
---|---|---|
committer | Chris Zankel <chris@zankel.net> | 2009-06-22 05:37:11 -0400 |
commit | f24e552c2dc3221dc7bd2296fd8a705283c4b2d7 (patch) | |
tree | fb48784672fb3b0085da2955a77cf90e87a17a74 /arch/xtensa/variants/s6000/dmac.c | |
parent | 1beee21030ed3dc39a41c7b524dbc1a318b518bd (diff) |
xtensa: s6000 dma engine support
There are four slightly different dma engines on the s6000 family.
One for memory-memory transfers, the other three for memory-device.
This patch implements a platform-specific kernel-API to control these
engines. It is needed for the network, video, audio peripherals on
s6000.
Signed-off-by: Oskar Schirmer <os@emlix.com>
Signed-off-by: Daniel Glockner <dg@emlix.com>
Signed-off-by: Fabian Godehardt <fg@emlix.com>
Cc: Daniel Glockner <dg@emlix.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Chris Zankel <chris@zankel.net>
Diffstat (limited to 'arch/xtensa/variants/s6000/dmac.c')
-rw-r--r-- | arch/xtensa/variants/s6000/dmac.c | 173 |
1 files changed, 173 insertions, 0 deletions
diff --git a/arch/xtensa/variants/s6000/dmac.c b/arch/xtensa/variants/s6000/dmac.c new file mode 100644 index 000000000000..dc7f7c573518 --- /dev/null +++ b/arch/xtensa/variants/s6000/dmac.c | |||
@@ -0,0 +1,173 @@ | |||
1 | /* | ||
2 | * Authors: Oskar Schirmer <os@emlix.com> | ||
3 | * Daniel Gloeckner <dg@emlix.com> | ||
4 | * (c) 2008 emlix GmbH http://www.emlix.com | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify it | ||
7 | * under the terms of the GNU General Public License as published by the | ||
8 | * Free Software Foundation; either version 2 of the License, or (at your | ||
9 | * option) any later version. | ||
10 | */ | ||
11 | |||
12 | #include <linux/kernel.h> | ||
13 | #include <linux/io.h> | ||
14 | #include <linux/types.h> | ||
15 | #include <linux/errno.h> | ||
16 | #include <linux/spinlock.h> | ||
17 | #include <asm/cacheflush.h> | ||
18 | #include <variant/dmac.h> | ||
19 | |||
20 | /* DMA engine lookup */ | ||
21 | |||
22 | struct s6dmac_ctrl s6dmac_ctrl[S6_DMAC_NB]; | ||
23 | |||
24 | |||
25 | /* DMA control, per engine */ | ||
26 | |||
27 | void s6dmac_put_fifo_cache(u32 dmac, int chan, u32 src, u32 dst, u32 size) | ||
28 | { | ||
29 | if (xtensa_need_flush_dma_source(src)) { | ||
30 | u32 base = src; | ||
31 | u32 span = size; | ||
32 | u32 chunk = readl(DMA_CHNL(dmac, chan) + S6_DMA_CMONCHUNK); | ||
33 | if (chunk && (size > chunk)) { | ||
34 | s32 skip = | ||
35 | readl(DMA_CHNL(dmac, chan) + S6_DMA_SRCSKIP); | ||
36 | u32 gaps = (size+chunk-1)/chunk - 1; | ||
37 | if (skip >= 0) { | ||
38 | span += gaps * skip; | ||
39 | } else if (-skip > chunk) { | ||
40 | s32 decr = gaps * (chunk + skip); | ||
41 | base += decr; | ||
42 | span = chunk - decr; | ||
43 | } else { | ||
44 | span = max(span + gaps * skip, | ||
45 | (chunk + skip) * gaps - skip); | ||
46 | } | ||
47 | } | ||
48 | flush_dcache_unaligned(base, span); | ||
49 | } | ||
50 | if (xtensa_need_invalidate_dma_destination(dst)) { | ||
51 | u32 base = dst; | ||
52 | u32 span = size; | ||
53 | u32 chunk = readl(DMA_CHNL(dmac, chan) + S6_DMA_CMONCHUNK); | ||
54 | if (chunk && (size > chunk)) { | ||
55 | s32 skip = | ||
56 | readl(DMA_CHNL(dmac, chan) + S6_DMA_DSTSKIP); | ||
57 | u32 gaps = (size+chunk-1)/chunk - 1; | ||
58 | if (skip >= 0) { | ||
59 | span += gaps * skip; | ||
60 | } else if (-skip > chunk) { | ||
61 | s32 decr = gaps * (chunk + skip); | ||
62 | base += decr; | ||
63 | span = chunk - decr; | ||
64 | } else { | ||
65 | span = max(span + gaps * skip, | ||
66 | (chunk + skip) * gaps - skip); | ||
67 | } | ||
68 | } | ||
69 | invalidate_dcache_unaligned(base, span); | ||
70 | } | ||
71 | s6dmac_put_fifo(dmac, chan, src, dst, size); | ||
72 | } | ||
73 | |||
74 | void s6dmac_disable_error_irqs(u32 dmac, u32 mask) | ||
75 | { | ||
76 | unsigned long flags; | ||
77 | spinlock_t *spinl = &s6dmac_ctrl[_dmac_addr_index(dmac)].lock; | ||
78 | spin_lock_irqsave(spinl, flags); | ||
79 | _s6dmac_disable_error_irqs(dmac, mask); | ||
80 | spin_unlock_irqrestore(spinl, flags); | ||
81 | } | ||
82 | |||
83 | u32 s6dmac_int_sources(u32 dmac, u32 channel) | ||
84 | { | ||
85 | u32 mask, ret, tmp; | ||
86 | mask = 1 << channel; | ||
87 | |||
88 | tmp = readl(dmac + S6_DMA_TERMCNTIRQSTAT); | ||
89 | tmp &= mask; | ||
90 | writel(tmp, dmac + S6_DMA_TERMCNTIRQCLR); | ||
91 | ret = tmp >> channel; | ||
92 | |||
93 | tmp = readl(dmac + S6_DMA_PENDCNTIRQSTAT); | ||
94 | tmp &= mask; | ||
95 | writel(tmp, dmac + S6_DMA_PENDCNTIRQCLR); | ||
96 | ret |= (tmp >> channel) << 1; | ||
97 | |||
98 | tmp = readl(dmac + S6_DMA_LOWWMRKIRQSTAT); | ||
99 | tmp &= mask; | ||
100 | writel(tmp, dmac + S6_DMA_LOWWMRKIRQCLR); | ||
101 | ret |= (tmp >> channel) << 2; | ||
102 | |||
103 | tmp = readl(dmac + S6_DMA_INTRAW0); | ||
104 | tmp &= (mask << S6_DMA_INT0_OVER) | (mask << S6_DMA_INT0_UNDER); | ||
105 | writel(tmp, dmac + S6_DMA_INTCLEAR0); | ||
106 | |||
107 | if (tmp & (mask << S6_DMA_INT0_UNDER)) | ||
108 | ret |= 1 << 3; | ||
109 | if (tmp & (mask << S6_DMA_INT0_OVER)) | ||
110 | ret |= 1 << 4; | ||
111 | |||
112 | tmp = readl(dmac + S6_DMA_MASTERERRINFO); | ||
113 | mask <<= S6_DMA_INT1_CHANNEL; | ||
114 | if (((tmp >> S6_DMA_MASTERERR_CHAN(0)) & S6_DMA_MASTERERR_CHAN_MASK) | ||
115 | == channel) | ||
116 | mask |= 1 << S6_DMA_INT1_MASTER; | ||
117 | if (((tmp >> S6_DMA_MASTERERR_CHAN(1)) & S6_DMA_MASTERERR_CHAN_MASK) | ||
118 | == channel) | ||
119 | mask |= 1 << (S6_DMA_INT1_MASTER + 1); | ||
120 | if (((tmp >> S6_DMA_MASTERERR_CHAN(2)) & S6_DMA_MASTERERR_CHAN_MASK) | ||
121 | == channel) | ||
122 | mask |= 1 << (S6_DMA_INT1_MASTER + 2); | ||
123 | |||
124 | tmp = readl(dmac + S6_DMA_INTRAW1) & mask; | ||
125 | writel(tmp, dmac + S6_DMA_INTCLEAR1); | ||
126 | ret |= ((tmp >> channel) & 1) << 5; | ||
127 | ret |= ((tmp >> S6_DMA_INT1_MASTER) & S6_DMA_INT1_MASTER_MASK) << 6; | ||
128 | |||
129 | return ret; | ||
130 | } | ||
131 | |||
132 | void s6dmac_release_chan(u32 dmac, int chan) | ||
133 | { | ||
134 | if (chan >= 0) | ||
135 | s6dmac_disable_chan(dmac, chan); | ||
136 | } | ||
137 | |||
138 | |||
139 | /* global init */ | ||
140 | |||
141 | static inline void __init dmac_init(u32 dmac, u8 chan_nb) | ||
142 | { | ||
143 | s6dmac_ctrl[S6_DMAC_INDEX(dmac)].dmac = dmac; | ||
144 | spin_lock_init(&s6dmac_ctrl[S6_DMAC_INDEX(dmac)].lock); | ||
145 | s6dmac_ctrl[S6_DMAC_INDEX(dmac)].chan_nb = chan_nb; | ||
146 | writel(S6_DMA_INT1_MASTER_MASK << S6_DMA_INT1_MASTER, | ||
147 | dmac + S6_DMA_INTCLEAR1); | ||
148 | } | ||
149 | |||
150 | static inline void __init dmac_master(u32 dmac, | ||
151 | u32 m0start, u32 m0end, u32 m1start, u32 m1end) | ||
152 | { | ||
153 | writel(m0start, dmac + S6_DMA_MASTER0START); | ||
154 | writel(m0end - 1, dmac + S6_DMA_MASTER0END); | ||
155 | writel(m1start, dmac + S6_DMA_MASTER1START); | ||
156 | writel(m1end - 1, dmac + S6_DMA_MASTER1END); | ||
157 | } | ||
158 | |||
159 | static void __init s6_dmac_init(void) | ||
160 | { | ||
161 | dmac_init(S6_REG_LMSDMA, S6_LMSDMA_NB); | ||
162 | dmac_master(S6_REG_LMSDMA, | ||
163 | S6_MEM_DDR, S6_MEM_PCIE_APER, S6_MEM_EFI, S6_MEM_GMAC); | ||
164 | dmac_init(S6_REG_NIDMA, S6_NIDMA_NB); | ||
165 | dmac_init(S6_REG_DPDMA, S6_DPDMA_NB); | ||
166 | dmac_master(S6_REG_DPDMA, | ||
167 | S6_MEM_DDR, S6_MEM_PCIE_APER, S6_REG_DP, S6_REG_DPDMA); | ||
168 | dmac_init(S6_REG_HIFDMA, S6_HIFDMA_NB); | ||
169 | dmac_master(S6_REG_HIFDMA, | ||
170 | S6_MEM_GMAC, S6_MEM_PCIE_CFG, S6_MEM_PCIE_APER, S6_MEM_AUX); | ||
171 | } | ||
172 | |||
173 | arch_initcall(s6_dmac_init); | ||