U-Boot Archive on lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH v5 0/7] Implement the Generic System Interconnect Subsystem for U-Boot
@ 2025-11-20  8:12 Neil Armstrong
  2025-11-20  8:12 ` [PATCH v5 1/7] Introduce the Generic System Interconnect Subsystem Neil Armstrong
                   ` (7 more replies)
  0 siblings, 8 replies; 9+ messages in thread
From: Neil Armstrong @ 2025-11-20  8:12 UTC (permalink / raw)
  To: u-boot, Tom Rini, Simon Glass, Casey Connolly
  Cc: u-boot-qcom, Ilias Apalodimas, Jerome Forissier, Dinesh Maniyam,
	Sughosh Ganu, Mattijs Korpershoek, Hrushikesh Salunke,
	Anurag Dutta, Svyatoslav Ryhel, Ion Agorria,
	Kory Maincent (TI.com), Mario Six, Christian Marangi,
	Heiko Schocher, Guillaume La Roque, Miquel Raynal, Peng Fan,
	Michal Simek, Heinrich Schuchardt, Pieter Van Trappen,
	Alexander Graf, Quentin Schulz, Rasmus Villemoes, Greg Malysa,
	Arturs Artamonovs, Vasileios Bimpikas, Utsav Agarwal, Ian Roberts,
	Nathan Barrett-Morrison, Alif Zakuan Yuslaimi, Stefan Roese,
	Sumit Garg, Bhupesh Sharma, Neha Malcom Francis, Marek Vasut,
	Varadarajan Narayanan, Sam Day, Sam Protsenko, Marek Vasut,
	Alper Nebi Yasak, Oliver Gaskell, Paul Sajna, Neil Armstrong

Let's introduce the Generic System Interconnect subsystem based on
the counterpart Linux framework which is used to vote for bandwidth
across multiple SoC busses.

Documentation for the Linux Generic System Interconnect Subsystem can
be found at [1].

Each bus endpoints are materialised as "nodes" which are linked together,
and the DT will specify a pair of nodes to enable and set a bandwidth
on the route between those endpoints.

The hardware resources that provide those nodes and provides the way
to vote for the bandwidth are called "providers".

The Interconnect uclass code is heavily based on the Linux one, with
some small differences:
- nodes are allocated as udevices instead of Linux idr_alloc()
- tag management is minimal, only normal xlate is supported
- getting nodes states at probe is not implemented
- providers are probed on demand while the nodes links are traversed
- nodes are populated on bind
- id management is simplified, static IDs and dynamics IDs can be used
- identical consume API as Linux, only implementation differs

DM Testsuite is provided, testing all the API and nodes lifetime.

A first implementation for Qualcomm SoCs using the RPMh subsystem
is added, bandwidth vote is required to have full bandwidth for
storage devices and enabling some subsystem like Display to access
the DDR memory.

[1] https://docs.kernel.org/driver-api/interconnect.html

Qcom implementation depends on:
- https://lore.kernel.org/all/20250711143851.1490617-2-aswin.murugan@oss.qualcomm.com/

Signed-off-by: Neil Armstrong <neil.armstrong@linaro.org>
---
Changes in v5:
- Fixed memory allocation issues fixing sandbox tests on CI
  - req allocations were not zero-ed, leading to funky avg values
  - xlate nodes mapping tables were not freed on drivers unbind, leading to memory leak
- Link to v4: https://patch.msgid.link/20251118-topic-interconnect-next-v4-0-9c00d6b19add@linaro.org

Changes in v4:
- Rebased on upstream next to prepare for a PR
- Link to v3: https://lore.kernel.org/r/20251010-topic-interconnect-next-v3-0-b384713283a8@linaro.org

Changes in v3:
- Add doc page, hevily based on the linux one
- Documented all the structs and functions
- Removed id field from icc_node, now using the device driver_data
- icc_node id is not an ulong, can accept a pointer for dynamic id
- Removed all functions call with icc_provider, now using the struct device only
- Removed the icc_node provider entry, now only using dev->parent
- Removed the interconnect pre_remove and pre_unbind, they are not needed
- Removed more unused struct entries
- Fixed the icc_path alloc, added the called dev to print errors with dev_err()
- Removed IDs in sandbox interconnect, uses the "dynamic id" using the node pointers as ID
- Now of_icc_get/_index will return NULL is prop is missing, like for Linux
- Cleaned all the debug() calls, added new ones to track the bw agregations
- Renamed node_find() to icc_node_find() to avoid name collision
- Moved icc_node busy check to icc_node_busy()
- Now icc_node_create passes the ID via driver_data
- Added more bw test after put() calls, moved enable() after disable()
- Used "Generic System Interconnect Subsystem" when possible
- Dropped the icc_node init_avg/init_peak which is a Linux thing for sync_state
- Dropped icc_node_add
- Link to v2: https://lore.kernel.org/r/20251008-topic-interconnect-next-v2-0-c46e94dd328e@linaro.org

Changes in v2:
- Simplified nodes linking, looking for nodes at traversal instead of complexe debice reparent
- Populates nodes at bind time, only probinf nodes and providers when a path if found
- With proper refcounting the node lifetime is safe
- DM test is added, testing with a small graph that calculations are good
- Fixed plenty of typo and errors, and dropped useless code
- Dropped RFC since DM test is available, and code works as expected
- Link to v1: https://lore.kernel.org/r/20251003-topic-interconnect-next-v1-0-955eef068b6e@linaro.org

---
Neil Armstrong (7):
      Introduce the Generic System Interconnect Subsystem
      interconnect: add DM test suite
      MAINTAINERS: add myself as Maintainer of the Generic System Interconnect Subsystem
      interconnect: add support for the Qualcomm RPMh helpers
      interconnect: add support for the SM8650 SoC
      ufs: qcom: vote for interconnect bandwidth on probe
      qcom_defconfig: enable interconnect for SM8650

 MAINTAINERS                                      |   11 +
 arch/sandbox/dts/test.dts                        |   36 +
 arch/sandbox/include/asm/interconnect.h          |   19 +
 configs/qcom_defconfig                           |    3 +
 configs/sandbox64_defconfig                      |    2 +
 configs/sandbox_defconfig                        |    2 +
 doc/api/index.rst                                |    1 +
 doc/api/interconnect.rst                         |  117 ++
 drivers/Kconfig                                  |    2 +
 drivers/Makefile                                 |    1 +
 drivers/interconnect/Kconfig                     |   22 +
 drivers/interconnect/Makefile                    |    8 +
 drivers/interconnect/interconnect-uclass.c       |  545 +++++++
 drivers/interconnect/qcom/Kconfig                |   12 +
 drivers/interconnect/qcom/Makefile               |    7 +
 drivers/interconnect/qcom/bcm-voter.c            |  340 +++++
 drivers/interconnect/qcom/bcm-voter.h            |   19 +
 drivers/interconnect/qcom/icc-rpmh.c             |  224 +++
 drivers/interconnect/qcom/icc-rpmh.h             |  130 ++
 drivers/interconnect/qcom/sm8650.c               | 1665 ++++++++++++++++++++++
 drivers/interconnect/qcom/sm8650.h               |  144 ++
 drivers/interconnect/sandbox-interconnect-test.c |   89 ++
 drivers/interconnect/sandbox-interconnect.c      |  303 ++++
 drivers/ufs/ufs-qcom.c                           |   13 +
 include/dm/uclass-id.h                           |    2 +
 include/interconnect-uclass.h                    |  136 ++
 include/interconnect.h                           |  155 ++
 test/dm/Makefile                                 |    1 +
 test/dm/interconnect.c                           |  195 +++
 29 files changed, 4204 insertions(+)
---
base-commit: eb7a61d2cd7b24265c884dadf05ba20778ba99cd
change-id: 20251003-topic-interconnect-next-4ed75aecc01c

Best regards,
-- 
Neil Armstrong <neil.armstrong@linaro.org>


^ permalink raw reply	[flat|nested] 9+ messages in thread

* [PATCH v5 1/7] Introduce the Generic System Interconnect Subsystem
  2025-11-20  8:12 [PATCH v5 0/7] Implement the Generic System Interconnect Subsystem for U-Boot Neil Armstrong
