diff --git a/drivers/soc/qcom/rpmh-internal.h b/drivers/soc/qcom/rpmh-internal.h index fc6bc1b9d119..6d68231f09ac 100644 --- a/drivers/soc/qcom/rpmh-internal.h +++ b/drivers/soc/qcom/rpmh-internal.h @@ -77,12 +77,14 @@ struct rpmh_request { * @cache: the list of cached requests * @cache_lock: synchronize access to the cache data * @dirty: was the cache updated since flush + * @in_solver_mode: Controller is busy in solver mode * @batch_cache: Cache sleep and wake requests sent as batch */ struct rpmh_ctrlr { struct list_head cache; spinlock_t cache_lock; bool dirty; + bool in_solver_mode; struct list_head batch_cache; }; @@ -95,6 +97,7 @@ struct rpmh_ctrlr { * @id: Instance id in the controller (Direct Resource Voter). * @num_tcs: Number of TCSes in this DRV. * @irq: IRQ at gic. + * @in_solver_mode: Controller is busy in solver mode * @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. @@ -118,6 +121,7 @@ struct rsc_drv { int id; int num_tcs; int irq; + bool in_solver_mode; struct notifier_block rsc_pm; atomic_t cpus_in_pm; struct tcs_group tcs[TCS_TYPE_NR]; @@ -132,6 +136,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_debug(struct rsc_drv *drv, struct completion *compl); +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); diff --git a/drivers/soc/qcom/rpmh-rsc.c b/drivers/soc/qcom/rpmh-rsc.c index 79651b8119e0..b480e6b67f09 100644 --- a/drivers/soc/qcom/rpmh-rsc.c +++ b/drivers/soc/qcom/rpmh-rsc.c @@ -626,6 +626,12 @@ int rpmh_rsc_send_data(struct rsc_drv *drv, const struct tcs_request *msg) spin_lock_irqsave(&drv->lock, flags); + /* Controller is busy in 'solver' mode */ + if (drv->in_solver_mode) { + spin_unlock_irqrestore(&drv->lock, flags); + return -EBUSY; + } + /* Wait forever for a free tcs. It better be there eventually! */ wait_event_lock_irq(drv->tcs_wait, (tcs_id = claim_tcs_for_req(drv, tcs, msg)) >= 0, @@ -950,6 +956,31 @@ static int rpmh_rsc_cpu_pm_callback(struct notifier_block *nfb, return ret; } +/** + * rpmh_rsc_mode_solver_set() - Enable/disable solver mode. + * @drv: The controller. + * @enable: Boolean state to be set - true/false + * + * Return: + * * 0 - success + * * -EBUSY - AMCs are busy + */ +int rpmh_rsc_mode_solver_set(struct rsc_drv *drv, bool enable) +{ + int ret = -EBUSY; + + if (spin_trylock(&drv->lock)) { + if (!enable || !rpmh_rsc_ctrlr_is_busy(drv)) { + drv->in_solver_mode = enable; + trace_rpmh_solver_set(drv, enable); + ret = 0; + } + spin_unlock(&drv->lock); + } + + return ret; +} + static int rpmh_probe_tcs_config(struct platform_device *pdev, struct rsc_drv *drv, void __iomem *base) { diff --git a/drivers/soc/qcom/rpmh.c b/drivers/soc/qcom/rpmh.c index 1f8b4765685f..aafb3722cc5e 100644 --- a/drivers/soc/qcom/rpmh.c +++ b/drivers/soc/qcom/rpmh.c @@ -76,6 +76,22 @@ static struct rpmh_ctrlr *get_rpmh_ctrlr(const struct device *dev) return &drv->client; } +static int check_ctrlr_state(struct rpmh_ctrlr *ctrlr, enum rpmh_state state) +{ + int ret = 0; + + if (state != RPMH_ACTIVE_ONLY_STATE) + return ret; + + /* Do not allow sending active votes when in solver mode */ + spin_lock(&ctrlr->cache_lock); + if (ctrlr->in_solver_mode) + ret = -EBUSY; + spin_unlock(&ctrlr->cache_lock); + + return ret; +} + void rpmh_tx_done(const struct tcs_request *msg, int r) { struct rpmh_request *rpm_msg = container_of(msg, struct rpmh_request, @@ -229,9 +245,14 @@ static int __fill_rpmh_msg(struct rpmh_request *req, enum rpmh_state state, int rpmh_write_async(const struct device *dev, enum rpmh_state state, const struct tcs_cmd *cmd, u32 n) { + struct rpmh_ctrlr *ctrlr = get_rpmh_ctrlr(dev); struct rpmh_request *rpm_msg; int ret; + ret = check_ctrlr_state(ctrlr, state); + if (ret) + return ret; + rpm_msg = kzalloc(sizeof(*rpm_msg), GFP_ATOMIC); if (!rpm_msg) return -ENOMEM; @@ -265,6 +286,10 @@ int rpmh_write(const struct device *dev, enum rpmh_state state, struct rpmh_ctrlr *ctrlr = get_rpmh_ctrlr(dev); int ret; + ret = check_ctrlr_state(ctrlr, state); + if (ret) + return ret; + ret = __fill_rpmh_msg(&rpm_msg, state, cmd, n); if (ret) return ret; @@ -343,6 +368,10 @@ int rpmh_write_batch(const struct device *dev, enum rpmh_state state, int ret, i; void *ptr; + ret = check_ctrlr_state(ctrlr, state); + if (ret) + return ret; + if (!cmd || !n) return -EINVAL; @@ -510,3 +539,28 @@ void rpmh_invalidate(const struct device *dev) spin_unlock_irqrestore(&ctrlr->cache_lock, flags); } EXPORT_SYMBOL(rpmh_invalidate); + +/** + * rpmh_mode_solver_set: Indicate that the RSC controller hardware has + * been configured to be in solver mode + * + * @dev: The device making the request + * @enable: Boolean value indicating if the controller is in solver mode. + * Return: + * * 0 - Success + * * Error code - Otherwise + */ +int rpmh_mode_solver_set(const struct device *dev, bool enable) +{ + int ret; + struct rpmh_ctrlr *ctrlr = get_rpmh_ctrlr(dev); + + spin_lock(&ctrlr->cache_lock); + ret = rpmh_rsc_mode_solver_set(ctrlr_to_drv(ctrlr), enable); + if (!ret) + ctrlr->in_solver_mode = enable; + spin_unlock(&ctrlr->cache_lock); + + return ret; +} +EXPORT_SYMBOL(rpmh_mode_solver_set); diff --git a/drivers/soc/qcom/trace-rpmh.h b/drivers/soc/qcom/trace-rpmh.h index feb0cb455e37..fb7285185f96 100644 --- a/drivers/soc/qcom/trace-rpmh.h +++ b/drivers/soc/qcom/trace-rpmh.h @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0 */ /* - * Copyright (c) 2016-2018, The Linux Foundation. All rights reserved. + * Copyright (c) 2016-2019, The Linux Foundation. All rights reserved. */ #if !defined(_TRACE_RPMH_H) || defined(TRACE_HEADER_MULTI_READ) @@ -71,6 +71,26 @@ TRACE_EVENT(rpmh_send_msg, __entry->addr, __entry->data, __entry->wait) ); +TRACE_EVENT(rpmh_solver_set, + + TP_PROTO(struct rsc_drv *d, bool set), + + TP_ARGS(d, set), + + TP_STRUCT__entry( + __string(name, d->name) + __field(bool, set) + ), + + TP_fast_assign( + __assign_str(name, d->name); + __entry->set = set; + ), + + TP_printk("%s: solver mode set: %d", + __get_str(name), __entry->set) +); + #endif /* _TRACE_RPMH_H */ #undef TRACE_INCLUDE_PATH diff --git a/include/soc/qcom/rpmh.h b/include/soc/qcom/rpmh.h index bdbee1a97d36..a3fa8af8b35f 100644 --- a/include/soc/qcom/rpmh.h +++ b/include/soc/qcom/rpmh.h @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0 */ /* - * Copyright (c) 2016-2018, The Linux Foundation. All rights reserved. + * Copyright (c) 2016-2019, The Linux Foundation. All rights reserved. */ #ifndef __SOC_QCOM_RPMH_H__ @@ -20,6 +20,8 @@ int rpmh_write_async(const struct device *dev, enum rpmh_state state, int rpmh_write_batch(const struct device *dev, enum rpmh_state state, const struct tcs_cmd *cmd, u32 *n); +int rpmh_mode_solver_set(const struct device *dev, bool enable); + void rpmh_invalidate(const struct device *dev); #else @@ -38,6 +40,9 @@ static inline int rpmh_write_batch(const struct device *dev, const struct tcs_cmd *cmd, u32 *n) { return -ENODEV; } +static int rpmh_mode_solver_set(const struct device *dev, bool enable) +{ return -ENODEV; } + static inline void rpmh_invalidate(const struct device *dev) { }