Lines Matching +full:interconnect +full:- +full:0
1 // SPDX-License-Identifier: GPL-2.0
3 * Interconnect framework core driver
5 * Copyright (c) 2017-2019, Linaro Ltd.
13 #include <linux/interconnect.h>
14 #include <linux/interconnect-provider.h>
41 seq_printf(s, "%-42s %12u %12u\n", in icc_summary_show_one()
42 n->name, n->avg_bw, n->peak_bw); in icc_summary_show_one()
50 seq_puts(s, "--------------------------------------------------------------------\n"); in icc_summary_show()
57 list_for_each_entry(n, &provider->nodes, node_list) { in icc_summary_show()
61 hlist_for_each_entry(r, &n->req_list, req_node) { in icc_summary_show()
62 u32 avg_bw = 0, peak_bw = 0; in icc_summary_show()
64 if (!r->dev) in icc_summary_show()
67 if (r->enabled) { in icc_summary_show()
68 avg_bw = r->avg_bw; in icc_summary_show()
69 peak_bw = r->peak_bw; in icc_summary_show()
72 seq_printf(s, " %-27s %12u %12u %12u\n", in icc_summary_show()
73 dev_name(r->dev), r->tag, avg_bw, peak_bw); in icc_summary_show()
80 return 0; in icc_summary_show()
87 seq_printf(s, "%s\"%d:%s\" -> \"%d:%s\"\n", in icc_graph_show_link()
89 n->id, n->name, m->id, m->name); in icc_graph_show_link()
95 n->id, n->name, n->id, n->name); in icc_graph_show_node()
96 seq_printf(s, "\n\t\t\t|avg_bw=%ukBps", n->avg_bw); in icc_graph_show_node()
97 seq_printf(s, "\n\t\t\t|peak_bw=%ukBps", n->peak_bw); in icc_graph_show_node()
105 int cluster_index = 0; in icc_graph_show()
112 cluster_index = 0; in icc_graph_show()
115 if (provider->dev) in icc_graph_show()
117 dev_name(provider->dev)); in icc_graph_show()
120 list_for_each_entry(n, &provider->nodes, node_list) in icc_graph_show()
124 list_for_each_entry(n, &provider->nodes, node_list) in icc_graph_show()
125 for (i = 0; i < n->num_links; ++i) in icc_graph_show()
126 if (n->provider == n->links[i]->provider) in icc_graph_show()
128 n->links[i]); in icc_graph_show()
135 list_for_each_entry(n, &provider->nodes, node_list) in icc_graph_show()
136 for (i = 0; i < n->num_links; ++i) in icc_graph_show()
137 if (n->provider != n->links[i]->provider) in icc_graph_show()
139 n->links[i]); in icc_graph_show()
144 return 0; in icc_graph_show()
159 list_for_each_entry(n, &provider->nodes, node_list) { in node_find_by_name()
160 if (!strcmp(n->name, name)) in node_find_by_name()
177 return ERR_PTR(-ENOMEM); in path_init()
179 path->num_nodes = num_nodes; in path_init()
183 for (i = num_nodes - 1; i >= 0; i--) { in path_init()
184 node->provider->users++; in path_init()
185 hlist_add_head(&path->reqs[i].req_node, &node->req_list); in path_init()
186 path->reqs[i].node = node; in path_init()
187 path->reqs[i].dev = dev; in path_init()
188 path->reqs[i].enabled = true; in path_init()
190 node = node->reverse; in path_init()
201 struct icc_path *path = ERR_PTR(-EPROBE_DEFER); in path_find()
213 list_add(&src->search_list, &traverse_list); in path_find()
214 src->reverse = NULL; in path_find()
224 for (i = 0; i < node->num_links; i++) { in path_find()
225 struct icc_node *tmp = node->links[i]; in path_find()
228 path = ERR_PTR(-ENOENT); in path_find()
232 if (tmp->is_traversed) in path_find()
235 tmp->is_traversed = true; in path_find()
236 tmp->reverse = node; in path_find()
237 list_add_tail(&tmp->search_list, &edge_list); in path_find()
256 n->is_traversed = false; in path_find()
273 struct icc_provider *p = node->provider; in aggregate_requests()
277 node->avg_bw = 0; in aggregate_requests()
278 node->peak_bw = 0; in aggregate_requests()
280 if (p->pre_aggregate) in aggregate_requests()
281 p->pre_aggregate(node); in aggregate_requests()
283 hlist_for_each_entry(r, &node->req_list, req_node) { in aggregate_requests()
284 if (r->enabled) { in aggregate_requests()
285 avg_bw = r->avg_bw; in aggregate_requests()
286 peak_bw = r->peak_bw; in aggregate_requests()
288 avg_bw = 0; in aggregate_requests()
289 peak_bw = 0; in aggregate_requests()
291 p->aggregate(node, r->tag, avg_bw, peak_bw, in aggregate_requests()
292 &node->avg_bw, &node->peak_bw); in aggregate_requests()
296 node->avg_bw = max(node->avg_bw, node->init_avg); in aggregate_requests()
297 node->peak_bw = max(node->peak_bw, node->init_peak); in aggregate_requests()
301 return 0; in aggregate_requests()
308 int ret = -EINVAL; in apply_constraints()
311 for (i = 0; i < path->num_nodes; i++) { in apply_constraints()
312 next = path->reqs[i].node; in apply_constraints()
313 p = next->provider; in apply_constraints()
315 /* both endpoints should be valid master-slave pairs */ in apply_constraints()
316 if (!prev || (p != prev->provider && !p->inter_set)) { in apply_constraints()
322 ret = p->set(prev, next); in apply_constraints()
338 return 0; in icc_std_aggregate()
342 /* of_icc_xlate_onecell() - Translate function using a single index.
343 * @spec: OF phandle args to map into an interconnect node.
347 * interconnect providers that have one device tree node and provide
348 * multiple interconnect nodes. A single cell is used as an index into
356 unsigned int idx = spec->args[0]; in of_icc_xlate_onecell()
358 if (idx >= icc_data->num_nodes) { in of_icc_xlate_onecell()
360 return ERR_PTR(-EINVAL); in of_icc_xlate_onecell()
363 return icc_data->nodes[idx]; in of_icc_xlate_onecell()
368 * of_icc_get_from_provider() - Look-up interconnect node
369 * @spec: OF phandle args to use for look-up
371 * Looks for interconnect provider under the node specified by @spec and if
379 struct icc_node *node = ERR_PTR(-EPROBE_DEFER); in of_icc_get_from_provider()
384 return ERR_PTR(-EINVAL); in of_icc_get_from_provider()
388 if (provider->dev->of_node == spec->np) { in of_icc_get_from_provider()
389 if (provider->xlate_extended) { in of_icc_get_from_provider()
390 data = provider->xlate_extended(spec, provider->data); in of_icc_get_from_provider()
392 node = data->node; in of_icc_get_from_provider()
396 node = provider->xlate(spec, provider->data); in of_icc_get_from_provider()
405 return ERR_PTR(-EINVAL); in of_icc_get_from_provider()
413 return ERR_PTR(-ENOMEM); in of_icc_get_from_provider()
414 data->node = node; in of_icc_get_from_provider()
432 return ERR_PTR(-ENOMEM); in devm_of_icc_get()
447 * of_icc_get_by_index() - get a path handle from a DT node based on index
449 * @idx: interconnect path index
454 * If the interconnect API is disabled, NULL is returned and the consumer
469 if (!dev || !dev->of_node) in of_icc_get_by_index()
470 return ERR_PTR(-ENODEV); in of_icc_get_by_index()
472 np = dev->of_node; in of_icc_get_by_index()
487 "#interconnect-cells", idx * 2, in of_icc_get_by_index()
495 "#interconnect-cells", idx * 2 + 1, in of_icc_get_by_index()
518 path = path_find(dev, src_data->node, dst_data->node); in of_icc_get_by_index()
525 if (src_data->tag && src_data->tag == dst_data->tag) in of_icc_get_by_index()
526 icc_set_tag(path, src_data->tag); in of_icc_get_by_index()
528 path->name = kasprintf(GFP_KERNEL, "%s-%s", in of_icc_get_by_index()
529 src_data->node->name, dst_data->node->name); in of_icc_get_by_index()
530 if (!path->name) { in of_icc_get_by_index()
532 path = ERR_PTR(-ENOMEM); in of_icc_get_by_index()
543 * of_icc_get() - get a path handle from a DT node based on name
545 * @name: interconnect path name
550 * If the interconnect API is disabled, NULL is returned and the consumer
560 int idx = 0; in of_icc_get()
562 if (!dev || !dev->of_node) in of_icc_get()
563 return ERR_PTR(-ENODEV); in of_icc_get()
565 np = dev->of_node; in of_icc_get()
580 idx = of_property_match_string(np, "interconnect-names", name); in of_icc_get()
581 if (idx < 0) in of_icc_get()
590 * icc_get() - get a path handle between two endpoints
605 struct icc_path *path = ERR_PTR(-EPROBE_DEFER); in icc_get()
627 path->name = kasprintf(GFP_KERNEL, "%s-%s", src_node->name, dst_node->name); in icc_get()
628 if (!path->name) { in icc_get()
630 path = ERR_PTR(-ENOMEM); in icc_get()
638 * icc_set_tag() - set an optional tag on a path
654 for (i = 0; i < path->num_nodes; i++) in icc_set_tag()
655 path->reqs[i].tag = tag; in icc_set_tag()
662 * icc_get_name() - Get name of the icc path
663 * @path: interconnect path
665 * This function is used by an interconnect consumer to get the name of the icc
675 return path->name; in icc_get_name()
680 * icc_set_bw() - set bandwidth constraints on an interconnect path
681 * @path: interconnect path
685 * This function is used by an interconnect consumer to express its own needs
692 * Returns 0 on success, or an appropriate error code otherwise.
702 return 0; in icc_set_bw()
704 if (WARN_ON(IS_ERR(path) || !path->num_nodes)) in icc_set_bw()
705 return -EINVAL; in icc_set_bw()
709 old_avg = path->reqs[0].avg_bw; in icc_set_bw()
710 old_peak = path->reqs[0].peak_bw; in icc_set_bw()
712 for (i = 0; i < path->num_nodes; i++) { in icc_set_bw()
713 node = path->reqs[i].node; in icc_set_bw()
716 path->reqs[i].avg_bw = avg_bw; in icc_set_bw()
717 path->reqs[i].peak_bw = peak_bw; in icc_set_bw()
727 pr_debug("interconnect: error applying constraints (%d)\n", in icc_set_bw()
730 for (i = 0; i < path->num_nodes; i++) { in icc_set_bw()
731 node = path->reqs[i].node; in icc_set_bw()
732 path->reqs[i].avg_bw = old_avg; in icc_set_bw()
733 path->reqs[i].peak_bw = old_peak; in icc_set_bw()
752 return 0; in __icc_enable()
754 if (WARN_ON(IS_ERR(path) || !path->num_nodes)) in __icc_enable()
755 return -EINVAL; in __icc_enable()
759 for (i = 0; i < path->num_nodes; i++) in __icc_enable()
760 path->reqs[i].enabled = enable; in __icc_enable()
764 return icc_set_bw(path, path->reqs[0].avg_bw, in __icc_enable()
765 path->reqs[0].peak_bw); in __icc_enable()
781 * icc_put() - release the reference to the icc_path
782 * @path: interconnect path
785 * no longer needed. The constraints will be re-aggregated.
796 ret = icc_set_bw(path, 0, 0); in icc_put()
803 for (i = 0; i < path->num_nodes; i++) { in icc_put()
804 node = path->reqs[i].node; in icc_put()
805 hlist_del(&path->reqs[i].req_node); in icc_put()
806 if (!WARN_ON(!node->provider->users)) in icc_put()
807 node->provider->users--; in icc_put()
813 kfree(path->name); in icc_put()
823 return ERR_PTR(-EINVAL); in icc_node_create_nolock()
832 return ERR_PTR(-ENOMEM); in icc_node_create_nolock()
836 id = idr_alloc(&icc_idr, node, ICC_DYN_ID_START, 0, GFP_KERNEL); in icc_node_create_nolock()
840 if (id < 0) { in icc_node_create_nolock()
846 node->id = id; in icc_node_create_nolock()
852 * icc_node_create_dyn() - create a node with dynamic id
871 * icc_node_create() - create a node
891 * icc_node_destroy() - destroy a node
902 idr_remove(&icc_idr, node->id); in icc_node_destroy()
903 WARN_ON(!hlist_empty(&node->req_list)); in icc_node_destroy()
911 kfree(node->links); in icc_node_destroy()
912 if (node->id >= ICC_DYN_ID_START) in icc_node_destroy()
913 kfree(node->name); in icc_node_destroy()
919 * icc_node_set_name() - set node name
924 * Return: 0 on success, or -ENOMEM on allocation failure
928 if (node->id >= ICC_DYN_ID_START) { in icc_node_set_name()
929 node->name = kasprintf(GFP_KERNEL, "%s@%s", name, in icc_node_set_name()
930 dev_name(provider->dev)); in icc_node_set_name()
931 if (!node->name) in icc_node_set_name()
932 return -ENOMEM; in icc_node_set_name()
934 node->name = name; in icc_node_set_name()
937 return 0; in icc_node_set_name()
942 * icc_link_nodes() - create link between two nodes
947 * interconnect providers and the @dst_node might not exist (if the
952 * Return: 0 on success, or an error code otherwise
957 int ret = 0; in icc_link_nodes()
959 if (!src_node->provider) in icc_link_nodes()
960 return -EINVAL; in icc_link_nodes()
973 new = krealloc(src_node->links, in icc_link_nodes()
974 (src_node->num_links + 1) * sizeof(*src_node->links), in icc_link_nodes()
977 ret = -ENOMEM; in icc_link_nodes()
981 src_node->links = new; in icc_link_nodes()
982 src_node->links[src_node->num_links++] = *dst_node; in icc_link_nodes()
992 * icc_link_create() - create a link between two nodes
997 * interconnect providers and the @dst_id node might not exist (if the
1002 * Return: 0 on success, or an error code otherwise
1008 int ret = 0; in icc_link_create()
1010 if (!node->provider) in icc_link_create()
1011 return -EINVAL; in icc_link_create()
1025 new = krealloc(node->links, in icc_link_create()
1026 (node->num_links + 1) * sizeof(*node->links), in icc_link_create()
1029 ret = -ENOMEM; in icc_link_create()
1033 node->links = new; in icc_link_create()
1034 node->links[node->num_links++] = dst; in icc_link_create()
1044 * icc_node_add() - add interconnect node to interconnect provider
1045 * @node: pointer to the interconnect node
1046 * @provider: pointer to the interconnect provider
1050 if (WARN_ON(node->provider)) in icc_node_add()
1056 node->provider = provider; in icc_node_add()
1057 list_add_tail(&node->node_list, &provider->nodes); in icc_node_add()
1060 if (provider->get_bw) { in icc_node_add()
1061 provider->get_bw(node, &node->init_avg, &node->init_peak); in icc_node_add()
1063 node->init_avg = INT_MAX; in icc_node_add()
1064 node->init_peak = INT_MAX; in icc_node_add()
1066 node->avg_bw = node->init_avg; in icc_node_add()
1067 node->peak_bw = node->init_peak; in icc_node_add()
1069 if (node->avg_bw || node->peak_bw) { in icc_node_add()
1070 if (provider->pre_aggregate) in icc_node_add()
1071 provider->pre_aggregate(node); in icc_node_add()
1073 if (provider->aggregate) in icc_node_add()
1074 provider->aggregate(node, 0, node->init_avg, node->init_peak, in icc_node_add()
1075 &node->avg_bw, &node->peak_bw); in icc_node_add()
1076 if (provider->set) in icc_node_add()
1077 provider->set(node, node); in icc_node_add()
1080 node->avg_bw = 0; in icc_node_add()
1081 node->peak_bw = 0; in icc_node_add()
1089 * icc_node_del() - delete interconnect node from interconnect provider
1090 * @node: pointer to the interconnect node
1096 list_del(&node->node_list); in icc_node_del()
1103 * icc_nodes_remove() - remove all previously added nodes from provider
1104 * @provider: the interconnect provider we are removing nodes from
1106 * Return: 0 on success, or an error code otherwise
1113 return -EINVAL; in icc_nodes_remove()
1115 list_for_each_entry_safe_reverse(n, tmp, &provider->nodes, node_list) { in icc_nodes_remove()
1117 icc_node_destroy(n->id); in icc_nodes_remove()
1120 return 0; in icc_nodes_remove()
1125 * icc_provider_init() - initialize a new interconnect provider
1126 * @provider: the interconnect provider to initialize
1132 WARN_ON(!provider->set); in icc_provider_init()
1134 INIT_LIST_HEAD(&provider->nodes); in icc_provider_init()
1139 * icc_provider_register() - register a new interconnect provider
1140 * @provider: the interconnect provider to register
1142 * Return: 0 on success, or an error code otherwise
1146 if (WARN_ON(!provider->xlate && !provider->xlate_extended)) in icc_provider_register()
1147 return -EINVAL; in icc_provider_register()
1150 list_add_tail(&provider->provider_list, &icc_providers); in icc_provider_register()
1153 dev_dbg(provider->dev, "interconnect provider registered\n"); in icc_provider_register()
1155 return 0; in icc_provider_register()
1160 * icc_provider_deregister() - deregister an interconnect provider
1161 * @provider: the interconnect provider to deregister
1166 WARN_ON(provider->users); in icc_provider_deregister()
1168 list_del(&provider->provider_list); in icc_provider_deregister()
1174 { .compatible = "qcom,sc7180-ipa-virt" },
1175 { .compatible = "qcom,sc8180x-ipa-virt" },
1176 { .compatible = "qcom,sdx55-ipa-virt" },
1177 { .compatible = "qcom,sm8150-ipa-virt" },
1178 { .compatible = "qcom,sm8250-ipa-virt" },
1185 int count = 0; in of_count_icc_providers()
1188 if (of_property_present(child, "#interconnect-cells") && in of_count_icc_providers()
1212 dev_dbg(p->dev, "interconnect provider is in synced state\n"); in icc_sync_state()
1213 list_for_each_entry(n, &p->nodes, node_list) { in icc_sync_state()
1214 if (n->init_avg || n->init_peak) { in icc_sync_state()
1215 n->init_avg = 0; in icc_sync_state()
1216 n->init_peak = 0; in icc_sync_state()
1218 p->set(n, n); in icc_sync_state()
1241 icc_debugfs_dir = debugfs_create_dir("interconnect", NULL); in icc_init()
1249 return 0; in icc_init()