* [PATCH 0/2] mmc: sunxi: Add runtime PM support @ 2017-12-20 10:50 Maxime Ripard 2017-12-20 10:50 ` [PATCH 1/2] mmc: sunxi: Reorder the headers Maxime Ripard 2017-12-20 10:50 ` [PATCH 2/2] mmc: sunxi: Add runtime_pm support Maxime Ripard 0 siblings, 2 replies; 5+ messages in thread From: Maxime Ripard @ 2017-12-20 10:50 UTC (permalink / raw) To: linux-arm-kernel Hi, Since it's introduction, our MMC controller has had its external clocks running all the time. While that was working great, the power usage and most importantly the EMI that it generated was pretty bad. Let's implement some runtime_pm hooks with an autosuspend to cut the external clock when the MMC is not active. I didn't get all the way to also shutdown the bus clocks since on some older SoCs (before the A31), it also means that the controler will be reset which has some quite important implications obviously. And I couldn't make it work reliably at the moment. Anyway, it can always be implemented as a second step if needed. (Quite simple) benchmarks have shown no noticeable regressions since the controller state is maintained. Let me know what you think, Maxime Maxime Ripard (2): mmc: sunxi: Reorder the headers mmc: sunxi: Add runtime_pm support drivers/mmc/host/sunxi-mmc.c | 133 +++++++++++++++++++++--------------- 1 file changed, 80 insertions(+), 53 deletions(-) base-commit: 4fbd8d194f06c8a3fd2af1ce560ddb31f7ec8323 -- git-series 0.9.1 ^ permalink raw reply [flat|nested] 5+ messages in thread
* [PATCH 1/2] mmc: sunxi: Reorder the headers 2017-12-20 10:50 [PATCH 0/2] mmc: sunxi: Add runtime PM support Maxime Ripard @ 2017-12-20 10:50 ` Maxime Ripard 2017-12-20 10:50 ` [PATCH 2/2] mmc: sunxi: Add runtime_pm support Maxime Ripard 1 sibling, 0 replies; 5+ messages in thread From: Maxime Ripard @ 2017-12-20 10:50 UTC (permalink / raw) To: linux-arm-kernel Our headers sort algorithm has had pretty chaotic results. Let's fix that. Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com> --- drivers/mmc/host/sunxi-mmc.c | 43 +++++++++++++++++-------------------- 1 file changed, 20 insertions(+), 23 deletions(-) diff --git a/drivers/mmc/host/sunxi-mmc.c b/drivers/mmc/host/sunxi-mmc.c index cc98355dbdb9..3ce46ebd3488 100644 --- a/drivers/mmc/host/sunxi-mmc.c +++ b/drivers/mmc/host/sunxi-mmc.c @@ -13,36 +13,33 @@ * the License, or (at your option) any later version. */ -#include <linux/kernel.h> -#include <linux/module.h> -#include <linux/io.h> -#include <linux/device.h> -#include <linux/interrupt.h> -#include <linux/delay.h> -#include <linux/err.h> - #include <linux/clk.h> #include <linux/clk/sunxi-ng.h> -#include <linux/gpio.h> -#include <linux/platform_device.h> -#include <linux/spinlock.h> -#include <linux/scatterlist.h> +#include <linux/delay.h> +#include <linux/device.h> #include <linux/dma-mapping.h> -#include <linux/slab.h> -#include <linux/reset.h> -#include <linux/regulator/consumer.h> - -#include <linux/of_address.h> -#include <linux/of_gpio.h> -#include <linux/of_platform.h> - +#include <linux/err.h> +#include <linux/gpio.h> +#include <linux/interrupt.h> +#include <linux/io.h> +#include <linux/kernel.h> +#include <linux/mmc/card.h> +#include <linux/mmc/core.h> +#include <linux/mmc/mmc.h> #include <linux/mmc/host.h> #include <linux/mmc/sd.h> #include <linux/mmc/sdio.h> -#include <linux/mmc/mmc.h> -#include <linux/mmc/core.h> -#include <linux/mmc/card.h> #include <linux/mmc/slot-gpio.h> +#include <linux/module.h> +#include <linux/of_address.h> +#include <linux/of_gpio.h> +#include <linux/of_platform.h> +#include <linux/platform_device.h> +#include <linux/regulator/consumer.h> +#include <linux/reset.h> +#include <linux/scatterlist.h> +#include <linux/slab.h> +#include <linux/spinlock.h> /* register offset definitions */ #define SDXC_REG_GCTRL (0x00) /* SMC Global Control Register */ -- git-series 0.9.1 ^ permalink raw reply related [flat|nested] 5+ messages in thread
* [PATCH 2/2] mmc: sunxi: Add runtime_pm support 2017-12-20 10:50 [PATCH 0/2] mmc: sunxi: Add runtime PM support Maxime Ripard 2017-12-20 10:50 ` [PATCH 1/2] mmc: sunxi: Reorder the headers Maxime Ripard @ 2017-12-20 10:50 ` Maxime Ripard 2017-12-21 12:21 ` Ulf Hansson 1 sibling, 1 reply; 5+ messages in thread From: Maxime Ripard @ 2017-12-20 10:50 UTC (permalink / raw) To: linux-arm-kernel So far, even if our card was not in use, we didn't shut down our main clock, which meant that it was still output on the MMC bus. While this obviously means that we could save some power there, it also created issues when it comes with EMC control since we'll have a perfect peak at the card clock rate. Let's implement runtime_pm with autosuspend so that we will shut down the clock when it's not been in use for quite some time. Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com> --- drivers/mmc/host/sunxi-mmc.c | 90 ++++++++++++++++++++++++------------- 1 file changed, 60 insertions(+), 30 deletions(-) diff --git a/drivers/mmc/host/sunxi-mmc.c b/drivers/mmc/host/sunxi-mmc.c index 3ce46ebd3488..c6a0bd0e0476 100644 --- a/drivers/mmc/host/sunxi-mmc.c +++ b/drivers/mmc/host/sunxi-mmc.c @@ -35,6 +35,7 @@ #include <linux/of_gpio.h> #include <linux/of_platform.h> #include <linux/platform_device.h> +#include <linux/pm_runtime.h> #include <linux/regulator/consumer.h> #include <linux/reset.h> #include <linux/scatterlist.h> @@ -1217,29 +1218,11 @@ static int sunxi_mmc_resource_request(struct sunxi_mmc_host *host, return ret; } - ret = clk_prepare_enable(host->clk_mmc); - if (ret) { - dev_err(&pdev->dev, "Enable mmc clk err %d\n", ret); - goto error_disable_clk_ahb; - } - - ret = clk_prepare_enable(host->clk_output); - if (ret) { - dev_err(&pdev->dev, "Enable output clk err %d\n", ret); - goto error_disable_clk_mmc; - } - - ret = clk_prepare_enable(host->clk_sample); - if (ret) { - dev_err(&pdev->dev, "Enable sample clk err %d\n", ret); - goto error_disable_clk_output; - } - if (!IS_ERR(host->reset)) { ret = reset_control_reset(host->reset); if (ret) { dev_err(&pdev->dev, "reset err %d\n", ret); - goto error_disable_clk_sample; + goto error_disable_clk_ahb; } } @@ -1258,12 +1241,6 @@ static int sunxi_mmc_resource_request(struct sunxi_mmc_host *host, error_assert_reset: if (!IS_ERR(host->reset)) reset_control_assert(host->reset); -error_disable_clk_sample: - clk_disable_unprepare(host->clk_sample); -error_disable_clk_output: - clk_disable_unprepare(host->clk_output); -error_disable_clk_mmc: - clk_disable_unprepare(host->clk_mmc); error_disable_clk_ahb: clk_disable_unprepare(host->clk_ahb); return ret; @@ -1280,6 +1257,7 @@ static int sunxi_mmc_probe(struct platform_device *pdev) dev_err(&pdev->dev, "mmc alloc host failed\n"); return -ENOMEM; } + platform_set_drvdata(pdev, mmc); host = mmc_priv(mmc); host->mmc = mmc; @@ -1340,12 +1318,16 @@ static int sunxi_mmc_probe(struct platform_device *pdev) if (ret) goto error_free_dma; + pm_runtime_set_autosuspend_delay(&pdev->dev, 50); + pm_runtime_use_autosuspend(&pdev->dev); + pm_runtime_enable(&pdev->dev); + ret = mmc_add_host(mmc); if (ret) goto error_free_dma; dev_info(&pdev->dev, "base:0x%p irq:%u\n", host->reg_base, host->irq); - platform_set_drvdata(pdev, mmc); + return 0; error_free_dma: @@ -1361,27 +1343,75 @@ static int sunxi_mmc_remove(struct platform_device *pdev) struct sunxi_mmc_host *host = mmc_priv(mmc); mmc_remove_host(mmc); + pm_runtime_force_suspend(&pdev->dev); disable_irq(host->irq); sunxi_mmc_reset_host(host); if (!IS_ERR(host->reset)) reset_control_assert(host->reset); - clk_disable_unprepare(host->clk_sample); + dma_free_coherent(&pdev->dev, PAGE_SIZE, host->sg_cpu, host->sg_dma); + mmc_free_host(mmc); + + return 0; +} + +static int sunxi_mmc_runtime_resume(struct device *dev) +{ + struct mmc_host *mmc = dev_get_drvdata(dev); + struct sunxi_mmc_host *host = mmc_priv(mmc); + int ret; + + ret = clk_prepare_enable(host->clk_mmc); + if (ret) { + dev_err(dev, "Enable mmc clk err %d\n", ret); + return ret; + } + + ret = clk_prepare_enable(host->clk_output); + if (ret) { + dev_err(dev, "Enable output clk err %d\n", ret); + goto error_disable_clk_mmc; + } + + ret = clk_prepare_enable(host->clk_sample); + if (ret) { + dev_err(dev, "Enable sample clk err %d\n", ret); + goto error_disable_clk_output; + } + + return 0; + +error_disable_clk_output: clk_disable_unprepare(host->clk_output); +error_disable_clk_mmc: clk_disable_unprepare(host->clk_mmc); - clk_disable_unprepare(host->clk_ahb); + return ret; +} - dma_free_coherent(&pdev->dev, PAGE_SIZE, host->sg_cpu, host->sg_dma); - mmc_free_host(mmc); +static int sunxi_mmc_runtime_suspend(struct device *dev) +{ + struct mmc_host *mmc = dev_get_drvdata(dev); + struct sunxi_mmc_host *host = mmc_priv(mmc); + + clk_disable_unprepare(host->clk_sample); + clk_disable_unprepare(host->clk_output); + clk_disable_unprepare(host->clk_mmc); return 0; } +static const struct dev_pm_ops sunxi_mmc_pm_ops = { + SET_RUNTIME_PM_OPS(sunxi_mmc_runtime_suspend, + sunxi_mmc_runtime_resume, + NULL) +}; + static struct platform_driver sunxi_mmc_driver = { .driver = { .name = "sunxi-mmc", .of_match_table = of_match_ptr(sunxi_mmc_of_match), + .pm = &sunxi_mmc_pm_ops, }, .probe = sunxi_mmc_probe, .remove = sunxi_mmc_remove, -- git-series 0.9.1 ^ permalink raw reply related [flat|nested] 5+ messages in thread
* [PATCH 2/2] mmc: sunxi: Add runtime_pm support 2017-12-20 10:50 ` [PATCH 2/2] mmc: sunxi: Add runtime_pm support Maxime Ripard @ 2017-12-21 12:21 ` Ulf Hansson 2018-01-04 19:54 ` Maxime Ripard 0 siblings, 1 reply; 5+ messages in thread From: Ulf Hansson @ 2017-12-21 12:21 UTC (permalink / raw) To: linux-arm-kernel On 20 December 2017 at 11:50, Maxime Ripard <maxime.ripard@free-electrons.com> wrote: > So far, even if our card was not in use, we didn't shut down our main > clock, which meant that it was still output on the MMC bus. > > While this obviously means that we could save some power there, it also > created issues when it comes with EMC control since we'll have a perfect > peak at the card clock rate. > > Let's implement runtime_pm with autosuspend so that we will shut down the > clock when it's not been in use for quite some time. > > Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com> > --- > drivers/mmc/host/sunxi-mmc.c | 90 ++++++++++++++++++++++++------------- > 1 file changed, 60 insertions(+), 30 deletions(-) > > diff --git a/drivers/mmc/host/sunxi-mmc.c b/drivers/mmc/host/sunxi-mmc.c > index 3ce46ebd3488..c6a0bd0e0476 100644 > --- a/drivers/mmc/host/sunxi-mmc.c > +++ b/drivers/mmc/host/sunxi-mmc.c > @@ -35,6 +35,7 @@ > #include <linux/of_gpio.h> > #include <linux/of_platform.h> > #include <linux/platform_device.h> > +#include <linux/pm_runtime.h> > #include <linux/regulator/consumer.h> > #include <linux/reset.h> > #include <linux/scatterlist.h> > @@ -1217,29 +1218,11 @@ static int sunxi_mmc_resource_request(struct sunxi_mmc_host *host, > return ret; > } > > - ret = clk_prepare_enable(host->clk_mmc); > - if (ret) { > - dev_err(&pdev->dev, "Enable mmc clk err %d\n", ret); > - goto error_disable_clk_ahb; > - } > - > - ret = clk_prepare_enable(host->clk_output); > - if (ret) { > - dev_err(&pdev->dev, "Enable output clk err %d\n", ret); > - goto error_disable_clk_mmc; > - } > - > - ret = clk_prepare_enable(host->clk_sample); > - if (ret) { > - dev_err(&pdev->dev, "Enable sample clk err %d\n", ret); > - goto error_disable_clk_output; > - } > - Actually, I think you should keep all the above. Perhaps you may want to move it to a separate helper function though, which the ->runtime_resume() callbacks can invoke as well. More reasons to why, see the comment in the ->probe() function. > if (!IS_ERR(host->reset)) { > ret = reset_control_reset(host->reset); > if (ret) { > dev_err(&pdev->dev, "reset err %d\n", ret); > - goto error_disable_clk_sample; > + goto error_disable_clk_ahb; > } > } > > @@ -1258,12 +1241,6 @@ static int sunxi_mmc_resource_request(struct sunxi_mmc_host *host, > error_assert_reset: > if (!IS_ERR(host->reset)) > reset_control_assert(host->reset); > -error_disable_clk_sample: > - clk_disable_unprepare(host->clk_sample); > -error_disable_clk_output: > - clk_disable_unprepare(host->clk_output); > -error_disable_clk_mmc: > - clk_disable_unprepare(host->clk_mmc); > error_disable_clk_ahb: > clk_disable_unprepare(host->clk_ahb); > return ret; > @@ -1280,6 +1257,7 @@ static int sunxi_mmc_probe(struct platform_device *pdev) > dev_err(&pdev->dev, "mmc alloc host failed\n"); > return -ENOMEM; > } > + platform_set_drvdata(pdev, mmc); > > host = mmc_priv(mmc); > host->mmc = mmc; > @@ -1340,12 +1318,16 @@ static int sunxi_mmc_probe(struct platform_device *pdev) > if (ret) > goto error_free_dma; > > + pm_runtime_set_autosuspend_delay(&pdev->dev, 50); > + pm_runtime_use_autosuspend(&pdev->dev); > + pm_runtime_enable(&pdev->dev); > + This means in case you don't have CONFIG_PM set when compiling this driver, the clocks will never be enabled using runtime PM. I think it's good practice to deal with this, therefore I think you should enable the clocks as before, and instead indicate that the device is already runtime resumed. In other words, before you call pm_runtime_enable(), invoke pm_runtime_set_active(). > ret = mmc_add_host(mmc); > if (ret) > goto error_free_dma; > > dev_info(&pdev->dev, "base:0x%p irq:%u\n", host->reg_base, host->irq); > - platform_set_drvdata(pdev, mmc); > + > return 0; > > error_free_dma: > @@ -1361,27 +1343,75 @@ static int sunxi_mmc_remove(struct platform_device *pdev) > struct sunxi_mmc_host *host = mmc_priv(mmc); > > mmc_remove_host(mmc); > + pm_runtime_force_suspend(&pdev->dev); Do you need the clocks to be enabled, while calling disable_irq() and sunxi_mmc_reset_host()? In such case you need to call pm_runtime_get_sync() here. Then move pm_runtime_force_suspend() a few lines below, and later call pm_runtime_put_noidle(). > disable_irq(host->irq); > sunxi_mmc_reset_host(host); > > if (!IS_ERR(host->reset)) > reset_control_assert(host->reset); > > - clk_disable_unprepare(host->clk_sample); > + dma_free_coherent(&pdev->dev, PAGE_SIZE, host->sg_cpu, host->sg_dma); > + mmc_free_host(mmc); > + > + return 0; > +} > + > +static int sunxi_mmc_runtime_resume(struct device *dev) > +{ > + struct mmc_host *mmc = dev_get_drvdata(dev); > + struct sunxi_mmc_host *host = mmc_priv(mmc); > + int ret; > + > + ret = clk_prepare_enable(host->clk_mmc); > + if (ret) { > + dev_err(dev, "Enable mmc clk err %d\n", ret); > + return ret; > + } > + > + ret = clk_prepare_enable(host->clk_output); > + if (ret) { > + dev_err(dev, "Enable output clk err %d\n", ret); > + goto error_disable_clk_mmc; > + } > + > + ret = clk_prepare_enable(host->clk_sample); > + if (ret) { > + dev_err(dev, "Enable sample clk err %d\n", ret); > + goto error_disable_clk_output; > + } > + > + return 0; > + > +error_disable_clk_output: > clk_disable_unprepare(host->clk_output); > +error_disable_clk_mmc: > clk_disable_unprepare(host->clk_mmc); > - clk_disable_unprepare(host->clk_ahb); > + return ret; > +} > > - dma_free_coherent(&pdev->dev, PAGE_SIZE, host->sg_cpu, host->sg_dma); > - mmc_free_host(mmc); > +static int sunxi_mmc_runtime_suspend(struct device *dev) > +{ > + struct mmc_host *mmc = dev_get_drvdata(dev); > + struct sunxi_mmc_host *host = mmc_priv(mmc); > + > + clk_disable_unprepare(host->clk_sample); > + clk_disable_unprepare(host->clk_output); > + clk_disable_unprepare(host->clk_mmc); > > return 0; > } > > +static const struct dev_pm_ops sunxi_mmc_pm_ops = { > + SET_RUNTIME_PM_OPS(sunxi_mmc_runtime_suspend, > + sunxi_mmc_runtime_resume, > + NULL) > +}; > + > static struct platform_driver sunxi_mmc_driver = { > .driver = { > .name = "sunxi-mmc", > .of_match_table = of_match_ptr(sunxi_mmc_of_match), > + .pm = &sunxi_mmc_pm_ops, > }, > .probe = sunxi_mmc_probe, > .remove = sunxi_mmc_remove, > -- > git-series 0.9.1 Otherwise this looks good to me! BTW, isn't system wide suspend/resume() also important to save power for? That's rather simple to implement, after you have enabled runtime PM support. Kind regards Uffe ^ permalink raw reply [flat|nested] 5+ messages in thread
* [PATCH 2/2] mmc: sunxi: Add runtime_pm support 2017-12-21 12:21 ` Ulf Hansson @ 2018-01-04 19:54 ` Maxime Ripard 0 siblings, 0 replies; 5+ messages in thread From: Maxime Ripard @ 2018-01-04 19:54 UTC (permalink / raw) To: linux-arm-kernel Hi Ulf, On Thu, Dec 21, 2017 at 01:21:51PM +0100, Ulf Hansson wrote: > On 20 December 2017 at 11:50, Maxime Ripard > <maxime.ripard@free-electrons.com> wrote: > > So far, even if our card was not in use, we didn't shut down our main > > clock, which meant that it was still output on the MMC bus. > > > > While this obviously means that we could save some power there, it also > > created issues when it comes with EMC control since we'll have a perfect > > peak at the card clock rate. > > > > Let's implement runtime_pm with autosuspend so that we will shut down the > > clock when it's not been in use for quite some time. > > > > Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com> > > --- > > drivers/mmc/host/sunxi-mmc.c | 90 ++++++++++++++++++++++++------------- > > 1 file changed, 60 insertions(+), 30 deletions(-) > > > > diff --git a/drivers/mmc/host/sunxi-mmc.c b/drivers/mmc/host/sunxi-mmc.c > > index 3ce46ebd3488..c6a0bd0e0476 100644 > > --- a/drivers/mmc/host/sunxi-mmc.c > > +++ b/drivers/mmc/host/sunxi-mmc.c > > @@ -35,6 +35,7 @@ > > #include <linux/of_gpio.h> > > #include <linux/of_platform.h> > > #include <linux/platform_device.h> > > +#include <linux/pm_runtime.h> > > #include <linux/regulator/consumer.h> > > #include <linux/reset.h> > > #include <linux/scatterlist.h> > > @@ -1217,29 +1218,11 @@ static int sunxi_mmc_resource_request(struct sunxi_mmc_host *host, > > return ret; > > } > > > > - ret = clk_prepare_enable(host->clk_mmc); > > - if (ret) { > > - dev_err(&pdev->dev, "Enable mmc clk err %d\n", ret); > > - goto error_disable_clk_ahb; > > - } > > - > > - ret = clk_prepare_enable(host->clk_output); > > - if (ret) { > > - dev_err(&pdev->dev, "Enable output clk err %d\n", ret); > > - goto error_disable_clk_mmc; > > - } > > - > > - ret = clk_prepare_enable(host->clk_sample); > > - if (ret) { > > - dev_err(&pdev->dev, "Enable sample clk err %d\n", ret); > > - goto error_disable_clk_output; > > - } > > - > > Actually, I think you should keep all the above. Perhaps you may want > to move it to a separate helper function though, which the > ->runtime_resume() callbacks can invoke as well. > > More reasons to why, see the comment in the ->probe() function. > > > if (!IS_ERR(host->reset)) { > > ret = reset_control_reset(host->reset); > > if (ret) { > > dev_err(&pdev->dev, "reset err %d\n", ret); > > - goto error_disable_clk_sample; > > + goto error_disable_clk_ahb; > > } > > } > > > > @@ -1258,12 +1241,6 @@ static int sunxi_mmc_resource_request(struct sunxi_mmc_host *host, > > error_assert_reset: > > if (!IS_ERR(host->reset)) > > reset_control_assert(host->reset); > > -error_disable_clk_sample: > > - clk_disable_unprepare(host->clk_sample); > > -error_disable_clk_output: > > - clk_disable_unprepare(host->clk_output); > > -error_disable_clk_mmc: > > - clk_disable_unprepare(host->clk_mmc); > > error_disable_clk_ahb: > > clk_disable_unprepare(host->clk_ahb); > > return ret; > > @@ -1280,6 +1257,7 @@ static int sunxi_mmc_probe(struct platform_device *pdev) > > dev_err(&pdev->dev, "mmc alloc host failed\n"); > > return -ENOMEM; > > } > > + platform_set_drvdata(pdev, mmc); > > > > host = mmc_priv(mmc); > > host->mmc = mmc; > > @@ -1340,12 +1318,16 @@ static int sunxi_mmc_probe(struct platform_device *pdev) > > if (ret) > > goto error_free_dma; > > > > + pm_runtime_set_autosuspend_delay(&pdev->dev, 50); > > + pm_runtime_use_autosuspend(&pdev->dev); > > + pm_runtime_enable(&pdev->dev); > > + > > This means in case you don't have CONFIG_PM set when compiling this > driver, the clocks will never be enabled using runtime PM. > > I think it's good practice to deal with this, therefore I think you > should enable the clocks as before, and instead indicate that the > device is already runtime resumed. > > In other words, before you call pm_runtime_enable(), invoke > pm_runtime_set_active(). That's a very good point, I'll do that in the next iteration. > > ret = mmc_add_host(mmc); > > if (ret) > > goto error_free_dma; > > > > dev_info(&pdev->dev, "base:0x%p irq:%u\n", host->reg_base, host->irq); > > - platform_set_drvdata(pdev, mmc); > > + > > return 0; > > > > error_free_dma: > > @@ -1361,27 +1343,75 @@ static int sunxi_mmc_remove(struct platform_device *pdev) > > struct sunxi_mmc_host *host = mmc_priv(mmc); > > > > mmc_remove_host(mmc); > > + pm_runtime_force_suspend(&pdev->dev); > > Do you need the clocks to be enabled, while calling disable_irq() and > sunxi_mmc_reset_host()? > > In such case you need to call pm_runtime_get_sync() here. Then move > pm_runtime_force_suspend() a few lines below, and later call > pm_runtime_put_noidle(). We don't for disable_irq, but we need it for sunxi_mmc_reset_host. I'll do what you suggest. > > disable_irq(host->irq); > > sunxi_mmc_reset_host(host); > > > > if (!IS_ERR(host->reset)) > > reset_control_assert(host->reset); > > > > - clk_disable_unprepare(host->clk_sample); > > + dma_free_coherent(&pdev->dev, PAGE_SIZE, host->sg_cpu, host->sg_dma); > > + mmc_free_host(mmc); > > + > > + return 0; > > +} > > + > > +static int sunxi_mmc_runtime_resume(struct device *dev) > > +{ > > + struct mmc_host *mmc = dev_get_drvdata(dev); > > + struct sunxi_mmc_host *host = mmc_priv(mmc); > > + int ret; > > + > > + ret = clk_prepare_enable(host->clk_mmc); > > + if (ret) { > > + dev_err(dev, "Enable mmc clk err %d\n", ret); > > + return ret; > > + } > > + > > + ret = clk_prepare_enable(host->clk_output); > > + if (ret) { > > + dev_err(dev, "Enable output clk err %d\n", ret); > > + goto error_disable_clk_mmc; > > + } > > + > > + ret = clk_prepare_enable(host->clk_sample); > > + if (ret) { > > + dev_err(dev, "Enable sample clk err %d\n", ret); > > + goto error_disable_clk_output; > > + } > > + > > + return 0; > > + > > +error_disable_clk_output: > > clk_disable_unprepare(host->clk_output); > > +error_disable_clk_mmc: > > clk_disable_unprepare(host->clk_mmc); > > - clk_disable_unprepare(host->clk_ahb); > > + return ret; > > +} > > > > - dma_free_coherent(&pdev->dev, PAGE_SIZE, host->sg_cpu, host->sg_dma); > > - mmc_free_host(mmc); > > +static int sunxi_mmc_runtime_suspend(struct device *dev) > > +{ > > + struct mmc_host *mmc = dev_get_drvdata(dev); > > + struct sunxi_mmc_host *host = mmc_priv(mmc); > > + > > + clk_disable_unprepare(host->clk_sample); > > + clk_disable_unprepare(host->clk_output); > > + clk_disable_unprepare(host->clk_mmc); > > > > return 0; > > } > > > > +static const struct dev_pm_ops sunxi_mmc_pm_ops = { > > + SET_RUNTIME_PM_OPS(sunxi_mmc_runtime_suspend, > > + sunxi_mmc_runtime_resume, > > + NULL) > > +}; > > + > > static struct platform_driver sunxi_mmc_driver = { > > .driver = { > > .name = "sunxi-mmc", > > .of_match_table = of_match_ptr(sunxi_mmc_of_match), > > + .pm = &sunxi_mmc_pm_ops, > > }, > > .probe = sunxi_mmc_probe, > > .remove = sunxi_mmc_remove, > > -- > > git-series 0.9.1 > > Otherwise this looks good to me! > > BTW, isn't system wide suspend/resume() also important to save power > for? That's rather simple to implement, after you have enabled runtime > PM support. We don't have (unfortunately) any kind of suspend support at the moment, so I guess that'll come as a single step when we'll have it :) Thanks! Maxime -- Maxime Ripard, Free Electrons Embedded Linux and Kernel engineering http://free-electrons.com -------------- next part -------------- A non-text attachment was scrubbed... Name: signature.asc Type: application/pgp-signature Size: 833 bytes Desc: not available URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20180104/1e76c357/attachment.sig> ^ permalink raw reply [flat|nested] 5+ messages in thread
end of thread, other threads:[~2018-01-04 19:54 UTC | newest] Thread overview: 5+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- 2017-12-20 10:50 [PATCH 0/2] mmc: sunxi: Add runtime PM support Maxime Ripard 2017-12-20 10:50 ` [PATCH 1/2] mmc: sunxi: Reorder the headers Maxime Ripard 2017-12-20 10:50 ` [PATCH 2/2] mmc: sunxi: Add runtime_pm support Maxime Ripard 2017-12-21 12:21 ` Ulf Hansson 2018-01-04 19:54 ` Maxime Ripard
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox