plan9fox/sys/src/9/omap/dma.c
cinap_lenrek d6e0e9c402 kernel: move devether and wifi to port/
the only architecture dependence of devether was enabling interrupts,
which is now done at the end of the driver's reset() function now.

the wifi stack and dummy ethersink also go to port/.

do the IRQ2->IRQ9 hack for pc kernels in intrenabale(), so not
every caller of intrenable() has to be aware of it.
2018-02-11 18:08:03 +01:00

264 lines
5.6 KiB
C

/*
* 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)&regs->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 = ROUND(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;
}