aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/i2c
diff options
context:
space:
mode:
authoraddy ke <addy.ke@rock-chips.com>2014-10-14 02:09:21 -0400
committerWolfram Sang <wsa@the-dreams.de>2014-11-10 09:56:26 -0500
commit0285f8f5fd7cf7f458e13d9189eb735dacc244b5 (patch)
treea987fb295469a367b9ae1753a5101eec33a2c425 /drivers/i2c
parent900ef800a2a4c08aa28af4785a5b2d5d7e199103 (diff)
i2c: rk3x: adjust the LOW divison based on characteristics of SCL
As show in I2C specification: - Standard-mode: the minimum HIGH period of the scl clock is 4.0us the minimum LOW period of the scl clock is 4.7us - Fast-mode: the minimum HIGH period of the scl clock is 0.6us the minimum LOW period of the scl clock is 1.3us I have measured i2c SCL waveforms in fast-mode by oscilloscope on rk3288-pinky board. the LOW period of the scl clock is 1.3us. It is so critical that we must adjust LOW division to increase the LOW period of the scl clock. Thanks Doug for the suggestion about division formulas. Signed-off-by: Addy Ke <addy.ke@rock-chips.com> Tested-by: Heiko Stuebner <heiko@sntech.de> Reviewed-by: Doug Anderson <dianders@chromium.org> Reviewed-by: Max Schwarz <max.schwarz@online.de> Signed-off-by: Wolfram Sang <wsa@the-dreams.de>
Diffstat (limited to 'drivers/i2c')
-rw-r--r--drivers/i2c/busses/i2c-rk3x.c162
1 files changed, 153 insertions, 9 deletions
diff --git a/drivers/i2c/busses/i2c-rk3x.c b/drivers/i2c/busses/i2c-rk3x.c
index f486d0eac4d0..e276ffbfbd1a 100644
--- a/drivers/i2c/busses/i2c-rk3x.c
+++ b/drivers/i2c/busses/i2c-rk3x.c
@@ -24,6 +24,7 @@
24#include <linux/wait.h> 24#include <linux/wait.h>
25#include <linux/mfd/syscon.h> 25#include <linux/mfd/syscon.h>
26#include <linux/regmap.h> 26#include <linux/regmap.h>
27#include <linux/math64.h>
27 28
28 29
29/* Register Map */ 30/* Register Map */
@@ -428,18 +429,158 @@ out:
428 return IRQ_HANDLED; 429 return IRQ_HANDLED;
429} 430}
430 431
431static void rk3x_i2c_set_scl_rate(struct rk3x_i2c *i2c, unsigned long scl_rate) 432static int rk3x_i2c_calc_divs(unsigned long i2c_rate, unsigned long scl_rate,
433 unsigned long *div_low, unsigned long *div_high)
432{ 434{
433 unsigned long i2c_rate = clk_get_rate(i2c->clk); 435 unsigned long min_low_ns, min_high_ns;
434 unsigned int div; 436 unsigned long max_data_hold_ns;
437 unsigned long data_hold_buffer_ns;
438 unsigned long max_low_ns, min_total_ns;
439
440 unsigned long i2c_rate_khz, scl_rate_khz;
441
442 unsigned long min_low_div, min_high_div;
443 unsigned long max_low_div;
444
445 unsigned long min_div_for_hold, min_total_div;
446 unsigned long extra_div, extra_low_div, ideal_low_div;
447
448 /* Only support standard-mode and fast-mode */
449 if (WARN_ON(scl_rate > 400000))
450 scl_rate = 400000;
451
452 /* prevent scl_rate_khz from becoming 0 */
453 if (WARN_ON(scl_rate < 1000))
454 scl_rate = 1000;
455
456 /*
457 * min_low_ns: The minimum number of ns we need to hold low
458 * to meet i2c spec
459 * min_high_ns: The minimum number of ns we need to hold high
460 * to meet i2c spec
461 * max_low_ns: The maximum number of ns we can hold low
462 * to meet i2c spec
463 *
464 * Note: max_low_ns should be (max data hold time * 2 - buffer)
465 * This is because the i2c host on Rockchip holds the data line
466 * for half the low time.
467 */
468 if (scl_rate <= 100000) {
469 min_low_ns = 4700;
470 min_high_ns = 4000;
471 max_data_hold_ns = 3450;
472 data_hold_buffer_ns = 50;
473 } else {
474 min_low_ns = 1300;
475 min_high_ns = 600;
476 max_data_hold_ns = 900;
477 data_hold_buffer_ns = 50;
478 }
479 max_low_ns = max_data_hold_ns * 2 - data_hold_buffer_ns;
480 min_total_ns = min_low_ns + min_high_ns;
481
482 /* Adjust to avoid overflow */
483 i2c_rate_khz = DIV_ROUND_UP(i2c_rate, 1000);
484 scl_rate_khz = scl_rate / 1000;
435 485
436 /* set DIV = DIVH = DIVL 486 /*
437 * SCL rate = (clk rate) / (8 * (DIVH + 1 + DIVL + 1)) 487 * We need the total div to be >= this number
438 * = (clk rate) / (16 * (DIV + 1)) 488 * so we don't clock too fast.
489 */
490 min_total_div = DIV_ROUND_UP(i2c_rate_khz, scl_rate_khz * 8);
491
492 /* These are the min dividers needed for min hold times. */
493 min_low_div = DIV_ROUND_UP(i2c_rate_khz * min_low_ns, 8 * 1000000);
494 min_high_div = DIV_ROUND_UP(i2c_rate_khz * min_high_ns, 8 * 1000000);
495 min_div_for_hold = (min_low_div + min_high_div);
496
497 /*
498 * This is the maximum divider so we don't go over the max.
499 * We don't round up here (we round down) since this is a max.
439 */ 500 */
440 div = DIV_ROUND_UP(i2c_rate, scl_rate * 16) - 1; 501 max_low_div = i2c_rate_khz * max_low_ns / (8 * 1000000);
502
503 if (min_low_div > max_low_div) {
504 WARN_ONCE(true,
505 "Conflicting, min_low_div %lu, max_low_div %lu\n",
506 min_low_div, max_low_div);
507 max_low_div = min_low_div;
508 }
441 509
442 i2c_writel(i2c, (div << 16) | (div & 0xffff), REG_CLKDIV); 510 if (min_div_for_hold > min_total_div) {
511 /*
512 * Time needed to meet hold requirements is important.
513 * Just use that.
514 */
515 *div_low = min_low_div;
516 *div_high = min_high_div;
517 } else {
518 /*
519 * We've got to distribute some time among the low and high
520 * so we don't run too fast.
521 */
522 extra_div = min_total_div - min_div_for_hold;
523
524 /*
525 * We'll try to split things up perfectly evenly,
526 * biasing slightly towards having a higher div
527 * for low (spend more time low).
528 */
529 ideal_low_div = DIV_ROUND_UP(i2c_rate_khz * min_low_ns,
530 scl_rate_khz * 8 * min_total_ns);
531
532 /* Don't allow it to go over the max */
533 if (ideal_low_div > max_low_div)
534 ideal_low_div = max_low_div;
535
536 /*
537 * Handle when the ideal low div is going to take up
538 * more than we have.
539 */
540 if (ideal_low_div > min_low_div + extra_div)
541 ideal_low_div = min_low_div + extra_div;
542
543 /* Give low the "ideal" and give high whatever extra is left */
544 extra_low_div = ideal_low_div - min_low_div;
545 *div_low = ideal_low_div;
546 *div_high = min_high_div + (extra_div - extra_low_div);
547 }
548
549 /*
550 * Adjust to the fact that the hardware has an implicit "+1".
551 * NOTE: Above calculations always produce div_low > 0 and div_high > 0.
552 */
553 *div_low = *div_low - 1;
554 *div_high = *div_high - 1;
555
556 if (*div_low >= 0xffff || *div_high >= 0xffff)
557 return -EINVAL;
558 else
559 return 0;
560}
561
562static int rk3x_i2c_set_scl_rate(struct rk3x_i2c *i2c, unsigned long scl_rate)
563{
564 unsigned long i2c_rate = clk_get_rate(i2c->clk);
565 unsigned long div_low, div_high;
566 u64 t_low_ns, t_high_ns;
567 int ret = 0;
568
569 ret = rk3x_i2c_calc_divs(i2c_rate, scl_rate, &div_low, &div_high);
570 if (ret < 0)
571 return ret;
572
573 i2c_writel(i2c, (div_high << 16) | (div_low & 0xffff), REG_CLKDIV);
574
575 t_low_ns = div_u64(((u64)div_low + 1) * 8 * 1000000000, i2c_rate);
576 t_high_ns = div_u64(((u64)div_high + 1) * 8 * 1000000000, i2c_rate);
577 dev_dbg(i2c->dev,
578 "CLK %lukhz, Req %luns, Act low %lluns high %lluns\n",
579 i2c_rate / 1000,
580 1000000000 / scl_rate,
581 t_low_ns, t_high_ns);
582
583 return ret;
443} 584}
444 585
445/** 586/**
@@ -537,7 +678,9 @@ static int rk3x_i2c_xfer(struct i2c_adapter *adap,
537 clk_enable(i2c->clk); 678 clk_enable(i2c->clk);
538 679
539 /* The clock rate might have changed, so setup the divider again */ 680 /* The clock rate might have changed, so setup the divider again */
540 rk3x_i2c_set_scl_rate(i2c, i2c->scl_frequency); 681 ret = rk3x_i2c_set_scl_rate(i2c, i2c->scl_frequency);
682 if (ret < 0)
683 goto exit;
541 684
542 i2c->is_last_msg = false; 685 i2c->is_last_msg = false;
543 686
@@ -585,6 +728,7 @@ static int rk3x_i2c_xfer(struct i2c_adapter *adap,
585 } 728 }
586 } 729 }
587 730
731exit:
588 clk_disable(i2c->clk); 732 clk_disable(i2c->clk);
589 spin_unlock_irqrestore(&i2c->lock, flags); 733 spin_unlock_irqrestore(&i2c->lock, flags);
590 734