237b42653c6beb51e6cdc8043a8ec0941b925f6b
[oweals/u-boot.git] / drivers / core / devres.c
1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3  * Copyright (C) 2015 Masahiro Yamada <yamada.masahiro@socionext.com>
4  *
5  * Based on the original work in Linux by
6  * Copyright (c) 2006  SUSE Linux Products GmbH
7  * Copyright (c) 2006  Tejun Heo <teheo@suse.de>
8  */
9
10 #define LOG_CATEGORY LOGC_DEVRES
11
12 #include <common.h>
13 #include <linux/compat.h>
14 #include <linux/kernel.h>
15 #include <linux/list.h>
16 #include <dm/device.h>
17 #include <dm/root.h>
18 #include <dm/util.h>
19
20 /** enum devres_phase - Shows where resource was allocated
21  *
22  * DEVRES_PHASE_BIND: In the bind() method
23  * DEVRES_PHASE_OFDATA: In the ofdata_to_platdata() method
24  * DEVRES_PHASE_PROBE: In the probe() method
25  */
26 enum devres_phase {
27         DEVRES_PHASE_BIND,
28         DEVRES_PHASE_OFDATA,
29         DEVRES_PHASE_PROBE,
30 };
31
32 /**
33  * struct devres - Bookkeeping info for managed device resource
34  * @entry: List to associate this structure with a device
35  * @release: Callback invoked when this resource is released
36  * @probe: Show where this resource was allocated
37  * @name: Name of release function
38  * @size: Size of resource data
39  * @data: Resource data
40  */
41 struct devres {
42         struct list_head                entry;
43         dr_release_t                    release;
44         enum devres_phase               phase;
45 #ifdef CONFIG_DEBUG_DEVRES
46         const char                      *name;
47         size_t                          size;
48 #endif
49         unsigned long long              data[];
50 };
51
52 #ifdef CONFIG_DEBUG_DEVRES
53 static void set_node_dbginfo(struct devres *dr, const char *name, size_t size)
54 {
55         dr->name = name;
56         dr->size = size;
57 }
58
59 static void devres_log(struct udevice *dev, struct devres *dr,
60                        const char *op)
61 {
62         log_debug("%s: DEVRES %3s %p %s (%lu bytes)\n", dev->name, op, dr,
63                   dr->name, (unsigned long)dr->size);
64 }
65 #else /* CONFIG_DEBUG_DEVRES */
66 #define set_node_dbginfo(dr, n, s)      do {} while (0)
67 #define devres_log(dev, dr, op)         do {} while (0)
68 #endif
69
70 #if CONFIG_DEBUG_DEVRES
71 void *__devres_alloc(dr_release_t release, size_t size, gfp_t gfp,
72                      const char *name)
73 #else
74 void *_devres_alloc(dr_release_t release, size_t size, gfp_t gfp)
75 #endif
76 {
77         size_t tot_size = sizeof(struct devres) + size;
78         struct devres *dr;
79
80         dr = kmalloc(tot_size, gfp);
81         if (unlikely(!dr))
82                 return NULL;
83
84         INIT_LIST_HEAD(&dr->entry);
85         dr->release = release;
86         set_node_dbginfo(dr, name, size);
87
88         return dr->data;
89 }
90
91 void devres_free(void *res)
92 {
93         if (res) {
94                 struct devres *dr = container_of(res, struct devres, data);
95
96                 assert_noisy(list_empty(&dr->entry));
97                 kfree(dr);
98         }
99 }
100
101 void devres_add(struct udevice *dev, void *res)
102 {
103         struct devres *dr = container_of(res, struct devres, data);
104
105         devres_log(dev, dr, "ADD");
106         assert_noisy(list_empty(&dr->entry));
107         if (dev->flags & DM_FLAG_PLATDATA_VALID)
108                 dr->phase = DEVRES_PHASE_PROBE;
109         else if (dev->flags & DM_FLAG_BOUND)
110                 dr->phase = DEVRES_PHASE_OFDATA;
111         else
112                 dr->phase = DEVRES_PHASE_BIND;
113         list_add_tail(&dr->entry, &dev->devres_head);
114 }
115
116 void *devres_find(struct udevice *dev, dr_release_t release,
117                   dr_match_t match, void *match_data)
118 {
119         struct devres *dr;
120
121         list_for_each_entry_reverse(dr, &dev->devres_head, entry) {
122                 if (dr->release != release)
123                         continue;
124                 if (match && !match(dev, dr->data, match_data))
125                         continue;
126                 return dr->data;
127         }
128
129         return NULL;
130 }
131
132 void *devres_get(struct udevice *dev, void *new_res,
133                  dr_match_t match, void *match_data)
134 {
135         struct devres *new_dr = container_of(new_res, struct devres, data);
136         void *res;
137
138         res = devres_find(dev, new_dr->release, match, match_data);
139         if (!res) {
140                 devres_add(dev, new_res);
141                 res = new_res;
142                 new_res = NULL;
143         }
144         devres_free(new_res);
145
146         return res;
147 }
148
149 void *devres_remove(struct udevice *dev, dr_release_t release,
150                     dr_match_t match, void *match_data)
151 {
152         void *res;
153
154         res = devres_find(dev, release, match, match_data);
155         if (res) {
156                 struct devres *dr = container_of(res, struct devres, data);
157
158                 list_del_init(&dr->entry);
159                 devres_log(dev, dr, "REM");
160         }
161
162         return res;
163 }
164
165 int devres_destroy(struct udevice *dev, dr_release_t release,
166                    dr_match_t match, void *match_data)
167 {
168         void *res;
169
170         res = devres_remove(dev, release, match, match_data);
171         if (unlikely(!res))
172                 return -ENOENT;
173
174         devres_free(res);
175         return 0;
176 }
177
178 int devres_release(struct udevice *dev, dr_release_t release,
179                    dr_match_t match, void *match_data)
180 {
181         void *res;
182
183         res = devres_remove(dev, release, match, match_data);
184         if (unlikely(!res))
185                 return -ENOENT;
186
187         (*release)(dev, res);
188         devres_free(res);
189         return 0;
190 }
191
192 static void release_nodes(struct udevice *dev, struct list_head *head,
193                           bool probe_and_ofdata_only)
194 {
195         struct devres *dr, *tmp;
196
197         list_for_each_entry_safe_reverse(dr, tmp, head, entry)  {
198                 if (probe_and_ofdata_only && dr->phase == DEVRES_PHASE_BIND)
199                         break;
200                 devres_log(dev, dr, "REL");
201                 dr->release(dev, dr->data);
202                 list_del(&dr->entry);
203                 kfree(dr);
204         }
205 }
206
207 void devres_release_probe(struct udevice *dev)
208 {
209         release_nodes(dev, &dev->devres_head, true);
210 }
211
212 void devres_release_all(struct udevice *dev)
213 {
214         release_nodes(dev, &dev->devres_head, false);
215 }
216
217 #ifdef CONFIG_DEBUG_DEVRES
218 static char *const devres_phase_name[] = {"BIND", "OFDATA", "PROBE"};
219
220 static void dump_resources(struct udevice *dev, int depth)
221 {
222         struct devres *dr;
223         struct udevice *child;
224
225         printf("- %s\n", dev->name);
226
227         list_for_each_entry(dr, &dev->devres_head, entry)
228                 printf("    %p (%lu byte) %s  %s\n", dr,
229                        (unsigned long)dr->size, dr->name,
230                        devres_phase_name[dr->phase]);
231
232         list_for_each_entry(child, &dev->child_head, sibling_node)
233                 dump_resources(child, depth + 1);
234 }
235
236 void dm_dump_devres(void)
237 {
238         struct udevice *root;
239
240         root = dm_root();
241         if (root)
242                 dump_resources(root, 0);
243 }
244
245 void devres_get_stats(const struct udevice *dev, struct devres_stats *stats)
246 {
247         struct devres *dr;
248
249         stats->allocs = 0;
250         stats->total_size = 0;
251         list_for_each_entry(dr, &dev->devres_head, entry) {
252                 stats->allocs++;
253                 stats->total_size += dr->size;
254         }
255 }
256
257 #endif
258
259 /*
260  * Managed kmalloc/kfree
261  */
262 static void devm_kmalloc_release(struct udevice *dev, void *res)
263 {
264         /* noop */
265 }
266
267 static int devm_kmalloc_match(struct udevice *dev, void *res, void *data)
268 {
269         return res == data;
270 }
271
272 void *devm_kmalloc(struct udevice *dev, size_t size, gfp_t gfp)
273 {
274         void *data;
275
276         data = _devres_alloc(devm_kmalloc_release, size, gfp);
277         if (unlikely(!data))
278                 return NULL;
279
280         devres_add(dev, data);
281
282         return data;
283 }
284
285 void devm_kfree(struct udevice *dev, void *p)
286 {
287         int rc;
288
289         rc = devres_destroy(dev, devm_kmalloc_release, devm_kmalloc_match, p);
290         assert_noisy(!rc);
291 }