@ 2025-11-20  8:12 ` Neil Armstrong
  2025-11-20  8:12 ` [PATCH v5 2/7] interconnect: add DM test suite Neil Armstrong
                   ` (6 subsequent siblings)
  7 siblings, 0 replies; 9+ messages in thread
From: Neil Armstrong @ 2025-11-20  8:12 UTC (permalink / raw)
  To: u-boot, Tom Rini, Simon Glass, Casey Connolly
  Cc: u-boot-qcom, Ilias Apalodimas, Jerome Forissier, Dinesh Maniyam,
	Sughosh Ganu, Mattijs Korpershoek, Hrushikesh Salunke,
	Anurag Dutta, Svyatoslav Ryhel, Ion Agorria,
	Kory Maincent (TI.com), Mario Six, Christian Marangi,
	Heiko Schocher, Guillaume La Roque, Miquel Raynal, Peng Fan,
	Michal Simek, Heinrich Schuchardt, Pieter Van Trappen,
	Alexander Graf, Quentin Schulz, Rasmus Villemoes, Greg Malysa,
	Arturs Artamonovs, Vasileios Bimpikas, Utsav Agarwal, Ian Roberts,
	Nathan Barrett-Morrison, Alif Zakuan Yuslaimi, Stefan Roese,
	Sumit Garg, Bhupesh Sharma, Neha Malcom Francis, Marek Vasut,
	Varadarajan Narayanan, Sam Day, Sam Protsenko, Marek Vasut,
	Alper Nebi Yasak, Oliver Gaskell, Paul Sajna, Neil Armstrong

Let's introduce the Generic System Interconnect subsystem based on
the counterpart Linux framework which is used to vote for bandwidth
across multiple SoC busses.

Documentation for the Linux Generic System Interconnect Subsystem can
be found at [1].

Each bus endpoints are materialised as "nodes" which are linked together,
and the DT will specify a pair of nodes to enable and set a bandwidth
on the route between those endpoints.

The hardware resources that provide those nodes and provides the way
to vote for the bandwidth are called "providers".

The Interconnect uclass code is heavily based on the Linux one, with
some small differences:
- nodes are allocated as udevices instead of Linux idr_alloc()
- tag management is minimal, only normal xlate is supported
- getting nodes states at probe is not implemented
- providers are probed on demand while the nodes links are traversed
- nodes are populated on bind
- id management is simplified, static IDs and dynamics IDs can be used
- identical consume API as Linux, only implementation differs

Fully tested with associated DM test suite.

[1] https://docs.kernel.org/driver-api/interconnect.html

Signed-off-by: Neil Armstrong <neil.armstrong@linaro.org>
---
 doc/api/index.rst                          |   1 +
 doc/api/interconnect.rst                   | 117 +++++++
 drivers/Kconfig                            |   2 +
 drivers/Makefile                           |   1 +
 drivers/interconnect/Kconfig               |  10 +
 drivers/interconnect/Makefile              |   6 +
 drivers/interconnect/interconnect-uclass.c | 545 +++++++++++++++++++++++++++++
 include/dm/uclass-id.h                     |   2 +
 include/interconnect-uclass.h              | 136 +++++++
 include/interconnect.h                     | 155 ++++++++
 10 files changed, 975 insertions(+)

diff --git a/doc/api/index.rst b/doc/api/index.rst
index d97630e7671..40134e4f42c 100644
--- a/doc/api/index.rst
+++ b/doc/api/index.rst
@@ -15,6 +15,7 @@ U-Boot API documentation
    fs
    getopt
    interrupt
+   interconnect
    i3c
    led
    linker_lists
diff --git a/doc/api/interconnect.rst b/doc/api/interconnect.rst
new file mode 100644
index 00000000000..c5182a3eb98
--- /dev/null
+++ b/doc/api/interconnect.rst
@@ -0,0 +1,117 @@
+.. SPDX-License-Identifier: GPL-2.0
+
+Generic System Interconnect Subsystem
+=====================================
+
+Introduction
+------------
+
+This framework is designed to provide a standard kernel interface to control
+the settings of the interconnects on an SoC. These settings can be throughput,
+latency and priority between multiple interconnected devices or functional
+blocks. This can be controlled dynamically in order to save power or provide
+maximum performance.
+
+The interconnect bus is hardware with configurable parameters, which can be
+set on a data path according to the requests received from various drivers.
+An example of interconnect buses are the interconnects between various
+components or functional blocks in chipsets. There can be multiple interconnects
+on an SoC that can be multi-tiered.
+
+Below is a simplified diagram of a real-world SoC interconnect bus topology.
+
+::
+
+ +----------------+    +----------------+
+ | HW Accelerator |--->|      M NoC     |<---------------+
+ +----------------+    +----------------+                |
+                         |      |                    +------------+
+  +-----+  +-------------+      V       +------+     |            |
+  | DDR |  |                +--------+  | PCIe |     |            |
+  +-----+  |                | Slaves |  +------+     |            |
+    ^ ^    |                +--------+     |         |   C NoC    |
+    | |    V                               V         |            |
+ +------------------+   +------------------------+   |            |   +-----+
+ |                  |-->|                        |-->|            |-->| CPU |
+ |                  |-->|                        |<--|            |   +-----+
+ |     Mem NoC      |   |         S NoC          |   +------------+
+ |                  |<--|                        |---------+    |
+ |                  |<--|                        |<------+ |    |   +--------+
+ +------------------+   +------------------------+       | |    +-->| Slaves |
+   ^  ^    ^    ^          ^                             | |        +--------+
+   |  |    |    |          |                             | V
+ +------+  |  +-----+   +-----+  +---------+   +----------------+   +--------+
+ | CPUs |  |  | GPU |   | DSP |  | Masters |-->|       P NoC    |-->| Slaves |
+ +------+  |  +-----+   +-----+  +---------+   +----------------+   +--------+
+           |
+       +-------+
+       | Modem |
+       +-------+
+
+Terminology
+-----------
+
+Interconnect provider is the software definition of the interconnect hardware.
+The interconnect providers on the above diagram are M NoC, S NoC, C NoC, P NoC
+and Mem NoC.
+
+Interconnect node is the software definition of the interconnect hardware
+port. Each interconnect provider consists of multiple interconnect nodes,
+which are connected to other SoC components including other interconnect
+providers. The point on the diagram where the CPUs connect to the memory is
+called an interconnect node, which belongs to the Mem NoC interconnect provider.
+
+Interconnect endpoints are the first or the last element of the path. Every
+endpoint is a node, but not every node is an endpoint.
+
+Interconnect path is everything between two endpoints including all the nodes
+that have to be traversed to reach from a source to destination node. It may
+include multiple master-slave pairs across several interconnect providers.
+
+Interconnect consumers are the entities which make use of the data paths exposed
+by the providers. The consumers send requests to providers requesting various
+throughput, latency and priority. Usually the consumers are device drivers, that
+send request based on their needs. An example for a consumer is a video decoder
+that supports various formats and image sizes.
+
+U-Boot Implementation
+---------------------
+
+The implementation is derived from the Linux 6.17 Interconnect implementation,
+adapted to use the U-Boot Driver Model. Under Linux the nodes are allocated
+via `idr_alloc()`, while under U-Boot they are created as `icc_node` devices
+which are children of the provider device. This provides the same lifetime
+by using a robust and ready to use mechanism, simplifying the implementation.
+
+Under Linux, the nodes link is done by always allocating a new `icc_node` when
+creating a link, and when the link with the associated ID is registered
+it is associated to the new provider. Under U-Boot only the nodes from a provider
+are created at bind time, and when the node graph is traversed to calculate
+a path the link ID is looked dynamically amongst the node devices. This
+may take longer at the gain of time when registering nodes a bind time.
+
+Since U-Boot Driver Model does on-demand device probe, the nodes and provider
+devices are also probed when a path is determined and removed when the path
+is deleted.
+
+A test suite is present in `test/dm/interconnect.c` using a test driver
+`sandbox-interconnect` to exercise those U-Boot specific aspects while making
+sure the graph traversal and calculation are accurate.
+
+Interconnect consumers API
+--------------------------
+
+Interconnect consumers are the clients which use the interconnect APIs to
+get paths between endpoints and set their bandwidth/latency/QoS requirements
+for these interconnect paths.
+
+.. kernel-doc:: include/interconnect.h
+
+Interconnect uclass providers API
+---------------------------------
+
+Interconnect provider is an entity that implements methods to initialize and
+configure interconnect bus hardware. The interconnect provider drivers should
+be registered a interconnect uclass drivers.
+
+.. kernel-doc:: include/interconnect-uclass.h
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..38d39651ab5
--- /dev/null
+++ b/drivers/interconnect/Kconfig
@@ -0,0 +1,10 @@
+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.
+
+endmenu
diff --git a/drivers/interconnect/Makefile b/drivers/interconnect/Makefile
new file mode 100644
index 00000000000..1f276e98051
--- /dev/null
+++ b/drivers/interconnect/Makefile
@@ -0,0 +1,6 @@
+# SPDX-License-Identifier: GPL-2.0
+#
+# Copyright (c) 2025 Linaro Limited
+#
+
+obj-$(CONFIG_$(PHASE_)INTERCONNECT) += interconnect-uclass.o
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 <georgi.djakov@linaro.org>
+ */
+
+#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/include/dm/uclass-id.h b/include/dm/uclass-id.h
index eb6416b5917..36b5d87c304 100644
--- a/include/dm/uclass-id.h
+++ b/include/dm/uclass-id.h
@@ -83,6 +83,8 @@ enum uclass_id {
 	UCLASS_I3C,		/* I3C bus */
 	UCLASS_IDE,		/* IDE device */
 	UCLASS_IOMMU,		/* IOMMU */
+	UCLASS_INTERCONNECT,	/* Interconnect */
+	UCLASS_ICC_NODE,	/* Interconnect Node */
 	UCLASS_IRQ,		/* Interrupt controller */
 	UCLASS_KEYBOARD,	/* Keyboard input device */
 	UCLASS_LED,		/* Light-emitting diode (LED) */
diff --git a/include/interconnect-uclass.h b/include/interconnect-uclass.h
new file mode 100644
index 00000000000..d942ddec41b
--- /dev/null
+++ b/include/interconnect-uclass.h
@@ -0,0 +1,136 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (c) 2025 Linaro Limited
+ */
+
+#ifndef _INTERCONNECT_UCLASS_H
+#define _INTERCONNECT_UCLASS_H
+
+#include <interconnect.h>
+
+#define icc_units_to_bps(bw)  ((bw) * 1000ULL)
+
+struct udevice;
+
+/**
+ * struct icc_req - constraints that are attached to each node
+ *
+ * @req_node:	entry in list of requests for the particular @node
+ * @node:	the interconnect node to which this constraint applies
+ * @enabled:	indicates whether the path with this request is enabled
+ * @tag:	path tag (optional)
+ * @avg_bw:	an integer describing the average bandwidth in kBps
+ * @peak_bw:	an integer describing the peak bandwidth in kBps
+ */
+struct icc_req {
+	struct hlist_node	req_node;
+	struct icc_node		*node;
+	bool			enabled;
+	u32			tag;
+	u32			avg_bw;
+	u32			peak_bw;
+};
+
+/**
+ * struct icc_path - An interconnect path
+ *
+ * @dev:	Device who requested the path
+ * @num_nodes:	number of nodes (hops) in the path
+ * @reqs:	array of the requests applicable to this path of nodes
+ */
+struct icc_path {
+	struct udevice		*dev;
+	size_t			num_nodes;
+	struct icc_req		reqs[];
+};
+
+/**
+ * struct icc_provider - interconnect provider (controller) entity that might
+ * provide multiple interconnect controls
+ *
+ * @inter_set:		whether inter-provider pairs will be configured with @set
+ * @xlate_num_nodes:	provider-specific nodes counts for mapping nodes from phandle arguments
+ * @xlate_nodes:	provider-specific array for mapping nodes from phandle arguments
+ */
+struct icc_provider {
+	bool			inter_set;
+	unsigned int		xlate_num_nodes;
+	struct icc_node		**xlate_nodes;
+};
+
+/**
+ * struct icc_node - entity that is part of the interconnect topology
+ *
+ * @dev:		points to the interconnect provider of this node
+ * @links:		a list of targets pointing to where we can go next when traversing
+ * @num_links:		number of links to other interconnect nodes
+ * @users:		count of active users
+ * @node_list:		the list entry in the parent provider's "nodes" list
+ * @search_list:	list used when walking the nodes graph
+ * @reverse:		pointer to previous node when walking the nodes graph
+ * @is_traversed:	flag that is used when walking the nodes graph
+ * @req_list:		a list of QoS constraint requests associated with this node
+ * @avg_bw:		aggregated value of average bandwidth requests from all consumers
+ * @peak_bw:		aggregated value of peak bandwidth requests from all consumers
+ * @data:		pointer to private data
+ */
+struct icc_node {
+	struct udevice		*dev;
+	ulong			*links;
+	size_t			num_links;
+	int			users;
+
+	struct list_head	node_list;
+	struct list_head	search_list;
+	struct icc_node		*reverse;
+	u8			is_traversed:1;
+	struct hlist_head	req_list;
+	u32			avg_bw;
+	u32			peak_bw;
+	void			*data;
+};
+
+/**
+ * struct interconnect_ops - Interconnect uclass operations
+ *
+ * @of_xlate:		provider-specific callback for mapping nodes from phandle arguments
+ * @set:		pointer to device specific set operation function
+ * @pre_aggregate:	pointer to device specific function that is called
+ *			before the aggregation begins (optional)
+ * @aggregate:		pointer to device specific aggregate operation function
+ */
+struct interconnect_ops {
+	struct icc_node *(*of_xlate)(struct udevice *dev,
+				     const struct ofnode_phandle_args *args);
+	int (*set)(struct icc_node *src, struct icc_node *dst);
+	void (*pre_aggregate)(struct icc_node *node);
+	int (*aggregate)(struct icc_node *node, u32 tag, u32 avg_bw,
+			 u32 peak_bw, u32 *agg_avg, u32 *agg_peak);
+};
+
+/**
+ * icc_node_create() - create a node
+ *
+ * @dev:	Provider device
+ * @id:		node id, can be a numeric ID or pointer casted to ulong
+ * @name:	node name
+ *
+ * Return: icc_node pointer on success, or ERR_PTR() on error
+ */
+struct icc_node *icc_node_create(struct udevice *dev,
+				 ulong id, const char *name);
+
+/**
+ * icc_link_create() - create a link between two nodes
+ * @node: source node id
+ * @dst_id: destination node id
+ *
+ * Create a link between two nodes. The nodes might belong to different
+ * interconnect providers and the @dst_id node might not exist, the link
+ * will be done at runtime in `icc_path_find()`.
+ *
+ * Return: 0 on success, or an error code otherwise
+ */
+int icc_link_create(struct icc_node *node, const ulong dst_id);
+
+#endif
diff --git a/include/interconnect.h b/include/interconnect.h
new file mode 100644
index 00000000000..11ed30d79d6
--- /dev/null
+++ b/include/interconnect.h
@@ -0,0 +1,155 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (c) 2025 Linaro Limited
+ */
+
+#ifndef _INTERCONNECT_H
+#define _INTERCONNECT_H
+
+#include <linux/errno.h>
+
+struct udevice;
+
+/* macros for converting to icc units */
+#define Bps_to_icc(x)	((x) / 1000)
+#define kBps_to_icc(x)	(x)
+#define MBps_to_icc(x)	((x) * 1000)
+#define GBps_to_icc(x)	((x) * 1000 * 1000)
+#define bps_to_icc(x)	(1)
+#define kbps_to_icc(x)	((x) / 8 + ((x) % 8 ? 1 : 0))
+#define Mbps_to_icc(x)	((x) * 1000 / 8)
+#define Gbps_to_icc(x)	((x) * 1000 * 1000 / 8)
+
+struct icc_path;
+
+/**
+ * of_icc_get - Get an Interconnect path from a DT node based on name
+ *
+ * This function will search for a path between two endpoints and return an
+ * icc_path handle on success. Use icc_put() to release constraints when they
+ * are not needed anymore.
+ * If the interconnect API is disabled, NULL is returned and the consumer
+ * drivers will still build. Drivers are free to handle this specifically,
+ * but they don't have to.
+ *
+ * @dev:	The client device.
+ * @name:	Name of the interconnect endpoint pair.
+ * Return:	icc_path pointer on success or ERR_PTR() on error. NULL is returned
+ * when the API is disabled or the "interconnects" DT property is missing.
+ */
+#if CONFIG_IS_ENABLED(INTERCONNECT)
+struct icc_path *of_icc_get(struct udevice *dev, const char *name);
+#else
+static inline
+struct icc_path *of_icc_get(struct udevice *dev, const char *name)
+{
+	return NULL;
+}
+#endif
+
+/**
+ * of_icc_get - Get an Interconnect path from a DT node based on index
+ *
+ * This function will search for a path between two endpoints and return an
+ * icc_path handle on success. Use icc_put() to release constraints when they
+ * are not needed anymore.
+ * If the interconnect API is disabled, NULL is returned and the consumer
+ * drivers will still build. Drivers are free to handle this specifically,
+ * but they don't have to.
+ *
+ * @dev:	The client device.
+ * @idx:	Index of the interconnect endpoint pair.
+ * Return:	icc_path pointer on success or ERR_PTR() on error. NULL is returned
+ * when the API is disabled or the "interconnects" DT property is missing.
+ */
+#if CONFIG_IS_ENABLED(INTERCONNECT)
+struct icc_path *of_icc_get_by_index(struct udevice *dev, int idx);
+#else
+static inline
+struct icc_path *of_icc_get_by_index(struct udevice *dev, int idx)
+{
+	return NULL;
+}
+#endif
+
+/**
+ * icc_put - release the reference to the Interconnect path.
+ *
+ * Use this function to release the constraints on a path when the path is
+ * no longer needed. The constraints will be re-aggregated.
+ *
+ * @path:	An interconnect path
+ * Return: 0 if OK, or a negative error code.
+ */
+#if CONFIG_IS_ENABLED(INTERCONNECT)
+int icc_put(struct icc_path *path);
+#else
+static inline int icc_put(struct icc_path *path)
+{
+	return 0;
+}
+#endif
+
+/**
+ * icc_enable - Enable an Interconnect path.
+ *
+ * This will enable all the endpoints in the path, using the
+ * bandwidth set by the `icc_set_bw()` call. Otherwise a zero
+ * bandwidth will be set. Usually used after a call to `icc_disable()`.
+ *
+ * @path:	An interconnect path
+ * Return: 0 if OK, or a negative error code. -ENOSYS if not implemented.
+ */
+#if CONFIG_IS_ENABLED(INTERCONNECT)
+int icc_enable(struct icc_path *path);
+#else
+static inline int icc_enable(struct icc_path *path)
+{
+	return -ENOSYS;
+}
+#endif
+
+/**
+ * icc_disable - Disable an Interconnect path.
+ *
+ * This will disable all the endpoints in the path, effectively setting
+ * a zero bandwidth. Calling `icc_enable()` will restore the bandwidth set
+ * by calling `icc_set_bw()`.
+ *
+ * @path:	An interconnect path
+ * Return: 0 if OK, or a negative error code. -ENOSYS if not implemented.
+ */
+#if CONFIG_IS_ENABLED(INTERCONNECT)
+int icc_disable(struct icc_path *path);
+#else
+static inline int icc_disable(struct icc_path *path)
+{
+	return -ENOSYS;
+}
+#endif
+
+/**
+ * icc_set_bw - set bandwidth constraints on an interconnect path.
+ *
+ * This function is used by an interconnect consumer to express its own needs
+ * in terms of bandwidth for a previously requested path between two endpoints.
+ * The requests are aggregated and each node is updated accordingly. The entire
+ * path is locked by a mutex to ensure that the set() is completed.
+ * The @path can be NULL when the "interconnects" DT properties is missing,
+ * which will mean that no constraints will be set.
+ *
+ * @path:	An interconnect path
+ * @avg_bw:	Average bandwidth request in kBps
+ * @peak_bw:	Peak bandwidth in request kBps
+ * Return: 0 if OK, or a negative error code. -ENOSYS if not implemented.
+ */
+#if CONFIG_IS_ENABLED(INTERCONNECT)
+int icc_set_bw(struct icc_path *path, u32 avg_bw, u32 peak_bw);
+#else
+static inline int icc_set_bw(struct icc_path *path, u32 avg_bw, u32 peak_bw)
+{
+	return -ENOSYS;
+}
+#endif
+
+#endif

-- 
2.34.1


^ permalink raw reply related	[flat|nested] 9+ messages in thread

* [PATCH v5 2/7] interconnect: add DM test suite
  2025-11-20  8:12 [PATCH v5 0/7] Implement the Generic System Interconnect Subsystem for U-Boot Neil Armstrong
  2025-11-20  8:12 ` [PATCH v5 1/7] Introduce the Generic System Interconnect Subsystem Neil Armstrong
