Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
65 changes: 65 additions & 0 deletions drivers/soc/qcom/cmd-db.c
Original file line number Diff line number Diff line change
Expand Up @@ -267,6 +267,71 @@ enum cmd_db_hw_type cmd_db_read_slave_id(const char *id)
}
EXPORT_SYMBOL_GPL(cmd_db_read_slave_id);

/**
* cmd_db_hw_type_str() - Return the name string for an RPMh accelerator address.
* @addr: RPMh resource address whose slave ID encodes the accelerator type.
*
* Extracts the slave ID from bits [19:16] of @addr and maps it to the
* corresponding cmd_db_hw_type name.
*
* Return: A constant string: "ARC", "VRM", "BCM", or "unknown".
*/
const char *cmd_db_hw_type_str(u32 addr)
{
switch (SLAVE_ID(addr)) {
case CMD_DB_HW_ARC: return "ARC";
case CMD_DB_HW_VRM: return "VRM";
case CMD_DB_HW_BCM: return "BCM";
default: return "unknown";
}
}
EXPORT_SYMBOL_GPL(cmd_db_hw_type_str);

/**
* cmd_db_read_name() - Look up the resource name for a given RPMh address.
* @addr: RPMh resource address to reverse-look up.
* @buf: Output buffer to write the resource name into.
* @len: Size of @buf; must be at least CMD_DB_ID_SIZE + 1.
*
* Iterates the command DB to find the entry whose address matches @addr.
* For VRM resources, which have up to 4 contiguous 4-byte-aligned addresses
* per resource, the match is performed on bits [19:4] so that any of the
* sub-addresses resolve to the same resource name.
*
* Return: 0 on success, -ENODEV if not found or DB not ready.
*/
int cmd_db_read_name(u32 addr, char *buf, size_t len)
{
const struct rsc_hdr *rsc_hdr;
const struct entry_header *ent;
int ret, i, j;

ret = cmd_db_ready();
if (ret)
return ret;

for (i = 0; i < MAX_SLV_ID; i++) {
rsc_hdr = &cmd_db_header->header[i];
if (!rsc_hdr->slv_id)
break;

ent = rsc_to_entry_header(rsc_hdr);
for (j = 0; j < le16_to_cpu(rsc_hdr->cnt); j++, ent++) {
u32 ent_addr = le32_to_cpu(ent->addr);

if (cmd_db_match_resource_addr(ent_addr, addr)) {
snprintf(buf, len, "%.*s",
(int)strnlen(ent->id, sizeof(ent->id)),
ent->id);
return 0;
}
}
}

return -ENODEV;
}
EXPORT_SYMBOL_GPL(cmd_db_read_name);

#ifdef CONFIG_DEBUG_FS
static int cmd_db_debugfs_dump(struct seq_file *seq, void *p)
{
Expand Down
5 changes: 5 additions & 0 deletions drivers/soc/qcom/rpmh-internal.h
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,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.
Expand All @@ -123,6 +126,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;
Expand All @@ -141,6 +145,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);
Expand Down
90 changes: 90 additions & 0 deletions drivers/soc/qcom/rpmh-rsc.c
Original file line number Diff line number Diff line change
Expand Up @@ -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>
Expand Down Expand Up @@ -89,6 +90,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)

Expand Down Expand Up @@ -684,6 +686,92 @@ 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]);
pr_warn("Request: tcs-in-use:%s state=%d wait_for_compl=%u\n",
in_use ? "YES" : "NO",
req->state, req->wait_for_compl);
pr_warn("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 SoC 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));
pr_warn("\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;

pr_err("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) {
pr_err("No IRQ data for RSC:%s\n", drv->name);
return;
}

irq_get_irqchip_state(drv->irq, IRQCHIP_STATE_PENDING, &gic_irq_sts);
pr_warn("HW IRQ %lu is %s at GIC\n", rsc_irq_data->hwirq,
gic_irq_sts ? "PENDING" : "NOT PENDING");
pr_warn("Completion is %s\n",
completion_done(compl) ? "done" : "not done");

if ((busy && !gic_irq_sts) || !aoss_irq_sts)
pr_err("ERROR: Accelerator(s) at AOSS did not respond\n");
else if (gic_irq_sts)
pr_err("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.
Expand Down Expand Up @@ -1092,6 +1180,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
Expand Down
4 changes: 4 additions & 0 deletions drivers/soc/qcom/rpmh.c
Original file line number Diff line number Diff line change
Expand Up @@ -255,6 +255,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);
Expand All @@ -266,6 +267,8 @@ int rpmh_write(const struct device *dev, enum rpmh_state state,
return ret;

ret = wait_for_completion_timeout(&compl, RPMH_TIMEOUT_MS);
if (!ret)
rpmh_rsc_debug(ctrlr_to_drv(ctrlr), &compl);
WARN_ON(!ret);
return (ret > 0) ? 0 : -ETIMEDOUT;
}
Expand Down Expand Up @@ -383,6 +386,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;
Expand Down
10 changes: 10 additions & 0 deletions include/soc/qcom/cmd-db.h
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@

#include <linux/err.h>

#define CMD_DB_ID_SIZE 8

enum cmd_db_hw_type {
CMD_DB_HW_INVALID = 0,
CMD_DB_HW_MIN = 3,
Expand All @@ -29,6 +31,9 @@ bool cmd_db_match_resource_addr(u32 addr1, u32 addr2);
enum cmd_db_hw_type cmd_db_read_slave_id(const char *resource_id);

int cmd_db_ready(void);

const char *cmd_db_hw_type_str(u32 addr);
int cmd_db_read_name(u32 addr, char *buf, size_t len);
#else
static inline u32 cmd_db_read_addr(const char *resource_id)
{ return 0; }
Expand All @@ -44,5 +49,10 @@ static inline enum cmd_db_hw_type cmd_db_read_slave_id(const char *resource_id)

static inline int cmd_db_ready(void)
{ return -ENODEV; }

static inline const char *cmd_db_hw_type_str(u32 addr)
{ return "unknown"; }
static inline int cmd_db_read_name(u32 addr, char *buf, size_t len)
{ return -ENODEV; }
#endif /* CONFIG_QCOM_COMMAND_DB */
#endif /* __QCOM_COMMAND_DB_H__ */
Loading