#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) & 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, };