diff options
Diffstat (limited to 'drivers/i2c/mxc_i2c.c')
| -rw-r--r-- | drivers/i2c/mxc_i2c.c | 172 | 
1 files changed, 127 insertions, 45 deletions
| diff --git a/drivers/i2c/mxc_i2c.c b/drivers/i2c/mxc_i2c.c index 8e10fbb21..c5ec486a7 100644 --- a/drivers/i2c/mxc_i2c.c +++ b/drivers/i2c/mxc_i2c.c @@ -23,11 +23,17 @@   */  #include <common.h> +#include <asm/io.h>  #if defined(CONFIG_HARD_I2C) +#if defined(CONFIG_MX31)  #include <asm/arch/mx31.h>  #include <asm/arch/mx31-regs.h> +#else +#include <asm/arch/imx-regs.h> +#include <asm/arch/clock.h> +#endif  #define IADR	0x00  #define IFDR	0x04 @@ -47,7 +53,7 @@  #define I2SR_IIF	(1 << 1)  #define I2SR_RX_NO_AK	(1 << 0) -#ifdef CONFIG_SYS_I2C_MX31_PORT1 +#if defined(CONFIG_SYS_I2C_MX31_PORT1)  #define I2C_BASE	0x43f80000  #define I2C_CLK_OFFSET	26  #elif defined (CONFIG_SYS_I2C_MX31_PORT2) @@ -56,130 +62,205 @@  #elif defined (CONFIG_SYS_I2C_MX31_PORT3)  #define I2C_BASE	0x43f84000  #define I2C_CLK_OFFSET	30 +#elif defined(CONFIG_SYS_I2C_MX53_PORT1) +#define I2C_BASE        I2C1_BASE_ADDR +#elif defined(CONFIG_SYS_I2C_MX53_PORT2) +#define I2C_BASE        I2C2_BASE_ADDR +#elif defined(CONFIG_SYS_I2C_MX35_PORT1) +#define I2C_BASE	I2C_BASE_ADDR  #else -#error "define CONFIG_SYS_I2C_MX31_PORTx to use the mx31 I2C driver" +#error "define CONFIG_SYS_I2C_MX<Processor>_PORTx to use the mx I2C driver"  #endif -#ifdef DEBUG -#define DPRINTF(args...)  printf(args) -#else -#define DPRINTF(args...) -#endif +#define I2C_MAX_TIMEOUT		10000 +#define I2C_MAX_RETRIES		3  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}; +static inline void i2c_reset(void) +{ +	writew(0, I2C_BASE + I2CR);	/* Reset module */ +	writew(0, I2C_BASE + I2SR); +	writew(I2CR_IEN, I2C_BASE + I2CR); +} +  void i2c_init(int speed, int unused)  { -	int freq = mx31_get_ipg_clk(); +	int freq;  	int i; +#if defined(CONFIG_MX31) +	struct clock_control_regs *sc_regs = +		(struct clock_control_regs *)CCM_BASE; + +	freq = mx31_get_ipg_clk();  	/* start the required I2C clock */ -	__REG(CCM_CGR0) = __REG(CCM_CGR0) | (3 << I2C_CLK_OFFSET); +	writel(readl(&sc_regs->cgr0) | (3 << I2C_CLK_OFFSET), +		&sc_regs->cgr0); +#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); +	debug("%s: speed: %d\n", __func__, speed); -	__REG16(I2C_BASE + I2CR) = 0; /* Reset module */ -	__REG16(I2C_BASE + IFDR) = i; -	__REG16(I2C_BASE + I2CR) = I2CR_IEN; -	__REG16(I2C_BASE + I2SR) = 0; +	writew(i, I2C_BASE + IFDR); +	i2c_reset(); +} + +static int wait_idle(void) +{ +	int timeout = I2C_MAX_TIMEOUT; + +	while ((readw(I2C_BASE + I2SR) & I2SR_IBB) && --timeout) { +		writew(0, I2C_BASE + I2SR); +		udelay(1); +	} +	return timeout ? timeout : (!(readw(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) +	while (!(readw(I2C_BASE + I2SR) & I2SR_IBB) && --timeout)  		udelay(1); -	__REG16(I2C_BASE + I2SR) = 0; /* clear interrupt */ +	writew(0, I2C_BASE + I2SR); /* clear interrupt */  	return timeout;  } +static int wait_complete(void) +{ +	int timeout = I2C_MAX_TIMEOUT; + +	while ((!(readw(I2C_BASE + I2SR) & I2SR_ICF)) && (--timeout)) { +		writew(0, I2C_BASE + I2SR); +		udelay(1); +	} +	udelay(200); + +	writew(0, I2C_BASE + I2SR);	/* clear interrupt */ + +	return timeout; +} + +  static int tx_byte(u8 byte)  { -	__REG16(I2C_BASE + I2DR) = byte; +	writew(byte, I2C_BASE + I2DR); -	if (!wait_busy() || __REG16(I2C_BASE + I2SR) & I2SR_RX_NO_AK) +	if (!wait_complete() || readw(I2C_BASE + I2SR) & I2SR_RX_NO_AK)  		return -1;  	return 0;  } -static int rx_byte(void) +static int rx_byte(int last)  { -	if (!wait_busy()) +	if (!wait_complete())  		return -1; -	return __REG16(I2C_BASE + I2DR); +	if (last) +		writew(I2CR_IEN, I2C_BASE + I2CR); + +	return readw(I2C_BASE + I2DR);  }  int i2c_probe(uchar chip)  {  	int ret; -	__REG16(I2C_BASE + I2CR) = 0; /* Reset module */ -	__REG16(I2C_BASE + I2CR) = I2CR_IEN; +	writew(0, I2C_BASE + I2CR); /* Reset module */ +	writew(I2CR_IEN, I2C_BASE + I2CR); -	__REG16(I2C_BASE + I2CR) = I2CR_IEN |  I2CR_MSTA | I2CR_MTX; +	writew(I2CR_IEN |  I2CR_MSTA | I2CR_MTX, I2C_BASE + I2CR);  	ret = tx_byte(chip << 1); -	__REG16(I2C_BASE + I2CR) = I2CR_IEN | I2CR_MTX; +	writew(I2CR_IEN | I2CR_MTX, I2C_BASE + I2CR);  	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; +	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(1); +	} +	if (retry >= I2C_MAX_RETRIES) { +		debug("%s:bus is busy(%x)\n", +		       __func__, readw(I2C_BASE + I2SR)); +		return -1; +	} +	writew(I2CR_IEN | I2CR_MSTA | I2CR_MTX, I2C_BASE + I2CR); -	if (tx_byte(chip << 1)) +	if (!wait_busy()) { +		debug("%s:trigger start fail(%x)\n", +		       __func__, readw(I2C_BASE + I2SR));  		return -1; +	} +	if (tx_byte(chip << 1) || (readw(I2C_BASE + I2SR) & I2SR_RX_NO_AK)) { +		debug("%s:chip address cycle fail(%x)\n", +		       __func__, readw(I2C_BASE + I2SR)); +		return -1; +	}  	while (alen--) -		if (tx_byte((addr >> (alen * 8)) & 0xff)) +		if (tx_byte((addr >> (alen * 8)) & 0xff) || +		    (readw(I2C_BASE + I2SR) & I2SR_RX_NO_AK)) { +			debug("%s:device address cycle fail(%x)\n", +			       __func__, readw(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); +	debug("%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; +	writew(I2CR_IEN | I2CR_MSTA | I2CR_MTX | I2CR_RSTA, I2C_BASE + I2CR);  	if (tx_byte(chip << 1 | 1))  		return -1; -	__REG16(I2C_BASE + I2CR) = I2CR_IEN |  I2CR_MSTA | ((len == 1) ? I2CR_TX_NO_AK : 0); +	writew(I2CR_IEN | I2CR_MSTA | +		((len == 1) ? I2CR_TX_NO_AK : 0), +		I2C_BASE + I2CR); -	ret = __REG16(I2C_BASE + I2DR); +	ret = readw(I2C_BASE + I2DR);  	while (len--) { -		if ((ret = rx_byte()) < 0) +		ret = rx_byte(len == 0); +		if (ret  < 0)  			return -1;  		*buf++ = ret;  		if (len <= 1) -			__REG16(I2C_BASE + I2CR) = I2CR_IEN |  I2CR_MSTA | I2CR_TX_NO_AK; +			writew(I2CR_IEN | I2CR_MSTA | +				I2CR_TX_NO_AK, +				I2C_BASE + I2CR);  	} -	wait_busy(); - -	__REG16(I2C_BASE + I2CR) = I2CR_IEN; +	writew(I2CR_IEN, I2C_BASE + I2CR); -	while (__REG16(I2C_BASE + I2SR) & I2SR_IBB && --timeout) +	while (readw(I2C_BASE + I2SR) & I2SR_IBB && --timeout)  		udelay(1);  	return 0; @@ -187,8 +268,9 @@ int i2c_read(uchar chip, uint addr, int alen, uchar *buf, int len)  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; +	debug("%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; @@ -197,9 +279,9 @@ int i2c_write(uchar chip, uint addr, int alen, uchar *buf, int len)  		if (tx_byte(*buf++))  			return -1; -	__REG16(I2C_BASE + I2CR) = I2CR_IEN; +	writew(I2CR_IEN, I2C_BASE + I2CR); -	while (__REG16(I2C_BASE + I2SR) & I2SR_IBB && --timeout) +	while (readw(I2C_BASE + I2SR) & I2SR_IBB && --timeout)  		udelay(1);  	return 0; |