code: plan9front

Download patch

ref: f9a61a0813fa1eb4ca026373f9b421cc3aeae040
parent: f24038d601ebe23a213ca1eaf2cf2faa47c22873
author: cinap_lenrek <cinap_lenrek@felloff.net>
date: Fri Oct 13 11:46:53 EDT 2023

bcm, bcm64: Add bitbang i2cgpio driver

Adding a bitbang i2cgpio driver to overcome the
limitations and brokenness of the i2cbcm driver
and broadcom hardware.

Move the i2cbcm driver from bcm64/ to bcm/ and
comment it out in all kernel configurations
using i2cgpio instead.

--- /dev/null
+++ b/sys/src/9/bcm/i2cbcm.c
@@ -1,0 +1,217 @@
+/*
+ * bcm2835 i2c controller
+ *
+ *	Only i2c1 is supported.
+ *	i2c2 is reserved for HDMI.
+ *	i2c0 SDA0/SCL0 pins are not routed to P1 connector (except for early Rev 0 boards)
+ *
+ * maybe hardware problems lurking, see: https://github.com/raspberrypi/linux/issues/254
+ *
+ * modified by adventuresin9@gmail.com to work with 9Front's port/devi2c
+ */
+
+#include	"u.h"
+#include	"../port/lib.h"
+#include	"../port/error.h"
+#include	"mem.h"
+#include	"dat.h"
+#include	"fns.h"
+#include	"io.h"
+#include	"../port/i2c.h"
+
+#define I2CREGS	(VIRTIO+0x804000)
+
+
+typedef struct Ctlr Ctlr;
+typedef struct Bsc Bsc;
+
+/*
+ * Registers for Broadcom Serial Controller (i2c compatible)
+ */
+struct Bsc {
+	u32int	ctrl;
+	u32int	stat;
+	u32int	dlen;
+	u32int	addr;
+	u32int	fifo;
+	u32int	clkdiv;		/* default 1500 => 100 KHz assuming 150Mhz input clock */
+	u32int	delay;		/* default (48<<16)|48 falling:rising edge */
+	u32int	clktimeout;	/* default 64 */
+};
+
+/*
+ * Per-controller info
+ */
+struct Ctlr {
+	Bsc	*regs;
+	Rendez r;
+	Lock ilock;
+};
+
+static Ctlr ctlr;
+
+enum {
+	/* ctrl */
+	I2cen	= 1<<15,	/* I2c enable */
+	Intr	= 1<<10,	/* interrupt on reception */
+	Intt	= 1<<9,		/* interrupt on transmission */
+	Intd	= 1<<8,		/* interrupt on done */
+	Start	= 1<<7,		/* aka ST, start a transfer */
+	Clear	= 1<<4,		/* clear fifo */
+	Read	= 1<<0,		/* read transfer */
+	Write	= 0<<0,		/* write transfer */
+
+	/* stat */
+	Clkt	= 1<<9,		/* clock stretch timeout */
+	Err	= 1<<8,			/* NAK */
+	Rxf	= 1<<7,			/* RX fifo full */
+	Txe	= 1<<6,			/* TX fifo full */
+	Rxd	= 1<<5,			/* RX fifo has data */
+	Txd	= 1<<4,			/* TX fifo has space */
+	Rxr	= 1<<3,			/* RX fiio needs reading */
+	Txw	= 1<<2,			/* TX fifo needs writing */
+	Done	= 1<<1,		/* transfer done */
+	Ta	= 1<<0,			/* Transfer active */
+
+	/* pin settings */
+	SDA0Pin	= 2,
+	SCL0Pin	= 3,
+};
+
+static void
+i2cinterrupt(Ureg*, void*)
+{
+	Bsc *r;
+	int st;
+
+	ilock(&ctlr.ilock);
+	r = ctlr.regs;
+	st = 0;
+	if((r->ctrl & Intr) && (r->stat & Rxd))
+		st |= Intr;
+	if((r->ctrl & Intt) && (r->stat & Txd))
+		st |= Intt;
+	if(r->stat & Done)
+		st |= Intd;
+	if(st){
+		r->ctrl &= ~st;
+		wakeup(&ctlr.r);
+	}
+	iunlock(&ctlr.ilock);
+}
+
+static int
+i2cready(void *st)
+{
+	return (ctlr.regs->stat & (uintptr)st);
+}
+
+static int
+i2cinit(I2Cbus*)
+{
+	ctlr.regs = (Bsc*)I2CREGS;
+	ctlr.regs->clkdiv = 2500;
+
+	gpiosel(SDA0Pin, Alt0);
+	gpiosel(SCL0Pin, Alt0);
+	gpiopullup(SDA0Pin);
+	gpiopullup(SCL0Pin);
+
+	intrenable(IRQi2c, i2cinterrupt, nil, BUSUNKNOWN, "i2c");
+
+	return 0;
+}
+
+/*
+ *	Basic I²C driver for Raspberry Pi
+ *	subaddressing wasn't reliable, so it is just not allowed
+ *
+ *	10 bit addressing is also disabled.
+ */
+static int
+i2cio(I2Cdev *dev, uchar *pkt, int olen, int ilen)
+{
+	Bsc *r;
+	uchar *p;
+	int st;
+	int o;
+	int rw, len;
+	uint addr;
+	o = 0;
+
+	if(dev->subaddr > 0){				/* subaddressing in not implemented */
+		return -1;
+	}
+
+	if((pkt[0] & 0xF8) == 0xF0){		/* b11110xxx reserved for 10bit addressing*/
+		return -1;
+	}
+
+	rw = pkt[0] & 1;					/* rw bit is first bit of pkt[0], read == 1 */
+	addr = dev->addr;
+	pkt++;								/* move past device addr packet */
+	o++;								/* have to at least return processing the dev addr */
+
+	/* 
+	 * If 9Front is just running a probe
+	 * return 1,
+	 * else the controller throws an NAK error
+	 * when doing a write with just the dev addr
+	 */
+
+	if((olen == 1) && (ilen == 0)){
+		return 1;
+	}
+
+	r = ctlr.regs;
+	r->ctrl = I2cen | Clear;
+	r->addr = addr;
+	r->stat = Clkt|Err|Done;
+
+	len = (olen - 1) + ilen;
+	r->dlen = len;
+	r->ctrl = I2cen | Start | Intd | rw;
+
+	p = pkt;
+	st = rw == Read? Rxd : Txd;
+	while(len > 0){
+		while((r->stat & (st|Done)) == 0){
+			r->ctrl |= rw == Read? Intr : Intt;
+			sleep(&ctlr.r, i2cready, (void*)(st|Done));
+		}
+		if(r->stat & (Err|Clkt)){
+			r->ctrl = 0;
+			return -1;
+		}
+		if(rw == Read){
+			do{
+				*p++ = r->fifo;
+				len--;
+				o++;
+			}while ((r->stat & Rxd) && len > 0);
+		}else{
+			do{
+				r->fifo = *p++;
+				len--;
+				o++;
+			}while((r->stat & Txd) && len > 0);
+		}
+	}
+
+	while((r->stat & Done) == 0)
+		sleep(&ctlr.r, i2cready, (void*)Done);
+	if(r->stat & (Err|Clkt)){
+		r->ctrl = 0;
+		return -1;
+	}
+	r->ctrl = 0;
+	return o;
+}
+
+
+void
+i2cbcmlink(void)
+{
+	static I2Cbus i2c = {"i2c1", 400000, &ctlr, i2cinit, i2cio};
+	addi2cbus(&i2c);
+}
--- /dev/null
+++ b/sys/src/9/bcm/i2cgpio.c
@@ -1,0 +1,216 @@
+/*
+ * I²C bitbang driver using GPIO pins.
+ */
+#include	"u.h"
+#include	"../port/lib.h"
+#include	"../port/error.h"
+#include	"mem.h"
+#include	"dat.h"
+#include	"fns.h"
+#include	"io.h"
+#include	"../port/i2c.h"
+
+typedef struct Ctlr Ctlr;
+struct Ctlr
+{
+	uint	sda, scl;
+	int	delay;
+};
+
+static void
+Setpin(uint pin, int val)
+{
+	gpiosel(pin, val?Input:Output);
+	gpioout(pin, val);
+}
+
+static int
+Getpin(uint pin)
+{
+	return gpioin(pin);
+}
+
+static void
+Delay(Ctlr *ctlr)
+{
+	microdelay(ctlr->delay);
+}
+
+static int
+Stretch(Ctlr *ctlr)
+{
+	ulong to;
+
+	to = µs();
+	do {
+		if(Getpin(ctlr->scl))
+			return 0;
+	} while(µs() - to < 25*1000);
+	return -1;
+}
+
+static int
+Writebit(Ctlr *ctlr, int bit)
+{
+	Setpin(ctlr->sda, bit);
+	Delay(ctlr);
+	Setpin(ctlr->scl, 1);
+	Delay(ctlr);
+	if(Stretch(ctlr) < 0)
+		return -1;
+	if(bit && !Getpin(ctlr->sda))
+		return -2;
+	Setpin(ctlr->scl, 0);
+	return 0;
+}
+
+static int
+Readbit(Ctlr *ctlr)
+{
+	int bit;
+
+	Setpin(ctlr->sda, 1);
+	Delay(ctlr);
+	Setpin(ctlr->scl, 1);
+	if(Stretch(ctlr) < 0)
+		return -1;
+	Delay(ctlr);
+	bit = Getpin(ctlr->sda);
+	Setpin(ctlr->scl, 0);
+	return bit;
+}
+
+static int
+Readbyte(Ctlr *ctlr, int nack)
+{
+	int byte, i, e;
+
+	byte = 0;
+	for(i=0; i<8; i++){
+		if((e = Readbit(ctlr)) < 0)
+			return e;
+		byte <<= 1;
+		byte |= e;
+	}
+	if((e = Writebit(ctlr, nack)) < 0)
+		return e;
+	return byte;
+}
+
+static int
+Writebyte(Ctlr *ctlr, int byte)
+{
+	int i, e;
+
+	for(i=0; i<8; i++){
+		if((e = Writebit(ctlr, (byte>>7)&1)) < 0)
+			return  e;
+		byte <<= 1;
+	}
+	return Readbit(ctlr);
+}
+
+static int
+Start(Ctlr *ctlr)
+{
+	if(!Getpin(ctlr->sda) || !Getpin(ctlr->scl))
+		return -2;
+	Setpin(ctlr->sda, 0);
+	Delay(ctlr);
+	Setpin(ctlr->scl, 0);
+	return 0;
+}
+
+static int
+Restart(Ctlr *ctlr)
+{
+	Setpin(ctlr->sda, 1);
+	Delay(ctlr);
+	Setpin(ctlr->scl, 1);
+	if(Stretch(ctlr) < 0)
+		return -1;
+	Delay(ctlr);
+	return Start(ctlr);
+}
+
+static int
+Stop(Ctlr *ctlr)
+{
+	Setpin(ctlr->sda, 0);
+	Delay(ctlr);
+	Setpin(ctlr->scl, 1);
+	if(Stretch(ctlr) < 0){
+		Setpin(ctlr->sda, 1);
+		return -1;
+	}
+	Delay(ctlr);
+	Setpin(ctlr->sda, 1);
+	Delay(ctlr);
+	return 0;
+}
+
+static int
+init(I2Cbus *bus)
+{
+	Ctlr *ctlr = bus->ctlr;
+
+	ctlr->delay = 1 + (1000000 / (2 * bus->speed));
+	Setpin(ctlr->sda, 1);
+	Setpin(ctlr->scl, 1);
+	gpiopullup(ctlr->sda);
+	gpiopullup(ctlr->scl);
+	return 0;
+}
+
+static int
+io(I2Cdev *dev, uchar *pkt, int olen, int ilen)
+{
+	I2Cbus *bus = dev->bus;
+	Ctlr *ctlr = bus->ctlr;
+	int i, o, v, alen;
+
+	alen = olen > 0;
+	if(olen > alen && (pkt[0] & 0xF8) == 0xF0)
+		alen++;
+
+	if((v = Start(ctlr)) < 0)
+		return -1;
+	if(olen > alen)
+		pkt[0] &= ~1;
+	for(o=0; o<olen; o++){
+		if((v = Writebyte(ctlr, pkt[o])) != 0)
+			goto Stop;
+	}
+	if(ilen <= 0 || olen <= 0)
+		goto Stop;
+	if((pkt[0]&1) == 0){
+		if((v = Restart(ctlr)) < 0)
+			goto Stop;
+		pkt[0] |= 1;
+		for(i=0; i<alen; i++){
+			if((v = Writebyte(ctlr, pkt[i])) != 0)
+				goto Stop;
+		}
+	}
+	for(i=1; i<=ilen; i++){
+		if((v = Readbyte(ctlr, i==ilen)) < 0)
+			goto Stop;
+		pkt[o++] = v;
+	}
+Stop:
+	if(v == -2)	/* arbitration lost */
+		return -1;
+	Stop(ctlr);
+	return o;
+}
+
+void
+i2cgpiolink(void)
+{
+	static Ctlr ctlr1 = {
+		.sda = 2,
+		.scl = 3,
+	};
+	static I2Cbus i2c1 = {"i2c1", 100*1000, &ctlr1, init, io};
+	addi2cbus(&i2c1);
+}
--- a/sys/src/9/bcm/mkfile
+++ b/sys/src/9/bcm/mkfile
@@ -112,6 +112,7 @@
 main.$O: errstr.h rebootcode.i
 devmouse.$O mouse.$O screen.$O: screen.h
 usbdwc.$O: dwcotg.h ../port/usb.h
