Merge "soc: qcom: rpmh-rsc: Add fast-path request support"

This commit is contained in:
qctecmdr 2022-05-10 09:14:03 -07:00 committed by Gerrit - the friendly Code Review server
commit 6d3016517b
5 changed files with 256 additions and 42 deletions

View File

@ -11,12 +11,15 @@
#include <linux/wait.h>
#include <soc/qcom/tcs.h>
#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)
#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;
};
@ -114,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;
@ -129,8 +136,12 @@ 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;
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);
@ -140,5 +151,11 @@ 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);
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__ */

View File

@ -14,10 +14,13 @@
#include <linux/kernel.h>
#include <linux/list.h>
#include <linux/module.h>
#include <linux/notifier.h>
#include <linux/of.h>
#include <linux/of_irq.h>
#include <linux/of_platform.h>
#include <linux/platform_device.h>
#include <linux/pm_domain.h>
#include <linux/pm_runtime.h>
#include <linux/slab.h>
#include <linux/spinlock.h>
#include <linux/wait.h>
@ -95,6 +98,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
@ -872,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;
}
@ -981,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)
{
@ -1053,6 +1146,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;
@ -1074,6 +1193,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;
@ -1111,6 +1235,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
@ -1119,9 +1244,18 @@ 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;
} else {
drv->client.flags |= SOLVER_PRESENT;
}
/* Enable the active TCS to send requests immediately */

View File

@ -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)
@ -249,6 +252,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 +292,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 +377,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;
@ -458,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
*
@ -469,55 +520,52 @@ 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;
lockdep_assert_irqs_disabled();
if (rpmh_standalone)
return 0;
/*
* 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.
* 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();
/*
* 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;
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;
}
/**
* 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
*
@ -531,6 +579,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 +606,12 @@ 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;
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)

View File

@ -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__ */

View File

@ -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)
{
}