// SPDX-License-Identifier: GPL-2.0 /* * Copyright (c) 2025 Linaro Limited */ #include #include #include #include #include #include #include #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, };