From 6643b532b3c3412a2e2c351aed100ab4fb31884c Mon Sep 17 00:00:00 2001 From: Konrad Dybcio Date: Fri, 7 Apr 2023 22:14:43 +0200 Subject: [PATCH 01/15] interconnect: qcom: rpm: Rename icc desc clocks to bus_blocks Rename the "clocks" (and _names) fields of qcom_icc_desc to "bus_clocks" in preparation for introducing handling of clocks that need to be enabled but not voted on with aggregate frequency. Signed-off-by: Konrad Dybcio Link: https://lore.kernel.org/r/20230228-topic-qos-v8-1-ee696a2c15a9@linaro.org Signed-off-by: Georgi Djakov --- drivers/interconnect/qcom/icc-rpm.c | 6 +++--- drivers/interconnect/qcom/icc-rpm.h | 4 ++-- drivers/interconnect/qcom/msm8996.c | 12 ++++++------ drivers/interconnect/qcom/sdm660.c | 8 ++++---- 4 files changed, 15 insertions(+), 15 deletions(-) diff --git a/drivers/interconnect/qcom/icc-rpm.c b/drivers/interconnect/qcom/icc-rpm.c index 5341fa169dbf..ee3d09a6850e 100644 --- a/drivers/interconnect/qcom/icc-rpm.c +++ b/drivers/interconnect/qcom/icc-rpm.c @@ -440,9 +440,9 @@ int qnoc_probe(struct platform_device *pdev) qnodes = desc->nodes; num_nodes = desc->num_nodes; - if (desc->num_clocks) { - cds = desc->clocks; - cd_num = desc->num_clocks; + if (desc->num_bus_clocks) { + cds = desc->bus_clocks; + cd_num = desc->num_bus_clocks; } else { cds = bus_clocks; cd_num = ARRAY_SIZE(bus_clocks); diff --git a/drivers/interconnect/qcom/icc-rpm.h b/drivers/interconnect/qcom/icc-rpm.h index 22bdb1e4bb12..689300a2fd4e 100644 --- a/drivers/interconnect/qcom/icc-rpm.h +++ b/drivers/interconnect/qcom/icc-rpm.h @@ -91,8 +91,8 @@ struct qcom_icc_node { struct qcom_icc_desc { struct qcom_icc_node * const *nodes; size_t num_nodes; - const char * const *clocks; - size_t num_clocks; + const char * const *bus_clocks; + size_t num_bus_clocks; enum qcom_icc_type type; const struct regmap_config *regmap_cfg; unsigned int qos_offset; diff --git a/drivers/interconnect/qcom/msm8996.c b/drivers/interconnect/qcom/msm8996.c index 14efd2761b7a..21f998cd19f0 100644 --- a/drivers/interconnect/qcom/msm8996.c +++ b/drivers/interconnect/qcom/msm8996.c @@ -1821,8 +1821,8 @@ static const struct qcom_icc_desc msm8996_a0noc = { .type = QCOM_ICC_NOC, .nodes = a0noc_nodes, .num_nodes = ARRAY_SIZE(a0noc_nodes), - .clocks = bus_a0noc_clocks, - .num_clocks = ARRAY_SIZE(bus_a0noc_clocks), + .bus_clocks = bus_a0noc_clocks, + .num_bus_clocks = ARRAY_SIZE(bus_a0noc_clocks), .regmap_cfg = &msm8996_a0noc_regmap_config }; @@ -1865,8 +1865,8 @@ static const struct qcom_icc_desc msm8996_a2noc = { .type = QCOM_ICC_NOC, .nodes = a2noc_nodes, .num_nodes = ARRAY_SIZE(a2noc_nodes), - .clocks = bus_a2noc_clocks, - .num_clocks = ARRAY_SIZE(bus_a2noc_clocks), + .bus_clocks = bus_a2noc_clocks, + .num_bus_clocks = ARRAY_SIZE(bus_a2noc_clocks), .regmap_cfg = &msm8996_a2noc_regmap_config }; @@ -2004,8 +2004,8 @@ static const struct qcom_icc_desc msm8996_mnoc = { .type = QCOM_ICC_NOC, .nodes = mnoc_nodes, .num_nodes = ARRAY_SIZE(mnoc_nodes), - .clocks = bus_mm_clocks, - .num_clocks = ARRAY_SIZE(bus_mm_clocks), + .bus_clocks = bus_mm_clocks, + .num_bus_clocks = ARRAY_SIZE(bus_mm_clocks), .regmap_cfg = &msm8996_mnoc_regmap_config }; diff --git a/drivers/interconnect/qcom/sdm660.c b/drivers/interconnect/qcom/sdm660.c index 8d879b0bcabc..a22ba821efbf 100644 --- a/drivers/interconnect/qcom/sdm660.c +++ b/drivers/interconnect/qcom/sdm660.c @@ -1516,8 +1516,8 @@ static const struct qcom_icc_desc sdm660_a2noc = { .type = QCOM_ICC_NOC, .nodes = sdm660_a2noc_nodes, .num_nodes = ARRAY_SIZE(sdm660_a2noc_nodes), - .clocks = bus_a2noc_clocks, - .num_clocks = ARRAY_SIZE(bus_a2noc_clocks), + .bus_clocks = bus_a2noc_clocks, + .num_bus_clocks = ARRAY_SIZE(bus_a2noc_clocks), .regmap_cfg = &sdm660_a2noc_regmap_config, }; @@ -1659,8 +1659,8 @@ static const struct qcom_icc_desc sdm660_mnoc = { .type = QCOM_ICC_NOC, .nodes = sdm660_mnoc_nodes, .num_nodes = ARRAY_SIZE(sdm660_mnoc_nodes), - .clocks = bus_mm_clocks, - .num_clocks = ARRAY_SIZE(bus_mm_clocks), + .bus_clocks = bus_mm_clocks, + .num_bus_clocks = ARRAY_SIZE(bus_mm_clocks), .regmap_cfg = &sdm660_mnoc_regmap_config, }; From 1a12928e25627e02126ad2d1c12cfdba79d6bd94 Mon Sep 17 00:00:00 2001 From: Konrad Dybcio Date: Fri, 7 Apr 2023 22:14:44 +0200 Subject: [PATCH 02/15] interconnect: qcom: rpm: Rename icc provider num_clocks to num_bus_clocks In preparation for handling non-scaling clocks that we still have to enable, rename num_clocks to more descriptive num_bus_clocks. Signed-off-by: Konrad Dybcio Link: https://lore.kernel.org/r/20230228-topic-qos-v8-2-ee696a2c15a9@linaro.org Signed-off-by: Georgi Djakov --- drivers/interconnect/qcom/icc-rpm.c | 12 ++++++------ drivers/interconnect/qcom/icc-rpm.h | 4 ++-- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/drivers/interconnect/qcom/icc-rpm.c b/drivers/interconnect/qcom/icc-rpm.c index ee3d09a6850e..8dce3dcff8da 100644 --- a/drivers/interconnect/qcom/icc-rpm.c +++ b/drivers/interconnect/qcom/icc-rpm.c @@ -379,7 +379,7 @@ static int qcom_icc_set(struct icc_node *src, struct icc_node *dst) return ret; } - for (i = 0; i < qp->num_clks; i++) { + for (i = 0; i < qp->num_bus_clks; i++) { /* * Use WAKE bucket for active clock, otherwise, use SLEEP bucket * for other clocks. If a platform doesn't set interconnect @@ -464,7 +464,7 @@ int qnoc_probe(struct platform_device *pdev) for (i = 0; i < cd_num; i++) qp->bus_clks[i].id = cds[i]; - qp->num_clks = cd_num; + qp->num_bus_clks = cd_num; qp->type = desc->type; qp->qos_offset = desc->qos_offset; @@ -494,11 +494,11 @@ int qnoc_probe(struct platform_device *pdev) } regmap_done: - ret = devm_clk_bulk_get_optional(dev, qp->num_clks, qp->bus_clks); + ret = devm_clk_bulk_get_optional(dev, qp->num_bus_clks, qp->bus_clks); if (ret) return ret; - ret = clk_bulk_prepare_enable(qp->num_clks, qp->bus_clks); + ret = clk_bulk_prepare_enable(qp->num_bus_clks, qp->bus_clks); if (ret) return ret; @@ -551,7 +551,7 @@ int qnoc_probe(struct platform_device *pdev) icc_provider_deregister(provider); err_remove_nodes: icc_nodes_remove(provider); - clk_bulk_disable_unprepare(qp->num_clks, qp->bus_clks); + clk_bulk_disable_unprepare(qp->num_bus_clks, qp->bus_clks); return ret; } @@ -563,7 +563,7 @@ int qnoc_remove(struct platform_device *pdev) icc_provider_deregister(&qp->provider); icc_nodes_remove(&qp->provider); - clk_bulk_disable_unprepare(qp->num_clks, qp->bus_clks); + clk_bulk_disable_unprepare(qp->num_bus_clks, qp->bus_clks); return 0; } diff --git a/drivers/interconnect/qcom/icc-rpm.h b/drivers/interconnect/qcom/icc-rpm.h index 689300a2fd4e..2436777820a4 100644 --- a/drivers/interconnect/qcom/icc-rpm.h +++ b/drivers/interconnect/qcom/icc-rpm.h @@ -23,7 +23,7 @@ enum qcom_icc_type { /** * struct qcom_icc_provider - Qualcomm specific interconnect provider * @provider: generic interconnect provider - * @num_clks: the total number of clk_bulk_data entries + * @num_bus_clks: the total number of bus_clks clk_bulk_data entries * @type: the ICC provider type * @regmap: regmap for QoS registers read/write access * @qos_offset: offset to QoS registers @@ -32,7 +32,7 @@ enum qcom_icc_type { */ struct qcom_icc_provider { struct icc_provider provider; - int num_clks; + int num_bus_clks; enum qcom_icc_type type; struct regmap *regmap; unsigned int qos_offset; From ca545907c712f0045468ae66e1bf52dc69922ebe Mon Sep 17 00:00:00 2001 From: Konrad Dybcio Date: Fri, 7 Apr 2023 22:14:45 +0200 Subject: [PATCH 03/15] interconnect: qcom: rpm: Drop unused parameters The QoS-setting functions do not care about the bandwidth values passed. Drop them. Signed-off-by: Konrad Dybcio Link: https://lore.kernel.org/r/20230228-topic-qos-v8-3-ee696a2c15a9@linaro.org Signed-off-by: Georgi Djakov --- drivers/interconnect/qcom/icc-rpm.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/drivers/interconnect/qcom/icc-rpm.c b/drivers/interconnect/qcom/icc-rpm.c index 8dce3dcff8da..7d62c0bf2722 100644 --- a/drivers/interconnect/qcom/icc-rpm.c +++ b/drivers/interconnect/qcom/icc-rpm.c @@ -50,7 +50,7 @@ #define NOC_QOS_MODE_FIXED_VAL 0x0 #define NOC_QOS_MODE_BYPASS_VAL 0x2 -static int qcom_icc_set_qnoc_qos(struct icc_node *src, u64 max_bw) +static int qcom_icc_set_qnoc_qos(struct icc_node *src) { struct icc_provider *provider = src->provider; struct qcom_icc_provider *qp = to_qcom_provider(provider); @@ -95,7 +95,7 @@ static int qcom_icc_bimc_set_qos_health(struct qcom_icc_provider *qp, mask, val); } -static int qcom_icc_set_bimc_qos(struct icc_node *src, u64 max_bw) +static int qcom_icc_set_bimc_qos(struct icc_node *src) { struct qcom_icc_provider *qp; struct qcom_icc_node *qn; @@ -150,7 +150,7 @@ static int qcom_icc_noc_set_qos_priority(struct qcom_icc_provider *qp, NOC_QOS_PRIORITY_P0_MASK, qos->prio_level); } -static int qcom_icc_set_noc_qos(struct icc_node *src, u64 max_bw) +static int qcom_icc_set_noc_qos(struct icc_node *src) { struct qcom_icc_provider *qp; struct qcom_icc_node *qn; @@ -187,7 +187,7 @@ static int qcom_icc_set_noc_qos(struct icc_node *src, u64 max_bw) NOC_QOS_MODEn_MASK, mode); } -static int qcom_icc_qos_set(struct icc_node *node, u64 sum_bw) +static int qcom_icc_qos_set(struct icc_node *node) { struct qcom_icc_provider *qp = to_qcom_provider(node->provider); struct qcom_icc_node *qn = node->data; @@ -196,11 +196,11 @@ static int qcom_icc_qos_set(struct icc_node *node, u64 sum_bw) switch (qp->type) { case QCOM_ICC_BIMC: - return qcom_icc_set_bimc_qos(node, sum_bw); + return qcom_icc_set_bimc_qos(node); case QCOM_ICC_QNOC: - return qcom_icc_set_qnoc_qos(node, sum_bw); + return qcom_icc_set_qnoc_qos(node); default: - return qcom_icc_set_noc_qos(node, sum_bw); + return qcom_icc_set_noc_qos(node); } } From 32882f657e7811abb6a883653b68dce0539a693e Mon Sep 17 00:00:00 2001 From: Konrad Dybcio Date: Fri, 7 Apr 2023 22:14:46 +0200 Subject: [PATCH 04/15] interconnect: qcom: rpm: Set QoS registers only once The QoS registers are (or according to Qualcomm folks, on most platforms) persistent until a full chip reboot. Move the QoS-setting functions to the probe function so that we don't needlessly do it over and over again. Signed-off-by: Konrad Dybcio Link: https://lore.kernel.org/r/20230228-topic-qos-v8-4-ee696a2c15a9@linaro.org Signed-off-by: Georgi Djakov --- drivers/interconnect/qcom/icc-rpm.c | 50 ++++++++++++----------------- 1 file changed, 21 insertions(+), 29 deletions(-) diff --git a/drivers/interconnect/qcom/icc-rpm.c b/drivers/interconnect/qcom/icc-rpm.c index 7d62c0bf2722..d79e508cb717 100644 --- a/drivers/interconnect/qcom/icc-rpm.c +++ b/drivers/interconnect/qcom/icc-rpm.c @@ -204,30 +204,33 @@ static int qcom_icc_qos_set(struct icc_node *node) } } -static int qcom_icc_rpm_set(int mas_rpm_id, int slv_rpm_id, u64 sum_bw) +static int qcom_icc_rpm_set(struct qcom_icc_node *qn, u64 sum_bw) { int ret = 0; - if (mas_rpm_id != -1) { + if (qn->qos.ap_owned) + return 0; + + if (qn->mas_rpm_id != -1) { ret = qcom_icc_rpm_smd_send(QCOM_SMD_RPM_ACTIVE_STATE, RPM_BUS_MASTER_REQ, - mas_rpm_id, + qn->mas_rpm_id, sum_bw); if (ret) { pr_err("qcom_icc_rpm_smd_send mas %d error %d\n", - mas_rpm_id, ret); + qn->mas_rpm_id, ret); return ret; } } - if (slv_rpm_id != -1) { + if (qn->slv_rpm_id != -1) { ret = qcom_icc_rpm_smd_send(QCOM_SMD_RPM_ACTIVE_STATE, RPM_BUS_SLAVE_REQ, - slv_rpm_id, + qn->slv_rpm_id, sum_bw); if (ret) { pr_err("qcom_icc_rpm_smd_send slv %d error %d\n", - slv_rpm_id, ret); + qn->slv_rpm_id, ret); return ret; } } @@ -235,26 +238,6 @@ static int qcom_icc_rpm_set(int mas_rpm_id, int slv_rpm_id, u64 sum_bw) return ret; } -static int __qcom_icc_set(struct icc_node *n, struct qcom_icc_node *qn, - u64 sum_bw) -{ - int ret; - - if (!qn->qos.ap_owned) { - /* send bandwidth request message to the RPM processor */ - ret = qcom_icc_rpm_set(qn->mas_rpm_id, qn->slv_rpm_id, sum_bw); - if (ret) - return ret; - } else if (qn->qos.qos_mode != NOC_QOS_MODE_INVALID) { - /* set bandwidth directly from the AP */ - ret = qcom_icc_qos_set(n, sum_bw); - if (ret) - return ret; - } - - return 0; -} - /** * qcom_icc_pre_bw_aggregate - cleans up values before re-aggregate requests * @node: icc node to operate on @@ -370,11 +353,12 @@ static int qcom_icc_set(struct icc_node *src, struct icc_node *dst) sum_bw = icc_units_to_bps(max_agg_avg); - ret = __qcom_icc_set(src, src_qn, sum_bw); + ret = qcom_icc_rpm_set(src_qn, sum_bw); if (ret) return ret; + if (dst_qn) { - ret = __qcom_icc_set(dst, dst_qn, sum_bw); + ret = qcom_icc_rpm_set(dst_qn, sum_bw); if (ret) return ret; } @@ -528,6 +512,14 @@ int qnoc_probe(struct platform_device *pdev) for (j = 0; j < qnodes[i]->num_links; j++) icc_link_create(node, qnodes[i]->links[j]); + /* Set QoS registers (we only need to do it once, generally) */ + if (qnodes[i]->qos.ap_owned && + qnodes[i]->qos.qos_mode != NOC_QOS_MODE_INVALID) { + ret = qcom_icc_qos_set(node); + if (ret) + return ret; + } + data->nodes[i] = node; } data->num_nodes = num_nodes; From 375cccc6593650ab9436d3d5fad7eabd7085fff3 Mon Sep 17 00:00:00 2001 From: Dmitry Baryshkov Date: Fri, 12 May 2023 03:13:31 +0300 Subject: [PATCH 05/15] dt-bindings: interconnect/msm8996-cbf: add defines to be used by CBF On msm8996 CBF interconnects power and performance CPU clusters. Add corresponding interconnect defines to be used in device trees. Acked-by: Krzysztof Kozlowski Tested-by: Yassine Oudjana Signed-off-by: Dmitry Baryshkov Link: https://lore.kernel.org/r/20230512001334.2983048-2-dmitry.baryshkov@linaro.org Signed-off-by: Georgi Djakov --- include/dt-bindings/interconnect/qcom,msm8996-cbf.h | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 include/dt-bindings/interconnect/qcom,msm8996-cbf.h diff --git a/include/dt-bindings/interconnect/qcom,msm8996-cbf.h b/include/dt-bindings/interconnect/qcom,msm8996-cbf.h new file mode 100644 index 000000000000..aac5e69f6bd5 --- /dev/null +++ b/include/dt-bindings/interconnect/qcom,msm8996-cbf.h @@ -0,0 +1,12 @@ +/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) */ +/* + * Copyright (C) 2023 Linaro Ltd. All rights reserved. + */ + +#ifndef __DT_BINDINGS_INTERCONNECT_QCOM_MSM8996_CBF_H +#define __DT_BINDINGS_INTERCONNECT_QCOM_MSM8996_CBF_H + +#define MASTER_CBF_M4M 0 +#define SLAVE_CBF_M4M 1 + +#endif From 0ac2a08f42ce5c06d5d1216eac59c046961acd4f Mon Sep 17 00:00:00 2001 From: Dmitry Baryshkov Date: Fri, 12 May 2023 03:13:32 +0300 Subject: [PATCH 06/15] interconnect: add clk-based icc provider support For some devices it is useful to export clocks as interconnect providers, if the clock corresponds to bus bandwidth. For example, on MSM8996 the cluster interconnect clock should be scaled according to the cluster frequencies. Exporting it as an interconnect allows one to properly describe this as the cluster bandwidth requirements. Tested-by: Yassine Oudjana Signed-off-by: Dmitry Baryshkov Link: https://lore.kernel.org/r/20230512001334.2983048-3-dmitry.baryshkov@linaro.org Signed-off-by: Georgi Djakov --- drivers/interconnect/Kconfig | 6 ++ drivers/interconnect/Makefile | 2 + drivers/interconnect/icc-clk.c | 168 +++++++++++++++++++++++++++++++ include/linux/interconnect-clk.h | 22 ++++ 4 files changed, 198 insertions(+) create mode 100644 drivers/interconnect/icc-clk.c create mode 100644 include/linux/interconnect-clk.h diff --git a/drivers/interconnect/Kconfig b/drivers/interconnect/Kconfig index d637a89d4695..5faa8d2aecff 100644 --- a/drivers/interconnect/Kconfig +++ b/drivers/interconnect/Kconfig @@ -15,4 +15,10 @@ source "drivers/interconnect/imx/Kconfig" source "drivers/interconnect/qcom/Kconfig" source "drivers/interconnect/samsung/Kconfig" +config INTERCONNECT_CLK + tristate + depends on COMMON_CLK + help + Support for wrapping clocks into the interconnect nodes. + endif diff --git a/drivers/interconnect/Makefile b/drivers/interconnect/Makefile index 97d393fd638d..5604ce351a9f 100644 --- a/drivers/interconnect/Makefile +++ b/drivers/interconnect/Makefile @@ -7,3 +7,5 @@ obj-$(CONFIG_INTERCONNECT) += icc-core.o obj-$(CONFIG_INTERCONNECT_IMX) += imx/ obj-$(CONFIG_INTERCONNECT_QCOM) += qcom/ obj-$(CONFIG_INTERCONNECT_SAMSUNG) += samsung/ + +obj-$(CONFIG_INTERCONNECT_CLK) += icc-clk.o diff --git a/drivers/interconnect/icc-clk.c b/drivers/interconnect/icc-clk.c new file mode 100644 index 000000000000..0db3b654548b --- /dev/null +++ b/drivers/interconnect/icc-clk.c @@ -0,0 +1,168 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (c) 2023, Linaro Ltd. + */ + +#include +#include +#include +#include + +struct icc_clk_node { + struct clk *clk; + bool enabled; +}; + +struct icc_clk_provider { + struct icc_provider provider; + int num_clocks; + struct icc_clk_node clocks[]; +}; + +#define to_icc_clk_provider(_provider) \ + container_of(_provider, struct icc_clk_provider, provider) + +static int icc_clk_set(struct icc_node *src, struct icc_node *dst) +{ + struct icc_clk_node *qn = src->data; + int ret; + + if (!qn || !qn->clk) + return 0; + + if (!src->peak_bw) { + if (qn->enabled) + clk_disable_unprepare(qn->clk); + qn->enabled = false; + + return 0; + } + + if (!qn->enabled) { + ret = clk_prepare_enable(qn->clk); + if (ret) + return ret; + qn->enabled = true; + } + + return clk_set_rate(qn->clk, icc_units_to_bps(src->peak_bw)); +} + +static int icc_clk_get_bw(struct icc_node *node, u32 *avg, u32 *peak) +{ + struct icc_clk_node *qn = node->data; + + if (!qn || !qn->clk) + *peak = INT_MAX; + else + *peak = Bps_to_icc(clk_get_rate(qn->clk)); + + return 0; +} + +/** + * icc_clk_register() - register a new clk-based interconnect provider + * @dev: device supporting this provider + * @first_id: an ID of the first provider's node + * @num_clocks: number of instances of struct icc_clk_data + * @data: data for the provider + * + * Registers and returns a clk-based interconnect provider. It is a simple + * wrapper around COMMON_CLK framework, allowing other devices to vote on the + * clock rate. + * + * Return: 0 on success, or an error code otherwise + */ +struct icc_provider *icc_clk_register(struct device *dev, + unsigned int first_id, + unsigned int num_clocks, + const struct icc_clk_data *data) +{ + struct icc_clk_provider *qp; + struct icc_provider *provider; + struct icc_onecell_data *onecell; + struct icc_node *node; + int ret, i, j; + + onecell = devm_kzalloc(dev, struct_size(onecell, nodes, 2 * num_clocks), GFP_KERNEL); + if (!onecell) + return ERR_PTR(-ENOMEM); + + qp = devm_kzalloc(dev, struct_size(qp, clocks, num_clocks), GFP_KERNEL); + if (!qp) + return ERR_PTR(-ENOMEM); + + qp->num_clocks = num_clocks; + + provider = &qp->provider; + provider->dev = dev; + provider->get_bw = icc_clk_get_bw; + provider->set = icc_clk_set; + provider->aggregate = icc_std_aggregate; + provider->xlate = of_icc_xlate_onecell; + INIT_LIST_HEAD(&provider->nodes); + provider->data = onecell; + + icc_provider_init(provider); + + for (i = 0, j = 0; i < num_clocks; i++) { + qp->clocks[i].clk = data[i].clk; + + node = icc_node_create(first_id + j); + if (IS_ERR(node)) { + ret = PTR_ERR(node); + goto err; + } + + node->name = devm_kasprintf(dev, GFP_KERNEL, "%s_master", data[i].name); + node->data = &qp->clocks[i]; + icc_node_add(node, provider); + /* link to the next node, slave */ + icc_link_create(node, first_id + j + 1); + onecell->nodes[j++] = node; + + node = icc_node_create(first_id + j); + if (IS_ERR(node)) { + ret = PTR_ERR(node); + goto err; + } + + node->name = devm_kasprintf(dev, GFP_KERNEL, "%s_slave", data[i].name); + /* no data for slave node */ + icc_node_add(node, provider); + onecell->nodes[j++] = node; + } + + onecell->num_nodes = j; + + ret = icc_provider_register(provider); + if (ret) + goto err; + + return provider; + +err: + icc_nodes_remove(provider); + + return ERR_PTR(ret); +} + +/** + * icc_clk_unregister() - unregister a previously registered clk interconnect provider + * @provider: provider returned by icc_clk_register() + */ +void icc_clk_unregister(struct icc_provider *provider) +{ + struct icc_clk_provider *qp = container_of(provider, struct icc_clk_provider, provider); + int i; + + icc_provider_deregister(&qp->provider); + icc_nodes_remove(&qp->provider); + + for (i = 0; i < qp->num_clocks; i++) { + struct icc_clk_node *qn = &qp->clocks[i]; + + if (qn->enabled) + clk_disable_unprepare(qn->clk); + } +} diff --git a/include/linux/interconnect-clk.h b/include/linux/interconnect-clk.h new file mode 100644 index 000000000000..0cd80112bea5 --- /dev/null +++ b/include/linux/interconnect-clk.h @@ -0,0 +1,22 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (c) 2023, Linaro Ltd. + */ + +#ifndef __LINUX_INTERCONNECT_CLK_H +#define __LINUX_INTERCONNECT_CLK_H + +struct device; + +struct icc_clk_data { + struct clk *clk; + const char *name; +}; + +struct icc_provider *icc_clk_register(struct device *dev, + unsigned int first_id, + unsigned int num_clocks, + const struct icc_clk_data *data); +void icc_clk_unregister(struct icc_provider *provider); + +#endif From 2e2113c8a64f2668baca28a82144a4f0dc22a217 Mon Sep 17 00:00:00 2001 From: Konrad Dybcio Date: Thu, 18 May 2023 21:58:01 +0200 Subject: [PATCH 07/15] interconnect: qcom: rpm: Handle interface clocks Some (but not all) providers (or their specific nodes) require specific clocks to be turned on before they can be accessed. Failure to ensure that results in a seemingly random system crash (which would usually happen at boot with the interconnect driver built-in), resulting in the platform not booting up properly. Limit the number of bus_clocks to 2 (which is the maximum that SMD RPM interconnect supports anyway) and handle non-scaling clocks separately. Update MSM8996 and SDM660 drivers to make sure they do not regress with this change. This unfortunately has to be done in one patch to prevent either compile errors or broken bisect. Signed-off-by: Konrad Dybcio Link: https://lore.kernel.org/r/20230518195801.2556998-1-konrad.dybcio@linaro.org Signed-off-by: Georgi Djakov --- drivers/interconnect/qcom/icc-rpm.c | 52 ++++++++++++++++++++--------- drivers/interconnect/qcom/icc-rpm.h | 14 ++++++-- drivers/interconnect/qcom/msm8996.c | 22 +++++------- drivers/interconnect/qcom/sdm660.c | 16 ++++----- 4 files changed, 64 insertions(+), 40 deletions(-) diff --git a/drivers/interconnect/qcom/icc-rpm.c b/drivers/interconnect/qcom/icc-rpm.c index d79e508cb717..c34f75703a66 100644 --- a/drivers/interconnect/qcom/icc-rpm.c +++ b/drivers/interconnect/qcom/icc-rpm.c @@ -409,7 +409,7 @@ int qnoc_probe(struct platform_device *pdev) struct qcom_icc_provider *qp; struct icc_node *node; size_t num_nodes, i; - const char * const *cds; + const char * const *cds = NULL; int cd_num; int ret; @@ -424,6 +424,31 @@ int qnoc_probe(struct platform_device *pdev) qnodes = desc->nodes; num_nodes = desc->num_nodes; + if (desc->num_intf_clocks) { + cds = desc->intf_clocks; + cd_num = desc->num_intf_clocks; + } else { + /* 0 intf clocks is perfectly fine */ + cd_num = 0; + } + + qp = devm_kzalloc(dev, sizeof(*qp), GFP_KERNEL); + if (!qp) + return -ENOMEM; + + qp->intf_clks = devm_kzalloc(dev, sizeof(qp->intf_clks), GFP_KERNEL); + if (!qp->intf_clks) + return -ENOMEM; + + data = devm_kzalloc(dev, struct_size(data, nodes, num_nodes), + GFP_KERNEL); + if (!data) + return -ENOMEM; + + qp->num_intf_clks = cd_num; + for (i = 0; i < cd_num; i++) + qp->intf_clks[i].id = cds[i]; + if (desc->num_bus_clocks) { cds = desc->bus_clocks; cd_num = desc->num_bus_clocks; @@ -432,20 +457,6 @@ int qnoc_probe(struct platform_device *pdev) cd_num = ARRAY_SIZE(bus_clocks); } - qp = devm_kzalloc(dev, struct_size(qp, bus_clks, cd_num), GFP_KERNEL); - if (!qp) - return -ENOMEM; - - qp->bus_clk_rate = devm_kcalloc(dev, cd_num, sizeof(*qp->bus_clk_rate), - GFP_KERNEL); - if (!qp->bus_clk_rate) - return -ENOMEM; - - data = devm_kzalloc(dev, struct_size(data, nodes, num_nodes), - GFP_KERNEL); - if (!data) - return -ENOMEM; - for (i = 0; i < cd_num; i++) qp->bus_clks[i].id = cds[i]; qp->num_bus_clks = cd_num; @@ -486,6 +497,10 @@ int qnoc_probe(struct platform_device *pdev) if (ret) return ret; + ret = devm_clk_bulk_get(dev, qp->num_intf_clks, qp->intf_clks); + if (ret) + return ret; + provider = &qp->provider; provider->dev = dev; provider->set = qcom_icc_set; @@ -496,6 +511,11 @@ int qnoc_probe(struct platform_device *pdev) icc_provider_init(provider); + /* If this fails, bus accesses will crash the platform! */ + ret = clk_bulk_prepare_enable(qp->num_intf_clks, qp->intf_clks); + if (ret) + return ret; + for (i = 0; i < num_nodes; i++) { size_t j; @@ -524,6 +544,8 @@ int qnoc_probe(struct platform_device *pdev) } data->num_nodes = num_nodes; + clk_bulk_disable_unprepare(qp->num_intf_clks, qp->intf_clks); + ret = icc_provider_register(provider); if (ret) goto err_remove_nodes; diff --git a/drivers/interconnect/qcom/icc-rpm.h b/drivers/interconnect/qcom/icc-rpm.h index 2436777820a4..b9b63860042f 100644 --- a/drivers/interconnect/qcom/icc-rpm.h +++ b/drivers/interconnect/qcom/icc-rpm.h @@ -20,24 +20,32 @@ enum qcom_icc_type { QCOM_ICC_QNOC, }; +#define NUM_BUS_CLKS 2 + /** * struct qcom_icc_provider - Qualcomm specific interconnect provider * @provider: generic interconnect provider * @num_bus_clks: the total number of bus_clks clk_bulk_data entries + * @num_intf_clks: the total number of intf_clks clk_bulk_data entries * @type: the ICC provider type * @regmap: regmap for QoS registers read/write access * @qos_offset: offset to QoS registers * @bus_clk_rate: bus clock rate in Hz * @bus_clks: the clk_bulk_data table of bus clocks + * @intf_clks: a clk_bulk_data array of interface clocks + * @is_on: whether the bus is powered on */ struct qcom_icc_provider { struct icc_provider provider; int num_bus_clks; + int num_intf_clks; enum qcom_icc_type type; struct regmap *regmap; unsigned int qos_offset; - u64 *bus_clk_rate; - struct clk_bulk_data bus_clks[]; + u64 bus_clk_rate[NUM_BUS_CLKS]; + struct clk_bulk_data bus_clks[NUM_BUS_CLKS]; + struct clk_bulk_data *intf_clks; + bool is_on; }; /** @@ -93,6 +101,8 @@ struct qcom_icc_desc { size_t num_nodes; const char * const *bus_clocks; size_t num_bus_clocks; + const char * const *intf_clocks; + size_t num_intf_clocks; enum qcom_icc_type type; const struct regmap_config *regmap_cfg; unsigned int qos_offset; diff --git a/drivers/interconnect/qcom/msm8996.c b/drivers/interconnect/qcom/msm8996.c index 21f998cd19f0..9aedfc8de4bf 100644 --- a/drivers/interconnect/qcom/msm8996.c +++ b/drivers/interconnect/qcom/msm8996.c @@ -21,21 +21,17 @@ #include "smd-rpm.h" #include "msm8996.h" -static const char * const bus_mm_clocks[] = { - "bus", - "bus_a", +static const char * const mm_intf_clocks[] = { "iface" }; -static const char * const bus_a0noc_clocks[] = { +static const char * const a0noc_intf_clocks[] = { "aggre0_snoc_axi", "aggre0_cnoc_ahb", "aggre0_noc_mpu_cfg" }; -static const char * const bus_a2noc_clocks[] = { - "bus", - "bus_a", +static const char * const a2noc_intf_clocks[] = { "aggre2_ufs_axi", "ufs_axi" }; @@ -1821,8 +1817,8 @@ static const struct qcom_icc_desc msm8996_a0noc = { .type = QCOM_ICC_NOC, .nodes = a0noc_nodes, .num_nodes = ARRAY_SIZE(a0noc_nodes), - .bus_clocks = bus_a0noc_clocks, - .num_bus_clocks = ARRAY_SIZE(bus_a0noc_clocks), + .intf_clocks = a0noc_intf_clocks, + .num_intf_clocks = ARRAY_SIZE(a0noc_intf_clocks), .regmap_cfg = &msm8996_a0noc_regmap_config }; @@ -1865,8 +1861,8 @@ static const struct qcom_icc_desc msm8996_a2noc = { .type = QCOM_ICC_NOC, .nodes = a2noc_nodes, .num_nodes = ARRAY_SIZE(a2noc_nodes), - .bus_clocks = bus_a2noc_clocks, - .num_bus_clocks = ARRAY_SIZE(bus_a2noc_clocks), + .intf_clocks = a2noc_intf_clocks, + .num_intf_clocks = ARRAY_SIZE(a2noc_intf_clocks), .regmap_cfg = &msm8996_a2noc_regmap_config }; @@ -2004,8 +2000,8 @@ static const struct qcom_icc_desc msm8996_mnoc = { .type = QCOM_ICC_NOC, .nodes = mnoc_nodes, .num_nodes = ARRAY_SIZE(mnoc_nodes), - .bus_clocks = bus_mm_clocks, - .num_bus_clocks = ARRAY_SIZE(bus_mm_clocks), + .intf_clocks = mm_intf_clocks, + .num_intf_clocks = ARRAY_SIZE(mm_intf_clocks), .regmap_cfg = &msm8996_mnoc_regmap_config }; diff --git a/drivers/interconnect/qcom/sdm660.c b/drivers/interconnect/qcom/sdm660.c index a22ba821efbf..0e8a96f4ce90 100644 --- a/drivers/interconnect/qcom/sdm660.c +++ b/drivers/interconnect/qcom/sdm660.c @@ -127,15 +127,11 @@ enum { SDM660_SNOC, }; -static const char * const bus_mm_clocks[] = { - "bus", - "bus_a", +static const char * const mm_intf_clocks[] = { "iface", }; -static const char * const bus_a2noc_clocks[] = { - "bus", - "bus_a", +static const char * const a2noc_intf_clocks[] = { "ipa", "ufs_axi", "aggre2_ufs_axi", @@ -1516,8 +1512,8 @@ static const struct qcom_icc_desc sdm660_a2noc = { .type = QCOM_ICC_NOC, .nodes = sdm660_a2noc_nodes, .num_nodes = ARRAY_SIZE(sdm660_a2noc_nodes), - .bus_clocks = bus_a2noc_clocks, - .num_bus_clocks = ARRAY_SIZE(bus_a2noc_clocks), + .intf_clocks = a2noc_intf_clocks, + .num_intf_clocks = ARRAY_SIZE(a2noc_intf_clocks), .regmap_cfg = &sdm660_a2noc_regmap_config, }; @@ -1659,8 +1655,8 @@ static const struct qcom_icc_desc sdm660_mnoc = { .type = QCOM_ICC_NOC, .nodes = sdm660_mnoc_nodes, .num_nodes = ARRAY_SIZE(sdm660_mnoc_nodes), - .bus_clocks = bus_mm_clocks, - .num_bus_clocks = ARRAY_SIZE(bus_mm_clocks), + .intf_clocks = mm_intf_clocks, + .num_intf_clocks = ARRAY_SIZE(mm_intf_clocks), .regmap_cfg = &sdm660_mnoc_regmap_config, }; From a867cf9b65eadc172bc73e56b13458742d4d050e Mon Sep 17 00:00:00 2001 From: Konrad Dybcio Date: Fri, 7 Apr 2023 22:14:48 +0200 Subject: [PATCH 08/15] interconnect: qcom: icc-rpm: Enforce 2 or 0 bus clocks For SMD RPM bus scaling to work, we need a pair of sleep-wake clocks. The variable number of them we previously supported was only a hack to keep the clocks required for QoS register access, but now that these are separated, we can leave bus_clks to the actual bus clocks. In cases where there is no actual bus scaling (such as A0NoC on MSM8996 and GNoC on SDM660 where the HLOS is only supposed to program the QoS registers and the bus is either static or controlled remotely), allow for no clock scaling with a boolean property. Remove all the code related to allowing an arbitrary number of bus_clks, replace the number by BUS_CLK_MAX (= 2) and guard the bus clock paths to ensure they are not taken on non-scaling buses. Signed-off-by: Konrad Dybcio Link: https://lore.kernel.org/r/20230228-topic-qos-v8-6-ee696a2c15a9@linaro.org Signed-off-by: Georgi Djakov --- drivers/interconnect/qcom/icc-rpm.c | 14 +++----------- drivers/interconnect/qcom/icc-rpm.h | 4 ++-- drivers/interconnect/qcom/msm8996.c | 1 + drivers/interconnect/qcom/sdm660.c | 1 + 4 files changed, 7 insertions(+), 13 deletions(-) diff --git a/drivers/interconnect/qcom/icc-rpm.c b/drivers/interconnect/qcom/icc-rpm.c index c34f75703a66..bebc7928af24 100644 --- a/drivers/interconnect/qcom/icc-rpm.c +++ b/drivers/interconnect/qcom/icc-rpm.c @@ -449,17 +449,9 @@ int qnoc_probe(struct platform_device *pdev) for (i = 0; i < cd_num; i++) qp->intf_clks[i].id = cds[i]; - if (desc->num_bus_clocks) { - cds = desc->bus_clocks; - cd_num = desc->num_bus_clocks; - } else { - cds = bus_clocks; - cd_num = ARRAY_SIZE(bus_clocks); - } - - for (i = 0; i < cd_num; i++) - qp->bus_clks[i].id = cds[i]; - qp->num_bus_clks = cd_num; + qp->num_bus_clks = desc->no_clk_scaling ? 0 : NUM_BUS_CLKS; + for (i = 0; i < qp->num_bus_clks; i++) + qp->bus_clks[i].id = bus_clocks[i]; qp->type = desc->type; qp->qos_offset = desc->qos_offset; diff --git a/drivers/interconnect/qcom/icc-rpm.h b/drivers/interconnect/qcom/icc-rpm.h index b9b63860042f..ee705edf19dd 100644 --- a/drivers/interconnect/qcom/icc-rpm.h +++ b/drivers/interconnect/qcom/icc-rpm.h @@ -25,7 +25,7 @@ enum qcom_icc_type { /** * struct qcom_icc_provider - Qualcomm specific interconnect provider * @provider: generic interconnect provider - * @num_bus_clks: the total number of bus_clks clk_bulk_data entries + * @num_bus_clks: the total number of bus_clks clk_bulk_data entries (0 or 2) * @num_intf_clks: the total number of intf_clks clk_bulk_data entries * @type: the ICC provider type * @regmap: regmap for QoS registers read/write access @@ -100,9 +100,9 @@ struct qcom_icc_desc { struct qcom_icc_node * const *nodes; size_t num_nodes; const char * const *bus_clocks; - size_t num_bus_clocks; const char * const *intf_clocks; size_t num_intf_clocks; + bool no_clk_scaling; enum qcom_icc_type type; const struct regmap_config *regmap_cfg; unsigned int qos_offset; diff --git a/drivers/interconnect/qcom/msm8996.c b/drivers/interconnect/qcom/msm8996.c index 9aedfc8de4bf..dc9959a87df2 100644 --- a/drivers/interconnect/qcom/msm8996.c +++ b/drivers/interconnect/qcom/msm8996.c @@ -1819,6 +1819,7 @@ static const struct qcom_icc_desc msm8996_a0noc = { .num_nodes = ARRAY_SIZE(a0noc_nodes), .intf_clocks = a0noc_intf_clocks, .num_intf_clocks = ARRAY_SIZE(a0noc_intf_clocks), + .no_clk_scaling = true, .regmap_cfg = &msm8996_a0noc_regmap_config }; diff --git a/drivers/interconnect/qcom/sdm660.c b/drivers/interconnect/qcom/sdm660.c index 0e8a96f4ce90..7ffaf70d62d3 100644 --- a/drivers/interconnect/qcom/sdm660.c +++ b/drivers/interconnect/qcom/sdm660.c @@ -1616,6 +1616,7 @@ static const struct qcom_icc_desc sdm660_gnoc = { .nodes = sdm660_gnoc_nodes, .num_nodes = ARRAY_SIZE(sdm660_gnoc_nodes), .regmap_cfg = &sdm660_gnoc_regmap_config, + .no_clk_scaling = true, }; static struct qcom_icc_node * const sdm660_mnoc_nodes[] = { From 1ff7aedcdcdd4fe02201269ab428b09491e5cf6e Mon Sep 17 00:00:00 2001 From: Konrad Dybcio Date: Fri, 7 Apr 2023 22:14:49 +0200 Subject: [PATCH 09/15] interconnect: qcom: rpm: Don't use clk_get_optional for bus clocks anymore Commit dd42ec8ea5b9 ("interconnect: qcom: rpm: Use _optional func for provider clocks") relaxed the requirements around probing bus clocks. This was a decent solution for making sure MSM8996 would still boot with old DTs, but now that there's a proper fix in place that both old and new DTs will be happy about, revert back to the safer variant of the function. Fixes: dd42ec8ea5b9 ("interconnect: qcom: rpm: Use _optional func for provider clocks") Signed-off-by: Konrad Dybcio Link: https://lore.kernel.org/r/20230228-topic-qos-v8-7-ee696a2c15a9@linaro.org Signed-off-by: Georgi Djakov --- drivers/interconnect/qcom/icc-rpm.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/interconnect/qcom/icc-rpm.c b/drivers/interconnect/qcom/icc-rpm.c index bebc7928af24..f4627c4a1bdd 100644 --- a/drivers/interconnect/qcom/icc-rpm.c +++ b/drivers/interconnect/qcom/icc-rpm.c @@ -481,7 +481,7 @@ int qnoc_probe(struct platform_device *pdev) } regmap_done: - ret = devm_clk_bulk_get_optional(dev, qp->num_bus_clks, qp->bus_clks); + ret = devm_clk_bulk_get(dev, qp->num_bus_clks, qp->bus_clks); if (ret) return ret; From 130733a10079102a78b51bf19bf4e4fa4d119c67 Mon Sep 17 00:00:00 2001 From: Konrad Dybcio Date: Fri, 7 Apr 2023 22:14:50 +0200 Subject: [PATCH 10/15] interconnect: qcom: msm8996: Promote to core_initcall The interconnect driver is (or soon will be) vital to many other devices, as it's not a given that the bootloader will set up enough bandwidth for us or that the values we come into are reasonable. Promote the driver to core_initcall to ensure the consumers (i.e. most "meaningful" parts of the SoC) can probe without deferrals. Signed-off-by: Konrad Dybcio Link: https://lore.kernel.org/r/20230228-topic-qos-v8-8-ee696a2c15a9@linaro.org Signed-off-by: Georgi Djakov --- drivers/interconnect/qcom/msm8996.c | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/drivers/interconnect/qcom/msm8996.c b/drivers/interconnect/qcom/msm8996.c index dc9959a87df2..20340fb62fe6 100644 --- a/drivers/interconnect/qcom/msm8996.c +++ b/drivers/interconnect/qcom/msm8996.c @@ -2108,7 +2108,17 @@ static struct platform_driver qnoc_driver = { .sync_state = icc_sync_state, } }; -module_platform_driver(qnoc_driver); +static int __init qnoc_driver_init(void) +{ + return platform_driver_register(&qnoc_driver); +} +core_initcall(qnoc_driver_init); + +static void __exit qnoc_driver_exit(void) +{ + platform_driver_unregister(&qnoc_driver); +} +module_exit(qnoc_driver_exit); MODULE_AUTHOR("Yassine Oudjana "); MODULE_DESCRIPTION("Qualcomm MSM8996 NoC driver"); From 0ebee0a6f73e7169fb2ee59587aad2880438485a Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Tue, 23 May 2023 13:27:28 +0300 Subject: [PATCH 11/15] interconnect: qcom: rpm: allocate enough data in probe() This was not allocating enough bytes. There are two issue here. First, there was a typo where it was taking the size of the pointer instead of the size of the struct, "sizeof(qp->intf_clks)" vs "sizeof(*qp->intf_clks)". Second, it's an array of "cd_num" clocks so we need to allocate space for more than one element. Fixes: 2e2113c8a64f ("interconnect: qcom: rpm: Handle interface clocks") Signed-off-by: Dan Carpenter Reviewed-by: Konrad Dybcio Link: https://lore.kernel.org/r/e0fa275c-ae63-4342-9c9e-0ffaf6314da1@kili.mountain Signed-off-by: Georgi Djakov --- drivers/interconnect/qcom/icc-rpm.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/interconnect/qcom/icc-rpm.c b/drivers/interconnect/qcom/icc-rpm.c index f4627c4a1bdd..6acc7686ed38 100644 --- a/drivers/interconnect/qcom/icc-rpm.c +++ b/drivers/interconnect/qcom/icc-rpm.c @@ -436,7 +436,7 @@ int qnoc_probe(struct platform_device *pdev) if (!qp) return -ENOMEM; - qp->intf_clks = devm_kzalloc(dev, sizeof(qp->intf_clks), GFP_KERNEL); + qp->intf_clks = devm_kcalloc(dev, cd_num, sizeof(*qp->intf_clks), GFP_KERNEL); if (!qp->intf_clks) return -ENOMEM; From 7dcdad6f32c96af6e6fb2afe83ec4028dbe1da44 Mon Sep 17 00:00:00 2001 From: Johan Hovold Date: Tue, 23 May 2023 11:52:48 +0200 Subject: [PATCH 12/15] interconnect: drop unused icc_get() interface The icc_get() interface can be used to lookup an interconnect path based on global node ids. There has never been any users of this interface and all lookups are currently done from the devicetree. Remove the unused icc_get() interface. Reviewed-by: Konrad Dybcio Signed-off-by: Johan Hovold Reviewed-by: Bjorn Andersson Link: https://lore.kernel.org/r/20230523095248.25211-1-johan+linaro@kernel.org Signed-off-by: Georgi Djakov --- drivers/interconnect/core.c | 52 ++---------------------------------- include/linux/interconnect.h | 8 ------ 2 files changed, 2 insertions(+), 58 deletions(-) diff --git a/drivers/interconnect/core.c b/drivers/interconnect/core.c index ec46bcb16d5e..5fac448c28fd 100644 --- a/drivers/interconnect/core.c +++ b/drivers/interconnect/core.c @@ -587,7 +587,7 @@ EXPORT_SYMBOL_GPL(icc_set_tag); /** * icc_get_name() - Get name of the icc path - * @path: reference to the path returned by icc_get() + * @path: interconnect path * * This function is used by an interconnect consumer to get the name of the icc * path. @@ -605,7 +605,7 @@ EXPORT_SYMBOL_GPL(icc_get_name); /** * icc_set_bw() - set bandwidth constraints on an interconnect path - * @path: reference to the path returned by icc_get() + * @path: interconnect path * @avg_bw: average bandwidth in kilobytes per second * @peak_bw: peak bandwidth in kilobytes per second * @@ -704,54 +704,6 @@ int icc_disable(struct icc_path *path) } EXPORT_SYMBOL_GPL(icc_disable); -/** - * icc_get() - return a handle for path between two endpoints - * @dev: the device requesting the path - * @src_id: source device port id - * @dst_id: destination device port id - * - * This function will search for a path between two endpoints and return an - * icc_path handle on success. Use icc_put() to release - * constraints when they are not needed anymore. - * If the interconnect API is disabled, NULL is returned and the consumer - * drivers will still build. Drivers are free to handle this specifically, - * but they don't have to. - * - * Return: icc_path pointer on success, ERR_PTR() on error or NULL if the - * interconnect API is disabled. - */ -struct icc_path *icc_get(struct device *dev, const int src_id, const int dst_id) -{ - struct icc_node *src, *dst; - struct icc_path *path = ERR_PTR(-EPROBE_DEFER); - - mutex_lock(&icc_lock); - - src = node_find(src_id); - if (!src) - goto out; - - dst = node_find(dst_id); - if (!dst) - goto out; - - path = path_find(dev, src, dst); - if (IS_ERR(path)) { - dev_err(dev, "%s: invalid path=%ld\n", __func__, PTR_ERR(path)); - goto out; - } - - path->name = kasprintf(GFP_KERNEL, "%s-%s", src->name, dst->name); - if (!path->name) { - kfree(path); - path = ERR_PTR(-ENOMEM); - } -out: - mutex_unlock(&icc_lock); - return path; -} -EXPORT_SYMBOL_GPL(icc_get); - /** * icc_put() - release the reference to the icc_path * @path: interconnect path diff --git a/include/linux/interconnect.h b/include/linux/interconnect.h index 2b0e784ba771..97ac253df62c 100644 --- a/include/linux/interconnect.h +++ b/include/linux/interconnect.h @@ -40,8 +40,6 @@ struct icc_bulk_data { #if IS_ENABLED(CONFIG_INTERCONNECT) -struct icc_path *icc_get(struct device *dev, const int src_id, - const int dst_id); struct icc_path *of_icc_get(struct device *dev, const char *name); struct icc_path *devm_of_icc_get(struct device *dev, const char *name); int devm_of_icc_bulk_get(struct device *dev, int num_paths, struct icc_bulk_data *paths); @@ -61,12 +59,6 @@ void icc_bulk_disable(int num_paths, const struct icc_bulk_data *paths); #else -static inline struct icc_path *icc_get(struct device *dev, const int src_id, - const int dst_id) -{ - return NULL; -} - static inline struct icc_path *of_icc_get(struct device *dev, const char *name) { From 12dc71953e664f084918d3e1b63b211b0e6f8e98 Mon Sep 17 00:00:00 2001 From: Dmitry Baryshkov Date: Fri, 12 May 2023 03:13:33 +0300 Subject: [PATCH 13/15] clk: qcom: cbf-msm8996: scale CBF clock according to the CPUfreq Turn CBF into the interconnect provider. Scale CBF frequency (bandwidth) according to CPU frequencies. Reviewed-by: Konrad Dybcio Tested-by: Yassine Oudjana Signed-off-by: Dmitry Baryshkov Acked-by: Bjorn Andersson Link: https://lore.kernel.org/r/20230512001334.2983048-4-dmitry.baryshkov@linaro.org Signed-off-by: Georgi Djakov --- drivers/clk/qcom/Kconfig | 1 + drivers/clk/qcom/clk-cbf-8996.c | 60 ++++++++++++++++++++++++++++++++- 2 files changed, 60 insertions(+), 1 deletion(-) diff --git a/drivers/clk/qcom/Kconfig b/drivers/clk/qcom/Kconfig index 12be3e2371b3..85869e7a9f16 100644 --- a/drivers/clk/qcom/Kconfig +++ b/drivers/clk/qcom/Kconfig @@ -48,6 +48,7 @@ config QCOM_CLK_APCS_MSM8916 config QCOM_CLK_APCC_MSM8996 tristate "MSM8996 CPU Clock Controller" select QCOM_KRYO_L2_ACCESSORS + select INTERCONNECT_CLK if INTERCONNECT depends on ARM64 help Support for the CPU clock controller on msm8996 devices. diff --git a/drivers/clk/qcom/clk-cbf-8996.c b/drivers/clk/qcom/clk-cbf-8996.c index cfd567636f4e..1e23b734abb3 100644 --- a/drivers/clk/qcom/clk-cbf-8996.c +++ b/drivers/clk/qcom/clk-cbf-8996.c @@ -5,11 +5,15 @@ #include #include #include +#include +#include #include #include #include #include +#include + #include "clk-alpha-pll.h" #include "clk-regmap.h" @@ -223,6 +227,49 @@ static const struct regmap_config cbf_msm8996_regmap_config = { .val_format_endian = REGMAP_ENDIAN_LITTLE, }; +#ifdef CONFIG_INTERCONNECT + +/* Random ID that doesn't clash with main qnoc and OSM */ +#define CBF_MASTER_NODE 2000 + +static int qcom_msm8996_cbf_icc_register(struct platform_device *pdev, struct clk_hw *cbf_hw) +{ + struct device *dev = &pdev->dev; + struct clk *clk = devm_clk_hw_get_clk(dev, cbf_hw, "cbf"); + const struct icc_clk_data data[] = { + { .clk = clk, .name = "cbf", }, + }; + struct icc_provider *provider; + + provider = icc_clk_register(dev, CBF_MASTER_NODE, ARRAY_SIZE(data), data); + if (IS_ERR(provider)) + return PTR_ERR(provider); + + platform_set_drvdata(pdev, provider); + + return 0; +} + +static int qcom_msm8996_cbf_icc_remove(struct platform_device *pdev) +{ + struct icc_provider *provider = platform_get_drvdata(pdev); + + icc_clk_unregister(provider); + + return 0; +} +#define qcom_msm8996_cbf_icc_sync_state icc_sync_state +#else +static int qcom_msm8996_cbf_icc_register(struct platform_device *pdev, struct clk_hw *cbf_hw) +{ + dev_warn(&pdev->dev, "CONFIG_INTERCONNECT is disabled, CBF clock is fixed\n"); + + return 0; +} +#define qcom_msm8996_cbf_icc_remove(pdev) (0) +#define qcom_msm8996_cbf_icc_sync_state NULL +#endif + static int qcom_msm8996_cbf_probe(struct platform_device *pdev) { void __iomem *base; @@ -281,7 +328,16 @@ static int qcom_msm8996_cbf_probe(struct platform_device *pdev) if (ret) return ret; - return devm_of_clk_add_hw_provider(dev, of_clk_hw_simple_get, &cbf_mux.clkr.hw); + ret = devm_of_clk_add_hw_provider(dev, of_clk_hw_simple_get, &cbf_mux.clkr.hw); + if (ret) + return ret; + + return qcom_msm8996_cbf_icc_register(pdev, &cbf_mux.clkr.hw); +} + +static int qcom_msm8996_cbf_remove(struct platform_device *pdev) +{ + return qcom_msm8996_cbf_icc_remove(pdev); } static const struct of_device_id qcom_msm8996_cbf_match_table[] = { @@ -292,9 +348,11 @@ MODULE_DEVICE_TABLE(of, qcom_msm8996_cbf_match_table); static struct platform_driver qcom_msm8996_cbf_driver = { .probe = qcom_msm8996_cbf_probe, + .remove = qcom_msm8996_cbf_remove, .driver = { .name = "qcom-msm8996-cbf", .of_match_table = qcom_msm8996_cbf_match_table, + .sync_state = qcom_msm8996_cbf_icc_sync_state, }, }; From a1f7e72178475e0e0d90cd89ef7457b6c69c50ec Mon Sep 17 00:00:00 2001 From: Dmitry Baryshkov Date: Sat, 20 May 2023 02:01:22 +0300 Subject: [PATCH 14/15] interconnect: icc-clk: fix modular build Fix building interconnect-clk as a module: - Add EXPORT_SYMBOL_GPL to the exported functions - Add MODULE_DESCRIPTION/_LICENSE/_AUTHOR Reported-by: Nick Desaulniers Signed-off-by: Dmitry Baryshkov Link: https://lore.kernel.org/r/20230519230122.3958816-1-dmitry.baryshkov@linaro.org Signed-off-by: Georgi Djakov --- drivers/interconnect/icc-clk.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/interconnect/icc-clk.c b/drivers/interconnect/icc-clk.c index 0db3b654548b..4d43ebff4257 100644 --- a/drivers/interconnect/icc-clk.c +++ b/drivers/interconnect/icc-clk.c @@ -146,6 +146,7 @@ struct icc_provider *icc_clk_register(struct device *dev, return ERR_PTR(ret); } +EXPORT_SYMBOL_GPL(icc_clk_register); /** * icc_clk_unregister() - unregister a previously registered clk interconnect provider @@ -166,3 +167,8 @@ void icc_clk_unregister(struct icc_provider *provider) clk_disable_unprepare(qn->clk); } } +EXPORT_SYMBOL_GPL(icc_clk_unregister); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Interconnect wrapper for clocks"); +MODULE_AUTHOR("Dmitry Baryshkov "); From 4396f5fc6d032388012a4772278975210398f525 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Fri, 9 Jun 2023 16:07:32 +0200 Subject: [PATCH 15/15] dt-bindings: interconnect: fsl,imx8m-noc: drop unneeded quotes Cleanup bindings dropping unneeded quotes. Once all these are fixed, checking for this can be enabled in yamllint. Signed-off-by: Krzysztof Kozlowski Link: https://lore.kernel.org/r/20230609140732.64828-1-krzysztof.kozlowski@linaro.org Signed-off-by: Georgi Djakov --- .../devicetree/bindings/interconnect/fsl,imx8m-noc.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Documentation/devicetree/bindings/interconnect/fsl,imx8m-noc.yaml b/Documentation/devicetree/bindings/interconnect/fsl,imx8m-noc.yaml index f7a5e31c506e..fc21fe3e7b37 100644 --- a/Documentation/devicetree/bindings/interconnect/fsl,imx8m-noc.yaml +++ b/Documentation/devicetree/bindings/interconnect/fsl,imx8m-noc.yaml @@ -51,7 +51,7 @@ properties: type: object fsl,ddrc: - $ref: "/schemas/types.yaml#/definitions/phandle" + $ref: /schemas/types.yaml#/definitions/phandle description: Phandle to DDR Controller.