diff options
| author | Tom Rini <[email protected]> | 2026-02-23 13:45:55 -0600 |
|---|---|---|
| committer | Tom Rini <[email protected]> | 2026-02-23 13:45:55 -0600 |
| commit | 15bd3258174b8c8791d7779f66d26ece9545b1c0 (patch) | |
| tree | 634188f66b83e1554ffea95c4702df25b61ce20e /drivers/usb | |
| parent | ef65b15383c99f4562539a635fe166fb4d75c0e3 (diff) | |
| parent | 4f70106beafe30df8b0528a3d8b2543cd48fe241 (diff) | |
Merge tag 'v2026.04-rc3' into next
Prepare v2026.04-rc3
Diffstat (limited to 'drivers/usb')
| -rw-r--r-- | drivers/usb/cdns3/core.c | 53 | ||||
| -rw-r--r-- | drivers/usb/cdns3/drd.c | 11 | ||||
| -rw-r--r-- | drivers/usb/dwc3/core.c | 92 | ||||
| -rw-r--r-- | drivers/usb/dwc3/core.h | 60 | ||||
| -rw-r--r-- | drivers/usb/dwc3/gadget.c | 20 | ||||
| -rw-r--r-- | drivers/usb/dwc3/gadget.h | 14 | ||||
| -rw-r--r-- | drivers/usb/gadget/dwc2_udc_otg.c | 19 |
7 files changed, 220 insertions, 49 deletions
diff --git a/drivers/usb/cdns3/core.c b/drivers/usb/cdns3/core.c index 4434dc15bec..10bc4cabed4 100644 --- a/drivers/usb/cdns3/core.c +++ b/drivers/usb/cdns3/core.c @@ -392,6 +392,52 @@ static const struct udevice_id cdns3_ids[] = { { }, }; +/* + * The VBUS Valid Bit in the OTG Status register can be used to determine + * the role. When VBUS Valid is set, it indicates that a USB Host is supplying + * power, so the Controller should assume the PERIPHERAL role. If it isn't set, + * it indicates the absence of a USB Host, so the Controller should assume the + * HOST role. If the OTG Status register is inaccessible, return an error. + */ +static int cdns3_get_otg_mode(struct udevice *parent, enum usb_dr_mode *mode) +{ + /* Create a temporary child device for using devfdt_remap_addr_name() */ + struct udevice child = { + .parent = parent, + }; + struct cdns3 cdns, *cdnsp; + void __iomem *otg_regs; + + dev_set_ofnode(&child, ofnode_first_subnode(dev_ofnode(parent))); + otg_regs = devfdt_remap_addr_name(&child, "otg"); + if (!otg_regs) { + dev_err(parent, "failed to get otg registers for child node\n"); + return -ENXIO; + } + + /* + * As mentioned in drivers/usb/cdns3/drd.c, there are two versions + * of the Controller. The following logic detects the version of the + * Controller and interprets the register layout accordingly. + */ + cdnsp = &cdns; + cdnsp->otg_v0_regs = otg_regs; + if (!readl(&cdnsp->otg_v0_regs->cmd)) { + cdnsp->otg_regs = otg_regs; + } else { + cdnsp->otg_v1_regs = otg_regs; + cdnsp->otg_regs = (void *)&cdnsp->otg_v1_regs->cmd; + } + + /* Use VBUS Valid to determine role */ + if (readl(&cdnsp->otg_regs->sts) & OTGSTS_VBUS_VALID) + *mode = USB_DR_MODE_PERIPHERAL; + else + *mode = USB_DR_MODE_HOST; + + return 0; +} + int cdns3_bind(struct udevice *parent) { enum usb_dr_mode dr_mode; @@ -413,6 +459,13 @@ int cdns3_bind(struct udevice *parent) if (dr_mode == USB_DR_MODE_UNKNOWN) dr_mode = usb_get_dr_mode(dev_ofnode(parent)); + /* Use VBUS Valid to determine role */ + if (dr_mode == USB_DR_MODE_OTG) { + ret = cdns3_get_otg_mode(parent, &dr_mode); + if (ret < 0) + return ret; + } + switch (dr_mode) { #if defined(CONFIG_SPL_USB_HOST) || \ (!defined(CONFIG_XPL_BUILD) && defined(CONFIG_USB_HOST)) diff --git a/drivers/usb/cdns3/drd.c b/drivers/usb/cdns3/drd.c index cbb13342343..0ca40a5cc8d 100644 --- a/drivers/usb/cdns3/drd.c +++ b/drivers/usb/cdns3/drd.c @@ -301,6 +301,17 @@ int cdns3_drd_init(struct cdns3 *cdns) cdns->dr_mode = USB_DR_MODE_PERIPHERAL; } + /* + * In the absence of STRAP configuration, use VBUS Valid to + * determine the appropriate role to be assigned to dr_mode. + */ + if (cdns->dr_mode == USB_DR_MODE_OTG) { + if (cdns3_get_vbus(cdns)) + cdns->dr_mode = USB_DR_MODE_PERIPHERAL; + else + cdns->dr_mode = USB_DR_MODE_HOST; + } + state = readl(&cdns->otg_regs->sts); if (OTGSTS_OTG_NRDY(state) != 0) { dev_err(cdns->dev, "Cadence USB3 OTG device not ready\n"); diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c index 847fa1f82c3..6f22b9232ba 100644 --- a/drivers/usb/dwc3/core.c +++ b/drivers/usb/dwc3/core.c @@ -59,40 +59,52 @@ static void dwc3_set_mode(struct dwc3 *dwc, u32 mode) static int dwc3_core_soft_reset(struct dwc3 *dwc) { u32 reg; + int retries = 1000; - /* Before Resetting PHY, put Core in Reset */ - reg = dwc3_readl(dwc->regs, DWC3_GCTL); - reg |= DWC3_GCTL_CORESOFTRESET; - dwc3_writel(dwc->regs, DWC3_GCTL, reg); + /* + * We're resetting only the device side because, if we're in host mode, + * XHCI driver will reset the host block. If dwc3 was configured for + * host-only mode, then we can return early. + */ + if (dwc->dr_mode == USB_DR_MODE_HOST) + return 0; - /* Assert USB3 PHY reset */ - reg = dwc3_readl(dwc->regs, DWC3_GUSB3PIPECTL(0)); - reg |= DWC3_GUSB3PIPECTL_PHYSOFTRST; - dwc3_writel(dwc->regs, DWC3_GUSB3PIPECTL(0), reg); + reg = dwc3_readl(dwc->regs, DWC3_DCTL); + reg |= DWC3_DCTL_CSFTRST; + reg &= ~DWC3_DCTL_RUN_STOP; + dwc3_gadget_dctl_write_safe(dwc, reg); - /* Assert USB2 PHY reset */ - reg = dwc3_readl(dwc->regs, DWC3_GUSB2PHYCFG(0)); - reg |= DWC3_GUSB2PHYCFG_PHYSOFTRST; - dwc3_writel(dwc->regs, DWC3_GUSB2PHYCFG(0), reg); + /* + * For DWC_usb31 controller 1.90a and later, the DCTL.CSFRST bit + * is cleared only after all the clocks are synchronized. This can + * take a little more than 50ms. Set the polling rate at 20ms + * for 10 times instead. + */ + if (DWC3_VER_IS_WITHIN(DWC31, 190A, ANY) || DWC3_IP_IS(DWC32)) + retries = 10; - mdelay(100); + do { + reg = dwc3_readl(dwc->regs, DWC3_DCTL); + if (!(reg & DWC3_DCTL_CSFTRST)) + goto done; - /* Clear USB3 PHY reset */ - reg = dwc3_readl(dwc->regs, DWC3_GUSB3PIPECTL(0)); - reg &= ~DWC3_GUSB3PIPECTL_PHYSOFTRST; - dwc3_writel(dwc->regs, DWC3_GUSB3PIPECTL(0), reg); + if (DWC3_VER_IS_WITHIN(DWC31, 190A, ANY) || DWC3_IP_IS(DWC32)) + mdelay(20); + else + udelay(1); + } while (--retries); - /* Clear USB2 PHY reset */ - reg = dwc3_readl(dwc->regs, DWC3_GUSB2PHYCFG(0)); - reg &= ~DWC3_GUSB2PHYCFG_PHYSOFTRST; - dwc3_writel(dwc->regs, DWC3_GUSB2PHYCFG(0), reg); + dev_warn(dwc->dev, "DWC3 controller soft reset failed.\n"); + return -ETIMEDOUT; - mdelay(100); - - /* After PHYs are stable we can take Core out of reset state */ - reg = dwc3_readl(dwc->regs, DWC3_GCTL); - reg &= ~DWC3_GCTL_CORESOFTRESET; - dwc3_writel(dwc->regs, DWC3_GCTL, reg); +done: + /* + * For DWC_usb31 controller 1.80a and prior, once DCTL.CSFRST bit + * is cleared, we must wait at least 50ms before accessing the PHY + * domain (synchronization delay). + */ + if (DWC3_VER_IS_WITHIN(DWC31, ANY, 180A)) + mdelay(50); return 0; } @@ -580,6 +592,26 @@ static void dwc3_set_incr_burst_type(struct dwc3 *dwc) dwc3_writel(dwc->regs, DWC3_GSBUSCFG0, cfg); } +static bool dwc3_core_is_valid(struct dwc3 *dwc) +{ + u32 reg; + + reg = dwc3_readl(dwc->regs, DWC3_GSNPSID); + dwc->ip = DWC3_GSNPS_ID(reg); + + /* This should read as U3 followed by revision number */ + if (DWC3_IP_IS(DWC3)) { + dwc->revision = reg; + } else if (DWC3_IP_IS(DWC31) || DWC3_IP_IS(DWC32)) { + dwc->revision = dwc3_readl(dwc->regs, DWC3_VER_NUMBER); + dwc->version_type = dwc3_readl(dwc->regs, DWC3_VER_TYPE); + } else { + return false; + } + + return true; +} + /** * dwc3_core_init - Low-level initialization of DWC3 Core * @dwc: Pointer to our controller context structure @@ -592,15 +624,11 @@ static int dwc3_core_init(struct dwc3 *dwc) u32 reg; int ret; - reg = dwc3_readl(dwc->regs, DWC3_GSNPSID); - /* This should read as U3 followed by revision number */ - if ((reg & DWC3_GSNPSID_MASK) != 0x55330000 && - (reg & DWC3_GSNPSID_MASK) != 0x33310000) { + if (!dwc3_core_is_valid(dwc)) { dev_err(dwc->dev, "this is not a DesignWare USB3 DRD Core\n"); ret = -ENODEV; goto err0; } - dwc->revision = reg; /* Handle USB2.0-only core configuration */ if (DWC3_GHWPARAMS3_SSPHY_IFC(dwc->hwparams.hwparams3) == diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h index b572ea340c8..cdbfdce76bb 100644 --- a/drivers/usb/dwc3/core.h +++ b/drivers/usb/dwc3/core.h @@ -56,6 +56,7 @@ #define DWC3_GEVNTCOUNT_MASK 0xfffc #define DWC3_GSNPSID_MASK 0xffff0000 #define DWC3_GSNPSREV_MASK 0xffff +#define DWC3_GSNPS_ID(p) (((p) & DWC3_GSNPSID_MASK) >> 16) /* DWC3 registers memory space boundries */ #define DWC3_XHCI_REGS_START 0x0 @@ -99,6 +100,9 @@ #define DWC3_GPRTBIMAP_FS0 0xc188 #define DWC3_GPRTBIMAP_FS1 0xc18c +#define DWC3_VER_NUMBER 0xc1a0 +#define DWC3_VER_TYPE 0xc1a4 + #define DWC3_GUSB2PHYCFG(n) (0xc200 + (n * 0x04)) #define DWC3_GUSB2I2CCTL(n) (0xc240 + (n * 0x04)) @@ -686,7 +690,9 @@ struct dwc3_scratchpad_array { * @num_event_buffers: calculated number of event buffers * @u1u2: only used on revisions <1.83a for workaround * @maximum_speed: maximum speed requested (mainly for testing purposes) + * @ip: controller's ID * @revision: revision register contents + * @version_type: VERSIONTYPE register contents, a sub release of a revision * @dr_mode: requested mode of operation * @hsphy_mode: UTMI phy mode, one of following: * - USBPHY_INTERFACE_MODE_UTMI @@ -795,6 +801,13 @@ struct dwc3 { u32 num_event_buffers; u32 u1u2; u32 maximum_speed; + + u32 ip; + +#define DWC3_IP 0x5533 +#define DWC31_IP 0x3331 +#define DWC32_IP 0x3332 + u32 revision; #define DWC3_REVISION_173A 0x5533173a @@ -817,6 +830,32 @@ struct dwc3 { #define DWC3_REVISION_270A 0x5533270a #define DWC3_REVISION_280A 0x5533280a #define DWC3_REVISION_290A 0x5533290a +#define DWC3_REVISION_300A 0x5533300a +#define DWC3_REVISION_310A 0x5533310a +#define DWC3_REVISION_320A 0x5533320a +#define DWC3_REVISION_330A 0x5533330a + +#define DWC31_REVISION_ANY 0x0 +#define DWC31_REVISION_110A 0x3131302a +#define DWC31_REVISION_120A 0x3132302a +#define DWC31_REVISION_160A 0x3136302a +#define DWC31_REVISION_170A 0x3137302a +#define DWC31_REVISION_180A 0x3138302a +#define DWC31_REVISION_190A 0x3139302a +#define DWC31_REVISION_200A 0x3230302a + +#define DWC32_REVISION_ANY 0x0 +#define DWC32_REVISION_100A 0x3130302a + + u32 version_type; + +#define DWC31_VERSIONTYPE_ANY 0x0 +#define DWC31_VERSIONTYPE_EA01 0x65613031 +#define DWC31_VERSIONTYPE_EA02 0x65613032 +#define DWC31_VERSIONTYPE_EA03 0x65613033 +#define DWC31_VERSIONTYPE_EA04 0x65613034 +#define DWC31_VERSIONTYPE_EA05 0x65613035 +#define DWC31_VERSIONTYPE_EA06 0x65613036 enum dwc3_ep0_next ep0_next_event; enum dwc3_ep0_state ep0state; @@ -1062,6 +1101,27 @@ void dwc3_of_parse(struct dwc3 *dwc); int dwc3_init(struct dwc3 *dwc); void dwc3_remove(struct dwc3 *dwc); +#define DWC3_IP_IS(_ip) \ + (dwc->ip == _ip##_IP) + +#define DWC3_VER_IS(_ip, _ver) \ + (DWC3_IP_IS(_ip) && dwc->revision == _ip##_REVISION_##_ver) + +#define DWC3_VER_IS_PRIOR(_ip, _ver) \ + (DWC3_IP_IS(_ip) && dwc->revision < _ip##_REVISION_##_ver) + +#define DWC3_VER_IS_WITHIN(_ip, _from, _to) \ + (DWC3_IP_IS(_ip) && \ + dwc->revision >= _ip##_REVISION_##_from && \ + (!(_ip##_REVISION_##_to) || \ + dwc->revision <= _ip##_REVISION_##_to)) + +#define DWC3_VER_TYPE_IS_WITHIN(_ip, _ver, _from, _to) \ + (DWC3_VER_IS(_ip, _ver) && \ + dwc->version_type >= _ip##_VERSIONTYPE_##_from && \ + (!(_ip##_VERSIONTYPE_##_to) || \ + dwc->version_type <= _ip##_VERSIONTYPE_##_to)) + static inline int dwc3_host_init(struct dwc3 *dwc) { return 0; } static inline void dwc3_host_exit(struct dwc3 *dwc) diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index 2b01113d54c..24ae0c232f6 100644 --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c @@ -62,7 +62,7 @@ int dwc3_gadget_set_test_mode(struct dwc3 *dwc, int mode) return -EINVAL; } - dwc3_writel(dwc->regs, DWC3_DCTL, reg); + dwc3_gadget_dctl_write_safe(dwc, reg); return 0; } @@ -1396,7 +1396,7 @@ static int dwc3_gadget_set_selfpowered(struct usb_gadget *g, static int dwc3_gadget_run_stop(struct dwc3 *dwc, int is_on, int suspend) { u32 reg; - u32 timeout = 500; + u32 timeout = 2000; reg = dwc3_readl(dwc->regs, DWC3_DCTL); if (is_on) { @@ -1422,9 +1422,10 @@ static int dwc3_gadget_run_stop(struct dwc3 *dwc, int is_on, int suspend) dwc->pullups_connected = false; } - dwc3_writel(dwc->regs, DWC3_DCTL, reg); + dwc3_gadget_dctl_write_safe(dwc, reg); do { + udelay(2000); reg = dwc3_readl(dwc->regs, DWC3_DSTS); if (is_on) { if (!(reg & DWC3_DSTS_DEVCTRLHLT)) @@ -1436,7 +1437,6 @@ static int dwc3_gadget_run_stop(struct dwc3 *dwc, int is_on, int suspend) timeout--; if (!timeout) return -ETIMEDOUT; - udelay(1); } while (1); dev_vdbg(dwc->dev, "gadget %s data soft-%s\n", @@ -2137,10 +2137,8 @@ static void dwc3_gadget_disconnect_interrupt(struct dwc3 *dwc) reg = dwc3_readl(dwc->regs, DWC3_DCTL); reg &= ~DWC3_DCTL_INITU1ENA; - dwc3_writel(dwc->regs, DWC3_DCTL, reg); - reg &= ~DWC3_DCTL_INITU2ENA; - dwc3_writel(dwc->regs, DWC3_DCTL, reg); + dwc3_gadget_dctl_write_safe(dwc, reg); dwc3_disconnect_gadget(dwc); dwc->start_config_issued = false; @@ -2189,7 +2187,7 @@ static void dwc3_gadget_reset_interrupt(struct dwc3 *dwc) reg = dwc3_readl(dwc->regs, DWC3_DCTL); reg &= ~DWC3_DCTL_TSTCTRL_MASK; - dwc3_writel(dwc->regs, DWC3_DCTL, reg); + dwc3_gadget_dctl_write_safe(dwc, reg); dwc->test_mode = false; dwc3_stop_active_transfers(dwc); @@ -2305,11 +2303,11 @@ static void dwc3_gadget_conndone_interrupt(struct dwc3 *dwc) if (dwc->has_lpm_erratum && dwc->revision >= DWC3_REVISION_240A) reg |= DWC3_DCTL_LPM_ERRATA(dwc->lpm_nyet_threshold); - dwc3_writel(dwc->regs, DWC3_DCTL, reg); + dwc3_gadget_dctl_write_safe(dwc, reg); } else { reg = dwc3_readl(dwc->regs, DWC3_DCTL); reg &= ~DWC3_DCTL_HIRD_THRES_MASK; - dwc3_writel(dwc->regs, DWC3_DCTL, reg); + dwc3_gadget_dctl_write_safe(dwc, reg); } dep = dwc->eps[0]; @@ -2417,7 +2415,7 @@ static void dwc3_gadget_linksts_change_interrupt(struct dwc3 *dwc, reg &= ~u1u2; - dwc3_writel(dwc->regs, DWC3_DCTL, reg); + dwc3_gadget_dctl_write_safe(dwc, reg); break; default: /* do nothing */ diff --git a/drivers/usb/dwc3/gadget.h b/drivers/usb/dwc3/gadget.h index f28a9755dcb..e4f5a096956 100644 --- a/drivers/usb/dwc3/gadget.h +++ b/drivers/usb/dwc3/gadget.h @@ -104,4 +104,18 @@ static inline u32 dwc3_gadget_ep_get_transfer_index(struct dwc3 *dwc, u8 number) return DWC3_DEPCMD_GET_RSC_IDX(res_id); } +/** + * dwc3_gadget_dctl_write_safe - write to DCTL safe from link state change + * @dwc: pointer to our context structure + * @value: value to write to DCTL + * + * Use this function when doing read-modify-write to DCTL. It will not + * send link state change request. + */ +static inline void dwc3_gadget_dctl_write_safe(struct dwc3 *dwc, u32 value) +{ + value &= ~DWC3_DCTL_ULSTCHNGREQ_MASK; + dwc3_writel(dwc->regs, DWC3_DCTL, value); +} + #endif /* __DRIVERS_USB_DWC3_GADGET_H */ diff --git a/drivers/usb/gadget/dwc2_udc_otg.c b/drivers/usb/gadget/dwc2_udc_otg.c index 40393141ca9..e475b14b9ac 100644 --- a/drivers/usb/gadget/dwc2_udc_otg.c +++ b/drivers/usb/gadget/dwc2_udc_otg.c @@ -975,12 +975,6 @@ static int dwc2_udc_otg_of_to_plat(struct udevice *dev) void (*set_params)(struct dwc2_plat_otg_data *data); int ret; - if (usb_get_dr_mode(dev_ofnode(dev)) != USB_DR_MODE_PERIPHERAL && - usb_get_dr_mode(dev_ofnode(dev)) != USB_DR_MODE_OTG) { - dev_dbg(dev, "Invalid mode\n"); - return -ENODEV; - } - plat->regs_otg = dev_read_addr(dev); plat->rx_fifo_sz = dev_read_u32_default(dev, "g-rx-fifo-size", 0); @@ -1163,6 +1157,18 @@ static int dwc2_udc_otg_remove(struct udevice *dev) return dm_scan_fdt_dev(dev); } +static int dwc2_udc_otg_bind(struct udevice *dev) +{ + enum usb_dr_mode dr_mode = usb_get_dr_mode(dev_ofnode(dev)); + + if (dr_mode != USB_DR_MODE_PERIPHERAL && dr_mode != USB_DR_MODE_OTG) { + dev_dbg(dev, "Invalid dr_mode %d\n", dr_mode); + return -ENODEV; + } + + return 0; +} + static int dwc2_gadget_handle_interrupts(struct udevice *dev) { return dwc2_udc_handle_interrupt(); @@ -1186,6 +1192,7 @@ U_BOOT_DRIVER(dwc2_udc_otg) = { .of_match = dwc2_udc_otg_ids, .ops = &dwc2_gadget_ops, .of_to_plat = dwc2_udc_otg_of_to_plat, + .bind = dwc2_udc_otg_bind, .probe = dwc2_udc_otg_probe, .remove = dwc2_udc_otg_remove, .plat_auto = sizeof(struct dwc2_plat_otg_data), |
