From: Maulik Shah <maulik.shah@oss.qualcomm.com>
To: Bjorn Andersson <andersson@kernel.org>,
Konrad Dybcio <konradybcio@kernel.org>,
Abel Vesa <abelvesa@kernel.org>
Cc: linux-arm-msm@vger.kernel.org, linux-kernel@vger.kernel.org,
Maulik Shah <maulik.shah@oss.qualcomm.com>,
"Raju P.L.S.S.S.N" <quic_rplsssn@quicinc.com>,
"Raju P.L.S.S.S.N" <quic_rplsssn@quicinc.com>
Subject: [PATCH v4 3/3] soc: qcom: rpmh-rsc: Output debug information from RSC
Date: Sun, 13 Sep 2026 18:24:25 +0530 [thread overview]
Message-ID: <20260913-rpmh-timeout-debug-v1-v4-3-e94d3e416ea1@oss.qualcomm.com> (raw)
In-Reply-To: <20260913-rpmh-timeout-debug-v1-v4-0-e94d3e416ea1@oss.qualcomm.com>
From: "Raju P.L.S.S.S.N" <rplsssn@codeaurora.org>
When an RPMh transfer times out there is no visibility into which
TCS was stuck, what commands it was carrying, or whether the
completion IRQ was pending at the GIC. Add rpmh_rsc_debug() to
capture this state at timeout:
- Iterates all in-use TCSes and dumps per-TCS control register
state (controller status, AMC mode, IRQ status) and per-command
register state (address, data, message ID, completion status).
The accelerator type (ARC/VRM/BCM) is decoded from the address
using cmd_db_hw_type_str().
- Queries the GIC pending state for the RSC IRQ via
irq_get_irqchip_state() to distinguish two failure modes:
* AOSS firmware did not respond (IRQ never fired)
* Linux lockup (IRQ pending at GIC but handler never ran)
- Reports the completion object state to cross-check with the
IRQ status.
Store the IRQ number in struct rsc_drv to enable the GIC query.
Wire rpmh_rsc_debug() into the rpmh_write(), rpmh_write_batch(), and
rpmh_read() timeout paths so that all three blocking RPMh callers
produce the same structured TCS/GIC diagnostic output on timeout.
Signed-off-by: Raju P.L.S.S.S.N <rplsssn@codeaurora.org>
Co-developed-by: Maulik Shah <maulik.shah@oss.qualcomm.com>
Signed-off-by: Maulik Shah <maulik.shah@oss.qualcomm.com>
---
drivers/soc/qcom/rpmh-internal.h | 5 +++
drivers/soc/qcom/rpmh-rsc.c | 94 ++++++++++++++++++++++++++++++++++++++++
drivers/soc/qcom/rpmh.c | 14 +++++-
3 files changed, 111 insertions(+), 2 deletions(-)
diff --git a/drivers/soc/qcom/rpmh-internal.h b/drivers/soc/qcom/rpmh-internal.h
index 39441a25af9b..4e465bdb5a27 100644
--- a/drivers/soc/qcom/rpmh-internal.h
+++ b/drivers/soc/qcom/rpmh-internal.h
@@ -104,6 +104,9 @@ struct rsc_ver {
* @tcs_base: Start address of the TCS registers in this controller.
* @id: Instance id in the controller (Direct Resource Voter).
* @num_tcs: Number of TCSes in this DRV.
+ * @irq: IRQ number used by the TCS completion interrupt;
+ * stored to allow querying GIC pending state for
+ * timeout diagnostics.
* @rsc_pm: CPU PM notifier for controller.
* Used when solver mode is not present.
* @cpus_in_pm: Number of CPUs not in idle power collapse.
@@ -131,6 +134,7 @@ struct rsc_drv {
void __iomem *tcs_base;
int id;
int num_tcs;
+ int irq;
struct notifier_block rsc_pm;
struct notifier_block genpd_nb;
atomic_t cpus_in_pm;
@@ -149,6 +153,7 @@ int rpmh_rsc_write_ctrl_data(struct rsc_drv *drv,
const struct tcs_request *msg);
void rpmh_rsc_invalidate(struct rsc_drv *drv);
void rpmh_rsc_write_next_wakeup(struct rsc_drv *drv);
+void rpmh_rsc_debug(struct rsc_drv *drv, struct completion *compl);
void rpmh_tx_done(const struct tcs_request *msg);
int rpmh_flush(struct rpmh_ctrlr *ctrlr);
diff --git a/drivers/soc/qcom/rpmh-rsc.c b/drivers/soc/qcom/rpmh-rsc.c
index c4542318eac4..2aa5e035d3f1 100644
--- a/drivers/soc/qcom/rpmh-rsc.c
+++ b/drivers/soc/qcom/rpmh-rsc.c
@@ -10,6 +10,7 @@
#include <linux/cpu_pm.h>
#include <linux/delay.h>
#include <linux/interrupt.h>
+#include <linux/irq.h>
#include <linux/io.h>
#include <linux/iopoll.h>
#include <linux/kernel.h>
@@ -25,6 +26,7 @@
#include <linux/pm_runtime.h>
#include <linux/slab.h>
#include <linux/spinlock.h>
+#include <linux/string_choices.h>
#include <linux/wait.h>
#include <clocksource/arm_arch_timer.h>
@@ -89,6 +91,7 @@ enum {
#define CMD_MSGID_LEN 8
#define CMD_MSGID_RESP_REQ BIT(8)
#define CMD_MSGID_WRITE BIT(16)
+#define CMD_STATUS_TRIGGERED BIT(0)
#define CMD_STATUS_ISSUED BIT(8)
#define CMD_STATUS_COMPL BIT(16)
@@ -693,6 +696,95 @@ int rpmh_rsc_send_data(struct rsc_drv *drv, const struct tcs_request *msg)
return 0;
}
+static void print_tcs_info(struct rsc_drv *drv, int tcs_id,
+ bool *aoss_irq_sts)
+{
+ const struct tcs_request *req = get_req_from_tcs(drv, tcs_id);
+ unsigned long cmds_enabled;
+ char rname[CMD_DB_ID_SIZE + 1];
+ u32 addr, data, msgid, sts, irq_sts;
+ bool in_use = test_bit(tcs_id, drv->tcs_in_use);
+ int i;
+
+ sts = read_tcs_reg(drv, drv->regs[RSC_DRV_STATUS], tcs_id);
+ cmds_enabled = read_tcs_reg(drv, drv->regs[RSC_DRV_CMD_ENABLE], tcs_id);
+ if (!cmds_enabled)
+ return;
+
+ if (!req)
+ goto print_tcs_data;
+
+ data = read_tcs_reg(drv, drv->regs[RSC_DRV_CONTROL], tcs_id);
+ irq_sts = readl_relaxed(drv->tcs_base + drv->regs[RSC_DRV_IRQ_STATUS]);
+ dev_warn(drv->dev, "Request: tcs-in-use:%s state=%d wait_for_compl=%u\n",
+ str_yes_no(in_use), req->state, req->wait_for_compl);
+ dev_warn(drv->dev, "TCS=%d [ctrlr-sts:%s amc-mode:0x%x irq-sts:%s]\n",
+ tcs_id, sts ? "IDLE" : "BUSY", data,
+ (irq_sts & BIT(tcs_id)) ? "DONE" : "WAITING");
+
+ *aoss_irq_sts = !!(irq_sts & BIT(tcs_id));
+
+print_tcs_data:
+ /* All TCSes on a given RSC have the same number of commands per TCS. */
+ for_each_set_bit(i, &cmds_enabled, drv->tcs[0].ncpt) {
+ addr = read_tcs_cmd(drv, drv->regs[RSC_DRV_CMD_ADDR], tcs_id, i);
+ data = read_tcs_cmd(drv, drv->regs[RSC_DRV_CMD_DATA], tcs_id, i);
+ msgid = read_tcs_cmd(drv, drv->regs[RSC_DRV_CMD_MSGID], tcs_id, i);
+ sts = read_tcs_cmd(drv, drv->regs[RSC_DRV_CMD_STATUS], tcs_id, i);
+ cmd_db_read_name(addr, rname, sizeof(rname));
+ dev_warn(drv->dev,
+ "\tCMD=%d [addr=0x%x(%s/%s) data=0x%x %s sts=%s%s%s]\n",
+ i, addr, cmd_db_hw_type_str(addr), rname, data,
+ (msgid & CMD_MSGID_RESP_REQ) ? "resp-required" :
+ "fire-n-forget",
+ (sts & CMD_STATUS_TRIGGERED) ? "triggered" : "-",
+ (sts & CMD_STATUS_ISSUED) ? "+sent-to-aoss" : "",
+ (sts & CMD_STATUS_COMPL) ? "+resp-received" : "");
+ }
+}
+
+/**
+ * rpmh_rsc_debug() - Dump debug information on a transfer timeout.
+ * @drv: The RSC controller.
+ * @compl: The completion object that timed out.
+ *
+ * Dumps TCS state for all in-use TCSes and reports which accelerators
+ * did not respond, to aid in diagnosing RPMH timeout failures.
+ */
+void rpmh_rsc_debug(struct rsc_drv *drv, struct completion *compl)
+{
+ struct irq_data *rsc_irq_data = irq_get_irq_data(drv->irq);
+ bool gic_irq_sts, aoss_irq_sts = false;
+ int i, busy = 0;
+
+ dev_err(drv->dev, "Timeout on RSC %s\n", drv->name);
+
+ for (i = 0; i < drv->num_tcs; i++) {
+ if (!test_bit(i, drv->tcs_in_use))
+ continue;
+ busy++;
+ print_tcs_info(drv, i, &aoss_irq_sts);
+ }
+
+ if (!rsc_irq_data) {
+ dev_err(drv->dev, "No IRQ data for RSC:%s\n", drv->name);
+ return;
+ }
+
+ irq_get_irqchip_state(drv->irq, IRQCHIP_STATE_PENDING, &gic_irq_sts);
+ dev_warn(drv->dev, "HW IRQ %lu is %s at GIC\n", rsc_irq_data->hwirq,
+ gic_irq_sts ? "PENDING" : "NOT PENDING");
+ dev_warn(drv->dev, "Completion is %s\n",
+ completion_done(compl) ? "done" : "not done");
+
+ if ((busy && !gic_irq_sts) || !aoss_irq_sts)
+ dev_err(drv->dev,
+ "ERROR: Accelerator(s) at AOSS did not respond\n");
+ else if (gic_irq_sts)
+ dev_err(drv->dev,
+ "ERROR: IRQ pending at GIC but not handled within timeout\n");
+}
+
/**
* find_slots() - Find a place to write the given message.
* @tcs: The tcs group to search.
@@ -1114,6 +1206,8 @@ static int rpmh_rsc_probe(struct platform_device *pdev)
if (ret)
return ret;
+ drv->irq = irq;
+
/*
* CPU PM/genpd notification are not required for controllers that support
* 'HW solver' mode where they can be in autonomous mode executing low
diff --git a/drivers/soc/qcom/rpmh.c b/drivers/soc/qcom/rpmh.c
index 360242a315e3..1f10b85514f1 100644
--- a/drivers/soc/qcom/rpmh.c
+++ b/drivers/soc/qcom/rpmh.c
@@ -224,7 +224,7 @@ static int __fill_rpmh_msg(struct rpmh_request *req, enum rpmh_state state,
}
/**
- * rpmh_read: Read a resource value
+ * rpmh_read() - Read a resource value
*
* @dev: The device making the request
* @cmd: The payload having address of resource to read
@@ -240,6 +240,7 @@ int rpmh_read(const struct device *dev, struct tcs_cmd *cmd)
{
DECLARE_COMPLETION_ONSTACK(compl);
DEFINE_RPMH_MSG_ONSTACK(dev, RPMH_ACTIVE_ONLY_STATE, &compl, rpm_msg);
+ struct rpmh_ctrlr *ctrlr = get_rpmh_ctrlr(dev);
int ret;
ret = __fill_rpmh_msg(&rpm_msg, RPMH_ACTIVE_ONLY_STATE, cmd, 1, true);
@@ -251,6 +252,10 @@ int rpmh_read(const struct device *dev, struct tcs_cmd *cmd)
return ret;
ret = wait_for_completion_timeout(&compl, RPMH_TIMEOUT_MS);
+ if (!ret) {
+ rpmh_rsc_debug(ctrlr_to_drv(ctrlr), &compl);
+ WARN_ON(1);
+ }
cmd[0].data = rpm_msg.cmd[0].data;
return (ret > 0) ? 0 : -ETIMEDOUT;
@@ -308,6 +313,7 @@ int rpmh_write(const struct device *dev, enum rpmh_state state,
{
DECLARE_COMPLETION_ONSTACK(compl);
DEFINE_RPMH_MSG_ONSTACK(dev, state, &compl, rpm_msg);
+ struct rpmh_ctrlr *ctrlr = get_rpmh_ctrlr(dev);
int ret;
ret = __fill_rpmh_msg(&rpm_msg, state, cmd, n, false);
@@ -319,7 +325,10 @@ int rpmh_write(const struct device *dev, enum rpmh_state state,
return ret;
ret = wait_for_completion_timeout(&compl, RPMH_TIMEOUT_MS);
- WARN_ON(!ret);
+ if (!ret) {
+ rpmh_rsc_debug(ctrlr_to_drv(ctrlr), &compl);
+ WARN_ON(1);
+ }
return (ret > 0) ? 0 : -ETIMEDOUT;
}
EXPORT_SYMBOL_GPL(rpmh_write);
@@ -438,6 +447,7 @@ int rpmh_write_batch(const struct device *dev, enum rpmh_state state,
* the completion that we're going to free once
* we've returned from this function.
*/
+ rpmh_rsc_debug(ctrlr_to_drv(ctrlr), &compls[i]);
WARN_ON(1);
ret = -ETIMEDOUT;
goto exit;
--
2.43.0
prev parent reply other threads:[~2026-09-13 12:55 UTC|newest]
Thread overview: 4+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-09-13 12:54 [PATCH v4 0/3] " Maulik Shah
2026-09-13 12:54 ` [PATCH v4 1/3] soc: qcom: cmd-db: export RPMh accelerator type string helper Maulik Shah
2026-09-13 12:54 ` [PATCH v4 2/3] soc: qcom: cmd-db: add reverse address-to-name lookup Maulik Shah
2026-09-13 12:54 ` Maulik Shah [this message]
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20260913-rpmh-timeout-debug-v1-v4-3-e94d3e416ea1@oss.qualcomm.com \
--to=maulik.shah@oss.qualcomm.com \
--cc=abelvesa@kernel.org \
--cc=andersson@kernel.org \
--cc=konradybcio@kernel.org \
--cc=linux-arm-msm@vger.kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=quic_rplsssn@quicinc.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®