Commit b8e0ddd3 authored by Marc Kleine-Budde's avatar Marc Kleine-Budde

can: mcp251xfd: tef: prepare to workaround broken TEF FIFO tail index erratum

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: default avatarMarc Kleine-Budde <mkl@pengutronix.de>
parent 24436be5
...@@ -486,6 +486,8 @@ int mcp251xfd_ring_alloc(struct mcp251xfd_priv *priv) ...@@ -486,6 +486,8 @@ int mcp251xfd_ring_alloc(struct mcp251xfd_priv *priv)
clear_bit(MCP251XFD_FLAGS_FD_MODE, priv->flags); 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; tx_ring->obj_size = tx_obj_size;
rem = priv->rx_obj_num; rem = priv->rx_obj_num;
......
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
// //
// mcp251xfd - Microchip MCP251xFD Family CAN controller driver // 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> // Marc Kleine-Budde <kernel@pengutronix.de>
// //
// Based on: // Based on:
...@@ -16,6 +16,11 @@ ...@@ -16,6 +16,11 @@
#include "mcp251xfd.h" #include "mcp251xfd.h"
static inline bool mcp251xfd_tx_fifo_sta_full(u32 fifo_sta)
{
return !(fifo_sta & MCP251XFD_REG_FIFOSTA_TFNRFNIF);
}
static inline int static inline int
mcp251xfd_tef_tail_get_from_chip(const struct mcp251xfd_priv *priv, mcp251xfd_tef_tail_get_from_chip(const struct mcp251xfd_priv *priv,
u8 *tef_tail) u8 *tef_tail)
...@@ -120,28 +125,44 @@ mcp251xfd_handle_tefif_one(struct mcp251xfd_priv *priv, ...@@ -120,28 +125,44 @@ mcp251xfd_handle_tefif_one(struct mcp251xfd_priv *priv,
return 0; 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; const struct mcp251xfd_tx_ring *tx_ring = priv->tx;
unsigned int new_head; const u8 shift = tx_ring->obj_num_shift_to_u8;
u8 chip_tx_tail; u8 chip_tx_tail, tail, len;
u32 fifo_sta;
int err; 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;
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) if (err)
return err; return err;
tail = mcp251xfd_get_tef_tail(priv);
/* chip_tx_tail, is the next TX-Object send by the HW. /* First shift to full u8. The subtraction works on signed
* The new TEF head must be >= the old head, ... * 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; BUILD_BUG_ON(sizeof(tx_ring->obj_num) != sizeof(chip_tx_tail));
if (new_head <= priv->tef->head) BUILD_BUG_ON(sizeof(tx_ring->obj_num) != sizeof(tail));
new_head += tx_ring->obj_num; BUILD_BUG_ON(sizeof(tx_ring->obj_num) != sizeof(len));
/* ... but it cannot exceed the TX head. */ len = (chip_tx_tail << shift) - (tail << shift);
priv->tef->head = min(new_head, tx_ring->head); *len_p = len >> shift;
return mcp251xfd_check_tef_tail(priv); return 0;
} }
static inline int static inline int
...@@ -182,13 +203,12 @@ int mcp251xfd_handle_tefif(struct mcp251xfd_priv *priv) ...@@ -182,13 +203,12 @@ int mcp251xfd_handle_tefif(struct mcp251xfd_priv *priv)
u8 tef_tail, len, l; u8 tef_tail, len, l;
int err, i; int err, i;
err = mcp251xfd_tef_ring_update(priv); err = mcp251xfd_get_tef_len(priv, &len);
if (err) if (err)
return err; return err;
tef_tail = mcp251xfd_get_tef_tail(priv); tef_tail = mcp251xfd_get_tef_tail(priv);
len = mcp251xfd_get_tef_len(priv); l = mcp251xfd_get_tef_linear_len(priv, len);
l = mcp251xfd_get_tef_linear_len(priv);
err = mcp251xfd_tef_obj_read(priv, hw_tef_obj, tef_tail, l); err = mcp251xfd_tef_obj_read(priv, hw_tef_obj, tef_tail, l);
if (err) if (err)
return err; return err;
...@@ -223,6 +243,8 @@ int mcp251xfd_handle_tefif(struct mcp251xfd_priv *priv) ...@@ -223,6 +243,8 @@ int mcp251xfd_handle_tefif(struct mcp251xfd_priv *priv)
struct mcp251xfd_tx_ring *tx_ring = priv->tx; struct mcp251xfd_tx_ring *tx_ring = priv->tx;
int offset; int offset;
ring->head += len;
/* Increment the TEF FIFO tail pointer 'len' times in /* Increment the TEF FIFO tail pointer 'len' times in
* a single SPI message. * a single SPI message.
* *
......
...@@ -524,6 +524,7 @@ struct mcp251xfd_tef_ring { ...@@ -524,6 +524,7 @@ struct mcp251xfd_tef_ring {
/* u8 obj_num equals tx_ring->obj_num */ /* u8 obj_num equals tx_ring->obj_num */
/* u8 obj_size equals sizeof(struct mcp251xfd_hw_tef_obj) */ /* 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; union mcp251xfd_write_reg_buf irq_enable_buf;
struct spi_transfer irq_enable_xfer; struct spi_transfer irq_enable_xfer;
...@@ -542,6 +543,7 @@ struct mcp251xfd_tx_ring { ...@@ -542,6 +543,7 @@ struct mcp251xfd_tx_ring {
u8 nr; u8 nr;
u8 fifo_nr; u8 fifo_nr;
u8 obj_num; u8 obj_num;
u8 obj_num_shift_to_u8;
u8 obj_size; u8 obj_size;
struct mcp251xfd_tx_obj obj[MCP251XFD_TX_OBJ_NUM_MAX]; 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) ...@@ -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); return priv->tef->tail & (priv->tx->obj_num - 1);
} }
static inline u8 mcp251xfd_get_tef_len(const struct mcp251xfd_priv *priv) static inline u8 mcp251xfd_get_tef_linear_len(const struct mcp251xfd_priv *priv, u8 len)
{
return priv->tef->head - priv->tef->tail;
}
static inline u8 mcp251xfd_get_tef_linear_len(const struct mcp251xfd_priv *priv)
{ {
u8 len;
len = mcp251xfd_get_tef_len(priv);
return min_t(u8, len, priv->tx->obj_num - mcp251xfd_get_tef_tail(priv)); return min_t(u8, len, priv->tx->obj_num - mcp251xfd_get_tef_tail(priv));
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment