]> git.karo-electronics.de Git - karo-tx-linux.git/blob - drivers/staging/greybus/core.c
greybus: create host device cport id map
[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 spinlock_t cport_id_map_lock;
34
35 static int greybus_module_match(struct device *dev, struct device_driver *drv)
36 {
37         struct greybus_driver *driver = to_greybus_driver(dev->driver);
38         struct gb_module *gmod = to_gb_module(dev);
39         const struct greybus_module_id *id;
40
41         id = gb_module_match_id(gmod, driver->id_table);
42         if (id)
43                 return 1;
44         /* FIXME - Dyanmic ids? */
45         return 0;
46 }
47
48 static int greybus_uevent(struct device *dev, struct kobj_uevent_env *env)
49 {
50         /* struct gb_module *gmod = to_gb_module(dev); */
51
52         /* FIXME - add some uevents here... */
53         return 0;
54 }
55
56 static struct bus_type greybus_bus_type = {
57         .name =         "greybus",
58         .match =        greybus_module_match,
59         .uevent =       greybus_uevent,
60 };
61
62 static int greybus_probe(struct device *dev)
63 {
64         struct greybus_driver *driver = to_greybus_driver(dev->driver);
65         struct gb_module *gmod = to_gb_module(dev);
66         const struct greybus_module_id *id;
67         int retval;
68
69         /* match id */
70         id = gb_module_match_id(gmod, driver->id_table);
71         if (!id)
72                 return -ENODEV;
73
74         retval = driver->probe(gmod, id);
75         if (retval)
76                 return retval;
77
78         return 0;
79 }
80
81 static int greybus_remove(struct device *dev)
82 {
83         struct greybus_driver *driver = to_greybus_driver(dev->driver);
84         struct gb_module *gmod = to_gb_module(dev);
85
86         driver->disconnect(gmod);
87         return 0;
88 }
89
90 int greybus_register_driver(struct greybus_driver *driver, struct module *owner,
91                 const char *mod_name)
92 {
93         int retval;
94
95         if (greybus_disabled())
96                 return -ENODEV;
97
98         driver->driver.name = driver->name;
99         driver->driver.probe = greybus_probe;
100         driver->driver.remove = greybus_remove;
101         driver->driver.owner = owner;
102         driver->driver.mod_name = mod_name;
103
104         retval = driver_register(&driver->driver);
105         if (retval)
106                 return retval;
107
108         pr_info("registered new driver %s\n", driver->name);
109         return 0;
110 }
111 EXPORT_SYMBOL_GPL(greybus_register_driver);
112
113 void greybus_deregister(struct greybus_driver *driver)
114 {
115         driver_unregister(&driver->driver);
116 }
117 EXPORT_SYMBOL_GPL(greybus_deregister);
118
119
120 static void greybus_module_release(struct device *dev)
121 {
122         struct gb_module *gmod = to_gb_module(dev);
123         int i;
124
125         for (i = 0; i < gmod->num_strings; ++i)
126                 kfree(gmod->string[i]);
127         kfree(gmod);
128 }
129
130
131 static struct device_type greybus_module_type = {
132         .name =         "greybus_module",
133         .release =      greybus_module_release,
134 };
135
136 /* XXX
137  * This needs to be driven by the list of functions that the
138  * manifest says are present.
139  */
140 static int gb_init_subdevs(struct gb_module *gmod,
141                            const struct greybus_module_id *id)
142 {
143         int retval;
144
145         /* Allocate all of the different "sub device types" for this device */
146
147         /* XXX
148          * Decide what exactly we should get supplied for the i2c
149          * probe, and then work that back to what should be present
150          * in the manifest.
151          */
152         retval = gb_i2c_probe(gmod, id);
153         if (retval)
154                 goto error_i2c;
155
156         retval = gb_gpio_probe(gmod, id);
157         if (retval)
158                 goto error_gpio;
159
160         retval = gb_sdio_probe(gmod, id);
161         if (retval)
162                 goto error_sdio;
163
164         retval = gb_tty_probe(gmod, id);
165         if (retval)
166                 goto error_tty;
167
168         retval = gb_battery_probe(gmod, id);
169         if (retval)
170                 goto error_battery;
171         return 0;
172
173 error_battery:
174         gb_tty_disconnect(gmod);
175
176 error_tty:
177         gb_sdio_disconnect(gmod);
178
179 error_sdio:
180         gb_gpio_disconnect(gmod);
181
182 error_gpio:
183         gb_i2c_disconnect(gmod);
184
185 error_i2c:
186         return retval;
187 }
188
189 static const struct greybus_module_id fake_greybus_module_id = {
190         GREYBUS_DEVICE(0x42, 0x42)
191 };
192
193
194 /**
195  * gb_add_module
196  *
197  * Pass in a buffer that _should_ contain a Greybus module manifest
198  * and register a greybus device structure with the kernel core.
199  */
200 void gb_add_module(struct greybus_host_device *hd, u8 module_id,
201                    u8 *data, int size)
202 {
203         struct gb_module *gmod;
204         int retval;
205
206         /*
207          * Parse the manifest and build up our data structures
208          * representing what's in it.
209          */
210         gmod = gb_manifest_parse(data, size);
211         if (!gmod) {
212                 dev_err(hd->parent, "manifest error\n");
213                 return;
214         }
215
216         /*
217          * XXX
218          * We've successfully parsed the manifest.  Now we need to
219          * allocate CPort Id's for connecting to the CPorts found on
220          * other modules.  For each of these, establish a connection
221          * between the local and remote CPorts (including
222          * configuring the switch to allow them to communicate).
223          */
224
225         gmod->dev.parent = hd->parent;
226         gmod->dev.driver = NULL;
227         gmod->dev.bus = &greybus_bus_type;
228         gmod->dev.type = &greybus_module_type;
229         gmod->dev.groups = greybus_module_groups;
230         gmod->dev.dma_mask = hd->parent->dma_mask;
231         device_initialize(&gmod->dev);
232         dev_set_name(&gmod->dev, "%d", module_id);
233
234         retval = gb_init_subdevs(gmod, &fake_greybus_module_id);
235         if (retval)
236                 goto error;
237
238         // FIXME device_add(&gmod->dev);
239
240         //return gmod;
241         return;
242 error:
243         put_device(&gmod->dev);
244         greybus_module_release(&gmod->dev);
245 }
246
247 void gb_remove_module(struct greybus_host_device *hd, u8 module_id)
248 {
249         // FIXME should be the remove_device call...
250 }
251
252 void greybus_remove_device(struct gb_module *gmod)
253 {
254         /* tear down all of the "sub device types" for this device */
255         gb_i2c_disconnect(gmod);
256         gb_gpio_disconnect(gmod);
257         gb_sdio_disconnect(gmod);
258         gb_tty_disconnect(gmod);
259         gb_battery_disconnect(gmod);
260
261         // FIXME - device_remove(&gmod->dev);
262 }
263
264 static DEFINE_MUTEX(hd_mutex);
265
266 /*
267  * Allocate an available CPort Id for use on the given host device.
268  * Returns the CPort Id, or CPORT_ID_BAD of none remain.
269  *
270  * The lowest-available id is returned, so the first call is
271  * guaranteed to allocate CPort Id 0.
272  */
273 u16 greybus_hd_cport_id_alloc(struct greybus_host_device *hd)
274 {
275         unsigned long cport_id;
276
277         /* If none left, return BAD */
278         if (hd->cport_id_count == HOST_DEV_CPORT_ID_MAX)
279                 return CPORT_ID_BAD;
280
281         spin_lock_irq(&cport_id_map_lock);
282         cport_id = find_next_zero_bit(hd->cport_id_map, hd->cport_id_count,
283                                                 hd->cport_id_next_free);
284         if (cport_id < hd->cport_id_count) {
285                 hd->cport_id_next_free = cport_id + 1;  /* Success */
286                 hd->cport_id_count++;
287         } else {
288                 /* Lost a race for the last one */
289                 if (hd->cport_id_count != HOST_DEV_CPORT_ID_MAX) {
290                         pr_err("bad cport_id_count in alloc");
291                         hd->cport_id_count = HOST_DEV_CPORT_ID_MAX;
292                 }
293                 cport_id = CPORT_ID_BAD;
294         }
295         spin_unlock_irq(&cport_id_map_lock);
296
297         return cport_id;
298 }
299
300 /*
301  * Free a previously-allocated CPort Id on the given host device.
302  */
303 void greybus_hd_cport_id_free(struct greybus_host_device *hd, u16 cport_id)
304 {
305         if (cport_id >= HOST_DEV_CPORT_ID_MAX) {
306                 pr_err("bad cport_id %hu\n", cport_id);
307                 return;
308         }
309         if (!hd->cport_id_count) {
310                 pr_err("too many cport_id frees\n");
311                 return;
312         }
313
314         spin_lock_irq(&cport_id_map_lock);
315         if (test_and_clear_bit(cport_id, hd->cport_id_map)) {
316                 if (hd->cport_id_count) {
317                         hd->cport_id_count--;
318                         if (cport_id < hd->cport_id_next_free)
319                                 hd->cport_id_next_free = cport_id;
320                 } else {
321                         pr_err("bad cport_id_count in free");
322                 }
323         } else {
324                 pr_err("duplicate cport_id %hu free\n", cport_id);
325         }
326         spin_unlock_irq(&cport_id_map_lock);
327 }
328
329 static void free_hd(struct kref *kref)
330 {
331         struct greybus_host_device *hd;
332
333         hd = container_of(kref, struct greybus_host_device, kref);
334
335         kfree(hd);
336 }
337
338 struct greybus_host_device *greybus_create_hd(struct greybus_host_driver *driver,
339                                               struct device *parent)
340 {
341         struct greybus_host_device *hd;
342
343         hd = kzalloc(sizeof(*hd) + driver->hd_priv_size, GFP_KERNEL);
344         if (!hd)
345                 return NULL;
346
347         kref_init(&hd->kref);
348         hd->parent = parent;
349         hd->driver = driver;
350         INIT_LIST_HEAD(&hd->modules);
351
352         /* Pre-allocate CPort 0 for control stuff. XXX */
353         if (greybus_hd_cport_id_alloc(hd) != 0) {
354                 pr_err("couldn't allocate cport 0\n");
355                 kfree(hd);
356                 return NULL;
357         }
358
359         return hd;
360 }
361 EXPORT_SYMBOL_GPL(greybus_create_hd);
362
363 void greybus_remove_hd(struct greybus_host_device *hd)
364 {
365         kref_put_mutex(&hd->kref, free_hd, &hd_mutex);
366 }
367 EXPORT_SYMBOL_GPL(greybus_remove_hd);
368
369
370 static int __init gb_init(void)
371 {
372         int retval;
373
374         BUILD_BUG_ON(HOST_DEV_CPORT_ID_MAX >= (long)CPORT_ID_BAD);
375         spin_lock_init(&cport_id_map_lock);
376
377         retval = gb_debugfs_init();
378         if (retval) {
379                 pr_err("debugfs failed\n");
380                 return retval;
381         }
382
383         retval = bus_register(&greybus_bus_type);
384         if (retval) {
385                 pr_err("bus_register failed\n");
386                 goto error_bus;
387         }
388
389         retval = gb_ap_init();
390         if (retval) {
391                 pr_err("gb_ap_init failed\n");
392                 goto error_ap;
393         }
394
395         retval = gb_gbuf_init();
396         if (retval) {
397                 pr_err("gb_gbuf_init failed\n");
398                 goto error_gbuf;
399         }
400
401         retval = gb_tty_init();
402         if (retval) {
403                 pr_err("gb_tty_init failed\n");
404                 goto error_tty;
405         }
406
407         return 0;
408
409 error_tty:
410         gb_gbuf_exit();
411
412 error_gbuf:
413         gb_ap_exit();
414
415 error_ap:
416         bus_unregister(&greybus_bus_type);
417
418 error_bus:
419         gb_debugfs_cleanup();
420
421         return retval;
422 }
423
424 static void __exit gb_exit(void)
425 {
426         gb_tty_exit();
427         gb_gbuf_exit();
428         gb_ap_exit();
429         bus_unregister(&greybus_bus_type);
430         gb_debugfs_cleanup();
431 }
432
433 module_init(gb_init);
434 module_exit(gb_exit);
435
436 MODULE_LICENSE("GPL");
437 MODULE_AUTHOR("Greg Kroah-Hartman <gregkh@linuxfoundation.org>");