diff options
Diffstat (limited to 'drivers/i2c')
-rw-r--r-- | drivers/i2c/mxc_i2c.c | 210 |
1 files changed, 150 insertions, 60 deletions
diff --git a/drivers/i2c/mxc_i2c.c b/drivers/i2c/mxc_i2c.c index 8e10fbb..1f36970 100644 --- a/drivers/i2c/mxc_i2c.c +++ b/drivers/i2c/mxc_i2c.c @@ -3,6 +3,8 @@ * * (c) 2007 Pengutronix, Sascha Hauer <s.hauer@pengutronix.de> * + * (C) Copyright 2008-2009 Freescale Semiconductor, Inc. + * * See file CREDITS for list of people who contributed to this * project. * @@ -26,8 +28,14 @@ #if defined(CONFIG_HARD_I2C) +#ifdef CONFIG_MX31 #include <asm/arch/mx31.h> #include <asm/arch/mx31-regs.h> +#elif defined(CONFIG_MX35) +#include <asm/arch/mx35.h> +#else +#error "Please include specific headfile " +#endif #define IADR 0x00 #define IFDR 0x04 @@ -47,19 +55,17 @@ #define I2SR_IIF (1 << 1) #define I2SR_RX_NO_AK (1 << 0) -#ifdef CONFIG_SYS_I2C_MX31_PORT1 -#define I2C_BASE 0x43f80000 -#define I2C_CLK_OFFSET 26 -#elif defined (CONFIG_SYS_I2C_MX31_PORT2) -#define I2C_BASE 0x43f98000 -#define I2C_CLK_OFFSET 28 -#elif defined (CONFIG_SYS_I2C_MX31_PORT3) -#define I2C_BASE 0x43f84000 -#define I2C_CLK_OFFSET 30 +#ifdef CONFIG_SYS_I2C_PORT +# define I2C_BASE CONFIG_SYS_I2C_PORT #else -#error "define CONFIG_SYS_I2C_MX31_PORTx to use the mx31 I2C driver" +# error "define CFG_I2C_PORT(I2C base address) to use the I2C driver" #endif +#define I2C_MAX_TIMEOUT 100000 +#define I2C_TIMEOUT_TICKET 1 + +#undef DEBUG + #ifdef DEBUG #define DPRINTF(args...) printf(args) #else @@ -67,36 +73,77 @@ #endif static u16 div[] = { 30, 32, 36, 42, 48, 52, 60, 72, 80, 88, 104, 128, 144, - 160, 192, 240, 288, 320, 384, 480, 576, 640, 768, 960, - 1152, 1280, 1536, 1920, 2304, 2560, 3072, 3840}; + 160, 192, 240, 288, 320, 384, 480, 576, 640, 768, 960, + 1152, 1280, 1536, 1920, 2304, 2560, 3072, 3840 +}; + +static inline void i2c_reset(void) +{ + __REG16(I2C_BASE + I2CR) = 0; /* Reset module */ + __REG16(I2C_BASE + I2SR) = 0; + __REG16(I2C_BASE + I2CR) = I2CR_IEN; +} void i2c_init(int speed, int unused) { - int freq = mx31_get_ipg_clk(); + int freq; int i; - /* start the required I2C clock */ - __REG(CCM_CGR0) = __REG(CCM_CGR0) | (3 << I2C_CLK_OFFSET); - - for (i = 0; i < 0x1f; i++) +#ifdef CONFIG_MX31 + freq = mx31_get_ipg_clk(); +#else + freq = mxc_get_clock(MXC_IPG_PERCLK); +#endif + For (i = 0; i < 0x1f; i++) if (freq / div[i] <= speed) break; - DPRINTF("%s: speed: %d\n",__FUNCTION__, speed); + DPRINTF("%s: root clock: %d, speed: %d div: %x\n", + __func__, freq, speed, i); - __REG16(I2C_BASE + I2CR) = 0; /* Reset module */ __REG16(I2C_BASE + IFDR) = i; - __REG16(I2C_BASE + I2CR) = I2CR_IEN; - __REG16(I2C_BASE + I2SR) = 0; + i2c_reset(); +} + +static int wait_idle(void) +{ + int timeout = I2C_MAX_TIMEOUT; + + while ((__REG16(I2C_BASE + I2SR) & I2SR_IBB) && --timeout) { + __REG16(I2C_BASE + I2SR) = 0; + udelay(I2C_TIMEOUT_TICKET); + } + DPRINTF("%s:%x\n", __func__, __REG16(I2C_BASE + I2SR)); + return timeout ? timeout : (!(__REG16(I2C_BASE + I2SR) & I2SR_IBB)); } static int wait_busy(void) { - int timeout = 10000; + int timeout = I2C_MAX_TIMEOUT; - while (!(__REG16(I2C_BASE + I2SR) & I2SR_IIF) && --timeout) - udelay(1); - __REG16(I2C_BASE + I2SR) = 0; /* clear interrupt */ + while ((!(__REG16(I2C_BASE + I2SR) & I2SR_IBB) && (--timeout))) { + __REG16(I2C_BASE + I2SR) = 0; + udelay(I2C_TIMEOUT_TICKET); + } + return timeout ? timeout : (__REG16(I2C_BASE + I2SR) & I2SR_IBB); +} + +static int wait_complete(void) +{ + int timeout = I2C_MAX_TIMEOUT; + + while ((!(__REG16(I2C_BASE + I2SR) & I2SR_ICF)) && (--timeout)) { + __REG16(I2C_BASE + I2SR) = 0; + udelay(I2C_TIMEOUT_TICKET); + } + DPRINTF("%s:%x\n", __func__, __REG16(I2C_BASE + I2SR)); + { + int i; + for (i = 0; i < 200; i++) + udelay(10); + + } + __REG16(I2C_BASE + I2SR) = 0; /* clear interrupt */ return timeout; } @@ -105,90 +152,133 @@ static int tx_byte(u8 byte) { __REG16(I2C_BASE + I2DR) = byte; - if (!wait_busy() || __REG16(I2C_BASE + I2SR) & I2SR_RX_NO_AK) + if (!wait_complete() || __REG16(I2C_BASE + I2SR) & I2SR_RX_NO_AK) { + DPRINTF("%s:%x <= %x\n", __func__, __REG16(I2C_BASE + I2SR), + byte); return -1; + } + DPRINTF("%s:%x\n", __func__, byte); return 0; } -static int rx_byte(void) +static int rx_byte(u32 *pdata, int last) { - if (!wait_busy()) + if (!wait_complete()) return -1; - return __REG16(I2C_BASE + I2DR); + if (last) + __REG16(I2C_BASE + I2CR) = I2CR_IEN; + + *pdata = __REG16(I2C_BASE + I2DR); + DPRINTF("%s:%x\n", __func__, *pdata); + return 0; } int i2c_probe(uchar chip) { int ret; - __REG16(I2C_BASE + I2CR) = 0; /* Reset module */ + __REG16(I2C_BASE + I2CR) = 0; /* Reset module */ __REG16(I2C_BASE + I2CR) = I2CR_IEN; - - __REG16(I2C_BASE + I2CR) = I2CR_IEN | I2CR_MSTA | I2CR_MTX; + for (ret = 0; ret < 1000; ret++) + udelay(1); + __REG16(I2C_BASE + I2CR) = I2CR_IEN | I2CR_MSTA | I2CR_MTX; ret = tx_byte(chip << 1); - __REG16(I2C_BASE + I2CR) = I2CR_IEN | I2CR_MTX; + __REG16(I2C_BASE + I2CR) = I2CR_IEN; return ret; } static int i2c_addr(uchar chip, uint addr, int alen) { - __REG16(I2C_BASE + I2SR) = 0; /* clear interrupt */ - __REG16(I2C_BASE + I2CR) = I2CR_IEN | I2CR_MSTA | I2CR_MTX; - - if (tx_byte(chip << 1)) + int i, retry = 0; + for (retry = 0; retry < 3; retry++) { + if (wait_idle()) + break; + i2c_reset(); + for (i = 0; i < I2C_MAX_TIMEOUT; i++) + udelay(I2C_TIMEOUT_TICKET); + } + if (retry >= 3) { + printf("%s:bus is busy(%x)\n", + __func__, __REG16(I2C_BASE + I2SR)); return -1; - + } + __REG16(I2C_BASE + I2CR) = I2CR_IEN | I2CR_MSTA | I2CR_MTX; + if (!wait_busy()) { + printf("%s:trigger start fail(%x)\n", + __func__, __REG16(I2C_BASE + I2SR)); + return -1; + } + if (tx_byte(chip << 1) || (__REG16(I2C_BASE + I2SR) & I2SR_RX_NO_AK)) { + printf("%s:chip address cycle fail(%x)\n", + __func__, __REG16(I2C_BASE + I2SR)); + return -1; + } while (alen--) - if (tx_byte((addr >> (alen * 8)) & 0xff)) + if (tx_byte((addr >> (alen * 8)) & 0xff) || + (__REG16(I2C_BASE + I2SR) & I2SR_RX_NO_AK)) { + printf("%s:device address cycle fail(%x)\n", + __func__, __REG16(I2C_BASE + I2SR)); return -1; + } return 0; } int i2c_read(uchar chip, uint addr, int alen, uchar *buf, int len) { - int timeout = 10000; + int timeout = I2C_MAX_TIMEOUT; int ret; - DPRINTF("%s chip: 0x%02x addr: 0x%04x alen: %d len: %d\n",__FUNCTION__, chip, addr, alen, len); + DPRINTF("%s chip: 0x%02x addr: 0x%04x alen: %d len: %d\n", + __func__, chip, addr, alen, len); if (i2c_addr(chip, addr, alen)) { printf("i2c_addr failed\n"); return -1; } - __REG16(I2C_BASE + I2CR) = I2CR_IEN | I2CR_MSTA | I2CR_MTX | I2CR_RSTA; + __REG16(I2C_BASE + I2CR) = I2CR_IEN | I2CR_MSTA | I2CR_MTX | I2CR_RSTA; - if (tx_byte(chip << 1 | 1)) + if (tx_byte(chip << 1 | 1) || + (__REG16(I2C_BASE + I2SR) & I2SR_RX_NO_AK)) { + printf("%s:Send 2th chip address fail(%x)\n", + __func__, __REG16(I2C_BASE + I2SR)); return -1; - - __REG16(I2C_BASE + I2CR) = I2CR_IEN | I2CR_MSTA | ((len == 1) ? I2CR_TX_NO_AK : 0); - + } + __REG16(I2C_BASE + I2CR) = I2CR_IEN | I2CR_MSTA | + ((len == 1) ? I2CR_TX_NO_AK : 0); + DPRINTF("CR=%x\n", __REG16(I2C_BASE + I2CR)); ret = __REG16(I2C_BASE + I2DR); while (len--) { - if ((ret = rx_byte()) < 0) + if (len == 1) + __REG16(I2C_BASE + I2CR) = I2CR_IEN | I2CR_MSTA | + I2CR_TX_NO_AK; + + if (rx_byte(&ret, len == 0) < 0) { + printf("Read: rx_byte fail\n"); return -1; + } *buf++ = ret; - if (len <= 1) - __REG16(I2C_BASE + I2CR) = I2CR_IEN | I2CR_MSTA | I2CR_TX_NO_AK; } - wait_busy(); - - __REG16(I2C_BASE + I2CR) = I2CR_IEN; - - while (__REG16(I2C_BASE + I2SR) & I2SR_IBB && --timeout) - udelay(1); - + while (__REG16(I2C_BASE + I2SR) & I2SR_IBB && --timeout) { + __REG16(I2C_BASE + I2SR) = 0; + udelay(I2C_TIMEOUT_TICKET); + } + if (!timeout) { + printf("%s:trigger stop fail(%x)\n", + __func__, __REG16(I2C_BASE + I2SR)); + } return 0; } int i2c_write(uchar chip, uint addr, int alen, uchar *buf, int len) { - int timeout = 10000; - DPRINTF("%s chip: 0x%02x addr: 0x%04x alen: %d len: %d\n",__FUNCTION__, chip, addr, alen, len); + int timeout = I2C_MAX_TIMEOUT; + DPRINTF("%s chip: 0x%02x addr: 0x%04x alen: %d len: %d\n", + __func__, chip, addr, alen, len); if (i2c_addr(chip, addr, alen)) return -1; @@ -200,9 +290,9 @@ int i2c_write(uchar chip, uint addr, int alen, uchar *buf, int len) __REG16(I2C_BASE + I2CR) = I2CR_IEN; while (__REG16(I2C_BASE + I2SR) & I2SR_IBB && --timeout) - udelay(1); + udelay(I2C_TIMEOUT_TICKET); return 0; } -#endif /* CONFIG_HARD_I2C */ +#endif /* CONFIG_HARD_I2C */ |