diff --git a/drivers/interconnect/qcom/bcm-voter.c b/drivers/interconnect/qcom/bcm-voter.c index a2d437a05a11..c15abb57cd24 100644 --- a/drivers/interconnect/qcom/bcm-voter.c +++ b/drivers/interconnect/qcom/bcm-voter.c @@ -261,6 +261,40 @@ void qcom_icc_bcm_voter_add(struct bcm_voter *voter, struct qcom_icc_bcm *bcm) } EXPORT_SYMBOL_GPL(qcom_icc_bcm_voter_add); +/** + * qcom_icc_bcm_get_bw - get current bcm vote + * @voter: voter used to query bcm + * @bcm: bcm to get current vote from + */ +void qcom_icc_bcm_get_bw(struct bcm_voter *voter, + struct qcom_icc_bcm *bcm) +{ + struct tcs_cmd cmd = { .addr = bcm->addr }; + int ret, i; + u64 x, y; + + mutex_lock(&voter->lock); + + ret = rpmh_read(voter->dev, &cmd); + if (ret) { + pr_err("Error sending AMC RPMH requests (%d)\n", ret); + goto out; + } + + x = FIELD_GET(BCM_TCS_CMD_VOTE_X_MASK, cmd.data); + y = FIELD_GET(BCM_TCS_CMD_VOTE_Y_MASK, cmd.data); + + /* For boot-up, fill the AMC vote in all buckets */ + for (i = 0; i < QCOM_ICC_NUM_BUCKETS; i++) { + bcm->vote_x[i] = x; + bcm->vote_y[i] = y; + } + +out: + mutex_unlock(&voter->lock); +} +EXPORT_SYMBOL_GPL(qcom_icc_bcm_get_bw); + /** * qcom_icc_bcm_voter_commit - generates and commits tcs cmds based on bcms * @voter: voter that needs flushing diff --git a/drivers/interconnect/qcom/bcm-voter.h b/drivers/interconnect/qcom/bcm-voter.h index b4d36e349f3c..fc75d457dcc7 100644 --- a/drivers/interconnect/qcom/bcm-voter.h +++ b/drivers/interconnect/qcom/bcm-voter.h @@ -13,6 +13,7 @@ #include "icc-rpmh.h" struct bcm_voter *of_bcm_voter_get(struct device *dev, const char *name); +void qcom_icc_bcm_get_bw(struct bcm_voter *voter, struct qcom_icc_bcm *bcm); void qcom_icc_bcm_voter_add(struct bcm_voter *voter, struct qcom_icc_bcm *bcm); int qcom_icc_bcm_voter_commit(struct bcm_voter *voter); diff --git a/drivers/interconnect/qcom/icc-rpmh.c b/drivers/interconnect/qcom/icc-rpmh.c index 3b445acefece..7f2b5673629b 100644 --- a/drivers/interconnect/qcom/icc-rpmh.c +++ b/drivers/interconnect/qcom/icc-rpmh.c @@ -136,6 +136,61 @@ int qcom_icc_set(struct icc_node *src, struct icc_node *dst) } EXPORT_SYMBOL_GPL(qcom_icc_set); +static int qcom_icc_get_bw(struct icc_node *node, u32 *avg, u32 *peak) +{ + struct qcom_icc_node *qn = node->data; + u32 avg_max = 0; + u32 peak_max = 0; + u64 x, y; + int i; + + if (!qn->num_bcms) { + *avg = INT_MAX; + *peak = INT_MAX; + + return 0; + } + + for (i = 0; i < qn->num_bcms; ++i) { + struct qcom_icc_bcm *bcm = qn->bcms[i]; + + /* Use AMC vote for boot-up */ + x = bcm->vote_x[QCOM_ICC_BUCKET_AMC]; + y = bcm->vote_y[QCOM_ICC_BUCKET_AMC]; + + /* Consider enable mask and convert to INT_MAX */ + if (bcm->enable_mask) { + if (x & bcm->enable_mask) + avg_max = INT_MAX; + if (y & bcm->enable_mask) + peak_max = INT_MAX; + } else { + if (x) { + x *= bcm->aux_data.unit; + do_div(x, bcm->vote_scale); + x *= qn->buswidth * qn->channels; + do_div(x, bcm->aux_data.width); + + avg_max = max(avg_max, x); + } + + if (y) { + y *= bcm->aux_data.unit; + do_div(y, bcm->vote_scale); + y *= qn->buswidth; + do_div(y, bcm->aux_data.width); + + peak_max = max(peak_max, y); + } + } + } + + *avg = avg_max; + *peak = peak_max; + + return 0; +} + /** * qcom_icc_bcm_init - populates bcm aux data and connect qnodes * @bcm: bcm to be initialized @@ -255,6 +310,7 @@ int qcom_icc_rpmh_probe(struct platform_device *pdev) provider = &qp->provider; provider->dev = dev; provider->set = qcom_icc_set; + provider->get_bw = qcom_icc_get_bw; provider->pre_aggregate = qcom_icc_pre_aggregate; provider->aggregate = qcom_icc_aggregate; provider->xlate_extended = qcom_icc_xlate_extended; @@ -272,8 +328,10 @@ int qcom_icc_rpmh_probe(struct platform_device *pdev) if (IS_ERR(qp->voter)) return PTR_ERR(qp->voter); - for (i = 0; i < qp->num_bcms; i++) + for (i = 0; i < qp->num_bcms; i++) { qcom_icc_bcm_init(qp->bcms[i], dev); + qcom_icc_bcm_get_bw(qp->voter, qp->bcms[i]); + } for (i = 0; i < num_nodes; i++) { qn = qnodes[i];