blob: 67628719f8d1f83ac4bba7945846bbefecf0d66e [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
Alex Elder778c69c2014-09-22 19:19:03 -050033static int greybus_module_match(struct device *dev, struct device_driver *drv)
Greg Kroah-Hartmanc8a797a2014-08-11 15:30:45 +080034{
35 struct greybus_driver *driver = to_greybus_driver(dev->driver);
Alex Eldere1e9dbd2014-10-01 21:54:11 -050036 struct gb_module *gmod = to_gb_module(dev);
Greg Kroah-Hartman6584c8a2014-09-01 13:31:31 -070037 const struct greybus_module_id *id;
Greg Kroah-Hartmanc8a797a2014-08-11 15:30:45 +080038
Alex Eldere1e9dbd2014-10-01 21:54:11 -050039 id = gb_module_match_id(gmod, driver->id_table);
Greg Kroah-Hartmanc8a797a2014-08-11 15:30:45 +080040 if (id)
41 return 1;
42 /* FIXME - Dyanmic ids? */
43 return 0;
44}
45
46static int greybus_uevent(struct device *dev, struct kobj_uevent_env *env)
47{
Alex Eldere1e9dbd2014-10-01 21:54:11 -050048 /* struct gb_module *gmod = to_gb_module(dev); */
Greg Kroah-Hartmanc8a797a2014-08-11 15:30:45 +080049
50 /* FIXME - add some uevents here... */
51 return 0;
52}
53
Greg Kroah-Hartman27fb8312014-08-31 13:54:59 -070054static struct bus_type greybus_bus_type = {
Greg Kroah-Hartmanc8a797a2014-08-11 15:30:45 +080055 .name = "greybus",
Alex Elder778c69c2014-09-22 19:19:03 -050056 .match = greybus_module_match,
Greg Kroah-Hartmanc8a797a2014-08-11 15:30:45 +080057 .uevent = greybus_uevent,
58};
59
60static int greybus_probe(struct device *dev)
61{
62 struct greybus_driver *driver = to_greybus_driver(dev->driver);
Alex Eldere1e9dbd2014-10-01 21:54:11 -050063 struct gb_module *gmod = to_gb_module(dev);
Greg Kroah-Hartman6584c8a2014-09-01 13:31:31 -070064 const struct greybus_module_id *id;
Greg Kroah-Hartmanc8a797a2014-08-11 15:30:45 +080065 int retval;
66
67 /* match id */
Alex Eldere1e9dbd2014-10-01 21:54:11 -050068 id = gb_module_match_id(gmod, driver->id_table);
Greg Kroah-Hartmanc8a797a2014-08-11 15:30:45 +080069 if (!id)
70 return -ENODEV;
71
Alex Elder778c69c2014-09-22 19:19:03 -050072 retval = driver->probe(gmod, id);
Greg Kroah-Hartmanc8a797a2014-08-11 15:30:45 +080073 if (retval)
74 return retval;
75
76 return 0;
77}
78
79static int greybus_remove(struct device *dev)
80{
81 struct greybus_driver *driver = to_greybus_driver(dev->driver);
Alex Eldere1e9dbd2014-10-01 21:54:11 -050082 struct gb_module *gmod = to_gb_module(dev);
Greg Kroah-Hartmanc8a797a2014-08-11 15:30:45 +080083
Alex Elder778c69c2014-09-22 19:19:03 -050084 driver->disconnect(gmod);
Greg Kroah-Hartmanc8a797a2014-08-11 15:30:45 +080085 return 0;
86}
87
88int 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}
109EXPORT_SYMBOL_GPL(greybus_register_driver);
110
111void greybus_deregister(struct greybus_driver *driver)
112{
113 driver_unregister(&driver->driver);
114}
115EXPORT_SYMBOL_GPL(greybus_deregister);
116
Greg Kroah-Hartman199d68d2014-08-30 16:20:22 -0700117
Greg Kroah-Hartmanb94295e2014-09-01 18:34:28 -0700118static void greybus_module_release(struct device *dev)
119{
Alex Eldere1e9dbd2014-10-01 21:54:11 -0500120 struct gb_module *gmod = to_gb_module(dev);
Greg Kroah-Hartmanb94295e2014-09-01 18:34:28 -0700121 int i;
122
Alex Elder778c69c2014-09-22 19:19:03 -0500123 for (i = 0; i < gmod->num_strings; ++i)
124 kfree(gmod->string[i]);
Alex Elder778c69c2014-09-22 19:19:03 -0500125 kfree(gmod);
Greg Kroah-Hartmanb94295e2014-09-01 18:34:28 -0700126}
127
128
Greg Kroah-Hartmanb94295e2014-09-01 18:34:28 -0700129static struct device_type greybus_module_type = {
130 .name = "greybus_module",
131 .release = greybus_module_release,
132};
133
Alex Elderc68adb22014-10-01 21:54:14 -0500134/* XXX
135 * This needs to be driven by the list of functions that the
136 * manifest says are present.
137 */
Alex Eldere1e9dbd2014-10-01 21:54:11 -0500138static int gb_init_subdevs(struct gb_module *gmod,
Greg Kroah-Hartmana239f672014-09-01 14:39:49 -0700139 const struct greybus_module_id *id)
Greg Kroah-Hartman199d68d2014-08-30 16:20:22 -0700140{
141 int retval;
142
143 /* Allocate all of the different "sub device types" for this device */
Alex Elderc68adb22014-10-01 21:54:14 -0500144
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 */
Alex Elder778c69c2014-09-22 19:19:03 -0500150 retval = gb_i2c_probe(gmod, id);
Greg Kroah-Hartmandb6e1fd2014-08-30 16:47:26 -0700151 if (retval)
152 goto error_i2c;
153
Alex Elder778c69c2014-09-22 19:19:03 -0500154 retval = gb_gpio_probe(gmod, id);
Greg Kroah-Hartmandb6e1fd2014-08-30 16:47:26 -0700155 if (retval)
156 goto error_gpio;
157
Alex Elder778c69c2014-09-22 19:19:03 -0500158 retval = gb_sdio_probe(gmod, id);
Greg Kroah-Hartmandb6e1fd2014-08-30 16:47:26 -0700159 if (retval)
160 goto error_sdio;
161
Alex Elder778c69c2014-09-22 19:19:03 -0500162 retval = gb_tty_probe(gmod, id);
Greg Kroah-Hartmandb6e1fd2014-08-30 16:47:26 -0700163 if (retval)
164 goto error_tty;
Greg Kroah-Hartman33ea3a32014-09-07 15:39:34 -0700165
Alex Elder778c69c2014-09-22 19:19:03 -0500166 retval = gb_battery_probe(gmod, id);
Greg Kroah-Hartman33ea3a32014-09-07 15:39:34 -0700167 if (retval)
168 goto error_battery;
Greg Kroah-Hartman199d68d2014-08-30 16:20:22 -0700169 return 0;
Greg Kroah-Hartmandb6e1fd2014-08-30 16:47:26 -0700170
Greg Kroah-Hartman33ea3a32014-09-07 15:39:34 -0700171error_battery:
Alex Elder778c69c2014-09-22 19:19:03 -0500172 gb_tty_disconnect(gmod);
Greg Kroah-Hartman33ea3a32014-09-07 15:39:34 -0700173
Greg Kroah-Hartmandb6e1fd2014-08-30 16:47:26 -0700174error_tty:
Alex Elder778c69c2014-09-22 19:19:03 -0500175 gb_sdio_disconnect(gmod);
Greg Kroah-Hartmandb6e1fd2014-08-30 16:47:26 -0700176
177error_sdio:
Alex Elder778c69c2014-09-22 19:19:03 -0500178 gb_gpio_disconnect(gmod);
Greg Kroah-Hartmandb6e1fd2014-08-30 16:47:26 -0700179
180error_gpio:
Alex Elder778c69c2014-09-22 19:19:03 -0500181 gb_i2c_disconnect(gmod);
Greg Kroah-Hartmandb6e1fd2014-08-30 16:47:26 -0700182
183error_i2c:
184 return retval;
Greg Kroah-Hartman199d68d2014-08-30 16:20:22 -0700185}
186
Alex Eldere1e9dbd2014-10-01 21:54:11 -0500187static const struct greybus_module_id fake_greybus_module_id = {
Greg Kroah-Hartman3be03d42014-09-01 19:10:06 -0700188 GREYBUS_DEVICE(0x42, 0x42)
189};
Greg Kroah-Hartmana239f672014-09-01 14:39:49 -0700190
Greg Kroah-Hartmanb94295e2014-09-01 18:34:28 -0700191
Greg Kroah-Hartmana239f672014-09-01 14:39:49 -0700192/**
Greg Kroah-Hartman4a833fd2014-09-21 19:17:55 -0700193 * gb_add_module
Greg Kroah-Hartmana239f672014-09-01 14:39:49 -0700194 *
Alex Elder05ad1892014-09-09 13:55:03 -0500195 * Pass in a buffer that _should_ contain a Greybus module manifest
Greg Kroah-Hartman4a833fd2014-09-21 19:17:55 -0700196 * and register a greybus device structure with the kernel core.
Greg Kroah-Hartmana239f672014-09-01 14:39:49 -0700197 */
Greg Kroah-Hartmand0cfd102014-09-21 19:10:39 -0700198void gb_add_module(struct greybus_host_device *hd, u8 module_id,
199 u8 *data, int size)
Greg Kroah-Hartman67799972014-09-21 18:17:36 -0700200{
Alex Eldere1e9dbd2014-10-01 21:54:11 -0500201 struct gb_module *gmod;
Greg Kroah-Hartmana239f672014-09-01 14:39:49 -0700202 int retval;
Greg Kroah-Hartmana239f672014-09-01 14:39:49 -0700203
Alex Elder937d0da2014-10-03 14:14:25 -0500204 gmod = gb_module_create(hd, module_id);
205 if (!gmod) {
206 dev_err(hd->parent, "failed to create module\n");
207 return;
208 }
209
Alex Elderb09c94a2014-10-01 21:54:16 -0500210 /*
211 * Parse the manifest and build up our data structures
212 * representing what's in it.
213 */
Alex Elder937d0da2014-10-03 14:14:25 -0500214 if (!gb_manifest_parse(gmod, data, size)) {
Alex Elderb09c94a2014-10-01 21:54:16 -0500215 dev_err(hd->parent, "manifest error\n");
Alex Elder937d0da2014-10-03 14:14:25 -0500216 goto error;
Alex Elderb09c94a2014-10-01 21:54:16 -0500217 }
Greg Kroah-Hartmana239f672014-09-01 14:39:49 -0700218
Alex Elder459164b2014-10-01 21:54:19 -0500219 /*
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
Alex Elder778c69c2014-09-22 19:19:03 -0500228 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);
Greg Kroah-Hartmana239f672014-09-01 14:39:49 -0700236
Matt Porter32dff132014-10-03 13:38:24 -0400237 retval = device_add(&gmod->dev);
Greg Kroah-Hartmana239f672014-09-01 14:39:49 -0700238 if (retval)
239 goto error;
Greg Kroah-Hartmanb94295e2014-09-01 18:34:28 -0700240
Matt Porter32dff132014-10-03 13:38:24 -0400241 retval = gb_init_subdevs(gmod, &fake_greybus_module_id);
242 if (retval)
243 goto error_subdevs;
Greg Kroah-Hartmanb94295e2014-09-01 18:34:28 -0700244
Alex Elder778c69c2014-09-22 19:19:03 -0500245 //return gmod;
Greg Kroah-Hartman4a833fd2014-09-21 19:17:55 -0700246 return;
Matt Porter32dff132014-10-03 13:38:24 -0400247
248error_subdevs:
249 device_del(&gmod->dev);
250
Greg Kroah-Hartmana239f672014-09-01 14:39:49 -0700251error:
Alex Elder937d0da2014-10-03 14:14:25 -0500252 gb_module_destroy(gmod);
253
Alex Elder778c69c2014-09-22 19:19:03 -0500254 put_device(&gmod->dev);
255 greybus_module_release(&gmod->dev);
Greg Kroah-Hartmana239f672014-09-01 14:39:49 -0700256}
257
Greg Kroah-Hartman67799972014-09-21 18:17:36 -0700258void gb_remove_module(struct greybus_host_device *hd, u8 module_id)
259{
Matt Porterd7f9be42014-10-03 14:32:35 -0400260 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);
Greg Kroah-Hartman67799972014-09-21 18:17:36 -0700273}
274
Alex Eldere1e9dbd2014-10-01 21:54:11 -0500275void greybus_remove_device(struct gb_module *gmod)
Greg Kroah-Hartmandb6e1fd2014-08-30 16:47:26 -0700276{
277 /* tear down all of the "sub device types" for this device */
Alex Elder778c69c2014-09-22 19:19:03 -0500278 gb_i2c_disconnect(gmod);
279 gb_gpio_disconnect(gmod);
280 gb_sdio_disconnect(gmod);
281 gb_tty_disconnect(gmod);
282 gb_battery_disconnect(gmod);
Greg Kroah-Hartmanb94295e2014-09-01 18:34:28 -0700283
Matt Porterd7f9be42014-10-03 14:32:35 -0400284 device_del(&gmod->dev);
285 put_device(&gmod->dev);
Greg Kroah-Hartmandb6e1fd2014-08-30 16:47:26 -0700286}
Greg Kroah-Hartman199d68d2014-08-30 16:20:22 -0700287
Greg Kroah-Hartman68f1fc42014-09-07 13:12:11 -0700288static DEFINE_MUTEX(hd_mutex);
289
290static 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);
Alex Eldera06df4b2014-10-16 06:35:26 -0500297 mutex_unlock(&hd_mutex);
Greg Kroah-Hartman68f1fc42014-09-07 13:12:11 -0700298}
299
Greg Kroah-Hartmana39879f2014-09-06 16:57:36 -0700300struct greybus_host_device *greybus_create_hd(struct greybus_host_driver *driver,
301 struct device *parent)
302{
303 struct greybus_host_device *hd;
304
305 hd = kzalloc(sizeof(*hd) + driver->hd_priv_size, GFP_KERNEL);
306 if (!hd)
307 return NULL;
308
309 kref_init(&hd->kref);
Greg Kroah-Hartman772149b2014-09-14 12:27:28 -0700310 hd->parent = parent;
311 hd->driver = driver;
Alex Eldere1e9dbd2014-10-01 21:54:11 -0500312 INIT_LIST_HEAD(&hd->modules);
Alex Elderee9ebe42014-10-06 06:53:08 -0500313 hd->connections = RB_ROOT;
Alex Elder177404b2014-10-03 14:14:24 -0500314 ida_init(&hd->cport_id_map);
Greg Kroah-Hartman25b7b6d2014-10-06 20:29:40 -0700315 spin_lock_init(&hd->cport_id_map_lock);
Alex Elder1bb3c722014-10-02 12:30:03 -0500316
Greg Kroah-Hartmana39879f2014-09-06 16:57:36 -0700317 return hd;
318}
319EXPORT_SYMBOL_GPL(greybus_create_hd);
320
Greg Kroah-Hartman68f1fc42014-09-07 13:12:11 -0700321void greybus_remove_hd(struct greybus_host_device *hd)
322{
323 kref_put_mutex(&hd->kref, free_hd, &hd_mutex);
324}
325EXPORT_SYMBOL_GPL(greybus_remove_hd);
326
Greg Kroah-Hartmana39879f2014-09-06 16:57:36 -0700327
Greg Kroah-Hartman503c1cd2014-08-30 16:21:03 -0700328static int __init gb_init(void)
Greg Kroah-Hartman199d68d2014-08-30 16:20:22 -0700329{
Greg Kroah-Hartmandb6e1fd2014-08-30 16:47:26 -0700330 int retval;
331
Alex Elder1bb3c722014-10-02 12:30:03 -0500332 BUILD_BUG_ON(HOST_DEV_CPORT_ID_MAX >= (long)CPORT_ID_BAD);
Alex Elder1bb3c722014-10-02 12:30:03 -0500333
Greg Kroah-Hartmande536e32014-08-31 16:17:04 -0700334 retval = gb_debugfs_init();
Greg Kroah-Hartman168db1c2014-09-13 16:15:52 -0700335 if (retval) {
336 pr_err("debugfs failed\n");
Greg Kroah-Hartmandb6e1fd2014-08-30 16:47:26 -0700337 return retval;
Greg Kroah-Hartman168db1c2014-09-13 16:15:52 -0700338 }
Greg Kroah-Hartmandb6e1fd2014-08-30 16:47:26 -0700339
Greg Kroah-Hartman27fb8312014-08-31 13:54:59 -0700340 retval = bus_register(&greybus_bus_type);
Greg Kroah-Hartman168db1c2014-09-13 16:15:52 -0700341 if (retval) {
342 pr_err("bus_register failed\n");
Greg Kroah-Hartman27fb8312014-08-31 13:54:59 -0700343 goto error_bus;
Greg Kroah-Hartman168db1c2014-09-13 16:15:52 -0700344 }
Greg Kroah-Hartman27fb8312014-08-31 13:54:59 -0700345
Greg Kroah-Hartman45f36782014-09-14 11:40:35 -0700346 retval = gb_ap_init();
Greg Kroah-Hartman168db1c2014-09-13 16:15:52 -0700347 if (retval) {
Greg Kroah-Hartman45f36782014-09-14 11:40:35 -0700348 pr_err("gb_ap_init failed\n");
349 goto error_ap;
Greg Kroah-Hartman168db1c2014-09-13 16:15:52 -0700350 }
Greg Kroah-Hartmande536e32014-08-31 16:17:04 -0700351
Greg Kroah-Hartman45f36782014-09-14 11:40:35 -0700352 retval = gb_gbuf_init();
353 if (retval) {
354 pr_err("gb_gbuf_init failed\n");
355 goto error_gbuf;
356 }
Greg Kroah-Hartman27fb8312014-08-31 13:54:59 -0700357
358 retval = gb_tty_init();
Greg Kroah-Hartman168db1c2014-09-13 16:15:52 -0700359 if (retval) {
360 pr_err("gb_tty_init failed\n");
Greg Kroah-Hartman27fb8312014-08-31 13:54:59 -0700361 goto error_tty;
Greg Kroah-Hartman168db1c2014-09-13 16:15:52 -0700362 }
Greg Kroah-Hartman27fb8312014-08-31 13:54:59 -0700363
Greg Kroah-Hartman199d68d2014-08-30 16:20:22 -0700364 return 0;
Greg Kroah-Hartman27fb8312014-08-31 13:54:59 -0700365
366error_tty:
Greg Kroah-Hartman45f36782014-09-14 11:40:35 -0700367 gb_gbuf_exit();
Greg Kroah-Hartmande536e32014-08-31 16:17:04 -0700368
Greg Kroah-Hartman45f36782014-09-14 11:40:35 -0700369error_gbuf:
370 gb_ap_exit();
371
372error_ap:
Greg Kroah-Hartman27fb8312014-08-31 13:54:59 -0700373 bus_unregister(&greybus_bus_type);
374
375error_bus:
Greg Kroah-Hartmande536e32014-08-31 16:17:04 -0700376 gb_debugfs_cleanup();
Greg Kroah-Hartman27fb8312014-08-31 13:54:59 -0700377
378 return retval;
Greg Kroah-Hartman199d68d2014-08-30 16:20:22 -0700379}
380
Greg Kroah-Hartman503c1cd2014-08-30 16:21:03 -0700381static void __exit gb_exit(void)
Greg Kroah-Hartman199d68d2014-08-30 16:20:22 -0700382{
Greg Kroah-Hartmandb6e1fd2014-08-30 16:47:26 -0700383 gb_tty_exit();
Greg Kroah-Hartman45f36782014-09-14 11:40:35 -0700384 gb_gbuf_exit();
385 gb_ap_exit();
Greg Kroah-Hartman27fb8312014-08-31 13:54:59 -0700386 bus_unregister(&greybus_bus_type);
Greg Kroah-Hartmande536e32014-08-31 16:17:04 -0700387 gb_debugfs_cleanup();
Greg Kroah-Hartman199d68d2014-08-30 16:20:22 -0700388}
389
390module_init(gb_init);
391module_exit(gb_exit);
392
Greg Kroah-Hartmanc8a797a2014-08-11 15:30:45 +0800393MODULE_LICENSE("GPL");
394MODULE_AUTHOR("Greg Kroah-Hartman <gregkh@linuxfoundation.org>");