[U-Boot] [PATCH v2 13/16] I2C: adding new "i2c bus" Command to the I2C Subsystem.

Heiko Schocher hs at denx.de
Wed Oct 15 09:39:47 CEST 2008


With this Command it is possible to add new I2C Busses,
which are behind 1 .. n I2C Muxes. Details see README.

Signed-off-by: Heiko Schocher <hs at denx.de>
---
 README                   |   47 ++++++++
 common/cmd_i2c.c         |  267 ++++++++++++++++++++++++++++++++++++++++++++++
 cpu/mpc8260/i2c.c        |   15 +++-
 drivers/i2c/soft_i2c.c   |   15 +++-
 include/configs/mgcoge.h |    1 +
 include/configs/mgsuvd.h |    1 +
 include/i2c.h            |   23 ++++
 7 files changed, 367 insertions(+), 2 deletions(-)

diff --git a/README b/README
index b10b539..46353f4 100644
--- a/README
+++ b/README
@@ -1429,6 +1429,53 @@ The following options need to be configured:
 		Define this option if you want to use Freescale's I2C driver in
 		drivers/i2c/fsl_i2c.c.

+		CONFIG_I2C_MUX
+
+		Define this option if you have I2C devices reached over 1 .. n
+		I2C Muxes like the pca9544a. This option addes a new I2C
+		Command "i2c bus [muxtype:muxaddr:muxchannel]" which adds a
+		new I2C Bus to the existing I2C Busses. If you select the
+		new Bus with "i2c dev", u-bbot sends first the commandos for
+		the muxes to activate this new "bus".
+
+		CONFIG_I2C_MULTI_BUS must be also defined, to use this
+		feature!
+
+		Example:
+		Adding a new I2C Bus reached over 2 pca9544a muxes
+			The First mux with address 70 and channel 6
+			The Second mux with address 71 and channel 4
+
+		=> i2c bus pca9544a:70:6:pca9544a:71:4
+
+		Use the "i2c bus" command without parameter, to get a list
+		of I2C Busses with muxes:
+
+		=> i2c bus
+		Busses reached over muxes:
+		Bus ID: 2
+		  reached over Mux(es):
+		    pca9544a at 70 ch: 4
+		Bus ID: 3
+		  reached over Mux(es):
+		    pca9544a at 70 ch: 6
+		    pca9544a at 71 ch: 4
+		=>	
+
+		If you now switch to the new I2C Bus 3 with "i2c dev 3"
+		u-boot sends First the Commando to the mux at 70 to enable
+		channel 6, and then the Commando to the mux at 71 to enable
+		the channel 4.
+
+		After that, you can use the "normal" i2c commands as
+		usual, to communicate with your I2C devices behind
+		the 2 muxes.
+
+		This option is actually implemented for the bitbanging
+		algorithm in common/soft_i2c.c and for the Hardware I2C
+		Bus on the MPC8260. But it should be not so difficult
+		to add this option to other architectures.
+

 - SPI Support:	CONFIG_SPI

diff --git a/common/cmd_i2c.c b/common/cmd_i2c.c
index c0afd11..17cdc9f 100644
--- a/common/cmd_i2c.c
+++ b/common/cmd_i2c.c
@@ -83,7 +83,9 @@

 #include <common.h>
 #include <command.h>
+#include <environment.h>
 #include <i2c.h>
