iommu: msm: Add support for probe deferral

Probe of the IOMMU devices might fail because clocks or
other depedencies might not be available at the time of probe.
In this case the IOMMU driver needs to handle this gracefully
and request probe deferral. This change ensures that the IOMMU
driver handles the probe deferral correctly by cleaning up properly
and returning the proper error code when a probe deferral occurs.

Change-Id: Ia9dd5997500742133f72295fcd90a546e5a70ea1
Signed-off-by: Olav Haugan <ohaugan@codeaurora.org>
diff --git a/drivers/iommu/msm_iommu_dev-v0.c b/drivers/iommu/msm_iommu_dev-v0.c
index 4fca473..4ee65d8 100644
--- a/drivers/iommu/msm_iommu_dev-v0.c
+++ b/drivers/iommu/msm_iommu_dev-v0.c
@@ -80,10 +80,13 @@
 	}
 	mutex_unlock(&iommu_list_lock);
 
-	if (!dev || !dev_get_drvdata(dev))
-		pr_err("Could not find context <%s>\n", ctx_name);
 	put_device(dev);
 
+	if (!dev || !dev_get_drvdata(dev)) {
+		pr_debug("Could not find context <%s>\n", ctx_name);
+		dev = ERR_PTR(-EPROBE_DEFER);
+	}
+
 	return dev;
 }
 EXPORT_SYMBOL(msm_iommu_get_ctx);
@@ -131,6 +134,52 @@
 	mb();
 }
 
+static int __get_clocks(struct platform_device *pdev,
+			struct msm_iommu_drvdata *drvdata,
+			int needs_alt_core_clk)
+{
+	int ret = 0;
+
+	drvdata->pclk = devm_clk_get(&pdev->dev, "iface_clk");
+	if (IS_ERR(drvdata->pclk)) {
+		ret = PTR_ERR(drvdata->pclk);
+		drvdata->pclk = NULL;
+		if (ret != -EPROBE_DEFER) {
+			pr_err("Unable to get %s clock for %s IOMMU device\n",
+				dev_name(&pdev->dev), drvdata->name);
+		}
+		goto fail;
+	}
+
+	drvdata->clk = devm_clk_get(&pdev->dev, "core_clk");
+
+	if (!IS_ERR(drvdata->clk)) {
+		if (clk_get_rate(drvdata->clk) == 0) {
+			ret = clk_round_rate(drvdata->clk, 1000);
+			clk_set_rate(drvdata->clk, ret);
+		}
+	} else {
+		drvdata->clk = NULL;
+	}
+
+	if (needs_alt_core_clk) {
+		drvdata->aclk = devm_clk_get(&pdev->dev, "alt_core_clk");
+		if (IS_ERR(drvdata->aclk)) {
+			ret = PTR_ERR(drvdata->aclk);
+			goto fail;
+		}
+	}
+
+	if (drvdata->aclk && clk_get_rate(drvdata->aclk) == 0) {
+		ret = clk_round_rate(drvdata->aclk, 1000);
+		clk_set_rate(drvdata->aclk, ret);
+	}
+
+	return 0;
+fail:
+	return ret;
+}
+
 #ifdef CONFIG_OF_DEVICE
 
 static int __get_bus_vote_client(struct platform_device *pdev,
@@ -165,13 +214,13 @@
 }
 
 static int msm_iommu_parse_dt(struct platform_device *pdev,
-				struct msm_iommu_drvdata *drvdata,
-				int *needs_alt_core_clk)
+				struct msm_iommu_drvdata *drvdata)
 {
 	struct device_node *child;
 	struct resource *r;
 	u32 glb_offset = 0;
 	int ret = 0;
+	int needs_alt_core_clk;
 
 	ret = __get_bus_vote_client(pdev, drvdata);
 
@@ -212,9 +261,14 @@
 		goto fail;
 	}
 
-	*needs_alt_core_clk = of_property_read_bool(pdev->dev.of_node,
+	needs_alt_core_clk = of_property_read_bool(pdev->dev.of_node,
 						   "qcom,needs-alt-core-clk");
 
+	ret = __get_clocks(pdev, drvdata, needs_alt_core_clk);
+
+	if (ret)
+		goto fail;
+
 	drvdata->sec_id = -1;
 	drvdata->ttbr_split = 0;
 
@@ -235,8 +289,7 @@
 
 #else
 static int msm_iommu_parse_dt(struct platform_device *pdev,
-				struct msm_iommu_drvdata *drvdata,
-				int *needs_alt_core_clk)
+				struct msm_iommu_drvdata *drvdata)
 {
 	return 0;
 }
