| // 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, |
| }; |