can: rockchip_canfd: rkcanfd_get_berr_counter_corrected(): work around broken {RX...
authorMarc Kleine-Budde <mkl@pengutronix.de>
Sun, 10 Dec 2023 13:43:14 +0000 (14:43 +0100)
committerMarc Kleine-Budde <mkl@pengutronix.de>
Wed, 4 Sep 2024 12:41:53 +0000 (14:41 +0200)
Tests show that sometimes both CAN bus error counters read 0x0, even
if the controller is in warning mode
(RKCANFD_REG_STATE_ERROR_WARNING_STATE in RKCANFD_REG_STATE
set).

To work around this issue, if both error counters read from hardware
are 0x0, use the structure priv->bec, otherwise save the read value in
priv->bec.

In rkcanfd_handle_rx_int_one() decrement the priv->bec.rxerr for
successfully RX'ed CAN frames.

In rkcanfd_handle_tx_done_one() decrement the priv->bec.txerr for
successfully TX'ed CAN frames.

Tested-by: Alibek Omarov <a1ba.omarov@gmail.com>
Acked-by: Heiko Stuebner <heiko@sntech.de>
Link: https://patch.msgid.link/20240904-rockchip-canfd-v5-14-8ae22bcb27cc@pengutronix.de
Signed-off-by: Marc Kleine-Budde <mkl@pengutronix.de>
drivers/net/can/rockchip/rockchip_canfd-core.c
drivers/net/can/rockchip/rockchip_canfd-rx.c
drivers/net/can/rockchip/rockchip_canfd-tx.c
drivers/net/can/rockchip/rockchip_canfd.h

index 700702e..cf17618 100644 (file)
@@ -159,11 +159,47 @@ static int rkcanfd_set_bittiming(struct rkcanfd_priv *priv)
        return 0;
 }
 
