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 
bind_by_class_index(const char * uclass,int index,const char * drv_name)14 static 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 
find_dev(const char * uclass,int index,struct udevice ** devp)51 static 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 
unbind_by_class_index(const char * uclass,int index)71 static 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 
unbind_child_by_class_index(const char * uclass,int index,const char * drv_name)95 static 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 
bind_by_node_path(const char * path,const char * drv_name)123 static 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 
unbind_by_node_path(const char * path)165 static 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 
do_bind_unbind(struct cmd_tbl * cmdtp,int flag,int argc,char * const argv[])199 static 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) ? simple_strtoul(argv[2], NULL, 10) : 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) ? simple_strtoul(argv[2], NULL, 10) : 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 
244 U_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 
251 U_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