greybus: interface: make sure type is invariant during reactivation

An interface is not expected to change its type after a power down and
reactivation so make sure to treat that as a fatal error.

This is complicated by the current Toshiba ES3 hack which requires us
to retry activation as Greybus interfaces are sometimes misdetected as
UniPro interfaces. Handle that by only retrying activation the first
time an interface is activated, and for interfaces already detected as
having Greybus type.

Signed-off-by: Johan Hovold <johan@hovoldconsulting.com>
Reviewed-by: Alex Elder <elder@linaro.org>
Reviewed-by: Viresh Kumar <viresh.kumar@linaro.org>
Reviewed-by: Sandeep Patil <sspatil@google.com>
Reviewed-by: Patrick Titiano <ptitiano@baylibre.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@google.com>
This commit is contained in:
Johan Hovold 2016-07-20 16:40:23 +02:00 committed by Greg Kroah-Hartman
parent 3e93cb6abb
commit 62491622db

View File

@ -822,7 +822,8 @@ static int gb_interface_unipro_set(struct gb_interface *intf, bool enable)
return 0;
}
static int gb_interface_activate_operation(struct gb_interface *intf)
static int gb_interface_activate_operation(struct gb_interface *intf,
enum gb_interface_type *intf_type)
{
struct gb_svc *svc = intf->hd->svc;
u8 type;
@ -838,20 +839,20 @@ static int gb_interface_activate_operation(struct gb_interface *intf)
switch (type) {
case GB_SVC_INTF_TYPE_DUMMY:
intf->type = GB_INTERFACE_TYPE_DUMMY;
*intf_type = GB_INTERFACE_TYPE_DUMMY;
/* FIXME: handle as an error for now */
return -ENODEV;
case GB_SVC_INTF_TYPE_UNIPRO:
intf->type = GB_INTERFACE_TYPE_UNIPRO;
*intf_type = GB_INTERFACE_TYPE_UNIPRO;
dev_err(&intf->dev, "interface type UniPro not supported\n");
/* FIXME: handle as an error for now */
return -ENODEV;
case GB_SVC_INTF_TYPE_GREYBUS:
intf->type = GB_INTERFACE_TYPE_GREYBUS;
*intf_type = GB_INTERFACE_TYPE_GREYBUS;
break;
default:
dev_err(&intf->dev, "unknown interface type: %u\n", type);
intf->type = GB_INTERFACE_TYPE_UNKNOWN;
*intf_type = GB_INTERFACE_TYPE_UNKNOWN;
return -ENODEV;
}
@ -865,10 +866,13 @@ static int gb_interface_hibernate_link(struct gb_interface *intf)
return gb_svc_intf_set_power_mode_hibernate(svc, intf->interface_id);
}
static int _gb_interface_activate(struct gb_interface *intf)
static int _gb_interface_activate(struct gb_interface *intf,
enum gb_interface_type *type)
{
int ret;
*type = GB_INTERFACE_TYPE_UNKNOWN;
if (intf->ejected)
return -ENODEV;
@ -884,7 +888,7 @@ static int _gb_interface_activate(struct gb_interface *intf)
if (ret)
goto err_refclk_disable;
ret = gb_interface_activate_operation(intf);
ret = gb_interface_activate_operation(intf, type);
if (ret)
goto err_unipro_disable;
@ -914,6 +918,30 @@ static int _gb_interface_activate(struct gb_interface *intf)
return ret;
}
/*
* At present, we assume a UniPro-only module to be a Greybus module that
* failed to send its mailbox poke. There is some reason to believe that this
* is because of a bug in the ES3 bootrom.
*
* FIXME: Check if this is a Toshiba bridge before retrying?
*/
static int _gb_interface_activate_es3_hack(struct gb_interface *intf,
enum gb_interface_type *type)
{
int retries = 3;
int ret;
while (retries--) {
ret = _gb_interface_activate(intf, type);
if (ret == -ENODEV && *type == GB_INTERFACE_TYPE_UNIPRO)
continue;
break;
}
return ret;
}
/*
* Activate an interface.
*
@ -921,23 +949,30 @@ static int _gb_interface_activate(struct gb_interface *intf)
*/
int gb_interface_activate(struct gb_interface *intf)
{
int retries = 3;
enum gb_interface_type type;
int ret;
/*
* At present, we assume a UniPro-only module
* to be a Greybus module that failed to send its mailbox
* poke. There is some reason to believe that this is
* because of a bug in the ES3 bootrom.
*
* FIXME: Check if this is a Toshiba bridge before retrying?
*/
while (retries--) {
ret = _gb_interface_activate(intf);
if (ret == -ENODEV && intf->type == GB_SVC_INTF_TYPE_UNIPRO)
continue;
switch (intf->type) {
case GB_INTERFACE_TYPE_INVALID:
case GB_INTERFACE_TYPE_GREYBUS:
ret = _gb_interface_activate_es3_hack(intf, &type);
break;
default:
ret = _gb_interface_activate(intf, &type);
}
/* Make sure type is detected correctly during reactivation. */
if (intf->type != GB_INTERFACE_TYPE_INVALID) {
if (type != intf->type) {
dev_err(&intf->dev, "failed to detect interface type\n");
if (!ret)
gb_interface_deactivate(intf);
return -EIO;
}
} else {
intf->type = type;
}
return ret;