Re: [RFC] usb: Add support for root hub port status CAS

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

 



> On 06/04/2012 09:34 PM, Stanislaw Ledwon wrote:
>>> On 06/04/2012 04:46 PM, Stanislaw Ledwon wrote:
>>>> The host controller port status register supports CAS (Could Attach
>
> s/Could/Cold/
>
>>>> Status) bit. This bit could be set when USB3.0 device is connectd
>
> s/connectd/connected/
>
>>>> when system is in Sx state. When the system wakes to S0 this port
>>>> status with CAS bit is reported and this port can't be used by any
>>>> device.
>>>>
>>>> When CAS bit is set the port should be reset by warm reset. This
>>>> was not supported by xhci driver.
>>>>
>>>> The issue was found when pendrive was connected to suspended
>>>> platfrom. The link state of "Compliance Mode" was reported togather
>
> s/platfrom/platform/
> s/togather/together/
>
>>>> with CAS bit. This link state was also not supported by xhci and
>>>> core/hub.c.
>>>>
>>>> The CAS bit is defined only for xhci root hub port and it is
>>>> not supported on regular hubs. The link status is used to force
>>>> warm reset on port. Make the USB core issue a warm reset when port
>>>> is in ether the 'inactive' or 'compliance mode'. Change the xHCI
>>>> driver
>>>> to report 'compliance mode' when the CAS is set. This force warm reset
>>>> on the root hub port.
>>>>
>>>
>>> Why fake the port status to compliance mode? All you want to do is
>>> issue
>>> a warm reset when finding a CAS bit set. Fake a bogus USB3 wPortStatus
>>> bit is simpler and clearer.
>>>
>>> It's like this:
>>>
>>> 1. define a bogus bit in ch11.h. You can use bit13 since it's reserved.
>>>
>>> #define USB_SS_PORT_STAT_CAS_BOGUS 0x2000 /* bit 13 */
>>>
>>> 2. In GetPortStatus in xhci_hub_control, report the CAS.
>>>
>>> 	If (temp & PORT_CAS)
>>> 		status |= USB_SS_PORT_STAT_CAS_BOGUS;
>>>
>>> 3. in hub.c, issue a warm reset if CAS is found.
>>>
>> The CAS bit is defined only on XHCI port status so I thought that this
>> is
>> better to not propagate this bit to core/hub. When the bogus bit is
>> defined in the reserved area of the port status then some conflicts are
>> possible on future USB products.
>> I think that this bogus bit should not be associated only with CAS but
>> should be used as general port reset request form controller specific
>> layer.
>> The reset on Compliance Modes should be supported as well - based on
>> USB3.0 HUB state diagram this is only possible way to return from this
>> state.
>>
>
> OK.
>
>>> +static bool hub_port_warm_reset_required(struct usb_hub *hub, u16
>>> portstatus)
>>>  {
>>>  	return hub_is_superspeed(hub->hdev) &&
>>> -		(portstatus & USB_PORT_STAT_LINK_STATE) ==
>>> -		USB_SS_PORT_LS_SS_INACTIVE;
>>> +		(((portstatus & USB_PORT_STAT_LINK_STATE) ==
>>> +		  USB_SS_PORT_LS_SS_INACTIVE) ||
>>> +		 (portstatus & USB_PORT_STAT_CAS_BOGUS));
>>>  }
>>>
>>>> Signed-off-by: Stanislaw Ledwon <staszek.ledwon@xxxxxxxxxxxxxxx>
>>>> ---
>>>>  drivers/usb/core/hub.c      |   18 +++++++++-------
>>>>  drivers/usb/host/xhci-hub.c |   44
>>>> +++++++++++++++++++++++++++++++++++++-----
>>>>  drivers/usb/host/xhci.h     |    6 ++++-
>>>>  3 files changed, 53 insertions(+), 15 deletions(-)
>>>>
>>>> diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c
>>>> index ec6c97d..572769b 100644
>>>> --- a/drivers/usb/core/hub.c
>>>> +++ b/drivers/usb/core/hub.c
>>>> @@ -2102,12 +2102,16 @@ static unsigned hub_is_wusb(struct usb_hub
>>>> *hub)
>>>>  static int hub_port_reset(struct usb_hub *hub, int port1,
>>>>  			struct usb_device *udev, unsigned int delay, bool warm);
>>>>
>>>> -/* Is a USB 3.0 port in the Inactive state? */
>>>> -static bool hub_port_inactive(struct usb_hub *hub, u16 portstatus)
>>>> +/* Is a USB 3.0 port in the Inactive or Complinance Mode state?
>>>> + * Port worm reset is required to recover
>>>> + */
>>>> +static bool hub_port_warm_reset_required(struct usb_hub *hub, u16
>>>> portstatus)
>>>>  {
>>>>  	return hub_is_superspeed(hub->hdev) &&
>>>> -		(portstatus & USB_PORT_STAT_LINK_STATE) ==
>>>> -		USB_SS_PORT_LS_SS_INACTIVE;
>>>> +		(((portstatus & USB_PORT_STAT_LINK_STATE) ==
>>>> +		  USB_SS_PORT_LS_SS_INACTIVE) ||
>>>> +		 ((portstatus & USB_PORT_STAT_LINK_STATE) ==
>>>> +		  USB_SS_PORT_LS_COMP_MOD)) ;
>>>>  }
>>>>
>>>>  static int hub_port_wait_reset(struct usb_hub *hub, int port1,
>>>> @@ -2143,7 +2147,7 @@ static int hub_port_wait_reset(struct usb_hub
>>>> *hub, int port1,
>>>>  			 *
>>>>  			 * See https://bugzilla.kernel.org/show_bug.cgi?id=41752
>>>>  			 */
>>>> -			if (hub_port_inactive(hub, portstatus)) {
>>>> +			if (hub_port_warm_reset_required(hub, portstatus)) {
>>>>  				int ret;
>>>>
>>>>  				if ((portchange & USB_PORT_STAT_C_CONNECTION))
>>>> @@ -3753,9 +3757,7 @@ static void hub_events(void)
>>>>  			/* Warm reset a USB3 protocol port if it's in
>>>>  			 * SS.Inactive state.
>>>>  			 */
>>>> -			if (hub_is_superspeed(hub->hdev) &&
>>>> -				(portstatus & USB_PORT_STAT_LINK_STATE)
>>>> -					== USB_SS_PORT_LS_SS_INACTIVE) {
>>>> +			if (hub_port_warm_reset_required(hub, portstatus)) {
>>>>  				dev_dbg(hub_dev, "warm reset port %d\n", i);
>>>>  				hub_port_reset(hub, i, NULL,
>>>>  						HUB_BH_RESET_TIME, true);
>>>> diff --git a/drivers/usb/host/xhci-hub.c b/drivers/usb/host/xhci-hub.c
>>>> index 673ad12..58e1e73 100644
>>>> --- a/drivers/usb/host/xhci-hub.c
>>>> +++ b/drivers/usb/host/xhci-hub.c
>>>> @@ -462,6 +462,42 @@ void xhci_test_and_clear_bit(struct xhci_hcd
>>>> *xhci,
>>>> __le32 __iomem **port_array,
>>>>  	}
>>>>  }
>>>>
>>>> +/* Updates Link Status for super Speed port */
>>>> +void xhci_hub_set_link_status(u32 *status, u32 status_reg)
>>>
>>> The function name is ambiguous. Its purpose is to report a bogus port
>>> status, not "set_link_status". There is another function named
>>> xhci_set_link_state() in the same file, which does complete different
>>> things.
>>>
>> What about xhci_report_link_state()?
>>
>
> That's better.
>
>>> Also, this should be a static function.
>> Yes.
>>>
>>>> +{
>>>> +	u32 pls = status_reg & PORT_PLS_MASK;
>>>> +
>>>> +	/* resume state is a xHCI internal state.
>>>> +	 * Do not report it to usb core.
>>>> +	 */
>>>> +	if (pls == XDEV_RESUME)
>>>> +		return;
>>>> +
>>>> +	/* When the CAS bit is set then warm reset
>>>> +	 * should be performed on port
>>>> +	 */
>>>> +	if (status_reg & PORT_CAS) {
>>>> +		/* The CAS bit can be set while the port is
>>>> +		 * in any link state.
>>>> +		 * Only roothubs have CAS bit, so we
>>>> +		 * pretend to be in compliance mode
>>>> +		 * unless we're already in compliance
>>>> +		 * or the inactive state.
>>>> +		 */
>>>> +		if (pls != USB_SS_PORT_LS_COMP_MOD &&
>>>> +		    pls != USB_SS_PORT_LS_SS_INACTIVE) {
>
> This if is not necessary if you want a warm reset anyway.
> What is the real link state when CAS is set? Is it the same every time?
The link state is not guarantied for CAS case. I observe 'Compliance Mode'
every time. This link state is set with PORT_CONNECTED bit clear and with
CSC (Connect Status Change) bit set. I'm not sure if this sate will always
be the same (on other xhci, other device) so I check the link state to be
sure that warm reset will be performed. If we change solution to bogus CAS
bit support in core/hub then this part is useless.


Thanks,

Staszek

>
>>>> +			pls = USB_SS_PORT_LS_COMP_MOD;
>>>> +		}
>>>> +		/* Return also connection bit -
>>>> +		 * hub state machine resets port
>>>> +		 * when this bit is set.
>>>> +		 */
>>>> +		pls |= USB_PORT_STAT_CONNECTION;
>>>
>>> Why do you need this setting? Does PORTSC return a PORT_CONNECT when
>>> CAS
>>> is set?
>> In case of exiting S3 when CAS bit is set, the PORT_CONNECT is not set
>> and
>> port status change is ignored by core/hub.
>>
>
> I see.
>
> Thanks,
> Andiry
>
>
>>>
>>>> +	}
>>>> +	/* update status field */
>>>> +	*status |= pls;
>>>> +}
>>>> +
>>>>  int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
>>>>  		u16 wIndex, char *buf, u16 wLength)
>>>>  {
>>>> @@ -604,13 +640,9 @@ int xhci_hub_control(struct usb_hcd *hcd, u16
>>>> typeReq, u16 wValue,
>>>>  			else
>>>>  				status |= USB_PORT_STAT_POWER;
>>>>  		}
>>>> -		/* Port Link State */
>>>> +		/* Update Port Link State for super speed ports*/
>>>>  		if (hcd->speed == HCD_USB3) {
>>>> -			/* resume state is a xHCI internal state.
>>>> -			 * Do not report it to usb core.
>>>> -			 */
>>>> -			if ((temp & PORT_PLS_MASK) != XDEV_RESUME)
>>>> -				status |= (temp & PORT_PLS_MASK);
>>>> +			xhci_hub_set_link_status(&status, temp);
>>>>  		}
>>>>  		if (bus_state->port_c_suspend & (1 << wIndex))
>>>>  			status |= 1 << USB_PORT_FEAT_C_SUSPEND;
>>>> diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h
>>>> index 3d69c4b..7e8280a 100644
>>>> --- a/drivers/usb/host/xhci.h
>>>> +++ b/drivers/usb/host/xhci.h
>>>> @@ -341,7 +341,11 @@ struct xhci_op_regs {
>>>>  #define PORT_PLC	(1 << 22)
>>>>  /* port configure error change - port failed to configure its link
>>>> partner */
>>>>  #define PORT_CEC	(1 << 23)
>>>> -/* bit 24 reserved */
>>>> +/* Cold Attach Status - xHC can set this bit to report device
>>>> attached
>>>> during
>>>> + * Sx state. Warm port reset should be perfomed to clear this bit and
>>>> move port
>>>> + * to connected state.
>>>> + */
>>>> +#define PORT_CAS	(1 << 24)
>>>>  /* wake on connect (enable) */
>>>>  #define PORT_WKCONN_E	(1 << 25)
>>>>  /* wake on disconnect (enable) */
>>>
>>>
>>>
>>
>>
>
>
>

--
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