@ 2025-11-20  8:12 ` Neil Armstrong
  2025-11-20  8:12 ` [PATCH v5 3/7] MAINTAINERS: add myself as Maintainer of the Generic System Interconnect Subsystem Neil Armstrong
                   ` (5 subsequent siblings)
  7 siblings, 0 replies; 9+ messages in thread
From: Neil Armstrong @ 2025-11-20  8:12 UTC (permalink / raw)
  To: u-boot, Tom Rini, Simon Glass, Casey Connolly
  Cc: u-boot-qcom, Ilias Apalodimas, Jerome Forissier, Dinesh Maniyam,
	Sughosh Ganu, Mattijs Korpershoek, Hrushikesh Salunke,
	Anurag Dutta, Svyatoslav Ryhel, Ion Agorria,
	Kory Maincent (TI.com), Mario Six, Christian Marangi,
	Heiko Schocher, Guillaume La Roque, Miquel Raynal, Peng Fan,
	Michal Simek, Heinrich Schuchardt, Pieter Van Trappen,
	Alexander Graf, Quentin Schulz, Rasmus Villemoes, Greg Malysa,
	Arturs Artamonovs, Vasileios Bimpikas, Utsav Agarwal, Ian Roberts,
	Nathan Barrett-Morrison, Alif Zakuan Yuslaimi, Stefan Roese,
	Sumit Garg, Bhupesh Sharma, Neha Malcom Francis, Marek Vasut,
	Varadarajan Narayanan, Sam Day, Sam Protsenko, Marek Vasut,
	Alper Nebi Yasak, Oliver Gaskell, Paul Sajna, Neil Armstrong

Add a test suite exercising the whole lifetime and callbacks
of interconnect with a fake 5 providers with a split node graph.

The test suite checks the calculus are right and goes to the correct
nodes, and the lifetime of the node is correct.

Signed-off-by: Neil Armstrong <neil.armstrong@linaro.org>
---
 arch/sandbox/dts/test.dts                        |  36 +++
 arch/sandbox/include/asm/interconnect.h          |  19 ++
 configs/sandbox64_defconfig                      |   2 +
 configs/sandbox_defconfig                        |   2 +
 drivers/interconnect/Kconfig                     |  10 +
 drivers/interconnect/Makefile                    |   1 +
 drivers/interconnect/sandbox-interconnect-test.c |  89 +++++++
 drivers/interconnect/sandbox-interconnect.c      | 303 +++++++++++++++++++++++
 test/dm/Makefile                                 |   1 +
 test/dm/interconnect.c                           | 195 +++++++++++++++
 10 files changed, 658 insertions(+)

diff --git a/arch/sandbox/dts/test.dts b/arch/sandbox/dts/test.dts
index 13d8e498b03..0b3fc505e53 100644
--- a/arch/sandbox/dts/test.dts
+++ b/arch/sandbox/dts/test.dts
@@ -864,6 +864,42 @@
 		};
 	};
 
+	icc0: interconnect-0 {
+		compatible = "sandbox,interconnect0";
+		#interconnect-cells = <1>;
+	};
+
+	icc1: interconnect-1 {
+		compatible = "sandbox,interconnect1";
+		#interconnect-cells = <1>;
+	};
+
+	icc2: interconnect-2 {
+		compatible = "sandbox,interconnect2";
+		#interconnect-cells = <1>;
+	};
+
+	icc3: interconnect-3 {
+		compatible = "sandbox,interconnect3";
+		#interconnect-cells = <1>;
+	};
+
+	icc4: interconnect-4 {
+		compatible = "sandbox,interconnect4";
+		#interconnect-cells = <1>;
+	};
+
+	interconnect-test-0 {
+		compatible = "sandbox,interconnect-test";
+		interconnects = <&icc0 0 &icc3 0>;
+	};
+
+	interconnect-test-1 {
+		compatible = "sandbox,interconnect-test";
+		interconnects = <&icc1 0 &icc4 0>;
+		interconnect-names = "icc-path";
+	};
+
 	i2c@0 {
 		#address-cells = <1>;
 		#size-cells = <0>;
diff --git a/arch/sandbox/include/asm/interconnect.h b/arch/sandbox/include/asm/interconnect.h
new file mode 100644
index 00000000000..17065bf6a11
--- /dev/null
+++ b/arch/sandbox/include/asm/interconnect.h
@@ -0,0 +1,19 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (c) 2025 Linaro Limited
+ */
+
+#ifndef __SANDBOX_INTERCONNECT_H
+#define __SANDBOX_INTERCONNECT_H
+
+struct udevice;
+
+int sandbox_interconnect_get_bw(struct udevice *dev, u64 *avg, u64 *peak);
+int sandbox_interconnect_test_get(struct udevice *dev, char *name);
+int sandbox_interconnect_test_get_index(struct udevice *dev, int index);
+int sandbox_interconnect_test_enable(struct udevice *dev);
+int sandbox_interconnect_test_disable(struct udevice *dev);
+int sandbox_interconnect_test_set_bw(struct udevice *dev, u32 avg_bw, u32 peak_bw);
+int sandbox_interconnect_test_put(struct udevice *dev);
+
+#endif
diff --git a/configs/sandbox64_defconfig b/configs/sandbox64_defconfig
index 169d0a2adef..1603e86d184 100644
--- a/configs/sandbox64_defconfig
+++ b/configs/sandbox64_defconfig
@@ -202,6 +202,8 @@ CONFIG_PINCTRL_SANDBOX=y
 CONFIG_PINCTRL_SINGLE=y
 CONFIG_POWER_DOMAIN=y
 CONFIG_SANDBOX_POWER_DOMAIN=y
+CONFIG_INTERCONNECT=y
+CONFIG_INTERCONNECT_SANDBOX=y
 CONFIG_DM_PMIC=y
 CONFIG_PMIC_ACT8846=y
 CONFIG_DM_PMIC_PFUZE100=y
diff --git a/configs/sandbox_defconfig b/configs/sandbox_defconfig
index f4c2cc5d155..57d5b1f15ca 100644
--- a/configs/sandbox_defconfig
+++ b/configs/sandbox_defconfig
@@ -281,6 +281,8 @@ CONFIG_PINCTRL_SANDBOX=y
 CONFIG_PINCTRL_SINGLE=y
 CONFIG_POWER_DOMAIN=y
 CONFIG_SANDBOX_POWER_DOMAIN=y
+CONFIG_INTERCONNECT=y
+CONFIG_INTERCONNECT_SANDBOX=y
 CONFIG_SCMI_POWER_DOMAIN=y
 CONFIG_DM_PMIC=y
 CONFIG_PMIC_ACT8846=y
diff --git a/drivers/interconnect/Kconfig b/drivers/interconnect/Kconfig
index 38d39651ab5..e9f4cc662ae 100644
--- a/drivers/interconnect/Kconfig
+++ b/drivers/interconnect/Kconfig
@@ -7,4 +7,14 @@ config INTERCONNECT
 	  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.
+
+endif
+
 endmenu
diff --git a/drivers/interconnect/Makefile b/drivers/interconnect/Makefile
index 1f276e98051..04b18589b7f 100644
--- a/drivers/interconnect/Makefile
+++ b/drivers/interconnect/Makefile
@@ -4,3 +4,4 @@
 #
 
 obj-$(CONFIG_$(PHASE_)INTERCONNECT) += interconnect-uclass.o
+obj-$(CONFIG_$(PHASE_)INTERCONNECT_SANDBOX) += sandbox-interconnect.o sandbox-interconnect-test.o
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/test/dm/Makefile b/test/dm/Makefile
index a261b3fb4b7..771b703b737 100644
--- a/test/dm/Makefile
+++ b/test/dm/Makefile
@@ -57,6 +57,7 @@ obj-$(CONFIG_DM_FPGA) += fpga.o
 obj-$(CONFIG_FWU_MDATA_GPT_BLK) += fwu_mdata.o
 obj-$(CONFIG_SANDBOX) += host.o
 obj-$(CONFIG_DM_HWSPINLOCK) += hwspinlock.o
+obj-$(CONFIG_INTERCONNECT) += interconnect.o
 obj-$(CONFIG_DM_I2C) += i2c.o
 obj-$(CONFIG_I3C) += i3c.o
 obj-$(CONFIG_SOUND) += i2s.o
diff --git a/test/dm/interconnect.c b/test/dm/interconnect.c
new file mode 100644
index 00000000000..e8c2eccd7b1
--- /dev/null
+++ b/test/dm/interconnect.c
@@ -0,0 +1,195 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2025 Linaro Limited
+ */
+
+#include <dm.h>
+#include <malloc.h>
+#include <dm/test.h>
+#include <asm/interconnect.h>
+#include <dm/device-internal.h>
+#include <dm/uclass-internal.h>
+#include <test/test.h>
+#include <test/ut.h>
+
+static int dm_test_interconnect(struct unit_test_state *uts)
+{
+	struct udevice *dev_interconnect_0,
+		       *dev_interconnect_1,
+		       *dev_interconnect_2,
+		       *dev_interconnect_3,
+		       *dev_interconnect_4;
+	struct udevice *dev_test_0, *dev_test_1, *dev;
+	u64 avg = 0, peak = 0;
+
+	ut_assertok(uclass_get_device_by_name(UCLASS_MISC, "interconnect-test-0",
+					      &dev_test_0));
+	ut_assertok(uclass_get_device_by_name(UCLASS_MISC, "interconnect-test-1",
+					      &dev_test_1));
+
+	ut_assertok(sandbox_interconnect_test_get_index(dev_test_0, 0));
+	ut_assertok(sandbox_interconnect_test_get(dev_test_1, "icc-path"));
+
+	ut_assertok(uclass_find_device_by_name(UCLASS_INTERCONNECT,
+					       "interconnect-0",
+					      &dev_interconnect_0));
+	ut_assertok(uclass_find_device_by_name(UCLASS_INTERCONNECT,
+					       "interconnect-1",
+					      &dev_interconnect_1));
+	ut_assertok(uclass_find_device_by_name(UCLASS_INTERCONNECT,
+					       "interconnect-2",
+					      &dev_interconnect_2));
+	ut_assertok(uclass_find_device_by_name(UCLASS_INTERCONNECT,
+					       "interconnect-3",
+					      &dev_interconnect_3));
+	ut_assertok(uclass_find_device_by_name(UCLASS_INTERCONNECT,
+					       "interconnect-4",
+					      &dev_interconnect_4));
+
+	ut_assertok(sandbox_interconnect_get_bw(dev_interconnect_0, &avg, &peak));
+	ut_asserteq(avg, 0); ut_asserteq(peak, 0);
+	ut_assertok(sandbox_interconnect_get_bw(dev_interconnect_1, &avg, &peak));
+	ut_asserteq(avg, 0); ut_asserteq(peak, 0);
+	ut_assertok(sandbox_interconnect_get_bw(dev_interconnect_2, &avg, &peak));
+	ut_asserteq(avg, 0); ut_asserteq(peak, 0);
+	ut_assertok(sandbox_interconnect_get_bw(dev_interconnect_3, &avg, &peak));
+	ut_asserteq(avg, 0); ut_asserteq(peak, 0);
+	ut_assertok(sandbox_interconnect_get_bw(dev_interconnect_4, &avg, &peak));
+	ut_asserteq(avg, 0); ut_asserteq(peak, 0);
+
+	ut_assertok(sandbox_interconnect_test_set_bw(dev_test_0, 10000, 100000));
+
+	ut_assertok(sandbox_interconnect_get_bw(dev_interconnect_0, &avg, &peak));
+	ut_asserteq(avg, 0); ut_asserteq(peak, 0);
+	ut_assertok(sandbox_interconnect_get_bw(dev_interconnect_1, &avg, &peak));
+	ut_asserteq(avg, 0); ut_asserteq(peak, 0);
+	ut_assertok(sandbox_interconnect_get_bw(dev_interconnect_2, &avg, &peak));
+	ut_asserteq(avg, 10000); ut_asserteq(peak, 100000);
+	ut_assertok(sandbox_interconnect_get_bw(dev_interconnect_3, &avg, &peak));
+	ut_asserteq(avg, 0); ut_asserteq(peak, 0);
+	ut_assertok(sandbox_interconnect_get_bw(dev_interconnect_4, &avg, &peak));
+	ut_asserteq(avg, 0); ut_asserteq(peak, 0);
+
+	ut_assertok(sandbox_interconnect_test_set_bw(dev_test_1, 20000, 200000));
+
+	ut_assertok(sandbox_interconnect_get_bw(dev_interconnect_0, &avg, &peak));
+	ut_asserteq(avg, 0); ut_asserteq(peak, 0);
+	ut_assertok(sandbox_interconnect_get_bw(dev_interconnect_1, &avg, &peak));
+	ut_asserteq(avg, 0); ut_asserteq(peak, 0);
+	ut_assertok(sandbox_interconnect_get_bw(dev_interconnect_2, &avg, &peak));
+	ut_asserteq(avg, 30000); ut_asserteq(peak, 200000);
+	ut_assertok(sandbox_interconnect_get_bw(dev_interconnect_3, &avg, &peak));
+	ut_asserteq(avg, 0); ut_asserteq(peak, 0);
+	ut_assertok(sandbox_interconnect_get_bw(dev_interconnect_4, &avg, &peak));
+	ut_asserteq(avg, 0); ut_asserteq(peak, 0);
+
+	ut_assertok(sandbox_interconnect_test_disable(dev_test_0));
+
+	ut_assertok(sandbox_interconnect_get_bw(dev_interconnect_0, &avg, &peak));
+	ut_asserteq(avg, 0); ut_asserteq(peak, 0);
+	ut_assertok(sandbox_interconnect_get_bw(dev_interconnect_1, &avg, &peak));
+	ut_asserteq(avg, 0); ut_asserteq(peak, 0);
+	ut_assertok(sandbox_interconnect_get_bw(dev_interconnect_2, &avg, &peak));
+	ut_asserteq(avg, 20000); ut_asserteq(peak, 200000);
+	ut_assertok(sandbox_interconnect_get_bw(dev_interconnect_3, &avg, &peak));
+	ut_asserteq(avg, 0); ut_asserteq(peak, 0);
+	ut_assertok(sandbox_interconnect_get_bw(dev_interconnect_4, &avg, &peak));
+	ut_asserteq(avg, 0); ut_asserteq(peak, 0);
+
+	ut_assertok(sandbox_interconnect_test_disable(dev_test_1));
+
+	ut_assertok(sandbox_interconnect_get_bw(dev_interconnect_0, &avg, &peak));
+	ut_asserteq(avg, 0); ut_asserteq(peak, 0);
+	ut_assertok(sandbox_interconnect_get_bw(dev_interconnect_1, &avg, &peak));
+	ut_asserteq(avg, 0); ut_asserteq(peak, 0);
+	ut_assertok(sandbox_interconnect_get_bw(dev_interconnect_2, &avg, &peak));
+	ut_asserteq(avg, 0); ut_asserteq(peak, 0);
+	ut_assertok(sandbox_interconnect_get_bw(dev_interconnect_3, &avg, &peak));
+	ut_asserteq(avg, 0); ut_asserteq(peak, 0);
+	ut_assertok(sandbox_interconnect_get_bw(dev_interconnect_4, &avg, &peak));
+	ut_asserteq(avg, 0); ut_asserteq(peak, 0);
+
+	ut_assertok(sandbox_interconnect_test_enable(dev_test_0));
+
+	ut_assertok(sandbox_interconnect_get_bw(dev_interconnect_0, &avg, &peak));
+	ut_asserteq(avg, 0); ut_asserteq(peak, 0);
+	ut_assertok(sandbox_interconnect_get_bw(dev_interconnect_1, &avg, &peak));
+	ut_asserteq(avg, 0); ut_asserteq(peak, 0);
+	ut_assertok(sandbox_interconnect_get_bw(dev_interconnect_2, &avg, &peak));
+	ut_asserteq(avg, 10000); ut_asserteq(peak, 100000);
+	ut_assertok(sandbox_interconnect_get_bw(dev_interconnect_3, &avg, &peak));
+	ut_asserteq(avg, 0); ut_asserteq(peak, 0);
+	ut_assertok(sandbox_interconnect_get_bw(dev_interconnect_4, &avg, &peak));
+	ut_asserteq(avg, 0); ut_asserteq(peak, 0);
+
+	ut_assertok(sandbox_interconnect_test_enable(dev_test_1));
+
+	ut_assertok(sandbox_interconnect_get_bw(dev_interconnect_0, &avg, &peak));
+	ut_asserteq(avg, 0); ut_asserteq(peak, 0);
+	ut_assertok(sandbox_interconnect_get_bw(dev_interconnect_1, &avg, &peak));
+	ut_asserteq(avg, 0); ut_asserteq(peak, 0);
+	ut_assertok(sandbox_interconnect_get_bw(dev_interconnect_2, &avg, &peak));
+	ut_asserteq(avg, 30000); ut_asserteq(peak, 200000);
+	ut_assertok(sandbox_interconnect_get_bw(dev_interconnect_3, &avg, &peak));
+	ut_asserteq(avg, 0); ut_asserteq(peak, 0);
+	ut_assertok(sandbox_interconnect_get_bw(dev_interconnect_4, &avg, &peak));
+	ut_asserteq(avg, 0); ut_asserteq(peak, 0);
+
+	ut_asserteq(-EBUSY, device_remove(dev_interconnect_0, DM_REMOVE_NORMAL));
+	ut_asserteq(-EBUSY, device_remove(dev_interconnect_1, DM_REMOVE_NORMAL));
+	ut_asserteq(-EBUSY, device_remove(dev_interconnect_2, DM_REMOVE_NORMAL));
+	ut_asserteq(-EBUSY, device_remove(dev_interconnect_3, DM_REMOVE_NORMAL));
+	ut_asserteq(-EBUSY, device_remove(dev_interconnect_4, DM_REMOVE_NORMAL));
+
+	ut_assertok(sandbox_interconnect_test_put(dev_test_0));
+
+	ut_assertok(sandbox_interconnect_get_bw(dev_interconnect_0, &avg, &peak));
+	ut_asserteq(avg, 0); ut_asserteq(peak, 0);
+	ut_assertok(sandbox_interconnect_get_bw(dev_interconnect_1, &avg, &peak));
+	ut_asserteq(avg, 0); ut_asserteq(peak, 0);
+	ut_assertok(sandbox_interconnect_get_bw(dev_interconnect_2, &avg, &peak));
+	ut_asserteq(avg, 20000); ut_asserteq(peak, 200000);
+	ut_assertok(sandbox_interconnect_get_bw(dev_interconnect_3, &avg, &peak));
+	ut_asserteq(avg, 0); ut_asserteq(peak, 0);
+	ut_assertok(sandbox_interconnect_get_bw(dev_interconnect_4, &avg, &peak));
+	ut_asserteq(avg, 0); ut_asserteq(peak, 0);
+
+	ut_assertok(sandbox_interconnect_test_put(dev_test_1));
+
+	ut_assertok(sandbox_interconnect_get_bw(dev_interconnect_0, &avg, &peak));
+	ut_asserteq(avg, 0); ut_asserteq(peak, 0);
+	ut_assertok(sandbox_interconnect_get_bw(dev_interconnect_1, &avg, &peak));
+	ut_asserteq(avg, 0); ut_asserteq(peak, 0);
+	ut_assertok(sandbox_interconnect_get_bw(dev_interconnect_2, &avg, &peak));
+	ut_asserteq(avg, 0); ut_asserteq(peak, 0);
+	ut_assertok(sandbox_interconnect_get_bw(dev_interconnect_3, &avg, &peak));
+	ut_asserteq(avg, 0); ut_asserteq(peak, 0);
+	ut_assertok(sandbox_interconnect_get_bw(dev_interconnect_4, &avg, &peak));
+	ut_asserteq(avg, 0); ut_asserteq(peak, 0);
+
+	ut_asserteq(-ENOENT, sandbox_interconnect_test_get_index(dev_test_0, 1));
+	ut_asserteq(-ENOENT, sandbox_interconnect_test_get_index(dev_test_1, 1));
+	ut_asserteq(-ENODATA, sandbox_interconnect_test_get(dev_test_1, "pwet"));
+
+	ut_assertok(device_remove(dev_interconnect_0, DM_REMOVE_NORMAL));
+	ut_assertok(device_remove(dev_interconnect_1, DM_REMOVE_NORMAL));
+	ut_assertok(device_remove(dev_interconnect_2, DM_REMOVE_NORMAL));
+	ut_assertok(device_remove(dev_interconnect_3, DM_REMOVE_NORMAL));
+	ut_assertok(device_remove(dev_interconnect_4, DM_REMOVE_NORMAL));
+
+	ut_assertok(device_unbind(dev_interconnect_0));
+	ut_assertok(device_unbind(dev_interconnect_1));
+	ut_assertok(device_unbind(dev_interconnect_2));
+	ut_assertok(device_unbind(dev_interconnect_3));
+	ut_assertok(device_unbind(dev_interconnect_4));
+
+	uclass_find_first_device(UCLASS_INTERCONNECT, &dev);
+	ut_assert(!dev);
+
+	uclass_find_first_device(UCLASS_ICC_NODE, &dev);
+	ut_assert(!dev);
+
+	return 0;
+}
+
+DM_TEST(dm_test_interconnect, UTF_SCAN_FDT);

-- 
2.34.1


^ permalink raw reply related	[flat|nested] 9+ messages in thread

* [PATCH v5 3/7] MAINTAINERS: add myself as Maintainer of the Generic System Interconnect Subsystem
  2025-11-20  8:12 [PATCH v5 0/7] Implement the Generic System Interconnect Subsystem for U-Boot Neil Armstrong
  2025-11-20  8:12 ` [PATCH v5 1/7] Introduce the Generic System Interconnect Subsystem Neil Armstrong
  2025-11-20  8:12 ` [PATCH v5 2/7] interconnect: add DM test suite Neil Armstrong
@ 2025-11-20  8:12 ` Neil Armstrong
  2025-11-20  8:12 ` [PATCH v5 4/7] interconnect: add support for the Qualcomm RPMh helpers Neil Armstrong
                   ` (4 subsequent siblings)
  7 siblings, 0 replies; 9+ messages in thread
