gpu: host1x: Call ->remove() only when a device is bound
[deliverable/linux.git] / drivers / gpu / host1x / bus.c
CommitLineData
776dc384
TR
1/*
2 * Copyright (C) 2012 Avionic Design GmbH
3 * Copyright (C) 2012-2013, NVIDIA Corporation
4 *
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms and conditions of the GNU General Public License,
7 * version 2, as published by the Free Software Foundation.
8 *
9 * This program is distributed in the hope it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
12 * more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program. If not, see <http://www.gnu.org/licenses/>.
16 */
17
18#include <linux/host1x.h>
19#include <linux/of.h>
20#include <linux/slab.h>
21
d24b2898 22#include "bus.h"
776dc384
TR
23#include "dev.h"
24
25static DEFINE_MUTEX(clients_lock);
26static LIST_HEAD(clients);
27
28static DEFINE_MUTEX(drivers_lock);
29static LIST_HEAD(drivers);
30
31static DEFINE_MUTEX(devices_lock);
32static LIST_HEAD(devices);
33
34struct host1x_subdev {
35 struct host1x_client *client;
36 struct device_node *np;
37 struct list_head list;
38};
39
40/**
41 * host1x_subdev_add() - add a new subdevice with an associated device node
42 */
43static int host1x_subdev_add(struct host1x_device *device,
44 struct device_node *np)
45{
46 struct host1x_subdev *subdev;
47
48 subdev = kzalloc(sizeof(*subdev), GFP_KERNEL);
49 if (!subdev)
50 return -ENOMEM;
51
52 INIT_LIST_HEAD(&subdev->list);
53 subdev->np = of_node_get(np);
54
55 mutex_lock(&device->subdevs_lock);
56 list_add_tail(&subdev->list, &device->subdevs);
57 mutex_unlock(&device->subdevs_lock);
58
59 return 0;
60}
61
62/**
63 * host1x_subdev_del() - remove subdevice
64 */
65static void host1x_subdev_del(struct host1x_subdev *subdev)
66{
67 list_del(&subdev->list);
68 of_node_put(subdev->np);
69 kfree(subdev);
70}
71
72/**
73 * host1x_device_parse_dt() - scan device tree and add matching subdevices
74 */
75static int host1x_device_parse_dt(struct host1x_device *device)
76{
77 struct device_node *np;
78 int err;
79
80 for_each_child_of_node(device->dev.parent->of_node, np) {
81 if (of_match_node(device->driver->subdevs, np) &&
82 of_device_is_available(np)) {
83 err = host1x_subdev_add(device, np);
84 if (err < 0)
85 return err;
86 }
87 }
88
89 return 0;
90}
91
92static void host1x_subdev_register(struct host1x_device *device,
93 struct host1x_subdev *subdev,
94 struct host1x_client *client)
95{
96 int err;
97
98 /*
99 * Move the subdevice to the list of active (registered) subdevices
100 * and associate it with a client. At the same time, associate the
101 * client with its parent device.
102 */
103 mutex_lock(&device->subdevs_lock);
104 mutex_lock(&device->clients_lock);
105 list_move_tail(&client->list, &device->clients);
106 list_move_tail(&subdev->list, &device->active);
107 client->parent = &device->dev;
108 subdev->client = client;
109 mutex_unlock(&device->clients_lock);
110 mutex_unlock(&device->subdevs_lock);
111
112 /*
113 * When all subdevices have been registered, the composite device is
114 * ready to be probed.
115 */
116 if (list_empty(&device->subdevs)) {
117 err = device->driver->probe(device);
118 if (err < 0)
536e1715
TR
119 dev_err(&device->dev, "probe failed for %ps: %d\n",
120 device->driver, err);
121 else
122 device->bound = true;
776dc384
TR
123 }
124}
125
126static void __host1x_subdev_unregister(struct host1x_device *device,
127 struct host1x_subdev *subdev)
128{
129 struct host1x_client *client = subdev->client;
130 int err;
131
132 /*
133 * If all subdevices have been activated, we're about to remove the
134 * first active subdevice, so unload the driver first.
135 */
536e1715 136 if (list_empty(&device->subdevs) && device->bound) {
776dc384
TR
137 err = device->driver->remove(device);
138 if (err < 0)
139 dev_err(&device->dev, "remove failed: %d\n", err);
536e1715
TR
140
141 device->bound = false;
776dc384
TR
142 }
143
144 /*
145 * Move the subdevice back to the list of idle subdevices and remove
146 * it from list of clients.
147 */
148 mutex_lock(&device->clients_lock);
149 subdev->client = NULL;
150 client->parent = NULL;
151 list_move_tail(&subdev->list, &device->subdevs);
152 /*
153 * XXX: Perhaps don't do this here, but rather explicitly remove it
154 * when the device is about to be deleted.
155 *
156 * This is somewhat complicated by the fact that this function is
157 * used to remove the subdevice when a client is unregistered but
158 * also when the composite device is about to be removed.
159 */
160 list_del_init(&client->list);
161 mutex_unlock(&device->clients_lock);
162}
163
164static void host1x_subdev_unregister(struct host1x_device *device,
165 struct host1x_subdev *subdev)
166{
167 mutex_lock(&device->subdevs_lock);
168 __host1x_subdev_unregister(device, subdev);
169 mutex_unlock(&device->subdevs_lock);
170}
171
172int host1x_device_init(struct host1x_device *device)
173{
174 struct host1x_client *client;
175 int err;
176
177 mutex_lock(&device->clients_lock);
178
179 list_for_each_entry(client, &device->clients, list) {
180 if (client->ops && client->ops->init) {
181 err = client->ops->init(client);
182 if (err < 0) {
183 dev_err(&device->dev,
184 "failed to initialize %s: %d\n",
185 dev_name(client->dev), err);
186 mutex_unlock(&device->clients_lock);
187 return err;
188 }
189 }
190 }
191
192 mutex_unlock(&device->clients_lock);
193
194 return 0;
195}
fae798a1 196EXPORT_SYMBOL(host1x_device_init);
776dc384
TR
197
198int host1x_device_exit(struct host1x_device *device)
199{
200 struct host1x_client *client;
201 int err;
202
203 mutex_lock(&device->clients_lock);
204
205 list_for_each_entry_reverse(client, &device->clients, list) {
206 if (client->ops && client->ops->exit) {
207 err = client->ops->exit(client);
208 if (err < 0) {
209 dev_err(&device->dev,
210 "failed to cleanup %s: %d\n",
211 dev_name(client->dev), err);
212 mutex_unlock(&device->clients_lock);
213 return err;
214 }
215 }
216 }
217
218 mutex_unlock(&device->clients_lock);
219
220 return 0;
221}
fae798a1 222EXPORT_SYMBOL(host1x_device_exit);
776dc384 223
0c7dfd36
TR
224static int host1x_add_client(struct host1x *host1x,
225 struct host1x_client *client)
776dc384
TR
226{
227 struct host1x_device *device;
228 struct host1x_subdev *subdev;
229
230 mutex_lock(&host1x->devices_lock);
231
232 list_for_each_entry(device, &host1x->devices, list) {
233 list_for_each_entry(subdev, &device->subdevs, list) {
234 if (subdev->np == client->dev->of_node) {
235 host1x_subdev_register(device, subdev, client);
236 mutex_unlock(&host1x->devices_lock);
237 return 0;
238 }
239 }
240 }
241
242 mutex_unlock(&host1x->devices_lock);
243 return -ENODEV;
244}
245
0c7dfd36
TR
246static int host1x_del_client(struct host1x *host1x,
247 struct host1x_client *client)
776dc384
TR
248{
249 struct host1x_device *device, *dt;
250 struct host1x_subdev *subdev;
251
252 mutex_lock(&host1x->devices_lock);
253
254 list_for_each_entry_safe(device, dt, &host1x->devices, list) {
255 list_for_each_entry(subdev, &device->active, list) {
256 if (subdev->client == client) {
257 host1x_subdev_unregister(device, subdev);
258 mutex_unlock(&host1x->devices_lock);
259 return 0;
260 }
261 }
262 }
263
264 mutex_unlock(&host1x->devices_lock);
265 return -ENODEV;
266}
267
d24b2898 268static struct bus_type host1x_bus_type = {
776dc384
TR
269 .name = "host1x",
270};
271
272int host1x_bus_init(void)
273{
274 return bus_register(&host1x_bus_type);
275}
276
277void host1x_bus_exit(void)
278{
279 bus_unregister(&host1x_bus_type);
280}
281
282static void host1x_device_release(struct device *dev)
283{
284 struct host1x_device *device = to_host1x_device(dev);
285
286 kfree(device);
287}
288
289static int host1x_device_add(struct host1x *host1x,
290 struct host1x_driver *driver)
291{
292 struct host1x_client *client, *tmp;
293 struct host1x_subdev *subdev;
294 struct host1x_device *device;
295 int err;
296
297 device = kzalloc(sizeof(*device), GFP_KERNEL);
298 if (!device)
299 return -ENOMEM;
300
301 mutex_init(&device->subdevs_lock);
302 INIT_LIST_HEAD(&device->subdevs);
303 INIT_LIST_HEAD(&device->active);
304 mutex_init(&device->clients_lock);
305 INIT_LIST_HEAD(&device->clients);
306 INIT_LIST_HEAD(&device->list);
307 device->driver = driver;
308
309 device->dev.coherent_dma_mask = host1x->dev->coherent_dma_mask;
310 device->dev.dma_mask = &device->dev.coherent_dma_mask;
311 device->dev.release = host1x_device_release;
d24b2898 312 dev_set_name(&device->dev, "%s", driver->name);
776dc384
TR
313 device->dev.bus = &host1x_bus_type;
314 device->dev.parent = host1x->dev;
315
316 err = device_register(&device->dev);
317 if (err < 0)
318 return err;
319
320 err = host1x_device_parse_dt(device);
321 if (err < 0) {
322 device_unregister(&device->dev);
323 return err;
324 }
325
326 mutex_lock(&host1x->devices_lock);
327 list_add_tail(&device->list, &host1x->devices);
328 mutex_unlock(&host1x->devices_lock);
329
330 mutex_lock(&clients_lock);
331
332 list_for_each_entry_safe(client, tmp, &clients, list) {
333 list_for_each_entry(subdev, &device->subdevs, list) {
334 if (subdev->np == client->dev->of_node) {
335 host1x_subdev_register(device, subdev, client);
336 break;
337 }
338 }
339 }
340
341 mutex_unlock(&clients_lock);
342
343 return 0;
344}
345
346/*
347 * Removes a device by first unregistering any subdevices and then removing
348 * itself from the list of devices.
349 *
350 * This function must be called with the host1x->devices_lock held.
351 */
352static void host1x_device_del(struct host1x *host1x,
353 struct host1x_device *device)
354{
355 struct host1x_subdev *subdev, *sd;
356 struct host1x_client *client, *cl;
357
358 mutex_lock(&device->subdevs_lock);
359
360 /* unregister subdevices */
361 list_for_each_entry_safe(subdev, sd, &device->active, list) {
362 /*
363 * host1x_subdev_unregister() will remove the client from
364 * any lists, so we'll need to manually add it back to the
365 * list of idle clients.
366 *
367 * XXX: Alternatively, perhaps don't remove the client from
368 * any lists in host1x_subdev_unregister() and instead do
369 * that explicitly from host1x_unregister_client()?
370 */
371 client = subdev->client;
372
373 __host1x_subdev_unregister(device, subdev);
374
375 /* add the client to the list of idle clients */
376 mutex_lock(&clients_lock);
377 list_add_tail(&client->list, &clients);
378 mutex_unlock(&clients_lock);
379 }
380
381 /* remove subdevices */
382 list_for_each_entry_safe(subdev, sd, &device->subdevs, list)
383 host1x_subdev_del(subdev);
384
385 mutex_unlock(&device->subdevs_lock);
386
387 /* move clients to idle list */
388 mutex_lock(&clients_lock);
389 mutex_lock(&device->clients_lock);
390
391 list_for_each_entry_safe(client, cl, &device->clients, list)
392 list_move_tail(&client->list, &clients);
393
394 mutex_unlock(&device->clients_lock);
395 mutex_unlock(&clients_lock);
396
397 /* finally remove the device */
398 list_del_init(&device->list);
399 device_unregister(&device->dev);
400}
401
402static void host1x_attach_driver(struct host1x *host1x,
403 struct host1x_driver *driver)
404{
405 struct host1x_device *device;
406 int err;
407
408 mutex_lock(&host1x->devices_lock);
409
410 list_for_each_entry(device, &host1x->devices, list) {
411 if (device->driver == driver) {
412 mutex_unlock(&host1x->devices_lock);
413 return;
414 }
415 }
416
417 mutex_unlock(&host1x->devices_lock);
418
419 err = host1x_device_add(host1x, driver);
420 if (err < 0)
421 dev_err(host1x->dev, "failed to allocate device: %d\n", err);
422}
423
424static void host1x_detach_driver(struct host1x *host1x,
425 struct host1x_driver *driver)
426{
427 struct host1x_device *device, *tmp;
428
429 mutex_lock(&host1x->devices_lock);
430
431 list_for_each_entry_safe(device, tmp, &host1x->devices, list)
432 if (device->driver == driver)
433 host1x_device_del(host1x, device);
434
435 mutex_unlock(&host1x->devices_lock);
436}
437
438int host1x_register(struct host1x *host1x)
439{
440 struct host1x_driver *driver;
441
442 mutex_lock(&devices_lock);
443 list_add_tail(&host1x->list, &devices);
444 mutex_unlock(&devices_lock);
445
446 mutex_lock(&drivers_lock);
447
448 list_for_each_entry(driver, &drivers, list)
449 host1x_attach_driver(host1x, driver);
450
451 mutex_unlock(&drivers_lock);
452
453 return 0;
454}
455
456int host1x_unregister(struct host1x *host1x)
457{
458 struct host1x_driver *driver;
459
460 mutex_lock(&drivers_lock);
461
462 list_for_each_entry(driver, &drivers, list)
463 host1x_detach_driver(host1x, driver);
464
465 mutex_unlock(&drivers_lock);
466
467 mutex_lock(&devices_lock);
468 list_del_init(&host1x->list);
469 mutex_unlock(&devices_lock);
470
471 return 0;
472}
473
474int host1x_driver_register(struct host1x_driver *driver)
475{
476 struct host1x *host1x;
477
478 INIT_LIST_HEAD(&driver->list);
479
480 mutex_lock(&drivers_lock);
481 list_add_tail(&driver->list, &drivers);
482 mutex_unlock(&drivers_lock);
483
484 mutex_lock(&devices_lock);
485
486 list_for_each_entry(host1x, &devices, list)
487 host1x_attach_driver(host1x, driver);
488
489 mutex_unlock(&devices_lock);
490
491 return 0;
492}
493EXPORT_SYMBOL(host1x_driver_register);
494
495void host1x_driver_unregister(struct host1x_driver *driver)
496{
497 mutex_lock(&drivers_lock);
498 list_del_init(&driver->list);
499 mutex_unlock(&drivers_lock);
500}
501EXPORT_SYMBOL(host1x_driver_unregister);
502
503int host1x_client_register(struct host1x_client *client)
504{
505 struct host1x *host1x;
506 int err;
507
508 mutex_lock(&devices_lock);
509
510 list_for_each_entry(host1x, &devices, list) {
0c7dfd36 511 err = host1x_add_client(host1x, client);
776dc384
TR
512 if (!err) {
513 mutex_unlock(&devices_lock);
514 return 0;
515 }
516 }
517
518 mutex_unlock(&devices_lock);
519
520 mutex_lock(&clients_lock);
521 list_add_tail(&client->list, &clients);
522 mutex_unlock(&clients_lock);
523
524 return 0;
525}
526EXPORT_SYMBOL(host1x_client_register);
527
528int host1x_client_unregister(struct host1x_client *client)
529{
530 struct host1x_client *c;
531 struct host1x *host1x;
532 int err;
533
534 mutex_lock(&devices_lock);
535
536 list_for_each_entry(host1x, &devices, list) {
0c7dfd36 537 err = host1x_del_client(host1x, client);
776dc384
TR
538 if (!err) {
539 mutex_unlock(&devices_lock);
540 return 0;
541 }
542 }
543
544 mutex_unlock(&devices_lock);
545 mutex_lock(&clients_lock);
546
547 list_for_each_entry(c, &clients, list) {
548 if (c == client) {
549 list_del_init(&c->list);
550 break;
551 }
552 }
553
554 mutex_unlock(&clients_lock);
555
556 return 0;
557}
558EXPORT_SYMBOL(host1x_client_unregister);
This page took 0.101728 seconds and 5 git commands to generate.