blob: 5c633a3945c14e30389bf45c64dd27c71f1e330b [file] [log] [blame]
Greg Kroah-Hartmanc8a797a2014-08-11 15:30:45 +08001/*
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>
Greg Kroah-Hartmana239f672014-09-01 14:39:49 -070015#include <linux/slab.h>
Greg Kroah-Hartmanc8a797a2014-08-11 15:30:45 +080016#include <linux/device.h>
17
18#include "greybus.h"
19
20/* Allow greybus to be disabled at boot if needed */
21static bool nogreybus;
22#ifdef MODULE
23module_param(nogreybus, bool, 0444);
24#else
25core_param(nogreybus, bool, 0444);
26#endif
27int greybus_disabled(void)
28{
29 return nogreybus;
30}
31EXPORT_SYMBOL_GPL(greybus_disabled);
32
33static int greybus_match_one_id(struct greybus_device *gdev,
Greg Kroah-Hartman6584c8a2014-09-01 13:31:31 -070034 const struct greybus_module_id *id)
Greg Kroah-Hartmanc8a797a2014-08-11 15:30:45 +080035{
Greg Kroah-Hartman6dca7b92014-09-01 13:42:43 -070036 struct greybus_descriptor_module_id *module_id;
37 struct greybus_descriptor_serial_number *serial_num;
38
39 module_id = &gdev->module_id;
40 serial_num = &gdev->serial_number;
Greg Kroah-Hartmanc8a797a2014-08-11 15:30:45 +080041
42 if ((id->match_flags & GREYBUS_DEVICE_ID_MATCH_VENDOR) &&
Greg Kroah-Hartman6dca7b92014-09-01 13:42:43 -070043 (id->vendor != le16_to_cpu(module_id->vendor)))
Greg Kroah-Hartmanc8a797a2014-08-11 15:30:45 +080044 return 0;
45
46 if ((id->match_flags & GREYBUS_DEVICE_ID_MATCH_PRODUCT) &&
Greg Kroah-Hartman6dca7b92014-09-01 13:42:43 -070047 (id->product != le16_to_cpu(module_id->product)))
Greg Kroah-Hartmanc8a797a2014-08-11 15:30:45 +080048 return 0;
49
50 if ((id->match_flags & GREYBUS_DEVICE_ID_MATCH_SERIAL) &&
Greg Kroah-Hartman6dca7b92014-09-01 13:42:43 -070051 (id->serial_number != le64_to_cpu(serial_num->serial_number)))
Greg Kroah-Hartmanc8a797a2014-08-11 15:30:45 +080052 return 0;
53
54 return 1;
55}
56
Greg Kroah-Hartman6584c8a2014-09-01 13:31:31 -070057static const struct greybus_module_id *greybus_match_id(
Greg Kroah-Hartmanc8a797a2014-08-11 15:30:45 +080058 struct greybus_device *gdev,
Greg Kroah-Hartman6584c8a2014-09-01 13:31:31 -070059 const struct greybus_module_id *id)
Greg Kroah-Hartmanc8a797a2014-08-11 15:30:45 +080060{
61 if (id == NULL)
62 return NULL;
63
Greg Kroah-Hartman6dca7b92014-09-01 13:42:43 -070064 for (; id->vendor || id->product || id->serial_number ||
Greg Kroah-Hartmanc8a797a2014-08-11 15:30:45 +080065 id->driver_info ; id++) {
66 if (greybus_match_one_id(gdev, id))
67 return id;
68 }
69
70 return NULL;
71}
72
73static int greybus_device_match(struct device *dev, struct device_driver *drv)
74{
75 struct greybus_driver *driver = to_greybus_driver(dev->driver);
76 struct greybus_device *gdev = to_greybus_device(dev);
Greg Kroah-Hartman6584c8a2014-09-01 13:31:31 -070077 const struct greybus_module_id *id;
Greg Kroah-Hartmanc8a797a2014-08-11 15:30:45 +080078
79 id = greybus_match_id(gdev, driver->id_table);
80 if (id)
81 return 1;
82 /* FIXME - Dyanmic ids? */
83 return 0;
84}
85
86static int greybus_uevent(struct device *dev, struct kobj_uevent_env *env)
87{
88 /* struct greybus_device *gdev = to_greybus_device(dev); */
89
90 /* FIXME - add some uevents here... */
91 return 0;
92}
93
Greg Kroah-Hartman27fb8312014-08-31 13:54:59 -070094static struct bus_type greybus_bus_type = {
Greg Kroah-Hartmanc8a797a2014-08-11 15:30:45 +080095 .name = "greybus",
96 .match = greybus_device_match,
97 .uevent = greybus_uevent,
98};
99
100static int greybus_probe(struct device *dev)
101{
102 struct greybus_driver *driver = to_greybus_driver(dev->driver);
103 struct greybus_device *gdev = to_greybus_device(dev);
Greg Kroah-Hartman6584c8a2014-09-01 13:31:31 -0700104 const struct greybus_module_id *id;
Greg Kroah-Hartmanc8a797a2014-08-11 15:30:45 +0800105 int retval;
106
107 /* match id */
108 id = greybus_match_id(gdev, driver->id_table);
109 if (!id)
110 return -ENODEV;
111
112 retval = driver->probe(gdev, id);
113 if (retval)
114 return retval;
115
116 return 0;
117}
118
119static int greybus_remove(struct device *dev)
120{
121 struct greybus_driver *driver = to_greybus_driver(dev->driver);
122 struct greybus_device *gdev = to_greybus_device(dev);
123
124 driver->disconnect(gdev);
125 return 0;
126}
127
128int greybus_register_driver(struct greybus_driver *driver, struct module *owner,
129 const char *mod_name)
130{
131 int retval;
132
133 if (greybus_disabled())
134 return -ENODEV;
135
136 driver->driver.name = driver->name;
137 driver->driver.probe = greybus_probe;
138 driver->driver.remove = greybus_remove;
139 driver->driver.owner = owner;
140 driver->driver.mod_name = mod_name;
141
142 retval = driver_register(&driver->driver);
143 if (retval)
144 return retval;
145
146 pr_info("registered new driver %s\n", driver->name);
147 return 0;
148}
149EXPORT_SYMBOL_GPL(greybus_register_driver);
150
151void greybus_deregister(struct greybus_driver *driver)
152{
153 driver_unregister(&driver->driver);
154}
155EXPORT_SYMBOL_GPL(greybus_deregister);
156
Greg Kroah-Hartman199d68d2014-08-30 16:20:22 -0700157
Greg Kroah-Hartmanb94295e2014-09-01 18:34:28 -0700158static void greybus_module_release(struct device *dev)
159{
160 struct greybus_device *gdev = to_greybus_device(dev);
161 int i;
162
163 for (i = 0; i < gdev->num_strings; ++i)
164 kfree(gdev->string[i]);
165 for (i = 0; i < gdev->num_cports; ++i)
166 kfree(gdev->cport[i]);
167 kfree(gdev);
168}
169
170
Greg Kroah-Hartmane24e7252014-09-01 19:01:14 -0700171const u8 *greybus_string(struct greybus_device *gdev, int id)
172{
173 int i;
174 struct gdev_string *string;
175
176 if (!gdev)
177 return NULL;
178
179 for (i = 0; i < gdev->num_strings; ++i) {
180 string = gdev->string[i];
181 if (string->id == id)
182 return &string->string[0];
183 }
184 return NULL;
185}
186
Greg Kroah-Hartmanb94295e2014-09-01 18:34:28 -0700187static struct device_type greybus_module_type = {
188 .name = "greybus_module",
189 .release = greybus_module_release,
190};
191
Greg Kroah-Hartmana239f672014-09-01 14:39:49 -0700192static int gb_init_subdevs(struct greybus_device *gdev,
193 const struct greybus_module_id *id)
Greg Kroah-Hartman199d68d2014-08-30 16:20:22 -0700194{
195 int retval;
196
197 /* Allocate all of the different "sub device types" for this device */
198 retval = gb_i2c_probe(gdev, id);
Greg Kroah-Hartmandb6e1fd2014-08-30 16:47:26 -0700199 if (retval)
200 goto error_i2c;
201
202 retval = gb_gpio_probe(gdev, id);
203 if (retval)
204 goto error_gpio;
205
206 retval = gb_sdio_probe(gdev, id);
207 if (retval)
208 goto error_sdio;
209
210 retval = gb_tty_probe(gdev, id);
211 if (retval)
212 goto error_tty;
Greg Kroah-Hartman199d68d2014-08-30 16:20:22 -0700213 return 0;
Greg Kroah-Hartmandb6e1fd2014-08-30 16:47:26 -0700214
215error_tty:
216 gb_sdio_disconnect(gdev);
217
218error_sdio:
219 gb_gpio_disconnect(gdev);
220
221error_gpio:
222 gb_i2c_disconnect(gdev);
223
224error_i2c:
225 return retval;
Greg Kroah-Hartman199d68d2014-08-30 16:20:22 -0700226}
227
Greg Kroah-Hartman3be03d42014-09-01 19:10:06 -0700228static const struct greybus_module_id fake_gb_id = {
229 GREYBUS_DEVICE(0x42, 0x42)
230};
Greg Kroah-Hartmana239f672014-09-01 14:39:49 -0700231
Greg Kroah-Hartman526c5c82014-09-01 16:03:31 -0700232static int create_function(struct greybus_device *gdev,
233 struct greybus_descriptor *desc, int desc_size)
234{
235 int header_size = sizeof(struct greybus_descriptor_function);
236
237 if (desc_size != header_size) {
Greg Kroah-Hartmanb94295e2014-09-01 18:34:28 -0700238 dev_err(gdev->dev.parent, "invalid function header size %d\n",
239 desc_size);
Greg Kroah-Hartman526c5c82014-09-01 16:03:31 -0700240 return -EINVAL;
241 }
242 memcpy(&gdev->function, &desc->function, header_size);
243 return 0;
244}
245
246static int create_module_id(struct greybus_device *gdev,
247 struct greybus_descriptor *desc, int desc_size)
248{
249 int header_size = sizeof(struct greybus_descriptor_module_id);
250
251 if (desc_size != header_size) {
Greg Kroah-Hartmanb94295e2014-09-01 18:34:28 -0700252 dev_err(gdev->dev.parent, "invalid module header size %d\n",
253 desc_size);
Greg Kroah-Hartman526c5c82014-09-01 16:03:31 -0700254 return -EINVAL;
255 }
256 memcpy(&gdev->module_id, &desc->module_id, header_size);
257 return 0;
258}
259
260static int create_serial_number(struct greybus_device *gdev,
261 struct greybus_descriptor *desc, int desc_size)
262{
263 int header_size = sizeof(struct greybus_descriptor_serial_number);
264
265 if (desc_size != header_size) {
Greg Kroah-Hartmanb94295e2014-09-01 18:34:28 -0700266 dev_err(gdev->dev.parent, "invalid serial number header size %d\n",
267 desc_size);
Greg Kroah-Hartman526c5c82014-09-01 16:03:31 -0700268 return -EINVAL;
269 }
270 memcpy(&gdev->serial_number, &desc->serial_number, header_size);
271 return 0;
272}
273
274static int create_string(struct greybus_device *gdev,
275 struct greybus_descriptor *desc, int desc_size)
276{
277 int string_size;
278 struct gdev_string *string;
279 int header_size = sizeof(struct greybus_descriptor_string);
280
281 if ((gdev->num_strings + 1) >= MAX_STRINGS_PER_MODULE) {
Greg Kroah-Hartmanb94295e2014-09-01 18:34:28 -0700282 dev_err(gdev->dev.parent,
283 "too many strings for this module!\n");
Greg Kroah-Hartman526c5c82014-09-01 16:03:31 -0700284 return -EINVAL;
285 }
286
287 if (desc_size < header_size) {
Greg Kroah-Hartmanb94295e2014-09-01 18:34:28 -0700288 dev_err(gdev->dev.parent, "invalid string header size %d\n",
289 desc_size);
Greg Kroah-Hartman526c5c82014-09-01 16:03:31 -0700290 return -EINVAL;
291 }
292
293 string_size = le16_to_cpu(desc->string.length);
294 string = kzalloc(sizeof(*string) + string_size + 1, GFP_KERNEL);
295 if (!string)
296 return -ENOMEM;
297
298 string->length = string_size;
299 string->id = desc->string.id;
300 memcpy(&string->string, &desc->string.string, string_size);
Greg Kroah-Hartmanb94295e2014-09-01 18:34:28 -0700301
Greg Kroah-Hartman526c5c82014-09-01 16:03:31 -0700302 gdev->string[gdev->num_strings] = string;
303 gdev->num_strings++;
304
305 return 0;
306}
307
Greg Kroah-Hartmanb94295e2014-09-01 18:34:28 -0700308static int create_cport(struct greybus_device *gdev,
309 struct greybus_descriptor *desc, int desc_size)
310{
311 struct gdev_cport *cport;
312 int header_size = sizeof(struct greybus_descriptor_cport);
313
314 if ((gdev->num_cports + 1) >= MAX_CPORTS_PER_MODULE) {
315 dev_err(gdev->dev.parent, "too many cports for this module!\n");
316 return -EINVAL;
317 }
318
319 if (desc_size != header_size) {
320 dev_err(gdev->dev.parent,
321 "invalid serial number header size %d\n", desc_size);
322 return -EINVAL;
323 }
324
325 cport = kzalloc(sizeof(*cport), GFP_KERNEL);
326 if (!cport)
327 return -ENOMEM;
328
329 cport->number = le16_to_cpu(desc->cport.number);
330 cport->size = le16_to_cpu(desc->cport.size);
331 cport->speed = desc->cport.speed;
332
333 gdev->cport[gdev->num_cports] = cport;
334 gdev->num_cports++;
335
336 return 0;
337}
338
Greg Kroah-Hartmana239f672014-09-01 14:39:49 -0700339/**
340 * greybus_new_device:
341 *
342 * Pass in a buffer that _should_ be a set of greybus descriptor fields and spit
343 * out a greybus device structure.
344 */
Greg Kroah-Hartmanb94295e2014-09-01 18:34:28 -0700345struct greybus_device *greybus_new_device(struct device *parent,
346 int module_number, u8 *data, int size)
Greg Kroah-Hartmana239f672014-09-01 14:39:49 -0700347{
348 struct greybus_device *gdev;
349 struct greybus_descriptor_block_header *block;
350 struct greybus_descriptor *desc;
351 int retval;
352 int overall_size;
Greg Kroah-Hartmana239f672014-09-01 14:39:49 -0700353 int desc_size;
354 u8 version_major;
355 u8 version_minor;
356
357 /* we have to have at _least_ the block header */
358 if (size <= sizeof(struct greybus_descriptor_block_header))
359 return NULL;
360
361 gdev = kzalloc(sizeof(*gdev), GFP_KERNEL);
362 if (!gdev)
363 return NULL;
364
365 gdev->module_number = module_number;
Greg Kroah-Hartmanb94295e2014-09-01 18:34:28 -0700366 gdev->dev.parent = parent;
367 gdev->dev.driver = NULL;
368 gdev->dev.bus = &greybus_bus_type;
369 gdev->dev.type = &greybus_module_type;
370 gdev->dev.groups = greybus_module_groups;
371 gdev->dev.dma_mask = parent->dma_mask;
372 device_initialize(&gdev->dev);
373 dev_set_name(&gdev->dev, "%d", module_number);
Greg Kroah-Hartmana239f672014-09-01 14:39:49 -0700374
375 block = (struct greybus_descriptor_block_header *)data;
376 overall_size = le16_to_cpu(block->size);
377 if (overall_size != size) {
Greg Kroah-Hartmanb94295e2014-09-01 18:34:28 -0700378 dev_err(parent, "size != block header size, %d != %d\n", size,
379 overall_size);
Greg Kroah-Hartmana239f672014-09-01 14:39:49 -0700380 goto error;
381 }
382
383 version_major = block->version_major;
384 version_minor = block->version_minor;
385
386 // FIXME - check version major/minor here!
387
388 size -= sizeof(struct greybus_descriptor_block_header);
389 data += sizeof(struct greybus_descriptor_block_header);
390 while (size > 0) {
391 desc = (struct greybus_descriptor *)data;
392 desc_size = le16_to_cpu(desc->header.size);
393
394 switch (desc->header.type) {
395 case GREYBUS_TYPE_FUNCTION:
Greg Kroah-Hartman526c5c82014-09-01 16:03:31 -0700396 retval = create_function(gdev, desc, desc_size);
Greg Kroah-Hartmana239f672014-09-01 14:39:49 -0700397 break;
398
399 case GREYBUS_TYPE_MODULE_ID:
Greg Kroah-Hartman526c5c82014-09-01 16:03:31 -0700400 retval = create_module_id(gdev, desc, desc_size);
Greg Kroah-Hartmana239f672014-09-01 14:39:49 -0700401 break;
402
403 case GREYBUS_TYPE_SERIAL_NUMBER:
Greg Kroah-Hartman526c5c82014-09-01 16:03:31 -0700404 retval = create_serial_number(gdev, desc, desc_size);
405 break;
406
407 case GREYBUS_TYPE_STRING:
408 retval = create_string(gdev, desc, desc_size);
409 break;
410
Greg Kroah-Hartmanb94295e2014-09-01 18:34:28 -0700411 case GREYBUS_TYPE_CPORT:
412 retval = create_cport(gdev, desc, desc_size);
Greg Kroah-Hartman526c5c82014-09-01 16:03:31 -0700413 break;
Greg Kroah-Hartmanb94295e2014-09-01 18:34:28 -0700414
Greg Kroah-Hartmana239f672014-09-01 14:39:49 -0700415 case GREYBUS_TYPE_INVALID:
416 default:
Greg Kroah-Hartmanb94295e2014-09-01 18:34:28 -0700417 dev_err(parent, "invalid descriptor type %d\n",
418 desc->header.type);
Greg Kroah-Hartmana239f672014-09-01 14:39:49 -0700419 goto error;
420 }
Greg Kroah-Hartman526c5c82014-09-01 16:03:31 -0700421 if (retval)
422 goto error;
423 size -= desc_size;
424 data += desc_size;
Greg Kroah-Hartmana239f672014-09-01 14:39:49 -0700425 }
Greg Kroah-Hartman526c5c82014-09-01 16:03:31 -0700426
Greg Kroah-Hartmana239f672014-09-01 14:39:49 -0700427 retval = gb_init_subdevs(gdev, &fake_gb_id);
428 if (retval)
429 goto error;
Greg Kroah-Hartmanb94295e2014-09-01 18:34:28 -0700430
431 // FIXME device_add(&gdev->dev);
432
433
Greg Kroah-Hartmana239f672014-09-01 14:39:49 -0700434 return gdev;
435error:
Greg Kroah-Hartmanb94295e2014-09-01 18:34:28 -0700436 greybus_module_release(&gdev->dev);
Greg Kroah-Hartmana239f672014-09-01 14:39:49 -0700437 return NULL;
438}
439
Greg Kroah-Hartmanb94295e2014-09-01 18:34:28 -0700440void greybus_remove_device(struct greybus_device *gdev)
Greg Kroah-Hartmandb6e1fd2014-08-30 16:47:26 -0700441{
442 /* tear down all of the "sub device types" for this device */
443 gb_i2c_disconnect(gdev);
444 gb_gpio_disconnect(gdev);
445 gb_sdio_disconnect(gdev);
446 gb_tty_disconnect(gdev);
Greg Kroah-Hartmanb94295e2014-09-01 18:34:28 -0700447
448 // FIXME - device_remove(&gdev->dev);
Greg Kroah-Hartmandb6e1fd2014-08-30 16:47:26 -0700449}
Greg Kroah-Hartman199d68d2014-08-30 16:20:22 -0700450
Greg Kroah-Hartman68f1fc42014-09-07 13:12:11 -0700451static DEFINE_MUTEX(hd_mutex);
452
453static void free_hd(struct kref *kref)
454{
455 struct greybus_host_device *hd;
456
457 hd = container_of(kref, struct greybus_host_device, kref);
458
459 kfree(hd);
460}
461
Greg Kroah-Hartmana39879f2014-09-06 16:57:36 -0700462struct greybus_host_device *greybus_create_hd(struct greybus_host_driver *driver,
463 struct device *parent)
464{
465 struct greybus_host_device *hd;
466
467 hd = kzalloc(sizeof(*hd) + driver->hd_priv_size, GFP_KERNEL);
468 if (!hd)
469 return NULL;
470
471 kref_init(&hd->kref);
472
473 return hd;
474}
475EXPORT_SYMBOL_GPL(greybus_create_hd);
476
Greg Kroah-Hartman68f1fc42014-09-07 13:12:11 -0700477void greybus_remove_hd(struct greybus_host_device *hd)
478{
479 kref_put_mutex(&hd->kref, free_hd, &hd_mutex);
480}
481EXPORT_SYMBOL_GPL(greybus_remove_hd);
482
Greg Kroah-Hartmana39879f2014-09-06 16:57:36 -0700483
Greg Kroah-Hartman503c1cd2014-08-30 16:21:03 -0700484static int __init gb_init(void)
Greg Kroah-Hartman199d68d2014-08-30 16:20:22 -0700485{
Greg Kroah-Hartmandb6e1fd2014-08-30 16:47:26 -0700486 int retval;
487
Greg Kroah-Hartmande536e32014-08-31 16:17:04 -0700488 retval = gb_debugfs_init();
Greg Kroah-Hartmandb6e1fd2014-08-30 16:47:26 -0700489 if (retval)
490 return retval;
491
Greg Kroah-Hartman27fb8312014-08-31 13:54:59 -0700492 retval = bus_register(&greybus_bus_type);
493 if (retval)
494 goto error_bus;
495
Greg Kroah-Hartmande536e32014-08-31 16:17:04 -0700496 retval = gb_thread_init();
497 if (retval)
498 goto error_thread;
499
Greg Kroah-Hartman27fb8312014-08-31 13:54:59 -0700500 // FIXME - more gb core init goes here
501
502 retval = gb_tty_init();
503 if (retval)
504 goto error_tty;
505
Greg Kroah-Hartman199d68d2014-08-30 16:20:22 -0700506 return 0;
Greg Kroah-Hartman27fb8312014-08-31 13:54:59 -0700507
508error_tty:
Greg Kroah-Hartmande536e32014-08-31 16:17:04 -0700509 gb_thread_destroy();
510
511error_thread:
Greg Kroah-Hartman27fb8312014-08-31 13:54:59 -0700512 bus_unregister(&greybus_bus_type);
513
514error_bus:
Greg Kroah-Hartmande536e32014-08-31 16:17:04 -0700515 gb_debugfs_cleanup();
Greg Kroah-Hartman27fb8312014-08-31 13:54:59 -0700516
517 return retval;
Greg Kroah-Hartman199d68d2014-08-30 16:20:22 -0700518}
519
Greg Kroah-Hartman503c1cd2014-08-30 16:21:03 -0700520static void __exit gb_exit(void)
Greg Kroah-Hartman199d68d2014-08-30 16:20:22 -0700521{
Greg Kroah-Hartmandb6e1fd2014-08-30 16:47:26 -0700522 gb_tty_exit();
Greg Kroah-Hartman27fb8312014-08-31 13:54:59 -0700523 bus_unregister(&greybus_bus_type);
Greg Kroah-Hartmande536e32014-08-31 16:17:04 -0700524 gb_debugfs_cleanup();
Greg Kroah-Hartman199d68d2014-08-30 16:20:22 -0700525}
526
527module_init(gb_init);
528module_exit(gb_exit);
529
Greg Kroah-Hartmanc8a797a2014-08-11 15:30:45 +0800530MODULE_LICENSE("GPL");
531MODULE_AUTHOR("Greg Kroah-Hartman <gregkh@linuxfoundation.org>");