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> 91966f56eSPatrick Delaunay #include <clk.h> 10f58a41e0SSimon Glass #include <dm.h> 116e9e0626SOleksandr Tymoshenko #include <errno.h> 121e03ec26SPatrick Delaunay #include <generic-phy.h> 136e9e0626SOleksandr Tymoshenko #include <malloc.h> 14cf92e05cSSimon Glass #include <memalign.h> 155c0beb5cSStephen Warren #include <phys2bus.h> 161966f56eSPatrick Delaunay #include <usb.h> 176e9e0626SOleksandr Tymoshenko #include <usbroothubdes.h> 18fd2cd662SMateusz Kulikowski #include <wait_bit.h> 196e9e0626SOleksandr Tymoshenko #include <asm/io.h> 205c735367SKever Yang #include <power/regulator.h> 21a1bebf37SLey Foon Tan #include <reset.h> 226e9e0626SOleksandr Tymoshenko 236e9e0626SOleksandr Tymoshenko #include "dwc2.h" 246e9e0626SOleksandr Tymoshenko 25b4fbd089SMarek Vasut DECLARE_GLOBAL_DATA_PTR; 26b4fbd089SMarek Vasut 276e9e0626SOleksandr Tymoshenko /* Use only HC channel 0. */ 286e9e0626SOleksandr Tymoshenko #define DWC2_HC_CHANNEL 0 296e9e0626SOleksandr Tymoshenko 306e9e0626SOleksandr Tymoshenko #define DWC2_STATUS_BUF_SIZE 64 3179bf39c6SAlexey Brodkin #define DWC2_DATA_BUF_SIZE (CONFIG_USB_DWC2_BUFFER_SIZE * 1024) 326e9e0626SOleksandr Tymoshenko 336e9e0626SOleksandr Tymoshenko #define MAX_DEVICE 16 346e9e0626SOleksandr Tymoshenko #define MAX_ENDPOINT 16 356e9e0626SOleksandr Tymoshenko 36cc3e3a9eSSimon Glass struct dwc2_priv { 373739bf7eSSven Schwermer #if CONFIG_IS_ENABLED(DM_USB) 38db402e00SAlexander Stein uint8_t aligned_buffer[DWC2_DATA_BUF_SIZE] __aligned(ARCH_DMA_MINALIGN); 39db402e00SAlexander Stein uint8_t status_buffer[DWC2_STATUS_BUF_SIZE] __aligned(ARCH_DMA_MINALIGN); 40782be0c4SChristophe Kerello #ifdef CONFIG_DM_REGULATOR 41782be0c4SChristophe Kerello struct udevice *vbus_supply; 42782be0c4SChristophe Kerello #endif 431e03ec26SPatrick Delaunay struct phy phy; 441966f56eSPatrick Delaunay struct clk_bulk clks; 45f58a41e0SSimon Glass #else 46cc3e3a9eSSimon Glass uint8_t *aligned_buffer; 47cc3e3a9eSSimon Glass uint8_t *status_buffer; 48f58a41e0SSimon Glass #endif 4925612f23SStefan Brüns u8 in_data_toggle[MAX_DEVICE][MAX_ENDPOINT]; 5025612f23SStefan Brüns u8 out_data_toggle[MAX_DEVICE][MAX_ENDPOINT]; 51cc3e3a9eSSimon Glass struct dwc2_core_regs *regs; 52cc3e3a9eSSimon Glass int root_hub_devnum; 53618da563SMarek Vasut bool ext_vbus; 54dd22baceSMeng Dongyang /* 55dd22baceSMeng Dongyang * The hnp/srp capability must be disabled if the platform 56dd22baceSMeng Dongyang * does't support hnp/srp. Otherwise the force mode can't work. 57dd22baceSMeng Dongyang */ 58c65a3494SMeng Dongyang bool hnp_srp_disable; 59b4fbd089SMarek Vasut bool oc_disable; 60a1bebf37SLey Foon Tan 61a1bebf37SLey Foon Tan struct reset_ctl_bulk resets; 62cc3e3a9eSSimon Glass }; 636e9e0626SOleksandr Tymoshenko 643739bf7eSSven Schwermer #if !CONFIG_IS_ENABLED(DM_USB) 65db402e00SAlexander Stein /* We need cacheline-aligned buffers for DMA transfers and dcache support */ 66db402e00SAlexander Stein DEFINE_ALIGN_BUFFER(uint8_t, aligned_buffer_addr, DWC2_DATA_BUF_SIZE, 67db402e00SAlexander Stein ARCH_DMA_MINALIGN); 68db402e00SAlexander Stein DEFINE_ALIGN_BUFFER(uint8_t, status_buffer_addr, DWC2_STATUS_BUF_SIZE, 69db402e00SAlexander Stein ARCH_DMA_MINALIGN); 70cc3e3a9eSSimon Glass 71cc3e3a9eSSimon Glass static struct dwc2_priv local; 72f58a41e0SSimon Glass #endif 736e9e0626SOleksandr Tymoshenko 746e9e0626SOleksandr Tymoshenko /* 756e9e0626SOleksandr Tymoshenko * DWC2 IP interface 766e9e0626SOleksandr Tymoshenko */ 776e9e0626SOleksandr Tymoshenko 786e9e0626SOleksandr Tymoshenko /* 796e9e0626SOleksandr Tymoshenko * Initializes the FSLSPClkSel field of the HCFG register 806e9e0626SOleksandr Tymoshenko * depending on the PHY type. 816e9e0626SOleksandr Tymoshenko */ 826e9e0626SOleksandr Tymoshenko static void init_fslspclksel(struct dwc2_core_regs *regs) 836e9e0626SOleksandr Tymoshenko { 846e9e0626SOleksandr Tymoshenko uint32_t phyclk; 856e9e0626SOleksandr Tymoshenko 866e9e0626SOleksandr Tymoshenko #if (CONFIG_DWC2_PHY_TYPE == DWC2_PHY_TYPE_FS) 876e9e0626SOleksandr Tymoshenko phyclk = DWC2_HCFG_FSLSPCLKSEL_48_MHZ; /* Full speed PHY */ 886e9e0626SOleksandr Tymoshenko #else 896e9e0626SOleksandr Tymoshenko /* High speed PHY running at full speed or high speed */ 906e9e0626SOleksandr Tymoshenko phyclk = DWC2_HCFG_FSLSPCLKSEL_30_60_MHZ; 916e9e0626SOleksandr Tymoshenko #endif 926e9e0626SOleksandr Tymoshenko 936e9e0626SOleksandr Tymoshenko #ifdef CONFIG_DWC2_ULPI_FS_LS 946e9e0626SOleksandr Tymoshenko uint32_t hwcfg2 = readl(®s->ghwcfg2); 956e9e0626SOleksandr Tymoshenko uint32_t hval = (ghwcfg2 & DWC2_HWCFG2_HS_PHY_TYPE_MASK) >> 966e9e0626SOleksandr Tymoshenko DWC2_HWCFG2_HS_PHY_TYPE_OFFSET; 976e9e0626SOleksandr Tymoshenko uint32_t fval = (ghwcfg2 & DWC2_HWCFG2_FS_PHY_TYPE_MASK) >> 986e9e0626SOleksandr Tymoshenko DWC2_HWCFG2_FS_PHY_TYPE_OFFSET; 996e9e0626SOleksandr Tymoshenko 1006e9e0626SOleksandr Tymoshenko if (hval == 2 && fval == 1) 1016e9e0626SOleksandr Tymoshenko phyclk = DWC2_HCFG_FSLSPCLKSEL_48_MHZ; /* Full speed PHY */ 1026e9e0626SOleksandr Tymoshenko #endif 1036e9e0626SOleksandr Tymoshenko 1046e9e0626SOleksandr Tymoshenko clrsetbits_le32(®s->host_regs.hcfg, 1056e9e0626SOleksandr Tymoshenko DWC2_HCFG_FSLSPCLKSEL_MASK, 1066e9e0626SOleksandr Tymoshenko phyclk << DWC2_HCFG_FSLSPCLKSEL_OFFSET); 1076e9e0626SOleksandr Tymoshenko } 1086e9e0626SOleksandr Tymoshenko 1096e9e0626SOleksandr Tymoshenko /* 1106e9e0626SOleksandr Tymoshenko * Flush a Tx FIFO. 1116e9e0626SOleksandr Tymoshenko * 1126e9e0626SOleksandr Tymoshenko * @param regs Programming view of DWC_otg controller. 1136e9e0626SOleksandr Tymoshenko * @param num Tx FIFO to flush. 1146e9e0626SOleksandr Tymoshenko */ 1156e9e0626SOleksandr Tymoshenko static void dwc_otg_flush_tx_fifo(struct dwc2_core_regs *regs, const int num) 1166e9e0626SOleksandr Tymoshenko { 1176e9e0626SOleksandr Tymoshenko int ret; 1186e9e0626SOleksandr Tymoshenko 1196e9e0626SOleksandr Tymoshenko writel(DWC2_GRSTCTL_TXFFLSH | (num << DWC2_GRSTCTL_TXFNUM_OFFSET), 1206e9e0626SOleksandr Tymoshenko ®s->grstctl); 121b491b498SJon Lin ret = wait_for_bit_le32(®s->grstctl, DWC2_GRSTCTL_TXFFLSH, 122fd2cd662SMateusz Kulikowski false, 1000, false); 1236e9e0626SOleksandr Tymoshenko if (ret) 124071d6bebSPatrice Chotard dev_info(dev, "%s: Timeout!\n", __func__); 1256e9e0626SOleksandr Tymoshenko 1266e9e0626SOleksandr Tymoshenko /* Wait for 3 PHY Clocks */ 1276e9e0626SOleksandr Tymoshenko udelay(1); 1286e9e0626SOleksandr Tymoshenko } 1296e9e0626SOleksandr Tymoshenko 1306e9e0626SOleksandr Tymoshenko /* 1316e9e0626SOleksandr Tymoshenko * Flush Rx FIFO. 1326e9e0626SOleksandr Tymoshenko * 1336e9e0626SOleksandr Tymoshenko * @param regs Programming view of DWC_otg controller. 1346e9e0626SOleksandr Tymoshenko */ 1356e9e0626SOleksandr Tymoshenko static void dwc_otg_flush_rx_fifo(struct dwc2_core_regs *regs) 1366e9e0626SOleksandr Tymoshenko { 1376e9e0626SOleksandr Tymoshenko int ret; 1386e9e0626SOleksandr Tymoshenko 1396e9e0626SOleksandr Tymoshenko writel(DWC2_GRSTCTL_RXFFLSH, ®s->grstctl); 140b491b498SJon Lin ret = wait_for_bit_le32(®s->grstctl, DWC2_GRSTCTL_RXFFLSH, 141fd2cd662SMateusz Kulikowski false, 1000, false); 1426e9e0626SOleksandr Tymoshenko if (ret) 143071d6bebSPatrice Chotard dev_info(dev, "%s: Timeout!\n", __func__); 1446e9e0626SOleksandr Tymoshenko 1456e9e0626SOleksandr Tymoshenko /* Wait for 3 PHY Clocks */ 1466e9e0626SOleksandr Tymoshenko udelay(1); 1476e9e0626SOleksandr Tymoshenko } 1486e9e0626SOleksandr Tymoshenko 1496e9e0626SOleksandr Tymoshenko /* 1506e9e0626SOleksandr Tymoshenko * Do core a soft reset of the core. Be careful with this because it 1516e9e0626SOleksandr Tymoshenko * resets all the internal state machines of the core. 1526e9e0626SOleksandr Tymoshenko */ 1536e9e0626SOleksandr Tymoshenko static void dwc_otg_core_reset(struct dwc2_core_regs *regs) 1546e9e0626SOleksandr Tymoshenko { 1556e9e0626SOleksandr Tymoshenko int ret; 1566e9e0626SOleksandr Tymoshenko 1576e9e0626SOleksandr Tymoshenko /* Wait for AHB master IDLE state. */ 158b491b498SJon Lin ret = wait_for_bit_le32(®s->grstctl, DWC2_GRSTCTL_AHBIDLE, 159fd2cd662SMateusz Kulikowski true, 1000, false); 1606e9e0626SOleksandr Tymoshenko if (ret) 161071d6bebSPatrice Chotard dev_info(dev, "%s: Timeout!\n", __func__); 1626e9e0626SOleksandr Tymoshenko 1636e9e0626SOleksandr Tymoshenko /* Core Soft Reset */ 1646e9e0626SOleksandr Tymoshenko writel(DWC2_GRSTCTL_CSFTRST, ®s->grstctl); 165b491b498SJon Lin ret = wait_for_bit_le32(®s->grstctl, DWC2_GRSTCTL_CSFTRST, 166fd2cd662SMateusz Kulikowski false, 1000, false); 1676e9e0626SOleksandr Tymoshenko if (ret) 168071d6bebSPatrice Chotard dev_info(dev, "%s: Timeout!\n", __func__); 1696e9e0626SOleksandr Tymoshenko 1706e9e0626SOleksandr Tymoshenko /* 1716e9e0626SOleksandr Tymoshenko * Wait for core to come out of reset. 1726e9e0626SOleksandr Tymoshenko * NOTE: This long sleep is _very_ important, otherwise the core will 1736e9e0626SOleksandr Tymoshenko * not stay in host mode after a connector ID change! 1746e9e0626SOleksandr Tymoshenko */ 1756e9e0626SOleksandr Tymoshenko mdelay(100); 1766e9e0626SOleksandr Tymoshenko } 1776e9e0626SOleksandr Tymoshenko 1783739bf7eSSven Schwermer #if CONFIG_IS_ENABLED(DM_USB) && defined(CONFIG_DM_REGULATOR) 1795c735367SKever Yang static int dwc_vbus_supply_init(struct udevice *dev) 1805c735367SKever Yang { 181782be0c4SChristophe Kerello struct dwc2_priv *priv = dev_get_priv(dev); 1825c735367SKever Yang int ret; 1835c735367SKever Yang 184782be0c4SChristophe Kerello ret = device_get_supply_regulator(dev, "vbus-supply", 185782be0c4SChristophe Kerello &priv->vbus_supply); 1865c735367SKever Yang if (ret) { 1875c735367SKever Yang debug("%s: No vbus supply\n", dev->name); 1885c735367SKever Yang return 0; 1895c735367SKever Yang } 1905c735367SKever Yang 191782be0c4SChristophe Kerello ret = regulator_set_enable(priv->vbus_supply, true); 1925c735367SKever Yang if (ret) { 193071d6bebSPatrice Chotard dev_err(dev, "Error enabling vbus supply\n"); 1945c735367SKever Yang return ret; 1955c735367SKever Yang } 1965c735367SKever Yang 1975c735367SKever Yang return 0; 1985c735367SKever Yang } 199782be0c4SChristophe Kerello 200782be0c4SChristophe Kerello static int dwc_vbus_supply_exit(struct udevice *dev) 201782be0c4SChristophe Kerello { 202782be0c4SChristophe Kerello struct dwc2_priv *priv = dev_get_priv(dev); 203782be0c4SChristophe Kerello int ret; 204782be0c4SChristophe Kerello 205782be0c4SChristophe Kerello if (priv->vbus_supply) { 206782be0c4SChristophe Kerello ret = regulator_set_enable(priv->vbus_supply, false); 207782be0c4SChristophe Kerello if (ret) { 208782be0c4SChristophe Kerello dev_err(dev, "Error disabling vbus supply\n"); 209782be0c4SChristophe Kerello return ret; 210782be0c4SChristophe Kerello } 211782be0c4SChristophe Kerello } 212782be0c4SChristophe Kerello 213782be0c4SChristophe Kerello return 0; 214782be0c4SChristophe Kerello } 2155c735367SKever Yang #else 2165c735367SKever Yang static int dwc_vbus_supply_init(struct udevice *dev) 2175c735367SKever Yang { 2185c735367SKever Yang return 0; 2195c735367SKever Yang } 220782be0c4SChristophe Kerello 2213739bf7eSSven Schwermer #if CONFIG_IS_ENABLED(DM_USB) 222782be0c4SChristophe Kerello static int dwc_vbus_supply_exit(struct udevice *dev) 223782be0c4SChristophe Kerello { 224782be0c4SChristophe Kerello return 0; 225782be0c4SChristophe Kerello } 226782be0c4SChristophe Kerello #endif 2275c735367SKever Yang #endif 2285c735367SKever Yang 2296e9e0626SOleksandr Tymoshenko /* 2306e9e0626SOleksandr Tymoshenko * This function initializes the DWC_otg controller registers for 2316e9e0626SOleksandr Tymoshenko * host mode. 2326e9e0626SOleksandr Tymoshenko * 2336e9e0626SOleksandr Tymoshenko * This function flushes the Tx and Rx FIFOs and it flushes any entries in the 2346e9e0626SOleksandr Tymoshenko * request queues. Host channels are reset to ensure that they are ready for 2356e9e0626SOleksandr Tymoshenko * performing transfers. 2366e9e0626SOleksandr Tymoshenko * 2375c735367SKever Yang * @param dev USB Device (NULL if driver model is not being used) 2386e9e0626SOleksandr Tymoshenko * @param regs Programming view of DWC_otg controller 2396e9e0626SOleksandr Tymoshenko * 2406e9e0626SOleksandr Tymoshenko */ 2415c735367SKever Yang static void dwc_otg_core_host_init(struct udevice *dev, 2425c735367SKever Yang struct dwc2_core_regs *regs) 2436e9e0626SOleksandr Tymoshenko { 2446e9e0626SOleksandr Tymoshenko uint32_t nptxfifosize = 0; 2456e9e0626SOleksandr Tymoshenko uint32_t ptxfifosize = 0; 2466e9e0626SOleksandr Tymoshenko uint32_t hprt0 = 0; 2476e9e0626SOleksandr Tymoshenko int i, ret, num_channels; 2486e9e0626SOleksandr Tymoshenko 2496e9e0626SOleksandr Tymoshenko /* Restart the Phy Clock */ 2506e9e0626SOleksandr Tymoshenko writel(0, ®s->pcgcctl); 2516e9e0626SOleksandr Tymoshenko 2526e9e0626SOleksandr Tymoshenko /* Initialize Host Configuration Register */ 2536e9e0626SOleksandr Tymoshenko init_fslspclksel(regs); 2546e9e0626SOleksandr Tymoshenko #ifdef CONFIG_DWC2_DFLT_SPEED_FULL 2556e9e0626SOleksandr Tymoshenko setbits_le32(®s->host_regs.hcfg, DWC2_HCFG_FSLSSUPP); 2566e9e0626SOleksandr Tymoshenko #endif 2576e9e0626SOleksandr Tymoshenko 2586e9e0626SOleksandr Tymoshenko /* Configure data FIFO sizes */ 2596e9e0626SOleksandr Tymoshenko #ifdef CONFIG_DWC2_ENABLE_DYNAMIC_FIFO 2606e9e0626SOleksandr Tymoshenko if (readl(®s->ghwcfg2) & DWC2_HWCFG2_DYNAMIC_FIFO) { 2616e9e0626SOleksandr Tymoshenko /* Rx FIFO */ 2626e9e0626SOleksandr Tymoshenko writel(CONFIG_DWC2_HOST_RX_FIFO_SIZE, ®s->grxfsiz); 2636e9e0626SOleksandr Tymoshenko 2646e9e0626SOleksandr Tymoshenko /* Non-periodic Tx FIFO */ 2656e9e0626SOleksandr Tymoshenko nptxfifosize |= CONFIG_DWC2_HOST_NPERIO_TX_FIFO_SIZE << 2666e9e0626SOleksandr Tymoshenko DWC2_FIFOSIZE_DEPTH_OFFSET; 2676e9e0626SOleksandr Tymoshenko nptxfifosize |= CONFIG_DWC2_HOST_RX_FIFO_SIZE << 2686e9e0626SOleksandr Tymoshenko DWC2_FIFOSIZE_STARTADDR_OFFSET; 2696e9e0626SOleksandr Tymoshenko writel(nptxfifosize, ®s->gnptxfsiz); 2706e9e0626SOleksandr Tymoshenko 2716e9e0626SOleksandr Tymoshenko /* Periodic Tx FIFO */ 2726e9e0626SOleksandr Tymoshenko ptxfifosize |= CONFIG_DWC2_HOST_PERIO_TX_FIFO_SIZE << 2736e9e0626SOleksandr Tymoshenko DWC2_FIFOSIZE_DEPTH_OFFSET; 2746e9e0626SOleksandr Tymoshenko ptxfifosize |= (CONFIG_DWC2_HOST_RX_FIFO_SIZE + 2756e9e0626SOleksandr Tymoshenko CONFIG_DWC2_HOST_NPERIO_TX_FIFO_SIZE) << 2766e9e0626SOleksandr Tymoshenko DWC2_FIFOSIZE_STARTADDR_OFFSET; 2776e9e0626SOleksandr Tymoshenko writel(ptxfifosize, ®s->hptxfsiz); 2786e9e0626SOleksandr Tymoshenko } 2796e9e0626SOleksandr Tymoshenko #endif 2806e9e0626SOleksandr Tymoshenko 2816e9e0626SOleksandr Tymoshenko /* Clear Host Set HNP Enable in the OTG Control Register */ 2826e9e0626SOleksandr Tymoshenko clrbits_le32(®s->gotgctl, DWC2_GOTGCTL_HSTSETHNPEN); 2836e9e0626SOleksandr Tymoshenko 2846e9e0626SOleksandr Tymoshenko /* Make sure the FIFOs are flushed. */ 2856e9e0626SOleksandr Tymoshenko dwc_otg_flush_tx_fifo(regs, 0x10); /* All Tx FIFOs */ 2866e9e0626SOleksandr Tymoshenko dwc_otg_flush_rx_fifo(regs); 2876e9e0626SOleksandr Tymoshenko 2886e9e0626SOleksandr Tymoshenko /* Flush out any leftover queued requests. */ 2896e9e0626SOleksandr Tymoshenko num_channels = readl(®s->ghwcfg2); 2906e9e0626SOleksandr Tymoshenko num_channels &= DWC2_HWCFG2_NUM_HOST_CHAN_MASK; 2916e9e0626SOleksandr Tymoshenko num_channels >>= DWC2_HWCFG2_NUM_HOST_CHAN_OFFSET; 2926e9e0626SOleksandr Tymoshenko num_channels += 1; 2936e9e0626SOleksandr Tymoshenko 2946e9e0626SOleksandr Tymoshenko for (i = 0; i < num_channels; i++) 2956e9e0626SOleksandr Tymoshenko clrsetbits_le32(®s->hc_regs[i].hcchar, 2966e9e0626SOleksandr Tymoshenko DWC2_HCCHAR_CHEN | DWC2_HCCHAR_EPDIR, 2976e9e0626SOleksandr Tymoshenko DWC2_HCCHAR_CHDIS); 2986e9e0626SOleksandr Tymoshenko 2996e9e0626SOleksandr Tymoshenko /* Halt all channels to put them into a known state. */ 3006e9e0626SOleksandr Tymoshenko for (i = 0; i < num_channels; i++) { 3016e9e0626SOleksandr Tymoshenko clrsetbits_le32(®s->hc_regs[i].hcchar, 3026e9e0626SOleksandr Tymoshenko DWC2_HCCHAR_EPDIR, 3036e9e0626SOleksandr Tymoshenko DWC2_HCCHAR_CHEN | DWC2_HCCHAR_CHDIS); 304b491b498SJon Lin ret = wait_for_bit_le32(®s->hc_regs[i].hcchar, 305fd2cd662SMateusz Kulikowski DWC2_HCCHAR_CHEN, false, 1000, false); 3066e9e0626SOleksandr Tymoshenko if (ret) 307071d6bebSPatrice Chotard dev_info("%s: Timeout!\n", __func__); 3086e9e0626SOleksandr Tymoshenko } 3096e9e0626SOleksandr Tymoshenko 3106e9e0626SOleksandr Tymoshenko /* Turn on the vbus power. */ 3116e9e0626SOleksandr Tymoshenko if (readl(®s->gintsts) & DWC2_GINTSTS_CURMODE_HOST) { 3126e9e0626SOleksandr Tymoshenko hprt0 = readl(®s->hprt0); 3136e9e0626SOleksandr Tymoshenko hprt0 &= ~(DWC2_HPRT0_PRTENA | DWC2_HPRT0_PRTCONNDET); 3146e9e0626SOleksandr Tymoshenko hprt0 &= ~(DWC2_HPRT0_PRTENCHNG | DWC2_HPRT0_PRTOVRCURRCHNG); 3156e9e0626SOleksandr Tymoshenko if (!(hprt0 & DWC2_HPRT0_PRTPWR)) { 3166e9e0626SOleksandr Tymoshenko hprt0 |= DWC2_HPRT0_PRTPWR; 3176e9e0626SOleksandr Tymoshenko writel(hprt0, ®s->hprt0); 3186e9e0626SOleksandr Tymoshenko } 3196e9e0626SOleksandr Tymoshenko } 3205c735367SKever Yang 3215c735367SKever Yang if (dev) 3225c735367SKever Yang dwc_vbus_supply_init(dev); 3236e9e0626SOleksandr Tymoshenko } 3246e9e0626SOleksandr Tymoshenko 3256e9e0626SOleksandr Tymoshenko /* 3266e9e0626SOleksandr Tymoshenko * This function initializes the DWC_otg controller registers and 3276e9e0626SOleksandr Tymoshenko * prepares the core for device mode or host mode operation. 3286e9e0626SOleksandr Tymoshenko * 3296e9e0626SOleksandr Tymoshenko * @param regs Programming view of the DWC_otg controller 3306e9e0626SOleksandr Tymoshenko */ 33155901989SMarek Vasut static void dwc_otg_core_init(struct dwc2_priv *priv) 3326e9e0626SOleksandr Tymoshenko { 33355901989SMarek Vasut struct dwc2_core_regs *regs = priv->regs; 3346e9e0626SOleksandr Tymoshenko uint32_t ahbcfg = 0; 3356e9e0626SOleksandr Tymoshenko uint32_t usbcfg = 0; 3366e9e0626SOleksandr Tymoshenko uint8_t brst_sz = CONFIG_DWC2_DMA_BURST_SIZE; 3376e9e0626SOleksandr Tymoshenko 3386e9e0626SOleksandr Tymoshenko /* Common Initialization */ 3396e9e0626SOleksandr Tymoshenko usbcfg = readl(®s->gusbcfg); 3406e9e0626SOleksandr Tymoshenko 3416e9e0626SOleksandr Tymoshenko /* Program the ULPI External VBUS bit if needed */ 342618da563SMarek Vasut if (priv->ext_vbus) { 343b4fbd089SMarek Vasut usbcfg |= DWC2_GUSBCFG_ULPI_EXT_VBUS_DRV; 344b4fbd089SMarek Vasut if (!priv->oc_disable) { 345b4fbd089SMarek Vasut usbcfg |= DWC2_GUSBCFG_ULPI_INT_VBUS_INDICATOR | 346b4fbd089SMarek Vasut DWC2_GUSBCFG_INDICATOR_PASSTHROUGH; 347b4fbd089SMarek Vasut } 348618da563SMarek Vasut } else { 3496e9e0626SOleksandr Tymoshenko usbcfg &= ~DWC2_GUSBCFG_ULPI_EXT_VBUS_DRV; 350618da563SMarek Vasut } 3516e9e0626SOleksandr Tymoshenko 3526e9e0626SOleksandr Tymoshenko /* Set external TS Dline pulsing */ 3536e9e0626SOleksandr Tymoshenko #ifdef CONFIG_DWC2_TS_DLINE 3546e9e0626SOleksandr Tymoshenko usbcfg |= DWC2_GUSBCFG_TERM_SEL_DL_PULSE; 3556e9e0626SOleksandr Tymoshenko #else 3566e9e0626SOleksandr Tymoshenko usbcfg &= ~DWC2_GUSBCFG_TERM_SEL_DL_PULSE; 3576e9e0626SOleksandr Tymoshenko #endif 3586e9e0626SOleksandr Tymoshenko writel(usbcfg, ®s->gusbcfg); 3596e9e0626SOleksandr Tymoshenko 3606e9e0626SOleksandr Tymoshenko /* Reset the Controller */ 3616e9e0626SOleksandr Tymoshenko dwc_otg_core_reset(regs); 3626e9e0626SOleksandr Tymoshenko 3636e9e0626SOleksandr Tymoshenko /* 3646e9e0626SOleksandr Tymoshenko * This programming sequence needs to happen in FS mode before 3656e9e0626SOleksandr Tymoshenko * any other programming occurs 3666e9e0626SOleksandr Tymoshenko */ 3676e9e0626SOleksandr Tymoshenko #if defined(CONFIG_DWC2_DFLT_SPEED_FULL) && \ 3686e9e0626SOleksandr Tymoshenko (CONFIG_DWC2_PHY_TYPE == DWC2_PHY_TYPE_FS) 3696e9e0626SOleksandr Tymoshenko /* If FS mode with FS PHY */ 3706e9e0626SOleksandr Tymoshenko setbits_le32(®s->gusbcfg, DWC2_GUSBCFG_PHYSEL); 3716e9e0626SOleksandr Tymoshenko 3726e9e0626SOleksandr Tymoshenko /* Reset after a PHY select */ 3736e9e0626SOleksandr Tymoshenko dwc_otg_core_reset(regs); 3746e9e0626SOleksandr Tymoshenko 3756e9e0626SOleksandr Tymoshenko /* 3766e9e0626SOleksandr Tymoshenko * Program DCFG.DevSpd or HCFG.FSLSPclkSel to 48Mhz in FS. 3776e9e0626SOleksandr Tymoshenko * Also do this on HNP Dev/Host mode switches (done in dev_init 3786e9e0626SOleksandr Tymoshenko * and host_init). 3796e9e0626SOleksandr Tymoshenko */ 3806e9e0626SOleksandr Tymoshenko if (readl(®s->gintsts) & DWC2_GINTSTS_CURMODE_HOST) 3816e9e0626SOleksandr Tymoshenko init_fslspclksel(regs); 3826e9e0626SOleksandr Tymoshenko 3836e9e0626SOleksandr Tymoshenko #ifdef CONFIG_DWC2_I2C_ENABLE 3846e9e0626SOleksandr Tymoshenko /* Program GUSBCFG.OtgUtmifsSel to I2C */ 3856e9e0626SOleksandr Tymoshenko setbits_le32(®s->gusbcfg, DWC2_GUSBCFG_OTGUTMIFSSEL); 3866e9e0626SOleksandr Tymoshenko 3876e9e0626SOleksandr Tymoshenko /* Program GI2CCTL.I2CEn */ 3886e9e0626SOleksandr Tymoshenko clrsetbits_le32(®s->gi2cctl, DWC2_GI2CCTL_I2CEN | 3896e9e0626SOleksandr Tymoshenko DWC2_GI2CCTL_I2CDEVADDR_MASK, 3906e9e0626SOleksandr Tymoshenko 1 << DWC2_GI2CCTL_I2CDEVADDR_OFFSET); 3916e9e0626SOleksandr Tymoshenko setbits_le32(®s->gi2cctl, DWC2_GI2CCTL_I2CEN); 3926e9e0626SOleksandr Tymoshenko #endif 3936e9e0626SOleksandr Tymoshenko 3946e9e0626SOleksandr Tymoshenko #else 3956e9e0626SOleksandr Tymoshenko /* High speed PHY. */ 3966e9e0626SOleksandr Tymoshenko 3976e9e0626SOleksandr Tymoshenko /* 3986e9e0626SOleksandr Tymoshenko * HS PHY parameters. These parameters are preserved during 3996e9e0626SOleksandr Tymoshenko * soft reset so only program the first time. Do a soft reset 4006e9e0626SOleksandr Tymoshenko * immediately after setting phyif. 4016e9e0626SOleksandr Tymoshenko */ 4026e9e0626SOleksandr Tymoshenko usbcfg &= ~(DWC2_GUSBCFG_ULPI_UTMI_SEL | DWC2_GUSBCFG_PHYIF); 4036e9e0626SOleksandr Tymoshenko usbcfg |= CONFIG_DWC2_PHY_TYPE << DWC2_GUSBCFG_ULPI_UTMI_SEL_OFFSET; 4046e9e0626SOleksandr Tymoshenko 4056e9e0626SOleksandr Tymoshenko if (usbcfg & DWC2_GUSBCFG_ULPI_UTMI_SEL) { /* ULPI interface */ 4066e9e0626SOleksandr Tymoshenko #ifdef CONFIG_DWC2_PHY_ULPI_DDR 4076e9e0626SOleksandr Tymoshenko usbcfg |= DWC2_GUSBCFG_DDRSEL; 4086e9e0626SOleksandr Tymoshenko #else 4096e9e0626SOleksandr Tymoshenko usbcfg &= ~DWC2_GUSBCFG_DDRSEL; 4106e9e0626SOleksandr Tymoshenko #endif 4116e9e0626SOleksandr Tymoshenko } else { /* UTMI+ interface */ 4123cd21242SAlexey Brodkin #if (CONFIG_DWC2_UTMI_WIDTH == 16) 4136e9e0626SOleksandr Tymoshenko usbcfg |= DWC2_GUSBCFG_PHYIF; 4146e9e0626SOleksandr Tymoshenko #endif 4156e9e0626SOleksandr Tymoshenko } 4166e9e0626SOleksandr Tymoshenko 4176e9e0626SOleksandr Tymoshenko writel(usbcfg, ®s->gusbcfg); 4186e9e0626SOleksandr Tymoshenko 4196e9e0626SOleksandr Tymoshenko /* Reset after setting the PHY parameters */ 4206e9e0626SOleksandr Tymoshenko dwc_otg_core_reset(regs); 4216e9e0626SOleksandr Tymoshenko #endif 4226e9e0626SOleksandr Tymoshenko 4236e9e0626SOleksandr Tymoshenko usbcfg = readl(®s->gusbcfg); 4246e9e0626SOleksandr Tymoshenko usbcfg &= ~(DWC2_GUSBCFG_ULPI_FSLS | DWC2_GUSBCFG_ULPI_CLK_SUS_M); 4256e9e0626SOleksandr Tymoshenko #ifdef CONFIG_DWC2_ULPI_FS_LS 4266e9e0626SOleksandr Tymoshenko uint32_t hwcfg2 = readl(®s->ghwcfg2); 4276e9e0626SOleksandr Tymoshenko uint32_t hval = (ghwcfg2 & DWC2_HWCFG2_HS_PHY_TYPE_MASK) >> 4286e9e0626SOleksandr Tymoshenko DWC2_HWCFG2_HS_PHY_TYPE_OFFSET; 4296e9e0626SOleksandr Tymoshenko uint32_t fval = (ghwcfg2 & DWC2_HWCFG2_FS_PHY_TYPE_MASK) >> 4306e9e0626SOleksandr Tymoshenko DWC2_HWCFG2_FS_PHY_TYPE_OFFSET; 4316e9e0626SOleksandr Tymoshenko if (hval == 2 && fval == 1) { 4326e9e0626SOleksandr Tymoshenko usbcfg |= DWC2_GUSBCFG_ULPI_FSLS; 4336e9e0626SOleksandr Tymoshenko usbcfg |= DWC2_GUSBCFG_ULPI_CLK_SUS_M; 4346e9e0626SOleksandr Tymoshenko } 4356e9e0626SOleksandr Tymoshenko #endif 436c65a3494SMeng Dongyang if (priv->hnp_srp_disable) 437c65a3494SMeng Dongyang usbcfg |= DWC2_GUSBCFG_FORCEHOSTMODE; 438c65a3494SMeng Dongyang 4396e9e0626SOleksandr Tymoshenko writel(usbcfg, ®s->gusbcfg); 4406e9e0626SOleksandr Tymoshenko 441*16601491SFrank Wang /* 442*16601491SFrank Wang * Refer to DWC2 Databook 3.10a Table 5-10 443*16601491SFrank Wang * After setting the force bit, the application must wait at least 444*16601491SFrank Wang * 25 ms before the change to take effect. 445*16601491SFrank Wang */ 446*16601491SFrank Wang if (usbcfg & DWC2_GUSBCFG_FORCEHOSTMODE) 447*16601491SFrank Wang mdelay(30); 448*16601491SFrank Wang 4496e9e0626SOleksandr Tymoshenko /* Program the GAHBCFG Register. */ 4506e9e0626SOleksandr Tymoshenko switch (readl(®s->ghwcfg2) & DWC2_HWCFG2_ARCHITECTURE_MASK) { 4516e9e0626SOleksandr Tymoshenko case DWC2_HWCFG2_ARCHITECTURE_SLAVE_ONLY: 4526e9e0626SOleksandr Tymoshenko break; 4536e9e0626SOleksandr Tymoshenko case DWC2_HWCFG2_ARCHITECTURE_EXT_DMA: 4546e9e0626SOleksandr Tymoshenko while (brst_sz > 1) { 4556e9e0626SOleksandr Tymoshenko ahbcfg |= ahbcfg + (1 << DWC2_GAHBCFG_HBURSTLEN_OFFSET); 4566e9e0626SOleksandr Tymoshenko ahbcfg &= DWC2_GAHBCFG_HBURSTLEN_MASK; 4576e9e0626SOleksandr Tymoshenko brst_sz >>= 1; 4586e9e0626SOleksandr Tymoshenko } 4596e9e0626SOleksandr Tymoshenko 4606e9e0626SOleksandr Tymoshenko #ifdef CONFIG_DWC2_DMA_ENABLE 4616e9e0626SOleksandr Tymoshenko ahbcfg |= DWC2_GAHBCFG_DMAENABLE; 4626e9e0626SOleksandr Tymoshenko #endif 4636e9e0626SOleksandr Tymoshenko break; 4646e9e0626SOleksandr Tymoshenko 4656e9e0626SOleksandr Tymoshenko case DWC2_HWCFG2_ARCHITECTURE_INT_DMA: 4666e9e0626SOleksandr Tymoshenko ahbcfg |= DWC2_GAHBCFG_HBURSTLEN_INCR4; 4676e9e0626SOleksandr Tymoshenko #ifdef CONFIG_DWC2_DMA_ENABLE 4686e9e0626SOleksandr Tymoshenko ahbcfg |= DWC2_GAHBCFG_DMAENABLE; 4696e9e0626SOleksandr Tymoshenko #endif 4706e9e0626SOleksandr Tymoshenko break; 4716e9e0626SOleksandr Tymoshenko } 4726e9e0626SOleksandr Tymoshenko 4736e9e0626SOleksandr Tymoshenko writel(ahbcfg, ®s->gahbcfg); 4746e9e0626SOleksandr Tymoshenko 475c65a3494SMeng Dongyang /* Program the capabilities in GUSBCFG Register */ 476c65a3494SMeng Dongyang usbcfg = 0; 4776e9e0626SOleksandr Tymoshenko 478c65a3494SMeng Dongyang if (!priv->hnp_srp_disable) 479c65a3494SMeng Dongyang usbcfg |= DWC2_GUSBCFG_HNPCAP | DWC2_GUSBCFG_SRPCAP; 4806e9e0626SOleksandr Tymoshenko #ifdef CONFIG_DWC2_IC_USB_CAP 481c65a3494SMeng Dongyang usbcfg |= DWC2_GUSBCFG_IC_USB_CAP; 4826e9e0626SOleksandr Tymoshenko #endif 483c65a3494SMeng Dongyang 484c65a3494SMeng Dongyang setbits_le32(®s->gusbcfg, usbcfg); 4856e9e0626SOleksandr Tymoshenko } 4866e9e0626SOleksandr Tymoshenko 4876e9e0626SOleksandr Tymoshenko /* 4886e9e0626SOleksandr Tymoshenko * Prepares a host channel for transferring packets to/from a specific 4896e9e0626SOleksandr Tymoshenko * endpoint. The HCCHARn register is set up with the characteristics specified 4906e9e0626SOleksandr Tymoshenko * in _hc. Host channel interrupts that may need to be serviced while this 4916e9e0626SOleksandr Tymoshenko * transfer is in progress are enabled. 4926e9e0626SOleksandr Tymoshenko * 4936e9e0626SOleksandr Tymoshenko * @param regs Programming view of DWC_otg controller 4946e9e0626SOleksandr Tymoshenko * @param hc Information needed to initialize the host channel 4956e9e0626SOleksandr Tymoshenko */ 4966e9e0626SOleksandr Tymoshenko static void dwc_otg_hc_init(struct dwc2_core_regs *regs, uint8_t hc_num, 497ed9bcbc7SStephen Warren struct usb_device *dev, uint8_t dev_addr, uint8_t ep_num, 498ed9bcbc7SStephen Warren uint8_t ep_is_in, uint8_t ep_type, uint16_t max_packet) 4996e9e0626SOleksandr Tymoshenko { 5006e9e0626SOleksandr Tymoshenko struct dwc2_hc_regs *hc_regs = ®s->hc_regs[hc_num]; 501ed9bcbc7SStephen Warren uint32_t hcchar = (dev_addr << DWC2_HCCHAR_DEVADDR_OFFSET) | 5026e9e0626SOleksandr Tymoshenko (ep_num << DWC2_HCCHAR_EPNUM_OFFSET) | 5036e9e0626SOleksandr Tymoshenko (ep_is_in << DWC2_HCCHAR_EPDIR_OFFSET) | 5046e9e0626SOleksandr Tymoshenko (ep_type << DWC2_HCCHAR_EPTYPE_OFFSET) | 5056e9e0626SOleksandr Tymoshenko (max_packet << DWC2_HCCHAR_MPS_OFFSET); 5066e9e0626SOleksandr Tymoshenko 507ed9bcbc7SStephen Warren if (dev->speed == USB_SPEED_LOW) 508ed9bcbc7SStephen Warren hcchar |= DWC2_HCCHAR_LSPDDEV; 509ed9bcbc7SStephen Warren 5106e9e0626SOleksandr Tymoshenko /* 5116e9e0626SOleksandr Tymoshenko * Program the HCCHARn register with the endpoint characteristics 5126e9e0626SOleksandr Tymoshenko * for the current transfer. 5136e9e0626SOleksandr Tymoshenko */ 5146e9e0626SOleksandr Tymoshenko writel(hcchar, &hc_regs->hcchar); 5156e9e0626SOleksandr Tymoshenko 516890f0ee4SStefan Brüns /* Program the HCSPLIT register, default to no SPLIT */ 5176e9e0626SOleksandr Tymoshenko writel(0, &hc_regs->hcsplt); 5186e9e0626SOleksandr Tymoshenko } 5196e9e0626SOleksandr Tymoshenko 520890f0ee4SStefan Brüns static void dwc_otg_hc_init_split(struct dwc2_hc_regs *hc_regs, 521890f0ee4SStefan Brüns uint8_t hub_devnum, uint8_t hub_port) 522890f0ee4SStefan Brüns { 523890f0ee4SStefan Brüns uint32_t hcsplt = 0; 524890f0ee4SStefan Brüns 525890f0ee4SStefan Brüns hcsplt = DWC2_HCSPLT_SPLTENA; 526890f0ee4SStefan Brüns hcsplt |= hub_devnum << DWC2_HCSPLT_HUBADDR_OFFSET; 527890f0ee4SStefan Brüns hcsplt |= hub_port << DWC2_HCSPLT_PRTADDR_OFFSET; 528890f0ee4SStefan Brüns 529890f0ee4SStefan Brüns /* Program the HCSPLIT register for SPLITs */ 530890f0ee4SStefan Brüns writel(hcsplt, &hc_regs->hcsplt); 531890f0ee4SStefan Brüns } 532890f0ee4SStefan Brüns 5336e9e0626SOleksandr Tymoshenko /* 5346e9e0626SOleksandr Tymoshenko * DWC2 to USB API interface 5356e9e0626SOleksandr Tymoshenko */ 5366e9e0626SOleksandr Tymoshenko /* Direction: In ; Request: Status */ 537cc3e3a9eSSimon Glass static int dwc_otg_submit_rh_msg_in_status(struct dwc2_core_regs *regs, 538cc3e3a9eSSimon Glass struct usb_device *dev, void *buffer, 5396e9e0626SOleksandr Tymoshenko int txlen, struct devrequest *cmd) 5406e9e0626SOleksandr Tymoshenko { 5416e9e0626SOleksandr Tymoshenko uint32_t hprt0 = 0; 5426e9e0626SOleksandr Tymoshenko uint32_t port_status = 0; 5436e9e0626SOleksandr Tymoshenko uint32_t port_change = 0; 5446e9e0626SOleksandr Tymoshenko int len = 0; 5456e9e0626SOleksandr Tymoshenko int stat = 0; 5466e9e0626SOleksandr Tymoshenko 5476e9e0626SOleksandr Tymoshenko switch (cmd->requesttype & ~USB_DIR_IN) { 5486e9e0626SOleksandr Tymoshenko case 0: 5496e9e0626SOleksandr Tymoshenko *(uint16_t *)buffer = cpu_to_le16(1); 5506e9e0626SOleksandr Tymoshenko len = 2; 5516e9e0626SOleksandr Tymoshenko break; 5526e9e0626SOleksandr Tymoshenko case USB_RECIP_INTERFACE: 5536e9e0626SOleksandr Tymoshenko case USB_RECIP_ENDPOINT: 5546e9e0626SOleksandr Tymoshenko *(uint16_t *)buffer = cpu_to_le16(0); 5556e9e0626SOleksandr Tymoshenko len = 2; 5566e9e0626SOleksandr Tymoshenko break; 5576e9e0626SOleksandr Tymoshenko case USB_TYPE_CLASS: 5586e9e0626SOleksandr Tymoshenko *(uint32_t *)buffer = cpu_to_le32(0); 5596e9e0626SOleksandr Tymoshenko len = 4; 5606e9e0626SOleksandr Tymoshenko break; 5616e9e0626SOleksandr Tymoshenko case USB_RECIP_OTHER | USB_TYPE_CLASS: 5626e9e0626SOleksandr Tymoshenko hprt0 = readl(®s->hprt0); 5636e9e0626SOleksandr Tymoshenko if (hprt0 & DWC2_HPRT0_PRTCONNSTS) 5646e9e0626SOleksandr Tymoshenko port_status |= USB_PORT_STAT_CONNECTION; 5656e9e0626SOleksandr Tymoshenko if (hprt0 & DWC2_HPRT0_PRTENA) 5666e9e0626SOleksandr Tymoshenko port_status |= USB_PORT_STAT_ENABLE; 5676e9e0626SOleksandr Tymoshenko if (hprt0 & DWC2_HPRT0_PRTSUSP) 5686e9e0626SOleksandr Tymoshenko port_status |= USB_PORT_STAT_SUSPEND; 5696e9e0626SOleksandr Tymoshenko if (hprt0 & DWC2_HPRT0_PRTOVRCURRACT) 5706e9e0626SOleksandr Tymoshenko port_status |= USB_PORT_STAT_OVERCURRENT; 5716e9e0626SOleksandr Tymoshenko if (hprt0 & DWC2_HPRT0_PRTRST) 5726e9e0626SOleksandr Tymoshenko port_status |= USB_PORT_STAT_RESET; 5736e9e0626SOleksandr Tymoshenko if (hprt0 & DWC2_HPRT0_PRTPWR) 5746e9e0626SOleksandr Tymoshenko port_status |= USB_PORT_STAT_POWER; 5756e9e0626SOleksandr Tymoshenko 5764748cce5SStephen Warren if ((hprt0 & DWC2_HPRT0_PRTSPD_MASK) == DWC2_HPRT0_PRTSPD_LOW) 5774748cce5SStephen Warren port_status |= USB_PORT_STAT_LOW_SPEED; 5784748cce5SStephen Warren else if ((hprt0 & DWC2_HPRT0_PRTSPD_MASK) == 5794748cce5SStephen Warren DWC2_HPRT0_PRTSPD_HIGH) 5806e9e0626SOleksandr Tymoshenko port_status |= USB_PORT_STAT_HIGH_SPEED; 5816e9e0626SOleksandr Tymoshenko 5826e9e0626SOleksandr Tymoshenko if (hprt0 & DWC2_HPRT0_PRTENCHNG) 5836e9e0626SOleksandr Tymoshenko port_change |= USB_PORT_STAT_C_ENABLE; 5846e9e0626SOleksandr Tymoshenko if (hprt0 & DWC2_HPRT0_PRTCONNDET) 5856e9e0626SOleksandr Tymoshenko port_change |= USB_PORT_STAT_C_CONNECTION; 5866e9e0626SOleksandr Tymoshenko if (hprt0 & DWC2_HPRT0_PRTOVRCURRCHNG) 5876e9e0626SOleksandr Tymoshenko port_change |= USB_PORT_STAT_C_OVERCURRENT; 5886e9e0626SOleksandr Tymoshenko 5896e9e0626SOleksandr Tymoshenko *(uint32_t *)buffer = cpu_to_le32(port_status | 5906e9e0626SOleksandr Tymoshenko (port_change << 16)); 5916e9e0626SOleksandr Tymoshenko len = 4; 5926e9e0626SOleksandr Tymoshenko break; 5936e9e0626SOleksandr Tymoshenko default: 5946e9e0626SOleksandr Tymoshenko puts("unsupported root hub command\n"); 5956e9e0626SOleksandr Tymoshenko stat = USB_ST_STALLED; 5966e9e0626SOleksandr Tymoshenko } 5976e9e0626SOleksandr Tymoshenko 5986e9e0626SOleksandr Tymoshenko dev->act_len = min(len, txlen); 5996e9e0626SOleksandr Tymoshenko dev->status = stat; 6006e9e0626SOleksandr Tymoshenko 6016e9e0626SOleksandr Tymoshenko return stat; 6026e9e0626SOleksandr Tymoshenko } 6036e9e0626SOleksandr Tymoshenko 6046e9e0626SOleksandr Tymoshenko /* Direction: In ; Request: Descriptor */ 6056e9e0626SOleksandr Tymoshenko static int dwc_otg_submit_rh_msg_in_descriptor(struct usb_device *dev, 6066e9e0626SOleksandr Tymoshenko void *buffer, int txlen, 6076e9e0626SOleksandr Tymoshenko struct devrequest *cmd) 6086e9e0626SOleksandr Tymoshenko { 6096e9e0626SOleksandr Tymoshenko unsigned char data[32]; 6106e9e0626SOleksandr Tymoshenko uint32_t dsc; 6116e9e0626SOleksandr Tymoshenko int len = 0; 6126e9e0626SOleksandr Tymoshenko int stat = 0; 6136e9e0626SOleksandr Tymoshenko uint16_t wValue = cpu_to_le16(cmd->value); 6146e9e0626SOleksandr Tymoshenko uint16_t wLength = cpu_to_le16(cmd->length); 6156e9e0626SOleksandr Tymoshenko 6166e9e0626SOleksandr Tymoshenko switch (cmd->requesttype & ~USB_DIR_IN) { 6176e9e0626SOleksandr Tymoshenko case 0: 6186e9e0626SOleksandr Tymoshenko switch (wValue & 0xff00) { 6196e9e0626SOleksandr Tymoshenko case 0x0100: /* device descriptor */ 620b4141195SMasahiro Yamada len = min3(txlen, (int)sizeof(root_hub_dev_des), (int)wLength); 6216e9e0626SOleksandr Tymoshenko memcpy(buffer, root_hub_dev_des, len); 6226e9e0626SOleksandr Tymoshenko break; 6236e9e0626SOleksandr Tymoshenko case 0x0200: /* configuration descriptor */ 624b4141195SMasahiro Yamada len = min3(txlen, (int)sizeof(root_hub_config_des), (int)wLength); 6256e9e0626SOleksandr Tymoshenko memcpy(buffer, root_hub_config_des, len); 6266e9e0626SOleksandr Tymoshenko break; 6276e9e0626SOleksandr Tymoshenko case 0x0300: /* string descriptors */ 6286e9e0626SOleksandr Tymoshenko switch (wValue & 0xff) { 6296e9e0626SOleksandr Tymoshenko case 0x00: 630b4141195SMasahiro Yamada len = min3(txlen, (int)sizeof(root_hub_str_index0), 631b4141195SMasahiro Yamada (int)wLength); 6326e9e0626SOleksandr Tymoshenko memcpy(buffer, root_hub_str_index0, len); 6336e9e0626SOleksandr Tymoshenko break; 6346e9e0626SOleksandr Tymoshenko case 0x01: 635b4141195SMasahiro Yamada len = min3(txlen, (int)sizeof(root_hub_str_index1), 636b4141195SMasahiro Yamada (int)wLength); 6376e9e0626SOleksandr Tymoshenko memcpy(buffer, root_hub_str_index1, len); 6386e9e0626SOleksandr Tymoshenko break; 6396e9e0626SOleksandr Tymoshenko } 6406e9e0626SOleksandr Tymoshenko break; 6416e9e0626SOleksandr Tymoshenko default: 6426e9e0626SOleksandr Tymoshenko stat = USB_ST_STALLED; 6436e9e0626SOleksandr Tymoshenko } 6446e9e0626SOleksandr Tymoshenko break; 6456e9e0626SOleksandr Tymoshenko 6466e9e0626SOleksandr Tymoshenko case USB_TYPE_CLASS: 6476e9e0626SOleksandr Tymoshenko /* Root port config, set 1 port and nothing else. */ 6486e9e0626SOleksandr Tymoshenko dsc = 0x00000001; 6496e9e0626SOleksandr Tymoshenko 6506e9e0626SOleksandr Tymoshenko data[0] = 9; /* min length; */ 6516e9e0626SOleksandr Tymoshenko data[1] = 0x29; 6526e9e0626SOleksandr Tymoshenko data[2] = dsc & RH_A_NDP; 6536e9e0626SOleksandr Tymoshenko data[3] = 0; 6546e9e0626SOleksandr Tymoshenko if (dsc & RH_A_PSM) 6556e9e0626SOleksandr Tymoshenko data[3] |= 0x1; 6566e9e0626SOleksandr Tymoshenko if (dsc & RH_A_NOCP) 6576e9e0626SOleksandr Tymoshenko data[3] |= 0x10; 6586e9e0626SOleksandr Tymoshenko else if (dsc & RH_A_OCPM) 6596e9e0626SOleksandr Tymoshenko data[3] |= 0x8; 6606e9e0626SOleksandr Tymoshenko 6616e9e0626SOleksandr Tymoshenko /* corresponds to data[4-7] */ 6626e9e0626SOleksandr Tymoshenko data[5] = (dsc & RH_A_POTPGT) >> 24; 6636e9e0626SOleksandr Tymoshenko data[7] = dsc & RH_B_DR; 6646e9e0626SOleksandr Tymoshenko if (data[2] < 7) { 6656e9e0626SOleksandr Tymoshenko data[8] = 0xff; 6666e9e0626SOleksandr Tymoshenko } else { 6676e9e0626SOleksandr Tymoshenko data[0] += 2; 6686e9e0626SOleksandr Tymoshenko data[8] = (dsc & RH_B_DR) >> 8; 6696e9e0626SOleksandr Tymoshenko data[9] = 0xff; 6706e9e0626SOleksandr Tymoshenko data[10] = data[9]; 6716e9e0626SOleksandr Tymoshenko } 6726e9e0626SOleksandr Tymoshenko 673b4141195SMasahiro Yamada len = min3(txlen, (int)data[0], (int)wLength); 6746e9e0626SOleksandr Tymoshenko memcpy(buffer, data, len); 6756e9e0626SOleksandr Tymoshenko break; 6766e9e0626SOleksandr Tymoshenko default: 6776e9e0626SOleksandr Tymoshenko puts("unsupported root hub command\n"); 6786e9e0626SOleksandr Tymoshenko stat = USB_ST_STALLED; 6796e9e0626SOleksandr Tymoshenko } 6806e9e0626SOleksandr Tymoshenko 6816e9e0626SOleksandr Tymoshenko dev->act_len = min(len, txlen); 6826e9e0626SOleksandr Tymoshenko dev->status = stat; 6836e9e0626SOleksandr Tymoshenko 6846e9e0626SOleksandr Tymoshenko return stat; 6856e9e0626SOleksandr Tymoshenko } 6866e9e0626SOleksandr Tymoshenko 6876e9e0626SOleksandr Tymoshenko /* Direction: In ; Request: Configuration */ 6886e9e0626SOleksandr Tymoshenko static int dwc_otg_submit_rh_msg_in_configuration(struct usb_device *dev, 6896e9e0626SOleksandr Tymoshenko void *buffer, int txlen, 6906e9e0626SOleksandr Tymoshenko struct devrequest *cmd) 6916e9e0626SOleksandr Tymoshenko { 6926e9e0626SOleksandr Tymoshenko int len = 0; 6936e9e0626SOleksandr Tymoshenko int stat = 0; 6946e9e0626SOleksandr Tymoshenko 6956e9e0626SOleksandr Tymoshenko switch (cmd->requesttype & ~USB_DIR_IN) { 6966e9e0626SOleksandr Tymoshenko case 0: 6976e9e0626SOleksandr Tymoshenko *(uint8_t *)buffer = 0x01; 6986e9e0626SOleksandr Tymoshenko len = 1; 6996e9e0626SOleksandr Tymoshenko break; 7006e9e0626SOleksandr Tymoshenko default: 7016e9e0626SOleksandr Tymoshenko puts("unsupported root hub command\n"); 7026e9e0626SOleksandr Tymoshenko stat = USB_ST_STALLED; 7036e9e0626SOleksandr Tymoshenko } 7046e9e0626SOleksandr Tymoshenko 7056e9e0626SOleksandr Tymoshenko dev->act_len = min(len, txlen); 7066e9e0626SOleksandr Tymoshenko dev->status = stat; 7076e9e0626SOleksandr Tymoshenko 7086e9e0626SOleksandr Tymoshenko return stat; 7096e9e0626SOleksandr Tymoshenko } 7106e9e0626SOleksandr Tymoshenko 7116e9e0626SOleksandr Tymoshenko /* Direction: In */ 712cc3e3a9eSSimon Glass static int dwc_otg_submit_rh_msg_in(struct dwc2_priv *priv, 713cc3e3a9eSSimon Glass struct usb_device *dev, void *buffer, 714cc3e3a9eSSimon Glass int txlen, struct devrequest *cmd) 7156e9e0626SOleksandr Tymoshenko { 7166e9e0626SOleksandr Tymoshenko switch (cmd->request) { 7176e9e0626SOleksandr Tymoshenko case USB_REQ_GET_STATUS: 718cc3e3a9eSSimon Glass return dwc_otg_submit_rh_msg_in_status(priv->regs, dev, buffer, 7196e9e0626SOleksandr Tymoshenko txlen, cmd); 7206e9e0626SOleksandr Tymoshenko case USB_REQ_GET_DESCRIPTOR: 7216e9e0626SOleksandr Tymoshenko return dwc_otg_submit_rh_msg_in_descriptor(dev, buffer, 7226e9e0626SOleksandr Tymoshenko txlen, cmd); 7236e9e0626SOleksandr Tymoshenko case USB_REQ_GET_CONFIGURATION: 7246e9e0626SOleksandr Tymoshenko return dwc_otg_submit_rh_msg_in_configuration(dev, buffer, 7256e9e0626SOleksandr Tymoshenko txlen, cmd); 7266e9e0626SOleksandr Tymoshenko default: 7276e9e0626SOleksandr Tymoshenko puts("unsupported root hub command\n"); 7286e9e0626SOleksandr Tymoshenko return USB_ST_STALLED; 7296e9e0626SOleksandr Tymoshenko } 7306e9e0626SOleksandr Tymoshenko } 7316e9e0626SOleksandr Tymoshenko 7326e9e0626SOleksandr Tymoshenko /* Direction: Out */ 733cc3e3a9eSSimon Glass static int dwc_otg_submit_rh_msg_out(struct dwc2_priv *priv, 734cc3e3a9eSSimon Glass struct usb_device *dev, 7356e9e0626SOleksandr Tymoshenko void *buffer, int txlen, 7366e9e0626SOleksandr Tymoshenko struct devrequest *cmd) 7376e9e0626SOleksandr Tymoshenko { 738cc3e3a9eSSimon Glass struct dwc2_core_regs *regs = priv->regs; 7396e9e0626SOleksandr Tymoshenko int len = 0; 7406e9e0626SOleksandr Tymoshenko int stat = 0; 7416e9e0626SOleksandr Tymoshenko uint16_t bmrtype_breq = cmd->requesttype | (cmd->request << 8); 7426e9e0626SOleksandr Tymoshenko uint16_t wValue = cpu_to_le16(cmd->value); 7436e9e0626SOleksandr Tymoshenko 7446e9e0626SOleksandr Tymoshenko switch (bmrtype_breq & ~USB_DIR_IN) { 7456e9e0626SOleksandr Tymoshenko case (USB_REQ_CLEAR_FEATURE << 8) | USB_RECIP_ENDPOINT: 7466e9e0626SOleksandr Tymoshenko case (USB_REQ_CLEAR_FEATURE << 8) | USB_TYPE_CLASS: 7476e9e0626SOleksandr Tymoshenko break; 7486e9e0626SOleksandr Tymoshenko 7496e9e0626SOleksandr Tymoshenko case (USB_REQ_CLEAR_FEATURE << 8) | USB_RECIP_OTHER | USB_TYPE_CLASS: 7506e9e0626SOleksandr Tymoshenko switch (wValue) { 7516e9e0626SOleksandr Tymoshenko case USB_PORT_FEAT_C_CONNECTION: 7526e9e0626SOleksandr Tymoshenko setbits_le32(®s->hprt0, DWC2_HPRT0_PRTCONNDET); 7536e9e0626SOleksandr Tymoshenko break; 7546e9e0626SOleksandr Tymoshenko } 7556e9e0626SOleksandr Tymoshenko break; 7566e9e0626SOleksandr Tymoshenko 7576e9e0626SOleksandr Tymoshenko case (USB_REQ_SET_FEATURE << 8) | USB_RECIP_OTHER | USB_TYPE_CLASS: 7586e9e0626SOleksandr Tymoshenko switch (wValue) { 7596e9e0626SOleksandr Tymoshenko case USB_PORT_FEAT_SUSPEND: 7606e9e0626SOleksandr Tymoshenko break; 7616e9e0626SOleksandr Tymoshenko 7626e9e0626SOleksandr Tymoshenko case USB_PORT_FEAT_RESET: 7636e9e0626SOleksandr Tymoshenko clrsetbits_le32(®s->hprt0, DWC2_HPRT0_PRTENA | 7646e9e0626SOleksandr Tymoshenko DWC2_HPRT0_PRTCONNDET | 7656e9e0626SOleksandr Tymoshenko DWC2_HPRT0_PRTENCHNG | 7666e9e0626SOleksandr Tymoshenko DWC2_HPRT0_PRTOVRCURRCHNG, 7676e9e0626SOleksandr Tymoshenko DWC2_HPRT0_PRTRST); 7686e9e0626SOleksandr Tymoshenko mdelay(50); 7696e9e0626SOleksandr Tymoshenko clrbits_le32(®s->hprt0, DWC2_HPRT0_PRTRST); 7706e9e0626SOleksandr Tymoshenko break; 7716e9e0626SOleksandr Tymoshenko 7726e9e0626SOleksandr Tymoshenko case USB_PORT_FEAT_POWER: 7736e9e0626SOleksandr Tymoshenko clrsetbits_le32(®s->hprt0, DWC2_HPRT0_PRTENA | 7746e9e0626SOleksandr Tymoshenko DWC2_HPRT0_PRTCONNDET | 7756e9e0626SOleksandr Tymoshenko DWC2_HPRT0_PRTENCHNG | 7766e9e0626SOleksandr Tymoshenko DWC2_HPRT0_PRTOVRCURRCHNG, 7776e9e0626SOleksandr Tymoshenko DWC2_HPRT0_PRTRST); 7786e9e0626SOleksandr Tymoshenko break; 7796e9e0626SOleksandr Tymoshenko 7806e9e0626SOleksandr Tymoshenko case USB_PORT_FEAT_ENABLE: 7816e9e0626SOleksandr Tymoshenko break; 7826e9e0626SOleksandr Tymoshenko } 7836e9e0626SOleksandr Tymoshenko break; 7846e9e0626SOleksandr Tymoshenko case (USB_REQ_SET_ADDRESS << 8): 785cc3e3a9eSSimon Glass priv->root_hub_devnum = wValue; 7866e9e0626SOleksandr Tymoshenko break; 7876e9e0626SOleksandr Tymoshenko case (USB_REQ_SET_CONFIGURATION << 8): 7886e9e0626SOleksandr Tymoshenko break; 7896e9e0626SOleksandr Tymoshenko default: 7906e9e0626SOleksandr Tymoshenko puts("unsupported root hub command\n"); 7916e9e0626SOleksandr Tymoshenko stat = USB_ST_STALLED; 7926e9e0626SOleksandr Tymoshenko } 7936e9e0626SOleksandr Tymoshenko 7946e9e0626SOleksandr Tymoshenko len = min(len, txlen); 7956e9e0626SOleksandr Tymoshenko 7966e9e0626SOleksandr Tymoshenko dev->act_len = len; 7976e9e0626SOleksandr Tymoshenko dev->status = stat; 7986e9e0626SOleksandr Tymoshenko 7996e9e0626SOleksandr Tymoshenko return stat; 8006e9e0626SOleksandr Tymoshenko } 8016e9e0626SOleksandr Tymoshenko 802cc3e3a9eSSimon Glass static int dwc_otg_submit_rh_msg(struct dwc2_priv *priv, struct usb_device *dev, 803cc3e3a9eSSimon Glass unsigned long pipe, void *buffer, int txlen, 8046e9e0626SOleksandr Tymoshenko struct devrequest *cmd) 8056e9e0626SOleksandr Tymoshenko { 8066e9e0626SOleksandr Tymoshenko int stat = 0; 8076e9e0626SOleksandr Tymoshenko 8086e9e0626SOleksandr Tymoshenko if (usb_pipeint(pipe)) { 8096e9e0626SOleksandr Tymoshenko puts("Root-Hub submit IRQ: NOT implemented\n"); 8106e9e0626SOleksandr Tymoshenko return 0; 8116e9e0626SOleksandr Tymoshenko } 8126e9e0626SOleksandr Tymoshenko 8136e9e0626SOleksandr Tymoshenko if (cmd->requesttype & USB_DIR_IN) 814cc3e3a9eSSimon Glass stat = dwc_otg_submit_rh_msg_in(priv, dev, buffer, txlen, cmd); 8156e9e0626SOleksandr Tymoshenko else 816cc3e3a9eSSimon Glass stat = dwc_otg_submit_rh_msg_out(priv, dev, buffer, txlen, cmd); 8176e9e0626SOleksandr Tymoshenko 8186e9e0626SOleksandr Tymoshenko mdelay(1); 8196e9e0626SOleksandr Tymoshenko 8206e9e0626SOleksandr Tymoshenko return stat; 8216e9e0626SOleksandr Tymoshenko } 8226e9e0626SOleksandr Tymoshenko 82325612f23SStefan Brüns int wait_for_chhltd(struct dwc2_hc_regs *hc_regs, uint32_t *sub, u8 *toggle) 8244a1d21fcSStephen Warren { 8254a1d21fcSStephen Warren int ret; 8264a1d21fcSStephen Warren uint32_t hcint, hctsiz; 8274a1d21fcSStephen Warren 828b491b498SJon Lin ret = wait_for_bit_le32(&hc_regs->hcint, DWC2_HCINT_CHHLTD, true, 8298d1c811eSChristophe Kerello 2000, false); 8304a1d21fcSStephen Warren if (ret) 8314a1d21fcSStephen Warren return ret; 8324a1d21fcSStephen Warren 8334a1d21fcSStephen Warren hcint = readl(&hc_regs->hcint); 8344a1d21fcSStephen Warren hctsiz = readl(&hc_regs->hctsiz); 8354a1d21fcSStephen Warren *sub = (hctsiz & DWC2_HCTSIZ_XFERSIZE_MASK) >> 8364a1d21fcSStephen Warren DWC2_HCTSIZ_XFERSIZE_OFFSET; 83766ffc875SStephen Warren *toggle = (hctsiz & DWC2_HCTSIZ_PID_MASK) >> DWC2_HCTSIZ_PID_OFFSET; 8384a1d21fcSStephen Warren 83903460cdcSStefan Brüns debug("%s: HCINT=%08x sub=%u toggle=%d\n", __func__, hcint, *sub, 84003460cdcSStefan Brüns *toggle); 8414a1d21fcSStephen Warren 84203460cdcSStefan Brüns if (hcint & DWC2_HCINT_XFERCOMP) 8434a1d21fcSStephen Warren return 0; 84403460cdcSStefan Brüns 84503460cdcSStefan Brüns if (hcint & (DWC2_HCINT_NAK | DWC2_HCINT_FRMOVRUN)) 84603460cdcSStefan Brüns return -EAGAIN; 84703460cdcSStefan Brüns 84803460cdcSStefan Brüns debug("%s: Error (HCINT=%08x)\n", __func__, hcint); 84903460cdcSStefan Brüns return -EINVAL; 8504a1d21fcSStephen Warren } 8514a1d21fcSStephen Warren 8527b5e504dSStephen Warren static int dwc2_eptype[] = { 8537b5e504dSStephen Warren DWC2_HCCHAR_EPTYPE_ISOC, 8547b5e504dSStephen Warren DWC2_HCCHAR_EPTYPE_INTR, 8557b5e504dSStephen Warren DWC2_HCCHAR_EPTYPE_CONTROL, 8567b5e504dSStephen Warren DWC2_HCCHAR_EPTYPE_BULK, 8577b5e504dSStephen Warren }; 8587b5e504dSStephen Warren 859daed3059SStefan Brüns static int transfer_chunk(struct dwc2_hc_regs *hc_regs, void *aligned_buffer, 86025612f23SStefan Brüns u8 *pid, int in, void *buffer, int num_packets, 861d2ff51b3SStefan Brüns int xfer_len, int *actual_len, int odd_frame) 8626e9e0626SOleksandr Tymoshenko { 8635877de91SStephen Warren int ret = 0; 8644a1d21fcSStephen Warren uint32_t sub; 8656e9e0626SOleksandr Tymoshenko 8667b5e504dSStephen Warren debug("%s: chunk: pid %d xfer_len %u pkts %u\n", __func__, 8677b5e504dSStephen Warren *pid, xfer_len, num_packets); 8687b5e504dSStephen Warren 8696e9e0626SOleksandr Tymoshenko writel((xfer_len << DWC2_HCTSIZ_XFERSIZE_OFFSET) | 8706e9e0626SOleksandr Tymoshenko (num_packets << DWC2_HCTSIZ_PKTCNT_OFFSET) | 8717b5e504dSStephen Warren (*pid << DWC2_HCTSIZ_PID_OFFSET), 8726e9e0626SOleksandr Tymoshenko &hc_regs->hctsiz); 8736e9e0626SOleksandr Tymoshenko 87457ca63b8SEddie Cai if (xfer_len) { 87557ca63b8SEddie Cai if (in) { 87657ca63b8SEddie Cai invalidate_dcache_range( 87757ca63b8SEddie Cai (uintptr_t)aligned_buffer, 87857ca63b8SEddie Cai (uintptr_t)aligned_buffer + 879daed3059SStefan Brüns roundup(xfer_len, ARCH_DMA_MINALIGN)); 88057ca63b8SEddie Cai } else { 88157ca63b8SEddie Cai memcpy(aligned_buffer, buffer, xfer_len); 88257ca63b8SEddie Cai flush_dcache_range( 88357ca63b8SEddie Cai (uintptr_t)aligned_buffer, 88457ca63b8SEddie Cai (uintptr_t)aligned_buffer + 88557ca63b8SEddie Cai roundup(xfer_len, ARCH_DMA_MINALIGN)); 88657ca63b8SEddie Cai } 887cc3e3a9eSSimon Glass } 888d1c880c6SStephen Warren 889daed3059SStefan Brüns writel(phys_to_bus((unsigned long)aligned_buffer), &hc_regs->hcdma); 890daed3059SStefan Brüns 891daed3059SStefan Brüns /* Clear old interrupt conditions for this host channel. */ 892daed3059SStefan Brüns writel(0x3fff, &hc_regs->hcint); 8936e9e0626SOleksandr Tymoshenko 8946e9e0626SOleksandr Tymoshenko /* Set host channel enable after all other setup is complete. */ 8956e9e0626SOleksandr Tymoshenko clrsetbits_le32(&hc_regs->hcchar, DWC2_HCCHAR_MULTICNT_MASK | 896d2ff51b3SStefan Brüns DWC2_HCCHAR_CHEN | DWC2_HCCHAR_CHDIS | 897d2ff51b3SStefan Brüns DWC2_HCCHAR_ODDFRM, 8986e9e0626SOleksandr Tymoshenko (1 << DWC2_HCCHAR_MULTICNT_OFFSET) | 899d2ff51b3SStefan Brüns (odd_frame << DWC2_HCCHAR_ODDFRM_OFFSET) | 9006e9e0626SOleksandr Tymoshenko DWC2_HCCHAR_CHEN); 9016e9e0626SOleksandr Tymoshenko 902daed3059SStefan Brüns ret = wait_for_chhltd(hc_regs, &sub, pid); 903daed3059SStefan Brüns if (ret < 0) 904daed3059SStefan Brüns return ret; 9056e9e0626SOleksandr Tymoshenko 9067b5e504dSStephen Warren if (in) { 907d1c880c6SStephen Warren xfer_len -= sub; 908db402e00SAlexander Stein 909daed3059SStefan Brüns invalidate_dcache_range((unsigned long)aligned_buffer, 910daed3059SStefan Brüns (unsigned long)aligned_buffer + 911daed3059SStefan Brüns roundup(xfer_len, ARCH_DMA_MINALIGN)); 912db402e00SAlexander Stein 913daed3059SStefan Brüns memcpy(buffer, aligned_buffer, xfer_len); 914daed3059SStefan Brüns } 915daed3059SStefan Brüns *actual_len = xfer_len; 916daed3059SStefan Brüns 917daed3059SStefan Brüns return ret; 9186e9e0626SOleksandr Tymoshenko } 9196e9e0626SOleksandr Tymoshenko 9206e9e0626SOleksandr Tymoshenko int chunk_msg(struct dwc2_priv *priv, struct usb_device *dev, 92125612f23SStefan Brüns unsigned long pipe, u8 *pid, int in, void *buffer, int len) 9226e9e0626SOleksandr Tymoshenko { 9236e9e0626SOleksandr Tymoshenko struct dwc2_core_regs *regs = priv->regs; 9246e9e0626SOleksandr Tymoshenko struct dwc2_hc_regs *hc_regs = ®s->hc_regs[DWC2_HC_CHANNEL]; 925d2ff51b3SStefan Brüns struct dwc2_host_regs *host_regs = ®s->host_regs; 9266e9e0626SOleksandr Tymoshenko int devnum = usb_pipedevice(pipe); 9276e9e0626SOleksandr Tymoshenko int ep = usb_pipeendpoint(pipe); 9286e9e0626SOleksandr Tymoshenko int max = usb_maxpacket(dev, pipe); 9296e9e0626SOleksandr Tymoshenko int eptype = dwc2_eptype[usb_pipetype(pipe)]; 9306e9e0626SOleksandr Tymoshenko int done = 0; 9316e9e0626SOleksandr Tymoshenko int ret = 0; 932b54e4470SStefan Brüns int do_split = 0; 933b54e4470SStefan Brüns int complete_split = 0; 9346e9e0626SOleksandr Tymoshenko uint32_t xfer_len; 9356e9e0626SOleksandr Tymoshenko uint32_t num_packets; 9366e9e0626SOleksandr Tymoshenko int stop_transfer = 0; 93756a7bbd7SStefan Brüns uint32_t max_xfer_len; 938d2ff51b3SStefan Brüns int ssplit_frame_num = 0; 939d1c880c6SStephen Warren 9406e9e0626SOleksandr Tymoshenko debug("%s: msg: pipe %lx pid %d in %d len %d\n", __func__, pipe, *pid, 9416e9e0626SOleksandr Tymoshenko in, len); 9426e9e0626SOleksandr Tymoshenko 94356a7bbd7SStefan Brüns max_xfer_len = CONFIG_DWC2_MAX_PACKET_COUNT * max; 94456a7bbd7SStefan Brüns if (max_xfer_len > CONFIG_DWC2_MAX_TRANSFER_SIZE) 94556a7bbd7SStefan Brüns max_xfer_len = CONFIG_DWC2_MAX_TRANSFER_SIZE; 94656a7bbd7SStefan Brüns if (max_xfer_len > DWC2_DATA_BUF_SIZE) 94756a7bbd7SStefan Brüns max_xfer_len = DWC2_DATA_BUF_SIZE; 94856a7bbd7SStefan Brüns 94956a7bbd7SStefan Brüns /* Make sure that max_xfer_len is a multiple of max packet size. */ 95056a7bbd7SStefan Brüns num_packets = max_xfer_len / max; 95156a7bbd7SStefan Brüns max_xfer_len = num_packets * max; 95256a7bbd7SStefan Brüns 9536e9e0626SOleksandr Tymoshenko /* Initialize channel */ 9546e9e0626SOleksandr Tymoshenko dwc_otg_hc_init(regs, DWC2_HC_CHANNEL, dev, devnum, ep, in, 9556e9e0626SOleksandr Tymoshenko eptype, max); 9566e9e0626SOleksandr Tymoshenko 957b54e4470SStefan Brüns /* Check if the target is a FS/LS device behind a HS hub */ 958b54e4470SStefan Brüns if (dev->speed != USB_SPEED_HIGH) { 959b54e4470SStefan Brüns uint8_t hub_addr; 960b54e4470SStefan Brüns uint8_t hub_port; 961b54e4470SStefan Brüns uint32_t hprt0 = readl(®s->hprt0); 962b54e4470SStefan Brüns if ((hprt0 & DWC2_HPRT0_PRTSPD_MASK) == 963b54e4470SStefan Brüns DWC2_HPRT0_PRTSPD_HIGH) { 964b54e4470SStefan Brüns usb_find_usb2_hub_address_port(dev, &hub_addr, 965b54e4470SStefan Brüns &hub_port); 966b54e4470SStefan Brüns dwc_otg_hc_init_split(hc_regs, hub_addr, hub_port); 967b54e4470SStefan Brüns 968b54e4470SStefan Brüns do_split = 1; 969b54e4470SStefan Brüns num_packets = 1; 970b54e4470SStefan Brüns max_xfer_len = max; 971b54e4470SStefan Brüns } 972b54e4470SStefan Brüns } 973b54e4470SStefan Brüns 974daed3059SStefan Brüns do { 975daed3059SStefan Brüns int actual_len = 0; 976b54e4470SStefan Brüns uint32_t hcint; 977d2ff51b3SStefan Brüns int odd_frame = 0; 9786e9e0626SOleksandr Tymoshenko xfer_len = len - done; 9796e9e0626SOleksandr Tymoshenko 98056a7bbd7SStefan Brüns if (xfer_len > max_xfer_len) 98156a7bbd7SStefan Brüns xfer_len = max_xfer_len; 98256a7bbd7SStefan Brüns else if (xfer_len > max) 9836e9e0626SOleksandr Tymoshenko num_packets = (xfer_len + max - 1) / max; 98456a7bbd7SStefan Brüns else 9856e9e0626SOleksandr Tymoshenko num_packets = 1; 9866e9e0626SOleksandr Tymoshenko 987b54e4470SStefan Brüns if (complete_split) 988b54e4470SStefan Brüns setbits_le32(&hc_regs->hcsplt, DWC2_HCSPLT_COMPSPLT); 989b54e4470SStefan Brüns else if (do_split) 990b54e4470SStefan Brüns clrbits_le32(&hc_regs->hcsplt, DWC2_HCSPLT_COMPSPLT); 991b54e4470SStefan Brüns 992d2ff51b3SStefan Brüns if (eptype == DWC2_HCCHAR_EPTYPE_INTR) { 993d2ff51b3SStefan Brüns int uframe_num = readl(&host_regs->hfnum); 994d2ff51b3SStefan Brüns if (!(uframe_num & 0x1)) 995d2ff51b3SStefan Brüns odd_frame = 1; 996d2ff51b3SStefan Brüns } 997d2ff51b3SStefan Brüns 998daed3059SStefan Brüns ret = transfer_chunk(hc_regs, priv->aligned_buffer, pid, 999daed3059SStefan Brüns in, (char *)buffer + done, num_packets, 1000d2ff51b3SStefan Brüns xfer_len, &actual_len, odd_frame); 10016e9e0626SOleksandr Tymoshenko 1002b54e4470SStefan Brüns hcint = readl(&hc_regs->hcint); 1003b54e4470SStefan Brüns if (complete_split) { 1004b54e4470SStefan Brüns stop_transfer = 0; 1005d2ff51b3SStefan Brüns if (hcint & DWC2_HCINT_NYET) { 1006b54e4470SStefan Brüns ret = 0; 1007d2ff51b3SStefan Brüns int frame_num = DWC2_HFNUM_MAX_FRNUM & 1008d2ff51b3SStefan Brüns readl(&host_regs->hfnum); 1009d2ff51b3SStefan Brüns if (((frame_num - ssplit_frame_num) & 1010d2ff51b3SStefan Brüns DWC2_HFNUM_MAX_FRNUM) > 4) 1011d2ff51b3SStefan Brüns ret = -EAGAIN; 1012d2ff51b3SStefan Brüns } else 1013b54e4470SStefan Brüns complete_split = 0; 1014b54e4470SStefan Brüns } else if (do_split) { 1015b54e4470SStefan Brüns if (hcint & DWC2_HCINT_ACK) { 1016d2ff51b3SStefan Brüns ssplit_frame_num = DWC2_HFNUM_MAX_FRNUM & 1017d2ff51b3SStefan Brüns readl(&host_regs->hfnum); 1018b54e4470SStefan Brüns ret = 0; 1019b54e4470SStefan Brüns complete_split = 1; 1020b54e4470SStefan Brüns } 1021b54e4470SStefan Brüns } 1022b54e4470SStefan Brüns 10236e9e0626SOleksandr Tymoshenko if (ret) 10246e9e0626SOleksandr Tymoshenko break; 10256e9e0626SOleksandr Tymoshenko 1026daed3059SStefan Brüns if (actual_len < xfer_len) 10276e9e0626SOleksandr Tymoshenko stop_transfer = 1; 10286e9e0626SOleksandr Tymoshenko 1029daed3059SStefan Brüns done += actual_len; 1030d1c880c6SStephen Warren 1031b54e4470SStefan Brüns /* Transactions are done when when either all data is transferred or 1032b54e4470SStefan Brüns * there is a short transfer. In case of a SPLIT make sure the CSPLIT 1033b54e4470SStefan Brüns * is executed. 1034b54e4470SStefan Brüns */ 1035b54e4470SStefan Brüns } while (((done < len) && !stop_transfer) || complete_split); 10366e9e0626SOleksandr Tymoshenko 10376e9e0626SOleksandr Tymoshenko writel(0, &hc_regs->hcintmsk); 10386e9e0626SOleksandr Tymoshenko writel(0xFFFFFFFF, &hc_regs->hcint); 10396e9e0626SOleksandr Tymoshenko 10406e9e0626SOleksandr Tymoshenko dev->status = 0; 10416e9e0626SOleksandr Tymoshenko dev->act_len = done; 10426e9e0626SOleksandr Tymoshenko 10435877de91SStephen Warren return ret; 10446e9e0626SOleksandr Tymoshenko } 10456e9e0626SOleksandr Tymoshenko 10467b5e504dSStephen Warren /* U-Boot USB transmission interface */ 1047cc3e3a9eSSimon Glass int _submit_bulk_msg(struct dwc2_priv *priv, struct usb_device *dev, 1048cc3e3a9eSSimon Glass unsigned long pipe, void *buffer, int len) 10497b5e504dSStephen Warren { 10507b5e504dSStephen Warren int devnum = usb_pipedevice(pipe); 10517b5e504dSStephen Warren int ep = usb_pipeendpoint(pipe); 105225612f23SStefan Brüns u8* pid; 10537b5e504dSStephen Warren 105425612f23SStefan Brüns if ((devnum >= MAX_DEVICE) || (devnum == priv->root_hub_devnum)) { 10557b5e504dSStephen Warren dev->status = 0; 10567b5e504dSStephen Warren return -EINVAL; 10577b5e504dSStephen Warren } 10587b5e504dSStephen Warren 105925612f23SStefan Brüns if (usb_pipein(pipe)) 106025612f23SStefan Brüns pid = &priv->in_data_toggle[devnum][ep]; 106125612f23SStefan Brüns else 106225612f23SStefan Brüns pid = &priv->out_data_toggle[devnum][ep]; 106325612f23SStefan Brüns 106425612f23SStefan Brüns return chunk_msg(priv, dev, pipe, pid, usb_pipein(pipe), buffer, len); 10657b5e504dSStephen Warren } 10667b5e504dSStephen Warren 1067cc3e3a9eSSimon Glass static int _submit_control_msg(struct dwc2_priv *priv, struct usb_device *dev, 1068cc3e3a9eSSimon Glass unsigned long pipe, void *buffer, int len, 1069cc3e3a9eSSimon Glass struct devrequest *setup) 10706e9e0626SOleksandr Tymoshenko { 10716e9e0626SOleksandr Tymoshenko int devnum = usb_pipedevice(pipe); 107225612f23SStefan Brüns int ret, act_len; 107325612f23SStefan Brüns u8 pid; 10746e9e0626SOleksandr Tymoshenko /* For CONTROL endpoint pid should start with DATA1 */ 10756e9e0626SOleksandr Tymoshenko int status_direction; 10766e9e0626SOleksandr Tymoshenko 1077cc3e3a9eSSimon Glass if (devnum == priv->root_hub_devnum) { 10786e9e0626SOleksandr Tymoshenko dev->status = 0; 10796e9e0626SOleksandr Tymoshenko dev->speed = USB_SPEED_HIGH; 1080cc3e3a9eSSimon Glass return dwc_otg_submit_rh_msg(priv, dev, pipe, buffer, len, 1081cc3e3a9eSSimon Glass setup); 10826e9e0626SOleksandr Tymoshenko } 10836e9e0626SOleksandr Tymoshenko 1084b54e4470SStefan Brüns /* SETUP stage */ 1085ee837554SStephen Warren pid = DWC2_HC_PID_SETUP; 1086b54e4470SStefan Brüns do { 108703460cdcSStefan Brüns ret = chunk_msg(priv, dev, pipe, &pid, 0, setup, 8); 1088b54e4470SStefan Brüns } while (ret == -EAGAIN); 1089ee837554SStephen Warren if (ret) 1090ee837554SStephen Warren return ret; 10916e9e0626SOleksandr Tymoshenko 1092b54e4470SStefan Brüns /* DATA stage */ 1093b54e4470SStefan Brüns act_len = 0; 10946e9e0626SOleksandr Tymoshenko if (buffer) { 1095282685e0SStephen Warren pid = DWC2_HC_PID_DATA1; 1096b54e4470SStefan Brüns do { 1097b54e4470SStefan Brüns ret = chunk_msg(priv, dev, pipe, &pid, usb_pipein(pipe), 1098b54e4470SStefan Brüns buffer, len); 1099b54e4470SStefan Brüns act_len += dev->act_len; 1100b54e4470SStefan Brüns buffer += dev->act_len; 1101b54e4470SStefan Brüns len -= dev->act_len; 1102b54e4470SStefan Brüns } while (ret == -EAGAIN); 1103ee837554SStephen Warren if (ret) 1104ee837554SStephen Warren return ret; 1105b54e4470SStefan Brüns status_direction = usb_pipeout(pipe); 1106b54e4470SStefan Brüns } else { 1107b54e4470SStefan Brüns /* No-data CONTROL always ends with an IN transaction */ 1108b54e4470SStefan Brüns status_direction = 1; 1109b54e4470SStefan Brüns } 11106e9e0626SOleksandr Tymoshenko 11116e9e0626SOleksandr Tymoshenko /* STATUS stage */ 1112ee837554SStephen Warren pid = DWC2_HC_PID_DATA1; 1113b54e4470SStefan Brüns do { 1114cc3e3a9eSSimon Glass ret = chunk_msg(priv, dev, pipe, &pid, status_direction, 111503460cdcSStefan Brüns priv->status_buffer, 0); 1116b54e4470SStefan Brüns } while (ret == -EAGAIN); 1117ee837554SStephen Warren if (ret) 1118ee837554SStephen Warren return ret; 11196e9e0626SOleksandr Tymoshenko 1120ee837554SStephen Warren dev->act_len = act_len; 11216e9e0626SOleksandr Tymoshenko 11224a1d21fcSStephen Warren return 0; 11236e9e0626SOleksandr Tymoshenko } 11246e9e0626SOleksandr Tymoshenko 1125cc3e3a9eSSimon Glass int _submit_int_msg(struct dwc2_priv *priv, struct usb_device *dev, 112692937b1fSMichal Suchanek unsigned long pipe, void *buffer, int len, int interval, 112792937b1fSMichal Suchanek bool nonblock) 11286e9e0626SOleksandr Tymoshenko { 11295877de91SStephen Warren unsigned long timeout; 11305877de91SStephen Warren int ret; 11315877de91SStephen Warren 1132e236519bSStephen Warren /* FIXME: what is interval? */ 11335877de91SStephen Warren 11345877de91SStephen Warren timeout = get_timer(0) + USB_TIMEOUT_MS(pipe); 11355877de91SStephen Warren for (;;) { 11365877de91SStephen Warren if (get_timer(0) > timeout) { 1137071d6bebSPatrice Chotard dev_err(dev, "Timeout poll on interrupt endpoint\n"); 11385877de91SStephen Warren return -ETIMEDOUT; 11395877de91SStephen Warren } 1140cc3e3a9eSSimon Glass ret = _submit_bulk_msg(priv, dev, pipe, buffer, len); 114185bca00eSMichal Suchanek if ((ret != -EAGAIN) || nonblock) 11425877de91SStephen Warren return ret; 11435877de91SStephen Warren } 11446e9e0626SOleksandr Tymoshenko } 11456e9e0626SOleksandr Tymoshenko 1146a1bebf37SLey Foon Tan static int dwc2_reset(struct udevice *dev) 1147a1bebf37SLey Foon Tan { 1148a1bebf37SLey Foon Tan int ret; 1149a1bebf37SLey Foon Tan struct dwc2_priv *priv = dev_get_priv(dev); 1150a1bebf37SLey Foon Tan 1151a1bebf37SLey Foon Tan ret = reset_get_bulk(dev, &priv->resets); 1152a1bebf37SLey Foon Tan if (ret) { 1153a1bebf37SLey Foon Tan dev_warn(dev, "Can't get reset: %d\n", ret); 1154a1bebf37SLey Foon Tan /* Return 0 if error due to !CONFIG_DM_RESET and reset 1155a1bebf37SLey Foon Tan * DT property is not present. 1156a1bebf37SLey Foon Tan */ 1157a1bebf37SLey Foon Tan if (ret == -ENOENT || ret == -ENOTSUPP) 1158a1bebf37SLey Foon Tan return 0; 1159a1bebf37SLey Foon Tan else 1160a1bebf37SLey Foon Tan return ret; 1161a1bebf37SLey Foon Tan } 1162a1bebf37SLey Foon Tan 11633fd577deSPatrick Delaunay /* force reset to clear all IP register */ 11643fd577deSPatrick Delaunay reset_assert_bulk(&priv->resets); 1165a1bebf37SLey Foon Tan ret = reset_deassert_bulk(&priv->resets); 1166a1bebf37SLey Foon Tan if (ret) { 1167a1bebf37SLey Foon Tan reset_release_bulk(&priv->resets); 1168a1bebf37SLey Foon Tan dev_err(dev, "Failed to reset: %d\n", ret); 1169a1bebf37SLey Foon Tan return ret; 1170a1bebf37SLey Foon Tan } 1171a1bebf37SLey Foon Tan 1172a1bebf37SLey Foon Tan return 0; 1173a1bebf37SLey Foon Tan } 1174a1bebf37SLey Foon Tan 11755c735367SKever Yang static int dwc2_init_common(struct udevice *dev, struct dwc2_priv *priv) 11766e9e0626SOleksandr Tymoshenko { 1177cc3e3a9eSSimon Glass struct dwc2_core_regs *regs = priv->regs; 11786e9e0626SOleksandr Tymoshenko uint32_t snpsid; 11796e9e0626SOleksandr Tymoshenko int i, j; 1180a1bebf37SLey Foon Tan int ret; 1181a1bebf37SLey Foon Tan 1182a1bebf37SLey Foon Tan ret = dwc2_reset(dev); 1183a1bebf37SLey Foon Tan if (ret) 1184a1bebf37SLey Foon Tan return ret; 11856e9e0626SOleksandr Tymoshenko 11866e9e0626SOleksandr Tymoshenko snpsid = readl(®s->gsnpsid); 1187071d6bebSPatrice Chotard dev_info(dev, "Core Release: %x.%03x\n", 1188071d6bebSPatrice Chotard snpsid >> 12 & 0xf, snpsid & 0xfff); 11896e9e0626SOleksandr Tymoshenko 11905cfd6c00SPeter Griffin if ((snpsid & DWC2_SNPSID_DEVID_MASK) != DWC2_SNPSID_DEVID_VER_2xx && 11915cfd6c00SPeter Griffin (snpsid & DWC2_SNPSID_DEVID_MASK) != DWC2_SNPSID_DEVID_VER_3xx) { 1192071d6bebSPatrice Chotard dev_info(dev, "SNPSID invalid (not DWC2 OTG device): %08x\n", 1193071d6bebSPatrice Chotard snpsid); 11946e9e0626SOleksandr Tymoshenko return -ENODEV; 11956e9e0626SOleksandr Tymoshenko } 11966e9e0626SOleksandr Tymoshenko 1197618da563SMarek Vasut #ifdef CONFIG_DWC2_PHY_ULPI_EXT_VBUS 1198618da563SMarek Vasut priv->ext_vbus = 1; 1199618da563SMarek Vasut #else 1200618da563SMarek Vasut priv->ext_vbus = 0; 1201618da563SMarek Vasut #endif 1202618da563SMarek Vasut 120355901989SMarek Vasut dwc_otg_core_init(priv); 12045c735367SKever Yang dwc_otg_core_host_init(dev, regs); 12056e9e0626SOleksandr Tymoshenko 12066e9e0626SOleksandr Tymoshenko clrsetbits_le32(®s->hprt0, DWC2_HPRT0_PRTENA | 12076e9e0626SOleksandr Tymoshenko DWC2_HPRT0_PRTCONNDET | DWC2_HPRT0_PRTENCHNG | 12086e9e0626SOleksandr Tymoshenko DWC2_HPRT0_PRTOVRCURRCHNG, 12096e9e0626SOleksandr Tymoshenko DWC2_HPRT0_PRTRST); 12106e9e0626SOleksandr Tymoshenko mdelay(50); 12116e9e0626SOleksandr Tymoshenko clrbits_le32(®s->hprt0, DWC2_HPRT0_PRTENA | DWC2_HPRT0_PRTCONNDET | 12126e9e0626SOleksandr Tymoshenko DWC2_HPRT0_PRTENCHNG | DWC2_HPRT0_PRTOVRCURRCHNG | 12136e9e0626SOleksandr Tymoshenko DWC2_HPRT0_PRTRST); 12146e9e0626SOleksandr Tymoshenko 12156e9e0626SOleksandr Tymoshenko for (i = 0; i < MAX_DEVICE; i++) { 121625612f23SStefan Brüns for (j = 0; j < MAX_ENDPOINT; j++) { 121725612f23SStefan Brüns priv->in_data_toggle[i][j] = DWC2_HC_PID_DATA0; 121825612f23SStefan Brüns priv->out_data_toggle[i][j] = DWC2_HC_PID_DATA0; 121925612f23SStefan Brüns } 12206e9e0626SOleksandr Tymoshenko } 12216e9e0626SOleksandr Tymoshenko 12222bf352f0SStefan Roese /* 12232bf352f0SStefan Roese * Add a 1 second delay here. This gives the host controller 12242bf352f0SStefan Roese * a bit time before the comminucation with the USB devices 12252bf352f0SStefan Roese * is started (the bus is scanned) and fixes the USB detection 12262bf352f0SStefan Roese * problems with some problematic USB keys. 12272bf352f0SStefan Roese */ 12282bf352f0SStefan Roese if (readl(®s->gintsts) & DWC2_GINTSTS_CURMODE_HOST) 12292bf352f0SStefan Roese mdelay(1000); 12302bf352f0SStefan Roese 12317bcaf050SPatrick Delaunay printf("USB DWC2\n"); 12327bcaf050SPatrick Delaunay 12336e9e0626SOleksandr Tymoshenko return 0; 12346e9e0626SOleksandr Tymoshenko } 12356e9e0626SOleksandr Tymoshenko 1236cc3e3a9eSSimon Glass static void dwc2_uninit_common(struct dwc2_core_regs *regs) 12376e9e0626SOleksandr Tymoshenko { 12386e9e0626SOleksandr Tymoshenko /* Put everything in reset. */ 12396e9e0626SOleksandr Tymoshenko clrsetbits_le32(®s->hprt0, DWC2_HPRT0_PRTENA | 12406e9e0626SOleksandr Tymoshenko DWC2_HPRT0_PRTCONNDET | DWC2_HPRT0_PRTENCHNG | 12416e9e0626SOleksandr Tymoshenko DWC2_HPRT0_PRTOVRCURRCHNG, 12426e9e0626SOleksandr Tymoshenko DWC2_HPRT0_PRTRST); 1243cc3e3a9eSSimon Glass } 1244cc3e3a9eSSimon Glass 12453739bf7eSSven Schwermer #if !CONFIG_IS_ENABLED(DM_USB) 1246cc3e3a9eSSimon Glass int submit_control_msg(struct usb_device *dev, unsigned long pipe, void *buffer, 1247cc3e3a9eSSimon Glass int len, struct devrequest *setup) 1248cc3e3a9eSSimon Glass { 1249cc3e3a9eSSimon Glass return _submit_control_msg(&local, dev, pipe, buffer, len, setup); 1250cc3e3a9eSSimon Glass } 1251cc3e3a9eSSimon Glass 1252cc3e3a9eSSimon Glass int submit_bulk_msg(struct usb_device *dev, unsigned long pipe, void *buffer, 1253cc3e3a9eSSimon Glass int len) 1254cc3e3a9eSSimon Glass { 1255cc3e3a9eSSimon Glass return _submit_bulk_msg(&local, dev, pipe, buffer, len); 1256cc3e3a9eSSimon Glass } 1257cc3e3a9eSSimon Glass 1258cc3e3a9eSSimon Glass int submit_int_msg(struct usb_device *dev, unsigned long pipe, void *buffer, 125992937b1fSMichal Suchanek int len, int interval, bool nonblock) 1260cc3e3a9eSSimon Glass { 126192937b1fSMichal Suchanek return _submit_int_msg(&local, dev, pipe, buffer, len, interval, 126292937b1fSMichal Suchanek nonblock); 1263cc3e3a9eSSimon Glass } 1264cc3e3a9eSSimon Glass 1265cc3e3a9eSSimon Glass /* U-Boot USB control interface */ 1266cc3e3a9eSSimon Glass int usb_lowlevel_init(int index, enum usb_init_type init, void **controller) 1267cc3e3a9eSSimon Glass { 1268cc3e3a9eSSimon Glass struct dwc2_priv *priv = &local; 1269cc3e3a9eSSimon Glass 1270cc3e3a9eSSimon Glass memset(priv, '\0', sizeof(*priv)); 1271cc3e3a9eSSimon Glass priv->root_hub_devnum = 0; 1272cc3e3a9eSSimon Glass priv->regs = (struct dwc2_core_regs *)CONFIG_USB_DWC2_REG_ADDR; 1273cc3e3a9eSSimon Glass priv->aligned_buffer = aligned_buffer_addr; 1274cc3e3a9eSSimon Glass priv->status_buffer = status_buffer_addr; 1275cc3e3a9eSSimon Glass 1276cc3e3a9eSSimon Glass /* board-dependant init */ 1277cc3e3a9eSSimon Glass if (board_usb_init(index, USB_INIT_HOST)) 1278cc3e3a9eSSimon Glass return -1; 1279cc3e3a9eSSimon Glass 12805c735367SKever Yang return dwc2_init_common(NULL, priv); 1281cc3e3a9eSSimon Glass } 1282cc3e3a9eSSimon Glass 1283cc3e3a9eSSimon Glass int usb_lowlevel_stop(int index) 1284cc3e3a9eSSimon Glass { 1285cc3e3a9eSSimon Glass dwc2_uninit_common(local.regs); 1286cc3e3a9eSSimon Glass 12876e9e0626SOleksandr Tymoshenko return 0; 12886e9e0626SOleksandr Tymoshenko } 1289f58a41e0SSimon Glass #endif 1290f58a41e0SSimon Glass 12913739bf7eSSven Schwermer #if CONFIG_IS_ENABLED(DM_USB) 1292f58a41e0SSimon Glass static int dwc2_submit_control_msg(struct udevice *dev, struct usb_device *udev, 1293f58a41e0SSimon Glass unsigned long pipe, void *buffer, int length, 1294f58a41e0SSimon Glass struct devrequest *setup) 1295f58a41e0SSimon Glass { 1296f58a41e0SSimon Glass struct dwc2_priv *priv = dev_get_priv(dev); 1297f58a41e0SSimon Glass 1298f58a41e0SSimon Glass debug("%s: dev='%s', udev=%p, udev->dev='%s', portnr=%d\n", __func__, 1299f58a41e0SSimon Glass dev->name, udev, udev->dev->name, udev->portnr); 1300f58a41e0SSimon Glass 1301f58a41e0SSimon Glass return _submit_control_msg(priv, udev, pipe, buffer, length, setup); 1302f58a41e0SSimon Glass } 1303f58a41e0SSimon Glass 1304f58a41e0SSimon Glass static int dwc2_submit_bulk_msg(struct udevice *dev, struct usb_device *udev, 1305f58a41e0SSimon Glass unsigned long pipe, void *buffer, int length) 1306f58a41e0SSimon Glass { 1307f58a41e0SSimon Glass struct dwc2_priv *priv = dev_get_priv(dev); 1308f58a41e0SSimon Glass 1309f58a41e0SSimon Glass debug("%s: dev='%s', udev=%p\n", __func__, dev->name, udev); 1310f58a41e0SSimon Glass 1311f58a41e0SSimon Glass return _submit_bulk_msg(priv, udev, pipe, buffer, length); 1312f58a41e0SSimon Glass } 1313f58a41e0SSimon Glass 1314f58a41e0SSimon Glass static int dwc2_submit_int_msg(struct udevice *dev, struct usb_device *udev, 1315f58a41e0SSimon Glass unsigned long pipe, void *buffer, int length, 131692937b1fSMichal Suchanek int interval, bool nonblock) 1317f58a41e0SSimon Glass { 1318f58a41e0SSimon Glass struct dwc2_priv *priv = dev_get_priv(dev); 1319f58a41e0SSimon Glass 1320f58a41e0SSimon Glass debug("%s: dev='%s', udev=%p\n", __func__, dev->name, udev); 1321f58a41e0SSimon Glass 132292937b1fSMichal Suchanek return _submit_int_msg(priv, udev, pipe, buffer, length, interval, 132392937b1fSMichal Suchanek nonblock); 1324f58a41e0SSimon Glass } 1325f58a41e0SSimon Glass 1326f58a41e0SSimon Glass static int dwc2_usb_ofdata_to_platdata(struct udevice *dev) 1327f58a41e0SSimon Glass { 1328f58a41e0SSimon Glass struct dwc2_priv *priv = dev_get_priv(dev); 1329f58a41e0SSimon Glass fdt_addr_t addr; 1330f58a41e0SSimon Glass 1331c32504a8SPhilipp Tomsich addr = dev_read_addr(dev); 1332f58a41e0SSimon Glass if (addr == FDT_ADDR_T_NONE) 1333f58a41e0SSimon Glass return -EINVAL; 1334f58a41e0SSimon Glass priv->regs = (struct dwc2_core_regs *)addr; 1335f58a41e0SSimon Glass 1336dd22baceSMeng Dongyang priv->oc_disable = dev_read_bool(dev, "disable-over-current"); 1337dd22baceSMeng Dongyang priv->hnp_srp_disable = dev_read_bool(dev, "hnp-srp-disable"); 1338c65a3494SMeng Dongyang 1339f58a41e0SSimon Glass return 0; 1340f58a41e0SSimon Glass } 1341f58a41e0SSimon Glass 13421e03ec26SPatrick Delaunay static int dwc2_setup_phy(struct udevice *dev) 13431e03ec26SPatrick Delaunay { 13441e03ec26SPatrick Delaunay struct dwc2_priv *priv = dev_get_priv(dev); 13451e03ec26SPatrick Delaunay int ret; 13461e03ec26SPatrick Delaunay 13471e03ec26SPatrick Delaunay ret = generic_phy_get_by_index(dev, 0, &priv->phy); 13481e03ec26SPatrick Delaunay if (ret) { 13491e03ec26SPatrick Delaunay if (ret == -ENOENT) 13501e03ec26SPatrick Delaunay return 0; /* no PHY, nothing to do */ 13511e03ec26SPatrick Delaunay dev_err(dev, "Failed to get USB PHY: %d.\n", ret); 13521e03ec26SPatrick Delaunay return ret; 13531e03ec26SPatrick Delaunay } 13541e03ec26SPatrick Delaunay 13551e03ec26SPatrick Delaunay ret = generic_phy_init(&priv->phy); 13561e03ec26SPatrick Delaunay if (ret) { 13571e03ec26SPatrick Delaunay dev_dbg(dev, "Failed to init USB PHY: %d.\n", ret); 13581e03ec26SPatrick Delaunay return ret; 13591e03ec26SPatrick Delaunay } 13601e03ec26SPatrick Delaunay 13611e03ec26SPatrick Delaunay ret = generic_phy_power_on(&priv->phy); 13621e03ec26SPatrick Delaunay if (ret) { 13631e03ec26SPatrick Delaunay dev_dbg(dev, "Failed to power on USB PHY: %d.\n", ret); 13641e03ec26SPatrick Delaunay generic_phy_exit(&priv->phy); 13651e03ec26SPatrick Delaunay return ret; 13661e03ec26SPatrick Delaunay } 13671e03ec26SPatrick Delaunay 13681e03ec26SPatrick Delaunay return 0; 13691e03ec26SPatrick Delaunay } 13701e03ec26SPatrick Delaunay 13711e03ec26SPatrick Delaunay static int dwc2_shutdown_phy(struct udevice *dev) 13721e03ec26SPatrick Delaunay { 13731e03ec26SPatrick Delaunay struct dwc2_priv *priv = dev_get_priv(dev); 13741e03ec26SPatrick Delaunay int ret; 13751e03ec26SPatrick Delaunay 13761e03ec26SPatrick Delaunay /* PHY is not valid when generic_phy_get_by_index() = -ENOENT */ 13771e03ec26SPatrick Delaunay if (!generic_phy_valid(&priv->phy)) 13781e03ec26SPatrick Delaunay return 0; /* no PHY, nothing to do */ 13791e03ec26SPatrick Delaunay 13801e03ec26SPatrick Delaunay ret = generic_phy_power_off(&priv->phy); 13811e03ec26SPatrick Delaunay if (ret) { 13821e03ec26SPatrick Delaunay dev_dbg(dev, "Failed to power off USB PHY: %d.\n", ret); 13831e03ec26SPatrick Delaunay return ret; 13841e03ec26SPatrick Delaunay } 13851e03ec26SPatrick Delaunay 13861e03ec26SPatrick Delaunay ret = generic_phy_exit(&priv->phy); 13871e03ec26SPatrick Delaunay if (ret) { 13881e03ec26SPatrick Delaunay dev_dbg(dev, "Failed to power off USB PHY: %d.\n", ret); 13891e03ec26SPatrick Delaunay return ret; 13901e03ec26SPatrick Delaunay } 13911e03ec26SPatrick Delaunay 13921e03ec26SPatrick Delaunay return 0; 13931e03ec26SPatrick Delaunay } 13941e03ec26SPatrick Delaunay 13951966f56eSPatrick Delaunay static int dwc2_clk_init(struct udevice *dev) 13961966f56eSPatrick Delaunay { 13971966f56eSPatrick Delaunay struct dwc2_priv *priv = dev_get_priv(dev); 13981966f56eSPatrick Delaunay int ret; 13991966f56eSPatrick Delaunay 14001966f56eSPatrick Delaunay ret = clk_get_bulk(dev, &priv->clks); 14011966f56eSPatrick Delaunay if (ret == -ENOSYS || ret == -ENOENT) 14021966f56eSPatrick Delaunay return 0; 14031966f56eSPatrick Delaunay if (ret) 14041966f56eSPatrick Delaunay return ret; 14051966f56eSPatrick Delaunay 14061966f56eSPatrick Delaunay ret = clk_enable_bulk(&priv->clks); 14071966f56eSPatrick Delaunay if (ret) { 14081966f56eSPatrick Delaunay clk_release_bulk(&priv->clks); 14091966f56eSPatrick Delaunay return ret; 14101966f56eSPatrick Delaunay } 14111966f56eSPatrick Delaunay 14121966f56eSPatrick Delaunay return 0; 14131966f56eSPatrick Delaunay } 14141966f56eSPatrick Delaunay 1415f58a41e0SSimon Glass static int dwc2_usb_probe(struct udevice *dev) 1416f58a41e0SSimon Glass { 1417f58a41e0SSimon Glass struct dwc2_priv *priv = dev_get_priv(dev); 1418e96e064fSMarek Vasut struct usb_bus_priv *bus_priv = dev_get_uclass_priv(dev); 14191e03ec26SPatrick Delaunay int ret; 1420e96e064fSMarek Vasut 1421e96e064fSMarek Vasut bus_priv->desc_before_addr = true; 1422f58a41e0SSimon Glass 1423ee0a9610SFrank Wang #ifdef CONFIG_ARCH_ROCKCHIP 1424ee0a9610SFrank Wang priv->hnp_srp_disable = true; 1425ee0a9610SFrank Wang #endif 1426ee0a9610SFrank Wang 14271966f56eSPatrick Delaunay ret = dwc2_clk_init(dev); 14281966f56eSPatrick Delaunay if (ret) 14291966f56eSPatrick Delaunay return ret; 14301966f56eSPatrick Delaunay 14311e03ec26SPatrick Delaunay ret = dwc2_setup_phy(dev); 14321e03ec26SPatrick Delaunay if (ret) 14331e03ec26SPatrick Delaunay return ret; 14341e03ec26SPatrick Delaunay 14355c735367SKever Yang return dwc2_init_common(dev, priv); 1436f58a41e0SSimon Glass } 1437f58a41e0SSimon Glass 1438f58a41e0SSimon Glass static int dwc2_usb_remove(struct udevice *dev) 1439f58a41e0SSimon Glass { 1440f58a41e0SSimon Glass struct dwc2_priv *priv = dev_get_priv(dev); 1441782be0c4SChristophe Kerello int ret; 1442782be0c4SChristophe Kerello 1443782be0c4SChristophe Kerello ret = dwc_vbus_supply_exit(dev); 1444782be0c4SChristophe Kerello if (ret) 1445782be0c4SChristophe Kerello return ret; 1446f58a41e0SSimon Glass 14471e03ec26SPatrick Delaunay ret = dwc2_shutdown_phy(dev); 14481e03ec26SPatrick Delaunay if (ret) { 14491e03ec26SPatrick Delaunay dev_dbg(dev, "Failed to shutdown USB PHY: %d.\n", ret); 14501e03ec26SPatrick Delaunay return ret; 14511e03ec26SPatrick Delaunay } 14521e03ec26SPatrick Delaunay 1453f58a41e0SSimon Glass dwc2_uninit_common(priv->regs); 1454f58a41e0SSimon Glass 1455a1bebf37SLey Foon Tan reset_release_bulk(&priv->resets); 14561966f56eSPatrick Delaunay clk_disable_bulk(&priv->clks); 14571966f56eSPatrick Delaunay clk_release_bulk(&priv->clks); 1458a1bebf37SLey Foon Tan 1459f58a41e0SSimon Glass return 0; 1460f58a41e0SSimon Glass } 1461f58a41e0SSimon Glass 1462f58a41e0SSimon Glass struct dm_usb_ops dwc2_usb_ops = { 1463f58a41e0SSimon Glass .control = dwc2_submit_control_msg, 1464f58a41e0SSimon Glass .bulk = dwc2_submit_bulk_msg, 1465f58a41e0SSimon Glass .interrupt = dwc2_submit_int_msg, 1466f58a41e0SSimon Glass }; 1467f58a41e0SSimon Glass 1468f58a41e0SSimon Glass static const struct udevice_id dwc2_usb_ids[] = { 1469f58a41e0SSimon Glass { .compatible = "brcm,bcm2835-usb" }, 1470b56d3e0fSEmmanuel Vadot { .compatible = "brcm,bcm2708-usb" }, 1471f522f947SMarek Vasut { .compatible = "snps,dwc2" }, 1472f58a41e0SSimon Glass { } 1473f58a41e0SSimon Glass }; 1474f58a41e0SSimon Glass 1475f58a41e0SSimon Glass U_BOOT_DRIVER(usb_dwc2) = { 14767a1386f9SMarek Vasut .name = "dwc2_usb", 1477f58a41e0SSimon Glass .id = UCLASS_USB, 1478f58a41e0SSimon Glass .of_match = dwc2_usb_ids, 1479f58a41e0SSimon Glass .ofdata_to_platdata = dwc2_usb_ofdata_to_platdata, 1480f58a41e0SSimon Glass .probe = dwc2_usb_probe, 1481f58a41e0SSimon Glass .remove = dwc2_usb_remove, 1482f58a41e0SSimon Glass .ops = &dwc2_usb_ops, 1483f58a41e0SSimon Glass .priv_auto_alloc_size = sizeof(struct dwc2_priv), 1484f58a41e0SSimon Glass .flags = DM_FLAG_ALLOC_PRIV_DMA, 1485f58a41e0SSimon Glass }; 1486f58a41e0SSimon Glass #endif 1487