]> git.karo-electronics.de Git - karo-tx-linux.git/blob - drivers/staging/greybus/svc.c
greybus: svc: unexport few internal functions
[karo-tx-linux.git] / drivers / staging / greybus / svc.c
1 /*
2  * SVC Greybus driver.
3  *
4  * Copyright 2015 Google Inc.
5  * Copyright 2015 Linaro Ltd.
6  *
7  * Released under the GPLv2 only.
8  */
9
10 #include <linux/workqueue.h>
11
12 #include "greybus.h"
13
14 #define CPORT_FLAGS_E2EFC       (1)
15 #define CPORT_FLAGS_CSD_N       (2)
16 #define CPORT_FLAGS_CSV_N       (4)
17
18 enum gb_svc_state {
19         GB_SVC_STATE_RESET,
20         GB_SVC_STATE_PROTOCOL_VERSION,
21         GB_SVC_STATE_SVC_HELLO,
22 };
23
24 struct gb_svc {
25         struct gb_connection    *connection;
26         enum gb_svc_state       state;
27 };
28
29 struct svc_hotplug {
30         struct work_struct work;
31         struct gb_connection *connection;
32         struct gb_svc_intf_hotplug_request data;
33 };
34
35 static struct ida greybus_svc_device_id_map;
36
37 /*
38  * AP's SVC cport is required early to get messages from the SVC. This happens
39  * even before the Endo is created and hence any modules or interfaces.
40  *
41  * This is a temporary connection, used only at initial bootup.
42  */
43 struct gb_connection *
44 gb_ap_svc_connection_create(struct greybus_host_device *hd)
45 {
46         struct gb_connection *connection;
47
48         connection = gb_connection_create_range(hd, NULL, hd->parent,
49                                                 GB_SVC_CPORT_ID,
50                                                 GREYBUS_PROTOCOL_SVC,
51                                                 GB_SVC_CPORT_ID,
52                                                 GB_SVC_CPORT_ID + 1);
53
54         return connection;
55 }
56
57 /*
58  * We know endo-type and AP's interface id now, lets create a proper svc
59  * connection (and its interface/bundle) now and get rid of the initial
60  * 'partially' initialized one svc connection.
61  */
62 static struct gb_interface *
63 gb_ap_interface_create(struct greybus_host_device *hd,
64                        struct gb_connection *connection, u8 interface_id)
65 {
66         struct gb_interface *intf;
67         struct device *dev = &hd->endo->dev;
68
69         intf = gb_interface_create(hd, interface_id);
70         if (!intf) {
71                 dev_err(dev, "%s: Failed to create interface with id %hhu\n",
72                         __func__, interface_id);
73                 return NULL;
74         }
75
76         intf->device_id = GB_DEVICE_ID_AP;
77         svc_update_connection(intf, connection);
78
79         /* Its no longer a partially initialized connection */
80         hd->initial_svc_connection = NULL;
81
82         return intf;
83 }
84
85 static int gb_svc_intf_device_id(struct gb_svc *svc, u8 intf_id, u8 device_id)
86 {
87         struct gb_svc_intf_device_id_request request;
88
89         request.intf_id = intf_id;
90         request.device_id = device_id;
91
92         return gb_operation_sync(svc->connection, GB_SVC_TYPE_INTF_DEVICE_ID,
93                                  &request, sizeof(request), NULL, 0);
94 }
95
96 int gb_svc_intf_reset(struct gb_svc *svc, u8 intf_id)
97 {
98         struct gb_svc_intf_reset_request request;
99
100         request.intf_id = intf_id;
101
102         return gb_operation_sync(svc->connection, GB_SVC_TYPE_INTF_RESET,
103                                  &request, sizeof(request), NULL, 0);
104 }
105 EXPORT_SYMBOL_GPL(gb_svc_intf_reset);
106
107 int gb_svc_connection_create(struct gb_svc *svc,
108                                 u8 intf1_id, u16 cport1_id,
109                                 u8 intf2_id, u16 cport2_id)
110 {
111         struct gb_svc_conn_create_request request;
112
113         request.intf1_id = intf1_id;
114         request.cport1_id = cport1_id;
115         request.intf2_id = intf2_id;
116         request.cport2_id = cport2_id;
117         /*
118          * XXX: fix connections paramaters to TC0 and all CPort flags
119          * for now.
120          */
121         request.tc = 0;
122         request.flags = CPORT_FLAGS_CSV_N | CPORT_FLAGS_E2EFC;
123
124         return gb_operation_sync(svc->connection, GB_SVC_TYPE_CONN_CREATE,
125                                  &request, sizeof(request), NULL, 0);
126 }
127 EXPORT_SYMBOL_GPL(gb_svc_connection_create);
128
129 void gb_svc_connection_destroy(struct gb_svc *svc, u8 intf1_id, u16 cport1_id,
130                                u8 intf2_id, u16 cport2_id)
131 {
132         struct gb_svc_conn_destroy_request request;
133         struct gb_connection *connection = svc->connection;
134         int ret;
135
136         request.intf1_id = intf1_id;
137         request.cport1_id = cport1_id;
138         request.intf2_id = intf2_id;
139         request.cport2_id = cport2_id;
140
141         ret = gb_operation_sync(connection, GB_SVC_TYPE_CONN_DESTROY,
142                                 &request, sizeof(request), NULL, 0);
143         if (ret) {
144                 dev_err(&connection->dev,
145                         "failed to destroy connection (%hhx:%hx %hhx:%hx) %d\n",
146                         intf1_id, cport1_id, intf2_id, cport2_id, ret);
147         }
148 }
149 EXPORT_SYMBOL_GPL(gb_svc_connection_destroy);
150
151 static int gb_svc_route_create(struct gb_svc *svc, u8 intf1_id, u8 dev1_id,
152                                u8 intf2_id, u8 dev2_id)
153 {
154         struct gb_svc_route_create_request request;
155
156         request.intf1_id = intf1_id;
157         request.dev1_id = dev1_id;
158         request.intf2_id = intf2_id;
159         request.dev2_id = dev2_id;
160
161         return gb_operation_sync(svc->connection, GB_SVC_TYPE_ROUTE_CREATE,
162                                  &request, sizeof(request), NULL, 0);
163 }
164
165 static int gb_svc_version_request(struct gb_operation *op)
166 {
167         struct gb_connection *connection = op->connection;
168         struct gb_protocol_version_response *version;
169         struct device *dev = &connection->dev;
170
171         version = op->request->payload;
172
173         if (version->major > GB_SVC_VERSION_MAJOR) {
174                 dev_err(&connection->dev,
175                         "unsupported major version (%hhu > %hhu)\n",
176                         version->major, GB_SVC_VERSION_MAJOR);
177                 return -ENOTSUPP;
178         }
179
180         connection->module_major = version->major;
181         connection->module_minor = version->minor;
182
183         if (!gb_operation_response_alloc(op, sizeof(*version), GFP_KERNEL)) {
184                 dev_err(dev, "%s: error allocating response\n",
185                                 __func__);
186                 return -ENOMEM;
187         }
188
189         version = op->response->payload;
190         version->major = GB_SVC_VERSION_MAJOR;
191         version->minor = GB_SVC_VERSION_MINOR;
192         return 0;
193 }
194
195 static int gb_svc_hello(struct gb_operation *op)
196 {
197         struct gb_connection *connection = op->connection;
198         struct greybus_host_device *hd = connection->hd;
199         struct gb_svc_hello_request *hello_request;
200         struct device *dev = &connection->dev;
201         struct gb_interface *intf;
202         u16 endo_id;
203         u8 interface_id;
204         int ret;
205
206         /*
207          * SVC sends information about the endo and interface-id on the hello
208          * request, use that to create an endo.
209          */
210         if (op->request->payload_size < sizeof(*hello_request)) {
211                 dev_err(dev, "%s: Illegal size of hello request (%zu < %zu)\n",
212                         __func__, op->request->payload_size,
213                         sizeof(*hello_request));
214                 return -EINVAL;
215         }
216
217         hello_request = op->request->payload;
218         endo_id = le16_to_cpu(hello_request->endo_id);
219         interface_id = hello_request->interface_id;
220
221         /* Setup Endo */
222         ret = greybus_endo_setup(hd, endo_id, interface_id);
223         if (ret)
224                 return ret;
225
226         /*
227          * Endo and its modules are ready now, fix AP's partially initialized
228          * svc protocol and its connection.
229          */
230         intf = gb_ap_interface_create(hd, connection, interface_id);
231         if (!intf) {
232                 gb_endo_remove(hd->endo);
233                 return ret;
234         }
235
236         return 0;
237 }
238
239 /*
240  * 'struct svc_hotplug' should be freed by svc_process_hotplug() before it
241  * returns, irrespective of success or Failure in bringing up the module.
242  */
243 static void svc_process_hotplug(struct work_struct *work)
244 {
245         struct svc_hotplug *svc_hotplug = container_of(work, struct svc_hotplug,
246                                                        work);
247         struct gb_svc_intf_hotplug_request *hotplug = &svc_hotplug->data;
248         struct gb_connection *connection = svc_hotplug->connection;
249         struct gb_svc *svc = connection->private;
250         struct greybus_host_device *hd = connection->hd;
251         struct device *dev = &connection->dev;
252         struct gb_interface *intf;
253         u8 intf_id, device_id;
254         int ret;
255
256         /*
257          * Grab the information we need.
258          */
259         intf_id = hotplug->intf_id;
260
261         intf = gb_interface_create(hd, intf_id);
262         if (!intf) {
263                 dev_err(dev, "%s: Failed to create interface with id %hhu\n",
264                         __func__, intf_id);
265                 goto free_svc_hotplug;
266         }
267
268         intf->unipro_mfg_id = le32_to_cpu(hotplug->data.unipro_mfg_id);
269         intf->unipro_prod_id = le32_to_cpu(hotplug->data.unipro_prod_id);
270         intf->ara_vend_id = le32_to_cpu(hotplug->data.ara_vend_id);
271         intf->ara_prod_id = le32_to_cpu(hotplug->data.ara_prod_id);
272
273         /*
274          * Create a device id for the interface:
275          * - device id 0 (GB_DEVICE_ID_SVC) belongs to the SVC
276          * - device id 1 (GB_DEVICE_ID_AP) belongs to the AP
277          *
278          * XXX Do we need to allocate device ID for SVC or the AP here? And what
279          * XXX about an AP with multiple interface blocks?
280          */
281         device_id = ida_simple_get(&greybus_svc_device_id_map,
282                                    GB_DEVICE_ID_MODULES_START, 0, GFP_KERNEL);
283         if (device_id < 0) {
284                 ret = device_id;
285                 dev_err(dev, "%s: Failed to allocate device id for interface with id %hhu (%d)\n",
286                         __func__, intf_id, ret);
287                 goto destroy_interface;
288         }
289
290         ret = gb_svc_intf_device_id(svc, intf_id, device_id);
291         if (ret) {
292                 dev_err(dev, "%s: Device id operation failed, interface %hhu device_id %hhu (%d)\n",
293                         __func__, intf_id, device_id, ret);
294                 goto ida_put;
295         }
296
297         /*
298          * Create a two-way route between the AP and the new interface
299          */
300         ret = gb_svc_route_create(svc, hd->endo->ap_intf_id, GB_DEVICE_ID_AP,
301                                   intf_id, device_id);
302         if (ret) {
303                 dev_err(dev, "%s: Route create operation failed, interface %hhu device_id %hhu (%d)\n",
304                         __func__, intf_id, device_id, ret);
305                 goto ida_put;
306         }
307
308         ret = gb_svc_route_create(svc, intf_id, device_id, hd->endo->ap_intf_id,
309                                   GB_DEVICE_ID_AP);
310         if (ret) {
311                 dev_err(dev, "%s: Route create operation failed, interface %hhu device_id %hhu (%d)\n",
312                         __func__, intf_id, device_id, ret);
313                 goto ida_put;
314         }
315
316         ret = gb_interface_init(intf, device_id);
317         if (ret) {
318                 dev_err(dev, "%s: Failed to initialize interface, interface %hhu device_id %hhu (%d)\n",
319                         __func__, intf_id, device_id, ret);
320                 goto svc_id_free;
321         }
322
323         goto free_svc_hotplug;
324
325 svc_id_free:
326         /*
327          * XXX Should we tell SVC that this id doesn't belong to interface
328          * XXX anymore.
329          */
330 ida_put:
331         ida_simple_remove(&greybus_svc_device_id_map, device_id);
332 destroy_interface:
333         gb_interface_remove(hd, intf_id);
334 free_svc_hotplug:
335         kfree(svc_hotplug);
336 }
337
338 /*
339  * Bringing up a module can be time consuming, as that may require lots of
340  * initialization on the module side. Over that, we may also need to download
341  * the firmware first and flash that on the module.
342  *
343  * In order to make other hotplug events to not wait for all this to finish,
344  * handle most of module hotplug stuff outside of the hotplug callback, with
345  * help of a workqueue.
346  */
347 static int gb_svc_intf_hotplug_recv(struct gb_operation *op)
348 {
349         struct gb_message *request = op->request;
350         struct svc_hotplug *svc_hotplug;
351
352         if (request->payload_size < sizeof(svc_hotplug->data)) {
353                 dev_err(&op->connection->dev,
354                         "%s: short hotplug request received (%zu < %zu)\n",
355                         __func__, request->payload_size,
356                         sizeof(svc_hotplug->data));
357                 return -EINVAL;
358         }
359
360         svc_hotplug = kmalloc(sizeof(*svc_hotplug), GFP_KERNEL);
361         if (!svc_hotplug)
362                 return -ENOMEM;
363
364         svc_hotplug->connection = op->connection;
365         memcpy(&svc_hotplug->data, op->request->payload, sizeof(svc_hotplug->data));
366
367         INIT_WORK(&svc_hotplug->work, svc_process_hotplug);
368         queue_work(system_unbound_wq, &svc_hotplug->work);
369
370         return 0;
371 }
372
373 static int gb_svc_intf_hot_unplug_recv(struct gb_operation *op)
374 {
375         struct gb_message *request = op->request;
376         struct gb_svc_intf_hot_unplug_request *hot_unplug = request->payload;
377         struct greybus_host_device *hd = op->connection->hd;
378         struct device *dev = &op->connection->dev;
379         u8 device_id;
380         struct gb_interface *intf;
381         u8 intf_id;
382
383         if (request->payload_size < sizeof(*hot_unplug)) {
384                 dev_err(&op->connection->dev,
385                         "short hot unplug request received (%zu < %zu)\n",
386                         request->payload_size, sizeof(*hot_unplug));
387                 return -EINVAL;
388         }
389
390         intf_id = hot_unplug->intf_id;
391
392         intf = gb_interface_find(hd, intf_id);
393         if (!intf) {
394                 dev_err(dev, "%s: Couldn't find interface for id %hhu\n",
395                         __func__, intf_id);
396                 return -EINVAL;
397         }
398
399         device_id = intf->device_id;
400         gb_interface_remove(hd, intf_id);
401         ida_simple_remove(&greybus_svc_device_id_map, device_id);
402
403         return 0;
404 }
405
406 static int gb_svc_intf_reset_recv(struct gb_operation *op)
407 {
408         struct gb_message *request = op->request;
409         struct gb_svc_intf_reset_request *reset;
410         u8 intf_id;
411
412         if (request->payload_size < sizeof(*reset)) {
413                 dev_err(&op->connection->dev,
414                         "short reset request received (%zu < %zu)\n",
415                         request->payload_size, sizeof(*reset));
416                 return -EINVAL;
417         }
418         reset = request->payload;
419
420         intf_id = reset->intf_id;
421
422         /* FIXME Reset the interface here */
423
424         return 0;
425 }
426
427 static int gb_svc_request_recv(u8 type, struct gb_operation *op)
428 {
429         struct gb_connection *connection = op->connection;
430         struct gb_svc *svc = connection->private;
431         int ret = 0;
432
433         /*
434          * SVC requests need to follow a specific order (at least initially) and
435          * below code takes care of enforcing that. The expected order is:
436          * - PROTOCOL_VERSION
437          * - SVC_HELLO
438          * - Any other request, but the earlier two.
439          *
440          * Incoming requests are guaranteed to be serialized and so we don't
441          * need to protect 'state' for any races.
442          */
443         switch (type) {
444         case GB_REQUEST_TYPE_PROTOCOL_VERSION:
445                 if (svc->state != GB_SVC_STATE_RESET)
446                         ret = -EINVAL;
447                 break;
448         case GB_SVC_TYPE_SVC_HELLO:
449                 if (svc->state != GB_SVC_STATE_PROTOCOL_VERSION)
450                         ret = -EINVAL;
451                 break;
452         default:
453                 if (svc->state != GB_SVC_STATE_SVC_HELLO)
454                         ret = -EINVAL;
455                 break;
456         }
457
458         if (ret) {
459                 dev_warn(&connection->dev,
460                          "unexpected SVC request 0x%02x received (state %u)\n",
461                          type, svc->state);
462                 return ret;
463         }
464
465         switch (type) {
466         case GB_REQUEST_TYPE_PROTOCOL_VERSION:
467                 ret = gb_svc_version_request(op);
468                 if (!ret)
469                         svc->state = GB_SVC_STATE_PROTOCOL_VERSION;
470                 return ret;
471         case GB_SVC_TYPE_SVC_HELLO:
472                 ret = gb_svc_hello(op);
473                 if (!ret)
474                         svc->state = GB_SVC_STATE_SVC_HELLO;
475                 return ret;
476         case GB_SVC_TYPE_INTF_HOTPLUG:
477                 return gb_svc_intf_hotplug_recv(op);
478         case GB_SVC_TYPE_INTF_HOT_UNPLUG:
479                 return gb_svc_intf_hot_unplug_recv(op);
480         case GB_SVC_TYPE_INTF_RESET:
481                 return gb_svc_intf_reset_recv(op);
482         default:
483                 dev_err(&op->connection->dev,
484                         "unsupported request: %hhu\n", type);
485                 return -EINVAL;
486         }
487 }
488
489 static int gb_svc_connection_init(struct gb_connection *connection)
490 {
491         struct gb_svc *svc;
492
493         svc = kzalloc(sizeof(*svc), GFP_KERNEL);
494         if (!svc)
495                 return -ENOMEM;
496
497         connection->hd->svc = svc;
498         svc->state = GB_SVC_STATE_RESET;
499         svc->connection = connection;
500         connection->private = svc;
501
502         WARN_ON(connection->hd->initial_svc_connection);
503         connection->hd->initial_svc_connection = connection;
504
505         ida_init(&greybus_svc_device_id_map);
506
507         return 0;
508 }
509
510 static void gb_svc_connection_exit(struct gb_connection *connection)
511 {
512         struct gb_svc *svc = connection->private;
513
514         connection->hd->svc = NULL;
515         connection->private = NULL;
516         kfree(svc);
517 }
518
519 static struct gb_protocol svc_protocol = {
520         .name                   = "svc",
521         .id                     = GREYBUS_PROTOCOL_SVC,
522         .major                  = GB_SVC_VERSION_MAJOR,
523         .minor                  = GB_SVC_VERSION_MINOR,
524         .connection_init        = gb_svc_connection_init,
525         .connection_exit        = gb_svc_connection_exit,
526         .request_recv           = gb_svc_request_recv,
527 };
528 gb_builtin_protocol_driver(svc_protocol);