plan9fox/sys/src/9/cycv/dma.c

266 lines
5.9 KiB
C

#include "u.h"
#include "../port/lib.h"
#include "mem.h"
#include "dat.h"
#include "fns.h"
#include "io.h"
#include "ureg.h"
#include "../port/error.h"
#define dmar ((ulong*)DMAS_BASE)
enum {
DSR = 0x000,
DPC = 0x004/4,
INTEN = 0x20 / 4,
INT_EVENT_RIS = 0x024 / 4,
INTMIS = 0x028 / 4,
INTCLR = 0x02C / 4,
FSRD = 0x030 / 4,
FSRC = 0x034 / 4,
FTRD = 0x038 / 4,
DBGSTATUS = 0xD00 / 4,
DBGCMD = 0xD04 / 4,
DBGINST0 = 0xD08 / 4,
DBGINST1 = 0xD0C / 4,
CR0 = 0xE00 / 4,
CR1, CR2, CR3, CR4, CRD,
WD = 0xE80 / 4,
};
enum {
DMAStopped,
DMAExecuting,
DMACacheMiss,
DMAUpdatingPC,
DMAWaitingForEvent,
DMAAtBarrier,
DMAWaitingForPeripheral = 7,
DMAKilling,
DMACompleting,
DMAFaultingCompleting=14,
DMAFaulting,
};
#define FTR(n) dmar[(0x40/4 + (n))]
#define CSR(n) dmar[(0x100/4 + (n)*2)]
#define CPC(n) dmar[(0x104/4 + (n)*2)]
#define SAR(n) dmar[(0x400/4 + (n)*8)]
#define DAR(n) dmar[(0x404/4 + (n)*8)]
#define CCR(n) dmar[(0x408/4 + (n)*8)]
#define LC0(n) dmar[(0x40C/4 + (n)*8)]
#define LC1(n) dmar[(0x410/4 + (n)*8)]
#define DST_BURST(n) (((n)-1&0xf)<<18)
#define DST_BEAT_1 (0)
#define DST_BEAT_2 (1<<15)
#define DST_BEAT_4 (2<<15)
#define DST_BEAT_8 (3<<15)
#define DST_BEAT_16 (4<<15)
#define SRC_BURST(n) (((n)-1&0xf)<<4)
#define SRC_BEAT_1 (0)
#define SRC_BEAT_2 (1<<1)
#define SRC_BEAT_4 (2<<1)
#define SRC_BEAT_8 (3<<1)
#define SRC_BEAT_16 (4<<1)
#define dmaMOV_SARn 0x00bc
#define dmaMOV_CCRn 0x01bc
#define dmaMOV_DARn 0x02bc
#define dmaLP0(n) (((n)-1&0xff)<<8|0x20)
#define dmaLP1(n) (((n)-1&0xff)<<8|0x22)
#define dmaLPEND0(n) (((n)&0xff)<<8|0x38)
#define dmaLPEND1(n) (((n)&0xff)<<8|0x3c)
#define dmaLD 0x04
#define dmaST 0x08
#define dmaSEV(n) (((n)&31)<<11|0x34)
#define dmaEND 0x00
#define dmaWMB 0x13
static QLock dmalock;
static Rendez dmarend;
static int finished;
static ulong code[64];
static int
isfinished(void *)
{
return finished;
}
static void
dmairq(Ureg *, void *)
{
dmar[INTCLR] = -1;
finished = 1;
wakeup(&dmarend);
}
static void
compactify(ulong *lp)
{
uchar *p, *q;
q = p = (uchar *) lp;
for(;;){
switch(*p){
case 0xbc:
q[0] = p[0];
q[1] = p[1];
q[2] = p[4];
q[3] = p[5];
q[4] = p[6];
q[5] = p[7];
q += 6; p += 8;
break;
case 0x20: case 0x22: case 0x38: case 0x3c: case 0x34:
q[0] = p[0];
q[1] = p[1];
q += 2; p += 4;
break;
case 0x04: case 0x08: case 0x13:
q[0] = p[0];
q++; p += 4;
break;
case 0x00:
q[0] = 0;
return;
default:
panic("DMA: unknown opcode %.2x", *p);
}
}
}
#define BURST(n) *p++ = dmaMOV_CCRn, *p++ = DST_BEAT_4 | SRC_BEAT_4 | SRC_BURST(n) | DST_BURST(n) | attr
void
dmacopy(void *dst, void *src, ulong n, int attr)
{
ulong *p;
assert((n & 3) == 0 && ((uintptr)src & 3) == 0 && ((uintptr)dst & 3) == 0);
while(n > (1<<22)){
dmacopy(dst, src, 1<<22, attr);
if((attr & SRC_INC) != 0) src = (uchar*)src + (1<<22);
if((attr & DST_INC) != 0) dst = (uchar*)dst + (1<<22);
}
if(n == 0) return;
qlock(&dmalock);
p = code;
*p++ = dmaMOV_SARn; *p++ = PADDR(src);
*p++ = dmaMOV_DARn; *p++ = PADDR(dst);
if((n >> 6) >= 1){
BURST(16);
if((n>>14) >= 1){
if((n>>14) > 1) *p++ = dmaLP0(n >> 14);
*p++ = dmaLP1(256);
*p++ = dmaLD;
*p++ = dmaST;
*p++ = dmaLPEND1(2);
if((n>>14) > 1) *p++ = dmaLPEND0(6);
n &= (1<<14)-1;
}
if((n >> 6) >= 1){
if((n>>6) > 1) *p++ = dmaLP0(n >> 6);
*p++ = dmaLD;
*p++ = dmaST;
if((n>>6) > 1) *p++ = dmaLPEND0(2);
n &= 63;
}
}
if(n >= 4){
BURST(n>>2);
*p++ = dmaLD;
*p++ = dmaST;
}
*p++ = dmaWMB;
*p++ = dmaSEV(0);
*p = dmaEND;
compactify(code);
if((CSR(0) & 0xf) != DMAStopped){
while((dmar[DBGSTATUS] & 1) != 0)
tsleep(&up->sleep, return0, nil, 1);
dmar[DBGINST0] = 0x1 << 16 | 1;
dmar[DBGCMD] = 0;
while((dmar[DBGSTATUS] & 1) != 0)
tsleep(&up->sleep, return0, nil, 1);
while((CSR(0) & 0xf) != DMAStopped)
tsleep(&up->sleep, return0, nil, 1);
}
cleandse(code, code + nelem(code));
while((dmar[DBGSTATUS] & 1) != 0)
tsleep(&up->sleep, return0, nil, 1);
dmar[DBGINST0] = 0xa0 << 16;
dmar[DBGINST1] = PADDR(code);
finished = 0;
dmar[DBGCMD] = 0;
while(!finished)
sleep(&dmarend, isfinished, nil);
qunlock(&dmalock);
}
static void
dmaabort(Ureg *, void *)
{
int i;
if((dmar[FSRD] & 1) != 0){
iprint("dma: manager fault: ");
if((dmar[FTRD] & 1<<30) != 0)
iprint("debug instruction, ");
if((dmar[FTRD] & 1<<16) != 0)
iprint("instruction fetch error, ");
if((dmar[FTRD] & 1<<5) != 0)
iprint("event security violation, ");
if((dmar[FTRD] & 1<<4) != 0)
iprint("DMAGO security violation, ");
if((dmar[FTRD] & 1<<1) != 0)
iprint("operand invalid, ");
if((dmar[FTRD] & 1<<0) != 0)
iprint("undefined instruction, ");
iprint("\n");
}
for(i = 0; i < 8; i++){
if((dmar[FSRC] & 1<<i) == 0)
continue;
iprint("dma: channel %d fault\n", i);
iprint("code = %.8p, PC = %.8ulx\n", code, CPC(i));
iprint("CCRn = %.8ulx, CSRn = %.8ulx\n", CCR(i), CSR(i));
iprint("LC0 = %.2ulx, LC1 = %.2ulx\n", LC0(i), LC1(i));
if((FTR(i) & 1<<31) != 0)
iprint("insufficient resources, ");
if((FTR(i) & 1<<30) != 0)
iprint("debug instruction, ");
if((FTR(i) & 1<<18) != 0)
iprint("read error, ");
if((FTR(i) & 1<<17) != 0)
iprint("write error, ");
if((FTR(i) & 1<<16) != 0)
iprint("instruction fetch error, ");
if((FTR(i) & 1<<13) != 0)
iprint("FIFO underflow, ");
if((FTR(i) & 1<<12) != 0)
iprint("FIFO error, ");
if((FTR(i) & 1<<7) != 0)
iprint("CCRn security violation, ");
if((FTR(i) & 1<<6) != 0)
iprint("peripheral security violation, ");
if((FTR(i) & 1<<5) != 0)
iprint("event security violation, ");
if((FTR(i) & 1<<4) != 0)
iprint("DMAGO security violation, ");
if((FTR(i) & 1<<1) != 0)
iprint("operand invalid, ");
if((FTR(i) & 1<<0) != 0)
iprint("undefined instruction, ");
iprint("\n");
}
panic("DMA fault");
}
void
dmalink(void)
{
dmar[INTEN] = 1;
intrenable(DMAIRQ0, dmairq, nil, LEVEL, "dma");
intrenable(DMAABORTIRQ, dmaabort, nil, LEVEL, "dma_abort");
}