diff --git a/drivers/interconnect/qcom/bcm-voter.c b/drivers/interconnect/qcom/bcm-voter.c index 262aea4f5612..db743e2dba38 100644 --- a/drivers/interconnect/qcom/bcm-voter.c +++ b/drivers/interconnect/qcom/bcm-voter.c @@ -28,6 +28,7 @@ static DEFINE_MUTEX(bcm_voter_lock); * @ws_list: list containing bcms that have different wake/sleep votes * @voter_node: list of bcm voters * @tcs_wait: mask for which buckets require TCS completion + * @init: flag to determine when init has completed. */ struct bcm_voter { struct device *dev; @@ -37,6 +38,7 @@ struct bcm_voter { struct list_head ws_list; struct list_head voter_node; u32 tcs_wait; + bool init; }; static int cmp_vcd(void *priv, const struct list_head *a, const struct list_head *b) @@ -58,7 +60,7 @@ static u64 bcm_div(u64 num, u32 base) return num; } -static void bcm_aggregate(struct qcom_icc_bcm *bcm) +static void bcm_aggregate(struct qcom_icc_bcm *bcm, bool init) { struct qcom_icc_node *node; size_t i, bucket; @@ -86,10 +88,18 @@ static void bcm_aggregate(struct qcom_icc_bcm *bcm) } if (bcm->keepalive) { - bcm->vote_x[QCOM_ICC_BUCKET_AMC] = 16000; - bcm->vote_x[QCOM_ICC_BUCKET_WAKE] = 16000; - bcm->vote_y[QCOM_ICC_BUCKET_AMC] = 16000; - bcm->vote_y[QCOM_ICC_BUCKET_WAKE] = 16000; + if (init) { + bcm->vote_x[QCOM_ICC_BUCKET_AMC] = 16000; + bcm->vote_x[QCOM_ICC_BUCKET_WAKE] = 16000; + bcm->vote_y[QCOM_ICC_BUCKET_AMC] = 16000; + bcm->vote_y[QCOM_ICC_BUCKET_WAKE] = 16000; + } else if (bcm->vote_x[QCOM_ICC_BUCKET_AMC] == 0 && + bcm->vote_y[QCOM_ICC_BUCKET_AMC] == 0) { + bcm->vote_x[QCOM_ICC_BUCKET_AMC] = 1; + bcm->vote_x[QCOM_ICC_BUCKET_WAKE] = 1; + bcm->vote_y[QCOM_ICC_BUCKET_AMC] = 1; + bcm->vote_y[QCOM_ICC_BUCKET_WAKE] = 1; + } } } @@ -255,7 +265,7 @@ int qcom_icc_bcm_voter_commit(struct bcm_voter *voter) mutex_lock(&voter->lock); list_for_each_entry(bcm, &voter->commit_list, list) - bcm_aggregate(bcm); + bcm_aggregate(bcm, voter->init); /* * Pre sort the BCMs based on VCD for ease of generating a command list @@ -332,6 +342,21 @@ int qcom_icc_bcm_voter_commit(struct bcm_voter *voter) } EXPORT_SYMBOL_GPL(qcom_icc_bcm_voter_commit); +/** + * qcom_icc_bcm_voter_clear_init - clear init flag used during boot up + * @voter: voter that we need to clear the init flag for + */ +void qcom_icc_bcm_voter_clear_init(struct bcm_voter *voter) +{ + if (!voter) + return; + + mutex_lock(&voter->lock); + voter->init = false; + mutex_unlock(&voter->lock); +} +EXPORT_SYMBOL(qcom_icc_bcm_voter_clear_init); + static int qcom_icc_bcm_voter_probe(struct platform_device *pdev) { struct device_node *np = pdev->dev.of_node; @@ -343,6 +368,7 @@ static int qcom_icc_bcm_voter_probe(struct platform_device *pdev) voter->dev = &pdev->dev; voter->np = np; + voter->init = true; if (of_property_read_u32(np, "qcom,tcs-wait", &voter->tcs_wait)) voter->tcs_wait = QCOM_ICC_TAG_ACTIVE_ONLY; diff --git a/drivers/interconnect/qcom/bcm-voter.h b/drivers/interconnect/qcom/bcm-voter.h index 0f64c0bab2c0..8f96ba1e1e13 100644 --- a/drivers/interconnect/qcom/bcm-voter.h +++ b/drivers/interconnect/qcom/bcm-voter.h @@ -23,5 +23,6 @@ static struct qcom_icc_bcm _name = { \ struct bcm_voter *of_bcm_voter_get(struct device *dev, const char *name); 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); +void qcom_icc_bcm_voter_clear_init(struct bcm_voter *voter); #endif diff --git a/drivers/interconnect/qcom/icc-rpmh.c b/drivers/interconnect/qcom/icc-rpmh.c index 2c8e12549804..ff3a46689bfa 100644 --- a/drivers/interconnect/qcom/icc-rpmh.c +++ b/drivers/interconnect/qcom/icc-rpmh.c @@ -7,12 +7,16 @@ #include #include #include +#include #include #include #include "bcm-voter.h" #include "icc-rpmh.h" +static LIST_HEAD(qnoc_probe_list); +static DEFINE_MUTEX(probe_list_lock); + /** * qcom_icc_pre_aggregate - cleans up stale values from prior icc_set * @node: icc node to operate on @@ -258,6 +262,10 @@ int qcom_icc_rpmh_probe(struct platform_device *pdev) data->num_nodes = num_nodes; platform_set_drvdata(pdev, qp); + mutex_lock(&probe_list_lock); + list_add_tail(&qp->probe_list, &qnoc_probe_list); + mutex_unlock(&probe_list_lock); + return 0; err: icc_nodes_remove(provider); @@ -275,4 +283,46 @@ int qcom_icc_rpmh_remove(struct platform_device *pdev) } EXPORT_SYMBOL_GPL(qcom_icc_rpmh_remove); +void qcom_icc_rpmh_sync_state(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + const struct of_device_id *oft = dev->driver->of_match_table; + struct qcom_icc_provider *qp = platform_get_drvdata(pdev); + struct qcom_icc_bcm *bcm; + struct bcm_voter *voter; + static int probe_count; + int num_providers; + + for (num_providers = 0; oft[num_providers].data; num_providers++) + ; + + mutex_lock(&probe_list_lock); + probe_count++; + + if (probe_count < num_providers) { + mutex_unlock(&probe_list_lock); + return; + } + + list_for_each_entry(qp, &qnoc_probe_list, probe_list) { + int i; + + for (i = 0; i < qp->num_voters; i++) + qcom_icc_bcm_voter_clear_init(qp->voters[i]); + + for (i = 0; i < qp->num_bcms; i++) { + bcm = qp->bcms[i]; + if (!bcm->keepalive) + continue; + + voter = qp->voters[bcm->voter_idx]; + qcom_icc_bcm_voter_add(voter, bcm); + qcom_icc_bcm_voter_commit(voter); + } + } + + mutex_unlock(&probe_list_lock); +} +EXPORT_SYMBOL(qcom_icc_rpmh_sync_state); + MODULE_LICENSE("GPL v2"); diff --git a/drivers/interconnect/qcom/icc-rpmh.h b/drivers/interconnect/qcom/icc-rpmh.h index 4bfc060529ba..9af364147fff 100644 --- a/drivers/interconnect/qcom/icc-rpmh.h +++ b/drivers/interconnect/qcom/icc-rpmh.h @@ -25,6 +25,7 @@ struct qcom_icc_provider { struct qcom_icc_bcm **bcms; size_t num_bcms; struct bcm_voter *voter; + struct list_head probe_list; }; /** @@ -136,5 +137,5 @@ int qcom_icc_bcm_init(struct qcom_icc_bcm *bcm, struct device *dev); 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); - +void qcom_icc_rpmh_sync_state(struct device *dev); #endif