It makes zero (or less) sense to consume BCM voters per interconnect
provider. They are shared throughout the entire system and it's enough
to keep a single reference to each of them.
Since the list of these voters is common across SoCs and across buses
on them, turn to caching a pointer to each voter at a dt-bindings-defined
index in a shared array to make accesses O(1) (instead of a clunky
loop-based lookup) and vastly save on redefining & referencing the same
set over and over again.
Signed-off-by: Konrad Dybcio <konrad.dybcio@linaro.org>
---
drivers/interconnect/qcom/bcm-voter.c | 29 ++++++++++++++++++++++++++++-
drivers/interconnect/qcom/icc-rpmh.c | 16 +++++++++-------
drivers/interconnect/qcom/icc-rpmh.h | 4 ++++
3 files changed, 41 insertions(+), 8 deletions(-)
@@ -19,6 +19,17 @@
static LIST_HEAD(bcm_voters);
static DEFINE_MUTEX(bcm_voter_lock);
+struct bcm_voter *qcom_icc_bcm_voters[ICC_BCM_VOTER_MAX] = { };
+EXPORT_SYMBOL_GPL(qcom_icc_bcm_voters);
+
+static const char * const bcm_voter_names[ICC_BCM_VOTER_MAX] = {
+ [ICC_BCM_VOTER_APPS] = "APPS",
+ [ICC_BCM_VOTER_DISP] = "DISP",
+ [ICC_BCM_VOTER_CAM0] = "CAM0",
+ [ICC_BCM_VOTER_CAM1] = "CAM1",
+ [ICC_BCM_VOTER_CAM2] = "CAM2",
+};
+
/**
* struct bcm_voter - Bus Clock Manager voter
* @dev: reference to the device that communicates with the BCM
@@ -37,6 +48,7 @@ struct bcm_voter {
struct list_head ws_list;
struct list_head voter_node;
u32 tcs_wait;
+ u32 voter_idx;
};
static int cmp_vcd(void *priv, const struct list_head *a, const struct list_head *b)
@@ -353,12 +365,27 @@ static int qcom_icc_bcm_voter_probe(struct platform_device *pdev)
if (of_property_read_u32(np, "qcom,tcs-wait", &voter->tcs_wait))
voter->tcs_wait = QCOM_ICC_TAG_ACTIVE_ONLY;
+ /*
+ * This is the best guess we can make..
+ * Not registering BCMs correctly would be gamebreaking anyway!
+ */
+ if (of_property_read_u32(np, "qcom,bcm-voter-idx", &voter->voter_idx))
+ voter->voter_idx = ICC_BCM_VOTER_APPS;
+
mutex_init(&voter->lock);
INIT_LIST_HEAD(&voter->commit_list);
INIT_LIST_HEAD(&voter->ws_list);
mutex_lock(&bcm_voter_lock);
- list_add_tail(&voter->voter_node, &bcm_voters);
+ /* Do not attempt to register BCMs with the same ID twice! */
+ if (qcom_icc_bcm_voters[voter->voter_idx]) {
+ mutex_unlock(&bcm_voter_lock);
+ dev_err(&pdev->dev, "Attempted to overwrite %s BCM voter!\n",
+ bcm_voter_names[voter->voter_idx]);
+ return -EINVAL;
+ }
+
+ qcom_icc_bcm_voters[voter->voter_idx] = voter;
mutex_unlock(&bcm_voter_lock);
return 0;
@@ -20,9 +20,9 @@
*/
void qcom_icc_pre_aggregate(struct icc_node *node)
{
- size_t i;
- struct qcom_icc_node *qn;
struct qcom_icc_provider *qp;
+ struct qcom_icc_node *qn;
+ int i;
qn = node->data;
qp = to_qcom_provider(node->provider);
@@ -33,7 +33,7 @@ void qcom_icc_pre_aggregate(struct icc_node *node)
}
for (i = 0; i < qn->num_bcms; i++)
- qcom_icc_bcm_voter_add(qp->voter, qn->bcms[i]);
+ qcom_icc_bcm_voter_add(qcom_icc_bcm_voters[ICC_BCM_VOTER_APPS], qn->bcms[i]);
}
EXPORT_SYMBOL_GPL(qcom_icc_pre_aggregate);
@@ -95,7 +95,7 @@ int qcom_icc_set(struct icc_node *src, struct icc_node *dst)
qp = to_qcom_provider(node->provider);
- qcom_icc_bcm_voter_commit(qp->voter);
+ qcom_icc_bcm_voter_commit(qcom_icc_bcm_voters[ICC_BCM_VOTER_APPS]);
return 0;
}
@@ -167,6 +167,7 @@ int qcom_icc_rpmh_probe(struct platform_device *pdev)
struct icc_provider *provider;
struct qcom_icc_node * const *qnodes, *qn;
struct qcom_icc_provider *qp;
+ struct device_node *bcm_node;
struct icc_node *node;
size_t num_nodes, i, j;
int ret;
@@ -200,9 +201,10 @@ int qcom_icc_rpmh_probe(struct platform_device *pdev)
qp->bcms = desc->bcms;
qp->num_bcms = desc->num_bcms;
- qp->voter = of_bcm_voter_get(qp->dev, NULL);
- if (IS_ERR(qp->voter))
- return PTR_ERR(qp->voter);
+ /* Ensure the BCM voter is reachable (unless we don't have any) */
+ qp->voter = qcom_icc_bcm_voters[ICC_BCM_VOTER_APPS];
+ if (qp->num_bcms && !qp->voter)
+ return -EPROBE_DEFER;
for (i = 0; i < qp->num_bcms; i++)
qcom_icc_bcm_init(qp->bcms[i], dev);
@@ -88,6 +88,7 @@ struct qcom_icc_node {
* communicating with RPMh
* @list: used to link to other bcms when compiling lists for commit
* @ws_list: used to keep track of bcms that may transition between wake/sleep
+ * @voter_idx: index of the BCM voter used to convey votes to AOSS
* @num_nodes: total number of @num_nodes
* @nodes: list of qcom_icc_nodes that this BCM encapsulates
*/
@@ -104,6 +105,7 @@ struct qcom_icc_bcm {
struct bcm_db aux_data;
struct list_head list;
struct list_head ws_list;
+ u8 voter_idx;
size_t num_nodes;
struct qcom_icc_node *nodes[];
};
@@ -138,4 +140,6 @@ void qcom_icc_pre_aggregate(struct icc_node *node);
int qcom_icc_rpmh_probe(struct platform_device *pdev);
int qcom_icc_rpmh_remove(struct platform_device *pdev);
+extern struct bcm_voter *qcom_icc_bcm_voters[ICC_BCM_VOTER_MAX];
+
#endif