Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit c72c55c3 authored by Chintan Pandya's avatar Chintan Pandya
Browse files

iommu: msm: Handle global fault interrupt



Global fault happens if SMMU global space is mis-configured,
or some external abort happens on the BUS. SMMU corners useful
information about the faults along with raising interrupt.
Install global fault interrupt by handling those interrupts.

Change-Id: I43c8be42f65adcdb61fe36c99c6f340c8812eb81
Signed-off-by: default avatarChintan Pandya <cpandya@codeaurora.org>
parent b3f49348
Loading
Loading
Loading
Loading
+7 −1
Original line number Diff line number Diff line
@@ -16,7 +16,13 @@ Optional properties:
- qcom,vdd-supply: Regulator needed to access IOMMU
- qcom,alt-vdd-supply : Alternative regulator needed to access IOMMU
  configuration registers.
- interrupts : should contain the performance monitor overflow interrupt number.
- interrupts : Interrupt numbers for permormance and global fault interrupts
- interrupt-names: Refers to the interrupts number mentioned above.
	"pmon" : the performance monitor overflow interrupt number
	"global_cfg_NS_irq" : the global config non-secure interrupt number
	"global_client_NS_irq" : the global client non-secure interrupt number
	"global_cfg_S_irq" : the global config secure interrupt number
	"global_client_S_irq" : the global client secure interrupt number
- qcom,iommu-enable-halt : Enable halt of the IOMMU before programming certain	19
  registers
- qcom,iommu-pmu-ngroups: Number of Performance Monitor Unit (PMU) groups.
+66 −11
Original line number Diff line number Diff line
@@ -18,7 +18,12 @@
		ranges;
		reg = <0xfda64000 0x10000>;
		reg-names = "iommu_base";
		interrupts = <0 67 0>;
		interrupts = <0 67 0>,
				<0 229 0>, <0 231 0>,
				<0 230 0>, <0 232 0>;
		interrupt-names = "pmon",
				"global_cfg_NS_irq", "global_client_NS_irq",
				"global_cfg_S_irq", "global_client_S_irq";
		qcom,needs-alt-core-clk;
		label = "jpeg_iommu";
		status = "disabled";
@@ -114,7 +119,12 @@
		ranges;
		reg = <0xfd928000 0x10000>;
		reg-names = "iommu_base";
		interrupts = <0 73 0>;
		interrupts = <0 73 0>,
				<0 229 0>, <0 231 0>,
				<0 230 0>, <0 232 0>;
		interrupt-names = "pmon",
				"global_cfg_NS_irq", "global_client_NS_irq",
				"global_cfg_S_irq", "global_client_S_irq";
		qcom,iommu-secure-id = <1>;
		label = "mdp_iommu";
		qcom,msm-bus,name = "mdp_ebi";
@@ -219,7 +229,12 @@
		reg = <0xfdc84000 0x10000
		       0xfdce0004 0x4>;
		reg-names = "iommu_base", "clk_base";
		interrupts = <0 45 0>;
		interrupts = <0 45 0>,
				<0 229 0>, <0 231 0>,
				<0 230 0>, <0 232 0>;
		interrupt-names = "pmon",
				"global_cfg_NS_irq", "global_client_NS_irq",
				"global_cfg_S_irq", "global_client_S_irq";
		qcom,iommu-secure-id = <0>;
		qcom,needs-alt-core-clk;
		label = "venus_iommu";
@@ -336,7 +351,12 @@
		ranges;
		reg = <0xfdb10000 0x10000>;
		reg-names = "iommu_base";
		interrupts = <0 38 0>;
		interrupts = <0 38 0>,
				<0 229 0>, <0 231 0>,
				<0 230 0>, <0 232 0>;
		interrupt-names = "pmon",
				"global_cfg_NS_irq", "global_client_NS_irq",
				"global_cfg_S_irq", "global_client_S_irq";
		label = "kgsl_iommu";
		qcom,msm-bus,name = "kgsl_ebi";
		qcom,msm-bus,num-cases = <2>;
@@ -426,7 +446,12 @@
		ranges;
		reg = <0xfda44000 0x10000>;
		reg-names = "iommu_base";
		interrupts = <0 62 0>;
		interrupts = <0 62 0>,
				<0 229 0>, <0 231 0>,
				<0 230 0>, <0 232 0>;
		interrupt-names = "pmon",
				"global_cfg_NS_irq", "global_client_NS_irq",
				"global_cfg_S_irq", "global_client_S_irq";
		qcom,needs-alt-core-clk;
		label = "vfe_iommu";
		qcom,msm-bus,name = "vfe_ebi";
