[PATCH 3.12 61/78] net: qmi_wwan: MDM9x30 specific power management

From: Jiri Slaby
Date: Fri Apr 22 2016 - 07:09:30 EST


From: BjÃrn Mork <bjorn@xxxxxxx>

3.12-stable review patch. If anyone has any objections, please let me know.

===============

commit 93725149794d3d418cf1eddcae60c7b536c5faa1 upstream.

MDM9x30 based modems appear to go into a deeper sleep when
suspended without "Remote Wakeup" enabled. The QMI interface
will not respond unless a "set DTR" control request is sent
on resume. The effect is similar to a QMI_CTL SYNC request,
resetting (some of) the firmware state.

We allow userspace sessions to span multiple character device
open/close sequences. This means that userspace can depend
on firmware state while both the netdev and the character
device are closed. We have disabled "needs_remote_wakeup" at
this point to allow devices without remote wakeup support to
be auto-suspended.

To make sure the MDM9x30 keeps firmware state, we need to
keep "needs_remote_wakeup" always set. We also need to
issue a "set DTR" request to enable the QMI interface.

Signed-off-by: BjÃrn Mork <bjorn@xxxxxxx>
Signed-off-by: David S. Miller <davem@xxxxxxxxxxxxx>
Signed-off-by: Jiri Slaby <jslaby@xxxxxxx>
---
drivers/net/usb/qmi_wwan.c | 38 ++++++++++++++++++++++++++++++++++++++
1 file changed, 38 insertions(+)

diff --git a/drivers/net/usb/qmi_wwan.c b/drivers/net/usb/qmi_wwan.c
index 18101e3fcd04..415bbe0365c6 100644
--- a/drivers/net/usb/qmi_wwan.c
+++ b/drivers/net/usb/qmi_wwan.c
@@ -216,6 +216,20 @@ err:
return rv;
}

+/* Send CDC SetControlLineState request, setting or clearing the DTR.
+ * "Required for Autoconnect and 9x30 to wake up" according to the
+ * GobiNet driver. The requirement has been verified on an MDM9230
+ * based Sierra Wireless MC7455
+ */
+static int qmi_wwan_change_dtr(struct usbnet *dev, bool on)
+{
+ u8 intf = dev->intf->cur_altsetting->desc.bInterfaceNumber;
+
+ return usbnet_write_cmd(dev, USB_CDC_REQ_SET_CONTROL_LINE_STATE,
+ USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE,
+ on ? 0x01 : 0x00, intf, NULL, 0);
+}
+
static int qmi_wwan_bind(struct usbnet *dev, struct usb_interface *intf)
{
int status = -1;
@@ -320,6 +334,24 @@ next_desc:
usb_driver_release_interface(driver, info->data);
}

+ /* disabling remote wakeup on MDM9x30 devices has the same
+ * effect as clearing DTR. The device will not respond to QMI
+ * requests until we set DTR again. This is similar to a
+ * QMI_CTL SYNC request, clearing a lot of firmware state
+ * including the client ID allocations.
+ *
+ * Our usage model allows a session to span multiple
+ * open/close events, so we must prevent the firmware from
+ * clearing out state the clients might need.
+ *
+ * MDM9x30 is the first QMI chipset with USB3 support. Abuse
+ * this fact to enable the quirk.
+ */
+ if (le16_to_cpu(dev->udev->descriptor.bcdUSB) >= 0x0201) {
+ qmi_wwan_manage_power(dev, 1);
+ qmi_wwan_change_dtr(dev, true);
+ }
+
/* Never use the same address on both ends of the link, even
* if the buggy firmware told us to.
*/
@@ -345,6 +377,12 @@ static void qmi_wwan_unbind(struct usbnet *dev, struct usb_interface *intf)
if (info->subdriver && info->subdriver->disconnect)
info->subdriver->disconnect(info->control);

+ /* disable MDM9x30 quirk */
+ if (le16_to_cpu(dev->udev->descriptor.bcdUSB) >= 0x0201) {
+ qmi_wwan_change_dtr(dev, false);
+ qmi_wwan_manage_power(dev, 0);
+ }
+
/* allow user to unbind using either control or data */
if (intf == info->control)
other = info->data;
--
2.8.1