Skip to content

Commit 8ebd83b

Browse files
anssihmarckleinebudde
authored andcommitted
can: xilinx_can: fix power management handling
There are several issues with the suspend/resume handling code of the driver: - The device is attached and detached in the runtime_suspend() and runtime_resume() callbacks if the interface is running. However, during xcan_chip_start() the interface is considered running, causing the resume handler to incorrectly call netif_start_queue() at the beginning of xcan_chip_start(), and on xcan_chip_start() error return the suspend handler detaches the device leaving the user unable to bring-up the device anymore. - The device is not brought properly up on system resume. A reset is done and the code tries to determine the bus state after that. However, after reset the device is always in Configuration mode (down), so the state checking code does not make sense and communication will also not work. - The suspend callback tries to set the device to sleep mode (low-power mode which monitors the bus and brings the device back to normal mode on activity), but then immediately disables the clocks (possibly before the device reaches the sleep mode), which does not make sense to me. If a clean shutdown is wanted before disabling clocks, we can just bring it down completely instead of only sleep mode. Reorganize the PM code so that only the clock logic remains in the runtime PM callbacks and the system PM callbacks contain the device bring-up/down logic. This makes calling the runtime PM callbacks during e.g. xcan_chip_start() safe. The system PM callbacks now simply call common code to start/stop the HW if the interface was running, replacing the broken code from before. xcan_chip_stop() is updated to use the common reset code so that it will wait for the reset to complete. Reset also disables all interrupts so do not do that separately. Also, the device_may_wakeup() checks are removed as the driver does not have wakeup support. Tested on Zynq-7000 integrated CAN. Signed-off-by: Anssi Hannula <[email protected]> Cc: Michal Simek <[email protected]> Cc: <[email protected]> Signed-off-by: Marc Kleine-Budde <[email protected]>
1 parent 2f4f0f3 commit 8ebd83b

File tree

1 file changed

+28
-41
lines changed

1 file changed

+28
-41
lines changed

drivers/net/can/xilinx_can.c

Lines changed: 28 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -984,13 +984,9 @@ static irqreturn_t xcan_interrupt(int irq, void *dev_id)
984984
static void xcan_chip_stop(struct net_device *ndev)
985985
{
986986
struct xcan_priv *priv = netdev_priv(ndev);
987-
u32 ier;
988987

989988
/* Disable interrupts and leave the can in configuration mode */
990-
ier = priv->read_reg(priv, XCAN_IER_OFFSET);
991-
ier &= ~XCAN_INTR_ALL;
992-
priv->write_reg(priv, XCAN_IER_OFFSET, ier);
993-
priv->write_reg(priv, XCAN_SRR_OFFSET, XCAN_SRR_RESET_MASK);
989+
set_reset_mode(ndev);
994990
priv->can.state = CAN_STATE_STOPPED;
995991
}
996992

@@ -1123,10 +1119,15 @@ static const struct net_device_ops xcan_netdev_ops = {
11231119
*/
11241120
static int __maybe_unused xcan_suspend(struct device *dev)
11251121
{
1126-
if (!device_may_wakeup(dev))
1127-
return pm_runtime_force_suspend(dev);
1122+
struct net_device *ndev = dev_get_drvdata(dev);
11281123

1129-
return 0;
1124+
if (netif_running(ndev)) {
1125+
netif_stop_queue(ndev);
1126+
netif_device_detach(ndev);
1127+
xcan_chip_stop(ndev);
1128+
}
1129+
1130+
return pm_runtime_force_suspend(dev);
11301131
}
11311132

11321133
/**
@@ -1138,11 +1139,27 @@ static int __maybe_unused xcan_suspend(struct device *dev)
11381139
*/
11391140
static int __maybe_unused xcan_resume(struct device *dev)
11401141
{
1141-
if (!device_may_wakeup(dev))
1142-
return pm_runtime_force_resume(dev);
1142+
struct net_device *ndev = dev_get_drvdata(dev);
1143+
int ret;
11431144

1144-
return 0;
1145+
ret = pm_runtime_force_resume(dev);
1146+
if (ret) {
1147+
dev_err(dev, "pm_runtime_force_resume failed on resume\n");
1148+
return ret;
1149+
}
1150+
1151+
if (netif_running(ndev)) {
1152+
ret = xcan_chip_start(ndev);
1153+
if (ret) {
1154+
dev_err(dev, "xcan_chip_start failed on resume\n");
1155+
return ret;
1156+
}
11451157

1158+
netif_device_attach(ndev);
1159+
netif_start_queue(ndev);
1160+
}
1161+
1162+
return 0;
11461163
}
11471164

11481165
/**
@@ -1157,14 +1174,6 @@ static int __maybe_unused xcan_runtime_suspend(struct device *dev)
11571174
struct net_device *ndev = dev_get_drvdata(dev);
11581175
struct xcan_priv *priv = netdev_priv(ndev);
11591176

1160-
if (netif_running(ndev)) {
1161-
netif_stop_queue(ndev);
1162-
netif_device_detach(ndev);
1163-
}
1164-
1165-
priv->write_reg(priv, XCAN_MSR_OFFSET, XCAN_MSR_SLEEP_MASK);
1166-
priv->can.state = CAN_STATE_SLEEPING;
1167-
11681177
clk_disable_unprepare(priv->bus_clk);
11691178
clk_disable_unprepare(priv->can_clk);
11701179

@@ -1183,7 +1192,6 @@ static int __maybe_unused xcan_runtime_resume(struct device *dev)
11831192
struct net_device *ndev = dev_get_drvdata(dev);
11841193
struct xcan_priv *priv = netdev_priv(ndev);
11851194
int ret;
1186-
u32 isr, status;
11871195

11881196
ret = clk_prepare_enable(priv->bus_clk);
11891197
if (ret) {
@@ -1197,27 +1205,6 @@ static int __maybe_unused xcan_runtime_resume(struct device *dev)
11971205
return ret;
11981206
}
11991207

1200-
priv->write_reg(priv, XCAN_SRR_OFFSET, XCAN_SRR_RESET_MASK);
1201-
isr = priv->read_reg(priv, XCAN_ISR_OFFSET);
1202-
status = priv->read_reg(priv, XCAN_SR_OFFSET);
1203-
1204-
if (netif_running(ndev)) {
1205-
if (isr & XCAN_IXR_BSOFF_MASK) {
1206-
priv->can.state = CAN_STATE_BUS_OFF;
1207-
priv->write_reg(priv, XCAN_SRR_OFFSET,
1208-
XCAN_SRR_RESET_MASK);
1209-
} else if ((status & XCAN_SR_ESTAT_MASK) ==
1210-
XCAN_SR_ESTAT_MASK) {
1211-
priv->can.state = CAN_STATE_ERROR_PASSIVE;
1212-
} else if (status & XCAN_SR_ERRWRN_MASK) {
1213-
priv->can.state = CAN_STATE_ERROR_WARNING;
1214-
} else {
1215-
priv->can.state = CAN_STATE_ERROR_ACTIVE;
1216-
}
1217-
netif_device_attach(ndev);
1218-
netif_start_queue(ndev);
1219-
}
1220-
12211208
return 0;
12221209
}
12231210

0 commit comments

Comments
 (0)