Skip to content

Commit

Permalink
[nrf fromlist] drivers: udc_dwc2: Workaround hibernation exit glitch
Browse files Browse the repository at this point in the history
DWC2 otg versions earlier than 5.00a are subject to randomly occurring
glitch on Hibernation Exit by Host Initiated Resume, Hibernation Exit by
Device Inititated Resume and Hibernation Exit by Host Initiated Reset.
When the glitch happens the device address is not correctly restored.
If the address is not correctly restored then the tokens addressed to
the device will timeout leading to host resetting the bus.

Upstream PR #: 85039

Signed-off-by: Tomasz Moń <[email protected]>
  • Loading branch information
tmon-nordic committed Feb 3, 2025
1 parent 68801c6 commit c600dc8
Show file tree
Hide file tree
Showing 2 changed files with 25 additions and 8 deletions.
3 changes: 3 additions & 0 deletions drivers/usb/common/usb_dwc2_hw.h
Original file line number Diff line number Diff line change
Expand Up @@ -338,6 +338,9 @@ USB_DWC2_SET_FIELD_DEFINE(gnptxfsiz_nptxfstaddr, GNPTXFSIZ_NPTXFSTADDR)
#define USB_DWC2_GGPIO_STM32_PWRDWN_POS 16UL
#define USB_DWC2_GGPIO_STM32_PWRDWN BIT(USB_DWC2_GGPIO_STM32_PWRDWN_POS)

/* GSNPSID register */
#define USB_DWC2_GSNPSID_REV_5_00A 0x4F54500AUL

Check notice on line 343 in drivers/usb/common/usb_dwc2_hw.h

View workflow job for this annotation

GitHub Actions / Run compliance checks on patch series (PR)

You may want to run clang-format on this change

drivers/usb/common/usb_dwc2_hw.h:343 -#define USB_DWC2_GSNPSID_REV_5_00A 0x4F54500AUL +#define USB_DWC2_GSNPSID_REV_5_00A 0x4F54500AUL
/* GHWCFG1 register */
#define USB_DWC2_GHWCFG1 0x0044UL
#define USB_DWC2_GHWCFG1_EPDIR_POS(i) (i * 2)
Expand Down
30 changes: 22 additions & 8 deletions drivers/usb/udc/udc_dwc2.c
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,8 @@ struct udc_dwc2_data {
unsigned int dynfifosizing : 1;
unsigned int bufferdma : 1;
unsigned int syncrst : 1;
/* Defect workarounds */
unsigned int wa_essregrestored : 1;
/* Runtime state flags */

Check notice on line 126 in drivers/usb/udc/udc_dwc2.c

View workflow job for this annotation

GitHub Actions / Run compliance checks on patch series (PR)

You may want to run clang-format on this change

drivers/usb/udc/udc_dwc2.c:126 - unsigned int wa_essregrestored : 1; + unsigned int wa_essregrestored: 1;
unsigned int hibernated : 1;
unsigned int enumdone : 1;
Expand Down Expand Up @@ -945,6 +947,21 @@ static void dwc2_restore_essential_registers(const struct device *dev,

pcgcctl |= USB_DWC2_PCGCCTL_ESSREGRESTORED;
sys_write32(pcgcctl, (mem_addr_t)&base->pcgcctl);

/* Note: in Remote Wakeup case 15 ms max signaling time starts now */

/* Wait for Restore Done Interrupt */
dwc2_wait_for_bit(dev, (mem_addr_t)&base->gintsts, USB_DWC2_GINTSTS_RSTRDONEINT);

if (priv->wa_essregrestored) {
pcgcctl &= ~USB_DWC2_PCGCCTL_ESSREGRESTORED;
sys_write32(pcgcctl, (mem_addr_t)&base->pcgcctl);
k_busy_wait(1);
}

if (!bus_reset) {
sys_write32(0xFFFFFFFFUL, (mem_addr_t)&base->gintsts);
}
}

static void dwc2_restore_device_registers(const struct device *dev, bool rwup)
Expand Down Expand Up @@ -1081,14 +1098,6 @@ static void dwc2_exit_hibernation(const struct device *dev,

dwc2_restore_essential_registers(dev, rwup, bus_reset);

/* Note: in Remote Wakeup case 15 ms max signaling time starts now */

/* Wait for Restore Done Interrupt */
dwc2_wait_for_bit(dev, (mem_addr_t)&base->gintsts, USB_DWC2_GINTSTS_RSTRDONEINT);
if (!bus_reset) {
sys_write32(0xFFFFFFFFUL, (mem_addr_t)&base->gintsts);
}

/* Disable restore from PMU */
sys_clear_bits(gpwrdn_reg, USB_DWC2_GPWRDN_RESTORE);
k_busy_wait(1);
Expand Down Expand Up @@ -1757,6 +1766,7 @@ static int udc_dwc2_init_controller(const struct device *dev)
mem_addr_t gahbcfg_reg = (mem_addr_t)&base->gahbcfg;
mem_addr_t gusbcfg_reg = (mem_addr_t)&base->gusbcfg;
mem_addr_t dcfg_reg = (mem_addr_t)&base->dcfg;
uint32_t gsnpsid;
uint32_t dcfg;
uint32_t gusbcfg;
uint32_t gahbcfg;
Expand All @@ -1772,6 +1782,10 @@ static int udc_dwc2_init_controller(const struct device *dev)
return ret;
}

/* Enable RTL workarounds based on controller revision */
gsnpsid = sys_read32((mem_addr_t)&base->gsnpsid);
priv->wa_essregrestored = gsnpsid < USB_DWC2_GSNPSID_REV_5_00A;

priv->ghwcfg1 = sys_read32((mem_addr_t)&base->ghwcfg1);
ghwcfg2 = sys_read32((mem_addr_t)&base->ghwcfg2);
ghwcfg3 = sys_read32((mem_addr_t)&base->ghwcfg3);
Expand Down

0 comments on commit c600dc8

Please sign in to comment.