]> git.proxmox.com Git - mirror_ubuntu-hirsute-kernel.git/commitdiff
can: flexcan: flexcan_chip_start(): fix erroneous flexcan_transceiver_enable() during...
authorMarc Kleine-Budde <mkl@pengutronix.de>
Wed, 18 Nov 2020 15:01:48 +0000 (16:01 +0100)
committerMarc Kleine-Budde <mkl@pengutronix.de>
Wed, 18 Nov 2020 15:02:34 +0000 (16:02 +0100)
If the CAN controller goes into bus off, the do_set_mode() callback with
CAN_MODE_START can be used to recover the controller, which then calls
flexcan_chip_start(). If configured, this is done automatically by the
framework or manually by the user.

In flexcan_chip_start() there is an explicit call to
flexcan_transceiver_enable(), which does a regulator_enable() on the
transceiver regulator. This results in a net usage counter increase, as there
is no corresponding flexcan_transceiver_disable() in the bus off code path.
This further leads to the transceiver stuck enabled, even if the CAN interface
is shut down.

To fix this problem the
flexcan_transceiver_enable()/flexcan_transceiver_disable() are moved out of
flexcan_chip_start()/flexcan_chip_stop() into flexcan_open()/flexcan_close().

Fixes: e955cead0311 ("CAN: Add Flexcan CAN controller driver")
Link: https://lore.kernel.org/r/20201118150148.2664024-1-mkl@pengutronix.de
Signed-off-by: Marc Kleine-Budde <mkl@pengutronix.de>
drivers/net/can/flexcan.c

index d6a9cf0e9b607399e7437a8806b6fe39785649b2..99e5f272205d33bb4dabc9695db1e8adc85c9f47 100644 (file)
@@ -1567,14 +1567,10 @@ static int flexcan_chip_start(struct net_device *dev)
                priv->write(reg_ctrl2, &regs->ctrl2);
        }
 
-       err = flexcan_transceiver_enable(priv);
-       if (err)
-               goto out_chip_disable;
-
        /* synchronize with the can bus */
        err = flexcan_chip_unfreeze(priv);
        if (err)
-               goto out_transceiver_disable;
+               goto out_chip_disable;
 
        priv->can.state = CAN_STATE_ERROR_ACTIVE;
 
@@ -1592,8 +1588,6 @@ static int flexcan_chip_start(struct net_device *dev)
 
        return 0;
 
- out_transceiver_disable:
-       flexcan_transceiver_disable(priv);
  out_chip_disable:
        flexcan_chip_disable(priv);
        return err;
@@ -1623,7 +1617,6 @@ static int __flexcan_chip_stop(struct net_device *dev, bool disable_on_error)
        priv->write(priv->reg_ctrl_default & ~FLEXCAN_CTRL_ERR_ALL,
                    &regs->ctrl);
 
-       flexcan_transceiver_disable(priv);
        priv->can.state = CAN_STATE_STOPPED;
 
        return 0;
@@ -1665,10 +1658,14 @@ static int flexcan_open(struct net_device *dev)
        if (err)
                goto out_runtime_put;
 
-       err = request_irq(dev->irq, flexcan_irq, IRQF_SHARED, dev->name, dev);
+       err = flexcan_transceiver_enable(priv);
        if (err)
                goto out_close;
 
+       err = request_irq(dev->irq, flexcan_irq, IRQF_SHARED, dev->name, dev);
+       if (err)
+               goto out_transceiver_disable;
+
        if (priv->can.ctrlmode & CAN_CTRLMODE_FD)
                priv->mb_size = sizeof(struct flexcan_mb) + CANFD_MAX_DLEN;
        else
@@ -1720,6 +1717,8 @@ static int flexcan_open(struct net_device *dev)
        can_rx_offload_del(&priv->offload);
  out_free_irq:
        free_irq(dev->irq, dev);
+ out_transceiver_disable:
+       flexcan_transceiver_disable(priv);
  out_close:
        close_candev(dev);
  out_runtime_put:
@@ -1738,6 +1737,7 @@ static int flexcan_close(struct net_device *dev)
 
        can_rx_offload_del(&priv->offload);
        free_irq(dev->irq, dev);
+       flexcan_transceiver_disable(priv);
 
        close_candev(dev);
        pm_runtime_put(priv->dev);