• Hiromitsu Yamasaki's avatar
    i2c: rcar: fix concurrency issue related to ICDMAER · a35ba2f7
    Hiromitsu Yamasaki authored
    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: 73e8b052 ("i2c: rcar: add DMA support")
    Signed-off-by: default avatarHiromitsu Yamasaki <hiromitsu.yamasaki.ym@renesas.com>
    Signed-off-by: default avatarWolfram Sang <wsa+renesas@sang-engineering.com>
    [wsa: updated test case to be more reliable, added note to comment]
    Reviewed-by: default avatarSimon Horman <horms+renesas@verge.net.au>
    Signed-off-by: default avatarWolfram Sang <wsa@the-dreams.de>
    a35ba2f7
i2c-rcar.c 26.4 KB