1*5d9de2d3SDavid du Colombier /*
2*5d9de2d3SDavid du Colombier * bcm2835 dma controller
3*5d9de2d3SDavid du Colombier *
4*5d9de2d3SDavid du Colombier * simplest to use only channels 0-6
5*5d9de2d3SDavid du Colombier * channels 7-14 have reduced functionality
6*5d9de2d3SDavid du Colombier * channel 15 is at a weird address
7*5d9de2d3SDavid du Colombier * channels 0 and 15 have an "external 128 bit 8 word read FIFO"
8*5d9de2d3SDavid du Colombier * for memory to memory transfers
9*5d9de2d3SDavid du Colombier *
10*5d9de2d3SDavid du Colombier * Experiments show that only channels 2-5,11-12 work with mmc
11*5d9de2d3SDavid du Colombier */
12*5d9de2d3SDavid du Colombier
13*5d9de2d3SDavid du Colombier #include "u.h"
14*5d9de2d3SDavid du Colombier #include "../port/lib.h"
15*5d9de2d3SDavid du Colombier #include "../port/error.h"
16*5d9de2d3SDavid du Colombier #include "mem.h"
17*5d9de2d3SDavid du Colombier #include "dat.h"
18*5d9de2d3SDavid du Colombier #include "fns.h"
19*5d9de2d3SDavid du Colombier #include "io.h"
20*5d9de2d3SDavid du Colombier
21*5d9de2d3SDavid du Colombier #define DMAREGS (VIRTIO+0x7000)
22*5d9de2d3SDavid du Colombier
23*5d9de2d3SDavid du Colombier #define DBG if(Dbg)
24*5d9de2d3SDavid du Colombier
25*5d9de2d3SDavid du Colombier enum {
26*5d9de2d3SDavid du Colombier Nchan = 7, /* number of dma channels */
27*5d9de2d3SDavid du Colombier Regsize = 0x100, /* size of regs for each chan */
28*5d9de2d3SDavid du Colombier Cbalign = 32, /* control block byte alignment */
29*5d9de2d3SDavid du Colombier Dbg = 0,
30*5d9de2d3SDavid du Colombier
31*5d9de2d3SDavid du Colombier /* registers for each dma controller */
32*5d9de2d3SDavid du Colombier Cs = 0x00>>2,
33*5d9de2d3SDavid du Colombier Conblkad = 0x04>>2,
34*5d9de2d3SDavid du Colombier Ti = 0x08>>2,
35*5d9de2d3SDavid du Colombier Sourcead = 0x0c>>2,
36*5d9de2d3SDavid du Colombier Destad = 0x10>>2,
37*5d9de2d3SDavid du Colombier Txfrlen = 0x14>>2,
38*5d9de2d3SDavid du Colombier Stride = 0x18>>2,
39*5d9de2d3SDavid du Colombier Nextconbk = 0x1c>>2,
40*5d9de2d3SDavid du Colombier Debug = 0x20>>2,
41*5d9de2d3SDavid du Colombier
42*5d9de2d3SDavid du Colombier /* collective registers */
43*5d9de2d3SDavid du Colombier Intstatus = 0xfe0>>2,
44*5d9de2d3SDavid du Colombier Enable = 0xff0>>2,
45*5d9de2d3SDavid du Colombier
46*5d9de2d3SDavid du Colombier /* Cs */
47*5d9de2d3SDavid du Colombier Reset = 1<<31,
48*5d9de2d3SDavid du Colombier Abort = 1<<30,
49*5d9de2d3SDavid du Colombier Error = 1<<8,
50*5d9de2d3SDavid du Colombier Waitwrite = 1<<6,
51*5d9de2d3SDavid du Colombier Waitdreq = 1<<5,
52*5d9de2d3SDavid du Colombier Paused = 1<<4,
53*5d9de2d3SDavid du Colombier Dreq = 1<<3,
54*5d9de2d3SDavid du Colombier Int = 1<<2,
55*5d9de2d3SDavid du Colombier End = 1<<1,
56*5d9de2d3SDavid du Colombier Active = 1<<0,
57*5d9de2d3SDavid du Colombier
58*5d9de2d3SDavid du Colombier /* Ti */
59*5d9de2d3SDavid du Colombier Permapshift= 16,
60*5d9de2d3SDavid du Colombier Srcignore = 1<<11,
61*5d9de2d3SDavid du Colombier Srcdreq = 1<<10,
62*5d9de2d3SDavid du Colombier Srcwidth128 = 1<<9,
63*5d9de2d3SDavid du Colombier Srcinc = 1<<8,
64*5d9de2d3SDavid du Colombier Destignore = 1<<7,
65*5d9de2d3SDavid du Colombier Destdreq = 1<<6,
66*5d9de2d3SDavid du Colombier Destwidth128 = 1<<5,
67*5d9de2d3SDavid du Colombier Destinc = 1<<4,
68*5d9de2d3SDavid du Colombier Waitresp = 1<<3,
69*5d9de2d3SDavid du Colombier Tdmode = 1<<1,
70*5d9de2d3SDavid du Colombier Inten = 1<<0,
71*5d9de2d3SDavid du Colombier
72*5d9de2d3SDavid du Colombier /* Debug */
73*5d9de2d3SDavid du Colombier Lite = 1<<28,
74*5d9de2d3SDavid du Colombier Clrerrors = 7<<0,
75*5d9de2d3SDavid du Colombier };
76*5d9de2d3SDavid du Colombier
77*5d9de2d3SDavid du Colombier typedef struct Ctlr Ctlr;
78*5d9de2d3SDavid du Colombier typedef struct Cb Cb;
79*5d9de2d3SDavid du Colombier
80*5d9de2d3SDavid du Colombier struct Ctlr {
81*5d9de2d3SDavid du Colombier u32int *regs;
82*5d9de2d3SDavid du Colombier Cb *cb;
83*5d9de2d3SDavid du Colombier Rendez r;
84*5d9de2d3SDavid du Colombier int dmadone;
85*5d9de2d3SDavid du Colombier };
86*5d9de2d3SDavid du Colombier
87*5d9de2d3SDavid du Colombier struct Cb {
88*5d9de2d3SDavid du Colombier u32int ti;
89*5d9de2d3SDavid du Colombier u32int sourcead;
90*5d9de2d3SDavid du Colombier u32int destad;
91*5d9de2d3SDavid du Colombier u32int txfrlen;
92*5d9de2d3SDavid du Colombier u32int stride;
93*5d9de2d3SDavid du Colombier u32int nextconbk;
94*5d9de2d3SDavid du Colombier u32int reserved[2];
95*5d9de2d3SDavid du Colombier };
96*5d9de2d3SDavid du Colombier
97*5d9de2d3SDavid du Colombier static Ctlr dma[Nchan];
98*5d9de2d3SDavid du Colombier static u32int *dmaregs = (u32int*)DMAREGS;
99*5d9de2d3SDavid du Colombier
100*5d9de2d3SDavid du Colombier static void
dump(char * msg,uchar * p,int n)101*5d9de2d3SDavid du Colombier dump(char *msg, uchar *p, int n)
102*5d9de2d3SDavid du Colombier {
103*5d9de2d3SDavid du Colombier print("%s", msg);
104*5d9de2d3SDavid du Colombier while(n-- > 0)
105*5d9de2d3SDavid du Colombier print(" %2.2x", *p++);
106*5d9de2d3SDavid du Colombier print("\n");
107*5d9de2d3SDavid du Colombier }
108*5d9de2d3SDavid du Colombier
109*5d9de2d3SDavid du Colombier static void
dumpdregs(char * msg,u32int * r)110*5d9de2d3SDavid du Colombier dumpdregs(char *msg, u32int *r)
111*5d9de2d3SDavid du Colombier {
112*5d9de2d3SDavid du Colombier int i;
113*5d9de2d3SDavid du Colombier
114*5d9de2d3SDavid du Colombier print("%s: %#p =", msg, r);
115*5d9de2d3SDavid du Colombier for(i = 0; i < 9; i++)
116*5d9de2d3SDavid du Colombier print(" %8.8uX", r[i]);
117*5d9de2d3SDavid du Colombier print("\n");
118*5d9de2d3SDavid du Colombier }
119*5d9de2d3SDavid du Colombier
120*5d9de2d3SDavid du Colombier static int
dmadone(void * a)121*5d9de2d3SDavid du Colombier dmadone(void *a)
122*5d9de2d3SDavid du Colombier {
123*5d9de2d3SDavid du Colombier return ((Ctlr*)a)->dmadone;
124*5d9de2d3SDavid du Colombier }
125*5d9de2d3SDavid du Colombier
126*5d9de2d3SDavid du Colombier static void
dmainterrupt(Ureg *,void * a)127*5d9de2d3SDavid du Colombier dmainterrupt(Ureg*, void *a)
128*5d9de2d3SDavid du Colombier {
129*5d9de2d3SDavid du Colombier Ctlr *ctlr;
130*5d9de2d3SDavid du Colombier
131*5d9de2d3SDavid du Colombier ctlr = a;
132*5d9de2d3SDavid du Colombier ctlr->regs[Cs] = Int;
133*5d9de2d3SDavid du Colombier ctlr->dmadone = 1;
134*5d9de2d3SDavid du Colombier wakeup(&ctlr->r);
135*5d9de2d3SDavid du Colombier }
136*5d9de2d3SDavid du Colombier
137*5d9de2d3SDavid du Colombier void
dmastart(int chan,int dev,int dir,void * src,void * dst,int len)138*5d9de2d3SDavid du Colombier dmastart(int chan, int dev, int dir, void *src, void *dst, int len)
139*5d9de2d3SDavid du Colombier {
140*5d9de2d3SDavid du Colombier Ctlr *ctlr;
141*5d9de2d3SDavid du Colombier Cb *cb;
142*5d9de2d3SDavid du Colombier int ti;
143*5d9de2d3SDavid du Colombier
144*5d9de2d3SDavid du Colombier ctlr = &dma[chan];
145*5d9de2d3SDavid du Colombier if(ctlr->regs == nil){
146*5d9de2d3SDavid du Colombier ctlr->regs = (u32int*)(DMAREGS + chan*Regsize);
147*5d9de2d3SDavid du Colombier ctlr->cb = xspanalloc(sizeof(Cb), Cbalign, 0);
148*5d9de2d3SDavid du Colombier assert(ctlr->cb != nil);
149*5d9de2d3SDavid du Colombier dmaregs[Enable] |= 1<<chan;
150*5d9de2d3SDavid du Colombier ctlr->regs[Cs] = Reset;
151*5d9de2d3SDavid du Colombier while(ctlr->regs[Cs] & Reset)
152*5d9de2d3SDavid du Colombier ;
153*5d9de2d3SDavid du Colombier intrenable(IRQDMA(chan), dmainterrupt, ctlr, 0, "dma");
154*5d9de2d3SDavid du Colombier }
155*5d9de2d3SDavid du Colombier cb = ctlr->cb;
156*5d9de2d3SDavid du Colombier ti = 0;
157*5d9de2d3SDavid du Colombier switch(dir){
158*5d9de2d3SDavid du Colombier case DmaD2M:
159*5d9de2d3SDavid du Colombier cachedwbinvse(dst, len);
160*5d9de2d3SDavid du Colombier ti = Srcdreq | Destinc;
161*5d9de2d3SDavid du Colombier cb->sourcead = DMAIO(src);
162*5d9de2d3SDavid du Colombier cb->destad = DMAADDR(dst);
163*5d9de2d3SDavid du Colombier break;
164*5d9de2d3SDavid du Colombier case DmaM2D:
165*5d9de2d3SDavid du Colombier cachedwbse(src, len);
166*5d9de2d3SDavid du Colombier ti = Destdreq | Srcinc;
167*5d9de2d3SDavid du Colombier cb->sourcead = DMAADDR(src);
168*5d9de2d3SDavid du Colombier cb->destad = DMAIO(dst);
169*5d9de2d3SDavid du Colombier break;
170*5d9de2d3SDavid du Colombier case DmaM2M:
171*5d9de2d3SDavid du Colombier cachedwbse(src, len);
172*5d9de2d3SDavid du Colombier cachedwbinvse(dst, len);
173*5d9de2d3SDavid du Colombier ti = Srcinc | Destinc;
174*5d9de2d3SDavid du Colombier cb->sourcead = DMAADDR(src);
175*5d9de2d3SDavid du Colombier cb->destad = DMAADDR(dst);
176*5d9de2d3SDavid du Colombier break;
177*5d9de2d3SDavid du Colombier }
178*5d9de2d3SDavid du Colombier cb->ti = ti | dev<<Permapshift | Inten;
179*5d9de2d3SDavid du Colombier cb->txfrlen = len;
180*5d9de2d3SDavid du Colombier cb->stride = 0;
181*5d9de2d3SDavid du Colombier cb->nextconbk = 0;
182*5d9de2d3SDavid du Colombier cachedwbse(cb, sizeof(Cb));
183*5d9de2d3SDavid du Colombier ctlr->regs[Cs] = 0;
184*5d9de2d3SDavid du Colombier microdelay(1);
185*5d9de2d3SDavid du Colombier ctlr->regs[Conblkad] = DMAADDR(cb);
186*5d9de2d3SDavid du Colombier DBG print("dma start: %ux %ux %ux %ux %ux %ux\n",
187*5d9de2d3SDavid du Colombier cb->ti, cb->sourcead, cb->destad, cb->txfrlen,
188*5d9de2d3SDavid du Colombier cb->stride, cb->nextconbk);
189*5d9de2d3SDavid du Colombier DBG print("intstatus %ux\n", dmaregs[Intstatus]);
190*5d9de2d3SDavid du Colombier dmaregs[Intstatus] = 0;
191*5d9de2d3SDavid du Colombier ctlr->regs[Cs] = Int;
192*5d9de2d3SDavid du Colombier microdelay(1);
193*5d9de2d3SDavid du Colombier coherence();
194*5d9de2d3SDavid du Colombier DBG dumpdregs("before Active", ctlr->regs);
195*5d9de2d3SDavid du Colombier ctlr->regs[Cs] = Active;
196*5d9de2d3SDavid du Colombier DBG dumpdregs("after Active", ctlr->regs);
197*5d9de2d3SDavid du Colombier }
198*5d9de2d3SDavid du Colombier
199*5d9de2d3SDavid du Colombier int
dmawait(int chan)200*5d9de2d3SDavid du Colombier dmawait(int chan)
201*5d9de2d3SDavid du Colombier {
202*5d9de2d3SDavid du Colombier Ctlr *ctlr;
203*5d9de2d3SDavid du Colombier u32int *r;
204*5d9de2d3SDavid du Colombier int s;
205*5d9de2d3SDavid du Colombier
206*5d9de2d3SDavid du Colombier ctlr = &dma[chan];
207*5d9de2d3SDavid du Colombier tsleep(&ctlr->r, dmadone, ctlr, 3000);
208*5d9de2d3SDavid du Colombier ctlr->dmadone = 0;
209*5d9de2d3SDavid du Colombier r = ctlr->regs;
210*5d9de2d3SDavid du Colombier DBG dumpdregs("after sleep", r);
211*5d9de2d3SDavid du Colombier s = r[Cs];
212*5d9de2d3SDavid du Colombier if((s & (Active|End|Error)) != End){
213*5d9de2d3SDavid du Colombier print("dma chan %d %s Cs %ux Debug %ux\n", chan,
214*5d9de2d3SDavid du Colombier (s&End)? "error" : "timeout", s, r[Debug]);
215*5d9de2d3SDavid du Colombier r[Cs] = Reset;
216*5d9de2d3SDavid du Colombier r[Debug] = Clrerrors;
217*5d9de2d3SDavid du Colombier return -1;
218*5d9de2d3SDavid du Colombier }
219*5d9de2d3SDavid du Colombier r[Cs] = Int|End;
220*5d9de2d3SDavid du Colombier return 0;
221*5d9de2d3SDavid du Colombier }
222