[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