diff options
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/usb/common/Makefile | 2 | ||||
-rw-r--r-- | drivers/usb/common/dwc2_core.c | 131 | ||||
-rw-r--r-- | drivers/usb/common/dwc2_core.h | 4 | ||||
-rw-r--r-- | drivers/usb/gadget/dwc2_udc_otg.c | 12 | ||||
-rw-r--r-- | drivers/usb/gadget/dwc2_udc_otg_xfer_dma.c | 6 | ||||
-rw-r--r-- | drivers/usb/host/dwc2.c | 80 |
6 files changed, 145 insertions, 90 deletions
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 <seashell11234455@gmail.com> + */ + +#include <linux/bitfield.h> +#include <linux/errno.h> +#include <linux/io.h> +#include <wait_bit.h> + +#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); |