blob: 34e26dc6e249ec41551ef3db31fbe4f10ce6c5bb [file] [log] [blame]
Alex Elderc68adb22014-10-01 21:54:14 -05001/*
2 * Greybus connections
3 *
4 * Copyright 2014 Google Inc.
Alex Eldera46e9672014-12-12 12:08:42 -06005 * Copyright 2014 Linaro Ltd.
Alex Elderc68adb22014-10-01 21:54:14 -05006 *
7 * Released under the GPLv2 only.
8 */
9
Johan Hovold5a5bc352015-07-23 10:50:02 +020010#include <linux/workqueue.h>
11
Alex Elderc68adb22014-10-01 21:54:14 -050012#include "greybus.h"
13
Johan Hovold2d54e4d2015-12-07 15:05:30 +010014
Johan Hovold0e46fab2016-01-19 12:51:25 +010015static void gb_connection_kref_release(struct kref *kref);
16
17
Alex Elder748e1232014-10-03 14:14:22 -050018static DEFINE_SPINLOCK(gb_connections_lock);
Johan Hovold210b5082016-01-19 12:51:26 +010019static DEFINE_MUTEX(gb_connection_mutex);
20
Alex Elder748e1232014-10-03 14:14:22 -050021
Johan Hovoldb53e0c92016-01-19 12:51:27 +010022/* Caller holds gb_connection_mutex. */
Alex Elderf5c2be92015-06-09 17:42:58 -050023static struct gb_connection *
Viresh Kumarc3a16172015-07-01 12:13:56 +053024gb_connection_intf_find(struct gb_interface *intf, u16 cport_id)
Alex Elderf5c2be92015-06-09 17:42:58 -050025{
Johan Hovold25376362015-11-03 18:03:23 +010026 struct gb_host_device *hd = intf->hd;
Alex Elderf5c2be92015-06-09 17:42:58 -050027 struct gb_connection *connection;
28
Johan Hovold0daf17b2015-11-25 15:59:12 +010029 list_for_each_entry(connection, &hd->connections, hd_links) {
30 if (connection->intf == intf &&
Viresh Kumarc3a16172015-07-01 12:13:56 +053031 connection->intf_cport_id == cport_id)
Alex Elderf5c2be92015-06-09 17:42:58 -050032 return connection;
Johan Hovold0daf17b2015-11-25 15:59:12 +010033 }
34
Alex Elderf5c2be92015-06-09 17:42:58 -050035 return NULL;
36}
37
Johan Hovold0e46fab2016-01-19 12:51:25 +010038static void gb_connection_get(struct gb_connection *connection)
39{
40 kref_get(&connection->kref);
41}
42
43static void gb_connection_put(struct gb_connection *connection)
44{
45 kref_put(&connection->kref, gb_connection_kref_release);
46}
47
48/*
49 * Returns a reference-counted pointer to the connection if found.
50 */
Alex Elder8bd0ae62015-06-08 12:05:11 -050051static struct gb_connection *
Johan Hovold25376362015-11-03 18:03:23 +010052gb_connection_hd_find(struct gb_host_device *hd, u16 cport_id)
Alex Elderee9ebe42014-10-06 06:53:08 -050053{
Alex Elderf5c2be92015-06-09 17:42:58 -050054 struct gb_connection *connection;
Johan Hovold8f5eadb2015-03-02 09:55:26 +010055 unsigned long flags;
Alex Elderee9ebe42014-10-06 06:53:08 -050056
Johan Hovold8f5eadb2015-03-02 09:55:26 +010057 spin_lock_irqsave(&gb_connections_lock, flags);
Alex Elder2c43ce42014-11-17 08:08:44 -060058 list_for_each_entry(connection, &hd->connections, hd_links)
Johan Hovold0e46fab2016-01-19 12:51:25 +010059 if (connection->hd_cport_id == cport_id) {
60 gb_connection_get(connection);
Marti Bolivare86905b2014-10-06 13:26:02 -040061 goto found;
Johan Hovold0e46fab2016-01-19 12:51:25 +010062 }
Marti Bolivare86905b2014-10-06 13:26:02 -040063 connection = NULL;
Alex Elderf5c2be92015-06-09 17:42:58 -050064found:
Johan Hovold8f5eadb2015-03-02 09:55:26 +010065 spin_unlock_irqrestore(&gb_connections_lock, flags);
Alex Elderee9ebe42014-10-06 06:53:08 -050066
67 return connection;
68}
69
Alex Elderde3557d2014-11-20 16:09:18 -060070/*
71 * Callback from the host driver to let us know that data has been
Greg Kroah-Hartman1db0a5f2014-12-12 17:10:17 -050072 * received on the bundle.
Alex Elderde3557d2014-11-20 16:09:18 -060073 */
Johan Hovold25376362015-11-03 18:03:23 +010074void greybus_data_rcvd(struct gb_host_device *hd, u16 cport_id,
Alex Elder374e6a22014-11-17 18:08:37 -060075 u8 *data, size_t length)
76{
77 struct gb_connection *connection;
78
Viresh Kumar12eba9f2015-05-20 16:48:00 +053079 connection = gb_connection_hd_find(hd, cport_id);
Alex Elder374e6a22014-11-17 18:08:37 -060080 if (!connection) {
Johan Hovold2adaefb2015-11-25 15:59:02 +010081 dev_err(&hd->dev,
Alex Elder374e6a22014-11-17 18:08:37 -060082 "nonexistent connection (%zu bytes dropped)\n", length);
83 return;
84 }
Alex Elder61089e82014-11-18 13:26:50 -060085 gb_connection_recv(connection, data, length);
Johan Hovold0e46fab2016-01-19 12:51:25 +010086 gb_connection_put(connection);
Alex Elder374e6a22014-11-17 18:08:37 -060087}
Alex Elderde3557d2014-11-20 16:09:18 -060088EXPORT_SYMBOL_GPL(greybus_data_rcvd);
Alex Elder374e6a22014-11-17 18:08:37 -060089
Greg Kroah-Hartmanb750fa32015-10-16 16:56:38 -070090static void gb_connection_kref_release(struct kref *kref)
Greg Kroah-Hartmanf0f61b92014-10-24 17:34:46 +080091{
Greg Kroah-Hartmanb750fa32015-10-16 16:56:38 -070092 struct gb_connection *connection;
Greg Kroah-Hartmanf0f61b92014-10-24 17:34:46 +080093
Greg Kroah-Hartmanb750fa32015-10-16 16:56:38 -070094 connection = container_of(kref, struct gb_connection, kref);
Johan Hovoldc3681f62016-01-19 12:51:23 +010095
Greg Kroah-Hartmanf0f61b92014-10-24 17:34:46 +080096 kfree(connection);
97}
98
Johan Hovold729b2602015-11-25 15:59:14 +010099static void gb_connection_init_name(struct gb_connection *connection)
100{
101 u16 hd_cport_id = connection->hd_cport_id;
102 u16 cport_id = 0;
103 u8 intf_id = 0;
104
105 if (connection->intf) {
106 intf_id = connection->intf->interface_id;
107 cport_id = connection->intf_cport_id;
108 }
109
110 snprintf(connection->name, sizeof(connection->name),
Viresh Kumar2f3db922015-12-04 21:30:09 +0530111 "%u/%u:%u", hd_cport_id, intf_id, cport_id);
Johan Hovold729b2602015-11-25 15:59:14 +0100112}
113
Alex Elder177404b2014-10-03 14:14:24 -0500114/*
Johan Hovold96c2af52016-01-21 17:34:15 +0100115 * _gb_connection_create() - create a Greybus connection
Johan Hovold2566fae2015-11-25 15:59:11 +0100116 * @hd: host device of the connection
117 * @hd_cport_id: host-device cport id, or -1 for dynamic allocation
118 * @intf: remote interface, or NULL for static connections
119 * @bundle: remote-interface bundle (may be NULL)
120 * @cport_id: remote-interface cport id, or 0 for static connections
Johan Hovoldf7ee0812016-01-21 17:34:21 +0100121 * @handler: request handler (may be NULL)
Johan Hovold2566fae2015-11-25 15:59:11 +0100122 *
123 * Create a Greybus connection, representing the bidirectional link
Alex Elderc68adb22014-10-01 21:54:14 -0500124 * between a CPort on a (local) Greybus host device and a CPort on
Johan Hovold2566fae2015-11-25 15:59:11 +0100125 * another Greybus interface.
Alex Elderc68adb22014-10-01 21:54:14 -0500126 *
Alex Eldere88afa52014-10-01 21:54:15 -0500127 * A connection also maintains the state of operations sent over the
128 * connection.
129 *
Johan Hovold210b5082016-01-19 12:51:26 +0100130 * Serialised against concurrent create and destroy using the
131 * gb_connection_mutex.
132 *
Johan Hovold24e094d2016-01-21 17:34:16 +0100133 * Return: A pointer to the new connection if successful, or an ERR_PTR
134 * otherwise.
Alex Elderc68adb22014-10-01 21:54:14 -0500135 */
Johan Hovold2566fae2015-11-25 15:59:11 +0100136static struct gb_connection *
Johan Hovold96c2af52016-01-21 17:34:15 +0100137_gb_connection_create(struct gb_host_device *hd, int hd_cport_id,
Johan Hovold2566fae2015-11-25 15:59:11 +0100138 struct gb_interface *intf,
Johan Hovoldf7ee0812016-01-21 17:34:21 +0100139 struct gb_bundle *bundle, int cport_id,
140 gb_request_handler_t handler)
Alex Elderc68adb22014-10-01 21:54:14 -0500141{
142 struct gb_connection *connection;
Alex Elderf9b03662015-06-12 10:21:08 -0500143 struct ida *id_map = &hd->cport_id_map;
Johan Hovold2566fae2015-11-25 15:59:11 +0100144 int ida_start, ida_end;
Johan Hovold24e094d2016-01-21 17:34:16 +0100145 int ret;
Alex Elderc68adb22014-10-01 21:54:14 -0500146
Johan Hovold2566fae2015-11-25 15:59:11 +0100147 if (hd_cport_id < 0) {
148 ida_start = 0;
149 ida_end = hd->num_cports;
150 } else if (hd_cport_id < hd->num_cports) {
151 ida_start = hd_cport_id;
152 ida_end = hd_cport_id + 1;
153 } else {
154 dev_err(&hd->dev, "cport %d not available\n", hd_cport_id);
Johan Hovold24e094d2016-01-21 17:34:16 +0100155 return ERR_PTR(-EINVAL);
Johan Hovold2566fae2015-11-25 15:59:11 +0100156 }
157
Johan Hovold210b5082016-01-19 12:51:26 +0100158 mutex_lock(&gb_connection_mutex);
159
Johan Hovoldb53e0c92016-01-19 12:51:27 +0100160 if (intf && gb_connection_intf_find(intf, cport_id)) {
161 dev_err(&intf->dev, "cport %u already in use\n", cport_id);
Johan Hovold24e094d2016-01-21 17:34:16 +0100162 ret = -EBUSY;
Johan Hovoldb53e0c92016-01-19 12:51:27 +0100163 goto err_unlock;
164 }
165
Johan Hovold24e094d2016-01-21 17:34:16 +0100166 ret = ida_simple_get(id_map, ida_start, ida_end, GFP_KERNEL);
167 if (ret < 0)
Johan Hovold210b5082016-01-19 12:51:26 +0100168 goto err_unlock;
Johan Hovold24e094d2016-01-21 17:34:16 +0100169 hd_cport_id = ret;
Alex Elderc68adb22014-10-01 21:54:14 -0500170
Johan Hovold10f9fa12015-07-23 10:50:01 +0200171 connection = kzalloc(sizeof(*connection), GFP_KERNEL);
Johan Hovold24e094d2016-01-21 17:34:16 +0100172 if (!connection) {
173 ret = -ENOMEM;
Johan Hovold10f9fa12015-07-23 10:50:01 +0200174 goto err_remove_ida;
Johan Hovold24e094d2016-01-21 17:34:16 +0100175 }
Johan Hovold10f9fa12015-07-23 10:50:01 +0200176
Johan Hovold7c63a822015-07-23 10:50:00 +0200177 connection->hd_cport_id = hd_cport_id;
Alex Elderf9b03662015-06-12 10:21:08 -0500178 connection->intf_cport_id = cport_id;
179 connection->hd = hd;
Johan Hovold2566fae2015-11-25 15:59:11 +0100180 connection->intf = intf;
Greg Kroah-Hartman1db0a5f2014-12-12 17:10:17 -0500181 connection->bundle = bundle;
Johan Hovoldf7ee0812016-01-21 17:34:21 +0100182 connection->handler = handler;
Alex Elder36561f22014-10-22 02:04:30 -0500183 connection->state = GB_CONNECTION_STATE_DISABLED;
Alex Elderad1c4492014-10-02 12:30:06 -0500184
Johan Hovold4e2b1e42015-07-22 17:49:19 +0200185 atomic_set(&connection->op_cycle, 0);
Johan Hovold23268782016-01-19 12:51:06 +0100186 mutex_init(&connection->mutex);
Johan Hovold4e2b1e42015-07-22 17:49:19 +0200187 spin_lock_init(&connection->lock);
188 INIT_LIST_HEAD(&connection->operations);
189
Johan Hovold5a5bc352015-07-23 10:50:02 +0200190 connection->wq = alloc_workqueue("%s:%d", WQ_UNBOUND, 1,
Johan Hovold582b3a12015-11-25 15:59:03 +0100191 dev_name(&hd->dev), hd_cport_id);
Johan Hovold24e094d2016-01-21 17:34:16 +0100192 if (!connection->wq) {
193 ret = -ENOMEM;
Johan Hovold5a5bc352015-07-23 10:50:02 +0200194 goto err_free_connection;
Johan Hovold24e094d2016-01-21 17:34:16 +0100195 }
Johan Hovold5a5bc352015-07-23 10:50:02 +0200196
Greg Kroah-Hartmanb750fa32015-10-16 16:56:38 -0700197 kref_init(&connection->kref);
Greg Kroah-Hartmanf0f61b92014-10-24 17:34:46 +0800198
Johan Hovold729b2602015-11-25 15:59:14 +0100199 gb_connection_init_name(connection);
200
Alex Elder748e1232014-10-03 14:14:22 -0500201 spin_lock_irq(&gb_connections_lock);
Viresh Kumar928f2ab2015-06-04 18:16:45 +0530202 list_add(&connection->hd_links, &hd->connections);
Viresh Kumar75662e52015-07-21 17:44:16 +0530203
204 if (bundle)
205 list_add(&connection->bundle_links, &bundle->connections);
206 else
207 INIT_LIST_HEAD(&connection->bundle_links);
208
Alex Elder748e1232014-10-03 14:14:22 -0500209 spin_unlock_irq(&gb_connections_lock);
210
Johan Hovold210b5082016-01-19 12:51:26 +0100211 mutex_unlock(&gb_connection_mutex);
212
Alex Elderc68adb22014-10-01 21:54:14 -0500213 return connection;
Johan Hovold10f9fa12015-07-23 10:50:01 +0200214
Johan Hovold5a5bc352015-07-23 10:50:02 +0200215err_free_connection:
216 kfree(connection);
Johan Hovold10f9fa12015-07-23 10:50:01 +0200217err_remove_ida:
218 ida_simple_remove(id_map, hd_cport_id);
Johan Hovold210b5082016-01-19 12:51:26 +0100219err_unlock:
220 mutex_unlock(&gb_connection_mutex);
Johan Hovold10f9fa12015-07-23 10:50:01 +0200221
Johan Hovold24e094d2016-01-21 17:34:16 +0100222 return ERR_PTR(ret);
Alex Elderc68adb22014-10-01 21:54:14 -0500223}
224
Johan Hovold2566fae2015-11-25 15:59:11 +0100225struct gb_connection *
Johan Hovoldf7ee0812016-01-21 17:34:21 +0100226gb_connection_create_static(struct gb_host_device *hd, u16 hd_cport_id,
227 gb_request_handler_t handler)
Johan Hovold2566fae2015-11-25 15:59:11 +0100228{
Johan Hovoldf7ee0812016-01-21 17:34:21 +0100229 return _gb_connection_create(hd, hd_cport_id, NULL, NULL, 0, handler);
Johan Hovold2566fae2015-11-25 15:59:11 +0100230}
231
232struct gb_connection *
Johan Hovold59507e22016-01-21 17:34:12 +0100233gb_connection_create_control(struct gb_interface *intf)
234{
Johan Hovoldf7ee0812016-01-21 17:34:21 +0100235 return _gb_connection_create(intf->hd, -1, intf, NULL, 0, NULL);
Johan Hovold59507e22016-01-21 17:34:12 +0100236}
237
238struct gb_connection *
Johan Hovoldf7ee0812016-01-21 17:34:21 +0100239gb_connection_create(struct gb_bundle *bundle, u16 cport_id,
240 gb_request_handler_t handler)
Johan Hovold2566fae2015-11-25 15:59:11 +0100241{
Johan Hovold8cff6c62016-01-21 17:34:14 +0100242 struct gb_interface *intf = bundle->intf;
243
Johan Hovoldf7ee0812016-01-21 17:34:21 +0100244 return _gb_connection_create(intf->hd, -1, intf, bundle, cport_id,
245 handler);
Johan Hovold2566fae2015-11-25 15:59:11 +0100246}
Johan Hovold96c2af52016-01-21 17:34:15 +0100247EXPORT_SYMBOL_GPL(gb_connection_create);
Johan Hovold2566fae2015-11-25 15:59:11 +0100248
Johan Hovoldd7ea30a52015-09-17 13:17:26 +0200249static int gb_connection_hd_cport_enable(struct gb_connection *connection)
250{
Johan Hovold25376362015-11-03 18:03:23 +0100251 struct gb_host_device *hd = connection->hd;
Johan Hovoldd7ea30a52015-09-17 13:17:26 +0200252 int ret;
253
254 if (!hd->driver->cport_enable)
255 return 0;
256
257 ret = hd->driver->cport_enable(hd, connection->hd_cport_id);
258 if (ret) {
Johan Hovold2adaefb2015-11-25 15:59:02 +0100259 dev_err(&hd->dev,
Greg Kroah-Hartman30482c12015-10-16 16:56:23 -0700260 "failed to enable host cport: %d\n", ret);
Johan Hovoldd7ea30a52015-09-17 13:17:26 +0200261 return ret;
262 }
263
264 return 0;
265}
266
267static void gb_connection_hd_cport_disable(struct gb_connection *connection)
268{
Johan Hovold25376362015-11-03 18:03:23 +0100269 struct gb_host_device *hd = connection->hd;
Johan Hovoldd7ea30a52015-09-17 13:17:26 +0200270
271 if (!hd->driver->cport_disable)
272 return;
273
274 hd->driver->cport_disable(hd, connection->hd_cport_id);
275}
276
Fabien Parent9ed5e1b2016-02-23 18:46:08 +0100277static int gb_connection_hd_fct_flow_enable(struct gb_connection *connection)
278{
279 struct gb_host_device *hd = connection->hd;
280 int ret;
281
282 if (!hd->driver->fct_flow_enable)
283 return 0;
284
285 ret = hd->driver->fct_flow_enable(hd, connection->hd_cport_id);
286 if (ret) {
287 dev_err(&hd->dev, "%s: failed to enable FCT flow: %d\n",
288 connection->name, ret);
289 return ret;
290 }
291
292 return 0;
293}
294
295static void gb_connection_hd_fct_flow_disable(struct gb_connection *connection)
296{
297 struct gb_host_device *hd = connection->hd;
298
299 if (!hd->driver->fct_flow_disable)
300 return;
301
302 hd->driver->fct_flow_disable(hd, connection->hd_cport_id);
303}
304
Alex Elderc68adb22014-10-01 21:54:14 -0500305/*
Johan Hovolda95c2582015-09-17 13:17:21 +0200306 * Request the SVC to create a connection from AP's cport to interface's
307 * cport.
308 */
309static int
310gb_connection_svc_connection_create(struct gb_connection *connection)
311{
Johan Hovold25376362015-11-03 18:03:23 +0100312 struct gb_host_device *hd = connection->hd;
Viresh Kumar1575ef12015-10-07 15:40:24 -0400313 struct gb_interface *intf;
Johan Hovolda95c2582015-09-17 13:17:21 +0200314 int ret;
315
Johan Hovold4ec15742015-11-25 15:59:13 +0100316 if (gb_connection_is_static(connection))
Fabien Parent71f6c322016-02-23 18:46:11 +0100317 return gb_connection_hd_fct_flow_enable(connection);
Johan Hovolda95c2582015-09-17 13:17:21 +0200318
Johan Hovold35822c02015-11-25 15:59:22 +0100319 intf = connection->intf;
Johan Hovolda95c2582015-09-17 13:17:21 +0200320 ret = gb_svc_connection_create(hd->svc,
Johan Hovold66069fb2015-11-25 15:59:09 +0100321 hd->svc->ap_intf_id,
Johan Hovolda95c2582015-09-17 13:17:21 +0200322 connection->hd_cport_id,
Viresh Kumar1575ef12015-10-07 15:40:24 -0400323 intf->interface_id,
324 connection->intf_cport_id,
325 intf->boot_over_unipro);
Johan Hovolda95c2582015-09-17 13:17:21 +0200326 if (ret) {
Johan Hovold4c4b5022015-11-25 15:59:15 +0100327 dev_err(&connection->hd->dev,
328 "%s: failed to create svc connection: %d\n",
329 connection->name, ret);
Johan Hovolda95c2582015-09-17 13:17:21 +0200330 return ret;
331 }
332
Fabien Parent71f6c322016-02-23 18:46:11 +0100333 ret = gb_connection_hd_fct_flow_enable(connection);
334 if (ret) {
335 gb_svc_connection_destroy(hd->svc, hd->svc->ap_intf_id,
336 connection->hd_cport_id,
337 intf->interface_id,
338 connection->intf_cport_id);
339 return ret;
340 }
341
Johan Hovolda95c2582015-09-17 13:17:21 +0200342 return 0;
343}
344
Viresh Kumar1b7a9cd2015-09-07 16:01:22 +0530345static void
346gb_connection_svc_connection_destroy(struct gb_connection *connection)
347{
Fabien Parent71f6c322016-02-23 18:46:11 +0100348 gb_connection_hd_fct_flow_disable(connection);
349
Johan Hovold4ec15742015-11-25 15:59:13 +0100350 if (gb_connection_is_static(connection))
Viresh Kumar1b7a9cd2015-09-07 16:01:22 +0530351 return;
352
353 gb_svc_connection_destroy(connection->hd->svc,
Johan Hovold66069fb2015-11-25 15:59:09 +0100354 connection->hd->svc->ap_intf_id,
Viresh Kumar1b7a9cd2015-09-07 16:01:22 +0530355 connection->hd_cport_id,
Johan Hovold35822c02015-11-25 15:59:22 +0100356 connection->intf->interface_id,
Viresh Kumar1b7a9cd2015-09-07 16:01:22 +0530357 connection->intf_cport_id);
358}
359
Johan Hovold9d7fc252015-09-17 13:17:24 +0200360/* Inform Interface about active CPorts */
361static int gb_connection_control_connected(struct gb_connection *connection)
362{
Johan Hovold9d7fc252015-09-17 13:17:24 +0200363 struct gb_control *control;
364 u16 cport_id = connection->intf_cport_id;
365 int ret;
366
Johan Hovoldbc3be172016-01-19 12:51:12 +0100367 if (gb_connection_is_static(connection))
Johan Hovold9d7fc252015-09-17 13:17:24 +0200368 return 0;
369
Johan Hovoldbc3be172016-01-19 12:51:12 +0100370 control = connection->intf->control;
371
372 if (connection == control->connection)
373 return 0;
Johan Hovold9d7fc252015-09-17 13:17:24 +0200374
375 ret = gb_control_connected_operation(control, cport_id);
376 if (ret) {
Greg Kroah-Hartman30482c12015-10-16 16:56:23 -0700377 dev_err(&connection->bundle->dev,
378 "failed to connect cport: %d\n", ret);
Johan Hovold9d7fc252015-09-17 13:17:24 +0200379 return ret;
380 }
381
382 return 0;
383}
384
Johan Hovold72d74822015-09-17 13:17:23 +0200385/* Inform Interface about inactive CPorts */
386static void
387gb_connection_control_disconnected(struct gb_connection *connection)
Viresh Kumar18690652015-08-11 07:35:56 +0530388{
389 struct gb_control *control;
Johan Hovold72d74822015-09-17 13:17:23 +0200390 u16 cport_id = connection->intf_cport_id;
Viresh Kumar18690652015-08-11 07:35:56 +0530391 int ret;
392
Johan Hovoldbc3be172016-01-19 12:51:12 +0100393 if (gb_connection_is_static(connection))
Viresh Kumar18690652015-08-11 07:35:56 +0530394 return;
395
Johan Hovoldbc3be172016-01-19 12:51:12 +0100396 control = connection->intf->control;
397
398 if (connection == control->connection)
399 return;
Viresh Kumar18690652015-08-11 07:35:56 +0530400
401 ret = gb_control_disconnected_operation(control, cport_id);
Johan Hovold72d74822015-09-17 13:17:23 +0200402 if (ret) {
Greg Kroah-Hartman30482c12015-10-16 16:56:23 -0700403 dev_warn(&connection->bundle->dev,
404 "failed to disconnect cport: %d\n", ret);
Johan Hovold72d74822015-09-17 13:17:23 +0200405 }
Viresh Kumar18690652015-08-11 07:35:56 +0530406}
407
Johan Hovold2846d3e2015-09-17 13:17:25 +0200408/*
Johan Hovold520c6ea2016-01-19 12:51:04 +0100409 * Cancel all active operations on a connection.
410 *
411 * Locking: Called with connection lock held and state set to DISABLED.
412 */
413static void gb_connection_cancel_operations(struct gb_connection *connection,
414 int errno)
Viresh Kumar127c1fb2016-01-28 15:50:48 +0530415 __must_hold(&connection->lock)
Johan Hovold520c6ea2016-01-19 12:51:04 +0100416{
417 struct gb_operation *operation;
418
419 while (!list_empty(&connection->operations)) {
420 operation = list_last_entry(&connection->operations,
421 struct gb_operation, links);
422 gb_operation_get(operation);
423 spin_unlock_irq(&connection->lock);
424
425 if (gb_operation_is_incoming(operation))
426 gb_operation_cancel_incoming(operation, errno);
427 else
428 gb_operation_cancel(operation, errno);
429
430 gb_operation_put(operation);
431
432 spin_lock_irq(&connection->lock);
433 }
434}
435
Johan Hovoldbeb6b7f2016-01-19 12:51:08 +0100436/*
437 * Cancel all active incoming operations on a connection.
438 *
439 * Locking: Called with connection lock held and state set to ENABLED_TX.
440 */
441static void
442gb_connection_flush_incoming_operations(struct gb_connection *connection,
443 int errno)
Viresh Kumar127c1fb2016-01-28 15:50:48 +0530444 __must_hold(&connection->lock)
Johan Hovoldbeb6b7f2016-01-19 12:51:08 +0100445{
446 struct gb_operation *operation;
447 bool incoming;
448
449 while (!list_empty(&connection->operations)) {
450 incoming = false;
451 list_for_each_entry(operation, &connection->operations,
452 links) {
453 if (gb_operation_is_incoming(operation)) {
454 gb_operation_get(operation);
455 incoming = true;
456 break;
457 }
458 }
459
460 if (!incoming)
461 break;
462
463 spin_unlock_irq(&connection->lock);
464
465 /* FIXME: flush, not cancel? */
466 gb_operation_cancel_incoming(operation, errno);
467 gb_operation_put(operation);
468
469 spin_lock_irq(&connection->lock);
470 }
471}
472
Johan Hovoldf7ee0812016-01-21 17:34:21 +0100473/*
474 * _gb_connection_enable() - enable a connection
475 * @connection: connection to enable
476 * @rx: whether to enable incoming requests
477 *
478 * Connection-enable helper for DISABLED->ENABLED, DISABLED->ENABLED_TX, and
479 * ENABLED_TX->ENABLED state transitions.
480 *
481 * Locking: Caller holds connection->mutex.
482 */
483static int _gb_connection_enable(struct gb_connection *connection, bool rx)
Alex Elder574341c2014-10-16 06:35:35 -0500484{
Alex Elder36561f22014-10-22 02:04:30 -0500485 int ret;
486
Johan Hovoldf7ee0812016-01-21 17:34:21 +0100487 /* Handle ENABLED_TX -> ENABLED transitions. */
Johan Hovold570dfa72016-01-19 12:51:07 +0100488 if (connection->state == GB_CONNECTION_STATE_ENABLED_TX) {
Johan Hovoldf7ee0812016-01-21 17:34:21 +0100489 if (!(connection->handler && rx))
490 return 0;
Johan Hovold570dfa72016-01-19 12:51:07 +0100491
492 spin_lock_irq(&connection->lock);
Johan Hovold570dfa72016-01-19 12:51:07 +0100493 connection->state = GB_CONNECTION_STATE_ENABLED;
494 spin_unlock_irq(&connection->lock);
495
Johan Hovoldf7ee0812016-01-21 17:34:21 +0100496 return 0;
Johan Hovold570dfa72016-01-19 12:51:07 +0100497 }
498
Johan Hovoldd7ea30a52015-09-17 13:17:26 +0200499 ret = gb_connection_hd_cport_enable(connection);
Johan Hovolda95c2582015-09-17 13:17:21 +0200500 if (ret)
Johan Hovoldf7ee0812016-01-21 17:34:21 +0100501 return ret;
Viresh Kumara1163fa2015-09-07 16:01:21 +0530502
Johan Hovoldd7ea30a52015-09-17 13:17:26 +0200503 ret = gb_connection_svc_connection_create(connection);
504 if (ret)
505 goto err_hd_cport_disable;
506
Johan Hovoldcad09a82015-07-14 15:43:30 +0200507 spin_lock_irq(&connection->lock);
Johan Hovoldf7ee0812016-01-21 17:34:21 +0100508 if (connection->handler && rx)
Johan Hovold570dfa72016-01-19 12:51:07 +0100509 connection->state = GB_CONNECTION_STATE_ENABLED;
510 else
511 connection->state = GB_CONNECTION_STATE_ENABLED_TX;
Johan Hovoldcad09a82015-07-14 15:43:30 +0200512 spin_unlock_irq(&connection->lock);
513
Johan Hovold4d0bee12016-01-08 20:13:45 +0100514 ret = gb_connection_control_connected(connection);
515 if (ret)
516 goto err_svc_destroy;
517
Johan Hovold8d7a7122015-09-17 13:17:22 +0200518 return 0;
519
Johan Hovold3ea6a812016-01-08 20:13:46 +0100520err_svc_destroy:
521 spin_lock_irq(&connection->lock);
522 connection->state = GB_CONNECTION_STATE_DISABLED;
Johan Hovold192bee42016-01-19 12:51:05 +0100523 gb_connection_cancel_operations(connection, -ESHUTDOWN);
Johan Hovold3ea6a812016-01-08 20:13:46 +0100524 spin_unlock_irq(&connection->lock);
525
526 gb_connection_svc_connection_destroy(connection);
527err_hd_cport_disable:
528 gb_connection_hd_cport_disable(connection);
Johan Hovoldf7ee0812016-01-21 17:34:21 +0100529
530 return ret;
531}
532
533int gb_connection_enable(struct gb_connection *connection)
534{
535 int ret = 0;
536
537 mutex_lock(&connection->mutex);
538
539 if (connection->state == GB_CONNECTION_STATE_ENABLED)
540 goto out_unlock;
541
542 ret = _gb_connection_enable(connection, true);
543out_unlock:
Johan Hovold23268782016-01-19 12:51:06 +0100544 mutex_unlock(&connection->mutex);
Johan Hovold3ea6a812016-01-08 20:13:46 +0100545
546 return ret;
547}
548EXPORT_SYMBOL_GPL(gb_connection_enable);
549
Johan Hovoldf7ee0812016-01-21 17:34:21 +0100550int gb_connection_enable_tx(struct gb_connection *connection)
551{
552 int ret = 0;
553
554 mutex_lock(&connection->mutex);
555
556 if (connection->state == GB_CONNECTION_STATE_ENABLED) {
557 ret = -EINVAL;
558 goto out_unlock;
559 }
560
561 if (connection->state == GB_CONNECTION_STATE_ENABLED_TX)
562 goto out_unlock;
563
564 ret = _gb_connection_enable(connection, false);
565out_unlock:
566 mutex_unlock(&connection->mutex);
567
568 return ret;
569}
570EXPORT_SYMBOL_GPL(gb_connection_enable_tx);
571
Johan Hovoldbeb6b7f2016-01-19 12:51:08 +0100572void gb_connection_disable_rx(struct gb_connection *connection)
573{
574 mutex_lock(&connection->mutex);
575
576 spin_lock_irq(&connection->lock);
577 if (connection->state != GB_CONNECTION_STATE_ENABLED) {
578 spin_unlock_irq(&connection->lock);
579 goto out_unlock;
580 }
581 connection->state = GB_CONNECTION_STATE_ENABLED_TX;
582 gb_connection_flush_incoming_operations(connection, -ESHUTDOWN);
Johan Hovoldbeb6b7f2016-01-19 12:51:08 +0100583 spin_unlock_irq(&connection->lock);
584
585out_unlock:
586 mutex_unlock(&connection->mutex);
587}
588
Johan Hovold3ea6a812016-01-08 20:13:46 +0100589void gb_connection_disable(struct gb_connection *connection)
590{
Johan Hovold23268782016-01-19 12:51:06 +0100591 mutex_lock(&connection->mutex);
592
Johan Hovold81fba2492016-01-19 12:51:03 +0100593 if (connection->state == GB_CONNECTION_STATE_DISABLED)
Johan Hovold23268782016-01-19 12:51:06 +0100594 goto out_unlock;
Johan Hovold81fba2492016-01-19 12:51:03 +0100595
Johan Hovold72d74822015-09-17 13:17:23 +0200596 gb_connection_control_disconnected(connection);
Johan Hovold4d0bee12016-01-08 20:13:45 +0100597
598 spin_lock_irq(&connection->lock);
599 connection->state = GB_CONNECTION_STATE_DISABLED;
Johan Hovold81fba2492016-01-19 12:51:03 +0100600 gb_connection_cancel_operations(connection, -ESHUTDOWN);
Johan Hovold520c6ea2016-01-19 12:51:04 +0100601 spin_unlock_irq(&connection->lock);
Johan Hovold81fba2492016-01-19 12:51:03 +0100602
Viresh Kumar1b7a9cd2015-09-07 16:01:22 +0530603 gb_connection_svc_connection_destroy(connection);
Johan Hovoldd7ea30a52015-09-17 13:17:26 +0200604 gb_connection_hd_cport_disable(connection);
Johan Hovold23268782016-01-19 12:51:06 +0100605
606out_unlock:
607 mutex_unlock(&connection->mutex);
Johan Hovold3ea6a812016-01-08 20:13:46 +0100608}
609EXPORT_SYMBOL_GPL(gb_connection_disable);
610
Johan Hovoldc3681f62016-01-19 12:51:23 +0100611/* Caller must have disabled the connection before destroying it. */
Viresh Kumarfda23812015-08-31 17:21:13 +0530612void gb_connection_destroy(struct gb_connection *connection)
613{
614 struct ida *id_map;
615
Johan Hovold2edbf5f2016-01-19 12:51:22 +0100616 if (!connection)
Viresh Kumarfda23812015-08-31 17:21:13 +0530617 return;
618
Johan Hovold210b5082016-01-19 12:51:26 +0100619 mutex_lock(&gb_connection_mutex);
620
Viresh Kumarfda23812015-08-31 17:21:13 +0530621 spin_lock_irq(&gb_connections_lock);
622 list_del(&connection->bundle_links);
623 list_del(&connection->hd_links);
624 spin_unlock_irq(&gb_connections_lock);
625
Johan Hovoldc3681f62016-01-19 12:51:23 +0100626 destroy_workqueue(connection->wq);
627
Viresh Kumarfda23812015-08-31 17:21:13 +0530628 id_map = &connection->hd->cport_id_map;
629 ida_simple_remove(id_map, connection->hd_cport_id);
630 connection->hd_cport_id = CPORT_ID_BAD;
631
Johan Hovold210b5082016-01-19 12:51:26 +0100632 mutex_unlock(&gb_connection_mutex);
633
Johan Hovold0e46fab2016-01-19 12:51:25 +0100634 gb_connection_put(connection);
Viresh Kumarfda23812015-08-31 17:21:13 +0530635}
Johan Hovold98fdf5a2016-01-21 17:34:09 +0100636EXPORT_SYMBOL_GPL(gb_connection_destroy);
Viresh Kumarfda23812015-08-31 17:21:13 +0530637
Bryan O'Donoghuee7e2efc2015-10-15 16:10:42 +0100638void gb_connection_latency_tag_enable(struct gb_connection *connection)
639{
Johan Hovold25376362015-11-03 18:03:23 +0100640 struct gb_host_device *hd = connection->hd;
Bryan O'Donoghuee7e2efc2015-10-15 16:10:42 +0100641 int ret;
642
643 if (!hd->driver->latency_tag_enable)
644 return;
645
646 ret = hd->driver->latency_tag_enable(hd, connection->hd_cport_id);
647 if (ret) {
Johan Hovold4c4b5022015-11-25 15:59:15 +0100648 dev_err(&connection->hd->dev,
649 "%s: failed to enable latency tag: %d\n",
650 connection->name, ret);
Bryan O'Donoghuee7e2efc2015-10-15 16:10:42 +0100651 }
652}
653EXPORT_SYMBOL_GPL(gb_connection_latency_tag_enable);
654
655void gb_connection_latency_tag_disable(struct gb_connection *connection)
656{
Johan Hovold25376362015-11-03 18:03:23 +0100657 struct gb_host_device *hd = connection->hd;
Bryan O'Donoghuee7e2efc2015-10-15 16:10:42 +0100658 int ret;
659
660 if (!hd->driver->latency_tag_disable)
661 return;
662
663 ret = hd->driver->latency_tag_disable(hd, connection->hd_cport_id);
664 if (ret) {
Johan Hovold4c4b5022015-11-25 15:59:15 +0100665 dev_err(&connection->hd->dev,
666 "%s: failed to disable latency tag: %d\n",
667 connection->name, ret);
Bryan O'Donoghuee7e2efc2015-10-15 16:10:42 +0100668 }
669}
670EXPORT_SYMBOL_GPL(gb_connection_latency_tag_disable);