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