123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221 |
- /*
- * bcm2835 dma controller
- *
- * simplest to use only channels 0-6
- * channels 7-14 have reduced functionality
- * channel 15 is at a weird address
- * channels 0 and 15 have an "external 128 bit 8 word read FIFO"
- * for memory to memory transfers
- *
- * Experiments show that only channels 2-5,11-12 work with mmc
- */
- #include "u.h"
- #include "../port/lib.h"
- #include "../port/error.h"
- #include "mem.h"
- #include "dat.h"
- #include "fns.h"
- #include "io.h"
- #define DMAREGS (VIRTIO+0x7000)
- #define DBG if(Dbg)
- enum {
- Nchan = 7, /* number of dma channels */
- Regsize = 0x100, /* size of regs for each chan */
- Cbalign = 32, /* control block byte alignment */
- Dbg = 0,
-
- /* registers for each dma controller */
- Cs = 0x00>>2,
- Conblkad = 0x04>>2,
- Ti = 0x08>>2,
- Sourcead = 0x0c>>2,
- Destad = 0x10>>2,
- Txfrlen = 0x14>>2,
- Stride = 0x18>>2,
- Nextconbk = 0x1c>>2,
- Debug = 0x20>>2,
- /* collective registers */
- Intstatus = 0xfe0>>2,
- Enable = 0xff0>>2,
- /* Cs */
- Reset = 1<<31,
- Abort = 1<<30,
- Error = 1<<8,
- Waitwrite = 1<<6,
- Waitdreq = 1<<5,
- Paused = 1<<4,
- Dreq = 1<<3,
- Int = 1<<2,
- End = 1<<1,
- Active = 1<<0,
- /* Ti */
- Permapshift= 16,
- Srcignore = 1<<11,
- Srcdreq = 1<<10,
- Srcwidth128 = 1<<9,
- Srcinc = 1<<8,
- Destignore = 1<<7,
- Destdreq = 1<<6,
- Destwidth128 = 1<<5,
- Destinc = 1<<4,
- Waitresp = 1<<3,
- Tdmode = 1<<1,
- Inten = 1<<0,
- /* Debug */
- Lite = 1<<28,
- Clrerrors = 7<<0,
- };
- typedef struct Ctlr Ctlr;
- typedef struct Cb Cb;
- struct Ctlr {
- u32int *regs;
- Cb *cb;
- Rendez r;
- int dmadone;
- };
- struct Cb {
- u32int ti;
- u32int sourcead;
- u32int destad;
- u32int txfrlen;
- u32int stride;
- u32int nextconbk;
- u32int reserved[2];
- };
- static Ctlr dma[Nchan];
- static u32int *dmaregs = (u32int*)DMAREGS;
- static void
- dump(char *msg, uchar *p, int n)
- {
- print("%s", msg);
- while(n-- > 0)
- print(" %2.2x", *p++);
- print("\n");
- }
- static void
- dumpdregs(char *msg, u32int *r)
- {
- int i;
- print("%s: %#p =", msg, r);
- for(i = 0; i < 9; i++)
- print(" %8.8uX", r[i]);
- print("\n");
- }
- static int
- dmadone(void *a)
- {
- return ((Ctlr*)a)->dmadone;
- }
- static void
- dmainterrupt(Ureg*, void *a)
- {
- Ctlr *ctlr;
- ctlr = a;
- ctlr->regs[Cs] = Int;
- ctlr->dmadone = 1;
- wakeup(&ctlr->r);
- }
- void
- dmastart(int chan, int dev, int dir, void *src, void *dst, int len)
- {
- Ctlr *ctlr;
- Cb *cb;
- int ti;
- ctlr = &dma[chan];
- if(ctlr->regs == nil){
- ctlr->regs = (u32int*)(DMAREGS + chan*Regsize);
- ctlr->cb = xspanalloc(sizeof(Cb), Cbalign, 0);
- assert(ctlr->cb != nil);
- dmaregs[Enable] |= 1<<chan;
- ctlr->regs[Cs] = Reset;
- while(ctlr->regs[Cs] & Reset)
- ;
- intrenable(IRQDMA(chan), dmainterrupt, ctlr, 0, "dma");
- }
- cb = ctlr->cb;
- ti = 0;
- switch(dir){
- case DmaD2M:
- cachedwbinvse(dst, len);
- ti = Srcdreq | Destinc;
- cb->sourcead = DMAIO(src);
- cb->destad = DMAADDR(dst);
- break;
- case DmaM2D:
- cachedwbse(src, len);
- ti = Destdreq | Srcinc;
- cb->sourcead = DMAADDR(src);
- cb->destad = DMAIO(dst);
- break;
- case DmaM2M:
- cachedwbse(src, len);
- cachedwbinvse(dst, len);
- ti = Srcinc | Destinc;
- cb->sourcead = DMAADDR(src);
- cb->destad = DMAADDR(dst);
- break;
- }
- cb->ti = ti | dev<<Permapshift | Inten;
- cb->txfrlen = len;
- cb->stride = 0;
- cb->nextconbk = 0;
- cachedwbse(cb, sizeof(Cb));
- ctlr->regs[Cs] = 0;
- microdelay(1);
- ctlr->regs[Conblkad] = DMAADDR(cb);
- DBG print("dma start: %ux %ux %ux %ux %ux %ux\n",
- cb->ti, cb->sourcead, cb->destad, cb->txfrlen,
- cb->stride, cb->nextconbk);
- DBG print("intstatus %ux\n", dmaregs[Intstatus]);
- dmaregs[Intstatus] = 0;
- ctlr->regs[Cs] = Int;
- microdelay(1);
- coherence();
- DBG dumpdregs("before Active", ctlr->regs);
- ctlr->regs[Cs] = Active;
- DBG dumpdregs("after Active", ctlr->regs);
- }
- int
- dmawait(int chan)
- {
- Ctlr *ctlr;
- u32int *r;
- int s;
- ctlr = &dma[chan];
- tsleep(&ctlr->r, dmadone, ctlr, 3000);
- ctlr->dmadone = 0;
- r = ctlr->regs;
- DBG dumpdregs("after sleep", r);
- s = r[Cs];
- if((s & (Active|End|Error)) != End){
- print("dma chan %d %s Cs %ux Debug %ux\n", chan,
- (s&End)? "error" : "timeout", s, r[Debug]);
- r[Cs] = Reset;
- r[Debug] = Clrerrors;
- return -1;
- }
- r[Cs] = Int|End;
- return 0;
- }
|