diff options
author | Cho KyongHo <pullip.cho@samsung.com> | 2014-05-12 11:44:55 +0530 |
---|---|---|
committer | Joerg Roedel <jroedel@suse.de> | 2014-05-13 19:12:55 +0200 |
commit | 7060587052e0370ea1b7a41c84d5ad364be16f51 (patch) | |
tree | e9cdb5f0764594786ccf9f83415fb0317c15aba0 /drivers/iommu/exynos-iommu.c | |
parent | 46c16d1e4c11ffa0481b1dd6841e0bcabcf278e7 (diff) |
iommu/exynos: Gating clocks of master H/W
This patch gates clocks of master H/W as well as clocks of System MMU
if master clocks are specified.
Some Exynos SoCs (i.e. GScalers in Exynos5250) have dependencies in
the gating clocks of master H/W and its System MMU. If a H/W is the
case, accessing control registers of System MMU is prohibited unless
both of the gating clocks of System MMU and its master H/W.
CC: Tomasz Figa <t.figa@samsung.com>
Signed-off-by: Cho KyongHo <pullip.cho@samsung.com>
Signed-off-by: Shaik Ameer Basha <shaik.ameer@samsung.com>
Signed-off-by: Joerg Roedel <jroedel@suse.de>
Diffstat (limited to 'drivers/iommu/exynos-iommu.c')
-rw-r--r-- | drivers/iommu/exynos-iommu.c | 40 |
1 files changed, 38 insertions, 2 deletions
diff --git a/drivers/iommu/exynos-iommu.c b/drivers/iommu/exynos-iommu.c index c86e3745e98f..5af5c5c16f49 100644 --- a/drivers/iommu/exynos-iommu.c +++ b/drivers/iommu/exynos-iommu.c @@ -172,6 +172,7 @@ struct sysmmu_drvdata { struct device *dev; /* Owner of system MMU */ void __iomem *sfrbase; struct clk *clk; + struct clk *clk_master; int activations; rwlock_t lock; struct iommu_domain *domain; @@ -300,6 +301,8 @@ static irqreturn_t exynos_sysmmu_irq(int irq, void *dev_id) WARN_ON(!is_sysmmu_active(data)); + if (!IS_ERR(data->clk_master)) + clk_enable(data->clk_master); itype = (enum exynos_sysmmu_inttype) __ffs(__raw_readl(data->sfrbase + REG_INT_STATUS)); if (WARN_ON(!((itype >= 0) && (itype < SYSMMU_FAULT_UNKNOWN)))) @@ -326,6 +329,9 @@ static irqreturn_t exynos_sysmmu_irq(int irq, void *dev_id) if (itype != SYSMMU_FAULT_UNKNOWN) sysmmu_unblock(data->sfrbase); + if (!IS_ERR(data->clk_master)) + clk_disable(data->clk_master); + read_unlock(&data->lock); return IRQ_HANDLED; @@ -341,9 +347,14 @@ static bool __exynos_sysmmu_disable(struct sysmmu_drvdata *data) if (!set_sysmmu_inactive(data)) goto finish; + if (!IS_ERR(data->clk_master)) + clk_enable(data->clk_master); + __raw_writel(CTRL_DISABLE, data->sfrbase + REG_MMU_CTRL); clk_disable(data->clk); + if (!IS_ERR(data->clk_master)) + clk_disable(data->clk_master); disabled = true; data->pgtable = 0; @@ -386,14 +397,19 @@ static int __exynos_sysmmu_enable(struct sysmmu_drvdata *data, goto finish; } - clk_enable(data->clk); - data->pgtable = pgtable; + if (!IS_ERR(data->clk_master)) + clk_enable(data->clk_master); + clk_enable(data->clk); + __sysmmu_set_ptbase(data->sfrbase, pgtable); __raw_writel(CTRL_ENABLE, data->sfrbase + REG_MMU_CTRL); + if (!IS_ERR(data->clk_master)) + clk_disable(data->clk_master); + data->domain = domain; dev_dbg(data->sysmmu, "Enabled\n"); @@ -450,6 +466,10 @@ static void sysmmu_tlb_invalidate_entry(struct device *dev, unsigned long iova, if (is_sysmmu_active(data)) { unsigned int maj; unsigned int num_inv = 1; + + if (!IS_ERR(data->clk_master)) + clk_enable(data->clk_master); + maj = __raw_readl(data->sfrbase + REG_MMU_VERSION); /* * L2TLB invalidation required @@ -469,6 +489,8 @@ static void sysmmu_tlb_invalidate_entry(struct device *dev, unsigned long iova, data->sfrbase, iova, num_inv); sysmmu_unblock(data->sfrbase); } + if (!IS_ERR(data->clk_master)) + clk_disable(data->clk_master); } else { dev_dbg(data->sysmmu, "Disabled. Skipping invalidating TLB.\n"); } @@ -484,10 +506,14 @@ void exynos_sysmmu_tlb_invalidate(struct device *dev) read_lock_irqsave(&data->lock, flags); if (is_sysmmu_active(data)) { + if (!IS_ERR(data->clk_master)) + clk_enable(data->clk_master); if (sysmmu_block(data->sfrbase)) { __sysmmu_tlb_invalidate(data->sfrbase); sysmmu_unblock(data->sfrbase); } + if (!IS_ERR(data->clk_master)) + clk_disable(data->clk_master); } else { dev_dbg(data->sysmmu, "Disabled. Skipping invalidating TLB.\n"); } @@ -536,6 +562,16 @@ static int exynos_sysmmu_probe(struct platform_device *pdev) } } + data->clk_master = devm_clk_get(dev, "master"); + if (!IS_ERR(data->clk_master)) { + ret = clk_prepare(data->clk_master); + if (ret) { + clk_unprepare(data->clk); + dev_err(dev, "Failed to prepare master's clk\n"); + return ret; + } + } + data->sysmmu = dev; rwlock_init(&data->lock); INIT_LIST_HEAD(&data->node); |