From 50922dfe8f99243e0feeae2bfbccff7c3c139154 Mon Sep 17 00:00:00 2001 From: Maulik Shah Date: Mon, 9 May 2022 13:41:37 +0530 Subject: [PATCH] drivers: qcom: rpmh-rsc: Add hibernation support Add hibernation support for rpmh-rsc driver. Change-Id: I55904c2332016815dcf1e127d2ae8a8510d48e38 Signed-off-by: Maulik Shah --- drivers/soc/qcom/rpmh-internal.h | 18 +++++++ drivers/soc/qcom/rpmh-rsc.c | 88 ++++++++++++++++++++++++++++++++ drivers/soc/qcom/rpmh.c | 60 +++++++++++++++++++++- include/soc/qcom/rpmh.h | 9 ++++ 4 files changed, 173 insertions(+), 2 deletions(-) diff --git a/drivers/soc/qcom/rpmh-internal.h b/drivers/soc/qcom/rpmh-internal.h index 3b055f82194b..68cae765bc51 100644 --- a/drivers/soc/qcom/rpmh-internal.h +++ b/drivers/soc/qcom/rpmh-internal.h @@ -111,6 +111,23 @@ struct drv_channel { bool initialized; }; +/** + * struct rsc_drv_top: our representation of the top RSC device + * + * @name: Controller RSC device name. + * @drv_count: No. of DRV controllers in the RSC device + * @drv: Controller for each DRV + * @dev: RSC top device + * @list: RSC device added in rpmh_rsc_dev_list. + */ +struct rsc_drv_top { + char name[MAX_NAME_LENGTH]; + int drv_count; + struct rsc_drv *drv; + struct device *dev; + struct list_head list; +}; + /** * struct rsc_drv: the Direct Resource Voter (DRV) of the * Resource State Coordinator controller (RSC) @@ -182,6 +199,7 @@ int rpmh_rsc_mode_solver_set(struct rsc_drv *drv, bool enable); int rpmh_rsc_get_channel(struct rsc_drv *drv); int rpmh_rsc_switch_channel(struct rsc_drv *drv, int ch); int rpmh_rsc_drv_enable(struct rsc_drv *drv, bool enable); +const struct device *rpmh_rsc_get_device(const char *name, u32 drv_id); void rpmh_tx_done(const struct tcs_request *msg); int rpmh_flush(struct rpmh_ctrlr *ctrlr, int ch); diff --git a/drivers/soc/qcom/rpmh-rsc.c b/drivers/soc/qcom/rpmh-rsc.c index 28cc0a35e085..f9b8802fe31c 100644 --- a/drivers/soc/qcom/rpmh-rsc.c +++ b/drivers/soc/qcom/rpmh-rsc.c @@ -82,6 +82,7 @@ static const char * const accl_str[] = { "", "", "", "CLK", "VREG", "BUS", }; +static LIST_HEAD(rpmh_rsc_dev_list); static struct rsc_drv *__rsc_drv[MAX_RSC_COUNT]; static int __rsc_count; bool rpmh_standalone; @@ -1345,6 +1346,73 @@ int rpmh_rsc_update_fast_path(struct rsc_drv *drv, return 0; } +static int rpmh_rsc_poweroff_noirq(struct device *dev) +{ + return 0; +} + +static void rpmh_rsc_tcs_irq_enable(struct rsc_drv *drv) +{ + u32 tcs_mask; + int ch; + + for (ch = 0; ch < MAX_CHANNEL; ch++) { + if (!drv->ch[ch].initialized) + continue; + + tcs_mask = readl_relaxed(drv->tcs_base + drv->regs[RSC_DRV_IRQ_ENABLE]); + tcs_mask |= drv->ch[ch].tcs[ACTIVE_TCS].mask; + writel_relaxed(tcs_mask, drv->tcs_base + drv->regs[RSC_DRV_IRQ_ENABLE]); + } +} + +static int rpmh_rsc_restore_noirq(struct device *dev) +{ + struct rsc_drv_top *rsc_top = dev_get_drvdata(dev); + int i; + + for (i = 0; i < rsc_top->drv_count; i++) { + if (rsc_top->drv[i].initialized) + rpmh_rsc_tcs_irq_enable(&rsc_top->drv[i]); + } + + return 0; +} + +static struct rsc_drv_top *rpmh_rsc_get_top_device(const char *name) +{ + struct rsc_drv_top *rsc_top; + bool rsc_dev_present = false; + + list_for_each_entry(rsc_top, &rpmh_rsc_dev_list, list) { + if (!strcmp(name, rsc_top->name)) { + rsc_dev_present = true; + break; + } + } + + if (!rsc_dev_present) + return ERR_PTR(-ENODEV); + + return rsc_top; +} + +const struct device *rpmh_rsc_get_device(const char *name, u32 drv_id) +{ + struct rsc_drv_top *rsc_top = rpmh_rsc_get_top_device(name); + int i; + + if (IS_ERR(rsc_top)) + return ERR_PTR(-ENODEV); + + for (i = 0; i < rsc_top->drv_count; i++) { + if (i == drv_id && rsc_top->drv[i].initialized) + return rsc_top->drv[i].dev; + } + + return ERR_PTR(-ENODEV); +} + static int rpmh_probe_channel_tcs_config(struct device_node *np, struct rsc_drv *drv, u32 max_tcs, u32 ncpt, int ch) @@ -1481,6 +1549,7 @@ static int rpmh_rsc_probe(struct platform_device *pdev) { struct device_node *np, *dn = pdev->dev.of_node; struct rsc_drv *drv; + struct rsc_drv_top *rsc_top; int ret, irq; u32 rsc_id, major_ver, minor_ver, solver_config; int i, drv_count; @@ -1503,6 +1572,10 @@ static int rpmh_rsc_probe(struct platform_device *pdev) dev_info(&pdev->dev, "RPMH is running in standalone mode.\n"); + rsc_top = devm_kzalloc(&pdev->dev, sizeof(*rsc_top), GFP_KERNEL); + if (!rsc_top) + return -ENOMEM; + ret = of_property_read_u32(dn, "qcom,drv-count", &drv_count); if (ret) return ret; @@ -1515,6 +1588,11 @@ static int rpmh_rsc_probe(struct platform_device *pdev) if (!name) name = dev_name(&pdev->dev); + rsc_top->drv_count = drv_count; + rsc_top->drv = drv; + rsc_top->dev = &pdev->dev; + scnprintf(rsc_top->name, sizeof(rsc_top->name), "%s", name); + for_each_child_of_node(dn, np) { struct device *drv_dev; @@ -1633,9 +1711,18 @@ static int rpmh_rsc_probe(struct platform_device *pdev) return ret; } + INIT_LIST_HEAD(&rsc_top->list); + list_add_tail(&rsc_top->list, &rpmh_rsc_dev_list); + dev_set_drvdata(&pdev->dev, &rsc_top); + return devm_of_platform_populate(&pdev->dev); } +static const struct dev_pm_ops rpmh_rsc_dev_pm_ops = { + .poweroff_noirq = rpmh_rsc_poweroff_noirq, + .restore_noirq = rpmh_rsc_restore_noirq, +}; + static const struct of_device_id rpmh_drv_match[] = { { .compatible = "qcom,rpmh-rsc", }, { } @@ -1647,6 +1734,7 @@ static struct platform_driver rpmh_driver = { .driver = { .name = "rpmh", .of_match_table = rpmh_drv_match, + .pm = &rpmh_rsc_dev_pm_ops, .suppress_bind_attrs = true, }, }; diff --git a/drivers/soc/qcom/rpmh.c b/drivers/soc/qcom/rpmh.c index 87cb46dea06c..06c90d5d6663 100644 --- a/drivers/soc/qcom/rpmh.c +++ b/drivers/soc/qcom/rpmh.c @@ -76,6 +76,13 @@ static struct rpmh_ctrlr *get_rpmh_ctrlr(const struct device *dev) return &drv->client; } +static struct rpmh_ctrlr *get_rpmh_ctrlr_no_child(const struct device *dev) +{ + struct rsc_drv *drv = dev_get_drvdata(dev); + + return &drv->client; +} + static int check_ctrlr_state(struct rpmh_ctrlr *ctrlr, enum rpmh_state state) { int ret = 0; @@ -582,6 +589,34 @@ int rpmh_write_sleep_and_wake(const struct device *dev) } EXPORT_SYMBOL(rpmh_write_sleep_and_wake); +/** + * rpmh_write_sleep_and_wake_no_child: Writes the buffered wake and sleep sets to TCSes + * + * Used when the client calling this is not a child device of RSC device. + * Use it only after getting the device using rpmh_get_device(). + * @dev: The device making the request + * + * Return: + * * 0 - Success + * * Error code - Otherwise + */ +int rpmh_write_sleep_and_wake_no_child(const struct device *dev) +{ + struct rpmh_ctrlr *ctrlr = get_rpmh_ctrlr_no_child(dev); + int ch, ret; + + ch = rpmh_rsc_get_channel(ctrlr_to_drv(ctrlr)); + if (ch < 0) + return ch; + + ret = rpmh_flush(ctrlr, ch); + if (ret || !(ctrlr->flags & HW_CHANNEL_PRESENT)) + return ret; + + return rpmh_rsc_switch_channel(ctrlr_to_drv(ctrlr), ch); +} +EXPORT_SYMBOL(rpmh_write_sleep_and_wake_no_child); + /** * rpmh_invalidate: Invalidate sleep and wake sets in batch_cache * @@ -728,6 +763,27 @@ int rpmh_update_fast_path(const struct device *dev, } EXPORT_SYMBOL(rpmh_update_fast_path); +/** + * rpmh_get_device: Get the DRV device + * + * @name: The RSC device used for DRV DRV + * @drv_id: The index of DRV + * + * Used when the device voting to RPMh is not a child device + * of RSC device. Such device can get RSC device using this API. + * but will be able to use only rpmh_drv_start(), rpmh_drv_stop() + * and rpmh_write_sleep_and_wake_no_child(). + * + * Return: + * * dev - Device to use when calling above APIs + * * Error - Error pointer + */ +const struct device *rpmh_get_device(const char *name, u32 drv_id) +{ + return rpmh_rsc_get_device(name, drv_id); +} +EXPORT_SYMBOL(rpmh_get_device); + /** * rpmh_drv_start: Start the DRV channel * @@ -739,7 +795,7 @@ EXPORT_SYMBOL(rpmh_update_fast_path); */ int rpmh_drv_start(const struct device *dev) { - struct rpmh_ctrlr *ctrlr = get_rpmh_ctrlr(dev); + struct rpmh_ctrlr *ctrlr = get_rpmh_ctrlr_no_child(dev); if (rpmh_standalone) return 0; @@ -759,7 +815,7 @@ EXPORT_SYMBOL(rpmh_drv_start); */ int rpmh_drv_stop(const struct device *dev) { - struct rpmh_ctrlr *ctrlr = get_rpmh_ctrlr(dev); + struct rpmh_ctrlr *ctrlr = get_rpmh_ctrlr_no_child(dev); if (rpmh_standalone) return 0; diff --git a/include/soc/qcom/rpmh.h b/include/soc/qcom/rpmh.h index 23c0c5dd48b8..16b1b093557b 100644 --- a/include/soc/qcom/rpmh.h +++ b/include/soc/qcom/rpmh.h @@ -24,6 +24,8 @@ int rpmh_mode_solver_set(const struct device *dev, bool enable); int rpmh_write_sleep_and_wake(const struct device *dev); +int rpmh_write_sleep_and_wake_no_child(const struct device *dev); + void rpmh_invalidate(const struct device *dev); int rpmh_init_fast_path(const struct device *dev, @@ -34,6 +36,8 @@ int rpmh_update_fast_path(const struct device *dev, int rpmh_drv_start(const struct device *dev); int rpmh_drv_stop(const struct device *dev); + +const struct device *rpmh_get_device(const char *name, u32 drv_id); #else static inline int rpmh_write(const struct device *dev, enum rpmh_state state, @@ -56,6 +60,9 @@ static int rpmh_mode_solver_set(const struct device *dev, bool enable) static int rpmh_write_sleep_and_wake(const struct device *dev) { return -ENODEV; } +static int rpmh_write_sleep_and_wake_no_child(const struct device *dev) +{ return -ENODEV; } + static inline void rpmh_invalidate(const struct device *dev) { } @@ -75,6 +82,8 @@ int rpmh_drv_start(const struct device *dev) int rpmh_drv_stop(const struct device *dev) { return -ENODEV; } +static const struct device *rpmh_get_device(char *name, u32 drv_id) +{ return ERR_PTR(-ENODEV); } #endif /* CONFIG_QCOM_RPMH */ #endif /* __SOC_QCOM_RPMH_H__ */