aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/usb
diff options
context:
space:
mode:
authorStanislaw Ledwon <staszek.ledwon@linux.jf.intel.com>2012-06-18 09:20:00 -0400
committerSarah Sharp <sarah.a.sharp@linux.intel.com>2012-07-02 15:51:24 -0400
commit8bea2bd37df08aaa599aa361a9f8b836ba98e554 (patch)
tree512482947a1d4a69796443d7f28fb968d8e52236 /drivers/usb
parent6887a4131da3adaab011613776d865f4bcfb5678 (diff)
usb: Add support for root hub port status CAS
The host controller port status register supports CAS (Cold Attach Status) bit. This bit could be set when USB3.0 device is 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 platform. The link state of "Compliance Mode" was reported 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. This patch should be backported to stable kernels as old as 3.2, that contain the commit 10d674a82e553cb8a1f41027bb3c3e309b3f6804 "USB: When hot reset for USB3 fails, try warm reset." Signed-off-by: Stanislaw Ledwon <staszek.ledwon@linux.intel.com> Signed-off-by: Sarah Sharp <sarah.a.sharp@linux.intel.com> Acked-by: Andiry Xu <andiry.xu@amd.com> Cc: stable@vger.kernel.org
Diffstat (limited to 'drivers/usb')
-rw-r--r--drivers/usb/core/hub.c18
-rw-r--r--drivers/usb/host/xhci-hub.c44
-rw-r--r--drivers/usb/host/xhci.h6
3 files changed, 53 insertions, 15 deletions
diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c
index 25a7422ee657..8fb484984c86 100644
--- a/drivers/usb/core/hub.c
+++ b/drivers/usb/core/hub.c
@@ -2324,12 +2324,16 @@ static unsigned hub_is_wusb(struct usb_hub *hub)
2324static int hub_port_reset(struct usb_hub *hub, int port1, 2324static int hub_port_reset(struct usb_hub *hub, int port1,
2325 struct usb_device *udev, unsigned int delay, bool warm); 2325 struct usb_device *udev, unsigned int delay, bool warm);
2326 2326
2327/* Is a USB 3.0 port in the Inactive state? */ 2327/* Is a USB 3.0 port in the Inactive or Complinance Mode state?
2328static bool hub_port_inactive(struct usb_hub *hub, u16 portstatus) 2328 * Port worm reset is required to recover
2329 */
2330static bool hub_port_warm_reset_required(struct usb_hub *hub, u16 portstatus)
2329{ 2331{
2330 return hub_is_superspeed(hub->hdev) && 2332 return hub_is_superspeed(hub->hdev) &&
2331 (portstatus & USB_PORT_STAT_LINK_STATE) == 2333 (((portstatus & USB_PORT_STAT_LINK_STATE) ==
2332 USB_SS_PORT_LS_SS_INACTIVE; 2334 USB_SS_PORT_LS_SS_INACTIVE) ||
2335 ((portstatus & USB_PORT_STAT_LINK_STATE) ==
2336 USB_SS_PORT_LS_COMP_MOD)) ;
2333} 2337}
2334 2338
2335static int hub_port_wait_reset(struct usb_hub *hub, int port1, 2339static int hub_port_wait_reset(struct usb_hub *hub, int port1,
@@ -2365,7 +2369,7 @@ static int hub_port_wait_reset(struct usb_hub *hub, int port1,
2365 * 2369 *
2366 * See https://bugzilla.kernel.org/show_bug.cgi?id=41752 2370 * See https://bugzilla.kernel.org/show_bug.cgi?id=41752
2367 */ 2371 */
2368 if (hub_port_inactive(hub, portstatus)) { 2372 if (hub_port_warm_reset_required(hub, portstatus)) {
2369 int ret; 2373 int ret;
2370 2374
2371 if ((portchange & USB_PORT_STAT_C_CONNECTION)) 2375 if ((portchange & USB_PORT_STAT_C_CONNECTION))
@@ -4408,9 +4412,7 @@ static void hub_events(void)
4408 /* Warm reset a USB3 protocol port if it's in 4412 /* Warm reset a USB3 protocol port if it's in
4409 * SS.Inactive state. 4413 * SS.Inactive state.
4410 */ 4414 */
4411 if (hub_is_superspeed(hub->hdev) && 4415 if (hub_port_warm_reset_required(hub, portstatus)) {
4412 (portstatus & USB_PORT_STAT_LINK_STATE)
4413 == USB_SS_PORT_LS_SS_INACTIVE) {
4414 dev_dbg(hub_dev, "warm reset port %d\n", i); 4416 dev_dbg(hub_dev, "warm reset port %d\n", i);
4415 hub_port_reset(hub, i, NULL, 4417 hub_port_reset(hub, i, NULL,
4416 HUB_BH_RESET_TIME, true); 4418 HUB_BH_RESET_TIME, true);
diff --git a/drivers/usb/host/xhci-hub.c b/drivers/usb/host/xhci-hub.c
index 2732ef660c5c..7b01094d7993 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,
462 } 462 }
463} 463}
464 464
465/* Updates Link Status for super Speed port */
466static void xhci_hub_report_link_state(u32 *status, u32 status_reg)
467{
468 u32 pls = status_reg & PORT_PLS_MASK;
469
470 /* resume state is a xHCI internal state.
471 * Do not report it to usb core.
472 */
473 if (pls == XDEV_RESUME)
474 return;
475
476 /* When the CAS bit is set then warm reset
477 * should be performed on port
478 */
479 if (status_reg & PORT_CAS) {
480 /* The CAS bit can be set while the port is
481 * in any link state.
482 * Only roothubs have CAS bit, so we
483 * pretend to be in compliance mode
484 * unless we're already in compliance
485 * or the inactive state.
486 */
487 if (pls != USB_SS_PORT_LS_COMP_MOD &&
488 pls != USB_SS_PORT_LS_SS_INACTIVE) {
489 pls = USB_SS_PORT_LS_COMP_MOD;
490 }
491 /* Return also connection bit -
492 * hub state machine resets port
493 * when this bit is set.
494 */
495 pls |= USB_PORT_STAT_CONNECTION;
496 }
497 /* update status field */
498 *status |= pls;
499}
500
465int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, 501int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
466 u16 wIndex, char *buf, u16 wLength) 502 u16 wIndex, char *buf, u16 wLength)
467{ 503{
@@ -606,13 +642,9 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
606 else 642 else
607 status |= USB_PORT_STAT_POWER; 643 status |= USB_PORT_STAT_POWER;
608 } 644 }
609 /* Port Link State */ 645 /* Update Port Link State for super speed ports*/
610 if (hcd->speed == HCD_USB3) { 646 if (hcd->speed == HCD_USB3) {
611 /* resume state is a xHCI internal state. 647 xhci_hub_report_link_state(&status, temp);
612 * Do not report it to usb core.
613 */
614 if ((temp & PORT_PLS_MASK) != XDEV_RESUME)
615 status |= (temp & PORT_PLS_MASK);
616 } 648 }
617 if (bus_state->port_c_suspend & (1 << wIndex)) 649 if (bus_state->port_c_suspend & (1 << wIndex))
618 status |= 1 << USB_PORT_FEAT_C_SUSPEND; 650 status |= 1 << USB_PORT_FEAT_C_SUSPEND;
diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h
index de3d6e3e57be..55c0785810c9 100644
--- a/drivers/usb/host/xhci.h
+++ b/drivers/usb/host/xhci.h
@@ -341,7 +341,11 @@ struct xhci_op_regs {
341#define PORT_PLC (1 << 22) 341#define PORT_PLC (1 << 22)
342/* port configure error change - port failed to configure its link partner */ 342/* port configure error change - port failed to configure its link partner */
343#define PORT_CEC (1 << 23) 343#define PORT_CEC (1 << 23)
344/* bit 24 reserved */ 344/* Cold Attach Status - xHC can set this bit to report device attached during
345 * Sx state. Warm port reset should be perfomed to clear this bit and move port
346 * to connected state.
347 */
348#define PORT_CAS (1 << 24)
345/* wake on connect (enable) */ 349/* wake on connect (enable) */
346#define PORT_WKCONN_E (1 << 25) 350#define PORT_WKCONN_E (1 << 25)
347/* wake on disconnect (enable) */ 351/* wake on disconnect (enable) */