]> git.karo-electronics.de Git - karo-tx-linux.git/blob - drivers/staging/greybus/svc.c
greybus: svc: Implement SVC requests
[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
12 static struct ida greybus_svc_device_id_map;
13
14 /* Define get_version() routine */
15 define_get_version(gb_svc, SVC);
16
17 /*
18  * AP's SVC cport is required early to get messages from the SVC. This happens
19  * even before the Endo is created and hence any modules or interfaces.
20  *
21  * This is a temporary connection, used only at initial bootup.
22  */
23 struct gb_connection *
24 gb_ap_svc_connection_create(struct greybus_host_device *hd)
25 {
26         struct gb_connection *connection;
27
28         connection = gb_connection_create_range(hd, NULL, hd->parent,
29                                                 GB_SVC_CPORT_ID,
30                                                 GREYBUS_PROTOCOL_SVC,
31                                                 GB_SVC_CPORT_ID,
32                                                 GB_SVC_CPORT_ID + 1);
33
34         return connection;
35 }
36 EXPORT_SYMBOL_GPL(gb_ap_svc_connection_create);
37
38 /*
39  * We know endo-type and AP's interface id now, lets create a proper svc
40  * connection (and its interface/bundle) now and get rid of the initial
41  * 'partially' initialized one svc connection.
42  */
43 static struct gb_interface *
44 gb_ap_interface_create(struct greybus_host_device *hd,
45                        struct gb_connection *connection, u8 interface_id)
46 {
47         struct gb_interface *intf;
48         struct device *dev = &hd->endo->dev;
49         int ret;
50
51         intf = gb_interface_create(hd, interface_id);
52         if (!intf) {
53                 dev_err(dev, "%s: Failed to create interface with id %hhu\n",
54                         __func__, interface_id);
55                 return NULL;
56         }
57
58         intf->device_id = GB_DEVICE_ID_AP;
59
60         /*
61          * XXX: Disable the initial svc connection here, but don't destroy it
62          * yet. We do need to send a response of 'svc-hello message' on that.
63          */
64
65         /* Establish new control CPort connection */
66         ret = gb_create_bundle_connection(intf, GREYBUS_CLASS_SVC);
67         if (ret) {
68                 dev_err(&intf->dev, "%s: Failed to create svc connection (%d %d)\n",
69                         __func__, interface_id, ret);
70                 gb_interface_destroy(intf);
71                 intf = NULL;
72         }
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         return gb_operation_sync(svc->connection, GB_SVC_TYPE_CONN_CREATE,
111                                  &request, sizeof(request), NULL, 0);
112 }
113
114 static int connection_destroy_operation(struct gb_svc *svc,
115                                 u8 intf1_id, u16 cport1_id,
116                                 u8 intf2_id, u16 cport2_id)
117 {
118         struct gb_svc_conn_destroy_request request;
119
120         request.intf1_id = intf1_id;
121         request.cport1_id = cport1_id;
122         request.intf2_id = intf2_id;
123         request.cport2_id = cport2_id;
124
125         return gb_operation_sync(svc->connection, GB_SVC_TYPE_CONN_DESTROY,
126                                  &request, sizeof(request), NULL, 0);
127 }
128
129 int gb_svc_intf_device_id(struct gb_svc *svc, u8 intf_id, u8 device_id)
130 {
131         return intf_device_id_operation(svc, intf_id, device_id);
132 }
133 EXPORT_SYMBOL_GPL(gb_svc_intf_device_id);
134
135 int gb_svc_intf_reset(struct gb_svc *svc, u8 intf_id)
136 {
137         return intf_reset_operation(svc, intf_id);
138 }
139 EXPORT_SYMBOL_GPL(gb_svc_intf_reset);
140
141 int gb_svc_connection_create(struct gb_svc *svc,
142                                 u8 intf1_id, u16 cport1_id,
143                                 u8 intf2_id, u16 cport2_id)
144 {
145         return connection_create_operation(svc, intf1_id, cport1_id,
146                                                 intf2_id, cport2_id);
147 }
148 EXPORT_SYMBOL_GPL(gb_svc_connection_create);
149
150 int gb_svc_connection_destroy(struct gb_svc *svc,
151                                 u8 intf1_id, u16 cport1_id,
152                                 u8 intf2_id, u16 cport2_id)
153 {
154         return connection_destroy_operation(svc, intf1_id, cport1_id,
155                                                 intf2_id, cport2_id);
156 }
157 EXPORT_SYMBOL_GPL(gb_svc_connection_destroy);
158
159 static int gb_svc_version_request(struct gb_operation *op)
160 {
161         struct gb_connection *connection = op->connection;
162         struct gb_protocol_version_response *version;
163         struct device *dev = &connection->dev;
164
165         version = op->request->payload;
166
167         if (version->major > GB_SVC_VERSION_MAJOR) {
168                 dev_err(&connection->dev,
169                         "unsupported major version (%hhu > %hhu)\n",
170                         version->major, GB_SVC_VERSION_MAJOR);
171                 return -ENOTSUPP;
172         }
173
174         if (!gb_operation_response_alloc(op, sizeof(*version), GFP_KERNEL)) {
175                 dev_err(dev, "%s: error allocating response\n",
176                                 __func__);
177                 return -ENOMEM;
178         }
179
180         version = op->response->payload;
181         version->major = GB_SVC_VERSION_MAJOR;
182         version->minor = GB_SVC_VERSION_MINOR;
183         return 0;
184 }
185
186 static int gb_svc_hello(struct gb_operation *op)
187 {
188         struct gb_connection *connection = op->connection;
189         struct greybus_host_device *hd = connection->hd;
190         struct gb_svc_hello_request *hello_request;
191         struct device *dev = &connection->dev;
192         struct gb_interface *intf;
193         u16 endo_id;
194         u8 interface_id;
195         int ret;
196
197         /* Hello message should be received only during early bootup */
198         WARN_ON(hd->initial_svc_connection != connection);
199
200         /*
201          * SVC sends information about the endo and interface-id on the hello
202          * request, use that to create an endo.
203          */
204         if (op->request->payload_size != sizeof(*hello_request)) {
205                 dev_err(dev, "%s: Illegal size of hello request (%d %d)\n",
206                         __func__, op->request->payload_size,
207                         sizeof(*hello_request));
208                 return -EINVAL;
209         }
210
211         hello_request = op->request->payload;
212         endo_id = le16_to_cpu(hello_request->endo_id);
213         interface_id = hello_request->interface_id;
214
215         /* Setup Endo */
216         ret = greybus_endo_setup(hd, endo_id, interface_id);
217         if (ret)
218                 return ret;
219
220         /*
221          * Endo and its modules are ready now, fix AP's partially initialized
222          * svc protocol and its connection.
223          */
224         intf = gb_ap_interface_create(hd, connection, interface_id);
225         if (!intf) {
226                 gb_endo_remove(hd->endo);
227                 return ret;
228         }
229
230         return 0;
231 }
232
233 static int gb_svc_intf_hotplug_recv(struct gb_operation *op)
234 {
235         struct gb_message *request = op->request;
236         struct gb_svc_intf_hotplug_request *hotplug = request->payload;
237         struct gb_svc *svc = op->connection->private;
238         struct greybus_host_device *hd = op->connection->bundle->intf->hd;
239         struct device *dev = &op->connection->dev;
240         struct gb_interface *intf;
241         u8 intf_id, device_id;
242         u32 unipro_mfg_id;
243         u32 unipro_prod_id;
244         u32 ara_vend_id;
245         u32 ara_prod_id;
246         int ret;
247
248         if (request->payload_size < sizeof(*hotplug)) {
249                 dev_err(dev, "%s: short hotplug request received\n", __func__);
250                 return -EINVAL;
251         }
252
253         /*
254          * Grab the information we need.
255          *
256          * XXX I'd really like to acknowledge receipt, and then
257          * XXX continue processing the request.  There's no need
258          * XXX for the SVC to wait.  In fact, it might be best to
259          * XXX have the SVC get acknowledgement before we proceed.
260          */
261         intf_id = hotplug->intf_id;
262         unipro_mfg_id = le32_to_cpu(hotplug->data.unipro_mfg_id);
263         unipro_prod_id = le32_to_cpu(hotplug->data.unipro_prod_id);
264         ara_vend_id = le32_to_cpu(hotplug->data.ara_vend_id);
265         ara_prod_id = le32_to_cpu(hotplug->data.ara_prod_id);
266
267         // FIXME May require firmware download
268         intf = gb_interface_create(hd, intf_id);
269         if (!intf) {
270                 dev_err(dev, "%s: Failed to create interface with id %hhu\n",
271                         __func__, intf_id);
272                 return -EINVAL;
273         }
274
275         /*
276          * Create a device id for the interface:
277          * - device id 0 (GB_DEVICE_ID_SVC) belongs to the SVC
278          * - device id 1 (GB_DEVICE_ID_AP) belongs to the AP
279          *
280          * XXX Do we need to allocate device ID for SVC or the AP here? And what
281          * XXX about an AP with multiple interface blocks?
282          */
283         device_id = ida_simple_get(&greybus_svc_device_id_map,
284                                    GB_DEVICE_ID_MODULES_START, 0, GFP_ATOMIC);
285         if (device_id < 0) {
286                 ret = device_id;
287                 dev_err(dev, "%s: Failed to allocate device id for interface with id %hhu (%d)\n",
288                         __func__, intf_id, ret);
289                 goto destroy_interface;
290         }
291
292         ret = intf_device_id_operation(svc, intf_id, device_id);
293         if (ret) {
294                 dev_err(dev, "%s: Device id operation failed, interface %hhu device_id %hhu (%d)\n",
295                         __func__, intf_id, device_id, ret);
296                 goto ida_put;
297         }
298
299         ret = gb_interface_init(intf, device_id);
300         if (ret) {
301                 dev_err(dev, "%s: Failed to initialize interface, interface %hhu device_id %hhu (%d)\n",
302                         __func__, intf_id, device_id, ret);
303                 goto svc_id_free;
304         }
305
306         return 0;
307
308 svc_id_free:
309         /*
310          * XXX Should we tell SVC that this id doesn't belong to interface
311          * XXX anymore.
312          */
313 ida_put:
314         ida_simple_remove(&greybus_svc_device_id_map, device_id);
315 destroy_interface:
316         gb_interface_remove(hd, intf_id);
317
318         return ret;
319 }
320
321 static int gb_svc_intf_hot_unplug_recv(struct gb_operation *op)
322 {
323         struct gb_message *request = op->request;
324         struct gb_svc_intf_hot_unplug_request *hot_unplug = request->payload;
325         struct greybus_host_device *hd = op->connection->bundle->intf->hd;
326         struct device *dev = &op->connection->dev;
327         u8 device_id;
328         struct gb_interface *intf;
329         u8 intf_id;
330
331         if (request->payload_size < sizeof(*hot_unplug)) {
332                 dev_err(&op->connection->dev,
333                         "short hot unplug request received\n");
334                 return -EINVAL;
335         }
336
337         intf_id = hot_unplug->intf_id;
338
339         intf = gb_interface_find(hd, intf_id);
340         if (!intf) {
341                 dev_err(dev, "%s: Couldn't find interface for id %hhu\n",
342                         __func__, intf_id);
343                 return -EINVAL;
344         }
345
346         device_id = intf->device_id;
347         gb_interface_remove(hd, intf_id);
348         ida_simple_remove(&greybus_svc_device_id_map, device_id);
349
350         return 0;
351 }
352
353 static int gb_svc_intf_reset_recv(struct gb_operation *op)
354 {
355         struct gb_message *request = op->request;
356         struct gb_svc_intf_reset_request *reset;
357         u8 intf_id;
358
359         if (request->payload_size < sizeof(*reset)) {
360                 dev_err(&op->connection->dev,
361                         "short reset request received\n");
362                 return -EINVAL;
363         }
364         reset = request->payload;
365
366         intf_id = reset->intf_id;
367
368         /* FIXME Reset the interface here */
369
370         return 0;
371 }
372
373 static int gb_svc_request_recv(u8 type, struct gb_operation *op)
374 {
375         switch (type) {
376         case GB_SVC_TYPE_PROTOCOL_VERSION:
377                 return gb_svc_version_request(op);
378         case GB_SVC_TYPE_SVC_HELLO:
379                 return gb_svc_hello(op);
380         case GB_SVC_TYPE_INTF_HOTPLUG:
381                 return gb_svc_intf_hotplug_recv(op);
382         case GB_SVC_TYPE_INTF_HOT_UNPLUG:
383                 return gb_svc_intf_hot_unplug_recv(op);
384         case GB_SVC_TYPE_INTF_RESET:
385                 return gb_svc_intf_reset_recv(op);
386         default:
387                 dev_err(&op->connection->dev,
388                         "unsupported request: %hhu\n", type);
389                 return -EINVAL;
390         }
391 }
392
393 /*
394  * Do initial setup of the SVC.
395  */
396 static int gb_svc_device_setup(struct gb_svc *gb_svc)
397 {
398         /* First thing we need to do is check the version */
399         return get_version(gb_svc);
400 }
401
402 static int gb_svc_connection_init(struct gb_connection *connection)
403 {
404         struct gb_svc *svc;
405         int ret;
406
407         svc = kzalloc(sizeof(*svc), GFP_KERNEL);
408         if (!svc)
409                 return -ENOMEM;
410
411         svc->connection = connection;
412         connection->private = svc;
413
414         /*
415          * SVC connection is created twice:
416          * - before the interface-id of the AP and the endo type is known.
417          * - after receiving endo type and interface-id of the AP from the SVC.
418          *
419          * We should do light-weight initialization for the first case.
420          */
421         if (!connection->bundle) {
422                 WARN_ON(connection->hd->initial_svc_connection);
423                 connection->hd->initial_svc_connection = connection;
424                 return 0;
425         }
426
427         ida_init(&greybus_svc_device_id_map);
428
429         ret = gb_svc_device_setup(svc);
430         if (ret)
431                 kfree(svc);
432
433         /* Set interface's svc connection */
434         connection->bundle->intf->svc = svc;
435
436         return ret;
437 }
438
439 static void gb_svc_connection_exit(struct gb_connection *connection)
440 {
441         struct gb_svc *svc = connection->private;
442
443         if (connection->hd->initial_svc_connection == connection) {
444                 connection->hd->initial_svc_connection = NULL;
445         } else {
446                 if (WARN_ON(connection->bundle->intf->svc != svc))
447                         return;
448                 connection->bundle->intf->svc = NULL;
449         }
450
451         connection->private = NULL;
452         kfree(svc);
453 }
454
455 static struct gb_protocol svc_protocol = {
456         .name                   = "svc",
457         .id                     = GREYBUS_PROTOCOL_SVC,
458         .major                  = GB_SVC_VERSION_MAJOR,
459         .minor                  = GB_SVC_VERSION_MINOR,
460         .connection_init        = gb_svc_connection_init,
461         .connection_exit        = gb_svc_connection_exit,
462         .request_recv           = gb_svc_request_recv,
463 };
464 gb_builtin_protocol_driver(svc_protocol);