From patchwork Thu Oct 24 20:12:58 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Kamel BOUHARA X-Patchwork-Id: 1183551 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Authentication-Results: ozlabs.org; spf=none (no SPF record) smtp.mailfrom=vger.kernel.org (client-ip=209.132.180.67; helo=vger.kernel.org; envelope-from=linux-i2c-owner@vger.kernel.org; receiver=) Authentication-Results: ozlabs.org; dmarc=none (p=none dis=none) header.from=bootlin.com Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by ozlabs.org (Postfix) with ESMTP id 46zdjn18Zcz9sQq for ; Fri, 25 Oct 2019 07:13:49 +1100 (AEDT) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1726655AbfJXUNc (ORCPT ); Thu, 24 Oct 2019 16:13:32 -0400 Received: from relay10.mail.gandi.net ([217.70.178.230]:43405 "EHLO relay10.mail.gandi.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1725811AbfJXUNc (ORCPT ); Thu, 24 Oct 2019 16:13:32 -0400 Received: from localhost (unknown [78.193.40.249]) (Authenticated sender: kamel.bouhara@bootlin.com) by relay10.mail.gandi.net (Postfix) with ESMTPSA id 7EF92240004; Thu, 24 Oct 2019 20:13:26 +0000 (UTC) From: Kamel Bouhara To: Wolfram Sang , linux-i2c@vger.kernel.org, linux-kernel@vger.kernel.org Cc: Nicolas Ferre , Alexandre Belloni , Ludovic Desroches , linux-arm-kernel@lists.infradead.org, Thomas Petazzoni , Rob Herring , devicetree@vger.kernel.org, Codrin Ciubotariu Subject: [PATCH v2 1/5] i2c: at91: Send bus clear command if SCL or SDA is down Date: Thu, 24 Oct 2019 22:12:58 +0200 Message-Id: <20191024201302.23376-2-kamel.bouhara@bootlin.com> X-Mailer: git-send-email 2.23.0 In-Reply-To: <20191024201302.23376-1-kamel.bouhara@bootlin.com> References: <20191024201302.23376-1-kamel.bouhara@bootlin.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: Codrin Ciubotariu After a transfer timeout, some faulty I2C slave devices might hold down the SCL or the SDA pins. We can generate a bus clear command, hoping that the slave might release the pins. Signed-off-by: Codrin Ciubotariu Acked-by: Ludovic Desroches --- drivers/i2c/busses/i2c-at91-core.c | 8 ++++++++ drivers/i2c/busses/i2c-at91-master.c | 22 ++++++++++++++++++++++ drivers/i2c/busses/i2c-at91.h | 7 ++++++- 3 files changed, 36 insertions(+), 1 deletion(-) diff --git a/drivers/i2c/busses/i2c-at91-core.c b/drivers/i2c/busses/i2c-at91-core.c index 435c7d7377a3..cb07489e698f 100644 --- a/drivers/i2c/busses/i2c-at91-core.c +++ b/drivers/i2c/busses/i2c-at91-core.c @@ -68,6 +68,7 @@ static struct at91_twi_pdata at91rm9200_config = { .has_unre_flag = true, .has_alt_cmd = false, .has_hold_field = false, + .has_clear_cmd = false, }; static struct at91_twi_pdata at91sam9261_config = { @@ -76,6 +77,7 @@ static struct at91_twi_pdata at91sam9261_config = { .has_unre_flag = false, .has_alt_cmd = false, .has_hold_field = false, + .has_clear_cmd = false, }; static struct at91_twi_pdata at91sam9260_config = { @@ -84,6 +86,7 @@ static struct at91_twi_pdata at91sam9260_config = { .has_unre_flag = false, .has_alt_cmd = false, .has_hold_field = false, + .has_clear_cmd = false, }; static struct at91_twi_pdata at91sam9g20_config = { @@ -92,6 +95,7 @@ static struct at91_twi_pdata at91sam9g20_config = { .has_unre_flag = false, .has_alt_cmd = false, .has_hold_field = false, + .has_clear_cmd = false, }; static struct at91_twi_pdata at91sam9g10_config = { @@ -100,6 +104,7 @@ static struct at91_twi_pdata at91sam9g10_config = { .has_unre_flag = false, .has_alt_cmd = false, .has_hold_field = false, + .has_clear_cmd = false, }; static const struct platform_device_id at91_twi_devtypes[] = { @@ -130,6 +135,7 @@ static struct at91_twi_pdata at91sam9x5_config = { .has_unre_flag = false, .has_alt_cmd = false, .has_hold_field = false, + .has_clear_cmd = false, }; static struct at91_twi_pdata sama5d4_config = { @@ -138,6 +144,7 @@ static struct at91_twi_pdata sama5d4_config = { .has_unre_flag = false, .has_alt_cmd = false, .has_hold_field = true, + .has_clear_cmd = false, }; static struct at91_twi_pdata sama5d2_config = { @@ -146,6 +153,7 @@ static struct at91_twi_pdata sama5d2_config = { .has_unre_flag = true, .has_alt_cmd = true, .has_hold_field = true, + .has_clear_cmd = true, }; static const struct of_device_id atmel_twi_dt_ids[] = { diff --git a/drivers/i2c/busses/i2c-at91-master.c b/drivers/i2c/busses/i2c-at91-master.c index a3fcc35ffd3b..12d4fa946a82 100644 --- a/drivers/i2c/busses/i2c-at91-master.c +++ b/drivers/i2c/busses/i2c-at91-master.c @@ -440,6 +440,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; + bool has_clear_cmd = dev->pdata->has_clear_cmd; /* * WARNING: the TXCOMP bit in the Status Register is NOT a clear on @@ -599,6 +600,27 @@ static int at91_do_twi_transfer(struct at91_twi_dev *dev) at91_twi_write(dev, AT91_TWI_CR, AT91_TWI_THRCLR | AT91_TWI_LOCKCLR); } + + /* + * After timeout, some faulty I2C slave devices might hold SCL/SDA down; + * we can send a bus clear command, hoping that the pins will be + * released + */ + if (has_clear_cmd && + (!(dev->transfer_status & AT91_TWI_SDA) || + !(dev->transfer_status & AT91_TWI_SCL))) { + dev_dbg(dev->dev, + "SDA/SCL are down; sending bus clear command\n"); + if (dev->use_alt_cmd) { + unsigned int acr; + + acr = at91_twi_read(dev, AT91_TWI_ACR); + acr &= ~AT91_TWI_ACR_DATAL_MASK; + at91_twi_write(dev, AT91_TWI_ACR, acr); + } + at91_twi_write(dev, AT91_TWI_CR, AT91_TWI_CLEAR); + } + return ret; } diff --git a/drivers/i2c/busses/i2c-at91.h b/drivers/i2c/busses/i2c-at91.h index 499b506f6128..0827c28a84db 100644 --- a/drivers/i2c/busses/i2c-at91.h +++ b/drivers/i2c/busses/i2c-at91.h @@ -36,6 +36,7 @@ #define AT91_TWI_SVDIS BIT(5) /* Slave Transfer Disable */ #define AT91_TWI_QUICK BIT(6) /* SMBus quick command */ #define AT91_TWI_SWRST BIT(7) /* Software Reset */ +#define AT91_TWI_CLEAR BIT(15) /* Bus clear command */ #define AT91_TWI_ACMEN BIT(16) /* Alternative Command Mode Enable */ #define AT91_TWI_ACMDIS BIT(17) /* Alternative Command Mode Disable */ #define AT91_TWI_THRCLR BIT(24) /* Transmit Holding Register Clear */ @@ -69,6 +70,8 @@ #define AT91_TWI_NACK BIT(8) /* Not Acknowledged */ #define AT91_TWI_EOSACC BIT(11) /* End Of Slave Access */ #define AT91_TWI_LOCK BIT(23) /* TWI Lock due to Frame Errors */ +#define AT91_TWI_SCL BIT(24) /* TWI SCL status */ +#define AT91_TWI_SDA BIT(25) /* TWI SDA status */ #define AT91_TWI_INT_MASK \ (AT91_TWI_TXCOMP | AT91_TWI_RXRDY | AT91_TWI_TXRDY | AT91_TWI_NACK \ @@ -81,7 +84,8 @@ #define AT91_TWI_THR 0x0034 /* Transmit Holding Register */ #define AT91_TWI_ACR 0x0040 /* Alternative Command Register */ -#define AT91_TWI_ACR_DATAL(len) ((len) & 0xff) +#define AT91_TWI_ACR_DATAL_MASK GENMASK(15, 0) +#define AT91_TWI_ACR_DATAL(len) ((len) & AT91_TWI_ACR_DATAL_MASK) #define AT91_TWI_ACR_DIR BIT(8) #define AT91_TWI_FMR 0x0050 /* FIFO Mode Register */ @@ -108,6 +112,7 @@ struct at91_twi_pdata { bool has_unre_flag; bool has_alt_cmd; bool has_hold_field; + bool has_clear_cmd; struct at_dma_slave dma_slave; };