+#include <malloc.h>
 #include <asm/byteorder.h>

 /* Display values from last command.
@@ -125,6 +127,14 @@ static uchar i2c_no_probes[] = CFG_I2C_NOPROBES;
 #define NUM_ELEMENTS_NOPROBE (sizeof(i2c_no_probes)/sizeof(i2c_no_probes[0]))
 #endif

+#if defined(CONFIG_I2C_MUX)
+static I2C_MUX_DEVICE	*i2c_mux_devices = NULL;
+static	int	i2c_mux_busid = CFG_MAX_I2C_BUS;
+
+DECLARE_GLOBAL_DATA_PTR;
+
+#endif
+
 static int
 mod_i2c_mem(cmd_tbl_t *cmdtp, int incrflag, int flag, int argc, char *argv[]);

@@ -1188,6 +1198,37 @@ int do_i2c_reset(cmd_tbl_t * cmdtp, int flag, int argc, char *argv[])
 	return 0;
 }

+#if defined(CONFIG_I2C_MUX)
+int do_i2c_add_bus(cmd_tbl_t * cmdtp, int flag, int argc, char *argv[])
+{
+	int ret=0;
+
+	if (argc == 1) {
+		/* show all busses */
+		I2C_MUX		*mux;
+		I2C_MUX_DEVICE	*device = i2c_mux_devices;
+
+		printf ("Busses reached over muxes:\n");
+		while (device != NULL) {
+			printf ("Bus ID: %x\n", device->busid);
+			printf ("  reached over Mux(es):\n");
+			mux = device->mux;
+			while (mux != NULL) {
+				printf ("    %s@%x ch: %x\n", mux->name, mux->chip, mux->channel);
+				mux = mux->next;
+			}
+			device = device->next;
+		}
+	} else {
+		I2C_MUX_DEVICE *dev;
+
+		dev = i2c_mux_ident_muxstring ((uchar *)argv[1]);
+		ret = 0;
+	}
+	return ret;
+}
+#endif  /* CONFIG_I2C_MUX */
+
 #if defined(CONFIG_I2C_MULTI_BUS)
 int do_i2c_bus_num(cmd_tbl_t * cmdtp, int flag, int argc, char *argv[])
 {
@@ -1226,6 +1267,10 @@ int do_i2c_bus_speed(cmd_tbl_t * cmdtp, int flag, int argc, char *argv[])

 int do_i2c(cmd_tbl_t * cmdtp, int flag, int argc, char *argv[])
 {
+#if defined(CONFIG_I2C_MUX)
+	if (!strncmp(argv[1], "bu", 2))
+		return do_i2c_add_bus(cmdtp, flag, --argc, ++argv);
+#endif  /* CONFIG_I2C_MUX */
 	if (!strncmp(argv[1], "sp", 2))
 		return do_i2c_bus_speed(cmdtp, flag, --argc, ++argv);
 #if defined(CONFIG_I2C_MULTI_BUS)
@@ -1264,6 +1309,9 @@ int do_i2c(cmd_tbl_t * cmdtp, int flag, int argc, char *argv[])
 U_BOOT_CMD(
 	i2c, 6, 1, do_i2c,
 	"i2c     - I2C sub-system\n",
+#if defined(CONFIG_I2C_MUX)
+	"bus [muxtype:muxaddr:muxchannel] - add a new bus reached over muxes.\n"
+#endif  /* CONFIG_I2C_MUX */
 	"speed [speed] - show or set I2C bus speed\n"
 #if defined(CONFIG_I2C_MULTI_BUS)
 	"i2c dev [dev] - show or set current I2C bus\n"
@@ -1335,3 +1383,222 @@ U_BOOT_CMD(
 	"      (valid chip values 50..57)\n"
 );
 #endif
+
+#if defined(CONFIG_I2C_MUX)
+
+int i2c_mux_add_device(I2C_MUX_DEVICE *dev)
+{
+	I2C_MUX_DEVICE	*devtmp = i2c_mux_devices;
+
+	if (i2c_mux_devices == NULL) {
+		i2c_mux_devices = dev;
+		return 0;
+	}
+	while (devtmp->next != NULL)
+		devtmp = devtmp->next;
+
+	devtmp->next = dev;
+	return 0;
+}
+
+I2C_MUX_DEVICE	*i2c_mux_search_device(int id)
+{
+	I2C_MUX_DEVICE	*device = i2c_mux_devices;
+
+	while (device != NULL) {
+		if (device->busid == id)
+			return device;
+		device = device->next;
+	}
+	return NULL;
+}
+
+/* searches in the buf from *pos the next ':'.
+ * returns:
+ *     0 if found (with *pos = where)
+ *   < 0 if an error occured
+ *   > 0 if the end of buf is reached
+ */
+static int i2c_mux_search_next (int *pos, uchar	*buf, int len)
+{
+	while ((buf[*pos] != ':') && (*pos < len)) {
+		*pos += 1;
+	}
+	if (*pos >= len)
+		return 1;
+	if (buf[*pos] != ':')
+		return -1;
+	return 0;
+}
+
+static int i2c_mux_get_busid (void)
+{
+	int	tmp = i2c_mux_busid;
+
+	i2c_mux_busid ++;
+	return tmp;
+}
+
+/* Analyses a Muxstring and sends immediately the
+   Commands to the Muxes. Runs from Flash.
+ */
+int i2c_mux_ident_muxstring_f (uchar *buf)
+{
+	int	pos = 0;
+	int	oldpos;
+	int	ret = 0;
+	int	len = strlen((char *)buf);
+	int	chip;
+	uchar	channel;
+	int	was = 0;
+
+	while (ret == 0) {
+		oldpos = pos;
+		/* search name */
+		ret = i2c_mux_search_next(&pos, buf, len);
+		if (ret != 0)
+			printf ("ERROR\n");
+		/* search address */
+		pos ++;
+		oldpos = pos;
+		ret = i2c_mux_search_next(&pos, buf, len);
+		if (ret != 0)
+			printf ("ERROR\n");
+		buf[pos] = 0;
+		chip = simple_strtoul((char *)&buf[oldpos], NULL, 16);
+		buf[pos] = ':';
+		/* search channel */
+		pos ++;
+		oldpos = pos;
+		ret = i2c_mux_search_next(&pos, buf, len);
+		if (ret < 0)
+			printf ("ERROR\n");
+		was = 0;
+		if (buf[pos] != 0) {
+			buf[pos] = 0;
+			was = 1;
+		}
+		channel = simple_strtoul((char *)&buf[oldpos], NULL, 16);
+		if (was)
+			buf[pos] = ':';
+		if (i2c_write(chip, 0, 0, &channel, 1) != 0) {
+			printf ("Error setting Mux: chip:%x channel: \
+				%x\n", chip, channel);
+			return -1;
+		}
+		pos ++;
+		oldpos = pos;
+		
+	}
+	
+	return 0;
+}
+
+/* Analyses a Muxstring and if this String is correct
+ * adds a new I2C Bus.
+ */
+I2C_MUX_DEVICE *i2c_mux_ident_muxstring (uchar *buf)
+{
+	I2C_MUX_DEVICE	*device;
+	I2C_MUX		*mux;
+	int	pos = 0;
+	int	oldpos;
+	int	ret = 0;
+	int	len = strlen((char *)buf);
+	int	was = 0;
+
+	device = (I2C_MUX_DEVICE *)malloc (sizeof(I2C_MUX_DEVICE));
+	device->mux = NULL;
+	device->busid = i2c_mux_get_busid ();
+	device->next = NULL;
+	while (ret == 0) {
+		mux = (I2C_MUX *)malloc (sizeof(I2C_MUX));
+		mux->next = NULL;
+		/* search name of mux */
+		oldpos = pos;
+		ret = i2c_mux_search_next(&pos, buf, len);
+		if (ret != 0)
+			printf ("%s no name.\n", __FUNCTION__);
+		mux->name = (char *)malloc (pos - oldpos + 1);
+		memcpy (mux->name, &buf[oldpos], pos - oldpos);
+		mux->name[pos - oldpos] = 0;
+		/* search address */
+		pos ++;
+		oldpos = pos;
+		ret = i2c_mux_search_next(&pos, buf, len);
+		if (ret != 0)
+			printf ("%s no mux address.\n", __FUNCTION__);
+		buf[pos] = 0;
+		mux->chip = simple_strtoul((char *)&buf[oldpos], NULL, 16);
+		buf[pos] = ':';
+		/* search channel */
+		pos ++;
+		oldpos = pos;
+		ret = i2c_mux_search_next(&pos, buf, len);
+		if (ret < 0)
+			printf ("%s no mux channel.\n", __FUNCTION__);
+		was = 0;
+		if (buf[pos] != 0) {
+			buf[pos] = 0;
+			was = 1;
+		}
+		mux->channel = simple_strtoul((char *)&buf[oldpos], NULL, 16);
+		if (was)
+			buf[pos] = ':';
+		if (device->mux == NULL)
+			device->mux = mux;
+		else {
+			I2C_MUX		*muxtmp = device->mux;
+			while (muxtmp->next != NULL) {
+				muxtmp = muxtmp->next;
+			}
+			muxtmp->next = mux;
+		}
+		pos ++;
+		oldpos = pos;
+	}
+	if (ret > 0) {
+		/* Add Device */
+		i2c_mux_add_device (device);
+		return device;
+	}
+
+	return NULL;
+}
+
+int i2x_mux_select_mux(int bus)
+{
+	I2C_MUX_DEVICE  *dev;
+	I2C_MUX		*mux;
+
+	if ((gd->flags & GD_FLG_RELOC) != GD_FLG_RELOC) {
+		/* select Default Mux Bus */
+#if defined(CFG_I2C_IVM_BUS)
+		i2c_mux_ident_muxstring_f ((uchar *)CFG_I2C_IVM_BUS);
+#else
+		{
+		unsigned char *buf;
+		buf = (unsigned char *) getenv("EEprom_ivm");
+		if (buf != NULL)
+			i2c_mux_ident_muxstring_f (buf);
+		}
+#endif
+		return 0;
+	}
+	dev = i2c_mux_search_device(bus);
+	if (dev == NULL)
+		return -1;
+
+	mux = dev->mux;
+	while (mux != NULL) {
+		if (i2c_write(mux->chip, 0, 0, &mux->channel, 1) != 0) {
+			printf ("Error setting Mux: chip:%x channel: \
+				%x\n", mux->chip, mux->channel);
+			return -1;
+		}
+		mux = mux->next;
+	}
+	return 0;
+}
+#endif /* CONFIG_I2C_MUX */
+
diff --git a/cpu/mpc8260/i2c.c b/cpu/mpc8260/i2c.c
index 335177f..a96fbf8 100644
--- a/cpu/mpc8260/i2c.c
+++ b/cpu/mpc8260/i2c.c
@@ -780,10 +780,23 @@ unsigned int i2c_get_bus_num(void)

 int i2c_set_bus_num(unsigned int bus)
 {
+#if defined(CONFIG_I2C_MUX)
+	if (bus < CFG_MAX_I2C_BUS) {
+		i2c_bus_num = bus;
+	} else {
+		int	ret;
+
+		ret = i2x_mux_select_mux(bus);
+		if (ret == 0)
+			i2c_bus_num = bus;
+		else
+			return ret;
+	}
+#else
 	if (bus >= CFG_MAX_I2C_BUS)
 		return -1;
 	i2c_bus_num = bus;
-
+#endif
 	return 0;
 }
 /* TODO: add 100/400k switching */
diff --git a/drivers/i2c/soft_i2c.c b/drivers/i2c/soft_i2c.c
index 63e6a7b..0a9feb6 100644
--- a/drivers/i2c/soft_i2c.c
+++ b/drivers/i2c/soft_i2c.c
@@ -223,10 +223,23 @@ unsigned int i2c_get_bus_num(void)

 int i2c_set_bus_num(unsigned int bus)
 {
+#if defined(CONFIG_I2C_MUX)
+	if (bus < CFG_MAX_I2C_BUS) {
+		i2c_bus_num = bus;
+	} else {
+		int	ret;
+
+		ret = i2x_mux_select_mux(bus);
+		if (ret == 0)
+			i2c_bus_num = bus;
+		else
+			return ret;
+	}
+#else
 	if (bus >= CFG_MAX_I2C_BUS)
 		return -1;
 	i2c_bus_num = bus;
-
+#endif
 	return 0;
 }

diff --git a/include/configs/mgcoge.h b/include/configs/mgcoge.h
index 398e092..6564c15 100644
--- a/include/configs/mgcoge.h
+++ b/include/configs/mgcoge.h
@@ -203,6 +203,7 @@
 #define CONFIG_I2C_CMD_TREE	1
 #define CFG_MAX_I2C_BUS		2
 #define CFG_I2C_INIT_BOARD	1
+#define CONFIG_I2C_MUX		1

 /* EEprom support */
 #define CFG_I2C_EEPROM_ADDR_LEN	1
diff --git a/include/configs/mgsuvd.h b/include/configs/mgsuvd.h
index 2048575..e2a7c07 100644
--- a/include/configs/mgsuvd.h
+++ b/include/configs/mgsuvd.h
@@ -374,6 +374,7 @@
 #define CONFIG_I2C_CMD_TREE	1
 #define CFG_MAX_I2C_BUS		2
 #define CFG_I2C_INIT_BOARD	1
+#define CONFIG_I2C_MUX		1

 /* EEprom support */
 #define CFG_I2C_EEPROM_ADDR_LEN	1
diff --git a/include/i2c.h b/include/i2c.h
index a6e797a..9f771dd 100644
--- a/include/i2c.h
+++ b/include/i2c.h
@@ -85,6 +85,29 @@ void i2c_init(int speed, int slaveaddr);
 void i2c_init_board(void);
 #endif

+#if defined(CONFIG_I2C_MUX)
+
+typedef struct _mux {
+	uchar	chip;
+	uchar	channel;
+	char	*name;
+	struct _mux	*next;
+} I2C_MUX;
+
+typedef struct _mux_device {
+	int	busid;
+	I2C_MUX	*mux;	/* List of muxes, to reach the device */
+	struct _mux_device	*next;
+} I2C_MUX_DEVICE;
+
+int	i2c_mux_add_device(I2C_MUX_DEVICE *dev);
+
+I2C_MUX_DEVICE	*i2c_mux_search_device(int id);
+I2C_MUX_DEVICE *i2c_mux_ident_muxstring (uchar *buf);
+int i2x_mux_select_mux(int bus);
+int i2c_mux_ident_muxstring_f (uchar *buf);
+#endif
+
 /*
  * Probe the given I2C chip address.  Returns 0 if a chip responded,
  * not 0 on failure.
-- 
1.5.6.1

-- 
DENX Software Engineering GmbH,     MD: Wolfgang Denk & Detlev Zundel
HRB 165235 Munich, Office: Kirchenstr.5, D-82194 Groebenzell, Germany


More information about the U-Boot mailing list