diff options
author | Johan Hovold <johan@hovoldconsulting.com> | 2016-05-27 17:26:40 +0200 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@google.com> | 2016-05-27 12:24:17 -0700 |
commit | 55742d2a071a569bf20f90d37b1b5b8a25a3f882 (patch) | |
tree | 7438d9c12c2ec57706973a7eb9a80bc44b897edb /drivers/staging/greybus/connection.c | |
parent | greybus: control: add error message to mode-switch helper (diff) | |
download | linux-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.c | 36 |
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); |