[PATCH 1/2] i2c: xiic: Wait for TX empty to avoid missed TX NAKs

Robert Hancock robert.hancock at calian.com
Mon Nov 20 15:36:52 PST 2023


On Fri, 2023-11-17 at 18:14 +0000, Robert Hancock wrote:
> Frequently an I2C write will be followed by a read, such as a
> register
> address write followed by a read of the register value. In this
> driver,
> when the TX FIFO half empty interrupt was raised and it was
> determined
> that there was enough space in the TX FIFO to send the following read
> command, it would do so without waiting for the TX FIFO to actually
> empty.
> 
> Unfortunately it appears that in some cases this can result in a NAK
> that was raised by the target device on the write, such as due to an
> unsupported register address, being ignored and the subsequent read
> being done anyway. This can potentially put the I2C bus into an
> invalid state and/or result in invalid read data being processed.
> 
> To avoid this, once a message has been fully written to the TX FIFO,
> wait for the TX FIFO empty interrupt before moving on to the next
> message, to ensure NAKs are handled properly.
> 
> Fixes: e1d5b6598cdc ("i2c: Add support for Xilinx XPS IIC Bus
> Interface")
> Signed-off-by: Robert Hancock <robert.hancock at calian.com>
> ---
>  drivers/i2c/busses/i2c-xiic.c | 15 +++++++--------
>  1 file changed, 7 insertions(+), 8 deletions(-)
> 
> diff --git a/drivers/i2c/busses/i2c-xiic.c b/drivers/i2c/busses/i2c-
> xiic.c
> index 71391b590ada..6e84b4d67fd9 100644
> --- a/drivers/i2c/busses/i2c-xiic.c
> +++ b/drivers/i2c/busses/i2c-xiic.c
> @@ -774,12 +774,15 @@ static irqreturn_t xiic_process(int irq, void
> *dev_id)
>  
>                 xiic_fill_tx_fifo(i2c);
>  
> -               /* current message sent and there is space in the
> fifo */
> -               if (!xiic_tx_space(i2c) && xiic_tx_fifo_space(i2c) >=
> 2) {
> +               /* current message fully written */
> +               if (!xiic_tx_space(i2c)) {
> 

Looks like there may be another small bug here - we should only proceed
with the next message if the FIFO was empty when the interrupt was
raised, not if we just finished writing it in the previous
xiic_fill_tx_fifo call. Another version coming.

>                         dev_dbg(i2c->adap.dev.parent,
>                                 "%s end of message sent, nmsgs:
> %d\n",
>                                 __func__, i2c->nmsgs);
> -                       if (i2c->nmsgs > 1) {
> +                       /* Don't move onto the next message until the
> TX FIFO empties,
> +                        * to ensure that a NAK is not missed.
> +                        */
> +                       if (i2c->nmsgs > 1 && (pend &
> XIIC_INTR_TX_EMPTY_MASK)) {
>                                 i2c->nmsgs--;
>                                 i2c->tx_msg++;
>                                 xfer_more = 1;
> @@ -790,11 +793,7 @@ static irqreturn_t xiic_process(int irq, void
> *dev_id)
>                                         "%s Got TX IRQ but no more to
> do...\n",
>                                         __func__);
>                         }
> -               } else if (!xiic_tx_space(i2c) && (i2c->nmsgs == 1))
> -                       /* current frame is sent and is last,
> -                        * make sure to disable tx half
> -                        */
> -                       xiic_irq_dis(i2c, XIIC_INTR_TX_HALF_MASK);
> +               }
>         }
>  
>         if (pend & XIIC_INTR_BNB_MASK) {

-- 
Robert Hancock <robert.hancock at calian.com>


More information about the linux-arm-kernel mailing list