From: Neil Armstrong @ 2025-11-20  8:12 UTC (permalink / raw)
  To: u-boot, Tom Rini, Simon Glass, Casey Connolly
  Cc: u-boot-qcom, Ilias Apalodimas, Jerome Forissier, Dinesh Maniyam,
	Sughosh Ganu, Mattijs Korpershoek, Hrushikesh Salunke,
	Anurag Dutta, Svyatoslav Ryhel, Ion Agorria,
	Kory Maincent (TI.com), Mario Six, Christian Marangi,
	Heiko Schocher, Guillaume La Roque, Miquel Raynal, Peng Fan,
	Michal Simek, Heinrich Schuchardt, Pieter Van Trappen,
	Alexander Graf, Quentin Schulz, Rasmus Villemoes, Greg Malysa,
	Arturs Artamonovs, Vasileios Bimpikas, Utsav Agarwal, Ian Roberts,
	Nathan Barrett-Morrison, Alif Zakuan Yuslaimi, Stefan Roese,
	Sumit Garg, Bhupesh Sharma, Neha Malcom Francis, Marek Vasut,
	Varadarajan Narayanan, Sam Day, Sam Protsenko, Marek Vasut,
	Alper Nebi Yasak, Oliver Gaskell, Paul Sajna, Neil Armstrong

