blob: 95655e258e84c6c0b54e31abf12618510e362568 [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-Hartman33ea3a32014-09-07 15:39:34 -0700213
214 retval = gb_battery_probe(gdev, id);
215 if (retval)
216 goto error_battery;
Greg Kroah-Hartman199d68d2014-08-30 16:20:22 -0700217 return 0;
Greg Kroah-Hartmandb6e1fd2014-08-30 16:47:26 -0700218
Greg Kroah-Hartman33ea3a32014-09-07 15:39:34 -0700219error_battery:
220 gb_tty_disconnect(gdev);
221
Greg Kroah-Hartmandb6e1fd2014-08-30 16:47:26 -0700222error_tty:
223 gb_sdio_disconnect(gdev);
224
225error_sdio:
226 gb_gpio_disconnect(gdev);
227
228error_gpio:
229 gb_i2c_disconnect(gdev);
230
231error_i2c:
232 return retval;
Greg Kroah-Hartman199d68d2014-08-30 16:20:22 -0700233}
234
Greg Kroah-Hartman3be03d42014-09-01 19:10:06 -0700235static const struct greybus_module_id fake_gb_id = {
236 GREYBUS_DEVICE(0x42, 0x42)
237};
Greg Kroah-Hartmana239f672014-09-01 14:39:49 -0700238
Greg Kroah-Hartman526c5c82014-09-01 16:03:31 -0700239static int create_function(struct greybus_device *gdev,
240 struct greybus_descriptor *desc, int desc_size)
241{
242 int header_size = sizeof(struct greybus_descriptor_function);
243
244 if (desc_size != header_size) {
Greg Kroah-Hartmanb94295e2014-09-01 18:34:28 -0700245 dev_err(gdev->dev.parent, "invalid function header size %d\n",
246 desc_size);
Greg Kroah-Hartman526c5c82014-09-01 16:03:31 -0700247 return -EINVAL;
248 }
249 memcpy(&gdev->function, &desc->function, header_size);
250 return 0;
251}
252
253static int create_module_id(struct greybus_device *gdev,
254 struct greybus_descriptor *desc, int desc_size)
255{
256 int header_size = sizeof(struct greybus_descriptor_module_id);
257
258 if (desc_size != header_size) {
Greg Kroah-Hartmanb94295e2014-09-01 18:34:28 -0700259 dev_err(gdev->dev.parent, "invalid module header size %d\n",
260 desc_size);
Greg Kroah-Hartman526c5c82014-09-01 16:03:31 -0700261 return -EINVAL;
262 }
263 memcpy(&gdev->module_id, &desc->module_id, header_size);
264 return 0;
265}
266
267static int create_serial_number(struct greybus_device *gdev,
268 struct greybus_descriptor *desc, int desc_size)
269{
270 int header_size = sizeof(struct greybus_descriptor_serial_number);
271
272 if (desc_size != header_size) {
Greg Kroah-Hartmanb94295e2014-09-01 18:34:28 -0700273 dev_err(gdev->dev.parent, "invalid serial number header size %d\n",
274 desc_size);
Greg Kroah-Hartman526c5c82014-09-01 16:03:31 -0700275 return -EINVAL;
276 }
277 memcpy(&gdev->serial_number, &desc->serial_number, header_size);
278 return 0;
279}
280
281static int create_string(struct greybus_device *gdev,
282 struct greybus_descriptor *desc, int desc_size)
283{
284 int string_size;
285 struct gdev_string *string;
286 int header_size = sizeof(struct greybus_descriptor_string);
287
288 if ((gdev->num_strings + 1) >= MAX_STRINGS_PER_MODULE) {
Greg Kroah-Hartmanb94295e2014-09-01 18:34:28 -0700289 dev_err(gdev->dev.parent,
290 "too many strings for this module!\n");
Greg Kroah-Hartman526c5c82014-09-01 16:03:31 -0700291 return -EINVAL;
292 }
293
294 if (desc_size < header_size) {
Greg Kroah-Hartmanb94295e2014-09-01 18:34:28 -0700295 dev_err(gdev->dev.parent, "invalid string header size %d\n",
296 desc_size);
Greg Kroah-Hartman526c5c82014-09-01 16:03:31 -0700297 return -EINVAL;
298 }
299
300 string_size = le16_to_cpu(desc->string.length);
301 string = kzalloc(sizeof(*string) + string_size + 1, GFP_KERNEL);
302 if (!string)
303 return -ENOMEM;
304
305 string->length = string_size;
306 string->id = desc->string.id;
307 memcpy(&string->string, &desc->string.string, string_size);
Greg Kroah-Hartmanb94295e2014-09-01 18:34:28 -0700308
Greg Kroah-Hartman526c5c82014-09-01 16:03:31 -0700309 gdev->string[gdev->num_strings] = string;
310 gdev->num_strings++;
311
312 return 0;
313}
314
Greg Kroah-Hartmanb94295e2014-09-01 18:34:28 -0700315static int create_cport(struct greybus_device *gdev,
316 struct greybus_descriptor *desc, int desc_size)
317{
318 struct gdev_cport *cport;
319 int header_size = sizeof(struct greybus_descriptor_cport);
320
321 if ((gdev->num_cports + 1) >= MAX_CPORTS_PER_MODULE) {
322 dev_err(gdev->dev.parent, "too many cports for this module!\n");
323 return -EINVAL;
324 }
325
326 if (desc_size != header_size) {
327 dev_err(gdev->dev.parent,
328 "invalid serial number header size %d\n", desc_size);
329 return -EINVAL;
330 }
331
332 cport = kzalloc(sizeof(*cport), GFP_KERNEL);
333 if (!cport)
334 return -ENOMEM;
335
336 cport->number = le16_to_cpu(desc->cport.number);
337 cport->size = le16_to_cpu(desc->cport.size);
338 cport->speed = desc->cport.speed;
339
340 gdev->cport[gdev->num_cports] = cport;
341 gdev->num_cports++;
342
343 return 0;
344}
345
Greg Kroah-Hartmana239f672014-09-01 14:39:49 -0700346/**
Alex Elder05ad1892014-09-09 13:55:03 -0500347 * greybus_new_module:
Greg Kroah-Hartmana239f672014-09-01 14:39:49 -0700348 *
Alex Elder05ad1892014-09-09 13:55:03 -0500349 * Pass in a buffer that _should_ contain a Greybus module manifest
350 * and spit out a greybus device structure.
Greg Kroah-Hartmana239f672014-09-01 14:39:49 -0700351 */
Alex Elder05ad1892014-09-09 13:55:03 -0500352struct greybus_device *greybus_new_module(struct device *parent,
Greg Kroah-Hartmanb94295e2014-09-01 18:34:28 -0700353 int module_number, u8 *data, int size)
Greg Kroah-Hartmana239f672014-09-01 14:39:49 -0700354{
355 struct greybus_device *gdev;
Alex Elderbadad682014-09-09 13:55:04 -0500356 struct greybus_manifest *manifest;
Greg Kroah-Hartmana239f672014-09-01 14:39:49 -0700357 struct greybus_descriptor *desc;
358 int retval;
359 int overall_size;
Greg Kroah-Hartmana239f672014-09-01 14:39:49 -0700360 int desc_size;
361 u8 version_major;
362 u8 version_minor;
363
Alex Elder05ad1892014-09-09 13:55:03 -0500364 /* we have to have at _least_ the manifest header */
Alex Elderbadad682014-09-09 13:55:04 -0500365 if (size <= sizeof(manifest->header))
Greg Kroah-Hartmana239f672014-09-01 14:39:49 -0700366 return NULL;
367
368 gdev = kzalloc(sizeof(*gdev), GFP_KERNEL);
369 if (!gdev)
370 return NULL;
371
372 gdev->module_number = module_number;
Greg Kroah-Hartmanb94295e2014-09-01 18:34:28 -0700373 gdev->dev.parent = parent;
374 gdev->dev.driver = NULL;
375 gdev->dev.bus = &greybus_bus_type;
376 gdev->dev.type = &greybus_module_type;
377 gdev->dev.groups = greybus_module_groups;
378 gdev->dev.dma_mask = parent->dma_mask;
379 device_initialize(&gdev->dev);
380 dev_set_name(&gdev->dev, "%d", module_number);
Greg Kroah-Hartmana239f672014-09-01 14:39:49 -0700381
Alex Elderbadad682014-09-09 13:55:04 -0500382 manifest = (struct greybus_manifest *)data;
383 overall_size = le16_to_cpu(manifest->header.size);
Greg Kroah-Hartmana239f672014-09-01 14:39:49 -0700384 if (overall_size != size) {
Alex Elder05ad1892014-09-09 13:55:03 -0500385 dev_err(parent, "size != manifest header size, %d != %d\n",
386 size, overall_size);
Greg Kroah-Hartmana239f672014-09-01 14:39:49 -0700387 goto error;
388 }
389
Alex Elderbadad682014-09-09 13:55:04 -0500390 version_major = manifest->header.version_major;
391 version_minor = manifest->header.version_minor;
Greg Kroah-Hartmana239f672014-09-01 14:39:49 -0700392
393 // FIXME - check version major/minor here!
394
Alex Elderbadad682014-09-09 13:55:04 -0500395 size -= sizeof(manifest->header);
396 data += sizeof(manifest->header);
Greg Kroah-Hartmana239f672014-09-01 14:39:49 -0700397 while (size > 0) {
398 desc = (struct greybus_descriptor *)data;
399 desc_size = le16_to_cpu(desc->header.size);
400
401 switch (desc->header.type) {
402 case GREYBUS_TYPE_FUNCTION:
Greg Kroah-Hartman526c5c82014-09-01 16:03:31 -0700403 retval = create_function(gdev, desc, desc_size);
Greg Kroah-Hartmana239f672014-09-01 14:39:49 -0700404 break;
405
406 case GREYBUS_TYPE_MODULE_ID:
Greg Kroah-Hartman526c5c82014-09-01 16:03:31 -0700407 retval = create_module_id(gdev, desc, desc_size);
Greg Kroah-Hartmana239f672014-09-01 14:39:49 -0700408 break;
409
410 case GREYBUS_TYPE_SERIAL_NUMBER:
Greg Kroah-Hartman526c5c82014-09-01 16:03:31 -0700411 retval = create_serial_number(gdev, desc, desc_size);
412 break;
413
414 case GREYBUS_TYPE_STRING:
415 retval = create_string(gdev, desc, desc_size);
416 break;
417
Greg Kroah-Hartmanb94295e2014-09-01 18:34:28 -0700418 case GREYBUS_TYPE_CPORT:
419 retval = create_cport(gdev, desc, desc_size);
Greg Kroah-Hartman526c5c82014-09-01 16:03:31 -0700420 break;
Greg Kroah-Hartmanb94295e2014-09-01 18:34:28 -0700421
Greg Kroah-Hartmana239f672014-09-01 14:39:49 -0700422 case GREYBUS_TYPE_INVALID:
423 default:
Greg Kroah-Hartmanb94295e2014-09-01 18:34:28 -0700424 dev_err(parent, "invalid descriptor type %d\n",
425 desc->header.type);
Greg Kroah-Hartmana239f672014-09-01 14:39:49 -0700426 goto error;
427 }
Greg Kroah-Hartman526c5c82014-09-01 16:03:31 -0700428 if (retval)
429 goto error;
430 size -= desc_size;
431 data += desc_size;
Greg Kroah-Hartmana239f672014-09-01 14:39:49 -0700432 }
Greg Kroah-Hartman526c5c82014-09-01 16:03:31 -0700433
Greg Kroah-Hartmana239f672014-09-01 14:39:49 -0700434 retval = gb_init_subdevs(gdev, &fake_gb_id);
435 if (retval)
436 goto error;
Greg Kroah-Hartmanb94295e2014-09-01 18:34:28 -0700437
438 // FIXME device_add(&gdev->dev);
439
440
Greg Kroah-Hartmana239f672014-09-01 14:39:49 -0700441 return gdev;
442error:
Greg Kroah-Hartmanb94295e2014-09-01 18:34:28 -0700443 greybus_module_release(&gdev->dev);
Greg Kroah-Hartmana239f672014-09-01 14:39:49 -0700444 return NULL;
445}
446
Greg Kroah-Hartmanb94295e2014-09-01 18:34:28 -0700447void greybus_remove_device(struct greybus_device *gdev)
Greg Kroah-Hartmandb6e1fd2014-08-30 16:47:26 -0700448{
449 /* tear down all of the "sub device types" for this device */
450 gb_i2c_disconnect(gdev);
451 gb_gpio_disconnect(gdev);
452 gb_sdio_disconnect(gdev);
453 gb_tty_disconnect(gdev);
Greg Kroah-Hartman33ea3a32014-09-07 15:39:34 -0700454 gb_battery_disconnect(gdev);
Greg Kroah-Hartmanb94295e2014-09-01 18:34:28 -0700455
456 // FIXME - device_remove(&gdev->dev);
Greg Kroah-Hartmandb6e1fd2014-08-30 16:47:26 -0700457}
Greg Kroah-Hartman199d68d2014-08-30 16:20:22 -0700458
Greg Kroah-Hartman68f1fc42014-09-07 13:12:11 -0700459static DEFINE_MUTEX(hd_mutex);
460
461static void free_hd(struct kref *kref)
462{
463 struct greybus_host_device *hd;
464
465 hd = container_of(kref, struct greybus_host_device, kref);
466
467 kfree(hd);
468}
469
Greg Kroah-Hartmana39879f2014-09-06 16:57:36 -0700470struct greybus_host_device *greybus_create_hd(struct greybus_host_driver *driver,
471 struct device *parent)
472{
473 struct greybus_host_device *hd;
474
475 hd = kzalloc(sizeof(*hd) + driver->hd_priv_size, GFP_KERNEL);
476 if (!hd)
477 return NULL;
478
479 kref_init(&hd->kref);
480
481 return hd;
482}
483EXPORT_SYMBOL_GPL(greybus_create_hd);
484
Greg Kroah-Hartman68f1fc42014-09-07 13:12:11 -0700485void greybus_remove_hd(struct greybus_host_device *hd)
486{
487 kref_put_mutex(&hd->kref, free_hd, &hd_mutex);
488}
489EXPORT_SYMBOL_GPL(greybus_remove_hd);
490
Greg Kroah-Hartmana39879f2014-09-06 16:57:36 -0700491
Greg Kroah-Hartman503c1cd2014-08-30 16:21:03 -0700492static int __init gb_init(void)
Greg Kroah-Hartman199d68d2014-08-30 16:20:22 -0700493{
Greg Kroah-Hartmandb6e1fd2014-08-30 16:47:26 -0700494 int retval;
495
Greg Kroah-Hartmande536e32014-08-31 16:17:04 -0700496 retval = gb_debugfs_init();
Greg Kroah-Hartmandb6e1fd2014-08-30 16:47:26 -0700497 if (retval)
498 return retval;
499
Greg Kroah-Hartman27fb8312014-08-31 13:54:59 -0700500 retval = bus_register(&greybus_bus_type);
501 if (retval)
502 goto error_bus;
503
Greg Kroah-Hartmande536e32014-08-31 16:17:04 -0700504 retval = gb_thread_init();
505 if (retval)
506 goto error_thread;
507
Greg Kroah-Hartman27fb8312014-08-31 13:54:59 -0700508 // FIXME - more gb core init goes here
509
510 retval = gb_tty_init();
511 if (retval)
512 goto error_tty;
513
Greg Kroah-Hartman199d68d2014-08-30 16:20:22 -0700514 return 0;
Greg Kroah-Hartman27fb8312014-08-31 13:54:59 -0700515
516error_tty:
Greg Kroah-Hartmande536e32014-08-31 16:17:04 -0700517 gb_thread_destroy();
518
519error_thread:
Greg Kroah-Hartman27fb8312014-08-31 13:54:59 -0700520 bus_unregister(&greybus_bus_type);
521
522error_bus:
Greg Kroah-Hartmande536e32014-08-31 16:17:04 -0700523 gb_debugfs_cleanup();
Greg Kroah-Hartman27fb8312014-08-31 13:54:59 -0700524
525 return retval;
Greg Kroah-Hartman199d68d2014-08-30 16:20:22 -0700526}
527
Greg Kroah-Hartman503c1cd2014-08-30 16:21:03 -0700528static void __exit gb_exit(void)
Greg Kroah-Hartman199d68d2014-08-30 16:20:22 -0700529{
Greg Kroah-Hartmandb6e1fd2014-08-30 16:47:26 -0700530 gb_tty_exit();
Greg Kroah-Hartman27fb8312014-08-31 13:54:59 -0700531 bus_unregister(&greybus_bus_type);
Greg Kroah-Hartmande536e32014-08-31 16:17:04 -0700532 gb_debugfs_cleanup();
Greg Kroah-Hartman199d68d2014-08-30 16:20:22 -0700533}
534
535module_init(gb_init);
536module_exit(gb_exit);
537
Greg Kroah-Hartmanc8a797a2014-08-11 15:30:45 +0800538MODULE_LICENSE("GPL");
539MODULE_AUTHOR("Greg Kroah-Hartman <gregkh@linuxfoundation.org>");