can: mcp251x: only reset hardware as required
authorTimo Schlüßler <schluessler@krause.de>
Fri, 11 Oct 2019 13:38:20 +0000 (15:38 +0200)
committerMarc Kleine-Budde <mkl@pengutronix.de>
Mon, 11 Nov 2019 20:57:28 +0000 (21:57 +0100)
This prevents unwanted glitches on the outputs when changing the link
state of the can interface or when resuming from suspend. Only if the
device is powered off during suspend it needs to be resetted as required
by the specs.

Signed-off-by: Timo Schlüßler <schluessler@krause.de>
Signed-off-by: Marc Kleine-Budde <mkl@pengutronix.de>
drivers/net/can/spi/mcp251x.c

index d371d38..5009ff2 100644 (file)
@@ -468,6 +468,39 @@ static void mcp251x_hw_sleep(struct spi_device *spi)
        mcp251x_write_reg(spi, CANCTRL, CANCTRL_REQOP_SLEEP);
 }
 
+/* May only be called when device is sleeping! */
+static int mcp251x_hw_wake(struct spi_device *spi)
+{
+       unsigned long timeout;
+
+       /* Force wakeup interrupt to wake device, but don't execute IST */
+       disable_irq(spi->irq);
+       mcp251x_write_2regs(spi, CANINTE, CANINTE_WAKIE, CANINTF_WAKIF);
+
+       /* Wait for oscillator startup timer after wake up */
+       mdelay(MCP251X_OST_DELAY_MS);
+
+       /* Put device into config mode */
+       mcp251x_write_reg(spi, CANCTRL, CANCTRL_REQOP_CONF);
+
+       /* Wait for the device to enter config mode */
+       timeout = jiffies + HZ;
+       while ((mcp251x_read_reg(spi, CANSTAT) & CANCTRL_REQOP_MASK) !=
+                       CANCTRL_REQOP_CONF) {
+               schedule();
+               if (time_after(jiffies, timeout)) {
+                       dev_err(&spi->dev, "MCP251x didn't enter in config mode\n");
+                       return -EBUSY;
+               }
+       }
+
+       /* Disable and clear pending interrupts */
+       mcp251x_write_2regs(spi, CANINTE, 0x00, 0x00);
+       enable_irq(spi->irq);
+
+       return 0;
+}
+
 static netdev_tx_t mcp251x_hard_start_xmit(struct sk_buff *skb,
                                           struct net_device *net)
 {
@@ -725,8 +758,12 @@ static void mcp251x_restart_work_handler(struct work_struct *ws)
 
        mutex_lock(&priv->mcp_lock);
        if (priv->after_suspend) {
-               mcp251x_hw_reset(spi);
-               mcp251x_setup(net, spi);
+               if (priv->after_suspend & AFTER_SUSPEND_POWER) {
+                       mcp251x_hw_reset(spi);
+                       mcp251x_setup(net, spi);
+               } else {
+                       mcp251x_hw_wake(spi);
+               }
                priv->force_quit = 0;
                if (priv->after_suspend & AFTER_SUSPEND_RESTART) {
                        mcp251x_set_normal_mode(spi);
@@ -923,7 +960,7 @@ static int mcp251x_open(struct net_device *net)
        INIT_WORK(&priv->tx_work, mcp251x_tx_work_handler);
        INIT_WORK(&priv->restart_work, mcp251x_restart_work_handler);
 
-       ret = mcp251x_hw_reset(spi);
+       ret = mcp251x_hw_wake(spi);
        if (ret)
                goto out_free_wq;
        ret = mcp251x_setup(net, spi);
@@ -1165,13 +1202,13 @@ static int __maybe_unused mcp251x_can_resume(struct device *dev)
 
        if (priv->after_suspend & AFTER_SUSPEND_POWER)
                mcp251x_power_enable(priv->power, 1);
-
-       if (priv->after_suspend & AFTER_SUSPEND_UP) {
+       if (priv->after_suspend & AFTER_SUSPEND_UP)
                mcp251x_power_enable(priv->transceiver, 1);
+
+       if (priv->after_suspend & (AFTER_SUSPEND_POWER | AFTER_SUSPEND_UP))
                queue_work(priv->wq, &priv->restart_work);
-       } else {
+       else
                priv->after_suspend = 0;
-       }
 
        priv->force_quit = 0;
        enable_irq(spi->irq);