diff mbox

[U-Boot,v2,15/21] keymile, common; fix i2c deblocking support

Message ID 1300179844-8951-16-git-send-email-hs@denx.de
State Superseded
Headers show

Commit Message

Heiko Schocher March 15, 2011, 9:03 a.m. UTC
This patch fix the i2c deblocking facility with the i2c HW-Controller.
The required delays for byte reading, the enhanced criteria for stop
the dummy read and required 5 start/stop sequences are added.

Add i2c deblocking before ivm eeprom read.

Improve i2c deblocking sequence by respecting stop hold time.

Cleaned function for deblocking. Have now one function i2c_make_abort()
available for bitbang, mpc82xx and mpc83xx harware controller.

Signed-off-by: Stefan Bigler <stefan.bigler@keymile.com>
Signed-off-by: Holger Brunck <holger.brunck@keymile.com>
Signed-off-by: Heiko Schocher <hs@denx.de>
cc: Valentin Longchamp <valentin.longchamp@keymile.com>
---
Changes for v2:
  - fix checkpatch.pl errors and warnings

 board/keymile/common/common.c |  148 ++++++++++++++++++++++++++++-------------
 board/keymile/common/common.h |    6 +-
 2 files changed, 105 insertions(+), 49 deletions(-)
diff mbox

Patch

diff --git a/board/keymile/common/common.c b/board/keymile/common/common.c
index e84c214..5bb18a8 100644
--- a/board/keymile/common/common.c
+++ b/board/keymile/common/common.c
@@ -325,6 +325,9 @@  int ivm_read_eeprom (void)
 	if (buf != NULL)
 		dev_addr = simple_strtoul ((char *)buf, NULL, 16);
 
+	/* add deblocking here */
+	i2c_make_abort();
+
 	if (i2c_read(dev_addr, 0, 1, i2c_buffer, CONFIG_SYS_IVM_EEPROM_MAX_LEN) != 0) {
 		printf ("Error reading EEprom\n");
 		return -2;
@@ -334,7 +337,7 @@  int ivm_read_eeprom (void)
 }
 
 #if defined(CONFIG_SYS_I2C_INIT_BOARD)
-#define DELAY_ABORT_SEQ		62
+#define DELAY_ABORT_SEQ		62  /* @200kHz 9 clocks = 44us, 62us is ok */
 #define DELAY_HALF_PERIOD	(500 / (CONFIG_SYS_I2C_SPEED / 1000))
 
 #if defined(CONFIG_MGCOGE) || defined(CONFIG_MGCOGE2NE)
@@ -426,16 +429,16 @@  static int get_scl (void)
 #endif
 
 #if !defined(CONFIG_MPC83xx)
-static void writeStartSeq (void)
+void writeStartSeq(void)
 {
-	set_sda (1);
-	udelay (DELAY_HALF_PERIOD);
-	set_scl (1);
-	udelay (DELAY_HALF_PERIOD);
-	set_sda (0);
-	udelay (DELAY_HALF_PERIOD);
-	set_scl (0);
-	udelay (DELAY_HALF_PERIOD);
+	set_sda(1);
+	udelay(DELAY_HALF_PERIOD);
+	set_scl(1);
+	udelay(DELAY_HALF_PERIOD);
+	set_sda(0);
+	udelay(DELAY_HALF_PERIOD);
+	set_scl(0);
+	udelay(DELAY_HALF_PERIOD);
 }
 
 /* I2C is a synchronous protocol and resets of the processor in the middle
@@ -445,14 +448,27 @@  static void writeStartSeq (void)
    This I2C Deblocking mechanism was developed by Keymile in association
    with Anatech and Atmel in 1998.
  */
