Patch "i2c: designware: fix __i2c_dw_disable() in case master is holding SCL low" has been added to the 6.5-stable tree

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



This is a note to let you know that I've just added the patch titled

    i2c: designware: fix __i2c_dw_disable() in case master is holding SCL low

to the 6.5-stable tree which can be found at:
    http://www.kernel.org/git/?p=linux/kernel/git/stable/stable-queue.git;a=summary

The filename of the patch is:
     i2c-designware-fix-__i2c_dw_disable-in-case-master-i.patch
and it can be found in the queue-6.5 subdirectory.

If you, or anyone else, feels it should not be added to the stable tree,
please let <stable@xxxxxxxxxxxxxxx> know about it.



commit 484cb1cb71191e7a383bc47f555a6d34278d68b9
Author: Yann Sionneau <ysionneau@xxxxxxxxx>
Date:   Mon Sep 11 16:07:49 2023 +0200

    i2c: designware: fix __i2c_dw_disable() in case master is holding SCL low
    
    [ Upstream commit 2409205acd3c7c877f3d0080cac6a5feb3358f83 ]
    
    The DesignWare IP can be synthesized with the IC_EMPTYFIFO_HOLD_MASTER_EN
    parameter.
    In this case, when the TX FIFO gets empty and the last command didn't have
    the STOP bit (IC_DATA_CMD[9]), the controller will hold SCL low until
    a new command is pushed into the TX FIFO or the transfer is aborted.
    
    When the controller is holding SCL low, it cannot be disabled.
    The transfer must first be aborted.
    Also, the bus recovery won't work because SCL is held low by the master.
    
    Check if the master is holding SCL low in __i2c_dw_disable() before trying
    to disable the controller. If SCL is held low, an abort is initiated.
    When the abort is done, then proceed with disabling the controller.
    
    This whole situation can happen for instance during SMBus read data block
    if the slave just responds with "byte count == 0".
    This puts the driver in an unrecoverable state, because the controller is
    holding SCL low and the current __i2c_dw_disable() procedure is not
    working. In this situation only a SoC reset can fix the i2c bus.
    
    Co-developed-by: Jonathan Borne <jborne@xxxxxxxxx>
    Signed-off-by: Jonathan Borne <jborne@xxxxxxxxx>
    Signed-off-by: Yann Sionneau <ysionneau@xxxxxxxxx>
    Acked-by: Jarkko Nikula <jarkko.nikula@xxxxxxxxxxxxxxx>
    Signed-off-by: Wolfram Sang <wsa@xxxxxxxxxx>
    Signed-off-by: Sasha Levin <sashal@xxxxxxxxxx>

diff --git a/drivers/i2c/busses/i2c-designware-common.c b/drivers/i2c/busses/i2c-designware-common.c
index cdd8c67d91298..affcfb243f0f5 100644
--- a/drivers/i2c/busses/i2c-designware-common.c
+++ b/drivers/i2c/busses/i2c-designware-common.c
@@ -441,8 +441,25 @@ int i2c_dw_set_sda_hold(struct dw_i2c_dev *dev)
 
 void __i2c_dw_disable(struct dw_i2c_dev *dev)
 {
+	unsigned int raw_intr_stats;
+	unsigned int enable;
 	int timeout = 100;
+	bool abort_needed;
 	unsigned int status;
+	int ret;
+
+	regmap_read(dev->map, DW_IC_RAW_INTR_STAT, &raw_intr_stats);
+	regmap_read(dev->map, DW_IC_ENABLE, &enable);
+
+	abort_needed = raw_intr_stats & DW_IC_INTR_MST_ON_HOLD;
+	if (abort_needed) {
+		regmap_write(dev->map, DW_IC_ENABLE, enable | DW_IC_ENABLE_ABORT);
+		ret = regmap_read_poll_timeout(dev->map, DW_IC_ENABLE, enable,
+					       !(enable & DW_IC_ENABLE_ABORT), 10,
+					       100);
+		if (ret)
+			dev_err(dev->dev, "timeout while trying to abort current transfer\n");
+	}
 
 	do {
 		__i2c_dw_disable_nowait(dev);
diff --git a/drivers/i2c/busses/i2c-designware-core.h b/drivers/i2c/busses/i2c-designware-core.h
index cf4f684f53566..a7f6f3eafad7d 100644
--- a/drivers/i2c/busses/i2c-designware-core.h
+++ b/drivers/i2c/busses/i2c-designware-core.h
@@ -98,6 +98,7 @@
 #define DW_IC_INTR_START_DET			BIT(10)
 #define DW_IC_INTR_GEN_CALL			BIT(11)
 #define DW_IC_INTR_RESTART_DET			BIT(12)
+#define DW_IC_INTR_MST_ON_HOLD			BIT(13)
 
 #define DW_IC_INTR_DEFAULT_MASK			(DW_IC_INTR_RX_FULL | \
 						 DW_IC_INTR_TX_ABRT | \
@@ -108,6 +109,8 @@
 						 DW_IC_INTR_RX_UNDER | \
 						 DW_IC_INTR_RD_REQ)
 
+#define DW_IC_ENABLE_ABORT			BIT(1)
+
 #define DW_IC_STATUS_ACTIVITY			BIT(0)
 #define DW_IC_STATUS_TFE			BIT(2)
 #define DW_IC_STATUS_RFNE			BIT(3)



[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[Index of Archives]     [Linux USB Devel]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]

  Powered by Linux