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 * @return 0 on success, -ve on error
  50 */
  51static int device_chld_remove(struct udevice *dev)
  52{
  53        struct udevice *pos, *n;
  54        int ret;
  55
  56        assert(dev);
  57
  58        list_for_each_entry_safe(pos, n, &dev->child_head, sibling_node) {
  59                ret = device_remove(pos);
  60                if (ret)
  61                        return ret;
  62        }
  63
  64        return 0;
  65}
  66
  67int device_unbind(struct udevice *dev)
  68{
  69        const struct driver *drv;
  70        int ret;
  71
  72        if (!dev)
  73                return -EINVAL;
  74
  75        if (dev->flags & DM_FLAG_ACTIVATED)
  76                return -EINVAL;
  77
  78        if (!(dev->flags & DM_FLAG_BOUND))
  79                return -EINVAL;
  80
  81        drv = dev->driver;
  82        assert(drv);
  83
  84        if (drv->unbind) {
  85                ret = drv->unbind(dev);
  86                if (ret)
  87                        return ret;
  88        }
  89
  90        ret = device_chld_unbind(dev);
  91        if (ret)
  92                return ret;
  93
  94        if (dev->flags & DM_FLAG_ALLOC_PDATA) {
  95                free(dev->platdata);
  96                dev->platdata = NULL;
  97        }
  98        if (dev->flags & DM_FLAG_ALLOC_UCLASS_PDATA) {
  99                free(dev->uclass_platdata);
 100                dev->uclass_platdata = NULL;
 101        }
 102        if (dev->flags & DM_FLAG_ALLOC_PARENT_PDATA) {
 103                free(dev->parent_platdata);
 104                dev->parent_platdata = NULL;
 105        }
 106        ret = uclass_unbind_device(dev);
 107        if (ret)
 108                return ret;
 109
 110        if (dev->parent)
 111                list_del(&dev->sibling_node);
 112
 113        devres_release_all(dev);
 114
 115        if (dev->flags & DM_NAME_ALLOCED)
 116                free((char *)dev->name);
 117        free(dev);
 118
 119        return 0;
 120}
 121
 122/**
 123 * device_free() - Free memory buffers allocated by a device
 124 * @dev:        Device that is to be started
 125 */
 126void device_free(struct udevice *dev)
 127{
 128        int size;
 129
 130        if (dev->driver->priv_auto_alloc_size) {
 131                free(dev->priv);
 132                dev->priv = NULL;
 133        }
 134        size = dev->uclass->uc_drv->per_device_auto_alloc_size;
 135        if (size) {
 136                free(dev->uclass_priv);
 137                dev->uclass_priv = NULL;
 138        }
 139        if (dev->parent) {
 140                size = dev->parent->driver->per_child_auto_alloc_size;
 141                if (!size) {
 142                        size = dev->parent->uclass->uc_drv->
 143                                        per_child_auto_alloc_size;
 144                }
 145                if (size) {
 146                        free(dev->parent_priv);
 147                        dev->parent_priv = NULL;
 148                }
 149        }
 150
 151        devres_release_probe(dev);
 152}
 153
 154int device_remove(struct udevice *dev)
 155{
 156        const struct driver *drv;
 157        int ret;
 158
 159        if (!dev)
 160                return -EINVAL;
 161
 162        if (!(dev->flags & DM_FLAG_ACTIVATED))
 163                return 0;
 164
 165        drv = dev->driver;
 166        assert(drv);
 167
 168        ret = uclass_pre_remove_device(dev);
 169        if (ret)
 170                return ret;
 171
 172        ret = device_chld_remove(dev);
 173        if (ret)
 174                goto err;
 175
 176        if (drv->remove) {
 177                ret = drv->remove(dev);
 178                if (ret)
 179                        goto err_remove;
 180        }
 181
 182        if (dev->parent && dev->parent->driver->child_post_remove) {
 183                ret = dev->parent->driver->child_post_remove(dev);
 184                if (ret) {
 185                        dm_warn("%s: Device '%s' failed child_post_remove()",
 186                                __func__, dev->name);
 187                }
 188        }
 189
 190        device_free(dev);
 191
 192        dev->seq = -1;
 193        dev->flags &= ~DM_FLAG_ACTIVATED;
 194
 195        return ret;
 196
 197err_remove:
 198        /* We can't put the children back */
 199        dm_warn("%s: Device '%s' failed to remove, but children are gone\n",
 200                __func__, dev->name);
 201err:
 202        ret = uclass_post_probe_device(dev);
 203        if (ret) {
 204                dm_warn("%s: Device '%s' failed to post_probe on error path\n",
 205                        __func__, dev->name);
 206        }
 207
 208        return ret;
 209}
 210