123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285 |
- /*
- * Copyright (c) 2013 Google, Inc
- *
- * (C) Copyright 2012
- * Pavel Herrmann <morpheus.ibis@gmail.com>
- *
- * SPDX-License-Identifier: GPL-2.0+
- */
- #include <common.h>
- #include <errno.h>
- #include <malloc.h>
- #include <dm/device.h>
- #include <dm/device-internal.h>
- #include <dm/lists.h>
- #include <dm/uclass.h>
- #include <dm/uclass-internal.h>
- #include <dm/util.h>
- DECLARE_GLOBAL_DATA_PTR;
- struct uclass *uclass_find(enum uclass_id key)
- {
- struct uclass *uc;
- /*
- * TODO(sjg@chromium.org): Optimise this, perhaps moving the found
- * node to the start of the list, or creating a linear array mapping
- * id to node.
- */
- list_for_each_entry(uc, &gd->uclass_root, sibling_node) {
- if (uc->uc_drv->id == key)
- return uc;
- }
- return NULL;
- }
- /**
- * uclass_add() - Create new uclass in list
- * @id: Id number to create
- * @ucp: Returns pointer to uclass, or NULL on error
- * @return 0 on success, -ve on error
- *
- * The new uclass is added to the list. There must be only one uclass for
- * each id.
- */
- static int uclass_add(enum uclass_id id, struct uclass **ucp)
- {
- struct uclass_driver *uc_drv;
- struct uclass *uc;
- int ret;
- *ucp = NULL;
- uc_drv = lists_uclass_lookup(id);
- if (!uc_drv) {
- dm_warn("Cannot find uclass for id %d: please add the UCLASS_DRIVER() declaration for this UCLASS_... id\n",
- id);
- return -ENOENT;
- }
- if (uc_drv->ops) {
- dm_warn("No ops for uclass id %d\n", id);
- return -EINVAL;
- }
- uc = calloc(1, sizeof(*uc));
- if (!uc)
- return -ENOMEM;
- if (uc_drv->priv_auto_alloc_size) {
- uc->priv = calloc(1, uc_drv->priv_auto_alloc_size);
- if (!uc->priv) {
- ret = -ENOMEM;
- goto fail_mem;
- }
- }
- uc->uc_drv = uc_drv;
- INIT_LIST_HEAD(&uc->sibling_node);
- INIT_LIST_HEAD(&uc->dev_head);
- list_add(&uc->sibling_node, &gd->uclass_root);
- if (uc_drv->init) {
- ret = uc_drv->init(uc);
- if (ret)
- goto fail;
- }
- *ucp = uc;
- return 0;
- fail:
- if (uc_drv->priv_auto_alloc_size) {
- free(uc->priv);
- uc->priv = NULL;
- }
- list_del(&uc->sibling_node);
- fail_mem:
- free(uc);
- return ret;
- }
- int uclass_destroy(struct uclass *uc)
- {
- struct uclass_driver *uc_drv;
- struct device *dev, *tmp;
- int ret;
- list_for_each_entry_safe(dev, tmp, &uc->dev_head, uclass_node) {
- ret = device_remove(dev);
- if (ret)
- return ret;
- ret = device_unbind(dev);
- if (ret)
- return ret;
- }
- uc_drv = uc->uc_drv;
- if (uc_drv->destroy)
- uc_drv->destroy(uc);
- list_del(&uc->sibling_node);
- if (uc_drv->priv_auto_alloc_size)
- free(uc->priv);
- free(uc);
- return 0;
- }
- int uclass_get(enum uclass_id id, struct uclass **ucp)
- {
- struct uclass *uc;
- *ucp = NULL;
- uc = uclass_find(id);
- if (!uc)
- return uclass_add(id, ucp);
- *ucp = uc;
- return 0;
- }
- int uclass_find_device(enum uclass_id id, int index, struct device **devp)
- {
- struct uclass *uc;
- struct device *dev;
- int ret;
- *devp = NULL;
- ret = uclass_get(id, &uc);
- if (ret)
- return ret;
- list_for_each_entry(dev, &uc->dev_head, uclass_node) {
- if (!index--) {
- *devp = dev;
- return 0;
- }
- }
- return -ENODEV;
- }
- int uclass_get_device(enum uclass_id id, int index, struct device **devp)
- {
- struct device *dev;
- int ret;
- *devp = NULL;
- ret = uclass_find_device(id, index, &dev);
- if (ret)
- return ret;
- ret = device_probe(dev);
- if (ret)
- return ret;
- *devp = dev;
- return 0;
- }
- int uclass_first_device(enum uclass_id id, struct device **devp)
- {
- struct uclass *uc;
- struct device *dev;
- int ret;
- *devp = NULL;
- ret = uclass_get(id, &uc);
- if (ret)
- return ret;
- if (list_empty(&uc->dev_head))
- return 0;
- dev = list_first_entry(&uc->dev_head, struct device, uclass_node);
- ret = device_probe(dev);
- if (ret)
- return ret;
- *devp = dev;
- return 0;
- }
- int uclass_next_device(struct device **devp)
- {
- struct device *dev = *devp;
- int ret;
- *devp = NULL;
- if (list_is_last(&dev->uclass_node, &dev->uclass->dev_head))
- return 0;
- dev = list_entry(dev->uclass_node.next, struct device, uclass_node);
- ret = device_probe(dev);
- if (ret)
- return ret;
- *devp = dev;
- return 0;
- }
- int uclass_bind_device(struct device *dev)
- {
- struct uclass *uc;
- int ret;
- uc = dev->uclass;
- list_add_tail(&dev->uclass_node, &uc->dev_head);
- if (uc->uc_drv->post_bind) {
- ret = uc->uc_drv->post_bind(dev);
- if (ret) {
- list_del(&dev->uclass_node);
- return ret;
- }
- }
- return 0;
- }
- int uclass_unbind_device(struct device *dev)
- {
- struct uclass *uc;
- int ret;
- uc = dev->uclass;
- if (uc->uc_drv->pre_unbind) {
- ret = uc->uc_drv->pre_unbind(dev);
- if (ret)
- return ret;
- }
- list_del(&dev->uclass_node);
- return 0;
- }
- int uclass_post_probe_device(struct device *dev)
- {
- struct uclass_driver *uc_drv = dev->uclass->uc_drv;
- if (uc_drv->post_probe)
- return uc_drv->post_probe(dev);
- return 0;
- }
- int uclass_pre_remove_device(struct device *dev)
- {
- struct uclass_driver *uc_drv;
- struct uclass *uc;
- int ret;
- uc = dev->uclass;
- uc_drv = uc->uc_drv;
- if (uc->uc_drv->pre_remove) {
- ret = uc->uc_drv->pre_remove(dev);
- if (ret)
- return ret;
- }
- if (uc_drv->per_device_auto_alloc_size) {
- free(dev->uclass_priv);
- dev->uclass_priv = NULL;
- }
- return 0;
- }
|