From patchwork Wed Oct 21 13:44:03 2015 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Ludovic Desroches X-Patchwork-Id: 7456811 Return-Path: X-Original-To: patchwork-linux-arm@patchwork.kernel.org Delivered-To: patchwork-parsemail@patchwork2.web.kernel.org Received: from mail.kernel.org (mail.kernel.org [198.145.29.136]) by patchwork2.web.kernel.org (Postfix) with ESMTP id F181CBEEA4 for ; Wed, 21 Oct 2015 13:46:48 +0000 (UTC) Received: from mail.kernel.org (localhost [127.0.0.1]) by mail.kernel.org (Postfix) with ESMTP id 028FD208BA for ; Wed, 21 Oct 2015 13:46:48 +0000 (UTC) Received: from bombadil.infradead.org (bombadil.infradead.org [198.137.202.9]) (using TLSv1.2 with cipher AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by mail.kernel.org (Postfix) with ESMTPS id E7C17208AD for ; Wed, 21 Oct 2015 13:46:46 +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 1Zotge-0008TN-CQ; Wed, 21 Oct 2015 13:44:16 +0000 Received: from eusmtp01.atmel.com ([212.144.249.242]) by bombadil.infradead.org with esmtps (Exim 4.80.1 #2 (Red Hat Linux)) id 1Zotgb-0008Qn-LW for linux-arm-kernel@lists.infradead.org; Wed, 21 Oct 2015 13:44:14 +0000 Received: from ibiza.corp.atmel.com (10.161.101.13) by eusmtp01.atmel.com (10.161.101.30) with Microsoft SMTP Server id 14.3.235.1; Wed, 21 Oct 2015 15:43:48 +0200 From: Ludovic Desroches To: Subject: [PATCH v2 1/2] i2c: at91: fix write transfers by clearing pending interrupt first Date: Wed, 21 Oct 2015 15:44:03 +0200 Message-ID: <1445435048-25889-1-git-send-email-ludovic.desroches@atmel.com> X-Mailer: git-send-email 2.5.0 MIME-Version: 1.0 X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20151021_064414_102863_04BE499D X-CRM114-Status: GOOD ( 16.98 ) X-Spam-Score: -4.2 (----) X-BeenThere: linux-arm-kernel@lists.infradead.org X-Mailman-Version: 2.1.20 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: peda@lysator.liu.se, nicolas.ferre@atmel.com, linux-kernel@vger.kernel.org, Ludovic Desroches , linux-i2c@vger.kernel.org, cyrille.pitchen@atmel.com, linux-arm-kernel@lists.infradead.org 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.2 required=5.0 tests=BAYES_00, RCVD_IN_DNSWL_MED, 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: Cyrille Pitchen In some cases a NACK interrupt may be pending in the Status Register (SR) as a result of a previous transfer. However at91_do_twi_transfer() did not read the SR to clear pending interruptions before starting a new transfer. Hence a NACK interrupt rose as soon as it was enabled again at the I2C controller level, resulting in a wrong sequence of operations and strange patterns of behaviour on the I2C bus, such as a clock stretch followed by a restart of the transfer. This first issue occurred with both DMA and PIO write transfers. Also when a NACK error was detected during a PIO write transfer, the interrupt handler used to wrongly start a new transfer by writing into the Transmit Holding Register (THR). Then the I2C slave was likely to reply with a second NACK. This second issue is fixed in atmel_twi_interrupt() by handling the TXRDY status bit only if both the TXCOMP and NACK status bits are cleared. Tested with a at24 eeprom on sama5d36ek board running a linux-4.1-at91 kernel image. Adapted to linux-next. Signed-off-by: Cyrille Pitchen Fixes: 93563a6a71bb ("i2c: at91: fix a race condition when using the DMA controller") Reported-by: Peter Rosin Signed-off-by: Ludovic Desroches Tested-by: Peter Rosin Cc: stable@vger.kernel.org #4.1 --- drivers/i2c/busses/i2c-at91.c | 58 +++++++++++++++++++++++++++++++++++++------ 1 file changed, 50 insertions(+), 8 deletions(-) diff --git a/drivers/i2c/busses/i2c-at91.c b/drivers/i2c/busses/i2c-at91.c index 1c758cd..94c087b 100644 --- a/drivers/i2c/busses/i2c-at91.c +++ b/drivers/i2c/busses/i2c-at91.c @@ -465,19 +465,57 @@ static irqreturn_t atmel_twi_interrupt(int irq, void *dev_id) if (!irqstatus) return IRQ_NONE; - else if (irqstatus & AT91_TWI_RXRDY) - at91_twi_read_next_byte(dev); - else if (irqstatus & AT91_TWI_TXRDY) - at91_twi_write_next_byte(dev); - - /* catch error flags */ - dev->transfer_status |= status; + /* + * When a NACK condition is detected, the I2C controller sets the NACK, + * TXCOMP and TXRDY bits all together in the Status Register (SR). + * + * 1 - Handling NACK errors with CPU write transfer. + * + * In such case, we should not write the next byte into the Transmit + * Holding Register (THR) otherwise the I2C controller would start a new + * transfer and the I2C slave is likely to reply by another NACK. + * + * 2 - Handling NACK errors with DMA write transfer. + * + * By setting the TXRDY bit in the SR, the I2C controller also triggers + * the DMA controller to write the next data into the THR. Then the + * result depends on the hardware version of the I2C controller. + * + * 2a - Without support of the Alternative Command mode. + * + * This is the worst case: the DMA controller is triggered to write the + * next data into the THR, hence starting a new transfer: the I2C slave + * is likely to reply by another NACK. + * Concurrently, this interrupt handler is likely to be called to manage + * the first NACK before the I2C controller detects the second NACK and + * sets once again the NACK bit into the SR. + * When handling the first NACK, this interrupt handler disables the I2C + * controller interruptions, especially the NACK interrupt. + * Hence, the NACK bit is pending into the SR. This is why we should + * read the SR to clear all pending interrupts at the beginning of + * at91_do_twi_transfer() before actually starting a new transfer. + * + * 2b - With support of the Alternative Command mode. + * + * When a NACK condition is detected, the I2C controller also locks the + * THR (and sets the LOCK bit in the SR): even though the DMA controller + * is triggered by the TXRDY bit to write the next data into the THR, + * this data actually won't go on the I2C bus hence a second NACK is not + * generated. + */ if (irqstatus & (AT91_TWI_TXCOMP | AT91_TWI_NACK)) { at91_disable_twi_interrupts(dev); complete(&dev->cmd_complete); + } else if (irqstatus & AT91_TWI_RXRDY) { + at91_twi_read_next_byte(dev); + } else if (irqstatus & AT91_TWI_TXRDY) { + at91_twi_write_next_byte(dev); } + /* catch error flags */ + dev->transfer_status |= status; + return IRQ_HANDLED; } @@ -487,6 +525,7 @@ static int at91_do_twi_transfer(struct at91_twi_dev *dev) unsigned long time_left; bool has_unre_flag = dev->pdata->has_unre_flag; bool has_alt_cmd = dev->pdata->has_alt_cmd; + unsigned sr; /* * WARNING: the TXCOMP bit in the Status Register is NOT a clear on @@ -537,6 +576,9 @@ static int at91_do_twi_transfer(struct at91_twi_dev *dev) reinit_completion(&dev->cmd_complete); dev->transfer_status = 0; + /* Clear pending interrupts, such as NACK. */ + sr = at91_twi_read(dev, AT91_TWI_SR); + if (dev->fifo_size) { unsigned fifo_mr = at91_twi_read(dev, AT91_TWI_FMR); @@ -558,7 +600,7 @@ static int at91_do_twi_transfer(struct at91_twi_dev *dev) } else if (dev->msg->flags & I2C_M_RD) { unsigned start_flags = AT91_TWI_START; - if (at91_twi_read(dev, AT91_TWI_SR) & AT91_TWI_RXRDY) { + if (sr & AT91_TWI_RXRDY) { dev_err(dev->dev, "RXRDY still set!"); at91_twi_read(dev, AT91_TWI_RHR); }