From patchwork Sat Sep 27 07:11:28 2014 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: addy ke X-Patchwork-Id: 4989151 Return-Path: X-Original-To: patchwork-linux-rockchip@patchwork.kernel.org Delivered-To: patchwork-parsemail@patchwork1.web.kernel.org Received: from mail.kernel.org (mail.kernel.org [198.145.19.201]) by patchwork1.web.kernel.org (Postfix) with ESMTP id A593C9F2BB for ; Sat, 27 Sep 2014 07:12:38 +0000 (UTC) Received: from mail.kernel.org (localhost [127.0.0.1]) by mail.kernel.org (Postfix) with ESMTP id AEFFB201FB for ; Sat, 27 Sep 2014 07:12:37 +0000 (UTC) Received: from bombadil.infradead.org (bombadil.infradead.org [198.137.202.9]) (using TLSv1.2 with cipher DHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mail.kernel.org (Postfix) with ESMTPS id 744692013A for ; Sat, 27 Sep 2014 07:12:36 +0000 (UTC) Received: from localhost ([127.0.0.1] helo=bombadil.infradead.org) by bombadil.infradead.org with esmtp (Exim 4.80.1 #2 (Red Hat Linux)) id 1XXmBD-0006oR-8w; Sat, 27 Sep 2014 07:12:31 +0000 Received: from regular1.263xmail.com ([211.150.99.131]) by bombadil.infradead.org with esmtps (Exim 4.80.1 #2 (Red Hat Linux)) id 1XXmB1-0006dt-LP; Sat, 27 Sep 2014 07:12:22 +0000 Received: from addy.ke?rock-chips.com (unknown [192.168.167.84]) by regular1.263xmail.com (Postfix) with SMTP id 936D224F4; Sat, 27 Sep 2014 15:11:44 +0800 (CST) X-263anti-spam: KSV:0; X-MAIL-GRAY: 0 X-MAIL-DELIVERY: 1 X-KSVirus-check: 0 X-ABS-CHECKED: 4 Received: from addy-vm.localdomain (localhost.localdomain [127.0.0.1]) by smtp.263.net (Postfix) with ESMTP id A45B01EB38; Sat, 27 Sep 2014 15:11:40 +0800 (CST) X-RL-SENDER: addy.ke@rock-chips.com X-FST-TO: wsa@the-dreams.de X-SENDER-IP: 127.0.0.1 X-LOGIN-NAME: addy.ke@rock-chips.com X-UNIQUE-TAG: <403e4710341bb600c92f00f61fe06e4c> X-ATTACHMENT-NUM: 0 X-SENDER: kfx@rock-chips.com X-DNS-TYPE: 1 Received: from addy-vm.localdomain (localhost [127.0.0.1]) by smtp.263.net (Postfix) whith ESMTP id 287662IJYBL; Sat, 27 Sep 2014 15:11:40 +0800 (CST) From: Addy Ke To: wsa@the-dreams.de, max.schwarz@online.de, heiko@sntech.de, olof@lixom.net, dianders@chromium.org Subject: [PATCH v2] i2c: rk3x: adjust the LOW divison based on characteristics of SCL Date: Sat, 27 Sep 2014 15:11:28 +0800 Message-Id: <1411801888-3152-1-git-send-email-addy.ke@rock-chips.com> X-Mailer: git-send-email 1.8.3.2 X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20140927_001220_032589_A750F11C X-CRM114-Status: GOOD ( 10.98 ) X-Spam-Score: -0.2 (/) Cc: huangtao@rock-chips.com, Addy , hl@rock-chips.com, yzq@rock-chips.com, zyw@rock-chips.com, linux-kernel@vger.kernel.org, kever.yang@rock-chips.com, linux-rockchip@lists.infradead.org, xjq@rock-chips.com, linux-i2c@vger.kernel.org, caesar.wang@rock-chips.com, cf@rock-chips.com, hj@rock-chips.com, zhengsq@rock-chips.com, linux-arm-kernel@lists.infradead.org X-BeenThere: linux-rockchip@lists.infradead.org X-Mailman-Version: 2.1.18-1 Precedence: list List-Id: Upstream kernel work for Rockchip platforms List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , MIME-Version: 1.0 Sender: "Linux-rockchip" Errors-To: linux-rockchip-bounces+patchwork-linux-rockchip=patchwork.kernel.org@lists.infradead.org X-Spam-Status: No, score=-2.8 required=5.0 tests=BAYES_00, RCVD_IN_DNSWL_NONE, RP_MATCHES_RCVD, UNPARSEABLE_RELAY autolearn=unavailable version=3.3.1 X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on mail.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP From: Addy 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. After put this patch, we can find that min_low_ns is about 5.4us in Standard-mode and 1.6us in Fast-mode. Thanks Doug for the suggestion about division formulas. Signed-off-by: Addy --- Changes in v2: - remove Fast-mode plus and HS-mode - use new formulas suggested by Doug drivers/i2c/busses/i2c-rk3x.c | 104 +++++++++++++++++++++++++++++++++++++++--- 1 file changed, 97 insertions(+), 7 deletions(-) diff --git a/drivers/i2c/busses/i2c-rk3x.c b/drivers/i2c/busses/i2c-rk3x.c index e637c32..81672a8 100644 --- a/drivers/i2c/busses/i2c-rk3x.c +++ b/drivers/i2c/busses/i2c-rk3x.c @@ -428,19 +428,109 @@ out: return IRQ_HANDLED; } +static void rk3x_i2c_get_min_ns(unsigned int scl_rate, + unsigned long *min_low_ns, + unsigned long *min_high_ns) +{ + WARN_ON(scl_rate > 400000); + + /* As show in I2C specification: + * - Standard-mode(100KHz): + * min_low_ns is 4700ns + * min_high_ns is 4000ns + * - Fast-mode(400KHz): + * min_low_ns is 1300ns + * min_high_ns is 600ns + * + * (min_low_ns + min_high_ns) up to 10000ns in Standard-mode + * and 2500ns in Fast-mode + */ + if (scl_rate <= 100000) { + *min_low_ns = 4700 + 650; + *min_high_ns = 4000 + 650; + } else { + *min_low_ns = 1300 + 300; + *min_high_ns = 600 + 300; + } +} + +static void rk3x_i2c_calc_divs(unsigned long i2c_rate, unsigned long scl_rate, + unsigned long *div_low, unsigned long *div_high) +{ + unsigned long min_low_ns, min_high_ns, min_total_ns; + unsigned long min_low_div, min_high_div; + unsigned long min_total_div, min_div_for_hold; + unsigned long extra_div, extra_low_div, ideal_low_div; + unsigned long i2c_rate_khz, scl_rate_khz; + + rk3x_i2c_get_min_ns(scl_rate, &min_low_ns, &min_high_ns); + min_total_ns = min_low_ns + min_high_ns; + + /* To avoid from overflow warning */ + i2c_rate_khz = i2c_rate / 1000; + scl_rate_khz = scl_rate / 1000; + + /*We need the total div to be >= this number + * so we don't clock too fast. + */ + min_total_div = DIV_ROUND_UP(i2c_rate_khz, scl_rate_khz * 8); + + /* These are the min dividers needed for hold times */ + min_low_div = DIV_ROUND_UP(i2c_rate_khz * min_low_ns, 8 * 1000000); + min_high_div = DIV_ROUND_UP(i2c_rate_khz * min_high_ns, 8 * 1000000); + min_div_for_hold = (min_low_div + min_high_div); + + if (min_div_for_hold > min_total_div) { + /* Time needed to meet hold requirements is important. + * Just use that + * */ + *div_low = min_low_div; + *div_high = min_high_div; + } else { + /* We've got to distribute some time among the low and high + * so we don't run too fast. + */ + extra_div = min_total_div - min_div_for_hold; + /* We'll try to split things up perfectly evenly, + * biasing slightly towards having a higher div + * for low (spend more time low). + */ + ideal_low_div = DIV_ROUND_UP(i2c_rate_khz * min_low_ns, + scl_rate_khz * 8 * min_total_ns); + /* Handle when the ideal low div is going to take up + * more than we have. + */ + if (ideal_low_div > min_low_div + extra_div) + ideal_low_div = min_low_div + extra_div; + /* Give low the "ideal" and give high whatever extra is left.*/ + extra_low_div = ideal_low_div - min_low_div; + *div_low = ideal_low_div; + *div_high + = min_high_div + (extra_div - extra_low_div); + } + + /* Adjust to the fact that the hardware has an implicit "+1".*/ + *div_low = *div_low - 1; + *div_high = *div_high - 1; +} + static void rk3x_i2c_set_scl_rate(struct rk3x_i2c *i2c, unsigned long scl_rate) { unsigned long i2c_rate = clk_get_rate(i2c->clk); - unsigned int div; + unsigned long div_low, div_high; - /* SCL rate = (clk rate) / (8 * DIV) */ - div = DIV_ROUND_UP(i2c_rate, scl_rate * 8); + /* The formulas in rk3x TRM: + * - T_scl_high = T_clk * (divh + 1) * 8 + * - T_scl_low = T_clk * (divl + 1) * 8 + * */ + rk3x_i2c_calc_divs(i2c_rate, scl_rate, &div_low, &div_high); - /* The lower and upper half of the CLKDIV reg describe the length of - * SCL low & high periods. */ - div = DIV_ROUND_UP(div, 2); + i2c_writel(i2c, (div_high << 16) | (div_low & 0xffff), REG_CLKDIV); - i2c_writel(i2c, (div << 16) | (div & 0xffff), REG_CLKDIV); + dev_dbg(i2c->dev, "scl_ns: %luns, scl_low_ns: %luns, scl_high_ns: %luns\n", + 1000000000/scl_rate, + (1000000000 / i2c_rate) * (div_low + 1) * 8, + (1000000000 / i2c_rate) * (div_high + 1) * 8); } /**