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 rejected (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: Inaky Perez-Gonzalez <inaky@linux.intel.com>
This commit is contained in:
parent
4f72427998
commit
e069c0cf7c
1 changed files with 30 additions and 5 deletions
|
@ -505,27 +505,52 @@ int i2400mu_suspend(struct usb_interface *iface, pm_message_t pm_msg)
|
|||
#ifdef CONFIG_PM
|
||||
struct usb_device *usb_dev = i2400mu->usb_dev;
|
||||
#endif
|
||||
unsigned is_autosuspend = 0;
|
||||
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);
|
||||
if (i2400m->updown == 0)
|
||||
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);
|
||||
result = i2400m_cmd_enter_powersave(i2400m);
|
||||
atomic_inc(&i2400mu->do_autopm);
|
||||
#ifdef CONFIG_PM
|
||||
if (result < 0 && usb_dev->auto_pm == 0) {
|
||||
if (result < 0 && !is_autosuspend) {
|
||||
/* System suspend, can't fail */
|
||||
dev_err(dev, "failed to suspend, will reset on resume\n");
|
||||
result = 0;
|
||||
}
|
||||
#endif
|
||||
if (result < 0)
|
||||
goto error_enter_powersave;
|
||||
i2400mu_notification_release(i2400mu);
|
||||
d_printf(1, dev, "fw up, got standby\n");
|
||||
d_printf(1, dev, "powersave requested\n");
|
||||
error_enter_powersave:
|
||||
error_not_now:
|
||||
no_firmware:
|
||||
d_fnend(3, dev, "(iface %p pm_msg %u) = %d\n",
|
||||
iface, pm_msg.event, result);
|
||||
|
|
Reference in a new issue