Linux Kernel  3.7.1
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
dmac.c
Go to the documentation of this file.
1 /*
2  * Authors: Oskar Schirmer <[email protected]>
3  * Daniel Gloeckner <[email protected]>
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 
23 
24 
25 /* DMA control, per engine */
26 
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 
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 
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;
115  == channel)
116  mask |= 1 << S6_DMA_INT1_MASTER;
118  == channel)
119  mask |= 1 << (S6_DMA_INT1_MASTER + 1);
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 
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 {
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,
164  dmac_init(S6_REG_NIDMA, S6_NIDMA_NB);
165  dmac_init(S6_REG_DPDMA, S6_DPDMA_NB);
166  dmac_master(S6_REG_DPDMA,
168  dmac_init(S6_REG_HIFDMA, S6_HIFDMA_NB);
169  dmac_master(S6_REG_HIFDMA,
171 }
172 
173 arch_initcall(s6_dmac_init);