diff --git a/drivers/interconnect/qcom/icc-rpmh.c b/drivers/interconnect/qcom/icc-rpmh.c index 367b70810db5..2d7d5f2b7341 100644 --- a/drivers/interconnect/qcom/icc-rpmh.c +++ b/drivers/interconnect/qcom/icc-rpmh.c @@ -177,4 +177,82 @@ int qcom_icc_bcm_init(struct qcom_icc_bcm *bcm, struct device *dev) } EXPORT_SYMBOL(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; +} + +/** + * qcom_icc_enable_qos_deps - enable clocks and BCMs required for QoS + * @qp: interconnect provider associated with masters whose QoS to be set + * + * Return: 0 on success, or an error code otherwise + */ +int qcom_icc_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; +} +EXPORT_SYMBOL(qcom_icc_enable_qos_deps); + +/** + * qcom_icc_disable_qos_deps - disable clocks and BCMs + * @qp: interconnect provider associated with masters whose QoS to be set + */ +void qcom_icc_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); + } + } +} +EXPORT_SYMBOL(qcom_icc_disable_qos_deps); + MODULE_LICENSE("GPL v2"); diff --git a/drivers/interconnect/qcom/icc-rpmh.h b/drivers/interconnect/qcom/icc-rpmh.h index c4d255ffe365..ce0e9a5fb5fb 100644 --- a/drivers/interconnect/qcom/icc-rpmh.h +++ b/drivers/interconnect/qcom/icc-rpmh.h @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0-only */ /* - * Copyright (c) 2019-2020, The Linux Foundation. All rights reserved. + * Copyright (c) 2019-2021, The Linux Foundation. All rights reserved. * */ @@ -149,5 +149,7 @@ int qcom_icc_aggregate(struct icc_node *node, u32 tag, u32 avg_bw, int qcom_icc_set(struct icc_node *src, struct icc_node *dst); 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_enable_qos_deps(struct qcom_icc_provider *qp); +void qcom_icc_disable_qos_deps(struct qcom_icc_provider *qp); #endif diff --git a/drivers/interconnect/qcom/shima.c b/drivers/interconnect/qcom/shima.c index ad6c4bb014ce..f372f4515d28 100644 --- a/drivers/interconnect/qcom/shima.c +++ b/drivers/interconnect/qcom/shima.c @@ -2548,12 +2548,12 @@ static int qnoc_probe(struct platform_device *pdev) if (qp->num_clks < 0) return qp->num_clks; - ret = clk_bulk_prepare_enable(qp->num_clks, qp->clks); + for (i = 0; i < qp->num_bcms; i++) + qcom_icc_bcm_init(qp->bcms[i], &pdev->dev); - if (ret) { - dev_err(&pdev->dev, "failed to enable clocks\n"); + ret = qcom_icc_enable_qos_deps(qp); + if (ret) return ret; - } for (i = 0; i < num_nodes; i++) { size_t j; @@ -2589,11 +2589,7 @@ static int qnoc_probe(struct platform_device *pdev) } data->num_nodes = num_nodes; - clk_bulk_disable_unprepare(qp->num_clks, qp->clks); - - for (i = 0; i < qp->num_bcms; i++) - qcom_icc_bcm_init(qp->bcms[i], &pdev->dev); - + qcom_icc_disable_qos_deps(qp); platform_set_drvdata(pdev, qp); dev_info(&pdev->dev, "Registered SHIMA ICC\n"); @@ -2609,6 +2605,7 @@ err: icc_node_destroy(node->id); } + qcom_icc_disable_qos_deps(qp); clk_bulk_put_all(qp->num_clks, qp->clks); icc_provider_del(provider);