Skip to content

Commit

Permalink
usb: dwc3: Add workaround for host mode VBUS glitch when boot
Browse files Browse the repository at this point in the history
When DWC3 is set to host mode by programming register DWC3_GCTL, VBUS
(or its control signal) will be turned on immediately on related Root Hub
ports. Then, the VBUS is turned off for a little while(15us) when do xhci
reset (conducted by xhci driver) and back to normal finally, we can
observe a negative glitch of related signal happen.

This VBUS glitch might cause some USB devices enumeration fail if kernel
boot with them connected. Such as LS1012AFWRY/LS1043ARDB/LX2160AQDS
/LS1088ARDB with Kingston 16GB USB2.0/Kingston USB3.0/JetFlash Transcend
4GB USB2.0 drives. The fail cases include enumerated as full-speed device
or report wrong device descriptor, etc.

One SW workaround which can fix this is by programing all xhci PORTSC[PP]
to 0 to turn off VBUS immediately after setting host mode in DWC3 driver
(per signal measurement result, it will be too late to do it in
xhci-plat.c or xhci.c). Then, after xhci reset complete in xhci driver,
PORTSC[PP]s' value will back to 1 automatically and VBUS on at that time,
no glitch happen and normal enumeration process has no impact.

Signed-off-by: Ran Wang <[email protected]>
Reviewed-by: Peter Chen <[email protected]>
  • Loading branch information
RanWang1 authored and Dong Aisheng committed Nov 25, 2019
1 parent bca3c35 commit b30e41d
Show file tree
Hide file tree
Showing 3 changed files with 54 additions and 0 deletions.
3 changes: 3 additions & 0 deletions drivers/usb/dwc3/core.c
Original file line number Diff line number Diff line change
Expand Up @@ -1347,6 +1347,9 @@ static void dwc3_get_properties(struct dwc3 *dwc)
dwc->dis_metastability_quirk = device_property_read_bool(dev,
"snps,dis_metastability_quirk");

dwc->host_vbus_glitches = device_property_read_bool(dev,
"snps,host-vbus-glitches");

dwc->lpm_nyet_threshold = lpm_nyet_threshold;
dwc->tx_de_emphasis = tx_de_emphasis;

Expand Down
3 changes: 3 additions & 0 deletions drivers/usb/dwc3/core.h
Original file line number Diff line number Diff line change
Expand Up @@ -1032,6 +1032,8 @@ struct dwc3_scratchpad_array {
* 2 - No de-emphasis
* 3 - Reserved
* @dis_metastability_quirk: set to disable metastability quirk.
* @host_vbus_glitches: set to avoid vbus glitch during
* xhci reset.
* @imod_interval: set the interrupt moderation interval in 250ns
* increments or 0 to disable.
*/
Expand Down Expand Up @@ -1223,6 +1225,7 @@ struct dwc3 {
unsigned tx_de_emphasis:2;

unsigned dis_metastability_quirk:1;
unsigned host_vbus_glitches:1;

u16 imod_interval;
};
Expand Down
48 changes: 48 additions & 0 deletions drivers/usb/dwc3/host.c
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,49 @@

#include <linux/platform_device.h>

#include "../host/xhci.h"

#include "core.h"


#define XHCI_HCSPARAMS1 0x4
#define XHCI_PORTSC_BASE 0x400

/*
* dwc3_power_off_all_roothub_ports - Power off all Root hub ports
* @dwc3: Pointer to our controller context structure
*/
static void dwc3_power_off_all_roothub_ports(struct dwc3 *dwc)
{
int i, port_num;
u32 reg, op_regs_base, offset;
void __iomem *xhci_regs;

/* xhci regs is not mapped yet, do it temperary here */
if (dwc->xhci_resources[0].start) {
xhci_regs = ioremap(dwc->xhci_resources[0].start,
DWC3_XHCI_REGS_END);
if (IS_ERR(xhci_regs)) {
dev_err(dwc->dev, "Failed to ioremap xhci_regs\n");
return;
}

op_regs_base = HC_LENGTH(readl(xhci_regs));
reg = readl(xhci_regs + XHCI_HCSPARAMS1);
port_num = HCS_MAX_PORTS(reg);

for (i = 1; i <= port_num; i++) {
offset = op_regs_base + XHCI_PORTSC_BASE + 0x10*(i-1);
reg = readl(xhci_regs + offset);
reg &= ~PORT_POWER;
writel(reg, xhci_regs + offset);
}

iounmap(xhci_regs);
} else
dev_err(dwc->dev, "xhci base reg invalid\n");
}

static int dwc3_host_get_irq(struct dwc3 *dwc)
{
struct platform_device *dwc3_pdev = to_platform_device(dwc->dev);
Expand Down Expand Up @@ -50,6 +91,13 @@ int dwc3_host_init(struct dwc3 *dwc)
struct platform_device *dwc3_pdev = to_platform_device(dwc->dev);
int prop_idx = 0;

/*
* We have to power off all Root hub ports immediately after DWC3 set
* to host mode to avoid VBUS glitch happen when xhci get reset later.
*/
if (dwc->host_vbus_glitches)
dwc3_power_off_all_roothub_ports(dwc);

irq = dwc3_host_get_irq(dwc);
if (irq < 0)
return irq;
Expand Down

0 comments on commit b30e41d

Please sign in to comment.