[PATCH v1 1/2] soc: qcom: rpmh-rsc: Clear TCS state before kernel boot

Balaji Selvanathan balaji.selvanathan at oss.qualcomm.com
Thu Jan 8 11:17:20 CET 2026


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 <aswin.murugan at oss.qualcomm.com>
Signed-off-by: Balaji Selvanathan <balaji.selvanathan at oss.qualcomm.com>
---
 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,
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);
+
 #endif /* __SOC_QCOM_RPMH_H__ */
-- 
2.34.1



More information about the U-Boot mailing list