Re: [RFC v2 5/9] USB/xHCI: Enable USB 3.0 hub remote wakeup.

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On Wed, Jan 25, 2012 at 03:36:10PM -0800, Sarah Sharp wrote:
> USB 3.0 hubs have a different remote wakeup policy than USB 2.0 hubs.
> USB 2.0 hubs, once they have remote wakeup enabled, will always send
> remote wakes when anything changes on a port.
> 
> However, USB 3.0 hubs have a per-port remote wake up policy that is off
> by default.  The Set Feature remote wake mask can be changed for any
> port, enabling remote wakeup for a connect, disconnect, or overcurrent
> event, much like EHCI and xHCI host controller "wake on" port status
> bits.  The bits are cleared to zero on the initial hub power on, or
> after the hub has been reset.
> 
> Without this patch, when a USB 3.0 hub gets suspended, it will not send
> a remote wakeup on device connect or disconnect.  This would show up to
> the user as "dead ports" unless they ran lsusb -v (since newer versions
> of lsusb use the sysfs files, rather than sending control transfers).
> 
> Change the hub driver's suspend method to enable remote wake up for
> disconnect, connect, and overcurrent for all ports on the hub.  Modify
> the xHCI driver's roothub code to handle that request, and set the "wake
> on" bits in the port status registers accordingly.
> 
> Alan: there are three things I'm uncertain about here, and I would
> appreciate your input.

Sorry Alan, I changed the patch and forgot to take out my questions that
you've already answered.  Please ignore the remainder of the commit
message.

