Skip site navigation (1)Skip section navigation (2)
Date:      Thu, 22 Dec 2011 13:08:55 +0100
From:      Stefan Bethke <stb@lassitu.de>
To:        Adrian Chadd <adrian@freebsd.org>
Cc:        Oleksandr Tymoshenko <gonzo@freebsd.org>, "freebsd-embedded@freebsd.org" <freebsd-embedded@freebsd.org>
Subject:   Re: Updated switch/glue patch?
Message-ID:  <7FDE5802-B680-464F-8765-9353D9DDD043@lassitu.de>
In-Reply-To: <45529EC2-73BE-4F69-A9BE-E22D9FEAADD7@lassitu.de>
References:  <CAJ-Vmon8%2BOXQ4g752zZEB-O0BR0sFWO0QUvw--xp2jsBDkx6tQ@mail.gmail.com> <0F6CC18F-6973-42A2-AC03-F01BF59458AE@lassitu.de> <CAJ-Vmo=Y8pp4iFnw%2B1hcPae6QXFboz=a7puwgC1kVSZ3JwMgPQ@mail.gmail.com> <1100F70E-9DA9-4163-AC9A-423ECE5AA9A3@lassitu.de> <CAJ-VmonrnJ7cC6u2LsL9AGusz_%2BkSwY62Rr1__sg5U_NynJ1SQ@mail.gmail.com> <CAJ-Vmo=WSN1oLM=B2HqSHrWyOaOD9BSwwu8=1Wys0CLRJ_N-TA@mail.gmail.com> <C637C171-A1A2-4296-84FA-6DE97137DC42@lassitu.de> <CAJ-Vmon2boy7OCh_4O0MeCi0yCdZu0OYb5dxHCEK=-%2B46zBGtg@mail.gmail.com> <CAJ-Vmoku5eLEYi5_DXVxK=0=4Ewn2aGepv3YUw4ApuVh_7y2%2Bw@mail.gmail.com> <CAJ-VmonvpnaS1rAO%2BsDRh1E5WfsrZTYE297Kc96prhfKjrM89Q@mail.gmail.com> <CAJ-VmokQxQs2DUKL=ONyxnnS7Q28ytmwZJ_thqvc4SvMkmS=cQ@mail.gmail.com> <18CABB46-9B9A-41CB-8742-6723C5FF4D67@lassitu.de> <C0BF20FD-E30F-4E9C-A0FE-500BE4807B99@bsdimp.com> <CAJ-VmokgiQCEG4et3X=3o_MuCMkO9MqkKqa-fjdpEqQNucn=Lw@mail.gmail.com> <2CBD8651-E132-49DC-A082-37A8F5C626EA@bsdimp.com> <AFE755D6-E462-40B4-A97B-9261303B3A4F@lassitu.de> <CAJ-Vm o=ERixAN96QSOaaej8avPHYkGC28AJZErdEhO0%2B=1R0Pw@mail.gmail.com> <09670C34-0D30-46BC-BA7E-4AAA22193B61@lassitu.de> <CAJ-VmokwSHN8U2=HJQT9kxFQ9oE-6H2h3KDb%2BMHN1Z8sur9=yw@mail.gmail.com> <45529EC2-73BE-4F69-A9BE-E22D9FEAADD7@lassitu.de>

next in thread | previous in thread | raw e-mail | index | archive | help

--Apple-Mail=_80F6A1CE-E774-4603-B510-783FDD6677E0
Content-Transfer-Encoding: quoted-printable
Content-Type: text/plain;
	charset=windows-1252

Am 22.12.2011 um 12:55 schrieb Stefan Bethke:

>=20
> Am 22.12.2011 um 06:41 schrieb Adrian Chadd:
>=20
>> On 21 December 2011 07:13, Stefan Bethke <stb@lassitu.de> wrote:
>>=20
>>> I've replaced the generic PHY status poll with a custom one that =
uses the switch registers instead of the PHYs.  I've cloned your git and =
have started adding my changes to it (branch also named work/ath):
>>> http://gitorious.org/~stb/freebsd/stb-adrianchadd-freebsd-work
>>>=20
>>> I tried a merge request, but gitorious didn't like it (at least at =
the top level).
>>=20
>> Cool! Can you please either figure out why that's the case, or just
>> throw me some diffs?
>=20
> It complains about the merge being too big to display in a browser?
>=20
> Anyway, here's two diffs.  I believe you can track my tree by adding =
it to your local repository, and then merge it locally. That should be =
easier and less error prone than applying diffs.
>=20
>> We should figure out how to merge stuff back into your tree to mine
>> (and then propagate the changes back to you) so we don't have to face
>> this when the changes are bigger.
>=20
> I'm tracking your tree and merging changes into mine.  One option =
could be to allow me write access to your repo and let me stuff my =
changes into a branch there.
>=20
>> After that's done, I'll work on you to tidy up the management =
utility.
>> Then we can start merging in whatever's needed from zrouter, and =
start
>> pushing this into -HEAD. I don't mind if it's a work in progress - at
>> least something will be in the tree.
>=20
> Cool!
>=20
> I've got a WRT160NL here with an RTL8306S which I could write a driver =
for.  I started bringing FreeBSD up on that last night, but accidentally =
overwrote half my uboot.  Waiting for the JTAG adapter to ship=85

Updated and corrected patch:

--Apple-Mail=_80F6A1CE-E774-4603-B510-783FDD6677E0
Content-Disposition: attachment;
	filename=iicbb.patch
Content-Type: application/octet-stream; x-unix-mode=0644; name="iicbb.patch"
Content-Transfer-Encoding: 7bit

diff --git a/sys/dev/iicbus/iicbb.c b/sys/dev/iicbus/iicbb.c
index fed203c..cdb65e5 100644
--- a/sys/dev/iicbus/iicbb.c
+++ b/sys/dev/iicbus/iicbb.c
@@ -25,7 +25,7 @@
  */
 
 #include <sys/cdefs.h>
-__FBSDID("$FreeBSD$");
+__FBSDID("$FreeBSD: head/sys/dev/iicbus/iicbb.c 188461 2009-02-10 22:50:23Z imp $");
 
 /*
  * Generic I2C bit-banging code
@@ -48,9 +48,9 @@ __FBSDID("$FreeBSD$");
 #include <sys/systm.h>
 #include <sys/module.h>
 #include <sys/bus.h>
+#include <sys/sysctl.h>
 #include <sys/uio.h>
 
-
 #include <dev/iicbus/iiconf.h>
 #include <dev/iicbus/iicbus.h>
 
@@ -59,9 +59,31 @@ __FBSDID("$FreeBSD$");
 #include "iicbus_if.h"
 #include "iicbb_if.h"
 
+#define IIC_DELAY_100KHZ	3	/* 3 microseconds per quarter cycle, should be 2.5 */
+
+#if defined(DEBUG)
+static int i2c_debug = 0;
+#define I2C_DEBUG(x)				\
+	do {					\
+		if (i2c_debug) (x);		\
+	} while (0)
+
+#define I2C_LOG(args...)			\
+	do {					\
+		if (i2c_debug) printf(args);	\
+	} while (0)
+static SYSCTL_NODE(_debug, OID_AUTO, iicbb, CTLFLAG_RD, 0, "iicbb");
+SYSCTL_INT(_debug_iicbb, OID_AUTO, debug, CTLFLAG_RW, &i2c_debug, 0,
+	"print bus transactions");
+#else
+#define I2C_DEBUG(x)
+#define	I2C_LOG(args...)
+#endif
+
 struct iicbb_softc {
 	device_t iicbus;
-	int udelay;		/* signal toggle delay in usec */
+	int iicdelay;
+	int iictimeout;
 };
 
 static int iicbb_attach(device_t);
@@ -126,11 +148,11 @@ iicbb_attach(device_t dev)
 	if (!sc->iicbus)
 		return (ENXIO);
 
