xref: /openbmc/u-boot/drivers/core/uclass.c (revision 9ca296a1)
16494d708SSimon Glass /*
26494d708SSimon Glass  * Copyright (c) 2013 Google, Inc
36494d708SSimon Glass  *
46494d708SSimon Glass  * (C) Copyright 2012
56494d708SSimon Glass  * Pavel Herrmann <morpheus.ibis@gmail.com>
66494d708SSimon Glass  *
76494d708SSimon Glass  * SPDX-License-Identifier:	GPL-2.0+
86494d708SSimon Glass  */
96494d708SSimon Glass 
106494d708SSimon Glass #include <common.h>
116494d708SSimon Glass #include <errno.h>
126494d708SSimon Glass #include <malloc.h>
136494d708SSimon Glass #include <dm/device.h>
146494d708SSimon Glass #include <dm/device-internal.h>
156494d708SSimon Glass #include <dm/lists.h>
166494d708SSimon Glass #include <dm/uclass.h>
176494d708SSimon Glass #include <dm/uclass-internal.h>
186494d708SSimon Glass #include <dm/util.h>
196494d708SSimon Glass 
206494d708SSimon Glass DECLARE_GLOBAL_DATA_PTR;
216494d708SSimon Glass 
226494d708SSimon Glass struct uclass *uclass_find(enum uclass_id key)
236494d708SSimon Glass {
246494d708SSimon Glass 	struct uclass *uc;
256494d708SSimon Glass 
266494d708SSimon Glass 	/*
276494d708SSimon Glass 	 * TODO(sjg@chromium.org): Optimise this, perhaps moving the found
286494d708SSimon Glass 	 * node to the start of the list, or creating a linear array mapping
296494d708SSimon Glass 	 * id to node.
306494d708SSimon Glass 	 */
316494d708SSimon Glass 	list_for_each_entry(uc, &gd->uclass_root, sibling_node) {
326494d708SSimon Glass 		if (uc->uc_drv->id == key)
336494d708SSimon Glass 			return uc;
346494d708SSimon Glass 	}
356494d708SSimon Glass 
366494d708SSimon Glass 	return NULL;
376494d708SSimon Glass }
386494d708SSimon Glass 
396494d708SSimon Glass /**
406494d708SSimon Glass  * uclass_add() - Create new uclass in list
416494d708SSimon Glass  * @id: Id number to create
426494d708SSimon Glass  * @ucp: Returns pointer to uclass, or NULL on error
436494d708SSimon Glass  * @return 0 on success, -ve on error
446494d708SSimon Glass  *
456494d708SSimon Glass  * The new uclass is added to the list. There must be only one uclass for
466494d708SSimon Glass  * each id.
476494d708SSimon Glass  */
486494d708SSimon Glass static int uclass_add(enum uclass_id id, struct uclass **ucp)
496494d708SSimon Glass {
506494d708SSimon Glass 	struct uclass_driver *uc_drv;
516494d708SSimon Glass 	struct uclass *uc;
526494d708SSimon Glass 	int ret;
536494d708SSimon Glass 
546494d708SSimon Glass 	*ucp = NULL;
556494d708SSimon Glass 	uc_drv = lists_uclass_lookup(id);
566494d708SSimon Glass 	if (!uc_drv) {
576494d708SSimon Glass 		dm_warn("Cannot find uclass for id %d: please add the UCLASS_DRIVER() declaration for this UCLASS_... id\n",
586494d708SSimon Glass 			id);
596494d708SSimon Glass 		return -ENOENT;
606494d708SSimon Glass 	}
616494d708SSimon Glass 	if (uc_drv->ops) {
626494d708SSimon Glass 		dm_warn("No ops for uclass id %d\n", id);
636494d708SSimon Glass 		return -EINVAL;
646494d708SSimon Glass 	}
656494d708SSimon Glass 	uc = calloc(1, sizeof(*uc));
666494d708SSimon Glass 	if (!uc)
676494d708SSimon Glass 		return -ENOMEM;
686494d708SSimon Glass 	if (uc_drv->priv_auto_alloc_size) {
696494d708SSimon Glass 		uc->priv = calloc(1, uc_drv->priv_auto_alloc_size);
706494d708SSimon Glass 		if (!uc->priv) {
716494d708SSimon Glass 			ret = -ENOMEM;
726494d708SSimon Glass 			goto fail_mem;
736494d708SSimon Glass 		}
746494d708SSimon Glass 	}
756494d708SSimon Glass 	uc->uc_drv = uc_drv;
766494d708SSimon Glass 	INIT_LIST_HEAD(&uc->sibling_node);
776494d708SSimon Glass 	INIT_LIST_HEAD(&uc->dev_head);
7889876a55SSimon Glass 	list_add(&uc->sibling_node, &DM_UCLASS_ROOT_NON_CONST);
796494d708SSimon Glass 
806494d708SSimon Glass 	if (uc_drv->init) {
816494d708SSimon Glass 		ret = uc_drv->init(uc);
826494d708SSimon Glass 		if (ret)
836494d708SSimon Glass 			goto fail;
846494d708SSimon Glass 	}
856494d708SSimon Glass 
866494d708SSimon Glass 	*ucp = uc;
876494d708SSimon Glass 
886494d708SSimon Glass 	return 0;
896494d708SSimon Glass fail:
906494d708SSimon Glass 	if (uc_drv->priv_auto_alloc_size) {
916494d708SSimon Glass 		free(uc->priv);
926494d708SSimon Glass 		uc->priv = NULL;
936494d708SSimon Glass 	}
946494d708SSimon Glass 	list_del(&uc->sibling_node);
956494d708SSimon Glass fail_mem:
966494d708SSimon Glass 	free(uc);
976494d708SSimon Glass 
986494d708SSimon Glass 	return ret;
996494d708SSimon Glass }
1006494d708SSimon Glass 
1016494d708SSimon Glass int uclass_destroy(struct uclass *uc)
1026494d708SSimon Glass {
1036494d708SSimon Glass 	struct uclass_driver *uc_drv;
10454c5d08aSHeiko Schocher 	struct udevice *dev, *tmp;
1056494d708SSimon Glass 	int ret;
1066494d708SSimon Glass 
1076494d708SSimon Glass 	list_for_each_entry_safe(dev, tmp, &uc->dev_head, uclass_node) {
1086494d708SSimon Glass 		ret = device_remove(dev);
1096494d708SSimon Glass 		if (ret)
1106494d708SSimon Glass 			return ret;
1116494d708SSimon Glass 		ret = device_unbind(dev);
1126494d708SSimon Glass 		if (ret)
1136494d708SSimon Glass 			return ret;
1146494d708SSimon Glass 	}
1156494d708SSimon Glass 
1166494d708SSimon Glass 	uc_drv = uc->uc_drv;
1176494d708SSimon Glass 	if (uc_drv->destroy)
1186494d708SSimon Glass 		uc_drv->destroy(uc);
1196494d708SSimon Glass 	list_del(&uc->sibling_node);
1206494d708SSimon Glass 	if (uc_drv->priv_auto_alloc_size)
1216494d708SSimon Glass 		free(uc->priv);
1226494d708SSimon Glass 	free(uc);
1236494d708SSimon Glass 
1246494d708SSimon Glass 	return 0;
1256494d708SSimon Glass }
1266494d708SSimon Glass 
1276494d708SSimon Glass int uclass_get(enum uclass_id id, struct uclass **ucp)
1286494d708SSimon Glass {
1296494d708SSimon Glass 	struct uclass *uc;
1306494d708SSimon Glass 
1316494d708SSimon Glass 	*ucp = NULL;
1326494d708SSimon Glass 	uc = uclass_find(id);
1336494d708SSimon Glass 	if (!uc)
1346494d708SSimon Glass 		return uclass_add(id, ucp);
1356494d708SSimon Glass 	*ucp = uc;
1366494d708SSimon Glass 
1376494d708SSimon Glass 	return 0;
1386494d708SSimon Glass }
1396494d708SSimon Glass 
14054c5d08aSHeiko Schocher int uclass_find_device(enum uclass_id id, int index, struct udevice **devp)
1416494d708SSimon Glass {
1426494d708SSimon Glass 	struct uclass *uc;
14354c5d08aSHeiko Schocher 	struct udevice *dev;
1446494d708SSimon Glass 	int ret;
1456494d708SSimon Glass 
1466494d708SSimon Glass 	*devp = NULL;
1476494d708SSimon Glass 	ret = uclass_get(id, &uc);
1486494d708SSimon Glass 	if (ret)
1496494d708SSimon Glass 		return ret;
1506494d708SSimon Glass 
1516494d708SSimon Glass 	list_for_each_entry(dev, &uc->dev_head, uclass_node) {
1526494d708SSimon Glass 		if (!index--) {
1536494d708SSimon Glass 			*devp = dev;
1546494d708SSimon Glass 			return 0;
1556494d708SSimon Glass 		}
1566494d708SSimon Glass 	}
1576494d708SSimon Glass 
1586494d708SSimon Glass 	return -ENODEV;
1596494d708SSimon Glass }
1606494d708SSimon Glass 
161*9ca296a1SSimon Glass /**
162*9ca296a1SSimon Glass  * uclass_get_device_tail() - handle the end of a get_device call
163*9ca296a1SSimon Glass  *
164*9ca296a1SSimon Glass  * This handles returning an error or probing a device as needed.
165*9ca296a1SSimon Glass  *
166*9ca296a1SSimon Glass  * @dev: Device that needs to be probed
167*9ca296a1SSimon Glass  * @ret: Error to return. If non-zero then the device is not probed
168*9ca296a1SSimon Glass  * @devp: Returns the value of 'dev' if there is no error
169*9ca296a1SSimon Glass  * @return ret, if non-zero, else the result of the device_probe() call
170*9ca296a1SSimon Glass  */
171*9ca296a1SSimon Glass static int uclass_get_device_tail(struct udevice *dev, int ret,
172*9ca296a1SSimon Glass 				  struct udevice **devp)
1736494d708SSimon Glass {
1746494d708SSimon Glass 	if (ret)
1756494d708SSimon Glass 		return ret;
1766494d708SSimon Glass 
1776494d708SSimon Glass 	ret = device_probe(dev);
1786494d708SSimon Glass 	if (ret)
1796494d708SSimon Glass 		return ret;
1806494d708SSimon Glass 
1816494d708SSimon Glass 	*devp = dev;
1826494d708SSimon Glass 
1836494d708SSimon Glass 	return 0;
1846494d708SSimon Glass }
1856494d708SSimon Glass 
186*9ca296a1SSimon Glass int uclass_get_device(enum uclass_id id, int index, struct udevice **devp)
187*9ca296a1SSimon Glass {
188*9ca296a1SSimon Glass 	struct udevice *dev;
189*9ca296a1SSimon Glass 	int ret;
190*9ca296a1SSimon Glass 
191*9ca296a1SSimon Glass 	*devp = NULL;
192*9ca296a1SSimon Glass 	ret = uclass_find_device(id, index, &dev);
193*9ca296a1SSimon Glass 	return uclass_get_device_tail(dev, ret, devp);
194*9ca296a1SSimon Glass }
195*9ca296a1SSimon Glass 
19654c5d08aSHeiko Schocher int uclass_first_device(enum uclass_id id, struct udevice **devp)
1976494d708SSimon Glass {
1986494d708SSimon Glass 	struct uclass *uc;
19954c5d08aSHeiko Schocher 	struct udevice *dev;
2006494d708SSimon Glass 	int ret;
2016494d708SSimon Glass 
2026494d708SSimon Glass 	*devp = NULL;
2036494d708SSimon Glass 	ret = uclass_get(id, &uc);
2046494d708SSimon Glass 	if (ret)
2056494d708SSimon Glass 		return ret;
2066494d708SSimon Glass 	if (list_empty(&uc->dev_head))
2076494d708SSimon Glass 		return 0;
2086494d708SSimon Glass 
20954c5d08aSHeiko Schocher 	dev = list_first_entry(&uc->dev_head, struct udevice, uclass_node);
2106494d708SSimon Glass 	ret = device_probe(dev);
2116494d708SSimon Glass 	if (ret)
2126494d708SSimon Glass 		return ret;
2136494d708SSimon Glass 	*devp = dev;
2146494d708SSimon Glass 
2156494d708SSimon Glass 	return 0;
2166494d708SSimon Glass }
2176494d708SSimon Glass 
21854c5d08aSHeiko Schocher int uclass_next_device(struct udevice **devp)
2196494d708SSimon Glass {
22054c5d08aSHeiko Schocher 	struct udevice *dev = *devp;
2216494d708SSimon Glass 	int ret;
2226494d708SSimon Glass 
2236494d708SSimon Glass 	*devp = NULL;
2246494d708SSimon Glass 	if (list_is_last(&dev->uclass_node, &dev->uclass->dev_head))
2256494d708SSimon Glass 		return 0;
2266494d708SSimon Glass 
22754c5d08aSHeiko Schocher 	dev = list_entry(dev->uclass_node.next, struct udevice,
22854c5d08aSHeiko Schocher 			 uclass_node);
2296494d708SSimon Glass 	ret = device_probe(dev);
2306494d708SSimon Glass 	if (ret)
2316494d708SSimon Glass 		return ret;
2326494d708SSimon Glass 	*devp = dev;
2336494d708SSimon Glass 
2346494d708SSimon Glass 	return 0;
2356494d708SSimon Glass }
2366494d708SSimon Glass 
23754c5d08aSHeiko Schocher int uclass_bind_device(struct udevice *dev)
2386494d708SSimon Glass {
2396494d708SSimon Glass 	struct uclass *uc;
2406494d708SSimon Glass 	int ret;
2416494d708SSimon Glass 
2426494d708SSimon Glass 	uc = dev->uclass;
2436494d708SSimon Glass 
2446494d708SSimon Glass 	list_add_tail(&dev->uclass_node, &uc->dev_head);
2456494d708SSimon Glass 
2466494d708SSimon Glass 	if (uc->uc_drv->post_bind) {
2476494d708SSimon Glass 		ret = uc->uc_drv->post_bind(dev);
2486494d708SSimon Glass 		if (ret) {
2496494d708SSimon Glass 			list_del(&dev->uclass_node);
2506494d708SSimon Glass 			return ret;
2516494d708SSimon Glass 		}
2526494d708SSimon Glass 	}
2536494d708SSimon Glass 
2546494d708SSimon Glass 	return 0;
2556494d708SSimon Glass }
2566494d708SSimon Glass 
25754c5d08aSHeiko Schocher int uclass_unbind_device(struct udevice *dev)
2586494d708SSimon Glass {
2596494d708SSimon Glass 	struct uclass *uc;
2606494d708SSimon Glass 	int ret;
2616494d708SSimon Glass 
2626494d708SSimon Glass 	uc = dev->uclass;
2636494d708SSimon Glass 	if (uc->uc_drv->pre_unbind) {
2646494d708SSimon Glass 		ret = uc->uc_drv->pre_unbind(dev);
2656494d708SSimon Glass 		if (ret)
2666494d708SSimon Glass 			return ret;
2676494d708SSimon Glass 	}
2686494d708SSimon Glass 
2696494d708SSimon Glass 	list_del(&dev->uclass_node);
2706494d708SSimon Glass 	return 0;
2716494d708SSimon Glass }
2726494d708SSimon Glass 
27354c5d08aSHeiko Schocher int uclass_post_probe_device(struct udevice *dev)
2746494d708SSimon Glass {
2756494d708SSimon Glass 	struct uclass_driver *uc_drv = dev->uclass->uc_drv;
2766494d708SSimon Glass 
2776494d708SSimon Glass 	if (uc_drv->post_probe)
2786494d708SSimon Glass 		return uc_drv->post_probe(dev);
2796494d708SSimon Glass 
2806494d708SSimon Glass 	return 0;
2816494d708SSimon Glass }
2826494d708SSimon Glass 
28354c5d08aSHeiko Schocher int uclass_pre_remove_device(struct udevice *dev)
2846494d708SSimon Glass {
2856494d708SSimon Glass 	struct uclass_driver *uc_drv;
2866494d708SSimon Glass 	struct uclass *uc;
2876494d708SSimon Glass 	int ret;
2886494d708SSimon Glass 
2896494d708SSimon Glass 	uc = dev->uclass;
2906494d708SSimon Glass 	uc_drv = uc->uc_drv;
2916494d708SSimon Glass 	if (uc->uc_drv->pre_remove) {
2926494d708SSimon Glass 		ret = uc->uc_drv->pre_remove(dev);
2936494d708SSimon Glass 		if (ret)
2946494d708SSimon Glass 			return ret;
2956494d708SSimon Glass 	}
2966494d708SSimon Glass 	if (uc_drv->per_device_auto_alloc_size) {
2976494d708SSimon Glass 		free(dev->uclass_priv);
2986494d708SSimon Glass 		dev->uclass_priv = NULL;
2996494d708SSimon Glass 	}
3006494d708SSimon Glass 
3016494d708SSimon Glass 	return 0;
3026494d708SSimon Glass }
303