Commit 2618ab77 authored by Inaky Perez-Gonzalez's avatar Inaky Perez-Gonzalez

wimax/i2400m: usb: fix device reset on autosuspend while not yet idle

When the i2400m is connected to a network, the host interface (USB)
cannot be suspended. For that to happen, the device has to have
negotiated with the basestation to put the link on IDLE state.

If the host tries to put the device in standby while it is connected
but not idle, the device resets, as the driver should not do that.

To avoid triggering that, when the USB susbsytem requires the driver
to autosuspend the device, the driver checks if the device is not yet
idle. If it is not, the request is requested (will be retried again
later on after the autosuspend timeout). At some point the device will
enter idle and the request will succeed (unless of course, there is
network traffic, but at that point, there is no idle neither in the
link or the host interface).
Signed-off-by: default avatarInaky Perez-Gonzalez <inaky@linux.intel.com>
parent 7f0333eb
...@@ -505,27 +505,52 @@ int i2400mu_suspend(struct usb_interface *iface, pm_message_t pm_msg) ...@@ -505,27 +505,52 @@ int i2400mu_suspend(struct usb_interface *iface, pm_message_t pm_msg)
#ifdef CONFIG_PM #ifdef CONFIG_PM
struct usb_device *usb_dev = i2400mu->usb_dev; struct usb_device *usb_dev = i2400mu->usb_dev;
#endif #endif
unsigned is_autosuspend = 0;
struct i2400m *i2400m = &i2400mu->i2400m; struct i2400m *i2400m = &i2400mu->i2400m;
#ifdef CONFIG_PM
if (usb_dev->auto_pm > 0)
is_autosuspend = 1;
#endif
d_fnstart(3, dev, "(iface %p pm_msg %u)\n", iface, pm_msg.event); d_fnstart(3, dev, "(iface %p pm_msg %u)\n", iface, pm_msg.event);
if (i2400m->updown == 0) if (i2400m->updown == 0)
goto no_firmware; goto no_firmware;
d_printf(1, dev, "fw up, requesting standby\n"); if (i2400m->state == I2400M_SS_DATA_PATH_CONNECTED && is_autosuspend) {
/* ugh -- the device is connected and this suspend
* request is an autosuspend one (not a system standby
* / hibernate).
*
* The only way the device can go to standby is if the
* link with the base station is in IDLE mode; that
* were the case, we'd be in status
* I2400M_SS_CONNECTED_IDLE. But we are not.
*
* If we *tell* him to go power save now, it'll reset
* as a precautionary measure, so if this is an
* autosuspend thing, say no and it'll come back
* later, when the link is IDLE
*/
result = -EBADF;
d_printf(1, dev, "fw up, link up, not-idle, autosuspend: "
"not entering powersave\n");
goto error_not_now;
}
d_printf(1, dev, "fw up: entering powersave\n");
atomic_dec(&i2400mu->do_autopm); atomic_dec(&i2400mu->do_autopm);
result = i2400m_cmd_enter_powersave(i2400m); result = i2400m_cmd_enter_powersave(i2400m);
atomic_inc(&i2400mu->do_autopm); atomic_inc(&i2400mu->do_autopm);
#ifdef CONFIG_PM if (result < 0 && !is_autosuspend) {
if (result < 0 && usb_dev->auto_pm == 0) {
/* System suspend, can't fail */ /* System suspend, can't fail */
dev_err(dev, "failed to suspend, will reset on resume\n"); dev_err(dev, "failed to suspend, will reset on resume\n");
result = 0; result = 0;
} }
#endif
if (result < 0) if (result < 0)
goto error_enter_powersave; goto error_enter_powersave;
i2400mu_notification_release(i2400mu); i2400mu_notification_release(i2400mu);
d_printf(1, dev, "fw up, got standby\n"); d_printf(1, dev, "powersave requested\n");
error_enter_powersave: error_enter_powersave:
error_not_now:
no_firmware: no_firmware:
d_fnend(3, dev, "(iface %p pm_msg %u) = %d\n", d_fnend(3, dev, "(iface %p pm_msg %u) = %d\n",
iface, pm_msg.event, result); iface, pm_msg.event, result);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment