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