+i2cbcm.$O i2cgpio.$O: ../port/i2c.h
 arch.$O archbcm.$O archbcm2.$O clock.$O coproc.$O fpiarn.$O mmu.$O trap.$O vfp3.$O rebootcode.$O: arm.h mem.h
 rebootcode.$O: arm.s cache.v7.s
 
--- a/sys/src/9/bcm/pi
+++ b/sys/src/9/bcm/pi
@@ -19,6 +19,7 @@
 	mouse	mouse
 	uart	gpio
 	gpio	gpio
+	i2c
 	sd
 	usb
 
@@ -29,6 +30,8 @@
 	loopbackmedium
 	netdevmedium
 	emmc
+#	i2cbcm		devi2c
+	i2cgpio		devi2c gpio
 
 ip
 	tcp
--- a/sys/src/9/bcm/pi2
+++ b/sys/src/9/bcm/pi2
@@ -19,6 +19,7 @@
 	mouse	mouse
 	uart	gpio
 	gpio	gpio
+	i2c
 	sd
 	usb
 
@@ -29,6 +30,8 @@
 	loopbackmedium
 	netdevmedium
 	emmc
+#	i2cbcm		devi2c
+	i2cgpio		devi2c gpio
 
 ip
 	tcp
--- a/sys/src/9/bcm64/i2cbcm.c
+++ /dev/null
@@ -1,217 +1,0 @@
-/*
- * bcm2835 i2c controller
- *
- *	Only i2c1 is supported.
- *	i2c2 is reserved for HDMI.
- *	i2c0 SDA0/SCL0 pins are not routed to P1 connector (except for early Rev 0 boards)
- *
- * maybe hardware problems lurking, see: https://github.com/raspberrypi/linux/issues/254
- *
- * modified by adventuresin9@gmail.com to work with 9Front's port/devi2c
- */
-
-#include	"u.h"
-#include	"../port/lib.h"
-#include	"../port/error.h"
-#include	"mem.h"
-#include	"dat.h"
-#include	"fns.h"
-#include	"io.h"
-#include	"../port/i2c.h"
-
-#define I2CREGS	(VIRTIO+0x804000)
-
-
-typedef struct Ctlr Ctlr;
-typedef struct Bsc Bsc;
-
-/*
- * Registers for Broadcom Serial Controller (i2c compatible)
- */
-struct Bsc {
-	u32int	ctrl;
-	u32int	stat;
-	u32int	dlen;
-	u32int	addr;
-	u32int	fifo;
-	u32int	clkdiv;		/* default 1500 => 100 KHz assuming 150Mhz input clock */
-	u32int	delay;		/* default (48<<16)|48 falling:rising edge */
-	u32int	clktimeout;	/* default 64 */
-};
-
-/*
- * Per-controller info
- */
-struct Ctlr {
-	Bsc	*regs;
-	Rendez r;
-	Lock ilock;
-};
-
-static Ctlr ctlr;
-
-enum {
-	/* ctrl */
-	I2cen	= 1<<15,	/* I2c enable */
-	Intr	= 1<<10,	/* interrupt on reception */
-	Intt	= 1<<9,		/* interrupt on transmission */
-	Intd	= 1<<8,		/* interrupt on done */
-	Start	= 1<<7,		/* aka ST, start a transfer */
-	Clear	= 1<<4,		/* clear fifo */
-	Read	= 1<<0,		/* read transfer */
-	Write	= 0<<0,		/* write transfer */
-
-	/* stat */
-	Clkt	= 1<<9,		/* clock stretch timeout */
-	Err	= 1<<8,			/* NAK */
-	Rxf	= 1<<7,			/* RX fifo full */
-	Txe	= 1<<6,			/* TX fifo full */
-	Rxd	= 1<<5,			/* RX fifo has data */
-	Txd	= 1<<4,			/* TX fifo has space */
-	Rxr	= 1<<3,			/* RX fiio needs reading */
-	Txw	= 1<<2,			/* TX fifo needs writing */
-	Done	= 1<<1,		/* transfer done */
-	Ta	= 1<<0,			/* Transfer active */
-
-	/* pin settings */
-	SDA0Pin	= 2,
-	SCL0Pin	= 3,
-};
-
-static void
-i2cinterrupt(Ureg*, void*)
-{
-	Bsc *r;
-	int st;
-
-	ilock(&ctlr.ilock);
-	r = ctlr.regs;
-	st = 0;
-	if((r->ctrl & Intr) && (r->stat & Rxd))
-		st |= Intr;
-	if((r->ctrl & Intt) && (r->stat & Txd))
-		st |= Intt;
-	if(r->stat & Done)
-		st |= Intd;
-	if(st){
-		r->ctrl &= ~st;
-		wakeup(&ctlr.r);
-	}
-	iunlock(&ctlr.ilock);
-}
-
-static int
-i2cready(void *st)
-{
-	return (ctlr.regs->stat & (uintptr)st);
-}
-
-static int
-i2cinit(I2Cbus*)
-{
-	ctlr.regs = (Bsc*)I2CREGS;
-	ctlr.regs->clkdiv = 2500;
-
-	gpiosel(SDA0Pin, Alt0);
-	gpiosel(SCL0Pin, Alt0);
-	gpiopullup(SDA0Pin);
-	gpiopullup(SCL0Pin);
-
-	intrenable(IRQi2c, i2cinterrupt, nil, BUSUNKNOWN, "i2c");
-
-	return 0;
-}
-
-/*
- *	Basic I²C driver for Raspberry Pi
- *	subaddressing wasn't reliable, so it is just not allowed
- *
- *	10 bit addressing is also disabled.
- */
-static int
-i2cio(I2Cdev *dev, uchar *pkt, int olen, int ilen)
-{
-	Bsc *r;
-	uchar *p;
-	int st;
-	int o;
-	int rw, len;
-	uint addr;
-	o = 0;
-
-	if(dev->subaddr > 0){				/* subaddressing in not implemented */
-		return -1;
-	}
-
-	if((pkt[0] & 0xF8) == 0xF0){		/* b11110xxx reserved for 10bit addressing*/
-		return -1;
-	}
-
-	rw = pkt[0] & 1;					/* rw bit is first bit of pkt[0], read == 1 */
-	addr = dev->addr;
-	pkt++;								/* move past device addr packet */
-	o++;								/* have to at least return processing the dev addr */
-
-	/* 
-	 * If 9Front is just running a probe
-	 * return 1,
-	 * else the controller throws an NAK error
-	 * when doing a write with just the dev addr
-	 */
-
-	if((olen == 1) && (ilen == 0)){
-		return 1;
-	}
-
-	r = ctlr.regs;
-	r->ctrl = I2cen | Clear;
-	r->addr = addr;
-	r->stat = Clkt|Err|Done;
-
-	len = (olen - 1) + ilen;
-	r->dlen = len;
-	r->ctrl = I2cen | Start | Intd | rw;
-
-	p = pkt;
-	st = rw == Read? Rxd : Txd;
-	while(len > 0){
-		while((r->stat & (st|Done)) == 0){
-			r->ctrl |= rw == Read? Intr : Intt;
-			sleep(&ctlr.r, i2cready, (void*)(st|Done));
-		}
-		if(r->stat & (Err|Clkt)){
-			r->ctrl = 0;
-			return -1;
-		}
-		if(rw == Read){
-			do{
-				*p++ = r->fifo;
-				len--;
-				o++;
-			}while ((r->stat & Rxd) && len > 0);
-		}else{
-			do{
-				r->fifo = *p++;
-				len--;
-				o++;
-			}while((r->stat & Txd) && len > 0);
-		}
-	}
-
-	while((r->stat & Done) == 0)
-		sleep(&ctlr.r, i2cready, (void*)Done);
-	if(r->stat & (Err|Clkt)){
-		r->ctrl = 0;
-		return -1;
-	}
-	r->ctrl = 0;
-	return o;
-}
-
-
-void
-i2cbcmlink(void)
-{
-	static I2Cbus i2c = {"i2c1", 400000, &ctlr, i2cinit, i2cio};
-	addi2cbus(&i2c);
-}
--- a/sys/src/9/bcm64/mkfile
+++ b/sys/src/9/bcm64/mkfile
@@ -107,6 +107,7 @@
 
 devmouse.$O mouse.$O screen.$O: screen.h
 usbdwc.$O: dwcotg.h ../port/usb.h
+i2cbcm.$O i2cgpio.$O: ../port/i2c.h
 
 io.h:	../bcm/io.h
 	touch $target
--- a/sys/src/9/bcm64/pi3
+++ b/sys/src/9/bcm64/pi3
@@ -31,7 +31,8 @@
 	loopbackmedium 
 	netdevmedium
 	emmc
-	i2cbcm	devi2c
+#	i2cbcm		devi2c
+	i2cgpio		devi2c gpio
 
 ip
 	tcp
--- a/sys/src/9/bcm64/pi4
+++ b/sys/src/9/bcm64/pi4
@@ -20,6 +20,7 @@
 	mouse	mouse
 	uart	gpio
 	gpio	gpio
+	i2c
 	pci	pci
 	sd
 	usb
@@ -35,6 +36,8 @@
 	loopbackmedium
 	netdevmedium
 	sdhc
+#	i2cbcm		devi2c
+	i2cgpio		devi2c gpio
 
 ip
 	tcp