4a45a943470d990054f10c6db162aaf880d95888
[cascardo/linux.git] / drivers / staging / greybus / legacy.c
1 /*
2  * Greybus legacy-protocol 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 "legacy.h"
12 #include "protocol.h"
13
14
15 struct legacy_connection {
16         struct gb_connection *connection;
17         bool initialized;
18         struct gb_protocol *protocol;
19 };
20
21 struct legacy_data {
22         size_t num_cports;
23         struct legacy_connection *connections;
24 };
25
26
27 static int legacy_connection_get_version(struct gb_connection *connection)
28 {
29         int ret;
30
31         ret = gb_protocol_get_version(connection);
32         if (ret) {
33                 dev_err(&connection->hd->dev,
34                         "%s: failed to get protocol version: %d\n",
35                         connection->name, ret);
36                 return ret;
37         }
38
39         return 0;
40 }
41
42 static int legacy_request_handler(struct gb_operation *operation)
43 {
44         struct gb_protocol *protocol = operation->connection->protocol;
45
46         return protocol->request_recv(operation->type, operation);
47 }
48
49 static int legacy_connection_init(struct legacy_connection *lc)
50 {
51         struct gb_connection *connection = lc->connection;
52         int ret;
53
54         dev_dbg(&connection->bundle->dev, "%s - %s\n", __func__,
55                         connection->name);
56
57         ret = gb_connection_enable(connection);
58         if (ret)
59                 return ret;
60
61         ret = legacy_connection_get_version(connection);
62         if (ret)
63                 goto err_disable;
64
65         ret = connection->protocol->connection_init(connection);
66         if (ret)
67                 goto err_disable;
68
69         lc->initialized = true;
70
71         return 0;
72
73 err_disable:
74         gb_connection_disable(connection);
75
76         return ret;
77 }
78
79 static void legacy_connection_exit(struct legacy_connection *lc)
80 {
81         struct gb_connection *connection = lc->connection;
82
83         if (!lc->initialized)
84                 return;
85
86         gb_connection_disable(connection);
87
88         connection->protocol->connection_exit(connection);
89
90         lc->initialized = false;
91 }
92
93 static int legacy_connection_create(struct legacy_connection *lc,
94                                         struct gb_bundle *bundle,
95                                         struct greybus_descriptor_cport *desc)
96 {
97         struct gb_connection *connection;
98         struct gb_protocol *protocol;
99         gb_request_handler_t handler;
100         u8 major, minor;
101         int ret;
102
103         /*
104          * The legacy protocols have always been looked up using a hard-coded
105          * version of 0.1, despite (or perhaps rather, due to) the fact that
106          * module version negotiation could not take place until after the
107          * protocol was bound.
108          */
109         major = 0;
110         minor = 1;
111
112         protocol = gb_protocol_get(desc->protocol_id, major, minor);
113         if (!protocol) {
114                 dev_err(&bundle->dev,
115                                 "protocol 0x%02x version %u.%u not found\n",
116                                 desc->protocol_id, major, minor);
117                 return -EPROTONOSUPPORT;
118         }
119
120         if (protocol->request_recv)
121                 handler = legacy_request_handler;
122         else
123                 handler = NULL;
124
125         connection = gb_connection_create(bundle, le16_to_cpu(desc->id),
126                                                 handler);
127         if (IS_ERR(connection)) {
128                 ret = PTR_ERR(connection);
129                 goto err_protocol_put;
130         }
131
132         /*
133          * NOTE: We need to keep a pointer to the protocol in the actual
134          *       connection structure for now.
135          */
136         connection->protocol = protocol;
137
138         lc->connection = connection;
139         lc->protocol = protocol;
140
141         return 0;
142
143 err_protocol_put:
144         gb_protocol_put(protocol);
145
146         return ret;
147 }
148
149 static void legacy_connection_destroy(struct legacy_connection *lc)
150 {
151         lc->connection->protocol = NULL;
152
153         gb_connection_destroy(lc->connection);
154
155         gb_protocol_put(lc->protocol);
156 }
157
158 static int legacy_probe(struct gb_bundle *bundle,
159                         const struct greybus_bundle_id *id)
160 {
161         struct greybus_descriptor_cport *cport_desc;
162         struct legacy_data *data;
163         struct legacy_connection *lc;
164         int i;
165         int ret;
166
167         dev_dbg(&bundle->dev,
168                         "%s - bundle class = 0x%02x, num_cports = %zu\n",
169                         __func__, bundle->class, bundle->num_cports);
170
171         data = kzalloc(sizeof(*data), GFP_KERNEL);
172         if (!data)
173                 return -ENOMEM;
174
175         data->num_cports = bundle->num_cports;
176         data->connections = kcalloc(data->num_cports,
177                                                 sizeof(*data->connections),
178                                                 GFP_KERNEL);
179         if (!data->connections) {
180                 ret = -ENOMEM;
181                 goto err_free_data;
182         }
183
184         for (i = 0; i < data->num_cports; ++i) {
185                 cport_desc = &bundle->cport_desc[i];
186                 lc = &data->connections[i];
187
188                 ret = legacy_connection_create(lc, bundle, cport_desc);
189                 if (ret)
190                         goto err_connections_destroy;
191         }
192
193         greybus_set_drvdata(bundle, data);
194
195         for (i = 0; i < data->num_cports; ++i) {
196                 lc = &data->connections[i];
197
198                 ret = legacy_connection_init(lc);
199                 if (ret)
200                         goto err_connections_disable;
201         }
202
203         return 0;
204
205 err_connections_disable:
206         for (--i; i >= 0; --i)
207                 legacy_connection_exit(&data->connections[i]);
208 err_connections_destroy:
209         for (i = 0; i < data->num_cports; ++i)
210                 legacy_connection_destroy(&data->connections[i]);
211         kfree(data->connections);
212 err_free_data:
213         kfree(data);
214
215         return ret;
216 }
217
218 static void legacy_disconnect(struct gb_bundle *bundle)
219 {
220         struct legacy_data *data = greybus_get_drvdata(bundle);
221         int i;
222
223         dev_dbg(&bundle->dev, "%s - bundle class = 0x%02x\n", __func__,
224                         bundle->class);
225
226         for (i = 0; i < data->num_cports; ++i) {
227                 legacy_connection_exit(&data->connections[i]);
228                 legacy_connection_destroy(&data->connections[i]);
229         }
230
231         kfree(data->connections);
232         kfree(data);
233 }
234
235 static const struct greybus_bundle_id legacy_id_table[] = {
236         { GREYBUS_DEVICE_CLASS(GREYBUS_CLASS_GPIO) },
237         { GREYBUS_DEVICE_CLASS(GREYBUS_CLASS_I2C) },
238         { GREYBUS_DEVICE_CLASS(GREYBUS_CLASS_UART) },
239         { GREYBUS_DEVICE_CLASS(GREYBUS_CLASS_USB) },
240         { GREYBUS_DEVICE_CLASS(GREYBUS_CLASS_SDIO) },
241         { GREYBUS_DEVICE_CLASS(GREYBUS_CLASS_POWER_SUPPLY) },
242         { GREYBUS_DEVICE_CLASS(GREYBUS_CLASS_PWM) },
243         { GREYBUS_DEVICE_CLASS(GREYBUS_CLASS_SPI) },
244         { GREYBUS_DEVICE_CLASS(GREYBUS_CLASS_DISPLAY) },
245         { GREYBUS_DEVICE_CLASS(GREYBUS_CLASS_CAMERA) },
246         { GREYBUS_DEVICE_CLASS(GREYBUS_CLASS_SENSOR) },
247         { GREYBUS_DEVICE_CLASS(GREYBUS_CLASS_LIGHTS) },
248         { GREYBUS_DEVICE_CLASS(GREYBUS_CLASS_LOOPBACK) },
249         { GREYBUS_DEVICE_CLASS(GREYBUS_CLASS_AUDIO_MGMT) },
250         { GREYBUS_DEVICE_CLASS(GREYBUS_CLASS_AUDIO_DATA) },
251         { GREYBUS_DEVICE_CLASS(GREYBUS_CLASS_SVC) },
252         { GREYBUS_DEVICE_CLASS(GREYBUS_CLASS_FIRMWARE) },
253         { GREYBUS_DEVICE_CLASS(GREYBUS_CLASS_RAW) },
254         { }
255 };
256 MODULE_DEVICE_TABLE(greybus, legacy_id_table);
257
258 static struct greybus_driver legacy_driver = {
259         .name           = "legacy",
260         .probe          = legacy_probe,
261         .disconnect     = legacy_disconnect,
262         .id_table       = legacy_id_table,
263 };
264
265 int gb_legacy_init(void)
266 {
267         return greybus_register(&legacy_driver);
268 }
269
270 void gb_legacy_exit(void)
271 {
272         greybus_deregister(&legacy_driver);
273 }