Multi-bus I2C implementation of MPC834x

Hello,

Attached is a patch implementing multiple I2C buses on the MPC834x CPU
family and the MPC8349EMDS board in particular.
This patch requires Patch 1 (Add support for multiple I2C buses).
Testing was performed on a 533MHz board.

/*** Note: This patch replaces ticket DNX#2006083042000027 ***/

Signed-off-by: Ben Warren <bwarren@qstreams.com>

CHANGELOG:
        Implemented driver-level code to support two I2C buses on the
MPC834x CPU family and the MPC8349EMDS board.  Available I2C bus speeds
are 50kHz, 100kHz and 400kHz on each bus.

regards,
Ben
diff --git a/cpu/mpc83xx/i2c.c b/cpu/mpc83xx/i2c.c
index 723feeb..fc89fb1 100644
--- a/cpu/mpc83xx/i2c.c
+++ b/cpu/mpc83xx/i2c.c
@@ -45,37 +45,123 @@
 #include <i2c.h>
 #include <asm/i2c.h>
 
-#if defined(CONFIG_MPC8349EMDS) || defined(CONFIG_TQM834X)
-i2c_t * mpc83xx_i2c = (i2c_t*)(CFG_IMMRBAR + CFG_I2C_OFFSET);
+DECLARE_GLOBAL_DATA_PTR;
+
+/* Three I2C bus speeds are supported here (50kHz, 100kHz
+ * and 400kHz).  It should be easy to add more.  Note that
+ * the maximum bus speed for I2C bus 1 is CSB/3, while I2C
+ * bus 2 can go as high as CSB.
+ * Typical values for CSB are 266MHz and 200MHz.  */
+
+                                                                      /* 50kH  100kHz  400kHz */
+static const uchar speed_map_266[][3] =
+                                                                      {{0x2e,         0x2a,   0x20},  /* base 88MHz */
+                                                                       {0x34,         0x30,   0x28}}; /* base 266 MHz */
+
+static const uchar speed_map_200[][3] =
+                                                                      {{0x2c,         0x28,   0x20},  /* base 66 MHz */
+                                                                       {0x33,         0x2f,   0x26}}; /* base 200 MHz */
+
+/* Initialize the bus pointer to whatever one the SPD EEPROM is on.
+ * Default is bus 1.  This is necessary because the DDR initialization
+ * runs from ROM, and we can't switch buses because we can't modify
+ * the i2c_dev variable.  Everything gets straightened out once i2c_init
+ * is called from RAM.  */
+
+#if defined CFG_SPD_BUS_NUM
+static i2c_t *i2c_dev = CFG_SPD_BUS_NUM;
+#else
+static i2c_t *i2c_dev = I2C_1;
 #endif
 
