xref: /openbmc/u-boot/drivers/core/uclass.c (revision cf659819)
1 /*
2  * Copyright (c) 2013 Google, Inc
3  *
4  * (C) Copyright 2012
5  * Pavel Herrmann <morpheus.ibis@gmail.com>
6  *
7  * SPDX-License-Identifier:	GPL-2.0+
8  */
9 
10 #include <common.h>
11 #include <errno.h>
12 #include <malloc.h>
13 #include <dm/device.h>
14 #include <dm/device-internal.h>
15 #include <dm/lists.h>
16 #include <dm/uclass.h>
17 #include <dm/uclass-internal.h>
18 #include <dm/util.h>
19 
20 DECLARE_GLOBAL_DATA_PTR;
21 
22 struct uclass *uclass_find(enum uclass_id key)
23 {
24 	struct uclass *uc;
25 
26 	if (!gd->dm_root)
27 		return NULL;
28 	/*
29 	 * TODO(sjg@chromium.org): Optimise this, perhaps moving the found
30 	 * node to the start of the list, or creating a linear array mapping
31 	 * id to node.
32 	 */
33 	list_for_each_entry(uc, &gd->uclass_root, sibling_node) {
34 		if (uc->uc_drv->id == key)
35 			return uc;
36 	}
37 
38 	return NULL;
39 }
40 
41 /**
42  * uclass_add() - Create new uclass in list
43  * @id: Id number to create
44  * @ucp: Returns pointer to uclass, or NULL on error
45  * @return 0 on success, -ve on error
46  *
47  * The new uclass is added to the list. There must be only one uclass for
48  * each id.
49  */
50 static int uclass_add(enum uclass_id id, struct uclass **ucp)
51 {
52 	struct uclass_driver *uc_drv;
53 	struct uclass *uc;
54 	int ret;
55 
56 	*ucp = NULL;
57 	uc_drv = lists_uclass_lookup(id);
58 	if (!uc_drv) {
59 		dm_warn("Cannot find uclass for id %d: please add the UCLASS_DRIVER() declaration for this UCLASS_... id\n",
60 			id);
61 		return -ENOENT;
62 	}
63 	if (uc_drv->ops) {
64 		dm_warn("No ops for uclass id %d\n", id);
65 		return -EINVAL;
66 	}
67 	uc = calloc(1, sizeof(*uc));
68 	if (!uc)
69 		return -ENOMEM;
70 	if (uc_drv->priv_auto_alloc_size) {
71 		uc->priv = calloc(1, uc_drv->priv_auto_alloc_size);
72 		if (!uc->priv) {
73 			ret = -ENOMEM;
74 			goto fail_mem;
75 		}
76 	}
77 	uc->uc_drv = uc_drv;
78 	INIT_LIST_HEAD(&uc->sibling_node);
79 	INIT_LIST_HEAD(&uc->dev_head);
80 	list_add(&uc->sibling_node, &DM_UCLASS_ROOT_NON_CONST);
81 
82 	if (uc_drv->init) {
83 		ret = uc_drv->init(uc);
84 		if (ret)
85 			goto fail;
86 	}
87 
88 	*ucp = uc;
89 
90 	return 0;
91 fail:
92 	if (uc_drv->priv_auto_alloc_size) {
93 		free(uc->priv);
94 		uc->priv = NULL;
95 	}
96 	list_del(&uc->sibling_node);
97 fail_mem:
98 	free(uc);
99 
100 	return ret;
101 }
102 
103 int uclass_destroy(struct uclass *uc)
104 {
105 	struct uclass_driver *uc_drv;
106 	struct udevice *dev, *tmp;
107 	int ret;
108 
109 	list_for_each_entry_safe(dev, tmp, &uc->dev_head, uclass_node) {
110 		ret = device_remove(dev);
111 		if (ret)
112 			return ret;
113 		ret = device_unbind(dev);
114 		if (ret)
115 			return ret;
116 	}
117 
118 	uc_drv = uc->uc_drv;
119 	if (uc_drv->destroy)
120 		uc_drv->destroy(uc);
121 	list_del(&uc->sibling_node);
122 	if (uc_drv->priv_auto_alloc_size)
123 		free(uc->priv);
124 	free(uc);
125 
126 	return 0;
127 }
128 
129 int uclass_get(enum uclass_id id, struct uclass **ucp)
130 {
131 	struct uclass *uc;
132 
133 	*ucp = NULL;
134 	uc = uclass_find(id);
135 	if (!uc)
136 		return uclass_add(id, ucp);
137 	*ucp = uc;
138 
139 	return 0;
140 }
141 
142 int uclass_find_device(enum uclass_id id, int index, struct udevice **devp)
143 {
144 	struct uclass *uc;
145 	struct udevice *dev;
146 	int ret;
147 
148 	*devp = NULL;
149 	ret = uclass_get(id, &uc);
150 	if (ret)
151 		return ret;
152 
153 	list_for_each_entry(dev, &uc->dev_head, uclass_node) {
154 		if (!index--) {
155 			*devp = dev;
156 			return 0;
157 		}
158 	}
159 
160 	return -ENODEV;
161 }
162 
163 int uclass_find_device_by_seq(enum uclass_id id, int seq_or_req_seq,
164 			      bool find_req_seq, struct udevice **devp)
165 {
166 	struct uclass *uc;
167 	struct udevice *dev;
168 	int ret;
169 
170 	*devp = NULL;
171 	debug("%s: %d %d\n", __func__, find_req_seq, seq_or_req_seq);
172 	if (seq_or_req_seq == -1)
173 		return -ENODEV;
174 	ret = uclass_get(id, &uc);
175 	if (ret)
176 		return ret;
177 
178 	list_for_each_entry(dev, &uc->dev_head, uclass_node) {
179 		debug("   - %d %d\n", dev->req_seq, dev->seq);
180 		if ((find_req_seq ? dev->req_seq : dev->seq) ==
181 				seq_or_req_seq) {
182 			*devp = dev;
183 			debug("   - found\n");
184 			return 0;
185 		}
186 	}
187 	debug("   - not found\n");
188 
189 	return -ENODEV;
190 }
191 
192 static int uclass_find_device_by_of_offset(enum uclass_id id, int node,
193 					   struct udevice **devp)
194 {
195 	struct uclass *uc;
196 	struct udevice *dev;
197 	int ret;
198 
199 	*devp = NULL;
200 	if (node < 0)
201 		return -ENODEV;
202 	ret = uclass_get(id, &uc);
203 	if (ret)
204 		return ret;
205 
206 	list_for_each_entry(dev, &uc->dev_head, uclass_node) {
207 		if (dev->of_offset == node) {
208 			*devp = dev;
209 			return 0;
210 		}
211 	}
212 
213 	return -ENODEV;
214 }
215 
216 /**
217  * uclass_get_device_tail() - handle the end of a get_device call
218  *
219  * This handles returning an error or probing a device as needed.
220  *
221  * @dev: Device that needs to be probed
222  * @ret: Error to return. If non-zero then the device is not probed
223  * @devp: Returns the value of 'dev' if there is no error
224  * @return ret, if non-zero, else the result of the device_probe() call
225  */
226 static int uclass_get_device_tail(struct udevice *dev, int ret,
227 				  struct udevice **devp)
228 {
229 	if (ret)
230 		return ret;
231 
232 	ret = device_probe(dev);
233 	if (ret)
234 		return ret;
235 
236 	*devp = dev;
237 
238 	return 0;
239 }
240 
241 int uclass_get_device(enum uclass_id id, int index, struct udevice **devp)
242 {
243 	struct udevice *dev;
244 	int ret;
245 
246 	*devp = NULL;
247 	ret = uclass_find_device(id, index, &dev);
248 	return uclass_get_device_tail(dev, ret, devp);
249 }
250 
251 int uclass_get_device_by_seq(enum uclass_id id, int seq, struct udevice **devp)
252 {
253 	struct udevice *dev;
254 	int ret;
255 
256 	*devp = NULL;
257 	ret = uclass_find_device_by_seq(id, seq, false, &dev);
258 	if (ret == -ENODEV) {
259 		/*
260 		 * We didn't find it in probed devices. See if there is one
261 		 * that will request this seq if probed.
262 		 */
263 		ret = uclass_find_device_by_seq(id, seq, true, &dev);
264 	}
265 	return uclass_get_device_tail(dev, ret, devp);
266 }
267 
268 int uclass_get_device_by_of_offset(enum uclass_id id, int node,
269 				   struct udevice **devp)
270 {
271 	struct udevice *dev;
272 	int ret;
273 
274 	*devp = NULL;
275 	ret = uclass_find_device_by_of_offset(id, node, &dev);
276 	return uclass_get_device_tail(dev, ret, devp);
277 }
278 
279 int uclass_first_device(enum uclass_id id, struct udevice **devp)
280 {
281 	struct uclass *uc;
282 	struct udevice *dev;
283 	int ret;
284 
285 	*devp = NULL;
286 	ret = uclass_get(id, &uc);
287 	if (ret)
288 		return ret;
289 	if (list_empty(&uc->dev_head))
290 		return 0;
291 
292 	dev = list_first_entry(&uc->dev_head, struct udevice, uclass_node);
293 	ret = device_probe(dev);
294 	if (ret)
295 		return ret;
296 	*devp = dev;
297 
298 	return 0;
299 }
300 
301 int uclass_next_device(struct udevice **devp)
302 {
303 	struct udevice *dev = *devp;
304 	int ret;
305 
306 	*devp = NULL;
307 	if (list_is_last(&dev->uclass_node, &dev->uclass->dev_head))
308 		return 0;
309 
310 	dev = list_entry(dev->uclass_node.next, struct udevice,
311 			 uclass_node);
312 	ret = device_probe(dev);
313 	if (ret)
314 		return ret;
315 	*devp = dev;
316 
317 	return 0;
318 }
319 
320 int uclass_bind_device(struct udevice *dev)
321 {
322 	struct uclass *uc;
323 	int ret;
324 
325 	uc = dev->uclass;
326 
327 	list_add_tail(&dev->uclass_node, &uc->dev_head);
328 
329 	if (uc->uc_drv->post_bind) {
330 		ret = uc->uc_drv->post_bind(dev);
331 		if (ret) {
332 			list_del(&dev->uclass_node);
333 			return ret;
334 		}
335 	}
336 
337 	return 0;
338 }
339 
340 int uclass_unbind_device(struct udevice *dev)
341 {
342 	struct uclass *uc;
343 	int ret;
344 
345 	uc = dev->uclass;
346 	if (uc->uc_drv->pre_unbind) {
347 		ret = uc->uc_drv->pre_unbind(dev);
348 		if (ret)
349 			return ret;
350 	}
351 
352 	list_del(&dev->uclass_node);
353 	return 0;
354 }
355 
356 int uclass_resolve_seq(struct udevice *dev)
357 {
358 	struct udevice *dup;
359 	int seq;
360 	int ret;
361 
362 	assert(dev->seq == -1);
363 	ret = uclass_find_device_by_seq(dev->uclass->uc_drv->id, dev->req_seq,
364 					false, &dup);
365 	if (!ret) {
366 		dm_warn("Device '%s': seq %d is in use by '%s'\n",
367 			dev->name, dev->req_seq, dup->name);
368 	} else if (ret == -ENODEV) {
369 		/* Our requested sequence number is available */
370 		if (dev->req_seq != -1)
371 			return dev->req_seq;
372 	} else {
373 		return ret;
374 	}
375 
376 	for (seq = 0; seq < DM_MAX_SEQ; seq++) {
377 		ret = uclass_find_device_by_seq(dev->uclass->uc_drv->id, seq,
378 						false, &dup);
379 		if (ret == -ENODEV)
380 			break;
381 		if (ret)
382 			return ret;
383 	}
384 	return seq;
385 }
386 
387 int uclass_post_probe_device(struct udevice *dev)
388 {
389 	struct uclass_driver *uc_drv = dev->uclass->uc_drv;
390 
391 	if (uc_drv->post_probe)
392 		return uc_drv->post_probe(dev);
393 
394 	return 0;
395 }
396 
397 int uclass_pre_remove_device(struct udevice *dev)
398 {
399 	struct uclass_driver *uc_drv;
400 	struct uclass *uc;
401 	int ret;
402 
403 	uc = dev->uclass;
404 	uc_drv = uc->uc_drv;
405 	if (uc->uc_drv->pre_remove) {
406 		ret = uc->uc_drv->pre_remove(dev);
407 		if (ret)
408 			return ret;
409 	}
410 	if (uc_drv->per_device_auto_alloc_size) {
411 		free(dev->uclass_priv);
412 		dev->uclass_priv = NULL;
413 	}
414 	dev->seq = -1;
415 
416 	return 0;
417 }
418