blob: 96bd97481cf2c698a6c5b8dba5cdb5212a092bb3 [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
Viresh Kumar8597e6b2014-10-20 16:45:50 +053025core_param(nogreybus, nogreybus, bool, 0444);
Greg Kroah-Hartmanc8a797a2014-08-11 15:30:45 +080026#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{
Viresh Kumar95bd99d2014-11-14 17:24:59 +053035 struct greybus_driver *driver = to_greybus_driver(drv);
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{
Greg Kroah-Hartman0ac5a832014-11-15 12:12:16 -080048 struct gb_module *gmod = NULL;
49 struct gb_interface *interface = NULL;
50 struct gb_connection *connection = NULL;
Greg Kroah-Hartmanc8a797a2014-08-11 15:30:45 +080051
Greg Kroah-Hartman0ac5a832014-11-15 12:12:16 -080052 if (is_gb_module(dev)) {
53 gmod = to_gb_module(dev);
54 } else if (is_gb_interface(dev)) {
55 interface = to_gb_interface(dev);
56 gmod = interface->gmod;
57 } else if (is_gb_connection(dev)) {
58 connection = to_gb_connection(dev);
59 interface = connection->interface;
60 gmod = interface->gmod;
61 } else {
62 dev_WARN(dev, "uevent for unknown greybus device \"type\"!\n");
63 return -EINVAL;
64 }
Greg Kroah-Hartmanf0f61b92014-10-24 17:34:46 +080065
Greg Kroah-Hartman0ac5a832014-11-15 12:12:16 -080066 if (connection) {
67 // FIXME
68 // add a uevent that can "load" a connection type
69 return 0;
70 }
Greg Kroah-Hartmanf0f61b92014-10-24 17:34:46 +080071
Greg Kroah-Hartman0ac5a832014-11-15 12:12:16 -080072 if (interface) {
73 // FIXME
74 // add a uevent that can "load" a interface type
75 // This is what we need to bind a driver to so use the info
76 // in gmod here as well
77 return 0;
78 }
79
80 // FIXME
81 // "just" a module, be vague here, nothing binds to a module except
82 // the greybus core, so there's not much, if anything, we need to
83 // advertise.
Greg Kroah-Hartmanc8a797a2014-08-11 15:30:45 +080084 return 0;
85}
86
Greg Kroah-Hartmanf0f61b92014-10-24 17:34:46 +080087struct bus_type greybus_bus_type = {
Greg Kroah-Hartmanc8a797a2014-08-11 15:30:45 +080088 .name = "greybus",
Alex Elder778c69c2014-09-22 19:19:03 -050089 .match = greybus_module_match,
Greg Kroah-Hartmanc8a797a2014-08-11 15:30:45 +080090 .uevent = greybus_uevent,
91};
92
93static int greybus_probe(struct device *dev)
94{
95 struct greybus_driver *driver = to_greybus_driver(dev->driver);
Alex Eldere1e9dbd2014-10-01 21:54:11 -050096 struct gb_module *gmod = to_gb_module(dev);
Greg Kroah-Hartman6584c8a2014-09-01 13:31:31 -070097 const struct greybus_module_id *id;
Greg Kroah-Hartmanc8a797a2014-08-11 15:30:45 +080098 int retval;
99
100 /* match id */
Alex Eldere1e9dbd2014-10-01 21:54:11 -0500101 id = gb_module_match_id(gmod, driver->id_table);
Greg Kroah-Hartmanc8a797a2014-08-11 15:30:45 +0800102 if (!id)
103 return -ENODEV;
104
Alex Elder778c69c2014-09-22 19:19:03 -0500105 retval = driver->probe(gmod, id);
Greg Kroah-Hartmanc8a797a2014-08-11 15:30:45 +0800106 if (retval)
107 return retval;
108
109 return 0;
110}
111
112static int greybus_remove(struct device *dev)
113{
114 struct greybus_driver *driver = to_greybus_driver(dev->driver);
Alex Eldere1e9dbd2014-10-01 21:54:11 -0500115 struct gb_module *gmod = to_gb_module(dev);
Greg Kroah-Hartmanc8a797a2014-08-11 15:30:45 +0800116
Alex Elder778c69c2014-09-22 19:19:03 -0500117 driver->disconnect(gmod);
Greg Kroah-Hartmanc8a797a2014-08-11 15:30:45 +0800118 return 0;
119}
120
121int greybus_register_driver(struct greybus_driver *driver, struct module *owner,
122 const char *mod_name)
123{
124 int retval;
125
126 if (greybus_disabled())
127 return -ENODEV;
128
129 driver->driver.name = driver->name;
130 driver->driver.probe = greybus_probe;
131 driver->driver.remove = greybus_remove;
132 driver->driver.owner = owner;
133 driver->driver.mod_name = mod_name;
134
135 retval = driver_register(&driver->driver);
136 if (retval)
137 return retval;
138
139 pr_info("registered new driver %s\n", driver->name);
140 return 0;
141}
142EXPORT_SYMBOL_GPL(greybus_register_driver);
143
144void greybus_deregister(struct greybus_driver *driver)
145{
146 driver_unregister(&driver->driver);
147}
148EXPORT_SYMBOL_GPL(greybus_deregister);
149
Greg Kroah-Hartman199d68d2014-08-30 16:20:22 -0700150
Greg Kroah-Hartman68f1fc42014-09-07 13:12:11 -0700151static DEFINE_MUTEX(hd_mutex);
152
153static void free_hd(struct kref *kref)
154{
155 struct greybus_host_device *hd;
156
157 hd = container_of(kref, struct greybus_host_device, kref);
158
159 kfree(hd);
Alex Eldera06df4b2014-10-16 06:35:26 -0500160 mutex_unlock(&hd_mutex);
Greg Kroah-Hartman68f1fc42014-09-07 13:12:11 -0700161}
162
Greg Kroah-Hartmana39879f2014-09-06 16:57:36 -0700163struct greybus_host_device *greybus_create_hd(struct greybus_host_driver *driver,
164 struct device *parent)
165{
166 struct greybus_host_device *hd;
167
Greg Kroah-Hartman724b6192014-10-27 13:32:27 +0800168 /*
169 * Validate that the driver implements all of the callbacks
170 * so that we don't have to every time we make them.
171 */
172 if ((!driver->alloc_gbuf_data) ||
173 (!driver->free_gbuf_data) ||
174 (!driver->submit_svc) ||
175 (!driver->submit_gbuf) ||
Greg Kroah-Hartman4afbba02014-10-27 14:01:06 +0800176 (!driver->kill_gbuf)) {
Greg Kroah-Hartman724b6192014-10-27 13:32:27 +0800177 pr_err("Must implement all greybus_host_driver callbacks!\n");
178 return NULL;
179 }
180
Greg Kroah-Hartmana39879f2014-09-06 16:57:36 -0700181 hd = kzalloc(sizeof(*hd) + driver->hd_priv_size, GFP_KERNEL);
182 if (!hd)
183 return NULL;
184
185 kref_init(&hd->kref);
Greg Kroah-Hartman772149b2014-09-14 12:27:28 -0700186 hd->parent = parent;
187 hd->driver = driver;
Alex Eldere1e9dbd2014-10-01 21:54:11 -0500188 INIT_LIST_HEAD(&hd->modules);
Alex Elder2c43ce42014-11-17 08:08:44 -0600189 INIT_LIST_HEAD(&hd->connections);
Alex Elder177404b2014-10-03 14:14:24 -0500190 ida_init(&hd->cport_id_map);
Greg Kroah-Hartman25b7b6d2014-10-06 20:29:40 -0700191 spin_lock_init(&hd->cport_id_map_lock);
Alex Elder1bb3c722014-10-02 12:30:03 -0500192
Greg Kroah-Hartmana39879f2014-09-06 16:57:36 -0700193 return hd;
194}
195EXPORT_SYMBOL_GPL(greybus_create_hd);
196
Greg Kroah-Hartman68f1fc42014-09-07 13:12:11 -0700197void greybus_remove_hd(struct greybus_host_device *hd)
198{
Greg Kroah-Hartmanf0f61b92014-10-24 17:34:46 +0800199 /* Tear down all modules that happen to be associated with this host
200 * controller */
Greg Kroah-Hartman63e4a8e2014-10-22 16:38:07 +0800201 gb_remove_modules(hd);
Greg Kroah-Hartman68f1fc42014-09-07 13:12:11 -0700202 kref_put_mutex(&hd->kref, free_hd, &hd_mutex);
203}
204EXPORT_SYMBOL_GPL(greybus_remove_hd);
205
Greg Kroah-Hartman503c1cd2014-08-30 16:21:03 -0700206static int __init gb_init(void)
Greg Kroah-Hartman199d68d2014-08-30 16:20:22 -0700207{
Greg Kroah-Hartmandb6e1fd2014-08-30 16:47:26 -0700208 int retval;
209
Alex Elder1bb3c722014-10-02 12:30:03 -0500210 BUILD_BUG_ON(HOST_DEV_CPORT_ID_MAX >= (long)CPORT_ID_BAD);
Alex Elder1bb3c722014-10-02 12:30:03 -0500211
Greg Kroah-Hartmande536e32014-08-31 16:17:04 -0700212 retval = gb_debugfs_init();
Greg Kroah-Hartman168db1c2014-09-13 16:15:52 -0700213 if (retval) {
214 pr_err("debugfs failed\n");
Greg Kroah-Hartmandb6e1fd2014-08-30 16:47:26 -0700215 return retval;
Greg Kroah-Hartman168db1c2014-09-13 16:15:52 -0700216 }
Greg Kroah-Hartmandb6e1fd2014-08-30 16:47:26 -0700217
Greg Kroah-Hartman27fb8312014-08-31 13:54:59 -0700218 retval = bus_register(&greybus_bus_type);
Greg Kroah-Hartman168db1c2014-09-13 16:15:52 -0700219 if (retval) {
220 pr_err("bus_register failed\n");
Greg Kroah-Hartman27fb8312014-08-31 13:54:59 -0700221 goto error_bus;
Greg Kroah-Hartman168db1c2014-09-13 16:15:52 -0700222 }
Greg Kroah-Hartman27fb8312014-08-31 13:54:59 -0700223
Greg Kroah-Hartman45f36782014-09-14 11:40:35 -0700224 retval = gb_ap_init();
Greg Kroah-Hartman168db1c2014-09-13 16:15:52 -0700225 if (retval) {
Greg Kroah-Hartman45f36782014-09-14 11:40:35 -0700226 pr_err("gb_ap_init failed\n");
227 goto error_ap;
Greg Kroah-Hartman168db1c2014-09-13 16:15:52 -0700228 }
Greg Kroah-Hartmande536e32014-08-31 16:17:04 -0700229
Greg Kroah-Hartman45f36782014-09-14 11:40:35 -0700230 retval = gb_gbuf_init();
231 if (retval) {
232 pr_err("gb_gbuf_init failed\n");
233 goto error_gbuf;
234 }
Greg Kroah-Hartman27fb8312014-08-31 13:54:59 -0700235
Alex Elder2eb585f2014-10-16 06:35:34 -0500236 retval = gb_operation_init();
237 if (retval) {
238 pr_err("gb_operation_init failed\n");
239 goto error_operation;
240 }
241
Alex Elder19d03de2014-11-05 16:12:53 -0600242 if (!gb_protocol_init()) {
243 /* This only fails for duplicate protocol registration */
244 retval = -EEXIST;
245 pr_err("gb_protocol_init failed\n");
246 goto error_protocol;
247 }
Greg Kroah-Hartman27fb8312014-08-31 13:54:59 -0700248
Alex Elder19d03de2014-11-05 16:12:53 -0600249 return 0; /* Success */
250
251error_protocol:
252 gb_operation_exit();
Alex Elder2eb585f2014-10-16 06:35:34 -0500253error_operation:
Greg Kroah-Hartman45f36782014-09-14 11:40:35 -0700254 gb_gbuf_exit();
Greg Kroah-Hartman45f36782014-09-14 11:40:35 -0700255error_gbuf:
256 gb_ap_exit();
Greg Kroah-Hartman45f36782014-09-14 11:40:35 -0700257error_ap:
Greg Kroah-Hartman27fb8312014-08-31 13:54:59 -0700258 bus_unregister(&greybus_bus_type);
Greg Kroah-Hartman27fb8312014-08-31 13:54:59 -0700259error_bus:
Greg Kroah-Hartmande536e32014-08-31 16:17:04 -0700260 gb_debugfs_cleanup();
Greg Kroah-Hartman27fb8312014-08-31 13:54:59 -0700261
262 return retval;
Greg Kroah-Hartman199d68d2014-08-30 16:20:22 -0700263}
264
Greg Kroah-Hartman503c1cd2014-08-30 16:21:03 -0700265static void __exit gb_exit(void)
Greg Kroah-Hartman199d68d2014-08-30 16:20:22 -0700266{
Viresh Kumar35a52ca2014-11-13 18:14:33 +0530267 gb_protocol_exit();
Alex Elder2eb585f2014-10-16 06:35:34 -0500268 gb_operation_exit();
Greg Kroah-Hartman45f36782014-09-14 11:40:35 -0700269 gb_gbuf_exit();
270 gb_ap_exit();
Greg Kroah-Hartman27fb8312014-08-31 13:54:59 -0700271 bus_unregister(&greybus_bus_type);
Greg Kroah-Hartmande536e32014-08-31 16:17:04 -0700272 gb_debugfs_cleanup();
Greg Kroah-Hartman199d68d2014-08-30 16:20:22 -0700273}
274
275module_init(gb_init);
276module_exit(gb_exit);
277
Greg Kroah-Hartmanc8a797a2014-08-11 15:30:45 +0800278MODULE_LICENSE("GPL");
279MODULE_AUTHOR("Greg Kroah-Hartman <gregkh@linuxfoundation.org>");