From 796c5f9e431d359396a94ad672ca7db0d8c71324 Mon Sep 17 00:00:00 2001 From: Mike Tipton Date: Wed, 5 May 2021 00:09:22 -0700 Subject: [PATCH 1/8] interconnect: qcom: icc-rpmh: Enable BCMs needed for QoS configuration Some QoS blocks require voting for BCMs before their registers can be accessed, since they require clocks and regulators controlled by BCM. Vote for these BCMs before configuring QoS during probe. Change-Id: Ic93d224c7dc56c8e56d527179a0c2d1e888e3822 Signed-off-by: Mike Tipton --- drivers/interconnect/qcom/icc-rpmh.c | 78 +++++++++++++++++++++++++--- 1 file changed, 72 insertions(+), 6 deletions(-) diff --git a/drivers/interconnect/qcom/icc-rpmh.c b/drivers/interconnect/qcom/icc-rpmh.c index 93e999f99e63..0b4e6124c709 100644 --- a/drivers/interconnect/qcom/icc-rpmh.c +++ b/drivers/interconnect/qcom/icc-rpmh.c @@ -218,6 +218,72 @@ int qcom_icc_bcm_init(struct qcom_icc_bcm *bcm, struct device *dev) } EXPORT_SYMBOL_GPL(qcom_icc_bcm_init); +static bool bcm_needs_qos_proxy(struct qcom_icc_bcm *bcm) +{ + int i; + + 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) { @@ -307,15 +373,13 @@ 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); + ret = enable_qos_deps(qp); + if (ret) + return ret; + for (i = 0; i < num_nodes; i++) { qn = qnodes[i]; if (!qn) @@ -342,6 +406,8 @@ int qcom_icc_rpmh_probe(struct platform_device *pdev) data->nodes[i] = node; } + disable_qos_deps(qp); + data->num_nodes = num_nodes; platform_set_drvdata(pdev, qp); From d9aae10c87c7b87edc12af99b2087092b6c777cb Mon Sep 17 00:00:00 2001 From: Vivek Aknurwar Date: Thu, 9 Sep 2021 18:44:25 -0700 Subject: [PATCH 2/8] interconnect: qcom: icc-rpmh: Add skip-qos node property Add skip-qos node property to skip qos settings which happens on each provider probe. Early in presil this property is useful to skip qos settings as not all needed clocks are voted/set on and device ends up unclocked access. This property will help to deliver interconnect earlier without qos settings which can be enabled when things are ready before bring up. Change-Id: I9619f651f40f49ec03d9ea0dc7c45b3b13ffae6c Signed-off-by: Vivek Aknurwar Signed-off-by: Mike Tipton --- drivers/interconnect/qcom/icc-rpmh.c | 14 +++++++++----- drivers/interconnect/qcom/icc-rpmh.h | 1 + 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/drivers/interconnect/qcom/icc-rpmh.c b/drivers/interconnect/qcom/icc-rpmh.c index 0b4e6124c709..2f1d1ccf64e3 100644 --- a/drivers/interconnect/qcom/icc-rpmh.c +++ b/drivers/interconnect/qcom/icc-rpmh.c @@ -330,6 +330,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; @@ -376,9 +377,11 @@ int qcom_icc_rpmh_probe(struct platform_device *pdev) for (i = 0; i < qp->num_bcms; i++) qcom_icc_bcm_init(qp->bcms[i], dev); - ret = enable_qos_deps(qp); - if (ret) - return ret; + if (!qp->skip_qos) { + ret = enable_qos_deps(qp); + if (ret) + return ret; + } for (i = 0; i < num_nodes; i++) { qn = qnodes[i]; @@ -393,7 +396,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; @@ -406,7 +409,8 @@ int qcom_icc_rpmh_probe(struct platform_device *pdev) data->nodes[i] = node; } - disable_qos_deps(qp); + if (!qp->skip_qos) + disable_qos_deps(qp); data->num_nodes = num_nodes; platform_set_drvdata(pdev, qp); diff --git a/drivers/interconnect/qcom/icc-rpmh.h b/drivers/interconnect/qcom/icc-rpmh.h index b75609a55e9a..a5cb4485d0de 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; }; /** From 487d9a368e255fb72fc6f712f523c0abe431b93c Mon Sep 17 00:00:00 2001 From: Veera Vegivada Date: Tue, 1 Feb 2022 11:36:22 +0530 Subject: [PATCH 3/8] interconnect: qcom: icc-rpmh: use devm_ioremap Using devm_ioremap_resource will check the resource boundaries. In legacy hardwares we have memory overlaps between the noc devices. Hence use devm_ioremap instead of devm_ioremap_resource. Change-Id: Ifc8a1bb60183058a7c3845c231edfa53115d817e Signed-off-by: Veera Vegivada --- drivers/interconnect/qcom/icc-rpmh.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/interconnect/qcom/icc-rpmh.c b/drivers/interconnect/qcom/icc-rpmh.c index 2f1d1ccf64e3..40e5c19c5627 100644 --- a/drivers/interconnect/qcom/icc-rpmh.c +++ b/drivers/interconnect/qcom/icc-rpmh.c @@ -295,7 +295,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); From fd8e26e71e92cea03eb89a5031d069f32842d765 Mon Sep 17 00:00:00 2001 From: Veera Vegivada Date: Tue, 7 Sep 2021 11:35:15 +0530 Subject: [PATCH 4/8] interconnect: qcom: icc-rpmh: Add support for enabling BCMs needed for QoS Some QoS blocks require voting for BCMs before their registers can be accessed, since they require clocks and regulators controlled by BCM. Hence add qos_proxy flag to indicate the BCM needs to be voted for QoS configuration. Change-Id: Idc618e7a3d28d31d38d85f5b21e6b9f12e86ebe2 Signed-off-by: Veera Vegivada --- drivers/interconnect/qcom/icc-rpmh.c | 3 +++ drivers/interconnect/qcom/icc-rpmh.h | 3 +++ 2 files changed, 6 insertions(+) diff --git a/drivers/interconnect/qcom/icc-rpmh.c b/drivers/interconnect/qcom/icc-rpmh.c index 40e5c19c5627..82e919ab1094 100644 --- a/drivers/interconnect/qcom/icc-rpmh.c +++ b/drivers/interconnect/qcom/icc-rpmh.c @@ -222,6 +222,9 @@ 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) diff --git a/drivers/interconnect/qcom/icc-rpmh.h b/drivers/interconnect/qcom/icc-rpmh.h index a5cb4485d0de..fa8cf1b84631 100644 --- a/drivers/interconnect/qcom/icc-rpmh.h +++ b/drivers/interconnect/qcom/icc-rpmh.h @@ -99,6 +99,8 @@ 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 + * @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 @@ -117,6 +119,7 @@ struct qcom_icc_bcm { u32 perf_mode_mask; bool dirty; bool keepalive; + bool qos_proxy; struct bcm_db aux_data; struct list_head list; struct list_head ws_list; From bba0dddd2a757198496bd23f238fa9c045d7024c Mon Sep 17 00:00:00 2001 From: Vivek Aknurwar Date: Fri, 28 Jan 2022 13:52:54 -0800 Subject: [PATCH 5/8] interconnect: qcom: qnoc-qos: Add prio_fwd_disable support Add prio_fwd_disable flag support to stop forwarding QoS information from the master. So respective noc will ignore master's claimed priority and send out the default priortiy. Change-Id: I21ca11b44f200453c70c02f195779ef583a90e8c Signed-off-by: Vivek Aknurwar --- drivers/interconnect/qcom/qnoc-qos.c | 6 ++++++ drivers/interconnect/qcom/qnoc-qos.h | 1 + 2 files changed, 7 insertions(+) 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 { From b3588ad7e068e4478ee4a506824c9db06c42b52e Mon Sep 17 00:00:00 2001 From: Mike Tipton Date: Fri, 9 Jul 2021 12:54:49 -0700 Subject: [PATCH 6/8] interconnect: qcom: icc-rpmh: Vote initial floor in sleep set Currently the initial floor is only voted in AMC/WAKE, which means the floor is removed when entering the sleep set. However, some use cases require bus BW even when the CPU is idle. If all clients haven't voted yet, then assume we need to keep these votes around permanently. Change-Id: I2277e62e0a4e1ea949f1c7fbad052e76cf451c68 Signed-off-by: Mike Tipton --- drivers/interconnect/qcom/bcm-voter.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/drivers/interconnect/qcom/bcm-voter.c b/drivers/interconnect/qcom/bcm-voter.c index 8a1ef9301e77..499944018dcd 100644 --- a/drivers/interconnect/qcom/bcm-voter.c +++ b/drivers/interconnect/qcom/bcm-voter.c @@ -98,11 +98,20 @@ static void bcm_aggregate(struct qcom_icc_bcm *bcm, bool init) } if (bcm->keepalive) { + /* + * 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; From d2d4f6d7778bfeb32982f41a8ba14449eb298ce3 Mon Sep 17 00:00:00 2001 From: Mike Tipton Date: Wed, 7 Jul 2021 15:32:10 -0700 Subject: [PATCH 7/8] interconnect: qcom: icc-rpmh: Add keepalive_early flag Some BCMs only need to be kept alive early in the boot process. Add the keepalive_early flag, which only prevents them from turning off before the sync_state callback. Change-Id: I0699fb0420d1d9f9473e28ede57380fd5b08ca53 Signed-off-by: Mike Tipton --- drivers/interconnect/qcom/bcm-voter.c | 2 +- drivers/interconnect/qcom/icc-rpmh.c | 4 +++- drivers/interconnect/qcom/icc-rpmh.h | 2 ++ 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/drivers/interconnect/qcom/bcm-voter.c b/drivers/interconnect/qcom/bcm-voter.c index 499944018dcd..d93526cee898 100644 --- a/drivers/interconnect/qcom/bcm-voter.c +++ b/drivers/interconnect/qcom/bcm-voter.c @@ -97,7 +97,7 @@ 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 diff --git a/drivers/interconnect/qcom/icc-rpmh.c b/drivers/interconnect/qcom/icc-rpmh.c index 82e919ab1094..a0f4924d7f7a 100644 --- a/drivers/interconnect/qcom/icc-rpmh.c +++ b/drivers/interconnect/qcom/icc-rpmh.c @@ -480,9 +480,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 fa8cf1b84631..d3d74a69b7f7 100644 --- a/drivers/interconnect/qcom/icc-rpmh.h +++ b/drivers/interconnect/qcom/icc-rpmh.h @@ -99,6 +99,7 @@ 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 @@ -119,6 +120,7 @@ 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; From 2fbcc44e15fb64489c2fc5e623bbedb9719a905f Mon Sep 17 00:00:00 2001 From: Mike Tipton Date: Thu, 24 Mar 2022 16:04:42 -0700 Subject: [PATCH 8/8] interconnect: qcom: icc-rpmh: Vote for keepalive BCMs during init Place initial votes for BCMs marked as keepalive during initialization such that we force the BCMs on even in the absence of explicit client votes. Change-Id: I0cf1b2f2a76d5fc094329520ce2a9f1691a3f681 Signed-off-by: Mike Tipton --- drivers/interconnect/qcom/icc-rpmh.c | 19 +++++++++++++++++-- drivers/interconnect/qcom/icc-rpmh.h | 2 +- 2 files changed, 18 insertions(+), 3 deletions(-) diff --git a/drivers/interconnect/qcom/icc-rpmh.c b/drivers/interconnect/qcom/icc-rpmh.c index a0f4924d7f7a..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,6 +217,18 @@ 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); @@ -378,7 +393,7 @@ int qcom_icc_rpmh_probe(struct platform_device *pdev) return qp->num_clks; 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); diff --git a/drivers/interconnect/qcom/icc-rpmh.h b/drivers/interconnect/qcom/icc-rpmh.h index d3d74a69b7f7..01d52d8eb187 100644 --- a/drivers/interconnect/qcom/icc-rpmh.h +++ b/drivers/interconnect/qcom/icc-rpmh.h @@ -162,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);