Skip to content
Permalink
Browse files
usb: hub: Power cycle root hub if CSC is set during hub_port_reset
When a FS device is following a suspend-reset-enumeration-data
transfer sequence, sometimes it goes back in suspend just after reset
without the link entering L0. This is seen in only when the following
scenarios are met:
- SOF and EOR happens at the same clock cycle
- UTMI line state should transition from SE0 to K at the same clock
cycle(if the UTMI line state transition from SE0 to J at the same
clock cycle then problem is not seen)

Attemting a power cycle of the root hub recovers the problem described.
To identify the issue, PLS goes to disabled state followed by CSC bit
being set(because of CCS status change).

Signed-off-by: Pratham Pratap <quic_ppratap@quicinc.com>
  • Loading branch information
Pratham Pratap authored and intel-lab-lkp committed Jan 19, 2022
1 parent cbb4f5f commit ec4d5f04b268fc19d3b5d2843d1889531dafd22f
Show file tree
Hide file tree
Showing 4 changed files with 31 additions and 8 deletions.
@@ -2834,10 +2834,20 @@ static bool hub_port_warm_reset_required(struct usb_hub *hub, int port1,
|| link_state == USB_SS_PORT_LS_COMP_MOD;
}

static void usb_hub_port_power_cycle(struct usb_device *hdev, struct usb_hub *hub, int port1)
{
dev_info(&hub->ports[port1 - 1]->dev, "attempt power cycle\n");
usb_hub_set_port_power(hdev, hub, port1, false);
msleep(2 * hub_power_on_good_delay(hub));
usb_hub_set_port_power(hdev, hub, port1, true);
msleep(hub_power_on_good_delay(hub));
}

static int hub_port_wait_reset(struct usb_hub *hub, int port1,
struct usb_device *udev, unsigned int delay, bool warm)
{
int delay_time, ret;
struct usb_hcd *hcd = bus_to_hcd(udev->bus);
u16 portstatus;
u16 portchange;
u32 ext_portstatus = 0;
@@ -2887,8 +2897,21 @@ static int hub_port_wait_reset(struct usb_hub *hub, int port1,
return -ENOTCONN;

/* Device went away? */
if (!(portstatus & USB_PORT_STAT_CONNECTION))
if (!(portstatus & USB_PORT_STAT_CONNECTION)) {
/*
* When a FS device is following a suspend-reset-enumeration-data_transfer
* sequence, sometimes it goes back in suspend just after reset without the
* link entering L0. To fix this when CSC bit is set(because of CCS status
* change) power cycle the root hub.
*/
if (udev->reset_resume && (!udev->parent && hcd->fs_suspend_reset) &&
(portstatus & USB_PORT_STAT_CSC)) {
usb_hub_port_power_cycle(hdev, hub, port1);
return -EAGAIN;
}

return -ENOTCONN;
}

/* Retry if connect change is set but status is still connected.
* A USB 3.0 connection may bounce if multiple warm resets were issued,
@@ -5393,13 +5416,8 @@ static void hub_port_connect(struct usb_hub *hub, int port1, u16 portstatus,
break;

/* When halfway through our retry count, power-cycle the port */
if (i == (PORT_INIT_TRIES - 1) / 2) {
dev_info(&port_dev->dev, "attempt power cycle\n");
usb_hub_set_port_power(hdev, hub, port1, false);
msleep(2 * hub_power_on_good_delay(hub));
usb_hub_set_port_power(hdev, hub, port1, true);
msleep(hub_power_on_good_delay(hub));
}
if (i == (PORT_INIT_TRIES - 1) / 2)
usb_hub_port_power_cycle(hdev, hub, port1);
}
if (hub->hdev->parent ||
!hcd->driver->port_handed_over ||
@@ -342,6 +342,9 @@ static int xhci_plat_probe(struct platform_device *pdev)
hcd->tpl_support = of_usb_host_tpl_support(sysdev->of_node);
xhci->shared_hcd->tpl_support = hcd->tpl_support;

hcd->fs_suspend_reset = of_property_read_bool(sysdev->of_node, "fs-suspend-reset");
xhci->shared_hcd->fs_suspend_reset = hcd->fs_suspend_reset;

if (priv) {
ret = xhci_priv_plat_setup(hcd);
if (ret)
@@ -172,6 +172,7 @@ struct usb_hcd {
unsigned tpl_support:1; /* OTG & EH TPL support */
unsigned cant_recv_wakeups:1;
/* wakeup requests from downstream aren't received */
unsigned fs_suspend_reset:1; /* fs suspend reset bug */

unsigned int irq; /* irq allocated */
void __iomem *regs; /* device memory/io */
@@ -135,6 +135,7 @@ struct usb_port_status {
#define USB_PORT_STAT_TEST 0x0800
#define USB_PORT_STAT_INDICATOR 0x1000
/* bits 13 to 15 are reserved */
#define USB_PORT_STAT_CSC 0x20000

/*
* Additions to wPortStatus bit field from USB 3.0

0 comments on commit ec4d5f0

Please sign in to comment.