-void
-i2c_init(int speed, int slaveadd)
+static uchar busNum = I2C_BUS_1 ;
+static int bus_speed[2] = {0, 0};
+
+static int set_speed(int speed)
+{
+      uchar value;
+      const uchar *spdPtr;
+
+      /* Global data contains maximum I2C bus 1 speed, which is CSB/3 */
+      if(gd->i2c_clk == 88000000)
+      {
+              spdPtr = speed_map_266[busNum];
+      }
+      else if(gd->i2c_clk == 66000000)
+      {
+              spdPtr = speed_map_200[busNum];
+      }
+      else
+      {
+              printf("Max I2C bus speed %d not supported\n", gd->i2c_clk);
+              return -1;
+      }
+
+      switch(speed)
+      {
+              case 50000:
+                      value   = *(spdPtr + 0);
+                      break;
+              case 100000:
+                      value   = *(spdPtr + 1);
+                      break;
+              case 400000:
+                      value   = *(spdPtr + 2);
+                      break;
+              default:
+                      printf("I2C bus speed %d not supported\n", speed);
+                      return -2;
+      }
+      /* set clock */
+      writeb(value, &i2c_dev->fdr);
+      bus_speed[busNum] = speed;
+      return 0;
+}
+
+
+static void _i2c_init(int speed, int slaveadd)
 {
 	/* stop I2C controller */
-	writeb(0x00 , &I2C->cr);
+	writeb(0x00 , &i2c_dev->cr);
 
 	/* set clock */
-	writeb(speed, &I2C->fdr);
+	writeb(speed, &i2c_dev->fdr);
 
 	/* set default filter */
-	writeb(0x10,&I2C->dfsrr);
+	writeb(0x10,&i2c_dev->dfsrr);
 
 	/* write slave address */
-	writeb(slaveadd, &I2C->adr);
+	writeb(slaveadd, &i2c_dev->adr);
 
 	/* clear status register */
-	writeb(0x00, &I2C->sr);
+	writeb(0x00, &i2c_dev->sr);
 
 	/* start I2C controller */
-	writeb(I2C_CR_MEN, &I2C->cr);
+	writeb(I2C_CR_MEN, &i2c_dev->cr);
+}
+
+void i2c_init(int speed, int slaveadd)
+{
+      /* Set both interfaces to the same speed and slave address */
+      /* Note: This function gets called twice - before and after
+       * relocation to RAM.  The first time it's called, we are unable
+       * to change buses, so whichever one 'i2c_dev' was initialized to
+       * gets set twice.  When run from RAM both buses get set properly */
+
+      i2c_set_bus_num(I2C_BUS_1);
+      _i2c_init(speed, slaveadd);
+#ifdef        CFG_I2C2_OFFSET
+      i2c_set_bus_num(I2C_BUS_2);
+      _i2c_init(speed, slaveadd);
+      i2c_set_bus_num(I2C_BUS_1);
+#endif        /* CFG_I2C2_OFFSET */
 }
 
 static __inline__ int
 i2c_wait4bus (void)
 {
 	ulong timeval = get_timer (0);
-	while (readb(&I2C->sr) & I2C_SR_MBB) {
+	while (readb(&i2c_dev->sr) & I2C_SR_MBB) {
 		if (get_timer (timeval) > I2C_TIMEOUT) {
 			return -1;
 		}
@@ -89,12 +175,12 @@
 	u32 csr;
 	ulong timeval = get_timer(0);
 	do {
-		csr = readb(&I2C->sr);
+		csr = readb(&i2c_dev->sr);
 
 		if (!(csr & I2C_SR_MIF))
 			continue;
 
-		writeb(0x0, &I2C->sr);
+		writeb(0x0, &i2c_dev->sr);
 
 		if (csr & I2C_SR_MAL) {
 			debug("i2c_wait: MAL\n");
@@ -123,9 +209,9 @@
 {
 	writeb(I2C_CR_MEN | I2C_CR_MSTA | I2C_CR_MTX |
 	       (rsta?I2C_CR_RSTA:0),
-	       &I2C->cr);
+	       &i2c_dev->cr);
 
-	writeb((dev << 1) | dir, &I2C->dr);
+	writeb((dev << 1) | dir, &i2c_dev->dr);
 
 	if (i2c_wait (I2C_WRITE) < 0)
 		return 0;
@@ -138,10 +224,10 @@
 	int i;
 
 	writeb(I2C_CR_MEN | I2C_CR_MSTA | I2C_CR_MTX,
-	       &I2C->cr);
+	       &i2c_dev->cr);
 
 	for (i=0; i < length; i++) {
-		writeb(data[i], &I2C->dr);
+		writeb(data[i], &i2c_dev->dr);
 
 		if (i2c_wait (I2C_WRITE) < 0)
 			break;
@@ -156,10 +242,10 @@
 
 	writeb(I2C_CR_MEN | I2C_CR_MSTA |
 	       ((length == 1) ? I2C_CR_TXAK : 0),
-	       &I2C->cr);
+	       &i2c_dev->cr);
 
 	/* dummy read */
-	readb(&I2C->dr);
+	readb(&i2c_dev->dr);
 
 	for (i=0; i < length; i++) {
 		if (i2c_wait (I2C_READ) < 0)
@@ -169,13 +255,13 @@
 		if (i == length - 2)
 			writeb(I2C_CR_MEN | I2C_CR_MSTA |
 			       I2C_CR_TXAK,
-			       &I2C->cr);
+			       &i2c_dev->cr);
 
 		/* Generate stop on last byte */
 		if (i == length - 1)
-			writeb(I2C_CR_MEN | I2C_CR_TXAK, &I2C->cr);
+			writeb(I2C_CR_MEN | I2C_CR_TXAK, &i2c_dev->cr);
 
-		data[i] = readb(&I2C->dr);
+		data[i] = readb(&i2c_dev->dr);
 	}
 	return i;
 }
@@ -201,7 +287,7 @@
 	i = __i2c_read (data, length);
 
  exit:
-	writeb(I2C_CR_MEN, &I2C->cr);
+	writeb(I2C_CR_MEN, &i2c_dev->cr);
 	return !(i == length);
 }
 
@@ -223,7 +309,7 @@
 	i = __i2c_write (data, length);
 
  exit:
-	writeb(I2C_CR_MEN, &I2C->cr);
+	writeb(I2C_CR_MEN, &i2c_dev->cr);
 	return !(i == length);
 }
 