I'll maintain the Interconnect subsystem.

Signed-off-by: Neil Armstrong <neil.armstrong@linaro.org>
---
 MAINTAINERS | 11 +++++++++++
 1 file changed, 11 insertions(+)

diff --git a/MAINTAINERS b/MAINTAINERS
index 60772a494a3..c0c5717f45d 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -1259,6 +1259,17 @@ S:	Maintained
 F:	drivers/misc/gsc.c
 F:	include/gsc.h
 
+INTERCONNECT:
+M:	Neil Armstrong <neil.armstrong@linaro.org>
+S:	Maintained
+T:	git https://source.denx.de/u-boot/u-boot.git
+F:	arch/sandbox/include/asm/interconnect.h
+F:	doc/api/interconnect.rst
+F:	drivers/interconnect/
+F:	include/interconnect-uclass.h
+F:	include/interconnect.h
+F:	test/dm/interconnect.c
+
 I2C
 M:	Heiko Schocher <hs@nabladev.com>
 S:	Maintained

-- 
2.34.1


^ permalink raw reply related	[flat|nested] 9+ messages in thread

* [PATCH v5 4/7] interconnect: add support for the Qualcomm RPMh helpers
  2025-11-20  8:12 [PATCH v5 0/7] Implement the Generic System Interconnect Subsystem for U-Boot Neil Armstrong
                   ` (2 preceding siblings ...)
  2025-11-20  8:12 ` [PATCH v5 3/7] MAINTAINERS: add myself as Maintainer of the Generic System Interconnect Subsystem Neil Armstrong