@@ -246,60 +299,8 @@
 
 }
 
-
 #endif
 
-static int __get_clocks(struct platform_device *pdev,
-			struct msm_iommu_drvdata *drvdata,
-			int needs_alt_core_clk)
-{
-	int ret = 0;
-
-	drvdata->pclk = clk_get(&pdev->dev, "iface_clk");
-	if (IS_ERR(drvdata->pclk)) {
-		ret = PTR_ERR(drvdata->pclk);
-		drvdata->pclk = NULL;
-		pr_err("Unable to get %s clock for %s IOMMU device\n",
-			dev_name(&pdev->dev), drvdata->name);
-		goto fail;
-	}
-
-	drvdata->clk = clk_get(&pdev->dev, "core_clk");
-
-	if (!IS_ERR(drvdata->clk)) {
-		if (clk_get_rate(drvdata->clk) == 0) {
-			ret = clk_round_rate(drvdata->clk, 1000);
-			clk_set_rate(drvdata->clk, ret);
-		}
-	} else {
-		drvdata->clk = NULL;
-	}
-
-	if (needs_alt_core_clk) {
-		drvdata->aclk = devm_clk_get(&pdev->dev, "alt_core_clk");
-		if (IS_ERR(drvdata->aclk))
-			return PTR_ERR(drvdata->aclk);
-	}
-
-	if (drvdata->aclk && clk_get_rate(drvdata->aclk) == 0) {
-		ret = clk_round_rate(drvdata->aclk, 1000);
-		clk_set_rate(drvdata->aclk, ret);
-	}
-
-	return 0;
-fail:
-	return ret;
-}
-
-static void __put_clocks(struct msm_iommu_drvdata *drvdata)
-{
-	if (drvdata->aclk)
-		clk_put(drvdata->aclk);
-	if (drvdata->clk)
-		clk_put(drvdata->clk);
-	clk_put(drvdata->pclk);
-}
-
 /*
  * Do a basic check of the IOMMU by performing an ATS operation
  * on context bank 0.
@@ -391,7 +392,6 @@
 	struct msm_iommu_drvdata *drvdata;
 	struct msm_iommu_dev *iommu_dev = pdev->dev.platform_data;
 	int ret;
-	int needs_alt_core_clk = 0;
 
 	drvdata = devm_kzalloc(&pdev->dev, sizeof(*drvdata), GFP_KERNEL);
 
@@ -401,13 +401,18 @@
 	}
 
 	if (pdev->dev.of_node) {
-		ret = msm_iommu_parse_dt(pdev, drvdata, &needs_alt_core_clk);
+		ret = msm_iommu_parse_dt(pdev, drvdata);
 		if (ret)
 			goto fail;
 	} else if (pdev->dev.platform_data) {
 		struct resource *r, *r2;
 		resource_size_t	len;
 
+		ret = __get_clocks(pdev, drvdata, 0);
+
+		if (ret)
+			goto fail;
+
 		r = platform_get_resource_byname(pdev, IORESOURCE_MEM,
 						"physbase");
 
@@ -418,7 +423,8 @@
 
 		len = resource_size(r);
 
-		r2 = request_mem_region(r->start, len, r->name);
+		r2 = devm_request_mem_region(&pdev->dev, r->start,
+					     len, r->name);
 		if (!r2) {
 			pr_err("Could not request memory region: %pr\n", r);
 			ret = -EBUSY;
@@ -448,11 +454,6 @@
 
 	drvdata->dev = &pdev->dev;
 
-	ret = __get_clocks(pdev, drvdata, needs_alt_core_clk);
-
-	if (ret)
-		goto fail;
-
 	iommu_access_ops_v0.iommu_clk_on(drvdata);
 
 	msm_iommu_reset(drvdata->base, drvdata->glb_base, drvdata->ncb);
@@ -461,14 +462,14 @@
 	if (ret)
 		goto fail_clk;
 
+	iommu_access_ops_v0.iommu_clk_off(drvdata);
+
 	pr_info("device %s mapped at %p, with %d ctx banks\n",
 		drvdata->name, drvdata->base, drvdata->ncb);
 
 	msm_iommu_add_drv(drvdata);
 	platform_set_drvdata(pdev, drvdata);
 
-	iommu_access_ops_v0.iommu_clk_off(drvdata);
-
 	pmon_info = msm_iommu_pm_alloc(&pdev->dev);
 	if (pmon_info != NULL) {
 		ret = msm_iommu_pmon_parse_dt(pdev, pmon_info);
@@ -497,7 +498,6 @@
 
 fail_clk:
 	iommu_access_ops_v0.iommu_clk_off(drvdata);
-	__put_clocks(drvdata);
 fail:
 	__put_bus_vote_client(drvdata);
 fail_mem:
@@ -508,13 +508,13 @@
 {
 	struct msm_iommu_drvdata *drv = NULL;
 
+	msm_iommu_pm_iommu_unregister(&pdev->dev);
+	msm_iommu_pm_free(&pdev->dev);
+
 	drv = platform_get_drvdata(pdev);
 	if (drv) {
 		__put_bus_vote_client(drv);
 		msm_iommu_remove_drv(drv);
-		if (drv->clk)
-			clk_put(drv->clk);
-		clk_put(drv->pclk);
 		platform_set_drvdata(pdev, NULL);
 	}
 	return 0;
@@ -530,26 +530,28 @@
 
 	irq = platform_get_irq(pdev, 0);
 	if (irq > 0) {
-		ret = request_threaded_irq(irq, NULL,
+		ret = devm_request_threaded_irq(&pdev->dev, irq, NULL,
 				msm_iommu_fault_handler,
 				IRQF_ONESHOT | IRQF_SHARED,
 				"msm_iommu_nonsecure_irq", ctx_drvdata);
 		if (ret) {
 			pr_err("Request IRQ %d failed with ret=%d\n", irq, ret);
-			return ret;
+			goto out;
 		}
 	}
 
 	r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 	if (!r) {
 		pr_err("Could not find reg property for context bank\n");
-		return -EINVAL;
+		ret = -EINVAL;
+		goto out;
 	}
 
 	ret = of_address_to_resource(pdev->dev.parent->of_node, 0, &rp);
 	if (ret) {
 		pr_err("of_address_to_resource failed\n");
-		return -EINVAL;
+		ret = -EINVAL;
+		goto out;
 	}
 
 	/* Calculate the context bank number using the base addresses. CB0
@@ -560,29 +562,34 @@
 	if (of_property_read_string(pdev->dev.of_node, "label",
 					&ctx_drvdata->name)) {
 		pr_err("Could not find label property\n");
-		return -EINVAL;
+		ret = -EINVAL;
+		goto out;
 	}
 
 	if (!of_get_property(pdev->dev.of_node, "qcom,iommu-ctx-mids",
 			     &nmid_array_size)) {
 		pr_err("Could not find iommu-ctx-mids property\n");
-		return -EINVAL;
+		ret = -EINVAL;
+		goto out;
 	}
 	if (nmid_array_size >= sizeof(ctx_drvdata->sids)) {
 		pr_err("Too many mids defined - array size: %u, mids size: %u\n",
 			nmid_array_size, sizeof(ctx_drvdata->sids));
-		return -EINVAL;
+		ret = -EINVAL;
+		goto out;
 	}
 	nmid = nmid_array_size / sizeof(*ctx_drvdata->sids);
 
 	if (of_property_read_u32_array(pdev->dev.of_node, "qcom,iommu-ctx-mids",
 				       ctx_drvdata->sids, nmid)) {
 		pr_err("Could not find iommu-ctx-mids property\n");
-		return -EINVAL;
+		ret = -EINVAL;
+		goto out;
 	}
 	ctx_drvdata->nsid = nmid;
 
-	return 0;
+out:
+	return ret;
 }
 
 static void __program_m2v_tables(struct msm_iommu_drvdata *drvdata,
@@ -632,7 +639,7 @@
 	drvdata = dev_get_drvdata(pdev->dev.parent);
 
 	if (!drvdata) {
-		ret = -ENODEV;
+		ret = -EPROBE_DEFER;
 		goto fail;
 	}
 
@@ -650,8 +657,10 @@
 
 	if (pdev->dev.of_node) {
 		ret = msm_iommu_ctx_parse_dt(pdev, ctx_drvdata);
-		if (ret)
+		if (ret) {
+			platform_set_drvdata(pdev, NULL);
 			goto fail;
+		}
 	} else if (pdev->dev.platform_data) {
 		struct msm_iommu_ctx_dev *c = pdev->dev.platform_data;
 
@@ -673,7 +682,8 @@
 			goto fail;
 		}
 
-		ret = request_threaded_irq(irq, NULL, msm_iommu_fault_handler,
+		ret = devm_request_threaded_irq(&pdev->dev, irq, NULL,
+					msm_iommu_fault_handler,
 					IRQF_ONESHOT | IRQF_SHARED,
 					"msm_iommu_nonsecure_irq", ctx_drvdata);