X-Git-Url: https://git.librecmc.org/?a=blobdiff_plain;f=drivers%2Fusb%2Fhost%2Fdwc2.c;h=4862ab0e7db541b2c1b90d972dfea0fcadd3f38d;hb=103774b71cc0e510f890f639f9c3a65ad9d005c9;hp=9e95ecb42a5b98533bb34662c62e08ba5c39462d;hpb=fd2cd66290ff45614a52c82e38ff627ed134a44f;p=oweals%2Fu-boot.git diff --git a/drivers/usb/host/dwc2.c b/drivers/usb/host/dwc2.c index 9e95ecb42a..4862ab0e7d 100644 --- a/drivers/usb/host/dwc2.c +++ b/drivers/usb/host/dwc2.c @@ -15,14 +15,17 @@ #include #include #include +#include #include "dwc2.h" +DECLARE_GLOBAL_DATA_PTR; + /* Use only HC channel 0. */ #define DWC2_HC_CHANNEL 0 #define DWC2_STATUS_BUF_SIZE 64 -#define DWC2_DATA_BUF_SIZE (64 * 1024) +#define DWC2_DATA_BUF_SIZE (CONFIG_USB_DWC2_BUFFER_SIZE * 1024) #define MAX_DEVICE 16 #define MAX_ENDPOINT 16 @@ -31,13 +34,24 @@ struct dwc2_priv { #ifdef CONFIG_DM_USB uint8_t aligned_buffer[DWC2_DATA_BUF_SIZE] __aligned(ARCH_DMA_MINALIGN); uint8_t status_buffer[DWC2_STATUS_BUF_SIZE] __aligned(ARCH_DMA_MINALIGN); +#ifdef CONFIG_DM_REGULATOR + struct udevice *vbus_supply; +#endif #else uint8_t *aligned_buffer; uint8_t *status_buffer; #endif - int bulk_data_toggle[MAX_DEVICE][MAX_ENDPOINT]; + u8 in_data_toggle[MAX_DEVICE][MAX_ENDPOINT]; + u8 out_data_toggle[MAX_DEVICE][MAX_ENDPOINT]; struct dwc2_core_regs *regs; int root_hub_devnum; + bool ext_vbus; + /* + * The hnp/srp capability must be disabled if the platform + * does't support hnp/srp. Otherwise the force mode can't work. + */ + bool hnp_srp_disable; + bool oc_disable; }; #ifndef CONFIG_DM_USB @@ -97,10 +111,10 @@ static void dwc_otg_flush_tx_fifo(struct dwc2_core_regs *regs, const int num) writel(DWC2_GRSTCTL_TXFFLSH | (num << DWC2_GRSTCTL_TXFNUM_OFFSET), ®s->grstctl); - ret = wait_for_bit(__func__, ®s->grstctl, DWC2_GRSTCTL_TXFFLSH, - false, 1000, false); + ret = wait_for_bit_le32(®s->grstctl, DWC2_GRSTCTL_TXFFLSH, + false, 1000, false); if (ret) - printf("%s: Timeout!\n", __func__); + dev_info(dev, "%s: Timeout!\n", __func__); /* Wait for 3 PHY Clocks */ udelay(1); @@ -116,10 +130,10 @@ static void dwc_otg_flush_rx_fifo(struct dwc2_core_regs *regs) int ret; writel(DWC2_GRSTCTL_RXFFLSH, ®s->grstctl); - ret = wait_for_bit(__func__, ®s->grstctl, DWC2_GRSTCTL_RXFFLSH, - false, 1000, false); + ret = wait_for_bit_le32(®s->grstctl, DWC2_GRSTCTL_RXFFLSH, + false, 1000, false); if (ret) - printf("%s: Timeout!\n", __func__); + dev_info(dev, "%s: Timeout!\n", __func__); /* Wait for 3 PHY Clocks */ udelay(1); @@ -134,17 +148,17 @@ static void dwc_otg_core_reset(struct dwc2_core_regs *regs) int ret; /* Wait for AHB master IDLE state. */ - ret = wait_for_bit(__func__, ®s->grstctl, DWC2_GRSTCTL_AHBIDLE, - true, 1000, false); + ret = wait_for_bit_le32(®s->grstctl, DWC2_GRSTCTL_AHBIDLE, + true, 1000, false); if (ret) - printf("%s: Timeout!\n", __func__); + dev_info(dev, "%s: Timeout!\n", __func__); /* Core Soft Reset */ writel(DWC2_GRSTCTL_CSFTRST, ®s->grstctl); - ret = wait_for_bit(__func__, ®s->grstctl, DWC2_GRSTCTL_CSFTRST, - false, 1000, false); + ret = wait_for_bit_le32(®s->grstctl, DWC2_GRSTCTL_CSFTRST, + false, 1000, false); if (ret) - printf("%s: Timeout!\n", __func__); + dev_info(dev, "%s: Timeout!\n", __func__); /* * Wait for core to come out of reset. @@ -154,6 +168,57 @@ static void dwc_otg_core_reset(struct dwc2_core_regs *regs) mdelay(100); } +#if defined(CONFIG_DM_USB) && defined(CONFIG_DM_REGULATOR) +static int dwc_vbus_supply_init(struct udevice *dev) +{ + struct dwc2_priv *priv = dev_get_priv(dev); + int ret; + + ret = device_get_supply_regulator(dev, "vbus-supply", + &priv->vbus_supply); + if (ret) { + debug("%s: No vbus supply\n", dev->name); + return 0; + } + + ret = regulator_set_enable(priv->vbus_supply, true); + if (ret) { + dev_err(dev, "Error enabling vbus supply\n"); + return ret; + } + + return 0; +} + +static int dwc_vbus_supply_exit(struct udevice *dev) +{ + struct dwc2_priv *priv = dev_get_priv(dev); + int ret; + + if (priv->vbus_supply) { + ret = regulator_set_enable(priv->vbus_supply, false); + if (ret) { + dev_err(dev, "Error disabling vbus supply\n"); + return ret; + } + } + + return 0; +} +#else +static int dwc_vbus_supply_init(struct udevice *dev) +{ + return 0; +} + +#if defined(CONFIG_DM_USB) +static int dwc_vbus_supply_exit(struct udevice *dev) +{ + return 0; +} +#endif +#endif + /* * This function initializes the DWC_otg controller registers for * host mode. @@ -162,10 +227,12 @@ static void dwc_otg_core_reset(struct dwc2_core_regs *regs) * request queues. Host channels are reset to ensure that they are ready for * performing transfers. * + * @param dev USB Device (NULL if driver model is not being used) * @param regs Programming view of DWC_otg controller * */ -static void dwc_otg_core_host_init(struct dwc2_core_regs *regs) +static void dwc_otg_core_host_init(struct udevice *dev, + struct dwc2_core_regs *regs) { uint32_t nptxfifosize = 0; uint32_t ptxfifosize = 0; @@ -227,10 +294,10 @@ static void dwc_otg_core_host_init(struct dwc2_core_regs *regs) clrsetbits_le32(®s->hc_regs[i].hcchar, DWC2_HCCHAR_EPDIR, DWC2_HCCHAR_CHEN | DWC2_HCCHAR_CHDIS); - ret = wait_for_bit(__func__, ®s->hc_regs[i].hcchar, - DWC2_HCCHAR_CHEN, false, 1000, false); + ret = wait_for_bit_le32(®s->hc_regs[i].hcchar, + DWC2_HCCHAR_CHEN, false, 1000, false); if (ret) - printf("%s: Timeout!\n", __func__); + dev_info("%s: Timeout!\n", __func__); } /* Turn on the vbus power. */ @@ -243,6 +310,9 @@ static void dwc_otg_core_host_init(struct dwc2_core_regs *regs) writel(hprt0, ®s->hprt0); } } + + if (dev) + dwc_vbus_supply_init(dev); } /* @@ -251,8 +321,9 @@ static void dwc_otg_core_host_init(struct dwc2_core_regs *regs) * * @param regs Programming view of the DWC_otg controller */ -static void dwc_otg_core_init(struct dwc2_core_regs *regs) +static void dwc_otg_core_init(struct dwc2_priv *priv) { + struct dwc2_core_regs *regs = priv->regs; uint32_t ahbcfg = 0; uint32_t usbcfg = 0; uint8_t brst_sz = CONFIG_DWC2_DMA_BURST_SIZE; @@ -261,11 +332,15 @@ static void dwc_otg_core_init(struct dwc2_core_regs *regs) usbcfg = readl(®s->gusbcfg); /* Program the ULPI External VBUS bit if needed */ -#ifdef CONFIG_DWC2_PHY_ULPI_EXT_VBUS - usbcfg |= DWC2_GUSBCFG_ULPI_EXT_VBUS_DRV; -#else - usbcfg &= ~DWC2_GUSBCFG_ULPI_EXT_VBUS_DRV; -#endif + if (priv->ext_vbus) { + usbcfg |= DWC2_GUSBCFG_ULPI_EXT_VBUS_DRV; + if (!priv->oc_disable) { + usbcfg |= DWC2_GUSBCFG_ULPI_INT_VBUS_INDICATOR | + DWC2_GUSBCFG_INDICATOR_PASSTHROUGH; + } + } else { + usbcfg &= ~DWC2_GUSBCFG_ULPI_EXT_VBUS_DRV; + } /* Set external TS Dline pulsing */ #ifdef CONFIG_DWC2_TS_DLINE @@ -327,7 +402,7 @@ static void dwc_otg_core_init(struct dwc2_core_regs *regs) usbcfg &= ~DWC2_GUSBCFG_DDRSEL; #endif } else { /* UTMI+ interface */ -#if (CONFIG_DWC2_UTMI_PHY_WIDTH == 16) +#if (CONFIG_DWC2_UTMI_WIDTH == 16) usbcfg |= DWC2_GUSBCFG_PHYIF; #endif } @@ -351,6 +426,9 @@ static void dwc_otg_core_init(struct dwc2_core_regs *regs) usbcfg |= DWC2_GUSBCFG_ULPI_CLK_SUS_M; } #endif + if (priv->hnp_srp_disable) + usbcfg |= DWC2_GUSBCFG_FORCEHOSTMODE; + writel(usbcfg, ®s->gusbcfg); /* Program the GAHBCFG Register. */ @@ -379,12 +457,16 @@ static void dwc_otg_core_init(struct dwc2_core_regs *regs) writel(ahbcfg, ®s->gahbcfg); - /* Program the GUSBCFG register for HNP/SRP. */ - setbits_le32(®s->gusbcfg, DWC2_GUSBCFG_HNPCAP | DWC2_GUSBCFG_SRPCAP); + /* Program the capabilities in GUSBCFG Register */ + usbcfg = 0; + if (!priv->hnp_srp_disable) + usbcfg |= DWC2_GUSBCFG_HNPCAP | DWC2_GUSBCFG_SRPCAP; #ifdef CONFIG_DWC2_IC_USB_CAP - setbits_le32(®s->gusbcfg, DWC2_GUSBCFG_IC_USB_CAP); + usbcfg |= DWC2_GUSBCFG_IC_USB_CAP; #endif + + setbits_le32(®s->gusbcfg, usbcfg); } /* @@ -410,19 +492,29 @@ static void dwc_otg_hc_init(struct dwc2_core_regs *regs, uint8_t hc_num, if (dev->speed == USB_SPEED_LOW) hcchar |= DWC2_HCCHAR_LSPDDEV; - /* Clear old interrupt conditions for this host channel. */ - writel(0x3fff, &hc_regs->hcint); - /* * Program the HCCHARn register with the endpoint characteristics * for the current transfer. */ writel(hcchar, &hc_regs->hcchar); - /* Program the HCSPLIT register for SPLITs */ + /* Program the HCSPLIT register, default to no SPLIT */ writel(0, &hc_regs->hcsplt); } +static void dwc_otg_hc_init_split(struct dwc2_hc_regs *hc_regs, + uint8_t hub_devnum, uint8_t hub_port) +{ + uint32_t hcsplt = 0; + + hcsplt = DWC2_HCSPLT_SPLTENA; + hcsplt |= hub_devnum << DWC2_HCSPLT_HUBADDR_OFFSET; + hcsplt |= hub_port << DWC2_HCSPLT_PRTADDR_OFFSET; + + /* Program the HCSPLIT register for SPLITs */ + writel(hcsplt, &hc_regs->hcsplt); +} + /* * DWC2 to USB API interface */ @@ -713,39 +805,33 @@ static int dwc_otg_submit_rh_msg(struct dwc2_priv *priv, struct usb_device *dev, return stat; } -int wait_for_chhltd(struct dwc2_core_regs *regs, uint32_t *sub, int *toggle, - bool ignore_ack) +int wait_for_chhltd(struct dwc2_hc_regs *hc_regs, uint32_t *sub, u8 *toggle) { - uint32_t hcint_comp_hlt_ack = DWC2_HCINT_XFERCOMP | DWC2_HCINT_CHHLTD; - struct dwc2_hc_regs *hc_regs = ®s->hc_regs[DWC2_HC_CHANNEL]; int ret; uint32_t hcint, hctsiz; - ret = wait_for_bit(__func__, &hc_regs->hcint, DWC2_HCINT_CHHLTD, true, - 1000, false); + ret = wait_for_bit_le32(&hc_regs->hcint, DWC2_HCINT_CHHLTD, true, + 2000, false); if (ret) return ret; hcint = readl(&hc_regs->hcint); - if (hcint & (DWC2_HCINT_NAK | DWC2_HCINT_FRMOVRUN)) - return -EAGAIN; - if (ignore_ack) - hcint &= ~DWC2_HCINT_ACK; - else - hcint_comp_hlt_ack |= DWC2_HCINT_ACK; - if (hcint != hcint_comp_hlt_ack) { - debug("%s: Error (HCINT=%08x)\n", __func__, hcint); - return -EINVAL; - } - hctsiz = readl(&hc_regs->hctsiz); *sub = (hctsiz & DWC2_HCTSIZ_XFERSIZE_MASK) >> DWC2_HCTSIZ_XFERSIZE_OFFSET; *toggle = (hctsiz & DWC2_HCTSIZ_PID_MASK) >> DWC2_HCTSIZ_PID_OFFSET; - debug("%s: sub=%u toggle=%d\n", __func__, *sub, *toggle); + debug("%s: HCINT=%08x sub=%u toggle=%d\n", __func__, hcint, *sub, + *toggle); - return 0; + if (hcint & DWC2_HCINT_XFERCOMP) + return 0; + + if (hcint & (DWC2_HCINT_NAK | DWC2_HCINT_FRMOVRUN)) + return -EAGAIN; + + debug("%s: Error (HCINT=%08x)\n", __func__, hcint); + return -EINVAL; } static int dwc2_eptype[] = { @@ -755,96 +841,183 @@ static int dwc2_eptype[] = { DWC2_HCCHAR_EPTYPE_BULK, }; +static int transfer_chunk(struct dwc2_hc_regs *hc_regs, void *aligned_buffer, + u8 *pid, int in, void *buffer, int num_packets, + int xfer_len, int *actual_len, int odd_frame) +{ + int ret = 0; + uint32_t sub; + + debug("%s: chunk: pid %d xfer_len %u pkts %u\n", __func__, + *pid, xfer_len, num_packets); + + writel((xfer_len << DWC2_HCTSIZ_XFERSIZE_OFFSET) | + (num_packets << DWC2_HCTSIZ_PKTCNT_OFFSET) | + (*pid << DWC2_HCTSIZ_PID_OFFSET), + &hc_regs->hctsiz); + + if (xfer_len) { + if (in) { + invalidate_dcache_range( + (uintptr_t)aligned_buffer, + (uintptr_t)aligned_buffer + + roundup(xfer_len, ARCH_DMA_MINALIGN)); + } else { + memcpy(aligned_buffer, buffer, xfer_len); + flush_dcache_range( + (uintptr_t)aligned_buffer, + (uintptr_t)aligned_buffer + + roundup(xfer_len, ARCH_DMA_MINALIGN)); + } + } + + writel(phys_to_bus((unsigned long)aligned_buffer), &hc_regs->hcdma); + + /* Clear old interrupt conditions for this host channel. */ + writel(0x3fff, &hc_regs->hcint); + + /* Set host channel enable after all other setup is complete. */ + clrsetbits_le32(&hc_regs->hcchar, DWC2_HCCHAR_MULTICNT_MASK | + DWC2_HCCHAR_CHEN | DWC2_HCCHAR_CHDIS | + DWC2_HCCHAR_ODDFRM, + (1 << DWC2_HCCHAR_MULTICNT_OFFSET) | + (odd_frame << DWC2_HCCHAR_ODDFRM_OFFSET) | + DWC2_HCCHAR_CHEN); + + ret = wait_for_chhltd(hc_regs, &sub, pid); + if (ret < 0) + return ret; + + if (in) { + xfer_len -= sub; + + invalidate_dcache_range((unsigned long)aligned_buffer, + (unsigned long)aligned_buffer + + roundup(xfer_len, ARCH_DMA_MINALIGN)); + + memcpy(buffer, aligned_buffer, xfer_len); + } + *actual_len = xfer_len; + + return ret; +} + int chunk_msg(struct dwc2_priv *priv, struct usb_device *dev, - unsigned long pipe, int *pid, int in, void *buffer, int len, - bool ignore_ack) + unsigned long pipe, u8 *pid, int in, void *buffer, int len) { struct dwc2_core_regs *regs = priv->regs; struct dwc2_hc_regs *hc_regs = ®s->hc_regs[DWC2_HC_CHANNEL]; + struct dwc2_host_regs *host_regs = ®s->host_regs; int devnum = usb_pipedevice(pipe); int ep = usb_pipeendpoint(pipe); int max = usb_maxpacket(dev, pipe); int eptype = dwc2_eptype[usb_pipetype(pipe)]; int done = 0; int ret = 0; - uint32_t sub; + int do_split = 0; + int complete_split = 0; uint32_t xfer_len; uint32_t num_packets; int stop_transfer = 0; + uint32_t max_xfer_len; + int ssplit_frame_num = 0; debug("%s: msg: pipe %lx pid %d in %d len %d\n", __func__, pipe, *pid, in, len); - do { - /* Initialize channel */ - dwc_otg_hc_init(regs, DWC2_HC_CHANNEL, dev, devnum, ep, in, - eptype, max); + max_xfer_len = CONFIG_DWC2_MAX_PACKET_COUNT * max; + if (max_xfer_len > CONFIG_DWC2_MAX_TRANSFER_SIZE) + max_xfer_len = CONFIG_DWC2_MAX_TRANSFER_SIZE; + if (max_xfer_len > DWC2_DATA_BUF_SIZE) + max_xfer_len = DWC2_DATA_BUF_SIZE; + + /* Make sure that max_xfer_len is a multiple of max packet size. */ + num_packets = max_xfer_len / max; + max_xfer_len = num_packets * max; + + /* Initialize channel */ + dwc_otg_hc_init(regs, DWC2_HC_CHANNEL, dev, devnum, ep, in, + eptype, max); + + /* Check if the target is a FS/LS device behind a HS hub */ + if (dev->speed != USB_SPEED_HIGH) { + uint8_t hub_addr; + uint8_t hub_port; + uint32_t hprt0 = readl(®s->hprt0); + if ((hprt0 & DWC2_HPRT0_PRTSPD_MASK) == + DWC2_HPRT0_PRTSPD_HIGH) { + usb_find_usb2_hub_address_port(dev, &hub_addr, + &hub_port); + dwc_otg_hc_init_split(hc_regs, hub_addr, hub_port); + + do_split = 1; + num_packets = 1; + max_xfer_len = max; + } + } + do { + int actual_len = 0; + uint32_t hcint; + int odd_frame = 0; xfer_len = len - done; - if (xfer_len > CONFIG_DWC2_MAX_TRANSFER_SIZE) - xfer_len = CONFIG_DWC2_MAX_TRANSFER_SIZE - max + 1; - if (xfer_len > DWC2_DATA_BUF_SIZE) - xfer_len = DWC2_DATA_BUF_SIZE - max + 1; - /* Make sure that xfer_len is a multiple of max packet size. */ - if (xfer_len > 0) { + if (xfer_len > max_xfer_len) + xfer_len = max_xfer_len; + else if (xfer_len > max) num_packets = (xfer_len + max - 1) / max; - if (num_packets > CONFIG_DWC2_MAX_PACKET_COUNT) { - num_packets = CONFIG_DWC2_MAX_PACKET_COUNT; - xfer_len = num_packets * max; - } - } else { + else num_packets = 1; - } - - if (in) - xfer_len = num_packets * max; - - debug("%s: chunk: pid %d xfer_len %u pkts %u\n", __func__, - *pid, xfer_len, num_packets); - writel((xfer_len << DWC2_HCTSIZ_XFERSIZE_OFFSET) | - (num_packets << DWC2_HCTSIZ_PKTCNT_OFFSET) | - (*pid << DWC2_HCTSIZ_PID_OFFSET), - &hc_regs->hctsiz); + if (complete_split) + setbits_le32(&hc_regs->hcsplt, DWC2_HCSPLT_COMPSPLT); + else if (do_split) + clrbits_le32(&hc_regs->hcsplt, DWC2_HCSPLT_COMPSPLT); - if (!in && xfer_len) { - memcpy(priv->aligned_buffer, (char *)buffer + done, - xfer_len); - - flush_dcache_range((unsigned long)priv->aligned_buffer, - (unsigned long)((void *)priv->aligned_buffer + - roundup(xfer_len, ARCH_DMA_MINALIGN))); + if (eptype == DWC2_HCCHAR_EPTYPE_INTR) { + int uframe_num = readl(&host_regs->hfnum); + if (!(uframe_num & 0x1)) + odd_frame = 1; } - writel(phys_to_bus((unsigned long)priv->aligned_buffer), - &hc_regs->hcdma); - - /* Set host channel enable after all other setup is complete. */ - clrsetbits_le32(&hc_regs->hcchar, DWC2_HCCHAR_MULTICNT_MASK | - DWC2_HCCHAR_CHEN | DWC2_HCCHAR_CHDIS, - (1 << DWC2_HCCHAR_MULTICNT_OFFSET) | - DWC2_HCCHAR_CHEN); + ret = transfer_chunk(hc_regs, priv->aligned_buffer, pid, + in, (char *)buffer + done, num_packets, + xfer_len, &actual_len, odd_frame); + + hcint = readl(&hc_regs->hcint); + if (complete_split) { + stop_transfer = 0; + if (hcint & DWC2_HCINT_NYET) { + ret = 0; + int frame_num = DWC2_HFNUM_MAX_FRNUM & + readl(&host_regs->hfnum); + if (((frame_num - ssplit_frame_num) & + DWC2_HFNUM_MAX_FRNUM) > 4) + ret = -EAGAIN; + } else + complete_split = 0; + } else if (do_split) { + if (hcint & DWC2_HCINT_ACK) { + ssplit_frame_num = DWC2_HFNUM_MAX_FRNUM & + readl(&host_regs->hfnum); + ret = 0; + complete_split = 1; + } + } - ret = wait_for_chhltd(regs, &sub, pid, ignore_ack); if (ret) break; - if (in) { - xfer_len -= sub; + if (actual_len < xfer_len) + stop_transfer = 1; - invalidate_dcache_range((unsigned long)priv->aligned_buffer, - (unsigned long)((void *)priv->aligned_buffer + - roundup(xfer_len, ARCH_DMA_MINALIGN))); + done += actual_len; - memcpy(buffer + done, priv->aligned_buffer, xfer_len); - if (sub) - stop_transfer = 1; - } - - done += xfer_len; - - } while ((done < len) && !stop_transfer); + /* Transactions are done when when either all data is transferred or + * there is a short transfer. In case of a SPLIT make sure the CSPLIT + * is executed. + */ + } while (((done < len) && !stop_transfer) || complete_split); writel(0, &hc_regs->hcintmsk); writel(0xFFFFFFFF, &hc_regs->hcint); @@ -861,14 +1034,19 @@ int _submit_bulk_msg(struct dwc2_priv *priv, struct usb_device *dev, { int devnum = usb_pipedevice(pipe); int ep = usb_pipeendpoint(pipe); + u8* pid; - if (devnum == priv->root_hub_devnum) { + if ((devnum >= MAX_DEVICE) || (devnum == priv->root_hub_devnum)) { dev->status = 0; return -EINVAL; } - return chunk_msg(priv, dev, pipe, &priv->bulk_data_toggle[devnum][ep], - usb_pipein(pipe), buffer, len, true); + if (usb_pipein(pipe)) + pid = &priv->in_data_toggle[devnum][ep]; + else + pid = &priv->out_data_toggle[devnum][ep]; + + return chunk_msg(priv, dev, pipe, pid, usb_pipein(pipe), buffer, len); } static int _submit_control_msg(struct dwc2_priv *priv, struct usb_device *dev, @@ -876,7 +1054,8 @@ static int _submit_control_msg(struct dwc2_priv *priv, struct usb_device *dev, struct devrequest *setup) { int devnum = usb_pipedevice(pipe); - int pid, ret, act_len; + int ret, act_len; + u8 pid; /* For CONTROL endpoint pid should start with DATA1 */ int status_direction; @@ -887,31 +1066,39 @@ static int _submit_control_msg(struct dwc2_priv *priv, struct usb_device *dev, setup); } + /* SETUP stage */ pid = DWC2_HC_PID_SETUP; - ret = chunk_msg(priv, dev, pipe, &pid, 0, setup, 8, true); + do { + ret = chunk_msg(priv, dev, pipe, &pid, 0, setup, 8); + } while (ret == -EAGAIN); if (ret) return ret; + /* DATA stage */ + act_len = 0; if (buffer) { pid = DWC2_HC_PID_DATA1; - ret = chunk_msg(priv, dev, pipe, &pid, usb_pipein(pipe), buffer, - len, false); + do { + ret = chunk_msg(priv, dev, pipe, &pid, usb_pipein(pipe), + buffer, len); + act_len += dev->act_len; + buffer += dev->act_len; + len -= dev->act_len; + } while (ret == -EAGAIN); if (ret) return ret; - act_len = dev->act_len; - } /* End of DATA stage */ - else - act_len = 0; - - /* STATUS stage */ - if ((len == 0) || usb_pipeout(pipe)) + status_direction = usb_pipeout(pipe); + } else { + /* No-data CONTROL always ends with an IN transaction */ status_direction = 1; - else - status_direction = 0; + } + /* STATUS stage */ pid = DWC2_HC_PID_DATA1; - ret = chunk_msg(priv, dev, pipe, &pid, status_direction, - priv->status_buffer, 0, false); + do { + ret = chunk_msg(priv, dev, pipe, &pid, status_direction, + priv->status_buffer, 0); + } while (ret == -EAGAIN); if (ret) return ret; @@ -931,7 +1118,7 @@ int _submit_int_msg(struct dwc2_priv *priv, struct usb_device *dev, timeout = get_timer(0) + USB_TIMEOUT_MS(pipe); for (;;) { if (get_timer(0) > timeout) { - printf("Timeout poll on interrupt endpoint\n"); + dev_err(dev, "Timeout poll on interrupt endpoint\n"); return -ETIMEDOUT; } ret = _submit_bulk_msg(priv, dev, pipe, buffer, len); @@ -940,23 +1127,31 @@ int _submit_int_msg(struct dwc2_priv *priv, struct usb_device *dev, } } -static int dwc2_init_common(struct dwc2_priv *priv) +static int dwc2_init_common(struct udevice *dev, struct dwc2_priv *priv) { struct dwc2_core_regs *regs = priv->regs; uint32_t snpsid; int i, j; snpsid = readl(®s->gsnpsid); - printf("Core Release: %x.%03x\n", snpsid >> 12 & 0xf, snpsid & 0xfff); + dev_info(dev, "Core Release: %x.%03x\n", + snpsid >> 12 & 0xf, snpsid & 0xfff); if ((snpsid & DWC2_SNPSID_DEVID_MASK) != DWC2_SNPSID_DEVID_VER_2xx && (snpsid & DWC2_SNPSID_DEVID_MASK) != DWC2_SNPSID_DEVID_VER_3xx) { - printf("SNPSID invalid (not DWC2 OTG device): %08x\n", snpsid); + dev_info(dev, "SNPSID invalid (not DWC2 OTG device): %08x\n", + snpsid); return -ENODEV; } - dwc_otg_core_init(regs); - dwc_otg_core_host_init(regs); +#ifdef CONFIG_DWC2_PHY_ULPI_EXT_VBUS + priv->ext_vbus = 1; +#else + priv->ext_vbus = 0; +#endif + + dwc_otg_core_init(priv); + dwc_otg_core_host_init(dev, regs); clrsetbits_le32(®s->hprt0, DWC2_HPRT0_PRTENA | DWC2_HPRT0_PRTCONNDET | DWC2_HPRT0_PRTENCHNG | @@ -968,10 +1163,21 @@ static int dwc2_init_common(struct dwc2_priv *priv) DWC2_HPRT0_PRTRST); for (i = 0; i < MAX_DEVICE; i++) { - for (j = 0; j < MAX_ENDPOINT; j++) - priv->bulk_data_toggle[i][j] = DWC2_HC_PID_DATA0; + for (j = 0; j < MAX_ENDPOINT; j++) { + priv->in_data_toggle[i][j] = DWC2_HC_PID_DATA0; + priv->out_data_toggle[i][j] = DWC2_HC_PID_DATA0; + } } + /* + * Add a 1 second delay here. This gives the host controller + * a bit time before the comminucation with the USB devices + * is started (the bus is scanned) and fixes the USB detection + * problems with some problematic USB keys. + */ + if (readl(®s->gintsts) & DWC2_GINTSTS_CURMODE_HOST) + mdelay(1000); + return 0; } @@ -1018,7 +1224,7 @@ int usb_lowlevel_init(int index, enum usb_init_type init, void **controller) if (board_usb_init(index, USB_INIT_HOST)) return -1; - return dwc2_init_common(priv); + return dwc2_init_common(NULL, priv); } int usb_lowlevel_stop(int index) @@ -1068,24 +1274,35 @@ static int dwc2_usb_ofdata_to_platdata(struct udevice *dev) struct dwc2_priv *priv = dev_get_priv(dev); fdt_addr_t addr; - addr = dev_get_addr(dev); + addr = dev_read_addr(dev); if (addr == FDT_ADDR_T_NONE) return -EINVAL; priv->regs = (struct dwc2_core_regs *)addr; + priv->oc_disable = dev_read_bool(dev, "disable-over-current"); + priv->hnp_srp_disable = dev_read_bool(dev, "hnp-srp-disable"); + return 0; } static int dwc2_usb_probe(struct udevice *dev) { struct dwc2_priv *priv = dev_get_priv(dev); + struct usb_bus_priv *bus_priv = dev_get_uclass_priv(dev); + + bus_priv->desc_before_addr = true; - return dwc2_init_common(priv); + return dwc2_init_common(dev, priv); } static int dwc2_usb_remove(struct udevice *dev) { struct dwc2_priv *priv = dev_get_priv(dev); + int ret; + + ret = dwc_vbus_supply_exit(dev); + if (ret) + return ret; dwc2_uninit_common(priv->regs);