Moves all calls which would alter the clock setup, apart from probe, into PM calls. --- .../net/can/spi/mcp251xfd/mcp251xfd-core.c | 91 ++++++++++--------- 1 file changed, 48 insertions(+), 43 deletions(-) diff --git a/drivers/net/can/spi/mcp251xfd/mcp251xfd-core.c b/drivers/net/can/spi/mcp251xfd/mcp251xfd-core.c index c3f49543f..3c440f9c8 100644 --- a/drivers/net/can/spi/mcp251xfd/mcp251xfd-core.c +++ b/drivers/net/can/spi/mcp251xfd/mcp251xfd-core.c @@ -519,25 +519,22 @@ static inline bool mcp251xfd_osc_invalid(u32 reg) return reg == 0x0 || reg == 0xffffffff; } -static int mcp251xfd_chip_clock_enable(const struct mcp251xfd_priv *priv) +static int mcp251xfd_chip_wake(const struct mcp251xfd_priv *priv) { u32 osc, osc_reference, osc_mask; int err; - /* Set Power On Defaults for "Clock Output Divisor" and remove - * "Oscillator Disable" bit. + /* For normal sleep in MCP2517FD and MCP2518FD, clearing + * "Oscillator Disable" will wake it. For low power mode in + * MCP2518DF, asserting the chip will wake it. Writing to + * the Oscillator register will wake it in both cases. */ + osc = FIELD_PREP(MCP251XFD_REG_OSC_CLKODIV_MASK, MCP251XFD_REG_OSC_CLKODIV_10); osc_reference = MCP251XFD_REG_OSC_OSCRDY; - osc_mask = MCP251XFD_REG_OSC_OSCRDY | MCP251XFD_REG_OSC_PLLRDY; + osc_mask = MCP251XFD_REG_OSC_OSCRDY; - /* Note: - * - * If the controller is in Sleep Mode the following write only - * removes the "Oscillator Disable" bit and powers it up. All - * other bits are unaffected. - */ err = regmap_write(priv->map_reg, MCP251XFD_REG_OSC, osc); if (err) return err; @@ -569,13 +566,6 @@ static int mcp251xfd_chip_softreset_do(const struct mcp251xfd_priv *priv) const __be16 cmd = mcp251xfd_cmd_reset(); int err; - /* The Set Mode and SPI Reset command only seems to works if - * the controller is not in Sleep Mode. - */ - err = mcp251xfd_chip_clock_enable(priv); - if (err) - return err; - err = mcp251xfd_chip_set_mode(priv, MCP251XFD_REG_CON_MODE_CONFIG); if (err) return err; @@ -652,20 +642,6 @@ static int mcp251xfd_chip_softreset(const struct mcp251xfd_priv *priv) static int mcp251xfd_chip_clock_init(const struct mcp251xfd_priv *priv) { - u32 osc; - int err; - - /* Activate Low Power Mode on Oscillator Disable. This only - * works on the MCP2518FD. The MCP2517FD will go into normal - * Sleep Mode instead. - */ - osc = MCP251XFD_REG_OSC_LPMEN | - FIELD_PREP(MCP251XFD_REG_OSC_CLKODIV_MASK, - MCP251XFD_REG_OSC_CLKODIV_10); - err = regmap_write(priv->map_reg, MCP251XFD_REG_OSC, osc); - if (err) - return err; - /* Set Time Base Counter Prescaler to 1. * * This means an overflow of the 32 bit Time Base Counter @@ -1020,17 +996,13 @@ static int mcp251xfd_chip_stop(struct mcp251xfd_priv *priv, mcp251xfd_chip_interrupts_disable(priv); mcp251xfd_chip_rx_int_disable(priv); - return mcp251xfd_chip_set_mode(priv, MCP251XFD_REG_CON_MODE_SLEEP); + return mcp251xfd_chip_set_mode(priv, MCP251XFD_REG_CON_MODE_CONFIG); } static int mcp251xfd_chip_start(struct mcp251xfd_priv *priv) { int err; - err = mcp251xfd_chip_softreset(priv); - if (err) - goto out_chip_stop; - err = mcp251xfd_chip_clock_init(priv); if (err) goto out_chip_stop; @@ -2660,12 +2632,9 @@ static int mcp251xfd_register(struct mcp251xfd_priv *priv) /* Put controller into sleep mode and let pm_runtime_put() * disable the clocks and vdd. If CONFIG_PM is not enabled, - * the clocks and vdd will stay powered. + * the clocks and vdd will stay powered, and the chip will + * not enter sleep. */ - err = mcp251xfd_chip_set_mode(priv, MCP251XFD_REG_CON_MODE_SLEEP); - if (err) - goto out_unregister_candev; - pm_runtime_put(ndev->dev.parent); return 0; @@ -2893,16 +2862,52 @@ static int mcp251xfd_remove(struct spi_device *spi) static int __maybe_unused mcp251xfd_runtime_suspend(struct device *device) { + u32 osc; + int err; const struct mcp251xfd_priv *priv = dev_get_drvdata(device); - return mcp251xfd_clks_and_vdd_disable(priv); + /* Activate Low Power Mode on Oscillator Disable. This only + * works on the MCP2518FD. The MCP2517FD will go into normal + * Sleep Mode instead. + */ + err = regmap_read(priv->map_reg, MCP251XFD_REG_OSC, &osc); + if (err) + return err; + + osc |= MCP251XFD_REG_OSC_LPMEN; + err = regmap_write(priv->map_reg, MCP251XFD_REG_OSC, osc); + if (err) + return err; + + err = mcp251xfd_chip_set_mode(priv, MCP251XFD_REG_CON_MODE_SLEEP); + if (err) + return err; + + err = mcp251xfd_clks_and_vdd_disable(priv); + if (err) + return err; + + return err; } static int __maybe_unused mcp251xfd_runtime_resume(struct device *device) { + int err; const struct mcp251xfd_priv *priv = dev_get_drvdata(device); - return mcp251xfd_clks_and_vdd_enable(priv); + err = mcp251xfd_clks_and_vdd_enable(priv); + if (err) + return err; + + err = mcp251xfd_chip_wake(priv); + if (err) + return err; + + err = mcp251xfd_chip_softreset(priv); + if (err) + return err; + + return err; } static const struct dev_pm_ops mcp251xfd_pm_ops = { -- 2.20.1