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> 96e9e0626SOleksandr Tymoshenko #include <errno.h> 106e9e0626SOleksandr Tymoshenko #include <usb.h> 116e9e0626SOleksandr Tymoshenko #include <malloc.h> 126e9e0626SOleksandr Tymoshenko #include <usbroothubdes.h> 136e9e0626SOleksandr Tymoshenko #include <asm/io.h> 146e9e0626SOleksandr Tymoshenko 156e9e0626SOleksandr Tymoshenko #include "dwc2.h" 166e9e0626SOleksandr Tymoshenko 176e9e0626SOleksandr Tymoshenko /* Use only HC channel 0. */ 186e9e0626SOleksandr Tymoshenko #define DWC2_HC_CHANNEL 0 196e9e0626SOleksandr Tymoshenko 206e9e0626SOleksandr Tymoshenko #define DWC2_STATUS_BUF_SIZE 64 216e9e0626SOleksandr Tymoshenko #define DWC2_DATA_BUF_SIZE (64 * 1024) 226e9e0626SOleksandr Tymoshenko 236e9e0626SOleksandr Tymoshenko /* We need doubleword-aligned buffers for DMA transfers */ 246e9e0626SOleksandr Tymoshenko DEFINE_ALIGN_BUFFER(uint8_t, aligned_buffer, DWC2_DATA_BUF_SIZE, 8); 256e9e0626SOleksandr Tymoshenko DEFINE_ALIGN_BUFFER(uint8_t, status_buffer, DWC2_STATUS_BUF_SIZE, 8); 266e9e0626SOleksandr Tymoshenko 276e9e0626SOleksandr Tymoshenko #define MAX_DEVICE 16 286e9e0626SOleksandr Tymoshenko #define MAX_ENDPOINT 16 296e9e0626SOleksandr Tymoshenko static int bulk_data_toggle[MAX_DEVICE][MAX_ENDPOINT]; 306e9e0626SOleksandr Tymoshenko static int control_data_toggle[MAX_DEVICE][MAX_ENDPOINT]; 316e9e0626SOleksandr Tymoshenko 326e9e0626SOleksandr Tymoshenko static int root_hub_devnum; 336e9e0626SOleksandr Tymoshenko 346e9e0626SOleksandr Tymoshenko static struct dwc2_core_regs *regs = 356e9e0626SOleksandr Tymoshenko (struct dwc2_core_regs *)CONFIG_USB_DWC2_REG_ADDR; 366e9e0626SOleksandr Tymoshenko 376e9e0626SOleksandr Tymoshenko /* 386e9e0626SOleksandr Tymoshenko * DWC2 IP interface 396e9e0626SOleksandr Tymoshenko */ 406e9e0626SOleksandr Tymoshenko static int wait_for_bit(void *reg, const uint32_t mask, bool set) 416e9e0626SOleksandr Tymoshenko { 426e9e0626SOleksandr Tymoshenko unsigned int timeout = 1000000; 436e9e0626SOleksandr Tymoshenko uint32_t val; 446e9e0626SOleksandr Tymoshenko 456e9e0626SOleksandr Tymoshenko while (--timeout) { 466e9e0626SOleksandr Tymoshenko val = readl(reg); 476e9e0626SOleksandr Tymoshenko if (!set) 486e9e0626SOleksandr Tymoshenko val = ~val; 496e9e0626SOleksandr Tymoshenko 506e9e0626SOleksandr Tymoshenko if ((val & mask) == mask) 516e9e0626SOleksandr Tymoshenko return 0; 526e9e0626SOleksandr Tymoshenko 536e9e0626SOleksandr Tymoshenko udelay(1); 546e9e0626SOleksandr Tymoshenko } 556e9e0626SOleksandr Tymoshenko 566e9e0626SOleksandr Tymoshenko debug("%s: Timeout (reg=%p mask=%08x wait_set=%i)\n", 576e9e0626SOleksandr Tymoshenko __func__, reg, mask, set); 586e9e0626SOleksandr Tymoshenko 596e9e0626SOleksandr Tymoshenko return -ETIMEDOUT; 606e9e0626SOleksandr Tymoshenko } 616e9e0626SOleksandr Tymoshenko 626e9e0626SOleksandr Tymoshenko /* 636e9e0626SOleksandr Tymoshenko * Initializes the FSLSPClkSel field of the HCFG register 646e9e0626SOleksandr Tymoshenko * depending on the PHY type. 656e9e0626SOleksandr Tymoshenko */ 666e9e0626SOleksandr Tymoshenko static void init_fslspclksel(struct dwc2_core_regs *regs) 676e9e0626SOleksandr Tymoshenko { 686e9e0626SOleksandr Tymoshenko uint32_t phyclk; 696e9e0626SOleksandr Tymoshenko 706e9e0626SOleksandr Tymoshenko #if (CONFIG_DWC2_PHY_TYPE == DWC2_PHY_TYPE_FS) 716e9e0626SOleksandr Tymoshenko phyclk = DWC2_HCFG_FSLSPCLKSEL_48_MHZ; /* Full speed PHY */ 726e9e0626SOleksandr Tymoshenko #else 736e9e0626SOleksandr Tymoshenko /* High speed PHY running at full speed or high speed */ 746e9e0626SOleksandr Tymoshenko phyclk = DWC2_HCFG_FSLSPCLKSEL_30_60_MHZ; 756e9e0626SOleksandr Tymoshenko #endif 766e9e0626SOleksandr Tymoshenko 776e9e0626SOleksandr Tymoshenko #ifdef CONFIG_DWC2_ULPI_FS_LS 786e9e0626SOleksandr Tymoshenko uint32_t hwcfg2 = readl(®s->ghwcfg2); 796e9e0626SOleksandr Tymoshenko uint32_t hval = (ghwcfg2 & DWC2_HWCFG2_HS_PHY_TYPE_MASK) >> 806e9e0626SOleksandr Tymoshenko DWC2_HWCFG2_HS_PHY_TYPE_OFFSET; 816e9e0626SOleksandr Tymoshenko uint32_t fval = (ghwcfg2 & DWC2_HWCFG2_FS_PHY_TYPE_MASK) >> 826e9e0626SOleksandr Tymoshenko DWC2_HWCFG2_FS_PHY_TYPE_OFFSET; 836e9e0626SOleksandr Tymoshenko 846e9e0626SOleksandr Tymoshenko if (hval == 2 && fval == 1) 856e9e0626SOleksandr Tymoshenko phyclk = DWC2_HCFG_FSLSPCLKSEL_48_MHZ; /* Full speed PHY */ 866e9e0626SOleksandr Tymoshenko #endif 876e9e0626SOleksandr Tymoshenko 886e9e0626SOleksandr Tymoshenko clrsetbits_le32(®s->host_regs.hcfg, 896e9e0626SOleksandr Tymoshenko DWC2_HCFG_FSLSPCLKSEL_MASK, 906e9e0626SOleksandr Tymoshenko phyclk << DWC2_HCFG_FSLSPCLKSEL_OFFSET); 916e9e0626SOleksandr Tymoshenko } 926e9e0626SOleksandr Tymoshenko 936e9e0626SOleksandr Tymoshenko /* 946e9e0626SOleksandr Tymoshenko * Flush a Tx FIFO. 956e9e0626SOleksandr Tymoshenko * 966e9e0626SOleksandr Tymoshenko * @param regs Programming view of DWC_otg controller. 976e9e0626SOleksandr Tymoshenko * @param num Tx FIFO to flush. 986e9e0626SOleksandr Tymoshenko */ 996e9e0626SOleksandr Tymoshenko static void dwc_otg_flush_tx_fifo(struct dwc2_core_regs *regs, const int num) 1006e9e0626SOleksandr Tymoshenko { 1016e9e0626SOleksandr Tymoshenko int ret; 1026e9e0626SOleksandr Tymoshenko 1036e9e0626SOleksandr Tymoshenko writel(DWC2_GRSTCTL_TXFFLSH | (num << DWC2_GRSTCTL_TXFNUM_OFFSET), 1046e9e0626SOleksandr Tymoshenko ®s->grstctl); 1056e9e0626SOleksandr Tymoshenko ret = wait_for_bit(®s->grstctl, DWC2_GRSTCTL_TXFFLSH, 0); 1066e9e0626SOleksandr Tymoshenko if (ret) 1076e9e0626SOleksandr Tymoshenko printf("%s: Timeout!\n", __func__); 1086e9e0626SOleksandr Tymoshenko 1096e9e0626SOleksandr Tymoshenko /* Wait for 3 PHY Clocks */ 1106e9e0626SOleksandr Tymoshenko udelay(1); 1116e9e0626SOleksandr Tymoshenko } 1126e9e0626SOleksandr Tymoshenko 1136e9e0626SOleksandr Tymoshenko /* 1146e9e0626SOleksandr Tymoshenko * Flush Rx FIFO. 1156e9e0626SOleksandr Tymoshenko * 1166e9e0626SOleksandr Tymoshenko * @param regs Programming view of DWC_otg controller. 1176e9e0626SOleksandr Tymoshenko */ 1186e9e0626SOleksandr Tymoshenko static void dwc_otg_flush_rx_fifo(struct dwc2_core_regs *regs) 1196e9e0626SOleksandr Tymoshenko { 1206e9e0626SOleksandr Tymoshenko int ret; 1216e9e0626SOleksandr Tymoshenko 1226e9e0626SOleksandr Tymoshenko writel(DWC2_GRSTCTL_RXFFLSH, ®s->grstctl); 1236e9e0626SOleksandr Tymoshenko ret = wait_for_bit(®s->grstctl, DWC2_GRSTCTL_RXFFLSH, 0); 1246e9e0626SOleksandr Tymoshenko if (ret) 1256e9e0626SOleksandr Tymoshenko printf("%s: Timeout!\n", __func__); 1266e9e0626SOleksandr Tymoshenko 1276e9e0626SOleksandr Tymoshenko /* Wait for 3 PHY Clocks */ 1286e9e0626SOleksandr Tymoshenko udelay(1); 1296e9e0626SOleksandr Tymoshenko } 1306e9e0626SOleksandr Tymoshenko 1316e9e0626SOleksandr Tymoshenko /* 1326e9e0626SOleksandr Tymoshenko * Do core a soft reset of the core. Be careful with this because it 1336e9e0626SOleksandr Tymoshenko * resets all the internal state machines of the core. 1346e9e0626SOleksandr Tymoshenko */ 1356e9e0626SOleksandr Tymoshenko static void dwc_otg_core_reset(struct dwc2_core_regs *regs) 1366e9e0626SOleksandr Tymoshenko { 1376e9e0626SOleksandr Tymoshenko int ret; 1386e9e0626SOleksandr Tymoshenko 1396e9e0626SOleksandr Tymoshenko /* Wait for AHB master IDLE state. */ 1406e9e0626SOleksandr Tymoshenko ret = wait_for_bit(®s->grstctl, DWC2_GRSTCTL_AHBIDLE, 1); 1416e9e0626SOleksandr Tymoshenko if (ret) 1426e9e0626SOleksandr Tymoshenko printf("%s: Timeout!\n", __func__); 1436e9e0626SOleksandr Tymoshenko 1446e9e0626SOleksandr Tymoshenko /* Core Soft Reset */ 1456e9e0626SOleksandr Tymoshenko writel(DWC2_GRSTCTL_CSFTRST, ®s->grstctl); 1466e9e0626SOleksandr Tymoshenko ret = wait_for_bit(®s->grstctl, DWC2_GRSTCTL_CSFTRST, 0); 1476e9e0626SOleksandr Tymoshenko if (ret) 1486e9e0626SOleksandr Tymoshenko printf("%s: Timeout!\n", __func__); 1496e9e0626SOleksandr Tymoshenko 1506e9e0626SOleksandr Tymoshenko /* 1516e9e0626SOleksandr Tymoshenko * Wait for core to come out of reset. 1526e9e0626SOleksandr Tymoshenko * NOTE: This long sleep is _very_ important, otherwise the core will 1536e9e0626SOleksandr Tymoshenko * not stay in host mode after a connector ID change! 1546e9e0626SOleksandr Tymoshenko */ 1556e9e0626SOleksandr Tymoshenko mdelay(100); 1566e9e0626SOleksandr Tymoshenko } 1576e9e0626SOleksandr Tymoshenko 1586e9e0626SOleksandr Tymoshenko /* 1596e9e0626SOleksandr Tymoshenko * This function initializes the DWC_otg controller registers for 1606e9e0626SOleksandr Tymoshenko * host mode. 1616e9e0626SOleksandr Tymoshenko * 1626e9e0626SOleksandr Tymoshenko * This function flushes the Tx and Rx FIFOs and it flushes any entries in the 1636e9e0626SOleksandr Tymoshenko * request queues. Host channels are reset to ensure that they are ready for 1646e9e0626SOleksandr Tymoshenko * performing transfers. 1656e9e0626SOleksandr Tymoshenko * 1666e9e0626SOleksandr Tymoshenko * @param regs Programming view of DWC_otg controller 1676e9e0626SOleksandr Tymoshenko * 1686e9e0626SOleksandr Tymoshenko */ 1696e9e0626SOleksandr Tymoshenko static void dwc_otg_core_host_init(struct dwc2_core_regs *regs) 1706e9e0626SOleksandr Tymoshenko { 1716e9e0626SOleksandr Tymoshenko uint32_t nptxfifosize = 0; 1726e9e0626SOleksandr Tymoshenko uint32_t ptxfifosize = 0; 1736e9e0626SOleksandr Tymoshenko uint32_t hprt0 = 0; 1746e9e0626SOleksandr Tymoshenko int i, ret, num_channels; 1756e9e0626SOleksandr Tymoshenko 1766e9e0626SOleksandr Tymoshenko /* Restart the Phy Clock */ 1776e9e0626SOleksandr Tymoshenko writel(0, ®s->pcgcctl); 1786e9e0626SOleksandr Tymoshenko 1796e9e0626SOleksandr Tymoshenko /* Initialize Host Configuration Register */ 1806e9e0626SOleksandr Tymoshenko init_fslspclksel(regs); 1816e9e0626SOleksandr Tymoshenko #ifdef CONFIG_DWC2_DFLT_SPEED_FULL 1826e9e0626SOleksandr Tymoshenko setbits_le32(®s->host_regs.hcfg, DWC2_HCFG_FSLSSUPP); 1836e9e0626SOleksandr Tymoshenko #endif 1846e9e0626SOleksandr Tymoshenko 1856e9e0626SOleksandr Tymoshenko /* Configure data FIFO sizes */ 1866e9e0626SOleksandr Tymoshenko #ifdef CONFIG_DWC2_ENABLE_DYNAMIC_FIFO 1876e9e0626SOleksandr Tymoshenko if (readl(®s->ghwcfg2) & DWC2_HWCFG2_DYNAMIC_FIFO) { 1886e9e0626SOleksandr Tymoshenko /* Rx FIFO */ 1896e9e0626SOleksandr Tymoshenko writel(CONFIG_DWC2_HOST_RX_FIFO_SIZE, ®s->grxfsiz); 1906e9e0626SOleksandr Tymoshenko 1916e9e0626SOleksandr Tymoshenko /* Non-periodic Tx FIFO */ 1926e9e0626SOleksandr Tymoshenko nptxfifosize |= CONFIG_DWC2_HOST_NPERIO_TX_FIFO_SIZE << 1936e9e0626SOleksandr Tymoshenko DWC2_FIFOSIZE_DEPTH_OFFSET; 1946e9e0626SOleksandr Tymoshenko nptxfifosize |= CONFIG_DWC2_HOST_RX_FIFO_SIZE << 1956e9e0626SOleksandr Tymoshenko DWC2_FIFOSIZE_STARTADDR_OFFSET; 1966e9e0626SOleksandr Tymoshenko writel(nptxfifosize, ®s->gnptxfsiz); 1976e9e0626SOleksandr Tymoshenko 1986e9e0626SOleksandr Tymoshenko /* Periodic Tx FIFO */ 1996e9e0626SOleksandr Tymoshenko ptxfifosize |= CONFIG_DWC2_HOST_PERIO_TX_FIFO_SIZE << 2006e9e0626SOleksandr Tymoshenko DWC2_FIFOSIZE_DEPTH_OFFSET; 2016e9e0626SOleksandr Tymoshenko ptxfifosize |= (CONFIG_DWC2_HOST_RX_FIFO_SIZE + 2026e9e0626SOleksandr Tymoshenko CONFIG_DWC2_HOST_NPERIO_TX_FIFO_SIZE) << 2036e9e0626SOleksandr Tymoshenko DWC2_FIFOSIZE_STARTADDR_OFFSET; 2046e9e0626SOleksandr Tymoshenko writel(ptxfifosize, ®s->hptxfsiz); 2056e9e0626SOleksandr Tymoshenko } 2066e9e0626SOleksandr Tymoshenko #endif 2076e9e0626SOleksandr Tymoshenko 2086e9e0626SOleksandr Tymoshenko /* Clear Host Set HNP Enable in the OTG Control Register */ 2096e9e0626SOleksandr Tymoshenko clrbits_le32(®s->gotgctl, DWC2_GOTGCTL_HSTSETHNPEN); 2106e9e0626SOleksandr Tymoshenko 2116e9e0626SOleksandr Tymoshenko /* Make sure the FIFOs are flushed. */ 2126e9e0626SOleksandr Tymoshenko dwc_otg_flush_tx_fifo(regs, 0x10); /* All Tx FIFOs */ 2136e9e0626SOleksandr Tymoshenko dwc_otg_flush_rx_fifo(regs); 2146e9e0626SOleksandr Tymoshenko 2156e9e0626SOleksandr Tymoshenko /* Flush out any leftover queued requests. */ 2166e9e0626SOleksandr Tymoshenko num_channels = readl(®s->ghwcfg2); 2176e9e0626SOleksandr Tymoshenko num_channels &= DWC2_HWCFG2_NUM_HOST_CHAN_MASK; 2186e9e0626SOleksandr Tymoshenko num_channels >>= DWC2_HWCFG2_NUM_HOST_CHAN_OFFSET; 2196e9e0626SOleksandr Tymoshenko num_channels += 1; 2206e9e0626SOleksandr Tymoshenko 2216e9e0626SOleksandr Tymoshenko for (i = 0; i < num_channels; i++) 2226e9e0626SOleksandr Tymoshenko clrsetbits_le32(®s->hc_regs[i].hcchar, 2236e9e0626SOleksandr Tymoshenko DWC2_HCCHAR_CHEN | DWC2_HCCHAR_EPDIR, 2246e9e0626SOleksandr Tymoshenko DWC2_HCCHAR_CHDIS); 2256e9e0626SOleksandr Tymoshenko 2266e9e0626SOleksandr Tymoshenko /* Halt all channels to put them into a known state. */ 2276e9e0626SOleksandr Tymoshenko for (i = 0; i < num_channels; i++) { 2286e9e0626SOleksandr Tymoshenko clrsetbits_le32(®s->hc_regs[i].hcchar, 2296e9e0626SOleksandr Tymoshenko DWC2_HCCHAR_EPDIR, 2306e9e0626SOleksandr Tymoshenko DWC2_HCCHAR_CHEN | DWC2_HCCHAR_CHDIS); 2316e9e0626SOleksandr Tymoshenko ret = wait_for_bit(®s->hc_regs[i].hcchar, 2326e9e0626SOleksandr Tymoshenko DWC2_HCCHAR_CHEN, 0); 2336e9e0626SOleksandr Tymoshenko if (ret) 2346e9e0626SOleksandr Tymoshenko printf("%s: Timeout!\n", __func__); 2356e9e0626SOleksandr Tymoshenko } 2366e9e0626SOleksandr Tymoshenko 2376e9e0626SOleksandr Tymoshenko /* Turn on the vbus power. */ 2386e9e0626SOleksandr Tymoshenko if (readl(®s->gintsts) & DWC2_GINTSTS_CURMODE_HOST) { 2396e9e0626SOleksandr Tymoshenko hprt0 = readl(®s->hprt0); 2406e9e0626SOleksandr Tymoshenko hprt0 &= ~(DWC2_HPRT0_PRTENA | DWC2_HPRT0_PRTCONNDET); 2416e9e0626SOleksandr Tymoshenko hprt0 &= ~(DWC2_HPRT0_PRTENCHNG | DWC2_HPRT0_PRTOVRCURRCHNG); 2426e9e0626SOleksandr Tymoshenko if (!(hprt0 & DWC2_HPRT0_PRTPWR)) { 2436e9e0626SOleksandr Tymoshenko hprt0 |= DWC2_HPRT0_PRTPWR; 2446e9e0626SOleksandr Tymoshenko writel(hprt0, ®s->hprt0); 2456e9e0626SOleksandr Tymoshenko } 2466e9e0626SOleksandr Tymoshenko } 2476e9e0626SOleksandr Tymoshenko } 2486e9e0626SOleksandr Tymoshenko 2496e9e0626SOleksandr Tymoshenko /* 2506e9e0626SOleksandr Tymoshenko * This function initializes the DWC_otg controller registers and 2516e9e0626SOleksandr Tymoshenko * prepares the core for device mode or host mode operation. 2526e9e0626SOleksandr Tymoshenko * 2536e9e0626SOleksandr Tymoshenko * @param regs Programming view of the DWC_otg controller 2546e9e0626SOleksandr Tymoshenko */ 2556e9e0626SOleksandr Tymoshenko static void dwc_otg_core_init(struct dwc2_core_regs *regs) 2566e9e0626SOleksandr Tymoshenko { 2576e9e0626SOleksandr Tymoshenko uint32_t ahbcfg = 0; 2586e9e0626SOleksandr Tymoshenko uint32_t usbcfg = 0; 2596e9e0626SOleksandr Tymoshenko uint8_t brst_sz = CONFIG_DWC2_DMA_BURST_SIZE; 2606e9e0626SOleksandr Tymoshenko 2616e9e0626SOleksandr Tymoshenko /* Common Initialization */ 2626e9e0626SOleksandr Tymoshenko usbcfg = readl(®s->gusbcfg); 2636e9e0626SOleksandr Tymoshenko 2646e9e0626SOleksandr Tymoshenko /* Program the ULPI External VBUS bit if needed */ 2656e9e0626SOleksandr Tymoshenko #ifdef CONFIG_DWC2_PHY_ULPI_EXT_VBUS 2666e9e0626SOleksandr Tymoshenko usbcfg |= DWC2_GUSBCFG_ULPI_EXT_VBUS_DRV; 2676e9e0626SOleksandr Tymoshenko #else 2686e9e0626SOleksandr Tymoshenko usbcfg &= ~DWC2_GUSBCFG_ULPI_EXT_VBUS_DRV; 2696e9e0626SOleksandr Tymoshenko #endif 2706e9e0626SOleksandr Tymoshenko 2716e9e0626SOleksandr Tymoshenko /* Set external TS Dline pulsing */ 2726e9e0626SOleksandr Tymoshenko #ifdef CONFIG_DWC2_TS_DLINE 2736e9e0626SOleksandr Tymoshenko usbcfg |= DWC2_GUSBCFG_TERM_SEL_DL_PULSE; 2746e9e0626SOleksandr Tymoshenko #else 2756e9e0626SOleksandr Tymoshenko usbcfg &= ~DWC2_GUSBCFG_TERM_SEL_DL_PULSE; 2766e9e0626SOleksandr Tymoshenko #endif 2776e9e0626SOleksandr Tymoshenko writel(usbcfg, ®s->gusbcfg); 2786e9e0626SOleksandr Tymoshenko 2796e9e0626SOleksandr Tymoshenko /* Reset the Controller */ 2806e9e0626SOleksandr Tymoshenko dwc_otg_core_reset(regs); 2816e9e0626SOleksandr Tymoshenko 2826e9e0626SOleksandr Tymoshenko /* 2836e9e0626SOleksandr Tymoshenko * This programming sequence needs to happen in FS mode before 2846e9e0626SOleksandr Tymoshenko * any other programming occurs 2856e9e0626SOleksandr Tymoshenko */ 2866e9e0626SOleksandr Tymoshenko #if defined(CONFIG_DWC2_DFLT_SPEED_FULL) && \ 2876e9e0626SOleksandr Tymoshenko (CONFIG_DWC2_PHY_TYPE == DWC2_PHY_TYPE_FS) 2886e9e0626SOleksandr Tymoshenko /* If FS mode with FS PHY */ 2896e9e0626SOleksandr Tymoshenko setbits_le32(®s->gusbcfg, DWC2_GUSBCFG_PHYSEL); 2906e9e0626SOleksandr Tymoshenko 2916e9e0626SOleksandr Tymoshenko /* Reset after a PHY select */ 2926e9e0626SOleksandr Tymoshenko dwc_otg_core_reset(regs); 2936e9e0626SOleksandr Tymoshenko 2946e9e0626SOleksandr Tymoshenko /* 2956e9e0626SOleksandr Tymoshenko * Program DCFG.DevSpd or HCFG.FSLSPclkSel to 48Mhz in FS. 2966e9e0626SOleksandr Tymoshenko * Also do this on HNP Dev/Host mode switches (done in dev_init 2976e9e0626SOleksandr Tymoshenko * and host_init). 2986e9e0626SOleksandr Tymoshenko */ 2996e9e0626SOleksandr Tymoshenko if (readl(®s->gintsts) & DWC2_GINTSTS_CURMODE_HOST) 3006e9e0626SOleksandr Tymoshenko init_fslspclksel(regs); 3016e9e0626SOleksandr Tymoshenko 3026e9e0626SOleksandr Tymoshenko #ifdef CONFIG_DWC2_I2C_ENABLE 3036e9e0626SOleksandr Tymoshenko /* Program GUSBCFG.OtgUtmifsSel to I2C */ 3046e9e0626SOleksandr Tymoshenko setbits_le32(®s->gusbcfg, DWC2_GUSBCFG_OTGUTMIFSSEL); 3056e9e0626SOleksandr Tymoshenko 3066e9e0626SOleksandr Tymoshenko /* Program GI2CCTL.I2CEn */ 3076e9e0626SOleksandr Tymoshenko clrsetbits_le32(®s->gi2cctl, DWC2_GI2CCTL_I2CEN | 3086e9e0626SOleksandr Tymoshenko DWC2_GI2CCTL_I2CDEVADDR_MASK, 3096e9e0626SOleksandr Tymoshenko 1 << DWC2_GI2CCTL_I2CDEVADDR_OFFSET); 3106e9e0626SOleksandr Tymoshenko setbits_le32(®s->gi2cctl, DWC2_GI2CCTL_I2CEN); 3116e9e0626SOleksandr Tymoshenko #endif 3126e9e0626SOleksandr Tymoshenko 3136e9e0626SOleksandr Tymoshenko #else 3146e9e0626SOleksandr Tymoshenko /* High speed PHY. */ 3156e9e0626SOleksandr Tymoshenko 3166e9e0626SOleksandr Tymoshenko /* 3176e9e0626SOleksandr Tymoshenko * HS PHY parameters. These parameters are preserved during 3186e9e0626SOleksandr Tymoshenko * soft reset so only program the first time. Do a soft reset 3196e9e0626SOleksandr Tymoshenko * immediately after setting phyif. 3206e9e0626SOleksandr Tymoshenko */ 3216e9e0626SOleksandr Tymoshenko usbcfg &= ~(DWC2_GUSBCFG_ULPI_UTMI_SEL | DWC2_GUSBCFG_PHYIF); 3226e9e0626SOleksandr Tymoshenko usbcfg |= CONFIG_DWC2_PHY_TYPE << DWC2_GUSBCFG_ULPI_UTMI_SEL_OFFSET; 3236e9e0626SOleksandr Tymoshenko 3246e9e0626SOleksandr Tymoshenko if (usbcfg & DWC2_GUSBCFG_ULPI_UTMI_SEL) { /* ULPI interface */ 3256e9e0626SOleksandr Tymoshenko #ifdef CONFIG_DWC2_PHY_ULPI_DDR 3266e9e0626SOleksandr Tymoshenko usbcfg |= DWC2_GUSBCFG_DDRSEL; 3276e9e0626SOleksandr Tymoshenko #else 3286e9e0626SOleksandr Tymoshenko usbcfg &= ~DWC2_GUSBCFG_DDRSEL; 3296e9e0626SOleksandr Tymoshenko #endif 3306e9e0626SOleksandr Tymoshenko } else { /* UTMI+ interface */ 3316e9e0626SOleksandr Tymoshenko #if (CONFIG_DWC2_UTMI_PHY_WIDTH == 16) 3326e9e0626SOleksandr Tymoshenko usbcfg |= DWC2_GUSBCFG_PHYIF; 3336e9e0626SOleksandr Tymoshenko #endif 3346e9e0626SOleksandr Tymoshenko } 3356e9e0626SOleksandr Tymoshenko 3366e9e0626SOleksandr Tymoshenko writel(usbcfg, ®s->gusbcfg); 3376e9e0626SOleksandr Tymoshenko 3386e9e0626SOleksandr Tymoshenko /* Reset after setting the PHY parameters */ 3396e9e0626SOleksandr Tymoshenko dwc_otg_core_reset(regs); 3406e9e0626SOleksandr Tymoshenko #endif 3416e9e0626SOleksandr Tymoshenko 3426e9e0626SOleksandr Tymoshenko usbcfg = readl(®s->gusbcfg); 3436e9e0626SOleksandr Tymoshenko usbcfg &= ~(DWC2_GUSBCFG_ULPI_FSLS | DWC2_GUSBCFG_ULPI_CLK_SUS_M); 3446e9e0626SOleksandr Tymoshenko #ifdef CONFIG_DWC2_ULPI_FS_LS 3456e9e0626SOleksandr Tymoshenko uint32_t hwcfg2 = readl(®s->ghwcfg2); 3466e9e0626SOleksandr Tymoshenko uint32_t hval = (ghwcfg2 & DWC2_HWCFG2_HS_PHY_TYPE_MASK) >> 3476e9e0626SOleksandr Tymoshenko DWC2_HWCFG2_HS_PHY_TYPE_OFFSET; 3486e9e0626SOleksandr Tymoshenko uint32_t fval = (ghwcfg2 & DWC2_HWCFG2_FS_PHY_TYPE_MASK) >> 3496e9e0626SOleksandr Tymoshenko DWC2_HWCFG2_FS_PHY_TYPE_OFFSET; 3506e9e0626SOleksandr Tymoshenko if (hval == 2 && fval == 1) { 3516e9e0626SOleksandr Tymoshenko usbcfg |= DWC2_GUSBCFG_ULPI_FSLS; 3526e9e0626SOleksandr Tymoshenko usbcfg |= DWC2_GUSBCFG_ULPI_CLK_SUS_M; 3536e9e0626SOleksandr Tymoshenko } 3546e9e0626SOleksandr Tymoshenko #endif 3556e9e0626SOleksandr Tymoshenko writel(usbcfg, ®s->gusbcfg); 3566e9e0626SOleksandr Tymoshenko 3576e9e0626SOleksandr Tymoshenko /* Program the GAHBCFG Register. */ 3586e9e0626SOleksandr Tymoshenko switch (readl(®s->ghwcfg2) & DWC2_HWCFG2_ARCHITECTURE_MASK) { 3596e9e0626SOleksandr Tymoshenko case DWC2_HWCFG2_ARCHITECTURE_SLAVE_ONLY: 3606e9e0626SOleksandr Tymoshenko break; 3616e9e0626SOleksandr Tymoshenko case DWC2_HWCFG2_ARCHITECTURE_EXT_DMA: 3626e9e0626SOleksandr Tymoshenko while (brst_sz > 1) { 3636e9e0626SOleksandr Tymoshenko ahbcfg |= ahbcfg + (1 << DWC2_GAHBCFG_HBURSTLEN_OFFSET); 3646e9e0626SOleksandr Tymoshenko ahbcfg &= DWC2_GAHBCFG_HBURSTLEN_MASK; 3656e9e0626SOleksandr Tymoshenko brst_sz >>= 1; 3666e9e0626SOleksandr Tymoshenko } 3676e9e0626SOleksandr Tymoshenko 3686e9e0626SOleksandr Tymoshenko #ifdef CONFIG_DWC2_DMA_ENABLE 3696e9e0626SOleksandr Tymoshenko ahbcfg |= DWC2_GAHBCFG_DMAENABLE; 3706e9e0626SOleksandr Tymoshenko #endif 3716e9e0626SOleksandr Tymoshenko break; 3726e9e0626SOleksandr Tymoshenko 3736e9e0626SOleksandr Tymoshenko case DWC2_HWCFG2_ARCHITECTURE_INT_DMA: 3746e9e0626SOleksandr Tymoshenko ahbcfg |= DWC2_GAHBCFG_HBURSTLEN_INCR4; 3756e9e0626SOleksandr Tymoshenko #ifdef CONFIG_DWC2_DMA_ENABLE 3766e9e0626SOleksandr Tymoshenko ahbcfg |= DWC2_GAHBCFG_DMAENABLE; 3776e9e0626SOleksandr Tymoshenko #endif 3786e9e0626SOleksandr Tymoshenko break; 3796e9e0626SOleksandr Tymoshenko } 3806e9e0626SOleksandr Tymoshenko 3816e9e0626SOleksandr Tymoshenko writel(ahbcfg, ®s->gahbcfg); 3826e9e0626SOleksandr Tymoshenko 3836e9e0626SOleksandr Tymoshenko /* Program the GUSBCFG register for HNP/SRP. */ 3846e9e0626SOleksandr Tymoshenko setbits_le32(®s->gusbcfg, DWC2_GUSBCFG_HNPCAP | DWC2_GUSBCFG_SRPCAP); 3856e9e0626SOleksandr Tymoshenko 3866e9e0626SOleksandr Tymoshenko #ifdef CONFIG_DWC2_IC_USB_CAP 3876e9e0626SOleksandr Tymoshenko setbits_le32(®s->gusbcfg, DWC2_GUSBCFG_IC_USB_CAP); 3886e9e0626SOleksandr Tymoshenko #endif 3896e9e0626SOleksandr Tymoshenko } 3906e9e0626SOleksandr Tymoshenko 3916e9e0626SOleksandr Tymoshenko /* 3926e9e0626SOleksandr Tymoshenko * Prepares a host channel for transferring packets to/from a specific 3936e9e0626SOleksandr Tymoshenko * endpoint. The HCCHARn register is set up with the characteristics specified 3946e9e0626SOleksandr Tymoshenko * in _hc. Host channel interrupts that may need to be serviced while this 3956e9e0626SOleksandr Tymoshenko * transfer is in progress are enabled. 3966e9e0626SOleksandr Tymoshenko * 3976e9e0626SOleksandr Tymoshenko * @param regs Programming view of DWC_otg controller 3986e9e0626SOleksandr Tymoshenko * @param hc Information needed to initialize the host channel 3996e9e0626SOleksandr Tymoshenko */ 4006e9e0626SOleksandr Tymoshenko static void dwc_otg_hc_init(struct dwc2_core_regs *regs, uint8_t hc_num, 4016e9e0626SOleksandr Tymoshenko uint8_t dev_addr, uint8_t ep_num, uint8_t ep_is_in, 4026e9e0626SOleksandr Tymoshenko uint8_t ep_type, uint16_t max_packet) 4036e9e0626SOleksandr Tymoshenko { 4046e9e0626SOleksandr Tymoshenko struct dwc2_hc_regs *hc_regs = ®s->hc_regs[hc_num]; 4056e9e0626SOleksandr Tymoshenko const uint32_t hcchar = (dev_addr << DWC2_HCCHAR_DEVADDR_OFFSET) | 4066e9e0626SOleksandr Tymoshenko (ep_num << DWC2_HCCHAR_EPNUM_OFFSET) | 4076e9e0626SOleksandr Tymoshenko (ep_is_in << DWC2_HCCHAR_EPDIR_OFFSET) | 4086e9e0626SOleksandr Tymoshenko (ep_type << DWC2_HCCHAR_EPTYPE_OFFSET) | 4096e9e0626SOleksandr Tymoshenko (max_packet << DWC2_HCCHAR_MPS_OFFSET); 4106e9e0626SOleksandr Tymoshenko 4116e9e0626SOleksandr Tymoshenko /* Clear old interrupt conditions for this host channel. */ 4126e9e0626SOleksandr Tymoshenko writel(0x3fff, &hc_regs->hcint); 4136e9e0626SOleksandr Tymoshenko 4146e9e0626SOleksandr Tymoshenko /* 4156e9e0626SOleksandr Tymoshenko * Program the HCCHARn register with the endpoint characteristics 4166e9e0626SOleksandr Tymoshenko * for the current transfer. 4176e9e0626SOleksandr Tymoshenko */ 4186e9e0626SOleksandr Tymoshenko writel(hcchar, &hc_regs->hcchar); 4196e9e0626SOleksandr Tymoshenko 4206e9e0626SOleksandr Tymoshenko /* Program the HCSPLIT register for SPLITs */ 4216e9e0626SOleksandr Tymoshenko writel(0, &hc_regs->hcsplt); 4226e9e0626SOleksandr Tymoshenko } 4236e9e0626SOleksandr Tymoshenko 4246e9e0626SOleksandr Tymoshenko /* 4256e9e0626SOleksandr Tymoshenko * DWC2 to USB API interface 4266e9e0626SOleksandr Tymoshenko */ 4276e9e0626SOleksandr Tymoshenko /* Direction: In ; Request: Status */ 4286e9e0626SOleksandr Tymoshenko static int dwc_otg_submit_rh_msg_in_status(struct usb_device *dev, void *buffer, 4296e9e0626SOleksandr Tymoshenko int txlen, struct devrequest *cmd) 4306e9e0626SOleksandr Tymoshenko { 4316e9e0626SOleksandr Tymoshenko uint32_t hprt0 = 0; 4326e9e0626SOleksandr Tymoshenko uint32_t port_status = 0; 4336e9e0626SOleksandr Tymoshenko uint32_t port_change = 0; 4346e9e0626SOleksandr Tymoshenko int len = 0; 4356e9e0626SOleksandr Tymoshenko int stat = 0; 4366e9e0626SOleksandr Tymoshenko 4376e9e0626SOleksandr Tymoshenko switch (cmd->requesttype & ~USB_DIR_IN) { 4386e9e0626SOleksandr Tymoshenko case 0: 4396e9e0626SOleksandr Tymoshenko *(uint16_t *)buffer = cpu_to_le16(1); 4406e9e0626SOleksandr Tymoshenko len = 2; 4416e9e0626SOleksandr Tymoshenko break; 4426e9e0626SOleksandr Tymoshenko case USB_RECIP_INTERFACE: 4436e9e0626SOleksandr Tymoshenko case USB_RECIP_ENDPOINT: 4446e9e0626SOleksandr Tymoshenko *(uint16_t *)buffer = cpu_to_le16(0); 4456e9e0626SOleksandr Tymoshenko len = 2; 4466e9e0626SOleksandr Tymoshenko break; 4476e9e0626SOleksandr Tymoshenko case USB_TYPE_CLASS: 4486e9e0626SOleksandr Tymoshenko *(uint32_t *)buffer = cpu_to_le32(0); 4496e9e0626SOleksandr Tymoshenko len = 4; 4506e9e0626SOleksandr Tymoshenko break; 4516e9e0626SOleksandr Tymoshenko case USB_RECIP_OTHER | USB_TYPE_CLASS: 4526e9e0626SOleksandr Tymoshenko hprt0 = readl(®s->hprt0); 4536e9e0626SOleksandr Tymoshenko if (hprt0 & DWC2_HPRT0_PRTCONNSTS) 4546e9e0626SOleksandr Tymoshenko port_status |= USB_PORT_STAT_CONNECTION; 4556e9e0626SOleksandr Tymoshenko if (hprt0 & DWC2_HPRT0_PRTENA) 4566e9e0626SOleksandr Tymoshenko port_status |= USB_PORT_STAT_ENABLE; 4576e9e0626SOleksandr Tymoshenko if (hprt0 & DWC2_HPRT0_PRTSUSP) 4586e9e0626SOleksandr Tymoshenko port_status |= USB_PORT_STAT_SUSPEND; 4596e9e0626SOleksandr Tymoshenko if (hprt0 & DWC2_HPRT0_PRTOVRCURRACT) 4606e9e0626SOleksandr Tymoshenko port_status |= USB_PORT_STAT_OVERCURRENT; 4616e9e0626SOleksandr Tymoshenko if (hprt0 & DWC2_HPRT0_PRTRST) 4626e9e0626SOleksandr Tymoshenko port_status |= USB_PORT_STAT_RESET; 4636e9e0626SOleksandr Tymoshenko if (hprt0 & DWC2_HPRT0_PRTPWR) 4646e9e0626SOleksandr Tymoshenko port_status |= USB_PORT_STAT_POWER; 4656e9e0626SOleksandr Tymoshenko 4666e9e0626SOleksandr Tymoshenko port_status |= USB_PORT_STAT_HIGH_SPEED; 4676e9e0626SOleksandr Tymoshenko 4686e9e0626SOleksandr Tymoshenko if (hprt0 & DWC2_HPRT0_PRTENCHNG) 4696e9e0626SOleksandr Tymoshenko port_change |= USB_PORT_STAT_C_ENABLE; 4706e9e0626SOleksandr Tymoshenko if (hprt0 & DWC2_HPRT0_PRTCONNDET) 4716e9e0626SOleksandr Tymoshenko port_change |= USB_PORT_STAT_C_CONNECTION; 4726e9e0626SOleksandr Tymoshenko if (hprt0 & DWC2_HPRT0_PRTOVRCURRCHNG) 4736e9e0626SOleksandr Tymoshenko port_change |= USB_PORT_STAT_C_OVERCURRENT; 4746e9e0626SOleksandr Tymoshenko 4756e9e0626SOleksandr Tymoshenko *(uint32_t *)buffer = cpu_to_le32(port_status | 4766e9e0626SOleksandr Tymoshenko (port_change << 16)); 4776e9e0626SOleksandr Tymoshenko len = 4; 4786e9e0626SOleksandr Tymoshenko break; 4796e9e0626SOleksandr Tymoshenko default: 4806e9e0626SOleksandr Tymoshenko puts("unsupported root hub command\n"); 4816e9e0626SOleksandr Tymoshenko stat = USB_ST_STALLED; 4826e9e0626SOleksandr Tymoshenko } 4836e9e0626SOleksandr Tymoshenko 4846e9e0626SOleksandr Tymoshenko dev->act_len = min(len, txlen); 4856e9e0626SOleksandr Tymoshenko dev->status = stat; 4866e9e0626SOleksandr Tymoshenko 4876e9e0626SOleksandr Tymoshenko return stat; 4886e9e0626SOleksandr Tymoshenko } 4896e9e0626SOleksandr Tymoshenko 4906e9e0626SOleksandr Tymoshenko /* Direction: In ; Request: Descriptor */ 4916e9e0626SOleksandr Tymoshenko static int dwc_otg_submit_rh_msg_in_descriptor(struct usb_device *dev, 4926e9e0626SOleksandr Tymoshenko void *buffer, int txlen, 4936e9e0626SOleksandr Tymoshenko struct devrequest *cmd) 4946e9e0626SOleksandr Tymoshenko { 4956e9e0626SOleksandr Tymoshenko unsigned char data[32]; 4966e9e0626SOleksandr Tymoshenko uint32_t dsc; 4976e9e0626SOleksandr Tymoshenko int len = 0; 4986e9e0626SOleksandr Tymoshenko int stat = 0; 4996e9e0626SOleksandr Tymoshenko uint16_t wValue = cpu_to_le16(cmd->value); 5006e9e0626SOleksandr Tymoshenko uint16_t wLength = cpu_to_le16(cmd->length); 5016e9e0626SOleksandr Tymoshenko 5026e9e0626SOleksandr Tymoshenko switch (cmd->requesttype & ~USB_DIR_IN) { 5036e9e0626SOleksandr Tymoshenko case 0: 5046e9e0626SOleksandr Tymoshenko switch (wValue & 0xff00) { 5056e9e0626SOleksandr Tymoshenko case 0x0100: /* device descriptor */ 506b4141195SMasahiro Yamada len = min3(txlen, (int)sizeof(root_hub_dev_des), (int)wLength); 5076e9e0626SOleksandr Tymoshenko memcpy(buffer, root_hub_dev_des, len); 5086e9e0626SOleksandr Tymoshenko break; 5096e9e0626SOleksandr Tymoshenko case 0x0200: /* configuration descriptor */ 510b4141195SMasahiro Yamada len = min3(txlen, (int)sizeof(root_hub_config_des), (int)wLength); 5116e9e0626SOleksandr Tymoshenko memcpy(buffer, root_hub_config_des, len); 5126e9e0626SOleksandr Tymoshenko break; 5136e9e0626SOleksandr Tymoshenko case 0x0300: /* string descriptors */ 5146e9e0626SOleksandr Tymoshenko switch (wValue & 0xff) { 5156e9e0626SOleksandr Tymoshenko case 0x00: 516b4141195SMasahiro Yamada len = min3(txlen, (int)sizeof(root_hub_str_index0), 517b4141195SMasahiro Yamada (int)wLength); 5186e9e0626SOleksandr Tymoshenko memcpy(buffer, root_hub_str_index0, len); 5196e9e0626SOleksandr Tymoshenko break; 5206e9e0626SOleksandr Tymoshenko case 0x01: 521b4141195SMasahiro Yamada len = min3(txlen, (int)sizeof(root_hub_str_index1), 522b4141195SMasahiro Yamada (int)wLength); 5236e9e0626SOleksandr Tymoshenko memcpy(buffer, root_hub_str_index1, len); 5246e9e0626SOleksandr Tymoshenko break; 5256e9e0626SOleksandr Tymoshenko } 5266e9e0626SOleksandr Tymoshenko break; 5276e9e0626SOleksandr Tymoshenko default: 5286e9e0626SOleksandr Tymoshenko stat = USB_ST_STALLED; 5296e9e0626SOleksandr Tymoshenko } 5306e9e0626SOleksandr Tymoshenko break; 5316e9e0626SOleksandr Tymoshenko 5326e9e0626SOleksandr Tymoshenko case USB_TYPE_CLASS: 5336e9e0626SOleksandr Tymoshenko /* Root port config, set 1 port and nothing else. */ 5346e9e0626SOleksandr Tymoshenko dsc = 0x00000001; 5356e9e0626SOleksandr Tymoshenko 5366e9e0626SOleksandr Tymoshenko data[0] = 9; /* min length; */ 5376e9e0626SOleksandr Tymoshenko data[1] = 0x29; 5386e9e0626SOleksandr Tymoshenko data[2] = dsc & RH_A_NDP; 5396e9e0626SOleksandr Tymoshenko data[3] = 0; 5406e9e0626SOleksandr Tymoshenko if (dsc & RH_A_PSM) 5416e9e0626SOleksandr Tymoshenko data[3] |= 0x1; 5426e9e0626SOleksandr Tymoshenko if (dsc & RH_A_NOCP) 5436e9e0626SOleksandr Tymoshenko data[3] |= 0x10; 5446e9e0626SOleksandr Tymoshenko else if (dsc & RH_A_OCPM) 5456e9e0626SOleksandr Tymoshenko data[3] |= 0x8; 5466e9e0626SOleksandr Tymoshenko 5476e9e0626SOleksandr Tymoshenko /* corresponds to data[4-7] */ 5486e9e0626SOleksandr Tymoshenko data[5] = (dsc & RH_A_POTPGT) >> 24; 5496e9e0626SOleksandr Tymoshenko data[7] = dsc & RH_B_DR; 5506e9e0626SOleksandr Tymoshenko if (data[2] < 7) { 5516e9e0626SOleksandr Tymoshenko data[8] = 0xff; 5526e9e0626SOleksandr Tymoshenko } else { 5536e9e0626SOleksandr Tymoshenko data[0] += 2; 5546e9e0626SOleksandr Tymoshenko data[8] = (dsc & RH_B_DR) >> 8; 5556e9e0626SOleksandr Tymoshenko data[9] = 0xff; 5566e9e0626SOleksandr Tymoshenko data[10] = data[9]; 5576e9e0626SOleksandr Tymoshenko } 5586e9e0626SOleksandr Tymoshenko 559b4141195SMasahiro Yamada len = min3(txlen, (int)data[0], (int)wLength); 5606e9e0626SOleksandr Tymoshenko memcpy(buffer, data, len); 5616e9e0626SOleksandr Tymoshenko break; 5626e9e0626SOleksandr Tymoshenko default: 5636e9e0626SOleksandr Tymoshenko puts("unsupported root hub command\n"); 5646e9e0626SOleksandr Tymoshenko stat = USB_ST_STALLED; 5656e9e0626SOleksandr Tymoshenko } 5666e9e0626SOleksandr Tymoshenko 5676e9e0626SOleksandr Tymoshenko dev->act_len = min(len, txlen); 5686e9e0626SOleksandr Tymoshenko dev->status = stat; 5696e9e0626SOleksandr Tymoshenko 5706e9e0626SOleksandr Tymoshenko return stat; 5716e9e0626SOleksandr Tymoshenko } 5726e9e0626SOleksandr Tymoshenko 5736e9e0626SOleksandr Tymoshenko /* Direction: In ; Request: Configuration */ 5746e9e0626SOleksandr Tymoshenko static int dwc_otg_submit_rh_msg_in_configuration(struct usb_device *dev, 5756e9e0626SOleksandr Tymoshenko void *buffer, int txlen, 5766e9e0626SOleksandr Tymoshenko struct devrequest *cmd) 5776e9e0626SOleksandr Tymoshenko { 5786e9e0626SOleksandr Tymoshenko int len = 0; 5796e9e0626SOleksandr Tymoshenko int stat = 0; 5806e9e0626SOleksandr Tymoshenko 5816e9e0626SOleksandr Tymoshenko switch (cmd->requesttype & ~USB_DIR_IN) { 5826e9e0626SOleksandr Tymoshenko case 0: 5836e9e0626SOleksandr Tymoshenko *(uint8_t *)buffer = 0x01; 5846e9e0626SOleksandr Tymoshenko len = 1; 5856e9e0626SOleksandr Tymoshenko break; 5866e9e0626SOleksandr Tymoshenko default: 5876e9e0626SOleksandr Tymoshenko puts("unsupported root hub command\n"); 5886e9e0626SOleksandr Tymoshenko stat = USB_ST_STALLED; 5896e9e0626SOleksandr Tymoshenko } 5906e9e0626SOleksandr Tymoshenko 5916e9e0626SOleksandr Tymoshenko dev->act_len = min(len, txlen); 5926e9e0626SOleksandr Tymoshenko dev->status = stat; 5936e9e0626SOleksandr Tymoshenko 5946e9e0626SOleksandr Tymoshenko return stat; 5956e9e0626SOleksandr Tymoshenko } 5966e9e0626SOleksandr Tymoshenko 5976e9e0626SOleksandr Tymoshenko /* Direction: In */ 5986e9e0626SOleksandr Tymoshenko static int dwc_otg_submit_rh_msg_in(struct usb_device *dev, 5996e9e0626SOleksandr Tymoshenko void *buffer, int txlen, 6006e9e0626SOleksandr Tymoshenko struct devrequest *cmd) 6016e9e0626SOleksandr Tymoshenko { 6026e9e0626SOleksandr Tymoshenko switch (cmd->request) { 6036e9e0626SOleksandr Tymoshenko case USB_REQ_GET_STATUS: 6046e9e0626SOleksandr Tymoshenko return dwc_otg_submit_rh_msg_in_status(dev, buffer, 6056e9e0626SOleksandr Tymoshenko txlen, cmd); 6066e9e0626SOleksandr Tymoshenko case USB_REQ_GET_DESCRIPTOR: 6076e9e0626SOleksandr Tymoshenko return dwc_otg_submit_rh_msg_in_descriptor(dev, buffer, 6086e9e0626SOleksandr Tymoshenko txlen, cmd); 6096e9e0626SOleksandr Tymoshenko case USB_REQ_GET_CONFIGURATION: 6106e9e0626SOleksandr Tymoshenko return dwc_otg_submit_rh_msg_in_configuration(dev, buffer, 6116e9e0626SOleksandr Tymoshenko txlen, cmd); 6126e9e0626SOleksandr Tymoshenko default: 6136e9e0626SOleksandr Tymoshenko puts("unsupported root hub command\n"); 6146e9e0626SOleksandr Tymoshenko return USB_ST_STALLED; 6156e9e0626SOleksandr Tymoshenko } 6166e9e0626SOleksandr Tymoshenko } 6176e9e0626SOleksandr Tymoshenko 6186e9e0626SOleksandr Tymoshenko /* Direction: Out */ 6196e9e0626SOleksandr Tymoshenko static int dwc_otg_submit_rh_msg_out(struct usb_device *dev, 6206e9e0626SOleksandr Tymoshenko void *buffer, int txlen, 6216e9e0626SOleksandr Tymoshenko struct devrequest *cmd) 6226e9e0626SOleksandr Tymoshenko { 6236e9e0626SOleksandr Tymoshenko int len = 0; 6246e9e0626SOleksandr Tymoshenko int stat = 0; 6256e9e0626SOleksandr Tymoshenko uint16_t bmrtype_breq = cmd->requesttype | (cmd->request << 8); 6266e9e0626SOleksandr Tymoshenko uint16_t wValue = cpu_to_le16(cmd->value); 6276e9e0626SOleksandr Tymoshenko 6286e9e0626SOleksandr Tymoshenko switch (bmrtype_breq & ~USB_DIR_IN) { 6296e9e0626SOleksandr Tymoshenko case (USB_REQ_CLEAR_FEATURE << 8) | USB_RECIP_ENDPOINT: 6306e9e0626SOleksandr Tymoshenko case (USB_REQ_CLEAR_FEATURE << 8) | USB_TYPE_CLASS: 6316e9e0626SOleksandr Tymoshenko break; 6326e9e0626SOleksandr Tymoshenko 6336e9e0626SOleksandr Tymoshenko case (USB_REQ_CLEAR_FEATURE << 8) | USB_RECIP_OTHER | USB_TYPE_CLASS: 6346e9e0626SOleksandr Tymoshenko switch (wValue) { 6356e9e0626SOleksandr Tymoshenko case USB_PORT_FEAT_C_CONNECTION: 6366e9e0626SOleksandr Tymoshenko setbits_le32(®s->hprt0, DWC2_HPRT0_PRTCONNDET); 6376e9e0626SOleksandr Tymoshenko break; 6386e9e0626SOleksandr Tymoshenko } 6396e9e0626SOleksandr Tymoshenko break; 6406e9e0626SOleksandr Tymoshenko 6416e9e0626SOleksandr Tymoshenko case (USB_REQ_SET_FEATURE << 8) | USB_RECIP_OTHER | USB_TYPE_CLASS: 6426e9e0626SOleksandr Tymoshenko switch (wValue) { 6436e9e0626SOleksandr Tymoshenko case USB_PORT_FEAT_SUSPEND: 6446e9e0626SOleksandr Tymoshenko break; 6456e9e0626SOleksandr Tymoshenko 6466e9e0626SOleksandr Tymoshenko case USB_PORT_FEAT_RESET: 6476e9e0626SOleksandr Tymoshenko clrsetbits_le32(®s->hprt0, DWC2_HPRT0_PRTENA | 6486e9e0626SOleksandr Tymoshenko DWC2_HPRT0_PRTCONNDET | 6496e9e0626SOleksandr Tymoshenko DWC2_HPRT0_PRTENCHNG | 6506e9e0626SOleksandr Tymoshenko DWC2_HPRT0_PRTOVRCURRCHNG, 6516e9e0626SOleksandr Tymoshenko DWC2_HPRT0_PRTRST); 6526e9e0626SOleksandr Tymoshenko mdelay(50); 6536e9e0626SOleksandr Tymoshenko clrbits_le32(®s->hprt0, DWC2_HPRT0_PRTRST); 6546e9e0626SOleksandr Tymoshenko break; 6556e9e0626SOleksandr Tymoshenko 6566e9e0626SOleksandr Tymoshenko case USB_PORT_FEAT_POWER: 6576e9e0626SOleksandr Tymoshenko clrsetbits_le32(®s->hprt0, DWC2_HPRT0_PRTENA | 6586e9e0626SOleksandr Tymoshenko DWC2_HPRT0_PRTCONNDET | 6596e9e0626SOleksandr Tymoshenko DWC2_HPRT0_PRTENCHNG | 6606e9e0626SOleksandr Tymoshenko DWC2_HPRT0_PRTOVRCURRCHNG, 6616e9e0626SOleksandr Tymoshenko DWC2_HPRT0_PRTRST); 6626e9e0626SOleksandr Tymoshenko break; 6636e9e0626SOleksandr Tymoshenko 6646e9e0626SOleksandr Tymoshenko case USB_PORT_FEAT_ENABLE: 6656e9e0626SOleksandr Tymoshenko break; 6666e9e0626SOleksandr Tymoshenko } 6676e9e0626SOleksandr Tymoshenko break; 6686e9e0626SOleksandr Tymoshenko case (USB_REQ_SET_ADDRESS << 8): 6696e9e0626SOleksandr Tymoshenko root_hub_devnum = wValue; 6706e9e0626SOleksandr Tymoshenko break; 6716e9e0626SOleksandr Tymoshenko case (USB_REQ_SET_CONFIGURATION << 8): 6726e9e0626SOleksandr Tymoshenko break; 6736e9e0626SOleksandr Tymoshenko default: 6746e9e0626SOleksandr Tymoshenko puts("unsupported root hub command\n"); 6756e9e0626SOleksandr Tymoshenko stat = USB_ST_STALLED; 6766e9e0626SOleksandr Tymoshenko } 6776e9e0626SOleksandr Tymoshenko 6786e9e0626SOleksandr Tymoshenko len = min(len, txlen); 6796e9e0626SOleksandr Tymoshenko 6806e9e0626SOleksandr Tymoshenko dev->act_len = len; 6816e9e0626SOleksandr Tymoshenko dev->status = stat; 6826e9e0626SOleksandr Tymoshenko 6836e9e0626SOleksandr Tymoshenko return stat; 6846e9e0626SOleksandr Tymoshenko } 6856e9e0626SOleksandr Tymoshenko 6866e9e0626SOleksandr Tymoshenko static int dwc_otg_submit_rh_msg(struct usb_device *dev, unsigned long pipe, 6876e9e0626SOleksandr Tymoshenko void *buffer, int txlen, 6886e9e0626SOleksandr Tymoshenko struct devrequest *cmd) 6896e9e0626SOleksandr Tymoshenko { 6906e9e0626SOleksandr Tymoshenko int stat = 0; 6916e9e0626SOleksandr Tymoshenko 6926e9e0626SOleksandr Tymoshenko if (usb_pipeint(pipe)) { 6936e9e0626SOleksandr Tymoshenko puts("Root-Hub submit IRQ: NOT implemented\n"); 6946e9e0626SOleksandr Tymoshenko return 0; 6956e9e0626SOleksandr Tymoshenko } 6966e9e0626SOleksandr Tymoshenko 6976e9e0626SOleksandr Tymoshenko if (cmd->requesttype & USB_DIR_IN) 6986e9e0626SOleksandr Tymoshenko stat = dwc_otg_submit_rh_msg_in(dev, buffer, txlen, cmd); 6996e9e0626SOleksandr Tymoshenko else 7006e9e0626SOleksandr Tymoshenko stat = dwc_otg_submit_rh_msg_out(dev, buffer, txlen, cmd); 7016e9e0626SOleksandr Tymoshenko 7026e9e0626SOleksandr Tymoshenko mdelay(1); 7036e9e0626SOleksandr Tymoshenko 7046e9e0626SOleksandr Tymoshenko return stat; 7056e9e0626SOleksandr Tymoshenko } 7066e9e0626SOleksandr Tymoshenko 7074a1d21fcSStephen Warren int wait_for_chhltd(uint32_t *sub, int *toggle) 7084a1d21fcSStephen Warren { 7094a1d21fcSStephen Warren const uint32_t hcint_comp_hlt_ack = DWC2_HCINT_XFERCOMP | 7104a1d21fcSStephen Warren DWC2_HCINT_CHHLTD | DWC2_HCINT_ACK; 7114a1d21fcSStephen Warren struct dwc2_hc_regs *hc_regs = ®s->hc_regs[DWC2_HC_CHANNEL]; 7124a1d21fcSStephen Warren int ret; 7134a1d21fcSStephen Warren uint32_t hcint, hctsiz; 7144a1d21fcSStephen Warren 7154a1d21fcSStephen Warren ret = wait_for_bit(&hc_regs->hcint, DWC2_HCINT_CHHLTD, true); 7164a1d21fcSStephen Warren if (ret) 7174a1d21fcSStephen Warren return ret; 7184a1d21fcSStephen Warren 7194a1d21fcSStephen Warren hcint = readl(&hc_regs->hcint); 7204a1d21fcSStephen Warren if (hcint != hcint_comp_hlt_ack) { 7214a1d21fcSStephen Warren debug("%s: Error (HCINT=%08x)\n", __func__, hcint); 7224a1d21fcSStephen Warren return -EINVAL; 7234a1d21fcSStephen Warren } 7244a1d21fcSStephen Warren 7254a1d21fcSStephen Warren hctsiz = readl(&hc_regs->hctsiz); 7264a1d21fcSStephen Warren *sub = (hctsiz & DWC2_HCTSIZ_XFERSIZE_MASK) >> 7274a1d21fcSStephen Warren DWC2_HCTSIZ_XFERSIZE_OFFSET; 7284a1d21fcSStephen Warren if (toggle) 7294a1d21fcSStephen Warren *toggle = (hctsiz & DWC2_HCTSIZ_PID_MASK) >> 7304a1d21fcSStephen Warren DWC2_HCTSIZ_PID_OFFSET; 7314a1d21fcSStephen Warren 7324a1d21fcSStephen Warren debug("%s: sub=%u toggle=%d\n", __func__, *sub, toggle ? *toggle : -1); 7334a1d21fcSStephen Warren 7344a1d21fcSStephen Warren return 0; 7354a1d21fcSStephen Warren } 7364a1d21fcSStephen Warren 737*7b5e504dSStephen Warren static int dwc2_eptype[] = { 738*7b5e504dSStephen Warren DWC2_HCCHAR_EPTYPE_ISOC, 739*7b5e504dSStephen Warren DWC2_HCCHAR_EPTYPE_INTR, 740*7b5e504dSStephen Warren DWC2_HCCHAR_EPTYPE_CONTROL, 741*7b5e504dSStephen Warren DWC2_HCCHAR_EPTYPE_BULK, 742*7b5e504dSStephen Warren }; 743*7b5e504dSStephen Warren 744*7b5e504dSStephen Warren int chunk_msg(struct usb_device *dev, unsigned long pipe, int *pid, int in, 745*7b5e504dSStephen Warren void *buffer, int len) 7466e9e0626SOleksandr Tymoshenko { 747*7b5e504dSStephen Warren struct dwc2_hc_regs *hc_regs = ®s->hc_regs[DWC2_HC_CHANNEL]; 7486e9e0626SOleksandr Tymoshenko int devnum = usb_pipedevice(pipe); 7496e9e0626SOleksandr Tymoshenko int ep = usb_pipeendpoint(pipe); 7506e9e0626SOleksandr Tymoshenko int max = usb_maxpacket(dev, pipe); 751*7b5e504dSStephen Warren int eptype = dwc2_eptype[usb_pipetype(pipe)]; 7526e9e0626SOleksandr Tymoshenko int done = 0; 7534a1d21fcSStephen Warren int ret; 7544a1d21fcSStephen Warren uint32_t sub; 7556e9e0626SOleksandr Tymoshenko uint32_t xfer_len; 7566e9e0626SOleksandr Tymoshenko uint32_t num_packets; 7576e9e0626SOleksandr Tymoshenko int stop_transfer = 0; 7586e9e0626SOleksandr Tymoshenko 759*7b5e504dSStephen Warren debug("%s: msg: pipe %lx pid %d in %d len %d\n", __func__, pipe, *pid, 760*7b5e504dSStephen Warren in, len); 7616e9e0626SOleksandr Tymoshenko 7626e9e0626SOleksandr Tymoshenko if (len > DWC2_DATA_BUF_SIZE) { 7636e9e0626SOleksandr Tymoshenko printf("%s: %d is more then available buffer size (%d)\n", 7646e9e0626SOleksandr Tymoshenko __func__, len, DWC2_DATA_BUF_SIZE); 7656e9e0626SOleksandr Tymoshenko dev->status = 0; 7666e9e0626SOleksandr Tymoshenko dev->act_len = 0; 7676e9e0626SOleksandr Tymoshenko return -EINVAL; 7686e9e0626SOleksandr Tymoshenko } 7696e9e0626SOleksandr Tymoshenko 7706e9e0626SOleksandr Tymoshenko while ((done < len) && !stop_transfer) { 7716e9e0626SOleksandr Tymoshenko /* Initialize channel */ 772*7b5e504dSStephen Warren dwc_otg_hc_init(regs, DWC2_HC_CHANNEL, devnum, ep, in, eptype, 773*7b5e504dSStephen Warren max); 7746e9e0626SOleksandr Tymoshenko 7756e9e0626SOleksandr Tymoshenko xfer_len = len - done; 7766e9e0626SOleksandr Tymoshenko /* Make sure that xfer_len is a multiple of max packet size. */ 7776e9e0626SOleksandr Tymoshenko if (xfer_len > CONFIG_DWC2_MAX_TRANSFER_SIZE) 7786e9e0626SOleksandr Tymoshenko xfer_len = CONFIG_DWC2_MAX_TRANSFER_SIZE - max + 1; 7796e9e0626SOleksandr Tymoshenko 7806e9e0626SOleksandr Tymoshenko if (xfer_len > 0) { 7816e9e0626SOleksandr Tymoshenko num_packets = (xfer_len + max - 1) / max; 7826e9e0626SOleksandr Tymoshenko if (num_packets > CONFIG_DWC2_MAX_PACKET_COUNT) { 7836e9e0626SOleksandr Tymoshenko num_packets = CONFIG_DWC2_MAX_PACKET_COUNT; 7846e9e0626SOleksandr Tymoshenko xfer_len = num_packets * max; 7856e9e0626SOleksandr Tymoshenko } 7866e9e0626SOleksandr Tymoshenko } else { 7876e9e0626SOleksandr Tymoshenko num_packets = 1; 7886e9e0626SOleksandr Tymoshenko } 7896e9e0626SOleksandr Tymoshenko 790*7b5e504dSStephen Warren if (in) 7916e9e0626SOleksandr Tymoshenko xfer_len = num_packets * max; 7926e9e0626SOleksandr Tymoshenko 793*7b5e504dSStephen Warren debug("%s: chunk: pid %d xfer_len %u pkts %u\n", __func__, 794*7b5e504dSStephen Warren *pid, xfer_len, num_packets); 795*7b5e504dSStephen Warren 7966e9e0626SOleksandr Tymoshenko writel((xfer_len << DWC2_HCTSIZ_XFERSIZE_OFFSET) | 7976e9e0626SOleksandr Tymoshenko (num_packets << DWC2_HCTSIZ_PKTCNT_OFFSET) | 798*7b5e504dSStephen Warren (*pid << DWC2_HCTSIZ_PID_OFFSET), 7996e9e0626SOleksandr Tymoshenko &hc_regs->hctsiz); 8006e9e0626SOleksandr Tymoshenko 8016e9e0626SOleksandr Tymoshenko memcpy(aligned_buffer, (char *)buffer + done, len - done); 8026e9e0626SOleksandr Tymoshenko writel((uint32_t)aligned_buffer, &hc_regs->hcdma); 8036e9e0626SOleksandr Tymoshenko 8046e9e0626SOleksandr Tymoshenko /* Set host channel enable after all other setup is complete. */ 8056e9e0626SOleksandr Tymoshenko clrsetbits_le32(&hc_regs->hcchar, DWC2_HCCHAR_MULTICNT_MASK | 8066e9e0626SOleksandr Tymoshenko DWC2_HCCHAR_CHEN | DWC2_HCCHAR_CHDIS, 8076e9e0626SOleksandr Tymoshenko (1 << DWC2_HCCHAR_MULTICNT_OFFSET) | 8086e9e0626SOleksandr Tymoshenko DWC2_HCCHAR_CHEN); 8096e9e0626SOleksandr Tymoshenko 810*7b5e504dSStephen Warren ret = wait_for_chhltd(&sub, pid); 8114a1d21fcSStephen Warren if (ret) { 8124a1d21fcSStephen Warren stop_transfer = 1; 8134a1d21fcSStephen Warren break; 8144a1d21fcSStephen Warren } 8156e9e0626SOleksandr Tymoshenko 8166e9e0626SOleksandr Tymoshenko done += xfer_len; 817*7b5e504dSStephen Warren if (in) { 8186e9e0626SOleksandr Tymoshenko done -= sub; 8194a1d21fcSStephen Warren if (sub) 8206e9e0626SOleksandr Tymoshenko stop_transfer = 1; 8216e9e0626SOleksandr Tymoshenko } 8226e9e0626SOleksandr Tymoshenko } 8236e9e0626SOleksandr Tymoshenko 824*7b5e504dSStephen Warren if (done && in) 8256e9e0626SOleksandr Tymoshenko memcpy(buffer, aligned_buffer, done); 8266e9e0626SOleksandr Tymoshenko 8276e9e0626SOleksandr Tymoshenko writel(0, &hc_regs->hcintmsk); 8286e9e0626SOleksandr Tymoshenko writel(0xFFFFFFFF, &hc_regs->hcint); 8296e9e0626SOleksandr Tymoshenko 8306e9e0626SOleksandr Tymoshenko dev->status = 0; 8316e9e0626SOleksandr Tymoshenko dev->act_len = done; 8326e9e0626SOleksandr Tymoshenko 8336e9e0626SOleksandr Tymoshenko return 0; 8346e9e0626SOleksandr Tymoshenko } 8356e9e0626SOleksandr Tymoshenko 836*7b5e504dSStephen Warren /* U-Boot USB transmission interface */ 837*7b5e504dSStephen Warren int submit_bulk_msg(struct usb_device *dev, unsigned long pipe, void *buffer, 838*7b5e504dSStephen Warren int len) 839*7b5e504dSStephen Warren { 840*7b5e504dSStephen Warren int devnum = usb_pipedevice(pipe); 841*7b5e504dSStephen Warren int ep = usb_pipeendpoint(pipe); 842*7b5e504dSStephen Warren 843*7b5e504dSStephen Warren if (devnum == root_hub_devnum) { 844*7b5e504dSStephen Warren dev->status = 0; 845*7b5e504dSStephen Warren return -EINVAL; 846*7b5e504dSStephen Warren } 847*7b5e504dSStephen Warren 848*7b5e504dSStephen Warren return chunk_msg(dev, pipe, &bulk_data_toggle[devnum][ep], 849*7b5e504dSStephen Warren usb_pipein(pipe), buffer, len); 850*7b5e504dSStephen Warren } 851*7b5e504dSStephen Warren 8526e9e0626SOleksandr Tymoshenko int submit_control_msg(struct usb_device *dev, unsigned long pipe, void *buffer, 8536e9e0626SOleksandr Tymoshenko int len, struct devrequest *setup) 8546e9e0626SOleksandr Tymoshenko { 8556e9e0626SOleksandr Tymoshenko struct dwc2_hc_regs *hc_regs = ®s->hc_regs[DWC2_HC_CHANNEL]; 8566e9e0626SOleksandr Tymoshenko int done = 0; 8576e9e0626SOleksandr Tymoshenko int devnum = usb_pipedevice(pipe); 8586e9e0626SOleksandr Tymoshenko int ep = usb_pipeendpoint(pipe); 8596e9e0626SOleksandr Tymoshenko int max = usb_maxpacket(dev, pipe); 8604a1d21fcSStephen Warren int ret; 8614a1d21fcSStephen Warren uint32_t sub; 8626e9e0626SOleksandr Tymoshenko 8636e9e0626SOleksandr Tymoshenko /* For CONTROL endpoint pid should start with DATA1 */ 8646e9e0626SOleksandr Tymoshenko int status_direction; 8656e9e0626SOleksandr Tymoshenko 8666e9e0626SOleksandr Tymoshenko if (devnum == root_hub_devnum) { 8676e9e0626SOleksandr Tymoshenko dev->status = 0; 8686e9e0626SOleksandr Tymoshenko dev->speed = USB_SPEED_HIGH; 8696e9e0626SOleksandr Tymoshenko return dwc_otg_submit_rh_msg(dev, pipe, buffer, len, setup); 8706e9e0626SOleksandr Tymoshenko } 8716e9e0626SOleksandr Tymoshenko 8726e9e0626SOleksandr Tymoshenko if (len > DWC2_DATA_BUF_SIZE) { 8736e9e0626SOleksandr Tymoshenko printf("%s: %d is more then available buffer size(%d)\n", 8746e9e0626SOleksandr Tymoshenko __func__, len, DWC2_DATA_BUF_SIZE); 8756e9e0626SOleksandr Tymoshenko dev->status = 0; 8766e9e0626SOleksandr Tymoshenko dev->act_len = 0; 8776e9e0626SOleksandr Tymoshenko return -EINVAL; 8786e9e0626SOleksandr Tymoshenko } 8796e9e0626SOleksandr Tymoshenko 8806e9e0626SOleksandr Tymoshenko /* Initialize channel, OUT for setup buffer */ 8816e9e0626SOleksandr Tymoshenko dwc_otg_hc_init(regs, DWC2_HC_CHANNEL, devnum, ep, 0, 8826e9e0626SOleksandr Tymoshenko DWC2_HCCHAR_EPTYPE_CONTROL, max); 8836e9e0626SOleksandr Tymoshenko 8846e9e0626SOleksandr Tymoshenko /* SETUP stage */ 8856e9e0626SOleksandr Tymoshenko writel((8 << DWC2_HCTSIZ_XFERSIZE_OFFSET) | 8866e9e0626SOleksandr Tymoshenko (1 << DWC2_HCTSIZ_PKTCNT_OFFSET) | 8876e9e0626SOleksandr Tymoshenko (DWC2_HC_PID_SETUP << DWC2_HCTSIZ_PID_OFFSET), 8886e9e0626SOleksandr Tymoshenko &hc_regs->hctsiz); 8896e9e0626SOleksandr Tymoshenko 8906e9e0626SOleksandr Tymoshenko writel((uint32_t)setup, &hc_regs->hcdma); 8916e9e0626SOleksandr Tymoshenko 8926e9e0626SOleksandr Tymoshenko /* Set host channel enable after all other setup is complete. */ 8936e9e0626SOleksandr Tymoshenko clrsetbits_le32(&hc_regs->hcchar, DWC2_HCCHAR_MULTICNT_MASK | 8946e9e0626SOleksandr Tymoshenko DWC2_HCCHAR_CHEN | DWC2_HCCHAR_CHDIS, 8956e9e0626SOleksandr Tymoshenko (1 << DWC2_HCCHAR_MULTICNT_OFFSET) | DWC2_HCCHAR_CHEN); 8966e9e0626SOleksandr Tymoshenko 8974a1d21fcSStephen Warren ret = wait_for_chhltd(&sub, NULL); 8984a1d21fcSStephen Warren if (ret) { 8996e9e0626SOleksandr Tymoshenko dev->status = 0; 9006e9e0626SOleksandr Tymoshenko dev->act_len = 0; 9016e9e0626SOleksandr Tymoshenko return -EINVAL; 9026e9e0626SOleksandr Tymoshenko } 9036e9e0626SOleksandr Tymoshenko 9046e9e0626SOleksandr Tymoshenko /* Clear interrupts */ 9056e9e0626SOleksandr Tymoshenko writel(0, &hc_regs->hcintmsk); 9066e9e0626SOleksandr Tymoshenko writel(0xFFFFFFFF, &hc_regs->hcint); 9076e9e0626SOleksandr Tymoshenko 9086e9e0626SOleksandr Tymoshenko if (buffer) { 9096e9e0626SOleksandr Tymoshenko /* DATA stage */ 9106e9e0626SOleksandr Tymoshenko dwc_otg_hc_init(regs, DWC2_HC_CHANNEL, devnum, ep, 9116e9e0626SOleksandr Tymoshenko usb_pipein(pipe), 9126e9e0626SOleksandr Tymoshenko DWC2_HCCHAR_EPTYPE_CONTROL, max); 9136e9e0626SOleksandr Tymoshenko 9146e9e0626SOleksandr Tymoshenko /* TODO: check if len < 64 */ 9156e9e0626SOleksandr Tymoshenko control_data_toggle[devnum][ep] = DWC2_HC_PID_DATA1; 9166e9e0626SOleksandr Tymoshenko writel((len << DWC2_HCTSIZ_XFERSIZE_OFFSET) | 9176e9e0626SOleksandr Tymoshenko (1 << DWC2_HCTSIZ_PKTCNT_OFFSET) | 9186e9e0626SOleksandr Tymoshenko (control_data_toggle[devnum][ep] << 9196e9e0626SOleksandr Tymoshenko DWC2_HCTSIZ_PID_OFFSET), 9206e9e0626SOleksandr Tymoshenko &hc_regs->hctsiz); 9216e9e0626SOleksandr Tymoshenko 9226e9e0626SOleksandr Tymoshenko writel((uint32_t)buffer, &hc_regs->hcdma); 9236e9e0626SOleksandr Tymoshenko 9246e9e0626SOleksandr Tymoshenko /* Set host channel enable after all other setup is complete */ 9256e9e0626SOleksandr Tymoshenko clrsetbits_le32(&hc_regs->hcchar, DWC2_HCCHAR_MULTICNT_MASK | 9266e9e0626SOleksandr Tymoshenko DWC2_HCCHAR_CHEN | DWC2_HCCHAR_CHDIS, 9276e9e0626SOleksandr Tymoshenko (1 << DWC2_HCCHAR_MULTICNT_OFFSET) | 9286e9e0626SOleksandr Tymoshenko DWC2_HCCHAR_CHEN); 9296e9e0626SOleksandr Tymoshenko 9304a1d21fcSStephen Warren ret = wait_for_chhltd(&sub, &control_data_toggle[devnum][ep]); 9314a1d21fcSStephen Warren if (ret) { 9324a1d21fcSStephen Warren dev->status = 0; 9334a1d21fcSStephen Warren dev->act_len = 0; 9344a1d21fcSStephen Warren return -EINVAL; 9354a1d21fcSStephen Warren } 9366e9e0626SOleksandr Tymoshenko 9376e9e0626SOleksandr Tymoshenko done = len; 9386e9e0626SOleksandr Tymoshenko if (usb_pipein(pipe)) 9396e9e0626SOleksandr Tymoshenko done -= sub; 9406e9e0626SOleksandr Tymoshenko } /* End of DATA stage */ 9416e9e0626SOleksandr Tymoshenko 9426e9e0626SOleksandr Tymoshenko /* STATUS stage */ 9436e9e0626SOleksandr Tymoshenko if ((len == 0) || usb_pipeout(pipe)) 9446e9e0626SOleksandr Tymoshenko status_direction = 1; 9456e9e0626SOleksandr Tymoshenko else 9466e9e0626SOleksandr Tymoshenko status_direction = 0; 9476e9e0626SOleksandr Tymoshenko 9486e9e0626SOleksandr Tymoshenko dwc_otg_hc_init(regs, DWC2_HC_CHANNEL, devnum, ep, 9496e9e0626SOleksandr Tymoshenko status_direction, DWC2_HCCHAR_EPTYPE_CONTROL, max); 9506e9e0626SOleksandr Tymoshenko 9516e9e0626SOleksandr Tymoshenko writel((1 << DWC2_HCTSIZ_PKTCNT_OFFSET) | 9526e9e0626SOleksandr Tymoshenko (DWC2_HC_PID_DATA1 << DWC2_HCTSIZ_PID_OFFSET), 9536e9e0626SOleksandr Tymoshenko &hc_regs->hctsiz); 9546e9e0626SOleksandr Tymoshenko 9556e9e0626SOleksandr Tymoshenko writel((uint32_t)status_buffer, &hc_regs->hcdma); 9566e9e0626SOleksandr Tymoshenko 9576e9e0626SOleksandr Tymoshenko /* Set host channel enable after all other setup is complete. */ 9586e9e0626SOleksandr Tymoshenko clrsetbits_le32(&hc_regs->hcchar, DWC2_HCCHAR_MULTICNT_MASK | 9596e9e0626SOleksandr Tymoshenko DWC2_HCCHAR_CHEN | DWC2_HCCHAR_CHDIS, 9606e9e0626SOleksandr Tymoshenko (1 << DWC2_HCCHAR_MULTICNT_OFFSET) | DWC2_HCCHAR_CHEN); 9616e9e0626SOleksandr Tymoshenko 9624a1d21fcSStephen Warren ret = wait_for_chhltd(&sub, NULL); 9634a1d21fcSStephen Warren if (ret) { 9644a1d21fcSStephen Warren dev->status = 0; 9654a1d21fcSStephen Warren dev->act_len = 0; 9664a1d21fcSStephen Warren return -EINVAL; 9676e9e0626SOleksandr Tymoshenko } 9686e9e0626SOleksandr Tymoshenko 9696e9e0626SOleksandr Tymoshenko dev->act_len = done; 9706e9e0626SOleksandr Tymoshenko dev->status = 0; 9716e9e0626SOleksandr Tymoshenko 9724a1d21fcSStephen Warren return 0; 9736e9e0626SOleksandr Tymoshenko } 9746e9e0626SOleksandr Tymoshenko 9756e9e0626SOleksandr Tymoshenko int submit_int_msg(struct usb_device *dev, unsigned long pipe, void *buffer, 9766e9e0626SOleksandr Tymoshenko int len, int interval) 9776e9e0626SOleksandr Tymoshenko { 9786e9e0626SOleksandr Tymoshenko printf("dev = %p pipe = %#lx buf = %p size = %d int = %d\n", 9796e9e0626SOleksandr Tymoshenko dev, pipe, buffer, len, interval); 9806e9e0626SOleksandr Tymoshenko return -ENOSYS; 9816e9e0626SOleksandr Tymoshenko } 9826e9e0626SOleksandr Tymoshenko 9836e9e0626SOleksandr Tymoshenko /* U-Boot USB control interface */ 9846e9e0626SOleksandr Tymoshenko int usb_lowlevel_init(int index, enum usb_init_type init, void **controller) 9856e9e0626SOleksandr Tymoshenko { 9866e9e0626SOleksandr Tymoshenko uint32_t snpsid; 9876e9e0626SOleksandr Tymoshenko int i, j; 9886e9e0626SOleksandr Tymoshenko 9896e9e0626SOleksandr Tymoshenko root_hub_devnum = 0; 9906e9e0626SOleksandr Tymoshenko 9916e9e0626SOleksandr Tymoshenko snpsid = readl(®s->gsnpsid); 9926e9e0626SOleksandr Tymoshenko printf("Core Release: %x.%03x\n", snpsid >> 12 & 0xf, snpsid & 0xfff); 9936e9e0626SOleksandr Tymoshenko 9946e9e0626SOleksandr Tymoshenko if ((snpsid & DWC2_SNPSID_DEVID_MASK) != DWC2_SNPSID_DEVID_VER_2xx) { 9956e9e0626SOleksandr Tymoshenko printf("SNPSID invalid (not DWC2 OTG device): %08x\n", snpsid); 9966e9e0626SOleksandr Tymoshenko return -ENODEV; 9976e9e0626SOleksandr Tymoshenko } 9986e9e0626SOleksandr Tymoshenko 9996e9e0626SOleksandr Tymoshenko dwc_otg_core_init(regs); 10006e9e0626SOleksandr Tymoshenko dwc_otg_core_host_init(regs); 10016e9e0626SOleksandr Tymoshenko 10026e9e0626SOleksandr Tymoshenko clrsetbits_le32(®s->hprt0, DWC2_HPRT0_PRTENA | 10036e9e0626SOleksandr Tymoshenko DWC2_HPRT0_PRTCONNDET | DWC2_HPRT0_PRTENCHNG | 10046e9e0626SOleksandr Tymoshenko DWC2_HPRT0_PRTOVRCURRCHNG, 10056e9e0626SOleksandr Tymoshenko DWC2_HPRT0_PRTRST); 10066e9e0626SOleksandr Tymoshenko mdelay(50); 10076e9e0626SOleksandr Tymoshenko clrbits_le32(®s->hprt0, DWC2_HPRT0_PRTENA | DWC2_HPRT0_PRTCONNDET | 10086e9e0626SOleksandr Tymoshenko DWC2_HPRT0_PRTENCHNG | DWC2_HPRT0_PRTOVRCURRCHNG | 10096e9e0626SOleksandr Tymoshenko DWC2_HPRT0_PRTRST); 10106e9e0626SOleksandr Tymoshenko 10116e9e0626SOleksandr Tymoshenko for (i = 0; i < MAX_DEVICE; i++) { 10126e9e0626SOleksandr Tymoshenko for (j = 0; j < MAX_ENDPOINT; j++) { 10136e9e0626SOleksandr Tymoshenko control_data_toggle[i][j] = DWC2_HC_PID_DATA1; 10146e9e0626SOleksandr Tymoshenko bulk_data_toggle[i][j] = DWC2_HC_PID_DATA0; 10156e9e0626SOleksandr Tymoshenko } 10166e9e0626SOleksandr Tymoshenko } 10176e9e0626SOleksandr Tymoshenko 10186e9e0626SOleksandr Tymoshenko return 0; 10196e9e0626SOleksandr Tymoshenko } 10206e9e0626SOleksandr Tymoshenko 10216e9e0626SOleksandr Tymoshenko int usb_lowlevel_stop(int index) 10226e9e0626SOleksandr Tymoshenko { 10236e9e0626SOleksandr Tymoshenko /* Put everything in reset. */ 10246e9e0626SOleksandr Tymoshenko clrsetbits_le32(®s->hprt0, DWC2_HPRT0_PRTENA | 10256e9e0626SOleksandr Tymoshenko DWC2_HPRT0_PRTCONNDET | DWC2_HPRT0_PRTENCHNG | 10266e9e0626SOleksandr Tymoshenko DWC2_HPRT0_PRTOVRCURRCHNG, 10276e9e0626SOleksandr Tymoshenko DWC2_HPRT0_PRTRST); 10286e9e0626SOleksandr Tymoshenko return 0; 10296e9e0626SOleksandr Tymoshenko } 1030