Johan Hovold | 5dda7e5 | 2016-01-19 12:50:59 +0100 | [diff] [blame] | 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" |
Johan Hovold | 50dfb87 | 2016-01-19 12:51:16 +0100 | [diff] [blame] | 12 | #include "protocol.h" |
| 13 | |
| 14 | |
Johan Hovold | 431b3eb | 2016-01-21 17:34:17 +0100 | [diff] [blame] | 15 | struct legacy_connection { |
| 16 | struct gb_connection *connection; |
Johan Hovold | 8278fae | 2016-01-21 17:34:18 +0100 | [diff] [blame] | 17 | bool initialized; |
Johan Hovold | 2399032 | 2016-01-21 17:34:20 +0100 | [diff] [blame] | 18 | struct gb_protocol *protocol; |
Johan Hovold | 431b3eb | 2016-01-21 17:34:17 +0100 | [diff] [blame] | 19 | }; |
| 20 | |
Johan Hovold | 7d81baf | 2016-01-19 12:51:28 +0100 | [diff] [blame] | 21 | struct legacy_data { |
| 22 | size_t num_cports; |
Johan Hovold | 431b3eb | 2016-01-21 17:34:17 +0100 | [diff] [blame] | 23 | struct legacy_connection *connections; |
Johan Hovold | 7d81baf | 2016-01-19 12:51:28 +0100 | [diff] [blame] | 24 | }; |
| 25 | |
| 26 | |
Johan Hovold | 50dfb87 | 2016-01-19 12:51:16 +0100 | [diff] [blame] | 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 | |
Johan Hovold | 50dfb87 | 2016-01-19 12:51:16 +0100 | [diff] [blame] | 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 | |
Johan Hovold | 431b3eb | 2016-01-21 17:34:17 +0100 | [diff] [blame] | 49 | static int legacy_connection_init(struct legacy_connection *lc) |
Johan Hovold | 50dfb87 | 2016-01-19 12:51:16 +0100 | [diff] [blame] | 50 | { |
Johan Hovold | 431b3eb | 2016-01-21 17:34:17 +0100 | [diff] [blame] | 51 | struct gb_connection *connection = lc->connection; |
Johan Hovold | 50dfb87 | 2016-01-19 12:51:16 +0100 | [diff] [blame] | 52 | int ret; |
| 53 | |
Johan Hovold | 431b3eb | 2016-01-21 17:34:17 +0100 | [diff] [blame] | 54 | dev_dbg(&connection->bundle->dev, "%s - %s\n", __func__, |
| 55 | connection->name); |
| 56 | |
Johan Hovold | f7ee081 | 2016-01-21 17:34:21 +0100 | [diff] [blame] | 57 | ret = gb_connection_enable(connection); |
Johan Hovold | 50dfb87 | 2016-01-19 12:51:16 +0100 | [diff] [blame] | 58 | if (ret) |
Johan Hovold | 2399032 | 2016-01-21 17:34:20 +0100 | [diff] [blame] | 59 | return ret; |
Johan Hovold | 50dfb87 | 2016-01-19 12:51:16 +0100 | [diff] [blame] | 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 | |
Johan Hovold | 8278fae | 2016-01-21 17:34:18 +0100 | [diff] [blame] | 69 | lc->initialized = true; |
| 70 | |
Johan Hovold | 50dfb87 | 2016-01-19 12:51:16 +0100 | [diff] [blame] | 71 | return 0; |
| 72 | |
| 73 | err_disable: |
| 74 | gb_connection_disable(connection); |
Johan Hovold | 50dfb87 | 2016-01-19 12:51:16 +0100 | [diff] [blame] | 75 | |
| 76 | return ret; |
| 77 | } |
| 78 | |
Johan Hovold | 431b3eb | 2016-01-21 17:34:17 +0100 | [diff] [blame] | 79 | static void legacy_connection_exit(struct legacy_connection *lc) |
Johan Hovold | 50dfb87 | 2016-01-19 12:51:16 +0100 | [diff] [blame] | 80 | { |
Johan Hovold | 431b3eb | 2016-01-21 17:34:17 +0100 | [diff] [blame] | 81 | struct gb_connection *connection = lc->connection; |
| 82 | |
Johan Hovold | 8278fae | 2016-01-21 17:34:18 +0100 | [diff] [blame] | 83 | if (!lc->initialized) |
Johan Hovold | 50dfb87 | 2016-01-19 12:51:16 +0100 | [diff] [blame] | 84 | return; |
| 85 | |
| 86 | gb_connection_disable(connection); |
| 87 | |
| 88 | connection->protocol->connection_exit(connection); |
| 89 | |
Johan Hovold | 8278fae | 2016-01-21 17:34:18 +0100 | [diff] [blame] | 90 | lc->initialized = false; |
Johan Hovold | 50dfb87 | 2016-01-19 12:51:16 +0100 | [diff] [blame] | 91 | } |
Johan Hovold | 5dda7e5 | 2016-01-19 12:50:59 +0100 | [diff] [blame] | 92 | |
Johan Hovold | 431b3eb | 2016-01-21 17:34:17 +0100 | [diff] [blame] | 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; |
Johan Hovold | 2399032 | 2016-01-21 17:34:20 +0100 | [diff] [blame] | 98 | struct gb_protocol *protocol; |
Johan Hovold | f7ee081 | 2016-01-21 17:34:21 +0100 | [diff] [blame] | 99 | gb_request_handler_t handler; |
Johan Hovold | 2399032 | 2016-01-21 17:34:20 +0100 | [diff] [blame] | 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 | } |
Johan Hovold | 431b3eb | 2016-01-21 17:34:17 +0100 | [diff] [blame] | 119 | |
Johan Hovold | f7ee081 | 2016-01-21 17:34:21 +0100 | [diff] [blame] | 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); |
Johan Hovold | 2399032 | 2016-01-21 17:34:20 +0100 | [diff] [blame] | 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; |
Johan Hovold | 431b3eb | 2016-01-21 17:34:17 +0100 | [diff] [blame] | 137 | |
| 138 | lc->connection = connection; |
Johan Hovold | 2399032 | 2016-01-21 17:34:20 +0100 | [diff] [blame] | 139 | lc->protocol = protocol; |
Johan Hovold | 431b3eb | 2016-01-21 17:34:17 +0100 | [diff] [blame] | 140 | |
| 141 | return 0; |
Johan Hovold | 2399032 | 2016-01-21 17:34:20 +0100 | [diff] [blame] | 142 | |
| 143 | err_protocol_put: |
| 144 | gb_protocol_put(protocol); |
| 145 | |
| 146 | return ret; |
Johan Hovold | 431b3eb | 2016-01-21 17:34:17 +0100 | [diff] [blame] | 147 | } |
| 148 | |
| 149 | static void legacy_connection_destroy(struct legacy_connection *lc) |
| 150 | { |
Bartosz Golaszewski | 6e304f5 | 2016-01-27 13:17:52 +0100 | [diff] [blame] | 151 | if (!lc->connection) |
| 152 | return; |
| 153 | |
Johan Hovold | 2399032 | 2016-01-21 17:34:20 +0100 | [diff] [blame] | 154 | lc->connection->protocol = NULL; |
| 155 | |
Johan Hovold | 431b3eb | 2016-01-21 17:34:17 +0100 | [diff] [blame] | 156 | gb_connection_destroy(lc->connection); |
Johan Hovold | 2399032 | 2016-01-21 17:34:20 +0100 | [diff] [blame] | 157 | |
| 158 | gb_protocol_put(lc->protocol); |
Johan Hovold | 431b3eb | 2016-01-21 17:34:17 +0100 | [diff] [blame] | 159 | } |
| 160 | |
Johan Hovold | 5dda7e5 | 2016-01-19 12:50:59 +0100 | [diff] [blame] | 161 | static int legacy_probe(struct gb_bundle *bundle, |
| 162 | const struct greybus_bundle_id *id) |
| 163 | { |
Johan Hovold | 98fdf5a | 2016-01-21 17:34:09 +0100 | [diff] [blame] | 164 | struct greybus_descriptor_cport *cport_desc; |
Johan Hovold | 7d81baf | 2016-01-19 12:51:28 +0100 | [diff] [blame] | 165 | struct legacy_data *data; |
Johan Hovold | 431b3eb | 2016-01-21 17:34:17 +0100 | [diff] [blame] | 166 | struct legacy_connection *lc; |
Johan Hovold | 98fdf5a | 2016-01-21 17:34:09 +0100 | [diff] [blame] | 167 | int i; |
Johan Hovold | 24e094d | 2016-01-21 17:34:16 +0100 | [diff] [blame] | 168 | int ret; |
Johan Hovold | 98fdf5a | 2016-01-21 17:34:09 +0100 | [diff] [blame] | 169 | |
| 170 | dev_dbg(&bundle->dev, |
| 171 | "%s - bundle class = 0x%02x, num_cports = %zu\n", |
| 172 | __func__, bundle->class, bundle->num_cports); |
Johan Hovold | 5dda7e5 | 2016-01-19 12:50:59 +0100 | [diff] [blame] | 173 | |
Johan Hovold | 7d81baf | 2016-01-19 12:51:28 +0100 | [diff] [blame] | 174 | data = kzalloc(sizeof(*data), GFP_KERNEL); |
| 175 | if (!data) |
| 176 | return -ENOMEM; |
| 177 | |
Johan Hovold | 98fdf5a | 2016-01-21 17:34:09 +0100 | [diff] [blame] | 178 | data->num_cports = bundle->num_cports; |
| 179 | data->connections = kcalloc(data->num_cports, |
| 180 | sizeof(*data->connections), |
| 181 | GFP_KERNEL); |
Johan Hovold | 24e094d | 2016-01-21 17:34:16 +0100 | [diff] [blame] | 182 | if (!data->connections) { |
| 183 | ret = -ENOMEM; |
Johan Hovold | 98fdf5a | 2016-01-21 17:34:09 +0100 | [diff] [blame] | 184 | goto err_free_data; |
Johan Hovold | 24e094d | 2016-01-21 17:34:16 +0100 | [diff] [blame] | 185 | } |
Johan Hovold | 7d81baf | 2016-01-19 12:51:28 +0100 | [diff] [blame] | 186 | |
Johan Hovold | 98fdf5a | 2016-01-21 17:34:09 +0100 | [diff] [blame] | 187 | for (i = 0; i < data->num_cports; ++i) { |
| 188 | cport_desc = &bundle->cport_desc[i]; |
Johan Hovold | 431b3eb | 2016-01-21 17:34:17 +0100 | [diff] [blame] | 189 | lc = &data->connections[i]; |
Johan Hovold | 98fdf5a | 2016-01-21 17:34:09 +0100 | [diff] [blame] | 190 | |
Johan Hovold | 431b3eb | 2016-01-21 17:34:17 +0100 | [diff] [blame] | 191 | ret = legacy_connection_create(lc, bundle, cport_desc); |
| 192 | if (ret) |
Johan Hovold | 98fdf5a | 2016-01-21 17:34:09 +0100 | [diff] [blame] | 193 | goto err_connections_destroy; |
Johan Hovold | 98fdf5a | 2016-01-21 17:34:09 +0100 | [diff] [blame] | 194 | } |
Johan Hovold | 7d81baf | 2016-01-19 12:51:28 +0100 | [diff] [blame] | 195 | |
| 196 | greybus_set_drvdata(bundle, data); |
Johan Hovold | 5dda7e5 | 2016-01-19 12:50:59 +0100 | [diff] [blame] | 197 | |
Johan Hovold | 98fdf5a | 2016-01-21 17:34:09 +0100 | [diff] [blame] | 198 | for (i = 0; i < data->num_cports; ++i) { |
Johan Hovold | 431b3eb | 2016-01-21 17:34:17 +0100 | [diff] [blame] | 199 | lc = &data->connections[i]; |
Johan Hovold | 5dda7e5 | 2016-01-19 12:50:59 +0100 | [diff] [blame] | 200 | |
Johan Hovold | 431b3eb | 2016-01-21 17:34:17 +0100 | [diff] [blame] | 201 | ret = legacy_connection_init(lc); |
Johan Hovold | 5dda7e5 | 2016-01-19 12:50:59 +0100 | [diff] [blame] | 202 | if (ret) |
| 203 | goto err_connections_disable; |
| 204 | } |
| 205 | |
| 206 | return 0; |
| 207 | |
| 208 | err_connections_disable: |
Johan Hovold | 98fdf5a | 2016-01-21 17:34:09 +0100 | [diff] [blame] | 209 | for (--i; i >= 0; --i) |
Johan Hovold | 431b3eb | 2016-01-21 17:34:17 +0100 | [diff] [blame] | 210 | legacy_connection_exit(&data->connections[i]); |
Johan Hovold | 98fdf5a | 2016-01-21 17:34:09 +0100 | [diff] [blame] | 211 | err_connections_destroy: |
| 212 | for (i = 0; i < data->num_cports; ++i) |
Johan Hovold | 431b3eb | 2016-01-21 17:34:17 +0100 | [diff] [blame] | 213 | legacy_connection_destroy(&data->connections[i]); |
Johan Hovold | 98fdf5a | 2016-01-21 17:34:09 +0100 | [diff] [blame] | 214 | kfree(data->connections); |
| 215 | err_free_data: |
Johan Hovold | 7d81baf | 2016-01-19 12:51:28 +0100 | [diff] [blame] | 216 | kfree(data); |
Johan Hovold | 5dda7e5 | 2016-01-19 12:50:59 +0100 | [diff] [blame] | 217 | |
| 218 | return ret; |
| 219 | } |
| 220 | |
| 221 | static void legacy_disconnect(struct gb_bundle *bundle) |
| 222 | { |
Johan Hovold | 7d81baf | 2016-01-19 12:51:28 +0100 | [diff] [blame] | 223 | struct legacy_data *data = greybus_get_drvdata(bundle); |
Johan Hovold | 98fdf5a | 2016-01-21 17:34:09 +0100 | [diff] [blame] | 224 | int i; |
Johan Hovold | 5dda7e5 | 2016-01-19 12:50:59 +0100 | [diff] [blame] | 225 | |
| 226 | dev_dbg(&bundle->dev, "%s - bundle class = 0x%02x\n", __func__, |
| 227 | bundle->class); |
| 228 | |
Johan Hovold | 98fdf5a | 2016-01-21 17:34:09 +0100 | [diff] [blame] | 229 | for (i = 0; i < data->num_cports; ++i) { |
Johan Hovold | 431b3eb | 2016-01-21 17:34:17 +0100 | [diff] [blame] | 230 | legacy_connection_exit(&data->connections[i]); |
| 231 | legacy_connection_destroy(&data->connections[i]); |
Johan Hovold | 5dda7e5 | 2016-01-19 12:50:59 +0100 | [diff] [blame] | 232 | } |
Johan Hovold | 7d81baf | 2016-01-19 12:51:28 +0100 | [diff] [blame] | 233 | |
Johan Hovold | 98fdf5a | 2016-01-21 17:34:09 +0100 | [diff] [blame] | 234 | kfree(data->connections); |
Johan Hovold | 7d81baf | 2016-01-19 12:51:28 +0100 | [diff] [blame] | 235 | kfree(data); |
Johan Hovold | 5dda7e5 | 2016-01-19 12:50:59 +0100 | [diff] [blame] | 236 | } |
| 237 | |
| 238 | static 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 Hovold | 5dda7e5 | 2016-01-19 12:50:59 +0100 | [diff] [blame] | 242 | { GREYBUS_DEVICE_CLASS(GREYBUS_CLASS_USB) }, |
| 243 | { GREYBUS_DEVICE_CLASS(GREYBUS_CLASS_SDIO) }, |
Johan Hovold | 5dda7e5 | 2016-01-19 12:50:59 +0100 | [diff] [blame] | 244 | { GREYBUS_DEVICE_CLASS(GREYBUS_CLASS_PWM) }, |
| 245 | { GREYBUS_DEVICE_CLASS(GREYBUS_CLASS_SPI) }, |
Johan Hovold | 5dda7e5 | 2016-01-19 12:50:59 +0100 | [diff] [blame] | 246 | { GREYBUS_DEVICE_CLASS(GREYBUS_CLASS_CAMERA) }, |
Johan Hovold | 5dda7e5 | 2016-01-19 12:50:59 +0100 | [diff] [blame] | 247 | { } |
| 248 | }; |
| 249 | MODULE_DEVICE_TABLE(greybus, legacy_id_table); |
| 250 | |
| 251 | static struct greybus_driver legacy_driver = { |
| 252 | .name = "legacy", |
| 253 | .probe = legacy_probe, |
| 254 | .disconnect = legacy_disconnect, |
| 255 | .id_table = legacy_id_table, |
| 256 | }; |
| 257 | |
| 258 | int gb_legacy_init(void) |
| 259 | { |
| 260 | return greybus_register(&legacy_driver); |
| 261 | } |
| 262 | |
| 263 | void gb_legacy_exit(void) |
| 264 | { |
| 265 | greybus_deregister(&legacy_driver); |
| 266 | } |