summaryrefslogtreecommitdiff
path: root/sys/src/9/bcm/dma.c
diff options
context:
space:
mode:
authorcinap_lenrek <cinap_lenrek@gmx.de>2013-01-26 17:33:56 +0100
committercinap_lenrek <cinap_lenrek@gmx.de>2013-01-26 17:33:56 +0100
commitbc610a1b1c32f6e2e9b034217bb3ce9a7defa739 (patch)
tree98fa1e680867eaf19c1be5e818a570713fa7a286 /sys/src/9/bcm/dma.c
parentea108c8ca6e726ac008f75775ab83775ec233171 (diff)
add raspberry pi kernel (from sources)
Diffstat (limited to 'sys/src/9/bcm/dma.c')
-rw-r--r--sys/src/9/bcm/dma.c221
1 files changed, 221 insertions, 0 deletions
diff --git a/sys/src/9/bcm/dma.c b/sys/src/9/bcm/dma.c
new file mode 100644
index 000000000..0a071ca00
--- /dev/null
+++ b/sys/src/9/bcm/dma.c
@@ -0,0 +1,221 @@
+/*
+ * 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;
+}