]> www.infradead.org Git - users/hch/misc.git/commitdiff
can: m_can: m_can_handle_state_errors(): fix CAN state transition to Error Active
authorMarc Kleine-Budde <mkl@pengutronix.de>
Wed, 6 Aug 2025 14:56:15 +0000 (16:56 +0200)
committerMarc Kleine-Budde <mkl@pengutronix.de>
Wed, 8 Oct 2025 08:18:51 +0000 (10:18 +0200)
The CAN Error State is determined by the receive and transmit error
counters. The CAN error counters decrease when reception/transmission
is successful, so that a status transition back to the Error Active
status is possible. This transition is not handled by
m_can_handle_state_errors().

Add the missing detection of the Error Active state to
m_can_handle_state_errors() and extend the handling of this state in
m_can_handle_state_change().

Fixes: e0d1f4816f2a ("can: m_can: add Bosch M_CAN controller support")
Fixes: cd0d83eab2e0 ("can: m_can: m_can_handle_state_change(): fix state change")
Reviewed-by: Markus Schneider-Pargmann <msp@baylibre.com>
Link: https://patch.msgid.link/20250929-m_can-fix-state-handling-v4-2-682b49b49d9a@pengutronix.de
Signed-off-by: Marc Kleine-Budde <mkl@pengutronix.de>
drivers/net/can/m_can/m_can.c

index e1d725979685ff90bff9957bcf6193265bfbcb55..ac864183a536495ac264287ca8555c5e223a8355 100644 (file)
@@ -812,6 +812,9 @@ static int m_can_handle_state_change(struct net_device *dev,
        u32 timestamp = 0;
 
        switch (new_state) {
+       case CAN_STATE_ERROR_ACTIVE:
+               cdev->can.state = CAN_STATE_ERROR_ACTIVE;
+               break;
        case CAN_STATE_ERROR_WARNING:
                /* error warning state */
                cdev->can.can_stats.error_warning++;
@@ -841,6 +844,12 @@ static int m_can_handle_state_change(struct net_device *dev,
        __m_can_get_berr_counter(dev, &bec);
 
        switch (new_state) {
+       case CAN_STATE_ERROR_ACTIVE:
+               cf->can_id |= CAN_ERR_CRTL | CAN_ERR_CNT;
+               cf->data[1] = CAN_ERR_CRTL_ACTIVE;
+               cf->data[6] = bec.txerr;
+               cf->data[7] = bec.rxerr;
+               break;
        case CAN_STATE_ERROR_WARNING:
                /* error warning state */
                cf->can_id |= CAN_ERR_CRTL | CAN_ERR_CNT;
@@ -877,30 +886,33 @@ static int m_can_handle_state_change(struct net_device *dev,
        return 1;
 }
 
-static int m_can_handle_state_errors(struct net_device *dev, u32 psr)
+static enum can_state
+m_can_state_get_by_psr(struct m_can_classdev *cdev)
 {
-       struct m_can_classdev *cdev = netdev_priv(dev);
-       int work_done = 0;
+       u32 reg_psr;
 
-       if (psr & PSR_EW && cdev->can.state != CAN_STATE_ERROR_WARNING) {
-               netdev_dbg(dev, "entered error warning state\n");
-               work_done += m_can_handle_state_change(dev,
-                                                      CAN_STATE_ERROR_WARNING);
-       }
+       reg_psr = m_can_read(cdev, M_CAN_PSR);
 
-       if (psr & PSR_EP && cdev->can.state != CAN_STATE_ERROR_PASSIVE) {
-               netdev_dbg(dev, "entered error passive state\n");
-               work_done += m_can_handle_state_change(dev,
-                                                      CAN_STATE_ERROR_PASSIVE);
-       }
+       if (reg_psr & PSR_BO)
+               return CAN_STATE_BUS_OFF;
+       if (reg_psr & PSR_EP)
+               return CAN_STATE_ERROR_PASSIVE;
+       if (reg_psr & PSR_EW)
+               return CAN_STATE_ERROR_WARNING;
 
-       if (psr & PSR_BO && cdev->can.state != CAN_STATE_BUS_OFF) {
-               netdev_dbg(dev, "entered error bus off state\n");
-               work_done += m_can_handle_state_change(dev,
-                                                      CAN_STATE_BUS_OFF);
-       }
+       return CAN_STATE_ERROR_ACTIVE;
+}
 
-       return work_done;
+static int m_can_handle_state_errors(struct net_device *dev)
+{
+       struct m_can_classdev *cdev = netdev_priv(dev);
+       enum can_state new_state;
+
+       new_state = m_can_state_get_by_psr(cdev);
+       if (new_state == cdev->can.state)
+               return 0;
+
+       return m_can_handle_state_change(dev, new_state);
 }
 
 static void m_can_handle_other_err(struct net_device *dev, u32 irqstatus)
@@ -1031,8 +1043,7 @@ static int m_can_rx_handler(struct net_device *dev, int quota, u32 irqstatus)
        }
 
        if (irqstatus & IR_ERR_STATE)
-               work_done += m_can_handle_state_errors(dev,
-                                                      m_can_read(cdev, M_CAN_PSR));
+               work_done += m_can_handle_state_errors(dev);
 
        if (irqstatus & IR_ERR_BUS_30X)
                work_done += m_can_handle_bus_errors(dev, irqstatus,