mirror of
https://source.denx.de/u-boot/u-boot.git
synced 2026-06-13 15:03:58 +03:00
Merge tag 'interconnect-next-20251120' of https://source.denx.de/u-boot/custodians/u-boot-snapdragon into next
- Qualcomm RPMh cmd_db_read_slave_id() & cmd_db_read_aux_data() - Initial Interconnect implementation + Qualcomm RPMh support
This commit is contained in:
11
MAINTAINERS
11
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
|
||||
|
||||
@@ -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>;
|
||||
|
||||
19
arch/sandbox/include/asm/interconnect.h
Normal file
19
arch/sandbox/include/asm/interconnect.h
Normal file
@@ -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
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -15,6 +15,7 @@ U-Boot API documentation
|
||||
fs
|
||||
getopt
|
||||
interrupt
|
||||
interconnect
|
||||
i3c
|
||||
led
|
||||
linker_lists
|
||||
|
||||
117
doc/api/interconnect.rst
Normal file
117
doc/api/interconnect.rst
Normal file
@@ -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
|
||||
@@ -60,6 +60,8 @@ source "drivers/i3c/Kconfig"
|
||||
|
||||
source "drivers/input/Kconfig"
|
||||
|
||||
source "drivers/interconnect/Kconfig"
|
||||
|
||||
source "drivers/iommu/Kconfig"
|
||||
|
||||
source "drivers/led/Kconfig"
|
||||
|
||||
@@ -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/
|
||||
|
||||
22
drivers/interconnect/Kconfig
Normal file
22
drivers/interconnect/Kconfig
Normal file
@@ -0,0 +1,22 @@
|
||||
menu "Interconnect Support"
|
||||
|
||||
config INTERCONNECT
|
||||
bool "Enable interconnect support using Driver Model"
|
||||
depends on DM && OF_CONTROL
|
||||
help
|
||||
Enable support for the interconnect driver class. Many SoCs allow
|
||||
bandwidth to be tuned on busses within the SoC.
|
||||
|
||||
if INTERCONNECT
|
||||
|
||||
config INTERCONNECT_SANDBOX
|
||||
bool "Enable interconnect sandbox driver"
|
||||
depends on SANDBOX
|
||||
help
|
||||
Enable support for the interconnect sandbox drivers.
|
||||
|
||||
source "drivers/interconnect/qcom/Kconfig"
|
||||
|
||||
endif
|
||||
|
||||
endmenu
|
||||
8
drivers/interconnect/Makefile
Normal file
8
drivers/interconnect/Makefile
Normal file
@@ -0,0 +1,8 @@
|
||||
# SPDX-License-Identifier: GPL-2.0
|
||||
#
|
||||
# Copyright (c) 2025 Linaro Limited
|
||||
#
|
||||
|
||||
obj-$(CONFIG_$(PHASE_)INTERCONNECT) += interconnect-uclass.o
|
||||
obj-$(CONFIG_$(PHASE_)INTERCONNECT_SANDBOX) += sandbox-interconnect.o sandbox-interconnect-test.o
|
||||
obj-y += qcom/
|
||||
545
drivers/interconnect/interconnect-uclass.c
Normal file
545
drivers/interconnect/interconnect-uclass.c
Normal file
@@ -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),
|
||||
};
|
||||
12
drivers/interconnect/qcom/Kconfig
Normal file
12
drivers/interconnect/qcom/Kconfig
Normal file
@@ -0,0 +1,12 @@
|
||||
config INTERCONNECT_QCOM_RPMH
|
||||
bool "Enable interconnect support for SoCs with RPMh"
|
||||
depends on QCOM_RPMH
|
||||
help
|
||||
Enable support for the interconnect helpers to vote with
|
||||
the RPMh subsystems in Qualcomm SoCs
|
||||
|
||||
config INTERCONNECT_QCOM_SM8650
|
||||
bool "Enable interconnect support for SM8650 SoC"
|
||||
depends on INTERCONNECT_QCOM_RPMH
|
||||
help
|
||||
Enable support for the interconnect driver for the SM8650 SoC.
|
||||
7
drivers/interconnect/qcom/Makefile
Normal file
7
drivers/interconnect/qcom/Makefile
Normal file
@@ -0,0 +1,7 @@
|
||||
# SPDX-License-Identifier: GPL-2.0
|
||||
#
|
||||
# Copyright (c) 2025 Linaro Limited
|
||||
#
|
||||
|
||||
obj-$(CONFIG_$(PHASE_)INTERCONNECT_QCOM_RPMH) += icc-rpmh.o bcm-voter.o
|
||||
obj-$(CONFIG_$(PHASE_)INTERCONNECT_QCOM_SM8650) += sm8650.o
|
||||
340
drivers/interconnect/qcom/bcm-voter.c
Normal file
340
drivers/interconnect/qcom/bcm-voter.c
Normal file
@@ -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,
|
||||
};
|
||||
19
drivers/interconnect/qcom/bcm-voter.h
Normal file
19
drivers/interconnect/qcom/bcm-voter.h
Normal file
@@ -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
|
||||
224
drivers/interconnect/qcom/icc-rpmh.c
Normal file
224
drivers/interconnect/qcom/icc-rpmh.c
Normal file
@@ -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;
|
||||
}
|
||||
130
drivers/interconnect/qcom/icc-rpmh.h
Normal file
130
drivers/interconnect/qcom/icc-rpmh.h
Normal file
@@ -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
|
||||
1665
drivers/interconnect/qcom/sm8650.c
Normal file
1665
drivers/interconnect/qcom/sm8650.c
Normal file
File diff suppressed because it is too large
Load Diff
144
drivers/interconnect/qcom/sm8650.h
Normal file
144
drivers/interconnect/qcom/sm8650.h
Normal file
@@ -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
|
||||
89
drivers/interconnect/sandbox-interconnect-test.c
Normal file
89
drivers/interconnect/sandbox-interconnect-test.c
Normal file
@@ -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),
|
||||
};
|
||||
303
drivers/interconnect/sandbox-interconnect.c
Normal file
303
drivers/interconnect/sandbox-interconnect.c
Normal file
@@ -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,
|
||||
};
|
||||
@@ -183,6 +183,51 @@ u32 cmd_db_read_addr(const char *id)
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(cmd_db_read_addr);
|
||||
|
||||
/**
|
||||
* cmd_db_read_slave_id - Get the slave ID for a given resource address
|
||||
*
|
||||
* @id: Resource id to query the DB for version
|
||||
*
|
||||
* Return: cmd_db_hw_type enum on success, CMD_DB_HW_INVALID on error
|
||||
*/
|
||||
enum cmd_db_hw_type cmd_db_read_slave_id(const char *id)
|
||||
{
|
||||
int ret;
|
||||
const struct entry_header *ent;
|
||||
u32 addr;
|
||||
|
||||
ret = cmd_db_get_header(id, &ent, NULL);
|
||||
if (ret < 0)
|
||||
return CMD_DB_HW_INVALID;
|
||||
|
||||
addr = le32_to_cpu(ent->addr);
|
||||
return (addr >> SLAVE_ID_SHIFT) & SLAVE_ID_MASK;
|
||||
}
|
||||
|
||||
/**
|
||||
* cmd_db_read_aux_data() - Query command db for aux data.
|
||||
*
|
||||
* @id: Resource to retrieve AUX Data on
|
||||
* @len: size of data buffer returned
|
||||
*
|
||||
* Return: pointer to data on success, error pointer otherwise
|
||||
*/
|
||||
const void *cmd_db_read_aux_data(const char *id, size_t *len)
|
||||
{
|
||||
int ret;
|
||||
const struct entry_header *ent;
|
||||
const struct rsc_hdr *rsc_hdr;
|
||||
|
||||
ret = cmd_db_get_header(id, &ent, &rsc_hdr);
|
||||
if (ret)
|
||||
return ERR_PTR(ret);
|
||||
|
||||
if (len)
|
||||
*len = le16_to_cpu(ent->len);
|
||||
|
||||
return rsc_offset(rsc_hdr, ent);
|
||||
}
|
||||
|
||||
static int cmd_db_bind(struct udevice *dev)
|
||||
{
|
||||
void __iomem *base;
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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) */
|
||||
|
||||
136
include/interconnect-uclass.h
Normal file
136
include/interconnect-uclass.h
Normal file
@@ -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
|
||||
155
include/interconnect.h
Normal file
155
include/interconnect.h
Normal file
@@ -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
|
||||
@@ -21,6 +21,8 @@ enum cmd_db_hw_type {
|
||||
|
||||
#if IS_ENABLED(CONFIG_QCOM_COMMAND_DB)
|
||||
u32 cmd_db_read_addr(const char *resource_id);
|
||||
enum cmd_db_hw_type cmd_db_read_slave_id(const char *resource_id);
|
||||
const void *cmd_db_read_aux_data(const char *resource_id, size_t *len);
|
||||
|
||||
#else
|
||||
static inline u32 cmd_db_read_addr(const char *resource_id)
|
||||
|
||||
@@ -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
|
||||
|
||||
195
test/dm/interconnect.c
Normal file
195
test/dm/interconnect.c
Normal file
@@ -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);
|
||||
Reference in New Issue
Block a user