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@xxxxxxxxxx> --- 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(-) diff --git a/drivers/interconnect/qcom/bcm-voter.c b/drivers/interconnect/qcom/bcm-voter.c index d5f2a6b5376b..f8fbddb87e6b 100644 --- a/drivers/interconnect/qcom/bcm-voter.c +++ b/drivers/interconnect/qcom/bcm-voter.c @@ -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; diff --git a/drivers/interconnect/qcom/icc-rpmh.c b/drivers/interconnect/qcom/icc-rpmh.c index fdb5e58e408b..53298148f24b 100644 --- a/drivers/interconnect/qcom/icc-rpmh.c +++ b/drivers/interconnect/qcom/icc-rpmh.c @@ -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); diff --git a/drivers/interconnect/qcom/icc-rpmh.h b/drivers/interconnect/qcom/icc-rpmh.h index 7843d8864d6b..5634d302963a 100644 --- a/drivers/interconnect/qcom/icc-rpmh.h +++ b/drivers/interconnect/qcom/icc-rpmh.h @@ -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 -- 2.41.0