Blame view

drivers/greybus/connection.c 22.6 KB
eb50fd3a2   Greg Kroah-Hartman   staging: greybus:...
1
  // SPDX-License-Identifier: GPL-2.0
c68adb2f2   Alex Elder   greybus: introduc...
2
3
4
5
  /*
   * Greybus connections
   *
   * Copyright 2014 Google Inc.
a46e96719   Alex Elder   greybus: add Lina...
6
   * Copyright 2014 Linaro Ltd.
c68adb2f2   Alex Elder   greybus: introduc...
7
   */
5a5bc354c   Johan Hovold   greybus: operatio...
8
  #include <linux/workqueue.h>
ec0ad8681   Greg Kroah-Hartman   staging: greybus:...
9
  #include <linux/greybus.h>
5a5bc354c   Johan Hovold   greybus: operatio...
10

79c8c6494   Alex Elder   greybus: tracing:...
11
  #include "greybus_trace.h"
c68adb2f2   Alex Elder   greybus: introduc...
12

aac0839ea   Johan Hovold   greybus: connecti...
13
  #define GB_CONNECTION_CPORT_QUIESCE_TIMEOUT	1000
0e46fab7d   Johan Hovold   greybus: connecti...
14
  static void gb_connection_kref_release(struct kref *kref);
748e1230c   Alex Elder   greybus: fix some...
15
  static DEFINE_SPINLOCK(gb_connections_lock);
210b508e4   Johan Hovold   greybus: connecti...
16
  static DEFINE_MUTEX(gb_connection_mutex);
b53e0c9e8   Johan Hovold   greybus: connecti...
17
  /* Caller holds gb_connection_mutex. */
54131222e   Viresh Kumar   greybus: connecti...
18
  static bool gb_connection_cport_in_use(struct gb_interface *intf, u16 cport_id)
f5c2be9e9   Alex Elder   greybus: connecti...
19
  {
2537636ab   Johan Hovold   greybus: hd: rena...
20
  	struct gb_host_device *hd = intf->hd;
f5c2be9e9   Alex Elder   greybus: connecti...
21
  	struct gb_connection *connection;
0daf17b9e   Johan Hovold   greybus: connecti...
22
23
  	list_for_each_entry(connection, &hd->connections, hd_links) {
  		if (connection->intf == intf &&
8478c35a8   Cristian Sicilia   staging: greybus:...
24
  		    connection->intf_cport_id == cport_id)
54131222e   Viresh Kumar   greybus: connecti...
25
  			return true;
0daf17b9e   Johan Hovold   greybus: connecti...
26
  	}
54131222e   Viresh Kumar   greybus: connecti...
27
  	return false;
f5c2be9e9   Alex Elder   greybus: connecti...
28
  }
0e46fab7d   Johan Hovold   greybus: connecti...
29
30
31
  static void gb_connection_get(struct gb_connection *connection)
  {
  	kref_get(&connection->kref);
79c8c6494   Alex Elder   greybus: tracing:...
32
33
  
  	trace_gb_connection_get(connection);
0e46fab7d   Johan Hovold   greybus: connecti...
34
35
36
37
  }
  
  static void gb_connection_put(struct gb_connection *connection)
  {
79c8c6494   Alex Elder   greybus: tracing:...
38
  	trace_gb_connection_put(connection);
0e46fab7d   Johan Hovold   greybus: connecti...
39
40
41
42
43
44
  	kref_put(&connection->kref, gb_connection_kref_release);
  }
  
  /*
   * Returns a reference-counted pointer to the connection if found.
   */
8bd0ae6e7   Alex Elder   greybus: connecti...
45
  static struct gb_connection *
2537636ab   Johan Hovold   greybus: hd: rena...
46
  gb_connection_hd_find(struct gb_host_device *hd, u16 cport_id)
ee9ebe4d0   Alex Elder   greybus: add bg_h...
47
  {
f5c2be9e9   Alex Elder   greybus: connecti...
48
  	struct gb_connection *connection;
8f5eadb7e   Johan Hovold   greybus: connecti...
49
  	unsigned long flags;
ee9ebe4d0   Alex Elder   greybus: add bg_h...
50

8f5eadb7e   Johan Hovold   greybus: connecti...
51
  	spin_lock_irqsave(&gb_connections_lock, flags);
2c43ce496   Alex Elder   greybus: use a si...
52
  	list_for_each_entry(connection, &hd->connections, hd_links)
0e46fab7d   Johan Hovold   greybus: connecti...
53
54
  		if (connection->hd_cport_id == cport_id) {
  			gb_connection_get(connection);
e86905b6c   Marti Bolivar   greybus: gb_hd_co...
55
  			goto found;
0e46fab7d   Johan Hovold   greybus: connecti...
56
  		}
e86905b6c   Marti Bolivar   greybus: gb_hd_co...
57
  	connection = NULL;
f5c2be9e9   Alex Elder   greybus: connecti...
58
  found:
8f5eadb7e   Johan Hovold   greybus: connecti...
59
  	spin_unlock_irqrestore(&gb_connections_lock, flags);
ee9ebe4d0   Alex Elder   greybus: add bg_h...
60
61
62
  
  	return connection;
  }
de3557d92   Alex Elder   greybus: rename g...
63
64
  /*
   * Callback from the host driver to let us know that data has been
1db0a5ff3   Greg Kroah-Hartman   greybus: bundle: ...
65
   * received on the bundle.
de3557d92   Alex Elder   greybus: rename g...
66
   */
2537636ab   Johan Hovold   greybus: hd: rena...
67
  void greybus_data_rcvd(struct gb_host_device *hd, u16 cport_id,
8478c35a8   Cristian Sicilia   staging: greybus:...
68
  		       u8 *data, size_t length)