-	sc->udelay = 10;		/* 10 uS default */
+	sc->iicdelay = IIC_DELAY_100KHZ;
 	if (resource_int_value(device_get_name(dev),
 	    device_get_unit(dev), "udelay", &udelay) == 0)
-	    sc->udelay = udelay;
-	device_printf(dev, "udelay=%d microseconds\n", sc->udelay);
+	    sc->iicdelay = udelay;
+	device_printf(dev, "udelay=%d microseconds\n", sc->iicdelay);
 
 	bus_generic_attach(dev);
 
@@ -193,149 +215,191 @@ iicbb_print_child(device_t bus, device_t dev)
 	return (retval);
 }
 
-#define I2C_SETSDA(sc,dev,val) do {			\
-	IICBB_SETSDA(device_get_parent(dev), val);	\
-	DELAY(sc->udelay);				\
-	} while (0)
 
-#define I2C_SETSCL(dev,val) do {			\
-	iicbb_setscl(dev, val, 100);			\
-	} while (0)
-
-#define I2C_SET(sc,dev,ctrl,data) do {			\
-	I2C_SETSCL(dev, ctrl);				\
-	I2C_SETSDA(sc, dev, data);			\
-	} while (0)
-
-#define I2C_GETSDA(dev) (IICBB_GETSDA(device_get_parent(dev)))
-
-#define I2C_GETSCL(dev) (IICBB_GETSCL(device_get_parent(dev)))
-
-static int i2c_debug = 0;
-#define I2C_DEBUG(x)	do {					\
-				if (i2c_debug) (x);		\
-			} while (0)
-
-#define I2C_LOG(format,args...)	do {				\
-					printf(format, args);	\
-				} while (0)
+/*
+ * Low-level bus state functions.  The following functions implement the
+ * basic bus states for I2C.  We divide the full clock cycle into four
+ * phases.  Transitions of SCL and SDA occur between these phases.  The
+ * nominal duration of each phase is 100 kHz/4, or 2.5 us.  Implementations
+ * for busses and devices can choose a lower delay for faster operation.
+ *
+ * Each of the functions returns -1 if the operation could not be completed.
+ */
 
