iommu/msm: Program global address space on first attach
SMMU global address space programming need to be performed
each time the device comes out of power collapse, move the
programming of global address space from driver initialization
to the point where the attach is initiated by the clients.
When the first context is attached, the global address space
is programmed prior to the programming the context.
Change-Id: I36e4f161861823aa43d15c3271f8d9b26214cb84
Signed-off-by: Sathish Ambley <sambley@codeaurora.org>
diff --git a/drivers/iommu/msm_iommu-v2.c b/drivers/iommu/msm_iommu-v2.c
index 10d0b66..bd80468 100644
--- a/drivers/iommu/msm_iommu-v2.c
+++ b/drivers/iommu/msm_iommu-v2.c
@@ -23,6 +23,8 @@
#include <linux/iommu.h>
#include <linux/clk.h>
#include <linux/scatterlist.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
#include <asm/sizes.h>
@@ -112,6 +114,42 @@
return 0;
}
+static void __reset_iommu(void __iomem *base)
+{
+ int i;
+
+ SET_ACR(base, 0);
+ SET_NSACR(base, 0);
+ SET_CR2(base, 0);
+ SET_NSCR2(base, 0);
+ SET_GFAR(base, 0);
+ SET_GFSRRESTORE(base, 0);
+ SET_TLBIALLNSNH(base, 0);
+ SET_PMCR(base, 0);
+ SET_SCR1(base, 0);
+ SET_SSDR_N(base, 0, 0);
+
+ for (i = 0; i < MAX_NUM_SMR; i++)
+ SET_SMR_VALID(base, i, 0);
+
+ mb();
+}
+
+static void __program_iommu(void __iomem *base)
+{
+ __reset_iommu(base);
+
+ SET_CR0_SMCFCFG(base, 1);
+ SET_CR0_USFCFG(base, 1);
+ SET_CR0_STALLD(base, 1);
+ SET_CR0_GCFGFIE(base, 1);
+ SET_CR0_GCFGFRE(base, 1);
+ SET_CR0_GFIE(base, 1);
+ SET_CR0_GFRE(base, 1);
+ SET_CR0_CLIENTPD(base, 0);
+ mb(); /* Make sure writes complete before returning */
+}
+
static void __reset_context(void __iomem *base, int ctx)
{
SET_ACTLR(base, ctx, 0);
@@ -129,11 +167,12 @@
}
static void __program_context(void __iomem *base, int ctx, int ncb,
- phys_addr_t pgtable, int redirect)
+ phys_addr_t pgtable, int redirect,
+ u32 *sids, int len)
{
unsigned int prrr, nmrr;
unsigned int pn;
- int i, j, found;
+ int i, j, found, num = 0;
__reset_context(base, ctx);
@@ -172,6 +211,30 @@
SET_CB_TTBR0_RGN(base, ctx, 1); /* WB, WA */
}
+ /* Program the M2V tables for this context */
+ for (i = 0; i < len / sizeof(*sids); i++) {
+ for (; num < MAX_NUM_SMR; num++)
+ if (GET_SMR_VALID(base, num) == 0)
+ break;
+ BUG_ON(num >= MAX_NUM_SMR);
+
+ SET_SMR_VALID(base, num, 1);
+ SET_SMR_MASK(base, num, 0);
+ SET_SMR_ID(base, num, sids[i]);
+
+ /* Set VMID = 0 */
+ SET_S2CR_N(base, num, 0);
+ SET_S2CR_CBNDX(base, num, ctx);
+ /* Set security bit override to be Non-secure */
+ SET_S2CR_NSCFG(base, sids[i], 3);
+
+ SET_CBAR_N(base, ctx, 0);
+ /* Stage 1 Context with Stage 2 bypass */
+ SET_CBAR_TYPE(base, ctx, 1);
+ /* Route page faults to the non-secure interrupt */
+ SET_CBAR_IRPTNDX(base, ctx, 1);
+ }
+
/* Find if this page table is used elsewhere, and re-use ASID */
found = 0;
for (i = 0; i < ncb; i++)
@@ -244,13 +307,33 @@
mutex_unlock(&msm_iommu_lock);
}
+static int msm_iommu_ctx_attached(struct device *dev)
+{
+ struct platform_device *pdev;
+ struct device_node *child;
+ struct msm_iommu_ctx_drvdata *ctx;
+
+ for_each_child_of_node(dev->of_node, child) {
+ pdev = of_find_device_by_node(child);
+
+ ctx = dev_get_drvdata(&pdev->dev);
+ if (ctx->attached_domain) {
+ of_node_put(child);
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
static int msm_iommu_attach_dev(struct iommu_domain *domain, struct device *dev)
{
struct msm_priv *priv;
struct msm_iommu_drvdata *iommu_drvdata;
struct msm_iommu_ctx_drvdata *ctx_drvdata;
struct msm_iommu_ctx_drvdata *tmp_drvdata;
- int ret = 0;
+ u32 sids[MAX_NUM_SMR];
+ int len = 0, ret = 0;
mutex_lock(&msm_iommu_lock);
@@ -278,15 +361,25 @@
goto fail;
}
- ret = __enable_clocks(iommu_drvdata);
- if (ret)
+ of_get_property(dev->of_node, "qcom,iommu-ctx-sids", &len);
+ BUG_ON(len >= sizeof(sids));
+ if (of_property_read_u32_array(dev->of_node, "qcom,iommu-ctx-sids",
+ sids, len / sizeof(*sids))) {
+ ret = -EINVAL;
goto fail;
+ }
+
+ if (!msm_iommu_ctx_attached(dev->parent)) {
+ ret = __enable_clocks(iommu_drvdata);
+ if (ret)
+ goto fail;
+ __program_iommu(iommu_drvdata->base);
+ }
__program_context(iommu_drvdata->base, ctx_drvdata->num,
iommu_drvdata->ncb, __pa(priv->pt.fl_table),
- priv->pt.redirect);
+ priv->pt.redirect, sids, len);
- __disable_clocks(iommu_drvdata);
list_add(&(ctx_drvdata->attached_elm), &priv->list_attached);
ctx_drvdata->attached_domain = domain;
@@ -301,7 +394,6 @@
struct msm_priv *priv;
struct msm_iommu_drvdata *iommu_drvdata;
struct msm_iommu_ctx_drvdata *ctx_drvdata;
- int ret;
mutex_lock(&msm_iommu_lock);
priv = domain->priv;
@@ -313,18 +405,16 @@
if (!iommu_drvdata || !ctx_drvdata || !ctx_drvdata->attached_domain)
goto fail;
- ret = __enable_clocks(iommu_drvdata);
- if (ret)
- goto fail;
-
SET_TLBIASID(iommu_drvdata->base, ctx_drvdata->num,
GET_CB_CONTEXTIDR_ASID(iommu_drvdata->base, ctx_drvdata->num));
__reset_context(iommu_drvdata->base, ctx_drvdata->num);
- __disable_clocks(iommu_drvdata);
list_del_init(&ctx_drvdata->attached_elm);
ctx_drvdata->attached_domain = NULL;
+ if (!msm_iommu_ctx_attached(dev->parent))
+ __disable_clocks(iommu_drvdata);
+
fail:
mutex_unlock(&msm_iommu_lock);
}
diff --git a/drivers/iommu/msm_iommu_dev-v2.c b/drivers/iommu/msm_iommu_dev-v2.c
index e690ada..d6858de 100644
--- a/drivers/iommu/msm_iommu_dev-v2.c
+++ b/drivers/iommu/msm_iommu_dev-v2.c
@@ -29,27 +29,6 @@
#include <mach/iommu_hw-v2.h>
#include <mach/iommu.h>
-static void msm_iommu_reset(void __iomem *base)
-{
- int i;
-
- SET_ACR(base, 0);
- SET_NSACR(base, 0);
- SET_CR2(base, 0);
- SET_NSCR2(base, 0);
- SET_GFAR(base, 0);
- SET_GFSRRESTORE(base, 0);
- SET_TLBIALLNSNH(base, 0);
- SET_PMCR(base, 0);
- SET_SCR1(base, 0);
- SET_SSDR_N(base, 0, 0);
-
- for (i = 0; i < MAX_NUM_SMR; i++)
- SET_SMR_VALID(base, i, 0);
-
- mb();
-}
-
static int msm_iommu_parse_dt(struct platform_device *pdev,
struct msm_iommu_drvdata *drvdata)
{
@@ -119,17 +98,6 @@
} else
drvdata->clk = NULL;
- msm_iommu_reset(drvdata->base);
-
- SET_CR0_SMCFCFG(drvdata->base, 1);
- SET_CR0_USFCFG(drvdata->base, 1);
- SET_CR0_STALLD(drvdata->base, 1);
- SET_CR0_GCFGFIE(drvdata->base, 1);
- SET_CR0_GCFGFRE(drvdata->base, 1);
- SET_CR0_GFIE(drvdata->base, 1);
- SET_CR0_GFRE(drvdata->base, 1);
- SET_CR0_CLIENTPD(drvdata->base, 0);
-
ret = msm_iommu_parse_dt(pdev, drvdata);
if (ret)
goto fail_clk;
@@ -173,13 +141,10 @@
}
static int msm_iommu_ctx_parse_dt(struct platform_device *pdev,
- struct msm_iommu_drvdata *drvdata,
struct msm_iommu_ctx_drvdata *ctx_drvdata)
{
struct resource *r, rp;
- u32 sids[MAX_NUM_SMR];
- int num = 0;
- int irq, i, ret, len = 0;
+ int irq, ret;
irq = platform_get_irq(pdev, 0);
if (irq > 0) {
@@ -212,53 +177,17 @@
&ctx_drvdata->name))
ctx_drvdata->name = dev_name(&pdev->dev);
- of_get_property(pdev->dev.of_node, "qcom,iommu-ctx-sids", &len);
- BUG_ON(len >= sizeof(sids));
- if (of_property_read_u32_array(pdev->dev.of_node, "qcom,iommu-ctx-sids",
- sids, len / sizeof(*sids)))
- return -EINVAL;
-
- /* Program the M2V tables for this context */
- for (i = 0; i < len / sizeof(*sids); i++) {
- for (; num < MAX_NUM_SMR; num++)
- if (GET_SMR_VALID(drvdata->base, num) == 0)
- break;
- BUG_ON(num >= MAX_NUM_SMR);
-
- SET_SMR_VALID(drvdata->base, num, 1);
- SET_SMR_MASK(drvdata->base, num, 0);
- SET_SMR_ID(drvdata->base, num, sids[i]);
-
- /* Set VMID = 0 */
- SET_S2CR_N(drvdata->base, num, 0);
- SET_S2CR_CBNDX(drvdata->base, num, ctx_drvdata->num);
- /* Set security bit override to be Non-secure */
- SET_S2CR_NSCFG(drvdata->base, sids[i], 3);
-
- SET_CBAR_N(drvdata->base, ctx_drvdata->num, 0);
- /* Stage 1 Context with Stage 2 bypass */
- SET_CBAR_TYPE(drvdata->base, ctx_drvdata->num, 1);
- /* Route page faults to the non-secure interrupt */
- SET_CBAR_IRPTNDX(drvdata->base, ctx_drvdata->num, 1);
- }
- mb();
-
return 0;
}
static int __devinit msm_iommu_ctx_probe(struct platform_device *pdev)
{
- struct msm_iommu_drvdata *drvdata;
struct msm_iommu_ctx_drvdata *ctx_drvdata = NULL;
int ret;
if (!pdev->dev.parent)
return -EINVAL;
- drvdata = dev_get_drvdata(pdev->dev.parent);
- if (!drvdata)
- return -ENODEV;
-
ctx_drvdata = devm_kzalloc(&pdev->dev, sizeof(*ctx_drvdata),
GFP_KERNEL);
if (!ctx_drvdata)
@@ -268,27 +197,11 @@
INIT_LIST_HEAD(&ctx_drvdata->attached_elm);
platform_set_drvdata(pdev, ctx_drvdata);
- ret = clk_prepare_enable(drvdata->pclk);
- if (ret)
- return ret;
-
- if (drvdata->clk) {
- ret = clk_prepare_enable(drvdata->clk);
- if (ret) {
- clk_disable_unprepare(drvdata->pclk);
- return ret;
- }
- }
-
- ret = msm_iommu_ctx_parse_dt(pdev, drvdata, ctx_drvdata);
+ ret = msm_iommu_ctx_parse_dt(pdev, ctx_drvdata);
if (!ret)
dev_info(&pdev->dev, "context %s using bank %d\n",
dev_name(&pdev->dev), ctx_drvdata->num);
- if (drvdata->clk)
- clk_disable_unprepare(drvdata->clk);
- clk_disable_unprepare(drvdata->pclk);
-
return ret;
}