From patchwork Mon Apr 25 14:33:37 2016 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Jan Glauber X-Patchwork-Id: 614505 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by ozlabs.org (Postfix) with ESMTP id 3qtphc48QJz9s3T for ; Tue, 26 Apr 2016 00:37:04 +1000 (AEST) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1754903AbcDYOeG (ORCPT ); Mon, 25 Apr 2016 10:34:06 -0400 Received: from mail-wm0-f67.google.com ([74.125.82.67]:34238 "EHLO mail-wm0-f67.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1754824AbcDYOeE (ORCPT ); Mon, 25 Apr 2016 10:34:04 -0400 Received: by mail-wm0-f67.google.com with SMTP id n3so25456100wmn.1; Mon, 25 Apr 2016 07:34:03 -0700 (PDT) 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:in-reply-to :references:mime-version:content-transfer-encoding; bh=YAcO4uR2lfeuEdNVtwMF/wxYDrON9m1RA0kcCPlEDGY=; b=F5GLsOFEfJxbkYaCCxiAou2B9XjT3e2QlRNL+4My4Fb8D+JNgnwFga+hdM5pNSPwDq dFqfBqI0m5GJZkV5bGENT8YDueWClYWC0OEiOkmevtxjrMoch7wURZtIpcDfEQF5AJu+ abGP4gOqFeZM+9yZnbUqg0U2/F5B4fWw/AKPMCER8j8TGZJsdjhv0PcXDE/ogEy7ZCnU cDwqxl3+xg3lPLn282ibvmWR3/ORp/io7MJG+cxcxYD3UKGkBRSTy8ERwTWhDSjGVw3v /u8+XP+VvwyKOt/9n9Jm2XpMMzypzdSYJRv/5wgY87hhSPv8T72HkLVk5z/4TzjzFNEW 3KkQ== X-Gm-Message-State: AOPr4FVuMKHXS7ZgA2MnqGFqMWZNiDBD1EJodc89hgfBCRzAJMD0RtCw64a9PHNv+8CTZQ== X-Received: by 10.194.3.20 with SMTP id 20mr40722160wjy.59.1461594842751; Mon, 25 Apr 2016 07:34:02 -0700 (PDT) Received: from wintermute.fritz.box (HSI-KBW-46-223-170-19.hsi.kabel-badenwuerttemberg.de. [46.223.170.19]) by smtp.gmail.com with ESMTPSA id o73sm19084272wme.16.2016.04.25.07.34.01 (version=TLSv1/SSLv3 cipher=OTHER); Mon, 25 Apr 2016 07:34:02 -0700 (PDT) From: Jan Glauber To: Wolfram Sang Cc: linux-kernel@vger.kernel.org, linux-i2c@vger.kernel.org, David Daney , Peter Swain , Jan Glauber Subject: [PATCH v7 08/15] i2c: octeon: Improve performance if interrupt is early Date: Mon, 25 Apr 2016 16:33:37 +0200 Message-Id: <1461594824-7215-9-git-send-email-jglauber@cavium.com> X-Mailer: git-send-email 1.9.1 In-Reply-To: <1461594824-7215-1-git-send-email-jglauber@cavium.com> References: <1461594824-7215-1-git-send-email-jglauber@cavium.com> MIME-Version: 1.0 Sender: linux-i2c-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-i2c@vger.kernel.org From: Peter Swain There is a race between the TWSI interrupt and the condition that is required before proceeding: Low-level: interrupt flag bit must be set High-level controller: valid bit must be clear If the interrupt comes too early and the condition is not met the wait will time out, and the transfer is aborted leading to very poor performance. To avoid this race retry for the condition ~80 µs later. The retry is avoided on the very first invocation of wait_event_timeout() (which tests the condition before entering the wait and is therefore always wrong in this case). EEPROM reads on 100kHz i2c now measure ~5.2kB/s, about 1/2 what's achievable, and much better than the worst-case 100 bytes/sec before. While at it remove the debug print from the low-level wait function. Signed-off-by: Peter Swain Signed-off-by: Jan Glauber --- drivers/i2c/busses/i2c-octeon.c | 55 ++++++++++++++++++++++++++++++++++------- 1 file changed, 46 insertions(+), 9 deletions(-) diff --git a/drivers/i2c/busses/i2c-octeon.c b/drivers/i2c/busses/i2c-octeon.c index 76fa479..3144fe9 100644 --- a/drivers/i2c/busses/i2c-octeon.c +++ b/drivers/i2c/busses/i2c-octeon.c @@ -109,6 +109,8 @@ #define TWSI_INT_SDA BIT_ULL(10) #define TWSI_INT_SCL BIT_ULL(11) +#define I2C_OCTEON_EVENT_WAIT 80 /* microseconds */ + struct octeon_i2c { wait_queue_head_t queue; struct i2c_adapter adap; @@ -340,11 +342,29 @@ static irqreturn_t octeon_i2c_hlc_isr78(int irq, void *dev_id) return IRQ_HANDLED; } -static int octeon_i2c_test_iflg(struct octeon_i2c *i2c) +static bool octeon_i2c_test_iflg(struct octeon_i2c *i2c) { return (octeon_i2c_ctl_read(i2c) & TWSI_CTL_IFLG); } +static bool octeon_i2c_test_ready(struct octeon_i2c *i2c, bool *first) +{ + if (octeon_i2c_test_iflg(i2c)) + return true; + + if (*first) { + *first = false; + return false; + } + + /* + * IRQ has signaled an event but IFLG hasn't changed. + * Sleep and retry once. + */ + usleep_range(I2C_OCTEON_EVENT_WAIT, 2 * I2C_OCTEON_EVENT_WAIT); + return octeon_i2c_test_iflg(i2c); +} + /** * octeon_i2c_wait - wait for the IFLG to be set * @i2c: The struct octeon_i2c @@ -354,15 +374,14 @@ static int octeon_i2c_test_iflg(struct octeon_i2c *i2c) static int octeon_i2c_wait(struct octeon_i2c *i2c) { long time_left; + bool first = 1; i2c->int_enable(i2c); - time_left = wait_event_timeout(i2c->queue, octeon_i2c_test_iflg(i2c), + time_left = wait_event_timeout(i2c->queue, octeon_i2c_test_ready(i2c, &first), i2c->adap.timeout); i2c->int_disable(i2c); - if (!time_left) { - dev_dbg(i2c->dev, "%s: timeout\n", __func__); + if (!time_left) return -ETIMEDOUT; - } return 0; } @@ -428,11 +447,28 @@ static int octeon_i2c_check_status(struct octeon_i2c *i2c, int final_read) } } -static bool octeon_i2c_hlc_test_ready(struct octeon_i2c *i2c) +static bool octeon_i2c_hlc_test_valid(struct octeon_i2c *i2c) +{ + return (__raw_readq(i2c->twsi_base + SW_TWSI) & SW_TWSI_V) == 0; +} + +static bool octeon_i2c_hlc_test_ready(struct octeon_i2c *i2c, bool *first) { - u64 val = __raw_readq(i2c->twsi_base + SW_TWSI); + /* check if valid bit is cleared */ + if (octeon_i2c_hlc_test_valid(i2c)) + return true; - return (val & SW_TWSI_V) == 0; + if (*first) { + *first = false; + return false; + } + + /* + * IRQ has signaled an event but valid bit isn't cleared. + * Sleep and retry once. + */ + usleep_range(I2C_OCTEON_EVENT_WAIT, 2 * I2C_OCTEON_EVENT_WAIT); + return octeon_i2c_hlc_test_valid(i2c); } static void octeon_i2c_hlc_int_enable(struct octeon_i2c *i2c) @@ -454,11 +490,12 @@ static void octeon_i2c_hlc_int_clear(struct octeon_i2c *i2c) */ static int octeon_i2c_hlc_wait(struct octeon_i2c *i2c) { + bool first = 1; int time_left; i2c->hlc_int_enable(i2c); time_left = wait_event_timeout(i2c->queue, - octeon_i2c_hlc_test_ready(i2c), + octeon_i2c_hlc_test_ready(i2c, &first), i2c->adap.timeout); i2c->hlc_int_disable(i2c); if (!time_left) {