diff options
| author | Neil Armstrong <[email protected]> | 2025-11-20 09:12:53 +0100 |
|---|---|---|
| committer | Neil Armstrong <[email protected]> | 2025-11-20 09:17:58 +0100 |
| commit | 9ab7163710d390672a7325cd0980ce32e2a8191a (patch) | |
| tree | 422be0ecb86bc7ee18f6850e00b663a62209b822 /drivers/interconnect | |
| parent | 60a99d5ca374c83c0118f2634a7fcf4cc707b965 (diff) | |
interconnect: add DM test suite
Add a test suite exercising the whole lifetime and callbacks
of interconnect with a fake 5 providers with a split node graph.
The test suite checks the calculus are right and goes to the correct
nodes, and the lifetime of the node is correct.
Link: https://patch.msgid.link/[email protected]
Signed-off-by: Neil Armstrong <[email protected]>
Diffstat (limited to 'drivers/interconnect')
| -rw-r--r-- | drivers/interconnect/Kconfig | 10 | ||||
| -rw-r--r-- | drivers/interconnect/Makefile | 1 | ||||
| -rw-r--r-- | drivers/interconnect/sandbox-interconnect-test.c | 89 | ||||
| -rw-r--r-- | drivers/interconnect/sandbox-interconnect.c | 303 |
4 files changed, 403 insertions, 0 deletions
diff --git a/drivers/interconnect/Kconfig b/drivers/interconnect/Kconfig index 38d39651ab5..e9f4cc662ae 100644 --- a/drivers/interconnect/Kconfig +++ b/drivers/interconnect/Kconfig @@ -7,4 +7,14 @@ config INTERCONNECT Enable support for the interconnect driver class. Many SoCs allow bandwidth to be tuned on busses within the SoC. +if INTERCONNECT + +config INTERCONNECT_SANDBOX + bool "Enable interconnect sandbox driver" + depends on SANDBOX + help + Enable support for the interconnect sandbox drivers. + +endif + endmenu diff --git a/drivers/interconnect/Makefile b/drivers/interconnect/Makefile index 1f276e98051..04b18589b7f 100644 --- a/drivers/interconnect/Makefile +++ b/drivers/interconnect/Makefile @@ -4,3 +4,4 @@ # obj-$(CONFIG_$(PHASE_)INTERCONNECT) += interconnect-uclass.o +obj-$(CONFIG_$(PHASE_)INTERCONNECT_SANDBOX) += sandbox-interconnect.o sandbox-interconnect-test.o diff --git a/drivers/interconnect/sandbox-interconnect-test.c b/drivers/interconnect/sandbox-interconnect-test.c new file mode 100644 index 00000000000..ff5f327f6da --- /dev/null +++ b/drivers/interconnect/sandbox-interconnect-test.c @@ -0,0 +1,89 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2025 Linaro Limited + */ + +#include <dm.h> +#include <malloc.h> +#include <interconnect.h> +#include <asm/io.h> +#include <linux/err.h> + +struct sandbox_interconnect_test { + struct icc_path *path; +}; + +int sandbox_interconnect_test_get(struct udevice *dev, char *name) +{ + struct sandbox_interconnect_test *priv = dev_get_priv(dev); + + priv->path = of_icc_get(dev, name); + if (IS_ERR(priv->path)) + return PTR_ERR(priv->path); + + if (!priv->path) + return -ENOSYS; + + return 0; +} + +int sandbox_interconnect_test_get_index(struct udevice *dev, int index) +{ + struct sandbox_interconnect_test *priv = dev_get_priv(dev); + + priv->path = of_icc_get_by_index(dev, index); + if (IS_ERR(priv->path)) + return PTR_ERR(priv->path); + + if (!priv->path) + return -ENOSYS; + + return 0; +} + +int sandbox_interconnect_test_enable(struct udevice *dev) +{ + struct sandbox_interconnect_test *priv = dev_get_priv(dev); + + return icc_enable(priv->path); +} + +int sandbox_interconnect_test_disable(struct udevice *dev) +{ + struct sandbox_interconnect_test *priv = dev_get_priv(dev); + + return icc_disable(priv->path); +} + +int sandbox_interconnect_test_set_bw(struct udevice *dev, u32 avg_bw, u32 peak_bw) +{ + struct sandbox_interconnect_test *priv = dev_get_priv(dev); + + return icc_set_bw(priv->path, avg_bw, peak_bw); +} + +int sandbox_interconnect_test_put(struct udevice *dev) +{ + struct sandbox_interconnect_test *priv = dev_get_priv(dev); + int ret; + + ret = icc_put(priv->path); + if (ret) + return ret; + + priv->path = NULL; + + return 0; +} + +static const struct udevice_id sandbox_interconnect_test_ids[] = { + { .compatible = "sandbox,interconnect-test" }, + { } +}; + +U_BOOT_DRIVER(sandbox_interconnect_test) = { + .name = "sandbox_interconnect_test", + .id = UCLASS_MISC, + .of_match = sandbox_interconnect_test_ids, + .priv_auto = sizeof(struct sandbox_interconnect_test), +}; diff --git a/drivers/interconnect/sandbox-interconnect.c b/drivers/interconnect/sandbox-interconnect.c new file mode 100644 index 00000000000..0fc0e3e0d49 --- /dev/null +++ b/drivers/interconnect/sandbox-interconnect.c @@ -0,0 +1,303 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2025 Linaro Limited + */ + +#include <dm.h> +#include <log.h> +#include <malloc.h> +#include <interconnect-uclass.h> +#include <asm/io.h> +#include <interconnect.h> +#include <linux/err.h> + +#define MAX_LINKS 2 + +struct sandbox_interconnect_node { + const char *name; + unsigned int num_links; + struct sandbox_interconnect_node *links[MAX_LINKS]; + u64 avg_bw; + u64 peak_bw; +}; + +struct sandbox_interconnect_data { + struct sandbox_interconnect_node **nodes; + const unsigned int num_nodes; +}; + +struct sandbox_interconnect_provider { + struct udevice *dev; + struct sandbox_interconnect_data *data; + u64 avg; + u64 peak; +}; + +/* + * Node graph: + * ______________________________ + * [ NODE0 ]--\ / \ /-->[ NODE3 ] + * |-->| NODE2_SLAVE --> NODE2_MASTER |--| + * [ NODE1 ]--/ \______________________________/ \-->[ NODE4 ] + * + */ + +static struct sandbox_interconnect_node node2_slave; +static struct sandbox_interconnect_node node2_master; +static struct sandbox_interconnect_node node3; +static struct sandbox_interconnect_node node4; + +static struct sandbox_interconnect_node node0 = { + .name = "node0", + .num_links = 1, + .links = { &node2_slave }, +}; + +static struct sandbox_interconnect_node node1 = { + .name = "node1", + .num_links = 1, + .links = { &node2_slave }, +}; + +static struct sandbox_interconnect_node node2_slave = { + .name = "node2_slave", + .num_links = 1, + .links = { &node2_master }, +}; + +static struct sandbox_interconnect_node node2_master = { + .name = "node2_master", + .num_links = 2, + .links = { &node3, &node4 }, +}; + +static struct sandbox_interconnect_node node3 = { + .name = "node3", +}; + +static struct sandbox_interconnect_node node4 = { + .name = "node4", +}; + +/* xlate mapping */ +static struct sandbox_interconnect_node *interconnect0_nodes[] = { + [0] = &node0, +}; + +static struct sandbox_interconnect_node *interconnect1_nodes[] = { + [0] = &node1, +}; + +static struct sandbox_interconnect_node *interconnect2_nodes[] = { + [0] = &node2_slave, + [1] = &node2_master, +}; + +static struct sandbox_interconnect_node *interconnect3_nodes[] = { + [0] = &node3, +}; + +static struct sandbox_interconnect_node *interconnect4_nodes[] = { + [0] = &node4, +}; + +static struct sandbox_interconnect_data interconnect0_data = { + .nodes = interconnect0_nodes, + .num_nodes = ARRAY_SIZE(interconnect0_nodes), +}; + +static struct sandbox_interconnect_data interconnect1_data = { + .nodes = interconnect1_nodes, + .num_nodes = ARRAY_SIZE(interconnect1_nodes), +}; + +static struct sandbox_interconnect_data interconnect2_data = { + .nodes = interconnect2_nodes, + .num_nodes = ARRAY_SIZE(interconnect2_nodes), +}; + +static struct sandbox_interconnect_data interconnect3_data = { + .nodes = interconnect3_nodes, + .num_nodes = ARRAY_SIZE(interconnect3_nodes), +}; + +static struct sandbox_interconnect_data interconnect4_data = { + .nodes = interconnect4_nodes, + .num_nodes = ARRAY_SIZE(interconnect4_nodes), +}; + +int sandbox_interconnect_get_bw(struct udevice *dev, u64 *avg, u64 *peak) +{ + struct sandbox_interconnect_provider *priv = dev_get_plat(dev); + + *avg = priv->avg; + *peak = priv->peak; + + return 0; +} + +static int sandbox_interconnect_links_aggregate(struct udevice *dev) +{ + struct sandbox_interconnect_provider *priv = dev_get_plat(dev); + u64 avg = 0, peak = 0; + int i; + + debug("(provider=%s)\n", dev->name); + + for (i = 0; i < priv->data->num_nodes; i++) { + struct sandbox_interconnect_node *sandbox_node = priv->data->nodes[i]; + + if (!sandbox_node) + continue; + + avg += sandbox_node->avg_bw; + peak = max_t(u32, sandbox_node->peak_bw, peak); + } + + priv->avg = avg / priv->data->num_nodes; + priv->peak = peak; + + debug("(provider=%s,avg=%llu peak=%llu)\n", + dev->name, priv->avg, priv->peak); + + return 0; +} + +static int sandbox_interconnect_set(struct icc_node *src, struct icc_node *dst) +{ + struct icc_node *node; + + debug("(src=%s,dst=%s)\n", src->dev->name, dst->dev->name); + + if (!src) + node = dst; + else + node = src; + + return sandbox_interconnect_links_aggregate(node->dev->parent); +} + +static int sandbox_interconnect_aggregate(struct icc_node *node, u32 tag, u32 avg_bw, + u32 peak_bw, u32 *agg_avg, u32 *agg_peak) +{ + struct sandbox_interconnect_node *sandbox_node = node->data; + + debug("(node=%s,tag=%d,avg=%u,peak=%u)\n", + node->dev->name, tag, avg_bw, peak_bw); + + sandbox_node->avg_bw += avg_bw; + sandbox_node->peak_bw = max_t(u32, sandbox_node->peak_bw, peak_bw); + + *agg_avg += avg_bw; + *agg_peak = max_t(u32, *agg_peak, peak_bw); + + debug("(node=%s,new avg=%llu,new peak=%llu)\n", + node->dev->name, sandbox_node->avg_bw, sandbox_node->peak_bw); + + return 0; +} + +static void sandbox_interconnect_pre_aggregate(struct icc_node *node) +{ + struct sandbox_interconnect_node *sandbox_node = node->data; + + debug("(node=%s)\n", node->dev->name); + + sandbox_node->avg_bw = 0; + sandbox_node->peak_bw = 0; +} + +static struct icc_node *sandbox_interconnect_xlate(struct udevice *dev, + const struct ofnode_phandle_args *spec) +{ + struct icc_provider *plat = dev_get_uclass_plat(dev); + unsigned int idx = spec->args[0]; + + debug("(dev=%s)\n", dev->name); + + if (idx >= plat->xlate_num_nodes) { + pr_err("%s: invalid index %u\n", __func__, idx); + return ERR_PTR(-EINVAL); + } + + return plat->xlate_nodes[idx]; +} + +static int sandbox_interconnect_bind(struct udevice *dev) +{ + struct sandbox_interconnect_provider *priv = dev_get_plat(dev); + struct icc_provider *plat = dev_get_uclass_plat(dev); + size_t i; + + debug("(dev=%s)\n", dev->name); + + priv->data = (struct sandbox_interconnect_data *)dev_get_driver_data(dev); + if (!priv->data) + return -EINVAL; + + plat->xlate_num_nodes = priv->data->num_nodes; + plat->xlate_nodes = calloc(sizeof(struct icc_node *), priv->data->num_nodes); + if (!plat->xlate_nodes) + return -ENOMEM; + + priv->dev = dev; + + for (i = 0; i < priv->data->num_nodes; i++) { + struct sandbox_interconnect_node *sandbox_node; + struct icc_node *node; + int j; + + sandbox_node = priv->data->nodes[i]; + if (!sandbox_node) + continue; + + node = icc_node_create(dev, (ulong)sandbox_node, + sandbox_node->name); + if (IS_ERR(node)) + return PTR_ERR(node); + + node->data = sandbox_node; + + for (j = 0; j < sandbox_node->num_links; ++j) + icc_link_create(node, (ulong)sandbox_node->links[j]); + + plat->xlate_nodes[i] = node; + } + + return 0; +} + +static int sandbox_interconnect_unbind(struct udevice *dev) +{ + struct icc_provider *plat = dev_get_uclass_plat(dev); + + free(plat->xlate_nodes); + + return 0; +} + +static const struct udevice_id sandbox_interconnect_ids[] = { + { .compatible = "sandbox,interconnect0", .data = (ulong)&interconnect0_data, }, + { .compatible = "sandbox,interconnect1", .data = (ulong)&interconnect1_data, }, + { .compatible = "sandbox,interconnect2", .data = (ulong)&interconnect2_data, }, + { .compatible = "sandbox,interconnect3", .data = (ulong)&interconnect3_data, }, + { .compatible = "sandbox,interconnect4", .data = (ulong)&interconnect4_data, }, + { } +}; + +static struct interconnect_ops sandbox_interconnect_ops = { + .of_xlate = sandbox_interconnect_xlate, + .set = sandbox_interconnect_set, + .pre_aggregate = sandbox_interconnect_pre_aggregate, + .aggregate = sandbox_interconnect_aggregate, +}; + +U_BOOT_DRIVER(sandbox_interconnect) = { + .name = "sandbox_interconnect", + .id = UCLASS_INTERCONNECT, + .of_match = sandbox_interconnect_ids, + .bind = sandbox_interconnect_bind, + .unbind = sandbox_interconnect_unbind, + .plat_auto = sizeof(struct sandbox_interconnect_provider), + .ops = &sandbox_interconnect_ops, +}; |
