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