mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
synced 2026-05-26 11:40:24 +02:00
can: m_can: m_can_handle_state_errors(): fix CAN state transition to Error Active
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:e0d1f4816f("can: m_can: add Bosch M_CAN controller support") Fixes:cd0d83eab2("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>
This commit is contained in:
@@ -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)
|
||||
{
|
||||
u32 reg_psr;
|
||||
|
||||
reg_psr = m_can_read(cdev, M_CAN_PSR);
|
||||
|
||||
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;
|
||||
|
||||
return CAN_STATE_ERROR_ACTIVE;
|
||||
}
|
||||
|
||||
static int m_can_handle_state_errors(struct net_device *dev)
|
||||
{
|
||||
struct m_can_classdev *cdev = netdev_priv(dev);
|
||||
int work_done = 0;
|
||||
enum can_state new_state;
|
||||
|
||||
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);
|
||||
}
|
||||
new_state = m_can_state_get_by_psr(cdev);
|
||||
if (new_state == cdev->can.state)
|
||||
return 0;
|
||||
|
||||
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 (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 work_done;
|
||||
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,
|
||||
|
||||
Reference in New Issue
Block a user