@@ -528,7 +553,12 @@
		ranges;
		reg = <0xf9bc4000 0x10000>;
		reg-names = "iommu_base";
		interrupts = <0 153 0>;
		interrupts = <0 153 0>,
				<0 229 0>, <0 231 0>,
				<0 230 0>, <0 232 0>;
		interrupt-names = "pmon",
				"global_cfg_NS_irq", "global_client_NS_irq",
				"global_cfg_S_irq", "global_client_S_irq";
		qcom,iommu-secure-id = <8>;
		label = "copss_iommu";
		qcom,msm-bus,name = "copss_ebi";
@@ -688,7 +718,12 @@
		ranges;
		reg = <0xfdee4000 0x10000>;
		reg-names = "iommu_base";
		interrupts = <0 147 0>;
		interrupts = <0 147 0>,
				<0 229 0>, <0 231 0>,
				<0 230 0>, <0 232 0>;
		interrupt-names = "pmon",
				"global_cfg_NS_irq", "global_client_NS_irq",
				"global_cfg_S_irq", "global_client_S_irq";
		qcom,iommu-secure-id = <7>;
		label = "vpu_iommu";
		qcom,msm-bus,name = "vpu_ebi";
@@ -822,7 +857,12 @@
		ranges;
		reg = <0xfe054000 0x10000>;
		reg-names = "iommu_base";
		interrupts = <0 202 0>;
		interrupts = <0 202 0>,
				<0 229 0>, <0 231 0>,
				<0 230 0>, <0 232 0>;
		interrupt-names = "pmon",
				"global_cfg_NS_irq", "global_client_NS_irq",
				"global_cfg_S_irq", "global_client_S_irq";
		qcom,iommu-secure-id = <2>;
		label = "lpass_qdsp_iommu";
		qcom,msm-bus,name = "lpass_qdsp_ebi";
@@ -951,7 +991,12 @@
		ranges;
		reg = <0xfe064000 0x10000>;
		reg-names = "iommu_base";
		interrupts = <0 166 0>;
		interrupts = <0 166 0>,
				<0 229 0>, <0 231 0>,
				<0 230 0>, <0 232 0>;
		interrupt-names = "pmon",
				"global_cfg_NS_irq", "global_client_NS_irq",
				"global_cfg_S_irq", "global_client_S_irq";
		qcom,iommu-secure-id = <6>;
		label = "lpass_core_iommu";
		qcom,msm-bus,name = "lpass_core_ebi";
@@ -1072,7 +1117,12 @@
		ranges;
		reg = <0xfdfb6000 0x10000>;
		reg-names = "iommu_base";
		interrupts = <0 315 0>;
		interrupts = <0 315 0>,
				<0 229 0>, <0 231 0>,
				<0 230 0>, <0 232 0>;
		interrupt-names = "pmon",
				"global_cfg_NS_irq", "global_client_NS_irq",
				"global_cfg_S_irq", "global_client_S_irq";
		qcom,needs-alt-core-clk;
		label = "vcap_iommu";
		status = "disabled";
@@ -1166,7 +1216,12 @@
		ranges;
		reg = <0xfc734000 0x10000>;
		reg-names = "iommu_base";
		interrupts = <0 279 0>;
		interrupts = <0 279 0>,
				<0 229 0>, <0 231 0>,
				<0 230 0>, <0 232 0>;
		interrupt-names = "pmon",
				"global_cfg_NS_irq", "global_client_NS_irq",
				"global_cfg_S_irq", "global_client_S_irq";
		qcom,needs-alt-core-clk;
		label = "bcast_iommu";
		status = "disabled";
+1 −0
Original line number Diff line number Diff line
@@ -236,6 +236,7 @@ void print_ctx_regs(struct msm_iommu_context_reg regs[]);
 * interrupt is not supported in the API yet, but this will print an error
 * message and dump useful IOMMU registers.
 */
irqreturn_t msm_iommu_global_fault_handler(int irq, void *dev_id);
irqreturn_t msm_iommu_fault_handler(int irq, void *dev_id);
irqreturn_t msm_iommu_fault_handler_v2(int irq, void *dev_id);
irqreturn_t msm_iommu_secure_fault_handler_v2(int irq, void *dev_id);
+69 −0
Original line number Diff line number Diff line
@@ -1002,6 +1002,75 @@ static void __print_ctx_regs(void __iomem *base, int ctx, unsigned int fsr)
	print_ctx_regs(regs);
}

