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