summaryrefslogtreecommitdiff
path: root/drivers
diff options
context:
space:
mode:
authorTom Rini <[email protected]>2025-11-20 08:00:11 -0600
committerTom Rini <[email protected]>2025-11-20 08:00:11 -0600
commit8ff90aa64b730c3f511921d4ff79e9f64e625867 (patch)
treee43ed96f7840d878bd9da89f3afdbd82eda2f5d9 /drivers
parentb8872deb4450b09586e28550c23d33a71084d94f (diff)
parentfb3db4aa8df2ae5093a5c1580b9cd5789c248de4 (diff)
Merge tag 'interconnect-next-20251120' of https://source.denx.de/u-boot/custodians/u-boot-snapdragon into next
- Qualcomm RPMh cmd_db_read_slave_id() & cmd_db_read_aux_data() - Initial Interconnect implementation + Qualcomm RPMh support
Diffstat (limited to 'drivers')
-rw-r--r--drivers/Kconfig2
-rw-r--r--drivers/Makefile1
-rw-r--r--drivers/interconnect/Kconfig22
-rw-r--r--drivers/interconnect/Makefile8
-rw-r--r--drivers/interconnect/interconnect-uclass.c545
-rw-r--r--drivers/interconnect/qcom/Kconfig12
-rw-r--r--drivers/interconnect/qcom/Makefile7
-rw-r--r--drivers/interconnect/qcom/bcm-voter.c340
-rw-r--r--drivers/interconnect/qcom/bcm-voter.h19
-rw-r--r--drivers/interconnect/qcom/icc-rpmh.c224
-rw-r--r--drivers/interconnect/qcom/icc-rpmh.h130
-rw-r--r--drivers/interconnect/qcom/sm8650.c1665
-rw-r--r--drivers/interconnect/qcom/sm8650.h144
-rw-r--r--drivers/interconnect/sandbox-interconnect-test.c89
-rw-r--r--drivers/interconnect/sandbox-interconnect.c303
-rw-r--r--drivers/soc/qcom/cmd-db.c45
-rw-r--r--drivers/ufs/ufs-qcom.c13
17 files changed, 3569 insertions, 0 deletions
diff --git a/drivers/Kconfig b/drivers/Kconfig
index fe35e07b124..47606ddc6c8 100644
--- a/drivers/Kconfig
+++ b/drivers/Kconfig
@@ -60,6 +60,8 @@ source "drivers/i3c/Kconfig"
source "drivers/input/Kconfig"
+source "drivers/interconnect/Kconfig"
+
source "drivers/iommu/Kconfig"
source "drivers/led/Kconfig"
diff --git a/drivers/Makefile b/drivers/Makefile
index 77fc66eb8ba..de993ae42ac 100644
--- a/drivers/Makefile
+++ b/drivers/Makefile
@@ -19,6 +19,7 @@ obj-$(CONFIG_$(PHASE_)FIRMWARE) +=firmware/
obj-$(CONFIG_$(PHASE_)I2C) += i2c/
obj-$(CONFIG_$(PHASE_)I3C) += i3c/
obj-$(CONFIG_$(PHASE_)INPUT) += input/
+obj-$(CONFIG_$(PHASE_)INTERCONNECT) += interconnect/
obj-$(CONFIG_$(PHASE_)LED) += led/
obj-$(CONFIG_$(PHASE_)MMC) += mmc/
obj-y += mtd/
diff --git a/drivers/interconnect/Kconfig b/drivers/interconnect/Kconfig
new file mode 100644
index 00000000000..de868b9ad38
--- /dev/null
+++ b/drivers/interconnect/Kconfig
@@ -0,0 +1,22 @@
+menu "Interconnect Support"
+
+config INTERCONNECT
+ bool "Enable interconnect support using Driver Model"
+ depends on DM && OF_CONTROL
+ help
+ Enable support for the interconnect driver class. Many SoCs allow
+ bandwidth to be tuned on busses within the SoC.
+
+if INTERCONNECT
+
+config INTERCONNECT_SANDBOX
+ bool "Enable interconnect sandbox driver"
+ depends on SANDBOX
+ help
+ Enable support for the interconnect sandbox drivers.
+
+source "drivers/interconnect/qcom/Kconfig"
+
+endif
+
+endmenu
diff --git a/drivers/interconnect/Makefile b/drivers/interconnect/Makefile
new file mode 100644
index 00000000000..c9e8077b0f1
--- /dev/null
+++ b/drivers/interconnect/Makefile
@@ -0,0 +1,8 @@
+# SPDX-License-Identifier: GPL-2.0
+#
+# Copyright (c) 2025 Linaro Limited
+#
+
+obj-$(CONFIG_$(PHASE_)INTERCONNECT) += interconnect-uclass.o
+obj-$(CONFIG_$(PHASE_)INTERCONNECT_SANDBOX) += sandbox-interconnect.o sandbox-interconnect-test.o
+obj-y += qcom/
diff --git a/drivers/interconnect/interconnect-uclass.c b/drivers/interconnect/interconnect-uclass.c
new file mode 100644
index 00000000000..c4faaa5ab09
--- /dev/null
+++ b/drivers/interconnect/interconnect-uclass.c
@@ -0,0 +1,545 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2025 Linaro Limited
+ * Based on the Linux Driver:
+ * Copyright (c) 2017-2019, Linaro Ltd.
+ * Author: Georgi Djakov <[email protected]>
+ */
+
+#define LOG_CATEGORY UCLASS_INTERCONNECT
+
+#include <dm.h>
+#include <log.h>
+#include <malloc.h>
+#include <linux/err.h>
+#include <interconnect.h>
+#include <interconnect-uclass.h>
+#include <dm/lists.h>
+#include <dm/uclass-internal.h>
+#include <dm/device-internal.h>
+#include <dm/device_compat.h>
+
+static struct icc_node *of_icc_get_from_provider(struct udevice *dev,
+ const struct ofnode_phandle_args *args);
+static struct icc_path *icc_path_find(struct udevice *dev,
+ struct icc_node *src, struct icc_node *dst);
+static struct icc_node *icc_node_find(const ulong id);
+
+/* Public API */
+
+struct icc_path *of_icc_get(struct udevice *dev, const char *name)
+{
+ int index = 0;
+
+ if (!dev)
+ return ERR_PTR(-ENODEV);
+
+ if (!ofnode_has_property(dev_ofnode(dev), "interconnects"))
+ return NULL;
+
+ if (name) {
+ index = dev_read_stringlist_search(dev, "interconnect-names", name);
+ if (index < 0) {
+ debug("fdt_stringlist_search() failed: %d\n", index);
+ return ERR_PTR(index);
+ }
+ }
+
+ return of_icc_get_by_index(dev, index);
+}
+
+struct icc_path *of_icc_get_by_index(struct udevice *dev, int index)
+{
+ struct ofnode_phandle_args src_args, dst_args;
+ struct icc_node *src_node, *dst_node;
+ struct icc_path *path;
+ int ret;
+
+ if (!dev)
+ return ERR_PTR(-ENODEV);
+
+ debug("(dev=%p,idx=%d)\n", dev, index);
+
+ if (!ofnode_has_property(dev_ofnode(dev), "interconnects"))
+ return NULL;
+
+ ret = dev_read_phandle_with_args(dev, "interconnects",
+ "#interconnect-cells", 0, index * 2,
+ &src_args);
+ if (ret) {
+ dev_err(dev, "dev_read_phandle_with_args src failed: %d\n", ret);
+ return ERR_PTR(ret);
+ }
+
+ ret = dev_read_phandle_with_args(dev, "interconnects",
+ "#interconnect-cells", 0, index * 2 + 1,
+ &dst_args);
+ if (ret) {
+ dev_err(dev, "dev_read_phandle_with_args dst failed: %d\n", ret);
+ return ERR_PTR(ret);
+ }
+
+ src_node = of_icc_get_from_provider(dev, &src_args);
+ if (IS_ERR(src_node)) {
+ dev_err(dev, "error finding src node\n");
+ return ERR_CAST(src_node);
+ }
+
+ dst_node = of_icc_get_from_provider(dev, &dst_args);
+ if (IS_ERR(dst_node)) {
+ dev_err(dev, "error finding dst node\n");
+ return ERR_CAST(dst_node);
+ }
+
+ path = icc_path_find(dev, src_node, dst_node);
+ if (IS_ERR(path))
+ dev_err(dev, "invalid path=%ld\n", PTR_ERR(path));
+
+ debug("(path=%p)\n", path);
+
+ return path;
+}
+
+int icc_put(struct icc_path *path)
+{
+ struct icc_node *node;
+ size_t i;
+ int ret;
+
+ debug("(path=%p)\n", path);
+
+ if (!path || IS_ERR(path))
+ return 0;
+
+ ret = icc_set_bw(path, 0, 0);
+ if (ret) {
+ dev_err(path->dev, "failed to set bandwidth (%d)\n", ret);
+ return ret;
+ }
+
+ for (i = 0; i < path->num_nodes; i++) {
+ node = path->reqs[i].node;
+
+ if (node->users)
+ node->users--;
+ if (!node->users)
+ device_remove(node->dev, DM_REMOVE_NORMAL);
+
+ hlist_del(&path->reqs[i].req_node);
+ }
+
+ kfree(path);
+
+ return 0;
+}
+
+static int __icc_enable(struct icc_path *path, bool enable)
+{
+ int i;
+
+ if (!path)
+ return 0;
+
+ if (IS_ERR(path) || !path->num_nodes)
+ return -EINVAL;
+
+ for (i = 0; i < path->num_nodes; i++)
+ path->reqs[i].enabled = enable;
+
+ return icc_set_bw(path, path->reqs[0].avg_bw,
+ path->reqs[0].peak_bw);
+}
+
+int icc_enable(struct icc_path *path)
+{
+ debug("(path=%p)\n", path);
+ return __icc_enable(path, true);
+}
+
+int icc_disable(struct icc_path *path)
+{
+ debug("(path=%p)\n", path);
+ return __icc_enable(path, false);
+}
+
+static int apply_constraints(struct icc_path *path)
+{
+ struct icc_node *next, *prev = NULL;
+ const struct interconnect_ops *ops;
+ struct icc_provider *provider;
+ struct udevice *p;
+ int ret = -EINVAL;
+ int i;
+
+ debug("(path=%p)\n", path);
+
+ for (i = 0; i < path->num_nodes; i++) {
+ next = path->reqs[i].node;
+ p = next->dev->parent;
+ provider = dev_get_uclass_plat(p);
+
+ /* both endpoints should be valid master-slave pairs */
+ if (!prev || (p != prev->dev->parent && !provider->inter_set)) {
+ prev = next;
+ continue;
+ }
+
+ debug("(path=%p,req=%d,node=%s,provider=%s)\n",
+ path, i, next->dev->name, p->name);
+
+ ops = device_get_ops(p);
+
+ /* set the constraints */
+ if (ops->set) {
+ ret = ops->set(prev, next);
+ if (ret)
+ goto out;
+ }
+
+ prev = next;
+ }
+out:
+ return ret;
+}
+
+/*
+ * We want the path to honor all bandwidth requests, so the average and peak
+ * bandwidth requirements from each consumer are aggregated at each node.
+ * The aggregation is platform specific, so each platform can customize it by
+ * implementing its own aggregate() function.
+ */
+
+static int aggregate_requests(struct icc_node *node)
+{
+ const struct interconnect_ops *ops = device_get_ops(node->dev->parent);
+ struct icc_req *r;
+ u32 avg_bw, peak_bw;
+
+ debug("(dev=%s)\n", node->dev->name);
+
+ node->avg_bw = 0;
+ node->peak_bw = 0;
+
+ if (ops->pre_aggregate)
+ ops->pre_aggregate(node);
+
+ hlist_for_each_entry(r, &node->req_list, req_node) {
+ if (r->enabled) {
+ avg_bw = r->avg_bw;
+ peak_bw = r->peak_bw;
+ } else {
+ avg_bw = 0;
+ peak_bw = 0;
+ }
+ debug("(dev=%s,req=%s,avg=%d,peak=%d)\n",
+ node->dev->name, r->node->dev->name,
+ avg_bw, peak_bw);
+ if (ops->aggregate)
+ ops->aggregate(node, r->tag, avg_bw, peak_bw,
+ &node->avg_bw, &node->peak_bw);
+ }
+
+ return 0;
+}
+
+int icc_set_bw(struct icc_path *path, u32 avg_bw, u32 peak_bw)
+{
+ struct icc_node *node;
+ u32 old_avg, old_peak;
+ size_t i;
+ int ret;
+
+ debug("(path=%p,avg=%d,peak=%d)\n", path, avg_bw, peak_bw);
+
+ if (!path)
+ return 0;
+
+ if (IS_ERR(path) || !path->num_nodes)
+ return -EINVAL;
+
+ old_avg = path->reqs[0].avg_bw;
+ old_peak = path->reqs[0].peak_bw;
+
+ for (i = 0; i < path->num_nodes; i++) {
+ node = path->reqs[i].node;
+
+ /* update the consumer request for this path */
+ path->reqs[i].avg_bw = avg_bw;
+ path->reqs[i].peak_bw = peak_bw;
+
+ /* aggregate requests for this node */
+ aggregate_requests(node);
+ }
+
+ ret = apply_constraints(path);
+ if (ret) {
+ dev_err(path->dev, "error applying constraints (%d)\n", ret);
+
+ for (i = 0; i < path->num_nodes; i++) {
+ node = path->reqs[i].node;
+ path->reqs[i].avg_bw = old_avg;
+ path->reqs[i].peak_bw = old_peak;
+ aggregate_requests(node);
+ }
+ apply_constraints(path);
+ }
+
+ return ret;
+}
+
+/* Provider API */
+
+static struct icc_path *icc_path_init(struct udevice *dev, struct icc_node *dst,
+ ssize_t num_nodes)
+{
+ struct icc_node *node = dst;
+ struct icc_path *path;
+ struct udevice *node_dev;
+ int i, ret;
+
+ debug("(dev=%s,node=%s)\n", dev->name, node->dev->name);
+
+ path = kzalloc(sizeof(struct icc_path) +
+ sizeof(struct icc_req) * num_nodes,
+ GFP_KERNEL);
+ if (!path)
+ return ERR_PTR(-ENOMEM);
+
+ path->dev = dev;
+ path->num_nodes = num_nodes;
+
+ for (i = num_nodes - 1; i >= 0; i--) {
+ debug("(req[%d]=%s)\n", i, node->dev->name);
+ hlist_add_head(&path->reqs[i].req_node, &node->req_list);
+ path->reqs[i].node = node;
+ path->reqs[i].enabled = true;
+
+ /* Probe this node since used in an active path */
+ ret = uclass_get_device_tail(node->dev, 0, &node_dev);
+ if (ret)
+ return ERR_PTR(ret);
+
+ node->users++;
+
+ /* reference to previous node was saved during path traversal */
+ node = node->reverse;
+ }
+
+ return path;
+}
+
+static struct icc_path *icc_path_find(struct udevice *dev, struct icc_node *src,
+ struct icc_node *dst)
+{
+ struct icc_path *path = ERR_PTR(-EPROBE_DEFER);
+ struct icc_node *n, *node = NULL;
+ struct list_head traverse_list;
+ struct list_head edge_list;
+ struct list_head visited_list;
+ size_t i, depth = 1;
+ bool found = false;
+
+ debug("(dev=%s,src=%s,dest=%p\n",
+ dev->name, src->dev->name, dst->dev->name);
+
+ INIT_LIST_HEAD(&traverse_list);
+ INIT_LIST_HEAD(&edge_list);
+ INIT_LIST_HEAD(&visited_list);
+
+ list_add(&src->search_list, &traverse_list);
+ src->reverse = NULL;
+
+ do {
+ list_for_each_entry_safe(node, n, &traverse_list, search_list) {
+ if (node == dst) {
+ found = true;
+ list_splice_init(&edge_list, &visited_list);
+ list_splice_init(&traverse_list, &visited_list);
+ break;
+ }
+ for (i = 0; i < node->num_links; i++) {
+ struct icc_node *tmp;
+
+ tmp = icc_node_find(node->links[i]);
+ if (!tmp) {
+ dev_err(dev, "missing link to node id %lx\n",
+ node->links[i]);
+ path = ERR_PTR(-ENOENT);
+ goto out;
+ }
+
+ if (tmp->is_traversed)
+ continue;
+
+ tmp->is_traversed = true;
+ tmp->reverse = node;
+ list_add_tail(&tmp->search_list, &edge_list);
+ }
+ }
+
+ if (found)
+ break;
+
+ list_splice_init(&traverse_list, &visited_list);
+ list_splice_init(&edge_list, &traverse_list);
+
+ /* count the hops including the source */
+ depth++;
+
+ } while (!list_empty(&traverse_list));
+
+out:
+ /* reset the traversed state */
+ list_for_each_entry_reverse(n, &visited_list, search_list)
+ n->is_traversed = false;
+
+ if (found)
+ path = icc_path_init(dev, dst, depth);
+
+ return path;
+}
+
+static struct icc_node *of_icc_get_from_provider(struct udevice *dev,
+ const struct ofnode_phandle_args *args)
+{
+ const struct interconnect_ops *ops;
+ struct udevice *icc_dev;
+ int ret;
+
+ ret = uclass_get_device_by_ofnode(UCLASS_INTERCONNECT, args->node,
+ &icc_dev);
+ if (ret) {
+ dev_err(dev, "uclass_get_device_by_ofnode failed: %d\n", ret);
+ return ERR_PTR(ret);
+ }
+ ops = device_get_ops(icc_dev);
+
+ return ops->of_xlate(icc_dev, args);
+}
+
+static struct icc_node *icc_node_find(const ulong id)
+{
+ struct udevice *dev;
+
+ for (uclass_find_first_device(UCLASS_ICC_NODE, &dev);
+ dev;
+ uclass_find_next_device(&dev)) {
+ if (dev_get_driver_data(dev) == id)
+ return dev_get_uclass_plat(dev);
+ }
+
+ return NULL;
+}
+
+static bool icc_node_busy(struct udevice *dev)
+{
+ struct icc_node *node = dev_get_uclass_plat(dev);
+
+ debug("(dev=%s,users=%d)\n", dev->name, node->users);
+
+ return !!node->users;
+}
+
+struct icc_node *icc_node_create(struct udevice *dev,
+ ulong id, const char *name)
+{
+ struct udevice *node;
+ struct driver *drv;
+ int ret;
+
+ drv = lists_driver_lookup_name("icc_node");
+ if (!drv)
+ return ERR_PTR(-ENOENT);
+
+ ret = device_bind_with_driver_data(dev, drv, strdup(name),
+ id, ofnode_null(), &node);
+ if (ret)
+ return ERR_PTR(ret);
+
+ device_set_name_alloced(node);
+
+ return dev_get_uclass_plat(node);
+}
+
+int icc_link_create(struct icc_node *node, const ulong dst_id)
+{
+ ulong *new;
+
+ new = realloc(node->links,
+ (node->num_links + 1) * sizeof(*node->links));
+ if (!new)
+ return -ENOMEM;
+
+ node->links = new;
+ node->links[node->num_links++] = dst_id;
+
+ return 0;
+}
+
+static int icc_node_bind(struct udevice *dev)
+{
+ struct icc_node *node = dev_get_uclass_plat(dev);
+
+ debug("(dev=%s)\n", dev->name);
+
+ node->dev = dev;
+
+ return 0;
+}
+
+static int icc_node_probe(struct udevice *dev)
+{
+ struct icc_node *node = dev_get_uclass_plat(dev);
+
+ debug("(dev=%s,parent=%p,id=%lx)\n",
+ dev->name, dev->parent->name, dev_get_driver_data(dev));
+
+ node->avg_bw = 0;
+ node->peak_bw = 0;
+
+ return 0;
+}
+
+static int icc_node_remove(struct udevice *dev)
+{
+ debug("(dev=%s,parent=%s,id=%lx)\n",
+ dev->name, dev->parent->name, dev_get_driver_data(dev));
+
+ if (icc_node_busy(dev))
+ return -EBUSY;
+
+ return 0;
+}
+
+static int icc_node_unbind(struct udevice *dev)
+{
+ struct icc_node *node = dev_get_uclass_plat(dev);
+
+ debug("(dev=%s,id=%lx)\n",
+ dev->name, dev_get_driver_data(dev));
+
+ kfree(node->links);
+
+ return 0;
+}
+
+UCLASS_DRIVER(interconnect) = {
+ .id = UCLASS_INTERCONNECT,
+ .name = "interconnect",
+ .per_device_plat_auto = sizeof(struct icc_provider),
+};
+
+U_BOOT_DRIVER(icc_node) = {
+ .name = "icc_node",
+ .id = UCLASS_ICC_NODE,
+ .bind = icc_node_bind,
+ .probe = icc_node_probe,
+ .remove = icc_node_remove,
+ .unbind = icc_node_unbind,
+};
+
+UCLASS_DRIVER(icc_node) = {
+ .id = UCLASS_ICC_NODE,
+ .name = "icc_node",
+ .per_device_plat_auto = sizeof(struct icc_node),
+};
diff --git a/drivers/interconnect/qcom/Kconfig b/drivers/interconnect/qcom/Kconfig
new file mode 100644
index 00000000000..b105c3a1f9a
--- /dev/null
+++ b/drivers/interconnect/qcom/Kconfig
@@ -0,0 +1,12 @@
+config INTERCONNECT_QCOM_RPMH
+ bool "Enable interconnect support for SoCs with RPMh"
+ depends on QCOM_RPMH
+ help
+ Enable support for the interconnect helpers to vote with
+ the RPMh subsystems in Qualcomm SoCs
+
+config INTERCONNECT_QCOM_SM8650
+ bool "Enable interconnect support for SM8650 SoC"
+ depends on INTERCONNECT_QCOM_RPMH
+ help
+ Enable support for the interconnect driver for the SM8650 SoC.
diff --git a/drivers/interconnect/qcom/Makefile b/drivers/interconnect/qcom/Makefile
new file mode 100644
index 00000000000..a33a65bd82e
--- /dev/null
+++ b/drivers/interconnect/qcom/Makefile
@@ -0,0 +1,7 @@
+# SPDX-License-Identifier: GPL-2.0
+#
+# Copyright (c) 2025 Linaro Limited
+#
+
+obj-$(CONFIG_$(PHASE_)INTERCONNECT_QCOM_RPMH) += icc-rpmh.o bcm-voter.o
+obj-$(CONFIG_$(PHASE_)INTERCONNECT_QCOM_SM8650) += sm8650.o
diff --git a/drivers/interconnect/qcom/bcm-voter.c b/drivers/interconnect/qcom/bcm-voter.c
new file mode 100644
index 00000000000..361b03f207a
--- /dev/null
+++ b/drivers/interconnect/qcom/bcm-voter.c
@@ -0,0 +1,340 @@
+// 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.
+ * Copyright (c) 2025 Linaro Limited
+ */
+
+#include <dm.h>
+#include <log.h>
+#include <malloc.h>
+#include <linux/err.h>
+#include <div64.h>
+#include <dm/device_compat.h>
+#include <linux/list_sort.h>
+
+#include "bcm-voter.h"
+
+/* TODO drop WAKE/SLEEP buckets fills if we really don't need them */
+
+/**
+ * struct bcm_voter - Bus Clock Manager voter
+ * @dev: reference to the device that communicates with the BCM
+ * @np: reference to the device node to match bcm voters
+ * @commit_list: list containing bcms to be committed to hardware
+ * @ws_list: list containing bcms that have different wake/sleep votes
+ * @voter_node: list of bcm voters
+ * @tcs_wait: mask for which buckets require TCS completion
+ */
+struct bcm_voter {
+ struct udevice *dev;
+ struct list_head commit_list;
+ struct list_head ws_list;
+ u32 tcs_wait;
+};
+
+static int cmp_vcd(void *priv, struct list_head *a, struct list_head *b)
+{
+ struct qcom_icc_bcm *bcm_a = list_entry(a, struct qcom_icc_bcm, list);
+ struct qcom_icc_bcm *bcm_b = list_entry(b, struct qcom_icc_bcm, list);
+
+ return bcm_a->aux_data.vcd - bcm_b->aux_data.vcd;
+}
+
+static u64 bcm_div(u64 num, u32 base)
+{
+ /* Ensure that small votes aren't lost. */
+ if (num && num < base)
+ return 1;
+
+ do_div(num, base);
+
+ return num;
+}
+
+/* BCMs with enable_mask use one-hot-encoding for on/off signaling */
+static void bcm_aggregate_mask(struct qcom_icc_bcm *bcm)
+{
+ struct qcom_icc_node *node;
+ int bucket, i;
+
+ for (bucket = 0; bucket < QCOM_ICC_NUM_BUCKETS; bucket++) {
+ bcm->vote_x[bucket] = 0;
+ bcm->vote_y[bucket] = 0;
+
+ for (i = 0; i < bcm->num_nodes; i++) {
+ node = bcm->nodes[i];
+
+ /* If any vote in this bucket exists, keep the BCM enabled */
+ if (node->sum_avg[bucket] || node->max_peak[bucket]) {
+ bcm->vote_x[bucket] = 0;
+ bcm->vote_y[bucket] = bcm->enable_mask;
+ break;
+ }
+ }
+ }
+
+ if (bcm->keepalive) {
+ bcm->vote_x[QCOM_ICC_BUCKET_AMC] = bcm->enable_mask;
+ bcm->vote_x[QCOM_ICC_BUCKET_WAKE] = bcm->enable_mask;
+ bcm->vote_y[QCOM_ICC_BUCKET_AMC] = bcm->enable_mask;
+ bcm->vote_y[QCOM_ICC_BUCKET_WAKE] = bcm->enable_mask;
+ }
+}
+
+static void bcm_aggregate(struct qcom_icc_bcm *bcm)
+{
+ struct qcom_icc_node *node;
+ size_t i, bucket;
+ u64 agg_avg[QCOM_ICC_NUM_BUCKETS] = {0};
+ u64 agg_peak[QCOM_ICC_NUM_BUCKETS] = {0};
+ u64 temp;
+
+ for (bucket = 0; bucket < QCOM_ICC_NUM_BUCKETS; bucket++) {
+ for (i = 0; i < bcm->num_nodes; i++) {
+ node = bcm->nodes[i];
+ temp = bcm_div(node->sum_avg[bucket] * bcm->aux_data.width,
+ node->buswidth * node->channels);
+ agg_avg[bucket] = max(agg_avg[bucket], temp);
+
+ temp = bcm_div(node->max_peak[bucket] * bcm->aux_data.width,
+ node->buswidth);
+ agg_peak[bucket] = max(agg_peak[bucket], temp);
+ }
+
+ temp = agg_avg[bucket] * bcm->vote_scale;
+ bcm->vote_x[bucket] = bcm_div(temp, bcm->aux_data.unit);
+
+ temp = agg_peak[bucket] * bcm->vote_scale;
+ bcm->vote_y[bucket] = bcm_div(temp, bcm->aux_data.unit);
+ }
+
+ if (bcm->keepalive && bcm->vote_x[QCOM_ICC_BUCKET_AMC] == 0 &&
+ bcm->vote_y[QCOM_ICC_BUCKET_AMC] == 0) {
+ bcm->vote_x[QCOM_ICC_BUCKET_AMC] = 1;
+ bcm->vote_x[QCOM_ICC_BUCKET_WAKE] = 1;
+ bcm->vote_y[QCOM_ICC_BUCKET_AMC] = 1;
+ bcm->vote_y[QCOM_ICC_BUCKET_WAKE] = 1;
+ }
+}
+
+static inline void tcs_cmd_gen(struct tcs_cmd *cmd, u64 vote_x, u64 vote_y,
+ u32 addr, bool commit, bool wait)
+{
+ bool valid = true;
+
+ if (!cmd)
+ return;
+
+ memset(cmd, 0, sizeof(*cmd));
+
+ if (vote_x == 0 && vote_y == 0)
+ valid = false;
+
+ if (vote_x > BCM_TCS_CMD_VOTE_MASK)
+ vote_x = BCM_TCS_CMD_VOTE_MASK;
+
+ if (vote_y > BCM_TCS_CMD_VOTE_MASK)
+ vote_y = BCM_TCS_CMD_VOTE_MASK;
+
+ cmd->addr = addr;
+ cmd->data = BCM_TCS_CMD(commit, valid, vote_x, vote_y);
+
+ /*
+ * Set the wait for completion flag on command that need to be completed
+ * before the next command.
+ */
+ cmd->wait = wait;
+}
+
+static void tcs_list_gen(struct bcm_voter *voter, int bucket,
+ struct tcs_cmd tcs_list[MAX_VCD],
+ int n[MAX_VCD + 1])
+{
+ struct list_head *bcm_list = &voter->commit_list;
+ struct qcom_icc_bcm *bcm;
+ bool commit, wait;
+ size_t idx = 0, batch = 0, cur_vcd_size = 0;
+
+ memset(n, 0, sizeof(int) * (MAX_VCD + 1));
+
+ list_for_each_entry(bcm, bcm_list, list) {
+ commit = false;
+ cur_vcd_size++;
+ if ((list_is_last(&bcm->list, bcm_list)) ||
+ bcm->aux_data.vcd != list_next_entry(bcm, list)->aux_data.vcd) {
+ commit = true;
+ cur_vcd_size = 0;
+ }
+
+ wait = commit && (voter->tcs_wait & BIT(bucket));
+
+ tcs_cmd_gen(&tcs_list[idx], bcm->vote_x[bucket],
+ bcm->vote_y[bucket], bcm->addr, commit, wait);
+ idx++;
+ n[batch]++;
+ /*
+ * Batch the BCMs in such a way that we do not split them in
+ * multiple payloads when they are under the same VCD. This is
+ * to ensure that every BCM is committed since we only set the
+ * commit bit on the last BCM request of every VCD.
+ */
+ if (n[batch] >= MAX_RPMH_PAYLOAD) {
+ if (!commit) {
+ n[batch] -= cur_vcd_size;
+ n[batch + 1] = cur_vcd_size;
+ }
+ batch++;
+ }
+ }
+}
+
+/**
+ * of_bcm_voter_get - gets a bcm voter handle from DT node
+ * @dev: device pointer for the consumer device
+ * @name: name for the bcm voter device
+ *
+ * This function will match a device_node pointer for the phandle
+ * specified in the device DT and return a bcm_voter handle on success.
+ *
+ * Returns bcm_voter pointer or ERR_PTR() on error. EPROBE_DEFER is returned
+ * when matching bcm voter is yet to be found.
+ */
+struct bcm_voter *of_bcm_voter_get(struct udevice *dev, const char *name)
+{
+ struct ofnode_phandle_args args;
+ struct udevice *bcm_dev;
+ int ret, idx = 0;
+
+ if (name) {
+ idx = dev_read_stringlist_search(dev, "qcom,bcm-voter-names", name);
+ if (idx < 0)
+ return ERR_PTR(idx);
+ }
+
+ ret = dev_read_phandle_with_args(dev, "qcom,bcm-voters", NULL, 0,
+ idx, &args);
+ if (ret)
+ return ERR_PTR(idx);
+
+ ret = uclass_get_device_by_ofnode(UCLASS_MISC, args.node,
+ &bcm_dev);
+ if (ret) {
+ debug("%s: uclass_get_device_by_ofnode failed: %d\n",
+ __func__, ret);
+ return ERR_PTR(ret);
+ }
+
+ return dev_get_priv(bcm_dev);
+}
+
+/**
+ * qcom_icc_bcm_voter_add - queues up the bcm nodes that require updates
+ * @voter: voter that the bcms are being added to
+ * @bcm: bcm to add to the commit and wake sleep list
+ */
+void qcom_icc_bcm_voter_add(struct bcm_voter *voter, struct qcom_icc_bcm *bcm)
+{
+ if (!voter)
+ return;
+
+ if (list_empty(&bcm->list))
+ list_add_tail(&bcm->list, &voter->commit_list);
+
+ if (list_empty(&bcm->ws_list))
+ list_add_tail(&bcm->ws_list, &voter->ws_list);
+}
+
+/**
+ * qcom_icc_bcm_voter_commit - generates and commits tcs cmds based on bcms
+ * @voter: voter that needs flushing
+ *
+ * This function generates a set of AMC commands and flushes to the BCM device
+ * associated with the voter. It conditionally generate WAKE and SLEEP commands
+ * based on deltas between WAKE/SLEEP requirements. The ws_list persists
+ * through multiple commit requests and bcm nodes are removed only when the
+ * requirements for WAKE matches SLEEP.
+ *
+ * Returns 0 on success, or an appropriate error code otherwise.
+ */
+int qcom_icc_bcm_voter_commit(struct bcm_voter *voter)
+{
+ struct qcom_icc_bcm *bcm;
+ struct qcom_icc_bcm *bcm_tmp;
+ int commit_idx[MAX_VCD + 1];
+ struct tcs_cmd cmds[MAX_BCMS];
+ int ret = 0;
+
+ if (!voter)
+ return 0;
+
+ list_for_each_entry(bcm, &voter->commit_list, list) {
+ if (bcm->enable_mask)
+ bcm_aggregate_mask(bcm);
+ else
+ bcm_aggregate(bcm);
+ }
+
+ /*
+ * Pre sort the BCMs based on VCD for ease of generating a command list
+ * that groups the BCMs with the same VCD together. VCDs are numbered
+ * with lowest being the most expensive time wise, ensuring that
+ * those commands are being sent the earliest in the queue. This needs
+ * to be sorted every commit since we can't guarantee the order in which
+ * the BCMs are added to the list.
+ */
+ list_sort(NULL, &voter->commit_list, cmp_vcd);
+
+ /*
+ * Construct the command list based on a pre ordered list of BCMs
+ * based on VCD.
+ */
+ tcs_list_gen(voter, QCOM_ICC_BUCKET_AMC, cmds, commit_idx);
+ if (!commit_idx[0])
+ goto out;
+
+ for (int i = 0 ; commit_idx[i] ; ++i) {
+ ret = rpmh_write(voter->dev, RPMH_ACTIVE_ONLY_STATE,
+ &cmds[i], commit_idx[i]);
+ if (ret) {
+ pr_err("Error sending AMC RPMH requests (%d)\n", ret);
+ goto out;
+ }
+ }
+
+ /* TOFIX vote for WAKE & SLEEP ?? */
+
+out:
+ list_for_each_entry_safe(bcm, bcm_tmp, &voter->commit_list, list)
+ list_del_init(&bcm->list);
+
+ return ret;
+}
+
+static int qcom_icc_bcm_voter_probe(struct udevice *dev)
+{
+ struct bcm_voter *voter = dev_get_priv(dev);
+
+ voter->dev = dev;
+
+ if (dev_read_u32(dev, "qcom,tcs-wait", &voter->tcs_wait))
+ voter->tcs_wait = QCOM_ICC_TAG_ACTIVE_ONLY;
+
+ INIT_LIST_HEAD(&voter->commit_list);
+ INIT_LIST_HEAD(&voter->ws_list);
+
+ return 0;
+}
+
+static const struct udevice_id qcom_icc_bcm_voter_ids[] = {
+ { .compatible = "qcom,bcm-voter" },
+ { }
+};
+
+U_BOOT_DRIVER(qcom_icc_bcm_voter) = {
+ .name = "qcom_bcm_voter",
+ .id = UCLASS_MISC,
+ .priv_auto = sizeof(struct bcm_voter),
+ .probe = qcom_icc_bcm_voter_probe,
+ .of_match = qcom_icc_bcm_voter_ids,
+};
diff --git a/drivers/interconnect/qcom/bcm-voter.h b/drivers/interconnect/qcom/bcm-voter.h
new file mode 100644
index 00000000000..5acb5822e0e
--- /dev/null
+++ b/drivers/interconnect/qcom/bcm-voter.h
@@ -0,0 +1,19 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (c) 2020, The Linux Foundation. All rights reserved.
+ */
+
+#ifndef __DRIVERS_INTERCONNECT_QCOM_BCM_VOTER_H__
+#define __DRIVERS_INTERCONNECT_QCOM_BCM_VOTER_H__
+
+#include <soc/qcom/cmd-db.h>
+#include <soc/qcom/rpmh.h>
+#include <soc/qcom/tcs.h>
+
+#include "icc-rpmh.h"
+
+struct bcm_voter *of_bcm_voter_get(struct udevice *dev, const char *name);
+void qcom_icc_bcm_voter_add(struct bcm_voter *voter, struct qcom_icc_bcm *bcm);
+int qcom_icc_bcm_voter_commit(struct bcm_voter *voter);
+
+#endif
diff --git a/drivers/interconnect/qcom/icc-rpmh.c b/drivers/interconnect/qcom/icc-rpmh.c
new file mode 100644
index 00000000000..23b4f5e16c6
--- /dev/null
+++ b/drivers/interconnect/qcom/icc-rpmh.c
@@ -0,0 +1,224 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2021, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2022, Qualcomm Innovation Center, Inc. All rights reserved.
+ * Copyright (c) 2025 Linaro Limited
+ */
+
+#include <dm.h>
+#include <log.h>
+#include <malloc.h>
+#include <interconnect-uclass.h>
+#include <dt-bindings/interconnect/qcom,icc.h>
+#include <linux/err.h>
+#include <dm/device_compat.h>
+
+#include "icc-rpmh.h"
+#include "bcm-voter.h"
+
+static inline struct qcom_icc_provider *to_qcom_provider(struct udevice *dev)
+{
+ return dev_get_plat(dev);
+}
+
+static int qcom_icc_set(struct icc_node *src, struct icc_node *dst)
+{
+ struct qcom_icc_provider *qp;
+ struct icc_node *node;
+
+ if (!src)
+ node = dst;
+ else
+ node = src;
+
+ qp = to_qcom_provider(node->dev->parent);
+ qcom_icc_bcm_voter_commit(qp->voter);
+
+ return 0;
+}
+
+static int qcom_icc_aggregate(struct icc_node *node, u32 tag, u32 avg_bw,
+ u32 peak_bw, u32 *agg_avg, u32 *agg_peak)
+{
+ size_t i;
+ struct qcom_icc_node *qn;
+
+ qn = node->data;
+
+ if (!tag)
+ tag = QCOM_ICC_TAG_ALWAYS;
+
+ for (i = 0; i < QCOM_ICC_NUM_BUCKETS; i++) {
+ if (tag & BIT(i)) {
+ qn->sum_avg[i] += avg_bw;
+ qn->max_peak[i] = max_t(u32, qn->max_peak[i], peak_bw);
+ }
+ }
+
+ *agg_avg += avg_bw;
+ *agg_peak = max_t(u32, *agg_peak, peak_bw);
+
+ return 0;
+}
+
+static void qcom_icc_pre_aggregate(struct icc_node *node)
+{
+ size_t i;
+ struct qcom_icc_node *qn;
+ struct qcom_icc_provider *qp;
+
+ qn = node->data;
+ qp = to_qcom_provider(node->dev->parent);
+
+ for (i = 0; i < QCOM_ICC_NUM_BUCKETS; i++) {
+ qn->sum_avg[i] = 0;
+ qn->max_peak[i] = 0;
+ }
+
+ for (i = 0; i < qn->num_bcms; i++)
+ qcom_icc_bcm_voter_add(qp->voter, qn->bcms[i]);
+}
+
+static struct icc_node *qcom_icc_xlate(struct udevice *dev,
+ const struct ofnode_phandle_args *spec)
+{
+ struct icc_provider *priv = dev_get_uclass_plat(dev);
+ unsigned int idx = spec->args[0];
+
+ if (idx >= priv->xlate_num_nodes) {
+ pr_err("%s: invalid index %u\n", __func__, idx);
+ return ERR_PTR(-EINVAL);
+ }
+
+ return priv->xlate_nodes[idx];
+}
+
+struct interconnect_ops qcom_icc_rpmh_ops = {
+ .set = qcom_icc_set,
+ .pre_aggregate = qcom_icc_pre_aggregate,
+ .aggregate = qcom_icc_aggregate,
+ .of_xlate = qcom_icc_xlate,
+};
+
+/**
+ * qcom_icc_bcm_init - populates bcm aux data and connect qnodes
+ * @bcm: bcm to be initialized
+ * @dev: associated provider device
+ *
+ * Return: 0 on success, or an error code otherwise
+ */
+int qcom_icc_bcm_init(struct qcom_icc_bcm *bcm, struct udevice *dev)
+{
+ struct qcom_icc_node *qn;
+ const struct bcm_db *data;
+ size_t data_count;
+ int i;
+
+ /* BCM is already initialised*/
+ if (bcm->addr)
+ return 0;
+
+ bcm->addr = cmd_db_read_addr(bcm->name);
+ if (!bcm->addr) {
+ dev_err(dev, "%s could not find RPMh address\n",
+ bcm->name);
+ return -EINVAL;
+ }
+
+ data = cmd_db_read_aux_data(bcm->name, &data_count);
+ if (IS_ERR(data)) {
+ dev_err(dev, "%s command db read error (%ld)\n",
+ bcm->name, PTR_ERR(data));
+ return PTR_ERR(data);
+ }
+ if (!data_count) {
+ dev_err(dev, "%s command db missing or partial aux data\n",
+ bcm->name);
+ return -EINVAL;
+ }
+
+ bcm->aux_data.unit = le32_to_cpu(data->unit);
+ bcm->aux_data.width = le16_to_cpu(data->width);
+ bcm->aux_data.vcd = data->vcd;
+ bcm->aux_data.reserved = data->reserved;
+ INIT_LIST_HEAD(&bcm->list);
+ INIT_LIST_HEAD(&bcm->ws_list);
+
+ if (!bcm->vote_scale)
+ bcm->vote_scale = 1000;
+
+ /* Link Qnodes to their respective BCMs */
+ for (i = 0; i < bcm->num_nodes; i++) {
+ qn = bcm->nodes[i];
+ qn->bcms[qn->num_bcms] = bcm;
+ qn->num_bcms++;
+ }
+
+ return 0;
+}
+
+int qcom_icc_rpmh_probe(struct udevice *dev)
+{
+ struct qcom_icc_provider *qp = dev_get_plat(dev);
+ int i;
+
+ qp->voter = of_bcm_voter_get(qp->dev, NULL);
+ if (IS_ERR(qp->voter))
+ return PTR_ERR(qp->voter);
+
+ for (i = 0; i < qp->desc->num_bcms; i++)
+ qcom_icc_bcm_init(qp->desc->bcms[i], dev);
+
+ return 0;
+}
+
+int qcom_icc_rpmh_bind(struct udevice *dev)
+{
+ struct icc_provider *priv = dev_get_uclass_plat(dev);
+ struct qcom_icc_provider *qp = dev_get_plat(dev);
+ struct qcom_icc_node * const *qnodes, *qn;
+ struct icc_node *node;
+ size_t num_nodes, i, j;
+
+ qp->desc = (const struct qcom_icc_desc *)dev_get_driver_data(dev);
+ if (!qp->desc)
+ return -EINVAL;
+
+ qnodes = qp->desc->nodes;
+ num_nodes = qp->desc->num_nodes;
+
+ priv->xlate_num_nodes = num_nodes;
+ priv->xlate_nodes = calloc(sizeof(node), num_nodes);
+ if (!priv->xlate_nodes)
+ return -ENOMEM;
+
+ qp->dev = dev;
+
+ for (i = 0; i < num_nodes; i++) {
+ qn = qnodes[i];
+ if (!qn)
+ continue;
+
+ node = icc_node_create(dev, qn->id, qn->name);
+ if (IS_ERR(node))
+ return PTR_ERR(node);
+
+ node->data = qn;
+
+ for (j = 0; j < qn->num_links; j++)
+ icc_link_create(node, qn->links[j]);
+
+ priv->xlate_nodes[i] = node;
+ }
+
+ return 0;
+}
+
+int qcom_icc_rpmh_unbind(struct udevice *dev)
+{
+ struct icc_provider *priv = dev_get_uclass_plat(dev);
+
+ free(priv->xlate_nodes);
+
+ return 0;
+}
diff --git a/drivers/interconnect/qcom/icc-rpmh.h b/drivers/interconnect/qcom/icc-rpmh.h
new file mode 100644
index 00000000000..95421d61033
--- /dev/null
+++ b/drivers/interconnect/qcom/icc-rpmh.h
@@ -0,0 +1,130 @@
+/* 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.
+ */
+
+#ifndef __DRIVERS_INTERCONNECT_QCOM_ICC_RPMH_H__
+#define __DRIVERS_INTERCONNECT_QCOM_ICC_RPMH_H__
+
+#include <dt-bindings/interconnect/qcom,icc.h>
+
+/**
+ * struct qcom_icc_provider - Qualcomm specific interconnect provider
+ * @dev:
+ * @desc:
+ */
+struct qcom_icc_provider {
+ struct udevice *dev;
+ const struct qcom_icc_desc *desc;
+ struct bcm_voter *voter;
+};
+
+/**
+ * struct bcm_db - Auxiliary data pertaining to each Bus Clock Manager (BCM)
+ * @unit: divisor used to convert bytes/sec bw value to an RPMh msg
+ * @width: multiplier used to convert bytes/sec bw value to an RPMh msg
+ * @vcd: virtual clock domain that this bcm belongs to
+ * @reserved: reserved field
+ */
+struct bcm_db {
+ __le32 unit;
+ __le16 width;
+ u8 vcd;
+ u8 reserved;
+};
+
+#define MAX_PORTS 2
+
+#define MAX_LINKS 128
+#define MAX_BCMS 64
+#define MAX_BCM_PER_NODE 3
+#define MAX_VCD 10
+
+/**
+ * struct qcom_icc_node - Qualcomm specific interconnect nodes
+ * @name: the node name used in debugfs
+ * @links: an array of nodes where we can go next while traversing
+ * @id: a unique node identifier
+ * @link_nodes: links associated with this node
+ * @node: icc_node associated with this node
+ * @num_links: the total number of @links
+ * @channels: num of channels at this node
+ * @buswidth: width of the interconnect between a node and the bus
+ * @sum_avg: current sum aggregate value of all avg bw requests
+ * @max_peak: current max aggregate value of all peak bw requests
+ * @bcms: list of bcms associated with this logical node
+ * @num_bcms: num of @bcms
+ */
+struct qcom_icc_node {
+ const char *name;
+ u16 links[MAX_LINKS];
+ u16 id;
+ struct qcom_icc_node **link_nodes;
+ struct icc_node *node;
+ u16 num_links;
+ u16 channels;
+ u16 buswidth;
+ u64 sum_avg[QCOM_ICC_NUM_BUCKETS];
+ u64 max_peak[QCOM_ICC_NUM_BUCKETS];
+ struct qcom_icc_bcm *bcms[MAX_BCM_PER_NODE];
+ size_t num_bcms;
+};
+
+/**
+ * struct qcom_icc_bcm - Qualcomm specific hardware accelerator nodes
+ * known as Bus Clock Manager (BCM)
+ * @name: the bcm node name used to fetch BCM data from command db
+ * @type: latency or bandwidth bcm
+ * @addr: address offsets used when voting to RPMH
+ * @vote_x: aggregated threshold values, represents sum_bw when @type is bw bcm
+ * @vote_y: aggregated threshold values, represents peak_bw when @type is bw bcm
+ * @vote_scale: scaling factor for vote_x and vote_y
+ * @enable_mask: optional mask to send as vote instead of vote_x/vote_y
+ * @dirty: flag used to indicate whether the bcm needs to be committed
+ * @keepalive: flag used to indicate whether a keepalive is required
+ * @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
+ * @ws_list: used to keep track of bcms that may transition between wake/sleep
+ * @num_nodes: total number of @num_nodes
+ * @nodes: list of qcom_icc_nodes that this BCM encapsulates
+ */
+struct qcom_icc_bcm {
+ const char *name;
+ u32 type;
+ u32 addr;
+ u64 vote_x[QCOM_ICC_NUM_BUCKETS];
+ u64 vote_y[QCOM_ICC_NUM_BUCKETS];
+ u64 vote_scale;
+ u32 enable_mask;
+ bool dirty;
+ bool keepalive;
+ struct bcm_db aux_data;
+ struct list_head list;
+ struct list_head ws_list;
+ size_t num_nodes;
+ struct qcom_icc_node *nodes[];
+};
+
+struct qcom_icc_fabric {
+ struct qcom_icc_node **nodes;
+ size_t num_nodes;
+};
+
+struct qcom_icc_desc {
+ const struct regmap_config *config;
+ struct qcom_icc_node * const *nodes;
+ size_t num_nodes;
+ struct qcom_icc_bcm * const *bcms;
+ size_t num_bcms;
+ bool qos_requires_clocks;
+ bool alloc_dyn_id;
+};
+
+extern struct interconnect_ops qcom_icc_rpmh_ops;
+int qcom_icc_rpmh_probe(struct udevice *dev);
+int qcom_icc_rpmh_bind(struct udevice *dev);
+int qcom_icc_rpmh_unbind(struct udevice *dev);
+
+#endif
diff --git a/drivers/interconnect/qcom/sm8650.c b/drivers/interconnect/qcom/sm8650.c
new file mode 100644
index 00000000000..99c5923dc3c
--- /dev/null
+++ b/drivers/interconnect/qcom/sm8650.c
@@ -0,0 +1,1665 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2021, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2022, Qualcomm Innovation Center, Inc. All rights reserved.
+ * Copyright (c) 2025 Linaro Limited
+ */
+
+#include <dm.h>
+#include <interconnect-uclass.h>
+#include <dt-bindings/interconnect/qcom,sm8650-rpmh.h>
+
+#include "icc-rpmh.h"
+#include "sm8650.h"
+
+static struct qcom_icc_node qhm_qspi = {
+ .name = "qhm_qspi",
+ .id = SM8650_MASTER_QSPI_0,
+ .channels = 1,
+ .buswidth = 4,
+ .num_links = 1,
+ .links = { SM8650_SLAVE_A1NOC_SNOC },
+};
+
+static struct qcom_icc_node qhm_qup1 = {
+ .name = "qhm_qup1",
+ .id = SM8650_MASTER_QUP_1,
+ .channels = 1,
+ .buswidth = 4,
+ .num_links = 1,
+ .links = { SM8650_SLAVE_A1NOC_SNOC },
+};
+
+static struct qcom_icc_node qxm_qup02 = {
+ .name = "qxm_qup02",
+ .id = SM8650_MASTER_QUP_3,
+ .channels = 1,
+ .buswidth = 8,
+ .num_links = 1,
+ .links = { SM8650_SLAVE_A1NOC_SNOC },
+};
+
+static struct qcom_icc_node xm_sdc4 = {
+ .name = "xm_sdc4",
+ .id = SM8650_MASTER_SDCC_4,
+ .channels = 1,
+ .buswidth = 8,
+ .num_links = 1,
+ .links = { SM8650_SLAVE_A1NOC_SNOC },
+};
+
+static struct qcom_icc_node xm_ufs_mem = {
+ .name = "xm_ufs_mem",
+ .id = SM8650_MASTER_UFS_MEM,
+ .channels = 1,
+ .buswidth = 16,
+ .num_links = 1,
+ .links = { SM8650_SLAVE_A1NOC_SNOC },
+};
+
+static struct qcom_icc_node xm_usb3_0 = {
+ .name = "xm_usb3_0",
+ .id = SM8650_MASTER_USB3_0,
+ .channels = 1,
+ .buswidth = 8,
+ .num_links = 1,
+ .links = { SM8650_SLAVE_A1NOC_SNOC },
+};
+
+static struct qcom_icc_node qhm_qdss_bam = {
+ .name = "qhm_qdss_bam",
+ .id = SM8650_MASTER_QDSS_BAM,
+ .channels = 1,
+ .buswidth = 4,
+ .num_links = 1,
+ .links = { SM8650_SLAVE_A2NOC_SNOC },
+};
+
+static struct qcom_icc_node qhm_qup2 = {
+ .name = "qhm_qup2",
+ .id = SM8650_MASTER_QUP_2,
+ .channels = 1,
+ .buswidth = 4,
+ .num_links = 1,
+ .links = { SM8650_SLAVE_A2NOC_SNOC },
+};
+
+static struct qcom_icc_node qxm_crypto = {
+ .name = "qxm_crypto",
+ .id = SM8650_MASTER_CRYPTO,
+ .channels = 1,
+ .buswidth = 8,
+ .num_links = 1,
+ .links = { SM8650_SLAVE_A2NOC_SNOC },
+};
+
+static struct qcom_icc_node qxm_ipa = {
+ .name = "qxm_ipa",
+ .id = SM8650_MASTER_IPA,
+ .channels = 1,
+ .buswidth = 8,
+ .num_links = 1,
+ .links = { SM8650_SLAVE_A2NOC_SNOC },
+};
+
+static struct qcom_icc_node qxm_sp = {
+ .name = "qxm_sp",
+ .id = SM8650_MASTER_SP,
+ .channels = 1,
+ .buswidth = 8,
+ .num_links = 1,
+ .links = { SM8650_SLAVE_A2NOC_SNOC },
+};
+
+static struct qcom_icc_node xm_qdss_etr_0 = {
+ .name = "xm_qdss_etr_0",
+ .id = SM8650_MASTER_QDSS_ETR,
+ .channels = 1,
+ .buswidth = 8,
+ .num_links = 1,
+ .links = { SM8650_SLAVE_A2NOC_SNOC },
+};
+
+static struct qcom_icc_node xm_qdss_etr_1 = {
+ .name = "xm_qdss_etr_1",
+ .id = SM8650_MASTER_QDSS_ETR_1,
+ .channels = 1,
+ .buswidth = 8,
+ .num_links = 1,
+ .links = { SM8650_SLAVE_A2NOC_SNOC },
+};
+
+static struct qcom_icc_node xm_sdc2 = {
+ .name = "xm_sdc2",
+ .id = SM8650_MASTER_SDCC_2,
+ .channels = 1,
+ .buswidth = 8,
+ .num_links = 1,
+ .links = { SM8650_SLAVE_A2NOC_SNOC },
+};
+
+static struct qcom_icc_node qup0_core_master = {
+ .name = "qup0_core_master",
+ .id = SM8650_MASTER_QUP_CORE_0,
+ .channels = 1,
+ .buswidth = 4,
+ .num_links = 1,
+ .links = { SM8650_SLAVE_QUP_CORE_0 },
+};
+
+static struct qcom_icc_node qup1_core_master = {
+ .name = "qup1_core_master",
+ .id = SM8650_MASTER_QUP_CORE_1,
+ .channels = 1,
+ .buswidth = 4,
+ .num_links = 1,
+ .links = { SM8650_SLAVE_QUP_CORE_1 },
+};
+
+static struct qcom_icc_node qup2_core_master = {
+ .name = "qup2_core_master",
+ .id = SM8650_MASTER_QUP_CORE_2,
+ .channels = 1,
+ .buswidth = 4,
+ .num_links = 1,
+ .links = { SM8650_SLAVE_QUP_CORE_2 },
+};
+
+static struct qcom_icc_node qsm_cfg = {
+ .name = "qsm_cfg",
+ .id = SM8650_MASTER_CNOC_CFG,
+ .channels = 1,
+ .buswidth = 4,
+ .num_links = 46,
+ .links = { SM8650_SLAVE_AHB2PHY_SOUTH, SM8650_SLAVE_AHB2PHY_NORTH,
+ SM8650_SLAVE_CAMERA_CFG, SM8650_SLAVE_CLK_CTL,
+ SM8650_SLAVE_RBCPR_CX_CFG, SM8650_SLAVE_CPR_HMX,
+ SM8650_SLAVE_RBCPR_MMCX_CFG, SM8650_SLAVE_RBCPR_MXA_CFG,
+ SM8650_SLAVE_RBCPR_MXC_CFG, SM8650_SLAVE_CPR_NSPCX,
+ SM8650_SLAVE_CRYPTO_0_CFG, SM8650_SLAVE_CX_RDPM,
+ SM8650_SLAVE_DISPLAY_CFG, SM8650_SLAVE_GFX3D_CFG,
+ SM8650_SLAVE_I2C, SM8650_SLAVE_I3C_IBI0_CFG,
+ SM8650_SLAVE_I3C_IBI1_CFG, SM8650_SLAVE_IMEM_CFG,
+ SM8650_SLAVE_CNOC_MSS, SM8650_SLAVE_MX_2_RDPM,
+ SM8650_SLAVE_MX_RDPM, SM8650_SLAVE_PCIE_0_CFG,
+ SM8650_SLAVE_PCIE_1_CFG, SM8650_SLAVE_PCIE_RSCC,
+ SM8650_SLAVE_PDM, SM8650_SLAVE_PRNG,
+ SM8650_SLAVE_QDSS_CFG, SM8650_SLAVE_QSPI_0,
+ SM8650_SLAVE_QUP_3, SM8650_SLAVE_QUP_1,
+ SM8650_SLAVE_QUP_2, SM8650_SLAVE_SDCC_2,
+ SM8650_SLAVE_SDCC_4, SM8650_SLAVE_SPSS_CFG,
+ SM8650_SLAVE_TCSR, SM8650_SLAVE_TLMM,
+ SM8650_SLAVE_UFS_MEM_CFG, SM8650_SLAVE_USB3_0,
+ SM8650_SLAVE_VENUS_CFG, SM8650_SLAVE_VSENSE_CTRL_CFG,
+ SM8650_SLAVE_CNOC_MNOC_CFG, SM8650_SLAVE_NSP_QTB_CFG,
+ SM8650_SLAVE_PCIE_ANOC_CFG, SM8650_SLAVE_SERVICE_CNOC_CFG,
+ SM8650_SLAVE_QDSS_STM, SM8650_SLAVE_TCU },
+};
+
+static struct qcom_icc_node qnm_gemnoc_cnoc = {
+ .name = "qnm_gemnoc_cnoc",
+ .id = SM8650_MASTER_GEM_NOC_CNOC,
+ .channels = 1,
+ .buswidth = 16,
+ .num_links = 9,
+ .links = { SM8650_SLAVE_AOSS, SM8650_SLAVE_IPA_CFG,
+ SM8650_SLAVE_IPC_ROUTER_CFG, SM8650_SLAVE_TME_CFG,
+ SM8650_SLAVE_APPSS, SM8650_SLAVE_CNOC_CFG,
+ SM8650_SLAVE_DDRSS_CFG, SM8650_SLAVE_IMEM,
+ SM8650_SLAVE_SERVICE_CNOC },
+};
+
+static struct qcom_icc_node qnm_gemnoc_pcie = {
+ .name = "qnm_gemnoc_pcie",
+ .id = SM8650_MASTER_GEM_NOC_PCIE_SNOC,
+ .channels = 1,
+ .buswidth = 16,
+ .num_links = 2,
+ .links = { SM8650_SLAVE_PCIE_0, SM8650_SLAVE_PCIE_1 },
+};
+
+static struct qcom_icc_node alm_gpu_tcu = {
+ .name = "alm_gpu_tcu",
+ .id = SM8650_MASTER_GPU_TCU,
+ .channels = 1,
+ .buswidth = 8,
+ .num_links = 2,
+ .links = { SM8650_SLAVE_GEM_NOC_CNOC, SM8650_SLAVE_LLCC },
+};
+
+static struct qcom_icc_node alm_sys_tcu = {
+ .name = "alm_sys_tcu",
+ .id = SM8650_MASTER_SYS_TCU,
+ .channels = 1,
+ .buswidth = 8,
+ .num_links = 2,
+ .links = { SM8650_SLAVE_GEM_NOC_CNOC, SM8650_SLAVE_LLCC },
+};
+
+static struct qcom_icc_node alm_ubwc_p_tcu = {
+ .name = "alm_ubwc_p_tcu",
+ .id = SM8650_MASTER_UBWC_P_TCU,
+ .channels = 1,
+ .buswidth = 8,
+ .num_links = 2,
+ .links = { SM8650_SLAVE_GEM_NOC_CNOC, SM8650_SLAVE_LLCC },
+};
+
+static struct qcom_icc_node chm_apps = {
+ .name = "chm_apps",
+ .id = SM8650_MASTER_APPSS_PROC,
+ .channels = 3,
+ .buswidth = 32,
+ .num_links = 3,
+ .links = { SM8650_SLAVE_GEM_NOC_CNOC, SM8650_SLAVE_LLCC,
+ SM8650_SLAVE_MEM_NOC_PCIE_SNOC },
+};
+
+static struct qcom_icc_node qnm_gpu = {
+ .name = "qnm_gpu",
+ .id = SM8650_MASTER_GFX3D,
+ .channels = 2,
+ .buswidth = 32,
+ .num_links = 2,
+ .links = { SM8650_SLAVE_GEM_NOC_CNOC, SM8650_SLAVE_LLCC },
+};
+
+static struct qcom_icc_node qnm_lpass_gemnoc = {
+ .name = "qnm_lpass_gemnoc",
+ .id = SM8650_MASTER_LPASS_GEM_NOC,
+ .channels = 1,
+ .buswidth = 16,
+ .num_links = 3,
+ .links = { SM8650_SLAVE_GEM_NOC_CNOC, SM8650_SLAVE_LLCC,
+ SM8650_SLAVE_MEM_NOC_PCIE_SNOC },
+};
+
+static struct qcom_icc_node qnm_mdsp = {
+ .name = "qnm_mdsp",
+ .id = SM8650_MASTER_MSS_PROC,
+ .channels = 1,
+ .buswidth = 16,
+ .num_links = 3,
+ .links = { SM8650_SLAVE_GEM_NOC_CNOC, SM8650_SLAVE_LLCC,
+ SM8650_SLAVE_MEM_NOC_PCIE_SNOC },
+};
+
+static struct qcom_icc_node qnm_mnoc_hf = {
+ .name = "qnm_mnoc_hf",
+ .id = SM8650_MASTER_MNOC_HF_MEM_NOC,
+ .channels = 2,
+ .buswidth = 32,
+ .num_links = 2,
+ .links = { SM8650_SLAVE_GEM_NOC_CNOC, SM8650_SLAVE_LLCC },
+};
+
+static struct qcom_icc_node qnm_mnoc_sf = {
+ .name = "qnm_mnoc_sf",
+ .id = SM8650_MASTER_MNOC_SF_MEM_NOC,
+ .channels = 2,
+ .buswidth = 32,
+ .num_links = 2,
+ .links = { SM8650_SLAVE_GEM_NOC_CNOC, SM8650_SLAVE_LLCC },
+};
+
+static struct qcom_icc_node qnm_nsp_gemnoc = {
+ .name = "qnm_nsp_gemnoc",
+ .id = SM8650_MASTER_COMPUTE_NOC,
+ .channels = 2,
+ .buswidth = 32,
+ .num_links = 3,
+ .links = { SM8650_SLAVE_GEM_NOC_CNOC, SM8650_SLAVE_LLCC,
+ SM8650_SLAVE_MEM_NOC_PCIE_SNOC },
+};
+
+static struct qcom_icc_node qnm_pcie = {
+ .name = "qnm_pcie",
+ .id = SM8650_MASTER_ANOC_PCIE_GEM_NOC,
+ .channels = 1,
+ .buswidth = 16,
+ .num_links = 2,
+ .links = { SM8650_SLAVE_GEM_NOC_CNOC, SM8650_SLAVE_LLCC },
+};
+
+static struct qcom_icc_node qnm_snoc_sf = {
+ .name = "qnm_snoc_sf",
+ .id = SM8650_MASTER_SNOC_SF_MEM_NOC,
+ .channels = 1,
+ .buswidth = 16,
+ .num_links = 3,
+ .links = { SM8650_SLAVE_GEM_NOC_CNOC, SM8650_SLAVE_LLCC,
+ SM8650_SLAVE_MEM_NOC_PCIE_SNOC },
+};
+
+static struct qcom_icc_node qnm_ubwc_p = {
+ .name = "qnm_ubwc_p",
+ .id = SM8650_MASTER_UBWC_P,
+ .channels = 1,
+ .buswidth = 32,
+ .num_links = 1,
+ .links = { SM8650_SLAVE_LLCC },
+};
+
+static struct qcom_icc_node xm_gic = {
+ .name = "xm_gic",
+ .id = SM8650_MASTER_GIC,
+ .channels = 1,
+ .buswidth = 8,
+ .num_links = 1,
+ .links = { SM8650_SLAVE_LLCC },
+};
+
+static struct qcom_icc_node qnm_lpiaon_noc = {
+ .name = "qnm_lpiaon_noc",
+ .id = SM8650_MASTER_LPIAON_NOC,
+ .channels = 1,
+ .buswidth = 16,
+ .num_links = 1,
+ .links = { SM8650_SLAVE_LPASS_GEM_NOC },
+};
+
+static struct qcom_icc_node qnm_lpass_lpinoc = {
+ .name = "qnm_lpass_lpinoc",
+ .id = SM8650_MASTER_LPASS_LPINOC,
+ .channels = 1,
+ .buswidth = 16,
+ .num_links = 1,
+ .links = { SM8650_SLAVE_LPIAON_NOC_LPASS_AG_NOC },
+};
+
+static struct qcom_icc_node qxm_lpinoc_dsp_axim = {
+ .name = "qxm_lpinoc_dsp_axim",
+ .id = SM8650_MASTER_LPASS_PROC,
+ .channels = 1,
+ .buswidth = 16,
+ .num_links = 1,
+ .links = { SM8650_SLAVE_LPICX_NOC_LPIAON_NOC },
+};
+
+static struct qcom_icc_node llcc_mc = {
+ .name = "llcc_mc",
+ .id = SM8650_MASTER_LLCC,
+ .channels = 4,
+ .buswidth = 4,
+ .num_links = 1,
+ .links = { SM8650_SLAVE_EBI1 },
+};
+
+static struct qcom_icc_node qnm_camnoc_hf = {
+ .name = "qnm_camnoc_hf",
+ .id = SM8650_MASTER_CAMNOC_HF,
+ .channels = 2,
+ .buswidth = 32,
+ .num_links = 1,
+ .links = { SM8650_SLAVE_MNOC_HF_MEM_NOC },
+};
+
+static struct qcom_icc_node qnm_camnoc_icp = {
+ .name = "qnm_camnoc_icp",
+ .id = SM8650_MASTER_CAMNOC_ICP,
+ .channels = 1,
+ .buswidth = 8,
+ .num_links = 1,
+ .links = { SM8650_SLAVE_MNOC_SF_MEM_NOC },
+};
+
+static struct qcom_icc_node qnm_camnoc_sf = {
+ .name = "qnm_camnoc_sf",
+ .id = SM8650_MASTER_CAMNOC_SF,
+ .channels = 2,
+ .buswidth = 32,
+ .num_links = 1,
+ .links = { SM8650_SLAVE_MNOC_SF_MEM_NOC },
+};
+
+static struct qcom_icc_node qnm_mdp = {
+ .name = "qnm_mdp",
+ .id = SM8650_MASTER_MDP,
+ .channels = 2,
+ .buswidth = 32,
+ .num_links = 1,
+ .links = { SM8650_SLAVE_MNOC_HF_MEM_NOC },
+};
+
+static struct qcom_icc_node qnm_vapss_hcp = {
+ .name = "qnm_vapss_hcp",
+ .id = SM8650_MASTER_CDSP_HCP,
+ .channels = 1,
+ .buswidth = 32,
+ .num_links = 1,
+ .links = { SM8650_SLAVE_MNOC_SF_MEM_NOC },
+};
+
+static struct qcom_icc_node qnm_video = {
+ .name = "qnm_video",
+ .id = SM8650_MASTER_VIDEO,
+ .channels = 2,
+ .buswidth = 32,
+ .num_links = 1,
+ .links = { SM8650_SLAVE_MNOC_SF_MEM_NOC },
+};
+
+static struct qcom_icc_node qnm_video_cv_cpu = {
+ .name = "qnm_video_cv_cpu",
+ .id = SM8650_MASTER_VIDEO_CV_PROC,
+ .channels = 1,
+ .buswidth = 8,
+ .num_links = 1,
+ .links = { SM8650_SLAVE_MNOC_SF_MEM_NOC },
+};
+
+static struct qcom_icc_node qnm_video_cvp = {
+ .name = "qnm_video_cvp",
+ .id = SM8650_MASTER_VIDEO_PROC,
+ .channels = 2,
+ .buswidth = 32,
+ .num_links = 1,
+ .links = { SM8650_SLAVE_MNOC_SF_MEM_NOC },
+};
+
+static struct qcom_icc_node qnm_video_v_cpu = {
+ .name = "qnm_video_v_cpu",
+ .id = SM8650_MASTER_VIDEO_V_PROC,
+ .channels = 1,
+ .buswidth = 8,
+ .num_links = 1,
+ .links = { SM8650_SLAVE_MNOC_SF_MEM_NOC },
+};
+
+static struct qcom_icc_node qsm_mnoc_cfg = {
+ .name = "qsm_mnoc_cfg",
+ .id = SM8650_MASTER_CNOC_MNOC_CFG,
+ .channels = 1,
+ .buswidth = 4,
+ .num_links = 1,
+ .links = { SM8650_SLAVE_SERVICE_MNOC },
+};
+
+static struct qcom_icc_node qnm_nsp = {
+ .name = "qnm_nsp",
+ .id = SM8650_MASTER_CDSP_PROC,
+ .channels = 2,
+ .buswidth = 32,
+ .num_links = 1,
+ .links = { SM8650_SLAVE_CDSP_MEM_NOC },
+};
+
+static struct qcom_icc_node qsm_pcie_anoc_cfg = {
+ .name = "qsm_pcie_anoc_cfg",
+ .id = SM8650_MASTER_PCIE_ANOC_CFG,
+ .channels = 1,
+ .buswidth = 4,
+ .num_links = 1,
+ .links = { SM8650_SLAVE_SERVICE_PCIE_ANOC },
+};
+
+static struct qcom_icc_node xm_pcie3_0 = {
+ .name = "xm_pcie3_0",
+ .id = SM8650_MASTER_PCIE_0,
+ .channels = 1,
+ .buswidth = 8,
+ .num_links = 1,
+ .links = { SM8650_SLAVE_ANOC_PCIE_GEM_NOC },
+};
+
+static struct qcom_icc_node xm_pcie3_1 = {
+ .name = "xm_pcie3_1",
+ .id = SM8650_MASTER_PCIE_1,
+ .channels = 1,
+ .buswidth = 16,
+ .num_links = 1,
+ .links = { SM8650_SLAVE_ANOC_PCIE_GEM_NOC },
+};
+
+static struct qcom_icc_node qnm_aggre1_noc = {
+ .name = "qnm_aggre1_noc",
+ .id = SM8650_MASTER_A1NOC_SNOC,
+ .channels = 1,
+ .buswidth = 16,
+ .num_links = 1,
+ .links = { SM8650_SLAVE_SNOC_GEM_NOC_SF },
+};
+
+static struct qcom_icc_node qnm_aggre2_noc = {
+ .name = "qnm_aggre2_noc",
+ .id = SM8650_MASTER_A2NOC_SNOC,
+ .channels = 1,
+ .buswidth = 16,
+ .num_links = 1,
+ .links = { SM8650_SLAVE_SNOC_GEM_NOC_SF },
+};
+
+static struct qcom_icc_node qnm_apss_noc = {
+ .name = "qnm_apss_noc",
+ .id = SM8650_MASTER_APSS_NOC,
+ .channels = 1,
+ .buswidth = 4,
+ .num_links = 1,
+ .links = { SM8650_SLAVE_SNOC_GEM_NOC_SF },
+};
+
+static struct qcom_icc_node qns_a1noc_snoc = {
+ .name = "qns_a1noc_snoc",
+ .id = SM8650_SLAVE_A1NOC_SNOC,
+ .channels = 1,
+ .buswidth = 16,
+ .num_links = 1,
+ .links = { SM8650_MASTER_A1NOC_SNOC },
+};
+
+static struct qcom_icc_node qns_a2noc_snoc = {
+ .name = "qns_a2noc_snoc",
+ .id = SM8650_SLAVE_A2NOC_SNOC,
+ .channels = 1,
+ .buswidth = 16,
+ .num_links = 1,
+ .links = { SM8650_MASTER_A2NOC_SNOC },
+};
+
+static struct qcom_icc_node qup0_core_slave = {
+ .name = "qup0_core_slave",
+ .id = SM8650_SLAVE_QUP_CORE_0,
+ .channels = 1,
+ .buswidth = 4,
+ .num_links = 0,
+};
+
+static struct qcom_icc_node qup1_core_slave = {
+ .name = "qup1_core_slave",
+ .id = SM8650_SLAVE_QUP_CORE_1,
+ .channels = 1,
+ .buswidth = 4,
+ .num_links = 0,
+};
+
+static struct qcom_icc_node qup2_core_slave = {
+ .name = "qup2_core_slave",
+ .id = SM8650_SLAVE_QUP_CORE_2,
+ .channels = 1,
+ .buswidth = 4,
+ .num_links = 0,
+};
+
+static struct qcom_icc_node qhs_ahb2phy0 = {
+ .name = "qhs_ahb2phy0",
+ .id = SM8650_SLAVE_AHB2PHY_SOUTH,
+ .channels = 1,
+ .buswidth = 4,
+ .num_links = 0,
+};
+
+static struct qcom_icc_node qhs_ahb2phy1 = {
+ .name = "qhs_ahb2phy1",
+ .id = SM8650_SLAVE_AHB2PHY_NORTH,
+ .channels = 1,
+ .buswidth = 4,
+ .num_links = 0,
+};
+
+static struct qcom_icc_node qhs_camera_cfg = {
+ .name = "qhs_camera_cfg",
+ .id = SM8650_SLAVE_CAMERA_CFG,
+ .channels = 1,
+ .buswidth = 4,
+ .num_links = 0,
+};
+
+static struct qcom_icc_node qhs_clk_ctl = {
+ .name = "qhs_clk_ctl",
+ .id = SM8650_SLAVE_CLK_CTL,
+ .channels = 1,
+ .buswidth = 4,
+ .num_links = 0,
+};
+
+static struct qcom_icc_node qhs_cpr_cx = {
+ .name = "qhs_cpr_cx",
+ .id = SM8650_SLAVE_RBCPR_CX_CFG,
+ .channels = 1,
+ .buswidth = 4,
+ .num_links = 0,
+};
+
+static struct qcom_icc_node qhs_cpr_hmx = {
+ .name = "qhs_cpr_hmx",
+ .id = SM8650_SLAVE_CPR_HMX,
+ .channels = 1,
+ .buswidth = 4,
+ .num_links = 0,
+};
+
+static struct qcom_icc_node qhs_cpr_mmcx = {
+ .name = "qhs_cpr_mmcx",
+ .id = SM8650_SLAVE_RBCPR_MMCX_CFG,
+ .channels = 1,
+ .buswidth = 4,
+ .num_links = 0,
+};
+
+static struct qcom_icc_node qhs_cpr_mxa = {
+ .name = "qhs_cpr_mxa",
+ .id = SM8650_SLAVE_RBCPR_MXA_CFG,
+ .channels = 1,
+ .buswidth = 4,
+ .num_links = 0,
+};
+
+static struct qcom_icc_node qhs_cpr_mxc = {
+ .name = "qhs_cpr_mxc",
+ .id = SM8650_SLAVE_RBCPR_MXC_CFG,
+ .channels = 1,
+ .buswidth = 4,
+ .num_links = 0,
+};
+
+static struct qcom_icc_node qhs_cpr_nspcx = {
+ .name = "qhs_cpr_nspcx",
+ .id = SM8650_SLAVE_CPR_NSPCX,
+ .channels = 1,
+ .buswidth = 4,
+ .num_links = 0,
+};
+
+static struct qcom_icc_node qhs_crypto0_cfg = {
+ .name = "qhs_crypto0_cfg",
+ .id = SM8650_SLAVE_CRYPTO_0_CFG,
+ .channels = 1,
+ .buswidth = 4,
+ .num_links = 0,
+};
+
+static struct qcom_icc_node qhs_cx_rdpm = {
+ .name = "qhs_cx_rdpm",
+ .id = SM8650_SLAVE_CX_RDPM,
+ .channels = 1,
+ .buswidth = 4,
+ .num_links = 0,
+};
+
+static struct qcom_icc_node qhs_display_cfg = {
+ .name = "qhs_display_cfg",
+ .id = SM8650_SLAVE_DISPLAY_CFG,
+ .channels = 1,
+ .buswidth = 4,
+ .num_links = 0,
+};
+
+static struct qcom_icc_node qhs_gpuss_cfg = {
+ .name = "qhs_gpuss_cfg",
+ .id = SM8650_SLAVE_GFX3D_CFG,
+ .channels = 1,
+ .buswidth = 8,
+ .num_links = 0,
+};
+
+static struct qcom_icc_node qhs_i2c = {
+ .name = "qhs_i2c",
+ .id = SM8650_SLAVE_I2C,
+ .channels = 1,
+ .buswidth = 4,
+ .num_links = 0,
+};
+
+static struct qcom_icc_node qhs_i3c_ibi0_cfg = {
+ .name = "qhs_i3c_ibi0_cfg",
+ .id = SM8650_SLAVE_I3C_IBI0_CFG,
+ .channels = 1,
+ .buswidth = 4,
+ .num_links = 0,
+};
+
+static struct qcom_icc_node qhs_i3c_ibi1_cfg = {
+ .name = "qhs_i3c_ibi1_cfg",
+ .id = SM8650_SLAVE_I3C_IBI1_CFG,
+ .channels = 1,
+ .buswidth = 4,
+ .num_links = 0,
+};
+
+static struct qcom_icc_node qhs_imem_cfg = {
+ .name = "qhs_imem_cfg",
+ .id = SM8650_SLAVE_IMEM_CFG,
+ .channels = 1,
+ .buswidth = 4,
+ .num_links = 0,
+};
+
+static struct qcom_icc_node qhs_mss_cfg = {
+ .name = "qhs_mss_cfg",
+ .id = SM8650_SLAVE_CNOC_MSS,
+ .channels = 1,
+ .buswidth = 4,
+ .num_links = 0,
+};
+
+static struct qcom_icc_node qhs_mx_2_rdpm = {
+ .name = "qhs_mx_2_rdpm",
+ .id = SM8650_SLAVE_MX_2_RDPM,
+ .channels = 1,
+ .buswidth = 4,
+ .num_links = 0,
+};
+
+static struct qcom_icc_node qhs_mx_rdpm = {
+ .name = "qhs_mx_rdpm",
+ .id = SM8650_SLAVE_MX_RDPM,
+ .channels = 1,
+ .buswidth = 4,
+ .num_links = 0,
+};
+
+static struct qcom_icc_node qhs_pcie0_cfg = {
+ .name = "qhs_pcie0_cfg",
+ .id = SM8650_SLAVE_PCIE_0_CFG,
+ .channels = 1,
+ .buswidth = 4,
+ .num_links = 0,
+};
+
+static struct qcom_icc_node qhs_pcie1_cfg = {
+ .name = "qhs_pcie1_cfg",
+ .id = SM8650_SLAVE_PCIE_1_CFG,
+ .channels = 1,
+ .buswidth = 4,
+ .num_links = 0,
+};
+
+static struct qcom_icc_node qhs_pcie_rscc = {
+ .name = "qhs_pcie_rscc",
+ .id = SM8650_SLAVE_PCIE_RSCC,
+ .channels = 1,
+ .buswidth = 4,
+ .num_links = 0,
+};
+
+static struct qcom_icc_node qhs_pdm = {
+ .name = "qhs_pdm",
+ .id = SM8650_SLAVE_PDM,
+ .channels = 1,
+ .buswidth = 4,
+ .num_links = 0,
+};
+
+static struct qcom_icc_node qhs_prng = {
+ .name = "qhs_prng",
+ .id = SM8650_SLAVE_PRNG,
+ .channels = 1,
+ .buswidth = 4,
+ .num_links = 0,
+};
+
+static struct qcom_icc_node qhs_qdss_cfg = {
+ .name = "qhs_qdss_cfg",
+ .id = SM8650_SLAVE_QDSS_CFG,
+ .channels = 1,
+ .buswidth = 4,
+ .num_links = 0,
+};
+
+static struct qcom_icc_node qhs_qspi = {
+ .name = "qhs_qspi",
+ .id = SM8650_SLAVE_QSPI_0,
+ .channels = 1,
+ .buswidth = 4,
+ .num_links = 0,
+};
+
+static struct qcom_icc_node qhs_qup02 = {
+ .name = "qhs_qup02",
+ .id = SM8650_SLAVE_QUP_3,
+ .channels = 1,
+ .buswidth = 4,
+ .num_links = 0,
+};
+
+static struct qcom_icc_node qhs_qup1 = {
+ .name = "qhs_qup1",
+ .id = SM8650_SLAVE_QUP_1,
+ .channels = 1,
+ .buswidth = 4,
+ .num_links = 0,
+};
+
+static struct qcom_icc_node qhs_qup2 = {
+ .name = "qhs_qup2",
+ .id = SM8650_SLAVE_QUP_2,
+ .channels = 1,
+ .buswidth = 4,
+ .num_links = 0,
+};
+
+static struct qcom_icc_node qhs_sdc2 = {
+ .name = "qhs_sdc2",
+ .id = SM8650_SLAVE_SDCC_2,
+ .channels = 1,
+ .buswidth = 4,
+ .num_links = 0,
+};
+
+static struct qcom_icc_node qhs_sdc4 = {
+ .name = "qhs_sdc4",
+ .id = SM8650_SLAVE_SDCC_4,
+ .channels = 1,
+ .buswidth = 4,
+ .num_links = 0,
+};
+
+static struct qcom_icc_node qhs_spss_cfg = {
+ .name = "qhs_spss_cfg",
+ .id = SM8650_SLAVE_SPSS_CFG,
+ .channels = 1,
+ .buswidth = 4,
+ .num_links = 0,
+};
+
+static struct qcom_icc_node qhs_tcsr = {
+ .name = "qhs_tcsr",
+ .id = SM8650_SLAVE_TCSR,
+ .channels = 1,
+ .buswidth = 4,
+ .num_links = 0,
+};
+
+static struct qcom_icc_node qhs_tlmm = {
+ .name = "qhs_tlmm",
+ .id = SM8650_SLAVE_TLMM,
+ .channels = 1,
+ .buswidth = 4,
+ .num_links = 0,
+};
+
+static struct qcom_icc_node qhs_ufs_mem_cfg = {
+ .name = "qhs_ufs_mem_cfg",
+ .id = SM8650_SLAVE_UFS_MEM_CFG,
+ .channels = 1,
+ .buswidth = 4,
+ .num_links = 0,
+};
+
+static struct qcom_icc_node qhs_usb3_0 = {
+ .name = "qhs_usb3_0",
+ .id = SM8650_SLAVE_USB3_0,
+ .channels = 1,
+ .buswidth = 4,
+ .num_links = 0,
+};
+
+static struct qcom_icc_node qhs_venus_cfg = {
+ .name = "qhs_venus_cfg",
+ .id = SM8650_SLAVE_VENUS_CFG,
+ .channels = 1,
+ .buswidth = 4,
+ .num_links = 0,
+};
+
+static struct qcom_icc_node qhs_vsense_ctrl_cfg = {
+ .name = "qhs_vsense_ctrl_cfg",
+ .id = SM8650_SLAVE_VSENSE_CTRL_CFG,
+ .channels = 1,
+ .buswidth = 4,
+ .num_links = 0,
+};
+
+static struct qcom_icc_node qss_mnoc_cfg = {
+ .name = "qss_mnoc_cfg",
+ .id = SM8650_SLAVE_CNOC_MNOC_CFG,
+ .channels = 1,
+ .buswidth = 4,
+ .num_links = 1,
+ .links = { SM8650_MASTER_CNOC_MNOC_CFG },
+};
+
+static struct qcom_icc_node qss_nsp_qtb_cfg = {
+ .name = "qss_nsp_qtb_cfg",
+ .id = SM8650_SLAVE_NSP_QTB_CFG,
+ .channels = 1,
+ .buswidth = 4,
+ .num_links = 0,
+};
+
+static struct qcom_icc_node qss_pcie_anoc_cfg = {
+ .name = "qss_pcie_anoc_cfg",
+ .id = SM8650_SLAVE_PCIE_ANOC_CFG,
+ .channels = 1,
+ .buswidth = 4,
+ .num_links = 1,
+ .links = { SM8650_MASTER_PCIE_ANOC_CFG },
+};
+
+static struct qcom_icc_node srvc_cnoc_cfg = {
+ .name = "srvc_cnoc_cfg",
+ .id = SM8650_SLAVE_SERVICE_CNOC_CFG,
+ .channels = 1,
+ .buswidth = 4,
+ .num_links = 0,
+};
+
+static struct qcom_icc_node xs_qdss_stm = {
+ .name = "xs_qdss_stm",
+ .id = SM8650_SLAVE_QDSS_STM,
+ .channels = 1,
+ .buswidth = 4,
+ .num_links = 0,
+};
+
+static struct qcom_icc_node xs_sys_tcu_cfg = {
+ .name = "xs_sys_tcu_cfg",
+ .id = SM8650_SLAVE_TCU,
+ .channels = 1,
+ .buswidth = 8,
+ .num_links = 0,
+};
+
+static struct qcom_icc_node qhs_aoss = {
+ .name = "qhs_aoss",
+ .id = SM8650_SLAVE_AOSS,
+ .channels = 1,
+ .buswidth = 4,
+ .num_links = 0,
+};
+
+static struct qcom_icc_node qhs_ipa = {
+ .name = "qhs_ipa",
+ .id = SM8650_SLAVE_IPA_CFG,
+ .channels = 1,
+ .buswidth = 4,
+ .num_links = 0,
+};
+
+static struct qcom_icc_node qhs_ipc_router = {
+ .name = "qhs_ipc_router",
+ .id = SM8650_SLAVE_IPC_ROUTER_CFG,
+ .channels = 1,
+ .buswidth = 4,
+ .num_links = 0,
+};
+
+static struct qcom_icc_node qhs_tme_cfg = {
+ .name = "qhs_tme_cfg",
+ .id = SM8650_SLAVE_TME_CFG,
+ .channels = 1,
+ .buswidth = 4,
+ .num_links = 0,
+};
+
+static struct qcom_icc_node qss_apss = {
+ .name = "qss_apss",
+ .id = SM8650_SLAVE_APPSS,
+ .channels = 1,
+ .buswidth = 4,
+ .num_links = 0,
+};
+
+static struct qcom_icc_node qss_cfg = {
+ .name = "qss_cfg",
+ .id = SM8650_SLAVE_CNOC_CFG,
+ .channels = 1,
+ .buswidth = 4,
+ .num_links = 1,
+ .links = { SM8650_MASTER_CNOC_CFG },
+};
+
+static struct qcom_icc_node qss_ddrss_cfg = {
+ .name = "qss_ddrss_cfg",
+ .id = SM8650_SLAVE_DDRSS_CFG,
+ .channels = 1,
+ .buswidth = 4,
+ .num_links = 0,
+};
+
+static struct qcom_icc_node qxs_imem = {
+ .name = "qxs_imem",
+ .id = SM8650_SLAVE_IMEM,
+ .channels = 1,
+ .buswidth = 8,
+ .num_links = 0,
+};
+
+static struct qcom_icc_node srvc_cnoc_main = {
+ .name = "srvc_cnoc_main",
+ .id = SM8650_SLAVE_SERVICE_CNOC,
+ .channels = 1,
+ .buswidth = 4,
+ .num_links = 0,
+};
+
+static struct qcom_icc_node xs_pcie_0 = {
+ .name = "xs_pcie_0",
+ .id = SM8650_SLAVE_PCIE_0,
+ .channels = 1,
+ .buswidth = 8,
+ .num_links = 0,
+};
+
+static struct qcom_icc_node xs_pcie_1 = {
+ .name = "xs_pcie_1",
+ .id = SM8650_SLAVE_PCIE_1,
+ .channels = 1,
+ .buswidth = 16,
+ .num_links = 0,
+};
+
+static struct qcom_icc_node qns_gem_noc_cnoc = {
+ .name = "qns_gem_noc_cnoc",
+ .id = SM8650_SLAVE_GEM_NOC_CNOC,
+ .channels = 1,
+ .buswidth = 16,
+ .num_links = 1,
+ .links = { SM8650_MASTER_GEM_NOC_CNOC },
+};
+
+static struct qcom_icc_node qns_llcc = {
+ .name = "qns_llcc",
+ .id = SM8650_SLAVE_LLCC,
+ .channels = 4,
+ .buswidth = 16,
+ .num_links = 1,
+ .links = { SM8650_MASTER_LLCC },
+};
+
+static struct qcom_icc_node qns_pcie = {
+ .name = "qns_pcie",
+ .id = SM8650_SLAVE_MEM_NOC_PCIE_SNOC,
+ .channels = 1,
+ .buswidth = 8,
+ .num_links = 1,
+ .links = { SM8650_MASTER_GEM_NOC_PCIE_SNOC },
+};
+
+static struct qcom_icc_node qns_lpass_ag_noc_gemnoc = {
+ .name = "qns_lpass_ag_noc_gemnoc",
+ .id = SM8650_SLAVE_LPASS_GEM_NOC,
+ .channels = 1,
+ .buswidth = 16,
+ .num_links = 1,
+ .links = { SM8650_MASTER_LPASS_GEM_NOC },
+};
+
+static struct qcom_icc_node qns_lpass_aggnoc = {
+ .name = "qns_lpass_aggnoc",
+ .id = SM8650_SLAVE_LPIAON_NOC_LPASS_AG_NOC,
+ .channels = 1,
+ .buswidth = 16,
+ .num_links = 1,
+ .links = { SM8650_MASTER_LPIAON_NOC },
+};
+
+static struct qcom_icc_node qns_lpi_aon_noc = {
+ .name = "qns_lpi_aon_noc",
+ .id = SM8650_SLAVE_LPICX_NOC_LPIAON_NOC,
+ .channels = 1,
+ .buswidth = 16,
+ .num_links = 1,
+ .links = { SM8650_MASTER_LPASS_LPINOC },
+};
+
+static struct qcom_icc_node ebi = {
+ .name = "ebi",
+ .id = SM8650_SLAVE_EBI1,
+ .channels = 4,
+ .buswidth = 4,
+ .num_links = 0,
+};
+
+static struct qcom_icc_node qns_mem_noc_hf = {
+ .name = "qns_mem_noc_hf",
+ .id = SM8650_SLAVE_MNOC_HF_MEM_NOC,
+ .channels = 2,
+ .buswidth = 32,
+ .num_links = 1,
+ .links = { SM8650_MASTER_MNOC_HF_MEM_NOC },
+};
+
+static struct qcom_icc_node qns_mem_noc_sf = {
+ .name = "qns_mem_noc_sf",
+ .id = SM8650_SLAVE_MNOC_SF_MEM_NOC,
+ .channels = 2,
+ .buswidth = 32,
+ .num_links = 1,
+ .links = { SM8650_MASTER_MNOC_SF_MEM_NOC },
+};
+
+static struct qcom_icc_node srvc_mnoc = {
+ .name = "srvc_mnoc",
+ .id = SM8650_SLAVE_SERVICE_MNOC,
+ .channels = 1,
+ .buswidth = 4,
+ .num_links = 0,
+};
+
+static struct qcom_icc_node qns_nsp_gemnoc = {
+ .name = "qns_nsp_gemnoc",
+ .id = SM8650_SLAVE_CDSP_MEM_NOC,
+ .channels = 2,
+ .buswidth = 32,
+ .num_links = 1,
+ .links = { SM8650_MASTER_COMPUTE_NOC },
+};
+
+static struct qcom_icc_node qns_pcie_mem_noc = {
+ .name = "qns_pcie_mem_noc",
+ .id = SM8650_SLAVE_ANOC_PCIE_GEM_NOC,
+ .channels = 1,
+ .buswidth = 16,
+ .num_links = 1,
+ .links = { SM8650_MASTER_ANOC_PCIE_GEM_NOC },
+};
+
+static struct qcom_icc_node srvc_pcie_aggre_noc = {
+ .name = "srvc_pcie_aggre_noc",
+ .id = SM8650_SLAVE_SERVICE_PCIE_ANOC,
+ .channels = 1,
+ .buswidth = 4,
+ .num_links = 0,
+};
+
+static struct qcom_icc_node qns_gemnoc_sf = {
+ .name = "qns_gemnoc_sf",
+ .id = SM8650_SLAVE_SNOC_GEM_NOC_SF,
+ .channels = 1,
+ .buswidth = 16,
+ .num_links = 1,
+ .links = { SM8650_MASTER_SNOC_SF_MEM_NOC },
+};
+
+static struct qcom_icc_bcm bcm_acv = {
+ .name = "ACV",
+ .enable_mask = BIT(0),
+ .num_nodes = 1,
+ .nodes = { &ebi },
+};
+
+static struct qcom_icc_bcm bcm_ce0 = {
+ .name = "CE0",
+ .num_nodes = 1,
+ .nodes = { &qxm_crypto },
+};
+
+static struct qcom_icc_bcm bcm_cn0 = {
+ .name = "CN0",
+ .enable_mask = BIT(0),
+ .keepalive = true,
+ .num_nodes = 59,
+ .nodes = { &qsm_cfg, &qhs_ahb2phy0,
+ &qhs_ahb2phy1, &qhs_camera_cfg,
+ &qhs_clk_ctl, &qhs_cpr_cx,
+ &qhs_cpr_hmx, &qhs_cpr_mmcx,
+ &qhs_cpr_mxa, &qhs_cpr_mxc,
+ &qhs_cpr_nspcx, &qhs_crypto0_cfg,
+ &qhs_cx_rdpm, &qhs_display_cfg,
+ &qhs_gpuss_cfg, &qhs_i2c,
+ &qhs_i3c_ibi0_cfg, &qhs_i3c_ibi1_cfg,
+ &qhs_imem_cfg, &qhs_mss_cfg,
+ &qhs_mx_2_rdpm, &qhs_mx_rdpm,
+ &qhs_pcie0_cfg, &qhs_pcie1_cfg,
+ &qhs_pcie_rscc, &qhs_pdm,
+ &qhs_prng, &qhs_qdss_cfg,
+ &qhs_qspi, &qhs_qup02,
+ &qhs_qup1, &qhs_qup2,
+ &qhs_sdc2, &qhs_sdc4,
+ &qhs_spss_cfg, &qhs_tcsr,
+ &qhs_tlmm, &qhs_ufs_mem_cfg,
+ &qhs_usb3_0, &qhs_venus_cfg,
+ &qhs_vsense_ctrl_cfg, &qss_mnoc_cfg,
+ &qss_nsp_qtb_cfg, &qss_pcie_anoc_cfg,
+ &srvc_cnoc_cfg, &xs_qdss_stm,
+ &xs_sys_tcu_cfg, &qnm_gemnoc_cnoc,
+ &qnm_gemnoc_pcie, &qhs_aoss,
+ &qhs_ipa, &qhs_ipc_router,
+ &qhs_tme_cfg, &qss_apss,
+ &qss_cfg, &qss_ddrss_cfg,
+ &qxs_imem, &srvc_cnoc_main,
+ &xs_pcie_0, &xs_pcie_1 },
+};
+
+static struct qcom_icc_bcm bcm_co0 = {
+ .name = "CO0",
+ .enable_mask = BIT(0),
+ .num_nodes = 2,
+ .nodes = { &qnm_nsp, &qns_nsp_gemnoc },
+};
+
+static struct qcom_icc_bcm bcm_lp0 = {
+ .name = "LP0",
+ .num_nodes = 2,
+ .nodes = { &qnm_lpass_lpinoc, &qns_lpass_aggnoc },
+};
+
+static struct qcom_icc_bcm bcm_mc0 = {
+ .name = "MC0",
+ .keepalive = true,
+ .num_nodes = 1,
+ .nodes = { &ebi },
+};
+
+static struct qcom_icc_bcm bcm_mm0 = {
+ .name = "MM0",
+ .keepalive = true,
+ .num_nodes = 1,
+ .nodes = { &qns_mem_noc_hf },
+};
+
+static struct qcom_icc_bcm bcm_mm1 = {
+ .name = "MM1",
+ .enable_mask = BIT(0),
+ .num_nodes = 8,
+ .nodes = { &qnm_camnoc_hf, &qnm_camnoc_icp,
+ &qnm_camnoc_sf, &qnm_vapss_hcp,
+ &qnm_video_cv_cpu, &qnm_video_cvp,
+ &qnm_video_v_cpu, &qns_mem_noc_sf },
+};
+
+static struct qcom_icc_bcm bcm_qup0 = {
+ .name = "QUP0",
+ .keepalive = true,
+ .vote_scale = 1,
+ .num_nodes = 1,
+ .nodes = { &qup0_core_slave },
+};
+
+static struct qcom_icc_bcm bcm_qup1 = {
+ .name = "QUP1",
+ .keepalive = true,
+ .vote_scale = 1,
+ .num_nodes = 1,
+ .nodes = { &qup1_core_slave },
+};
+
+static struct qcom_icc_bcm bcm_qup2 = {
+ .name = "QUP2",
+ .keepalive = true,
+ .vote_scale = 1,
+ .num_nodes = 1,
+ .nodes = { &qup2_core_slave },
+};
+
+static struct qcom_icc_bcm bcm_sh0 = {
+ .name = "SH0",
+ .keepalive = true,
+ .num_nodes = 1,
+ .nodes = { &qns_llcc },
+};
+
+static struct qcom_icc_bcm bcm_sh1 = {
+ .name = "SH1",
+ .enable_mask = BIT(0),
+ .num_nodes = 15,
+ .nodes = { &alm_gpu_tcu, &alm_sys_tcu,
+ &alm_ubwc_p_tcu, &chm_apps,
+ &qnm_gpu, &qnm_mdsp,
+ &qnm_mnoc_hf, &qnm_mnoc_sf,
+ &qnm_nsp_gemnoc, &qnm_pcie,
+ &qnm_snoc_sf, &qnm_ubwc_p,
+ &xm_gic, &qns_gem_noc_cnoc,
+ &qns_pcie },
+};
+
+static struct qcom_icc_bcm bcm_sn0 = {
+ .name = "SN0",
+ .keepalive = true,
+ .num_nodes = 1,
+ .nodes = { &qns_gemnoc_sf },
+};
+
+static struct qcom_icc_bcm bcm_sn2 = {
+ .name = "SN2",
+ .num_nodes = 1,
+ .nodes = { &qnm_aggre1_noc },
+};
+
+static struct qcom_icc_bcm bcm_sn3 = {
+ .name = "SN3",
+ .num_nodes = 1,
+ .nodes = { &qnm_aggre2_noc },
+};
+
+static struct qcom_icc_bcm bcm_sn4 = {
+ .name = "SN4",
+ .num_nodes = 1,
+ .nodes = { &qns_pcie_mem_noc },
+};
+
+static struct qcom_icc_node * const aggre1_noc_nodes[] = {
+ [MASTER_QSPI_0] = &qhm_qspi,
+ [MASTER_QUP_1] = &qhm_qup1,
+ [MASTER_QUP_3] = &qxm_qup02,
+ [MASTER_SDCC_4] = &xm_sdc4,
+ [MASTER_UFS_MEM] = &xm_ufs_mem,
+ [MASTER_USB3_0] = &xm_usb3_0,
+ [SLAVE_A1NOC_SNOC] = &qns_a1noc_snoc,
+};
+
+static const struct qcom_icc_desc sm8650_aggre1_noc = {
+ .nodes = aggre1_noc_nodes,
+ .num_nodes = ARRAY_SIZE(aggre1_noc_nodes),
+};
+
+static struct qcom_icc_bcm * const aggre2_noc_bcms[] = {
+ &bcm_ce0,
+};
+
+static struct qcom_icc_node * const aggre2_noc_nodes[] = {
+ [MASTER_QDSS_BAM] = &qhm_qdss_bam,
+ [MASTER_QUP_2] = &qhm_qup2,
+ [MASTER_CRYPTO] = &qxm_crypto,
+ [MASTER_IPA] = &qxm_ipa,
+ [MASTER_SP] = &qxm_sp,
+ [MASTER_QDSS_ETR] = &xm_qdss_etr_0,
+ [MASTER_QDSS_ETR_1] = &xm_qdss_etr_1,
+ [MASTER_SDCC_2] = &xm_sdc2,
+ [SLAVE_A2NOC_SNOC] = &qns_a2noc_snoc,
+};
+
+static const struct qcom_icc_desc sm8650_aggre2_noc = {
+ .nodes = aggre2_noc_nodes,
+ .num_nodes = ARRAY_SIZE(aggre2_noc_nodes),
+ .bcms = aggre2_noc_bcms,
+ .num_bcms = ARRAY_SIZE(aggre2_noc_bcms),
+};
+
+static struct qcom_icc_bcm * const clk_virt_bcms[] = {
+ &bcm_qup0,
+ &bcm_qup1,
+ &bcm_qup2,
+};
+
+static struct qcom_icc_node * const clk_virt_nodes[] = {
+ [MASTER_QUP_CORE_0] = &qup0_core_master,
+ [MASTER_QUP_CORE_1] = &qup1_core_master,
+ [MASTER_QUP_CORE_2] = &qup2_core_master,
+ [SLAVE_QUP_CORE_0] = &qup0_core_slave,
+ [SLAVE_QUP_CORE_1] = &qup1_core_slave,
+ [SLAVE_QUP_CORE_2] = &qup2_core_slave,
+};
+
+static const struct qcom_icc_desc sm8650_clk_virt = {
+ .nodes = clk_virt_nodes,
+ .num_nodes = ARRAY_SIZE(clk_virt_nodes),
+ .bcms = clk_virt_bcms,
+ .num_bcms = ARRAY_SIZE(clk_virt_bcms),
+};
+
+static struct qcom_icc_bcm * const config_noc_bcms[] = {
+ &bcm_cn0,
+};
+
+static struct qcom_icc_node * const config_noc_nodes[] = {
+ [MASTER_CNOC_CFG] = &qsm_cfg,
+ [SLAVE_AHB2PHY_SOUTH] = &qhs_ahb2phy0,
+ [SLAVE_AHB2PHY_NORTH] = &qhs_ahb2phy1,
+ [SLAVE_CAMERA_CFG] = &qhs_camera_cfg,
+ [SLAVE_CLK_CTL] = &qhs_clk_ctl,
+ [SLAVE_RBCPR_CX_CFG] = &qhs_cpr_cx,
+ [SLAVE_CPR_HMX] = &qhs_cpr_hmx,
+ [SLAVE_RBCPR_MMCX_CFG] = &qhs_cpr_mmcx,
+ [SLAVE_RBCPR_MXA_CFG] = &qhs_cpr_mxa,
+ [SLAVE_RBCPR_MXC_CFG] = &qhs_cpr_mxc,
+ [SLAVE_CPR_NSPCX] = &qhs_cpr_nspcx,
+ [SLAVE_CRYPTO_0_CFG] = &qhs_crypto0_cfg,
+ [SLAVE_CX_RDPM] = &qhs_cx_rdpm,
+ [SLAVE_DISPLAY_CFG] = &qhs_display_cfg,
+ [SLAVE_GFX3D_CFG] = &qhs_gpuss_cfg,
+ [SLAVE_I2C] = &qhs_i2c,
+ [SLAVE_I3C_IBI0_CFG] = &qhs_i3c_ibi0_cfg,
+ [SLAVE_I3C_IBI1_CFG] = &qhs_i3c_ibi1_cfg,
+ [SLAVE_IMEM_CFG] = &qhs_imem_cfg,
+ [SLAVE_CNOC_MSS] = &qhs_mss_cfg,
+ [SLAVE_MX_2_RDPM] = &qhs_mx_2_rdpm,
+ [SLAVE_MX_RDPM] = &qhs_mx_rdpm,
+ [SLAVE_PCIE_0_CFG] = &qhs_pcie0_cfg,
+ [SLAVE_PCIE_1_CFG] = &qhs_pcie1_cfg,
+ [SLAVE_PCIE_RSCC] = &qhs_pcie_rscc,
+ [SLAVE_PDM] = &qhs_pdm,
+ [SLAVE_PRNG] = &qhs_prng,
+ [SLAVE_QDSS_CFG] = &qhs_qdss_cfg,
+ [SLAVE_QSPI_0] = &qhs_qspi,
+ [SLAVE_QUP_3] = &qhs_qup02,
+ [SLAVE_QUP_1] = &qhs_qup1,
+ [SLAVE_QUP_2] = &qhs_qup2,
+ [SLAVE_SDCC_2] = &qhs_sdc2,
+ [SLAVE_SDCC_4] = &qhs_sdc4,
+ [SLAVE_SPSS_CFG] = &qhs_spss_cfg,
+ [SLAVE_TCSR] = &qhs_tcsr,
+ [SLAVE_TLMM] = &qhs_tlmm,
+ [SLAVE_UFS_MEM_CFG] = &qhs_ufs_mem_cfg,
+ [SLAVE_USB3_0] = &qhs_usb3_0,
+ [SLAVE_VENUS_CFG] = &qhs_venus_cfg,
+ [SLAVE_VSENSE_CTRL_CFG] = &qhs_vsense_ctrl_cfg,
+ [SLAVE_CNOC_MNOC_CFG] = &qss_mnoc_cfg,
+ [SLAVE_NSP_QTB_CFG] = &qss_nsp_qtb_cfg,
+ [SLAVE_PCIE_ANOC_CFG] = &qss_pcie_anoc_cfg,
+ [SLAVE_SERVICE_CNOC_CFG] = &srvc_cnoc_cfg,
+ [SLAVE_QDSS_STM] = &xs_qdss_stm,
+ [SLAVE_TCU] = &xs_sys_tcu_cfg,
+};
+
+static const struct qcom_icc_desc sm8650_config_noc = {
+ .nodes = config_noc_nodes,
+ .num_nodes = ARRAY_SIZE(config_noc_nodes),
+ .bcms = config_noc_bcms,
+ .num_bcms = ARRAY_SIZE(config_noc_bcms),
+};
+
+static struct qcom_icc_bcm * const cnoc_main_bcms[] = {
+ &bcm_cn0,
+};
+
+static struct qcom_icc_node * const cnoc_main_nodes[] = {
+ [MASTER_GEM_NOC_CNOC] = &qnm_gemnoc_cnoc,
+ [MASTER_GEM_NOC_PCIE_SNOC] = &qnm_gemnoc_pcie,
+ [SLAVE_AOSS] = &qhs_aoss,
+ [SLAVE_IPA_CFG] = &qhs_ipa,
+ [SLAVE_IPC_ROUTER_CFG] = &qhs_ipc_router,
+ [SLAVE_TME_CFG] = &qhs_tme_cfg,
+ [SLAVE_APPSS] = &qss_apss,
+ [SLAVE_CNOC_CFG] = &qss_cfg,
+ [SLAVE_DDRSS_CFG] = &qss_ddrss_cfg,
+ [SLAVE_IMEM] = &qxs_imem,
+ [SLAVE_SERVICE_CNOC] = &srvc_cnoc_main,
+ [SLAVE_PCIE_0] = &xs_pcie_0,
+ [SLAVE_PCIE_1] = &xs_pcie_1,
+};
+
+static const struct qcom_icc_desc sm8650_cnoc_main = {
+ .nodes = cnoc_main_nodes,
+ .num_nodes = ARRAY_SIZE(cnoc_main_nodes),
+ .bcms = cnoc_main_bcms,
+ .num_bcms = ARRAY_SIZE(cnoc_main_bcms),
+};
+
+static struct qcom_icc_bcm * const gem_noc_bcms[] = {
+ &bcm_sh0,
+ &bcm_sh1,
+};
+
+static struct qcom_icc_node * const gem_noc_nodes[] = {
+ [MASTER_GPU_TCU] = &alm_gpu_tcu,
+ [MASTER_SYS_TCU] = &alm_sys_tcu,
+ [MASTER_UBWC_P_TCU] = &alm_ubwc_p_tcu,
+ [MASTER_APPSS_PROC] = &chm_apps,
+ [MASTER_GFX3D] = &qnm_gpu,
+ [MASTER_LPASS_GEM_NOC] = &qnm_lpass_gemnoc,
+ [MASTER_MSS_PROC] = &qnm_mdsp,
+ [MASTER_MNOC_HF_MEM_NOC] = &qnm_mnoc_hf,
+ [MASTER_MNOC_SF_MEM_NOC] = &qnm_mnoc_sf,
+ [MASTER_COMPUTE_NOC] = &qnm_nsp_gemnoc,
+ [MASTER_ANOC_PCIE_GEM_NOC] = &qnm_pcie,
+ [MASTER_SNOC_SF_MEM_NOC] = &qnm_snoc_sf,
+ [MASTER_UBWC_P] = &qnm_ubwc_p,
+ [MASTER_GIC] = &xm_gic,
+ [SLAVE_GEM_NOC_CNOC] = &qns_gem_noc_cnoc,
+ [SLAVE_LLCC] = &qns_llcc,
+ [SLAVE_MEM_NOC_PCIE_SNOC] = &qns_pcie,
+};
+
+static const struct qcom_icc_desc sm8650_gem_noc = {
+ .nodes = gem_noc_nodes,
+ .num_nodes = ARRAY_SIZE(gem_noc_nodes),
+ .bcms = gem_noc_bcms,
+ .num_bcms = ARRAY_SIZE(gem_noc_bcms),
+};
+
+static struct qcom_icc_node * const lpass_ag_noc_nodes[] = {
+ [MASTER_LPIAON_NOC] = &qnm_lpiaon_noc,
+ [SLAVE_LPASS_GEM_NOC] = &qns_lpass_ag_noc_gemnoc,
+};
+
+static const struct qcom_icc_desc sm8650_lpass_ag_noc = {
+ .nodes = lpass_ag_noc_nodes,
+ .num_nodes = ARRAY_SIZE(lpass_ag_noc_nodes),
+};
+
+static struct qcom_icc_bcm * const lpass_lpiaon_noc_bcms[] = {
+ &bcm_lp0,
+};
+
+static struct qcom_icc_node * const lpass_lpiaon_noc_nodes[] = {
+ [MASTER_LPASS_LPINOC] = &qnm_lpass_lpinoc,
+ [SLAVE_LPIAON_NOC_LPASS_AG_NOC] = &qns_lpass_aggnoc,
+};
+
+static const struct qcom_icc_desc sm8650_lpass_lpiaon_noc = {
+ .nodes = lpass_lpiaon_noc_nodes,
+ .num_nodes = ARRAY_SIZE(lpass_lpiaon_noc_nodes),
+ .bcms = lpass_lpiaon_noc_bcms,
+ .num_bcms = ARRAY_SIZE(lpass_lpiaon_noc_bcms),
+};
+
+static struct qcom_icc_node * const lpass_lpicx_noc_nodes[] = {
+ [MASTER_LPASS_PROC] = &qxm_lpinoc_dsp_axim,
+ [SLAVE_LPICX_NOC_LPIAON_NOC] = &qns_lpi_aon_noc,
+};
+
+static const struct qcom_icc_desc sm8650_lpass_lpicx_noc = {
+ .nodes = lpass_lpicx_noc_nodes,
+ .num_nodes = ARRAY_SIZE(lpass_lpicx_noc_nodes),
+};
+
+static struct qcom_icc_bcm * const mc_virt_bcms[] = {
+ &bcm_acv,
+ &bcm_mc0,
+};
+
+static struct qcom_icc_node * const mc_virt_nodes[] = {
+ [MASTER_LLCC] = &llcc_mc,
+ [SLAVE_EBI1] = &ebi,
+};
+
+static const struct qcom_icc_desc sm8650_mc_virt = {
+ .nodes = mc_virt_nodes,
+ .num_nodes = ARRAY_SIZE(mc_virt_nodes),
+ .bcms = mc_virt_bcms,
+ .num_bcms = ARRAY_SIZE(mc_virt_bcms),
+};
+
+static struct qcom_icc_bcm * const mmss_noc_bcms[] = {
+ &bcm_mm0,
+ &bcm_mm1,
+};
+
+static struct qcom_icc_node * const mmss_noc_nodes[] = {
+ [MASTER_CAMNOC_HF] = &qnm_camnoc_hf,
+ [MASTER_CAMNOC_ICP] = &qnm_camnoc_icp,
+ [MASTER_CAMNOC_SF] = &qnm_camnoc_sf,
+ [MASTER_MDP] = &qnm_mdp,
+ [MASTER_CDSP_HCP] = &qnm_vapss_hcp,
+ [MASTER_VIDEO] = &qnm_video,
+ [MASTER_VIDEO_CV_PROC] = &qnm_video_cv_cpu,
+ [MASTER_VIDEO_PROC] = &qnm_video_cvp,
+ [MASTER_VIDEO_V_PROC] = &qnm_video_v_cpu,
+ [MASTER_CNOC_MNOC_CFG] = &qsm_mnoc_cfg,
+ [SLAVE_MNOC_HF_MEM_NOC] = &qns_mem_noc_hf,
+ [SLAVE_MNOC_SF_MEM_NOC] = &qns_mem_noc_sf,
+ [SLAVE_SERVICE_MNOC] = &srvc_mnoc,
+};
+
+static const struct qcom_icc_desc sm8650_mmss_noc = {
+ .nodes = mmss_noc_nodes,
+ .num_nodes = ARRAY_SIZE(mmss_noc_nodes),
+ .bcms = mmss_noc_bcms,
+ .num_bcms = ARRAY_SIZE(mmss_noc_bcms),
+};
+
+static struct qcom_icc_bcm * const nsp_noc_bcms[] = {
+ &bcm_co0,
+};
+
+static struct qcom_icc_node * const nsp_noc_nodes[] = {
+ [MASTER_CDSP_PROC] = &qnm_nsp,
+ [SLAVE_CDSP_MEM_NOC] = &qns_nsp_gemnoc,
+};
+
+static const struct qcom_icc_desc sm8650_nsp_noc = {
+ .nodes = nsp_noc_nodes,
+ .num_nodes = ARRAY_SIZE(nsp_noc_nodes),
+ .bcms = nsp_noc_bcms,
+ .num_bcms = ARRAY_SIZE(nsp_noc_bcms),
+};
+
+static struct qcom_icc_bcm * const pcie_anoc_bcms[] = {
+ &bcm_sn4,
+};
+
+static struct qcom_icc_node * const pcie_anoc_nodes[] = {
+ [MASTER_PCIE_ANOC_CFG] = &qsm_pcie_anoc_cfg,
+ [MASTER_PCIE_0] = &xm_pcie3_0,
+ [MASTER_PCIE_1] = &xm_pcie3_1,
+ [SLAVE_ANOC_PCIE_GEM_NOC] = &qns_pcie_mem_noc,
+ [SLAVE_SERVICE_PCIE_ANOC] = &srvc_pcie_aggre_noc,
+};
+
+static const struct qcom_icc_desc sm8650_pcie_anoc = {
+ .nodes = pcie_anoc_nodes,
+ .num_nodes = ARRAY_SIZE(pcie_anoc_nodes),
+ .bcms = pcie_anoc_bcms,
+ .num_bcms = ARRAY_SIZE(pcie_anoc_bcms),
+};
+
+static struct qcom_icc_bcm * const system_noc_bcms[] = {
+ &bcm_sn0,
+ &bcm_sn2,
+ &bcm_sn3,
+};
+
+static struct qcom_icc_node * const system_noc_nodes[] = {
+ [MASTER_A1NOC_SNOC] = &qnm_aggre1_noc,
+ [MASTER_A2NOC_SNOC] = &qnm_aggre2_noc,
+ [SLAVE_SNOC_GEM_NOC_SF] = &qns_gemnoc_sf,
+ [MASTER_APSS_NOC] = &qnm_apss_noc,
+};
+
+static const struct qcom_icc_desc sm8650_system_noc = {
+ .nodes = system_noc_nodes,
+ .num_nodes = ARRAY_SIZE(system_noc_nodes),
+ .bcms = system_noc_bcms,
+ .num_bcms = ARRAY_SIZE(system_noc_bcms),
+};
+
+static const struct udevice_id qnoc_of_match[] = {
+ { .compatible = "qcom,sm8650-aggre1-noc", .data = (ulong)&sm8650_aggre1_noc },
+ { .compatible = "qcom,sm8650-aggre2-noc", .data = (ulong)&sm8650_aggre2_noc },
+ { .compatible = "qcom,sm8650-clk-virt", .data = (ulong)&sm8650_clk_virt },
+ { .compatible = "qcom,sm8650-config-noc", .data = (ulong)&sm8650_config_noc },
+ { .compatible = "qcom,sm8650-cnoc-main", .data = (ulong)&sm8650_cnoc_main },
+ { .compatible = "qcom,sm8650-gem-noc", .data = (ulong)&sm8650_gem_noc },
+ { .compatible = "qcom,sm8650-lpass-ag-noc", .data = (ulong)&sm8650_lpass_ag_noc },
+ { .compatible = "qcom,sm8650-lpass-lpiaon-noc", .data = (ulong)&sm8650_lpass_lpiaon_noc },
+ { .compatible = "qcom,sm8650-lpass-lpicx-noc", .data = (ulong)&sm8650_lpass_lpicx_noc },
+ { .compatible = "qcom,sm8650-mc-virt", .data = (ulong)&sm8650_mc_virt },
+ { .compatible = "qcom,sm8650-mmss-noc", .data = (ulong)&sm8650_mmss_noc },
+ { .compatible = "qcom,sm8650-nsp-noc", .data = (ulong)&sm8650_nsp_noc },
+ { .compatible = "qcom,sm8650-pcie-anoc", .data = (ulong)&sm8650_pcie_anoc },
+ { .compatible = "qcom,sm8650-system-noc", .data = (ulong)&sm8650_system_noc },
+ { }
+};
+
+U_BOOT_DRIVER(qnoc_sm8650) = {
+ .name = "qnoc-sm8650",
+ .id = UCLASS_INTERCONNECT,
+ .of_match = qnoc_of_match,
+ .probe = qcom_icc_rpmh_probe,
+ .bind = qcom_icc_rpmh_bind,
+ .unbind = qcom_icc_rpmh_unbind,
+ .ops = &qcom_icc_rpmh_ops,
+ .plat_auto = sizeof(struct qcom_icc_provider),
+};
diff --git a/drivers/interconnect/qcom/sm8650.h b/drivers/interconnect/qcom/sm8650.h
new file mode 100644
index 00000000000..b6610225b38
--- /dev/null
+++ b/drivers/interconnect/qcom/sm8650.h
@@ -0,0 +1,144 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * SM8650 interconnect IDs
+ *
+ * Copyright (c) 2020-2021, The Linux Foundation. All rights reserved.
+ * Copyright (c) 2023, Linaro Limited
+ */
+
+#ifndef __DRIVERS_INTERCONNECT_QCOM_SM8650_H
+#define __DRIVERS_INTERCONNECT_QCOM_SM8650_H
+
+#define SM8650_MASTER_A1NOC_SNOC 0
+#define SM8650_MASTER_A2NOC_SNOC 1
+#define SM8650_MASTER_ANOC_PCIE_GEM_NOC 2
+#define SM8650_MASTER_APPSS_PROC 3
+#define SM8650_MASTER_CAMNOC_HF 4
+#define SM8650_MASTER_CAMNOC_ICP 5
+#define SM8650_MASTER_CAMNOC_SF 6
+#define SM8650_MASTER_CDSP_HCP 7
+#define SM8650_MASTER_CDSP_PROC 8
+#define SM8650_MASTER_CNOC_CFG 9
+#define SM8650_MASTER_CNOC_MNOC_CFG 10
+#define SM8650_MASTER_COMPUTE_NOC 11
+#define SM8650_MASTER_CRYPTO 12
+#define SM8650_MASTER_GEM_NOC_CNOC 13
+#define SM8650_MASTER_GEM_NOC_PCIE_SNOC 14
+#define SM8650_MASTER_GFX3D 15
+#define SM8650_MASTER_GIC 16
+#define SM8650_MASTER_GPU_TCU 17
+#define SM8650_MASTER_IPA 18
+#define SM8650_MASTER_LLCC 19
+#define SM8650_MASTER_LPASS_GEM_NOC 20
+#define SM8650_MASTER_LPASS_LPINOC 21
+#define SM8650_MASTER_LPASS_PROC 22
+#define SM8650_MASTER_LPIAON_NOC 23
+#define SM8650_MASTER_MDP 24
+#define SM8650_MASTER_MNOC_HF_MEM_NOC 25
+#define SM8650_MASTER_MNOC_SF_MEM_NOC 26
+#define SM8650_MASTER_MSS_PROC 27
+#define SM8650_MASTER_PCIE_0 28
+#define SM8650_MASTER_PCIE_1 29
+#define SM8650_MASTER_PCIE_ANOC_CFG 30
+#define SM8650_MASTER_QDSS_BAM 31
+#define SM8650_MASTER_QDSS_ETR 32
+#define SM8650_MASTER_QDSS_ETR_1 33
+#define SM8650_MASTER_QSPI_0 34
+#define SM8650_MASTER_QUP_1 35
+#define SM8650_MASTER_QUP_2 36
+#define SM8650_MASTER_QUP_3 37
+#define SM8650_MASTER_QUP_CORE_0 38
+#define SM8650_MASTER_QUP_CORE_1 39
+#define SM8650_MASTER_QUP_CORE_2 40
+#define SM8650_MASTER_SDCC_2 41
+#define SM8650_MASTER_SDCC_4 42
+#define SM8650_MASTER_SNOC_SF_MEM_NOC 43
+#define SM8650_MASTER_SP 44
+#define SM8650_MASTER_SYS_TCU 45
+#define SM8650_MASTER_UBWC_P 46
+#define SM8650_MASTER_UBWC_P_TCU 47
+#define SM8650_MASTER_UFS_MEM 48
+#define SM8650_MASTER_USB3_0 49
+#define SM8650_MASTER_VIDEO 50
+#define SM8650_MASTER_VIDEO_CV_PROC 51
+#define SM8650_MASTER_VIDEO_PROC 52
+#define SM8650_MASTER_VIDEO_V_PROC 53
+#define SM8650_SLAVE_A1NOC_SNOC 54
+#define SM8650_SLAVE_A2NOC_SNOC 55
+#define SM8650_SLAVE_AHB2PHY_NORTH 56
+#define SM8650_SLAVE_AHB2PHY_SOUTH 57
+#define SM8650_SLAVE_ANOC_PCIE_GEM_NOC 58
+#define SM8650_SLAVE_AOSS 59
+#define SM8650_SLAVE_APPSS 60
+#define SM8650_SLAVE_CAMERA_CFG 61
+#define SM8650_SLAVE_CDSP_MEM_NOC 62
+#define SM8650_SLAVE_CLK_CTL 63
+#define SM8650_SLAVE_CNOC_CFG 64
+#define SM8650_SLAVE_CNOC_MNOC_CFG 65
+#define SM8650_SLAVE_CNOC_MSS 66
+#define SM8650_SLAVE_CPR_HMX 67
+#define SM8650_SLAVE_CPR_NSPCX 68
+#define SM8650_SLAVE_CRYPTO_0_CFG 69
+#define SM8650_SLAVE_CX_RDPM 70
+#define SM8650_SLAVE_DDRSS_CFG 71
+#define SM8650_SLAVE_DISPLAY_CFG 72
+#define SM8650_SLAVE_EBI1 73
+#define SM8650_SLAVE_GEM_NOC_CNOC 74
+#define SM8650_SLAVE_GFX3D_CFG 75
+#define SM8650_SLAVE_I2C 76
+#define SM8650_SLAVE_I3C_IBI0_CFG 77
+#define SM8650_SLAVE_I3C_IBI1_CFG 78
+#define SM8650_SLAVE_IMEM 79
+#define SM8650_SLAVE_IMEM_CFG 80
+#define SM8650_SLAVE_IPA_CFG 81
+#define SM8650_SLAVE_IPC_ROUTER_CFG 82
+#define SM8650_SLAVE_LLCC 83
+#define SM8650_SLAVE_LPASS_GEM_NOC 84
+#define SM8650_SLAVE_LPIAON_NOC_LPASS_AG_NOC 85
+#define SM8650_SLAVE_LPICX_NOC_LPIAON_NOC 86
+#define SM8650_SLAVE_MEM_NOC_PCIE_SNOC 87
+#define SM8650_SLAVE_MNOC_HF_MEM_NOC 88
+#define SM8650_SLAVE_MNOC_SF_MEM_NOC 89
+#define SM8650_SLAVE_MX_2_RDPM 90
+#define SM8650_SLAVE_MX_RDPM 91
+#define SM8650_SLAVE_NSP_QTB_CFG 92
+#define SM8650_SLAVE_PCIE_0 93
+#define SM8650_SLAVE_PCIE_1 94
+#define SM8650_SLAVE_PCIE_0_CFG 95
+#define SM8650_SLAVE_PCIE_1_CFG 96
+#define SM8650_SLAVE_PCIE_ANOC_CFG 97
+#define SM8650_SLAVE_PCIE_RSCC 98
+#define SM8650_SLAVE_PDM 99
+#define SM8650_SLAVE_PRNG 100
+#define SM8650_SLAVE_QDSS_CFG 101
+#define SM8650_SLAVE_QDSS_STM 102
+#define SM8650_SLAVE_QSPI_0 103
+#define SM8650_SLAVE_QUP_1 104
+#define SM8650_SLAVE_QUP_2 105
+#define SM8650_SLAVE_QUP_3 106
+#define SM8650_SLAVE_QUP_CORE_0 107
+#define SM8650_SLAVE_QUP_CORE_1 108
+#define SM8650_SLAVE_QUP_CORE_2 109
+#define SM8650_SLAVE_RBCPR_CX_CFG 110
+#define SM8650_SLAVE_RBCPR_MMCX_CFG 111
+#define SM8650_SLAVE_RBCPR_MXA_CFG 112
+#define SM8650_SLAVE_RBCPR_MXC_CFG 113
+#define SM8650_SLAVE_SDCC_2 114
+#define SM8650_SLAVE_SDCC_4 115
+#define SM8650_SLAVE_SERVICE_CNOC 116
+#define SM8650_SLAVE_SERVICE_CNOC_CFG 117
+#define SM8650_SLAVE_SERVICE_MNOC 118
+#define SM8650_SLAVE_SERVICE_PCIE_ANOC 119
+#define SM8650_SLAVE_SNOC_GEM_NOC_SF 120
+#define SM8650_SLAVE_SPSS_CFG 121
+#define SM8650_SLAVE_TCSR 122
+#define SM8650_SLAVE_TCU 123
+#define SM8650_SLAVE_TLMM 124
+#define SM8650_SLAVE_TME_CFG 125
+#define SM8650_SLAVE_UFS_MEM_CFG 126
+#define SM8650_SLAVE_USB3_0 127
+#define SM8650_SLAVE_VENUS_CFG 128
+#define SM8650_SLAVE_VSENSE_CTRL_CFG 129
+#define SM8650_MASTER_APSS_NOC 130
+
+#endif
diff --git a/drivers/interconnect/sandbox-interconnect-test.c b/drivers/interconnect/sandbox-interconnect-test.c
new file mode 100644
index 00000000000..ff5f327f6da
--- /dev/null
+++ b/drivers/interconnect/sandbox-interconnect-test.c
@@ -0,0 +1,89 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2025 Linaro Limited
+ */
+
+#include <dm.h>
+#include <malloc.h>
+#include <interconnect.h>
+#include <asm/io.h>
+#include <linux/err.h>
+
+struct sandbox_interconnect_test {
+ struct icc_path *path;
+};
+
+int sandbox_interconnect_test_get(struct udevice *dev, char *name)
+{
+ struct sandbox_interconnect_test *priv = dev_get_priv(dev);
+
+ priv->path = of_icc_get(dev, name);
+ if (IS_ERR(priv->path))
+ return PTR_ERR(priv->path);
+
+ if (!priv->path)
+ return -ENOSYS;
+
+ return 0;
+}
+
+int sandbox_interconnect_test_get_index(struct udevice *dev, int index)
+{
+ struct sandbox_interconnect_test *priv = dev_get_priv(dev);
+
+ priv->path = of_icc_get_by_index(dev, index);
+ if (IS_ERR(priv->path))
+ return PTR_ERR(priv->path);
+
+ if (!priv->path)
+ return -ENOSYS;
+
+ return 0;
+}
+
+int sandbox_interconnect_test_enable(struct udevice *dev)
+{
+ struct sandbox_interconnect_test *priv = dev_get_priv(dev);
+
+ return icc_enable(priv->path);
+}
+
+int sandbox_interconnect_test_disable(struct udevice *dev)
+{
+ struct sandbox_interconnect_test *priv = dev_get_priv(dev);
+
+ return icc_disable(priv->path);
+}
+
+int sandbox_interconnect_test_set_bw(struct udevice *dev, u32 avg_bw, u32 peak_bw)
+{
+ struct sandbox_interconnect_test *priv = dev_get_priv(dev);
+
+ return icc_set_bw(priv->path, avg_bw, peak_bw);
+}
+
+int sandbox_interconnect_test_put(struct udevice *dev)
+{
+ struct sandbox_interconnect_test *priv = dev_get_priv(dev);
+ int ret;
+
+ ret = icc_put(priv->path);
+ if (ret)
+ return ret;
+
+ priv->path = NULL;
+
+ return 0;
+}
+
+static const struct udevice_id sandbox_interconnect_test_ids[] = {
+ { .compatible = "sandbox,interconnect-test" },
+ { }
+};
+
+U_BOOT_DRIVER(sandbox_interconnect_test) = {
+ .name = "sandbox_interconnect_test",
+ .id = UCLASS_MISC,
+ .of_match = sandbox_interconnect_test_ids,
+ .priv_auto = sizeof(struct sandbox_interconnect_test),
+};
diff --git a/drivers/interconnect/sandbox-interconnect.c b/drivers/interconnect/sandbox-interconnect.c
new file mode 100644
index 00000000000..0fc0e3e0d49
--- /dev/null
+++ b/drivers/interconnect/sandbox-interconnect.c
@@ -0,0 +1,303 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2025 Linaro Limited
+ */
+
+#include <dm.h>
+#include <log.h>
+#include <malloc.h>
+#include <interconnect-uclass.h>
+#include <asm/io.h>
+#include <interconnect.h>
+#include <linux/err.h>
+
+#define MAX_LINKS 2
+
+struct sandbox_interconnect_node {
+ const char *name;
+ unsigned int num_links;
+ struct sandbox_interconnect_node *links[MAX_LINKS];
+ u64 avg_bw;
+ u64 peak_bw;
+};
+
+struct sandbox_interconnect_data {
+ struct sandbox_interconnect_node **nodes;
+ const unsigned int num_nodes;
+};
+
+struct sandbox_interconnect_provider {
+ struct udevice *dev;
+ struct sandbox_interconnect_data *data;
+ u64 avg;
+ u64 peak;
+};
+
+/*
+ * Node graph:
+ * ______________________________
+ * [ NODE0 ]--\ / \ /-->[ NODE3 ]
+ * |-->| NODE2_SLAVE --> NODE2_MASTER |--|
+ * [ NODE1 ]--/ \______________________________/ \-->[ NODE4 ]
+ *
+ */
+
+static struct sandbox_interconnect_node node2_slave;
+static struct sandbox_interconnect_node node2_master;
+static struct sandbox_interconnect_node node3;
+static struct sandbox_interconnect_node node4;
+
+static struct sandbox_interconnect_node node0 = {
+ .name = "node0",
+ .num_links = 1,
+ .links = { &node2_slave },
+};
+
+static struct sandbox_interconnect_node node1 = {
+ .name = "node1",
+ .num_links = 1,
+ .links = { &node2_slave },
+};
+
+static struct sandbox_interconnect_node node2_slave = {
+ .name = "node2_slave",
+ .num_links = 1,
+ .links = { &node2_master },
+};
+
+static struct sandbox_interconnect_node node2_master = {
+ .name = "node2_master",
+ .num_links = 2,
+ .links = { &node3, &node4 },
+};
+
+static struct sandbox_interconnect_node node3 = {
+ .name = "node3",
+};
+
+static struct sandbox_interconnect_node node4 = {
+ .name = "node4",
+};
+
+/* xlate mapping */
+static struct sandbox_interconnect_node *interconnect0_nodes[] = {
+ [0] = &node0,
+};
+
+static struct sandbox_interconnect_node *interconnect1_nodes[] = {
+ [0] = &node1,
+};
+
+static struct sandbox_interconnect_node *interconnect2_nodes[] = {
+ [0] = &node2_slave,
+ [1] = &node2_master,
+};
+
+static struct sandbox_interconnect_node *interconnect3_nodes[] = {
+ [0] = &node3,
+};
+
+static struct sandbox_interconnect_node *interconnect4_nodes[] = {
+ [0] = &node4,
+};
+
+static struct sandbox_interconnect_data interconnect0_data = {
+ .nodes = interconnect0_nodes,
+ .num_nodes = ARRAY_SIZE(interconnect0_nodes),
+};
+
+static struct sandbox_interconnect_data interconnect1_data = {
+ .nodes = interconnect1_nodes,
+ .num_nodes = ARRAY_SIZE(interconnect1_nodes),
+};
+
+static struct sandbox_interconnect_data interconnect2_data = {
+ .nodes = interconnect2_nodes,
+ .num_nodes = ARRAY_SIZE(interconnect2_nodes),
+};
+
+static struct sandbox_interconnect_data interconnect3_data = {
+ .nodes = interconnect3_nodes,
+ .num_nodes = ARRAY_SIZE(interconnect3_nodes),
+};
+
+static struct sandbox_interconnect_data interconnect4_data = {
+ .nodes = interconnect4_nodes,
+ .num_nodes = ARRAY_SIZE(interconnect4_nodes),
+};
+
+int sandbox_interconnect_get_bw(struct udevice *dev, u64 *avg, u64 *peak)
+{
+ struct sandbox_interconnect_provider *priv = dev_get_plat(dev);
+
+ *avg = priv->avg;
+ *peak = priv->peak;
+
+ return 0;
+}
+
+static int sandbox_interconnect_links_aggregate(struct udevice *dev)
+{
+ struct sandbox_interconnect_provider *priv = dev_get_plat(dev);
+ u64 avg = 0, peak = 0;
+ int i;
+
+ debug("(provider=%s)\n", dev->name);
+
+ for (i = 0; i < priv->data->num_nodes; i++) {
+ struct sandbox_interconnect_node *sandbox_node = priv->data->nodes[i];
+
+ if (!sandbox_node)
+ continue;
+
+ avg += sandbox_node->avg_bw;
+ peak = max_t(u32, sandbox_node->peak_bw, peak);
+ }
+
+ priv->avg = avg / priv->data->num_nodes;
+ priv->peak = peak;
+
+ debug("(provider=%s,avg=%llu peak=%llu)\n",
+ dev->name, priv->avg, priv->peak);
+
+ return 0;
+}
+
+static int sandbox_interconnect_set(struct icc_node *src, struct icc_node *dst)
+{
+ struct icc_node *node;
+
+ debug("(src=%s,dst=%s)\n", src->dev->name, dst->dev->name);
+
+ if (!src)
+ node = dst;
+ else
+ node = src;
+
+ return sandbox_interconnect_links_aggregate(node->dev->parent);
+}
+
+static int sandbox_interconnect_aggregate(struct icc_node *node, u32 tag, u32 avg_bw,
+ u32 peak_bw, u32 *agg_avg, u32 *agg_peak)
+{
+ struct sandbox_interconnect_node *sandbox_node = node->data;
+
+ debug("(node=%s,tag=%d,avg=%u,peak=%u)\n",
+ node->dev->name, tag, avg_bw, peak_bw);
+
+ sandbox_node->avg_bw += avg_bw;
+ sandbox_node->peak_bw = max_t(u32, sandbox_node->peak_bw, peak_bw);
+
+ *agg_avg += avg_bw;
+ *agg_peak = max_t(u32, *agg_peak, peak_bw);
+
+ debug("(node=%s,new avg=%llu,new peak=%llu)\n",
+ node->dev->name, sandbox_node->avg_bw, sandbox_node->peak_bw);
+
+ return 0;
+}
+
+static void sandbox_interconnect_pre_aggregate(struct icc_node *node)
+{
+ struct sandbox_interconnect_node *sandbox_node = node->data;
+
+ debug("(node=%s)\n", node->dev->name);
+
+ sandbox_node->avg_bw = 0;
+ sandbox_node->peak_bw = 0;
+}
+
+static struct icc_node *sandbox_interconnect_xlate(struct udevice *dev,
+ const struct ofnode_phandle_args *spec)
+{
+ struct icc_provider *plat = dev_get_uclass_plat(dev);
+ unsigned int idx = spec->args[0];
+
+ debug("(dev=%s)\n", dev->name);
+
+ if (idx >= plat->xlate_num_nodes) {
+ pr_err("%s: invalid index %u\n", __func__, idx);
+ return ERR_PTR(-EINVAL);
+ }
+
+ return plat->xlate_nodes[idx];
+}
+
+static int sandbox_interconnect_bind(struct udevice *dev)
+{
+ struct sandbox_interconnect_provider *priv = dev_get_plat(dev);
+ struct icc_provider *plat = dev_get_uclass_plat(dev);
+ size_t i;
+
+ debug("(dev=%s)\n", dev->name);
+
+ priv->data = (struct sandbox_interconnect_data *)dev_get_driver_data(dev);
+ if (!priv->data)
+ return -EINVAL;
+
+ plat->xlate_num_nodes = priv->data->num_nodes;
+ plat->xlate_nodes = calloc(sizeof(struct icc_node *), priv->data->num_nodes);
+ if (!plat->xlate_nodes)
+ return -ENOMEM;
+
+ priv->dev = dev;
+
+ for (i = 0; i < priv->data->num_nodes; i++) {
+ struct sandbox_interconnect_node *sandbox_node;
+ struct icc_node *node;
+ int j;
+
+ sandbox_node = priv->data->nodes[i];
+ if (!sandbox_node)
+ continue;
+
+ node = icc_node_create(dev, (ulong)sandbox_node,
+ sandbox_node->name);
+ if (IS_ERR(node))
+ return PTR_ERR(node);
+
+ node->data = sandbox_node;
+
+ for (j = 0; j < sandbox_node->num_links; ++j)
+ icc_link_create(node, (ulong)sandbox_node->links[j]);
+
+ plat->xlate_nodes[i] = node;
+ }
+
+ return 0;
+}
+
+static int sandbox_interconnect_unbind(struct udevice *dev)
+{
+ struct icc_provider *plat = dev_get_uclass_plat(dev);
+
+ free(plat->xlate_nodes);
+
+ return 0;
+}
+
+static const struct udevice_id sandbox_interconnect_ids[] = {
+ { .compatible = "sandbox,interconnect0", .data = (ulong)&interconnect0_data, },
+ { .compatible = "sandbox,interconnect1", .data = (ulong)&interconnect1_data, },
+ { .compatible = "sandbox,interconnect2", .data = (ulong)&interconnect2_data, },
+ { .compatible = "sandbox,interconnect3", .data = (ulong)&interconnect3_data, },
+ { .compatible = "sandbox,interconnect4", .data = (ulong)&interconnect4_data, },
+ { }
+};
+
+static struct interconnect_ops sandbox_interconnect_ops = {
+ .of_xlate = sandbox_interconnect_xlate,
+ .set = sandbox_interconnect_set,
+ .pre_aggregate = sandbox_interconnect_pre_aggregate,
+ .aggregate = sandbox_interconnect_aggregate,
+};
+
+U_BOOT_DRIVER(sandbox_interconnect) = {
+ .name = "sandbox_interconnect",
+ .id = UCLASS_INTERCONNECT,
+ .of_match = sandbox_interconnect_ids,
+ .bind = sandbox_interconnect_bind,
+ .unbind = sandbox_interconnect_unbind,
+ .plat_auto = sizeof(struct sandbox_interconnect_provider),
+ .ops = &sandbox_interconnect_ops,
+};
diff --git a/drivers/soc/qcom/cmd-db.c b/drivers/soc/qcom/cmd-db.c
index 67be18e89f4..d0a6047b8a6 100644
--- a/drivers/soc/qcom/cmd-db.c
+++ b/drivers/soc/qcom/cmd-db.c
@@ -183,6 +183,51 @@ u32 cmd_db_read_addr(const char *id)
}
EXPORT_SYMBOL_GPL(cmd_db_read_addr);
+/**
+ * cmd_db_read_slave_id - Get the slave ID for a given resource address
+ *
+ * @id: Resource id to query the DB for version
+ *
+ * Return: cmd_db_hw_type enum on success, CMD_DB_HW_INVALID on error
+ */
+enum cmd_db_hw_type cmd_db_read_slave_id(const char *id)
+{
+ int ret;
+ const struct entry_header *ent;
+ u32 addr;
+
+ ret = cmd_db_get_header(id, &ent, NULL);
+ if (ret < 0)
+ return CMD_DB_HW_INVALID;
+
+ addr = le32_to_cpu(ent->addr);
+ return (addr >> SLAVE_ID_SHIFT) & SLAVE_ID_MASK;
+}
+
+/**
+ * cmd_db_read_aux_data() - Query command db for aux data.
+ *
+ * @id: Resource to retrieve AUX Data on
+ * @len: size of data buffer returned
+ *
+ * Return: pointer to data on success, error pointer otherwise
+ */
+const void *cmd_db_read_aux_data(const char *id, size_t *len)
+{
+ int ret;
+ const struct entry_header *ent;
+ const struct rsc_hdr *rsc_hdr;
+
+ ret = cmd_db_get_header(id, &ent, &rsc_hdr);
+ if (ret)
+ return ERR_PTR(ret);
+
+ if (len)
+ *len = le16_to_cpu(ent->len);
+
+ return rsc_offset(rsc_hdr, ent);
+}
+
static int cmd_db_bind(struct udevice *dev)
{
void __iomem *base;
diff --git a/drivers/ufs/ufs-qcom.c b/drivers/ufs/ufs-qcom.c
index 9f0a6940d98..ee43958d5d8 100644
--- a/drivers/ufs/ufs-qcom.c
+++ b/drivers/ufs/ufs-qcom.c
@@ -16,6 +16,7 @@
#include <generic-phy.h>
#include <ufs.h>
#include <asm/gpio.h>
+#include <interconnect.h>
#include <linux/bitops.h>
#include <linux/delay.h>
@@ -26,6 +27,9 @@
#define ceil(freq, div) ((freq) % (div) == 0 ? ((freq) / (div)) : ((freq) / (div) + 1))
+#define UFS_DDR_MAX_BANDWIDTH 7643136
+#define UFS_CPU_MAX_BANDWIDTH 819200
+
static void ufs_qcom_dev_ref_clk_ctrl(struct ufs_hba *hba, bool enable);
static int ufs_qcom_enable_clks(struct ufs_qcom_priv *priv)
@@ -625,8 +629,17 @@ static struct ufs_hba_ops ufs_qcom_hba_ops = {
static int ufs_qcom_probe(struct udevice *dev)
{
struct ufs_qcom_priv *priv = dev_get_priv(dev);
+ struct icc_path *path;
int ret;
+ path = of_icc_get(dev, "ufs-ddr");
+ if (!IS_ERR(path))
+ icc_set_bw(path, 0, UFS_DDR_MAX_BANDWIDTH);
+
+ path = of_icc_get(dev, "cpu-ufs");
+ if (!IS_ERR(path))
+ icc_set_bw(path, 0, UFS_CPU_MAX_BANDWIDTH);
+
/* get resets */
ret = reset_get_by_name(dev, "rst", &priv->core_reset);
if (ret) {