From: Aisheng Dong <aisheng.dong@xxxxxxx> Flexcan will be disabled during suspend if no wakeup function required and enabled after resume accordingly. During this period, we could explicitly disable clocks. Implement Runtime PM which will: 1) Keep device in suspend state (clocks disabled) if it's not openned. 2) Make Power Domain framework be able to shutdown the corresponding power domain of this device. Signed-off-by: Aisheng Dong <aisheng.dong@xxxxxxx> Signed-off-by: Joakim Zhang <qiangqing.zhang@xxxxxxx> --- ChangeLog: V1->V2: *rebased on patch "can: flexcan: add self wakeup support". *add check when to do flexcan_clks_disable/enable() during Runtime PM. --- drivers/net/can/flexcan.c | 119 ++++++++++++++++++++++++-------------- 1 file changed, 77 insertions(+), 42 deletions(-) diff --git a/drivers/net/can/flexcan.c b/drivers/net/can/flexcan.c index d12b2f7d2834..130a2cb04827 100644 --- a/drivers/net/can/flexcan.c +++ b/drivers/net/can/flexcan.c @@ -24,6 +24,7 @@ #include <linux/of.h> #include <linux/of_device.h> #include <linux/platform_device.h> +#include <linux/pm_runtime.h> #include <linux/regulator/consumer.h> #include <linux/regmap.h> @@ -277,6 +278,7 @@ struct flexcan_priv { u32 reg_imask1_default; u32 reg_imask2_default; + struct device *dev; struct clk *clk_ipg; struct clk *clk_per; const struct flexcan_devtype_data *devtype_data; @@ -444,6 +446,27 @@ static inline void flexcan_error_irq_disable(const struct flexcan_priv *priv) priv->write(reg_ctrl, ®s->ctrl); } +static int flexcan_clks_enable(const struct flexcan_priv *priv) +{ + int err; + + err = clk_prepare_enable(priv->clk_ipg); + if (err) + return err; + + err = clk_prepare_enable(priv->clk_per); + if (err) + clk_disable_unprepare(priv->clk_ipg); + + return err; +} + +static void flexcan_clks_disable(const struct flexcan_priv *priv) +{ + clk_disable_unprepare(priv->clk_ipg); + clk_disable_unprepare(priv->clk_per); +} + static inline int flexcan_transceiver_enable(const struct flexcan_priv *priv) { if (!priv->reg_xceiver) @@ -570,19 +593,11 @@ static int flexcan_get_berr_counter(const struct net_device *dev, const struct flexcan_priv *priv = netdev_priv(dev); int err; - err = clk_prepare_enable(priv->clk_ipg); - if (err) - return err; - - err = clk_prepare_enable(priv->clk_per); - if (err) - goto out_disable_ipg; + pm_runtime_get_sync(priv->dev); err = __flexcan_get_berr_counter(dev, bec); - clk_disable_unprepare(priv->clk_per); - out_disable_ipg: - clk_disable_unprepare(priv->clk_ipg); + pm_runtime_put(priv->dev); return err; } @@ -1215,17 +1230,13 @@ static int flexcan_open(struct net_device *dev) struct flexcan_priv *priv = netdev_priv(dev); int err; - err = clk_prepare_enable(priv->clk_ipg); + err = pm_runtime_get_sync(priv->dev); if (err) return err; - err = clk_prepare_enable(priv->clk_per); - if (err) - goto out_disable_ipg; - err = open_candev(dev); if (err) - goto out_disable_per; + goto out_pm_runtime; err = request_irq(dev->irq, flexcan_irq, IRQF_SHARED, dev->name, dev); if (err) @@ -1288,10 +1299,8 @@ static int flexcan_open(struct net_device *dev) free_irq(dev->irq, dev); out_close: close_candev(dev); - out_disable_per: - clk_disable_unprepare(priv->clk_per); - out_disable_ipg: - clk_disable_unprepare(priv->clk_ipg); + out_pm_runtime: + pm_runtime_put(priv->dev); return err; } @@ -1306,11 +1315,11 @@ static int flexcan_close(struct net_device *dev) can_rx_offload_del(&priv->offload); free_irq(dev->irq, dev); - clk_disable_unprepare(priv->clk_per); - clk_disable_unprepare(priv->clk_ipg); close_candev(dev); + pm_runtime_put(priv->dev); + can_led_event(dev, CAN_LED_EVENT_STOP); return 0; @@ -1349,18 +1358,10 @@ static int register_flexcandev(struct net_device *dev) struct flexcan_regs __iomem *regs = priv->regs; u32 reg, err; - err = clk_prepare_enable(priv->clk_ipg); - if (err) - return err; - - err = clk_prepare_enable(priv->clk_per); - if (err) - goto out_disable_ipg; - /* select "bus clock", chip must be disabled */ err = flexcan_chip_disable(priv); if (err) - goto out_disable_per; + return err; reg = priv->read(®s->ctrl); reg |= FLEXCAN_CTRL_CLK_SRC; priv->write(reg, ®s->ctrl); @@ -1389,13 +1390,8 @@ static int register_flexcandev(struct net_device *dev) err = register_candev(dev); - /* disable core and turn off clocks */ out_chip_disable: flexcan_chip_disable(priv); - out_disable_per: - clk_disable_unprepare(priv->clk_per); - out_disable_ipg: - clk_disable_unprepare(priv->clk_ipg); return err; } @@ -1556,6 +1552,7 @@ static int flexcan_probe(struct platform_device *pdev) priv->write = flexcan_write_le; } + priv->dev = &pdev->dev; priv->can.clock.freq = clock_freq; priv->can.bittiming_const = &flexcan_bittiming_const; priv->can.do_set_mode = flexcan_set_mode; @@ -1569,14 +1566,23 @@ static int flexcan_probe(struct platform_device *pdev) priv->devtype_data = devtype_data; priv->reg_xceiver = reg_xceiver; + pm_runtime_enable(&pdev->dev); + err = pm_runtime_get_sync(&pdev->dev); + if (err < 0) { + dev_err(&pdev->dev, "pm_runtime_get failed(%d)\n", err); + goto failed_rpm_disable; + } + err = register_flexcandev(dev); if (err) { dev_err(&pdev->dev, "registering netdev failed\n"); - goto failed_register; + goto failed_rpm_put; } devm_can_led_init(dev); + pm_runtime_put(&pdev->dev); + if (priv->devtype_data->quirks & FLEXCAN_QUIRK_SETUP_STOP_MODE) { err = flexcan_setup_stop_mode(pdev); if (err) @@ -1588,8 +1594,11 @@ static int flexcan_probe(struct platform_device *pdev) return 0; - failed_register: + failed_rpm_put: + pm_runtime_put(&pdev->dev); free_candev(dev); + failed_rpm_disable: + pm_runtime_disable(&pdev->dev); return err; } @@ -1598,6 +1607,7 @@ static int flexcan_remove(struct platform_device *pdev) struct net_device *dev = platform_get_drvdata(pdev); unregister_flexcandev(dev); + pm_runtime_disable(&pdev->dev); free_candev(dev); return 0; @@ -1607,7 +1617,7 @@ static int __maybe_unused flexcan_suspend(struct device *device) { struct net_device *dev = dev_get_drvdata(device); struct flexcan_priv *priv = netdev_priv(dev); - int err; + int err = 0; if (netif_running(dev)) { /* if wakeup is enabled, enter stop mode @@ -1620,20 +1630,21 @@ static int __maybe_unused flexcan_suspend(struct device *device) err = flexcan_chip_disable(priv); if (err) return err; + err = pm_runtime_force_suspend(device); } netif_stop_queue(dev); netif_device_detach(dev); } priv->can.state = CAN_STATE_SLEEPING; - return 0; + return err; } static int __maybe_unused flexcan_resume(struct device *device) { struct net_device *dev = dev_get_drvdata(device); struct flexcan_priv *priv = netdev_priv(dev); - int err; + int err = 0; priv->can.state = CAN_STATE_ERROR_ACTIVE; if (netif_running(dev)) { @@ -1642,11 +1653,34 @@ static int __maybe_unused flexcan_resume(struct device *device) if (device_may_wakeup(device)) { disable_irq_wake(dev->irq); } else { - err = flexcan_chip_enable(priv); + err = pm_runtime_force_resume(device); if (err) return err; + err = flexcan_chip_enable(priv); } } + return err; +} + +static int __maybe_unused flexcan_runtime_suspend(struct device *device) +{ + struct net_device *dev = dev_get_drvdata(device); + struct flexcan_priv *priv = netdev_priv(dev); + + if (netif_running(dev)) + flexcan_clks_disable(priv); + + return 0; +} + +static int __maybe_unused flexcan_runtime_resume(struct device *device) +{ + struct net_device *dev = dev_get_drvdata(device); + struct flexcan_priv *priv = netdev_priv(dev); + + if (!netif_running(dev)) + flexcan_clks_enable(priv); + return 0; } @@ -1676,6 +1710,7 @@ static int __maybe_unused flexcan_noirq_resume(struct device *device) static const struct dev_pm_ops flexcan_pm_ops = { SET_SYSTEM_SLEEP_PM_OPS(flexcan_suspend, flexcan_resume) + SET_RUNTIME_PM_OPS(flexcan_runtime_suspend, flexcan_runtime_resume, NULL) SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(flexcan_noirq_suspend, flexcan_noirq_resume) }; -- 2.17.1