Merge branch '2019-10-28-azure-ci-support'
[oweals/u-boot.git] / cmd / bind.c
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 );