From fe5a6c48fd954489f51aab151d5dad9254c76419 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Thu, 12 May 2016 16:06:41 +0300 Subject: [PATCH 001/109] usb: gadget: storage: get rid of fsg_num_buffers_validate() valid range for storage buffers is encoded in Kconfig already. Instead of checking again, let's drop fsg_num_buffers_validate() altogether. Signed-off-by: Felipe Balbi --- drivers/usb/gadget/function/f_mass_storage.c | 22 +------------------- 1 file changed, 1 insertion(+), 21 deletions(-) diff --git a/drivers/usb/gadget/function/f_mass_storage.c b/drivers/usb/gadget/function/f_mass_storage.c index 5c6d4d7ca605..2505117e88e8 100644 --- a/drivers/usb/gadget/function/f_mass_storage.c +++ b/drivers/usb/gadget/function/f_mass_storage.c @@ -2655,18 +2655,6 @@ void fsg_common_put(struct fsg_common *common) } EXPORT_SYMBOL_GPL(fsg_common_put); -/* check if fsg_num_buffers is within a valid range */ -static inline int fsg_num_buffers_validate(unsigned int fsg_num_buffers) -{ -#define FSG_MAX_NUM_BUFFERS 32 - - if (fsg_num_buffers >= 2 && fsg_num_buffers <= FSG_MAX_NUM_BUFFERS) - return 0; - pr_err("fsg_num_buffers %u is out of range (%d to %d)\n", - fsg_num_buffers, 2, FSG_MAX_NUM_BUFFERS); - return -EINVAL; -} - static struct fsg_common *fsg_common_setup(struct fsg_common *common) { if (!common) { @@ -2709,11 +2697,7 @@ static void _fsg_common_free_buffers(struct fsg_buffhd *buffhds, unsigned n) int fsg_common_set_num_buffers(struct fsg_common *common, unsigned int n) { struct fsg_buffhd *bh, *buffhds; - int i, rc; - - rc = fsg_num_buffers_validate(n); - if (rc != 0) - return rc; + int i; buffhds = kcalloc(n, sizeof(*buffhds), GFP_KERNEL); if (!buffhds) @@ -3401,10 +3385,6 @@ static ssize_t fsg_opts_num_buffers_store(struct config_item *item, if (ret) goto end; - ret = fsg_num_buffers_validate(num); - if (ret) - goto end; - fsg_common_set_num_buffers(opts->common, num); ret = len; From d8877fc7e7ec3eb4515a4e6b947599bdda058323 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Thu, 12 May 2016 15:02:29 +0300 Subject: [PATCH 002/109] usb: gadget: storage: increase maximum storage num buffers With a default size of 16kiB and with maximum of 32 buffers, we can transfer up to 512kiB, however Linux can transfer up to 1MiB in a single mass storage block transfer to USB3 storage devices. Because of this, 1MiB block transfers end up being slower than 512kiB block transfers. Let's increase maximum number of storage buffers to a ridiculous amount (256) so that anybody wanting to test maximum achievable throughput can do so. Signed-off-by: Felipe Balbi --- drivers/usb/gadget/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig index 2057add439f0..3c3f31ceece7 100644 --- a/drivers/usb/gadget/Kconfig +++ b/drivers/usb/gadget/Kconfig @@ -114,7 +114,7 @@ config USB_GADGET_VBUS_DRAW config USB_GADGET_STORAGE_NUM_BUFFERS int "Number of storage pipeline buffers" - range 2 32 + range 2 256 default 2 help Usually 2 buffers are enough to establish a good buffering From 5185c91385d73cdf79836eb8548e4726e43ae831 Mon Sep 17 00:00:00 2001 From: Tim Harvey Date: Mon, 23 May 2016 06:58:41 -0700 Subject: [PATCH 003/109] usb: gadget: net2280: add USB2380 support The PLX USB2380 is a PCIe version of the NET2280 and behaves more like the USB338x but without the USB3.0 superspeed support. This was tested with g_ether, g_serial, g_mass_storage on a Gateworks Ventana GW2383. Cc: Justin DeFields Signed-off-by: Tim Harvey Signed-off-by: Felipe Balbi --- drivers/usb/gadget/udc/Kconfig | 4 ++- drivers/usb/gadget/udc/net2280.c | 51 +++++++++++++++++++------------- drivers/usb/gadget/udc/net2280.h | 1 + 3 files changed, 34 insertions(+), 22 deletions(-) diff --git a/drivers/usb/gadget/udc/Kconfig b/drivers/usb/gadget/udc/Kconfig index 7c289416f87d..658b8da60915 100644 --- a/drivers/usb/gadget/udc/Kconfig +++ b/drivers/usb/gadget/udc/Kconfig @@ -312,7 +312,7 @@ config USB_NET2272_DMA If unsure, say "N" here. The driver works fine in PIO mode. config USB_NET2280 - tristate "NetChip 228x / PLX USB338x" + tristate "NetChip NET228x / PLX USB3x8x" depends on PCI help NetChip 2280 / 2282 is a PCI based USB peripheral controller which @@ -322,6 +322,8 @@ config USB_NET2280 (for control transfers) and several endpoints with dedicated functions. + PLX 2380 is a PCIe version of the PLX 2380. + PLX 3380 / 3382 is a PCIe based USB peripheral controller which supports full, high speed USB 2.0 and super speed USB 3.0 data transfers. diff --git a/drivers/usb/gadget/udc/net2280.c b/drivers/usb/gadget/udc/net2280.c index c894b94b234b..614ab951a4ae 100644 --- a/drivers/usb/gadget/udc/net2280.c +++ b/drivers/usb/gadget/udc/net2280.c @@ -211,7 +211,7 @@ net2280_enable(struct usb_ep *_ep, const struct usb_endpoint_descriptor *desc) goto print_err; } - if (dev->quirks & PLX_SUPERSPEED) { + if (dev->quirks & PLX_PCIE) { if ((desc->bEndpointAddress & 0x0f) >= 0x0c) { ret = -EDOM; goto print_err; @@ -245,7 +245,7 @@ net2280_enable(struct usb_ep *_ep, const struct usb_endpoint_descriptor *desc) /* set type, direction, address; reset fifo counters */ writel(BIT(FIFO_FLUSH), &ep->regs->ep_stat); - if ((dev->quirks & PLX_SUPERSPEED) && dev->enhanced_mode) { + if ((dev->quirks & PLX_PCIE) && dev->enhanced_mode) { tmp = readl(&ep->cfg->ep_cfg); /* If USB ep number doesn't match hardware ep number */ if ((tmp & 0xf) != usb_endpoint_num(desc)) { @@ -316,7 +316,7 @@ net2280_enable(struct usb_ep *_ep, const struct usb_endpoint_descriptor *desc) BIT(CLEAR_NAK_OUT_PACKETS_MODE), &ep->regs->ep_rsp); } - if (dev->quirks & PLX_SUPERSPEED) + if (dev->quirks & PLX_PCIE) ep_clear_seqnum(ep); writel(tmp, &ep->cfg->ep_cfg); @@ -527,7 +527,7 @@ static int net2280_disable(struct usb_ep *_ep) spin_lock_irqsave(&ep->dev->lock, flags); nuke(ep); - if (ep->dev->quirks & PLX_SUPERSPEED) + if (ep->dev->quirks & PLX_PCIE) ep_reset_338x(ep->dev->regs, ep); else ep_reset_228x(ep->dev->regs, ep); @@ -862,7 +862,7 @@ static void start_queue(struct net2280_ep *ep, u32 dmactl, u32 td_dma) writel(readl(&dma->dmastat), &dma->dmastat); writel(td_dma, &dma->dmadesc); - if (ep->dev->quirks & PLX_SUPERSPEED) + if (ep->dev->quirks & PLX_PCIE) dmactl |= BIT(DMA_REQUEST_OUTSTANDING); writel(dmactl, &dma->dmactl); @@ -1046,7 +1046,7 @@ net2280_queue(struct usb_ep *_ep, struct usb_request *_req, gfp_t gfp_flags) /* kickstart this i/o queue? */ if (list_empty(&ep->queue) && !ep->stopped && - !((dev->quirks & PLX_SUPERSPEED) && ep->dma && + !((dev->quirks & PLX_PCIE) && ep->dma && (readl(&ep->regs->ep_rsp) & BIT(CLEAR_ENDPOINT_HALT)))) { /* use DMA if the endpoint supports it, else pio */ @@ -1169,7 +1169,7 @@ static void scan_dma_completions(struct net2280_ep *ep) break; } else if (!ep->is_in && (req->req.length % ep->ep.maxpacket) && - !(ep->dev->quirks & PLX_SUPERSPEED)) { + !(ep->dev->quirks & PLX_PCIE)) { tmp = readl(&ep->regs->ep_stat); /* AVOID TROUBLE HERE by not issuing short reads from @@ -1367,7 +1367,7 @@ net2280_set_halt_and_wedge(struct usb_ep *_ep, int value, int wedged) ep->wedged = 1; } else { clear_halt(ep); - if (ep->dev->quirks & PLX_SUPERSPEED && + if (ep->dev->quirks & PLX_PCIE && !list_empty(&ep->queue) && ep->td_dma) restart_dma(ep); ep->wedged = 0; @@ -2394,7 +2394,7 @@ static int net2280_start(struct usb_gadget *_gadget, */ net2280_led_active(dev, 1); - if ((dev->quirks & PLX_SUPERSPEED) && !dev->bug7734_patched) + if ((dev->quirks & PLX_PCIE) && !dev->bug7734_patched) defect7374_enable_data_eps_zero(dev); ep0_start(dev); @@ -3063,7 +3063,7 @@ static void handle_stat0_irqs(struct net2280 *dev, u32 stat) } ep->stopped = 0; dev->protocol_stall = 0; - if (!(dev->quirks & PLX_SUPERSPEED)) { + if (!(dev->quirks & PLX_PCIE)) { if (ep->dev->quirks & PLX_2280) tmp = BIT(FIFO_OVERFLOW) | BIT(FIFO_UNDERFLOW); @@ -3090,7 +3090,7 @@ static void handle_stat0_irqs(struct net2280 *dev, u32 stat) cpu_to_le32s(&u.raw[0]); cpu_to_le32s(&u.raw[1]); - if ((dev->quirks & PLX_SUPERSPEED) && !dev->bug7734_patched) + if ((dev->quirks & PLX_PCIE) && !dev->bug7734_patched) defect7374_workaround(dev, u.r); tmp = 0; @@ -3173,7 +3173,7 @@ static void handle_stat0_irqs(struct net2280 *dev, u32 stat) } else { ep_vdbg(dev, "%s clear halt\n", e->ep.name); clear_halt(e); - if ((ep->dev->quirks & PLX_SUPERSPEED) && + if ((ep->dev->quirks & PLX_PCIE) && !list_empty(&e->queue) && e->td_dma) restart_dma(e); } @@ -3195,7 +3195,7 @@ static void handle_stat0_irqs(struct net2280 *dev, u32 stat) if (e->ep.name == ep0name) goto do_stall; set_halt(e); - if ((dev->quirks & PLX_SUPERSPEED) && e->dma) + if ((dev->quirks & PLX_PCIE) && e->dma) abort_dma(e); allow_status(ep); ep_vdbg(dev, "%s set halt\n", ep->ep.name); @@ -3234,7 +3234,7 @@ do_stall: #undef w_length next_endpoints: - if ((dev->quirks & PLX_SUPERSPEED) && dev->enhanced_mode) { + if ((dev->quirks & PLX_PCIE) && dev->enhanced_mode) { u32 mask = (BIT(ENDPOINT_0_INTERRUPT) | USB3380_IRQSTAT0_EP_INTR_MASK_IN | USB3380_IRQSTAT0_EP_INTR_MASK_OUT); @@ -3399,7 +3399,7 @@ __acquires(dev->lock) writel(tmp, &dma->dmastat); /* dma sync*/ - if (dev->quirks & PLX_SUPERSPEED) { + if (dev->quirks & PLX_PCIE) { u32 r_dmacount = readl(&dma->dmacount); if (!ep->is_in && (r_dmacount & 0x00FFFFFF) && (tmp & BIT(DMA_TRANSACTION_DONE_INTERRUPT))) @@ -3468,7 +3468,7 @@ static irqreturn_t net2280_irq(int irq, void *_dev) /* control requests and PIO */ handle_stat0_irqs(dev, readl(&dev->regs->irqstat0)); - if (dev->quirks & PLX_SUPERSPEED) { + if (dev->quirks & PLX_PCIE) { /* re-enable interrupt to trigger any possible new interrupt */ u32 pciirqenb1 = readl(&dev->regs->pciirqenb1); writel(pciirqenb1 & 0x7FFFFFFF, &dev->regs->pciirqenb1); @@ -3513,7 +3513,7 @@ static void net2280_remove(struct pci_dev *pdev) } if (dev->got_irq) free_irq(pdev->irq, dev); - if (dev->quirks & PLX_SUPERSPEED) + if (dev->quirks & PLX_PCIE) pci_disable_msi(pdev); if (dev->regs) iounmap(dev->regs); @@ -3593,7 +3593,7 @@ static int net2280_probe(struct pci_dev *pdev, const struct pci_device_id *id) dev->dep = (struct net2280_dep_regs __iomem *) (base + 0x0200); dev->epregs = (struct net2280_ep_regs __iomem *) (base + 0x0300); - if (dev->quirks & PLX_SUPERSPEED) { + if (dev->quirks & PLX_PCIE) { u32 fsmvalue; u32 usbstat; dev->usb_ext = (struct usb338x_usb_ext_regs __iomem *) @@ -3637,7 +3637,7 @@ static int net2280_probe(struct pci_dev *pdev, const struct pci_device_id *id) goto done; } - if (dev->quirks & PLX_SUPERSPEED) + if (dev->quirks & PLX_PCIE) if (pci_enable_msi(pdev)) ep_err(dev, "Failed to enable MSI mode\n"); @@ -3755,10 +3755,19 @@ static const struct pci_device_id pci_ids[] = { { .class = PCI_CLASS_SERIAL_USB_DEVICE, .class_mask = ~0, .vendor = PCI_VENDOR_ID_PLX, + .device = 0x2380, + .subvendor = PCI_ANY_ID, + .subdevice = PCI_ANY_ID, + .driver_data = PLX_PCIE, + }, + { + .class = ((PCI_CLASS_SERIAL_USB << 8) | 0xfe), + .class_mask = ~0, + .vendor = PCI_VENDOR_ID_PLX, .device = 0x3380, .subvendor = PCI_ANY_ID, .subdevice = PCI_ANY_ID, - .driver_data = PLX_SUPERSPEED, + .driver_data = PLX_PCIE | PLX_SUPERSPEED, }, { .class = PCI_CLASS_SERIAL_USB_DEVICE, @@ -3767,7 +3776,7 @@ static const struct pci_device_id pci_ids[] = { { .device = 0x3382, .subvendor = PCI_ANY_ID, .subdevice = PCI_ANY_ID, - .driver_data = PLX_SUPERSPEED, + .driver_data = PLX_PCIE | PLX_SUPERSPEED, }, { /* end: all zeroes */ } }; diff --git a/drivers/usb/gadget/udc/net2280.h b/drivers/usb/gadget/udc/net2280.h index 0d32052bf16f..2736a95751c3 100644 --- a/drivers/usb/gadget/udc/net2280.h +++ b/drivers/usb/gadget/udc/net2280.h @@ -47,6 +47,7 @@ set_idx_reg(struct net2280_regs __iomem *regs, u32 index, u32 value) #define PLX_LEGACY BIT(0) #define PLX_2280 BIT(1) #define PLX_SUPERSPEED BIT(2) +#define PLX_PCIE BIT(3) #define REG_DIAG 0x0 #define RETRY_COUNTER 16 From d7be295243bbe53649986d869b398403eaa41bb7 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Wed, 4 May 2016 15:49:37 +0300 Subject: [PATCH 004/109] usb: dwc3: gadget: re-factor ->udc_start and ->udc_stop we will be re-using it for suspend/resume, so instead of duplicating code, let's just re-factor the functions so they can be re-used. Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/gadget.c | 92 +++++++++++++++++++++------------------ 1 file changed, 50 insertions(+), 42 deletions(-) diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index 07248ff1be5c..54e36b70361a 100644 --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c @@ -1616,37 +1616,12 @@ static void dwc3_gadget_disable_irq(struct dwc3 *dwc) static irqreturn_t dwc3_interrupt(int irq, void *_dwc); static irqreturn_t dwc3_thread_interrupt(int irq, void *_dwc); -static int dwc3_gadget_start(struct usb_gadget *g, - struct usb_gadget_driver *driver) +static int __dwc3_gadget_start(struct dwc3 *dwc) { - struct dwc3 *dwc = gadget_to_dwc(g); struct dwc3_ep *dep; - unsigned long flags; int ret = 0; - int irq; u32 reg; - irq = platform_get_irq(to_platform_device(dwc->dev), 0); - ret = request_threaded_irq(irq, dwc3_interrupt, dwc3_thread_interrupt, - IRQF_SHARED, "dwc3", dwc->ev_buf); - if (ret) { - dev_err(dwc->dev, "failed to request irq #%d --> %d\n", - irq, ret); - goto err0; - } - - spin_lock_irqsave(&dwc->lock, flags); - - if (dwc->gadget_driver) { - dev_err(dwc->dev, "%s is already bound to %s\n", - dwc->gadget.name, - dwc->gadget_driver->driver.name); - ret = -EBUSY; - goto err1; - } - - dwc->gadget_driver = driver; - reg = dwc3_readl(dwc->regs, DWC3_DCFG); reg &= ~(DWC3_DCFG_SPEED_MASK); @@ -1709,7 +1684,7 @@ static int dwc3_gadget_start(struct usb_gadget *g, false); if (ret) { dev_err(dwc->dev, "failed to enable %s\n", dep->name); - goto err2; + goto err0; } dep = dwc->eps[1]; @@ -1717,7 +1692,7 @@ static int dwc3_gadget_start(struct usb_gadget *g, false); if (ret) { dev_err(dwc->dev, "failed to enable %s\n", dep->name); - goto err3; + goto err1; } /* begin to receive SETUP packets */ @@ -1726,25 +1701,63 @@ static int dwc3_gadget_start(struct usb_gadget *g, dwc3_gadget_enable_irq(dwc); + return 0; + +err1: + __dwc3_gadget_ep_disable(dwc->eps[0]); + +err0: + return ret; +} + +static int dwc3_gadget_start(struct usb_gadget *g, + struct usb_gadget_driver *driver) +{ + struct dwc3 *dwc = gadget_to_dwc(g); + unsigned long flags; + int ret = 0; + int irq; + + irq = platform_get_irq(to_platform_device(dwc->dev), 0); + ret = request_threaded_irq(irq, dwc3_interrupt, dwc3_thread_interrupt, + IRQF_SHARED, "dwc3", dwc->ev_buf); + if (ret) { + dev_err(dwc->dev, "failed to request irq #%d --> %d\n", + irq, ret); + goto err0; + } + + spin_lock_irqsave(&dwc->lock, flags); + if (dwc->gadget_driver) { + dev_err(dwc->dev, "%s is already bound to %s\n", + dwc->gadget.name, + dwc->gadget_driver->driver.name); + ret = -EBUSY; + goto err1; + } + + dwc->gadget_driver = driver; + + __dwc3_gadget_start(dwc); spin_unlock_irqrestore(&dwc->lock, flags); return 0; -err3: - __dwc3_gadget_ep_disable(dwc->eps[0]); - -err2: - dwc->gadget_driver = NULL; - err1: spin_unlock_irqrestore(&dwc->lock, flags); - - free_irq(irq, dwc->ev_buf); + free_irq(irq, dwc); err0: return ret; } +static void __dwc3_gadget_stop(struct dwc3 *dwc) +{ + dwc3_gadget_disable_irq(dwc); + __dwc3_gadget_ep_disable(dwc->eps[0]); + __dwc3_gadget_ep_disable(dwc->eps[1]); +} + static int dwc3_gadget_stop(struct usb_gadget *g) { struct dwc3 *dwc = gadget_to_dwc(g); @@ -1752,13 +1765,8 @@ static int dwc3_gadget_stop(struct usb_gadget *g) int irq; spin_lock_irqsave(&dwc->lock, flags); - - dwc3_gadget_disable_irq(dwc); - __dwc3_gadget_ep_disable(dwc->eps[0]); - __dwc3_gadget_ep_disable(dwc->eps[1]); - + __dwc3_gadget_stop(dwc); dwc->gadget_driver = NULL; - spin_unlock_irqrestore(&dwc->lock, flags); irq = platform_get_irq(to_platform_device(dwc->dev), 0); From 9f8a67b65a49d0e35c6ca782136c84541d948a64 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Wed, 4 May 2016 15:50:27 +0300 Subject: [PATCH 005/109] usb: dwc3: gadget: fix gadget suspend/resume Instead of trying hard to stay connected to the host, it's best (and far easier) to disconnect from the host already. Anything relying on KEEP_CONNECT will just have that ignored, but we don't have proper hibernation implementation yet, so there are no regressions. In any case, hibernation is only useful for runtime PM, not system sleep. While at that, also remove dwc3.dcfg which has been rendered unnecessary. Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/core.h | 1 - drivers/usb/dwc3/gadget.c | 44 +++++++++++---------------------------- 2 files changed, 12 insertions(+), 33 deletions(-) diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h index 654050684f4f..c881c8232e21 100644 --- a/drivers/usb/dwc3/core.h +++ b/drivers/usb/dwc3/core.h @@ -819,7 +819,6 @@ struct dwc3 { enum usb_dr_mode dr_mode; /* used for suspend/resume */ - u32 dcfg; u32 gctl; u32 nr_scratch; diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index 54e36b70361a..ae55f500cdbd 100644 --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c @@ -2943,60 +2943,40 @@ void dwc3_gadget_exit(struct dwc3 *dwc) int dwc3_gadget_suspend(struct dwc3 *dwc) { + int ret; + if (!dwc->gadget_driver) return 0; - if (dwc->pullups_connected) { - dwc3_gadget_disable_irq(dwc); - dwc3_gadget_run_stop(dwc, true, true); - } + ret = dwc3_gadget_run_stop(dwc, false, false); + if (ret < 0) + return ret; - __dwc3_gadget_ep_disable(dwc->eps[0]); - __dwc3_gadget_ep_disable(dwc->eps[1]); - - dwc->dcfg = dwc3_readl(dwc->regs, DWC3_DCFG); + dwc3_disconnect_gadget(dwc); + __dwc3_gadget_stop(dwc); return 0; } int dwc3_gadget_resume(struct dwc3 *dwc) { - struct dwc3_ep *dep; int ret; if (!dwc->gadget_driver) return 0; - /* Start with SuperSpeed Default */ - dwc3_gadget_ep0_desc.wMaxPacketSize = cpu_to_le16(512); - - dep = dwc->eps[0]; - ret = __dwc3_gadget_ep_enable(dep, &dwc3_gadget_ep0_desc, NULL, false, - false); - if (ret) + ret = __dwc3_gadget_start(dwc); + if (ret < 0) goto err0; - dep = dwc->eps[1]; - ret = __dwc3_gadget_ep_enable(dep, &dwc3_gadget_ep0_desc, NULL, false, - false); - if (ret) + ret = dwc3_gadget_run_stop(dwc, true, false); + if (ret < 0) goto err1; - /* begin to receive SETUP packets */ - dwc->ep0state = EP0_SETUP_PHASE; - dwc3_ep0_out_start(dwc); - - dwc3_writel(dwc->regs, DWC3_DCFG, dwc->dcfg); - - if (dwc->pullups_connected) { - dwc3_gadget_enable_irq(dwc); - dwc3_gadget_run_stop(dwc, true, false); - } - return 0; err1: - __dwc3_gadget_ep_disable(dwc->eps[0]); + __dwc3_gadget_stop(dwc); err0: return ret; From 7f370ed0cfe9aa1520696c1c71e8a51e2c0bbcc1 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Mon, 9 May 2016 15:27:01 +0300 Subject: [PATCH 006/109] usb: dwc3: core: get rid of DWC3_PM_OPS macro that macro is unnecessary and just adds pointless obfuscation. Let's remove it. Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/core.c | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c index a590cd225bb7..245f4ff6ae16 100644 --- a/drivers/usb/dwc3/core.c +++ b/drivers/usb/dwc3/core.c @@ -1209,16 +1209,12 @@ err_usb2phy_power: return ret; } +#endif /* CONFIG_PM_SLEEP */ static const struct dev_pm_ops dwc3_dev_pm_ops = { SET_SYSTEM_SLEEP_PM_OPS(dwc3_suspend, dwc3_resume) }; -#define DWC3_PM_OPS &(dwc3_dev_pm_ops) -#else -#define DWC3_PM_OPS NULL -#endif - #ifdef CONFIG_OF static const struct of_device_id of_dwc3_match[] = { { @@ -1250,7 +1246,7 @@ static struct platform_driver dwc3_driver = { .name = "dwc3", .of_match_table = of_match_ptr(of_dwc3_match), .acpi_match_table = ACPI_PTR(dwc3_acpi_match), - .pm = DWC3_PM_OPS, + .pm = &dwc3_dev_pm_ops, }, }; From c4233573f6ee611033faa9116fc7003775a450b9 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Thu, 12 May 2016 14:08:34 +0300 Subject: [PATCH 007/109] usb: dwc3: gadget: prepare TRBs on update transfers too If we're updating transfers, we can also prepare as many TRBs as we can fit in the ring. Let's start doing that. This patch 'solves' a limitation of how many TRBs we can prepare when we're getting close the end of the ring. Instead driver to prepare only up to end of the ring, we check if we have space to wrap around the ring properly. Note that this only happens when our enqueue and dequeue pointers are equal (which is the case for bulk endpoints after an XferComplete event). Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/gadget.c | 50 +++++++++++++++++++++++---------------- 1 file changed, 29 insertions(+), 21 deletions(-) diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index ae55f500cdbd..236c231114df 100644 --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c @@ -880,16 +880,40 @@ static void dwc3_prepare_one_trb(struct dwc3_ep *dep, trace_dwc3_prepare_trb(dep, trb); } +static u32 dwc3_calc_trbs_left(struct dwc3_ep *dep) +{ + struct dwc3_trb *tmp; + + /* + * If enqueue & dequeue are equal than it is either full or empty. + * + * One way to know for sure is if the TRB right before us has HWO bit + * set or not. If it has, then we're definitely full and can't fit any + * more transfers in our ring. + */ + if (dep->trb_enqueue == dep->trb_dequeue) { + /* If we're full, enqueue/dequeue are > 0 */ + if (dep->trb_enqueue) { + tmp = &dep->trb_pool[dep->trb_enqueue - 1]; + if (tmp->ctrl & DWC3_TRB_CTRL_HWO) + return 0; + } + + return DWC3_TRB_NUM - 1; + } + + return dep->trb_dequeue - dep->trb_enqueue; +} + /* * dwc3_prepare_trbs - setup TRBs from requests * @dep: endpoint for which requests are being prepared - * @starting: true if the endpoint is idle and no requests are queued. * * The function goes through the requests list and sets up TRBs for the * transfers. The function returns once there are no more TRBs available or * it runs out of requests. */ -static void dwc3_prepare_trbs(struct dwc3_ep *dep, bool starting) +static void dwc3_prepare_trbs(struct dwc3_ep *dep) { struct dwc3_request *req, *n; u32 trbs_left; @@ -897,23 +921,7 @@ static void dwc3_prepare_trbs(struct dwc3_ep *dep, bool starting) BUILD_BUG_ON_NOT_POWER_OF_2(DWC3_TRB_NUM); - trbs_left = dep->trb_dequeue - dep->trb_enqueue; - - /* - * If enqueue & dequeue are equal than it is either full or empty. If we - * are starting to process requests then we are empty. Otherwise we are - * full and don't do anything - */ - if (!trbs_left) { - if (!starting) - return; - - trbs_left = DWC3_TRB_NUM; - } - - /* The last TRB is a link TRB, not used for xfer */ - if (trbs_left <= 1) - return; + trbs_left = dwc3_calc_trbs_left(dep); list_for_each_entry_safe(req, n, &dep->pending_list, list) { unsigned length; @@ -996,12 +1004,12 @@ static int __dwc3_gadget_kick_transfer(struct dwc3_ep *dep, u16 cmd_param, */ if (start_new) { if (list_empty(&dep->started_list)) - dwc3_prepare_trbs(dep, start_new); + dwc3_prepare_trbs(dep); /* req points to the first request which will be sent */ req = next_request(&dep->started_list); } else { - dwc3_prepare_trbs(dep, start_new); + dwc3_prepare_trbs(dep); /* * req points to the first request where HWO changed from 0 to 1 From 4fae2e3e15157ac312b91234389e79f7a76667b3 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Thu, 12 May 2016 16:53:59 +0300 Subject: [PATCH 008/109] usb: dwc3: gadget: simplify __dwc3_gadget_kick_transfer() as it turns out, we don't need the extra 'start_new' argument as that can be inferred from DWC3_EP_BUSY flag. Because of that, we can simplify __dwc3_gadget_kick_transfer() by quite a bit, even allowing us to prepare more TRBs unconditionally. Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/gadget.c | 48 ++++++++++++--------------------------- 1 file changed, 14 insertions(+), 34 deletions(-) diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index 236c231114df..f82882023d54 100644 --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c @@ -984,38 +984,19 @@ static void dwc3_prepare_trbs(struct dwc3_ep *dep) } } -static int __dwc3_gadget_kick_transfer(struct dwc3_ep *dep, u16 cmd_param, - int start_new) +static int __dwc3_gadget_kick_transfer(struct dwc3_ep *dep, u16 cmd_param) { struct dwc3_gadget_ep_cmd_params params; struct dwc3_request *req; struct dwc3 *dwc = dep->dwc; + int starting; int ret; u32 cmd; - if (start_new && (dep->flags & DWC3_EP_BUSY)) { - dwc3_trace(trace_dwc3_gadget, "%s: endpoint busy", dep->name); - return -EBUSY; - } + starting = !(dep->flags & DWC3_EP_BUSY); - /* - * If we are getting here after a short-out-packet we don't enqueue any - * new requests as we try to set the IOC bit only on the last request. - */ - if (start_new) { - if (list_empty(&dep->started_list)) - dwc3_prepare_trbs(dep); - - /* req points to the first request which will be sent */ - req = next_request(&dep->started_list); - } else { - dwc3_prepare_trbs(dep); - - /* - * req points to the first request where HWO changed from 0 to 1 - */ - req = next_request(&dep->started_list); - } + dwc3_prepare_trbs(dep); + req = next_request(&dep->started_list); if (!req) { dep->flags |= DWC3_EP_PENDING_REQUEST; return 0; @@ -1023,7 +1004,7 @@ static int __dwc3_gadget_kick_transfer(struct dwc3_ep *dep, u16 cmd_param, memset(¶ms, 0, sizeof(params)); - if (start_new) { + if (starting) { params.param0 = upper_32_bits(req->trb_dma); params.param1 = lower_32_bits(req->trb_dma); cmd = DWC3_DEPCMD_STARTTRANSFER; @@ -1047,7 +1028,7 @@ static int __dwc3_gadget_kick_transfer(struct dwc3_ep *dep, u16 cmd_param, dep->flags |= DWC3_EP_BUSY; - if (start_new) { + if (starting) { dep->resource_index = dwc3_gadget_ep_get_transfer_index(dwc, dep->number); WARN_ON_ONCE(!dep->resource_index); @@ -1072,7 +1053,7 @@ static void __dwc3_gadget_start_isoc(struct dwc3 *dwc, /* 4 micro frames in the future */ uf = cur_uf + dep->interval * 4; - __dwc3_gadget_kick_transfer(dep, uf, 1); + __dwc3_gadget_kick_transfer(dep, uf); } static void dwc3_gadget_start_isoc(struct dwc3 *dwc, @@ -1141,7 +1122,7 @@ static int __dwc3_gadget_ep_queue(struct dwc3_ep *dep, struct dwc3_request *req) if (!usb_endpoint_xfer_isoc(dep->endpoint.desc) && !usb_endpoint_xfer_int(dep->endpoint.desc) && !(dep->flags & DWC3_EP_BUSY)) { - ret = __dwc3_gadget_kick_transfer(dep, 0, true); + ret = __dwc3_gadget_kick_transfer(dep, 0); goto out; } @@ -1171,7 +1152,7 @@ static int __dwc3_gadget_ep_queue(struct dwc3_ep *dep, struct dwc3_request *req) return 0; } - ret = __dwc3_gadget_kick_transfer(dep, 0, true); + ret = __dwc3_gadget_kick_transfer(dep, 0); if (!ret) dep->flags &= ~DWC3_EP_PENDING_REQUEST; @@ -1187,8 +1168,7 @@ static int __dwc3_gadget_ep_queue(struct dwc3_ep *dep, struct dwc3_request *req) (dep->flags & DWC3_EP_BUSY) && !(dep->flags & DWC3_EP_MISSED_ISOC)) { WARN_ON_ONCE(!dep->resource_index); - ret = __dwc3_gadget_kick_transfer(dep, dep->resource_index, - false); + ret = __dwc3_gadget_kick_transfer(dep, dep->resource_index); goto out; } @@ -1198,7 +1178,7 @@ static int __dwc3_gadget_ep_queue(struct dwc3_ep *dep, struct dwc3_request *req) * handled. */ if (dep->stream_capable) - ret = __dwc3_gadget_kick_transfer(dep, 0, true); + ret = __dwc3_gadget_kick_transfer(dep, 0); out: if (ret && ret != -EBUSY) @@ -2087,7 +2067,7 @@ static void dwc3_endpoint_transfer_complete(struct dwc3 *dwc, if (!usb_endpoint_xfer_isoc(dep->endpoint.desc)) { int ret; - ret = __dwc3_gadget_kick_transfer(dep, 0, is_xfer_complete); + ret = __dwc3_gadget_kick_transfer(dep, 0); if (!ret || ret == -EBUSY) return; } @@ -2138,7 +2118,7 @@ static void dwc3_endpoint_interrupt(struct dwc3 *dwc, dep->name, active ? "Transfer Active" : "Transfer Not Active"); - ret = __dwc3_gadget_kick_transfer(dep, 0, !active); + ret = __dwc3_gadget_kick_transfer(dep, 0); if (!ret || ret == -EBUSY) return; From 6aff483295950abcf3be1a99f56f308bd6a1bab4 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Fri, 13 May 2016 10:07:47 +0300 Subject: [PATCH 009/109] usb: dwc3: gadget: rely on sg_is_last() and list_is_last() sg_is_last() and list_is_last() will encode the required information for the driver to make decisions WRT CHN and LST bits. While at that, also replace '1' with 'true' for consistency. Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/gadget.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index f82882023d54..8ca3855e09a6 100644 --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c @@ -940,10 +940,10 @@ static void dwc3_prepare_trbs(struct dwc3_ep *dep) length = sg_dma_len(s); dma = sg_dma_address(s); - if (i == (request->num_mapped_sgs - 1) || - sg_is_last(s)) { - if (list_empty(&dep->pending_list)) + if (sg_is_last(s)) { + if (list_is_last(&req->list, &dep->pending_list)) last_one = true; + chain = false; } @@ -969,11 +969,11 @@ static void dwc3_prepare_trbs(struct dwc3_ep *dep) trbs_left--; if (!trbs_left) - last_one = 1; + last_one = true; /* Is this the last request? */ if (list_is_last(&req->list, &dep->pending_list)) - last_one = 1; + last_one = true; dwc3_prepare_one_trb(dep, req, dma, length, last_one, false, 0); From b43bba96b9036e42b2c2c71ee15e1f77b0b37aec Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Fri, 13 May 2016 10:11:59 +0300 Subject: [PATCH 010/109] usb: dwc3: gadget: remove udelay(1) when sending ep cmds When we send an endpoint command, we want that to complete as soon as possible, so let's remove the unnecessary udelay(1) call. Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/gadget.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index 8ca3855e09a6..79d1882f1908 100644 --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c @@ -334,8 +334,6 @@ int dwc3_send_gadget_ep_cmd(struct dwc3 *dwc, unsigned ep, ret = -ETIMEDOUT; break; } - - udelay(1); } while (1); if (unlikely(susphy)) { From 6b74289937f624439c87135cfabb3deb2955fb53 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Fri, 13 May 2016 10:19:42 +0300 Subject: [PATCH 011/109] usb: dwc3: gadget: return 0 if we try to Wakeup in superspeed Instead of returning -EINVAL when someone calls __dwc3_gadget_wakeup() in speeds > highspeed, let's return 0. There are no problems for the driver for calling it in superspeed as we cleanly just return. This avoids an annoying WARN_ONCE() always triggering during superspeed enumeration with LPM enabled. Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/gadget.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index 79d1882f1908..4c9fe7b55100 100644 --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c @@ -1431,7 +1431,7 @@ static int __dwc3_gadget_wakeup(struct dwc3 *dwc) if ((speed == DWC3_DSTS_SUPERSPEED) || (speed == DWC3_DSTS_SUPERSPEED_PLUS)) { dwc3_trace(trace_dwc3_gadget, "no wakeup on SuperSpeed\n"); - return -EINVAL; + return 0; } link_state = DWC3_DSTS_USBLNKST(reg); From 5ee85d890f8de5c6f1ab22ba13734a63fdf3ff2d Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Fri, 13 May 2016 12:42:44 +0300 Subject: [PATCH 012/109] usb: dwc3: gadget: split __dwc3_gadget_kick_transfer() To aid code readability, we're gonna split __dwc3_gadget_kick_transfer() into its constituent parts: scatter gather and linear buffers. That way, it's easier to follow the code and focus debug effort when one or the other fails. Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/gadget.c | 122 ++++++++++++++++++++------------------ 1 file changed, 65 insertions(+), 57 deletions(-) diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index 4c9fe7b55100..bff2c35f37a8 100644 --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c @@ -903,6 +903,65 @@ static u32 dwc3_calc_trbs_left(struct dwc3_ep *dep) return dep->trb_dequeue - dep->trb_enqueue; } +static void dwc3_prepare_one_trb_sg(struct dwc3_ep *dep, + struct dwc3_request *req, unsigned int trbs_left) +{ + struct usb_request *request = &req->request; + struct scatterlist *sg = request->sg; + struct scatterlist *s; + unsigned int last = false; + unsigned int length; + dma_addr_t dma; + int i; + + for_each_sg(sg, s, request->num_mapped_sgs, i) { + unsigned chain = true; + + length = sg_dma_len(s); + dma = sg_dma_address(s); + + if (sg_is_last(s)) { + if (list_is_last(&req->list, &dep->pending_list)) + last = true; + + chain = false; + } + + if (!trbs_left) + last = true; + + if (last) + chain = false; + + dwc3_prepare_one_trb(dep, req, dma, length, + last, chain, i); + + if (last) + break; + } +} + +static void dwc3_prepare_one_trb_linear(struct dwc3_ep *dep, + struct dwc3_request *req, unsigned int trbs_left) +{ + unsigned int last = false; + unsigned int length; + dma_addr_t dma; + + dma = req->request.dma; + length = req->request.length; + + if (!trbs_left) + last = true; + + /* Is this the last request? */ + if (list_is_last(&req->list, &dep->pending_list)) + last = true; + + dwc3_prepare_one_trb(dep, req, dma, length, + last, false, 0); +} + /* * dwc3_prepare_trbs - setup TRBs from requests * @dep: endpoint for which requests are being prepared @@ -915,70 +974,19 @@ static void dwc3_prepare_trbs(struct dwc3_ep *dep) { struct dwc3_request *req, *n; u32 trbs_left; - unsigned int last_one = 0; BUILD_BUG_ON_NOT_POWER_OF_2(DWC3_TRB_NUM); trbs_left = dwc3_calc_trbs_left(dep); list_for_each_entry_safe(req, n, &dep->pending_list, list) { - unsigned length; - dma_addr_t dma; - last_one = false; + if (req->request.num_mapped_sgs > 0) + dwc3_prepare_one_trb_sg(dep, req, trbs_left--); + else + dwc3_prepare_one_trb_linear(dep, req, trbs_left--); - if (req->request.num_mapped_sgs > 0) { - struct usb_request *request = &req->request; - struct scatterlist *sg = request->sg; - struct scatterlist *s; - int i; - - for_each_sg(sg, s, request->num_mapped_sgs, i) { - unsigned chain = true; - - length = sg_dma_len(s); - dma = sg_dma_address(s); - - if (sg_is_last(s)) { - if (list_is_last(&req->list, &dep->pending_list)) - last_one = true; - - chain = false; - } - - trbs_left--; - if (!trbs_left) - last_one = true; - - if (last_one) - chain = false; - - dwc3_prepare_one_trb(dep, req, dma, length, - last_one, chain, i); - - if (last_one) - break; - } - - if (last_one) - break; - } else { - dma = req->request.dma; - length = req->request.length; - trbs_left--; - - if (!trbs_left) - last_one = true; - - /* Is this the last request? */ - if (list_is_last(&req->list, &dep->pending_list)) - last_one = true; - - dwc3_prepare_one_trb(dep, req, dma, length, - last_one, false, 0); - - if (last_one) - break; - } + if (!trbs_left) + return; } } From 4e99472bc10bda9906526d725ff6d5f27b4ddca1 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Fri, 13 May 2016 14:09:59 +0300 Subject: [PATCH 013/109] usb: dwc3: gadget: initialize NUMP based on RxFIFO Size Instead of using burst size to configure NUMP, we should be using RxFIFO Size instead. DWC3 is smart enough to know that it shouldn't burst in case burst size is 0. Reported-by: John Youn Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/core.h | 12 +++++++++ drivers/usb/dwc3/gadget.c | 54 +++++++++++++++++++++++++++++++-------- 2 files changed, 55 insertions(+), 11 deletions(-) diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h index c881c8232e21..f4bec0668877 100644 --- a/drivers/usb/dwc3/core.h +++ b/drivers/usb/dwc3/core.h @@ -231,6 +231,14 @@ #define DWC3_GEVNTSIZ_INTMASK (1 << 31) #define DWC3_GEVNTSIZ_SIZE(n) ((n) & 0xffff) +/* Global HWPARAMS0 Register */ +#define DWC3_GHWPARAMS0_USB3_MODE(n) ((n) & 0x3) +#define DWC3_GHWPARAMS0_MBUS_TYPE(n) (((n) >> 3) & 0x7) +#define DWC3_GHWPARAMS0_SBUS_TYPE(n) (((n) >> 6) & 0x3) +#define DWC3_GHWPARAMS0_MDWIDTH(n) (((n) >> 8) & 0xff) +#define DWC3_GHWPARAMS0_SDWIDTH(n) (((n) >> 16) & 0xff) +#define DWC3_GHWPARAMS0_AWIDTH(n) (((n) >> 24) & 0xff) + /* Global HWPARAMS1 Register */ #define DWC3_GHWPARAMS1_EN_PWROPT(n) (((n) & (3 << 24)) >> 24) #define DWC3_GHWPARAMS1_EN_PWROPT_NO 0 @@ -260,6 +268,10 @@ /* Global HWPARAMS6 Register */ #define DWC3_GHWPARAMS6_EN_FPGA (1 << 7) +/* Global HWPARAMS7 Register */ +#define DWC3_GHWPARAMS7_RAM1_DEPTH(n) ((n) & 0xffff) +#define DWC3_GHWPARAMS7_RAM2_DEPTH(n) (((n) >> 16) & 0xffff) + /* Global Frame Length Adjustment Register */ #define DWC3_GFLADJ_30MHZ_SDBND_SEL (1 << 7) #define DWC3_GFLADJ_30MHZ_MASK 0x3f diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index bff2c35f37a8..eb28326a633d 100644 --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c @@ -485,17 +485,6 @@ static int dwc3_gadget_set_ep_config(struct dwc3 *dwc, struct dwc3_ep *dep, /* Burst size is only needed in SuperSpeed mode */ if (dwc->gadget.speed >= USB_SPEED_SUPER) { u32 burst = dep->endpoint.maxburst; - u32 nump; - u32 reg; - - /* update NumP */ - reg = dwc3_readl(dwc->regs, DWC3_DCFG); - nump = DWC3_DCFG_NUMP(reg); - nump = max(nump, burst); - reg &= ~DWC3_DCFG_NUMP_MASK; - reg |= nump << DWC3_DCFG_NUMP_SHIFT; - dwc3_writel(dwc->regs, DWC3_DCFG, reg); - params.param0 |= DWC3_DEPCFG_BURST_SIZE(burst - 1); } @@ -1610,6 +1599,47 @@ static void dwc3_gadget_disable_irq(struct dwc3 *dwc) static irqreturn_t dwc3_interrupt(int irq, void *_dwc); static irqreturn_t dwc3_thread_interrupt(int irq, void *_dwc); +/** + * dwc3_gadget_setup_nump - Calculate and initialize NUMP field of DCFG + * dwc: pointer to our context structure + * + * The following looks like complex but it's actually very simple. In order to + * calculate the number of packets we can burst at once on OUT transfers, we're + * gonna use RxFIFO size. + * + * To calculate RxFIFO size we need two numbers: + * MDWIDTH = size, in bits, of the internal memory bus + * RAM2_DEPTH = depth, in MDWIDTH, of internal RAM2 (where RxFIFO sits) + * + * Given these two numbers, the formula is simple: + * + * RxFIFO Size = (RAM2_DEPTH * MDWIDTH / 8) - 24 - 16; + * + * 24 bytes is for 3x SETUP packets + * 16 bytes is a clock domain crossing tolerance + * + * Given RxFIFO Size, NUMP = RxFIFOSize / 1024; + */ +static void dwc3_gadget_setup_nump(struct dwc3 *dwc) +{ + u32 ram2_depth; + u32 mdwidth; + u32 nump; + u32 reg; + + ram2_depth = DWC3_GHWPARAMS7_RAM2_DEPTH(dwc->hwparams.hwparams7); + mdwidth = DWC3_GHWPARAMS0_MDWIDTH(dwc->hwparams.hwparams0); + + nump = ((ram2_depth * mdwidth / 8) - 24 - 16) / 1024; + nump = min_t(u32, nump, 16); + + /* update NumP */ + reg = dwc3_readl(dwc->regs, DWC3_DCFG); + reg &= ~DWC3_DCFG_NUMP_MASK; + reg |= nump << DWC3_DCFG_NUMP_SHIFT; + dwc3_writel(dwc->regs, DWC3_DCFG, reg); +} + static int __dwc3_gadget_start(struct dwc3 *dwc) { struct dwc3_ep *dep; @@ -1670,6 +1700,8 @@ static int __dwc3_gadget_start(struct dwc3 *dwc) reg &= ~DWC3_GRXTHRCFG_PKTCNTSEL; dwc3_writel(dwc->regs, DWC3_GRXTHRCFG, reg); + dwc3_gadget_setup_nump(dwc); + /* Start with SuperSpeed Default */ dwc3_gadget_ep0_desc.wMaxPacketSize = cpu_to_le16(512); From 2cd4718d0bbe1906fcf517f0b254fbd7c072383a Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Tue, 12 Apr 2016 16:42:43 +0300 Subject: [PATCH 014/109] usb: dwc3: gadget: pass dep as argument to endpoint command In all call sites of dwc3_send_gadget_ep_cmd() we already had a valid dep pointer, so instead of passing dwc and dep->number, which would be used to fetch the same pointer we already had, just pass dep directly. In other words, we're changing: struct dwc3_ep *dep = dwc[dep->number]; to just passing struct dwc3_ep *dep as argument. Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/core.h | 8 ++++---- drivers/usb/dwc3/ep0.c | 5 ++--- drivers/usb/dwc3/gadget.c | 28 +++++++++++++++------------- 3 files changed, 21 insertions(+), 20 deletions(-) diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h index f4bec0668877..27afe14d7d26 100644 --- a/drivers/usb/dwc3/core.h +++ b/drivers/usb/dwc3/core.h @@ -1105,8 +1105,8 @@ void dwc3_gadget_exit(struct dwc3 *dwc); int dwc3_gadget_set_test_mode(struct dwc3 *dwc, int mode); int dwc3_gadget_get_link_state(struct dwc3 *dwc); int dwc3_gadget_set_link_state(struct dwc3 *dwc, enum dwc3_link_state state); -int dwc3_send_gadget_ep_cmd(struct dwc3 *dwc, unsigned ep, - unsigned cmd, struct dwc3_gadget_ep_cmd_params *params); +int dwc3_send_gadget_ep_cmd(struct dwc3_ep *dep, unsigned cmd, + struct dwc3_gadget_ep_cmd_params *params); int dwc3_send_gadget_generic_command(struct dwc3 *dwc, unsigned cmd, u32 param); #else static inline int dwc3_gadget_init(struct dwc3 *dwc) @@ -1121,8 +1121,8 @@ static inline int dwc3_gadget_set_link_state(struct dwc3 *dwc, enum dwc3_link_state state) { return 0; } -static inline int dwc3_send_gadget_ep_cmd(struct dwc3 *dwc, unsigned ep, - unsigned cmd, struct dwc3_gadget_ep_cmd_params *params) +static inline int dwc3_send_gadget_ep_cmd(struct dwc3_ep *dep, unsigned cmd, + struct dwc3_gadget_ep_cmd_params *params) { return 0; } static inline int dwc3_send_gadget_generic_command(struct dwc3 *dwc, int cmd, u32 param) diff --git a/drivers/usb/dwc3/ep0.c b/drivers/usb/dwc3/ep0.c index 51b52a79dfec..e08150a7cf8f 100644 --- a/drivers/usb/dwc3/ep0.c +++ b/drivers/usb/dwc3/ep0.c @@ -98,8 +98,7 @@ static int dwc3_ep0_start_trans(struct dwc3 *dwc, u8 epnum, dma_addr_t buf_dma, trace_dwc3_prepare_trb(dep, trb); - ret = dwc3_send_gadget_ep_cmd(dwc, dep->number, - DWC3_DEPCMD_STARTTRANSFER, ¶ms); + ret = dwc3_send_gadget_ep_cmd(dep, DWC3_DEPCMD_STARTTRANSFER, ¶ms); if (ret < 0) { dwc3_trace(trace_dwc3_ep0, "%s STARTTRANSFER failed", dep->name); @@ -1058,7 +1057,7 @@ static void dwc3_ep0_end_control_data(struct dwc3 *dwc, struct dwc3_ep *dep) cmd |= DWC3_DEPCMD_CMDIOC; cmd |= DWC3_DEPCMD_PARAM(dep->resource_index); memset(¶ms, 0, sizeof(params)); - ret = dwc3_send_gadget_ep_cmd(dwc, dep->number, cmd, ¶ms); + ret = dwc3_send_gadget_ep_cmd(dep, cmd, ¶ms); WARN_ON_ONCE(ret); dep->resource_index = 0; } diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index eb28326a633d..5225f0e52577 100644 --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c @@ -238,16 +238,18 @@ int dwc3_send_gadget_generic_command(struct dwc3 *dwc, unsigned cmd, u32 param) static int __dwc3_gadget_wakeup(struct dwc3 *dwc); -int dwc3_send_gadget_ep_cmd(struct dwc3 *dwc, unsigned ep, - unsigned cmd, struct dwc3_gadget_ep_cmd_params *params) +int dwc3_send_gadget_ep_cmd(struct dwc3_ep *dep, unsigned cmd, + struct dwc3_gadget_ep_cmd_params *params) { - struct dwc3_ep *dep = dwc->eps[ep]; + struct dwc3 *dwc = dep->dwc; u32 timeout = 500; u32 reg; int susphy = false; int ret = -EINVAL; + unsigned ep = dep->number; + trace_dwc3_gadget_ep_cmd(dep, cmd, params); /* @@ -364,7 +366,7 @@ static int dwc3_send_clear_stall_ep_cmd(struct dwc3_ep *dep) memset(¶ms, 0, sizeof(params)); - return dwc3_send_gadget_ep_cmd(dwc, dep->number, cmd, ¶ms); + return dwc3_send_gadget_ep_cmd(dep, cmd, ¶ms); } static dma_addr_t dwc3_trb_dma_offset(struct dwc3_ep *dep, @@ -452,7 +454,7 @@ static int dwc3_gadget_start_config(struct dwc3 *dwc, struct dwc3_ep *dep) memset(¶ms, 0x00, sizeof(params)); cmd = DWC3_DEPCMD_DEPSTARTCFG; - ret = dwc3_send_gadget_ep_cmd(dwc, 0, cmd, ¶ms); + ret = dwc3_send_gadget_ep_cmd(dep, cmd, ¶ms); if (ret) return ret; @@ -528,8 +530,7 @@ static int dwc3_gadget_set_ep_config(struct dwc3 *dwc, struct dwc3_ep *dep, dep->interval = 1 << (desc->bInterval - 1); } - return dwc3_send_gadget_ep_cmd(dwc, dep->number, - DWC3_DEPCMD_SETEPCONFIG, ¶ms); + return dwc3_send_gadget_ep_cmd(dep, DWC3_DEPCMD_SETEPCONFIG, ¶ms); } static int dwc3_gadget_set_xfer_resource(struct dwc3 *dwc, struct dwc3_ep *dep) @@ -540,8 +541,8 @@ static int dwc3_gadget_set_xfer_resource(struct dwc3 *dwc, struct dwc3_ep *dep) params.param0 = DWC3_DEPXFERCFG_NUM_XFER_RES(1); - return dwc3_send_gadget_ep_cmd(dwc, dep->number, - DWC3_DEPCMD_SETTRANSFRESOURCE, ¶ms); + return dwc3_send_gadget_ep_cmd(dep, DWC3_DEPCMD_SETTRANSFRESOURCE, + ¶ms); } /** @@ -1008,7 +1009,7 @@ static int __dwc3_gadget_kick_transfer(struct dwc3_ep *dep, u16 cmd_param) } cmd |= DWC3_DEPCMD_PARAM(cmd_param); - ret = dwc3_send_gadget_ep_cmd(dwc, dep->number, cmd, ¶ms); + ret = dwc3_send_gadget_ep_cmd(dep, cmd, ¶ms); if (ret < 0) { /* * FIXME we need to iterate over the list of requests @@ -1311,14 +1312,15 @@ int __dwc3_gadget_ep_set_halt(struct dwc3_ep *dep, int value, int protocol) return -EAGAIN; } - ret = dwc3_send_gadget_ep_cmd(dwc, dep->number, - DWC3_DEPCMD_SETSTALL, ¶ms); + ret = dwc3_send_gadget_ep_cmd(dep, DWC3_DEPCMD_SETSTALL, + ¶ms); if (ret) dev_err(dwc->dev, "failed to set STALL on %s\n", dep->name); else dep->flags |= DWC3_EP_STALL; } else { + ret = dwc3_send_clear_stall_ep_cmd(dep); if (ret) dev_err(dwc->dev, "failed to clear STALL on %s\n", @@ -2271,7 +2273,7 @@ static void dwc3_stop_active_transfer(struct dwc3 *dwc, u32 epnum, bool force) cmd |= DWC3_DEPCMD_CMDIOC; cmd |= DWC3_DEPCMD_PARAM(dep->resource_index); memset(¶ms, 0, sizeof(params)); - ret = dwc3_send_gadget_ep_cmd(dwc, dep->number, cmd, ¶ms); + ret = dwc3_send_gadget_ep_cmd(dep, cmd, ¶ms); WARN_ON_ONCE(ret); dep->resource_index = 0; dep->flags &= ~DWC3_EP_BUSY; From 2eb8801650b315394ca376a56be2971c867aa9ec Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Tue, 12 Apr 2016 16:53:39 +0300 Subject: [PATCH 015/109] usb: dwc3: gadget: add a pointer to endpoint registers By adding a pointer to endpoint registers' base address, we can avoid using our controller-wide struct dwc3 pointer for everything. At some point this will allow us to have per-endpoint locks which will, in turn, let us queue requests to separate endpoints in parallel. Because of this change our debugfs interface and io accessors need to be changed accordingly. Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/core.h | 13 ++- drivers/usb/dwc3/debugfs.c | 190 +++++++++++-------------------------- drivers/usb/dwc3/ep0.c | 4 +- drivers/usb/dwc3/gadget.c | 16 ++-- drivers/usb/dwc3/gadget.h | 4 +- drivers/usb/dwc3/io.h | 7 +- 6 files changed, 78 insertions(+), 156 deletions(-) diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h index 27afe14d7d26..02990ab6d7f6 100644 --- a/drivers/usb/dwc3/core.h +++ b/drivers/usb/dwc3/core.h @@ -138,10 +138,12 @@ #define DWC3_DGCMDPAR 0xc710 #define DWC3_DGCMD 0xc714 #define DWC3_DALEPENA 0xc720 -#define DWC3_DEPCMDPAR2(n) (0xc800 + (n * 0x10)) -#define DWC3_DEPCMDPAR1(n) (0xc804 + (n * 0x10)) -#define DWC3_DEPCMDPAR0(n) (0xc808 + (n * 0x10)) -#define DWC3_DEPCMD(n) (0xc80c + (n * 0x10)) + +#define DWC3_DEP_BASE(n) (0xc800 + (n * 0x10)) +#define DWC3_DEPCMDPAR2 0x00 +#define DWC3_DEPCMDPAR1 0x04 +#define DWC3_DEPCMDPAR0 0x08 +#define DWC3_DEPCMD 0x0c /* OTG Registers */ #define DWC3_OCFG 0xcc00 @@ -480,6 +482,7 @@ struct dwc3_event_buffer { * @endpoint: usb endpoint * @pending_list: list of pending requests for this endpoint * @started_list: list of started requests on this endpoint + * @regs: pointer to first endpoint register * @trb_pool: array of transaction buffers * @trb_pool_dma: dma address of @trb_pool * @trb_enqueue: enqueue 'pointer' into TRB array @@ -501,6 +504,8 @@ struct dwc3_ep { struct list_head pending_list; struct list_head started_list; + void __iomem *regs; + struct dwc3_trb *trb_pool; dma_addr_t trb_pool_dma; const struct usb_ss_ep_comp_descriptor *comp_desc; diff --git a/drivers/usb/dwc3/debugfs.c b/drivers/usb/dwc3/debugfs.c index b1dd3c6d7ef7..89c26e09870c 100644 --- a/drivers/usb/dwc3/debugfs.c +++ b/drivers/usb/dwc3/debugfs.c @@ -36,9 +36,32 @@ #define dump_register(nm) \ { \ .name = __stringify(nm), \ - .offset = DWC3_ ##nm - DWC3_GLOBALS_REGS_START, \ + .offset = DWC3_ ##nm, \ } +#define dump_ep_register_set(n) \ + { \ + .name = "DEPCMDPAR2("__stringify(n)")", \ + .offset = DWC3_DEP_BASE(n) + \ + DWC3_DEPCMDPAR2, \ + }, \ + { \ + .name = "DEPCMDPAR1("__stringify(n)")", \ + .offset = DWC3_DEP_BASE(n) + \ + DWC3_DEPCMDPAR1, \ + }, \ + { \ + .name = "DEPCMDPAR0("__stringify(n)")", \ + .offset = DWC3_DEP_BASE(n) + \ + DWC3_DEPCMDPAR0, \ + }, \ + { \ + .name = "DEPCMD("__stringify(n)")", \ + .offset = DWC3_DEP_BASE(n) + \ + DWC3_DEPCMD, \ + } + + static const struct debugfs_reg32 dwc3_regs[] = { dump_register(GSBUSCFG0), dump_register(GSBUSCFG1), @@ -218,137 +241,38 @@ static const struct debugfs_reg32 dwc3_regs[] = { dump_register(DGCMD), dump_register(DALEPENA), - dump_register(DEPCMDPAR2(0)), - dump_register(DEPCMDPAR2(1)), - dump_register(DEPCMDPAR2(2)), - dump_register(DEPCMDPAR2(3)), - dump_register(DEPCMDPAR2(4)), - dump_register(DEPCMDPAR2(5)), - dump_register(DEPCMDPAR2(6)), - dump_register(DEPCMDPAR2(7)), - dump_register(DEPCMDPAR2(8)), - dump_register(DEPCMDPAR2(9)), - dump_register(DEPCMDPAR2(10)), - dump_register(DEPCMDPAR2(11)), - dump_register(DEPCMDPAR2(12)), - dump_register(DEPCMDPAR2(13)), - dump_register(DEPCMDPAR2(14)), - dump_register(DEPCMDPAR2(15)), - dump_register(DEPCMDPAR2(16)), - dump_register(DEPCMDPAR2(17)), - dump_register(DEPCMDPAR2(18)), - dump_register(DEPCMDPAR2(19)), - dump_register(DEPCMDPAR2(20)), - dump_register(DEPCMDPAR2(21)), - dump_register(DEPCMDPAR2(22)), - dump_register(DEPCMDPAR2(23)), - dump_register(DEPCMDPAR2(24)), - dump_register(DEPCMDPAR2(25)), - dump_register(DEPCMDPAR2(26)), - dump_register(DEPCMDPAR2(27)), - dump_register(DEPCMDPAR2(28)), - dump_register(DEPCMDPAR2(29)), - dump_register(DEPCMDPAR2(30)), - dump_register(DEPCMDPAR2(31)), - - dump_register(DEPCMDPAR1(0)), - dump_register(DEPCMDPAR1(1)), - dump_register(DEPCMDPAR1(2)), - dump_register(DEPCMDPAR1(3)), - dump_register(DEPCMDPAR1(4)), - dump_register(DEPCMDPAR1(5)), - dump_register(DEPCMDPAR1(6)), - dump_register(DEPCMDPAR1(7)), - dump_register(DEPCMDPAR1(8)), - dump_register(DEPCMDPAR1(9)), - dump_register(DEPCMDPAR1(10)), - dump_register(DEPCMDPAR1(11)), - dump_register(DEPCMDPAR1(12)), - dump_register(DEPCMDPAR1(13)), - dump_register(DEPCMDPAR1(14)), - dump_register(DEPCMDPAR1(15)), - dump_register(DEPCMDPAR1(16)), - dump_register(DEPCMDPAR1(17)), - dump_register(DEPCMDPAR1(18)), - dump_register(DEPCMDPAR1(19)), - dump_register(DEPCMDPAR1(20)), - dump_register(DEPCMDPAR1(21)), - dump_register(DEPCMDPAR1(22)), - dump_register(DEPCMDPAR1(23)), - dump_register(DEPCMDPAR1(24)), - dump_register(DEPCMDPAR1(25)), - dump_register(DEPCMDPAR1(26)), - dump_register(DEPCMDPAR1(27)), - dump_register(DEPCMDPAR1(28)), - dump_register(DEPCMDPAR1(29)), - dump_register(DEPCMDPAR1(30)), - dump_register(DEPCMDPAR1(31)), - - dump_register(DEPCMDPAR0(0)), - dump_register(DEPCMDPAR0(1)), - dump_register(DEPCMDPAR0(2)), - dump_register(DEPCMDPAR0(3)), - dump_register(DEPCMDPAR0(4)), - dump_register(DEPCMDPAR0(5)), - dump_register(DEPCMDPAR0(6)), - dump_register(DEPCMDPAR0(7)), - dump_register(DEPCMDPAR0(8)), - dump_register(DEPCMDPAR0(9)), - dump_register(DEPCMDPAR0(10)), - dump_register(DEPCMDPAR0(11)), - dump_register(DEPCMDPAR0(12)), - dump_register(DEPCMDPAR0(13)), - dump_register(DEPCMDPAR0(14)), - dump_register(DEPCMDPAR0(15)), - dump_register(DEPCMDPAR0(16)), - dump_register(DEPCMDPAR0(17)), - dump_register(DEPCMDPAR0(18)), - dump_register(DEPCMDPAR0(19)), - dump_register(DEPCMDPAR0(20)), - dump_register(DEPCMDPAR0(21)), - dump_register(DEPCMDPAR0(22)), - dump_register(DEPCMDPAR0(23)), - dump_register(DEPCMDPAR0(24)), - dump_register(DEPCMDPAR0(25)), - dump_register(DEPCMDPAR0(26)), - dump_register(DEPCMDPAR0(27)), - dump_register(DEPCMDPAR0(28)), - dump_register(DEPCMDPAR0(29)), - dump_register(DEPCMDPAR0(30)), - dump_register(DEPCMDPAR0(31)), - - dump_register(DEPCMD(0)), - dump_register(DEPCMD(1)), - dump_register(DEPCMD(2)), - dump_register(DEPCMD(3)), - dump_register(DEPCMD(4)), - dump_register(DEPCMD(5)), - dump_register(DEPCMD(6)), - dump_register(DEPCMD(7)), - dump_register(DEPCMD(8)), - dump_register(DEPCMD(9)), - dump_register(DEPCMD(10)), - dump_register(DEPCMD(11)), - dump_register(DEPCMD(12)), - dump_register(DEPCMD(13)), - dump_register(DEPCMD(14)), - dump_register(DEPCMD(15)), - dump_register(DEPCMD(16)), - dump_register(DEPCMD(17)), - dump_register(DEPCMD(18)), - dump_register(DEPCMD(19)), - dump_register(DEPCMD(20)), - dump_register(DEPCMD(21)), - dump_register(DEPCMD(22)), - dump_register(DEPCMD(23)), - dump_register(DEPCMD(24)), - dump_register(DEPCMD(25)), - dump_register(DEPCMD(26)), - dump_register(DEPCMD(27)), - dump_register(DEPCMD(28)), - dump_register(DEPCMD(29)), - dump_register(DEPCMD(30)), - dump_register(DEPCMD(31)), + dump_ep_register_set(0), + dump_ep_register_set(1), + dump_ep_register_set(2), + dump_ep_register_set(3), + dump_ep_register_set(4), + dump_ep_register_set(5), + dump_ep_register_set(6), + dump_ep_register_set(7), + dump_ep_register_set(8), + dump_ep_register_set(9), + dump_ep_register_set(10), + dump_ep_register_set(11), + dump_ep_register_set(12), + dump_ep_register_set(13), + dump_ep_register_set(14), + dump_ep_register_set(15), + dump_ep_register_set(16), + dump_ep_register_set(17), + dump_ep_register_set(18), + dump_ep_register_set(19), + dump_ep_register_set(20), + dump_ep_register_set(21), + dump_ep_register_set(22), + dump_ep_register_set(23), + dump_ep_register_set(24), + dump_ep_register_set(25), + dump_ep_register_set(26), + dump_ep_register_set(27), + dump_ep_register_set(28), + dump_ep_register_set(29), + dump_ep_register_set(30), + dump_ep_register_set(31), dump_register(OCFG), dump_register(OCTL), @@ -939,7 +863,7 @@ void dwc3_debugfs_init(struct dwc3 *dwc) dwc->regset->regs = dwc3_regs; dwc->regset->nregs = ARRAY_SIZE(dwc3_regs); - dwc->regset->base = dwc->regs; + dwc->regset->base = dwc->regs - DWC3_GLOBALS_REGS_START; file = debugfs_create_regset32("regdump", S_IRUGO, root, dwc->regset); if (!file) diff --git a/drivers/usb/dwc3/ep0.c b/drivers/usb/dwc3/ep0.c index e08150a7cf8f..3103a8f4b7e5 100644 --- a/drivers/usb/dwc3/ep0.c +++ b/drivers/usb/dwc3/ep0.c @@ -106,9 +106,7 @@ static int dwc3_ep0_start_trans(struct dwc3 *dwc, u8 epnum, dma_addr_t buf_dma, } dep->flags |= DWC3_EP_BUSY; - dep->resource_index = dwc3_gadget_ep_get_transfer_index(dwc, - dep->number); - + dep->resource_index = dwc3_gadget_ep_get_transfer_index(dep); dwc->ep0_next_event = DWC3_EP0_COMPLETE; return 0; diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index 5225f0e52577..7ba4287f5c71 100644 --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c @@ -248,8 +248,6 @@ int dwc3_send_gadget_ep_cmd(struct dwc3_ep *dep, unsigned cmd, int susphy = false; int ret = -EINVAL; - unsigned ep = dep->number; - trace_dwc3_gadget_ep_cmd(dep, cmd, params); /* @@ -281,13 +279,13 @@ int dwc3_send_gadget_ep_cmd(struct dwc3_ep *dep, unsigned cmd, } } - dwc3_writel(dwc->regs, DWC3_DEPCMDPAR0(ep), params->param0); - dwc3_writel(dwc->regs, DWC3_DEPCMDPAR1(ep), params->param1); - dwc3_writel(dwc->regs, DWC3_DEPCMDPAR2(ep), params->param2); + dwc3_writel(dep->regs, DWC3_DEPCMDPAR0, params->param0); + dwc3_writel(dep->regs, DWC3_DEPCMDPAR1, params->param1); + dwc3_writel(dep->regs, DWC3_DEPCMDPAR2, params->param2); - dwc3_writel(dwc->regs, DWC3_DEPCMD(ep), cmd | DWC3_DEPCMD_CMDACT); + dwc3_writel(dep->regs, DWC3_DEPCMD, cmd | DWC3_DEPCMD_CMDACT); do { - reg = dwc3_readl(dwc->regs, DWC3_DEPCMD(ep)); + reg = dwc3_readl(dep->regs, DWC3_DEPCMD); if (!(reg & DWC3_DEPCMD_CMDACT)) { int cmd_status = DWC3_DEPCMD_STATUS(reg); @@ -1025,8 +1023,7 @@ static int __dwc3_gadget_kick_transfer(struct dwc3_ep *dep, u16 cmd_param) dep->flags |= DWC3_EP_BUSY; if (starting) { - dep->resource_index = dwc3_gadget_ep_get_transfer_index(dwc, - dep->number); + dep->resource_index = dwc3_gadget_ep_get_transfer_index(dep); WARN_ON_ONCE(!dep->resource_index); } @@ -1830,6 +1827,7 @@ static int dwc3_gadget_init_hw_endpoints(struct dwc3 *dwc, dep->dwc = dwc; dep->number = epnum; dep->direction = !!direction; + dep->regs = dwc->regs + DWC3_DEP_BASE(epnum); dwc->eps[epnum] = dep; snprintf(dep->name, sizeof(dep->name), "ep%d%s", epnum >> 1, diff --git a/drivers/usb/dwc3/gadget.h b/drivers/usb/dwc3/gadget.h index f21c0fccbebd..e4a1d974a5ae 100644 --- a/drivers/usb/dwc3/gadget.h +++ b/drivers/usb/dwc3/gadget.h @@ -95,11 +95,11 @@ int __dwc3_gadget_ep_set_halt(struct dwc3_ep *dep, int value, int protocol); * * Caller should take care of locking */ -static inline u32 dwc3_gadget_ep_get_transfer_index(struct dwc3 *dwc, u8 number) +static inline u32 dwc3_gadget_ep_get_transfer_index(struct dwc3_ep *dep) { u32 res_id; - res_id = dwc3_readl(dwc->regs, DWC3_DEPCMD(number)); + res_id = dwc3_readl(dep->regs, DWC3_DEPCMD); return DWC3_DEPCMD_GET_RSC_IDX(res_id); } diff --git a/drivers/usb/dwc3/io.h b/drivers/usb/dwc3/io.h index 6a79c8e66bbc..a06f9a8fecc7 100644 --- a/drivers/usb/dwc3/io.h +++ b/drivers/usb/dwc3/io.h @@ -26,7 +26,6 @@ static inline u32 dwc3_readl(void __iomem *base, u32 offset) { - u32 offs = offset - DWC3_GLOBALS_REGS_START; u32 value; /* @@ -34,7 +33,7 @@ static inline u32 dwc3_readl(void __iomem *base, u32 offset) * space, see dwc3_probe in core.c. * However, the offsets are given starting from xHCI address space. */ - value = readl(base + offs); + value = readl(base + offset - DWC3_GLOBALS_REGS_START); /* * When tracing we want to make it easy to find the correct address on @@ -49,14 +48,12 @@ static inline u32 dwc3_readl(void __iomem *base, u32 offset) static inline void dwc3_writel(void __iomem *base, u32 offset, u32 value) { - u32 offs = offset - DWC3_GLOBALS_REGS_START; - /* * We requested the mem region starting from the Globals address * space, see dwc3_probe in core.c. * However, the offsets are given starting from xHCI address space. */ - writel(value, base + offs); + writel(value, base + offset - DWC3_GLOBALS_REGS_START); /* * When tracing we want to make it easy to find the correct address on From bcdb3272e889f4d2a5c8efc5e12b0fb2dcbf75e9 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Mon, 16 May 2016 10:42:23 +0300 Subject: [PATCH 016/109] usb: dwc3: core: move fladj to dwc3 structure this patch is in preparation for some further re-factoring in dwc3 initialization. No functional changes. Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/core.c | 16 +++++++--------- drivers/usb/dwc3/core.h | 2 ++ 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c index 245f4ff6ae16..1f4ac355f384 100644 --- a/drivers/usb/dwc3/core.c +++ b/drivers/usb/dwc3/core.c @@ -149,9 +149,8 @@ static int dwc3_soft_reset(struct dwc3 *dwc) /* * dwc3_frame_length_adjustment - Adjusts frame length if required * @dwc3: Pointer to our controller context structure - * @fladj: Value of GFLADJ_30MHZ to adjust frame length */ -static void dwc3_frame_length_adjustment(struct dwc3 *dwc, u32 fladj) +static void dwc3_frame_length_adjustment(struct dwc3 *dwc) { u32 reg; u32 dft; @@ -159,15 +158,15 @@ static void dwc3_frame_length_adjustment(struct dwc3 *dwc, u32 fladj) if (dwc->revision < DWC3_REVISION_250A) return; - if (fladj == 0) + if (dwc->fladj == 0) return; reg = dwc3_readl(dwc->regs, DWC3_GFLADJ); dft = reg & DWC3_GFLADJ_30MHZ_MASK; - if (!dev_WARN_ONCE(dwc->dev, dft == fladj, + if (!dev_WARN_ONCE(dwc->dev, dft == dwc->fladj, "request value same as default, ignoring\n")) { reg &= ~DWC3_GFLADJ_30MHZ_MASK; - reg |= DWC3_GFLADJ_30MHZ_SDBND_SEL | fladj; + reg |= DWC3_GFLADJ_30MHZ_SDBND_SEL | dwc->fladj; dwc3_writel(dwc->regs, DWC3_GFLADJ, reg); } } @@ -799,7 +798,6 @@ static int dwc3_probe(struct platform_device *pdev) u8 lpm_nyet_threshold; u8 tx_de_emphasis; u8 hird_threshold; - u32 fladj = 0; int ret; @@ -909,7 +907,7 @@ static int dwc3_probe(struct platform_device *pdev) device_property_read_string(dev, "snps,hsphy_interface", &dwc->hsphy_interface); device_property_read_u32(dev, "snps,quirk-frame-length-adjustment", - &fladj); + &dwc->fladj); if (pdata) { dwc->maximum_speed = pdata->maximum_speed; @@ -941,7 +939,7 @@ static int dwc3_probe(struct platform_device *pdev) tx_de_emphasis = pdata->tx_de_emphasis; dwc->hsphy_interface = pdata->hsphy_interface; - fladj = pdata->fladj_value; + dwc->fladj = pdata->fladj_value; } dwc->lpm_nyet_threshold = lpm_nyet_threshold; @@ -1022,7 +1020,7 @@ static int dwc3_probe(struct platform_device *pdev) } /* Adjust Frame Length */ - dwc3_frame_length_adjustment(dwc, fladj); + dwc3_frame_length_adjustment(dwc); usb_phy_set_suspend(dwc->usb2_phy, 0); usb_phy_set_suspend(dwc->usb3_phy, 0); diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h index 02990ab6d7f6..e01f6371d118 100644 --- a/drivers/usb/dwc3/core.h +++ b/drivers/usb/dwc3/core.h @@ -729,6 +729,7 @@ struct dwc3_scratchpad_array { * @gadget_driver: pointer to the gadget driver * @regs: base address for our registers * @regs_size: address space size + * @fladj: frame length adjustment * @nr_scratch: number of scratch buffers * @u1u2: only used on revisions <1.83a for workaround * @maximum_speed: maximum speed requested (mainly for testing purposes) @@ -838,6 +839,7 @@ struct dwc3 { /* used for suspend/resume */ u32 gctl; + u32 fladj; u32 nr_scratch; u32 u1u2; u32 maximum_speed; From c499ff71ff2a281366c6ec7a904c547d806cbcd1 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Mon, 16 May 2016 10:49:01 +0300 Subject: [PATCH 017/109] usb: dwc3: core: re-factor init and exit paths The idea of this patch is for dwc3_core_init() to abstract all the details about how to initialize dwc3 and dwc3_core_exit() to do the same for teardown. With this, we can simplify suspend/resume operations by a large margin and always know that we're going to start dwc3 from a known starting point. Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/core.c | 118 ++++++++++++++++++++-------------------- 1 file changed, 60 insertions(+), 58 deletions(-) diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c index 1f4ac355f384..cbdefbb3d302 100644 --- a/drivers/usb/dwc3/core.c +++ b/drivers/usb/dwc3/core.c @@ -506,6 +506,21 @@ static int dwc3_phy_setup(struct dwc3 *dwc) return 0; } +static void dwc3_core_exit(struct dwc3 *dwc) +{ + dwc3_event_buffers_cleanup(dwc); + + usb_phy_shutdown(dwc->usb2_phy); + usb_phy_shutdown(dwc->usb3_phy); + phy_exit(dwc->usb2_generic_phy); + phy_exit(dwc->usb3_generic_phy); + + usb_phy_set_suspend(dwc->usb2_phy, 1); + usb_phy_set_suspend(dwc->usb3_phy, 1); + phy_power_off(dwc->usb2_generic_phy); + phy_power_off(dwc->usb3_generic_phy); +} + /** * dwc3_core_init - Low-level initialization of DWC3 Core * @dwc: Pointer to our controller context structure @@ -555,6 +570,10 @@ static int dwc3_core_init(struct dwc3 *dwc) if (ret) goto err0; + ret = dwc3_phy_setup(dwc); + if (ret) + goto err0; + reg = dwc3_readl(dwc->regs, DWC3_GCTL); reg &= ~DWC3_GCTL_SCALEDOWN_MASK; @@ -621,22 +640,45 @@ static int dwc3_core_init(struct dwc3 *dwc) if (dwc->revision < DWC3_REVISION_190A) reg |= DWC3_GCTL_U2RSTECN; - dwc3_core_num_eps(dwc); - dwc3_writel(dwc->regs, DWC3_GCTL, reg); - ret = dwc3_alloc_scratch_buffers(dwc); - if (ret) - goto err1; + dwc3_core_num_eps(dwc); ret = dwc3_setup_scratch_buffers(dwc); if (ret) + goto err1; + + /* Adjust Frame Length */ + dwc3_frame_length_adjustment(dwc); + + usb_phy_set_suspend(dwc->usb2_phy, 0); + usb_phy_set_suspend(dwc->usb3_phy, 0); + ret = phy_power_on(dwc->usb2_generic_phy); + if (ret < 0) goto err2; + ret = phy_power_on(dwc->usb3_generic_phy); + if (ret < 0) + goto err3; + + ret = dwc3_event_buffers_setup(dwc); + if (ret) { + dev_err(dwc->dev, "failed to setup event buffers\n"); + goto err4; + } + return 0; +err4: + phy_power_off(dwc->usb2_generic_phy); + +err3: + phy_power_off(dwc->usb3_generic_phy); + err2: - dwc3_free_scratch_buffers(dwc); + usb_phy_set_suspend(dwc->usb2_phy, 1); + usb_phy_set_suspend(dwc->usb3_phy, 1); + dwc3_core_exit(dwc); err1: usb_phy_shutdown(dwc->usb2_phy); @@ -648,15 +690,6 @@ err0: return ret; } -static void dwc3_core_exit(struct dwc3 *dwc) -{ - dwc3_free_scratch_buffers(dwc); - usb_phy_shutdown(dwc->usb2_phy); - usb_phy_shutdown(dwc->usb3_phy); - phy_exit(dwc->usb2_generic_phy); - phy_exit(dwc->usb3_generic_phy); -} - static int dwc3_core_get_phy(struct dwc3 *dwc) { struct device *dev = dwc->dev; @@ -951,10 +984,6 @@ static int dwc3_probe(struct platform_device *pdev) platform_set_drvdata(pdev, dwc); dwc3_cache_hwparams(dwc); - ret = dwc3_phy_setup(dwc); - if (ret) - goto err0; - ret = dwc3_core_get_phy(dwc); if (ret) goto err0; @@ -975,7 +1004,7 @@ static int dwc3_probe(struct platform_device *pdev) if (ret) { dev_err(dwc->dev, "failed to allocate event buffers\n"); ret = -ENOMEM; - goto err1; + goto err0; } if (IS_ENABLED(CONFIG_USB_DWC3_HOST)) @@ -986,10 +1015,14 @@ static int dwc3_probe(struct platform_device *pdev) if (dwc->dr_mode == USB_DR_MODE_UNKNOWN) dwc->dr_mode = USB_DR_MODE_OTG; + ret = dwc3_alloc_scratch_buffers(dwc); + if (ret) + goto err1; + ret = dwc3_core_init(dwc); if (ret) { dev_err(dev, "failed to initialize core\n"); - goto err1; + goto err2; } /* Check the maximum_speed parameter */ @@ -1019,47 +1052,20 @@ static int dwc3_probe(struct platform_device *pdev) break; } - /* Adjust Frame Length */ - dwc3_frame_length_adjustment(dwc); - - usb_phy_set_suspend(dwc->usb2_phy, 0); - usb_phy_set_suspend(dwc->usb3_phy, 0); - ret = phy_power_on(dwc->usb2_generic_phy); - if (ret < 0) - goto err2; - - ret = phy_power_on(dwc->usb3_generic_phy); - if (ret < 0) - goto err3; - - ret = dwc3_event_buffers_setup(dwc); - if (ret) { - dev_err(dwc->dev, "failed to setup event buffers\n"); - goto err4; - } - ret = dwc3_core_init_mode(dwc); if (ret) - goto err5; + goto err3; dwc3_debugfs_init(dwc); pm_runtime_allow(dev); return 0; -err5: +err3: dwc3_event_buffers_cleanup(dwc); -err4: - phy_power_off(dwc->usb3_generic_phy); - -err3: - phy_power_off(dwc->usb2_generic_phy); - err2: - usb_phy_set_suspend(dwc->usb2_phy, 1); - usb_phy_set_suspend(dwc->usb3_phy, 1); - dwc3_core_exit(dwc); + dwc3_free_scratch_buffers(dwc); err1: dwc3_free_event_buffers(dwc); @@ -1090,17 +1096,13 @@ static int dwc3_remove(struct platform_device *pdev) dwc3_debugfs_exit(dwc); dwc3_core_exit_mode(dwc); - dwc3_event_buffers_cleanup(dwc); - dwc3_free_event_buffers(dwc); - - usb_phy_set_suspend(dwc->usb2_phy, 1); - usb_phy_set_suspend(dwc->usb3_phy, 1); - phy_power_off(dwc->usb2_generic_phy); - phy_power_off(dwc->usb3_generic_phy); dwc3_core_exit(dwc); dwc3_ulpi_exit(dwc); + dwc3_free_event_buffers(dwc); + dwc3_free_scratch_buffers(dwc); + pm_runtime_put_sync(&pdev->dev); pm_runtime_disable(&pdev->dev); From 51f5d49ad6f011ee380b866ea617fd90584189a2 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Mon, 16 May 2016 10:52:58 +0300 Subject: [PATCH 018/109] usb: dwc3: core: simplify suspend/resume operations now that we have re-factored dwc3_core_init() and dwc3_core_exit() we can use them for suspend/resume operations. This will help us avoid some common mistakes when patching code when we have duplicated pieces of code doing the same thing. Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/core.c | 59 ++++------------------------------------- drivers/usb/dwc3/core.h | 3 --- 2 files changed, 5 insertions(+), 57 deletions(-) diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c index cbdefbb3d302..80e9affd3d77 100644 --- a/drivers/usb/dwc3/core.c +++ b/drivers/usb/dwc3/core.c @@ -1113,33 +1113,19 @@ static int dwc3_remove(struct platform_device *pdev) static int dwc3_suspend(struct device *dev) { struct dwc3 *dwc = dev_get_drvdata(dev); - unsigned long flags; - - spin_lock_irqsave(&dwc->lock, flags); switch (dwc->dr_mode) { case USB_DR_MODE_PERIPHERAL: case USB_DR_MODE_OTG: dwc3_gadget_suspend(dwc); - /* FALLTHROUGH */ + break; case USB_DR_MODE_HOST: default: - dwc3_event_buffers_cleanup(dwc); + /* do nothing */ break; } - dwc->gctl = dwc3_readl(dwc->regs, DWC3_GCTL); - spin_unlock_irqrestore(&dwc->lock, flags); - - usb_phy_shutdown(dwc->usb3_phy); - usb_phy_shutdown(dwc->usb2_phy); - phy_exit(dwc->usb2_generic_phy); - phy_exit(dwc->usb3_generic_phy); - - usb_phy_set_suspend(dwc->usb2_phy, 1); - usb_phy_set_suspend(dwc->usb3_phy, 1); - WARN_ON(phy_power_off(dwc->usb2_generic_phy) < 0); - WARN_ON(phy_power_off(dwc->usb3_generic_phy) < 0); + dwc3_core_exit(dwc); pinctrl_pm_select_sleep_state(dev); @@ -1149,36 +1135,14 @@ static int dwc3_suspend(struct device *dev) static int dwc3_resume(struct device *dev) { struct dwc3 *dwc = dev_get_drvdata(dev); - unsigned long flags; int ret; pinctrl_pm_select_default_state(dev); - usb_phy_set_suspend(dwc->usb2_phy, 0); - usb_phy_set_suspend(dwc->usb3_phy, 0); - ret = phy_power_on(dwc->usb2_generic_phy); - if (ret < 0) + ret = dwc3_core_init(dwc); + if (ret) return ret; - ret = phy_power_on(dwc->usb3_generic_phy); - if (ret < 0) - goto err_usb2phy_power; - - usb_phy_init(dwc->usb3_phy); - usb_phy_init(dwc->usb2_phy); - ret = phy_init(dwc->usb2_generic_phy); - if (ret < 0) - goto err_usb3phy_power; - - ret = phy_init(dwc->usb3_generic_phy); - if (ret < 0) - goto err_usb2phy_init; - - spin_lock_irqsave(&dwc->lock, flags); - - dwc3_event_buffers_setup(dwc); - dwc3_writel(dwc->regs, DWC3_GCTL, dwc->gctl); - switch (dwc->dr_mode) { case USB_DR_MODE_PERIPHERAL: case USB_DR_MODE_OTG: @@ -1190,24 +1154,11 @@ static int dwc3_resume(struct device *dev) break; } - spin_unlock_irqrestore(&dwc->lock, flags); - pm_runtime_disable(dev); pm_runtime_set_active(dev); pm_runtime_enable(dev); return 0; - -err_usb2phy_init: - phy_exit(dwc->usb2_generic_phy); - -err_usb3phy_power: - phy_power_off(dwc->usb3_generic_phy); - -err_usb2phy_power: - phy_power_off(dwc->usb2_generic_phy); - - return ret; } #endif /* CONFIG_PM_SLEEP */ diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h index e01f6371d118..211729519eb4 100644 --- a/drivers/usb/dwc3/core.h +++ b/drivers/usb/dwc3/core.h @@ -836,9 +836,6 @@ struct dwc3 { enum usb_dr_mode dr_mode; - /* used for suspend/resume */ - u32 gctl; - u32 fladj; u32 nr_scratch; u32 u1u2; From 3f308d17d7abbf35a6d40a7f16dc70cf43f12c98 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Mon, 16 May 2016 14:17:06 +0300 Subject: [PATCH 019/109] usb: dwc3: gadget: hold gadget IRQ in dwc->irq_gadget by holding gadget's IRQ number in dwc->irq_gadget, it'll be simpler to free_irq() and disable the IRQ in case an IRQ fires while we are runtime suspended. Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/core.h | 2 ++ drivers/usb/dwc3/gadget.c | 5 ++--- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h index 211729519eb4..5148c563167e 100644 --- a/drivers/usb/dwc3/core.h +++ b/drivers/usb/dwc3/core.h @@ -730,6 +730,7 @@ struct dwc3_scratchpad_array { * @regs: base address for our registers * @regs_size: address space size * @fladj: frame length adjustment + * @irq_gadget: peripheral controller's IRQ number * @nr_scratch: number of scratch buffers * @u1u2: only used on revisions <1.83a for workaround * @maximum_speed: maximum speed requested (mainly for testing purposes) @@ -837,6 +838,7 @@ struct dwc3 { enum usb_dr_mode dr_mode; u32 fladj; + u32 irq_gadget; u32 nr_scratch; u32 u1u2; u32 maximum_speed; diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index 7ba4287f5c71..378c14c45fcb 100644 --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c @@ -1751,6 +1751,7 @@ static int dwc3_gadget_start(struct usb_gadget *g, irq, ret); goto err0; } + dwc->irq_gadget = irq; spin_lock_irqsave(&dwc->lock, flags); if (dwc->gadget_driver) { @@ -1787,15 +1788,13 @@ static int dwc3_gadget_stop(struct usb_gadget *g) { struct dwc3 *dwc = gadget_to_dwc(g); unsigned long flags; - int irq; spin_lock_irqsave(&dwc->lock, flags); __dwc3_gadget_stop(dwc); dwc->gadget_driver = NULL; spin_unlock_irqrestore(&dwc->lock, flags); - irq = platform_get_irq(to_platform_device(dwc->dev), 0); - free_irq(irq, dwc->ev_buf); + free_irq(dwc->irq_gadget, dwc->ev_buf); return 0; } From ab2a92e7a608c09f13baf1730b9ba24c73c35d52 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Tue, 17 May 2016 14:55:58 +0300 Subject: [PATCH 020/109] usb: dwc3: gadget: only resume USB2 PHY in <=HIGHSPEED As a micro-power optimization, let's only resume the USB2 PHY if we're working on <=HIGHSPEED. If we're gonna work on SUPERSPEED or SUPERSPEED+, there's no point in resuming the USB2 PHY. Fixes: 2b0f11df84bb ("usb: dwc3: gadget: clear SUSPHY bit before ep cmds") Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/gadget.c | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index 378c14c45fcb..5e7b2ba8312b 100644 --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c @@ -258,11 +258,13 @@ int dwc3_send_gadget_ep_cmd(struct dwc3_ep *dep, unsigned cmd, * We will also set SUSPHY bit to what it was before returning as stated * by the same section on Synopsys databook. */ - reg = dwc3_readl(dwc->regs, DWC3_GUSB2PHYCFG(0)); - if (unlikely(reg & DWC3_GUSB2PHYCFG_SUSPHY)) { - susphy = true; - reg &= ~DWC3_GUSB2PHYCFG_SUSPHY; - dwc3_writel(dwc->regs, DWC3_GUSB2PHYCFG(0), reg); + if (dwc->gadget.speed <= USB_SPEED_HIGH) { + reg = dwc3_readl(dwc->regs, DWC3_GUSB2PHYCFG(0)); + if (unlikely(reg & DWC3_GUSB2PHYCFG_SUSPHY)) { + susphy = true; + reg &= ~DWC3_GUSB2PHYCFG_SUSPHY; + dwc3_writel(dwc->regs, DWC3_GUSB2PHYCFG(0), reg); + } } if (cmd == DWC3_DEPCMD_STARTTRANSFER) { From 9cad39fe4e4a4fe95d8ea5a7b0692b0a6e89e38b Mon Sep 17 00:00:00 2001 From: Konrad Leszczynski Date: Mon, 8 Feb 2016 16:13:12 +0100 Subject: [PATCH 021/109] usb: dwc3: fix for the isoc transfer EP_BUSY flag commit f3af36511e60 ("usb: dwc3: gadget: always enable IOC on bulk/interrupt transfers") ended up regressing Isochronous endpoints by clearing DWC3_EP_BUSY flag too early, which resulted in choppy audio playback over USB. Fix that by partially reverting original commit and making sure that we check for isochronous endpoints. Fixes: f3af36511e60 ("usb: dwc3: gadget: always enable IOC on bulk/interrupt transfers") Cc: Signed-off-by: Konrad Leszczynski Signed-off-by: Rafal Redzimski Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/gadget.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index 5e7b2ba8312b..867adc9a2496 100644 --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c @@ -2058,6 +2058,10 @@ static int dwc3_cleanup_done_reqs(struct dwc3 *dwc, struct dwc3_ep *dep, return 1; } + if (usb_endpoint_xfer_isoc(dep->endpoint.desc)) + if ((event->status & DEPEVT_STATUS_IOC) && + (trb->ctrl & DWC3_TRB_CTRL_IOC)) + return 0; return 1; } From 4cb4221764ef473cd36e1953f1fea11865786d65 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Wed, 18 May 2016 12:37:21 +0300 Subject: [PATCH 022/109] usb: dwc3: gadget: fix for possible endpoint disable race when we call dwc3_gadget_giveback(), we end up releasing our controller's lock. Another thread could get scheduled and disable the endpoint, subsequently setting dep->endpoint.desc to NULL. In that case, we would end up dereferencing a NULL pointer which would result in a Kernel Oops. Let's avoid the problem by simply returning early if we have a NULL descriptor. Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/gadget.c | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index 867adc9a2496..b59893c3093a 100644 --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c @@ -2041,6 +2041,14 @@ static int dwc3_cleanup_done_reqs(struct dwc3 *dwc, struct dwc3_ep *dep, break; } while (1); + /* + * Our endpoint might get disabled by another thread during + * dwc3_gadget_giveback(). If that happens, we're just gonna return 1 + * early on so DWC3_EP_BUSY flag gets cleared + */ + if (!dep->endpoint.desc) + return 1; + if (usb_endpoint_xfer_isoc(dep->endpoint.desc) && list_empty(&dep->started_list)) { if (list_empty(&dep->pending_list)) { @@ -2078,7 +2086,7 @@ static void dwc3_endpoint_transfer_complete(struct dwc3 *dwc, status = -ECONNRESET; clean_busy = dwc3_cleanup_done_reqs(dwc, dep, event, status); - if (clean_busy && (is_xfer_complete || + if (clean_busy && (!dep->endpoint.desc || is_xfer_complete || usb_endpoint_xfer_isoc(dep->endpoint.desc))) dep->flags &= ~DWC3_EP_BUSY; @@ -2107,6 +2115,14 @@ static void dwc3_endpoint_transfer_complete(struct dwc3 *dwc, dwc->u1u2 = 0; } + /* + * Our endpoint might get disabled by another thread during + * dwc3_gadget_giveback(). If that happens, we're just gonna return 1 + * early on so DWC3_EP_BUSY flag gets cleared + */ + if (!dep->endpoint.desc) + return; + if (!usb_endpoint_xfer_isoc(dep->endpoint.desc)) { int ret; From fc8bb91bc83ef82868533e75f5a11abc1158ec81 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Mon, 16 May 2016 13:14:48 +0300 Subject: [PATCH 023/109] usb: dwc3: implement runtime PM this patch implements the most basic pm_runtime support for dwc3. Whenever USB cable is dettached, then we will allow core to runtime_suspend. Runtime suspending will involve completely tearing down event buffers and require a full soft-reset of the IP. Note that a further optimization could be implemented once we decide to support hibernation, which is to allow runtime_suspend with cable connected when bus is in U3. That's subject to a separate patch, however. Tested-by: Baolin Wang Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/core.c | 160 +++++++++++++++++++++++++++++++++----- drivers/usb/dwc3/core.h | 9 +++ drivers/usb/dwc3/gadget.c | 32 +++++++- 3 files changed, 180 insertions(+), 21 deletions(-) diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c index 80e9affd3d77..bf1789f134ac 100644 --- a/drivers/usb/dwc3/core.c +++ b/drivers/usb/dwc3/core.c @@ -48,7 +48,7 @@ #include "debug.h" -/* -------------------------------------------------------------------------- */ +#define DWC3_DEFAULT_AUTOSUSPEND_DELAY 5000 /* ms */ void dwc3_set_mode(struct dwc3 *dwc, u32 mode) { @@ -996,6 +996,9 @@ static int dwc3_probe(struct platform_device *pdev) dma_set_coherent_mask(dev, dev->parent->coherent_dma_mask); } + pm_runtime_set_active(dev); + pm_runtime_use_autosuspend(dev); + pm_runtime_set_autosuspend_delay(dev, DWC3_DEFAULT_AUTOSUSPEND_DELAY); pm_runtime_enable(dev); pm_runtime_get_sync(dev); pm_runtime_forbid(dev); @@ -1057,7 +1060,7 @@ static int dwc3_probe(struct platform_device *pdev) goto err3; dwc3_debugfs_init(dwc); - pm_runtime_allow(dev); + pm_runtime_put(dev); return 0; @@ -1087,6 +1090,7 @@ static int dwc3_remove(struct platform_device *pdev) struct dwc3 *dwc = platform_get_drvdata(pdev); struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + pm_runtime_get_sync(&pdev->dev); /* * restore res->start back to its original value so that, in case the * probe is deferred, we don't end up getting error in request the @@ -1100,24 +1104,27 @@ static int dwc3_remove(struct platform_device *pdev) dwc3_core_exit(dwc); dwc3_ulpi_exit(dwc); + pm_runtime_put_sync(&pdev->dev); + pm_runtime_allow(&pdev->dev); + pm_runtime_disable(&pdev->dev); + dwc3_free_event_buffers(dwc); dwc3_free_scratch_buffers(dwc); - pm_runtime_put_sync(&pdev->dev); - pm_runtime_disable(&pdev->dev); - return 0; } -#ifdef CONFIG_PM_SLEEP -static int dwc3_suspend(struct device *dev) +#ifdef CONFIG_PM +static int dwc3_suspend_common(struct dwc3 *dwc) { - struct dwc3 *dwc = dev_get_drvdata(dev); + unsigned long flags; switch (dwc->dr_mode) { case USB_DR_MODE_PERIPHERAL: case USB_DR_MODE_OTG: + spin_lock_irqsave(&dwc->lock, flags); dwc3_gadget_suspend(dwc); + spin_unlock_irqrestore(&dwc->lock, flags); break; case USB_DR_MODE_HOST: default: @@ -1127,6 +1134,128 @@ static int dwc3_suspend(struct device *dev) dwc3_core_exit(dwc); + return 0; +} + +static int dwc3_resume_common(struct dwc3 *dwc) +{ + unsigned long flags; + int ret; + + ret = dwc3_core_init(dwc); + if (ret) + return ret; + + switch (dwc->dr_mode) { + case USB_DR_MODE_PERIPHERAL: + case USB_DR_MODE_OTG: + spin_lock_irqsave(&dwc->lock, flags); + dwc3_gadget_resume(dwc); + spin_unlock_irqrestore(&dwc->lock, flags); + /* FALLTHROUGH */ + case USB_DR_MODE_HOST: + default: + /* do nothing */ + break; + } + + return 0; +} + +static int dwc3_runtime_checks(struct dwc3 *dwc) +{ + switch (dwc->dr_mode) { + case USB_DR_MODE_PERIPHERAL: + case USB_DR_MODE_OTG: + if (dwc->connected) + return -EBUSY; + break; + case USB_DR_MODE_HOST: + default: + /* do nothing */ + break; + } + + return 0; +} + +static int dwc3_runtime_suspend(struct device *dev) +{ + struct dwc3 *dwc = dev_get_drvdata(dev); + int ret; + + if (dwc3_runtime_checks(dwc)) + return -EBUSY; + + ret = dwc3_suspend_common(dwc); + if (ret) + return ret; + + device_init_wakeup(dev, true); + + return 0; +} + +static int dwc3_runtime_resume(struct device *dev) +{ + struct dwc3 *dwc = dev_get_drvdata(dev); + int ret; + + device_init_wakeup(dev, false); + + ret = dwc3_resume_common(dwc); + if (ret) + return ret; + + switch (dwc->dr_mode) { + case USB_DR_MODE_PERIPHERAL: + case USB_DR_MODE_OTG: + dwc3_gadget_process_pending_events(dwc); + break; + case USB_DR_MODE_HOST: + default: + /* do nothing */ + break; + } + + pm_runtime_mark_last_busy(dev); + + return 0; +} + +static int dwc3_runtime_idle(struct device *dev) +{ + struct dwc3 *dwc = dev_get_drvdata(dev); + + switch (dwc->dr_mode) { + case USB_DR_MODE_PERIPHERAL: + case USB_DR_MODE_OTG: + if (dwc3_runtime_checks(dwc)) + return -EBUSY; + break; + case USB_DR_MODE_HOST: + default: + /* do nothing */ + break; + } + + pm_runtime_mark_last_busy(dev); + pm_runtime_autosuspend(dev); + + return 0; +} +#endif /* CONFIG_PM */ + +#ifdef CONFIG_PM_SLEEP +static int dwc3_suspend(struct device *dev) +{ + struct dwc3 *dwc = dev_get_drvdata(dev); + int ret; + + ret = dwc3_suspend_common(dwc); + if (ret) + return ret; + pinctrl_pm_select_sleep_state(dev); return 0; @@ -1139,21 +1268,10 @@ static int dwc3_resume(struct device *dev) pinctrl_pm_select_default_state(dev); - ret = dwc3_core_init(dwc); + ret = dwc3_resume_common(dwc); if (ret) return ret; - switch (dwc->dr_mode) { - case USB_DR_MODE_PERIPHERAL: - case USB_DR_MODE_OTG: - dwc3_gadget_resume(dwc); - /* FALLTHROUGH */ - case USB_DR_MODE_HOST: - default: - /* do nothing */ - break; - } - pm_runtime_disable(dev); pm_runtime_set_active(dev); pm_runtime_enable(dev); @@ -1164,6 +1282,8 @@ static int dwc3_resume(struct device *dev) static const struct dev_pm_ops dwc3_dev_pm_ops = { SET_SYSTEM_SLEEP_PM_OPS(dwc3_suspend, dwc3_resume) + SET_RUNTIME_PM_OPS(dwc3_runtime_suspend, dwc3_runtime_resume, + dwc3_runtime_idle) }; #ifdef CONFIG_OF diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h index 5148c563167e..94b9fd2d583e 100644 --- a/drivers/usb/dwc3/core.h +++ b/drivers/usb/dwc3/core.h @@ -763,6 +763,7 @@ struct dwc3_scratchpad_array { * @lpm_nyet_threshold: LPM NYET response threshold * @hird_threshold: HIRD threshold * @hsphy_interface: "utmi" or "ulpi" + * @connected: true when we're connected to a host, false otherwise * @delayed_status: true when gadget driver asks for delayed status * @ep0_bounced: true when we used bounce buffer * @ep0_expect_in: true when we expect a DATA IN transfer @@ -773,6 +774,7 @@ struct dwc3_scratchpad_array { * 0 - utmi_sleep_n * 1 - utmi_l1_suspend_n * @is_fpga: true when we are using the FPGA board + * @pending_events: true when we have pending IRQs to be handled * @pullups_connected: true when Run/Stop bit is set * @setup_packet_pending: true when there's a Setup Packet in FIFO. Workaround * @start_config_issued: true when StartConfig command has been issued @@ -907,6 +909,7 @@ struct dwc3 { const char *hsphy_interface; + unsigned connected:1; unsigned delayed_status:1; unsigned ep0_bounced:1; unsigned ep0_expect_in:1; @@ -914,6 +917,7 @@ struct dwc3 { unsigned has_lpm_erratum:1; unsigned is_utmi_l1_suspend:1; unsigned is_fpga:1; + unsigned pending_events:1; unsigned pullups_connected:1; unsigned setup_packet_pending:1; unsigned three_stage_setup:1; @@ -1139,6 +1143,7 @@ static inline int dwc3_send_gadget_generic_command(struct dwc3 *dwc, #if !IS_ENABLED(CONFIG_USB_DWC3_HOST) int dwc3_gadget_suspend(struct dwc3 *dwc); int dwc3_gadget_resume(struct dwc3 *dwc); +void dwc3_gadget_process_pending_events(struct dwc3 *dwc); #else static inline int dwc3_gadget_suspend(struct dwc3 *dwc) { @@ -1149,6 +1154,10 @@ static inline int dwc3_gadget_resume(struct dwc3 *dwc) { return 0; } + +static inline void dwc3_gadget_process_pending_events(struct dwc3 *dwc) +{ +} #endif /* !IS_ENABLED(CONFIG_USB_DWC3_HOST) */ #if IS_ENABLED(CONFIG_USB_DWC3_ULPI) diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index b59893c3093a..194775184a58 100644 --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c @@ -199,6 +199,9 @@ void dwc3_gadget_giveback(struct dwc3_ep *dep, struct dwc3_request *req, spin_unlock(&dwc->lock); usb_gadget_giveback_request(&dep->endpoint, &req->request); spin_lock(&dwc->lock); + + if (dep->number > 1) + pm_runtime_put(dwc->dev); } int dwc3_send_gadget_generic_command(struct dwc3 *dwc, unsigned cmd, u32 param) @@ -1081,6 +1084,8 @@ static int __dwc3_gadget_ep_queue(struct dwc3_ep *dep, struct dwc3_request *req) return -EINVAL; } + pm_runtime_get(dwc->dev); + req->request.actual = 0; req->request.status = -EINPROGRESS; req->direction = dep->direction; @@ -1509,6 +1514,9 @@ static int dwc3_gadget_run_stop(struct dwc3 *dwc, int is_on, int suspend) u32 reg; u32 timeout = 500; + if (pm_runtime_suspended(dwc->dev)) + return 0; + reg = dwc3_readl(dwc->regs, DWC3_DCTL); if (is_on) { if (dwc->revision <= DWC3_REVISION_187A) { @@ -1766,7 +1774,9 @@ static int dwc3_gadget_start(struct usb_gadget *g, dwc->gadget_driver = driver; - __dwc3_gadget_start(dwc); + if (pm_runtime_active(dwc->dev)) + __dwc3_gadget_start(dwc); + spin_unlock_irqrestore(&dwc->lock, flags); return 0; @@ -2355,12 +2365,16 @@ static void dwc3_gadget_disconnect_interrupt(struct dwc3 *dwc) dwc->gadget.speed = USB_SPEED_UNKNOWN; dwc->setup_packet_pending = false; usb_gadget_set_state(&dwc->gadget, USB_STATE_NOTATTACHED); + + dwc->connected = false; } static void dwc3_gadget_reset_interrupt(struct dwc3 *dwc) { u32 reg; + dwc->connected = true; + /* * WORKAROUND: DWC3 revisions <1.88a have an issue which * would cause a missing Disconnect Event if there's a @@ -2822,6 +2836,13 @@ static irqreturn_t dwc3_check_event_buf(struct dwc3_event_buffer *evt) u32 count; u32 reg; + if (pm_runtime_suspended(dwc->dev)) { + pm_runtime_get(dwc->dev); + disable_irq_nosync(dwc->irq_gadget); + dwc->pending_events = true; + return IRQ_HANDLED; + } + count = dwc3_readl(dwc->regs, DWC3_GEVNTCOUNT(0)); count &= DWC3_GEVNTCOUNT_MASK; if (!count) @@ -3028,3 +3049,12 @@ err1: err0: return ret; } + +void dwc3_gadget_process_pending_events(struct dwc3 *dwc) +{ + if (dwc->pending_events) { + dwc3_interrupt(dwc->irq_gadget, dwc->ev_buf); + dwc->pending_events = false; + enable_irq(dwc->irq_gadget); + } +} From e9af9229098d92145d0b894b78878bbc6ac7c910 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Tue, 17 May 2016 10:15:02 +0300 Subject: [PATCH 024/109] usb: dwc3: pci: add Power Management dummy hooks Allow for dwc3-pci to reach D3 and enable pm_runtime by providing dummy PM hooks. Without them, PCI subsystem won't put device to D3. Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/dwc3-pci.c | 39 +++++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/drivers/usb/dwc3/dwc3-pci.c b/drivers/usb/dwc3/dwc3-pci.c index 14196cd416b3..a7b6a1c8bc81 100644 --- a/drivers/usb/dwc3/dwc3-pci.c +++ b/drivers/usb/dwc3/dwc3-pci.c @@ -20,6 +20,7 @@ #include #include #include +#include #include #include #include @@ -180,7 +181,11 @@ static int dwc3_pci_probe(struct pci_dev *pci, goto err; } + device_init_wakeup(dev, true); + device_set_run_wake(dev, true); pci_set_drvdata(pci, dwc3); + pm_runtime_put(dev); + return 0; err: platform_device_put(dwc3); @@ -189,6 +194,8 @@ err: static void dwc3_pci_remove(struct pci_dev *pci) { + device_init_wakeup(&pci->dev, false); + pm_runtime_get(&pci->dev); acpi_dev_remove_driver_gpios(ACPI_COMPANION(&pci->dev)); platform_device_unregister(pci_get_drvdata(pci)); } @@ -219,11 +226,43 @@ static const struct pci_device_id dwc3_pci_id_table[] = { }; MODULE_DEVICE_TABLE(pci, dwc3_pci_id_table); +#ifdef CONFIG_PM +static int dwc3_pci_runtime_suspend(struct device *dev) +{ + if (device_run_wake(dev)) + return 0; + + return -EBUSY; +} + +static int dwc3_pci_pm_dummy(struct device *dev) +{ + /* + * There's nothing to do here. No, seriously. Everything is either taken + * care either by PCI subsystem or dwc3/core.c, so we have nothing + * missing here. + * + * So you'd think we didn't need this at all, but PCI subsystem will + * bail out if we don't have a valid callback :-s + */ + return 0; +} +#endif /* CONFIG_PM */ + +static struct dev_pm_ops dwc3_pci_dev_pm_ops = { + SET_SYSTEM_SLEEP_PM_OPS(dwc3_pci_pm_dummy, dwc3_pci_pm_dummy) + SET_RUNTIME_PM_OPS(dwc3_pci_runtime_suspend, dwc3_pci_pm_dummy, + NULL) +}; + static struct pci_driver dwc3_pci_driver = { .name = "dwc3-pci", .id_table = dwc3_pci_id_table, .probe = dwc3_pci_probe, .remove = dwc3_pci_remove, + .driver = { + .pm = &dwc3_pci_dev_pm_ops, + } }; MODULE_AUTHOR("Felipe Balbi "); From 74674cbf858ff6a83c3f06f4ab0ffa5d3c91bf0b Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Wed, 13 Apr 2016 16:44:39 +0300 Subject: [PATCH 025/109] usb: dwc3: gadget: add a per-endpoint request queue lock This will allow us to process several endpoints at a time by making sure that we lock only shared resources. Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/core.h | 2 ++ drivers/usb/dwc3/gadget.c | 1 + 2 files changed, 3 insertions(+) diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h index 94b9fd2d583e..484bb5d8261c 100644 --- a/drivers/usb/dwc3/core.h +++ b/drivers/usb/dwc3/core.h @@ -482,6 +482,7 @@ struct dwc3_event_buffer { * @endpoint: usb endpoint * @pending_list: list of pending requests for this endpoint * @started_list: list of started requests on this endpoint + * @lock: spinlock for endpoint request queue traversal * @regs: pointer to first endpoint register * @trb_pool: array of transaction buffers * @trb_pool_dma: dma address of @trb_pool @@ -504,6 +505,7 @@ struct dwc3_ep { struct list_head pending_list; struct list_head started_list; + spinlock_t lock; void __iomem *regs; struct dwc3_trb *trb_pool; diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index 194775184a58..8b932033d29b 100644 --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c @@ -1845,6 +1845,7 @@ static int dwc3_gadget_init_hw_endpoints(struct dwc3 *dwc, (epnum & 1) ? "in" : "out"); dep->endpoint.name = dep->name; + spin_lock_init(&dep->lock); dwc3_trace(trace_dwc3_gadget, "initializing %s", dep->name); From 22f2c619a116f120fe57de1225cfe7fdeb0b12d2 Mon Sep 17 00:00:00 2001 From: Janusz Dziedzic Date: Fri, 20 May 2016 10:37:13 +0200 Subject: [PATCH 026/109] usb: dwc3: trace: pretty-print TRB's ctrl field Improve trb tracing by showing trb flags, interrupts trb type. trb flags: - h - hardware owner of descriptor - l - last TRB - c - chain buffers - s - continue on short packet interrupt flags: - s - interrupt on short packet - c - interrupt on complete Capital letter means that bit is set, while lowercase letter means bit is cleared. Signed-off-by: Janusz Dziedzic Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/trace.h | 40 ++++++++++++++++++++++++++++++++++++++-- 1 file changed, 38 insertions(+), 2 deletions(-) diff --git a/drivers/usb/dwc3/trace.h b/drivers/usb/dwc3/trace.h index 3ac7252f4427..8cbe1fc9c8d4 100644 --- a/drivers/usb/dwc3/trace.h +++ b/drivers/usb/dwc3/trace.h @@ -237,9 +237,45 @@ DECLARE_EVENT_CLASS(dwc3_log_trb, __entry->size = trb->size; __entry->ctrl = trb->ctrl; ), - TP_printk("%s: trb %p bph %08x bpl %08x size %08x ctrl %08x", + TP_printk("%s: trb %p buf %08x%08x size %d ctrl %08x (%c%c%c%c:%c%c:%s)", __get_str(name), __entry->trb, __entry->bph, __entry->bpl, - __entry->size, __entry->ctrl + __entry->size, __entry->ctrl, + __entry->ctrl & DWC3_TRB_CTRL_HWO ? 'H' : 'h', + __entry->ctrl & DWC3_TRB_CTRL_LST ? 'L' : 'l', + __entry->ctrl & DWC3_TRB_CTRL_CHN ? 'C' : 'c', + __entry->ctrl & DWC3_TRB_CTRL_CSP ? 'S' : 's', + __entry->ctrl & DWC3_TRB_CTRL_ISP_IMI ? 'S' : 's', + __entry->ctrl & DWC3_TRB_CTRL_IOC ? 'C' : 'c', + ({char *s; + switch (__entry->ctrl & 0x3f0) { + case DWC3_TRBCTL_NORMAL: + s = "normal"; + break; + case DWC3_TRBCTL_CONTROL_SETUP: + s = "setup"; + break; + case DWC3_TRBCTL_CONTROL_STATUS2: + s = "status2"; + break; + case DWC3_TRBCTL_CONTROL_STATUS3: + s = "status3"; + break; + case DWC3_TRBCTL_CONTROL_DATA: + s = "data"; + break; + case DWC3_TRBCTL_ISOCHRONOUS_FIRST: + s = "isoc-first"; + break; + case DWC3_TRBCTL_ISOCHRONOUS: + s = "isoc"; + break; + case DWC3_TRBCTL_LINK_TRB: + s = "link"; + break; + default: + s = "UNKNOWN"; + break; + } s; }) ) ); From 7ab373aadbd0463c0f020d368947b05e67a20bd5 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Mon, 23 May 2016 11:27:26 +0300 Subject: [PATCH 027/109] usb: dwc3: gadget: no more tracking endpoint type with its name I really thought this would be useful, but as it turns out, it creates more problems than fixes. The amount of times we had to fix this because some other commit shuffled things around and ended up regressing this tiny little string manupulation... Might as well remove it, since it has a negligible added benefit. Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/gadget.c | 24 +----------------------- 1 file changed, 1 insertion(+), 23 deletions(-) diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index 8b932033d29b..05818152555b 100644 --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c @@ -591,7 +591,7 @@ static int __dwc3_gadget_ep_enable(struct dwc3_ep *dep, dwc3_writel(dwc->regs, DWC3_DALEPENA, reg); if (usb_endpoint_xfer_control(desc)) - goto out; + return 0; /* Link TRB. The HWO bit is never reset */ trb_st_hw = &dep->trb_pool[0]; @@ -605,24 +605,6 @@ static int __dwc3_gadget_ep_enable(struct dwc3_ep *dep, trb_link->ctrl |= DWC3_TRB_CTRL_HWO; } -out: - switch (usb_endpoint_type(desc)) { - case USB_ENDPOINT_XFER_CONTROL: - /* don't change name */ - break; - case USB_ENDPOINT_XFER_ISOC: - strlcat(dep->name, "-isoc", sizeof(dep->name)); - break; - case USB_ENDPOINT_XFER_BULK: - strlcat(dep->name, "-bulk", sizeof(dep->name)); - break; - case USB_ENDPOINT_XFER_INT: - strlcat(dep->name, "-int", sizeof(dep->name)); - break; - default: - dev_err(dwc->dev, "invalid endpoint transfer type\n"); - } - return 0; } @@ -680,10 +662,6 @@ static int __dwc3_gadget_ep_disable(struct dwc3_ep *dep) dep->type = 0; dep->flags = 0; - snprintf(dep->name, sizeof(dep->name), "ep%d%s", - dep->number >> 1, - (dep->number & 1) ? "in" : "out"); - return 0; } From f75cacc468edc4826305909d6102d60fba55199f Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Mon, 23 May 2016 11:10:08 +0300 Subject: [PATCH 028/109] usb: dwc3: trace: fully decode IRQ events This will make it more human-friendly to read trace output from dwc3. Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/debug.h | 110 +++++++++++++++++++++++++++++++-------- drivers/usb/dwc3/ep0.c | 7 +-- drivers/usb/dwc3/trace.h | 3 +- 3 files changed, 92 insertions(+), 28 deletions(-) diff --git a/drivers/usb/dwc3/debug.h b/drivers/usb/dwc3/debug.h index 71e318025964..e3e0b4111c53 100644 --- a/drivers/usb/dwc3/debug.h +++ b/drivers/usb/dwc3/debug.h @@ -128,56 +128,112 @@ dwc3_gadget_link_string(enum dwc3_link_state link_state) * dwc3_gadget_event_string - returns event name * @event: the event code */ -static inline const char *dwc3_gadget_event_string(u8 event) +static inline const char * +dwc3_gadget_event_string(const struct dwc3_event_devt *event) { - switch (event) { + static char str[256]; + enum dwc3_link_state state = event->event_info & DWC3_LINK_STATE_MASK; + + switch (event->type) { case DWC3_DEVICE_EVENT_DISCONNECT: - return "Disconnect"; + sprintf(str, "Disconnect: [%s]", + dwc3_gadget_link_string(state)); + break; case DWC3_DEVICE_EVENT_RESET: - return "Reset"; + sprintf(str, "Reset [%s]", dwc3_gadget_link_string(state)); + break; case DWC3_DEVICE_EVENT_CONNECT_DONE: - return "Connection Done"; + sprintf(str, "Connection Done [%s]", + dwc3_gadget_link_string(state)); + break; case DWC3_DEVICE_EVENT_LINK_STATUS_CHANGE: - return "Link Status Change"; + sprintf(str, "Link Change [%s]", + dwc3_gadget_link_string(state)); + break; case DWC3_DEVICE_EVENT_WAKEUP: - return "WakeUp"; + sprintf(str, "WakeUp [%s]", dwc3_gadget_link_string(state)); + break; case DWC3_DEVICE_EVENT_EOPF: - return "End-Of-Frame"; + sprintf(str, "End-Of-Frame [%s]", + dwc3_gadget_link_string(state)); + break; case DWC3_DEVICE_EVENT_SOF: - return "Start-Of-Frame"; + sprintf(str, "Start-Of-Frame [%s]", + dwc3_gadget_link_string(state)); + break; case DWC3_DEVICE_EVENT_ERRATIC_ERROR: - return "Erratic Error"; + sprintf(str, "Erratic Error [%s]", + dwc3_gadget_link_string(state)); + break; case DWC3_DEVICE_EVENT_CMD_CMPL: - return "Command Complete"; + sprintf(str, "Command Complete [%s]", + dwc3_gadget_link_string(state)); + break; case DWC3_DEVICE_EVENT_OVERFLOW: - return "Overflow"; + sprintf(str, "Overflow [%s]", dwc3_gadget_link_string(state)); + break; + default: + sprintf(str, "UNKNOWN"); } - return "UNKNOWN"; + return str; } /** * dwc3_ep_event_string - returns event name * @event: then event code */ -static inline const char *dwc3_ep_event_string(u8 event) +static inline const char * +dwc3_ep_event_string(const struct dwc3_event_depevt *event) { - switch (event) { + u8 epnum = event->endpoint_number; + static char str[256]; + int status; + int ret; + + ret = sprintf(str, "ep%d%s: ", epnum >> 1, + (epnum & 1) ? "in" : "in"); + if (ret < 0) + return "UNKNOWN"; + + switch (event->endpoint_event) { case DWC3_DEPEVT_XFERCOMPLETE: - return "Transfer Complete"; + strcat(str, "Transfer Complete"); + break; case DWC3_DEPEVT_XFERINPROGRESS: - return "Transfer In-Progress"; + strcat(str, "Transfer In-Progress"); + break; case DWC3_DEPEVT_XFERNOTREADY: - return "Transfer Not Ready"; + strcat(str, "Transfer Not Ready"); + status = event->status & DEPEVT_STATUS_TRANSFER_ACTIVE; + strcat(str, status ? " (Active)" : " (Not Active)"); + break; case DWC3_DEPEVT_RXTXFIFOEVT: - return "FIFO"; + strcat(str, "FIFO"); + break; case DWC3_DEPEVT_STREAMEVT: - return "Stream"; + status = event->status; + + switch (status) { + case DEPEVT_STREAMEVT_FOUND: + sprintf(str + ret, " Stream %d Found", + event->parameters); + break; + case DEPEVT_STREAMEVT_NOTFOUND: + default: + strcat(str, " Stream Not Found"); + break; + } + + break; case DWC3_DEPEVT_EPCMDCMPLT: - return "Endpoint Command Complete"; + strcat(str, "Endpoint Command Complete"); + break; + default: + sprintf(str, "UNKNOWN"); } - return "UNKNOWN"; + return str; } /** @@ -214,6 +270,16 @@ static inline const char *dwc3_gadget_event_type_string(u8 event) } } +static inline const char *dwc3_decode_event(u32 event) +{ + const union dwc3_event evt = (union dwc3_event) event; + + if (evt.type.is_devspec) + return dwc3_gadget_event_string(&evt.devt); + else + return dwc3_ep_event_string(&evt.depevt); +} + void dwc3_trace(void (*trace)(struct va_format *), const char *fmt, ...); #ifdef CONFIG_DEBUG_FS diff --git a/drivers/usb/dwc3/ep0.c b/drivers/usb/dwc3/ep0.c index 3103a8f4b7e5..54628c37b21f 100644 --- a/drivers/usb/dwc3/ep0.c +++ b/drivers/usb/dwc3/ep0.c @@ -1109,11 +1109,8 @@ static void dwc3_ep0_xfernotready(struct dwc3 *dwc, void dwc3_ep0_interrupt(struct dwc3 *dwc, const struct dwc3_event_depevt *event) { - u8 epnum = event->endpoint_number; - - dwc3_trace(trace_dwc3_ep0, "%s while ep%d%s in state '%s'", - dwc3_ep_event_string(event->endpoint_event), - epnum >> 1, (epnum & 1) ? "in" : "out", + dwc3_trace(trace_dwc3_ep0, "%s: state '%s'", + dwc3_ep_event_string(event), dwc3_ep0_state_string(dwc->ep0state)); switch (event->endpoint_event) { diff --git a/drivers/usb/dwc3/trace.h b/drivers/usb/dwc3/trace.h index 8cbe1fc9c8d4..2389dd864ffb 100644 --- a/drivers/usb/dwc3/trace.h +++ b/drivers/usb/dwc3/trace.h @@ -71,7 +71,8 @@ DECLARE_EVENT_CLASS(dwc3_log_event, TP_fast_assign( __entry->event = event; ), - TP_printk("event %08x", __entry->event) + TP_printk("event (%08x): %s", __entry->event, + dwc3_decode_event(__entry->event)) ); DEFINE_EVENT(dwc3_log_event, dwc3_event, From ba1598410eff646e10be4e42d773e5bdc511d898 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Mon, 23 May 2016 13:50:29 +0300 Subject: [PATCH 029/109] usb: dwc3: gadget: fix trace output when command fails We don't need the extra %s when command fails. Let's remove it Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/gadget.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index 05818152555b..e553a7c4d737 100644 --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c @@ -303,7 +303,7 @@ int dwc3_send_gadget_ep_cmd(struct dwc3_ep *dep, unsigned cmd, ret = 0; break; case DEPEVT_TRANSFER_NO_RESOURCE: - dwc3_trace(trace_dwc3_gadget, "%s: no resource available"); + dwc3_trace(trace_dwc3_gadget, "no resource available"); ret = -EINVAL; break; case DEPEVT_TRANSFER_BUS_EXPIRY: @@ -318,7 +318,7 @@ int dwc3_send_gadget_ep_cmd(struct dwc3_ep *dep, unsigned cmd, * give a hint to the gadget driver that this is * the case by returning -EAGAIN. */ - dwc3_trace(trace_dwc3_gadget, "%s: bus expiry"); + dwc3_trace(trace_dwc3_gadget, "bus expiry"); ret = -EAGAIN; break; default: From f6bb225bb3ca7988ff373c62cc298e56cae1eee5 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Mon, 23 May 2016 13:53:34 +0300 Subject: [PATCH 030/109] usb: dwc3: gadget: loop while (timeout) instead of having infinite loop and always checking timeout value as a break condition, we can just decrement timeout inside while's condition. Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/gadget.c | 18 ++++++------------ 1 file changed, 6 insertions(+), 12 deletions(-) diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index e553a7c4d737..25170fd5fbd4 100644 --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c @@ -327,19 +327,13 @@ int dwc3_send_gadget_ep_cmd(struct dwc3_ep *dep, unsigned cmd, break; } + } while (--timeout); - /* - * We can't sleep here, because it is also called from - * interrupt context. - */ - timeout--; - if (!timeout) { - dwc3_trace(trace_dwc3_gadget, - "Command Timed Out"); - ret = -ETIMEDOUT; - break; - } - } while (1); + if (timeout == 0) { + dwc3_trace(trace_dwc3_gadget, + "Command Timed Out"); + ret = -ETIMEDOUT; + } if (unlikely(susphy)) { reg = dwc3_readl(dwc->regs, DWC3_GUSB2PHYCFG(0)); From 0933df159c5c82f97c6bb811b149fa1158a26087 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Mon, 23 May 2016 14:02:33 +0300 Subject: [PATCH 031/109] usb: dwc3: trace: print ep cmd status with a single trace Instead of printing command's status with a separate trace printout, let's print it within a single call. Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/debug.h | 16 ++++++++++++++++ drivers/usb/dwc3/gadget.c | 8 +++++--- drivers/usb/dwc3/trace.h | 15 +++++++++------ 3 files changed, 30 insertions(+), 9 deletions(-) diff --git a/drivers/usb/dwc3/debug.h b/drivers/usb/dwc3/debug.h index e3e0b4111c53..8eed4c7cc76b 100644 --- a/drivers/usb/dwc3/debug.h +++ b/drivers/usb/dwc3/debug.h @@ -280,6 +280,22 @@ static inline const char *dwc3_decode_event(u32 event) return dwc3_ep_event_string(&evt.depevt); } +static inline const char *dwc3_ep_cmd_status_string(int status) +{ + switch (status) { + case -ETIMEDOUT: + return "Timed Out"; + case 0: + return "Successful"; + case DEPEVT_TRANSFER_NO_RESOURCE: + return "No Resource"; + case DEPEVT_TRANSFER_BUS_EXPIRY: + return "Bus Expiry"; + default: + return "UNKNOWN"; + } +} + void dwc3_trace(void (*trace)(struct va_format *), const char *fmt, ...); #ifdef CONFIG_DEBUG_FS diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index 25170fd5fbd4..42eefd7c99ef 100644 --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c @@ -248,11 +248,10 @@ int dwc3_send_gadget_ep_cmd(struct dwc3_ep *dep, unsigned cmd, u32 timeout = 500; u32 reg; + int cmd_status = 0; int susphy = false; int ret = -EINVAL; - trace_dwc3_gadget_ep_cmd(dep, cmd, params); - /* * Synopsys Databook 2.60a states, on section 6.3.2.5.[1-8], that if * we're issuing an endpoint command, we must check if @@ -292,7 +291,7 @@ int dwc3_send_gadget_ep_cmd(struct dwc3_ep *dep, unsigned cmd, do { reg = dwc3_readl(dep->regs, DWC3_DEPCMD); if (!(reg & DWC3_DEPCMD_CMDACT)) { - int cmd_status = DWC3_DEPCMD_STATUS(reg); + cmd_status = DWC3_DEPCMD_STATUS(reg); dwc3_trace(trace_dwc3_gadget, "Command Complete --> %d", @@ -333,8 +332,11 @@ int dwc3_send_gadget_ep_cmd(struct dwc3_ep *dep, unsigned cmd, dwc3_trace(trace_dwc3_gadget, "Command Timed Out"); ret = -ETIMEDOUT; + cmd_status = -ETIMEDOUT; } + trace_dwc3_gadget_ep_cmd(dep, cmd, params, cmd_status); + if (unlikely(susphy)) { reg = dwc3_readl(dwc->regs, DWC3_GUSB2PHYCFG(0)); reg |= DWC3_GUSB2PHYCFG_SUSPHY; diff --git a/drivers/usb/dwc3/trace.h b/drivers/usb/dwc3/trace.h index 2389dd864ffb..040f28b27d56 100644 --- a/drivers/usb/dwc3/trace.h +++ b/drivers/usb/dwc3/trace.h @@ -190,14 +190,15 @@ DEFINE_EVENT(dwc3_log_generic_cmd, dwc3_gadget_generic_cmd, DECLARE_EVENT_CLASS(dwc3_log_gadget_ep_cmd, TP_PROTO(struct dwc3_ep *dep, unsigned int cmd, - struct dwc3_gadget_ep_cmd_params *params), - TP_ARGS(dep, cmd, params), + struct dwc3_gadget_ep_cmd_params *params, int cmd_status), + TP_ARGS(dep, cmd, params, cmd_status), TP_STRUCT__entry( __dynamic_array(char, name, DWC3_MSG_MAX) __field(unsigned int, cmd) __field(u32, param0) __field(u32, param1) __field(u32, param2) + __field(int, cmd_status) ), TP_fast_assign( snprintf(__get_str(name), DWC3_MSG_MAX, "%s", dep->name); @@ -205,18 +206,20 @@ DECLARE_EVENT_CLASS(dwc3_log_gadget_ep_cmd, __entry->param0 = params->param0; __entry->param1 = params->param1; __entry->param2 = params->param2; + __entry->cmd_status = cmd_status; ), - TP_printk("%s: cmd '%s' [%d] params %08x %08x %08x", + TP_printk("%s: cmd '%s' [%d] params %08x %08x %08x --> status: %s", __get_str(name), dwc3_gadget_ep_cmd_string(__entry->cmd), __entry->cmd, __entry->param0, - __entry->param1, __entry->param2 + __entry->param1, __entry->param2, + dwc3_ep_cmd_status_string(__entry->cmd_status) ) ); DEFINE_EVENT(dwc3_log_gadget_ep_cmd, dwc3_gadget_ep_cmd, TP_PROTO(struct dwc3_ep *dep, unsigned int cmd, - struct dwc3_gadget_ep_cmd_params *params), - TP_ARGS(dep, cmd, params) + struct dwc3_gadget_ep_cmd_params *params, int cmd_status), + TP_ARGS(dep, cmd, params, cmd_status) ); DECLARE_EVENT_CLASS(dwc3_log_trb, From 0fe886cdb07aeaf75a61154f34abc8dd6860978e Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Mon, 23 May 2016 14:06:07 +0300 Subject: [PATCH 032/109] usb: dwc3: gadget: single return point on generic commands Just like we did for endpoint commands, let's use a single return point for generic commands as well. This aids readability. Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/gadget.c | 25 ++++++++++++------------- 1 file changed, 12 insertions(+), 13 deletions(-) diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index 42eefd7c99ef..f148b11f6e4d 100644 --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c @@ -207,6 +207,7 @@ void dwc3_gadget_giveback(struct dwc3_ep *dep, struct dwc3_request *req, int dwc3_send_gadget_generic_command(struct dwc3 *dwc, unsigned cmd, u32 param) { u32 timeout = 500; + int ret = 0; u32 reg; trace_dwc3_gadget_generic_cmd(cmd, param); @@ -221,22 +222,20 @@ int dwc3_send_gadget_generic_command(struct dwc3 *dwc, unsigned cmd, u32 param) "Command Complete --> %d", DWC3_DGCMD_STATUS(reg)); if (DWC3_DGCMD_STATUS(reg)) - return -EINVAL; - return 0; + ret = -EINVAL; + break; } - /* - * We can't sleep here, because it's also called from - * interrupt context. - */ - timeout--; - if (!timeout) { - dwc3_trace(trace_dwc3_gadget, - "Command Timed Out"); - return -ETIMEDOUT; - } udelay(1); - } while (1); + } while (timeout--); + + if (!timeout) { + dwc3_trace(trace_dwc3_gadget, + "Command Timed Out"); + ret = -ETIMEDOUT; + } + + return ret; } static int __dwc3_gadget_wakeup(struct dwc3 *dwc); From 88811f7b722c417ececbc7f278c26d8df8606d02 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Mon, 23 May 2016 14:08:47 +0300 Subject: [PATCH 033/109] usb: dwc3: gadget: remove udelay() from generic cmd We want commands to finish ASAP, so let's remove that udelay() call. Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/gadget.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index f148b11f6e4d..609acd7b1520 100644 --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c @@ -225,8 +225,6 @@ int dwc3_send_gadget_generic_command(struct dwc3 *dwc, unsigned cmd, u32 param) ret = -EINVAL; break; } - - udelay(1); } while (timeout--); if (!timeout) { From 71f7e7027028d5a8ef15dccc587dbd6c6b7f544f Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Mon, 23 May 2016 14:16:19 +0300 Subject: [PATCH 034/109] usb: dwc3: gadget: improve gcmd trace Just like we did for endpoint commands, let's have a single trace output for the command and its status. This will improve trace readability Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/debug.h | 14 ++++++++++++++ drivers/usb/dwc3/gadget.c | 14 ++++++-------- drivers/usb/dwc3/trace.h | 15 +++++++++------ 3 files changed, 29 insertions(+), 14 deletions(-) diff --git a/drivers/usb/dwc3/debug.h b/drivers/usb/dwc3/debug.h index 8eed4c7cc76b..22dfc3dd6a13 100644 --- a/drivers/usb/dwc3/debug.h +++ b/drivers/usb/dwc3/debug.h @@ -296,6 +296,20 @@ static inline const char *dwc3_ep_cmd_status_string(int status) } } +static inline const char *dwc3_gadget_generic_cmd_status_string(int status) +{ + switch (status) { + case -ETIMEDOUT: + return "Timed Out"; + case 0: + return "Successful"; + case 1: + return "Error"; + default: + return "UNKNOWN"; + } +} + void dwc3_trace(void (*trace)(struct va_format *), const char *fmt, ...); #ifdef CONFIG_DEBUG_FS diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index 609acd7b1520..6a18b3d0dccf 100644 --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c @@ -207,32 +207,30 @@ void dwc3_gadget_giveback(struct dwc3_ep *dep, struct dwc3_request *req, int dwc3_send_gadget_generic_command(struct dwc3 *dwc, unsigned cmd, u32 param) { u32 timeout = 500; + int status = 0; int ret = 0; u32 reg; - trace_dwc3_gadget_generic_cmd(cmd, param); - dwc3_writel(dwc->regs, DWC3_DGCMDPAR, param); dwc3_writel(dwc->regs, DWC3_DGCMD, cmd | DWC3_DGCMD_CMDACT); do { reg = dwc3_readl(dwc->regs, DWC3_DGCMD); if (!(reg & DWC3_DGCMD_CMDACT)) { - dwc3_trace(trace_dwc3_gadget, - "Command Complete --> %d", - DWC3_DGCMD_STATUS(reg)); - if (DWC3_DGCMD_STATUS(reg)) + status = DWC3_DGCMD_STATUS(reg); + if (status) ret = -EINVAL; break; } } while (timeout--); if (!timeout) { - dwc3_trace(trace_dwc3_gadget, - "Command Timed Out"); ret = -ETIMEDOUT; + status = -ETIMEDOUT; } + trace_dwc3_gadget_generic_cmd(cmd, param, status); + return ret; } diff --git a/drivers/usb/dwc3/trace.h b/drivers/usb/dwc3/trace.h index 040f28b27d56..f43f9ebf7c3f 100644 --- a/drivers/usb/dwc3/trace.h +++ b/drivers/usb/dwc3/trace.h @@ -167,25 +167,28 @@ DEFINE_EVENT(dwc3_log_request, dwc3_gadget_giveback, ); DECLARE_EVENT_CLASS(dwc3_log_generic_cmd, - TP_PROTO(unsigned int cmd, u32 param), - TP_ARGS(cmd, param), + TP_PROTO(unsigned int cmd, u32 param, int status), + TP_ARGS(cmd, param, status), TP_STRUCT__entry( __field(unsigned int, cmd) __field(u32, param) + __field(int, status) ), TP_fast_assign( __entry->cmd = cmd; __entry->param = param; + __entry->status = status; ), - TP_printk("cmd '%s' [%d] param %08x", + TP_printk("cmd '%s' [%d] param %08x --> status: %s", dwc3_gadget_generic_cmd_string(__entry->cmd), - __entry->cmd, __entry->param + __entry->cmd, __entry->param, + dwc3_gadget_generic_cmd_status_string(__entry->status) ) ); DEFINE_EVENT(dwc3_log_generic_cmd, dwc3_gadget_generic_cmd, - TP_PROTO(unsigned int cmd, u32 param), - TP_ARGS(cmd, param) + TP_PROTO(unsigned int cmd, u32 param, int status), + TP_ARGS(cmd, param, status) ); DECLARE_EVENT_CLASS(dwc3_log_gadget_ep_cmd, From 958b9fa7f8cfd5799534e98ba3d05d96a5e7ccb9 Mon Sep 17 00:00:00 2001 From: John Youn Date: Mon, 23 May 2016 11:32:38 -0700 Subject: [PATCH 035/109] usb: dwc3: ep0: Fix endianness of wIndex passed to dwc3_wIndex_to_dep The wIndex passed in here is CPU endianness, but the function expects little endian. Found with sparse. Signed-off-by: John Youn Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/ep0.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/usb/dwc3/ep0.c b/drivers/usb/dwc3/ep0.c index 54628c37b21f..c814dde27138 100644 --- a/drivers/usb/dwc3/ep0.c +++ b/drivers/usb/dwc3/ep0.c @@ -496,7 +496,7 @@ static int dwc3_ep0_handle_feature(struct dwc3 *dwc, case USB_RECIP_ENDPOINT: switch (wValue) { case USB_ENDPOINT_HALT: - dep = dwc3_wIndex_to_dep(dwc, wIndex); + dep = dwc3_wIndex_to_dep(dwc, ctrl->wIndex); if (!dep) return -EINVAL; if (set == 0 && (dep->flags & DWC3_EP_WEDGE)) From 501058edebf957a3c101c8119c3286e496873840 Mon Sep 17 00:00:00 2001 From: John Youn Date: Mon, 23 May 2016 11:32:40 -0700 Subject: [PATCH 036/109] usb: dwc3: ep0: Use the correct type for SET_SEL data u2sel and u2pel should be __le16. Doesn't fix any issue. Found with sparse. Signed-off-by: John Youn Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/ep0.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/usb/dwc3/ep0.c b/drivers/usb/dwc3/ep0.c index c814dde27138..93d6cdd9e0c9 100644 --- a/drivers/usb/dwc3/ep0.c +++ b/drivers/usb/dwc3/ep0.c @@ -619,8 +619,8 @@ static void dwc3_ep0_set_sel_cmpl(struct usb_ep *ep, struct usb_request *req) struct timing { u8 u1sel; u8 u1pel; - u16 u2sel; - u16 u2pel; + __le16 u2sel; + __le16 u2pel; } __packed timing; int ret; From d07fa665c79d85fead080f4b611c3f7645576454 Mon Sep 17 00:00:00 2001 From: John Youn Date: Mon, 23 May 2016 11:32:43 -0700 Subject: [PATCH 037/109] usb: dwc3: gadget: Fix usage of bitwise operator Cleans up the sparse warning: warning: dubious: x | !y Since we do want a bitwise OR here, don't use a logical (true/false) value. Probably is not a real issue but it cleans up the warning. Signed-off-by: John Youn Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/gadget.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index 6a18b3d0dccf..f38db3b79d3f 100644 --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c @@ -1798,7 +1798,7 @@ static int dwc3_gadget_init_hw_endpoints(struct dwc3 *dwc, u8 i; for (i = 0; i < num; i++) { - u8 epnum = (i << 1) | (!!direction); + u8 epnum = (i << 1) | (direction ? 1 : 0); dep = kzalloc(sizeof(*dep), GFP_KERNEL); if (!dep) From 96bedb637a1e1ad8e7ed781fa020550aae2a8f16 Mon Sep 17 00:00:00 2001 From: John Youn Date: Mon, 23 May 2016 11:32:47 -0700 Subject: [PATCH 038/109] usb: dwc3: Endianness issue on dwc3_log_ctrl Sparse complains even though it looks ok. Probably it cannot detect that the wValue, wIndex, and wLength are declared __le16 due to the macro magic. Redeclare them as CPU endianness and make the conversion on assignment. Signed-off-by: John Youn Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/trace.h | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/drivers/usb/dwc3/trace.h b/drivers/usb/dwc3/trace.h index f43f9ebf7c3f..cafd69fc7eb8 100644 --- a/drivers/usb/dwc3/trace.h +++ b/drivers/usb/dwc3/trace.h @@ -86,21 +86,21 @@ DECLARE_EVENT_CLASS(dwc3_log_ctrl, TP_STRUCT__entry( __field(__u8, bRequestType) __field(__u8, bRequest) - __field(__le16, wValue) - __field(__le16, wIndex) - __field(__le16, wLength) + __field(__u16, wValue) + __field(__u16, wIndex) + __field(__u16, wLength) ), TP_fast_assign( __entry->bRequestType = ctrl->bRequestType; __entry->bRequest = ctrl->bRequest; - __entry->wValue = ctrl->wValue; - __entry->wIndex = ctrl->wIndex; - __entry->wLength = ctrl->wLength; + __entry->wValue = le16_to_cpu(ctrl->wValue); + __entry->wIndex = le16_to_cpu(ctrl->wIndex); + __entry->wLength = le16_to_cpu(ctrl->wLength); ), TP_printk("bRequestType %02x bRequest %02x wValue %04x wIndex %04x wLength %d", __entry->bRequestType, __entry->bRequest, - le16_to_cpu(__entry->wValue), le16_to_cpu(__entry->wIndex), - le16_to_cpu(__entry->wLength) + __entry->wValue, __entry->wIndex, + __entry->wLength ) ); From dca0119c3ab69fea2429ec2db7e3686614bd1439 Mon Sep 17 00:00:00 2001 From: John Youn Date: Thu, 19 May 2016 17:26:05 -0700 Subject: [PATCH 039/109] usb: dwc3: gadget: Simplify skipping of link TRBs Make the skipping of the link TRBS built-in to the increment operation. This simplifies the code wherever we increment the trb index and ensures that we never end up pointing to a link trb. Signed-off-by: John Youn Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/gadget.c | 36 +++++++++++++++++------------------- 1 file changed, 17 insertions(+), 19 deletions(-) diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index f38db3b79d3f..24bb94258bb8 100644 --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c @@ -145,21 +145,29 @@ int dwc3_gadget_set_link_state(struct dwc3 *dwc, enum dwc3_link_state state) return -ETIMEDOUT; } +/** + * dwc3_ep_inc_trb() - Increment a TRB index. + * @index - Pointer to the TRB index to increment. + * + * The index should never point to the link TRB. After incrementing, + * if it is point to the link TRB, wrap around to the beginning. The + * link TRB is always at the last TRB entry. + */ +static void dwc3_ep_inc_trb(u8 *index) +{ + (*index)++; + if (*index == (DWC3_TRB_NUM - 1)) + *index = 0; +} + static void dwc3_ep_inc_enq(struct dwc3_ep *dep) { - dep->trb_enqueue++; - dep->trb_enqueue %= DWC3_TRB_NUM; + dwc3_ep_inc_trb(&dep->trb_enqueue); } static void dwc3_ep_inc_deq(struct dwc3_ep *dep) { - dep->trb_dequeue++; - dep->trb_dequeue %= DWC3_TRB_NUM; -} - -static int dwc3_ep_is_last_trb(unsigned int index) -{ - return index == DWC3_TRB_NUM - 1; + dwc3_ep_inc_trb(&dep->trb_dequeue); } void dwc3_gadget_giveback(struct dwc3_ep *dep, struct dwc3_request *req, @@ -172,13 +180,6 @@ void dwc3_gadget_giveback(struct dwc3_ep *dep, struct dwc3_request *req, i = 0; do { dwc3_ep_inc_deq(dep); - /* - * Skip LINK TRB. We can't use req->trb and check for - * DWC3_TRBCTL_LINK_TRB because it points the TRB we - * just completed (not the LINK TRB). - */ - if (dwc3_ep_is_last_trb(dep->trb_dequeue)) - dwc3_ep_inc_deq(dep); } while(++i < req->request.num_mapped_sgs); req->started = false; } @@ -785,9 +786,6 @@ static void dwc3_prepare_one_trb(struct dwc3_ep *dep, } dwc3_ep_inc_enq(dep); - /* Skip the LINK-TRB */ - if (dwc3_ep_is_last_trb(dep->trb_enqueue)) - dwc3_ep_inc_enq(dep); trb->size = DWC3_TRB_SIZE_LENGTH(length); trb->bpl = lower_32_bits(dma); From 0d25744ad107b6c9010153f3ebf8b59672b5b8e7 Mon Sep 17 00:00:00 2001 From: John Youn Date: Thu, 19 May 2016 17:26:08 -0700 Subject: [PATCH 040/109] usb: dwc3: gadget: Initialize the TRB ring Clears out all the TRBs in the ring to clean up any stale data that might be in them from the previous time the endpoint was enabled. Also removed the existing clear of the LINK trb since the entire ring is cleard just before. Signed-off-by: John Youn Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/gadget.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index 24bb94258bb8..6408f7ff8734 100644 --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c @@ -585,12 +585,16 @@ static int __dwc3_gadget_ep_enable(struct dwc3_ep *dep, if (usb_endpoint_xfer_control(desc)) return 0; + /* Initialize the TRB ring */ + dep->trb_dequeue = 0; + dep->trb_enqueue = 0; + memset(dep->trb_pool, 0, + sizeof(struct dwc3_trb) * DWC3_TRB_NUM); + /* Link TRB. The HWO bit is never reset */ trb_st_hw = &dep->trb_pool[0]; trb_link = &dep->trb_pool[DWC3_TRB_NUM - 1]; - memset(trb_link, 0, sizeof(*trb_link)); - trb_link->bpl = lower_32_bits(dwc3_trb_dma_offset(dep, trb_st_hw)); trb_link->bph = upper_32_bits(dwc3_trb_dma_offset(dep, trb_st_hw)); trb_link->ctrl |= DWC3_TRBCTL_LINK_TRB; From 89bc856e5a7462e47d90d5a3f5396bd5795efd0c Mon Sep 17 00:00:00 2001 From: John Youn Date: Thu, 19 May 2016 17:26:10 -0700 Subject: [PATCH 041/109] usb: dwc3: gadget: Don't prepare TRBs if no space If trbs_left == 0, we don't have any space left in the TRB ring so don't prepare anything. Signed-off-by: John Youn Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/gadget.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index 6408f7ff8734..663ea9ed5993 100644 --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c @@ -942,6 +942,8 @@ static void dwc3_prepare_trbs(struct dwc3_ep *dep) BUILD_BUG_ON_NOT_POWER_OF_2(DWC3_TRB_NUM); trbs_left = dwc3_calc_trbs_left(dep); + if (!trbs_left) + return; list_for_each_entry_safe(req, n, &dep->pending_list, list) { if (req->request.num_mapped_sgs > 0) From 32db3d9437b6bd560daeef82a8325436a4ac3366 Mon Sep 17 00:00:00 2001 From: John Youn Date: Thu, 19 May 2016 17:26:12 -0700 Subject: [PATCH 042/109] usb: dwc3: gadget: Account for max size in TRB space The current calculation takes dep->trb_dequeue - dep->trb_enqueue to find the TRB space left. If you enqueue 1, that results in: (u8) 0 - (u8) 1 = 0xff = 255 TRBs left. This is correct if DWC3_TRB_NUM == 256. If DWC3_TRB_NUM is less than 256 (but still a power of 2) you need to mod the result by DWC3_TRB_NUM. For example the same calculation with DWC3_TRB_NUM = 8, results in: 255 % 6 = 7 TRBs left. Signed-off-by: John Youn Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/gadget.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index 663ea9ed5993..4dea3e08f95c 100644 --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c @@ -845,6 +845,7 @@ static void dwc3_prepare_one_trb(struct dwc3_ep *dep, static u32 dwc3_calc_trbs_left(struct dwc3_ep *dep) { struct dwc3_trb *tmp; + u8 trbs_left; /* * If enqueue & dequeue are equal than it is either full or empty. @@ -864,7 +865,10 @@ static u32 dwc3_calc_trbs_left(struct dwc3_ep *dep) return DWC3_TRB_NUM - 1; } - return dep->trb_dequeue - dep->trb_enqueue; + trbs_left = dep->trb_dequeue - dep->trb_enqueue; + trbs_left %= DWC3_TRB_NUM; + + return trbs_left; } static void dwc3_prepare_one_trb_sg(struct dwc3_ep *dep, From 7d0a038b130cde7265d6bbc5e148734cb127f341 Mon Sep 17 00:00:00 2001 From: John Youn Date: Thu, 19 May 2016 17:26:15 -0700 Subject: [PATCH 043/109] usb: dwc3: gadget: Account for link TRB in TRBs left The TRBs left calculation didn't account for the link TRB taking up one spot. If the trb_dequeue < trb_enqueue, then the result includes the link TRB slot so it must be adjusted. Signed-off-by: John Youn Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/gadget.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index 4dea3e08f95c..3c4fd4854317 100644 --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c @@ -868,6 +868,9 @@ static u32 dwc3_calc_trbs_left(struct dwc3_ep *dep) trbs_left = dep->trb_dequeue - dep->trb_enqueue; trbs_left %= DWC3_TRB_NUM; + if (dep->trb_dequeue < dep->trb_enqueue) + trbs_left--; + return trbs_left; } From 361572b5f7a95b341a92d34b9bf41f71bbdae34d Mon Sep 17 00:00:00 2001 From: John Youn Date: Thu, 19 May 2016 17:26:17 -0700 Subject: [PATCH 044/109] usb: dwc3: gadget: Handle TRB index 0 when full or empty If the trb->enqueue == trb->dequeue, then it could be full or empty. This could also happen at TRB index 0, so modify the check to handle that condition. At index 0, the previous TRB is the one just before the link TRB. Signed-off-by: John Youn Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/gadget.c | 28 ++++++++++++++++++++++------ 1 file changed, 22 insertions(+), 6 deletions(-) diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index 3c4fd4854317..fbc796892f6b 100644 --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c @@ -842,6 +842,25 @@ static void dwc3_prepare_one_trb(struct dwc3_ep *dep, trace_dwc3_prepare_trb(dep, trb); } +/** + * dwc3_ep_prev_trb() - Returns the previous TRB in the ring + * @dep: The endpoint with the TRB ring + * @index: The index of the current TRB in the ring + * + * Returns the TRB prior to the one pointed to by the index. If the + * index is 0, we will wrap backwards, skip the link TRB, and return + * the one just before that. + */ +static struct dwc3_trb *dwc3_ep_prev_trb(struct dwc3_ep *dep, u8 index) +{ + if (!index) + index = DWC3_TRB_NUM - 2; + else + index = dep->trb_enqueue - 1; + + return &dep->trb_pool[index]; +} + static u32 dwc3_calc_trbs_left(struct dwc3_ep *dep) { struct dwc3_trb *tmp; @@ -855,12 +874,9 @@ static u32 dwc3_calc_trbs_left(struct dwc3_ep *dep) * more transfers in our ring. */ if (dep->trb_enqueue == dep->trb_dequeue) { - /* If we're full, enqueue/dequeue are > 0 */ - if (dep->trb_enqueue) { - tmp = &dep->trb_pool[dep->trb_enqueue - 1]; - if (tmp->ctrl & DWC3_TRB_CTRL_HWO) - return 0; - } + tmp = dwc3_ep_prev_trb(dep, dep->trb_enqueue); + if (tmp->ctrl & DWC3_TRB_CTRL_HWO) + return 0; return DWC3_TRB_NUM - 1; } From 3de2685f0c395a56b909dbefd40fb287c9df31b2 Mon Sep 17 00:00:00 2001 From: John Youn Date: Mon, 23 May 2016 11:32:45 -0700 Subject: [PATCH 045/109] usb: dwc3: gadget: Fix truncated cast issue From sparse: warning: cast truncates bits from constant value (100 becomes 0) The DWC3_TRB_NUM constant is too big for u8. Do the calculation a slightly different way that should still be optimized out for the case where DWC3_TRB_NUM == 256. Signed-off-by: John Youn Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/gadget.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index fbc796892f6b..d0f74583c955 100644 --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c @@ -882,7 +882,7 @@ static u32 dwc3_calc_trbs_left(struct dwc3_ep *dep) } trbs_left = dep->trb_dequeue - dep->trb_enqueue; - trbs_left %= DWC3_TRB_NUM; + trbs_left &= (DWC3_TRB_NUM - 1); if (dep->trb_dequeue < dep->trb_enqueue) trbs_left--; From 2da9ad761e70c6a22e3a84f9a3e67faeef122a58 Mon Sep 17 00:00:00 2001 From: John Youn Date: Fri, 20 May 2016 16:34:26 -0700 Subject: [PATCH 046/109] usb: dwc3: Use the correct speed macros for DSTS/DCFG Correct the use of the DWC3_DSTS_XXX_SPEED and DWC3_DCFG_XXX_SPEED macros. The wrong set of macros were being used in a few places. This is only a cosmetic change as the values for both sets are identical. Signed-off-by: John Youn Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/gadget.c | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index d0f74583c955..0c11fd6e305e 100644 --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c @@ -1672,16 +1672,16 @@ static int __dwc3_gadget_start(struct dwc3 *dwc) } else { switch (dwc->maximum_speed) { case USB_SPEED_LOW: - reg |= DWC3_DSTS_LOWSPEED; + reg |= DWC3_DCFG_LOWSPEED; break; case USB_SPEED_FULL: - reg |= DWC3_DSTS_FULLSPEED1; + reg |= DWC3_DCFG_FULLSPEED1; break; case USB_SPEED_HIGH: - reg |= DWC3_DSTS_HIGHSPEED; + reg |= DWC3_DCFG_HIGHSPEED; break; case USB_SPEED_SUPER_PLUS: - reg |= DWC3_DSTS_SUPERSPEED_PLUS; + reg |= DWC3_DCFG_SUPERSPEED_PLUS; break; default: dev_err(dwc->dev, "invalid dwc->maximum_speed (%d)\n", @@ -2459,12 +2459,12 @@ static void dwc3_gadget_conndone_interrupt(struct dwc3 *dwc) dwc3_update_ram_clk_sel(dwc, speed); switch (speed) { - case DWC3_DCFG_SUPERSPEED_PLUS: + case DWC3_DSTS_SUPERSPEED_PLUS: dwc3_gadget_ep0_desc.wMaxPacketSize = cpu_to_le16(512); dwc->gadget.ep0->maxpacket = 512; dwc->gadget.speed = USB_SPEED_SUPER_PLUS; break; - case DWC3_DCFG_SUPERSPEED: + case DWC3_DSTS_SUPERSPEED: /* * WORKAROUND: DWC3 revisions <1.90a have an issue which * would cause a missing USB3 Reset event. @@ -2485,18 +2485,18 @@ static void dwc3_gadget_conndone_interrupt(struct dwc3 *dwc) dwc->gadget.ep0->maxpacket = 512; dwc->gadget.speed = USB_SPEED_SUPER; break; - case DWC3_DCFG_HIGHSPEED: + case DWC3_DSTS_HIGHSPEED: dwc3_gadget_ep0_desc.wMaxPacketSize = cpu_to_le16(64); dwc->gadget.ep0->maxpacket = 64; dwc->gadget.speed = USB_SPEED_HIGH; break; - case DWC3_DCFG_FULLSPEED2: - case DWC3_DCFG_FULLSPEED1: + case DWC3_DSTS_FULLSPEED2: + case DWC3_DSTS_FULLSPEED1: dwc3_gadget_ep0_desc.wMaxPacketSize = cpu_to_le16(64); dwc->gadget.ep0->maxpacket = 64; dwc->gadget.speed = USB_SPEED_FULL; break; - case DWC3_DCFG_LOWSPEED: + case DWC3_DSTS_LOWSPEED: dwc3_gadget_ep0_desc.wMaxPacketSize = cpu_to_le16(8); dwc->gadget.ep0->maxpacket = 8; dwc->gadget.speed = USB_SPEED_LOW; @@ -2506,8 +2506,8 @@ static void dwc3_gadget_conndone_interrupt(struct dwc3 *dwc) /* Enable USB2 LPM Capability */ if ((dwc->revision > DWC3_REVISION_194A) && - (speed != DWC3_DCFG_SUPERSPEED) && - (speed != DWC3_DCFG_SUPERSPEED_PLUS)) { + (speed != DWC3_DSTS_SUPERSPEED) && + (speed != DWC3_DSTS_SUPERSPEED_PLUS)) { reg = dwc3_readl(dwc->regs, DWC3_DCFG); reg |= DWC3_DCFG_LPM_CAP; dwc3_writel(dwc->regs, DWC3_DCFG, reg); From e77c561432bccf5ea9df2f49aa039c015529590e Mon Sep 17 00:00:00 2001 From: John Youn Date: Fri, 20 May 2016 16:34:23 -0700 Subject: [PATCH 047/109] usb: dwc3: Fix DWC3_USB31_REVISION_110A definition The DWC3_USB31_REVISION_110A macro uses an invalid constant name in its definition. This is currently not used. Signed-off-by: John Youn Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/core.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h index 484bb5d8261c..243e7a1cdc8c 100644 --- a/drivers/usb/dwc3/core.h +++ b/drivers/usb/dwc3/core.h @@ -881,7 +881,7 @@ struct dwc3 { * just so dwc31 revisions are always larger than dwc3. */ #define DWC3_REVISION_IS_DWC31 0x80000000 -#define DWC3_USB31_REVISION_110A (0x3131302a | DWC3_REVISION_IS_USB31) +#define DWC3_USB31_REVISION_110A (0x3131302a | DWC3_REVISION_IS_DWC31) enum dwc3_ep0_next ep0_next_event; enum dwc3_ep0_state ep0state; From 475c8beb35e129c2f33182f476373db04008892e Mon Sep 17 00:00:00 2001 From: William Wu Date: Fri, 13 May 2016 18:13:46 +0800 Subject: [PATCH 048/109] usb: dwc3: add DWC3_GUCTL1 reg for debug GUCTL1 reg has some useful functions which can be written by user. For rockchip platform, we set GUCTL1.DEV_FORCE_20_CLK_FOR_30_CLK (bit26, applicable for the core is programmed to operate in 2.0 device only) to 1 in bootrom, and after start the kernel, we want to check whether this bit can be reset to default 0 after the core reset. Dump GUCTL1 reg from debugfs is more convenient for us. Signed-off-by: William Wu Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/core.h | 1 + drivers/usb/dwc3/debugfs.c | 1 + 2 files changed, 2 insertions(+) diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h index 243e7a1cdc8c..dcdba14e10f0 100644 --- a/drivers/usb/dwc3/core.h +++ b/drivers/usb/dwc3/core.h @@ -86,6 +86,7 @@ #define DWC3_GCTL 0xc110 #define DWC3_GEVTEN 0xc114 #define DWC3_GSTS 0xc118 +#define DWC3_GUCTL1 0xc11c #define DWC3_GSNPSID 0xc120 #define DWC3_GGPIO 0xc124 #define DWC3_GUID 0xc128 diff --git a/drivers/usb/dwc3/debugfs.c b/drivers/usb/dwc3/debugfs.c index 89c26e09870c..31926dda43c9 100644 --- a/drivers/usb/dwc3/debugfs.c +++ b/drivers/usb/dwc3/debugfs.c @@ -70,6 +70,7 @@ static const struct debugfs_reg32 dwc3_regs[] = { dump_register(GCTL), dump_register(GEVTEN), dump_register(GSTS), + dump_register(GUCTL1), dump_register(GSNPSID), dump_register(GGPIO), dump_register(GUID), From 3f586c92d87778bc3d550435a31f8ac685541121 Mon Sep 17 00:00:00 2001 From: Roger Quadros Date: Wed, 11 May 2016 17:36:42 +0300 Subject: [PATCH 049/109] usb: dwc3: omap: use request_threaded_irq() We intend to share this interrupt with the OTG driver an to ensure that irqflags match for the shared interrupt handlers we use request_threaded_irq() If we don't use request_treaded_irq() then forced threaded irq will set IRQF_ONESHOT and this won't match with the OTG IRQ handler's IRQ flags. NOTE: OTG IRQ handler is yet to be added. This is a preparatory step. Signed-off-by: Roger Quadros Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/dwc3-omap.c | 32 ++++++++++++++++++++++++++------ 1 file changed, 26 insertions(+), 6 deletions(-) diff --git a/drivers/usb/dwc3/dwc3-omap.c b/drivers/usb/dwc3/dwc3-omap.c index af264493bbae..0142544d061f 100644 --- a/drivers/usb/dwc3/dwc3-omap.c +++ b/drivers/usb/dwc3/dwc3-omap.c @@ -165,7 +165,7 @@ static void dwc3_omap_write_utmi_ctrl(struct dwc3_omap *omap, u32 value) static u32 dwc3_omap_read_irq0_status(struct dwc3_omap *omap) { - return dwc3_omap_readl(omap->base, USBOTGSS_IRQSTATUS_0 - + return dwc3_omap_readl(omap->base, USBOTGSS_IRQSTATUS_RAW_0 - omap->irq0_offset); } @@ -178,7 +178,7 @@ static void dwc3_omap_write_irq0_status(struct dwc3_omap *omap, u32 value) static u32 dwc3_omap_read_irqmisc_status(struct dwc3_omap *omap) { - return dwc3_omap_readl(omap->base, USBOTGSS_IRQSTATUS_MISC + + return dwc3_omap_readl(omap->base, USBOTGSS_IRQSTATUS_RAW_MISC + omap->irqmisc_offset); } @@ -268,19 +268,38 @@ static void dwc3_omap_set_mailbox(struct dwc3_omap *omap, } } +static void dwc3_omap_enable_irqs(struct dwc3_omap *omap); +static void dwc3_omap_disable_irqs(struct dwc3_omap *omap); + static irqreturn_t dwc3_omap_interrupt(int irq, void *_omap) +{ + struct dwc3_omap *omap = _omap; + + if (dwc3_omap_read_irqmisc_status(omap) || + dwc3_omap_read_irq0_status(omap)) { + /* mask irqs */ + dwc3_omap_disable_irqs(omap); + return IRQ_WAKE_THREAD; + } + + return IRQ_NONE; +} + +static irqreturn_t dwc3_omap_interrupt_thread(int irq, void *_omap) { struct dwc3_omap *omap = _omap; u32 reg; + /* clear irq status flags */ reg = dwc3_omap_read_irqmisc_status(omap); - dwc3_omap_write_irqmisc_status(omap, reg); reg = dwc3_omap_read_irq0_status(omap); - dwc3_omap_write_irq0_status(omap, reg); + /* unmask irqs */ + dwc3_omap_enable_irqs(omap); + return IRQ_HANDLED; } @@ -497,8 +516,9 @@ static int dwc3_omap_probe(struct platform_device *pdev) /* check the DMA Status */ reg = dwc3_omap_readl(omap->base, USBOTGSS_SYSCONFIG); - ret = devm_request_irq(dev, omap->irq, dwc3_omap_interrupt, 0, - "dwc3-omap", omap); + ret = devm_request_threaded_irq(dev, omap->irq, dwc3_omap_interrupt, + dwc3_omap_interrupt_thread, 0, + "dwc3-omap", omap); if (ret) { dev_err(dev, "failed to request IRQ #%d --> %d\n", omap->irq, ret); From 12da8eae208d6f388c3aeecb91955c8b5a468654 Mon Sep 17 00:00:00 2001 From: Roger Quadros Date: Wed, 11 May 2016 17:36:43 +0300 Subject: [PATCH 050/109] usb: dwc3: omap: Mark the interrupt handler as shared On OMAPs, OTG events come on the same IRQ so we need to share this IRQ with the OTG device driver. Signed-off-by: Roger Quadros Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/dwc3-omap.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/usb/dwc3/dwc3-omap.c b/drivers/usb/dwc3/dwc3-omap.c index 0142544d061f..bde69fc3fe77 100644 --- a/drivers/usb/dwc3/dwc3-omap.c +++ b/drivers/usb/dwc3/dwc3-omap.c @@ -517,7 +517,7 @@ static int dwc3_omap_probe(struct platform_device *pdev) reg = dwc3_omap_readl(omap->base, USBOTGSS_SYSCONFIG); ret = devm_request_threaded_irq(dev, omap->irq, dwc3_omap_interrupt, - dwc3_omap_interrupt_thread, 0, + dwc3_omap_interrupt_thread, IRQF_SHARED, "dwc3-omap", omap); if (ret) { dev_err(dev, "failed to request IRQ #%d --> %d\n", From 9ab330bf4dfd677a19d03359af9bc0e168a2c4b2 Mon Sep 17 00:00:00 2001 From: Roger Quadros Date: Wed, 11 May 2016 17:36:44 +0300 Subject: [PATCH 051/109] usb: dwc3: omap: Don't set POWERPRESENT TRM [1] recommends that POWERPRESENT bit must not be set and left at it's default value of 0. [1] OMAP542x TRM - http://www.ti.com/lit/pdf/swpu249 Section 23.11.4.5.1 Mailbox VBUS/ID Management "Because PIPE powerpresent has a different meaning in host and in device mode, and because of the redundancy with the UTMI signals, the controller ORes together the appropriate PIPE and UTMI inputs to create its internal VBUS status. For that reason, it is recommended to leave field USBOTGSS_UTMI_OTG_STATUS[9] POWERPRESENT at its default value (=0), and only to fill in the USB2 VBUS status fields in the same register." Signed-off-by: Roger Quadros Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/dwc3-omap.c | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/drivers/usb/dwc3/dwc3-omap.c b/drivers/usb/dwc3/dwc3-omap.c index bde69fc3fe77..046bb379120e 100644 --- a/drivers/usb/dwc3/dwc3-omap.c +++ b/drivers/usb/dwc3/dwc3-omap.c @@ -234,8 +234,7 @@ static void dwc3_omap_set_mailbox(struct dwc3_omap *omap, val &= ~(USBOTGSS_UTMI_OTG_CTRL_IDDIG | USBOTGSS_UTMI_OTG_CTRL_VBUSVALID | USBOTGSS_UTMI_OTG_CTRL_SESSEND); - val |= USBOTGSS_UTMI_OTG_CTRL_SESSVALID - | USBOTGSS_UTMI_OTG_CTRL_POWERPRESENT; + val |= USBOTGSS_UTMI_OTG_CTRL_SESSVALID; dwc3_omap_write_utmi_ctrl(omap, val); break; @@ -244,8 +243,7 @@ static void dwc3_omap_set_mailbox(struct dwc3_omap *omap, val &= ~USBOTGSS_UTMI_OTG_CTRL_SESSEND; val |= USBOTGSS_UTMI_OTG_CTRL_IDDIG | USBOTGSS_UTMI_OTG_CTRL_VBUSVALID - | USBOTGSS_UTMI_OTG_CTRL_SESSVALID - | USBOTGSS_UTMI_OTG_CTRL_POWERPRESENT; + | USBOTGSS_UTMI_OTG_CTRL_SESSVALID; dwc3_omap_write_utmi_ctrl(omap, val); break; @@ -256,8 +254,7 @@ static void dwc3_omap_set_mailbox(struct dwc3_omap *omap, case OMAP_DWC3_VBUS_OFF: val = dwc3_omap_read_utmi_ctrl(omap); val &= ~(USBOTGSS_UTMI_OTG_CTRL_SESSVALID - | USBOTGSS_UTMI_OTG_CTRL_VBUSVALID - | USBOTGSS_UTMI_OTG_CTRL_POWERPRESENT); + | USBOTGSS_UTMI_OTG_CTRL_VBUSVALID); val |= USBOTGSS_UTMI_OTG_CTRL_SESSEND | USBOTGSS_UTMI_OTG_CTRL_IDDIG; dwc3_omap_write_utmi_ctrl(omap, val); From d2728fb3e01f9265571a5f7a5feeac4493d2a365 Mon Sep 17 00:00:00 2001 From: Roger Quadros Date: Wed, 11 May 2016 17:36:45 +0300 Subject: [PATCH 052/109] usb: dwc3: omap: Pass VBUS and ID events transparently Don't make any decisions regarding VBUS session based on ID status. That is best left to the OTG core. Pass ID and VBUS events independent of each other so that OTG core knows exactly what to do. This makes dual-role with extcon work with OTG irq on OMAP platforms. Signed-off-by: Roger Quadros Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/dwc3-omap.c | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/drivers/usb/dwc3/dwc3-omap.c b/drivers/usb/dwc3/dwc3-omap.c index 046bb379120e..29e80cc9b634 100644 --- a/drivers/usb/dwc3/dwc3-omap.c +++ b/drivers/usb/dwc3/dwc3-omap.c @@ -231,18 +231,14 @@ static void dwc3_omap_set_mailbox(struct dwc3_omap *omap, } val = dwc3_omap_read_utmi_ctrl(omap); - val &= ~(USBOTGSS_UTMI_OTG_CTRL_IDDIG - | USBOTGSS_UTMI_OTG_CTRL_VBUSVALID - | USBOTGSS_UTMI_OTG_CTRL_SESSEND); - val |= USBOTGSS_UTMI_OTG_CTRL_SESSVALID; + val &= ~USBOTGSS_UTMI_OTG_CTRL_IDDIG; dwc3_omap_write_utmi_ctrl(omap, val); break; case OMAP_DWC3_VBUS_VALID: val = dwc3_omap_read_utmi_ctrl(omap); val &= ~USBOTGSS_UTMI_OTG_CTRL_SESSEND; - val |= USBOTGSS_UTMI_OTG_CTRL_IDDIG - | USBOTGSS_UTMI_OTG_CTRL_VBUSVALID + val |= USBOTGSS_UTMI_OTG_CTRL_VBUSVALID | USBOTGSS_UTMI_OTG_CTRL_SESSVALID; dwc3_omap_write_utmi_ctrl(omap, val); break; @@ -250,13 +246,15 @@ static void dwc3_omap_set_mailbox(struct dwc3_omap *omap, case OMAP_DWC3_ID_FLOAT: if (omap->vbus_reg) regulator_disable(omap->vbus_reg); + val = dwc3_omap_read_utmi_ctrl(omap); + val |= USBOTGSS_UTMI_OTG_CTRL_IDDIG; + dwc3_omap_write_utmi_ctrl(omap, val); case OMAP_DWC3_VBUS_OFF: val = dwc3_omap_read_utmi_ctrl(omap); val &= ~(USBOTGSS_UTMI_OTG_CTRL_SESSVALID | USBOTGSS_UTMI_OTG_CTRL_VBUSVALID); - val |= USBOTGSS_UTMI_OTG_CTRL_SESSEND - | USBOTGSS_UTMI_OTG_CTRL_IDDIG; + val |= USBOTGSS_UTMI_OTG_CTRL_SESSEND; dwc3_omap_write_utmi_ctrl(omap, val); break; From 60cfb37ac9bc620334fff6e644a2b09019115939 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Tue, 24 May 2016 13:45:17 +0300 Subject: [PATCH 053/109] usb: dwc3: remove trailing newline from dwc3_trace when passing strings to trace, we don't need the trailing newline character. Trace already appends a newline character automatically. Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/ep0.c | 4 ++-- drivers/usb/dwc3/gadget.c | 32 ++++++++++++-------------------- 2 files changed, 14 insertions(+), 22 deletions(-) diff --git a/drivers/usb/dwc3/ep0.c b/drivers/usb/dwc3/ep0.c index 93d6cdd9e0c9..fe79d771dee4 100644 --- a/drivers/usb/dwc3/ep0.c +++ b/drivers/usb/dwc3/ep0.c @@ -977,7 +977,7 @@ static void __dwc3_ep0_do_control_data(struct dwc3 *dwc, ret = usb_gadget_map_request(&dwc->gadget, &req->request, dep->number); if (ret) { - dwc3_trace(trace_dwc3_ep0, "failed to map request\n"); + dwc3_trace(trace_dwc3_ep0, "failed to map request"); return; } @@ -1005,7 +1005,7 @@ static void __dwc3_ep0_do_control_data(struct dwc3 *dwc, ret = usb_gadget_map_request(&dwc->gadget, &req->request, dep->number); if (ret) { - dwc3_trace(trace_dwc3_ep0, "failed to map request\n"); + dwc3_trace(trace_dwc3_ep0, "failed to map request"); return; } diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index 0c11fd6e305e..61edb6650e2f 100644 --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c @@ -289,16 +289,11 @@ int dwc3_send_gadget_ep_cmd(struct dwc3_ep *dep, unsigned cmd, if (!(reg & DWC3_DEPCMD_CMDACT)) { cmd_status = DWC3_DEPCMD_STATUS(reg); - dwc3_trace(trace_dwc3_gadget, - "Command Complete --> %d", - cmd_status); - switch (cmd_status) { case 0: ret = 0; break; case DEPEVT_TRANSFER_NO_RESOURCE: - dwc3_trace(trace_dwc3_gadget, "no resource available"); ret = -EINVAL; break; case DEPEVT_TRANSFER_BUS_EXPIRY: @@ -313,7 +308,6 @@ int dwc3_send_gadget_ep_cmd(struct dwc3_ep *dep, unsigned cmd, * give a hint to the gadget driver that this is * the case by returning -EAGAIN. */ - dwc3_trace(trace_dwc3_gadget, "bus expiry"); ret = -EAGAIN; break; default: @@ -325,8 +319,6 @@ int dwc3_send_gadget_ep_cmd(struct dwc3_ep *dep, unsigned cmd, } while (--timeout); if (timeout == 0) { - dwc3_trace(trace_dwc3_gadget, - "Command Timed Out"); ret = -ETIMEDOUT; cmd_status = -ETIMEDOUT; } @@ -1068,14 +1060,14 @@ static int __dwc3_gadget_ep_queue(struct dwc3_ep *dep, struct dwc3_request *req) if (!dep->endpoint.desc) { dwc3_trace(trace_dwc3_gadget, - "trying to queue request %p to disabled %s\n", + "trying to queue request %p to disabled %s", &req->request, dep->endpoint.name); return -ESHUTDOWN; } if (WARN(req->dep != dep, "request %p belongs to '%s'\n", &req->request, req->dep->name)) { - dwc3_trace(trace_dwc3_gadget, "request %p belongs to '%s'\n", + dwc3_trace(trace_dwc3_gadget, "request %p belongs to '%s'", &req->request, req->dep->name); return -EINVAL; } @@ -1179,7 +1171,7 @@ static int __dwc3_gadget_ep_queue(struct dwc3_ep *dep, struct dwc3_request *req) out: if (ret && ret != -EBUSY) dwc3_trace(trace_dwc3_gadget, - "%s: failed to kick transfers\n", + "%s: failed to kick transfers", dep->name); if (ret == -EBUSY) ret = 0; @@ -1199,7 +1191,7 @@ static int __dwc3_gadget_ep_queue_zlp(struct dwc3 *dwc, struct dwc3_ep *dep) struct usb_request *request; struct usb_ep *ep = &dep->endpoint; - dwc3_trace(trace_dwc3_gadget, "queueing ZLP\n"); + dwc3_trace(trace_dwc3_gadget, "queueing ZLP"); request = dwc3_gadget_ep_alloc_request(ep, GFP_ATOMIC); if (!request) return -ENOMEM; @@ -1429,7 +1421,7 @@ static int __dwc3_gadget_wakeup(struct dwc3 *dwc) speed = reg & DWC3_DSTS_CONNECTSPD; if ((speed == DWC3_DSTS_SUPERSPEED) || (speed == DWC3_DSTS_SUPERSPEED_PLUS)) { - dwc3_trace(trace_dwc3_gadget, "no wakeup on SuperSpeed\n"); + dwc3_trace(trace_dwc3_gadget, "no wakeup on SuperSpeed"); return 0; } @@ -1441,7 +1433,7 @@ static int __dwc3_gadget_wakeup(struct dwc3 *dwc) break; default: dwc3_trace(trace_dwc3_gadget, - "can't wakeup from '%s'\n", + "can't wakeup from '%s'", dwc3_gadget_link_string(link_state)); return -EINVAL; } @@ -1963,7 +1955,7 @@ static int __dwc3_cleanup_done_trbs(struct dwc3 *dwc, struct dwc3_ep *dep, trb_status = DWC3_TRB_SIZE_TRBSTS(trb->size); if (trb_status == DWC3_TRBSTS_MISSED_ISOC) { dwc3_trace(trace_dwc3_gadget, - "%s: incomplete IN transfer\n", + "%s: incomplete IN transfer", dep->name); /* * If missed isoc occurred and there is @@ -2161,7 +2153,7 @@ static void dwc3_endpoint_interrupt(struct dwc3 *dwc, if (usb_endpoint_xfer_isoc(dep->endpoint.desc)) { dwc3_trace(trace_dwc3_gadget, - "%s is an Isochronous endpoint\n", + "%s is an Isochronous endpoint", dep->name); return; } @@ -2189,7 +2181,7 @@ static void dwc3_endpoint_interrupt(struct dwc3 *dwc, return; dwc3_trace(trace_dwc3_gadget, - "%s: failed to kick transfers\n", + "%s: failed to kick transfers", dep->name); } @@ -2212,11 +2204,11 @@ static void dwc3_endpoint_interrupt(struct dwc3 *dwc, /* FALLTHROUGH */ default: dwc3_trace(trace_dwc3_gadget, - "unable to find suitable stream\n"); + "unable to find suitable stream"); } break; case DWC3_DEPEVT_RXTXFIFOEVT: - dwc3_trace(trace_dwc3_gadget, "%s FIFO Overrun\n", dep->name); + dwc3_trace(trace_dwc3_gadget, "%s FIFO Overrun", dep->name); break; case DWC3_DEPEVT_EPCMDCMPLT: dwc3_trace(trace_dwc3_gadget, "Endpoint Command Complete"); @@ -2934,7 +2926,7 @@ int dwc3_gadget_init(struct dwc3 *dwc) */ if (dwc->revision < DWC3_REVISION_220A) dwc3_trace(trace_dwc3_gadget, - "Changing max_speed on rev %08x\n", + "Changing max_speed on rev %08x", dwc->revision); dwc->gadget.max_speed = dwc->maximum_speed; From cf48305de0c6b556ff6af64e93295cc54c4fe246 Mon Sep 17 00:00:00 2001 From: Heikki Krogerus Date: Fri, 22 Apr 2016 11:17:39 +0300 Subject: [PATCH 054/109] usb: dwc3: pci: use build-in properties instead of platform data This should allow the core driver to drop handling of platform data and expect the platform specific details to always come from properties. Tested-by: John Youn Signed-off-by: Heikki Krogerus Cc: Huang Rui CC: John Youn Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/dwc3-pci.c | 66 +++++++++++++++++-------------------- 1 file changed, 30 insertions(+), 36 deletions(-) diff --git a/drivers/usb/dwc3/dwc3-pci.c b/drivers/usb/dwc3/dwc3-pci.c index a7b6a1c8bc81..6bc4c2b08ac7 100644 --- a/drivers/usb/dwc3/dwc3-pci.c +++ b/drivers/usb/dwc3/dwc3-pci.c @@ -24,8 +24,7 @@ #include #include #include - -#include "platform_data.h" +#include #define PCI_DEVICE_ID_SYNOPSYS_HAPSUSB3 0xabcd #define PCI_DEVICE_ID_SYNOPSYS_HAPSUSB3_AXI 0xabce @@ -52,33 +51,29 @@ static int dwc3_pci_quirks(struct pci_dev *pdev, struct platform_device *dwc3) { if (pdev->vendor == PCI_VENDOR_ID_AMD && pdev->device == PCI_DEVICE_ID_AMD_NL_USB) { - struct dwc3_platform_data pdata; + struct property_entry properties[] = { + PROPERTY_ENTRY_BOOL("snps,has-lpm-erratum"), + PROPERTY_ENTRY_U8("snps,lpm-nyet-threshold", 0xf), + PROPERTY_ENTRY_BOOL("snps,u2exit_lfps_quirk"), + PROPERTY_ENTRY_BOOL("snps,u2ss_inp3_quirk"), + PROPERTY_ENTRY_BOOL("snps,req_p1p2p3_quirk"), + PROPERTY_ENTRY_BOOL("snps,del_p1p2p3_quirk"), + PROPERTY_ENTRY_BOOL("snps,del_phy_power_chg_quirk"), + PROPERTY_ENTRY_BOOL("snps,lfps_filter_quirk"), + PROPERTY_ENTRY_BOOL("snps,rx_detect_poll_quirk"), + PROPERTY_ENTRY_BOOL("snps,tx_de_emphasis_quirk"), + PROPERTY_ENTRY_U8("snps,tx_de_emphasis", 1), + /* + * FIXME these quirks should be removed when AMD NL + * tapes out + */ + PROPERTY_ENTRY_BOOL("snps,disable_scramble_quirk"), + PROPERTY_ENTRY_BOOL("snps,dis_u3_susphy_quirk"), + PROPERTY_ENTRY_BOOL("snps,dis_u2_susphy_quirk"), + { }, + }; - memset(&pdata, 0, sizeof(pdata)); - - pdata.has_lpm_erratum = true; - pdata.lpm_nyet_threshold = 0xf; - - pdata.u2exit_lfps_quirk = true; - pdata.u2ss_inp3_quirk = true; - pdata.req_p1p2p3_quirk = true; - pdata.del_p1p2p3_quirk = true; - pdata.del_phy_power_chg_quirk = true; - pdata.lfps_filter_quirk = true; - pdata.rx_detect_poll_quirk = true; - - pdata.tx_de_emphasis_quirk = true; - pdata.tx_de_emphasis = 1; - - /* - * FIXME these quirks should be removed when AMD NL - * taps out - */ - pdata.disable_scramble_quirk = true; - pdata.dis_u3_susphy_quirk = true; - pdata.dis_u2_susphy_quirk = true; - - return platform_device_add_data(dwc3, &pdata, sizeof(pdata)); + return platform_device_add_properties(dwc3, properties); } if (pdev->vendor == PCI_VENDOR_ID_INTEL && @@ -115,15 +110,14 @@ static int dwc3_pci_quirks(struct pci_dev *pdev, struct platform_device *dwc3) (pdev->device == PCI_DEVICE_ID_SYNOPSYS_HAPSUSB3 || pdev->device == PCI_DEVICE_ID_SYNOPSYS_HAPSUSB3_AXI || pdev->device == PCI_DEVICE_ID_SYNOPSYS_HAPSUSB31)) { + struct property_entry properties[] = { + PROPERTY_ENTRY_BOOL("snps,usb3_lpm_capable"), + PROPERTY_ENTRY_BOOL("snps,has-lpm-erratum"), + PROPERTY_ENTRY_BOOL("snps,dis_enblslpm_quirk"), + { }, + }; - struct dwc3_platform_data pdata; - - memset(&pdata, 0, sizeof(pdata)); - pdata.usb3_lpm_capable = true; - pdata.has_lpm_erratum = true; - pdata.dis_enblslpm_quirk = true; - - return platform_device_add_data(dwc3, &pdata, sizeof(pdata)); + return platform_device_add_properties(dwc3, properties); } return 0; From d18e65470a4d9b6f7d7a72a0d102d009b45cf9d4 Mon Sep 17 00:00:00 2001 From: Heikki Krogerus Date: Fri, 22 Apr 2016 11:17:40 +0300 Subject: [PATCH 055/109] usb: dwc3: remove handling of platform data No more users for it. Tested-by: John Youn Signed-off-by: Heikki Krogerus Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/core.c | 35 --------------------- drivers/usb/dwc3/platform_data.h | 53 -------------------------------- 2 files changed, 88 deletions(-) delete mode 100644 drivers/usb/dwc3/platform_data.h diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c index bf1789f134ac..9c4e1d8d01db 100644 --- a/drivers/usb/dwc3/core.c +++ b/drivers/usb/dwc3/core.c @@ -41,7 +41,6 @@ #include #include -#include "platform_data.h" #include "core.h" #include "gadget.h" #include "io.h" @@ -825,7 +824,6 @@ static void dwc3_core_exit_mode(struct dwc3 *dwc) static int dwc3_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; - struct dwc3_platform_data *pdata = dev_get_platdata(dev); struct resource *res; struct dwc3 *dwc; u8 lpm_nyet_threshold; @@ -942,39 +940,6 @@ static int dwc3_probe(struct platform_device *pdev) device_property_read_u32(dev, "snps,quirk-frame-length-adjustment", &dwc->fladj); - if (pdata) { - dwc->maximum_speed = pdata->maximum_speed; - dwc->has_lpm_erratum = pdata->has_lpm_erratum; - if (pdata->lpm_nyet_threshold) - lpm_nyet_threshold = pdata->lpm_nyet_threshold; - dwc->is_utmi_l1_suspend = pdata->is_utmi_l1_suspend; - if (pdata->hird_threshold) - hird_threshold = pdata->hird_threshold; - - dwc->usb3_lpm_capable = pdata->usb3_lpm_capable; - dwc->dr_mode = pdata->dr_mode; - - dwc->disable_scramble_quirk = pdata->disable_scramble_quirk; - dwc->u2exit_lfps_quirk = pdata->u2exit_lfps_quirk; - dwc->u2ss_inp3_quirk = pdata->u2ss_inp3_quirk; - dwc->req_p1p2p3_quirk = pdata->req_p1p2p3_quirk; - dwc->del_p1p2p3_quirk = pdata->del_p1p2p3_quirk; - dwc->del_phy_power_chg_quirk = pdata->del_phy_power_chg_quirk; - dwc->lfps_filter_quirk = pdata->lfps_filter_quirk; - dwc->rx_detect_poll_quirk = pdata->rx_detect_poll_quirk; - dwc->dis_u3_susphy_quirk = pdata->dis_u3_susphy_quirk; - dwc->dis_u2_susphy_quirk = pdata->dis_u2_susphy_quirk; - dwc->dis_enblslpm_quirk = pdata->dis_enblslpm_quirk; - dwc->dis_rxdet_inp3_quirk = pdata->dis_rxdet_inp3_quirk; - - dwc->tx_de_emphasis_quirk = pdata->tx_de_emphasis_quirk; - if (pdata->tx_de_emphasis) - tx_de_emphasis = pdata->tx_de_emphasis; - - dwc->hsphy_interface = pdata->hsphy_interface; - dwc->fladj = pdata->fladj_value; - } - dwc->lpm_nyet_threshold = lpm_nyet_threshold; dwc->tx_de_emphasis = tx_de_emphasis; diff --git a/drivers/usb/dwc3/platform_data.h b/drivers/usb/dwc3/platform_data.h deleted file mode 100644 index 8826cca5fc6f..000000000000 --- a/drivers/usb/dwc3/platform_data.h +++ /dev/null @@ -1,53 +0,0 @@ -/** - * platform_data.h - USB DWC3 Platform Data Support - * - * Copyright (C) 2013 Texas Instruments Incorporated - http://www.ti.com - * Author: Felipe Balbi - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 of - * the License as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ - -#include -#include - -struct dwc3_platform_data { - enum usb_device_speed maximum_speed; - enum usb_dr_mode dr_mode; - bool usb3_lpm_capable; - - unsigned is_utmi_l1_suspend:1; - u8 hird_threshold; - - u8 lpm_nyet_threshold; - - unsigned disable_scramble_quirk:1; - unsigned has_lpm_erratum:1; - unsigned u2exit_lfps_quirk:1; - unsigned u2ss_inp3_quirk:1; - unsigned req_p1p2p3_quirk:1; - unsigned del_p1p2p3_quirk:1; - unsigned del_phy_power_chg_quirk:1; - unsigned lfps_filter_quirk:1; - unsigned rx_detect_poll_quirk:1; - unsigned dis_u3_susphy_quirk:1; - unsigned dis_u2_susphy_quirk:1; - unsigned dis_enblslpm_quirk:1; - unsigned dis_rxdet_inp3_quirk:1; - - unsigned tx_de_emphasis_quirk:1; - unsigned tx_de_emphasis:2; - - u32 fladj_value; - - const char *hsphy_interface; -}; From b6b1c6db4c7f04ffe3fd411baa9df31f4dd355dd Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Mon, 30 May 2016 13:29:35 +0300 Subject: [PATCH 056/109] usb: dwc3: gadget: update transfer needs transfer resource According to SNPS databook, we need to pass transfer resource on update transfer command, let's do it. Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/gadget.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index 61edb6650e2f..e7e493b45084 100644 --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c @@ -994,12 +994,13 @@ static int __dwc3_gadget_kick_transfer(struct dwc3_ep *dep, u16 cmd_param) if (starting) { params.param0 = upper_32_bits(req->trb_dma); params.param1 = lower_32_bits(req->trb_dma); - cmd = DWC3_DEPCMD_STARTTRANSFER; + cmd = DWC3_DEPCMD_STARTTRANSFER | + DWC3_DEPCMD_PARAM(cmd_param); } else { - cmd = DWC3_DEPCMD_UPDATETRANSFER; + cmd = DWC3_DEPCMD_UPDATETRANSFER | + DWC3_DEPCMD_PARAM(dep->resource_index); } - cmd |= DWC3_DEPCMD_PARAM(cmd_param); ret = dwc3_send_gadget_ep_cmd(dep, cmd, ¶ms); if (ret < 0) { /* From 68d34c8a744d16ae449577a71c67b19a4b1100d0 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Mon, 30 May 2016 13:34:58 +0300 Subject: [PATCH 057/109] usb: dwc3: gadget: keep track of allocated and queued reqs We will be using this information to change how we figure out when we need LST bit. For now, just update our counters. Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/core.h | 4 ++++ drivers/usb/dwc3/gadget.c | 7 +++++++ drivers/usb/dwc3/trace.h | 9 +++++++-- 3 files changed, 18 insertions(+), 2 deletions(-) diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h index dcdba14e10f0..45d6de5107c7 100644 --- a/drivers/usb/dwc3/core.h +++ b/drivers/usb/dwc3/core.h @@ -497,6 +497,8 @@ struct dwc3_event_buffer { * @type: set to bmAttributes & USB_ENDPOINT_XFERTYPE_MASK * @resource_index: Resource transfer index * @interval: the interval on which the ISOC transfer is started + * @allocated_requests: number of requests allocated + * @queued_requests: number of requests queued for transfer * @name: a human readable name e.g. ep1out-bulk * @direction: true for TX, false for RX * @stream_capable: true when streams are enabled @@ -541,6 +543,8 @@ struct dwc3_ep { u8 number; u8 type; u8 resource_index; + u32 allocated_requests; + u32 queued_requests; u32 interval; char name[20]; diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index e7e493b45084..1cd4b50cf35b 100644 --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c @@ -741,6 +741,8 @@ static struct usb_request *dwc3_gadget_ep_alloc_request(struct usb_ep *ep, req->epnum = dep->number; req->dep = dep; + dep->allocated_requests++; + trace_dwc3_alloc_request(req); return &req->request; @@ -750,7 +752,9 @@ static void dwc3_gadget_ep_free_request(struct usb_ep *ep, struct usb_request *request) { struct dwc3_request *req = to_dwc3_request(request); + struct dwc3_ep *dep = to_dwc3_ep(ep); + dep->allocated_requests--; trace_dwc3_free_request(req); kfree(req); } @@ -831,6 +835,8 @@ static void dwc3_prepare_one_trb(struct dwc3_ep *dep, trb->ctrl |= DWC3_TRB_CTRL_HWO; + dep->queued_requests++; + trace_dwc3_prepare_trb(dep, trb); } @@ -1936,6 +1942,7 @@ static int __dwc3_cleanup_done_trbs(struct dwc3 *dwc, struct dwc3_ep *dep, unsigned int s_pkt = 0; unsigned int trb_status; + dep->queued_requests--; trace_dwc3_complete_trb(dep, trb); if ((trb->ctrl & DWC3_TRB_CTRL_HWO) && status != -ESHUTDOWN) diff --git a/drivers/usb/dwc3/trace.h b/drivers/usb/dwc3/trace.h index cafd69fc7eb8..d24cefd191b5 100644 --- a/drivers/usb/dwc3/trace.h +++ b/drivers/usb/dwc3/trace.h @@ -231,6 +231,8 @@ DECLARE_EVENT_CLASS(dwc3_log_trb, TP_STRUCT__entry( __dynamic_array(char, name, DWC3_MSG_MAX) __field(struct dwc3_trb *, trb) + __field(u32, allocated) + __field(u32, queued) __field(u32, bpl) __field(u32, bph) __field(u32, size) @@ -239,13 +241,16 @@ DECLARE_EVENT_CLASS(dwc3_log_trb, TP_fast_assign( snprintf(__get_str(name), DWC3_MSG_MAX, "%s", dep->name); __entry->trb = trb; + __entry->allocated = dep->allocated_requests; + __entry->queued = dep->queued_requests; __entry->bpl = trb->bpl; __entry->bph = trb->bph; __entry->size = trb->size; __entry->ctrl = trb->ctrl; ), - TP_printk("%s: trb %p buf %08x%08x size %d ctrl %08x (%c%c%c%c:%c%c:%s)", - __get_str(name), __entry->trb, __entry->bph, __entry->bpl, + TP_printk("%s: %d/%d trb %p buf %08x%08x size %d ctrl %08x (%c%c%c%c:%c%c:%s)", + __get_str(name), __entry->queued, __entry->allocated, + __entry->trb, __entry->bph, __entry->bpl, __entry->size, __entry->ctrl, __entry->ctrl & DWC3_TRB_CTRL_HWO ? 'H' : 'h', __entry->ctrl & DWC3_TRB_CTRL_LST ? 'L' : 'l', From 69450c4dc164d9ecbc0b54cb47a2ec80cde45da4 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Mon, 30 May 2016 13:37:02 +0300 Subject: [PATCH 058/109] usb: dwc3: gadget: halt and stop based HWO bit Instead of relying on empty list of queued requests, let's rely on the fact that we have a TRB being processed right now. Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/gadget.c | 28 ++++++++++++++++++++++++---- 1 file changed, 24 insertions(+), 4 deletions(-) diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index 1cd4b50cf35b..a7c2548166b0 100644 --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c @@ -600,8 +600,16 @@ static void dwc3_stop_active_transfer(struct dwc3 *dwc, u32 epnum, bool force); static void dwc3_remove_requests(struct dwc3 *dwc, struct dwc3_ep *dep) { struct dwc3_request *req; + struct dwc3_trb *current_trb; + unsigned transfer_in_flight; - if (!list_empty(&dep->started_list)) { + if (dep->number > 1) + current_trb = &dep->trb_pool[dep->trb_enqueue]; + else + current_trb = &dwc->ep0_trb[dep->trb_enqueue]; + transfer_in_flight = current_trb->ctrl & DWC3_TRB_CTRL_HWO; + + if (transfer_in_flight && !list_empty(&dep->started_list)) { dwc3_stop_active_transfer(dwc, dep->number, true); /* - giveback all requests to gadget driver */ @@ -1302,9 +1310,21 @@ int __dwc3_gadget_ep_set_halt(struct dwc3_ep *dep, int value, int protocol) memset(¶ms, 0x00, sizeof(params)); if (value) { - if (!protocol && ((dep->direction && dep->flags & DWC3_EP_BUSY) || - (!list_empty(&dep->started_list) || - !list_empty(&dep->pending_list)))) { + struct dwc3_trb *trb; + + unsigned transfer_in_flight; + unsigned started; + + if (dep->number > 1) + trb = dwc3_ep_prev_trb(dep, dep->trb_enqueue); + else + trb = &dwc->ep0_trb[dep->trb_enqueue]; + + transfer_in_flight = trb->ctrl & DWC3_TRB_CTRL_HWO; + started = !list_empty(&dep->started_list); + + if (!protocol && ((dep->direction && transfer_in_flight) || + (!dep->direction && started))) { dwc3_trace(trace_dwc3_gadget, "%s: pending request, cannot halt", dep->name); From 55a0237f8f47957163125e20ee9260538c5c341c Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Mon, 30 May 2016 13:38:32 +0300 Subject: [PATCH 059/109] usb: dwc3: gadget: use allocated/queued reqs for LST bit Let's only set LST bit when we run out of space in our TRB ring. For all other cases, we keep LST bit unset which will prevent constant allocation and deallocation of endpoint transfer resources. Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/gadget.c | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index a7c2548166b0..eea412720f33 100644 --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c @@ -897,7 +897,8 @@ static u32 dwc3_calc_trbs_left(struct dwc3_ep *dep) } static void dwc3_prepare_one_trb_sg(struct dwc3_ep *dep, - struct dwc3_request *req, unsigned int trbs_left) + struct dwc3_request *req, unsigned int trbs_left, + unsigned int more_coming) { struct usb_request *request = &req->request; struct scatterlist *sg = request->sg; @@ -914,7 +915,8 @@ static void dwc3_prepare_one_trb_sg(struct dwc3_ep *dep, dma = sg_dma_address(s); if (sg_is_last(s)) { - if (list_is_last(&req->list, &dep->pending_list)) + if (usb_endpoint_xfer_int(dep->endpoint.desc) || + !more_coming) last = true; chain = false; @@ -935,7 +937,8 @@ static void dwc3_prepare_one_trb_sg(struct dwc3_ep *dep, } static void dwc3_prepare_one_trb_linear(struct dwc3_ep *dep, - struct dwc3_request *req, unsigned int trbs_left) + struct dwc3_request *req, unsigned int trbs_left, + unsigned int more_coming) { unsigned int last = false; unsigned int length; @@ -948,7 +951,7 @@ static void dwc3_prepare_one_trb_linear(struct dwc3_ep *dep, last = true; /* Is this the last request? */ - if (list_is_last(&req->list, &dep->pending_list)) + if (usb_endpoint_xfer_int(dep->endpoint.desc) || !more_coming) last = true; dwc3_prepare_one_trb(dep, req, dma, length, @@ -966,6 +969,7 @@ static void dwc3_prepare_one_trb_linear(struct dwc3_ep *dep, static void dwc3_prepare_trbs(struct dwc3_ep *dep) { struct dwc3_request *req, *n; + unsigned int more_coming; u32 trbs_left; BUILD_BUG_ON_NOT_POWER_OF_2(DWC3_TRB_NUM); @@ -974,11 +978,15 @@ static void dwc3_prepare_trbs(struct dwc3_ep *dep) if (!trbs_left) return; + more_coming = dep->allocated_requests - dep->queued_requests; + list_for_each_entry_safe(req, n, &dep->pending_list, list) { if (req->request.num_mapped_sgs > 0) - dwc3_prepare_one_trb_sg(dep, req, trbs_left--); + dwc3_prepare_one_trb_sg(dep, req, trbs_left--, + more_coming); else - dwc3_prepare_one_trb_linear(dep, req, trbs_left--); + dwc3_prepare_one_trb_linear(dep, req, trbs_left--, + more_coming); if (!trbs_left) return; From 13fa2e69b1dda31bddb11fe61f250b9415885ead Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Mon, 30 May 2016 13:40:00 +0300 Subject: [PATCH 060/109] usb: dwc3: gadget: disable XFER_NOT_READY We don't need this IRQ anymore for interrupt or bulk endpoints. Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/gadget.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index eea412720f33..126e8b883595 100644 --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c @@ -485,8 +485,10 @@ static int dwc3_gadget_set_ep_config(struct dwc3 *dwc, struct dwc3_ep *dep, params.param2 |= dep->saved_state; } - params.param1 = DWC3_DEPCFG_XFER_COMPLETE_EN - | DWC3_DEPCFG_XFER_NOT_READY_EN; + params.param1 = DWC3_DEPCFG_XFER_COMPLETE_EN; + + if (dep->number <= 1 || usb_endpoint_xfer_isoc(desc)) + params.param1 |= DWC3_DEPCFG_XFER_NOT_READY_EN; if (usb_ss_max_streams(comp_desc) && usb_endpoint_xfer_bulk(desc)) { params.param1 |= DWC3_DEPCFG_STREAM_CAPABLE From ba62c09d5cc240e55eb39e92d88f1036bb1d9221 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Mon, 30 May 2016 13:41:22 +0300 Subject: [PATCH 061/109] usb: dwc3: gadget: start Bulk endpoints more frequently Now we can try to issue Update Transfer every time gadget driver queues a new request. This will make sure we keep controller's queue busy for as long as possible. Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/gadget.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index 126e8b883595..3d2978cebdff 100644 --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c @@ -1133,8 +1133,7 @@ static int __dwc3_gadget_ep_queue(struct dwc3_ep *dep, struct dwc3_request *req) * little bit faster. */ if (!usb_endpoint_xfer_isoc(dep->endpoint.desc) && - !usb_endpoint_xfer_int(dep->endpoint.desc) && - !(dep->flags & DWC3_EP_BUSY)) { + !usb_endpoint_xfer_int(dep->endpoint.desc)) { ret = __dwc3_gadget_kick_transfer(dep, 0); goto out; } From d6dc2e76a860d6be0129daae43e5f12461531d20 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Mon, 30 May 2016 13:42:33 +0300 Subject: [PATCH 062/109] usb: dwc3: gadget: decrement trbs_left for each sg entry If we don't, we will overwrite valid TRBs. Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/gadget.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index 3d2978cebdff..d2884a414e20 100644 --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c @@ -924,7 +924,7 @@ static void dwc3_prepare_one_trb_sg(struct dwc3_ep *dep, chain = false; } - if (!trbs_left) + if (!trbs_left--) last = true; if (last) From 5a8d651a2bde01e00caf78496390d6ae46df80af Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Tue, 31 May 2016 13:07:47 +0300 Subject: [PATCH 063/109] usb: gadget: move gadget API functions to udc-core instead of defining all functions as static inlines, let's move them to udc-core and export them with EXPORT_SYMBOL_GPL, that way we can make sure that only GPL drivers will use them. As a side effect, it'll be nicer to add tracepoints to the gadget API. While at that, also fix Kconfig dependencies to avoid randconfig build failures. Acked-By: Sebastian Reichel Acked-by: Peter Chen Signed-off-by: Felipe Balbi --- drivers/phy/Kconfig | 1 + drivers/power/Kconfig | 1 + drivers/usb/gadget/udc/udc-core.c | 573 +++++++++++++++++++++++++++++ drivers/usb/host/Kconfig | 2 +- drivers/usb/phy/Kconfig | 11 +- include/linux/usb/gadget.h | 585 +++--------------------------- 6 files changed, 645 insertions(+), 528 deletions(-) diff --git a/drivers/phy/Kconfig b/drivers/phy/Kconfig index b869b98835f4..8d1cfb7f3ea2 100644 --- a/drivers/phy/Kconfig +++ b/drivers/phy/Kconfig @@ -176,6 +176,7 @@ config TWL4030_USB tristate "TWL4030 USB Transceiver Driver" depends on TWL4030_CORE && REGULATOR_TWL4030 && USB_MUSB_OMAP2PLUS depends on USB_SUPPORT + depends on USB_GADGET || !USB_GADGET # if USB_GADGET=m, this can't 'y' select GENERIC_PHY select USB_PHY help diff --git a/drivers/power/Kconfig b/drivers/power/Kconfig index 421770ddafa3..0f11a0f4c369 100644 --- a/drivers/power/Kconfig +++ b/drivers/power/Kconfig @@ -309,6 +309,7 @@ config BATTERY_RX51 config CHARGER_ISP1704 tristate "ISP1704 USB Charger Detection" depends on USB_PHY + depends on USB_GADGET || !USB_GADGET # if USB_GADGET=m, this can't be 'y' help Say Y to enable support for USB Charger Detection with ISP1707/ISP1704 USB transceivers. diff --git a/drivers/usb/gadget/udc/udc-core.c b/drivers/usb/gadget/udc/udc-core.c index e1b2dcebdc2e..abf013865e51 100644 --- a/drivers/usb/gadget/udc/udc-core.c +++ b/drivers/usb/gadget/udc/udc-core.c @@ -59,6 +59,579 @@ static int udc_bind_to_driver(struct usb_udc *udc, /* ------------------------------------------------------------------------- */ +/** + * usb_ep_set_maxpacket_limit - set maximum packet size limit for endpoint + * @ep:the endpoint being configured + * @maxpacket_limit:value of maximum packet size limit + * + * This function should be used only in UDC drivers to initialize endpoint + * (usually in probe function). + */ +void usb_ep_set_maxpacket_limit(struct usb_ep *ep, + unsigned maxpacket_limit) +{ + ep->maxpacket_limit = maxpacket_limit; + ep->maxpacket = maxpacket_limit; +} +EXPORT_SYMBOL_GPL(usb_ep_set_maxpacket_limit); + +/** + * usb_ep_enable - configure endpoint, making it usable + * @ep:the endpoint being configured. may not be the endpoint named "ep0". + * drivers discover endpoints through the ep_list of a usb_gadget. + * + * When configurations are set, or when interface settings change, the driver + * will enable or disable the relevant endpoints. while it is enabled, an + * endpoint may be used for i/o until the driver receives a disconnect() from + * the host or until the endpoint is disabled. + * + * the ep0 implementation (which calls this routine) must ensure that the + * hardware capabilities of each endpoint match the descriptor provided + * for it. for example, an endpoint named "ep2in-bulk" would be usable + * for interrupt transfers as well as bulk, but it likely couldn't be used + * for iso transfers or for endpoint 14. some endpoints are fully + * configurable, with more generic names like "ep-a". (remember that for + * USB, "in" means "towards the USB master".) + * + * returns zero, or a negative error code. + */ +int usb_ep_enable(struct usb_ep *ep) +{ + int ret; + + if (ep->enabled) + return 0; + + ret = ep->ops->enable(ep, ep->desc); + if (ret) + return ret; + + ep->enabled = true; + + return 0; +} +EXPORT_SYMBOL_GPL(usb_ep_enable); + +/** + * usb_ep_disable - endpoint is no longer usable + * @ep:the endpoint being unconfigured. may not be the endpoint named "ep0". + * + * no other task may be using this endpoint when this is called. + * any pending and uncompleted requests will complete with status + * indicating disconnect (-ESHUTDOWN) before this call returns. + * gadget drivers must call usb_ep_enable() again before queueing + * requests to the endpoint. + * + * returns zero, or a negative error code. + */ +int usb_ep_disable(struct usb_ep *ep) +{ + int ret; + + if (!ep->enabled) + return 0; + + ret = ep->ops->disable(ep); + if (ret) + return ret; + + ep->enabled = false; + + return 0; +} +EXPORT_SYMBOL_GPL(usb_ep_disable); + +/** + * usb_ep_alloc_request - allocate a request object to use with this endpoint + * @ep:the endpoint to be used with with the request + * @gfp_flags:GFP_* flags to use + * + * Request objects must be allocated with this call, since they normally + * need controller-specific setup and may even need endpoint-specific + * resources such as allocation of DMA descriptors. + * Requests may be submitted with usb_ep_queue(), and receive a single + * completion callback. Free requests with usb_ep_free_request(), when + * they are no longer needed. + * + * Returns the request, or null if one could not be allocated. + */ +struct usb_request *usb_ep_alloc_request(struct usb_ep *ep, + gfp_t gfp_flags) +{ + return ep->ops->alloc_request(ep, gfp_flags); +} +EXPORT_SYMBOL_GPL(usb_ep_alloc_request); + +/** + * usb_ep_free_request - frees a request object + * @ep:the endpoint associated with the request + * @req:the request being freed + * + * Reverses the effect of usb_ep_alloc_request(). + * Caller guarantees the request is not queued, and that it will + * no longer be requeued (or otherwise used). + */ +void usb_ep_free_request(struct usb_ep *ep, + struct usb_request *req) +{ + ep->ops->free_request(ep, req); +} +EXPORT_SYMBOL_GPL(usb_ep_free_request); + +/** + * usb_ep_queue - queues (submits) an I/O request to an endpoint. + * @ep:the endpoint associated with the request + * @req:the request being submitted + * @gfp_flags: GFP_* flags to use in case the lower level driver couldn't + * pre-allocate all necessary memory with the request. + * + * This tells the device controller to perform the specified request through + * that endpoint (reading or writing a buffer). When the request completes, + * including being canceled by usb_ep_dequeue(), the request's completion + * routine is called to return the request to the driver. Any endpoint + * (except control endpoints like ep0) may have more than one transfer + * request queued; they complete in FIFO order. Once a gadget driver + * submits a request, that request may not be examined or modified until it + * is given back to that driver through the completion callback. + * + * Each request is turned into one or more packets. The controller driver + * never merges adjacent requests into the same packet. OUT transfers + * will sometimes use data that's already buffered in the hardware. + * Drivers can rely on the fact that the first byte of the request's buffer + * always corresponds to the first byte of some USB packet, for both + * IN and OUT transfers. + * + * Bulk endpoints can queue any amount of data; the transfer is packetized + * automatically. The last packet will be short if the request doesn't fill it + * out completely. Zero length packets (ZLPs) should be avoided in portable + * protocols since not all usb hardware can successfully handle zero length + * packets. (ZLPs may be explicitly written, and may be implicitly written if + * the request 'zero' flag is set.) Bulk endpoints may also be used + * for interrupt transfers; but the reverse is not true, and some endpoints + * won't support every interrupt transfer. (Such as 768 byte packets.) + * + * Interrupt-only endpoints are less functional than bulk endpoints, for + * example by not supporting queueing or not handling buffers that are + * larger than the endpoint's maxpacket size. They may also treat data + * toggle differently. + * + * Control endpoints ... after getting a setup() callback, the driver queues + * one response (even if it would be zero length). That enables the + * status ack, after transferring data as specified in the response. Setup + * functions may return negative error codes to generate protocol stalls. + * (Note that some USB device controllers disallow protocol stall responses + * in some cases.) When control responses are deferred (the response is + * written after the setup callback returns), then usb_ep_set_halt() may be + * used on ep0 to trigger protocol stalls. Depending on the controller, + * it may not be possible to trigger a status-stage protocol stall when the + * data stage is over, that is, from within the response's completion + * routine. + * + * For periodic endpoints, like interrupt or isochronous ones, the usb host + * arranges to poll once per interval, and the gadget driver usually will + * have queued some data to transfer at that time. + * + * Returns zero, or a negative error code. Endpoints that are not enabled + * report errors; errors will also be + * reported when the usb peripheral is disconnected. + */ +int usb_ep_queue(struct usb_ep *ep, + struct usb_request *req, gfp_t gfp_flags) +{ + if (WARN_ON_ONCE(!ep->enabled && ep->address)) + return -ESHUTDOWN; + + return ep->ops->queue(ep, req, gfp_flags); +} +EXPORT_SYMBOL_GPL(usb_ep_queue); + +/** + * usb_ep_dequeue - dequeues (cancels, unlinks) an I/O request from an endpoint + * @ep:the endpoint associated with the request + * @req:the request being canceled + * + * If the request is still active on the endpoint, it is dequeued and its + * completion routine is called (with status -ECONNRESET); else a negative + * error code is returned. This is guaranteed to happen before the call to + * usb_ep_dequeue() returns. + * + * Note that some hardware can't clear out write fifos (to unlink the request + * at the head of the queue) except as part of disconnecting from usb. Such + * restrictions prevent drivers from supporting configuration changes, + * even to configuration zero (a "chapter 9" requirement). + */ +int usb_ep_dequeue(struct usb_ep *ep, struct usb_request *req) +{ + return ep->ops->dequeue(ep, req); +} +EXPORT_SYMBOL_GPL(usb_ep_dequeue); + +/** + * usb_ep_set_halt - sets the endpoint halt feature. + * @ep: the non-isochronous endpoint being stalled + * + * Use this to stall an endpoint, perhaps as an error report. + * Except for control endpoints, + * the endpoint stays halted (will not stream any data) until the host + * clears this feature; drivers may need to empty the endpoint's request + * queue first, to make sure no inappropriate transfers happen. + * + * Note that while an endpoint CLEAR_FEATURE will be invisible to the + * gadget driver, a SET_INTERFACE will not be. To reset endpoints for the + * current altsetting, see usb_ep_clear_halt(). When switching altsettings, + * it's simplest to use usb_ep_enable() or usb_ep_disable() for the endpoints. + * + * Returns zero, or a negative error code. On success, this call sets + * underlying hardware state that blocks data transfers. + * Attempts to halt IN endpoints will fail (returning -EAGAIN) if any + * transfer requests are still queued, or if the controller hardware + * (usually a FIFO) still holds bytes that the host hasn't collected. + */ +int usb_ep_set_halt(struct usb_ep *ep) +{ + return ep->ops->set_halt(ep, 1); +} +EXPORT_SYMBOL_GPL(usb_ep_set_halt); + +/** + * usb_ep_clear_halt - clears endpoint halt, and resets toggle + * @ep:the bulk or interrupt endpoint being reset + * + * Use this when responding to the standard usb "set interface" request, + * for endpoints that aren't reconfigured, after clearing any other state + * in the endpoint's i/o queue. + * + * Returns zero, or a negative error code. On success, this call clears + * the underlying hardware state reflecting endpoint halt and data toggle. + * Note that some hardware can't support this request (like pxa2xx_udc), + * and accordingly can't correctly implement interface altsettings. + */ +int usb_ep_clear_halt(struct usb_ep *ep) +{ + return ep->ops->set_halt(ep, 0); +} +EXPORT_SYMBOL_GPL(usb_ep_clear_halt); + +/** + * usb_ep_set_wedge - sets the halt feature and ignores clear requests + * @ep: the endpoint being wedged + * + * Use this to stall an endpoint and ignore CLEAR_FEATURE(HALT_ENDPOINT) + * requests. If the gadget driver clears the halt status, it will + * automatically unwedge the endpoint. + * + * Returns zero on success, else negative errno. + */ +int usb_ep_set_wedge(struct usb_ep *ep) +{ + if (ep->ops->set_wedge) + return ep->ops->set_wedge(ep); + else + return ep->ops->set_halt(ep, 1); +} +EXPORT_SYMBOL_GPL(usb_ep_set_wedge); + +/** + * usb_ep_fifo_status - returns number of bytes in fifo, or error + * @ep: the endpoint whose fifo status is being checked. + * + * FIFO endpoints may have "unclaimed data" in them in certain cases, + * such as after aborted transfers. Hosts may not have collected all + * the IN data written by the gadget driver (and reported by a request + * completion). The gadget driver may not have collected all the data + * written OUT to it by the host. Drivers that need precise handling for + * fault reporting or recovery may need to use this call. + * + * This returns the number of such bytes in the fifo, or a negative + * errno if the endpoint doesn't use a FIFO or doesn't support such + * precise handling. + */ +int usb_ep_fifo_status(struct usb_ep *ep) +{ + if (ep->ops->fifo_status) + return ep->ops->fifo_status(ep); + else + return -EOPNOTSUPP; +} +EXPORT_SYMBOL_GPL(usb_ep_fifo_status); + +/** + * usb_ep_fifo_flush - flushes contents of a fifo + * @ep: the endpoint whose fifo is being flushed. + * + * This call may be used to flush the "unclaimed data" that may exist in + * an endpoint fifo after abnormal transaction terminations. The call + * must never be used except when endpoint is not being used for any + * protocol translation. + */ +void usb_ep_fifo_flush(struct usb_ep *ep) +{ + if (ep->ops->fifo_flush) + ep->ops->fifo_flush(ep); +} +EXPORT_SYMBOL_GPL(usb_ep_fifo_flush); + +/* ------------------------------------------------------------------------- */ + +/** + * usb_gadget_frame_number - returns the current frame number + * @gadget: controller that reports the frame number + * + * Returns the usb frame number, normally eleven bits from a SOF packet, + * or negative errno if this device doesn't support this capability. + */ +int usb_gadget_frame_number(struct usb_gadget *gadget) +{ + return gadget->ops->get_frame(gadget); +} +EXPORT_SYMBOL_GPL(usb_gadget_frame_number); + +/** + * usb_gadget_wakeup - tries to wake up the host connected to this gadget + * @gadget: controller used to wake up the host + * + * Returns zero on success, else negative error code if the hardware + * doesn't support such attempts, or its support has not been enabled + * by the usb host. Drivers must return device descriptors that report + * their ability to support this, or hosts won't enable it. + * + * This may also try to use SRP to wake the host and start enumeration, + * even if OTG isn't otherwise in use. OTG devices may also start + * remote wakeup even when hosts don't explicitly enable it. + */ +int usb_gadget_wakeup(struct usb_gadget *gadget) +{ + if (!gadget->ops->wakeup) + return -EOPNOTSUPP; + return gadget->ops->wakeup(gadget); +} +EXPORT_SYMBOL_GPL(usb_gadget_wakeup); + +/** + * usb_gadget_set_selfpowered - sets the device selfpowered feature. + * @gadget:the device being declared as self-powered + * + * this affects the device status reported by the hardware driver + * to reflect that it now has a local power supply. + * + * returns zero on success, else negative errno. + */ +int usb_gadget_set_selfpowered(struct usb_gadget *gadget) +{ + if (!gadget->ops->set_selfpowered) + return -EOPNOTSUPP; + return gadget->ops->set_selfpowered(gadget, 1); +} +EXPORT_SYMBOL_GPL(usb_gadget_set_selfpowered); + +/** + * usb_gadget_clear_selfpowered - clear the device selfpowered feature. + * @gadget:the device being declared as bus-powered + * + * this affects the device status reported by the hardware driver. + * some hardware may not support bus-powered operation, in which + * case this feature's value can never change. + * + * returns zero on success, else negative errno. + */ +int usb_gadget_clear_selfpowered(struct usb_gadget *gadget) +{ + if (!gadget->ops->set_selfpowered) + return -EOPNOTSUPP; + return gadget->ops->set_selfpowered(gadget, 0); +} +EXPORT_SYMBOL_GPL(usb_gadget_clear_selfpowered); + +/** + * usb_gadget_vbus_connect - Notify controller that VBUS is powered + * @gadget:The device which now has VBUS power. + * Context: can sleep + * + * This call is used by a driver for an external transceiver (or GPIO) + * that detects a VBUS power session starting. Common responses include + * resuming the controller, activating the D+ (or D-) pullup to let the + * host detect that a USB device is attached, and starting to draw power + * (8mA or possibly more, especially after SET_CONFIGURATION). + * + * Returns zero on success, else negative errno. + */ +int usb_gadget_vbus_connect(struct usb_gadget *gadget) +{ + if (!gadget->ops->vbus_session) + return -EOPNOTSUPP; + return gadget->ops->vbus_session(gadget, 1); +} +EXPORT_SYMBOL_GPL(usb_gadget_vbus_connect); + +/** + * usb_gadget_vbus_draw - constrain controller's VBUS power usage + * @gadget:The device whose VBUS usage is being described + * @mA:How much current to draw, in milliAmperes. This should be twice + * the value listed in the configuration descriptor bMaxPower field. + * + * This call is used by gadget drivers during SET_CONFIGURATION calls, + * reporting how much power the device may consume. For example, this + * could affect how quickly batteries are recharged. + * + * Returns zero on success, else negative errno. + */ +int usb_gadget_vbus_draw(struct usb_gadget *gadget, unsigned mA) +{ + if (!gadget->ops->vbus_draw) + return -EOPNOTSUPP; + return gadget->ops->vbus_draw(gadget, mA); +} +EXPORT_SYMBOL_GPL(usb_gadget_vbus_draw); + +/** + * usb_gadget_vbus_disconnect - notify controller about VBUS session end + * @gadget:the device whose VBUS supply is being described + * Context: can sleep + * + * This call is used by a driver for an external transceiver (or GPIO) + * that detects a VBUS power session ending. Common responses include + * reversing everything done in usb_gadget_vbus_connect(). + * + * Returns zero on success, else negative errno. + */ +int usb_gadget_vbus_disconnect(struct usb_gadget *gadget) +{ + if (!gadget->ops->vbus_session) + return -EOPNOTSUPP; + return gadget->ops->vbus_session(gadget, 0); +} +EXPORT_SYMBOL_GPL(usb_gadget_vbus_disconnect); + +/** + * usb_gadget_connect - software-controlled connect to USB host + * @gadget:the peripheral being connected + * + * Enables the D+ (or potentially D-) pullup. The host will start + * enumerating this gadget when the pullup is active and a VBUS session + * is active (the link is powered). This pullup is always enabled unless + * usb_gadget_disconnect() has been used to disable it. + * + * Returns zero on success, else negative errno. + */ +int usb_gadget_connect(struct usb_gadget *gadget) +{ + int ret; + + if (!gadget->ops->pullup) + return -EOPNOTSUPP; + + if (gadget->deactivated) { + /* + * If gadget is deactivated we only save new state. + * Gadget will be connected automatically after activation. + */ + gadget->connected = true; + return 0; + } + + ret = gadget->ops->pullup(gadget, 1); + if (!ret) + gadget->connected = 1; + return ret; +} +EXPORT_SYMBOL_GPL(usb_gadget_connect); + +/** + * usb_gadget_disconnect - software-controlled disconnect from USB host + * @gadget:the peripheral being disconnected + * + * Disables the D+ (or potentially D-) pullup, which the host may see + * as a disconnect (when a VBUS session is active). Not all systems + * support software pullup controls. + * + * Returns zero on success, else negative errno. + */ +int usb_gadget_disconnect(struct usb_gadget *gadget) +{ + int ret; + + if (!gadget->ops->pullup) + return -EOPNOTSUPP; + + if (gadget->deactivated) { + /* + * If gadget is deactivated we only save new state. + * Gadget will stay disconnected after activation. + */ + gadget->connected = false; + return 0; + } + + ret = gadget->ops->pullup(gadget, 0); + if (!ret) + gadget->connected = 0; + return ret; +} +EXPORT_SYMBOL_GPL(usb_gadget_disconnect); + +/** + * usb_gadget_deactivate - deactivate function which is not ready to work + * @gadget: the peripheral being deactivated + * + * This routine may be used during the gadget driver bind() call to prevent + * the peripheral from ever being visible to the USB host, unless later + * usb_gadget_activate() is called. For example, user mode components may + * need to be activated before the system can talk to hosts. + * + * Returns zero on success, else negative errno. + */ +int usb_gadget_deactivate(struct usb_gadget *gadget) +{ + int ret; + + if (gadget->deactivated) + return 0; + + if (gadget->connected) { + ret = usb_gadget_disconnect(gadget); + if (ret) + return ret; + /* + * If gadget was being connected before deactivation, we want + * to reconnect it in usb_gadget_activate(). + */ + gadget->connected = true; + } + gadget->deactivated = true; + + return 0; +} +EXPORT_SYMBOL_GPL(usb_gadget_deactivate); + +/** + * usb_gadget_activate - activate function which is not ready to work + * @gadget: the peripheral being activated + * + * This routine activates gadget which was previously deactivated with + * usb_gadget_deactivate() call. It calls usb_gadget_connect() if needed. + * + * Returns zero on success, else negative errno. + */ +int usb_gadget_activate(struct usb_gadget *gadget) +{ + if (!gadget->deactivated) + return 0; + + gadget->deactivated = false; + + /* + * If gadget has been connected before deactivation, or became connected + * while it was being deactivated, we call usb_gadget_connect(). + */ + if (gadget->connected) + return usb_gadget_connect(gadget); + + return 0; +} +EXPORT_SYMBOL_GPL(usb_gadget_activate); + +/* ------------------------------------------------------------------------- */ + #ifdef CONFIG_HAS_DMA int usb_gadget_map_request_by_dev(struct device *dev, diff --git a/drivers/usb/host/Kconfig b/drivers/usb/host/Kconfig index d8f5674809e8..2e710a4cca52 100644 --- a/drivers/usb/host/Kconfig +++ b/drivers/usb/host/Kconfig @@ -180,7 +180,7 @@ config USB_EHCI_MXC config USB_EHCI_HCD_OMAP tristate "EHCI support for OMAP3 and later chips" depends on ARCH_OMAP - select NOP_USB_XCEIV + depends on NOP_USB_XCEIV default y ---help--- Enables support for the on-chip EHCI controller on diff --git a/drivers/usb/phy/Kconfig b/drivers/usb/phy/Kconfig index c6904742e2aa..b9c409a18faa 100644 --- a/drivers/usb/phy/Kconfig +++ b/drivers/usb/phy/Kconfig @@ -21,6 +21,7 @@ config AB8500_USB config FSL_USB2_OTG bool "Freescale USB OTG Transceiver Driver" depends on USB_EHCI_FSL && USB_FSL_USB2 && USB_OTG_FSM && PM + depends on USB_GADGET || !USB_GADGET # if USB_GADGET=m, this can't be 'y' select USB_PHY help Enable this to support Freescale USB OTG transceiver. @@ -29,6 +30,7 @@ config ISP1301_OMAP tristate "Philips ISP1301 with OMAP OTG" depends on I2C && ARCH_OMAP_OTG depends on USB + depends on USB_GADGET || !USB_GADGET # if USB_GADGET=m, this can't be 'y' select USB_PHY help If you say yes here you get support for the Philips ISP1301 @@ -43,7 +45,7 @@ config ISP1301_OMAP config KEYSTONE_USB_PHY tristate "Keystone USB PHY Driver" depends on ARCH_KEYSTONE || COMPILE_TEST - select NOP_USB_XCEIV + depends on NOP_USB_XCEIV help Enable this to support Keystone USB phy. This driver provides interface to interact with USB 2.0 and USB 3.0 PHY that is part @@ -51,6 +53,7 @@ config KEYSTONE_USB_PHY config NOP_USB_XCEIV tristate "NOP USB Transceiver Driver" + depends on USB_GADGET || !USB_GADGET # if USB_GADGET=m, NOP can't be built-in select USB_PHY help This driver is to be used by all the usb transceiver which are either @@ -63,9 +66,9 @@ config AM335X_CONTROL_USB config AM335X_PHY_USB tristate "AM335x USB PHY Driver" depends on ARM || COMPILE_TEST + depends on NOP_USB_XCEIV select USB_PHY select AM335X_CONTROL_USB - select NOP_USB_XCEIV select USB_COMMON help This driver provides PHY support for that phy which part for the @@ -92,6 +95,7 @@ config TWL6030_USB config USB_GPIO_VBUS tristate "GPIO based peripheral-only VBUS sensing 'transceiver'" depends on GPIOLIB || COMPILE_TEST + depends on USB_GADGET || !USB_GADGET # if USB_GADGET=m, this can't be 'y' select USB_PHY help Provides simple GPIO VBUS sensing for controllers with an @@ -112,6 +116,7 @@ config OMAP_OTG config TAHVO_USB tristate "Tahvo USB transceiver driver" depends on MFD_RETU && EXTCON + depends on USB_GADGET || !USB_GADGET # if USB_GADGET=m, this can't be 'y' select USB_PHY help Enable this to support USB transceiver on Tahvo. This is used @@ -140,6 +145,7 @@ config USB_ISP1301 config USB_MSM_OTG tristate "Qualcomm on-chip USB OTG controller support" depends on (USB || USB_GADGET) && (ARCH_QCOM || COMPILE_TEST) + depends on USB_GADGET || !USB_GADGET # if USB_GADGET=m, this can't be 'y' depends on RESET_CONTROLLER depends on EXTCON select USB_PHY @@ -169,6 +175,7 @@ config USB_QCOM_8X16_PHY config USB_MV_OTG tristate "Marvell USB OTG support" depends on USB_EHCI_MV && USB_MV_UDC && PM && USB_OTG + depends on USB_GADGET || !USB_GADGET # if USB_GADGET=m, this can't be 'y' select USB_PHY help Say Y here if you want to build Marvell USB OTG transciever diff --git a/include/linux/usb/gadget.h b/include/linux/usb/gadget.h index fefe8b06a63d..c6e1149ddb0d 100644 --- a/include/linux/usb/gadget.h +++ b/include/linux/usb/gadget.h @@ -228,307 +228,49 @@ struct usb_ep { /*-------------------------------------------------------------------------*/ -/** - * usb_ep_set_maxpacket_limit - set maximum packet size limit for endpoint - * @ep:the endpoint being configured - * @maxpacket_limit:value of maximum packet size limit - * - * This function should be used only in UDC drivers to initialize endpoint - * (usually in probe function). - */ +#if IS_ENABLED(CONFIG_USB_GADGET) +void usb_ep_set_maxpacket_limit(struct usb_ep *ep, unsigned maxpacket_limit); +int usb_ep_enable(struct usb_ep *ep); +int usb_ep_disable(struct usb_ep *ep); +struct usb_request *usb_ep_alloc_request(struct usb_ep *ep, gfp_t gfp_flags); +void usb_ep_free_request(struct usb_ep *ep, struct usb_request *req); +int usb_ep_queue(struct usb_ep *ep, struct usb_request *req, gfp_t gfp_flags); +int usb_ep_dequeue(struct usb_ep *ep, struct usb_request *req); +int usb_ep_set_halt(struct usb_ep *ep); +int usb_ep_clear_halt(struct usb_ep *ep); +int usb_ep_set_wedge(struct usb_ep *ep); +int usb_ep_fifo_status(struct usb_ep *ep); +void usb_ep_fifo_flush(struct usb_ep *ep); +#else static inline void usb_ep_set_maxpacket_limit(struct usb_ep *ep, - unsigned maxpacket_limit) -{ - ep->maxpacket_limit = maxpacket_limit; - ep->maxpacket = maxpacket_limit; -} - -/** - * usb_ep_enable - configure endpoint, making it usable - * @ep:the endpoint being configured. may not be the endpoint named "ep0". - * drivers discover endpoints through the ep_list of a usb_gadget. - * - * When configurations are set, or when interface settings change, the driver - * will enable or disable the relevant endpoints. while it is enabled, an - * endpoint may be used for i/o until the driver receives a disconnect() from - * the host or until the endpoint is disabled. - * - * the ep0 implementation (which calls this routine) must ensure that the - * hardware capabilities of each endpoint match the descriptor provided - * for it. for example, an endpoint named "ep2in-bulk" would be usable - * for interrupt transfers as well as bulk, but it likely couldn't be used - * for iso transfers or for endpoint 14. some endpoints are fully - * configurable, with more generic names like "ep-a". (remember that for - * USB, "in" means "towards the USB master".) - * - * returns zero, or a negative error code. - */ + unsigned maxpacket_limit) +{ } static inline int usb_ep_enable(struct usb_ep *ep) -{ - int ret; - - if (ep->enabled) - return 0; - - ret = ep->ops->enable(ep, ep->desc); - if (ret) - return ret; - - ep->enabled = true; - - return 0; -} - -/** - * usb_ep_disable - endpoint is no longer usable - * @ep:the endpoint being unconfigured. may not be the endpoint named "ep0". - * - * no other task may be using this endpoint when this is called. - * any pending and uncompleted requests will complete with status - * indicating disconnect (-ESHUTDOWN) before this call returns. - * gadget drivers must call usb_ep_enable() again before queueing - * requests to the endpoint. - * - * returns zero, or a negative error code. - */ +{ return 0; } static inline int usb_ep_disable(struct usb_ep *ep) -{ - int ret; - - if (!ep->enabled) - return 0; - - ret = ep->ops->disable(ep); - if (ret) - return ret; - - ep->enabled = false; - - return 0; -} - -/** - * usb_ep_alloc_request - allocate a request object to use with this endpoint - * @ep:the endpoint to be used with with the request - * @gfp_flags:GFP_* flags to use - * - * Request objects must be allocated with this call, since they normally - * need controller-specific setup and may even need endpoint-specific - * resources such as allocation of DMA descriptors. - * Requests may be submitted with usb_ep_queue(), and receive a single - * completion callback. Free requests with usb_ep_free_request(), when - * they are no longer needed. - * - * Returns the request, or null if one could not be allocated. - */ +{ return 0; } static inline struct usb_request *usb_ep_alloc_request(struct usb_ep *ep, - gfp_t gfp_flags) -{ - return ep->ops->alloc_request(ep, gfp_flags); -} - -/** - * usb_ep_free_request - frees a request object - * @ep:the endpoint associated with the request - * @req:the request being freed - * - * Reverses the effect of usb_ep_alloc_request(). - * Caller guarantees the request is not queued, and that it will - * no longer be requeued (or otherwise used). - */ + gfp_t gfp_flags) +{ return NULL; } static inline void usb_ep_free_request(struct usb_ep *ep, - struct usb_request *req) -{ - ep->ops->free_request(ep, req); -} - -/** - * usb_ep_queue - queues (submits) an I/O request to an endpoint. - * @ep:the endpoint associated with the request - * @req:the request being submitted - * @gfp_flags: GFP_* flags to use in case the lower level driver couldn't - * pre-allocate all necessary memory with the request. - * - * This tells the device controller to perform the specified request through - * that endpoint (reading or writing a buffer). When the request completes, - * including being canceled by usb_ep_dequeue(), the request's completion - * routine is called to return the request to the driver. Any endpoint - * (except control endpoints like ep0) may have more than one transfer - * request queued; they complete in FIFO order. Once a gadget driver - * submits a request, that request may not be examined or modified until it - * is given back to that driver through the completion callback. - * - * Each request is turned into one or more packets. The controller driver - * never merges adjacent requests into the same packet. OUT transfers - * will sometimes use data that's already buffered in the hardware. - * Drivers can rely on the fact that the first byte of the request's buffer - * always corresponds to the first byte of some USB packet, for both - * IN and OUT transfers. - * - * Bulk endpoints can queue any amount of data; the transfer is packetized - * automatically. The last packet will be short if the request doesn't fill it - * out completely. Zero length packets (ZLPs) should be avoided in portable - * protocols since not all usb hardware can successfully handle zero length - * packets. (ZLPs may be explicitly written, and may be implicitly written if - * the request 'zero' flag is set.) Bulk endpoints may also be used - * for interrupt transfers; but the reverse is not true, and some endpoints - * won't support every interrupt transfer. (Such as 768 byte packets.) - * - * Interrupt-only endpoints are less functional than bulk endpoints, for - * example by not supporting queueing or not handling buffers that are - * larger than the endpoint's maxpacket size. They may also treat data - * toggle differently. - * - * Control endpoints ... after getting a setup() callback, the driver queues - * one response (even if it would be zero length). That enables the - * status ack, after transferring data as specified in the response. Setup - * functions may return negative error codes to generate protocol stalls. - * (Note that some USB device controllers disallow protocol stall responses - * in some cases.) When control responses are deferred (the response is - * written after the setup callback returns), then usb_ep_set_halt() may be - * used on ep0 to trigger protocol stalls. Depending on the controller, - * it may not be possible to trigger a status-stage protocol stall when the - * data stage is over, that is, from within the response's completion - * routine. - * - * For periodic endpoints, like interrupt or isochronous ones, the usb host - * arranges to poll once per interval, and the gadget driver usually will - * have queued some data to transfer at that time. - * - * Returns zero, or a negative error code. Endpoints that are not enabled - * report errors; errors will also be - * reported when the usb peripheral is disconnected. - */ -static inline int usb_ep_queue(struct usb_ep *ep, - struct usb_request *req, gfp_t gfp_flags) -{ - if (WARN_ON_ONCE(!ep->enabled && ep->address)) - return -ESHUTDOWN; - - return ep->ops->queue(ep, req, gfp_flags); -} - -/** - * usb_ep_dequeue - dequeues (cancels, unlinks) an I/O request from an endpoint - * @ep:the endpoint associated with the request - * @req:the request being canceled - * - * If the request is still active on the endpoint, it is dequeued and its - * completion routine is called (with status -ECONNRESET); else a negative - * error code is returned. This is guaranteed to happen before the call to - * usb_ep_dequeue() returns. - * - * Note that some hardware can't clear out write fifos (to unlink the request - * at the head of the queue) except as part of disconnecting from usb. Such - * restrictions prevent drivers from supporting configuration changes, - * even to configuration zero (a "chapter 9" requirement). - */ + struct usb_request *req) +{ } +static inline int usb_ep_queue(struct usb_ep *ep, struct usb_request *req, + gfp_t gfp_flags) +{ return 0; } static inline int usb_ep_dequeue(struct usb_ep *ep, struct usb_request *req) -{ - return ep->ops->dequeue(ep, req); -} - -/** - * usb_ep_set_halt - sets the endpoint halt feature. - * @ep: the non-isochronous endpoint being stalled - * - * Use this to stall an endpoint, perhaps as an error report. - * Except for control endpoints, - * the endpoint stays halted (will not stream any data) until the host - * clears this feature; drivers may need to empty the endpoint's request - * queue first, to make sure no inappropriate transfers happen. - * - * Note that while an endpoint CLEAR_FEATURE will be invisible to the - * gadget driver, a SET_INTERFACE will not be. To reset endpoints for the - * current altsetting, see usb_ep_clear_halt(). When switching altsettings, - * it's simplest to use usb_ep_enable() or usb_ep_disable() for the endpoints. - * - * Returns zero, or a negative error code. On success, this call sets - * underlying hardware state that blocks data transfers. - * Attempts to halt IN endpoints will fail (returning -EAGAIN) if any - * transfer requests are still queued, or if the controller hardware - * (usually a FIFO) still holds bytes that the host hasn't collected. - */ +{ return 0; } static inline int usb_ep_set_halt(struct usb_ep *ep) -{ - return ep->ops->set_halt(ep, 1); -} - -/** - * usb_ep_clear_halt - clears endpoint halt, and resets toggle - * @ep:the bulk or interrupt endpoint being reset - * - * Use this when responding to the standard usb "set interface" request, - * for endpoints that aren't reconfigured, after clearing any other state - * in the endpoint's i/o queue. - * - * Returns zero, or a negative error code. On success, this call clears - * the underlying hardware state reflecting endpoint halt and data toggle. - * Note that some hardware can't support this request (like pxa2xx_udc), - * and accordingly can't correctly implement interface altsettings. - */ +{ return 0; } static inline int usb_ep_clear_halt(struct usb_ep *ep) -{ - return ep->ops->set_halt(ep, 0); -} - -/** - * usb_ep_set_wedge - sets the halt feature and ignores clear requests - * @ep: the endpoint being wedged - * - * Use this to stall an endpoint and ignore CLEAR_FEATURE(HALT_ENDPOINT) - * requests. If the gadget driver clears the halt status, it will - * automatically unwedge the endpoint. - * - * Returns zero on success, else negative errno. - */ -static inline int -usb_ep_set_wedge(struct usb_ep *ep) -{ - if (ep->ops->set_wedge) - return ep->ops->set_wedge(ep); - else - return ep->ops->set_halt(ep, 1); -} - -/** - * usb_ep_fifo_status - returns number of bytes in fifo, or error - * @ep: the endpoint whose fifo status is being checked. - * - * FIFO endpoints may have "unclaimed data" in them in certain cases, - * such as after aborted transfers. Hosts may not have collected all - * the IN data written by the gadget driver (and reported by a request - * completion). The gadget driver may not have collected all the data - * written OUT to it by the host. Drivers that need precise handling for - * fault reporting or recovery may need to use this call. - * - * This returns the number of such bytes in the fifo, or a negative - * errno if the endpoint doesn't use a FIFO or doesn't support such - * precise handling. - */ +{ return 0; } +static inline int usb_ep_set_wedge(struct usb_ep *ep) +{ return 0; } static inline int usb_ep_fifo_status(struct usb_ep *ep) -{ - if (ep->ops->fifo_status) - return ep->ops->fifo_status(ep); - else - return -EOPNOTSUPP; -} - -/** - * usb_ep_fifo_flush - flushes contents of a fifo - * @ep: the endpoint whose fifo is being flushed. - * - * This call may be used to flush the "unclaimed data" that may exist in - * an endpoint fifo after abnormal transaction terminations. The call - * must never be used except when endpoint is not being used for any - * protocol translation. - */ +{ return 0; } static inline void usb_ep_fifo_flush(struct usb_ep *ep) -{ - if (ep->ops->fifo_flush) - ep->ops->fifo_flush(ep); -} - +{ } +#endif /* USB_GADGET */ /*-------------------------------------------------------------------------*/ @@ -760,251 +502,44 @@ static inline int gadget_is_otg(struct usb_gadget *g) #endif } -/** - * usb_gadget_frame_number - returns the current frame number - * @gadget: controller that reports the frame number - * - * Returns the usb frame number, normally eleven bits from a SOF packet, - * or negative errno if this device doesn't support this capability. - */ +/*-------------------------------------------------------------------------*/ + +#if IS_ENABLED(CONFIG_USB_GADGET) +int usb_gadget_frame_number(struct usb_gadget *gadget); +int usb_gadget_wakeup(struct usb_gadget *gadget); +int usb_gadget_set_selfpowered(struct usb_gadget *gadget); +int usb_gadget_clear_selfpowered(struct usb_gadget *gadget); +int usb_gadget_vbus_connect(struct usb_gadget *gadget); +int usb_gadget_vbus_draw(struct usb_gadget *gadget, unsigned mA); +int usb_gadget_vbus_disconnect(struct usb_gadget *gadget); +int usb_gadget_connect(struct usb_gadget *gadget); +int usb_gadget_disconnect(struct usb_gadget *gadget); +int usb_gadget_deactivate(struct usb_gadget *gadget); +int usb_gadget_activate(struct usb_gadget *gadget); +#else static inline int usb_gadget_frame_number(struct usb_gadget *gadget) -{ - return gadget->ops->get_frame(gadget); -} - -/** - * usb_gadget_wakeup - tries to wake up the host connected to this gadget - * @gadget: controller used to wake up the host - * - * Returns zero on success, else negative error code if the hardware - * doesn't support such attempts, or its support has not been enabled - * by the usb host. Drivers must return device descriptors that report - * their ability to support this, or hosts won't enable it. - * - * This may also try to use SRP to wake the host and start enumeration, - * even if OTG isn't otherwise in use. OTG devices may also start - * remote wakeup even when hosts don't explicitly enable it. - */ +{ return 0; } static inline int usb_gadget_wakeup(struct usb_gadget *gadget) -{ - if (!gadget->ops->wakeup) - return -EOPNOTSUPP; - return gadget->ops->wakeup(gadget); -} - -/** - * usb_gadget_set_selfpowered - sets the device selfpowered feature. - * @gadget:the device being declared as self-powered - * - * this affects the device status reported by the hardware driver - * to reflect that it now has a local power supply. - * - * returns zero on success, else negative errno. - */ +{ return 0; } static inline int usb_gadget_set_selfpowered(struct usb_gadget *gadget) -{ - if (!gadget->ops->set_selfpowered) - return -EOPNOTSUPP; - return gadget->ops->set_selfpowered(gadget, 1); -} - -/** - * usb_gadget_clear_selfpowered - clear the device selfpowered feature. - * @gadget:the device being declared as bus-powered - * - * this affects the device status reported by the hardware driver. - * some hardware may not support bus-powered operation, in which - * case this feature's value can never change. - * - * returns zero on success, else negative errno. - */ +{ return 0; } static inline int usb_gadget_clear_selfpowered(struct usb_gadget *gadget) -{ - if (!gadget->ops->set_selfpowered) - return -EOPNOTSUPP; - return gadget->ops->set_selfpowered(gadget, 0); -} - -/** - * usb_gadget_vbus_connect - Notify controller that VBUS is powered - * @gadget:The device which now has VBUS power. - * Context: can sleep - * - * This call is used by a driver for an external transceiver (or GPIO) - * that detects a VBUS power session starting. Common responses include - * resuming the controller, activating the D+ (or D-) pullup to let the - * host detect that a USB device is attached, and starting to draw power - * (8mA or possibly more, especially after SET_CONFIGURATION). - * - * Returns zero on success, else negative errno. - */ +{ return 0; } static inline int usb_gadget_vbus_connect(struct usb_gadget *gadget) -{ - if (!gadget->ops->vbus_session) - return -EOPNOTSUPP; - return gadget->ops->vbus_session(gadget, 1); -} - -/** - * usb_gadget_vbus_draw - constrain controller's VBUS power usage - * @gadget:The device whose VBUS usage is being described - * @mA:How much current to draw, in milliAmperes. This should be twice - * the value listed in the configuration descriptor bMaxPower field. - * - * This call is used by gadget drivers during SET_CONFIGURATION calls, - * reporting how much power the device may consume. For example, this - * could affect how quickly batteries are recharged. - * - * Returns zero on success, else negative errno. - */ +{ return 0; } static inline int usb_gadget_vbus_draw(struct usb_gadget *gadget, unsigned mA) -{ - if (!gadget->ops->vbus_draw) - return -EOPNOTSUPP; - return gadget->ops->vbus_draw(gadget, mA); -} - -/** - * usb_gadget_vbus_disconnect - notify controller about VBUS session end - * @gadget:the device whose VBUS supply is being described - * Context: can sleep - * - * This call is used by a driver for an external transceiver (or GPIO) - * that detects a VBUS power session ending. Common responses include - * reversing everything done in usb_gadget_vbus_connect(). - * - * Returns zero on success, else negative errno. - */ +{ return 0; } static inline int usb_gadget_vbus_disconnect(struct usb_gadget *gadget) -{ - if (!gadget->ops->vbus_session) - return -EOPNOTSUPP; - return gadget->ops->vbus_session(gadget, 0); -} - -/** - * usb_gadget_connect - software-controlled connect to USB host - * @gadget:the peripheral being connected - * - * Enables the D+ (or potentially D-) pullup. The host will start - * enumerating this gadget when the pullup is active and a VBUS session - * is active (the link is powered). This pullup is always enabled unless - * usb_gadget_disconnect() has been used to disable it. - * - * Returns zero on success, else negative errno. - */ +{ return 0; } static inline int usb_gadget_connect(struct usb_gadget *gadget) -{ - int ret; - - if (!gadget->ops->pullup) - return -EOPNOTSUPP; - - if (gadget->deactivated) { - /* - * If gadget is deactivated we only save new state. - * Gadget will be connected automatically after activation. - */ - gadget->connected = true; - return 0; - } - - ret = gadget->ops->pullup(gadget, 1); - if (!ret) - gadget->connected = 1; - return ret; -} - -/** - * usb_gadget_disconnect - software-controlled disconnect from USB host - * @gadget:the peripheral being disconnected - * - * Disables the D+ (or potentially D-) pullup, which the host may see - * as a disconnect (when a VBUS session is active). Not all systems - * support software pullup controls. - * - * Returns zero on success, else negative errno. - */ +{ return 0; } static inline int usb_gadget_disconnect(struct usb_gadget *gadget) -{ - int ret; - - if (!gadget->ops->pullup) - return -EOPNOTSUPP; - - if (gadget->deactivated) { - /* - * If gadget is deactivated we only save new state. - * Gadget will stay disconnected after activation. - */ - gadget->connected = false; - return 0; - } - - ret = gadget->ops->pullup(gadget, 0); - if (!ret) - gadget->connected = 0; - return ret; -} - -/** - * usb_gadget_deactivate - deactivate function which is not ready to work - * @gadget: the peripheral being deactivated - * - * This routine may be used during the gadget driver bind() call to prevent - * the peripheral from ever being visible to the USB host, unless later - * usb_gadget_activate() is called. For example, user mode components may - * need to be activated before the system can talk to hosts. - * - * Returns zero on success, else negative errno. - */ +{ return 0; } static inline int usb_gadget_deactivate(struct usb_gadget *gadget) -{ - int ret; - - if (gadget->deactivated) - return 0; - - if (gadget->connected) { - ret = usb_gadget_disconnect(gadget); - if (ret) - return ret; - /* - * If gadget was being connected before deactivation, we want - * to reconnect it in usb_gadget_activate(). - */ - gadget->connected = true; - } - gadget->deactivated = true; - - return 0; -} - -/** - * usb_gadget_activate - activate function which is not ready to work - * @gadget: the peripheral being activated - * - * This routine activates gadget which was previously deactivated with - * usb_gadget_deactivate() call. It calls usb_gadget_connect() if needed. - * - * Returns zero on success, else negative errno. - */ +{ return 0; } static inline int usb_gadget_activate(struct usb_gadget *gadget) -{ - if (!gadget->deactivated) - return 0; - - gadget->deactivated = false; - - /* - * If gadget has been connected before deactivation, or became connected - * while it was being deactivated, we call usb_gadget_connect(). - */ - if (gadget->connected) - return usb_gadget_connect(gadget); - - return 0; -} +{ return 0; } +#endif /* CONFIG_USB_GADGET */ /*-------------------------------------------------------------------------*/ From 5e42d710a108c23c104e083900d4ba9398e418b0 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Tue, 31 May 2016 13:39:21 +0300 Subject: [PATCH 064/109] usb: gadget: add tracepoints to the gadget API This new set of tracepoints will help all gadget drivers and UDC drivers when problem appears. Note that, in order to be able to add tracepoints to udc-core.c we had to rename that to core.c and statically link it with trace.c to form udc-core.o. This is to make sure that module name stays the same. Signed-off-by: Felipe Balbi --- drivers/usb/gadget/udc/Makefile | 5 + drivers/usb/gadget/udc/{udc-core.c => core.c} | 260 +++++++++++---- drivers/usb/gadget/udc/trace.c | 18 ++ drivers/usb/gadget/udc/trace.h | 298 ++++++++++++++++++ include/linux/usb/gadget.h | 4 + 5 files changed, 530 insertions(+), 55 deletions(-) rename drivers/usb/gadget/udc/{udc-core.c => core.c} (92%) create mode 100644 drivers/usb/gadget/udc/trace.c create mode 100644 drivers/usb/gadget/udc/trace.h diff --git a/drivers/usb/gadget/udc/Makefile b/drivers/usb/gadget/udc/Makefile index dfee53446319..98e74ed9f555 100644 --- a/drivers/usb/gadget/udc/Makefile +++ b/drivers/usb/gadget/udc/Makefile @@ -1,3 +1,8 @@ +# define_trace.h needs to know how to find our header +CFLAGS_trace.o := -I$(src) + +udc-core-y := core.o trace.o + # # USB peripheral controller drivers # diff --git a/drivers/usb/gadget/udc/udc-core.c b/drivers/usb/gadget/udc/core.c similarity index 92% rename from drivers/usb/gadget/udc/udc-core.c rename to drivers/usb/gadget/udc/core.c index abf013865e51..ff8685ea7219 100644 --- a/drivers/usb/gadget/udc/udc-core.c +++ b/drivers/usb/gadget/udc/core.c @@ -29,6 +29,8 @@ #include #include +#include "trace.h" + /** * struct usb_udc - describes one usb device controller * @driver - the gadget driver pointer. For use by the class code @@ -72,6 +74,8 @@ void usb_ep_set_maxpacket_limit(struct usb_ep *ep, { ep->maxpacket_limit = maxpacket_limit; ep->maxpacket = maxpacket_limit; + + trace_usb_ep_set_maxpacket_limit(ep, 0); } EXPORT_SYMBOL_GPL(usb_ep_set_maxpacket_limit); @@ -97,18 +101,23 @@ EXPORT_SYMBOL_GPL(usb_ep_set_maxpacket_limit); */ int usb_ep_enable(struct usb_ep *ep) { - int ret; + int ret = 0; if (ep->enabled) - return 0; + goto out; ret = ep->ops->enable(ep, ep->desc); - if (ret) - return ret; + if (ret) { + ret = ret; + goto out; + } ep->enabled = true; - return 0; +out: + trace_usb_ep_enable(ep, ret); + + return ret; } EXPORT_SYMBOL_GPL(usb_ep_enable); @@ -126,18 +135,23 @@ EXPORT_SYMBOL_GPL(usb_ep_enable); */ int usb_ep_disable(struct usb_ep *ep) { - int ret; + int ret = 0; if (!ep->enabled) - return 0; + goto out; ret = ep->ops->disable(ep); - if (ret) - return ret; + if (ret) { + ret = ret; + goto out; + } ep->enabled = false; - return 0; +out: + trace_usb_ep_disable(ep, ret); + + return ret; } EXPORT_SYMBOL_GPL(usb_ep_disable); @@ -158,7 +172,13 @@ EXPORT_SYMBOL_GPL(usb_ep_disable); struct usb_request *usb_ep_alloc_request(struct usb_ep *ep, gfp_t gfp_flags) { - return ep->ops->alloc_request(ep, gfp_flags); + struct usb_request *req = NULL; + + req = ep->ops->alloc_request(ep, gfp_flags); + + trace_usb_ep_alloc_request(ep, req, req ? 0 : -ENOMEM); + + return req; } EXPORT_SYMBOL_GPL(usb_ep_alloc_request); @@ -175,6 +195,7 @@ void usb_ep_free_request(struct usb_ep *ep, struct usb_request *req) { ep->ops->free_request(ep, req); + trace_usb_ep_free_request(ep, req, 0); } EXPORT_SYMBOL_GPL(usb_ep_free_request); @@ -238,10 +259,19 @@ EXPORT_SYMBOL_GPL(usb_ep_free_request); int usb_ep_queue(struct usb_ep *ep, struct usb_request *req, gfp_t gfp_flags) { - if (WARN_ON_ONCE(!ep->enabled && ep->address)) - return -ESHUTDOWN; + int ret = 0; - return ep->ops->queue(ep, req, gfp_flags); + if (WARN_ON_ONCE(!ep->enabled && ep->address)) { + ret = -ESHUTDOWN; + goto out; + } + + ret = ep->ops->queue(ep, req, gfp_flags); + +out: + trace_usb_ep_queue(ep, req, ret); + + return ret; } EXPORT_SYMBOL_GPL(usb_ep_queue); @@ -262,7 +292,12 @@ EXPORT_SYMBOL_GPL(usb_ep_queue); */ int usb_ep_dequeue(struct usb_ep *ep, struct usb_request *req) { - return ep->ops->dequeue(ep, req); + int ret; + + ret = ep->ops->dequeue(ep, req); + trace_usb_ep_dequeue(ep, req, ret); + + return ret; } EXPORT_SYMBOL_GPL(usb_ep_dequeue); @@ -289,7 +324,12 @@ EXPORT_SYMBOL_GPL(usb_ep_dequeue); */ int usb_ep_set_halt(struct usb_ep *ep) { - return ep->ops->set_halt(ep, 1); + int ret; + + ret = ep->ops->set_halt(ep, 1); + trace_usb_ep_set_halt(ep, ret); + + return ret; } EXPORT_SYMBOL_GPL(usb_ep_set_halt); @@ -308,7 +348,12 @@ EXPORT_SYMBOL_GPL(usb_ep_set_halt); */ int usb_ep_clear_halt(struct usb_ep *ep) { - return ep->ops->set_halt(ep, 0); + int ret; + + ret = ep->ops->set_halt(ep, 0); + trace_usb_ep_clear_halt(ep, ret); + + return ret; } EXPORT_SYMBOL_GPL(usb_ep_clear_halt); @@ -324,10 +369,16 @@ EXPORT_SYMBOL_GPL(usb_ep_clear_halt); */ int usb_ep_set_wedge(struct usb_ep *ep) { + int ret; + if (ep->ops->set_wedge) - return ep->ops->set_wedge(ep); + ret = ep->ops->set_wedge(ep); else - return ep->ops->set_halt(ep, 1); + ret = ep->ops->set_halt(ep, 1); + + trace_usb_ep_set_wedge(ep, ret); + + return ret; } EXPORT_SYMBOL_GPL(usb_ep_set_wedge); @@ -348,10 +399,16 @@ EXPORT_SYMBOL_GPL(usb_ep_set_wedge); */ int usb_ep_fifo_status(struct usb_ep *ep) { + int ret; + if (ep->ops->fifo_status) - return ep->ops->fifo_status(ep); + ret = ep->ops->fifo_status(ep); else - return -EOPNOTSUPP; + ret = -EOPNOTSUPP; + + trace_usb_ep_fifo_status(ep, ret); + + return ret; } EXPORT_SYMBOL_GPL(usb_ep_fifo_status); @@ -368,6 +425,8 @@ void usb_ep_fifo_flush(struct usb_ep *ep) { if (ep->ops->fifo_flush) ep->ops->fifo_flush(ep); + + trace_usb_ep_fifo_flush(ep, 0); } EXPORT_SYMBOL_GPL(usb_ep_fifo_flush); @@ -382,7 +441,13 @@ EXPORT_SYMBOL_GPL(usb_ep_fifo_flush); */ int usb_gadget_frame_number(struct usb_gadget *gadget) { - return gadget->ops->get_frame(gadget); + int ret; + + ret = gadget->ops->get_frame(gadget); + + trace_usb_gadget_frame_number(gadget, ret); + + return ret; } EXPORT_SYMBOL_GPL(usb_gadget_frame_number); @@ -401,9 +466,19 @@ EXPORT_SYMBOL_GPL(usb_gadget_frame_number); */ int usb_gadget_wakeup(struct usb_gadget *gadget) { - if (!gadget->ops->wakeup) - return -EOPNOTSUPP; - return gadget->ops->wakeup(gadget); + int ret = 0; + + if (!gadget->ops->wakeup) { + ret = -EOPNOTSUPP; + goto out; + } + + ret = gadget->ops->wakeup(gadget); + +out: + trace_usb_gadget_wakeup(gadget, ret); + + return ret; } EXPORT_SYMBOL_GPL(usb_gadget_wakeup); @@ -418,9 +493,19 @@ EXPORT_SYMBOL_GPL(usb_gadget_wakeup); */ int usb_gadget_set_selfpowered(struct usb_gadget *gadget) { - if (!gadget->ops->set_selfpowered) - return -EOPNOTSUPP; - return gadget->ops->set_selfpowered(gadget, 1); + int ret = 0; + + if (!gadget->ops->set_selfpowered) { + ret = -EOPNOTSUPP; + goto out; + } + + ret = gadget->ops->set_selfpowered(gadget, 1); + +out: + trace_usb_gadget_set_selfpowered(gadget, ret); + + return ret; } EXPORT_SYMBOL_GPL(usb_gadget_set_selfpowered); @@ -436,9 +521,19 @@ EXPORT_SYMBOL_GPL(usb_gadget_set_selfpowered); */ int usb_gadget_clear_selfpowered(struct usb_gadget *gadget) { - if (!gadget->ops->set_selfpowered) - return -EOPNOTSUPP; - return gadget->ops->set_selfpowered(gadget, 0); + int ret = 0; + + if (!gadget->ops->set_selfpowered) { + ret = -EOPNOTSUPP; + goto out; + } + + ret = gadget->ops->set_selfpowered(gadget, 0); + +out: + trace_usb_gadget_clear_selfpowered(gadget, ret); + + return ret; } EXPORT_SYMBOL_GPL(usb_gadget_clear_selfpowered); @@ -457,9 +552,19 @@ EXPORT_SYMBOL_GPL(usb_gadget_clear_selfpowered); */ int usb_gadget_vbus_connect(struct usb_gadget *gadget) { - if (!gadget->ops->vbus_session) - return -EOPNOTSUPP; - return gadget->ops->vbus_session(gadget, 1); + int ret = 0; + + if (!gadget->ops->vbus_session) { + ret = -EOPNOTSUPP; + goto out; + } + + ret = gadget->ops->vbus_session(gadget, 1); + +out: + trace_usb_gadget_vbus_connect(gadget, ret); + + return ret; } EXPORT_SYMBOL_GPL(usb_gadget_vbus_connect); @@ -477,9 +582,21 @@ EXPORT_SYMBOL_GPL(usb_gadget_vbus_connect); */ int usb_gadget_vbus_draw(struct usb_gadget *gadget, unsigned mA) { - if (!gadget->ops->vbus_draw) - return -EOPNOTSUPP; - return gadget->ops->vbus_draw(gadget, mA); + int ret = 0; + + if (!gadget->ops->vbus_draw) { + ret = -EOPNOTSUPP; + goto out; + } + + ret = gadget->ops->vbus_draw(gadget, mA); + if (!ret) + gadget->mA = mA; + +out: + trace_usb_gadget_vbus_draw(gadget, ret); + + return ret; } EXPORT_SYMBOL_GPL(usb_gadget_vbus_draw); @@ -496,9 +613,19 @@ EXPORT_SYMBOL_GPL(usb_gadget_vbus_draw); */ int usb_gadget_vbus_disconnect(struct usb_gadget *gadget) { - if (!gadget->ops->vbus_session) - return -EOPNOTSUPP; - return gadget->ops->vbus_session(gadget, 0); + int ret = 0; + + if (!gadget->ops->vbus_session) { + ret = -EOPNOTSUPP; + goto out; + } + + ret = gadget->ops->vbus_session(gadget, 0); + +out: + trace_usb_gadget_vbus_disconnect(gadget, ret); + + return ret; } EXPORT_SYMBOL_GPL(usb_gadget_vbus_disconnect); @@ -515,10 +642,12 @@ EXPORT_SYMBOL_GPL(usb_gadget_vbus_disconnect); */ int usb_gadget_connect(struct usb_gadget *gadget) { - int ret; + int ret = 0; - if (!gadget->ops->pullup) - return -EOPNOTSUPP; + if (!gadget->ops->pullup) { + ret = -EOPNOTSUPP; + goto out; + } if (gadget->deactivated) { /* @@ -526,12 +655,16 @@ int usb_gadget_connect(struct usb_gadget *gadget) * Gadget will be connected automatically after activation. */ gadget->connected = true; - return 0; + goto out; } ret = gadget->ops->pullup(gadget, 1); if (!ret) gadget->connected = 1; + +out: + trace_usb_gadget_connect(gadget, ret); + return ret; } EXPORT_SYMBOL_GPL(usb_gadget_connect); @@ -548,10 +681,12 @@ EXPORT_SYMBOL_GPL(usb_gadget_connect); */ int usb_gadget_disconnect(struct usb_gadget *gadget) { - int ret; + int ret = 0; - if (!gadget->ops->pullup) - return -EOPNOTSUPP; + if (!gadget->ops->pullup) { + ret = -EOPNOTSUPP; + goto out; + } if (gadget->deactivated) { /* @@ -559,12 +694,16 @@ int usb_gadget_disconnect(struct usb_gadget *gadget) * Gadget will stay disconnected after activation. */ gadget->connected = false; - return 0; + goto out; } ret = gadget->ops->pullup(gadget, 0); if (!ret) gadget->connected = 0; + +out: + trace_usb_gadget_disconnect(gadget, ret); + return ret; } EXPORT_SYMBOL_GPL(usb_gadget_disconnect); @@ -582,15 +721,16 @@ EXPORT_SYMBOL_GPL(usb_gadget_disconnect); */ int usb_gadget_deactivate(struct usb_gadget *gadget) { - int ret; + int ret = 0; if (gadget->deactivated) - return 0; + goto out; if (gadget->connected) { ret = usb_gadget_disconnect(gadget); if (ret) - return ret; + goto out; + /* * If gadget was being connected before deactivation, we want * to reconnect it in usb_gadget_activate(). @@ -599,7 +739,10 @@ int usb_gadget_deactivate(struct usb_gadget *gadget) } gadget->deactivated = true; - return 0; +out: + trace_usb_gadget_deactivate(gadget, ret); + + return ret; } EXPORT_SYMBOL_GPL(usb_gadget_deactivate); @@ -614,8 +757,10 @@ EXPORT_SYMBOL_GPL(usb_gadget_deactivate); */ int usb_gadget_activate(struct usb_gadget *gadget) { + int ret = 0; + if (!gadget->deactivated) - return 0; + goto out; gadget->deactivated = false; @@ -624,9 +769,12 @@ int usb_gadget_activate(struct usb_gadget *gadget) * while it was being deactivated, we call usb_gadget_connect(). */ if (gadget->connected) - return usb_gadget_connect(gadget); + ret = usb_gadget_connect(gadget); - return 0; +out: + trace_usb_gadget_activate(gadget, ret); + + return ret; } EXPORT_SYMBOL_GPL(usb_gadget_activate); @@ -714,6 +862,8 @@ void usb_gadget_giveback_request(struct usb_ep *ep, if (likely(req->status == 0)) usb_led_activity(USB_LED_EVENT_GADGET); + trace_usb_gadget_giveback_request(ep, req, 0); + req->complete(ep, req); } EXPORT_SYMBOL_GPL(usb_gadget_giveback_request); diff --git a/drivers/usb/gadget/udc/trace.c b/drivers/usb/gadget/udc/trace.c new file mode 100644 index 000000000000..8c551ab91ad8 --- /dev/null +++ b/drivers/usb/gadget/udc/trace.c @@ -0,0 +1,18 @@ +/** + * trace.c - USB Gadget Framework Trace Support + * + * Copyright (C) 2016 Intel Corporation + * Author: Felipe Balbi + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 of + * the License as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#define CREATE_TRACE_POINTS +#include "trace.h" diff --git a/drivers/usb/gadget/udc/trace.h b/drivers/usb/gadget/udc/trace.h new file mode 100644 index 000000000000..da29874b5366 --- /dev/null +++ b/drivers/usb/gadget/udc/trace.h @@ -0,0 +1,298 @@ +/** + * udc.c - Core UDC Framework + * + * Copyright (C) 2016 Intel Corporation + * Author: Felipe Balbi + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 of + * the License as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#undef TRACE_SYSTEM +#define TRACE_SYSTEM gadget + +#if !defined(__UDC_TRACE_H) || defined(TRACE_HEADER_MULTI_READ) +#define __UDC_TRACE_H + +#include +#include +#include +#include + +DECLARE_EVENT_CLASS(udc_log_gadget, + TP_PROTO(struct usb_gadget *g, int ret), + TP_ARGS(g, ret), + TP_STRUCT__entry( + __field(enum usb_device_speed, speed) + __field(enum usb_device_speed, max_speed) + __field(enum usb_device_state, state) + __field(unsigned, mA) + __field(unsigned, sg_supported) + __field(unsigned, is_otg) + __field(unsigned, is_a_peripheral) + __field(unsigned, b_hnp_enable) + __field(unsigned, a_hnp_support) + __field(unsigned, hnp_polling_support) + __field(unsigned, host_request_flag) + __field(unsigned, quirk_ep_out_aligned_size) + __field(unsigned, quirk_altset_not_supp) + __field(unsigned, quirk_stall_not_supp) + __field(unsigned, quirk_zlp_not_supp) + __field(unsigned, is_selfpowered) + __field(unsigned, deactivated) + __field(unsigned, connected) + __field(int, ret) + ), + TP_fast_assign( + __entry->speed = g->speed; + __entry->max_speed = g->max_speed; + __entry->state = g->state; + __entry->mA = g->mA; + __entry->sg_supported = g->sg_supported; + __entry->is_otg = g->is_otg; + __entry->is_a_peripheral = g->is_a_peripheral; + __entry->b_hnp_enable = g->b_hnp_enable; + __entry->a_hnp_support = g->a_hnp_support; + __entry->hnp_polling_support = g->hnp_polling_support; + __entry->host_request_flag = g->host_request_flag; + __entry->quirk_ep_out_aligned_size = g->quirk_ep_out_aligned_size; + __entry->quirk_altset_not_supp = g->quirk_altset_not_supp; + __entry->quirk_stall_not_supp = g->quirk_stall_not_supp; + __entry->quirk_zlp_not_supp = g->quirk_zlp_not_supp; + __entry->is_selfpowered = g->is_selfpowered; + __entry->deactivated = g->deactivated; + __entry->connected = g->connected; + __entry->ret = ret; + ), + TP_printk("speed %d/%d state %d %dmA [%s%s%s%s%s%s%s%s%s%s%s%s%s%s] --> %d", + __entry->speed, __entry->max_speed, __entry->state, __entry->mA, + __entry->sg_supported ? "sg:" : "", + __entry->is_otg ? "OTG:" : "", + __entry->is_a_peripheral ? "a_peripheral:" : "", + __entry->b_hnp_enable ? "b_hnp:" : "", + __entry->a_hnp_support ? "a_hnp:" : "", + __entry->hnp_polling_support ? "hnp_poll:" : "", + __entry->host_request_flag ? "hostreq:" : "", + __entry->quirk_ep_out_aligned_size ? "out_aligned:" : "", + __entry->quirk_altset_not_supp ? "no_altset:" : "", + __entry->quirk_stall_not_supp ? "no_stall:" : "", + __entry->quirk_zlp_not_supp ? "no_zlp" : "", + __entry->is_selfpowered ? "self-powered:" : "bus-powered:", + __entry->deactivated ? "deactivated:" : "activated:", + __entry->connected ? "connected" : "disconnected", + __entry->ret) +); + +DEFINE_EVENT(udc_log_gadget, usb_gadget_frame_number, + TP_PROTO(struct usb_gadget *g, int ret), + TP_ARGS(g, ret) +); + +DEFINE_EVENT(udc_log_gadget, usb_gadget_wakeup, + TP_PROTO(struct usb_gadget *g, int ret), + TP_ARGS(g, ret) +); + +DEFINE_EVENT(udc_log_gadget, usb_gadget_set_selfpowered, + TP_PROTO(struct usb_gadget *g, int ret), + TP_ARGS(g, ret) +); + +DEFINE_EVENT(udc_log_gadget, usb_gadget_clear_selfpowered, + TP_PROTO(struct usb_gadget *g, int ret), + TP_ARGS(g, ret) +); + +DEFINE_EVENT(udc_log_gadget, usb_gadget_vbus_connect, + TP_PROTO(struct usb_gadget *g, int ret), + TP_ARGS(g, ret) +); + +DEFINE_EVENT(udc_log_gadget, usb_gadget_vbus_draw, + TP_PROTO(struct usb_gadget *g, int ret), + TP_ARGS(g, ret) +); + +DEFINE_EVENT(udc_log_gadget, usb_gadget_vbus_disconnect, + TP_PROTO(struct usb_gadget *g, int ret), + TP_ARGS(g, ret) +); + +DEFINE_EVENT(udc_log_gadget, usb_gadget_connect, + TP_PROTO(struct usb_gadget *g, int ret), + TP_ARGS(g, ret) +); + +DEFINE_EVENT(udc_log_gadget, usb_gadget_disconnect, + TP_PROTO(struct usb_gadget *g, int ret), + TP_ARGS(g, ret) +); + +DEFINE_EVENT(udc_log_gadget, usb_gadget_deactivate, + TP_PROTO(struct usb_gadget *g, int ret), + TP_ARGS(g, ret) +); + +DEFINE_EVENT(udc_log_gadget, usb_gadget_activate, + TP_PROTO(struct usb_gadget *g, int ret), + TP_ARGS(g, ret) +); + +DECLARE_EVENT_CLASS(udc_log_ep, + TP_PROTO(struct usb_ep *ep, int ret), + TP_ARGS(ep, ret), + TP_STRUCT__entry( + __dynamic_array(char, name, UDC_TRACE_STR_MAX) + __field(unsigned, maxpacket) + __field(unsigned, maxpacket_limit) + __field(unsigned, max_streams) + __field(unsigned, mult) + __field(unsigned, maxburst) + __field(u8, address) + __field(bool, claimed) + __field(bool, enabled) + __field(int, ret) + ), + TP_fast_assign( + snprintf(__get_str(name), UDC_TRACE_STR_MAX, "%s", ep->name); + __entry->maxpacket = ep->maxpacket; + __entry->maxpacket_limit = ep->maxpacket_limit; + __entry->max_streams = ep->max_streams; + __entry->mult = ep->mult; + __entry->maxburst = ep->maxburst; + __entry->address = ep->address, + __entry->claimed = ep->claimed; + __entry->enabled = ep->enabled; + __entry->ret = ret; + ), + TP_printk("%s: mps %d/%d streams %d mult %d burst %d addr %02x %s%s --> %d", + __get_str(name), __entry->maxpacket, __entry->maxpacket_limit, + __entry->max_streams, __entry->mult, __entry->maxburst, + __entry->address, __entry->claimed ? "claimed:" : "released:", + __entry->enabled ? "enabled" : "disabled", ret) +); + +DEFINE_EVENT(udc_log_ep, usb_ep_set_maxpacket_limit, + TP_PROTO(struct usb_ep *ep, int ret), + TP_ARGS(ep, ret) +); + +DEFINE_EVENT(udc_log_ep, usb_ep_enable, + TP_PROTO(struct usb_ep *ep, int ret), + TP_ARGS(ep, ret) +); + +DEFINE_EVENT(udc_log_ep, usb_ep_disable, + TP_PROTO(struct usb_ep *ep, int ret), + TP_ARGS(ep, ret) +); + +DEFINE_EVENT(udc_log_ep, usb_ep_set_halt, + TP_PROTO(struct usb_ep *ep, int ret), + TP_ARGS(ep, ret) +); + +DEFINE_EVENT(udc_log_ep, usb_ep_clear_halt, + TP_PROTO(struct usb_ep *ep, int ret), + TP_ARGS(ep, ret) +); + +DEFINE_EVENT(udc_log_ep, usb_ep_set_wedge, + TP_PROTO(struct usb_ep *ep, int ret), + TP_ARGS(ep, ret) +); + +DEFINE_EVENT(udc_log_ep, usb_ep_fifo_status, + TP_PROTO(struct usb_ep *ep, int ret), + TP_ARGS(ep, ret) +); + +DEFINE_EVENT(udc_log_ep, usb_ep_fifo_flush, + TP_PROTO(struct usb_ep *ep, int ret), + TP_ARGS(ep, ret) +); + +DECLARE_EVENT_CLASS(udc_log_req, + TP_PROTO(struct usb_ep *ep, struct usb_request *req, int ret), + TP_ARGS(ep, req, ret), + TP_STRUCT__entry( + __dynamic_array(char, name, UDC_TRACE_STR_MAX) + __field(unsigned, length) + __field(unsigned, actual) + __field(unsigned, num_sgs) + __field(unsigned, num_mapped_sgs) + __field(unsigned, stream_id) + __field(unsigned, no_interrupt) + __field(unsigned, zero) + __field(unsigned, short_not_ok) + __field(int, status) + __field(int, ret) + ), + TP_fast_assign( + snprintf(__get_str(name), UDC_TRACE_STR_MAX, "%s", ep->name); + __entry->length = req->length; + __entry->actual = req->actual; + __entry->num_sgs = req->num_sgs; + __entry->num_mapped_sgs = req->num_mapped_sgs; + __entry->stream_id = req->stream_id; + __entry->no_interrupt = req->no_interrupt; + __entry->zero = req->zero; + __entry->short_not_ok = req->short_not_ok; + __entry->status = req->status; + __entry->ret = ret; + ), + TP_printk("%s: length %d/%d sgs %d/%d stream %d %s%s%s status %d --> %d", + __get_str(name), __entry->actual, __entry->length, + __entry->num_mapped_sgs, __entry->num_sgs, __entry->stream_id, + __entry->zero ? "Z" : "z", + __entry->short_not_ok ? "S" : "s", + __entry->no_interrupt ? "i" : "I", + __entry->status, __entry->ret + ) +); + +DEFINE_EVENT(udc_log_req, usb_ep_alloc_request, + TP_PROTO(struct usb_ep *ep, struct usb_request *req, int ret), + TP_ARGS(ep, req, ret) +); + +DEFINE_EVENT(udc_log_req, usb_ep_free_request, + TP_PROTO(struct usb_ep *ep, struct usb_request *req, int ret), + TP_ARGS(ep, req, ret) +); + +DEFINE_EVENT(udc_log_req, usb_ep_queue, + TP_PROTO(struct usb_ep *ep, struct usb_request *req, int ret), + TP_ARGS(ep, req, ret) +); + +DEFINE_EVENT(udc_log_req, usb_ep_dequeue, + TP_PROTO(struct usb_ep *ep, struct usb_request *req, int ret), + TP_ARGS(ep, req, ret) +); + +DEFINE_EVENT(udc_log_req, usb_gadget_giveback_request, + TP_PROTO(struct usb_ep *ep, struct usb_request *req, int ret), + TP_ARGS(ep, req, ret) +); + +#endif /* __UDC_TRACE_H */ + +/* this part has to be here */ + +#undef TRACE_INCLUDE_PATH +#define TRACE_INCLUDE_PATH . + +#undef TRACE_INCLUDE_FILE +#define TRACE_INCLUDE_FILE trace + +#include diff --git a/include/linux/usb/gadget.h b/include/linux/usb/gadget.h index c6e1149ddb0d..612dbdfa388e 100644 --- a/include/linux/usb/gadget.h +++ b/include/linux/usb/gadget.h @@ -25,6 +25,8 @@ #include #include +#define UDC_TRACE_STR_MAX 512 + struct usb_ep; /** @@ -324,6 +326,7 @@ struct usb_gadget_ops { * @dev: Driver model state for this abstract device. * @out_epnum: last used out ep number * @in_epnum: last used in ep number + * @mA: last set mA value * @otg_caps: OTG capabilities of this gadget. * @sg_supported: true if we can handle scatter-gather * @is_otg: True if the USB device port uses a Mini-AB jack, so that the @@ -380,6 +383,7 @@ struct usb_gadget { struct device dev; unsigned out_epnum; unsigned in_epnum; + unsigned mA; struct usb_otg_caps *otg_caps; unsigned sg_supported:1; From 21e64bf20df5e120b37c403b46395d4f0c5d8e86 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Thu, 2 Jun 2016 12:37:31 +0300 Subject: [PATCH 065/109] usb: dwc3: gadget: rename 'ignore' argument to 'modify' 'modify' is what the current action is called. Let's rename it so it matches databook. While at that, also make sure to add support 'init' action too. Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/gadget.c | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index d2884a414e20..c889ee371cb6 100644 --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c @@ -462,10 +462,14 @@ static int dwc3_gadget_start_config(struct dwc3 *dwc, struct dwc3_ep *dep) static int dwc3_gadget_set_ep_config(struct dwc3 *dwc, struct dwc3_ep *dep, const struct usb_endpoint_descriptor *desc, const struct usb_ss_ep_comp_descriptor *comp_desc, - bool ignore, bool restore) + bool modify, bool restore) { struct dwc3_gadget_ep_cmd_params params; + if (dev_WARN_ONCE(dwc->dev, modify && restore, + "Can't modify and restore\n")) + return -EINVAL; + memset(¶ms, 0x00, sizeof(params)); params.param0 = DWC3_DEPCFG_EP_TYPE(usb_endpoint_type(desc)) @@ -477,12 +481,13 @@ static int dwc3_gadget_set_ep_config(struct dwc3 *dwc, struct dwc3_ep *dep, params.param0 |= DWC3_DEPCFG_BURST_SIZE(burst - 1); } - if (ignore) - params.param0 |= DWC3_DEPCFG_IGN_SEQ_NUM; - - if (restore) { + if (modify) { + params.param0 |= DWC3_DEPCFG_ACTION_MODIFY; + } else if (restore) { params.param0 |= DWC3_DEPCFG_ACTION_RESTORE; params.param2 |= dep->saved_state; + } else { + params.param0 |= DWC3_DEPCFG_ACTION_INIT; } params.param1 = DWC3_DEPCFG_XFER_COMPLETE_EN; @@ -544,7 +549,7 @@ static int dwc3_gadget_set_xfer_resource(struct dwc3 *dwc, struct dwc3_ep *dep) static int __dwc3_gadget_ep_enable(struct dwc3_ep *dep, const struct usb_endpoint_descriptor *desc, const struct usb_ss_ep_comp_descriptor *comp_desc, - bool ignore, bool restore) + bool modify, bool restore) { struct dwc3 *dwc = dep->dwc; u32 reg; @@ -558,7 +563,7 @@ static int __dwc3_gadget_ep_enable(struct dwc3_ep *dep, return ret; } - ret = dwc3_gadget_set_ep_config(dwc, dep, desc, comp_desc, ignore, + ret = dwc3_gadget_set_ep_config(dwc, dep, desc, comp_desc, modify, restore); if (ret) return ret; From e6fe66fe08cdf9c5d0eb6a6e209621f74f7ee60b Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Tue, 7 Jun 2016 12:49:52 +0300 Subject: [PATCH 066/109] usb: dwc3: pci: add dr-mode for Intel dwc3 It's know that Intel's SoCs' dwc3 integration is peripheral-only since Intel implements its own portmux for role-swapping. In order to prevent dwc3 from ever registering and XHCI platform_device, let's just set dr-mode to peripheral-only on Intel SoCs. Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/dwc3-pci.c | 52 +++++++++++++++++++++++-------------- 1 file changed, 32 insertions(+), 20 deletions(-) diff --git a/drivers/usb/dwc3/dwc3-pci.c b/drivers/usb/dwc3/dwc3-pci.c index 6bc4c2b08ac7..45f5a232d9fb 100644 --- a/drivers/usb/dwc3/dwc3-pci.c +++ b/drivers/usb/dwc3/dwc3-pci.c @@ -76,33 +76,45 @@ static int dwc3_pci_quirks(struct pci_dev *pdev, struct platform_device *dwc3) return platform_device_add_properties(dwc3, properties); } - if (pdev->vendor == PCI_VENDOR_ID_INTEL && - pdev->device == PCI_DEVICE_ID_INTEL_BYT) { - struct gpio_desc *gpio; + if (pdev->vendor == PCI_VENDOR_ID_INTEL) { + int ret; - acpi_dev_add_driver_gpios(ACPI_COMPANION(&pdev->dev), - acpi_dwc3_byt_gpios); + struct property_entry properties[] = { + PROPERTY_ENTRY_STRING("dr-mode", "peripheral"), + { } + }; - /* - * These GPIOs will turn on the USB2 PHY. Note that we have to - * put the gpio descriptors again here because the phy driver - * might want to grab them, too. - */ - gpio = gpiod_get_optional(&pdev->dev, "cs", GPIOD_OUT_LOW); - if (IS_ERR(gpio)) - return PTR_ERR(gpio); + ret = platform_device_add_properties(dwc3, properties); + if (ret < 0) + return ret; - gpiod_set_value_cansleep(gpio, 1); - gpiod_put(gpio); + if (pdev->device == PCI_DEVICE_ID_INTEL_BYT) { + struct gpio_desc *gpio; - gpio = gpiod_get_optional(&pdev->dev, "reset", GPIOD_OUT_LOW); - if (IS_ERR(gpio)) - return PTR_ERR(gpio); + acpi_dev_add_driver_gpios(ACPI_COMPANION(&pdev->dev), + acpi_dwc3_byt_gpios); + + /* + * These GPIOs will turn on the USB2 PHY. Note that we have to + * put the gpio descriptors again here because the phy driver + * might want to grab them, too. + */ + gpio = gpiod_get_optional(&pdev->dev, "cs", GPIOD_OUT_LOW); + if (IS_ERR(gpio)) + return PTR_ERR(gpio); - if (gpio) { gpiod_set_value_cansleep(gpio, 1); gpiod_put(gpio); - usleep_range(10000, 11000); + + gpio = gpiod_get_optional(&pdev->dev, "reset", GPIOD_OUT_LOW); + if (IS_ERR(gpio)) + return PTR_ERR(gpio); + + if (gpio) { + gpiod_set_value_cansleep(gpio, 1); + gpiod_put(gpio); + usleep_range(10000, 11000); + } } } From 5f82279a0c76825bfc6f5fa213ff82c932161462 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Tue, 7 Jun 2016 12:55:19 +0300 Subject: [PATCH 067/109] usb: dwc3: core: fixup dr_mode fallback selection We shouldn't change a host-only dwc3 to gadget-only if driver is built as gadget-only. Fix that up here. Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/core.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c index 9c4e1d8d01db..8fceeb1a32a4 100644 --- a/drivers/usb/dwc3/core.c +++ b/drivers/usb/dwc3/core.c @@ -975,9 +975,13 @@ static int dwc3_probe(struct platform_device *pdev) goto err0; } - if (IS_ENABLED(CONFIG_USB_DWC3_HOST)) + if (IS_ENABLED(CONFIG_USB_DWC3_HOST) && + (dwc->dr_mode == USB_DR_MODE_OTG || + dwc->dr_mode == USB_DR_MODE_UNKNOWN)) dwc->dr_mode = USB_DR_MODE_HOST; - else if (IS_ENABLED(CONFIG_USB_DWC3_GADGET)) + else if (IS_ENABLED(CONFIG_USB_DWC3_GADGET) && + (dwc->dr_mode == USB_DR_MODE_OTG || + dwc->dr_mode == USB_DR_MODE_UNKNOWN)) dwc->dr_mode = USB_DR_MODE_PERIPHERAL; if (dwc->dr_mode == USB_DR_MODE_UNKNOWN) From d807bdd02845d53047346a4ba6d8934597fba6d6 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Thu, 9 Jun 2016 16:24:08 +0300 Subject: [PATCH 068/109] usb: dwc3: gadget: remove udelay() from run_stop() testing shows that udelay() is unnecessary as controller reaches Halted state almost instantenously as can be seen by our timeout variable never actually decrementing. Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/gadget.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index c889ee371cb6..b3b5df6dcbd2 100644 --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c @@ -1584,7 +1584,6 @@ static int dwc3_gadget_run_stop(struct dwc3 *dwc, int is_on, int suspend) timeout--; if (!timeout) return -ETIMEDOUT; - udelay(1); } while (1); dwc3_trace(trace_dwc3_gadget, "gadget %s data soft-%s", From f2df679b6c556fd3b0b7ffafea170f1679086455 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Thu, 9 Jun 2016 16:31:34 +0300 Subject: [PATCH 069/109] usb: dwc3: gadget: avoid while(1) in run_stop() instead of looping forever and forcing a return if timeout reaches zero, we can just use timeout and loop's break condition directly. Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/gadget.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index b3b5df6dcbd2..9b9367b22ad3 100644 --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c @@ -1581,10 +1581,10 @@ static int dwc3_gadget_run_stop(struct dwc3 *dwc, int is_on, int suspend) if (reg & DWC3_DSTS_DEVCTRLHLT) break; } - timeout--; - if (!timeout) - return -ETIMEDOUT; - } while (1); + } while (--timeout); + + if (!timeout) + return -ETIMEDOUT; dwc3_trace(trace_dwc3_gadget, "gadget %s data soft-%s", dwc->gadget_driver From b6d4e16e831376b676edb3463f2bdaa192e5f7be Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Thu, 9 Jun 2016 16:47:05 +0300 Subject: [PATCH 070/109] usb: dwc3: gadget: simplify run_stop() break condition it's clear now that when is_on=true, we must loop until DWC3_DSTS_DEVCTRLHLT clears; while when is_on=false we must loop until DWC3_DSTS_DEVCTRLHLT gets set. Instead of adding actual if() statements, we can rely on XOR operation to evaluate to true only when the above conditions apply. Then, we can move the break condition back to the while() statement together with our timeout check and the resulting code is very compact and simpler to read. Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/gadget.c | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index 9b9367b22ad3..0afaa9d58562 100644 --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c @@ -1574,14 +1574,8 @@ static int dwc3_gadget_run_stop(struct dwc3 *dwc, int is_on, int suspend) do { reg = dwc3_readl(dwc->regs, DWC3_DSTS); - if (is_on) { - if (!(reg & DWC3_DSTS_DEVCTRLHLT)) - break; - } else { - if (reg & DWC3_DSTS_DEVCTRLHLT) - break; - } - } while (--timeout); + reg &= DWC3_DSTS_DEVCTRLHLT; + } while (--timeout && !(!is_on ^ !reg)); if (!timeout) return -ETIMEDOUT; From 328082376aea6016b63bca1e5c067a9539f9e8c9 Mon Sep 17 00:00:00 2001 From: Roger Quadros Date: Fri, 10 Jun 2016 14:38:02 +0300 Subject: [PATCH 071/109] usb: dwc3: fix runtime PM in error path If there is a failure after pm_runtime_enable/get_sync() we need to call pm_runtime_disable/put_sync(). Otherwise it will lead to an unbalanced pm_runtime_enable() on the subsequent probe if the earlier probe bailed out due to -EPROBE_DEFER. pm_runtime_get_sync() can fail as well so deal with that case too. Signed-off-by: Roger Quadros Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/core.c | 26 ++++++++++++++++++-------- 1 file changed, 18 insertions(+), 8 deletions(-) diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c index 8fceeb1a32a4..ca22e4885116 100644 --- a/drivers/usb/dwc3/core.c +++ b/drivers/usb/dwc3/core.c @@ -965,14 +965,17 @@ static int dwc3_probe(struct platform_device *pdev) pm_runtime_use_autosuspend(dev); pm_runtime_set_autosuspend_delay(dev, DWC3_DEFAULT_AUTOSUSPEND_DELAY); pm_runtime_enable(dev); - pm_runtime_get_sync(dev); + ret = pm_runtime_get_sync(dev); + if (ret < 0) + goto err1; + pm_runtime_forbid(dev); ret = dwc3_alloc_event_buffers(dwc, DWC3_EVENT_BUFFERS_SIZE); if (ret) { dev_err(dwc->dev, "failed to allocate event buffers\n"); ret = -ENOMEM; - goto err0; + goto err2; } if (IS_ENABLED(CONFIG_USB_DWC3_HOST) && @@ -989,12 +992,12 @@ static int dwc3_probe(struct platform_device *pdev) ret = dwc3_alloc_scratch_buffers(dwc); if (ret) - goto err1; + goto err3; ret = dwc3_core_init(dwc); if (ret) { dev_err(dev, "failed to initialize core\n"); - goto err2; + goto err4; } /* Check the maximum_speed parameter */ @@ -1026,23 +1029,30 @@ static int dwc3_probe(struct platform_device *pdev) ret = dwc3_core_init_mode(dwc); if (ret) - goto err3; + goto err5; dwc3_debugfs_init(dwc); pm_runtime_put(dev); return 0; -err3: +err5: dwc3_event_buffers_cleanup(dwc); -err2: +err4: dwc3_free_scratch_buffers(dwc); -err1: +err3: dwc3_free_event_buffers(dwc); dwc3_ulpi_exit(dwc); +err2: + pm_runtime_allow(&pdev->dev); + +err1: + pm_runtime_put_sync(&pdev->dev); + pm_runtime_disable(&pdev->dev); + err0: /* * restore res->start back to its original value so that, in case the From 0e146028eebf989e86d3fe9385b76434e954c84e Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Tue, 21 Jun 2016 10:32:02 +0300 Subject: [PATCH 072/109] usb: dwc3: gadget: issue ENDTRANSFER conditional on resource_index Because of recent changes to transfer handling on DWC3, we will not get XferComplete unless we completely fill up our TRB ring. This means that we might get a Reset or Disconnect without getting a XferComplete first. In order to correctly release our allocated Transfer Resource, we must issue ENDTRANSFER command whenever dep->resource_index is valid. Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/gadget.c | 20 +++++--------------- 1 file changed, 5 insertions(+), 15 deletions(-) diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index 0afaa9d58562..b1fec36f4764 100644 --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c @@ -607,24 +607,14 @@ static void dwc3_stop_active_transfer(struct dwc3 *dwc, u32 epnum, bool force); static void dwc3_remove_requests(struct dwc3 *dwc, struct dwc3_ep *dep) { struct dwc3_request *req; - struct dwc3_trb *current_trb; - unsigned transfer_in_flight; - if (dep->number > 1) - current_trb = &dep->trb_pool[dep->trb_enqueue]; - else - current_trb = &dwc->ep0_trb[dep->trb_enqueue]; - transfer_in_flight = current_trb->ctrl & DWC3_TRB_CTRL_HWO; + dwc3_stop_active_transfer(dwc, dep->number, true); - if (transfer_in_flight && !list_empty(&dep->started_list)) { - dwc3_stop_active_transfer(dwc, dep->number, true); + /* - giveback all requests to gadget driver */ + while (!list_empty(&dep->started_list)) { + req = next_request(&dep->started_list); - /* - giveback all requests to gadget driver */ - while (!list_empty(&dep->started_list)) { - req = next_request(&dep->started_list); - - dwc3_gadget_giveback(dep, req, -ESHUTDOWN); - } + dwc3_gadget_giveback(dep, req, -ESHUTDOWN); } while (!list_empty(&dep->pending_list)) { From da1410be21bfedd16740aee6d772e669cf4e852f Mon Sep 17 00:00:00 2001 From: Baolin Wang Date: Mon, 20 Jun 2016 16:19:48 +0800 Subject: [PATCH 073/109] usb: dwc3: gadget: Add the suspend state checking when stopping gadget It will be crash to stop gadget when the dwc3 device had been into suspend state, thus we need to check if the dwc3 device had been into suspend state when UDC try to stop gadget. Signed-off-by: Baolin Wang Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/gadget.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index b1fec36f4764..b9bc646511ce 100644 --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c @@ -1803,6 +1803,9 @@ err0: static void __dwc3_gadget_stop(struct dwc3 *dwc) { + if (pm_runtime_suspended(dwc->dev)) + return; + dwc3_gadget_disable_irq(dwc); __dwc3_gadget_ep_disable(dwc->eps[0]); __dwc3_gadget_ep_disable(dwc->eps[1]); From b89e5f1a677f3a711047d686115f2416e1200a3b Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Thu, 16 Jun 2016 13:38:25 +0200 Subject: [PATCH 074/109] usb: pxa27x_udc: remove unused function argument We get a warning for this when building with W=1 because the argument gets assigned to something else but never read: drivers/usb/gadget/udc/pxa27x_udc.c: In function 'stop_activity': drivers/usb/gadget/udc/pxa27x_udc.c:1828:74: error: parameter 'driver' set but not used [-Werror=unused-but-set-parameter] This remove the argument entirely. Acked-by: Robert Jarzmik Signed-off-by: Arnd Bergmann Signed-off-by: Felipe Balbi --- drivers/usb/gadget/udc/pxa27x_udc.c | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/drivers/usb/gadget/udc/pxa27x_udc.c b/drivers/usb/gadget/udc/pxa27x_udc.c index 001a3b74a993..ad140aa00132 100644 --- a/drivers/usb/gadget/udc/pxa27x_udc.c +++ b/drivers/usb/gadget/udc/pxa27x_udc.c @@ -1825,13 +1825,10 @@ fail: * Disables all udc endpoints (even control endpoint), report disconnect to * the gadget user. */ -static void stop_activity(struct pxa_udc *udc, struct usb_gadget_driver *driver) +static void stop_activity(struct pxa_udc *udc) { int i; - /* don't disconnect drivers more than once */ - if (udc->gadget.speed == USB_SPEED_UNKNOWN) - driver = NULL; udc->gadget.speed = USB_SPEED_UNKNOWN; for (i = 0; i < NR_USB_ENDPOINTS; i++) @@ -1848,7 +1845,7 @@ static int pxa27x_udc_stop(struct usb_gadget *g) { struct pxa_udc *udc = to_pxa(g); - stop_activity(udc, NULL); + stop_activity(udc); udc_disable(udc); udc->driver = NULL; @@ -2296,7 +2293,7 @@ static void irq_udc_reset(struct pxa_udc *udc) if ((udccr & UDCCR_UDA) == 0) { dev_dbg(udc->dev, "USB reset start\n"); - stop_activity(udc, udc->driver); + stop_activity(udc); } udc->gadget.speed = USB_SPEED_FULL; memset(&udc->stats, 0, sizeof udc->stats); From 106528b21df7dd9da9ca3e5e4ff4e015c33211be Mon Sep 17 00:00:00 2001 From: Vardan Mikayelyan Date: Wed, 25 May 2016 18:06:53 -0700 Subject: [PATCH 075/109] usb: dwc2: Add missing register field definitions Added register field definitions, register names are according DWC-OTG databook. Tested-by: John Keeping Signed-off-by: Vardan Mikayelyan Signed-off-by: John Youn Signed-off-by: Felipe Balbi --- drivers/usb/dwc2/hw.h | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/drivers/usb/dwc2/hw.h b/drivers/usb/dwc2/hw.h index 281b57b36ab4..1126141cf47c 100644 --- a/drivers/usb/dwc2/hw.h +++ b/drivers/usb/dwc2/hw.h @@ -459,6 +459,9 @@ #define DSTS_SUSPSTS (1 << 0) #define DIEPMSK HSOTG_REG(0x810) +#define DIEPMSK_NAKMSK (1 << 13) +#define DIEPMSK_BNAININTRMSK (1 << 9) +#define DIEPMSK_TXFIFOUNDRNMSK (1 << 8) #define DIEPMSK_TXFIFOEMPTY (1 << 7) #define DIEPMSK_INEPNAKEFFMSK (1 << 6) #define DIEPMSK_INTKNEPMISMSK (1 << 5) @@ -470,6 +473,7 @@ #define DOEPMSK HSOTG_REG(0x814) #define DOEPMSK_BACK2BACKSETUP (1 << 6) +#define DOEPMSK_STSPHSERCVDMSK (1 << 5) #define DOEPMSK_OUTTKNEPDISMSK (1 << 4) #define DOEPMSK_SETUPMSK (1 << 3) #define DOEPMSK_AHBERRMSK (1 << 2) @@ -486,6 +490,7 @@ #define DTKNQR2 HSOTG_REG(0x824) #define DTKNQR3 HSOTG_REG(0x830) #define DTKNQR4 HSOTG_REG(0x834) +#define DIEPEMPMSK HSOTG_REG(0x834) #define DVBUSDIS HSOTG_REG(0x828) #define DVBUSPULSE HSOTG_REG(0x82C) @@ -544,6 +549,14 @@ #define DIEPINT(_a) HSOTG_REG(0x908 + ((_a) * 0x20)) #define DOEPINT(_a) HSOTG_REG(0xB08 + ((_a) * 0x20)) #define DXEPINT_SETUP_RCVD (1 << 15) +#define DXEPINT_NYETINTRPT (1 << 14) +#define DXEPINT_NAKINTRPT (1 << 13) +#define DXEPINT_BBLEERRINTRPT (1 << 12) +#define DXEPINT_PKTDRPSTS (1 << 11) +#define DXEPINT_BNAINTR (1 << 9) +#define DXEPINT_TXFIFOUNDRN (1 << 8) +#define DXEPINT_OUTPKTERR (1 << 8) +#define DXEPINT_TXFEMP (1 << 7) #define DXEPINT_INEPNAKEFF (1 << 6) #define DXEPINT_BACK2BACKSETUP (1 << 6) #define DXEPINT_INTKNEPMIS (1 << 5) From 04cde4787d8d9c56bd7b0bc5ee15af4435275134 Mon Sep 17 00:00:00 2001 From: Vardan Mikayelyan Date: Wed, 25 May 2016 18:06:55 -0700 Subject: [PATCH 076/109] usb: dwc2: gadget: Remove unnecessary line Removed "ctrl |= DXEPCTL_USBACTEP" from dwc2_hsotg_start_req() function because this step is done in dwc2_hsotg_ep_enable(). Tested-by: John Keeping Signed-off-by: Vardan Mikayelyan Signed-off-by: John Youn Signed-off-by: Felipe Balbi --- drivers/usb/dwc2/gadget.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/usb/dwc2/gadget.c b/drivers/usb/dwc2/gadget.c index 26cf09d0fe3c..34c63e94f1f5 100644 --- a/drivers/usb/dwc2/gadget.c +++ b/drivers/usb/dwc2/gadget.c @@ -632,7 +632,6 @@ static void dwc2_hsotg_start_req(struct dwc2_hsotg *hsotg, } ctrl |= DXEPCTL_EPENA; /* ensure ep enabled */ - ctrl |= DXEPCTL_USBACTEP; dev_dbg(hsotg->dev, "ep0 state:%d\n", hsotg->ep0_state); From 7c01b99154c5819bb811100738880a1341bca64a Mon Sep 17 00:00:00 2001 From: Vardan Mikayelyan Date: Wed, 25 May 2016 18:06:58 -0700 Subject: [PATCH 077/109] usb: dwc2: gadget: Remove unnecessary code This chunk is not needed here. There is no functionality depend on this, so if no-op, I think we do not need to have this interrupt unmasked. Tested-by: John Keeping Signed-off-by: Vardan Mikayelyan Signed-off-by: John Youn Signed-off-by: Felipe Balbi --- drivers/usb/dwc2/gadget.c | 8 -------- 1 file changed, 8 deletions(-) diff --git a/drivers/usb/dwc2/gadget.c b/drivers/usb/dwc2/gadget.c index 34c63e94f1f5..1487968f590c 100644 --- a/drivers/usb/dwc2/gadget.c +++ b/drivers/usb/dwc2/gadget.c @@ -657,14 +657,6 @@ static void dwc2_hsotg_start_req(struct dwc2_hsotg *hsotg, dwc2_hsotg_write_fifo(hsotg, hs_ep, hs_req); } - /* - * clear the INTknTXFEmpMsk when we start request, more as a aide - * to debugging to see what is going on. - */ - if (dir_in) - dwc2_writel(DIEPMSK_INTKNTXFEMPMSK, - hsotg->regs + DIEPINT(index)); - /* * Note, trying to clear the NAK here causes problems with transmit * on the S3C6400 ending up with the TXFIFO becoming full. From 26ddef5da62993fb9189a822d3545fc00451d242 Mon Sep 17 00:00:00 2001 From: Vardan Mikayelyan Date: Wed, 25 May 2016 18:07:00 -0700 Subject: [PATCH 078/109] usb: dwc2: gadget: Corrected field names No-op change. Changed field names to prevent misunderstanding. Tested-by: John Keeping Signed-off-by: Vardan Mikayelyan Signed-off-by: John Youn Signed-off-by: Felipe Balbi --- drivers/usb/dwc2/gadget.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/usb/dwc2/gadget.c b/drivers/usb/dwc2/gadget.c index 1487968f590c..71681727c66d 100644 --- a/drivers/usb/dwc2/gadget.c +++ b/drivers/usb/dwc2/gadget.c @@ -2037,20 +2037,20 @@ static void dwc2_hsotg_epint(struct dwc2_hsotg *hsotg, unsigned int idx, if (dir_in && !hs_ep->isochronous) { /* not sure if this is important, but we'll clear it anyway */ - if (ints & DIEPMSK_INTKNTXFEMPMSK) { + if (ints & DXEPINT_INTKNTXFEMP) { dev_dbg(hsotg->dev, "%s: ep%d: INTknTXFEmpMsk\n", __func__, idx); } /* this probably means something bad is happening */ - if (ints & DIEPMSK_INTKNEPMISMSK) { + if (ints & DXEPINT_INTKNEPMIS) { dev_warn(hsotg->dev, "%s: ep%d: INTknEP\n", __func__, idx); } /* FIFO has space or is empty (see GAHBCFG) */ if (hsotg->dedicated_fifos && - ints & DIEPMSK_TXFIFOEMPTY) { + ints & DXEPINT_TXFEMP) { dev_dbg(hsotg->dev, "%s: ep%d: TxFIFOEmpty\n", __func__, idx); if (!using_dma(hsotg)) From 6b58cb07a850f9b6d348feb2455b2c264a515f4a Mon Sep 17 00:00:00 2001 From: Vardan Mikayelyan Date: Wed, 25 May 2016 18:07:02 -0700 Subject: [PATCH 079/109] usb: dwc2: gadget: Fix transfer stop programming for out endpoint According DWC-OTG databook, "GOUTNakEff" is read only and can be cleared only by "DCTL.CGOUTNak", but here we do not need to clear it because DWC-OTG programming guide says that before disabling any OUT endpoint, the application must enable Global OUT NAK mode, so if this mode is enabled we can continue without this step. Tested-by: John Keeping Signed-off-by: Vardan Mikayelyan Signed-off-by: John Youn Signed-off-by: Felipe Balbi --- drivers/usb/dwc2/gadget.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/usb/dwc2/gadget.c b/drivers/usb/dwc2/gadget.c index 71681727c66d..54d242b6d2aa 100644 --- a/drivers/usb/dwc2/gadget.c +++ b/drivers/usb/dwc2/gadget.c @@ -2866,10 +2866,8 @@ static void dwc2_hsotg_ep_stop_xfr(struct dwc2_hsotg *hsotg, dev_warn(hsotg->dev, "%s: timeout DIEPINT.NAKEFF\n", __func__); } else { - /* Clear any pending nak effect interrupt */ - dwc2_writel(GINTSTS_GOUTNAKEFF, hsotg->regs + GINTSTS); - - __orr32(hsotg->regs + DCTL, DCTL_SGOUTNAK); + if (!(dwc2_readl(hsotg->regs + GINTSTS) & GINTSTS_GOUTNAKEFF)) + __orr32(hsotg->regs + DCTL, DCTL_SGOUTNAK); /* Wait for global nak to take effect */ if (dwc2_hsotg_wait_bit_set(hsotg, GINTSTS, From 92d1635d781ac17fc7d886b0c126838083f3c2b9 Mon Sep 17 00:00:00 2001 From: Vardan Mikayelyan Date: Wed, 25 May 2016 18:07:05 -0700 Subject: [PATCH 080/109] usb: dwc2: gadget: Add dwc2_gadget_incr_frame_num() Increases and checks targeted frame number of current ep if overrun happened, sets flag and masks with DSTS_SOFFN_LIMIT Added following fields to struct dwc2_hsotg_ep -target_frame: Targeted frame num to setup next ISOC transfer -frame_overrun: Indicates SOF number overrun in DSTS Tested-by: John Keeping Signed-off-by: Vardan Mikayelyan Signed-off-by: John Youn Signed-off-by: Felipe Balbi --- drivers/usb/dwc2/core.h | 5 +++++ drivers/usb/dwc2/gadget.c | 19 +++++++++++++++++++ 2 files changed, 24 insertions(+) diff --git a/drivers/usb/dwc2/core.h b/drivers/usb/dwc2/core.h index dec0b21fc626..55160992b181 100644 --- a/drivers/usb/dwc2/core.h +++ b/drivers/usb/dwc2/core.h @@ -177,6 +177,8 @@ struct dwc2_hsotg_req; * @fifo_load: The amount of data loaded into the FIFO (periodic IN) * @last_load: The offset of data for the last start of request. * @size_loaded: The last loaded size for DxEPTSIZE for periodic IN + * @target_frame: Targeted frame num to setup next ISOC transfer + * @frame_overrun: Indicates SOF number overrun in DSTS * * This is the driver's state for each registered enpoint, allowing it * to keep track of transactions that need doing. Each endpoint has a @@ -214,6 +216,9 @@ struct dwc2_hsotg_ep { unsigned int isochronous:1; unsigned int send_zlp:1; unsigned int has_correct_parity:1; + unsigned int target_frame; +#define TARGET_FRAME_INITIAL 0xFFFFFFFF + bool frame_overrun; char name[10]; }; diff --git a/drivers/usb/dwc2/gadget.c b/drivers/usb/dwc2/gadget.c index 54d242b6d2aa..b8f3661771f0 100644 --- a/drivers/usb/dwc2/gadget.c +++ b/drivers/usb/dwc2/gadget.c @@ -96,6 +96,25 @@ static inline bool using_dma(struct dwc2_hsotg *hsotg) return hsotg->g_using_dma; } +/** + * dwc2_gadget_incr_frame_num - Increments the targeted frame number. + * @hs_ep: The endpoint + * @increment: The value to increment by + * + * This function will also check if the frame number overruns DSTS_SOFFN_LIMIT. + * If an overrun occurs it will wrap the value and set the frame_overrun flag. + */ +static inline void dwc2_gadget_incr_frame_num(struct dwc2_hsotg_ep *hs_ep) +{ + hs_ep->target_frame += hs_ep->interval; + if (hs_ep->target_frame > DSTS_SOFFN_LIMIT) { + hs_ep->frame_overrun = 1; + hs_ep->target_frame &= DSTS_SOFFN_LIMIT; + } else { + hs_ep->frame_overrun = 0; + } +} + /** * dwc2_hsotg_en_gsint - enable one or more of the general interrupt * @hsotg: The device state From 142bd33fcd185d850178f7f8697ecbeaaa18e257 Mon Sep 17 00:00:00 2001 From: Vardan Mikayelyan Date: Wed, 25 May 2016 18:07:07 -0700 Subject: [PATCH 081/109] usb: dwc2: gadget: Corrected interval calculation Calculate the interval according to the USB 2.0 specification section 9.6.6. Tested-by: John Keeping Signed-off-by: Vardan Mikayelyan Signed-off-by: John Youn Signed-off-by: Felipe Balbi --- drivers/usb/dwc2/core.h | 2 +- drivers/usb/dwc2/gadget.c | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/usb/dwc2/core.h b/drivers/usb/dwc2/core.h index 55160992b181..0ba359945dbd 100644 --- a/drivers/usb/dwc2/core.h +++ b/drivers/usb/dwc2/core.h @@ -166,7 +166,7 @@ struct dwc2_hsotg_req; * means that it is sending data to the Host. * @index: The index for the endpoint registers. * @mc: Multi Count - number of transactions per microframe - * @interval - Interval for periodic endpoints + * @interval - Interval for periodic endpoints, in frames or microframes. * @name: The name array passed to the USB core. * @halted: Set if the endpoint has been halted. * @periodic: Set if this is a periodic ep, such as Interrupt diff --git a/drivers/usb/dwc2/gadget.c b/drivers/usb/dwc2/gadget.c index b8f3661771f0..2cef7a9cb527 100644 --- a/drivers/usb/dwc2/gadget.c +++ b/drivers/usb/dwc2/gadget.c @@ -2693,16 +2693,13 @@ static int dwc2_hsotg_ep_enable(struct usb_ep *ep, hs_ep->periodic = 0; hs_ep->halted = 0; hs_ep->interval = desc->bInterval; - hs_ep->has_correct_parity = 0; - - if (hs_ep->interval > 1 && hs_ep->mc > 1) - dev_err(hsotg->dev, "MC > 1 when interval is not 1\n"); switch (desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) { case USB_ENDPOINT_XFER_ISOC: epctrl |= DXEPCTL_EPTYPE_ISO; epctrl |= DXEPCTL_SETEVENFR; hs_ep->isochronous = 1; + hs_ep->interval = 1 << (desc->bInterval - 1); if (dir_in) hs_ep->periodic = 1; break; @@ -2715,6 +2712,9 @@ static int dwc2_hsotg_ep_enable(struct usb_ep *ep, if (dir_in) hs_ep->periodic = 1; + if (hsotg->gadget.speed == USB_SPEED_HIGH) + hs_ep->interval = 1 << (desc->bInterval - 1); + epctrl |= DXEPCTL_EPTYPE_INTERRUPT; break; From 326015887b6a1321fd61f7a16816241ad841a03c Mon Sep 17 00:00:00 2001 From: Vardan Mikayelyan Date: Wed, 25 May 2016 18:07:10 -0700 Subject: [PATCH 082/109] usb: dwc2: gadget: Add dwc2_gadget_read_ep_interrupts function Reads and returns interrupts for given endpoint, by masking epint_reg with corresponding mask. Tested-by: John Keeping Signed-off-by: Vardan Mikayelyan Signed-off-by: John Youn Signed-off-by: Felipe Balbi --- drivers/usb/dwc2/gadget.c | 30 +++++++++++++++++++++++++++++- 1 file changed, 29 insertions(+), 1 deletion(-) diff --git a/drivers/usb/dwc2/gadget.c b/drivers/usb/dwc2/gadget.c index 2cef7a9cb527..8139efdf00fb 100644 --- a/drivers/usb/dwc2/gadget.c +++ b/drivers/usb/dwc2/gadget.c @@ -1946,6 +1946,34 @@ static void dwc2_hsotg_complete_in(struct dwc2_hsotg *hsotg, dwc2_hsotg_complete_request(hsotg, hs_ep, hs_req, 0); } +/** + * dwc2_gadget_read_ep_interrupts - reads interrupts for given ep + * @hsotg: The device state. + * @idx: Index of ep. + * @dir_in: Endpoint direction 1-in 0-out. + * + * Reads for endpoint with given index and direction, by masking + * epint_reg with coresponding mask. + */ +static u32 dwc2_gadget_read_ep_interrupts(struct dwc2_hsotg *hsotg, + unsigned int idx, int dir_in) +{ + u32 epmsk_reg = dir_in ? DIEPMSK : DOEPMSK; + u32 epint_reg = dir_in ? DIEPINT(idx) : DOEPINT(idx); + u32 ints; + u32 mask; + u32 diepempmsk; + + mask = dwc2_readl(hsotg->regs + epmsk_reg); + diepempmsk = dwc2_readl(hsotg->regs + DIEPEMPMSK); + mask |= ((diepempmsk >> idx) & 0x1) ? DIEPMSK_TXFIFOEMPTY : 0; + mask |= DXEPINT_SETUP_RCVD; + + ints = dwc2_readl(hsotg->regs + epint_reg); + ints &= mask; + return ints; +} + /** * dwc2_hsotg_epint - handle an in/out endpoint interrupt * @hsotg: The driver state @@ -1964,7 +1992,7 @@ static void dwc2_hsotg_epint(struct dwc2_hsotg *hsotg, unsigned int idx, u32 ints; u32 ctrl; - ints = dwc2_readl(hsotg->regs + epint_reg); + ints = dwc2_gadget_read_ep_interrupts(hsotg, idx, dir_in); ctrl = dwc2_readl(hsotg->regs + epctl_reg); /* Clear endpoint interrupts */ From 41cc4cd2716fa6d18a1a09d740ea075adecfa7dd Mon Sep 17 00:00:00 2001 From: Vardan Mikayelyan Date: Wed, 25 May 2016 18:07:12 -0700 Subject: [PATCH 083/109] usb: dwc2: gadget: Add dwc2_gadget_start_next_request function Replaced repeating code with function call. Starts next request from ep queue. If queue is empty and ep is isoc -In case of OUT-EP unmasks OUTTKNEPDIS. OUTTKNEPDIS is masked in it's handler, so we need to unmask it here to be able to do resynchronization. Tested-by: John Keeping Signed-off-by: Vardan Mikayelyan Signed-off-by: John Youn Signed-off-by: Felipe Balbi --- drivers/usb/dwc2/gadget.c | 51 +++++++++++++++++++++++++++++---------- 1 file changed, 38 insertions(+), 13 deletions(-) diff --git a/drivers/usb/dwc2/gadget.c b/drivers/usb/dwc2/gadget.c index 8139efdf00fb..7db9f9fa40de 100644 --- a/drivers/usb/dwc2/gadget.c +++ b/drivers/usb/dwc2/gadget.c @@ -1044,6 +1044,42 @@ static struct dwc2_hsotg_req *get_ep_head(struct dwc2_hsotg_ep *hs_ep) return list_first_entry(&hs_ep->queue, struct dwc2_hsotg_req, queue); } +/** + * dwc2_gadget_start_next_request - Starts next request from ep queue + * @hs_ep: Endpoint structure + * + * If queue is empty and EP is ISOC-OUT - unmasks OUTTKNEPDIS which is masked + * in its handler. Hence we need to unmask it here to be able to do + * resynchronization. + */ +static void dwc2_gadget_start_next_request(struct dwc2_hsotg_ep *hs_ep) +{ + u32 mask; + struct dwc2_hsotg *hsotg = hs_ep->parent; + int dir_in = hs_ep->dir_in; + struct dwc2_hsotg_req *hs_req; + u32 epmsk_reg = dir_in ? DIEPMSK : DOEPMSK; + + if (!list_empty(&hs_ep->queue)) { + hs_req = get_ep_head(hs_ep); + dwc2_hsotg_start_req(hsotg, hs_ep, hs_req, false); + return; + } + if (!hs_ep->isochronous) + return; + + if (dir_in) { + dev_dbg(hsotg->dev, "%s: No more ISOC-IN requests\n", + __func__); + } else { + dev_dbg(hsotg->dev, "%s: No more ISOC-OUT requests\n", + __func__); + mask = dwc2_readl(hsotg->regs + epmsk_reg); + mask |= DOEPMSK_OUTTKNEPDISMSK; + dwc2_writel(mask, hsotg->regs + epmsk_reg); + } +} + /** * dwc2_hsotg_process_req_feature - process request {SET,CLEAR}_FEATURE * @hsotg: The device state @@ -1054,7 +1090,6 @@ static int dwc2_hsotg_process_req_feature(struct dwc2_hsotg *hsotg, { struct dwc2_hsotg_ep *ep0 = hsotg->eps_out[0]; struct dwc2_hsotg_req *hs_req; - bool restart; bool set = (ctrl->bRequest == USB_REQ_SET_FEATURE); struct dwc2_hsotg_ep *ep; int ret; @@ -1137,12 +1172,7 @@ static int dwc2_hsotg_process_req_feature(struct dwc2_hsotg *hsotg, /* If we have pending request, then start it */ if (!ep->req) { - restart = !list_empty(&ep->queue); - if (restart) { - hs_req = get_ep_head(ep); - dwc2_hsotg_start_req(hsotg, ep, - hs_req, false); - } + dwc2_gadget_start_next_request(ep); } } @@ -1383,7 +1413,6 @@ static void dwc2_hsotg_complete_request(struct dwc2_hsotg *hsotg, struct dwc2_hsotg_req *hs_req, int result) { - bool restart; if (!hs_req) { dev_dbg(hsotg->dev, "%s: nothing to complete?\n", __func__); @@ -1427,11 +1456,7 @@ static void dwc2_hsotg_complete_request(struct dwc2_hsotg *hsotg, */ if (!hs_ep->req && result >= 0) { - restart = !list_empty(&hs_ep->queue); - if (restart) { - hs_req = get_ep_head(hs_ep); - dwc2_hsotg_start_req(hsotg, hs_ep, hs_req, false); - } + dwc2_gadget_start_next_request(hs_ep); } } From 5321922cb6fa0f513fdd8ce73b281f4b9957886b Mon Sep 17 00:00:00 2001 From: Vardan Mikayelyan Date: Wed, 25 May 2016 18:07:14 -0700 Subject: [PATCH 084/109] usb: dwc2: gadget: Add OUTTKNEPDIS and NAKINTRPT handlers NAKINTRPT interrupt is starting point for isoc-in transfer, synchronization done with first in token received from host, core asserts this interrupt when responds with 0 length data to in token, received from host. The first IN token is asynchronous for device - device does not know when first one token will arrive from host. On first token arrival HW generates 2 interrupts: 'in token received while FIFO empty' and 'NAK'. NAK interrupt for ISOC in means that token has arrived and ZLP was sent in response to that as there was no data in FIFO. SW is basing on this interrupt to obtain frame in which token has come and then based on the interval calculates next frame for transfer. OUTTKNEPDIS interrupt is starting point for isoc-out transfer, synchronization done with first out token received from host while corresponding ep is disabled. For OUTs the reason is same - device does not know initial frame in which out token will come. For this HW generates OUTTKNEPDIS - out token is received while EP is disabled. Upon getting this interrupt SW starts calculation for next transfer frame. Tested-by: John Keeping Signed-off-by: Vardan Mikayelyan Signed-off-by: John Youn Signed-off-by: Felipe Balbi --- drivers/usb/dwc2/gadget.c | 94 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 94 insertions(+) diff --git a/drivers/usb/dwc2/gadget.c b/drivers/usb/dwc2/gadget.c index 7db9f9fa40de..256c929fece9 100644 --- a/drivers/usb/dwc2/gadget.c +++ b/drivers/usb/dwc2/gadget.c @@ -1999,6 +1999,94 @@ static u32 dwc2_gadget_read_ep_interrupts(struct dwc2_hsotg *hsotg, return ints; } +/** + * dwc2_gadget_handle_out_token_ep_disabled - handle DXEPINT_OUTTKNEPDIS + * @hs_ep: The endpoint on which interrupt is asserted. + * + * This is starting point for ISOC-OUT transfer, synchronization done with + * first out token received from host while corresponding EP is disabled. + * + * Device does not know initial frame in which out token will come. For this + * HW generates OUTTKNEPDIS - out token is received while EP is disabled. Upon + * getting this interrupt SW starts calculation for next transfer frame. + */ +static void dwc2_gadget_handle_out_token_ep_disabled(struct dwc2_hsotg_ep *ep) +{ + struct dwc2_hsotg *hsotg = ep->parent; + int dir_in = ep->dir_in; + u32 doepmsk; + + if (dir_in || !ep->isochronous) + return; + + dwc2_hsotg_complete_request(hsotg, ep, get_ep_head(ep), -ENODATA); + + if (ep->interval > 1 && + ep->target_frame == TARGET_FRAME_INITIAL) { + u32 dsts; + u32 ctrl; + + dsts = dwc2_readl(hsotg->regs + DSTS); + ep->target_frame = dwc2_hsotg_read_frameno(hsotg); + dwc2_gadget_incr_frame_num(ep); + + ctrl = dwc2_readl(hsotg->regs + DOEPCTL(ep->index)); + if (ep->target_frame & 0x1) + ctrl |= DXEPCTL_SETODDFR; + else + ctrl |= DXEPCTL_SETEVENFR; + + dwc2_writel(ctrl, hsotg->regs + DOEPCTL(ep->index)); + } + + dwc2_gadget_start_next_request(ep); + doepmsk = dwc2_readl(hsotg->regs + DOEPMSK); + doepmsk &= ~DOEPMSK_OUTTKNEPDISMSK; + dwc2_writel(doepmsk, hsotg->regs + DOEPMSK); +} + +/** +* dwc2_gadget_handle_nak - handle NAK interrupt +* @hs_ep: The endpoint on which interrupt is asserted. +* +* This is starting point for ISOC-IN transfer, synchronization done with +* first IN token received from host while corresponding EP is disabled. +* +* Device does not know when first one token will arrive from host. On first +* token arrival HW generates 2 interrupts: 'in token received while FIFO empty' +* and 'NAK'. NAK interrupt for ISOC-IN means that token has arrived and ZLP was +* sent in response to that as there was no data in FIFO. SW is basing on this +* interrupt to obtain frame in which token has come and then based on the +* interval calculates next frame for transfer. +*/ +static void dwc2_gadget_handle_nak(struct dwc2_hsotg_ep *hs_ep) +{ + struct dwc2_hsotg *hsotg = hs_ep->parent; + int dir_in = hs_ep->dir_in; + + if (!dir_in || !hs_ep->isochronous) + return; + + if (hs_ep->target_frame == TARGET_FRAME_INITIAL) { + hs_ep->target_frame = dwc2_hsotg_read_frameno(hsotg); + if (hs_ep->interval > 1) { + u32 ctrl = dwc2_readl(hsotg->regs + + DIEPCTL(hs_ep->index)); + if (hs_ep->target_frame & 0x1) + ctrl |= DXEPCTL_SETODDFR; + else + ctrl |= DXEPCTL_SETEVENFR; + + dwc2_writel(ctrl, hsotg->regs + DIEPCTL(hs_ep->index)); + } + + dwc2_hsotg_complete_request(hsotg, hs_ep, + get_ep_head(hs_ep), 0); + } + + dwc2_gadget_incr_frame_num(hs_ep); +} + /** * dwc2_hsotg_epint - handle an in/out endpoint interrupt * @hsotg: The driver state @@ -2083,6 +2171,12 @@ static void dwc2_hsotg_epint(struct dwc2_hsotg *hsotg, unsigned int idx, } } + if (ints & DXEPINT_OUTTKNEPDIS) + dwc2_gadget_handle_out_token_ep_disabled(hs_ep); + + if (ints & DXEPINT_NAKINTRPT) + dwc2_gadget_handle_nak(hs_ep); + if (ints & DXEPINT_AHBERR) dev_dbg(hsotg->dev, "%s: AHBErr\n", __func__); From 381fc8f8228923026b3d75c8230fa2ee4d688f32 Mon Sep 17 00:00:00 2001 From: Vardan Mikayelyan Date: Wed, 25 May 2016 18:07:17 -0700 Subject: [PATCH 085/109] usb: dwc2: gadget: Add Incomplete ISO IN/OUT Interrupt handlers Incomplete ISO IN interrupt indicates one of the following conditions occurred while transmitting an ISOC transaction. - Corrupted IN Token for ISOC EP. - Packet not complete in FIFO. Incomplete ISO OUT indicates that there is at least one isochronous OUT endpoint on which the transfer is not completed in the current microframe. The following actions will be taken: In case of EP-IN - Determine the EP - Disable EP directly from this handler; when "Endpoint Disabled" interrupt is received flush FIFO In case of EP-OUT - Determine the EP - If target frame elapsed set DCTL_SGOUTNAK, unmask GOUTNAKEFF and proceed as described in section 7.5.1 of DWC-HSOTG Programming Guide Also added dwc2_gadget_target_frame_elapsed() helper function which will be used in Incomplete ISO IN/OUT Interrupt handlers. Tested-by: John Keeping Signed-off-by: Vardan Mikayelyan Signed-off-by: John Youn Signed-off-by: Felipe Balbi --- drivers/usb/dwc2/gadget.c | 173 +++++++++++++++++++++++++++----------- 1 file changed, 124 insertions(+), 49 deletions(-) diff --git a/drivers/usb/dwc2/gadget.c b/drivers/usb/dwc2/gadget.c index 256c929fece9..61f913d491be 100644 --- a/drivers/usb/dwc2/gadget.c +++ b/drivers/usb/dwc2/gadget.c @@ -522,6 +522,23 @@ static unsigned get_ep_limit(struct dwc2_hsotg_ep *hs_ep) return maxsize; } +/** +* dwc2_hsotg_read_frameno - read current frame number +* @hsotg: The device instance +* +* Return the current frame number +*/ +static u32 dwc2_hsotg_read_frameno(struct dwc2_hsotg *hsotg) +{ + u32 dsts; + + dsts = dwc2_readl(hsotg->regs + DSTS); + dsts &= DSTS_SOFFN_MASK; + dsts >>= DSTS_SOFFN_SHIFT; + + return dsts; +} + /** * dwc2_hsotg_start_req - start a USB request from an endpoint's queue * @hsotg: The controller state. @@ -783,6 +800,30 @@ static void dwc2_hsotg_handle_unaligned_buf_complete(struct dwc2_hsotg *hsotg, hs_req->saved_req_buf = NULL; } +/** + * dwc2_gadget_target_frame_elapsed - Checks target frame + * @hs_ep: The driver endpoint to check + * + * Returns 1 if targeted frame elapsed. If returned 1 then we need to drop + * corresponding transfer. + */ +static bool dwc2_gadget_target_frame_elapsed(struct dwc2_hsotg_ep *hs_ep) +{ + struct dwc2_hsotg *hsotg = hs_ep->parent; + u32 target_frame = hs_ep->target_frame; + u32 current_frame = dwc2_hsotg_read_frameno(hsotg); + bool frame_overrun = hs_ep->frame_overrun; + + if (!frame_overrun && current_frame >= target_frame) + return true; + + if (frame_overrun && current_frame >= target_frame && + ((current_frame - target_frame) < DSTS_SOFFN_LIMIT / 2)) + return true; + + return false; +} + static int dwc2_hsotg_ep_queue(struct usb_ep *ep, struct usb_request *req, gfp_t gfp_flags) { @@ -1640,23 +1681,6 @@ static void dwc2_hsotg_handle_outdone(struct dwc2_hsotg *hsotg, int epnum) dwc2_hsotg_complete_request(hsotg, hs_ep, hs_req, result); } -/** - * dwc2_hsotg_read_frameno - read current frame number - * @hsotg: The device instance - * - * Return the current frame number - */ -static u32 dwc2_hsotg_read_frameno(struct dwc2_hsotg *hsotg) -{ - u32 dsts; - - dsts = dwc2_readl(hsotg->regs + DSTS); - dsts &= DSTS_SOFFN_MASK; - dsts >>= DSTS_SOFFN_SHIFT; - - return dsts; -} - /** * dwc2_hsotg_handle_rx - RX FIFO has data * @hsotg: The device instance @@ -2570,6 +2594,85 @@ void dwc2_hsotg_core_connect(struct dwc2_hsotg *hsotg) __bic32(hsotg->regs + DCTL, DCTL_SFTDISCON); } +/** + * dwc2_gadget_handle_incomplete_isoc_in - handle incomplete ISO IN Interrupt. + * @hsotg: The device state: + * + * This interrupt indicates one of the following conditions occurred while + * transmitting an ISOC transaction. + * - Corrupted IN Token for ISOC EP. + * - Packet not complete in FIFO. + * + * The following actions will be taken: + * - Determine the EP + * - Disable EP; when 'Endpoint Disabled' interrupt is received Flush FIFO + */ +static void dwc2_gadget_handle_incomplete_isoc_in(struct dwc2_hsotg *hsotg) +{ + struct dwc2_hsotg_ep *hs_ep; + u32 epctrl; + u32 idx; + + dev_dbg(hsotg->dev, "Incomplete isoc in interrupt received:\n"); + + for (idx = 1; idx <= hsotg->num_of_eps; idx++) { + hs_ep = hsotg->eps_in[idx]; + epctrl = dwc2_readl(hsotg->regs + DIEPCTL(idx)); + if ((epctrl & DXEPCTL_EPENA) && hs_ep->isochronous && + dwc2_gadget_target_frame_elapsed(hs_ep)) { + epctrl |= DXEPCTL_SNAK; + epctrl |= DXEPCTL_EPDIS; + dwc2_writel(epctrl, hsotg->regs + DIEPCTL(idx)); + } + } + + /* Clear interrupt */ + dwc2_writel(GINTSTS_INCOMPL_SOIN, hsotg->regs + GINTSTS); +} + +/** + * dwc2_gadget_handle_incomplete_isoc_out - handle incomplete ISO OUT Interrupt + * @hsotg: The device state: + * + * This interrupt indicates one of the following conditions occurred while + * transmitting an ISOC transaction. + * - Corrupted OUT Token for ISOC EP. + * - Packet not complete in FIFO. + * + * The following actions will be taken: + * - Determine the EP + * - Set DCTL_SGOUTNAK and unmask GOUTNAKEFF if target frame elapsed. + */ +static void dwc2_gadget_handle_incomplete_isoc_out(struct dwc2_hsotg *hsotg) +{ + u32 gintsts; + u32 gintmsk; + u32 epctrl; + struct dwc2_hsotg_ep *hs_ep; + int idx; + + dev_dbg(hsotg->dev, "%s: GINTSTS_INCOMPL_SOOUT\n", __func__); + + for (idx = 1; idx <= hsotg->num_of_eps; idx++) { + hs_ep = hsotg->eps_out[idx]; + epctrl = dwc2_readl(hsotg->regs + DOEPCTL(idx)); + if ((epctrl & DXEPCTL_EPENA) && hs_ep->isochronous && + dwc2_gadget_target_frame_elapsed(hs_ep)) { + /* Unmask GOUTNAKEFF interrupt */ + gintmsk = dwc2_readl(hsotg->regs + GINTMSK); + gintmsk |= GINTSTS_GOUTNAKEFF; + dwc2_writel(gintmsk, hsotg->regs + GINTMSK); + + gintsts = dwc2_readl(hsotg->regs + GINTSTS); + if (!(gintsts & GINTSTS_GOUTNAKEFF)) + __orr32(hsotg->regs + DCTL, DCTL_SGOUTNAK); + } + } + + /* Clear interrupt */ + dwc2_writel(GINTSTS_INCOMPL_SOOUT, hsotg->regs + GINTSTS); +} + /** * dwc2_hsotg_irq - handle device interrupt * @irq: The IRQ number triggered @@ -2717,39 +2820,11 @@ irq_retry: dwc2_hsotg_dump(hsotg); } - if (gintsts & GINTSTS_INCOMPL_SOIN) { - u32 idx, epctl_reg; - struct dwc2_hsotg_ep *hs_ep; + if (gintsts & GINTSTS_INCOMPL_SOIN) + dwc2_gadget_handle_incomplete_isoc_in(hsotg); - dev_dbg(hsotg->dev, "%s: GINTSTS_INCOMPL_SOIN\n", __func__); - for (idx = 1; idx < hsotg->num_of_eps; idx++) { - hs_ep = hsotg->eps_in[idx]; - - if (!hs_ep->isochronous || hs_ep->has_correct_parity) - continue; - - epctl_reg = DIEPCTL(idx); - dwc2_hsotg_change_ep_iso_parity(hsotg, epctl_reg); - } - dwc2_writel(GINTSTS_INCOMPL_SOIN, hsotg->regs + GINTSTS); - } - - if (gintsts & GINTSTS_INCOMPL_SOOUT) { - u32 idx, epctl_reg; - struct dwc2_hsotg_ep *hs_ep; - - dev_dbg(hsotg->dev, "%s: GINTSTS_INCOMPL_SOOUT\n", __func__); - for (idx = 1; idx < hsotg->num_of_eps; idx++) { - hs_ep = hsotg->eps_out[idx]; - - if (!hs_ep->isochronous || hs_ep->has_correct_parity) - continue; - - epctl_reg = DOEPCTL(idx); - dwc2_hsotg_change_ep_iso_parity(hsotg, epctl_reg); - } - dwc2_writel(GINTSTS_INCOMPL_SOOUT, hsotg->regs + GINTSTS); - } + if (gintsts & GINTSTS_INCOMPL_SOOUT) + dwc2_gadget_handle_incomplete_isoc_out(hsotg); /* * if we've had fifo events, we should try and go around the From bd9971f0a1efe4b3ac6e2f8ec864c75f73ca7829 Mon Sep 17 00:00:00 2001 From: Vardan Mikayelyan Date: Wed, 25 May 2016 18:07:19 -0700 Subject: [PATCH 086/109] usb: dwc2: gadget: Add EP disabled interrupt handler Reimplemented EP disabled interrupt handler and moved to corresponding function. This interrupt indicates that the endpoint has been disabled per the application's request. For IN endpoints flushes txfifo, in case of BULK clears DCTL_CGNPINNAK, in case of ISOC completes current request. For ISOC-OUT endpoints completes expired requests. If there is remaining request starts it. This is the part of ISOC-OUT transfer drop flow. When ISOC-OUT transfer expired we must disable ep to drop ongoing transfer. Tested-by: John Keeping Reviewed-by: Vahram Aharonyan Signed-off-by: Vardan Mikayelyan Signed-off-by: John Youn Signed-off-by: Felipe Balbi --- drivers/usb/dwc2/gadget.c | 87 +++++++++++++++++++++++++++++++-------- 1 file changed, 70 insertions(+), 17 deletions(-) diff --git a/drivers/usb/dwc2/gadget.c b/drivers/usb/dwc2/gadget.c index 61f913d491be..4a6074cc8e17 100644 --- a/drivers/usb/dwc2/gadget.c +++ b/drivers/usb/dwc2/gadget.c @@ -2023,6 +2023,74 @@ static u32 dwc2_gadget_read_ep_interrupts(struct dwc2_hsotg *hsotg, return ints; } +/** + * dwc2_gadget_handle_ep_disabled - handle DXEPINT_EPDISBLD + * @hs_ep: The endpoint on which interrupt is asserted. + * + * This interrupt indicates that the endpoint has been disabled per the + * application's request. + * + * For IN endpoints flushes txfifo, in case of BULK clears DCTL_CGNPINNAK, + * in case of ISOC completes current request. + * + * For ISOC-OUT endpoints completes expired requests. If there is remaining + * request starts it. + */ +static void dwc2_gadget_handle_ep_disabled(struct dwc2_hsotg_ep *hs_ep) +{ + struct dwc2_hsotg *hsotg = hs_ep->parent; + struct dwc2_hsotg_req *hs_req; + unsigned char idx = hs_ep->index; + int dir_in = hs_ep->dir_in; + u32 epctl_reg = dir_in ? DIEPCTL(idx) : DOEPCTL(idx); + int dctl = dwc2_readl(hsotg->regs + DCTL); + + dev_dbg(hsotg->dev, "%s: EPDisbld\n", __func__); + + if (dir_in) { + int epctl = dwc2_readl(hsotg->regs + epctl_reg); + + dwc2_hsotg_txfifo_flush(hsotg, hs_ep->fifo_index); + + if (hs_ep->isochronous) { + dwc2_hsotg_complete_in(hsotg, hs_ep); + return; + } + + if ((epctl & DXEPCTL_STALL) && (epctl & DXEPCTL_EPTYPE_BULK)) { + int dctl = dwc2_readl(hsotg->regs + DCTL); + + dctl |= DCTL_CGNPINNAK; + dwc2_writel(dctl, hsotg->regs + DCTL); + } + return; + } + + if (dctl & DCTL_GOUTNAKSTS) { + dctl |= DCTL_CGOUTNAK; + dwc2_writel(dctl, hsotg->regs + DCTL); + } + + if (!hs_ep->isochronous) + return; + + if (list_empty(&hs_ep->queue)) { + dev_dbg(hsotg->dev, "%s: complete_ep 0x%p, ep->queue empty!\n", + __func__, hs_ep); + return; + } + + do { + hs_req = get_ep_head(hs_ep); + if (hs_req) + dwc2_hsotg_complete_request(hsotg, hs_ep, hs_req, + -ENODATA); + dwc2_gadget_incr_frame_num(hs_ep); + } while (dwc2_gadget_target_frame_elapsed(hs_ep)); + + dwc2_gadget_start_next_request(hs_ep); +} + /** * dwc2_gadget_handle_out_token_ep_disabled - handle DXEPINT_OUTTKNEPDIS * @hs_ep: The endpoint on which interrupt is asserted. @@ -2177,23 +2245,8 @@ static void dwc2_hsotg_epint(struct dwc2_hsotg *hsotg, unsigned int idx, } } - if (ints & DXEPINT_EPDISBLD) { - dev_dbg(hsotg->dev, "%s: EPDisbld\n", __func__); - - if (dir_in) { - int epctl = dwc2_readl(hsotg->regs + epctl_reg); - - dwc2_hsotg_txfifo_flush(hsotg, hs_ep->fifo_index); - - if ((epctl & DXEPCTL_STALL) && - (epctl & DXEPCTL_EPTYPE_BULK)) { - int dctl = dwc2_readl(hsotg->regs + DCTL); - - dctl |= DCTL_CGNPINNAK; - dwc2_writel(dctl, hsotg->regs + DCTL); - } - } - } + if (ints & DXEPINT_EPDISBLD) + dwc2_gadget_handle_ep_disabled(hs_ep); if (ints & DXEPINT_OUTTKNEPDIS) dwc2_gadget_handle_out_token_ep_disabled(hs_ep); From 837e9f00bf9966d64abc1bce678271099a72423b Mon Sep 17 00:00:00 2001 From: Vardan Mikayelyan Date: Wed, 25 May 2016 18:07:22 -0700 Subject: [PATCH 087/109] usb: dwc2: gadget: Final fixes for BDMA ISOC Done fixes and tested hsotg gadget's BDMA mode. Tested Control, Bulk, Isoc, Inter transfers. Added code for isoc transfers, removed unusable code, done minor fixes. Affected functions and IRQ handlers: - dwc2_hsotg_start_req(), - dwc2_hsotg_ep_enable(), - dwc2_hsotg_ep_queue(), - dwc2_hsotg_handle_outdone(), - GINTSTS_GOUTNAKEFF handler, Removed 'has_correct_parity' flag from 'dwc2_hsotg_ep' struct. Before this patch series, to set the data pid the DWC2 gadget driver was toggling the even/odd until it match, then were leaving it set. But now I have added mechanism to set pid and excluded all code where this flag was set. Tested-by: John Keeping Signed-off-by: Vardan Mikayelyan Signed-off-by: John Youn Signed-off-by: Felipe Balbi --- drivers/usb/dwc2/core.h | 1 - drivers/usb/dwc2/gadget.c | 95 ++++++++++++++++++++++++++++----------- drivers/usb/dwc2/hw.h | 1 + 3 files changed, 70 insertions(+), 27 deletions(-) diff --git a/drivers/usb/dwc2/core.h b/drivers/usb/dwc2/core.h index 0ba359945dbd..9fae0291cd69 100644 --- a/drivers/usb/dwc2/core.h +++ b/drivers/usb/dwc2/core.h @@ -215,7 +215,6 @@ struct dwc2_hsotg_ep { unsigned int periodic:1; unsigned int isochronous:1; unsigned int send_zlp:1; - unsigned int has_correct_parity:1; unsigned int target_frame; #define TARGET_FRAME_INITIAL 0xFFFFFFFF bool frame_overrun; diff --git a/drivers/usb/dwc2/gadget.c b/drivers/usb/dwc2/gadget.c index 4a6074cc8e17..af46adfae41c 100644 --- a/drivers/usb/dwc2/gadget.c +++ b/drivers/usb/dwc2/gadget.c @@ -667,6 +667,16 @@ static void dwc2_hsotg_start_req(struct dwc2_hsotg *hsotg, __func__, &ureq->dma, dma_reg); } + if (hs_ep->isochronous && hs_ep->interval == 1) { + hs_ep->target_frame = dwc2_hsotg_read_frameno(hsotg); + dwc2_gadget_incr_frame_num(hs_ep); + + if (hs_ep->target_frame & 0x1) + ctrl |= DXEPCTL_SETODDFR; + else + ctrl |= DXEPCTL_SETEVENFR; + } + ctrl |= DXEPCTL_EPENA; /* ensure ep enabled */ dev_dbg(hsotg->dev, "ep0 state:%d\n", hsotg->ep0_state); @@ -863,9 +873,18 @@ static int dwc2_hsotg_ep_queue(struct usb_ep *ep, struct usb_request *req, first = list_empty(&hs_ep->queue); list_add_tail(&hs_req->queue, &hs_ep->queue); - if (first) - dwc2_hsotg_start_req(hs, hs_ep, hs_req, false); + if (first) { + if (!hs_ep->isochronous) { + dwc2_hsotg_start_req(hs, hs_ep, hs_req, false); + return 0; + } + while (dwc2_gadget_target_frame_elapsed(hs_ep)) + dwc2_gadget_incr_frame_num(hs_ep); + + if (hs_ep->target_frame != TARGET_FRAME_INITIAL) + dwc2_hsotg_start_req(hs, hs_ep, hs_req, false); + } return 0; } @@ -1673,9 +1692,10 @@ static void dwc2_hsotg_handle_outdone(struct dwc2_hsotg *hsotg, int epnum) * adjust the ISOC parity here. */ if (!using_dma(hsotg)) { - hs_ep->has_correct_parity = 1; if (hs_ep->isochronous && hs_ep->interval == 1) dwc2_hsotg_change_ep_iso_parity(hsotg, DOEPCTL(epnum)); + else if (hs_ep->isochronous && hs_ep->interval > 1) + dwc2_gadget_incr_frame_num(hs_ep); } dwc2_hsotg_complete_request(hsotg, hs_ep, hs_req, result); @@ -2216,11 +2236,10 @@ static void dwc2_hsotg_epint(struct dwc2_hsotg *hsotg, unsigned int idx, if (idx == 0 && (ints & (DXEPINT_SETUP | DXEPINT_SETUP_RCVD))) ints &= ~DXEPINT_XFERCOMPL; - if (ints & DXEPINT_XFERCOMPL) { - hs_ep->has_correct_parity = 1; - if (hs_ep->isochronous && hs_ep->interval == 1) - dwc2_hsotg_change_ep_iso_parity(hsotg, epctl_reg); + if (ints & DXEPINT_STSPHSERCVD) + dev_dbg(hsotg->dev, "%s: StsPhseRcvd asserted\n", __func__); + if (ints & DXEPINT_XFERCOMPL) { dev_dbg(hsotg->dev, "%s: XferCompl: DxEPCTL=0x%08x, DXEPTSIZ=%08x\n", __func__, dwc2_readl(hsotg->regs + epctl_reg), @@ -2231,7 +2250,12 @@ static void dwc2_hsotg_epint(struct dwc2_hsotg *hsotg, unsigned int idx, * at completing IN requests here */ if (dir_in) { + if (hs_ep->isochronous && hs_ep->interval > 1) + dwc2_gadget_incr_frame_num(hs_ep); + dwc2_hsotg_complete_in(hsotg, hs_ep); + if (ints & DXEPINT_NAKINTRPT) + ints &= ~DXEPINT_NAKINTRPT; if (idx == 0 && !hs_ep->req) dwc2_hsotg_enqueue_setup(hsotg); @@ -2240,6 +2264,8 @@ static void dwc2_hsotg_epint(struct dwc2_hsotg *hsotg, unsigned int idx, * We're using DMA, we need to fire an OutDone here * as we ignore the RXFIFO. */ + if (hs_ep->isochronous && hs_ep->interval > 1) + dwc2_gadget_incr_frame_num(hs_ep); dwc2_hsotg_handle_outdone(hsotg, idx); } @@ -2556,18 +2582,16 @@ void dwc2_hsotg_core_init_disconnected(struct dwc2_hsotg *hsotg, dwc2_writel(((hsotg->dedicated_fifos && !using_dma(hsotg)) ? DIEPMSK_TXFIFOEMPTY | DIEPMSK_INTKNTXFEMPMSK : 0) | DIEPMSK_EPDISBLDMSK | DIEPMSK_XFERCOMPLMSK | - DIEPMSK_TIMEOUTMSK | DIEPMSK_AHBERRMSK | - DIEPMSK_INTKNEPMISMSK, + DIEPMSK_TIMEOUTMSK | DIEPMSK_AHBERRMSK, hsotg->regs + DIEPMSK); /* * don't need XferCompl, we get that from RXFIFO in slave mode. In * DMA mode we may need this. */ - dwc2_writel((using_dma(hsotg) ? (DIEPMSK_XFERCOMPLMSK | - DIEPMSK_TIMEOUTMSK) : 0) | + dwc2_writel((using_dma(hsotg) ? (DIEPMSK_XFERCOMPLMSK) : 0) | DOEPMSK_EPDISBLDMSK | DOEPMSK_AHBERRMSK | - DOEPMSK_SETUPMSK, + DOEPMSK_SETUPMSK | DOEPMSK_STSPHSERCVDMSK, hsotg->regs + DOEPMSK); dwc2_writel(0, hsotg->regs + DAINTMSK); @@ -2858,11 +2882,29 @@ irq_retry: */ if (gintsts & GINTSTS_GOUTNAKEFF) { - dev_info(hsotg->dev, "GOUTNakEff triggered\n"); + u8 idx; + u32 epctrl; + u32 gintmsk; + struct dwc2_hsotg_ep *hs_ep; - __orr32(hsotg->regs + DCTL, DCTL_CGOUTNAK); + /* Mask this interrupt */ + gintmsk = dwc2_readl(hsotg->regs + GINTMSK); + gintmsk &= ~GINTSTS_GOUTNAKEFF; + dwc2_writel(gintmsk, hsotg->regs + GINTMSK); - dwc2_hsotg_dump(hsotg); + dev_dbg(hsotg->dev, "GOUTNakEff triggered\n"); + for (idx = 1; idx <= hsotg->num_of_eps; idx++) { + hs_ep = hsotg->eps_out[idx]; + epctrl = dwc2_readl(hsotg->regs + DOEPCTL(idx)); + + if ((epctrl & DXEPCTL_EPENA) && hs_ep->isochronous) { + epctrl |= DXEPCTL_SNAK; + epctrl |= DXEPCTL_EPDIS; + dwc2_writel(epctrl, hsotg->regs + DOEPCTL(idx)); + } + } + + /* This interrupt bit is cleared in DXEPINT_EPDISBLD handler */ } if (gintsts & GINTSTS_GINNAKEFF) { @@ -2909,6 +2951,7 @@ static int dwc2_hsotg_ep_enable(struct usb_ep *ep, u32 epctrl_reg; u32 epctrl; u32 mps; + u32 mask; unsigned int dir_in; unsigned int i, val, size; int ret = 0; @@ -2951,15 +2994,6 @@ static int dwc2_hsotg_ep_enable(struct usb_ep *ep, */ epctrl |= DXEPCTL_USBACTEP; - /* - * set the NAK status on the endpoint, otherwise we might try and - * do something with data that we've yet got a request to process - * since the RXFIFO will take data for an endpoint even if the - * size register hasn't been set. - */ - - epctrl |= DXEPCTL_SNAK; - /* update the endpoint state */ dwc2_hsotg_set_ep_maxpacket(hsotg, hs_ep->index, mps, dir_in); @@ -2975,8 +3009,17 @@ static int dwc2_hsotg_ep_enable(struct usb_ep *ep, epctrl |= DXEPCTL_SETEVENFR; hs_ep->isochronous = 1; hs_ep->interval = 1 << (desc->bInterval - 1); - if (dir_in) + hs_ep->target_frame = TARGET_FRAME_INITIAL; + if (dir_in) { hs_ep->periodic = 1; + mask = dwc2_readl(hsotg->regs + DIEPMSK); + mask |= DIEPMSK_NAKMSK; + dwc2_writel(mask, hsotg->regs + DIEPMSK); + } else { + mask = dwc2_readl(hsotg->regs + DOEPMSK); + mask |= DOEPMSK_OUTTKNEPDISMSK; + dwc2_writel(mask, hsotg->regs + DOEPMSK); + } break; case USB_ENDPOINT_XFER_BULK: @@ -3043,7 +3086,7 @@ static int dwc2_hsotg_ep_enable(struct usb_ep *ep, } /* for non control endpoints, set PID to D0 */ - if (index) + if (index && !hs_ep->isochronous) epctrl |= DXEPCTL_SETD0PID; dev_dbg(hsotg->dev, "%s: write DxEPCTL=0x%08x\n", diff --git a/drivers/usb/dwc2/hw.h b/drivers/usb/dwc2/hw.h index 1126141cf47c..efc3bcde2822 100644 --- a/drivers/usb/dwc2/hw.h +++ b/drivers/usb/dwc2/hw.h @@ -560,6 +560,7 @@ #define DXEPINT_INEPNAKEFF (1 << 6) #define DXEPINT_BACK2BACKSETUP (1 << 6) #define DXEPINT_INTKNEPMIS (1 << 5) +#define DXEPINT_STSPHSERCVD (1 << 5) #define DXEPINT_INTKNTXFEMP (1 << 4) #define DXEPINT_OUTTKNEPDIS (1 << 4) #define DXEPINT_TIMEOUT (1 << 3) From 882bd9fc46321c9d4721b376039a142cbfe8a17a Mon Sep 17 00:00:00 2001 From: Alexandre Belloni Date: Mon, 13 Jun 2016 10:47:30 +0200 Subject: [PATCH 088/109] usb: gadget: udc: atmel: Also get regmap for at91sam9x5-pmc MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The "atmel,at91sam9g45-udc" compatible UDC is also used on at91sam9x5 so it is also necessary to try to get the syscon for at91sam9x5-pmc. Fixes: 4747639f01c9 ("usb: gadget: atmel: access the PMC using regmap") Reported-by: Uwe Kleine-König Signed-off-by: Alexandre Belloni Signed-off-by: Felipe Balbi --- drivers/usb/gadget/udc/atmel_usba_udc.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/usb/gadget/udc/atmel_usba_udc.c b/drivers/usb/gadget/udc/atmel_usba_udc.c index 18569de06b04..bb1f6c8f0f01 100644 --- a/drivers/usb/gadget/udc/atmel_usba_udc.c +++ b/drivers/usb/gadget/udc/atmel_usba_udc.c @@ -1920,6 +1920,8 @@ static struct usba_ep * atmel_udc_of_init(struct platform_device *pdev, udc->errata = match->data; udc->pmc = syscon_regmap_lookup_by_compatible("atmel,at91sam9g45-pmc"); + if (IS_ERR(udc->pmc)) + udc->pmc = syscon_regmap_lookup_by_compatible("atmel,at91sam9x5-pmc"); if (udc->errata && IS_ERR(udc->pmc)) return ERR_CAST(udc->pmc); From ce15ed4c5dfb3f7757e6611902aed5db253af977 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Fri, 10 Jun 2016 11:46:25 +0200 Subject: [PATCH 089/109] USB: Fix of_usb_get_dr_mode_by_phy with a shared phy block Some SoCs have a single phy-hw-block with multiple phys, this is modelled by a single phy dts node, so we end up with multiple controller nodes with a phys property pointing to the phy-node of the otg-phy. Only one of these controllers typically is an otg controller, yet we were checking the first controller who uses a phy from the block and then end up looking for a dr_mode property in e.g. the ehci controller. This commit fixes this by adding an arg0 parameter to of_usb_get_dr_mode_by_phy and make of_usb_get_dr_mode_by_phy check that this matches the phandle args[0] value when looking for the otg controller. Signed-off-by: Hans de Goede Signed-off-by: Felipe Balbi --- drivers/usb/common/common.c | 26 ++++++++++++++++++++------ drivers/usb/phy/phy-am335x.c | 2 +- include/linux/usb/of.h | 4 ++-- 3 files changed, 23 insertions(+), 9 deletions(-) diff --git a/drivers/usb/common/common.c b/drivers/usb/common/common.c index e3d01619d6b3..5ef8da6e67c3 100644 --- a/drivers/usb/common/common.c +++ b/drivers/usb/common/common.c @@ -131,15 +131,17 @@ EXPORT_SYMBOL_GPL(usb_get_dr_mode); * of_usb_get_dr_mode_by_phy - Get dual role mode for the controller device * which is associated with the given phy device_node * @np: Pointer to the given phy device_node + * @arg0: phandle args[0] for phy's with #phy-cells >= 1, or -1 for + * phys which do not have phy-cells * * In dts a usb controller associates with phy devices. The function gets * the string from property 'dr_mode' of the controller associated with the * given phy device node, and returns the correspondig enum usb_dr_mode. */ -enum usb_dr_mode of_usb_get_dr_mode_by_phy(struct device_node *phy_np) +enum usb_dr_mode of_usb_get_dr_mode_by_phy(struct device_node *np, int arg0) { struct device_node *controller = NULL; - struct device_node *phy; + struct of_phandle_args args; const char *dr_mode; int index; int err; @@ -148,12 +150,24 @@ enum usb_dr_mode of_usb_get_dr_mode_by_phy(struct device_node *phy_np) controller = of_find_node_with_property(controller, "phys"); index = 0; do { - phy = of_parse_phandle(controller, "phys", index); - of_node_put(phy); - if (phy == phy_np) + if (arg0 == -1) { + args.np = of_parse_phandle(controller, "phys", + index); + args.args_count = 0; + } else { + err = of_parse_phandle_with_args(controller, + "phys", "#phy-cells", + index, &args); + if (err) + break; + } + + of_node_put(args.np); + if (args.np == np && (args.args_count == 0 || + args.args[0] == arg0)) goto finish; index++; - } while (phy); + } while (args.np); } while (controller); finish: diff --git a/drivers/usb/phy/phy-am335x.c b/drivers/usb/phy/phy-am335x.c index a262a4343f29..7e5aece769da 100644 --- a/drivers/usb/phy/phy-am335x.c +++ b/drivers/usb/phy/phy-am335x.c @@ -54,7 +54,7 @@ static int am335x_phy_probe(struct platform_device *pdev) return am_phy->id; } - am_phy->dr_mode = of_usb_get_dr_mode_by_phy(pdev->dev.of_node); + am_phy->dr_mode = of_usb_get_dr_mode_by_phy(pdev->dev.of_node, -1); ret = usb_phy_gen_create_phy(dev, &am_phy->usb_phy_gen, NULL); if (ret) diff --git a/include/linux/usb/of.h b/include/linux/usb/of.h index de3237fce6b2..5ff9032ee1b4 100644 --- a/include/linux/usb/of.h +++ b/include/linux/usb/of.h @@ -12,7 +12,7 @@ #include #if IS_ENABLED(CONFIG_OF) -enum usb_dr_mode of_usb_get_dr_mode_by_phy(struct device_node *phy_np); +enum usb_dr_mode of_usb_get_dr_mode_by_phy(struct device_node *np, int arg0); bool of_usb_host_tpl_support(struct device_node *np); int of_usb_update_otg_caps(struct device_node *np, struct usb_otg_caps *otg_caps); @@ -20,7 +20,7 @@ struct device_node *usb_of_get_child_node(struct device_node *parent, int portnum); #else static inline enum usb_dr_mode -of_usb_get_dr_mode_by_phy(struct device_node *phy_np) +of_usb_get_dr_mode_by_phy(struct device_node *np, int arg0) { return USB_DR_MODE_UNKNOWN; } From 9522def40065194aa75b0a7b7e1ff5b8e2014724 Mon Sep 17 00:00:00 2001 From: Roger Quadros Date: Fri, 10 Jun 2016 14:48:38 +0300 Subject: [PATCH 090/109] usb: dwc3: core: cleanup IRQ resources Implementations might use different IRQs for host, gadget so use named interrupt resources to allow device tree to specify the interrupts. Following are the interrupt names Peripheral Interrupt - peripheral HOST Interrupt - host Maintain backward compatibility for a single named interrupt ("dwc3_usb3") for all interrupts as well as unnamed interrupt at index 0 for all interrupts. As platform_get_irq() variants are used, tackle the -EPROBE_DEFER case as well. Signed-off-by: Roger Quadros Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/core.c | 22 ++++++++------------ drivers/usb/dwc3/gadget.c | 31 +++++++++++++++++++++++++--- drivers/usb/dwc3/host.c | 43 ++++++++++++++++++++++++++++++++++++++- 3 files changed, 78 insertions(+), 18 deletions(-) diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c index ca22e4885116..946643157b78 100644 --- a/drivers/usb/dwc3/core.c +++ b/drivers/usb/dwc3/core.c @@ -766,7 +766,8 @@ static int dwc3_core_init_mode(struct dwc3 *dwc) dwc3_set_mode(dwc, DWC3_GCTL_PRTCAP_DEVICE); ret = dwc3_gadget_init(dwc); if (ret) { - dev_err(dev, "failed to initialize gadget\n"); + if (ret != -EPROBE_DEFER) + dev_err(dev, "failed to initialize gadget\n"); return ret; } break; @@ -774,7 +775,8 @@ static int dwc3_core_init_mode(struct dwc3 *dwc) dwc3_set_mode(dwc, DWC3_GCTL_PRTCAP_HOST); ret = dwc3_host_init(dwc); if (ret) { - dev_err(dev, "failed to initialize host\n"); + if (ret != -EPROBE_DEFER) + dev_err(dev, "failed to initialize host\n"); return ret; } break; @@ -782,13 +784,15 @@ static int dwc3_core_init_mode(struct dwc3 *dwc) dwc3_set_mode(dwc, DWC3_GCTL_PRTCAP_OTG); ret = dwc3_host_init(dwc); if (ret) { - dev_err(dev, "failed to initialize host\n"); + if (ret != -EPROBE_DEFER) + dev_err(dev, "failed to initialize host\n"); return ret; } ret = dwc3_gadget_init(dwc); if (ret) { - dev_err(dev, "failed to initialize gadget\n"); + if (ret != -EPROBE_DEFER) + dev_err(dev, "failed to initialize gadget\n"); return ret; } break; @@ -843,16 +847,6 @@ static int dwc3_probe(struct platform_device *pdev) dwc->mem = mem; dwc->dev = dev; - res = platform_get_resource(pdev, IORESOURCE_IRQ, 0); - if (!res) { - dev_err(dev, "missing IRQ\n"); - return -ENODEV; - } - dwc->xhci_resources[1].start = res->start; - dwc->xhci_resources[1].end = res->end; - dwc->xhci_resources[1].flags = res->flags; - dwc->xhci_resources[1].name = res->name; - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (!res) { dev_err(dev, "missing memory resource\n"); diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index b9bc646511ce..f20975300594 100644 --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c @@ -1765,7 +1765,7 @@ static int dwc3_gadget_start(struct usb_gadget *g, int ret = 0; int irq; - irq = platform_get_irq(to_platform_device(dwc->dev), 0); + irq = dwc->irq_gadget; ret = request_threaded_irq(irq, dwc3_interrupt, dwc3_thread_interrupt, IRQF_SHARED, "dwc3", dwc->ev_buf); if (ret) { @@ -1773,7 +1773,6 @@ static int dwc3_gadget_start(struct usb_gadget *g, irq, ret); goto err0; } - dwc->irq_gadget = irq; spin_lock_irqsave(&dwc->lock, flags); if (dwc->gadget_driver) { @@ -2891,7 +2890,33 @@ static irqreturn_t dwc3_interrupt(int irq, void *_evt) */ int dwc3_gadget_init(struct dwc3 *dwc) { - int ret; + int ret, irq; + struct platform_device *dwc3_pdev = to_platform_device(dwc->dev); + + irq = platform_get_irq_byname(dwc3_pdev, "peripheral"); + if (irq == -EPROBE_DEFER) + return irq; + + if (irq <= 0) { + irq = platform_get_irq_byname(dwc3_pdev, "dwc_usb3"); + if (irq == -EPROBE_DEFER) + return irq; + + if (irq <= 0) { + irq = platform_get_irq(dwc3_pdev, 0); + if (irq <= 0) { + if (irq != -EPROBE_DEFER) { + dev_err(dwc->dev, + "missing peripheral IRQ\n"); + } + if (!irq) + irq = -EINVAL; + return irq; + } + } + } + + dwc->irq_gadget = irq; dwc->ctrl_req = dma_alloc_coherent(dwc->dev, sizeof(*dwc->ctrl_req), &dwc->ctrl_req_addr, GFP_KERNEL); diff --git a/drivers/usb/dwc3/host.c b/drivers/usb/dwc3/host.c index c679f63783ae..2e960edeb695 100644 --- a/drivers/usb/dwc3/host.c +++ b/drivers/usb/dwc3/host.c @@ -24,7 +24,48 @@ int dwc3_host_init(struct dwc3 *dwc) { struct platform_device *xhci; struct usb_xhci_pdata pdata; - int ret; + int ret, irq; + struct resource *res; + struct platform_device *dwc3_pdev = to_platform_device(dwc->dev); + + irq = platform_get_irq_byname(dwc3_pdev, "host"); + if (irq == -EPROBE_DEFER) + return irq; + + if (irq <= 0) { + irq = platform_get_irq_byname(dwc3_pdev, "dwc_usb3"); + if (irq == -EPROBE_DEFER) + return irq; + + if (irq <= 0) { + irq = platform_get_irq(dwc3_pdev, 0); + if (irq <= 0) { + if (irq != -EPROBE_DEFER) { + dev_err(dwc->dev, + "missing host IRQ\n"); + } + if (!irq) + irq = -EINVAL; + return irq; + } else { + res = platform_get_resource(dwc3_pdev, + IORESOURCE_IRQ, 0); + } + } else { + res = platform_get_resource_byname(dwc3_pdev, + IORESOURCE_IRQ, + "dwc_usb3"); + } + + } else { + res = platform_get_resource_byname(dwc3_pdev, IORESOURCE_IRQ, + "host"); + } + + dwc->xhci_resources[1].start = irq; + dwc->xhci_resources[1].end = irq; + dwc->xhci_resources[1].flags = res->flags; + dwc->xhci_resources[1].name = res->name; xhci = platform_device_alloc("xhci-hcd", PLATFORM_DEVID_AUTO); if (!xhci) { From 872ce5119524f33fafacc4d9610a431185ea66a2 Mon Sep 17 00:00:00 2001 From: Michal Nazarewicz Date: Tue, 31 May 2016 14:17:21 +0200 Subject: [PATCH 091/109] usb: gadget: fix unused-but-set-variale warnings Those are enabled with W=1 make option. The patch leaves of some type-limits warnings which are caused by generic macros used in a way where they produce always-false conditions. Signed-off-by: Michal Nazarewicz Signed-off-by: Felipe Balbi --- drivers/usb/gadget/function/f_fs.c | 3 +-- drivers/usb/gadget/function/u_serial.c | 3 +-- drivers/usb/gadget/legacy/g_ffs.c | 15 ++++++--------- drivers/usb/gadget/udc/amd5536udc.c | 9 +-------- drivers/usb/gadget/udc/bdc/bdc_cmd.c | 3 --- drivers/usb/gadget/udc/bdc/bdc_ep.c | 4 ---- drivers/usb/gadget/udc/dummy_hcd.c | 5 ----- drivers/usb/gadget/udc/mv_udc_core.c | 9 ++------- drivers/usb/gadget/udc/net2272.c | 4 ---- drivers/usb/gadget/udc/pch_udc.c | 20 +++++++------------- drivers/usb/gadget/udc/udc-xilinx.c | 3 --- 11 files changed, 18 insertions(+), 60 deletions(-) diff --git a/drivers/usb/gadget/function/f_fs.c b/drivers/usb/gadget/function/f_fs.c index cc33d2667408..70ed37065bab 100644 --- a/drivers/usb/gadget/function/f_fs.c +++ b/drivers/usb/gadget/function/f_fs.c @@ -2227,8 +2227,8 @@ static int __ffs_data_got_strings(struct ffs_data *ffs, { u32 str_count, needed_count, lang_count; struct usb_gadget_strings **stringtabs, *t; - struct usb_string *strings, *s; const char *data = _data; + struct usb_string *s; ENTER(); @@ -2286,7 +2286,6 @@ static int __ffs_data_got_strings(struct ffs_data *ffs, stringtabs = vla_ptr(vlabuf, d, stringtabs); t = vla_ptr(vlabuf, d, stringtab); s = vla_ptr(vlabuf, d, strings); - strings = s; } /* For each language */ diff --git a/drivers/usb/gadget/function/u_serial.c b/drivers/usb/gadget/function/u_serial.c index 3580f198df8b..6ded6345cd09 100644 --- a/drivers/usb/gadget/function/u_serial.c +++ b/drivers/usb/gadget/function/u_serial.c @@ -907,7 +907,6 @@ static int gs_write(struct tty_struct *tty, const unsigned char *buf, int count) { struct gs_port *port = tty->driver_data; unsigned long flags; - int status; pr_vdebug("gs_write: ttyGS%d (%p) writing %d bytes\n", port->port_num, tty, count); @@ -917,7 +916,7 @@ static int gs_write(struct tty_struct *tty, const unsigned char *buf, int count) count = gs_buf_put(&port->port_write_buf, buf, count); /* treat count == 0 as flush_chars() */ if (port->port_usb) - status = gs_start_tx(port); + gs_start_tx(port); spin_unlock_irqrestore(&port->port_lock, flags); return count; diff --git a/drivers/usb/gadget/legacy/g_ffs.c b/drivers/usb/gadget/legacy/g_ffs.c index f85639ef8a8f..6da7316f8e87 100644 --- a/drivers/usb/gadget/legacy/g_ffs.c +++ b/drivers/usb/gadget/legacy/g_ffs.c @@ -265,7 +265,7 @@ static void *functionfs_acquire_dev(struct ffs_dev *dev) { if (!try_module_get(THIS_MODULE)) return ERR_PTR(-ENOENT); - + return NULL; } @@ -275,7 +275,7 @@ static void functionfs_release_dev(struct ffs_dev *dev) } /* - * The caller of this function takes ffs_lock + * The caller of this function takes ffs_lock */ static int functionfs_ready_callback(struct ffs_data *ffs) { @@ -294,12 +294,12 @@ static int functionfs_ready_callback(struct ffs_data *ffs) ++missing_funcs; gfs_registered = false; } - + return ret; } /* - * The caller of this function takes ffs_lock + * The caller of this function takes ffs_lock */ static void functionfs_closed_callback(struct ffs_data *ffs) { @@ -347,17 +347,14 @@ static int gfs_bind(struct usb_composite_dev *cdev) #ifdef CONFIG_USB_FUNCTIONFS_RNDIS { - struct f_rndis_opts *rndis_opts; - fi_rndis = usb_get_function_instance("rndis"); if (IS_ERR(fi_rndis)) { ret = PTR_ERR(fi_rndis); goto error; } - rndis_opts = container_of(fi_rndis, struct f_rndis_opts, - func_inst); #ifndef CONFIG_USB_FUNCTIONFS_ETH - net = rndis_opts->net; + net = container_of(fi_rndis, struct f_rndis_opts, + func_inst)->net; #endif } #endif diff --git a/drivers/usb/gadget/udc/amd5536udc.c b/drivers/usb/gadget/udc/amd5536udc.c index 39d70b4a8958..ea03ca7ae29a 100644 --- a/drivers/usb/gadget/udc/amd5536udc.c +++ b/drivers/usb/gadget/udc/amd5536udc.c @@ -2340,7 +2340,6 @@ static irqreturn_t udc_data_in_isr(struct udc *dev, int ep_ix) struct udc_ep *ep; struct udc_request *req; struct udc_data_dma *td; - unsigned dma_done; unsigned len; ep = &dev->ep[ep_ix]; @@ -2385,13 +2384,8 @@ static irqreturn_t udc_data_in_isr(struct udc *dev, int ep_ix) */ if (use_dma_ppb_du) { td = udc_get_last_dma_desc(req); - if (td) { - dma_done = - AMD_GETBITS(td->status, - UDC_DMA_IN_STS_BS); - /* don't care DMA done */ + if (td) req->req.actual = req->req.length; - } } else { /* assume all bytes transferred */ req->req.actual = req->req.length; @@ -3417,4 +3411,3 @@ module_pci_driver(udc_pci_driver); MODULE_DESCRIPTION(UDC_MOD_DESCRIPTION); MODULE_AUTHOR("Thomas Dahlmann"); MODULE_LICENSE("GPL"); - diff --git a/drivers/usb/gadget/udc/bdc/bdc_cmd.c b/drivers/usb/gadget/udc/bdc/bdc_cmd.c index 6a4155c4bd86..4d5e9188beae 100644 --- a/drivers/usb/gadget/udc/bdc/bdc_cmd.c +++ b/drivers/usb/gadget/udc/bdc/bdc_cmd.c @@ -57,7 +57,6 @@ static int bdc_submit_cmd(struct bdc *bdc, u32 cmd_sc, u32 param0, u32 param1, u32 param2) { u32 temp, cmd_status; - int reset_bdc = 0; int ret; temp = bdc_readl(bdc->regs, BDC_CMDSC); @@ -94,7 +93,6 @@ static int bdc_submit_cmd(struct bdc *bdc, u32 cmd_sc, case BDC_CMDS_INTL: dev_err(bdc->dev, "BDC Internal error\n"); - reset_bdc = 1; ret = -ECONNRESET; break; @@ -102,7 +100,6 @@ static int bdc_submit_cmd(struct bdc *bdc, u32 cmd_sc, dev_err(bdc->dev, "command timedout waited for %dusec\n", BDC_CMD_TIMEOUT); - reset_bdc = 1; ret = -ECONNRESET; break; default: diff --git a/drivers/usb/gadget/udc/bdc/bdc_ep.c b/drivers/usb/gadget/udc/bdc/bdc_ep.c index d6199507f861..650717e7192a 100644 --- a/drivers/usb/gadget/udc/bdc/bdc_ep.c +++ b/drivers/usb/gadget/udc/bdc/bdc_ep.c @@ -702,11 +702,9 @@ static int ep0_queue(struct bdc_ep *ep, struct bdc_req *req) /* Queue data stage */ static int ep0_queue_data_stage(struct bdc *bdc) { - struct usb_request *ep0_usb_req; struct bdc_ep *ep; dev_dbg(bdc->dev, "%s\n", __func__); - ep0_usb_req = &bdc->ep0_req.usb_req; ep = bdc->bdc_ep_array[1]; bdc->ep0_req.ep = ep; bdc->ep0_req.usb_req.complete = NULL; @@ -1393,10 +1391,8 @@ static int ep0_set_sel(struct bdc *bdc, { struct bdc_ep *ep; u16 wLength; - u16 wValue; dev_dbg(bdc->dev, "%s\n", __func__); - wValue = le16_to_cpu(setup_pkt->wValue); wLength = le16_to_cpu(setup_pkt->wLength); if (unlikely(wLength != 6)) { dev_err(bdc->dev, "%s Wrong wLength:%d\n", __func__, wLength); diff --git a/drivers/usb/gadget/udc/dummy_hcd.c b/drivers/usb/gadget/udc/dummy_hcd.c index dde44450dfa9..77d07904f932 100644 --- a/drivers/usb/gadget/udc/dummy_hcd.c +++ b/drivers/usb/gadget/udc/dummy_hcd.c @@ -647,12 +647,10 @@ static int dummy_disable(struct usb_ep *_ep) static struct usb_request *dummy_alloc_request(struct usb_ep *_ep, gfp_t mem_flags) { - struct dummy_ep *ep; struct dummy_request *req; if (!_ep) return NULL; - ep = usb_ep_to_dummy_ep(_ep); req = kzalloc(sizeof(*req), mem_flags); if (!req) @@ -2444,9 +2442,6 @@ static int dummy_start(struct usb_hcd *hcd) static void dummy_stop(struct usb_hcd *hcd) { - struct dummy *dum; - - dum = hcd_to_dummy_hcd(hcd)->dum; device_remove_file(dummy_dev(hcd_to_dummy_hcd(hcd)), &dev_attr_urbs); dev_info(dummy_dev(hcd_to_dummy_hcd(hcd)), "stopped\n"); } diff --git a/drivers/usb/gadget/udc/mv_udc_core.c b/drivers/usb/gadget/udc/mv_udc_core.c index 81b6229c7805..ce73b3552269 100644 --- a/drivers/usb/gadget/udc/mv_udc_core.c +++ b/drivers/usb/gadget/udc/mv_udc_core.c @@ -129,7 +129,7 @@ static int process_ep_req(struct mv_udc *udc, int index, { struct mv_dtd *curr_dtd; struct mv_dqh *curr_dqh; - int td_complete, actual, remaining_length; + int actual, remaining_length; int i, direction; int retval = 0; u32 errors; @@ -139,7 +139,6 @@ static int process_ep_req(struct mv_udc *udc, int index, direction = index % 2; curr_dtd = curr_req->head; - td_complete = 0; actual = curr_req->req.length; for (i = 0; i < curr_req->dtd_count; i++) { @@ -412,11 +411,8 @@ static int req_to_dtd(struct mv_req *req) unsigned count; int is_last, is_first = 1; struct mv_dtd *dtd, *last_dtd = NULL; - struct mv_udc *udc; dma_addr_t dma; - udc = req->ep->udc; - do { dtd = build_dtd(req, &count, &dma, &is_last); if (dtd == NULL) @@ -567,7 +563,7 @@ static int mv_ep_disable(struct usb_ep *_ep) struct mv_udc *udc; struct mv_ep *ep; struct mv_dqh *dqh; - u32 bit_pos, epctrlx, direction; + u32 epctrlx, direction; unsigned long flags; ep = container_of(_ep, struct mv_ep, ep); @@ -582,7 +578,6 @@ static int mv_ep_disable(struct usb_ep *_ep) spin_lock_irqsave(&udc->lock, flags); direction = ep_dir(ep); - bit_pos = 1 << ((direction == EP_DIR_OUT ? 0 : 16) + ep->ep_num); /* Reset the max packet length and the interrupt on Setup */ dqh->max_packet_length = 0; diff --git a/drivers/usb/gadget/udc/net2272.c b/drivers/usb/gadget/udc/net2272.c index 18f5ebd447b8..7c6113432093 100644 --- a/drivers/usb/gadget/udc/net2272.c +++ b/drivers/usb/gadget/udc/net2272.c @@ -329,12 +329,10 @@ static int net2272_disable(struct usb_ep *_ep) static struct usb_request * net2272_alloc_request(struct usb_ep *_ep, gfp_t gfp_flags) { - struct net2272_ep *ep; struct net2272_request *req; if (!_ep) return NULL; - ep = container_of(_ep, struct net2272_ep, ep); req = kzalloc(sizeof(*req), gfp_flags); if (!req) @@ -348,10 +346,8 @@ net2272_alloc_request(struct usb_ep *_ep, gfp_t gfp_flags) static void net2272_free_request(struct usb_ep *_ep, struct usb_request *_req) { - struct net2272_ep *ep; struct net2272_request *req; - ep = container_of(_ep, struct net2272_ep, ep); if (!_ep || !_req) return; diff --git a/drivers/usb/gadget/udc/pch_udc.c b/drivers/usb/gadget/udc/pch_udc.c index ebc51ec5790a..8ad847a5d4e4 100644 --- a/drivers/usb/gadget/udc/pch_udc.c +++ b/drivers/usb/gadget/udc/pch_udc.c @@ -1984,9 +1984,8 @@ static int pch_udc_pcd_set_halt(struct usb_ep *usbep, int halt) if (ep->num == PCH_UDC_EP0) ep->dev->stall = 1; pch_udc_ep_set_stall(ep); - pch_udc_enable_ep_interrupts(ep->dev, - PCH_UDC_EPINT(ep->in, - ep->num)); + pch_udc_enable_ep_interrupts( + ep->dev, PCH_UDC_EPINT(ep->in, ep->num)); } else { pch_udc_ep_clear_stall(ep); } @@ -2451,16 +2450,11 @@ static void pch_udc_svc_control_out(struct pch_udc_dev *dev) */ static void pch_udc_postsvc_epinters(struct pch_udc_dev *dev, int ep_num) { - struct pch_udc_ep *ep; - struct pch_udc_request *req; - - ep = &dev->ep[UDC_EPIN_IDX(ep_num)]; - if (!list_empty(&ep->queue)) { - req = list_entry(ep->queue.next, struct pch_udc_request, queue); - pch_udc_enable_ep_interrupts(ep->dev, - PCH_UDC_EPINT(ep->in, ep->num)); - pch_udc_ep_clear_nak(ep); - } + struct pch_udc_ep *ep = &dev->ep[UDC_EPIN_IDX(ep_num)]; + if (list_empty(&ep->queue)) + return; + pch_udc_enable_ep_interrupts(ep->dev, PCH_UDC_EPINT(ep->in, ep->num)); + pch_udc_ep_clear_nak(ep); } /** diff --git a/drivers/usb/gadget/udc/udc-xilinx.c b/drivers/usb/gadget/udc/udc-xilinx.c index 1cbb0ac6b182..f8bf290f1894 100644 --- a/drivers/usb/gadget/udc/udc-xilinx.c +++ b/drivers/usb/gadget/udc/udc-xilinx.c @@ -2055,7 +2055,6 @@ static int xudc_probe(struct platform_device *pdev) struct device_node *np = pdev->dev.of_node; struct resource *res; struct xusb_udc *udc; - struct xusb_ep *ep0; int irq; int ret; u32 ier; @@ -2119,8 +2118,6 @@ static int xudc_probe(struct platform_device *pdev) xudc_eps_init(udc); - ep0 = &udc->ep[0]; - /* Set device address to 0.*/ udc->write_fn(udc->addr, XUSB_ADDRESS_OFFSET, 0); From d58fcf81bb8facc0cd22490c43698f272c1ee07b Mon Sep 17 00:00:00 2001 From: Michal Nazarewicz Date: Tue, 31 May 2016 14:17:22 +0200 Subject: [PATCH 092/109] usb: gadget: m66592: fix unused-but-set-variable warnings MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This patch fixes the following (W=1) warnings: drivers/usb/gadget/udc/m66592-udc.c: In function ‘m66592_irq’: drivers/usb/gadget/udc/m66592-udc.c:1203:15: warning: variable ‘nrdyenb’ set but not used [-Wunused-but-set-variable] u16 brdyenb, nrdyenb, bempenb; ^ drivers/usb/gadget/udc/m66592-udc.c:1202:15: warning: variable ‘nrdysts’ set but not used [-Wunused-but-set-variable] u16 brdysts, nrdysts, bempsts; ^ In doing so, it removes calls to m66592_read function which does I/O with the device, but I hope the reads don’t have any side effects that are needed. Signed-off-by: Michal Nazarewicz Signed-off-by: Felipe Balbi --- drivers/usb/gadget/udc/m66592-udc.c | 24 ++++++------------------ 1 file changed, 6 insertions(+), 18 deletions(-) diff --git a/drivers/usb/gadget/udc/m66592-udc.c b/drivers/usb/gadget/udc/m66592-udc.c index b1cfa96cc88f..6e977dc22570 100644 --- a/drivers/usb/gadget/udc/m66592-udc.c +++ b/drivers/usb/gadget/udc/m66592-udc.c @@ -1199,8 +1199,6 @@ static irqreturn_t m66592_irq(int irq, void *_m66592) struct m66592 *m66592 = _m66592; u16 intsts0; u16 intenb0; - u16 brdysts, nrdysts, bempsts; - u16 brdyenb, nrdyenb, bempenb; u16 savepipe; u16 mask0; @@ -1224,12 +1222,10 @@ static irqreturn_t m66592_irq(int irq, void *_m66592) mask0 = intsts0 & intenb0; if (mask0) { - brdysts = m66592_read(m66592, M66592_BRDYSTS); - nrdysts = m66592_read(m66592, M66592_NRDYSTS); - bempsts = m66592_read(m66592, M66592_BEMPSTS); - brdyenb = m66592_read(m66592, M66592_BRDYENB); - nrdyenb = m66592_read(m66592, M66592_NRDYENB); - bempenb = m66592_read(m66592, M66592_BEMPENB); + u16 brdysts = m66592_read(m66592, M66592_BRDYSTS); + u16 bempsts = m66592_read(m66592, M66592_BEMPSTS); + u16 brdyenb = m66592_read(m66592, M66592_BRDYENB); + u16 bempenb = m66592_read(m66592, M66592_BEMPENB); if (mask0 & M66592_VBINT) { m66592_write(m66592, 0xffff & ~M66592_VBINT, @@ -1408,28 +1404,20 @@ static int m66592_dequeue(struct usb_ep *_ep, struct usb_request *_req) static int m66592_set_halt(struct usb_ep *_ep, int value) { - struct m66592_ep *ep; - struct m66592_request *req; + struct m66592_ep *ep = container_of(_ep, struct m66592_ep, ep); unsigned long flags; int ret = 0; - ep = container_of(_ep, struct m66592_ep, ep); - req = list_entry(ep->queue.next, struct m66592_request, queue); - spin_lock_irqsave(&ep->m66592->lock, flags); if (!list_empty(&ep->queue)) { ret = -EAGAIN; - goto out; - } - if (value) { + } else if (value) { ep->busy = 1; pipe_stall(ep->m66592, ep->pipenum); } else { ep->busy = 0; pipe_stop(ep->m66592, ep->pipenum); } - -out: spin_unlock_irqrestore(&ep->m66592->lock, flags); return ret; } From 906ae52db2695f8b34b292cc7d74d126f4a4612d Mon Sep 17 00:00:00 2001 From: Michal Nazarewicz Date: Tue, 31 May 2016 14:17:23 +0200 Subject: [PATCH 093/109] usb: gadget: r8a66597: fix unused-but-set-variable warnings MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This patch fixes the following (W=1) warnings: drivers/usb/gadget/udc/r8a66597-udc.c: In function ‘r8a66597_irq’: drivers/usb/gadget/udc/r8a66597-udc.c:1468:15: warning: variable ‘nrdyenb’ set but not used [-Wunused-but-set-variable] u16 brdyenb, nrdyenb, bempenb; ^ drivers/usb/gadget/udc/r8a66597-udc.c:1467:15: warning: variable ‘nrdysts’ set but not used [-Wunused-but-set-variable] u16 brdysts, nrdysts, bempsts; ^ In doing so, it removes calls to r8a66597_read function which does I/O with the device, but I hope the reads don’t have any side effects that are needed. Signed-off-by: Michal Nazarewicz Signed-off-by: Felipe Balbi --- drivers/usb/gadget/udc/r8a66597-udc.c | 24 ++++++------------------ 1 file changed, 6 insertions(+), 18 deletions(-) diff --git a/drivers/usb/gadget/udc/r8a66597-udc.c b/drivers/usb/gadget/udc/r8a66597-udc.c index 8b300e6da7fc..f2c8862093a2 100644 --- a/drivers/usb/gadget/udc/r8a66597-udc.c +++ b/drivers/usb/gadget/udc/r8a66597-udc.c @@ -1464,8 +1464,6 @@ static irqreturn_t r8a66597_irq(int irq, void *_r8a66597) struct r8a66597 *r8a66597 = _r8a66597; u16 intsts0; u16 intenb0; - u16 brdysts, nrdysts, bempsts; - u16 brdyenb, nrdyenb, bempenb; u16 savepipe; u16 mask0; @@ -1481,12 +1479,10 @@ static irqreturn_t r8a66597_irq(int irq, void *_r8a66597) mask0 = intsts0 & intenb0; if (mask0) { - brdysts = r8a66597_read(r8a66597, BRDYSTS); - nrdysts = r8a66597_read(r8a66597, NRDYSTS); - bempsts = r8a66597_read(r8a66597, BEMPSTS); - brdyenb = r8a66597_read(r8a66597, BRDYENB); - nrdyenb = r8a66597_read(r8a66597, NRDYENB); - bempenb = r8a66597_read(r8a66597, BEMPENB); + u16 brdysts = r8a66597_read(r8a66597, BRDYSTS); + u16 bempsts = r8a66597_read(r8a66597, BEMPSTS); + u16 brdyenb = r8a66597_read(r8a66597, BRDYENB); + u16 bempenb = r8a66597_read(r8a66597, BEMPENB); if (mask0 & VBINT) { r8a66597_write(r8a66597, 0xffff & ~VBINT, @@ -1658,20 +1654,14 @@ static int r8a66597_dequeue(struct usb_ep *_ep, struct usb_request *_req) static int r8a66597_set_halt(struct usb_ep *_ep, int value) { - struct r8a66597_ep *ep; - struct r8a66597_request *req; + struct r8a66597_ep *ep = container_of(_ep, struct r8a66597_ep, ep); unsigned long flags; int ret = 0; - ep = container_of(_ep, struct r8a66597_ep, ep); - req = get_request_from_ep(ep); - spin_lock_irqsave(&ep->r8a66597->lock, flags); if (!list_empty(&ep->queue)) { ret = -EAGAIN; - goto out; - } - if (value) { + } else if (value) { ep->busy = 1; pipe_stall(ep->r8a66597, ep->pipenum); } else { @@ -1679,8 +1669,6 @@ static int r8a66597_set_halt(struct usb_ep *_ep, int value) ep->wedge = 0; pipe_stop(ep->r8a66597, ep->pipenum); } - -out: spin_unlock_irqrestore(&ep->r8a66597->lock, flags); return ret; } From bd8dc7375b7e147de0f09d8be89460d4143daf31 Mon Sep 17 00:00:00 2001 From: Michal Nazarewicz Date: Tue, 31 May 2016 14:17:24 +0200 Subject: [PATCH 094/109] usb: gadget: mv_u3d: fix unused-but-set-variable warnings MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This patch fixes the following (W=1) warnings: drivers/usb/gadget/udc/mv_u3d_core.c: In function ‘mv_u3d_process_ep_req’: drivers/usb/gadget/udc/mv_u3d_core.c:124:6: warning: variable ‘trb_complete’ set but not used [-Wunused-but-set-variable] int trb_complete, actual, remaining_length = 0; ^ drivers/usb/gadget/udc/mv_u3d_core.c:123:28: warning: variable ‘curr_ep_context’ set but not used [-Wunused-but-set-variable] struct mv_u3d_ep_context *curr_ep_context; ^ drivers/usb/gadget/udc/mv_u3d_core.c:122:13: warning: variable ‘cur_deq_lo’ set but not used [-Wunused-but-set-variable] dma_addr_t cur_deq_lo; ^ drivers/usb/gadget/udc/mv_u3d_core.c: In function ‘mv_u3d_ep_enable’: drivers/usb/gadget/udc/mv_u3d_core.c:530:28: warning: variable ‘ep_context’ set but not used [-Wunused-but-set-variable] struct mv_u3d_ep_context *ep_context; ^ drivers/usb/gadget/udc/mv_u3d_core.c: In function ‘mv_u3d_ep_disable’: drivers/usb/gadget/udc/mv_u3d_core.c:636:28: warning: variable ‘ep_context’ set but not used [-Wunused-but-set-variable] struct mv_u3d_ep_context *ep_context; ^ In doing so, it removes calls to ioread32 function which does I/O with the device, but I hope the reads don’t have any side effects that are needed. Signed-off-by: Michal Nazarewicz Signed-off-by: Felipe Balbi --- drivers/usb/gadget/udc/mv_u3d_core.c | 23 +++-------------------- 1 file changed, 3 insertions(+), 20 deletions(-) diff --git a/drivers/usb/gadget/udc/mv_u3d_core.c b/drivers/usb/gadget/udc/mv_u3d_core.c index dafe74eb9ade..b9e19a591322 100644 --- a/drivers/usb/gadget/udc/mv_u3d_core.c +++ b/drivers/usb/gadget/udc/mv_u3d_core.c @@ -119,18 +119,14 @@ static int mv_u3d_process_ep_req(struct mv_u3d *u3d, int index, struct mv_u3d_req *curr_req) { struct mv_u3d_trb *curr_trb; - dma_addr_t cur_deq_lo; - struct mv_u3d_ep_context *curr_ep_context; - int trb_complete, actual, remaining_length = 0; + int actual, remaining_length = 0; int direction, ep_num; int retval = 0; u32 tmp, status, length; - curr_ep_context = &u3d->ep_context[index]; direction = index % 2; ep_num = index / 2; - trb_complete = 0; actual = curr_req->req.length; while (!list_empty(&curr_req->trb_list)) { @@ -143,15 +139,10 @@ static int mv_u3d_process_ep_req(struct mv_u3d *u3d, int index, } curr_trb->trb_hw->ctrl.own = 0; - if (direction == MV_U3D_EP_DIR_OUT) { + if (direction == MV_U3D_EP_DIR_OUT) tmp = ioread32(&u3d->vuc_regs->rxst[ep_num].statuslo); - cur_deq_lo = - ioread32(&u3d->vuc_regs->rxst[ep_num].curdeqlo); - } else { + else tmp = ioread32(&u3d->vuc_regs->txst[ep_num].statuslo); - cur_deq_lo = - ioread32(&u3d->vuc_regs->txst[ep_num].curdeqlo); - } status = tmp >> MV_U3D_XFERSTATUS_COMPLETE_SHIFT; length = tmp & MV_U3D_XFERSTATUS_TRB_LENGTH_MASK; @@ -527,7 +518,6 @@ static int mv_u3d_ep_enable(struct usb_ep *_ep, { struct mv_u3d *u3d; struct mv_u3d_ep *ep; - struct mv_u3d_ep_context *ep_context; u16 max = 0; unsigned maxburst = 0; u32 epxcr, direction; @@ -548,9 +538,6 @@ static int mv_u3d_ep_enable(struct usb_ep *_ep, _ep->maxburst = 1; maxburst = _ep->maxburst; - /* Get the endpoint context address */ - ep_context = (struct mv_u3d_ep_context *)ep->ep_context; - /* Set the max burst size */ switch (desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) { case USB_ENDPOINT_XFER_BULK: @@ -633,7 +620,6 @@ static int mv_u3d_ep_disable(struct usb_ep *_ep) { struct mv_u3d *u3d; struct mv_u3d_ep *ep; - struct mv_u3d_ep_context *ep_context; u32 epxcr, direction; unsigned long flags; @@ -646,9 +632,6 @@ static int mv_u3d_ep_disable(struct usb_ep *_ep) u3d = ep->u3d; - /* Get the endpoint context address */ - ep_context = ep->ep_context; - direction = mv_u3d_ep_dir(ep); /* nuke all pending requests (does flush) */ From f7d74de355976cbab89eeefe0063771c0de71457 Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Sat, 4 Jun 2016 06:53:25 +0100 Subject: [PATCH 095/109] usb: gadget: bdc: fix spelling mistake: "allocted" -> "allocated" trivial fix to spelling mistake Signed-off-by: Colin Ian King Signed-off-by: Felipe Balbi --- drivers/usb/gadget/udc/bdc/bdc_ep.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/usb/gadget/udc/bdc/bdc_ep.c b/drivers/usb/gadget/udc/bdc/bdc_ep.c index 650717e7192a..ccaa74ab6c0e 100644 --- a/drivers/usb/gadget/udc/bdc/bdc_ep.c +++ b/drivers/usb/gadget/udc/bdc/bdc_ep.c @@ -81,7 +81,7 @@ static void ep_bd_list_free(struct bdc_ep *ep, u32 num_tabs) continue; } if (!bd_table->start_bd) { - dev_dbg(bdc->dev, "bd dma pool not allocted\n"); + dev_dbg(bdc->dev, "bd dma pool not allocated\n"); continue; } From 7c8b9c318490afe3e8bb244ef7e488265871aba7 Mon Sep 17 00:00:00 2001 From: Sandhya Bankar Date: Tue, 31 May 2016 08:59:44 -0400 Subject: [PATCH 096/109] usb: phy: omap-otg: Space required after that ','. Space required after that ','. Reviewed-by: Aaro Koskinen Signed-off-by: Sandhya Bankar Signed-off-by: Felipe Balbi --- drivers/usb/phy/phy-omap-otg.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/usb/phy/phy-omap-otg.c b/drivers/usb/phy/phy-omap-otg.c index c4bf2de6d14e..6f6d2a7fd5a0 100644 --- a/drivers/usb/phy/phy-omap-otg.c +++ b/drivers/usb/phy/phy-omap-otg.c @@ -148,7 +148,7 @@ static int omap_otg_remove(struct platform_device *pdev) struct otg_device *otg_dev = platform_get_drvdata(pdev); struct extcon_dev *edev = otg_dev->extcon; - extcon_unregister_notifier(edev, EXTCON_USB_HOST,&otg_dev->id_nb); + extcon_unregister_notifier(edev, EXTCON_USB_HOST, &otg_dev->id_nb); extcon_unregister_notifier(edev, EXTCON_USB, &otg_dev->vbus_nb); return 0; From c662a31be755f5e315945e2c802c335eb8baddb8 Mon Sep 17 00:00:00 2001 From: Michal Nazarewicz Date: Sat, 21 May 2016 20:47:34 +0200 Subject: [PATCH 097/109] usb: gadget: f_fs: printk error when excess data is dropped on read Add a pr_err when host sent more data then the size of the buffer user space gave us. This may happen on UDCs which require OUT requests to be aligned to max packet size. The patch includes a description of the situation. Signed-off-by: Michal Nazarewicz Signed-off-by: Felipe Balbi --- drivers/usb/gadget/function/f_fs.c | 61 ++++++++++++++++++++++-------- 1 file changed, 46 insertions(+), 15 deletions(-) diff --git a/drivers/usb/gadget/function/f_fs.c b/drivers/usb/gadget/function/f_fs.c index 70ed37065bab..bb3d40acae78 100644 --- a/drivers/usb/gadget/function/f_fs.c +++ b/drivers/usb/gadget/function/f_fs.c @@ -640,6 +640,44 @@ static void ffs_epfile_io_complete(struct usb_ep *_ep, struct usb_request *req) } } +static ssize_t ffs_copy_to_iter(void *data, int data_len, struct iov_iter *iter) +{ + ssize_t ret = copy_to_iter(data, data_len, iter); + if (likely(ret == data_len)) + return ret; + + if (unlikely(iov_iter_count(iter))) + return -EFAULT; + + /* + * Dear user space developer! + * + * TL;DR: To stop getting below error message in your kernel log, change + * user space code using functionfs to align read buffers to a max + * packet size. + * + * Some UDCs (e.g. dwc3) require request sizes to be a multiple of a max + * packet size. When unaligned buffer is passed to functionfs, it + * internally uses a larger, aligned buffer so that such UDCs are happy. + * + * Unfortunately, this means that host may send more data than was + * requested in read(2) system call. f_fs doesn’t know what to do with + * that excess data so it simply drops it. + * + * Was the buffer aligned in the first place, no such problem would + * happen. + * + * This only affects OUT endpoints, i.e. reading data with a read(2), + * aio_read(2) etc. system calls. Writing data to an IN endpoint is not + * affected. + */ + pr_err("functionfs read size %d > requested size %zd, dropping excess data. " + "Align read buffer size to max packet size to avoid the problem.\n", + data_len, ret); + + return ret; +} + static void ffs_user_copy_worker(struct work_struct *work) { struct ffs_io_data *io_data = container_of(work, struct ffs_io_data, @@ -650,9 +688,7 @@ static void ffs_user_copy_worker(struct work_struct *work) if (io_data->read && ret > 0) { use_mm(io_data->mm); - ret = copy_to_iter(io_data->buf, ret, &io_data->data); - if (ret != io_data->req->actual && iov_iter_count(&io_data->data)) - ret = -EFAULT; + ret = ffs_copy_to_iter(io_data->buf, ret, &io_data->data); unuse_mm(io_data->mm); } @@ -803,18 +839,13 @@ static ssize_t ffs_epfile_io(struct file *file, struct ffs_io_data *io_data) interrupted = ep->status < 0; } - /* - * XXX We may end up silently droping data here. Since data_len - * (i.e. req->length) may be bigger than len (after being - * rounded up to maxpacketsize), we may end up with more data - * then user space has space for. - */ - ret = interrupted ? -EINTR : ep->status; - if (io_data->read && ret > 0) { - ret = copy_to_iter(data, ret, &io_data->data); - if (!ret) - ret = -EFAULT; - } + if (interrupted) + ret = -EINTR; + else if (io_data->read && ep->status > 0) + ret = ffs_copy_to_iter(data, ep->status, + &io_data->data); + else + ret = ep->status; goto error_mutex; } else if (!(req = usb_ep_alloc_request(ep->ep, GFP_KERNEL))) { ret = -ENOMEM; From 9353afbbfa7b10779a1aa108fcc23e32fd625990 Mon Sep 17 00:00:00 2001 From: Michal Nazarewicz Date: Sat, 21 May 2016 20:47:35 +0200 Subject: [PATCH 098/109] =?UTF-8?q?usb:=20gadget:=20f=5Ffs:=20buffer=20dat?= =?UTF-8?q?a=20from=20=E2=80=98oversized=E2=80=99=20OUT=20requests?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit f_fs rounds up read(2) requests to a multiple of a max packet size which means that host may provide more data than user has space for. So far, the excess data has been silently ignored. This introduces a buffer for a tail of such requests so that they are returned on next read instead of being ignored. Signed-off-by: Michal Nazarewicz Signed-off-by: Felipe Balbi --- drivers/usb/gadget/function/f_fs.c | 130 ++++++++++++++++++++++++----- 1 file changed, 109 insertions(+), 21 deletions(-) diff --git a/drivers/usb/gadget/function/f_fs.c b/drivers/usb/gadget/function/f_fs.c index bb3d40acae78..a91fcb0475b2 100644 --- a/drivers/usb/gadget/function/f_fs.c +++ b/drivers/usb/gadget/function/f_fs.c @@ -130,6 +130,12 @@ struct ffs_epfile { struct dentry *dentry; + /* + * Buffer for holding data from partial reads which may happen since + * we’re rounding user read requests to a multiple of a max packet size. + */ + struct ffs_buffer *read_buffer; /* P: epfile->mutex */ + char name[5]; unsigned char in; /* P: ffs->eps_lock */ @@ -138,6 +144,12 @@ struct ffs_epfile { unsigned char _pad; }; +struct ffs_buffer { + size_t length; + char *data; + char storage[]; +}; + /* ffs_io_data structure ***************************************************/ struct ffs_io_data { @@ -667,6 +679,11 @@ static ssize_t ffs_copy_to_iter(void *data, int data_len, struct iov_iter *iter) * Was the buffer aligned in the first place, no such problem would * happen. * + * Data may be dropped only in AIO reads. Synchronous reads are handled + * by splitting a request into multiple parts. This splitting may still + * be a problem though so it’s likely best to align the buffer + * regardless of it being AIO or not.. + * * This only affects OUT endpoints, i.e. reading data with a read(2), * aio_read(2) etc. system calls. Writing data to an IN endpoint is not * affected. @@ -716,6 +733,56 @@ static void ffs_epfile_async_io_complete(struct usb_ep *_ep, schedule_work(&io_data->work); } +/* Assumes epfile->mutex is held. */ +static ssize_t __ffs_epfile_read_buffered(struct ffs_epfile *epfile, + struct iov_iter *iter) +{ + struct ffs_buffer *buf = epfile->read_buffer; + ssize_t ret; + if (!buf) + return 0; + + ret = copy_to_iter(buf->data, buf->length, iter); + if (buf->length == ret) { + kfree(buf); + epfile->read_buffer = NULL; + } else if (unlikely(iov_iter_count(iter))) { + ret = -EFAULT; + } else { + buf->length -= ret; + buf->data += ret; + } + return ret; +} + +/* Assumes epfile->mutex is held. */ +static ssize_t __ffs_epfile_read_data(struct ffs_epfile *epfile, + void *data, int data_len, + struct iov_iter *iter) +{ + struct ffs_buffer *buf; + + ssize_t ret = copy_to_iter(data, data_len, iter); + if (likely(data_len == ret)) + return ret; + + if (unlikely(iov_iter_count(iter))) + return -EFAULT; + + /* See ffs_copy_to_iter for more context. */ + pr_warn("functionfs read size %d > requested size %zd, splitting request into multiple reads.", + data_len, ret); + + data_len -= ret; + buf = kmalloc(sizeof(*buf) + data_len, GFP_KERNEL); + buf->length = data_len; + buf->data = buf->storage; + memcpy(buf->storage, data + ret, data_len); + epfile->read_buffer = buf; + + return ret; +} + static ssize_t ffs_epfile_io(struct file *file, struct ffs_io_data *io_data) { struct ffs_epfile *epfile = file->private_data; @@ -745,21 +812,40 @@ static ssize_t ffs_epfile_io(struct file *file, struct ffs_io_data *io_data) if (halt && epfile->isoc) return -EINVAL; + /* We will be using request and read_buffer */ + ret = ffs_mutex_lock(&epfile->mutex, file->f_flags & O_NONBLOCK); + if (unlikely(ret)) + goto error; + /* Allocate & copy */ if (!halt) { + struct usb_gadget *gadget; + + /* + * Do we have buffered data from previous partial read? Check + * that for synchronous case only because we do not have + * facility to ‘wake up’ a pending asynchronous read and push + * buffered data to it which we would need to make things behave + * consistently. + */ + if (!io_data->aio && io_data->read) { + ret = __ffs_epfile_read_buffered(epfile, &io_data->data); + if (ret) + goto error_mutex; + } + /* * if we _do_ wait above, the epfile->ffs->gadget might be NULL * before the waiting completes, so do not assign to 'gadget' * earlier */ - struct usb_gadget *gadget = epfile->ffs->gadget; - size_t copied; + gadget = epfile->ffs->gadget; spin_lock_irq(&epfile->ffs->eps_lock); /* In the meantime, endpoint got disabled or changed. */ if (epfile->ep != ep) { - spin_unlock_irq(&epfile->ffs->eps_lock); - return -ESHUTDOWN; + ret = -ESHUTDOWN; + goto error_lock; } data_len = iov_iter_count(&io_data->data); /* @@ -771,22 +857,17 @@ static ssize_t ffs_epfile_io(struct file *file, struct ffs_io_data *io_data) spin_unlock_irq(&epfile->ffs->eps_lock); data = kmalloc(data_len, GFP_KERNEL); - if (unlikely(!data)) - return -ENOMEM; - if (!io_data->read) { - copied = copy_from_iter(data, data_len, &io_data->data); - if (copied != data_len) { - ret = -EFAULT; - goto error; - } + if (unlikely(!data)) { + ret = -ENOMEM; + goto error_mutex; + } + if (!io_data->read && + copy_from_iter(data, data_len, &io_data->data) != data_len) { + ret = -EFAULT; + goto error_mutex; } } - /* We will be using request */ - ret = ffs_mutex_lock(&epfile->mutex, file->f_flags & O_NONBLOCK); - if (unlikely(ret)) - goto error; - spin_lock_irq(&epfile->ffs->eps_lock); if (epfile->ep != ep) { @@ -842,8 +923,8 @@ static ssize_t ffs_epfile_io(struct file *file, struct ffs_io_data *io_data) if (interrupted) ret = -EINTR; else if (io_data->read && ep->status > 0) - ret = ffs_copy_to_iter(data, ep->status, - &io_data->data); + ret = __ffs_epfile_read_data(epfile, data, ep->status, + &io_data->data); else ret = ep->status; goto error_mutex; @@ -1011,6 +1092,8 @@ ffs_epfile_release(struct inode *inode, struct file *file) ENTER(); + kfree(epfile->read_buffer); + epfile->read_buffer = NULL; ffs_data_closed(epfile->ffs); return 0; @@ -1636,19 +1719,24 @@ static void ffs_func_eps_disable(struct ffs_function *func) unsigned count = func->ffs->eps_count; unsigned long flags; - spin_lock_irqsave(&func->ffs->eps_lock, flags); do { + if (epfile) + mutex_lock(&epfile->mutex); + spin_lock_irqsave(&func->ffs->eps_lock, flags); /* pending requests get nuked */ if (likely(ep->ep)) usb_ep_disable(ep->ep); ++ep; + spin_unlock_irqrestore(&func->ffs->eps_lock, flags); if (epfile) { epfile->ep = NULL; + kfree(epfile->read_buffer); + epfile->read_buffer = NULL; + mutex_unlock(&epfile->mutex); ++epfile; } } while (--count); - spin_unlock_irqrestore(&func->ffs->eps_lock, flags); } static int ffs_func_eps_enable(struct ffs_function *func) From b9b092319be29bf665f1b004679de722b81c9123 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Wed, 18 May 2016 23:24:06 +0200 Subject: [PATCH 099/109] usb: phy: move msm_hsusb.h into driver As a preparation for another cleanup, this moves the header file for the phy-msm-usb driver into the driver itself. No other file includes it any more, and we don't really want it in the global namespace anyway. Signed-off-by: Arnd Bergmann Signed-off-by: Felipe Balbi --- drivers/usb/phy/phy-msm-usb.c | 178 +++++++++++++++++++++++++++++- include/linux/usb/msm_hsusb.h | 200 ---------------------------------- 2 files changed, 177 insertions(+), 201 deletions(-) delete mode 100644 include/linux/usb/msm_hsusb.h diff --git a/drivers/usb/phy/phy-msm-usb.c b/drivers/usb/phy/phy-msm-usb.c index 72b387d592c2..8a34759727bb 100644 --- a/drivers/usb/phy/phy-msm-usb.c +++ b/drivers/usb/phy/phy-msm-usb.c @@ -18,6 +18,7 @@ #include #include +#include #include #include #include @@ -35,6 +36,8 @@ #include #include #include +#include +#include #include #include @@ -42,10 +45,183 @@ #include #include #include -#include #include #include +/** + * OTG control + * + * OTG_NO_CONTROL Id/VBUS notifications not required. Useful in host + * only configuration. + * OTG_PHY_CONTROL Id/VBUS notifications comes form USB PHY. + * OTG_PMIC_CONTROL Id/VBUS notifications comes from PMIC hardware. + * OTG_USER_CONTROL Id/VBUS notifcations comes from User via sysfs. + * + */ +enum otg_control_type { + OTG_NO_CONTROL = 0, + OTG_PHY_CONTROL, + OTG_PMIC_CONTROL, + OTG_USER_CONTROL, +}; + +/** + * PHY used in + * + * INVALID_PHY Unsupported PHY + * CI_45NM_INTEGRATED_PHY Chipidea 45nm integrated PHY + * SNPS_28NM_INTEGRATED_PHY Synopsis 28nm integrated PHY + * + */ +enum msm_usb_phy_type { + INVALID_PHY = 0, + CI_45NM_INTEGRATED_PHY, + SNPS_28NM_INTEGRATED_PHY, +}; + +#define IDEV_CHG_MAX 1500 +#define IUNIT 100 + +/** + * Different states involved in USB charger detection. + * + * USB_CHG_STATE_UNDEFINED USB charger is not connected or detection + * process is not yet started. + * USB_CHG_STATE_WAIT_FOR_DCD Waiting for Data pins contact. + * USB_CHG_STATE_DCD_DONE Data pin contact is detected. + * USB_CHG_STATE_PRIMARY_DONE Primary detection is completed (Detects + * between SDP and DCP/CDP). + * USB_CHG_STATE_SECONDARY_DONE Secondary detection is completed (Detects + * between DCP and CDP). + * USB_CHG_STATE_DETECTED USB charger type is determined. + * + */ +enum usb_chg_state { + USB_CHG_STATE_UNDEFINED = 0, + USB_CHG_STATE_WAIT_FOR_DCD, + USB_CHG_STATE_DCD_DONE, + USB_CHG_STATE_PRIMARY_DONE, + USB_CHG_STATE_SECONDARY_DONE, + USB_CHG_STATE_DETECTED, +}; + +/** + * USB charger types + * + * USB_INVALID_CHARGER Invalid USB charger. + * USB_SDP_CHARGER Standard downstream port. Refers to a downstream port + * on USB2.0 compliant host/hub. + * USB_DCP_CHARGER Dedicated charger port (AC charger/ Wall charger). + * USB_CDP_CHARGER Charging downstream port. Enumeration can happen and + * IDEV_CHG_MAX can be drawn irrespective of USB state. + * + */ +enum usb_chg_type { + USB_INVALID_CHARGER = 0, + USB_SDP_CHARGER, + USB_DCP_CHARGER, + USB_CDP_CHARGER, +}; + +/** + * struct msm_otg_platform_data - platform device data + * for msm_otg driver. + * @phy_init_seq: PHY configuration sequence values. Value of -1 is reserved as + * "do not overwrite default vaule at this address". + * @phy_init_sz: PHY configuration sequence size. + * @vbus_power: VBUS power on/off routine. + * @power_budget: VBUS power budget in mA (0 will be treated as 500mA). + * @mode: Supported mode (OTG/peripheral/host). + * @otg_control: OTG switch controlled by user/Id pin + */ +struct msm_otg_platform_data { + int *phy_init_seq; + int phy_init_sz; + void (*vbus_power)(bool on); + unsigned power_budget; + enum usb_dr_mode mode; + enum otg_control_type otg_control; + enum msm_usb_phy_type phy_type; + void (*setup_gpio)(enum usb_otg_state state); +}; + +/** + * struct msm_usb_cable - structure for exteternal connector cable + * state tracking + * @nb: hold event notification callback + * @conn: used for notification registration + */ +struct msm_usb_cable { + struct notifier_block nb; + struct extcon_dev *extcon; +}; + +/** + * struct msm_otg: OTG driver data. Shared by HCD and DCD. + * @otg: USB OTG Transceiver structure. + * @pdata: otg device platform data. + * @irq: IRQ number assigned for HSUSB controller. + * @clk: clock struct of usb_hs_clk. + * @pclk: clock struct of usb_hs_pclk. + * @core_clk: clock struct of usb_hs_core_clk. + * @regs: ioremapped register base address. + * @inputs: OTG state machine inputs(Id, SessValid etc). + * @sm_work: OTG state machine work. + * @in_lpm: indicates low power mode (LPM) state. + * @async_int: Async interrupt arrived. + * @cur_power: The amount of mA available from downstream port. + * @chg_work: Charger detection work. + * @chg_state: The state of charger detection process. + * @chg_type: The type of charger attached. + * @dcd_retires: The retry count used to track Data contact + * detection process. + * @manual_pullup: true if VBUS is not routed to USB controller/phy + * and controller driver therefore enables pull-up explicitly before + * starting controller using usbcmd run/stop bit. + * @vbus: VBUS signal state trakining, using extcon framework + * @id: ID signal state trakining, using extcon framework + * @switch_gpio: Descriptor for GPIO used to control external Dual + * SPDT USB Switch. + * @reboot: Used to inform the driver to route USB D+/D- line to Device + * connector + */ +struct msm_otg { + struct usb_phy phy; + struct msm_otg_platform_data *pdata; + int irq; + struct clk *clk; + struct clk *pclk; + struct clk *core_clk; + void __iomem *regs; +#define ID 0 +#define B_SESS_VLD 1 + unsigned long inputs; + struct work_struct sm_work; + atomic_t in_lpm; + int async_int; + unsigned cur_power; + int phy_number; + struct delayed_work chg_work; + enum usb_chg_state chg_state; + enum usb_chg_type chg_type; + u8 dcd_retries; + struct regulator *v3p3; + struct regulator *v1p8; + struct regulator *vddcx; + + struct reset_control *phy_rst; + struct reset_control *link_rst; + int vdd_levels[3]; + + bool manual_pullup; + + struct msm_usb_cable vbus; + struct msm_usb_cable id; + + struct gpio_desc *switch_gpio; + struct notifier_block reboot; +}; + #define MSM_USB_BASE (motg->regs) #define DRIVER_NAME "msm_otg" diff --git a/include/linux/usb/msm_hsusb.h b/include/linux/usb/msm_hsusb.h deleted file mode 100644 index 8c8f6854c993..000000000000 --- a/include/linux/usb/msm_hsusb.h +++ /dev/null @@ -1,200 +0,0 @@ -/* linux/include/asm-arm/arch-msm/hsusb.h - * - * Copyright (C) 2008 Google, Inc. - * Author: Brian Swetland - * Copyright (c) 2009-2011, Code Aurora Forum. All rights reserved. - * - * This software is licensed under the terms of the GNU General Public - * License version 2, as published by the Free Software Foundation, and - * may be copied, distributed, and modified under those terms. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - */ - -#ifndef __ASM_ARCH_MSM_HSUSB_H -#define __ASM_ARCH_MSM_HSUSB_H - -#include -#include -#include -#include - -/** - * OTG control - * - * OTG_NO_CONTROL Id/VBUS notifications not required. Useful in host - * only configuration. - * OTG_PHY_CONTROL Id/VBUS notifications comes form USB PHY. - * OTG_PMIC_CONTROL Id/VBUS notifications comes from PMIC hardware. - * OTG_USER_CONTROL Id/VBUS notifcations comes from User via sysfs. - * - */ -enum otg_control_type { - OTG_NO_CONTROL = 0, - OTG_PHY_CONTROL, - OTG_PMIC_CONTROL, - OTG_USER_CONTROL, -}; - -/** - * PHY used in - * - * INVALID_PHY Unsupported PHY - * CI_45NM_INTEGRATED_PHY Chipidea 45nm integrated PHY - * SNPS_28NM_INTEGRATED_PHY Synopsis 28nm integrated PHY - * - */ -enum msm_usb_phy_type { - INVALID_PHY = 0, - CI_45NM_INTEGRATED_PHY, - SNPS_28NM_INTEGRATED_PHY, -}; - -#define IDEV_CHG_MAX 1500 -#define IUNIT 100 - -/** - * Different states involved in USB charger detection. - * - * USB_CHG_STATE_UNDEFINED USB charger is not connected or detection - * process is not yet started. - * USB_CHG_STATE_WAIT_FOR_DCD Waiting for Data pins contact. - * USB_CHG_STATE_DCD_DONE Data pin contact is detected. - * USB_CHG_STATE_PRIMARY_DONE Primary detection is completed (Detects - * between SDP and DCP/CDP). - * USB_CHG_STATE_SECONDARY_DONE Secondary detection is completed (Detects - * between DCP and CDP). - * USB_CHG_STATE_DETECTED USB charger type is determined. - * - */ -enum usb_chg_state { - USB_CHG_STATE_UNDEFINED = 0, - USB_CHG_STATE_WAIT_FOR_DCD, - USB_CHG_STATE_DCD_DONE, - USB_CHG_STATE_PRIMARY_DONE, - USB_CHG_STATE_SECONDARY_DONE, - USB_CHG_STATE_DETECTED, -}; - -/** - * USB charger types - * - * USB_INVALID_CHARGER Invalid USB charger. - * USB_SDP_CHARGER Standard downstream port. Refers to a downstream port - * on USB2.0 compliant host/hub. - * USB_DCP_CHARGER Dedicated charger port (AC charger/ Wall charger). - * USB_CDP_CHARGER Charging downstream port. Enumeration can happen and - * IDEV_CHG_MAX can be drawn irrespective of USB state. - * - */ -enum usb_chg_type { - USB_INVALID_CHARGER = 0, - USB_SDP_CHARGER, - USB_DCP_CHARGER, - USB_CDP_CHARGER, -}; - -/** - * struct msm_otg_platform_data - platform device data - * for msm_otg driver. - * @phy_init_seq: PHY configuration sequence values. Value of -1 is reserved as - * "do not overwrite default vaule at this address". - * @phy_init_sz: PHY configuration sequence size. - * @vbus_power: VBUS power on/off routine. - * @power_budget: VBUS power budget in mA (0 will be treated as 500mA). - * @mode: Supported mode (OTG/peripheral/host). - * @otg_control: OTG switch controlled by user/Id pin - */ -struct msm_otg_platform_data { - int *phy_init_seq; - int phy_init_sz; - void (*vbus_power)(bool on); - unsigned power_budget; - enum usb_dr_mode mode; - enum otg_control_type otg_control; - enum msm_usb_phy_type phy_type; - void (*setup_gpio)(enum usb_otg_state state); -}; - -/** - * struct msm_usb_cable - structure for exteternal connector cable - * state tracking - * @nb: hold event notification callback - * @conn: used for notification registration - */ -struct msm_usb_cable { - struct notifier_block nb; - struct extcon_dev *extcon; -}; - -/** - * struct msm_otg: OTG driver data. Shared by HCD and DCD. - * @otg: USB OTG Transceiver structure. - * @pdata: otg device platform data. - * @irq: IRQ number assigned for HSUSB controller. - * @clk: clock struct of usb_hs_clk. - * @pclk: clock struct of usb_hs_pclk. - * @core_clk: clock struct of usb_hs_core_clk. - * @regs: ioremapped register base address. - * @inputs: OTG state machine inputs(Id, SessValid etc). - * @sm_work: OTG state machine work. - * @in_lpm: indicates low power mode (LPM) state. - * @async_int: Async interrupt arrived. - * @cur_power: The amount of mA available from downstream port. - * @chg_work: Charger detection work. - * @chg_state: The state of charger detection process. - * @chg_type: The type of charger attached. - * @dcd_retires: The retry count used to track Data contact - * detection process. - * @manual_pullup: true if VBUS is not routed to USB controller/phy - * and controller driver therefore enables pull-up explicitly before - * starting controller using usbcmd run/stop bit. - * @vbus: VBUS signal state trakining, using extcon framework - * @id: ID signal state trakining, using extcon framework - * @switch_gpio: Descriptor for GPIO used to control external Dual - * SPDT USB Switch. - * @reboot: Used to inform the driver to route USB D+/D- line to Device - * connector - */ -struct msm_otg { - struct usb_phy phy; - struct msm_otg_platform_data *pdata; - int irq; - struct clk *clk; - struct clk *pclk; - struct clk *core_clk; - void __iomem *regs; -#define ID 0 -#define B_SESS_VLD 1 - unsigned long inputs; - struct work_struct sm_work; - atomic_t in_lpm; - int async_int; - unsigned cur_power; - int phy_number; - struct delayed_work chg_work; - enum usb_chg_state chg_state; - enum usb_chg_type chg_type; - u8 dcd_retries; - struct regulator *v3p3; - struct regulator *v1p8; - struct regulator *vddcx; - - struct reset_control *phy_rst; - struct reset_control *link_rst; - int vdd_levels[3]; - - bool manual_pullup; - - struct msm_usb_cable vbus; - struct msm_usb_cable id; - - struct gpio_desc *switch_gpio; - struct notifier_block reboot; -}; - -#endif From 72704f876f50b00f08d41d4b13d5a5d11262254f Mon Sep 17 00:00:00 2001 From: Baolin Wang Date: Mon, 16 May 2016 16:43:53 +0800 Subject: [PATCH 100/109] dwc3: gadget: Implement the suspend entry event handler It had changed to be suspend event for BIT6 in DEVT register from version 2.30a and above. Thus this patch introduces one suspend event handler to handle the suspend event. Signed-off-by: Baolin Wang Signed-off-by: Felipe Balbi --- drivers/usb/dwc3/gadget.c | 26 +++++++++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index f20975300594..8f8c2157910e 100644 --- a/drivers/usb/dwc3/gadget.c +++ b/drivers/usb/dwc3/gadget.c @@ -2695,6 +2695,17 @@ static void dwc3_gadget_linksts_change_interrupt(struct dwc3 *dwc, dwc->link_state = next; } +static void dwc3_gadget_suspend_interrupt(struct dwc3 *dwc, + unsigned int evtinfo) +{ + enum dwc3_link_state next = evtinfo & DWC3_LINK_STATE_MASK; + + if (dwc->link_state != next && next == DWC3_LINK_STATE_U3) + dwc3_suspend_gadget(dwc); + + dwc->link_state = next; +} + static void dwc3_gadget_hibernation_interrupt(struct dwc3 *dwc, unsigned int evtinfo) { @@ -2746,7 +2757,20 @@ static void dwc3_gadget_interrupt(struct dwc3 *dwc, dwc3_gadget_linksts_change_interrupt(dwc, event->event_info); break; case DWC3_DEVICE_EVENT_EOPF: - dwc3_trace(trace_dwc3_gadget, "End of Periodic Frame"); + /* It changed to be suspend event for version 2.30a and above */ + if (dwc->revision < DWC3_REVISION_230A) { + dwc3_trace(trace_dwc3_gadget, "End of Periodic Frame"); + } else { + dwc3_trace(trace_dwc3_gadget, "U3/L1-L2 Suspend Event"); + + /* + * Ignore suspend event until the gadget enters into + * USB_STATE_CONFIGURED state. + */ + if (dwc->gadget.state >= USB_STATE_CONFIGURED) + dwc3_gadget_suspend_interrupt(dwc, + event->event_info); + } break; case DWC3_DEVICE_EVENT_SOF: dwc3_trace(trace_dwc3_gadget, "Start of Periodic Frame"); From 43202800010102a857ffb119429d7dc5b41b7096 Mon Sep 17 00:00:00 2001 From: Sandhya Bankar Date: Wed, 4 May 2016 12:23:14 +0530 Subject: [PATCH 101/109] usb: Use (foo *) instead of (foo*). Use (foo *) instead of (foo*). Signed-off-by: Sandhya Bankar Signed-off-by: Felipe Balbi --- drivers/usb/gadget/config.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/usb/gadget/config.c b/drivers/usb/gadget/config.c index e6c0542a063b..17a6077b89a4 100644 --- a/drivers/usb/gadget/config.c +++ b/drivers/usb/gadget/config.c @@ -93,7 +93,7 @@ int usb_gadget_config_buf( *cp = *config; /* then interface/endpoint/class/vendor/... */ - len = usb_descriptor_fillbuf(USB_DT_CONFIG_SIZE + (u8*)buf, + len = usb_descriptor_fillbuf(USB_DT_CONFIG_SIZE + (u8 *)buf, length - USB_DT_CONFIG_SIZE, desc); if (len < 0) return len; From 1d23d16a88e6c8143b07339435ba061b131ebb8c Mon Sep 17 00:00:00 2001 From: Iago Abal Date: Tue, 21 Jun 2016 12:01:11 +0200 Subject: [PATCH 102/109] usb: gadget: pch_udc: reorder spin_[un]lock to avoid deadlock The above commit reordered spin_lock/unlock and now `&dev->lock' is acquired (rather than released) before calling `dev->driver->disconnect', `dev->driver->setup', `dev->driver->suspend', `usb_gadget_giveback_request', and `usb_gadget_udc_reset'. But this *may* not be the right way to fix the problem pointed by d3cb25a12138. Note that the other usb/gadget/udc drivers do release the lock before calling these functions. There are also inconsistencies within pch_udc.c, where `dev->driver->disconnect' is called while holding `&dev->lock' in lines 613 and 1184, but not in line 2739. Finally, commit d3cb25a12138 may have introduced several potential deadlocks. For instance, EBA (https://github.com/models-team/eba) reports: Double lock in drivers/usb/gadget/udc/pch_udc.c first at 2791: spin_lock(& dev->lock); [pch_udc_isr] second at 2694: spin_lock(& dev->lock); [pch_udc_svc_cfg_interrupt] after calling from 2793: pch_udc_dev_isr(dev, dev_intr); after calling from 2724: pch_udc_svc_cfg_interrupt(dev); Similarly, other potential deadlocks are 2791 -> 2793 -> 2721 -> 2657; and 2791 -> 2793 -> 2711 -> 2573 -> 1499 -> 1480. Fixes: d3cb25a12138 ("usb: gadget: udc: fix spin_lock in pch_udc") Signed-off-by: Iago Abal Signed-off-by: Felipe Balbi --- drivers/usb/gadget/udc/pch_udc.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/drivers/usb/gadget/udc/pch_udc.c b/drivers/usb/gadget/udc/pch_udc.c index 8ad847a5d4e4..a97da645c1b9 100644 --- a/drivers/usb/gadget/udc/pch_udc.c +++ b/drivers/usb/gadget/udc/pch_udc.c @@ -1477,11 +1477,11 @@ static void complete_req(struct pch_udc_ep *ep, struct pch_udc_request *req, req->dma_mapped = 0; } ep->halted = 1; - spin_lock(&dev->lock); + spin_unlock(&dev->lock); if (!ep->in) pch_udc_ep_clear_rrdy(ep); usb_gadget_giveback_request(&ep->ep, &req->req); - spin_unlock(&dev->lock); + spin_lock(&dev->lock); ep->halted = halted; } @@ -2567,9 +2567,9 @@ static void pch_udc_svc_ur_interrupt(struct pch_udc_dev *dev) empty_req_queue(ep); } if (dev->driver) { - spin_lock(&dev->lock); - usb_gadget_udc_reset(&dev->gadget, dev->driver); spin_unlock(&dev->lock); + usb_gadget_udc_reset(&dev->gadget, dev->driver); + spin_lock(&dev->lock); } } @@ -2648,9 +2648,9 @@ static void pch_udc_svc_intf_interrupt(struct pch_udc_dev *dev) dev->ep[i].halted = 0; } dev->stall = 0; - spin_lock(&dev->lock); - dev->driver->setup(&dev->gadget, &dev->setup_data); spin_unlock(&dev->lock); + dev->driver->setup(&dev->gadget, &dev->setup_data); + spin_lock(&dev->lock); } /** @@ -2685,9 +2685,9 @@ static void pch_udc_svc_cfg_interrupt(struct pch_udc_dev *dev) dev->stall = 0; /* call gadget zero with setup data received */ - spin_lock(&dev->lock); - dev->driver->setup(&dev->gadget, &dev->setup_data); spin_unlock(&dev->lock); + dev->driver->setup(&dev->gadget, &dev->setup_data); + spin_lock(&dev->lock); } /** From 70992588193617511e1b50bf7adee2fd371a476b Mon Sep 17 00:00:00 2001 From: Ben Dooks Date: Tue, 21 Jun 2016 18:52:54 +0100 Subject: [PATCH 103/109] usb: renesas_usbhs: make usbhs_write32() static The usbhs_write32 function is not used outside of the rcar3.c file, so fix the following sparse warning by making it static: drivers/usb/renesas_usbhs/rcar3.c:26:6: warning: symbol 'usbhs_write32' was not declared. Should it be static? Signed-off-by: Ben Dooks Signed-off-by: Felipe Balbi --- drivers/usb/renesas_usbhs/rcar3.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/usb/renesas_usbhs/rcar3.c b/drivers/usb/renesas_usbhs/rcar3.c index 38b01f2aeeb0..1d70add926f0 100644 --- a/drivers/usb/renesas_usbhs/rcar3.c +++ b/drivers/usb/renesas_usbhs/rcar3.c @@ -23,7 +23,7 @@ #define UGCTRL2_RESERVED_3 0x00000001 /* bit[3:0] should be B'0001 */ #define UGCTRL2_USB0SEL_OTG 0x00000030 -void usbhs_write32(struct usbhs_priv *priv, u32 reg, u32 data) +static void usbhs_write32(struct usbhs_priv *priv, u32 reg, u32 data) { iowrite32(data, priv->base + reg); } From 0d7995031a8e7a34e5638d57a44a51aae39e321c Mon Sep 17 00:00:00 2001 From: Yoshihiro Shimoda Date: Mon, 27 Jun 2016 21:09:18 +0900 Subject: [PATCH 104/109] usb: renesas_usbhs: show error code when probe failed To know why the driver probing failed, this patch shows error code. Signed-off-by: Yoshihiro Shimoda Signed-off-by: Felipe Balbi --- drivers/usb/renesas_usbhs/common.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/usb/renesas_usbhs/common.c b/drivers/usb/renesas_usbhs/common.c index baeb7d23bf24..8fbbc2d32371 100644 --- a/drivers/usb/renesas_usbhs/common.c +++ b/drivers/usb/renesas_usbhs/common.c @@ -697,7 +697,7 @@ probe_end_fifo_exit: probe_end_pipe_exit: usbhs_pipe_remove(priv); - dev_info(&pdev->dev, "probe failed\n"); + dev_info(&pdev->dev, "probe failed (%d)\n", ret); return ret; } From e135ab7405f562c7709806e355b1521ee68548dc Mon Sep 17 00:00:00 2001 From: Nicolas Iooss Date: Sun, 26 Jun 2016 10:12:38 +0200 Subject: [PATCH 105/109] usb: dwc2: add printf attribute to cat_printf() As cat_printf() uses printf format strings in its parameters, adding __printf attribute allows the compiler to detect at compile-time some errors related to format strings (with -Wformat warning flag). Signed-off-by: Nicolas Iooss Acked-by: John Youn Signed-off-by: Felipe Balbi --- drivers/usb/dwc2/hcd_queue.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/usb/dwc2/hcd_queue.c b/drivers/usb/dwc2/hcd_queue.c index b5c7793a2df2..13754353251f 100644 --- a/drivers/usb/dwc2/hcd_queue.c +++ b/drivers/usb/dwc2/hcd_queue.c @@ -367,7 +367,8 @@ static void pmap_unschedule(unsigned long *map, int bits_per_period, * @fmt: The format for printf. * @...: The args for printf. */ -static void cat_printf(char **buf, size_t *size, const char *fmt, ...) +static __printf(3, 4) +void cat_printf(char **buf, size_t *size, const char *fmt, ...) { va_list args; int i; From e43470dbdfe8922f9b2962184336efaa71e59727 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Fri, 24 Jun 2016 09:30:26 +0200 Subject: [PATCH 106/109] USB: dwc2-usb: add USB_GADGET dependency The driver selects NOP_USB_XCEIV, which can only be built-in if USB_GADGET is either disabled or also built-in, so with USB_DWC2_PCI=y and USB_GADGET=m, NOP_USB_XCEIV is also built-in and we get this link error: drivers/usb/built-in.o: In function `nop_set_peripheral': (text+0x1927c): undefined reference to `usb_gadget_vbus_connect' drivers/usb/built-in.o: In function `nop_gpio_vbus_thread': (text+0x197a0): undefined reference to `usb_gadget_vbus_connect' (text+0x19830): undefined reference to `usb_gadget_vbus_disconnect' This adds the same dependency for the dwc2 driver to avoid that broken configuration. Signed-off-by: Arnd Bergmann Acked-by: John Youn Signed-off-by: Felipe Balbi --- drivers/usb/dwc2/Kconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/usb/dwc2/Kconfig b/drivers/usb/dwc2/Kconfig index c1f29caa8990..e838701d6dd5 100644 --- a/drivers/usb/dwc2/Kconfig +++ b/drivers/usb/dwc2/Kconfig @@ -55,6 +55,7 @@ endchoice config USB_DWC2_PCI tristate "DWC2 PCI" depends on PCI + depends on USB_GADGET || !USB_GADGET default n select NOP_USB_XCEIV help From 44963d649da63ce8ed8f41b8a267c745ca1ec0b0 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Fri, 24 Jun 2016 15:23:16 +0300 Subject: [PATCH 107/109] usb: gadget: f_fs: check for allocation failure MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Return -ENOMEM if kmalloc() fails. Fixes: 9353afbbfa7b ('usb: gadget: f_fs: buffer data from ‘oversized’ OUT requests') Signed-off-by: Dan Carpenter Acked-by: Michal Nazarewicz Signed-off-by: Felipe Balbi --- drivers/usb/gadget/function/f_fs.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/usb/gadget/function/f_fs.c b/drivers/usb/gadget/function/f_fs.c index a91fcb0475b2..5c8429f23a89 100644 --- a/drivers/usb/gadget/function/f_fs.c +++ b/drivers/usb/gadget/function/f_fs.c @@ -775,6 +775,8 @@ static ssize_t __ffs_epfile_read_data(struct ffs_epfile *epfile, data_len -= ret; buf = kmalloc(sizeof(*buf) + data_len, GFP_KERNEL); + if (!buf) + return -ENOMEM; buf->length = data_len; buf->data = buf->storage; memcpy(buf->storage, data + ret, data_len); From 4fdef698383db07d829da567e0e405fc41ff3a89 Mon Sep 17 00:00:00 2001 From: Yoshihiro Shimoda Date: Wed, 8 Jun 2016 16:32:49 +0900 Subject: [PATCH 108/109] usb: renesas_usbhs: fix NULL pointer dereference in xfer_work() This patch fixes an issue that the xfer_work() is possible to cause NULL pointer dereference if the usb cable is disconnected while data transfer is running. In such case, a gadget driver may call usb_ep_disable()) before xfer_work() is actually called. In this case, the usbhs_pkt_pop() will call usbhsf_fifo_unselect(), and then usbhs_pipe_to_fifo() in xfer_work() will return NULL. Fixes: e73a989 ("usb: renesas_usbhs: add DMAEngine support") Cc: # v3.1+ Signed-off-by: Yoshihiro Shimoda Signed-off-by: Felipe Balbi --- drivers/usb/renesas_usbhs/fifo.c | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/drivers/usb/renesas_usbhs/fifo.c b/drivers/usb/renesas_usbhs/fifo.c index 7be4e7d57ace..280ed5ff021b 100644 --- a/drivers/usb/renesas_usbhs/fifo.c +++ b/drivers/usb/renesas_usbhs/fifo.c @@ -810,20 +810,27 @@ static void xfer_work(struct work_struct *work) { struct usbhs_pkt *pkt = container_of(work, struct usbhs_pkt, work); struct usbhs_pipe *pipe = pkt->pipe; - struct usbhs_fifo *fifo = usbhs_pipe_to_fifo(pipe); + struct usbhs_fifo *fifo; struct usbhs_priv *priv = usbhs_pipe_to_priv(pipe); struct dma_async_tx_descriptor *desc; - struct dma_chan *chan = usbhsf_dma_chan_get(fifo, pkt); + struct dma_chan *chan; struct device *dev = usbhs_priv_to_dev(priv); enum dma_transfer_direction dir; + unsigned long flags; + usbhs_lock(priv, flags); + fifo = usbhs_pipe_to_fifo(pipe); + if (!fifo) + goto xfer_work_end; + + chan = usbhsf_dma_chan_get(fifo, pkt); dir = usbhs_pipe_is_dir_in(pipe) ? DMA_DEV_TO_MEM : DMA_MEM_TO_DEV; desc = dmaengine_prep_slave_single(chan, pkt->dma + pkt->actual, pkt->trans, dir, DMA_PREP_INTERRUPT | DMA_CTRL_ACK); if (!desc) - return; + goto xfer_work_end; desc->callback = usbhsf_dma_complete; desc->callback_param = pipe; @@ -831,7 +838,7 @@ static void xfer_work(struct work_struct *work) pkt->cookie = dmaengine_submit(desc); if (pkt->cookie < 0) { dev_err(dev, "Failed to submit dma descriptor\n"); - return; + goto xfer_work_end; } dev_dbg(dev, " %s %d (%d/ %d)\n", @@ -842,6 +849,9 @@ static void xfer_work(struct work_struct *work) usbhs_pipe_set_trans_count_if_bulk(pipe, pkt->trans); dma_async_issue_pending(chan); usbhs_pipe_enable(pipe); + +xfer_work_end: + usbhs_unlock(priv, flags); } /* From 15e4292a2d21e9997fdb2b8c014cc461b3f268f0 Mon Sep 17 00:00:00 2001 From: Yoshihiro Shimoda Date: Wed, 8 Jun 2016 16:32:50 +0900 Subject: [PATCH 109/109] usb: renesas_usbhs: protect the CFIFOSEL setting in usbhsg_ep_enable() This patch fixes an issue that the CFIFOSEL register value is possible to be changed by usbhsg_ep_enable() wrongly. And then, a data transfer using CFIFO may not work correctly. For example: # modprobe g_multi file=usb-storage.bin # ifconfig usb0 192.168.1.1 up (During the USB host is sending file to the mass storage) # ifconfig usb0 down In this case, since the u_ether.c may call usb_ep_enable() in eth_stop(), if the renesas_usbhs driver is also using CFIFO for mass storage, the mass storage may not work correctly. So, this patch adds usbhs_lock() and usbhs_unlock() calling in usbhsg_ep_enable() to protect CFIFOSEL register. This is because: - CFIFOSEL.CURPIPE = 0 is also needed for the pipe configuration - The CFIFOSEL (fifo->sel) is already protected by usbhs_lock() Fixes: 97664a207bc2 ("usb: renesas_usbhs: shrink spin lock area") Cc: # v3.1+ Signed-off-by: Yoshihiro Shimoda Signed-off-by: Felipe Balbi --- drivers/usb/renesas_usbhs/mod_gadget.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/drivers/usb/renesas_usbhs/mod_gadget.c b/drivers/usb/renesas_usbhs/mod_gadget.c index 30345c2d01be..50f3363cc382 100644 --- a/drivers/usb/renesas_usbhs/mod_gadget.c +++ b/drivers/usb/renesas_usbhs/mod_gadget.c @@ -585,6 +585,9 @@ static int usbhsg_ep_enable(struct usb_ep *ep, struct usbhs_priv *priv = usbhsg_gpriv_to_priv(gpriv); struct usbhs_pipe *pipe; int ret = -EIO; + unsigned long flags; + + usbhs_lock(priv, flags); /* * if it already have pipe, @@ -593,7 +596,8 @@ static int usbhsg_ep_enable(struct usb_ep *ep, if (uep->pipe) { usbhs_pipe_clear(uep->pipe); usbhs_pipe_sequence_data0(uep->pipe); - return 0; + ret = 0; + goto usbhsg_ep_enable_end; } pipe = usbhs_pipe_malloc(priv, @@ -621,6 +625,9 @@ static int usbhsg_ep_enable(struct usb_ep *ep, ret = 0; } +usbhsg_ep_enable_end: + usbhs_unlock(priv, flags); + return ret; }