/* * omap3530 system dma controller * * terminology: a block consist of frame(s), a frame consist of elements * (uchar, ushort, or ulong sized). */ #include "u.h" #include "../port/lib.h" #include "mem.h" #include "dat.h" #include "fns.h" #include "io.h" #include "../port/error.h" #include "../port/netif.h" enum { Nirq = 4, Baseirq = 12, Nchan = 32, }; /* * has a sw reset bit * dma req lines 1, 2, 6, 63 are available for `system expansion' */ typedef struct Regs Regs; typedef struct Dchan Dchan; struct Regs { uchar _pad0[8]; /* bitfield of intrs pending, by Dchan; write 1s to clear */ ulong irqsts[Nirq]; ulong irqen[Nirq]; /* bitfield of intrs enabled, by Dchan */ ulong syssts; /* 1<<0 is Resetdone */ ulong syscfg; /* 1<<1 is Softreset */ uchar _pad1[0x64 - 0x30]; ulong caps[5]; /* caps[1] not defined */ ulong gcr; /* knobs */ ulong _pad2; struct Dchan { ulong ccr; /* chan ctrl: incr, etc. */ ulong clnkctrl; /* link ctrl */ ulong cicr; /* intr ctrl */ ulong csr; /* status */ ulong csdp; /* src & dest params */ ulong cen; /* element # */ ulong cfn; /* frame # */ ulong cssa; /* src start addr */ ulong cdsa; /* dest start addr */ ulong csei; /* src element index */ ulong csfi; /* src frame index | pkt size */ ulong cdei; /* dest element index */ ulong cdfi; /* dest frame index | pkt size */ ulong csac; /* src addr value (read-only?) */ ulong cdac; /* dest addr value */ ulong ccen; /* curr transferred element # (in frame) */ ulong ccfn; /* curr transferred frame # (in xfer) */ ulong color; uchar _pad3[24]; } chan[Nchan]; }; enum { /* cicr/csr bits */ Blocki = 1 << 5, /* ccr bits */ Enable = 1 << 7, }; typedef struct Xfer Xfer; static struct Xfer { Rendez *rend; int *done; /* flag to set on intr */ } xfer[Nirq]; int isdmadone(int irq) { Dchan *cp; Regs *regs = (Regs *)PHYSSDMA; cp = regs->chan + irq; return cp->csr & Blocki; } static void dmaintr(Ureg *, void *a) { int i = (int)a; /* dma request & chan # */ Dchan *cp; Regs *regs = (Regs *)PHYSSDMA; assert(i >= 0 && i < Nirq); *xfer[i].done = 1; assert(xfer[i].rend != nil); wakeup(xfer[i].rend); cp = regs->chan + i; if(!(cp->csr & Blocki)) iprint("dmaintr: req %d: Blocki not set; csr %#lux\n", i, cp->csr); cp->csr |= cp->csr; /* extinguish intr source */ coherence(); regs->irqsts[i] = regs->irqsts[i]; /* extinguish intr source */ coherence(); regs->irqen[i] &= ~(1 << i); coherence(); xfer[i].rend = nil; coherence(); } void zerowds(ulong *wdp, int cnt) { while (cnt-- > 0) *wdp++ = 0; } static int istestdmadone(void *arg) { return *(int *)arg; } void dmainit(void) { int n; char name[16]; Dchan *cp; Regs *regs = (Regs *)PHYSSDMA; if (probeaddr((uintptr)®s->syssts) < 0) panic("dmainit: no syssts reg"); regs->syssts = 0; coherence(); regs->syscfg |= 1<<1; /* Softreset */ coherence(); while(!(regs->syssts & (1<<0))) /* Resetdone? */ ; for (n = 0; n < Nchan; n++) { cp = regs->chan + n; cp->ccr = 0; cp->clnkctrl = 0; cp->cicr = 0; cp->csr = 0; cp->csdp = 0; cp->cen = cp->cfn = 0; cp->cssa = cp->cdsa = 0; cp->csei = cp->csfi = 0; cp->cdei = cp->cdfi = 0; // cp->csac = cp->cdac = 0; // ro cp->ccen = cp->ccfn = 0; cp->color = 0; } zerowds((void *)regs->irqsts, sizeof regs->irqsts / sizeof(ulong)); zerowds((void *)regs->irqen, sizeof regs->irqen / sizeof(ulong)); coherence(); regs->gcr = 65; /* burst size + 1 */ coherence(); for (n = 0; n < Nirq; n++) { snprint(name, sizeof name, "dma%d", n); intrenable(Baseirq + n, dmaintr, (void *)n, nil, name); } } enum { Testbyte = 0252, Testsize = 256, Scratch = MB, }; /* * try to confirm sane operation */ void dmatest(void) { int n, done; uchar *bp; static ulong pat = 0x87654321; static Rendez trendez; if (up == nil) panic("dmatest: up not set yet"); bp = (uchar *)KADDR(PHYSDRAM + 128*MB); memset(bp, Testbyte, Scratch); done = 0; dmastart((void *)PADDR(bp), Postincr, (void *)PADDR(&pat), Const, Testsize, &trendez, &done); sleep(&trendez, istestdmadone, &done); cachedinvse(bp, Scratch); if (((ulong *)bp)[0] != pat) panic("dmainit: copied incorrect data %#lux != %#lux", ((ulong *)bp)[0], pat); for (n = Testsize; n < Scratch && bp[n] != Testbyte; n++) ; if (n >= Scratch) panic("dmainit: ran wild over memory, clobbered ≥%,d bytes", n); if (bp[n] == Testbyte && n != Testsize) iprint("dma: %d-byte dma stopped after %d bytes!\n", Testsize, n); } /* addresses are physical */ int dmastart(void *to, int tmode, void *from, int fmode, uint len, Rendez *rend, int *done) { int irq, chan; uint ruplen; Dchan *cp; Regs *regs = (Regs *)PHYSSDMA; static Lock alloclck; /* allocate free irq (and chan) */ ilock(&alloclck); for (irq = 0; irq < Nirq && xfer[irq].rend != nil; irq++) ; if (irq >= Nirq) panic("dmastart: no available irqs; too many concurrent dmas"); chan = irq; xfer[irq].rend = rend; /* for wakeup at intr time */ xfer[irq].done = done; *done = 0; iunlock(&alloclck); ruplen = ROUNDUP(len, sizeof(ulong)); assert(to != from); cp = regs->chan + chan; cp->ccr &= ~Enable; /* paranoia */ cp->cicr = 0; regs->irqen[irq] &= ~(1 << chan); coherence(); cp->csdp = 2; /* 2 = log2(sizeof(ulong)) */ cp->cssa = (uintptr)from; cp->cdsa = (uintptr)to; cp->ccr = tmode << 14 | fmode << 12; cp->csei = cp->csfi = cp->cdei = cp->cdfi = 1; cp->cen = ruplen / sizeof(ulong); /* ulongs / frame */ cp->cfn = 1; /* 1 frame / xfer */ cp->cicr = Blocki; /* intr at end of block */ regs->irqen[irq] |= 1 << chan; coherence(); cp->ccr |= Enable; /* fire! */ coherence(); return irq; }