static void print_global_regs(void __iomem *base, unsigned int gfsr)
{
	pr_err("GFAR    = %016llx\n", GET_GFAR(base));

	pr_err("GFSR    = %08x [%s%s%s%s%s%s%s%s%s%s]\n", gfsr,
			(gfsr & 0x01) ? "ICF " : "",
			(gfsr & 0x02) ? "USF " : "",
			(gfsr & 0x04) ? "SMCF " : "",
			(gfsr & 0x08) ? "UCBF " : "",
			(gfsr & 0x10) ? "UCIF " : "",
			(gfsr & 0x20) ? "CAF " : "",
			(gfsr & 0x40) ? "EF " : "",
			(gfsr & 0x80) ? "PF " : "",
			(gfsr & 0x40000000) ? "SS " : "",
			(gfsr & 0x80000000) ? "MULTI " : "");

	pr_err("GFSYNR0	= %08x\n", GET_GFSYNR0(base));
	pr_err("GFSYNR1	= %08x\n", GET_GFSYNR1(base));
	pr_err("GFSYNR2	= %08x\n", GET_GFSYNR2(base));
}

irqreturn_t msm_iommu_global_fault_handler(int irq, void *dev_id)
{
	struct platform_device *pdev = dev_id;
	struct msm_iommu_drvdata *drvdata;
	unsigned int gfsr;
	int ret;

	mutex_lock(&msm_iommu_lock);
	BUG_ON(!pdev);

	drvdata = dev_get_drvdata(&pdev->dev);
	BUG_ON(!drvdata);

	if (!drvdata->ctx_attach_count) {
		pr_err("Unexpected IOMMU global fault !!\n");
		pr_err("name = %s\n", drvdata->name);
		pr_err("Power is OFF. Can't read global fault information\n");
		ret = IRQ_HANDLED;
		goto fail;
	}

	if (drvdata->sec_id != -1) {
		pr_err("NON-secure interrupt from secure %s\n", drvdata->name);
		ret = IRQ_HANDLED;
		goto fail;
	}

	ret = __enable_clocks(drvdata);
	if (ret) {
		ret = IRQ_NONE;
		goto fail;
	}

	gfsr = GET_GFSR(drvdata->base);
	if (gfsr) {
		pr_err("Unexpected %s global fault !!\n", drvdata->name);
		print_global_regs(drvdata->base, gfsr);
		SET_GFSR(drvdata->base, gfsr);
		ret = IRQ_HANDLED;
	} else
		ret = IRQ_NONE;

	__disable_clocks(drvdata);
fail:
	mutex_unlock(&msm_iommu_lock);
	return ret;
}

irqreturn_t msm_iommu_fault_handler_v2(int irq, void *dev_id)
{
	struct platform_device *pdev = dev_id;
+30 −0
Original line number Diff line number Diff line
@@ -282,6 +282,7 @@ static int msm_iommu_probe(struct platform_device *pdev)
	struct msm_iommu_drvdata *drvdata;
	struct resource *r;
	int ret, needs_alt_core_clk;
	int global_cfg_irq, global_client_irq;

	drvdata = devm_kzalloc(&pdev->dev, sizeof(*drvdata), GFP_KERNEL);
	if (!drvdata)
@@ -368,6 +369,35 @@ static int msm_iommu_probe(struct platform_device *pdev)
			}
		}
	}

	global_cfg_irq =
		platform_get_irq_byname(pdev, "global_cfg_NS_irq");
	if (global_cfg_irq > 0) {
		ret = devm_request_threaded_irq(&pdev->dev, global_cfg_irq,
				NULL,
				msm_iommu_global_fault_handler,
				IRQF_ONESHOT | IRQF_SHARED |
				IRQF_TRIGGER_RISING,
				"msm_iommu_global_cfg_irq", pdev);
		if (ret < 0)
			pr_err("Request Global CFG IRQ %d failed with ret=%d\n",
					global_cfg_irq, ret);
	}

	global_client_irq =
		platform_get_irq_byname(pdev, "global_client_NS_irq");
	if (global_client_irq > 0) {
		ret = devm_request_threaded_irq(&pdev->dev, global_client_irq,
				NULL,
				msm_iommu_global_fault_handler,
				IRQF_ONESHOT | IRQF_SHARED |
				IRQF_TRIGGER_RISING,
				"msm_iommu_global_client_irq", pdev);
		if (ret < 0)
			pr_err("Request Global Client IRQ %d failed with ret=%d\n",
					global_client_irq, ret);
	}

	return 0;
}