uboot/cmd/bind.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0+
   2/*
   3 * Copyright (c) 2018 JJ Hiblot <jjhiblot@ti.com>
   4 */
   5
   6#include <common.h>
   7#include <command.h>
   8#include <dm.h>
   9#include <dm/device-internal.h>
  10#include <dm/lists.h>
  11#include <dm/root.h>
  12#include <dm/uclass-internal.h>
  13
  14static int bind_by_class_index(const char *uclass, int index,
  15                               const char *drv_name)
  16{
  17        static enum uclass_id uclass_id;
  18        struct udevice *dev;
  19        struct udevice *parent;
  20        int ret;
  21        struct driver *drv;
  22
  23        drv = lists_driver_lookup_name(drv_name);
  24        if (!drv) {
  25                printf("Cannot find driver '%s'\n", drv_name);
  26                return -ENOENT;
  27        }
  28
  29        uclass_id = uclass_get_by_name(uclass);
  30        if (uclass_id == UCLASS_INVALID) {
  31                printf("%s is not a valid uclass\n", uclass);
  32                return -EINVAL;
  33        }
  34
  35        ret = uclass_find_device(uclass_id, index, &parent);
  36        if (!parent || ret) {
  37                printf("Cannot find device %d of class %s\n", index, uclass);
  38                return ret;
  39        }
  40
  41        ret = device_bind_with_driver_data(parent, drv, drv->name, 0,
  42                                           ofnode_null(), &dev);
  43        if (!dev || ret) {
  44                printf("Unable to bind. err:%d\n", ret);
  45                return ret;
  46        }
  47
  48        return 0;
  49}
  50
  51static int find_dev(const char *uclass, int index, struct udevice **devp)
  52{
  53        static enum uclass_id uclass_id;
  54        int rc;
  55
  56        uclass_id = uclass_get_by_name(uclass);
  57        if (uclass_id == UCLASS_INVALID) {
  58                printf("%s is not a valid uclass\n", uclass);
  59                return -EINVAL;
  60        }
  61
  62        rc = uclass_find_device(uclass_id, index, devp);
  63        if (!*devp || rc) {
  64                printf("Cannot find device %d of class %s\n", index, uclass);
  65                return rc;
  66        }
  67
  68        return 0;
  69}
  70
  71static int unbind_by_class_index(const char *uclass, int index)
  72{
  73        int ret;
  74        struct udevice *dev;
  75
  76        ret = find_dev(uclass, index, &dev);
  77        if (ret)
  78                return ret;
  79
  80        ret = device_remove(dev, DM_REMOVE_NORMAL);
  81        if (ret) {
  82                printf("Unable to remove. err:%d\n", ret);
  83                return ret;
  84        }
  85
  86        ret = device_unbind(dev);
  87        if (ret) {
  88                printf("Unable to unbind. err:%d\n", ret);
  89                return ret;
  90        }
  91
  92        return 0;
  93}
  94
  95static int unbind_child_by_class_index(const char *uclass, int index,
  96                                       const char *drv_name)
  97{
  98        struct udevice *parent;
  99        int ret;
 100        struct driver *drv;
 101
 102        drv = lists_driver_lookup_name(drv_name);
 103        if (!drv) {
 104                printf("Cannot find driver '%s'\n", drv_name);
 105                return -ENOENT;
 106        }
 107
 108        ret = find_dev(uclass, index, &parent);
 109        if (ret)
 110                return ret;
 111
 112        ret = device_chld_remove(parent, drv, DM_REMOVE_NORMAL);
 113        if (ret)
 114                printf("Unable to remove all. err:%d\n", ret);
 115
 116        ret = device_chld_unbind(parent, drv);
 117        if (ret)
 118                printf("Unable to unbind all. err:%d\n", ret);
 119
 120        return ret;
 121}
 122
 123static int bind_by_node_path(const char *path, const char *drv_name)
 124{
 125        struct udevice *dev;
 126        struct udevice *parent = NULL;
 127        int ret;
 128        ofnode ofnode;
 129        struct driver *drv;
 130
 131        drv = lists_driver_lookup_name(drv_name);
 132        if (!drv) {
 133                printf("%s is not a valid driver name\n", drv_name);
 134                return -ENOENT;
 135        }
 136
 137        ofnode = ofnode_path(path);
 138        if (!ofnode_valid(ofnode)) {
 139                printf("%s is not a valid node path\n", path);
 140                return -EINVAL;
 141        }
 142
 143        while (ofnode_valid(ofnode)) {
 144                if (!device_find_global_by_ofnode(ofnode, &parent))
 145                        break;
 146                ofnode = ofnode_get_parent(ofnode);
 147        }
 148
 149        if (!parent) {
 150                printf("Cannot find a parent device for node path %s\n", path);
 151                return -ENODEV;
 152        }
 153
 154        ofnode = ofnode_path(path);
 155        ret = lists_bind_fdt(parent, ofnode, &dev, false);
 156
 157        if (!dev || ret) {
 158                printf("Unable to bind. err:%d\n", ret);
 159                return ret;
 160        }
 161
 162        return 0;
 163}
 164
 165static int unbind_by_node_path(const char *path)
 166{
 167        struct udevice *dev;
 168        int ret;
 169        ofnode ofnode;
 170
 171        ofnode = ofnode_path(path);
 172        if (!ofnode_valid(ofnode)) {
 173                printf("%s is not a valid node path\n", path);
 174                return -EINVAL;
 175        }
 176
 177        ret = device_find_global_by_ofnode(ofnode, &dev);
 178
 179        if (!dev || ret) {
 180                printf("Cannot find a device with path %s\n", path);
 181                return -ENODEV;
 182        }
 183
 184        ret = device_remove(dev, DM_REMOVE_NORMAL);
 185        if (ret) {
 186                printf("Unable to remove. err:%d\n", ret);
 187                return ret;
 188        }
 189
 190        ret = device_unbind(dev);
 191        if (ret) {
 192                printf("Unable to unbind. err:%d\n", ret);
 193                return ret;
 194        }
 195
 196        return 0;
 197}
 198
 199static int do_bind_unbind(struct cmd_tbl *cmdtp, int flag, int argc,
 200                          char *const argv[])
 201{
 202        int ret = 0;
 203        bool bind;
 204        bool by_node;
 205
 206        if (argc < 2)
 207                return CMD_RET_USAGE;
 208
 209        bind = (argv[0][0] == 'b');
 210        by_node = (argv[1][0] == '/');
 211
 212        if (by_node && bind) {
 213                if (argc != 3)
 214                        return CMD_RET_USAGE;
 215                ret = bind_by_node_path(argv[1], argv[2]);
 216        } else if (by_node && !bind) {
 217                if (argc != 2)
 218                        return CMD_RET_USAGE;
 219                ret = unbind_by_node_path(argv[1]);
 220        } else if (!by_node && bind) {
 221                int index = (argc > 2) ? dectoul(argv[2], NULL) : 0;
 222
 223                if (argc != 4)
 224                        return CMD_RET_USAGE;
 225                ret = bind_by_class_index(argv[1], index, argv[3]);
 226        } else if (!by_node && !bind) {
 227                int index = (argc > 2) ? dectoul(argv[2], NULL) : 0;
 228
 229                if (argc == 3)
 230                        ret = unbind_by_class_index(argv[1], index);
 231                else if (argc == 4)
 232                        ret = unbind_child_by_class_index(argv[1], index,
 233                                                          argv[3]);
 234                else
 235                        return CMD_RET_USAGE;
 236        }
 237
 238        if (ret)
 239                return CMD_RET_FAILURE;
 240        else
 241                return CMD_RET_SUCCESS;
 242}
 243
 244U_BOOT_CMD(
 245        bind,   4,      0,      do_bind_unbind,
 246        "Bind a device to a driver",
 247        "<node path> <driver>\n"
 248        "bind <class> <index> <driver>\n"
 249);
 250
 251U_BOOT_CMD(
 252        unbind, 4,      0,      do_bind_unbind,
 253        "Unbind a device from a driver",
 254        "<node path>\n"
 255        "unbind <class> <index>\n"
 256        "unbind <class> <index> <driver>\n"
 257);
 258