greybus: svc: remove interface->svc pointer
[cascardo/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         u8                      version_major;
15         u8                      version_minor;
16 };
17
18 static struct ida greybus_svc_device_id_map;
19
20 /*
21  * AP's SVC cport is required early to get messages from the SVC. This happens
22  * even before the Endo is created and hence any modules or interfaces.
23  *
24  * This is a temporary connection, used only at initial bootup.
25  */
26 struct gb_connection *
27 gb_ap_svc_connection_create(struct greybus_host_device *hd)
28 {
29         struct gb_connection *connection;
30
31         connection = gb_connection_create_range(hd, NULL, hd->parent,
32                                                 GB_SVC_CPORT_ID,
33                                                 GREYBUS_PROTOCOL_SVC,
34                                                 GB_SVC_CPORT_ID,
35                                                 GB_SVC_CPORT_ID + 1);
36
37         return connection;
38 }
39 EXPORT_SYMBOL_GPL(gb_ap_svc_connection_create);
40
41 /*
42  * We know endo-type and AP's interface id now, lets create a proper svc
43  * connection (and its interface/bundle) now and get rid of the initial
44  * 'partially' initialized one svc connection.
45  */
46 static struct gb_interface *
47 gb_ap_interface_create(struct greybus_host_device *hd,
48                        struct gb_connection *connection, u8 interface_id)
49 {
50         struct gb_interface *intf;
51         struct device *dev = &hd->endo->dev;
52
53         intf = gb_interface_create(hd, interface_id);
54         if (!intf) {
55                 dev_err(dev, "%s: Failed to create interface with id %hhu\n",
56                         __func__, interface_id);
57                 return NULL;
58         }
59
60         intf->device_id = GB_DEVICE_ID_AP;
61         svc_update_connection(intf, connection);
62
63         /* Its no longer a partially initialized connection */
64         hd->initial_svc_connection = NULL;
65
66         return intf;
67 }
68
69 static int intf_device_id_operation(struct gb_svc *svc,
70                                 u8 intf_id, u8 device_id)
71 {
72         struct gb_svc_intf_device_id_request request;
73
74         request.intf_id = intf_id;
75         request.device_id = device_id;
76
77         return gb_operation_sync(svc->connection, GB_SVC_TYPE_INTF_DEVICE_ID,
78                                  &request, sizeof(request), NULL, 0);
79 }
80
81 static int intf_reset_operation(struct gb_svc *svc, u8 intf_id)
82 {
83         struct gb_svc_intf_reset_request request;
84
85         request.intf_id = intf_id;
86
87         return gb_operation_sync(svc->connection, GB_SVC_TYPE_INTF_RESET,
88                                  &request, sizeof(request), NULL, 0);
89 }
90
91 static int connection_create_operation(struct gb_svc *svc,
92                                 u8 intf1_id, u16 cport1_id,
93                                 u8 intf2_id, u16 cport2_id)
94 {
95         struct gb_svc_conn_create_request request;
96
97         request.intf1_id = intf1_id;
98         request.cport1_id = cport1_id;
99         request.intf2_id = intf2_id;
100         request.cport2_id = cport2_id;
101
102         return gb_operation_sync(svc->connection, GB_SVC_TYPE_CONN_CREATE,
103                                  &request, sizeof(request), NULL, 0);
104 }
105
106 static int connection_destroy_operation(struct gb_svc *svc,
107                                 u8 intf1_id, u16 cport1_id,
108                                 u8 intf2_id, u16 cport2_id)
109 {
110         struct gb_svc_conn_destroy_request request;
111
112         request.intf1_id = intf1_id;
113         request.cport1_id = cport1_id;
114         request.intf2_id = intf2_id;
115         request.cport2_id = cport2_id;
116
117         return gb_operation_sync(svc->connection, GB_SVC_TYPE_CONN_DESTROY,
118                                  &request, sizeof(request), NULL, 0);
119 }
120
121 int gb_svc_intf_device_id(struct gb_svc *svc, u8 intf_id, u8 device_id)
122 {
123         return intf_device_id_operation(svc, intf_id, device_id);
124 }
125 EXPORT_SYMBOL_GPL(gb_svc_intf_device_id);
126
127 int gb_svc_intf_reset(struct gb_svc *svc, u8 intf_id)
128 {
129         return intf_reset_operation(svc, intf_id);
130 }
131 EXPORT_SYMBOL_GPL(gb_svc_intf_reset);
132
133 int gb_svc_connection_create(struct gb_svc *svc,
134                                 u8 intf1_id, u16 cport1_id,
135                                 u8 intf2_id, u16 cport2_id)
136 {
137         return connection_create_operation(svc, intf1_id, cport1_id,
138                                                 intf2_id, cport2_id);
139 }
140 EXPORT_SYMBOL_GPL(gb_svc_connection_create);
141
142 int gb_svc_connection_destroy(struct gb_svc *svc,
143                                 u8 intf1_id, u16 cport1_id,
144                                 u8 intf2_id, u16 cport2_id)
145 {
146         return connection_destroy_operation(svc, intf1_id, cport1_id,
147                                                 intf2_id, cport2_id);
148 }
149 EXPORT_SYMBOL_GPL(gb_svc_connection_destroy);
150
151 static int gb_svc_version_request(struct gb_operation *op)
152 {
153         struct gb_connection *connection = op->connection;
154         struct gb_protocol_version_response *version;
155         struct device *dev = &connection->dev;
156
157         version = op->request->payload;
158
159         if (version->major > GB_SVC_VERSION_MAJOR) {
160                 dev_err(&connection->dev,
161                         "unsupported major version (%hhu > %hhu)\n",
162                         version->major, GB_SVC_VERSION_MAJOR);
163                 return -ENOTSUPP;
164         }
165
166         if (!gb_operation_response_alloc(op, sizeof(*version), GFP_KERNEL)) {
167                 dev_err(dev, "%s: error allocating response\n",
168                                 __func__);
169                 return -ENOMEM;
170         }
171
172         version = op->response->payload;
173         version->major = GB_SVC_VERSION_MAJOR;
174         version->minor = GB_SVC_VERSION_MINOR;
175         return 0;
176 }
177
178 static int gb_svc_hello(struct gb_operation *op)
179 {
180         struct gb_connection *connection = op->connection;
181         struct greybus_host_device *hd = connection->hd;
182         struct gb_svc_hello_request *hello_request;
183         struct device *dev = &connection->dev;
184         struct gb_interface *intf;
185         u16 endo_id;
186         u8 interface_id;
187         int ret;
188
189         /* Hello message should be received only during early bootup */
190         WARN_ON(hd->initial_svc_connection != connection);
191
192         /*
193          * SVC sends information about the endo and interface-id on the hello
194          * request, use that to create an endo.
195          */
196         if (op->request->payload_size != sizeof(*hello_request)) {
197                 dev_err(dev, "%s: Illegal size of hello request (%zu %zu)\n",
198                         __func__, op->request->payload_size,
199                         sizeof(*hello_request));
200                 return -EINVAL;
201         }
202
203         hello_request = op->request->payload;
204         endo_id = le16_to_cpu(hello_request->endo_id);
205         interface_id = hello_request->interface_id;
206
207         /* Setup Endo */
208         ret = greybus_endo_setup(hd, endo_id, interface_id);
209         if (ret)
210                 return ret;
211
212         /*
213          * Endo and its modules are ready now, fix AP's partially initialized
214          * svc protocol and its connection.
215          */
216         intf = gb_ap_interface_create(hd, connection, interface_id);
217         if (!intf) {
218                 gb_endo_remove(hd->endo);
219                 return ret;
220         }
221
222         return 0;
223 }
224
225 static int gb_svc_intf_hotplug_recv(struct gb_operation *op)
226 {
227         struct gb_message *request = op->request;
228         struct gb_svc_intf_hotplug_request *hotplug = request->payload;
229         struct gb_svc *svc = op->connection->private;
230         struct greybus_host_device *hd = op->connection->bundle->intf->hd;
231         struct device *dev = &op->connection->dev;
232         struct gb_interface *intf;
233         u8 intf_id, device_id;
234         u32 unipro_mfg_id;
235         u32 unipro_prod_id;
236         u32 ara_vend_id;
237         u32 ara_prod_id;
238         int ret;
239
240         if (request->payload_size < sizeof(*hotplug)) {
241                 dev_err(dev, "%s: short hotplug request received\n", __func__);
242                 return -EINVAL;
243         }
244
245         /*
246          * Grab the information we need.
247          *
248          * XXX I'd really like to acknowledge receipt, and then
249          * XXX continue processing the request.  There's no need
250          * XXX for the SVC to wait.  In fact, it might be best to
251          * XXX have the SVC get acknowledgement before we proceed.
252          */
253         intf_id = hotplug->intf_id;
254         unipro_mfg_id = le32_to_cpu(hotplug->data.unipro_mfg_id);
255         unipro_prod_id = le32_to_cpu(hotplug->data.unipro_prod_id);
256         ara_vend_id = le32_to_cpu(hotplug->data.ara_vend_id);
257         ara_prod_id = le32_to_cpu(hotplug->data.ara_prod_id);
258
259         // FIXME May require firmware download
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);