]>
Commit | Line | Data |
---|---|---|
83d290c5 | 1 | // SPDX-License-Identifier: GPL-2.0+ |
6494d708 SG |
2 | /* |
3 | * Copyright (c) 2013 Google, Inc | |
4 | * | |
5 | * (C) Copyright 2012 | |
6 | * Marek Vasut <[email protected]> | |
6494d708 SG |
7 | */ |
8 | ||
d3e77361 SG |
9 | #define LOG_CATEGORY LOGC_DM |
10 | ||
6494d708 SG |
11 | #include <common.h> |
12 | #include <errno.h> | |
f7ae49fc | 13 | #include <log.h> |
6494d708 SG |
14 | #include <dm/device.h> |
15 | #include <dm/device-internal.h> | |
fd536d81 | 16 | #include <dm/lists.h> |
6494d708 SG |
17 | #include <dm/platdata.h> |
18 | #include <dm/uclass.h> | |
19 | #include <dm/util.h> | |
6a6d8fbe | 20 | #include <fdtdec.h> |
6494d708 SG |
21 | #include <linux/compiler.h> |
22 | ||
23 | struct driver *lists_driver_lookup_name(const char *name) | |
24 | { | |
25 | struct driver *drv = | |
26 | ll_entry_start(struct driver, driver); | |
27 | const int n_ents = ll_entry_count(struct driver, driver); | |
28 | struct driver *entry; | |
6494d708 | 29 | |
6494d708 | 30 | for (entry = drv; entry != drv + n_ents; entry++) { |
2cede453 | 31 | if (!strcmp(name, entry->name)) |
6494d708 SG |
32 | return entry; |
33 | } | |
34 | ||
35 | /* Not found */ | |
36 | return NULL; | |
37 | } | |
38 | ||
39 | struct uclass_driver *lists_uclass_lookup(enum uclass_id id) | |
40 | { | |
41 | struct uclass_driver *uclass = | |
9c503137 SG |
42 | ll_entry_start(struct uclass_driver, uclass_driver); |
43 | const int n_ents = ll_entry_count(struct uclass_driver, uclass_driver); | |
6494d708 SG |
44 | struct uclass_driver *entry; |
45 | ||
6494d708 SG |
46 | for (entry = uclass; entry != uclass + n_ents; entry++) { |
47 | if (entry->id == id) | |
48 | return entry; | |
49 | } | |
50 | ||
51 | return NULL; | |
52 | } | |
53 | ||
e41651ff | 54 | static int bind_drivers_pass(struct udevice *parent, bool pre_reloc_only) |
6494d708 SG |
55 | { |
56 | struct driver_info *info = | |
57 | ll_entry_start(struct driver_info, driver_info); | |
58 | const int n_ents = ll_entry_count(struct driver_info, driver_info); | |
e41651ff | 59 | bool missing_parent = false; |
6494d708 | 60 | int result = 0; |
a294ead8 SG |
61 | uint idx; |
62 | ||
e41651ff SG |
63 | /* |
64 | * Do one iteration through the driver_info records. For of-platdata, | |
65 | * bind only devices whose parent is already bound. If we find any | |
66 | * device we can't bind, set missing_parent to true, which will cause | |
67 | * this function to be called again. | |
68 | */ | |
a294ead8 | 69 | for (idx = 0; idx < n_ents; idx++) { |
e41651ff | 70 | struct udevice *par = parent; |
a294ead8 SG |
71 | const struct driver_info *entry = info + idx; |
72 | struct driver_rt *drt = gd_dm_driver_rt() + idx; | |
73 | struct udevice *dev; | |
74 | int ret; | |
6494d708 | 75 | |
e41651ff SG |
76 | if (CONFIG_IS_ENABLED(OF_PLATDATA)) { |
77 | int parent_idx = driver_info_parent_id(entry); | |
78 | ||
79 | if (drt->dev) | |
80 | continue; | |
81 | ||
82 | if (CONFIG_IS_ENABLED(OF_PLATDATA_PARENT) && | |
83 | parent_idx != -1) { | |
84 | struct driver_rt *parent_drt; | |
85 | ||
86 | parent_drt = gd_dm_driver_rt() + parent_idx; | |
87 | if (!parent_drt->dev) { | |
88 | missing_parent = true; | |
89 | continue; | |
90 | } | |
91 | ||
92 | par = parent_drt->dev; | |
93 | } | |
94 | } | |
95 | ret = device_bind_by_name(par, pre_reloc_only, entry, &dev); | |
a294ead8 SG |
96 | if (!ret) { |
97 | if (CONFIG_IS_ENABLED(OF_PLATDATA)) | |
98 | drt->dev = dev; | |
99 | } else if (ret != -EPERM) { | |
6494d708 SG |
100 | dm_warn("No match for driver '%s'\n", entry->name); |
101 | if (!result || ret != -ENOENT) | |
102 | result = ret; | |
103 | } | |
104 | } | |
105 | ||
e41651ff SG |
106 | return result ? result : missing_parent ? -EAGAIN : 0; |
107 | } | |
108 | ||
109 | int lists_bind_drivers(struct udevice *parent, bool pre_reloc_only) | |
110 | { | |
111 | int result = 0; | |
112 | int pass; | |
113 | ||
114 | /* | |
115 | * 10 passes is 10 levels deep in the devicetree, which is plenty. If | |
116 | * OF_PLATDATA_PARENT is not enabled, then bind_drivers_pass() will | |
117 | * always succeed on the first pass. | |
118 | */ | |
119 | for (pass = 0; pass < 10; pass++) { | |
120 | int ret; | |
121 | ||
122 | ret = bind_drivers_pass(parent, pre_reloc_only); | |
123 | if (!ret) | |
124 | break; | |
125 | if (ret != -EAGAIN && !result) | |
126 | result = ret; | |
127 | } | |
128 | ||
6494d708 SG |
129 | return result; |
130 | } | |
131 | ||
e33dc221 SG |
132 | int device_bind_driver(struct udevice *parent, const char *drv_name, |
133 | const char *dev_name, struct udevice **devp) | |
5b9000dd | 134 | { |
45a26867 SG |
135 | return device_bind_driver_to_node(parent, drv_name, dev_name, |
136 | ofnode_null(), devp); | |
5b9000dd SG |
137 | } |
138 | ||
139 | int device_bind_driver_to_node(struct udevice *parent, const char *drv_name, | |
45a26867 | 140 | const char *dev_name, ofnode node, |
5b9000dd | 141 | struct udevice **devp) |
e33dc221 SG |
142 | { |
143 | struct driver *drv; | |
144 | int ret; | |
145 | ||
146 | drv = lists_driver_lookup_name(drv_name); | |
147 | if (!drv) { | |
3039811e | 148 | debug("Cannot find driver '%s'\n", drv_name); |
e33dc221 SG |
149 | return -ENOENT; |
150 | } | |
45a26867 SG |
151 | ret = device_bind_with_driver_data(parent, drv, dev_name, 0 /* data */, |
152 | node, devp); | |
e33dc221 | 153 | |
45a26867 | 154 | return ret; |
e33dc221 SG |
155 | } |
156 | ||
29629eb8 | 157 | #if CONFIG_IS_ENABLED(OF_CONTROL) && !CONFIG_IS_ENABLED(OF_PLATDATA) |
6494d708 | 158 | /** |
ce701721 | 159 | * driver_check_compatible() - Check if a driver matches a compatible string |
6494d708 | 160 | * |
2ef249b4 SG |
161 | * @param of_match: List of compatible strings to match |
162 | * @param of_idp: Returns the match that was found | |
ce701721 PB |
163 | * @param compat: The compatible string to search for |
164 | * @return 0 if there is a match, -ENOENT if no match | |
6494d708 | 165 | */ |
ce701721 PB |
166 | static int driver_check_compatible(const struct udevice_id *of_match, |
167 | const struct udevice_id **of_idp, | |
168 | const char *compat) | |
6494d708 | 169 | { |
6494d708 SG |
170 | if (!of_match) |
171 | return -ENOENT; | |
172 | ||
173 | while (of_match->compatible) { | |
ce701721 | 174 | if (!strcmp(of_match->compatible, compat)) { |
2ef249b4 | 175 | *of_idp = of_match; |
6494d708 | 176 | return 0; |
2ef249b4 | 177 | } |
6494d708 SG |
178 | of_match++; |
179 | } | |
180 | ||
181 | return -ENOENT; | |
182 | } | |
183 | ||
8d773c4a BM |
184 | int lists_bind_fdt(struct udevice *parent, ofnode node, struct udevice **devp, |
185 | bool pre_reloc_only) | |
6494d708 SG |
186 | { |
187 | struct driver *driver = ll_entry_start(struct driver, driver); | |
188 | const int n_ents = ll_entry_count(struct driver, driver); | |
2ef249b4 | 189 | const struct udevice_id *id; |
6494d708 | 190 | struct driver *entry; |
54c5d08a | 191 | struct udevice *dev; |
9b0ba067 | 192 | bool found = false; |
ce701721 PB |
193 | const char *name, *compat_list, *compat; |
194 | int compat_length, i; | |
6494d708 | 195 | int result = 0; |
9b0ba067 | 196 | int ret = 0; |
6494d708 | 197 | |
1f359e36 SG |
198 | if (devp) |
199 | *devp = NULL; | |
f5b5719c | 200 | name = ofnode_get_name(node); |
d3e77361 | 201 | log_debug("bind node %s\n", name); |
ce701721 | 202 | |
61e51bab | 203 | compat_list = ofnode_get_property(node, "compatible", &compat_length); |
ce701721 PB |
204 | if (!compat_list) { |
205 | if (compat_length == -FDT_ERR_NOTFOUND) { | |
d3e77361 SG |
206 | log_debug("Device '%s' has no compatible string\n", |
207 | name); | |
ce701721 | 208 | return 0; |
6494d708 SG |
209 | } |
210 | ||
f5b5719c | 211 | dm_warn("Device tree error at node '%s'\n", name); |
ce701721 PB |
212 | return compat_length; |
213 | } | |
214 | ||
215 | /* | |
216 | * Walk through the compatible string list, attempting to match each | |
217 | * compatible string in order such that we match in order of priority | |
218 | * from the first string to the last. | |
219 | */ | |
220 | for (i = 0; i < compat_length; i += strlen(compat) + 1) { | |
221 | compat = compat_list + i; | |
d3e77361 SG |
222 | log_debug(" - attempt to match compatible string '%s'\n", |
223 | compat); | |
ce701721 PB |
224 | |
225 | for (entry = driver; entry != driver + n_ents; entry++) { | |
226 | ret = driver_check_compatible(entry->of_match, &id, | |
227 | compat); | |
228 | if (!ret) | |
229 | break; | |
230 | } | |
231 | if (entry == driver + n_ents) | |
232 | continue; | |
233 | ||
8d773c4a | 234 | if (pre_reloc_only) { |
5c9c9bc9 | 235 | if (!ofnode_pre_reloc(node) && |
7fc0c2b1 SA |
236 | !(entry->flags & DM_FLAG_PRE_RELOC)) { |
237 | log_debug("Skipping device pre-relocation\n"); | |
8d773c4a | 238 | return 0; |
7fc0c2b1 | 239 | } |
8d773c4a BM |
240 | } |
241 | ||
d3e77361 SG |
242 | log_debug(" - found match at '%s': '%s' matches '%s'\n", |
243 | entry->name, entry->of_match->compatible, | |
244 | id->compatible); | |
daac3bfe | 245 | ret = device_bind_with_driver_data(parent, entry, name, |
f5b5719c | 246 | id->data, node, &dev); |
9fdfadf8 | 247 | if (ret == -ENODEV) { |
d3e77361 | 248 | log_debug("Driver '%s' refuses to bind\n", entry->name); |
9fdfadf8 SW |
249 | continue; |
250 | } | |
6494d708 | 251 | if (ret) { |
d062482b SG |
252 | dm_warn("Error binding driver '%s': %d\n", entry->name, |
253 | ret); | |
97e8684c | 254 | return log_msg_ret("bind", ret); |
9b0ba067 SG |
255 | } else { |
256 | found = true; | |
1f359e36 SG |
257 | if (devp) |
258 | *devp = dev; | |
6494d708 | 259 | } |
9b0ba067 SG |
260 | break; |
261 | } | |
262 | ||
ce701721 | 263 | if (!found && !result && ret != -ENODEV) |
d3e77361 | 264 | log_debug("No match for node '%s'\n", name); |
6494d708 SG |
265 | |
266 | return result; | |
267 | } | |
268 | #endif |