Hi Simon,

Thank you for your feedback. The following patch series addresses the same issue that we had reported:

https://lore.kernel.org/u-boot/[email protected]

We can proceed with this series going forward.

Regards,
Balaji

On 1/8/2026 11:12 PM, Simon Glass wrote:
Hi Balaji,

On Thu, 8 Jan 2026 at 03:17, Balaji Selvanathan
<[email protected]> wrote:
Add cleanup mechanism to clear RPMH TCS hardware state before
booting the kernel. Without this cleanup, leftover U-Boot TCS
configurations cause timeout errors during kernel RPMH driver
initialization.

The cleanup clears CMD_STATUS, CMD_WAIT_FOR_CMPL, CMD_ENABLE,
CONTROL, and IRQ_STATUS registers for all TCS.

Signed-off-by: Aswin Murugan <[email protected]>
Signed-off-by: Balaji Selvanathan <[email protected]>
---
  drivers/soc/qcom/rpmh-rsc.c | 101 ++++++++++++++++++++++++++++++++++++
  include/soc/qcom/rpmh.h     |   9 ++++
  2 files changed, 110 insertions(+)

diff --git a/drivers/soc/qcom/rpmh-rsc.c b/drivers/soc/qcom/rpmh-rsc.c
index aee9e55194e..772be401f0d 100644
--- a/drivers/soc/qcom/rpmh-rsc.c
+++ b/drivers/soc/qcom/rpmh-rsc.c
@@ -471,6 +471,106 @@ static int rpmh_probe_tcs_config(struct udevice *dev, 
struct rsc_drv *drv)
         return 0;
  }

+/**
+ * rpmh_rsc_clear_tcs() - Clear TCS state before kernel handoff
+ * @drv: The RSC controller
+ *
+ * Clears all TCS hardware state to ensure kernel starts with clean state.
+ * This prevents timeout errors during kernel RPMH initialization by clearing:
+ * - CMD_STATUS: Leftover ISSUED/COMPLETED flags
+ * - CMD_WAIT_FOR_CMPL: Completion wait configuration
+ * - CMD_ENABLE: Command slot enables
+ * - CONTROL: AMC mode bits
+ * - IRQ_STATUS: Pending interrupts
+ */
+static void rpmh_rsc_clear_tcs(struct rsc_drv *drv)
+{
+       int i, j, ncpt, pending_cmds = 0;
+       u32 status, irq_status;
+
+       ncpt = drv->tcs[ACTIVE_TCS].ncpt;
+
+       for (i = 0; i < drv->num_tcs; i++) {
+               for (j = 0; j < ncpt; j++) {
+                       status = read_tcs_cmd(drv, 
drv->regs[RSC_DRV_CMD_STATUS], i, j);
+                       if (status)
+                               pending_cmds++;
+               }
+
+               /* Clear completion wait, command enable, and control registers 
*/
+               write_tcs_reg(drv, drv->regs[RSC_DRV_CMD_WAIT_FOR_CMPL], i, 0);
+               write_tcs_reg(drv, drv->regs[RSC_DRV_CMD_ENABLE], i, 0);
+               write_tcs_reg(drv, drv->regs[RSC_DRV_CONTROL], i, 0);
+       }
+
+       /* Clear any pending IRQ status */
+       irq_status = readl(drv->tcs_base + drv->regs[RSC_DRV_IRQ_STATUS]);
+       if (irq_status)
+               writel(irq_status, drv->tcs_base + 
drv->regs[RSC_DRV_IRQ_CLEAR]);
+
+       log_debug("RPMH: Cleared %d TCS for %s (pending: %d cmds, IRQ: 0x%x)\n",
+                 drv->num_tcs, drv->name, pending_cmds, irq_status);
+}
+
+/**
+ * rpmh_rsc_cleanup_all() - Public function to cleanup all RPMH controllers
+ *
+ * This function should be called before booting the kernel to ensure all
+ * RPMH controllers have clean TCS state. Can be called from 
board_quiesce_devices()
+ * or directly before kernel boot.
+ */
+void rpmh_rsc_cleanup_all(void)
+{
+       struct udevice *dev;
+       struct uclass *uc;
+       int ret, count = 0;
+
+       ret = uclass_get(UCLASS_MISC, &uc);
+       if (ret) {
+               log_err("RPMH: Failed to get MISC uclass: %d\n", ret);
+               return;
+       }
+
+       uclass_foreach_dev(dev, uc) {
+               if (device_is_compatible(dev, "qcom,rpmh-rsc")) {
+                       struct rsc_drv *drv = dev_get_priv(dev);
+
+                       if (drv && drv->num_tcs > 0) {
+                               rpmh_rsc_clear_tcs(drv);
+                               count++;
+                       }
+               }
+       }
+
+       if (count > 0)
+               log_debug("RPMH: Cleaned up %d controller(s)\n", count);
+       else
+               log_warning("RPMH: No controllers found to clean up\n");
+}
+
+/**
+ * rpmh_rsc_remove() - Cleanup before handing off to kernel
+ * @dev: The device
+ *
+ * Called before booting kernel to ensure clean TCS state. This prevents
+ * kernel RPMH driver from encountering busy/configured TCS that could
+ * cause timeout errors during initialization.
+ *
+ * Return: 0 on success
+ */
+static int rpmh_rsc_remove(struct udevice *dev)
+{
+       struct rsc_drv *drv = dev_get_priv(dev);
+
+       if (!drv)
+               return 0;
+
+       /* Clear all TCS configurations to provide clean state for kernel */
+       rpmh_rsc_clear_tcs(drv);
+
+       return 0;
+}
+
  static int rpmh_rsc_probe(struct udevice *dev)
  {
         ofnode dn = dev_ofnode(dev);
@@ -539,6 +639,7 @@ U_BOOT_DRIVER(qcom_rpmh_rsc) = {
         .id             = UCLASS_MISC,
         .priv_auto      = sizeof(struct rsc_drv),
         .probe          = rpmh_rsc_probe,
+       .remove         = rpmh_rsc_remove,
         .of_match       = qcom_rpmh_ids,
         /* rpmh is under CLUSTER_PD which we don't support, so skip trying to 
enable PDs */
         .flags          = DM_FLAG_DEFAULT_PD_CTRL_OFF,
add DM_REMOVE_OS_PREPARE in there too so it will cleanup

diff --git a/include/soc/qcom/rpmh.h b/include/soc/qcom/rpmh.h
index 3421fbf1ee3..da3b1517392 100644
--- a/include/soc/qcom/rpmh.h
+++ b/include/soc/qcom/rpmh.h
@@ -25,4 +25,13 @@ static inline int rpmh_write(const struct device *dev, enum 
rpmh_state state,
  /* u-boot: no multithreading */
  #define rpmh_write_async(dev, state, cmd, n) rpmh_write(dev, state, cmd, n)

+/**
+ * rpmh_rsc_cleanup_all() - Cleanup all RPMH controllers before kernel boot
+ *
+ * This function clears TCS (Trigger Command Set) hardware state for all
+ * RPMH controllers to ensure the kernel starts with clean state. Should
+ * be called from board_quiesce_devices() before booting the kernel.
+ */
+void rpmh_rsc_cleanup_all(void);
I'm not sure who calls this, but each device will be removed before
booting if you use the DM_REMOVE_OS_PREPARE flag

+
  #endif /* __SOC_QCOM_RPMH_H__ */
--
2.34.1

Regards,
Simon

Reply via email to