From fe5a6c48fd954489f51aab151d5dad9254c76419 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Thu, 12 May 2016 16:06:41 +0300 Subject: 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 diff --git a/drivers/usb/gadget/function/f_mass_storage.c b/drivers/usb/gadget/function/f_mass_storage.c index 5c6d4d7..2505117 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; -- cgit v0.10.2 From d8877fc7e7ec3eb4515a4e6b947599bdda058323 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Thu, 12 May 2016 15:02:29 +0300 Subject: 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 diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig index 2057add..3c3f31c 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 -- cgit v0.10.2 From 5185c91385d73cdf79836eb8548e4726e43ae831 Mon Sep 17 00:00:00 2001 From: Tim Harvey Date: Mon, 23 May 2016 06:58:41 -0700 Subject: 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 diff --git a/drivers/usb/gadget/udc/Kconfig b/drivers/usb/gadget/udc/Kconfig index 7c28941..658b8da 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 c894b94..614ab951 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 0d32052..2736a95 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 -- cgit v0.10.2 From d7be295243bbe53649986d869b398403eaa41bb7 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Wed, 4 May 2016 15:49:37 +0300 Subject: 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 diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index 07248ff..54e36b7 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,39 +1701,72 @@ static int dwc3_gadget_start(struct usb_gadget *g, dwc3_gadget_enable_irq(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); + __dwc3_gadget_ep_disable(dwc->eps[0]); err0: return ret; } -static int dwc3_gadget_stop(struct usb_gadget *g) +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; + +err1: + spin_unlock_irqrestore(&dwc->lock, flags); + 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]); +} - dwc->gadget_driver = NULL; +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); -- cgit v0.10.2 From 9f8a67b65a49d0e35c6ca782136c84541d948a64 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Wed, 4 May 2016 15:50:27 +0300 Subject: 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 diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h index 6540506..c881c82 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 54e36b7..ae55f50 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); - } - - __dwc3_gadget_ep_disable(dwc->eps[0]); - __dwc3_gadget_ep_disable(dwc->eps[1]); + ret = dwc3_gadget_run_stop(dwc, false, false); + if (ret < 0) + return ret; - 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; -- cgit v0.10.2 From 7f370ed0cfe9aa1520696c1c71e8a51e2c0bbcc1 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Mon, 9 May 2016 15:27:01 +0300 Subject: 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 diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c index a590cd2..245f4ff 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, }, }; -- cgit v0.10.2 From c4233573f6ee611033faa9116fc7003775a450b9 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Thu, 12 May 2016 14:08:34 +0300 Subject: 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 diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index ae55f50..236c231 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 -- cgit v0.10.2 From 4fae2e3e15157ac312b91234389e79f7a76667b3 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Thu, 12 May 2016 16:53:59 +0300 Subject: 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 diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index 236c231..f828820 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; - } - - /* - * 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); + starting = !(dep->flags & DWC3_EP_BUSY); - /* 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; -- cgit v0.10.2 From 6aff483295950abcf3be1a99f56f308bd6a1bab4 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Fri, 13 May 2016 10:07:47 +0300 Subject: 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 diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index f828820..8ca3855 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); -- cgit v0.10.2 From b43bba96b9036e42b2c2c71ee15e1f77b0b37aec Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Fri, 13 May 2016 10:11:59 +0300 Subject: 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 diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index 8ca3855..79d1882 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)) { -- cgit v0.10.2 From 6b74289937f624439c87135cfabb3deb2955fb53 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Fri, 13 May 2016 10:19:42 +0300 Subject: 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 diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index 79d1882..4c9fe7b 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); -- cgit v0.10.2 From 5ee85d890f8de5c6f1ab22ba13734a63fdf3ff2d Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Fri, 13 May 2016 12:42:44 +0300 Subject: 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 diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index 4c9fe7b..bff2c35 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) { - 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 (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 (last_one) - break; - } + if (!trbs_left) + return; } } -- cgit v0.10.2 From 4e99472bc10bda9906526d725ff6d5f27b4ddca1 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Fri, 13 May 2016 14:09:59 +0300 Subject: 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 diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h index c881c82..f4bec06 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 bff2c35..eb28326 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); -- cgit v0.10.2 From 2cd4718d0bbe1906fcf517f0b254fbd7c072383a Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Tue, 12 Apr 2016 16:42:43 +0300 Subject: 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 diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h index f4bec06..27afe14 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 51b52a7..e08150a 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 eb28326..5225f0e 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; -- cgit v0.10.2 From 2eb8801650b315394ca376a56be2971c867aa9ec Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Tue, 12 Apr 2016 16:53:39 +0300 Subject: 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 diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h index 27afe14..02990ab 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 b1dd3c6..89c26e0 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 e08150a..3103a8f 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 5225f0e..7ba4287 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 f21c0fc..e4a1d97 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 6a79c8e..a06f9a8 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 -- cgit v0.10.2 From bcdb3272e889f4d2a5c8efc5e12b0fb2dcbf75e9 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Mon, 16 May 2016 10:42:23 +0300 Subject: 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 diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c index 245f4ff..1f4ac35 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 02990ab..e01f637 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; -- cgit v0.10.2 From c499ff71ff2a281366c6ec7a904c547d806cbcd1 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Mon, 16 May 2016 10:49:01 +0300 Subject: 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 diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c index 1f4ac35..cbdefbb 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: - dwc3_event_buffers_cleanup(dwc); - -err4: - phy_power_off(dwc->usb3_generic_phy); - err3: - phy_power_off(dwc->usb2_generic_phy); + dwc3_event_buffers_cleanup(dwc); 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); -- cgit v0.10.2 From 51f5d49ad6f011ee380b866ea617fd90584189a2 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Mon, 16 May 2016 10:52:58 +0300 Subject: 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 diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c index cbdefbb..80e9aff 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 e01f637..2117295 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; -- cgit v0.10.2 From 3f308d17d7abbf35a6d40a7f16dc70cf43f12c98 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Mon, 16 May 2016 14:17:06 +0300 Subject: 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 diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h index 2117295..5148c56 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 7ba4287..378c14c 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; } -- cgit v0.10.2 From ab2a92e7a608c09f13baf1730b9ba24c73c35d52 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Tue, 17 May 2016 14:55:58 +0300 Subject: 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 diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index 378c14c..5e7b2ba 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) { -- cgit v0.10.2 From 9cad39fe4e4a4fe95d8ea5a7b0692b0a6e89e38b Mon Sep 17 00:00:00 2001 From: Konrad Leszczynski Date: Mon, 8 Feb 2016 16:13:12 +0100 Subject: 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 diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index 5e7b2ba..867adc9 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; } -- cgit v0.10.2 From 4cb4221764ef473cd36e1953f1fea11865786d65 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Wed, 18 May 2016 12:37:21 +0300 Subject: 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 diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index 867adc9..b59893c 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; -- cgit v0.10.2 From fc8bb91bc83ef82868533e75f5a11abc1158ec81 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Mon, 16 May 2016 13:14:48 +0300 Subject: 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 diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c index 80e9aff..bf1789f 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); - dwc3_free_event_buffers(dwc); - dwc3_free_scratch_buffers(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); + 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,18 +1134,14 @@ static int dwc3_suspend(struct device *dev) dwc3_core_exit(dwc); - pinctrl_pm_select_sleep_state(dev); - return 0; } -static int dwc3_resume(struct device *dev) +static int dwc3_resume_common(struct dwc3 *dwc) { - struct dwc3 *dwc = dev_get_drvdata(dev); + unsigned long flags; int ret; - pinctrl_pm_select_default_state(dev); - ret = dwc3_core_init(dwc); if (ret) return ret; @@ -1146,7 +1149,9 @@ static int dwc3_resume(struct device *dev) 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: @@ -1154,6 +1159,119 @@ static int dwc3_resume(struct device *dev) 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; +} + +static int dwc3_resume(struct device *dev) +{ + struct dwc3 *dwc = dev_get_drvdata(dev); + int ret; + + pinctrl_pm_select_default_state(dev); + + ret = dwc3_resume_common(dwc); + if (ret) + return ret; + 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 5148c56..94b9fd2 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 b59893c..1947751 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); + } +} -- cgit v0.10.2 From e9af9229098d92145d0b894b78878bbc6ac7c910 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Tue, 17 May 2016 10:15:02 +0300 Subject: 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 diff --git a/drivers/usb/dwc3/dwc3-pci.c b/drivers/usb/dwc3/dwc3-pci.c index 14196cd..a7b6a1c 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 "); -- cgit v0.10.2 From 74674cbf858ff6a83c3f06f4ab0ffa5d3c91bf0b Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Wed, 13 Apr 2016 16:44:39 +0300 Subject: 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 diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h index 94b9fd2..484bb5d 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 1947751..8b93203 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); -- cgit v0.10.2 From 22f2c619a116f120fe57de1225cfe7fdeb0b12d2 Mon Sep 17 00:00:00 2001 From: Janusz Dziedzic Date: Fri, 20 May 2016 10:37:13 +0200 Subject: 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 diff --git a/drivers/usb/dwc3/trace.h b/drivers/usb/dwc3/trace.h index 3ac7252..8cbe1fc 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; }) ) ); -- cgit v0.10.2 From 7ab373aadbd0463c0f020d368947b05e67a20bd5 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Mon, 23 May 2016 11:27:26 +0300 Subject: 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 diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index 8b93203..0581815 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; } -- cgit v0.10.2 From f75cacc468edc4826305909d6102d60fba55199f Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Mon, 23 May 2016 11:10:08 +0300 Subject: 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 diff --git a/drivers/usb/dwc3/debug.h b/drivers/usb/dwc3/debug.h index 71e3180..e3e0b41 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 3103a8f..54628c3 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 8cbe1fc..2389dd8 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, -- cgit v0.10.2 From ba1598410eff646e10be4e42d773e5bdc511d898 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Mon, 23 May 2016 13:50:29 +0300 Subject: 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 diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index 0581815..e553a7c 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: -- cgit v0.10.2 From f6bb225bb3ca7988ff373c62cc298e56cae1eee5 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Mon, 23 May 2016 13:53:34 +0300 Subject: 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 diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index e553a7c..25170fd 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)); -- cgit v0.10.2 From 0933df159c5c82f97c6bb811b149fa1158a26087 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Mon, 23 May 2016 14:02:33 +0300 Subject: 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 diff --git a/drivers/usb/dwc3/debug.h b/drivers/usb/dwc3/debug.h index e3e0b41..8eed4c7 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 25170fd..42eefd7 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 2389dd8..040f28b 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, -- cgit v0.10.2 From 0fe886cdb07aeaf75a61154f34abc8dd6860978e Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Mon, 23 May 2016 14:06:07 +0300 Subject: 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 diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index 42eefd7..f148b11 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); -- cgit v0.10.2 From 88811f7b722c417ececbc7f278c26d8df8606d02 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Mon, 23 May 2016 14:08:47 +0300 Subject: 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 diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index f148b11..609acd7 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) { -- cgit v0.10.2 From 71f7e7027028d5a8ef15dccc587dbd6c6b7f544f Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Mon, 23 May 2016 14:16:19 +0300 Subject: 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 diff --git a/drivers/usb/dwc3/debug.h b/drivers/usb/dwc3/debug.h index 8eed4c7..22dfc3d 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 609acd7..6a18b3d 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 040f28b..f43f9eb 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, -- cgit v0.10.2 From 958b9fa7f8cfd5799534e98ba3d05d96a5e7ccb9 Mon Sep 17 00:00:00 2001 From: John Youn Date: Mon, 23 May 2016 11:32:38 -0700 Subject: 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 diff --git a/drivers/usb/dwc3/ep0.c b/drivers/usb/dwc3/ep0.c index 54628c3..c814dde 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)) -- cgit v0.10.2 From 501058edebf957a3c101c8119c3286e496873840 Mon Sep 17 00:00:00 2001 From: John Youn Date: Mon, 23 May 2016 11:32:40 -0700 Subject: 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 diff --git a/drivers/usb/dwc3/ep0.c b/drivers/usb/dwc3/ep0.c index c814dde..93d6cdd 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; -- cgit v0.10.2 From d07fa665c79d85fead080f4b611c3f7645576454 Mon Sep 17 00:00:00 2001 From: John Youn Date: Mon, 23 May 2016 11:32:43 -0700 Subject: 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 diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index 6a18b3d..f38db3b 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) -- cgit v0.10.2 From 96bedb637a1e1ad8e7ed781fa020550aae2a8f16 Mon Sep 17 00:00:00 2001 From: John Youn Date: Mon, 23 May 2016 11:32:47 -0700 Subject: 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 diff --git a/drivers/usb/dwc3/trace.h b/drivers/usb/dwc3/trace.h index f43f9eb..cafd69f 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 ) ); -- cgit v0.10.2 From dca0119c3ab69fea2429ec2db7e3686614bd1439 Mon Sep 17 00:00:00 2001 From: John Youn Date: Thu, 19 May 2016 17:26:05 -0700 Subject: 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 diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index f38db3b..24bb942 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; } -static void dwc3_ep_inc_enq(struct dwc3_ep *dep) +/** + * 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) { - dep->trb_enqueue++; - dep->trb_enqueue %= DWC3_TRB_NUM; + (*index)++; + if (*index == (DWC3_TRB_NUM - 1)) + *index = 0; } -static void dwc3_ep_inc_deq(struct dwc3_ep *dep) +static void dwc3_ep_inc_enq(struct dwc3_ep *dep) { - dep->trb_dequeue++; - dep->trb_dequeue %= DWC3_TRB_NUM; + dwc3_ep_inc_trb(&dep->trb_enqueue); } -static int dwc3_ep_is_last_trb(unsigned int index) +static void dwc3_ep_inc_deq(struct dwc3_ep *dep) { - 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); -- cgit v0.10.2 From 0d25744ad107b6c9010153f3ebf8b59672b5b8e7 Mon Sep 17 00:00:00 2001 From: John Youn Date: Thu, 19 May 2016 17:26:08 -0700 Subject: 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 diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index 24bb942..6408f7f 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; -- cgit v0.10.2 From 89bc856e5a7462e47d90d5a3f5396bd5795efd0c Mon Sep 17 00:00:00 2001 From: John Youn Date: Thu, 19 May 2016 17:26:10 -0700 Subject: 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 diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index 6408f7f..663ea9e 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) -- cgit v0.10.2 From 32db3d9437b6bd560daeef82a8325436a4ac3366 Mon Sep 17 00:00:00 2001 From: John Youn Date: Thu, 19 May 2016 17:26:12 -0700 Subject: 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 diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index 663ea9e..4dea3e0 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, -- cgit v0.10.2 From 7d0a038b130cde7265d6bbc5e148734cb127f341 Mon Sep 17 00:00:00 2001 From: John Youn Date: Thu, 19 May 2016 17:26:15 -0700 Subject: 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 diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index 4dea3e0..3c4fd48 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; } -- cgit v0.10.2 From 361572b5f7a95b341a92d34b9bf41f71bbdae34d Mon Sep 17 00:00:00 2001 From: John Youn Date: Thu, 19 May 2016 17:26:17 -0700 Subject: 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 diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index 3c4fd48..fbc7968 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; } -- cgit v0.10.2 From 3de2685f0c395a56b909dbefd40fb287c9df31b2 Mon Sep 17 00:00:00 2001 From: John Youn Date: Mon, 23 May 2016 11:32:45 -0700 Subject: 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 diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index fbc7968..d0f7458 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--; -- cgit v0.10.2 From 2da9ad761e70c6a22e3a84f9a3e67faeef122a58 Mon Sep 17 00:00:00 2001 From: John Youn Date: Fri, 20 May 2016 16:34:26 -0700 Subject: 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 diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index d0f7458..0c11fd6 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); -- cgit v0.10.2 From e77c561432bccf5ea9df2f49aa039c015529590e Mon Sep 17 00:00:00 2001 From: John Youn Date: Fri, 20 May 2016 16:34:23 -0700 Subject: 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 diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h index 484bb5d..243e7a1 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; -- cgit v0.10.2 From 475c8beb35e129c2f33182f476373db04008892e Mon Sep 17 00:00:00 2001 From: William Wu Date: Fri, 13 May 2016 18:13:46 +0800 Subject: 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 diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h index 243e7a1..dcdba14 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 89c26e0..31926dd 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), -- cgit v0.10.2 From 3f586c92d87778bc3d550435a31f8ac685541121 Mon Sep 17 00:00:00 2001 From: Roger Quadros Date: Wed, 11 May 2016 17:36:42 +0300 Subject: 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 diff --git a/drivers/usb/dwc3/dwc3-omap.c b/drivers/usb/dwc3/dwc3-omap.c index af26449..0142544 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); -- cgit v0.10.2 From 12da8eae208d6f388c3aeecb91955c8b5a468654 Mon Sep 17 00:00:00 2001 From: Roger Quadros Date: Wed, 11 May 2016 17:36:43 +0300 Subject: 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 diff --git a/drivers/usb/dwc3/dwc3-omap.c b/drivers/usb/dwc3/dwc3-omap.c index 0142544..bde69fc 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", -- cgit v0.10.2 From 9ab330bf4dfd677a19d03359af9bc0e168a2c4b2 Mon Sep 17 00:00:00 2001 From: Roger Quadros Date: Wed, 11 May 2016 17:36:44 +0300 Subject: 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 diff --git a/drivers/usb/dwc3/dwc3-omap.c b/drivers/usb/dwc3/dwc3-omap.c index bde69fc..046bb37 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); -- cgit v0.10.2 From d2728fb3e01f9265571a5f7a5feeac4493d2a365 Mon Sep 17 00:00:00 2001 From: Roger Quadros Date: Wed, 11 May 2016 17:36:45 +0300 Subject: 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 diff --git a/drivers/usb/dwc3/dwc3-omap.c b/drivers/usb/dwc3/dwc3-omap.c index 046bb37..29e80cc 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; -- cgit v0.10.2 From 60cfb37ac9bc620334fff6e644a2b09019115939 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Tue, 24 May 2016 13:45:17 +0300 Subject: 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 diff --git a/drivers/usb/dwc3/ep0.c b/drivers/usb/dwc3/ep0.c index 93d6cdd..fe79d77 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 0c11fd6..61edb665 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; -- cgit v0.10.2 From cf48305de0c6b556ff6af64e93295cc54c4fe246 Mon Sep 17 00:00:00 2001 From: Heikki Krogerus Date: Fri, 22 Apr 2016 11:17:39 +0300 Subject: 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 diff --git a/drivers/usb/dwc3/dwc3-pci.c b/drivers/usb/dwc3/dwc3-pci.c index a7b6a1c..6bc4c2b 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; - - 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)); + 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"), + { }, + }; + + 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 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)); + 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"), + { }, + }; + + return platform_device_add_properties(dwc3, properties); } return 0; -- cgit v0.10.2 From d18e65470a4d9b6f7d7a72a0d102d009b45cf9d4 Mon Sep 17 00:00:00 2001 From: Heikki Krogerus Date: Fri, 22 Apr 2016 11:17:40 +0300 Subject: 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 diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c index bf1789f..9c4e1d8d 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 8826cca..0000000 --- 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; -}; -- cgit v0.10.2 From b6b1c6db4c7f04ffe3fd411baa9df31f4dd355dd Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Mon, 30 May 2016 13:29:35 +0300 Subject: 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 diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index 61edb665..e7e493b 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) { /* -- cgit v0.10.2 From 68d34c8a744d16ae449577a71c67b19a4b1100d0 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Mon, 30 May 2016 13:34:58 +0300 Subject: 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 diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h index dcdba14..45d6de5 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 e7e493b..1cd4b50 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 cafd69f..d24cefd 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', -- cgit v0.10.2 From 69450c4dc164d9ecbc0b54cb47a2ec80cde45da4 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Mon, 30 May 2016 13:37:02 +0300 Subject: 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 diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index 1cd4b50..a7c2548 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); -- cgit v0.10.2 From 55a0237f8f47957163125e20ee9260538c5c341c Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Mon, 30 May 2016 13:38:32 +0300 Subject: 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 diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index a7c2548..eea4127 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; -- cgit v0.10.2 From 13fa2e69b1dda31bddb11fe61f250b9415885ead Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Mon, 30 May 2016 13:40:00 +0300 Subject: usb: dwc3: gadget: disable XFER_NOT_READY We don't need this IRQ anymore for interrupt or bulk endpoints. Signed-off-by: Felipe Balbi diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index eea4127..126e8b8 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 -- cgit v0.10.2 From ba62c09d5cc240e55eb39e92d88f1036bb1d9221 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Mon, 30 May 2016 13:41:22 +0300 Subject: 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 diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index 126e8b8..3d2978c 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; } -- cgit v0.10.2 From d6dc2e76a860d6be0129daae43e5f12461531d20 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Mon, 30 May 2016 13:42:33 +0300 Subject: usb: dwc3: gadget: decrement trbs_left for each sg entry If we don't, we will overwrite valid TRBs. Signed-off-by: Felipe Balbi diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index 3d2978c..d2884a4 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) -- cgit v0.10.2 From 5a8d651a2bde01e00caf78496390d6ae46df80af Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Tue, 31 May 2016 13:07:47 +0300 Subject: 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 diff --git a/drivers/phy/Kconfig b/drivers/phy/Kconfig index b869b98..8d1cfb7 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 421770d..0f11a0f 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 e1b2dce..abf0138 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 d8f5674..2e710a4 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 c690474..b9c409a 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 fefe8b0..c6e1149 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. - */ -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. - */ +#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 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 */ /*-------------------------------------------------------------------------*/ -- cgit v0.10.2 From 5e42d710a108c23c104e083900d4ba9398e418b0 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Tue, 31 May 2016 13:39:21 +0300 Subject: 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 diff --git a/drivers/usb/gadget/udc/Makefile b/drivers/usb/gadget/udc/Makefile index dfee534..98e74ed 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/core.c b/drivers/usb/gadget/udc/core.c new file mode 100644 index 0000000..ff8685e --- /dev/null +++ b/drivers/usb/gadget/udc/core.c @@ -0,0 +1,1523 @@ +/** + * udc.c - Core UDC Framework + * + * Copyright (C) 2010 Texas Instruments + * 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 +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "trace.h" + +/** + * struct usb_udc - describes one usb device controller + * @driver - the gadget driver pointer. For use by the class code + * @dev - the child device to the actual controller + * @gadget - the gadget. For use by the class code + * @list - for use by the udc class driver + * @vbus - for udcs who care about vbus status, this value is real vbus status; + * for udcs who do not care about vbus status, this value is always true + * + * This represents the internal data structure which is used by the UDC-class + * to hold information about udc driver and gadget together. + */ +struct usb_udc { + struct usb_gadget_driver *driver; + struct usb_gadget *gadget; + struct device dev; + struct list_head list; + bool vbus; +}; + +static struct class *udc_class; +static LIST_HEAD(udc_list); +static LIST_HEAD(gadget_driver_pending_list); +static DEFINE_MUTEX(udc_lock); + +static int udc_bind_to_driver(struct usb_udc *udc, + struct usb_gadget_driver *driver); + +/* ------------------------------------------------------------------------- */ + +/** + * 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; + + trace_usb_ep_set_maxpacket_limit(ep, 0); +} +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 = 0; + + if (ep->enabled) + goto out; + + ret = ep->ops->enable(ep, ep->desc); + if (ret) { + ret = ret; + goto out; + } + + ep->enabled = true; + +out: + trace_usb_ep_enable(ep, ret); + + return ret; +} +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 = 0; + + if (!ep->enabled) + goto out; + + ret = ep->ops->disable(ep); + if (ret) { + ret = ret; + goto out; + } + + ep->enabled = false; + +out: + trace_usb_ep_disable(ep, ret); + + return ret; +} +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) +{ + 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); + +/** + * 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); + trace_usb_ep_free_request(ep, req, 0); +} +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) +{ + int ret = 0; + + 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); + +/** + * 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) +{ + int ret; + + ret = ep->ops->dequeue(ep, req); + trace_usb_ep_dequeue(ep, req, ret); + + return ret; +} +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) +{ + 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); + +/** + * 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) +{ + 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); + +/** + * 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) +{ + int ret; + + if (ep->ops->set_wedge) + ret = ep->ops->set_wedge(ep); + else + ret = ep->ops->set_halt(ep, 1); + + trace_usb_ep_set_wedge(ep, ret); + + return ret; +} +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) +{ + int ret; + + if (ep->ops->fifo_status) + ret = ep->ops->fifo_status(ep); + else + ret = -EOPNOTSUPP; + + trace_usb_ep_fifo_status(ep, ret); + + return ret; +} +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); + + trace_usb_ep_fifo_flush(ep, 0); +} +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) +{ + int ret; + + ret = gadget->ops->get_frame(gadget); + + trace_usb_gadget_frame_number(gadget, ret); + + return ret; +} +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) +{ + 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); + +/** + * 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) +{ + 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); + +/** + * 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) +{ + 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); + +/** + * 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) +{ + 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); + +/** + * 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) +{ + 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); + +/** + * 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) +{ + 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); + +/** + * 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 = 0; + + if (!gadget->ops->pullup) { + ret = -EOPNOTSUPP; + goto out; + } + + if (gadget->deactivated) { + /* + * If gadget is deactivated we only save new state. + * Gadget will be connected automatically after activation. + */ + gadget->connected = true; + 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); + +/** + * 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 = 0; + + if (!gadget->ops->pullup) { + ret = -EOPNOTSUPP; + goto out; + } + + if (gadget->deactivated) { + /* + * If gadget is deactivated we only save new state. + * Gadget will stay disconnected after activation. + */ + gadget->connected = false; + 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); + +/** + * 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 = 0; + + if (gadget->deactivated) + goto out; + + if (gadget->connected) { + ret = usb_gadget_disconnect(gadget); + if (ret) + goto out; + + /* + * If gadget was being connected before deactivation, we want + * to reconnect it in usb_gadget_activate(). + */ + gadget->connected = true; + } + gadget->deactivated = true; + +out: + trace_usb_gadget_deactivate(gadget, ret); + + return ret; +} +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) +{ + int ret = 0; + + if (!gadget->deactivated) + goto out; + + 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) + ret = usb_gadget_connect(gadget); + +out: + trace_usb_gadget_activate(gadget, ret); + + return ret; +} +EXPORT_SYMBOL_GPL(usb_gadget_activate); + +/* ------------------------------------------------------------------------- */ + +#ifdef CONFIG_HAS_DMA + +int usb_gadget_map_request_by_dev(struct device *dev, + struct usb_request *req, int is_in) +{ + if (req->length == 0) + return 0; + + if (req->num_sgs) { + int mapped; + + mapped = dma_map_sg(dev, req->sg, req->num_sgs, + is_in ? DMA_TO_DEVICE : DMA_FROM_DEVICE); + if (mapped == 0) { + dev_err(dev, "failed to map SGs\n"); + return -EFAULT; + } + + req->num_mapped_sgs = mapped; + } else { + req->dma = dma_map_single(dev, req->buf, req->length, + is_in ? DMA_TO_DEVICE : DMA_FROM_DEVICE); + + if (dma_mapping_error(dev, req->dma)) { + dev_err(dev, "failed to map buffer\n"); + return -EFAULT; + } + } + + return 0; +} +EXPORT_SYMBOL_GPL(usb_gadget_map_request_by_dev); + +int usb_gadget_map_request(struct usb_gadget *gadget, + struct usb_request *req, int is_in) +{ + return usb_gadget_map_request_by_dev(gadget->dev.parent, req, is_in); +} +EXPORT_SYMBOL_GPL(usb_gadget_map_request); + +void usb_gadget_unmap_request_by_dev(struct device *dev, + struct usb_request *req, int is_in) +{ + if (req->length == 0) + return; + + if (req->num_mapped_sgs) { + dma_unmap_sg(dev, req->sg, req->num_mapped_sgs, + is_in ? DMA_TO_DEVICE : DMA_FROM_DEVICE); + + req->num_mapped_sgs = 0; + } else { + dma_unmap_single(dev, req->dma, req->length, + is_in ? DMA_TO_DEVICE : DMA_FROM_DEVICE); + } +} +EXPORT_SYMBOL_GPL(usb_gadget_unmap_request_by_dev); + +void usb_gadget_unmap_request(struct usb_gadget *gadget, + struct usb_request *req, int is_in) +{ + usb_gadget_unmap_request_by_dev(gadget->dev.parent, req, is_in); +} +EXPORT_SYMBOL_GPL(usb_gadget_unmap_request); + +#endif /* CONFIG_HAS_DMA */ + +/* ------------------------------------------------------------------------- */ + +/** + * usb_gadget_giveback_request - give the request back to the gadget layer + * Context: in_interrupt() + * + * This is called by device controller drivers in order to return the + * completed request back to the gadget layer. + */ +void usb_gadget_giveback_request(struct usb_ep *ep, + struct usb_request *req) +{ + 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); + +/* ------------------------------------------------------------------------- */ + +/** + * gadget_find_ep_by_name - returns ep whose name is the same as sting passed + * in second parameter or NULL if searched endpoint not found + * @g: controller to check for quirk + * @name: name of searched endpoint + */ +struct usb_ep *gadget_find_ep_by_name(struct usb_gadget *g, const char *name) +{ + struct usb_ep *ep; + + gadget_for_each_ep(ep, g) { + if (!strcmp(ep->name, name)) + return ep; + } + + return NULL; +} +EXPORT_SYMBOL_GPL(gadget_find_ep_by_name); + +/* ------------------------------------------------------------------------- */ + +int usb_gadget_ep_match_desc(struct usb_gadget *gadget, + struct usb_ep *ep, struct usb_endpoint_descriptor *desc, + struct usb_ss_ep_comp_descriptor *ep_comp) +{ + u8 type; + u16 max; + int num_req_streams = 0; + + /* endpoint already claimed? */ + if (ep->claimed) + return 0; + + type = usb_endpoint_type(desc); + max = 0x7ff & usb_endpoint_maxp(desc); + + if (usb_endpoint_dir_in(desc) && !ep->caps.dir_in) + return 0; + if (usb_endpoint_dir_out(desc) && !ep->caps.dir_out) + return 0; + + if (max > ep->maxpacket_limit) + return 0; + + /* "high bandwidth" works only at high speed */ + if (!gadget_is_dualspeed(gadget) && usb_endpoint_maxp(desc) & (3<<11)) + return 0; + + switch (type) { + case USB_ENDPOINT_XFER_CONTROL: + /* only support ep0 for portable CONTROL traffic */ + return 0; + case USB_ENDPOINT_XFER_ISOC: + if (!ep->caps.type_iso) + return 0; + /* ISO: limit 1023 bytes full speed, 1024 high/super speed */ + if (!gadget_is_dualspeed(gadget) && max > 1023) + return 0; + break; + case USB_ENDPOINT_XFER_BULK: + if (!ep->caps.type_bulk) + return 0; + if (ep_comp && gadget_is_superspeed(gadget)) { + /* Get the number of required streams from the + * EP companion descriptor and see if the EP + * matches it + */ + num_req_streams = ep_comp->bmAttributes & 0x1f; + if (num_req_streams > ep->max_streams) + return 0; + } + break; + case USB_ENDPOINT_XFER_INT: + /* Bulk endpoints handle interrupt transfers, + * except the toggle-quirky iso-synch kind + */ + if (!ep->caps.type_int && !ep->caps.type_bulk) + return 0; + /* INT: limit 64 bytes full speed, 1024 high/super speed */ + if (!gadget_is_dualspeed(gadget) && max > 64) + return 0; + break; + } + + return 1; +} +EXPORT_SYMBOL_GPL(usb_gadget_ep_match_desc); + +/* ------------------------------------------------------------------------- */ + +static void usb_gadget_state_work(struct work_struct *work) +{ + struct usb_gadget *gadget = work_to_gadget(work); + struct usb_udc *udc = gadget->udc; + + if (udc) + sysfs_notify(&udc->dev.kobj, NULL, "state"); +} + +void usb_gadget_set_state(struct usb_gadget *gadget, + enum usb_device_state state) +{ + gadget->state = state; + schedule_work(&gadget->work); +} +EXPORT_SYMBOL_GPL(usb_gadget_set_state); + +/* ------------------------------------------------------------------------- */ + +static void usb_udc_connect_control(struct usb_udc *udc) +{ + if (udc->vbus) + usb_gadget_connect(udc->gadget); + else + usb_gadget_disconnect(udc->gadget); +} + +/** + * usb_udc_vbus_handler - updates the udc core vbus status, and try to + * connect or disconnect gadget + * @gadget: The gadget which vbus change occurs + * @status: The vbus status + * + * The udc driver calls it when it wants to connect or disconnect gadget + * according to vbus status. + */ +void usb_udc_vbus_handler(struct usb_gadget *gadget, bool status) +{ + struct usb_udc *udc = gadget->udc; + + if (udc) { + udc->vbus = status; + usb_udc_connect_control(udc); + } +} +EXPORT_SYMBOL_GPL(usb_udc_vbus_handler); + +/** + * usb_gadget_udc_reset - notifies the udc core that bus reset occurs + * @gadget: The gadget which bus reset occurs + * @driver: The gadget driver we want to notify + * + * If the udc driver has bus reset handler, it needs to call this when the bus + * reset occurs, it notifies the gadget driver that the bus reset occurs as + * well as updates gadget state. + */ +void usb_gadget_udc_reset(struct usb_gadget *gadget, + struct usb_gadget_driver *driver) +{ + driver->reset(gadget); + usb_gadget_set_state(gadget, USB_STATE_DEFAULT); +} +EXPORT_SYMBOL_GPL(usb_gadget_udc_reset); + +/** + * usb_gadget_udc_start - tells usb device controller to start up + * @udc: The UDC to be started + * + * This call is issued by the UDC Class driver when it's about + * to register a gadget driver to the device controller, before + * calling gadget driver's bind() method. + * + * It allows the controller to be powered off until strictly + * necessary to have it powered on. + * + * Returns zero on success, else negative errno. + */ +static inline int usb_gadget_udc_start(struct usb_udc *udc) +{ + return udc->gadget->ops->udc_start(udc->gadget, udc->driver); +} + +/** + * usb_gadget_udc_stop - tells usb device controller we don't need it anymore + * @gadget: The device we want to stop activity + * @driver: The driver to unbind from @gadget + * + * This call is issued by the UDC Class driver after calling + * gadget driver's unbind() method. + * + * The details are implementation specific, but it can go as + * far as powering off UDC completely and disable its data + * line pullups. + */ +static inline void usb_gadget_udc_stop(struct usb_udc *udc) +{ + udc->gadget->ops->udc_stop(udc->gadget); +} + +/** + * usb_udc_release - release the usb_udc struct + * @dev: the dev member within usb_udc + * + * This is called by driver's core in order to free memory once the last + * reference is released. + */ +static void usb_udc_release(struct device *dev) +{ + struct usb_udc *udc; + + udc = container_of(dev, struct usb_udc, dev); + dev_dbg(dev, "releasing '%s'\n", dev_name(dev)); + kfree(udc); +} + +static const struct attribute_group *usb_udc_attr_groups[]; + +static void usb_udc_nop_release(struct device *dev) +{ + dev_vdbg(dev, "%s\n", __func__); +} + +/** + * usb_add_gadget_udc_release - adds a new gadget to the udc class driver list + * @parent: the parent device to this udc. Usually the controller driver's + * device. + * @gadget: the gadget to be added to the list. + * @release: a gadget release function. + * + * Returns zero on success, negative errno otherwise. + */ +int usb_add_gadget_udc_release(struct device *parent, struct usb_gadget *gadget, + void (*release)(struct device *dev)) +{ + struct usb_udc *udc; + struct usb_gadget_driver *driver; + int ret = -ENOMEM; + + udc = kzalloc(sizeof(*udc), GFP_KERNEL); + if (!udc) + goto err1; + + dev_set_name(&gadget->dev, "gadget"); + INIT_WORK(&gadget->work, usb_gadget_state_work); + gadget->dev.parent = parent; + + if (release) + gadget->dev.release = release; + else + gadget->dev.release = usb_udc_nop_release; + + ret = device_register(&gadget->dev); + if (ret) + goto err2; + + device_initialize(&udc->dev); + udc->dev.release = usb_udc_release; + udc->dev.class = udc_class; + udc->dev.groups = usb_udc_attr_groups; + udc->dev.parent = parent; + ret = dev_set_name(&udc->dev, "%s", kobject_name(&parent->kobj)); + if (ret) + goto err3; + + udc->gadget = gadget; + gadget->udc = udc; + + mutex_lock(&udc_lock); + list_add_tail(&udc->list, &udc_list); + + ret = device_add(&udc->dev); + if (ret) + goto err4; + + usb_gadget_set_state(gadget, USB_STATE_NOTATTACHED); + udc->vbus = true; + + /* pick up one of pending gadget drivers */ + list_for_each_entry(driver, &gadget_driver_pending_list, pending) { + if (!driver->udc_name || strcmp(driver->udc_name, + dev_name(&udc->dev)) == 0) { + ret = udc_bind_to_driver(udc, driver); + if (ret != -EPROBE_DEFER) + list_del(&driver->pending); + if (ret) + goto err4; + break; + } + } + + mutex_unlock(&udc_lock); + + return 0; + +err4: + list_del(&udc->list); + mutex_unlock(&udc_lock); + +err3: + put_device(&udc->dev); + device_del(&gadget->dev); + +err2: + put_device(&gadget->dev); + kfree(udc); + +err1: + return ret; +} +EXPORT_SYMBOL_GPL(usb_add_gadget_udc_release); + +/** + * usb_get_gadget_udc_name - get the name of the first UDC controller + * This functions returns the name of the first UDC controller in the system. + * Please note that this interface is usefull only for legacy drivers which + * assume that there is only one UDC controller in the system and they need to + * get its name before initialization. There is no guarantee that the UDC + * of the returned name will be still available, when gadget driver registers + * itself. + * + * Returns pointer to string with UDC controller name on success, NULL + * otherwise. Caller should kfree() returned string. + */ +char *usb_get_gadget_udc_name(void) +{ + struct usb_udc *udc; + char *name = NULL; + + /* For now we take the first available UDC */ + mutex_lock(&udc_lock); + list_for_each_entry(udc, &udc_list, list) { + if (!udc->driver) { + name = kstrdup(udc->gadget->name, GFP_KERNEL); + break; + } + } + mutex_unlock(&udc_lock); + return name; +} +EXPORT_SYMBOL_GPL(usb_get_gadget_udc_name); + +/** + * usb_add_gadget_udc - adds a new gadget to the udc class driver list + * @parent: the parent device to this udc. Usually the controller + * driver's device. + * @gadget: the gadget to be added to the list + * + * Returns zero on success, negative errno otherwise. + */ +int usb_add_gadget_udc(struct device *parent, struct usb_gadget *gadget) +{ + return usb_add_gadget_udc_release(parent, gadget, NULL); +} +EXPORT_SYMBOL_GPL(usb_add_gadget_udc); + +static void usb_gadget_remove_driver(struct usb_udc *udc) +{ + dev_dbg(&udc->dev, "unregistering UDC driver [%s]\n", + udc->driver->function); + + kobject_uevent(&udc->dev.kobj, KOBJ_CHANGE); + + usb_gadget_disconnect(udc->gadget); + udc->driver->disconnect(udc->gadget); + udc->driver->unbind(udc->gadget); + usb_gadget_udc_stop(udc); + + udc->driver = NULL; + udc->dev.driver = NULL; + udc->gadget->dev.driver = NULL; +} + +/** + * usb_del_gadget_udc - deletes @udc from udc_list + * @gadget: the gadget to be removed. + * + * This, will call usb_gadget_unregister_driver() if + * the @udc is still busy. + */ +void usb_del_gadget_udc(struct usb_gadget *gadget) +{ + struct usb_udc *udc = gadget->udc; + + if (!udc) + return; + + dev_vdbg(gadget->dev.parent, "unregistering gadget\n"); + + mutex_lock(&udc_lock); + list_del(&udc->list); + + if (udc->driver) { + struct usb_gadget_driver *driver = udc->driver; + + usb_gadget_remove_driver(udc); + list_add(&driver->pending, &gadget_driver_pending_list); + } + mutex_unlock(&udc_lock); + + kobject_uevent(&udc->dev.kobj, KOBJ_REMOVE); + flush_work(&gadget->work); + device_unregister(&udc->dev); + device_unregister(&gadget->dev); +} +EXPORT_SYMBOL_GPL(usb_del_gadget_udc); + +/* ------------------------------------------------------------------------- */ + +static int udc_bind_to_driver(struct usb_udc *udc, struct usb_gadget_driver *driver) +{ + int ret; + + dev_dbg(&udc->dev, "registering UDC driver [%s]\n", + driver->function); + + udc->driver = driver; + udc->dev.driver = &driver->driver; + udc->gadget->dev.driver = &driver->driver; + + ret = driver->bind(udc->gadget, driver); + if (ret) + goto err1; + ret = usb_gadget_udc_start(udc); + if (ret) { + driver->unbind(udc->gadget); + goto err1; + } + usb_udc_connect_control(udc); + + kobject_uevent(&udc->dev.kobj, KOBJ_CHANGE); + return 0; +err1: + if (ret != -EISNAM) + dev_err(&udc->dev, "failed to start %s: %d\n", + udc->driver->function, ret); + udc->driver = NULL; + udc->dev.driver = NULL; + udc->gadget->dev.driver = NULL; + return ret; +} + +int usb_gadget_probe_driver(struct usb_gadget_driver *driver) +{ + struct usb_udc *udc = NULL; + int ret = -ENODEV; + + if (!driver || !driver->bind || !driver->setup) + return -EINVAL; + + mutex_lock(&udc_lock); + if (driver->udc_name) { + list_for_each_entry(udc, &udc_list, list) { + ret = strcmp(driver->udc_name, dev_name(&udc->dev)); + if (!ret) + break; + } + if (!ret && !udc->driver) + goto found; + } else { + list_for_each_entry(udc, &udc_list, list) { + /* For now we take the first one */ + if (!udc->driver) + goto found; + } + } + + if (!driver->match_existing_only) { + list_add_tail(&driver->pending, &gadget_driver_pending_list); + pr_info("udc-core: couldn't find an available UDC - added [%s] to list of pending drivers\n", + driver->function); + ret = 0; + } + + mutex_unlock(&udc_lock); + return ret; +found: + ret = udc_bind_to_driver(udc, driver); + mutex_unlock(&udc_lock); + return ret; +} +EXPORT_SYMBOL_GPL(usb_gadget_probe_driver); + +int usb_gadget_unregister_driver(struct usb_gadget_driver *driver) +{ + struct usb_udc *udc = NULL; + int ret = -ENODEV; + + if (!driver || !driver->unbind) + return -EINVAL; + + mutex_lock(&udc_lock); + list_for_each_entry(udc, &udc_list, list) + if (udc->driver == driver) { + usb_gadget_remove_driver(udc); + usb_gadget_set_state(udc->gadget, + USB_STATE_NOTATTACHED); + ret = 0; + break; + } + + if (ret) { + list_del(&driver->pending); + ret = 0; + } + mutex_unlock(&udc_lock); + return ret; +} +EXPORT_SYMBOL_GPL(usb_gadget_unregister_driver); + +/* ------------------------------------------------------------------------- */ + +static ssize_t usb_udc_srp_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t n) +{ + struct usb_udc *udc = container_of(dev, struct usb_udc, dev); + + if (sysfs_streq(buf, "1")) + usb_gadget_wakeup(udc->gadget); + + return n; +} +static DEVICE_ATTR(srp, S_IWUSR, NULL, usb_udc_srp_store); + +static ssize_t usb_udc_softconn_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t n) +{ + struct usb_udc *udc = container_of(dev, struct usb_udc, dev); + + if (!udc->driver) { + dev_err(dev, "soft-connect without a gadget driver\n"); + return -EOPNOTSUPP; + } + + if (sysfs_streq(buf, "connect")) { + usb_gadget_udc_start(udc); + usb_gadget_connect(udc->gadget); + } else if (sysfs_streq(buf, "disconnect")) { + usb_gadget_disconnect(udc->gadget); + udc->driver->disconnect(udc->gadget); + usb_gadget_udc_stop(udc); + } else { + dev_err(dev, "unsupported command '%s'\n", buf); + return -EINVAL; + } + + return n; +} +static DEVICE_ATTR(soft_connect, S_IWUSR, NULL, usb_udc_softconn_store); + +static ssize_t state_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct usb_udc *udc = container_of(dev, struct usb_udc, dev); + struct usb_gadget *gadget = udc->gadget; + + return sprintf(buf, "%s\n", usb_state_string(gadget->state)); +} +static DEVICE_ATTR_RO(state); + +#define USB_UDC_SPEED_ATTR(name, param) \ +ssize_t name##_show(struct device *dev, \ + struct device_attribute *attr, char *buf) \ +{ \ + struct usb_udc *udc = container_of(dev, struct usb_udc, dev); \ + return snprintf(buf, PAGE_SIZE, "%s\n", \ + usb_speed_string(udc->gadget->param)); \ +} \ +static DEVICE_ATTR_RO(name) + +static USB_UDC_SPEED_ATTR(current_speed, speed); +static USB_UDC_SPEED_ATTR(maximum_speed, max_speed); + +#define USB_UDC_ATTR(name) \ +ssize_t name##_show(struct device *dev, \ + struct device_attribute *attr, char *buf) \ +{ \ + struct usb_udc *udc = container_of(dev, struct usb_udc, dev); \ + struct usb_gadget *gadget = udc->gadget; \ + \ + return snprintf(buf, PAGE_SIZE, "%d\n", gadget->name); \ +} \ +static DEVICE_ATTR_RO(name) + +static USB_UDC_ATTR(is_otg); +static USB_UDC_ATTR(is_a_peripheral); +static USB_UDC_ATTR(b_hnp_enable); +static USB_UDC_ATTR(a_hnp_support); +static USB_UDC_ATTR(a_alt_hnp_support); +static USB_UDC_ATTR(is_selfpowered); + +static struct attribute *usb_udc_attrs[] = { + &dev_attr_srp.attr, + &dev_attr_soft_connect.attr, + &dev_attr_state.attr, + &dev_attr_current_speed.attr, + &dev_attr_maximum_speed.attr, + + &dev_attr_is_otg.attr, + &dev_attr_is_a_peripheral.attr, + &dev_attr_b_hnp_enable.attr, + &dev_attr_a_hnp_support.attr, + &dev_attr_a_alt_hnp_support.attr, + &dev_attr_is_selfpowered.attr, + NULL, +}; + +static const struct attribute_group usb_udc_attr_group = { + .attrs = usb_udc_attrs, +}; + +static const struct attribute_group *usb_udc_attr_groups[] = { + &usb_udc_attr_group, + NULL, +}; + +static int usb_udc_uevent(struct device *dev, struct kobj_uevent_env *env) +{ + struct usb_udc *udc = container_of(dev, struct usb_udc, dev); + int ret; + + ret = add_uevent_var(env, "USB_UDC_NAME=%s", udc->gadget->name); + if (ret) { + dev_err(dev, "failed to add uevent USB_UDC_NAME\n"); + return ret; + } + + if (udc->driver) { + ret = add_uevent_var(env, "USB_UDC_DRIVER=%s", + udc->driver->function); + if (ret) { + dev_err(dev, "failed to add uevent USB_UDC_DRIVER\n"); + return ret; + } + } + + return 0; +} + +static int __init usb_udc_init(void) +{ + udc_class = class_create(THIS_MODULE, "udc"); + if (IS_ERR(udc_class)) { + pr_err("failed to create udc class --> %ld\n", + PTR_ERR(udc_class)); + return PTR_ERR(udc_class); + } + + udc_class->dev_uevent = usb_udc_uevent; + return 0; +} +subsys_initcall(usb_udc_init); + +static void __exit usb_udc_exit(void) +{ + class_destroy(udc_class); +} +module_exit(usb_udc_exit); + +MODULE_DESCRIPTION("UDC Framework"); +MODULE_AUTHOR("Felipe Balbi "); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/usb/gadget/udc/trace.c b/drivers/usb/gadget/udc/trace.c new file mode 100644 index 0000000..8c551ab --- /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 0000000..da29874 --- /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/drivers/usb/gadget/udc/udc-core.c b/drivers/usb/gadget/udc/udc-core.c deleted file mode 100644 index abf0138..0000000 --- a/drivers/usb/gadget/udc/udc-core.c +++ /dev/null @@ -1,1373 +0,0 @@ -/** - * udc.c - Core UDC Framework - * - * Copyright (C) 2010 Texas Instruments - * 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 -#include -#include -#include -#include -#include - -#include -#include -#include - -/** - * struct usb_udc - describes one usb device controller - * @driver - the gadget driver pointer. For use by the class code - * @dev - the child device to the actual controller - * @gadget - the gadget. For use by the class code - * @list - for use by the udc class driver - * @vbus - for udcs who care about vbus status, this value is real vbus status; - * for udcs who do not care about vbus status, this value is always true - * - * This represents the internal data structure which is used by the UDC-class - * to hold information about udc driver and gadget together. - */ -struct usb_udc { - struct usb_gadget_driver *driver; - struct usb_gadget *gadget; - struct device dev; - struct list_head list; - bool vbus; -}; - -static struct class *udc_class; -static LIST_HEAD(udc_list); -static LIST_HEAD(gadget_driver_pending_list); -static DEFINE_MUTEX(udc_lock); - -static int udc_bind_to_driver(struct usb_udc *udc, - struct usb_gadget_driver *driver); - -/* ------------------------------------------------------------------------- */ - -/** - * 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, - struct usb_request *req, int is_in) -{ - if (req->length == 0) - return 0; - - if (req->num_sgs) { - int mapped; - - mapped = dma_map_sg(dev, req->sg, req->num_sgs, - is_in ? DMA_TO_DEVICE : DMA_FROM_DEVICE); - if (mapped == 0) { - dev_err(dev, "failed to map SGs\n"); - return -EFAULT; - } - - req->num_mapped_sgs = mapped; - } else { - req->dma = dma_map_single(dev, req->buf, req->length, - is_in ? DMA_TO_DEVICE : DMA_FROM_DEVICE); - - if (dma_mapping_error(dev, req->dma)) { - dev_err(dev, "failed to map buffer\n"); - return -EFAULT; - } - } - - return 0; -} -EXPORT_SYMBOL_GPL(usb_gadget_map_request_by_dev); - -int usb_gadget_map_request(struct usb_gadget *gadget, - struct usb_request *req, int is_in) -{ - return usb_gadget_map_request_by_dev(gadget->dev.parent, req, is_in); -} -EXPORT_SYMBOL_GPL(usb_gadget_map_request); - -void usb_gadget_unmap_request_by_dev(struct device *dev, - struct usb_request *req, int is_in) -{ - if (req->length == 0) - return; - - if (req->num_mapped_sgs) { - dma_unmap_sg(dev, req->sg, req->num_mapped_sgs, - is_in ? DMA_TO_DEVICE : DMA_FROM_DEVICE); - - req->num_mapped_sgs = 0; - } else { - dma_unmap_single(dev, req->dma, req->length, - is_in ? DMA_TO_DEVICE : DMA_FROM_DEVICE); - } -} -EXPORT_SYMBOL_GPL(usb_gadget_unmap_request_by_dev); - -void usb_gadget_unmap_request(struct usb_gadget *gadget, - struct usb_request *req, int is_in) -{ - usb_gadget_unmap_request_by_dev(gadget->dev.parent, req, is_in); -} -EXPORT_SYMBOL_GPL(usb_gadget_unmap_request); - -#endif /* CONFIG_HAS_DMA */ - -/* ------------------------------------------------------------------------- */ - -/** - * usb_gadget_giveback_request - give the request back to the gadget layer - * Context: in_interrupt() - * - * This is called by device controller drivers in order to return the - * completed request back to the gadget layer. - */ -void usb_gadget_giveback_request(struct usb_ep *ep, - struct usb_request *req) -{ - if (likely(req->status == 0)) - usb_led_activity(USB_LED_EVENT_GADGET); - - req->complete(ep, req); -} -EXPORT_SYMBOL_GPL(usb_gadget_giveback_request); - -/* ------------------------------------------------------------------------- */ - -/** - * gadget_find_ep_by_name - returns ep whose name is the same as sting passed - * in second parameter or NULL if searched endpoint not found - * @g: controller to check for quirk - * @name: name of searched endpoint - */ -struct usb_ep *gadget_find_ep_by_name(struct usb_gadget *g, const char *name) -{ - struct usb_ep *ep; - - gadget_for_each_ep(ep, g) { - if (!strcmp(ep->name, name)) - return ep; - } - - return NULL; -} -EXPORT_SYMBOL_GPL(gadget_find_ep_by_name); - -/* ------------------------------------------------------------------------- */ - -int usb_gadget_ep_match_desc(struct usb_gadget *gadget, - struct usb_ep *ep, struct usb_endpoint_descriptor *desc, - struct usb_ss_ep_comp_descriptor *ep_comp) -{ - u8 type; - u16 max; - int num_req_streams = 0; - - /* endpoint already claimed? */ - if (ep->claimed) - return 0; - - type = usb_endpoint_type(desc); - max = 0x7ff & usb_endpoint_maxp(desc); - - if (usb_endpoint_dir_in(desc) && !ep->caps.dir_in) - return 0; - if (usb_endpoint_dir_out(desc) && !ep->caps.dir_out) - return 0; - - if (max > ep->maxpacket_limit) - return 0; - - /* "high bandwidth" works only at high speed */ - if (!gadget_is_dualspeed(gadget) && usb_endpoint_maxp(desc) & (3<<11)) - return 0; - - switch (type) { - case USB_ENDPOINT_XFER_CONTROL: - /* only support ep0 for portable CONTROL traffic */ - return 0; - case USB_ENDPOINT_XFER_ISOC: - if (!ep->caps.type_iso) - return 0; - /* ISO: limit 1023 bytes full speed, 1024 high/super speed */ - if (!gadget_is_dualspeed(gadget) && max > 1023) - return 0; - break; - case USB_ENDPOINT_XFER_BULK: - if (!ep->caps.type_bulk) - return 0; - if (ep_comp && gadget_is_superspeed(gadget)) { - /* Get the number of required streams from the - * EP companion descriptor and see if the EP - * matches it - */ - num_req_streams = ep_comp->bmAttributes & 0x1f; - if (num_req_streams > ep->max_streams) - return 0; - } - break; - case USB_ENDPOINT_XFER_INT: - /* Bulk endpoints handle interrupt transfers, - * except the toggle-quirky iso-synch kind - */ - if (!ep->caps.type_int && !ep->caps.type_bulk) - return 0; - /* INT: limit 64 bytes full speed, 1024 high/super speed */ - if (!gadget_is_dualspeed(gadget) && max > 64) - return 0; - break; - } - - return 1; -} -EXPORT_SYMBOL_GPL(usb_gadget_ep_match_desc); - -/* ------------------------------------------------------------------------- */ - -static void usb_gadget_state_work(struct work_struct *work) -{ - struct usb_gadget *gadget = work_to_gadget(work); - struct usb_udc *udc = gadget->udc; - - if (udc) - sysfs_notify(&udc->dev.kobj, NULL, "state"); -} - -void usb_gadget_set_state(struct usb_gadget *gadget, - enum usb_device_state state) -{ - gadget->state = state; - schedule_work(&gadget->work); -} -EXPORT_SYMBOL_GPL(usb_gadget_set_state); - -/* ------------------------------------------------------------------------- */ - -static void usb_udc_connect_control(struct usb_udc *udc) -{ - if (udc->vbus) - usb_gadget_connect(udc->gadget); - else - usb_gadget_disconnect(udc->gadget); -} - -/** - * usb_udc_vbus_handler - updates the udc core vbus status, and try to - * connect or disconnect gadget - * @gadget: The gadget which vbus change occurs - * @status: The vbus status - * - * The udc driver calls it when it wants to connect or disconnect gadget - * according to vbus status. - */ -void usb_udc_vbus_handler(struct usb_gadget *gadget, bool status) -{ - struct usb_udc *udc = gadget->udc; - - if (udc) { - udc->vbus = status; - usb_udc_connect_control(udc); - } -} -EXPORT_SYMBOL_GPL(usb_udc_vbus_handler); - -/** - * usb_gadget_udc_reset - notifies the udc core that bus reset occurs - * @gadget: The gadget which bus reset occurs - * @driver: The gadget driver we want to notify - * - * If the udc driver has bus reset handler, it needs to call this when the bus - * reset occurs, it notifies the gadget driver that the bus reset occurs as - * well as updates gadget state. - */ -void usb_gadget_udc_reset(struct usb_gadget *gadget, - struct usb_gadget_driver *driver) -{ - driver->reset(gadget); - usb_gadget_set_state(gadget, USB_STATE_DEFAULT); -} -EXPORT_SYMBOL_GPL(usb_gadget_udc_reset); - -/** - * usb_gadget_udc_start - tells usb device controller to start up - * @udc: The UDC to be started - * - * This call is issued by the UDC Class driver when it's about - * to register a gadget driver to the device controller, before - * calling gadget driver's bind() method. - * - * It allows the controller to be powered off until strictly - * necessary to have it powered on. - * - * Returns zero on success, else negative errno. - */ -static inline int usb_gadget_udc_start(struct usb_udc *udc) -{ - return udc->gadget->ops->udc_start(udc->gadget, udc->driver); -} - -/** - * usb_gadget_udc_stop - tells usb device controller we don't need it anymore - * @gadget: The device we want to stop activity - * @driver: The driver to unbind from @gadget - * - * This call is issued by the UDC Class driver after calling - * gadget driver's unbind() method. - * - * The details are implementation specific, but it can go as - * far as powering off UDC completely and disable its data - * line pullups. - */ -static inline void usb_gadget_udc_stop(struct usb_udc *udc) -{ - udc->gadget->ops->udc_stop(udc->gadget); -} - -/** - * usb_udc_release - release the usb_udc struct - * @dev: the dev member within usb_udc - * - * This is called by driver's core in order to free memory once the last - * reference is released. - */ -static void usb_udc_release(struct device *dev) -{ - struct usb_udc *udc; - - udc = container_of(dev, struct usb_udc, dev); - dev_dbg(dev, "releasing '%s'\n", dev_name(dev)); - kfree(udc); -} - -static const struct attribute_group *usb_udc_attr_groups[]; - -static void usb_udc_nop_release(struct device *dev) -{ - dev_vdbg(dev, "%s\n", __func__); -} - -/** - * usb_add_gadget_udc_release - adds a new gadget to the udc class driver list - * @parent: the parent device to this udc. Usually the controller driver's - * device. - * @gadget: the gadget to be added to the list. - * @release: a gadget release function. - * - * Returns zero on success, negative errno otherwise. - */ -int usb_add_gadget_udc_release(struct device *parent, struct usb_gadget *gadget, - void (*release)(struct device *dev)) -{ - struct usb_udc *udc; - struct usb_gadget_driver *driver; - int ret = -ENOMEM; - - udc = kzalloc(sizeof(*udc), GFP_KERNEL); - if (!udc) - goto err1; - - dev_set_name(&gadget->dev, "gadget"); - INIT_WORK(&gadget->work, usb_gadget_state_work); - gadget->dev.parent = parent; - - if (release) - gadget->dev.release = release; - else - gadget->dev.release = usb_udc_nop_release; - - ret = device_register(&gadget->dev); - if (ret) - goto err2; - - device_initialize(&udc->dev); - udc->dev.release = usb_udc_release; - udc->dev.class = udc_class; - udc->dev.groups = usb_udc_attr_groups; - udc->dev.parent = parent; - ret = dev_set_name(&udc->dev, "%s", kobject_name(&parent->kobj)); - if (ret) - goto err3; - - udc->gadget = gadget; - gadget->udc = udc; - - mutex_lock(&udc_lock); - list_add_tail(&udc->list, &udc_list); - - ret = device_add(&udc->dev); - if (ret) - goto err4; - - usb_gadget_set_state(gadget, USB_STATE_NOTATTACHED); - udc->vbus = true; - - /* pick up one of pending gadget drivers */ - list_for_each_entry(driver, &gadget_driver_pending_list, pending) { - if (!driver->udc_name || strcmp(driver->udc_name, - dev_name(&udc->dev)) == 0) { - ret = udc_bind_to_driver(udc, driver); - if (ret != -EPROBE_DEFER) - list_del(&driver->pending); - if (ret) - goto err4; - break; - } - } - - mutex_unlock(&udc_lock); - - return 0; - -err4: - list_del(&udc->list); - mutex_unlock(&udc_lock); - -err3: - put_device(&udc->dev); - device_del(&gadget->dev); - -err2: - put_device(&gadget->dev); - kfree(udc); - -err1: - return ret; -} -EXPORT_SYMBOL_GPL(usb_add_gadget_udc_release); - -/** - * usb_get_gadget_udc_name - get the name of the first UDC controller - * This functions returns the name of the first UDC controller in the system. - * Please note that this interface is usefull only for legacy drivers which - * assume that there is only one UDC controller in the system and they need to - * get its name before initialization. There is no guarantee that the UDC - * of the returned name will be still available, when gadget driver registers - * itself. - * - * Returns pointer to string with UDC controller name on success, NULL - * otherwise. Caller should kfree() returned string. - */ -char *usb_get_gadget_udc_name(void) -{ - struct usb_udc *udc; - char *name = NULL; - - /* For now we take the first available UDC */ - mutex_lock(&udc_lock); - list_for_each_entry(udc, &udc_list, list) { - if (!udc->driver) { - name = kstrdup(udc->gadget->name, GFP_KERNEL); - break; - } - } - mutex_unlock(&udc_lock); - return name; -} -EXPORT_SYMBOL_GPL(usb_get_gadget_udc_name); - -/** - * usb_add_gadget_udc - adds a new gadget to the udc class driver list - * @parent: the parent device to this udc. Usually the controller - * driver's device. - * @gadget: the gadget to be added to the list - * - * Returns zero on success, negative errno otherwise. - */ -int usb_add_gadget_udc(struct device *parent, struct usb_gadget *gadget) -{ - return usb_add_gadget_udc_release(parent, gadget, NULL); -} -EXPORT_SYMBOL_GPL(usb_add_gadget_udc); - -static void usb_gadget_remove_driver(struct usb_udc *udc) -{ - dev_dbg(&udc->dev, "unregistering UDC driver [%s]\n", - udc->driver->function); - - kobject_uevent(&udc->dev.kobj, KOBJ_CHANGE); - - usb_gadget_disconnect(udc->gadget); - udc->driver->disconnect(udc->gadget); - udc->driver->unbind(udc->gadget); - usb_gadget_udc_stop(udc); - - udc->driver = NULL; - udc->dev.driver = NULL; - udc->gadget->dev.driver = NULL; -} - -/** - * usb_del_gadget_udc - deletes @udc from udc_list - * @gadget: the gadget to be removed. - * - * This, will call usb_gadget_unregister_driver() if - * the @udc is still busy. - */ -void usb_del_gadget_udc(struct usb_gadget *gadget) -{ - struct usb_udc *udc = gadget->udc; - - if (!udc) - return; - - dev_vdbg(gadget->dev.parent, "unregistering gadget\n"); - - mutex_lock(&udc_lock); - list_del(&udc->list); - - if (udc->driver) { - struct usb_gadget_driver *driver = udc->driver; - - usb_gadget_remove_driver(udc); - list_add(&driver->pending, &gadget_driver_pending_list); - } - mutex_unlock(&udc_lock); - - kobject_uevent(&udc->dev.kobj, KOBJ_REMOVE); - flush_work(&gadget->work); - device_unregister(&udc->dev); - device_unregister(&gadget->dev); -} -EXPORT_SYMBOL_GPL(usb_del_gadget_udc); - -/* ------------------------------------------------------------------------- */ - -static int udc_bind_to_driver(struct usb_udc *udc, struct usb_gadget_driver *driver) -{ - int ret; - - dev_dbg(&udc->dev, "registering UDC driver [%s]\n", - driver->function); - - udc->driver = driver; - udc->dev.driver = &driver->driver; - udc->gadget->dev.driver = &driver->driver; - - ret = driver->bind(udc->gadget, driver); - if (ret) - goto err1; - ret = usb_gadget_udc_start(udc); - if (ret) { - driver->unbind(udc->gadget); - goto err1; - } - usb_udc_connect_control(udc); - - kobject_uevent(&udc->dev.kobj, KOBJ_CHANGE); - return 0; -err1: - if (ret != -EISNAM) - dev_err(&udc->dev, "failed to start %s: %d\n", - udc->driver->function, ret); - udc->driver = NULL; - udc->dev.driver = NULL; - udc->gadget->dev.driver = NULL; - return ret; -} - -int usb_gadget_probe_driver(struct usb_gadget_driver *driver) -{ - struct usb_udc *udc = NULL; - int ret = -ENODEV; - - if (!driver || !driver->bind || !driver->setup) - return -EINVAL; - - mutex_lock(&udc_lock); - if (driver->udc_name) { - list_for_each_entry(udc, &udc_list, list) { - ret = strcmp(driver->udc_name, dev_name(&udc->dev)); - if (!ret) - break; - } - if (!ret && !udc->driver) - goto found; - } else { - list_for_each_entry(udc, &udc_list, list) { - /* For now we take the first one */ - if (!udc->driver) - goto found; - } - } - - if (!driver->match_existing_only) { - list_add_tail(&driver->pending, &gadget_driver_pending_list); - pr_info("udc-core: couldn't find an available UDC - added [%s] to list of pending drivers\n", - driver->function); - ret = 0; - } - - mutex_unlock(&udc_lock); - return ret; -found: - ret = udc_bind_to_driver(udc, driver); - mutex_unlock(&udc_lock); - return ret; -} -EXPORT_SYMBOL_GPL(usb_gadget_probe_driver); - -int usb_gadget_unregister_driver(struct usb_gadget_driver *driver) -{ - struct usb_udc *udc = NULL; - int ret = -ENODEV; - - if (!driver || !driver->unbind) - return -EINVAL; - - mutex_lock(&udc_lock); - list_for_each_entry(udc, &udc_list, list) - if (udc->driver == driver) { - usb_gadget_remove_driver(udc); - usb_gadget_set_state(udc->gadget, - USB_STATE_NOTATTACHED); - ret = 0; - break; - } - - if (ret) { - list_del(&driver->pending); - ret = 0; - } - mutex_unlock(&udc_lock); - return ret; -} -EXPORT_SYMBOL_GPL(usb_gadget_unregister_driver); - -/* ------------------------------------------------------------------------- */ - -static ssize_t usb_udc_srp_store(struct device *dev, - struct device_attribute *attr, const char *buf, size_t n) -{ - struct usb_udc *udc = container_of(dev, struct usb_udc, dev); - - if (sysfs_streq(buf, "1")) - usb_gadget_wakeup(udc->gadget); - - return n; -} -static DEVICE_ATTR(srp, S_IWUSR, NULL, usb_udc_srp_store); - -static ssize_t usb_udc_softconn_store(struct device *dev, - struct device_attribute *attr, const char *buf, size_t n) -{ - struct usb_udc *udc = container_of(dev, struct usb_udc, dev); - - if (!udc->driver) { - dev_err(dev, "soft-connect without a gadget driver\n"); - return -EOPNOTSUPP; - } - - if (sysfs_streq(buf, "connect")) { - usb_gadget_udc_start(udc); - usb_gadget_connect(udc->gadget); - } else if (sysfs_streq(buf, "disconnect")) { - usb_gadget_disconnect(udc->gadget); - udc->driver->disconnect(udc->gadget); - usb_gadget_udc_stop(udc); - } else { - dev_err(dev, "unsupported command '%s'\n", buf); - return -EINVAL; - } - - return n; -} -static DEVICE_ATTR(soft_connect, S_IWUSR, NULL, usb_udc_softconn_store); - -static ssize_t state_show(struct device *dev, struct device_attribute *attr, - char *buf) -{ - struct usb_udc *udc = container_of(dev, struct usb_udc, dev); - struct usb_gadget *gadget = udc->gadget; - - return sprintf(buf, "%s\n", usb_state_string(gadget->state)); -} -static DEVICE_ATTR_RO(state); - -#define USB_UDC_SPEED_ATTR(name, param) \ -ssize_t name##_show(struct device *dev, \ - struct device_attribute *attr, char *buf) \ -{ \ - struct usb_udc *udc = container_of(dev, struct usb_udc, dev); \ - return snprintf(buf, PAGE_SIZE, "%s\n", \ - usb_speed_string(udc->gadget->param)); \ -} \ -static DEVICE_ATTR_RO(name) - -static USB_UDC_SPEED_ATTR(current_speed, speed); -static USB_UDC_SPEED_ATTR(maximum_speed, max_speed); - -#define USB_UDC_ATTR(name) \ -ssize_t name##_show(struct device *dev, \ - struct device_attribute *attr, char *buf) \ -{ \ - struct usb_udc *udc = container_of(dev, struct usb_udc, dev); \ - struct usb_gadget *gadget = udc->gadget; \ - \ - return snprintf(buf, PAGE_SIZE, "%d\n", gadget->name); \ -} \ -static DEVICE_ATTR_RO(name) - -static USB_UDC_ATTR(is_otg); -static USB_UDC_ATTR(is_a_peripheral); -static USB_UDC_ATTR(b_hnp_enable); -static USB_UDC_ATTR(a_hnp_support); -static USB_UDC_ATTR(a_alt_hnp_support); -static USB_UDC_ATTR(is_selfpowered); - -static struct attribute *usb_udc_attrs[] = { - &dev_attr_srp.attr, - &dev_attr_soft_connect.attr, - &dev_attr_state.attr, - &dev_attr_current_speed.attr, - &dev_attr_maximum_speed.attr, - - &dev_attr_is_otg.attr, - &dev_attr_is_a_peripheral.attr, - &dev_attr_b_hnp_enable.attr, - &dev_attr_a_hnp_support.attr, - &dev_attr_a_alt_hnp_support.attr, - &dev_attr_is_selfpowered.attr, - NULL, -}; - -static const struct attribute_group usb_udc_attr_group = { - .attrs = usb_udc_attrs, -}; - -static const struct attribute_group *usb_udc_attr_groups[] = { - &usb_udc_attr_group, - NULL, -}; - -static int usb_udc_uevent(struct device *dev, struct kobj_uevent_env *env) -{ - struct usb_udc *udc = container_of(dev, struct usb_udc, dev); - int ret; - - ret = add_uevent_var(env, "USB_UDC_NAME=%s", udc->gadget->name); - if (ret) { - dev_err(dev, "failed to add uevent USB_UDC_NAME\n"); - return ret; - } - - if (udc->driver) { - ret = add_uevent_var(env, "USB_UDC_DRIVER=%s", - udc->driver->function); - if (ret) { - dev_err(dev, "failed to add uevent USB_UDC_DRIVER\n"); - return ret; - } - } - - return 0; -} - -static int __init usb_udc_init(void) -{ - udc_class = class_create(THIS_MODULE, "udc"); - if (IS_ERR(udc_class)) { - pr_err("failed to create udc class --> %ld\n", - PTR_ERR(udc_class)); - return PTR_ERR(udc_class); - } - - udc_class->dev_uevent = usb_udc_uevent; - return 0; -} -subsys_initcall(usb_udc_init); - -static void __exit usb_udc_exit(void) -{ - class_destroy(udc_class); -} -module_exit(usb_udc_exit); - -MODULE_DESCRIPTION("UDC Framework"); -MODULE_AUTHOR("Felipe Balbi "); -MODULE_LICENSE("GPL v2"); diff --git a/include/linux/usb/gadget.h b/include/linux/usb/gadget.h index c6e1149..612dbdf 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; -- cgit v0.10.2 From 21e64bf20df5e120b37c403b46395d4f0c5d8e86 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Thu, 2 Jun 2016 12:37:31 +0300 Subject: 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 diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index d2884a4..c889ee3 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; -- cgit v0.10.2 From e6fe66fe08cdf9c5d0eb6a6e209621f74f7ee60b Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Tue, 7 Jun 2016 12:49:52 +0300 Subject: 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 diff --git a/drivers/usb/dwc3/dwc3-pci.c b/drivers/usb/dwc3/dwc3-pci.c index 6bc4c2b..45f5a23 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; - - 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); - - gpiod_set_value_cansleep(gpio, 1); - gpiod_put(gpio); - - gpio = gpiod_get_optional(&pdev->dev, "reset", GPIOD_OUT_LOW); - if (IS_ERR(gpio)) - return PTR_ERR(gpio); - - if (gpio) { + if (pdev->vendor == PCI_VENDOR_ID_INTEL) { + int ret; + + struct property_entry properties[] = { + PROPERTY_ENTRY_STRING("dr-mode", "peripheral"), + { } + }; + + ret = platform_device_add_properties(dwc3, properties); + if (ret < 0) + return ret; + + if (pdev->device == PCI_DEVICE_ID_INTEL_BYT) { + struct gpio_desc *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); + 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); + } } } -- cgit v0.10.2 From 5f82279a0c76825bfc6f5fa213ff82c932161462 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Tue, 7 Jun 2016 12:55:19 +0300 Subject: 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 diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c index 9c4e1d8d..8fceeb1 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) -- cgit v0.10.2 From d807bdd02845d53047346a4ba6d8934597fba6d6 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Thu, 9 Jun 2016 16:24:08 +0300 Subject: 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 diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index c889ee3..b3b5df6 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", -- cgit v0.10.2 From f2df679b6c556fd3b0b7ffafea170f1679086455 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Thu, 9 Jun 2016 16:31:34 +0300 Subject: 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 diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index b3b5df6..9b9367b 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 -- cgit v0.10.2 From b6d4e16e831376b676edb3463f2bdaa192e5f7be Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Thu, 9 Jun 2016 16:47:05 +0300 Subject: 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 diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index 9b9367b..0afaa9d 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; -- cgit v0.10.2 From 328082376aea6016b63bca1e5c067a9539f9e8c9 Mon Sep 17 00:00:00 2001 From: Roger Quadros Date: Fri, 10 Jun 2016 14:38:02 +0300 Subject: 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 diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c index 8fceeb1..ca22e48 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 -- cgit v0.10.2 From 0e146028eebf989e86d3fe9385b76434e954c84e Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Tue, 21 Jun 2016 10:32:02 +0300 Subject: 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 diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index 0afaa9d..b1fec36 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; - - if (transfer_in_flight && !list_empty(&dep->started_list)) { - dwc3_stop_active_transfer(dwc, dep->number, true); + 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)) { -- cgit v0.10.2 From da1410be21bfedd16740aee6d772e669cf4e852f Mon Sep 17 00:00:00 2001 From: Baolin Wang Date: Mon, 20 Jun 2016 16:19:48 +0800 Subject: 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 diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index b1fec36..b9bc646 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]); -- cgit v0.10.2 From b89e5f1a677f3a711047d686115f2416e1200a3b Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Thu, 16 Jun 2016 13:38:25 +0200 Subject: 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 diff --git a/drivers/usb/gadget/udc/pxa27x_udc.c b/drivers/usb/gadget/udc/pxa27x_udc.c index 001a3b7..ad140aa 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); -- cgit v0.10.2 From 106528b21df7dd9da9ca3e5e4ff4e015c33211be Mon Sep 17 00:00:00 2001 From: Vardan Mikayelyan Date: Wed, 25 May 2016 18:06:53 -0700 Subject: 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 diff --git a/drivers/usb/dwc2/hw.h b/drivers/usb/dwc2/hw.h index 281b57b..1126141 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) -- cgit v0.10.2 From 04cde4787d8d9c56bd7b0bc5ee15af4435275134 Mon Sep 17 00:00:00 2001 From: Vardan Mikayelyan Date: Wed, 25 May 2016 18:06:55 -0700 Subject: 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 diff --git a/drivers/usb/dwc2/gadget.c b/drivers/usb/dwc2/gadget.c index 26cf09d..34c63e9 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); -- cgit v0.10.2 From 7c01b99154c5819bb811100738880a1341bca64a Mon Sep 17 00:00:00 2001 From: Vardan Mikayelyan Date: Wed, 25 May 2016 18:06:58 -0700 Subject: 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 diff --git a/drivers/usb/dwc2/gadget.c b/drivers/usb/dwc2/gadget.c index 34c63e9..1487968 100644 --- a/drivers/usb/dwc2/gadget.c +++ b/drivers/usb/dwc2/gadget.c @@ -658,14 +658,6 @@ static void dwc2_hsotg_start_req(struct dwc2_hsotg *hsotg, } /* - * 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. */ -- cgit v0.10.2 From 26ddef5da62993fb9189a822d3545fc00451d242 Mon Sep 17 00:00:00 2001 From: Vardan Mikayelyan Date: Wed, 25 May 2016 18:07:00 -0700 Subject: 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 diff --git a/drivers/usb/dwc2/gadget.c b/drivers/usb/dwc2/gadget.c index 1487968..7168172 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)) -- cgit v0.10.2 From 6b58cb07a850f9b6d348feb2455b2c264a515f4a Mon Sep 17 00:00:00 2001 From: Vardan Mikayelyan Date: Wed, 25 May 2016 18:07:02 -0700 Subject: 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 diff --git a/drivers/usb/dwc2/gadget.c b/drivers/usb/dwc2/gadget.c index 7168172..54d242b 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, -- cgit v0.10.2 From 92d1635d781ac17fc7d886b0c126838083f3c2b9 Mon Sep 17 00:00:00 2001 From: Vardan Mikayelyan Date: Wed, 25 May 2016 18:07:05 -0700 Subject: 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 diff --git a/drivers/usb/dwc2/core.h b/drivers/usb/dwc2/core.h index dec0b21..5516099 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 54d242b..b8f3661 100644 --- a/drivers/usb/dwc2/gadget.c +++ b/drivers/usb/dwc2/gadget.c @@ -97,6 +97,25 @@ static inline bool using_dma(struct dwc2_hsotg *hsotg) } /** + * 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 * @ints: A bitmask of the interrupts to enable -- cgit v0.10.2 From 142bd33fcd185d850178f7f8697ecbeaaa18e257 Mon Sep 17 00:00:00 2001 From: Vardan Mikayelyan Date: Wed, 25 May 2016 18:07:07 -0700 Subject: 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 diff --git a/drivers/usb/dwc2/core.h b/drivers/usb/dwc2/core.h index 5516099..0ba3599 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 b8f3661..2cef7a9 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; -- cgit v0.10.2 From 326015887b6a1321fd61f7a16816241ad841a03c Mon Sep 17 00:00:00 2001 From: Vardan Mikayelyan Date: Wed, 25 May 2016 18:07:10 -0700 Subject: 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 diff --git a/drivers/usb/dwc2/gadget.c b/drivers/usb/dwc2/gadget.c index 2cef7a9..8139efd 100644 --- a/drivers/usb/dwc2/gadget.c +++ b/drivers/usb/dwc2/gadget.c @@ -1947,6 +1947,34 @@ static void dwc2_hsotg_complete_in(struct dwc2_hsotg *hsotg, } /** + * 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 * @idx: The index for the endpoint (0..15) @@ -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 */ -- cgit v0.10.2 From 41cc4cd2716fa6d18a1a09d740ea075adecfa7dd Mon Sep 17 00:00:00 2001 From: Vardan Mikayelyan Date: Wed, 25 May 2016 18:07:12 -0700 Subject: 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 diff --git a/drivers/usb/dwc2/gadget.c b/drivers/usb/dwc2/gadget.c index 8139efd..7db9f9f 100644 --- a/drivers/usb/dwc2/gadget.c +++ b/drivers/usb/dwc2/gadget.c @@ -1045,6 +1045,42 @@ static struct dwc2_hsotg_req *get_ep_head(struct dwc2_hsotg_ep *hs_ep) } /** + * 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 * @ctrl: USB control request @@ -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); } } -- cgit v0.10.2 From 5321922cb6fa0f513fdd8ce73b281f4b9957886b Mon Sep 17 00:00:00 2001 From: Vardan Mikayelyan Date: Wed, 25 May 2016 18:07:14 -0700 Subject: 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 diff --git a/drivers/usb/dwc2/gadget.c b/drivers/usb/dwc2/gadget.c index 7db9f9f..256c929 100644 --- a/drivers/usb/dwc2/gadget.c +++ b/drivers/usb/dwc2/gadget.c @@ -2000,6 +2000,94 @@ static u32 dwc2_gadget_read_ep_interrupts(struct dwc2_hsotg *hsotg, } /** + * 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 * @idx: The index for the endpoint (0..15) @@ -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__); -- cgit v0.10.2 From 381fc8f8228923026b3d75c8230fa2ee4d688f32 Mon Sep 17 00:00:00 2001 From: Vardan Mikayelyan Date: Wed, 25 May 2016 18:07:17 -0700 Subject: 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 diff --git a/drivers/usb/dwc2/gadget.c b/drivers/usb/dwc2/gadget.c index 256c929..61f913d 100644 --- a/drivers/usb/dwc2/gadget.c +++ b/drivers/usb/dwc2/gadget.c @@ -523,6 +523,23 @@ static unsigned get_ep_limit(struct dwc2_hsotg_ep *hs_ep) } /** +* 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. * @hs_ep: The endpoint to process a request for @@ -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) { @@ -1641,23 +1682,6 @@ static void dwc2_hsotg_handle_outdone(struct dwc2_hsotg *hsotg, int epnum) } /** - * 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 * @@ -2571,6 +2595,85 @@ void dwc2_hsotg_core_connect(struct dwc2_hsotg *hsotg) } /** + * 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 * @pw: The pw value when registered the handler. @@ -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; - - 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 (gintsts & GINTSTS_INCOMPL_SOIN) + dwc2_gadget_handle_incomplete_isoc_in(hsotg); - 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 -- cgit v0.10.2 From bd9971f0a1efe4b3ac6e2f8ec864c75f73ca7829 Mon Sep 17 00:00:00 2001 From: Vardan Mikayelyan Date: Wed, 25 May 2016 18:07:19 -0700 Subject: 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 diff --git a/drivers/usb/dwc2/gadget.c b/drivers/usb/dwc2/gadget.c index 61f913d..4a6074c 100644 --- a/drivers/usb/dwc2/gadget.c +++ b/drivers/usb/dwc2/gadget.c @@ -2024,6 +2024,74 @@ static u32 dwc2_gadget_read_ep_interrupts(struct dwc2_hsotg *hsotg, } /** + * 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); -- cgit v0.10.2 From 837e9f00bf9966d64abc1bce678271099a72423b Mon Sep 17 00:00:00 2001 From: Vardan Mikayelyan Date: Wed, 25 May 2016 18:07:22 -0700 Subject: 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 diff --git a/drivers/usb/dwc2/core.h b/drivers/usb/dwc2/core.h index 0ba3599..9fae029 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 4a6074c..af46adf 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"); - - __orr32(hsotg->regs + DCTL, DCTL_CGOUTNAK); + u8 idx; + u32 epctrl; + u32 gintmsk; + struct dwc2_hsotg_ep *hs_ep; + + /* Mask this interrupt */ + gintmsk = dwc2_readl(hsotg->regs + GINTMSK); + gintmsk &= ~GINTSTS_GOUTNAKEFF; + dwc2_writel(gintmsk, hsotg->regs + GINTMSK); + + 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)); + } + } - dwc2_hsotg_dump(hsotg); + /* 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 1126141..efc3bcd 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) -- cgit v0.10.2 From 882bd9fc46321c9d4721b376039a142cbfe8a17a Mon Sep 17 00:00:00 2001 From: Alexandre Belloni Date: Mon, 13 Jun 2016 10:47:30 +0200 Subject: 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 diff --git a/drivers/usb/gadget/udc/atmel_usba_udc.c b/drivers/usb/gadget/udc/atmel_usba_udc.c index 18569de..bb1f6c8 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); -- cgit v0.10.2 From ce15ed4c5dfb3f7757e6611902aed5db253af977 Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Fri, 10 Jun 2016 11:46:25 +0200 Subject: 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 diff --git a/drivers/usb/common/common.c b/drivers/usb/common/common.c index e3d0161..5ef8da6 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 a262a43..7e5aece 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 de3237f..5ff9032 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; } -- cgit v0.10.2 From 9522def40065194aa75b0a7b7e1ff5b8e2014724 Mon Sep 17 00:00:00 2001 From: Roger Quadros Date: Fri, 10 Jun 2016 14:48:38 +0300 Subject: 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 diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c index ca22e48..9466431 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 b9bc646..f209753 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 c679f63..2e960ed 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) { -- cgit v0.10.2 From 872ce5119524f33fafacc4d9610a431185ea66a2 Mon Sep 17 00:00:00 2001 From: Michal Nazarewicz Date: Tue, 31 May 2016 14:17:21 +0200 Subject: 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 diff --git a/drivers/usb/gadget/function/f_fs.c b/drivers/usb/gadget/function/f_fs.c index cc33d26..70ed370 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 3580f19..6ded634 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 f85639e..6da7316 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 39d70b4..ea03ca7 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 6a4155c..4d5e918 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 d619950..650717e7 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 dde4445..77d0790 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 81b6229..ce73b35 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 18f5ebd..7c61134 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 ebc51ec..8ad847a 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 1cbb0ac..f8bf290 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); -- cgit v0.10.2 From d58fcf81bb8facc0cd22490c43698f272c1ee07b Mon Sep 17 00:00:00 2001 From: Michal Nazarewicz Date: Tue, 31 May 2016 14:17:22 +0200 Subject: 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 diff --git a/drivers/usb/gadget/udc/m66592-udc.c b/drivers/usb/gadget/udc/m66592-udc.c index b1cfa96..6e977dc 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; } -- cgit v0.10.2 From 906ae52db2695f8b34b292cc7d74d126f4a4612d Mon Sep 17 00:00:00 2001 From: Michal Nazarewicz Date: Tue, 31 May 2016 14:17:23 +0200 Subject: 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 diff --git a/drivers/usb/gadget/udc/r8a66597-udc.c b/drivers/usb/gadget/udc/r8a66597-udc.c index 8b300e6..f2c8862 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; } -- cgit v0.10.2 From bd8dc7375b7e147de0f09d8be89460d4143daf31 Mon Sep 17 00:00:00 2001 From: Michal Nazarewicz Date: Tue, 31 May 2016 14:17:24 +0200 Subject: 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 diff --git a/drivers/usb/gadget/udc/mv_u3d_core.c b/drivers/usb/gadget/udc/mv_u3d_core.c index dafe74e..b9e19a5 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) */ -- cgit v0.10.2 From f7d74de355976cbab89eeefe0063771c0de71457 Mon Sep 17 00:00:00 2001 From: Colin Ian King Date: Sat, 4 Jun 2016 06:53:25 +0100 Subject: usb: gadget: bdc: fix spelling mistake: "allocted" -> "allocated" trivial fix to spelling mistake Signed-off-by: Colin Ian King Signed-off-by: Felipe Balbi diff --git a/drivers/usb/gadget/udc/bdc/bdc_ep.c b/drivers/usb/gadget/udc/bdc/bdc_ep.c index 650717e7..ccaa74a 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; } -- cgit v0.10.2 From 7c8b9c318490afe3e8bb244ef7e488265871aba7 Mon Sep 17 00:00:00 2001 From: Sandhya Bankar Date: Tue, 31 May 2016 08:59:44 -0400 Subject: 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 diff --git a/drivers/usb/phy/phy-omap-otg.c b/drivers/usb/phy/phy-omap-otg.c index c4bf2de..6f6d2a7 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; -- cgit v0.10.2 From c662a31be755f5e315945e2c802c335eb8baddb8 Mon Sep 17 00:00:00 2001 From: Michal Nazarewicz Date: Sat, 21 May 2016 20:47:34 +0200 Subject: 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 diff --git a/drivers/usb/gadget/function/f_fs.c b/drivers/usb/gadget/function/f_fs.c index 70ed370..bb3d40a 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; -- cgit v0.10.2 From 9353afbbfa7b10779a1aa108fcc23e32fd625990 Mon Sep 17 00:00:00 2001 From: Michal Nazarewicz Date: Sat, 21 May 2016 20:47:35 +0200 Subject: =?UTF-8?q?usb:=20gadget:=20f=5Ffs:=20buffer=20data=20from=20?= =?UTF-8?q?=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 diff --git a/drivers/usb/gadget/function/f_fs.c b/drivers/usb/gadget/function/f_fs.c index bb3d40a..a91fcb0 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) -- cgit v0.10.2 From b9b092319be29bf665f1b004679de722b81c9123 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Wed, 18 May 2016 23:24:06 +0200 Subject: 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 diff --git a/drivers/usb/phy/phy-msm-usb.c b/drivers/usb/phy/phy-msm-usb.c index 72b387d..8a34759 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 8c8f685..0000000 --- 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 -- cgit v0.10.2 From 72704f876f50b00f08d41d4b13d5a5d11262254f Mon Sep 17 00:00:00 2001 From: Baolin Wang Date: Mon, 16 May 2016 16:43:53 +0800 Subject: 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 diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c index f209753..8f8c215 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"); -- cgit v0.10.2 From 43202800010102a857ffb119429d7dc5b41b7096 Mon Sep 17 00:00:00 2001 From: Sandhya Bankar Date: Wed, 4 May 2016 12:23:14 +0530 Subject: usb: Use (foo *) instead of (foo*). Use (foo *) instead of (foo*). Signed-off-by: Sandhya Bankar Signed-off-by: Felipe Balbi diff --git a/drivers/usb/gadget/config.c b/drivers/usb/gadget/config.c index e6c0542..17a6077 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; -- cgit v0.10.2 From 1d23d16a88e6c8143b07339435ba061b131ebb8c Mon Sep 17 00:00:00 2001 From: Iago Abal Date: Tue, 21 Jun 2016 12:01:11 +0200 Subject: 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 diff --git a/drivers/usb/gadget/udc/pch_udc.c b/drivers/usb/gadget/udc/pch_udc.c index 8ad847a..a97da64 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); } /** -- cgit v0.10.2 From 70992588193617511e1b50bf7adee2fd371a476b Mon Sep 17 00:00:00 2001 From: Ben Dooks Date: Tue, 21 Jun 2016 18:52:54 +0100 Subject: 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 diff --git a/drivers/usb/renesas_usbhs/rcar3.c b/drivers/usb/renesas_usbhs/rcar3.c index 38b01f2..1d70add 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); } -- cgit v0.10.2 From 0d7995031a8e7a34e5638d57a44a51aae39e321c Mon Sep 17 00:00:00 2001 From: Yoshihiro Shimoda Date: Mon, 27 Jun 2016 21:09:18 +0900 Subject: 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 diff --git a/drivers/usb/renesas_usbhs/common.c b/drivers/usb/renesas_usbhs/common.c index baeb7d2..8fbbc2d 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; } -- cgit v0.10.2 From e135ab7405f562c7709806e355b1521ee68548dc Mon Sep 17 00:00:00 2001 From: Nicolas Iooss Date: Sun, 26 Jun 2016 10:12:38 +0200 Subject: 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 diff --git a/drivers/usb/dwc2/hcd_queue.c b/drivers/usb/dwc2/hcd_queue.c index b5c7793..1375435 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; -- cgit v0.10.2 From e43470dbdfe8922f9b2962184336efaa71e59727 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Fri, 24 Jun 2016 09:30:26 +0200 Subject: 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 diff --git a/drivers/usb/dwc2/Kconfig b/drivers/usb/dwc2/Kconfig index c1f29ca..e838701 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 -- cgit v0.10.2 From 44963d649da63ce8ed8f41b8a267c745ca1ec0b0 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Fri, 24 Jun 2016 15:23:16 +0300 Subject: 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 diff --git a/drivers/usb/gadget/function/f_fs.c b/drivers/usb/gadget/function/f_fs.c index a91fcb0..5c8429f 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); -- cgit v0.10.2 From 4fdef698383db07d829da567e0e405fc41ff3a89 Mon Sep 17 00:00:00 2001 From: Yoshihiro Shimoda Date: Wed, 8 Jun 2016 16:32:49 +0900 Subject: 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 diff --git a/drivers/usb/renesas_usbhs/fifo.c b/drivers/usb/renesas_usbhs/fifo.c index 7be4e7d..280ed5f 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); } /* -- cgit v0.10.2 From 15e4292a2d21e9997fdb2b8c014cc461b3f268f0 Mon Sep 17 00:00:00 2001 From: Yoshihiro Shimoda Date: Wed, 8 Jun 2016 16:32:50 +0900 Subject: 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 diff --git a/drivers/usb/renesas_usbhs/mod_gadget.c b/drivers/usb/renesas_usbhs/mod_gadget.c index 30345c2..50f3363 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; } -- cgit v0.10.2