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, drv, 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 "Use 'dm tree' to list all devices registered in the driver model,\n" 250 "their path, class, index and current driver.\n" 251); 252 253U_BOOT_CMD( 254 unbind, 4, 0, do_bind_unbind, 255 "Unbind a device from a driver", 256 "<node path>\n" 257 "unbind <class> <index>\n" 258 "unbind <class> <index> <driver>\n" 259 "Use 'dm tree' to list all devices registered in the driver model,\n" 260 "their path, class, index and current driver.\n" 261); 262