]>
Commit | Line | Data |
---|---|---|
6494d708 SG |
1 | /* |
2 | * Copyright (c) 2013 Google, Inc | |
3 | * | |
4 | * (C) Copyright 2012 | |
5 | * Marek Vasut <marex@denx.de> | |
6 | * | |
7 | * SPDX-License-Identifier: GPL-2.0+ | |
8 | */ | |
9 | ||
10 | #include <common.h> | |
11 | #include <errno.h> | |
12 | #include <dm/device.h> | |
13 | #include <dm/device-internal.h> | |
fd536d81 | 14 | #include <dm/lists.h> |
6494d708 SG |
15 | #include <dm/platdata.h> |
16 | #include <dm/uclass.h> | |
17 | #include <dm/util.h> | |
6a6d8fbe | 18 | #include <fdtdec.h> |
6494d708 SG |
19 | #include <linux/compiler.h> |
20 | ||
21 | struct driver *lists_driver_lookup_name(const char *name) | |
22 | { | |
23 | struct driver *drv = | |
24 | ll_entry_start(struct driver, driver); | |
25 | const int n_ents = ll_entry_count(struct driver, driver); | |
26 | struct driver *entry; | |
6494d708 | 27 | |
6494d708 | 28 | for (entry = drv; entry != drv + n_ents; entry++) { |
2cede453 | 29 | if (!strcmp(name, entry->name)) |
6494d708 SG |
30 | return entry; |
31 | } | |
32 | ||
33 | /* Not found */ | |
34 | return NULL; | |
35 | } | |
36 | ||
37 | struct uclass_driver *lists_uclass_lookup(enum uclass_id id) | |
38 | { | |
39 | struct uclass_driver *uclass = | |
40 | ll_entry_start(struct uclass_driver, uclass); | |
41 | const int n_ents = ll_entry_count(struct uclass_driver, uclass); | |
42 | struct uclass_driver *entry; | |
43 | ||
6494d708 SG |
44 | for (entry = uclass; entry != uclass + n_ents; entry++) { |
45 | if (entry->id == id) | |
46 | return entry; | |
47 | } | |
48 | ||
49 | return NULL; | |
50 | } | |
51 | ||
00606d7e | 52 | int lists_bind_drivers(struct udevice *parent, bool pre_reloc_only) |
6494d708 SG |
53 | { |
54 | struct driver_info *info = | |
55 | ll_entry_start(struct driver_info, driver_info); | |
56 | const int n_ents = ll_entry_count(struct driver_info, driver_info); | |
57 | struct driver_info *entry; | |
54c5d08a | 58 | struct udevice *dev; |
6494d708 SG |
59 | int result = 0; |
60 | int ret; | |
61 | ||
62 | for (entry = info; entry != info + n_ents; entry++) { | |
00606d7e SG |
63 | ret = device_bind_by_name(parent, pre_reloc_only, entry, &dev); |
64 | if (ret && ret != -EPERM) { | |
6494d708 SG |
65 | dm_warn("No match for driver '%s'\n", entry->name); |
66 | if (!result || ret != -ENOENT) | |
67 | result = ret; | |
68 | } | |
69 | } | |
70 | ||
71 | return result; | |
72 | } | |
73 | ||
e33dc221 SG |
74 | int device_bind_driver(struct udevice *parent, const char *drv_name, |
75 | const char *dev_name, struct udevice **devp) | |
5b9000dd SG |
76 | { |
77 | return device_bind_driver_to_node(parent, drv_name, dev_name, -1, devp); | |
78 | } | |
79 | ||
80 | int device_bind_driver_to_node(struct udevice *parent, const char *drv_name, | |
81 | const char *dev_name, int node, | |
82 | struct udevice **devp) | |
e33dc221 SG |
83 | { |
84 | struct driver *drv; | |
85 | int ret; | |
86 | ||
87 | drv = lists_driver_lookup_name(drv_name); | |
88 | if (!drv) { | |
3039811e | 89 | debug("Cannot find driver '%s'\n", drv_name); |
e33dc221 SG |
90 | return -ENOENT; |
91 | } | |
5b9000dd | 92 | ret = device_bind(parent, drv, dev_name, NULL, node, devp); |
e33dc221 | 93 | if (ret) { |
3039811e SG |
94 | debug("Cannot create device named '%s' (err=%d)\n", |
95 | dev_name, ret); | |
e33dc221 SG |
96 | return ret; |
97 | } | |
98 | ||
99 | return 0; | |
100 | } | |
101 | ||
29629eb8 | 102 | #if CONFIG_IS_ENABLED(OF_CONTROL) && !CONFIG_IS_ENABLED(OF_PLATDATA) |
6494d708 | 103 | /** |
ce701721 | 104 | * driver_check_compatible() - Check if a driver matches a compatible string |
6494d708 | 105 | * |
2ef249b4 SG |
106 | * @param of_match: List of compatible strings to match |
107 | * @param of_idp: Returns the match that was found | |
ce701721 PB |
108 | * @param compat: The compatible string to search for |
109 | * @return 0 if there is a match, -ENOENT if no match | |
6494d708 | 110 | */ |
ce701721 PB |
111 | static int driver_check_compatible(const struct udevice_id *of_match, |
112 | const struct udevice_id **of_idp, | |
113 | const char *compat) | |
6494d708 | 114 | { |
6494d708 SG |
115 | if (!of_match) |
116 | return -ENOENT; | |
117 | ||
118 | while (of_match->compatible) { | |
ce701721 | 119 | if (!strcmp(of_match->compatible, compat)) { |
2ef249b4 | 120 | *of_idp = of_match; |
6494d708 | 121 | return 0; |
2ef249b4 | 122 | } |
6494d708 SG |
123 | of_match++; |
124 | } | |
125 | ||
126 | return -ENOENT; | |
127 | } | |
128 | ||
1f359e36 SG |
129 | int lists_bind_fdt(struct udevice *parent, const void *blob, int offset, |
130 | struct udevice **devp) | |
6494d708 SG |
131 | { |
132 | struct driver *driver = ll_entry_start(struct driver, driver); | |
133 | const int n_ents = ll_entry_count(struct driver, driver); | |
2ef249b4 | 134 | const struct udevice_id *id; |
6494d708 | 135 | struct driver *entry; |
54c5d08a | 136 | struct udevice *dev; |
9b0ba067 | 137 | bool found = false; |
ce701721 PB |
138 | const char *name, *compat_list, *compat; |
139 | int compat_length, i; | |
6494d708 | 140 | int result = 0; |
9b0ba067 | 141 | int ret = 0; |
6494d708 | 142 | |
ce701721 PB |
143 | name = fdt_get_name(blob, offset, NULL); |
144 | dm_dbg("bind node %s\n", name); | |
1f359e36 SG |
145 | if (devp) |
146 | *devp = NULL; | |
ce701721 PB |
147 | |
148 | compat_list = fdt_getprop(blob, offset, "compatible", &compat_length); | |
149 | if (!compat_list) { | |
150 | if (compat_length == -FDT_ERR_NOTFOUND) { | |
9b0ba067 | 151 | dm_dbg("Device '%s' has no compatible string\n", name); |
ce701721 | 152 | return 0; |
6494d708 SG |
153 | } |
154 | ||
ce701721 PB |
155 | dm_warn("Device tree error at offset %d\n", offset); |
156 | return compat_length; | |
157 | } | |
158 | ||
159 | /* | |
160 | * Walk through the compatible string list, attempting to match each | |
161 | * compatible string in order such that we match in order of priority | |
162 | * from the first string to the last. | |
163 | */ | |
164 | for (i = 0; i < compat_length; i += strlen(compat) + 1) { | |
165 | compat = compat_list + i; | |
166 | dm_dbg(" - attempt to match compatible string '%s'\n", | |
167 | compat); | |
168 | ||
169 | for (entry = driver; entry != driver + n_ents; entry++) { | |
170 | ret = driver_check_compatible(entry->of_match, &id, | |
171 | compat); | |
172 | if (!ret) | |
173 | break; | |
174 | } | |
175 | if (entry == driver + n_ents) | |
176 | continue; | |
177 | ||
6494d708 | 178 | dm_dbg(" - found match at '%s'\n", entry->name); |
daac3bfe SW |
179 | ret = device_bind_with_driver_data(parent, entry, name, |
180 | id->data, offset, &dev); | |
9fdfadf8 SW |
181 | if (ret == -ENODEV) { |
182 | dm_dbg("Driver '%s' refuses to bind\n", entry->name); | |
183 | continue; | |
184 | } | |
6494d708 | 185 | if (ret) { |
d062482b SG |
186 | dm_warn("Error binding driver '%s': %d\n", entry->name, |
187 | ret); | |
1f359e36 | 188 | return ret; |
9b0ba067 SG |
189 | } else { |
190 | found = true; | |
1f359e36 SG |
191 | if (devp) |
192 | *devp = dev; | |
6494d708 | 193 | } |
9b0ba067 SG |
194 | break; |
195 | } | |
196 | ||
ce701721 PB |
197 | if (!found && !result && ret != -ENODEV) |
198 | dm_dbg("No match for node '%s'\n", name); | |
6494d708 SG |
199 | |
200 | return result; | |
201 | } | |
202 | #endif |