-static int i2c_make_abort (void)
+int i2c_make_abort(void)
 {
+
+#if defined(CONFIG_HARD_I2C) && !defined(MACH_TYPE_KM_KIRKWOOD)
+	volatile immap_t *immap = (immap_t *)CONFIG_SYS_IMMR ;
+	volatile i2c8260_t *i2c	= (i2c8260_t *)&immap->im_i2c;
+
+	/* disable I2C controller first, otherwhise it thinks we want to    */
+	/* talk to the slave port...                                        */
+	i2c->i2c_i2mod &= ~0x01;
+
+	/* Set the PortPins to GPIO */
+	setports(1);
+#endif
+
 	int	scl_state = 0;
 	int	sda_state = 0;
 	int	i = 0;
 	int	ret = 0;
 
-	if (!get_sda ()) {
+	if (!get_sda()) {
 		ret = -1;
 		while (i < 16) {
 			i++;
@@ -469,58 +485,96 @@  static int i2c_make_abort (void)
 		}
 	}
 	if (ret == 0) {
-		for (i =0; i < 5; i++) {
+		for (i = 0; i < 5; i++)
 			writeStartSeq ();
-		}
 	}
-	get_sda ();
+
+	/* respect stop setup time */
+	udelay(DELAY_ABORT_SEQ);
+	set_scl(1);
+	udelay(DELAY_ABORT_SEQ);
+	set_sda(1);
+	get_sda();
+
+#if defined(CONFIG_HARD_I2C)
+	/* Set the PortPins back to use for I2C */
+	setports(0);
+#endif
 	return ret;
 }
 #endif
 
-/**
- * i2c_init_board - reset i2c bus. When the board is powercycled during a
- * bus transfer it might hang; for details see doc/I2C_Edge_Conditions.
- */
-void i2c_init_board(void)
-{
 #if defined(CONFIG_MPC83xx)
+void writeStartSeq(void)
+{
 	struct fsl_i2c *dev;
 	dev = (struct fsl_i2c *) (CONFIG_SYS_IMMR + CONFIG_SYS_I2C_OFFSET);
-	uchar	dummy;
+	udelay(DELAY_ABORT_SEQ);
+	out_8(&dev->cr, (I2C_CR_MEN | I2C_CR_MSTA));
+	udelay(DELAY_ABORT_SEQ);
+	out_8(&dev->cr, (I2C_CR_MEN));
+}
 
-	out_8 (&dev->cr, (I2C_CR_MSTA));
-	out_8 (&dev->cr, (I2C_CR_MEN | I2C_CR_MSTA));
-	dummy = in_8(&dev->dr);
+int i2c_make_abort(void)
+{
+	struct fsl_i2c *dev;
+	dev = (struct fsl_i2c *) (CONFIG_SYS_IMMR + CONFIG_SYS_I2C_OFFSET);
+	uchar	dummy;
+	uchar   last;
+	int     nbr_read = 0;
+	int     i = 0;
+	int	    ret = 0;
+
+	/* wait after each operation to finsh with a delay */
+	out_8(&dev->cr, (I2C_CR_MSTA));
+	udelay(DELAY_ABORT_SEQ);
+	out_8(&dev->cr, (I2C_CR_MEN | I2C_CR_MSTA));
+	udelay(DELAY_ABORT_SEQ);
 	dummy = in_8(&dev->dr);
-	if (dummy != 0xff) {
-		dummy = in_8(&dev->dr);
+	udelay(DELAY_ABORT_SEQ);
+	last = in_8(&dev->dr);
+	nbr_read++;
+
+	/*
+	 * do read until the last bit is 1, but stop if the full eeprom is
+	 * read.
+	 */
+	while (((last & 0x01) != 0x01) &&
+		(nbr_read < CONFIG_SYS_IVM_EEPROM_MAX_LEN)) {
+		udelay(DELAY_ABORT_SEQ);
+		last = in_8(&dev->dr);
+		nbr_read++;
 	}
-	out_8 (&dev->cr, (I2C_CR_MEN));
-	out_8 (&dev->cr, 0x00);
-	out_8 (&dev->cr, (I2C_CR_MEN));
-
-#else
-#if defined(CONFIG_HARD_I2C) && !defined(MACH_TYPE_KM_KIRKWOOD)
-	volatile immap_t *immap = (immap_t *)CONFIG_SYS_IMMR ;
-	volatile i2c8260_t *i2c	= (i2c8260_t *)&immap->im_i2c;
+	if ((last & 0x01) != 0x01)
+		ret = -2;
+	if ((last != 0xff) || (nbr_read > 1))
+		printf("[INFO] i2c abort after %d bytes (0x%02x)\n",
+			nbr_read, last);
+	udelay(DELAY_ABORT_SEQ);
+	out_8(&dev->cr, (I2C_CR_MEN));
+	udelay(DELAY_ABORT_SEQ);
+	/* clear status reg */
+	out_8(&dev->sr, 0);
+
+	for (i = 0; i < 5; i++)
+		writeStartSeq();
+	if (ret != 0)
+		printf("[ERROR] i2c abort failed after %d bytes (0x%02x)\n",
+			nbr_read, last);
 
-	/* disable I2C controller first, otherwhise it thinks we want to    */
-	/* talk to the slave port...                                        */
-	i2c->i2c_i2mod &= ~0x01;
+	return ret;
+}
 
-	/* Set the PortPins to GPIO */
-	setports (1);
 #endif
 
+/**
+ * i2c_init_board - reset i2c bus. When the board is powercycled during a
+ * bus transfer it might hang; for details see doc/I2C_Edge_Conditions.
+ */
+void i2c_init_board(void)
+{
 	/* Now run the AbortSequence() */
-	i2c_make_abort ();
-
-#if defined(CONFIG_HARD_I2C)
-	/* Set the PortPins back to use for I2C */
-	setports (0);
-#endif
-#endif
+	i2c_make_abort();
 }
 #endif
 #endif
diff --git a/board/keymile/common/common.h b/board/keymile/common/common.h
index a38c727..8fabe77 100644
--- a/board/keymile/common/common.h
+++ b/board/keymile/common/common.h
@@ -11,8 +11,10 @@ 
 #ifndef __KEYMILE_COMMON_H
 #define __KEYMILE_COMMON_H
 
-int ethernet_present (void);
-int ivm_read_eeprom (void);
+int ethernet_present(void);
+int ivm_read_eeprom(void);
+void writeStartSeq(void);
+int i2c_make_abort(void);
 
 #ifdef CONFIG_KEYMILE_HDLC_ENET
 int keymile_hdlc_enet_initialize (bd_t *bis);