diff options
Diffstat (limited to 'drivers/usb/dwc3/host.c')
-rw-r--r-- | drivers/usb/dwc3/host.c | 50 |
1 files changed, 50 insertions, 0 deletions
diff --git a/drivers/usb/dwc3/host.c b/drivers/usb/dwc3/host.c index 6c143f7d24..a171b27a78 100644 --- a/drivers/usb/dwc3/host.c +++ b/drivers/usb/dwc3/host.c @@ -13,9 +13,53 @@ #include <linux/usb.h> #include <linux/usb/hcd.h> +#include "../host/xhci-port.h" +#include "../host/xhci-ext-caps.h" +#include "../host/xhci-caps.h" #include "../host/xhci-plat.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 + * @dwc: Pointer to our controller context structure + */ +static void dwc3_power_off_all_roothub_ports(struct dwc3 *dwc) +{ + void __iomem *xhci_regs; + u32 op_regs_base; + int port_num; + u32 offset; + u32 reg; + int i; + + /* 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 (!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 void dwc3_xhci_plat_start(struct usb_hcd *hcd) { struct platform_device *pdev; @@ -87,6 +131,12 @@ int dwc3_host_init(struct dwc3 *dwc) int ret, irq; int prop_idx = 0; + /* + * Some platforms need to power off all Root hub ports immediately after DWC3 set to host + * mode to avoid VBUS glitch happen when xhci get reset later. + */ + dwc3_power_off_all_roothub_ports(dwc); + irq = dwc3_host_get_irq(dwc); if (irq < 0) return irq; |