Skip to content

Commit a35ba2f

Browse files
Hiromitsu YamasakiWolfram Sang
authored andcommitted
i2c: rcar: fix concurrency issue related to ICDMAER
This patch fixes the problem that an interrupt may set up a new I2C message and the DMA callback overwrites this setup. By disabling the DMA Enable Register(ICDMAER), rcar_i2c_dma_unmap() enables interrupts for register settings (such as Master Control Register(ICMCR)) and advances the I2C transfer sequence. If an interrupt occurs immediately after ICDMAER is disabled, the callback handler later continues and overwrites the previous settings from the interrupt. So, disable ICDMAER at the end of the callback to ensure other interrupts are masked until then. Note that this driver needs to work lock-free because there are IP cores with a HW race condition which prevent us from using a spinlock in the interrupt handler. Reproduction test: 1. Add a delay after disabling ICDMAER. (It is expected to generate an interrupt of rcar_i2c_irq()) void rcar_i2c_dma_unmap(struct rcar_i2c_priv *priv) { ... rcar_i2c_write(priv, ICDMAER, 0); usleep_range(500, 800) ... priv->dma_direction = DMA_NONE; } 2. Execute DMA transfers $ i2ctransfer -y 4 w9@0x6a 1 1+ r16 3. A log message of BUG_ON() will be displayed. Fixes: 73e8b05 ("i2c: rcar: add DMA support") Signed-off-by: Hiromitsu Yamasaki <hiromitsu.yamasaki.ym@renesas.com> Signed-off-by: Wolfram Sang <wsa+renesas@sang-engineering.com> [wsa: updated test case to be more reliable, added note to comment] Reviewed-by: Simon Horman <horms+renesas@verge.net.au> Signed-off-by: Wolfram Sang <wsa@the-dreams.de>
1 parent 60f7691 commit a35ba2f

File tree

1 file changed

+3
-3
lines changed

1 file changed

+3
-3
lines changed

drivers/i2c/busses/i2c-rcar.c

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -363,9 +363,6 @@ static void rcar_i2c_dma_unmap(struct rcar_i2c_priv *priv)
363363
struct dma_chan *chan = priv->dma_direction == DMA_FROM_DEVICE
364364
? priv->dma_rx : priv->dma_tx;
365365

366-
/* Disable DMA Master Received/Transmitted */
367-
rcar_i2c_write(priv, ICDMAER, 0);
368-
369366
dma_unmap_single(chan->device->dev, sg_dma_address(&priv->sg),
370367
sg_dma_len(&priv->sg), priv->dma_direction);
371368

@@ -375,6 +372,9 @@ static void rcar_i2c_dma_unmap(struct rcar_i2c_priv *priv)
375372
priv->flags |= ID_P_NO_RXDMA;
376373

377374
priv->dma_direction = DMA_NONE;
375+
376+
/* Disable DMA Master Received/Transmitted, must be last! */
377+
rcar_i2c_write(priv, ICDMAER, 0);
378378
}
379379

380380
static void rcar_i2c_cleanup_dma(struct rcar_i2c_priv *priv)

0 commit comments

Comments
 (0)