usb: ehci: Avoid phy lockup due to SOFs during port reset
There could be race condition between h/w initiating sof (start of frames) and finishing port reset. This can cause hsic phy lockup, which results in enumeration failure. Avoid this race condition by: 1. Moving to the old enumeration sequence where set_address is initiated before get_device descriptor. This results in one port reset instead of two port resets. 2. Halt usb controller before initiating port reset and start it after port reset is complete. This is tricky because PORT_RESET bit automatically clears when h/w completes the reset and failure to start the controller within 3ms causes the peripheral device to suspend resulting in enumeration failure. Hence, after setting port reset bit forcefully drive strobe/data lines to reset using ulpi interface and once port reset is complete, disable forceful reset and start the controller in atomic context. CRs-fixed: 364458, 359930 Change-Id: I49a2eac8043eb3001956c7ee9ead2c3a901524db Signed-off-by: Vamsi Krishna <vskrishn@codeaurora.org>
This commit is contained in:
committed by
Stephen Boyd
parent
c9c033935d
commit
a197142a84
@@ -2808,6 +2808,7 @@ int usb_port_resume(struct usb_device *udev, pm_message_t msg)
|
||||
int usb_remote_wakeup(struct usb_device *udev)
|
||||
{
|
||||
int status = 0;
|
||||
struct usb_hcd *hcd = bus_to_hcd(udev->bus);
|
||||
|
||||
if (udev->state == USB_STATE_SUSPENDED) {
|
||||
dev_dbg(&udev->dev, "usb %sresume\n", "wakeup-");
|
||||
@@ -2816,7 +2817,11 @@ int usb_remote_wakeup(struct usb_device *udev)
|
||||
/* Let the drivers do their thing, then... */
|
||||
usb_autosuspend_device(udev);
|
||||
}
|
||||
} else {
|
||||
dev_dbg(&udev->dev, "usb not suspended\n");
|
||||
clear_bit(HCD_FLAG_WAKEUP_PENDING, &hcd->flags);
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
@@ -3152,7 +3157,9 @@ hub_port_init (struct usb_hub *hub, struct usb_device *udev, int port1,
|
||||
* value.
|
||||
*/
|
||||
for (i = 0; i < GET_DESCRIPTOR_TRIES; (++i, msleep(100))) {
|
||||
if (USE_NEW_SCHEME(retry_counter) && !(hcd->driver->flags & HCD_USB3)) {
|
||||
if (USE_NEW_SCHEME(retry_counter) &&
|
||||
!(hcd->driver->flags & HCD_USB3) &&
|
||||
!(hcd->driver->flags & HCD_OLD_ENUM)) {
|
||||
struct usb_device_descriptor *buf;
|
||||
int r = 0;
|
||||
|
||||
@@ -3252,7 +3259,9 @@ hub_port_init (struct usb_hub *hub, struct usb_device *udev, int port1,
|
||||
* - read ep0 maxpacket even for high and low speed,
|
||||
*/
|
||||
msleep(10);
|
||||
if (USE_NEW_SCHEME(retry_counter) && !(hcd->driver->flags & HCD_USB3))
|
||||
if (USE_NEW_SCHEME(retry_counter) &&
|
||||
!(hcd->driver->flags & HCD_USB3) &&
|
||||
!(hcd->driver->flags & HCD_OLD_ENUM))
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
@@ -823,7 +823,7 @@ static int ehci_hub_control (
|
||||
u32 __iomem *status_reg = &ehci->regs->port_status[
|
||||
(wIndex & 0xff) - 1];
|
||||
u32 __iomem *hostpc_reg = NULL;
|
||||
u32 temp, temp1, status;
|
||||
u32 temp, temp1, status, cmd = 0;
|
||||
unsigned long flags;
|
||||
int retval = 0;
|
||||
unsigned selector;
|
||||
@@ -1202,7 +1202,32 @@ static int ehci_hub_control (
|
||||
ehci->reset_done [wIndex] = jiffies
|
||||
+ msecs_to_jiffies (50);
|
||||
}
|
||||
|
||||
if (ehci->reset_sof_bug && (temp & PORT_RESET)) {
|
||||
cmd = ehci_readl(ehci, &ehci->regs->command);
|
||||
cmd &= ~CMD_RUN;
|
||||
ehci_writel(ehci, cmd, &ehci->regs->command);
|
||||
if (handshake(ehci, &ehci->regs->status,
|
||||
STS_HALT, STS_HALT, 16 * 125))
|
||||
ehci_info(ehci,
|
||||
"controller halt failed\n");
|
||||
}
|
||||
ehci_writel(ehci, temp, status_reg);
|
||||
if (ehci->reset_sof_bug && (temp & PORT_RESET)
|
||||
&& hcd->driver->enable_ulpi_control) {
|
||||
hcd->driver->enable_ulpi_control(hcd,
|
||||
PORT_RESET);
|
||||
spin_unlock_irqrestore(&ehci->lock, flags);
|
||||
usleep_range(50000, 55000);
|
||||
if (handshake(ehci, status_reg,
|
||||
PORT_RESET, 0, 10 * 1000))
|
||||
ehci_info(ehci,
|
||||
"failed to clear reset\n");
|
||||
spin_lock_irqsave(&ehci->lock, flags);
|
||||
hcd->driver->disable_ulpi_control(hcd);
|
||||
cmd |= CMD_RUN;
|
||||
ehci_writel(ehci, cmd, &ehci->regs->command);
|
||||
}
|
||||
break;
|
||||
|
||||
/* For downstream facing ports (these): one hub port is put
|
||||
|
||||
@@ -328,6 +328,29 @@ reg_enable_err:
|
||||
|
||||
}
|
||||
|
||||
static int ulpi_read(struct msm_hsic_hcd *mehci, u32 reg)
|
||||
{
|
||||
struct usb_hcd *hcd = hsic_to_hcd(mehci);
|
||||
unsigned long timeout;
|
||||
|
||||
/* initiate read operation */
|
||||
writel_relaxed(ULPI_RUN | ULPI_READ | ULPI_ADDR(reg),
|
||||
USB_ULPI_VIEWPORT);
|
||||
|
||||
/* wait for completion */
|
||||
timeout = jiffies + usecs_to_jiffies(ULPI_IO_TIMEOUT_USEC);
|
||||
while (readl_relaxed(USB_ULPI_VIEWPORT) & ULPI_RUN) {
|
||||
if (time_after(jiffies, timeout)) {
|
||||
dev_err(mehci->dev, "ulpi_read: timeout %08x\n",
|
||||
readl_relaxed(USB_ULPI_VIEWPORT));
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
udelay(1);
|
||||
}
|
||||
|
||||
return ULPI_DATA_READ(readl_relaxed(USB_ULPI_VIEWPORT));
|
||||
}
|
||||
|
||||
static int ulpi_write(struct msm_hsic_hcd *mehci, u32 val, u32 reg)
|
||||
{
|
||||
struct usb_hcd *hcd = hsic_to_hcd(mehci);
|
||||
@@ -354,6 +377,37 @@ static int ulpi_write(struct msm_hsic_hcd *mehci, u32 val, u32 reg)
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define HSIC_DBG1 0X38
|
||||
#define ULPI_MANUAL_ENABLE BIT(4)
|
||||
#define ULPI_LINESTATE_DATA BIT(5)
|
||||
#define ULPI_LINESTATE_STROBE BIT(6)
|
||||
static void ehci_msm_enable_ulpi_control(struct usb_hcd *hcd, u32 linestate)
|
||||
{
|
||||
struct msm_hsic_hcd *mehci = hcd_to_hsic(hcd);
|
||||
int val;
|
||||
|
||||
switch (linestate) {
|
||||
case PORT_RESET:
|
||||
val = ulpi_read(mehci, HSIC_DBG1);
|
||||
val |= ULPI_MANUAL_ENABLE;
|
||||
val &= ~(ULPI_LINESTATE_DATA | ULPI_LINESTATE_STROBE);
|
||||
ulpi_write(mehci, val, HSIC_DBG1);
|
||||
break;
|
||||
default:
|
||||
pr_info("%s: Unknown linestate:%0x\n", __func__, linestate);
|
||||
}
|
||||
}
|
||||
|
||||
static void ehci_msm_disable_ulpi_control(struct usb_hcd *hcd)
|
||||
{
|
||||
struct msm_hsic_hcd *mehci = hcd_to_hsic(hcd);
|
||||
int val;
|
||||
|
||||
val = ulpi_read(mehci, HSIC_DBG1);
|
||||
val &= ~ULPI_MANUAL_ENABLE;
|
||||
ulpi_write(mehci, val, HSIC_DBG1);
|
||||
}
|
||||
|
||||
static int msm_hsic_config_gpios(struct msm_hsic_hcd *mehci, int gpio_en)
|
||||
{
|
||||
int rc = 0;
|
||||
@@ -788,7 +842,7 @@ static struct hc_driver msm_hsic_driver = {
|
||||
* generic hardware linkage
|
||||
*/
|
||||
.irq = msm_hsic_irq,
|
||||
.flags = HCD_USB2 | HCD_MEMORY,
|
||||
.flags = HCD_USB2 | HCD_MEMORY | HCD_OLD_ENUM,
|
||||
|
||||
.reset = ehci_hsic_reset,
|
||||
.start = ehci_run,
|
||||
@@ -825,6 +879,9 @@ static struct hc_driver msm_hsic_driver = {
|
||||
.bus_resume = ehci_hsic_bus_resume,
|
||||
|
||||
.log_urb_complete = dbg_log_event,
|
||||
|
||||
.enable_ulpi_control = ehci_msm_enable_ulpi_control,
|
||||
.disable_ulpi_control = ehci_msm_disable_ulpi_control,
|
||||
};
|
||||
|
||||
static int msm_hsic_init_clocks(struct msm_hsic_hcd *mehci, u32 init)
|
||||
@@ -1187,6 +1244,7 @@ static int __devinit ehci_hsic_msm_probe(struct platform_device *pdev)
|
||||
mehci->dev = &pdev->dev;
|
||||
|
||||
mehci->ehci.susp_sof_bug = 1;
|
||||
mehci->ehci.reset_sof_bug = 1;
|
||||
|
||||
mehci->ehci.max_log2_irq_thresh = 6;
|
||||
|
||||
|
||||
@@ -152,6 +152,7 @@ struct ehci_hcd { /* one per controller */
|
||||
unsigned has_synopsys_hc_bug:1; /* Synopsys HC */
|
||||
unsigned frame_index_bug:1; /* MosChip (AKA NetMos) */
|
||||
unsigned susp_sof_bug:1; /*Chip Idea HC*/
|
||||
unsigned reset_sof_bug:1; /*Chip Idea HC*/
|
||||
|
||||
/* required for usb32 quirk */
|
||||
#define OHCI_CTRL_HCFS (3 << 6)
|
||||
|
||||
@@ -212,6 +212,7 @@ struct hc_driver {
|
||||
#define HCD_MEMORY 0x0001 /* HC regs use memory (else I/O) */
|
||||
#define HCD_LOCAL_MEM 0x0002 /* HC needs local memory */
|
||||
#define HCD_SHARED 0x0004 /* Two (or more) usb_hcds share HW */
|
||||
#define HCD_OLD_ENUM 0x0008 /* HC supports short enumeration */
|
||||
#define HCD_USB11 0x0010 /* USB 1.1 */
|
||||
#define HCD_USB2 0x0020 /* USB 2.0 */
|
||||
#define HCD_USB3 0x0040 /* USB 3.0 */
|
||||
@@ -348,6 +349,8 @@ struct hc_driver {
|
||||
/* to log completion events*/
|
||||
void (*log_urb_complete)(struct urb *urb, char * event,
|
||||
unsigned extra);
|
||||
void (*enable_ulpi_control)(struct usb_hcd *hcd, u32 linestate);
|
||||
void (*disable_ulpi_control)(struct usb_hcd *hcd);
|
||||
};
|
||||
|
||||
extern int usb_hcd_link_urb_to_ep(struct usb_hcd *hcd, struct urb *urb);
|
||||
|
||||
Reference in New Issue
Block a user