diff --git a/drivers/interconnect/qcom/bcm-voter.c b/drivers/interconnect/qcom/bcm-voter.c index 8a1ef9301e77..d93526cee898 100644 --- a/drivers/interconnect/qcom/bcm-voter.c +++ b/drivers/interconnect/qcom/bcm-voter.c @@ -97,12 +97,21 @@ static void bcm_aggregate(struct qcom_icc_bcm *bcm, bool init) } } - if (bcm->keepalive) { + if (bcm->keepalive || bcm->keepalive_early) { + /* + * Keepalive should normally only be enforced for AMC/WAKE so + * that BCMs are only kept alive when HLOS is active. But early + * during init all clients haven't had a chance to vot yet, and + * some have use cases that persist when HLOS is asleep. So + * during init vote to all sets, including SLEEP. + */ if (init) { bcm->vote_x[QCOM_ICC_BUCKET_AMC] = 16000; bcm->vote_x[QCOM_ICC_BUCKET_WAKE] = 16000; + bcm->vote_x[QCOM_ICC_BUCKET_SLEEP] = 16000; bcm->vote_y[QCOM_ICC_BUCKET_AMC] = 16000; bcm->vote_y[QCOM_ICC_BUCKET_WAKE] = 16000; + bcm->vote_y[QCOM_ICC_BUCKET_SLEEP] = 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; diff --git a/drivers/interconnect/qcom/icc-rpmh.c b/drivers/interconnect/qcom/icc-rpmh.c index 93e999f99e63..766b7fe95a90 100644 --- a/drivers/interconnect/qcom/icc-rpmh.c +++ b/drivers/interconnect/qcom/icc-rpmh.c @@ -167,11 +167,14 @@ EXPORT_SYMBOL_GPL(qcom_icc_xlate_extended); * * Return: 0 on success, or an error code otherwise */ -int qcom_icc_bcm_init(struct qcom_icc_bcm *bcm, struct device *dev) +int qcom_icc_bcm_init(struct qcom_icc_provider *qp, struct qcom_icc_bcm *bcm, + struct device *dev) { struct qcom_icc_node *qn; const struct bcm_db *data; + struct bcm_voter *voter; size_t data_count; + int ret; int i; /* BCM is already initialised*/ @@ -214,10 +217,91 @@ int qcom_icc_bcm_init(struct qcom_icc_bcm *bcm, struct device *dev) qn->num_bcms++; } + if (bcm->keepalive || bcm->keepalive_early) { + voter = qp->voters[bcm->voter_idx]; + qcom_icc_bcm_voter_add(voter, bcm); + + ret = qcom_icc_bcm_voter_commit(voter); + if (ret) { + dev_err(dev, "failed to place initial vote for %s\n", + bcm->name); + return ret; + } + } + return 0; } EXPORT_SYMBOL_GPL(qcom_icc_bcm_init); +static bool bcm_needs_qos_proxy(struct qcom_icc_bcm *bcm) +{ + int i; + + if (bcm->qos_proxy) + return true; + + if (bcm->voter_idx == 0) + for (i = 0; i < bcm->num_nodes; i++) + if (bcm->nodes[i]->qosbox) + return true; + + return false; +} + +static int enable_qos_deps(struct qcom_icc_provider *qp) +{ + struct qcom_icc_bcm *bcm; + struct bcm_voter *voter; + bool keepalive; + int ret, i; + + for (i = 0; i < qp->num_bcms; i++) { + bcm = qp->bcms[i]; + if (bcm_needs_qos_proxy(bcm)) { + keepalive = bcm->keepalive; + bcm->keepalive = true; + + voter = qp->voters[bcm->voter_idx]; + qcom_icc_bcm_voter_add(voter, bcm); + ret = qcom_icc_bcm_voter_commit(voter); + + bcm->keepalive = keepalive; + + if (ret) { + dev_err(qp->dev, "failed to vote BW to %s for QoS\n", + bcm->name); + return ret; + } + } + } + + ret = clk_bulk_prepare_enable(qp->num_clks, qp->clks); + if (ret) { + dev_err(qp->dev, "failed to enable clocks for QoS\n"); + return ret; + } + + return 0; +} + +static void disable_qos_deps(struct qcom_icc_provider *qp) +{ + struct qcom_icc_bcm *bcm; + struct bcm_voter *voter; + int i; + + clk_bulk_disable_unprepare(qp->num_clks, qp->clks); + + for (i = 0; i < qp->num_bcms; i++) { + bcm = qp->bcms[i]; + if (bcm_needs_qos_proxy(bcm)) { + voter = qp->voters[bcm->voter_idx]; + qcom_icc_bcm_voter_add(voter, bcm); + qcom_icc_bcm_voter_commit(voter); + } + } +} + static struct regmap *qcom_icc_rpmh_map(struct platform_device *pdev, const struct qcom_icc_desc *desc) { @@ -229,7 +313,7 @@ static struct regmap *qcom_icc_rpmh_map(struct platform_device *pdev, if (!res) return NULL; - base = devm_ioremap_resource(dev, res); + base = devm_ioremap(dev, res->start, resource_size(res)); if (IS_ERR(base)) return ERR_CAST(base); @@ -264,6 +348,7 @@ int qcom_icc_rpmh_probe(struct platform_device *pdev) return -ENOMEM; qp->stub = of_property_read_bool(pdev->dev.of_node, "qcom,stub"); + qp->skip_qos = of_property_read_bool(pdev->dev.of_node, "qcom,skip-qos"); provider = &qp->provider; provider->dev = dev; @@ -307,14 +392,14 @@ int qcom_icc_rpmh_probe(struct platform_device *pdev) if (qp->num_clks < 0) return qp->num_clks; - ret = clk_bulk_prepare_enable(qp->num_clks, qp->clks); - if (ret) { - dev_err(&pdev->dev, "failed to enable clocks\n"); - return ret; - } - for (i = 0; i < qp->num_bcms; i++) - qcom_icc_bcm_init(qp->bcms[i], dev); + qcom_icc_bcm_init(qp, qp->bcms[i], dev); + + if (!qp->skip_qos) { + ret = enable_qos_deps(qp); + if (ret) + return ret; + } for (i = 0; i < num_nodes; i++) { qn = qnodes[i]; @@ -329,7 +414,7 @@ int qcom_icc_rpmh_probe(struct platform_device *pdev) goto err; } - if (qn->qosbox) + if (qn->qosbox && !qp->skip_qos) qn->noc_ops->set_qos(qn); node->name = qn->name; @@ -342,6 +427,9 @@ int qcom_icc_rpmh_probe(struct platform_device *pdev) data->nodes[i] = node; } + if (!qp->skip_qos) + disable_qos_deps(qp); + data->num_nodes = num_nodes; platform_set_drvdata(pdev, qp); @@ -407,9 +495,11 @@ void qcom_icc_rpmh_sync_state(struct device *dev) for (i = 0; i < qp->num_bcms; i++) { bcm = qp->bcms[i]; - if (!bcm->keepalive) + if (!bcm->keepalive && !bcm->keepalive_early) continue; + bcm->keepalive_early = false; + voter = qp->voters[bcm->voter_idx]; qcom_icc_bcm_voter_add(voter, bcm); qcom_icc_bcm_voter_commit(voter); diff --git a/drivers/interconnect/qcom/icc-rpmh.h b/drivers/interconnect/qcom/icc-rpmh.h index b75609a55e9a..01d52d8eb187 100644 --- a/drivers/interconnect/qcom/icc-rpmh.h +++ b/drivers/interconnect/qcom/icc-rpmh.h @@ -33,6 +33,7 @@ struct qcom_icc_provider { struct bcm_voter **voters; size_t num_voters; bool stub; + bool skip_qos; }; /** @@ -98,6 +99,9 @@ struct qcom_icc_node { * @perf_mode_mask: mask to OR with enable_mask when QCOM_ICC_TAG_PERF_MODE is set * @dirty: flag used to indicate whether the bcm needs to be committed * @keepalive: flag used to indicate whether a keepalive is required + * @keepalive_early: keepalive only prior to sync-state + * @qos_proxy: flag used to indicate whether a proxy vote needed as part of + * qos configuration * @aux_data: auxiliary data used when calculating threshold values and * communicating with RPMh * @list: used to link to other bcms when compiling lists for commit @@ -116,6 +120,8 @@ struct qcom_icc_bcm { u32 perf_mode_mask; bool dirty; bool keepalive; + bool keepalive_early; + bool qos_proxy; struct bcm_db aux_data; struct list_head list; struct list_head ws_list; @@ -156,7 +162,7 @@ int qcom_icc_aggregate_stub(struct icc_node *node, u32 tag, u32 avg_bw, int qcom_icc_set(struct icc_node *src, struct icc_node *dst); int qcom_icc_set_stub(struct icc_node *src, struct icc_node *dst); struct icc_node_data *qcom_icc_xlate_extended(struct of_phandle_args *spec, void *data); -int qcom_icc_bcm_init(struct qcom_icc_bcm *bcm, struct device *dev); +int qcom_icc_bcm_init(struct qcom_icc_provider *qp, 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); diff --git a/drivers/interconnect/qcom/qnoc-qos.c b/drivers/interconnect/qcom/qnoc-qos.c index 2eb98897a216..d87ca48a0eb6 100644 --- a/drivers/interconnect/qcom/qnoc-qos.c +++ b/drivers/interconnect/qcom/qnoc-qos.c @@ -16,6 +16,8 @@ #define QOS_SLV_URG_MSG_EN_SHFT 3 # define QOS_DFLT_PRIO_MASK 0x7 # define QOS_DFLT_PRIO_SHFT 4 +#define QOS_DISABLE_SHIFT 24 + const u8 icc_qnoc_qos_regs[][QOSGEN_OFF_MAX_REGS] = { [ICC_QNOC_QOSGEN_TYPE_RPMH] = { @@ -45,6 +47,10 @@ static void qcom_icc_set_qos(struct qcom_icc_node *node) return; for (port = 0; port < qos->num_ports; port++) { + regmap_update_bits(node->regmap, QOSGEN_MAINCTL_LO(qos, port), + BIT(QOS_DISABLE_SHIFT), + qos->config->prio_fwd_disable << QOS_DISABLE_SHIFT); + regmap_update_bits(node->regmap, QOSGEN_MAINCTL_LO(qos, port), QOS_DFLT_PRIO_MASK << QOS_DFLT_PRIO_SHFT, qos->config->prio << QOS_DFLT_PRIO_SHFT); diff --git a/drivers/interconnect/qcom/qnoc-qos.h b/drivers/interconnect/qcom/qnoc-qos.h index f22d0cd0e3ac..4f04dd781190 100644 --- a/drivers/interconnect/qcom/qnoc-qos.h +++ b/drivers/interconnect/qcom/qnoc-qos.h @@ -32,6 +32,7 @@ struct qcom_icc_noc_ops { struct qos_config { u32 prio; u32 urg_fwd; + bool prio_fwd_disable; }; struct qcom_icc_qosbox {