FreeBSD/Linux Kernel Cross Reference
sys/bitsy/sa1110dma.c
1 #include "u.h"
2 #include "../port/lib.h"
3 #include "mem.h"
4 #include "dat.h"
5 #include "fns.h"
6 #include "io.h"
7 #include "../port/error.h"
8 #include "sa1110dma.h"
9
10 static int debug = 0;
11
12 /*
13 * DMA helper routines
14 */
15
16 enum {
17 NDMA = 6, /* Number of DMA channels */
18 DMAREGS = 0xb0000000, /* DMA registers, physical */
19 };
20
21 enum {
22 /* Device Address Register, DDAR */
23 RW = 0,
24 E = 1,
25 BS = 2,
26 DW = 3,
27 DS = 4, /* bits 4 - 7 */
28 DA = 8 /* bits 8 - 31 */
29 };
30
31 enum {
32 /* Device Control & Status Register, DCSR */
33 RUN = 0,
34 IE = 1,
35 ERROR = 2,
36 DONEA = 3,
37 STRTA = 4,
38 DONEB = 5,
39 STRTB = 6,
40 BIU = 7
41 };
42
43 typedef struct DMAchan {
44 int allocated;
45 Rendez r;
46 void (*intr)(void*, ulong);
47 void *param;
48 } DMAchan;
49
50 struct {
51 Lock;
52 DMAchan chan[6];
53 } dma;
54
55 struct dmaregs {
56 ulong ddar;
57 ulong dcsr_set;
58 ulong dcsr_clr;
59 ulong dcsr_rd;
60 ulong dstrtA;
61 ulong dxcntA;
62 ulong dstrtB;
63 ulong dxcntB;
64 } *dmaregs;
65
66 static void dmaintr(Ureg*, void *);
67
68 void
69 dmainit(void) {
70 int i;
71
72 /* map the lcd regs into the kernel's virtual space */
73 dmaregs = (struct dmaregs*)mapspecial(DMAREGS, NDMA*sizeof(struct dmaregs));
74 if (debug) print("dma: dmaalloc registers 0x%ux mapped at 0x%p\n",
75 DMAREGS, dmaregs);
76 for (i = 0; i < NDMA; i++) {
77 intrenable(IRQ, IRQdma0+i, dmaintr, &dmaregs[i], "DMA");
78 }
79 }
80
81 void
82 dmareset(int i, int rd, int bigendian, int burstsize, int datumsize, int device, ulong port) {
83 ulong ddar;
84
85 ddar =
86 (rd?1:0)<<RW |
87 (bigendian?1:0)<<E |
88 ((burstsize==8)?1:0)<<BS |
89 ((datumsize==2)?1:0)<<DW |
90 device<<DS |
91 0x80000000 | ((ulong)port << 6);
92 dmaregs[i].ddar = ddar;
93 dmaregs[i].dcsr_clr = 0xff;
94 if (debug) print("dma: dmareset: 0x%lux\n", ddar);
95 }
96
97 int
98 dmaalloc(int rd, int bigendian, int burstsize, int datumsize, int device, ulong port, void (*intr)(void*, ulong), void *param) {
99 int i;
100
101 lock(&dma);
102 for (i = 0; i < NDMA; i++) {
103 if (dma.chan[i].allocated)
104 continue;
105 dma.chan[i].allocated++;
106 unlock(&dma);
107 dmareset(i, rd, bigendian, burstsize, datumsize, device, port);
108 dma.chan[i].intr = intr;
109 dma.chan[i].param = param;
110 return i;
111 }
112 unlock(&dma);
113 return -1;
114 }
115
116 void
117 dmafree(int i) {
118 dmaregs[i].dcsr_clr = 0xff;
119 dmaregs[i].ddar = 0;
120 dma.chan[i].allocated = 0;
121 dma.chan[i].intr = nil;
122 }
123
124 void
125 dmastop(int i) {
126 dmaregs[i].dcsr_clr = 0xff;
127 }
128
129 ulong
130 dmastart(int chan, ulong addr, int count) {
131 ulong status, n;
132 static int last;
133
134 /* If this gets called from interrupt routines, make sure ilocks are used */
135 status = dmaregs[chan].dcsr_rd;
136 if (debug > 1)
137 iprint("dma: dmastart 0x%lux\n", status);
138
139 if ((status & (1<<STRTA|1<<STRTB|1<<RUN)) == (1<<STRTA|1<<STRTB|1<<RUN)) {
140 return 0;
141 }
142 cachewbregion(addr, count);
143 n = 0x100;
144 if ((status & (1<<BIU | 1<<STRTB)) == (1<<BIU | 1<<STRTB) ||
145 (status & (1<<BIU | 1<<STRTA)) == 0) {
146 if (status & 1<<STRTA)
147 iprint("writing busy dma entry 0x%lux\n", status);
148 if (status & 1<<STRTB)
149 n = (last == 1)?0x200:0x300;
150 last = 2;
151 dmaregs[chan].dstrtA = addr;
152 dmaregs[chan].dxcntA = count;
153 dmaregs[chan].dcsr_set = 1<<RUN | 1<<IE | 1<<STRTA;
154 n |= 1<<DONEA;
155 } else {
156 if (status & 1<<STRTB)
157 iprint("writing busy dma entry 0x%lux\n", status);
158 if (status & 1<<STRTA)
159 n = (last == 2)?0x200:0x300;
160 last = 1;
161 dmaregs[chan].dstrtB = addr;
162 dmaregs[chan].dxcntB = count;
163 dmaregs[chan].dcsr_set = 1<<RUN | 1<<IE | 1<<STRTB;
164 n |= 1<<DONEB;
165 }
166 return n;
167 }
168
169 int
170 dmaidle(int chan) {
171 ulong status;
172
173 status = dmaregs[chan].dcsr_rd;
174 if (debug > 1) print("dmaidle: 0x%lux\n", status);
175 return (status & (1<<STRTA|1<<STRTB)) == 0;
176 }
177
178 static int
179 _dmaidle(void* chan) {
180 ulong status;
181
182 status = dmaregs[(int)chan].dcsr_rd;
183 return (status & (1<<STRTA|1<<STRTB)) == 0;
184 }
185
186 void
187 dmawait(int chan) {
188 while (!dmaidle(chan))
189 sleep(&dma.chan[chan].r, _dmaidle, (void*)chan);
190 }
191
192 /*
193 * interrupt routine
194 */
195 static void
196 dmaintr(Ureg*, void *x)
197 {
198 int i;
199 struct dmaregs *regs = x;
200 ulong dcsr, donebit;
201
202 i = regs - dmaregs;
203 dcsr = regs->dcsr_rd;
204 if (debug > 1)
205 iprint("dma: interrupt channel %d, status 0x%lux\n", i, dcsr);
206 if (dcsr & 1<<ERROR)
207 iprint("error, channel %d, status 0x%lux\n", i, dcsr);
208 donebit = 1<<((dcsr&1<<BIU)?DONEA:DONEB);
209 if (dcsr & donebit) {
210 regs->dcsr_clr = 1<<DONEA|1<<DONEB;
211 if (dma.chan[i].intr) {
212 (*dma.chan[i].intr)(dma.chan[i].param, dcsr & (1<<DONEA|1<<DONEB));
213 }
214 wakeup(&dma.chan[i].r);
215 return;
216 }
217 if (dcsr & 1<<ERROR) {
218 regs->dcsr_clr = ERROR;
219 iprint("DMA error, channel %d, status 0x%lux\n", i, dcsr);
220 if (dma.chan[i].intr) {
221 (*dma.chan[i].intr)(dma.chan[i].param, 0);
222 }
223 wakeup(&dma.chan[i].r);
224 return;
225 }
226 iprint("spurious DMA interrupt, channel %d, status 0x%lux\n", i, dcsr);
227 }
Cache object: 96dabf9c3461d7290f970d87ce1598f0
|