]> git.proxmox.com Git - mirror_ubuntu-focal-kernel.git/blame - drivers/interconnect/core.c
interconnect: Allow endpoints translation via DT
[mirror_ubuntu-focal-kernel.git] / drivers / interconnect / core.c
CommitLineData
11f1ceca
GD
1// SPDX-License-Identifier: GPL-2.0
2/*
3 * Interconnect framework core driver
4 *
5 * Copyright (c) 2017-2019, Linaro Ltd.
6 * Author: Georgi Djakov <georgi.djakov@linaro.org>
7 */
8
9#include <linux/device.h>
10#include <linux/idr.h>
11#include <linux/init.h>
12#include <linux/interconnect.h>
13#include <linux/interconnect-provider.h>
14#include <linux/list.h>
15#include <linux/module.h>
16#include <linux/mutex.h>
17#include <linux/slab.h>
87e3031b 18#include <linux/of.h>
11f1ceca
GD
19#include <linux/overflow.h>
20
21static DEFINE_IDR(icc_idr);
22static LIST_HEAD(icc_providers);
23static DEFINE_MUTEX(icc_lock);
24
25/**
26 * struct icc_req - constraints that are attached to each node
27 * @req_node: entry in list of requests for the particular @node
28 * @node: the interconnect node to which this constraint applies
29 * @dev: reference to the device that sets the constraints
30 * @avg_bw: an integer describing the average bandwidth in kBps
31 * @peak_bw: an integer describing the peak bandwidth in kBps
32 */
33struct icc_req {
34 struct hlist_node req_node;
35 struct icc_node *node;
36 struct device *dev;
37 u32 avg_bw;
38 u32 peak_bw;
39};
40
41/**
42 * struct icc_path - interconnect path structure
43 * @num_nodes: number of hops (nodes)
44 * @reqs: array of the requests applicable to this path of nodes
45 */
46struct icc_path {
47 size_t num_nodes;
48 struct icc_req reqs[];
49};
50
51static struct icc_node *node_find(const int id)
52{
53 return idr_find(&icc_idr, id);
54}
55
56static struct icc_path *path_init(struct device *dev, struct icc_node *dst,
57 ssize_t num_nodes)
58{
59 struct icc_node *node = dst;
60 struct icc_path *path;
61 int i;
62
63 path = kzalloc(struct_size(path, reqs, num_nodes), GFP_KERNEL);
64 if (!path)
65 return ERR_PTR(-ENOMEM);
66
67 path->num_nodes = num_nodes;
68
69 for (i = num_nodes - 1; i >= 0; i--) {
70 node->provider->users++;
71 hlist_add_head(&path->reqs[i].req_node, &node->req_list);
72 path->reqs[i].node = node;
73 path->reqs[i].dev = dev;
74 /* reference to previous node was saved during path traversal */
75 node = node->reverse;
76 }
77
78 return path;
79}
80
81static struct icc_path *path_find(struct device *dev, struct icc_node *src,
82 struct icc_node *dst)
83{
84 struct icc_path *path = ERR_PTR(-EPROBE_DEFER);
85 struct icc_node *n, *node = NULL;
86 struct list_head traverse_list;
87 struct list_head edge_list;
88 struct list_head visited_list;
89 size_t i, depth = 1;
90 bool found = false;
91
92 INIT_LIST_HEAD(&traverse_list);
93 INIT_LIST_HEAD(&edge_list);
94 INIT_LIST_HEAD(&visited_list);
95
96 list_add(&src->search_list, &traverse_list);
97 src->reverse = NULL;
98
99 do {
100 list_for_each_entry_safe(node, n, &traverse_list, search_list) {
101 if (node == dst) {
102 found = true;
103 list_splice_init(&edge_list, &visited_list);
104 list_splice_init(&traverse_list, &visited_list);
105 break;
106 }
107 for (i = 0; i < node->num_links; i++) {
108 struct icc_node *tmp = node->links[i];
109
110 if (!tmp) {
111 path = ERR_PTR(-ENOENT);
112 goto out;
113 }
114
115 if (tmp->is_traversed)
116 continue;
117
118 tmp->is_traversed = true;
119 tmp->reverse = node;
120 list_add_tail(&tmp->search_list, &edge_list);
121 }
122 }
123
124 if (found)
125 break;
126
127 list_splice_init(&traverse_list, &visited_list);
128 list_splice_init(&edge_list, &traverse_list);
129
130 /* count the hops including the source */
131 depth++;
132
133 } while (!list_empty(&traverse_list));
134
135out:
136
137 /* reset the traversed state */
138 list_for_each_entry_reverse(n, &visited_list, search_list)
139 n->is_traversed = false;
140
141 if (found)
142 path = path_init(dev, dst, depth);
143
144 return path;
145}
146
147/*
148 * We want the path to honor all bandwidth requests, so the average and peak
149 * bandwidth requirements from each consumer are aggregated at each node.
150 * The aggregation is platform specific, so each platform can customize it by
151 * implementing its own aggregate() function.
152 */
153
154static int aggregate_requests(struct icc_node *node)
155{
156 struct icc_provider *p = node->provider;
157 struct icc_req *r;
158
159 node->avg_bw = 0;
160 node->peak_bw = 0;
161
162 hlist_for_each_entry(r, &node->req_list, req_node)
163 p->aggregate(node, r->avg_bw, r->peak_bw,
164 &node->avg_bw, &node->peak_bw);
165
166 return 0;
167}
168
169static int apply_constraints(struct icc_path *path)
170{
171 struct icc_node *next, *prev = NULL;
172 int ret = -EINVAL;
173 int i;
174
175 for (i = 0; i < path->num_nodes; i++) {
176 next = path->reqs[i].node;
177
178 /*
179 * Both endpoints should be valid master-slave pairs of the
180 * same interconnect provider that will be configured.
181 */
182 if (!prev || next->provider != prev->provider) {
183 prev = next;
184 continue;
185 }
186
187 /* set the constraints */
188 ret = next->provider->set(prev, next);
189 if (ret)
190 goto out;
191
192 prev = next;
193 }
194out:
195 return ret;
196}
197
87e3031b
GD
198/* of_icc_xlate_onecell() - Translate function using a single index.
199 * @spec: OF phandle args to map into an interconnect node.
200 * @data: private data (pointer to struct icc_onecell_data)
201 *
202 * This is a generic translate function that can be used to model simple
203 * interconnect providers that have one device tree node and provide
204 * multiple interconnect nodes. A single cell is used as an index into
205 * an array of icc nodes specified in the icc_onecell_data struct when
206 * registering the provider.
207 */
208struct icc_node *of_icc_xlate_onecell(struct of_phandle_args *spec,
209 void *data)
210{
211 struct icc_onecell_data *icc_data = data;
212 unsigned int idx = spec->args[0];
213
214 if (idx >= icc_data->num_nodes) {
215 pr_err("%s: invalid index %u\n", __func__, idx);
216 return ERR_PTR(-EINVAL);
217 }
218
219 return icc_data->nodes[idx];
220}
221EXPORT_SYMBOL_GPL(of_icc_xlate_onecell);
222
223/**
224 * of_icc_get_from_provider() - Look-up interconnect node
225 * @spec: OF phandle args to use for look-up
226 *
227 * Looks for interconnect provider under the node specified by @spec and if
228 * found, uses xlate function of the provider to map phandle args to node.
229 *
230 * Returns a valid pointer to struct icc_node on success or ERR_PTR()
231 * on failure.
232 */
233static struct icc_node *of_icc_get_from_provider(struct of_phandle_args *spec)
234{
235 struct icc_node *node = ERR_PTR(-EPROBE_DEFER);
236 struct icc_provider *provider;
237
238 if (!spec || spec->args_count != 1)
239 return ERR_PTR(-EINVAL);
240
241 mutex_lock(&icc_lock);
242 list_for_each_entry(provider, &icc_providers, provider_list) {
243 if (provider->dev->of_node == spec->np)
244 node = provider->xlate(spec, provider->data);
245 if (!IS_ERR(node))
246 break;
247 }
248 mutex_unlock(&icc_lock);
249
250 return node;
251}
252
253/**
254 * of_icc_get() - get a path handle from a DT node based on name
255 * @dev: device pointer for the consumer device
256 * @name: interconnect path name
257 *
258 * This function will search for a path between two endpoints and return an
259 * icc_path handle on success. Use icc_put() to release constraints when they
260 * are not needed anymore.
261 * If the interconnect API is disabled, NULL is returned and the consumer
262 * drivers will still build. Drivers are free to handle this specifically,
263 * but they don't have to.
264 *
265 * Return: icc_path pointer on success or ERR_PTR() on error. NULL is returned
266 * when the API is disabled or the "interconnects" DT property is missing.
267 */
268struct icc_path *of_icc_get(struct device *dev, const char *name)
269{
270 struct icc_path *path = ERR_PTR(-EPROBE_DEFER);
271 struct icc_node *src_node, *dst_node;
272 struct device_node *np = NULL;
273 struct of_phandle_args src_args, dst_args;
274 int idx = 0;
275 int ret;
276
277 if (!dev || !dev->of_node)
278 return ERR_PTR(-ENODEV);
279
280 np = dev->of_node;
281
282 /*
283 * When the consumer DT node do not have "interconnects" property
284 * return a NULL path to skip setting constraints.
285 */
286 if (!of_find_property(np, "interconnects", NULL))
287 return NULL;
288
289 /*
290 * We use a combination of phandle and specifier for endpoint. For now
291 * lets support only global ids and extend this in the future if needed
292 * without breaking DT compatibility.
293 */
294 if (name) {
295 idx = of_property_match_string(np, "interconnect-names", name);
296 if (idx < 0)
297 return ERR_PTR(idx);
298 }
299
300 ret = of_parse_phandle_with_args(np, "interconnects",
301 "#interconnect-cells", idx * 2,
302 &src_args);
303 if (ret)
304 return ERR_PTR(ret);
305
306 of_node_put(src_args.np);
307
308 ret = of_parse_phandle_with_args(np, "interconnects",
309 "#interconnect-cells", idx * 2 + 1,
310 &dst_args);
311 if (ret)
312 return ERR_PTR(ret);
313
314 of_node_put(dst_args.np);
315
316 src_node = of_icc_get_from_provider(&src_args);
317
318 if (IS_ERR(src_node)) {
319 if (PTR_ERR(src_node) != -EPROBE_DEFER)
320 dev_err(dev, "error finding src node: %ld\n",
321 PTR_ERR(src_node));
322 return ERR_CAST(src_node);
323 }
324
325 dst_node = of_icc_get_from_provider(&dst_args);
326
327 if (IS_ERR(dst_node)) {
328 if (PTR_ERR(dst_node) != -EPROBE_DEFER)
329 dev_err(dev, "error finding dst node: %ld\n",
330 PTR_ERR(dst_node));
331 return ERR_CAST(dst_node);
332 }
333
334 mutex_lock(&icc_lock);
335 path = path_find(dev, src_node, dst_node);
336 if (IS_ERR(path))
337 dev_err(dev, "%s: invalid path=%ld\n", __func__, PTR_ERR(path));
338 mutex_unlock(&icc_lock);
339
340 return path;
341}
342EXPORT_SYMBOL_GPL(of_icc_get);
343
11f1ceca
GD
344/**
345 * icc_set_bw() - set bandwidth constraints on an interconnect path
346 * @path: reference to the path returned by icc_get()
347 * @avg_bw: average bandwidth in kilobytes per second
348 * @peak_bw: peak bandwidth in kilobytes per second
349 *
350 * This function is used by an interconnect consumer to express its own needs
351 * in terms of bandwidth for a previously requested path between two endpoints.
352 * The requests are aggregated and each node is updated accordingly. The entire
353 * path is locked by a mutex to ensure that the set() is completed.
354 * The @path can be NULL when the "interconnects" DT properties is missing,
355 * which will mean that no constraints will be set.
356 *
357 * Returns 0 on success, or an appropriate error code otherwise.
358 */
359int icc_set_bw(struct icc_path *path, u32 avg_bw, u32 peak_bw)
360{
361 struct icc_node *node;
362 size_t i;
363 int ret;
364
365 if (!path)
366 return 0;
367
368 mutex_lock(&icc_lock);
369
370 for (i = 0; i < path->num_nodes; i++) {
371 node = path->reqs[i].node;
372
373 /* update the consumer request for this path */
374 path->reqs[i].avg_bw = avg_bw;
375 path->reqs[i].peak_bw = peak_bw;
376
377 /* aggregate requests for this node */
378 aggregate_requests(node);
379 }
380
381 ret = apply_constraints(path);
382 if (ret)
383 pr_debug("interconnect: error applying constraints (%d)\n",
384 ret);
385
386 mutex_unlock(&icc_lock);
387
388 return ret;
389}
390EXPORT_SYMBOL_GPL(icc_set_bw);
391
392/**
393 * icc_get() - return a handle for path between two endpoints
394 * @dev: the device requesting the path
395 * @src_id: source device port id
396 * @dst_id: destination device port id
397 *
398 * This function will search for a path between two endpoints and return an
399 * icc_path handle on success. Use icc_put() to release
400 * constraints when they are not needed anymore.
401 * If the interconnect API is disabled, NULL is returned and the consumer
402 * drivers will still build. Drivers are free to handle this specifically,
403 * but they don't have to.
404 *
405 * Return: icc_path pointer on success, ERR_PTR() on error or NULL if the
406 * interconnect API is disabled.
407 */
408struct icc_path *icc_get(struct device *dev, const int src_id, const int dst_id)
409{
410 struct icc_node *src, *dst;
411 struct icc_path *path = ERR_PTR(-EPROBE_DEFER);
412
413 mutex_lock(&icc_lock);
414
415 src = node_find(src_id);
416 if (!src)
417 goto out;
418
419 dst = node_find(dst_id);
420 if (!dst)
421 goto out;
422
423 path = path_find(dev, src, dst);
424 if (IS_ERR(path))
425 dev_err(dev, "%s: invalid path=%ld\n", __func__, PTR_ERR(path));
426
427out:
428 mutex_unlock(&icc_lock);
429 return path;
430}
431EXPORT_SYMBOL_GPL(icc_get);
432
433/**
434 * icc_put() - release the reference to the icc_path
435 * @path: interconnect path
436 *
437 * Use this function to release the constraints on a path when the path is
438 * no longer needed. The constraints will be re-aggregated.
439 */
440void icc_put(struct icc_path *path)
441{
442 struct icc_node *node;
443 size_t i;
444 int ret;
445
446 if (!path || WARN_ON(IS_ERR(path)))
447 return;
448
449 ret = icc_set_bw(path, 0, 0);
450 if (ret)
451 pr_err("%s: error (%d)\n", __func__, ret);
452
453 mutex_lock(&icc_lock);
454 for (i = 0; i < path->num_nodes; i++) {
455 node = path->reqs[i].node;
456 hlist_del(&path->reqs[i].req_node);
457 if (!WARN_ON(!node->provider->users))
458 node->provider->users--;
459 }
460 mutex_unlock(&icc_lock);
461
462 kfree(path);
463}
464EXPORT_SYMBOL_GPL(icc_put);
465
466static struct icc_node *icc_node_create_nolock(int id)
467{
468 struct icc_node *node;
469
470 /* check if node already exists */
471 node = node_find(id);
472 if (node)
473 return node;
474
475 node = kzalloc(sizeof(*node), GFP_KERNEL);
476 if (!node)
477 return ERR_PTR(-ENOMEM);
478
479 id = idr_alloc(&icc_idr, node, id, id + 1, GFP_KERNEL);
480 if (id < 0) {
481 WARN(1, "%s: couldn't get idr\n", __func__);
482 kfree(node);
483 return ERR_PTR(id);
484 }
485
486 node->id = id;
487
488 return node;
489}
490
491/**
492 * icc_node_create() - create a node
493 * @id: node id
494 *
495 * Return: icc_node pointer on success, or ERR_PTR() on error
496 */
497struct icc_node *icc_node_create(int id)
498{
499 struct icc_node *node;
500
501 mutex_lock(&icc_lock);
502
503 node = icc_node_create_nolock(id);
504
505 mutex_unlock(&icc_lock);
506
507 return node;
508}
509EXPORT_SYMBOL_GPL(icc_node_create);
510
511/**
512 * icc_node_destroy() - destroy a node
513 * @id: node id
514 */
515void icc_node_destroy(int id)
516{
517 struct icc_node *node;
518
519 mutex_lock(&icc_lock);
520
521 node = node_find(id);
522 if (node) {
523 idr_remove(&icc_idr, node->id);
524 WARN_ON(!hlist_empty(&node->req_list));
525 }
526
527 mutex_unlock(&icc_lock);
528
529 kfree(node);
530}
531EXPORT_SYMBOL_GPL(icc_node_destroy);
532
533/**
534 * icc_link_create() - create a link between two nodes
535 * @node: source node id
536 * @dst_id: destination node id
537 *
538 * Create a link between two nodes. The nodes might belong to different
539 * interconnect providers and the @dst_id node might not exist (if the
540 * provider driver has not probed yet). So just create the @dst_id node
541 * and when the actual provider driver is probed, the rest of the node
542 * data is filled.
543 *
544 * Return: 0 on success, or an error code otherwise
545 */
546int icc_link_create(struct icc_node *node, const int dst_id)
547{
548 struct icc_node *dst;
549 struct icc_node **new;
550 int ret = 0;
551
552 if (!node->provider)
553 return -EINVAL;
554
555 mutex_lock(&icc_lock);
556
557 dst = node_find(dst_id);
558 if (!dst) {
559 dst = icc_node_create_nolock(dst_id);
560
561 if (IS_ERR(dst)) {
562 ret = PTR_ERR(dst);
563 goto out;
564 }
565 }
566
567 new = krealloc(node->links,
568 (node->num_links + 1) * sizeof(*node->links),
569 GFP_KERNEL);
570 if (!new) {
571 ret = -ENOMEM;
572 goto out;
573 }
574
575 node->links = new;
576 node->links[node->num_links++] = dst;
577
578out:
579 mutex_unlock(&icc_lock);
580
581 return ret;
582}
583EXPORT_SYMBOL_GPL(icc_link_create);
584
585/**
586 * icc_link_destroy() - destroy a link between two nodes
587 * @src: pointer to source node
588 * @dst: pointer to destination node
589 *
590 * Return: 0 on success, or an error code otherwise
591 */
592int icc_link_destroy(struct icc_node *src, struct icc_node *dst)
593{
594 struct icc_node **new;
595 size_t slot;
596 int ret = 0;
597
598 if (IS_ERR_OR_NULL(src))
599 return -EINVAL;
600
601 if (IS_ERR_OR_NULL(dst))
602 return -EINVAL;
603
604 mutex_lock(&icc_lock);
605
606 for (slot = 0; slot < src->num_links; slot++)
607 if (src->links[slot] == dst)
608 break;
609
610 if (WARN_ON(slot == src->num_links)) {
611 ret = -ENXIO;
612 goto out;
613 }
614
615 src->links[slot] = src->links[--src->num_links];
616
617 new = krealloc(src->links, src->num_links * sizeof(*src->links),
618 GFP_KERNEL);
619 if (new)
620 src->links = new;
621
622out:
623 mutex_unlock(&icc_lock);
624
625 return ret;
626}
627EXPORT_SYMBOL_GPL(icc_link_destroy);
628
629/**
630 * icc_node_add() - add interconnect node to interconnect provider
631 * @node: pointer to the interconnect node
632 * @provider: pointer to the interconnect provider
633 */
634void icc_node_add(struct icc_node *node, struct icc_provider *provider)
635{
636 mutex_lock(&icc_lock);
637
638 node->provider = provider;
639 list_add_tail(&node->node_list, &provider->nodes);
640
641 mutex_unlock(&icc_lock);
642}
643EXPORT_SYMBOL_GPL(icc_node_add);
644
645/**
646 * icc_node_del() - delete interconnect node from interconnect provider
647 * @node: pointer to the interconnect node
648 */
649void icc_node_del(struct icc_node *node)
650{
651 mutex_lock(&icc_lock);
652
653 list_del(&node->node_list);
654
655 mutex_unlock(&icc_lock);
656}
657EXPORT_SYMBOL_GPL(icc_node_del);
658
659/**
660 * icc_provider_add() - add a new interconnect provider
661 * @provider: the interconnect provider that will be added into topology
662 *
663 * Return: 0 on success, or an error code otherwise
664 */
665int icc_provider_add(struct icc_provider *provider)
666{
667 if (WARN_ON(!provider->set))
668 return -EINVAL;
87e3031b
GD
669 if (WARN_ON(!provider->xlate))
670 return -EINVAL;
11f1ceca
GD
671
672 mutex_lock(&icc_lock);
673
674 INIT_LIST_HEAD(&provider->nodes);
675 list_add_tail(&provider->provider_list, &icc_providers);
676
677 mutex_unlock(&icc_lock);
678
679 dev_dbg(provider->dev, "interconnect provider added to topology\n");
680
681 return 0;
682}
683EXPORT_SYMBOL_GPL(icc_provider_add);
684
685/**
686 * icc_provider_del() - delete previously added interconnect provider
687 * @provider: the interconnect provider that will be removed from topology
688 *
689 * Return: 0 on success, or an error code otherwise
690 */
691int icc_provider_del(struct icc_provider *provider)
692{
693 mutex_lock(&icc_lock);
694 if (provider->users) {
695 pr_warn("interconnect provider still has %d users\n",
696 provider->users);
697 mutex_unlock(&icc_lock);
698 return -EBUSY;
699 }
700
701 if (!list_empty(&provider->nodes)) {
702 pr_warn("interconnect provider still has nodes\n");
703 mutex_unlock(&icc_lock);
704 return -EBUSY;
705 }
706
707 list_del(&provider->provider_list);
708 mutex_unlock(&icc_lock);
709
710 return 0;
711}
712EXPORT_SYMBOL_GPL(icc_provider_del);
713
714MODULE_AUTHOR("Georgi Djakov <georgi.djakov@linaro.org>");
715MODULE_DESCRIPTION("Interconnect Driver Core");
716MODULE_LICENSE("GPL v2");