@ 2025-11-20  8:12 ` Neil Armstrong
  2025-11-20  8:12 ` [PATCH v5 5/7] interconnect: add support for the SM8650 SoC Neil Armstrong
                   ` (3 subsequent siblings)
  7 siblings, 0 replies; 9+ messages in thread
From: Neil Armstrong @ 2025-11-20  8:12 UTC (permalink / raw)
  To: u-boot, Tom Rini, Simon Glass, Casey Connolly
  Cc: u-boot-qcom, Ilias Apalodimas, Jerome Forissier, Dinesh Maniyam,
	Sughosh Ganu, Mattijs Korpershoek, Hrushikesh Salunke,
	Anurag Dutta, Svyatoslav Ryhel, Ion Agorria,
	Kory Maincent (TI.com), Mario Six, Christian Marangi,
	Heiko Schocher, Guillaume La Roque, Miquel Raynal, Peng Fan,
	Michal Simek, Heinrich Schuchardt, Pieter Van Trappen,
	Alexander Graf, Quentin Schulz, Rasmus Villemoes, Greg Malysa,
	Arturs Artamonovs, Vasileios Bimpikas, Utsav Agarwal, Ian Roberts,
	Nathan Barrett-Morrison, Alif Zakuan Yuslaimi, Stefan Roese,
	Sumit Garg, Bhupesh Sharma, Neha Malcom Francis, Marek Vasut,
	Varadarajan Narayanan, Sam Day, Sam Protsenko, Marek Vasut,
	Alper Nebi Yasak, Oliver Gaskell, Paul Sajna, Neil Armstrong

The Qualcomm SoCs votes for common resources via the RPMh subsystem.

Implement the necessary helpers for Interconnect providers to add the
nodes and vote via the RPPh "BCM" voters, which are vote endpoints for
each SoC subsystems. The APPS (ARM subsystem) has a dedicated endpoint.

The BCM voter will aggregate all the bandwidth for all the nodes
associated with a BCM voter, and internally the RPMh with also
aggregate all the votes from all the SoC subsystems for the same
BCM voter.

Signed-off-by: Neil Armstrong <neil.armstrong@linaro.org>
---
 drivers/interconnect/Kconfig          |   2 +
 drivers/interconnect/Makefile         |   1 +
 drivers/interconnect/qcom/Kconfig     |   6 +
 drivers/interconnect/qcom/Makefile    |   6 +
 drivers/interconnect/qcom/bcm-voter.c | 340 ++++++++++++++++++++++++++++++++++
 drivers/interconnect/qcom/bcm-voter.h |  19 ++
 drivers/interconnect/qcom/icc-rpmh.c  | 224 ++++++++++++++++++++++
 drivers/interconnect/qcom/icc-rpmh.h  | 130 +++++++++++++
 8 files changed, 728 insertions(+)

diff --git a/drivers/interconnect/Kconfig b/drivers/interconnect/Kconfig
index e9f4cc662ae..de868b9ad38 100644
--- a/drivers/interconnect/Kconfig
+++ b/drivers/interconnect/Kconfig
@@ -15,6 +15,8 @@ config INTERCONNECT_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
index 04b18589b7f..c9e8077b0f1 100644
--- a/drivers/interconnect/Makefile
+++ b/drivers/interconnect/Makefile
@@ -5,3 +5,4 @@
 
 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/qcom/Kconfig b/drivers/interconnect/qcom/Kconfig
new file mode 100644
index 00000000000..ab5b19567ec
--- /dev/null
+++ b/drivers/interconnect/qcom/Kconfig
@@ -0,0 +1,6 @@
+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
diff --git a/drivers/interconnect/qcom/Makefile b/drivers/interconnect/qcom/Makefile
new file mode 100644
index 00000000000..4369ca07f61
--- /dev/null
+++ b/drivers/interconnect/qcom/Makefile
@@ -0,0 +1,6 @@
+# SPDX-License-Identifier: GPL-2.0
+#
+# Copyright (c) 2025 Linaro Limited
+#
+
+obj-$(CONFIG_$(PHASE_)INTERCONNECT_QCOM_RPMH) += icc-rpmh.o bcm-voter.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

-- 
2.34.1


^ permalink raw reply related	[flat|nested] 9+ messages in thread

* [PATCH v5 5/7] interconnect: add support for the SM8650 SoC
  2025-11-20  8:12 [PATCH v5 0/7] Implement the Generic System Interconnect Subsystem for U-Boot Neil Armstrong
                   ` (3 preceding siblings ...)
  2025-11-20  8:12 ` [PATCH v5 4/7] interconnect: add support for the Qualcomm RPMh helpers Neil Armstrong
@ 2025-11-20  8:12 ` Neil Armstrong
  2025-11-20  8:12 ` [PATCH v5 6/7] ufs: qcom: vote for interconnect bandwidth on probe Neil Armstrong
                   ` (2 subsequent siblings)
  7 siblings, 0 replies; 9+ messages in thread
