aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorShinya Kuribayashi <shinya.kuribayashi@necel.com>2009-11-06 07:47:01 -0500
committerBen Dooks <ben-linux@fluff.org>2009-12-08 19:19:10 -0500
commitd60c7e81dda2041302791c6a5261bd0c74d60fba (patch)
treeca66636592174c7325f465eb6467dfc8548d743b
parent6d2ea4875f7e5e495526bdfd32bce093cb130276 (diff)
i2c-designware: Improved _HCNT/_LCNT calculation
* Calculate with accurate conditional expressions from DW manuals. * Round ic_clk by adding 0.5 as it's important at high ic_clk rate. * Take into account "tHD;STA" issue for _HCNT calculation. * Take into account "tf" for _LCNT calculation. * Add "cond" and "offset" fot further correction requirements. For _HCNT calculation, there's one issue needs to be carefully considered; DesignWare I2C core doesn't seem to have solid strategy to meet the tHD;STA timing spec. If you configure _HCNT based on the tHIGH timing spec, it easily results in violation of the tHD;STA spec. After many trials, we came to the conclusion that the tHD;STA period is proportional to (_HCNT + 3). For the safety's sake, this should be selected by default. As for _LCNT calculation, DW I2C core has one characteristic behavior; he starts counting the SCL CNTs for the LOW period of the SCL clock (tLOW) as soon as it pulls the SCL line. At that time, he doesn't take into account the fall time of SCL signal (tf), IOW, he starts counting CNTs without confirming the SCL input voltage has dropped to below VIL. This characteristics becomes a problem on some platforms where tf is considerably long, and results in violation of the tLOW timing spec. To make the driver configurable as much as possible for various cases, we'd have separated arguments "tf" and "offset", and for safety default values should be 0.3 us and 0, respectively. Signed-off-by: Shinya Kuribayashi <shinya.kuribayashi@necel.com> Acked-by: Baruch Siach <baruch@tkos.co.il> Signed-off-by: Ben Dooks <ben-linux@fluff.org>
-rw-r--r--drivers/i2c/busses/i2c-designware.c93
1 files changed, 84 insertions, 9 deletions
diff --git a/drivers/i2c/busses/i2c-designware.c b/drivers/i2c/busses/i2c-designware.c
index fd1616b3d2b5..cdfd94e41ccc 100644
--- a/drivers/i2c/busses/i2c-designware.c
+++ b/drivers/i2c/busses/i2c-designware.c
@@ -196,6 +196,61 @@ struct dw_i2c_dev {
196 unsigned int rx_fifo_depth; 196 unsigned int rx_fifo_depth;
197}; 197};
198 198
199static u32
200i2c_dw_scl_hcnt(u32 ic_clk, u32 tSYMBOL, u32 tf, int cond, int offset)
201{
202 /*
203 * DesignWare I2C core doesn't seem to have solid strategy to meet
204 * the tHD;STA timing spec. Configuring _HCNT based on tHIGH spec
205 * will result in violation of the tHD;STA spec.
206 */
207 if (cond)
208 /*
209 * Conditional expression:
210 *
211 * IC_[FS]S_SCL_HCNT + (1+4+3) >= IC_CLK * tHIGH
212 *
213 * This is based on the DW manuals, and represents an ideal
214 * configuration. The resulting I2C bus speed will be
215 * faster than any of the others.
216 *
217 * If your hardware is free from tHD;STA issue, try this one.
218 */
219 return (ic_clk * tSYMBOL + 5000) / 10000 - 8 + offset;
220 else
221 /*
222 * Conditional expression:
223 *
224 * IC_[FS]S_SCL_HCNT + 3 >= IC_CLK * (tHD;STA + tf)
225 *
226 * This is just experimental rule; the tHD;STA period turned
227 * out to be proportinal to (_HCNT + 3). With this setting,
228 * we could meet both tHIGH and tHD;STA timing specs.
229 *
230 * If unsure, you'd better to take this alternative.
231 *
232 * The reason why we need to take into account "tf" here,
233 * is the same as described in i2c_dw_scl_lcnt().
234 */
235 return (ic_clk * (tSYMBOL + tf) + 5000) / 10000 - 3 + offset;
236}
237
238static u32 i2c_dw_scl_lcnt(u32 ic_clk, u32 tLOW, u32 tf, int offset)
239{
240 /*
241 * Conditional expression:
242 *
243 * IC_[FS]S_SCL_LCNT + 1 >= IC_CLK * (tLOW + tf)
244 *
245 * DW I2C core starts counting the SCL CNTs for the LOW period
246 * of the SCL clock (tLOW) as soon as it pulls the SCL line.
247 * In order to meet the tLOW timing spec, we need to take into
248 * account the fall time of SCL signal (tf). Default tf value
249 * should be 0.3 us, for safety.
250 */
251 return ((ic_clk * (tLOW + tf) + 5000) / 10000) - 1 + offset;
252}
253
199/** 254/**
200 * i2c_dw_init() - initialize the designware i2c master hardware 255 * i2c_dw_init() - initialize the designware i2c master hardware
201 * @dev: device private data 256 * @dev: device private data
@@ -207,20 +262,40 @@ struct dw_i2c_dev {
207static void i2c_dw_init(struct dw_i2c_dev *dev) 262static void i2c_dw_init(struct dw_i2c_dev *dev)
208{ 263{
209 u32 input_clock_khz = clk_get_rate(dev->clk) / 1000; 264 u32 input_clock_khz = clk_get_rate(dev->clk) / 1000;
210 u32 ic_con; 265 u32 ic_con, hcnt, lcnt;
211 266
212 /* Disable the adapter */ 267 /* Disable the adapter */
213 writel(0, dev->base + DW_IC_ENABLE); 268 writel(0, dev->base + DW_IC_ENABLE);
214 269
215 /* set standard and fast speed deviders for high/low periods */ 270 /* set standard and fast speed deviders for high/low periods */
216 writel((input_clock_khz * 40 / 10000)+1, /* std speed high, 4us */ 271
217 dev->base + DW_IC_SS_SCL_HCNT); 272 /* Standard-mode */
218 writel((input_clock_khz * 47 / 10000)+1, /* std speed low, 4.7us */ 273 hcnt = i2c_dw_scl_hcnt(input_clock_khz,
219 dev->base + DW_IC_SS_SCL_LCNT); 274 40, /* tHD;STA = tHIGH = 4.0 us */
220 writel((input_clock_khz * 6 / 10000)+1, /* fast speed high, 0.6us */ 275 3, /* tf = 0.3 us */
221 dev->base + DW_IC_FS_SCL_HCNT); 276 0, /* 0: DW default, 1: Ideal */
222 writel((input_clock_khz * 13 / 10000)+1, /* fast speed low, 1.3us */ 277 0); /* No offset */
223 dev->base + DW_IC_FS_SCL_LCNT); 278 lcnt = i2c_dw_scl_lcnt(input_clock_khz,
279 47, /* tLOW = 4.7 us */
280 3, /* tf = 0.3 us */
281 0); /* No offset */
282 writel(hcnt, dev->base + DW_IC_SS_SCL_HCNT);
283 writel(lcnt, dev->base + DW_IC_SS_SCL_LCNT);
284 dev_dbg(dev->dev, "Standard-mode HCNT:LCNT = %d:%d\n", hcnt, lcnt);
285
286 /* Fast-mode */
287 hcnt = i2c_dw_scl_hcnt(input_clock_khz,
288 6, /* tHD;STA = tHIGH = 0.6 us */
289 3, /* tf = 0.3 us */
290 0, /* 0: DW default, 1: Ideal */
291 0); /* No offset */
292 lcnt = i2c_dw_scl_lcnt(input_clock_khz,
293 13, /* tLOW = 1.3 us */
294 3, /* tf = 0.3 us */
295 0); /* No offset */
296 writel(hcnt, dev->base + DW_IC_FS_SCL_HCNT);
297 writel(lcnt, dev->base + DW_IC_FS_SCL_LCNT);
298 dev_dbg(dev->dev, "Fast-mode HCNT:LCNT = %d:%d\n", hcnt, lcnt);
224 299
225 /* configure the i2c master */ 300 /* configure the i2c master */
226 ic_con = DW_IC_CON_MASTER | DW_IC_CON_SLAVE_DISABLE | 301 ic_con = DW_IC_CON_MASTER | DW_IC_CON_SLAVE_DISABLE |