blob: 58b277979a608e6a4fe78ffdc9597d731cce3a98 [file] [log] [blame]
Johan Hovold5dda7e52016-01-19 12:50:59 +01001/*
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"
Johan Hovold50dfb872016-01-19 12:51:16 +010012#include "protocol.h"
13
14
Johan Hovold431b3eb2016-01-21 17:34:17 +010015struct legacy_connection {
16 struct gb_connection *connection;
Johan Hovold8278fae2016-01-21 17:34:18 +010017 bool initialized;
Johan Hovold23990322016-01-21 17:34:20 +010018 struct gb_protocol *protocol;
Johan Hovold431b3eb2016-01-21 17:34:17 +010019};
20
Johan Hovold7d81baf2016-01-19 12:51:28 +010021struct legacy_data {
22 size_t num_cports;
Johan Hovold431b3eb2016-01-21 17:34:17 +010023 struct legacy_connection *connections;
Johan Hovold7d81baf2016-01-19 12:51:28 +010024};
25
26
Johan Hovold50dfb872016-01-19 12:51:16 +010027static 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
Johan Hovold50dfb872016-01-19 12:51:16 +010042static 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
Johan Hovold431b3eb2016-01-21 17:34:17 +010049static int legacy_connection_init(struct legacy_connection *lc)
Johan Hovold50dfb872016-01-19 12:51:16 +010050{
Johan Hovold431b3eb2016-01-21 17:34:17 +010051 struct gb_connection *connection = lc->connection;
Johan Hovold50dfb872016-01-19 12:51:16 +010052 int ret;
53
Johan Hovold431b3eb2016-01-21 17:34:17 +010054 dev_dbg(&connection->bundle->dev, "%s - %s\n", __func__,
55 connection->name);
56
Johan Hovoldf7ee0812016-01-21 17:34:21 +010057 ret = gb_connection_enable(connection);
Johan Hovold50dfb872016-01-19 12:51:16 +010058 if (ret)
Johan Hovold23990322016-01-21 17:34:20 +010059 return ret;
Johan Hovold50dfb872016-01-19 12:51:16 +010060
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
Johan Hovold8278fae2016-01-21 17:34:18 +010069 lc->initialized = true;
70
Johan Hovold50dfb872016-01-19 12:51:16 +010071 return 0;
72
73err_disable:
74 gb_connection_disable(connection);
Johan Hovold50dfb872016-01-19 12:51:16 +010075
76 return ret;
77}
78
Johan Hovold431b3eb2016-01-21 17:34:17 +010079static void legacy_connection_exit(struct legacy_connection *lc)
Johan Hovold50dfb872016-01-19 12:51:16 +010080{
Johan Hovold431b3eb2016-01-21 17:34:17 +010081 struct gb_connection *connection = lc->connection;
82
Johan Hovold8278fae2016-01-21 17:34:18 +010083 if (!lc->initialized)
Johan Hovold50dfb872016-01-19 12:51:16 +010084 return;
85
86 gb_connection_disable(connection);
87
88 connection->protocol->connection_exit(connection);
89
Johan Hovold8278fae2016-01-21 17:34:18 +010090 lc->initialized = false;
Johan Hovold50dfb872016-01-19 12:51:16 +010091}
Johan Hovold5dda7e52016-01-19 12:50:59 +010092
Johan Hovold431b3eb2016-01-21 17:34:17 +010093static 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;
Johan Hovold23990322016-01-21 17:34:20 +010098 struct gb_protocol *protocol;
Johan Hovoldf7ee0812016-01-21 17:34:21 +010099 gb_request_handler_t handler;
Johan Hovold23990322016-01-21 17:34:20 +0100100 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 }
Johan Hovold431b3eb2016-01-21 17:34:17 +0100119
Johan Hovoldf7ee0812016-01-21 17:34:21 +0100120 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);
Johan Hovold23990322016-01-21 17:34:20 +0100127 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;
Johan Hovold431b3eb2016-01-21 17:34:17 +0100137
138 lc->connection = connection;
Johan Hovold23990322016-01-21 17:34:20 +0100139 lc->protocol = protocol;
Johan Hovold431b3eb2016-01-21 17:34:17 +0100140
141 return 0;
Johan Hovold23990322016-01-21 17:34:20 +0100142
143err_protocol_put:
144 gb_protocol_put(protocol);
145
146 return ret;
Johan Hovold431b3eb2016-01-21 17:34:17 +0100147}
148
149static void legacy_connection_destroy(struct legacy_connection *lc)
150{
Bartosz Golaszewski6e304f52016-01-27 13:17:52 +0100151 if (!lc->connection)
152 return;
153
Johan Hovold23990322016-01-21 17:34:20 +0100154 lc->connection->protocol = NULL;
155
Johan Hovold431b3eb2016-01-21 17:34:17 +0100156 gb_connection_destroy(lc->connection);
Johan Hovold23990322016-01-21 17:34:20 +0100157
158 gb_protocol_put(lc->protocol);
Johan Hovold431b3eb2016-01-21 17:34:17 +0100159}
160
Johan Hovold5dda7e52016-01-19 12:50:59 +0100161static int legacy_probe(struct gb_bundle *bundle,
162 const struct greybus_bundle_id *id)
163{
Johan Hovold98fdf5a2016-01-21 17:34:09 +0100164 struct greybus_descriptor_cport *cport_desc;
Johan Hovold7d81baf2016-01-19 12:51:28 +0100165 struct legacy_data *data;
Johan Hovold431b3eb2016-01-21 17:34:17 +0100166 struct legacy_connection *lc;
Johan Hovold98fdf5a2016-01-21 17:34:09 +0100167 int i;
Johan Hovold24e094d2016-01-21 17:34:16 +0100168 int ret;
Johan Hovold98fdf5a2016-01-21 17:34:09 +0100169
170 dev_dbg(&bundle->dev,
171 "%s - bundle class = 0x%02x, num_cports = %zu\n",
172 __func__, bundle->class, bundle->num_cports);
Johan Hovold5dda7e52016-01-19 12:50:59 +0100173
Johan Hovold7d81baf2016-01-19 12:51:28 +0100174 data = kzalloc(sizeof(*data), GFP_KERNEL);
175 if (!data)
176 return -ENOMEM;
177
Johan Hovold98fdf5a2016-01-21 17:34:09 +0100178 data->num_cports = bundle->num_cports;
179 data->connections = kcalloc(data->num_cports,
180 sizeof(*data->connections),
181 GFP_KERNEL);
Johan Hovold24e094d2016-01-21 17:34:16 +0100182 if (!data->connections) {
183 ret = -ENOMEM;
Johan Hovold98fdf5a2016-01-21 17:34:09 +0100184 goto err_free_data;
Johan Hovold24e094d2016-01-21 17:34:16 +0100185 }
Johan Hovold7d81baf2016-01-19 12:51:28 +0100186
Johan Hovold98fdf5a2016-01-21 17:34:09 +0100187 for (i = 0; i < data->num_cports; ++i) {
188 cport_desc = &bundle->cport_desc[i];
Johan Hovold431b3eb2016-01-21 17:34:17 +0100189 lc = &data->connections[i];
Johan Hovold98fdf5a2016-01-21 17:34:09 +0100190
Johan Hovold431b3eb2016-01-21 17:34:17 +0100191 ret = legacy_connection_create(lc, bundle, cport_desc);
192 if (ret)
Johan Hovold98fdf5a2016-01-21 17:34:09 +0100193 goto err_connections_destroy;
Johan Hovold98fdf5a2016-01-21 17:34:09 +0100194 }
Johan Hovold7d81baf2016-01-19 12:51:28 +0100195
196 greybus_set_drvdata(bundle, data);
Johan Hovold5dda7e52016-01-19 12:50:59 +0100197
Johan Hovold98fdf5a2016-01-21 17:34:09 +0100198 for (i = 0; i < data->num_cports; ++i) {
Johan Hovold431b3eb2016-01-21 17:34:17 +0100199 lc = &data->connections[i];
Johan Hovold5dda7e52016-01-19 12:50:59 +0100200
Johan Hovold431b3eb2016-01-21 17:34:17 +0100201 ret = legacy_connection_init(lc);
Johan Hovold5dda7e52016-01-19 12:50:59 +0100202 if (ret)
203 goto err_connections_disable;
204 }
205
206 return 0;
207
208err_connections_disable:
Johan Hovold98fdf5a2016-01-21 17:34:09 +0100209 for (--i; i >= 0; --i)
Johan Hovold431b3eb2016-01-21 17:34:17 +0100210 legacy_connection_exit(&data->connections[i]);
Johan Hovold98fdf5a2016-01-21 17:34:09 +0100211err_connections_destroy:
212 for (i = 0; i < data->num_cports; ++i)
Johan Hovold431b3eb2016-01-21 17:34:17 +0100213 legacy_connection_destroy(&data->connections[i]);
Johan Hovold98fdf5a2016-01-21 17:34:09 +0100214 kfree(data->connections);
215err_free_data:
Johan Hovold7d81baf2016-01-19 12:51:28 +0100216 kfree(data);
Johan Hovold5dda7e52016-01-19 12:50:59 +0100217
218 return ret;
219}
220
221static void legacy_disconnect(struct gb_bundle *bundle)
222{
Johan Hovold7d81baf2016-01-19 12:51:28 +0100223 struct legacy_data *data = greybus_get_drvdata(bundle);
Johan Hovold98fdf5a2016-01-21 17:34:09 +0100224 int i;
Johan Hovold5dda7e52016-01-19 12:50:59 +0100225
226 dev_dbg(&bundle->dev, "%s - bundle class = 0x%02x\n", __func__,
227 bundle->class);
228
Johan Hovold98fdf5a2016-01-21 17:34:09 +0100229 for (i = 0; i < data->num_cports; ++i) {
Johan Hovold431b3eb2016-01-21 17:34:17 +0100230 legacy_connection_exit(&data->connections[i]);
231 legacy_connection_destroy(&data->connections[i]);
Johan Hovold5dda7e52016-01-19 12:50:59 +0100232 }
Johan Hovold7d81baf2016-01-19 12:51:28 +0100233
Johan Hovold98fdf5a2016-01-21 17:34:09 +0100234 kfree(data->connections);
Johan Hovold7d81baf2016-01-19 12:51:28 +0100235 kfree(data);
Johan Hovold5dda7e52016-01-19 12:50:59 +0100236}
237
238static const struct greybus_bundle_id legacy_id_table[] = {
239 { GREYBUS_DEVICE_CLASS(GREYBUS_CLASS_GPIO) },
240 { GREYBUS_DEVICE_CLASS(GREYBUS_CLASS_I2C) },
241 { GREYBUS_DEVICE_CLASS(GREYBUS_CLASS_UART) },
Johan Hovold5dda7e52016-01-19 12:50:59 +0100242 { GREYBUS_DEVICE_CLASS(GREYBUS_CLASS_USB) },
243 { GREYBUS_DEVICE_CLASS(GREYBUS_CLASS_SDIO) },
Johan Hovold5dda7e52016-01-19 12:50:59 +0100244 { GREYBUS_DEVICE_CLASS(GREYBUS_CLASS_PWM) },
245 { GREYBUS_DEVICE_CLASS(GREYBUS_CLASS_SPI) },
Johan Hovold5dda7e52016-01-19 12:50:59 +0100246 { GREYBUS_DEVICE_CLASS(GREYBUS_CLASS_CAMERA) },
Johan Hovold5dda7e52016-01-19 12:50:59 +0100247 { }
248};
249MODULE_DEVICE_TABLE(greybus, legacy_id_table);
250
251static struct greybus_driver legacy_driver = {
252 .name = "legacy",
253 .probe = legacy_probe,
254 .disconnect = legacy_disconnect,
255 .id_table = legacy_id_table,
256};
257
258int gb_legacy_init(void)
259{
260 return greybus_register(&legacy_driver);
261}
262
263void gb_legacy_exit(void)
264{
265 greybus_deregister(&legacy_driver);
266}