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 
s6dmac_put_fifo_cache(u32 dmac,int chan,u32 src,u32 dst,u32 size)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 
s6dmac_disable_error_irqs(u32 dmac,u32 mask)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 
s6dmac_int_sources(u32 dmac,u32 channel)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 
s6dmac_release_chan(u32 dmac,int chan)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 
dmac_init(u32 dmac,u8 chan_nb)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 
dmac_master(u32 dmac,u32 m0start,u32 m0end,u32 m1start,u32 m1end)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 
s6_dmac_init(void)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);
174