[PATCH v4 12/13] iommu/rockchip: Add runtime PM support
Tomasz Figa
tfiga at chromium.org
Thu Jan 18 20:01:33 PST 2018
On Thu, Jan 18, 2018 at 8:52 PM, Jeffy Chen <jeffy.chen at rock-chips.com> wrote:
> When the power domain is powered off, the IOMMU cannot be accessed and
> register programming must be deferred until the power domain becomes
> enabled.
>
> Add runtime PM support, and use runtime PM device link from IOMMU to
> master to startup and shutdown IOMMU.
>
> Signed-off-by: Jeffy Chen <jeffy.chen at rock-chips.com>
> ---
>
> Changes in v4: None
> Changes in v3:
> Only call startup() and shutdown() when iommu attached.
> Remove pm_mutex.
> Check runtime PM disabled.
> Check pm_runtime in rk_iommu_irq().
>
> Changes in v2: None
>
> drivers/iommu/rockchip-iommu.c | 180 ++++++++++++++++++++++++++++++++---------
> 1 file changed, 141 insertions(+), 39 deletions(-)
>
> diff --git a/drivers/iommu/rockchip-iommu.c b/drivers/iommu/rockchip-iommu.c
> index 2c095f96c033..e2e7acc3039d 100644
> --- a/drivers/iommu/rockchip-iommu.c
> +++ b/drivers/iommu/rockchip-iommu.c
> @@ -22,6 +22,7 @@
> #include <linux/of_iommu.h>
> #include <linux/of_platform.h>
> #include <linux/platform_device.h>
> +#include <linux/pm_runtime.h>
> #include <linux/slab.h>
> #include <linux/spinlock.h>
>
> @@ -99,6 +100,7 @@ struct rk_iommu {
> };
>
> struct rk_iommudata {
> + struct device_link *link; /* runtime PM link from IOMMU to master */
> struct rk_iommu *iommu;
> };
>
> @@ -583,7 +585,11 @@ static irqreturn_t rk_iommu_irq(int irq, void *dev_id)
> u32 int_status;
> dma_addr_t iova;
> irqreturn_t ret = IRQ_NONE;
> - int i;
> + int i, err;
> +
> + err = pm_runtime_get_if_in_use(iommu->dev);
> + if (err <= 0 && err != -EINVAL)
> + return ret;
>
> WARN_ON(rk_iommu_enable_clocks(iommu));
>
> @@ -635,6 +641,9 @@ static irqreturn_t rk_iommu_irq(int irq, void *dev_id)
>
> rk_iommu_disable_clocks(iommu);
>
> + if (pm_runtime_enabled(iommu->dev))
> + pm_runtime_put(iommu->dev);
I think this might be racy. There are some places where
pm_runtime_enable/disable() are called on devices implicitly and I'm
not sure if we're guaranteed that they don't happen between our
pm_runtime_get_if_in_use() and pm_runtime_enabled() calls.
An example of a race-free solution would be to save the
pm_runtime_get_if_in_use() result to a local variable (e.g. bool
need_runtime_put) and then call pm_runtime_put() based on that.
> +
> return ret;
> }
>
> @@ -676,10 +685,20 @@ static void rk_iommu_zap_iova(struct rk_iommu_domain *rk_domain,
> spin_lock_irqsave(&rk_domain->iommus_lock, flags);
> list_for_each(pos, &rk_domain->iommus) {
> struct rk_iommu *iommu;
> + int ret;
> +
> iommu = list_entry(pos, struct rk_iommu, node);
> - rk_iommu_enable_clocks(iommu);
> - rk_iommu_zap_lines(iommu, iova, size);
> - rk_iommu_disable_clocks(iommu);
> +
> + /* Only zap TLBs of IOMMUs that are powered on. */
> + ret = pm_runtime_get_if_in_use(iommu->dev);
> + if (ret > 0 || ret == -EINVAL) {
> + rk_iommu_enable_clocks(iommu);
> + rk_iommu_zap_lines(iommu, iova, size);
> + rk_iommu_disable_clocks(iommu);
> + }
> +
> + if (ret > 0)
> + pm_runtime_put(iommu->dev);
This one nicely avoids the race I mentioned above. :)
> }
> spin_unlock_irqrestore(&rk_domain->iommus_lock, flags);
> }
> @@ -882,22 +901,30 @@ static struct rk_iommu *rk_iommu_from_dev(struct device *dev)
> return data ? data->iommu : NULL;
> }
>
[snip]
> + spin_lock_irqsave(&rk_domain->iommus_lock, flags);
> + list_add_tail(&iommu->node, &rk_domain->iommus);
> + spin_unlock_irqrestore(&rk_domain->iommus_lock, flags);
>
> - dev_dbg(dev, "Detached from iommu domain\n");
> + ret = pm_runtime_get_if_in_use(iommu->dev);
> + if (ret <= 0 && ret != -EINVAL)
> + return 0;
> +
> + ret = rk_iommu_startup(iommu);
> + if (ret)
> + rk_iommu_detach_device(iommu->domain, dev);
> +
> + if (pm_runtime_enabled(iommu->dev))
> + pm_runtime_put(iommu->dev);
Here we should also probably act based on what
pm_runtime_get_if_in_use() returned rather than asking
pm_runtime_enabled().
Best regards,
Tomasz
More information about the linux-arm-kernel
mailing list