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 	static const 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