diff options
author | Sascha Hauer <s.hauer@pengutronix.de> | 2020-08-19 07:37:56 +0200 |
---|---|---|
committer | Sascha Hauer <s.hauer@pengutronix.de> | 2020-08-19 07:37:56 +0200 |
commit | 8551ec67beac6ae3f052a75ced33e7a4fba86cdd (patch) | |
tree | afb2027abb718730018dfe236985713c37919027 /drivers/usb | |
parent | 4a16b52ebacf1874bfe2035077c2ba17633f7490 (diff) | |
parent | 1290406d170f774c9cdda33b96a57565ed435946 (diff) | |
download | barebox-8551ec67beac6ae3f052a75ced33e7a4fba86cdd.tar.gz barebox-8551ec67beac6ae3f052a75ced33e7a4fba86cdd.tar.xz |
Merge branch 'for-next/usb-dwc2' into master
Diffstat (limited to 'drivers/usb')
-rw-r--r-- | drivers/usb/Kconfig | 2 | ||||
-rw-r--r-- | drivers/usb/Makefile | 1 | ||||
-rw-r--r-- | drivers/usb/dwc2/Kconfig | 21 | ||||
-rw-r--r-- | drivers/usb/dwc2/Makefile | 2 | ||||
-rw-r--r-- | drivers/usb/dwc2/core.c | 806 | ||||
-rw-r--r-- | drivers/usb/dwc2/core.h | 562 | ||||
-rw-r--r-- | drivers/usb/dwc2/dwc2.c | 117 | ||||
-rw-r--r-- | drivers/usb/dwc2/dwc2.h | 46 | ||||
-rw-r--r-- | drivers/usb/dwc2/gadget.c | 2736 | ||||
-rw-r--r-- | drivers/usb/dwc2/host.c | 790 | ||||
-rw-r--r-- | drivers/usb/dwc2/regs.h | 841 | ||||
-rw-r--r-- | drivers/usb/dwc2/rhub.c | 384 | ||||
-rw-r--r-- | drivers/usb/host/Kconfig | 3 | ||||
-rw-r--r-- | drivers/usb/host/Makefile | 1 | ||||
-rw-r--r-- | drivers/usb/host/dwc2.c | 1132 | ||||
-rw-r--r-- | drivers/usb/host/dwc2.h | 778 |
16 files changed, 6308 insertions, 1914 deletions
diff --git a/drivers/usb/Kconfig b/drivers/usb/Kconfig index 99eff1c8d2..aab1564ab3 100644 --- a/drivers/usb/Kconfig +++ b/drivers/usb/Kconfig @@ -9,6 +9,8 @@ if USB_HOST source "drivers/usb/imx/Kconfig" +source "drivers/usb/dwc2/Kconfig" + source "drivers/usb/dwc3/Kconfig" source "drivers/usb/host/Kconfig" diff --git a/drivers/usb/Makefile b/drivers/usb/Makefile index 64d4bddad4..c39ce8bb66 100644 --- a/drivers/usb/Makefile +++ b/drivers/usb/Makefile @@ -1,5 +1,6 @@ obj-$(CONFIG_USB) += core/ obj-$(CONFIG_USB_IMX_CHIPIDEA) += imx/ +obj-$(CONFIG_USB_DWC2) += dwc2/ obj-$(CONFIG_USB_DWC3) += dwc3/ obj-$(CONFIG_USB_MUSB) += musb/ obj-$(CONFIG_USB_STORAGE) += storage/ diff --git a/drivers/usb/dwc2/Kconfig b/drivers/usb/dwc2/Kconfig new file mode 100644 index 0000000000..3c3cdc3de6 --- /dev/null +++ b/drivers/usb/dwc2/Kconfig @@ -0,0 +1,21 @@ +config USB_DWC2 + bool + depends on USB && HAS_DMA + select USB_OTGDEV + select OFDEVICE + help + DesignWare Core USB2 OTG driver. + +config USB_DWC2_HOST + bool "DWC2 Host mode support" + depends on USB_HOST + select USB_DWC2 + help + Select this when you want to use DWC2 in host mode. + +config USB_DWC2_GADGET + bool "DWC2 Gadget mode support" + depends on USB_GADGET + select USB_DWC2 + help + Select this when you want to use DWC2 in gadget mode. diff --git a/drivers/usb/dwc2/Makefile b/drivers/usb/dwc2/Makefile new file mode 100644 index 0000000000..f019051d82 --- /dev/null +++ b/drivers/usb/dwc2/Makefile @@ -0,0 +1,2 @@ +obj-$(CONFIG_USB_DWC2_HOST) += dwc2.o core.o host.o rhub.o +obj-$(CONFIG_USB_DWC2_GADGET) += dwc2.o core.o gadget.o diff --git a/drivers/usb/dwc2/core.c b/drivers/usb/dwc2/core.c new file mode 100644 index 0000000000..c4a3cc789b --- /dev/null +++ b/drivers/usb/dwc2/core.c @@ -0,0 +1,806 @@ +// SPDX-License-Identifier: GPL-2.0+ +#include "dwc2.h" + +/* Returns the controller's GHWCFG2.OTG_MODE. */ +static unsigned int dwc2_op_mode(struct dwc2 *dwc2) +{ + u32 ghwcfg2 = dwc2_readl(dwc2, GHWCFG2); + + return (ghwcfg2 & GHWCFG2_OP_MODE_MASK) >> + GHWCFG2_OP_MODE_SHIFT; +} + +/* Returns true if the controller is host-only. */ +static bool dwc2_hw_is_host(struct dwc2 *dwc2) +{ + unsigned int op_mode = dwc2_op_mode(dwc2); + + return (op_mode == GHWCFG2_OP_MODE_SRP_CAPABLE_HOST) || + (op_mode == GHWCFG2_OP_MODE_NO_SRP_CAPABLE_HOST); +} + +/* Returns true if the controller is device-only. */ +static bool dwc2_hw_is_device(struct dwc2 *dwc2) +{ + unsigned int op_mode = dwc2_op_mode(dwc2); + + return (op_mode == GHWCFG2_OP_MODE_SRP_CAPABLE_DEVICE) || + (op_mode == GHWCFG2_OP_MODE_NO_SRP_CAPABLE_DEVICE); +} + +void dwc2_set_param_otg_cap(struct dwc2 *dwc2) +{ + u8 val; + + switch (dwc2->hw_params.op_mode) { + case GHWCFG2_OP_MODE_HNP_SRP_CAPABLE: + val = DWC2_CAP_PARAM_HNP_SRP_CAPABLE; + break; + case GHWCFG2_OP_MODE_SRP_ONLY_CAPABLE: + case GHWCFG2_OP_MODE_SRP_CAPABLE_DEVICE: + case GHWCFG2_OP_MODE_SRP_CAPABLE_HOST: + val = DWC2_CAP_PARAM_SRP_ONLY_CAPABLE; + break; + default: + val = DWC2_CAP_PARAM_NO_HNP_SRP_CAPABLE; + break; + } + + dwc2->params.otg_cap = val; +} + +void dwc2_set_param_phy_type(struct dwc2 *dwc2) +{ + u8 val; + + switch (dwc2->hw_params.hs_phy_type) { + case GHWCFG2_HS_PHY_TYPE_UTMI: + case GHWCFG2_HS_PHY_TYPE_UTMI_ULPI: + val = DWC2_PHY_TYPE_PARAM_UTMI; + break; + case GHWCFG2_HS_PHY_TYPE_ULPI: + val = DWC2_PHY_TYPE_PARAM_ULPI; + break; + case GHWCFG2_HS_PHY_TYPE_NOT_SUPPORTED: + val = DWC2_PHY_TYPE_PARAM_FS; + break; + } + + dwc2->params.phy_type = val; +} + +void dwc2_set_param_speed(struct dwc2 *dwc2) +{ + if (dwc2->params.phy_type == DWC2_PHY_TYPE_PARAM_FS) + dwc2->params.speed = DWC2_SPEED_PARAM_FULL; + else + dwc2->params.speed = DWC2_SPEED_PARAM_HIGH; +} + +void dwc2_set_param_phy_utmi_width(struct dwc2 *dwc2) +{ + int val; + + val = (dwc2->hw_params.utmi_phy_data_width == + GHWCFG4_UTMI_PHY_DATA_WIDTH_8) ? 8 : 16; + + dwc2->params.phy_utmi_width = val; +} + +/** + * dwc2_hsotg_tx_fifo_count - return count of TX FIFOs in device mode + * + * @hsotg: Programming view of the DWC_otg controller + */ +int dwc2_tx_fifo_count(struct dwc2 *dwc2) +{ + if (dwc2->hw_params.en_multiple_tx_fifo) + /* In dedicated FIFO mode we need count of IN EPs */ + return dwc2->hw_params.num_dev_in_eps; + else + /* In shared FIFO mode we need count of Periodic IN EPs */ + return dwc2->hw_params.num_dev_perio_in_ep; +} + +static void dwc2_set_param_fifo_sizes(struct dwc2 *dwc2) +{ + struct dwc2_hw_params *hw = &dwc2->hw_params; + struct dwc2_core_params *p = &dwc2->params; + u32 total_fifo_size = dwc2->hw_params.total_fifo_size; + u32 max_np_tx_fifo_size = hw->dev_nperio_tx_fifo_size; + u32 max_rx_fifo_size = hw->rx_fifo_size; + u32 depth; + int count, i; + + count = dwc2_tx_fifo_count(dwc2); + + depth = total_fifo_size / 4; + p->g_np_tx_fifo_size = min(max_np_tx_fifo_size, depth); + total_fifo_size -= p->g_np_tx_fifo_size; + + depth = 8 * count + 256; + depth = max(total_fifo_size / count, depth); + p->g_rx_fifo_size = min(max_rx_fifo_size, depth); + total_fifo_size -= p->g_rx_fifo_size; + + for (i = 1; i <= count; i++) + p->g_tx_fifo_size[i] = total_fifo_size / count; +} + +/** + * dwc2_set_default_params() - Set all core parameters to their + * auto-detected default values. + * + * @dwc2: Programming view of the DWC2 controller + * + */ +void dwc2_set_default_params(struct dwc2 *dwc2) +{ + struct dwc2_hw_params *hw = &dwc2->hw_params; + struct dwc2_core_params *p = &dwc2->params; + bool dma_capable = !(hw->arch == GHWCFG2_SLAVE_ONLY_ARCH); + + dwc2_set_param_otg_cap(dwc2); + dwc2_set_param_phy_type(dwc2); + dwc2_set_param_speed(dwc2); + dwc2_set_param_phy_utmi_width(dwc2); + p->phy_ulpi_ddr = false; + p->phy_ulpi_ext_vbus = false; + + p->enable_dynamic_fifo = hw->enable_dynamic_fifo; + p->en_multiple_tx_fifo = hw->en_multiple_tx_fifo; + p->i2c_enable = hw->i2c_enable; + p->acg_enable = hw->acg_enable; + p->ulpi_fs_ls = false; + p->ts_dline = false; + p->reload_ctl = (hw->snpsid >= DWC2_CORE_REV_2_92a); + p->uframe_sched = true; + p->external_id_pin_ctl = false; + p->lpm = true; + p->lpm_clock_gating = true; + p->besl = true; + p->hird_threshold_en = true; + p->hird_threshold = 4; + p->ipg_isoc_en = false; + p->max_packet_count = hw->max_packet_count; + p->max_transfer_size = hw->max_transfer_size; + p->ahbcfg = GAHBCFG_HBSTLEN_INCR << GAHBCFG_HBSTLEN_SHIFT; + + p->dma = dma_capable; + p->dma_desc = false; + + if (dwc2->dr_mode == USB_DR_MODE_HOST || + dwc2->dr_mode == USB_DR_MODE_OTG) { + p->host_support_fs_ls_low_power = false; + p->host_ls_low_power_phy_clk = false; + p->host_channels = hw->host_channels; + p->host_rx_fifo_size = hw->rx_fifo_size; + p->host_nperio_tx_fifo_size = hw->host_nperio_tx_fifo_size; + p->host_perio_tx_fifo_size = hw->host_perio_tx_fifo_size; + } + + if ((dwc2->dr_mode == USB_DR_MODE_PERIPHERAL) || + (dwc2->dr_mode == USB_DR_MODE_OTG)) { + dwc2_set_param_fifo_sizes(dwc2); + } +} + +int dwc2_core_snpsid(struct dwc2 *dwc2) +{ + struct dwc2_hw_params *hw = &dwc2->hw_params; + + hw->snpsid = dwc2_readl(dwc2, GSNPSID); + + /* + * Attempt to ensure this device is really a DWC2 Controller. + * Read and verify the GSNPSID register contents. The value should be + * 0x4f54xxxx, 0x5531xxxx or 0x5532xxxx + */ + hw->snpsid = dwc2_readl(dwc2, GSNPSID); + if ((hw->snpsid & GSNPSID_ID_MASK) != DWC2_OTG_ID && + (hw->snpsid & GSNPSID_ID_MASK) != DWC2_FS_IOT_ID && + (hw->snpsid & GSNPSID_ID_MASK) != DWC2_HS_IOT_ID) { + dwc2_err(dwc2, "Bad value for GSNPSID: 0x%08x\n", + hw->snpsid); + return -ENODEV; + } + + dwc2_dbg(dwc2, "Core Release: %1x.%1x%1x%1x (snpsid=%x)\n", + hw->snpsid >> 12 & 0xf, hw->snpsid >> 8 & 0xf, + hw->snpsid >> 4 & 0xf, hw->snpsid & 0xf, hw->snpsid); + + return 0; +} + +static void dwc2_get_dev_hwparams(struct dwc2 *dwc2) +{ + struct dwc2_hw_params *hw = &dwc2->hw_params; + u32 size; + int count, i; + + size = FIFOSIZE_DEPTH_GET(dwc2_readl(dwc2, GNPTXFSIZ)); + hw->dev_nperio_tx_fifo_size = size; + + count = dwc2_tx_fifo_count(dwc2); + + for (i = 1; i <= count; i++) { + size = FIFOSIZE_DEPTH_GET(dwc2_readl(dwc2, DPTXFSIZN(i))); + hw->g_tx_fifo_size[i] = size; + } +} + +/** + * During device initialization, read various hardware configuration + * registers and interpret the contents. + * + * @dwc2: Programming view of the DWC2 controller + * + */ +void dwc2_get_hwparams(struct dwc2 *dwc2) +{ + struct dwc2_hw_params *hw = &dwc2->hw_params; + unsigned int width; + u32 hwcfg1, hwcfg2, hwcfg3, hwcfg4; + u32 grxfsiz; + + hwcfg1 = dwc2_readl(dwc2, GHWCFG1); + hwcfg2 = dwc2_readl(dwc2, GHWCFG2); + hwcfg3 = dwc2_readl(dwc2, GHWCFG3); + hwcfg4 = dwc2_readl(dwc2, GHWCFG4); + grxfsiz = dwc2_readl(dwc2, GRXFSIZ); + + /* hwcfg1 */ + hw->dev_ep_dirs = hwcfg1; + + /* hwcfg2 */ + hw->op_mode = (hwcfg2 & GHWCFG2_OP_MODE_MASK) >> + GHWCFG2_OP_MODE_SHIFT; + hw->arch = (hwcfg2 & GHWCFG2_ARCHITECTURE_MASK) >> + GHWCFG2_ARCHITECTURE_SHIFT; + hw->enable_dynamic_fifo = !!(hwcfg2 & GHWCFG2_DYNAMIC_FIFO); + hw->host_channels = 1 + ((hwcfg2 & GHWCFG2_NUM_HOST_CHAN_MASK) >> + GHWCFG2_NUM_HOST_CHAN_SHIFT); + hw->hs_phy_type = (hwcfg2 & GHWCFG2_HS_PHY_TYPE_MASK) >> + GHWCFG2_HS_PHY_TYPE_SHIFT; + hw->fs_phy_type = (hwcfg2 & GHWCFG2_FS_PHY_TYPE_MASK) >> + GHWCFG2_FS_PHY_TYPE_SHIFT; + hw->num_dev_ep = (hwcfg2 & GHWCFG2_NUM_DEV_EP_MASK) >> + GHWCFG2_NUM_DEV_EP_SHIFT; + hw->nperio_tx_q_depth = + (hwcfg2 & GHWCFG2_NONPERIO_TX_Q_DEPTH_MASK) >> + GHWCFG2_NONPERIO_TX_Q_DEPTH_SHIFT << 1; + hw->host_perio_tx_q_depth = + (hwcfg2 & GHWCFG2_HOST_PERIO_TX_Q_DEPTH_MASK) >> + GHWCFG2_HOST_PERIO_TX_Q_DEPTH_SHIFT << 1; + hw->dev_token_q_depth = + (hwcfg2 & GHWCFG2_DEV_TOKEN_Q_DEPTH_MASK) >> + GHWCFG2_DEV_TOKEN_Q_DEPTH_SHIFT; + + /* hwcfg3 */ + width = (hwcfg3 & GHWCFG3_XFER_SIZE_CNTR_WIDTH_MASK) >> + GHWCFG3_XFER_SIZE_CNTR_WIDTH_SHIFT; + hw->max_transfer_size = (1 << (width + 11)) - 1; + width = (hwcfg3 & GHWCFG3_PACKET_SIZE_CNTR_WIDTH_MASK) >> + GHWCFG3_PACKET_SIZE_CNTR_WIDTH_SHIFT; + hw->max_packet_count = (1 << (width + 4)) - 1; + hw->i2c_enable = !!(hwcfg3 & GHWCFG3_I2C); + hw->total_fifo_size = (hwcfg3 & GHWCFG3_DFIFO_DEPTH_MASK) >> + GHWCFG3_DFIFO_DEPTH_SHIFT; + hw->lpm_mode = !!(hwcfg3 & GHWCFG3_OTG_LPM_EN); + + /* hwcfg4 */ + hw->en_multiple_tx_fifo = !!(hwcfg4 & GHWCFG4_DED_FIFO_EN); + hw->num_dev_perio_in_ep = (hwcfg4 & GHWCFG4_NUM_DEV_PERIO_IN_EP_MASK) >> + GHWCFG4_NUM_DEV_PERIO_IN_EP_SHIFT; + hw->num_dev_in_eps = (hwcfg4 & GHWCFG4_NUM_IN_EPS_MASK) >> + GHWCFG4_NUM_IN_EPS_SHIFT; + hw->dma_desc_enable = !!(hwcfg4 & GHWCFG4_DESC_DMA); + hw->power_optimized = !!(hwcfg4 & GHWCFG4_POWER_OPTIMIZ); + hw->hibernation = !!(hwcfg4 & GHWCFG4_HIBER); + hw->utmi_phy_data_width = (hwcfg4 & GHWCFG4_UTMI_PHY_DATA_WIDTH_MASK) >> + GHWCFG4_UTMI_PHY_DATA_WIDTH_SHIFT; + hw->acg_enable = !!(hwcfg4 & GHWCFG4_ACG_SUPPORTED); + hw->ipg_isoc_en = !!(hwcfg4 & GHWCFG4_IPG_ISOC_SUPPORTED); + + /* fifo sizes */ + hw->rx_fifo_size = (grxfsiz & GRXFSIZ_DEPTH_MASK) >> + GRXFSIZ_DEPTH_SHIFT; + + dwc2_get_dev_hwparams(dwc2); +} + +/* + * Initializes the FSLSPClkSel field of the HCFG register depending on the + * PHY type + */ +void dwc2_init_fs_ls_pclk_sel(struct dwc2 *dwc2) +{ + u32 hcfg, val; + + if ((dwc2->hw_params.hs_phy_type == GHWCFG2_HS_PHY_TYPE_ULPI && + dwc2->hw_params.fs_phy_type == GHWCFG2_FS_PHY_TYPE_DEDICATED && + dwc2->params.ulpi_fs_ls) || + dwc2->params.phy_type == DWC2_PHY_TYPE_PARAM_FS) { + /* Full speed PHY */ + val = HCFG_FSLSPCLKSEL_48_MHZ; + } else { + /* High speed PHY running at full speed or high speed */ + val = HCFG_FSLSPCLKSEL_30_60_MHZ; + } + + dwc2_dbg(dwc2, "Initializing HCFG.FSLSPClkSel to %08x\n", val); + hcfg = dwc2_readl(dwc2, HCFG); + hcfg &= ~HCFG_FSLSPCLKSEL_MASK; + hcfg |= val << HCFG_FSLSPCLKSEL_SHIFT; + dwc2_writel(dwc2, hcfg, HCFG); +} + +void dwc2_flush_all_fifo(struct dwc2 *dwc2) +{ + uint32_t greset; + + /* Wait for AHB master IDLE state */ + if (dwc2_wait_bit_set(dwc2, GRSTCTL, GRSTCTL_AHBIDLE, 10000)) { + dwc2_warn(dwc2, "%s: Timeout waiting for AHB Idle\n", __func__); + return; + } + + greset = GRSTCTL_TXFFLSH | GRSTCTL_RXFFLSH; + /* TXFNUM of 0x10 is to flush all TX FIFO */ + dwc2_writel(dwc2, greset | GRSTCTL_TXFNUM(0x10), GRSTCTL); + + /* Wait for TxFIFO and RxFIFO flush done */ + if (dwc2_wait_bit_clear(dwc2, GRSTCTL, greset, 10000)) + dwc2_warn(dwc2, "Timeout flushing fifos (GRSTCTL=%08x)\n", + dwc2_readl(dwc2, GRSTCTL)); + + /* Wait for 3 PHY Clocks */ + udelay(1); +} + +/** + * dwc2_flush_tx_fifo() - Flushes a Tx FIFO + * + * @hsotg: Programming view of DWC_otg controller + * @idx: The fifo index (0..15) + */ +void dwc2_flush_tx_fifo(struct dwc2 *dwc2, const int idx) +{ + u32 greset; + + if (idx > 15) + return; + + dwc2_dbg(dwc2, "Flush Tx FIFO %d\n", idx); + + /* Wait for AHB master IDLE state */ + if (dwc2_wait_bit_set(dwc2, GRSTCTL, GRSTCTL_AHBIDLE, 10000)) { + dwc2_warn(dwc2, "%s: Timeout waiting for AHB Idle\n", __func__); + return; + } + + greset = GRSTCTL_TXFFLSH; + greset |= GRSTCTL_TXFNUM(idx) & GRSTCTL_TXFNUM_MASK; + dwc2_writel(dwc2, greset, GRSTCTL); + + if (dwc2_wait_bit_clear(dwc2, GRSTCTL, GRSTCTL_TXFFLSH, 10000)) + dwc2_warn(dwc2, "%s: Timeout flushing tx fifo (GRSTCTL=%08x)\n", + __func__, dwc2_readl(dwc2, GRSTCTL)); + + /* Wait for at least 3 PHY Clocks */ + udelay(1); +} + +static int dwc2_fs_phy_init(struct dwc2 *dwc2, bool select_phy) +{ + u32 usbcfg, ggpio, i2cctl; + int retval = 0; + + /* + * core_init() is now called on every switch so only call the + * following for the first time through + */ + if (select_phy) { + dwc2_dbg(dwc2, "FS PHY selected\n"); + + usbcfg = dwc2_readl(dwc2, GUSBCFG); + if (!(usbcfg & GUSBCFG_PHYSEL)) { + usbcfg |= GUSBCFG_PHYSEL; + dwc2_writel(dwc2, usbcfg, GUSBCFG); + + /* Reset after a PHY select */ + retval = dwc2_core_reset(dwc2); + + if (retval) { + dwc2_err(dwc2, + "%s: Reset failed, aborting", __func__); + return retval; + } + } + + if (dwc2->params.activate_stm_fs_transceiver) { + ggpio = dwc2_readl(dwc2, GGPIO); + if (!(ggpio & GGPIO_STM32_OTG_GCCFG_PWRDWN)) { + dwc2_dbg(dwc2, "Activating transceiver\n"); + /* + * STM32F4x9 uses the GGPIO register as general + * core configuration register. + */ + ggpio |= GGPIO_STM32_OTG_GCCFG_PWRDWN; + dwc2_writel(dwc2, ggpio, GGPIO); + } + } + } + + if (dwc2->params.i2c_enable) { + dwc2_dbg(dwc2, "FS PHY enabling I2C\n"); + + /* Program GUSBCFG.OtgUtmiFsSel to I2C */ + usbcfg = dwc2_readl(dwc2, GUSBCFG); + usbcfg |= GUSBCFG_OTG_UTMI_FS_SEL; + dwc2_writel(dwc2, usbcfg, GUSBCFG); + + /* Program GI2CCTL.I2CEn */ + i2cctl = dwc2_readl(dwc2, GI2CCTL); + i2cctl &= ~GI2CCTL_I2CDEVADDR_MASK; + i2cctl |= 1 << GI2CCTL_I2CDEVADDR_SHIFT; + i2cctl &= ~GI2CCTL_I2CEN; + dwc2_writel(dwc2, i2cctl, GI2CCTL); + i2cctl |= GI2CCTL_I2CEN; + dwc2_writel(dwc2, i2cctl, GI2CCTL); + } + + return retval; +} + +static int dwc2_hs_phy_init(struct dwc2 *dwc2, bool select_phy) +{ + u32 usbcfg, usbcfg_old; + int retval = 0; + + if (!select_phy) + return 0; + + usbcfg = dwc2_readl(dwc2, GUSBCFG); + usbcfg_old = usbcfg; + + /* + * HS PHY parameters. These parameters are preserved during soft reset + * so only program the first time. Do a soft reset immediately after + * setting phyif. + */ + switch (dwc2->params.phy_type) { + case DWC2_PHY_TYPE_PARAM_ULPI: + /* ULPI interface */ + dwc2_dbg(dwc2, "HS ULPI PHY selected\n"); + usbcfg |= GUSBCFG_ULPI_UTMI_SEL; + usbcfg &= ~(GUSBCFG_PHYIF16 | GUSBCFG_DDRSEL | GUSBCFG_PHYSEL); + if (dwc2->params.phy_ulpi_ddr) + usbcfg |= GUSBCFG_DDRSEL; + + /* Set external VBUS indicator as needed. */ + if (dwc2->params.phy_ulpi_ext_vbus_ind) { + dwc2_dbg(dwc2, "Use external VBUS indicator\n"); + usbcfg |= GUSBCFG_ULPI_EXT_VBUS_IND; + usbcfg &= ~GUSBCFG_INDICATORCOMPLEMENT; + usbcfg &= ~GUSBCFG_INDICATORPASSTHROUGH; + + if (dwc2->params.phy_ulpi_ext_vbus_ind_complement) + usbcfg |= GUSBCFG_INDICATORCOMPLEMENT; + if (dwc2->params.phy_ulpi_ext_vbus_ind_passthrough) + usbcfg |= GUSBCFG_INDICATORPASSTHROUGH; + } + break; + case DWC2_PHY_TYPE_PARAM_UTMI: + /* UTMI+ interface */ + dwc2_dbg(dwc2, "HS UTMI+ PHY selected\n"); + usbcfg &= ~(GUSBCFG_ULPI_UTMI_SEL | GUSBCFG_PHYIF16); + if (dwc2->params.phy_utmi_width == 16) + usbcfg |= GUSBCFG_PHYIF16; + break; + default: + dwc2_err(dwc2, "FS PHY selected at HS!\n"); + break; + } + + if (usbcfg != usbcfg_old) { + dwc2_writel(dwc2, usbcfg, GUSBCFG); + + /* Reset after setting the PHY parameters */ + retval = dwc2_core_reset(dwc2); + if (retval) { + dwc2_err(dwc2, + "%s: Reset failed, aborting", __func__); + return retval; + } + } + + return retval; +} + +int dwc2_phy_init(struct dwc2 *dwc2, bool select_phy) +{ + u32 usbcfg; + int retval = 0; + + if ((dwc2->params.speed == DWC2_SPEED_PARAM_FULL || + dwc2->params.speed == DWC2_SPEED_PARAM_LOW) && + dwc2->params.phy_type == DWC2_PHY_TYPE_PARAM_FS) { + /* If FS/LS mode with FS/LS PHY */ + retval = dwc2_fs_phy_init(dwc2, select_phy); + if (retval) + return retval; + } else { + /* High speed PHY */ + retval = dwc2_hs_phy_init(dwc2, select_phy); + if (retval) + return retval; + } + + usbcfg = dwc2_readl(dwc2, GUSBCFG); + usbcfg &= ~GUSBCFG_ULPI_FS_LS; + usbcfg &= ~GUSBCFG_ULPI_CLK_SUSP_M; + if (dwc2->hw_params.hs_phy_type == GHWCFG2_HS_PHY_TYPE_ULPI && + dwc2->hw_params.fs_phy_type == GHWCFG2_FS_PHY_TYPE_DEDICATED && + dwc2->params.ulpi_fs_ls) { + dwc2_dbg(dwc2, "Setting ULPI FSLS\n"); + usbcfg |= GUSBCFG_ULPI_FS_LS; + usbcfg |= GUSBCFG_ULPI_CLK_SUSP_M; + } + dwc2_writel(dwc2, usbcfg, GUSBCFG); + + return retval; +} + +int dwc2_gahbcfg_init(struct dwc2 *dwc2) +{ + u32 ahbcfg = dwc2_readl(dwc2, GAHBCFG); + + switch (dwc2->hw_params.arch) { + case GHWCFG2_EXT_DMA_ARCH: + dwc2_err(dwc2, "External DMA Mode not supported\n"); + return -EINVAL; + + case GHWCFG2_INT_DMA_ARCH: + dwc2_dbg(dwc2, "Internal DMA Mode\n"); + if (dwc2->params.ahbcfg != -1) { + ahbcfg &= GAHBCFG_CTRL_MASK; + ahbcfg |= dwc2->params.ahbcfg & + ~GAHBCFG_CTRL_MASK; + } + break; + + case GHWCFG2_SLAVE_ONLY_ARCH: + default: + dwc2_dbg(dwc2, "Slave Only Mode\n"); + break; + } + + if (dwc2->params.dma) + ahbcfg |= GAHBCFG_DMA_EN; + else + dwc2->params.dma_desc = false; + + dwc2_writel(dwc2, ahbcfg, GAHBCFG); + + return 0; +} + +void dwc2_gusbcfg_init(struct dwc2 *dwc2) +{ + u32 usbcfg; + + usbcfg = dwc2_readl(dwc2, GUSBCFG); + usbcfg &= ~(GUSBCFG_HNPCAP | GUSBCFG_SRPCAP); + + switch (dwc2->hw_params.op_mode) { + case GHWCFG2_OP_MODE_HNP_SRP_CAPABLE: + if (dwc2->params.otg_cap == + DWC2_CAP_PARAM_HNP_SRP_CAPABLE) + usbcfg |= GUSBCFG_HNPCAP; + + case GHWCFG2_OP_MODE_SRP_ONLY_CAPABLE: + case GHWCFG2_OP_MODE_SRP_CAPABLE_DEVICE: + case GHWCFG2_OP_MODE_SRP_CAPABLE_HOST: + if (dwc2->params.otg_cap != + DWC2_CAP_PARAM_NO_HNP_SRP_CAPABLE) + usbcfg |= GUSBCFG_SRPCAP; + break; + + case GHWCFG2_OP_MODE_NO_HNP_SRP_CAPABLE: + case GHWCFG2_OP_MODE_NO_SRP_CAPABLE_DEVICE: + case GHWCFG2_OP_MODE_NO_SRP_CAPABLE_HOST: + default: + break; + } + + dwc2_writel(dwc2, usbcfg, GUSBCFG); +} + +/* + * Check the dr_mode against the module configuration and hardware + * capabilities. + * + * The hardware, module, and dr_mode, can each be set to host, device, + * or otg. Check that all these values are compatible and adjust the + * value of dr_mode if possible. + * + * actual + * HW MOD dr_mode dr_mode + * ------------------------------ + * HST HST any : HST + * HST DEV any : --- + * HST OTG any : HST + * + * DEV HST any : --- + * DEV DEV any : DEV + * DEV OTG any : DEV + * + * OTG HST any : HST + * OTG DEV any : DEV + * OTG OTG any : dr_mode + */ +int dwc2_get_dr_mode(struct dwc2 *dwc2) +{ + enum usb_dr_mode mode; + + mode = of_usb_get_dr_mode(dwc2->dev->device_node, NULL); + dwc2->dr_mode = mode; + + if (dwc2_hw_is_device(dwc2)) { + dwc2_dbg(dwc2, "Controller is device only\n"); + if (IS_ENABLED(CONFIG_USB_DWC2_HOST)) { + dwc2_err(dwc2, + "Controller does not support host mode.\n"); + return -EINVAL; + } + mode = USB_DR_MODE_PERIPHERAL; + } else if (dwc2_hw_is_host(dwc2)) { + dwc2_dbg(dwc2, "Controller is host only\n"); + if (IS_ENABLED(CONFIG_USB_DWC2_GADGET)) { + dwc2_err(dwc2, + "Controller does not support device mode.\n"); + return -EINVAL; + } + mode = USB_DR_MODE_HOST; + } else { + dwc2_dbg(dwc2, "Controller is otg\n"); + if (IS_ENABLED(CONFIG_USB_DWC2_HOST) && + IS_ENABLED(CONFIG_USB_DWC2_GADGET)) + mode = dwc2->dr_mode; + else if (IS_ENABLED(CONFIG_USB_DWC2_HOST)) + mode = USB_DR_MODE_HOST; + else if (IS_ENABLED(CONFIG_USB_DWC2_GADGET)) + mode = USB_DR_MODE_PERIPHERAL; + } + + if (mode != dwc2->dr_mode) { + dwc2_warn(dwc2, + "Configuration mismatch. dr_mode forced to %s\n", + mode == USB_DR_MODE_HOST ? "host" : "device"); + + dwc2->dr_mode = mode; + } + + return 0; +} + +/* + * Do core a soft reset of the core. Be careful with this because it + * resets all the internal state machines of the core. + */ +int dwc2_core_reset(struct dwc2 *dwc2) +{ + bool wait_for_host_mode = false; + uint32_t greset; + int ret; + + dwc2_dbg(dwc2, "%s(%p)\n", __func__, dwc2); + + /* Wait for AHB master IDLE state. */ + ret = dwc2_wait_bit_set(dwc2, GRSTCTL, GRSTCTL_AHBIDLE, 10000); + if (ret) { + pr_info("%s: Timeout! Waiting for AHB master IDLE state\n", + __func__); + return ret; + } + + /* + * If the current mode is host, either due to the force mode + * bit being set (which persists after core reset) or the + * connector id pin, a core soft reset will temporarily reset + * the mode to device. A delay from the IDDIG debounce filter + * will occur before going back to host mode. + * + * Determine whether we will go back into host mode after a + * reset and account for this delay after the reset. + */ + { + u32 gotgctl = dwc2_readl(dwc2, GOTGCTL); + u32 gusbcfg = dwc2_readl(dwc2, GUSBCFG); + + if (!(gotgctl & GOTGCTL_CONID_B) || + (gusbcfg & GUSBCFG_FORCEHOSTMODE)) { + dwc2_dbg(dwc2, "HOST MODE\n"); + wait_for_host_mode = true; + } + } + /* Core Soft Reset */ + greset = dwc2_readl(dwc2, GRSTCTL); + greset |= GRSTCTL_CSFTRST; + dwc2_writel(dwc2, greset, GRSTCTL); + + ret = dwc2_wait_bit_clear(dwc2, GRSTCTL, GRSTCTL_CSFTRST, 10000); + if (ret) { + pr_info("%s: Timeout! Waiting for Core Soft Reset\n", __func__); + return ret; + } + + /* + * 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); + + return 0; +} + +/* + * This function initializes the DWC2 controller registers and + * prepares the core for device mode or host mode operation. + * + * @param regs Programming view of the DWC2 controller + */ +void dwc2_core_init(struct dwc2 *dwc2) +{ + uint32_t otgctl = 0; + uint32_t usbcfg = 0; + int retval; + + dwc2_dbg(dwc2, "%s(%p)\n", __func__, dwc2); + + /* Common Initialization */ + usbcfg = dwc2_readl(dwc2, GUSBCFG); + + /* Set ULPI External VBUS bit if needed */ + usbcfg &= ~GUSBCFG_ULPI_EXT_VBUS_DRV; + if (dwc2->params.phy_ulpi_ext_vbus) + usbcfg |= GUSBCFG_ULPI_EXT_VBUS_DRV; + + /* Set external TS Dline pulsing bit if needed */ + usbcfg &= ~GUSBCFG_TERMSELDLPULSE; + if (dwc2->params.ts_dline) + usbcfg |= GUSBCFG_TERMSELDLPULSE; + + dwc2_writel(dwc2, usbcfg, GUSBCFG); + + /* Reset the Controller */ + dwc2_core_reset(dwc2); + + /* + * This programming sequence needs to happen in FS mode before + * any other programming occurs + */ + retval = dwc2_phy_init(dwc2, true); + if (retval) + return; + + /* Program the GAHBCFG Register */ + retval = dwc2_gahbcfg_init(dwc2); + if (retval) + return; + + /* Program the GUSBCFG register */ + dwc2_gusbcfg_init(dwc2); + + /* Program the GOTGCTL register */ + otgctl = dwc2_readl(dwc2, GOTGCTL); + otgctl &= ~GOTGCTL_OTGVER; + dwc2_writel(dwc2, otgctl, GOTGCTL); + + if (dwc2_is_host_mode(dwc2)) + dwc2_dbg(dwc2, "Host Mode\n"); + else + dwc2_dbg(dwc2, "Device Mode\n"); +} diff --git a/drivers/usb/dwc2/core.h b/drivers/usb/dwc2/core.h new file mode 100644 index 0000000000..090ca15fec --- /dev/null +++ b/drivers/usb/dwc2/core.h @@ -0,0 +1,562 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* Maximum number of Endpoints/HostChannels */ +#define DWC2_MAX_EPS_CHANNELS 16 + +/** + * struct dwc2_core_params - Parameters for configuring the core + * + * @otg_cap: Specifies the OTG capabilities. + * 0 - HNP and SRP capable + * 1 - SRP Only capable + * 2 - No HNP/SRP capable (always available) + * Defaults to best available option (0, 1, then 2) + * @host_dma: Specifies whether to use slave or DMA mode for accessing + * the data FIFOs. The driver will automatically detect the + * value for this parameter if none is specified. + * 0 - Slave (always available) + * 1 - DMA (default, if available) + * @dma_desc_enable: When DMA mode is enabled, specifies whether to use + * address DMA mode or descriptor DMA mode for accessing + * the data FIFOs. The driver will automatically detect the + * value for this if none is specified. + * 0 - Address DMA + * 1 - Descriptor DMA (default, if available) + * @dma_desc_fs_enable: When DMA mode is enabled, specifies whether to use + * address DMA mode or descriptor DMA mode for accessing + * the data FIFOs in Full Speed mode only. The driver + * will automatically detect the value for this if none is + * specified. + * 0 - Address DMA + * 1 - Descriptor DMA in FS (default, if available) + * @speed: Specifies the maximum speed of operation in host and + * device mode. The actual speed depends on the speed of + * the attached device and the value of phy_type. + * 0 - High Speed + * (default when phy_type is UTMI+ or ULPI) + * 1 - Full Speed + * (default when phy_type is Full Speed) + * @enable_dynamic_fifo: 0 - Use coreConsultant-specified FIFO size parameters + * 1 - Allow dynamic FIFO sizing (default, if available) + * @en_multiple_tx_fifo: Specifies whether dedicated per-endpoint transmit FIFOs + * are enabled for non-periodic IN endpoints in device + * mode. + * @host_rx_fifo_size: Number of 4-byte words in the Rx FIFO in host mode when + * dynamic FIFO sizing is enabled + * 16 to 32768 + * Actual maximum value is autodetected and also + * the default. + * @host_nperio_tx_fifo_size: Number of 4-byte words in the non-periodic Tx FIFO + * in host mode when dynamic FIFO sizing is enabled + * 16 to 32768 + * Actual maximum value is autodetected and also + * the default. + * @host_perio_tx_fifo_size: Number of 4-byte words in the periodic Tx FIFO in + * host mode when dynamic FIFO sizing is enabled + * 16 to 32768 + * Actual maximum value is autodetected and also + * the default. + * @max_transfer_size: The maximum transfer size supported, in bytes + * 2047 to 65,535 + * Actual maximum value is autodetected and also + * the default. + * @max_packet_count: The maximum number of packets in a transfer + * 15 to 511 + * Actual maximum value is autodetected and also + * the default. + * @host_channels: The number of host channel registers to use + * 1 to 16 + * Actual maximum value is autodetected and also + * the default. + * @phy_type: Specifies the type of PHY interface to use. By default, + * the driver will automatically detect the phy_type. + * 0 - Full Speed Phy + * 1 - UTMI+ Phy + * 2 - ULPI Phy + * Defaults to best available option (2, 1, then 0) + * @phy_utmi_width: Specifies the UTMI+ Data Width (in bits). This parameter + * is applicable for a phy_type of UTMI+ or ULPI. (For a + * ULPI phy_type, this parameter indicates the data width + * between the MAC and the ULPI Wrapper.) Also, this + * parameter is applicable only if the OTG_HSPHY_WIDTH cC + * parameter was set to "8 and 16 bits", meaning that the + * core has been configured to work at either data path + * width. + * 8 or 16 (default 16 if available) + * @phy_ulpi_ddr: Specifies whether the ULPI operates at double or single + * data rate. This parameter is only applicable if phy_type + * is ULPI. + * 0 - single data rate ULPI interface with 8 bit wide + * data bus (default) + * 1 - double data rate ULPI interface with 4 bit wide + * data bus + * @phy_ulpi_ext_vbus: For a ULPI phy, specifies whether to use the internal or + * external supply to drive the VBus + * 0 - Internal supply (default) + * 1 - External supply + * @i2c_enable: Specifies whether to use the I2Cinterface for a full + * speed PHY. This parameter is only applicable if phy_type + * is FS. + * 0 - No (default) + * 1 - Yes + * @ipg_isoc_en: Indicates the IPG supports is enabled or disabled. + * 0 - Disable (default) + * 1 - Enable + * @acg_enable: For enabling Active Clock Gating in the controller + * 0 - No + * 1 - Yes + * @ulpi_fs_ls: Make ULPI phy operate in FS/LS mode only + * 0 - No (default) + * 1 - Yes + * @host_support_fs_ls_low_power: Specifies whether low power mode is supported + * when attached to a Full Speed or Low Speed device in + * host mode. + * 0 - Don't support low power mode (default) + * 1 - Support low power mode + * @host_ls_low_power_phy_clk: Specifies the PHY clock rate in low power mode + * when connected to a Low Speed device in host + * mode. This parameter is applicable only if + * host_support_fs_ls_low_power is enabled. + * 0 - 48 MHz + * (default when phy_type is UTMI+ or ULPI) + * 1 - 6 MHz + * (default when phy_type is Full Speed) + * @oc_disable: Flag to disable overcurrent condition. + * 0 - Allow overcurrent condition to get detected + * 1 - Disable overcurrent condtion to get detected + * @ts_dline: Enable Term Select Dline pulsing + * 0 - No (default) + * 1 - Yes + * @reload_ctl: Allow dynamic reloading of HFIR register during runtime + * 0 - No (default for core < 2.92a) + * 1 - Yes (default for core >= 2.92a) + * @ahbcfg: This field allows the default value of the GAHBCFG + * register to be overridden + * -1 - GAHBCFG value will be set to 0x06 + * (INCR, default) + * all others - GAHBCFG value will be overridden with + * this value + * Not all bits can be controlled like this, the + * bits defined by GAHBCFG_CTRL_MASK are controlled + * by the driver and are ignored in this + * configuration value. + * @uframe_sched: True to enable the microframe scheduler + * @external_id_pin_ctl: Specifies whether ID pin is handled externally. + * Disable CONIDSTSCHNG controller interrupt in such + * case. + * 0 - No (default) + * 1 - Yes + * @power_down: Specifies whether the controller support power_down. + * If power_down is enabled, the controller will enter + * power_down in both peripheral and host mode when + * needed. + * 0 - No (default) + * 1 - Partial power down + * 2 - Hibernation + * @lpm: Enable LPM support. + * 0 - No + * 1 - Yes + * @lpm_clock_gating: Enable core PHY clock gating. + * 0 - No + * 1 - Yes + * @besl: Enable LPM Errata support. + * 0 - No + * 1 - Yes + * @hird_threshold_en: HIRD or HIRD Threshold enable. + * 0 - No + * 1 - Yes + * @hird_threshold: Value of BESL or HIRD Threshold. + * @activate_stm_fs_transceiver: Activate internal transceiver using GGPIO + * register. + * 0 - Deactivate the transceiver (default) + * 1 - Activate the transceiver + * @g_dma: Enables gadget dma usage (default: autodetect). + * @g_dma_desc: Enables gadget descriptor DMA (default: autodetect). + * @g_rx_fifo_size: The periodic rx fifo size for the device, in + * DWORDS from 16-32768 (default: 2048 if + * possible, otherwise autodetect). + * @g_np_tx_fifo_size: The non-periodic tx fifo size for the device in + * DWORDS from 16-32768 (default: 1024 if + * possible, otherwise autodetect). + * @g_tx_fifo_size: An array of TX fifo sizes in dedicated fifo + * mode. Each value corresponds to one EP + * starting from EP1 (max 15 values). Sizes are + * in DWORDS with possible values from from + * 16-32768 (default: 256, 256, 256, 256, 768, + * 768, 768, 768, 0, 0, 0, 0, 0, 0, 0). + * @change_speed_quirk: Change speed configuration to DWC2_SPEED_PARAM_FULL + * while full&low speed device connect. And change speed + * back to DWC2_SPEED_PARAM_HIGH while device is gone. + * 0 - No (default) + * 1 - Yes + * + * The following parameters may be specified when starting the module. These + * parameters define how the DWC_otg controller should be configured. A + * value of -1 (or any other out of range value) for any parameter means + * to read the value from hardware (if possible) or use the builtin + * default described above. + */ +struct dwc2_core_params { + u8 otg_cap; +#define DWC2_CAP_PARAM_HNP_SRP_CAPABLE 0 +#define DWC2_CAP_PARAM_SRP_ONLY_CAPABLE 1 +#define DWC2_CAP_PARAM_NO_HNP_SRP_CAPABLE 2 + + u8 phy_type; +#define DWC2_PHY_TYPE_PARAM_FS 0 +#define DWC2_PHY_TYPE_PARAM_UTMI 1 +#define DWC2_PHY_TYPE_PARAM_ULPI 2 + + u8 speed; +#define DWC2_SPEED_PARAM_HIGH 0 +#define DWC2_SPEED_PARAM_FULL 1 +#define DWC2_SPEED_PARAM_LOW 2 + + u8 phy_utmi_width; + bool phy_ulpi_ddr; + bool phy_ulpi_ext_vbus; + bool phy_ulpi_ext_vbus_ind; + bool phy_ulpi_ext_vbus_ind_complement; + bool phy_ulpi_ext_vbus_ind_passthrough; + + bool enable_dynamic_fifo; + bool en_multiple_tx_fifo; + bool i2c_enable; + bool acg_enable; + bool ulpi_fs_ls; + bool ts_dline; + bool reload_ctl; + bool uframe_sched; + bool external_id_pin_ctl; + + int power_down; +#define DWC2_POWER_DOWN_PARAM_NONE 0 +#define DWC2_POWER_DOWN_PARAM_PARTIAL 1 +#define DWC2_POWER_DOWN_PARAM_HIBERNATION 2 + + bool lpm; + bool lpm_clock_gating; + bool besl; + bool hird_threshold_en; + u8 hird_threshold; + bool activate_stm_fs_transceiver; + bool ipg_isoc_en; + u16 max_packet_count; + u32 max_transfer_size; + u32 ahbcfg; + + bool dma; + bool dma_desc; + + /* Host parameters */ + bool host_support_fs_ls_low_power; + bool host_ls_low_power_phy_clk; + + u8 host_channels; + u16 host_rx_fifo_size; + u16 host_nperio_tx_fifo_size; + u16 host_perio_tx_fifo_size; + + /* Gadget parameters */ + u32 g_rx_fifo_size; + u32 g_np_tx_fifo_size; + u32 g_tx_fifo_size[DWC2_MAX_EPS_CHANNELS]; + + bool change_speed_quirk; +}; + +/** + * struct dwc2_hw_params - Autodetected parameters. + * + * These parameters are the various parameters read from hardware + * registers during initialization. They typically contain the best + * supported or maximum value that can be configured in the + * corresponding dwc2_core_params value. + * + * The values that are not in dwc2_core_params are documented below. + * + * @op_mode: Mode of Operation + * 0 - HNP- and SRP-Capable OTG (Host & Device) + * 1 - SRP-Capable OTG (Host & Device) + * 2 - Non-HNP and Non-SRP Capable OTG (Host & Device) + * 3 - SRP-Capable Device + * 4 - Non-OTG Device + * 5 - SRP-Capable Host + * 6 - Non-OTG Host + * @arch: Architecture + * 0 - Slave only + * 1 - External DMA + * 2 - Internal DMA + * @ipg_isoc_en: This feature indicates that the controller supports + * the worst-case scenario of Rx followed by Rx + * Interpacket Gap (IPG) (32 bitTimes) as per the utmi + * specification for any token following ISOC OUT token. + * 0 - Don't support + * 1 - Support + * @power_optimized: Are power optimizations enabled? + * @num_dev_ep: Number of device endpoints available + * @num_dev_in_eps: Number of device IN endpoints available + * @num_dev_perio_in_ep: Number of device periodic IN endpoints + * available + * @dev_token_q_depth: Device Mode IN Token Sequence Learning Queue + * Depth + * 0 to 30 + * @host_perio_tx_q_depth: + * Host Mode Periodic Request Queue Depth + * 2, 4 or 8 + * @nperio_tx_q_depth: + * Non-Periodic Request Queue Depth + * 2, 4 or 8 + * @hs_phy_type: High-speed PHY interface type + * 0 - High-speed interface not supported + * 1 - UTMI+ + * 2 - ULPI + * 3 - UTMI+ and ULPI + * @fs_phy_type: Full-speed PHY interface type + * 0 - Full speed interface not supported + * 1 - Dedicated full speed interface + * 2 - FS pins shared with UTMI+ pins + * 3 - FS pins shared with ULPI pins + * @total_fifo_size: Total internal RAM for FIFOs (bytes) + * @hibernation: Is hibernation enabled? + * @utmi_phy_data_width: UTMI+ PHY data width + * 0 - 8 bits + * 1 - 16 bits + * 2 - 8 or 16 bits + * @snpsid: Value from SNPSID register + * @dev_ep_dirs: Direction of device endpoints (GHWCFG1) + * @g_tx_fifo_size: Power-on values of TxFIFO sizes + * @dma_desc_enable: When DMA mode is enabled, specifies whether to use + * address DMA mode or descriptor DMA mode for accessing + * the data FIFOs. The driver will automatically detect the + * value for this if none is specified. + * 0 - Address DMA + * 1 - Descriptor DMA (default, if available) + * @enable_dynamic_fifo: 0 - Use coreConsultant-specified FIFO size parameters + * 1 - Allow dynamic FIFO sizing (default, if available) + * @en_multiple_tx_fifo: Specifies whether dedicated per-endpoint transmit FIFOs + * are enabled for non-periodic IN endpoints in device + * mode. + * @host_nperio_tx_fifo_size: Number of 4-byte words in the non-periodic Tx FIFO + * in host mode when dynamic FIFO sizing is enabled + * 16 to 32768 + * Actual maximum value is autodetected and also + * the default. + * @host_perio_tx_fifo_size: Number of 4-byte words in the periodic Tx FIFO in + * host mode when dynamic FIFO sizing is enabled + * 16 to 32768 + * Actual maximum value is autodetected and also + * the default. + * @max_transfer_size: The maximum transfer size supported, in bytes + * 2047 to 65,535 + * Actual maximum value is autodetected and also + * the default. + * @max_packet_count: The maximum number of packets in a transfer + * 15 to 511 + * Actual maximum value is autodetected and also + * the default. + * @host_channels: The number of host channel registers to use + * 1 to 16 + * Actual maximum value is autodetected and also + * the default. + * @dev_nperio_tx_fifo_size: Number of 4-byte words in the non-periodic Tx FIFO + * in device mode when dynamic FIFO sizing is enabled + * 16 to 32768 + * Actual maximum value is autodetected and also + * the default. + * @i2c_enable: Specifies whether to use the I2Cinterface for a full + * speed PHY. This parameter is only applicable if phy_type + * is FS. + * 0 - No (default) + * 1 - Yes + * @acg_enable: For enabling Active Clock Gating in the controller + * 0 - Disable + * 1 - Enable + * @lpm_mode: For enabling Link Power Management in the controller + * 0 - Disable + * 1 - Enable + * @rx_fifo_size: Number of 4-byte words in the Rx FIFO when dynamic + * FIFO sizing is enabled 16 to 32768 + * Actual maximum value is autodetected and also + * the default. + */ +struct dwc2_hw_params { + unsigned op_mode:3; + unsigned arch:2; + unsigned dma_desc_enable:1; + unsigned enable_dynamic_fifo:1; + unsigned en_multiple_tx_fifo:1; + unsigned rx_fifo_size:16; + unsigned host_nperio_tx_fifo_size:16; + unsigned dev_nperio_tx_fifo_size:16; + unsigned host_perio_tx_fifo_size:16; + unsigned nperio_tx_q_depth:3; + unsigned host_perio_tx_q_depth:3; + unsigned dev_token_q_depth:5; + unsigned max_transfer_size:26; + unsigned max_packet_count:11; + unsigned host_channels:5; + unsigned hs_phy_type:2; + unsigned fs_phy_type:2; + unsigned i2c_enable:1; + unsigned acg_enable:1; + unsigned num_dev_ep:4; + unsigned num_dev_in_eps:4; + unsigned num_dev_perio_in_ep:4; + unsigned total_fifo_size:16; + unsigned power_optimized:1; + unsigned hibernation:1; + unsigned utmi_phy_data_width:2; + unsigned lpm_mode:1; + unsigned ipg_isoc_en:1; + u32 snpsid; + u32 dev_ep_dirs; + u32 g_tx_fifo_size[DWC2_MAX_EPS_CHANNELS]; +}; + +#define MAX_DEVICE 16 +#define MAX_ENDPOINT DWC2_MAX_EPS_CHANNELS + +struct dwc2_ep { + struct usb_ep ep; + struct dwc2 *dwc2; + struct list_head queue; + struct dwc2_request *req; + char name[8]; + + unsigned int size_loaded; + unsigned int last_load; + unsigned short fifo_size; + unsigned short fifo_index; + + u8 dir_in; + u8 epnum; + u8 mc; + u16 interval; + + unsigned int halted:1; + unsigned int periodic:1; + unsigned int isochronous:1; + unsigned int send_zlp:1; + unsigned int target_frame; +#define TARGET_FRAME_INITIAL 0xFFFFFFFF + bool frame_overrun; +}; + +struct dwc2_request { + struct usb_request req; + struct list_head queue; +}; + +/* Gadget ep0 states */ +enum dwc2_ep0_state { + DWC2_EP0_SETUP, + DWC2_EP0_DATA_IN, + DWC2_EP0_DATA_OUT, + DWC2_EP0_STATUS_IN, + DWC2_EP0_STATUS_OUT, +}; + +/* Size of control and EP0 buffers */ +#define DWC2_CTRL_BUFF_SIZE 8 + +struct dwc2 { + struct device_d *dev; + void __iomem *regs; + enum usb_dr_mode dr_mode; + struct dwc2_hw_params hw_params; + struct dwc2_core_params params; + +#ifdef CONFIG_USB_DWC2_HOST + struct usb_host host; + u8 in_data_toggle[MAX_DEVICE][MAX_ENDPOINT]; + u8 out_data_toggle[MAX_DEVICE][MAX_ENDPOINT]; + int root_hub_devnum; +#endif + +#ifdef CONFIG_USB_DWC2_GADGET + struct usb_gadget gadget; + struct dwc2_ep *eps_in[MAX_ENDPOINT]; + struct dwc2_ep *eps_out[MAX_ENDPOINT]; + struct usb_request *ctrl_req; + void *ep0_buff; + void *ctrl_buff; + enum dwc2_ep0_state ep0_state; + struct usb_gadget_driver *driver; + + int num_eps; + u16 frame_number; + u32 fifo_map; + unsigned int dedicated_fifos:1; + unsigned int enabled:1; + unsigned int connected:1; + unsigned int is_selfpowered:1; +#endif +}; + +#define host_to_dwc2(ptr) container_of(ptr, struct dwc2, host) +#define gadget_to_dwc2(ptr) container_of(ptr, struct dwc2, gadget) + +#define dwc2_err(d, arg...) dev_err((d)->dev, ## arg) +#define dwc2_warn(d, arg...) dev_err((d)->dev, ## arg) +#define dwc2_info(d, arg...) dev_info((d)->dev, ## arg) +#define dwc2_dbg(d, arg...) dev_dbg((d)->dev, ## arg) +#define dwc2_vdbg(d, arg...) dev_vdbg((d)->dev, ## arg) + +static inline u32 dwc2_readl(struct dwc2 *dwc2, u32 offset) +{ + u32 val; + + val = readl(dwc2->regs + offset); + + return val; +} + +static inline void dwc2_writel(struct dwc2 *dwc2, u32 value, u32 offset) +{ + writel(value, dwc2->regs + offset); +} + +/** + * dwc2_wait_bit_set - Waits for bit to be set. + * @dwc2: Programming view of DWC2 controller. + * @offset: Register's offset where bit/bits must be set. + * @mask: Mask of the bit/bits which must be set. + * @timeout: Timeout to wait. + * + * Return: 0 if bit/bits are set or -ETIMEDOUT in case of timeout. + */ +static inline int dwc2_wait_bit_set(struct dwc2 *dwc2, u32 offset, u32 mask, + u32 timeout) +{ + return wait_on_timeout(timeout * USECOND, + dwc2_readl(dwc2, offset) & mask); +} + +/** + * dwc2_wait_bit_clear - Waits for bit to be clear. + * @dwc2: Programming view of DWC2 controller. + * @offset: Register's offset where bit/bits must be set. + * @mask: Mask of the bit/bits which must be set. + * @timeout: Timeout to wait. + * + * Return: 0 if bit/bits are set or -ETIMEDOUT in case of timeout. + */ +static inline int dwc2_wait_bit_clear(struct dwc2 *dwc2, u32 offset, u32 mask, + u32 timeout) +{ + return wait_on_timeout(timeout * USECOND, + !(dwc2_readl(dwc2, offset) & mask)); +} + +/* + * Returns the mode of operation, host or device + */ +static inline int dwc2_is_host_mode(struct dwc2 *dwc2) +{ + return (dwc2_readl(dwc2, GINTSTS) & GINTSTS_CURMODE_HOST) != 0; +} + +static inline int dwc2_is_device_mode(struct dwc2 *dwc2) +{ + return (dwc2_readl(dwc2, GINTSTS) & GINTSTS_CURMODE_HOST) == 0; +} diff --git a/drivers/usb/dwc2/dwc2.c b/drivers/usb/dwc2/dwc2.c new file mode 100644 index 0000000000..908d624794 --- /dev/null +++ b/drivers/usb/dwc2/dwc2.c @@ -0,0 +1,117 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (C) 2012 Oleksandr Tymoshenko <gonzo@freebsd.org> + * Copyright (C) 2014 Marek Vasut <marex@denx.de> + * + * Copied from u-Boot + */ +#include <common.h> +#include <of.h> +#include <dma.h> +#include <init.h> +#include <errno.h> +#include <driver.h> +#include <linux/clk.h> + +#include "dwc2.h" + +static void dwc2_uninit_common(struct dwc2 *dwc2) +{ + uint32_t hprt0; + + hprt0 = dwc2_readl(dwc2, HPRT0); + + /* Put everything in reset. */ + hprt0 &= ~(HPRT0_ENA | HPRT0_ENACHG | HPRT0_CONNDET | HPRT0_OVRCURRCHG); + hprt0 |= HPRT0_RST; + + dwc2_writel(dwc2, hprt0, HPRT0); +} + +static int dwc2_set_mode(void *ctx, enum usb_dr_mode mode) +{ + struct dwc2 *dwc2 = ctx; + int ret = -ENODEV; + + if (mode == USB_DR_MODE_HOST || mode == USB_DR_MODE_OTG) { + if (IS_ENABLED(CONFIG_USB_DWC2_HOST)) + ret = dwc2_register_host(dwc2); + else + dwc2_err(dwc2, "Host support not available\n"); + } + if (mode == USB_DR_MODE_PERIPHERAL || mode == USB_DR_MODE_OTG) { + if (IS_ENABLED(CONFIG_USB_DWC2_GADGET)) + ret = dwc2_gadget_init(dwc2); + else + dwc2_err(dwc2, "Peripheral support not available\n"); + } + + return ret; +} + +static int dwc2_probe(struct device_d *dev) +{ + struct resource *iores; + struct dwc2 *dwc2; + int ret; + + dwc2 = xzalloc(sizeof(*dwc2)); + + iores = dev_request_mem_resource(dev, 0); + if (IS_ERR(iores)) + return PTR_ERR(iores); + dwc2->regs = IOMEM(iores->start); + dwc2->dev = dev; + + ret = dwc2_core_snpsid(dwc2); + if (ret) + goto error; + + /* + * Reset before dwc2_get_hwparams() then it could get power-on real + * reset value form registers. + */ + ret = dwc2_core_reset(dwc2); + if (ret) + goto error; + + /* Detect config values from hardware */ + dwc2_get_hwparams(dwc2); + + ret = dwc2_get_dr_mode(dwc2); + + dwc2_set_default_params(dwc2); + + dma_set_mask(dev, DMA_BIT_MASK(32)); + dev->priv = dwc2; + + if (dwc2->dr_mode == USB_DR_MODE_OTG) + ret = usb_register_otg_device(dwc2->dev, dwc2_set_mode, dwc2); + else + ret = dwc2_set_mode(dwc2, dwc2->dr_mode); + +error: + return ret; +} + +static void dwc2_remove(struct device_d *dev) +{ + struct dwc2 *dwc2 = dev->priv; + + dwc2_uninit_common(dwc2); +} + +static const struct of_device_id dwc2_platform_dt_ids[] = { + { .compatible = "brcm,bcm2835-usb", }, + { .compatible = "brcm,bcm2708-usb", }, + { .compatible = "snps,dwc2", }, + { } +}; + +static struct driver_d dwc2_driver = { + .name = "dwc2", + .probe = dwc2_probe, + .remove = dwc2_remove, + .of_compatible = DRV_OF_COMPAT(dwc2_platform_dt_ids), +}; +device_platform_driver(dwc2_driver); diff --git a/drivers/usb/dwc2/dwc2.h b/drivers/usb/dwc2/dwc2.h new file mode 100644 index 0000000000..5e845f3491 --- /dev/null +++ b/drivers/usb/dwc2/dwc2.h @@ -0,0 +1,46 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +#include <usb/usb.h> +#include <usb/usb_defs.h> +#include <usb/gadget.h> + +#include "regs.h" +#include "core.h" + +/* Core functions */ +void dwc2_set_param_otg_cap(struct dwc2 *dwc2); +void dwc2_set_param_phy_type(struct dwc2 *dwc2); +void dwc2_set_param_speed(struct dwc2 *dwc2); +void dwc2_set_param_phy_utmi_width(struct dwc2 *dwc2); +void dwc2_set_default_params(struct dwc2 *dwc2); +int dwc2_core_snpsid(struct dwc2 *dwc2); +void dwc2_get_hwparams(struct dwc2 *dwc2); + +void dwc2_init_fs_ls_pclk_sel(struct dwc2 *dwc2); +void dwc2_flush_all_fifo(struct dwc2 *dwc2); +void dwc2_flush_tx_fifo(struct dwc2 *dwc2, const int idx); +int dwc2_tx_fifo_count(struct dwc2 *dwc2); + +int dwc2_phy_init(struct dwc2 *dwc2, bool select_phy); +int dwc2_gahbcfg_init(struct dwc2 *dwc2); +void dwc2_gusbcfg_init(struct dwc2 *dwc2); +int dwc2_get_dr_mode(struct dwc2 *dwc2); + +int dwc2_core_reset(struct dwc2 *dwc2); +void dwc2_core_init(struct dwc2 *dwc2); + +/* Host functions */ +#ifdef CONFIG_USB_DWC2_HOST +int dwc2_submit_roothub(struct dwc2 *dwc2, struct usb_device *dev, + unsigned long pipe, void *buf, int len, + struct devrequest *setup); +int dwc2_register_host(struct dwc2 *dwc2); +#else +static inline int dwc2_register_host(struct dwc2 *dwc2) { return -ENODEV; } +#endif + +/* Gadget functions */ +#ifdef CONFIG_USB_DWC2_GADGET +int dwc2_gadget_init(struct dwc2 *dwc2); +#else +static inline int dwc2_gadget_init(struct dwc2 *dwc2) { return -ENODEV; } +#endif diff --git a/drivers/usb/dwc2/gadget.c b/drivers/usb/dwc2/gadget.c new file mode 100644 index 0000000000..6a65b9b117 --- /dev/null +++ b/drivers/usb/dwc2/gadget.c @@ -0,0 +1,2736 @@ +// SPDX-License-Identifier: GPL-2.0+ +#include <dma.h> +#include <usb/gadget.h> +#include "dwc2.h" + +#define to_dwc2 gadget_to_dwc2 +#define dwc2_set_bit(d, r, b) dwc2_writel(d, (b) | dwc2_readl(d, r), r) +#define dwc2_clear_bit(d, r, b) dwc2_writel(d, ~(b) & dwc2_readl(d, r), r) + +#define spin_lock(lock) +#define spin_unlock(lock) +#define local_irq_save(flags)(void)(flags) +#define local_irq_restore(flags) (void)(flags) +#define spin_lock_irqsave(lock, flags) (void)(flags) +#define spin_unlock_irqrestore(lock, flags) (void)(flags) + +#ifndef USB_ENDPOINT_MAXP_MASK +#define USB_ENDPOINT_MAXP_MASK 0x07ff +#endif +#ifndef USB_EP_MAXP_MULT +#define USB_EP_MAXP_MULT(m) (((m) & 0x1800) >> 11) +#endif + +static void kill_all_requests(struct dwc2 *, struct dwc2_ep *, int); + +static inline struct dwc2_ep *index_to_ep(struct dwc2 *dwc2, + unsigned int ep, int in) +{ + if (ep >= DWC2_MAX_EPS_CHANNELS) + return NULL; + if (in) + return dwc2->eps_in[ep]; + else + return dwc2->eps_out[ep]; +} + +static inline int using_dma(struct dwc2 *dwc2) +{ + /* Only buffer dma is supported */ + return 1; +} + +static void dwc2_dcfg_set_addr(struct dwc2 *dwc2, int addr) +{ + u32 dcfg = dwc2_readl(dwc2, DCFG); + + dcfg &= ~DCFG_DEVADDR_MASK; + dcfg |= (addr << DCFG_DEVADDR_SHIFT) & DCFG_DEVADDR_MASK; + dwc2_writel(dwc2, dcfg, DCFG); +} + +/** + * dwc2_hsotg_ctrl_epint - enable/disable an endpoint irq + * @hsotg: The device state + * @ep: The endpoint index + * @dir_in: True if direction is in. + * @en: The enable value, true to enable + * + * Set or clear the mask for an individual endpoint's interrupt + * request. + */ +static void dwc2_hsotg_ctrl_epint(struct dwc2 *dwc2, + unsigned int ep, unsigned int dir_in, + unsigned int en) +{ + unsigned long flags; + u32 bit = 1 << ep; + u32 daint; + + if (!dir_in) + bit <<= 16; + + local_irq_save(flags); + daint = dwc2_readl(dwc2, DAINTMSK); + if (en) + daint |= bit; + else + daint &= ~bit; + dwc2_writel(dwc2, daint, DAINTMSK); + local_irq_restore(flags); + +} + +/** + * get_ep_head - return the first request on the endpoint + * @hs_ep: The controller endpoint to get + * + * Get the first request on the endpoint. + */ +static struct dwc2_request *get_ep_head(struct dwc2_ep *hs_ep) +{ + return list_first_entry_or_null(&hs_ep->queue, struct dwc2_request, + queue); +} + +/** + * get_ep_limit - get the maximum data legnth for this endpoint + * @hs_ep: The endpoint + * + * Return the maximum data that can be queued in one go on a given endpoint + * so that transfers that are too long can be split. + */ +static unsigned int get_ep_limit(struct dwc2_ep *hs_ep) +{ + int index = hs_ep->epnum; + unsigned int maxsize; + unsigned int maxpkt; + + if (index != 0) { + maxsize = DXEPTSIZ_XFERSIZE_LIMIT + 1; + maxpkt = DXEPTSIZ_PKTCNT_LIMIT + 1; + } else { + maxsize = 64 + 64; + if (hs_ep->dir_in) + maxpkt = DIEPTSIZ0_PKTCNT_LIMIT + 1; + else + maxpkt = 2; + } + + /* we made the constant loading easier above by using +1 */ + maxpkt--; + maxsize--; + + /* + * constrain by packet count if maxpkts*pktsize is greater + * than the length register size. + */ + + if ((maxpkt * hs_ep->ep.maxpacket) < maxsize) + maxsize = maxpkt * hs_ep->ep.maxpacket; + + return maxsize; +} + +static u32 dwc2_read_frameno(struct dwc2 *dwc2) +{ + u32 dsts; + + dsts = dwc2_readl(dwc2, DSTS); + dsts &= DSTS_SOFFN_MASK; + dsts >>= DSTS_SOFFN_SHIFT; + + return dsts; +} + +/** + * dwc2_gadget_incr_frame_num - Increments the targeted frame number. + * @hs_ep: The endpoint + * + * This function will also check if the frame number overruns DSTS_SOFFN_LIMIT. + * If an overrun occurs it will wrap the value and set the frame_overrun flag. + */ +static inline void dwc2_gadget_incr_frame_num(struct dwc2_ep *hs_ep) +{ + hs_ep->target_frame += hs_ep->interval; + if (hs_ep->target_frame > DSTS_SOFFN_LIMIT) { + hs_ep->frame_overrun = true; + hs_ep->target_frame &= DSTS_SOFFN_LIMIT; + } else { + hs_ep->frame_overrun = false; + } +} + +/** + * dwc2_gadget_target_frame_elapsed - Checks target frame + * @hs_ep: The driver endpoint to check + * + * Returns 1 if targeted frame elapsed. If returned 1 then we need to drop + * corresponding transfer. + */ +static bool dwc2_gadget_target_frame_elapsed(struct dwc2_ep *hs_ep) +{ + struct dwc2 *dwc2 = hs_ep->dwc2; + u32 target_frame = hs_ep->target_frame; + u32 current_frame = dwc2->frame_number; + bool frame_overrun = hs_ep->frame_overrun; + + if (!frame_overrun && current_frame >= target_frame) + return true; + + if (frame_overrun && current_frame >= target_frame && + ((current_frame - target_frame) < DSTS_SOFFN_LIMIT / 2)) + return true; + + return false; +} + + +/** + * dwc2_gadget_start_req - start a USB request from an endpoint's queue + * @dwc2: The controller state. + * @hs_ep: The endpoint to process a request for + * @hs_req: The request to start. + * @continuing: True if we are doing more for the current request. + * + * Start the given request running by setting the endpoint registers + * appropriately, and writing any data to the FIFOs. + */ +static void dwc2_gadget_start_req(struct dwc2 *dwc2, + struct dwc2_ep *hs_ep, + struct dwc2_request *hs_req, + bool continuing) +{ + struct usb_request *ureq = &hs_req->req; + int index = hs_ep->epnum; + int dir_in = hs_ep->dir_in; + u32 epctrl_reg; + u32 epsize_reg; + u32 epsize; + u32 ctrl; + unsigned int length; + unsigned int packets; + unsigned int maxreq; + unsigned int dma_reg; + + if (index != 0) { + if (hs_ep->req && !continuing) { + dwc2_err(dwc2, "%s: active request\n", __func__); + WARN_ON(1); + return; + } else if (hs_ep->req != hs_req && continuing) { + dwc2_err(dwc2, + "%s: continue different req\n", __func__); + WARN_ON(1); + return; + } + } + + dma_reg = dir_in ? DIEPDMA(index) : DOEPDMA(index); + epctrl_reg = dir_in ? DIEPCTL(index) : DOEPCTL(index); + epsize_reg = dir_in ? DIEPTSIZ(index) : DOEPTSIZ(index); + + dwc2_dbg(dwc2, "%s: DxEPCTL=0x%08x, ep %d, dir %s\n", + __func__, dwc2_readl(dwc2, epctrl_reg), index, + hs_ep->dir_in ? "in" : "out"); + + /* If endpoint is stalled, we will restart request later */ + ctrl = dwc2_readl(dwc2, epctrl_reg); + + if (index && ctrl & DXEPCTL_STALL) { + dwc2_warn(dwc2, "%s: ep%d is stalled\n", __func__, index); + return; + } + + length = ureq->length - ureq->actual; + dwc2_dbg(dwc2, "ureq->length:%d ureq->actual:%d\n", + ureq->length, ureq->actual); + + maxreq = get_ep_limit(hs_ep); + + if (length > maxreq) { + int round = maxreq % hs_ep->ep.maxpacket; + + dwc2_dbg(dwc2, "%s: length %d, max-req %d, r %d\n", + __func__, length, maxreq, round); + + /* round down to multiple of packets */ + if (round) + maxreq -= round; + + length = maxreq; + } + + if (length) + packets = DIV_ROUND_UP(length, hs_ep->ep.maxpacket); + else + packets = 1; /* send one packet if length is zero. */ + + if (dir_in && index != 0) + if (hs_ep->isochronous) + epsize = DXEPTSIZ_MC(packets); + else + epsize = DXEPTSIZ_MC(1); + else + epsize = 0; + + /* + * zero length packet should be programmed on its own and should not + * be counted in DIEPTSIZ.PktCnt with other packets. + */ + if (dir_in && ureq->zero && !continuing) { + /* Test if zlp is actually required. */ + if ((ureq->length >= hs_ep->ep.maxpacket) && + !(ureq->length % hs_ep->ep.maxpacket)) + hs_ep->send_zlp = 1; + } + + epsize |= DXEPTSIZ_PKTCNT(packets); + epsize |= DXEPTSIZ_XFERSIZE(length); + + dwc2_dbg(dwc2, "%s: %d@%d/%d, 0x%08x => 0x%08x\n", + __func__, packets, length, ureq->length, epsize, epsize_reg); + + /* store the request as the current one we're doing */ + hs_ep->req = hs_req; + + /* write size / packets */ + dwc2_writel(dwc2, epsize, epsize_reg); + + if (using_dma(dwc2) && !continuing && (length != 0)) { + /* + * write DMA address to control register, buffer + * already synced by dwc2_ep_queue(). + */ + + dwc2_writel(dwc2, ureq->dma, dma_reg); + + dwc2_dbg(dwc2, "%s: 0x%pad => 0x%08x\n", + __func__, (void *)ureq->dma, dma_reg); + } + + if (hs_ep->isochronous && hs_ep->interval == 1) { + hs_ep->target_frame = dwc2_read_frameno(dwc2); + dwc2_gadget_incr_frame_num(hs_ep); + + if (hs_ep->target_frame & 0x1) + ctrl |= DXEPCTL_SETODDFR; + else + ctrl |= DXEPCTL_SETEVENFR; + } + + ctrl |= DXEPCTL_EPENA; /* ensure ep enabled */ + + dwc2_dbg(dwc2, "%s: ep0 state:%d\n", __func__, dwc2->ep0_state); + + /* For Setup request do not clear NAK */ + if (!(index == 0 && dwc2->ep0_state == DWC2_EP0_SETUP)) + ctrl |= DXEPCTL_CNAK; /* clear NAK set by core */ + + dwc2_dbg(dwc2, "%s: DxEPCTL=0x%08x\n", __func__, ctrl); + dwc2_writel(dwc2, ctrl, epctrl_reg); + + /* + * set these, it seems that DMA support increments past the end + * of the packet buffer so we need to calculate the length from + * this information. + */ + hs_ep->size_loaded = length; + hs_ep->last_load = ureq->actual; + + /* + * Note, trying to clear the NAK here causes problems with transmit + * on the S3C6400 ending up with the TXFIFO becoming full. + */ + + /* check ep is enabled */ + if (!(dwc2_readl(dwc2, epctrl_reg) & DXEPCTL_EPENA)) + dwc2_dbg(dwc2, + "ep%d: failed to become enabled (DXEPCTL=0x%08x)?\n", + index, dwc2_readl(dwc2, epctrl_reg)); + + dwc2_dbg(dwc2, "%s: DXEPCTL=0x%08x\n", + __func__, dwc2_readl(dwc2, epctrl_reg)); + + /* enable ep interrupts */ + dwc2_hsotg_ctrl_epint(dwc2, hs_ep->epnum, hs_ep->dir_in, 1); +} + +/* conversion functions */ +static inline struct dwc2_request *our_req(struct usb_request *req) +{ + return container_of(req, struct dwc2_request, req); +} + +static inline struct dwc2_ep *our_ep(struct usb_ep *ep) +{ + return container_of(ep, struct dwc2_ep, ep); +} + + +/** + * dwc2_ep0_mps - turn max packet size into register setting + * @mps: The maximum packet size in bytes. + */ +static u32 dwc2_ep0_mps(unsigned int mps) +{ + switch (mps) { + case 64: + return D0EPCTL_MPS_64; + case 32: + return D0EPCTL_MPS_32; + case 16: + return D0EPCTL_MPS_16; + case 8: + return D0EPCTL_MPS_8; + } + + /* bad max packet size, warn and return invalid result */ + WARN_ON(1); + return (u32)-1; +} + +/** + * dwc2_set_ep_maxpacket - set endpoint's max-packet field + * @dwc2: The driver state. + * @ep: The index number of the endpoint + * @mps: The maximum packet size in bytes + * @mc: The multicount value + * @dir_in: True if direction is in. + * + * Configure the maximum packet size for the given endpoint, updating + * the hardware control registers to reflect this. + */ +static void dwc2_set_ep_maxpacket(struct dwc2 *dwc2, + unsigned int ep, unsigned int mps, + unsigned int mc, unsigned int dir_in) +{ + struct dwc2_ep *hs_ep; + u32 reg; + + hs_ep = index_to_ep(dwc2, ep, dir_in); + if (!hs_ep) + return; + + if (ep == 0) { + u32 mps_bytes = mps; + + /* EP0 is a special case */ + mps = dwc2_ep0_mps(mps_bytes); + if (mps > 3) + goto bad_mps; + hs_ep->ep.maxpacket = mps_bytes; + hs_ep->mc = 1; + } else { + if (mps > 1024) + goto bad_mps; + hs_ep->mc = mc; + if (mc > 3) + goto bad_mps; + hs_ep->ep.maxpacket = mps; + } + + if (dir_in) { + reg = dwc2_readl(dwc2, DIEPCTL(ep)); + reg &= ~DXEPCTL_MPS_MASK; + reg |= mps; + dwc2_writel(dwc2, reg, DIEPCTL(ep)); + } else { + reg = dwc2_readl(dwc2, DOEPCTL(ep)); + reg &= ~DXEPCTL_MPS_MASK; + reg |= mps; + dwc2_writel(dwc2, reg, DOEPCTL(ep)); + } + + return; + +bad_mps: + dwc2_err(dwc2, "ep%d: bad mps of %d\n", ep, mps); +} + +static int dwc2_ep_enable(struct usb_ep *ep, + const struct usb_endpoint_descriptor *desc) +{ + struct dwc2_ep *hs_ep = our_ep(ep); + struct dwc2 *dwc2 = hs_ep->dwc2; + unsigned long flags; + unsigned int index = hs_ep->epnum; + u32 epctrl_reg; + u32 epctrl; + u32 mps; + u32 mc; + u32 mask; + unsigned int dir_in; + unsigned int i, val, size; + int ret = 0; + unsigned char ep_type; + + dwc2_dbg(dwc2, + "%s: ep %s: a 0x%02x, attr 0x%02x, mps 0x%04x, intr %d\n", + __func__, ep->name, desc->bEndpointAddress, desc->bmAttributes, + desc->wMaxPacketSize, desc->bInterval); + + /* not to be called for EP0 */ + if (index == 0) { + dwc2_err(dwc2, "%s: called for EP 0\n", __func__); + return -EINVAL; + } + + dir_in = (desc->bEndpointAddress & USB_ENDPOINT_DIR_MASK) ? 1 : 0; + if (dir_in != hs_ep->dir_in) { + dwc2_err(dwc2, "%s: direction mismatch!\n", __func__); + return -EINVAL; + } + + ep_type = desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK; + mps = usb_endpoint_maxp(desc) & USB_ENDPOINT_MAXP_MASK; + mc = USB_EP_MAXP_MULT(usb_endpoint_maxp(desc)); + + /* note, we handle this here instead of dwc2_set_ep_maxpacket */ + epctrl_reg = dir_in ? DIEPCTL(index) : DOEPCTL(index); + epctrl = dwc2_readl(dwc2, epctrl_reg); + + dwc2_dbg(dwc2, "%s: read DxEPCTL=0x%08x from 0x%08x\n", + __func__, epctrl, epctrl_reg); + + spin_lock_irqsave(&dwc2->lock, flags); + + epctrl &= ~(DXEPCTL_EPTYPE_MASK | DXEPCTL_MPS_MASK); + epctrl |= DXEPCTL_MPS(mps); + + /* + * mark the endpoint as active, otherwise the core may ignore + * transactions entirely for this endpoint + */ + epctrl |= DXEPCTL_USBACTEP; + + /* update the endpoint state */ + dwc2_set_ep_maxpacket(dwc2, index, mps, mc, dir_in); + + /* default, set to non-periodic */ + hs_ep->isochronous = 0; + hs_ep->periodic = 0; + hs_ep->halted = 0; + hs_ep->interval = desc->bInterval; + + switch (ep_type) { + case USB_ENDPOINT_XFER_ISOC: + epctrl |= DXEPCTL_EPTYPE_ISO; + epctrl |= DXEPCTL_SETEVENFR; + hs_ep->isochronous = 1; + hs_ep->interval = 1 << (desc->bInterval - 1); + hs_ep->target_frame = TARGET_FRAME_INITIAL; + if (dir_in) { + hs_ep->periodic = 1; + mask = dwc2_readl(dwc2, DIEPMSK); + mask |= DIEPMSK_NAKMSK; + dwc2_writel(dwc2, mask, DIEPMSK); + } else { + mask = dwc2_readl(dwc2, DOEPMSK); + mask |= DOEPMSK_OUTTKNEPDISMSK; + dwc2_writel(dwc2, mask, DOEPMSK); + } + break; + + case USB_ENDPOINT_XFER_BULK: + epctrl |= DXEPCTL_EPTYPE_BULK; + break; + + case USB_ENDPOINT_XFER_INT: + if (dir_in) + hs_ep->periodic = 1; + + if (dwc2->gadget.speed == USB_SPEED_HIGH) + hs_ep->interval = 1 << (desc->bInterval - 1); + + epctrl |= DXEPCTL_EPTYPE_INTERRUPT; + break; + + case USB_ENDPOINT_XFER_CONTROL: + epctrl |= DXEPCTL_EPTYPE_CONTROL; + break; + } + + /* + * if the hardware has dedicated fifos, we must give each IN EP + * a unique tx-fifo even if it is non-periodic. + */ + if (dir_in && dwc2->dedicated_fifos) { + unsigned int fifo_count = dwc2_tx_fifo_count(dwc2); + u32 fifo_index = 0; + u32 fifo_size = UINT_MAX; + + size = hs_ep->ep.maxpacket * hs_ep->mc; + for (i = 1; i <= fifo_count; i++) { + if (dwc2->fifo_map & (1 << i)) + continue; + + val = dwc2_readl(dwc2, DPTXFSIZN(i)); + val = (val >> FIFOSIZE_DEPTH_SHIFT) * 4; + if (val < size) + continue; + /* Search for smallest acceptable fifo */ + if (val < fifo_size) { + fifo_size = val; + fifo_index = i; + } + } + if (!fifo_index) { + dwc2_err(dwc2, + "%s: No suitable fifo found\n", __func__); + ret = -ENOMEM; + goto error; + } + epctrl &= ~(DXEPCTL_TXFNUM_LIMIT << DXEPCTL_TXFNUM_SHIFT); + epctrl |= DXEPCTL_TXFNUM(fifo_index); + dwc2->fifo_map |= 1 << fifo_index; + hs_ep->fifo_index = fifo_index; + hs_ep->fifo_size = fifo_size; + } + + /* for non control endpoints, set PID to D0 */ + if (index && !hs_ep->isochronous) + epctrl |= DXEPCTL_SETD0PID; + + dwc2_dbg(dwc2, "%s: write DxEPCTL=0x%08x\n", + __func__, epctrl); + + dwc2_writel(dwc2, epctrl, epctrl_reg); + dwc2_dbg(dwc2, "%s: read DxEPCTL=0x%08x\n", + __func__, dwc2_readl(dwc2, epctrl_reg)); + + /* enable the endpoint interrupt */ + dwc2_hsotg_ctrl_epint(dwc2, index, dir_in, 1); + +error: + spin_unlock_irqrestore(&dwc2->lock, flags); + + return ret; +} + +static void dwc2_ep_stop_xfr(struct dwc2 *dwc2, struct dwc2_ep *hs_ep) +{ + int in = hs_ep->dir_in; + int epnum = hs_ep->epnum; + u32 epctl_reg = in ? DIEPCTL(epnum) : DOEPCTL(epnum); + u32 epint_reg = in ? DIEPINT(epnum) : DOEPINT(epnum); + + dwc2_dbg(dwc2, "%s: stopping transfer on %s\n", __func__, + hs_ep->name); + + if (in) { + if (dwc2->dedicated_fifos || hs_ep->periodic) { + dwc2_set_bit(dwc2, epctl_reg, DXEPCTL_SNAK); + /* Wait for Nak effect */ + if (dwc2_wait_bit_set(dwc2, epint_reg, + DXEPINT_INEPNAKEFF, 100)) + dwc2_warn(dwc2, + "%s: timeout DIEPINT.NAKEFF\n", + __func__); + } else { + dwc2_set_bit(dwc2, DCTL, DCTL_SGNPINNAK); + /* Wait for Nak effect */ + if (dwc2_wait_bit_set(dwc2, GINTSTS, + GINTSTS_GINNAKEFF, 100)) + dwc2_warn(dwc2, + "%s: timeout GINTSTS.GINNAKEFF\n", + __func__); + } + } else { + if (!(dwc2_readl(dwc2, GINTSTS) & GINTSTS_GOUTNAKEFF)) + dwc2_set_bit(dwc2, DCTL, DCTL_SGOUTNAK); + + /* Wait for global nak to take effect */ + if (dwc2_wait_bit_set(dwc2, GINTSTS, + GINTSTS_GOUTNAKEFF, 100)) + dwc2_warn(dwc2, "%s: timeout GINTSTS.GOUTNAKEFF\n", + __func__); + } + + /* Disable ep */ + dwc2_set_bit(dwc2, epctl_reg, DXEPCTL_EPDIS | DXEPCTL_SNAK); + + /* Wait for ep to be disabled */ + if (dwc2_wait_bit_set(dwc2, epint_reg, DXEPINT_EPDISBLD, 100)) + dwc2_warn(dwc2, "%s: timeout DOEPCTL.EPDisable\n", __func__); + + /* Clear EPDISBLD interrupt */ + dwc2_set_bit(dwc2, epint_reg, DXEPINT_EPDISBLD); + + if (in) { + unsigned short fifo_index; + + if (dwc2->dedicated_fifos || hs_ep->periodic) + fifo_index = hs_ep->fifo_index; + else + fifo_index = 0; + + dwc2_flush_tx_fifo(dwc2, fifo_index); + + /* Clear Global In NP NAK in Shared FIFO for non periodic ep */ + if (!dwc2->dedicated_fifos && !hs_ep->periodic) + dwc2_set_bit(dwc2, DCTL, DCTL_CGNPINNAK); + + } else { + /* Remove global NAKs */ + dwc2_set_bit(dwc2, DCTL, DCTL_CGOUTNAK); + } +} + +static int dwc2_ep_disable(struct usb_ep *ep) +{ + struct dwc2_ep *hs_ep = our_ep(ep); + struct dwc2 *dwc2 = hs_ep->dwc2; + int dir_in = hs_ep->dir_in; + int index = hs_ep->epnum; + u32 epctrl_reg = dir_in ? DIEPCTL(index) : DOEPCTL(index); + u32 ctrl; + + dwc2_dbg(dwc2, "%s(ep %p)\n", __func__, ep); + + if (index == 0) { + dwc2_err(dwc2, "%s: called for ep0\n", __func__); + return -EINVAL; + } + + ctrl = dwc2_readl(dwc2, epctrl_reg); + if (ctrl & DXEPCTL_EPENA) + dwc2_ep_stop_xfr(dwc2, hs_ep); + + ctrl &= ~DXEPCTL_EPENA; + ctrl &= ~DXEPCTL_USBACTEP; + ctrl |= DXEPCTL_SNAK; + + dwc2_dbg(dwc2, "%s: DxEPCTL=0x%08x\n", __func__, ctrl); + dwc2_writel(dwc2, ctrl, epctrl_reg); + + /* disable endpoint interrupts */ + dwc2_hsotg_ctrl_epint(dwc2, index, hs_ep->dir_in, 0); + + /* terminate all requests with shutdown */ + kill_all_requests(dwc2, hs_ep, -ESHUTDOWN); + + dwc2->fifo_map &= ~(1 << hs_ep->fifo_index); + hs_ep->fifo_index = 0; + hs_ep->fifo_size = 0; + + return 0; +} + +static struct usb_request *dwc2_ep_alloc_req(struct usb_ep *ep) +{ + struct dwc2_request *req; + + req = kzalloc(sizeof(*req), GFP_KERNEL); + if (!req) + return NULL; + + INIT_LIST_HEAD(&req->queue); + + return &req->req; +} + +static void dwc2_ep_free_req(struct usb_ep *ep, struct usb_request *req) +{ + struct dwc2_request *hs_req = our_req(req); + + kfree(hs_req); +} + +/** + * dwc2_start_next_request - Starts next request from ep queue + * @hs_ep: Endpoint structure + * + * If queue is empty and EP is ISOC-OUT - unmasks OUTTKNEPDIS which is masked + * in its handler. Hence we need to unmask it here to be able to do + * resynchronization. + */ +static void dwc2_start_next_request(struct dwc2_ep *hs_ep) +{ + u32 mask; + struct dwc2 *dwc2 = hs_ep->dwc2; + int dir_in = hs_ep->dir_in; + struct dwc2_request *hs_req; + u32 epmsk_reg = dir_in ? DIEPMSK : DOEPMSK; + + dwc2_dbg(dwc2, "%s: next req\n", __func__); + + if (!list_empty(&hs_ep->queue)) { + hs_req = get_ep_head(hs_ep); + dwc2_gadget_start_req(dwc2, hs_ep, hs_req, false); + return; + } + if (!hs_ep->isochronous) + return; + + if (dir_in) { + dwc2_dbg(dwc2, "%s: No more ISOC-IN requests\n", __func__); + } else { + dwc2_dbg(dwc2, "%s: No more ISOC-OUT requests\n", __func__); + mask = dwc2_readl(dwc2, epmsk_reg); + mask |= DOEPMSK_OUTTKNEPDISMSK; + dwc2_writel(dwc2, mask, epmsk_reg); + } +} + +static int dwc2_ep_queue(struct usb_ep *ep, struct usb_request *req) +{ + + struct dwc2_request *hs_req = our_req(req); + struct dwc2_ep *hs_ep = our_ep(ep); + struct dwc2 *dwc2 = hs_ep->dwc2; + bool first; + + dwc2_dbg(dwc2, "%s: req %p: %d@%p, noi=%d, zero=%d, snok=%d\n", + ep->name, req, req->length, req->buf, req->no_interrupt, + req->zero, req->short_not_ok); + + /* initialise status of the request */ + INIT_LIST_HEAD(&hs_req->queue); + req->actual = 0; + req->status = -EINPROGRESS; + + /* Don't queue ISOC request if length greater than mps*mc */ + if (hs_ep->isochronous && + req->length > (hs_ep->mc * hs_ep->ep.maxpacket)) { + dwc2_err(dwc2, "req length > maxpacket*mc\n"); + return -EINVAL; + } + + if ((long)req->buf & 3) + dwc2_err(dwc2, "dma buffer not aligned\n"); + + req->dma = dma_map_single(dwc2->dev, req->buf, req->length, + hs_ep->dir_in ? DMA_TO_DEVICE : DMA_FROM_DEVICE); + + if (dma_mapping_error(dwc2->dev, req->dma)) { + dwc2_err(dwc2, "failed to map buffer\n"); + return -EFAULT; + } + + first = list_empty(&hs_ep->queue); + list_add_tail(&hs_req->queue, &hs_ep->queue); + + /* Change EP direction if status phase request is after data out */ + if (hs_ep->epnum == 0 && !req->length && !hs_ep->dir_in && + dwc2->ep0_state == DWC2_EP0_DATA_OUT) + hs_ep->dir_in = 1; + + if (first) { + if (!hs_ep->isochronous) { + dwc2_gadget_start_req(dwc2, hs_ep, hs_req, false); + return 0; + } + + /* Update current frame number value. */ + dwc2->frame_number = dwc2_read_frameno(dwc2); + while (dwc2_gadget_target_frame_elapsed(hs_ep)) { + dwc2_gadget_incr_frame_num(hs_ep); + /* Update current frame number value once more as it + * changes here. + */ + dwc2->frame_number = dwc2_read_frameno(dwc2); + } + + if (hs_ep->target_frame != TARGET_FRAME_INITIAL) + dwc2_gadget_start_req(dwc2, hs_ep, hs_req, false); + } + return 0; +} + +static int dwc2_ep_dequeue(struct usb_ep *ep, struct usb_request *req) +{ + struct dwc2_ep *hs_ep = our_ep(ep); + struct dwc2 *dwc2 = hs_ep->dwc2; + dwc2_warn(dwc2, "%s\n", __func__); + return -EOPNOTSUPP; +} + +static int dwc2_ep_set_halt(struct usb_ep *ep, int value) +{ + struct dwc2_ep *hs_ep = our_ep(ep); + struct dwc2 *dwc2 = hs_ep->dwc2; + dwc2_warn(dwc2, "%s\n", __func__); + return -EOPNOTSUPP; +} + +static int dwc2_ep_set_wedge(struct usb_ep *ep) +{ + struct dwc2_ep *hs_ep = our_ep(ep); + struct dwc2 *dwc2 = hs_ep->dwc2; + dwc2_warn(dwc2, "%s\n", __func__); + return -EOPNOTSUPP; +} + +static int dwc2_ep_fifo_status(struct usb_ep *ep) +{ + struct dwc2_ep *hs_ep = our_ep(ep); + struct dwc2 *dwc2 = hs_ep->dwc2; + dwc2_warn(dwc2, "%s\n", __func__); + return -EOPNOTSUPP; +} + +static void dwc2_ep_fifo_flush(struct usb_ep *ep) +{ + struct dwc2_ep *hs_ep = our_ep(ep); + struct dwc2 *dwc2 = hs_ep->dwc2; + dwc2_warn(dwc2, "%s\n", __func__); +} + +static const struct usb_ep_ops dwc2_ep_ops = { + .enable = dwc2_ep_enable, + .disable = dwc2_ep_disable, + .alloc_request = dwc2_ep_alloc_req, + .free_request = dwc2_ep_free_req, + .queue = dwc2_ep_queue, + .dequeue = dwc2_ep_dequeue, + .set_halt = dwc2_ep_set_halt, + .set_wedge = dwc2_ep_set_wedge, + .fifo_status = dwc2_ep_fifo_status, + .fifo_flush = dwc2_ep_fifo_flush, +}; + +static void dwc2_program_zlp(struct dwc2 *dwc2, struct dwc2_ep *hs_ep) +{ + u32 ctrl; + u8 index = hs_ep->epnum; + u32 epctl_reg = hs_ep->dir_in ? DIEPCTL(index) : DOEPCTL(index); + u32 epsiz_reg = hs_ep->dir_in ? DIEPTSIZ(index) : DOEPTSIZ(index); + + if (hs_ep->dir_in) + dwc2_dbg(dwc2, "Sending zero-length packet on ep%d\n", index); + else + dwc2_dbg(dwc2, "Receiving zero-length packet on ep%d\n", index); + + dwc2_writel(dwc2, DXEPTSIZ_MC(1) | DXEPTSIZ_PKTCNT(1) | + DXEPTSIZ_XFERSIZE(0), + epsiz_reg); + + ctrl = dwc2_readl(dwc2, epctl_reg); + ctrl |= DXEPCTL_CNAK; /* clear NAK set by core */ + ctrl |= DXEPCTL_EPENA; /* ensure ep enabled */ + ctrl |= DXEPCTL_USBACTEP; + dwc2_writel(dwc2, ctrl, epctl_reg); +} + +/** + * windex_to_ep - convert control wIndex value to endpoint + * @dwc2: The driver state. + * @windex: The control request wIndex field (in host order). + * + * Convert the given wIndex into a pointer to an driver endpoint + * structure, or return NULL if it is not a valid endpoint. + */ +static struct dwc2_ep *windex_to_ep(struct dwc2 *dwc2, u16 windex) +{ + struct dwc2_ep *ep; + int dir = (windex & USB_DIR_IN) ? 1 : 0; + int idx = windex & 0x7F; + + if (windex >= 0x100) + return NULL; + + if (idx >= dwc2->num_eps) + return NULL; + + ep = index_to_ep(dwc2, idx, dir); + + if (idx && ep->dir_in != dir) + return NULL; + + return ep; +} + +/** + * dwc2_hsotg_send_reply - send reply to control request + * @dwc2: The device state + * @buff: Buffer for request + * @length: Length of reply. + * + * Create a request and queue it on the given endpoint. This is useful as + * an internal method of sending replies to certain control requests, etc. + */ +static int dwc2_hsotg_send_reply(struct dwc2 *dwc2, void *buff, int length) +{ + struct dwc2_ep *ep0 = dwc2->eps_out[0]; + struct usb_request *req; + int ret; + + if (length == 0 && (dwc2->ep0_state == DWC2_EP0_STATUS_IN || + dwc2->ep0_state == DWC2_EP0_STATUS_OUT)) { + dwc2_program_zlp(dwc2, ep0); + return 0; + } + + dwc2_dbg(dwc2, "%s: buff %p, len %d\n", __func__, buff, length); + + req = dwc2_ep_alloc_req(&ep0->ep); + if (!req) { + dwc2_warn(dwc2, "%s: cannot alloc req\n", __func__); + return -ENOMEM; + } + + req->buf = dwc2->ep0_buff; + req->length = length; + /* + * zero flag is for sending zlp in DATA IN stage. It has no impact on + * STATUS stage. + */ + req->zero = 0; + req->complete = dwc2_ep_free_req; + + if (length) + memcpy(req->buf, buff, length); + + ret = dwc2_ep_queue(&ep0->ep, req); + if (ret) { + dwc2_warn(dwc2, "%s: cannot queue req\n", __func__); + return ret; + } + + return 0; +} + +/** + * dwc2_process_req_status - process request GET_STATUS + * @dwc2: The device state + * @ctrl: USB control request + */ +static int dwc2_process_req_status(struct dwc2 *dwc2, + struct usb_ctrlrequest *ctrl) +{ + struct dwc2_ep *ep0 = dwc2->eps_out[0]; + struct dwc2_ep *ep; + __le16 reply; + u16 status; + int ret; + + dwc2_dbg(dwc2, "%s: USB_REQ_GET_STATUS\n", __func__); + + if (!ep0->dir_in) { + dwc2_warn(dwc2, "%s: direction out?\n", __func__); + return -EINVAL; + } + + switch (ctrl->bRequestType & USB_RECIP_MASK) { + case USB_RECIP_DEVICE: + status = dwc2->is_selfpowered << USB_DEVICE_SELF_POWERED; + status |= 0 << USB_DEVICE_REMOTE_WAKEUP; + + reply = cpu_to_le16(status); + break; + + case USB_RECIP_INTERFACE: + /* currently, the data result should be zero */ + reply = cpu_to_le16(0); + break; + + case USB_RECIP_ENDPOINT: + ep = windex_to_ep(dwc2, le16_to_cpu(ctrl->wIndex)); + if (!ep) + return -ENOENT; + + reply = cpu_to_le16(ep->halted ? 1 : 0); + break; + + default: + return 0; + } + + if (le16_to_cpu(ctrl->wLength) != 2) + return -EINVAL; + + ret = dwc2_hsotg_send_reply(dwc2, &reply, 2); + if (ret) { + dwc2_err(dwc2, "%s: failed to send reply\n", __func__); + return ret; + } + + return 1; +} + +/** + * dwc2_process_req_feature - process request {SET,CLEAR}_FEATURE + * @dwc2: The device state + * @ctrl: USB control request + */ +static int dwc2_process_req_feature(struct dwc2 *dwc2, + struct usb_ctrlrequest *ctrl) +{ + struct dwc2_request *hs_req; + bool set = (ctrl->bRequest == USB_REQ_SET_FEATURE); + struct dwc2_ep *ep; + int ret; + bool halted; + u32 recip; + u16 wValue; + u16 wIndex; + + dwc2_dbg(dwc2, "%s: %s_FEATURE\n", __func__, set ? "SET" : "CLEAR"); + + wValue = le16_to_cpu(ctrl->wValue); + wIndex = le16_to_cpu(ctrl->wIndex); + recip = ctrl->bRequestType & USB_RECIP_MASK; + + switch (recip) { + case USB_RECIP_DEVICE: + switch (wValue) { + case USB_DEVICE_REMOTE_WAKEUP: + case USB_DEVICE_TEST_MODE: + /* Not supported */ + default: + return -ENOENT; + } + break; + + case USB_RECIP_ENDPOINT: + ep = windex_to_ep(dwc2, wIndex); + if (!ep) + return -ENOENT; + + switch (wValue) { + case USB_ENDPOINT_HALT: + halted = ep->halted; + + dwc2_ep_set_halt(&ep->ep, set); + + ret = dwc2_hsotg_send_reply(dwc2, NULL, 0); + if (ret) { + dwc2_err(dwc2, + "%s: failed to send reply\n", __func__); + return ret; + } + + /* + * we have to complete all requests for ep if it was + * halted, and the halt was cleared by CLEAR_FEATURE + */ + + if (!set && halted) { + /* + * If we have request in progress, + * then complete it + */ + if (ep->req) { + hs_req = ep->req; + ep->req = NULL; + list_del_init(&hs_req->queue); + if (hs_req->req.complete) { + spin_unlock(&dwc2->lock); + hs_req->req.complete( + &ep->ep, &hs_req->req); + spin_lock(&dwc2->lock); + } + } + + /* If we have pending request, then start it */ + if (!ep->req) + dwc2_start_next_request(ep); + } + + break; + + default: + return -ENOENT; + } + break; + default: + return -ENOENT; + } + return 1; +} + +static int dwc2_gadget_get_frame(struct usb_gadget *gadget) +{ + return dwc2_read_frameno(to_dwc2(gadget)); +} + +static void dwc2_core_disconnect(struct dwc2 *dwc2) +{ + u32 dctl; + + dctl = dwc2_readl(dwc2, DCTL); + /* set the soft-disconnect bit */ + dctl |= DCTL_SFTDISCON; + dwc2_writel(dwc2, dctl, DCTL); +} + +static void dwc2_core_connect(struct dwc2 *dwc2) +{ + u32 dctl; + + dctl = dwc2_readl(dwc2, DCTL); + /* clear the soft-disconnect bit */ + dctl &= ~DCTL_SFTDISCON; + dwc2_writel(dwc2, dctl, DCTL); +} + +static void dwc2_enqueue_setup(struct dwc2 *dwc2); + +/** + * dwc2_stall_ep0 - stall ep0 + * @dwc2: The device state + * + * Set stall for ep0 as response for setup request. + */ +static void dwc2_stall_ep0(struct dwc2 *dwc2) +{ + struct dwc2_ep *ep0 = dwc2->eps_out[0]; + u32 reg; + u32 ctrl; + + dwc2_dbg(dwc2, "ep0 stall (dir=%d)\n", ep0->dir_in); + reg = (ep0->dir_in) ? DIEPCTL0 : DOEPCTL0; + + /* + * DxEPCTL_Stall will be cleared by EP once it has + * taken effect, so no need to clear later. + */ + + ctrl = dwc2_readl(dwc2, reg); + ctrl |= DXEPCTL_STALL; + ctrl |= DXEPCTL_CNAK; + dwc2_writel(dwc2, ctrl, reg); + + dwc2_dbg(dwc2, + "written DXEPCTL=0x%08x to %08x (DXEPCTL=0x%08x)\n", + ctrl, reg, dwc2_readl(dwc2, reg)); + + /* + * complete won't be called, so we enqueue + * setup request here + */ + dwc2_enqueue_setup(dwc2); +} + +/** + * dwc2_process_control - process a control request + * @dwc2: The device state + * @ctrl: The control request received + * + * The controller has received the SETUP phase of a control request, and + * needs to work out what to do next (and whether to pass it on to the + * gadget driver). + */ +static void dwc2_process_control(struct dwc2 *dwc2, + struct usb_ctrlrequest *ctrl) +{ + struct dwc2_ep *ep0 = dwc2->eps_out[0]; + int handled = false; + int ret = 0; + + dwc2_dbg(dwc2, + "ctrl Type=%02x, Req=%02x, V=%04x, I=%04x, L=%04x\n", + ctrl->bRequestType, ctrl->bRequest, ctrl->wValue, + ctrl->wIndex, ctrl->wLength); + + if (ctrl->wLength == 0) { + ep0->dir_in = 1; + dwc2->ep0_state = DWC2_EP0_STATUS_IN; + } else if (ctrl->bRequestType & USB_DIR_IN) { + ep0->dir_in = 1; + dwc2->ep0_state = DWC2_EP0_DATA_IN; + } else { + ep0->dir_in = 0; + dwc2->ep0_state = DWC2_EP0_DATA_OUT; + } + + if ((ctrl->bRequestType & USB_TYPE_MASK) == USB_TYPE_STANDARD) { + switch (ctrl->bRequest) { + case USB_REQ_SET_ADDRESS: + dwc2->connected = 1; + dwc2_dcfg_set_addr(dwc2, le16_to_cpu(ctrl->wValue)); + ret = dwc2_hsotg_send_reply(dwc2, NULL, 0); + handled = true; + dwc2_info(dwc2, "new address %d\n", ctrl->wValue); + break; + + case USB_REQ_GET_STATUS: + ret = dwc2_process_req_status(dwc2, ctrl); + handled = true; + break; + + case USB_REQ_CLEAR_FEATURE: + case USB_REQ_SET_FEATURE: + ret = dwc2_process_req_feature(dwc2, ctrl); + handled = true; + break; + } + } + + /* as a fallback, try delivering it to the driver to deal with */ + if (!handled && dwc2->driver) { + spin_unlock(&dwc2->lock); + ret = dwc2->driver->setup(&dwc2->gadget, ctrl); + spin_lock(&dwc2->lock); + if (ret < 0) + dwc2_dbg(dwc2, "driver->setup() ret %d\n", ret); + } + + /* + * the request is either unhandlable, or is not formatted correctly + * so respond with a STALL for the status stage to indicate failure. + */ + + if (ret < 0) { + dwc2_dbg(dwc2, "unhandled ctrl request "); + dwc2_stall_ep0(dwc2); + } +} + +/** + * dwc2_complete_setup - completion of a setup transfer + * @ep: The endpoint the request was on. + * @req: The request completed. + * + * Called on completion of any requests the driver itself submitted for + * EP0 setup packets + */ +static void dwc2_complete_setup(struct usb_ep *ep, struct usb_request *req) +{ + struct dwc2_ep *hs_ep = our_ep(ep); + struct dwc2 *dwc2 = hs_ep->dwc2; + + if (req->status < 0) { + dwc2_dbg(dwc2, "%s: failed %d\n", __func__, req->status); + return; + } + + spin_lock(&dwc2->lock); + if (req->actual == 0) + dwc2_enqueue_setup(dwc2); + else + dwc2_process_control(dwc2, req->buf); + spin_unlock(&dwc2->lock); +} + +static void dwc2_enqueue_setup(struct dwc2 *dwc2) +{ + struct usb_request *req = dwc2->ctrl_req; + struct dwc2_request *hs_req = our_req(req); + int ret; + + dwc2_dbg(dwc2, "%s: queueing setup request\n", __func__); + + req->zero = 0; + req->length = 8; + req->buf = dwc2->ctrl_buff; + req->complete = dwc2_complete_setup; + + if (!list_empty(&hs_req->queue)) { + dwc2_dbg(dwc2, "%s already queued???\n", __func__); + return; + } + + dwc2->eps_out[0]->dir_in = 0; + dwc2->eps_out[0]->send_zlp = 0; + dwc2->ep0_state = DWC2_EP0_SETUP; + + ret = dwc2_ep_queue(&dwc2->eps_out[0]->ep, req); + if (ret < 0) { + dwc2_err(dwc2, "%s: failed queue (%d)\n", __func__, ret); + /* + * Don't think there's much we can do other than watch the + * driver fail. + */ + } +} + +/** + * dwc2_complete_request - complete a request given to us + * @dwc2: The device state. + * @hs_ep: The endpoint the request was on. + * @hs_req: The request to complete. + * @result: The result code (0 => Ok, otherwise errno) + * + * The given request has finished, so call the necessary completion + * if it has one and then look to see if we can start a new request + * on the endpoint. + * + * Note, expects the ep to already be locked as appropriate. + */ +static void dwc2_complete_request(struct dwc2 *dwc2, struct dwc2_ep *hs_ep, + struct dwc2_request *hs_req, int status) +{ + if (!hs_req) { + dwc2_dbg(dwc2, "%s: nothing to complete?\n", __func__); + return; + } + + dwc2_dbg(dwc2, "complete: ep %p %s, req %p, %d => %p\n", + hs_ep, hs_ep->ep.name, hs_req, status, hs_req->req.complete); + + /* + * only replace the status if we've not already set an error + * from a previous transaction + */ + + if (hs_req->req.status == -EINPROGRESS) + hs_req->req.status = status; + + dma_unmap_single(dwc2->dev, hs_req->req.dma, hs_req->req.length, + hs_ep->dir_in ? DMA_TO_DEVICE : DMA_FROM_DEVICE); + hs_ep->req = NULL; + list_del_init(&hs_req->queue); + + /* + * call the complete request with the locks off, just in case the + * request tries to queue more work for this endpoint. + */ + + if (hs_req->req.complete) { + spin_unlock(&dwc2->lock); + hs_req->req.complete(&hs_ep->ep, &hs_req->req); + spin_lock(&dwc2->lock); + } + + /* + * Look to see if there is anything else to do. Note, the completion + * of the previous request may have caused a new request to be started + * so be careful when doing this. + */ + dwc2_dbg(dwc2, "%s: req %p, status %d\n", __func__, hs_ep->req, status); + if (!hs_ep->req && status >= 0) + dwc2_start_next_request(hs_ep); +} + +/** + * dwc2_handle_outdone - handle receiving OutDone/SetupDone from RXFIFO + * @dwc2: The device instance + * @epnum: The endpoint received from + * + * The RXFIFO has delivered an OutDone event, which means that the data + * transfer for an OUT endpoint has been completed, either by a short + * packet or by the finish of a transfer. + */ +static void dwc2_handle_outdone(struct dwc2 *dwc2, int epnum) +{ + u32 epsize = dwc2_readl(dwc2, DOEPTSIZ(epnum)); + struct dwc2_ep *hs_ep = dwc2->eps_out[epnum]; + struct dwc2_request *hs_req = hs_ep->req; + struct usb_request *req = &hs_req->req; + unsigned int size_left = DXEPTSIZ_XFERSIZE_GET(epsize); + int result = 0; + + if (!hs_req) { + dwc2_dbg(dwc2, "%s: no request active\n", __func__); + return; + } + + if (epnum == 0 && dwc2->ep0_state == DWC2_EP0_STATUS_OUT) { + dwc2_dbg(dwc2, "zlp packet received\n"); + dwc2_complete_request(dwc2, hs_ep, hs_req, 0); + dwc2_enqueue_setup(dwc2); + return; + } + + if (using_dma(dwc2)) { + unsigned int size_done; + + /* + * Calculate the size of the transfer by checking how much + * is left in the endpoint size register and then working it + * out from the amount we loaded for the transfer. + * + * We need to do this as DMA pointers are always 32bit aligned + * so may overshoot/undershoot the transfer. + */ + size_done = hs_ep->size_loaded - size_left; + size_done += hs_ep->last_load; + + req->actual = size_done; + } + + if (req->actual < req->length && size_left == 0) { + dwc2_gadget_start_req(dwc2, hs_ep, hs_req, true); + return; + } + + if (req->actual < req->length && req->short_not_ok) { + dwc2_dbg(dwc2, "%s: got %d/%d (short not ok) => error\n", + __func__, req->actual, req->length); + + /* + * todo - what should we return here? there's no one else + * even bothering to check the status. + */ + } + + if (epnum == 0 && dwc2->ep0_state == DWC2_EP0_DATA_OUT) { + /* Move to STATUS IN */ + dwc2->eps_out[0]->dir_in = 1; + dwc2->ep0_state = DWC2_EP0_STATUS_IN; + dwc2_program_zlp(dwc2, dwc2->eps_out[0]); + } + + /* + * Slave mode OUT transfers do not go through XferComplete so + * adjust the ISOC parity here. + */ +#if 0 + if (!using_dma(dwc2)) { + if (hs_ep->isochronous && hs_ep->interval == 1) + dwc2_hsotg_change_ep_iso_parity(dwc2, DOEPCTL(epnum)); + else if (hs_ep->isochronous && hs_ep->interval > 1) + dwc2_gadget_incr_frame_num(hs_ep); + } + + /* Set actual frame number for completed transfers */ + if (hs_ep->isochronous) + req->frame_number = dwc2->frame_number; +#endif + + dwc2_complete_request(dwc2, hs_ep, hs_req, result); +} + +/** + * kill_all_requests - remove all requests from the endpoint's queue + * @dwc2: The device state. + * @ep: The endpoint the requests may be on. + * @result: The result code to use. + * + * Go through the requests on the given endpoint and mark them + * completed with the given result code. + */ +static void kill_all_requests(struct dwc2 *dwc2, struct dwc2_ep *ep, int result) +{ + struct dwc2_request *req, *treq; + unsigned int size; + + ep->req = NULL; + + list_for_each_entry_safe(req, treq, &ep->queue, queue) + dwc2_complete_request(dwc2, ep, req, result); + + if (!dwc2->dedicated_fifos) + return; + size = (dwc2_readl(dwc2, DTXFSTS(ep->fifo_index)) & 0xffff) * 4; + if (size < ep->fifo_size) + dwc2_flush_tx_fifo(dwc2, ep->fifo_index); +} + +/** + * dwc2_gadget_disconnect - disconnect service + * @dwc2: The device state. + * + * The device has been disconnected. Remove all current + * transactions and signal the gadget driver that this + * has happened. + */ +static void dwc2_gadget_disconnect(struct dwc2 *dwc2) +{ + + unsigned int ep; + + if (!dwc2->connected) + return; + + dwc2->connected = 0; + + /* all endpoints should be shutdown */ + for (ep = 0; ep < dwc2->num_eps; ep++) { + if (dwc2->eps_in[ep]) + kill_all_requests(dwc2, dwc2->eps_in[ep], -ESHUTDOWN); + if (dwc2->eps_out[ep]) + kill_all_requests(dwc2, dwc2->eps_out[ep], -ESHUTDOWN); + } + + spin_unlock(&dwc2->lock); + dwc2->driver->disconnect(&dwc2->gadget); + spin_lock(&dwc2->lock); + + usb_gadget_set_state(&dwc2->gadget, USB_STATE_NOTATTACHED); +} + +static void dwc2_gadget_setup_fifo(struct dwc2 *dwc2) +{ + unsigned int ep; + unsigned int addr; + u32 np_tx_fifo_size = dwc2->params.g_np_tx_fifo_size; + u32 rx_fifo_size = dwc2->params.g_rx_fifo_size; + u32 fifo_size = dwc2->hw_params.total_fifo_size; + u32 *tx_fifo_size = dwc2->params.g_tx_fifo_size; + u32 size, depth; + u32 txfsz; + + /* Reset fifo map if not correctly cleared during previous session */ + WARN_ON(dwc2->fifo_map); + dwc2->fifo_map = 0; + + /* set RX/NPTX FIFO sizes */ + dwc2_writel(dwc2, rx_fifo_size, GRXFSIZ); + size = rx_fifo_size << FIFOSIZE_STARTADDR_SHIFT; + size |= np_tx_fifo_size << FIFOSIZE_DEPTH_SHIFT; + dwc2_writel(dwc2, size, GNPTXFSIZ); + + /* + * arange all the rest of the TX FIFOs, as some versions of this + * block have overlapping default addresses. This also ensures + * that if the settings have been changed, then they are set to + * known values. + */ + + /* start at the end of the GNPTXFSIZ, rounded up */ + addr = rx_fifo_size + np_tx_fifo_size; + + /* + * Configure fifos sizes from provided configuration and assign + * them to endpoints dynamically according to maxpacket size value of + * given endpoint. + */ + + for (ep = 1; ep < dwc2->num_eps; ep++) { + txfsz = dwc2_readl(dwc2, DPTXFSIZN(ep)); + depth = tx_fifo_size[ep]; + + if (addr + depth > fifo_size) + dwc2_err(dwc2, "insufficient fifo memory\n"); + + txfsz = depth << FIFOSIZE_DEPTH_SHIFT; + txfsz |= addr & 0xffff; + dwc2_writel(dwc2, txfsz, DPTXFSIZN(ep)); + + addr += depth; + } + + dwc2_writel(dwc2, dwc2->hw_params.total_fifo_size | + addr << GDFIFOCFG_EPINFOBASE_SHIFT, + GDFIFOCFG); + + dwc2_flush_all_fifo(dwc2); +} + +static void dwc2_gadget_interrupt_init(struct dwc2 *dwc2) +{ + /* unmask subset of endpoint interrupts */ + dwc2_writel(dwc2, DIEPMSK_TIMEOUTMSK | DIEPMSK_AHBERRMSK | + DIEPMSK_EPDISBLDMSK | DIEPMSK_XFERCOMPLMSK, + DIEPMSK); + + dwc2_writel(dwc2, DOEPMSK_SETUPMSK | DOEPMSK_AHBERRMSK | + DOEPMSK_EPDISBLDMSK | DOEPMSK_XFERCOMPLMSK, + DOEPMSK); + + dwc2_writel(dwc2, 0, DAINTMSK); +} + +/** + * dwc2_complete_in - complete IN transfer + * @dwc2: The device state. + * @hs_ep: The endpoint that has just completed. + * + * An IN transfer has been completed, update the transfer's state and then + * call the relevant completion routines. + */ +static void dwc2_complete_in(struct dwc2 *dwc2, struct dwc2_ep *hs_ep) +{ + struct dwc2_request *hs_req = hs_ep->req; + u32 epsize = dwc2_readl(dwc2, DIEPTSIZ(hs_ep->epnum)); + int size_left, size_done; + + if (!hs_req) { + dwc2_dbg(dwc2, "XferCompl but no req\n"); + return; + } + + /* Finish ZLP handling for IN EP0 transactions */ + if (hs_ep->epnum == 0 && dwc2->ep0_state == DWC2_EP0_STATUS_IN) { + dwc2_dbg(dwc2, "zlp packet sent\n"); + /* + * While send zlp for DWC2_EP0_STATUS_IN EP direction was + * changed to IN. Change back to complete OUT transfer request + */ + hs_ep->dir_in = 0; + dwc2_complete_request(dwc2, hs_ep, hs_req, 0); + dwc2_enqueue_setup(dwc2); + return; + } + + /* + * Calculate the size of the transfer by checking how much is left + * in the endpoint size register and then working it out from + * the amount we loaded for the transfer. + * + * We do this even for DMA, as the transfer may have incremented + * past the end of the buffer (DMA transfers are always 32bit + * aligned). + */ + size_left = DXEPTSIZ_XFERSIZE_GET(epsize); + + size_done = hs_ep->size_loaded - size_left; + size_done += hs_ep->last_load; + + if (hs_req->req.actual != size_done) + dwc2_dbg(dwc2, "%s: adjusting size done %d => %d\n", + __func__, hs_req->req.actual, size_done); + + hs_req->req.actual = size_done; + dwc2_dbg(dwc2, "req->length:%d req->actual:%d req->zero:%d\n", + hs_req->req.length, hs_req->req.actual, hs_req->req.zero); + + if (!size_left && hs_req->req.actual < hs_req->req.length) { + dwc2_dbg(dwc2, "%s trying more for req...\n", __func__); + dwc2_gadget_start_req(dwc2, hs_ep, hs_req, true); + return; + } + + /* Zlp for all endpoints, for ep0 only in DATA IN stage */ + if (hs_ep->send_zlp) { + dwc2_program_zlp(dwc2, hs_ep); + hs_ep->send_zlp = 0; + /* transfer will be completed on next complete interrupt */ + return; + } + + if (hs_ep->epnum == 0 && dwc2->ep0_state == DWC2_EP0_DATA_IN) { + /* Move to STATUS OUT */ + dwc2->eps_out[0]->dir_in = 0; + dwc2->ep0_state = DWC2_EP0_STATUS_OUT; + dwc2_program_zlp(dwc2, dwc2->eps_out[0]); + return; + } + + dwc2_complete_request(dwc2, hs_ep, hs_req, 0); +} + +/** + * dwc2_core_gadget_init - issue softreset to the core + * @dwc2: The device state + * @usb_reset: Usb resetting flag + * + * Issue a soft reset to the core, and await the core finishing it. + */ +static void dwc2_core_gadget_init(struct dwc2 *dwc2, bool usb_reset) +{ + u32 intmsk; + u32 dctl; + u32 usbcfg; + u32 dcfg; + int ep; + + /* Kill any ep0 requests as controller will be reinitialized */ + kill_all_requests(dwc2, dwc2->eps_out[0], -ECONNRESET); + + if (!usb_reset) { + if (dwc2_core_reset(dwc2)) + return; + } else { + /* all endpoints should be shutdown */ + for (ep = 1; ep < dwc2->num_eps; ep++) { + if (dwc2->eps_in[ep]) + dwc2_ep_disable(&dwc2->eps_in[ep]->ep); + if (dwc2->eps_out[ep]) + dwc2_ep_disable(&dwc2->eps_out[ep]->ep); + } + } + + /* + * we must now enable ep0 ready for host detection and then + * set configuration. + */ + + /* keep other bits untouched (so e.g. forced modes are not lost) */ + usbcfg = dwc2_readl(dwc2, GUSBCFG); + usbcfg &= ~GUSBCFG_TOUTCAL_MASK; + usbcfg |= GUSBCFG_TOUTCAL(7); + dwc2_writel(dwc2, usbcfg, GUSBCFG); + + dwc2_phy_init(dwc2, true); + + dwc2_gadget_setup_fifo(dwc2); + + if (!usb_reset) + dwc2_core_disconnect(dwc2); + + dcfg = DCFG_EPMISCNT(1); + + if (dwc2->params.speed == DWC2_SPEED_PARAM_LOW) { + dcfg |= DCFG_DEVSPD_LS; + } else if (dwc2->params.speed == DWC2_SPEED_PARAM_FULL && + dwc2->params.phy_type == DWC2_PHY_TYPE_PARAM_FS) { + dcfg |= DCFG_DEVSPD_FS48; + } else if (dwc2->params.speed == DWC2_SPEED_PARAM_FULL && + dwc2->params.phy_type != DWC2_PHY_TYPE_PARAM_FS) { + dcfg |= DCFG_DEVSPD_FS; + } + + if (dwc2->params.ipg_isoc_en) + dcfg |= DCFG_IPG_ISOC_SUPPORDED; + + dwc2_writel(dwc2, dcfg, DCFG); + + /* Clear any pending OTG interrupts */ + dwc2_writel(dwc2, 0xffffffff, GOTGINT); + + /* Clear any pending interrupts */ + dwc2_writel(dwc2, 0xffffffff, GINTSTS); + intmsk = GINTSTS_ERLYSUSP | + GINTSTS_GOUTNAKEFF | GINTSTS_GINNAKEFF | + GINTSTS_USBRST | GINTSTS_RESETDET | + GINTSTS_ENUMDONE | + GINTSTS_USBSUSP | GINTSTS_WKUPINT | + GINTSTS_LPMTRANRCVD; + + intmsk |= GINTSTS_INCOMPL_SOIN | GINTSTS_INCOMPL_SOOUT; + + /* enable in and out endpoint interrupts */ + intmsk |= GINTSTS_OEPINT | GINTSTS_IEPINT; + + /* + * Enable the RXFIFO when in slave mode, as this is how we collect + * the data. In DMA mode, we get events from the FIFO but also + * things we cannot process, so do not use it. + */ + if (!using_dma(dwc2)) + intmsk |= GINTSTS_RXFLVL; + + if (!dwc2->params.external_id_pin_ctl) + intmsk |= GINTSTS_CONIDSTSCHNG; + + dwc2_writel(dwc2, intmsk, GINTMSK); + + dwc2_gahbcfg_init(dwc2); + + /* + * If INTknTXFEmpMsk is enabled, it's important to disable ep interrupts + * when we have no data to transfer. Otherwise we get being flooded by + * interrupts. + */ + intmsk = DIEPMSK_EPDISBLDMSK | DIEPMSK_XFERCOMPLMSK | + DIEPMSK_TIMEOUTMSK | DIEPMSK_AHBERRMSK; + + if (dwc2->dedicated_fifos && !using_dma(dwc2)) + intmsk |= DIEPMSK_TXFIFOEMPTY | DIEPMSK_INTKNTXFEMPMSK; + + dwc2_writel(dwc2, intmsk, DIEPMSK); + + /* + * don't need XferCompl, we get that from RXFIFO in slave mode. In + * DMA mode we may need this and StsPhseRcvd. + */ + intmsk = DOEPMSK_EPDISBLDMSK | DOEPMSK_AHBERRMSK | DOEPMSK_SETUPMSK; + if (using_dma(dwc2)) + intmsk |= DIEPMSK_XFERCOMPLMSK | DOEPMSK_STSPHSERCVDMSK; + dwc2_writel(dwc2, intmsk, DOEPMSK); + + dwc2_writel(dwc2, 0, DAINTMSK); + + dwc2_dbg(dwc2, "EP0: DIEPCTL0=0x%08x, DOEPCTL0=0x%08x\n", + dwc2_readl(dwc2, DIEPCTL0), + dwc2_readl(dwc2, DOEPCTL0)); + + /* Enable interrupts for EP0 in and out */ + dwc2_hsotg_ctrl_epint(dwc2, 0, 0, 1); + dwc2_hsotg_ctrl_epint(dwc2, 0, 1, 1); + dwc2_dbg(dwc2, "DAINTMSK=0x%08x\n", dwc2_readl(dwc2, DAINTMSK)); + + if (!usb_reset) { + dctl = dwc2_readl(dwc2, DCTL); + dwc2_writel(dwc2, dctl | DCTL_PWRONPRGDONE, DCTL); + + udelay(10); /* see openiboot */ + + dctl = dwc2_readl(dwc2, DCTL); + dwc2_writel(dwc2, dctl & ~DCTL_PWRONPRGDONE, DCTL); + } + + dwc2_dbg(dwc2, "DCTL=0x%08x\n", dwc2_readl(dwc2, DCTL)); + + /* + * DxEPCTL_USBActEp says RO in manual, but seems to be set by + * writing to the EPCTL register.. + */ + dwc2_writel(dwc2, dwc2_ep0_mps(dwc2->eps_out[0]->ep.maxpacket) | + DXEPCTL_CNAK | DXEPCTL_EPENA | + DXEPCTL_USBACTEP, DOEPCTL0); + + /* enable, but don't activate EP0in */ + dwc2_writel(dwc2, dwc2_ep0_mps(dwc2->eps_out[0]->ep.maxpacket) | + DXEPCTL_USBACTEP, DIEPCTL0); + + /* clear global NAKs */ + dctl = dwc2_readl(dwc2, DCTL); + dctl |= DCTL_CGOUTNAK | DCTL_CGNPINNAK; + + if (!usb_reset) + dctl |= DCTL_SFTDISCON; + dwc2_writel(dwc2, dctl, DCTL); + + /* must be at-least 3ms to allow bus to see disconnect */ + mdelay(3); +} + +/** + * dwc2_set_selfpowered - set if device is self/bus powered + * @gadget: The usb gadget state + * @is_selfpowered: Whether the device is self-powered + * + * Set if the device is self or bus powered. + */ +static int dwc2_set_selfpowered(struct usb_gadget *gadget, int is_selfpowered) +{ + struct dwc2 *dwc2 = to_dwc2(gadget); + + dwc2->is_selfpowered = !!is_selfpowered; + + return 0; +} + +/** + * dwc2_gadget_pullup - connect/disconnect the USB PHY + * @gadget: The usb gadget state + * @is_on: Current state of the USB PHY + * + * Connect/Disconnect the USB PHY pullup + */ +static int dwc2_gadget_pullup(struct usb_gadget *gadget, int is_on) +{ + struct dwc2 *dwc2 = to_dwc2(gadget); + unsigned long flags = 0; + + dwc2_dbg(dwc2, "%s: is_on: %d\n", __func__, is_on); + + dwc2->enabled = is_on; + /* Don't modify pullup state while in host mode */ + if (dwc2_is_host_mode(dwc2)) { + WARN_ON(dwc2_is_host_mode(dwc2)); + return -EINVAL; + } + + spin_lock_irqsave(&dwc2->lock, flags); + if (is_on) { + dwc2_core_gadget_init(dwc2, false); + /* Enable ACG feature in device mode,if supported */ + dwc2_core_connect(dwc2); + } else { + dwc2_core_disconnect(dwc2); + dwc2_gadget_disconnect(dwc2); + } + + dwc2->gadget.speed = USB_SPEED_UNKNOWN; + spin_unlock_irqrestore(&dwc2->lock, flags); + + return 0; +} + +static int dwc2_gadget_vbus_session(struct usb_gadget *gadget, int is_active) +{ + struct dwc2 *dwc2 = to_dwc2(gadget); + unsigned long flags; + + dwc2_dbg(dwc2, "%s: is_active: %d\n", __func__, is_active); + spin_lock_irqsave(&dwc2->lock, flags); + + if (is_active) { + dwc2_core_gadget_init(dwc2, false); + if (dwc2->enabled) + dwc2_core_connect(dwc2); + } else { + dwc2_core_disconnect(dwc2); + dwc2_gadget_disconnect(dwc2); + } + + spin_unlock_irqrestore(&dwc2->lock, flags); + return 0; +} + + +/** + * dwc2_handle_ep_disabled - handle DXEPINT_EPDISBLD + * @hs_ep: The endpoint on which interrupt is asserted. + * + * This interrupt indicates that the endpoint has been disabled per the + * application's request. + * + * For IN endpoints flushes txfifo, in case of BULK clears DCTL_CGNPINNAK, + * in case of ISOC completes current request. + * + * For ISOC-OUT endpoints completes expired requests. If there is remaining + * request starts it. + */ +static void dwc2_handle_ep_disabled(struct dwc2_ep *hs_ep) +{ + struct dwc2 *dwc2 = hs_ep->dwc2; + struct dwc2_request *hs_req; + unsigned char idx = hs_ep->epnum; + int dir_in = hs_ep->dir_in; + u32 epctl_reg = dir_in ? DIEPCTL(idx) : DOEPCTL(idx); + int dctl = dwc2_readl(dwc2, DCTL); + + dwc2_dbg(dwc2, "%s: EPDisbld\n", __func__); + + if (dir_in) { + int epctl = dwc2_readl(dwc2, epctl_reg); + + dwc2_flush_tx_fifo(dwc2, hs_ep->fifo_index); + + if (hs_ep->isochronous) { + dwc2_complete_in(dwc2, hs_ep); + return; + } + + if ((epctl & DXEPCTL_STALL) && (epctl & DXEPCTL_EPTYPE_BULK)) { + int dctl = dwc2_readl(dwc2, DCTL); + + dctl |= DCTL_CGNPINNAK; + dwc2_writel(dwc2, dctl, DCTL); + } + return; + } + + if (dctl & DCTL_GOUTNAKSTS) { + dctl |= DCTL_CGOUTNAK; + dwc2_writel(dwc2, dctl, DCTL); + } + + if (!hs_ep->isochronous) + return; + + if (list_empty(&hs_ep->queue)) { + dwc2_dbg(dwc2, "%s: complete_ep 0x%p, ep->queue empty!\n", + __func__, hs_ep); + return; + } + + do { + hs_req = get_ep_head(hs_ep); + if (hs_req) + dwc2_complete_request(dwc2, hs_ep, hs_req, -ENODATA); + dwc2_gadget_incr_frame_num(hs_ep); + /* Update current frame number value. */ + dwc2->frame_number = dwc2_read_frameno(dwc2); + } while (dwc2_gadget_target_frame_elapsed(hs_ep)); + + dwc2_start_next_request(hs_ep); +} + +/** + * dwc2_handle_out_token_ep_disabled - handle DXEPINT_OUTTKNEPDIS + * @ep: The endpoint on which interrupt is asserted. + * + * This is starting point for ISOC-OUT transfer, synchronization done with + * first out token received from host while corresponding EP is disabled. + * + * Device does not know initial frame in which out token will come. For this + * HW generates OUTTKNEPDIS - out token is received while EP is disabled. Upon + * getting this interrupt SW starts calculation for next transfer frame. + */ +static void dwc2_handle_out_token_ep_disabled(struct dwc2_ep *ep) +{ + struct dwc2 *dwc2 = ep->dwc2; + int dir_in = ep->dir_in; + u32 doepmsk; + u32 ctrl; + + dwc2_dbg(dwc2, "%s\n", __func__); + + if (dir_in || !ep->isochronous) + return; + + if (ep->interval > 1 && ep->target_frame == TARGET_FRAME_INITIAL) { + ep->target_frame = dwc2->frame_number; + + dwc2_gadget_incr_frame_num(ep); + + ctrl = dwc2_readl(dwc2, DOEPCTL(ep->epnum)); + + if (ep->target_frame & 0x1) + ctrl |= DXEPCTL_SETODDFR; + else + ctrl |= DXEPCTL_SETEVENFR; + + dwc2_writel(dwc2, ctrl, DOEPCTL(ep->epnum)); + } + + dwc2_start_next_request(ep); + + doepmsk = dwc2_readl(dwc2, DOEPMSK); + doepmsk &= ~DOEPMSK_OUTTKNEPDISMSK; + dwc2_writel(dwc2, doepmsk, DOEPMSK); +} + +/** + * dwc2_handle_nak - handle NAK interrupt + * @hs_ep: The endpoint on which interrupt is asserted. + * + * This is starting point for ISOC-IN transfer, synchronization done with + * first IN token received from host while corresponding EP is disabled. + * + * Device does not know when first one token will arrive from host. On first + * token arrival HW generates 2 interrupts: 'in token received while FIFO empty' + * and 'NAK'. NAK interrupt for ISOC-IN means that token has arrived and ZLP was + * sent in response to that as there was no data in FIFO. SW is basing on this + * interrupt to obtain frame in which token has come and then based on the + * interval calculates next frame for transfer. + */ +static void dwc2_handle_nak(struct dwc2_ep *hs_ep) +{ + struct dwc2 *dwc2 = hs_ep->dwc2; + int dir_in = hs_ep->dir_in; + u32 ctrl; + + if (!dir_in || !hs_ep->isochronous) + return; + + if (hs_ep->target_frame == TARGET_FRAME_INITIAL) { + hs_ep->target_frame = dwc2->frame_number; + if (hs_ep->interval > 1) { + ctrl = dwc2_readl(dwc2, DIEPCTL(hs_ep->epnum)); + + if (hs_ep->target_frame & 0x1) + ctrl |= DXEPCTL_SETODDFR; + else + ctrl |= DXEPCTL_SETEVENFR; + + dwc2_writel(dwc2, ctrl, DIEPCTL(hs_ep->epnum)); + } + + dwc2_complete_request(dwc2, hs_ep, get_ep_head(hs_ep), 0); + } + + dwc2_gadget_incr_frame_num(hs_ep); +} + +/** + * dwc2_handle_epint - handle an in/out endpoint interrupt + * @dwc2: The driver state + * @idx: The index for the endpoint (0..15) + * @dir_in: Set if this is an IN endpoint + * + * Process and clear any interrupt pending for an individual endpoint + */ +static void dwc2_handle_epint(struct dwc2 *dwc2, unsigned int idx, + int dir_in) +{ + struct dwc2_ep *hs_ep = index_to_ep(dwc2, idx, dir_in); + u32 epmsk_reg = dir_in ? DIEPMSK : DOEPMSK; + u32 epint_reg = dir_in ? DIEPINT(idx) : DOEPINT(idx); + u32 epctl_reg = dir_in ? DIEPCTL(idx) : DOEPCTL(idx); + u32 epsiz_reg = dir_in ? DIEPTSIZ(idx) : DOEPTSIZ(idx); + u32 ints; + u32 mask; + u32 ctrl; + + mask = dwc2_readl(dwc2, epmsk_reg); + if ((dwc2_readl(dwc2, DIEPEMPMSK) >> idx) & 1) + mask |= DIEPMSK_TXFIFOEMPTY; + mask |= DXEPINT_SETUP_RCVD; + + ints = dwc2_readl(dwc2, epint_reg); + + if (ints & DXEPINT_AHBERR) + dwc2_err(dwc2, "%s: AHBErr\n", __func__); + + ints &= mask; + + ctrl = dwc2_readl(dwc2, epctl_reg); + + /* Clear endpoint interrupts */ + dwc2_writel(dwc2, ints, epint_reg); + + if (!hs_ep) { + dwc2_err(dwc2, "%s:Interrupt for unconfigured ep%d(%s)\n", + __func__, idx, dir_in ? "in" : "out"); + return; + } + + dwc2_dbg(dwc2, "%s: ep%d(%s) DxEPINT=0x%08x\n", + __func__, idx, dir_in ? "in" : "out", ints); + + /* Don't process XferCompl interrupt if it is a setup packet */ + if (idx == 0 && (ints & (DXEPINT_SETUP | DXEPINT_SETUP_RCVD))) + ints &= ~DXEPINT_XFERCOMPL; + + if (ints & DXEPINT_XFERCOMPL) { + dwc2_dbg(dwc2, + "%s: XferCompl: DxEPCTL=0x%08x, DXEPTSIZ=%08x\n", + __func__, dwc2_readl(dwc2, epctl_reg), + dwc2_readl(dwc2, epsiz_reg)); + if (dir_in) { + /* + * We get OutDone from the FIFO, so we only + * need to look at completing IN requests here + * if operating slave mode + */ + if (hs_ep->isochronous && hs_ep->interval > 1) + dwc2_gadget_incr_frame_num(hs_ep); + + dwc2_complete_in(dwc2, hs_ep); + if (ints & DXEPINT_NAKINTRPT) + ints &= ~DXEPINT_NAKINTRPT; + + if (idx == 0 && !hs_ep->req) + dwc2_enqueue_setup(dwc2); + } else if (using_dma(dwc2)) { + /* + * We're using DMA, we need to fire an OutDone here + * as we ignore the RXFIFO. + */ + if (hs_ep->isochronous && hs_ep->interval > 1) + dwc2_gadget_incr_frame_num(hs_ep); + + dwc2_handle_outdone(dwc2, idx); + } + } + + if (ints & DXEPINT_EPDISBLD) + dwc2_handle_ep_disabled(hs_ep); + + if (ints & DXEPINT_OUTTKNEPDIS) + dwc2_handle_out_token_ep_disabled(hs_ep); + + if (ints & DXEPINT_NAKINTRPT) + dwc2_handle_nak(hs_ep); + + if (ints & DXEPINT_SETUP) { /* Setup or Timeout */ + dwc2_dbg(dwc2, "%s: Setup/Timeout\n", __func__); + + if (using_dma(dwc2) && idx == 0) { + /* + * this is the notification we've received a + * setup packet. In non-DMA mode we'd get this + * from the RXFIFO, instead we need to process + * the setup here. + */ + + if (!dir_in && dwc2->ep0_state == DWC2_EP0_SETUP) + dwc2_handle_outdone(dwc2, 0); + } + } + + if (ints & DXEPINT_STSPHSERCVD) + dwc2_dbg(dwc2, "%s: StsPhseRcvd\n", __func__); + + if (ints & DXEPINT_BACK2BACKSETUP) + dwc2_dbg(dwc2, "%s: B2BSetup/INEPNakEff\n", __func__); + + if (ints & DXEPINT_BNAINTR) { + dwc2_dbg(dwc2, "%s: BNA interrupt\n", __func__); +#if 0 + if (hs_ep->isochronous) + dwc2_handle_isoc_bna(hs_ep); +#endif + } + + if (dir_in && !hs_ep->isochronous) { + /* not sure if this is important, but we'll clear it anyway */ + if (ints & DXEPINT_INTKNTXFEMP) { + dwc2_dbg(dwc2, "%s: ep%d: INTknTXFEmpMsk\n", + __func__, idx); + } + + /* this probably means something bad is happening */ + if (ints & DXEPINT_INTKNEPMIS) { + dwc2_warn(dwc2, "%s: ep%d: INTknEP\n", + __func__, idx); + } + + /* FIFO has space or is empty (see GAHBCFG) */ + if (dwc2->dedicated_fifos && ints & DXEPINT_TXFEMP) { + dwc2_dbg(dwc2, "%s: ep%d: TxFIFOEmpty\n", + __func__, idx); + } + } +} + +/** + * dwc2_handle_enumdone - Handle EnumDone interrupt (enumeration done) + * @dwc2: The device state. + * + * Handle updating the device settings after the enumeration phase has + * been completed. + */ +static void dwc2_handle_enumdone(struct dwc2 *dwc2) +{ + u32 dsts = dwc2_readl(dwc2, DSTS); + int ep0_mps = 0, ep_mps = 8; + int i; + + /* + * This should signal the finish of the enumeration phase + * of the USB handshaking, so we should now know what rate + * we connected at. + */ + dwc2_dbg(dwc2, "EnumDone (DSTS=0x%08x)\n", dsts); + + /* + * note, since we're limited by the size of transfer on EP0, and + * it seems IN transfers must be a even number of packets we do + * not advertise a 64byte MPS on EP0. + */ + + /* catch both EnumSpd_FS and EnumSpd_FS48 */ + switch ((dsts & DSTS_ENUMSPD_MASK) >> DSTS_ENUMSPD_SHIFT) { + case DSTS_ENUMSPD_FS: + case DSTS_ENUMSPD_FS48: + dwc2->gadget.speed = USB_SPEED_FULL; + ep0_mps = 64; + ep_mps = 1023; + break; + case DSTS_ENUMSPD_HS: + dwc2->gadget.speed = USB_SPEED_HIGH; + ep0_mps = 64; + ep_mps = 1024; + break; + case DSTS_ENUMSPD_LS: + dwc2->gadget.speed = USB_SPEED_LOW; + ep0_mps = 8; + ep_mps = 8; + /* + * note, we don't actually support LS in this driver at the + * moment, and the documentation seems to imply that it isn't + * supported by the PHYs on some of the devices. + */ + break; + } + dwc2_dbg(dwc2, "new %s device\n", usb_speed_string(dwc2->gadget.speed)); + + /* + * we should now know the maximum packet size for an + * endpoint, so set the endpoints to a default value. + */ + if (ep0_mps) { + /* Initialize ep0 for both in and out directions */ + dwc2_set_ep_maxpacket(dwc2, 0, ep0_mps, 0, 1); + dwc2_set_ep_maxpacket(dwc2, 0, ep0_mps, 0, 0); + for (i = 1; i < dwc2->num_eps; i++) { + if (dwc2->eps_in[i]) + dwc2_set_ep_maxpacket(dwc2, i, ep_mps, 0, 1); + if (dwc2->eps_out[i]) + dwc2_set_ep_maxpacket(dwc2, i, ep_mps, 0, 0); + } + } + + /* ensure after enumeration our EP0 is active */ + dwc2_enqueue_setup(dwc2); + + dwc2_dbg(dwc2, "EP0: DIEPCTL0=0x%08x, DOEPCTL0=0x%08x\n", + dwc2_readl(dwc2, DIEPCTL0), + dwc2_readl(dwc2, DOEPCTL0)); +} + +/* IRQ flags which will trigger a retry around the IRQ loop */ +#define IRQ_RETRY_MASK (GINTSTS_NPTXFEMP | \ + GINTSTS_PTXFEMP | \ + GINTSTS_RXFLVL) + +static void dwc2_gadget_udc_poll(struct usb_gadget *gadget) +{ + struct dwc2 *dwc2 = to_dwc2(gadget); + int retry_count = 4; + u32 gintsts; + u32 gintmsk; + + if (!dwc2_is_device_mode(dwc2)) + return; + + spin_lock(&dwc2->lock); + +irq_retry: + gintsts = readl(dwc2->regs + GINTSTS); + gintmsk = readl(dwc2->regs + GINTMSK); + gintsts &= gintmsk; + + if (!gintsts) + return; + + dwc2_dbg(dwc2, "%s: %08x (%08x) retry %d\n", + __func__, gintsts, gintmsk, 4 - retry_count); + + if (gintsts & GINTSTS_RESETDET) { + dwc2_dbg(dwc2, "%s: USBRstDet\n", __func__); + dwc2_writel(dwc2, GINTSTS_RESETDET, GINTSTS); + } + + if (gintsts & (GINTSTS_USBRST | GINTSTS_RESETDET)) { + u32 usb_status = dwc2_readl(dwc2, GOTGCTL); + u32 connected = dwc2->connected; + + dwc2_dbg(dwc2, "%s: USBRst\n", __func__); + dwc2_dbg(dwc2, "GNPTXSTS=%08x\n", dwc2_readl(dwc2, GNPTXSTS)); + + dwc2_writel(dwc2, GINTSTS_USBRST, GINTSTS); + + /* Report disconnection if it is not already done. */ + dwc2_gadget_disconnect(dwc2); + + /* Reset device address to zero */ + dwc2_dcfg_set_addr(dwc2, 0); + + if (usb_status & GOTGCTL_BSESVLD && connected) + dwc2_core_gadget_init(dwc2, true); + } + + if (gintsts & GINTSTS_ENUMDONE) { + dwc2_writel(dwc2, GINTSTS_ENUMDONE, GINTSTS); + + dwc2_handle_enumdone(dwc2); + } + + if (gintsts & (GINTSTS_OEPINT | GINTSTS_IEPINT)) { + u32 daint = dwc2_readl(dwc2, DAINT); + u32 daintmsk = dwc2_readl(dwc2, DAINTMSK); + u32 daint_out, daint_in; + int ep; + + daint &= daintmsk; + daint_out = daint >> DAINT_OUTEP_SHIFT; + daint_in = daint & ~(daint_out << DAINT_OUTEP_SHIFT); + + dwc2_dbg(dwc2, "%s: daint=%08x\n", __func__, daint); + + for (ep = 0; ep < dwc2->num_eps && daint_out; + ep++, daint_out >>= 1) { + if (daint_out & 1) + dwc2_handle_epint(dwc2, ep, 0); + } + + for (ep = 0; ep < dwc2->num_eps && daint_in; + ep++, daint_in >>= 1) { + if (daint_in & 1) + dwc2_handle_epint(dwc2, ep, 1); + } + } + + /* check both FIFOs */ + if (gintsts & GINTSTS_NPTXFEMP) { + dwc2_dbg(dwc2, "NPTxFEmp\n"); + + /* + * Disable the interrupt to stop it happening again + * unless one of these endpoint routines decides that + * it needs re-enabling + */ +#if 0 + dwc2_hsotg_disable_gsint(dwc2, GINTSTS_NPTXFEMP); + dwc2_hsotg_irq_fifoempty(dwc2, false); +#endif + } + + if (gintsts & GINTSTS_PTXFEMP) { + dwc2_dbg(dwc2, "PTxFEmp\n"); + + /* See note in GINTSTS_NPTxFEmp */ +#if 0 + dwc2_hsotg_disable_gsint(dwc2, GINTSTS_PTXFEMP); + dwc2_hsotg_irq_fifoempty(dwc2, true); +#endif + } + + if (gintsts & GINTSTS_RXFLVL) { + /* + * note, since GINTSTS_RxFLvl doubles as FIFO-not-empty, + * we need to retry dwc2_hsotg_handle_rx if this is still + * set. + */ + dwc2_err(dwc2, "RXFLVL\n"); +#if 0 + dwc2_handle_rx(dwc2); +#endif + } + + if (gintsts & GINTSTS_ERLYSUSP) { + dwc2_dbg(dwc2, "GINTSTS_ErlySusp\n"); + dwc2_writel(dwc2, GINTSTS_ERLYSUSP, GINTSTS); + } + if (gintsts & GINTSTS_USBSUSP) { + dwc2_dbg(dwc2, "USBSusp\n"); + dwc2_writel(dwc2, GINTSTS_USBSUSP, GINTSTS); + } + + /* + * these next two seem to crop-up occasionally causing the core + * to shutdown the USB transfer, so try clearing them and logging + * the occurrence. + */ + + if (gintsts & GINTSTS_GOUTNAKEFF) { + u8 idx; + u32 epctrl; + u32 gintmsk; + u32 daintmsk; + struct dwc2_ep *hs_ep; + + daintmsk = dwc2_readl(dwc2, DAINTMSK); + daintmsk >>= DAINT_OUTEP_SHIFT; + /* Mask this interrupt */ + gintmsk = dwc2_readl(dwc2, GINTMSK); + gintmsk &= ~GINTSTS_GOUTNAKEFF; + dwc2_writel(dwc2, gintmsk, GINTMSK); + + dwc2_dbg(dwc2, "GOUTNakEff triggered\n"); + for (idx = 1; idx < dwc2->num_eps; idx++) { + hs_ep = dwc2->eps_out[idx]; + /* Proceed only unmasked ISOC EPs */ + if ((BIT(idx) & ~daintmsk) || !hs_ep->isochronous) + continue; + + epctrl = dwc2_readl(dwc2, DOEPCTL(idx)); + + if (epctrl & DXEPCTL_EPENA) { + epctrl |= DXEPCTL_SNAK; + epctrl |= DXEPCTL_EPDIS; + dwc2_writel(dwc2, epctrl, DOEPCTL(idx)); + } + } + + /* This interrupt bit is cleared in DXEPINT_EPDISBLD handler */ + } + + if (gintsts & GINTSTS_GINNAKEFF) { + dwc2_info(dwc2, "GINNakEff triggered\n"); + dwc2_set_bit(dwc2, DCTL, DCTL_CGNPINNAK); + } +#if 0 + if (gintsts & GINTSTS_INCOMPL_SOIN) + dwc2_handle_incomplete_isoc_in(dwc2); + + if (gintsts & GINTSTS_INCOMPL_SOOUT) + dwc2_handle_incomplete_isoc_out(dwc2); +#endif + + /* + * if we've had fifo events, we should try and go around the + * loop again to see if there's any point in returning yet. + */ + if (gintsts & IRQ_RETRY_MASK && --retry_count > 0) + goto irq_retry; + + spin_unlock(&dwc2->lock); +} + +/** + * dwc2_dwc2_udc_start - prepare the udc for work + * @gadget: The usb gadget state + * @driver: The usb gadget driver + * + * Perform initialization to prepare udc device and driver + * to work. + */ +static int dwc2_gadget_udc_start(struct usb_gadget *gadget, + struct usb_gadget_driver *driver) +{ + struct dwc2 *dwc2 = to_dwc2(gadget); + + if (!driver) { + dwc2_err(dwc2, "%s: no driver\n", __func__); + return -EINVAL; + } + + if (driver->max_speed < USB_SPEED_FULL) { + dwc2_err(dwc2, "%s: bad speed\n", __func__); + return -EINVAL; + } + + dwc2->driver = driver; + dwc2->gadget.speed = USB_SPEED_UNKNOWN; + + dwc2_core_gadget_init(dwc2, false); + dwc2_gadget_interrupt_init(dwc2); + + dwc2_info(dwc2, "bound driver %s\n", driver->driver.name); + + return 0; +} + +static int dwc2_gadget_udc_stop(struct usb_gadget *gadget, + struct usb_gadget_driver *driver) +{ + struct dwc2 *dwc2 = to_dwc2(gadget); + unsigned long flags = 0; + + dwc2_dbg(dwc2, "%s\n", __func__); + + /* all endpoints should be shutdown */ + spin_lock_irqsave(&dwc2->lock, flags); + + dwc2->driver = NULL; + dwc2->gadget.speed = USB_SPEED_UNKNOWN; + dwc2->enabled = 0; + + spin_unlock_irqrestore(&dwc2->lock, flags); + + return 0; +} + +static const struct usb_gadget_ops dwc2_gadget_ops = { + .get_frame = dwc2_gadget_get_frame, + .set_selfpowered = dwc2_set_selfpowered, + .vbus_session = dwc2_gadget_vbus_session, + .vbus_draw = NULL, + .pullup = dwc2_gadget_pullup, + .udc_start = dwc2_gadget_udc_start, + .udc_stop = dwc2_gadget_udc_stop, + .udc_poll = dwc2_gadget_udc_poll, +}; + +/** + * dwc2_gadget_ep_init - initialise a single endpoint + * @dwc2: The device state. + * @epnum: The endpoint number + * @dir_in: True if direction is in. + * + * Initialise the given endpoint (as part of the probe and device state + * creation) to give to the gadget driver. Setup the endpoint name, any + * direction information and other state that may be required. + */ +static void dwc2_ep_init(struct dwc2 *dwc2, int epnum, bool dir_in) +{ + struct dwc2_ep *ep; + char *dir; + + if (dir_in) + ep = dwc2->eps_in[epnum]; + else + ep = dwc2->eps_out[epnum]; + + if (epnum == 0) + dir = ""; + else if (dir_in) + dir = "in"; + else + dir = "out"; + + ep->dir_in = dir_in; + ep->epnum = epnum; + + snprintf(ep->name, sizeof(ep->name), "ep%d%s", epnum, dir); + + INIT_LIST_HEAD(&ep->queue); + INIT_LIST_HEAD(&ep->ep.ep_list); + + if (epnum) + list_add_tail(&ep->ep.ep_list, &dwc2->gadget.ep_list); + + ep->dwc2 = dwc2; + ep->ep.name = ep->name; + + if (dwc2->params.speed == DWC2_SPEED_PARAM_LOW) + usb_ep_set_maxpacket_limit(&ep->ep, 8); + else if (epnum == 0) + usb_ep_set_maxpacket_limit(&ep->ep, D0EPCTL_MPS_LIMIT); + else + usb_ep_set_maxpacket_limit(&ep->ep, 1024); + + ep->ep.ops = &dwc2_ep_ops; +} + +static int dwc2_eps_alloc(struct dwc2 *dwc2) +{ + struct dwc2_ep *ep; + u32 cfg; + u32 ep_type; + u32 i; + + /* Number of Device Endpoints */ + dwc2->num_eps = 1 + dwc2->hw_params.num_dev_ep; + + ep = kzalloc(sizeof(*ep), GFP_KERNEL); + if (!ep) + return -ENOMEM; + + /* Same endpoint is used in both directions for ep0 */ + dwc2->eps_out[0] = dwc2->eps_in[0] = ep; + + cfg = dwc2->hw_params.dev_ep_dirs; + for (i = 1, cfg >>= 2; i < dwc2->num_eps; i++, cfg >>= 2) { + ep_type = cfg & 3; + /* Direction in or both */ + if (!(ep_type & 2)) { + ep = kzalloc(sizeof(*ep), GFP_KERNEL); + if (!ep) + return -ENOMEM; + dwc2->eps_in[i] = ep; + } + /* Direction out or both */ + if (!(ep_type & 1)) { + ep = kzalloc(sizeof(*ep), GFP_KERNEL); + if (!ep) + return -ENOMEM; + dwc2->eps_out[i] = ep; + } + } + + dwc2->dedicated_fifos = dwc2->hw_params.en_multiple_tx_fifo; + + dwc2_info(dwc2, "EPs: %d, %s fifos, 0x%x entries in SPRAM\n", + dwc2->num_eps, + dwc2->dedicated_fifos ? "dedicated" : "shared", + dwc2->hw_params.total_fifo_size); + return 0; +} + +int dwc2_gadget_init(struct dwc2 *dwc2) +{ + u32 dctl; + int epnum; + int ret; + + if (!dwc2->params.dma) { + dwc2_err(dwc2, "no DMA support, required by gadget driver"); + return -ENOTSUPP; + } + + dwc2_core_init(dwc2); + + dwc2->gadget.speed = USB_SPEED_UNKNOWN; + if (dwc2->params.speed == DWC2_SPEED_PARAM_HIGH) + dwc2->gadget.max_speed = USB_SPEED_HIGH; + else + dwc2->gadget.max_speed = USB_SPEED_FULL; + + dwc2->gadget.ops = &dwc2_gadget_ops; + dwc2->gadget.name = "DWC2 gadget"; + + dwc2->gadget.is_otg = (dwc2->dr_mode == USB_DR_MODE_OTG) ? 1 : 0; + + ret = dwc2_eps_alloc(dwc2); + if (ret) { + dwc2_err(dwc2, "Endpoints allocation failed: %d\n", ret); + return ret; + } + + dwc2->ctrl_buff = dma_alloc(DWC2_CTRL_BUFF_SIZE); + if (!dwc2->ctrl_buff) + return -ENOMEM; + + dwc2->ep0_buff = dma_alloc(DWC2_CTRL_BUFF_SIZE); + if (!dwc2->ep0_buff) + return -ENOMEM; + + if (dwc2->num_eps == 0) { + dwc2_err(dwc2, "wrong number of EPs (zero)\n"); + return -EINVAL; + } + + /* setup endpoint information */ + INIT_LIST_HEAD(&dwc2->gadget.ep_list); + dwc2->gadget.ep0 = &dwc2->eps_out[0]->ep; + + /* allocate EP0 request */ + dwc2->ctrl_req = dwc2_ep_alloc_req(&dwc2->eps_out[0]->ep); + if (!dwc2->ctrl_req) { + dwc2_err(dwc2, "failed to allocate ctrl req\n"); + return -ENOMEM; + } + + /* initialise the endpoints now the core has been initialised */ + for (epnum = 0; epnum < dwc2->num_eps; epnum++) { + if (dwc2->eps_in[epnum]) + dwc2_ep_init(dwc2, epnum, 1); + if (dwc2->eps_out[epnum]) + dwc2_ep_init(dwc2, epnum, 0); + } + + /* Be in disconnected state until gadget is registered */ + dctl = dwc2_readl(dwc2, DCTL); + dwc2_writel(dwc2, dctl | DCTL_SFTDISCON, DCTL); + + ret = usb_add_gadget_udc(dwc2->dev, &dwc2->gadget); + if (ret) { + dwc2_ep_free_req(&dwc2->eps_out[0]->ep, dwc2->ctrl_req); + return ret; + } + + return 0; +} diff --git a/drivers/usb/dwc2/host.c b/drivers/usb/dwc2/host.c new file mode 100644 index 0000000000..13cb3472d7 --- /dev/null +++ b/drivers/usb/dwc2/host.c @@ -0,0 +1,790 @@ +// SPDX-License-Identifier: GPL-2.0+ +#include <dma.h> +#include "dwc2.h" + +#define to_dwc2 host_to_dwc2 + +/* Use only HC channel 0. */ +#define DWC2_HC_CHANNEL 0 + +static int dwc2_do_split(struct dwc2 *dwc2, struct usb_device *dev) +{ + uint32_t hprt0 = dwc2_readl(dwc2, HPRT0); + uint32_t prtspd = (hprt0 & HPRT0_SPD_MASK) >> HPRT0_SPD_SHIFT; + + return prtspd == HPRT0_SPD_HIGH_SPEED && dev->speed != USB_SPEED_HIGH; +} + +static void dwc2_host_hub_info(struct dwc2 *dwc2, struct usb_device *dev, + uint8_t *hub_addr, uint8_t *hub_port) +{ + *hub_addr = dev->devnum; + *hub_port = dev->portnr; + + for (; dev->parent; dev = dev->parent) { + if (dev->parent->descriptor->bDeviceClass == USB_CLASS_HUB) { + *hub_addr = dev->parent->devnum; + *hub_port = dev->parent->portnr; + break; + } + } +} + +static void dwc2_hc_init_split(struct dwc2 *dwc2, struct usb_device *dev, + uint8_t hc) +{ + uint8_t hub_addr, hub_port; + uint32_t hcsplt = 0; + + dwc2_host_hub_info(dwc2, dev, &hub_addr, &hub_port); + + hcsplt = HCSPLT_SPLTENA; + hcsplt |= hub_addr << HCSPLT_HUBADDR_SHIFT; + hcsplt |= hub_port << HCSPLT_PRTADDR_SHIFT; + + /* Program the HCSPLIT register for SPLITs */ + dwc2_writel(dwc2, hcsplt, HCSPLT(hc)); +} + +static void dwc2_hc_enable_ints(struct dwc2 *dwc2, uint8_t hc) +{ + uint32_t intmsk; + uint32_t hcintmsk = HCINTMSK_CHHLTD; + + dwc2_writel(dwc2, hcintmsk, HCINTMSK(hc)); + + /* Enable the top level host channel interrupt */ + intmsk = dwc2_readl(dwc2, HAINTMSK); + intmsk |= 1 << hc; + dwc2_writel(dwc2, intmsk, HAINTMSK); + + /* Make sure host channel interrupts are enabled */ + intmsk = dwc2_readl(dwc2, GINTMSK); + intmsk |= GINTSTS_HCHINT; + dwc2_writel(dwc2, intmsk, GINTMSK); +} + +/** + * Prepares a host channel for transferring packets to/from a specific + * endpoint. The HCCHARn register is set up with the characteristics specified + * in _hc. Host channel interrupts that may need to be serviced while this + * transfer is in progress are enabled. + * + * @param regs Programming view of DWC2 controller + * @param hc Information needed to initialize the host channel + */ +static void dwc2_hc_init(struct dwc2 *dwc2, struct usb_device *dev, u8 hc, + unsigned long pipe, int is_in) +{ + int addr = usb_pipedevice(pipe); + int endp = usb_pipeendpoint(pipe); + int type = usb_pipetype(pipe); + int mps = usb_maxpacket(dev, pipe); + uint32_t hcchar = (addr << HCCHAR_DEVADDR_SHIFT) | + (endp << HCCHAR_EPNUM_SHIFT) | + (is_in ? HCCHAR_EPDIR : 0) | + (mps << HCCHAR_MPS_SHIFT); + + switch (type) { + case PIPE_ISOCHRONOUS: + hcchar |= DXEPCTL_EPTYPE_ISO; + break; + case PIPE_INTERRUPT: + hcchar |= DXEPCTL_EPTYPE_INTERRUPT; + break; + case PIPE_CONTROL: + hcchar |= DXEPCTL_EPTYPE_CONTROL; + break; + case PIPE_BULK: + hcchar |= DXEPCTL_EPTYPE_BULK; + break; + } + + if (dev->speed == USB_SPEED_LOW) + hcchar |= HCCHAR_LSPDDEV; + + /* Clear old interrupt conditions for this dwc2 channel */ + dwc2_writel(dwc2, ~HCINTMSK_RESERVED14_31, HCINT(hc)); + + /* Enable channel interrupts required for this transfer */ + dwc2_hc_enable_ints(dwc2, hc); + + /* + * Program the HCCHARn register with the endpoint characteristics + * for the current transfer. + */ + dwc2_writel(dwc2, hcchar, HCCHAR(hc)); + + /* Program the HCSPLIT register, default to no SPLIT */ + dwc2_writel(dwc2, 0, HCSPLT(hc)); +} + +static void dwc2_endpoint_reset(struct dwc2 *dwc2, int in, int devnum, int ep) +{ + if (in) + dwc2->in_data_toggle[devnum][ep] = TSIZ_SC_MC_PID_DATA0; + else + dwc2->out_data_toggle[devnum][ep] = TSIZ_SC_MC_PID_DATA0; +} + +static int wait_for_chhltd(struct dwc2 *dwc2, u8 hc, uint32_t *sub, u8 *tgl) +{ + int ret; + uint32_t hcint, hctsiz, hcchar; + + ret = dwc2_wait_bit_set(dwc2, HCINT(hc), HCINTMSK_CHHLTD, 10000); + if (ret) { + hcchar = dwc2_readl(dwc2, HCCHAR(hc)); + dwc2_writel(dwc2, hcchar | HCCHAR_CHDIS, HCCHAR(hc)); + dwc2_wait_bit_set(dwc2, HCINT(hc), HCINTMSK_CHHLTD, 10000); + return ret; + } + + hcint = dwc2_readl(dwc2, HCINT(hc)); + + if (hcint & HCINTMSK_AHBERR) + dwc2_err(dwc2, "%s: AHB error during internal DMA access\n", + __func__); + + if (hcint & HCINTMSK_XFERCOMPL) { + hctsiz = dwc2_readl(dwc2, HCTSIZ(hc)); + *sub = (hctsiz & TSIZ_XFERSIZE_MASK) >> TSIZ_XFERSIZE_SHIFT; + *tgl = (hctsiz & TSIZ_SC_MC_PID_MASK) >> TSIZ_SC_MC_PID_SHIFT; + + dwc2_dbg(dwc2, "%s: HCINT=%08x sub=%u toggle=%d\n", __func__, + hcint, *sub, *tgl); + return 0; + } + + if (hcint & (HCINTMSK_NAK | HCINTMSK_FRMOVRUN)) + return -EAGAIN; + + dwc2_dbg(dwc2, "%s: Unknown channel status: (HCINT=%08x)\n", __func__, + hcint); + return -EINVAL; +} + +static int transfer_chunk(struct dwc2 *dwc2, u8 hc, + u8 *pid, int in, void *buffer, int num_packets, + int xfer_len, int *actual_len, int odd_frame) +{ + uint32_t hctsiz, hcchar, sub; + dma_addr_t dma_addr; + int ret = 0; + + dma_addr = dma_map_single(dwc2->dev, buffer, xfer_len, + in ? DMA_FROM_DEVICE : DMA_TO_DEVICE); + + if (dma_mapping_error(dwc2->dev, dma_addr)) { + dwc2_err(dwc2, "Failed to map buffer@0x%p for dma\n", buffer); + return -EFAULT; + } + + dwc2_dbg(dwc2, "chunk: pid=%d xfer_len=%u pkts=%u dma_addr=%pad\n", + *pid, xfer_len, num_packets, (void *)dma_addr); + + dwc2_writel(dwc2, dma_addr, HCDMA(hc)); + + hctsiz = (xfer_len << TSIZ_XFERSIZE_SHIFT) + | (num_packets << TSIZ_PKTCNT_SHIFT) + | (*pid << TSIZ_SC_MC_PID_SHIFT); + + dwc2_writel(dwc2, hctsiz, HCTSIZ(hc)); + + /* Clear old interrupt conditions for this dwc2 channel. */ + dwc2_writel(dwc2, 0x3fff, HCINT(hc)); + + /* Set dwc2 channel enable after all other setup is complete. */ + hcchar = dwc2_readl(dwc2, HCCHAR(hc)); + hcchar &= ~(HCCHAR_MULTICNT_MASK | HCCHAR_CHDIS); + hcchar |= (1 << HCCHAR_MULTICNT_SHIFT) | HCCHAR_CHENA; + if (odd_frame) + hcchar |= HCCHAR_ODDFRM; + else + hcchar &= ~HCCHAR_ODDFRM; + dwc2_writel(dwc2, hcchar, HCCHAR(hc)); + + ret = wait_for_chhltd(dwc2, hc, &sub, pid); + if (ret < 0) + goto exit; + + if (in) + xfer_len -= sub; + *actual_len = xfer_len; + +exit: + dma_unmap_single(dwc2->dev, dma_addr, xfer_len, + in ? DMA_FROM_DEVICE : DMA_TO_DEVICE); + + return ret; +} + +static int dwc2_submit_packet(struct dwc2 *dwc2, struct usb_device *dev, u8 hc, + unsigned long pipe, u8 *pid, int in, void *buf, + int len) +{ + int mps = usb_maxpacket(dev, pipe); + int do_split = dwc2_do_split(dwc2, dev); + int complete_split = 0; + int done = 0; + int ret = 0; + uint32_t xfer_len; + uint32_t num_packets; + int stop_transfer = 0; + uint32_t max_xfer_len; + int ssplit_frame_num = 0; + + max_xfer_len = dwc2->params.max_packet_count * mps; + + if (max_xfer_len > dwc2->params.max_transfer_size) + max_xfer_len = dwc2->params.max_transfer_size; + + /* Make sure that max_xfer_len is a multiple of max packet size. */ + num_packets = max_xfer_len / mps; + max_xfer_len = num_packets * mps; + + /* Initialize channel */ + dwc2_hc_init(dwc2, dev, hc, pipe, in); + + /* Check if the target is a FS/LS device behind a HS hub */ + if (do_split) { + dwc2_hc_init_split(dwc2, dev, hc); + num_packets = 1; + max_xfer_len = mps; + } + do { + int actual_len = 0; + uint32_t hcint, hcsplt; + int odd_frame = 0; + + xfer_len = len - done; + + if (xfer_len > max_xfer_len) + xfer_len = max_xfer_len; + else if (xfer_len > mps) + num_packets = (xfer_len + mps - 1) / mps; + else + num_packets = 1; + + if (complete_split || do_split) { + hcsplt = dwc2_readl(dwc2, HCSPLT(hc)); + if (complete_split) + hcsplt |= HCSPLT_COMPSPLT; + else if (do_split) + hcsplt &= ~HCSPLT_COMPSPLT; + dwc2_writel(dwc2, hcsplt, HCSPLT(hc)); + } + + if (usb_pipeint(pipe)) { + int uframe_num = dwc2_readl(dwc2, HFNUM); + + if (!(uframe_num & 0x1)) + odd_frame = 1; + } + + ret = transfer_chunk(dwc2, hc, pid, + in, (char *)buf + done, num_packets, + xfer_len, &actual_len, odd_frame); + + hcint = dwc2_readl(dwc2, HCINT(hc)); + if (complete_split) { + stop_transfer = 0; + if (hcint & HCINTMSK_NYET) { + int frame_num = HFNUM_MAX_FRNUM & + dwc2_readl(dwc2, HFNUM); + + ret = 0; + if (((frame_num - ssplit_frame_num) & + HFNUM_MAX_FRNUM) > 4) + ret = -EAGAIN; + } else { + complete_split = 0; + } + } else if (do_split) { + if (hcint & HCINTMSK_ACK) { + ssplit_frame_num = HFNUM_MAX_FRNUM & + dwc2_readl(dwc2, HFNUM); + ret = 0; + complete_split = 1; + } + } + + if (ret) + break; + + if (actual_len < xfer_len) + stop_transfer = 1; + + done += actual_len; + + /* Transactions are done when when either all data is transferred or + * there is a short transfer. In case of a SPLIT make sure the CSPLIT + * is executed. + */ + } while (((done < len) && !stop_transfer) || complete_split); + + dwc2_writel(dwc2, 0, HCINTMSK(hc)); + dwc2_writel(dwc2, 0xFFFFFFFF, HCINT(hc)); + + dev->status = 0; + dev->act_len = done; + + return ret; +} + +static int dwc2_submit_control_msg(struct usb_device *udev, + unsigned long pipe, void *buffer, int len, + struct devrequest *setup, int timeout) +{ + struct usb_host *host = udev->host; + struct dwc2 *dwc2 = to_dwc2(host); + int devnum = usb_pipedevice(pipe); + int ret, act_len; + u8 pid; + u8 hc = DWC2_HC_CHANNEL; + /* For CONTROL endpoint pid should start with DATA1 */ + int status_direction; + + if (devnum == dwc2->root_hub_devnum) { + udev->speed = USB_SPEED_HIGH; + ret = dwc2_submit_roothub(dwc2, udev, pipe, buffer, len, setup); + return ret; + } + + /* SETUP stage */ + pid = TSIZ_SC_MC_PID_SETUP; + do { + ret = dwc2_submit_packet(dwc2, udev, hc, pipe, &pid, + 0, setup, 8); + } while (ret == -EAGAIN); + if (ret) + return ret; + + /* DATA stage */ + act_len = 0; + if (buffer) { + pid = TSIZ_SC_MC_PID_DATA1; + do { + ret = dwc2_submit_packet(dwc2, udev, hc, pipe, &pid, + usb_pipein(pipe), buffer, len); + act_len += udev->act_len; + buffer += udev->act_len; + len -= udev->act_len; + } while (ret == -EAGAIN); + if (ret) + return ret; + status_direction = usb_pipeout(pipe); + } else { + /* No-data CONTROL always ends with an IN transaction */ + status_direction = 1; + } + + /* STATUS stage */ + pid = TSIZ_SC_MC_PID_DATA1; + do { + ret = dwc2_submit_packet(dwc2, udev, hc, pipe, &pid, + status_direction, NULL, 0); + } while (ret == -EAGAIN); + if (ret) + return ret; + + if (setup->requesttype == USB_RECIP_ENDPOINT + && setup->request == USB_REQ_CLEAR_FEATURE) { + /* From USB 2.0, section 9.4.5: + * ClearFeature(ENDPOINT_HALT) request always results + * in the data toggle being reinitialized to DATA0. + */ + int ep = le16_to_cpu(setup->index) & 0xf; + dwc2_endpoint_reset(dwc2, usb_pipein(pipe), devnum, ep); + } + + udev->act_len = act_len; + udev->status = 0; + + return 0; +} + +static int dwc2_submit_bulk_msg(struct usb_device *udev, unsigned long pipe, + void *buffer, int len, int timeout) +{ + struct usb_host *host = udev->host; + struct dwc2 *dwc2 = to_dwc2(host); + int devnum = usb_pipedevice(pipe); + int ep = usb_pipeendpoint(pipe); + int in = usb_pipein(pipe); + u8 *pid; + u8 hc = DWC2_HC_CHANNEL; + uint64_t start; + int ret; + + if ((devnum >= MAX_DEVICE) || (devnum == dwc2->root_hub_devnum)) { + udev->status = 0; + return -EINVAL; + } + + if (in) + pid = &dwc2->in_data_toggle[devnum][ep]; + else + pid = &dwc2->out_data_toggle[devnum][ep]; + + start = get_time_ns(); + do { + ret = dwc2_submit_packet(dwc2, udev, hc, pipe, pid, in, + buffer, len); + } while (ret == -EAGAIN && !is_timeout(start, timeout * MSECOND)); + if (ret == -EAGAIN) { + dwc2_err(dwc2, "Timeout on bulk endpoint\n"); + ret = -ETIMEDOUT; + } + + dwc2_dbg(dwc2, "%s: return %d\n", __func__, ret); + + return ret; +} + +static int dwc2_submit_int_msg(struct usb_device *udev, unsigned long pipe, + void *buffer, int len, int interval) +{ + struct usb_host *host = udev->host; + struct dwc2 *dwc2 = to_dwc2(host); + int devnum = usb_pipedevice(pipe); + int ep = usb_pipeendpoint(pipe); + int in = usb_pipein(pipe); + u8 *pid; + u8 hc = DWC2_HC_CHANNEL; + uint64_t start; + int ret; + + if ((devnum >= MAX_DEVICE) || (devnum == dwc2->root_hub_devnum)) { + udev->status = 0; + return -EINVAL; + } + + if (usb_pipein(pipe)) + pid = &dwc2->in_data_toggle[devnum][ep]; + else + pid = &dwc2->out_data_toggle[devnum][ep]; + + start = get_time_ns(); + + while (1) { + ret = dwc2_submit_packet(dwc2, udev, hc, pipe, pid, in, + buffer, len); + if (ret != -EAGAIN) + return ret; + if (is_timeout(start, USB_CNTL_TIMEOUT * MSECOND)) { + dwc2_err(dwc2, "Timeout on interrupt endpoint\n"); + return -ETIMEDOUT; + } + } +} + +/** + * dwc2_calculate_dynamic_fifo() - Calculates the default fifo size + * For system that have a total fifo depth that is smaller than the default + * RX + TX fifo size. + * + * @dwc2: Programming view of DWC_otg controller + */ +static void dwc2_calculate_dynamic_fifo(struct dwc2 *dwc2) +{ + struct dwc2_core_params *params = &dwc2->params; + struct dwc2_hw_params *hw = &dwc2->hw_params; + u32 rxfsiz, nptxfsiz, ptxfsiz, total_fifo_size; + + total_fifo_size = hw->total_fifo_size; + rxfsiz = params->host_rx_fifo_size; + nptxfsiz = params->host_nperio_tx_fifo_size; + ptxfsiz = params->host_perio_tx_fifo_size; + + /* + * Will use Method 2 defined in the DWC2 spec: minimum FIFO depth + * allocation with support for high bandwidth endpoints. Synopsys + * defines MPS(Max Packet size) for a periodic EP=1024, and for + * non-periodic as 512. + */ + if (total_fifo_size < (rxfsiz + nptxfsiz + ptxfsiz)) { + /* + * For Buffer DMA mode/Scatter Gather DMA mode + * 2 * ((Largest Packet size / 4) + 1 + 1) + n + * with n = number of host channel. + * 2 * ((1024/4) + 2) = 516 + */ + rxfsiz = 516 + hw->host_channels; + + /* + * min non-periodic tx fifo depth + * 2 * (largest non-periodic USB packet used / 4) + * 2 * (512/4) = 256 + */ + nptxfsiz = 256; + + /* + * min periodic tx fifo depth + * (largest packet size*MC)/4 + * (1024 * 3)/4 = 768 + */ + ptxfsiz = 768; + } + + params->host_rx_fifo_size = rxfsiz; + params->host_nperio_tx_fifo_size = nptxfsiz; + params->host_perio_tx_fifo_size = ptxfsiz; + + /* + * If the summation of RX, NPTX and PTX fifo sizes is still + * bigger than the total_fifo_size, then we have a problem. + * + * We won't be able to allocate as many endpoints. Right now, + * we're just printing an error message, but ideally this FIFO + * allocation algorithm would be improved in the future. + * + * FIXME improve this FIFO allocation algorithm. + */ + if (unlikely(total_fifo_size < (rxfsiz + nptxfsiz + ptxfsiz))) + dwc2_err(dwc2, "invalid fifo sizes\n"); +} + +static void dwc2_config_fifos(struct dwc2 *dwc2) +{ + struct dwc2_core_params *params = &dwc2->params; + u32 nptxfsiz, hptxfsiz, dfifocfg, grxfsiz; + + if (!params->enable_dynamic_fifo) + return; + + dwc2_calculate_dynamic_fifo(dwc2); + + /* Rx FIFO */ + grxfsiz = dwc2_readl(dwc2, GRXFSIZ); + dwc2_dbg(dwc2, "initial grxfsiz=%08x\n", grxfsiz); + grxfsiz &= ~GRXFSIZ_DEPTH_MASK; + grxfsiz |= params->host_rx_fifo_size << + GRXFSIZ_DEPTH_SHIFT & GRXFSIZ_DEPTH_MASK; + dwc2_writel(dwc2, grxfsiz, GRXFSIZ); + dwc2_dbg(dwc2, "new grxfsiz=%08x\n", dwc2_readl(dwc2, GRXFSIZ)); + + /* Non-periodic Tx FIFO */ + dwc2_dbg(dwc2, "initial gnptxfsiz=%08x\n", dwc2_readl(dwc2, GNPTXFSIZ)); + nptxfsiz = params->host_nperio_tx_fifo_size << + FIFOSIZE_DEPTH_SHIFT & FIFOSIZE_DEPTH_MASK; + nptxfsiz |= params->host_rx_fifo_size << + FIFOSIZE_STARTADDR_SHIFT & FIFOSIZE_STARTADDR_MASK; + dwc2_writel(dwc2, nptxfsiz, GNPTXFSIZ); + dwc2_dbg(dwc2, "new gnptxfsiz=%08x\n", dwc2_readl(dwc2, GNPTXFSIZ)); + + /* Periodic Tx FIFO */ + dwc2_dbg(dwc2, "initial hptxfsiz=%08x\n", dwc2_readl(dwc2, HPTXFSIZ)); + hptxfsiz = params->host_perio_tx_fifo_size << + FIFOSIZE_DEPTH_SHIFT & FIFOSIZE_DEPTH_MASK; + hptxfsiz |= (params->host_rx_fifo_size + + params->host_nperio_tx_fifo_size) << + FIFOSIZE_STARTADDR_SHIFT & FIFOSIZE_STARTADDR_MASK; + dwc2_writel(dwc2, hptxfsiz, HPTXFSIZ); + dwc2_dbg(dwc2, "new hptxfsiz=%08x\n", dwc2_readl(dwc2, HPTXFSIZ)); + + if (dwc2->params.en_multiple_tx_fifo && + dwc2->hw_params.snpsid >= DWC2_CORE_REV_2_91a) { + /* + * This feature was implemented in 2.91a version + * Global DFIFOCFG calculation for Host mode - + * include RxFIFO, NPTXFIFO and HPTXFIFO + */ + dfifocfg = dwc2_readl(dwc2, GDFIFOCFG); + dfifocfg &= ~GDFIFOCFG_EPINFOBASE_MASK; + dfifocfg |= (params->host_rx_fifo_size + + params->host_nperio_tx_fifo_size + + params->host_perio_tx_fifo_size) << + GDFIFOCFG_EPINFOBASE_SHIFT & + GDFIFOCFG_EPINFOBASE_MASK; + dwc2_writel(dwc2, dfifocfg, GDFIFOCFG); + dwc2_dbg(dwc2, "new dfifocfg=%08x\n", dfifocfg); + } +} + +/* + * This function initializes the DWC2 controller registers for + * host mode. + * + * This function flushes the Tx and Rx FIFOs and it flushes any entries in the + * request queues. Host channels are reset to ensure that they are ready for + * performing transfers. + * + * @param dev USB Device (NULL if driver model is not being used) + * @param regs Programming view of DWC2 controller + * + */ +static void dwc2_core_host_init(struct device_d *dev, + struct dwc2 *dwc2) +{ + uint32_t hcchar, hcfg, hprt0, hotgctl, usbcfg; + int i, ret, num_channels; + + dwc2_dbg(dwc2, "%s(%p)\n", __func__, dwc2); + + /* Set HS/FS Timeout Calibration to 7 (max available value). + * The number of PHY clocks that the application programs in + * this field is added to the high/full speed interpacket timeout + * duration in the core to account for any additional delays + * introduced by the PHY. This can be required, because the delay + * introduced by the PHY in generating the linestate condition + * can vary from one PHY to another. + */ + usbcfg = dwc2_readl(dwc2, GUSBCFG); + usbcfg |= GUSBCFG_TOUTCAL(7); + dwc2_writel(dwc2, usbcfg, GUSBCFG); + + /* Restart the Phy Clock */ + dwc2_writel(dwc2, 0, PCGCTL); + + /* Initialize Host Configuration Register */ + dwc2_init_fs_ls_pclk_sel(dwc2); + if (dwc2->params.speed == DWC2_SPEED_PARAM_FULL || + dwc2->params.speed == DWC2_SPEED_PARAM_LOW) { + hcfg = dwc2_readl(dwc2, HCFG); + hcfg |= HCFG_FSLSSUPP; + dwc2_writel(dwc2, hcfg, HCFG); + } + + if (dwc2->params.dma_desc) { + u32 op_mode = dwc2->hw_params.op_mode; + + if (dwc2->hw_params.snpsid < DWC2_CORE_REV_2_90a || + !dwc2->hw_params.dma_desc_enable || + op_mode == GHWCFG2_OP_MODE_SRP_CAPABLE_DEVICE || + op_mode == GHWCFG2_OP_MODE_NO_SRP_CAPABLE_DEVICE || + op_mode == GHWCFG2_OP_MODE_UNDEFINED) { + dwc2_err(dwc2, "Descriptor DMA not suppported\n"); + dwc2_err(dwc2, "falling back to buffer DMA mode.\n"); + dwc2->params.dma_desc = false; + } else { + hcfg = dwc2_readl(dwc2, HCFG); + hcfg |= HCFG_DESCDMA; + dwc2_writel(dwc2, hcfg, HCFG); + } + } + + dwc2_config_fifos(dwc2); + + /* Clear Host Set HNP Enable in the OTG Control Register */ + hotgctl = dwc2_readl(dwc2, GOTGCTL); + hotgctl &= ~GOTGCTL_HSTSETHNPEN; + dwc2_writel(dwc2, hotgctl, GOTGCTL); + + /* Make sure the FIFOs are flushed. */ + dwc2_flush_all_fifo(dwc2); + + /* Flush out any leftover queued requests. */ + num_channels = dwc2->params.host_channels; + for (i = 0; i < num_channels; i++) { + hcchar = dwc2_readl(dwc2, HCCHAR(i)); + if (!(hcchar & HCCHAR_CHENA)) + continue; + hcchar |= HCCHAR_CHDIS; + hcchar &= ~(HCCHAR_CHENA | HCCHAR_EPDIR); + dwc2_writel(dwc2, hcchar, HCCHAR(i)); + } + + /* Halt all channels to put them into a known state. */ + for (i = 0; i < num_channels; i++) { + hcchar = dwc2_readl(dwc2, HCCHAR(i)); + if (!(hcchar & HCCHAR_CHENA)) + continue; + hcchar |= HCCHAR_CHENA | HCCHAR_CHDIS; + hcchar &= ~HCCHAR_EPDIR; + + dwc2_writel(dwc2, hcchar, HCCHAR(i)); + ret = dwc2_wait_bit_clear(dwc2, HCCHAR(i), HCCHAR_CHENA, 10000); + if (ret) + dwc2_warn(dwc2, "%s: Timeout! Reseting channel %d\n", + __func__, i); + } + + /* Turn on the vbus power */ + if (dwc2_is_host_mode(dwc2)) { + hprt0 = dwc2_readl(dwc2, HPRT0); + hprt0 &= ~(HPRT0_ENA | HPRT0_CONNDET); + hprt0 &= ~(HPRT0_ENACHG | HPRT0_OVRCURRCHG); + if (!(hprt0 & HPRT0_PWR)) { + hprt0 |= HPRT0_PWR; + dwc2_writel(dwc2, hprt0, HPRT0); + } + } + + /* Disable all interrupts */ + dwc2_writel(dwc2, 0, GINTMSK); + dwc2_writel(dwc2, 0, HAINTMSK); +} + +static int dwc2_host_init(struct usb_host *host) +{ + struct dwc2 *dwc2 = to_dwc2(host); + struct device_d *dev = dwc2->dev; + uint32_t hprt0, gusbcfg; + int i, j; + + /* Force Host mode in case the dwc2 controller is otg, + * otherwise the mode selection is dictated by the id + * pin, thus will require a otg A cable to be plugged-in. + */ + gusbcfg = dwc2_readl(dwc2, GUSBCFG) | GUSBCFG_FORCEHOSTMODE; + dwc2_writel(dwc2, gusbcfg, GUSBCFG); + mdelay(25); + + dwc2_core_init(dwc2); + dwc2_core_host_init(dev, dwc2); + + hprt0 = dwc2_readl(dwc2, HPRT0); + hprt0 &= ~(HPRT0_ENA | HPRT0_CONNDET); + /* clear HPRT0_ENACHG and HPRT0_OVRCURRCHG by writing 1 */ + hprt0 |= HPRT0_ENACHG | HPRT0_OVRCURRCHG; + hprt0 |= HPRT0_RST; + dwc2_writel(dwc2, hprt0, HPRT0); + + mdelay(50); + + hprt0 = dwc2_readl(dwc2, HPRT0); + hprt0 &= ~(HPRT0_ENA | HPRT0_CONNDET | HPRT0_RST); + dwc2_writel(dwc2, hprt0, HPRT0); + + for (i = 0; i < MAX_DEVICE; i++) { + for (j = 0; j < MAX_ENDPOINT; j++) { + dwc2->in_data_toggle[i][j] = TSIZ_SC_MC_PID_DATA0; + dwc2->out_data_toggle[i][j] = TSIZ_SC_MC_PID_DATA0; + } + } + + /* + * Add a 1 second delay here. This gives the host controller + * a bit time before the comminucation with the USB devices + * is started (the bus is scanned) and fixes the USB detection + * problems with some problematic USB keys. + */ + if (dwc2_is_host_mode(dwc2)) + mdelay(1000); + + return 0; +} + +static int dwc2_detect(struct device_d *dev) +{ + struct dwc2 *dwc2 = dev->priv; + + return usb_host_detect(&dwc2->host); +} + +int dwc2_register_host(struct dwc2 *dwc2) +{ + struct usb_host *host; + + host = &dwc2->host; + host->hw_dev = dwc2->dev; + host->init = dwc2_host_init; + host->submit_bulk_msg = dwc2_submit_bulk_msg; + host->submit_control_msg = dwc2_submit_control_msg; + host->submit_int_msg = dwc2_submit_int_msg; + + dwc2->dev->detect = dwc2_detect; + + return usb_register_host(host); +} diff --git a/drivers/usb/dwc2/regs.h b/drivers/usb/dwc2/regs.h new file mode 100644 index 0000000000..06fc8da822 --- /dev/null +++ b/drivers/usb/dwc2/regs.h @@ -0,0 +1,841 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Copyright (C) 2014 Marek Vasut <marex@denx.de> + */ + +#ifndef __DWC2_H__ +#define __DWC2_H__ + +#define HSOTG_REG(x) (x) + +#define GOTGCTL HSOTG_REG(0x000) +#define GOTGCTL_CHIRPEN BIT(27) +#define GOTGCTL_MULT_VALID_BC_MASK (0x1f << 22) +#define GOTGCTL_MULT_VALID_BC_SHIFT 22 +#define GOTGCTL_CURMODE BIT(21) /* was missing wtf ? */ +#define GOTGCTL_OTGVER BIT(20) +#define GOTGCTL_BSESVLD BIT(19) +#define GOTGCTL_ASESVLD BIT(18) +#define GOTGCTL_DBNC_SHORT BIT(17) +#define GOTGCTL_CONID_B BIT(16) +#define GOTGCTL_DBNCE_FLTR_BYPASS BIT(15) +#define GOTGCTL_EMBHOSTEN BIT(12) +#define GOTGCTL_DEVHNPEN BIT(11) +#define GOTGCTL_HSTSETHNPEN BIT(10) +#define GOTGCTL_HNPREQ BIT(9) +#define GOTGCTL_HSTNEGSCS BIT(8) +#define GOTGCTL_SESREQ BIT(1) +#define GOTGCTL_SESREQSCS BIT(0) + +#define GOTGINT HSOTG_REG(0x004) +#define GOTGINT_DBNCE_DONE BIT(19) +#define GOTGINT_A_DEV_TOUT_CHG BIT(18) +#define GOTGINT_HST_NEG_DET BIT(17) +#define GOTGINT_HST_NEG_SUC_STS_CHNG BIT(9) +#define GOTGINT_SES_REQ_SUC_STS_CHNG BIT(8) +#define GOTGINT_SES_END_DET BIT(2) + +#define GAHBCFG HSOTG_REG(0x008) +#define GAHBCFG_AHB_SINGLE BIT(23) +#define GAHBCFG_NOTI_ALL_DMA_WRIT BIT(22) +#define GAHBCFG_REM_MEM_SUPP BIT(21) +#define GAHBCFG_P_TXF_EMP_LVL BIT(8) +#define GAHBCFG_NP_TXF_EMP_LVL BIT(7) +#define GAHBCFG_DMA_EN BIT(5) +#define GAHBCFG_HBSTLEN_MASK (0xf << 1) +#define GAHBCFG_HBSTLEN_SHIFT 1 +#define GAHBCFG_HBSTLEN_SINGLE 0 +#define GAHBCFG_HBSTLEN_INCR 1 +#define GAHBCFG_HBSTLEN_INCR4 3 +#define GAHBCFG_HBSTLEN_INCR8 5 +#define GAHBCFG_HBSTLEN_INCR16 7 +#define GAHBCFG_GLBL_INTR_EN BIT(0) +#define GAHBCFG_CTRL_MASK (GAHBCFG_P_TXF_EMP_LVL | \ + GAHBCFG_NP_TXF_EMP_LVL | \ + GAHBCFG_DMA_EN | \ + GAHBCFG_GLBL_INTR_EN) + +#define GUSBCFG HSOTG_REG(0x00C) +#define GUSBCFG_FORCEDEVMODE BIT(30) +#define GUSBCFG_FORCEHOSTMODE BIT(29) +#define GUSBCFG_TXENDDELAY BIT(28) +#define GUSBCFG_ICTRAFFICPULLREMOVE BIT(27) +#define GUSBCFG_ICUSBCAP BIT(26) +#define GUSBCFG_ULPI_INT_PROT_DIS BIT(25) +#define GUSBCFG_INDICATORPASSTHROUGH BIT(24) +#define GUSBCFG_INDICATORCOMPLEMENT BIT(23) +#define GUSBCFG_TERMSELDLPULSE BIT(22) +#define GUSBCFG_ULPI_EXT_VBUS_IND BIT(21) +#define GUSBCFG_ULPI_EXT_VBUS_DRV BIT(20) +#define GUSBCFG_ULPI_CLK_SUSP_M BIT(19) +#define GUSBCFG_ULPI_AUTO_RES BIT(18) +#define GUSBCFG_ULPI_FS_LS BIT(17) +#define GUSBCFG_OTG_UTMI_FS_SEL BIT(16) +#define GUSBCFG_PHY_LP_CLK_SEL BIT(15) +#define GUSBCFG_USBTRDTIM_MASK (0xf << 10) +#define GUSBCFG_USBTRDTIM_SHIFT 10 +#define GUSBCFG_HNPCAP BIT(9) +#define GUSBCFG_SRPCAP BIT(8) +#define GUSBCFG_DDRSEL BIT(7) +#define GUSBCFG_PHYSEL BIT(6) +#define GUSBCFG_FSINTF BIT(5) +#define GUSBCFG_ULPI_UTMI_SEL BIT(4) +#define GUSBCFG_PHYIF16 BIT(3) +#define GUSBCFG_PHYIF8 (0 << 3) +#define GUSBCFG_TOUTCAL_MASK (0x7 << 0) +#define GUSBCFG_TOUTCAL_SHIFT 0 +#define GUSBCFG_TOUTCAL_LIMIT 0x7 +#define GUSBCFG_TOUTCAL(_x) ((_x) << 0) + +#define GRSTCTL HSOTG_REG(0x010) +#define GRSTCTL_AHBIDLE BIT(31) +#define GRSTCTL_DMAREQ BIT(30) +#define GRSTCTL_TXFNUM_MASK (0x1f << 6) +#define GRSTCTL_TXFNUM_SHIFT 6 +#define GRSTCTL_TXFNUM_LIMIT 0x1f +#define GRSTCTL_TXFNUM(_x) ((_x) << 6) +#define GRSTCTL_TXFFLSH BIT(5) +#define GRSTCTL_RXFFLSH BIT(4) +#define GRSTCTL_IN_TKNQ_FLSH BIT(3) +#define GRSTCTL_FRMCNTRRST BIT(2) +#define GRSTCTL_HSFTRST BIT(1) +#define GRSTCTL_CSFTRST BIT(0) + +#define GINTSTS HSOTG_REG(0x014) +#define GINTMSK HSOTG_REG(0x018) +#define GINTSTS_WKUPINT BIT(31) +#define GINTSTS_SESSREQINT BIT(30) +#define GINTSTS_DISCONNINT BIT(29) +#define GINTSTS_CONIDSTSCHNG BIT(28) +#define GINTSTS_LPMTRANRCVD BIT(27) +#define GINTSTS_PTXFEMP BIT(26) +#define GINTSTS_HCHINT BIT(25) +#define GINTSTS_PRTINT BIT(24) +#define GINTSTS_RESETDET BIT(23) +#define GINTSTS_FET_SUSP BIT(22) +#define GINTSTS_INCOMPL_IP BIT(21) +#define GINTSTS_INCOMPL_SOOUT BIT(21) +#define GINTSTS_INCOMPL_SOIN BIT(20) +#define GINTSTS_OEPINT BIT(19) +#define GINTSTS_IEPINT BIT(18) +#define GINTSTS_EPMIS BIT(17) +#define GINTSTS_RESTOREDONE BIT(16) +#define GINTSTS_EOPF BIT(15) +#define GINTSTS_ISOUTDROP BIT(14) +#define GINTSTS_ENUMDONE BIT(13) +#define GINTSTS_USBRST BIT(12) +#define GINTSTS_USBSUSP BIT(11) +#define GINTSTS_ERLYSUSP BIT(10) +#define GINTSTS_I2CINT BIT(9) +#define GINTSTS_ULPI_CK_INT BIT(8) +#define GINTSTS_GOUTNAKEFF BIT(7) +#define GINTSTS_GINNAKEFF BIT(6) +#define GINTSTS_NPTXFEMP BIT(5) +#define GINTSTS_RXFLVL BIT(4) +#define GINTSTS_SOF BIT(3) +#define GINTSTS_OTGINT BIT(2) +#define GINTSTS_MODEMIS BIT(1) +#define GINTSTS_CURMODE_HOST BIT(0) + +#define GRXSTSR HSOTG_REG(0x01C) +#define GRXSTSP HSOTG_REG(0x020) +#define GRXSTS_FN_MASK (0x7f << 25) +#define GRXSTS_FN_SHIFT 25 +#define GRXSTS_PKTSTS_MASK (0xf << 17) +#define GRXSTS_PKTSTS_SHIFT 17 +#define GRXSTS_PKTSTS_GLOBALOUTNAK 1 +#define GRXSTS_PKTSTS_OUTRX 2 +#define GRXSTS_PKTSTS_HCHIN 2 +#define GRXSTS_PKTSTS_OUTDONE 3 +#define GRXSTS_PKTSTS_HCHIN_XFER_COMP 3 +#define GRXSTS_PKTSTS_SETUPDONE 4 +#define GRXSTS_PKTSTS_DATATOGGLEERR 5 +#define GRXSTS_PKTSTS_SETUPRX 6 +#define GRXSTS_PKTSTS_HCHHALTED 7 +#define GRXSTS_HCHNUM_MASK (0xf << 0) +#define GRXSTS_HCHNUM_SHIFT 0 +#define GRXSTS_DPID_MASK (0x3 << 15) +#define GRXSTS_DPID_SHIFT 15 +#define GRXSTS_BYTECNT_MASK (0x7ff << 4) +#define GRXSTS_BYTECNT_SHIFT 4 +#define GRXSTS_EPNUM_MASK (0xf << 0) +#define GRXSTS_EPNUM_SHIFT 0 + +#define GRXFSIZ HSOTG_REG(0x024) +#define GRXFSIZ_DEPTH_MASK (0xffff << 0) +#define GRXFSIZ_DEPTH_SHIFT 0 + +#define GNPTXFSIZ HSOTG_REG(0x028) +/* Use FIFOSIZE_* constants to access this register */ + +#define GNPTXSTS HSOTG_REG(0x02C) +#define GNPTXSTS_NP_TXQ_TOP_MASK (0x7f << 24) +#define GNPTXSTS_NP_TXQ_TOP_SHIFT 24 +#define GNPTXSTS_NP_TXQ_SPC_AVAIL_MASK (0xff << 16) +#define GNPTXSTS_NP_TXQ_SPC_AVAIL_SHIFT 16 +#define GNPTXSTS_NP_TXQ_SPC_AVAIL_GET(_v) (((_v) >> 16) & 0xff) +#define GNPTXSTS_NP_TXF_SPC_AVAIL_MASK (0xffff << 0) +#define GNPTXSTS_NP_TXF_SPC_AVAIL_SHIFT 0 +#define GNPTXSTS_NP_TXF_SPC_AVAIL_GET(_v) (((_v) >> 0) & 0xffff) + +#define GI2CCTL HSOTG_REG(0x0030) +#define GI2CCTL_BSYDNE BIT(31) +#define GI2CCTL_RW BIT(30) +#define GI2CCTL_I2CDATSE0 BIT(28) +#define GI2CCTL_I2CDEVADDR_MASK (0x3 << 26) +#define GI2CCTL_I2CDEVADDR_SHIFT 26 +#define GI2CCTL_I2CSUSPCTL BIT(25) +#define GI2CCTL_ACK BIT(24) +#define GI2CCTL_I2CEN BIT(23) +#define GI2CCTL_ADDR_MASK (0x7f << 16) +#define GI2CCTL_ADDR_SHIFT 16 +#define GI2CCTL_REGADDR_MASK (0xff << 8) +#define GI2CCTL_REGADDR_SHIFT 8 +#define GI2CCTL_RWDATA_MASK (0xff << 0) +#define GI2CCTL_RWDATA_SHIFT 0 + +#define GPVNDCTL HSOTG_REG(0x0034) +#define GPVNDCTL_REGWR BIT(22) +#define GPVNDCTL_NEWREGREQ BIT(25) +#define GPVNDCTL_VSTSDONE BIT(27) +#define GPVNDCTL_REGADDR_SHIFT 16 +#define GPVNDCTL_REGADDR_MASK (0x3f << 16) +#define GPVNDCTL_REGDATA_SHIFT 0 +#define GPVNDCTL_REGDATA_MASK 0xff + +#define GGPIO HSOTG_REG(0x0038) +#define GGPIO_STM32_OTG_GCCFG_PWRDWN BIT(16) + +#define GUID HSOTG_REG(0x003c) +#define GSNPSID HSOTG_REG(0x0040) +#define GHWCFG1 HSOTG_REG(0x0044) +#define GSNPSID_ID_MASK GENMASK(31, 16) + +#define GHWCFG2 HSOTG_REG(0x0048) +#define GHWCFG2_OTG_ENABLE_IC_USB BIT(31) +#define GHWCFG2_DEV_TOKEN_Q_DEPTH_MASK (0x1f << 26) +#define GHWCFG2_DEV_TOKEN_Q_DEPTH_SHIFT 26 +#define GHWCFG2_HOST_PERIO_TX_Q_DEPTH_MASK (0x3 << 24) +#define GHWCFG2_HOST_PERIO_TX_Q_DEPTH_SHIFT 24 +#define GHWCFG2_NONPERIO_TX_Q_DEPTH_MASK (0x3 << 22) +#define GHWCFG2_NONPERIO_TX_Q_DEPTH_SHIFT 22 +#define GHWCFG2_MULTI_PROC_INT BIT(20) +#define GHWCFG2_DYNAMIC_FIFO BIT(19) +#define GHWCFG2_PERIO_EP_SUPPORTED BIT(18) +#define GHWCFG2_NUM_HOST_CHAN_MASK (0xf << 14) +#define GHWCFG2_NUM_HOST_CHAN_SHIFT 14 +#define GHWCFG2_NUM_DEV_EP_MASK (0xf << 10) +#define GHWCFG2_NUM_DEV_EP_SHIFT 10 +#define GHWCFG2_FS_PHY_TYPE_MASK (0x3 << 8) +#define GHWCFG2_FS_PHY_TYPE_SHIFT 8 +#define GHWCFG2_FS_PHY_TYPE_NOT_SUPPORTED 0 +#define GHWCFG2_FS_PHY_TYPE_DEDICATED 1 +#define GHWCFG2_FS_PHY_TYPE_SHARED_UTMI 2 +#define GHWCFG2_FS_PHY_TYPE_SHARED_ULPI 3 +#define GHWCFG2_HS_PHY_TYPE_MASK (0x3 << 6) +#define GHWCFG2_HS_PHY_TYPE_SHIFT 6 +#define GHWCFG2_HS_PHY_TYPE_NOT_SUPPORTED 0 +#define GHWCFG2_HS_PHY_TYPE_UTMI 1 +#define GHWCFG2_HS_PHY_TYPE_ULPI 2 +#define GHWCFG2_HS_PHY_TYPE_UTMI_ULPI 3 +#define GHWCFG2_POINT2POINT BIT(5) +#define GHWCFG2_ARCHITECTURE_MASK (0x3 << 3) +#define GHWCFG2_ARCHITECTURE_SHIFT 3 +#define GHWCFG2_SLAVE_ONLY_ARCH 0 +#define GHWCFG2_EXT_DMA_ARCH 1 +#define GHWCFG2_INT_DMA_ARCH 2 +#define GHWCFG2_OP_MODE_MASK (0x7 << 0) +#define GHWCFG2_OP_MODE_SHIFT 0 +#define GHWCFG2_OP_MODE_HNP_SRP_CAPABLE 0 +#define GHWCFG2_OP_MODE_SRP_ONLY_CAPABLE 1 +#define GHWCFG2_OP_MODE_NO_HNP_SRP_CAPABLE 2 +#define GHWCFG2_OP_MODE_SRP_CAPABLE_DEVICE 3 +#define GHWCFG2_OP_MODE_NO_SRP_CAPABLE_DEVICE 4 +#define GHWCFG2_OP_MODE_SRP_CAPABLE_HOST 5 +#define GHWCFG2_OP_MODE_NO_SRP_CAPABLE_HOST 6 +#define GHWCFG2_OP_MODE_UNDEFINED 7 + +#define GHWCFG3 HSOTG_REG(0x004c) +#define GHWCFG3_DFIFO_DEPTH_MASK (0xffff << 16) +#define GHWCFG3_DFIFO_DEPTH_SHIFT 16 +#define GHWCFG3_OTG_LPM_EN BIT(15) +#define GHWCFG3_BC_SUPPORT BIT(14) +#define GHWCFG3_OTG_ENABLE_HSIC BIT(13) +#define GHWCFG3_ADP_SUPP BIT(12) +#define GHWCFG3_SYNCH_RESET_TYPE BIT(11) +#define GHWCFG3_OPTIONAL_FEATURES BIT(10) +#define GHWCFG3_VENDOR_CTRL_IF BIT(9) +#define GHWCFG3_I2C BIT(8) +#define GHWCFG3_OTG_FUNC BIT(7) +#define GHWCFG3_PACKET_SIZE_CNTR_WIDTH_MASK (0x7 << 4) +#define GHWCFG3_PACKET_SIZE_CNTR_WIDTH_SHIFT 4 +#define GHWCFG3_XFER_SIZE_CNTR_WIDTH_MASK (0xf << 0) +#define GHWCFG3_XFER_SIZE_CNTR_WIDTH_SHIFT 0 + +#define GHWCFG4 HSOTG_REG(0x0050) +#define GHWCFG4_DESC_DMA_DYN BIT(31) +#define GHWCFG4_DESC_DMA BIT(30) +#define GHWCFG4_NUM_IN_EPS_MASK (0xf << 26) +#define GHWCFG4_NUM_IN_EPS_SHIFT 26 +#define GHWCFG4_DED_FIFO_EN BIT(25) +#define GHWCFG4_DED_FIFO_SHIFT 25 +#define GHWCFG4_SESSION_END_FILT_EN BIT(24) +#define GHWCFG4_B_VALID_FILT_EN BIT(23) +#define GHWCFG4_A_VALID_FILT_EN BIT(22) +#define GHWCFG4_VBUS_VALID_FILT_EN BIT(21) +#define GHWCFG4_IDDIG_FILT_EN BIT(20) +#define GHWCFG4_NUM_DEV_MODE_CTRL_EP_MASK (0xf << 16) +#define GHWCFG4_NUM_DEV_MODE_CTRL_EP_SHIFT 16 +#define GHWCFG4_UTMI_PHY_DATA_WIDTH_MASK (0x3 << 14) +#define GHWCFG4_UTMI_PHY_DATA_WIDTH_SHIFT 14 +#define GHWCFG4_ACG_SUPPORTED BIT(12) +#define GHWCFG4_IPG_ISOC_SUPPORTED BIT(11) +#define GHWCFG4_UTMI_PHY_DATA_WIDTH_8 0 +#define GHWCFG4_UTMI_PHY_DATA_WIDTH_16 1 +#define GHWCFG4_UTMI_PHY_DATA_WIDTH_8_OR_16 2 +#define GHWCFG4_XHIBER BIT(7) +#define GHWCFG4_HIBER BIT(6) +#define GHWCFG4_MIN_AHB_FREQ BIT(5) +#define GHWCFG4_POWER_OPTIMIZ BIT(4) +#define GHWCFG4_NUM_DEV_PERIO_IN_EP_MASK (0xf << 0) +#define GHWCFG4_NUM_DEV_PERIO_IN_EP_SHIFT 0 + +#define GLPMCFG HSOTG_REG(0x0054) +#define GLPMCFG_INVSELHSIC BIT(31) +#define GLPMCFG_HSICCON BIT(30) +#define GLPMCFG_RSTRSLPSTS BIT(29) +#define GLPMCFG_ENBESL BIT(28) +#define GLPMCFG_LPM_RETRYCNT_STS_MASK (0x7 << 25) +#define GLPMCFG_LPM_RETRYCNT_STS_SHIFT 25 +#define GLPMCFG_SNDLPM BIT(24) +#define GLPMCFG_RETRY_CNT_MASK (0x7 << 21) +#define GLPMCFG_RETRY_CNT_SHIFT 21 +#define GLPMCFG_LPM_CHNL_INDX_MASK (0xf << 17) +#define GLPMCFG_LPM_CHNL_INDX_SHIFT 17 +#define GLPMCFG_L1RESUMEOK BIT(16) +#define GLPMCFG_SLPSTS BIT(15) +#define GLPMCFG_COREL1RES_MASK (0x3 << 13) +#define GLPMCFG_COREL1RES_SHIFT 13 +#define GLPMCFG_HIRD_THRES_MASK (0x1f << 8) +#define GLPMCFG_HIRD_THRES_SHIFT 8 +#define GLPMCFG_HIRD_THRES_EN (0x10 << 8) +#define GLPMCFG_ENBLSLPM BIT(7) +#define GLPMCFG_BREMOTEWAKE BIT(6) +#define GLPMCFG_HIRD_MASK (0xf << 2) +#define GLPMCFG_HIRD_SHIFT 2 +#define GLPMCFG_APPL1RES BIT(1) +#define GLPMCFG_LPMCAP BIT(0) + +#define GPWRDN HSOTG_REG(0x0058) +#define GPWRDN_MULT_VAL_ID_BC_MASK (0x1f << 24) +#define GPWRDN_MULT_VAL_ID_BC_SHIFT 24 +#define GPWRDN_ADP_INT BIT(23) +#define GPWRDN_BSESSVLD BIT(22) +#define GPWRDN_IDSTS BIT(21) +#define GPWRDN_LINESTATE_MASK (0x3 << 19) +#define GPWRDN_LINESTATE_SHIFT 19 +#define GPWRDN_STS_CHGINT_MSK BIT(18) +#define GPWRDN_STS_CHGINT BIT(17) +#define GPWRDN_SRP_DET_MSK BIT(16) +#define GPWRDN_SRP_DET BIT(15) +#define GPWRDN_CONNECT_DET_MSK BIT(14) +#define GPWRDN_CONNECT_DET BIT(13) +#define GPWRDN_DISCONN_DET_MSK BIT(12) +#define GPWRDN_DISCONN_DET BIT(11) +#define GPWRDN_RST_DET_MSK BIT(10) +#define GPWRDN_RST_DET BIT(9) +#define GPWRDN_LNSTSCHG_MSK BIT(8) +#define GPWRDN_LNSTSCHG BIT(7) +#define GPWRDN_DIS_VBUS BIT(6) +#define GPWRDN_PWRDNSWTCH BIT(5) +#define GPWRDN_PWRDNRSTN BIT(4) +#define GPWRDN_PWRDNCLMP BIT(3) +#define GPWRDN_RESTORE BIT(2) +#define GPWRDN_PMUACTV BIT(1) +#define GPWRDN_PMUINTSEL BIT(0) + +#define GDFIFOCFG HSOTG_REG(0x005c) +#define GDFIFOCFG_EPINFOBASE_MASK (0xffff << 16) +#define GDFIFOCFG_EPINFOBASE_SHIFT 16 +#define GDFIFOCFG_GDFIFOCFG_MASK (0xffff << 0) +#define GDFIFOCFG_GDFIFOCFG_SHIFT 0 + +#define ADPCTL HSOTG_REG(0x0060) +#define ADPCTL_AR_MASK (0x3 << 27) +#define ADPCTL_AR_SHIFT 27 +#define ADPCTL_ADP_TMOUT_INT_MSK BIT(26) +#define ADPCTL_ADP_SNS_INT_MSK BIT(25) +#define ADPCTL_ADP_PRB_INT_MSK BIT(24) +#define ADPCTL_ADP_TMOUT_INT BIT(23) +#define ADPCTL_ADP_SNS_INT BIT(22) +#define ADPCTL_ADP_PRB_INT BIT(21) +#define ADPCTL_ADPENA BIT(20) +#define ADPCTL_ADPRES BIT(19) +#define ADPCTL_ENASNS BIT(18) +#define ADPCTL_ENAPRB BIT(17) +#define ADPCTL_RTIM_MASK (0x7ff << 6) +#define ADPCTL_RTIM_SHIFT 6 +#define ADPCTL_PRB_PER_MASK (0x3 << 4) +#define ADPCTL_PRB_PER_SHIFT 4 +#define ADPCTL_PRB_DELTA_MASK (0x3 << 2) +#define ADPCTL_PRB_DELTA_SHIFT 2 +#define ADPCTL_PRB_DSCHRG_MASK (0x3 << 0) +#define ADPCTL_PRB_DSCHRG_SHIFT 0 + +#define HPTXFSIZ HSOTG_REG(0x100) +/* Use FIFOSIZE_* constants to access this register */ + +#define DPTXFSIZN(_a) HSOTG_REG(0x104 + (((_a) - 1) * 4)) +/* Use FIFOSIZE_* constants to access this register */ + +/* These apply to the GNPTXFSIZ, HPTXFSIZ and DPTXFSIZN registers */ +#define FIFOSIZE_DEPTH_MASK (0xffff << 16) +#define FIFOSIZE_DEPTH_SHIFT 16 +#define FIFOSIZE_STARTADDR_MASK (0xffff << 0) +#define FIFOSIZE_STARTADDR_SHIFT 0 +#define FIFOSIZE_DEPTH_GET(_x) (((_x) >> 16) & 0xffff) + +/* Device mode registers */ + +#define DCFG HSOTG_REG(0x800) +#define DCFG_DESCDMA_EN BIT(23) +#define DCFG_EPMISCNT_MASK (0x1f << 18) +#define DCFG_EPMISCNT_SHIFT 18 +#define DCFG_EPMISCNT_LIMIT 0x1f +#define DCFG_EPMISCNT(_x) ((_x) << 18) +#define DCFG_IPG_ISOC_SUPPORDED BIT(17) +#define DCFG_PERFRINT_MASK (0x3 << 11) +#define DCFG_PERFRINT_SHIFT 11 +#define DCFG_PERFRINT_LIMIT 0x3 +#define DCFG_PERFRINT(_x) ((_x) << 11) +#define DCFG_DEVADDR_MASK (0x7f << 4) +#define DCFG_DEVADDR_SHIFT 4 +#define DCFG_DEVADDR_LIMIT 0x7f +#define DCFG_DEVADDR(_x) ((_x) << 4) +#define DCFG_NZ_STS_OUT_HSHK BIT(2) +#define DCFG_DEVSPD_MASK (0x3 << 0) +#define DCFG_DEVSPD_SHIFT 0 +#define DCFG_DEVSPD_HS 0 +#define DCFG_DEVSPD_FS 1 +#define DCFG_DEVSPD_LS 2 +#define DCFG_DEVSPD_FS48 3 + +#define DCTL HSOTG_REG(0x804) +#define DCTL_PWRONPRGDONE BIT(11) +#define DCTL_CGOUTNAK BIT(10) +#define DCTL_SGOUTNAK BIT(9) +#define DCTL_CGNPINNAK BIT(8) +#define DCTL_SGNPINNAK BIT(7) +#define DCTL_TSTCTL_MASK (0x7 << 4) +#define DCTL_TSTCTL_SHIFT 4 +#define DCTL_GOUTNAKSTS BIT(3) +#define DCTL_GNPINNAKSTS BIT(2) +#define DCTL_SFTDISCON BIT(1) +#define DCTL_RMTWKUPSIG BIT(0) + +#define DSTS HSOTG_REG(0x808) +#define DSTS_SOFFN_MASK (0x3fff << 8) +#define DSTS_SOFFN_SHIFT 8 +#define DSTS_SOFFN_LIMIT 0x3fff +#define DSTS_SOFFN(_x) ((_x) << 8) +#define DSTS_ERRATICERR BIT(3) +#define DSTS_ENUMSPD_MASK (0x3 << 1) +#define DSTS_ENUMSPD_SHIFT 1 +#define DSTS_ENUMSPD_HS 0 +#define DSTS_ENUMSPD_FS 1 +#define DSTS_ENUMSPD_LS 2 +#define DSTS_ENUMSPD_FS48 3 +#define DSTS_SUSPSTS BIT(0) + +#define DIEPMSK HSOTG_REG(0x810) +#define DIEPMSK_NAKMSK BIT(13) +#define DIEPMSK_BNAININTRMSK BIT(9) +#define DIEPMSK_TXFIFOUNDRNMSK BIT(8) +#define DIEPMSK_TXFIFOEMPTY BIT(7) +#define DIEPMSK_INEPNAKEFFMSK BIT(6) +#define DIEPMSK_INTKNEPMISMSK BIT(5) +#define DIEPMSK_INTKNTXFEMPMSK BIT(4) +#define DIEPMSK_TIMEOUTMSK BIT(3) +#define DIEPMSK_AHBERRMSK BIT(2) +#define DIEPMSK_EPDISBLDMSK BIT(1) +#define DIEPMSK_XFERCOMPLMSK BIT(0) + +#define DOEPMSK HSOTG_REG(0x814) +#define DOEPMSK_BNAMSK BIT(9) +#define DOEPMSK_BACK2BACKSETUP BIT(6) +#define DOEPMSK_STSPHSERCVDMSK BIT(5) +#define DOEPMSK_OUTTKNEPDISMSK BIT(4) +#define DOEPMSK_SETUPMSK BIT(3) +#define DOEPMSK_AHBERRMSK BIT(2) +#define DOEPMSK_EPDISBLDMSK BIT(1) +#define DOEPMSK_XFERCOMPLMSK BIT(0) + +#define DAINT HSOTG_REG(0x818) +#define DAINTMSK HSOTG_REG(0x81C) +#define DAINT_OUTEP_SHIFT 16 +#define DAINT_OUTEP(_x) (1 << ((_x) + 16)) +#define DAINT_INEP(_x) (1 << (_x)) + +#define DTKNQR1 HSOTG_REG(0x820) +#define DTKNQR2 HSOTG_REG(0x824) +#define DTKNQR3 HSOTG_REG(0x830) +#define DTKNQR4 HSOTG_REG(0x834) +#define DIEPEMPMSK HSOTG_REG(0x834) + +#define DVBUSDIS HSOTG_REG(0x828) +#define DVBUSPULSE HSOTG_REG(0x82C) + +#define DIEPCTL0 HSOTG_REG(0x900) +#define DIEPCTL(_a) HSOTG_REG(0x900 + ((_a) * 0x20)) + +#define DOEPCTL0 HSOTG_REG(0xB00) +#define DOEPCTL(_a) HSOTG_REG(0xB00 + ((_a) * 0x20)) + +/* EP0 specialness: + * bits[29..28] - reserved (no SetD0PID, SetD1PID) + * bits[25..22] - should always be zero, this isn't a periodic endpoint + * bits[10..0] - MPS setting different for EP0 + */ +#define D0EPCTL_MPS_MASK (0x3 << 0) +#define D0EPCTL_MPS_SHIFT 0 +#define D0EPCTL_MPS_64 0 +#define D0EPCTL_MPS_32 1 +#define D0EPCTL_MPS_16 2 +#define D0EPCTL_MPS_8 3 +#define D0EPCTL_MPS_LIMIT 64 + +#define DXEPCTL_EPENA BIT(31) +#define DXEPCTL_EPDIS BIT(30) +#define DXEPCTL_SETD1PID BIT(29) +#define DXEPCTL_SETODDFR BIT(29) +#define DXEPCTL_SETD0PID BIT(28) +#define DXEPCTL_SETEVENFR BIT(28) +#define DXEPCTL_SNAK BIT(27) +#define DXEPCTL_CNAK BIT(26) +#define DXEPCTL_TXFNUM_MASK (0xf << 22) +#define DXEPCTL_TXFNUM_SHIFT 22 +#define DXEPCTL_TXFNUM_LIMIT 0xf +#define DXEPCTL_TXFNUM(_x) ((_x) << 22) +#define DXEPCTL_STALL BIT(21) +#define DXEPCTL_SNP BIT(20) +#define DXEPCTL_EPTYPE_MASK (0x3 << 18) +#define DXEPCTL_EPTYPE_CONTROL (0x0 << 18) +#define DXEPCTL_EPTYPE_ISO (0x1 << 18) +#define DXEPCTL_EPTYPE_BULK (0x2 << 18) +#define DXEPCTL_EPTYPE_INTERRUPT (0x3 << 18) + +#define DXEPCTL_NAKSTS BIT(17) +#define DXEPCTL_DPID BIT(16) +#define DXEPCTL_EOFRNUM BIT(16) +#define DXEPCTL_USBACTEP BIT(15) +#define DXEPCTL_NEXTEP_MASK (0xf << 11) +#define DXEPCTL_NEXTEP_SHIFT 11 +#define DXEPCTL_NEXTEP_LIMIT 0xf +#define DXEPCTL_NEXTEP(_x) ((_x) << 11) +#define DXEPCTL_MPS_MASK (0x7ff << 0) +#define DXEPCTL_MPS_SHIFT 0 +#define DXEPCTL_MPS_LIMIT 0x7ff +#define DXEPCTL_MPS(_x) ((_x) << 0) + +#define DIEPINT(_a) HSOTG_REG(0x908 + ((_a) * 0x20)) +#define DOEPINT(_a) HSOTG_REG(0xB08 + ((_a) * 0x20)) +#define DXEPINT_SETUP_RCVD BIT(15) +#define DXEPINT_NYETINTRPT BIT(14) +#define DXEPINT_NAKINTRPT BIT(13) +#define DXEPINT_BBLEERRINTRPT BIT(12) +#define DXEPINT_PKTDRPSTS BIT(11) +#define DXEPINT_BNAINTR BIT(9) +#define DXEPINT_TXFIFOUNDRN BIT(8) +#define DXEPINT_OUTPKTERR BIT(8) +#define DXEPINT_TXFEMP BIT(7) +#define DXEPINT_INEPNAKEFF BIT(6) +#define DXEPINT_BACK2BACKSETUP BIT(6) +#define DXEPINT_INTKNEPMIS BIT(5) +#define DXEPINT_STSPHSERCVD BIT(5) +#define DXEPINT_INTKNTXFEMP BIT(4) +#define DXEPINT_OUTTKNEPDIS BIT(4) +#define DXEPINT_TIMEOUT BIT(3) +#define DXEPINT_SETUP BIT(3) +#define DXEPINT_AHBERR BIT(2) +#define DXEPINT_EPDISBLD BIT(1) +#define DXEPINT_XFERCOMPL BIT(0) + +#define DIEPTSIZ0 HSOTG_REG(0x910) +#define DIEPTSIZ0_PKTCNT_MASK (0x3 << 19) +#define DIEPTSIZ0_PKTCNT_SHIFT 19 +#define DIEPTSIZ0_PKTCNT_LIMIT 0x3 +#define DIEPTSIZ0_PKTCNT(_x) ((_x) << 19) +#define DIEPTSIZ0_XFERSIZE_MASK (0x7f << 0) +#define DIEPTSIZ0_XFERSIZE_SHIFT 0 +#define DIEPTSIZ0_XFERSIZE_LIMIT 0x7f +#define DIEPTSIZ0_XFERSIZE(_x) ((_x) << 0) + +#define DOEPTSIZ0 HSOTG_REG(0xB10) +#define DOEPTSIZ0_SUPCNT_MASK (0x3 << 29) +#define DOEPTSIZ0_SUPCNT_SHIFT 29 +#define DOEPTSIZ0_SUPCNT_LIMIT 0x3 +#define DOEPTSIZ0_SUPCNT(_x) ((_x) << 29) +#define DOEPTSIZ0_PKTCNT BIT(19) +#define DOEPTSIZ0_XFERSIZE_MASK (0x7f << 0) +#define DOEPTSIZ0_XFERSIZE_SHIFT 0 + +#define DIEPTSIZ(_a) HSOTG_REG(0x910 + ((_a) * 0x20)) +#define DOEPTSIZ(_a) HSOTG_REG(0xB10 + ((_a) * 0x20)) +#define DXEPTSIZ_MC_MASK (0x3 << 29) +#define DXEPTSIZ_MC_SHIFT 29 +#define DXEPTSIZ_MC_LIMIT 0x3 +#define DXEPTSIZ_MC(_x) ((_x) << 29) +#define DXEPTSIZ_PKTCNT_MASK (0x3ff << 19) +#define DXEPTSIZ_PKTCNT_SHIFT 19 +#define DXEPTSIZ_PKTCNT_LIMIT 0x3ff +#define DXEPTSIZ_PKTCNT_GET(_v) (((_v) >> 19) & 0x3ff) +#define DXEPTSIZ_PKTCNT(_x) ((_x) << 19) +#define DXEPTSIZ_XFERSIZE_MASK (0x7ffff << 0) +#define DXEPTSIZ_XFERSIZE_SHIFT 0 +#define DXEPTSIZ_XFERSIZE_LIMIT 0x7ffff +#define DXEPTSIZ_XFERSIZE_GET(_v) (((_v) >> 0) & 0x7ffff) +#define DXEPTSIZ_XFERSIZE(_x) ((_x) << 0) + +#define DIEPDMA(_a) HSOTG_REG(0x914 + ((_a) * 0x20)) +#define DOEPDMA(_a) HSOTG_REG(0xB14 + ((_a) * 0x20)) + +#define DTXFSTS(_a) HSOTG_REG(0x918 + ((_a) * 0x20)) + +#define PCGCTL HSOTG_REG(0x0e00) +#define PCGCTL_IF_DEV_MODE BIT(31) +#define PCGCTL_P2HD_PRT_SPD_MASK (0x3 << 29) +#define PCGCTL_P2HD_PRT_SPD_SHIFT 29 +#define PCGCTL_P2HD_DEV_ENUM_SPD_MASK (0x3 << 27) +#define PCGCTL_P2HD_DEV_ENUM_SPD_SHIFT 27 +#define PCGCTL_MAC_DEV_ADDR_MASK (0x7f << 20) +#define PCGCTL_MAC_DEV_ADDR_SHIFT 20 +#define PCGCTL_MAX_TERMSEL BIT(19) +#define PCGCTL_MAX_XCVRSELECT_MASK (0x3 << 17) +#define PCGCTL_MAX_XCVRSELECT_SHIFT 17 +#define PCGCTL_PORT_POWER BIT(16) +#define PCGCTL_PRT_CLK_SEL_MASK (0x3 << 14) +#define PCGCTL_PRT_CLK_SEL_SHIFT 14 +#define PCGCTL_ESS_REG_RESTORED BIT(13) +#define PCGCTL_EXTND_HIBER_SWITCH BIT(12) +#define PCGCTL_EXTND_HIBER_PWRCLMP BIT(11) +#define PCGCTL_ENBL_EXTND_HIBER BIT(10) +#define PCGCTL_RESTOREMODE BIT(9) +#define PCGCTL_RESETAFTSUSP BIT(8) +#define PCGCTL_DEEP_SLEEP BIT(7) +#define PCGCTL_PHY_IN_SLEEP BIT(6) +#define PCGCTL_ENBL_SLEEP_GATING BIT(5) +#define PCGCTL_RSTPDWNMODULE BIT(3) +#define PCGCTL_PWRCLMP BIT(2) +#define PCGCTL_GATEHCLK BIT(1) +#define PCGCTL_STOPPCLK BIT(0) + +#define PCGCCTL1 HSOTG_REG(0xe04) +#define PCGCCTL1_TIMER (0x3 << 1) +#define PCGCCTL1_GATEEN BIT(0) + +#define EPFIFO(_a) HSOTG_REG(0x1000 + ((_a) * 0x1000)) + +/* Host Mode Registers */ + +#define HCFG HSOTG_REG(0x0400) +#define HCFG_MODECHTIMEN BIT(31) +#define HCFG_PERSCHEDENA BIT(26) +#define HCFG_FRLISTEN_MASK (0x3 << 24) +#define HCFG_FRLISTEN_SHIFT 24 +#define HCFG_FRLISTEN_8 (0 << 24) +#define FRLISTEN_8_SIZE 8 +#define HCFG_FRLISTEN_16 BIT(24) +#define FRLISTEN_16_SIZE 16 +#define HCFG_FRLISTEN_32 (2 << 24) +#define FRLISTEN_32_SIZE 32 +#define HCFG_FRLISTEN_64 (3 << 24) +#define FRLISTEN_64_SIZE 64 +#define HCFG_DESCDMA BIT(23) +#define HCFG_RESVALID_MASK (0xff << 8) +#define HCFG_RESVALID_SHIFT 8 +#define HCFG_ENA32KHZ BIT(7) +#define HCFG_FSLSSUPP BIT(2) +#define HCFG_FSLSPCLKSEL_MASK (0x3 << 0) +#define HCFG_FSLSPCLKSEL_SHIFT 0 +#define HCFG_FSLSPCLKSEL_30_60_MHZ 0 +#define HCFG_FSLSPCLKSEL_48_MHZ 1 +#define HCFG_FSLSPCLKSEL_6_MHZ 2 + +#define HFIR HSOTG_REG(0x0404) +#define HFIR_FRINT_MASK (0xffff << 0) +#define HFIR_FRINT_SHIFT 0 +#define HFIR_RLDCTRL BIT(16) + +#define HFNUM HSOTG_REG(0x0408) +#define HFNUM_FRREM_MASK (0xffff << 16) +#define HFNUM_FRREM_SHIFT 16 +#define HFNUM_FRNUM_MASK (0xffff << 0) +#define HFNUM_FRNUM_SHIFT 0 +#define HFNUM_MAX_FRNUM 0x3fff + +#define HPTXSTS HSOTG_REG(0x0410) +#define TXSTS_QTOP_ODD BIT(31) +#define TXSTS_QTOP_CHNEP_MASK (0xf << 27) +#define TXSTS_QTOP_CHNEP_SHIFT 27 +#define TXSTS_QTOP_TOKEN_MASK (0x3 << 25) +#define TXSTS_QTOP_TOKEN_SHIFT 25 +#define TXSTS_QTOP_TERMINATE BIT(24) +#define TXSTS_QSPCAVAIL_MASK (0xff << 16) +#define TXSTS_QSPCAVAIL_SHIFT 16 +#define TXSTS_FSPCAVAIL_MASK (0xffff << 0) +#define TXSTS_FSPCAVAIL_SHIFT 0 + +#define HAINT HSOTG_REG(0x0414) +#define HAINTMSK HSOTG_REG(0x0418) +#define HFLBADDR HSOTG_REG(0x041c) + +#define HPRT0 HSOTG_REG(0x0440) +#define HPRT0_SPD_MASK (0x3 << 17) +#define HPRT0_SPD_SHIFT 17 +#define HPRT0_SPD_HIGH_SPEED 0 +#define HPRT0_SPD_FULL_SPEED 1 +#define HPRT0_SPD_LOW_SPEED 2 +#define HPRT0_TSTCTL_MASK (0xf << 13) +#define HPRT0_TSTCTL_SHIFT 13 +#define HPRT0_PWR BIT(12) +#define HPRT0_LNSTS_MASK (0x3 << 10) +#define HPRT0_LNSTS_SHIFT 10 +#define HPRT0_RST BIT(8) +#define HPRT0_SUSP BIT(7) +#define HPRT0_RES BIT(6) +#define HPRT0_OVRCURRCHG BIT(5) +#define HPRT0_OVRCURRACT BIT(4) +#define HPRT0_ENACHG BIT(3) +#define HPRT0_ENA BIT(2) +#define HPRT0_CONNDET BIT(1) +#define HPRT0_CONNSTS BIT(0) + +#define HCCHAR(_ch) HSOTG_REG(0x0500 + 0x20 * (_ch)) +#define HCCHAR_CHENA BIT(31) +#define HCCHAR_CHDIS BIT(30) +#define HCCHAR_ODDFRM BIT(29) +#define HCCHAR_DEVADDR_MASK (0x7f << 22) +#define HCCHAR_DEVADDR_SHIFT 22 +#define HCCHAR_MULTICNT_MASK (0x3 << 20) +#define HCCHAR_MULTICNT_SHIFT 20 +#define HCCHAR_EPTYPE_MASK (0x3 << 18) +#define HCCHAR_EPTYPE_SHIFT 18 +#define HCCHAR_LSPDDEV BIT(17) +#define HCCHAR_EPDIR BIT(15) +#define HCCHAR_EPNUM_MASK (0xf << 11) +#define HCCHAR_EPNUM_SHIFT 11 +#define HCCHAR_MPS_MASK (0x7ff << 0) +#define HCCHAR_MPS_SHIFT 0 + +#define HCSPLT(_ch) HSOTG_REG(0x0504 + 0x20 * (_ch)) +#define HCSPLT_SPLTENA BIT(31) +#define HCSPLT_COMPSPLT BIT(16) +#define HCSPLT_XACTPOS_MASK (0x3 << 14) +#define HCSPLT_XACTPOS_SHIFT 14 +#define HCSPLT_XACTPOS_MID 0 +#define HCSPLT_XACTPOS_END 1 +#define HCSPLT_XACTPOS_BEGIN 2 +#define HCSPLT_XACTPOS_ALL 3 +#define HCSPLT_HUBADDR_MASK (0x7f << 7) +#define HCSPLT_HUBADDR_SHIFT 7 +#define HCSPLT_PRTADDR_MASK (0x7f << 0) +#define HCSPLT_PRTADDR_SHIFT 0 + +#define HCINT(_ch) HSOTG_REG(0x0508 + 0x20 * (_ch)) +#define HCINTMSK(_ch) HSOTG_REG(0x050c + 0x20 * (_ch)) +#define HCINTMSK_RESERVED14_31 (0x3ffff << 14) +#define HCINTMSK_FRM_LIST_ROLL BIT(13) +#define HCINTMSK_XCS_XACT BIT(12) +#define HCINTMSK_BNA BIT(11) +#define HCINTMSK_DATATGLERR BIT(10) +#define HCINTMSK_FRMOVRUN BIT(9) +#define HCINTMSK_BBLERR BIT(8) +#define HCINTMSK_XACTERR BIT(7) +#define HCINTMSK_NYET BIT(6) +#define HCINTMSK_ACK BIT(5) +#define HCINTMSK_NAK BIT(4) +#define HCINTMSK_STALL BIT(3) +#define HCINTMSK_AHBERR BIT(2) +#define HCINTMSK_CHHLTD BIT(1) +#define HCINTMSK_XFERCOMPL BIT(0) + +#define HCTSIZ(_ch) HSOTG_REG(0x0510 + 0x20 * (_ch)) +#define TSIZ_DOPNG BIT(31) +#define TSIZ_SC_MC_PID_MASK (0x3 << 29) +#define TSIZ_SC_MC_PID_SHIFT 29 +#define TSIZ_SC_MC_PID_DATA0 0 +#define TSIZ_SC_MC_PID_DATA2 1 +#define TSIZ_SC_MC_PID_DATA1 2 +#define TSIZ_SC_MC_PID_MDATA 3 +#define TSIZ_SC_MC_PID_SETUP 3 +#define TSIZ_PKTCNT_MASK (0x3ff << 19) +#define TSIZ_PKTCNT_SHIFT 19 +#define TSIZ_NTD_MASK (0xff << 8) +#define TSIZ_NTD_SHIFT 8 +#define TSIZ_SCHINFO_MASK (0xff << 0) +#define TSIZ_SCHINFO_SHIFT 0 +#define TSIZ_XFERSIZE_MASK (0x7ffff << 0) +#define TSIZ_XFERSIZE_SHIFT 0 + +#define HCDMA(_ch) HSOTG_REG(0x0514 + 0x20 * (_ch)) + +#define HCDMAB(_ch) HSOTG_REG(0x051c + 0x20 * (_ch)) + +#define HCFIFO(_ch) HSOTG_REG(0x1000 + 0x1000 * (_ch)) + +/** + * struct dwc2_dma_desc - DMA descriptor structure, + * used for both host and gadget modes + * + * @status: DMA descriptor status quadlet + * @buf: DMA descriptor data buffer pointer + * + * DMA Descriptor structure contains two quadlets: + * Status quadlet and Data buffer pointer. + */ +struct dwc2_dma_desc { + u32 status; + u32 buf; +} __packed; + +/* Host Mode DMA descriptor status quadlet */ + +#define HOST_DMA_A BIT(31) +#define HOST_DMA_STS_MASK (0x3 << 28) +#define HOST_DMA_STS_SHIFT 28 +#define HOST_DMA_STS_PKTERR BIT(28) +#define HOST_DMA_EOL BIT(26) +#define HOST_DMA_IOC BIT(25) +#define HOST_DMA_SUP BIT(24) +#define HOST_DMA_ALT_QTD BIT(23) +#define HOST_DMA_QTD_OFFSET_MASK (0x3f << 17) +#define HOST_DMA_QTD_OFFSET_SHIFT 17 +#define HOST_DMA_ISOC_NBYTES_MASK (0xfff << 0) +#define HOST_DMA_ISOC_NBYTES_SHIFT 0 +#define HOST_DMA_NBYTES_MASK (0x1ffff << 0) +#define HOST_DMA_NBYTES_SHIFT 0 +#define HOST_DMA_NBYTES_LIMIT 131071 + +#define MAX_DMA_DESC_NUM_GENERIC 64 +#define MAX_DMA_DESC_NUM_HS_ISOC 256 + + /* DWC OTG HW Release versions */ +#define DWC2_CORE_REV_2_71a 0x4f54271a +#define DWC2_CORE_REV_2_72a 0x4f54272a +#define DWC2_CORE_REV_2_80a 0x4f54280a +#define DWC2_CORE_REV_2_90a 0x4f54290a +#define DWC2_CORE_REV_2_91a 0x4f54291a +#define DWC2_CORE_REV_2_92a 0x4f54292a +#define DWC2_CORE_REV_2_94a 0x4f54294a +#define DWC2_CORE_REV_3_00a 0x4f54300a +#define DWC2_CORE_REV_3_10a 0x4f54310a +#define DWC2_CORE_REV_4_00a 0x4f54400a +#define DWC2_FS_IOT_REV_1_00a 0x5531100a +#define DWC2_HS_IOT_REV_1_00a 0x5532100a + + /* DWC OTG HW Core ID */ +#define DWC2_OTG_ID 0x4f540000 +#define DWC2_FS_IOT_ID 0x55310000 +#define DWC2_HS_IOT_ID 0x55320000 + +#endif /* __DWC2_H__ */ diff --git a/drivers/usb/dwc2/rhub.c b/drivers/usb/dwc2/rhub.c new file mode 100644 index 0000000000..3560714444 --- /dev/null +++ b/drivers/usb/dwc2/rhub.c @@ -0,0 +1,384 @@ +// SPDX-License-Identifier: GPL-2.0+ +#include "dwc2.h" + +static struct descriptor { + struct usb_hub_descriptor hub; + struct usb_device_descriptor device; + struct usb_config_descriptor config; + struct usb_interface_descriptor interface; + struct usb_endpoint_descriptor endpoint; +} __packed descriptor = { + .hub = { + .bLength = USB_DT_HUB_NONVAR_SIZE + + ((USB_MAXCHILDREN + 1 + 7) / 8), + .bDescriptorType = USB_DT_HUB, + .bNbrPorts = 1, + .wHubCharacteristics = 0, + .bPwrOn2PwrGood = 0, + .bHubContrCurrent = 0, + .u.hs.DeviceRemovable = {0xff}, + .u.hs.PortPwrCtrlMask = {} + }, + .device = { + .bLength = USB_DT_DEVICE_SIZE, + .bDescriptorType = USB_DT_DEVICE, + .bcdUSB = cpu_to_le16(2), /* v2.0 */ + .bDeviceClass = USB_CLASS_HUB, + .bDeviceSubClass = 0, + .bDeviceProtocol = USB_HUB_PR_HS_NO_TT, + .bMaxPacketSize0 = 64, + .idVendor = 0x0000, + .idProduct = 0x0000, + .bcdDevice = 0x0000, + .iManufacturer = 1, + .iProduct = 2, + .iSerialNumber = 0, + .bNumConfigurations = 1 + }, + .config = { + .bLength = USB_DT_CONFIG_SIZE, + .bDescriptorType = USB_DT_CONFIG, + .wTotalLength = cpu_to_le16( + USB_DT_CONFIG_SIZE + + USB_DT_INTERFACE_SIZE + + USB_DT_ENDPOINT_SIZE), + .bNumInterfaces = 1, + .bConfigurationValue = 1, + .iConfiguration = 0, + .bmAttributes = USB_CONFIG_ATT_SELFPOWER, + .bMaxPower = 0 + }, + .interface = { + .bLength = USB_DT_INTERFACE_SIZE, + .bDescriptorType = USB_DT_INTERFACE, + .bInterfaceNumber = 0, + .bAlternateSetting = 0, + .bNumEndpoints = 1, + .bInterfaceClass = USB_CLASS_HUB, + .bInterfaceSubClass = 0, + .bInterfaceProtocol = 0, + .iInterface = 0 + }, + .endpoint = { + .bLength = USB_DT_ENDPOINT_SIZE, + .bDescriptorType = USB_DT_ENDPOINT, + .bEndpointAddress = 1 | USB_DIR_IN, /* 0x81 */ + .bmAttributes = USB_ENDPOINT_XFER_INT, + .wMaxPacketSize = cpu_to_le16( + (USB_MAXCHILDREN + 1 + 7) / 8), + .bInterval = 255 + }, +}; + +static int dwc2_get_port_status(struct dwc2 *dwc2, struct usb_device *dev, + void *buf, int len) +{ + struct usb_port_status *portsts; + uint32_t hprt0; + uint32_t status = 0; + uint32_t change = 0; + int speed; + + if (!buf || len < sizeof(*portsts)) + return -1; + + hprt0 = dwc2_readl(dwc2, HPRT0); + + if (hprt0 & HPRT0_CONNSTS) + status |= USB_PORT_STAT_CONNECTION; + if (hprt0 & HPRT0_ENA) + status |= USB_PORT_STAT_ENABLE; + if (hprt0 & HPRT0_SUSP) + status |= USB_PORT_STAT_SUSPEND; + if (hprt0 & HPRT0_OVRCURRACT) + status |= USB_PORT_STAT_OVERCURRENT; + if (hprt0 & HPRT0_RST) + status |= USB_PORT_STAT_RESET; + if (hprt0 & HPRT0_PWR) + status |= USB_PORT_STAT_POWER; + + speed = (hprt0 & HPRT0_SPD_MASK) >> HPRT0_SPD_SHIFT; + if (speed == HPRT0_SPD_HIGH_SPEED) + status |= USB_PORT_STAT_HIGH_SPEED; + else if (speed == HPRT0_SPD_LOW_SPEED) + status |= USB_PORT_STAT_LOW_SPEED; + + if (hprt0 & HPRT0_ENACHG) + change |= USB_PORT_STAT_C_ENABLE; + if (hprt0 & HPRT0_CONNDET) + change |= USB_PORT_STAT_C_CONNECTION; + if (hprt0 & HPRT0_OVRCURRCHG) + change |= USB_PORT_STAT_C_OVERCURRENT; + + portsts = buf; + portsts->wPortStatus = cpu_to_le16(status); + portsts->wPortChange = cpu_to_le16(change); + + dev->act_len = sizeof(*portsts); + dev->status = 0; + + return 0; +} + +static int dwc2_get_hub_status(struct dwc2 *dwc2, struct usb_device *dev, + void *buf, int len) +{ + if (!buf || len < 4) + return -1; + + *(uint32_t *)buf = 0; + dev->act_len = 4; + dev->status = 0; + + return 0; +} + +static int dwc2_get_hub_descriptor(struct dwc2 *dwc2, struct usb_device *dev, + void *buf, int len) +{ + if (!buf) + return -1; + + dev->act_len = min_t(int, len, descriptor.hub.bLength); + dev->status = 0; + memcpy(buf, &descriptor.hub, dev->act_len); + + return 0; +} + +static void strle16(__le16 *dest, char *src, size_t n) +{ + unsigned int i; + + for (i = 0; i < n && *src != '\0'; i++, src++) + dest[i] = cpu_to_le16(*src); +} + +static int dwc2_get_string_descriptor(struct dwc2 *dwc2, struct usb_device *dev, + void *buf, int len, int index) +{ + char *src, *str = buf; + __le16 *le16 = (__le16 *)(str + 2); + int size; + + if (!buf || len < 2) + return -1; + + switch (index) { + case 0: /* Language */ + src = "\x09\x04"; + size = strlen(src) + 2; + len = min_t(int, len, size); + + str[0] = size; + str[1] = 0x03; + memcpy(str + 2, src, len - 2); + break; + case 1: /* Vendor */ + src = "u-boot"; + size = 2 * strlen(src) + 2; + len = min_t(int, len, size); + + str[0] = size; + str[1] = 0x03; + strle16(le16, src, (len - 2) / 2); + break; + case 2: /* Product */ + src = "DWC2 root hub"; + size = 2 * strlen(src) + 2; + len = min_t(int, len, size); + + str[0] = size; + str[1] = 0x03; + strle16(le16, src, (len - 2) / 2); + break; + default: + dwc2_err(dwc2, "roothub: unknown string descriptor: 0x%x\n", + index); + return -1; + } + + dev->act_len = len; + dev->status = 0; + + return 0; +} + +static int dwc2_get_descriptor(struct dwc2 *dwc2, struct usb_device *dev, + void *buf, int len, int value) +{ + int index = value >> 8; + + if (!buf || len < 0) + return -1; + + switch (index) { + case USB_DT_DEVICE: + len = min(len, (int)descriptor.device.bLength); + memcpy(buf, &descriptor.device, len); + break; + case USB_DT_CONFIG: + len = min(len, (int)descriptor.config.wTotalLength); + memcpy(buf, &descriptor.config, len); + break; + case USB_DT_STRING: + value &= 0xff; + return dwc2_get_string_descriptor(dwc2, dev, buf, len, value); + default: + dwc2_err(dwc2, "roothub: unknown descriptor: 0x%x\n", index); + return -1; + } + + dev->act_len = len; + dev->status = 0; + + return 0; +} + +static int dwc2_set_port_feature(struct dwc2 *dwc2, struct usb_device *dev, + int feature) +{ + uint32_t hprt0; + + hprt0 = dwc2_readl(dwc2, HPRT0); + hprt0 &= ~(HPRT0_ENA | HPRT0_CONNDET | HPRT0_ENACHG | HPRT0_OVRCURRCHG); + + switch (feature) { + case USB_PORT_FEAT_SUSPEND: + break; + case USB_PORT_FEAT_RESET: + hprt0 |= HPRT0_RST; + dwc2_writel(dwc2, hprt0, HPRT0); + + mdelay(60); + + hprt0 = dwc2_readl(dwc2, HPRT0); + hprt0 &= ~HPRT0_RST; + dwc2_writel(dwc2, hprt0, HPRT0); + break; + case USB_PORT_FEAT_POWER: + break; + case USB_PORT_FEAT_ENABLE: + /* Set by the core after a reset */ + break; + default: + dwc2_dbg(dwc2, "roothub: unsupported set port feature 0x%x\n", + feature); + return -1; + } + + dev->act_len = 0; + dev->status = 0; + + return 0; +} + +static int dwc2_clear_port_feature(struct dwc2 *dwc2, struct usb_device *dev, + int feature) +{ + uint32_t hprt0; + + hprt0 = dwc2_readl(dwc2, HPRT0); + hprt0 &= ~(HPRT0_ENA | HPRT0_CONNDET | HPRT0_ENACHG | HPRT0_OVRCURRCHG); + + switch (feature) { + case USB_PORT_FEAT_ENABLE: + hprt0 |= HPRT0_ENA; + break; + case USB_PORT_FEAT_SUSPEND: + break; + case USB_PORT_FEAT_POWER: + break; + case USB_PORT_FEAT_C_CONNECTION: + hprt0 |= HPRT0_CONNDET; + break; + case USB_PORT_FEAT_C_ENABLE: + hprt0 |= HPRT0_ENACHG; + break; + case USB_PORT_FEAT_C_OVER_CURRENT: + hprt0 |= HPRT0_OVRCURRCHG; + break; + default: + dwc2_dbg(dwc2, "roothub: unsupported clear port feature 0x%x\n", + feature); + return -1; + } + + dwc2_writel(dwc2, hprt0, HPRT0); + + dev->act_len = 0; + dev->status = 0; + + return 0; +} + +static int dwc2_set_address(struct dwc2 *dwc2, struct usb_device *dev, int addr) +{ + dwc2_dbg(dwc2, "roothub: set address to %d\n", addr); + dwc2->root_hub_devnum = addr; + + dev->act_len = 0; + dev->status = 0; + + return 0; +} + +int dwc2_submit_roothub(struct dwc2 *dwc2, struct usb_device *dev, + unsigned long pipe, void *buf, int len, + struct devrequest *setup) +{ + unsigned char reqtype = setup->requesttype; + unsigned char request = setup->request; + unsigned short value = le16_to_cpu(setup->value); + unsigned short size = le16_to_cpu(setup->length); + int minlen = min_t(int, len, size); + + if (usb_pipeint(pipe)) { + dwc2_err(dwc2, "roothub: submit IRQ NOT implemented\n"); + return 0; + } + + dev->act_len = 0; + dev->status = USB_ST_STALLED; + +#define REQ(l, u) ((l) | ((u) << 8)) + + switch (REQ(request, reqtype)) { + case REQ(USB_REQ_GET_DESCRIPTOR, USB_DIR_IN | USB_RT_HUB): + return dwc2_get_hub_descriptor(dwc2, dev, buf, minlen); + + case REQ(USB_REQ_GET_DESCRIPTOR, USB_DIR_IN): + return dwc2_get_descriptor(dwc2, dev, buf, minlen, value); + + case REQ(USB_REQ_GET_STATUS, USB_DIR_IN | USB_RT_HUB): + return dwc2_get_hub_status(dwc2, dev, buf, len); + + case REQ(USB_REQ_GET_STATUS, USB_DIR_IN | USB_RT_PORT): + return dwc2_get_port_status(dwc2, dev, buf, len); + + case REQ(USB_REQ_SET_FEATURE, USB_DIR_OUT | USB_RT_PORT): + return dwc2_set_port_feature(dwc2, dev, value); + + case REQ(USB_REQ_CLEAR_FEATURE, USB_DIR_OUT | USB_RT_PORT): + return dwc2_clear_port_feature(dwc2, dev, value); + + case REQ(USB_REQ_SET_ADDRESS, USB_DIR_OUT): + return dwc2_set_address(dwc2, dev, value); + + case REQ(USB_REQ_SET_CONFIGURATION, USB_DIR_OUT): + dev->act_len = 0; + dev->status = 0; + return 0; + + case REQ(USB_REQ_GET_CONFIGURATION, USB_DIR_IN): + *(char *)buf = 1; + dev->act_len = 1; + dev->status = 0; + return 0; + } + + dwc2_err(dwc2, "roothub: unsupported request 0x%x requesttype 0x%x\n", + request, reqtype); + + return 0; +} diff --git a/drivers/usb/host/Kconfig b/drivers/usb/host/Kconfig index 51697e109e..891523c4d2 100644 --- a/drivers/usb/host/Kconfig +++ b/drivers/usb/host/Kconfig @@ -21,9 +21,6 @@ config USB_OHCI_AT91 depends on ARCH_AT91 bool "AT91 OHCI driver" -config USB_DWC2_HOST - bool "DWC2 Host driver" - config USB_XHCI bool "xHCI driver" depends on HAS_DMA diff --git a/drivers/usb/host/Makefile b/drivers/usb/host/Makefile index d417410e90..e7a6cf213e 100644 --- a/drivers/usb/host/Makefile +++ b/drivers/usb/host/Makefile @@ -3,5 +3,4 @@ obj-$(CONFIG_USB_EHCI_OMAP) += ehci-omap.o obj-$(CONFIG_USB_EHCI_ATMEL) += ehci-atmel.o obj-$(CONFIG_USB_OHCI) += ohci-hcd.o obj-$(CONFIG_USB_OHCI_AT91) += ohci-at91.o -obj-$(CONFIG_USB_DWC2_HOST) += dwc2.o obj-$(CONFIG_USB_XHCI) += xhci.o xhci-mem.o xhci-ring.o diff --git a/drivers/usb/host/dwc2.c b/drivers/usb/host/dwc2.c deleted file mode 100644 index 1df1149686..0000000000 --- a/drivers/usb/host/dwc2.c +++ /dev/null @@ -1,1132 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0+ -/* - * Copyright (C) 2012 Oleksandr Tymoshenko <gonzo@freebsd.org> - * Copyright (C) 2014 Marek Vasut <marex@denx.de> - */ - -#include <common.h> -#include <usb/usb.h> -#include <usb/usbroothubdes.h> -#include <malloc.h> -#include <init.h> -#include <io.h> -#include <of.h> -#include <linux/iopoll.h> -#include <dma.h> - -#include "dwc2.h" - -/* Use only HC channel 0. */ -#define DWC2_HC_CHANNEL 0 - -#define DWC2_STATUS_BUF_SIZE 64 -#define DWC2_DATA_BUF_SIZE (16 * 1024) - -#define MAX_DEVICE 16 -#define MAX_ENDPOINT 16 - -struct dwc2_priv { - struct device_d *dev; - struct usb_host host; - uint8_t *dmabuf; - - u8 in_data_toggle[MAX_DEVICE][MAX_ENDPOINT]; - u8 out_data_toggle[MAX_DEVICE][MAX_ENDPOINT]; - struct dwc2_core_regs *regs; - int root_hub_devnum; - bool ext_vbus; - /* - * The hnp/srp capability must be disabled if the platform - * does't support hnp/srp. Otherwise the force mode can't work. - */ - bool hnp_srp_disable; - bool oc_disable; -}; - -/* - * Initializes the FSLSPClkSel field of the HCFG register - * depending on the PHY type. - */ -static void init_fslspclksel(struct dwc2_priv *priv) -{ - struct dwc2_core_regs *regs = priv->regs; - uint32_t phyclk; - - phyclk = DWC2_HCFG_FSLSPCLKSEL_48_MHZ; /* Full speed PHY */ - - clrsetbits_le32(®s->host_regs.hcfg, - DWC2_HCFG_FSLSPCLKSEL_MASK, - phyclk << DWC2_HCFG_FSLSPCLKSEL_OFFSET); -} - -/* - * 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 dwc2_priv *priv, const int num) -{ - struct dwc2_core_regs *regs = priv->regs; - struct device_d *dev = priv->dev; - int ret; - uint32_t val; - - writel(DWC2_GRSTCTL_TXFFLSH | (num << DWC2_GRSTCTL_TXFNUM_OFFSET), - ®s->grstctl); - ret = readl_poll_timeout(®s->grstctl, val, !(val & DWC2_GRSTCTL_TXFFLSH), - 1000000); - if (ret) - dev_err(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 dwc2_priv *priv) -{ - struct dwc2_core_regs *regs = priv->regs; - struct device_d *dev = priv->dev; - int ret; - uint32_t val; - - writel(DWC2_GRSTCTL_RXFFLSH, ®s->grstctl); - ret = readl_poll_timeout(®s->grstctl, val, !(val & DWC2_GRSTCTL_RXFFLSH), - 1000000); - if (ret) - dev_err(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 dwc2_priv *priv) -{ - struct dwc2_core_regs *regs = priv->regs; - struct device_d *dev = priv->dev; - uint32_t val; - int ret; - - /* Wait for AHB master IDLE state. */ - ret = readl_poll_timeout(®s->grstctl, val, val & DWC2_GRSTCTL_AHBIDLE, - 1000000); - if (ret) - dev_err(dev, "%s: Timeout!\n", __func__); - - /* Core Soft Reset */ - writel(DWC2_GRSTCTL_CSFTRST, ®s->grstctl); - ret = readl_poll_timeout(®s->grstctl, val, !(val & DWC2_GRSTCTL_CSFTRST), - 1000000); - if (ret) - dev_err(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); -} - -/* - * This function initializes the DWC_otg controller registers for - * host mode. - * - * This function flushes the Tx and Rx FIFOs and it flushes any entries in the - * request queues. Host channels are reset to ensure that they are ready for - * performing transfers. - * - * @param dev USB Device (NULL if driver model is not being used) - * @param regs Programming view of DWC_otg controller - * - */ -static void dwc_otg_core_host_init(struct dwc2_priv *priv) -{ - struct dwc2_core_regs *regs = priv->regs; - struct device_d *dev = priv->dev; - uint32_t nptxfifosize = 0; - uint32_t ptxfifosize = 0; - uint32_t hprt0 = 0; - uint32_t val; - int i, ret, num_channels; - - /* Restart the Phy Clock */ - writel(0, ®s->pcgcctl); - - /* Initialize Host Configuration Register */ - init_fslspclksel(priv); - - /* Configure data FIFO sizes */ - if (readl(®s->ghwcfg2) & DWC2_HWCFG2_DYNAMIC_FIFO) { - /* Rx FIFO */ - writel(CONFIG_DWC2_HOST_RX_FIFO_SIZE, ®s->grxfsiz); - - /* Non-periodic Tx FIFO */ - nptxfifosize |= CONFIG_DWC2_HOST_NPERIO_TX_FIFO_SIZE << - DWC2_FIFOSIZE_DEPTH_OFFSET; - nptxfifosize |= CONFIG_DWC2_HOST_RX_FIFO_SIZE << - DWC2_FIFOSIZE_STARTADDR_OFFSET; - writel(nptxfifosize, ®s->gnptxfsiz); - - /* Periodic Tx FIFO */ - ptxfifosize |= CONFIG_DWC2_HOST_PERIO_TX_FIFO_SIZE << - DWC2_FIFOSIZE_DEPTH_OFFSET; - ptxfifosize |= (CONFIG_DWC2_HOST_RX_FIFO_SIZE + - CONFIG_DWC2_HOST_NPERIO_TX_FIFO_SIZE) << - DWC2_FIFOSIZE_STARTADDR_OFFSET; - writel(ptxfifosize, ®s->hptxfsiz); - } - - /* Clear Host Set HNP Enable in the OTG Control Register */ - clrbits_le32(®s->gotgctl, DWC2_GOTGCTL_HSTSETHNPEN); - - /* Make sure the FIFOs are flushed. */ - dwc_otg_flush_tx_fifo(priv, 0x10); /* All Tx FIFOs */ - dwc_otg_flush_rx_fifo(priv); - - /* Flush out any leftover queued requests. */ - num_channels = readl(®s->ghwcfg2); - num_channels &= DWC2_HWCFG2_NUM_HOST_CHAN_MASK; - num_channels >>= DWC2_HWCFG2_NUM_HOST_CHAN_OFFSET; - num_channels += 1; - - for (i = 0; i < num_channels; i++) - clrsetbits_le32(®s->hc_regs[i].hcchar, - DWC2_HCCHAR_CHEN | DWC2_HCCHAR_EPDIR, - DWC2_HCCHAR_CHDIS); - - /* Halt all channels to put them into a known state. */ - for (i = 0; i < num_channels; i++) { - clrsetbits_le32(®s->hc_regs[i].hcchar, - DWC2_HCCHAR_EPDIR, - DWC2_HCCHAR_CHEN | DWC2_HCCHAR_CHDIS); - ret = readl_poll_timeout(®s->hc_regs[i].hcchar, val, - !(val & DWC2_HCCHAR_CHEN), - 1000000); - if (ret) - dev_err(dev, "%s: Timeout!\n", __func__); - } - - /* Turn on the vbus power. */ - if (readl(®s->gintsts) & DWC2_GINTSTS_CURMODE_HOST) { - hprt0 = readl(®s->hprt0); - hprt0 &= ~(DWC2_HPRT0_PRTENA | DWC2_HPRT0_PRTCONNDET); - hprt0 &= ~(DWC2_HPRT0_PRTENCHNG | DWC2_HPRT0_PRTOVRCURRCHNG); - if (!(hprt0 & DWC2_HPRT0_PRTPWR)) { - hprt0 |= DWC2_HPRT0_PRTPWR; - writel(hprt0, ®s->hprt0); - } - } -} - -/* - * This function initializes the DWC_otg controller registers and - * prepares the core for device mode or host mode operation. - * - * @param regs Programming view of the DWC_otg controller - */ -static void dwc_otg_core_init(struct dwc2_priv *priv) -{ - struct dwc2_core_regs *regs = priv->regs; - uint32_t ahbcfg = 0; - uint32_t usbcfg = 0; - uint8_t brst_sz = 32; - - /* Common Initialization */ - usbcfg = readl(®s->gusbcfg); - - /* Program the ULPI External VBUS bit if needed */ - if (priv->ext_vbus) { - usbcfg |= DWC2_GUSBCFG_ULPI_EXT_VBUS_DRV; - if (!priv->oc_disable) { - usbcfg |= DWC2_GUSBCFG_ULPI_INT_VBUS_INDICATOR | - DWC2_GUSBCFG_INDICATOR_PASSTHROUGH; - } - } else { - usbcfg &= ~DWC2_GUSBCFG_ULPI_EXT_VBUS_DRV; - } - - /* Set external TS Dline pulsing */ - usbcfg &= ~DWC2_GUSBCFG_TERM_SEL_DL_PULSE; - writel(usbcfg, ®s->gusbcfg); - - /* Reset the Controller */ - dwc_otg_core_reset(priv); - - /* High speed PHY. */ - - /* - * HS PHY parameters. These parameters are preserved during - * soft reset so only program the first time. Do a soft reset - * immediately after setting phyif. - */ - usbcfg &= ~(DWC2_GUSBCFG_ULPI_UTMI_SEL | DWC2_GUSBCFG_PHYIF); - usbcfg |= CONFIG_DWC2_PHY_TYPE << DWC2_GUSBCFG_ULPI_UTMI_SEL_OFFSET; - - if (usbcfg & DWC2_GUSBCFG_ULPI_UTMI_SEL) /* ULPI interface */ - usbcfg &= ~DWC2_GUSBCFG_DDRSEL; - - writel(usbcfg, ®s->gusbcfg); - - /* Reset after setting the PHY parameters */ - dwc_otg_core_reset(priv); - - usbcfg = readl(®s->gusbcfg); - usbcfg &= ~(DWC2_GUSBCFG_ULPI_FSLS | DWC2_GUSBCFG_ULPI_CLK_SUS_M); - - if (priv->hnp_srp_disable) - usbcfg |= DWC2_GUSBCFG_FORCEHOSTMODE; - - writel(usbcfg, ®s->gusbcfg); - - /* Program the GAHBCFG Register. */ - switch (readl(®s->ghwcfg2) & DWC2_HWCFG2_ARCHITECTURE_MASK) { - case DWC2_HWCFG2_ARCHITECTURE_SLAVE_ONLY: - break; - case DWC2_HWCFG2_ARCHITECTURE_EXT_DMA: - while (brst_sz > 1) { - ahbcfg |= ahbcfg + (1 << DWC2_GAHBCFG_HBURSTLEN_OFFSET); - ahbcfg &= DWC2_GAHBCFG_HBURSTLEN_MASK; - brst_sz >>= 1; - } - - ahbcfg |= DWC2_GAHBCFG_DMAENABLE; - break; - - case DWC2_HWCFG2_ARCHITECTURE_INT_DMA: - ahbcfg |= DWC2_GAHBCFG_HBURSTLEN_INCR4; - ahbcfg |= DWC2_GAHBCFG_DMAENABLE; - break; - } - - writel(ahbcfg, ®s->gahbcfg); - - /* Program the capabilities in GUSBCFG Register */ - usbcfg = 0; - - if (!priv->hnp_srp_disable) - usbcfg |= DWC2_GUSBCFG_HNPCAP | DWC2_GUSBCFG_SRPCAP; - - setbits_le32(®s->gusbcfg, usbcfg); -} - -/* - * Prepares a host channel for transferring packets to/from a specific - * endpoint. The HCCHARn register is set up with the characteristics specified - * in _hc. Host channel interrupts that may need to be serviced while this - * transfer is in progress are enabled. - * - * @param regs Programming view of DWC_otg controller - * @param hc Information needed to initialize the host channel - */ -static void dwc_otg_hc_init(struct dwc2_core_regs *regs, uint8_t hc_num, - struct usb_device *dev, uint8_t dev_addr, uint8_t ep_num, - uint8_t ep_is_in, uint8_t ep_type, uint16_t max_packet) -{ - struct dwc2_hc_regs *hc_regs = ®s->hc_regs[hc_num]; - uint32_t hcchar = (dev_addr << DWC2_HCCHAR_DEVADDR_OFFSET) | - (ep_num << DWC2_HCCHAR_EPNUM_OFFSET) | - (ep_is_in << DWC2_HCCHAR_EPDIR_OFFSET) | - (ep_type << DWC2_HCCHAR_EPTYPE_OFFSET) | - (max_packet << DWC2_HCCHAR_MPS_OFFSET); - - if (dev->speed == USB_SPEED_LOW) - hcchar |= DWC2_HCCHAR_LSPDDEV; - - /* - * Program the HCCHARn register with the endpoint characteristics - * for the current transfer. - */ - writel(hcchar, &hc_regs->hcchar); - - /* Program the HCSPLIT register, default to no SPLIT */ - writel(0, &hc_regs->hcsplt); -} - -static void dwc_otg_hc_init_split(struct dwc2_hc_regs *hc_regs, - uint8_t hub_devnum, uint8_t hub_port) -{ - uint32_t hcsplt = 0; - - hcsplt = DWC2_HCSPLT_SPLTENA; - hcsplt |= hub_devnum << DWC2_HCSPLT_HUBADDR_OFFSET; - hcsplt |= hub_port << DWC2_HCSPLT_PRTADDR_OFFSET; - - /* Program the HCSPLIT register for SPLITs */ - writel(hcsplt, &hc_regs->hcsplt); -} - -/* - * DWC2 to USB API interface - */ -/* Direction: In ; Request: Status */ -static int dwc_otg_submit_rh_msg_in_status(struct dwc2_core_regs *regs, - struct usb_device *dev, void *buffer, - int txlen, struct devrequest *cmd) -{ - uint32_t hprt0 = 0; - uint32_t port_status = 0; - uint32_t port_change = 0; - int len = 0; - int stat = 0; - - switch (cmd->requesttype & ~USB_DIR_IN) { - case 0: - *(uint16_t *)buffer = cpu_to_le16(1); - len = 2; - break; - case USB_RECIP_INTERFACE: - case USB_RECIP_ENDPOINT: - *(uint16_t *)buffer = cpu_to_le16(0); - len = 2; - break; - case USB_TYPE_CLASS: - *(uint32_t *)buffer = cpu_to_le32(0); - len = 4; - break; - case USB_RECIP_OTHER | USB_TYPE_CLASS: - hprt0 = readl(®s->hprt0); - if (hprt0 & DWC2_HPRT0_PRTCONNSTS) - port_status |= USB_PORT_STAT_CONNECTION; - if (hprt0 & DWC2_HPRT0_PRTENA) - port_status |= USB_PORT_STAT_ENABLE; - if (hprt0 & DWC2_HPRT0_PRTSUSP) - port_status |= USB_PORT_STAT_SUSPEND; - if (hprt0 & DWC2_HPRT0_PRTOVRCURRACT) - port_status |= USB_PORT_STAT_OVERCURRENT; - if (hprt0 & DWC2_HPRT0_PRTRST) - port_status |= USB_PORT_STAT_RESET; - if (hprt0 & DWC2_HPRT0_PRTPWR) - port_status |= USB_PORT_STAT_POWER; - - if ((hprt0 & DWC2_HPRT0_PRTSPD_MASK) == DWC2_HPRT0_PRTSPD_LOW) - port_status |= USB_PORT_STAT_LOW_SPEED; - else if ((hprt0 & DWC2_HPRT0_PRTSPD_MASK) == - DWC2_HPRT0_PRTSPD_HIGH) - port_status |= USB_PORT_STAT_HIGH_SPEED; - - if (hprt0 & DWC2_HPRT0_PRTENCHNG) - port_change |= USB_PORT_STAT_C_ENABLE; - if (hprt0 & DWC2_HPRT0_PRTCONNDET) - port_change |= USB_PORT_STAT_C_CONNECTION; - if (hprt0 & DWC2_HPRT0_PRTOVRCURRCHNG) - port_change |= USB_PORT_STAT_C_OVERCURRENT; - - *(uint32_t *)buffer = cpu_to_le32(port_status | - (port_change << 16)); - len = 4; - break; - default: - pr_err("%s: unsupported root hub command\n", __func__); - stat = USB_ST_STALLED; - } - - dev->act_len = min(len, txlen); - dev->status = stat; - - return stat; -} - -/* Direction: In ; Request: Descriptor */ -static int dwc_otg_submit_rh_msg_in_descriptor(struct usb_device *dev, - void *buffer, int txlen, - struct devrequest *cmd) -{ - unsigned char data[32]; - uint32_t dsc; - int len = 0; - int stat = 0; - uint16_t wValue = cpu_to_le16(cmd->value); - uint16_t wLength = cpu_to_le16(cmd->length); - - switch (cmd->requesttype & ~USB_DIR_IN) { - case 0: - switch (wValue & 0xff00) { - case 0x0100: /* device descriptor */ - len = min3(txlen, (int)sizeof(root_hub_dev_des), (int)wLength); - memcpy(buffer, root_hub_dev_des, len); - break; - case 0x0200: /* configuration descriptor */ - len = min3(txlen, (int)sizeof(root_hub_config_des), (int)wLength); - memcpy(buffer, root_hub_config_des, len); - break; - case 0x0300: /* string descriptors */ - switch (wValue & 0xff) { - case 0x00: - len = min3(txlen, (int)sizeof(root_hub_str_index0), - (int)wLength); - memcpy(buffer, root_hub_str_index0, len); - break; - case 0x01: - len = min3(txlen, (int)sizeof(root_hub_str_index1), - (int)wLength); - memcpy(buffer, root_hub_str_index1, len); - break; - } - break; - default: - stat = USB_ST_STALLED; - } - break; - - case USB_TYPE_CLASS: - /* Root port config, set 1 port and nothing else. */ - dsc = 0x00000001; - - data[0] = 9; /* min length; */ - data[1] = 0x29; - data[2] = dsc & RH_A_NDP; - data[3] = 0; - if (dsc & RH_A_PSM) - data[3] |= 0x1; - if (dsc & RH_A_NOCP) - data[3] |= 0x10; - else if (dsc & RH_A_OCPM) - data[3] |= 0x8; - - /* corresponds to data[4-7] */ - data[5] = (dsc & RH_A_POTPGT) >> 24; - data[7] = dsc & RH_B_DR; - if (data[2] < 7) { - data[8] = 0xff; - } else { - data[0] += 2; - data[8] = (dsc & RH_B_DR) >> 8; - data[9] = 0xff; - data[10] = data[9]; - } - - len = min3(txlen, (int)data[0], (int)wLength); - memcpy(buffer, data, len); - break; - default: - pr_err("%s: unsupported root hub command\n", __func__); - stat = USB_ST_STALLED; - } - - dev->act_len = min(len, txlen); - dev->status = stat; - - return stat; -} - -/* Direction: In ; Request: Configuration */ -static int dwc_otg_submit_rh_msg_in_configuration(struct usb_device *dev, - void *buffer, int txlen, - struct devrequest *cmd) -{ - int len = 0; - int stat = 0; - - switch (cmd->requesttype & ~USB_DIR_IN) { - case 0: - *(uint8_t *)buffer = 0x01; - len = 1; - break; - default: - pr_err("%s: unsupported root hub command\n", __func__); - stat = USB_ST_STALLED; - } - - dev->act_len = min(len, txlen); - dev->status = stat; - - return stat; -} - -/* Direction: In */ -static int dwc_otg_submit_rh_msg_in(struct dwc2_priv *priv, - struct usb_device *dev, void *buffer, - int txlen, struct devrequest *cmd) -{ - switch (cmd->request) { - case USB_REQ_GET_STATUS: - return dwc_otg_submit_rh_msg_in_status(priv->regs, dev, buffer, - txlen, cmd); - case USB_REQ_GET_DESCRIPTOR: - return dwc_otg_submit_rh_msg_in_descriptor(dev, buffer, - txlen, cmd); - case USB_REQ_GET_CONFIGURATION: - return dwc_otg_submit_rh_msg_in_configuration(dev, buffer, - txlen, cmd); - default: - pr_err("%s: unsupported root hub command\n", __func__); - return USB_ST_STALLED; - } -} - -/* Direction: Out */ -static int dwc_otg_submit_rh_msg_out(struct dwc2_priv *priv, - struct usb_device *dev, - void *buffer, int txlen, - struct devrequest *cmd) -{ - struct dwc2_core_regs *regs = priv->regs; - int len = 0; - int stat = 0; - uint16_t bmrtype_breq = cmd->requesttype | (cmd->request << 8); - uint16_t wValue = cpu_to_le16(cmd->value); - - switch (bmrtype_breq & ~USB_DIR_IN) { - case (USB_REQ_CLEAR_FEATURE << 8) | USB_RECIP_ENDPOINT: - case (USB_REQ_CLEAR_FEATURE << 8) | USB_TYPE_CLASS: - break; - - case (USB_REQ_CLEAR_FEATURE << 8) | USB_RECIP_OTHER | USB_TYPE_CLASS: - switch (wValue) { - case USB_PORT_FEAT_C_CONNECTION: - setbits_le32(®s->hprt0, DWC2_HPRT0_PRTCONNDET); - break; - } - break; - - case (USB_REQ_SET_FEATURE << 8) | USB_RECIP_OTHER | USB_TYPE_CLASS: - switch (wValue) { - case USB_PORT_FEAT_SUSPEND: - break; - - case USB_PORT_FEAT_RESET: - clrsetbits_le32(®s->hprt0, DWC2_HPRT0_PRTENA | - DWC2_HPRT0_PRTCONNDET | - DWC2_HPRT0_PRTENCHNG | - DWC2_HPRT0_PRTOVRCURRCHNG, - DWC2_HPRT0_PRTRST); - mdelay(50); - clrbits_le32(®s->hprt0, DWC2_HPRT0_PRTRST); - break; - - case USB_PORT_FEAT_POWER: - clrsetbits_le32(®s->hprt0, DWC2_HPRT0_PRTENA | - DWC2_HPRT0_PRTCONNDET | - DWC2_HPRT0_PRTENCHNG | - DWC2_HPRT0_PRTOVRCURRCHNG, - DWC2_HPRT0_PRTRST); - break; - - case USB_PORT_FEAT_ENABLE: - break; - } - break; - case (USB_REQ_SET_ADDRESS << 8): - priv->root_hub_devnum = wValue; - break; - case (USB_REQ_SET_CONFIGURATION << 8): - break; - default: - pr_err("%s: unsupported root hub command\n", __func__); - stat = USB_ST_STALLED; - } - - len = min(len, txlen); - - dev->act_len = len; - dev->status = stat; - - return stat; -} - -static int dwc_otg_submit_rh_msg(struct dwc2_priv *priv, struct usb_device *dev, - unsigned long pipe, void *buffer, int txlen, - struct devrequest *cmd) -{ - int stat = 0; - - if (usb_pipeint(pipe)) { - pr_err("Root-Hub submit IRQ: NOT implemented\n"); - return 0; - } - - if (cmd->requesttype & USB_DIR_IN) - stat = dwc_otg_submit_rh_msg_in(priv, dev, buffer, txlen, cmd); - else - stat = dwc_otg_submit_rh_msg_out(priv, dev, buffer, txlen, cmd); - - mdelay(1); - - return stat; -} - -static int wait_for_chhltd(struct dwc2_priv *priv, uint32_t *sub, - u8 *toggle, int timeout_ms) -{ - struct dwc2_core_regs *regs = priv->regs; - struct dwc2_hc_regs *hc_regs = ®s->hc_regs[DWC2_HC_CHANNEL]; - struct device_d *dev = priv->dev; - int ret; - uint32_t hcint, hctsiz; - uint32_t val; - int timeout_us = timeout_ms * 1000; - - ret = readl_poll_timeout(&hc_regs->hcint, val, - val & DWC2_HCINT_CHHLTD, timeout_us); - if (ret) { - clrsetbits_le32(&hc_regs->hcchar, 0, DWC2_HCCHAR_CHDIS); - readl_poll_timeout(&hc_regs->hcint, val, - val & DWC2_HCINT_CHHLTD, 10000); - - return ret; - } - - hcint = readl(&hc_regs->hcint); - hctsiz = readl(&hc_regs->hctsiz); - *sub = (hctsiz & DWC2_HCTSIZ_XFERSIZE_MASK) >> - DWC2_HCTSIZ_XFERSIZE_OFFSET; - *toggle = (hctsiz & DWC2_HCTSIZ_PID_MASK) >> DWC2_HCTSIZ_PID_OFFSET; - - dev_dbg(dev, "%s: HCINT=%08x sub=%u toggle=%d\n", __func__, hcint, *sub, - *toggle); - - if (hcint & DWC2_HCINT_XFERCOMP) - return 0; - - if (hcint & (DWC2_HCINT_NAK | DWC2_HCINT_FRMOVRUN)) - return -EAGAIN; - - dev_dbg(dev, "%s: Error (HCINT=%08x)\n", __func__, hcint); - - return -EINVAL; -} - -static int dwc2_eptype[] = { - DWC2_HCCHAR_EPTYPE_ISOC, - DWC2_HCCHAR_EPTYPE_INTR, - DWC2_HCCHAR_EPTYPE_CONTROL, - DWC2_HCCHAR_EPTYPE_BULK, -}; - -static int transfer_chunk(struct dwc2_priv *priv, u8 *pid, int in, void *buffer, - int num_packets, int xfer_len, int *actual_len, - int odd_frame, int timeout_ms) -{ - struct dwc2_core_regs *regs = priv->regs; - struct dwc2_hc_regs *hc_regs = ®s->hc_regs[DWC2_HC_CHANNEL]; - int ret = 0; - uint32_t sub = 0; - enum dma_data_direction dir; - dma_addr_t dma = 0; - - dev_dbg(priv->dev, "%s: chunk: pid %d xfer_len %u pkts %u\n", - __func__, *pid, xfer_len, num_packets); - - writel((xfer_len << DWC2_HCTSIZ_XFERSIZE_OFFSET) | - (num_packets << DWC2_HCTSIZ_PKTCNT_OFFSET) | - (*pid << DWC2_HCTSIZ_PID_OFFSET), - &hc_regs->hctsiz); - - if (xfer_len) { - if (in) { - dir = DMA_FROM_DEVICE; - } else { - memcpy(priv->dmabuf, buffer, xfer_len); - dir = DMA_TO_DEVICE; - } - dma = dma_map_single(priv->dev, priv->dmabuf, xfer_len, dir); - } - - writel(dma, &hc_regs->hcdma); - - /* Clear old interrupt conditions for this host channel. */ - writel(0x3fff, &hc_regs->hcint); - - /* Set host channel enable after all other setup is complete. */ - clrsetbits_le32(&hc_regs->hcchar, DWC2_HCCHAR_MULTICNT_MASK | - DWC2_HCCHAR_CHEN | DWC2_HCCHAR_CHDIS | - DWC2_HCCHAR_ODDFRM, - (1 << DWC2_HCCHAR_MULTICNT_OFFSET) | - (odd_frame << DWC2_HCCHAR_ODDFRM_OFFSET) | - DWC2_HCCHAR_CHEN); - - ret = wait_for_chhltd(priv, &sub, pid, timeout_ms); - - if (xfer_len) - dma_unmap_single(priv->dev, dma, xfer_len, dir); - - if (in) { - xfer_len -= sub; - - memcpy(buffer, priv->dmabuf, xfer_len); - } - - if (!ret) - *actual_len = xfer_len; - - return ret; -} - -static int usb_find_usb2_hub_address_port(struct usb_device *udev, - uint8_t *hub_address, uint8_t *hub_port) -{ - /* Find out the nearest parent which is high speed */ - while (udev->parent->parent) { - if (udev->parent->speed != USB_SPEED_HIGH) { - udev = udev->parent; - } else { - *hub_address = udev->parent->devnum; - *hub_port = udev->portnr; - return 0; - } - } - - return -EINVAL; -} - -static int chunk_msg(struct dwc2_priv *priv, struct usb_device *dev, - unsigned long pipe, u8 *pid, int in, void *buffer, int len, - int timeout_ms) -{ - struct dwc2_core_regs *regs = priv->regs; - struct dwc2_hc_regs *hc_regs = ®s->hc_regs[DWC2_HC_CHANNEL]; - struct dwc2_host_regs *host_regs = ®s->host_regs; - int devnum = usb_pipedevice(pipe); - int ep = usb_pipeendpoint(pipe); - int max = usb_maxpacket(dev, pipe); - int eptype = dwc2_eptype[usb_pipetype(pipe)]; - int done = 0; - int ret = 0; - int do_split = 0; - int complete_split = 0; - uint32_t xfer_len; - uint32_t num_packets; - int stop_transfer = 0; - uint32_t max_xfer_len; - int ssplit_frame_num = 0; - - dev_dbg(priv->dev, "%s: msg: pipe %lx pid %d in %d len %d\n", - __func__, pipe, *pid, in, len); - - max_xfer_len = CONFIG_DWC2_MAX_PACKET_COUNT * max; - if (max_xfer_len > CONFIG_DWC2_MAX_TRANSFER_SIZE) - max_xfer_len = CONFIG_DWC2_MAX_TRANSFER_SIZE; - if (max_xfer_len > DWC2_DATA_BUF_SIZE) - max_xfer_len = DWC2_DATA_BUF_SIZE; - - /* Make sure that max_xfer_len is a multiple of max packet size. */ - num_packets = max_xfer_len / max; - max_xfer_len = num_packets * max; - - /* Initialize channel */ - dwc_otg_hc_init(regs, DWC2_HC_CHANNEL, dev, devnum, ep, in, - eptype, max); - - /* Check if the target is a FS/LS device behind a HS hub */ - if (dev->speed != USB_SPEED_HIGH) { - uint8_t hub_addr; - uint8_t hub_port; - uint32_t hprt0 = readl(®s->hprt0); - - if ((hprt0 & DWC2_HPRT0_PRTSPD_MASK) == DWC2_HPRT0_PRTSPD_HIGH) { - ret = usb_find_usb2_hub_address_port(dev, &hub_addr, - &hub_port); - if (ret) - return ret; - dwc_otg_hc_init_split(hc_regs, hub_addr, hub_port); - - do_split = 1; - num_packets = 1; - max_xfer_len = max; - } - } - - do { - int actual_len = 0; - uint32_t hcint; - int odd_frame = 0; - xfer_len = len - done; - - if (xfer_len > max_xfer_len) - xfer_len = max_xfer_len; - else if (xfer_len > max) - num_packets = (xfer_len + max - 1) / max; - else - num_packets = 1; - - if (complete_split) - setbits_le32(&hc_regs->hcsplt, DWC2_HCSPLT_COMPSPLT); - else if (do_split) - clrbits_le32(&hc_regs->hcsplt, DWC2_HCSPLT_COMPSPLT); - - if (eptype == DWC2_HCCHAR_EPTYPE_INTR) { - int uframe_num = readl(&host_regs->hfnum); - if (!(uframe_num & 0x1)) - odd_frame = 1; - } - - ret = transfer_chunk(priv, pid, in, (char *)buffer + done, - num_packets, xfer_len, &actual_len, - odd_frame, timeout_ms); - - hcint = readl(&hc_regs->hcint); - if (complete_split) { - stop_transfer = 0; - if (hcint & DWC2_HCINT_NYET) { - int frame_num = DWC2_HFNUM_MAX_FRNUM & - readl(&host_regs->hfnum); - ret = 0; - if (((frame_num - ssplit_frame_num) & - DWC2_HFNUM_MAX_FRNUM) > 4) - ret = -EAGAIN; - } else - complete_split = 0; - } else if (do_split) { - if (hcint & DWC2_HCINT_ACK) { - ssplit_frame_num = DWC2_HFNUM_MAX_FRNUM & - readl(&host_regs->hfnum); - ret = 0; - complete_split = 1; - } - } - - if (ret) - break; - - if (actual_len < xfer_len) - stop_transfer = 1; - - done += actual_len; - - /* - * Transactions are done when when either all data is transferred or - * there is a short transfer. In case of a SPLIT make sure the CSPLIT - * is executed. - */ - } while (((done < len) && !stop_transfer) || complete_split); - - writel(0, &hc_regs->hcintmsk); - writel(0xFFFFFFFF, &hc_regs->hcint); - - dev->status = 0; - dev->act_len = done; - - return ret; -} - -#define to_dwc2(ptr) container_of(ptr, struct dwc2_priv, host) - -static int dwc2_init_common(struct usb_host *host) -{ - struct dwc2_priv *priv = to_dwc2(host); - struct dwc2_core_regs *regs = priv->regs; - int i, j; - - priv->ext_vbus = 0; - - dwc_otg_core_init(priv); - dwc_otg_core_host_init(priv); - - clrsetbits_le32(®s->hprt0, DWC2_HPRT0_PRTENA | - DWC2_HPRT0_PRTCONNDET | DWC2_HPRT0_PRTENCHNG | - DWC2_HPRT0_PRTOVRCURRCHNG, - DWC2_HPRT0_PRTRST); - mdelay(50); - clrbits_le32(®s->hprt0, DWC2_HPRT0_PRTENA | DWC2_HPRT0_PRTCONNDET | - DWC2_HPRT0_PRTENCHNG | DWC2_HPRT0_PRTOVRCURRCHNG | - DWC2_HPRT0_PRTRST); - - for (i = 0; i < MAX_DEVICE; i++) { - for (j = 0; j < MAX_ENDPOINT; j++) { - priv->in_data_toggle[i][j] = DWC2_HC_PID_DATA0; - priv->out_data_toggle[i][j] = DWC2_HC_PID_DATA0; - } - } - - return 0; -} - -static void dwc2_uninit_common(struct dwc2_core_regs *regs) -{ - /* Put everything in reset. */ - clrsetbits_le32(®s->hprt0, DWC2_HPRT0_PRTENA | - DWC2_HPRT0_PRTCONNDET | DWC2_HPRT0_PRTENCHNG | - DWC2_HPRT0_PRTOVRCURRCHNG, - DWC2_HPRT0_PRTRST); -} - -static int dwc2_submit_control_msg(struct usb_device *udev, - unsigned long pipe, void *buffer, int len, - struct devrequest *setup, int timeout_ms) -{ - struct usb_host *host = udev->host; - struct dwc2_priv *priv = to_dwc2(host); - int devnum = usb_pipedevice(pipe); - int ret, act_len; - u8 pid; - /* For CONTROL endpoint pid should start with DATA1 */ - int status_direction; - - if (devnum == priv->root_hub_devnum) { - udev->status = 0; - udev->speed = USB_SPEED_HIGH; - return dwc_otg_submit_rh_msg(priv, udev, pipe, buffer, len, - setup); - } - - /* SETUP stage */ - pid = DWC2_HC_PID_SETUP; - do { - ret = chunk_msg(priv, udev, pipe, &pid, 0, setup, 8, timeout_ms); - } while (ret == -EAGAIN); - - if (ret) - return ret; - - /* DATA stage */ - act_len = 0; - if (buffer) { - pid = DWC2_HC_PID_DATA1; - do { - ret = chunk_msg(priv, udev, pipe, &pid, usb_pipein(pipe), - buffer, len, timeout_ms); - act_len += udev->act_len; - buffer += udev->act_len; - len -= udev->act_len; - } while (ret == -EAGAIN); - if (ret) - return ret; - status_direction = usb_pipeout(pipe); - } else { - /* No-data CONTROL always ends with an IN transaction */ - status_direction = 1; - } - - /* STATUS stage */ - pid = DWC2_HC_PID_DATA1; - do { - ret = chunk_msg(priv, udev, pipe, &pid, status_direction, - NULL, 0, timeout_ms); - } while (ret == -EAGAIN); - - if (ret) - return ret; - - udev->act_len = act_len; - - return 0; -} - -static int dwc2_submit_bulk_msg(struct usb_device *udev, unsigned long pipe, - void *buffer, int len, int timeout_ms) -{ - struct usb_host *host = udev->host; - struct dwc2_priv *priv = to_dwc2(host); - int devnum = usb_pipedevice(pipe); - int ep = usb_pipeendpoint(pipe); - u8* pid; - - if ((devnum >= MAX_DEVICE) || (devnum == priv->root_hub_devnum)) { - udev->status = 0; - return -EINVAL; - } - - if (usb_pipein(pipe)) - pid = &priv->in_data_toggle[devnum][ep]; - else - pid = &priv->out_data_toggle[devnum][ep]; - - return chunk_msg(priv, udev, pipe, pid, usb_pipein(pipe), buffer, len, - timeout_ms); -} - -static int dwc2_submit_int_msg(struct usb_device *udev, unsigned long pipe, - void *buffer, int len, int interval) -{ - uint64_t start; - int ret; - - start = get_time_ns(); - - while (1) { - ret = dwc2_submit_bulk_msg(udev, pipe, buffer, len, 0); - if (ret != -EAGAIN) - return ret; - if (is_timeout(start, USB_CNTL_TIMEOUT * MSECOND)) - return -ETIMEDOUT; - } -} - -static int dwc2_detect(struct device_d *dev) -{ - struct dwc2_priv *priv = dev->priv; - - return usb_host_detect(&priv->host); -} - -static int dwc2_probe(struct device_d *dev) -{ - struct resource *iores; - struct dwc2_priv *priv; - struct usb_host *host; - struct device_node *np = dev->device_node; - int ret; - uint32_t snpsid; - - priv = xzalloc(sizeof(*priv)); - - iores = dev_request_mem_resource(dev, 0); - if (IS_ERR(iores)) - return PTR_ERR(iores); - - priv->regs = IOMEM(iores->start); - priv->dev = dev; - - snpsid = readl(&priv->regs->gsnpsid); - dev_info(dev, "Core Release: %x.%03x\n", - snpsid >> 12 & 0xf, snpsid & 0xfff); - - if ((snpsid & DWC2_SNPSID_DEVID_MASK) != DWC2_SNPSID_DEVID_VER_2xx && - (snpsid & DWC2_SNPSID_DEVID_MASK) != DWC2_SNPSID_DEVID_VER_3xx) { - dev_info(dev, "SNPSID invalid (not DWC2 OTG device): %08x\n", - snpsid); - return -ENODEV; - } - - priv->oc_disable = of_property_read_bool(np, "disable-over-current"); - priv->hnp_srp_disable = of_property_read_bool(np, "hnp-srp-disable"); - priv->dmabuf = dma_alloc(DWC2_DATA_BUF_SIZE); - - host = &priv->host; - - host->init = dwc2_init_common; - host->submit_int_msg = dwc2_submit_int_msg; - host->submit_control_msg = dwc2_submit_control_msg; - host->submit_bulk_msg = dwc2_submit_bulk_msg; - - dev->priv = priv; - dev->detect = dwc2_detect; - - ret = usb_register_host(host); - if (ret) - return ret; - - return 0; -} - -static void dwc2_remove(struct device_d *dev) -{ - struct dwc2_priv *priv = dev->priv; - - dwc2_uninit_common(priv->regs); -} - -static const struct of_device_id dwc2_dt_ids[] = { - { .compatible = "brcm,bcm2835-usb" }, - { .compatible = "brcm,bcm2708-usb" }, - { .compatible = "snps,dwc2" }, - { /* sentinel */ } -}; - -static struct driver_d dwc2_driver = { - .name = "dwc2", - .probe = dwc2_probe, - .remove = dwc2_remove, - .of_compatible = DRV_OF_COMPAT(dwc2_dt_ids), -}; -device_platform_driver(dwc2_driver); diff --git a/drivers/usb/host/dwc2.h b/drivers/usb/host/dwc2.h deleted file mode 100644 index bdf338f1ed..0000000000 --- a/drivers/usb/host/dwc2.h +++ /dev/null @@ -1,778 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0+ */ -/* - * Copyright (C) 2014 Marek Vasut <marex@denx.de> - */ - -#ifndef __DWC2_H__ -#define __DWC2_H__ - -struct dwc2_hc_regs { - u32 hcchar; /* 0x00 */ - u32 hcsplt; - u32 hcint; - u32 hcintmsk; - u32 hctsiz; /* 0x10 */ - u32 hcdma; - u32 reserved; - u32 hcdmab; -}; - -struct dwc2_host_regs { - u32 hcfg; /* 0x00 */ - u32 hfir; - u32 hfnum; - u32 _pad_0x40c; - u32 hptxsts; /* 0x10 */ - u32 haint; - u32 haintmsk; - u32 hflbaddr; -}; - -struct dwc2_core_regs { - u32 gotgctl; /* 0x000 */ - u32 gotgint; - u32 gahbcfg; - u32 gusbcfg; - u32 grstctl; /* 0x010 */ - u32 gintsts; - u32 gintmsk; - u32 grxstsr; - u32 grxstsp; /* 0x020 */ - u32 grxfsiz; - u32 gnptxfsiz; - u32 gnptxsts; - u32 gi2cctl; /* 0x030 */ - u32 gpvndctl; - u32 ggpio; - u32 guid; - u32 gsnpsid; /* 0x040 */ - u32 ghwcfg1; - u32 ghwcfg2; - u32 ghwcfg3; - u32 ghwcfg4; /* 0x050 */ - u32 glpmcfg; - u32 _pad_0x58_0x9c[42]; - u32 hptxfsiz; /* 0x100 */ - u32 dptxfsiz_dieptxf[15]; - u32 _pad_0x140_0x3fc[176]; - struct dwc2_host_regs host_regs; /* 0x400 */ - u32 _pad_0x420_0x43c[8]; - u32 hprt0; /* 0x440 */ - u32 _pad_0x444_0x4fc[47]; - struct dwc2_hc_regs hc_regs[16]; /* 0x500 */ - u32 _pad_0x700_0xe00[448]; - u32 pcgcctl; /* 0xe00 */ -}; - -#define DWC2_GOTGCTL_SESREQSCS (1 << 0) -#define DWC2_GOTGCTL_SESREQSCS_OFFSET 0 -#define DWC2_GOTGCTL_SESREQ (1 << 1) -#define DWC2_GOTGCTL_SESREQ_OFFSET 1 -#define DWC2_GOTGCTL_HSTNEGSCS (1 << 8) -#define DWC2_GOTGCTL_HSTNEGSCS_OFFSET 8 -#define DWC2_GOTGCTL_HNPREQ (1 << 9) -#define DWC2_GOTGCTL_HNPREQ_OFFSET 9 -#define DWC2_GOTGCTL_HSTSETHNPEN (1 << 10) -#define DWC2_GOTGCTL_HSTSETHNPEN_OFFSET 10 -#define DWC2_GOTGCTL_DEVHNPEN (1 << 11) -#define DWC2_GOTGCTL_DEVHNPEN_OFFSET 11 -#define DWC2_GOTGCTL_CONIDSTS (1 << 16) -#define DWC2_GOTGCTL_CONIDSTS_OFFSET 16 -#define DWC2_GOTGCTL_DBNCTIME (1 << 17) -#define DWC2_GOTGCTL_DBNCTIME_OFFSET 17 -#define DWC2_GOTGCTL_ASESVLD (1 << 18) -#define DWC2_GOTGCTL_ASESVLD_OFFSET 18 -#define DWC2_GOTGCTL_BSESVLD (1 << 19) -#define DWC2_GOTGCTL_BSESVLD_OFFSET 19 -#define DWC2_GOTGCTL_OTGVER (1 << 20) -#define DWC2_GOTGCTL_OTGVER_OFFSET 20 -#define DWC2_GOTGINT_SESENDDET (1 << 2) -#define DWC2_GOTGINT_SESENDDET_OFFSET 2 -#define DWC2_GOTGINT_SESREQSUCSTSCHNG (1 << 8) -#define DWC2_GOTGINT_SESREQSUCSTSCHNG_OFFSET 8 -#define DWC2_GOTGINT_HSTNEGSUCSTSCHNG (1 << 9) -#define DWC2_GOTGINT_HSTNEGSUCSTSCHNG_OFFSET 9 -#define DWC2_GOTGINT_RESERVER10_16_MASK (0x7F << 10) -#define DWC2_GOTGINT_RESERVER10_16_OFFSET 10 -#define DWC2_GOTGINT_HSTNEGDET (1 << 17) -#define DWC2_GOTGINT_HSTNEGDET_OFFSET 17 -#define DWC2_GOTGINT_ADEVTOUTCHNG (1 << 18) -#define DWC2_GOTGINT_ADEVTOUTCHNG_OFFSET 18 -#define DWC2_GOTGINT_DEBDONE (1 << 19) -#define DWC2_GOTGINT_DEBDONE_OFFSET 19 -#define DWC2_GAHBCFG_GLBLINTRMSK (1 << 0) -#define DWC2_GAHBCFG_GLBLINTRMSK_OFFSET 0 -#define DWC2_GAHBCFG_HBURSTLEN_SINGLE (0 << 1) -#define DWC2_GAHBCFG_HBURSTLEN_INCR (1 << 1) -#define DWC2_GAHBCFG_HBURSTLEN_INCR4 (3 << 1) -#define DWC2_GAHBCFG_HBURSTLEN_INCR8 (5 << 1) -#define DWC2_GAHBCFG_HBURSTLEN_INCR16 (7 << 1) -#define DWC2_GAHBCFG_HBURSTLEN_MASK (0xF << 1) -#define DWC2_GAHBCFG_HBURSTLEN_OFFSET 1 -#define DWC2_GAHBCFG_DMAENABLE (1 << 5) -#define DWC2_GAHBCFG_DMAENABLE_OFFSET 5 -#define DWC2_GAHBCFG_NPTXFEMPLVL_TXFEMPLVL (1 << 7) -#define DWC2_GAHBCFG_NPTXFEMPLVL_TXFEMPLVL_OFFSET 7 -#define DWC2_GAHBCFG_PTXFEMPLVL (1 << 8) -#define DWC2_GAHBCFG_PTXFEMPLVL_OFFSET 8 -#define DWC2_GUSBCFG_TOUTCAL_MASK (0x7 << 0) -#define DWC2_GUSBCFG_TOUTCAL_OFFSET 0 -#define DWC2_GUSBCFG_PHYIF (1 << 3) -#define DWC2_GUSBCFG_PHYIF_OFFSET 3 -#define DWC2_GUSBCFG_ULPI_UTMI_SEL (1 << 4) -#define DWC2_GUSBCFG_ULPI_UTMI_SEL_OFFSET 4 -#define DWC2_GUSBCFG_FSINTF (1 << 5) -#define DWC2_GUSBCFG_FSINTF_OFFSET 5 -#define DWC2_GUSBCFG_PHYSEL (1 << 6) -#define DWC2_GUSBCFG_PHYSEL_OFFSET 6 -#define DWC2_GUSBCFG_DDRSEL (1 << 7) -#define DWC2_GUSBCFG_DDRSEL_OFFSET 7 -#define DWC2_GUSBCFG_SRPCAP (1 << 8) -#define DWC2_GUSBCFG_SRPCAP_OFFSET 8 -#define DWC2_GUSBCFG_HNPCAP (1 << 9) -#define DWC2_GUSBCFG_HNPCAP_OFFSET 9 -#define DWC2_GUSBCFG_USBTRDTIM_MASK (0xF << 10) -#define DWC2_GUSBCFG_USBTRDTIM_OFFSET 10 -#define DWC2_GUSBCFG_NPTXFRWNDEN (1 << 14) -#define DWC2_GUSBCFG_NPTXFRWNDEN_OFFSET 14 -#define DWC2_GUSBCFG_PHYLPWRCLKSEL (1 << 15) -#define DWC2_GUSBCFG_PHYLPWRCLKSEL_OFFSET 15 -#define DWC2_GUSBCFG_OTGUTMIFSSEL (1 << 16) -#define DWC2_GUSBCFG_OTGUTMIFSSEL_OFFSET 16 -#define DWC2_GUSBCFG_ULPI_FSLS (1 << 17) -#define DWC2_GUSBCFG_ULPI_FSLS_OFFSET 17 -#define DWC2_GUSBCFG_ULPI_AUTO_RES (1 << 18) -#define DWC2_GUSBCFG_ULPI_AUTO_RES_OFFSET 18 -#define DWC2_GUSBCFG_ULPI_CLK_SUS_M (1 << 19) -#define DWC2_GUSBCFG_ULPI_CLK_SUS_M_OFFSET 19 -#define DWC2_GUSBCFG_ULPI_EXT_VBUS_DRV (1 << 20) -#define DWC2_GUSBCFG_ULPI_EXT_VBUS_DRV_OFFSET 20 -#define DWC2_GUSBCFG_ULPI_INT_VBUS_INDICATOR (1 << 21) -#define DWC2_GUSBCFG_ULPI_INT_VBUS_INDICATOR_OFFSET 21 -#define DWC2_GUSBCFG_TERM_SEL_DL_PULSE (1 << 22) -#define DWC2_GUSBCFG_TERM_SEL_DL_PULSE_OFFSET 22 -#define DWC2_GUSBCFG_INDICATOR_PASSTHROUGH (1 << 24) -#define DWC2_GUSBCFG_INDICATOR_PASSTHROUGH_OFFSET 24 -#define DWC2_GUSBCFG_IC_USB_CAP (1 << 26) -#define DWC2_GUSBCFG_IC_USB_CAP_OFFSET 26 -#define DWC2_GUSBCFG_IC_TRAFFIC_PULL_REMOVE (1 << 27) -#define DWC2_GUSBCFG_IC_TRAFFIC_PULL_REMOVE_OFFSET 27 -#define DWC2_GUSBCFG_TX_END_DELAY (1 << 28) -#define DWC2_GUSBCFG_TX_END_DELAY_OFFSET 28 -#define DWC2_GUSBCFG_FORCEHOSTMODE (1 << 29) -#define DWC2_GUSBCFG_FORCEHOSTMODE_OFFSET 29 -#define DWC2_GUSBCFG_FORCEDEVMODE (1 << 30) -#define DWC2_GUSBCFG_FORCEDEVMODE_OFFSET 30 -#define DWC2_GLPMCTL_LPM_CAP_EN (1 << 0) -#define DWC2_GLPMCTL_LPM_CAP_EN_OFFSET 0 -#define DWC2_GLPMCTL_APPL_RESP (1 << 1) -#define DWC2_GLPMCTL_APPL_RESP_OFFSET 1 -#define DWC2_GLPMCTL_HIRD_MASK (0xF << 2) -#define DWC2_GLPMCTL_HIRD_OFFSET 2 -#define DWC2_GLPMCTL_REM_WKUP_EN (1 << 6) -#define DWC2_GLPMCTL_REM_WKUP_EN_OFFSET 6 -#define DWC2_GLPMCTL_EN_UTMI_SLEEP (1 << 7) -#define DWC2_GLPMCTL_EN_UTMI_SLEEP_OFFSET 7 -#define DWC2_GLPMCTL_HIRD_THRES_MASK (0x1F << 8) -#define DWC2_GLPMCTL_HIRD_THRES_OFFSET 8 -#define DWC2_GLPMCTL_LPM_RESP_MASK (0x3 << 13) -#define DWC2_GLPMCTL_LPM_RESP_OFFSET 13 -#define DWC2_GLPMCTL_PRT_SLEEP_STS (1 << 15) -#define DWC2_GLPMCTL_PRT_SLEEP_STS_OFFSET 15 -#define DWC2_GLPMCTL_SLEEP_STATE_RESUMEOK (1 << 16) -#define DWC2_GLPMCTL_SLEEP_STATE_RESUMEOK_OFFSET 16 -#define DWC2_GLPMCTL_LPM_CHAN_INDEX_MASK (0xF << 17) -#define DWC2_GLPMCTL_LPM_CHAN_INDEX_OFFSET 17 -#define DWC2_GLPMCTL_RETRY_COUNT_MASK (0x7 << 21) -#define DWC2_GLPMCTL_RETRY_COUNT_OFFSET 21 -#define DWC2_GLPMCTL_SEND_LPM (1 << 24) -#define DWC2_GLPMCTL_SEND_LPM_OFFSET 24 -#define DWC2_GLPMCTL_RETRY_COUNT_STS_MASK (0x7 << 25) -#define DWC2_GLPMCTL_RETRY_COUNT_STS_OFFSET 25 -#define DWC2_GLPMCTL_HSIC_CONNECT (1 << 30) -#define DWC2_GLPMCTL_HSIC_CONNECT_OFFSET 30 -#define DWC2_GLPMCTL_INV_SEL_HSIC (1 << 31) -#define DWC2_GLPMCTL_INV_SEL_HSIC_OFFSET 31 -#define DWC2_GRSTCTL_CSFTRST (1 << 0) -#define DWC2_GRSTCTL_CSFTRST_OFFSET 0 -#define DWC2_GRSTCTL_HSFTRST (1 << 1) -#define DWC2_GRSTCTL_HSFTRST_OFFSET 1 -#define DWC2_GRSTCTL_HSTFRM (1 << 2) -#define DWC2_GRSTCTL_HSTFRM_OFFSET 2 -#define DWC2_GRSTCTL_INTKNQFLSH (1 << 3) -#define DWC2_GRSTCTL_INTKNQFLSH_OFFSET 3 -#define DWC2_GRSTCTL_RXFFLSH (1 << 4) -#define DWC2_GRSTCTL_RXFFLSH_OFFSET 4 -#define DWC2_GRSTCTL_TXFFLSH (1 << 5) -#define DWC2_GRSTCTL_TXFFLSH_OFFSET 5 -#define DWC2_GRSTCTL_TXFNUM_MASK (0x1F << 6) -#define DWC2_GRSTCTL_TXFNUM_OFFSET 6 -#define DWC2_GRSTCTL_DMAREQ (1 << 30) -#define DWC2_GRSTCTL_DMAREQ_OFFSET 30 -#define DWC2_GRSTCTL_AHBIDLE (1 << 31) -#define DWC2_GRSTCTL_AHBIDLE_OFFSET 31 -#define DWC2_GINTMSK_MODEMISMATCH (1 << 1) -#define DWC2_GINTMSK_MODEMISMATCH_OFFSET 1 -#define DWC2_GINTMSK_OTGINTR (1 << 2) -#define DWC2_GINTMSK_OTGINTR_OFFSET 2 -#define DWC2_GINTMSK_SOFINTR (1 << 3) -#define DWC2_GINTMSK_SOFINTR_OFFSET 3 -#define DWC2_GINTMSK_RXSTSQLVL (1 << 4) -#define DWC2_GINTMSK_RXSTSQLVL_OFFSET 4 -#define DWC2_GINTMSK_NPTXFEMPTY (1 << 5) -#define DWC2_GINTMSK_NPTXFEMPTY_OFFSET 5 -#define DWC2_GINTMSK_GINNAKEFF (1 << 6) -#define DWC2_GINTMSK_GINNAKEFF_OFFSET 6 -#define DWC2_GINTMSK_GOUTNAKEFF (1 << 7) -#define DWC2_GINTMSK_GOUTNAKEFF_OFFSET 7 -#define DWC2_GINTMSK_I2CINTR (1 << 9) -#define DWC2_GINTMSK_I2CINTR_OFFSET 9 -#define DWC2_GINTMSK_ERLYSUSPEND (1 << 10) -#define DWC2_GINTMSK_ERLYSUSPEND_OFFSET 10 -#define DWC2_GINTMSK_USBSUSPEND (1 << 11) -#define DWC2_GINTMSK_USBSUSPEND_OFFSET 11 -#define DWC2_GINTMSK_USBRESET (1 << 12) -#define DWC2_GINTMSK_USBRESET_OFFSET 12 -#define DWC2_GINTMSK_ENUMDONE (1 << 13) -#define DWC2_GINTMSK_ENUMDONE_OFFSET 13 -#define DWC2_GINTMSK_ISOOUTDROP (1 << 14) -#define DWC2_GINTMSK_ISOOUTDROP_OFFSET 14 -#define DWC2_GINTMSK_EOPFRAME (1 << 15) -#define DWC2_GINTMSK_EOPFRAME_OFFSET 15 -#define DWC2_GINTMSK_EPMISMATCH (1 << 17) -#define DWC2_GINTMSK_EPMISMATCH_OFFSET 17 -#define DWC2_GINTMSK_INEPINTR (1 << 18) -#define DWC2_GINTMSK_INEPINTR_OFFSET 18 -#define DWC2_GINTMSK_OUTEPINTR (1 << 19) -#define DWC2_GINTMSK_OUTEPINTR_OFFSET 19 -#define DWC2_GINTMSK_INCOMPLISOIN (1 << 20) -#define DWC2_GINTMSK_INCOMPLISOIN_OFFSET 20 -#define DWC2_GINTMSK_INCOMPLISOOUT (1 << 21) -#define DWC2_GINTMSK_INCOMPLISOOUT_OFFSET 21 -#define DWC2_GINTMSK_PORTINTR (1 << 24) -#define DWC2_GINTMSK_PORTINTR_OFFSET 24 -#define DWC2_GINTMSK_HCINTR (1 << 25) -#define DWC2_GINTMSK_HCINTR_OFFSET 25 -#define DWC2_GINTMSK_PTXFEMPTY (1 << 26) -#define DWC2_GINTMSK_PTXFEMPTY_OFFSET 26 -#define DWC2_GINTMSK_LPMTRANRCVD (1 << 27) -#define DWC2_GINTMSK_LPMTRANRCVD_OFFSET 27 -#define DWC2_GINTMSK_CONIDSTSCHNG (1 << 28) -#define DWC2_GINTMSK_CONIDSTSCHNG_OFFSET 28 -#define DWC2_GINTMSK_DISCONNECT (1 << 29) -#define DWC2_GINTMSK_DISCONNECT_OFFSET 29 -#define DWC2_GINTMSK_SESSREQINTR (1 << 30) -#define DWC2_GINTMSK_SESSREQINTR_OFFSET 30 -#define DWC2_GINTMSK_WKUPINTR (1 << 31) -#define DWC2_GINTMSK_WKUPINTR_OFFSET 31 -#define DWC2_GINTSTS_CURMODE_DEVICE (0 << 0) -#define DWC2_GINTSTS_CURMODE_HOST (1 << 0) -#define DWC2_GINTSTS_CURMODE (1 << 0) -#define DWC2_GINTSTS_CURMODE_OFFSET 0 -#define DWC2_GINTSTS_MODEMISMATCH (1 << 1) -#define DWC2_GINTSTS_MODEMISMATCH_OFFSET 1 -#define DWC2_GINTSTS_OTGINTR (1 << 2) -#define DWC2_GINTSTS_OTGINTR_OFFSET 2 -#define DWC2_GINTSTS_SOFINTR (1 << 3) -#define DWC2_GINTSTS_SOFINTR_OFFSET 3 -#define DWC2_GINTSTS_RXSTSQLVL (1 << 4) -#define DWC2_GINTSTS_RXSTSQLVL_OFFSET 4 -#define DWC2_GINTSTS_NPTXFEMPTY (1 << 5) -#define DWC2_GINTSTS_NPTXFEMPTY_OFFSET 5 -#define DWC2_GINTSTS_GINNAKEFF (1 << 6) -#define DWC2_GINTSTS_GINNAKEFF_OFFSET 6 -#define DWC2_GINTSTS_GOUTNAKEFF (1 << 7) -#define DWC2_GINTSTS_GOUTNAKEFF_OFFSET 7 -#define DWC2_GINTSTS_I2CINTR (1 << 9) -#define DWC2_GINTSTS_I2CINTR_OFFSET 9 -#define DWC2_GINTSTS_ERLYSUSPEND (1 << 10) -#define DWC2_GINTSTS_ERLYSUSPEND_OFFSET 10 -#define DWC2_GINTSTS_USBSUSPEND (1 << 11) -#define DWC2_GINTSTS_USBSUSPEND_OFFSET 11 -#define DWC2_GINTSTS_USBRESET (1 << 12) -#define DWC2_GINTSTS_USBRESET_OFFSET 12 -#define DWC2_GINTSTS_ENUMDONE (1 << 13) -#define DWC2_GINTSTS_ENUMDONE_OFFSET 13 -#define DWC2_GINTSTS_ISOOUTDROP (1 << 14) -#define DWC2_GINTSTS_ISOOUTDROP_OFFSET 14 -#define DWC2_GINTSTS_EOPFRAME (1 << 15) -#define DWC2_GINTSTS_EOPFRAME_OFFSET 15 -#define DWC2_GINTSTS_INTOKENRX (1 << 16) -#define DWC2_GINTSTS_INTOKENRX_OFFSET 16 -#define DWC2_GINTSTS_EPMISMATCH (1 << 17) -#define DWC2_GINTSTS_EPMISMATCH_OFFSET 17 -#define DWC2_GINTSTS_INEPINT (1 << 18) -#define DWC2_GINTSTS_INEPINT_OFFSET 18 -#define DWC2_GINTSTS_OUTEPINTR (1 << 19) -#define DWC2_GINTSTS_OUTEPINTR_OFFSET 19 -#define DWC2_GINTSTS_INCOMPLISOIN (1 << 20) -#define DWC2_GINTSTS_INCOMPLISOIN_OFFSET 20 -#define DWC2_GINTSTS_INCOMPLISOOUT (1 << 21) -#define DWC2_GINTSTS_INCOMPLISOOUT_OFFSET 21 -#define DWC2_GINTSTS_PORTINTR (1 << 24) -#define DWC2_GINTSTS_PORTINTR_OFFSET 24 -#define DWC2_GINTSTS_HCINTR (1 << 25) -#define DWC2_GINTSTS_HCINTR_OFFSET 25 -#define DWC2_GINTSTS_PTXFEMPTY (1 << 26) -#define DWC2_GINTSTS_PTXFEMPTY_OFFSET 26 -#define DWC2_GINTSTS_LPMTRANRCVD (1 << 27) -#define DWC2_GINTSTS_LPMTRANRCVD_OFFSET 27 -#define DWC2_GINTSTS_CONIDSTSCHNG (1 << 28) -#define DWC2_GINTSTS_CONIDSTSCHNG_OFFSET 28 -#define DWC2_GINTSTS_DISCONNECT (1 << 29) -#define DWC2_GINTSTS_DISCONNECT_OFFSET 29 -#define DWC2_GINTSTS_SESSREQINTR (1 << 30) -#define DWC2_GINTSTS_SESSREQINTR_OFFSET 30 -#define DWC2_GINTSTS_WKUPINTR (1 << 31) -#define DWC2_GINTSTS_WKUPINTR_OFFSET 31 -#define DWC2_GRXSTS_EPNUM_MASK (0xF << 0) -#define DWC2_GRXSTS_EPNUM_OFFSET 0 -#define DWC2_GRXSTS_BCNT_MASK (0x7FF << 4) -#define DWC2_GRXSTS_BCNT_OFFSET 4 -#define DWC2_GRXSTS_DPID_MASK (0x3 << 15) -#define DWC2_GRXSTS_DPID_OFFSET 15 -#define DWC2_GRXSTS_PKTSTS_MASK (0xF << 17) -#define DWC2_GRXSTS_PKTSTS_OFFSET 17 -#define DWC2_GRXSTS_FN_MASK (0xF << 21) -#define DWC2_GRXSTS_FN_OFFSET 21 -#define DWC2_FIFOSIZE_STARTADDR_MASK (0xFFFF << 0) -#define DWC2_FIFOSIZE_STARTADDR_OFFSET 0 -#define DWC2_FIFOSIZE_DEPTH_MASK (0xFFFF << 16) -#define DWC2_FIFOSIZE_DEPTH_OFFSET 16 -#define DWC2_GNPTXSTS_NPTXFSPCAVAIL_MASK (0xFFFF << 0) -#define DWC2_GNPTXSTS_NPTXFSPCAVAIL_OFFSET 0 -#define DWC2_GNPTXSTS_NPTXQSPCAVAIL_MASK (0xFF << 16) -#define DWC2_GNPTXSTS_NPTXQSPCAVAIL_OFFSET 16 -#define DWC2_GNPTXSTS_NPTXQTOP_TERMINATE (1 << 24) -#define DWC2_GNPTXSTS_NPTXQTOP_TERMINATE_OFFSET 24 -#define DWC2_GNPTXSTS_NPTXQTOP_TOKEN_MASK (0x3 << 25) -#define DWC2_GNPTXSTS_NPTXQTOP_TOKEN_OFFSET 25 -#define DWC2_GNPTXSTS_NPTXQTOP_CHNEP_MASK (0xF << 27) -#define DWC2_GNPTXSTS_NPTXQTOP_CHNEP_OFFSET 27 -#define DWC2_DTXFSTS_TXFSPCAVAIL_MASK (0xFFFF << 0) -#define DWC2_DTXFSTS_TXFSPCAVAIL_OFFSET 0 -#define DWC2_GI2CCTL_RWDATA_MASK (0xFF << 0) -#define DWC2_GI2CCTL_RWDATA_OFFSET 0 -#define DWC2_GI2CCTL_REGADDR_MASK (0xFF << 8) -#define DWC2_GI2CCTL_REGADDR_OFFSET 8 -#define DWC2_GI2CCTL_ADDR_MASK (0x7F << 16) -#define DWC2_GI2CCTL_ADDR_OFFSET 16 -#define DWC2_GI2CCTL_I2CEN (1 << 23) -#define DWC2_GI2CCTL_I2CEN_OFFSET 23 -#define DWC2_GI2CCTL_ACK (1 << 24) -#define DWC2_GI2CCTL_ACK_OFFSET 24 -#define DWC2_GI2CCTL_I2CSUSPCTL (1 << 25) -#define DWC2_GI2CCTL_I2CSUSPCTL_OFFSET 25 -#define DWC2_GI2CCTL_I2CDEVADDR_MASK (0x3 << 26) -#define DWC2_GI2CCTL_I2CDEVADDR_OFFSET 26 -#define DWC2_GI2CCTL_RW (1 << 30) -#define DWC2_GI2CCTL_RW_OFFSET 30 -#define DWC2_GI2CCTL_BSYDNE (1 << 31) -#define DWC2_GI2CCTL_BSYDNE_OFFSET 31 -#define DWC2_HWCFG1_EP_DIR0_MASK (0x3 << 0) -#define DWC2_HWCFG1_EP_DIR0_OFFSET 0 -#define DWC2_HWCFG1_EP_DIR1_MASK (0x3 << 2) -#define DWC2_HWCFG1_EP_DIR1_OFFSET 2 -#define DWC2_HWCFG1_EP_DIR2_MASK (0x3 << 4) -#define DWC2_HWCFG1_EP_DIR2_OFFSET 4 -#define DWC2_HWCFG1_EP_DIR3_MASK (0x3 << 6) -#define DWC2_HWCFG1_EP_DIR3_OFFSET 6 -#define DWC2_HWCFG1_EP_DIR4_MASK (0x3 << 8) -#define DWC2_HWCFG1_EP_DIR4_OFFSET 8 -#define DWC2_HWCFG1_EP_DIR5_MASK (0x3 << 10) -#define DWC2_HWCFG1_EP_DIR5_OFFSET 10 -#define DWC2_HWCFG1_EP_DIR6_MASK (0x3 << 12) -#define DWC2_HWCFG1_EP_DIR6_OFFSET 12 -#define DWC2_HWCFG1_EP_DIR7_MASK (0x3 << 14) -#define DWC2_HWCFG1_EP_DIR7_OFFSET 14 -#define DWC2_HWCFG1_EP_DIR8_MASK (0x3 << 16) -#define DWC2_HWCFG1_EP_DIR8_OFFSET 16 -#define DWC2_HWCFG1_EP_DIR9_MASK (0x3 << 18) -#define DWC2_HWCFG1_EP_DIR9_OFFSET 18 -#define DWC2_HWCFG1_EP_DIR10_MASK (0x3 << 20) -#define DWC2_HWCFG1_EP_DIR10_OFFSET 20 -#define DWC2_HWCFG1_EP_DIR11_MASK (0x3 << 22) -#define DWC2_HWCFG1_EP_DIR11_OFFSET 22 -#define DWC2_HWCFG1_EP_DIR12_MASK (0x3 << 24) -#define DWC2_HWCFG1_EP_DIR12_OFFSET 24 -#define DWC2_HWCFG1_EP_DIR13_MASK (0x3 << 26) -#define DWC2_HWCFG1_EP_DIR13_OFFSET 26 -#define DWC2_HWCFG1_EP_DIR14_MASK (0x3 << 28) -#define DWC2_HWCFG1_EP_DIR14_OFFSET 28 -#define DWC2_HWCFG1_EP_DIR15_MASK (0x3 << 30) -#define DWC2_HWCFG1_EP_DIR15_OFFSET 30 -#define DWC2_HWCFG2_OP_MODE_MASK (0x7 << 0) -#define DWC2_HWCFG2_OP_MODE_OFFSET 0 -#define DWC2_HWCFG2_ARCHITECTURE_SLAVE_ONLY (0x0 << 3) -#define DWC2_HWCFG2_ARCHITECTURE_EXT_DMA (0x1 << 3) -#define DWC2_HWCFG2_ARCHITECTURE_INT_DMA (0x2 << 3) -#define DWC2_HWCFG2_ARCHITECTURE_MASK (0x3 << 3) -#define DWC2_HWCFG2_ARCHITECTURE_OFFSET 3 -#define DWC2_HWCFG2_POINT2POINT (1 << 5) -#define DWC2_HWCFG2_POINT2POINT_OFFSET 5 -#define DWC2_HWCFG2_HS_PHY_TYPE_MASK (0x3 << 6) -#define DWC2_HWCFG2_HS_PHY_TYPE_OFFSET 6 -#define DWC2_HWCFG2_FS_PHY_TYPE_MASK (0x3 << 8) -#define DWC2_HWCFG2_FS_PHY_TYPE_OFFSET 8 -#define DWC2_HWCFG2_NUM_DEV_EP_MASK (0xF << 10) -#define DWC2_HWCFG2_NUM_DEV_EP_OFFSET 10 -#define DWC2_HWCFG2_NUM_HOST_CHAN_MASK (0xF << 14) -#define DWC2_HWCFG2_NUM_HOST_CHAN_OFFSET 14 -#define DWC2_HWCFG2_PERIO_EP_SUPPORTED (1 << 18) -#define DWC2_HWCFG2_PERIO_EP_SUPPORTED_OFFSET 18 -#define DWC2_HWCFG2_DYNAMIC_FIFO (1 << 19) -#define DWC2_HWCFG2_DYNAMIC_FIFO_OFFSET 19 -#define DWC2_HWCFG2_MULTI_PROC_INT (1 << 20) -#define DWC2_HWCFG2_MULTI_PROC_INT_OFFSET 20 -#define DWC2_HWCFG2_NONPERIO_TX_Q_DEPTH_MASK (0x3 << 22) -#define DWC2_HWCFG2_NONPERIO_TX_Q_DEPTH_OFFSET 22 -#define DWC2_HWCFG2_HOST_PERIO_TX_Q_DEPTH_MASK (0x3 << 24) -#define DWC2_HWCFG2_HOST_PERIO_TX_Q_DEPTH_OFFSET 24 -#define DWC2_HWCFG2_DEV_TOKEN_Q_DEPTH_MASK (0x1F << 26) -#define DWC2_HWCFG2_DEV_TOKEN_Q_DEPTH_OFFSET 26 -#define DWC2_HWCFG3_XFER_SIZE_CNTR_WIDTH_MASK (0xF << 0) -#define DWC2_HWCFG3_XFER_SIZE_CNTR_WIDTH_OFFSET 0 -#define DWC2_HWCFG3_PACKET_SIZE_CNTR_WIDTH_MASK (0x7 << 4) -#define DWC2_HWCFG3_PACKET_SIZE_CNTR_WIDTH_OFFSET 4 -#define DWC2_HWCFG3_OTG_FUNC (1 << 7) -#define DWC2_HWCFG3_OTG_FUNC_OFFSET 7 -#define DWC2_HWCFG3_I2C (1 << 8) -#define DWC2_HWCFG3_I2C_OFFSET 8 -#define DWC2_HWCFG3_VENDOR_CTRL_IF (1 << 9) -#define DWC2_HWCFG3_VENDOR_CTRL_IF_OFFSET 9 -#define DWC2_HWCFG3_OPTIONAL_FEATURES (1 << 10) -#define DWC2_HWCFG3_OPTIONAL_FEATURES_OFFSET 10 -#define DWC2_HWCFG3_SYNCH_RESET_TYPE (1 << 11) -#define DWC2_HWCFG3_SYNCH_RESET_TYPE_OFFSET 11 -#define DWC2_HWCFG3_OTG_ENABLE_IC_USB (1 << 12) -#define DWC2_HWCFG3_OTG_ENABLE_IC_USB_OFFSET 12 -#define DWC2_HWCFG3_OTG_ENABLE_HSIC (1 << 13) -#define DWC2_HWCFG3_OTG_ENABLE_HSIC_OFFSET 13 -#define DWC2_HWCFG3_OTG_LPM_EN (1 << 15) -#define DWC2_HWCFG3_OTG_LPM_EN_OFFSET 15 -#define DWC2_HWCFG3_DFIFO_DEPTH_MASK (0xFFFF << 16) -#define DWC2_HWCFG3_DFIFO_DEPTH_OFFSET 16 -#define DWC2_HWCFG4_NUM_DEV_PERIO_IN_EP_MASK (0xF << 0) -#define DWC2_HWCFG4_NUM_DEV_PERIO_IN_EP_OFFSET 0 -#define DWC2_HWCFG4_POWER_OPTIMIZ (1 << 4) -#define DWC2_HWCFG4_POWER_OPTIMIZ_OFFSET 4 -#define DWC2_HWCFG4_MIN_AHB_FREQ_MASK (0x1FF << 5) -#define DWC2_HWCFG4_MIN_AHB_FREQ_OFFSET 5 -#define DWC2_HWCFG4_UTMI_PHY_DATA_WIDTH_MASK (0x3 << 14) -#define DWC2_HWCFG4_UTMI_PHY_DATA_WIDTH_OFFSET 14 -#define DWC2_HWCFG4_NUM_DEV_MODE_CTRL_EP_MASK (0xF << 16) -#define DWC2_HWCFG4_NUM_DEV_MODE_CTRL_EP_OFFSET 16 -#define DWC2_HWCFG4_IDDIG_FILT_EN (1 << 20) -#define DWC2_HWCFG4_IDDIG_FILT_EN_OFFSET 20 -#define DWC2_HWCFG4_VBUS_VALID_FILT_EN (1 << 21) -#define DWC2_HWCFG4_VBUS_VALID_FILT_EN_OFFSET 21 -#define DWC2_HWCFG4_A_VALID_FILT_EN (1 << 22) -#define DWC2_HWCFG4_A_VALID_FILT_EN_OFFSET 22 -#define DWC2_HWCFG4_B_VALID_FILT_EN (1 << 23) -#define DWC2_HWCFG4_B_VALID_FILT_EN_OFFSET 23 -#define DWC2_HWCFG4_SESSION_END_FILT_EN (1 << 24) -#define DWC2_HWCFG4_SESSION_END_FILT_EN_OFFSET 24 -#define DWC2_HWCFG4_DED_FIFO_EN (1 << 25) -#define DWC2_HWCFG4_DED_FIFO_EN_OFFSET 25 -#define DWC2_HWCFG4_NUM_IN_EPS_MASK (0xF << 26) -#define DWC2_HWCFG4_NUM_IN_EPS_OFFSET 26 -#define DWC2_HWCFG4_DESC_DMA (1 << 30) -#define DWC2_HWCFG4_DESC_DMA_OFFSET 30 -#define DWC2_HWCFG4_DESC_DMA_DYN (1 << 31) -#define DWC2_HWCFG4_DESC_DMA_DYN_OFFSET 31 -#define DWC2_HCFG_FSLSPCLKSEL_30_60_MHZ 0 -#define DWC2_HCFG_FSLSPCLKSEL_48_MHZ 1 -#define DWC2_HCFG_FSLSPCLKSEL_6_MHZ 2 -#define DWC2_HCFG_FSLSPCLKSEL_MASK (0x3 << 0) -#define DWC2_HCFG_FSLSPCLKSEL_OFFSET 0 -#define DWC2_HCFG_FSLSSUPP (1 << 2) -#define DWC2_HCFG_FSLSSUPP_OFFSET 2 -#define DWC2_HCFG_DESCDMA (1 << 23) -#define DWC2_HCFG_DESCDMA_OFFSET 23 -#define DWC2_HCFG_FRLISTEN_MASK (0x3 << 24) -#define DWC2_HCFG_FRLISTEN_OFFSET 24 -#define DWC2_HCFG_PERSCHEDENA (1 << 26) -#define DWC2_HCFG_PERSCHEDENA_OFFSET 26 -#define DWC2_HCFG_PERSCHEDSTAT (1 << 27) -#define DWC2_HCFG_PERSCHEDSTAT_OFFSET 27 -#define DWC2_HFIR_FRINT_MASK (0xFFFF << 0) -#define DWC2_HFIR_FRINT_OFFSET 0 -#define DWC2_HFNUM_FRNUM_MASK (0xFFFF << 0) -#define DWC2_HFNUM_FRNUM_OFFSET 0 -#define DWC2_HFNUM_FRREM_MASK (0xFFFF << 16) -#define DWC2_HFNUM_FRREM_OFFSET 16 -#define DWC2_HFNUM_MAX_FRNUM 0x3FFF -#define DWC2_HPTXSTS_PTXFSPCAVAIL_MASK (0xFFFF << 0) -#define DWC2_HPTXSTS_PTXFSPCAVAIL_OFFSET 0 -#define DWC2_HPTXSTS_PTXQSPCAVAIL_MASK (0xFF << 16) -#define DWC2_HPTXSTS_PTXQSPCAVAIL_OFFSET 16 -#define DWC2_HPTXSTS_PTXQTOP_TERMINATE (1 << 24) -#define DWC2_HPTXSTS_PTXQTOP_TERMINATE_OFFSET 24 -#define DWC2_HPTXSTS_PTXQTOP_TOKEN_MASK (0x3 << 25) -#define DWC2_HPTXSTS_PTXQTOP_TOKEN_OFFSET 25 -#define DWC2_HPTXSTS_PTXQTOP_CHNUM_MASK (0xF << 27) -#define DWC2_HPTXSTS_PTXQTOP_CHNUM_OFFSET 27 -#define DWC2_HPTXSTS_PTXQTOP_ODD (1 << 31) -#define DWC2_HPTXSTS_PTXQTOP_ODD_OFFSET 31 -#define DWC2_HPRT0_PRTCONNSTS (1 << 0) -#define DWC2_HPRT0_PRTCONNSTS_OFFSET 0 -#define DWC2_HPRT0_PRTCONNDET (1 << 1) -#define DWC2_HPRT0_PRTCONNDET_OFFSET 1 -#define DWC2_HPRT0_PRTENA (1 << 2) -#define DWC2_HPRT0_PRTENA_OFFSET 2 -#define DWC2_HPRT0_PRTENCHNG (1 << 3) -#define DWC2_HPRT0_PRTENCHNG_OFFSET 3 -#define DWC2_HPRT0_PRTOVRCURRACT (1 << 4) -#define DWC2_HPRT0_PRTOVRCURRACT_OFFSET 4 -#define DWC2_HPRT0_PRTOVRCURRCHNG (1 << 5) -#define DWC2_HPRT0_PRTOVRCURRCHNG_OFFSET 5 -#define DWC2_HPRT0_PRTRES (1 << 6) -#define DWC2_HPRT0_PRTRES_OFFSET 6 -#define DWC2_HPRT0_PRTSUSP (1 << 7) -#define DWC2_HPRT0_PRTSUSP_OFFSET 7 -#define DWC2_HPRT0_PRTRST (1 << 8) -#define DWC2_HPRT0_PRTRST_OFFSET 8 -#define DWC2_HPRT0_PRTLNSTS_MASK (0x3 << 10) -#define DWC2_HPRT0_PRTLNSTS_OFFSET 10 -#define DWC2_HPRT0_PRTPWR (1 << 12) -#define DWC2_HPRT0_PRTPWR_OFFSET 12 -#define DWC2_HPRT0_PRTTSTCTL_MASK (0xF << 13) -#define DWC2_HPRT0_PRTTSTCTL_OFFSET 13 -#define DWC2_HPRT0_PRTSPD_HIGH (0 << 17) -#define DWC2_HPRT0_PRTSPD_FULL (1 << 17) -#define DWC2_HPRT0_PRTSPD_LOW (2 << 17) -#define DWC2_HPRT0_PRTSPD_MASK (0x3 << 17) -#define DWC2_HPRT0_PRTSPD_OFFSET 17 -#define DWC2_HAINT_CH0 (1 << 0) -#define DWC2_HAINT_CH0_OFFSET 0 -#define DWC2_HAINT_CH1 (1 << 1) -#define DWC2_HAINT_CH1_OFFSET 1 -#define DWC2_HAINT_CH2 (1 << 2) -#define DWC2_HAINT_CH2_OFFSET 2 -#define DWC2_HAINT_CH3 (1 << 3) -#define DWC2_HAINT_CH3_OFFSET 3 -#define DWC2_HAINT_CH4 (1 << 4) -#define DWC2_HAINT_CH4_OFFSET 4 -#define DWC2_HAINT_CH5 (1 << 5) -#define DWC2_HAINT_CH5_OFFSET 5 -#define DWC2_HAINT_CH6 (1 << 6) -#define DWC2_HAINT_CH6_OFFSET 6 -#define DWC2_HAINT_CH7 (1 << 7) -#define DWC2_HAINT_CH7_OFFSET 7 -#define DWC2_HAINT_CH8 (1 << 8) -#define DWC2_HAINT_CH8_OFFSET 8 -#define DWC2_HAINT_CH9 (1 << 9) -#define DWC2_HAINT_CH9_OFFSET 9 -#define DWC2_HAINT_CH10 (1 << 10) -#define DWC2_HAINT_CH10_OFFSET 10 -#define DWC2_HAINT_CH11 (1 << 11) -#define DWC2_HAINT_CH11_OFFSET 11 -#define DWC2_HAINT_CH12 (1 << 12) -#define DWC2_HAINT_CH12_OFFSET 12 -#define DWC2_HAINT_CH13 (1 << 13) -#define DWC2_HAINT_CH13_OFFSET 13 -#define DWC2_HAINT_CH14 (1 << 14) -#define DWC2_HAINT_CH14_OFFSET 14 -#define DWC2_HAINT_CH15 (1 << 15) -#define DWC2_HAINT_CH15_OFFSET 15 -#define DWC2_HAINT_CHINT_MASK 0xffff -#define DWC2_HAINT_CHINT_OFFSET 0 -#define DWC2_HAINTMSK_CH0 (1 << 0) -#define DWC2_HAINTMSK_CH0_OFFSET 0 -#define DWC2_HAINTMSK_CH1 (1 << 1) -#define DWC2_HAINTMSK_CH1_OFFSET 1 -#define DWC2_HAINTMSK_CH2 (1 << 2) -#define DWC2_HAINTMSK_CH2_OFFSET 2 -#define DWC2_HAINTMSK_CH3 (1 << 3) -#define DWC2_HAINTMSK_CH3_OFFSET 3 -#define DWC2_HAINTMSK_CH4 (1 << 4) -#define DWC2_HAINTMSK_CH4_OFFSET 4 -#define DWC2_HAINTMSK_CH5 (1 << 5) -#define DWC2_HAINTMSK_CH5_OFFSET 5 -#define DWC2_HAINTMSK_CH6 (1 << 6) -#define DWC2_HAINTMSK_CH6_OFFSET 6 -#define DWC2_HAINTMSK_CH7 (1 << 7) -#define DWC2_HAINTMSK_CH7_OFFSET 7 -#define DWC2_HAINTMSK_CH8 (1 << 8) -#define DWC2_HAINTMSK_CH8_OFFSET 8 -#define DWC2_HAINTMSK_CH9 (1 << 9) -#define DWC2_HAINTMSK_CH9_OFFSET 9 -#define DWC2_HAINTMSK_CH10 (1 << 10) -#define DWC2_HAINTMSK_CH10_OFFSET 10 -#define DWC2_HAINTMSK_CH11 (1 << 11) -#define DWC2_HAINTMSK_CH11_OFFSET 11 -#define DWC2_HAINTMSK_CH12 (1 << 12) -#define DWC2_HAINTMSK_CH12_OFFSET 12 -#define DWC2_HAINTMSK_CH13 (1 << 13) -#define DWC2_HAINTMSK_CH13_OFFSET 13 -#define DWC2_HAINTMSK_CH14 (1 << 14) -#define DWC2_HAINTMSK_CH14_OFFSET 14 -#define DWC2_HAINTMSK_CH15 (1 << 15) -#define DWC2_HAINTMSK_CH15_OFFSET 15 -#define DWC2_HAINTMSK_CHINT_MASK 0xffff -#define DWC2_HAINTMSK_CHINT_OFFSET 0 -#define DWC2_HCCHAR_MPS_MASK (0x7FF << 0) -#define DWC2_HCCHAR_MPS_OFFSET 0 -#define DWC2_HCCHAR_EPNUM_MASK (0xF << 11) -#define DWC2_HCCHAR_EPNUM_OFFSET 11 -#define DWC2_HCCHAR_EPDIR (1 << 15) -#define DWC2_HCCHAR_EPDIR_OFFSET 15 -#define DWC2_HCCHAR_LSPDDEV (1 << 17) -#define DWC2_HCCHAR_LSPDDEV_OFFSET 17 -#define DWC2_HCCHAR_EPTYPE_CONTROL 0 -#define DWC2_HCCHAR_EPTYPE_ISOC 1 -#define DWC2_HCCHAR_EPTYPE_BULK 2 -#define DWC2_HCCHAR_EPTYPE_INTR 3 -#define DWC2_HCCHAR_EPTYPE_MASK (0x3 << 18) -#define DWC2_HCCHAR_EPTYPE_OFFSET 18 -#define DWC2_HCCHAR_MULTICNT_MASK (0x3 << 20) -#define DWC2_HCCHAR_MULTICNT_OFFSET 20 -#define DWC2_HCCHAR_DEVADDR_MASK (0x7F << 22) -#define DWC2_HCCHAR_DEVADDR_OFFSET 22 -#define DWC2_HCCHAR_ODDFRM (1 << 29) -#define DWC2_HCCHAR_ODDFRM_OFFSET 29 -#define DWC2_HCCHAR_CHDIS (1 << 30) -#define DWC2_HCCHAR_CHDIS_OFFSET 30 -#define DWC2_HCCHAR_CHEN (1 << 31) -#define DWC2_HCCHAR_CHEN_OFFSET 31 -#define DWC2_HCSPLT_PRTADDR_MASK (0x7F << 0) -#define DWC2_HCSPLT_PRTADDR_OFFSET 0 -#define DWC2_HCSPLT_HUBADDR_MASK (0x7F << 7) -#define DWC2_HCSPLT_HUBADDR_OFFSET 7 -#define DWC2_HCSPLT_XACTPOS_MASK (0x3 << 14) -#define DWC2_HCSPLT_XACTPOS_OFFSET 14 -#define DWC2_HCSPLT_COMPSPLT (1 << 16) -#define DWC2_HCSPLT_COMPSPLT_OFFSET 16 -#define DWC2_HCSPLT_SPLTENA (1 << 31) -#define DWC2_HCSPLT_SPLTENA_OFFSET 31 -#define DWC2_HCINT_XFERCOMP (1 << 0) -#define DWC2_HCINT_XFERCOMP_OFFSET 0 -#define DWC2_HCINT_CHHLTD (1 << 1) -#define DWC2_HCINT_CHHLTD_OFFSET 1 -#define DWC2_HCINT_AHBERR (1 << 2) -#define DWC2_HCINT_AHBERR_OFFSET 2 -#define DWC2_HCINT_STALL (1 << 3) -#define DWC2_HCINT_STALL_OFFSET 3 -#define DWC2_HCINT_NAK (1 << 4) -#define DWC2_HCINT_NAK_OFFSET 4 -#define DWC2_HCINT_ACK (1 << 5) -#define DWC2_HCINT_ACK_OFFSET 5 -#define DWC2_HCINT_NYET (1 << 6) -#define DWC2_HCINT_NYET_OFFSET 6 -#define DWC2_HCINT_XACTERR (1 << 7) -#define DWC2_HCINT_XACTERR_OFFSET 7 -#define DWC2_HCINT_BBLERR (1 << 8) -#define DWC2_HCINT_BBLERR_OFFSET 8 -#define DWC2_HCINT_FRMOVRUN (1 << 9) -#define DWC2_HCINT_FRMOVRUN_OFFSET 9 -#define DWC2_HCINT_DATATGLERR (1 << 10) -#define DWC2_HCINT_DATATGLERR_OFFSET 10 -#define DWC2_HCINT_BNA (1 << 11) -#define DWC2_HCINT_BNA_OFFSET 11 -#define DWC2_HCINT_XCS_XACT (1 << 12) -#define DWC2_HCINT_XCS_XACT_OFFSET 12 -#define DWC2_HCINT_FRM_LIST_ROLL (1 << 13) -#define DWC2_HCINT_FRM_LIST_ROLL_OFFSET 13 -#define DWC2_HCINTMSK_XFERCOMPL (1 << 0) -#define DWC2_HCINTMSK_XFERCOMPL_OFFSET 0 -#define DWC2_HCINTMSK_CHHLTD (1 << 1) -#define DWC2_HCINTMSK_CHHLTD_OFFSET 1 -#define DWC2_HCINTMSK_AHBERR (1 << 2) -#define DWC2_HCINTMSK_AHBERR_OFFSET 2 -#define DWC2_HCINTMSK_STALL (1 << 3) -#define DWC2_HCINTMSK_STALL_OFFSET 3 -#define DWC2_HCINTMSK_NAK (1 << 4) -#define DWC2_HCINTMSK_NAK_OFFSET 4 -#define DWC2_HCINTMSK_ACK (1 << 5) -#define DWC2_HCINTMSK_ACK_OFFSET 5 -#define DWC2_HCINTMSK_NYET (1 << 6) -#define DWC2_HCINTMSK_NYET_OFFSET 6 -#define DWC2_HCINTMSK_XACTERR (1 << 7) -#define DWC2_HCINTMSK_XACTERR_OFFSET 7 -#define DWC2_HCINTMSK_BBLERR (1 << 8) -#define DWC2_HCINTMSK_BBLERR_OFFSET 8 -#define DWC2_HCINTMSK_FRMOVRUN (1 << 9) -#define DWC2_HCINTMSK_FRMOVRUN_OFFSET 9 -#define DWC2_HCINTMSK_DATATGLERR (1 << 10) -#define DWC2_HCINTMSK_DATATGLERR_OFFSET 10 -#define DWC2_HCINTMSK_BNA (1 << 11) -#define DWC2_HCINTMSK_BNA_OFFSET 11 -#define DWC2_HCINTMSK_XCS_XACT (1 << 12) -#define DWC2_HCINTMSK_XCS_XACT_OFFSET 12 -#define DWC2_HCINTMSK_FRM_LIST_ROLL (1 << 13) -#define DWC2_HCINTMSK_FRM_LIST_ROLL_OFFSET 13 -#define DWC2_HCTSIZ_XFERSIZE_MASK 0x7ffff -#define DWC2_HCTSIZ_XFERSIZE_OFFSET 0 -#define DWC2_HCTSIZ_SCHINFO_MASK 0xff -#define DWC2_HCTSIZ_SCHINFO_OFFSET 0 -#define DWC2_HCTSIZ_NTD_MASK (0xff << 8) -#define DWC2_HCTSIZ_NTD_OFFSET 8 -#define DWC2_HCTSIZ_PKTCNT_MASK (0x3ff << 19) -#define DWC2_HCTSIZ_PKTCNT_OFFSET 19 -#define DWC2_HCTSIZ_PID_MASK (0x3 << 29) -#define DWC2_HCTSIZ_PID_OFFSET 29 -#define DWC2_HCTSIZ_DOPNG (1 << 31) -#define DWC2_HCTSIZ_DOPNG_OFFSET 31 -#define DWC2_HCDMA_CTD_MASK (0xFF << 3) -#define DWC2_HCDMA_CTD_OFFSET 3 -#define DWC2_HCDMA_DMA_ADDR_MASK (0x1FFFFF << 11) -#define DWC2_HCDMA_DMA_ADDR_OFFSET 11 -#define DWC2_PCGCCTL_STOPPCLK (1 << 0) -#define DWC2_PCGCCTL_STOPPCLK_OFFSET 0 -#define DWC2_PCGCCTL_GATEHCLK (1 << 1) -#define DWC2_PCGCCTL_GATEHCLK_OFFSET 1 -#define DWC2_PCGCCTL_PWRCLMP (1 << 2) -#define DWC2_PCGCCTL_PWRCLMP_OFFSET 2 -#define DWC2_PCGCCTL_RSTPDWNMODULE (1 << 3) -#define DWC2_PCGCCTL_RSTPDWNMODULE_OFFSET 3 -#define DWC2_PCGCCTL_PHYSUSPENDED (1 << 4) -#define DWC2_PCGCCTL_PHYSUSPENDED_OFFSET 4 -#define DWC2_PCGCCTL_ENBL_SLEEP_GATING (1 << 5) -#define DWC2_PCGCCTL_ENBL_SLEEP_GATING_OFFSET 5 -#define DWC2_PCGCCTL_PHY_IN_SLEEP (1 << 6) -#define DWC2_PCGCCTL_PHY_IN_SLEEP_OFFSET 6 -#define DWC2_PCGCCTL_DEEP_SLEEP (1 << 7) -#define DWC2_PCGCCTL_DEEP_SLEEP_OFFSET 7 -#define DWC2_SNPSID_DEVID_VER_2xx (0x4f542 << 12) -#define DWC2_SNPSID_DEVID_VER_3xx (0x4f543 << 12) -#define DWC2_SNPSID_DEVID_MASK (0xfffff << 12) -#define DWC2_SNPSID_DEVID_OFFSET 12 - -/* Host controller specific */ -#define DWC2_HC_PID_DATA0 0 -#define DWC2_HC_PID_DATA2 1 -#define DWC2_HC_PID_DATA1 2 -#define DWC2_HC_PID_MDATA 3 -#define DWC2_HC_PID_SETUP 3 - -/* roothub.a masks */ -#define RH_A_NDP (0xff << 0) /* number of downstream ports */ -#define RH_A_PSM (1 << 8) /* power switching mode */ -#define RH_A_NPS (1 << 9) /* no power switching */ -#define RH_A_DT (1 << 10) /* device type (mbz) */ -#define RH_A_OCPM (1 << 11) /* over current protection mode */ -#define RH_A_NOCP (1 << 12) /* no over current protection */ -#define RH_A_POTPGT (0xff << 24) /* power on to power good time */ - -/* roothub.b masks */ -#define RH_B_DR 0x0000ffff /* device removable flags */ -#define RH_B_PPCM 0xffff0000 /* port power control mask */ - -/* Default driver configuration */ -#define CONFIG_DWC2_MAX_CHANNELS 16 /* Max # of EPs */ -#define CONFIG_DWC2_HOST_RX_FIFO_SIZE (516 + CONFIG_DWC2_MAX_CHANNELS) -#define CONFIG_DWC2_HOST_NPERIO_TX_FIFO_SIZE 0x100 /* nPeriodic TX FIFO */ -#define CONFIG_DWC2_HOST_PERIO_TX_FIFO_SIZE 0x200 /* Periodic TX FIFO */ -#define CONFIG_DWC2_MAX_TRANSFER_SIZE 65535 -#define CONFIG_DWC2_MAX_PACKET_COUNT 511 - -#define DWC2_PHY_TYPE_FS 0 -#define DWC2_PHY_TYPE_UTMI 1 -#define DWC2_PHY_TYPE_ULPI 2 -#define CONFIG_DWC2_PHY_TYPE DWC2_PHY_TYPE_UTMI /* PHY type */ -#define CONFIG_DWC2_UTMI_WIDTH 8 /* UTMI bus width (8/16) */ - -#define CONFIG_DWC2_PHY_ULPI_EXT_VBUS /* ULPI PHY controls VBUS */ -#define CONFIG_DWC2_TX_THR_LENGTH 64 - -#endif /* __DWC2_H__ */ |