[PATCH v2 09/12] iommu/exynos: add supoort for runtime pm and suspend/resume
Prathyush K
prathyush at chromium.org
Tue Nov 20 03:03:11 EST 2012
On Tue, Nov 20, 2012 at 1:00 PM, Cho KyongHo <pullip.cho at samsung.com> wrote:
> This change enables the client device drivers not to care about
> the state of System MMU since the internal state of System MMU
> is controlled by the runtime PM and suspend/resume callback functions.
>
> Signed-off-by: KyongHo Cho <pullip.cho at samsung.com>
> ---
> drivers/iommu/exynos-iommu.c | 175
> ++++++++++++++++++++++---------------------
> 1 file changed, 89 insertions(+), 86 deletions(-)
>
> diff --git a/drivers/iommu/exynos-iommu.c b/drivers/iommu/exynos-iommu.c
> index 8f9239c..169f56e 100644
> --- a/drivers/iommu/exynos-iommu.c
> +++ b/drivers/iommu/exynos-iommu.c
> @@ -208,6 +208,7 @@ struct sysmmu_drvdata {
> struct iommu_domain *domain;
> sysmmu_fault_handler_t fault_handler;
> unsigned long pgtable;
> + bool runtime_active;
> void __iomem *sfrbases[0];
> };
>
> @@ -477,7 +478,8 @@ static bool __sysmmu_disable(struct sysmmu_drvdata
> *drvdata)
> drvdata->pgtable = 0;
> drvdata->domain = NULL;
>
> - __sysmmu_disable_nocount(drvdata);
> + if (drvdata->runtime_active)
> + __sysmmu_disable_nocount(drvdata);
>
> dev_dbg(drvdata->sysmmu, "Disabled\n");
> } else {
> @@ -490,30 +492,6 @@ static bool __sysmmu_disable(struct sysmmu_drvdata
> *drvdata)
> return disabled;
> }
>
> -static bool __exynos_sysmmu_disable(struct device *dev)
> -{
> - unsigned long flags;
> - bool disabled = true;
> - struct exynos_iommu_owner *owner = dev->archdata.iommu;
> - struct device *sysmmu;
> -
> - BUG_ON(!has_sysmmu(dev));
> -
> - spin_lock_irqsave(&owner->lock, flags);
> -
> - /* Every call to __sysmmu_disable() must return same result */
> - for_each_sysmmu(dev, sysmmu) {
> - struct sysmmu_drvdata *drvdata = dev_get_drvdata(sysmmu);
> - disabled = __sysmmu_disable(drvdata);
> - if (disabled)
> - drvdata->master = NULL;
> - }
> -
> - spin_unlock_irqrestore(&owner->lock, flags);
> -
> - return disabled;
> -}
> -
> static void __sysmmu_enable_nocount(struct sysmmu_drvdata *drvdata)
> {
> int i;
> @@ -554,7 +532,8 @@ static int __sysmmu_enable(struct sysmmu_drvdata
> *drvdata,
> drvdata->pgtable = pgtable;
> drvdata->domain = domain;
>
> - __sysmmu_enable_nocount(drvdata);
> + if (drvdata->runtime_active)
> + __sysmmu_enable_nocount(drvdata);
>
> dev_dbg(drvdata->sysmmu, "Enabled\n");
> } else {
> @@ -610,42 +589,31 @@ static int __exynos_sysmmu_enable(struct device
> *dev, unsigned long pgtable,
>
> int exynos_sysmmu_enable(struct device *dev, unsigned long pgtable)
> {
> - int ret;
> - struct device *sysmmu;
> -
> BUG_ON(!memblock_is_memory(pgtable));
>
> - for_each_sysmmu(dev, sysmmu) {
> - ret = pm_runtime_get_sync(sysmmu);
> - if (ret < 0)
> - break;
> - }
> -
> - if (ret < 0) {
> - struct device *start;
> - for_each_sysmmu_until(dev, start, sysmmu)
> - pm_runtime_put(start);
> -
> - return ret;
> - }
> -
> - ret = __exynos_sysmmu_enable(dev, pgtable, NULL);
> - if (ret < 0)
> - for_each_sysmmu(dev, sysmmu)
> - pm_runtime_put(sysmmu);
> -
> - return ret;
> + return __exynos_sysmmu_enable(dev, pgtable, NULL);
> }
>
> bool exynos_sysmmu_disable(struct device *dev)
> {
> - bool disabled;
> + unsigned long flags;
> + bool disabled = true;
> + struct exynos_iommu_owner *owner = dev->archdata.iommu;
> struct device *sysmmu;
>
> - disabled = __exynos_sysmmu_disable(dev);
> + BUG_ON(!has_sysmmu(dev));
>
> - for_each_sysmmu(dev, sysmmu)
> - pm_runtime_put(sysmmu);
> + spin_lock_irqsave(&owner->lock, flags);
> +
> + /* Every call to __sysmmu_disable() must return same result */
> + for_each_sysmmu(dev, sysmmu) {
> + struct sysmmu_drvdata *drvdata = dev_get_drvdata(sysmmu);
> + disabled = __sysmmu_disable(drvdata);
> + if (disabled)
> + drvdata->master = NULL;
> + }
> +
> + spin_unlock_irqrestore(&owner->lock, flags);
>
> return disabled;
> }
> @@ -661,7 +629,8 @@ static void sysmmu_tlb_invalidate_entry(struct device
> *dev, unsigned long iova)
> data = dev_get_drvdata(sysmmu);
>
> spin_lock_irqsave(&data->lock, flags);
> - if (is_sysmmu_active(data)) {
> + if (is_sysmmu_active(data) &&
> + data->runtime_active) {
> int i;
> for (i = 0; i < data->nsfrs; i++) {
> if (sysmmu_block(data->sfrbases[i])) {
> @@ -899,6 +868,7 @@ static int __init exynos_sysmmu_probe(struct
> platform_device *pdev)
>
> ret = __sysmmu_setup(dev, data);
> if (!ret) {
> + data->runtime_active = !pm_runtime_enabled(dev);
> data->sysmmu = dev;
> spin_lock_init(&data->lock);
>
> @@ -911,6 +881,64 @@ static int __init exynos_sysmmu_probe(struct
> platform_device *pdev)
> return ret;
> }
>
> +#ifdef CONFIG_PM_SLEEP
> +static int sysmmu_suspend(struct device *dev)
> +{
> + struct sysmmu_drvdata *drvdata = dev_get_drvdata(dev);
> + unsigned long flags;
> + spin_lock_irqsave(&drvdata->lock, flags);
> + if (is_sysmmu_active(drvdata) &&
> + (!pm_runtime_enabled(dev) || drvdata->runtime_active))
> + __sysmmu_disable_nocount(drvdata);
> + spin_unlock_irqrestore(&drvdata->lock, flags);
> + return 0;
> +}
> +
> +static int sysmmu_resume(struct device *dev)
> +{
> + struct sysmmu_drvdata *drvdata = dev_get_drvdata(dev);
> + unsigned long flags;
> + spin_lock_irqsave(&drvdata->lock, flags);
> + if (is_sysmmu_active(drvdata) &&
> + (!pm_runtime_enabled(dev) || drvdata->runtime_active)) {
> + __sysmmu_enable_nocount(drvdata);
> + }
> + spin_unlock_irqrestore(&drvdata->lock, flags);
> + return 0;
> +}
> +#endif
> +
> +#ifdef CONFIG_PM_RUNTIME
> +static int sysmmu_runtime_suspend(struct device *dev)
> +{
> + struct sysmmu_drvdata *drvdata = dev_get_drvdata(dev);
> + unsigned long flags;
> + spin_lock_irqsave(&drvdata->lock, flags);
> + if (is_sysmmu_active(drvdata))
> + __sysmmu_disable_nocount(drvdata);
> + drvdata->runtime_active = false;
> + spin_unlock_irqrestore(&drvdata->lock, flags);
> + return 0;
> +}
> +
> +static int sysmmu_runtime_resume(struct device *dev)
> +{
> + struct sysmmu_drvdata *drvdata = dev_get_drvdata(dev);
> + unsigned long flags;
> + spin_lock_irqsave(&drvdata->lock, flags);
> + drvdata->runtime_active = true;
> + if (is_sysmmu_active(drvdata))
> + __sysmmu_enable_nocount(drvdata);
> + spin_unlock_irqrestore(&drvdata->lock, flags);
> + return 0;
> +}
> +#endif
> +
> +static const struct dev_pm_ops __pm_ops = {
> + SET_SYSTEM_SLEEP_PM_OPS(sysmmu_suspend, sysmmu_resume)
> + SET_RUNTIME_PM_OPS(sysmmu_runtime_resume, sysmmu_runtime_suspend,
> NULL)
>
The runtime PM ops are not set correctly. The suspend_fn should be first,
followed
by resume_fn as per SET_RUNTIME_PM_OPS.
Thanks,
Prathyush
> +};
> +
> /*
> * Descriptions of Device Tree node for System MMU
> *
> @@ -948,6 +976,7 @@ static struct platform_driver exynos_sysmmu_driver
> __refdata = {
> .driver = {
> .owner = THIS_MODULE,
> .name = "exynos-sysmmu",
> + .pm = &__pm_ops,
> .of_match_table = of_match_ptr(sysmmu_of_match),
> }
> };
> @@ -1009,13 +1038,9 @@ static void exynos_iommu_domain_destroy(struct
> iommu_domain *domain)
> spin_lock_irqsave(&priv->lock, flags);
>
> list_for_each_entry_safe(owner, n, &priv->clients, client) {
> - struct device *sysmmu;
> - while (!__exynos_sysmmu_disable(owner->dev))
> + while (!exynos_sysmmu_disable(owner->dev))
> ; /* until System MMU is actually disabled */
> list_del_init(&owner->client);
> -
> - for_each_sysmmu(owner->dev, sysmmu)
> - pm_runtime_put(sysmmu);
> }
>
> spin_unlock_irqrestore(&priv->lock, flags);
> @@ -1038,7 +1063,6 @@ static int exynos_iommu_attach_device(struct
> iommu_domain *domain,
> struct exynos_iommu_domain *priv = domain->priv;
> unsigned long flags;
> int ret;
> - struct device *sysmmu;
>
> if (WARN_ON(!list_empty(&owner->client))) {
> bool found = false;
> @@ -1063,20 +1087,6 @@ static int exynos_iommu_attach_device(struct
> iommu_domain *domain,
> return 0;
> }
>
> - for_each_sysmmu(dev, sysmmu) {
> - ret = pm_runtime_get_sync(sysmmu);
> - if (ret < 0)
> - break;
> - }
> -
> - if (ret < 0) {
> - struct device *start;
> - for_each_sysmmu_until(dev, start, sysmmu)
> - pm_runtime_put(start);
> -
> - return ret;
> - }
> -
> spin_lock_irqsave(&priv->lock, flags);
>
> ret = __exynos_sysmmu_enable(dev, __pa(priv->pgtable), domain);
> @@ -1091,15 +1101,12 @@ static int exynos_iommu_attach_device(struct
> iommu_domain *domain,
>
> spin_unlock_irqrestore(&priv->lock, flags);
>
> - if (ret < 0) {
> + if (ret < 0)
> dev_err(dev, "%s: Failed to attach IOMMU with pgtable
> %#lx\n",
> __func__, __pa(priv->pgtable));
> - for_each_sysmmu(dev, sysmmu)
> - pm_runtime_put(sysmmu);
> - } else {
> + else
> dev_dbg(dev, "%s: Attached new IOMMU with pgtable 0x%lx\n",
> __func__, __pa(priv->pgtable));
> - }
>
> return ret;
> }
> @@ -1115,7 +1122,7 @@ static void exynos_iommu_detach_device(struct
> iommu_domain *domain,
>
> list_for_each_entry_safe(owner, n, &priv->clients, client) {
> if (owner == dev->archdata.iommu) {
> - if (__exynos_sysmmu_disable(dev))
> + if (exynos_sysmmu_disable(dev))
> list_del_init(&owner->client);
> else
> BUG();
> @@ -1125,14 +1132,10 @@ static void exynos_iommu_detach_device(struct
> iommu_domain *domain,
>
> spin_unlock_irqrestore(&priv->lock, flags);
>
> - if (owner == dev->archdata.iommu) {
> - struct device *sysmmu;
> + if (owner == dev->archdata.iommu)
> dev_dbg(dev, "%s: Detached IOMMU with pgtable %#lx\n",
> __func__, __pa(priv->pgtable));
> - for_each_sysmmu(dev, sysmmu)
> - pm_runtime_put(sysmmu);
> -
> - } else
> + else
> dev_dbg(dev, "%s: No IOMMU is attached\n", __func__);
> }
>
> --
> 1.8.0
>
>
> _______________________________________________
> iommu mailing list
> iommu at lists.linux-foundation.org
> https://lists.linuxfoundation.org/mailman/listinfo/iommu
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20121120/f9c21bc9/attachment-0001.html>
More information about the linux-arm-kernel
mailing list