diff options
Diffstat (limited to 'drivers/i2c')
-rw-r--r-- | drivers/i2c/busses/i2c-designware.c | 93 |
1 files changed, 84 insertions, 9 deletions
diff --git a/drivers/i2c/busses/i2c-designware.c b/drivers/i2c/busses/i2c-designware.c index fd1616b3d2b..cdfd94e41cc 100644 --- a/drivers/i2c/busses/i2c-designware.c +++ b/drivers/i2c/busses/i2c-designware.c @@ -196,6 +196,61 @@ struct dw_i2c_dev { unsigned int rx_fifo_depth; }; +static u32 +i2c_dw_scl_hcnt(u32 ic_clk, u32 tSYMBOL, u32 tf, int cond, int offset) +{ + /* + * DesignWare I2C core doesn't seem to have solid strategy to meet + * the tHD;STA timing spec. Configuring _HCNT based on tHIGH spec + * will result in violation of the tHD;STA spec. + */ + if (cond) + /* + * Conditional expression: + * + * IC_[FS]S_SCL_HCNT + (1+4+3) >= IC_CLK * tHIGH + * + * This is based on the DW manuals, and represents an ideal + * configuration. The resulting I2C bus speed will be + * faster than any of the others. + * + * If your hardware is free from tHD;STA issue, try this one. + */ + return (ic_clk * tSYMBOL + 5000) / 10000 - 8 + offset; + else + /* + * Conditional expression: + * + * IC_[FS]S_SCL_HCNT + 3 >= IC_CLK * (tHD;STA + tf) + * + * This is just experimental rule; the tHD;STA period turned + * out to be proportinal to (_HCNT + 3). With this setting, + * we could meet both tHIGH and tHD;STA timing specs. + * + * If unsure, you'd better to take this alternative. + * + * The reason why we need to take into account "tf" here, + * is the same as described in i2c_dw_scl_lcnt(). + */ + return (ic_clk * (tSYMBOL + tf) + 5000) / 10000 - 3 + offset; +} + +static u32 i2c_dw_scl_lcnt(u32 ic_clk, u32 tLOW, u32 tf, int offset) +{ + /* + * Conditional expression: + * + * IC_[FS]S_SCL_LCNT + 1 >= IC_CLK * (tLOW + tf) + * + * DW I2C core starts counting the SCL CNTs for the LOW period + * of the SCL clock (tLOW) as soon as it pulls the SCL line. + * In order to meet the tLOW timing spec, we need to take into + * account the fall time of SCL signal (tf). Default tf value + * should be 0.3 us, for safety. + */ + return ((ic_clk * (tLOW + tf) + 5000) / 10000) - 1 + offset; +} + /** * i2c_dw_init() - initialize the designware i2c master hardware * @dev: device private data @@ -207,20 +262,40 @@ struct dw_i2c_dev { static void i2c_dw_init(struct dw_i2c_dev *dev) { u32 input_clock_khz = clk_get_rate(dev->clk) / 1000; - u32 ic_con; + u32 ic_con, hcnt, lcnt; /* Disable the adapter */ writel(0, dev->base + DW_IC_ENABLE); /* set standard and fast speed deviders for high/low periods */ - writel((input_clock_khz * 40 / 10000)+1, /* std speed high, 4us */ - dev->base + DW_IC_SS_SCL_HCNT); - writel((input_clock_khz * 47 / 10000)+1, /* std speed low, 4.7us */ - dev->base + DW_IC_SS_SCL_LCNT); - writel((input_clock_khz * 6 / 10000)+1, /* fast speed high, 0.6us */ - dev->base + DW_IC_FS_SCL_HCNT); - writel((input_clock_khz * 13 / 10000)+1, /* fast speed low, 1.3us */ - dev->base + DW_IC_FS_SCL_LCNT); + + /* Standard-mode */ + hcnt = i2c_dw_scl_hcnt(input_clock_khz, + 40, /* tHD;STA = tHIGH = 4.0 us */ + 3, /* tf = 0.3 us */ + 0, /* 0: DW default, 1: Ideal */ + 0); /* No offset */ + lcnt = i2c_dw_scl_lcnt(input_clock_khz, + 47, /* tLOW = 4.7 us */ + 3, /* tf = 0.3 us */ + 0); /* No offset */ + writel(hcnt, dev->base + DW_IC_SS_SCL_HCNT); + writel(lcnt, dev->base + DW_IC_SS_SCL_LCNT); + dev_dbg(dev->dev, "Standard-mode HCNT:LCNT = %d:%d\n", hcnt, lcnt); + + /* Fast-mode */ + hcnt = i2c_dw_scl_hcnt(input_clock_khz, + 6, /* tHD;STA = tHIGH = 0.6 us */ + 3, /* tf = 0.3 us */ + 0, /* 0: DW default, 1: Ideal */ + 0); /* No offset */ + lcnt = i2c_dw_scl_lcnt(input_clock_khz, + 13, /* tLOW = 1.3 us */ + 3, /* tf = 0.3 us */ + 0); /* No offset */ + writel(hcnt, dev->base + DW_IC_FS_SCL_HCNT); + writel(lcnt, dev->base + DW_IC_FS_SCL_LCNT); + dev_dbg(dev->dev, "Fast-mode HCNT:LCNT = %d:%d\n", hcnt, lcnt); /* configure the i2c master */ ic_con = DW_IC_CON_MASTER | DW_IC_CON_SLAVE_DISABLE | |