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