xtensa: s6000 dma engine support
[pandora-kernel.git] / arch / xtensa / variants / s6000 / dmac.c
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);