uboot/drivers/core/device-remove.c
<<
>>
Prefs
   1/*
   2 * Device manager
   3 *
   4 * Copyright (c) 2014 Google, Inc
   5 *
   6 * (C) Copyright 2012
   7 * Pavel Herrmann <morpheus.ibis@gmail.com>
   8 *
   9 * SPDX-License-Identifier:     GPL-2.0+
  10 */
  11
  12#include <common.h>
  13#include <errno.h>
  14#include <malloc.h>
  15#include <dm/device.h>
  16#include <dm/device-internal.h>
  17#include <dm/uclass.h>
  18#include <dm/uclass-internal.h>
  19#include <dm/util.h>
  20
  21/**
  22 * device_chld_unbind() - Unbind all device's children from the device
  23 *
  24 * On error, the function continues to unbind all children, and reports the
  25 * first error.
  26 *
  27 * @dev:        The device that is to be stripped of its children
  28 * @return 0 on success, -ve on error
  29 */
  30static int device_chld_unbind(struct udevice *dev)
  31{
  32        struct udevice *pos, *n;
  33        int ret, saved_ret = 0;
  34
  35        assert(dev);
  36
  37        list_for_each_entry_safe(pos, n, &dev->child_head, sibling_node) {
  38                ret = device_unbind(pos);
  39                if (ret && !saved_ret)
  40                        saved_ret = ret;
  41        }
  42
  43        return saved_ret;
  44}
  45
  46/**
  47 * device_chld_remove() - Stop all device's children
  48 * @dev:        The device whose children are to be removed
  49 * @pre_os_remove: Flag, if this functions is called in the pre-OS stage
  50 * @return 0 on success, -ve on error
  51 */
  52static int device_chld_remove(struct udevice *dev, uint flags)
  53{
  54        struct udevice *pos, *n;
  55        int ret;
  56
  57        assert(dev);
  58
  59        list_for_each_entry_safe(pos, n, &dev->child_head, sibling_node) {
  60                ret = device_remove(pos, flags);
  61                if (ret)
  62                        return ret;
  63        }
  64
  65        return 0;
  66}
  67
  68int device_unbind(struct udevice *dev)
  69{
  70        const struct driver *drv;
  71        int ret;
  72
  73        if (!dev)
  74                return -EINVAL;
  75
  76        if (dev->flags & DM_FLAG_ACTIVATED)
  77                return -EINVAL;
  78
  79        if (!(dev->flags & DM_FLAG_BOUND))
  80                return -EINVAL;
  81
  82        drv = dev->driver;
  83        assert(drv);
  84
  85        if (drv->unbind) {
  86                ret = drv->unbind(dev);
  87                if (ret)
  88                        return ret;
  89        }
  90
  91        ret = device_chld_unbind(dev);
  92        if (ret)
  93                return ret;
  94
  95        if (dev->flags & DM_FLAG_ALLOC_PDATA) {
  96                free(dev->platdata);
  97                dev->platdata = NULL;
  98        }
  99        if (dev->flags & DM_FLAG_ALLOC_UCLASS_PDATA) {
 100                free(dev->uclass_platdata);
 101                dev->uclass_platdata = NULL;
 102        }
 103        if (dev->flags & DM_FLAG_ALLOC_PARENT_PDATA) {
 104                free(dev->parent_platdata);
 105                dev->parent_platdata = NULL;
 106        }
 107        ret = uclass_unbind_device(dev);
 108        if (ret)
 109                return ret;
 110
 111        if (dev->parent)
 112                list_del(&dev->sibling_node);
 113
 114        devres_release_all(dev);
 115
 116        if (dev->flags & DM_FLAG_NAME_ALLOCED)
 117                free((char *)dev->name);
 118        free(dev);
 119
 120        return 0;
 121}
 122
 123/**
 124 * device_free() - Free memory buffers allocated by a device
 125 * @dev:        Device that is to be started
 126 */
 127void device_free(struct udevice *dev)
 128{
 129        int size;
 130
 131        if (dev->driver->priv_auto_alloc_size) {
 132                free(dev->priv);
 133                dev->priv = NULL;
 134        }
 135        size = dev->uclass->uc_drv->per_device_auto_alloc_size;
 136        if (size) {
 137                free(dev->uclass_priv);
 138                dev->uclass_priv = NULL;
 139        }
 140        if (dev->parent) {
 141                size = dev->parent->driver->per_child_auto_alloc_size;
 142                if (!size) {
 143                        size = dev->parent->uclass->uc_drv->
 144                                        per_child_auto_alloc_size;
 145                }
 146                if (size) {
 147                        free(dev->parent_priv);
 148                        dev->parent_priv = NULL;
 149                }
 150        }
 151
 152        devres_release_probe(dev);
 153}
 154
 155int device_remove(struct udevice *dev, uint flags)
 156{
 157        const struct driver *drv;
 158        int ret;
 159
 160        if (!dev)
 161                return -EINVAL;
 162
 163        if (!(dev->flags & DM_FLAG_ACTIVATED))
 164                return 0;
 165
 166        drv = dev->driver;
 167        assert(drv);
 168
 169        ret = uclass_pre_remove_device(dev);
 170        if (ret)
 171                return ret;
 172
 173        ret = device_chld_remove(dev, flags);
 174        if (ret)
 175                goto err;
 176
 177        /*
 178         * Remove the device if called with the "normal" remove flag set,
 179         * or if the remove flag matches any of the drivers remove flags
 180         */
 181        if (drv->remove &&
 182            ((flags & DM_REMOVE_NORMAL) ||
 183             (flags & (drv->flags & DM_FLAG_ACTIVE_DMA)))) {
 184                ret = drv->remove(dev);
 185                if (ret)
 186                        goto err_remove;
 187        }
 188
 189        if (dev->parent && dev->parent->driver->child_post_remove) {
 190                ret = dev->parent->driver->child_post_remove(dev);
 191                if (ret) {
 192                        dm_warn("%s: Device '%s' failed child_post_remove()",
 193                                __func__, dev->name);
 194                }
 195        }
 196
 197        if ((flags & DM_REMOVE_NORMAL) ||
 198            (flags & (drv->flags & DM_FLAG_ACTIVE_DMA))) {
 199                device_free(dev);
 200
 201                dev->seq = -1;
 202                dev->flags &= ~DM_FLAG_ACTIVATED;
 203        }
 204
 205        return ret;
 206
 207err_remove:
 208        /* We can't put the children back */
 209        dm_warn("%s: Device '%s' failed to remove, but children are gone\n",
 210                __func__, dev->name);
 211err:
 212        ret = uclass_post_probe_device(dev);
 213        if (ret) {
 214                dm_warn("%s: Device '%s' failed to post_probe on error path\n",
 215                        __func__, dev->name);
 216        }
 217
 218        return ret;
 219}
 220