From 8fdc9a162e8d5ad05077236b6e5852dda606a56e Mon Sep 17 00:00:00 2001 From: Suman Anna Date: Thu, 10 Mar 2016 17:18:02 -0600 Subject: 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 --- arch/arm/mach-omap2/Makefile | 2 ++ arch/arm/mach-omap2/omap-iommu.c | 43 ++++++++++++++++++++++++++++++++ drivers/iommu/omap-iommu.c | 17 +++++++++++++ drivers/iommu/omap-iommu.h | 2 ++ include/linux/platform_data/iommu-omap.h | 2 ++ 5 files changed, 66 insertions(+) create mode 100644 arch/arm/mach-omap2/omap-iommu.c 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 $(call filechk,offsets,__TI_PM_ASM_OFFSETS_H__) $(obj)/sleep33xx.o $(obj)/sleep43xx.o: include/generated/ti-pm-asm-offsets.h + +obj-$(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 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * OMAP IOMMU quirks for various TI SoCs + * + * Copyright (C) 2015-2019 Texas Instruments Incorporated - http://www.ti.com/ + * Suman Anna + */ + +#include +#include + +#include "omap_hwmod.h" +#include "omap_device.h" +#include "powerdomain.h" + +int omap_iommu_set_pwrdm_constraint(struct platform_device *pdev, bool request, + u8 *pwrst) +{ + struct powerdomain *pwrdm; + struct omap_device *od; + u8 next_pwrst; + + od = to_omap_device(pdev); + if (!od) + return -ENODEV; + + if (od->hwmods_cnt != 1) + return -EINVAL; + + pwrdm = omap_hwmod_get_pwrdm(od->hwmods[0]); + if (!pwrdm) + return -EINVAL; + + if (request) + *pwrst = pwrdm_read_next_pwrst(pwrdm); + + if (*pwrst > PWRDM_POWER_RET) + return 0; + + next_pwrst = request ? PWRDM_POWER_ON : *pwrst; + + 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) struct platform_device *pdev = to_platform_device(obj->dev); struct iommu_platform_data *pdata = dev_get_platdata(&pdev->dev); + if (pdata && pdata->set_pwrdm_constraint) { + err = pdata->set_pwrdm_constraint(pdev, true, &obj->pwrst); + if (err) { + dev_warn(obj->dev, "pwrdm_constraint failed to be set, status = %d\n", + err); + } + } + if (pdata && pdata->deassert_reset) { err = pdata->deassert_reset(pdev, pdata->reset_name); if (err) { @@ -213,6 +221,7 @@ static void iommu_disable(struct omap_iommu *obj) { struct platform_device *pdev = to_platform_device(obj->dev); struct iommu_platform_data *pdata = dev_get_platdata(&pdev->dev); + int ret; omap2_iommu_disable(obj); @@ -220,6 +229,14 @@ static void iommu_disable(struct omap_iommu *obj) if (pdata && pdata->assert_reset) pdata->assert_reset(pdev, pdata->reset_name); + + if (pdata && pdata->set_pwrdm_constraint) { + ret = pdata->set_pwrdm_constraint(pdev, false, &obj->pwrst); + if (ret) { + dev_warn(obj->dev, "pwrdm_constraint failed to be reset, status = %d\n", + ret); + } + } } /* 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 { struct iommu_device iommu; struct iommu_group *group; + + u8 pwrst; }; /** 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 { const char *reset_name; int (*assert_reset)(struct platform_device *pdev, const char *name); int (*deassert_reset)(struct platform_device *pdev, const char *name); + int (*set_pwrdm_constraint)(struct platform_device *pdev, bool request, + u8 *pwrst); }; -- cgit v1.2.3-54-g00ecf From 4e6bf3ca947b766653f1bd136102c1d34e9d0714 Mon Sep 17 00:00:00 2001 From: Suman Anna Date: Thu, 17 Mar 2016 19:20:32 -0500 Subject: ARM: OMAP2+: Use separate IOMMU pdata to fix DRA7 IPU1 boot The IPU1 MMU has been using common IOMMU pdata quirks defined and used by all IPU IOMMU devices on OMAP4 and beyond. Separate out the pdata for IPU1 MMU with the additional .set_pwrdm_constraint ops plugged in, so that the IPU1 power domain can be restricted to ON state during the boot and active period of the IPU1 remote processor. This eliminates the pre-conditions for the IPU1 boot issue as described in commit 8fdc9a162e8d ("iommu/omap: Fix boot issue on remoteprocs with AMMU/Unicache"). NOTE: 1. RET is not a valid target power domain state on DRA7 platforms, and IPU power domain is normally programmed for OFF. The IPU1 still fails to boot though, and an unclearable l3_noc error is thrown currently on 4.19 kernel without this fix. This behavior is same as on 4.14 LTS kernel but slightly different from previous 4.9 LTS kernel. 2. The fix is currently applied only to IPU1 on DRA7xx SoC, as the other affected IPU processors on OMAP4/OMAP5/DRA7 are in domains that are not entering RET. IPU2 on DRA7 is in CORE power domain which is only programmed for ON power state. The fix can be easily scaled if these domains do hit RET or OFF and loose context in the future. 3. The issue was not seen on current DRA7 platforms if any of the DSP remote processors were booted and using one of the GPTimers 5, 6, 7 or 8 on previous 4.9 LTS kernel. This was due to the errata fix for i874 implemented in commit 1cbabcb9807e ("ARM: DRA7: clockdomain: Implement timer workaround for errata i874") which keeps the IPU1 power domain from entering RET when the timers are active. But the timer workaround did not make any difference on 4.14/4.19 kernels, and an l3_noc error was seen still without this fix. Signed-off-by: Suman Anna --- arch/arm/mach-omap2/pdata-quirks.c | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/arch/arm/mach-omap2/pdata-quirks.c b/arch/arm/mach-omap2/pdata-quirks.c index 2fe736fa14a3..f60faddd968c 100644 --- a/arch/arm/mach-omap2/pdata-quirks.c +++ b/arch/arm/mach-omap2/pdata-quirks.c @@ -46,6 +46,17 @@ struct pdata_init { static struct of_dev_auxdata omap_auxdata_lookup[]; static struct twl4030_gpio_platform_data twl_gpio_auxdata; +#if IS_ENABLED(CONFIG_OMAP_IOMMU) +int omap_iommu_set_pwrdm_constraint(struct platform_device *pdev, bool request, + u8 *pwrst); +#else +static inline int omap_iommu_set_pwrdm_constraint(struct platform_device *pdev, + bool request, u8 *pwrst) +{ + return 0; +} +#endif + #ifdef CONFIG_MACH_NOKIA_N8X0 static void __init omap2420_n8x0_legacy_init(void) { @@ -434,6 +445,13 @@ static void __init omap5_uevm_legacy_init(void) #endif #ifdef CONFIG_SOC_DRA7XX +static struct iommu_platform_data dra7_ipu1_iommu_pdata = { + .reset_name = "mmu_cache", + .assert_reset = omap_device_assert_hardreset, + .deassert_reset = omap_device_deassert_hardreset, + .set_pwrdm_constraint = omap_iommu_set_pwrdm_constraint, +}; + static struct omap_hsmmc_platform_data dra7_hsmmc_data_mmc1; static struct omap_hsmmc_platform_data dra7_hsmmc_data_mmc2; static struct omap_hsmmc_platform_data dra7_hsmmc_data_mmc3; @@ -600,7 +618,7 @@ static struct of_dev_auxdata omap_auxdata_lookup[] = { OF_DEV_AUXDATA("ti,dra7-iommu", 0x55082000, "55082000.mmu", &omap4_iommu_pdata), OF_DEV_AUXDATA("ti,dra7-iommu", 0x58882000, "58882000.mmu", - &omap4_iommu_pdata), + &dra7_ipu1_iommu_pdata), #endif /* Common auxdata */ OF_DEV_AUXDATA("ti,sysc", 0, NULL, &ti_sysc_pdata), -- cgit v1.2.3-54-g00ecf From 309814054352fbbaab8b8fbb283d9232ada316d0 Mon Sep 17 00:00:00 2001 From: Suman Anna Date: Sat, 2 Mar 2019 21:34:59 -0600 Subject: ARM: OMAP2+: Extend DRA7 IPU1 MMU pdata quirks to DSP MDMA MMUs The C66-based DSPs on DRA7xx SoCs do not support a Powerdown-RET mode, and only supports a Powerdown-Grid OFF mode which requires a boot from reset. The HW_AUTO setting and a target power domain state of OFF implies that the DSPs are powered off as soon as they are idled by executing an IDLE instruction. The DSPs lose their context as a result and will be unable to resume operations from any wakeup event. The DSP power domains therefore need to be restricted to ON state for the duration a DSP processor is actively running. This is similar to the restriction required for DRA7 IPU1 processor (albeit because of a different reason). The IPU1 behavior is handled in commit 4e6bf3ca947b ("ARM: OMAP2+: Use separate IOMMU pdata to fix DRA7 IPU1 boot") which adds a .set_pwrdm_constraint ops to the OMAP IOMMU platform data to restrict the IPU1 power domain to ON state during the active period of the IPU1 remote processor. Extend the IPU1 iommu pdata quirks to the DRA7 MDMA MMUs as well to restrict the DSP power domains to ON state. The MDMA MMU module configuration will be the first and last steps in the boot and shutdown sequences of the DSP processors. The existing IPU1 IOMMU pdata variable has also been renamed appropriately to reflect the common usage between the IPU1 and the DSPs. NOTE: 1. The functional behavior is inconsistent between different DSPs on DRA74x, DRA72x and DRA71x SoCs and silicon revisions. DSP1 on DRA7 EVM rev.H (DRA752 ES2.0), AM57xx GP EVM (DRA752 ES2.0) and AM572x IDK (DRA752 ES2.0) boards is entering idle mode without any fix and are getting powered down and losing context, but none of the other boards (DRA72 EVM, DRA71 EVM, AM571x IDK) exhibit this behavior. The idling behavior is unchanged even after this patch is applied. DSP2 is not idled on any board. These behaviors are different from those observed on 4.14 kernel. 2. This patch is also needed to preserve the DSP contexts when proper clock-gating is achieved during inactive periods. DSP power domains on these platforms should not be hitting OFF at the moment (even with firmware images executing an IDLE instruction) because of the issue described in errata i879 ("DSP MStandby requires CD_EMU in SW_WKUP") affecting these SoCs, but the behavior is different between different DSPs and SoCs as explained in #1. The i879 errata fix in the following patch achieves the DSP clock-gating with HW_AUTO mode, and would result in a power domain sleep transition to OFF mode without any software context save mechanism for all the DSPs. Signed-off-by: Suman Anna --- arch/arm/mach-omap2/pdata-quirks.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/arch/arm/mach-omap2/pdata-quirks.c b/arch/arm/mach-omap2/pdata-quirks.c index f60faddd968c..9126961b5f5f 100644 --- a/arch/arm/mach-omap2/pdata-quirks.c +++ b/arch/arm/mach-omap2/pdata-quirks.c @@ -445,7 +445,7 @@ static void __init omap5_uevm_legacy_init(void) #endif #ifdef CONFIG_SOC_DRA7XX -static struct iommu_platform_data dra7_ipu1_iommu_pdata = { +static struct iommu_platform_data dra7_ipu1_dsp_iommu_pdata = { .reset_name = "mmu_cache", .assert_reset = omap_device_assert_hardreset, .deassert_reset = omap_device_deassert_hardreset, @@ -612,13 +612,13 @@ static struct of_dev_auxdata omap_auxdata_lookup[] = { OF_DEV_AUXDATA("ti,dra7-hsmmc", 0x480ad000, "480ad000.mmc", &dra7_hsmmc_data_mmc3), OF_DEV_AUXDATA("ti,dra7-dsp-iommu", 0x40d01000, "40d01000.mmu", - &omap4_iommu_pdata), + &dra7_ipu1_dsp_iommu_pdata), OF_DEV_AUXDATA("ti,dra7-dsp-iommu", 0x41501000, "41501000.mmu", - &omap4_iommu_pdata), + &dra7_ipu1_dsp_iommu_pdata), OF_DEV_AUXDATA("ti,dra7-iommu", 0x55082000, "55082000.mmu", &omap4_iommu_pdata), OF_DEV_AUXDATA("ti,dra7-iommu", 0x58882000, "58882000.mmu", - &dra7_ipu1_iommu_pdata), + &dra7_ipu1_dsp_iommu_pdata), #endif /* Common auxdata */ OF_DEV_AUXDATA("ti,sysc", 0, NULL, &ti_sysc_pdata), -- cgit v1.2.3-54-g00ecf From d2fb19c469aa8799ba32b2aabb154703a0e69ed1 Mon Sep 17 00:00:00 2001 From: Suman Anna Date: Thu, 20 Aug 2015 17:58:25 -0500 Subject: ARM: OMAP2+: Add workaround for DRA7 DSP MStandby errata i879 Errata Title: i879: DSP MStandby requires CD_EMU in SW_WKUP Description: The DSP requires the internal emulation clock to be actively toggling in order to successfully enter a low power mode via execution of the IDLE instruction and PRCM MStandby/Idle handshake. This assumes that other prerequisites and software sequence are followed. Workaround: The emulation clock to the DSP is free-running anytime CCS is connected via JTAG debugger to the DSP subsystem or when the CD_EMU clock domain is set in SW_WKUP mode. The CD_EMU domain can be set in SW_WKUP mode via the CM_EMU_CLKSTCTRL [1:0]CLKTRCTRL field. Implementation: This patch implements this workaround by denying the HW_AUTO mode for the EMU clockdomain during the power-up of any DSP processor and re-enabling the HW_AUTO mode during the shutdown of the last DSP processor (actually done during the enabling and disabling of the respective DSP MDMA MMUs). Reference counting has to be used to manage the independent sequencing between the multiple DSP processors. This switching is done at runtime rather than a static clockdomain flags value to meet the target power domain state for the EMU power domain during suspend. Note that the DSP MStandby behavior is not consistent across all boards prior to this fix. Please see commit 45f871eec6c0 ("ARM: OMAP2+: Extend DRA7 IPU1 MMU pdata quirks to DSP MDMA MMUs") for details. Signed-off-by: Suman Anna --- arch/arm/mach-omap2/omap-iommu.c | 43 +++++++++++++++++++++++++++++++++++++--- 1 file changed, 40 insertions(+), 3 deletions(-) diff --git a/arch/arm/mach-omap2/omap-iommu.c b/arch/arm/mach-omap2/omap-iommu.c index 2a9abe4f156d..65e7d6ec8b27 100644 --- a/arch/arm/mach-omap2/omap-iommu.c +++ b/arch/arm/mach-omap2/omap-iommu.c @@ -11,14 +11,43 @@ #include "omap_hwmod.h" #include "omap_device.h" +#include "clockdomain.h" #include "powerdomain.h" +static void omap_iommu_dra7_emu_swsup_config(struct platform_device *pdev, + bool enable) +{ + static struct clockdomain *emu_clkdm; + static DEFINE_SPINLOCK(emu_lock); + static atomic_t count; + struct device_node *np = pdev->dev.of_node; + + if (!of_device_is_compatible(np, "ti,dra7-dsp-iommu")) + return; + + if (!emu_clkdm) { + emu_clkdm = clkdm_lookup("emu_clkdm"); + if (WARN_ON_ONCE(!emu_clkdm)) + return; + } + + spin_lock(&emu_lock); + + if (enable && (atomic_inc_return(&count) == 1)) + clkdm_deny_idle(emu_clkdm); + else if (!enable && (atomic_dec_return(&count) == 0)) + clkdm_allow_idle(emu_clkdm); + + spin_unlock(&emu_lock); +} + int omap_iommu_set_pwrdm_constraint(struct platform_device *pdev, bool request, u8 *pwrst) { struct powerdomain *pwrdm; struct omap_device *od; u8 next_pwrst; + int ret = 0; od = to_omap_device(pdev); if (!od) @@ -31,13 +60,21 @@ int omap_iommu_set_pwrdm_constraint(struct platform_device *pdev, bool request, if (!pwrdm) return -EINVAL; - if (request) + if (request) { *pwrst = pwrdm_read_next_pwrst(pwrdm); + omap_iommu_dra7_emu_swsup_config(pdev, true); + } if (*pwrst > PWRDM_POWER_RET) - return 0; + goto out; next_pwrst = request ? PWRDM_POWER_ON : *pwrst; - return pwrdm_set_next_pwrst(pwrdm, next_pwrst); + ret = pwrdm_set_next_pwrst(pwrdm, next_pwrst); + +out: + if (!request) + omap_iommu_dra7_emu_swsup_config(pdev, false); + + return ret; } -- cgit v1.2.3-54-g00ecf