Merge tag 'u-boot-imx-20200825' of https://gitlab.denx.de/u-boot/custodians/u-boot-imx
[platform/kernel/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/root.h>
12 #include <dm/uclass-internal.h>
13
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
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
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
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
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
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
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 );