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