
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 --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);