16e9e0626SOleksandr Tymoshenko /* 26e9e0626SOleksandr Tymoshenko * Copyright (C) 2012 Oleksandr Tymoshenko <gonzo@freebsd.org> 36e9e0626SOleksandr Tymoshenko * Copyright (C) 2014 Marek Vasut <marex@denx.de> 46e9e0626SOleksandr Tymoshenko * 56e9e0626SOleksandr Tymoshenko * SPDX-License-Identifier: GPL-2.0+ 66e9e0626SOleksandr Tymoshenko */ 76e9e0626SOleksandr Tymoshenko 86e9e0626SOleksandr Tymoshenko #include <common.h> 9f58a41e0SSimon Glass #include <dm.h> 106e9e0626SOleksandr Tymoshenko #include <errno.h> 116e9e0626SOleksandr Tymoshenko #include <usb.h> 126e9e0626SOleksandr Tymoshenko #include <malloc.h> 13cf92e05cSSimon Glass #include <memalign.h> 145c0beb5cSStephen Warren #include <phys2bus.h> 156e9e0626SOleksandr Tymoshenko #include <usbroothubdes.h> 166e9e0626SOleksandr Tymoshenko #include <asm/io.h> 176e9e0626SOleksandr Tymoshenko 186e9e0626SOleksandr Tymoshenko #include "dwc2.h" 196e9e0626SOleksandr Tymoshenko 206e9e0626SOleksandr Tymoshenko /* Use only HC channel 0. */ 216e9e0626SOleksandr Tymoshenko #define DWC2_HC_CHANNEL 0 226e9e0626SOleksandr Tymoshenko 236e9e0626SOleksandr Tymoshenko #define DWC2_STATUS_BUF_SIZE 64 246e9e0626SOleksandr Tymoshenko #define DWC2_DATA_BUF_SIZE (64 * 1024) 256e9e0626SOleksandr Tymoshenko 266e9e0626SOleksandr Tymoshenko #define MAX_DEVICE 16 276e9e0626SOleksandr Tymoshenko #define MAX_ENDPOINT 16 286e9e0626SOleksandr Tymoshenko 29cc3e3a9eSSimon Glass struct dwc2_priv { 30f58a41e0SSimon Glass #ifdef CONFIG_DM_USB 31db402e00SAlexander Stein uint8_t aligned_buffer[DWC2_DATA_BUF_SIZE] __aligned(ARCH_DMA_MINALIGN); 32db402e00SAlexander Stein uint8_t status_buffer[DWC2_STATUS_BUF_SIZE] __aligned(ARCH_DMA_MINALIGN); 33f58a41e0SSimon Glass #else 34cc3e3a9eSSimon Glass uint8_t *aligned_buffer; 35cc3e3a9eSSimon Glass uint8_t *status_buffer; 36f58a41e0SSimon Glass #endif 37cc3e3a9eSSimon Glass int bulk_data_toggle[MAX_DEVICE][MAX_ENDPOINT]; 38cc3e3a9eSSimon Glass struct dwc2_core_regs *regs; 39cc3e3a9eSSimon Glass int root_hub_devnum; 40cc3e3a9eSSimon Glass }; 416e9e0626SOleksandr Tymoshenko 42f58a41e0SSimon Glass #ifndef CONFIG_DM_USB 43db402e00SAlexander Stein /* We need cacheline-aligned buffers for DMA transfers and dcache support */ 44db402e00SAlexander Stein DEFINE_ALIGN_BUFFER(uint8_t, aligned_buffer_addr, DWC2_DATA_BUF_SIZE, 45db402e00SAlexander Stein ARCH_DMA_MINALIGN); 46db402e00SAlexander Stein DEFINE_ALIGN_BUFFER(uint8_t, status_buffer_addr, DWC2_STATUS_BUF_SIZE, 47db402e00SAlexander Stein ARCH_DMA_MINALIGN); 48cc3e3a9eSSimon Glass 49cc3e3a9eSSimon Glass static struct dwc2_priv local; 50f58a41e0SSimon Glass #endif 516e9e0626SOleksandr Tymoshenko 526e9e0626SOleksandr Tymoshenko /* 536e9e0626SOleksandr Tymoshenko * DWC2 IP interface 546e9e0626SOleksandr Tymoshenko */ 556e9e0626SOleksandr Tymoshenko static int wait_for_bit(void *reg, const uint32_t mask, bool set) 566e9e0626SOleksandr Tymoshenko { 576e9e0626SOleksandr Tymoshenko unsigned int timeout = 1000000; 586e9e0626SOleksandr Tymoshenko uint32_t val; 596e9e0626SOleksandr Tymoshenko 606e9e0626SOleksandr Tymoshenko while (--timeout) { 616e9e0626SOleksandr Tymoshenko val = readl(reg); 626e9e0626SOleksandr Tymoshenko if (!set) 636e9e0626SOleksandr Tymoshenko val = ~val; 646e9e0626SOleksandr Tymoshenko 656e9e0626SOleksandr Tymoshenko if ((val & mask) == mask) 666e9e0626SOleksandr Tymoshenko return 0; 676e9e0626SOleksandr Tymoshenko 686e9e0626SOleksandr Tymoshenko udelay(1); 696e9e0626SOleksandr Tymoshenko } 706e9e0626SOleksandr Tymoshenko 716e9e0626SOleksandr Tymoshenko debug("%s: Timeout (reg=%p mask=%08x wait_set=%i)\n", 726e9e0626SOleksandr Tymoshenko __func__, reg, mask, set); 736e9e0626SOleksandr Tymoshenko 746e9e0626SOleksandr Tymoshenko return -ETIMEDOUT; 756e9e0626SOleksandr Tymoshenko } 766e9e0626SOleksandr Tymoshenko 776e9e0626SOleksandr Tymoshenko /* 786e9e0626SOleksandr Tymoshenko * Initializes the FSLSPClkSel field of the HCFG register 796e9e0626SOleksandr Tymoshenko * depending on the PHY type. 806e9e0626SOleksandr Tymoshenko */ 816e9e0626SOleksandr Tymoshenko static void init_fslspclksel(struct dwc2_core_regs *regs) 826e9e0626SOleksandr Tymoshenko { 836e9e0626SOleksandr Tymoshenko uint32_t phyclk; 846e9e0626SOleksandr Tymoshenko 856e9e0626SOleksandr Tymoshenko #if (CONFIG_DWC2_PHY_TYPE == DWC2_PHY_TYPE_FS) 866e9e0626SOleksandr Tymoshenko phyclk = DWC2_HCFG_FSLSPCLKSEL_48_MHZ; /* Full speed PHY */ 876e9e0626SOleksandr Tymoshenko #else 886e9e0626SOleksandr Tymoshenko /* High speed PHY running at full speed or high speed */ 896e9e0626SOleksandr Tymoshenko phyclk = DWC2_HCFG_FSLSPCLKSEL_30_60_MHZ; 906e9e0626SOleksandr Tymoshenko #endif 916e9e0626SOleksandr Tymoshenko 926e9e0626SOleksandr Tymoshenko #ifdef CONFIG_DWC2_ULPI_FS_LS 936e9e0626SOleksandr Tymoshenko uint32_t hwcfg2 = readl(®s->ghwcfg2); 946e9e0626SOleksandr Tymoshenko uint32_t hval = (ghwcfg2 & DWC2_HWCFG2_HS_PHY_TYPE_MASK) >> 956e9e0626SOleksandr Tymoshenko DWC2_HWCFG2_HS_PHY_TYPE_OFFSET; 966e9e0626SOleksandr Tymoshenko uint32_t fval = (ghwcfg2 & DWC2_HWCFG2_FS_PHY_TYPE_MASK) >> 976e9e0626SOleksandr Tymoshenko DWC2_HWCFG2_FS_PHY_TYPE_OFFSET; 986e9e0626SOleksandr Tymoshenko 996e9e0626SOleksandr Tymoshenko if (hval == 2 && fval == 1) 1006e9e0626SOleksandr Tymoshenko phyclk = DWC2_HCFG_FSLSPCLKSEL_48_MHZ; /* Full speed PHY */ 1016e9e0626SOleksandr Tymoshenko #endif 1026e9e0626SOleksandr Tymoshenko 1036e9e0626SOleksandr Tymoshenko clrsetbits_le32(®s->host_regs.hcfg, 1046e9e0626SOleksandr Tymoshenko DWC2_HCFG_FSLSPCLKSEL_MASK, 1056e9e0626SOleksandr Tymoshenko phyclk << DWC2_HCFG_FSLSPCLKSEL_OFFSET); 1066e9e0626SOleksandr Tymoshenko } 1076e9e0626SOleksandr Tymoshenko 1086e9e0626SOleksandr Tymoshenko /* 1096e9e0626SOleksandr Tymoshenko * Flush a Tx FIFO. 1106e9e0626SOleksandr Tymoshenko * 1116e9e0626SOleksandr Tymoshenko * @param regs Programming view of DWC_otg controller. 1126e9e0626SOleksandr Tymoshenko * @param num Tx FIFO to flush. 1136e9e0626SOleksandr Tymoshenko */ 1146e9e0626SOleksandr Tymoshenko static void dwc_otg_flush_tx_fifo(struct dwc2_core_regs *regs, const int num) 1156e9e0626SOleksandr Tymoshenko { 1166e9e0626SOleksandr Tymoshenko int ret; 1176e9e0626SOleksandr Tymoshenko 1186e9e0626SOleksandr Tymoshenko writel(DWC2_GRSTCTL_TXFFLSH | (num << DWC2_GRSTCTL_TXFNUM_OFFSET), 1196e9e0626SOleksandr Tymoshenko ®s->grstctl); 1206e9e0626SOleksandr Tymoshenko ret = wait_for_bit(®s->grstctl, DWC2_GRSTCTL_TXFFLSH, 0); 1216e9e0626SOleksandr Tymoshenko if (ret) 1226e9e0626SOleksandr Tymoshenko printf("%s: Timeout!\n", __func__); 1236e9e0626SOleksandr Tymoshenko 1246e9e0626SOleksandr Tymoshenko /* Wait for 3 PHY Clocks */ 1256e9e0626SOleksandr Tymoshenko udelay(1); 1266e9e0626SOleksandr Tymoshenko } 1276e9e0626SOleksandr Tymoshenko 1286e9e0626SOleksandr Tymoshenko /* 1296e9e0626SOleksandr Tymoshenko * Flush Rx FIFO. 1306e9e0626SOleksandr Tymoshenko * 1316e9e0626SOleksandr Tymoshenko * @param regs Programming view of DWC_otg controller. 1326e9e0626SOleksandr Tymoshenko */ 1336e9e0626SOleksandr Tymoshenko static void dwc_otg_flush_rx_fifo(struct dwc2_core_regs *regs) 1346e9e0626SOleksandr Tymoshenko { 1356e9e0626SOleksandr Tymoshenko int ret; 1366e9e0626SOleksandr Tymoshenko 1376e9e0626SOleksandr Tymoshenko writel(DWC2_GRSTCTL_RXFFLSH, ®s->grstctl); 1386e9e0626SOleksandr Tymoshenko ret = wait_for_bit(®s->grstctl, DWC2_GRSTCTL_RXFFLSH, 0); 1396e9e0626SOleksandr Tymoshenko if (ret) 1406e9e0626SOleksandr Tymoshenko printf("%s: Timeout!\n", __func__); 1416e9e0626SOleksandr Tymoshenko 1426e9e0626SOleksandr Tymoshenko /* Wait for 3 PHY Clocks */ 1436e9e0626SOleksandr Tymoshenko udelay(1); 1446e9e0626SOleksandr Tymoshenko } 1456e9e0626SOleksandr Tymoshenko 1466e9e0626SOleksandr Tymoshenko /* 1476e9e0626SOleksandr Tymoshenko * Do core a soft reset of the core. Be careful with this because it 1486e9e0626SOleksandr Tymoshenko * resets all the internal state machines of the core. 1496e9e0626SOleksandr Tymoshenko */ 1506e9e0626SOleksandr Tymoshenko static void dwc_otg_core_reset(struct dwc2_core_regs *regs) 1516e9e0626SOleksandr Tymoshenko { 1526e9e0626SOleksandr Tymoshenko int ret; 1536e9e0626SOleksandr Tymoshenko 1546e9e0626SOleksandr Tymoshenko /* Wait for AHB master IDLE state. */ 1556e9e0626SOleksandr Tymoshenko ret = wait_for_bit(®s->grstctl, DWC2_GRSTCTL_AHBIDLE, 1); 1566e9e0626SOleksandr Tymoshenko if (ret) 1576e9e0626SOleksandr Tymoshenko printf("%s: Timeout!\n", __func__); 1586e9e0626SOleksandr Tymoshenko 1596e9e0626SOleksandr Tymoshenko /* Core Soft Reset */ 1606e9e0626SOleksandr Tymoshenko writel(DWC2_GRSTCTL_CSFTRST, ®s->grstctl); 1616e9e0626SOleksandr Tymoshenko ret = wait_for_bit(®s->grstctl, DWC2_GRSTCTL_CSFTRST, 0); 1626e9e0626SOleksandr Tymoshenko if (ret) 1636e9e0626SOleksandr Tymoshenko printf("%s: Timeout!\n", __func__); 1646e9e0626SOleksandr Tymoshenko 1656e9e0626SOleksandr Tymoshenko /* 1666e9e0626SOleksandr Tymoshenko * Wait for core to come out of reset. 1676e9e0626SOleksandr Tymoshenko * NOTE: This long sleep is _very_ important, otherwise the core will 1686e9e0626SOleksandr Tymoshenko * not stay in host mode after a connector ID change! 1696e9e0626SOleksandr Tymoshenko */ 1706e9e0626SOleksandr Tymoshenko mdelay(100); 1716e9e0626SOleksandr Tymoshenko } 1726e9e0626SOleksandr Tymoshenko 1736e9e0626SOleksandr Tymoshenko /* 1746e9e0626SOleksandr Tymoshenko * This function initializes the DWC_otg controller registers for 1756e9e0626SOleksandr Tymoshenko * host mode. 1766e9e0626SOleksandr Tymoshenko * 1776e9e0626SOleksandr Tymoshenko * This function flushes the Tx and Rx FIFOs and it flushes any entries in the 1786e9e0626SOleksandr Tymoshenko * request queues. Host channels are reset to ensure that they are ready for 1796e9e0626SOleksandr Tymoshenko * performing transfers. 1806e9e0626SOleksandr Tymoshenko * 1816e9e0626SOleksandr Tymoshenko * @param regs Programming view of DWC_otg controller 1826e9e0626SOleksandr Tymoshenko * 1836e9e0626SOleksandr Tymoshenko */ 1846e9e0626SOleksandr Tymoshenko static void dwc_otg_core_host_init(struct dwc2_core_regs *regs) 1856e9e0626SOleksandr Tymoshenko { 1866e9e0626SOleksandr Tymoshenko uint32_t nptxfifosize = 0; 1876e9e0626SOleksandr Tymoshenko uint32_t ptxfifosize = 0; 1886e9e0626SOleksandr Tymoshenko uint32_t hprt0 = 0; 1896e9e0626SOleksandr Tymoshenko int i, ret, num_channels; 1906e9e0626SOleksandr Tymoshenko 1916e9e0626SOleksandr Tymoshenko /* Restart the Phy Clock */ 1926e9e0626SOleksandr Tymoshenko writel(0, ®s->pcgcctl); 1936e9e0626SOleksandr Tymoshenko 1946e9e0626SOleksandr Tymoshenko /* Initialize Host Configuration Register */ 1956e9e0626SOleksandr Tymoshenko init_fslspclksel(regs); 1966e9e0626SOleksandr Tymoshenko #ifdef CONFIG_DWC2_DFLT_SPEED_FULL 1976e9e0626SOleksandr Tymoshenko setbits_le32(®s->host_regs.hcfg, DWC2_HCFG_FSLSSUPP); 1986e9e0626SOleksandr Tymoshenko #endif 1996e9e0626SOleksandr Tymoshenko 2006e9e0626SOleksandr Tymoshenko /* Configure data FIFO sizes */ 2016e9e0626SOleksandr Tymoshenko #ifdef CONFIG_DWC2_ENABLE_DYNAMIC_FIFO 2026e9e0626SOleksandr Tymoshenko if (readl(®s->ghwcfg2) & DWC2_HWCFG2_DYNAMIC_FIFO) { 2036e9e0626SOleksandr Tymoshenko /* Rx FIFO */ 2046e9e0626SOleksandr Tymoshenko writel(CONFIG_DWC2_HOST_RX_FIFO_SIZE, ®s->grxfsiz); 2056e9e0626SOleksandr Tymoshenko 2066e9e0626SOleksandr Tymoshenko /* Non-periodic Tx FIFO */ 2076e9e0626SOleksandr Tymoshenko nptxfifosize |= CONFIG_DWC2_HOST_NPERIO_TX_FIFO_SIZE << 2086e9e0626SOleksandr Tymoshenko DWC2_FIFOSIZE_DEPTH_OFFSET; 2096e9e0626SOleksandr Tymoshenko nptxfifosize |= CONFIG_DWC2_HOST_RX_FIFO_SIZE << 2106e9e0626SOleksandr Tymoshenko DWC2_FIFOSIZE_STARTADDR_OFFSET; 2116e9e0626SOleksandr Tymoshenko writel(nptxfifosize, ®s->gnptxfsiz); 2126e9e0626SOleksandr Tymoshenko 2136e9e0626SOleksandr Tymoshenko /* Periodic Tx FIFO */ 2146e9e0626SOleksandr Tymoshenko ptxfifosize |= CONFIG_DWC2_HOST_PERIO_TX_FIFO_SIZE << 2156e9e0626SOleksandr Tymoshenko DWC2_FIFOSIZE_DEPTH_OFFSET; 2166e9e0626SOleksandr Tymoshenko ptxfifosize |= (CONFIG_DWC2_HOST_RX_FIFO_SIZE + 2176e9e0626SOleksandr Tymoshenko CONFIG_DWC2_HOST_NPERIO_TX_FIFO_SIZE) << 2186e9e0626SOleksandr Tymoshenko DWC2_FIFOSIZE_STARTADDR_OFFSET; 2196e9e0626SOleksandr Tymoshenko writel(ptxfifosize, ®s->hptxfsiz); 2206e9e0626SOleksandr Tymoshenko } 2216e9e0626SOleksandr Tymoshenko #endif 2226e9e0626SOleksandr Tymoshenko 2236e9e0626SOleksandr Tymoshenko /* Clear Host Set HNP Enable in the OTG Control Register */ 2246e9e0626SOleksandr Tymoshenko clrbits_le32(®s->gotgctl, DWC2_GOTGCTL_HSTSETHNPEN); 2256e9e0626SOleksandr Tymoshenko 2266e9e0626SOleksandr Tymoshenko /* Make sure the FIFOs are flushed. */ 2276e9e0626SOleksandr Tymoshenko dwc_otg_flush_tx_fifo(regs, 0x10); /* All Tx FIFOs */ 2286e9e0626SOleksandr Tymoshenko dwc_otg_flush_rx_fifo(regs); 2296e9e0626SOleksandr Tymoshenko 2306e9e0626SOleksandr Tymoshenko /* Flush out any leftover queued requests. */ 2316e9e0626SOleksandr Tymoshenko num_channels = readl(®s->ghwcfg2); 2326e9e0626SOleksandr Tymoshenko num_channels &= DWC2_HWCFG2_NUM_HOST_CHAN_MASK; 2336e9e0626SOleksandr Tymoshenko num_channels >>= DWC2_HWCFG2_NUM_HOST_CHAN_OFFSET; 2346e9e0626SOleksandr Tymoshenko num_channels += 1; 2356e9e0626SOleksandr Tymoshenko 2366e9e0626SOleksandr Tymoshenko for (i = 0; i < num_channels; i++) 2376e9e0626SOleksandr Tymoshenko clrsetbits_le32(®s->hc_regs[i].hcchar, 2386e9e0626SOleksandr Tymoshenko DWC2_HCCHAR_CHEN | DWC2_HCCHAR_EPDIR, 2396e9e0626SOleksandr Tymoshenko DWC2_HCCHAR_CHDIS); 2406e9e0626SOleksandr Tymoshenko 2416e9e0626SOleksandr Tymoshenko /* Halt all channels to put them into a known state. */ 2426e9e0626SOleksandr Tymoshenko for (i = 0; i < num_channels; i++) { 2436e9e0626SOleksandr Tymoshenko clrsetbits_le32(®s->hc_regs[i].hcchar, 2446e9e0626SOleksandr Tymoshenko DWC2_HCCHAR_EPDIR, 2456e9e0626SOleksandr Tymoshenko DWC2_HCCHAR_CHEN | DWC2_HCCHAR_CHDIS); 2466e9e0626SOleksandr Tymoshenko ret = wait_for_bit(®s->hc_regs[i].hcchar, 2476e9e0626SOleksandr Tymoshenko DWC2_HCCHAR_CHEN, 0); 2486e9e0626SOleksandr Tymoshenko if (ret) 2496e9e0626SOleksandr Tymoshenko printf("%s: Timeout!\n", __func__); 2506e9e0626SOleksandr Tymoshenko } 2516e9e0626SOleksandr Tymoshenko 2526e9e0626SOleksandr Tymoshenko /* Turn on the vbus power. */ 2536e9e0626SOleksandr Tymoshenko if (readl(®s->gintsts) & DWC2_GINTSTS_CURMODE_HOST) { 2546e9e0626SOleksandr Tymoshenko hprt0 = readl(®s->hprt0); 2556e9e0626SOleksandr Tymoshenko hprt0 &= ~(DWC2_HPRT0_PRTENA | DWC2_HPRT0_PRTCONNDET); 2566e9e0626SOleksandr Tymoshenko hprt0 &= ~(DWC2_HPRT0_PRTENCHNG | DWC2_HPRT0_PRTOVRCURRCHNG); 2576e9e0626SOleksandr Tymoshenko if (!(hprt0 & DWC2_HPRT0_PRTPWR)) { 2586e9e0626SOleksandr Tymoshenko hprt0 |= DWC2_HPRT0_PRTPWR; 2596e9e0626SOleksandr Tymoshenko writel(hprt0, ®s->hprt0); 2606e9e0626SOleksandr Tymoshenko } 2616e9e0626SOleksandr Tymoshenko } 2626e9e0626SOleksandr Tymoshenko } 2636e9e0626SOleksandr Tymoshenko 2646e9e0626SOleksandr Tymoshenko /* 2656e9e0626SOleksandr Tymoshenko * This function initializes the DWC_otg controller registers and 2666e9e0626SOleksandr Tymoshenko * prepares the core for device mode or host mode operation. 2676e9e0626SOleksandr Tymoshenko * 2686e9e0626SOleksandr Tymoshenko * @param regs Programming view of the DWC_otg controller 2696e9e0626SOleksandr Tymoshenko */ 2706e9e0626SOleksandr Tymoshenko static void dwc_otg_core_init(struct dwc2_core_regs *regs) 2716e9e0626SOleksandr Tymoshenko { 2726e9e0626SOleksandr Tymoshenko uint32_t ahbcfg = 0; 2736e9e0626SOleksandr Tymoshenko uint32_t usbcfg = 0; 2746e9e0626SOleksandr Tymoshenko uint8_t brst_sz = CONFIG_DWC2_DMA_BURST_SIZE; 2756e9e0626SOleksandr Tymoshenko 2766e9e0626SOleksandr Tymoshenko /* Common Initialization */ 2776e9e0626SOleksandr Tymoshenko usbcfg = readl(®s->gusbcfg); 2786e9e0626SOleksandr Tymoshenko 2796e9e0626SOleksandr Tymoshenko /* Program the ULPI External VBUS bit if needed */ 2806e9e0626SOleksandr Tymoshenko #ifdef CONFIG_DWC2_PHY_ULPI_EXT_VBUS 2816e9e0626SOleksandr Tymoshenko usbcfg |= DWC2_GUSBCFG_ULPI_EXT_VBUS_DRV; 2826e9e0626SOleksandr Tymoshenko #else 2836e9e0626SOleksandr Tymoshenko usbcfg &= ~DWC2_GUSBCFG_ULPI_EXT_VBUS_DRV; 2846e9e0626SOleksandr Tymoshenko #endif 2856e9e0626SOleksandr Tymoshenko 2866e9e0626SOleksandr Tymoshenko /* Set external TS Dline pulsing */ 2876e9e0626SOleksandr Tymoshenko #ifdef CONFIG_DWC2_TS_DLINE 2886e9e0626SOleksandr Tymoshenko usbcfg |= DWC2_GUSBCFG_TERM_SEL_DL_PULSE; 2896e9e0626SOleksandr Tymoshenko #else 2906e9e0626SOleksandr Tymoshenko usbcfg &= ~DWC2_GUSBCFG_TERM_SEL_DL_PULSE; 2916e9e0626SOleksandr Tymoshenko #endif 2926e9e0626SOleksandr Tymoshenko writel(usbcfg, ®s->gusbcfg); 2936e9e0626SOleksandr Tymoshenko 2946e9e0626SOleksandr Tymoshenko /* Reset the Controller */ 2956e9e0626SOleksandr Tymoshenko dwc_otg_core_reset(regs); 2966e9e0626SOleksandr Tymoshenko 2976e9e0626SOleksandr Tymoshenko /* 2986e9e0626SOleksandr Tymoshenko * This programming sequence needs to happen in FS mode before 2996e9e0626SOleksandr Tymoshenko * any other programming occurs 3006e9e0626SOleksandr Tymoshenko */ 3016e9e0626SOleksandr Tymoshenko #if defined(CONFIG_DWC2_DFLT_SPEED_FULL) && \ 3026e9e0626SOleksandr Tymoshenko (CONFIG_DWC2_PHY_TYPE == DWC2_PHY_TYPE_FS) 3036e9e0626SOleksandr Tymoshenko /* If FS mode with FS PHY */ 3046e9e0626SOleksandr Tymoshenko setbits_le32(®s->gusbcfg, DWC2_GUSBCFG_PHYSEL); 3056e9e0626SOleksandr Tymoshenko 3066e9e0626SOleksandr Tymoshenko /* Reset after a PHY select */ 3076e9e0626SOleksandr Tymoshenko dwc_otg_core_reset(regs); 3086e9e0626SOleksandr Tymoshenko 3096e9e0626SOleksandr Tymoshenko /* 3106e9e0626SOleksandr Tymoshenko * Program DCFG.DevSpd or HCFG.FSLSPclkSel to 48Mhz in FS. 3116e9e0626SOleksandr Tymoshenko * Also do this on HNP Dev/Host mode switches (done in dev_init 3126e9e0626SOleksandr Tymoshenko * and host_init). 3136e9e0626SOleksandr Tymoshenko */ 3146e9e0626SOleksandr Tymoshenko if (readl(®s->gintsts) & DWC2_GINTSTS_CURMODE_HOST) 3156e9e0626SOleksandr Tymoshenko init_fslspclksel(regs); 3166e9e0626SOleksandr Tymoshenko 3176e9e0626SOleksandr Tymoshenko #ifdef CONFIG_DWC2_I2C_ENABLE 3186e9e0626SOleksandr Tymoshenko /* Program GUSBCFG.OtgUtmifsSel to I2C */ 3196e9e0626SOleksandr Tymoshenko setbits_le32(®s->gusbcfg, DWC2_GUSBCFG_OTGUTMIFSSEL); 3206e9e0626SOleksandr Tymoshenko 3216e9e0626SOleksandr Tymoshenko /* Program GI2CCTL.I2CEn */ 3226e9e0626SOleksandr Tymoshenko clrsetbits_le32(®s->gi2cctl, DWC2_GI2CCTL_I2CEN | 3236e9e0626SOleksandr Tymoshenko DWC2_GI2CCTL_I2CDEVADDR_MASK, 3246e9e0626SOleksandr Tymoshenko 1 << DWC2_GI2CCTL_I2CDEVADDR_OFFSET); 3256e9e0626SOleksandr Tymoshenko setbits_le32(®s->gi2cctl, DWC2_GI2CCTL_I2CEN); 3266e9e0626SOleksandr Tymoshenko #endif 3276e9e0626SOleksandr Tymoshenko 3286e9e0626SOleksandr Tymoshenko #else 3296e9e0626SOleksandr Tymoshenko /* High speed PHY. */ 3306e9e0626SOleksandr Tymoshenko 3316e9e0626SOleksandr Tymoshenko /* 3326e9e0626SOleksandr Tymoshenko * HS PHY parameters. These parameters are preserved during 3336e9e0626SOleksandr Tymoshenko * soft reset so only program the first time. Do a soft reset 3346e9e0626SOleksandr Tymoshenko * immediately after setting phyif. 3356e9e0626SOleksandr Tymoshenko */ 3366e9e0626SOleksandr Tymoshenko usbcfg &= ~(DWC2_GUSBCFG_ULPI_UTMI_SEL | DWC2_GUSBCFG_PHYIF); 3376e9e0626SOleksandr Tymoshenko usbcfg |= CONFIG_DWC2_PHY_TYPE << DWC2_GUSBCFG_ULPI_UTMI_SEL_OFFSET; 3386e9e0626SOleksandr Tymoshenko 3396e9e0626SOleksandr Tymoshenko if (usbcfg & DWC2_GUSBCFG_ULPI_UTMI_SEL) { /* ULPI interface */ 3406e9e0626SOleksandr Tymoshenko #ifdef CONFIG_DWC2_PHY_ULPI_DDR 3416e9e0626SOleksandr Tymoshenko usbcfg |= DWC2_GUSBCFG_DDRSEL; 3426e9e0626SOleksandr Tymoshenko #else 3436e9e0626SOleksandr Tymoshenko usbcfg &= ~DWC2_GUSBCFG_DDRSEL; 3446e9e0626SOleksandr Tymoshenko #endif 3456e9e0626SOleksandr Tymoshenko } else { /* UTMI+ interface */ 3466e9e0626SOleksandr Tymoshenko #if (CONFIG_DWC2_UTMI_PHY_WIDTH == 16) 3476e9e0626SOleksandr Tymoshenko usbcfg |= DWC2_GUSBCFG_PHYIF; 3486e9e0626SOleksandr Tymoshenko #endif 3496e9e0626SOleksandr Tymoshenko } 3506e9e0626SOleksandr Tymoshenko 3516e9e0626SOleksandr Tymoshenko writel(usbcfg, ®s->gusbcfg); 3526e9e0626SOleksandr Tymoshenko 3536e9e0626SOleksandr Tymoshenko /* Reset after setting the PHY parameters */ 3546e9e0626SOleksandr Tymoshenko dwc_otg_core_reset(regs); 3556e9e0626SOleksandr Tymoshenko #endif 3566e9e0626SOleksandr Tymoshenko 3576e9e0626SOleksandr Tymoshenko usbcfg = readl(®s->gusbcfg); 3586e9e0626SOleksandr Tymoshenko usbcfg &= ~(DWC2_GUSBCFG_ULPI_FSLS | DWC2_GUSBCFG_ULPI_CLK_SUS_M); 3596e9e0626SOleksandr Tymoshenko #ifdef CONFIG_DWC2_ULPI_FS_LS 3606e9e0626SOleksandr Tymoshenko uint32_t hwcfg2 = readl(®s->ghwcfg2); 3616e9e0626SOleksandr Tymoshenko uint32_t hval = (ghwcfg2 & DWC2_HWCFG2_HS_PHY_TYPE_MASK) >> 3626e9e0626SOleksandr Tymoshenko DWC2_HWCFG2_HS_PHY_TYPE_OFFSET; 3636e9e0626SOleksandr Tymoshenko uint32_t fval = (ghwcfg2 & DWC2_HWCFG2_FS_PHY_TYPE_MASK) >> 3646e9e0626SOleksandr Tymoshenko DWC2_HWCFG2_FS_PHY_TYPE_OFFSET; 3656e9e0626SOleksandr Tymoshenko if (hval == 2 && fval == 1) { 3666e9e0626SOleksandr Tymoshenko usbcfg |= DWC2_GUSBCFG_ULPI_FSLS; 3676e9e0626SOleksandr Tymoshenko usbcfg |= DWC2_GUSBCFG_ULPI_CLK_SUS_M; 3686e9e0626SOleksandr Tymoshenko } 3696e9e0626SOleksandr Tymoshenko #endif 3706e9e0626SOleksandr Tymoshenko writel(usbcfg, ®s->gusbcfg); 3716e9e0626SOleksandr Tymoshenko 3726e9e0626SOleksandr Tymoshenko /* Program the GAHBCFG Register. */ 3736e9e0626SOleksandr Tymoshenko switch (readl(®s->ghwcfg2) & DWC2_HWCFG2_ARCHITECTURE_MASK) { 3746e9e0626SOleksandr Tymoshenko case DWC2_HWCFG2_ARCHITECTURE_SLAVE_ONLY: 3756e9e0626SOleksandr Tymoshenko break; 3766e9e0626SOleksandr Tymoshenko case DWC2_HWCFG2_ARCHITECTURE_EXT_DMA: 3776e9e0626SOleksandr Tymoshenko while (brst_sz > 1) { 3786e9e0626SOleksandr Tymoshenko ahbcfg |= ahbcfg + (1 << DWC2_GAHBCFG_HBURSTLEN_OFFSET); 3796e9e0626SOleksandr Tymoshenko ahbcfg &= DWC2_GAHBCFG_HBURSTLEN_MASK; 3806e9e0626SOleksandr Tymoshenko brst_sz >>= 1; 3816e9e0626SOleksandr Tymoshenko } 3826e9e0626SOleksandr Tymoshenko 3836e9e0626SOleksandr Tymoshenko #ifdef CONFIG_DWC2_DMA_ENABLE 3846e9e0626SOleksandr Tymoshenko ahbcfg |= DWC2_GAHBCFG_DMAENABLE; 3856e9e0626SOleksandr Tymoshenko #endif 3866e9e0626SOleksandr Tymoshenko break; 3876e9e0626SOleksandr Tymoshenko 3886e9e0626SOleksandr Tymoshenko case DWC2_HWCFG2_ARCHITECTURE_INT_DMA: 3896e9e0626SOleksandr Tymoshenko ahbcfg |= DWC2_GAHBCFG_HBURSTLEN_INCR4; 3906e9e0626SOleksandr Tymoshenko #ifdef CONFIG_DWC2_DMA_ENABLE 3916e9e0626SOleksandr Tymoshenko ahbcfg |= DWC2_GAHBCFG_DMAENABLE; 3926e9e0626SOleksandr Tymoshenko #endif 3936e9e0626SOleksandr Tymoshenko break; 3946e9e0626SOleksandr Tymoshenko } 3956e9e0626SOleksandr Tymoshenko 3966e9e0626SOleksandr Tymoshenko writel(ahbcfg, ®s->gahbcfg); 3976e9e0626SOleksandr Tymoshenko 3986e9e0626SOleksandr Tymoshenko /* Program the GUSBCFG register for HNP/SRP. */ 3996e9e0626SOleksandr Tymoshenko setbits_le32(®s->gusbcfg, DWC2_GUSBCFG_HNPCAP | DWC2_GUSBCFG_SRPCAP); 4006e9e0626SOleksandr Tymoshenko 4016e9e0626SOleksandr Tymoshenko #ifdef CONFIG_DWC2_IC_USB_CAP 4026e9e0626SOleksandr Tymoshenko setbits_le32(®s->gusbcfg, DWC2_GUSBCFG_IC_USB_CAP); 4036e9e0626SOleksandr Tymoshenko #endif 4046e9e0626SOleksandr Tymoshenko } 4056e9e0626SOleksandr Tymoshenko 4066e9e0626SOleksandr Tymoshenko /* 4076e9e0626SOleksandr Tymoshenko * Prepares a host channel for transferring packets to/from a specific 4086e9e0626SOleksandr Tymoshenko * endpoint. The HCCHARn register is set up with the characteristics specified 4096e9e0626SOleksandr Tymoshenko * in _hc. Host channel interrupts that may need to be serviced while this 4106e9e0626SOleksandr Tymoshenko * transfer is in progress are enabled. 4116e9e0626SOleksandr Tymoshenko * 4126e9e0626SOleksandr Tymoshenko * @param regs Programming view of DWC_otg controller 4136e9e0626SOleksandr Tymoshenko * @param hc Information needed to initialize the host channel 4146e9e0626SOleksandr Tymoshenko */ 4156e9e0626SOleksandr Tymoshenko static void dwc_otg_hc_init(struct dwc2_core_regs *regs, uint8_t hc_num, 416ed9bcbc7SStephen Warren struct usb_device *dev, uint8_t dev_addr, uint8_t ep_num, 417ed9bcbc7SStephen Warren uint8_t ep_is_in, uint8_t ep_type, uint16_t max_packet) 4186e9e0626SOleksandr Tymoshenko { 4196e9e0626SOleksandr Tymoshenko struct dwc2_hc_regs *hc_regs = ®s->hc_regs[hc_num]; 420ed9bcbc7SStephen Warren uint32_t hcchar = (dev_addr << DWC2_HCCHAR_DEVADDR_OFFSET) | 4216e9e0626SOleksandr Tymoshenko (ep_num << DWC2_HCCHAR_EPNUM_OFFSET) | 4226e9e0626SOleksandr Tymoshenko (ep_is_in << DWC2_HCCHAR_EPDIR_OFFSET) | 4236e9e0626SOleksandr Tymoshenko (ep_type << DWC2_HCCHAR_EPTYPE_OFFSET) | 4246e9e0626SOleksandr Tymoshenko (max_packet << DWC2_HCCHAR_MPS_OFFSET); 4256e9e0626SOleksandr Tymoshenko 426ed9bcbc7SStephen Warren if (dev->speed == USB_SPEED_LOW) 427ed9bcbc7SStephen Warren hcchar |= DWC2_HCCHAR_LSPDDEV; 428ed9bcbc7SStephen Warren 4296e9e0626SOleksandr Tymoshenko /* 4306e9e0626SOleksandr Tymoshenko * Program the HCCHARn register with the endpoint characteristics 4316e9e0626SOleksandr Tymoshenko * for the current transfer. 4326e9e0626SOleksandr Tymoshenko */ 4336e9e0626SOleksandr Tymoshenko writel(hcchar, &hc_regs->hcchar); 4346e9e0626SOleksandr Tymoshenko 435890f0ee4SStefan Brüns /* Program the HCSPLIT register, default to no SPLIT */ 4366e9e0626SOleksandr Tymoshenko writel(0, &hc_regs->hcsplt); 4376e9e0626SOleksandr Tymoshenko } 4386e9e0626SOleksandr Tymoshenko 439890f0ee4SStefan Brüns static void dwc_otg_hc_init_split(struct dwc2_hc_regs *hc_regs, 440890f0ee4SStefan Brüns uint8_t hub_devnum, uint8_t hub_port) 441890f0ee4SStefan Brüns { 442890f0ee4SStefan Brüns uint32_t hcsplt = 0; 443890f0ee4SStefan Brüns 444890f0ee4SStefan Brüns hcsplt = DWC2_HCSPLT_SPLTENA; 445890f0ee4SStefan Brüns hcsplt |= hub_devnum << DWC2_HCSPLT_HUBADDR_OFFSET; 446890f0ee4SStefan Brüns hcsplt |= hub_port << DWC2_HCSPLT_PRTADDR_OFFSET; 447890f0ee4SStefan Brüns 448890f0ee4SStefan Brüns /* Program the HCSPLIT register for SPLITs */ 449890f0ee4SStefan Brüns writel(hcsplt, &hc_regs->hcsplt); 450890f0ee4SStefan Brüns } 451890f0ee4SStefan Brüns 4526e9e0626SOleksandr Tymoshenko /* 4536e9e0626SOleksandr Tymoshenko * DWC2 to USB API interface 4546e9e0626SOleksandr Tymoshenko */ 4556e9e0626SOleksandr Tymoshenko /* Direction: In ; Request: Status */ 456cc3e3a9eSSimon Glass static int dwc_otg_submit_rh_msg_in_status(struct dwc2_core_regs *regs, 457cc3e3a9eSSimon Glass struct usb_device *dev, void *buffer, 4586e9e0626SOleksandr Tymoshenko int txlen, struct devrequest *cmd) 4596e9e0626SOleksandr Tymoshenko { 4606e9e0626SOleksandr Tymoshenko uint32_t hprt0 = 0; 4616e9e0626SOleksandr Tymoshenko uint32_t port_status = 0; 4626e9e0626SOleksandr Tymoshenko uint32_t port_change = 0; 4636e9e0626SOleksandr Tymoshenko int len = 0; 4646e9e0626SOleksandr Tymoshenko int stat = 0; 4656e9e0626SOleksandr Tymoshenko 4666e9e0626SOleksandr Tymoshenko switch (cmd->requesttype & ~USB_DIR_IN) { 4676e9e0626SOleksandr Tymoshenko case 0: 4686e9e0626SOleksandr Tymoshenko *(uint16_t *)buffer = cpu_to_le16(1); 4696e9e0626SOleksandr Tymoshenko len = 2; 4706e9e0626SOleksandr Tymoshenko break; 4716e9e0626SOleksandr Tymoshenko case USB_RECIP_INTERFACE: 4726e9e0626SOleksandr Tymoshenko case USB_RECIP_ENDPOINT: 4736e9e0626SOleksandr Tymoshenko *(uint16_t *)buffer = cpu_to_le16(0); 4746e9e0626SOleksandr Tymoshenko len = 2; 4756e9e0626SOleksandr Tymoshenko break; 4766e9e0626SOleksandr Tymoshenko case USB_TYPE_CLASS: 4776e9e0626SOleksandr Tymoshenko *(uint32_t *)buffer = cpu_to_le32(0); 4786e9e0626SOleksandr Tymoshenko len = 4; 4796e9e0626SOleksandr Tymoshenko break; 4806e9e0626SOleksandr Tymoshenko case USB_RECIP_OTHER | USB_TYPE_CLASS: 4816e9e0626SOleksandr Tymoshenko hprt0 = readl(®s->hprt0); 4826e9e0626SOleksandr Tymoshenko if (hprt0 & DWC2_HPRT0_PRTCONNSTS) 4836e9e0626SOleksandr Tymoshenko port_status |= USB_PORT_STAT_CONNECTION; 4846e9e0626SOleksandr Tymoshenko if (hprt0 & DWC2_HPRT0_PRTENA) 4856e9e0626SOleksandr Tymoshenko port_status |= USB_PORT_STAT_ENABLE; 4866e9e0626SOleksandr Tymoshenko if (hprt0 & DWC2_HPRT0_PRTSUSP) 4876e9e0626SOleksandr Tymoshenko port_status |= USB_PORT_STAT_SUSPEND; 4886e9e0626SOleksandr Tymoshenko if (hprt0 & DWC2_HPRT0_PRTOVRCURRACT) 4896e9e0626SOleksandr Tymoshenko port_status |= USB_PORT_STAT_OVERCURRENT; 4906e9e0626SOleksandr Tymoshenko if (hprt0 & DWC2_HPRT0_PRTRST) 4916e9e0626SOleksandr Tymoshenko port_status |= USB_PORT_STAT_RESET; 4926e9e0626SOleksandr Tymoshenko if (hprt0 & DWC2_HPRT0_PRTPWR) 4936e9e0626SOleksandr Tymoshenko port_status |= USB_PORT_STAT_POWER; 4946e9e0626SOleksandr Tymoshenko 4954748cce5SStephen Warren if ((hprt0 & DWC2_HPRT0_PRTSPD_MASK) == DWC2_HPRT0_PRTSPD_LOW) 4964748cce5SStephen Warren port_status |= USB_PORT_STAT_LOW_SPEED; 4974748cce5SStephen Warren else if ((hprt0 & DWC2_HPRT0_PRTSPD_MASK) == 4984748cce5SStephen Warren DWC2_HPRT0_PRTSPD_HIGH) 4996e9e0626SOleksandr Tymoshenko port_status |= USB_PORT_STAT_HIGH_SPEED; 5006e9e0626SOleksandr Tymoshenko 5016e9e0626SOleksandr Tymoshenko if (hprt0 & DWC2_HPRT0_PRTENCHNG) 5026e9e0626SOleksandr Tymoshenko port_change |= USB_PORT_STAT_C_ENABLE; 5036e9e0626SOleksandr Tymoshenko if (hprt0 & DWC2_HPRT0_PRTCONNDET) 5046e9e0626SOleksandr Tymoshenko port_change |= USB_PORT_STAT_C_CONNECTION; 5056e9e0626SOleksandr Tymoshenko if (hprt0 & DWC2_HPRT0_PRTOVRCURRCHNG) 5066e9e0626SOleksandr Tymoshenko port_change |= USB_PORT_STAT_C_OVERCURRENT; 5076e9e0626SOleksandr Tymoshenko 5086e9e0626SOleksandr Tymoshenko *(uint32_t *)buffer = cpu_to_le32(port_status | 5096e9e0626SOleksandr Tymoshenko (port_change << 16)); 5106e9e0626SOleksandr Tymoshenko len = 4; 5116e9e0626SOleksandr Tymoshenko break; 5126e9e0626SOleksandr Tymoshenko default: 5136e9e0626SOleksandr Tymoshenko puts("unsupported root hub command\n"); 5146e9e0626SOleksandr Tymoshenko stat = USB_ST_STALLED; 5156e9e0626SOleksandr Tymoshenko } 5166e9e0626SOleksandr Tymoshenko 5176e9e0626SOleksandr Tymoshenko dev->act_len = min(len, txlen); 5186e9e0626SOleksandr Tymoshenko dev->status = stat; 5196e9e0626SOleksandr Tymoshenko 5206e9e0626SOleksandr Tymoshenko return stat; 5216e9e0626SOleksandr Tymoshenko } 5226e9e0626SOleksandr Tymoshenko 5236e9e0626SOleksandr Tymoshenko /* Direction: In ; Request: Descriptor */ 5246e9e0626SOleksandr Tymoshenko static int dwc_otg_submit_rh_msg_in_descriptor(struct usb_device *dev, 5256e9e0626SOleksandr Tymoshenko void *buffer, int txlen, 5266e9e0626SOleksandr Tymoshenko struct devrequest *cmd) 5276e9e0626SOleksandr Tymoshenko { 5286e9e0626SOleksandr Tymoshenko unsigned char data[32]; 5296e9e0626SOleksandr Tymoshenko uint32_t dsc; 5306e9e0626SOleksandr Tymoshenko int len = 0; 5316e9e0626SOleksandr Tymoshenko int stat = 0; 5326e9e0626SOleksandr Tymoshenko uint16_t wValue = cpu_to_le16(cmd->value); 5336e9e0626SOleksandr Tymoshenko uint16_t wLength = cpu_to_le16(cmd->length); 5346e9e0626SOleksandr Tymoshenko 5356e9e0626SOleksandr Tymoshenko switch (cmd->requesttype & ~USB_DIR_IN) { 5366e9e0626SOleksandr Tymoshenko case 0: 5376e9e0626SOleksandr Tymoshenko switch (wValue & 0xff00) { 5386e9e0626SOleksandr Tymoshenko case 0x0100: /* device descriptor */ 539b4141195SMasahiro Yamada len = min3(txlen, (int)sizeof(root_hub_dev_des), (int)wLength); 5406e9e0626SOleksandr Tymoshenko memcpy(buffer, root_hub_dev_des, len); 5416e9e0626SOleksandr Tymoshenko break; 5426e9e0626SOleksandr Tymoshenko case 0x0200: /* configuration descriptor */ 543b4141195SMasahiro Yamada len = min3(txlen, (int)sizeof(root_hub_config_des), (int)wLength); 5446e9e0626SOleksandr Tymoshenko memcpy(buffer, root_hub_config_des, len); 5456e9e0626SOleksandr Tymoshenko break; 5466e9e0626SOleksandr Tymoshenko case 0x0300: /* string descriptors */ 5476e9e0626SOleksandr Tymoshenko switch (wValue & 0xff) { 5486e9e0626SOleksandr Tymoshenko case 0x00: 549b4141195SMasahiro Yamada len = min3(txlen, (int)sizeof(root_hub_str_index0), 550b4141195SMasahiro Yamada (int)wLength); 5516e9e0626SOleksandr Tymoshenko memcpy(buffer, root_hub_str_index0, len); 5526e9e0626SOleksandr Tymoshenko break; 5536e9e0626SOleksandr Tymoshenko case 0x01: 554b4141195SMasahiro Yamada len = min3(txlen, (int)sizeof(root_hub_str_index1), 555b4141195SMasahiro Yamada (int)wLength); 5566e9e0626SOleksandr Tymoshenko memcpy(buffer, root_hub_str_index1, len); 5576e9e0626SOleksandr Tymoshenko break; 5586e9e0626SOleksandr Tymoshenko } 5596e9e0626SOleksandr Tymoshenko break; 5606e9e0626SOleksandr Tymoshenko default: 5616e9e0626SOleksandr Tymoshenko stat = USB_ST_STALLED; 5626e9e0626SOleksandr Tymoshenko } 5636e9e0626SOleksandr Tymoshenko break; 5646e9e0626SOleksandr Tymoshenko 5656e9e0626SOleksandr Tymoshenko case USB_TYPE_CLASS: 5666e9e0626SOleksandr Tymoshenko /* Root port config, set 1 port and nothing else. */ 5676e9e0626SOleksandr Tymoshenko dsc = 0x00000001; 5686e9e0626SOleksandr Tymoshenko 5696e9e0626SOleksandr Tymoshenko data[0] = 9; /* min length; */ 5706e9e0626SOleksandr Tymoshenko data[1] = 0x29; 5716e9e0626SOleksandr Tymoshenko data[2] = dsc & RH_A_NDP; 5726e9e0626SOleksandr Tymoshenko data[3] = 0; 5736e9e0626SOleksandr Tymoshenko if (dsc & RH_A_PSM) 5746e9e0626SOleksandr Tymoshenko data[3] |= 0x1; 5756e9e0626SOleksandr Tymoshenko if (dsc & RH_A_NOCP) 5766e9e0626SOleksandr Tymoshenko data[3] |= 0x10; 5776e9e0626SOleksandr Tymoshenko else if (dsc & RH_A_OCPM) 5786e9e0626SOleksandr Tymoshenko data[3] |= 0x8; 5796e9e0626SOleksandr Tymoshenko 5806e9e0626SOleksandr Tymoshenko /* corresponds to data[4-7] */ 5816e9e0626SOleksandr Tymoshenko data[5] = (dsc & RH_A_POTPGT) >> 24; 5826e9e0626SOleksandr Tymoshenko data[7] = dsc & RH_B_DR; 5836e9e0626SOleksandr Tymoshenko if (data[2] < 7) { 5846e9e0626SOleksandr Tymoshenko data[8] = 0xff; 5856e9e0626SOleksandr Tymoshenko } else { 5866e9e0626SOleksandr Tymoshenko data[0] += 2; 5876e9e0626SOleksandr Tymoshenko data[8] = (dsc & RH_B_DR) >> 8; 5886e9e0626SOleksandr Tymoshenko data[9] = 0xff; 5896e9e0626SOleksandr Tymoshenko data[10] = data[9]; 5906e9e0626SOleksandr Tymoshenko } 5916e9e0626SOleksandr Tymoshenko 592b4141195SMasahiro Yamada len = min3(txlen, (int)data[0], (int)wLength); 5936e9e0626SOleksandr Tymoshenko memcpy(buffer, data, len); 5946e9e0626SOleksandr Tymoshenko break; 5956e9e0626SOleksandr Tymoshenko default: 5966e9e0626SOleksandr Tymoshenko puts("unsupported root hub command\n"); 5976e9e0626SOleksandr Tymoshenko stat = USB_ST_STALLED; 5986e9e0626SOleksandr Tymoshenko } 5996e9e0626SOleksandr Tymoshenko 6006e9e0626SOleksandr Tymoshenko dev->act_len = min(len, txlen); 6016e9e0626SOleksandr Tymoshenko dev->status = stat; 6026e9e0626SOleksandr Tymoshenko 6036e9e0626SOleksandr Tymoshenko return stat; 6046e9e0626SOleksandr Tymoshenko } 6056e9e0626SOleksandr Tymoshenko 6066e9e0626SOleksandr Tymoshenko /* Direction: In ; Request: Configuration */ 6076e9e0626SOleksandr Tymoshenko static int dwc_otg_submit_rh_msg_in_configuration(struct usb_device *dev, 6086e9e0626SOleksandr Tymoshenko void *buffer, int txlen, 6096e9e0626SOleksandr Tymoshenko struct devrequest *cmd) 6106e9e0626SOleksandr Tymoshenko { 6116e9e0626SOleksandr Tymoshenko int len = 0; 6126e9e0626SOleksandr Tymoshenko int stat = 0; 6136e9e0626SOleksandr Tymoshenko 6146e9e0626SOleksandr Tymoshenko switch (cmd->requesttype & ~USB_DIR_IN) { 6156e9e0626SOleksandr Tymoshenko case 0: 6166e9e0626SOleksandr Tymoshenko *(uint8_t *)buffer = 0x01; 6176e9e0626SOleksandr Tymoshenko len = 1; 6186e9e0626SOleksandr Tymoshenko break; 6196e9e0626SOleksandr Tymoshenko default: 6206e9e0626SOleksandr Tymoshenko puts("unsupported root hub command\n"); 6216e9e0626SOleksandr Tymoshenko stat = USB_ST_STALLED; 6226e9e0626SOleksandr Tymoshenko } 6236e9e0626SOleksandr Tymoshenko 6246e9e0626SOleksandr Tymoshenko dev->act_len = min(len, txlen); 6256e9e0626SOleksandr Tymoshenko dev->status = stat; 6266e9e0626SOleksandr Tymoshenko 6276e9e0626SOleksandr Tymoshenko return stat; 6286e9e0626SOleksandr Tymoshenko } 6296e9e0626SOleksandr Tymoshenko 6306e9e0626SOleksandr Tymoshenko /* Direction: In */ 631cc3e3a9eSSimon Glass static int dwc_otg_submit_rh_msg_in(struct dwc2_priv *priv, 632cc3e3a9eSSimon Glass struct usb_device *dev, void *buffer, 633cc3e3a9eSSimon Glass int txlen, struct devrequest *cmd) 6346e9e0626SOleksandr Tymoshenko { 6356e9e0626SOleksandr Tymoshenko switch (cmd->request) { 6366e9e0626SOleksandr Tymoshenko case USB_REQ_GET_STATUS: 637cc3e3a9eSSimon Glass return dwc_otg_submit_rh_msg_in_status(priv->regs, dev, buffer, 6386e9e0626SOleksandr Tymoshenko txlen, cmd); 6396e9e0626SOleksandr Tymoshenko case USB_REQ_GET_DESCRIPTOR: 6406e9e0626SOleksandr Tymoshenko return dwc_otg_submit_rh_msg_in_descriptor(dev, buffer, 6416e9e0626SOleksandr Tymoshenko txlen, cmd); 6426e9e0626SOleksandr Tymoshenko case USB_REQ_GET_CONFIGURATION: 6436e9e0626SOleksandr Tymoshenko return dwc_otg_submit_rh_msg_in_configuration(dev, buffer, 6446e9e0626SOleksandr Tymoshenko txlen, cmd); 6456e9e0626SOleksandr Tymoshenko default: 6466e9e0626SOleksandr Tymoshenko puts("unsupported root hub command\n"); 6476e9e0626SOleksandr Tymoshenko return USB_ST_STALLED; 6486e9e0626SOleksandr Tymoshenko } 6496e9e0626SOleksandr Tymoshenko } 6506e9e0626SOleksandr Tymoshenko 6516e9e0626SOleksandr Tymoshenko /* Direction: Out */ 652cc3e3a9eSSimon Glass static int dwc_otg_submit_rh_msg_out(struct dwc2_priv *priv, 653cc3e3a9eSSimon Glass struct usb_device *dev, 6546e9e0626SOleksandr Tymoshenko void *buffer, int txlen, 6556e9e0626SOleksandr Tymoshenko struct devrequest *cmd) 6566e9e0626SOleksandr Tymoshenko { 657cc3e3a9eSSimon Glass struct dwc2_core_regs *regs = priv->regs; 6586e9e0626SOleksandr Tymoshenko int len = 0; 6596e9e0626SOleksandr Tymoshenko int stat = 0; 6606e9e0626SOleksandr Tymoshenko uint16_t bmrtype_breq = cmd->requesttype | (cmd->request << 8); 6616e9e0626SOleksandr Tymoshenko uint16_t wValue = cpu_to_le16(cmd->value); 6626e9e0626SOleksandr Tymoshenko 6636e9e0626SOleksandr Tymoshenko switch (bmrtype_breq & ~USB_DIR_IN) { 6646e9e0626SOleksandr Tymoshenko case (USB_REQ_CLEAR_FEATURE << 8) | USB_RECIP_ENDPOINT: 6656e9e0626SOleksandr Tymoshenko case (USB_REQ_CLEAR_FEATURE << 8) | USB_TYPE_CLASS: 6666e9e0626SOleksandr Tymoshenko break; 6676e9e0626SOleksandr Tymoshenko 6686e9e0626SOleksandr Tymoshenko case (USB_REQ_CLEAR_FEATURE << 8) | USB_RECIP_OTHER | USB_TYPE_CLASS: 6696e9e0626SOleksandr Tymoshenko switch (wValue) { 6706e9e0626SOleksandr Tymoshenko case USB_PORT_FEAT_C_CONNECTION: 6716e9e0626SOleksandr Tymoshenko setbits_le32(®s->hprt0, DWC2_HPRT0_PRTCONNDET); 6726e9e0626SOleksandr Tymoshenko break; 6736e9e0626SOleksandr Tymoshenko } 6746e9e0626SOleksandr Tymoshenko break; 6756e9e0626SOleksandr Tymoshenko 6766e9e0626SOleksandr Tymoshenko case (USB_REQ_SET_FEATURE << 8) | USB_RECIP_OTHER | USB_TYPE_CLASS: 6776e9e0626SOleksandr Tymoshenko switch (wValue) { 6786e9e0626SOleksandr Tymoshenko case USB_PORT_FEAT_SUSPEND: 6796e9e0626SOleksandr Tymoshenko break; 6806e9e0626SOleksandr Tymoshenko 6816e9e0626SOleksandr Tymoshenko case USB_PORT_FEAT_RESET: 6826e9e0626SOleksandr Tymoshenko clrsetbits_le32(®s->hprt0, DWC2_HPRT0_PRTENA | 6836e9e0626SOleksandr Tymoshenko DWC2_HPRT0_PRTCONNDET | 6846e9e0626SOleksandr Tymoshenko DWC2_HPRT0_PRTENCHNG | 6856e9e0626SOleksandr Tymoshenko DWC2_HPRT0_PRTOVRCURRCHNG, 6866e9e0626SOleksandr Tymoshenko DWC2_HPRT0_PRTRST); 6876e9e0626SOleksandr Tymoshenko mdelay(50); 6886e9e0626SOleksandr Tymoshenko clrbits_le32(®s->hprt0, DWC2_HPRT0_PRTRST); 6896e9e0626SOleksandr Tymoshenko break; 6906e9e0626SOleksandr Tymoshenko 6916e9e0626SOleksandr Tymoshenko case USB_PORT_FEAT_POWER: 6926e9e0626SOleksandr Tymoshenko clrsetbits_le32(®s->hprt0, DWC2_HPRT0_PRTENA | 6936e9e0626SOleksandr Tymoshenko DWC2_HPRT0_PRTCONNDET | 6946e9e0626SOleksandr Tymoshenko DWC2_HPRT0_PRTENCHNG | 6956e9e0626SOleksandr Tymoshenko DWC2_HPRT0_PRTOVRCURRCHNG, 6966e9e0626SOleksandr Tymoshenko DWC2_HPRT0_PRTRST); 6976e9e0626SOleksandr Tymoshenko break; 6986e9e0626SOleksandr Tymoshenko 6996e9e0626SOleksandr Tymoshenko case USB_PORT_FEAT_ENABLE: 7006e9e0626SOleksandr Tymoshenko break; 7016e9e0626SOleksandr Tymoshenko } 7026e9e0626SOleksandr Tymoshenko break; 7036e9e0626SOleksandr Tymoshenko case (USB_REQ_SET_ADDRESS << 8): 704cc3e3a9eSSimon Glass priv->root_hub_devnum = wValue; 7056e9e0626SOleksandr Tymoshenko break; 7066e9e0626SOleksandr Tymoshenko case (USB_REQ_SET_CONFIGURATION << 8): 7076e9e0626SOleksandr Tymoshenko break; 7086e9e0626SOleksandr Tymoshenko default: 7096e9e0626SOleksandr Tymoshenko puts("unsupported root hub command\n"); 7106e9e0626SOleksandr Tymoshenko stat = USB_ST_STALLED; 7116e9e0626SOleksandr Tymoshenko } 7126e9e0626SOleksandr Tymoshenko 7136e9e0626SOleksandr Tymoshenko len = min(len, txlen); 7146e9e0626SOleksandr Tymoshenko 7156e9e0626SOleksandr Tymoshenko dev->act_len = len; 7166e9e0626SOleksandr Tymoshenko dev->status = stat; 7176e9e0626SOleksandr Tymoshenko 7186e9e0626SOleksandr Tymoshenko return stat; 7196e9e0626SOleksandr Tymoshenko } 7206e9e0626SOleksandr Tymoshenko 721cc3e3a9eSSimon Glass static int dwc_otg_submit_rh_msg(struct dwc2_priv *priv, struct usb_device *dev, 722cc3e3a9eSSimon Glass unsigned long pipe, void *buffer, int txlen, 7236e9e0626SOleksandr Tymoshenko struct devrequest *cmd) 7246e9e0626SOleksandr Tymoshenko { 7256e9e0626SOleksandr Tymoshenko int stat = 0; 7266e9e0626SOleksandr Tymoshenko 7276e9e0626SOleksandr Tymoshenko if (usb_pipeint(pipe)) { 7286e9e0626SOleksandr Tymoshenko puts("Root-Hub submit IRQ: NOT implemented\n"); 7296e9e0626SOleksandr Tymoshenko return 0; 7306e9e0626SOleksandr Tymoshenko } 7316e9e0626SOleksandr Tymoshenko 7326e9e0626SOleksandr Tymoshenko if (cmd->requesttype & USB_DIR_IN) 733cc3e3a9eSSimon Glass stat = dwc_otg_submit_rh_msg_in(priv, dev, buffer, txlen, cmd); 7346e9e0626SOleksandr Tymoshenko else 735cc3e3a9eSSimon Glass stat = dwc_otg_submit_rh_msg_out(priv, dev, buffer, txlen, cmd); 7366e9e0626SOleksandr Tymoshenko 7376e9e0626SOleksandr Tymoshenko mdelay(1); 7386e9e0626SOleksandr Tymoshenko 7396e9e0626SOleksandr Tymoshenko return stat; 7406e9e0626SOleksandr Tymoshenko } 7416e9e0626SOleksandr Tymoshenko 742daed3059SStefan Brüns int wait_for_chhltd(struct dwc2_hc_regs *hc_regs, uint32_t *sub, int *toggle) 7434a1d21fcSStephen Warren { 7444a1d21fcSStephen Warren int ret; 7454a1d21fcSStephen Warren uint32_t hcint, hctsiz; 7464a1d21fcSStephen Warren 7474a1d21fcSStephen Warren ret = wait_for_bit(&hc_regs->hcint, DWC2_HCINT_CHHLTD, true); 7484a1d21fcSStephen Warren if (ret) 7494a1d21fcSStephen Warren return ret; 7504a1d21fcSStephen Warren 7514a1d21fcSStephen Warren hcint = readl(&hc_regs->hcint); 7524a1d21fcSStephen Warren hctsiz = readl(&hc_regs->hctsiz); 7534a1d21fcSStephen Warren *sub = (hctsiz & DWC2_HCTSIZ_XFERSIZE_MASK) >> 7544a1d21fcSStephen Warren DWC2_HCTSIZ_XFERSIZE_OFFSET; 75566ffc875SStephen Warren *toggle = (hctsiz & DWC2_HCTSIZ_PID_MASK) >> DWC2_HCTSIZ_PID_OFFSET; 7564a1d21fcSStephen Warren 75703460cdcSStefan Brüns debug("%s: HCINT=%08x sub=%u toggle=%d\n", __func__, hcint, *sub, 75803460cdcSStefan Brüns *toggle); 7594a1d21fcSStephen Warren 76003460cdcSStefan Brüns if (hcint & DWC2_HCINT_XFERCOMP) 7614a1d21fcSStephen Warren return 0; 76203460cdcSStefan Brüns 76303460cdcSStefan Brüns if (hcint & (DWC2_HCINT_NAK | DWC2_HCINT_FRMOVRUN)) 76403460cdcSStefan Brüns return -EAGAIN; 76503460cdcSStefan Brüns 76603460cdcSStefan Brüns debug("%s: Error (HCINT=%08x)\n", __func__, hcint); 76703460cdcSStefan Brüns return -EINVAL; 7684a1d21fcSStephen Warren } 7694a1d21fcSStephen Warren 7707b5e504dSStephen Warren static int dwc2_eptype[] = { 7717b5e504dSStephen Warren DWC2_HCCHAR_EPTYPE_ISOC, 7727b5e504dSStephen Warren DWC2_HCCHAR_EPTYPE_INTR, 7737b5e504dSStephen Warren DWC2_HCCHAR_EPTYPE_CONTROL, 7747b5e504dSStephen Warren DWC2_HCCHAR_EPTYPE_BULK, 7757b5e504dSStephen Warren }; 7767b5e504dSStephen Warren 777daed3059SStefan Brüns static int transfer_chunk(struct dwc2_hc_regs *hc_regs, void *aligned_buffer, 778daed3059SStefan Brüns int *pid, int in, void *buffer, int num_packets, 779*d2ff51b3SStefan Brüns int xfer_len, int *actual_len, int odd_frame) 780daed3059SStefan Brüns { 781daed3059SStefan Brüns int ret = 0; 782daed3059SStefan Brüns uint32_t sub; 783daed3059SStefan Brüns 784daed3059SStefan Brüns debug("%s: chunk: pid %d xfer_len %u pkts %u\n", __func__, 785daed3059SStefan Brüns *pid, xfer_len, num_packets); 786daed3059SStefan Brüns 787daed3059SStefan Brüns writel((xfer_len << DWC2_HCTSIZ_XFERSIZE_OFFSET) | 788daed3059SStefan Brüns (num_packets << DWC2_HCTSIZ_PKTCNT_OFFSET) | 789daed3059SStefan Brüns (*pid << DWC2_HCTSIZ_PID_OFFSET), 790daed3059SStefan Brüns &hc_regs->hctsiz); 791daed3059SStefan Brüns 792daed3059SStefan Brüns if (!in && xfer_len) { 793daed3059SStefan Brüns memcpy(aligned_buffer, buffer, xfer_len); 794daed3059SStefan Brüns 795daed3059SStefan Brüns flush_dcache_range((unsigned long)aligned_buffer, 796daed3059SStefan Brüns (unsigned long)aligned_buffer + 797daed3059SStefan Brüns roundup(xfer_len, ARCH_DMA_MINALIGN)); 798daed3059SStefan Brüns } 799daed3059SStefan Brüns 800daed3059SStefan Brüns writel(phys_to_bus((unsigned long)aligned_buffer), &hc_regs->hcdma); 801daed3059SStefan Brüns 802daed3059SStefan Brüns /* Clear old interrupt conditions for this host channel. */ 803daed3059SStefan Brüns writel(0x3fff, &hc_regs->hcint); 804daed3059SStefan Brüns 805daed3059SStefan Brüns /* Set host channel enable after all other setup is complete. */ 806daed3059SStefan Brüns clrsetbits_le32(&hc_regs->hcchar, DWC2_HCCHAR_MULTICNT_MASK | 807*d2ff51b3SStefan Brüns DWC2_HCCHAR_CHEN | DWC2_HCCHAR_CHDIS | 808*d2ff51b3SStefan Brüns DWC2_HCCHAR_ODDFRM, 809daed3059SStefan Brüns (1 << DWC2_HCCHAR_MULTICNT_OFFSET) | 810*d2ff51b3SStefan Brüns (odd_frame << DWC2_HCCHAR_ODDFRM_OFFSET) | 811daed3059SStefan Brüns DWC2_HCCHAR_CHEN); 812daed3059SStefan Brüns 813daed3059SStefan Brüns ret = wait_for_chhltd(hc_regs, &sub, pid); 814daed3059SStefan Brüns if (ret < 0) 815daed3059SStefan Brüns return ret; 816daed3059SStefan Brüns 817daed3059SStefan Brüns if (in) { 818daed3059SStefan Brüns xfer_len -= sub; 819daed3059SStefan Brüns 820daed3059SStefan Brüns invalidate_dcache_range((unsigned long)aligned_buffer, 821daed3059SStefan Brüns (unsigned long)aligned_buffer + 822daed3059SStefan Brüns roundup(xfer_len, ARCH_DMA_MINALIGN)); 823daed3059SStefan Brüns 824daed3059SStefan Brüns memcpy(buffer, aligned_buffer, xfer_len); 825daed3059SStefan Brüns } 826daed3059SStefan Brüns *actual_len = xfer_len; 827daed3059SStefan Brüns 828daed3059SStefan Brüns return ret; 829daed3059SStefan Brüns } 830daed3059SStefan Brüns 831cc3e3a9eSSimon Glass int chunk_msg(struct dwc2_priv *priv, struct usb_device *dev, 83203460cdcSStefan Brüns unsigned long pipe, int *pid, int in, void *buffer, int len) 8336e9e0626SOleksandr Tymoshenko { 834cc3e3a9eSSimon Glass struct dwc2_core_regs *regs = priv->regs; 8357b5e504dSStephen Warren struct dwc2_hc_regs *hc_regs = ®s->hc_regs[DWC2_HC_CHANNEL]; 836*d2ff51b3SStefan Brüns struct dwc2_host_regs *host_regs = ®s->host_regs; 8376e9e0626SOleksandr Tymoshenko int devnum = usb_pipedevice(pipe); 8386e9e0626SOleksandr Tymoshenko int ep = usb_pipeendpoint(pipe); 8396e9e0626SOleksandr Tymoshenko int max = usb_maxpacket(dev, pipe); 8407b5e504dSStephen Warren int eptype = dwc2_eptype[usb_pipetype(pipe)]; 8416e9e0626SOleksandr Tymoshenko int done = 0; 8425877de91SStephen Warren int ret = 0; 843b54e4470SStefan Brüns int do_split = 0; 844b54e4470SStefan Brüns int complete_split = 0; 8456e9e0626SOleksandr Tymoshenko uint32_t xfer_len; 8466e9e0626SOleksandr Tymoshenko uint32_t num_packets; 8476e9e0626SOleksandr Tymoshenko int stop_transfer = 0; 84856a7bbd7SStefan Brüns uint32_t max_xfer_len; 849*d2ff51b3SStefan Brüns int ssplit_frame_num = 0; 8506e9e0626SOleksandr Tymoshenko 8517b5e504dSStephen Warren debug("%s: msg: pipe %lx pid %d in %d len %d\n", __func__, pipe, *pid, 8527b5e504dSStephen Warren in, len); 8536e9e0626SOleksandr Tymoshenko 85456a7bbd7SStefan Brüns max_xfer_len = CONFIG_DWC2_MAX_PACKET_COUNT * max; 85556a7bbd7SStefan Brüns if (max_xfer_len > CONFIG_DWC2_MAX_TRANSFER_SIZE) 85656a7bbd7SStefan Brüns max_xfer_len = CONFIG_DWC2_MAX_TRANSFER_SIZE; 85756a7bbd7SStefan Brüns if (max_xfer_len > DWC2_DATA_BUF_SIZE) 85856a7bbd7SStefan Brüns max_xfer_len = DWC2_DATA_BUF_SIZE; 85956a7bbd7SStefan Brüns 86056a7bbd7SStefan Brüns /* Make sure that max_xfer_len is a multiple of max packet size. */ 86156a7bbd7SStefan Brüns num_packets = max_xfer_len / max; 86256a7bbd7SStefan Brüns max_xfer_len = num_packets * max; 86356a7bbd7SStefan Brüns 8646e9e0626SOleksandr Tymoshenko /* Initialize channel */ 865ed9bcbc7SStephen Warren dwc_otg_hc_init(regs, DWC2_HC_CHANNEL, dev, devnum, ep, in, 866ed9bcbc7SStephen Warren eptype, max); 8676e9e0626SOleksandr Tymoshenko 868b54e4470SStefan Brüns /* Check if the target is a FS/LS device behind a HS hub */ 869b54e4470SStefan Brüns if (dev->speed != USB_SPEED_HIGH) { 870b54e4470SStefan Brüns uint8_t hub_addr; 871b54e4470SStefan Brüns uint8_t hub_port; 872b54e4470SStefan Brüns uint32_t hprt0 = readl(®s->hprt0); 873b54e4470SStefan Brüns if ((hprt0 & DWC2_HPRT0_PRTSPD_MASK) == 874b54e4470SStefan Brüns DWC2_HPRT0_PRTSPD_HIGH) { 875b54e4470SStefan Brüns usb_find_usb2_hub_address_port(dev, &hub_addr, 876b54e4470SStefan Brüns &hub_port); 877b54e4470SStefan Brüns dwc_otg_hc_init_split(hc_regs, hub_addr, hub_port); 878b54e4470SStefan Brüns 879b54e4470SStefan Brüns do_split = 1; 880b54e4470SStefan Brüns num_packets = 1; 881b54e4470SStefan Brüns max_xfer_len = max; 882b54e4470SStefan Brüns } 883b54e4470SStefan Brüns } 884b54e4470SStefan Brüns 885daed3059SStefan Brüns do { 886daed3059SStefan Brüns int actual_len = 0; 887b54e4470SStefan Brüns uint32_t hcint; 888*d2ff51b3SStefan Brüns int odd_frame = 0; 8896e9e0626SOleksandr Tymoshenko xfer_len = len - done; 8906e9e0626SOleksandr Tymoshenko 89156a7bbd7SStefan Brüns if (xfer_len > max_xfer_len) 89256a7bbd7SStefan Brüns xfer_len = max_xfer_len; 89356a7bbd7SStefan Brüns else if (xfer_len > max) 8946e9e0626SOleksandr Tymoshenko num_packets = (xfer_len + max - 1) / max; 89556a7bbd7SStefan Brüns else 8966e9e0626SOleksandr Tymoshenko num_packets = 1; 8976e9e0626SOleksandr Tymoshenko 898b54e4470SStefan Brüns if (complete_split) 899b54e4470SStefan Brüns setbits_le32(&hc_regs->hcsplt, DWC2_HCSPLT_COMPSPLT); 900b54e4470SStefan Brüns else if (do_split) 901b54e4470SStefan Brüns clrbits_le32(&hc_regs->hcsplt, DWC2_HCSPLT_COMPSPLT); 902b54e4470SStefan Brüns 903*d2ff51b3SStefan Brüns if (eptype == DWC2_HCCHAR_EPTYPE_INTR) { 904*d2ff51b3SStefan Brüns int uframe_num = readl(&host_regs->hfnum); 905*d2ff51b3SStefan Brüns if (!(uframe_num & 0x1)) 906*d2ff51b3SStefan Brüns odd_frame = 1; 907*d2ff51b3SStefan Brüns } 908*d2ff51b3SStefan Brüns 909daed3059SStefan Brüns ret = transfer_chunk(hc_regs, priv->aligned_buffer, pid, 910daed3059SStefan Brüns in, (char *)buffer + done, num_packets, 911*d2ff51b3SStefan Brüns xfer_len, &actual_len, odd_frame); 9127b5e504dSStephen Warren 913b54e4470SStefan Brüns hcint = readl(&hc_regs->hcint); 914b54e4470SStefan Brüns if (complete_split) { 915b54e4470SStefan Brüns stop_transfer = 0; 916*d2ff51b3SStefan Brüns if (hcint & DWC2_HCINT_NYET) { 917b54e4470SStefan Brüns ret = 0; 918*d2ff51b3SStefan Brüns int frame_num = DWC2_HFNUM_MAX_FRNUM & 919*d2ff51b3SStefan Brüns readl(&host_regs->hfnum); 920*d2ff51b3SStefan Brüns if (((frame_num - ssplit_frame_num) & 921*d2ff51b3SStefan Brüns DWC2_HFNUM_MAX_FRNUM) > 4) 922*d2ff51b3SStefan Brüns ret = -EAGAIN; 923*d2ff51b3SStefan Brüns } else 924b54e4470SStefan Brüns complete_split = 0; 925b54e4470SStefan Brüns } else if (do_split) { 926b54e4470SStefan Brüns if (hcint & DWC2_HCINT_ACK) { 927*d2ff51b3SStefan Brüns ssplit_frame_num = DWC2_HFNUM_MAX_FRNUM & 928*d2ff51b3SStefan Brüns readl(&host_regs->hfnum); 929b54e4470SStefan Brüns ret = 0; 930b54e4470SStefan Brüns complete_split = 1; 931b54e4470SStefan Brüns } 932b54e4470SStefan Brüns } 933b54e4470SStefan Brüns 9345877de91SStephen Warren if (ret) 9354a1d21fcSStephen Warren break; 9366e9e0626SOleksandr Tymoshenko 937daed3059SStefan Brüns if (actual_len < xfer_len) 9386e9e0626SOleksandr Tymoshenko stop_transfer = 1; 9396e9e0626SOleksandr Tymoshenko 940daed3059SStefan Brüns done += actual_len; 941d1c880c6SStephen Warren 942b54e4470SStefan Brüns /* Transactions are done when when either all data is transferred or 943b54e4470SStefan Brüns * there is a short transfer. In case of a SPLIT make sure the CSPLIT 944b54e4470SStefan Brüns * is executed. 945b54e4470SStefan Brüns */ 946b54e4470SStefan Brüns } while (((done < len) && !stop_transfer) || complete_split); 9476e9e0626SOleksandr Tymoshenko 9486e9e0626SOleksandr Tymoshenko writel(0, &hc_regs->hcintmsk); 9496e9e0626SOleksandr Tymoshenko writel(0xFFFFFFFF, &hc_regs->hcint); 9506e9e0626SOleksandr Tymoshenko 9516e9e0626SOleksandr Tymoshenko dev->status = 0; 9526e9e0626SOleksandr Tymoshenko dev->act_len = done; 9536e9e0626SOleksandr Tymoshenko 9545877de91SStephen Warren return ret; 9556e9e0626SOleksandr Tymoshenko } 9566e9e0626SOleksandr Tymoshenko 9577b5e504dSStephen Warren /* U-Boot USB transmission interface */ 958cc3e3a9eSSimon Glass int _submit_bulk_msg(struct dwc2_priv *priv, struct usb_device *dev, 959cc3e3a9eSSimon Glass unsigned long pipe, void *buffer, int len) 9607b5e504dSStephen Warren { 9617b5e504dSStephen Warren int devnum = usb_pipedevice(pipe); 9627b5e504dSStephen Warren int ep = usb_pipeendpoint(pipe); 9637b5e504dSStephen Warren 964cc3e3a9eSSimon Glass if (devnum == priv->root_hub_devnum) { 9657b5e504dSStephen Warren dev->status = 0; 9667b5e504dSStephen Warren return -EINVAL; 9677b5e504dSStephen Warren } 9687b5e504dSStephen Warren 969cc3e3a9eSSimon Glass return chunk_msg(priv, dev, pipe, &priv->bulk_data_toggle[devnum][ep], 97003460cdcSStefan Brüns usb_pipein(pipe), buffer, len); 9717b5e504dSStephen Warren } 9727b5e504dSStephen Warren 973cc3e3a9eSSimon Glass static int _submit_control_msg(struct dwc2_priv *priv, struct usb_device *dev, 974cc3e3a9eSSimon Glass unsigned long pipe, void *buffer, int len, 975cc3e3a9eSSimon Glass struct devrequest *setup) 9766e9e0626SOleksandr Tymoshenko { 9776e9e0626SOleksandr Tymoshenko int devnum = usb_pipedevice(pipe); 978ee837554SStephen Warren int pid, ret, act_len; 9796e9e0626SOleksandr Tymoshenko /* For CONTROL endpoint pid should start with DATA1 */ 9806e9e0626SOleksandr Tymoshenko int status_direction; 9816e9e0626SOleksandr Tymoshenko 982cc3e3a9eSSimon Glass if (devnum == priv->root_hub_devnum) { 9836e9e0626SOleksandr Tymoshenko dev->status = 0; 9846e9e0626SOleksandr Tymoshenko dev->speed = USB_SPEED_HIGH; 985cc3e3a9eSSimon Glass return dwc_otg_submit_rh_msg(priv, dev, pipe, buffer, len, 986cc3e3a9eSSimon Glass setup); 9876e9e0626SOleksandr Tymoshenko } 9886e9e0626SOleksandr Tymoshenko 989b54e4470SStefan Brüns /* SETUP stage */ 990ee837554SStephen Warren pid = DWC2_HC_PID_SETUP; 991b54e4470SStefan Brüns do { 99203460cdcSStefan Brüns ret = chunk_msg(priv, dev, pipe, &pid, 0, setup, 8); 993b54e4470SStefan Brüns } while (ret == -EAGAIN); 994ee837554SStephen Warren if (ret) 995ee837554SStephen Warren return ret; 9966e9e0626SOleksandr Tymoshenko 997b54e4470SStefan Brüns /* DATA stage */ 998b54e4470SStefan Brüns act_len = 0; 9996e9e0626SOleksandr Tymoshenko if (buffer) { 1000282685e0SStephen Warren pid = DWC2_HC_PID_DATA1; 1001b54e4470SStefan Brüns do { 1002b54e4470SStefan Brüns ret = chunk_msg(priv, dev, pipe, &pid, usb_pipein(pipe), 1003b54e4470SStefan Brüns buffer, len); 1004b54e4470SStefan Brüns act_len += dev->act_len; 1005b54e4470SStefan Brüns buffer += dev->act_len; 1006b54e4470SStefan Brüns len -= dev->act_len; 1007b54e4470SStefan Brüns } while (ret == -EAGAIN); 1008ee837554SStephen Warren if (ret) 1009ee837554SStephen Warren return ret; 1010b54e4470SStefan Brüns status_direction = usb_pipeout(pipe); 1011b54e4470SStefan Brüns } else { 1012b54e4470SStefan Brüns /* No-data CONTROL always ends with an IN transaction */ 1013b54e4470SStefan Brüns status_direction = 1; 1014b54e4470SStefan Brüns } 10156e9e0626SOleksandr Tymoshenko 10166e9e0626SOleksandr Tymoshenko /* STATUS stage */ 1017ee837554SStephen Warren pid = DWC2_HC_PID_DATA1; 1018b54e4470SStefan Brüns do { 1019cc3e3a9eSSimon Glass ret = chunk_msg(priv, dev, pipe, &pid, status_direction, 102003460cdcSStefan Brüns priv->status_buffer, 0); 1021b54e4470SStefan Brüns } while (ret == -EAGAIN); 1022ee837554SStephen Warren if (ret) 1023ee837554SStephen Warren return ret; 10246e9e0626SOleksandr Tymoshenko 1025ee837554SStephen Warren dev->act_len = act_len; 10266e9e0626SOleksandr Tymoshenko 10274a1d21fcSStephen Warren return 0; 10286e9e0626SOleksandr Tymoshenko } 10296e9e0626SOleksandr Tymoshenko 1030cc3e3a9eSSimon Glass int _submit_int_msg(struct dwc2_priv *priv, struct usb_device *dev, 1031cc3e3a9eSSimon Glass unsigned long pipe, void *buffer, int len, int interval) 10326e9e0626SOleksandr Tymoshenko { 10335877de91SStephen Warren unsigned long timeout; 10345877de91SStephen Warren int ret; 10355877de91SStephen Warren 1036e236519bSStephen Warren /* FIXME: what is interval? */ 10375877de91SStephen Warren 10385877de91SStephen Warren timeout = get_timer(0) + USB_TIMEOUT_MS(pipe); 10395877de91SStephen Warren for (;;) { 10405877de91SStephen Warren if (get_timer(0) > timeout) { 10415877de91SStephen Warren printf("Timeout poll on interrupt endpoint\n"); 10425877de91SStephen Warren return -ETIMEDOUT; 10435877de91SStephen Warren } 1044cc3e3a9eSSimon Glass ret = _submit_bulk_msg(priv, dev, pipe, buffer, len); 10455877de91SStephen Warren if (ret != -EAGAIN) 10465877de91SStephen Warren return ret; 10475877de91SStephen Warren } 10486e9e0626SOleksandr Tymoshenko } 10496e9e0626SOleksandr Tymoshenko 1050cc3e3a9eSSimon Glass static int dwc2_init_common(struct dwc2_priv *priv) 10516e9e0626SOleksandr Tymoshenko { 1052cc3e3a9eSSimon Glass struct dwc2_core_regs *regs = priv->regs; 10536e9e0626SOleksandr Tymoshenko uint32_t snpsid; 10546e9e0626SOleksandr Tymoshenko int i, j; 10556e9e0626SOleksandr Tymoshenko 10566e9e0626SOleksandr Tymoshenko snpsid = readl(®s->gsnpsid); 10576e9e0626SOleksandr Tymoshenko printf("Core Release: %x.%03x\n", snpsid >> 12 & 0xf, snpsid & 0xfff); 10586e9e0626SOleksandr Tymoshenko 10595cfd6c00SPeter Griffin if ((snpsid & DWC2_SNPSID_DEVID_MASK) != DWC2_SNPSID_DEVID_VER_2xx && 10605cfd6c00SPeter Griffin (snpsid & DWC2_SNPSID_DEVID_MASK) != DWC2_SNPSID_DEVID_VER_3xx) { 10616e9e0626SOleksandr Tymoshenko printf("SNPSID invalid (not DWC2 OTG device): %08x\n", snpsid); 10626e9e0626SOleksandr Tymoshenko return -ENODEV; 10636e9e0626SOleksandr Tymoshenko } 10646e9e0626SOleksandr Tymoshenko 10656e9e0626SOleksandr Tymoshenko dwc_otg_core_init(regs); 10666e9e0626SOleksandr Tymoshenko dwc_otg_core_host_init(regs); 10676e9e0626SOleksandr Tymoshenko 10686e9e0626SOleksandr Tymoshenko clrsetbits_le32(®s->hprt0, DWC2_HPRT0_PRTENA | 10696e9e0626SOleksandr Tymoshenko DWC2_HPRT0_PRTCONNDET | DWC2_HPRT0_PRTENCHNG | 10706e9e0626SOleksandr Tymoshenko DWC2_HPRT0_PRTOVRCURRCHNG, 10716e9e0626SOleksandr Tymoshenko DWC2_HPRT0_PRTRST); 10726e9e0626SOleksandr Tymoshenko mdelay(50); 10736e9e0626SOleksandr Tymoshenko clrbits_le32(®s->hprt0, DWC2_HPRT0_PRTENA | DWC2_HPRT0_PRTCONNDET | 10746e9e0626SOleksandr Tymoshenko DWC2_HPRT0_PRTENCHNG | DWC2_HPRT0_PRTOVRCURRCHNG | 10756e9e0626SOleksandr Tymoshenko DWC2_HPRT0_PRTRST); 10766e9e0626SOleksandr Tymoshenko 10776e9e0626SOleksandr Tymoshenko for (i = 0; i < MAX_DEVICE; i++) { 1078282685e0SStephen Warren for (j = 0; j < MAX_ENDPOINT; j++) 1079cc3e3a9eSSimon Glass priv->bulk_data_toggle[i][j] = DWC2_HC_PID_DATA0; 10806e9e0626SOleksandr Tymoshenko } 10816e9e0626SOleksandr Tymoshenko 10826e9e0626SOleksandr Tymoshenko return 0; 10836e9e0626SOleksandr Tymoshenko } 10846e9e0626SOleksandr Tymoshenko 1085cc3e3a9eSSimon Glass static void dwc2_uninit_common(struct dwc2_core_regs *regs) 10866e9e0626SOleksandr Tymoshenko { 10876e9e0626SOleksandr Tymoshenko /* Put everything in reset. */ 10886e9e0626SOleksandr Tymoshenko clrsetbits_le32(®s->hprt0, DWC2_HPRT0_PRTENA | 10896e9e0626SOleksandr Tymoshenko DWC2_HPRT0_PRTCONNDET | DWC2_HPRT0_PRTENCHNG | 10906e9e0626SOleksandr Tymoshenko DWC2_HPRT0_PRTOVRCURRCHNG, 10916e9e0626SOleksandr Tymoshenko DWC2_HPRT0_PRTRST); 1092cc3e3a9eSSimon Glass } 1093cc3e3a9eSSimon Glass 1094f58a41e0SSimon Glass #ifndef CONFIG_DM_USB 1095cc3e3a9eSSimon Glass int submit_control_msg(struct usb_device *dev, unsigned long pipe, void *buffer, 1096cc3e3a9eSSimon Glass int len, struct devrequest *setup) 1097cc3e3a9eSSimon Glass { 1098cc3e3a9eSSimon Glass return _submit_control_msg(&local, dev, pipe, buffer, len, setup); 1099cc3e3a9eSSimon Glass } 1100cc3e3a9eSSimon Glass 1101cc3e3a9eSSimon Glass int submit_bulk_msg(struct usb_device *dev, unsigned long pipe, void *buffer, 1102cc3e3a9eSSimon Glass int len) 1103cc3e3a9eSSimon Glass { 1104cc3e3a9eSSimon Glass return _submit_bulk_msg(&local, dev, pipe, buffer, len); 1105cc3e3a9eSSimon Glass } 1106cc3e3a9eSSimon Glass 1107cc3e3a9eSSimon Glass int submit_int_msg(struct usb_device *dev, unsigned long pipe, void *buffer, 1108cc3e3a9eSSimon Glass int len, int interval) 1109cc3e3a9eSSimon Glass { 1110cc3e3a9eSSimon Glass return _submit_int_msg(&local, dev, pipe, buffer, len, interval); 1111cc3e3a9eSSimon Glass } 1112cc3e3a9eSSimon Glass 1113cc3e3a9eSSimon Glass /* U-Boot USB control interface */ 1114cc3e3a9eSSimon Glass int usb_lowlevel_init(int index, enum usb_init_type init, void **controller) 1115cc3e3a9eSSimon Glass { 1116cc3e3a9eSSimon Glass struct dwc2_priv *priv = &local; 1117cc3e3a9eSSimon Glass 1118cc3e3a9eSSimon Glass memset(priv, '\0', sizeof(*priv)); 1119cc3e3a9eSSimon Glass priv->root_hub_devnum = 0; 1120cc3e3a9eSSimon Glass priv->regs = (struct dwc2_core_regs *)CONFIG_USB_DWC2_REG_ADDR; 1121cc3e3a9eSSimon Glass priv->aligned_buffer = aligned_buffer_addr; 1122cc3e3a9eSSimon Glass priv->status_buffer = status_buffer_addr; 1123cc3e3a9eSSimon Glass 1124cc3e3a9eSSimon Glass /* board-dependant init */ 1125cc3e3a9eSSimon Glass if (board_usb_init(index, USB_INIT_HOST)) 1126cc3e3a9eSSimon Glass return -1; 1127cc3e3a9eSSimon Glass 1128cc3e3a9eSSimon Glass return dwc2_init_common(priv); 1129cc3e3a9eSSimon Glass } 1130cc3e3a9eSSimon Glass 1131cc3e3a9eSSimon Glass int usb_lowlevel_stop(int index) 1132cc3e3a9eSSimon Glass { 1133cc3e3a9eSSimon Glass dwc2_uninit_common(local.regs); 1134cc3e3a9eSSimon Glass 11356e9e0626SOleksandr Tymoshenko return 0; 11366e9e0626SOleksandr Tymoshenko } 1137f58a41e0SSimon Glass #endif 1138f58a41e0SSimon Glass 1139f58a41e0SSimon Glass #ifdef CONFIG_DM_USB 1140f58a41e0SSimon Glass static int dwc2_submit_control_msg(struct udevice *dev, struct usb_device *udev, 1141f58a41e0SSimon Glass unsigned long pipe, void *buffer, int length, 1142f58a41e0SSimon Glass struct devrequest *setup) 1143f58a41e0SSimon Glass { 1144f58a41e0SSimon Glass struct dwc2_priv *priv = dev_get_priv(dev); 1145f58a41e0SSimon Glass 1146f58a41e0SSimon Glass debug("%s: dev='%s', udev=%p, udev->dev='%s', portnr=%d\n", __func__, 1147f58a41e0SSimon Glass dev->name, udev, udev->dev->name, udev->portnr); 1148f58a41e0SSimon Glass 1149f58a41e0SSimon Glass return _submit_control_msg(priv, udev, pipe, buffer, length, setup); 1150f58a41e0SSimon Glass } 1151f58a41e0SSimon Glass 1152f58a41e0SSimon Glass static int dwc2_submit_bulk_msg(struct udevice *dev, struct usb_device *udev, 1153f58a41e0SSimon Glass unsigned long pipe, void *buffer, int length) 1154f58a41e0SSimon Glass { 1155f58a41e0SSimon Glass struct dwc2_priv *priv = dev_get_priv(dev); 1156f58a41e0SSimon Glass 1157f58a41e0SSimon Glass debug("%s: dev='%s', udev=%p\n", __func__, dev->name, udev); 1158f58a41e0SSimon Glass 1159f58a41e0SSimon Glass return _submit_bulk_msg(priv, udev, pipe, buffer, length); 1160f58a41e0SSimon Glass } 1161f58a41e0SSimon Glass 1162f58a41e0SSimon Glass static int dwc2_submit_int_msg(struct udevice *dev, struct usb_device *udev, 1163f58a41e0SSimon Glass unsigned long pipe, void *buffer, int length, 1164f58a41e0SSimon Glass int interval) 1165f58a41e0SSimon Glass { 1166f58a41e0SSimon Glass struct dwc2_priv *priv = dev_get_priv(dev); 1167f58a41e0SSimon Glass 1168f58a41e0SSimon Glass debug("%s: dev='%s', udev=%p\n", __func__, dev->name, udev); 1169f58a41e0SSimon Glass 1170f58a41e0SSimon Glass return _submit_int_msg(priv, udev, pipe, buffer, length, interval); 1171f58a41e0SSimon Glass } 1172f58a41e0SSimon Glass 1173f58a41e0SSimon Glass static int dwc2_usb_ofdata_to_platdata(struct udevice *dev) 1174f58a41e0SSimon Glass { 1175f58a41e0SSimon Glass struct dwc2_priv *priv = dev_get_priv(dev); 1176f58a41e0SSimon Glass fdt_addr_t addr; 1177f58a41e0SSimon Glass 1178f58a41e0SSimon Glass addr = dev_get_addr(dev); 1179f58a41e0SSimon Glass if (addr == FDT_ADDR_T_NONE) 1180f58a41e0SSimon Glass return -EINVAL; 1181f58a41e0SSimon Glass priv->regs = (struct dwc2_core_regs *)addr; 1182f58a41e0SSimon Glass 1183f58a41e0SSimon Glass return 0; 1184f58a41e0SSimon Glass } 1185f58a41e0SSimon Glass 1186f58a41e0SSimon Glass static int dwc2_usb_probe(struct udevice *dev) 1187f58a41e0SSimon Glass { 1188f58a41e0SSimon Glass struct dwc2_priv *priv = dev_get_priv(dev); 1189f58a41e0SSimon Glass 1190f58a41e0SSimon Glass return dwc2_init_common(priv); 1191f58a41e0SSimon Glass } 1192f58a41e0SSimon Glass 1193f58a41e0SSimon Glass static int dwc2_usb_remove(struct udevice *dev) 1194f58a41e0SSimon Glass { 1195f58a41e0SSimon Glass struct dwc2_priv *priv = dev_get_priv(dev); 1196f58a41e0SSimon Glass 1197f58a41e0SSimon Glass dwc2_uninit_common(priv->regs); 1198f58a41e0SSimon Glass 1199f58a41e0SSimon Glass return 0; 1200f58a41e0SSimon Glass } 1201f58a41e0SSimon Glass 1202f58a41e0SSimon Glass struct dm_usb_ops dwc2_usb_ops = { 1203f58a41e0SSimon Glass .control = dwc2_submit_control_msg, 1204f58a41e0SSimon Glass .bulk = dwc2_submit_bulk_msg, 1205f58a41e0SSimon Glass .interrupt = dwc2_submit_int_msg, 1206f58a41e0SSimon Glass }; 1207f58a41e0SSimon Glass 1208f58a41e0SSimon Glass static const struct udevice_id dwc2_usb_ids[] = { 1209f58a41e0SSimon Glass { .compatible = "brcm,bcm2835-usb" }, 1210f522f947SMarek Vasut { .compatible = "snps,dwc2" }, 1211f58a41e0SSimon Glass { } 1212f58a41e0SSimon Glass }; 1213f58a41e0SSimon Glass 1214f58a41e0SSimon Glass U_BOOT_DRIVER(usb_dwc2) = { 12157a1386f9SMarek Vasut .name = "dwc2_usb", 1216f58a41e0SSimon Glass .id = UCLASS_USB, 1217f58a41e0SSimon Glass .of_match = dwc2_usb_ids, 1218f58a41e0SSimon Glass .ofdata_to_platdata = dwc2_usb_ofdata_to_platdata, 1219f58a41e0SSimon Glass .probe = dwc2_usb_probe, 1220f58a41e0SSimon Glass .remove = dwc2_usb_remove, 1221f58a41e0SSimon Glass .ops = &dwc2_usb_ops, 1222f58a41e0SSimon Glass .priv_auto_alloc_size = sizeof(struct dwc2_priv), 1223f58a41e0SSimon Glass .flags = DM_FLAG_ALLOC_PRIV_DMA, 1224f58a41e0SSimon Glass }; 1225f58a41e0SSimon Glass #endif 1226