]>
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 | 76 | { |
45a26867 SG |
77 | return device_bind_driver_to_node(parent, drv_name, dev_name, |
78 | ofnode_null(), devp); | |
5b9000dd SG |
79 | } |
80 | ||
81 | int device_bind_driver_to_node(struct udevice *parent, const char *drv_name, | |
45a26867 | 82 | const char *dev_name, ofnode node, |
5b9000dd | 83 | struct udevice **devp) |
e33dc221 SG |
84 | { |
85 | struct driver *drv; | |
86 | int ret; | |
87 | ||
88 | drv = lists_driver_lookup_name(drv_name); | |
89 | if (!drv) { | |
3039811e | 90 | debug("Cannot find driver '%s'\n", drv_name); |
e33dc221 SG |
91 | return -ENOENT; |
92 | } | |
45a26867 SG |
93 | ret = device_bind_with_driver_data(parent, drv, dev_name, 0 /* data */, |
94 | node, devp); | |
e33dc221 | 95 | |
45a26867 | 96 | return ret; |
e33dc221 SG |
97 | } |
98 | ||
29629eb8 | 99 | #if CONFIG_IS_ENABLED(OF_CONTROL) && !CONFIG_IS_ENABLED(OF_PLATDATA) |
6494d708 | 100 | /** |
ce701721 | 101 | * driver_check_compatible() - Check if a driver matches a compatible string |
6494d708 | 102 | * |
2ef249b4 SG |
103 | * @param of_match: List of compatible strings to match |
104 | * @param of_idp: Returns the match that was found | |
ce701721 PB |
105 | * @param compat: The compatible string to search for |
106 | * @return 0 if there is a match, -ENOENT if no match | |
6494d708 | 107 | */ |
ce701721 PB |
108 | static int driver_check_compatible(const struct udevice_id *of_match, |
109 | const struct udevice_id **of_idp, | |
110 | const char *compat) | |
6494d708 | 111 | { |
6494d708 SG |
112 | if (!of_match) |
113 | return -ENOENT; | |
114 | ||
115 | while (of_match->compatible) { | |
ce701721 | 116 | if (!strcmp(of_match->compatible, compat)) { |
2ef249b4 | 117 | *of_idp = of_match; |
6494d708 | 118 | return 0; |
2ef249b4 | 119 | } |
6494d708 SG |
120 | of_match++; |
121 | } | |
122 | ||
123 | return -ENOENT; | |
124 | } | |
125 | ||
f5b5719c | 126 | int lists_bind_fdt(struct udevice *parent, ofnode node, struct udevice **devp) |
6494d708 SG |
127 | { |
128 | struct driver *driver = ll_entry_start(struct driver, driver); | |
129 | const int n_ents = ll_entry_count(struct driver, driver); | |
2ef249b4 | 130 | const struct udevice_id *id; |
6494d708 | 131 | struct driver *entry; |
54c5d08a | 132 | struct udevice *dev; |
9b0ba067 | 133 | bool found = false; |
ce701721 PB |
134 | const char *name, *compat_list, *compat; |
135 | int compat_length, i; | |
6494d708 | 136 | int result = 0; |
9b0ba067 | 137 | int ret = 0; |
6494d708 | 138 | |
1f359e36 SG |
139 | if (devp) |
140 | *devp = NULL; | |
f5b5719c | 141 | name = ofnode_get_name(node); |
ceb91909 | 142 | pr_debug("bind node %s\n", name); |
ce701721 | 143 | |
61e51bab | 144 | compat_list = ofnode_get_property(node, "compatible", &compat_length); |
ce701721 PB |
145 | if (!compat_list) { |
146 | if (compat_length == -FDT_ERR_NOTFOUND) { | |
ceb91909 MY |
147 | pr_debug("Device '%s' has no compatible string\n", |
148 | name); | |
ce701721 | 149 | return 0; |
6494d708 SG |
150 | } |
151 | ||
f5b5719c | 152 | dm_warn("Device tree error at node '%s'\n", name); |
ce701721 PB |
153 | return compat_length; |
154 | } | |
155 | ||
156 | /* | |
157 | * Walk through the compatible string list, attempting to match each | |
158 | * compatible string in order such that we match in order of priority | |
159 | * from the first string to the last. | |
160 | */ | |
161 | for (i = 0; i < compat_length; i += strlen(compat) + 1) { | |
162 | compat = compat_list + i; | |
ceb91909 MY |
163 | pr_debug(" - attempt to match compatible string '%s'\n", |
164 | compat); | |
ce701721 PB |
165 | |
166 | for (entry = driver; entry != driver + n_ents; entry++) { | |
167 | ret = driver_check_compatible(entry->of_match, &id, | |
168 | compat); | |
169 | if (!ret) | |
170 | break; | |
171 | } | |
172 | if (entry == driver + n_ents) | |
173 | continue; | |
174 | ||
ceb91909 | 175 | pr_debug(" - found match at '%s'\n", entry->name); |
daac3bfe | 176 | ret = device_bind_with_driver_data(parent, entry, name, |
f5b5719c | 177 | id->data, node, &dev); |
9fdfadf8 | 178 | if (ret == -ENODEV) { |
ceb91909 | 179 | pr_debug("Driver '%s' refuses to bind\n", entry->name); |
9fdfadf8 SW |
180 | continue; |
181 | } | |
6494d708 | 182 | if (ret) { |
d062482b SG |
183 | dm_warn("Error binding driver '%s': %d\n", entry->name, |
184 | ret); | |
1f359e36 | 185 | return ret; |
9b0ba067 SG |
186 | } else { |
187 | found = true; | |
1f359e36 SG |
188 | if (devp) |
189 | *devp = dev; | |
6494d708 | 190 | } |
9b0ba067 SG |
191 | break; |
192 | } | |
193 | ||
ce701721 | 194 | if (!found && !result && ret != -ENODEV) |
ceb91909 | 195 | pr_debug("No match for node '%s'\n", name); |
6494d708 SG |
196 | |
197 | return result; | |
198 | } | |
199 | #endif |