blob: 44a5f17f0da0aece2f52109b5289bfe4fc2a3236 [file] [log] [blame]
Jean-Jacques Hiblote83a31b2018-08-09 16:17:46 +02001// 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
12static 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
49static 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
69static 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
93static 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
121static 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
163static 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
197static 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
242U_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
249U_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);