I2C: adding new "i2c bus" Command to the I2C Subsystem.
authorHeiko Schocher <hs@denx.de>
Wed, 15 Oct 2008 07:39:47 +0000 (09:39 +0200)
committerWolfgang Denk <wd@denx.de>
Sat, 18 Oct 2008 19:54:02 +0000 (21:54 +0200)
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@denx.de>
README
common/cmd_i2c.c
cpu/mpc8260/i2c.c
drivers/i2c/soft_i2c.c
include/configs/mgcoge.h
include/configs/mgsuvd.h
include/i2c.h

diff --git a/README b/README
index b10b539fa8d3362dc476a5e7b8dd20e4e5740e73..c63c72014147ab15a56a2ac1dfee0b166178445b 100644 (file)
--- 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@70 ch: 4
+               Bus ID: 3
+                 reached over Mux(es):
+                   pca9544a@70 ch: 6
+                   pca9544a@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@70 to enable
+               channel 6, and then the Commando to the mux@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
 
index ef9123ebf914d331833bca8090ae75e6c15cca98..8d287fe5fae4288147bbfb8a0b75703e07164c49 100644 (file)
@@ -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 */
+
index 335177fad61016add36745c80e3a9d2fe8c6fcb1..a96fbf841e0a90c4e4d32dc55941de539c5897c8 100644 (file)
@@ -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 */
index 63e6a7b0d110e898c0a4dfbd6f5b0f0229209d89..0a9feb67c7c5b8cd42c7fee834935547b41605b9 100644 (file)
@@ -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;
 }
 
index 398e092686d6ad16da7ec9b0f4cbb51e957f9624..6564c15d07dd5aa7dfc2c744dfe3153596e8bfa2 100644 (file)
 #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
index 20485750ea1513959ad904265421e970c90c6220..e2a7c07cc38f894c76bdb35563b95bf5482c8e81 100644 (file)
 #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
index a6e797a3835e90ec2989607cc27520c1d3df46c9..9f771dda1057ac7bfe2df90bd97b49685c40dc5f 100644 (file)
@@ -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.