aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/staging/greybus/connection.c
diff options
context:
space:
mode:
authorJohan Hovold <johan@hovoldconsulting.com>2016-05-27 17:26:40 +0200
committerGreg Kroah-Hartman <gregkh@google.com>2016-05-27 12:24:17 -0700
commit55742d2a071a569bf20f90d37b1b5b8a25a3f882 (patch)
tree7438d9c12c2ec57706973a7eb9a80bc44b897edb /drivers/staging/greybus/connection.c
parentgreybus: control: add error message to mode-switch helper (diff)
downloadlinux-dev-55742d2a071a569bf20f90d37b1b5b8a25a3f882.tar.xz
linux-dev-55742d2a071a569bf20f90d37b1b5b8a25a3f882.zip
greybus: interface: implement generic mode-switch functionality
Add a generic interface for bundle drivers to use to request that a mode switch is carried out on its behalf. Mode switching involves tearing down all connections to an interface, sending a unidirectional mode-switch request, and waiting for a mailbox event that triggers deferred control connection reset and re-enumeration of the interface. In case of a timeout waiting for the interface mailbox event, or on other errors, the interface is powered off. All of this needs to be done by core from work-queue context in order not to block incoming SVC requests and bundle-device tear down. Care must also be taken to serialise against concurrent module removal events and eject requests. Special handling of legacy mode-switching is also added in order to continue to support the ES3 bootrom. Signed-off-by: Johan Hovold <johan@hovoldconsulting.com> Signed-off-by: Greg Kroah-Hartman <gregkh@google.com>
Diffstat (limited to 'drivers/staging/greybus/connection.c')
-rw-r--r--drivers/staging/greybus/connection.c36
1 files changed, 32 insertions, 4 deletions
diff --git a/drivers/staging/greybus/connection.c b/drivers/staging/greybus/connection.c
index c1cdfcd830a4..3be767b9a0de 100644
--- a/drivers/staging/greybus/connection.c
+++ b/drivers/staging/greybus/connection.c
@@ -487,10 +487,23 @@ gb_connection_control_disconnected(struct gb_connection *connection)
if (gb_connection_is_static(connection))
return;
- if (gb_connection_is_control(connection))
+ 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;
+ }
+ }
+
return;
+ }
- control = connection->intf->control;
ret = gb_control_disconnected_operation(control, cport_id);
if (ret) {
@@ -743,6 +756,18 @@ out_unlock:
}
EXPORT_SYMBOL_GPL(gb_connection_disable_rx);
+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);
+ gb_connection_hd_cport_disable(connection);
+ connection->mode_switch = false;
+}
+
void gb_connection_disable(struct gb_connection *connection)
{
mutex_lock(&connection->mutex);
@@ -768,8 +793,11 @@ void gb_connection_disable(struct gb_connection *connection)
connection->state = GB_CONNECTION_STATE_DISABLED;
- gb_connection_svc_connection_destroy(connection);
- gb_connection_hd_cport_disable(connection);
+ /* control-connection tear down is deferred when mode switching */
+ if (!connection->mode_switch) {
+ gb_connection_svc_connection_destroy(connection);
+ gb_connection_hd_cport_disable(connection);
+ }
out_unlock:
mutex_unlock(&connection->mutex);