From c5d685b8993cf9e2fa4321dc3f9f866d5655e976 Mon Sep 17 00:00:00 2001 From: Kongyang Liu Date: Fri, 10 Jan 2025 21:55:26 +0800 Subject: [PATCH] usb: dwc2: Unify flush and reset logic with v4.20a support This patch merges flush and reset logic for both host and gadget code into a common set of functions, reducing duplication. It also adds support for the updated reset logic to compatible with core version since v4.20a. This patch mainly refers to the patch in the kernel. link: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=65dc2e725286106f99c6f6b78e3d9c52c15f3a9c Signed-off-by: Kongyang Liu Tested-by: Peter Robinson Signed-off-by: Junhui Liu Reviewed-by: Mattijs Korpershoek Reviewed-by: Marek Vasut Link: https://lore.kernel.org/r/20250110-dwc2-dev-v4-7-987f4fd6f8b2@pigmoral.tech Signed-off-by: Mattijs Korpershoek --- drivers/usb/common/Makefile | 2 + drivers/usb/common/dwc2_core.c | 131 +++++++++++++++++++++ drivers/usb/common/dwc2_core.h | 4 + drivers/usb/gadget/dwc2_udc_otg.c | 12 +- drivers/usb/gadget/dwc2_udc_otg_xfer_dma.c | 6 +- drivers/usb/host/dwc2.c | 80 +------------ 6 files changed, 145 insertions(+), 90 deletions(-) create mode 100644 drivers/usb/common/dwc2_core.c diff --git a/drivers/usb/common/Makefile b/drivers/usb/common/Makefile index 4c597c166c6..db8f35c10c4 100644 --- a/drivers/usb/common/Makefile +++ b/drivers/usb/common/Makefile @@ -4,6 +4,8 @@ # obj-$(CONFIG_$(PHASE_)DM_USB) += common.o +obj-$(CONFIG_USB_DWC2) += dwc2_core.o +obj-$(CONFIG_USB_GADGET_DWC2_OTG) += dwc2_core.o obj-$(CONFIG_USB_ISP1760) += usb_urb.o obj-$(CONFIG_USB_MUSB_HOST) += usb_urb.o obj-$(CONFIG_USB_MUSB_GADGET) += usb_urb.o diff --git a/drivers/usb/common/dwc2_core.c b/drivers/usb/common/dwc2_core.c new file mode 100644 index 00000000000..63062d5cc94 --- /dev/null +++ b/drivers/usb/common/dwc2_core.c @@ -0,0 +1,131 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) 2024-2025, Kongyang Liu + */ + +#include +#include +#include +#include + +#include "dwc2_core.h" + +int dwc2_core_reset(struct dwc2_core_regs *regs) +{ + u32 snpsid; + int ret; + bool host_mode = false; + + if (!(readl(®s->global_regs.gotgctl) & GOTGCTL_CONID_B) || + (readl(®s->global_regs.gusbcfg) & GUSBCFG_FORCEDEVMODE)) + host_mode = true; + + /* Core Soft Reset */ + snpsid = readl(®s->global_regs.gsnpsid); + writel(GRSTCTL_CSFTRST, ®s->global_regs.grstctl); + if (FIELD_GET(GSNPSID_VER_MASK, snpsid) < 0x420a) { + ret = wait_for_bit_le32(®s->global_regs.grstctl, GRSTCTL_CSFTRST, + false, 1000, false); + if (ret) { + log_warning("%s: Waiting for GRSTCTL_CSFTRST timeout\n", __func__); + return ret; + } + } else { + ret = wait_for_bit_le32(®s->global_regs.grstctl, GRSTCTL_CSFTRST_DONE, + true, 1000, false); + if (ret) { + log_warning("%s: Waiting for GRSTCTL_CSFTRST_DONE timeout\n", __func__); + return ret; + } + clrsetbits_le32(®s->global_regs.grstctl, GRSTCTL_CSFTRST, GRSTCTL_CSFTRST_DONE); + } + + /* Wait for AHB master IDLE state. */ + ret = wait_for_bit_le32(®s->global_regs.grstctl, GRSTCTL_AHBIDLE, + true, 1000, false); + if (ret) { + log_warning("%s: Waiting for GRSTCTL_AHBIDLE timeout\n", __func__); + return ret; + } + + if (host_mode) { + ret = wait_for_bit_le32(®s->global_regs.gintsts, GINTSTS_CURMODE_HOST, + host_mode, 1000, false); + if (ret) { + log_warning("%s: Waiting for GINTSTS_CURMODE_HOST timeout\n", __func__); + return ret; + } + } + + return 0; +} + +int dwc2_flush_tx_fifo(struct dwc2_core_regs *regs, const int num) +{ + int ret; + + log_debug("Flush Tx FIFO %d\n", num); + + /* Wait for AHB master IDLE state */ + ret = wait_for_bit_le32(®s->global_regs.grstctl, GRSTCTL_AHBIDLE, true, 1000, false); + if (ret) { + log_warning("%s: Waiting for GRSTCTL_AHBIDLE timeout\n", __func__); + return ret; + } + + writel(GRSTCTL_TXFFLSH | FIELD_PREP(GRSTCTL_TXFNUM_MASK, num), ®s->global_regs.grstctl); + + ret = wait_for_bit_le32(®s->global_regs.grstctl, GRSTCTL_TXFFLSH, false, 1000, false); + if (ret) { + log_warning("%s: Waiting for GRSTCTL_TXFFLSH timeout\n", __func__); + return ret; + } + + /* + * Wait for at least 3 PHY clocks. + * + * The PHY clock frequency can be configured to 6/30/48/60 MHz + * based on the speed mode. A fixed delay of 1us ensures that the + * wait time is sufficient even at the lowest PHY clock frequency + * (6 MHz), where 1us corresponds to twice the duration of 3 PHY + * clocks. + */ + udelay(1); + + return 0; +} + +int dwc2_flush_rx_fifo(struct dwc2_core_regs *regs) +{ + int ret; + + log_debug("Flush Rx FIFO\n"); + + /* Wait for AHB master IDLE state */ + ret = wait_for_bit_le32(®s->global_regs.grstctl, GRSTCTL_AHBIDLE, true, 1000, false); + if (ret) { + log_warning("%s: Waiting for GRSTCTL_AHBIDLE timeout\n", __func__); + return ret; + } + + writel(GRSTCTL_RXFFLSH, ®s->global_regs.grstctl); + + ret = wait_for_bit_le32(®s->global_regs.grstctl, GRSTCTL_RXFFLSH, false, 1000, false); + if (ret) { + log_warning("%s: Waiting for GRSTCTL_RXFFLSH timeout\n", __func__); + return ret; + } + + /* + * Wait for at least 3 PHY clocks. + * + * The PHY clock frequency can be configured to 6/30/48/60 MHz + * based on the speed mode. A fixed delay of 1us ensures that the + * wait time is sufficient even at the lowest PHY clock frequency + * (6 MHz), where 1us corresponds to twice the duration of 3 PHY + * clocks. + */ + udelay(1); + + return 0; +} diff --git a/drivers/usb/common/dwc2_core.h b/drivers/usb/common/dwc2_core.h index 862d3b3691c..1897ad7cb54 100644 --- a/drivers/usb/common/dwc2_core.h +++ b/drivers/usb/common/dwc2_core.h @@ -125,6 +125,10 @@ struct dwc2_core_regs { u8 ep_fifo[16][0x1000]; /* 0x1000 */ }; +int dwc2_core_reset(struct dwc2_core_regs *regs); +int dwc2_flush_tx_fifo(struct dwc2_core_regs *regs, const int num); +int dwc2_flush_rx_fifo(struct dwc2_core_regs *regs); + /* Core Global Register */ #define GOTGCTL_CHIRPEN BIT(27) #define GOTGCTL_MULT_VALID_BC_MASK GENMASK(26, 22) diff --git a/drivers/usb/gadget/dwc2_udc_otg.c b/drivers/usb/gadget/dwc2_udc_otg.c index b08ea5ba79a..084e9824faa 100644 --- a/drivers/usb/gadget/dwc2_udc_otg.c +++ b/drivers/usb/gadget/dwc2_udc_otg.c @@ -471,7 +471,7 @@ static void reconfig_usbd(struct dwc2_udc *dev) u32 max_hw_ep; int pdata_hw_ep; - writel(GRSTCTL_CSFTRST, ®->global_regs.grstctl); + dwc2_core_reset(reg); debug("Resetting OTG controller\n"); @@ -575,16 +575,10 @@ static void reconfig_usbd(struct dwc2_udc *dev) ®->global_regs.dptxfsizn[i]); } /* Flush the RX FIFO */ - writel(GRSTCTL_RXFFLSH, ®->global_regs.grstctl); - while (readl(®->global_regs.grstctl) & GRSTCTL_RXFFLSH) - debug("%s: waiting for DWC2_UDC_OTG_GRSTCTL\n", __func__); + dwc2_flush_rx_fifo(reg); /* Flush all the Tx FIFO's */ - writel(FIELD_PREP(GRSTCTL_TXFNUM_MASK, GRSTCTL_TXFNUM_ALL), ®->global_regs.grstctl); - writel(FIELD_PREP(GRSTCTL_TXFNUM_MASK, GRSTCTL_TXFNUM_ALL) | GRSTCTL_TXFFLSH, - ®->global_regs.grstctl); - while (readl(®->global_regs.grstctl) & GRSTCTL_TXFFLSH) - debug("%s: waiting for DWC2_UDC_OTG_GRSTCTL\n", __func__); + dwc2_flush_tx_fifo(reg, GRSTCTL_TXFNUM_ALL); /* 13. Clear NAK bit of EP0, EP1, EP2*/ /* For Slave mode*/ diff --git a/drivers/usb/gadget/dwc2_udc_otg_xfer_dma.c b/drivers/usb/gadget/dwc2_udc_otg_xfer_dma.c index 64d2fe7bbde..2be93592c42 100644 --- a/drivers/usb/gadget/dwc2_udc_otg_xfer_dma.c +++ b/drivers/usb/gadget/dwc2_udc_otg_xfer_dma.c @@ -164,11 +164,7 @@ static int setdma_tx(struct dwc2_ep *ep, struct dwc2_request *req) pktcnt = (length - 1)/(ep->ep.maxpacket) + 1; /* Flush the endpoint's Tx FIFO */ - writel(FIELD_PREP(GRSTCTL_TXFNUM_MASK, ep->fifo_num), ®->global_regs.grstctl); - writel(FIELD_PREP(GRSTCTL_TXFNUM_MASK, ep->fifo_num) | GRSTCTL_TXFFLSH, - ®->global_regs.grstctl); - while (readl(®->global_regs.grstctl) & GRSTCTL_TXFFLSH) - ; + dwc2_flush_tx_fifo(reg, ep->fifo_num); writel(phys_to_bus((unsigned long)ep->dma_buf), ®->device_regs.in_endp[ep_num].diepdma); writel(FIELD_PREP(DXEPTSIZ_PKTCNT_MASK, pktcnt) | diff --git a/drivers/usb/host/dwc2.c b/drivers/usb/host/dwc2.c index ff7885f8195..b2742923579 100644 --- a/drivers/usb/host/dwc2.c +++ b/drivers/usb/host/dwc2.c @@ -108,78 +108,6 @@ static void init_fslspclksel(struct dwc2_core_regs *regs) FIELD_PREP(HCFG_FSLSPCLKSEL_MASK, phyclk)); } -/* - * Flush a Tx FIFO. - * - * @param regs Programming view of DWC_otg controller. - * @param num Tx FIFO to flush. - */ -static void dwc_otg_flush_tx_fifo(struct udevice *dev, - struct dwc2_core_regs *regs, const int num) -{ - int ret; - - writel(GRSTCTL_TXFFLSH | FIELD_PREP(GRSTCTL_TXFNUM_MASK, num), - ®s->global_regs.grstctl); - ret = wait_for_bit_le32(®s->global_regs.grstctl, GRSTCTL_TXFFLSH, - false, 1000, false); - if (ret) - dev_info(dev, "%s: Timeout!\n", __func__); - - /* Wait for 3 PHY Clocks */ - udelay(1); -} - -/* - * Flush Rx FIFO. - * - * @param regs Programming view of DWC_otg controller. - */ -static void dwc_otg_flush_rx_fifo(struct udevice *dev, - struct dwc2_core_regs *regs) -{ - int ret; - - writel(GRSTCTL_RXFFLSH, ®s->global_regs.grstctl); - ret = wait_for_bit_le32(®s->global_regs.grstctl, GRSTCTL_RXFFLSH, - false, 1000, false); - if (ret) - dev_info(dev, "%s: Timeout!\n", __func__); - - /* Wait for 3 PHY Clocks */ - udelay(1); -} - -/* - * Do core a soft reset of the core. Be careful with this because it - * resets all the internal state machines of the core. - */ -static void dwc_otg_core_reset(struct udevice *dev, - struct dwc2_core_regs *regs) -{ - int ret; - - /* Wait for AHB master IDLE state. */ - ret = wait_for_bit_le32(®s->global_regs.grstctl, GRSTCTL_AHBIDLE, - true, 1000, false); - if (ret) - dev_info(dev, "%s: Timeout!\n", __func__); - - /* Core Soft Reset */ - writel(GRSTCTL_CSFTRST, ®s->global_regs.grstctl); - ret = wait_for_bit_le32(®s->global_regs.grstctl, GRSTCTL_CSFTRST, - false, 1000, false); - if (ret) - dev_info(dev, "%s: Timeout!\n", __func__); - - /* - * Wait for core to come out of reset. - * NOTE: This long sleep is _very_ important, otherwise the core will - * not stay in host mode after a connector ID change! - */ - mdelay(100); -} - #if CONFIG_IS_ENABLED(DM_USB) && defined(CONFIG_DM_REGULATOR) static int dwc_vbus_supply_init(struct udevice *dev) { @@ -281,8 +209,8 @@ static void dwc_otg_core_host_init(struct udevice *dev, clrbits_le32(®s->global_regs.gotgctl, GOTGCTL_HSTSETHNPEN); /* Make sure the FIFOs are flushed. */ - dwc_otg_flush_tx_fifo(dev, regs, GRSTCTL_TXFNUM_ALL); /* All Tx FIFOs */ - dwc_otg_flush_rx_fifo(dev, regs); + dwc2_flush_tx_fifo(regs, GRSTCTL_TXFNUM_ALL); /* All Tx FIFOs */ + dwc2_flush_rx_fifo(regs); /* Flush out any leftover queued requests. */ num_channels = FIELD_GET(GHWCFG2_NUM_HOST_CHAN_MASK, readl(®s->global_regs.ghwcfg2)) + 1; @@ -352,7 +280,7 @@ static void dwc_otg_core_init(struct udevice *dev) writel(usbcfg, ®s->global_regs.gusbcfg); /* Reset the Controller */ - dwc_otg_core_reset(dev, regs); + dwc2_core_reset(regs); /* * This programming sequence needs to happen in FS mode before @@ -413,7 +341,7 @@ static void dwc_otg_core_init(struct udevice *dev) writel(usbcfg, ®s->global_regs.gusbcfg); /* Reset after setting the PHY parameters */ - dwc_otg_core_reset(dev, regs); + dwc2_core_reset(regs); #endif usbcfg = readl(®s->global_regs.gusbcfg); -- 2.47.2