From 8a6506e1ba0d2b831729808d958aae77604f12f9 Mon Sep 17 00:00:00 2001 From: Alexander Sverdlin Date: Thu, 17 Jul 2025 17:27:03 +0200 Subject: ARM: AM33xx: Implement TI advisory 1.0.36 (EMU0/EMU1 pins state on reset) There is an issue possible where TI AM33xx SoCs do not boot properly after a reset if EMU0/EMU1 pins were used as GPIO and have been driving low level actively prior to reset [1]. "Advisory 1.0.36 EMU0 and EMU1: Terminals Must be Pulled High Before ICEPick Samples The state of the EMU[1:0] terminals are latched during reset to determine ICEPick boot mode. For normal device operation, these terminals must be pulled up to a valid high logic level ( > VIH min) before ICEPick samples the state of these terminals, which occurs [five CLK_M_OSC clock cycles - 10 ns] after the falling edge of WARMRSTn. Many applications may not require the secondary GPIO function of the EMU[1:0] terminals. In this case, they would only be connected to pull-up resistors, which ensures they are always high when ICEPick samples. However, some applications may need to use these terminals as GPIO where they could be driven low before reset is asserted. This usage of the EMU[1:0] terminals may require special attention to ensure the terminals are allowed to return to a valid high-logic level before ICEPick samples the state of these terminals. When any device reset is asserted, the pin mux mode of EMU[1:0] terminals configured to operate as GPIO (mode 7) will change back to EMU input (mode 0) on the falling edge of WARMRSTn. This only provides a short period of time for the terminals to return high if driven low before reset is asserted... If the EMU[1:0] terminals are configured to operate as GPIO, the product should be designed such these terminals can be pulled to a valid high-logic level within 190 ns after the falling edge of WARMRSTn." We've noticed this problem with custom am335x hardware in combination with recently implemented cold reset method (commit 6521f6a195c70 ("ARM: AM33xx: PRM: Implement REBOOT_COLD")). It looks like the problem can affect other HW, for instance AM335x Chiliboard, because the latter has LEDs on GPIO3_7/GPIO3_8 as well. One option would be to check if the pins are in GPIO mode and either switch to output active high, or switch to input and poll until the external pull-ups have brought the pins to the desired high state. But fighting with GPIO driver for these pins is probably not the most straight forward approch in a reboot handler. Fortunately we can easily control pinmuxing here and rely on the external pull-ups. TI recommends 4k7 external pull up resistors [2] and even with quite conservative estimation for pin capacity (1 uF should never happen) the required delay shall not exceed 5ms. [1] Link: https://www.ti.com/lit/pdf/sprz360 [2] Link: https://e2e.ti.com/support/processors-group/processors/f/processors-forum/866346/am3352-emu-1-0-questions Cc: stable@vger.kernel.org Signed-off-by: Alexander Sverdlin Link: https://lore.kernel.org/r/20250717152708.487891-1-alexander.sverdlin@siemens.com Signed-off-by: Kevin Hilman --- arch/arm/mach-omap2/am33xx-restart.c | 36 ++++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/arch/arm/mach-omap2/am33xx-restart.c b/arch/arm/mach-omap2/am33xx-restart.c index fcf3d557aa78..3cdf223addcc 100644 --- a/arch/arm/mach-omap2/am33xx-restart.c +++ b/arch/arm/mach-omap2/am33xx-restart.c @@ -2,12 +2,46 @@ /* * am33xx-restart.c - Code common to all AM33xx machines. */ +#include +#include #include #include #include "common.h" +#include "control.h" #include "prm.h" +/* + * Advisory 1.0.36 EMU0 and EMU1: Terminals Must be Pulled High Before + * ICEPick Samples + * + * If EMU0/EMU1 pins have been used as GPIO outputs and actively driving low + * level, the device might not reboot in normal mode. We are in a bad position + * to override GPIO state here, so just switch the pins into EMU input mode + * (that's what reset will do anyway) and wait a bit, because the state will be + * latched 190 ns after reset. + */ +static void am33xx_advisory_1_0_36(void) +{ + u32 emu0 = omap_ctrl_readl(AM335X_PIN_EMU0); + u32 emu1 = omap_ctrl_readl(AM335X_PIN_EMU1); + + /* If both pins are in EMU mode, nothing to do */ + if (!(emu0 & 7) && !(emu1 & 7)) + return; + + /* Switch GPIO3_7/GPIO3_8 into EMU0/EMU1 modes respectively */ + omap_ctrl_writel(emu0 & ~7, AM335X_PIN_EMU0); + omap_ctrl_writel(emu1 & ~7, AM335X_PIN_EMU1); + + /* + * Give pull-ups time to load the pin/PCB trace capacity. + * 5 ms shall be enough to load 1 uF (would be huge capacity for these + * pins) with TI-recommended 4k7 external pull-ups. + */ + mdelay(5); +} + /** * am33xx_restart - trigger a software restart of the SoC * @mode: the "reboot mode", see arch/arm/kernel/{setup,process}.c @@ -18,6 +52,8 @@ */ void am33xx_restart(enum reboot_mode mode, const char *cmd) { + am33xx_advisory_1_0_36(); + /* TODO: Handle cmd if necessary */ prm_reboot_mode = mode; -- cgit v1.2.3 From 045e81d8d7e65baefbd4dea0503ca3330f93f3e6 Mon Sep 17 00:00:00 2001 From: Yang Xiuwei Date: Sun, 17 Aug 2025 16:34:49 +0800 Subject: ARM: OMAP2+: use IS_ERR_OR_NULL() helper Simplify error handling in OMAP powerdomain, voltage, and VP code by replacing open-coded '!ptr || IS_ERR(ptr)' checks with the combined IS_ERR_OR_NULL() helper. This improves readability and consistency across omap_set_pwrdm_state(), voltdm_get_voltage(), voltdm_scale(), voltdm_reset(), and related functions. No functional change intended. Signed-off-by: Yang Xiuwei Reviewed-by: Andreas Kemnade Link: https://lore.kernel.org/r/20250817083449.2249268-1-yangxiuwei2025@163.com Signed-off-by: Kevin Hilman --- arch/arm/mach-omap2/powerdomain.c | 2 +- arch/arm/mach-omap2/voltage.c | 12 ++++++------ arch/arm/mach-omap2/vp.c | 4 ++-- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/arch/arm/mach-omap2/powerdomain.c b/arch/arm/mach-omap2/powerdomain.c index a4785302b7ae..0225b9889404 100644 --- a/arch/arm/mach-omap2/powerdomain.c +++ b/arch/arm/mach-omap2/powerdomain.c @@ -1111,7 +1111,7 @@ int omap_set_pwrdm_state(struct powerdomain *pwrdm, u8 pwrst) int curr_pwrst; int ret = 0; - if (!pwrdm || IS_ERR(pwrdm)) + if (IS_ERR_OR_NULL(pwrdm)) return -EINVAL; while (!(pwrdm->pwrsts & (1 << pwrst))) { diff --git a/arch/arm/mach-omap2/voltage.c b/arch/arm/mach-omap2/voltage.c index 49e8bc69abdd..000c2bca5ef0 100644 --- a/arch/arm/mach-omap2/voltage.c +++ b/arch/arm/mach-omap2/voltage.c @@ -51,7 +51,7 @@ static LIST_HEAD(voltdm_list); */ unsigned long voltdm_get_voltage(struct voltagedomain *voltdm) { - if (!voltdm || IS_ERR(voltdm)) { + if (IS_ERR_OR_NULL(voltdm)) { pr_warn("%s: VDD specified does not exist!\n", __func__); return 0; } @@ -73,7 +73,7 @@ static int voltdm_scale(struct voltagedomain *voltdm, int ret, i; unsigned long volt = 0; - if (!voltdm || IS_ERR(voltdm)) { + if (IS_ERR_OR_NULL(voltdm)) { pr_warn("%s: VDD specified does not exist!\n", __func__); return -EINVAL; } @@ -124,7 +124,7 @@ void voltdm_reset(struct voltagedomain *voltdm) { unsigned long target_volt; - if (!voltdm || IS_ERR(voltdm)) { + if (IS_ERR_OR_NULL(voltdm)) { pr_warn("%s: VDD specified does not exist!\n", __func__); return; } @@ -154,7 +154,7 @@ void voltdm_reset(struct voltagedomain *voltdm) void omap_voltage_get_volttable(struct voltagedomain *voltdm, struct omap_volt_data **volt_data) { - if (!voltdm || IS_ERR(voltdm)) { + if (IS_ERR_OR_NULL(voltdm)) { pr_warn("%s: VDD specified does not exist!\n", __func__); return; } @@ -182,7 +182,7 @@ struct omap_volt_data *omap_voltage_get_voltdata(struct voltagedomain *voltdm, { int i; - if (!voltdm || IS_ERR(voltdm)) { + if (IS_ERR_OR_NULL(voltdm)) { pr_warn("%s: VDD specified does not exist!\n", __func__); return ERR_PTR(-EINVAL); } @@ -216,7 +216,7 @@ struct omap_volt_data *omap_voltage_get_voltdata(struct voltagedomain *voltdm, int omap_voltage_register_pmic(struct voltagedomain *voltdm, struct omap_voltdm_pmic *pmic) { - if (!voltdm || IS_ERR(voltdm)) { + if (IS_ERR_OR_NULL(voltdm)) { pr_warn("%s: VDD specified does not exist!\n", __func__); return -EINVAL; } diff --git a/arch/arm/mach-omap2/vp.c b/arch/arm/mach-omap2/vp.c index a709655b978c..03c481c4742c 100644 --- a/arch/arm/mach-omap2/vp.c +++ b/arch/arm/mach-omap2/vp.c @@ -199,7 +199,7 @@ void omap_vp_enable(struct voltagedomain *voltdm) struct omap_vp_instance *vp; u32 vpconfig, volt; - if (!voltdm || IS_ERR(voltdm)) { + if (IS_ERR_OR_NULL(voltdm)) { pr_warn("%s: VDD specified does not exist!\n", __func__); return; } @@ -244,7 +244,7 @@ void omap_vp_disable(struct voltagedomain *voltdm) u32 vpconfig; int timeout; - if (!voltdm || IS_ERR(voltdm)) { + if (IS_ERR_OR_NULL(voltdm)) { pr_warn("%s: VDD specified does not exist!\n", __func__); return; } -- cgit v1.2.3 From 74139a64e8cedb6d971c78d5d17384efeced1725 Mon Sep 17 00:00:00 2001 From: Miaoqian Lin Date: Tue, 2 Sep 2025 15:59:43 +0800 Subject: ARM: OMAP2+: pm33xx-core: ix device node reference leaks in amx3_idle_init Add missing of_node_put() calls to release device node references obtained via of_parse_phandle(). Fixes: 06ee7a950b6a ("ARM: OMAP2+: pm33xx-core: Add cpuidle_ops for am335x/am437x") Cc: stable@vger.kernel.org Signed-off-by: Miaoqian Lin Link: https://lore.kernel.org/r/20250902075943.2408832-1-linmq006@gmail.com Signed-off-by: Kevin Hilman --- arch/arm/mach-omap2/pm33xx-core.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/arch/arm/mach-omap2/pm33xx-core.c b/arch/arm/mach-omap2/pm33xx-core.c index c907478be196..4abb86dc98fd 100644 --- a/arch/arm/mach-omap2/pm33xx-core.c +++ b/arch/arm/mach-omap2/pm33xx-core.c @@ -388,12 +388,15 @@ static int __init amx3_idle_init(struct device_node *cpu_node, int cpu) if (!state_node) break; - if (!of_device_is_available(state_node)) + if (!of_device_is_available(state_node)) { + of_node_put(state_node); continue; + } if (i == CPUIDLE_STATE_MAX) { pr_warn("%s: cpuidle states reached max possible\n", __func__); + of_node_put(state_node); break; } @@ -403,6 +406,7 @@ static int __init amx3_idle_init(struct device_node *cpu_node, int cpu) states[state_count].wfi_flags |= WFI_FLAG_WAKE_M3 | WFI_FLAG_FLUSH_CACHE; + of_node_put(state_node); state_count++; } -- cgit v1.2.3 From 21e2e1abd4321a220a11c2963ff60214fd73c3e8 Mon Sep 17 00:00:00 2001 From: Kuninori Morimoto Date: Fri, 18 Jul 2025 01:52:01 +0000 Subject: arm: omap2: use string choices helper We can use string choices helper, let's use it. Signed-off-by: Kuninori Morimoto Reviewed-by: Andy Shevchenko Reviewed-by: Andreas Kemnade Link: https://lore.kernel.org/r/87ikjq5kqm.wl-kuninori.morimoto.gx@renesas.com Signed-off-by: Kevin Hilman --- arch/arm/mach-omap2/board-n8x0.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/arm/mach-omap2/board-n8x0.c b/arch/arm/mach-omap2/board-n8x0.c index ff2a4a4d8220..969265d5d5c6 100644 --- a/arch/arm/mach-omap2/board-n8x0.c +++ b/arch/arm/mach-omap2/board-n8x0.c @@ -167,7 +167,7 @@ static int n8x0_mmc_set_power_menelaus(struct device *dev, int slot, #ifdef CONFIG_MMC_DEBUG dev_dbg(dev, "Set slot %d power: %s (vdd %d)\n", slot + 1, - power_on ? "on" : "off", vdd); + str_on_off(power_on), vdd); #endif if (slot == 0) { if (!power_on) -- cgit v1.2.3 From e2c0510935c5485a2dacfd13af3958536b9d138b Mon Sep 17 00:00:00 2001 From: Brian Masney Date: Thu, 10 Jul 2025 19:42:16 -0400 Subject: ARM: OMAP1: clock: convert from round_rate() to determine_rate() The round_rate() clk ops is deprecated, so migrate this driver from round_rate() to determine_rate() using the Coccinelle semantic patch on the cover letter of this series. Signed-off-by: Brian Masney Acked-by: Janusz Krzysztofik Link: https://lore.kernel.org/r/20250710-arm32-clk-round-rate-v1-1-a9146b77aca9@redhat.com Signed-off-by: Kevin Hilman --- arch/arm/mach-omap1/clock.c | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/arch/arm/mach-omap1/clock.c b/arch/arm/mach-omap1/clock.c index 83381e23fab9..afc6404f62d3 100644 --- a/arch/arm/mach-omap1/clock.c +++ b/arch/arm/mach-omap1/clock.c @@ -705,14 +705,21 @@ static unsigned long omap1_clk_recalc_rate(struct clk_hw *hw, unsigned long p_ra return clk->rate; } -static long omap1_clk_round_rate(struct clk_hw *hw, unsigned long rate, unsigned long *p_rate) +static int omap1_clk_determine_rate(struct clk_hw *hw, + struct clk_rate_request *req) { struct omap1_clk *clk = to_omap1_clk(hw); - if (clk->round_rate != NULL) - return clk->round_rate(clk, rate, p_rate); + if (clk->round_rate != NULL) { + req->rate = clk->round_rate(clk, req->rate, + &req->best_parent_rate); - return omap1_clk_recalc_rate(hw, *p_rate); + return 0; + } + + req->rate = omap1_clk_recalc_rate(hw, req->best_parent_rate); + + return 0; } static int omap1_clk_set_rate(struct clk_hw *hw, unsigned long rate, unsigned long p_rate) @@ -771,7 +778,7 @@ const struct clk_ops omap1_clk_gate_ops = { const struct clk_ops omap1_clk_rate_ops = { .recalc_rate = omap1_clk_recalc_rate, - .round_rate = omap1_clk_round_rate, + .determine_rate = omap1_clk_determine_rate, .set_rate = omap1_clk_set_rate, .init = omap1_clk_init_op, }; @@ -784,7 +791,7 @@ const struct clk_ops omap1_clk_full_ops = { .disable_unused = omap1_clk_disable_unused, #endif .recalc_rate = omap1_clk_recalc_rate, - .round_rate = omap1_clk_round_rate, + .determine_rate = omap1_clk_determine_rate, .set_rate = omap1_clk_set_rate, .init = omap1_clk_init_op, }; -- cgit v1.2.3 From bb676996ed583464504123721195e98a708fbba9 Mon Sep 17 00:00:00 2001 From: Brian Masney Date: Thu, 10 Jul 2025 19:42:17 -0400 Subject: ARM: OMAP2+: clock: convert from round_rate() to determine_rate() The round_rate() clk ops is deprecated, so migrate this driver from round_rate() to determine_rate() using the Coccinelle semantic patch on the cover letter of this series. The change to virt_prcm_set_ops had to be made manually. Signed-off-by: Brian Masney Link: https://lore.kernel.org/r/20250710-arm32-clk-round-rate-v1-2-a9146b77aca9@redhat.com Signed-off-by: Kevin Hilman --- arch/arm/mach-omap2/clkt2xxx_virt_prcm_set.c | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/arch/arm/mach-omap2/clkt2xxx_virt_prcm_set.c b/arch/arm/mach-omap2/clkt2xxx_virt_prcm_set.c index 011076a5952f..96c5cdc718c8 100644 --- a/arch/arm/mach-omap2/clkt2xxx_virt_prcm_set.c +++ b/arch/arm/mach-omap2/clkt2xxx_virt_prcm_set.c @@ -70,8 +70,8 @@ static unsigned long omap2_table_mpu_recalc(struct clk_hw *clk, * Some might argue L3-DDR, others ARM, others IVA. This code is simple and * just uses the ARM rates. */ -static long omap2_round_to_table_rate(struct clk_hw *hw, unsigned long rate, - unsigned long *parent_rate) +static int omap2_determine_rate_to_table(struct clk_hw *hw, + struct clk_rate_request *req) { const struct prcm_config *ptr; long highest_rate; @@ -87,10 +87,12 @@ static long omap2_round_to_table_rate(struct clk_hw *hw, unsigned long rate, highest_rate = ptr->mpu_speed; /* Can check only after xtal frequency check */ - if (ptr->mpu_speed <= rate) + if (ptr->mpu_speed <= req->rate) break; } - return highest_rate; + req->rate = highest_rate; + + return 0; } /* Sets basic clocks based on the specified rate */ @@ -215,7 +217,7 @@ static void omap2xxx_clkt_vps_late_init(void) static const struct clk_ops virt_prcm_set_ops = { .recalc_rate = &omap2_table_mpu_recalc, .set_rate = &omap2_select_table_rate, - .round_rate = &omap2_round_to_table_rate, + .determine_rate = &omap2_determine_rate_to_table, }; /** -- cgit v1.2.3