@@ -254,4 +340,38 @@
 	i2c_write (i2c_addr, reg, 1, &val, 1);
 }
 
+int 	i2c_set_bus_num(uchar bus)
+{
+	if(bus == I2C_BUS_1)
+	{
+		i2c_dev = I2C_1;
+	}
+#ifdef	CFG_I2C2_OFFSET
+	else if(bus == I2C_BUS_2)
+	{
+		i2c_dev = I2C_2;
+	}
+#endif	/* CFG_I2C2_OFFSET */
+	else
+	{
+		return -1;
+	}
+	busNum = bus;
+	return 0;
+}
+
+int 	i2c_set_bus_speed(int speed)
+{
+	return set_speed(speed);
+}
+
+uchar i2c_get_bus_num(void)
+{
+	return busNum;
+}
+
+int 	i2c_get_bus_speed(void)
+{
+	return bus_speed[busNum];
+}
 #endif /* CONFIG_HARD_I2C */
diff --git a/include/asm-ppc/i2c.h b/include/asm-ppc/i2c.h
index 2ae3367..baf9d9a 100644
--- a/include/asm-ppc/i2c.h
+++ b/include/asm-ppc/i2c.h
@@ -79,6 +79,12 @@
 #endif
 #define I2C_TIMEOUT (CFG_HZ/4)
 
+enum	I2C_BUS_NUM
+{
+	I2C_BUS_1 = 0,
+	I2C_BUS_2,
+};
+
 #ifndef CFG_IMMRBAR
 #error CFG_IMMRBAR is not defined in /include/configs/${BOARD}.h
 #endif
@@ -87,15 +93,12 @@
 #error CFG_I2C_OFFSET is not defined in /include/configs/${BOARD}.h
 #endif
 
-#if defined(CONFIG_MPC8349EMDS) || defined(CONFIG_TQM834X)
-/*
- * MPC8349 have two i2c bus
- */
-extern i2c_t * mpc83xx_i2c;
-#define I2C mpc83xx_i2c
-#else
-#define I2C ((i2c_t*)(CFG_IMMRBAR + CFG_I2C_OFFSET))
-#endif
+#define I2C_1 ((i2c_t*)(CFG_IMMRBAR + CFG_I2C_OFFSET))
+
+/* Optional support for second I2C bus */
+#ifdef        CFG_I2C2_OFFSET
+#define I2C_2 ((i2c_t*)(CFG_IMMRBAR + CFG_I2C2_OFFSET))
+#endif        /* CFG_I2C2_OFFSET */
 
 #define I2C_READ  1
 #define I2C_WRITE 0
diff --git a/include/configs/MPC8349EMDS.h b/include/configs/MPC8349EMDS.h
index 66f1646..4d5ad57 100644
--- a/include/configs/MPC8349EMDS.h
+++ b/include/configs/MPC8349EMDS.h
@@ -35,7 +35,7 @@
  * High Level Configuration Options
  */
 #define CONFIG_E300		1	/* E300 Family */
-#define CONFIG_MPC83XX		1	/* MPC83XX family */
+#define CONFIG_MPC834X		1	/* MPC834X family */
 #define CONFIG_MPC8349		1	/* MPC8349 specific */
 #define CONFIG_MPC8349EMDS	1	/* MPC8349EMDS board specific */
 
@@ -311,9 +311,11 @@
 /* I2C */
 #define CONFIG_HARD_I2C			/* I2C with hardware support*/
 #undef CONFIG_SOFT_I2C			/* I2C bit-banged */
+#define CONFIG_I2C_MULTI_BUS
+#define CONFIG_I2C_CMD_TREE
 #define CFG_I2C_SPEED		400000	/* I2C speed and slave address */
 #define CFG_I2C_SLAVE		0x7F
-#define CFG_I2C_NOPROBES	{0x69}	/* Don't probe these addrs */
+#define CFG_I2C_NOPROBES	{{0,0x69}}	/* Don't probe these addrs */
 #define CFG_I2C_OFFSET		0x3000
 #define CFG_I2C2_OFFSET		0x3100