xref: /openbmc/u-boot/cmd/bind.c (revision 8a23fc9c)
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