Merge patch series "can: m_can: fix pm_runtime and CAN state handling"

Marc Kleine-Budde <mkl@pengutronix.de> says:

The first patch fixes a pm_runtime imbalance in the m_can_platform
driver.

The rest of this series fixes the CAN state handling in the m_can
driver:
- add the missing state transition from "Error Warning" back to "Error
  Active" (patch 2)
- address the fact that in some SoCs (observed on the STM32MP15) the
  M_CAN IP core keeps the CAN state and CAN error counters over an
  internal reset cycle. Set the correct CAN state during ifup and
  system resume (patches 3+4)

Link: https://patch.msgid.link/20250929-m_can-fix-state-handling-v4-0-682b49b49d9a@pengutronix.de
Signed-off-by: Marc Kleine-Budde <mkl@pengutronix.de>
This commit is contained in:
Marc Kleine-Budde
2025-10-08 10:19:46 +02:00
2 changed files with 38 additions and 28 deletions
+37 -27
View 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)
{
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,
@@ -1606,7 +1617,7 @@ static int m_can_start(struct net_device *dev)
netdev_queue_set_dql_min_limit(netdev_get_tx_queue(cdev->net, 0),
cdev->tx_max_coalesced_frames);
cdev->can.state = CAN_STATE_ERROR_ACTIVE;
cdev->can.state = m_can_state_get_by_psr(cdev);
m_can_enable_all_interrupts(cdev);
@@ -2492,12 +2503,11 @@ int m_can_class_suspend(struct device *dev)
}
m_can_clk_stop(cdev);
cdev->can.state = CAN_STATE_SLEEPING;
}
pinctrl_pm_select_sleep_state(dev);
cdev->can.state = CAN_STATE_SLEEPING;
return ret;
}
EXPORT_SYMBOL_GPL(m_can_class_suspend);
@@ -2510,8 +2520,6 @@ int m_can_class_resume(struct device *dev)
pinctrl_pm_select_default_state(dev);
cdev->can.state = CAN_STATE_ERROR_ACTIVE;
if (netif_running(ndev)) {
ret = m_can_clk_start(cdev);
if (ret)
@@ -2529,6 +2537,8 @@ int m_can_class_resume(struct device *dev)
if (cdev->ops->init)
ret = cdev->ops->init(cdev);
cdev->can.state = m_can_state_get_by_psr(cdev);
m_can_write(cdev, M_CAN_IE, cdev->active_interrupts);
} else {
ret = m_can_start(ndev);
+1 -1
View File
@@ -180,7 +180,7 @@ static void m_can_plat_remove(struct platform_device *pdev)
struct m_can_classdev *mcan_class = &priv->cdev;
m_can_class_unregister(mcan_class);
pm_runtime_disable(mcan_class->dev);
m_can_class_free_dev(mcan_class->net);
}