From 08c56391659df7100e2036df5e7dac2db0c2f2d9 Mon Sep 17 00:00:00 2001 From: Lina Iyer Date: Wed, 5 Sep 2018 15:51:11 -0600 Subject: [PATCH 1/7] 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 Signed-off-by: Maulik Shah --- drivers/soc/qcom/rpmh-internal.h | 2 ++ drivers/soc/qcom/rpmh-rsc.c | 6 ++++++ drivers/soc/qcom/rpmh.c | 18 ++++++++++++++++++ 3 files changed, 26 insertions(+) diff --git a/drivers/soc/qcom/rpmh-internal.h b/drivers/soc/qcom/rpmh-internal.h index 6d68231f09ac..52554d737093 100644 --- a/drivers/soc/qcom/rpmh-internal.h +++ b/drivers/soc/qcom/rpmh-internal.h @@ -131,6 +131,8 @@ struct rsc_drv { struct rpmh_ctrlr client; }; +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 b480e6b67f09..6d9ca686f527 100644 --- a/drivers/soc/qcom/rpmh-rsc.c +++ b/drivers/soc/qcom/rpmh-rsc.c @@ -95,6 +95,7 @@ static const char * const accl_str[] = { static struct rsc_drv *__rsc_drv[MAX_RSC_COUNT]; static int __rsc_count; +bool rpmh_standalone; /* * Here's a high level overview of how all the registers in RPMH work @@ -1074,6 +1075,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 aafb3722cc5e..166552d0255f 100644 --- a/drivers/soc/qcom/rpmh.c +++ b/drivers/soc/qcom/rpmh.c @@ -249,6 +249,9 @@ int rpmh_write_async(const struct device *dev, enum rpmh_state state, struct rpmh_request *rpm_msg; int ret; + if (rpmh_standalone) + return 0; + ret = check_ctrlr_state(ctrlr, state); if (ret) return ret; @@ -286,6 +289,9 @@ int rpmh_write(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; @@ -368,6 +374,9 @@ int rpmh_write_batch(const struct device *dev, enum rpmh_state state, int ret, i; void *ptr; + if (rpmh_standalone) + return 0; + ret = check_ctrlr_state(ctrlr, state); if (ret) return ret; @@ -472,6 +481,9 @@ int rpmh_flush(struct rpmh_ctrlr *ctrlr) struct cache_req *p; int ret = 0; + if (rpmh_standalone) + return 0; + lockdep_assert_irqs_disabled(); /* @@ -531,6 +543,9 @@ void rpmh_invalidate(const struct device *dev) struct batch_cache_req *req, *tmp; unsigned long flags; + if (rpmh_standalone) + return; + spin_lock_irqsave(&ctrlr->cache_lock, flags); list_for_each_entry_safe(req, tmp, &ctrlr->batch_cache, list) kfree(req); @@ -555,6 +570,9 @@ int rpmh_mode_solver_set(const struct device *dev, bool enable) int ret; struct rpmh_ctrlr *ctrlr = get_rpmh_ctrlr(dev); + if (rpmh_standalone) + return 0; + spin_lock(&ctrlr->cache_lock); ret = rpmh_rsc_mode_solver_set(ctrlr_to_drv(ctrlr), enable); if (!ret) From 3601181d1ba7238760e7bd22fef09b0afee9663d Mon Sep 17 00:00:00 2001 From: Maulik Shah Date: Fri, 1 May 2020 16:38:11 +0530 Subject: [PATCH 2/7] soc: qcom: rpmh: Add rpmh_write_sleep_and_wake() function Let clients call rpmh_write_sleep_and_wake() to immediately write cached sleep and wake data to the TCSes. Change-Id: I04a94ee622a39a0aa0e829f4c40190a870fb2fb1 Signed-off-by: Maulik Shah --- drivers/soc/qcom/rpmh.c | 15 +++++++++++++++ include/soc/qcom/rpmh.h | 5 +++++ 2 files changed, 20 insertions(+) diff --git a/drivers/soc/qcom/rpmh.c b/drivers/soc/qcom/rpmh.c index 166552d0255f..3f44e8526a7f 100644 --- a/drivers/soc/qcom/rpmh.c +++ b/drivers/soc/qcom/rpmh.c @@ -530,6 +530,21 @@ int rpmh_flush(struct rpmh_ctrlr *ctrlr) return ret; } +/** + * rpmh_write_sleep_and_wake: Writes the buffered wake and sleep sets to TCSes + * + * @dev: The device making the request + * + * Return: + * * 0 - Success + * * Error code - Otherwise + */ +int rpmh_write_sleep_and_wake(const struct device *dev) +{ + return rpmh_flush(get_rpmh_ctrlr(dev)); +} +EXPORT_SYMBOL(rpmh_write_sleep_and_wake); + /** * rpmh_invalidate: Invalidate sleep and wake sets in batch_cache * diff --git a/include/soc/qcom/rpmh.h b/include/soc/qcom/rpmh.h index a3fa8af8b35f..dd4fada3b7e2 100644 --- a/include/soc/qcom/rpmh.h +++ b/include/soc/qcom/rpmh.h @@ -22,6 +22,8 @@ int rpmh_write_batch(const struct device *dev, enum rpmh_state state, int rpmh_mode_solver_set(const struct device *dev, bool enable); +int rpmh_write_sleep_and_wake(const struct device *dev); + void rpmh_invalidate(const struct device *dev); #else @@ -43,6 +45,9 @@ static inline int rpmh_write_batch(const struct device *dev, static int rpmh_mode_solver_set(const struct device *dev, bool enable) { return -ENODEV; } +static int rpmh_write_sleep_and_wake(const struct device *dev) +{ return -ENODEV; } + static inline void rpmh_invalidate(const struct device *dev) { } From 5a1e88f0c55ed5d0d76a3018eead705b38e702db Mon Sep 17 00:00:00 2001 From: Maulik Shah Date: Fri, 18 Sep 2020 22:01:35 +0530 Subject: [PATCH 3/7] soc: qcom: rpmh: Conditionally check lockdep_assert_irqs_disabled() lockdep_assert_irqs_disabled() was added to check rpmh_flush() can only be invoked when irqs are disabled, this is true for APPS RSC as the last CPU going to deepest low power mode is writing sleep and wake TCSes. However for Display RSC, drivers can invoke rpmh_write_sleep_and_wake() to immediately write cached sleep and wake sets to TCSes from any CPU. Conditionally check if RSC controller supports solver mode then do not check for irqs disabled as such RSC can write sleep and wake TCSes at any point. Change-Id: I8ad05cd81af15a0d57353b55d8098cf0e47f79f7 Signed-off-by: Maulik Shah --- drivers/soc/qcom/rpmh-internal.h | 5 +++++ drivers/soc/qcom/rpmh-rsc.c | 3 +++ drivers/soc/qcom/rpmh.c | 26 ++++++++++++++++++++++---- 3 files changed, 30 insertions(+), 4 deletions(-) diff --git a/drivers/soc/qcom/rpmh-internal.h b/drivers/soc/qcom/rpmh-internal.h index 52554d737093..54c1e014f17f 100644 --- a/drivers/soc/qcom/rpmh-internal.h +++ b/drivers/soc/qcom/rpmh-internal.h @@ -17,6 +17,9 @@ #define MAX_TCS_NR (MAX_TCS_PER_TYPE * TCS_TYPE_NR) #define MAX_TCS_SLOTS (MAX_CMDS_PER_TCS * MAX_TCS_PER_TYPE) +/* CTRLR specific flags */ +#define SOLVER_PRESENT 1 + struct rsc_drv; /** @@ -78,6 +81,7 @@ struct rpmh_request { * @cache_lock: synchronize access to the cache data * @dirty: was the cache updated since flush * @in_solver_mode: Controller is busy in solver mode + * @flags: Controller specific flags * @batch_cache: Cache sleep and wake requests sent as batch */ struct rpmh_ctrlr { @@ -85,6 +89,7 @@ struct rpmh_ctrlr { spinlock_t cache_lock; bool dirty; bool in_solver_mode; + u32 flags; struct list_head batch_cache; }; diff --git a/drivers/soc/qcom/rpmh-rsc.c b/drivers/soc/qcom/rpmh-rsc.c index 6d9ca686f527..cf561f09bc3e 100644 --- a/drivers/soc/qcom/rpmh-rsc.c +++ b/drivers/soc/qcom/rpmh-rsc.c @@ -1128,6 +1128,9 @@ static int rpmh_rsc_probe(struct platform_device *pdev) if (!solver_config) { drv->rsc_pm.notifier_call = rpmh_rsc_cpu_pm_callback; cpu_pm_register_notifier(&drv->rsc_pm); + drv->client.flags &= ~SOLVER_PRESENT; + } else { + drv->client.flags |= SOLVER_PRESENT; } /* Enable the active TCS to send requests immediately */ diff --git a/drivers/soc/qcom/rpmh.c b/drivers/soc/qcom/rpmh.c index 3f44e8526a7f..e4a50fd41412 100644 --- a/drivers/soc/qcom/rpmh.c +++ b/drivers/soc/qcom/rpmh.c @@ -83,6 +83,9 @@ static int check_ctrlr_state(struct rpmh_ctrlr *ctrlr, enum rpmh_state state) if (state != RPMH_ACTIVE_ONLY_STATE) return ret; + if (!(ctrlr->flags & SOLVER_PRESENT)) + return ret; + /* Do not allow sending active votes when in solver mode */ spin_lock(&ctrlr->cache_lock); if (ctrlr->in_solver_mode) @@ -484,12 +487,24 @@ int rpmh_flush(struct rpmh_ctrlr *ctrlr) if (rpmh_standalone) return 0; - lockdep_assert_irqs_disabled(); + /* + * For RSC that don't have solver mode, + * rpmh_flush() is only called when we think we're running + * on the last CPU with irqs_disabled. + * + * For RSC that have solver mode, + * rpmh_flush() can be invoked with irqs enabled by any CPU. + * + * Conditionally check for irqs_disabled only when solver mode + * is not available. + */ + + if (!(ctrlr->flags & SOLVER_PRESENT)) + lockdep_assert_irqs_disabled(); /* - * Currently rpmh_flush() is only called when we think we're running - * on the last processor. If the lock is busy it means another - * processor is up and it's better to abort than spin. + * If the lock is busy it means another transaction is on going, + * in such case it's better to abort than spin. */ if (!spin_trylock(&ctrlr->cache_lock)) return -EBUSY; @@ -588,6 +603,9 @@ int rpmh_mode_solver_set(const struct device *dev, bool enable) if (rpmh_standalone) return 0; + if (!(ctrlr->flags & SOLVER_PRESENT)) + return -EINVAL; + spin_lock(&ctrlr->cache_lock); ret = rpmh_rsc_mode_solver_set(ctrlr_to_drv(ctrlr), enable); if (!ret) From 7466fd9c13c6de08f2de1bc69fce207d8a213022 Mon Sep 17 00:00:00 2001 From: Lina Iyer Date: Fri, 15 May 2020 15:47:46 -0600 Subject: [PATCH 4/7] soc: qcom: rpmh-rsc: Attach RSC to CPU cluster PM domain RSC is part the CPU subsystem and powers off the CPU domains when all the CPUs and no RPMH transactions are pending from any of the drivers. The RSC needs to flush the 'sleep' and 'wake' votes that are critical for saving power when all the CPUs are in idle. Doing this every time a CPU powers down, increases latency to enter idle state. So, we want to do this, only when it makes sense - when the last CPU is powering down. Let's make RSC part of the CPU PM domains, by attaching it to the cluster PD (defined in DT). Registering for PM domain notifications, RSC driver can be notified that the last CPU is powering down. When the last CPU is powering down the domain, let's flush the 'sleep' and 'wake' votes that are stored in the data buffers into the hardware. Change-Id: Id748413f0f86a4ac73c37e0780b2b22a143a1e61 Signed-off-by: Lina Iyer Signed-off-by: Maulik Shah --- drivers/soc/qcom/rpmh-internal.h | 4 ++++ drivers/soc/qcom/rpmh-rsc.c | 38 +++++++++++++++++++++++++++++++- 2 files changed, 41 insertions(+), 1 deletion(-) diff --git a/drivers/soc/qcom/rpmh-internal.h b/drivers/soc/qcom/rpmh-internal.h index 54c1e014f17f..02ad7da2e75e 100644 --- a/drivers/soc/qcom/rpmh-internal.h +++ b/drivers/soc/qcom/rpmh-internal.h @@ -119,6 +119,8 @@ struct rpmh_ctrlr { * @tcs_wait: Wait queue used to wait for @tcs_in_use to free up a * slot * @client: Handle to the DRV's client. + * @genpd_nb: PM Domain notifier + * @dev: RSC device */ struct rsc_drv { const char *name; @@ -134,6 +136,8 @@ struct rsc_drv { spinlock_t lock; wait_queue_head_t tcs_wait; struct rpmh_ctrlr client; + struct notifier_block genpd_nb; + struct device *dev; }; extern bool rpmh_standalone; diff --git a/drivers/soc/qcom/rpmh-rsc.c b/drivers/soc/qcom/rpmh-rsc.c index cf561f09bc3e..0f6b55ea426a 100644 --- a/drivers/soc/qcom/rpmh-rsc.c +++ b/drivers/soc/qcom/rpmh-rsc.c @@ -14,10 +14,13 @@ #include #include #include +#include #include #include #include #include +#include +#include #include #include #include @@ -1054,6 +1057,32 @@ static int rpmh_probe_tcs_config(struct platform_device *pdev, return 0; } +static int rpmh_rsc_pd_cb(struct notifier_block *nb, + unsigned long action, void *data) +{ + struct rsc_drv *drv = container_of(nb, struct rsc_drv, genpd_nb); + + /* We don't need to lock as domin on/off are serialized */ + if ((action == GENPD_NOTIFY_PRE_OFF) && + (rpmh_rsc_ctrlr_is_busy(drv) || rpmh_flush(&drv->client))) + return NOTIFY_BAD; + + return NOTIFY_OK; +} + +static int rpmh_rsc_pd_attach(struct rsc_drv *drv) +{ + int ret; + + pm_runtime_enable(drv->dev); + ret = dev_pm_domain_attach(drv->dev, false); + if (ret) + return ret; + + drv->genpd_nb.notifier_call = rpmh_rsc_pd_cb; + return dev_pm_genpd_add_notifier(drv->dev, &drv->genpd_nb); +} + static int rpmh_rsc_probe(struct platform_device *pdev) { struct device_node *dn = pdev->dev.of_node; @@ -1117,6 +1146,7 @@ static int rpmh_rsc_probe(struct platform_device *pdev) if (ret) return ret; + drv->dev = &pdev->dev; /* * CPU PM notification are not required for controllers that support * 'HW solver' mode where they can be in autonomous mode executing low @@ -1125,7 +1155,13 @@ static int rpmh_rsc_probe(struct platform_device *pdev) solver_config = readl_relaxed(base + DRV_SOLVER_CONFIG); solver_config &= DRV_HW_SOLVER_MASK << DRV_HW_SOLVER_SHIFT; solver_config = solver_config >> DRV_HW_SOLVER_SHIFT; - if (!solver_config) { + if (of_find_property(dn, "power-domains", NULL)) { + ret = rpmh_rsc_pd_attach(drv); + if (ret == -EPROBE_DEFER) { + pr_err("Failed to attach RSC %s to domain ret=%d\n", drv->name, ret); + return ret; + } + } else if (!solver_config) { drv->rsc_pm.notifier_call = rpmh_rsc_cpu_pm_callback; cpu_pm_register_notifier(&drv->rsc_pm); drv->client.flags &= ~SOLVER_PRESENT; From 0d2c70be3909090b729d13be143df85643922391 Mon Sep 17 00:00:00 2001 From: Lina Iyer Date: Thu, 3 Sep 2020 13:53:28 -0600 Subject: [PATCH 5/7] soc: qcom: rpmh: Optimize locking around flushing cache In OS-Initiated mode of PSCI, a serialization is established in Linux between the last CPU powering down the cluster / domain and the CPU powering up back. The serialization is established by the genpd framework and therefore it doesn't help to lock around the cache. Refactor out the core of the flush functionality from the locking wrappers. Calling the core functionality avoids a spin trylock, which the RCU framework might consider suspicious under the circumstances. Change-Id: I242fb4827b71152bda36ea5870393054b8703309 Signed-off-by: Lina Iyer Signed-off-by: Maulik Shah --- drivers/soc/qcom/rpmh-internal.h | 1 + drivers/soc/qcom/rpmh-rsc.c | 2 +- drivers/soc/qcom/rpmh.c | 78 +++++++++++++++++--------------- 3 files changed, 44 insertions(+), 37 deletions(-) diff --git a/drivers/soc/qcom/rpmh-internal.h b/drivers/soc/qcom/rpmh-internal.h index 02ad7da2e75e..81185196e5ef 100644 --- a/drivers/soc/qcom/rpmh-internal.h +++ b/drivers/soc/qcom/rpmh-internal.h @@ -151,5 +151,6 @@ int rpmh_rsc_mode_solver_set(struct rsc_drv *drv, bool enable); void rpmh_tx_done(const struct tcs_request *msg, int r); int rpmh_flush(struct rpmh_ctrlr *ctrlr); +int _rpmh_flush(struct rpmh_ctrlr *ctrlr); #endif /* __RPM_INTERNAL_H__ */ diff --git a/drivers/soc/qcom/rpmh-rsc.c b/drivers/soc/qcom/rpmh-rsc.c index 0f6b55ea426a..63fc115b3239 100644 --- a/drivers/soc/qcom/rpmh-rsc.c +++ b/drivers/soc/qcom/rpmh-rsc.c @@ -1064,7 +1064,7 @@ static int rpmh_rsc_pd_cb(struct notifier_block *nb, /* We don't need to lock as domin on/off are serialized */ if ((action == GENPD_NOTIFY_PRE_OFF) && - (rpmh_rsc_ctrlr_is_busy(drv) || rpmh_flush(&drv->client))) + (rpmh_rsc_ctrlr_is_busy(drv) || _rpmh_flush(&drv->client))) return NOTIFY_BAD; return NOTIFY_OK; diff --git a/drivers/soc/qcom/rpmh.c b/drivers/soc/qcom/rpmh.c index e4a50fd41412..43a06d93cd56 100644 --- a/drivers/soc/qcom/rpmh.c +++ b/drivers/soc/qcom/rpmh.c @@ -470,6 +470,45 @@ static int send_single(struct rpmh_ctrlr *ctrlr, enum rpmh_state state, return rpmh_rsc_write_ctrl_data(ctrlr_to_drv(ctrlr), &rpm_msg.msg); } +int _rpmh_flush(struct rpmh_ctrlr *ctrlr) +{ + struct cache_req *p; + int ret = 0; + + if (!ctrlr->dirty) { + pr_debug("Skipping flush, TCS has latest data.\n"); + return ret; + } + + /* Invalidate the TCSes first to avoid stale data */ + rpmh_rsc_invalidate(ctrlr_to_drv(ctrlr)); + + /* First flush the cached batch requests */ + ret = flush_batch(ctrlr); + if (ret) + return ret; + + list_for_each_entry(p, &ctrlr->cache, list) { + if (!is_req_valid(p)) { + pr_debug("%s: skipping RPMH req: a:%#x s:%#x w:%#x\n", + __func__, p->addr, p->sleep_val, p->wake_val); + continue; + } + ret = send_single(ctrlr, RPMH_SLEEP_STATE, p->addr, + p->sleep_val); + if (ret) + return ret; + ret = send_single(ctrlr, RPMH_WAKE_ONLY_STATE, p->addr, + p->wake_val); + if (ret) + return ret; + } + + ctrlr->dirty = false; + + return ret; +} + /** * rpmh_flush() - Flushes the buffered sleep and wake sets to TCSes * @@ -481,8 +520,7 @@ static int send_single(struct rpmh_ctrlr *ctrlr, enum rpmh_state state, */ int rpmh_flush(struct rpmh_ctrlr *ctrlr) { - struct cache_req *p; - int ret = 0; + int ret; if (rpmh_standalone) return 0; @@ -498,7 +536,6 @@ int rpmh_flush(struct rpmh_ctrlr *ctrlr) * Conditionally check for irqs_disabled only when solver mode * is not available. */ - if (!(ctrlr->flags & SOLVER_PRESENT)) lockdep_assert_irqs_disabled(); @@ -508,40 +545,9 @@ int rpmh_flush(struct rpmh_ctrlr *ctrlr) */ if (!spin_trylock(&ctrlr->cache_lock)) return -EBUSY; - - if (!ctrlr->dirty) { - pr_debug("Skipping flush, TCS has latest data.\n"); - goto exit; - } - - /* Invalidate the TCSes first to avoid stale data */ - rpmh_rsc_invalidate(ctrlr_to_drv(ctrlr)); - - /* First flush the cached batch requests */ - ret = flush_batch(ctrlr); - if (ret) - goto exit; - - list_for_each_entry(p, &ctrlr->cache, list) { - if (!is_req_valid(p)) { - pr_debug("%s: skipping RPMH req: a:%#x s:%#x w:%#x", - __func__, p->addr, p->sleep_val, p->wake_val); - continue; - } - ret = send_single(ctrlr, RPMH_SLEEP_STATE, p->addr, - p->sleep_val); - if (ret) - goto exit; - ret = send_single(ctrlr, RPMH_WAKE_ONLY_STATE, p->addr, - p->wake_val); - if (ret) - goto exit; - } - - ctrlr->dirty = false; - -exit: + ret = _rpmh_flush(ctrlr); spin_unlock(&ctrlr->cache_lock); + return ret; } From 419d3f80494fd8d2368909d9ae4270df9243db2a Mon Sep 17 00:00:00 2001 From: Lina Iyer Date: Mon, 19 Oct 2020 14:48:53 -0600 Subject: [PATCH 6/7] dt-bindings: Define FAST_PATH_TCS for use in RSC definition Add a new definition for fast-path TCS configuration. Change-Id: I610b14dcd893885e5b1c32c434caa7aa7102fd4c Signed-off-by: Lina Iyer Signed-off-by: Maulik Shah --- include/dt-bindings/soc/qcom,rpmh-rsc.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/include/dt-bindings/soc/qcom,rpmh-rsc.h b/include/dt-bindings/soc/qcom,rpmh-rsc.h index 868f998ea998..221049d6edac 100644 --- a/include/dt-bindings/soc/qcom,rpmh-rsc.h +++ b/include/dt-bindings/soc/qcom,rpmh-rsc.h @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0 */ /* - * Copyright (c) 2016-2018, The Linux Foundation. All rights reserved. + * Copyright (c) 2016-2020, The Linux Foundation. All rights reserved. */ #ifndef __DT_QCOM_RPMH_RSC_H__ @@ -10,5 +10,6 @@ #define WAKE_TCS 1 #define ACTIVE_TCS 2 #define CONTROL_TCS 3 +#define FAST_PATH_TCS 4 #endif /* __DT_QCOM_RPMH_RSC_H__ */ From ad5eb0a49a3de23a181c1575c8cb9ef36d6b2a34 Mon Sep 17 00:00:00 2001 From: Lina Iyer Date: Mon, 5 Oct 2020 11:06:44 -0600 Subject: [PATCH 7/7] soc: qcom: rpmh-rsc: Add fast-path request support In order to support lockless RPMh request, let's dedicate a TCS for this purpose. This TCS shall be defined as FAST_PATH_TCS in the TCS configuration for the RSC. The fast-path TCS will have the following properties - - A single TCS shall be defined as a fast-path TCS. - Fast-path TCS will not use interrupt for signalling request completion. - Fast path will poll controller status for idle before sending request. - DRV properties are not updated during the fast-path use. - Fast path requests must be first initialized with {addr, data}. - Subsequent updates will only update the data in the TCS and trigger. - Requests must be fire-n-forget only (no completion). Change-Id: I235ea60159e40affa9ce1e4069e11eca3925a423 Signed-off-by: Lina Iyer Signed-off-by: Maulik Shah --- drivers/soc/qcom/rpmh-internal.h | 7 ++- drivers/soc/qcom/rpmh-rsc.c | 89 ++++++++++++++++++++++++++++++++ 2 files changed, 95 insertions(+), 1 deletion(-) diff --git a/drivers/soc/qcom/rpmh-internal.h b/drivers/soc/qcom/rpmh-internal.h index 81185196e5ef..95e641702f4b 100644 --- a/drivers/soc/qcom/rpmh-internal.h +++ b/drivers/soc/qcom/rpmh-internal.h @@ -11,7 +11,7 @@ #include #include -#define TCS_TYPE_NR 4 +#define TCS_TYPE_NR 5 #define MAX_CMDS_PER_TCS 16 #define MAX_TCS_PER_TYPE 3 #define MAX_TCS_NR (MAX_TCS_PER_TYPE * TCS_TYPE_NR) @@ -153,4 +153,9 @@ void rpmh_tx_done(const struct tcs_request *msg, int r); int rpmh_flush(struct rpmh_ctrlr *ctrlr); int _rpmh_flush(struct rpmh_ctrlr *ctrlr); +int rpmh_rsc_init_fast_path(struct rsc_drv *drv, const struct tcs_request *msg); +int rpmh_rsc_update_fast_path(struct rsc_drv *drv, + const struct tcs_request *msg, + u32 update_mask); + #endif /* __RPM_INTERNAL_H__ */ diff --git a/drivers/soc/qcom/rpmh-rsc.c b/drivers/soc/qcom/rpmh-rsc.c index 63fc115b3239..bc54853e4b4b 100644 --- a/drivers/soc/qcom/rpmh-rsc.c +++ b/drivers/soc/qcom/rpmh-rsc.c @@ -876,6 +876,11 @@ static bool rpmh_rsc_ctrlr_is_busy(struct rsc_drv *drv) max = tcs->offset + tcs->num_tcs; set = find_next_bit(drv->tcs_in_use, max, tcs->offset); + /* Check if there is pending fastpath transaction */ + if (drv->tcs[FAST_PATH_TCS].num_tcs && + !read_tcs_reg(drv, RSC_DRV_STATUS, drv->tcs[FAST_PATH_TCS].offset)) + return true; + return set < max; } @@ -985,6 +990,90 @@ int rpmh_rsc_mode_solver_set(struct rsc_drv *drv, bool enable) return ret; } +/** + * rpmh_rsc_init_fast_path() - Initialize the fast-path TCS contents + * @drv: The controller. + * @msg: The TCS request to populate. + * + * Return: + * * 0 - success + * * -ENODEV - no fast-path TCS available + */ +int rpmh_rsc_init_fast_path(struct rsc_drv *drv, const struct tcs_request *msg) +{ + int tcs_id; + + if (!drv->tcs[FAST_PATH_TCS].num_tcs) + return -ENODEV; + + tcs_id = drv->tcs[FAST_PATH_TCS].offset; + + /* We won't use the AMC IRQ to confirm if the TCS is free */ + enable_tcs_irq(drv, tcs_id, false); + + __tcs_buffer_write(drv, tcs_id, 0, msg); + + return 0; +} + +/** + * rpmh_rsc_update_fast_path() - Update the fast-path TCS data and trigger + * @drv: The controller. + * @msg: The TCS request data to be updated. + * @mask: The update mask for elements in @msg to be sent + * + * NOTE: Caller should ensure serialization before making this call. + * Return: + * * 0 - success + * * -ENODEV - no fast-path TCS available + */ +int rpmh_rsc_update_fast_path(struct rsc_drv *drv, + const struct tcs_request *msg, + u32 mask) +{ + int i; + u32 sts; + int tcs_id; + struct tcs_cmd *cmd; + int retry = 5; + + if (!drv->tcs[FAST_PATH_TCS].num_tcs) + return -ENODEV; + + tcs_id = drv->tcs[FAST_PATH_TCS].offset; + + /* Ensure the TCS is free before writing to the TCS */ + do { + sts = read_tcs_reg(drv, RSC_DRV_STATUS, tcs_id); + if (!sts) { + retry--; + /* Report and bail, if it took too many attempts */ + if (!retry) { + pr_err("Fast-path TCS is too busy\n"); + return -EBUSY; + } + udelay(1); + } + } while (!sts); + + /* + * We only update the data, everything else remains the same. + * The number of commands and the addresses do not change with + * updates. + */ + for (i = 0; i < msg->num_cmds; i++) { + if (!(mask & BIT(i))) + continue; + cmd = &msg->cmds[i]; + write_tcs_cmd(drv, RSC_DRV_CMD_DATA, tcs_id, i, cmd->data); + } + + /* Trigger the TCS to send the request */ + __tcs_set_trigger(drv, tcs_id, true); + + return 0; +} + static int rpmh_probe_tcs_config(struct platform_device *pdev, struct rsc_drv *drv, void __iomem *base) {