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