summaryrefslogtreecommitdiff
path: root/drivers/interconnect/qcom/icc-rpmh.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/interconnect/qcom/icc-rpmh.c')
-rw-r--r--drivers/interconnect/qcom/icc-rpmh.c94
1 files changed, 94 insertions, 0 deletions
diff --git a/drivers/interconnect/qcom/icc-rpmh.c b/drivers/interconnect/qcom/icc-rpmh.c
index c1aa265c1f4e..f49a8e0cb03c 100644
--- a/drivers/interconnect/qcom/icc-rpmh.c
+++ b/drivers/interconnect/qcom/icc-rpmh.c
@@ -1,8 +1,11 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (c) 2020, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2024 Qualcomm Innovation Center, Inc. All rights reserved.
*/
+#include <linux/bitfield.h>
+#include <linux/clk.h>
#include <linux/interconnect.h>
#include <linux/interconnect-provider.h>
#include <linux/module.h>
@@ -14,6 +17,38 @@
#include "icc-common.h"
#include "icc-rpmh.h"
+/* QNOC QoS */
+#define QOSGEN_MAINCTL_LO(p, qp) (0x8 + (p->port_offsets[qp]))
+#define QOS_SLV_URG_MSG_EN_MASK GENMASK(3, 3)
+#define QOS_DFLT_PRIO_MASK GENMASK(6, 4)
+#define QOS_DISABLE_MASK GENMASK(24, 24)
+
+/**
+ * qcom_icc_set_qos - initialize static QoS configurations
+ * @qp: qcom icc provider to which @node belongs
+ * @node: qcom icc node to operate on
+ */
+static void qcom_icc_set_qos(struct qcom_icc_provider *qp,
+ struct qcom_icc_node *node)
+{
+ const struct qcom_icc_qosbox *qos = node->qosbox;
+ int port;
+
+ for (port = 0; port < qos->num_ports; port++) {
+ regmap_update_bits(qp->regmap, QOSGEN_MAINCTL_LO(qos, port),
+ QOS_DISABLE_MASK,
+ FIELD_PREP(QOS_DISABLE_MASK, qos->prio_fwd_disable));
+
+ regmap_update_bits(qp->regmap, QOSGEN_MAINCTL_LO(qos, port),
+ QOS_DFLT_PRIO_MASK,
+ FIELD_PREP(QOS_DFLT_PRIO_MASK, qos->prio));
+
+ regmap_update_bits(qp->regmap, QOSGEN_MAINCTL_LO(qos, port),
+ QOS_SLV_URG_MSG_EN_MASK,
+ FIELD_PREP(QOS_SLV_URG_MSG_EN_MASK, qos->urg_fwd));
+ }
+}
+
/**
* qcom_icc_pre_aggregate - cleans up stale values from prior icc_set
* @node: icc node to operate on
@@ -159,6 +194,36 @@ int qcom_icc_bcm_init(struct qcom_icc_bcm *bcm, struct device *dev)
}
EXPORT_SYMBOL_GPL(qcom_icc_bcm_init);
+/**
+ * qcom_icc_rpmh_configure_qos - configure QoS parameters
+ * @qp: qcom icc provider associated with QoS endpoint nodes
+ *
+ * Return: 0 on success, or an error code otherwise
+ */
+static int qcom_icc_rpmh_configure_qos(struct qcom_icc_provider *qp)
+{
+ struct qcom_icc_node *qnode;
+ size_t i;
+ int ret;
+
+ ret = clk_bulk_prepare_enable(qp->num_clks, qp->clks);
+ if (ret)
+ return ret;
+
+ for (i = 0; i < qp->num_nodes; i++) {
+ qnode = qp->nodes[i];
+ if (!qnode)
+ continue;
+
+ if (qnode->qosbox)
+ qcom_icc_set_qos(qp, qnode);
+ }
+
+ clk_bulk_disable_unprepare(qp->num_clks, qp->clks);
+
+ return ret;
+}
+
int qcom_icc_rpmh_probe(struct platform_device *pdev)
{
const struct qcom_icc_desc *desc;
@@ -199,7 +264,9 @@ int qcom_icc_rpmh_probe(struct platform_device *pdev)
qp->dev = dev;
qp->bcms = desc->bcms;
+ qp->nodes = desc->nodes;
qp->num_bcms = desc->num_bcms;
+ qp->num_nodes = desc->num_nodes;
qp->voter = of_bcm_voter_get(qp->dev, NULL);
if (IS_ERR(qp->voter))
@@ -229,6 +296,32 @@ int qcom_icc_rpmh_probe(struct platform_device *pdev)
data->nodes[i] = node;
}
+ if (desc->config) {
+ struct resource *res;
+ void __iomem *base;
+
+ base = devm_platform_get_and_ioremap_resource(pdev, 0, &res);
+ if (IS_ERR(base))
+ goto skip_qos_config;
+
+ qp->regmap = devm_regmap_init_mmio(dev, base, desc->config);
+ if (IS_ERR(qp->regmap)) {
+ dev_info(dev, "Skipping QoS, regmap failed; %ld\n", PTR_ERR(qp->regmap));
+ goto skip_qos_config;
+ }
+
+ qp->num_clks = devm_clk_bulk_get_all(qp->dev, &qp->clks);
+ if (qp->num_clks < 0 || (!qp->num_clks && desc->qos_clks_required)) {
+ dev_info(dev, "Skipping QoS, failed to get clk: %d\n", qp->num_clks);
+ goto skip_qos_config;
+ }
+
+ ret = qcom_icc_rpmh_configure_qos(qp);
+ if (ret)
+ dev_info(dev, "Failed to program QoS: %d\n", ret);
+ }
+
+skip_qos_config:
ret = icc_provider_register(provider);
if (ret)
goto err_remove_nodes;
@@ -262,4 +355,5 @@ void qcom_icc_rpmh_remove(struct platform_device *pdev)
}
EXPORT_SYMBOL_GPL(qcom_icc_rpmh_remove);
+MODULE_DESCRIPTION("Qualcomm RPMh interconnect driver");
MODULE_LICENSE("GPL v2");