xref: /plan9/sys/src/9/bcm/dma.c (revision b4d1cf41cd5301e4c76aef9c04ddee28ac168a8e)
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