]> git.karo-electronics.de Git - karo-tx-linux.git/blob - drivers/staging/greybus/core.c
greybus: connection: properly lock idr
[karo-tx-linux.git] / drivers / staging / greybus / core.c
1 /*
2  * Greybus "Core"
3  *
4  * Copyright 2014 Google Inc.
5  *
6  * Released under the GPLv2 only.
7  */
8
9 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
10
11 #include <linux/types.h>
12 #include <linux/module.h>
13 #include <linux/moduleparam.h>
14 #include <linux/kernel.h>
15 #include <linux/slab.h>
16 #include <linux/device.h>
17
18 #include "greybus.h"
19
20 /* Allow greybus to be disabled at boot if needed */
21 static bool nogreybus;
22 #ifdef MODULE
23 module_param(nogreybus, bool, 0444);
24 #else
25 core_param(nogreybus, bool, 0444);
26 #endif
27 int greybus_disabled(void)
28 {
29         return nogreybus;
30 }
31 EXPORT_SYMBOL_GPL(greybus_disabled);
32
33 static int greybus_module_match(struct device *dev, struct device_driver *drv)
34 {
35         struct greybus_driver *driver = to_greybus_driver(dev->driver);
36         struct gb_module *gmod = to_gb_module(dev);
37         const struct greybus_module_id *id;
38
39         id = gb_module_match_id(gmod, driver->id_table);
40         if (id)
41                 return 1;
42         /* FIXME - Dyanmic ids? */
43         return 0;
44 }
45
46 static int greybus_uevent(struct device *dev, struct kobj_uevent_env *env)
47 {
48         /* struct gb_module *gmod = to_gb_module(dev); */
49
50         /* FIXME - add some uevents here... */
51         return 0;
52 }
53
54 static struct bus_type greybus_bus_type = {
55         .name =         "greybus",
56         .match =        greybus_module_match,
57         .uevent =       greybus_uevent,
58 };
59
60 static int greybus_probe(struct device *dev)
61 {
62         struct greybus_driver *driver = to_greybus_driver(dev->driver);
63         struct gb_module *gmod = to_gb_module(dev);
64         const struct greybus_module_id *id;
65         int retval;
66
67         /* match id */
68         id = gb_module_match_id(gmod, driver->id_table);
69         if (!id)
70                 return -ENODEV;
71
72         retval = driver->probe(gmod, id);
73         if (retval)
74                 return retval;
75
76         return 0;
77 }
78
79 static int greybus_remove(struct device *dev)
80 {
81         struct greybus_driver *driver = to_greybus_driver(dev->driver);
82         struct gb_module *gmod = to_gb_module(dev);
83
84         driver->disconnect(gmod);
85         return 0;
86 }
87
88 int greybus_register_driver(struct greybus_driver *driver, struct module *owner,
89                 const char *mod_name)
90 {
91         int retval;
92
93         if (greybus_disabled())
94                 return -ENODEV;
95
96         driver->driver.name = driver->name;
97         driver->driver.probe = greybus_probe;
98         driver->driver.remove = greybus_remove;
99         driver->driver.owner = owner;
100         driver->driver.mod_name = mod_name;
101
102         retval = driver_register(&driver->driver);
103         if (retval)
104                 return retval;
105
106         pr_info("registered new driver %s\n", driver->name);
107         return 0;
108 }
109 EXPORT_SYMBOL_GPL(greybus_register_driver);
110
111 void greybus_deregister(struct greybus_driver *driver)
112 {
113         driver_unregister(&driver->driver);
114 }
115 EXPORT_SYMBOL_GPL(greybus_deregister);
116
117
118 static void greybus_module_release(struct device *dev)
119 {
120         struct gb_module *gmod = to_gb_module(dev);
121         int i;
122
123         for (i = 0; i < gmod->num_strings; ++i)
124                 kfree(gmod->string[i]);
125         kfree(gmod);
126 }
127
128
129 static struct device_type greybus_module_type = {
130         .name =         "greybus_module",
131         .release =      greybus_module_release,
132 };
133
134 /* XXX
135  * This needs to be driven by the list of functions that the
136  * manifest says are present.
137  */
138 static int gb_init_subdevs(struct gb_module *gmod,
139                            const struct greybus_module_id *id)
140 {
141         int retval;
142
143         /* Allocate all of the different "sub device types" for this device */
144
145         /* XXX
146          * Decide what exactly we should get supplied for the i2c
147          * probe, and then work that back to what should be present
148          * in the manifest.
149          */
150         retval = gb_i2c_probe(gmod, id);
151         if (retval)
152                 goto error_i2c;
153
154         retval = gb_gpio_probe(gmod, id);
155         if (retval)
156                 goto error_gpio;
157
158         retval = gb_sdio_probe(gmod, id);
159         if (retval)
160                 goto error_sdio;
161
162         retval = gb_tty_probe(gmod, id);
163         if (retval)
164                 goto error_tty;
165
166         retval = gb_battery_probe(gmod, id);
167         if (retval)
168                 goto error_battery;
169         return 0;
170
171 error_battery:
172         gb_tty_disconnect(gmod);
173
174 error_tty:
175         gb_sdio_disconnect(gmod);
176
177 error_sdio:
178         gb_gpio_disconnect(gmod);
179
180 error_gpio:
181         gb_i2c_disconnect(gmod);
182
183 error_i2c:
184         return retval;
185 }
186
187 static const struct greybus_module_id fake_greybus_module_id = {
188         GREYBUS_DEVICE(0x42, 0x42)
189 };
190
191
192 /**
193  * gb_add_module
194  *
195  * Pass in a buffer that _should_ contain a Greybus module manifest
196  * and register a greybus device structure with the kernel core.
197  */
198 void gb_add_module(struct greybus_host_device *hd, u8 module_id,
199                    u8 *data, int size)
200 {
201         struct gb_module *gmod;
202         int retval;
203
204         gmod = gb_module_create(hd, module_id);
205         if (!gmod) {
206                 dev_err(hd->parent, "failed to create module\n");
207                 return;
208         }
209
210         /*
211          * Parse the manifest and build up our data structures
212          * representing what's in it.
213          */
214         if (!gb_manifest_parse(gmod, data, size)) {
215                 dev_err(hd->parent, "manifest error\n");
216                 goto error;
217         }
218
219         /*
220          * XXX
221          * We've successfully parsed the manifest.  Now we need to
222          * allocate CPort Id's for connecting to the CPorts found on
223          * other modules.  For each of these, establish a connection
224          * between the local and remote CPorts (including
225          * configuring the switch to allow them to communicate).
226          */
227
228         gmod->dev.parent = hd->parent;
229         gmod->dev.driver = NULL;
230         gmod->dev.bus = &greybus_bus_type;
231         gmod->dev.type = &greybus_module_type;
232         gmod->dev.groups = greybus_module_groups;
233         gmod->dev.dma_mask = hd->parent->dma_mask;
234         device_initialize(&gmod->dev);
235         dev_set_name(&gmod->dev, "%d", module_id);
236
237         retval = device_add(&gmod->dev);
238         if (retval)
239                 goto error;
240
241         retval = gb_init_subdevs(gmod, &fake_greybus_module_id);
242         if (retval)
243                 goto error_subdevs;
244
245         //return gmod;
246         return;
247
248 error_subdevs:
249         device_del(&gmod->dev);
250
251 error:
252         gb_module_destroy(gmod);
253
254         put_device(&gmod->dev);
255         greybus_module_release(&gmod->dev);
256 }
257
258 void gb_remove_module(struct greybus_host_device *hd, u8 module_id)
259 {
260         struct gb_module *gmod;
261         bool found = false;
262
263         list_for_each_entry(gmod, &hd->modules, links)
264                 if (gmod->module_id == module_id) {
265                         found = true;
266                         break;
267                 }
268
269         if (found)
270                 greybus_remove_device(gmod);
271         else
272                 dev_err(hd->parent, "module id %d remove error\n", module_id);
273 }
274
275 void greybus_remove_device(struct gb_module *gmod)
276 {
277         /* tear down all of the "sub device types" for this device */
278         gb_i2c_disconnect(gmod);
279         gb_gpio_disconnect(gmod);
280         gb_sdio_disconnect(gmod);
281         gb_tty_disconnect(gmod);
282         gb_battery_disconnect(gmod);
283
284         device_del(&gmod->dev);
285         put_device(&gmod->dev);
286 }
287
288 static DEFINE_MUTEX(hd_mutex);
289
290 static void free_hd(struct kref *kref)
291 {
292         struct greybus_host_device *hd;
293
294         hd = container_of(kref, struct greybus_host_device, kref);
295
296         kfree(hd);
297 }
298
299 struct greybus_host_device *greybus_create_hd(struct greybus_host_driver *driver,
300                                               struct device *parent)
301 {
302         struct greybus_host_device *hd;
303
304         hd = kzalloc(sizeof(*hd) + driver->hd_priv_size, GFP_KERNEL);
305         if (!hd)
306                 return NULL;
307
308         kref_init(&hd->kref);
309         hd->parent = parent;
310         hd->driver = driver;
311         INIT_LIST_HEAD(&hd->modules);
312         hd->connections = RB_ROOT;
313         ida_init(&hd->cport_id_map);
314         spin_lock_init(&hd->cport_id_map_lock);
315
316         return hd;
317 }
318 EXPORT_SYMBOL_GPL(greybus_create_hd);
319
320 void greybus_remove_hd(struct greybus_host_device *hd)
321 {
322         kref_put_mutex(&hd->kref, free_hd, &hd_mutex);
323 }
324 EXPORT_SYMBOL_GPL(greybus_remove_hd);
325
326
327 static int __init gb_init(void)
328 {
329         int retval;
330
331         BUILD_BUG_ON(HOST_DEV_CPORT_ID_MAX >= (long)CPORT_ID_BAD);
332
333         retval = gb_debugfs_init();
334         if (retval) {
335                 pr_err("debugfs failed\n");
336                 return retval;
337         }
338
339         retval = bus_register(&greybus_bus_type);
340         if (retval) {
341                 pr_err("bus_register failed\n");
342                 goto error_bus;
343         }
344
345         retval = gb_ap_init();
346         if (retval) {
347                 pr_err("gb_ap_init failed\n");
348                 goto error_ap;
349         }
350
351         retval = gb_gbuf_init();
352         if (retval) {
353                 pr_err("gb_gbuf_init failed\n");
354                 goto error_gbuf;
355         }
356
357         retval = gb_tty_init();
358         if (retval) {
359                 pr_err("gb_tty_init failed\n");
360                 goto error_tty;
361         }
362
363         return 0;
364
365 error_tty:
366         gb_gbuf_exit();
367
368 error_gbuf:
369         gb_ap_exit();
370
371 error_ap:
372         bus_unregister(&greybus_bus_type);
373
374 error_bus:
375         gb_debugfs_cleanup();
376
377         return retval;
378 }
379
380 static void __exit gb_exit(void)
381 {
382         gb_tty_exit();
383         gb_gbuf_exit();
384         gb_ap_exit();
385         bus_unregister(&greybus_bus_type);
386         gb_debugfs_cleanup();
387 }
388
389 module_init(gb_init);
390 module_exit(gb_exit);
391
392 MODULE_LICENSE("GPL");
393 MODULE_AUTHOR("Greg Kroah-Hartman <gregkh@linuxfoundation.org>");