]> www.infradead.org Git - users/dwmw2/linux.git/commitdiff
can: mcp251xfd: tef: prepare to workaround broken TEF FIFO tail index erratum
authorMarc Kleine-Budde <mkl@pengutronix.de>
Sun, 22 Jan 2023 20:30:41 +0000 (21:30 +0100)
committerMarc Kleine-Budde <mkl@pengutronix.de>
Fri, 28 Jun 2024 21:48:12 +0000 (23:48 +0200)
This is a preparatory patch to work around a problem similar to
erratum DS80000789E 6 of the mcp2518fd, the other variants of the chip
family (mcp2517fd and mcp251863) are probably also affected.

Erratum DS80000789E 6 says "reading of the FIFOCI bits in the FIFOSTA
register for an RX FIFO may be corrupted". However observation shows
that this problem is not limited to RX FIFOs but also effects the TEF
FIFO.

When handling the TEF interrupt, the driver reads the FIFO header
index from the TEF FIFO STA register of the chip.

In the bad case, the driver reads a too large head index. In the
original code, the driver always trusted the read value, which caused
old CAN transmit complete events that were already processed to be
re-processed.

Instead of reading and trusting the head index, read the head index
and calculate the number of CAN frames that were supposedly received -
replace mcp251xfd_tef_ring_update() with mcp251xfd_get_tef_len().

The mcp251xfd_handle_tefif() function reads the CAN transmit complete
events from the chip, iterates over them and pushes them into the
network stack. The original driver already contains code to detect old
CAN transmit complete events, that will be updated in the next patch.

Cc: Stefan Althöfer <Stefan.Althoefer@janztec.com>
Cc: Thomas Kopp <thomas.kopp@microchip.com>
Signed-off-by: Marc Kleine-Budde <mkl@pengutronix.de>
drivers/net/can/spi/mcp251xfd/mcp251xfd-ring.c
drivers/net/can/spi/mcp251xfd/mcp251xfd-tef.c
drivers/net/can/spi/mcp251xfd/mcp251xfd.h

index 8464fc4f37d099b8c99b15e5b22f74571eb69560..7bd2bcb5cf876e6907d2b70d05be355ea5f55dfd 100644 (file)
@@ -486,6 +486,8 @@ int mcp251xfd_ring_alloc(struct mcp251xfd_priv *priv)
                clear_bit(MCP251XFD_FLAGS_FD_MODE, priv->flags);
        }
 
+       tx_ring->obj_num_shift_to_u8 = BITS_PER_TYPE(tx_ring->obj_num) -
+               ilog2(tx_ring->obj_num);
        tx_ring->obj_size = tx_obj_size;
 
        rem = priv->rx_obj_num;
index 4bc8d71fc491748b81d2e6a126ff486a4e0e5c95..3d9c348ad8684c72ee7abc459a81ac6fb414f3c9 100644 (file)
@@ -2,7 +2,7 @@
 //
 // mcp251xfd - Microchip MCP251xFD Family CAN controller driver
 //
-// Copyright (c) 2019, 2020, 2021 Pengutronix,
+// Copyright (c) 2019, 2020, 2021, 2023 Pengutronix,
 //               Marc Kleine-Budde <kernel@pengutronix.de>
 //
 // Based on:
 
 #include "mcp251xfd.h"
 
+static inline bool mcp251xfd_tx_fifo_sta_full(u32 fifo_sta)
+{
+       return !(fifo_sta & MCP251XFD_REG_FIFOSTA_TFNRFNIF);
+}
+
 static inline int
 mcp251xfd_tef_tail_get_from_chip(const struct mcp251xfd_priv *priv,
                                 u8 *tef_tail)
@@ -120,28 +125,44 @@ mcp251xfd_handle_tefif_one(struct mcp251xfd_priv *priv,
        return 0;
 }
 
