| From bed4ff1ed4d8f2ef5007c5c6ae1b29c5677a3632 Mon Sep 17 00:00:00 2001 |
| From: Esben Haabendal <eha@deif.com> |
| Date: Thu, 16 Aug 2018 10:43:12 +0200 |
| Subject: i2c: imx: Fix race condition in dma read |
| MIME-Version: 1.0 |
| Content-Type: text/plain; charset=UTF-8 |
| Content-Transfer-Encoding: 8bit |
| |
| From: Esben Haabendal <eha@deif.com> |
| |
| commit bed4ff1ed4d8f2ef5007c5c6ae1b29c5677a3632 upstream. |
| |
| This fixes a race condition, where the DMAEN bit ends up being set after |
| I2C slave has transmitted a byte following the dummy read. When that |
| happens, an interrupt is generated instead, and no DMA request is generated |
| to kickstart the DMA read, and a timeout happens after DMA_TIMEOUT (1 sec). |
| |
| Fixed by setting the DMAEN bit before the dummy read. |
| |
| Signed-off-by: Esben Haabendal <eha@deif.com> |
| Acked-by: Uwe Kleine-Kรถnig <u.kleine-koenig@pengutronix.de> |
| Signed-off-by: Wolfram Sang <wsa@the-dreams.de> |
| Cc: stable@kernel.org |
| Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> |
| |
| --- |
| drivers/i2c/busses/i2c-imx.c | 8 ++++---- |
| 1 file changed, 4 insertions(+), 4 deletions(-) |
| |
| --- a/drivers/i2c/busses/i2c-imx.c |
| +++ b/drivers/i2c/busses/i2c-imx.c |
| @@ -665,9 +665,6 @@ static int i2c_imx_dma_read(struct imx_i |
| struct imx_i2c_dma *dma = i2c_imx->dma; |
| struct device *dev = &i2c_imx->adapter.dev; |
| |
| - temp = imx_i2c_read_reg(i2c_imx, IMX_I2C_I2CR); |
| - temp |= I2CR_DMAEN; |
| - imx_i2c_write_reg(temp, i2c_imx, IMX_I2C_I2CR); |
| |
| dma->chan_using = dma->chan_rx; |
| dma->dma_transfer_dir = DMA_DEV_TO_MEM; |
| @@ -780,6 +777,7 @@ static int i2c_imx_read(struct imx_i2c_s |
| int i, result; |
| unsigned int temp; |
| int block_data = msgs->flags & I2C_M_RECV_LEN; |
| + int use_dma = i2c_imx->dma && msgs->len >= DMA_THRESHOLD && !block_data; |
| |
| dev_dbg(&i2c_imx->adapter.dev, |
| "<%s> write slave address: addr=0x%x\n", |
| @@ -806,12 +804,14 @@ static int i2c_imx_read(struct imx_i2c_s |
| */ |
| if ((msgs->len - 1) || block_data) |
| temp &= ~I2CR_TXAK; |
| + if (use_dma) |
| + temp |= I2CR_DMAEN; |
| imx_i2c_write_reg(temp, i2c_imx, IMX_I2C_I2CR); |
| imx_i2c_read_reg(i2c_imx, IMX_I2C_I2DR); /* dummy read */ |
| |
| dev_dbg(&i2c_imx->adapter.dev, "<%s> read data\n", __func__); |
| |
| - if (i2c_imx->dma && msgs->len >= DMA_THRESHOLD && !block_data) |
| + if (use_dma) |
| return i2c_imx_dma_read(i2c_imx, msgs, is_lastmsg); |
| |
| /* read data */ |