xref: /openbmc/u-boot/drivers/core/devres.c (revision 83d290c56fab2d38cd1ab4c4cc7099559c1d5046)
1*83d290c5STom Rini // SPDX-License-Identifier: GPL-2.0+
2608f26c5SMasahiro Yamada /*
3608f26c5SMasahiro Yamada  * Copyright (C) 2015 Masahiro Yamada <yamada.masahiro@socionext.com>
4608f26c5SMasahiro Yamada  *
5608f26c5SMasahiro Yamada  * Based on the original work in Linux by
6608f26c5SMasahiro Yamada  * Copyright (c) 2006  SUSE Linux Products GmbH
7608f26c5SMasahiro Yamada  * Copyright (c) 2006  Tejun Heo <teheo@suse.de>
8608f26c5SMasahiro Yamada  */
9608f26c5SMasahiro Yamada 
10608f26c5SMasahiro Yamada #include <common.h>
11608f26c5SMasahiro Yamada #include <linux/compat.h>
12608f26c5SMasahiro Yamada #include <linux/kernel.h>
13608f26c5SMasahiro Yamada #include <linux/list.h>
14608f26c5SMasahiro Yamada #include <dm/device.h>
1540b6f2d0SMasahiro Yamada #include <dm/root.h>
1640b6f2d0SMasahiro Yamada #include <dm/util.h>
17608f26c5SMasahiro Yamada 
18608f26c5SMasahiro Yamada /**
19608f26c5SMasahiro Yamada  * struct devres - Bookkeeping info for managed device resource
20608f26c5SMasahiro Yamada  * @entry: List to associate this structure with a device
21608f26c5SMasahiro Yamada  * @release: Callback invoked when this resource is released
22608f26c5SMasahiro Yamada  * @probe: Flag to show when this resource was allocated
23608f26c5SMasahiro Yamada 	   (true = probe, false = bind)
24608f26c5SMasahiro Yamada  * @name: Name of release function
25608f26c5SMasahiro Yamada  * @size: Size of resource data
26608f26c5SMasahiro Yamada  * @data: Resource data
27608f26c5SMasahiro Yamada  */
28608f26c5SMasahiro Yamada struct devres {
29608f26c5SMasahiro Yamada 	struct list_head		entry;
30608f26c5SMasahiro Yamada 	dr_release_t			release;
31608f26c5SMasahiro Yamada 	bool				probe;
32608f26c5SMasahiro Yamada #ifdef CONFIG_DEBUG_DEVRES
33608f26c5SMasahiro Yamada 	const char			*name;
34608f26c5SMasahiro Yamada 	size_t				size;
35608f26c5SMasahiro Yamada #endif
36608f26c5SMasahiro Yamada 	unsigned long long		data[];
37608f26c5SMasahiro Yamada };
38608f26c5SMasahiro Yamada 
39608f26c5SMasahiro Yamada #ifdef CONFIG_DEBUG_DEVRES
set_node_dbginfo(struct devres * dr,const char * name,size_t size)40608f26c5SMasahiro Yamada static void set_node_dbginfo(struct devres *dr, const char *name, size_t size)
41608f26c5SMasahiro Yamada {
42608f26c5SMasahiro Yamada 	dr->name = name;
43608f26c5SMasahiro Yamada 	dr->size = size;
44608f26c5SMasahiro Yamada }
45608f26c5SMasahiro Yamada 
devres_log(struct udevice * dev,struct devres * dr,const char * op)46608f26c5SMasahiro Yamada static void devres_log(struct udevice *dev, struct devres *dr,
47608f26c5SMasahiro Yamada 		       const char *op)
48608f26c5SMasahiro Yamada {
49608f26c5SMasahiro Yamada 	printf("%s: DEVRES %3s %p %s (%lu bytes)\n",
50608f26c5SMasahiro Yamada 	       dev->name, op, dr, dr->name, (unsigned long)dr->size);
51608f26c5SMasahiro Yamada }
52608f26c5SMasahiro Yamada #else /* CONFIG_DEBUG_DEVRES */
53608f26c5SMasahiro Yamada #define set_node_dbginfo(dr, n, s)	do {} while (0)
54608f26c5SMasahiro Yamada #define devres_log(dev, dr, op)		do {} while (0)
55608f26c5SMasahiro Yamada #endif
56608f26c5SMasahiro Yamada 
57608f26c5SMasahiro Yamada #if CONFIG_DEBUG_DEVRES
__devres_alloc(dr_release_t release,size_t size,gfp_t gfp,const char * name)58608f26c5SMasahiro Yamada void *__devres_alloc(dr_release_t release, size_t size, gfp_t gfp,
59608f26c5SMasahiro Yamada 		     const char *name)
60608f26c5SMasahiro Yamada #else
61608f26c5SMasahiro Yamada void *_devres_alloc(dr_release_t release, size_t size, gfp_t gfp)
62608f26c5SMasahiro Yamada #endif
63608f26c5SMasahiro Yamada {
64608f26c5SMasahiro Yamada 	size_t tot_size = sizeof(struct devres) + size;
65608f26c5SMasahiro Yamada 	struct devres *dr;
66608f26c5SMasahiro Yamada 
67608f26c5SMasahiro Yamada 	dr = kmalloc(tot_size, gfp);
68608f26c5SMasahiro Yamada 	if (unlikely(!dr))
69608f26c5SMasahiro Yamada 		return NULL;
70608f26c5SMasahiro Yamada 
71608f26c5SMasahiro Yamada 	INIT_LIST_HEAD(&dr->entry);
72608f26c5SMasahiro Yamada 	dr->release = release;
73608f26c5SMasahiro Yamada 	set_node_dbginfo(dr, name, size);
74608f26c5SMasahiro Yamada 
75608f26c5SMasahiro Yamada 	return dr->data;
76608f26c5SMasahiro Yamada }
77608f26c5SMasahiro Yamada 
devres_free(void * res)78608f26c5SMasahiro Yamada void devres_free(void *res)
79608f26c5SMasahiro Yamada {
80608f26c5SMasahiro Yamada 	if (res) {
81608f26c5SMasahiro Yamada 		struct devres *dr = container_of(res, struct devres, data);
82608f26c5SMasahiro Yamada 
83608f26c5SMasahiro Yamada 		BUG_ON(!list_empty(&dr->entry));
84608f26c5SMasahiro Yamada 		kfree(dr);
85608f26c5SMasahiro Yamada 	}
86608f26c5SMasahiro Yamada }
87608f26c5SMasahiro Yamada 
devres_add(struct udevice * dev,void * res)88608f26c5SMasahiro Yamada void devres_add(struct udevice *dev, void *res)
89608f26c5SMasahiro Yamada {
90608f26c5SMasahiro Yamada 	struct devres *dr = container_of(res, struct devres, data);
91608f26c5SMasahiro Yamada 
92608f26c5SMasahiro Yamada 	devres_log(dev, dr, "ADD");
93608f26c5SMasahiro Yamada 	BUG_ON(!list_empty(&dr->entry));
94608f26c5SMasahiro Yamada 	dr->probe = dev->flags & DM_FLAG_BOUND ? true : false;
95608f26c5SMasahiro Yamada 	list_add_tail(&dr->entry, &dev->devres_head);
96608f26c5SMasahiro Yamada }
97608f26c5SMasahiro Yamada 
devres_find(struct udevice * dev,dr_release_t release,dr_match_t match,void * match_data)98608f26c5SMasahiro Yamada void *devres_find(struct udevice *dev, dr_release_t release,
99608f26c5SMasahiro Yamada 		  dr_match_t match, void *match_data)
100608f26c5SMasahiro Yamada {
101608f26c5SMasahiro Yamada 	struct devres *dr;
102608f26c5SMasahiro Yamada 
103608f26c5SMasahiro Yamada 	list_for_each_entry_reverse(dr, &dev->devres_head, entry) {
104608f26c5SMasahiro Yamada 		if (dr->release != release)
105608f26c5SMasahiro Yamada 			continue;
106608f26c5SMasahiro Yamada 		if (match && !match(dev, dr->data, match_data))
107608f26c5SMasahiro Yamada 			continue;
108608f26c5SMasahiro Yamada 		return dr->data;
109608f26c5SMasahiro Yamada 	}
110608f26c5SMasahiro Yamada 
111608f26c5SMasahiro Yamada 	return NULL;
112608f26c5SMasahiro Yamada }
113608f26c5SMasahiro Yamada 
devres_get(struct udevice * dev,void * new_res,dr_match_t match,void * match_data)114608f26c5SMasahiro Yamada void *devres_get(struct udevice *dev, void *new_res,
115608f26c5SMasahiro Yamada 		 dr_match_t match, void *match_data)
116608f26c5SMasahiro Yamada {
117608f26c5SMasahiro Yamada 	struct devres *new_dr = container_of(new_res, struct devres, data);
118608f26c5SMasahiro Yamada 	void *res;
119608f26c5SMasahiro Yamada 
120608f26c5SMasahiro Yamada 	res = devres_find(dev, new_dr->release, match, match_data);
121608f26c5SMasahiro Yamada 	if (!res) {
122608f26c5SMasahiro Yamada 		devres_add(dev, new_res);
123608f26c5SMasahiro Yamada 		res = new_res;
124608f26c5SMasahiro Yamada 		new_res = NULL;
125608f26c5SMasahiro Yamada 	}
126608f26c5SMasahiro Yamada 	devres_free(new_res);
127608f26c5SMasahiro Yamada 
128608f26c5SMasahiro Yamada 	return res;
129608f26c5SMasahiro Yamada }
130608f26c5SMasahiro Yamada 
devres_remove(struct udevice * dev,dr_release_t release,dr_match_t match,void * match_data)131608f26c5SMasahiro Yamada void *devres_remove(struct udevice *dev, dr_release_t release,
132608f26c5SMasahiro Yamada 		    dr_match_t match, void *match_data)
133608f26c5SMasahiro Yamada {
134608f26c5SMasahiro Yamada 	void *res;
135608f26c5SMasahiro Yamada 
136608f26c5SMasahiro Yamada 	res = devres_find(dev, release, match, match_data);
137608f26c5SMasahiro Yamada 	if (res) {
138608f26c5SMasahiro Yamada 		struct devres *dr = container_of(res, struct devres, data);
139608f26c5SMasahiro Yamada 
140608f26c5SMasahiro Yamada 		list_del_init(&dr->entry);
141608f26c5SMasahiro Yamada 		devres_log(dev, dr, "REM");
142608f26c5SMasahiro Yamada 	}
143608f26c5SMasahiro Yamada 
144608f26c5SMasahiro Yamada 	return res;
145608f26c5SMasahiro Yamada }
146608f26c5SMasahiro Yamada 
devres_destroy(struct udevice * dev,dr_release_t release,dr_match_t match,void * match_data)147608f26c5SMasahiro Yamada int devres_destroy(struct udevice *dev, dr_release_t release,
148608f26c5SMasahiro Yamada 		   dr_match_t match, void *match_data)
149608f26c5SMasahiro Yamada {
150608f26c5SMasahiro Yamada 	void *res;
151608f26c5SMasahiro Yamada 
152608f26c5SMasahiro Yamada 	res = devres_remove(dev, release, match, match_data);
153608f26c5SMasahiro Yamada 	if (unlikely(!res))
154608f26c5SMasahiro Yamada 		return -ENOENT;
155608f26c5SMasahiro Yamada 
156608f26c5SMasahiro Yamada 	devres_free(res);
157608f26c5SMasahiro Yamada 	return 0;
158608f26c5SMasahiro Yamada }
159608f26c5SMasahiro Yamada 
devres_release(struct udevice * dev,dr_release_t release,dr_match_t match,void * match_data)160608f26c5SMasahiro Yamada int devres_release(struct udevice *dev, dr_release_t release,
161608f26c5SMasahiro Yamada 		   dr_match_t match, void *match_data)
162608f26c5SMasahiro Yamada {
163608f26c5SMasahiro Yamada 	void *res;
164608f26c5SMasahiro Yamada 
165608f26c5SMasahiro Yamada 	res = devres_remove(dev, release, match, match_data);
166608f26c5SMasahiro Yamada 	if (unlikely(!res))
167608f26c5SMasahiro Yamada 		return -ENOENT;
168608f26c5SMasahiro Yamada 
169608f26c5SMasahiro Yamada 	(*release)(dev, res);
170608f26c5SMasahiro Yamada 	devres_free(res);
171608f26c5SMasahiro Yamada 	return 0;
172608f26c5SMasahiro Yamada }
173608f26c5SMasahiro Yamada 
release_nodes(struct udevice * dev,struct list_head * head,bool probe_only)174608f26c5SMasahiro Yamada static void release_nodes(struct udevice *dev, struct list_head *head,
175608f26c5SMasahiro Yamada 			  bool probe_only)
176608f26c5SMasahiro Yamada {
177608f26c5SMasahiro Yamada 	struct devres *dr, *tmp;
178608f26c5SMasahiro Yamada 
179608f26c5SMasahiro Yamada 	list_for_each_entry_safe_reverse(dr, tmp, head, entry)  {
180608f26c5SMasahiro Yamada 		if (probe_only && !dr->probe)
181608f26c5SMasahiro Yamada 			break;
182608f26c5SMasahiro Yamada 		devres_log(dev, dr, "REL");
183608f26c5SMasahiro Yamada 		dr->release(dev, dr->data);
184608f26c5SMasahiro Yamada 		list_del(&dr->entry);
185608f26c5SMasahiro Yamada 		kfree(dr);
186608f26c5SMasahiro Yamada 	}
187608f26c5SMasahiro Yamada }
188608f26c5SMasahiro Yamada 
devres_release_probe(struct udevice * dev)189608f26c5SMasahiro Yamada void devres_release_probe(struct udevice *dev)
190608f26c5SMasahiro Yamada {
191608f26c5SMasahiro Yamada 	release_nodes(dev, &dev->devres_head, true);
192608f26c5SMasahiro Yamada }
193608f26c5SMasahiro Yamada 
devres_release_all(struct udevice * dev)194608f26c5SMasahiro Yamada void devres_release_all(struct udevice *dev)
195608f26c5SMasahiro Yamada {
196608f26c5SMasahiro Yamada 	release_nodes(dev, &dev->devres_head, false);
197608f26c5SMasahiro Yamada }
1982b07f685SMasahiro Yamada 
19940b6f2d0SMasahiro Yamada #ifdef CONFIG_DEBUG_DEVRES
dump_resources(struct udevice * dev,int depth)20040b6f2d0SMasahiro Yamada static void dump_resources(struct udevice *dev, int depth)
20140b6f2d0SMasahiro Yamada {
20240b6f2d0SMasahiro Yamada 	struct devres *dr;
20340b6f2d0SMasahiro Yamada 	struct udevice *child;
20440b6f2d0SMasahiro Yamada 
20540b6f2d0SMasahiro Yamada 	printf("- %s\n", dev->name);
20640b6f2d0SMasahiro Yamada 
20740b6f2d0SMasahiro Yamada 	list_for_each_entry(dr, &dev->devres_head, entry)
20840b6f2d0SMasahiro Yamada 		printf("    %p (%lu byte) %s  %s\n", dr,
20940b6f2d0SMasahiro Yamada 		       (unsigned long)dr->size, dr->name,
21040b6f2d0SMasahiro Yamada 		       dr->probe ? "PROBE" : "BIND");
21140b6f2d0SMasahiro Yamada 
21240b6f2d0SMasahiro Yamada 	list_for_each_entry(child, &dev->child_head, sibling_node)
21340b6f2d0SMasahiro Yamada 		dump_resources(child, depth + 1);
21440b6f2d0SMasahiro Yamada }
21540b6f2d0SMasahiro Yamada 
dm_dump_devres(void)21640b6f2d0SMasahiro Yamada void dm_dump_devres(void)
21740b6f2d0SMasahiro Yamada {
21840b6f2d0SMasahiro Yamada 	struct udevice *root;
21940b6f2d0SMasahiro Yamada 
22040b6f2d0SMasahiro Yamada 	root = dm_root();
22140b6f2d0SMasahiro Yamada 	if (root)
22240b6f2d0SMasahiro Yamada 		dump_resources(root, 0);
22340b6f2d0SMasahiro Yamada }
22440b6f2d0SMasahiro Yamada #endif
22540b6f2d0SMasahiro Yamada 
2262b07f685SMasahiro Yamada /*
2272b07f685SMasahiro Yamada  * Managed kmalloc/kfree
2282b07f685SMasahiro Yamada  */
devm_kmalloc_release(struct udevice * dev,void * res)2292b07f685SMasahiro Yamada static void devm_kmalloc_release(struct udevice *dev, void *res)
2302b07f685SMasahiro Yamada {
2312b07f685SMasahiro Yamada 	/* noop */
2322b07f685SMasahiro Yamada }
2332b07f685SMasahiro Yamada 
devm_kmalloc_match(struct udevice * dev,void * res,void * data)2342b07f685SMasahiro Yamada static int devm_kmalloc_match(struct udevice *dev, void *res, void *data)
2352b07f685SMasahiro Yamada {
2362b07f685SMasahiro Yamada 	return res == data;
2372b07f685SMasahiro Yamada }
2382b07f685SMasahiro Yamada 
devm_kmalloc(struct udevice * dev,size_t size,gfp_t gfp)2392b07f685SMasahiro Yamada void *devm_kmalloc(struct udevice *dev, size_t size, gfp_t gfp)
2402b07f685SMasahiro Yamada {
2412b07f685SMasahiro Yamada 	void *data;
2422b07f685SMasahiro Yamada 
2432b07f685SMasahiro Yamada 	data = _devres_alloc(devm_kmalloc_release, size, gfp);
2442b07f685SMasahiro Yamada 	if (unlikely(!data))
2452b07f685SMasahiro Yamada 		return NULL;
2462b07f685SMasahiro Yamada 
2472b07f685SMasahiro Yamada 	devres_add(dev, data);
2482b07f685SMasahiro Yamada 
2492b07f685SMasahiro Yamada 	return data;
2502b07f685SMasahiro Yamada }
2512b07f685SMasahiro Yamada 
devm_kfree(struct udevice * dev,void * p)2522b07f685SMasahiro Yamada void devm_kfree(struct udevice *dev, void *p)
2532b07f685SMasahiro Yamada {
2542b07f685SMasahiro Yamada 	int rc;
2552b07f685SMasahiro Yamada 
2562b07f685SMasahiro Yamada 	rc = devres_destroy(dev, devm_kmalloc_release, devm_kmalloc_match, p);
2572b07f685SMasahiro Yamada 	WARN_ON(rc);
2582b07f685SMasahiro Yamada }
259