-static int mcp251xfd_tef_ring_update(struct mcp251xfd_priv *priv)
+static int
+mcp251xfd_get_tef_len(struct mcp251xfd_priv *priv, u8 *len_p)
 {
        const struct mcp251xfd_tx_ring *tx_ring = priv->tx;
-       unsigned int new_head;
-       u8 chip_tx_tail;
+       const u8 shift = tx_ring->obj_num_shift_to_u8;
+       u8 chip_tx_tail, tail, len;
+       u32 fifo_sta;
        int err;
 
-       err = mcp251xfd_tx_tail_get_from_chip(priv, &chip_tx_tail);
+       err = regmap_read(priv->map_reg, MCP251XFD_REG_FIFOSTA(priv->tx->fifo_nr),
+                         &fifo_sta);
        if (err)
                return err;
 
-       /* chip_tx_tail, is the next TX-Object send by the HW.
-        * The new TEF head must be >= the old head, ...
+       if (mcp251xfd_tx_fifo_sta_full(fifo_sta)) {
+               *len_p = tx_ring->obj_num;
+               return 0;
+       }
+
+       chip_tx_tail = FIELD_GET(MCP251XFD_REG_FIFOSTA_FIFOCI_MASK, fifo_sta);
+
+       err =  mcp251xfd_check_tef_tail(priv);
+       if (err)
+               return err;
+       tail = mcp251xfd_get_tef_tail(priv);
+
+       /* First shift to full u8. The subtraction works on signed
+        * values, that keeps the difference steady around the u8
+        * overflow. The right shift acts on len, which is an u8.
         */
-       new_head = round_down(priv->tef->head, tx_ring->obj_num) + chip_tx_tail;
-       if (new_head <= priv->tef->head)
-               new_head += tx_ring->obj_num;
+       BUILD_BUG_ON(sizeof(tx_ring->obj_num) != sizeof(chip_tx_tail));
+       BUILD_BUG_ON(sizeof(tx_ring->obj_num) != sizeof(tail));
+       BUILD_BUG_ON(sizeof(tx_ring->obj_num) != sizeof(len));
 
-       /* ... but it cannot exceed the TX head. */
-       priv->tef->head = min(new_head, tx_ring->head);
+       len = (chip_tx_tail << shift) - (tail << shift);
+       *len_p = len >> shift;
 
-       return mcp251xfd_check_tef_tail(priv);
+       return 0;
 }
 
 static inline int
@@ -182,13 +203,12 @@ int mcp251xfd_handle_tefif(struct mcp251xfd_priv *priv)
        u8 tef_tail, len, l;
        int err, i;
 
-       err = mcp251xfd_tef_ring_update(priv);
+       err = mcp251xfd_get_tef_len(priv, &len);
        if (err)
                return err;
 
        tef_tail = mcp251xfd_get_tef_tail(priv);
-       len = mcp251xfd_get_tef_len(priv);
-       l = mcp251xfd_get_tef_linear_len(priv);
+       l = mcp251xfd_get_tef_linear_len(priv, len);
        err = mcp251xfd_tef_obj_read(priv, hw_tef_obj, tef_tail, l);
        if (err)
                return err;
@@ -223,6 +243,8 @@ out_netif_wake_queue:
                struct mcp251xfd_tx_ring *tx_ring = priv->tx;
                int offset;
 
+               ring->head += len;
+
                /* Increment the TEF FIFO tail pointer 'len' times in
                 * a single SPI message.
                 *
index d32ece3d7aee33bb30809338e18b85abaedeed3b..dcbbd2b2fae8273844bd1843209ecd5a2ed3a43c 100644 (file)
@@ -524,6 +524,7 @@ struct mcp251xfd_tef_ring {
 
        /* u8 obj_num equals tx_ring->obj_num */
        /* u8 obj_size equals sizeof(struct mcp251xfd_hw_tef_obj) */
+       /* u8 obj_num_shift_to_u8 equals tx_ring->obj_num_shift_to_u8 */
 
        union mcp251xfd_write_reg_buf irq_enable_buf;
        struct spi_transfer irq_enable_xfer;
@@ -542,6 +543,7 @@ struct mcp251xfd_tx_ring {
        u8 nr;
        u8 fifo_nr;
        u8 obj_num;
+       u8 obj_num_shift_to_u8;
        u8 obj_size;
 
        struct mcp251xfd_tx_obj obj[MCP251XFD_TX_OBJ_NUM_MAX];
@@ -882,17 +884,8 @@ static inline u8 mcp251xfd_get_tef_tail(const struct mcp251xfd_priv *priv)
        return priv->tef->tail & (priv->tx->obj_num - 1);
 }
 
-static inline u8 mcp251xfd_get_tef_len(const struct mcp251xfd_priv *priv)
-{
-       return priv->tef->head - priv->tef->tail;
-}
-
-static inline u8 mcp251xfd_get_tef_linear_len(const struct mcp251xfd_priv *priv)
+static inline u8 mcp251xfd_get_tef_linear_len(const struct mcp251xfd_priv *priv, u8 len)
 {
-       u8 len;
-
-       len = mcp251xfd_get_tef_len(priv);
-
        return min_t(u8, len, priv->tx->obj_num - mcp251xfd_get_tef_tail(priv));
 }