From b24f119d672b709d153ff2ac091d4aa63ec6877d Mon Sep 17 00:00:00 2001
From: Ben Warren <bwarren@qstreams.com>
Date: Thu, 7 Sep 2006 16:51:04 -0400
Subject: [PATCH] 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
---
 cpu/mpc83xx/i2c.c             | 168 ++++++++++++++++++++++++++++++++++++------
 include/asm-ppc/i2c.h         |  21 +++---
 include/configs/MPC8349EMDS.h |   6 +-
 3 files changed, 160 insertions(+), 35 deletions(-)

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 @@ i2c_wait (int write)
 	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 @@ i2c_write_addr (u8 dev, u8 dir, int rsta)
 {
 	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 @@ __i2c_write (u8 *data, int length)
 	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 @@ __i2c_read (u8 *data, int length)
 
 	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 @@ __i2c_read (u8 *data, int length)
 		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 @@ i2c_read (u8 dev, uint addr, int alen, u8 *data, int length)
 	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 @@ i2c_write (u8 dev, uint addr, int alen, u8 *data, int length)
 	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 @@ void i2c_reg_write (uchar i2c_addr, uchar reg, uchar val)
 	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 @@ typedef struct i2c
 #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 @@ typedef struct i2c
 #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