Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 93725149 authored by Bjørn Mork's avatar Bjørn Mork Committed by David S. Miller
Browse files

net: qmi_wwan: MDM9x30 specific power management



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: default avatarBjørn Mork <bjorn@mork.no>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 43dd7a8b
Loading
Loading
Loading
Loading
+38 −0
Original line number Original line Diff line number Diff line
@@ -223,6 +223,20 @@ static int qmi_wwan_register_subdriver(struct usbnet *dev)
	return rv;
	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)
static int qmi_wwan_bind(struct usbnet *dev, struct usb_interface *intf)
{
{
	int status = -1;
	int status = -1;
@@ -280,6 +294,24 @@ static int qmi_wwan_bind(struct usbnet *dev, struct usb_interface *intf)
		usb_driver_release_interface(driver, info->data);
		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
	/* Never use the same address on both ends of the link, even if the
	 * buggy firmware told us to. Or, if device is assigned the well-known
	 * buggy firmware told us to. Or, if device is assigned the well-known
	 * buggy firmware MAC address, replace it with a random address,
	 * buggy firmware MAC address, replace it with a random address,
@@ -307,6 +339,12 @@ static void qmi_wwan_unbind(struct usbnet *dev, struct usb_interface *intf)
	if (info->subdriver && info->subdriver->disconnect)
	if (info->subdriver && info->subdriver->disconnect)
		info->subdriver->disconnect(info->control);
		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 */
	/* allow user to unbind using either control or data */
	if (intf == info->control)
	if (intf == info->control)
		other = info->data;
		other = info->data;