summaryrefslogtreecommitdiff
path: root/os/ipengine/devfpga.c
diff options
context:
space:
mode:
authorCharles.Forsyth <devnull@localhost>2006-12-22 21:39:35 +0000
committerCharles.Forsyth <devnull@localhost>2006-12-22 21:39:35 +0000
commit74a4d8c26dd3c1e9febcb717cfd6cb6512991a7a (patch)
treec6e220ba61db3a6ea4052e6841296d829654e664 /os/ipengine/devfpga.c
parent46439007cf417cbd9ac8049bb4122c890097a0fa (diff)
20060303
Diffstat (limited to 'os/ipengine/devfpga.c')
-rw-r--r--os/ipengine/devfpga.c389
1 files changed, 389 insertions, 0 deletions
diff --git a/os/ipengine/devfpga.c b/os/ipengine/devfpga.c
new file mode 100644
index 00000000..41d1a445
--- /dev/null
+++ b/os/ipengine/devfpga.c
@@ -0,0 +1,389 @@
+#include "u.h"
+#include "../port/lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "../port/error.h"
+
+#include "io.h"
+#include "archipe.h"
+
+enum {
+ FPGASIZE = 8*1024*1024,
+ FPGATMR = 2-1, /* BCLK timer number (mapped to origin 0) */
+ TIMERSH = FPGATMR*4, /* timer field shift */
+
+ COM3= IBIT(1)|IBIT(2), /* sccr: clock output disabled */
+
+ ConfDone = 1<<1,
+ nStatus = 1<<0,
+};
+
+/*
+ * provisional FPGA interface for simple development work;
+ * for more complex things, use this to load the device then have a
+ * purpose-built device driver or module
+ */
+
+enum{
+ Qdir,
+ Qmemb,
+ Qmemw,
+ Qprog,
+ Qctl,
+ Qclk,
+ Qstatus,
+};
+
+static struct {
+ QLock;
+ int clkspeed;
+} fpga;
+
+static void resetfpga(void);
+static void startfpga(int);
+static int endfpga(void);
+static int fpgastatus(void);
+static void powerfpga(int);
+static void vclkenable(int);
+static void vclkset(char*, char*, char*, char*);
+static void memmovew(ushort*, ushort*, long);
+
+static Dirtab fpgadir[]={
+ ".", {Qdir, 0, QTDIR}, 0, 0555,
+ "fpgamemb", {Qmemb, 0}, FPGASIZE, 0666,
+ "fpgamemw", {Qmemw, 0}, FPGASIZE, 0666,
+ "fpgaprog", {Qprog, 0}, 0, 0222,
+ "fpgastatus", {Qstatus, 0}, 0, 0444,
+ "fpgactl", {Qctl, 0}, 0, 0666,
+ "fpgaclk", {Qclk, 0}, 0, 0666,
+};
+
+static char Eodd[] = "odd count or offset";
+
+static void
+fpgareset(void)
+{
+ powerfpga(0);
+}
+
+static Chan*
+fpgaattach(char *spec)
+{
+ return devattach('G', spec);
+}
+
+static Walkqid*
+fpgawalk(Chan *c, Chan *nc, char **name, int nname)
+{
+ return devwalk(c, nc, name, nname, fpgadir, nelem(fpgadir), devgen);
+}
+
+static int
+fpgastat(Chan *c, uchar *dp, int n)
+{
+ return devstat(c, dp, n, fpgadir, nelem(fpgadir), devgen);
+}
+
+static Chan*
+fpgaopen(Chan *c, int omode)
+{
+ return devopen(c, omode, fpgadir, nelem(fpgadir), devgen);
+}
+
+static void
+fpgaclose(Chan*)
+{
+}
+
+static long
+fpgaread(Chan *c, void *buf, long n, vlong offset)
+{
+ int v;
+ char stat[32], *p;
+
+ if(c->qid.type & QTDIR)
+ return devdirread(c, buf, n, fpgadir, nelem(fpgadir), devgen);
+
+ switch((ulong)c->qid.path){
+ case Qmemb:
+ if(offset >= FPGASIZE)
+ return 0;
+ if(offset+n >= FPGASIZE)
+ n = FPGASIZE-offset;
+ memmove(buf, KADDR(FPGAMEM+offset), n);
+ return n;
+ case Qmemw:
+ if((n | offset) & 1)
+ error(Eodd);
+ if(offset >= FPGASIZE)
+ return 0;
+ if(offset+n >= FPGASIZE)
+ n = FPGASIZE-offset;
+ memmovew((ushort*)buf, (ushort*)KADDR(FPGAMEM+offset), n);
+ return n;
+ case Qstatus:
+ v = fpgastatus();
+ p = seprint(stat, stat+sizeof(stat), "%sconfig", v&ConfDone?"":"!");
+ seprint(p, stat+sizeof(stat), " %sstatus\n", v&nStatus?"":"!");
+ return readstr(offset, buf, n, stat);
+ case Qclk:
+ return readnum(offset, buf, n, fpga.clkspeed, NUMSIZE);
+ case Qctl:
+ case Qprog:
+ return 0;
+ }
+ error(Egreg);
+ return 0; /* not reached */
+}
+
+static long
+fpgawrite(Chan *c, void *buf, long n, vlong offset)
+{
+ int i, j, v;
+ ulong w;
+ Cmdbuf *cb;
+ ulong *cfg;
+ uchar *cp;
+
+ switch((ulong)c->qid.path){
+ case Qmemb:
+ if(offset >= FPGASIZE)
+ return 0;
+ if(offset+n >= FPGASIZE)
+ n = FPGASIZE-offset;
+ memmove(KADDR(FPGAMEM+offset), buf, n);
+ return n;
+ case Qmemw:
+ if((n | offset) & 1)
+ error(Eodd);
+ if(offset >= FPGASIZE)
+ return 0;
+ if(offset+n >= FPGASIZE)
+ n = FPGASIZE-offset;
+ memmovew((ushort*)KADDR(FPGAMEM+offset), (ushort*)buf, n);
+ return n;
+ case Qctl:
+ cb = parsecmd(buf, n);
+ if(waserror()){
+ free(cb);
+ nexterror();
+ }
+ if(cb->nf < 1)
+ error(Ebadarg);
+ if(strcmp(cb->f[0], "reset") == 0)
+ resetfpga();
+ else if(strcmp(cb->f[0], "bclk") == 0){
+ v = 48;
+ if(cb->nf > 1)
+ v = strtoul(cb->f[1], nil, 0);
+ if(v <= 0 || 48%v != 0)
+ error(Ebadarg);
+ startfpga(48/v-1);
+ }else if(strcmp(cb->f[0], "vclk") == 0){
+ if(cb->nf == 5){ /* vclk n m v r */
+ vclkenable(1);
+ vclkset(cb->f[1], cb->f[2], cb->f[3], cb->f[4]);
+ }else
+ vclkenable(cb->nf < 2 || strcmp(cb->f[1], "on") == 0);
+ }else if(strcmp(cb->f[0], "power") == 0)
+ powerfpga(cb->nf < 2 || strcmp(cb->f[1], "off") != 0);
+ else
+ error(Ebadarg);
+ poperror();
+ free(cb);
+ return n;
+ case Qprog:
+ qlock(&fpga);
+ if(waserror()){
+ qunlock(&fpga);
+ nexterror();
+ }
+ powerfpga(1);
+ resetfpga();
+ cfg = KADDR(FPGACR);
+ cp = buf;
+ for(i=0; i<n; i++){
+ w = cp[i];
+ for(j=0; j<8; j++){
+ *cfg = w&1;
+ w >>= 1;
+ }
+ }
+ for(j=0; j<50; j++) /* Altera note says at least 10 clock cycles, but microblaster uses 50 */
+ *cfg = 0;
+ v = fpgastatus();
+ if(v != (nStatus|ConfDone)){
+ snprint(up->genbuf, sizeof(up->genbuf), "error loading fpga: status %d", v);
+ error(up->genbuf);
+ }
+ poperror();
+ qunlock(&fpga);
+ return n;
+ }
+ error(Egreg);
+ return 0; /* not reached */
+}
+
+/*
+ * PDN seems to control power to the FPGA subsystem
+ * but it is not documented nor is its scope clear (PLL as well?).
+ * It will not run without it.
+ */
+static void
+powerfpga(int on)
+{
+ IMM *io;
+
+ io = ioplock();
+ if(io->sccr & COM3){
+ io->sccrk = KEEP_ALIVE_KEY;
+ io->sccr &= ~ COM3; /* FPGA designs can use the clock */
+ io->sccrk = ~KEEP_ALIVE_KEY;
+ }
+ io->pcpar &= ~PDN;
+ io->pcdir |= PDN;
+ if(on)
+ io->pcdat &= ~PDN;
+ else
+ io->pcdat |= PDN;
+ iopunlock();
+}
+
+static void
+resetfpga(void)
+{
+ IMM *io;
+
+ io = ioplock();
+ io->pcpar &= ~nCONFIG;
+ io->pcdir |= nCONFIG;
+ io->pcdat &= ~nCONFIG;
+ microdelay(200);
+ io->pcdat |= nCONFIG;
+ iopunlock();
+}
+
+static int
+fpgastatus(void)
+{
+ /* isolate status bits IP_B0 and IP_B1 */
+ return (m->iomem->pipr>>14) & (ConfDone|nStatus);
+}
+
+static void
+startfpga(int scale)
+{
+ IMM *io;
+
+ io = ioplock();
+ io->tgcr &= ~(0xF<<TIMERSH);
+ io->tmr2 = ((scale&0xFF)<<8) | 0x2A;
+ io->tcn2 = 0;
+ io->trr2 = 0;
+ io->ter2 = 0xFFFF;
+ io->tgcr |= 0x1<<TIMERSH;
+ io->padir |= BCLK;
+ io->papar |= BCLK;
+ iopunlock();
+}
+
+static void
+vclkenable(int i)
+{
+ IMM *io;
+
+ io = ioplock();
+ io->padir &= ~VCLK;
+ io->papar &= ~VCLK;
+ io->pbdir |= EnableVCLK;
+ io->pbpar &= ~EnableVCLK;
+ if(i)
+ io->pbdat |= EnableVCLK;
+ else
+ io->pbdat &= ~EnableVCLK;
+ iopunlock();
+}
+
+static void
+vclkin(ulong *clk, int v)
+{
+ int i;
+
+ for(i=0; i<7; i++)
+ *clk = (v>>i) & 1;
+}
+
+static void
+vclkset(char *ns, char *ms, char *vs, char *rs)
+{
+ int n, m, v, r;
+ ulong *clk;
+
+ clk = KADDR(CLOCKCR);
+ n = strtol(ns, nil, 0);
+ m = strtol(ms, nil, 0);
+ v = strtol(vs, nil, 0);
+ r = strtol(rs, nil, 0);
+ if(n < 3 || n > 127 || m < 3 || m > 127 || v != 1 && v != 8 ||
+ r != 1 && r != 2 && r != 4 && r != 8)
+ error(Ebadarg);
+ vclkenable(0);
+ vclkin(clk, n);
+ vclkin(clk, m);
+ *clk = (v==0) & 1;
+ *clk = 1; *clk = 1;
+ *clk = r == 2 || r == 8;
+ *clk = r == 4 || r == 8;
+ *clk = 1; /* clock out */
+ *clk = 0; /* disable clk/x */
+ *clk = 1; *clk = 0; *clk = 1;
+ *clk = 0; *clk = 0; *clk = 0;
+ vclkenable(1);
+}
+
+/*
+ * copy data aligned on 16-bit word boundaries.
+ */
+static void
+memmovew(ushort *to, ushort *from, long count)
+{
+ int n;
+
+ if(count <= 0)
+ return;
+ count >>= 1;
+ n = (count+7) >> 3;
+ switch(count&7) { /* Duff's device */
+ case 0: do { *to++ = *from++;
+ case 7: *to++ = *from++;
+ case 6: *to++ = *from++;
+ case 5: *to++ = *from++;
+ case 4: *to++ = *from++;
+ case 3: *to++ = *from++;
+ case 2: *to++ = *from++;
+ case 1: *to++ = *from++;
+ } while(--n > 0);
+ }
+}
+
+Dev fpgadevtab = {
+ 'G',
+ "fpga",
+
+ fpgareset,
+ devinit,
+ devshutdown,
+ fpgaattach,
+ fpgawalk,
+ fpgastat,
+ fpgaopen,
+ devcreate,
+ fpgaclose,
+ fpgaread,
+ devbread,
+ fpgawrite,
+ devbwrite,
+ devremove,
+ devwstat,
+};