shithub: riscv

Download patch

ref: eb687adecd365876d6b6918a9ee4f8e8e7f205f6
parent: 88c998b2650d684086faca8e65f9b2f67c764b47
author: Matthew Veety <[email protected]>
date: Fri May 16 06:56:23 EDT 2014

added devgpio (thanks Krystian!). Also added getrevision() to vcore which allows you to get the raspberry pi board revision. I kept in the segment that allows direct access to the gpio memory

--- /dev/null
+++ b/sys/src/9/bcm/devgpio.c
@@ -1,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,
+};
--- a/sys/src/9/bcm/fns.h
+++ b/sys/src/9/bcm/fns.h
@@ -72,6 +72,7 @@
 extern void vectors(void);
 extern void vtable(void);
 extern uint gettemp(int);
+extern uint getrevision(void);
 
 /*
  * floating point emulation
--- a/sys/src/9/bcm/pif
+++ b/sys/src/9/bcm/pif
@@ -17,7 +17,7 @@
 	draw	screen swcursor
 	mouse	mouse
 	uart
-
+	gpio
 	sd
 	usb
 
--- a/sys/src/9/bcm/vcore.c
+++ b/sys/src/9/bcm/vcore.c
@@ -33,6 +33,7 @@
 	TagResp		= 1<<31,
 
 	TagGetfwrev	= 0x00000001,
+	TagGetbrdrev	= 0x00010002,
 	TagGetmac	= 0x00010003,
 	TagGetram	= 0x00010005,
 	TagGetpower	= 0x00020001,
@@ -257,6 +258,15 @@
 	u32int buf[1];
 
 	if(vcreq(TagGetfwrev, buf, 0, sizeof buf) != sizeof buf)
+		return 0;
+	return buf[0];
+}
+
+uint
+getrevision(void)
+{
+	u32int buf[1];
+	if(vcreq(TagGetbrdrev, buf, 0, sizeof buf) != sizeof buf)
 		return 0;
 	return buf[0];
 }