Blame view

drivers/base/component.c 11.8 KB
2a41e6070   Russell King   drivers/base: pro...
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
  /*
   * Componentized device handling.
   *
   * This program is free software; you can redistribute it and/or modify
   * it under the terms of the GNU General Public License version 2 as
   * published by the Free Software Foundation.
   *
   * This is work in progress.  We gather up the component devices into a list,
   * and bind them when instructed.  At the moment, we're specific to the DRM
   * subsystem, and only handles one master device, but this doesn't have to be
   * the case.
   */
  #include <linux/component.h>
  #include <linux/device.h>
  #include <linux/kref.h>
  #include <linux/list.h>
  #include <linux/module.h>
  #include <linux/mutex.h>
  #include <linux/slab.h>
ffc30b74f   Russell King   component: track ...
20
  struct component;
ce657b1cd   Russell King   component: add su...
21
22
23
24
25
26
27
  struct component_match_array {
  	void *data;
  	int (*compare)(struct device *, void *);
  	void (*release)(struct device *, void *);
  	struct component *component;
  	bool duplicate;
  };
6955b5825   Russell King   component: add su...
28
29
30
  struct component_match {
  	size_t alloc;
  	size_t num;
ce657b1cd   Russell King   component: add su...
31
  	struct component_match_array *compare;
6955b5825   Russell King   component: add su...
32
  };
2a41e6070   Russell King   drivers/base: pro...
33
34
  struct master {
  	struct list_head node;
2a41e6070   Russell King   drivers/base: pro...
35
36
37
38
  	bool bound;
  
  	const struct component_master_ops *ops;
  	struct device *dev;
6955b5825   Russell King   component: add su...
39
  	struct component_match *match;
2a41e6070   Russell King   drivers/base: pro...
40
41
42
43
  };
  
  struct component {
  	struct list_head node;
2a41e6070   Russell King   drivers/base: pro...
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
  	struct master *master;
  	bool bound;
  
  	const struct component_ops *ops;
  	struct device *dev;
  };
  
  static DEFINE_MUTEX(component_mutex);
  static LIST_HEAD(component_list);
  static LIST_HEAD(masters);
  
  static struct master *__master_find(struct device *dev,
  	const struct component_master_ops *ops)
  {
  	struct master *m;
  
  	list_for_each_entry(m, &masters, node)
  		if (m->dev == dev && (!ops || m->ops == ops))
  			return m;
  
  	return NULL;
  }
ffc30b74f   Russell King   component: track ...
66
  static struct component *find_component(struct master *master,
2a41e6070   Russell King   drivers/base: pro...
67
68
69
  	int (*compare)(struct device *, void *), void *compare_data)
  {
  	struct component *c;
2a41e6070   Russell King   drivers/base: pro...
70
71
  
  	list_for_each_entry(c, &component_list, node) {
fcbcebce7   Russell King   component: ignore...
72
  		if (c->master && c->master != master)
2a41e6070   Russell King   drivers/base: pro...
73
  			continue;
ffc30b74f   Russell King   component: track ...
74
75
  		if (compare(c->dev, compare_data))
  			return c;
2a41e6070   Russell King   drivers/base: pro...
76
  	}
ffc30b74f   Russell King   component: track ...
77
  	return NULL;
2a41e6070   Russell King   drivers/base: pro...
78
  }
2a41e6070   Russell King   drivers/base: pro...
79

