diff options
Diffstat (limited to 'drivers/usb/dwc2/gadget.c')
-rw-r--r-- | drivers/usb/dwc2/gadget.c | 190 |
1 files changed, 111 insertions, 79 deletions
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); } |