From 340f786f9b0e18941ac2cfb741338749d81d42fe Mon Sep 17 00:00:00 2001 From: "Raju P.L.S.S.S.N" Date: Wed, 25 Jul 2018 17:20:41 +0530 Subject: [PATCH 1/2] drivers: qcom: rpmh-rsc: output debug information from RSC Output the TCS state when the debug api is invoked. The state of the TCS, the contents and the IRQ status is presented. Additionally, crash the system if any TCS is busy to help with the debug. Change-Id: I7ef5480cc6da7208d5f82351d863561fcc1abf68 Signed-off-by: Raju P.L.S.S.S.N Signed-off-by: Lina Iyer --- drivers/soc/qcom/rpmh-internal.h | 3 + drivers/soc/qcom/rpmh-rsc.c | 117 ++++++++++++++++++++++++++++++- drivers/soc/qcom/rpmh.c | 10 ++- 3 files changed, 125 insertions(+), 5 deletions(-) diff --git a/drivers/soc/qcom/rpmh-internal.h b/drivers/soc/qcom/rpmh-internal.h index ce12ed94be07..8cea5717552a 100644 --- a/drivers/soc/qcom/rpmh-internal.h +++ b/drivers/soc/qcom/rpmh-internal.h @@ -93,6 +93,7 @@ struct rpmh_ctrlr { * @tcs_in_use: s/w state of the TCS * @lock: synchronize state of the controller * @client: handle to the DRV's client. + * @irq: IRQ at gic */ struct rsc_drv { const char *name; @@ -105,6 +106,7 @@ struct rsc_drv { DECLARE_BITMAP(tcs_in_use, MAX_TCS_NR); spinlock_t lock; struct rpmh_ctrlr client; + int irq; }; int rpmh_rsc_send_data(struct rsc_drv *drv, const struct tcs_request *msg); @@ -117,4 +119,5 @@ int rpmh_rsc_write_pdc_data(struct rsc_drv *drv, const struct tcs_request *msg); void rpmh_tx_done(const struct tcs_request *msg, int r); +void rpmh_rsc_debug(struct rsc_drv *drv); #endif /* __RPM_INTERNAL_H__ */ diff --git a/drivers/soc/qcom/rpmh-rsc.c b/drivers/soc/qcom/rpmh-rsc.c index aad70b13e49c..efb3a28b3759 100644 --- a/drivers/soc/qcom/rpmh-rsc.c +++ b/drivers/soc/qcom/rpmh-rsc.c @@ -65,6 +65,16 @@ #define RSC_PDC_DRV_DATA 0x38 #define RSC_PDC_DATA_OFFSET 0x08 +#define ACCL_TYPE(addr) ((addr >> 16) & 0xF) +#define NR_ACCL_TYPES 3 + +static const char * const accl_str[] = { + "", "", "", "CLK", "VREG", "BUS", +}; + +static struct rsc_drv *__rsc_drv[2]; +static int __rsc_count; + static u32 read_tcs_reg(struct rsc_drv *drv, int reg, int tcs_id, int cmd_id) { return readl_relaxed(drv->tcs_base + reg + RSC_DRV_TCS_OFFSET * tcs_id + @@ -450,8 +460,8 @@ int rpmh_rsc_send_data(struct rsc_drv *drv, const struct tcs_request *msg) do { ret = tcs_write(drv, msg); if (ret == -EBUSY) { - pr_info_ratelimited("TCS Busy, retrying RPMH message send: addr=%#x\n", - msg->cmds[0].addr); + pr_info_ratelimited("DRV:%s TCS Busy, retrying RPMH message send: addr=%#x\n", + drv->name, msg->cmds[0].addr); udelay(10); } } while (ret == -EBUSY); @@ -624,6 +634,106 @@ int rpmh_rsc_write_pdc_data(struct rsc_drv *drv, const struct tcs_request *msg) return 0; } +static struct tcs_group *get_tcs_from_index(struct rsc_drv *drv, int tcs_id) +{ + unsigned int i; + + for (i = 0; i < TCS_TYPE_NR; i++) { + if (drv->tcs[i].mask & BIT(tcs_id)) + return &drv->tcs[i]; + } + + return NULL; +} + +static void print_tcs_info(struct rsc_drv *drv, int tcs_id, unsigned long *accl) +{ + struct tcs_group *tcs_grp = get_tcs_from_index(drv, tcs_id); + const struct tcs_request *req = get_req_from_tcs(drv, tcs_id); + unsigned long cmds_enabled; + u32 addr, data, msgid, sts, irq_sts; + bool in_use = test_bit(tcs_id, drv->tcs_in_use); + int i; + + if (!tcs_grp || !req) + return; + + sts = read_tcs_reg(drv, RSC_DRV_STATUS, tcs_id, 0); + cmds_enabled = read_tcs_reg(drv, RSC_DRV_CMD_ENABLE, tcs_id, 0); + if (!cmds_enabled) + return; + + data = read_tcs_reg(drv, RSC_DRV_CONTROL, tcs_id, 0); + irq_sts = read_tcs_reg(drv, RSC_DRV_IRQ_STATUS, 0, 0); + pr_warn("Request: tcs-in-use:%s active_tcs=%s(%d) state=%d wait_for_compl=%u]\n", + (in_use ? "YES" : "NO"), + ((tcs_grp->type == ACTIVE_TCS) ? "YES" : "NO"), + tcs_grp->type, 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)) ? "COMPLETED" : "PENDING"); + + for_each_set_bit(i, &cmds_enabled, MAX_CMDS_PER_TCS) { + addr = read_tcs_reg(drv, RSC_DRV_CMD_ADDR, tcs_id, i); + data = read_tcs_reg(drv, RSC_DRV_CMD_DATA, tcs_id, i); + msgid = read_tcs_reg(drv, RSC_DRV_CMD_MSGID, tcs_id, i); + sts = read_tcs_reg(drv, RSC_DRV_CMD_STATUS, tcs_id, i); + pr_warn("\tCMD=%d [addr=0x%x data=0x%x hdr=0x%x sts=0x%x enabled=1]\n", + i, addr, data, msgid, sts); + if (!(sts & CMD_STATUS_ISSUED)) + continue; + if (!(sts & CMD_STATUS_COMPL)) + *accl |= BIT(ACCL_TYPE(addr)); + } +} + +void rpmh_rsc_debug(struct rsc_drv *drv) +{ + struct irq_data *rsc_irq_data = irq_get_irq_data(drv->irq); + bool irq_sts; + int i; + int busy = 0; + unsigned long accl = 0; + char str[20] = ""; + + pr_warn("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, &accl); + } + + 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, &irq_sts); + pr_warn("HW IRQ %lu is %s at GIC\n", rsc_irq_data->hwirq, + irq_sts ? "PENDING" : "NOT PENDING"); + + for_each_set_bit(i, &accl, ARRAY_SIZE(accl_str)) { + strlcat(str, accl_str[i], sizeof(str)); + strlcat(str, " ", sizeof(str)); + } + + if (busy && !irq_sts) + pr_warn("ERROR:Accelerator(s) { %s } at AOSS did not respond\n", + str); + else if (irq_sts) + pr_warn("ERROR:Possible lockup in Linux\n"); + + /* + * The TCS(s) are busy waiting, we have no way to recover from this. + * If this debug function is called, we assume it's because timeout + * has happened. + * Crash and report. + */ + BUG_ON(busy); +} + static int rpmh_probe_tcs_config(struct platform_device *pdev, struct rsc_drv *drv) { @@ -760,6 +870,8 @@ static int rpmh_rsc_probe(struct platform_device *pdev) if (irq < 0) return irq; + drv->irq = irq; + ret = devm_request_irq(&pdev->dev, irq, tcs_tx_done, IRQF_TRIGGER_HIGH | IRQF_NO_SUSPEND, drv->name, drv); @@ -774,6 +886,7 @@ static int rpmh_rsc_probe(struct platform_device *pdev) INIT_LIST_HEAD(&drv->client.batch_cache); dev_set_drvdata(&pdev->dev, drv); + __rsc_drv[__rsc_count++] = drv; return devm_of_platform_populate(&pdev->dev); } diff --git a/drivers/soc/qcom/rpmh.c b/drivers/soc/qcom/rpmh.c index b85d2eb2f06c..845d62b448bb 100644 --- a/drivers/soc/qcom/rpmh.c +++ b/drivers/soc/qcom/rpmh.c @@ -322,8 +322,12 @@ 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); - return (ret > 0) ? 0 : -ETIMEDOUT; + if (!ret) { + rpmh_rsc_debug(ctrlr_to_drv(ctrlr)); + return -ETIMEDOUT; + } + + return 0; } EXPORT_SYMBOL(rpmh_write); @@ -457,7 +461,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. */ - WARN_ON(1); + rpmh_rsc_debug(ctrlr_to_drv(ctrlr)); ret = -ETIMEDOUT; goto exit; } From 1ef26f82e16c64eae53a103e5734550154f18a98 Mon Sep 17 00:00:00 2001 From: Lina Iyer Date: Wed, 5 Sep 2018 15:51:11 -0600 Subject: [PATCH 2/2] drivers: qcom: rpmh: Add standalone mode support for RPMH Allow RPMH requests to succeed even when the always-On subsystem is not available. In standalone mode, requests are never sent to the TCS mailbox driver. The command DB provides the information to make the decision to run in standalone mode or not. Change-Id: Ia2bc5024da0d7e6607347b1a481b2feb9584059a Signed-off-by: Lina Iyer --- drivers/soc/qcom/rpmh-internal.h | 2 ++ drivers/soc/qcom/rpmh-rsc.c | 7 +++++++ drivers/soc/qcom/rpmh.c | 21 +++++++++++++++++++++ 3 files changed, 30 insertions(+) diff --git a/drivers/soc/qcom/rpmh-internal.h b/drivers/soc/qcom/rpmh-internal.h index 8cea5717552a..e11b3e11fd7d 100644 --- a/drivers/soc/qcom/rpmh-internal.h +++ b/drivers/soc/qcom/rpmh-internal.h @@ -109,6 +109,8 @@ struct rsc_drv { int irq; }; +extern bool rpmh_standalone; + int rpmh_rsc_send_data(struct rsc_drv *drv, const struct tcs_request *msg); int rpmh_rsc_write_ctrl_data(struct rsc_drv *drv, const struct tcs_request *msg); diff --git a/drivers/soc/qcom/rpmh-rsc.c b/drivers/soc/qcom/rpmh-rsc.c index efb3a28b3759..c9ce4ca0c2e7 100644 --- a/drivers/soc/qcom/rpmh-rsc.c +++ b/drivers/soc/qcom/rpmh-rsc.c @@ -75,6 +75,8 @@ static const char * const accl_str[] = { static struct rsc_drv *__rsc_drv[2]; static int __rsc_count; +bool rpmh_standalone; + static u32 read_tcs_reg(struct rsc_drv *drv, int reg, int tcs_id, int cmd_id) { return readl_relaxed(drv->tcs_base + reg + RSC_DRV_TCS_OFFSET * tcs_id + @@ -846,6 +848,11 @@ static int rpmh_rsc_probe(struct platform_device *pdev) return ret; } + rpmh_standalone = cmd_db_is_standalone(); + if (rpmh_standalone) + dev_info(&pdev->dev, "RPMH is running in standalone mode.\n"); + + drv = devm_kzalloc(&pdev->dev, sizeof(*drv), GFP_KERNEL); if (!drv) return -ENOMEM; diff --git a/drivers/soc/qcom/rpmh.c b/drivers/soc/qcom/rpmh.c index 845d62b448bb..11698a49f513 100644 --- a/drivers/soc/qcom/rpmh.c +++ b/drivers/soc/qcom/rpmh.c @@ -270,6 +270,9 @@ int rpmh_write_async(const struct device *dev, enum rpmh_state state, struct rpmh_ctrlr *ctrlr = get_rpmh_ctrlr(dev); int ret; + if (rpmh_standalone) + return 0; + ret = check_ctrlr_state(ctrlr, state); if (ret) return ret; @@ -310,6 +313,9 @@ int rpmh_write(const struct device *dev, enum rpmh_state state, if (!cmd || !n || n > MAX_RPMH_PAYLOAD) return -EINVAL; + if (rpmh_standalone) + return 0; + ret = check_ctrlr_state(ctrlr, state); if (ret) return ret; @@ -408,6 +414,9 @@ int rpmh_write_batch(const struct device *dev, enum rpmh_state state, if (!cmd || !n) return -EINVAL; + if (rpmh_standalone) + return 0; + ret = check_ctrlr_state(ctrlr, state); if (ret) return ret; @@ -494,6 +503,9 @@ int rpmh_write_pdc_data(const struct device *dev, if (!n || n > MAX_RPMH_PAYLOAD) return -EINVAL; + if (rpmh_standalone) + return 0; + memcpy(rpm_msg.cmd, cmd, n * sizeof(*cmd)); rpm_msg.msg.num_cmds = n; rpm_msg.msg.wait_for_compl = false; @@ -542,6 +554,9 @@ int rpmh_flush(const struct device *dev) struct rpmh_ctrlr *ctrlr = get_rpmh_ctrlr(dev); int ret; + if (rpmh_standalone) + return 0; + if (!ctrlr->dirty) { pr_debug("Skipping flush, TCS has latest data.\n"); return 0; @@ -590,6 +605,9 @@ int rpmh_invalidate(const struct device *dev) struct rpmh_ctrlr *ctrlr = get_rpmh_ctrlr(dev); int ret; + if (rpmh_standalone) + return 0; + invalidate_batch(ctrlr); ctrlr->dirty = true; @@ -610,6 +628,9 @@ int rpmh_ctrlr_idle(const struct device *dev) { struct rpmh_ctrlr *ctrlr = get_rpmh_ctrlr(dev); + if (rpmh_standalone) + return 0; + return rpmh_rsc_ctrlr_is_idle(ctrlr_to_drv(ctrlr)); } EXPORT_SYMBOL(rpmh_ctrlr_idle);