[PATCH 4/9] introduce-i2c.patch

Andy Green andy at openmoko.com
Tue Aug 19 16:56:22 CEST 2008


Introduce generic bitbang I2C system, and a s3c24xx-specific implementation of
it that knows which GPIO pins and how to control them.

The generic bitbang stuff exposes synchronous (ie, it will return when it is
done) and asynchronous read and write APIs, allowing the delay between bits to
be hidden in other slow, looping code if necessary.

Signed-off-by: Andy Green <andy at openmoko.com>
---

 include/i2c-bitbang-s3c24xx.h     |    3 
 include/i2c-bitbang.h             |  101 ++++++++++++++++
 src/drivers/i2c-bitbang-s3c24xx.c |   67 +++++++++++
 src/drivers/i2c-bitbang.c         |  230 +++++++++++++++++++++++++++++++++++++
 src/gta02/gta02.c                 |    2 
 5 files changed, 403 insertions(+), 0 deletions(-)
 create mode 100644 include/i2c-bitbang-s3c24xx.h
 create mode 100644 include/i2c-bitbang.h
 create mode 100644 src/drivers/i2c-bitbang-s3c24xx.c
 create mode 100644 src/drivers/i2c-bitbang.c

diff --git a/include/i2c-bitbang-s3c24xx.h b/include/i2c-bitbang-s3c24xx.h
new file mode 100644
index 0000000..6be18b9
--- /dev/null
+++ b/include/i2c-bitbang-s3c24xx.h
@@ -0,0 +1,3 @@
+#include <i2c-bitbang.h>
+
+extern struct i2c_bitbang bb_s3c24xx;
diff --git a/include/i2c-bitbang.h b/include/i2c-bitbang.h
new file mode 100644
index 0000000..0873b3b
--- /dev/null
+++ b/include/i2c-bitbang.h
@@ -0,0 +1,101 @@
+/*
+ * (C) Copyright 2007 OpenMoko, Inc.
+ * Author: Andy Green <andy at openmoko.com>
+ *
+ * Generic i2c bitbang state machine
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
+ * MA 02111-1307 USA
+ *
+ */
+
+/* controls symbol sequencing on i2c */
+
+enum i2c_bitbang_control {
+	IBCONTROL_DO_START = -1,
+	IBCONTROL_DO_STOP = -2,
+	IBCONTROL_DO_READ = -3,
+	IBCONTROL_COMPLETE = -4
+};
+
+/* control intra-bit and byte states */
+
+enum i2c_bitbang_states {
+	IBS_INIT,
+
+	IBS_START1,
+	IBS_START2,
+
+	IBS_ADS_TX_S,
+	IBS_ADS_TX_H,
+	IBS_ADS_TX_L,
+	IBS_ADS_TX_ACK_H,
+	IBS_ADS_TX_ACK_L,
+
+	IBS_DATA_RX_S,
+	IBS_DATA_RX_H,
+	IBS_DATA_RX_L,
+
+	IBS_DATA_RX_ACK_H,
+	IBS_DATA_RX_ACK_L,
+
+	IBS_STOP1,
+	IBS_STOP2,
+	IBS_STOP3
+};
+
+/* context for bitbang GPIO pins and transaction */
+
+struct i2c_bitbang {
+
+	enum i2c_bitbang_states state;
+	int count;
+	unsigned int data[8]; /* read result found here */
+	int index;
+	int index_read;
+
+	char (*read_sda)(void);
+	/* data = 0 = op low, 1 == inp */
+	void (*set)(char clock, char data);
+	/* delay > 1 half-bit time, used by i2c_complete_synchronously() */
+	void (*spin)(void);
+	void (*close)(void);
+};
+
+/* synchronous read and write functions spin until completed or failed
+ * i2c_read_sync returns -1 for fail or byte result from device
+ */
+
+extern int i2c_read_sync(struct i2c_bitbang * bb, unsigned char ads7,
+							     unsigned char reg);
+extern void i2c_write_sync(struct i2c_bitbang * bb, unsigned char ads7,
+					 unsigned char reg, unsigned char data);
+
+
+/*
+ * set up an asynchronous read or write transaction
+ */
+extern void i2c_read(struct i2c_bitbang * bb, unsigned char ads7,
+							     unsigned char reg);
+extern void i2c_write(struct i2c_bitbang * bb, unsigned char ads7,
+					 unsigned char reg, unsigned char data);
+
+/*
+ * after setting up a read or write transaction above, you loop calling this
+ * with >= 1.25us (400kHz) or >= 5us (100kHz) delay between calls.  You don't
+ * have to spin but can do something useful if you know it will take more than
+ * an i2c bit-time, hiding the time for the i2c transaction completely.
+ */
+extern int i2c_next_state(struct i2c_bitbang * bb); /* return !=0 = completed */
diff --git a/src/drivers/i2c-bitbang-s3c24xx.c b/src/drivers/i2c-bitbang-s3c24xx.c
new file mode 100644
index 0000000..353dedc
--- /dev/null
+++ b/src/drivers/i2c-bitbang-s3c24xx.c
@@ -0,0 +1,67 @@
+/*
+ * (C) Copyright 2007 OpenMoko, Inc.
+ * Author: Andy Green <andy at openmoko.com>
+ *
+ * s3c24xx-specific i2c shared by, eg, GTA02 and GTA03
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
+ * MA 02111-1307 USA
+ *
+ */
+
+#include <qi.h>
+#include <i2c-bitbang.h>
+
+static char i2c_read_sda_s3c24xx(void)
+{
+	return (rGPEDAT & 0x8000) != 0;
+}
+
+static void i2c_set_s3c24xx(char clock, char data)
+{
+	if (clock) /* SCL <- input */
+		rGPECON = (rGPECON & ~0x30000000);
+	else { /* SCL <- output 0 */
+		rGPEDAT = (rGPEDAT & ~0x4000);
+		rGPECON = (rGPECON & ~0x30000000) | 0x10000000;
+	}
+	if (data) /* SDA <- input */
+		rGPECON = (rGPECON & ~0xc0000000);
+	else { /* SDA <- output 0 */
+		rGPEDAT = (rGPEDAT & ~0x8000);
+		rGPECON = (rGPECON & ~0xc0000000) | 0x40000000;
+	}
+}
+
+static void i2c_close_s3c24xx(void)
+{
+	/* set back to hardware I2C ready for Linux */
+	rGPECON = (rGPECON & ~0xf0000000) | 0xa0000000;
+}
+
+static void i2c_spin_s3c24xx(void)
+{
+	int n;
+
+	for (n = 0; n < 700; n++)
+		rGPJDAT |= (1 << 5);
+}
+
+struct i2c_bitbang bb_s3c24xx = {
+	.read_sda = i2c_read_sda_s3c24xx,
+	.set = i2c_set_s3c24xx,
+	.spin = i2c_spin_s3c24xx,
+	.close = i2c_close_s3c24xx,
+};
diff --git a/src/drivers/i2c-bitbang.c b/src/drivers/i2c-bitbang.c
new file mode 100644
index 0000000..9762415
--- /dev/null
+++ b/src/drivers/i2c-bitbang.c
@@ -0,0 +1,230 @@
+/*
+ * (C) Copyright 2007 OpenMoko, Inc.
+ * Author: Andy Green <andy at openmoko.com>
+ *
+ * Generic i2c bitbang state machine
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
+ * MA 02111-1307 USA
+ *
+ */
+
+#include <qi.h>
+#include <i2c-bitbang.h>
+
+void i2c_read(struct i2c_bitbang * bb, unsigned char ads7, unsigned char reg)
+{
+	bb->data[0] = (ads7 << 1); /* write the register address */
+	bb->data[1] = reg;
+	bb->data[2] = IBCONTROL_DO_START;
+	bb->data[3] = (ads7 << 1) | 1; /* then issue read cycle to device */
+	bb->data[4] = IBCONTROL_DO_READ;
+	bb->data[5] = IBCONTROL_DO_STOP;
+	bb->data[6] = IBCONTROL_COMPLETE;
+	bb->state = IBS_INIT;
+}
+
+void i2c_write(struct i2c_bitbang * bb, unsigned char ads7, unsigned char reg,
+								unsigned char b)
+{
+	bb->data[0] = (ads7 << 1);
+	bb->data[1] = reg;
+	bb->data[2] = b;
+	bb->data[3] = IBCONTROL_DO_STOP;
+	bb->data[4] = IBCONTROL_COMPLETE;
+	bb->state = IBS_INIT;
+}
+
+int i2c_next_state(struct i2c_bitbang * bb)
+{
+	switch (bb->state) {
+	case IBS_INIT:
+		bb->index = 0;
+		bb->index_read = 0;
+		/* fall thru */
+	case IBS_START1:
+		(bb->set)(1, 0);
+		(bb->set)(0, 0); /* start */
+		bb->state = IBS_START2;
+		break;
+
+	case IBS_START2:
+		bb->count = 8;
+		bb->state = IBS_ADS_TX_S;
+		break;
+
+	/* transmit address or data */
+	case IBS_ADS_TX_S:
+		(bb->set)(0, !!(bb->data[bb->index] & 0x80));
+		bb->state = IBS_ADS_TX_H;
+		break;
+	case IBS_ADS_TX_H:
+		(bb->set)(1, !!(bb->data[bb->index] & 0x80));
+		bb->state = IBS_ADS_TX_L;
+		break;
+	case IBS_ADS_TX_L:
+		(bb->set)(0, !!(bb->data[bb->index] & 0x80));
+		bb->data[bb->index] <<= 1;
+		bb->count--;
+		if (bb->count) {
+			bb->state = IBS_ADS_TX_S;
+			break;
+		}
+
+		(bb->set)(0, 1);
+		bb->state = IBS_ADS_TX_ACK_H;
+		break;
+
+	case IBS_ADS_TX_ACK_H:
+		/* we finished... we expect an ack now */
+		if ((bb->read_sda)())
+			return -1;
+
+		(bb->set)(1, 1);
+		bb->state = IBS_ADS_TX_ACK_L;
+		break;
+
+	case IBS_ADS_TX_ACK_L:
+		(bb->set)(0, 1);
+
+		bb->count = 8;
+		bb->index++;
+		switch (bb->data[bb->index]) {
+		case IBCONTROL_DO_START:
+			bb->state = IBS_START1;
+			bb->index++;
+			break;
+		case IBCONTROL_DO_STOP:
+			bb->state = IBS_STOP1;
+			bb->index++;
+			break;
+		case IBCONTROL_DO_READ:
+			bb->data[bb->index_read] = 0;
+			bb->state = IBS_DATA_RX_S;
+			break;
+		case IBCONTROL_COMPLETE:
+			return 1;
+		default:
+			bb->state = IBS_ADS_TX_S; /* write it out */
+			break;
+		}
+		break;
+
+
+	/* receive data */
+	case IBS_DATA_RX_S:
+		(bb->set)(0, 1);
+		bb->state = IBS_DATA_RX_H;
+		break;
+
+	case IBS_DATA_RX_H:
+		(bb->set)(1, 1);
+		bb->state = IBS_DATA_RX_L;
+		break;
+
+	case IBS_DATA_RX_L:
+		bb->data[bb->index_read] <<= 1;
+		bb->data[bb->index_read] |= !!(bb->read_sda)();
+		(bb->set)(0, 1);
+		bb->count--;
+		if (bb->count) {
+			bb->state = IBS_DATA_RX_S;
+			break;
+		}
+
+		/* slave has released SDA now, bang down ACK */
+		(bb->set)(0, 0);
+		bb->state = IBS_DATA_RX_ACK_H;
+		break;
+
+	case IBS_DATA_RX_ACK_H:
+		(bb->set)(1, 0);
+		bb->state = IBS_DATA_RX_ACK_L;
+		break;
+
+	case IBS_DATA_RX_ACK_L:
+		(bb->set)(0, 1);
+		bb->index_read++;
+		bb->index++;
+		switch (bb->data[bb->index]) {
+		case IBCONTROL_DO_START:
+			bb->state = IBS_START1;
+			bb->index++;
+			break;
+		case IBCONTROL_DO_STOP:
+			bb->state = IBS_STOP1;
+			bb->index++;
+			break;
+		case IBCONTROL_DO_READ:
+			bb->state = IBS_DATA_RX_S;
+			bb->data[bb->index_read] = 0;
+			break;
+		case IBCONTROL_COMPLETE:
+			return 1;
+		default:
+			bb->state = IBS_ADS_TX_S; /* write it out */
+			break;
+		}
+		break;
+
+		break;
+
+
+	case IBS_STOP1:
+		(bb->set)(0, 0);
+		bb->state = IBS_STOP2;
+		break;
+
+	case IBS_STOP2:
+		(bb->set)(1, 0);
+		bb->state = IBS_STOP3;
+		break;
+
+	case IBS_STOP3:
+		(bb->set)(1, 1);
+		return 1; /* done */
+	}
+
+	return 0; /* keep going */
+}
+
+static int i2c_complete_synchronously(struct i2c_bitbang * bb)
+{
+	int ret = 0;
+
+	while (!ret) {
+		ret = i2c_next_state(bb);
+		(bb->spin)();
+	}
+
+	return ret;
+}
+
+int i2c_read_sync(struct i2c_bitbang * bb, unsigned char ads7,
+							      unsigned char reg)
+{
+	i2c_read(bb, ads7, reg);
+	if (i2c_complete_synchronously(bb) < 0)
+		return -1;
+
+	return bb->data[0];
+}
+
+void i2c_write_sync(struct i2c_bitbang * bb, unsigned char ads7,
+					     unsigned char reg, unsigned char b)
+{
+	i2c_write(bb, ads7, reg, b);
+	i2c_complete_synchronously(bb);
+}
diff --git a/src/gta02/gta02.c b/src/gta02/gta02.c
index b74b727..8bdcaa9 100644
--- a/src/gta02/gta02.c
+++ b/src/gta02/gta02.c
@@ -37,8 +37,10 @@ static const struct board_variant board_variants[] = {
 	}
 };
 
+
 void port_init_gta02(void)
 {
+
     //CAUTION:Follow the configuration order for setting the ports.
     // 1) setting value(GPnDAT)
     // 2) setting control register  (GPnCON)




More information about the openmoko-kernel mailing list