diff options
authorSuman Anna2016-03-10 17:18:02 -0600
committerSuman Anna2019-03-02 21:46:07 -0600
commit8fdc9a162e8d5ad05077236b6e5852dda606a56e (patch)
parentddd6bd9b252aee842e529e6516b788529270404f (diff)
iommu/omap: Fix boot issue on remoteprocs with AMMU/Unicache
Support has been added to the OMAP IOMMU driver to fix a boot hang issue on OMAP remoteprocs with AMMU/Unicache, caused by an improper AMMU/Unicache state upon initial deassertion of the processor reset. The issue is described in detail in the next three paragraphs. All the Cortex M3/M4 IPU processor subsystems in OMAP SoCs have a AMMU/Unicache IP that dictates the memory attributes for addresses seen by the processor cores. The AMMU/Unicache is configured/enabled by the SCACHE_CONFIG.BYPASS bit - a value of 1 enables the cache and mandates all addresses accessed by M3/M4 be defined in the AMMU. This bit is not programmable from the host processor. The M3/M4 boot sequence starts out with the AMMU/Unicache in disabled state, and SYS/BIOS programs the AMMU regions and enables the Unicache during one of its initial boot steps. This SCACHE_CONFIG.BYPASS bit is however enabled by default whenever a RET reset is applied to the IP, irrespective of whether it was previously enabled or not. The AMMU registers lose their context whenever this reset is applied. The reset is effective as long as the MMU portion of the subsystem is enabled and clocked. This behavior is common to all the IPU and DSP subsystems that have an AMMU/Unicache. The IPU boot sequence involves enabling and programming the MMU, and loading the processor and releasing the reset(s) for the processor. The PM setup code currently sets the target state for most of the power domains to RET. The L2 MMU can be enabled, programmed and accessed properly just fine with the domain in hardware supervised mode, while the power domain goes through a RET->ON->RET transition during the programming sequence. However, the ON->RET transition asserts a RET reset, and the SCACHE_CONFIG.BYPASS bit gets auto-set. An AMMU fault is thrown immediately when the M3/M4 core's reset is released since the first instruction address itself will not be defined in any valid AMMU regions. The ON->RET transition happens automatically on the power domain after enabling the iommu due to the hardware supervised mode. This patch adds and invokes the .set_pwrdm_constraint pdata ops, if present, during the OMAP IOMMU enable and disable functions to resolve the above boot hang issue. The ops will allow to invoke a mach-omap2 layer API pwrdm_set_next_pwrst() in a multi-arch kernel environment. The ops also returns the current power domain state while enforcing the constraint so that the driver can store it and use it to set back the power domain state while releasing the constraint. The pdata ops implementation restricts the target power domain to ON during enable, and back to the original power domain state during disable, and thereby eliminating the conditions for the boot issue. The implementation is effective only when the original power domain state is either RET or OFF, and is a no-op when it is ON or INACTIVE. The .set_pwrdm_constraint ops need to be plugged in pdata-quirks for the affected remote processors to be able to boot properly. Note that the current issue is seen only on kernels with the affected power domains programmed to enter RET. For eg., IPU1 on DRA7xx is in a separate domain and is susceptible to this bug, while the IPU2 subsystem is within CORE power domain, and CORE RET is not supported on this SoC. IPUs on OMAP4 and OMAP5 are also susceptible since they are in CORE power domain, and CORE RET is a valid power target on these SoCs. Signed-off-by: Suman Anna <s-anna@ti.com>
5 files changed, 66 insertions, 0 deletions
diff --git a/arch/arm/mach-omap2/Makefile b/arch/arm/mach-omap2/Makefile
index 01377c292db4..d51a694e74db 100644
--- a/arch/arm/mach-omap2/Makefile
+++ b/arch/arm/mach-omap2/Makefile
@@ -243,3 +243,5 @@ include/generated/ti-pm-asm-offsets.h: arch/arm/mach-omap2/pm-asm-offsets.s FORC
243 $(call filechk,offsets,__TI_PM_ASM_OFFSETS_H__) 243 $(call filechk,offsets,__TI_PM_ASM_OFFSETS_H__)
244 244
245$(obj)/sleep33xx.o $(obj)/sleep43xx.o: include/generated/ti-pm-asm-offsets.h 245$(obj)/sleep33xx.o $(obj)/sleep43xx.o: include/generated/ti-pm-asm-offsets.h
247obj-$(CONFIG_OMAP_IOMMU) += omap-iommu.o
diff --git a/arch/arm/mach-omap2/omap-iommu.c b/arch/arm/mach-omap2/omap-iommu.c
new file mode 100644
index 000000000000..2a9abe4f156d
--- /dev/null
+++ b/arch/arm/mach-omap2/omap-iommu.c
@@ -0,0 +1,43 @@
1// SPDX-License-Identifier: GPL-2.0
3 * OMAP IOMMU quirks for various TI SoCs
4 *
5 * Copyright (C) 2015-2019 Texas Instruments Incorporated - http://www.ti.com/
6 * Suman Anna <s-anna@ti.com>
7 */
9#include <linux/platform_device.h>
10#include <linux/err.h>
12#include "omap_hwmod.h"
13#include "omap_device.h"
14#include "powerdomain.h"
16int omap_iommu_set_pwrdm_constraint(struct platform_device *pdev, bool request,
17 u8 *pwrst)
19 struct powerdomain *pwrdm;
20 struct omap_device *od;
21 u8 next_pwrst;
23 od = to_omap_device(pdev);
24 if (!od)
25 return -ENODEV;
27 if (od->hwmods_cnt != 1)
28 return -EINVAL;
30 pwrdm = omap_hwmod_get_pwrdm(od->hwmods[0]);
31 if (!pwrdm)
32 return -EINVAL;
34 if (request)
35 *pwrst = pwrdm_read_next_pwrst(pwrdm);
37 if (*pwrst > PWRDM_POWER_RET)
38 return 0;
40 next_pwrst = request ? PWRDM_POWER_ON : *pwrst;
42 return pwrdm_set_next_pwrst(pwrdm, next_pwrst);
diff --git a/drivers/iommu/omap-iommu.c b/drivers/iommu/omap-iommu.c
index a3d0184e24f6..f623f76378a0 100644
--- a/drivers/iommu/omap-iommu.c
+++ b/drivers/iommu/omap-iommu.c
@@ -194,6 +194,14 @@ static int iommu_enable(struct omap_iommu *obj)
194 struct platform_device *pdev = to_platform_device(obj->dev); 194 struct platform_device *pdev = to_platform_device(obj->dev);
195 struct iommu_platform_data *pdata = dev_get_platdata(&pdev->dev); 195 struct iommu_platform_data *pdata = dev_get_platdata(&pdev->dev);
196 196
197 if (pdata && pdata->set_pwrdm_constraint) {
198 err = pdata->set_pwrdm_constraint(pdev, true, &obj->pwrst);
199 if (err) {
200 dev_warn(obj->dev, "pwrdm_constraint failed to be set, status = %d\n",
201 err);
202 }
203 }
197 if (pdata && pdata->deassert_reset) { 205 if (pdata && pdata->deassert_reset) {
198 err = pdata->deassert_reset(pdev, pdata->reset_name); 206 err = pdata->deassert_reset(pdev, pdata->reset_name);
199 if (err) { 207 if (err) {
@@ -213,6 +221,7 @@ static void iommu_disable(struct omap_iommu *obj)
213{ 221{
214 struct platform_device *pdev = to_platform_device(obj->dev); 222 struct platform_device *pdev = to_platform_device(obj->dev);
215 struct iommu_platform_data *pdata = dev_get_platdata(&pdev->dev); 223 struct iommu_platform_data *pdata = dev_get_platdata(&pdev->dev);
224 int ret;
216 225
217 omap2_iommu_disable(obj); 226 omap2_iommu_disable(obj);
218 227
@@ -220,6 +229,14 @@ static void iommu_disable(struct omap_iommu *obj)
220 229
221 if (pdata && pdata->assert_reset) 230 if (pdata && pdata->assert_reset)
222 pdata->assert_reset(pdev, pdata->reset_name); 231 pdata->assert_reset(pdev, pdata->reset_name);
233 if (pdata && pdata->set_pwrdm_constraint) {
234 ret = pdata->set_pwrdm_constraint(pdev, false, &obj->pwrst);
235 if (ret) {
236 dev_warn(obj->dev, "pwrdm_constraint failed to be reset, status = %d\n",
237 ret);
238 }
239 }
223} 240}
224 241
225/* 242/*
diff --git a/drivers/iommu/omap-iommu.h b/drivers/iommu/omap-iommu.h
index 1703159ef5af..7766d5a4f3f1 100644
--- a/drivers/iommu/omap-iommu.h
+++ b/drivers/iommu/omap-iommu.h
@@ -81,6 +81,8 @@ struct omap_iommu {
81 81
82 struct iommu_device iommu; 82 struct iommu_device iommu;
83 struct iommu_group *group; 83 struct iommu_group *group;
85 u8 pwrst;
84}; 86};
85 87
86/** 88/**
diff --git a/include/linux/platform_data/iommu-omap.h b/include/linux/platform_data/iommu-omap.h
index e8b12dbf6170..1a0aa46a5ad6 100644
--- a/include/linux/platform_data/iommu-omap.h
+++ b/include/linux/platform_data/iommu-omap.h
@@ -16,4 +16,6 @@ struct iommu_platform_data {
16 const char *reset_name; 16 const char *reset_name;
17 int (*assert_reset)(struct platform_device *pdev, const char *name); 17 int (*assert_reset)(struct platform_device *pdev, const char *name);
18 int (*deassert_reset)(struct platform_device *pdev, const char *name); 18 int (*deassert_reset)(struct platform_device *pdev, const char *name);
19 int (*set_pwrdm_constraint)(struct platform_device *pdev, bool request,
20 u8 *pwrst);
19}; 21};