-static void
-iicbb_setscl(device_t dev, int val, int timeout)
+/*
+ * I2C start (S) and repeated start (Sr) condition
+ *     0  1  2  3
+ *         __.__
+ * SCL XXXX     \__.
+ *         __
+ * SDA XXXX  \__.__.
+ * 
+ */
+static __inline int
+i2c_start(device_t dev)
 {
-	struct iicbb_softc *sc = device_get_softc(dev);
-	int k = 0;
-
-	IICBB_SETSCL(device_get_parent(dev), val);
-	DELAY(sc->udelay);
-
-	while (val && !I2C_GETSCL(dev) && k++ < timeout) {
-		IICBB_SETSCL(device_get_parent(dev), val);
-		DELAY(sc->udelay);
+	struct iicbb_softc *sc = (struct iicbb_softc *)device_get_softc(dev);
+	int timeout = sc->iictimeout;
+
+	/* 0 */
+	IICBB_SETSDA(device_get_parent(dev), 1);
+	IICBB_SETSCL(device_get_parent(dev), 1);
+	/* wait for the bus to become idle */
+	while(1) {
+		DELAY(sc->iicdelay);
+		if (IICBB_GETSDA(device_get_parent(dev)) != 0 &&
+			IICBB_GETSCL(device_get_parent(dev)) != 0)
+			break;
+		if (timeout <= 0)
+			return (-1);
+		timeout -= sc->iicdelay;
 	}
-
-	return;
+	/* 1 */
+	DELAY(sc->iicdelay);
+	/* 2 */
+	IICBB_SETSDA(device_get_parent(dev), 0);
+	DELAY(sc->iicdelay);
+	/* 3 */
+	IICBB_SETSCL(device_get_parent(dev), 0);
+	DELAY(sc->iicdelay);
+	return (0);
 }
 
-static void
-iicbb_one(device_t dev, int timeout)
+/*
+ * I2C stop (P) condition
+ *     0  1  2  3
+ *         __.__.__.
+ * SCL .__/     
+ *            _____.
+ * SDA XXXX__/
+ */
+static __inline int
+i2c_stop(device_t dev)
 {
-	struct iicbb_softc *sc = device_get_softc(dev);
+	struct iicbb_softc *sc = (struct iicbb_softc *)device_get_softc(dev);
 
-	I2C_SET(sc,dev,0,1);
-	I2C_SET(sc,dev,1,1);
-	I2C_SET(sc,dev,0,1);
-	return;
+	/* 0 */
+	IICBB_SETSDA(device_get_parent(dev), 0);
+	DELAY(sc->iicdelay);
+	/* 1 */
+	IICBB_SETSCL(device_get_parent(dev), 1);
+	DELAY(sc->iicdelay);
+	/* 2 */
+	IICBB_SETSDA(device_get_parent(dev), 1);
+	DELAY(sc->iicdelay);
+	/*
+	 * we can skip the last delay since the bus will either be idle,
+	 * or the next S will wait long enough to keep the timing correct.
+	 */
+	return (0);
 }
 
-static void
-iicbb_zero(device_t dev, int timeout)
+/*
+ * I2C transmit one bit
+ *     0  1  2  3
+ *         __.__
+ * SCL .__/     \__.
+ *         __.__
+ * SDA .XXX__.__XXX.
+ */
+static __inline int
+i2c_xmitbit(device_t dev, int value)
 {
-	struct iicbb_softc *sc = device_get_softc(dev);
-
-	I2C_SET(sc,dev,0,0);
-	I2C_SET(sc,dev,1,0);
-	I2C_SET(sc,dev,0,0);
-	return;
+	struct iicbb_softc *sc = (struct iicbb_softc *)device_get_softc(dev);
+	int timeout = sc->iictimeout;
+
+	/* 0 */
+	IICBB_SETSDA(device_get_parent(dev), value);
+	DELAY(sc->iicdelay);
+	/* 1 */
+	IICBB_SETSCL(device_get_parent(dev), 1);
+	while (1) {
+		DELAY(sc->iicdelay);
+		if (IICBB_GETSCL(device_get_parent(dev)) != 0)
+			break;
+		if (timeout <= 0)
+			return (-1);
+		timeout -= sc->iicdelay;
+	}
+	/* 2 */
+	if (value != 0 && IICBB_GETSDA(device_get_parent(dev)) == 0)
+		return (-1); /* another master is pulling down SDA */
+	DELAY(sc->iicdelay);
+	/* 3 */
+	IICBB_SETSCL(device_get_parent(dev), 0);
+	DELAY(sc->iicdelay);
+	return (0);
 }
 
 /*
- * Waiting for ACKNOWLEDGE.
- *
- * When a chip is being addressed or has received data it will issue an
- * ACKNOWLEDGE pulse. Therefore the MASTER must release the DATA line
- * (set it to high level) and then release the CLOCK line.
- * Now it must wait for the SLAVE to pull the DATA line low.
- * Actually on the bus this looks like a START condition so nothing happens
- * because of the fact that the IC's that have not been addressed are doing
- * nothing.
- *
- * When the SLAVE has pulled this line low the MASTER will take the CLOCK
- * line low and then the SLAVE will release the SDA (data) line.
+ * I2C receive one bit
+ *     0  1  2  3
+ *         __.__
+ * SCL .__/     \__.
+ *         __.__
+ * SDA .XXX__.__XXX.
  */
-static int
-iicbb_ack(device_t dev, int timeout)
+static __inline int
+i2c_recvbit(device_t dev)
 {
-	struct iicbb_softc *sc = device_get_softc(dev);
-	int noack;
-	int k = 0;
-
-	I2C_SET(sc,dev,0,1);
-	I2C_SET(sc,dev,1,1);
-	do {
-		noack = I2C_GETSDA(dev);
-		if (!noack)
+	struct iicbb_softc *sc = (struct iicbb_softc *)device_get_softc(dev);
+	int timeout = sc->iictimeout;
+	int value;
+
+	/* 0 */
+	IICBB_SETSDA(device_get_parent(dev), 1);
+	DELAY(sc->iicdelay);
+	IICBB_SETSCL(device_get_parent(dev), 1);
+	while (1) {
+		DELAY(sc->iicdelay);
+		if (IICBB_GETSCL(device_get_parent(dev)) != 0)
 			break;
-		DELAY(1);
-		k++;
-	} while (k < timeout);
-
-	I2C_SET(sc,dev,0,1);
-	I2C_DEBUG(printf("%c ",noack?'-':'+'));
-
-	return (noack);
+		if (timeout <= 0)
+			return (-1);
+		timeout -= sc->iicdelay;
+	}
+	value = IICBB_GETSDA(device_get_parent(dev));
+	DELAY(sc->iicdelay);
+	IICBB_SETSCL(device_get_parent(dev), 0);
+	DELAY(sc->iicdelay);
+	return (value != 0 ? 1 : 0);
 }
 
-static void
-iicbb_sendbyte(device_t dev, u_char data, int timeout)
+/*
+ * Transmit a byte. Returns -1 on error.
+ */
+static int
+i2c_xmitbyte(device_t dev, int value)
 {
-	int i;
-    
-	for (i=7; i>=0; i--) {
-		if (data&(1<<i)) {
-			iicbb_one(dev, timeout);
-		} else {
-			iicbb_zero(dev, timeout);
-		}
+	int i, error;
+	
+	for (i = 7; i >= 0; i--) {
+		error = i2c_xmitbit(dev, (value >> i) & 0x01);
+		if (error < 0)
+			return (error);
 	}
-	I2C_DEBUG(printf("w%02x",(int)data));
-	return;
+	return (error);
 }
 
-static u_char
-iicbb_readbyte(device_t dev, int last, int timeout)
+/*
+ * Receive a byte. Returns the byte value (0..255), or -1 on error.
+ */
+static int
+i2c_recvbyte(device_t dev)
 {
-	struct iicbb_softc *sc = device_get_softc(dev);
-	int i;
-	unsigned char data=0;
-
-	I2C_SET(sc,dev,0,1);
-	for (i=7; i>=0; i--) 
-	{
-		I2C_SET(sc,dev,1,1);
-		if (I2C_GETSDA(dev))
-			data |= (1<<i);
-		I2C_SET(sc,dev,0,1);
-	}
-	if (last) {
-		iicbb_one(dev, timeout);
-	} else {
-		iicbb_zero(dev, timeout);
+	int i, error, value;
+	
+	value = 0;
+	for (i = 7; i >= 0; i--) {
+		error = i2c_recvbit(dev);
+		if (error < 0)
+			return (error);
+		value |= error << i;
 	}
-	I2C_DEBUG(printf("r%02x%c ",(int)data,last?'-':'+'));
-	return data;
+	return (value);
 }
 
+
 static int
 iicbb_callback(device_t dev, int index, caddr_t data)
 {
@@ -351,61 +415,53 @@ iicbb_reset(device_t dev, u_char speed, u_char addr, u_char *oldaddr)
 static int
 iicbb_start(device_t dev, u_char slave, int timeout)
 {
-	struct iicbb_softc *sc = device_get_softc(dev);
 	int error;
+	struct iicbb_softc *sc = (struct iicbb_softc *)device_get_softc(dev);
 
-	I2C_DEBUG(printf("<"));
-
-	I2C_SET(sc,dev,1,1);
-	I2C_SET(sc,dev,1,0);
-	I2C_SET(sc,dev,0,0);
-
-	/* send address */
-	iicbb_sendbyte(dev, slave, timeout);
-
-	/* check for ack */
-	if (iicbb_ack(dev, timeout)) {
-		error = IIC_ENOACK;
-		goto error;
-	}
-
-	return(0);
-
-error:
-	iicbb_stop(dev);
-	return (error);
+	I2C_LOG("<");
+
+	sc->iictimeout = timeout;
+	if (i2c_start(dev) < 0)
+		return (IIC_ENOACK);
+	error = i2c_xmitbyte(dev, slave);
+	if (error < 0)
+		return (IIC_EBUSERR); /* lost arbitration */
+	I2C_LOG("%02x", slave);
+	error = i2c_recvbit(dev);
+	if (error < 0)
+		return (IIC_EBUSERR); /* lost arbitration */
+	I2C_LOG("%c", error != 0 ? '-' : '+');
+	return(error != 0 ? IIC_ENOACK : 0);
 }
 
 static int
 iicbb_stop(device_t dev)
 {
-	struct iicbb_softc *sc = device_get_softc(dev);
-
-	I2C_SET(sc,dev,0,0);
-	I2C_SET(sc,dev,1,0);
-	I2C_SET(sc,dev,1,1);
-	I2C_DEBUG(printf(">"));
-	I2C_DEBUG(printf("\n"));
+	i2c_stop(dev);
+	I2C_LOG(">\n");
 	return (0);
 }
 
 static int
 iicbb_write(device_t dev, const char *buf, int len, int *sent, int timeout)
 {
-	int bytes, error = 0;
+	int bytes, ack, error = 0;
 
+	I2C_LOG(" W%d:", len);
 	bytes = 0;
 	while (len) {
-		/* send byte */
-		iicbb_sendbyte(dev,(u_char)*buf++, timeout);
-
-		/* check for ack */
-		if (iicbb_ack(dev, timeout)) {
+		I2C_LOG(" %02x", (u_char)*buf);
+		i2c_xmitbyte(dev,(u_char)*buf++);
+		ack = i2c_recvbit(dev);
+		if (ack < 0)
+			return (IIC_EBUSERR);
+		I2C_LOG("%c", ack != 0 ? '-' : '+');
+		if (len != 1 && ack != 0) {
 			error = IIC_ENOACK;
 			goto error;
 		}
-		bytes ++;
-		len --;
+		bytes++;
+		len--;
 	}
 
 error:
@@ -416,19 +472,31 @@ error:
 static int
 iicbb_read(device_t dev, char * buf, int len, int *read, int last, int delay)
 {
-	int bytes;
+	int bytes, value, error = 0;
 
+	I2C_LOG(" R%d:", len);
 	bytes = 0;
 	while (len) {
-		/* XXX should insert delay here */
-		*buf++ = (char)iicbb_readbyte(dev, (len == 1) ? last : 0, delay);
-
-		bytes ++;
-		len --;
+		value = i2c_recvbyte(dev);
+		if (value < 0) {
+			error = IIC_EBUSERR;
+			goto error;
+		}
+		I2C_LOG(" %02x", value);
+		error = i2c_xmitbit(dev, len == 1 && last ? 1 : 0);
+		if (value < 0) {
+			error = IIC_EBUSERR;
+			goto error;
+		}
+		I2C_LOG("%c", len == 1 && last ? '-' : '+');
+		*buf++ = value;
+		bytes++;
+		len--;
 	}
 
+error:
 	*read = bytes;
-	return (0);
+	return (error);
 }
 
 DRIVER_MODULE(iicbus, iicbb, iicbus_driver, iicbus_devclass, 0, 0);

--Apple-Mail=_80F6A1CE-E774-4603-B510-783FDD6677E0
Content-Transfer-Encoding: 7bit
Content-Type: text/plain;
	charset=iso-8859-1


-- 
Stefan Bethke <stb@lassitu.de>   Fon +49 151 14070811




--Apple-Mail=_80F6A1CE-E774-4603-B510-783FDD6677E0--



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?7FDE5802-B680-464F-8765-9353D9DDD043>