diff options
| -rw-r--r-- | arch/arm/cpu/arm926ejs/mxs/clock.c | 2 | ||||
| -rw-r--r-- | arch/arm/include/asm/arch-mxs/clock.h | 1 | ||||
| -rw-r--r-- | drivers/i2c/mxs_i2c.c | 87 | 
3 files changed, 37 insertions, 53 deletions
| diff --git a/arch/arm/cpu/arm926ejs/mxs/clock.c b/arch/arm/cpu/arm926ejs/mxs/clock.c index bfea6abeb..4ff19c37e 100644 --- a/arch/arm/cpu/arm926ejs/mxs/clock.c +++ b/arch/arm/cpu/arm926ejs/mxs/clock.c @@ -333,6 +333,8 @@ uint32_t mxc_get_clock(enum mxc_clock clk)  		return mx28_get_sspclk(MXC_SSPCLK2);  	case MXC_SSP3_CLK:  		return mx28_get_sspclk(MXC_SSPCLK3); +	case MXC_XTAL_CLK: +		return XTAL_FREQ_KHZ * 1000;  	}  	return 0; diff --git a/arch/arm/include/asm/arch-mxs/clock.h b/arch/arm/include/asm/arch-mxs/clock.h index 1700fe391..3d39ef235 100644 --- a/arch/arm/include/asm/arch-mxs/clock.h +++ b/arch/arm/include/asm/arch-mxs/clock.h @@ -35,6 +35,7 @@ enum mxc_clock {  	MXC_SSP1_CLK,  	MXC_SSP2_CLK,  	MXC_SSP3_CLK, +	MXC_XTAL_CLK,  };  enum mxs_ioclock { diff --git a/drivers/i2c/mxs_i2c.c b/drivers/i2c/mxs_i2c.c index 73a6659dd..b907f7b37 100644 --- a/drivers/i2c/mxs_i2c.c +++ b/drivers/i2c/mxs_i2c.c @@ -28,6 +28,7 @@  #include <common.h>  #include <malloc.h> +#include <i2c.h>  #include <asm/errno.h>  #include <asm/io.h>  #include <asm/arch/clock.h> @@ -213,62 +214,39 @@ int i2c_probe(uchar chip)  	return ret;  } -static struct mxs_i2c_speed_table { -	uint32_t	speed; -	uint32_t	timing0; -	uint32_t	timing1; -} mxs_i2c_tbl[] = { -	{ -		100000, -		(0x0078 << I2C_TIMING0_HIGH_COUNT_OFFSET) | -		(0x0030 << I2C_TIMING0_RCV_COUNT_OFFSET), -		(0x0080 << I2C_TIMING1_LOW_COUNT_OFFSET) | -		(0x0030 << I2C_TIMING1_XMIT_COUNT_OFFSET) -	}, -	{ -		400000, -		(0x000f << I2C_TIMING0_HIGH_COUNT_OFFSET) | -		(0x0007 << I2C_TIMING0_RCV_COUNT_OFFSET), -		(0x001f << I2C_TIMING1_LOW_COUNT_OFFSET) | -		(0x000f << I2C_TIMING1_XMIT_COUNT_OFFSET), -	} -}; - -static struct mxs_i2c_speed_table *mxs_i2c_speed_to_cfg(uint32_t speed) -{ -	int i; -	for (i = 0; i < ARRAY_SIZE(mxs_i2c_tbl); i++) -		if (mxs_i2c_tbl[i].speed == speed) -			return &mxs_i2c_tbl[i]; -	return NULL; -} - -static uint32_t mxs_i2c_cfg_to_speed(uint32_t timing0, uint32_t timing1) -{ -	int i; -	for (i = 0; i < ARRAY_SIZE(mxs_i2c_tbl); i++) { -		if (mxs_i2c_tbl[i].timing0 != timing0) -			continue; -		if (mxs_i2c_tbl[i].timing1 != timing1) -			continue; -		return mxs_i2c_tbl[i].speed; -	} - -	return 0; -} -  int i2c_set_bus_speed(unsigned int speed)  {  	struct mxs_i2c_regs *i2c_regs = (struct mxs_i2c_regs *)MXS_I2C0_BASE; -	struct mxs_i2c_speed_table *spd = mxs_i2c_speed_to_cfg(speed); +	/* +	 * The timing derivation algorithm. There is no documentation for this +	 * algorithm available, it was derived by using the scope and fiddling +	 * with constants until the result observed on the scope was good enough +	 * for 20kHz, 50kHz, 100kHz, 200kHz, 300kHz and 400kHz. It should be +	 * possible to assume the algorithm works for other frequencies as well. +	 * +	 * Note it was necessary to cap the frequency on both ends as it's not +	 * possible to configure completely arbitrary frequency for the I2C bus +	 * clock. +	 */ +	uint32_t clk = mxc_get_clock(MXC_XTAL_CLK); +	uint32_t base = ((clk / speed) - 38) / 2; +	uint16_t high_count = base + 3; +	uint16_t low_count = base - 3; +	uint16_t rcv_count = (high_count * 3) / 4; +	uint16_t xmit_count = low_count / 4; -	if (!spd) { -		printf("MXS I2C: Invalid speed selected (%d Hz)\n", speed); +	if (speed > 540000) { +		printf("MXS I2C: Speed too high (%d Hz)\n", speed);  		return -EINVAL;  	} -	writel(spd->timing0, &i2c_regs->hw_i2c_timing0); -	writel(spd->timing1, &i2c_regs->hw_i2c_timing1); +	if (speed < 12000) { +		printf("MXS I2C: Speed too low (%d Hz)\n", speed); +		return -EINVAL; +	} + +	writel((high_count << 16) | rcv_count, &i2c_regs->hw_i2c_timing0); +	writel((low_count << 16) | xmit_count, &i2c_regs->hw_i2c_timing1);  	writel((0x0030 << I2C_TIMING2_BUS_FREE_OFFSET) |  		(0x0030 << I2C_TIMING2_LEADIN_COUNT_OFFSET), @@ -280,12 +258,15 @@ int i2c_set_bus_speed(unsigned int speed)  unsigned int i2c_get_bus_speed(void)  {  	struct mxs_i2c_regs *i2c_regs = (struct mxs_i2c_regs *)MXS_I2C0_BASE; -	uint32_t timing0, timing1; +	uint32_t clk = mxc_get_clock(MXC_XTAL_CLK); +	uint32_t timing0;  	timing0 = readl(&i2c_regs->hw_i2c_timing0); -	timing1 = readl(&i2c_regs->hw_i2c_timing1); - -	return mxs_i2c_cfg_to_speed(timing0, timing1); +	/* +	 * This is a reverse version of the algorithm presented in +	 * i2c_set_bus_speed(). Please refer there for details. +	 */ +	return clk / ((((timing0 >> 16) - 3) * 2) + 38);  }  void i2c_init(int speed, int slaveadd) |