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

Commit 65580b43 authored by Andiry Xu's avatar Andiry Xu Committed by Greg Kroah-Hartman
Browse files

xHCI: set USB2 hardware LPM



If the device pass the USB2 software LPM and the host supports hardware
LPM, enable hardware LPM for the device to let the host decide when to
put the link into lower power state.

If hardware LPM is enabled for a port and driver wants to put it into
suspend, it must first disable hardware LPM, resume the port into U0,
and then suspend the port.

Signed-off-by: default avatarAndiry Xu <andiry.xu@amd.com>
Signed-off-by: default avatarSarah Sharp <sarah.a.sharp@linux.intel.com>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@suse.de>
parent 9574323c
Loading
Loading
Loading
Loading
+14 −0
Original line number Diff line number Diff line
@@ -1700,6 +1700,20 @@ int usb_runtime_idle(struct device *dev)
	return 0;
}

int usb_set_usb2_hardware_lpm(struct usb_device *udev, int enable)
{
	struct usb_hcd *hcd = bus_to_hcd(udev->bus);
	int ret = -EPERM;

	if (hcd->driver->set_usb2_hw_lpm) {
		ret = hcd->driver->set_usb2_hw_lpm(hcd, udev, enable);
		if (!ret)
			udev->usb2_hw_lpm_enabled = enable;
	}

	return ret;
}

#endif /* CONFIG_USB_SUSPEND */

struct bus_type usb_bus_type = {
+9 −0
Original line number Diff line number Diff line
@@ -2392,6 +2392,10 @@ int usb_port_suspend(struct usb_device *udev, pm_message_t msg)
		}
	}

	/* disable USB2 hardware LPM */
	if (udev->usb2_hw_lpm_enabled == 1)
		usb_set_usb2_hardware_lpm(udev, 0);

	/* see 7.1.7.6 */
	if (hub_is_superspeed(hub->hdev))
		status = set_port_feature(hub->hdev,
@@ -2603,7 +2607,12 @@ int usb_port_resume(struct usb_device *udev, pm_message_t msg)
	if (status < 0) {
		dev_dbg(&udev->dev, "can't resume, status %d\n", status);
		hub_port_logical_disconnect(hub, port1);
	} else  {
		/* Try to enable USB2 hardware LPM */
		if (udev->usb2_hw_lpm_capable == 1)
			usb_set_usb2_hardware_lpm(udev, 1);
	}

	return status;
}

+5 −0
Original line number Diff line number Diff line
@@ -82,6 +82,7 @@ extern int usb_remote_wakeup(struct usb_device *dev);
extern int usb_runtime_suspend(struct device *dev);
extern int usb_runtime_resume(struct device *dev);
extern int usb_runtime_idle(struct device *dev);
extern int usb_set_usb2_hardware_lpm(struct usb_device *udev, int enable);

#else

@@ -96,6 +97,10 @@ static inline int usb_remote_wakeup(struct usb_device *udev)
	return 0;
}

static inline int usb_set_usb2_hardware_lpm(struct usb_device *udev, int enable)
{
	return 0;
}
#endif

extern struct bus_type usb_bus_type;
+9 −0
Original line number Diff line number Diff line
@@ -574,10 +574,19 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
		switch (wValue) {
		case USB_PORT_FEAT_SUSPEND:
			temp = xhci_readl(xhci, port_array[wIndex]);
			if ((temp & PORT_PLS_MASK) != XDEV_U0) {
				/* Resume the port to U0 first */
				xhci_set_link_state(xhci, port_array, wIndex,
							XDEV_U0);
				spin_unlock_irqrestore(&xhci->lock, flags);
				msleep(10);
				spin_lock_irqsave(&xhci->lock, flags);
			}
			/* In spec software should not attempt to suspend
			 * a port unless the port reports that it is in the
			 * enabled (PED = ‘1’,PLS < ‘3’) state.
			 */
			temp = xhci_readl(xhci, port_array[wIndex]);
			if ((temp & PORT_PE) == 0 || (temp & PORT_RESET)
				|| (temp & PORT_PLS_MASK) >= XDEV_U3) {
				xhci_warn(xhci, "USB core suspending device "
+1 −0
Original line number Diff line number Diff line
@@ -349,6 +349,7 @@ static const struct hc_driver xhci_pci_hc_driver = {
	 * call back when device connected and addressed
	 */
	.update_device =        xhci_update_device,
	.set_usb2_hw_lpm =	xhci_set_usb2_hardware_lpm,
};

/*-------------------------------------------------------------------------*/
Loading