-static void rkcanfd_get_berr_counter_raw(struct rkcanfd_priv *priv,
-                                        struct can_berr_counter *bec)
+static void rkcanfd_get_berr_counter_corrected(struct rkcanfd_priv *priv,
+                                              struct can_berr_counter *bec)
 {
+       struct can_berr_counter bec_raw;
+       u32 reg_state;
+
        bec->rxerr = rkcanfd_read(priv, RKCANFD_REG_RXERRORCNT);
        bec->txerr = rkcanfd_read(priv, RKCANFD_REG_TXERRORCNT);
+       bec_raw = *bec;
+
+       /* Tests show that sometimes both CAN bus error counters read
+        * 0x0, even if the controller is in warning mode
+        * (RKCANFD_REG_STATE_ERROR_WARNING_STATE in RKCANFD_REG_STATE
+        * set).
+        *
+        * In case both error counters read 0x0, use the struct
+        * priv->bec, otherwise save the read value to priv->bec.
+        *
+        * rkcanfd_handle_rx_int_one() handles the decrementing of
+        * priv->bec.rxerr for successfully RX'ed CAN frames.
+        *
+        * Luckily the controller doesn't decrement the RX CAN bus
+        * error counter in hardware for self received TX'ed CAN
+        * frames (RKCANFD_REG_MODE_RXSTX_MODE), so RXSTX doesn't
+        * interfere with proper RX CAN bus error counters.
+        *
+        * rkcanfd_handle_tx_done_one() handles the decrementing of
+        * priv->bec.txerr for successfully TX'ed CAN frames.
+        */
+       if (!bec->rxerr && !bec->txerr)
+               *bec = priv->bec;
+       else
+               priv->bec = *bec;
+
+       reg_state = rkcanfd_read(priv, RKCANFD_REG_STATE);
+       netdev_vdbg(priv->ndev,
+                   "%s: Raw/Cor: txerr=%3u/%3u rxerr=%3u/%3u Bus Off=%u Warning=%u\n",
+                   __func__,
+                   bec_raw.txerr, bec->txerr, bec_raw.rxerr, bec->rxerr,
+                   !!(reg_state & RKCANFD_REG_STATE_BUS_OFF_STATE),
+                   !!(reg_state & RKCANFD_REG_STATE_ERROR_WARNING_STATE));
 }
 
 static int rkcanfd_get_berr_counter(const struct net_device *ndev,
@@ -176,7 +212,7 @@ static int rkcanfd_get_berr_counter(const struct net_device *ndev,
        if (err)
                return err;
 
-       rkcanfd_get_berr_counter_raw(priv, bec);
+       rkcanfd_get_berr_counter_corrected(priv, bec);
 
        pm_runtime_put(ndev->dev.parent);
 
@@ -252,6 +288,8 @@ static void rkcanfd_chip_start(struct rkcanfd_priv *priv)
                RKCANFD_REG_INT_OVERLOAD_INT |
                RKCANFD_REG_INT_TX_FINISH_INT;
 
+       memset(&priv->bec, 0x0, sizeof(priv->bec));
+
        rkcanfd_chip_fifo_setup(priv);
        rkcanfd_timestamp_init(priv);
        rkcanfd_set_bittiming(priv);
@@ -488,7 +526,7 @@ static int rkcanfd_handle_error_int(struct rkcanfd_priv *priv)
        if (cf) {
                struct can_berr_counter bec;
 
-               rkcanfd_get_berr_counter_raw(priv, &bec);
+               rkcanfd_get_berr_counter_corrected(priv, &bec);
                cf->can_id |= CAN_ERR_PROT | CAN_ERR_BUSERROR | CAN_ERR_CNT;
                cf->data[6] = bec.txerr;
                cf->data[7] = bec.rxerr;
@@ -517,7 +555,7 @@ static int rkcanfd_handle_state_error_int(struct rkcanfd_priv *priv)
        u32 timestamp;
        int err;
 
-       rkcanfd_get_berr_counter_raw(priv, &bec);
+       rkcanfd_get_berr_counter_corrected(priv, &bec);
        can_state_get_by_berr_counter(ndev, &bec, &tx_state, &rx_state);
 
        new_state = max(tx_state, rx_state);
@@ -570,7 +608,7 @@ rkcanfd_handle_rx_fifo_overflow_int(struct rkcanfd_priv *priv)
        if (skb)
                return 0;
 
-       rkcanfd_get_berr_counter_raw(priv, &bec);
+       rkcanfd_get_berr_counter_corrected(priv, &bec);
 
        cf->can_id |= CAN_ERR_CRTL | CAN_ERR_CNT;
        cf->data[1] = CAN_ERR_CRTL_RX_OVERFLOW;
index 31cee33..eff0894 100644 (file)
@@ -167,6 +167,13 @@ static int rkcanfd_rxstx_filter(struct rkcanfd_priv *priv,
 
        /* Affected by Erratum 6 */
 
+       /* Manual handling of CAN Bus Error counters. See
+        * rkcanfd_get_corrected_berr_counter() for detailed
+        * explanation.
+        */
+       if (priv->bec.txerr)
+               priv->bec.txerr--;
+
        *tx_done = true;
 
        stats->tx_packets++;
@@ -229,6 +236,14 @@ static int rkcanfd_handle_rx_int_one(struct rkcanfd_priv *priv)
                        return 0;
        }
 
+       /* Manual handling of CAN Bus Error counters. See
+        * rkcanfd_get_corrected_berr_counter() for detailed
+        * explanation.
+        */
+       if (priv->bec.rxerr)
+               priv->bec.rxerr = min(CAN_ERROR_PASSIVE_THRESHOLD,
+                                     priv->bec.rxerr) - 1;
+
        if (header->frameinfo & RKCANFD_REG_FD_FRAMEINFO_FDF)
                skb = alloc_canfd_skb(priv->ndev, &skb_cfd);
        else
index 9db6d90..f8e74e8 100644 (file)
@@ -113,6 +113,14 @@ void rkcanfd_handle_tx_done_one(struct rkcanfd_priv *priv, const u32 ts,
        unsigned int tx_tail;
 
        tx_tail = rkcanfd_get_tx_tail(priv);
+
+       /* Manual handling of CAN Bus Error counters. See
+        * rkcanfd_get_corrected_berr_counter() for detailed
+        * explanation.
+        */
+       if (priv->bec.txerr)
+               priv->bec.txerr--;
+
        stats->tx_bytes +=
                can_rx_offload_get_echo_skb_queue_timestamp(&priv->offload,
                                                            tx_tail, ts,
index 3fe6ddc..67f135f 100644 (file)
@@ -459,6 +459,8 @@ struct rkcanfd_priv {
        u32 reg_int_mask_default;
        struct rkcanfd_devtype_data devtype_data;
 
+       struct can_berr_counter bec;
+
        struct reset_control *reset;
        struct clk_bulk_data *clks;
        int clks_num;