From a702e6f5d8cdc915ce46f987431dbf99a1167ea9 Mon Sep 17 00:00:00 2001 From: Daniel Leung Date: Wed, 2 Mar 2016 17:11:25 -0800 Subject: [PATCH] i2c: i2c_atmel_sam3: meet minimum timing requirements The I2C has minimum timing requirements for clock signal. This patch adds the code to make sure the minimum timing requirements are met. Change-Id: I3f148433e60be73866577fbe614b892481d7f69d Signed-off-by: Daniel Leung --- drivers/i2c/i2c_atmel_sam3.c | 55 ++++++++++++++++++++++++++++++++---- 1 file changed, 50 insertions(+), 5 deletions(-) diff --git a/drivers/i2c/i2c_atmel_sam3.c b/drivers/i2c/i2c_atmel_sam3.c index 718309822b7..cefdda9b0e2 100644 --- a/drivers/i2c/i2c_atmel_sam3.c +++ b/drivers/i2c/i2c_atmel_sam3.c @@ -36,6 +36,7 @@ #include #include +#include #include @@ -110,9 +111,10 @@ static uint32_t clk_div_calc(struct device *dev) return 0x0001D0D0; case I2C_SPEED_FAST: /* CKDIV = 0 - * CHDIV = CLDIV = 101 = 0x65 + * CHDIV = 101 = 0x65 + * CLDIV = 106 = 0x6A */ - return 0x00006565; + return 0x0000656A; default: /* Return 0 as error */ return 0; @@ -126,32 +128,75 @@ static uint32_t clk_div_calc(struct device *dev) struct i2c_sam3_dev_data * const dev_data = dev->driver_data; uint32_t i2c_clk; - uint32_t cldiv, ckdiv; + uint32_t cldiv, chdiv, ckdiv; + uint32_t i2c_h_min_time, i2c_l_min_time; + uint32_t cldiv_min, chdiv_min; + uint32_t mck; /* The T(low) and T(high) are used to calculate CLDIV and CHDIV. * Since we treat both clock low and clock high to have same period, * the I2C clock frequency used for calculation has to be doubled. + * + * The I2C spec has the following minimum timing requirement: + * Standard Speed: High 4000ns, Low 4700ns + * Fast Speed: High 600ns, Low 1300ns + * + * So use these to calculate chdiv_min and cldiv_min. */ switch ((dev_data->dev_config.bits.speed)) { case I2C_SPEED_STANDARD: i2c_clk = 100000 * 2; + i2c_h_min_time = 4000; + i2c_l_min_time = 4700; break; case I2C_SPEED_FAST: i2c_clk = 400000 * 2; + i2c_h_min_time = 600; + i2c_l_min_time = 1300; break; default: /* Return 0 as error */ return 0; } + /* Calculate CLDIV (which will be used for CHDIV also) */ cldiv = (CONFIG_SYS_CLOCK_HW_CYCLES_PER_SEC) / i2c_clk - 4; + + /* Calculate minimum CHDIV and CLDIV */ + + /* Make 1/mck be in micro second */ + mck = CONFIG_SYS_CLOCK_HW_CYCLES_PER_SEC + / MSEC_PER_SEC / USEC_PER_MSEC; + + /* The +1 is to make sure we don't go under the minimum + * after the division. In other words, force rounding up. + */ + cldiv_min = (i2c_l_min_time * mck / 1000) - 4 + 1; + chdiv_min = (i2c_h_min_time * mck / 1000) - 4 + 1; + ckdiv = 0; while (cldiv > 255) { ckdiv++; - cldiv /= 2; + + /* Math is there to round up. + * Rounding up makes the SCL periods longer, + * which makes clock slower. + * This is fine as faster clock may cause + * issues. + */ + cldiv = (cldiv >> 1) + (cldiv & 0x01); + + cldiv_min = (cldiv_min >> 1) + (cldiv_min & 0x01); + chdiv_min = (chdiv_min >> 1) + (chdiv_min & 0x01); } - return ((ckdiv << TWI_CWGR_CKDIV_POS) + (cldiv << TWI_CWGR_CHDIV_POS) + chdiv = cldiv; + + /* Make sure we are above minimum requirements */ + cldiv = max(cldiv, cldiv_min); + chdiv = max(chdiv, chdiv_min); + + return ((ckdiv << TWI_CWGR_CKDIV_POS) + (chdiv << TWI_CWGR_CHDIV_POS) + (cldiv << TWI_CWGR_CLDIV_POS)); #endif /* CONFIG_SYS_CLOCK_HW_CYCLES_PER_SEC == 84000000 */