374e6a269   Alex Elder   greybus: kill off...
69
70
  {
  	struct gb_connection *connection;
495787a79   Alex Elder   greybus: tracing:...
71
  	trace_gb_hd_in(hd);
12eba9f8e   Viresh Kumar   greybus: connecti...
72
  	connection = gb_connection_hd_find(hd, cport_id);
374e6a269   Alex Elder   greybus: kill off...
73
  	if (!connection) {
2adaefb14   Johan Hovold   greybus: hd: make...
74
  		dev_err(&hd->dev,
374e6a269   Alex Elder   greybus: kill off...
75
76
77
78
  			"nonexistent connection (%zu bytes dropped)
  ", length);
  		return;
  	}
61089e89e   Alex Elder   greybus: rework r...
79
  	gb_connection_recv(connection, data, length);
0e46fab7d   Johan Hovold   greybus: connecti...
80
  	gb_connection_put(connection);
374e6a269   Alex Elder   greybus: kill off...
81
  }
de3557d92   Alex Elder   greybus: rename g...
82
  EXPORT_SYMBOL_GPL(greybus_data_rcvd);
374e6a269   Alex Elder   greybus: kill off...
83

b750fa337   Greg Kroah-Hartman   greybus: connecti...
84
  static void gb_connection_kref_release(struct kref *kref)
f0f61b904   Greg Kroah-Hartman   greybus: hook up ...
85
  {
b750fa337   Greg Kroah-Hartman   greybus: connecti...
86
  	struct gb_connection *connection;
f0f61b904   Greg Kroah-Hartman   greybus: hook up ...
87

b750fa337   Greg Kroah-Hartman   greybus: connecti...
88
  	connection = container_of(kref, struct gb_connection, kref);
c3681f6c9   Johan Hovold   greybus: connecti...
89

79c8c6494   Alex Elder   greybus: tracing:...
90
  	trace_gb_connection_release(connection);
f0f61b904   Greg Kroah-Hartman   greybus: hook up ...
91
92
  	kfree(connection);
  }
729b260a6   Johan Hovold   greybus: connecti...
93
94
95
96
97
98
99
100
101
102
103
104
  static void gb_connection_init_name(struct gb_connection *connection)
  {
  	u16 hd_cport_id = connection->hd_cport_id;
  	u16 cport_id = 0;
  	u8 intf_id = 0;
  
  	if (connection->intf) {
  		intf_id = connection->intf->interface_id;
  		cport_id = connection->intf_cport_id;
  	}
  
  	snprintf(connection->name, sizeof(connection->name),
8478c35a8   Cristian Sicilia   staging: greybus:...
105
  		 "%u/%u:%u", hd_cport_id, intf_id, cport_id);
729b260a6   Johan Hovold   greybus: connecti...
106
  }
177404bd2   Alex Elder   greybus: use ida ...
107
  /*
96c2af5c6   Johan Hovold   greybus: connecti...
108
   * _gb_connection_create() - create a Greybus connection
2566fae6a   Johan Hovold   greybus: connecti...
109
110
111
112
113
   * @hd:			host device of the connection
   * @hd_cport_id:	host-device cport id, or -1 for dynamic allocation
   * @intf:		remote interface, or NULL for static connections
   * @bundle:		remote-interface bundle (may be NULL)
   * @cport_id:		remote-interface cport id, or 0 for static connections
f7ee081e3   Johan Hovold   greybus: connecti...
114
   * @handler:		request handler (may be NULL)
cb033188d   Johan Hovold   greybus: connecti...
115
   * @flags:		connection flags
2566fae6a   Johan Hovold   greybus: connecti...
116
117
   *
   * Create a Greybus connection, representing the bidirectional link
c68adb2f2   Alex Elder   greybus: introduc...
118
   * between a CPort on a (local) Greybus host device and a CPort on
2566fae6a   Johan Hovold   greybus: connecti...
119
   * another Greybus interface.
c68adb2f2   Alex Elder   greybus: introduc...
120
   *
e88afa581   Alex Elder   greybus: introduc...
121
122
123
   * A connection also maintains the state of operations sent over the
   * connection.
   *
210b508e4   Johan Hovold   greybus: connecti...
124
125
126
   * Serialised against concurrent create and destroy using the
   * gb_connection_mutex.
   *
24e094d68   Johan Hovold   greybus: connecti...
127
128
   * Return: A pointer to the new connection if successful, or an ERR_PTR
   * otherwise.
c68adb2f2   Alex Elder   greybus: introduc...
129
   */
2566fae6a   Johan Hovold   greybus: connecti...
130
  static struct gb_connection *
96c2af5c6   Johan Hovold   greybus: connecti...
131
  _gb_connection_create(struct gb_host_device *hd, int hd_cport_id,
8478c35a8   Cristian Sicilia   staging: greybus:...
132
133
134
135
  		      struct gb_interface *intf,
  		      struct gb_bundle *bundle, int cport_id,
  		      gb_request_handler_t handler,
  		      unsigned long flags)
c68adb2f2   Alex Elder   greybus: introduc...
136
137
  {
  	struct gb_connection *connection;
24e094d68   Johan Hovold   greybus: connecti...
138
  	int ret;
c68adb2f2   Alex Elder   greybus: introduc...
139

210b508e4   Johan Hovold   greybus: connecti...
140
  	mutex_lock(&gb_connection_mutex);
54131222e   Viresh Kumar   greybus: connecti...
141
  	if (intf && gb_connection_cport_in_use(intf, cport_id)) {
b53e0c9e8   Johan Hovold   greybus: connecti...
142
143
  		dev_err(&intf->dev, "cport %u already in use
  ", cport_id);
24e094d68   Johan Hovold   greybus: connecti...
144
  		ret = -EBUSY;
b53e0c9e8   Johan Hovold   greybus: connecti...
145
146
  		goto err_unlock;
  	}
f2aae1c6e   Johan Hovold   greybus: hd: gene...
147
  	ret = gb_hd_cport_allocate(hd, hd_cport_id, flags);
74a5d93ce   Johan Hovold   greybus: hd: move...
148
149
150
  	if (ret < 0) {
  		dev_err(&hd->dev, "failed to allocate cport: %d
  ", ret);
210b508e4   Johan Hovold   greybus: connecti...
151
  		goto err_unlock;
74a5d93ce   Johan Hovold   greybus: hd: move...
152
  	}
24e094d68   Johan Hovold   greybus: connecti...
153
  	hd_cport_id = ret;
10f9fa133   Johan Hovold   greybus: connecti...
154

c68adb2f2   Alex Elder   greybus: introduc...
155
  	connection = kzalloc(sizeof(*connection), GFP_KERNEL);
24e094d68   Johan Hovold   greybus: connecti...
156
157
  	if (!connection) {
  		ret = -ENOMEM;
74a5d93ce   Johan Hovold   greybus: hd: move...
158
  		goto err_hd_cport_release;
24e094d68   Johan Hovold   greybus: connecti...
159
  	}
c68adb2f2   Alex Elder   greybus: introduc...
160

7c63a827d   Johan Hovold   greybus: connecti...
161
  	connection->hd_cport_id = hd_cport_id;
f9b0366f1   Alex Elder   greybus: connecti...
162
  	connection->intf_cport_id = cport_id;
f5c2be9e9   Alex Elder   greybus: connecti...
163
  	connection->hd = hd;
2566fae6a   Johan Hovold   greybus: connecti...
164
  	connection->intf = intf;
1db0a5ff3   Greg Kroah-Hartman   greybus: bundle: ...
165
  	connection->bundle = bundle;
f7ee081e3   Johan Hovold   greybus: connecti...
166
  	connection->handler = handler;
cb033188d   Johan Hovold   greybus: connecti...
167
  	connection->flags = flags;
0e9b41ab9   Johan Hovold   greybus: connecti...
168
169
  	if (intf && (intf->quirks & GB_INTERFACE_QUIRK_NO_CPORT_FEATURES))
  		connection->flags |= GB_CONNECTION_FLAG_NO_FLOWCTRL;
36561f23a   Alex Elder   greybus: define c...
170
  	connection->state = GB_CONNECTION_STATE_DISABLED;
ad1c449eb   Alex Elder   greybus: record c...
171

4e2b1e46a   Johan Hovold   greybus: connecti...
172
  	atomic_set(&connection->op_cycle, 0);
23268785b   Johan Hovold   greybus: connecti...
173
  	mutex_init(&connection->mutex);
4e2b1e46a   Johan Hovold   greybus: connecti...
174
175
  	spin_lock_init(&connection->lock);
  	INIT_LIST_HEAD(&connection->operations);
5a5bc354c   Johan Hovold   greybus: operatio...
176
  	connection->wq = alloc_workqueue("%s:%d", WQ_UNBOUND, 1,
582b3a139   Johan Hovold   greybus: connecti...
177
  					 dev_name(&hd->dev), hd_cport_id);
24e094d68   Johan Hovold   greybus: connecti...
178
179
  	if (!connection->wq) {
  		ret = -ENOMEM;
5a5bc354c   Johan Hovold   greybus: operatio...
180
  		goto err_free_connection;
24e094d68   Johan Hovold   greybus: connecti...
181
  	}
5a5bc354c   Johan Hovold   greybus: operatio...
182

b750fa337   Greg Kroah-Hartman   greybus: connecti...
183
  	kref_init(&connection->kref);
f0f61b904   Greg Kroah-Hartman   greybus: hook up ...
184

729b260a6   Johan Hovold   greybus: connecti...
185
  	gb_connection_init_name(connection);
0b1d26235   Johan Hovold   greybus: Revert "...
186
  	spin_lock_irq(&gb_connections_lock);
928f2abd5   Viresh Kumar   greybus: Tear dow...
187
  	list_add(&connection->hd_links, &hd->connections);
75662e5ca   Viresh Kumar   greybus: connecti...
188
189
190
191
192
  
  	if (bundle)
  		list_add(&connection->bundle_links, &bundle->connections);
  	else
  		INIT_LIST_HEAD(&connection->bundle_links);
0b1d26235   Johan Hovold   greybus: Revert "...
193
  	spin_unlock_irq(&gb_connections_lock);
748e1230c   Alex Elder   greybus: fix some...
194

210b508e4   Johan Hovold   greybus: connecti...
195
  	mutex_unlock(&gb_connection_mutex);
79c8c6494   Alex Elder   greybus: tracing:...
196
  	trace_gb_connection_create(connection);
c68adb2f2   Alex Elder   greybus: introduc...
197
  	return connection;
10f9fa133   Johan Hovold   greybus: connecti...
198

5a5bc354c   Johan Hovold   greybus: operatio...
199
200
  err_free_connection:
  	kfree(connection);
74a5d93ce   Johan Hovold   greybus: hd: move...
201
202
  err_hd_cport_release:
  	gb_hd_cport_release(hd, hd_cport_id);
210b508e4   Johan Hovold   greybus: connecti...
203
204
  err_unlock:
  	mutex_unlock(&gb_connection_mutex);
10f9fa133   Johan Hovold   greybus: connecti...
205

24e094d68   Johan Hovold   greybus: connecti...
206
  	return ERR_PTR(ret);
c68adb2f2   Alex Elder   greybus: introduc...
207
  }
2566fae6a   Johan Hovold   greybus: connecti...
208
  struct gb_connection *
f7ee081e3   Johan Hovold   greybus: connecti...
209
  gb_connection_create_static(struct gb_host_device *hd, u16 hd_cport_id,
8478c35a8   Cristian Sicilia   staging: greybus:...
210
  			    gb_request_handler_t handler)
2566fae6a   Johan Hovold   greybus: connecti...
211
  {
cb033188d   Johan Hovold   greybus: connecti...
212
  	return _gb_connection_create(hd, hd_cport_id, NULL, NULL, 0, handler,
8478c35a8   Cristian Sicilia   staging: greybus:...
213
  				     GB_CONNECTION_FLAG_HIGH_PRIO);
2566fae6a   Johan Hovold   greybus: connecti...
214
215
216
  }
  
  struct gb_connection *
59507e261   Johan Hovold   greybus: connecti...
217
218
  gb_connection_create_control(struct gb_interface *intf)
  {
aca7aab39   Johan Hovold   greybus: connecti...
219
  	return _gb_connection_create(intf->hd, -1, intf, NULL, 0, NULL,
8478c35a8   Cristian Sicilia   staging: greybus:...
220
221
  				     GB_CONNECTION_FLAG_CONTROL |
  				     GB_CONNECTION_FLAG_HIGH_PRIO);
59507e261   Johan Hovold   greybus: connecti...
222
223
224
  }
  
  struct gb_connection *
f7ee081e3   Johan Hovold   greybus: connecti...
225
  gb_connection_create(struct gb_bundle *bundle, u16 cport_id,
8478c35a8   Cristian Sicilia   staging: greybus:...
226
  		     gb_request_handler_t handler)
2566fae6a   Johan Hovold   greybus: connecti...
227
  {
8cff6c647   Johan Hovold   greybus: connecti...
228
  	struct gb_interface *intf = bundle->intf;
f7ee081e3   Johan Hovold   greybus: connecti...
229
  	return _gb_connection_create(intf->hd, -1, intf, bundle, cport_id,
8478c35a8   Cristian Sicilia   staging: greybus:...
230
  				     handler, 0);
2566fae6a   Johan Hovold   greybus: connecti...
231
  }
96c2af5c6   Johan Hovold   greybus: connecti...
232
  EXPORT_SYMBOL_GPL(gb_connection_create);
2566fae6a   Johan Hovold   greybus: connecti...
233

cb033188d   Johan Hovold   greybus: connecti...
234
235
  struct gb_connection *
  gb_connection_create_flags(struct gb_bundle *bundle, u16 cport_id,
8478c35a8   Cristian Sicilia   staging: greybus:...
236
237
  			   gb_request_handler_t handler,
  			   unsigned long flags)
cb033188d   Johan Hovold   greybus: connecti...
238
239
  {
  	struct gb_interface *intf = bundle->intf;
1ba30c330   Johan Hovold   greybus: connecti...
240
241
  	if (WARN_ON_ONCE(flags & GB_CONNECTION_FLAG_CORE_MASK))
  		flags &= ~GB_CONNECTION_FLAG_CORE_MASK;
cb033188d   Johan Hovold   greybus: connecti...
242
  	return _gb_connection_create(intf->hd, -1, intf, bundle, cport_id,
8478c35a8   Cristian Sicilia   staging: greybus:...
243
  				     handler, flags);
cb033188d   Johan Hovold   greybus: connecti...
244
245
  }
  EXPORT_SYMBOL_GPL(gb_connection_create_flags);
781ac8662   Johan Hovold   greybus: connecti...
246
247
  struct gb_connection *
  gb_connection_create_offloaded(struct gb_bundle *bundle, u16 cport_id,
8478c35a8   Cristian Sicilia   staging: greybus:...
248
  			       unsigned long flags)
781ac8662   Johan Hovold   greybus: connecti...
249
  {
781ac8662   Johan Hovold   greybus: connecti...
250
  	flags |= GB_CONNECTION_FLAG_OFFLOADED;
1ba30c330   Johan Hovold   greybus: connecti...
251
  	return gb_connection_create_flags(bundle, cport_id, NULL, flags);
781ac8662   Johan Hovold   greybus: connecti...
252
253
  }
  EXPORT_SYMBOL_GPL(gb_connection_create_offloaded);
d7ea30a57   Johan Hovold   greybus: hd: add ...
254
255
  static int gb_connection_hd_cport_enable(struct gb_connection *connection)
  {
2537636ab   Johan Hovold   greybus: hd: rena...
256
  	struct gb_host_device *hd = connection->hd;
d7ea30a57   Johan Hovold   greybus: hd: add ...
257
258
259
260
  	int ret;
  
  	if (!hd->driver->cport_enable)
  		return 0;
6910fa2dd   Johan Hovold   greybus: hd: add ...
261
  	ret = hd->driver->cport_enable(hd, connection->hd_cport_id,
8478c35a8   Cristian Sicilia   staging: greybus:...
262
  				       connection->flags);
d7ea30a57   Johan Hovold   greybus: hd: add ...
263
  	if (ret) {
62e046235   Viresh Kumar   greybus: connecti...
264
265
  		dev_err(&hd->dev, "%s: failed to enable host cport: %d
  ",
8478c35a8   Cristian Sicilia   staging: greybus:...
266
  			connection->name, ret);
d7ea30a57   Johan Hovold   greybus: hd: add ...
267
268
269
270
271
272
273
274
  		return ret;
  	}
  
  	return 0;
  }
  
  static void gb_connection_hd_cport_disable(struct gb_connection *connection)
  {
2537636ab   Johan Hovold   greybus: hd: rena...
275
  	struct gb_host_device *hd = connection->hd;
3cbe52c2e   Johan Hovold   greybus: connecti...
276
  	int ret;
d7ea30a57   Johan Hovold   greybus: hd: add ...
277
278
279
  
  	if (!hd->driver->cport_disable)
  		return;
3cbe52c2e   Johan Hovold   greybus: connecti...
280
281
  	ret = hd->driver->cport_disable(hd, connection->hd_cport_id);
  	if (ret) {
62e046235   Viresh Kumar   greybus: connecti...
282
283
  		dev_err(&hd->dev, "%s: failed to disable host cport: %d
  ",
8478c35a8   Cristian Sicilia   staging: greybus:...
284
  			connection->name, ret);
3cbe52c2e   Johan Hovold   greybus: connecti...
285
  	}
d7ea30a57   Johan Hovold   greybus: hd: add ...
286
  }
aac0839ea   Johan Hovold   greybus: connecti...
287
288
289
290
291
292
293
294
295
296
297
298
  static int gb_connection_hd_cport_connected(struct gb_connection *connection)
  {
  	struct gb_host_device *hd = connection->hd;
  	int ret;
  
  	if (!hd->driver->cport_connected)
  		return 0;
  
  	ret = hd->driver->cport_connected(hd, connection->hd_cport_id);
  	if (ret) {
  		dev_err(&hd->dev, "%s: failed to set connected state: %d
  ",
8478c35a8   Cristian Sicilia   staging: greybus:...
299
  			connection->name, ret);
aac0839ea   Johan Hovold   greybus: connecti...
300
301
302
303
304
  		return ret;
  	}
  
  	return 0;
  }
800d6c8f4   Johan Hovold   greybus: connecti...
305
306
307
308
309
310
311
312
313
314
315
316
  static int gb_connection_hd_cport_flush(struct gb_connection *connection)
  {
  	struct gb_host_device *hd = connection->hd;
  	int ret;
  
  	if (!hd->driver->cport_flush)
  		return 0;
  
  	ret = hd->driver->cport_flush(hd, connection->hd_cport_id);
  	if (ret) {
  		dev_err(&hd->dev, "%s: failed to flush host cport: %d
  ",
8478c35a8   Cristian Sicilia   staging: greybus:...
317
  			connection->name, ret);
800d6c8f4   Johan Hovold   greybus: connecti...
318
319
320
321
322
  		return ret;
  	}
  
  	return 0;
  }
aac0839ea   Johan Hovold   greybus: connecti...
323
  static int gb_connection_hd_cport_quiesce(struct gb_connection *connection)
9ed5e1ba3   Fabien Parent   greybus: connecti...
324
325
  {
  	struct gb_host_device *hd = connection->hd;
aac0839ea   Johan Hovold   greybus: connecti...
326
  	size_t peer_space;
9ed5e1ba3   Fabien Parent   greybus: connecti...
327
  	int ret;
f05a88a39   Jason Hrycay   staging: greybus:...
328
329
  	if (!hd->driver->cport_quiesce)
  		return 0;
aac0839ea   Johan Hovold   greybus: connecti...
330
331
  	peer_space = sizeof(struct gb_operation_msg_hdr) +
  			sizeof(struct gb_cport_shutdown_request);
9ed5e1ba3   Fabien Parent   greybus: connecti...
332

aac0839ea   Johan Hovold   greybus: connecti...
333
334
335
336
337
338
  	if (connection->mode_switch)
  		peer_space += sizeof(struct gb_operation_msg_hdr);
  
  	ret = hd->driver->cport_quiesce(hd, connection->hd_cport_id,
  					peer_space,
  					GB_CONNECTION_CPORT_QUIESCE_TIMEOUT);
9ed5e1ba3   Fabien Parent   greybus: connecti...
339
  	if (ret) {
aac0839ea   Johan Hovold   greybus: connecti...
340
341
  		dev_err(&hd->dev, "%s: failed to quiesce host cport: %d
  ",
8478c35a8   Cristian Sicilia   staging: greybus:...
342
  			connection->name, ret);
9ed5e1ba3   Fabien Parent   greybus: connecti...
343
344
345
346
347
  		return ret;
  	}
  
  	return 0;
  }
aac0839ea   Johan Hovold   greybus: connecti...
348
  static int gb_connection_hd_cport_clear(struct gb_connection *connection)
9ed5e1ba3   Fabien Parent   greybus: connecti...
349
350
  {
  	struct gb_host_device *hd = connection->hd;
aac0839ea   Johan Hovold   greybus: connecti...
351
  	int ret;
9ed5e1ba3   Fabien Parent   greybus: connecti...
352

f05a88a39   Jason Hrycay   staging: greybus:...
353
354
  	if (!hd->driver->cport_clear)
  		return 0;
aac0839ea   Johan Hovold   greybus: connecti...
355
356
357
358
  	ret = hd->driver->cport_clear(hd, connection->hd_cport_id);
  	if (ret) {
  		dev_err(&hd->dev, "%s: failed to clear host cport: %d
  ",
8478c35a8   Cristian Sicilia   staging: greybus:...
359
  			connection->name, ret);
aac0839ea   Johan Hovold   greybus: connecti...
360
361
  		return ret;
  	}
9ed5e1ba3   Fabien Parent   greybus: connecti...
362

aac0839ea   Johan Hovold   greybus: connecti...
363
  	return 0;
9ed5e1ba3   Fabien Parent   greybus: connecti...
364
  }
c68adb2f2   Alex Elder   greybus: introduc...
365
  /*
a95c258c6   Johan Hovold   greybus: connecti...
366
367
368
369
370
371
   * Request the SVC to create a connection from AP's cport to interface's
   * cport.
   */
  static int
  gb_connection_svc_connection_create(struct gb_connection *connection)
  {
2537636ab   Johan Hovold   greybus: hd: rena...
372
  	struct gb_host_device *hd = connection->hd;
1575ef18a   Viresh Kumar   greybus: svc: ski...
373
  	struct gb_interface *intf;
27f25c17a   Johan Hovold   greybus: connecti...
374
  	u8 cport_flags;
a95c258c6   Johan Hovold   greybus: connecti...
375
  	int ret;
4ec1574ae   Johan Hovold   greybus: connecti...
376
  	if (gb_connection_is_static(connection))
00ad6975e   Johan Hovold   greybus: connecti...
377
  		return 0;
a95c258c6   Johan Hovold   greybus: connecti...
378

35822c04a   Johan Hovold   greybus: connecti...
379
  	intf = connection->intf;
27f25c17a   Johan Hovold   greybus: connecti...
380

ec199ccdd   Johan Hovold   greybus: interfac...
381
  	/*
0e9b41ab9   Johan Hovold   greybus: connecti...
382
  	 * Enable either E2EFC or CSD, unless no flow control is requested.
ec199ccdd   Johan Hovold   greybus: interfac...
383
  	 */
27f25c17a   Johan Hovold   greybus: connecti...
384
  	cport_flags = GB_SVC_CPORT_FLAG_CSV_N;
0e9b41ab9   Johan Hovold   greybus: connecti...
385
  	if (gb_connection_flow_control_disabled(connection)) {
27f25c17a   Johan Hovold   greybus: connecti...
386
  		cport_flags |= GB_SVC_CPORT_FLAG_CSD_N;
64a6d1388   Johan Hovold   greybus: connecti...
387
  	} else if (gb_connection_e2efc_enabled(connection)) {
27f25c17a   Johan Hovold   greybus: connecti...
388
389
390
  		cport_flags |= GB_SVC_CPORT_FLAG_CSD_N |
  				GB_SVC_CPORT_FLAG_E2EFC;
  	}
a95c258c6   Johan Hovold   greybus: connecti...
391
  	ret = gb_svc_connection_create(hd->svc,
8478c35a8   Cristian Sicilia   staging: greybus:...
392
393
394
395
396
  				       hd->svc->ap_intf_id,
  				       connection->hd_cport_id,
  				       intf->interface_id,
  				       connection->intf_cport_id,
  				       cport_flags);
a95c258c6   Johan Hovold   greybus: connecti...
397
  	if (ret) {
4c4b50218   Johan Hovold   greybus: connecti...
398
399
400
401
  		dev_err(&connection->hd->dev,
  			"%s: failed to create svc connection: %d
  ",
  			connection->name, ret);
a95c258c6   Johan Hovold   greybus: connecti...
402
403
  		return ret;
  	}
a95c258c6   Johan Hovold   greybus: connecti...
404
405
  	return 0;
  }
1b7a9cd5a   Viresh Kumar   greybus: connecti...
406
407
408
  static void
  gb_connection_svc_connection_destroy(struct gb_connection *connection)
  {
4ec1574ae   Johan Hovold   greybus: connecti...
409
  	if (gb_connection_is_static(connection))
1b7a9cd5a   Viresh Kumar   greybus: connecti...
410
411
412
  		return;
  
  	gb_svc_connection_destroy(connection->hd->svc,
66069fb06   Johan Hovold   greybus: svc: mov...
413
  				  connection->hd->svc->ap_intf_id,
1b7a9cd5a   Viresh Kumar   greybus: connecti...
414
  				  connection->hd_cport_id,
35822c04a   Johan Hovold   greybus: connecti...
415
  				  connection->intf->interface_id,
1b7a9cd5a   Viresh Kumar   greybus: connecti...
416
417
  				  connection->intf_cport_id);
  }
9d7fc25b3   Johan Hovold   greybus: connecti...
418
419
420
  /* Inform Interface about active CPorts */
  static int gb_connection_control_connected(struct gb_connection *connection)
  {
9d7fc25b3   Johan Hovold   greybus: connecti...
421
422
423
  	struct gb_control *control;
  	u16 cport_id = connection->intf_cport_id;
  	int ret;
bc3be1705   Johan Hovold   greybus: connecti...
424
  	if (gb_connection_is_static(connection))
9d7fc25b3   Johan Hovold   greybus: connecti...
425
  		return 0;
aca7aab39   Johan Hovold   greybus: connecti...
426
  	if (gb_connection_is_control(connection))
bc3be1705   Johan Hovold   greybus: connecti...
427
  		return 0;
9d7fc25b3   Johan Hovold   greybus: connecti...
428

aca7aab39   Johan Hovold   greybus: connecti...
429
  	control = connection->intf->control;
9d7fc25b3   Johan Hovold   greybus: connecti...
430
431
  	ret = gb_control_connected_operation(control, cport_id);
  	if (ret) {
30482c1e6   Greg Kroah-Hartman   greybus: connecti...
432
433
434
  		dev_err(&connection->bundle->dev,
  			"failed to connect cport: %d
  ", ret);
9d7fc25b3   Johan Hovold   greybus: connecti...
435
436
437
438
439
  		return ret;
  	}
  
  	return 0;
  }
3de5acfaf   Johan Hovold   greybus: connecti...
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
  static void
  gb_connection_control_disconnecting(struct gb_connection *connection)
  {
  	struct gb_control *control;
  	u16 cport_id = connection->intf_cport_id;
  	int ret;
  
  	if (gb_connection_is_static(connection))
  		return;
  
  	control = connection->intf->control;
  
  	ret = gb_control_disconnecting_operation(control, cport_id);
  	if (ret) {
  		dev_err(&connection->hd->dev,
8478c35a8   Cristian Sicilia   staging: greybus:...
455
456
457
  			"%s: failed to send disconnecting: %d
  ",
  			connection->name, ret);
3de5acfaf   Johan Hovold   greybus: connecti...
458
459
  	}
  }
72d748226   Johan Hovold   greybus: connecti...
460
461
  static void
  gb_connection_control_disconnected(struct gb_connection *connection)
186906590   Viresh Kumar   greybus: connecti...
462
463
  {
  	struct gb_control *control;
72d748226   Johan Hovold   greybus: connecti...
464
  	u16 cport_id = connection->intf_cport_id;
186906590   Viresh Kumar   greybus: connecti...
465
  	int ret;
bc3be1705   Johan Hovold   greybus: connecti...
466
  	if (gb_connection_is_static(connection))
186906590   Viresh Kumar   greybus: connecti...
467
  		return;
55742d2a0   Johan Hovold   greybus: interfac...
468
469
470
471
472
473
474
475
476
477
478
479
480
  	control = connection->intf->control;
  
  	if (gb_connection_is_control(connection)) {
  		if (connection->mode_switch) {
  			ret = gb_control_mode_switch_operation(control);
  			if (ret) {
  				/*
  				 * Allow mode switch to time out waiting for
  				 * mailbox event.
  				 */
  				return;
  			}
  		}
bc3be1705   Johan Hovold   greybus: connecti...
481
  		return;
55742d2a0   Johan Hovold   greybus: interfac...
482
  	}
186906590   Viresh Kumar   greybus: connecti...
483
484
  
  	ret = gb_control_disconnected_operation(control, cport_id);
72d748226   Johan Hovold   greybus: connecti...
485
  	if (ret) {
30482c1e6   Greg Kroah-Hartman   greybus: connecti...
486
487
488
  		dev_warn(&connection->bundle->dev,
  			 "failed to disconnect cport: %d
  ", ret);
72d748226   Johan Hovold   greybus: connecti...
489
  	}
186906590   Viresh Kumar   greybus: connecti...
490
  }
aac0839ea   Johan Hovold   greybus: connecti...
491
  static int gb_connection_shutdown_operation(struct gb_connection *connection,
8478c35a8   Cristian Sicilia   staging: greybus:...
492
  					    u8 phase)
3de5acfaf   Johan Hovold   greybus: connecti...
493
  {
aac0839ea   Johan Hovold   greybus: connecti...
494
  	struct gb_cport_shutdown_request *req;
3de5acfaf   Johan Hovold   greybus: connecti...
495
496
497
498
  	struct gb_operation *operation;
  	int ret;
  
  	operation = gb_operation_create_core(connection,
8478c35a8   Cristian Sicilia   staging: greybus:...
499
500
501
  					     GB_REQUEST_TYPE_CPORT_SHUTDOWN,
  					     sizeof(*req), 0, 0,
  					     GFP_KERNEL);
3de5acfaf   Johan Hovold   greybus: connecti...
502
503
  	if (!operation)
  		return -ENOMEM;
aac0839ea   Johan Hovold   greybus: connecti...
504
505
  	req = operation->request->payload;
  	req->phase = phase;
3de5acfaf   Johan Hovold   greybus: connecti...
506
507
508
509
510
511
  	ret = gb_operation_request_send_sync(operation);
  
  	gb_operation_put(operation);
  
  	return ret;
  }
aac0839ea   Johan Hovold   greybus: connecti...
512
513
  static int gb_connection_cport_shutdown(struct gb_connection *connection,
  					u8 phase)
3de5acfaf   Johan Hovold   greybus: connecti...
514
515
  {
  	struct gb_host_device *hd = connection->hd;
aac0839ea   Johan Hovold   greybus: connecti...
516
  	const struct gb_hd_driver *drv = hd->driver;
3de5acfaf   Johan Hovold   greybus: connecti...
517
518
519
520
521
522
  	int ret;
  
  	if (gb_connection_is_static(connection))
  		return 0;
  
  	if (gb_connection_is_offloaded(connection)) {
aac0839ea   Johan Hovold   greybus: connecti...
523
  		if (!drv->cport_shutdown)
3de5acfaf   Johan Hovold   greybus: connecti...
524
  			return 0;
aac0839ea   Johan Hovold   greybus: connecti...
525
  		ret = drv->cport_shutdown(hd, connection->hd_cport_id, phase,
8478c35a8   Cristian Sicilia   staging: greybus:...
526
  					  GB_OPERATION_TIMEOUT_DEFAULT);
3de5acfaf   Johan Hovold   greybus: connecti...
527
  	} else {
aac0839ea   Johan Hovold   greybus: connecti...
528
  		ret = gb_connection_shutdown_operation(connection, phase);
3de5acfaf   Johan Hovold   greybus: connecti...
529
530
531
  	}
  
  	if (ret) {
aac0839ea   Johan Hovold   greybus: connecti...
532
533
  		dev_err(&hd->dev, "%s: failed to send cport shutdown (phase %d): %d
  ",
8478c35a8   Cristian Sicilia   staging: greybus:...
534
  			connection->name, phase, ret);
3de5acfaf   Johan Hovold   greybus: connecti...
535
536
537
538
539
  		return ret;
  	}
  
  	return 0;
  }
aac0839ea   Johan Hovold   greybus: connecti...
540
541
542
543
544
545
546
547
548
549
550
  static int
  gb_connection_cport_shutdown_phase_1(struct gb_connection *connection)
  {
  	return gb_connection_cport_shutdown(connection, 1);
  }
  
  static int
  gb_connection_cport_shutdown_phase_2(struct gb_connection *connection)
  {
  	return gb_connection_cport_shutdown(connection, 2);
  }
2846d3ed2   Johan Hovold   greybus: connecti...
551
  /*
520c6eae9   Johan Hovold   greybus: connecti...
552
553
   * Cancel all active operations on a connection.
   *
3de5acfaf   Johan Hovold   greybus: connecti...
554
555
   * Locking: Called with connection lock held and state set to DISABLED or
   * DISCONNECTING.
520c6eae9   Johan Hovold   greybus: connecti...
556
557
   */
  static void gb_connection_cancel_operations(struct gb_connection *connection,
8478c35a8   Cristian Sicilia   staging: greybus:...
558
  					    int errno)
127c1fbd5   Viresh Kumar   greybus: connecti...
559
  	__must_hold(&connection->lock)
520c6eae9   Johan Hovold   greybus: connecti...
560
561
562
563
564
  {
  	struct gb_operation *operation;
  
  	while (!list_empty(&connection->operations)) {
  		operation = list_last_entry(&connection->operations,
8478c35a8   Cristian Sicilia   staging: greybus:...
565
  					    struct gb_operation, links);
520c6eae9   Johan Hovold   greybus: connecti...
566
  		gb_operation_get(operation);
a29bac623   Johan Hovold   greybus: Revert "...
567
  		spin_unlock_irq(&connection->lock);
520c6eae9   Johan Hovold   greybus: connecti...
568
569
570
571
572
573
574
  
  		if (gb_operation_is_incoming(operation))
  			gb_operation_cancel_incoming(operation, errno);
  		else
  			gb_operation_cancel(operation, errno);
  
  		gb_operation_put(operation);
a29bac623   Johan Hovold   greybus: Revert "...
575
  		spin_lock_irq(&connection->lock);
520c6eae9   Johan Hovold   greybus: connecti...
576
577
  	}
  }
beb6b7fed   Johan Hovold   greybus: connecti...
578
579
580
581
582
583
584
  /*
   * Cancel all active incoming operations on a connection.
   *
   * Locking: Called with connection lock held and state set to ENABLED_TX.
   */
  static void
  gb_connection_flush_incoming_operations(struct gb_connection *connection,
8478c35a8   Cristian Sicilia   staging: greybus:...
585
  					int errno)
127c1fbd5   Viresh Kumar   greybus: connecti...
586
  	__must_hold(&connection->lock)
beb6b7fed   Johan Hovold   greybus: connecti...
587
588
589
590
591
592
593
  {
  	struct gb_operation *operation;
  	bool incoming;
  
  	while (!list_empty(&connection->operations)) {
  		incoming = false;
  		list_for_each_entry(operation, &connection->operations,
8478c35a8   Cristian Sicilia   staging: greybus:...
594
  				    links) {
beb6b7fed   Johan Hovold   greybus: connecti...
595
596
597
598
599
600
601
602
603
  			if (gb_operation_is_incoming(operation)) {
  				gb_operation_get(operation);
  				incoming = true;
  				break;
  			}
  		}
  
  		if (!incoming)
  			break;
a29bac623   Johan Hovold   greybus: Revert "...
604
  		spin_unlock_irq(&connection->lock);
beb6b7fed   Johan Hovold   greybus: connecti...
605
606
607
608
  
  		/* FIXME: flush, not cancel? */
  		gb_operation_cancel_incoming(operation, errno);
  		gb_operation_put(operation);
a29bac623   Johan Hovold   greybus: Revert "...
609
  		spin_lock_irq(&connection->lock);
beb6b7fed   Johan Hovold   greybus: connecti...
610
611
  	}
  }
f7ee081e3   Johan Hovold   greybus: connecti...
612
613
614
615
616
617
618
619
620
621
622
  /*
   * _gb_connection_enable() - enable a connection
   * @connection:		connection to enable
   * @rx:			whether to enable incoming requests
   *
   * Connection-enable helper for DISABLED->ENABLED, DISABLED->ENABLED_TX, and
   * ENABLED_TX->ENABLED state transitions.
   *
   * Locking: Caller holds connection->mutex.
   */
  static int _gb_connection_enable(struct gb_connection *connection, bool rx)
574341c67   Alex Elder   greybus: add devi...
623
  {
36561f23a   Alex Elder   greybus: define c...
624
  	int ret;
f7ee081e3   Johan Hovold   greybus: connecti...
625
  	/* Handle ENABLED_TX -> ENABLED transitions. */
570dfa7c5   Johan Hovold   greybus: connecti...
626
  	if (connection->state == GB_CONNECTION_STATE_ENABLED_TX) {
f7ee081e3   Johan Hovold   greybus: connecti...
627
628
  		if (!(connection->handler && rx))
  			return 0;
570dfa7c5   Johan Hovold   greybus: connecti...
629

a29bac623   Johan Hovold   greybus: Revert "...
630
  		spin_lock_irq(&connection->lock);
570dfa7c5   Johan Hovold   greybus: connecti...
631
  		connection->state = GB_CONNECTION_STATE_ENABLED;
a29bac623   Johan Hovold   greybus: Revert "...
632
  		spin_unlock_irq(&connection->lock);
570dfa7c5   Johan Hovold   greybus: connecti...
633

f7ee081e3   Johan Hovold   greybus: connecti...
634
  		return 0;
570dfa7c5   Johan Hovold   greybus: connecti...
635
  	}
d7ea30a57   Johan Hovold   greybus: hd: add ...
636
  	ret = gb_connection_hd_cport_enable(connection);
a95c258c6   Johan Hovold   greybus: connecti...
637
  	if (ret)
f7ee081e3   Johan Hovold   greybus: connecti...
638
  		return ret;
a1163fae6   Viresh Kumar   greybus: connecti...
639

d7ea30a57   Johan Hovold   greybus: hd: add ...
640
641
  	ret = gb_connection_svc_connection_create(connection);
  	if (ret)
aac0839ea   Johan Hovold   greybus: connecti...
642
  		goto err_hd_cport_clear;
d7ea30a57   Johan Hovold   greybus: hd: add ...
643

aac0839ea   Johan Hovold   greybus: connecti...
644
  	ret = gb_connection_hd_cport_connected(connection);
00ad6975e   Johan Hovold   greybus: connecti...
645
646
  	if (ret)
  		goto err_svc_connection_destroy;
a29bac623   Johan Hovold   greybus: Revert "...
647
  	spin_lock_irq(&connection->lock);
f7ee081e3   Johan Hovold   greybus: connecti...
648
  	if (connection->handler && rx)
570dfa7c5   Johan Hovold   greybus: connecti...
649
650
651
  		connection->state = GB_CONNECTION_STATE_ENABLED;
  	else
  		connection->state = GB_CONNECTION_STATE_ENABLED_TX;
a29bac623   Johan Hovold   greybus: Revert "...
652
  	spin_unlock_irq(&connection->lock);
cad09a8f8   Johan Hovold   greybus: connecti...
653

4d0bee112   Johan Hovold   greybus: connecti...
654
655
  	ret = gb_connection_control_connected(connection);
  	if (ret)
3de5acfaf   Johan Hovold   greybus: connecti...
656
  		goto err_control_disconnecting;
4d0bee112   Johan Hovold   greybus: connecti...
657

3ea6a8156   Johan Hovold   greybus: connecti...
658
  	return 0;
36561f23a   Alex Elder   greybus: define c...
659

3de5acfaf   Johan Hovold   greybus: connecti...
660
  err_control_disconnecting:
a29bac623   Johan Hovold   greybus: Revert "...
661
  	spin_lock_irq(&connection->lock);
3de5acfaf   Johan Hovold   greybus: connecti...
662
  	connection->state = GB_CONNECTION_STATE_DISCONNECTING;
a29bac623   Johan Hovold   greybus: Revert "...
663
664
  	gb_connection_cancel_operations(connection, -ESHUTDOWN);
  	spin_unlock_irq(&connection->lock);
8d7a712ca   Johan Hovold   greybus: connecti...
665

800d6c8f4   Johan Hovold   greybus: connecti...
666
667
  	/* Transmit queue should already be empty. */
  	gb_connection_hd_cport_flush(connection);
aac0839ea   Johan Hovold   greybus: connecti...
668
669
670
671
  	gb_connection_control_disconnecting(connection);
  	gb_connection_cport_shutdown_phase_1(connection);
  	gb_connection_hd_cport_quiesce(connection);
  	gb_connection_cport_shutdown_phase_2(connection);
3de5acfaf   Johan Hovold   greybus: connecti...
672
673
  	gb_connection_control_disconnected(connection);
  	connection->state = GB_CONNECTION_STATE_DISABLED;
00ad6975e   Johan Hovold   greybus: connecti...
674
  err_svc_connection_destroy:
3ea6a8156   Johan Hovold   greybus: connecti...
675
  	gb_connection_svc_connection_destroy(connection);
aac0839ea   Johan Hovold   greybus: connecti...
676
677
  err_hd_cport_clear:
  	gb_connection_hd_cport_clear(connection);
3ea6a8156   Johan Hovold   greybus: connecti...
678
  	gb_connection_hd_cport_disable(connection);
f7ee081e3   Johan Hovold   greybus: connecti...
679
680
681
682
683
684
685
686
687
688
689
690
691
692
  
  	return ret;
  }
  
  int gb_connection_enable(struct gb_connection *connection)
  {
  	int ret = 0;
  
  	mutex_lock(&connection->mutex);
  
  	if (connection->state == GB_CONNECTION_STATE_ENABLED)
  		goto out_unlock;
  
  	ret = _gb_connection_enable(connection, true);
79c8c6494   Alex Elder   greybus: tracing:...
693
694
  	if (!ret)
  		trace_gb_connection_enable(connection);
f7ee081e3   Johan Hovold   greybus: connecti...
695
  out_unlock:
23268785b   Johan Hovold   greybus: connecti...
696
  	mutex_unlock(&connection->mutex);
3ea6a8156   Johan Hovold   greybus: connecti...
697
698
699
700
  
  	return ret;
  }
  EXPORT_SYMBOL_GPL(gb_connection_enable);
186906590   Viresh Kumar   greybus: connecti...
701

f7ee081e3   Johan Hovold   greybus: connecti...
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
  int gb_connection_enable_tx(struct gb_connection *connection)
  {
  	int ret = 0;
  
  	mutex_lock(&connection->mutex);
  
  	if (connection->state == GB_CONNECTION_STATE_ENABLED) {
  		ret = -EINVAL;
  		goto out_unlock;
  	}
  
  	if (connection->state == GB_CONNECTION_STATE_ENABLED_TX)
  		goto out_unlock;
  
  	ret = _gb_connection_enable(connection, false);
79c8c6494   Alex Elder   greybus: tracing:...
717
718
  	if (!ret)
  		trace_gb_connection_enable(connection);
f7ee081e3   Johan Hovold   greybus: connecti...
719
720
721
722
723
724
  out_unlock:
  	mutex_unlock(&connection->mutex);
  
  	return ret;
  }
  EXPORT_SYMBOL_GPL(gb_connection_enable_tx);
beb6b7fed   Johan Hovold   greybus: connecti...
725
726
727
  void gb_connection_disable_rx(struct gb_connection *connection)
  {
  	mutex_lock(&connection->mutex);
a29bac623   Johan Hovold   greybus: Revert "...
728
  	spin_lock_irq(&connection->lock);
beb6b7fed   Johan Hovold   greybus: connecti...
729
  	if (connection->state != GB_CONNECTION_STATE_ENABLED) {
a29bac623   Johan Hovold   greybus: Revert "...
730
  		spin_unlock_irq(&connection->lock);
beb6b7fed   Johan Hovold   greybus: connecti...
731
732
733
  		goto out_unlock;
  	}
  	connection->state = GB_CONNECTION_STATE_ENABLED_TX;
a29bac623   Johan Hovold   greybus: Revert "...
734
735
  	gb_connection_flush_incoming_operations(connection, -ESHUTDOWN);
  	spin_unlock_irq(&connection->lock);
beb6b7fed   Johan Hovold   greybus: connecti...
736

79c8c6494   Alex Elder   greybus: tracing:...
737
  	trace_gb_connection_disable(connection);
beb6b7fed   Johan Hovold   greybus: connecti...
738
739
740
  out_unlock:
  	mutex_unlock(&connection->mutex);
  }
6d58e7144   Vaibhav Hiremath   greybus: connecti...
741
  EXPORT_SYMBOL_GPL(gb_connection_disable_rx);
beb6b7fed   Johan Hovold   greybus: connecti...
742

55742d2a0   Johan Hovold   greybus: interfac...
743
744
745
746
747
748
749
750
  void gb_connection_mode_switch_prepare(struct gb_connection *connection)
  {
  	connection->mode_switch = true;
  }
  
  void gb_connection_mode_switch_complete(struct gb_connection *connection)
  {
  	gb_connection_svc_connection_destroy(connection);
aac0839ea   Johan Hovold   greybus: connecti...
751
  	gb_connection_hd_cport_clear(connection);
55742d2a0   Johan Hovold   greybus: interfac...
752
  	gb_connection_hd_cport_disable(connection);
aac0839ea   Johan Hovold   greybus: connecti...
753

55742d2a0   Johan Hovold   greybus: interfac...
754
755
  	connection->mode_switch = false;
  }
3ea6a8156   Johan Hovold   greybus: connecti...
756
757
  void gb_connection_disable(struct gb_connection *connection)
  {
23268785b   Johan Hovold   greybus: connecti...
758
  	mutex_lock(&connection->mutex);
81fba2496   Johan Hovold   greybus: connecti...
759
  	if (connection->state == GB_CONNECTION_STATE_DISABLED)
23268785b   Johan Hovold   greybus: connecti...
760
  		goto out_unlock;
81fba2496   Johan Hovold   greybus: connecti...
761

0698be028   Viresh Kumar   greybus: connecti...
762
  	trace_gb_connection_disable(connection);
a29bac623   Johan Hovold   greybus: Revert "...
763
  	spin_lock_irq(&connection->lock);
3de5acfaf   Johan Hovold   greybus: connecti...
764
  	connection->state = GB_CONNECTION_STATE_DISCONNECTING;
a29bac623   Johan Hovold   greybus: Revert "...
765
766
  	gb_connection_cancel_operations(connection, -ESHUTDOWN);
  	spin_unlock_irq(&connection->lock);
81fba2496   Johan Hovold   greybus: connecti...
767

800d6c8f4   Johan Hovold   greybus: connecti...
768
  	gb_connection_hd_cport_flush(connection);
aac0839ea   Johan Hovold   greybus: connecti...
769
770
771
772
  	gb_connection_control_disconnecting(connection);
  	gb_connection_cport_shutdown_phase_1(connection);
  	gb_connection_hd_cport_quiesce(connection);
  	gb_connection_cport_shutdown_phase_2(connection);
3de5acfaf   Johan Hovold   greybus: connecti...
773
774
775
  	gb_connection_control_disconnected(connection);
  
  	connection->state = GB_CONNECTION_STATE_DISABLED;
55742d2a0   Johan Hovold   greybus: interfac...
776
777
778
  	/* control-connection tear down is deferred when mode switching */
  	if (!connection->mode_switch) {
  		gb_connection_svc_connection_destroy(connection);
aac0839ea   Johan Hovold   greybus: connecti...
779
  		gb_connection_hd_cport_clear(connection);
55742d2a0   Johan Hovold   greybus: interfac...
780
781
  		gb_connection_hd_cport_disable(connection);
  	}
23268785b   Johan Hovold   greybus: connecti...
782
783
784
  
  out_unlock:
  	mutex_unlock(&connection->mutex);
3ea6a8156   Johan Hovold   greybus: connecti...
785
786
  }
  EXPORT_SYMBOL_GPL(gb_connection_disable);
7aefe7918   Johan Hovold   greybus: core: av...
787
788
789
790
791
792
793
  /* Disable a connection without communicating with the remote end. */
  void gb_connection_disable_forced(struct gb_connection *connection)
  {
  	mutex_lock(&connection->mutex);
  
  	if (connection->state == GB_CONNECTION_STATE_DISABLED)
  		goto out_unlock;
0698be028   Viresh Kumar   greybus: connecti...
794
  	trace_gb_connection_disable(connection);
a29bac623   Johan Hovold   greybus: Revert "...
795
  	spin_lock_irq(&connection->lock);
7aefe7918   Johan Hovold   greybus: core: av...
796
  	connection->state = GB_CONNECTION_STATE_DISABLED;
a29bac623   Johan Hovold   greybus: Revert "...
797
798
  	gb_connection_cancel_operations(connection, -ESHUTDOWN);
  	spin_unlock_irq(&connection->lock);
7aefe7918   Johan Hovold   greybus: core: av...
799

800d6c8f4   Johan Hovold   greybus: connecti...
800
  	gb_connection_hd_cport_flush(connection);
aac0839ea   Johan Hovold   greybus: connecti...
801

7aefe7918   Johan Hovold   greybus: core: av...
802
  	gb_connection_svc_connection_destroy(connection);
aac0839ea   Johan Hovold   greybus: connecti...
803
  	gb_connection_hd_cport_clear(connection);
7aefe7918   Johan Hovold   greybus: core: av...
804

aac0839ea   Johan Hovold   greybus: connecti...
805
  	gb_connection_hd_cport_disable(connection);
7aefe7918   Johan Hovold   greybus: core: av...
806
807
808
809
  out_unlock:
  	mutex_unlock(&connection->mutex);
  }
  EXPORT_SYMBOL_GPL(gb_connection_disable_forced);
c3681f6c9   Johan Hovold   greybus: connecti...
810
  /* Caller must have disabled the connection before destroying it. */
fda2381bd   Viresh Kumar   greybus: connecti...
811
812
  void gb_connection_destroy(struct gb_connection *connection)
  {
2edbf5ffb   Johan Hovold   greybus: connecti...
813
  	if (!connection)
fda2381bd   Viresh Kumar   greybus: connecti...
814
  		return;
814ae531d   Alex Elder   greybus: connecti...
815
816
  	if (WARN_ON(connection->state != GB_CONNECTION_STATE_DISABLED))
  		gb_connection_disable(connection);
210b508e4   Johan Hovold   greybus: connecti...
817
  	mutex_lock(&gb_connection_mutex);
0b1d26235   Johan Hovold   greybus: Revert "...
818
  	spin_lock_irq(&gb_connections_lock);
fda2381bd   Viresh Kumar   greybus: connecti...
819
820
  	list_del(&connection->bundle_links);
  	list_del(&connection->hd_links);
0b1d26235   Johan Hovold   greybus: Revert "...
821
  	spin_unlock_irq(&gb_connections_lock);
fda2381bd   Viresh Kumar   greybus: connecti...
822

c3681f6c9   Johan Hovold   greybus: connecti...
823
  	destroy_workqueue(connection->wq);
74a5d93ce   Johan Hovold   greybus: hd: move...
824
  	gb_hd_cport_release(connection->hd, connection->hd_cport_id);
fda2381bd   Viresh Kumar   greybus: connecti...
825
  	connection->hd_cport_id = CPORT_ID_BAD;
210b508e4   Johan Hovold   greybus: connecti...
826
  	mutex_unlock(&gb_connection_mutex);
0e46fab7d   Johan Hovold   greybus: connecti...
827
  	gb_connection_put(connection);
fda2381bd   Viresh Kumar   greybus: connecti...
828
  }
98fdf5a03   Johan Hovold   greybus: core: de...
829
  EXPORT_SYMBOL_GPL(gb_connection_destroy);
fda2381bd   Viresh Kumar   greybus: connecti...
830

e7e2efc43   Bryan O'Donoghue   greybus: connecti...
831
832
  void gb_connection_latency_tag_enable(struct gb_connection *connection)
  {
2537636ab   Johan Hovold   greybus: hd: rena...
833
  	struct gb_host_device *hd = connection->hd;
e7e2efc43   Bryan O'Donoghue   greybus: connecti...
834
835
836
837
838
839
840
  	int ret;
  
  	if (!hd->driver->latency_tag_enable)
  		return;
  
  	ret = hd->driver->latency_tag_enable(hd, connection->hd_cport_id);
  	if (ret) {
4c4b50218   Johan Hovold   greybus: connecti...
841
842
843
844
  		dev_err(&connection->hd->dev,
  			"%s: failed to enable latency tag: %d
  ",
  			connection->name, ret);
e7e2efc43   Bryan O'Donoghue   greybus: connecti...
845
846
847
848
849
850
  	}
  }
  EXPORT_SYMBOL_GPL(gb_connection_latency_tag_enable);
  
  void gb_connection_latency_tag_disable(struct gb_connection *connection)
  {
2537636ab   Johan Hovold   greybus: hd: rena...
851
  	struct gb_host_device *hd = connection->hd;
e7e2efc43   Bryan O'Donoghue   greybus: connecti...
852
853
854
855
856
857
858
  	int ret;
  
  	if (!hd->driver->latency_tag_disable)
  		return;
  
  	ret = hd->driver->latency_tag_disable(hd, connection->hd_cport_id);
  	if (ret) {
4c4b50218   Johan Hovold   greybus: connecti...
859
860
861
862
  		dev_err(&connection->hd->dev,
  			"%s: failed to disable latency tag: %d
  ",
  			connection->name, ret);
e7e2efc43   Bryan O'Donoghue   greybus: connecti...
863
864
865
  	}
  }
  EXPORT_SYMBOL_GPL(gb_connection_latency_tag_disable);