| b.liu | e958203 | 2025-04-17 19:18:16 +0800 | [diff] [blame] | 1 | From 61d471c8da972c7ebbaf63779bf8100ee1ec54eb Mon Sep 17 00:00:00 2001 |
| 2 | From: Ran Wang <ran.wang_1@nxp.com> |
| 3 | Date: Wed, 16 Jan 2019 13:23:17 +0800 |
| 4 | Subject: [PATCH] usb: dwc3: Add workaround for host mode VBUS glitch when boot |
| 5 | |
| 6 | When DWC3 is set to host mode by programming register DWC3_GCTL, VBUS |
| 7 | (or its control signal) will be turned on immediately on related Root Hub |
| 8 | ports. Then, the VBUS is turned off for a little while(15us) when do xhci |
| 9 | reset (conducted by xhci driver) and back to normal finally, we can |
| 10 | observe a negative glitch of related signal happen. |
| 11 | |
| 12 | This VBUS glitch might cause some USB devices enumeration fail if kernel |
| 13 | boot with them connected. Such as LS1012AFWRY/LS1043ARDB/LX2160AQDS |
| 14 | /LS1088ARDB with Kingston 16GB USB2.0/Kingston USB3.0/JetFlash Transcend |
| 15 | 4GB USB2.0 drives. The fail cases include enumerated as full-speed device |
| 16 | or report wrong device descriptor, etc. |
| 17 | |
| 18 | One SW workaround which can fix this is by programing all xhci PORTSC[PP] |
| 19 | to 0 to turn off VBUS immediately after setting host mode in DWC3 driver |
| 20 | (per signal measurement result, it will be too late to do it in |
| 21 | xhci-plat.c or xhci.c). Then, after xhci reset complete in xhci driver, |
| 22 | PORTSC[PP]s' value will back to 1 automatically and VBUS on at that time, |
| 23 | no glitch happen and normal enumeration process has no impact. |
| 24 | |
| 25 | Signed-off-by: Ran Wang <ran.wang_1@nxp.com> |
| 26 | Reviewed-by: Peter Chen <peter.chen@nxp.com> |
| 27 | --- |
| 28 | drivers/usb/dwc3/core.c | 3 +++ |
| 29 | drivers/usb/dwc3/core.h | 3 +++ |
| 30 | drivers/usb/dwc3/host.c | 48 ++++++++++++++++++++++++++++++++++++++++++++++++ |
| 31 | 3 files changed, 54 insertions(+) |
| 32 | |
| 33 | --- a/drivers/usb/dwc3/core.c |
| 34 | +++ b/drivers/usb/dwc3/core.c |
| 35 | @@ -1370,6 +1370,9 @@ static void dwc3_get_properties(struct d |
| 36 | dwc->dis_split_quirk = device_property_read_bool(dev, |
| 37 | "snps,dis-split-quirk"); |
| 38 | |
| 39 | + dwc->host_vbus_glitches = device_property_read_bool(dev, |
| 40 | + "snps,host-vbus-glitches"); |
| 41 | + |
| 42 | dwc->lpm_nyet_threshold = lpm_nyet_threshold; |
| 43 | dwc->tx_de_emphasis = tx_de_emphasis; |
| 44 | |
| 45 | --- a/drivers/usb/dwc3/core.h |
| 46 | +++ b/drivers/usb/dwc3/core.h |
| 47 | @@ -1049,6 +1049,8 @@ struct dwc3_scratchpad_array { |
| 48 | * 3 - Reserved |
| 49 | * @dis_metastability_quirk: set to disable metastability quirk. |
| 50 | * @dis_split_quirk: set to disable split boundary. |
| 51 | + * @host_vbus_glitches: set to avoid vbus glitch during |
| 52 | + * xhci reset. |
| 53 | * @imod_interval: set the interrupt moderation interval in 250ns |
| 54 | * increments or 0 to disable. |
| 55 | */ |
| 56 | @@ -1245,6 +1247,8 @@ struct dwc3 { |
| 57 | |
| 58 | unsigned dis_split_quirk:1; |
| 59 | |
| 60 | + unsigned host_vbus_glitches:1; |
| 61 | + |
| 62 | u16 imod_interval; |
| 63 | }; |
| 64 | |
| 65 | --- a/drivers/usb/dwc3/host.c |
| 66 | +++ b/drivers/usb/dwc3/host.c |
| 67 | @@ -9,8 +9,48 @@ |
| 68 | |
| 69 | #include <linux/platform_device.h> |
| 70 | |
| 71 | +#include "../host/xhci.h" |
| 72 | + |
| 73 | #include "core.h" |
| 74 | |
| 75 | +#define XHCI_HCSPARAMS1 0x4 |
| 76 | +#define XHCI_PORTSC_BASE 0x400 |
| 77 | + |
| 78 | +/* |
| 79 | + * dwc3_power_off_all_roothub_ports - Power off all Root hub ports |
| 80 | + * @dwc3: Pointer to our controller context structure |
| 81 | + */ |
| 82 | +static void dwc3_power_off_all_roothub_ports(struct dwc3 *dwc) |
| 83 | +{ |
| 84 | + int i, port_num; |
| 85 | + u32 reg, op_regs_base, offset; |
| 86 | + void __iomem *xhci_regs; |
| 87 | + |
| 88 | + /* xhci regs is not mapped yet, do it temperary here */ |
| 89 | + if (dwc->xhci_resources[0].start) { |
| 90 | + xhci_regs = ioremap(dwc->xhci_resources[0].start, |
| 91 | + DWC3_XHCI_REGS_END); |
| 92 | + if (IS_ERR(xhci_regs)) { |
| 93 | + dev_err(dwc->dev, "Failed to ioremap xhci_regs\n"); |
| 94 | + return; |
| 95 | + } |
| 96 | + |
| 97 | + op_regs_base = HC_LENGTH(readl(xhci_regs)); |
| 98 | + reg = readl(xhci_regs + XHCI_HCSPARAMS1); |
| 99 | + port_num = HCS_MAX_PORTS(reg); |
| 100 | + |
| 101 | + for (i = 1; i <= port_num; i++) { |
| 102 | + offset = op_regs_base + XHCI_PORTSC_BASE + 0x10*(i-1); |
| 103 | + reg = readl(xhci_regs + offset); |
| 104 | + reg &= ~PORT_POWER; |
| 105 | + writel(reg, xhci_regs + offset); |
| 106 | + } |
| 107 | + |
| 108 | + iounmap(xhci_regs); |
| 109 | + } else |
| 110 | + dev_err(dwc->dev, "xhci base reg invalid\n"); |
| 111 | +} |
| 112 | + |
| 113 | static int dwc3_host_get_irq(struct dwc3 *dwc) |
| 114 | { |
| 115 | struct platform_device *dwc3_pdev = to_platform_device(dwc->dev); |
| 116 | @@ -50,6 +90,13 @@ int dwc3_host_init(struct dwc3 *dwc) |
| 117 | struct platform_device *dwc3_pdev = to_platform_device(dwc->dev); |
| 118 | int prop_idx = 0; |
| 119 | |
| 120 | + /* |
| 121 | + * We have to power off all Root hub ports immediately after DWC3 set |
| 122 | + * to host mode to avoid VBUS glitch happen when xhci get reset later. |
| 123 | + */ |
| 124 | + if (dwc->host_vbus_glitches) |
| 125 | + dwc3_power_off_all_roothub_ports(dwc); |
| 126 | + |
| 127 | irq = dwc3_host_get_irq(dwc); |
| 128 | if (irq < 0) |
| 129 | return irq; |