diff options
author | Pierre-Yves MORDRET <pierre-yves.mordret@st.com> | 2018-04-11 15:24:58 +0200 |
---|---|---|
committer | Wolfram Sang <wsa@the-dreams.de> | 2018-04-30 10:39:28 +0200 |
commit | 562de4ff4cd2beb1bd3a1581e22623f05b114c97 (patch) | |
tree | 2e3905b504a388f5347dfb7647c2d1bfccb5a61f /drivers/i2c/busses/i2c-stm32f7.c | |
parent | i2c: i2c-stm32f7: Add DMA support (diff) | |
download | linux-562de4ff4cd2beb1bd3a1581e22623f05b114c97.tar.xz linux-562de4ff4cd2beb1bd3a1581e22623f05b114c97.zip |
i2c: i2c-stm32f7: Implement I2C release mechanism
Feature prevents I2C lock-ups. Mechanism resets I2C state machine
and releases SCL/SDA signals but preserves I2C registers.
Signed-off-by: Pierre-Yves MORDRET <pierre-yves.mordret@st.com>
Signed-off-by: Wolfram Sang <wsa@the-dreams.de>
Diffstat (limited to 'drivers/i2c/busses/i2c-stm32f7.c')
-rw-r--r-- | drivers/i2c/busses/i2c-stm32f7.c | 27 |
1 files changed, 24 insertions, 3 deletions
diff --git a/drivers/i2c/busses/i2c-stm32f7.c b/drivers/i2c/busses/i2c-stm32f7.c index 63ea71c5762f..0f87449fc6bd 100644 --- a/drivers/i2c/busses/i2c-stm32f7.c +++ b/drivers/i2c/busses/i2c-stm32f7.c @@ -718,6 +718,20 @@ static void stm32f7_i2c_smbus_reload(struct stm32f7_i2c_dev *i2c_dev) writel_relaxed(cr2, i2c_dev->base + STM32F7_I2C_CR2); } +static int stm32f7_i2c_release_bus(struct i2c_adapter *i2c_adap) +{ + struct stm32f7_i2c_dev *i2c_dev = i2c_get_adapdata(i2c_adap); + + dev_info(i2c_dev->dev, "Trying to recover bus\n"); + + stm32f7_i2c_clr_bits(i2c_dev->base + STM32F7_I2C_CR1, + STM32F7_I2C_CR1_PE); + + stm32f7_i2c_hw_config(i2c_dev); + + return 0; +} + static int stm32f7_i2c_wait_free_bus(struct stm32f7_i2c_dev *i2c_dev) { u32 status; @@ -727,12 +741,18 @@ static int stm32f7_i2c_wait_free_bus(struct stm32f7_i2c_dev *i2c_dev) status, !(status & STM32F7_I2C_ISR_BUSY), 10, 1000); + if (!ret) + return 0; + + dev_info(i2c_dev->dev, "bus busy\n"); + + ret = stm32f7_i2c_release_bus(&i2c_dev->adap); if (ret) { - dev_dbg(i2c_dev->dev, "bus busy\n"); - ret = -EBUSY; + dev_err(i2c_dev->dev, "Failed to recover the bus (%d)\n", ret); + return ret; } - return ret; + return -EBUSY; } static void stm32f7_i2c_xfer_msg(struct stm32f7_i2c_dev *i2c_dev, @@ -1474,6 +1494,7 @@ static irqreturn_t stm32f7_i2c_isr_error(int irq, void *data) if (status & STM32F7_I2C_ISR_BERR) { dev_err(dev, "<%s>: Bus error\n", __func__); writel_relaxed(STM32F7_I2C_ICR_BERRCF, base + STM32F7_I2C_ICR); + stm32f7_i2c_release_bus(&i2c_dev->adap); f7_msg->result = -EIO; } |