> 
> First, should we follow what EHCI and xHCI do during bus suspend, by
> only enabling disconnect and overcurrent wake notifications for ports
> with a device connected to a USB 3.0 hub, and enabling connect and
> overcurrent wake notifications for ports without a connection?  I've
> long wondered why xHCI can't just set all the wake-on bits for a port,
> rather than try to make the racy distinction between connected and
> unconnected ports.
> 
> Second, what is the expected behavior of system suspend when
> CONFIG_USB_SUSPEND is turned off?  It looks like in the current EHCI and
> xHCI code that the "wake on" bits are set in bus_suspend.  Does that
> method get called if CONFIG_USB_SUSPEND is turned off?  Is the intention
> that the system will resume if a new USB device is plugged in, even if
> CONFIG_USB_SUSPEND is off?
> 
> I ask because it seems like hub_suspend is not going to be called for
> the USB 3.0 roothub if CONFIG_USB_SUSPEND is off.  If I want to be sure
> the wake on bits are set for USB 3.0 ports (rather than relying on the
> new code in hub_suspend), I have to have duplicate code in xHCI's
> bus_suspend method to set those bits.
> 
> Third, should we only set the wake on bits for USB 3.0 hubs during the
> hub initialization, or should we wait to do that in hub_suspend?
> 
> Signed-off-by: Sarah Sharp <sarah.a.sharp@xxxxxxxxxxxxxxx>
> Cc: Alan Stern <stern@xxxxxxxxxxxxxxxxxxx>
> ---
>  drivers/usb/core/hub.c      |   12 ++++++++++++
>  drivers/usb/host/xhci-hub.c |   41 +++++++++++++++++++++++++++++++++++++++++
>  include/linux/usb/ch11.h    |    5 +++++
>  3 files changed, 58 insertions(+), 0 deletions(-)
> 
> diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c
> index eb6a76d..4423245 100644
> --- a/drivers/usb/core/hub.c
> +++ b/drivers/usb/core/hub.c
> @@ -2691,6 +2691,7 @@ static int hub_suspend(struct usb_interface *intf, pm_message_t msg)
>  	struct usb_hub		*hub = usb_get_intfdata (intf);
>  	struct usb_device	*hdev = hub->hdev;
>  	unsigned		port1;
> +	int			status;
>  
>  	/* Warn if children aren't already suspended */
>  	for (port1 = 1; port1 <= hdev->maxchild; port1++) {
> @@ -2703,6 +2704,17 @@ static int hub_suspend(struct usb_interface *intf, pm_message_t msg)
>  				return -EBUSY;
>  		}
>  	}
> +	if (hub_is_superspeed(hdev) && hdev->do_remote_wakeup) {
> +		/* Enable hub to send remote wakeup for all ports. */
> +		for (port1 = 1; port1 <= hdev->maxchild; port1++) {
> +			status = set_port_feature(hdev,
> +					port1 |
> +					USB_PORT_FEAT_REMOTE_WAKE_CONNECT |
> +					USB_PORT_FEAT_REMOTE_WAKE_DISCONNECT |
> +					USB_PORT_FEAT_REMOTE_WAKE_OVER_CURRENT,
> +					USB_PORT_FEAT_REMOTE_WAKE_MASK);
> +		}
> +	}
>  
>  	dev_dbg(&intf->dev, "%s\n", __func__);
>  
> diff --git a/drivers/usb/host/xhci-hub.c b/drivers/usb/host/xhci-hub.c
> index 35e257f..d98acc9 100644
> --- a/drivers/usb/host/xhci-hub.c
> +++ b/drivers/usb/host/xhci-hub.c
> @@ -422,6 +422,32 @@ void xhci_set_link_state(struct xhci_hcd *xhci, __le32 __iomem **port_array,
>  	xhci_writel(xhci, temp, port_array[port_id]);
>  }
>  
> +void xhci_set_remote_wake_mask(struct xhci_hcd *xhci,
> +		__le32 __iomem **port_array, int port_id, u16 wake_mask)
> +{
> +	u32 temp;
> +
> +	temp = xhci_readl(xhci, port_array[port_id]);
> +	temp = xhci_port_state_to_neutral(temp);
> +
> +	if (wake_mask & USB_PORT_FEAT_REMOTE_WAKE_CONNECT)
> +		temp |= PORT_WKCONN_E;
> +	else
> +		temp &= ~PORT_WKCONN_E;
> +
> +	if (wake_mask & USB_PORT_FEAT_REMOTE_WAKE_DISCONNECT)
> +		temp |= PORT_WKDISC_E;
> +	else
> +		temp &= ~PORT_WKDISC_E;
> +
> +	if (wake_mask & USB_PORT_FEAT_REMOTE_WAKE_OVER_CURRENT)
> +		temp |= PORT_WKOC_E;
> +	else
> +		temp &= ~PORT_WKOC_E;
> +
> +	xhci_writel(xhci, temp, port_array[port_id]);
> +}
> +
>  /* Test and clear port RWC bit */
>  void xhci_test_and_clear_bit(struct xhci_hcd *xhci, __le32 __iomem **port_array,
>  				int port_id, u32 port_bit)
> @@ -448,6 +474,7 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
>  	int slot_id;
>  	struct xhci_bus_state *bus_state;
>  	u16 link_state = 0;
> +	u16 wake_mask = 0;
>  
>  	max_ports = xhci_get_ports(hcd, &port_array);
>  	bus_state = &xhci->bus_state[hcd_index(hcd)];
> @@ -593,6 +620,8 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
>  	case SetPortFeature:
>  		if (wValue == USB_PORT_FEAT_LINK_STATE)
>  			link_state = (wIndex & 0xff00) >> 3;
> +		if (wValue == USB_PORT_FEAT_REMOTE_WAKE_MASK)
> +			wake_mask = wIndex & 0xff00;
>  		wIndex &= 0xff;
>  		if (!wIndex || wIndex > max_ports)
>  			goto error;
> @@ -703,6 +732,14 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
>  			temp = xhci_readl(xhci, port_array[wIndex]);
>  			xhci_dbg(xhci, "set port reset, actual port %d status  = 0x%x\n", wIndex, temp);
>  			break;
> +		case USB_PORT_FEAT_REMOTE_WAKE_MASK:
> +			xhci_set_remote_wake_mask(xhci, port_array,
> +					wIndex, wake_mask);
> +			temp = xhci_readl(xhci, port_array[wIndex]);
> +			xhci_dbg(xhci, "set port remote wake mask, "
> +					"actual port %d status  = 0x%x\n",
> +					wIndex, temp);
> +			break;
>  		case USB_PORT_FEAT_BH_PORT_RESET:
>  			temp |= PORT_WR;
>  			xhci_writel(xhci, temp, port_array[wIndex]);
> @@ -883,6 +920,10 @@ int xhci_bus_suspend(struct usb_hcd *hcd)
>  			t2 |= PORT_LINK_STROBE | XDEV_U3;
>  			set_bit(port_index, &bus_state->bus_suspended);
>  		}
> +		/* USB core sets remote wake mask for USB 3.0 hubs,
> +		 * including the USB 3.0 roothub, but only if CONFIG_USB_SUSPEND
> +		 * is enabled, so also enable remote wake here.
> +		 */
>  		if (hcd->self.root_hub->do_remote_wakeup) {
>  			if (t1 & PORT_CONNECT) {
>  				t2 |= PORT_WKOC_E | PORT_WKDISC_E;
> diff --git a/include/linux/usb/ch11.h b/include/linux/usb/ch11.h
> index 0b83acd..f1d26b6 100644
> --- a/include/linux/usb/ch11.h
> +++ b/include/linux/usb/ch11.h
> @@ -76,6 +76,11 @@
>  #define USB_PORT_FEAT_C_BH_PORT_RESET		29
>  #define USB_PORT_FEAT_FORCE_LINKPM_ACCEPT	30
>  
> +/* USB 3.0 hub remote wake mask bits, see table 10-14 */
> +#define USB_PORT_FEAT_REMOTE_WAKE_CONNECT	(1 << 8)
> +#define USB_PORT_FEAT_REMOTE_WAKE_DISCONNECT	(1 << 9)
> +#define USB_PORT_FEAT_REMOTE_WAKE_OVER_CURRENT	(1 << 10)
> +
>  /*
>   * Hub Status and Hub Change results
>   * See USB 2.0 spec Table 11-19 and Table 11-20
> -- 
> 1.7.5.4
> 
> --
> To unsubscribe from this list: send the line "unsubscribe linux-usb" in
> the body of a message to majordomo@xxxxxxxxxxxxxxx
> More majordomo info at  http://vger.kernel.org/majordomo-info.html
--
To unsubscribe from this list: send the line "unsubscribe linux-usb" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html


[Index of Archives]     [Linux Media]     [Linux Input]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]     [Old Linux USB Devel Archive]

  Powered by Linux