diff options
Diffstat (limited to 'drivers/usb/common/dwc2_core.c')
-rw-r--r-- | drivers/usb/common/dwc2_core.c | 131 |
1 files changed, 131 insertions, 0 deletions
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; +} |