forked from mirrors/linux
		
	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: Alan Stern <stern@rowland.harvard.edu> Signed-off-by: Dan Williams <dan.j.williams@intel.com> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
This commit is contained in:
		
							parent
							
								
									600856c231
								
							
						
					
					
						commit
						9262c19d14
					
				
					 3 changed files with 20 additions and 11 deletions
				
			
		| 
						 | 
				
			
			@ -818,8 +818,6 @@ static unsigned hub_power_on(struct usb_hub *hub, bool do_delay)
 | 
			
		|||
	int port1;
 | 
			
		||||
	unsigned pgood_delay = hub->descriptor->bPwrOn2PwrGood * 2;
 | 
			
		||||
	unsigned delay;
 | 
			
		||||
	u16 wHubCharacteristics =
 | 
			
		||||
			le16_to_cpu(hub->descriptor->wHubCharacteristics);
 | 
			
		||||
 | 
			
		||||
	/* Enable power on each port.  Some hubs have reserved values
 | 
			
		||||
	 * 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
 | 
			
		||||
	 * 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");
 | 
			
		||||
	else
 | 
			
		||||
		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 device *hub_dev = hub->intfdev;
 | 
			
		||||
	struct usb_hcd *hcd = bus_to_hcd(hdev->bus);
 | 
			
		||||
	unsigned wHubCharacteristics =
 | 
			
		||||
			le16_to_cpu(hub->descriptor->wHubCharacteristics);
 | 
			
		||||
	struct usb_device *udev;
 | 
			
		||||
	int status, i;
 | 
			
		||||
	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)) {
 | 
			
		||||
 | 
			
		||||
		/* 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))
 | 
			
		||||
			set_port_feature(hdev, port1, USB_PORT_FEAT_POWER);
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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,
 | 
			
		||||
		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,
 | 
			
		||||
		int port1)
 | 
			
		||||
{
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -177,12 +177,15 @@ int usb_hub_create_port_device(struct usb_hub *hub, int port1)
 | 
			
		|||
 | 
			
		||||
	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
 | 
			
		||||
	 * enable port runtime pm if failed to expose port's pm qos.
 | 
			
		||||
	/*
 | 
			
		||||
	 * Do not enable port runtime pm if the hub does not support
 | 
			
		||||
	 * 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,
 | 
			
		||||
			PM_QOS_FLAG_NO_POWER_OFF))
 | 
			
		||||
	if (hub_is_port_power_switchable(hub)
 | 
			
		||||
			&& dev_pm_qos_expose_flags(&port_dev->dev,
 | 
			
		||||
			PM_QOS_FLAG_NO_POWER_OFF) == 0)
 | 
			
		||||
		pm_runtime_enable(&port_dev->dev);
 | 
			
		||||
 | 
			
		||||
	device_enable_async_suspend(&port_dev->dev);
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in a new issue