diff options
Diffstat (limited to 'drivers/usb/dwc2')
-rw-r--r-- | drivers/usb/dwc2/core.c | 31 | ||||
-rw-r--r-- | drivers/usb/dwc2/core.h | 8 | ||||
-rw-r--r-- | drivers/usb/dwc2/gadget.c | 190 | ||||
-rw-r--r-- | drivers/usb/dwc2/hcd.c | 42 | ||||
-rw-r--r-- | drivers/usb/dwc2/hcd.h | 4 | ||||
-rw-r--r-- | drivers/usb/dwc2/hcd_intr.c | 30 | ||||
-rw-r--r-- | drivers/usb/dwc2/hcd_queue.c | 16 | ||||
-rw-r--r-- | drivers/usb/dwc2/platform.c | 33 |
8 files changed, 224 insertions, 130 deletions
diff --git a/drivers/usb/dwc2/core.c b/drivers/usb/dwc2/core.c index 27d2c9b8a034..d9269459d481 100644 --- a/drivers/usb/dwc2/core.c +++ b/drivers/usb/dwc2/core.c @@ -118,6 +118,7 @@ static int dwc2_core_reset(struct dwc2_hsotg *hsotg) { u32 greset; int count = 0; + u32 gusbcfg; dev_vdbg(hsotg->dev, "%s()\n", __func__); @@ -148,6 +149,23 @@ static int dwc2_core_reset(struct dwc2_hsotg *hsotg) } } while (greset & GRSTCTL_CSFTRST); + if (hsotg->dr_mode == USB_DR_MODE_HOST) { + gusbcfg = readl(hsotg->regs + GUSBCFG); + gusbcfg &= ~GUSBCFG_FORCEDEVMODE; + gusbcfg |= GUSBCFG_FORCEHOSTMODE; + writel(gusbcfg, hsotg->regs + GUSBCFG); + } else if (hsotg->dr_mode == USB_DR_MODE_PERIPHERAL) { + gusbcfg = readl(hsotg->regs + GUSBCFG); + gusbcfg &= ~GUSBCFG_FORCEHOSTMODE; + gusbcfg |= GUSBCFG_FORCEDEVMODE; + writel(gusbcfg, hsotg->regs + GUSBCFG); + } else if (hsotg->dr_mode == USB_DR_MODE_OTG) { + gusbcfg = readl(hsotg->regs + GUSBCFG); + gusbcfg &= ~GUSBCFG_FORCEHOSTMODE; + gusbcfg &= ~GUSBCFG_FORCEDEVMODE; + writel(gusbcfg, hsotg->regs + GUSBCFG); + } + /* * NOTE: This long sleep is _very_ important, otherwise the core will * not stay in host mode after a connector ID change! @@ -2674,23 +2692,23 @@ int dwc2_get_hwparams(struct dwc2_hsotg *hsotg) hwcfg2 = readl(hsotg->regs + GHWCFG2); hwcfg3 = readl(hsotg->regs + GHWCFG3); hwcfg4 = readl(hsotg->regs + GHWCFG4); - gnptxfsiz = readl(hsotg->regs + GNPTXFSIZ); grxfsiz = readl(hsotg->regs + GRXFSIZ); dev_dbg(hsotg->dev, "hwcfg1=%08x\n", hwcfg1); dev_dbg(hsotg->dev, "hwcfg2=%08x\n", hwcfg2); dev_dbg(hsotg->dev, "hwcfg3=%08x\n", hwcfg3); dev_dbg(hsotg->dev, "hwcfg4=%08x\n", hwcfg4); - dev_dbg(hsotg->dev, "gnptxfsiz=%08x\n", gnptxfsiz); dev_dbg(hsotg->dev, "grxfsiz=%08x\n", grxfsiz); - /* Force host mode to get HPTXFSIZ exact power on value */ + /* Force host mode to get HPTXFSIZ / GNPTXFSIZ exact power on value */ gusbcfg = readl(hsotg->regs + GUSBCFG); gusbcfg |= GUSBCFG_FORCEHOSTMODE; writel(gusbcfg, hsotg->regs + GUSBCFG); usleep_range(100000, 150000); + gnptxfsiz = readl(hsotg->regs + GNPTXFSIZ); hptxfsiz = readl(hsotg->regs + HPTXFSIZ); + dev_dbg(hsotg->dev, "gnptxfsiz=%08x\n", gnptxfsiz); dev_dbg(hsotg->dev, "hptxfsiz=%08x\n", hptxfsiz); gusbcfg = readl(hsotg->regs + GUSBCFG); gusbcfg &= ~GUSBCFG_FORCEHOSTMODE; @@ -2725,6 +2743,13 @@ int dwc2_get_hwparams(struct dwc2_hsotg *hsotg) width = (hwcfg3 & GHWCFG3_XFER_SIZE_CNTR_WIDTH_MASK) >> GHWCFG3_XFER_SIZE_CNTR_WIDTH_SHIFT; hw->max_transfer_size = (1 << (width + 11)) - 1; + /* + * Clip max_transfer_size to 65535. dwc2_hc_setup_align_buf() allocates + * coherent buffers with this size, and if it's too large we can + * exhaust the coherent DMA pool. + */ + if (hw->max_transfer_size > 65535) + hw->max_transfer_size = 65535; width = (hwcfg3 & GHWCFG3_PACKET_SIZE_CNTR_WIDTH_MASK) >> GHWCFG3_PACKET_SIZE_CNTR_WIDTH_SHIFT; hw->max_packet_count = (1 << (width + 4)) - 1; diff --git a/drivers/usb/dwc2/core.h b/drivers/usb/dwc2/core.h index 1efd10cc9629..bf015ab3b44c 100644 --- a/drivers/usb/dwc2/core.h +++ b/drivers/usb/dwc2/core.h @@ -139,6 +139,7 @@ struct s3c_hsotg_ep { unsigned int last_load; unsigned int fifo_load; unsigned short fifo_size; + unsigned short fifo_index; unsigned char dir_in; unsigned char index; @@ -194,8 +195,10 @@ struct s3c_hsotg { struct regulator_bulk_data supplies[ARRAY_SIZE(s3c_hsotg_supply_names)]; u32 phyif; + int fifo_mem; unsigned int dedicated_fifos:1; unsigned char num_of_eps; + u32 fifo_map; struct dentry *debug_root; struct dentry *debug_file; @@ -501,6 +504,10 @@ struct dwc2_hw_params { * a_peripheral and b_device=>b_host) this may not match * the core, but allows the software to determine * transitions + * @dr_mode: Requested mode of operation, one of following: + * - USB_DR_MODE_PERIPHERAL + * - USB_DR_MODE_HOST + * - USB_DR_MODE_OTG * @queuing_high_bandwidth: True if multiple packets of a high-bandwidth * transfer are in process of being queued * @srp_success: Stores status of SRP request in the case of a FS PHY @@ -592,6 +599,7 @@ struct dwc2_hsotg { /** Params to actually use */ struct dwc2_core_params *core_params; enum usb_otg_state op_state; + enum usb_dr_mode dr_mode; unsigned int queuing_high_bandwidth:1; unsigned int srp_success:1; diff --git a/drivers/usb/dwc2/gadget.c b/drivers/usb/dwc2/gadget.c index 7c9618e916e2..7b5856fadd93 100644 --- a/drivers/usb/dwc2/gadget.c +++ b/drivers/usb/dwc2/gadget.c @@ -182,16 +182,33 @@ static void s3c_hsotg_init_fifo(struct s3c_hsotg *hsotg) /* start at the end of the GNPTXFSIZ, rounded up */ addr = 2048 + 1024; - size = 768; /* - * currently we allocate TX FIFOs for all possible endpoints, - * and assume that they are all the same size. + * Because we have not enough memory to have each TX FIFO of size at + * least 3072 bytes (the maximum single packet size), we create four + * FIFOs of lenght 1024, and four of length 3072 bytes, and assing + * them to endpoints dynamically according to maxpacket size value of + * given endpoint. */ - for (ep = 1; ep <= 15; ep++) { + /* 256*4=1024 bytes FIFO length */ + size = 256; + for (ep = 1; ep <= 4; ep++) { + val = addr; + val |= size << FIFOSIZE_DEPTH_SHIFT; + WARN_ONCE(addr + size > hsotg->fifo_mem, + "insufficient fifo memory"); + addr += size; + + writel(val, hsotg->regs + DPTXFSIZN(ep)); + } + /* 768*4=3072 bytes FIFO length */ + size = 768; + for (ep = 5; ep <= 8; ep++) { val = addr; val |= size << FIFOSIZE_DEPTH_SHIFT; + WARN_ONCE(addr + size > hsotg->fifo_mem, + "insufficient fifo memory"); addr += size; writel(val, hsotg->regs + DPTXFSIZN(ep)); @@ -987,8 +1004,8 @@ static int s3c_hsotg_process_req_feature(struct s3c_hsotg *hsotg, hs_req = ep->req; ep->req = NULL; list_del_init(&hs_req->queue); - hs_req->req.complete(&ep->ep, - &hs_req->req); + usb_gadget_giveback_request(&ep->ep, + &hs_req->req); } /* If we have pending request, then start it */ @@ -1245,7 +1262,7 @@ static void s3c_hsotg_complete_request(struct s3c_hsotg *hsotg, if (hs_req->req.complete) { spin_unlock(&hsotg->lock); - hs_req->req.complete(&hs_ep->ep, &hs_req->req); + usb_gadget_giveback_request(&hs_ep->ep, &hs_req->req); spin_lock(&hsotg->lock); } @@ -1649,6 +1666,7 @@ static void s3c_hsotg_txfifo_flush(struct s3c_hsotg *hsotg, unsigned int idx) dev_err(hsotg->dev, "%s: timeout flushing fifo (GRSTCTL=%08x)\n", __func__, val); + break; } udelay(1); @@ -1831,7 +1849,7 @@ static void s3c_hsotg_epint(struct s3c_hsotg *hsotg, unsigned int idx, if (dir_in) { int epctl = readl(hsotg->regs + epctl_reg); - s3c_hsotg_txfifo_flush(hsotg, idx); + s3c_hsotg_txfifo_flush(hsotg, hs_ep->fifo_index); if ((epctl & DXEPCTL_STALL) && (epctl & DXEPCTL_EPTYPE_BULK)) { @@ -1980,6 +1998,7 @@ static void kill_all_requests(struct s3c_hsotg *hsotg, int result, bool force) { struct s3c_hsotg_req *req, *treq; + unsigned size; list_for_each_entry_safe(req, treq, &ep->queue, queue) { /* @@ -1993,9 +2012,11 @@ static void kill_all_requests(struct s3c_hsotg *hsotg, s3c_hsotg_complete_request(hsotg, ep, req, result); } - if (hsotg->dedicated_fifos) - if ((readl(hsotg->regs + DTXFSTS(ep->index)) & 0xffff) * 4 < 3072) - s3c_hsotg_txfifo_flush(hsotg, ep->index); + if (!hsotg->dedicated_fifos) + return; + size = (readl(hsotg->regs + DTXFSTS(ep->index)) & 0xffff) * 4; + if (size < ep->fifo_size) + s3c_hsotg_txfifo_flush(hsotg, ep->fifo_index); } /** @@ -2436,6 +2457,7 @@ static int s3c_hsotg_ep_enable(struct usb_ep *ep, u32 epctrl; u32 mps; int dir_in; + int i, val, size; int ret = 0; dev_dbg(hsotg->dev, @@ -2508,17 +2530,8 @@ static int s3c_hsotg_ep_enable(struct usb_ep *ep, break; case USB_ENDPOINT_XFER_INT: - if (dir_in) { - /* - * Allocate our TxFNum by simply using the index - * of the endpoint for the moment. We could do - * something better if the host indicates how - * many FIFOs we are expecting to use. - */ - + if (dir_in) hs_ep->periodic = 1; - epctrl |= DXEPCTL_TXFNUM(index); - } epctrl |= DXEPCTL_EPTYPE_INTERRUPT; break; @@ -2532,8 +2545,25 @@ static int s3c_hsotg_ep_enable(struct usb_ep *ep, * if the hardware has dedicated fifos, we must give each IN EP * a unique tx-fifo even if it is non-periodic. */ - if (dir_in && hsotg->dedicated_fifos) - epctrl |= DXEPCTL_TXFNUM(index); + if (dir_in && hsotg->dedicated_fifos) { + size = hs_ep->ep.maxpacket*hs_ep->mc; + for (i = 1; i <= 8; ++i) { + if (hsotg->fifo_map & (1<<i)) + continue; + val = readl(hsotg->regs + DPTXFSIZN(i)); + val = (val >> FIFOSIZE_DEPTH_SHIFT)*4; + if (val < size) + continue; + hsotg->fifo_map |= 1<<i; + + epctrl |= DXEPCTL_TXFNUM(i); + hs_ep->fifo_index = i; + hs_ep->fifo_size = val; + break; + } + if (i == 8) + return -ENOMEM; + } /* for non control endpoints, set PID to D0 */ if (index) @@ -2567,7 +2597,7 @@ static int s3c_hsotg_ep_disable(struct usb_ep *ep) u32 epctrl_reg; u32 ctrl; - dev_info(hsotg->dev, "%s(ep %p)\n", __func__, ep); + dev_dbg(hsotg->dev, "%s(ep %p)\n", __func__, ep); if (ep == &hsotg->eps[0].ep) { dev_err(hsotg->dev, "%s: called for ep0\n", __func__); @@ -2580,6 +2610,9 @@ static int s3c_hsotg_ep_disable(struct usb_ep *ep) /* terminate all requests with shutdown */ kill_all_requests(hsotg, hs_ep, -ESHUTDOWN, false); + hsotg->fifo_map &= ~(1<<hs_ep->fifo_index); + hs_ep->fifo_index = 0; + hs_ep->fifo_size = 0; ctrl = readl(hsotg->regs + epctrl_reg); ctrl &= ~DXEPCTL_EPENA; @@ -2625,7 +2658,7 @@ static int s3c_hsotg_ep_dequeue(struct usb_ep *ep, struct usb_request *req) struct s3c_hsotg *hs = hs_ep->parent; unsigned long flags; - dev_info(hs->dev, "ep_dequeue(%p,%p)\n", ep, req); + dev_dbg(hs->dev, "ep_dequeue(%p,%p)\n", ep, req); spin_lock_irqsave(&hs->lock, flags); @@ -2747,13 +2780,14 @@ static void s3c_hsotg_phy_enable(struct s3c_hsotg *hsotg) dev_dbg(hsotg->dev, "pdev 0x%p\n", pdev); - if (hsotg->phy) { - phy_init(hsotg->phy); - phy_power_on(hsotg->phy); - } else if (hsotg->uphy) + if (hsotg->uphy) usb_phy_init(hsotg->uphy); - else if (hsotg->plat->phy_init) + else if (hsotg->plat && hsotg->plat->phy_init) hsotg->plat->phy_init(pdev, hsotg->plat->phy_type); + else { + phy_init(hsotg->phy); + phy_power_on(hsotg->phy); + } } /** @@ -2767,13 +2801,14 @@ static void s3c_hsotg_phy_disable(struct s3c_hsotg *hsotg) { struct platform_device *pdev = to_platform_device(hsotg->dev); - if (hsotg->phy) { - phy_power_off(hsotg->phy); - phy_exit(hsotg->phy); - } else if (hsotg->uphy) + if (hsotg->uphy) usb_phy_shutdown(hsotg->uphy); - else if (hsotg->plat->phy_exit) + else if (hsotg->plat && hsotg->plat->phy_exit) hsotg->plat->phy_exit(pdev, hsotg->plat->phy_type); + else { + phy_power_off(hsotg->phy); + phy_exit(hsotg->phy); + } } /** @@ -2858,6 +2893,8 @@ static int s3c_hsotg_udc_start(struct usb_gadget *gadget, hsotg->gadget.dev.of_node = hsotg->dev->of_node; hsotg->gadget.speed = USB_SPEED_UNKNOWN; + clk_enable(hsotg->clk); + ret = regulator_bulk_enable(ARRAY_SIZE(hsotg->supplies), hsotg->supplies); if (ret) { @@ -2892,13 +2929,11 @@ static int s3c_hsotg_udc_stop(struct usb_gadget *gadget, return -ENODEV; /* all endpoints should be shutdown */ - for (ep = 0; ep < hsotg->num_of_eps; ep++) + for (ep = 1; ep < hsotg->num_of_eps; ep++) s3c_hsotg_ep_disable(&hsotg->eps[ep].ep); spin_lock_irqsave(&hsotg->lock, flags); - s3c_hsotg_phy_disable(hsotg); - if (!driver) hsotg->driver = NULL; @@ -2908,6 +2943,8 @@ static int s3c_hsotg_udc_stop(struct usb_gadget *gadget, regulator_bulk_disable(ARRAY_SIZE(hsotg->supplies), hsotg->supplies); + clk_disable(hsotg->clk); + return 0; } @@ -2934,14 +2971,15 @@ static int s3c_hsotg_pullup(struct usb_gadget *gadget, int is_on) struct s3c_hsotg *hsotg = to_hsotg(gadget); unsigned long flags = 0; - dev_dbg(hsotg->dev, "%s: is_in: %d\n", __func__, is_on); + dev_dbg(hsotg->dev, "%s: is_on: %d\n", __func__, is_on); spin_lock_irqsave(&hsotg->lock, flags); if (is_on) { s3c_hsotg_phy_enable(hsotg); + clk_enable(hsotg->clk); s3c_hsotg_core_init(hsotg); } else { - s3c_hsotg_disconnect(hsotg); + clk_disable(hsotg->clk); s3c_hsotg_phy_disable(hsotg); } @@ -2972,7 +3010,6 @@ static void s3c_hsotg_initep(struct s3c_hsotg *hsotg, struct s3c_hsotg_ep *hs_ep, int epnum) { - u32 ptxfifo; char *dir; if (epnum == 0) @@ -3001,15 +3038,6 @@ static void s3c_hsotg_initep(struct s3c_hsotg *hsotg, hs_ep->ep.ops = &s3c_hsotg_ep_ops; /* - * Read the FIFO size for the Periodic TX FIFO, even if we're - * an OUT endpoint, we may as well do this if in future the - * code is changed to make each endpoint's direction changeable. - */ - - ptxfifo = readl(hsotg->regs + DPTXFSIZN(epnum)); - hs_ep->fifo_size = FIFOSIZE_DEPTH_GET(ptxfifo) * 4; - - /* * if we're using dma, we need to set the next-endpoint pointer * to be something valid. */ @@ -3029,19 +3057,22 @@ static void s3c_hsotg_initep(struct s3c_hsotg *hsotg, */ static void s3c_hsotg_hw_cfg(struct s3c_hsotg *hsotg) { - u32 cfg2, cfg4; + u32 cfg2, cfg3, cfg4; /* check hardware configuration */ cfg2 = readl(hsotg->regs + 0x48); hsotg->num_of_eps = (cfg2 >> 10) & 0xF; - dev_info(hsotg->dev, "EPs:%d\n", hsotg->num_of_eps); + cfg3 = readl(hsotg->regs + 0x4C); + hsotg->fifo_mem = (cfg3 >> 16); cfg4 = readl(hsotg->regs + 0x50); hsotg->dedicated_fifos = (cfg4 >> 25) & 1; - dev_info(hsotg->dev, "%s fifos\n", - hsotg->dedicated_fifos ? "dedicated" : "shared"); + dev_info(hsotg->dev, "EPs: %d, %s fifos, %d entries in SPRAM\n", + hsotg->num_of_eps, + hsotg->dedicated_fifos ? "dedicated" : "shared", + hsotg->fifo_mem); } /** @@ -3392,6 +3423,9 @@ static int s3c_hsotg_probe(struct platform_device *pdev) if (!hsotg) return -ENOMEM; + /* Set default UTMI width */ + hsotg->phyif = GUSBCFG_PHYIF16; + /* * Attempt to find a generic PHY, then look for an old style * USB PHY, finally fall back to pdata @@ -3410,8 +3444,15 @@ static int s3c_hsotg_probe(struct platform_device *pdev) hsotg->plat = plat; } else hsotg->uphy = uphy; - } else + } else { hsotg->phy = phy; + /* + * If using the generic PHY framework, check if the PHY bus + * width is 8-bit and set the phyif appropriately. + */ + if (phy_get_bus_width(phy) == 8) + hsotg->phyif = GUSBCFG_PHYIF8; + } hsotg->dev = dev; @@ -3441,13 +3482,6 @@ static int s3c_hsotg_probe(struct platform_device *pdev) hsotg->irq = ret; - ret = devm_request_irq(&pdev->dev, hsotg->irq, s3c_hsotg_irq, 0, - dev_name(dev), hsotg); - if (ret < 0) { - dev_err(dev, "cannot claim IRQ\n"); - goto err_clk; - } - dev_info(dev, "regs %p, irq %d\n", hsotg->regs, hsotg->irq); hsotg->gadget.max_speed = USB_SPEED_HIGH; @@ -3478,25 +3512,23 @@ static int s3c_hsotg_probe(struct platform_device *pdev) goto err_supplies; } - /* Set default UTMI width */ - hsotg->phyif = GUSBCFG_PHYIF16; - - /* - * If using the generic PHY framework, check if the PHY bus - * width is 8-bit and set the phyif appropriately. - */ - if (hsotg->phy && (phy_get_bus_width(phy) == 8)) - hsotg->phyif = GUSBCFG_PHYIF8; - - if (hsotg->phy) - phy_init(hsotg->phy); - /* usb phy enable */ s3c_hsotg_phy_enable(hsotg); s3c_hsotg_corereset(hsotg); - s3c_hsotg_init(hsotg); s3c_hsotg_hw_cfg(hsotg); + s3c_hsotg_init(hsotg); + + ret = devm_request_irq(&pdev->dev, hsotg->irq, s3c_hsotg_irq, 0, + dev_name(dev), hsotg); + if (ret < 0) { + s3c_hsotg_phy_disable(hsotg); + clk_disable_unprepare(hsotg->clk); + regulator_bulk_disable(ARRAY_SIZE(hsotg->supplies), + hsotg->supplies); + dev_err(dev, "cannot claim IRQ\n"); + goto err_clk; + } /* hsotg->num_of_eps holds number of EPs other than ep0 */ @@ -3582,9 +3614,6 @@ static int s3c_hsotg_remove(struct platform_device *pdev) usb_gadget_unregister_driver(hsotg->driver); } - s3c_hsotg_phy_disable(hsotg); - if (hsotg->phy) - phy_exit(hsotg->phy); clk_disable_unprepare(hsotg->clk); return 0; @@ -3613,6 +3642,7 @@ static int s3c_hsotg_suspend(struct platform_device *pdev, pm_message_t state) ret = regulator_bulk_disable(ARRAY_SIZE(hsotg->supplies), hsotg->supplies); + clk_disable(hsotg->clk); } return ret; @@ -3627,6 +3657,8 @@ static int s3c_hsotg_resume(struct platform_device *pdev) if (hsotg->driver) { dev_info(hsotg->dev, "resuming usb gadget %s\n", hsotg->driver->driver.name); + + clk_enable(hsotg->clk); ret = regulator_bulk_enable(ARRAY_SIZE(hsotg->supplies), hsotg->supplies); } diff --git a/drivers/usb/dwc2/hcd.c b/drivers/usb/dwc2/hcd.c index 4d918ed8d343..0a0e6f0ad15f 100644 --- a/drivers/usb/dwc2/hcd.c +++ b/drivers/usb/dwc2/hcd.c @@ -697,29 +697,45 @@ static void *dwc2_hc_init_xfer(struct dwc2_hsotg *hsotg, } static int dwc2_hc_setup_align_buf(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh, - struct dwc2_host_chan *chan, void *bufptr) + struct dwc2_host_chan *chan, + struct dwc2_hcd_urb *urb, void *bufptr) { u32 buf_size; - - if (chan->ep_type != USB_ENDPOINT_XFER_ISOC) - buf_size = hsotg->core_params->max_transfer_size; - else - buf_size = 4096; + struct urb *usb_urb; + struct usb_hcd *hcd; if (!qh->dw_align_buf) { + if (chan->ep_type != USB_ENDPOINT_XFER_ISOC) + buf_size = hsotg->core_params->max_transfer_size; + else + /* 3072 = 3 max-size Isoc packets */ + buf_size = 3072; + qh->dw_align_buf = dma_alloc_coherent(hsotg->dev, buf_size, &qh->dw_align_buf_dma, GFP_ATOMIC); if (!qh->dw_align_buf) return -ENOMEM; + qh->dw_align_buf_size = buf_size; } - if (!chan->ep_is_in && chan->xfer_len) { - dma_sync_single_for_cpu(hsotg->dev, chan->xfer_dma, buf_size, - DMA_TO_DEVICE); - memcpy(qh->dw_align_buf, bufptr, chan->xfer_len); - dma_sync_single_for_device(hsotg->dev, chan->xfer_dma, buf_size, - DMA_TO_DEVICE); + if (chan->xfer_len) { + dev_vdbg(hsotg->dev, "%s(): non-aligned buffer\n", __func__); + usb_urb = urb->priv; + + if (usb_urb) { + if (usb_urb->transfer_flags & + (URB_SETUP_MAP_SINGLE | URB_DMA_MAP_SG | + URB_DMA_MAP_PAGE | URB_DMA_MAP_SINGLE)) { + hcd = dwc2_hsotg_to_hcd(hsotg); + usb_hcd_unmap_urb_for_dma(hcd, usb_urb); + } + if (!chan->ep_is_in) + memcpy(qh->dw_align_buf, bufptr, + chan->xfer_len); + } else { + dev_warn(hsotg->dev, "no URB in dwc2_urb\n"); + } } chan->align_buf = qh->dw_align_buf_dma; @@ -828,7 +844,7 @@ static int dwc2_assign_and_init_hc(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh) /* Non DWORD-aligned buffer case */ if (bufptr) { dev_vdbg(hsotg->dev, "Non-aligned buffer\n"); - if (dwc2_hc_setup_align_buf(hsotg, qh, chan, bufptr)) { + if (dwc2_hc_setup_align_buf(hsotg, qh, chan, urb, bufptr)) { dev_err(hsotg->dev, "%s: Failed to allocate memory to handle non-dword aligned buffer\n", __func__); diff --git a/drivers/usb/dwc2/hcd.h b/drivers/usb/dwc2/hcd.h index fdc6d489084a..a12bb1538666 100644 --- a/drivers/usb/dwc2/hcd.h +++ b/drivers/usb/dwc2/hcd.h @@ -243,7 +243,8 @@ enum dwc2_transaction_type { * @ntd: Actual number of transfer descriptors in a list * @dw_align_buf: Used instead of original buffer if its physical address * is not dword-aligned - * @dw_align_buf_dma: DMA address for align_buf + * @dw_align_buf_size: Size of dw_align_buf + * @dw_align_buf_dma: DMA address for dw_align_buf * @qtd_list: List of QTDs for this QH * @channel: Host channel currently processing transfers for this QH * @qh_list_entry: Entry for QH in either the periodic or non-periodic @@ -276,6 +277,7 @@ struct dwc2_qh { u16 start_split_frame; u16 ntd; u8 *dw_align_buf; + int dw_align_buf_size; dma_addr_t dw_align_buf_dma; struct list_head qtd_list; struct dwc2_host_chan *channel; diff --git a/drivers/usb/dwc2/hcd_intr.c b/drivers/usb/dwc2/hcd_intr.c index 47b9eb5389b4..551ba878b003 100644 --- a/drivers/usb/dwc2/hcd_intr.c +++ b/drivers/usb/dwc2/hcd_intr.c @@ -465,12 +465,8 @@ static int dwc2_update_urb_state(struct dwc2_hsotg *hsotg, /* Non DWORD-aligned buffer case handling */ if (chan->align_buf && xfer_length && chan->ep_is_in) { dev_vdbg(hsotg->dev, "%s(): non-aligned buffer\n", __func__); - dma_sync_single_for_cpu(hsotg->dev, urb->dma, urb->length, - DMA_FROM_DEVICE); memcpy(urb->buf + urb->actual_length, chan->qh->dw_align_buf, xfer_length); - dma_sync_single_for_device(hsotg->dev, urb->dma, urb->length, - DMA_FROM_DEVICE); } dev_vdbg(hsotg->dev, "urb->actual_length=%d xfer_length=%d\n", @@ -560,14 +556,9 @@ static enum dwc2_halt_status dwc2_update_isoc_urb_state( chan->ep_is_in) { dev_vdbg(hsotg->dev, "%s(): non-aligned buffer\n", __func__); - dma_sync_single_for_cpu(hsotg->dev, urb->dma, - urb->length, DMA_FROM_DEVICE); memcpy(urb->buf + frame_desc->offset + qtd->isoc_split_offset, chan->qh->dw_align_buf, frame_desc->actual_length); - dma_sync_single_for_device(hsotg->dev, urb->dma, - urb->length, - DMA_FROM_DEVICE); } break; case DWC2_HC_XFER_FRAME_OVERRUN: @@ -594,14 +585,9 @@ static enum dwc2_halt_status dwc2_update_isoc_urb_state( chan->ep_is_in) { dev_vdbg(hsotg->dev, "%s(): non-aligned buffer\n", __func__); - dma_sync_single_for_cpu(hsotg->dev, urb->dma, - urb->length, DMA_FROM_DEVICE); memcpy(urb->buf + frame_desc->offset + qtd->isoc_split_offset, chan->qh->dw_align_buf, frame_desc->actual_length); - dma_sync_single_for_device(hsotg->dev, urb->dma, - urb->length, - DMA_FROM_DEVICE); } /* Skip whole frame */ @@ -937,12 +923,8 @@ static int dwc2_xfercomp_isoc_split_in(struct dwc2_hsotg *hsotg, if (chan->align_buf) { dev_vdbg(hsotg->dev, "%s(): non-aligned buffer\n", __func__); - dma_sync_single_for_cpu(hsotg->dev, qtd->urb->dma, - qtd->urb->length, DMA_FROM_DEVICE); memcpy(qtd->urb->buf + frame_desc->offset + qtd->isoc_split_offset, chan->qh->dw_align_buf, len); - dma_sync_single_for_device(hsotg->dev, qtd->urb->dma, - qtd->urb->length, DMA_FROM_DEVICE); } qtd->isoc_split_offset += len; @@ -1170,12 +1152,8 @@ static void dwc2_update_urb_state_abn(struct dwc2_hsotg *hsotg, /* Non DWORD-aligned buffer case handling */ if (chan->align_buf && xfer_length && chan->ep_is_in) { dev_vdbg(hsotg->dev, "%s(): non-aligned buffer\n", __func__); - dma_sync_single_for_cpu(hsotg->dev, urb->dma, urb->length, - DMA_FROM_DEVICE); memcpy(urb->buf + urb->actual_length, chan->qh->dw_align_buf, xfer_length); - dma_sync_single_for_device(hsotg->dev, urb->dma, urb->length, - DMA_FROM_DEVICE); } urb->actual_length += xfer_length; @@ -1890,12 +1868,20 @@ static void dwc2_hc_chhltd_intr_dma(struct dwc2_hsotg *hsotg, "hcint 0x%08x, intsts 0x%08x\n", chan->hcint, readl(hsotg->regs + GINTSTS)); + goto error; } } } else { dev_info(hsotg->dev, "NYET/NAK/ACK/other in non-error case, 0x%08x\n", chan->hcint); +error: + /* Failthrough: use 3-strikes rule */ + qtd->error_count++; + dwc2_update_urb_state_abn(hsotg, chan, chnum, qtd->urb, + qtd, DWC2_HC_XFER_XACT_ERR); + dwc2_hcd_save_data_toggle(hsotg, chan, chnum, qtd); + dwc2_halt_channel(hsotg, chan, qtd, DWC2_HC_XFER_XACT_ERR); } } diff --git a/drivers/usb/dwc2/hcd_queue.c b/drivers/usb/dwc2/hcd_queue.c index 9540f7e1e20e..bb97838bc6c0 100644 --- a/drivers/usb/dwc2/hcd_queue.c +++ b/drivers/usb/dwc2/hcd_queue.c @@ -229,19 +229,11 @@ static struct dwc2_qh *dwc2_hcd_qh_create(struct dwc2_hsotg *hsotg, */ void dwc2_hcd_qh_free(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh) { - u32 buf_size; - - if (hsotg->core_params->dma_desc_enable > 0) { + if (hsotg->core_params->dma_desc_enable > 0) dwc2_hcd_qh_free_ddma(hsotg, qh); - } else if (qh->dw_align_buf) { - if (qh->ep_type == USB_ENDPOINT_XFER_ISOC) - buf_size = 4096; - else - buf_size = hsotg->core_params->max_transfer_size; - dma_free_coherent(hsotg->dev, buf_size, qh->dw_align_buf, - qh->dw_align_buf_dma); - } - + else if (qh->dw_align_buf) + dma_free_coherent(hsotg->dev, qh->dw_align_buf_size, + qh->dw_align_buf, qh->dw_align_buf_dma); kfree(qh); } diff --git a/drivers/usb/dwc2/platform.c b/drivers/usb/dwc2/platform.c index a10e7a353576..121dbdafc06b 100644 --- a/drivers/usb/dwc2/platform.c +++ b/drivers/usb/dwc2/platform.c @@ -42,6 +42,8 @@ #include <linux/of_device.h> #include <linux/platform_device.h> +#include <linux/usb/of.h> + #include "core.h" #include "hcd.h" @@ -75,6 +77,34 @@ static const struct dwc2_core_params params_bcm2835 = { .uframe_sched = 0, }; +static const struct dwc2_core_params params_rk3066 = { + .otg_cap = 2, /* non-HNP/non-SRP */ + .otg_ver = -1, + .dma_enable = -1, + .dma_desc_enable = 0, + .speed = -1, + .enable_dynamic_fifo = 1, + .en_multiple_tx_fifo = -1, + .host_rx_fifo_size = 520, /* 520 DWORDs */ + .host_nperio_tx_fifo_size = 128, /* 128 DWORDs */ + .host_perio_tx_fifo_size = 256, /* 256 DWORDs */ + .max_transfer_size = 65535, + .max_packet_count = -1, + .host_channels = -1, + .phy_type = -1, + .phy_utmi_width = -1, + .phy_ulpi_ddr = -1, + .phy_ulpi_ext_vbus = -1, + .i2c_enable = -1, + .ulpi_fs_ls = -1, + .host_support_fs_ls_low_power = -1, + .host_ls_low_power_phy_clk = -1, + .ts_dline = -1, + .reload_ctl = -1, + .ahbcfg = 0x7, /* INCR16 */ + .uframe_sched = -1, +}; + /** * dwc2_driver_remove() - Called when the DWC_otg core is unregistered with the * DWC_otg driver @@ -97,6 +127,7 @@ static int dwc2_driver_remove(struct platform_device *dev) static const struct of_device_id dwc2_of_match_table[] = { { .compatible = "brcm,bcm2835-usb", .data = ¶ms_bcm2835 }, + { .compatible = "rockchip,rk3066-usb", .data = ¶ms_rk3066 }, { .compatible = "snps,dwc2", .data = NULL }, {}, }; @@ -171,6 +202,8 @@ static int dwc2_driver_probe(struct platform_device *dev) dev_dbg(&dev->dev, "mapped PA %08lx to VA %p\n", (unsigned long)res->start, hsotg->regs); + hsotg->dr_mode = of_usb_get_dr_mode(dev->dev.of_node); + retval = dwc2_hcd_init(hsotg, irq, params); if (retval) return retval; |