diff options
Diffstat (limited to 'drivers/base/component.c')
-rw-r--r-- | drivers/base/component.c | 192 |
1 files changed, 157 insertions, 35 deletions
diff --git a/drivers/base/component.c b/drivers/base/component.c index c4778995cd72..f748430bb654 100644 --- a/drivers/base/component.c +++ b/drivers/base/component.c | |||
@@ -18,6 +18,15 @@ | |||
18 | #include <linux/mutex.h> | 18 | #include <linux/mutex.h> |
19 | #include <linux/slab.h> | 19 | #include <linux/slab.h> |
20 | 20 | ||
21 | struct component_match { | ||
22 | size_t alloc; | ||
23 | size_t num; | ||
24 | struct { | ||
25 | void *data; | ||
26 | int (*fn)(struct device *, void *); | ||
27 | } compare[0]; | ||
28 | }; | ||
29 | |||
21 | struct master { | 30 | struct master { |
22 | struct list_head node; | 31 | struct list_head node; |
23 | struct list_head components; | 32 | struct list_head components; |
@@ -25,6 +34,7 @@ struct master { | |||
25 | 34 | ||
26 | const struct component_master_ops *ops; | 35 | const struct component_master_ops *ops; |
27 | struct device *dev; | 36 | struct device *dev; |
37 | struct component_match *match; | ||
28 | }; | 38 | }; |
29 | 39 | ||
30 | struct component { | 40 | struct component { |
@@ -69,6 +79,11 @@ static void component_detach_master(struct master *master, struct component *c) | |||
69 | c->master = NULL; | 79 | c->master = NULL; |
70 | } | 80 | } |
71 | 81 | ||
82 | /* | ||
83 | * Add a component to a master, finding the component via the compare | ||
84 | * function and compare data. This is safe to call for duplicate matches | ||
85 | * and will not result in the same component being added multiple times. | ||
86 | */ | ||
72 | int component_master_add_child(struct master *master, | 87 | int component_master_add_child(struct master *master, |
73 | int (*compare)(struct device *, void *), void *compare_data) | 88 | int (*compare)(struct device *, void *), void *compare_data) |
74 | { | 89 | { |
@@ -76,11 +91,12 @@ int component_master_add_child(struct master *master, | |||
76 | int ret = -ENXIO; | 91 | int ret = -ENXIO; |
77 | 92 | ||
78 | list_for_each_entry(c, &component_list, node) { | 93 | list_for_each_entry(c, &component_list, node) { |
79 | if (c->master) | 94 | if (c->master && c->master != master) |
80 | continue; | 95 | continue; |
81 | 96 | ||
82 | if (compare(c->dev, compare_data)) { | 97 | if (compare(c->dev, compare_data)) { |
83 | component_attach_master(master, c); | 98 | if (!c->master) |
99 | component_attach_master(master, c); | ||
84 | ret = 0; | 100 | ret = 0; |
85 | break; | 101 | break; |
86 | } | 102 | } |
@@ -90,6 +106,34 @@ int component_master_add_child(struct master *master, | |||
90 | } | 106 | } |
91 | EXPORT_SYMBOL_GPL(component_master_add_child); | 107 | EXPORT_SYMBOL_GPL(component_master_add_child); |
92 | 108 | ||
109 | static int find_components(struct master *master) | ||
110 | { | ||
111 | struct component_match *match = master->match; | ||
112 | size_t i; | ||
113 | int ret = 0; | ||
114 | |||
115 | if (!match) { | ||
116 | /* | ||
117 | * Search the list of components, looking for components that | ||
118 | * belong to this master, and attach them to the master. | ||
119 | */ | ||
120 | return master->ops->add_components(master->dev, master); | ||
121 | } | ||
122 | |||
123 | /* | ||
124 | * Scan the array of match functions and attach | ||
125 | * any components which are found to this master. | ||
126 | */ | ||
127 | for (i = 0; i < match->num; i++) { | ||
128 | ret = component_master_add_child(master, | ||
129 | match->compare[i].fn, | ||
130 | match->compare[i].data); | ||
131 | if (ret) | ||
132 | break; | ||
133 | } | ||
134 | return ret; | ||
135 | } | ||
136 | |||
93 | /* Detach all attached components from this master */ | 137 | /* Detach all attached components from this master */ |
94 | static void master_remove_components(struct master *master) | 138 | static void master_remove_components(struct master *master) |
95 | { | 139 | { |
@@ -113,44 +157,44 @@ static void master_remove_components(struct master *master) | |||
113 | static int try_to_bring_up_master(struct master *master, | 157 | static int try_to_bring_up_master(struct master *master, |
114 | struct component *component) | 158 | struct component *component) |
115 | { | 159 | { |
116 | int ret = 0; | 160 | int ret; |
117 | 161 | ||
118 | if (!master->bound) { | 162 | if (master->bound) |
119 | /* | 163 | return 0; |
120 | * Search the list of components, looking for components that | ||
121 | * belong to this master, and attach them to the master. | ||
122 | */ | ||
123 | if (master->ops->add_components(master->dev, master)) { | ||
124 | /* Failed to find all components */ | ||
125 | master_remove_components(master); | ||
126 | ret = 0; | ||
127 | goto out; | ||
128 | } | ||
129 | 164 | ||
130 | if (component && component->master != master) { | 165 | /* |
131 | master_remove_components(master); | 166 | * Search the list of components, looking for components that |
132 | ret = 0; | 167 | * belong to this master, and attach them to the master. |
133 | goto out; | 168 | */ |
134 | } | 169 | if (find_components(master)) { |
170 | /* Failed to find all components */ | ||
171 | ret = 0; | ||
172 | goto out; | ||
173 | } | ||
135 | 174 | ||
136 | if (!devres_open_group(master->dev, NULL, GFP_KERNEL)) { | 175 | if (component && component->master != master) { |
137 | ret = -ENOMEM; | 176 | ret = 0; |
138 | goto out; | 177 | goto out; |
139 | } | 178 | } |
140 | 179 | ||
141 | /* Found all components */ | 180 | if (!devres_open_group(master->dev, NULL, GFP_KERNEL)) { |
142 | ret = master->ops->bind(master->dev); | 181 | ret = -ENOMEM; |
143 | if (ret < 0) { | 182 | goto out; |
144 | devres_release_group(master->dev, NULL); | 183 | } |
145 | dev_info(master->dev, "master bind failed: %d\n", ret); | ||
146 | master_remove_components(master); | ||
147 | goto out; | ||
148 | } | ||
149 | 184 | ||
150 | master->bound = true; | 185 | /* Found all components */ |
151 | ret = 1; | 186 | ret = master->ops->bind(master->dev); |
187 | if (ret < 0) { | ||
188 | devres_release_group(master->dev, NULL); | ||
189 | dev_info(master->dev, "master bind failed: %d\n", ret); | ||
190 | goto out; | ||
152 | } | 191 | } |
192 | |||
193 | master->bound = true; | ||
194 | return 1; | ||
195 | |||
153 | out: | 196 | out: |
197 | master_remove_components(master); | ||
154 | 198 | ||
155 | return ret; | 199 | return ret; |
156 | } | 200 | } |
@@ -180,18 +224,89 @@ static void take_down_master(struct master *master) | |||
180 | master_remove_components(master); | 224 | master_remove_components(master); |
181 | } | 225 | } |
182 | 226 | ||
183 | int component_master_add(struct device *dev, | 227 | static size_t component_match_size(size_t num) |
184 | const struct component_master_ops *ops) | 228 | { |
229 | return offsetof(struct component_match, compare[num]); | ||
230 | } | ||
231 | |||
232 | static struct component_match *component_match_realloc(struct device *dev, | ||
233 | struct component_match *match, size_t num) | ||
234 | { | ||
235 | struct component_match *new; | ||
236 | |||
237 | if (match && match->alloc == num) | ||
238 | return match; | ||
239 | |||
240 | new = devm_kmalloc(dev, component_match_size(num), GFP_KERNEL); | ||
241 | if (!new) | ||
242 | return ERR_PTR(-ENOMEM); | ||
243 | |||
244 | if (match) { | ||
245 | memcpy(new, match, component_match_size(min(match->num, num))); | ||
246 | devm_kfree(dev, match); | ||
247 | } else { | ||
248 | new->num = 0; | ||
249 | } | ||
250 | |||
251 | new->alloc = num; | ||
252 | |||
253 | return new; | ||
254 | } | ||
255 | |||
256 | /* | ||
257 | * Add a component to be matched. | ||
258 | * | ||
259 | * The match array is first created or extended if necessary. | ||
260 | */ | ||
261 | void component_match_add(struct device *dev, struct component_match **matchptr, | ||
262 | int (*compare)(struct device *, void *), void *compare_data) | ||
263 | { | ||
264 | struct component_match *match = *matchptr; | ||
265 | |||
266 | if (IS_ERR(match)) | ||
267 | return; | ||
268 | |||
269 | if (!match || match->num == match->alloc) { | ||
270 | size_t new_size = match ? match->alloc + 16 : 15; | ||
271 | |||
272 | match = component_match_realloc(dev, match, new_size); | ||
273 | |||
274 | *matchptr = match; | ||
275 | |||
276 | if (IS_ERR(match)) | ||
277 | return; | ||
278 | } | ||
279 | |||
280 | match->compare[match->num].fn = compare; | ||
281 | match->compare[match->num].data = compare_data; | ||
282 | match->num++; | ||
283 | } | ||
284 | EXPORT_SYMBOL(component_match_add); | ||
285 | |||
286 | int component_master_add_with_match(struct device *dev, | ||
287 | const struct component_master_ops *ops, | ||
288 | struct component_match *match) | ||
185 | { | 289 | { |
186 | struct master *master; | 290 | struct master *master; |
187 | int ret; | 291 | int ret; |
188 | 292 | ||
293 | if (ops->add_components && match) | ||
294 | return -EINVAL; | ||
295 | |||
296 | if (match) { | ||
297 | /* Reallocate the match array for its true size */ | ||
298 | match = component_match_realloc(dev, match, match->num); | ||
299 | if (IS_ERR(match)) | ||
300 | return PTR_ERR(match); | ||
301 | } | ||
302 | |||
189 | master = kzalloc(sizeof(*master), GFP_KERNEL); | 303 | master = kzalloc(sizeof(*master), GFP_KERNEL); |
190 | if (!master) | 304 | if (!master) |
191 | return -ENOMEM; | 305 | return -ENOMEM; |
192 | 306 | ||
193 | master->dev = dev; | 307 | master->dev = dev; |
194 | master->ops = ops; | 308 | master->ops = ops; |
309 | master->match = match; | ||
195 | INIT_LIST_HEAD(&master->components); | 310 | INIT_LIST_HEAD(&master->components); |
196 | 311 | ||
197 | /* Add to the list of available masters. */ | 312 | /* Add to the list of available masters. */ |
@@ -209,6 +324,13 @@ int component_master_add(struct device *dev, | |||
209 | 324 | ||
210 | return ret < 0 ? ret : 0; | 325 | return ret < 0 ? ret : 0; |
211 | } | 326 | } |
327 | EXPORT_SYMBOL_GPL(component_master_add_with_match); | ||
328 | |||
329 | int component_master_add(struct device *dev, | ||
330 | const struct component_master_ops *ops) | ||
331 | { | ||
332 | return component_master_add_with_match(dev, ops, NULL); | ||
333 | } | ||
212 | EXPORT_SYMBOL_GPL(component_master_add); | 334 | EXPORT_SYMBOL_GPL(component_master_add); |
213 | 335 | ||
214 | void component_master_del(struct device *dev, | 336 | void component_master_del(struct device *dev, |