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

Commit 9262c19d authored by Dan Williams's avatar Dan Williams Committed by Greg Kroah-Hartman
Browse files

usb: disable port power control if not supported in wHubCharacteristics



A hub indicates whether it supports per-port power control via the
wHubCharacteristics field in its descriptor.  If it is not supported
a hub will still emulate ClearPortPower(PORT_POWER) requests by
stopping the link state machine.  However, since this does not save
power do not bother suspending.

This also consolidates support checks into a
hub_is_port_power_switchable() helper.

Acked-by: default avatarAlan Stern <stern@rowland.harvard.edu>
Signed-off-by: default avatarDan Williams <dan.j.williams@intel.com>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
parent 600856c2
Loading
Loading
Loading
Loading
+2 −6
Original line number Original line Diff line number Diff line
@@ -818,8 +818,6 @@ static unsigned hub_power_on(struct usb_hub *hub, bool do_delay)
	int port1;
	int port1;
	unsigned pgood_delay = hub->descriptor->bPwrOn2PwrGood * 2;
	unsigned pgood_delay = hub->descriptor->bPwrOn2PwrGood * 2;
	unsigned delay;
	unsigned delay;
	u16 wHubCharacteristics =
			le16_to_cpu(hub->descriptor->wHubCharacteristics);


	/* Enable power on each port.  Some hubs have reserved values
	/* Enable power on each port.  Some hubs have reserved values
	 * of LPSM (> 2) in their descriptors, even though they are
	 * of LPSM (> 2) in their descriptors, even though they are
@@ -827,7 +825,7 @@ static unsigned hub_power_on(struct usb_hub *hub, bool do_delay)
	 * but only emulate it.  In all cases, the ports won't work
	 * but only emulate it.  In all cases, the ports won't work
	 * unless we send these messages to the hub.
	 * unless we send these messages to the hub.
	 */
	 */
	if ((wHubCharacteristics & HUB_CHAR_LPSM) < 2)
	if (hub_is_port_power_switchable(hub))
		dev_dbg(hub->intfdev, "enabling power on all ports\n");
		dev_dbg(hub->intfdev, "enabling power on all ports\n");
	else
	else
		dev_dbg(hub->intfdev, "trying to enable port power on "
		dev_dbg(hub->intfdev, "trying to enable port power on "
@@ -4417,8 +4415,6 @@ static void hub_port_connect_change(struct usb_hub *hub, int port1,
	struct usb_device *hdev = hub->hdev;
	struct usb_device *hdev = hub->hdev;
	struct device *hub_dev = hub->intfdev;
	struct device *hub_dev = hub->intfdev;
	struct usb_hcd *hcd = bus_to_hcd(hdev->bus);
	struct usb_hcd *hcd = bus_to_hcd(hdev->bus);
	unsigned wHubCharacteristics =
			le16_to_cpu(hub->descriptor->wHubCharacteristics);
	struct usb_device *udev;
	struct usb_device *udev;
	int status, i;
	int status, i;
	unsigned unit_load;
	unsigned unit_load;
@@ -4503,7 +4499,7 @@ static void hub_port_connect_change(struct usb_hub *hub, int port1,
			test_bit(port1, hub->removed_bits)) {
			test_bit(port1, hub->removed_bits)) {


		/* maybe switch power back on (e.g. root hub was reset) */
		/* maybe switch power back on (e.g. root hub was reset) */
		if ((wHubCharacteristics & HUB_CHAR_LPSM) < 2
		if (hub_is_port_power_switchable(hub)
				&& !port_is_power_on(hub, portstatus))
				&& !port_is_power_on(hub, portstatus))
			set_port_feature(hdev, port1, USB_PORT_FEAT_POWER);
			set_port_feature(hdev, port1, USB_PORT_FEAT_POWER);


+10 −0
Original line number Original line Diff line number Diff line
@@ -112,6 +112,16 @@ extern int hub_port_debounce(struct usb_hub *hub, int port1,
extern int usb_clear_port_feature(struct usb_device *hdev,
extern int usb_clear_port_feature(struct usb_device *hdev,
		int port1, int feature);
		int port1, int feature);


static inline bool hub_is_port_power_switchable(struct usb_hub *hub)
{
	__le16 hcs;

	if (!hub)
		return false;
	hcs = hub->descriptor->wHubCharacteristics;
	return (le16_to_cpu(hcs) & HUB_CHAR_LPSM) < HUB_CHAR_NO_LPSM;
}

static inline int hub_port_debounce_be_connected(struct usb_hub *hub,
static inline int hub_port_debounce_be_connected(struct usb_hub *hub,
		int port1)
		int port1)
{
{
+8 −5
Original line number Original line Diff line number Diff line
@@ -177,12 +177,15 @@ int usb_hub_create_port_device(struct usb_hub *hub, int port1)


	pm_runtime_set_active(&port_dev->dev);
	pm_runtime_set_active(&port_dev->dev);


	/* It would be dangerous if user space couldn't
	/*
	 * prevent usb device from being powered off. So don't
	 * Do not enable port runtime pm if the hub does not support
	 * enable port runtime pm if failed to expose port's pm qos.
	 * power switching.  Also, userspace must have final say of
	 * whether a port is permitted to power-off.  Do not enable
	 * runtime pm if we fail to expose pm_qos_no_power_off.
	 */
	 */
	if (!dev_pm_qos_expose_flags(&port_dev->dev,
	if (hub_is_port_power_switchable(hub)
			PM_QOS_FLAG_NO_POWER_OFF))
			&& dev_pm_qos_expose_flags(&port_dev->dev,
			PM_QOS_FLAG_NO_POWER_OFF) == 0)
		pm_runtime_enable(&port_dev->dev);
		pm_runtime_enable(&port_dev->dev);


	device_enable_async_suspend(&port_dev->dev);
	device_enable_async_suspend(&port_dev->dev);