From: Neil Armstrong @ 2025-11-20  8:12 UTC (permalink / raw)
  To: u-boot, Tom Rini, Simon Glass, Casey Connolly
  Cc: u-boot-qcom, Ilias Apalodimas, Jerome Forissier, Dinesh Maniyam,
	Sughosh Ganu, Mattijs Korpershoek, Hrushikesh Salunke,
	Anurag Dutta, Svyatoslav Ryhel, Ion Agorria,
	Kory Maincent (TI.com), Mario Six, Christian Marangi,
	Heiko Schocher, Guillaume La Roque, Miquel Raynal, Peng Fan,
	Michal Simek, Heinrich Schuchardt, Pieter Van Trappen,
	Alexander Graf, Quentin Schulz, Rasmus Villemoes, Greg Malysa,
	Arturs Artamonovs, Vasileios Bimpikas, Utsav Agarwal, Ian Roberts,
	Nathan Barrett-Morrison, Alif Zakuan Yuslaimi, Stefan Roese,
	Sumit Garg, Bhupesh Sharma, Neha Malcom Francis, Marek Vasut,
	Varadarajan Narayanan, Sam Day, Sam Protsenko, Marek Vasut,
	Alper Nebi Yasak, Oliver Gaskell, Paul Sajna, Neil Armstrong

Add the SM8650 Interconnect nodes definitions, this is heavily based
on the Linux driver without the QoS definitions.

Signed-off-by: Neil Armstrong <neil.armstrong@linaro.org>
---
 drivers/interconnect/qcom/Kconfig  |    6 +
 drivers/interconnect/qcom/Makefile |    1 +
 drivers/interconnect/qcom/sm8650.c | 1665 ++++++++++++++++++++++++++++++++++++
 drivers/interconnect/qcom/sm8650.h |  144 ++++
 4 files changed, 1816 insertions(+)

diff --git a/drivers/interconnect/qcom/Kconfig b/drivers/interconnect/qcom/Kconfig
index ab5b19567ec..b105c3a1f9a 100644
--- a/drivers/interconnect/qcom/Kconfig
+++ b/drivers/interconnect/qcom/Kconfig
@@ -4,3 +4,9 @@ config INTERCONNECT_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
index 4369ca07f61..a33a65bd82e 100644
--- a/drivers/interconnect/qcom/Makefile
+++ b/drivers/interconnect/qcom/Makefile
@@ -4,3 +4,4 @@
 #
 
 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/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

-- 
2.34.1


^ permalink raw reply related	[flat|nested] 9+ messages in thread

* [PATCH v5 6/7] ufs: qcom: vote for interconnect bandwidth on probe
  2025-11-20  8:12 [PATCH v5 0/7] Implement the Generic System Interconnect Subsystem for U-Boot Neil Armstrong
                   ` (4 preceding siblings ...)
  2025-11-20  8:12 ` [PATCH v5 5/7] interconnect: add support for the SM8650 SoC Neil Armstrong
