mirror of
https://source.denx.de/u-boot/u-boot.git
synced 2026-06-02 09:46:37 +03:00
Merge tag 'u-boot-dfu-20260211' of https://source.denx.de/u-boot/custodians/u-boot-dfu
u-boot-dfu-20260211 USB Gadget: * dwc3: Support ip and version type * dwc3: Increase controller halt timeout * dwc3: Don't send unintended link state change * dwc3: Improve reset sequence * dwc2: Move dr_mode check to bind to support RK3288/RK3506 with 2 DWC2 controllers
This commit is contained in:
@@ -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) ==
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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 */
|
||||
|
||||
@@ -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 */
|
||||
|
||||
@@ -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),
|
||||
|
||||
Reference in New Issue
Block a user