blob: 557075147f2dcc693f81ebd6384c2b5270fa24aa [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"
Alex Elder79c8c642016-06-03 15:55:37 -050013#include "greybus_trace.h"
Alex Elderc68adb22014-10-01 21:54:14 -050014
Johan Hovold2d54e4d2015-12-07 15:05:30 +010015
Johan Hovoldaac08392016-08-26 12:55:49 +020016#define GB_CONNECTION_CPORT_QUIESCE_TIMEOUT 1000
17
18
Johan Hovold0e46fab2016-01-19 12:51:25 +010019static void gb_connection_kref_release(struct kref *kref);
20
21
Alex Elder748e1232014-10-03 14:14:22 -050022static DEFINE_SPINLOCK(gb_connections_lock);
Johan Hovold210b5082016-01-19 12:51:26 +010023static DEFINE_MUTEX(gb_connection_mutex);
24
Alex Elder748e1232014-10-03 14:14:22 -050025
Johan Hovoldb53e0c92016-01-19 12:51:27 +010026/* Caller holds gb_connection_mutex. */
Viresh Kumar54131222016-06-10 14:59:09 +053027static bool gb_connection_cport_in_use(struct gb_interface *intf, u16 cport_id)
Alex Elderf5c2be92015-06-09 17:42:58 -050028{
Johan Hovold25376362015-11-03 18:03:23 +010029 struct gb_host_device *hd = intf->hd;
Alex Elderf5c2be92015-06-09 17:42:58 -050030 struct gb_connection *connection;
31
Johan Hovold0daf17b2015-11-25 15:59:12 +010032 list_for_each_entry(connection, &hd->connections, hd_links) {
33 if (connection->intf == intf &&
Viresh Kumarc3a16172015-07-01 12:13:56 +053034 connection->intf_cport_id == cport_id)
Viresh Kumar54131222016-06-10 14:59:09 +053035 return true;
Johan Hovold0daf17b2015-11-25 15:59:12 +010036 }
37
Viresh Kumar54131222016-06-10 14:59:09 +053038 return false;
Alex Elderf5c2be92015-06-09 17:42:58 -050039}
40
Johan Hovold0e46fab2016-01-19 12:51:25 +010041static void gb_connection_get(struct gb_connection *connection)
42{
43 kref_get(&connection->kref);
Alex Elder79c8c642016-06-03 15:55:37 -050044
45 trace_gb_connection_get(connection);
Johan Hovold0e46fab2016-01-19 12:51:25 +010046}
47
48static void gb_connection_put(struct gb_connection *connection)
49{
Alex Elder79c8c642016-06-03 15:55:37 -050050 trace_gb_connection_put(connection);
51
Johan Hovold0e46fab2016-01-19 12:51:25 +010052 kref_put(&connection->kref, gb_connection_kref_release);
53}
54
55/*
56 * Returns a reference-counted pointer to the connection if found.
57 */
Alex Elder8bd0ae62015-06-08 12:05:11 -050058static struct gb_connection *
Johan Hovold25376362015-11-03 18:03:23 +010059gb_connection_hd_find(struct gb_host_device *hd, u16 cport_id)
Alex Elderee9ebe42014-10-06 06:53:08 -050060{
Alex Elderf5c2be92015-06-09 17:42:58 -050061 struct gb_connection *connection;
Johan Hovold8f5eadb2015-03-02 09:55:26 +010062 unsigned long flags;
Alex Elderee9ebe42014-10-06 06:53:08 -050063
Johan Hovold8f5eadb2015-03-02 09:55:26 +010064 spin_lock_irqsave(&gb_connections_lock, flags);
Alex Elder2c43ce42014-11-17 08:08:44 -060065 list_for_each_entry(connection, &hd->connections, hd_links)
Johan Hovold0e46fab2016-01-19 12:51:25 +010066 if (connection->hd_cport_id == cport_id) {
67 gb_connection_get(connection);
Marti Bolivare86905b2014-10-06 13:26:02 -040068 goto found;
Johan Hovold0e46fab2016-01-19 12:51:25 +010069 }
Marti Bolivare86905b2014-10-06 13:26:02 -040070 connection = NULL;
Alex Elderf5c2be92015-06-09 17:42:58 -050071found:
Johan Hovold8f5eadb2015-03-02 09:55:26 +010072 spin_unlock_irqrestore(&gb_connections_lock, flags);
Alex Elderee9ebe42014-10-06 06:53:08 -050073
74 return connection;
75}
76
Alex Elderde3557d2014-11-20 16:09:18 -060077/*
78 * Callback from the host driver to let us know that data has been
Greg Kroah-Hartman1db0a5f2014-12-12 17:10:17 -050079 * received on the bundle.
Alex Elderde3557d2014-11-20 16:09:18 -060080 */
Johan Hovold25376362015-11-03 18:03:23 +010081void greybus_data_rcvd(struct gb_host_device *hd, u16 cport_id,
Alex Elder374e6a22014-11-17 18:08:37 -060082 u8 *data, size_t length)
83{
84 struct gb_connection *connection;
85
Alex Elder495787a2016-06-03 15:55:38 -050086 trace_gb_hd_in(hd);
87
Viresh Kumar12eba9f2015-05-20 16:48:00 +053088 connection = gb_connection_hd_find(hd, cport_id);
Alex Elder374e6a22014-11-17 18:08:37 -060089 if (!connection) {
Johan Hovold2adaefb2015-11-25 15:59:02 +010090 dev_err(&hd->dev,
Alex Elder374e6a22014-11-17 18:08:37 -060091 "nonexistent connection (%zu bytes dropped)\n", length);
92 return;
93 }
Alex Elder61089e82014-11-18 13:26:50 -060094 gb_connection_recv(connection, data, length);
Johan Hovold0e46fab2016-01-19 12:51:25 +010095 gb_connection_put(connection);
Alex Elder374e6a22014-11-17 18:08:37 -060096}
Alex Elderde3557d2014-11-20 16:09:18 -060097EXPORT_SYMBOL_GPL(greybus_data_rcvd);
Alex Elder374e6a22014-11-17 18:08:37 -060098
Greg Kroah-Hartmanb750fa32015-10-16 16:56:38 -070099static void gb_connection_kref_release(struct kref *kref)
Greg Kroah-Hartmanf0f61b92014-10-24 17:34:46 +0800100{
Greg Kroah-Hartmanb750fa32015-10-16 16:56:38 -0700101 struct gb_connection *connection;
Greg Kroah-Hartmanf0f61b92014-10-24 17:34:46 +0800102
Greg Kroah-Hartmanb750fa32015-10-16 16:56:38 -0700103 connection = container_of(kref, struct gb_connection, kref);
Johan Hovoldc3681f62016-01-19 12:51:23 +0100104
Alex Elder79c8c642016-06-03 15:55:37 -0500105 trace_gb_connection_release(connection);
106
Greg Kroah-Hartmanf0f61b92014-10-24 17:34:46 +0800107 kfree(connection);
108}
109
Johan Hovold729b2602015-11-25 15:59:14 +0100110static void gb_connection_init_name(struct gb_connection *connection)
111{
112 u16 hd_cport_id = connection->hd_cport_id;
113 u16 cport_id = 0;
114 u8 intf_id = 0;
115
116 if (connection->intf) {
117 intf_id = connection->intf->interface_id;
118 cport_id = connection->intf_cport_id;
119 }
120
121 snprintf(connection->name, sizeof(connection->name),
Viresh Kumar2f3db922015-12-04 21:30:09 +0530122 "%u/%u:%u", hd_cport_id, intf_id, cport_id);
Johan Hovold729b2602015-11-25 15:59:14 +0100123}
124
Alex Elder177404b2014-10-03 14:14:24 -0500125/*
Johan Hovold96c2af52016-01-21 17:34:15 +0100126 * _gb_connection_create() - create a Greybus connection
Johan Hovold2566fae2015-11-25 15:59:11 +0100127 * @hd: host device of the connection
128 * @hd_cport_id: host-device cport id, or -1 for dynamic allocation
129 * @intf: remote interface, or NULL for static connections
130 * @bundle: remote-interface bundle (may be NULL)
131 * @cport_id: remote-interface cport id, or 0 for static connections
Johan Hovoldf7ee0812016-01-21 17:34:21 +0100132 * @handler: request handler (may be NULL)
Johan Hovoldcb033182016-03-03 13:34:36 +0100133 * @flags: connection flags
Johan Hovold2566fae2015-11-25 15:59:11 +0100134 *
135 * Create a Greybus connection, representing the bidirectional link
Alex Elderc68adb22014-10-01 21:54:14 -0500136 * between a CPort on a (local) Greybus host device and a CPort on
Johan Hovold2566fae2015-11-25 15:59:11 +0100137 * another Greybus interface.
Alex Elderc68adb22014-10-01 21:54:14 -0500138 *
Alex Eldere88afa52014-10-01 21:54:15 -0500139 * A connection also maintains the state of operations sent over the
140 * connection.
141 *
Johan Hovold210b5082016-01-19 12:51:26 +0100142 * Serialised against concurrent create and destroy using the
143 * gb_connection_mutex.
144 *
Johan Hovold24e094d2016-01-21 17:34:16 +0100145 * Return: A pointer to the new connection if successful, or an ERR_PTR
146 * otherwise.
Alex Elderc68adb22014-10-01 21:54:14 -0500147 */
Johan Hovold2566fae2015-11-25 15:59:11 +0100148static struct gb_connection *
Johan Hovold96c2af52016-01-21 17:34:15 +0100149_gb_connection_create(struct gb_host_device *hd, int hd_cport_id,
Johan Hovold2566fae2015-11-25 15:59:11 +0100150 struct gb_interface *intf,
Johan Hovoldf7ee0812016-01-21 17:34:21 +0100151 struct gb_bundle *bundle, int cport_id,
Johan Hovoldcb033182016-03-03 13:34:36 +0100152 gb_request_handler_t handler,
153 unsigned long flags)
Alex Elderc68adb22014-10-01 21:54:14 -0500154{
155 struct gb_connection *connection;
Johan Hovold24e094d2016-01-21 17:34:16 +0100156 int ret;
Alex Elderc68adb22014-10-01 21:54:14 -0500157
Johan Hovold210b5082016-01-19 12:51:26 +0100158 mutex_lock(&gb_connection_mutex);
159
Viresh Kumar54131222016-06-10 14:59:09 +0530160 if (intf && gb_connection_cport_in_use(intf, cport_id)) {
Johan Hovoldb53e0c92016-01-19 12:51:27 +0100161 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 Hovoldf2aae1c2016-05-11 10:18:02 +0200166 ret = gb_hd_cport_allocate(hd, hd_cport_id, flags);
Johan Hovold74a5d932016-05-11 10:17:59 +0200167 if (ret < 0) {
168 dev_err(&hd->dev, "failed to allocate cport: %d\n", ret);
Johan Hovold210b5082016-01-19 12:51:26 +0100169 goto err_unlock;
Johan Hovold74a5d932016-05-11 10:17:59 +0200170 }
Johan Hovold24e094d2016-01-21 17:34:16 +0100171 hd_cport_id = ret;
Alex Elderc68adb22014-10-01 21:54:14 -0500172
Johan Hovold10f9fa12015-07-23 10:50:01 +0200173 connection = kzalloc(sizeof(*connection), GFP_KERNEL);
Johan Hovold24e094d2016-01-21 17:34:16 +0100174 if (!connection) {
175 ret = -ENOMEM;
Johan Hovold74a5d932016-05-11 10:17:59 +0200176 goto err_hd_cport_release;
Johan Hovold24e094d2016-01-21 17:34:16 +0100177 }
Johan Hovold10f9fa12015-07-23 10:50:01 +0200178
Johan Hovold7c63a822015-07-23 10:50:00 +0200179 connection->hd_cport_id = hd_cport_id;
Alex Elderf9b03662015-06-12 10:21:08 -0500180 connection->intf_cport_id = cport_id;
181 connection->hd = hd;
Johan Hovold2566fae2015-11-25 15:59:11 +0100182 connection->intf = intf;
Greg Kroah-Hartman1db0a5f2014-12-12 17:10:17 -0500183 connection->bundle = bundle;
Johan Hovoldf7ee0812016-01-21 17:34:21 +0100184 connection->handler = handler;
Johan Hovoldcb033182016-03-03 13:34:36 +0100185 connection->flags = flags;
Johan Hovold0e9b41a2016-05-11 10:17:55 +0200186 if (intf && (intf->quirks & GB_INTERFACE_QUIRK_NO_CPORT_FEATURES))
187 connection->flags |= GB_CONNECTION_FLAG_NO_FLOWCTRL;
Alex Elder36561f22014-10-22 02:04:30 -0500188 connection->state = GB_CONNECTION_STATE_DISABLED;
Alex Elderad1c4492014-10-02 12:30:06 -0500189
Johan Hovold4e2b1e42015-07-22 17:49:19 +0200190 atomic_set(&connection->op_cycle, 0);
Johan Hovold23268782016-01-19 12:51:06 +0100191 mutex_init(&connection->mutex);
Johan Hovold4e2b1e42015-07-22 17:49:19 +0200192 spin_lock_init(&connection->lock);
193 INIT_LIST_HEAD(&connection->operations);
194
Johan Hovold5a5bc352015-07-23 10:50:02 +0200195 connection->wq = alloc_workqueue("%s:%d", WQ_UNBOUND, 1,
Johan Hovold582b3a12015-11-25 15:59:03 +0100196 dev_name(&hd->dev), hd_cport_id);
Johan Hovold24e094d2016-01-21 17:34:16 +0100197 if (!connection->wq) {
198 ret = -ENOMEM;
Johan Hovold5a5bc352015-07-23 10:50:02 +0200199 goto err_free_connection;
Johan Hovold24e094d2016-01-21 17:34:16 +0100200 }
Johan Hovold5a5bc352015-07-23 10:50:02 +0200201
Greg Kroah-Hartmanb750fa32015-10-16 16:56:38 -0700202 kref_init(&connection->kref);
Greg Kroah-Hartmanf0f61b92014-10-24 17:34:46 +0800203
Johan Hovold729b2602015-11-25 15:59:14 +0100204 gb_connection_init_name(connection);
205
Johan Hovold0b1d2622016-06-27 20:07:10 +0200206 spin_lock_irq(&gb_connections_lock);
Viresh Kumar928f2ab2015-06-04 18:16:45 +0530207 list_add(&connection->hd_links, &hd->connections);
Viresh Kumar75662e52015-07-21 17:44:16 +0530208
209 if (bundle)
210 list_add(&connection->bundle_links, &bundle->connections);
211 else
212 INIT_LIST_HEAD(&connection->bundle_links);
213
Johan Hovold0b1d2622016-06-27 20:07:10 +0200214 spin_unlock_irq(&gb_connections_lock);
Alex Elder748e1232014-10-03 14:14:22 -0500215
Johan Hovold210b5082016-01-19 12:51:26 +0100216 mutex_unlock(&gb_connection_mutex);
217
Alex Elder79c8c642016-06-03 15:55:37 -0500218 trace_gb_connection_create(connection);
219
Alex Elderc68adb22014-10-01 21:54:14 -0500220 return connection;
Johan Hovold10f9fa12015-07-23 10:50:01 +0200221
Johan Hovold5a5bc352015-07-23 10:50:02 +0200222err_free_connection:
223 kfree(connection);
Johan Hovold74a5d932016-05-11 10:17:59 +0200224err_hd_cport_release:
225 gb_hd_cport_release(hd, hd_cport_id);
Johan Hovold210b5082016-01-19 12:51:26 +0100226err_unlock:
227 mutex_unlock(&gb_connection_mutex);
Johan Hovold10f9fa12015-07-23 10:50:01 +0200228
Johan Hovold24e094d2016-01-21 17:34:16 +0100229 return ERR_PTR(ret);
Alex Elderc68adb22014-10-01 21:54:14 -0500230}
231
Johan Hovold2566fae2015-11-25 15:59:11 +0100232struct gb_connection *
Johan Hovoldf7ee0812016-01-21 17:34:21 +0100233gb_connection_create_static(struct gb_host_device *hd, u16 hd_cport_id,
234 gb_request_handler_t handler)
Johan Hovold2566fae2015-11-25 15:59:11 +0100235{
Johan Hovoldcb033182016-03-03 13:34:36 +0100236 return _gb_connection_create(hd, hd_cport_id, NULL, NULL, 0, handler,
Johan Hovold3094f9472016-06-22 11:42:04 +0200237 GB_CONNECTION_FLAG_HIGH_PRIO);
Johan Hovold2566fae2015-11-25 15:59:11 +0100238}
239
240struct gb_connection *
Johan Hovold59507e22016-01-21 17:34:12 +0100241gb_connection_create_control(struct gb_interface *intf)
242{
Johan Hovoldaca7aab2016-05-27 17:26:25 +0200243 return _gb_connection_create(intf->hd, -1, intf, NULL, 0, NULL,
Johan Hovold3094f9472016-06-22 11:42:04 +0200244 GB_CONNECTION_FLAG_CONTROL |
245 GB_CONNECTION_FLAG_HIGH_PRIO);
Johan Hovold59507e22016-01-21 17:34:12 +0100246}
247
248struct gb_connection *
Johan Hovoldf7ee0812016-01-21 17:34:21 +0100249gb_connection_create(struct gb_bundle *bundle, u16 cport_id,
250 gb_request_handler_t handler)
Johan Hovold2566fae2015-11-25 15:59:11 +0100251{
Johan Hovold8cff6c62016-01-21 17:34:14 +0100252 struct gb_interface *intf = bundle->intf;
253
Johan Hovoldf7ee0812016-01-21 17:34:21 +0100254 return _gb_connection_create(intf->hd, -1, intf, bundle, cport_id,
Johan Hovoldcb033182016-03-03 13:34:36 +0100255 handler, 0);
Johan Hovold2566fae2015-11-25 15:59:11 +0100256}
Johan Hovold96c2af52016-01-21 17:34:15 +0100257EXPORT_SYMBOL_GPL(gb_connection_create);
Johan Hovold2566fae2015-11-25 15:59:11 +0100258
Johan Hovoldcb033182016-03-03 13:34:36 +0100259struct gb_connection *
260gb_connection_create_flags(struct gb_bundle *bundle, u16 cport_id,
261 gb_request_handler_t handler,
262 unsigned long flags)
263{
264 struct gb_interface *intf = bundle->intf;
265
Johan Hovold1ba30c32016-06-22 11:42:03 +0200266 if (WARN_ON_ONCE(flags & GB_CONNECTION_FLAG_CORE_MASK))
267 flags &= ~GB_CONNECTION_FLAG_CORE_MASK;
268
Johan Hovoldcb033182016-03-03 13:34:36 +0100269 return _gb_connection_create(intf->hd, -1, intf, bundle, cport_id,
270 handler, flags);
271}
272EXPORT_SYMBOL_GPL(gb_connection_create_flags);
273
Johan Hovold781ac862016-05-11 10:17:57 +0200274struct gb_connection *
275gb_connection_create_offloaded(struct gb_bundle *bundle, u16 cport_id,
276 unsigned long flags)
277{
Johan Hovold781ac862016-05-11 10:17:57 +0200278 flags |= GB_CONNECTION_FLAG_OFFLOADED;
279
Johan Hovold1ba30c32016-06-22 11:42:03 +0200280 return gb_connection_create_flags(bundle, cport_id, NULL, flags);
Johan Hovold781ac862016-05-11 10:17:57 +0200281}
282EXPORT_SYMBOL_GPL(gb_connection_create_offloaded);
283
Johan Hovoldd7ea30a52015-09-17 13:17:26 +0200284static int gb_connection_hd_cport_enable(struct gb_connection *connection)
285{
Johan Hovold25376362015-11-03 18:03:23 +0100286 struct gb_host_device *hd = connection->hd;
Johan Hovoldd7ea30a52015-09-17 13:17:26 +0200287 int ret;
288
289 if (!hd->driver->cport_enable)
290 return 0;
291
Johan Hovold6910fa22016-06-22 11:42:02 +0200292 ret = hd->driver->cport_enable(hd, connection->hd_cport_id,
293 connection->flags);
Johan Hovoldd7ea30a52015-09-17 13:17:26 +0200294 if (ret) {
Viresh Kumar62e04622016-06-09 16:34:42 +0530295 dev_err(&hd->dev, "%s: failed to enable host cport: %d\n",
Johan Hovold3cbe52c2016-05-27 17:26:28 +0200296 connection->name, ret);
Johan Hovoldd7ea30a52015-09-17 13:17:26 +0200297 return ret;
298 }
299
300 return 0;
301}
302
303static void gb_connection_hd_cport_disable(struct gb_connection *connection)
304{
Johan Hovold25376362015-11-03 18:03:23 +0100305 struct gb_host_device *hd = connection->hd;
Johan Hovold3cbe52c2016-05-27 17:26:28 +0200306 int ret;
Johan Hovoldd7ea30a52015-09-17 13:17:26 +0200307
308 if (!hd->driver->cport_disable)
309 return;
310
Johan Hovold3cbe52c2016-05-27 17:26:28 +0200311 ret = hd->driver->cport_disable(hd, connection->hd_cport_id);
312 if (ret) {
Viresh Kumar62e04622016-06-09 16:34:42 +0530313 dev_err(&hd->dev, "%s: failed to disable host cport: %d\n",
Johan Hovold3cbe52c2016-05-27 17:26:28 +0200314 connection->name, ret);
315 }
Johan Hovoldd7ea30a52015-09-17 13:17:26 +0200316}
317
Johan Hovoldaac08392016-08-26 12:55:49 +0200318static int gb_connection_hd_cport_connected(struct gb_connection *connection)
319{
320 struct gb_host_device *hd = connection->hd;
321 int ret;
322
323 if (!hd->driver->cport_connected)
324 return 0;
325
326 ret = hd->driver->cport_connected(hd, connection->hd_cport_id);
327 if (ret) {
328 dev_err(&hd->dev, "%s: failed to set connected state: %d\n",
329 connection->name, ret);
330 return ret;
331 }
332
333 return 0;
334}
335
Johan Hovold800d6c82016-05-27 17:26:37 +0200336static int gb_connection_hd_cport_flush(struct gb_connection *connection)
337{
338 struct gb_host_device *hd = connection->hd;
339 int ret;
340
341 if (!hd->driver->cport_flush)
342 return 0;
343
344 ret = hd->driver->cport_flush(hd, connection->hd_cport_id);
345 if (ret) {
346 dev_err(&hd->dev, "%s: failed to flush host cport: %d\n",
347 connection->name, ret);
348 return ret;
349 }
350
351 return 0;
352}
353
Johan Hovoldaac08392016-08-26 12:55:49 +0200354static int gb_connection_hd_cport_quiesce(struct gb_connection *connection)
Fabien Parent9ed5e1b2016-02-23 18:46:08 +0100355{
356 struct gb_host_device *hd = connection->hd;
Johan Hovoldaac08392016-08-26 12:55:49 +0200357 size_t peer_space;
Fabien Parent9ed5e1b2016-02-23 18:46:08 +0100358 int ret;
359
Johan Hovoldaac08392016-08-26 12:55:49 +0200360 peer_space = sizeof(struct gb_operation_msg_hdr) +
361 sizeof(struct gb_cport_shutdown_request);
Fabien Parent9ed5e1b2016-02-23 18:46:08 +0100362
Johan Hovoldaac08392016-08-26 12:55:49 +0200363 if (connection->mode_switch)
364 peer_space += sizeof(struct gb_operation_msg_hdr);
365
366 ret = hd->driver->cport_quiesce(hd, connection->hd_cport_id,
367 peer_space,
368 GB_CONNECTION_CPORT_QUIESCE_TIMEOUT);
Fabien Parent9ed5e1b2016-02-23 18:46:08 +0100369 if (ret) {
Johan Hovoldaac08392016-08-26 12:55:49 +0200370 dev_err(&hd->dev, "%s: failed to quiesce host cport: %d\n",
371 connection->name, ret);
Fabien Parent9ed5e1b2016-02-23 18:46:08 +0100372 return ret;
373 }
374
375 return 0;
376}
377
Johan Hovoldaac08392016-08-26 12:55:49 +0200378static int gb_connection_hd_cport_clear(struct gb_connection *connection)
Fabien Parent9ed5e1b2016-02-23 18:46:08 +0100379{
380 struct gb_host_device *hd = connection->hd;
Johan Hovoldaac08392016-08-26 12:55:49 +0200381 int ret;
Fabien Parent9ed5e1b2016-02-23 18:46:08 +0100382
Johan Hovoldaac08392016-08-26 12:55:49 +0200383 ret = hd->driver->cport_clear(hd, connection->hd_cport_id);
384 if (ret) {
385 dev_err(&hd->dev, "%s: failed to clear host cport: %d\n",
386 connection->name, ret);
387 return ret;
388 }
Fabien Parent9ed5e1b2016-02-23 18:46:08 +0100389
Johan Hovoldaac08392016-08-26 12:55:49 +0200390 return 0;
Fabien Parent9ed5e1b2016-02-23 18:46:08 +0100391}
392
Alex Elderc68adb22014-10-01 21:54:14 -0500393/*
Johan Hovolda95c2582015-09-17 13:17:21 +0200394 * Request the SVC to create a connection from AP's cport to interface's
395 * cport.
396 */
397static int
398gb_connection_svc_connection_create(struct gb_connection *connection)
399{
Johan Hovold25376362015-11-03 18:03:23 +0100400 struct gb_host_device *hd = connection->hd;
Viresh Kumar1575ef12015-10-07 15:40:24 -0400401 struct gb_interface *intf;
Johan Hovold27f25c12016-03-03 13:34:38 +0100402 u8 cport_flags;
Johan Hovolda95c2582015-09-17 13:17:21 +0200403 int ret;
404
Johan Hovold4ec15742015-11-25 15:59:13 +0100405 if (gb_connection_is_static(connection))
Johan Hovold00ad6972016-05-27 17:26:31 +0200406 return 0;
Johan Hovolda95c2582015-09-17 13:17:21 +0200407
Johan Hovold35822c02015-11-25 15:59:22 +0100408 intf = connection->intf;
Johan Hovold27f25c12016-03-03 13:34:38 +0100409
Johan Hovoldec199cc2016-03-29 18:56:03 -0400410 /*
Johan Hovold0e9b41a2016-05-11 10:17:55 +0200411 * Enable either E2EFC or CSD, unless no flow control is requested.
Johan Hovoldec199cc2016-03-29 18:56:03 -0400412 */
Johan Hovold27f25c12016-03-03 13:34:38 +0100413 cport_flags = GB_SVC_CPORT_FLAG_CSV_N;
Johan Hovold0e9b41a2016-05-11 10:17:55 +0200414 if (gb_connection_flow_control_disabled(connection)) {
Johan Hovold27f25c12016-03-03 13:34:38 +0100415 cport_flags |= GB_SVC_CPORT_FLAG_CSD_N;
Johan Hovold64a6d132016-03-03 13:34:39 +0100416 } else if (gb_connection_e2efc_enabled(connection)) {
Johan Hovold27f25c12016-03-03 13:34:38 +0100417 cport_flags |= GB_SVC_CPORT_FLAG_CSD_N |
418 GB_SVC_CPORT_FLAG_E2EFC;
419 }
420
Johan Hovolda95c2582015-09-17 13:17:21 +0200421 ret = gb_svc_connection_create(hd->svc,
Johan Hovold66069fb2015-11-25 15:59:09 +0100422 hd->svc->ap_intf_id,
Johan Hovolda95c2582015-09-17 13:17:21 +0200423 connection->hd_cport_id,
Viresh Kumar1575ef12015-10-07 15:40:24 -0400424 intf->interface_id,
425 connection->intf_cport_id,
Johan Hovold27f25c12016-03-03 13:34:38 +0100426 cport_flags);
Johan Hovolda95c2582015-09-17 13:17:21 +0200427 if (ret) {
Johan Hovold4c4b5022015-11-25 15:59:15 +0100428 dev_err(&connection->hd->dev,
429 "%s: failed to create svc connection: %d\n",
430 connection->name, ret);
Johan Hovolda95c2582015-09-17 13:17:21 +0200431 return ret;
432 }
433
Johan Hovolda95c2582015-09-17 13:17:21 +0200434 return 0;
435}
436
Viresh Kumar1b7a9cd2015-09-07 16:01:22 +0530437static void
438gb_connection_svc_connection_destroy(struct gb_connection *connection)
439{
Johan Hovold4ec15742015-11-25 15:59:13 +0100440 if (gb_connection_is_static(connection))
Viresh Kumar1b7a9cd2015-09-07 16:01:22 +0530441 return;
442
443 gb_svc_connection_destroy(connection->hd->svc,
Johan Hovold66069fb2015-11-25 15:59:09 +0100444 connection->hd->svc->ap_intf_id,
Viresh Kumar1b7a9cd2015-09-07 16:01:22 +0530445 connection->hd_cport_id,
Johan Hovold35822c02015-11-25 15:59:22 +0100446 connection->intf->interface_id,
Viresh Kumar1b7a9cd2015-09-07 16:01:22 +0530447 connection->intf_cport_id);
448}
449
Johan Hovold9d7fc252015-09-17 13:17:24 +0200450/* Inform Interface about active CPorts */
451static int gb_connection_control_connected(struct gb_connection *connection)
452{
Johan Hovold9d7fc252015-09-17 13:17:24 +0200453 struct gb_control *control;
454 u16 cport_id = connection->intf_cport_id;
455 int ret;
456
Johan Hovoldbc3be172016-01-19 12:51:12 +0100457 if (gb_connection_is_static(connection))
Johan Hovold9d7fc252015-09-17 13:17:24 +0200458 return 0;
459
Johan Hovoldaca7aab2016-05-27 17:26:25 +0200460 if (gb_connection_is_control(connection))
Johan Hovoldbc3be172016-01-19 12:51:12 +0100461 return 0;
Johan Hovold9d7fc252015-09-17 13:17:24 +0200462
Johan Hovoldaca7aab2016-05-27 17:26:25 +0200463 control = connection->intf->control;
464
Johan Hovold9d7fc252015-09-17 13:17:24 +0200465 ret = gb_control_connected_operation(control, cport_id);
466 if (ret) {
Greg Kroah-Hartman30482c12015-10-16 16:56:23 -0700467 dev_err(&connection->bundle->dev,
468 "failed to connect cport: %d\n", ret);
Johan Hovold9d7fc252015-09-17 13:17:24 +0200469 return ret;
470 }
471
472 return 0;
473}
474
Johan Hovold3de5acf2016-05-27 17:26:36 +0200475static void
476gb_connection_control_disconnecting(struct gb_connection *connection)
477{
478 struct gb_control *control;
479 u16 cport_id = connection->intf_cport_id;
480 int ret;
481
482 if (gb_connection_is_static(connection))
483 return;
484
485 control = connection->intf->control;
486
487 ret = gb_control_disconnecting_operation(control, cport_id);
488 if (ret) {
489 dev_err(&connection->hd->dev,
490 "%s: failed to send disconnecting: %d\n",
491 connection->name, ret);
492 }
493}
494
Johan Hovold72d74822015-09-17 13:17:23 +0200495static void
496gb_connection_control_disconnected(struct gb_connection *connection)
Viresh Kumar18690652015-08-11 07:35:56 +0530497{
498 struct gb_control *control;
Johan Hovold72d74822015-09-17 13:17:23 +0200499 u16 cport_id = connection->intf_cport_id;
Viresh Kumar18690652015-08-11 07:35:56 +0530500 int ret;
501
Johan Hovoldbc3be172016-01-19 12:51:12 +0100502 if (gb_connection_is_static(connection))
Viresh Kumar18690652015-08-11 07:35:56 +0530503 return;
504
Johan Hovoldaca7aab2016-05-27 17:26:25 +0200505 control = connection->intf->control;
506
Johan Hovold55742d22016-05-27 17:26:40 +0200507 if (gb_connection_is_control(connection)) {
508 if (connection->mode_switch) {
509 ret = gb_control_mode_switch_operation(control);
510 if (ret) {
511 /*
512 * Allow mode switch to time out waiting for
513 * mailbox event.
514 */
515 return;
516 }
517 }
518
519 return;
520 }
521
Viresh Kumar18690652015-08-11 07:35:56 +0530522 ret = gb_control_disconnected_operation(control, cport_id);
Johan Hovold72d74822015-09-17 13:17:23 +0200523 if (ret) {
Greg Kroah-Hartman30482c12015-10-16 16:56:23 -0700524 dev_warn(&connection->bundle->dev,
525 "failed to disconnect cport: %d\n", ret);
Johan Hovold72d74822015-09-17 13:17:23 +0200526 }
Viresh Kumar18690652015-08-11 07:35:56 +0530527}
528
Johan Hovoldaac08392016-08-26 12:55:49 +0200529static int gb_connection_shutdown_operation(struct gb_connection *connection,
530 u8 phase)
Johan Hovold3de5acf2016-05-27 17:26:36 +0200531{
Johan Hovoldaac08392016-08-26 12:55:49 +0200532 struct gb_cport_shutdown_request *req;
Johan Hovold3de5acf2016-05-27 17:26:36 +0200533 struct gb_operation *operation;
534 int ret;
535
536 operation = gb_operation_create_core(connection,
Johan Hovoldaac08392016-08-26 12:55:49 +0200537 GB_REQUEST_TYPE_CPORT_SHUTDOWN,
538 sizeof(*req), 0, 0,
Johan Hovold3de5acf2016-05-27 17:26:36 +0200539 GFP_KERNEL);
540 if (!operation)
541 return -ENOMEM;
542
Johan Hovoldaac08392016-08-26 12:55:49 +0200543 req = operation->request->payload;
544 req->phase = phase;
545
Johan Hovold3de5acf2016-05-27 17:26:36 +0200546 ret = gb_operation_request_send_sync(operation);
547
548 gb_operation_put(operation);
549
550 return ret;
551}
552
Johan Hovoldaac08392016-08-26 12:55:49 +0200553static int gb_connection_cport_shutdown(struct gb_connection *connection,
554 u8 phase)
Johan Hovold3de5acf2016-05-27 17:26:36 +0200555{
556 struct gb_host_device *hd = connection->hd;
Johan Hovoldaac08392016-08-26 12:55:49 +0200557 const struct gb_hd_driver *drv = hd->driver;
Johan Hovold3de5acf2016-05-27 17:26:36 +0200558 int ret;
559
560 if (gb_connection_is_static(connection))
561 return 0;
562
563 if (gb_connection_is_offloaded(connection)) {
Johan Hovoldaac08392016-08-26 12:55:49 +0200564 if (!drv->cport_shutdown)
Johan Hovold3de5acf2016-05-27 17:26:36 +0200565 return 0;
566
Johan Hovoldaac08392016-08-26 12:55:49 +0200567 ret = drv->cport_shutdown(hd, connection->hd_cport_id, phase,
568 GB_OPERATION_TIMEOUT_DEFAULT);
Johan Hovold3de5acf2016-05-27 17:26:36 +0200569 } else {
Johan Hovoldaac08392016-08-26 12:55:49 +0200570 ret = gb_connection_shutdown_operation(connection, phase);
Johan Hovold3de5acf2016-05-27 17:26:36 +0200571 }
572
573 if (ret) {
Johan Hovoldaac08392016-08-26 12:55:49 +0200574 dev_err(&hd->dev, "%s: failed to send cport shutdown (phase %d): %d\n",
575 connection->name, phase, ret);
Johan Hovold3de5acf2016-05-27 17:26:36 +0200576 return ret;
577 }
578
579 return 0;
580}
581
Johan Hovoldaac08392016-08-26 12:55:49 +0200582static int
583gb_connection_cport_shutdown_phase_1(struct gb_connection *connection)
584{
585 return gb_connection_cport_shutdown(connection, 1);
586}
587
588static int
589gb_connection_cport_shutdown_phase_2(struct gb_connection *connection)
590{
591 return gb_connection_cport_shutdown(connection, 2);
592}
593
Johan Hovold2846d3e2015-09-17 13:17:25 +0200594/*
Johan Hovold520c6ea2016-01-19 12:51:04 +0100595 * Cancel all active operations on a connection.
596 *
Johan Hovold3de5acf2016-05-27 17:26:36 +0200597 * Locking: Called with connection lock held and state set to DISABLED or
598 * DISCONNECTING.
Johan Hovold520c6ea2016-01-19 12:51:04 +0100599 */
600static void gb_connection_cancel_operations(struct gb_connection *connection,
Johan Hovolda29bac62016-06-27 20:07:09 +0200601 int errno)
Viresh Kumar127c1fb2016-01-28 15:50:48 +0530602 __must_hold(&connection->lock)
Johan Hovold520c6ea2016-01-19 12:51:04 +0100603{
604 struct gb_operation *operation;
605
606 while (!list_empty(&connection->operations)) {
607 operation = list_last_entry(&connection->operations,
608 struct gb_operation, links);
609 gb_operation_get(operation);
Johan Hovolda29bac62016-06-27 20:07:09 +0200610 spin_unlock_irq(&connection->lock);
Johan Hovold520c6ea2016-01-19 12:51:04 +0100611
612 if (gb_operation_is_incoming(operation))
613 gb_operation_cancel_incoming(operation, errno);
614 else
615 gb_operation_cancel(operation, errno);
616
617 gb_operation_put(operation);
618
Johan Hovolda29bac62016-06-27 20:07:09 +0200619 spin_lock_irq(&connection->lock);
Johan Hovold520c6ea2016-01-19 12:51:04 +0100620 }
621}
622
Johan Hovoldbeb6b7f2016-01-19 12:51:08 +0100623/*
624 * Cancel all active incoming operations on a connection.
625 *
626 * Locking: Called with connection lock held and state set to ENABLED_TX.
627 */
628static void
629gb_connection_flush_incoming_operations(struct gb_connection *connection,
Johan Hovolda29bac62016-06-27 20:07:09 +0200630 int errno)
Viresh Kumar127c1fb2016-01-28 15:50:48 +0530631 __must_hold(&connection->lock)
Johan Hovoldbeb6b7f2016-01-19 12:51:08 +0100632{
633 struct gb_operation *operation;
634 bool incoming;
635
636 while (!list_empty(&connection->operations)) {
637 incoming = false;
638 list_for_each_entry(operation, &connection->operations,
639 links) {
640 if (gb_operation_is_incoming(operation)) {
641 gb_operation_get(operation);
642 incoming = true;
643 break;
644 }
645 }
646
647 if (!incoming)
648 break;
649
Johan Hovolda29bac62016-06-27 20:07:09 +0200650 spin_unlock_irq(&connection->lock);
Johan Hovoldbeb6b7f2016-01-19 12:51:08 +0100651
652 /* FIXME: flush, not cancel? */
653 gb_operation_cancel_incoming(operation, errno);
654 gb_operation_put(operation);
655
Johan Hovolda29bac62016-06-27 20:07:09 +0200656 spin_lock_irq(&connection->lock);
Johan Hovoldbeb6b7f2016-01-19 12:51:08 +0100657 }
658}
659
Johan Hovoldf7ee0812016-01-21 17:34:21 +0100660/*
661 * _gb_connection_enable() - enable a connection
662 * @connection: connection to enable
663 * @rx: whether to enable incoming requests
664 *
665 * Connection-enable helper for DISABLED->ENABLED, DISABLED->ENABLED_TX, and
666 * ENABLED_TX->ENABLED state transitions.
667 *
668 * Locking: Caller holds connection->mutex.
669 */
670static int _gb_connection_enable(struct gb_connection *connection, bool rx)
Alex Elder574341c2014-10-16 06:35:35 -0500671{
Alex Elder36561f22014-10-22 02:04:30 -0500672 int ret;
673
Johan Hovoldf7ee0812016-01-21 17:34:21 +0100674 /* Handle ENABLED_TX -> ENABLED transitions. */
Johan Hovold570dfa72016-01-19 12:51:07 +0100675 if (connection->state == GB_CONNECTION_STATE_ENABLED_TX) {
Johan Hovoldf7ee0812016-01-21 17:34:21 +0100676 if (!(connection->handler && rx))
677 return 0;
Johan Hovold570dfa72016-01-19 12:51:07 +0100678
Johan Hovolda29bac62016-06-27 20:07:09 +0200679 spin_lock_irq(&connection->lock);
Johan Hovold570dfa72016-01-19 12:51:07 +0100680 connection->state = GB_CONNECTION_STATE_ENABLED;
Johan Hovolda29bac62016-06-27 20:07:09 +0200681 spin_unlock_irq(&connection->lock);
Johan Hovold570dfa72016-01-19 12:51:07 +0100682
Johan Hovoldf7ee0812016-01-21 17:34:21 +0100683 return 0;
Johan Hovold570dfa72016-01-19 12:51:07 +0100684 }
685
Johan Hovoldd7ea30a52015-09-17 13:17:26 +0200686 ret = gb_connection_hd_cport_enable(connection);
Johan Hovolda95c2582015-09-17 13:17:21 +0200687 if (ret)
Johan Hovoldf7ee0812016-01-21 17:34:21 +0100688 return ret;
Viresh Kumara1163fa2015-09-07 16:01:21 +0530689
Johan Hovoldd7ea30a52015-09-17 13:17:26 +0200690 ret = gb_connection_svc_connection_create(connection);
691 if (ret)
Johan Hovoldaac08392016-08-26 12:55:49 +0200692 goto err_hd_cport_clear;
Johan Hovoldd7ea30a52015-09-17 13:17:26 +0200693
Johan Hovoldaac08392016-08-26 12:55:49 +0200694 ret = gb_connection_hd_cport_connected(connection);
Johan Hovold00ad6972016-05-27 17:26:31 +0200695 if (ret)
696 goto err_svc_connection_destroy;
697
Johan Hovolda29bac62016-06-27 20:07:09 +0200698 spin_lock_irq(&connection->lock);
Johan Hovoldf7ee0812016-01-21 17:34:21 +0100699 if (connection->handler && rx)
Johan Hovold570dfa72016-01-19 12:51:07 +0100700 connection->state = GB_CONNECTION_STATE_ENABLED;
701 else
702 connection->state = GB_CONNECTION_STATE_ENABLED_TX;
Johan Hovolda29bac62016-06-27 20:07:09 +0200703 spin_unlock_irq(&connection->lock);
Johan Hovoldcad09a82015-07-14 15:43:30 +0200704
Johan Hovold4d0bee12016-01-08 20:13:45 +0100705 ret = gb_connection_control_connected(connection);
706 if (ret)
Johan Hovold3de5acf2016-05-27 17:26:36 +0200707 goto err_control_disconnecting;
Johan Hovold4d0bee12016-01-08 20:13:45 +0100708
Johan Hovold8d7a7122015-09-17 13:17:22 +0200709 return 0;
710
Johan Hovold3de5acf2016-05-27 17:26:36 +0200711err_control_disconnecting:
Johan Hovolda29bac62016-06-27 20:07:09 +0200712 spin_lock_irq(&connection->lock);
Johan Hovold3de5acf2016-05-27 17:26:36 +0200713 connection->state = GB_CONNECTION_STATE_DISCONNECTING;
Johan Hovolda29bac62016-06-27 20:07:09 +0200714 gb_connection_cancel_operations(connection, -ESHUTDOWN);
715 spin_unlock_irq(&connection->lock);
Johan Hovold3ea6a812016-01-08 20:13:46 +0100716
Johan Hovold800d6c82016-05-27 17:26:37 +0200717 /* Transmit queue should already be empty. */
718 gb_connection_hd_cport_flush(connection);
719
Johan Hovoldaac08392016-08-26 12:55:49 +0200720 gb_connection_control_disconnecting(connection);
721 gb_connection_cport_shutdown_phase_1(connection);
722 gb_connection_hd_cport_quiesce(connection);
723 gb_connection_cport_shutdown_phase_2(connection);
Johan Hovold3de5acf2016-05-27 17:26:36 +0200724 gb_connection_control_disconnected(connection);
725 connection->state = GB_CONNECTION_STATE_DISABLED;
Johan Hovold00ad6972016-05-27 17:26:31 +0200726err_svc_connection_destroy:
Johan Hovold3ea6a812016-01-08 20:13:46 +0100727 gb_connection_svc_connection_destroy(connection);
Johan Hovoldaac08392016-08-26 12:55:49 +0200728err_hd_cport_clear:
729 gb_connection_hd_cport_clear(connection);
730
Johan Hovold3ea6a812016-01-08 20:13:46 +0100731 gb_connection_hd_cport_disable(connection);
Johan Hovoldf7ee0812016-01-21 17:34:21 +0100732
733 return ret;
734}
735
736int gb_connection_enable(struct gb_connection *connection)
737{
738 int ret = 0;
739
740 mutex_lock(&connection->mutex);
741
742 if (connection->state == GB_CONNECTION_STATE_ENABLED)
743 goto out_unlock;
744
745 ret = _gb_connection_enable(connection, true);
Alex Elder79c8c642016-06-03 15:55:37 -0500746 if (!ret)
747 trace_gb_connection_enable(connection);
748
Johan Hovoldf7ee0812016-01-21 17:34:21 +0100749out_unlock:
Johan Hovold23268782016-01-19 12:51:06 +0100750 mutex_unlock(&connection->mutex);
Johan Hovold3ea6a812016-01-08 20:13:46 +0100751
752 return ret;
753}
754EXPORT_SYMBOL_GPL(gb_connection_enable);
755
Johan Hovoldf7ee0812016-01-21 17:34:21 +0100756int gb_connection_enable_tx(struct gb_connection *connection)
757{
758 int ret = 0;
759
760 mutex_lock(&connection->mutex);
761
762 if (connection->state == GB_CONNECTION_STATE_ENABLED) {
763 ret = -EINVAL;
764 goto out_unlock;
765 }
766
767 if (connection->state == GB_CONNECTION_STATE_ENABLED_TX)
768 goto out_unlock;
769
770 ret = _gb_connection_enable(connection, false);
Alex Elder79c8c642016-06-03 15:55:37 -0500771 if (!ret)
772 trace_gb_connection_enable(connection);
773
Johan Hovoldf7ee0812016-01-21 17:34:21 +0100774out_unlock:
775 mutex_unlock(&connection->mutex);
776
777 return ret;
778}
779EXPORT_SYMBOL_GPL(gb_connection_enable_tx);
780
Johan Hovoldbeb6b7f2016-01-19 12:51:08 +0100781void gb_connection_disable_rx(struct gb_connection *connection)
782{
783 mutex_lock(&connection->mutex);
784
Johan Hovolda29bac62016-06-27 20:07:09 +0200785 spin_lock_irq(&connection->lock);
Johan Hovoldbeb6b7f2016-01-19 12:51:08 +0100786 if (connection->state != GB_CONNECTION_STATE_ENABLED) {
Johan Hovolda29bac62016-06-27 20:07:09 +0200787 spin_unlock_irq(&connection->lock);
Johan Hovoldbeb6b7f2016-01-19 12:51:08 +0100788 goto out_unlock;
789 }
790 connection->state = GB_CONNECTION_STATE_ENABLED_TX;
Johan Hovolda29bac62016-06-27 20:07:09 +0200791 gb_connection_flush_incoming_operations(connection, -ESHUTDOWN);
792 spin_unlock_irq(&connection->lock);
Johan Hovoldbeb6b7f2016-01-19 12:51:08 +0100793
Alex Elder79c8c642016-06-03 15:55:37 -0500794 trace_gb_connection_disable(connection);
795
Johan Hovoldbeb6b7f2016-01-19 12:51:08 +0100796out_unlock:
797 mutex_unlock(&connection->mutex);
798}
Vaibhav Hiremath6d58e712016-05-05 14:32:29 +0530799EXPORT_SYMBOL_GPL(gb_connection_disable_rx);
Johan Hovoldbeb6b7f2016-01-19 12:51:08 +0100800
Johan Hovold55742d22016-05-27 17:26:40 +0200801void gb_connection_mode_switch_prepare(struct gb_connection *connection)
802{
803 connection->mode_switch = true;
804}
805
806void gb_connection_mode_switch_complete(struct gb_connection *connection)
807{
808 gb_connection_svc_connection_destroy(connection);
Johan Hovoldaac08392016-08-26 12:55:49 +0200809 gb_connection_hd_cport_clear(connection);
810
Johan Hovold55742d22016-05-27 17:26:40 +0200811 gb_connection_hd_cport_disable(connection);
Johan Hovoldaac08392016-08-26 12:55:49 +0200812
Johan Hovold55742d22016-05-27 17:26:40 +0200813 connection->mode_switch = false;
814}
815
Johan Hovold3ea6a812016-01-08 20:13:46 +0100816void gb_connection_disable(struct gb_connection *connection)
817{
Johan Hovold23268782016-01-19 12:51:06 +0100818 mutex_lock(&connection->mutex);
819
Johan Hovold81fba2492016-01-19 12:51:03 +0100820 if (connection->state == GB_CONNECTION_STATE_DISABLED)
Johan Hovold23268782016-01-19 12:51:06 +0100821 goto out_unlock;
Johan Hovold81fba2492016-01-19 12:51:03 +0100822
Viresh Kumar0698be02016-06-15 08:25:56 +0530823 trace_gb_connection_disable(connection);
824
Johan Hovolda29bac62016-06-27 20:07:09 +0200825 spin_lock_irq(&connection->lock);
Johan Hovold3de5acf2016-05-27 17:26:36 +0200826 connection->state = GB_CONNECTION_STATE_DISCONNECTING;
Johan Hovolda29bac62016-06-27 20:07:09 +0200827 gb_connection_cancel_operations(connection, -ESHUTDOWN);
828 spin_unlock_irq(&connection->lock);
Johan Hovold81fba2492016-01-19 12:51:03 +0100829
Johan Hovold800d6c82016-05-27 17:26:37 +0200830 gb_connection_hd_cport_flush(connection);
831
Johan Hovoldaac08392016-08-26 12:55:49 +0200832 gb_connection_control_disconnecting(connection);
833 gb_connection_cport_shutdown_phase_1(connection);
834 gb_connection_hd_cport_quiesce(connection);
835 gb_connection_cport_shutdown_phase_2(connection);
Johan Hovold3de5acf2016-05-27 17:26:36 +0200836 gb_connection_control_disconnected(connection);
837
838 connection->state = GB_CONNECTION_STATE_DISABLED;
839
Johan Hovold55742d22016-05-27 17:26:40 +0200840 /* control-connection tear down is deferred when mode switching */
841 if (!connection->mode_switch) {
842 gb_connection_svc_connection_destroy(connection);
Johan Hovoldaac08392016-08-26 12:55:49 +0200843 gb_connection_hd_cport_clear(connection);
844
Johan Hovold55742d22016-05-27 17:26:40 +0200845 gb_connection_hd_cport_disable(connection);
846 }
Johan Hovold23268782016-01-19 12:51:06 +0100847
848out_unlock:
849 mutex_unlock(&connection->mutex);
Johan Hovold3ea6a812016-01-08 20:13:46 +0100850}
851EXPORT_SYMBOL_GPL(gb_connection_disable);
852
Johan Hovold7aefe792016-05-27 17:26:22 +0200853/* Disable a connection without communicating with the remote end. */
854void gb_connection_disable_forced(struct gb_connection *connection)
855{
856 mutex_lock(&connection->mutex);
857
858 if (connection->state == GB_CONNECTION_STATE_DISABLED)
859 goto out_unlock;
860
Viresh Kumar0698be02016-06-15 08:25:56 +0530861 trace_gb_connection_disable(connection);
862
Johan Hovolda29bac62016-06-27 20:07:09 +0200863 spin_lock_irq(&connection->lock);
Johan Hovold7aefe792016-05-27 17:26:22 +0200864 connection->state = GB_CONNECTION_STATE_DISABLED;
Johan Hovolda29bac62016-06-27 20:07:09 +0200865 gb_connection_cancel_operations(connection, -ESHUTDOWN);
866 spin_unlock_irq(&connection->lock);
Johan Hovold7aefe792016-05-27 17:26:22 +0200867
Johan Hovold800d6c82016-05-27 17:26:37 +0200868 gb_connection_hd_cport_flush(connection);
Johan Hovold7aefe792016-05-27 17:26:22 +0200869
Johan Hovoldaac08392016-08-26 12:55:49 +0200870 gb_connection_svc_connection_destroy(connection);
871 gb_connection_hd_cport_clear(connection);
872
873 gb_connection_hd_cport_disable(connection);
Johan Hovold7aefe792016-05-27 17:26:22 +0200874out_unlock:
875 mutex_unlock(&connection->mutex);
876}
877EXPORT_SYMBOL_GPL(gb_connection_disable_forced);
878
Johan Hovoldc3681f62016-01-19 12:51:23 +0100879/* Caller must have disabled the connection before destroying it. */
Viresh Kumarfda23812015-08-31 17:21:13 +0530880void gb_connection_destroy(struct gb_connection *connection)
881{
Johan Hovold2edbf5f2016-01-19 12:51:22 +0100882 if (!connection)
Viresh Kumarfda23812015-08-31 17:21:13 +0530883 return;
884
Alex Elder814ae5312016-05-17 09:13:16 -0500885 if (WARN_ON(connection->state != GB_CONNECTION_STATE_DISABLED))
886 gb_connection_disable(connection);
887
Johan Hovold210b5082016-01-19 12:51:26 +0100888 mutex_lock(&gb_connection_mutex);
889
Johan Hovold0b1d2622016-06-27 20:07:10 +0200890 spin_lock_irq(&gb_connections_lock);
Viresh Kumarfda23812015-08-31 17:21:13 +0530891 list_del(&connection->bundle_links);
892 list_del(&connection->hd_links);
Johan Hovold0b1d2622016-06-27 20:07:10 +0200893 spin_unlock_irq(&gb_connections_lock);
Viresh Kumarfda23812015-08-31 17:21:13 +0530894
Johan Hovoldc3681f62016-01-19 12:51:23 +0100895 destroy_workqueue(connection->wq);
896
Johan Hovold74a5d932016-05-11 10:17:59 +0200897 gb_hd_cport_release(connection->hd, connection->hd_cport_id);
Viresh Kumarfda23812015-08-31 17:21:13 +0530898 connection->hd_cport_id = CPORT_ID_BAD;
899
Johan Hovold210b5082016-01-19 12:51:26 +0100900 mutex_unlock(&gb_connection_mutex);
901
Johan Hovold0e46fab2016-01-19 12:51:25 +0100902 gb_connection_put(connection);
Viresh Kumarfda23812015-08-31 17:21:13 +0530903}
Johan Hovold98fdf5a2016-01-21 17:34:09 +0100904EXPORT_SYMBOL_GPL(gb_connection_destroy);
Viresh Kumarfda23812015-08-31 17:21:13 +0530905
Bryan O'Donoghuee7e2efc2015-10-15 16:10:42 +0100906void gb_connection_latency_tag_enable(struct gb_connection *connection)
907{
Johan Hovold25376362015-11-03 18:03:23 +0100908 struct gb_host_device *hd = connection->hd;
Bryan O'Donoghuee7e2efc2015-10-15 16:10:42 +0100909 int ret;
910
911 if (!hd->driver->latency_tag_enable)
912 return;
913
914 ret = hd->driver->latency_tag_enable(hd, connection->hd_cport_id);
915 if (ret) {
Johan Hovold4c4b5022015-11-25 15:59:15 +0100916 dev_err(&connection->hd->dev,
917 "%s: failed to enable latency tag: %d\n",
918 connection->name, ret);
Bryan O'Donoghuee7e2efc2015-10-15 16:10:42 +0100919 }
920}
921EXPORT_SYMBOL_GPL(gb_connection_latency_tag_enable);
922
923void gb_connection_latency_tag_disable(struct gb_connection *connection)
924{
Johan Hovold25376362015-11-03 18:03:23 +0100925 struct gb_host_device *hd = connection->hd;
Bryan O'Donoghuee7e2efc2015-10-15 16:10:42 +0100926 int ret;
927
928 if (!hd->driver->latency_tag_disable)
929 return;
930
931 ret = hd->driver->latency_tag_disable(hd, connection->hd_cport_id);
932 if (ret) {
Johan Hovold4c4b5022015-11-25 15:59:15 +0100933 dev_err(&connection->hd->dev,
934 "%s: failed to disable latency tag: %d\n",
935 connection->name, ret);
Bryan O'Donoghuee7e2efc2015-10-15 16:10:42 +0100936 }
937}
938EXPORT_SYMBOL_GPL(gb_connection_latency_tag_disable);