From patchwork Thu Dec 11 19:18:31 2014 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Doug Anderson X-Patchwork-Id: 5477211 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 852D69F30B for ; Thu, 11 Dec 2014 19:19:21 +0000 (UTC) Received: from mail.kernel.org (localhost [127.0.0.1]) by mail.kernel.org (Postfix) with ESMTP id CFAD42014A for ; Thu, 11 Dec 2014 19:19:19 +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 46EAA2013A for ; Thu, 11 Dec 2014 19:19:18 +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 1Xz9Ge-0002Ug-VL; Thu, 11 Dec 2014 19:19:16 +0000 Received: from mail-ig0-x236.google.com ([2607:f8b0:4001:c05::236]) by bombadil.infradead.org with esmtps (Exim 4.80.1 #2 (Red Hat Linux)) id 1Xz9Gb-0002AO-M7 for linux-rockchip@lists.infradead.org; Thu, 11 Dec 2014 19:19:15 +0000 Received: by mail-ig0-f182.google.com with SMTP id hn15so210154igb.3 for ; Thu, 11 Dec 2014 11:18:52 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=chromium.org; s=google; h=from:to:cc:subject:date:message-id; bh=wub97oxZ1ea2Q52T5woxLHM9kPb5A1VwERdGONHg7Vs=; b=IFsNTcsFn3XwO9ns36McJO+TyCSujC6ZM0KZOa6pCETj1u16MLRatD6B+1JKlRxhP1 bqwzMjbvnGzYnMvISFQsw3b5mUjPWI4pqxFEP4i1PLQc4QLGI2HqNwmBnpfO0jg04PPs 6KLmxgZHz+Nx7rBWOdw4j7M8CCHCY5qLWXhTo= X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20130820; h=x-gm-message-state:from:to:cc:subject:date:message-id; bh=wub97oxZ1ea2Q52T5woxLHM9kPb5A1VwERdGONHg7Vs=; b=ez1u1UZjPchVBGvMcYBXTE7aHtq/MCqdqJ89ACgJ/Fd75lG5MBhX67mL/of8WyT2rT IPTuWOYiY/6tizHjIf9cTdaFnxGb/kN0f8JaLaGeG0jjn9hpaYF/B+owpGyzBG7xMXjk iRj7+CDqBHLYx1chaVIj/eBWBUoCPQbS3t3gl/q5HeAdbCUr1yNxZfRFUbzURr4qlVUV 3T6Wm9XFctk7Lh2COuGAw3y4Slv7qiCSyZ3W6EPVsMfcSNRGcg/shiNhfZ8OIg/8E1EX cfOp9Il/hN5nW+9deAvlhocqlp3GvtZaHMVDTDmuVRhlu6XypeWUPigjLJz1gdlXJFeq aIOw== X-Gm-Message-State: ALoCoQk1MH8DHlbpnT8oTr8Iz+V114lTgoYgjBqiZBULTTeMpNgU8LkEg0WbckdxzOi5bQTml6Ag X-Received: by 10.107.10.207 with SMTP id 76mr6049999iok.78.1418325532652; Thu, 11 Dec 2014 11:18:52 -0800 (PST) Received: from tictac.mtv.corp.google.com ([172.22.65.76]) by mx.google.com with ESMTPSA id mx10sm148142igb.21.2014.12.11.11.18.51 (version=TLSv1.2 cipher=ECDHE-RSA-AES128-SHA bits=128/128); Thu, 11 Dec 2014 11:18:52 -0800 (PST) From: Doug Anderson To: wsa@the-dreams.de, max.schwarz@online.de Subject: [PATCH] i2c: rk3x: Account for repeated start time requirement Date: Thu, 11 Dec 2014 11:18:31 -0800 Message-Id: <1418325511-16648-1-git-send-email-dianders@chromium.org> X-Mailer: git-send-email 2.2.0.rc0.207.ga3a616c X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20141211_111913_819424_32D6C552 X-CRM114-Status: GOOD ( 19.75 ) X-Spam-Score: -0.8 (/) Cc: mark.rutland@arm.com, devicetree@vger.kernel.org, addy.ke@rock-chips.com, cyw@rock-chips.com, hl@rock-chips.com, pawel.moll@arm.com, ijc+devicetree@hellion.org.uk, galak@codeaurora.org, Doug Anderson , linux-kernel@vger.kernel.org, linux-rockchip@lists.infradead.org, robh+dt@kernel.org, linux-i2c@vger.kernel.org, u.kleine-koenig@pengutronix.de, linux-arm-kernel@lists.infradead.org, heiko@sntech.de 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=-4.1 required=5.0 tests=BAYES_00,DKIM_SIGNED, RCVD_IN_DNSWL_MED, T_DKIM_INVALID, T_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 On Rockchip I2C the controller drops SDA low in the repeated start condition at half the SCL high time. If we want to meet timing requirements, that means we need to hold SCL high for (4.7us * 2) when we're sending a repeated start (.6us * 2 for Fast-mode). That lets us achieve minimum tSU;STA. However, we don't want to always hold SCL high for that long because we'd never be able to make 100kHz or 400kHz speeds. Let's fix this by doing our clock calculations twice: once taking the above into account and once running at normal speeds. We'll use the slower speed when sending the start bit and the normal speed otherwise. Note: we really only need the conservative timing when sending _repeated_ starts, not when sending the first start. We don't account for this so technically the first start bit will be longer too. ...well, except in the case when we use the combined write/read optimization which doesn't use the same code. As part of this change we needed to account for the SDA falling time. The specification indicates that this should be the same, but we'll follow Designware and add a binding. Note that we deviate from Designware and assign the default SDA falling time to be the same as the SCL falling time, which is incredibly likely. Signed-off-by: Doug Anderson --- Note: This is based on Addy's patch (i2c: rk3x: fix bug that cause measured high_ns doesn't meet I2C specification) that can be found at . Documentation/devicetree/bindings/i2c/i2c-rk3x.txt | 7 +- drivers/i2c/busses/i2c-rk3x.c | 90 +++++++++++++++++----- 2 files changed, 74 insertions(+), 23 deletions(-) diff --git a/Documentation/devicetree/bindings/i2c/i2c-rk3x.txt b/Documentation/devicetree/bindings/i2c/i2c-rk3x.txt index 1885bd8..8cc75d9 100644 --- a/Documentation/devicetree/bindings/i2c/i2c-rk3x.txt +++ b/Documentation/devicetree/bindings/i2c/i2c-rk3x.txt @@ -21,14 +21,17 @@ Required on RK3066, RK3188 : Optional properties : - clock-frequency : SCL frequency to use (in Hz). If omitted, 100kHz is used. - - i2c-scl-rising-time-ns : Number of nanoseconds the signal takes to rise + - i2c-scl-rising-time-ns : Number of nanoseconds the SCL signal takes to rise (t(r) in I2C specification). If not specified this is assumed to be the maximum the specification allows(1000 ns for Standard-mode, 300 ns for Fast-mode) which might cause slightly slower communication. - - i2c-scl-falling-time-ns : Number of nanoseconds the signal takes to fall + - i2c-scl-falling-time-ns : Number of nanoseconds the SCL signal takes to fall (t(f) in the I2C specification). If not specified this is assumed to be the maximum the specification allows (300 ns) which might cause slightly slowercommunication. + - i2c-sda-falling-time-ns : Number of nanoseconds the SDA signal takes to fall + (t(f) in the I2C specification). If not specified we'll use the SCL + value since they are the same in nearly all cases. Example: diff --git a/drivers/i2c/busses/i2c-rk3x.c b/drivers/i2c/busses/i2c-rk3x.c index 36a9224..1f994df 100644 --- a/drivers/i2c/busses/i2c-rk3x.c +++ b/drivers/i2c/busses/i2c-rk3x.c @@ -102,8 +102,14 @@ struct rk3x_i2c { /* Settings */ unsigned int scl_frequency; - unsigned int rise_ns; - unsigned int fall_ns; + unsigned int scl_rise_ns; + unsigned int scl_fall_ns; + unsigned int sda_fall_ns; + + /* DIV changes when we're sending a repeated start; keep both */ + bool sending_start; + u32 div_normal; + u32 div_start; /* Synchronization & notification */ spinlock_t lock; @@ -146,6 +152,9 @@ static void rk3x_i2c_start(struct rk3x_i2c *i2c) { u32 val; + i2c->sending_start = true; + i2c_writel(i2c, i2c->div_start, REG_CLKDIV); + rk3x_i2c_clean_ipd(i2c); i2c_writel(i2c, REG_INT_START, REG_IEN); @@ -281,6 +290,9 @@ static void rk3x_i2c_handle_start(struct rk3x_i2c *i2c, unsigned int ipd) /* disable start bit */ i2c_writel(i2c, i2c_readl(i2c, REG_CON) & ~REG_CON_START, REG_CON); + i2c->sending_start = false; + i2c_writel(i2c, i2c->div_normal, REG_CLKDIV); + /* enable appropriate interrupts and transition */ if (i2c->mode == REG_CON_MOD_TX) { i2c_writel(i2c, REG_INT_MBTF | REG_INT_NAKRCV, REG_IEN); @@ -437,21 +449,26 @@ out: * * @clk_rate: I2C input clock rate * @scl_rate: Desired SCL rate - * @rise_ns: How many ns it takes for signals to rise. - * @fall_ns: How many ns it takes for signals to fall. + * @scl_rise_ns: How many ns it takes for SCL to rise. + * @scl_fall_ns: How many ns it takes for SCL to fall. + * @sda_fall_ns: How many ns it takes for SDA to fall. * @div_low: Divider output for low * @div_high: Divider output for high + * @for_start: Take into account that we might be sending a start bit. * * Returns: 0 on success, -EINVAL if the goal SCL rate is too slow. In that case * a best-effort divider value is returned in divs. If the target rate is * too high, we silently use the highest possible rate. */ static int rk3x_i2c_calc_divs(unsigned long clk_rate, unsigned long scl_rate, - unsigned long rise_ns, unsigned long fall_ns, - unsigned long *div_low, unsigned long *div_high) + unsigned long scl_rise_ns, + unsigned long scl_fall_ns, + unsigned long sda_fall_ns, + unsigned long *div_low, unsigned long *div_high, + bool for_start) { unsigned long spec_min_low_ns, spec_min_high_ns; - unsigned long spec_max_data_hold_ns; + unsigned long spec_setup_start, spec_max_data_hold_ns; unsigned long data_hold_buffer_ns; unsigned long min_low_ns, min_high_ns; @@ -490,18 +507,32 @@ static int rk3x_i2c_calc_divs(unsigned long clk_rate, unsigned long scl_rate, if (scl_rate <= 100000) { /* Standard-mode */ spec_min_low_ns = 4700; + spec_setup_start = 4700; spec_min_high_ns = 4000; spec_max_data_hold_ns = 3450; data_hold_buffer_ns = 50; } else { /* Fast-mode */ spec_min_low_ns = 1300; + spec_setup_start = 600; spec_min_high_ns = 600; spec_max_data_hold_ns = 900; data_hold_buffer_ns = 50; } - min_low_ns = spec_min_low_ns + fall_ns; - min_high_ns = spec_min_high_ns + rise_ns; + /* + * For repeated start we need at least (spec_setup_start * 2) to meet + * (tSU;SDA) requirements. The controller drops data low at half the + * high time). Also need to meet normal specification requirements. + */ + if (for_start) + min_high_ns = max(spec_setup_start * 2, + spec_setup_start + sda_fall_ns + + spec_min_high_ns); + else + min_high_ns = spec_min_high_ns; + min_high_ns += scl_rise_ns; + + min_low_ns = spec_min_low_ns + scl_fall_ns; max_low_ns = spec_max_data_hold_ns * 2 - data_hold_buffer_ns; min_total_ns = min_low_ns + min_high_ns; @@ -597,15 +628,28 @@ static void rk3x_i2c_adapt_div(struct rk3x_i2c *i2c, unsigned long clk_rate) { unsigned long div_low, div_high; u64 t_low_ns, t_high_ns; + unsigned long flags; int ret; - ret = rk3x_i2c_calc_divs(clk_rate, i2c->scl_frequency, i2c->rise_ns, - i2c->fall_ns, &div_low, &div_high); + ret = rk3x_i2c_calc_divs(clk_rate, i2c->scl_frequency, i2c->scl_rise_ns, + i2c->scl_fall_ns, i2c->sda_fall_ns, + &div_low, &div_high, true); + i2c->div_start = (div_high << 16) | (div_low & 0xffff); + WARN_ONCE(ret != 0, "Could not reach SCL freq %u", i2c->scl_frequency); + ret = rk3x_i2c_calc_divs(clk_rate, i2c->scl_frequency, i2c->scl_rise_ns, + i2c->scl_fall_ns, i2c->sda_fall_ns, + &div_low, &div_high, false); + i2c->div_normal = (div_high << 16) | (div_low & 0xffff); WARN_ONCE(ret != 0, "Could not reach SCL freq %u", i2c->scl_frequency); clk_enable(i2c->clk); - i2c_writel(i2c, (div_high << 16) | (div_low & 0xffff), REG_CLKDIV); + spin_lock_irqsave(&i2c->lock, flags); + if (i2c->sending_start) + i2c_writel(i2c, i2c->div_start, REG_CLKDIV); + else + i2c_writel(i2c, i2c->div_normal, REG_CLKDIV); + spin_unlock_irqrestore(&i2c->lock, flags); clk_disable(i2c->clk); t_low_ns = div_u64(((u64)div_low + 1) * 8 * 1000000000, clk_rate); @@ -644,8 +688,9 @@ static int rk3x_i2c_clk_notifier_cb(struct notifier_block *nb, unsigned long switch (event) { case PRE_RATE_CHANGE: if (rk3x_i2c_calc_divs(ndata->new_rate, i2c->scl_frequency, - i2c->rise_ns, i2c->fall_ns, &div_low, - &div_high) != 0) + i2c->scl_rise_ns, i2c->scl_fall_ns, + i2c->sda_fall_ns, + &div_low, &div_high, true) != 0) return NOTIFY_STOP; /* scale up */ @@ -779,10 +824,10 @@ static int rk3x_i2c_xfer(struct i2c_adapter *adap, if (i + ret >= num) i2c->is_last_msg = true; - spin_unlock_irqrestore(&i2c->lock, flags); - rk3x_i2c_start(i2c); + spin_unlock_irqrestore(&i2c->lock, flags); + timeout = wait_event_timeout(i2c->wait, !i2c->busy, msecs_to_jiffies(WAIT_TIMEOUT)); @@ -875,15 +920,18 @@ static int rk3x_i2c_probe(struct platform_device *pdev) * the default maximum timing from the specification. */ if (of_property_read_u32(pdev->dev.of_node, "i2c-scl-rising-time-ns", - &i2c->rise_ns)) { + &i2c->scl_rise_ns)) { if (i2c->scl_frequency <= 100000) - i2c->rise_ns = 1000; + i2c->scl_rise_ns = 1000; else - i2c->rise_ns = 300; + i2c->scl_rise_ns = 300; } if (of_property_read_u32(pdev->dev.of_node, "i2c-scl-falling-time-ns", - &i2c->fall_ns)) - i2c->fall_ns = 300; + &i2c->scl_fall_ns)) + i2c->scl_fall_ns = 300; + if (of_property_read_u32(pdev->dev.of_node, "i2c-sda-falling-time-ns", + &i2c->scl_fall_ns)) + i2c->sda_fall_ns = i2c->scl_fall_ns; strlcpy(i2c->adap.name, "rk3x-i2c", sizeof(i2c->adap.name)); i2c->adap.owner = THIS_MODULE;