diff mbox series

[11/21] iommu/mediatek: Add power-domain operation

Message ID 20200711064846.16007-12-yong.wu@mediatek.com (mailing list archive)
State New, archived
Headers show
Series MT8192 IOMMU support | expand

Commit Message

Yong Wu (吴勇) July 11, 2020, 6:48 a.m. UTC
In the previous SoC, the M4U HW is in the EMI power domain which is
always on. the latest M4U is in the display power domain which may be
turned on/off, thus we have to add pm_runtime interface for it.

we should enable its power before M4U hw initial. and disable it after HW
initialize.

When the engine work, the engine always enable the power and clocks for
smi-larb/smi-common, then the M4U's power will always be powered on
automatically via the device link with smi-common.

Note: we don't enable the M4U power in iommu_map/unmap for tlb flush.
If its power already is on, of course it is ok. if the power is off,
the main tlb will be reset while M4U power on, thus the tlb flush while
m4u power off is unnecessary, just skip it.

Signed-off-by: Yong Wu <yong.wu@mediatek.com>
---
 drivers/iommu/mtk_iommu.c | 54 ++++++++++++++++++++++++++++++++++-----
 1 file changed, 47 insertions(+), 7 deletions(-)

Comments

Pi-Hsun Shih July 13, 2020, 7:03 a.m. UTC | #1
On Sat, Jul 11, 2020 at 2:51 PM Yong Wu <yong.wu@mediatek.com> wrote:
>
> In the previous SoC, the M4U HW is in the EMI power domain which is
> always on. the latest M4U is in the display power domain which may be
> turned on/off, thus we have to add pm_runtime interface for it.
>
> we should enable its power before M4U hw initial. and disable it after HW
> initialize.
>
> When the engine work, the engine always enable the power and clocks for
> smi-larb/smi-common, then the M4U's power will always be powered on
> automatically via the device link with smi-common.
>
> Note: we don't enable the M4U power in iommu_map/unmap for tlb flush.
> If its power already is on, of course it is ok. if the power is off,
> the main tlb will be reset while M4U power on, thus the tlb flush while
> m4u power off is unnecessary, just skip it.
>
> Signed-off-by: Yong Wu <yong.wu@mediatek.com>
> ---
>  drivers/iommu/mtk_iommu.c | 54 ++++++++++++++++++++++++++++++++++-----
>  1 file changed, 47 insertions(+), 7 deletions(-)
> ...
>         for_each_m4u(data) {
> +               /* skip tlb flush when pm is not active */
> +               if (pm_runtime_enabled(data->dev) &&
> +                   !pm_runtime_active(data->dev))
> +                       continue;
> +

pm_runtime_active(dev) == false implies dev->power.disable_depth == 0,
which implies pm_runtime_enabled(dev) == true, so the
pm_runtime_enabled(data->dev) can be omitted here.

>                 spin_lock_irqsave(&data->tlb_lock, flags);
>                 writel_relaxed(F_INVLD_EN1 | F_INVLD_EN0,
>                                data->base + data->plat_data->inv_sel_reg);
> ...
Yong Wu (吴勇) July 14, 2020, 9:33 a.m. UTC | #2
On Mon, 2020-07-13 at 15:03 +0800, Pi-Hsun Shih wrote:
> On Sat, Jul 11, 2020 at 2:51 PM Yong Wu <yong.wu@mediatek.com> wrote:
> >
> > In the previous SoC, the M4U HW is in the EMI power domain which is
> > always on. the latest M4U is in the display power domain which may be
> > turned on/off, thus we have to add pm_runtime interface for it.
> >
> > we should enable its power before M4U hw initial. and disable it after HW
> > initialize.
> >
> > When the engine work, the engine always enable the power and clocks for
> > smi-larb/smi-common, then the M4U's power will always be powered on
> > automatically via the device link with smi-common.
> >
> > Note: we don't enable the M4U power in iommu_map/unmap for tlb flush.
> > If its power already is on, of course it is ok. if the power is off,
> > the main tlb will be reset while M4U power on, thus the tlb flush while
> > m4u power off is unnecessary, just skip it.
> >
> > Signed-off-by: Yong Wu <yong.wu@mediatek.com>
> > ---
> >  drivers/iommu/mtk_iommu.c | 54 ++++++++++++++++++++++++++++++++++-----
> >  1 file changed, 47 insertions(+), 7 deletions(-)
> > ...
> >         for_each_m4u(data) {
> > +               /* skip tlb flush when pm is not active */
> > +               if (pm_runtime_enabled(data->dev) &&
> > +                   !pm_runtime_active(data->dev))
> > +                       continue;
> > +
> 
> pm_runtime_active(dev) == false implies dev->power.disable_depth == 0,
> which implies pm_runtime_enabled(dev) == true, so the
> pm_runtime_enabled(data->dev) can be omitted here.

Yes. Thanks.
Will fix in next version.

> 
> >                 spin_lock_irqsave(&data->tlb_lock, flags);
> >                 writel_relaxed(F_INVLD_EN1 | F_INVLD_EN0,
> >                                data->base + data->plat_data->inv_sel_reg);
> > ...
chao hao July 27, 2020, 8:49 a.m. UTC | #3
On Sat, 2020-07-11 at 14:48 +0800, Yong Wu wrote:
> In the previous SoC, the M4U HW is in the EMI power domain which is
> always on. the latest M4U is in the display power domain which may be
> turned on/off, thus we have to add pm_runtime interface for it.
> 
> we should enable its power before M4U hw initial. and disable it after HW
> initialize.
> 
> When the engine work, the engine always enable the power and clocks for
> smi-larb/smi-common, then the M4U's power will always be powered on
> automatically via the device link with smi-common.
> 
> Note: we don't enable the M4U power in iommu_map/unmap for tlb flush.
> If its power already is on, of course it is ok. if the power is off,
> the main tlb will be reset while M4U power on, thus the tlb flush while
> m4u power off is unnecessary, just skip it.
> 
> Signed-off-by: Yong Wu <yong.wu@mediatek.com>
> ---
>  drivers/iommu/mtk_iommu.c | 54 ++++++++++++++++++++++++++++++++++-----
>  1 file changed, 47 insertions(+), 7 deletions(-)
> 
> diff --git a/drivers/iommu/mtk_iommu.c b/drivers/iommu/mtk_iommu.c
> index 931fdd19c8f3..03a6d66f4bef 100644
> --- a/drivers/iommu/mtk_iommu.c
> +++ b/drivers/iommu/mtk_iommu.c
> @@ -20,6 +20,7 @@
>  #include <linux/of_irq.h>
>  #include <linux/of_platform.h>
>  #include <linux/platform_device.h>
> +#include <linux/pm_runtime.h>
>  #include <linux/slab.h>
>  #include <linux/spinlock.h>
>  #include <asm/barrier.h>
> @@ -172,6 +173,19 @@ static struct mtk_iommu_domain *to_mtk_domain(struct iommu_domain *dom)
>  	return container_of(dom, struct mtk_iommu_domain, domain);
>  }
>  
> +static int mtk_iommu_rpm_get(struct device *dev)
> +{
> +	if (pm_runtime_enabled(dev))
> +		return pm_runtime_get_sync(dev);
> +	return 0;
> +}
> +
> +static void mtk_iommu_rpm_put(struct device *dev)
> +{
> +	if (pm_runtime_enabled(dev))
> +		pm_runtime_put_autosuspend(dev);
> +}
> +
>  static void mtk_iommu_tlb_flush_all(void *cookie)
>  {
>  	struct mtk_iommu_data *data = cookie;
> @@ -193,6 +207,11 @@ static void mtk_iommu_tlb_flush_range_sync(unsigned long iova, size_t size,
>  	u32 tmp;
>  
>  	for_each_m4u(data) {
> +		/* skip tlb flush when pm is not active */
> +		if (pm_runtime_enabled(data->dev) &&
> +		    !pm_runtime_active(data->dev))
> +			continue;
> +
>  		spin_lock_irqsave(&data->tlb_lock, flags);
>  		writel_relaxed(F_INVLD_EN1 | F_INVLD_EN0,
>  			       data->base + data->plat_data->inv_sel_reg);
> @@ -377,15 +396,20 @@ static int mtk_iommu_attach_device(struct iommu_domain *domain,
>  {
>  	struct mtk_iommu_data *data = dev_iommu_priv_get(dev);
>  	struct mtk_iommu_domain *dom = to_mtk_domain(domain);
> +	int ret;
>  
>  	if (!data)
>  		return -ENODEV;
>  
>  	/* Update the pgtable base address register of the M4U HW */
>  	if (!data->m4u_dom) {
> +		ret = mtk_iommu_rpm_get(dev);
> +		if (ret < 0)
> +			return ret;
>  		data->m4u_dom = dom;
>  		writel(dom->cfg.arm_v7s_cfg.ttbr & MMU_PT_ADDR_MASK,
>  		       data->base + REG_MMU_PT_BASE_ADDR);
> +		mtk_iommu_rpm_put(dev);
>  	}
>  
>  	mtk_iommu_config(data, dev, true);
> @@ -543,10 +567,14 @@ static int mtk_iommu_hw_init(const struct mtk_iommu_data *data)
>  	u32 regval;
>  	int ret;
>  
> -	ret = clk_prepare_enable(data->bclk);
> -	if (ret) {
> -		dev_err(data->dev, "Failed to enable iommu bclk(%d)\n", ret);
> -		return ret;
> +	/* bclk will be enabled in pm callback in power-domain case. */
> +	if (!pm_runtime_enabled(data->dev)) {
> +		ret = clk_prepare_enable(data->bclk);
> +		if (ret) {
> +			dev_err(data->dev, "Failed to enable iommu bclk(%d)\n",
> +				ret);
> +			return ret;
> +		}
>  	}
>  
>  	if (data->plat_data->m4u_plat == M4U_MT8173) {
> @@ -728,7 +756,15 @@ static int mtk_iommu_probe(struct platform_device *pdev)
>  
>  	platform_set_drvdata(pdev, data);
>  
> +	if (dev->pm_domain)
> +		pm_runtime_enable(dev);

hi yong,

If you put "pm_runtime_enable" here, it maybe not device_link with
smi_common for previous patch: 
if(i || !pm_runtime_enabled(dev))
    continue;

Whether put it up front?

best regards,
chao

> +
> +	ret = mtk_iommu_rpm_get(dev);
> +	if (ret < 0)
> +		return ret;
> +
>  	ret = mtk_iommu_hw_init(data);
> +	mtk_iommu_rpm_put(dev);
>  	if (ret)
>  		return ret;
>  
> @@ -801,6 +837,10 @@ static int __maybe_unused mtk_iommu_resume(struct device *dev)
>  		dev_err(data->dev, "Failed to enable clk(%d) in resume\n", ret);
>  		return ret;
>  	}
> +
> +	/* Avoid first resume to affect the default value of registers below. */
> +	if (!m4u_dom)
> +		return 0;
>  	writel_relaxed(reg->wr_len_ctrl, base + REG_MMU_WR_LEN_CTRL);
>  	writel_relaxed(reg->misc_ctrl, base + REG_MMU_MISC_CTRL);
>  	writel_relaxed(reg->dcm_dis, base + REG_MMU_DCM_DIS);
> @@ -809,13 +849,13 @@ static int __maybe_unused mtk_iommu_resume(struct device *dev)
>  	writel_relaxed(reg->int_main_control, base + REG_MMU_INT_MAIN_CONTROL);
>  	writel_relaxed(reg->ivrp_paddr, base + REG_MMU_IVRP_PADDR);
>  	writel_relaxed(reg->vld_pa_rng, base + REG_MMU_VLD_PA_RNG);
> -	if (m4u_dom)
> -		writel(m4u_dom->cfg.arm_v7s_cfg.ttbr & MMU_PT_ADDR_MASK,
> -		       base + REG_MMU_PT_BASE_ADDR);
> +	writel(m4u_dom->cfg.arm_v7s_cfg.ttbr & MMU_PT_ADDR_MASK,
> +	       base + REG_MMU_PT_BASE_ADDR);
>  	return 0;
>  }
>  
>  static const struct dev_pm_ops mtk_iommu_pm_ops = {
> +	SET_RUNTIME_PM_OPS(mtk_iommu_suspend, mtk_iommu_resume, NULL)
>  	SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(mtk_iommu_suspend, mtk_iommu_resume)
>  };
>
Yong Wu (吴勇) Aug. 7, 2020, 2:13 a.m. UTC | #4
On Mon, 2020-07-27 at 16:49 +0800, chao hao wrote:
> On Sat, 2020-07-11 at 14:48 +0800, Yong Wu wrote:
> > In the previous SoC, the M4U HW is in the EMI power domain which is
> > always on. the latest M4U is in the display power domain which may be
> > turned on/off, thus we have to add pm_runtime interface for it.
> > 
> > we should enable its power before M4U hw initial. and disable it after HW
> > initialize.
> > 
> > When the engine work, the engine always enable the power and clocks for
> > smi-larb/smi-common, then the M4U's power will always be powered on
> > automatically via the device link with smi-common.
> > 
> > Note: we don't enable the M4U power in iommu_map/unmap for tlb flush.
> > If its power already is on, of course it is ok. if the power is off,
> > the main tlb will be reset while M4U power on, thus the tlb flush while
> > m4u power off is unnecessary, just skip it.
> > 
> > Signed-off-by: Yong Wu <yong.wu@mediatek.com>

...

> >  
> >  	if (data->plat_data->m4u_plat == M4U_MT8173) {
> > @@ -728,7 +756,15 @@ static int mtk_iommu_probe(struct platform_device *pdev)
> >  
> >  	platform_set_drvdata(pdev, data);
> >  
> > +	if (dev->pm_domain)
> > +		pm_runtime_enable(dev);
> 
> hi yong,
> 
> If you put "pm_runtime_enable" here, it maybe not device_link with
> smi_common for previous patch: 
> if(i || !pm_runtime_enabled(dev))
>     continue;
> 
> Whether put it up front?

Thanks for review. My fault here. I will fix it.

> 
> best regards,
> chao
diff mbox series

Patch

diff --git a/drivers/iommu/mtk_iommu.c b/drivers/iommu/mtk_iommu.c
index 931fdd19c8f3..03a6d66f4bef 100644
--- a/drivers/iommu/mtk_iommu.c
+++ b/drivers/iommu/mtk_iommu.c
@@ -20,6 +20,7 @@ 
 #include <linux/of_irq.h>
 #include <linux/of_platform.h>
 #include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
 #include <linux/slab.h>
 #include <linux/spinlock.h>
 #include <asm/barrier.h>
@@ -172,6 +173,19 @@  static struct mtk_iommu_domain *to_mtk_domain(struct iommu_domain *dom)
 	return container_of(dom, struct mtk_iommu_domain, domain);
 }
 
+static int mtk_iommu_rpm_get(struct device *dev)
+{
+	if (pm_runtime_enabled(dev))
+		return pm_runtime_get_sync(dev);
+	return 0;
+}
+
+static void mtk_iommu_rpm_put(struct device *dev)
+{
+	if (pm_runtime_enabled(dev))
+		pm_runtime_put_autosuspend(dev);
+}
+
 static void mtk_iommu_tlb_flush_all(void *cookie)
 {
 	struct mtk_iommu_data *data = cookie;
@@ -193,6 +207,11 @@  static void mtk_iommu_tlb_flush_range_sync(unsigned long iova, size_t size,
 	u32 tmp;
 
 	for_each_m4u(data) {
+		/* skip tlb flush when pm is not active */
+		if (pm_runtime_enabled(data->dev) &&
+		    !pm_runtime_active(data->dev))
+			continue;
+
 		spin_lock_irqsave(&data->tlb_lock, flags);
 		writel_relaxed(F_INVLD_EN1 | F_INVLD_EN0,
 			       data->base + data->plat_data->inv_sel_reg);
@@ -377,15 +396,20 @@  static int mtk_iommu_attach_device(struct iommu_domain *domain,
 {
 	struct mtk_iommu_data *data = dev_iommu_priv_get(dev);
 	struct mtk_iommu_domain *dom = to_mtk_domain(domain);
+	int ret;
 
 	if (!data)
 		return -ENODEV;
 
 	/* Update the pgtable base address register of the M4U HW */
 	if (!data->m4u_dom) {
+		ret = mtk_iommu_rpm_get(dev);
+		if (ret < 0)
+			return ret;
 		data->m4u_dom = dom;
 		writel(dom->cfg.arm_v7s_cfg.ttbr & MMU_PT_ADDR_MASK,
 		       data->base + REG_MMU_PT_BASE_ADDR);
+		mtk_iommu_rpm_put(dev);
 	}
 
 	mtk_iommu_config(data, dev, true);
@@ -543,10 +567,14 @@  static int mtk_iommu_hw_init(const struct mtk_iommu_data *data)
 	u32 regval;
 	int ret;
 
-	ret = clk_prepare_enable(data->bclk);
-	if (ret) {
-		dev_err(data->dev, "Failed to enable iommu bclk(%d)\n", ret);
-		return ret;
+	/* bclk will be enabled in pm callback in power-domain case. */
+	if (!pm_runtime_enabled(data->dev)) {
+		ret = clk_prepare_enable(data->bclk);
+		if (ret) {
+			dev_err(data->dev, "Failed to enable iommu bclk(%d)\n",
+				ret);
+			return ret;
+		}
 	}
 
 	if (data->plat_data->m4u_plat == M4U_MT8173) {
@@ -728,7 +756,15 @@  static int mtk_iommu_probe(struct platform_device *pdev)
 
 	platform_set_drvdata(pdev, data);
 
+	if (dev->pm_domain)
+		pm_runtime_enable(dev);
+
+	ret = mtk_iommu_rpm_get(dev);
+	if (ret < 0)
+		return ret;
+
 	ret = mtk_iommu_hw_init(data);
+	mtk_iommu_rpm_put(dev);
 	if (ret)
 		return ret;
 
@@ -801,6 +837,10 @@  static int __maybe_unused mtk_iommu_resume(struct device *dev)
 		dev_err(data->dev, "Failed to enable clk(%d) in resume\n", ret);
 		return ret;
 	}
+
+	/* Avoid first resume to affect the default value of registers below. */
+	if (!m4u_dom)
+		return 0;
 	writel_relaxed(reg->wr_len_ctrl, base + REG_MMU_WR_LEN_CTRL);
 	writel_relaxed(reg->misc_ctrl, base + REG_MMU_MISC_CTRL);
 	writel_relaxed(reg->dcm_dis, base + REG_MMU_DCM_DIS);
@@ -809,13 +849,13 @@  static int __maybe_unused mtk_iommu_resume(struct device *dev)
 	writel_relaxed(reg->int_main_control, base + REG_MMU_INT_MAIN_CONTROL);
 	writel_relaxed(reg->ivrp_paddr, base + REG_MMU_IVRP_PADDR);
 	writel_relaxed(reg->vld_pa_rng, base + REG_MMU_VLD_PA_RNG);
-	if (m4u_dom)
-		writel(m4u_dom->cfg.arm_v7s_cfg.ttbr & MMU_PT_ADDR_MASK,
-		       base + REG_MMU_PT_BASE_ADDR);
+	writel(m4u_dom->cfg.arm_v7s_cfg.ttbr & MMU_PT_ADDR_MASK,
+	       base + REG_MMU_PT_BASE_ADDR);
 	return 0;
 }
 
 static const struct dev_pm_ops mtk_iommu_pm_ops = {
+	SET_RUNTIME_PM_OPS(mtk_iommu_suspend, mtk_iommu_resume, NULL)
 	SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(mtk_iommu_suspend, mtk_iommu_resume)
 };