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