]>
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 | ||
cb44201c | 11 | #include <debug_uart.h> |
6494d708 | 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 | ||
cb44201c SG |
54 | /** |
55 | * bind_drivers_pass() - Perform a pass of driver binding | |
56 | * | |
57 | * Work through the driver_info records binding a driver for each one. If the | |
58 | * binding fails, continue binding others, but return the error. | |
59 | * | |
60 | * For OF_PLATDATA we must bind parent devices before their children. So only | |
61 | * children of bound parents are bound on each call to this function. When a | |
62 | * child is left unbound, -EAGAIN is returned, indicating that this function | |
63 | * should be called again | |
64 | * | |
65 | * @parent: Parent device to use when binding each child device | |
66 | * Return: 0 if OK, -EAGAIN if unbound children exist, -ENOENT if there is no | |
67 | * driver for one of the devices, other -ve on other error | |
68 | */ | |
e41651ff | 69 | static int bind_drivers_pass(struct udevice *parent, bool pre_reloc_only) |
6494d708 SG |
70 | { |
71 | struct driver_info *info = | |
72 | ll_entry_start(struct driver_info, driver_info); | |
73 | const int n_ents = ll_entry_count(struct driver_info, driver_info); | |
e41651ff | 74 | bool missing_parent = false; |
6494d708 | 75 | int result = 0; |
fe67ba74 | 76 | int idx; |
a294ead8 | 77 | |
e41651ff SG |
78 | /* |
79 | * Do one iteration through the driver_info records. For of-platdata, | |
80 | * bind only devices whose parent is already bound. If we find any | |
81 | * device we can't bind, set missing_parent to true, which will cause | |
82 | * this function to be called again. | |
83 | */ | |
a294ead8 | 84 | for (idx = 0; idx < n_ents; idx++) { |
e41651ff | 85 | struct udevice *par = parent; |
a294ead8 SG |
86 | const struct driver_info *entry = info + idx; |
87 | struct driver_rt *drt = gd_dm_driver_rt() + idx; | |
88 | struct udevice *dev; | |
89 | int ret; | |
6494d708 | 90 | |
e41651ff SG |
91 | if (CONFIG_IS_ENABLED(OF_PLATDATA)) { |
92 | int parent_idx = driver_info_parent_id(entry); | |
93 | ||
94 | if (drt->dev) | |
95 | continue; | |
96 | ||
97 | if (CONFIG_IS_ENABLED(OF_PLATDATA_PARENT) && | |
98 | parent_idx != -1) { | |
99 | struct driver_rt *parent_drt; | |
100 | ||
101 | parent_drt = gd_dm_driver_rt() + parent_idx; | |
102 | if (!parent_drt->dev) { | |
103 | missing_parent = true; | |
104 | continue; | |
105 | } | |
106 | ||
107 | par = parent_drt->dev; | |
108 | } | |
109 | } | |
110 | ret = device_bind_by_name(par, pre_reloc_only, entry, &dev); | |
a294ead8 SG |
111 | if (!ret) { |
112 | if (CONFIG_IS_ENABLED(OF_PLATDATA)) | |
113 | drt->dev = dev; | |
114 | } else if (ret != -EPERM) { | |
6494d708 SG |
115 | dm_warn("No match for driver '%s'\n", entry->name); |
116 | if (!result || ret != -ENOENT) | |
117 | result = ret; | |
118 | } | |
119 | } | |
120 | ||
e41651ff SG |
121 | return result ? result : missing_parent ? -EAGAIN : 0; |
122 | } | |
123 | ||
124 | int lists_bind_drivers(struct udevice *parent, bool pre_reloc_only) | |
125 | { | |
126 | int result = 0; | |
127 | int pass; | |
128 | ||
129 | /* | |
130 | * 10 passes is 10 levels deep in the devicetree, which is plenty. If | |
131 | * OF_PLATDATA_PARENT is not enabled, then bind_drivers_pass() will | |
132 | * always succeed on the first pass. | |
133 | */ | |
134 | for (pass = 0; pass < 10; pass++) { | |
135 | int ret; | |
136 | ||
137 | ret = bind_drivers_pass(parent, pre_reloc_only); | |
dec64d55 | 138 | if (!result || result == -EAGAIN) |
e41651ff | 139 | result = ret; |
dec64d55 PB |
140 | if (ret != -EAGAIN) |
141 | break; | |
e41651ff SG |
142 | } |
143 | ||
6494d708 SG |
144 | return result; |
145 | } | |
146 | ||
e33dc221 SG |
147 | int device_bind_driver(struct udevice *parent, const char *drv_name, |
148 | const char *dev_name, struct udevice **devp) | |
5b9000dd | 149 | { |
45a26867 SG |
150 | return device_bind_driver_to_node(parent, drv_name, dev_name, |
151 | ofnode_null(), devp); | |
5b9000dd SG |
152 | } |
153 | ||
154 | int device_bind_driver_to_node(struct udevice *parent, const char *drv_name, | |
45a26867 | 155 | const char *dev_name, ofnode node, |
5b9000dd | 156 | struct udevice **devp) |
e33dc221 SG |
157 | { |
158 | struct driver *drv; | |
159 | int ret; | |
160 | ||
161 | drv = lists_driver_lookup_name(drv_name); | |
162 | if (!drv) { | |
6afdb158 | 163 | dm_warn("Cannot find driver '%s'\n", drv_name); |
e33dc221 SG |
164 | return -ENOENT; |
165 | } | |
45a26867 SG |
166 | ret = device_bind_with_driver_data(parent, drv, dev_name, 0 /* data */, |
167 | node, devp); | |
e33dc221 | 168 | |
45a26867 | 169 | return ret; |
e33dc221 SG |
170 | } |
171 | ||
414cc151 | 172 | #if CONFIG_IS_ENABLED(OF_REAL) |
6494d708 | 173 | /** |
ce701721 | 174 | * driver_check_compatible() - Check if a driver matches a compatible string |
6494d708 | 175 | * |
2ef249b4 SG |
176 | * @param of_match: List of compatible strings to match |
177 | * @param of_idp: Returns the match that was found | |
ce701721 | 178 | * @param compat: The compatible string to search for |
185f812c | 179 | * Return: 0 if there is a match, -ENOENT if no match |
6494d708 | 180 | */ |
ce701721 PB |
181 | static int driver_check_compatible(const struct udevice_id *of_match, |
182 | const struct udevice_id **of_idp, | |
183 | const char *compat) | |
6494d708 | 184 | { |
6494d708 SG |
185 | if (!of_match) |
186 | return -ENOENT; | |
187 | ||
188 | while (of_match->compatible) { | |
ce701721 | 189 | if (!strcmp(of_match->compatible, compat)) { |
2ef249b4 | 190 | *of_idp = of_match; |
6494d708 | 191 | return 0; |
2ef249b4 | 192 | } |
6494d708 SG |
193 | of_match++; |
194 | } | |
195 | ||
196 | return -ENOENT; | |
197 | } | |
198 | ||
8d773c4a | 199 | int lists_bind_fdt(struct udevice *parent, ofnode node, struct udevice **devp, |
38f7d3b6 | 200 | struct driver *drv, bool pre_reloc_only) |
6494d708 SG |
201 | { |
202 | struct driver *driver = ll_entry_start(struct driver, driver); | |
203 | const int n_ents = ll_entry_count(struct driver, driver); | |
2ef249b4 | 204 | const struct udevice_id *id; |
6494d708 | 205 | struct driver *entry; |
54c5d08a | 206 | struct udevice *dev; |
9b0ba067 | 207 | bool found = false; |
ce701721 PB |
208 | const char *name, *compat_list, *compat; |
209 | int compat_length, i; | |
6494d708 | 210 | int result = 0; |
9b0ba067 | 211 | int ret = 0; |
6494d708 | 212 | |
1f359e36 SG |
213 | if (devp) |
214 | *devp = NULL; | |
f5b5719c | 215 | name = ofnode_get_name(node); |
d3e77361 | 216 | log_debug("bind node %s\n", name); |
ce701721 | 217 | |
61e51bab | 218 | compat_list = ofnode_get_property(node, "compatible", &compat_length); |
ce701721 PB |
219 | if (!compat_list) { |
220 | if (compat_length == -FDT_ERR_NOTFOUND) { | |
d3e77361 SG |
221 | log_debug("Device '%s' has no compatible string\n", |
222 | name); | |
ce701721 | 223 | return 0; |
6494d708 SG |
224 | } |
225 | ||
f5b5719c | 226 | dm_warn("Device tree error at node '%s'\n", name); |
ce701721 PB |
227 | return compat_length; |
228 | } | |
229 | ||
230 | /* | |
231 | * Walk through the compatible string list, attempting to match each | |
232 | * compatible string in order such that we match in order of priority | |
233 | * from the first string to the last. | |
234 | */ | |
235 | for (i = 0; i < compat_length; i += strlen(compat) + 1) { | |
236 | compat = compat_list + i; | |
d3e77361 SG |
237 | log_debug(" - attempt to match compatible string '%s'\n", |
238 | compat); | |
ce701721 | 239 | |
d1387921 | 240 | id = NULL; |
ce701721 | 241 | for (entry = driver; entry != driver + n_ents; entry++) { |
de84a4f0 HS |
242 | if (drv) { |
243 | if (drv != entry) | |
244 | continue; | |
245 | if (!entry->of_match) | |
246 | break; | |
247 | } | |
ce701721 PB |
248 | ret = driver_check_compatible(entry->of_match, &id, |
249 | compat); | |
250 | if (!ret) | |
251 | break; | |
252 | } | |
253 | if (entry == driver + n_ents) | |
254 | continue; | |
255 | ||
8d773c4a | 256 | if (pre_reloc_only) { |
5c9c9bc9 | 257 | if (!ofnode_pre_reloc(node) && |
7fc0c2b1 SA |
258 | !(entry->flags & DM_FLAG_PRE_RELOC)) { |
259 | log_debug("Skipping device pre-relocation\n"); | |
8d773c4a | 260 | return 0; |
7fc0c2b1 | 261 | } |
8d773c4a BM |
262 | } |
263 | ||
7a6f5a4e | 264 | if (entry->of_match) |
cc560eac QS |
265 | log_debug(" - found match at driver '%s' for '%s'\n", |
266 | entry->name, id->compatible); | |
daac3bfe | 267 | ret = device_bind_with_driver_data(parent, entry, name, |
d1387921 SG |
268 | id ? id->data : 0, node, |
269 | &dev); | |
9fdfadf8 | 270 | if (ret == -ENODEV) { |
d3e77361 | 271 | log_debug("Driver '%s' refuses to bind\n", entry->name); |
9fdfadf8 SW |
272 | continue; |
273 | } | |
6494d708 | 274 | if (ret) { |
d062482b SG |
275 | dm_warn("Error binding driver '%s': %d\n", entry->name, |
276 | ret); | |
97e8684c | 277 | return log_msg_ret("bind", ret); |
9b0ba067 SG |
278 | } else { |
279 | found = true; | |
1f359e36 SG |
280 | if (devp) |
281 | *devp = dev; | |
6494d708 | 282 | } |
9b0ba067 SG |
283 | break; |
284 | } | |
285 | ||
ce701721 | 286 | if (!found && !result && ret != -ENODEV) |
d3e77361 | 287 | log_debug("No match for node '%s'\n", name); |
6494d708 SG |
288 | |
289 | return result; | |
290 | } | |
291 | #endif |