From c600dc8ac99c0e34829708f10762cff3d28581cb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Mo=C5=84?= Date: Fri, 15 Nov 2024 12:31:27 +0100 Subject: [PATCH] [nrf fromlist] drivers: udc_dwc2: Workaround hibernation exit glitch MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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ń --- drivers/usb/common/usb_dwc2_hw.h | 3 +++ drivers/usb/udc/udc_dwc2.c | 30 ++++++++++++++++++++++-------- 2 files changed, 25 insertions(+), 8 deletions(-) diff --git a/drivers/usb/common/usb_dwc2_hw.h b/drivers/usb/common/usb_dwc2_hw.h index d5e272c8752..fcd0d2a0060 100644 --- a/drivers/usb/common/usb_dwc2_hw.h +++ b/drivers/usb/common/usb_dwc2_hw.h @@ -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 + /* GHWCFG1 register */ #define USB_DWC2_GHWCFG1 0x0044UL #define USB_DWC2_GHWCFG1_EPDIR_POS(i) (i * 2) diff --git a/drivers/usb/udc/udc_dwc2.c b/drivers/usb/udc/udc_dwc2.c index 072fdbd2e13..01c0f3040ce 100644 --- a/drivers/usb/udc/udc_dwc2.c +++ b/drivers/usb/udc/udc_dwc2.c @@ -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 */ unsigned int hibernated : 1; unsigned int enumdone : 1; @@ -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) @@ -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); @@ -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; @@ -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);