Return-Path: Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1753392AbcCGPNF (ORCPT ); Mon, 7 Mar 2016 10:13:05 -0500 Received: from mail-wm0-f67.google.com ([74.125.82.67]:33687 "EHLO mail-wm0-f67.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1753167AbcCGPLZ (ORCPT ); Mon, 7 Mar 2016 10:11:25 -0500 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 v3 10/14] i2c-octeon: Faster operation when IFLG signals late Date: Mon, 7 Mar 2016 16:10:53 +0100 Message-Id: <1f31d27c16e4bb80eccbf6f9dc24361a398a9b75.1457362545.git.jglauber@cavium.com> X-Mailer: git-send-email 1.9.1 In-Reply-To: References: In-Reply-To: References: Sender: linux-kernel-owner@vger.kernel.org List-ID: X-Mailing-List: linux-kernel@vger.kernel.org Content-Length: 2514 Lines: 77 From: Peter Swain Some versions can deliver low-level twsi irq before twsi_ctl.iflg is set, leading to timeout-driven i/o. When an irq signals event, but woken task does not see the expected twsi_ctl.iflg, re-check about 80uS later. 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. Signed-off-by: Peter Swain Signed-off-by: Jan Glauber Acked-by: David Daney --- drivers/i2c/busses/i2c-octeon.c | 31 ++++++++++++++++++++++++++++++- 1 file changed, 30 insertions(+), 1 deletion(-) diff --git a/drivers/i2c/busses/i2c-octeon.c b/drivers/i2c/busses/i2c-octeon.c index a8a56a0..afca316 100644 --- a/drivers/i2c/busses/i2c-octeon.c +++ b/drivers/i2c/busses/i2c-octeon.c @@ -126,6 +126,14 @@ static int timeout = 2; module_param(timeout, int, 0444); MODULE_PARM_DESC(timeout, "Low-level device timeout (ms)"); +/* + * On some hardware IFLG is not visible in TWSI_CTL until after low-level IRQ, + * so re-sample CTL a short time later to avoid stalls. + */ +static int irq_early_us = 80; +module_param(irq_early_us, int, 0644); +MODULE_PARM_DESC(irq_early_us, "Re-poll for IFLG after IRQ (us)"); + static void writeqflush(u64 val, void __iomem *addr) { __raw_writeq(val, addr); @@ -339,6 +347,26 @@ static int octeon_i2c_test_iflg(struct octeon_i2c *i2c) return (octeon_i2c_read_ctl(i2c) & TWSI_CTL_IFLG) != 0; } +/* + * Wait-helper which addresses the delayed-IFLAG problem by re-polling for + * missing TWSI_CTL[IFLG] a few us later, when irq has signalled an event, + * but none found. Skip this re-poll on the first (non-wakeup) call. + */ +static int poll_iflg(struct octeon_i2c *i2c, int *first_p) +{ + int iflg = octeon_i2c_test_iflg(i2c); + + if (iflg) + return 1; + if (*first_p) + *first_p = 0; + else { + usleep_range(irq_early_us, 2 * irq_early_us); + iflg = octeon_i2c_test_iflg(i2c); + } + return iflg; +} + /** * octeon_i2c_wait - wait for the IFLG to be set * @i2c: The struct octeon_i2c @@ -348,9 +376,10 @@ static int octeon_i2c_test_iflg(struct octeon_i2c *i2c) static int octeon_i2c_wait(struct octeon_i2c *i2c) { long time_left; + int first = 1; i2c->int_en(i2c); - time_left = wait_event_timeout(i2c->queue, octeon_i2c_test_iflg(i2c), + time_left = wait_event_timeout(i2c->queue, poll_iflg(i2c, &first), i2c->adap.timeout); i2c->int_dis(i2c); if (!time_left) { -- 1.9.1