]> git.karo-electronics.de Git - karo-tx-linux.git/blob - drivers/staging/greybus/core.c
greybus: Random spell fixes
[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, nogreybus, bool, 0444);
26 #endif
27 int greybus_disabled(void)
28 {
29         return nogreybus;
30 }
31 EXPORT_SYMBOL_GPL(greybus_disabled);
32
33 static int greybus_module_match(struct device *dev, struct device_driver *drv)
34 {
35         struct greybus_driver *driver = to_greybus_driver(drv);
36         struct gb_module *gmod = to_gb_module(dev);
37         const struct greybus_module_id *id;
38
39         id = gb_module_match_id(gmod, driver->id_table);
40         if (id)
41                 return 1;
42         /* FIXME - Dynamic ids? */
43         return 0;
44 }
45
46 static int greybus_uevent(struct device *dev, struct kobj_uevent_env *env)
47 {
48         struct gb_module *gmod = NULL;
49         struct gb_interface *interface = NULL;
50         struct gb_connection *connection = NULL;
51
52         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         }
65
66         if (connection) {
67                 // FIXME
68                 // add a uevent that can "load" a connection type
69                 return 0;
70         }
71
72         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.
84         return 0;
85 }
86
87 struct bus_type greybus_bus_type = {
88         .name =         "greybus",
89         .match =        greybus_module_match,
90         .uevent =       greybus_uevent,
91 };
92
93 static int greybus_probe(struct device *dev)
94 {
95         struct greybus_driver *driver = to_greybus_driver(dev->driver);
96         struct gb_module *gmod = to_gb_module(dev);
97         const struct greybus_module_id *id;
98         int retval;
99
100         /* match id */
101         id = gb_module_match_id(gmod, driver->id_table);
102         if (!id)
103                 return -ENODEV;
104
105         retval = driver->probe(gmod, id);
106         if (retval)
107                 return retval;
108
109         return 0;
110 }
111
112 static int greybus_remove(struct device *dev)
113 {
114         struct greybus_driver *driver = to_greybus_driver(dev->driver);
115         struct gb_module *gmod = to_gb_module(dev);
116
117         driver->disconnect(gmod);
118         return 0;
119 }
120
121 int 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 }
142 EXPORT_SYMBOL_GPL(greybus_register_driver);
143
144 void greybus_deregister(struct greybus_driver *driver)
145 {
146         driver_unregister(&driver->driver);
147 }
148 EXPORT_SYMBOL_GPL(greybus_deregister);
149
150
151 static DEFINE_MUTEX(hd_mutex);
152
153 static 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);
160         mutex_unlock(&hd_mutex);
161 }
162
163 struct greybus_host_device *greybus_create_hd(struct greybus_host_driver *driver,
164                                               struct device *parent)
165 {
166         struct greybus_host_device *hd;
167
168         /*
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->buffer_send) || (!driver->buffer_cancel) ||
173             (!driver->submit_svc)) {
174                 pr_err("Must implement all greybus_host_driver callbacks!\n");
175                 return NULL;
176         }
177
178         hd = kzalloc(sizeof(*hd) + driver->hd_priv_size, GFP_KERNEL);
179         if (!hd)
180                 return NULL;
181
182         kref_init(&hd->kref);
183         hd->parent = parent;
184         hd->driver = driver;
185         INIT_LIST_HEAD(&hd->modules);
186         INIT_LIST_HEAD(&hd->connections);
187         ida_init(&hd->cport_id_map);
188
189         return hd;
190 }
191 EXPORT_SYMBOL_GPL(greybus_create_hd);
192
193 void greybus_remove_hd(struct greybus_host_device *hd)
194 {
195         /* Tear down all modules that happen to be associated with this host
196          * controller */
197         gb_remove_modules(hd);
198         kref_put_mutex(&hd->kref, free_hd, &hd_mutex);
199 }
200 EXPORT_SYMBOL_GPL(greybus_remove_hd);
201
202 static int __init gb_init(void)
203 {
204         int retval;
205
206         BUILD_BUG_ON(HOST_DEV_CPORT_ID_MAX >= (long)CPORT_ID_BAD);
207
208         retval = gb_debugfs_init();
209         if (retval) {
210                 pr_err("debugfs failed\n");
211                 return retval;
212         }
213
214         retval = bus_register(&greybus_bus_type);
215         if (retval) {
216                 pr_err("bus_register failed\n");
217                 goto error_bus;
218         }
219
220         retval = gb_ap_init();
221         if (retval) {
222                 pr_err("gb_ap_init failed\n");
223                 goto error_ap;
224         }
225
226         retval = gb_operation_init();
227         if (retval) {
228                 pr_err("gb_operation_init failed\n");
229                 goto error_operation;
230         }
231
232         if (!gb_protocol_init()) {
233                 /* This only fails for duplicate protocol registration */
234                 retval = -EEXIST;
235                 pr_err("gb_protocol_init failed\n");
236                 goto error_protocol;
237         }
238
239         return 0;       /* Success */
240
241 error_protocol:
242         gb_operation_exit();
243 error_operation:
244         gb_ap_exit();
245 error_ap:
246         bus_unregister(&greybus_bus_type);
247 error_bus:
248         gb_debugfs_cleanup();
249
250         return retval;
251 }
252
253 static void __exit gb_exit(void)
254 {
255         gb_protocol_exit();
256         gb_operation_exit();
257         gb_ap_exit();
258         bus_unregister(&greybus_bus_type);
259         gb_debugfs_cleanup();
260 }
261
262 module_init(gb_init);
263 module_exit(gb_exit);
264
265 MODULE_LICENSE("GPL");
266 MODULE_AUTHOR("Greg Kroah-Hartman <gregkh@linuxfoundation.org>");