6955b5825   Russell King   component: add su...
80
81
82
83
84
  static int find_components(struct master *master)
  {
  	struct component_match *match = master->match;
  	size_t i;
  	int ret = 0;
6955b5825   Russell King   component: add su...
85
86
87
88
89
  	/*
  	 * Scan the array of match functions and attach
  	 * any components which are found to this master.
  	 */
  	for (i = 0; i < match->num; i++) {
ce657b1cd   Russell King   component: add su...
90
  		struct component_match_array *mc = &match->compare[i];
ffc30b74f   Russell King   component: track ...
91
92
93
94
95
96
97
  		struct component *c;
  
  		dev_dbg(master->dev, "Looking for component %zu
  ", i);
  
  		if (match->compare[i].component)
  			continue;
ce657b1cd   Russell King   component: add su...
98
  		c = find_component(master, mc->compare, mc->data);
ffc30b74f   Russell King   component: track ...
99
100
  		if (!c) {
  			ret = -ENXIO;
6955b5825   Russell King   component: add su...
101
  			break;
ffc30b74f   Russell King   component: track ...
102
103
104
105
106
107
108
109
110
  		}
  
  		dev_dbg(master->dev, "found component %s, duplicate %u
  ", dev_name(c->dev), !!c->master);
  
  		/* Attach this component to the master */
  		match->compare[i].duplicate = !!c->master;
  		match->compare[i].component = c;
  		c->master = master;
6955b5825   Russell King   component: add su...
111
112
113
  	}
  	return ret;
  }
ffc30b74f   Russell King   component: track ...
114
115
  /* Detach component from associated master */
  static void remove_component(struct master *master, struct component *c)
2a41e6070   Russell King   drivers/base: pro...
116
  {
ffc30b74f   Russell King   component: track ...
117
  	size_t i;
2a41e6070   Russell King   drivers/base: pro...
118

ffc30b74f   Russell King   component: track ...
119
120
121
122
  	/* Detach the component from this master. */
  	for (i = 0; i < master->match->num; i++)
  		if (master->match->compare[i].component == c)
  			master->match->compare[i].component = NULL;
2a41e6070   Russell King   drivers/base: pro...
123
124
125
126
127
128
129
130
131
132
133
134
  }
  
  /*
   * Try to bring up a master.  If component is NULL, we're interested in
   * this master, otherwise it's a component which must be present to try
   * and bring up the master.
   *
   * Returns 1 for successful bringup, 0 if not ready, or -ve errno.
   */
  static int try_to_bring_up_master(struct master *master,
  	struct component *component)
  {
c334940ea   Russell King   component: fix mi...
135
  	int ret;
ffc30b74f   Russell King   component: track ...
136
137
  	dev_dbg(master->dev, "trying to bring up master
  ");
6955b5825   Russell King   component: add su...
138
  	if (find_components(master)) {
ffc30b74f   Russell King   component: track ...
139
140
141
  		dev_dbg(master->dev, "master has incomplete components
  ");
  		return 0;
c334940ea   Russell King   component: fix mi...
142
  	}
2a41e6070   Russell King   drivers/base: pro...
143

c334940ea   Russell King   component: fix mi...
144
  	if (component && component->master != master) {
ffc30b74f   Russell King   component: track ...
145
146
147
148
  		dev_dbg(master->dev, "master is not for this component (%s)
  ",
  			dev_name(component->dev));
  		return 0;
c334940ea   Russell King   component: fix mi...
149
  	}
2a41e6070   Russell King   drivers/base: pro...
150

ffc30b74f   Russell King   component: track ...
151
152
  	if (!devres_open_group(master->dev, NULL, GFP_KERNEL))
  		return -ENOMEM;
2a41e6070   Russell King   drivers/base: pro...
153

c334940ea   Russell King   component: fix mi...
154
155
156
157
158
159
  	/* Found all components */
  	ret = master->ops->bind(master->dev);
  	if (ret < 0) {
  		devres_release_group(master->dev, NULL);
  		dev_info(master->dev, "master bind failed: %d
  ", ret);
ffc30b74f   Russell King   component: track ...
160
  		return ret;
c334940ea   Russell King   component: fix mi...
161
  	}
9e1ccb4a7   Russell King   drivers/base: fix...
162

c334940ea   Russell King   component: fix mi...
163
164
  	master->bound = true;
  	return 1;
2a41e6070   Russell King   drivers/base: pro...
165
166
167
168
169
170
171
172
  }
  
  static int try_to_bring_up_masters(struct component *component)
  {
  	struct master *m;
  	int ret = 0;
  
  	list_for_each_entry(m, &masters, node) {
29f1c7fd6   Russell King   component: move c...
173
174
175
176
177
  		if (!m->bound) {
  			ret = try_to_bring_up_master(m, component);
  			if (ret != 0)
  				break;
  		}
2a41e6070   Russell King   drivers/base: pro...
178
179
180
181
182
183
184
185
186
  	}
  
  	return ret;
  }
  
  static void take_down_master(struct master *master)
  {
  	if (master->bound) {
  		master->ops->unbind(master->dev);
9e1ccb4a7   Russell King   drivers/base: fix...
187
  		devres_release_group(master->dev, NULL);
2a41e6070   Russell King   drivers/base: pro...
188
189
  		master->bound = false;
  	}
2a41e6070   Russell King   drivers/base: pro...
190
  }
ce657b1cd   Russell King   component: add su...
191
192
  static void component_match_release(struct device *master,
  	struct component_match *match)
6955b5825   Russell King   component: add su...
193
  {
ce657b1cd   Russell King   component: add su...
194
195
196
197
198
199
200
201
  	unsigned int i;
  
  	for (i = 0; i < match->num; i++) {
  		struct component_match_array *mc = &match->compare[i];
  
  		if (mc->release)
  			mc->release(master, mc->data);
  	}
9a4e7849b   Russell King   component: fix cr...
202
203
  
  	kfree(match->compare);
6955b5825   Russell King   component: add su...
204
  }
ce657b1cd   Russell King   component: add su...
205
206
207
208
209
210
  static void devm_component_match_release(struct device *dev, void *res)
  {
  	component_match_release(dev, res);
  }
  
  static int component_match_realloc(struct device *dev,
6955b5825   Russell King   component: add su...
211
212
  	struct component_match *match, size_t num)
  {
ce657b1cd   Russell King   component: add su...
213
  	struct component_match_array *new;
6955b5825   Russell King   component: add su...
214

ce657b1cd   Russell King   component: add su...
215
216
  	if (match->alloc == num)
  		return 0;
6955b5825   Russell King   component: add su...
217

9a4e7849b   Russell King   component: fix cr...
218
  	new = kmalloc_array(num, sizeof(*new), GFP_KERNEL);
6955b5825   Russell King   component: add su...
219
  	if (!new)
ce657b1cd   Russell King   component: add su...
220
  		return -ENOMEM;
6955b5825   Russell King   component: add su...
221

ce657b1cd   Russell King   component: add su...
222
223
224
  	if (match->compare) {
  		memcpy(new, match->compare, sizeof(*new) *
  					    min(match->num, num));
9a4e7849b   Russell King   component: fix cr...
225
  		kfree(match->compare);
6955b5825   Russell King   component: add su...
226
  	}
ce657b1cd   Russell King   component: add su...
227
228
  	match->compare = new;
  	match->alloc = num;
6955b5825   Russell King   component: add su...
229

ce657b1cd   Russell King   component: add su...
230
  	return 0;
6955b5825   Russell King   component: add su...
231
232
233
  }
  
  /*
ce657b1cd   Russell King   component: add su...
234
   * Add a component to be matched, with a release function.
6955b5825   Russell King   component: add su...
235
236
237
   *
   * The match array is first created or extended if necessary.
   */
ce657b1cd   Russell King   component: add su...
238
239
240
  void component_match_add_release(struct device *master,
  	struct component_match **matchptr,
  	void (*release)(struct device *, void *),
6955b5825   Russell King   component: add su...
241
242
243
244
245
246
  	int (*compare)(struct device *, void *), void *compare_data)
  {
  	struct component_match *match = *matchptr;
  
  	if (IS_ERR(match))
  		return;
ce657b1cd   Russell King   component: add su...
247
248
249
250
251
252
253
  	if (!match) {
  		match = devres_alloc(devm_component_match_release,
  				     sizeof(*match), GFP_KERNEL);
  		if (!match) {
  			*matchptr = ERR_PTR(-ENOMEM);
  			return;
  		}
6955b5825   Russell King   component: add su...
254

ce657b1cd   Russell King   component: add su...
255
  		devres_add(master, match);
6955b5825   Russell King   component: add su...
256
257
  
  		*matchptr = match;
ce657b1cd   Russell King   component: add su...
258
  	}
6955b5825   Russell King   component: add su...
259

ce657b1cd   Russell King   component: add su...
260
  	if (match->num == match->alloc) {
4877bb917   Sudip Mukherjee   component: remove...
261
  		size_t new_size = match->alloc + 16;
ce657b1cd   Russell King   component: add su...
262
263
264
265
266
  		int ret;
  
  		ret = component_match_realloc(master, match, new_size);
  		if (ret) {
  			*matchptr = ERR_PTR(ret);
6955b5825   Russell King   component: add su...
267
  			return;
ce657b1cd   Russell King   component: add su...
268
  		}
6955b5825   Russell King   component: add su...
269
  	}
ce657b1cd   Russell King   component: add su...
270
271
  	match->compare[match->num].compare = compare;
  	match->compare[match->num].release = release;
6955b5825   Russell King   component: add su...
272
  	match->compare[match->num].data = compare_data;
ffc30b74f   Russell King   component: track ...
273
  	match->compare[match->num].component = NULL;
6955b5825   Russell King   component: add su...
274
275
  	match->num++;
  }
ce657b1cd   Russell King   component: add su...
276
  EXPORT_SYMBOL(component_match_add_release);
6955b5825   Russell King   component: add su...
277

57480484f   Jon Medhurst (Tixy)   component: Detach...
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
  static void free_master(struct master *master)
  {
  	struct component_match *match = master->match;
  	int i;
  
  	list_del(&master->node);
  
  	if (match) {
  		for (i = 0; i < match->num; i++) {
  			struct component *c = match->compare[i].component;
  			if (c)
  				c->master = NULL;
  		}
  	}
  
  	kfree(master);
  }
6955b5825   Russell King   component: add su...
295
296
297
  int component_master_add_with_match(struct device *dev,
  	const struct component_master_ops *ops,
  	struct component_match *match)
2a41e6070   Russell King   drivers/base: pro...
298
299
300
  {
  	struct master *master;
  	int ret;
fae9e2e07   Russell King   component: remove...
301
  	/* Reallocate the match array for its true size */
ce657b1cd   Russell King   component: add su...
302
303
304
  	ret = component_match_realloc(dev, match, match->num);
  	if (ret)
  		return ret;
6955b5825   Russell King   component: add su...
305

2a41e6070   Russell King   drivers/base: pro...
306
307
308
309
310
311
  	master = kzalloc(sizeof(*master), GFP_KERNEL);
  	if (!master)
  		return -ENOMEM;
  
  	master->dev = dev;
  	master->ops = ops;
6955b5825   Russell King   component: add su...
312
  	master->match = match;
2a41e6070   Russell King   drivers/base: pro...
313
314
315
316
317
318
  
  	/* Add to the list of available masters. */
  	mutex_lock(&component_mutex);
  	list_add(&master->node, &masters);
  
  	ret = try_to_bring_up_master(master, NULL);
57480484f   Jon Medhurst (Tixy)   component: Detach...
319
320
  	if (ret < 0)
  		free_master(master);
2a41e6070   Russell King   drivers/base: pro...
321
322
323
324
  	mutex_unlock(&component_mutex);
  
  	return ret < 0 ? ret : 0;
  }
6955b5825   Russell King   component: add su...
325
  EXPORT_SYMBOL_GPL(component_master_add_with_match);
2a41e6070   Russell King   drivers/base: pro...
326
327
328
329
330
331
332
333
334
  void component_master_del(struct device *dev,
  	const struct component_master_ops *ops)
  {
  	struct master *master;
  
  	mutex_lock(&component_mutex);
  	master = __master_find(dev, ops);
  	if (master) {
  		take_down_master(master);
57480484f   Jon Medhurst (Tixy)   component: Detach...
335
  		free_master(master);
2a41e6070   Russell King   drivers/base: pro...
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
  	}
  	mutex_unlock(&component_mutex);
  }
  EXPORT_SYMBOL_GPL(component_master_del);
  
  static void component_unbind(struct component *component,
  	struct master *master, void *data)
  {
  	WARN_ON(!component->bound);
  
  	component->ops->unbind(component->dev, master->dev, data);
  	component->bound = false;
  
  	/* Release all resources claimed in the binding of this component */
  	devres_release_group(component->dev, component);
  }
  
  void component_unbind_all(struct device *master_dev, void *data)
  {
  	struct master *master;
  	struct component *c;
ffc30b74f   Russell King   component: track ...
357
  	size_t i;
2a41e6070   Russell King   drivers/base: pro...
358
359
360
361
362
363
  
  	WARN_ON(!mutex_is_locked(&component_mutex));
  
  	master = __master_find(master_dev, NULL);
  	if (!master)
  		return;
ffc30b74f   Russell King   component: track ...
364
365
366
367
368
369
  	/* Unbind components in reverse order */
  	for (i = master->match->num; i--; )
  		if (!master->match->compare[i].duplicate) {
  			c = master->match->compare[i].component;
  			component_unbind(c, master, data);
  		}
2a41e6070   Russell King   drivers/base: pro...
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
  }
  EXPORT_SYMBOL_GPL(component_unbind_all);
  
  static int component_bind(struct component *component, struct master *master,
  	void *data)
  {
  	int ret;
  
  	/*
  	 * Each component initialises inside its own devres group.
  	 * This allows us to roll-back a failed component without
  	 * affecting anything else.
  	 */
  	if (!devres_open_group(master->dev, NULL, GFP_KERNEL))
  		return -ENOMEM;
  
  	/*
  	 * Also open a group for the device itself: this allows us
  	 * to release the resources claimed against the sub-device
  	 * at the appropriate moment.
  	 */
  	if (!devres_open_group(component->dev, component, GFP_KERNEL)) {
  		devres_release_group(master->dev, NULL);
  		return -ENOMEM;
  	}
  
  	dev_dbg(master->dev, "binding %s (ops %ps)
  ",
  		dev_name(component->dev), component->ops);
  
  	ret = component->ops->bind(component->dev, master->dev, data);
  	if (!ret) {
  		component->bound = true;
  
  		/*
  		 * Close the component device's group so that resources
  		 * allocated in the binding are encapsulated for removal
  		 * at unbind.  Remove the group on the DRM device as we
  		 * can clean those resources up independently.
  		 */
  		devres_close_group(component->dev, NULL);
  		devres_remove_group(master->dev, NULL);
  
  		dev_info(master->dev, "bound %s (ops %ps)
  ",
  			 dev_name(component->dev), component->ops);
  	} else {
  		devres_release_group(component->dev, NULL);
  		devres_release_group(master->dev, NULL);
  
  		dev_err(master->dev, "failed to bind %s (ops %ps): %d
  ",
  			dev_name(component->dev), component->ops, ret);
  	}
  
  	return ret;
  }
  
  int component_bind_all(struct device *master_dev, void *data)
  {
  	struct master *master;
  	struct component *c;
ffc30b74f   Russell King   component: track ...
432
  	size_t i;
2a41e6070   Russell King   drivers/base: pro...
433
434
435
436
437
438
439
  	int ret = 0;
  
  	WARN_ON(!mutex_is_locked(&component_mutex));
  
  	master = __master_find(master_dev, NULL);
  	if (!master)
  		return -EINVAL;
ffc30b74f   Russell King   component: track ...
440
441
442
443
444
445
446
447
  	/* Bind components in match order */
  	for (i = 0; i < master->match->num; i++)
  		if (!master->match->compare[i].duplicate) {
  			c = master->match->compare[i].component;
  			ret = component_bind(c, master, data);
  			if (ret)
  				break;
  		}
2a41e6070   Russell King   drivers/base: pro...
448
449
  
  	if (ret != 0) {
ffc30b74f   Russell King   component: track ...
450
451
452
453
454
  		for (; i--; )
  			if (!master->match->compare[i].duplicate) {
  				c = master->match->compare[i].component;
  				component_unbind(c, master, data);
  			}
2a41e6070   Russell King   drivers/base: pro...
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
  	}
  
  	return ret;
  }
  EXPORT_SYMBOL_GPL(component_bind_all);
  
  int component_add(struct device *dev, const struct component_ops *ops)
  {
  	struct component *component;
  	int ret;
  
  	component = kzalloc(sizeof(*component), GFP_KERNEL);
  	if (!component)
  		return -ENOMEM;
  
  	component->ops = ops;
  	component->dev = dev;
  
  	dev_dbg(dev, "adding component (ops %ps)
  ", ops);
  
  	mutex_lock(&component_mutex);
  	list_add_tail(&component->node, &component_list);
  
  	ret = try_to_bring_up_masters(component);
  	if (ret < 0) {
8e7199c2c   Daniel Stone   component: remove...
481
482
  		if (component->master)
  			remove_component(component->master, component);
2a41e6070   Russell King   drivers/base: pro...
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
  		list_del(&component->node);
  
  		kfree(component);
  	}
  	mutex_unlock(&component_mutex);
  
  	return ret < 0 ? ret : 0;
  }
  EXPORT_SYMBOL_GPL(component_add);
  
  void component_del(struct device *dev, const struct component_ops *ops)
  {
  	struct component *c, *component = NULL;
  
  	mutex_lock(&component_mutex);
  	list_for_each_entry(c, &component_list, node)
  		if (c->dev == dev && c->ops == ops) {
  			list_del(&c->node);
  			component = c;
  			break;
  		}
ffc30b74f   Russell King   component: track ...
504
  	if (component && component->master) {
2a41e6070   Russell King   drivers/base: pro...
505
  		take_down_master(component->master);
ffc30b74f   Russell King   component: track ...
506
507
  		remove_component(component->master, component);
  	}
2a41e6070   Russell King   drivers/base: pro...
508
509
510
511
512
513
514
515
516
  
  	mutex_unlock(&component_mutex);
  
  	WARN_ON(!component);
  	kfree(component);
  }
  EXPORT_SYMBOL_GPL(component_del);
  
  MODULE_LICENSE("GPL v2");