@ 2025-11-20  8:12 ` Neil Armstrong
  2025-11-20  8:12 ` [PATCH v5 7/7] qcom_defconfig: enable interconnect for SM8650 Neil Armstrong
  2025-11-20  9:44 ` [PATCH v5 0/7] Implement the Generic System Interconnect Subsystem for U-Boot Neil Armstrong
  7 siblings, 0 replies; 9+ messages in thread
From: Neil Armstrong @ 2025-11-20  8:12 UTC (permalink / raw)
  To: u-boot, Tom Rini, Simon Glass, Casey Connolly
  Cc: u-boot-qcom, Ilias Apalodimas, Jerome Forissier, Dinesh Maniyam,
	Sughosh Ganu, Mattijs Korpershoek, Hrushikesh Salunke,
	Anurag Dutta, Svyatoslav Ryhel, Ion Agorria,
	Kory Maincent (TI.com), Mario Six, Christian Marangi,
	Heiko Schocher, Guillaume La Roque, Miquel Raynal, Peng Fan,
	Michal Simek, Heinrich Schuchardt, Pieter Van Trappen,
	Alexander Graf, Quentin Schulz, Rasmus Villemoes, Greg Malysa,
	Arturs Artamonovs, Vasileios Bimpikas, Utsav Agarwal, Ian Roberts,
	Nathan Barrett-Morrison, Alif Zakuan Yuslaimi, Stefan Roese,
	Sumit Garg, Bhupesh Sharma, Neha Malcom Francis, Marek Vasut,
	Varadarajan Narayanan, Sam Day, Sam Protsenko, Marek Vasut,
	Alper Nebi Yasak, Oliver Gaskell, Paul Sajna, Neil Armstrong

Add Interconnect voting on the UFS probe to ask for the largest
bandwidth possible.

Signed-off-by: Neil Armstrong <neil.armstrong@linaro.org>
---
 drivers/ufs/ufs-qcom.c | 13 +++++++++++++
 1 file changed, 13 insertions(+)

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) {

-- 
2.34.1


^ permalink raw reply related	[flat|nested] 9+ messages in thread

* [PATCH v5 7/7] qcom_defconfig: enable interconnect for SM8650
  2025-11-20  8:12 [PATCH v5 0/7] Implement the Generic System Interconnect Subsystem for U-Boot Neil Armstrong
                   ` (5 preceding siblings ...)
  2025-11-20  8:12 ` [PATCH v5 6/7] ufs: qcom: vote for interconnect bandwidth on probe Neil Armstrong
@ 2025-11-20  8:12 ` Neil Armstrong
  2025-11-20  9:44 ` [PATCH v5 0/7] Implement the Generic System Interconnect Subsystem for U-Boot Neil Armstrong
  7 siblings, 0 replies; 9+ messages in thread
From: Neil Armstrong @ 2025-11-20  8:12 UTC (permalink / raw)
  To: u-boot, Tom Rini, Simon Glass, Casey Connolly
  Cc: u-boot-qcom, Ilias Apalodimas, Jerome Forissier, Dinesh Maniyam,
	Sughosh Ganu, Mattijs Korpershoek, Hrushikesh Salunke,
	Anurag Dutta, Svyatoslav Ryhel, Ion Agorria,
	Kory Maincent (TI.com), Mario Six, Christian Marangi,
	Heiko Schocher, Guillaume La Roque, Miquel Raynal, Peng Fan,
	Michal Simek, Heinrich Schuchardt, Pieter Van Trappen,
	Alexander Graf, Quentin Schulz, Rasmus Villemoes, Greg Malysa,
	Arturs Artamonovs, Vasileios Bimpikas, Utsav Agarwal, Ian Roberts,
	Nathan Barrett-Morrison, Alif Zakuan Yuslaimi, Stefan Roese,
	Sumit Garg, Bhupesh Sharma, Neha Malcom Francis, Marek Vasut,
	Varadarajan Narayanan, Sam Day, Sam Protsenko, Marek Vasut,
	Alper Nebi Yasak, Oliver Gaskell, Paul Sajna, Neil Armstrong

Enable the Interconnect drivers for SM8650

Signed-off-by: Neil Armstrong <neil.armstrong@linaro.org>
---
 configs/qcom_defconfig | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/configs/qcom_defconfig b/configs/qcom_defconfig
index e686ea857b9..a56e1282821 100644
--- a/configs/qcom_defconfig
+++ b/configs/qcom_defconfig
@@ -87,6 +87,9 @@ CONFIG_SYS_I2C_GENI=y
 CONFIG_I2C_MUX=y
 CONFIG_DM_KEYBOARD=y
 CONFIG_BUTTON_KEYBOARD=y
+CONFIG_INTERCONNECT=y
+CONFIG_INTERCONNECT_QCOM_RPMH=y
+CONFIG_INTERCONNECT_QCOM_SM8650=y
 CONFIG_IOMMU=y
 CONFIG_QCOM_HYP_SMMU=y
 CONFIG_MISC=y

-- 
2.34.1


^ permalink raw reply related	[flat|nested] 9+ messages in thread

* Re: [PATCH v5 0/7] Implement the Generic System Interconnect Subsystem for U-Boot
  2025-11-20  8:12 [PATCH v5 0/7] Implement the Generic System Interconnect Subsystem for U-Boot Neil Armstrong
                   ` (6 preceding siblings ...)
  2025-11-20  8:12 ` [PATCH v5 7/7] qcom_defconfig: enable interconnect for SM8650 Neil Armstrong
@ 2025-11-20  9:44 ` Neil Armstrong
  7 siblings, 0 replies; 9+ messages in thread
From: Neil Armstrong @ 2025-11-20  9:44 UTC (permalink / raw)
  To: u-boot, Tom Rini, Simon Glass, Casey Connolly, Neil Armstrong
  Cc: u-boot-qcom, Ilias Apalodimas, Jerome Forissier, Dinesh Maniyam,
	Sughosh Ganu, Mattijs Korpershoek, Hrushikesh Salunke,
	Anurag Dutta, Svyatoslav Ryhel, Ion Agorria,
	Kory Maincent (TI.com), Mario Six, Christian Marangi,
	Heiko Schocher, Guillaume La Roque, Miquel Raynal, Peng Fan,
	Michal Simek, Heinrich Schuchardt, Pieter Van Trappen,
	Alexander Graf, Quentin Schulz, Rasmus Villemoes, Greg Malysa,
	Arturs Artamonovs, Vasileios Bimpikas, Utsav Agarwal, Ian Roberts,
	Nathan Barrett-Morrison, Alif Zakuan Yuslaimi, Stefan Roese,
	Sumit Garg, Bhupesh Sharma, Neha Malcom Francis, Marek Vasut,
	Varadarajan Narayanan, Sam Day, Sam Protsenko, Marek Vasut,
	Alper Nebi Yasak, Oliver Gaskell, Paul Sajna


On Thu, 20 Nov 2025 09:12:51 +0100, Neil Armstrong wrote:
> Let's introduce the Generic System Interconnect subsystem based on
> the counterpart Linux framework which is used to vote for bandwidth
> across multiple SoC busses.
> 
> Documentation for the Linux Generic System Interconnect Subsystem can
> be found at [1].
> 
> [...]

Applied, thanks!

[1/7] Introduce the Generic System Interconnect Subsystem
      commit: 60a99d5ca374c83c0118f2634a7fcf4cc707b965
[2/7] interconnect: add DM test suite
      commit: 9ab7163710d390672a7325cd0980ce32e2a8191a
[3/7] MAINTAINERS: add myself as Maintainer of the Generic System Interconnect Subsystem
      commit: 3949b33049393c66140782adb2579c2fc09839bd
[4/7] interconnect: add support for the Qualcomm RPMh helpers
      commit: 591b9e14192f74a004e0be70c395c30f06810fca
[5/7] interconnect: add support for the SM8650 SoC
      commit: 8ec059c5ce7de20c22b8501bd4cbcb80f1dab781
[6/7] ufs: qcom: vote for interconnect bandwidth on probe
      commit: e1461a0f538152093c87b33022b629898294cff1
[7/7] qcom_defconfig: enable interconnect for SM8650
      commit: fb3db4aa8df2ae5093a5c1580b9cd5789c248de4

Best regards,
-- 
Neil Armstrong <neil.armstrong@linaro.org>

^ permalink raw reply	[flat|nested] 9+ messages in thread

end of thread, other threads:[~2025-11-20  9:45 UTC | newest]

Thread overview: 9+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2025-11-20  8:12 [PATCH v5 0/7] Implement the Generic System Interconnect Subsystem for U-Boot Neil Armstrong
2025-11-20  8:12 ` [PATCH v5 1/7] Introduce the Generic System Interconnect Subsystem Neil Armstrong
2025-11-20  8:12 ` [PATCH v5 2/7] interconnect: add DM test suite Neil Armstrong
2025-11-20  8:12 ` [PATCH v5 3/7] MAINTAINERS: add myself as Maintainer of the Generic System Interconnect Subsystem Neil Armstrong
2025-11-20  8:12 ` [PATCH v5 4/7] interconnect: add support for the Qualcomm RPMh helpers Neil Armstrong
2025-11-20  8:12 ` [PATCH v5 5/7] interconnect: add support for the SM8650 SoC Neil Armstrong
2025-11-20  8:12 ` [PATCH v5 6/7] ufs: qcom: vote for interconnect bandwidth on probe Neil Armstrong
2025-11-20  8:12 ` [PATCH v5 7/7] qcom_defconfig: enable interconnect for SM8650 Neil Armstrong
2025-11-20  9:44 ` [PATCH v5 0/7] Implement the Generic System Interconnect Subsystem for U-Boot Neil Armstrong

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox