xref: /plan9-contrib/sys/src/9/bcm/dma.c (revision 5c47fe09a0cc86dfb02c0ea4a2b6aec7eda2361f)
15d9de2d3SDavid du Colombier /*
25d9de2d3SDavid du Colombier  * bcm2835 dma controller
35d9de2d3SDavid du Colombier  *
45d9de2d3SDavid du Colombier  * simplest to use only channels 0-6
55d9de2d3SDavid du Colombier  *	channels 7-14 have reduced functionality
65d9de2d3SDavid du Colombier  *	channel 15 is at a weird address
75d9de2d3SDavid du Colombier  *	channels 0 and 15 have an "external 128 bit 8 word read FIFO"
85d9de2d3SDavid du Colombier  *	  for memory to memory transfers
95d9de2d3SDavid du Colombier  *
105d9de2d3SDavid du Colombier  * Experiments show that only channels 2-5,11-12 work with mmc
115d9de2d3SDavid du Colombier  */
125d9de2d3SDavid du Colombier 
135d9de2d3SDavid du Colombier #include "u.h"
145d9de2d3SDavid du Colombier #include "../port/lib.h"
155d9de2d3SDavid du Colombier #include "../port/error.h"
165d9de2d3SDavid du Colombier #include "mem.h"
175d9de2d3SDavid du Colombier #include "dat.h"
185d9de2d3SDavid du Colombier #include "fns.h"
195d9de2d3SDavid du Colombier #include "io.h"
205d9de2d3SDavid du Colombier 
215d9de2d3SDavid du Colombier #define DMAREGS	(VIRTIO+0x7000)
225d9de2d3SDavid du Colombier 
235d9de2d3SDavid du Colombier #define DBG	if(Dbg)
245d9de2d3SDavid du Colombier 
255d9de2d3SDavid du Colombier enum {
265d9de2d3SDavid du Colombier 	Nchan		= 7,		/* number of dma channels */
275d9de2d3SDavid du Colombier 	Regsize		= 0x100,	/* size of regs for each chan */
28*5c47fe09SDavid du Colombier 	Cbalign		= 64,		/* control block byte alignment (allow for 64-byte cache on bcm2836) */
295d9de2d3SDavid du Colombier 	Dbg		= 0,
305d9de2d3SDavid du Colombier 
315d9de2d3SDavid du Colombier 	/* registers for each dma controller */
325d9de2d3SDavid du Colombier 	Cs		= 0x00>>2,
335d9de2d3SDavid du Colombier 	Conblkad	= 0x04>>2,
345d9de2d3SDavid du Colombier 	Ti		= 0x08>>2,
355d9de2d3SDavid du Colombier 	Sourcead	= 0x0c>>2,
365d9de2d3SDavid du Colombier 	Destad		= 0x10>>2,
375d9de2d3SDavid du Colombier 	Txfrlen		= 0x14>>2,
385d9de2d3SDavid du Colombier 	Stride		= 0x18>>2,
395d9de2d3SDavid du Colombier 	Nextconbk	= 0x1c>>2,
405d9de2d3SDavid du Colombier 	Debug		= 0x20>>2,
415d9de2d3SDavid du Colombier 
425d9de2d3SDavid du Colombier 	/* collective registers */
435d9de2d3SDavid du Colombier 	Intstatus	= 0xfe0>>2,
445d9de2d3SDavid du Colombier 	Enable		= 0xff0>>2,
455d9de2d3SDavid du Colombier 
465d9de2d3SDavid du Colombier 	/* Cs */
475d9de2d3SDavid du Colombier 	Reset		= 1<<31,
485d9de2d3SDavid du Colombier 	Abort		= 1<<30,
495d9de2d3SDavid du Colombier 	Error		= 1<<8,
505d9de2d3SDavid du Colombier 	Waitwrite	= 1<<6,
515d9de2d3SDavid du Colombier 	Waitdreq	= 1<<5,
525d9de2d3SDavid du Colombier 	Paused		= 1<<4,
535d9de2d3SDavid du Colombier 	Dreq		= 1<<3,
545d9de2d3SDavid du Colombier 	Int		= 1<<2,
555d9de2d3SDavid du Colombier 	End		= 1<<1,
565d9de2d3SDavid du Colombier 	Active		= 1<<0,
575d9de2d3SDavid du Colombier 
585d9de2d3SDavid du Colombier 	/* Ti */
595d9de2d3SDavid du Colombier 	Permapshift= 16,
605d9de2d3SDavid du Colombier 	Srcignore	= 1<<11,
615d9de2d3SDavid du Colombier 	Srcdreq		= 1<<10,
625d9de2d3SDavid du Colombier 	Srcwidth128	= 1<<9,
635d9de2d3SDavid du Colombier 	Srcinc		= 1<<8,
645d9de2d3SDavid du Colombier 	Destignore	= 1<<7,
655d9de2d3SDavid du Colombier 	Destdreq	= 1<<6,
665d9de2d3SDavid du Colombier 	Destwidth128	= 1<<5,
675d9de2d3SDavid du Colombier 	Destinc		= 1<<4,
685d9de2d3SDavid du Colombier 	Waitresp	= 1<<3,
695d9de2d3SDavid du Colombier 	Tdmode		= 1<<1,
705d9de2d3SDavid du Colombier 	Inten		= 1<<0,
715d9de2d3SDavid du Colombier 
725d9de2d3SDavid du Colombier 	/* Debug */
735d9de2d3SDavid du Colombier 	Lite		= 1<<28,
745d9de2d3SDavid du Colombier 	Clrerrors	= 7<<0,
755d9de2d3SDavid du Colombier };
765d9de2d3SDavid du Colombier 
775d9de2d3SDavid du Colombier typedef struct Ctlr Ctlr;
785d9de2d3SDavid du Colombier typedef struct Cb Cb;
795d9de2d3SDavid du Colombier 
805d9de2d3SDavid du Colombier struct Ctlr {
815d9de2d3SDavid du Colombier 	u32int	*regs;
825d9de2d3SDavid du Colombier 	Cb	*cb;
835d9de2d3SDavid du Colombier 	Rendez	r;
845d9de2d3SDavid du Colombier 	int	dmadone;
855d9de2d3SDavid du Colombier };
865d9de2d3SDavid du Colombier 
875d9de2d3SDavid du Colombier struct Cb {
885d9de2d3SDavid du Colombier 	u32int	ti;
895d9de2d3SDavid du Colombier 	u32int	sourcead;
905d9de2d3SDavid du Colombier 	u32int	destad;
915d9de2d3SDavid du Colombier 	u32int	txfrlen;
925d9de2d3SDavid du Colombier 	u32int	stride;
935d9de2d3SDavid du Colombier 	u32int	nextconbk;
945d9de2d3SDavid du Colombier 	u32int	reserved[2];
955d9de2d3SDavid du Colombier };
965d9de2d3SDavid du Colombier 
975d9de2d3SDavid du Colombier static Ctlr dma[Nchan];
985d9de2d3SDavid du Colombier static u32int *dmaregs = (u32int*)DMAREGS;
995d9de2d3SDavid du Colombier 
100*5c47fe09SDavid du Colombier uintptr
dmaaddr(void * va)101*5c47fe09SDavid du Colombier dmaaddr(void *va)
102*5c47fe09SDavid du Colombier {
103*5c47fe09SDavid du Colombier 	if(PTR2UINT(va)&0x40000000)
104*5c47fe09SDavid du Colombier 		panic("dma address %#p (from%#p)\n", va, getcallerpc(&va));
105*5c47fe09SDavid du Colombier 	return soc.busdram | (PTR2UINT(va) & ~KSEGM);
106*5c47fe09SDavid du Colombier }
107*5c47fe09SDavid du Colombier 
108*5c47fe09SDavid du Colombier static uintptr
dmaioaddr(void * va)109*5c47fe09SDavid du Colombier dmaioaddr(void *va)
110*5c47fe09SDavid du Colombier {
111*5c47fe09SDavid du Colombier 	return soc.busio | (PTR2UINT(va) & ~VIRTIO);
112*5c47fe09SDavid du Colombier }
113*5c47fe09SDavid du Colombier 
1145d9de2d3SDavid du Colombier static void
dump(char * msg,uchar * p,int n)1155d9de2d3SDavid du Colombier dump(char *msg, uchar *p, int n)
1165d9de2d3SDavid du Colombier {
1175d9de2d3SDavid du Colombier 	print("%s", msg);
1185d9de2d3SDavid du Colombier 	while(n-- > 0)
1195d9de2d3SDavid du Colombier 		print(" %2.2x", *p++);
1205d9de2d3SDavid du Colombier 	print("\n");
1215d9de2d3SDavid du Colombier }
1225d9de2d3SDavid du Colombier 
1235d9de2d3SDavid du Colombier static void
dumpdregs(char * msg,u32int * r)1245d9de2d3SDavid du Colombier dumpdregs(char *msg, u32int *r)
1255d9de2d3SDavid du Colombier {
1265d9de2d3SDavid du Colombier 	int i;
1275d9de2d3SDavid du Colombier 
1285d9de2d3SDavid du Colombier 	print("%s: %#p =", msg, r);
1295d9de2d3SDavid du Colombier 	for(i = 0; i < 9; i++)
1305d9de2d3SDavid du Colombier 		print(" %8.8uX", r[i]);
1315d9de2d3SDavid du Colombier 	print("\n");
1325d9de2d3SDavid du Colombier }
1335d9de2d3SDavid du Colombier 
1345d9de2d3SDavid du Colombier static int
dmadone(void * a)1355d9de2d3SDavid du Colombier dmadone(void *a)
1365d9de2d3SDavid du Colombier {
1375d9de2d3SDavid du Colombier 	return ((Ctlr*)a)->dmadone;
1385d9de2d3SDavid du Colombier }
1395d9de2d3SDavid du Colombier 
1405d9de2d3SDavid du Colombier static void
dmainterrupt(Ureg *,void * a)1415d9de2d3SDavid du Colombier dmainterrupt(Ureg*, void *a)
1425d9de2d3SDavid du Colombier {
1435d9de2d3SDavid du Colombier 	Ctlr *ctlr;
1445d9de2d3SDavid du Colombier 
1455d9de2d3SDavid du Colombier 	ctlr = a;
1465d9de2d3SDavid du Colombier 	ctlr->regs[Cs] = Int;
1475d9de2d3SDavid du Colombier 	ctlr->dmadone = 1;
1485d9de2d3SDavid du Colombier 	wakeup(&ctlr->r);
1495d9de2d3SDavid du Colombier }
1505d9de2d3SDavid du Colombier 
1515d9de2d3SDavid du Colombier void
dmastart(int chan,int dev,int dir,void * src,void * dst,int len)1525d9de2d3SDavid du Colombier dmastart(int chan, int dev, int dir, void *src, void *dst, int len)
1535d9de2d3SDavid du Colombier {
1545d9de2d3SDavid du Colombier 	Ctlr *ctlr;
1555d9de2d3SDavid du Colombier 	Cb *cb;
1565d9de2d3SDavid du Colombier 	int ti;
1575d9de2d3SDavid du Colombier 
1585d9de2d3SDavid du Colombier 	ctlr = &dma[chan];
1595d9de2d3SDavid du Colombier 	if(ctlr->regs == nil){
1605d9de2d3SDavid du Colombier 		ctlr->regs = (u32int*)(DMAREGS + chan*Regsize);
1615d9de2d3SDavid du Colombier 		ctlr->cb = xspanalloc(sizeof(Cb), Cbalign, 0);
1625d9de2d3SDavid du Colombier 		assert(ctlr->cb != nil);
1635d9de2d3SDavid du Colombier 		dmaregs[Enable] |= 1<<chan;
1645d9de2d3SDavid du Colombier 		ctlr->regs[Cs] = Reset;
1655d9de2d3SDavid du Colombier 		while(ctlr->regs[Cs] & Reset)
1665d9de2d3SDavid du Colombier 			;
1675d9de2d3SDavid du Colombier 		intrenable(IRQDMA(chan), dmainterrupt, ctlr, 0, "dma");
1685d9de2d3SDavid du Colombier 	}
1695d9de2d3SDavid du Colombier 	cb = ctlr->cb;
1705d9de2d3SDavid du Colombier 	ti = 0;
1715d9de2d3SDavid du Colombier 	switch(dir){
1725d9de2d3SDavid du Colombier 	case DmaD2M:
1735d9de2d3SDavid du Colombier 		cachedwbinvse(dst, len);
1745d9de2d3SDavid du Colombier 		ti = Srcdreq | Destinc;
175*5c47fe09SDavid du Colombier 		cb->sourcead = dmaioaddr(src);
176*5c47fe09SDavid du Colombier 		cb->destad = dmaaddr(dst);
1775d9de2d3SDavid du Colombier 		break;
1785d9de2d3SDavid du Colombier 	case DmaM2D:
1795d9de2d3SDavid du Colombier 		cachedwbse(src, len);
1805d9de2d3SDavid du Colombier 		ti = Destdreq | Srcinc;
181*5c47fe09SDavid du Colombier 		cb->sourcead = dmaaddr(src);
182*5c47fe09SDavid du Colombier 		cb->destad = dmaioaddr(dst);
1835d9de2d3SDavid du Colombier 		break;
1845d9de2d3SDavid du Colombier 	case DmaM2M:
1855d9de2d3SDavid du Colombier 		cachedwbse(src, len);
186*5c47fe09SDavid du Colombier 		cachedinvse(dst, len);
1875d9de2d3SDavid du Colombier 		ti = Srcinc | Destinc;
188*5c47fe09SDavid du Colombier 		cb->sourcead = dmaaddr(src);
189*5c47fe09SDavid du Colombier 		cb->destad = dmaaddr(dst);
1905d9de2d3SDavid du Colombier 		break;
1915d9de2d3SDavid du Colombier 	}
1925d9de2d3SDavid du Colombier 	cb->ti = ti | dev<<Permapshift | Inten;
1935d9de2d3SDavid du Colombier 	cb->txfrlen = len;
1945d9de2d3SDavid du Colombier 	cb->stride = 0;
1955d9de2d3SDavid du Colombier 	cb->nextconbk = 0;
1965d9de2d3SDavid du Colombier 	cachedwbse(cb, sizeof(Cb));
1975d9de2d3SDavid du Colombier 	ctlr->regs[Cs] = 0;
1985d9de2d3SDavid du Colombier 	microdelay(1);
199*5c47fe09SDavid du Colombier 	ctlr->regs[Conblkad] = dmaaddr(cb);
2005d9de2d3SDavid du Colombier 	DBG print("dma start: %ux %ux %ux %ux %ux %ux\n",
2015d9de2d3SDavid du Colombier 		cb->ti, cb->sourcead, cb->destad, cb->txfrlen,
2025d9de2d3SDavid du Colombier 		cb->stride, cb->nextconbk);
2035d9de2d3SDavid du Colombier 	DBG print("intstatus %ux\n", dmaregs[Intstatus]);
2045d9de2d3SDavid du Colombier 	dmaregs[Intstatus] = 0;
2055d9de2d3SDavid du Colombier 	ctlr->regs[Cs] = Int;
2065d9de2d3SDavid du Colombier 	coherence();
207*5c47fe09SDavid du Colombier 	microdelay(1);
2085d9de2d3SDavid du Colombier 	DBG dumpdregs("before Active", ctlr->regs);
2095d9de2d3SDavid du Colombier 	ctlr->regs[Cs] = Active;
2105d9de2d3SDavid du Colombier 	DBG dumpdregs("after Active", ctlr->regs);
2115d9de2d3SDavid du Colombier }
2125d9de2d3SDavid du Colombier 
2135d9de2d3SDavid du Colombier int
dmawait(int chan)2145d9de2d3SDavid du Colombier dmawait(int chan)
2155d9de2d3SDavid du Colombier {
2165d9de2d3SDavid du Colombier 	Ctlr *ctlr;
2175d9de2d3SDavid du Colombier 	u32int *r;
2185d9de2d3SDavid du Colombier 	int s;
2195d9de2d3SDavid du Colombier 
2205d9de2d3SDavid du Colombier 	ctlr = &dma[chan];
2215d9de2d3SDavid du Colombier 	tsleep(&ctlr->r, dmadone, ctlr, 3000);
2225d9de2d3SDavid du Colombier 	ctlr->dmadone = 0;
2235d9de2d3SDavid du Colombier 	r = ctlr->regs;
2245d9de2d3SDavid du Colombier 	DBG dumpdregs("after sleep", r);
2255d9de2d3SDavid du Colombier 	s = r[Cs];
2265d9de2d3SDavid du Colombier 	if((s & (Active|End|Error)) != End){
2275d9de2d3SDavid du Colombier 		print("dma chan %d %s Cs %ux Debug %ux\n", chan,
2285d9de2d3SDavid du Colombier 			(s&End)? "error" : "timeout", s, r[Debug]);
2295d9de2d3SDavid du Colombier 		r[Cs] = Reset;
2305d9de2d3SDavid du Colombier 		r[Debug] = Clrerrors;
2315d9de2d3SDavid du Colombier 		return -1;
2325d9de2d3SDavid du Colombier 	}
2335d9de2d3SDavid du Colombier 	r[Cs] = Int|End;
2345d9de2d3SDavid du Colombier 	return 0;
2355d9de2d3SDavid du Colombier }
236