1 /*
2 * include/asm-xtensa/variant-s6000/dmac.h
3 *
4 * This file is subject to the terms and conditions of the GNU General Public
5 * License. See the file "COPYING" in the main directory of this archive
6 * for more details.
7 *
8 * Copyright (C) 2006 Tensilica Inc.
9 * Copyright (C) 2008 Emlix GmbH <info@emlix.com>
10 * Authors: Fabian Godehardt <fg@emlix.com>
11 * Oskar Schirmer <os@emlix.com>
12 * Daniel Gloeckner <dg@emlix.com>
13 */
14
15 #ifndef __ASM_XTENSA_S6000_DMAC_H
16 #define __ASM_XTENSA_S6000_DMAC_H
17 #include <linux/io.h>
18 #include <variant/hardware.h>
19
20 /* DMA global */
21
22 #define S6_DMA_INTSTAT0 0x000
23 #define S6_DMA_INTSTAT1 0x004
24 #define S6_DMA_INTENABLE0 0x008
25 #define S6_DMA_INTENABLE1 0x00C
26 #define S6_DMA_INTRAW0 0x010
27 #define S6_DMA_INTRAW1 0x014
28 #define S6_DMA_INTCLEAR0 0x018
29 #define S6_DMA_INTCLEAR1 0x01C
30 #define S6_DMA_INTSET0 0x020
31 #define S6_DMA_INTSET1 0x024
32 #define S6_DMA_INT0_UNDER 0
33 #define S6_DMA_INT0_OVER 16
34 #define S6_DMA_INT1_CHANNEL 0
35 #define S6_DMA_INT1_MASTER 16
36 #define S6_DMA_INT1_MASTER_MASK 7
37 #define S6_DMA_TERMCNTIRQSTAT 0x028
38 #define S6_DMA_TERMCNTIRQCLR 0x02C
39 #define S6_DMA_TERMCNTIRQSET 0x030
40 #define S6_DMA_PENDCNTIRQSTAT 0x034
41 #define S6_DMA_PENDCNTIRQCLR 0x038
42 #define S6_DMA_PENDCNTIRQSET 0x03C
43 #define S6_DMA_LOWWMRKIRQSTAT 0x040
44 #define S6_DMA_LOWWMRKIRQCLR 0x044
45 #define S6_DMA_LOWWMRKIRQSET 0x048
46 #define S6_DMA_MASTERERRINFO 0x04C
47 #define S6_DMA_MASTERERR_CHAN(n) (4*(n))
48 #define S6_DMA_MASTERERR_CHAN_MASK 0xF
49 #define S6_DMA_DESCRFIFO0 0x050
50 #define S6_DMA_DESCRFIFO1 0x054
51 #define S6_DMA_DESCRFIFO2 0x058
52 #define S6_DMA_DESCRFIFO2_AUTODISABLE 24
53 #define S6_DMA_DESCRFIFO3 0x05C
54 #define S6_DMA_MASTER0START 0x060
55 #define S6_DMA_MASTER0END 0x064
56 #define S6_DMA_MASTER1START 0x068
57 #define S6_DMA_MASTER1END 0x06C
58 #define S6_DMA_NEXTFREE 0x070
59 #define S6_DMA_NEXTFREE_CHAN 0
60 #define S6_DMA_NEXTFREE_CHAN_MASK 0x1F
61 #define S6_DMA_NEXTFREE_ENA 16
62 #define S6_DMA_NEXTFREE_ENA_MASK ((1 << 16) - 1)
63 #define S6_DMA_DPORTCTRLGRP(p) ((p) * 4 + 0x074)
64 #define S6_DMA_DPORTCTRLGRP_FRAMEREP 0
65 #define S6_DMA_DPORTCTRLGRP_NRCHANS 1
66 #define S6_DMA_DPORTCTRLGRP_NRCHANS_1 0
67 #define S6_DMA_DPORTCTRLGRP_NRCHANS_3 1
68 #define S6_DMA_DPORTCTRLGRP_NRCHANS_4 2
69 #define S6_DMA_DPORTCTRLGRP_NRCHANS_2 3
70 #define S6_DMA_DPORTCTRLGRP_ENA 31
71
72
73 /* DMA per channel */
74
75 #define DMA_CHNL(dmac, n) ((dmac) + 0x1000 + (n) * 0x100)
76 #define DMA_INDEX_CHNL(addr) (((addr) >> 8) & 0xF)
77 #define DMA_MASK_DMAC(addr) ((addr) & 0xFFFF0000)
78 #define S6_DMA_CHNCTRL 0x000
79 #define S6_DMA_CHNCTRL_ENABLE 0
80 #define S6_DMA_CHNCTRL_PAUSE 1
81 #define S6_DMA_CHNCTRL_PRIO 2
82 #define S6_DMA_CHNCTRL_PRIO_MASK 3
83 #define S6_DMA_CHNCTRL_PERIPHXFER 4
84 #define S6_DMA_CHNCTRL_PERIPHENA 5
85 #define S6_DMA_CHNCTRL_SRCINC 6
86 #define S6_DMA_CHNCTRL_DSTINC 7
87 #define S6_DMA_CHNCTRL_BURSTLOG 8
88 #define S6_DMA_CHNCTRL_BURSTLOG_MASK 7
89 #define S6_DMA_CHNCTRL_DESCFIFODEPTH 12
90 #define S6_DMA_CHNCTRL_DESCFIFODEPTH_MASK 0x1F
91 #define S6_DMA_CHNCTRL_DESCFIFOFULL 17
92 #define S6_DMA_CHNCTRL_BWCONSEL 18
93 #define S6_DMA_CHNCTRL_BWCONENA 19
94 #define S6_DMA_CHNCTRL_PENDGCNTSTAT 20
95 #define S6_DMA_CHNCTRL_PENDGCNTSTAT_MASK 0x3F
96 #define S6_DMA_CHNCTRL_LOWWMARK 26
97 #define S6_DMA_CHNCTRL_LOWWMARK_MASK 0xF
98 #define S6_DMA_CHNCTRL_TSTAMP 30
99 #define S6_DMA_TERMCNTNB 0x004
100 #define S6_DMA_TERMCNTNB_MASK 0xFFFF
101 #define S6_DMA_TERMCNTTMO 0x008
102 #define S6_DMA_TERMCNTSTAT 0x00C
103 #define S6_DMA_TERMCNTSTAT_MASK 0xFF
104 #define S6_DMA_CMONCHUNK 0x010
105 #define S6_DMA_SRCSKIP 0x014
106 #define S6_DMA_DSTSKIP 0x018
107 #define S6_DMA_CUR_SRC 0x024
108 #define S6_DMA_CUR_DST 0x028
109 #define S6_DMA_TIMESTAMP 0x030
110
111 /* DMA channel lists */
112
113 #define S6_DPDMA_CHAN(stream, channel) (4 * (stream) + (channel))
114 #define S6_DPDMA_NB 16
115
116 #define S6_HIFDMA_GMACTX 0
117 #define S6_HIFDMA_GMACRX 1
118 #define S6_HIFDMA_I2S0 2
119 #define S6_HIFDMA_I2S1 3
120 #define S6_HIFDMA_EGIB 4
121 #define S6_HIFDMA_PCITX 5
122 #define S6_HIFDMA_PCIRX 6
123 #define S6_HIFDMA_NB 7
124
125 #define S6_NIDMA_NB 4
126
127 #define S6_LMSDMA_NB 12
128
129 /* controller access */
130
131 #define S6_DMAC_NB 4
132 #define S6_DMAC_INDEX(dmac) (((unsigned)(dmac) >> 18) % S6_DMAC_NB)
133
134 struct s6dmac_ctrl {
135 u32 dmac;
136 spinlock_t lock;
137 u8 chan_nb;
138 };
139
140 extern struct s6dmac_ctrl s6dmac_ctrl[S6_DMAC_NB];
141
142
143 /* DMA control, per channel */
144
s6dmac_fifo_full(u32 dmac,int chan)145 static inline int s6dmac_fifo_full(u32 dmac, int chan)
146 {
147 return (readl(DMA_CHNL(dmac, chan) + S6_DMA_CHNCTRL)
148 & (1 << S6_DMA_CHNCTRL_DESCFIFOFULL)) && 1;
149 }
150
s6dmac_termcnt_irq(u32 dmac,int chan)151 static inline int s6dmac_termcnt_irq(u32 dmac, int chan)
152 {
153 u32 m = 1 << chan;
154 int r = (readl(dmac + S6_DMA_TERMCNTIRQSTAT) & m) && 1;
155 if (r)
156 writel(m, dmac + S6_DMA_TERMCNTIRQCLR);
157 return r;
158 }
159
s6dmac_pendcnt_irq(u32 dmac,int chan)160 static inline int s6dmac_pendcnt_irq(u32 dmac, int chan)
161 {
162 u32 m = 1 << chan;
163 int r = (readl(dmac + S6_DMA_PENDCNTIRQSTAT) & m) && 1;
164 if (r)
165 writel(m, dmac + S6_DMA_PENDCNTIRQCLR);
166 return r;
167 }
168
s6dmac_lowwmark_irq(u32 dmac,int chan)169 static inline int s6dmac_lowwmark_irq(u32 dmac, int chan)
170 {
171 int r = (readl(dmac + S6_DMA_LOWWMRKIRQSTAT) & (1 << chan)) ? 1 : 0;
172 if (r)
173 writel(1 << chan, dmac + S6_DMA_LOWWMRKIRQCLR);
174 return r;
175 }
176
s6dmac_pending_count(u32 dmac,int chan)177 static inline u32 s6dmac_pending_count(u32 dmac, int chan)
178 {
179 return (readl(DMA_CHNL(dmac, chan) + S6_DMA_CHNCTRL)
180 >> S6_DMA_CHNCTRL_PENDGCNTSTAT)
181 & S6_DMA_CHNCTRL_PENDGCNTSTAT_MASK;
182 }
183
s6dmac_set_terminal_count(u32 dmac,int chan,u32 n)184 static inline void s6dmac_set_terminal_count(u32 dmac, int chan, u32 n)
185 {
186 n &= S6_DMA_TERMCNTNB_MASK;
187 n |= readl(DMA_CHNL(dmac, chan) + S6_DMA_TERMCNTNB)
188 & ~S6_DMA_TERMCNTNB_MASK;
189 writel(n, DMA_CHNL(dmac, chan) + S6_DMA_TERMCNTNB);
190 }
191
s6dmac_get_terminal_count(u32 dmac,int chan)192 static inline u32 s6dmac_get_terminal_count(u32 dmac, int chan)
193 {
194 return (readl(DMA_CHNL(dmac, chan) + S6_DMA_TERMCNTNB))
195 & S6_DMA_TERMCNTNB_MASK;
196 }
197
s6dmac_timestamp(u32 dmac,int chan)198 static inline u32 s6dmac_timestamp(u32 dmac, int chan)
199 {
200 return readl(DMA_CHNL(dmac, chan) + S6_DMA_TIMESTAMP);
201 }
202
s6dmac_cur_src(u32 dmac,int chan)203 static inline u32 s6dmac_cur_src(u32 dmac, int chan)
204 {
205 return readl(DMA_CHNL(dmac, chan) + S6_DMA_CUR_SRC);
206 }
207
s6dmac_cur_dst(u32 dmac,int chan)208 static inline u32 s6dmac_cur_dst(u32 dmac, int chan)
209 {
210 return readl(DMA_CHNL(dmac, chan) + S6_DMA_CUR_DST);
211 }
212
s6dmac_disable_chan(u32 dmac,int chan)213 static inline void s6dmac_disable_chan(u32 dmac, int chan)
214 {
215 u32 ctrl;
216 writel(readl(DMA_CHNL(dmac, chan) + S6_DMA_CHNCTRL)
217 & ~(1 << S6_DMA_CHNCTRL_ENABLE),
218 DMA_CHNL(dmac, chan) + S6_DMA_CHNCTRL);
219 do
220 ctrl = readl(DMA_CHNL(dmac, chan) + S6_DMA_CHNCTRL);
221 while (ctrl & (1 << S6_DMA_CHNCTRL_ENABLE));
222 }
223
s6dmac_set_stride_skip(u32 dmac,int chan,int comchunk,int srcskip,int dstskip)224 static inline void s6dmac_set_stride_skip(u32 dmac, int chan,
225 int comchunk, /* 0: disable scatter/gather */
226 int srcskip, int dstskip)
227 {
228 writel(comchunk, DMA_CHNL(dmac, chan) + S6_DMA_CMONCHUNK);
229 writel(srcskip, DMA_CHNL(dmac, chan) + S6_DMA_SRCSKIP);
230 writel(dstskip, DMA_CHNL(dmac, chan) + S6_DMA_DSTSKIP);
231 }
232
s6dmac_enable_chan(u32 dmac,int chan,int prio,int periphxfer,int srcinc,int dstinc,int comchunk,int srcskip,int dstskip,int burstsize,int bandwidthconserve,int lowwmark,int timestamp,int enable)233 static inline void s6dmac_enable_chan(u32 dmac, int chan,
234 int prio, /* 0 (highest) .. 3 (lowest) */
235 int periphxfer, /* <0: disable p.req.line, 0..1: mode */
236 int srcinc, int dstinc, /* 0: dont increment src/dst address */
237 int comchunk, /* 0: disable scatter/gather */
238 int srcskip, int dstskip,
239 int burstsize, /* 4 for I2S, 7 for everything else */
240 int bandwidthconserve, /* <0: disable, 0..1: select */
241 int lowwmark, /* 0..15 */
242 int timestamp, /* 0: disable timestamp */
243 int enable) /* 0: disable for now */
244 {
245 writel(1, DMA_CHNL(dmac, chan) + S6_DMA_TERMCNTNB);
246 writel(0, DMA_CHNL(dmac, chan) + S6_DMA_TERMCNTTMO);
247 writel(lowwmark << S6_DMA_CHNCTRL_LOWWMARK,
248 DMA_CHNL(dmac, chan) + S6_DMA_CHNCTRL);
249 s6dmac_set_stride_skip(dmac, chan, comchunk, srcskip, dstskip);
250 writel(((enable ? 1 : 0) << S6_DMA_CHNCTRL_ENABLE) |
251 (prio << S6_DMA_CHNCTRL_PRIO) |
252 (((periphxfer > 0) ? 1 : 0) << S6_DMA_CHNCTRL_PERIPHXFER) |
253 (((periphxfer < 0) ? 0 : 1) << S6_DMA_CHNCTRL_PERIPHENA) |
254 ((srcinc ? 1 : 0) << S6_DMA_CHNCTRL_SRCINC) |
255 ((dstinc ? 1 : 0) << S6_DMA_CHNCTRL_DSTINC) |
256 (burstsize << S6_DMA_CHNCTRL_BURSTLOG) |
257 (((bandwidthconserve > 0) ? 1 : 0) << S6_DMA_CHNCTRL_BWCONSEL) |
258 (((bandwidthconserve < 0) ? 0 : 1) << S6_DMA_CHNCTRL_BWCONENA) |
259 (lowwmark << S6_DMA_CHNCTRL_LOWWMARK) |
260 ((timestamp ? 1 : 0) << S6_DMA_CHNCTRL_TSTAMP),
261 DMA_CHNL(dmac, chan) + S6_DMA_CHNCTRL);
262 }
263
264
265 /* DMA control, per engine */
266
_dmac_addr_index(u32 dmac)267 static inline unsigned _dmac_addr_index(u32 dmac)
268 {
269 unsigned i = S6_DMAC_INDEX(dmac);
270 if (s6dmac_ctrl[i].dmac != dmac)
271 BUG();
272 return i;
273 }
274
_s6dmac_disable_error_irqs(u32 dmac,u32 mask)275 static inline void _s6dmac_disable_error_irqs(u32 dmac, u32 mask)
276 {
277 writel(mask, dmac + S6_DMA_TERMCNTIRQCLR);
278 writel(mask, dmac + S6_DMA_PENDCNTIRQCLR);
279 writel(mask, dmac + S6_DMA_LOWWMRKIRQCLR);
280 writel(readl(dmac + S6_DMA_INTENABLE0)
281 & ~((mask << S6_DMA_INT0_UNDER) | (mask << S6_DMA_INT0_OVER)),
282 dmac + S6_DMA_INTENABLE0);
283 writel(readl(dmac + S6_DMA_INTENABLE1) & ~(mask << S6_DMA_INT1_CHANNEL),
284 dmac + S6_DMA_INTENABLE1);
285 writel((mask << S6_DMA_INT0_UNDER) | (mask << S6_DMA_INT0_OVER),
286 dmac + S6_DMA_INTCLEAR0);
287 writel(mask << S6_DMA_INT1_CHANNEL, dmac + S6_DMA_INTCLEAR1);
288 }
289
290 /*
291 * request channel from specified engine
292 * with chan<0, accept any channel
293 * further parameters see s6dmac_enable_chan
294 * returns < 0 upon error, channel nb otherwise
295 */
s6dmac_request_chan(u32 dmac,int chan,int prio,int periphxfer,int srcinc,int dstinc,int comchunk,int srcskip,int dstskip,int burstsize,int bandwidthconserve,int lowwmark,int timestamp,int enable)296 static inline int s6dmac_request_chan(u32 dmac, int chan,
297 int prio,
298 int periphxfer,
299 int srcinc, int dstinc,
300 int comchunk,
301 int srcskip, int dstskip,
302 int burstsize,
303 int bandwidthconserve,
304 int lowwmark,
305 int timestamp,
306 int enable)
307 {
308 int r = chan;
309 unsigned long flags;
310 spinlock_t *spinl = &s6dmac_ctrl[_dmac_addr_index(dmac)].lock;
311 spin_lock_irqsave(spinl, flags);
312 if (r < 0) {
313 r = (readl(dmac + S6_DMA_NEXTFREE) >> S6_DMA_NEXTFREE_CHAN)
314 & S6_DMA_NEXTFREE_CHAN_MASK;
315 }
316 if (r >= s6dmac_ctrl[_dmac_addr_index(dmac)].chan_nb) {
317 if (chan < 0)
318 r = -EBUSY;
319 else
320 r = -ENXIO;
321 } else if (((readl(dmac + S6_DMA_NEXTFREE) >> S6_DMA_NEXTFREE_ENA)
322 >> r) & 1) {
323 r = -EBUSY;
324 } else {
325 s6dmac_enable_chan(dmac, r, prio, periphxfer,
326 srcinc, dstinc, comchunk, srcskip, dstskip, burstsize,
327 bandwidthconserve, lowwmark, timestamp, enable);
328 }
329 spin_unlock_irqrestore(spinl, flags);
330 return r;
331 }
332
s6dmac_put_fifo(u32 dmac,int chan,u32 src,u32 dst,u32 size)333 static inline void s6dmac_put_fifo(u32 dmac, int chan,
334 u32 src, u32 dst, u32 size)
335 {
336 unsigned long flags;
337 spinlock_t *spinl = &s6dmac_ctrl[_dmac_addr_index(dmac)].lock;
338 spin_lock_irqsave(spinl, flags);
339 writel(src, dmac + S6_DMA_DESCRFIFO0);
340 writel(dst, dmac + S6_DMA_DESCRFIFO1);
341 writel(size, dmac + S6_DMA_DESCRFIFO2);
342 writel(chan, dmac + S6_DMA_DESCRFIFO3);
343 spin_unlock_irqrestore(spinl, flags);
344 }
345
s6dmac_channel_enabled(u32 dmac,int chan)346 static inline u32 s6dmac_channel_enabled(u32 dmac, int chan)
347 {
348 return readl(DMA_CHNL(dmac, chan) + S6_DMA_CHNCTRL) &
349 (1 << S6_DMA_CHNCTRL_ENABLE);
350 }
351
352 /*
353 * group 1-4 data port channels
354 * with port=0..3, nrch=1-4 channels,
355 * frrep=0/1 (dis- or enable frame repeat)
356 */
s6dmac_dp_setup_group(u32 dmac,int port,int nrch,int frrep)357 static inline void s6dmac_dp_setup_group(u32 dmac, int port,
358 int nrch, int frrep)
359 {
360 const static u8 mask[4] = {0, 3, 1, 2};
361 BUG_ON(dmac != S6_REG_DPDMA);
362 if ((port < 0) || (port > 3) || (nrch < 1) || (nrch > 4))
363 return;
364 writel((mask[nrch - 1] << S6_DMA_DPORTCTRLGRP_NRCHANS)
365 | ((frrep ? 1 : 0) << S6_DMA_DPORTCTRLGRP_FRAMEREP),
366 dmac + S6_DMA_DPORTCTRLGRP(port));
367 }
368
s6dmac_dp_switch_group(u32 dmac,int port,int enable)369 static inline void s6dmac_dp_switch_group(u32 dmac, int port, int enable)
370 {
371 u32 tmp;
372 BUG_ON(dmac != S6_REG_DPDMA);
373 tmp = readl(dmac + S6_DMA_DPORTCTRLGRP(port));
374 if (enable)
375 tmp |= (1 << S6_DMA_DPORTCTRLGRP_ENA);
376 else
377 tmp &= ~(1 << S6_DMA_DPORTCTRLGRP_ENA);
378 writel(tmp, dmac + S6_DMA_DPORTCTRLGRP(port));
379 }
380
381 extern void s6dmac_put_fifo_cache(u32 dmac, int chan,
382 u32 src, u32 dst, u32 size);
383 extern void s6dmac_disable_error_irqs(u32 dmac, u32 mask);
384 extern u32 s6dmac_int_sources(u32 dmac, u32 channel);
385 extern void s6dmac_release_chan(u32 dmac, int chan);
386
387 #endif /* __ASM_XTENSA_S6000_DMAC_H */
388