From patchwork Thu Dec 11 23:00:36 2014 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Doug Anderson X-Patchwork-Id: 5478201 Return-Path: X-Original-To: patchwork-linux-arm@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 1BBB79F30B for ; Thu, 11 Dec 2014 23:07:58 +0000 (UTC) Received: from mail.kernel.org (localhost [127.0.0.1]) by mail.kernel.org (Postfix) with ESMTP id C410A201BB for ; Thu, 11 Dec 2014 23:07:56 +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 708AC20115 for ; Thu, 11 Dec 2014 23:07:55 +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 1XzCjT-0006Qt-Kw; Thu, 11 Dec 2014 23:01:15 +0000 Received: from mail-ig0-x229.google.com ([2607:f8b0:4001:c05::229]) by bombadil.infradead.org with esmtps (Exim 4.80.1 #2 (Red Hat Linux)) id 1XzCjO-0006N3-Sp for linux-arm-kernel@lists.infradead.org; Thu, 11 Dec 2014 23:01:12 +0000 Received: by mail-ig0-f169.google.com with SMTP id hl2so1635099igb.2 for ; Thu, 11 Dec 2014 15:00:47 -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=SjnBLxf+HZRYeTT7XLV4sjx2B90uuxcie7ebIxsqb6Y=; b=WiQINMYhOU00GhSboxhcbJoAmWAXAtCA8b7EpIoW7cF3LoAq0bgedDo9OWWlMd9n+T C715Uvrsb6SlLm66euLKC2RHTWYBy/ewEsiyen7O5mRwcjZOVoBmH/gU12KaF9QWJ578 5H47weEyjiwhycJA2jv7mmqddMJ15Uf3ghyJM= 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=SjnBLxf+HZRYeTT7XLV4sjx2B90uuxcie7ebIxsqb6Y=; b=JCkTsxcE7WH6lc+qX5rEtdN+XvQ4Xk9V43Sbq/82goATOzpPEMkXh3++pH9bxu5PeG OTmDY823loLnGRiSLW9AX6XYkB0VjBhhBlRIJSCtObul4PI6Z9/3WvvlOEqIcWRIQ5cM vcSmYdwvK4B17Scx7r/6y6umB2vcGpX5cohqVINg8iOISTdKbnveUU7329VwsMwx1jgH /Jt1/rTPZlzFjqhW+9i3zwms09YZPm1Arbug3mMADuK9ZuRrZqBym7uQ7euM+/WfHUbd j+6zWmQBG1rPsIt80LL0XZDw5PZM/hksGPVVU7pHCWxHmllKmyyum7FmfHQXQrE2rxBl 2KAQ== X-Gm-Message-State: ALoCoQnka/BuTSSzkleqjGx/9EJEuiJULHbzRv9X4rR7WUhtnxVsi5eVtcqbwiIrKXEdlKqc2JDm X-Received: by 10.50.79.135 with SMTP id j7mr1682809igx.14.1418338846164; Thu, 11 Dec 2014 15:00:46 -0800 (PST) Received: from tictac.mtv.corp.google.com ([172.22.65.76]) by mx.google.com with ESMTPSA id i3sm1254583iod.19.2014.12.11.15.00.44 for (version=TLSv1.2 cipher=ECDHE-RSA-AES128-SHA bits=128/128); Thu, 11 Dec 2014 15:00:45 -0800 (PST) From: Doug Anderson To: wsa@the-dreams.de, max.schwarz@online.de Subject: [PATCH v2] i2c: rk3x: Account for repeated start time requirement Date: Thu, 11 Dec 2014 15:00:36 -0800 Message-Id: <1418338836-21838-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_150111_023154_9321DED1 X-CRM114-Status: GOOD ( 22.12 ) X-Spam-Score: -0.1 (/) 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-arm-kernel@lists.infradead.org X-Mailman-Version: 2.1.18-1 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , MIME-Version: 1.0 Sender: "linux-arm-kernel" Errors-To: linux-arm-kernel-bounces+patchwork-linux-arm=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 . Changes in v2: - Also multiply SCL rise time by 2 for repeated start calculation Documentation/devicetree/bindings/i2c/i2c-rk3x.txt | 7 +- drivers/i2c/busses/i2c-rk3x.c | 89 +++++++++++++++++----- 2 files changed, 73 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..f0e5da9 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,31 @@ 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((scl_rise_ns + spec_setup_start) * 2, + scl_rise_ns + spec_setup_start + + sda_fall_ns + spec_min_high_ns); + else + min_high_ns = scl_rise_ns + spec_min_high_ns; + + min_low_ns = scl_fall_ns + spec_min_low_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 +627,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 +687,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 +823,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 +919,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;