xref: /rk3399_rockchip-uboot/drivers/core/devres.c (revision 2b07f6859ad17b74ce490f371f4878add6ae5a11)
1608f26c5SMasahiro Yamada /*
2608f26c5SMasahiro Yamada  * Copyright (C) 2015 Masahiro Yamada <yamada.masahiro@socionext.com>
3608f26c5SMasahiro Yamada  *
4608f26c5SMasahiro Yamada  * Based on the original work in Linux by
5608f26c5SMasahiro Yamada  * Copyright (c) 2006  SUSE Linux Products GmbH
6608f26c5SMasahiro Yamada  * Copyright (c) 2006  Tejun Heo <teheo@suse.de>
7608f26c5SMasahiro Yamada  *
8608f26c5SMasahiro Yamada  * SPDX-License-Identifier:	GPL-2.0+
9608f26c5SMasahiro Yamada  */
10608f26c5SMasahiro Yamada 
11608f26c5SMasahiro Yamada #include <common.h>
12608f26c5SMasahiro Yamada #include <linux/compat.h>
13608f26c5SMasahiro Yamada #include <linux/kernel.h>
14608f26c5SMasahiro Yamada #include <linux/list.h>
15608f26c5SMasahiro Yamada #include <dm/device.h>
16608f26c5SMasahiro Yamada 
17608f26c5SMasahiro Yamada /**
18608f26c5SMasahiro Yamada  * struct devres - Bookkeeping info for managed device resource
19608f26c5SMasahiro Yamada  * @entry: List to associate this structure with a device
20608f26c5SMasahiro Yamada  * @release: Callback invoked when this resource is released
21608f26c5SMasahiro Yamada  * @probe: Flag to show when this resource was allocated
22608f26c5SMasahiro Yamada 	   (true = probe, false = bind)
23608f26c5SMasahiro Yamada  * @name: Name of release function
24608f26c5SMasahiro Yamada  * @size: Size of resource data
25608f26c5SMasahiro Yamada  * @data: Resource data
26608f26c5SMasahiro Yamada  */
27608f26c5SMasahiro Yamada struct devres {
28608f26c5SMasahiro Yamada 	struct list_head		entry;
29608f26c5SMasahiro Yamada 	dr_release_t			release;
30608f26c5SMasahiro Yamada 	bool				probe;
31608f26c5SMasahiro Yamada #ifdef CONFIG_DEBUG_DEVRES
32608f26c5SMasahiro Yamada 	const char			*name;
33608f26c5SMasahiro Yamada 	size_t				size;
34608f26c5SMasahiro Yamada #endif
35608f26c5SMasahiro Yamada 	unsigned long long		data[];
36608f26c5SMasahiro Yamada };
37608f26c5SMasahiro Yamada 
38608f26c5SMasahiro Yamada #ifdef CONFIG_DEBUG_DEVRES
39608f26c5SMasahiro Yamada static void set_node_dbginfo(struct devres *dr, const char *name, size_t size)
40608f26c5SMasahiro Yamada {
41608f26c5SMasahiro Yamada 	dr->name = name;
42608f26c5SMasahiro Yamada 	dr->size = size;
43608f26c5SMasahiro Yamada }
44608f26c5SMasahiro Yamada 
45608f26c5SMasahiro Yamada static void devres_log(struct udevice *dev, struct devres *dr,
46608f26c5SMasahiro Yamada 		       const char *op)
47608f26c5SMasahiro Yamada {
48608f26c5SMasahiro Yamada 	printf("%s: DEVRES %3s %p %s (%lu bytes)\n",
49608f26c5SMasahiro Yamada 	       dev->name, op, dr, dr->name, (unsigned long)dr->size);
50608f26c5SMasahiro Yamada }
51608f26c5SMasahiro Yamada #else /* CONFIG_DEBUG_DEVRES */
52608f26c5SMasahiro Yamada #define set_node_dbginfo(dr, n, s)	do {} while (0)
53608f26c5SMasahiro Yamada #define devres_log(dev, dr, op)		do {} while (0)
54608f26c5SMasahiro Yamada #endif
55608f26c5SMasahiro Yamada 
56608f26c5SMasahiro Yamada #if CONFIG_DEBUG_DEVRES
57608f26c5SMasahiro Yamada void *__devres_alloc(dr_release_t release, size_t size, gfp_t gfp,
58608f26c5SMasahiro Yamada 		     const char *name)
59608f26c5SMasahiro Yamada #else
60608f26c5SMasahiro Yamada void *_devres_alloc(dr_release_t release, size_t size, gfp_t gfp)
61608f26c5SMasahiro Yamada #endif
62608f26c5SMasahiro Yamada {
63608f26c5SMasahiro Yamada 	size_t tot_size = sizeof(struct devres) + size;
64608f26c5SMasahiro Yamada 	struct devres *dr;
65608f26c5SMasahiro Yamada 
66608f26c5SMasahiro Yamada 	dr = kmalloc(tot_size, gfp);
67608f26c5SMasahiro Yamada 	if (unlikely(!dr))
68608f26c5SMasahiro Yamada 		return NULL;
69608f26c5SMasahiro Yamada 
70608f26c5SMasahiro Yamada 	INIT_LIST_HEAD(&dr->entry);
71608f26c5SMasahiro Yamada 	dr->release = release;
72608f26c5SMasahiro Yamada 	set_node_dbginfo(dr, name, size);
73608f26c5SMasahiro Yamada 
74608f26c5SMasahiro Yamada 	return dr->data;
75608f26c5SMasahiro Yamada }
76608f26c5SMasahiro Yamada 
77608f26c5SMasahiro Yamada void devres_free(void *res)
78608f26c5SMasahiro Yamada {
79608f26c5SMasahiro Yamada 	if (res) {
80608f26c5SMasahiro Yamada 		struct devres *dr = container_of(res, struct devres, data);
81608f26c5SMasahiro Yamada 
82608f26c5SMasahiro Yamada 		BUG_ON(!list_empty(&dr->entry));
83608f26c5SMasahiro Yamada 		kfree(dr);
84608f26c5SMasahiro Yamada 	}
85608f26c5SMasahiro Yamada }
86608f26c5SMasahiro Yamada 
87608f26c5SMasahiro Yamada void devres_add(struct udevice *dev, void *res)
88608f26c5SMasahiro Yamada {
89608f26c5SMasahiro Yamada 	struct devres *dr = container_of(res, struct devres, data);
90608f26c5SMasahiro Yamada 
91608f26c5SMasahiro Yamada 	devres_log(dev, dr, "ADD");
92608f26c5SMasahiro Yamada 	BUG_ON(!list_empty(&dr->entry));
93608f26c5SMasahiro Yamada 	dr->probe = dev->flags & DM_FLAG_BOUND ? true : false;
94608f26c5SMasahiro Yamada 	list_add_tail(&dr->entry, &dev->devres_head);
95608f26c5SMasahiro Yamada }
96608f26c5SMasahiro Yamada 
97608f26c5SMasahiro Yamada void *devres_find(struct udevice *dev, dr_release_t release,
98608f26c5SMasahiro Yamada 		  dr_match_t match, void *match_data)
99608f26c5SMasahiro Yamada {
100608f26c5SMasahiro Yamada 	struct devres *dr;
101608f26c5SMasahiro Yamada 
102608f26c5SMasahiro Yamada 	list_for_each_entry_reverse(dr, &dev->devres_head, entry) {
103608f26c5SMasahiro Yamada 		if (dr->release != release)
104608f26c5SMasahiro Yamada 			continue;
105608f26c5SMasahiro Yamada 		if (match && !match(dev, dr->data, match_data))
106608f26c5SMasahiro Yamada 			continue;
107608f26c5SMasahiro Yamada 		return dr->data;
108608f26c5SMasahiro Yamada 	}
109608f26c5SMasahiro Yamada 
110608f26c5SMasahiro Yamada 	return NULL;
111608f26c5SMasahiro Yamada }
112608f26c5SMasahiro Yamada 
113608f26c5SMasahiro Yamada void *devres_get(struct udevice *dev, void *new_res,
114608f26c5SMasahiro Yamada 		 dr_match_t match, void *match_data)
115608f26c5SMasahiro Yamada {
116608f26c5SMasahiro Yamada 	struct devres *new_dr = container_of(new_res, struct devres, data);
117608f26c5SMasahiro Yamada 	void *res;
118608f26c5SMasahiro Yamada 
119608f26c5SMasahiro Yamada 	res = devres_find(dev, new_dr->release, match, match_data);
120608f26c5SMasahiro Yamada 	if (!res) {
121608f26c5SMasahiro Yamada 		devres_add(dev, new_res);
122608f26c5SMasahiro Yamada 		res = new_res;
123608f26c5SMasahiro Yamada 		new_res = NULL;
124608f26c5SMasahiro Yamada 	}
125608f26c5SMasahiro Yamada 	devres_free(new_res);
126608f26c5SMasahiro Yamada 
127608f26c5SMasahiro Yamada 	return res;
128608f26c5SMasahiro Yamada }
129608f26c5SMasahiro Yamada 
130608f26c5SMasahiro Yamada void *devres_remove(struct udevice *dev, dr_release_t release,
131608f26c5SMasahiro Yamada 		    dr_match_t match, void *match_data)
132608f26c5SMasahiro Yamada {
133608f26c5SMasahiro Yamada 	void *res;
134608f26c5SMasahiro Yamada 
135608f26c5SMasahiro Yamada 	res = devres_find(dev, release, match, match_data);
136608f26c5SMasahiro Yamada 	if (res) {
137608f26c5SMasahiro Yamada 		struct devres *dr = container_of(res, struct devres, data);
138608f26c5SMasahiro Yamada 
139608f26c5SMasahiro Yamada 		list_del_init(&dr->entry);
140608f26c5SMasahiro Yamada 		devres_log(dev, dr, "REM");
141608f26c5SMasahiro Yamada 	}
142608f26c5SMasahiro Yamada 
143608f26c5SMasahiro Yamada 	return res;
144608f26c5SMasahiro Yamada }
145608f26c5SMasahiro Yamada 
146608f26c5SMasahiro Yamada int devres_destroy(struct udevice *dev, dr_release_t release,
147608f26c5SMasahiro Yamada 		   dr_match_t match, void *match_data)
148608f26c5SMasahiro Yamada {
149608f26c5SMasahiro Yamada 	void *res;
150608f26c5SMasahiro Yamada 
151608f26c5SMasahiro Yamada 	res = devres_remove(dev, release, match, match_data);
152608f26c5SMasahiro Yamada 	if (unlikely(!res))
153608f26c5SMasahiro Yamada 		return -ENOENT;
154608f26c5SMasahiro Yamada 
155608f26c5SMasahiro Yamada 	devres_free(res);
156608f26c5SMasahiro Yamada 	return 0;
157608f26c5SMasahiro Yamada }
158608f26c5SMasahiro Yamada 
159608f26c5SMasahiro Yamada int devres_release(struct udevice *dev, dr_release_t release,
160608f26c5SMasahiro Yamada 		   dr_match_t match, void *match_data)
161608f26c5SMasahiro Yamada {
162608f26c5SMasahiro Yamada 	void *res;
163608f26c5SMasahiro Yamada 
164608f26c5SMasahiro Yamada 	res = devres_remove(dev, release, match, match_data);
165608f26c5SMasahiro Yamada 	if (unlikely(!res))
166608f26c5SMasahiro Yamada 		return -ENOENT;
167608f26c5SMasahiro Yamada 
168608f26c5SMasahiro Yamada 	(*release)(dev, res);
169608f26c5SMasahiro Yamada 	devres_free(res);
170608f26c5SMasahiro Yamada 	return 0;
171608f26c5SMasahiro Yamada }
172608f26c5SMasahiro Yamada 
173608f26c5SMasahiro Yamada static void release_nodes(struct udevice *dev, struct list_head *head,
174608f26c5SMasahiro Yamada 			  bool probe_only)
175608f26c5SMasahiro Yamada {
176608f26c5SMasahiro Yamada 	struct devres *dr, *tmp;
177608f26c5SMasahiro Yamada 
178608f26c5SMasahiro Yamada 	list_for_each_entry_safe_reverse(dr, tmp, head, entry)  {
179608f26c5SMasahiro Yamada 		if (probe_only && !dr->probe)
180608f26c5SMasahiro Yamada 			break;
181608f26c5SMasahiro Yamada 		devres_log(dev, dr, "REL");
182608f26c5SMasahiro Yamada 		dr->release(dev, dr->data);
183608f26c5SMasahiro Yamada 		list_del(&dr->entry);
184608f26c5SMasahiro Yamada 		kfree(dr);
185608f26c5SMasahiro Yamada 	}
186608f26c5SMasahiro Yamada }
187608f26c5SMasahiro Yamada 
188608f26c5SMasahiro Yamada void devres_release_probe(struct udevice *dev)
189608f26c5SMasahiro Yamada {
190608f26c5SMasahiro Yamada 	release_nodes(dev, &dev->devres_head, true);
191608f26c5SMasahiro Yamada }
192608f26c5SMasahiro Yamada 
193608f26c5SMasahiro Yamada void devres_release_all(struct udevice *dev)
194608f26c5SMasahiro Yamada {
195608f26c5SMasahiro Yamada 	release_nodes(dev, &dev->devres_head, false);
196608f26c5SMasahiro Yamada }
197*2b07f685SMasahiro Yamada 
198*2b07f685SMasahiro Yamada /*
199*2b07f685SMasahiro Yamada  * Managed kmalloc/kfree
200*2b07f685SMasahiro Yamada  */
201*2b07f685SMasahiro Yamada static void devm_kmalloc_release(struct udevice *dev, void *res)
202*2b07f685SMasahiro Yamada {
203*2b07f685SMasahiro Yamada 	/* noop */
204*2b07f685SMasahiro Yamada }
205*2b07f685SMasahiro Yamada 
206*2b07f685SMasahiro Yamada static int devm_kmalloc_match(struct udevice *dev, void *res, void *data)
207*2b07f685SMasahiro Yamada {
208*2b07f685SMasahiro Yamada 	return res == data;
209*2b07f685SMasahiro Yamada }
210*2b07f685SMasahiro Yamada 
211*2b07f685SMasahiro Yamada void *devm_kmalloc(struct udevice *dev, size_t size, gfp_t gfp)
212*2b07f685SMasahiro Yamada {
213*2b07f685SMasahiro Yamada 	void *data;
214*2b07f685SMasahiro Yamada 
215*2b07f685SMasahiro Yamada 	data = _devres_alloc(devm_kmalloc_release, size, gfp);
216*2b07f685SMasahiro Yamada 	if (unlikely(!data))
217*2b07f685SMasahiro Yamada 		return NULL;
218*2b07f685SMasahiro Yamada 
219*2b07f685SMasahiro Yamada 	devres_add(dev, data);
220*2b07f685SMasahiro Yamada 
221*2b07f685SMasahiro Yamada 	return data;
222*2b07f685SMasahiro Yamada }
223*2b07f685SMasahiro Yamada 
224*2b07f685SMasahiro Yamada void devm_kfree(struct udevice *dev, void *p)
225*2b07f685SMasahiro Yamada {
226*2b07f685SMasahiro Yamada 	int rc;
227*2b07f685SMasahiro Yamada 
228*2b07f685SMasahiro Yamada 	rc = devres_destroy(dev, devm_kmalloc_release, devm_kmalloc_match, p);
229*2b07f685SMasahiro Yamada 	WARN_ON(rc);
230*2b07f685SMasahiro Yamada }
231