[PATCH 1/2] PM: add runtime PM support to core Primecell driver
Rafael J. Wysocki
rjw at sisk.pl
Tue Aug 23 18:24:56 EDT 2011
On Tuesday, August 23, 2011, Russell King - ARM Linux wrote:
> Add runtime PM support to the core Primecell driver, following the PCI
> model of how this is done.
>
> Rather than having every driver fiddle about with enabling runtime PM,
> that's dealt with in the core and instead, drivers just do a put() in
> their probe and a balancing get() in their remove function to activate
> runtime PM for the device.
>
> As we're dealing with enabling runtime PM in the core, fix up spi-pl022
> as it must not enable and disable runtime PM itself anymore.
>
> Tested-by: Linus Walleij <linus.walleij at linaro.org>
> Signed-off-by: Russell King <rmk+kernel at arm.linux.org.uk>
Acked-by: Rafael J. Wysocki <rjw at sisk.pl>
> ---
> drivers/amba/bus.c | 57 +++++++++++++++++++++++++++++++--
> drivers/spi/spi-pl022.c | 80 ++++++++++++++++++++++++++++-------------------
> 2 files changed, 102 insertions(+), 35 deletions(-)
>
> diff --git a/drivers/amba/bus.c b/drivers/amba/bus.c
> index d74926e..84bdaac 100644
> --- a/drivers/amba/bus.c
> +++ b/drivers/amba/bus.c
> @@ -365,6 +365,40 @@ static int amba_pm_restore_noirq(struct device *dev)
>
> #endif /* !CONFIG_HIBERNATE_CALLBACKS */
>
> +#ifdef CONFIG_PM_RUNTIME
> +/*
> + * Hooks to provide runtime PM of the pclk (bus clock). It is safe to
> + * enable/disable the bus clock at runtime PM suspend/resume as this
> + * does not result in loss of context. However, disabling vcore power
> + * would do, so we leave that to the driver.
> + */
> +static int amba_pm_runtime_suspend(struct device *dev)
> +{
> + struct amba_device *pcdev = to_amba_device(dev);
> + int ret = pm_generic_runtime_suspend(dev);
> +
> + if (ret == 0 && dev->driver)
> + clk_disable(pcdev->pclk);
> +
> + return ret;
> +}
> +
> +static int amba_pm_runtime_resume(struct device *dev)
> +{
> + struct amba_device *pcdev = to_amba_device(dev);
> + int ret;
> +
> + if (dev->driver) {
> + ret = clk_enable(pcdev->pclk);
> + /* Failure is probably fatal to the system, but... */
> + if (ret)
> + return ret;
> + }
> +
> + return pm_generic_runtime_resume(dev);
> +}
> +#endif
> +
> #ifdef CONFIG_PM
>
> static const struct dev_pm_ops amba_pm = {
> @@ -383,8 +417,8 @@ static const struct dev_pm_ops amba_pm = {
> .poweroff_noirq = amba_pm_poweroff_noirq,
> .restore_noirq = amba_pm_restore_noirq,
> SET_RUNTIME_PM_OPS(
> - pm_generic_runtime_suspend,
> - pm_generic_runtime_resume,
> + amba_pm_runtime_suspend,
> + amba_pm_runtime_resume,
> pm_generic_runtime_idle
> )
> };
> @@ -494,10 +528,18 @@ static int amba_probe(struct device *dev)
> if (ret)
> break;
>
> + pm_runtime_get_noresume(dev);
> + pm_runtime_set_active(dev);
> + pm_runtime_enable(dev);
> +
> ret = pcdrv->probe(pcdev, id);
> if (ret == 0)
> break;
>
> + pm_runtime_disable(dev);
> + pm_runtime_set_suspended(dev);
> + pm_runtime_put_noidle(dev);
> +
> amba_put_disable_pclk(pcdev);
> amba_put_disable_vcore(pcdev);
> } while (0);
> @@ -509,7 +551,16 @@ static int amba_remove(struct device *dev)
> {
> struct amba_device *pcdev = to_amba_device(dev);
> struct amba_driver *drv = to_amba_driver(dev->driver);
> - int ret = drv->remove(pcdev);
> + int ret;
> +
> + pm_runtime_get_sync(dev);
> + ret = drv->remove(pcdev);
> + pm_runtime_put_noidle(dev);
> +
> + /* Undo the runtime PM settings in amba_probe() */
> + pm_runtime_disable(dev);
> + pm_runtime_set_suspended(dev);
> + pm_runtime_put_noidle(dev);
>
> amba_put_disable_pclk(pcdev);
> amba_put_disable_vcore(pcdev);
> diff --git a/drivers/spi/spi-pl022.c b/drivers/spi/spi-pl022.c
> index 730b4a3..078338f 100644
> --- a/drivers/spi/spi-pl022.c
> +++ b/drivers/spi/spi-pl022.c
> @@ -515,9 +515,6 @@ static void giveback(struct pl022 *pl022)
> if (msg->complete)
> msg->complete(msg->context);
> /* This message is completed, so let's turn off the clocks & power */
> - clk_disable(pl022->clk);
> - amba_pclk_disable(pl022->adev);
> - amba_vcore_disable(pl022->adev);
> pm_runtime_put(&pl022->adev->dev);
> }
>
> @@ -1545,9 +1542,6 @@ static void pump_messages(struct work_struct *work)
> * (poll/interrupt/DMA)
> */
> pm_runtime_get_sync(&pl022->adev->dev);
> - amba_vcore_enable(pl022->adev);
> - amba_pclk_enable(pl022->adev);
> - clk_enable(pl022->clk);
> restore_state(pl022);
> flush(pl022);
>
> @@ -2186,8 +2180,6 @@ pl022_probe(struct amba_device *adev, const struct amba_id *id)
> }
> printk(KERN_INFO "pl022: mapped registers from 0x%08x to %p\n",
> adev->res.start, pl022->virtbase);
> - pm_runtime_enable(dev);
> - pm_runtime_resume(dev);
>
> pl022->clk = clk_get(&adev->dev, NULL);
> if (IS_ERR(pl022->clk)) {
> @@ -2195,7 +2187,6 @@ pl022_probe(struct amba_device *adev, const struct amba_id *id)
> dev_err(&adev->dev, "could not retrieve SSP/SPI bus clock\n");
> goto err_no_clk;
> }
> -
> /* Disable SSP */
> writew((readw(SSP_CR1(pl022->virtbase)) & (~SSP_CR1_MASK_SSE)),
> SSP_CR1(pl022->virtbase));
> @@ -2235,12 +2226,9 @@ pl022_probe(struct amba_device *adev, const struct amba_id *id)
> goto err_spi_register;
> }
> dev_dbg(dev, "probe succeeded\n");
> - /*
> - * Disable the silicon block pclk and any voltage domain and just
> - * power it up and clock it when it's needed
> - */
> - amba_pclk_disable(adev);
> - amba_vcore_disable(adev);
> +
> + /* let runtime pm put suspend */
> + pm_runtime_put(dev);
> return 0;
>
> err_spi_register:
> @@ -2249,7 +2237,6 @@ pl022_probe(struct amba_device *adev, const struct amba_id *id)
> destroy_queue(pl022);
> pl022_dma_remove(pl022);
> free_irq(adev->irq[0], pl022);
> - pm_runtime_disable(&adev->dev);
> err_no_irq:
> clk_put(pl022->clk);
> err_no_clk:
> @@ -2271,6 +2258,12 @@ pl022_remove(struct amba_device *adev)
> if (!pl022)
> return 0;
>
> + /*
> + * undo pm_runtime_put() in probe. I assume that we're not
> + * accessing the primecell here.
> + */
> + pm_runtime_get_noresume(&adev->dev);
> +
> /* Remove the queue */
> if (destroy_queue(pl022) != 0)
> dev_err(&adev->dev, "queue remove failed\n");
> @@ -2288,10 +2281,10 @@ pl022_remove(struct amba_device *adev)
> return 0;
> }
>
> -#ifdef CONFIG_PM
> -static int pl022_suspend(struct amba_device *adev, pm_message_t state)
> +#ifdef CONFIG_SUSPEND
> +static int pl011_suspend(struct device *dev)
> {
> - struct pl022 *pl022 = amba_get_drvdata(adev);
> + struct pl022 *pl022 = dev_get_drvdata(dev);
> int status = 0;
>
> status = stop_queue(pl022);
> @@ -2300,34 +2293,58 @@ static int pl022_suspend(struct amba_device *adev, pm_message_t state)
> return status;
> }
>
> - amba_vcore_enable(adev);
> - amba_pclk_enable(adev);
> + amba_vcore_enable(pl022->adev);
> + amba_pclk_enable(pl022->adev);
> load_ssp_default_config(pl022);
> - amba_pclk_disable(adev);
> - amba_vcore_disable(adev);
> + amba_pclk_disable(pl022->adev);
> + amba_vcore_disable(pl022->adev);
> dev_dbg(&adev->dev, "suspended\n");
> return 0;
> }
>
> -static int pl022_resume(struct amba_device *adev)
> +static int pl022_resume(struct device *dev)
> {
> - struct pl022 *pl022 = amba_get_drvdata(adev);
> + struct pl022 *pl022 = dev_get_drvdata(dev);
> int status = 0;
>
> /* Start the queue running */
> status = start_queue(pl022);
> if (status)
> - dev_err(&adev->dev, "problem starting queue (%d)\n", status);
> + dev_err(dev, "problem starting queue (%d)\n", status);
> else
> - dev_dbg(&adev->dev, "resumed\n");
> + dev_dbg(dev, "resumed\n");
>
> return status;
> }
> -#else
> -#define pl022_suspend NULL
> -#define pl022_resume NULL
> #endif /* CONFIG_PM */
>
> +#ifdef CONFIG_PM_RUNTIME
> +static int pl022_runtime_suspend(struct device *dev)
> +{
> + struct pl022 *pl022 = dev_get_drvdata(dev);
> +
> + clk_disable(pl022->clk);
> + amba_vcore_disable(pl022->adev);
> +
> + return 0;
> +}
> +
> +static int pl022_runtime_resume(struct device *dev)
> +{
> + struct pl022 *pl022 = dev_get_drvdata(dev);
> +
> + amba_vcore_enable(pl022->adev);
> + clk_enable(pl022->clk);
> +
> + return 0;
> +}
> +#endif
> +
> +static const struct dev_pm_ops pl022_dev_pm_ops = {
> + SET_SYSTEM_SLEEP_PM_OPS(pl022_suspend, pl022_resume)
> + SET_RUNTIME_PM_OPS(pl022_runtime_suspend, pl022_runtime_resume, NULL)
> +};
> +
> static struct vendor_data vendor_arm = {
> .fifodepth = 8,
> .max_bpw = 16,
> @@ -2407,12 +2424,11 @@ static struct amba_id pl022_ids[] = {
> static struct amba_driver pl022_driver = {
> .drv = {
> .name = "ssp-pl022",
> + .pm = &pl022_dev_pm_ops,
> },
> .id_table = pl022_ids,
> .probe = pl022_probe,
> .remove = __devexit_p(pl022_remove),
> - .suspend = pl022_suspend,
> - .resume = pl022_resume,
> };
>
>
>
More information about the linux-arm-kernel
mailing list