diff options
-rw-r--r-- | sys/src/9/bcm/devgpio.c | 746 | ||||
-rw-r--r-- | sys/src/9/bcm/fns.h | 1 | ||||
-rw-r--r-- | sys/src/9/bcm/pif | 2 | ||||
-rw-r--r-- | sys/src/9/bcm/vcore.c | 10 |
4 files changed, 758 insertions, 1 deletions
diff --git a/sys/src/9/bcm/devgpio.c b/sys/src/9/bcm/devgpio.c new file mode 100644 index 000000000..76b893a02 --- /dev/null +++ b/sys/src/9/bcm/devgpio.c @@ -0,0 +1,746 @@ +#include "u.h" +#include "../port/lib.h" +#include "mem.h" +#include "dat.h" +#include "fns.h" +#include "../port/error.h" +#include "io.h" + +// path: +// 3 bits - generic file type (Qinctl, Qindata) +// 3 bits - parent type +// 3 bits - chosen scheme type (Qgeneric, Qbcm, Qboard, Qwpi) +// 6 bits - input number + +#define PIN_TABLE_SIZE 32 + +#define PIN_OFFSET SCHEME_OFFSET + SCHEME_BITS +#define PIN_BITS 6 +#define PIN_MASK ((1 << PIN_BITS) - 1) +#define PIN_NUMBER(q) (((q).path >> PIN_OFFSET) & PIN_MASK) + +#define SCHEME_OFFSET PARENT_OFFSET + PARENT_BITS +#define SCHEME_BITS 3 +#define SCHEME_MASK ((1 << SCHEME_BITS) - 1) +#define SCHEME_TYPE(q) (((q).path >> SCHEME_OFFSET) & SCHEME_MASK) + +#define PARENT_OFFSET FILE_OFFSET + FILE_BITS +#define PARENT_BITS 3 +#define PARENT_MASK ((1 << PARENT_BITS) - 1) +#define PARENT_TYPE(q) (((q).path >> PARENT_OFFSET) & PARENT_MASK) + +#define FILE_OFFSET 0 +#define FILE_BITS 3 +#define FILE_MASK ((1 << FILE_BITS) - 1) +#define FILE_TYPE(q) (((q).path >> FILE_OFFSET) & FILE_MASK) + +// pin is valid only when file is Qdata otherwise 0 is used +#define PATH(pin, scheme, parent, file) \ + ((pin & PIN_MASK) << PIN_OFFSET) \ + | ((scheme & SCHEME_MASK) << SCHEME_OFFSET) \ + | ((parent & PARENT_MASK) << PARENT_OFFSET) \ + | ((file & FILE_MASK) << FILE_OFFSET) + +#define SET_BIT(f, offset, value) \ + (*f = ((*f & ~(1 << (offset % 32))) | (value << (offset % 32)))) + +static int dflag = 0; +#define D(...) if(dflag) print(__VA_ARGS__) + +enum { + // parent types + Qtopdir = 0, + Qgpiodir, + // file types + Qdir, + Qdata, + Qctl, + Qevent, +}; +enum { + // naming schemes + Qbcm, + Qboard, + Qwpi, + Qgeneric +}; + + +// commands +enum { + CMzero, + CMone, + CMscheme, + CMfunc, + CMpull, + CMevent, +}; + +// dev entries +Dirtab topdir = { "#G", {PATH(0, Qgeneric, Qtopdir, Qdir), 0, QTDIR}, 0, 0555 }; +Dirtab gpiodir = { "gpio", {PATH(0, Qgeneric, Qgpiodir, Qdir), 0, QTDIR}, 0, 0555 }; + +Dirtab typedir[] = { + "OK", { PATH(16, Qgeneric, Qgpiodir, Qdata), 0, QTFILE }, 0, 0666, + "ctl", { PATH(0, Qgeneric, Qgpiodir, Qctl), 0, QTFILE }, 0, 0666, + "event", { PATH(0, Qgeneric, Qgpiodir, Qevent), 0, QTFILE }, 0, 0444, +}; + +// commands definition +static +Cmdtab gpiocmd[] = { + CMzero, "0", 1, + CMone, "1", 1, + CMscheme, "scheme", 2, + CMfunc, "function", 3, + CMpull, "pull", 3, + CMevent, "event", 4, +}; + +static int pinscheme; +static int boardrev; + +static Rendez rend; +static u32int eventvalue; +static long eventinuse; +static Lock eventlock; + +// +// BCM +// +enum { + Fin = 0, + Fout, + Ffunc5, + Ffunc4, + Ffunc0, + Ffunc1, + Ffunc2, + Ffunc3, +}; + +static char *funcname[] = { + "in", "out", "5", "4", "0", "1", "2", "3", +}; + +enum { + Poff = 0, + Pdown, + Pup, +}; + +static char *pudname[] = { + "off", "down", "up", +}; + +static char *evstatename[] = { + "disable", "enable", +}; + +enum { + Erising, + Efalling, +}; + +static char *evtypename[] = { + "edge-rising", "edge-falling", +}; + +static char *bcmtableR1[PIN_TABLE_SIZE] = { + "1", "2", 0, 0, // 0-3 + "4", 0, 0, "7", // 4-7 + "8", "9", "10", "11", // 8-11 + 0, 0, "14", "15", // 12-15 + 0, "17", "18", 0, // 16-19 + 0, "21", "22", "23", // 20-23 + "24", "25", 0, 0, // 24-27 + 0, 0, 0, 0, // 28-31 +}; + +static char *bcmtableR2[PIN_TABLE_SIZE] = { + 0, 0, "2", "3", // 0-3 + "4", 0, 0, "7", // 4-7 + "8", "9", "10", "11", // 8-11 + 0, 0, "14", "15", // 12-15 + 0, "17", "18", 0, // 16-19 + 0, 0, "22", "23", // 20-23 + "24", "25", 0, "27", // 24-27 + "28", "29", "30", "31", // 28-31 +}; + +static char *boardtableR1[PIN_TABLE_SIZE] = { + "SDA", "SCL", 0, 0, // 0-3 + "GPIO7", 0, 0, "CE1", // 4-7 + "CE0", "MISO", "MOSI", "SCLK", // 8-11 + 0, 0, "TxD", "RxD", // 12-15 + 0, "GPIO0", "GPIO1", 0, // 16-19 + 0, "GPIO2", "GPIO3", "GPIO4", // 20-23 + "GPIO5", "GPIO6", 0, 0, // 24-27 + 0, 0, 0, 0, // 28-31 +}; + +static char *boardtableR2[PIN_TABLE_SIZE] = { + 0, 0, "SDA", "SCL", // 0-3 + "GPIO7", 0, 0, "CE1", // 4-7 + "CE0", "MISO", "MOSI", "SCLK", // 8-11 + 0, 0, "TxD", "RxD", // 12-15 + 0, "GPIO0", "GPIO1", 0, // 16-19 + 0, 0, "GPIO3", "GPIO4", // 20-23 + "GPIO5", "GPIO6", 0, "GPIO2", // 24-27 + "GPIO8", "GPIO9", "GPIO10", "GPIO11", // 28-31 +}; + +static char *wpitableR1[PIN_TABLE_SIZE] = { + "8", "9", 0, 0, // 0-3 + "7", 0, 0, "11", // 4-7 + "10", "13", "12", "14", // 8-11 + 0, 0, "15", "16", // 12-15 + 0, "0", "1", 0, // 16-19 + 0, "2", "3", "4", // 20-23 + "5", "6", 0, 0, // 24-27 + 0, 0, 0, 0, // 28-31 +}; + +static char *wpitableR2[PIN_TABLE_SIZE] = { + 0, 0, "8", "9", // 0-3 + "7", 0, 0, "11", // 4-7 + "10", "13", "12", "14", // 8-11 + 0, 0, "15", "16", // 12-15 + 0, "0", "1", 0, // 16-19 + 0, 0, "3", "4", // 20-23 + "5", "6", 0, "2", // 24-27 + "17", "18", "19", "20", // 28-31 +}; + +static char *schemename[] = { + "bcm", "board", "wpi", +}; + +static char** +getpintable(void) +{ + switch(pinscheme) + { + case Qbcm: + return (boardrev>3)?bcmtableR2:bcmtableR1; + case Qboard: + return (boardrev>3)?boardtableR2:boardtableR1; + case Qwpi: + return (boardrev>3)?wpitableR2:wpitableR1; + default: + return nil; + } +} + +// stolen from uartmini.c +#define GPIOREGS (VIRTIO+0x200000) +/* GPIO regs */ +enum { + Fsel0 = 0x00>>2, + FuncMask= 0x7, + Set0 = 0x1c>>2, + Clr0 = 0x28>>2, + Lev0 = 0x34>>2, + Evds0 = 0x40>>2, + Redge0 = 0x4C>>2, + Fedge0 = 0x58>>2, + Hpin0 = 0x64>>2, + Lpin0 = 0x70>>2, + ARedge0 = 0x7C>>2, + AFedge0 = 0x88>2, + PUD = 0x94>>2, + PUDclk0 = 0x98>>2, + PUDclk1 = 0x9c>>2, +}; + +static void +gpiofuncset(uint pin, int func) +{ + u32int *gp, *fsel; + int off; + + gp = (u32int*)GPIOREGS; + fsel = &gp[Fsel0 + pin/10]; + off = (pin % 10) * 3; + *fsel = (*fsel & ~(FuncMask<<off)) | func<<off; +} + +static int +gpiofuncget(uint pin) +{ + u32int *gp, *fsel; + int off; + + gp = (u32int*)GPIOREGS; + fsel = &gp[Fsel0 + pin/10]; + off = (pin % 10) * 3; + return ((*fsel >> off) & FuncMask); +} + +static void +gpiopullset(uint pin, int state) +{ + u32int *gp, *reg; + u32int mask; + + gp = (u32int*)GPIOREGS; + reg = &gp[PUDclk0 + pin/32]; + mask = 1 << (pin % 32); + gp[PUD] = state; + microdelay(1); + *reg = mask; + microdelay(1); + *reg = 0; +} + +static void +gpioout(uint pin, int set) +{ + u32int *gp; + int v; + + gp = (u32int*)GPIOREGS; + v = set? Set0 : Clr0; + gp[v + pin/32] = 1 << (pin % 32); +} + +static int +gpioin(uint pin) +{ + u32int *gp; + + gp = (u32int*)GPIOREGS; + return (gp[Lev0 + pin/32] & (1 << (pin % 32))) != 0; +} + +static void +gpioevent(uint pin, int event, int enable) +{ + u32int *gp, *field; + int reg = 0; + + switch(event) + { + case Erising: + reg = Redge0; + break; + case Efalling: + reg = Fedge0; + break; + default: + panic("gpio: unknown event type"); + } + gp = (u32int*)GPIOREGS; + field = &gp[reg + pin/32]; + SET_BIT(field, pin, enable); +} + +static void +mkdeventry(Chan *c, Qid qid, Dirtab *tab, Dir *db) +{ + mkqid(&qid, tab->qid.path, tab->qid.vers, tab->qid.type); + devdir(c, qid, tab->name, tab->length, eve, tab->perm, db); +} + +static int +gpiogen(Chan *c, char *, Dirtab *, int , int s, Dir *db) +{ + Qid qid; + int parent, scheme, l; + char **pintable = getpintable(); + + qid.vers = 0; + parent = PARENT_TYPE(c->qid); + scheme = SCHEME_TYPE(c->qid); + + if(s == DEVDOTDOT) + { + switch(parent) + { + case Qtopdir: + case Qgpiodir: + mkdeventry(c, qid, &topdir, db); + break; + default: + return -1; + } + return 1; + } + + if(parent == Qtopdir) + { + switch(s) + { + case 0: + mkdeventry(c, qid, &gpiodir, db); + break; + default: + return -1; + } + return 1; + } + + if(scheme != Qgeneric && scheme != pinscheme) + { + error(nil); + } + + if(parent == Qgpiodir) + { + l = nelem(typedir); + if(s < l) + { + mkdeventry(c, qid, &typedir[s], db); + } else if (s < l + PIN_TABLE_SIZE) + { + s -= l; + + if(pintable[s] == 0) + { + return 0; + } + mkqid(&qid, PATH(s, pinscheme, Qgpiodir, Qdata), 0, QTFILE); + snprint(up->genbuf, sizeof up->genbuf, "%s", pintable[s]); + devdir(c, qid, up->genbuf, 0, eve, 0666, db); + } + else + { + return -1; + } + return 1; + } + + return 1; +} + +static void +interrupt(Ureg*, void *) +{ + + u32int *gp, *field; + char pin; + + gp = (u32int*)GPIOREGS; + + int set; + + coherence(); + + eventvalue = 0; + + for(pin = 0; pin < PIN_TABLE_SIZE; pin++) + { + set = (gp[Evds0 + pin/32] & (1 << (pin % 32))) != 0; + + if(set) + { + field = &gp[Evds0 + pin/32]; + SET_BIT(field, pin, 1); + SET_BIT(&eventvalue, pin, 1); + } + } + coherence(); + + wakeup(&rend); +} + +static void +gpioinit(void) +{ + boardrev = getrevision() & 0xff; + pinscheme = Qboard; + intrenable(49, interrupt, nil, 0, "gpio1"); +} + +static void +gpioshutdown(void) +{ } + +static Chan* +gpioattach(char *spec) +{ + return devattach('G', spec); +} + +static Walkqid* +gpiowalk(Chan *c, Chan *nc, char** name, int nname) +{ + return devwalk(c, nc, name, nname, 0, 0, gpiogen); +} + +static int +gpiostat(Chan *c, uchar *db, int n) +{ + return devstat(c, db, n, 0, 0, gpiogen); +} + +static Chan* +gpioopen(Chan *c, int omode) +{ + int type; + + c = devopen(c, omode, 0, 0, gpiogen); + + type = FILE_TYPE(c->qid); + + switch(type) + { + case Qdata: + c->iounit = 1; + break; + case Qctl: + break; + case Qevent: + lock(&eventlock); + if(eventinuse != 0){ + c->flag &= ~COPEN; + unlock(&eventlock); + error(Einuse); + } + eventinuse = 1; + unlock(&eventlock); + eventvalue = 0; + c->iounit = 4; + } + + return c; +} + +static void +gpioclose(Chan *c) +{ + int type; + type = FILE_TYPE(c->qid); + + switch(type) + { + case Qevent: + if(c->flag & COPEN) + { + if(c->flag & COPEN){ + eventinuse = 0; + } + } + break; + } +} + +static int +isset(void *) +{ + return eventvalue; +} + +static long +gpioread(Chan *c, void *va, long n, vlong off) +{ + int type, scheme; + uint pin; + char *a; + + a = va; + + if(c->qid.type & QTDIR) + { + return devdirread(c, va, n, 0, 0, gpiogen); + } + + type = FILE_TYPE(c->qid); + scheme = SCHEME_TYPE(c->qid); + + if(scheme != Qgeneric && scheme != pinscheme) + { + error(nil); + } + + switch(type) + { + case Qdata: + pin = PIN_NUMBER(c->qid); + a[0] = (gpioin(pin))?'1':'0'; + n = 1; + break; + case Qctl: + break; + case Qevent: + if(off >= 4) + { + off %= 4; + eventvalue = 0; + } + sleep(&rend, isset, 0); + + if(off + n > 4) + { + n = 4 - off; + } + memmove(a, &eventvalue + off, n); + } + + return n; +} + +static int +getpin(char *pinname) +{ + int i; + char **pintable = getpintable(); + for(i = 0; i < PIN_TABLE_SIZE; i++) + { + if(!pintable[i]) + { + continue; + } + if(strncmp(pintable[i], pinname, strlen(pintable[i])) == 0) + { + return i; + } + } + return -1; +} + +static long +gpiowrite(Chan *c, void *va, long n, vlong) +{ + int type, i, scheme; + uint pin; + char *arg; + + Cmdbuf *cb; + Cmdtab *ct; + + if(c->qid.type & QTDIR) + { + error(Eisdir); + } + + type = FILE_TYPE(c->qid); + + scheme = SCHEME_TYPE(c->qid); + + if(scheme != Qgeneric && scheme != pinscheme) + { + error(nil); + } + + cb = parsecmd(va, n); + if(waserror()) + { + free(cb); + nexterror(); + } + ct = lookupcmd(cb, gpiocmd, nelem(gpiocmd)); + if(ct == nil) + { + error(Ebadctl); + } + + switch(type) + { + case Qdata: + pin = PIN_NUMBER(c->qid); + + switch(ct->index) + { + case CMzero: + gpioout(pin, 0); + break; + case CMone: + gpioout(pin, 1); + break; + default: + error(Ebadctl); + } + break; + case Qctl: + switch(ct->index) + { + case CMscheme: + arg = cb->f[1]; + for(i = 0; i < nelem(schemename); i++) + { + if(strncmp(schemename[i], arg, strlen(schemename[i])) == 0) + { + pinscheme = i; + break; + } + } + break; + case CMfunc: + pin = getpin(cb->f[2]); + arg = cb->f[1]; + if(pin == -1) { + error(Ebadctl); + } + for(i = 0; i < nelem(funcname); i++) + { + if(strncmp(funcname[i], arg, strlen(funcname[i])) == 0) + { + gpiofuncset(pin, i); + break; + } + } + break; + case CMpull: + pin = getpin(cb->f[2]); + if(pin == -1) { + error(Ebadctl); + } + arg = cb->f[1]; + for(i = 0; i < nelem(pudname); i++) + { + if(strncmp(pudname[i], arg, strlen(pudname[i])) == 0) + { + gpiopullset(pin, i); + break; + } + } + break; + case CMevent: + pin = getpin(cb->f[3]); + if(pin == -1) { + error(Ebadctl); + } + + arg = cb->f[1]; + for(i = 0; i < nelem(evtypename); i++) + { + if(strncmp(evtypename[i], arg, strlen(evtypename[i])) == 0) + { + gpioevent(pin, i, (cb->f[2][0] == 'e')); + break; + } + } + break; + default: + error(Ebadctl); + } + break; + } + + free(cb); + + poperror(); + return n; +} + +Dev gpiodevtab = { + 'G', + "gpio", + + devreset, + gpioinit, + gpioshutdown, + gpioattach, + gpiowalk, + gpiostat, + gpioopen, + devcreate, + gpioclose, + gpioread, + devbread, + gpiowrite, + devbwrite, + devremove, + devwstat, +}; diff --git a/sys/src/9/bcm/fns.h b/sys/src/9/bcm/fns.h index 62aea4d86..f32132056 100644 --- a/sys/src/9/bcm/fns.h +++ b/sys/src/9/bcm/fns.h @@ -72,6 +72,7 @@ extern int userureg(Ureg*); extern void vectors(void); extern void vtable(void); extern uint gettemp(int); +extern uint getrevision(void); /* * floating point emulation diff --git a/sys/src/9/bcm/pif b/sys/src/9/bcm/pif index a30e02903..ef376f805 100644 --- a/sys/src/9/bcm/pif +++ b/sys/src/9/bcm/pif @@ -17,7 +17,7 @@ dev draw screen swcursor mouse mouse uart - + gpio sd usb diff --git a/sys/src/9/bcm/vcore.c b/sys/src/9/bcm/vcore.c index d4d52d6dc..2b82db238 100644 --- a/sys/src/9/bcm/vcore.c +++ b/sys/src/9/bcm/vcore.c @@ -33,6 +33,7 @@ enum { TagResp = 1<<31, TagGetfwrev = 0x00000001, + TagGetbrdrev = 0x00010002, TagGetmac = 0x00010003, TagGetram = 0x00010005, TagGetpower = 0x00020001, @@ -261,6 +262,15 @@ getfirmware(void) return buf[0]; } +uint +getrevision(void) +{ + u32int buf[1]; + if(vcreq(TagGetbrdrev, buf, 0, sizeof buf) != sizeof buf) + return 0; + return buf[0]; +} + /* * Get ARM ram */ |