summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGreg Kroah-Hartman <gregkh@linuxfoundation.org>2014-07-21 11:33:41 -0700
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2014-07-21 11:33:41 -0700
commit61fe2d75f138992f116ee70e83f10ff2d7e79143 (patch)
treec08ba135803a93852583a2916cd96981f53cfd06
parent499b3803d3e2f062f73bf22372b38393369ffcbf (diff)
parent8346b33fad01cfe93f0fd0e64cd32ff40bd4ba41 (diff)
Merge tag 'usb-for-v3.17' of git://git.kernel.org/pub/scm/linux/kernel/git/balbi/usb into usb-next
Felipe writes: usb: patches for v3.17 merge window Surprisingly enough, while a big set of patches, the majority is composed of cleanups (using devm_*, fixing sparse errors, moving code around, adding const, etc). The highlights are addition of new support for PLX USB338x devices, and support for USB 2.0-only configurations of the DWC3 IP core. Signed-of-by: Felipe Balbi <balbi@ti.com>
-rw-r--r--Documentation/DocBook/gadget.tmpl10
-rw-r--r--drivers/usb/dwc3/core.c51
-rw-r--r--drivers/usb/dwc3/core.h13
-rw-r--r--drivers/usb/dwc3/dwc3-omap.c172
-rw-r--r--drivers/usb/dwc3/gadget.c7
-rw-r--r--drivers/usb/gadget/Kconfig824
-rw-r--r--drivers/usb/gadget/Makefile101
-rw-r--r--drivers/usb/gadget/composite.c1
-rw-r--r--drivers/usb/gadget/configfs.c4
-rw-r--r--drivers/usb/gadget/function/Makefile34
-rw-r--r--drivers/usb/gadget/function/f_acm.c (renamed from drivers/usb/gadget/f_acm.c)0
-rw-r--r--drivers/usb/gadget/function/f_ecm.c (renamed from drivers/usb/gadget/f_ecm.c)0
-rw-r--r--drivers/usb/gadget/function/f_eem.c (renamed from drivers/usb/gadget/f_eem.c)22
-rw-r--r--drivers/usb/gadget/function/f_fs.c (renamed from drivers/usb/gadget/f_fs.c)352
-rw-r--r--drivers/usb/gadget/function/f_hid.c (renamed from drivers/usb/gadget/f_hid.c)0
-rw-r--r--drivers/usb/gadget/function/f_loopback.c (renamed from drivers/usb/gadget/f_loopback.c)0
-rw-r--r--drivers/usb/gadget/function/f_mass_storage.c (renamed from drivers/usb/gadget/f_mass_storage.c)0
-rw-r--r--drivers/usb/gadget/function/f_mass_storage.h (renamed from drivers/usb/gadget/f_mass_storage.h)0
-rw-r--r--drivers/usb/gadget/function/f_midi.c (renamed from drivers/usb/gadget/f_midi.c)0
-rw-r--r--drivers/usb/gadget/function/f_ncm.c (renamed from drivers/usb/gadget/f_ncm.c)480
-rw-r--r--drivers/usb/gadget/function/f_obex.c (renamed from drivers/usb/gadget/f_obex.c)0
-rw-r--r--drivers/usb/gadget/function/f_phonet.c (renamed from drivers/usb/gadget/f_phonet.c)0
-rw-r--r--drivers/usb/gadget/function/f_rndis.c (renamed from drivers/usb/gadget/f_rndis.c)4
-rw-r--r--drivers/usb/gadget/function/f_serial.c (renamed from drivers/usb/gadget/f_serial.c)0
-rw-r--r--drivers/usb/gadget/function/f_sourcesink.c (renamed from drivers/usb/gadget/f_sourcesink.c)0
-rw-r--r--drivers/usb/gadget/function/f_subset.c (renamed from drivers/usb/gadget/f_subset.c)0
-rw-r--r--drivers/usb/gadget/function/f_uac1.c (renamed from drivers/usb/gadget/f_uac1.c)0
-rw-r--r--drivers/usb/gadget/function/f_uac2.c (renamed from drivers/usb/gadget/f_uac2.c)24
-rw-r--r--drivers/usb/gadget/function/f_uvc.c (renamed from drivers/usb/gadget/f_uvc.c)0
-rw-r--r--drivers/usb/gadget/function/f_uvc.h (renamed from drivers/usb/gadget/f_uvc.h)0
-rw-r--r--drivers/usb/gadget/function/g_zero.h (renamed from drivers/usb/gadget/g_zero.h)0
-rw-r--r--drivers/usb/gadget/function/ndis.h (renamed from drivers/usb/gadget/ndis.h)0
-rw-r--r--drivers/usb/gadget/function/rndis.c (renamed from drivers/usb/gadget/rndis.c)0
-rw-r--r--drivers/usb/gadget/function/rndis.h (renamed from drivers/usb/gadget/rndis.h)0
-rw-r--r--drivers/usb/gadget/function/storage_common.c (renamed from drivers/usb/gadget/storage_common.c)0
-rw-r--r--drivers/usb/gadget/function/storage_common.h (renamed from drivers/usb/gadget/storage_common.h)0
-rw-r--r--drivers/usb/gadget/function/u_ecm.h (renamed from drivers/usb/gadget/u_ecm.h)0
-rw-r--r--drivers/usb/gadget/function/u_eem.h (renamed from drivers/usb/gadget/u_eem.h)0
-rw-r--r--drivers/usb/gadget/function/u_ether.c (renamed from drivers/usb/gadget/u_ether.c)19
-rw-r--r--drivers/usb/gadget/function/u_ether.h (renamed from drivers/usb/gadget/u_ether.h)2
-rw-r--r--drivers/usb/gadget/function/u_ether_configfs.h (renamed from drivers/usb/gadget/u_ether_configfs.h)0
-rw-r--r--drivers/usb/gadget/function/u_fs.h (renamed from drivers/usb/gadget/u_fs.h)7
-rw-r--r--drivers/usb/gadget/function/u_gether.h (renamed from drivers/usb/gadget/u_gether.h)0
-rw-r--r--drivers/usb/gadget/function/u_ncm.h (renamed from drivers/usb/gadget/u_ncm.h)0
-rw-r--r--drivers/usb/gadget/function/u_phonet.h (renamed from drivers/usb/gadget/u_phonet.h)0
-rw-r--r--drivers/usb/gadget/function/u_rndis.h (renamed from drivers/usb/gadget/u_rndis.h)0
-rw-r--r--drivers/usb/gadget/function/u_serial.c (renamed from drivers/usb/gadget/u_serial.c)0
-rw-r--r--drivers/usb/gadget/function/u_serial.h (renamed from drivers/usb/gadget/u_serial.h)0
-rw-r--r--drivers/usb/gadget/function/u_uac1.c (renamed from drivers/usb/gadget/u_uac1.c)0
-rw-r--r--drivers/usb/gadget/function/u_uac1.h (renamed from drivers/usb/gadget/u_uac1.h)0
-rw-r--r--drivers/usb/gadget/function/uvc.h (renamed from drivers/usb/gadget/uvc.h)0
-rw-r--r--drivers/usb/gadget/function/uvc_queue.c (renamed from drivers/usb/gadget/uvc_queue.c)0
-rw-r--r--drivers/usb/gadget/function/uvc_queue.h (renamed from drivers/usb/gadget/uvc_queue.h)0
-rw-r--r--drivers/usb/gadget/function/uvc_v4l2.c (renamed from drivers/usb/gadget/uvc_v4l2.c)0
-rw-r--r--drivers/usb/gadget/function/uvc_video.c (renamed from drivers/usb/gadget/uvc_video.c)0
-rw-r--r--drivers/usb/gadget/legacy/Kconfig475
-rw-r--r--drivers/usb/gadget/legacy/Makefile44
-rw-r--r--drivers/usb/gadget/legacy/acm_ms.c (renamed from drivers/usb/gadget/acm_ms.c)14
-rw-r--r--drivers/usb/gadget/legacy/audio.c (renamed from drivers/usb/gadget/audio.c)12
-rw-r--r--drivers/usb/gadget/legacy/cdc2.c (renamed from drivers/usb/gadget/cdc2.c)14
-rw-r--r--drivers/usb/gadget/legacy/dbgp.c (renamed from drivers/usb/gadget/dbgp.c)0
-rw-r--r--drivers/usb/gadget/legacy/ether.c (renamed from drivers/usb/gadget/ether.c)14
-rw-r--r--drivers/usb/gadget/legacy/g_ffs.c (renamed from drivers/usb/gadget/g_ffs.c)2
-rw-r--r--drivers/usb/gadget/legacy/gmidi.c (renamed from drivers/usb/gadget/gmidi.c)13
-rw-r--r--drivers/usb/gadget/legacy/hid.c (renamed from drivers/usb/gadget/hid.c)0
-rw-r--r--drivers/usb/gadget/legacy/inode.c (renamed from drivers/usb/gadget/inode.c)0
-rw-r--r--drivers/usb/gadget/legacy/mass_storage.c (renamed from drivers/usb/gadget/mass_storage.c)0
-rw-r--r--drivers/usb/gadget/legacy/multi.c (renamed from drivers/usb/gadget/multi.c)13
-rw-r--r--drivers/usb/gadget/legacy/ncm.c (renamed from drivers/usb/gadget/ncm.c)14
-rw-r--r--drivers/usb/gadget/legacy/nokia.c (renamed from drivers/usb/gadget/nokia.c)12
-rw-r--r--drivers/usb/gadget/legacy/printer.c (renamed from drivers/usb/gadget/printer.c)0
-rw-r--r--drivers/usb/gadget/legacy/serial.c (renamed from drivers/usb/gadget/serial.c)0
-rw-r--r--drivers/usb/gadget/legacy/tcm_usb_gadget.c (renamed from drivers/usb/gadget/tcm_usb_gadget.c)0
-rw-r--r--drivers/usb/gadget/legacy/tcm_usb_gadget.h (renamed from drivers/usb/gadget/tcm_usb_gadget.h)0
-rw-r--r--drivers/usb/gadget/legacy/webcam.c (renamed from drivers/usb/gadget/webcam.c)15
-rw-r--r--drivers/usb/gadget/legacy/zero.c (renamed from drivers/usb/gadget/zero.c)14
-rw-r--r--drivers/usb/gadget/net2280.c2905
-rw-r--r--drivers/usb/gadget/net2280.h308
-rw-r--r--drivers/usb/gadget/u_os_desc.h59
-rw-r--r--drivers/usb/gadget/udc/Kconfig385
-rw-r--r--drivers/usb/gadget/udc/Makefile31
-rw-r--r--drivers/usb/gadget/udc/amd5536udc.c (renamed from drivers/usb/gadget/amd5536udc.c)0
-rw-r--r--drivers/usb/gadget/udc/amd5536udc.h (renamed from drivers/usb/gadget/amd5536udc.h)0
-rw-r--r--drivers/usb/gadget/udc/at91_udc.c (renamed from drivers/usb/gadget/at91_udc.c)0
-rw-r--r--drivers/usb/gadget/udc/at91_udc.h (renamed from drivers/usb/gadget/at91_udc.h)0
-rw-r--r--drivers/usb/gadget/udc/atmel_usba_udc.c (renamed from drivers/usb/gadget/atmel_usba_udc.c)2
-rw-r--r--drivers/usb/gadget/udc/atmel_usba_udc.h (renamed from drivers/usb/gadget/atmel_usba_udc.h)0
-rw-r--r--drivers/usb/gadget/udc/bcm63xx_udc.c (renamed from drivers/usb/gadget/bcm63xx_udc.c)0
-rw-r--r--drivers/usb/gadget/udc/dummy_hcd.c (renamed from drivers/usb/gadget/dummy_hcd.c)0
-rw-r--r--drivers/usb/gadget/udc/fotg210-udc.c (renamed from drivers/usb/gadget/fotg210-udc.c)0
-rw-r--r--drivers/usb/gadget/udc/fotg210.h (renamed from drivers/usb/gadget/fotg210.h)0
-rw-r--r--drivers/usb/gadget/udc/fsl_mxc_udc.c (renamed from drivers/usb/gadget/fsl_mxc_udc.c)2
-rw-r--r--drivers/usb/gadget/udc/fsl_qe_udc.c (renamed from drivers/usb/gadget/fsl_qe_udc.c)19
-rw-r--r--drivers/usb/gadget/udc/fsl_qe_udc.h (renamed from drivers/usb/gadget/fsl_qe_udc.h)0
-rw-r--r--drivers/usb/gadget/udc/fsl_udc_core.c (renamed from drivers/usb/gadget/fsl_udc_core.c)19
-rw-r--r--drivers/usb/gadget/udc/fsl_usb2_udc.h (renamed from drivers/usb/gadget/fsl_usb2_udc.h)3
-rw-r--r--drivers/usb/gadget/udc/fusb300_udc.c (renamed from drivers/usb/gadget/fusb300_udc.c)4
-rw-r--r--drivers/usb/gadget/udc/fusb300_udc.h (renamed from drivers/usb/gadget/fusb300_udc.h)0
-rw-r--r--drivers/usb/gadget/udc/gadget_chips.h (renamed from drivers/usb/gadget/gadget_chips.h)0
-rw-r--r--drivers/usb/gadget/udc/goku_udc.c (renamed from drivers/usb/gadget/goku_udc.c)0
-rw-r--r--drivers/usb/gadget/udc/goku_udc.h (renamed from drivers/usb/gadget/goku_udc.h)0
-rw-r--r--drivers/usb/gadget/udc/gr_udc.c (renamed from drivers/usb/gadget/gr_udc.c)2
-rw-r--r--drivers/usb/gadget/udc/gr_udc.h (renamed from drivers/usb/gadget/gr_udc.h)0
-rw-r--r--drivers/usb/gadget/udc/lpc32xx_udc.c (renamed from drivers/usb/gadget/lpc32xx_udc.c)7
-rw-r--r--drivers/usb/gadget/udc/m66592-udc.c (renamed from drivers/usb/gadget/m66592-udc.c)4
-rw-r--r--drivers/usb/gadget/udc/m66592-udc.h (renamed from drivers/usb/gadget/m66592-udc.h)0
-rw-r--r--drivers/usb/gadget/udc/mv_u3d.h (renamed from drivers/usb/gadget/mv_u3d.h)0
-rw-r--r--drivers/usb/gadget/udc/mv_u3d_core.c (renamed from drivers/usb/gadget/mv_u3d_core.c)0
-rw-r--r--drivers/usb/gadget/udc/mv_udc.h (renamed from drivers/usb/gadget/mv_udc.h)0
-rw-r--r--drivers/usb/gadget/udc/mv_udc_core.c (renamed from drivers/usb/gadget/mv_udc_core.c)0
-rw-r--r--drivers/usb/gadget/udc/net2272.c (renamed from drivers/usb/gadget/net2272.c)2
-rw-r--r--drivers/usb/gadget/udc/net2272.h (renamed from drivers/usb/gadget/net2272.h)0
-rw-r--r--drivers/usb/gadget/udc/net2280.c3827
-rw-r--r--drivers/usb/gadget/udc/net2280.h403
-rw-r--r--drivers/usb/gadget/udc/omap_udc.c (renamed from drivers/usb/gadget/omap_udc.c)5
-rw-r--r--drivers/usb/gadget/udc/omap_udc.h (renamed from drivers/usb/gadget/omap_udc.h)0
-rw-r--r--drivers/usb/gadget/udc/pch_udc.c (renamed from drivers/usb/gadget/pch_udc.c)0
-rw-r--r--drivers/usb/gadget/udc/pxa25x_udc.c (renamed from drivers/usb/gadget/pxa25x_udc.c)75
-rw-r--r--drivers/usb/gadget/udc/pxa25x_udc.h (renamed from drivers/usb/gadget/pxa25x_udc.h)0
-rw-r--r--drivers/usb/gadget/udc/pxa27x_udc.c (renamed from drivers/usb/gadget/pxa27x_udc.c)6
-rw-r--r--drivers/usb/gadget/udc/pxa27x_udc.h (renamed from drivers/usb/gadget/pxa27x_udc.h)0
-rw-r--r--drivers/usb/gadget/udc/r8a66597-udc.c (renamed from drivers/usb/gadget/r8a66597-udc.c)92
-rw-r--r--drivers/usb/gadget/udc/r8a66597-udc.h (renamed from drivers/usb/gadget/r8a66597-udc.h)0
-rw-r--r--drivers/usb/gadget/udc/s3c-hsudc.c (renamed from drivers/usb/gadget/s3c-hsudc.c)0
-rw-r--r--drivers/usb/gadget/udc/s3c2410_udc.c (renamed from drivers/usb/gadget/s3c2410_udc.c)8
-rw-r--r--drivers/usb/gadget/udc/s3c2410_udc.h (renamed from drivers/usb/gadget/s3c2410_udc.h)0
-rw-r--r--drivers/usb/gadget/udc/udc-core.c (renamed from drivers/usb/gadget/udc-core.c)0
-rw-r--r--drivers/usb/musb/blackfin.c20
-rw-r--r--drivers/usb/musb/davinci.c20
-rw-r--r--drivers/usb/musb/jz4740.c3
-rw-r--r--drivers/usb/musb/musb_core.c41
-rw-r--r--drivers/usb/musb/musb_core.h12
-rw-r--r--drivers/usb/musb/musb_cppi41.c70
-rw-r--r--drivers/usb/musb/musb_dma.h1
-rw-r--r--drivers/usb/musb/musb_dsps.c104
-rw-r--r--drivers/usb/musb/musb_host.c19
-rw-r--r--drivers/usb/musb/musb_regs.h7
-rw-r--r--drivers/usb/musb/tusb6010.c16
-rw-r--r--drivers/usb/musb/ux500.c28
-rw-r--r--drivers/usb/phy/phy-am335x.c12
-rw-r--r--drivers/usb/phy/phy-gpio-vbus-usb.c45
-rw-r--r--drivers/usb/phy/phy-msm-usb.c12
-rw-r--r--drivers/usb/phy/phy-tegra-usb.c2
-rw-r--r--drivers/usb/renesas_usbhs/Makefile2
-rw-r--r--drivers/usb/renesas_usbhs/common.c66
-rw-r--r--drivers/usb/renesas_usbhs/common.h2
-rw-r--r--drivers/usb/renesas_usbhs/mod_gadget.c2
-rw-r--r--drivers/usb/renesas_usbhs/pipe.c11
-rw-r--r--drivers/usb/renesas_usbhs/pipe.h1
-rw-r--r--drivers/usb/renesas_usbhs/rcar2.c77
-rw-r--r--drivers/usb/renesas_usbhs/rcar2.h4
-rw-r--r--include/linux/usb/composite.h15
-rw-r--r--include/linux/usb/renesas_usbhs.h6
-rw-r--r--include/linux/usb/usb338x.h199
-rw-r--r--include/uapi/linux/usb/functionfs.h82
-rw-r--r--tools/usb/ffs-aio-example/multibuff/device_app/aio_multibuff.c39
-rw-r--r--tools/usb/ffs-aio-example/multibuff/host_app/test.c27
-rw-r--r--tools/usb/ffs-aio-example/simple/device_app/aio_simple.c39
-rw-r--r--tools/usb/ffs-aio-example/simple/host_app/test.c27
159 files changed, 7024 insertions, 4938 deletions
diff --git a/Documentation/DocBook/gadget.tmpl b/Documentation/DocBook/gadget.tmpl
index 2c425d70f7e2..641629221176 100644
--- a/Documentation/DocBook/gadget.tmpl
+++ b/Documentation/DocBook/gadget.tmpl
@@ -556,11 +556,11 @@ been converted to this framework.
Near-term plans include converting all of them, except for "gadgetfs".
</para>
-!Edrivers/usb/gadget/f_acm.c
-!Edrivers/usb/gadget/f_ecm.c
-!Edrivers/usb/gadget/f_subset.c
-!Edrivers/usb/gadget/f_obex.c
-!Edrivers/usb/gadget/f_serial.c
+!Edrivers/usb/gadget/function/f_acm.c
+!Edrivers/usb/gadget/function/f_ecm.c
+!Edrivers/usb/gadget/function/f_subset.c
+!Edrivers/usb/gadget/function/f_obex.c
+!Edrivers/usb/gadget/function/f_serial.c
</sect1>
diff --git a/drivers/usb/dwc3/core.c b/drivers/usb/dwc3/core.c
index eb69eb9f06c8..b769c1faaf03 100644
--- a/drivers/usb/dwc3/core.c
+++ b/drivers/usb/dwc3/core.c
@@ -386,6 +386,13 @@ static int dwc3_core_init(struct dwc3 *dwc)
}
dwc->revision = reg;
+ /* Handle USB2.0-only core configuration */
+ if (DWC3_GHWPARAMS3_SSPHY_IFC(dwc->hwparams.hwparams3) ==
+ DWC3_GHWPARAMS3_SSPHY_IFC_DIS) {
+ if (dwc->maximum_speed == USB_SPEED_SUPER)
+ dwc->maximum_speed = USB_SPEED_HIGH;
+ }
+
/* issue device SoftReset too */
timeout = jiffies + msecs_to_jiffies(500);
dwc3_writel(dwc->regs, DWC3_DCTL, DWC3_DCTL_CSFTRST);
@@ -656,6 +663,31 @@ static int dwc3_probe(struct platform_device *pdev)
return -ENODEV;
}
+ dwc->xhci_resources[0].start = res->start;
+ dwc->xhci_resources[0].end = dwc->xhci_resources[0].start +
+ DWC3_XHCI_REGS_END;
+ dwc->xhci_resources[0].flags = res->flags;
+ dwc->xhci_resources[0].name = res->name;
+
+ res->start += DWC3_GLOBALS_REGS_START;
+
+ /*
+ * Request memory region but exclude xHCI regs,
+ * since it will be requested by the xhci-plat driver.
+ */
+ regs = devm_ioremap_resource(dev, res);
+ if (IS_ERR(regs))
+ return PTR_ERR(regs);
+
+ dwc->regs = regs;
+ dwc->regs_size = resource_size(res);
+ /*
+ * 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 memory region the next time probe is called.
+ */
+ res->start -= DWC3_GLOBALS_REGS_START;
+
if (node) {
dwc->maximum_speed = of_usb_get_maximum_speed(node);
@@ -676,28 +708,9 @@ static int dwc3_probe(struct platform_device *pdev)
if (ret)
return ret;
- dwc->xhci_resources[0].start = res->start;
- dwc->xhci_resources[0].end = dwc->xhci_resources[0].start +
- DWC3_XHCI_REGS_END;
- dwc->xhci_resources[0].flags = res->flags;
- dwc->xhci_resources[0].name = res->name;
-
- res->start += DWC3_GLOBALS_REGS_START;
-
- /*
- * Request memory region but exclude xHCI regs,
- * since it will be requested by the xhci-plat driver.
- */
- regs = devm_ioremap_resource(dev, res);
- if (IS_ERR(regs))
- return PTR_ERR(regs);
-
spin_lock_init(&dwc->lock);
platform_set_drvdata(pdev, dwc);
- dwc->regs = regs;
- dwc->regs_size = resource_size(res);
-
dev->dma_mask = dev->parent->dma_mask;
dev->dma_parms = dev->parent->dma_parms;
dma_set_coherent_mask(dev, dev->parent->coherent_dma_mask);
diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h
index 57332e3768e4..48fb264065db 100644
--- a/drivers/usb/dwc3/core.h
+++ b/drivers/usb/dwc3/core.h
@@ -191,6 +191,19 @@
#define DWC3_GHWPARAMS1_PWROPT(n) ((n) << 24)
#define DWC3_GHWPARAMS1_PWROPT_MASK DWC3_GHWPARAMS1_PWROPT(3)
+/* Global HWPARAMS3 Register */
+#define DWC3_GHWPARAMS3_SSPHY_IFC(n) ((n) & 3)
+#define DWC3_GHWPARAMS3_SSPHY_IFC_DIS 0
+#define DWC3_GHWPARAMS3_SSPHY_IFC_ENA 1
+#define DWC3_GHWPARAMS3_HSPHY_IFC(n) (((n) & (3 << 2)) >> 2)
+#define DWC3_GHWPARAMS3_HSPHY_IFC_DIS 0
+#define DWC3_GHWPARAMS3_HSPHY_IFC_UTMI 1
+#define DWC3_GHWPARAMS3_HSPHY_IFC_ULPI 2
+#define DWC3_GHWPARAMS3_HSPHY_IFC_UTMI_ULPI 3
+#define DWC3_GHWPARAMS3_FSPHY_IFC(n) (((n) & (3 << 4)) >> 4)
+#define DWC3_GHWPARAMS3_FSPHY_IFC_DIS 0
+#define DWC3_GHWPARAMS3_FSPHY_IFC_ENA 1
+
/* Global HWPARAMS4 Register */
#define DWC3_GHWPARAMS4_HIBER_SCRATCHBUFS(n) (((n) & (0x0f << 13)) >> 13)
#define DWC3_MAX_HIBER_SCRATCHBUFS 15
diff --git a/drivers/usb/dwc3/dwc3-omap.c b/drivers/usb/dwc3/dwc3-omap.c
index 07a736acd0f2..ef4936ff626c 100644
--- a/drivers/usb/dwc3/dwc3-omap.c
+++ b/drivers/usb/dwc3/dwc3-omap.c
@@ -77,10 +77,6 @@
#define USBOTGSS_DEV_EBC_EN 0x0110
#define USBOTGSS_DEBUG_OFFSET 0x0600
-/* REVISION REGISTER */
-#define USBOTGSS_REVISION_XMAJOR(reg) ((reg >> 8) & 0x7)
-#define USBOTGSS_REVISION_XMAJOR1 1
-#define USBOTGSS_REVISION_XMAJOR2 2
/* SYSCONFIG REGISTER */
#define USBOTGSS_SYSCONFIG_DMADISABLE (1 << 16)
@@ -129,7 +125,6 @@ struct dwc3_omap {
u32 irq_eoi_offset;
u32 debug_offset;
u32 irq0_offset;
- u32 revision;
u32 dma_status:1;
@@ -383,6 +378,87 @@ static int dwc3_omap_vbus_notifier(struct notifier_block *nb,
return NOTIFY_DONE;
}
+static void dwc3_omap_map_offset(struct dwc3_omap *omap)
+{
+ struct device_node *node = omap->dev->of_node;
+
+ /*
+ * Differentiate between OMAP5 and AM437x.
+ *
+ * For OMAP5(ES2.0) and AM437x wrapper revision is same, even
+ * though there are changes in wrapper register offsets.
+ *
+ * Using dt compatible to differentiate AM437x.
+ */
+ if (of_device_is_compatible(node, "ti,am437x-dwc3")) {
+ omap->irq_eoi_offset = USBOTGSS_EOI_OFFSET;
+ omap->irq0_offset = USBOTGSS_IRQ0_OFFSET;
+ omap->irqmisc_offset = USBOTGSS_IRQMISC_OFFSET;
+ omap->utmi_otg_offset = USBOTGSS_UTMI_OTG_OFFSET;
+ omap->debug_offset = USBOTGSS_DEBUG_OFFSET;
+ }
+}
+
+static void dwc3_omap_set_utmi_mode(struct dwc3_omap *omap)
+{
+ u32 reg;
+ struct device_node *node = omap->dev->of_node;
+ int utmi_mode = 0;
+
+ reg = dwc3_omap_read_utmi_status(omap);
+
+ of_property_read_u32(node, "utmi-mode", &utmi_mode);
+
+ switch (utmi_mode) {
+ case DWC3_OMAP_UTMI_MODE_SW:
+ reg |= USBOTGSS_UTMI_OTG_STATUS_SW_MODE;
+ break;
+ case DWC3_OMAP_UTMI_MODE_HW:
+ reg &= ~USBOTGSS_UTMI_OTG_STATUS_SW_MODE;
+ break;
+ default:
+ dev_dbg(omap->dev, "UNKNOWN utmi mode %d\n", utmi_mode);
+ }
+
+ dwc3_omap_write_utmi_status(omap, reg);
+}
+
+static int dwc3_omap_extcon_register(struct dwc3_omap *omap)
+{
+ u32 ret;
+ struct device_node *node = omap->dev->of_node;
+ struct extcon_dev *edev;
+
+ if (of_property_read_bool(node, "extcon")) {
+ edev = extcon_get_edev_by_phandle(omap->dev, 0);
+ if (IS_ERR(edev)) {
+ dev_vdbg(omap->dev, "couldn't get extcon device\n");
+ return -EPROBE_DEFER;
+ }
+
+ omap->vbus_nb.notifier_call = dwc3_omap_vbus_notifier;
+ ret = extcon_register_interest(&omap->extcon_vbus_dev,
+ edev->name, "USB",
+ &omap->vbus_nb);
+ if (ret < 0)
+ dev_vdbg(omap->dev, "failed to register notifier for USB\n");
+
+ omap->id_nb.notifier_call = dwc3_omap_id_notifier;
+ ret = extcon_register_interest(&omap->extcon_id_dev,
+ edev->name, "USB-HOST",
+ &omap->id_nb);
+ if (ret < 0)
+ dev_vdbg(omap->dev, "failed to register notifier for USB-HOST\n");
+
+ if (extcon_get_cable_state(edev, "USB") == true)
+ dwc3_omap_set_mailbox(omap, OMAP_DWC3_VBUS_VALID);
+ if (extcon_get_cable_state(edev, "USB-HOST") == true)
+ dwc3_omap_set_mailbox(omap, OMAP_DWC3_ID_GROUND);
+ }
+
+ return 0;
+}
+
static int dwc3_omap_probe(struct platform_device *pdev)
{
struct device_node *node = pdev->dev.of_node;
@@ -390,15 +466,11 @@ static int dwc3_omap_probe(struct platform_device *pdev)
struct dwc3_omap *omap;
struct resource *res;
struct device *dev = &pdev->dev;
- struct extcon_dev *edev;
struct regulator *vbus_reg = NULL;
int ret;
int irq;
- int utmi_mode = 0;
- int x_major;
-
u32 reg;
void __iomem *base;
@@ -448,58 +520,8 @@ static int dwc3_omap_probe(struct platform_device *pdev)
goto err0;
}
- reg = dwc3_omap_readl(omap->base, USBOTGSS_REVISION);
- omap->revision = reg;
- x_major = USBOTGSS_REVISION_XMAJOR(reg);
-
- /* Differentiate between OMAP5 and AM437x */
- switch (x_major) {
- case USBOTGSS_REVISION_XMAJOR1:
- case USBOTGSS_REVISION_XMAJOR2:
- omap->irq_eoi_offset = 0;
- omap->irq0_offset = 0;
- omap->irqmisc_offset = 0;
- omap->utmi_otg_offset = 0;
- omap->debug_offset = 0;
- break;
- default:
- /* Default to the latest revision */
- omap->irq_eoi_offset = USBOTGSS_EOI_OFFSET;
- omap->irq0_offset = USBOTGSS_IRQ0_OFFSET;
- omap->irqmisc_offset = USBOTGSS_IRQMISC_OFFSET;
- omap->utmi_otg_offset = USBOTGSS_UTMI_OTG_OFFSET;
- omap->debug_offset = USBOTGSS_DEBUG_OFFSET;
- break;
- }
-
- /* For OMAP5(ES2.0) and AM437x x_major is 2 even though there are
- * changes in wrapper registers, Using dt compatible for aegis
- */
-
- if (of_device_is_compatible(node, "ti,am437x-dwc3")) {
- omap->irq_eoi_offset = USBOTGSS_EOI_OFFSET;
- omap->irq0_offset = USBOTGSS_IRQ0_OFFSET;
- omap->irqmisc_offset = USBOTGSS_IRQMISC_OFFSET;
- omap->utmi_otg_offset = USBOTGSS_UTMI_OTG_OFFSET;
- omap->debug_offset = USBOTGSS_DEBUG_OFFSET;
- }
-
- reg = dwc3_omap_read_utmi_status(omap);
-
- of_property_read_u32(node, "utmi-mode", &utmi_mode);
-
- switch (utmi_mode) {
- case DWC3_OMAP_UTMI_MODE_SW:
- reg |= USBOTGSS_UTMI_OTG_STATUS_SW_MODE;
- break;
- case DWC3_OMAP_UTMI_MODE_HW:
- reg &= ~USBOTGSS_UTMI_OTG_STATUS_SW_MODE;
- break;
- default:
- dev_dbg(dev, "UNKNOWN utmi mode %d\n", utmi_mode);
- }
-
- dwc3_omap_write_utmi_status(omap, reg);
+ dwc3_omap_map_offset(omap);
+ dwc3_omap_set_utmi_mode(omap);
/* check the DMA Status */
reg = dwc3_omap_readl(omap->base, USBOTGSS_SYSCONFIG);
@@ -515,31 +537,9 @@ static int dwc3_omap_probe(struct platform_device *pdev)
dwc3_omap_enable_irqs(omap);
- if (of_property_read_bool(node, "extcon")) {
- edev = extcon_get_edev_by_phandle(dev, 0);
- if (IS_ERR(edev)) {
- dev_vdbg(dev, "couldn't get extcon device\n");
- ret = -EPROBE_DEFER;
- goto err2;
- }
-
- omap->vbus_nb.notifier_call = dwc3_omap_vbus_notifier;
- ret = extcon_register_interest(&omap->extcon_vbus_dev,
- edev->name, "USB", &omap->vbus_nb);
- if (ret < 0)
- dev_vdbg(dev, "failed to register notifier for USB\n");
- omap->id_nb.notifier_call = dwc3_omap_id_notifier;
- ret = extcon_register_interest(&omap->extcon_id_dev, edev->name,
- "USB-HOST", &omap->id_nb);
- if (ret < 0)
- dev_vdbg(dev,
- "failed to register notifier for USB-HOST\n");
-
- if (extcon_get_cable_state(edev, "USB") == true)
- dwc3_omap_set_mailbox(omap, OMAP_DWC3_VBUS_VALID);
- if (extcon_get_cable_state(edev, "USB-HOST") == true)
- dwc3_omap_set_mailbox(omap, OMAP_DWC3_ID_GROUND);
- }
+ ret = dwc3_omap_extcon_register(omap);
+ if (ret < 0)
+ goto err2;
ret = of_platform_populate(node, NULL, NULL, dev);
if (ret) {
diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c
index dab7927d1009..349cacc577d8 100644
--- a/drivers/usb/dwc3/gadget.c
+++ b/drivers/usb/dwc3/gadget.c
@@ -1971,8 +1971,7 @@ static int dwc3_cleanup_done_reqs(struct dwc3 *dwc, struct dwc3_ep *dep,
}
static void dwc3_endpoint_transfer_complete(struct dwc3 *dwc,
- struct dwc3_ep *dep, const struct dwc3_event_depevt *event,
- int start_new)
+ struct dwc3_ep *dep, const struct dwc3_event_depevt *event)
{
unsigned status = 0;
int clean_busy;
@@ -2039,7 +2038,7 @@ static void dwc3_endpoint_interrupt(struct dwc3 *dwc,
return;
}
- dwc3_endpoint_transfer_complete(dwc, dep, event, 1);
+ dwc3_endpoint_transfer_complete(dwc, dep, event);
break;
case DWC3_DEPEVT_XFERINPROGRESS:
if (!usb_endpoint_xfer_isoc(dep->endpoint.desc)) {
@@ -2048,7 +2047,7 @@ static void dwc3_endpoint_interrupt(struct dwc3 *dwc,
return;
}
- dwc3_endpoint_transfer_complete(dwc, dep, event, 0);
+ dwc3_endpoint_transfer_complete(dwc, dep, event);
break;
case DWC3_DEPEVT_XFERNOTREADY:
if (usb_endpoint_xfer_isoc(dep->endpoint.desc)) {
diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig
index ba18e9c110cc..5c822afb6d70 100644
--- a/drivers/usb/gadget/Kconfig
+++ b/drivers/usb/gadget/Kconfig
@@ -127,368 +127,7 @@ config USB_GADGET_STORAGE_NUM_BUFFERS
a module parameter as well.
If unsure, say 2.
-#
-# USB Peripheral Controller Support
-#
-# The order here is alphabetical, except that integrated controllers go
-# before discrete ones so they will be the initial/default value:
-# - integrated/SOC controllers first
-# - licensed IP used in both SOC and discrete versions
-# - discrete ones (including all PCI-only controllers)
-# - debug/dummy gadget+hcd is last.
-#
-menu "USB Peripheral Controller"
-
-#
-# Integrated controllers
-#
-
-config USB_AT91
- tristate "Atmel AT91 USB Device Port"
- depends on ARCH_AT91
- help
- Many Atmel AT91 processors (such as the AT91RM2000) have a
- full speed USB Device Port with support for five configurable
- endpoints (plus endpoint zero).
-
- Say "y" to link the driver statically, or "m" to build a
- dynamically linked module called "at91_udc" and force all
- gadget drivers to also be dynamically linked.
-
-config USB_LPC32XX
- tristate "LPC32XX USB Peripheral Controller"
- depends on ARCH_LPC32XX && I2C
- select USB_ISP1301
- help
- This option selects the USB device controller in the LPC32xx SoC.
-
- Say "y" to link the driver statically, or "m" to build a
- dynamically linked module called "lpc32xx_udc" and force all
- gadget drivers to also be dynamically linked.
-
-config USB_ATMEL_USBA
- tristate "Atmel USBA"
- depends on AVR32 || ARCH_AT91
- help
- USBA is the integrated high-speed USB Device controller on
- the AT32AP700x, some AT91SAM9 and AT91CAP9 processors from Atmel.
-
-config USB_BCM63XX_UDC
- tristate "Broadcom BCM63xx Peripheral Controller"
- depends on BCM63XX
- help
- Many Broadcom BCM63xx chipsets (such as the BCM6328) have a
- high speed USB Device Port with support for four fixed endpoints
- (plus endpoint zero).
-
- Say "y" to link the driver statically, or "m" to build a
- dynamically linked module called "bcm63xx_udc".
-
-config USB_FSL_USB2
- tristate "Freescale Highspeed USB DR Peripheral Controller"
- depends on FSL_SOC || ARCH_MXC
- select USB_FSL_MPH_DR_OF if OF
- help
- Some of Freescale PowerPC and i.MX processors have a High Speed
- Dual-Role(DR) USB controller, which supports device mode.
-
- The number of programmable endpoints is different through
- SOC revisions.
-
- Say "y" to link the driver statically, or "m" to build a
- dynamically linked module called "fsl_usb2_udc" and force
- all gadget drivers to also be dynamically linked.
-
-config USB_FUSB300
- tristate "Faraday FUSB300 USB Peripheral Controller"
- depends on !PHYS_ADDR_T_64BIT && HAS_DMA
- help
- Faraday usb device controller FUSB300 driver
-
-config USB_FOTG210_UDC
- depends on HAS_DMA
- tristate "Faraday FOTG210 USB Peripheral Controller"
- help
- Faraday USB2.0 OTG controller which can be configured as
- high speed or full speed USB device. This driver supppors
- Bulk Transfer so far.
-
- Say "y" to link the driver statically, or "m" to build a
- dynamically linked module called "fotg210_udc".
-
-config USB_GR_UDC
- tristate "Aeroflex Gaisler GRUSBDC USB Peripheral Controller Driver"
- depends on HAS_DMA
- help
- Select this to support Aeroflex Gaisler GRUSBDC cores from the GRLIB
- VHDL IP core library.
-
-config USB_OMAP
- tristate "OMAP USB Device Controller"
- depends on ARCH_OMAP1
- depends on ISP1301_OMAP || !(MACH_OMAP_H2 || MACH_OMAP_H3)
- help
- Many Texas Instruments OMAP processors have flexible full
- speed USB device controllers, with support for up to 30
- endpoints (plus endpoint zero). This driver supports the
- controller in the OMAP 1611, and should work with controllers
- in other OMAP processors too, given minor tweaks.
-
- Say "y" to link the driver statically, or "m" to build a
- dynamically linked module called "omap_udc" and force all
- gadget drivers to also be dynamically linked.
-
-config USB_PXA25X
- tristate "PXA 25x or IXP 4xx"
- depends on (ARCH_PXA && PXA25x) || ARCH_IXP4XX
- help
- Intel's PXA 25x series XScale ARM-5TE processors include
- an integrated full speed USB 1.1 device controller. The
- controller in the IXP 4xx series is register-compatible.
-
- It has fifteen fixed-function endpoints, as well as endpoint
- zero (for control transfers).
-
- Say "y" to link the driver statically, or "m" to build a
- dynamically linked module called "pxa25x_udc" and force all
- gadget drivers to also be dynamically linked.
-
-# if there's only one gadget driver, using only two bulk endpoints,
-# don't waste memory for the other endpoints
-config USB_PXA25X_SMALL
- depends on USB_PXA25X
- bool
- default n if USB_ETH_RNDIS
- default y if USB_ZERO
- default y if USB_ETH
- default y if USB_G_SERIAL
-
-config USB_R8A66597
- tristate "Renesas R8A66597 USB Peripheral Controller"
- depends on HAS_DMA
- help
- R8A66597 is a discrete USB host and peripheral controller chip that
- supports both full and high speed USB 2.0 data transfers.
- It has nine configurable endpoints, and endpoint zero.
-
- Say "y" to link the driver statically, or "m" to build a
- dynamically linked module called "r8a66597_udc" and force all
- gadget drivers to also be dynamically linked.
-
-config USB_RENESAS_USBHS_UDC
- tristate 'Renesas USBHS controller'
- depends on USB_RENESAS_USBHS
- help
- Renesas USBHS is a discrete USB host and peripheral controller chip
- that supports both full and high speed USB 2.0 data transfers.
- It has nine or more configurable endpoints, and endpoint zero.
-
- Say "y" to link the driver statically, or "m" to build a
- dynamically linked module called "renesas_usbhs" and force all
- gadget drivers to also be dynamically linked.
-
-config USB_PXA27X
- tristate "PXA 27x"
- help
- Intel's PXA 27x series XScale ARM v5TE processors include
- an integrated full speed USB 1.1 device controller.
-
- It has up to 23 endpoints, as well as endpoint zero (for
- control transfers).
-
- Say "y" to link the driver statically, or "m" to build a
- dynamically linked module called "pxa27x_udc" and force all
- gadget drivers to also be dynamically linked.
-
-config USB_S3C2410
- tristate "S3C2410 USB Device Controller"
- depends on ARCH_S3C24XX
- help
- Samsung's S3C2410 is an ARM-4 processor with an integrated
- full speed USB 1.1 device controller. It has 4 configurable
- endpoints, as well as endpoint zero (for control transfers).
-
- This driver has been tested on the S3C2410, S3C2412, and
- S3C2440 processors.
-
-config USB_S3C2410_DEBUG
- boolean "S3C2410 udc debug messages"
- depends on USB_S3C2410
-
-config USB_S3C_HSUDC
- tristate "S3C2416, S3C2443 and S3C2450 USB Device Controller"
- depends on ARCH_S3C24XX
- help
- Samsung's S3C2416, S3C2443 and S3C2450 is an ARM9 based SoC
- integrated with dual speed USB 2.0 device controller. It has
- 8 endpoints, as well as endpoint zero.
-
- This driver has been tested on S3C2416 and S3C2450 processors.
-
-config USB_MV_UDC
- tristate "Marvell USB2.0 Device Controller"
- depends on HAS_DMA
- help
- Marvell Socs (including PXA and MMP series) include a high speed
- USB2.0 OTG controller, which can be configured as high speed or
- full speed USB peripheral.
-
-config USB_MV_U3D
- depends on HAS_DMA
- tristate "MARVELL PXA2128 USB 3.0 controller"
- help
- MARVELL PXA2128 Processor series include a super speed USB3.0 device
- controller, which support super speed USB peripheral.
-
-#
-# Controllers available in both integrated and discrete versions
-#
-
-config USB_M66592
- tristate "Renesas M66592 USB Peripheral Controller"
- help
- M66592 is a discrete USB peripheral controller chip that
- supports both full and high speed USB 2.0 data transfers.
- It has seven configurable endpoints, and endpoint zero.
-
- Say "y" to link the driver statically, or "m" to build a
- dynamically linked module called "m66592_udc" and force all
- gadget drivers to also be dynamically linked.
-
-#
-# Controllers available only in discrete form (and all PCI controllers)
-#
-
-config USB_AMD5536UDC
- tristate "AMD5536 UDC"
- depends on PCI
- help
- The AMD5536 UDC is part of the AMD Geode CS5536, an x86 southbridge.
- It is a USB Highspeed DMA capable USB device controller. Beside ep0
- it provides 4 IN and 4 OUT endpoints (bulk or interrupt type).
- The UDC port supports OTG operation, and may be used as a host port
- if it's not being used to implement peripheral or OTG roles.
-
- Say "y" to link the driver statically, or "m" to build a
- dynamically linked module called "amd5536udc" and force all
- gadget drivers to also be dynamically linked.
-
-config USB_FSL_QE
- tristate "Freescale QE/CPM USB Device Controller"
- depends on FSL_SOC && (QUICC_ENGINE || CPM)
- help
- Some of Freescale PowerPC processors have a Full Speed
- QE/CPM2 USB controller, which support device mode with 4
- programmable endpoints. This driver supports the
- controller in the MPC8360 and MPC8272, and should work with
- controllers having QE or CPM2, given minor tweaks.
-
- Set CONFIG_USB_GADGET to "m" to build this driver as a
- dynamically linked module called "fsl_qe_udc".
-
-config USB_NET2272
- tristate "PLX NET2272"
- help
- PLX NET2272 is a USB peripheral controller which supports
- both full and high speed USB 2.0 data transfers.
-
- It has three configurable endpoints, as well as endpoint zero
- (for control transfer).
- Say "y" to link the driver statically, or "m" to build a
- dynamically linked module called "net2272" and force all
- gadget drivers to also be dynamically linked.
-
-config USB_NET2272_DMA
- boolean "Support external DMA controller"
- depends on USB_NET2272 && HAS_DMA
- help
- The NET2272 part can optionally support an external DMA
- controller, but your board has to have support in the
- driver itself.
-
- If unsure, say "N" here. The driver works fine in PIO mode.
-
-config USB_NET2280
- tristate "NetChip 228x"
- depends on PCI
- help
- NetChip 2280 / 2282 is a PCI based USB peripheral controller which
- supports both full and high speed USB 2.0 data transfers.
-
- It has six configurable endpoints, as well as endpoint zero
- (for control transfers) and several endpoints with dedicated
- functions.
-
- Say "y" to link the driver statically, or "m" to build a
- dynamically linked module called "net2280" and force all
- gadget drivers to also be dynamically linked.
-
-config USB_GOKU
- tristate "Toshiba TC86C001 'Goku-S'"
- depends on PCI
- help
- The Toshiba TC86C001 is a PCI device which includes controllers
- for full speed USB devices, IDE, I2C, SIO, plus a USB host (OHCI).
-
- The device controller has three configurable (bulk or interrupt)
- endpoints, plus endpoint zero (for control transfers).
-
- Say "y" to link the driver statically, or "m" to build a
- dynamically linked module called "goku_udc" and to force all
- gadget drivers to also be dynamically linked.
-
-config USB_EG20T
- tristate "Intel EG20T PCH/LAPIS Semiconductor IOH(ML7213/ML7831) UDC"
- depends on PCI
- help
- This is a USB device driver for EG20T PCH.
- EG20T PCH is the platform controller hub that is used in Intel's
- general embedded platform. EG20T PCH has USB device interface.
- Using this interface, it is able to access system devices connected
- to USB device.
- This driver enables USB device function.
- USB device is a USB peripheral controller which
- supports both full and high speed USB 2.0 data transfers.
- This driver supports both control transfer and bulk transfer modes.
- This driver dose not support interrupt transfer or isochronous
- transfer modes.
-
- This driver also can be used for LAPIS Semiconductor's ML7213 which is
- for IVI(In-Vehicle Infotainment) use.
- ML7831 is for general purpose use.
- ML7213/ML7831 is companion chip for Intel Atom E6xx series.
- ML7213/ML7831 is completely compatible for Intel EG20T PCH.
-
-#
-# LAST -- dummy/emulated controller
-#
-
-config USB_DUMMY_HCD
- tristate "Dummy HCD (DEVELOPMENT)"
- depends on USB=y || (USB=m && USB_GADGET=m)
- help
- This host controller driver emulates USB, looping all data transfer
- requests back to a USB "gadget driver" in the same host. The host
- side is the master; the gadget side is the slave. Gadget drivers
- can be high, full, or low speed; and they have access to endpoints
- like those from NET2280, PXA2xx, or SA1100 hardware.
-
- This may help in some stages of creating a driver to embed in a
- Linux device, since it lets you debug several parts of the gadget
- driver without its hardware or drivers being involved.
-
- Since such a gadget side driver needs to interoperate with a host
- side Linux-USB device driver, this may help to debug both sides
- of a USB protocol stack.
-
- Say "y" to link the driver statically, or "m" to build a
- dynamically linked module called "dummy_hcd" and force all
- gadget drivers to also be dynamically linked.
-
-# NOTE: Please keep dummy_hcd LAST so that "real hardware" appears
-# first and will be selected by default.
-
-endmenu
+source "drivers/usb/gadget/udc/Kconfig"
#
# USB Gadget Drivers
@@ -714,466 +353,7 @@ config USB_CONFIGFS_F_FS
implemented in kernel space (for instance Ethernet, serial or
mass storage) and other are implemented in user space.
-config USB_ZERO
- tristate "Gadget Zero (DEVELOPMENT)"
- select USB_LIBCOMPOSITE
- select USB_F_SS_LB
- help
- Gadget Zero is a two-configuration device. It either sinks and
- sources bulk data; or it loops back a configurable number of
- transfers. It also implements control requests, for "chapter 9"
- conformance. The driver needs only two bulk-capable endpoints, so
- it can work on top of most device-side usb controllers. It's
- useful for testing, and is also a working example showing how
- USB "gadget drivers" can be written.
-
- Make this be the first driver you try using on top of any new
- USB peripheral controller driver. Then you can use host-side
- test software, like the "usbtest" driver, to put your hardware
- and its driver through a basic set of functional tests.
-
- Gadget Zero also works with the host-side "usb-skeleton" driver,
- and with many kinds of host-side test software. You may need
- to tweak product and vendor IDs before host software knows about
- this device, and arrange to select an appropriate configuration.
-
- Say "y" to link the driver statically, or "m" to build a
- dynamically linked module called "g_zero".
-
-config USB_ZERO_HNPTEST
- boolean "HNP Test Device"
- depends on USB_ZERO && USB_OTG
- help
- You can configure this device to enumerate using the device
- identifiers of the USB-OTG test device. That means that when
- this gadget connects to another OTG device, with this one using
- the "B-Peripheral" role, that device will use HNP to let this
- one serve as the USB host instead (in the "B-Host" role).
-
-config USB_AUDIO
- tristate "Audio Gadget"
- depends on SND
- select USB_LIBCOMPOSITE
- select SND_PCM
- help
- This Gadget Audio driver is compatible with USB Audio Class
- specification 2.0. It implements 1 AudioControl interface,
- 1 AudioStreaming Interface each for USB-OUT and USB-IN.
- Number of channels, sample rate and sample size can be
- specified as module parameters.
- This driver doesn't expect any real Audio codec to be present
- on the device - the audio streams are simply sinked to and
- sourced from a virtual ALSA sound card created. The user-space
- application may choose to do whatever it wants with the data
- received from the USB Host and choose to provide whatever it
- wants as audio data to the USB Host.
-
- Say "y" to link the driver statically, or "m" to build a
- dynamically linked module called "g_audio".
-
-config GADGET_UAC1
- bool "UAC 1.0 (Legacy)"
- depends on USB_AUDIO
- help
- If you instead want older UAC Spec-1.0 driver that also has audio
- paths hardwired to the Audio codec chip on-board and doesn't work
- without one.
-
-config USB_ETH
- tristate "Ethernet Gadget (with CDC Ethernet support)"
- depends on NET
- select USB_LIBCOMPOSITE
- select USB_U_ETHER
- select USB_F_ECM
- select USB_F_SUBSET
- select CRC32
- help
- This driver implements Ethernet style communication, in one of
- several ways:
-
- - The "Communication Device Class" (CDC) Ethernet Control Model.
- That protocol is often avoided with pure Ethernet adapters, in
- favor of simpler vendor-specific hardware, but is widely
- supported by firmware for smart network devices.
-
- - On hardware can't implement that protocol, a simple CDC subset
- is used, placing fewer demands on USB.
-
- - CDC Ethernet Emulation Model (EEM) is a newer standard that has
- a simpler interface that can be used by more USB hardware.
-
- RNDIS support is an additional option, more demanding than than
- subset.
-
- Within the USB device, this gadget driver exposes a network device
- "usbX", where X depends on what other networking devices you have.
- Treat it like a two-node Ethernet link: host, and gadget.
-
- The Linux-USB host-side "usbnet" driver interoperates with this
- driver, so that deep I/O queues can be supported. On 2.4 kernels,
- use "CDCEther" instead, if you're using the CDC option. That CDC
- mode should also interoperate with standard CDC Ethernet class
- drivers on other host operating systems.
-
- Say "y" to link the driver statically, or "m" to build a
- dynamically linked module called "g_ether".
-
-config USB_ETH_RNDIS
- bool "RNDIS support"
- depends on USB_ETH
- select USB_LIBCOMPOSITE
- select USB_F_RNDIS
- default y
- help
- Microsoft Windows XP bundles the "Remote NDIS" (RNDIS) protocol,
- and Microsoft provides redistributable binary RNDIS drivers for
- older versions of Windows.
-
- If you say "y" here, the Ethernet gadget driver will try to provide
- a second device configuration, supporting RNDIS to talk to such
- Microsoft USB hosts.
-
- To make MS-Windows work with this, use Documentation/usb/linux.inf
- as the "driver info file". For versions of MS-Windows older than
- XP, you'll need to download drivers from Microsoft's website; a URL
- is given in comments found in that info file.
-
-config USB_ETH_EEM
- bool "Ethernet Emulation Model (EEM) support"
- depends on USB_ETH
- select USB_LIBCOMPOSITE
- select USB_F_EEM
- default n
- help
- CDC EEM is a newer USB standard that is somewhat simpler than CDC ECM
- and therefore can be supported by more hardware. Technically ECM and
- EEM are designed for different applications. The ECM model extends
- the network interface to the target (e.g. a USB cable modem), and the
- EEM model is for mobile devices to communicate with hosts using
- ethernet over USB. For Linux gadgets, however, the interface with
- the host is the same (a usbX device), so the differences are minimal.
-
- If you say "y" here, the Ethernet gadget driver will use the EEM
- protocol rather than ECM. If unsure, say "n".
-
-config USB_G_NCM
- tristate "Network Control Model (NCM) support"
- depends on NET
- select USB_LIBCOMPOSITE
- select USB_U_ETHER
- select USB_F_NCM
- select CRC32
- help
- This driver implements USB CDC NCM subclass standard. NCM is
- an advanced protocol for Ethernet encapsulation, allows grouping
- of several ethernet frames into one USB transfer and different
- alignment possibilities.
-
- Say "y" to link the driver statically, or "m" to build a
- dynamically linked module called "g_ncm".
-
-config USB_GADGETFS
- tristate "Gadget Filesystem"
- help
- This driver provides a filesystem based API that lets user mode
- programs implement a single-configuration USB device, including
- endpoint I/O and control requests that don't relate to enumeration.
- All endpoints, transfer speeds, and transfer types supported by
- the hardware are available, through read() and write() calls.
-
- Say "y" to link the driver statically, or "m" to build a
- dynamically linked module called "gadgetfs".
-
-config USB_FUNCTIONFS
- tristate "Function Filesystem"
- select USB_LIBCOMPOSITE
- select USB_F_FS
- select USB_FUNCTIONFS_GENERIC if !(USB_FUNCTIONFS_ETH || USB_FUNCTIONFS_RNDIS)
- help
- The Function Filesystem (FunctionFS) lets one create USB
- composite functions in user space in the same way GadgetFS
- lets one create USB gadgets in user space. This allows creation
- of composite gadgets such that some of the functions are
- implemented in kernel space (for instance Ethernet, serial or
- mass storage) and other are implemented in user space.
-
- If you say "y" or "m" here you will be able what kind of
- configurations the gadget will provide.
-
- Say "y" to link the driver statically, or "m" to build
- a dynamically linked module called "g_ffs".
-
-config USB_FUNCTIONFS_ETH
- bool "Include configuration with CDC ECM (Ethernet)"
- depends on USB_FUNCTIONFS && NET
- select USB_U_ETHER
- select USB_F_ECM
- select USB_F_SUBSET
- help
- Include a configuration with CDC ECM function (Ethernet) and the
- Function Filesystem.
-
-config USB_FUNCTIONFS_RNDIS
- bool "Include configuration with RNDIS (Ethernet)"
- depends on USB_FUNCTIONFS && NET
- select USB_U_ETHER
- select USB_F_RNDIS
- help
- Include a configuration with RNDIS function (Ethernet) and the Filesystem.
-
-config USB_FUNCTIONFS_GENERIC
- bool "Include 'pure' configuration"
- depends on USB_FUNCTIONFS
- help
- Include a configuration with the Function Filesystem alone with
- no Ethernet interface.
-
-config USB_MASS_STORAGE
- tristate "Mass Storage Gadget"
- depends on BLOCK
- select USB_LIBCOMPOSITE
- select USB_F_MASS_STORAGE
- help
- The Mass Storage Gadget acts as a USB Mass Storage disk drive.
- As its storage repository it can use a regular file or a block
- device (in much the same way as the "loop" device driver),
- specified as a module parameter or sysfs option.
-
- This driver is a replacement for now removed File-backed
- Storage Gadget (g_file_storage).
-
- Say "y" to link the driver statically, or "m" to build
- a dynamically linked module called "g_mass_storage".
-
-config USB_GADGET_TARGET
- tristate "USB Gadget Target Fabric Module"
- depends on TARGET_CORE
- select USB_LIBCOMPOSITE
- help
- This fabric is an USB gadget. Two USB protocols are supported that is
- BBB or BOT (Bulk Only Transport) and UAS (USB Attached SCSI). BOT is
- advertised on alternative interface 0 (primary) and UAS is on
- alternative interface 1. Both protocols can work on USB2.0 and USB3.0.
- UAS utilizes the USB 3.0 feature called streams support.
-
-config USB_G_SERIAL
- tristate "Serial Gadget (with CDC ACM and CDC OBEX support)"
- depends on TTY
- select USB_U_SERIAL
- select USB_F_ACM
- select USB_F_SERIAL
- select USB_F_OBEX
- select USB_LIBCOMPOSITE
- help
- The Serial Gadget talks to the Linux-USB generic serial driver.
- This driver supports a CDC-ACM module option, which can be used
- to interoperate with MS-Windows hosts or with the Linux-USB
- "cdc-acm" driver.
-
- This driver also supports a CDC-OBEX option. You will need a
- user space OBEX server talking to /dev/ttyGS*, since the kernel
- itself doesn't implement the OBEX protocol.
-
- Say "y" to link the driver statically, or "m" to build a
- dynamically linked module called "g_serial".
-
- For more information, see Documentation/usb/gadget_serial.txt
- which includes instructions and a "driver info file" needed to
- make MS-Windows work with CDC ACM.
-
-config USB_MIDI_GADGET
- tristate "MIDI Gadget"
- depends on SND
- select USB_LIBCOMPOSITE
- select SND_RAWMIDI
- help
- The MIDI Gadget acts as a USB Audio device, with one MIDI
- input and one MIDI output. These MIDI jacks appear as
- a sound "card" in the ALSA sound system. Other MIDI
- connections can then be made on the gadget system, using
- ALSA's aconnect utility etc.
-
- Say "y" to link the driver statically, or "m" to build a
- dynamically linked module called "g_midi".
-
-config USB_G_PRINTER
- tristate "Printer Gadget"
- select USB_LIBCOMPOSITE
- help
- The Printer Gadget channels data between the USB host and a
- userspace program driving the print engine. The user space
- program reads and writes the device file /dev/g_printer to
- receive or send printer data. It can use ioctl calls to
- the device file to get or set printer status.
-
- Say "y" to link the driver statically, or "m" to build a
- dynamically linked module called "g_printer".
-
- For more information, see Documentation/usb/gadget_printer.txt
- which includes sample code for accessing the device file.
-
-if TTY
-
-config USB_CDC_COMPOSITE
- tristate "CDC Composite Device (Ethernet and ACM)"
- depends on NET
- select USB_LIBCOMPOSITE
- select USB_U_SERIAL
- select USB_U_ETHER
- select USB_F_ACM
- select USB_F_ECM
- help
- This driver provides two functions in one configuration:
- a CDC Ethernet (ECM) link, and a CDC ACM (serial port) link.
-
- This driver requires four bulk and two interrupt endpoints,
- plus the ability to handle altsettings. Not all peripheral
- controllers are that capable.
-
- Say "y" to link the driver statically, or "m" to build a
- dynamically linked module.
-
-config USB_G_NOKIA
- tristate "Nokia composite gadget"
- depends on PHONET
- select USB_LIBCOMPOSITE
- select USB_U_SERIAL
- select USB_U_ETHER
- select USB_F_ACM
- select USB_F_OBEX
- select USB_F_PHONET
- select USB_F_ECM
- help
- The Nokia composite gadget provides support for acm, obex
- and phonet in only one composite gadget driver.
-
- It's only really useful for N900 hardware. If you're building
- a kernel for N900, say Y or M here. If unsure, say N.
-
-config USB_G_ACM_MS
- tristate "CDC Composite Device (ACM and mass storage)"
- depends on BLOCK
- select USB_LIBCOMPOSITE
- select USB_U_SERIAL
- select USB_F_ACM
- select USB_F_MASS_STORAGE
- help
- This driver provides two functions in one configuration:
- a mass storage, and a CDC ACM (serial port) link.
-
- Say "y" to link the driver statically, or "m" to build a
- dynamically linked module called "g_acm_ms".
-
-config USB_G_MULTI
- tristate "Multifunction Composite Gadget"
- depends on BLOCK && NET
- select USB_G_MULTI_CDC if !USB_G_MULTI_RNDIS
- select USB_LIBCOMPOSITE
- select USB_U_SERIAL
- select USB_U_ETHER
- select USB_F_ACM
- select USB_F_MASS_STORAGE
- help
- The Multifunction Composite Gadget provides Ethernet (RNDIS
- and/or CDC Ethernet), mass storage and ACM serial link
- interfaces.
-
- You will be asked to choose which of the two configurations is
- to be available in the gadget. At least one configuration must
- be chosen to make the gadget usable. Selecting more than one
- configuration will prevent Windows from automatically detecting
- the gadget as a composite gadget, so an INF file will be needed to
- use the gadget.
-
- Say "y" to link the driver statically, or "m" to build a
- dynamically linked module called "g_multi".
-
-config USB_G_MULTI_RNDIS
- bool "RNDIS + CDC Serial + Storage configuration"
- depends on USB_G_MULTI
- select USB_F_RNDIS
- default y
- help
- This option enables a configuration with RNDIS, CDC Serial and
- Mass Storage functions available in the Multifunction Composite
- Gadget. This is the configuration dedicated for Windows since RNDIS
- is Microsoft's protocol.
-
- If unsure, say "y".
-
-config USB_G_MULTI_CDC
- bool "CDC Ethernet + CDC Serial + Storage configuration"
- depends on USB_G_MULTI
- default n
- select USB_F_ECM
- help
- This option enables a configuration with CDC Ethernet (ECM), CDC
- Serial and Mass Storage functions available in the Multifunction
- Composite Gadget.
-
- If unsure, say "y".
-
-endif # TTY
-
-config USB_G_HID
- tristate "HID Gadget"
- select USB_LIBCOMPOSITE
- help
- The HID gadget driver provides generic emulation of USB
- Human Interface Devices (HID).
-
- For more information, see Documentation/usb/gadget_hid.txt which
- includes sample code for accessing the device files.
-
- Say "y" to link the driver statically, or "m" to build a
- dynamically linked module called "g_hid".
-
-# Standalone / single function gadgets
-config USB_G_DBGP
- tristate "EHCI Debug Device Gadget"
- depends on TTY
- select USB_LIBCOMPOSITE
- help
- This gadget emulates an EHCI Debug device. This is useful when you want
- to interact with an EHCI Debug Port.
-
- Say "y" to link the driver statically, or "m" to build a
- dynamically linked module called "g_dbgp".
-
-if USB_G_DBGP
-choice
- prompt "EHCI Debug Device mode"
- default USB_G_DBGP_SERIAL
-
-config USB_G_DBGP_PRINTK
- depends on USB_G_DBGP
- bool "printk"
- help
- Directly printk() received data. No interaction.
-
-config USB_G_DBGP_SERIAL
- depends on USB_G_DBGP
- select USB_U_SERIAL
- bool "serial"
- help
- Userland can interact using /dev/ttyGSxxx.
-endchoice
-endif
-
-# put drivers that need isochronous transfer support (for audio
-# or video class gadget drivers), or specific hardware, here.
-config USB_G_WEBCAM
- tristate "USB Webcam Gadget"
- depends on VIDEO_DEV
- select USB_LIBCOMPOSITE
- select VIDEOBUF2_VMALLOC
- help
- The Webcam Gadget acts as a composite USB Audio and Video Class
- device. It provides a userspace API to process UVC control requests
- and stream video data to the host.
-
- Say "y" to link the driver statically, or "m" to build a
- dynamically linked module called "g_webcam".
+source "drivers/usb/gadget/legacy/Kconfig"
endchoice
diff --git a/drivers/usb/gadget/Makefile b/drivers/usb/gadget/Makefile
index 49514ea60a98..a186afeaa700 100644
--- a/drivers/usb/gadget/Makefile
+++ b/drivers/usb/gadget/Makefile
@@ -1,105 +1,12 @@
#
# USB peripheral controller drivers
#
-ccflags-$(CONFIG_USB_GADGET_DEBUG) := -DDEBUG
-ccflags-$(CONFIG_USB_GADGET_VERBOSE) += -DVERBOSE_DEBUG
+subdir-ccflags-$(CONFIG_USB_GADGET_DEBUG) := -DDEBUG
+subdir-ccflags-$(CONFIG_USB_GADGET_VERBOSE) += -DVERBOSE_DEBUG
+ccflags-y += -I$(PWD)/drivers/usb/gadget/udc
-obj-$(CONFIG_USB_GADGET) += udc-core.o
obj-$(CONFIG_USB_LIBCOMPOSITE) += libcomposite.o
libcomposite-y := usbstring.o config.o epautoconf.o
libcomposite-y += composite.o functions.o configfs.o u_f.o
-obj-$(CONFIG_USB_DUMMY_HCD) += dummy_hcd.o
-obj-$(CONFIG_USB_NET2272) += net2272.o
-obj-$(CONFIG_USB_NET2280) += net2280.o
-obj-$(CONFIG_USB_AMD5536UDC) += amd5536udc.o
-obj-$(CONFIG_USB_PXA25X) += pxa25x_udc.o
-obj-$(CONFIG_USB_PXA27X) += pxa27x_udc.o
-obj-$(CONFIG_USB_GOKU) += goku_udc.o
-obj-$(CONFIG_USB_OMAP) += omap_udc.o
-obj-$(CONFIG_USB_S3C2410) += s3c2410_udc.o
-obj-$(CONFIG_USB_AT91) += at91_udc.o
-obj-$(CONFIG_USB_ATMEL_USBA) += atmel_usba_udc.o
-obj-$(CONFIG_USB_BCM63XX_UDC) += bcm63xx_udc.o
-obj-$(CONFIG_USB_FSL_USB2) += fsl_usb2_udc.o
-fsl_usb2_udc-y := fsl_udc_core.o
-fsl_usb2_udc-$(CONFIG_ARCH_MXC) += fsl_mxc_udc.o
-obj-$(CONFIG_USB_M66592) += m66592-udc.o
-obj-$(CONFIG_USB_R8A66597) += r8a66597-udc.o
-obj-$(CONFIG_USB_FSL_QE) += fsl_qe_udc.o
-obj-$(CONFIG_USB_S3C_HSUDC) += s3c-hsudc.o
-obj-$(CONFIG_USB_LPC32XX) += lpc32xx_udc.o
-obj-$(CONFIG_USB_EG20T) += pch_udc.o
-obj-$(CONFIG_USB_MV_UDC) += mv_udc.o
-mv_udc-y := mv_udc_core.o
-obj-$(CONFIG_USB_FUSB300) += fusb300_udc.o
-obj-$(CONFIG_USB_FOTG210_UDC) += fotg210-udc.o
-obj-$(CONFIG_USB_MV_U3D) += mv_u3d_core.o
-obj-$(CONFIG_USB_GR_UDC) += gr_udc.o
-# USB Functions
-usb_f_acm-y := f_acm.o
-obj-$(CONFIG_USB_F_ACM) += usb_f_acm.o
-usb_f_ss_lb-y := f_loopback.o f_sourcesink.o
-obj-$(CONFIG_USB_F_SS_LB) += usb_f_ss_lb.o
-obj-$(CONFIG_USB_U_SERIAL) += u_serial.o
-usb_f_serial-y := f_serial.o
-obj-$(CONFIG_USB_F_SERIAL) += usb_f_serial.o
-usb_f_obex-y := f_obex.o
-obj-$(CONFIG_USB_F_OBEX) += usb_f_obex.o
-obj-$(CONFIG_USB_U_ETHER) += u_ether.o
-usb_f_ncm-y := f_ncm.o
-obj-$(CONFIG_USB_F_NCM) += usb_f_ncm.o
-usb_f_ecm-y := f_ecm.o
-obj-$(CONFIG_USB_F_ECM) += usb_f_ecm.o
-usb_f_phonet-y := f_phonet.o
-obj-$(CONFIG_USB_F_PHONET) += usb_f_phonet.o
-usb_f_eem-y := f_eem.o
-obj-$(CONFIG_USB_F_EEM) += usb_f_eem.o
-usb_f_ecm_subset-y := f_subset.o
-obj-$(CONFIG_USB_F_SUBSET) += usb_f_ecm_subset.o
-usb_f_rndis-y := f_rndis.o rndis.o
-obj-$(CONFIG_USB_F_RNDIS) += usb_f_rndis.o
-usb_f_mass_storage-y := f_mass_storage.o storage_common.o
-obj-$(CONFIG_USB_F_MASS_STORAGE)+= usb_f_mass_storage.o
-usb_f_fs-y := f_fs.o
-obj-$(CONFIG_USB_F_FS) += usb_f_fs.o
-
-#
-# USB gadget drivers
-#
-g_zero-y := zero.o
-g_audio-y := audio.o
-g_ether-y := ether.o
-g_serial-y := serial.o
-g_midi-y := gmidi.o
-gadgetfs-y := inode.o
-g_mass_storage-y := mass_storage.o
-g_printer-y := printer.o
-g_cdc-y := cdc2.o
-g_multi-y := multi.o
-g_hid-y := hid.o
-g_dbgp-y := dbgp.o
-g_nokia-y := nokia.o
-g_webcam-y := webcam.o
-g_ncm-y := ncm.o
-g_acm_ms-y := acm_ms.o
-g_tcm_usb_gadget-y := tcm_usb_gadget.o
-
-obj-$(CONFIG_USB_ZERO) += g_zero.o
-obj-$(CONFIG_USB_AUDIO) += g_audio.o
-obj-$(CONFIG_USB_ETH) += g_ether.o
-obj-$(CONFIG_USB_GADGETFS) += gadgetfs.o
-obj-$(CONFIG_USB_FUNCTIONFS) += g_ffs.o
-obj-$(CONFIG_USB_MASS_STORAGE) += g_mass_storage.o
-obj-$(CONFIG_USB_G_SERIAL) += g_serial.o
-obj-$(CONFIG_USB_G_PRINTER) += g_printer.o
-obj-$(CONFIG_USB_MIDI_GADGET) += g_midi.o
-obj-$(CONFIG_USB_CDC_COMPOSITE) += g_cdc.o
-obj-$(CONFIG_USB_G_HID) += g_hid.o
-obj-$(CONFIG_USB_G_DBGP) += g_dbgp.o
-obj-$(CONFIG_USB_G_MULTI) += g_multi.o
-obj-$(CONFIG_USB_G_NOKIA) += g_nokia.o
-obj-$(CONFIG_USB_G_WEBCAM) += g_webcam.o
-obj-$(CONFIG_USB_G_NCM) += g_ncm.o
-obj-$(CONFIG_USB_G_ACM_MS) += g_acm_ms.o
-obj-$(CONFIG_USB_GADGET_TARGET) += tcm_usb_gadget.o
+obj-$(CONFIG_USB_GADGET) += udc/ function/ legacy/
diff --git a/drivers/usb/gadget/composite.c b/drivers/usb/gadget/composite.c
index f80151932053..6935a822ce2b 100644
--- a/drivers/usb/gadget/composite.c
+++ b/drivers/usb/gadget/composite.c
@@ -1956,6 +1956,7 @@ void composite_dev_cleanup(struct usb_composite_dev *cdev)
}
if (cdev->req) {
kfree(cdev->req->buf);
+ usb_ep_dequeue(cdev->gadget->ep0, cdev->req);
usb_ep_free_request(cdev->gadget->ep0, cdev->req);
}
cdev->next_string_id = 0;
diff --git a/drivers/usb/gadget/configfs.c b/drivers/usb/gadget/configfs.c
index 97142146eead..811c2c7cc269 100644
--- a/drivers/usb/gadget/configfs.c
+++ b/drivers/usb/gadget/configfs.c
@@ -1021,12 +1021,10 @@ static ssize_t ext_prop_data_store(struct usb_os_desc_ext_prop *ext_prop,
if (page[len - 1] == '\n' || page[len - 1] == '\0')
--len;
- new_data = kzalloc(len, GFP_KERNEL);
+ new_data = kmemdup(page, len, GFP_KERNEL);
if (!new_data)
return -ENOMEM;
- memcpy(new_data, page, len);
-
if (desc->opts_mutex)
mutex_lock(desc->opts_mutex);
kfree(ext_prop->data);
diff --git a/drivers/usb/gadget/function/Makefile b/drivers/usb/gadget/function/Makefile
new file mode 100644
index 000000000000..6d91f21b52a6
--- /dev/null
+++ b/drivers/usb/gadget/function/Makefile
@@ -0,0 +1,34 @@
+#
+# USB peripheral controller drivers
+#
+
+ccflags-y := -I$(PWD)/drivers/usb/gadget/
+ccflags-y += -I$(PWD)/drivers/usb/gadget/udc/
+
+# USB Functions
+usb_f_acm-y := f_acm.o
+obj-$(CONFIG_USB_F_ACM) += usb_f_acm.o
+usb_f_ss_lb-y := f_loopback.o f_sourcesink.o
+obj-$(CONFIG_USB_F_SS_LB) += usb_f_ss_lb.o
+obj-$(CONFIG_USB_U_SERIAL) += u_serial.o
+usb_f_serial-y := f_serial.o
+obj-$(CONFIG_USB_F_SERIAL) += usb_f_serial.o
+usb_f_obex-y := f_obex.o
+obj-$(CONFIG_USB_F_OBEX) += usb_f_obex.o
+obj-$(CONFIG_USB_U_ETHER) += u_ether.o
+usb_f_ncm-y := f_ncm.o
+obj-$(CONFIG_USB_F_NCM) += usb_f_ncm.o
+usb_f_ecm-y := f_ecm.o
+obj-$(CONFIG_USB_F_ECM) += usb_f_ecm.o
+usb_f_phonet-y := f_phonet.o
+obj-$(CONFIG_USB_F_PHONET) += usb_f_phonet.o
+usb_f_eem-y := f_eem.o
+obj-$(CONFIG_USB_F_EEM) += usb_f_eem.o
+usb_f_ecm_subset-y := f_subset.o
+obj-$(CONFIG_USB_F_SUBSET) += usb_f_ecm_subset.o
+usb_f_rndis-y := f_rndis.o rndis.o
+obj-$(CONFIG_USB_F_RNDIS) += usb_f_rndis.o
+usb_f_mass_storage-y := f_mass_storage.o storage_common.o
+obj-$(CONFIG_USB_F_MASS_STORAGE)+= usb_f_mass_storage.o
+usb_f_fs-y := f_fs.o
+obj-$(CONFIG_USB_F_FS) += usb_f_fs.o
diff --git a/drivers/usb/gadget/f_acm.c b/drivers/usb/gadget/function/f_acm.c
index ab1065afbbd0..ab1065afbbd0 100644
--- a/drivers/usb/gadget/f_acm.c
+++ b/drivers/usb/gadget/function/f_acm.c
diff --git a/drivers/usb/gadget/f_ecm.c b/drivers/usb/gadget/function/f_ecm.c
index 798760fa7e70..798760fa7e70 100644
--- a/drivers/usb/gadget/f_ecm.c
+++ b/drivers/usb/gadget/function/f_ecm.c
diff --git a/drivers/usb/gadget/f_eem.c b/drivers/usb/gadget/function/f_eem.c
index d61c11d765d0..4d8b236ea608 100644
--- a/drivers/usb/gadget/f_eem.c
+++ b/drivers/usb/gadget/function/f_eem.c
@@ -355,20 +355,18 @@ static struct sk_buff *eem_wrap(struct gether *port, struct sk_buff *skb)
int padlen = 0;
u16 len = skb->len;
- if (!skb_cloned(skb)) {
- int headroom = skb_headroom(skb);
- int tailroom = skb_tailroom(skb);
+ int headroom = skb_headroom(skb);
+ int tailroom = skb_tailroom(skb);
- /* When (len + EEM_HLEN + ETH_FCS_LEN) % in->maxpacket) is 0,
- * stick two bytes of zero-length EEM packet on the end.
- */
- if (((len + EEM_HLEN + ETH_FCS_LEN) % in->maxpacket) == 0)
- padlen += 2;
+ /* When (len + EEM_HLEN + ETH_FCS_LEN) % in->maxpacket) is 0,
+ * stick two bytes of zero-length EEM packet on the end.
+ */
+ if (((len + EEM_HLEN + ETH_FCS_LEN) % in->maxpacket) == 0)
+ padlen += 2;
- if ((tailroom >= (ETH_FCS_LEN + padlen)) &&
- (headroom >= EEM_HLEN))
- goto done;
- }
+ if ((tailroom >= (ETH_FCS_LEN + padlen)) &&
+ (headroom >= EEM_HLEN) && !skb_cloned(skb))
+ goto done;
skb2 = skb_copy_expand(skb, EEM_HLEN, ETH_FCS_LEN + padlen, GFP_ATOMIC);
dev_kfree_skb_any(skb);
diff --git a/drivers/usb/gadget/f_fs.c b/drivers/usb/gadget/function/f_fs.c
index 8598c27c7d43..dc30adf15a01 100644
--- a/drivers/usb/gadget/f_fs.c
+++ b/drivers/usb/gadget/function/f_fs.c
@@ -34,6 +34,7 @@
#include "u_fs.h"
#include "u_f.h"
+#include "u_os_desc.h"
#include "configfs.h"
#define FUNCTIONFS_MAGIC 0xa647361 /* Chosen by a honest dice roll ;) */
@@ -1646,13 +1647,22 @@ enum ffs_entity_type {
FFS_DESCRIPTOR, FFS_INTERFACE, FFS_STRING, FFS_ENDPOINT
};
+enum ffs_os_desc_type {
+ FFS_OS_DESC, FFS_OS_DESC_EXT_COMPAT, FFS_OS_DESC_EXT_PROP
+};
+
typedef int (*ffs_entity_callback)(enum ffs_entity_type entity,
u8 *valuep,
struct usb_descriptor_header *desc,
void *priv);
-static int __must_check ffs_do_desc(char *data, unsigned len,
- ffs_entity_callback entity, void *priv)
+typedef int (*ffs_os_desc_callback)(enum ffs_os_desc_type entity,
+ struct usb_os_desc_header *h, void *data,
+ unsigned len, void *priv);
+
+static int __must_check ffs_do_single_desc(char *data, unsigned len,
+ ffs_entity_callback entity,
+ void *priv)
{
struct usb_descriptor_header *_ds = (void *)data;
u8 length;
@@ -1804,7 +1814,7 @@ static int __must_check ffs_do_descs(unsigned count, char *data, unsigned len,
if (!data)
return _len - len;
- ret = ffs_do_desc(data, len, entity, priv);
+ ret = ffs_do_single_desc(data, len, entity, priv);
if (unlikely(ret < 0)) {
pr_debug("%s returns %d\n", __func__, ret);
return ret;
@@ -1857,11 +1867,191 @@ static int __ffs_data_do_entity(enum ffs_entity_type type,
return 0;
}
+static int __ffs_do_os_desc_header(enum ffs_os_desc_type *next_type,
+ struct usb_os_desc_header *desc)
+{
+ u16 bcd_version = le16_to_cpu(desc->bcdVersion);
+ u16 w_index = le16_to_cpu(desc->wIndex);
+
+ if (bcd_version != 1) {
+ pr_vdebug("unsupported os descriptors version: %d",
+ bcd_version);
+ return -EINVAL;
+ }
+ switch (w_index) {
+ case 0x4:
+ *next_type = FFS_OS_DESC_EXT_COMPAT;
+ break;
+ case 0x5:
+ *next_type = FFS_OS_DESC_EXT_PROP;
+ break;
+ default:
+ pr_vdebug("unsupported os descriptor type: %d", w_index);
+ return -EINVAL;
+ }
+
+ return sizeof(*desc);
+}
+
+/*
+ * Process all extended compatibility/extended property descriptors
+ * of a feature descriptor
+ */
+static int __must_check ffs_do_single_os_desc(char *data, unsigned len,
+ enum ffs_os_desc_type type,
+ u16 feature_count,
+ ffs_os_desc_callback entity,
+ void *priv,
+ struct usb_os_desc_header *h)
+{
+ int ret;
+ const unsigned _len = len;
+
+ ENTER();
+
+ /* loop over all ext compat/ext prop descriptors */
+ while (feature_count--) {
+ ret = entity(type, h, data, len, priv);
+ if (unlikely(ret < 0)) {
+ pr_debug("bad OS descriptor, type: %d\n", type);
+ return ret;
+ }
+ data += ret;
+ len -= ret;
+ }
+ return _len - len;
+}
+
+/* Process a number of complete Feature Descriptors (Ext Compat or Ext Prop) */
+static int __must_check ffs_do_os_descs(unsigned count,
+ char *data, unsigned len,
+ ffs_os_desc_callback entity, void *priv)
+{
+ const unsigned _len = len;
+ unsigned long num = 0;
+
+ ENTER();
+
+ for (num = 0; num < count; ++num) {
+ int ret;
+ enum ffs_os_desc_type type;
+ u16 feature_count;
+ struct usb_os_desc_header *desc = (void *)data;
+
+ if (len < sizeof(*desc))
+ return -EINVAL;
+
+ /*
+ * Record "descriptor" entity.
+ * Process dwLength, bcdVersion, wIndex, get b/wCount.
+ * Move the data pointer to the beginning of extended
+ * compatibilities proper or extended properties proper
+ * portions of the data
+ */
+ if (le32_to_cpu(desc->dwLength) > len)
+ return -EINVAL;
+
+ ret = __ffs_do_os_desc_header(&type, desc);
+ if (unlikely(ret < 0)) {
+ pr_debug("entity OS_DESCRIPTOR(%02lx); ret = %d\n",
+ num, ret);
+ return ret;
+ }
+ /*
+ * 16-bit hex "?? 00" Little Endian looks like 8-bit hex "??"
+ */
+ feature_count = le16_to_cpu(desc->wCount);
+ if (type == FFS_OS_DESC_EXT_COMPAT &&
+ (feature_count > 255 || desc->Reserved))
+ return -EINVAL;
+ len -= ret;
+ data += ret;
+
+ /*
+ * Process all function/property descriptors
+ * of this Feature Descriptor
+ */
+ ret = ffs_do_single_os_desc(data, len, type,
+ feature_count, entity, priv, desc);
+ if (unlikely(ret < 0)) {
+ pr_debug("%s returns %d\n", __func__, ret);
+ return ret;
+ }
+
+ len -= ret;
+ data += ret;
+ }
+ return _len - len;
+}
+
+/**
+ * Validate contents of the buffer from userspace related to OS descriptors.
+ */
+static int __ffs_data_do_os_desc(enum ffs_os_desc_type type,
+ struct usb_os_desc_header *h, void *data,
+ unsigned len, void *priv)
+{
+ struct ffs_data *ffs = priv;
+ u8 length;
+
+ ENTER();
+
+ switch (type) {
+ case FFS_OS_DESC_EXT_COMPAT: {
+ struct usb_ext_compat_desc *d = data;
+ int i;
+
+ if (len < sizeof(*d) ||
+ d->bFirstInterfaceNumber >= ffs->interfaces_count ||
+ d->Reserved1)
+ return -EINVAL;
+ for (i = 0; i < ARRAY_SIZE(d->Reserved2); ++i)
+ if (d->Reserved2[i])
+ return -EINVAL;
+
+ length = sizeof(struct usb_ext_compat_desc);
+ }
+ break;
+ case FFS_OS_DESC_EXT_PROP: {
+ struct usb_ext_prop_desc *d = data;
+ u32 type, pdl;
+ u16 pnl;
+
+ if (len < sizeof(*d) || h->interface >= ffs->interfaces_count)
+ return -EINVAL;
+ length = le32_to_cpu(d->dwSize);
+ type = le32_to_cpu(d->dwPropertyDataType);
+ if (type < USB_EXT_PROP_UNICODE ||
+ type > USB_EXT_PROP_UNICODE_MULTI) {
+ pr_vdebug("unsupported os descriptor property type: %d",
+ type);
+ return -EINVAL;
+ }
+ pnl = le16_to_cpu(d->wPropertyNameLength);
+ pdl = le32_to_cpu(*(u32 *)((u8 *)data + 10 + pnl));
+ if (length != 14 + pnl + pdl) {
+ pr_vdebug("invalid os descriptor length: %d pnl:%d pdl:%d (descriptor %d)\n",
+ length, pnl, pdl, type);
+ return -EINVAL;
+ }
+ ++ffs->ms_os_descs_ext_prop_count;
+ /* property name reported to the host as "WCHAR"s */
+ ffs->ms_os_descs_ext_prop_name_len += pnl * 2;
+ ffs->ms_os_descs_ext_prop_data_len += pdl;
+ }
+ break;
+ default:
+ pr_vdebug("unknown descriptor: %d\n", type);
+ return -EINVAL;
+ }
+ return length;
+}
+
static int __ffs_data_got_descs(struct ffs_data *ffs,
char *const _data, size_t len)
{
char *data = _data, *raw_descs;
- unsigned counts[3], flags;
+ unsigned os_descs_count = 0, counts[3], flags;
int ret = -EINVAL, i;
ENTER();
@@ -1879,7 +2069,8 @@ static int __ffs_data_got_descs(struct ffs_data *ffs,
flags = get_unaligned_le32(data + 8);
if (flags & ~(FUNCTIONFS_HAS_FS_DESC |
FUNCTIONFS_HAS_HS_DESC |
- FUNCTIONFS_HAS_SS_DESC)) {
+ FUNCTIONFS_HAS_SS_DESC |
+ FUNCTIONFS_HAS_MS_OS_DESC)) {
ret = -ENOSYS;
goto error;
}
@@ -1902,6 +2093,11 @@ static int __ffs_data_got_descs(struct ffs_data *ffs,
len -= 4;
}
}
+ if (flags & (1 << i)) {
+ os_descs_count = get_unaligned_le32(data);
+ data += 4;
+ len -= 4;
+ };
/* Read descriptors */
raw_descs = data;
@@ -1915,6 +2111,14 @@ static int __ffs_data_got_descs(struct ffs_data *ffs,
data += ret;
len -= ret;
}
+ if (os_descs_count) {
+ ret = ffs_do_os_descs(os_descs_count, data, len,
+ __ffs_data_do_os_desc, ffs);
+ if (ret < 0)
+ goto error;
+ data += ret;
+ len -= ret;
+ }
if (raw_descs == data || len) {
ret = -EINVAL;
@@ -1927,6 +2131,7 @@ static int __ffs_data_got_descs(struct ffs_data *ffs,
ffs->fs_descs_count = counts[0];
ffs->hs_descs_count = counts[1];
ffs->ss_descs_count = counts[2];
+ ffs->ms_os_descs_count = os_descs_count;
return 0;
@@ -2268,6 +2473,85 @@ static int __ffs_func_bind_do_nums(enum ffs_entity_type type, u8 *valuep,
return 0;
}
+static int __ffs_func_bind_do_os_desc(enum ffs_os_desc_type type,
+ struct usb_os_desc_header *h, void *data,
+ unsigned len, void *priv)
+{
+ struct ffs_function *func = priv;
+ u8 length = 0;
+
+ switch (type) {
+ case FFS_OS_DESC_EXT_COMPAT: {
+ struct usb_ext_compat_desc *desc = data;
+ struct usb_os_desc_table *t;
+
+ t = &func->function.os_desc_table[desc->bFirstInterfaceNumber];
+ t->if_id = func->interfaces_nums[desc->bFirstInterfaceNumber];
+ memcpy(t->os_desc->ext_compat_id, &desc->CompatibleID,
+ ARRAY_SIZE(desc->CompatibleID) +
+ ARRAY_SIZE(desc->SubCompatibleID));
+ length = sizeof(*desc);
+ }
+ break;
+ case FFS_OS_DESC_EXT_PROP: {
+ struct usb_ext_prop_desc *desc = data;
+ struct usb_os_desc_table *t;
+ struct usb_os_desc_ext_prop *ext_prop;
+ char *ext_prop_name;
+ char *ext_prop_data;
+
+ t = &func->function.os_desc_table[h->interface];
+ t->if_id = func->interfaces_nums[h->interface];
+
+ ext_prop = func->ffs->ms_os_descs_ext_prop_avail;
+ func->ffs->ms_os_descs_ext_prop_avail += sizeof(*ext_prop);
+
+ ext_prop->type = le32_to_cpu(desc->dwPropertyDataType);
+ ext_prop->name_len = le16_to_cpu(desc->wPropertyNameLength);
+ ext_prop->data_len = le32_to_cpu(*(u32 *)
+ usb_ext_prop_data_len_ptr(data, ext_prop->name_len));
+ length = ext_prop->name_len + ext_prop->data_len + 14;
+
+ ext_prop_name = func->ffs->ms_os_descs_ext_prop_name_avail;
+ func->ffs->ms_os_descs_ext_prop_name_avail +=
+ ext_prop->name_len;
+
+ ext_prop_data = func->ffs->ms_os_descs_ext_prop_data_avail;
+ func->ffs->ms_os_descs_ext_prop_data_avail +=
+ ext_prop->data_len;
+ memcpy(ext_prop_data,
+ usb_ext_prop_data_ptr(data, ext_prop->name_len),
+ ext_prop->data_len);
+ /* unicode data reported to the host as "WCHAR"s */
+ switch (ext_prop->type) {
+ case USB_EXT_PROP_UNICODE:
+ case USB_EXT_PROP_UNICODE_ENV:
+ case USB_EXT_PROP_UNICODE_LINK:
+ case USB_EXT_PROP_UNICODE_MULTI:
+ ext_prop->data_len *= 2;
+ break;
+ }
+ ext_prop->data = ext_prop_data;
+
+ memcpy(ext_prop_name, usb_ext_prop_name_ptr(data),
+ ext_prop->name_len);
+ /* property name reported to the host as "WCHAR"s */
+ ext_prop->name_len *= 2;
+ ext_prop->name = ext_prop_name;
+
+ t->os_desc->ext_prop_len +=
+ ext_prop->name_len + ext_prop->data_len + 14;
+ ++t->os_desc->ext_prop_count;
+ list_add_tail(&ext_prop->entry, &t->os_desc->ext_prop);
+ }
+ break;
+ default:
+ pr_vdebug("unknown descriptor: %d\n", type);
+ }
+
+ return length;
+}
+
static inline struct f_fs_opts *ffs_do_functionfs_bind(struct usb_function *f,
struct usb_configuration *c)
{
@@ -2329,7 +2613,7 @@ static int _ffs_func_bind(struct usb_configuration *c,
const int super = gadget_is_superspeed(func->gadget) &&
func->ffs->ss_descs_count;
- int fs_len, hs_len, ret;
+ int fs_len, hs_len, ss_len, ret, i;
/* Make it a single chunk, less management later on */
vla_group(d);
@@ -2341,6 +2625,18 @@ static int _ffs_func_bind(struct usb_configuration *c,
vla_item_with_sz(d, struct usb_descriptor_header *, ss_descs,
super ? ffs->ss_descs_count + 1 : 0);
vla_item_with_sz(d, short, inums, ffs->interfaces_count);
+ vla_item_with_sz(d, struct usb_os_desc_table, os_desc_table,
+ c->cdev->use_os_string ? ffs->interfaces_count : 0);
+ vla_item_with_sz(d, char[16], ext_compat,
+ c->cdev->use_os_string ? ffs->interfaces_count : 0);
+ vla_item_with_sz(d, struct usb_os_desc, os_desc,
+ c->cdev->use_os_string ? ffs->interfaces_count : 0);
+ vla_item_with_sz(d, struct usb_os_desc_ext_prop, ext_prop,
+ ffs->ms_os_descs_ext_prop_count);
+ vla_item_with_sz(d, char, ext_prop_name,
+ ffs->ms_os_descs_ext_prop_name_len);
+ vla_item_with_sz(d, char, ext_prop_data,
+ ffs->ms_os_descs_ext_prop_data_len);
vla_item_with_sz(d, char, raw_descs, ffs->raw_descs_length);
char *vlabuf;
@@ -2351,12 +2647,16 @@ static int _ffs_func_bind(struct usb_configuration *c,
return -ENOTSUPP;
/* Allocate a single chunk, less management later on */
- vlabuf = kmalloc(vla_group_size(d), GFP_KERNEL);
+ vlabuf = kzalloc(vla_group_size(d), GFP_KERNEL);
if (unlikely(!vlabuf))
return -ENOMEM;
- /* Zero */
- memset(vla_ptr(vlabuf, d, eps), 0, d_eps__sz);
+ ffs->ms_os_descs_ext_prop_avail = vla_ptr(vlabuf, d, ext_prop);
+ ffs->ms_os_descs_ext_prop_name_avail =
+ vla_ptr(vlabuf, d, ext_prop_name);
+ ffs->ms_os_descs_ext_prop_data_avail =
+ vla_ptr(vlabuf, d, ext_prop_data);
+
/* Copy descriptors */
memcpy(vla_ptr(vlabuf, d, raw_descs), ffs->raw_descs,
ffs->raw_descs_length);
@@ -2410,12 +2710,16 @@ static int _ffs_func_bind(struct usb_configuration *c,
if (likely(super)) {
func->function.ss_descriptors = vla_ptr(vlabuf, d, ss_descs);
- ret = ffs_do_descs(ffs->ss_descs_count,
+ ss_len = ffs_do_descs(ffs->ss_descs_count,
vla_ptr(vlabuf, d, raw_descs) + fs_len + hs_len,
d_raw_descs__sz - fs_len - hs_len,
__ffs_func_bind_do_descs, func);
- if (unlikely(ret < 0))
+ if (unlikely(ss_len < 0)) {
+ ret = ss_len;
goto error;
+ }
+ } else {
+ ss_len = 0;
}
/*
@@ -2431,6 +2735,28 @@ static int _ffs_func_bind(struct usb_configuration *c,
if (unlikely(ret < 0))
goto error;
+ func->function.os_desc_table = vla_ptr(vlabuf, d, os_desc_table);
+ if (c->cdev->use_os_string)
+ for (i = 0; i < ffs->interfaces_count; ++i) {
+ struct usb_os_desc *desc;
+
+ desc = func->function.os_desc_table[i].os_desc =
+ vla_ptr(vlabuf, d, os_desc) +
+ i * sizeof(struct usb_os_desc);
+ desc->ext_compat_id =
+ vla_ptr(vlabuf, d, ext_compat) + i * 16;
+ INIT_LIST_HEAD(&desc->ext_prop);
+ }
+ ret = ffs_do_os_descs(ffs->ms_os_descs_count,
+ vla_ptr(vlabuf, d, raw_descs) +
+ fs_len + hs_len + ss_len,
+ d_raw_descs__sz - fs_len - hs_len - ss_len,
+ __ffs_func_bind_do_os_desc, func);
+ if (unlikely(ret < 0))
+ goto error;
+ func->function.os_desc_n =
+ c->cdev->use_os_string ? ffs->interfaces_count : 0;
+
/* And we're done */
ffs_event_add(ffs, FUNCTIONFS_BIND);
return 0;
@@ -2901,12 +3227,12 @@ static void *ffs_acquire_dev(const char *dev_name)
ffs_dev = _ffs_find_dev(dev_name);
if (!ffs_dev)
- ffs_dev = ERR_PTR(-ENODEV);
+ ffs_dev = ERR_PTR(-ENOENT);
else if (ffs_dev->mounted)
ffs_dev = ERR_PTR(-EBUSY);
else if (ffs_dev->ffs_acquire_dev_callback &&
ffs_dev->ffs_acquire_dev_callback(ffs_dev))
- ffs_dev = ERR_PTR(-ENODEV);
+ ffs_dev = ERR_PTR(-ENOENT);
else
ffs_dev->mounted = true;
diff --git a/drivers/usb/gadget/f_hid.c b/drivers/usb/gadget/function/f_hid.c
index a95290a1289f..a95290a1289f 100644
--- a/drivers/usb/gadget/f_hid.c
+++ b/drivers/usb/gadget/function/f_hid.c
diff --git a/drivers/usb/gadget/f_loopback.c b/drivers/usb/gadget/function/f_loopback.c
index 4557cd03f0b1..4557cd03f0b1 100644
--- a/drivers/usb/gadget/f_loopback.c
+++ b/drivers/usb/gadget/function/f_loopback.c
diff --git a/drivers/usb/gadget/f_mass_storage.c b/drivers/usb/gadget/function/f_mass_storage.c
index b96393908860..b96393908860 100644
--- a/drivers/usb/gadget/f_mass_storage.c
+++ b/drivers/usb/gadget/function/f_mass_storage.c
diff --git a/drivers/usb/gadget/f_mass_storage.h b/drivers/usb/gadget/function/f_mass_storage.h
index b4866fcef30b..b4866fcef30b 100644
--- a/drivers/usb/gadget/f_mass_storage.h
+++ b/drivers/usb/gadget/function/f_mass_storage.h
diff --git a/drivers/usb/gadget/f_midi.c b/drivers/usb/gadget/function/f_midi.c
index 807b31c0edc3..807b31c0edc3 100644
--- a/drivers/usb/gadget/f_midi.c
+++ b/drivers/usb/gadget/function/f_midi.c
diff --git a/drivers/usb/gadget/f_ncm.c b/drivers/usb/gadget/function/f_ncm.c
index a9499fd30792..bcdc882cd415 100644
--- a/drivers/usb/gadget/f_ncm.c
+++ b/drivers/usb/gadget/function/f_ncm.c
@@ -68,6 +68,18 @@ struct f_ncm {
* callback and ethernet open/close
*/
spinlock_t lock;
+
+ struct net_device *netdev;
+
+ /* For multi-frame NDP TX */
+ struct sk_buff *skb_tx_data;
+ struct sk_buff *skb_tx_ndp;
+ u16 ndp_dgram_count;
+ bool timer_force_tx;
+ struct tasklet_struct tx_tasklet;
+ struct hrtimer task_timer;
+
+ bool timer_stopping;
};
static inline struct f_ncm *func_to_ncm(struct usb_function *f)
@@ -92,15 +104,20 @@ static inline unsigned ncm_bitrate(struct usb_gadget *g)
* If the host can group frames, allow it to do that, 16K is selected,
* because it's used by default by the current linux host driver
*/
-#define NTB_DEFAULT_IN_SIZE USB_CDC_NCM_NTB_MIN_IN_SIZE
+#define NTB_DEFAULT_IN_SIZE 16384
#define NTB_OUT_SIZE 16384
-/*
- * skbs of size less than that will not be aligned
- * to NCM's dwNtbInMaxSize to save bus bandwidth
+/* Allocation for storing the NDP, 32 should suffice for a
+ * 16k packet. This allows a maximum of 32 * 507 Byte packets to
+ * be transmitted in a single 16kB skb, though when sending full size
+ * packets this limit will be plenty.
+ * Smaller packets are not likely to be trying to maximize the
+ * throughput and will be mstly sending smaller infrequent frames.
*/
+#define TX_MAX_NUM_DPE 32
-#define MAX_TX_NONFIXED (512 * 3)
+/* Delay for the transmit to wait before sending an unfilled NTB frame. */
+#define TX_TIMEOUT_NSECS 300000
#define FORMATS_SUPPORTED (USB_CDC_NCM_NTB16_SUPPORTED | \
USB_CDC_NCM_NTB32_SUPPORTED)
@@ -355,14 +372,15 @@ struct ndp_parser_opts {
u32 ndp_sign;
unsigned nth_size;
unsigned ndp_size;
+ unsigned dpe_size;
unsigned ndplen_align;
/* sizes in u16 units */
unsigned dgram_item_len; /* index or length */
unsigned block_length;
- unsigned fp_index;
+ unsigned ndp_index;
unsigned reserved1;
unsigned reserved2;
- unsigned next_fp_index;
+ unsigned next_ndp_index;
};
#define INIT_NDP16_OPTS { \
@@ -370,13 +388,14 @@ struct ndp_parser_opts {
.ndp_sign = USB_CDC_NCM_NDP16_NOCRC_SIGN, \
.nth_size = sizeof(struct usb_cdc_ncm_nth16), \
.ndp_size = sizeof(struct usb_cdc_ncm_ndp16), \
+ .dpe_size = sizeof(struct usb_cdc_ncm_dpe16), \
.ndplen_align = 4, \
.dgram_item_len = 1, \
.block_length = 1, \
- .fp_index = 1, \
+ .ndp_index = 1, \
.reserved1 = 0, \
.reserved2 = 0, \
- .next_fp_index = 1, \
+ .next_ndp_index = 1, \
}
@@ -385,13 +404,14 @@ struct ndp_parser_opts {
.ndp_sign = USB_CDC_NCM_NDP32_NOCRC_SIGN, \
.nth_size = sizeof(struct usb_cdc_ncm_nth32), \
.ndp_size = sizeof(struct usb_cdc_ncm_ndp32), \
+ .dpe_size = sizeof(struct usb_cdc_ncm_dpe32), \
.ndplen_align = 8, \
.dgram_item_len = 2, \
.block_length = 2, \
- .fp_index = 2, \
+ .ndp_index = 2, \
.reserved1 = 1, \
.reserved2 = 2, \
- .next_fp_index = 2, \
+ .next_ndp_index = 2, \
}
static const struct ndp_parser_opts ndp16_opts = INIT_NDP16_OPTS;
@@ -803,6 +823,8 @@ static int ncm_set_alt(struct usb_function *f, unsigned intf, unsigned alt)
if (ncm->port.in_ep->driver_data) {
DBG(cdev, "reset ncm\n");
+ ncm->timer_stopping = true;
+ ncm->netdev = NULL;
gether_disconnect(&ncm->port);
ncm_reset_values(ncm);
}
@@ -839,6 +861,8 @@ static int ncm_set_alt(struct usb_function *f, unsigned intf, unsigned alt)
net = gether_connect(&ncm->port);
if (IS_ERR(net))
return PTR_ERR(net);
+ ncm->netdev = net;
+ ncm->timer_stopping = false;
}
spin_lock(&ncm->lock);
@@ -865,95 +889,232 @@ static int ncm_get_alt(struct usb_function *f, unsigned intf)
return ncm->port.in_ep->driver_data ? 1 : 0;
}
+static struct sk_buff *package_for_tx(struct f_ncm *ncm)
+{
+ __le16 *ntb_iter;
+ struct sk_buff *skb2 = NULL;
+ unsigned ndp_pad;
+ unsigned ndp_index;
+ unsigned new_len;
+
+ const struct ndp_parser_opts *opts = ncm->parser_opts;
+ const int ndp_align = le16_to_cpu(ntb_parameters.wNdpInAlignment);
+ const int dgram_idx_len = 2 * 2 * opts->dgram_item_len;
+
+ /* Stop the timer */
+ hrtimer_try_to_cancel(&ncm->task_timer);
+
+ ndp_pad = ALIGN(ncm->skb_tx_data->len, ndp_align) -
+ ncm->skb_tx_data->len;
+ ndp_index = ncm->skb_tx_data->len + ndp_pad;
+ new_len = ndp_index + dgram_idx_len + ncm->skb_tx_ndp->len;
+
+ /* Set the final BlockLength and wNdpIndex */
+ ntb_iter = (void *) ncm->skb_tx_data->data;
+ /* Increment pointer to BlockLength */
+ ntb_iter += 2 + 1 + 1;
+ put_ncm(&ntb_iter, opts->block_length, new_len);
+ put_ncm(&ntb_iter, opts->ndp_index, ndp_index);
+
+ /* Set the final NDP wLength */
+ new_len = opts->ndp_size +
+ (ncm->ndp_dgram_count * dgram_idx_len);
+ ncm->ndp_dgram_count = 0;
+ /* Increment from start to wLength */
+ ntb_iter = (void *) ncm->skb_tx_ndp->data;
+ ntb_iter += 2;
+ put_unaligned_le16(new_len, ntb_iter);
+
+ /* Merge the skbs */
+ swap(skb2, ncm->skb_tx_data);
+ if (ncm->skb_tx_data) {
+ dev_kfree_skb_any(ncm->skb_tx_data);
+ ncm->skb_tx_data = NULL;
+ }
+
+ /* Insert NDP alignment. */
+ ntb_iter = (void *) skb_put(skb2, ndp_pad);
+ memset(ntb_iter, 0, ndp_pad);
+
+ /* Copy NTB across. */
+ ntb_iter = (void *) skb_put(skb2, ncm->skb_tx_ndp->len);
+ memcpy(ntb_iter, ncm->skb_tx_ndp->data, ncm->skb_tx_ndp->len);
+ dev_kfree_skb_any(ncm->skb_tx_ndp);
+ ncm->skb_tx_ndp = NULL;
+
+ /* Insert zero'd datagram. */
+ ntb_iter = (void *) skb_put(skb2, dgram_idx_len);
+ memset(ntb_iter, 0, dgram_idx_len);
+
+ return skb2;
+}
+
static struct sk_buff *ncm_wrap_ntb(struct gether *port,
struct sk_buff *skb)
{
struct f_ncm *ncm = func_to_ncm(&port->func);
- struct sk_buff *skb2;
+ struct sk_buff *skb2 = NULL;
int ncb_len = 0;
- __le16 *tmp;
- int div;
- int rem;
- int pad;
- int ndp_align;
- int ndp_pad;
+ __le16 *ntb_data;
+ __le16 *ntb_ndp;
+ int dgram_pad;
+
unsigned max_size = ncm->port.fixed_in_len;
const struct ndp_parser_opts *opts = ncm->parser_opts;
- unsigned crc_len = ncm->is_crc ? sizeof(uint32_t) : 0;
-
- div = le16_to_cpu(ntb_parameters.wNdpInDivisor);
- rem = le16_to_cpu(ntb_parameters.wNdpInPayloadRemainder);
- ndp_align = le16_to_cpu(ntb_parameters.wNdpInAlignment);
+ const int ndp_align = le16_to_cpu(ntb_parameters.wNdpInAlignment);
+ const int div = le16_to_cpu(ntb_parameters.wNdpInDivisor);
+ const int rem = le16_to_cpu(ntb_parameters.wNdpInPayloadRemainder);
+ const int dgram_idx_len = 2 * 2 * opts->dgram_item_len;
- ncb_len += opts->nth_size;
- ndp_pad = ALIGN(ncb_len, ndp_align) - ncb_len;
- ncb_len += ndp_pad;
- ncb_len += opts->ndp_size;
- ncb_len += 2 * 2 * opts->dgram_item_len; /* Datagram entry */
- ncb_len += 2 * 2 * opts->dgram_item_len; /* Zero datagram entry */
- pad = ALIGN(ncb_len, div) + rem - ncb_len;
- ncb_len += pad;
-
- if (ncb_len + skb->len + crc_len > max_size) {
- dev_kfree_skb_any(skb);
+ if (!skb && !ncm->skb_tx_data)
return NULL;
- }
- skb2 = skb_copy_expand(skb, ncb_len,
- max_size - skb->len - ncb_len - crc_len,
- GFP_ATOMIC);
- dev_kfree_skb_any(skb);
- if (!skb2)
- return NULL;
+ if (skb) {
+ /* Add the CRC if required up front */
+ if (ncm->is_crc) {
+ uint32_t crc;
+ __le16 *crc_pos;
+
+ crc = ~crc32_le(~0,
+ skb->data,
+ skb->len);
+ crc_pos = (void *) skb_put(skb, sizeof(uint32_t));
+ put_unaligned_le32(crc, crc_pos);
+ }
- skb = skb2;
+ /* If the new skb is too big for the current NCM NTB then
+ * set the current stored skb to be sent now and clear it
+ * ready for new data.
+ * NOTE: Assume maximum align for speed of calculation.
+ */
+ if (ncm->skb_tx_data
+ && (ncm->ndp_dgram_count >= TX_MAX_NUM_DPE
+ || (ncm->skb_tx_data->len +
+ div + rem + skb->len +
+ ncm->skb_tx_ndp->len + ndp_align + (2 * dgram_idx_len))
+ > max_size)) {
+ skb2 = package_for_tx(ncm);
+ if (!skb2)
+ goto err;
+ }
- tmp = (void *) skb_push(skb, ncb_len);
- memset(tmp, 0, ncb_len);
+ if (!ncm->skb_tx_data) {
+ ncb_len = opts->nth_size;
+ dgram_pad = ALIGN(ncb_len, div) + rem - ncb_len;
+ ncb_len += dgram_pad;
- put_unaligned_le32(opts->nth_sign, tmp); /* dwSignature */
- tmp += 2;
- /* wHeaderLength */
- put_unaligned_le16(opts->nth_size, tmp++);
- tmp++; /* skip wSequence */
- put_ncm(&tmp, opts->block_length, skb->len); /* (d)wBlockLength */
- /* (d)wFpIndex */
- /* the first pointer is right after the NTH + align */
- put_ncm(&tmp, opts->fp_index, opts->nth_size + ndp_pad);
+ /* Create a new skb for the NTH and datagrams. */
+ ncm->skb_tx_data = alloc_skb(max_size, GFP_ATOMIC);
+ if (!ncm->skb_tx_data)
+ goto err;
- tmp = (void *)tmp + ndp_pad;
+ ntb_data = (void *) skb_put(ncm->skb_tx_data, ncb_len);
+ memset(ntb_data, 0, ncb_len);
+ /* dwSignature */
+ put_unaligned_le32(opts->nth_sign, ntb_data);
+ ntb_data += 2;
+ /* wHeaderLength */
+ put_unaligned_le16(opts->nth_size, ntb_data++);
+
+ /* Allocate an skb for storing the NDP,
+ * TX_MAX_NUM_DPE should easily suffice for a
+ * 16k packet.
+ */
+ ncm->skb_tx_ndp = alloc_skb((int)(opts->ndp_size
+ + opts->dpe_size
+ * TX_MAX_NUM_DPE),
+ GFP_ATOMIC);
+ if (!ncm->skb_tx_ndp)
+ goto err;
+ ntb_ndp = (void *) skb_put(ncm->skb_tx_ndp,
+ opts->ndp_size);
+ memset(ntb_ndp, 0, ncb_len);
+ /* dwSignature */
+ put_unaligned_le32(ncm->ndp_sign, ntb_ndp);
+ ntb_ndp += 2;
- /* NDP */
- put_unaligned_le32(ncm->ndp_sign, tmp); /* dwSignature */
- tmp += 2;
- /* wLength */
- put_unaligned_le16(ncb_len - opts->nth_size - pad, tmp++);
+ /* There is always a zeroed entry */
+ ncm->ndp_dgram_count = 1;
- tmp += opts->reserved1;
- tmp += opts->next_fp_index; /* skip reserved (d)wNextFpIndex */
- tmp += opts->reserved2;
+ /* Note: we skip opts->next_ndp_index */
+ }
- if (ncm->is_crc) {
- uint32_t crc;
+ /* Delay the timer. */
+ hrtimer_start(&ncm->task_timer,
+ ktime_set(0, TX_TIMEOUT_NSECS),
+ HRTIMER_MODE_REL);
+
+ /* Add the datagram position entries */
+ ntb_ndp = (void *) skb_put(ncm->skb_tx_ndp, dgram_idx_len);
+ memset(ntb_ndp, 0, dgram_idx_len);
+
+ ncb_len = ncm->skb_tx_data->len;
+ dgram_pad = ALIGN(ncb_len, div) + rem - ncb_len;
+ ncb_len += dgram_pad;
+
+ /* (d)wDatagramIndex */
+ put_ncm(&ntb_ndp, opts->dgram_item_len, ncb_len);
+ /* (d)wDatagramLength */
+ put_ncm(&ntb_ndp, opts->dgram_item_len, skb->len);
+ ncm->ndp_dgram_count++;
+
+ /* Add the new data to the skb */
+ ntb_data = (void *) skb_put(ncm->skb_tx_data, dgram_pad);
+ memset(ntb_data, 0, dgram_pad);
+ ntb_data = (void *) skb_put(ncm->skb_tx_data, skb->len);
+ memcpy(ntb_data, skb->data, skb->len);
+ dev_kfree_skb_any(skb);
+ skb = NULL;
- crc = ~crc32_le(~0,
- skb->data + ncb_len,
- skb->len - ncb_len);
- put_unaligned_le32(crc, skb->data + skb->len);
- skb_put(skb, crc_len);
+ } else if (ncm->skb_tx_data && ncm->timer_force_tx) {
+ /* If the tx was requested because of a timeout then send */
+ skb2 = package_for_tx(ncm);
+ if (!skb2)
+ goto err;
}
- /* (d)wDatagramIndex[0] */
- put_ncm(&tmp, opts->dgram_item_len, ncb_len);
- /* (d)wDatagramLength[0] */
- put_ncm(&tmp, opts->dgram_item_len, skb->len - ncb_len);
- /* (d)wDatagramIndex[1] and (d)wDatagramLength[1] already zeroed */
+ return skb2;
- if (skb->len > MAX_TX_NONFIXED)
- memset(skb_put(skb, max_size - skb->len),
- 0, max_size - skb->len);
+err:
+ ncm->netdev->stats.tx_dropped++;
+
+ if (skb)
+ dev_kfree_skb_any(skb);
+ if (ncm->skb_tx_data)
+ dev_kfree_skb_any(ncm->skb_tx_data);
+ if (ncm->skb_tx_ndp)
+ dev_kfree_skb_any(ncm->skb_tx_ndp);
- return skb;
+ return NULL;
+}
+
+/*
+ * This transmits the NTB if there are frames waiting.
+ */
+static void ncm_tx_tasklet(unsigned long data)
+{
+ struct f_ncm *ncm = (void *)data;
+
+ if (ncm->timer_stopping)
+ return;
+
+ /* Only send if data is available. */
+ if (ncm->skb_tx_data) {
+ ncm->timer_force_tx = true;
+ ncm->netdev->netdev_ops->ndo_start_xmit(NULL, ncm->netdev);
+ ncm->timer_force_tx = false;
+ }
+}
+
+/*
+ * The transmit should only be run if no skb data has been sent
+ * for a certain duration.
+ */
+static enum hrtimer_restart ncm_tx_timeout(struct hrtimer *data)
+{
+ struct f_ncm *ncm = container_of(data, struct f_ncm, task_timer);
+ tasklet_schedule(&ncm->tx_tasklet);
+ return HRTIMER_NORESTART;
}
static int ncm_unwrap_ntb(struct gether *port,
@@ -963,6 +1124,7 @@ static int ncm_unwrap_ntb(struct gether *port,
struct f_ncm *ncm = func_to_ncm(&port->func);
__le16 *tmp = (void *) skb->data;
unsigned index, index2;
+ int ndp_index;
unsigned dg_len, dg_len2;
unsigned ndp_len;
struct sk_buff *skb2;
@@ -995,91 +1157,101 @@ static int ncm_unwrap_ntb(struct gether *port,
goto err;
}
- index = get_ncm(&tmp, opts->fp_index);
- /* NCM 3.2 */
- if (((index % 4) != 0) && (index < opts->nth_size)) {
- INFO(port->func.config->cdev, "Bad index: %x\n",
- index);
- goto err;
- }
-
- /* walk through NDP */
- tmp = ((void *)skb->data) + index;
- if (get_unaligned_le32(tmp) != ncm->ndp_sign) {
- INFO(port->func.config->cdev, "Wrong NDP SIGN\n");
- goto err;
- }
- tmp += 2;
-
- ndp_len = get_unaligned_le16(tmp++);
- /*
- * NCM 3.3.1
- * entry is 2 items
- * item size is 16/32 bits, opts->dgram_item_len * 2 bytes
- * minimal: struct usb_cdc_ncm_ndpX + normal entry + zero entry
- */
- if ((ndp_len < opts->ndp_size + 2 * 2 * (opts->dgram_item_len * 2))
- || (ndp_len % opts->ndplen_align != 0)) {
- INFO(port->func.config->cdev, "Bad NDP length: %x\n", ndp_len);
- goto err;
- }
- tmp += opts->reserved1;
- tmp += opts->next_fp_index; /* skip reserved (d)wNextFpIndex */
- tmp += opts->reserved2;
-
- ndp_len -= opts->ndp_size;
- index2 = get_ncm(&tmp, opts->dgram_item_len);
- dg_len2 = get_ncm(&tmp, opts->dgram_item_len);
- dgram_counter = 0;
+ ndp_index = get_ncm(&tmp, opts->ndp_index);
+ /* Run through all the NDP's in the NTB */
do {
- index = index2;
- dg_len = dg_len2;
- if (dg_len < 14 + crc_len) { /* ethernet header + crc */
- INFO(port->func.config->cdev, "Bad dgram length: %x\n",
- dg_len);
+ /* NCM 3.2 */
+ if (((ndp_index % 4) != 0) &&
+ (ndp_index < opts->nth_size)) {
+ INFO(port->func.config->cdev, "Bad index: %#X\n",
+ ndp_index);
goto err;
}
- if (ncm->is_crc) {
- uint32_t crc, crc2;
-
- crc = get_unaligned_le32(skb->data +
- index + dg_len - crc_len);
- crc2 = ~crc32_le(~0,
- skb->data + index,
- dg_len - crc_len);
- if (crc != crc2) {
- INFO(port->func.config->cdev, "Bad CRC\n");
- goto err;
- }
+
+ /* walk through NDP */
+ tmp = (void *)(skb->data + ndp_index);
+ if (get_unaligned_le32(tmp) != ncm->ndp_sign) {
+ INFO(port->func.config->cdev, "Wrong NDP SIGN\n");
+ goto err;
}
+ tmp += 2;
+ ndp_len = get_unaligned_le16(tmp++);
+ /*
+ * NCM 3.3.1
+ * entry is 2 items
+ * item size is 16/32 bits, opts->dgram_item_len * 2 bytes
+ * minimal: struct usb_cdc_ncm_ndpX + normal entry + zero entry
+ * Each entry is a dgram index and a dgram length.
+ */
+ if ((ndp_len < opts->ndp_size
+ + 2 * 2 * (opts->dgram_item_len * 2))
+ || (ndp_len % opts->ndplen_align != 0)) {
+ INFO(port->func.config->cdev, "Bad NDP length: %#X\n",
+ ndp_len);
+ goto err;
+ }
+ tmp += opts->reserved1;
+ /* Check for another NDP (d)wNextNdpIndex */
+ ndp_index = get_ncm(&tmp, opts->next_ndp_index);
+ tmp += opts->reserved2;
+
+ ndp_len -= opts->ndp_size;
index2 = get_ncm(&tmp, opts->dgram_item_len);
dg_len2 = get_ncm(&tmp, opts->dgram_item_len);
+ dgram_counter = 0;
+
+ do {
+ index = index2;
+ dg_len = dg_len2;
+ if (dg_len < 14 + crc_len) { /* ethernet hdr + crc */
+ INFO(port->func.config->cdev,
+ "Bad dgram length: %#X\n", dg_len);
+ goto err;
+ }
+ if (ncm->is_crc) {
+ uint32_t crc, crc2;
+
+ crc = get_unaligned_le32(skb->data +
+ index + dg_len -
+ crc_len);
+ crc2 = ~crc32_le(~0,
+ skb->data + index,
+ dg_len - crc_len);
+ if (crc != crc2) {
+ INFO(port->func.config->cdev,
+ "Bad CRC\n");
+ goto err;
+ }
+ }
+
+ index2 = get_ncm(&tmp, opts->dgram_item_len);
+ dg_len2 = get_ncm(&tmp, opts->dgram_item_len);
- if (index2 == 0 || dg_len2 == 0) {
- skb2 = skb;
- } else {
- skb2 = skb_clone(skb, GFP_ATOMIC);
+ /*
+ * Copy the data into a new skb.
+ * This ensures the truesize is correct
+ */
+ skb2 = netdev_alloc_skb_ip_align(ncm->netdev,
+ dg_len - crc_len);
if (skb2 == NULL)
goto err;
- }
+ memcpy(skb_put(skb2, dg_len - crc_len),
+ skb->data + index, dg_len - crc_len);
- if (!skb_pull(skb2, index)) {
- ret = -EOVERFLOW;
- goto err;
- }
+ skb_queue_tail(list, skb2);
- skb_trim(skb2, dg_len - crc_len);
- skb_queue_tail(list, skb2);
+ ndp_len -= 2 * (opts->dgram_item_len * 2);
- ndp_len -= 2 * (opts->dgram_item_len * 2);
+ dgram_counter++;
- dgram_counter++;
+ if (index2 == 0 || dg_len2 == 0)
+ break;
+ } while (ndp_len > 2 * (opts->dgram_item_len * 2));
+ } while (ndp_index);
- if (index2 == 0 || dg_len2 == 0)
- break;
- } while (ndp_len > 2 * (opts->dgram_item_len * 2)); /* zero entry */
+ dev_kfree_skb_any(skb);
VDBG(port->func.config->cdev,
"Parsed NTB with %d frames\n", dgram_counter);
@@ -1097,8 +1269,11 @@ static void ncm_disable(struct usb_function *f)
DBG(cdev, "ncm deactivated\n");
- if (ncm->port.in_ep->driver_data)
+ if (ncm->port.in_ep->driver_data) {
+ ncm->timer_stopping = true;
+ ncm->netdev = NULL;
gether_disconnect(&ncm->port);
+ }
if (ncm->notify->driver_data) {
usb_ep_disable(ncm->notify);
@@ -1267,6 +1442,10 @@ static int ncm_bind(struct usb_configuration *c, struct usb_function *f)
ncm->port.open = ncm_open;
ncm->port.close = ncm_close;
+ tasklet_init(&ncm->tx_tasklet, ncm_tx_tasklet, (unsigned long) ncm);
+ hrtimer_init(&ncm->task_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
+ ncm->task_timer.function = ncm_tx_timeout;
+
DBG(cdev, "CDC Network: %s speed IN/%s OUT/%s NOTIFY/%s\n",
gadget_is_dualspeed(c->cdev->gadget) ? "dual" : "full",
ncm->port.in_ep->name, ncm->port.out_ep->name,
@@ -1380,6 +1559,10 @@ static void ncm_unbind(struct usb_configuration *c, struct usb_function *f)
DBG(c->cdev, "ncm unbind\n");
+ hrtimer_cancel(&ncm->task_timer);
+ tasklet_kill(&ncm->tx_tasklet);
+
+ ncm_string_defs[0].id = 0;
usb_free_all_descriptors(f);
kfree(ncm->notify_req->buf);
@@ -1416,6 +1599,7 @@ static struct usb_function *ncm_alloc(struct usb_function_instance *fi)
ncm->port.ioport = netdev_priv(opts->net);
mutex_unlock(&opts->lock);
ncm->port.is_fixed = true;
+ ncm->port.supports_multi_frame = true;
ncm->port.func.name = "cdc_network";
/* descriptors are per-instance copies */
diff --git a/drivers/usb/gadget/f_obex.c b/drivers/usb/gadget/function/f_obex.c
index aebae1853bce..aebae1853bce 100644
--- a/drivers/usb/gadget/f_obex.c
+++ b/drivers/usb/gadget/function/f_obex.c
diff --git a/drivers/usb/gadget/f_phonet.c b/drivers/usb/gadget/function/f_phonet.c
index f2b781773eed..f2b781773eed 100644
--- a/drivers/usb/gadget/f_phonet.c
+++ b/drivers/usb/gadget/function/f_phonet.c
diff --git a/drivers/usb/gadget/f_rndis.c b/drivers/usb/gadget/function/f_rndis.c
index 9c41e9515b8e..ddb09dc6d1f2 100644
--- a/drivers/usb/gadget/f_rndis.c
+++ b/drivers/usb/gadget/function/f_rndis.c
@@ -727,6 +727,10 @@ rndis_bind(struct usb_configuration *c, struct usb_function *f)
rndis_control_intf.bInterfaceNumber = status;
rndis_union_desc.bMasterInterface0 = status;
+ if (cdev->use_os_string)
+ f->os_desc_table[0].if_id =
+ rndis_iad_descriptor.bFirstInterface;
+
status = usb_interface_id(c, f);
if (status < 0)
goto fail;
diff --git a/drivers/usb/gadget/f_serial.c b/drivers/usb/gadget/function/f_serial.c
index 9ecbcbf36a45..9ecbcbf36a45 100644
--- a/drivers/usb/gadget/f_serial.c
+++ b/drivers/usb/gadget/function/f_serial.c
diff --git a/drivers/usb/gadget/f_sourcesink.c b/drivers/usb/gadget/function/f_sourcesink.c
index d3cd52db78fe..d3cd52db78fe 100644
--- a/drivers/usb/gadget/f_sourcesink.c
+++ b/drivers/usb/gadget/function/f_sourcesink.c
diff --git a/drivers/usb/gadget/f_subset.c b/drivers/usb/gadget/function/f_subset.c
index 1ea8baf33333..1ea8baf33333 100644
--- a/drivers/usb/gadget/f_subset.c
+++ b/drivers/usb/gadget/function/f_subset.c
diff --git a/drivers/usb/gadget/f_uac1.c b/drivers/usb/gadget/function/f_uac1.c
index 2b4c82d84bfc..2b4c82d84bfc 100644
--- a/drivers/usb/gadget/f_uac1.c
+++ b/drivers/usb/gadget/function/f_uac1.c
diff --git a/drivers/usb/gadget/f_uac2.c b/drivers/usb/gadget/function/f_uac2.c
index 6261db4a9910..3ed89ecc8d6d 100644
--- a/drivers/usb/gadget/f_uac2.c
+++ b/drivers/usb/gadget/function/f_uac2.c
@@ -348,14 +348,34 @@ static int uac2_pcm_open(struct snd_pcm_substream *substream)
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
spin_lock_init(&uac2->p_prm.lock);
runtime->hw.rate_min = p_srate;
- runtime->hw.formats = SNDRV_PCM_FMTBIT_S16_LE; /* ! p_ssize ! */
+ switch (p_ssize) {
+ case 3:
+ runtime->hw.formats = SNDRV_PCM_FMTBIT_S24_3LE;
+ break;
+ case 4:
+ runtime->hw.formats = SNDRV_PCM_FMTBIT_S32_LE;
+ break;
+ default:
+ runtime->hw.formats = SNDRV_PCM_FMTBIT_S16_LE;
+ break;
+ }
runtime->hw.channels_min = num_channels(p_chmask);
runtime->hw.period_bytes_min = 2 * uac2->p_prm.max_psize
/ runtime->hw.periods_min;
} else {
spin_lock_init(&uac2->c_prm.lock);
runtime->hw.rate_min = c_srate;
- runtime->hw.formats = SNDRV_PCM_FMTBIT_S16_LE; /* ! c_ssize ! */
+ switch (c_ssize) {
+ case 3:
+ runtime->hw.formats = SNDRV_PCM_FMTBIT_S24_3LE;
+ break;
+ case 4:
+ runtime->hw.formats = SNDRV_PCM_FMTBIT_S32_LE;
+ break;
+ default:
+ runtime->hw.formats = SNDRV_PCM_FMTBIT_S16_LE;
+ break;
+ }
runtime->hw.channels_min = num_channels(c_chmask);
runtime->hw.period_bytes_min = 2 * uac2->c_prm.max_psize
/ runtime->hw.periods_min;
diff --git a/drivers/usb/gadget/f_uvc.c b/drivers/usb/gadget/function/f_uvc.c
index e2a1f50bd93c..e2a1f50bd93c 100644
--- a/drivers/usb/gadget/f_uvc.c
+++ b/drivers/usb/gadget/function/f_uvc.c
diff --git a/drivers/usb/gadget/f_uvc.h b/drivers/usb/gadget/function/f_uvc.h
index ec52752f7326..ec52752f7326 100644
--- a/drivers/usb/gadget/f_uvc.h
+++ b/drivers/usb/gadget/function/f_uvc.h
diff --git a/drivers/usb/gadget/g_zero.h b/drivers/usb/gadget/function/g_zero.h
index 15f180904f8a..15f180904f8a 100644
--- a/drivers/usb/gadget/g_zero.h
+++ b/drivers/usb/gadget/function/g_zero.h
diff --git a/drivers/usb/gadget/ndis.h b/drivers/usb/gadget/function/ndis.h
index a19f72dec0cd..a19f72dec0cd 100644
--- a/drivers/usb/gadget/ndis.h
+++ b/drivers/usb/gadget/function/ndis.h
diff --git a/drivers/usb/gadget/rndis.c b/drivers/usb/gadget/function/rndis.c
index 95d2324f6977..95d2324f6977 100644
--- a/drivers/usb/gadget/rndis.c
+++ b/drivers/usb/gadget/function/rndis.c
diff --git a/drivers/usb/gadget/rndis.h b/drivers/usb/gadget/function/rndis.h
index 0f4abb4c3775..0f4abb4c3775 100644
--- a/drivers/usb/gadget/rndis.h
+++ b/drivers/usb/gadget/function/rndis.h
diff --git a/drivers/usb/gadget/storage_common.c b/drivers/usb/gadget/function/storage_common.c
index 648f9e489b39..648f9e489b39 100644
--- a/drivers/usb/gadget/storage_common.c
+++ b/drivers/usb/gadget/function/storage_common.c
diff --git a/drivers/usb/gadget/storage_common.h b/drivers/usb/gadget/function/storage_common.h
index 70c891469f57..70c891469f57 100644
--- a/drivers/usb/gadget/storage_common.h
+++ b/drivers/usb/gadget/function/storage_common.h
diff --git a/drivers/usb/gadget/u_ecm.h b/drivers/usb/gadget/function/u_ecm.h
index 262cc03cc2c0..262cc03cc2c0 100644
--- a/drivers/usb/gadget/u_ecm.h
+++ b/drivers/usb/gadget/function/u_ecm.h
diff --git a/drivers/usb/gadget/u_eem.h b/drivers/usb/gadget/function/u_eem.h
index e3ae97874c4f..e3ae97874c4f 100644
--- a/drivers/usb/gadget/u_eem.h
+++ b/drivers/usb/gadget/function/u_eem.h
diff --git a/drivers/usb/gadget/u_ether.c b/drivers/usb/gadget/function/u_ether.c
index 97b027724ee7..d50adda913cf 100644
--- a/drivers/usb/gadget/u_ether.c
+++ b/drivers/usb/gadget/function/u_ether.c
@@ -483,7 +483,7 @@ static netdev_tx_t eth_start_xmit(struct sk_buff *skb,
struct net_device *net)
{
struct eth_dev *dev = netdev_priv(net);
- int length = skb->len;
+ int length = 0;
int retval;
struct usb_request *req = NULL;
unsigned long flags;
@@ -500,13 +500,13 @@ static netdev_tx_t eth_start_xmit(struct sk_buff *skb,
}
spin_unlock_irqrestore(&dev->lock, flags);
- if (!in) {
+ if (skb && !in) {
dev_kfree_skb_any(skb);
return NETDEV_TX_OK;
}
/* apply outgoing CDC or RNDIS filters */
- if (!is_promisc(cdc_filter)) {
+ if (skb && !is_promisc(cdc_filter)) {
u8 *dest = skb->data;
if (is_multicast_ether_addr(dest)) {
@@ -557,11 +557,17 @@ static netdev_tx_t eth_start_xmit(struct sk_buff *skb,
if (dev->port_usb)
skb = dev->wrap(dev->port_usb, skb);
spin_unlock_irqrestore(&dev->lock, flags);
- if (!skb)
+ if (!skb) {
+ /* Multi frame CDC protocols may store the frame for
+ * later which is not a dropped frame.
+ */
+ if (dev->port_usb->supports_multi_frame)
+ goto multiframe;
goto drop;
-
- length = skb->len;
+ }
}
+
+ length = skb->len;
req->buf = skb->data;
req->context = skb;
req->complete = tx_complete;
@@ -604,6 +610,7 @@ static netdev_tx_t eth_start_xmit(struct sk_buff *skb,
dev_kfree_skb_any(skb);
drop:
dev->net->stats.tx_dropped++;
+multiframe:
spin_lock_irqsave(&dev->req_lock, flags);
if (list_empty(&dev->tx_reqs))
netif_start_queue(net);
diff --git a/drivers/usb/gadget/u_ether.h b/drivers/usb/gadget/function/u_ether.h
index 0f0290acea7e..334b38947916 100644
--- a/drivers/usb/gadget/u_ether.h
+++ b/drivers/usb/gadget/function/u_ether.h
@@ -18,6 +18,7 @@
#include <linux/if_ether.h>
#include <linux/usb/composite.h>
#include <linux/usb/cdc.h>
+#include <linux/netdevice.h>
#include "gadget_chips.h"
@@ -74,6 +75,7 @@ struct gether {
bool is_fixed;
u32 fixed_out_len;
u32 fixed_in_len;
+ bool supports_multi_frame;
struct sk_buff *(*wrap)(struct gether *port,
struct sk_buff *skb);
int (*unwrap)(struct gether *port,
diff --git a/drivers/usb/gadget/u_ether_configfs.h b/drivers/usb/gadget/function/u_ether_configfs.h
index bcbd30146cfd..bcbd30146cfd 100644
--- a/drivers/usb/gadget/u_ether_configfs.h
+++ b/drivers/usb/gadget/function/u_ether_configfs.h
diff --git a/drivers/usb/gadget/u_fs.h b/drivers/usb/gadget/function/u_fs.h
index bf0ba375d459..63d6e71569c1 100644
--- a/drivers/usb/gadget/u_fs.h
+++ b/drivers/usb/gadget/function/u_fs.h
@@ -216,6 +216,13 @@ struct ffs_data {
unsigned fs_descs_count;
unsigned hs_descs_count;
unsigned ss_descs_count;
+ unsigned ms_os_descs_count;
+ unsigned ms_os_descs_ext_prop_count;
+ unsigned ms_os_descs_ext_prop_name_len;
+ unsigned ms_os_descs_ext_prop_data_len;
+ void *ms_os_descs_ext_prop_avail;
+ void *ms_os_descs_ext_prop_name_avail;
+ void *ms_os_descs_ext_prop_data_avail;
unsigned short strings_count;
unsigned short interfaces_count;
diff --git a/drivers/usb/gadget/u_gether.h b/drivers/usb/gadget/function/u_gether.h
index d4078426ba5d..d4078426ba5d 100644
--- a/drivers/usb/gadget/u_gether.h
+++ b/drivers/usb/gadget/function/u_gether.h
diff --git a/drivers/usb/gadget/u_ncm.h b/drivers/usb/gadget/function/u_ncm.h
index ce0f3a78ca13..ce0f3a78ca13 100644
--- a/drivers/usb/gadget/u_ncm.h
+++ b/drivers/usb/gadget/function/u_ncm.h
diff --git a/drivers/usb/gadget/u_phonet.h b/drivers/usb/gadget/function/u_phonet.h
index 98ced18779ea..98ced18779ea 100644
--- a/drivers/usb/gadget/u_phonet.h
+++ b/drivers/usb/gadget/function/u_phonet.h
diff --git a/drivers/usb/gadget/u_rndis.h b/drivers/usb/gadget/function/u_rndis.h
index e902aa42a297..e902aa42a297 100644
--- a/drivers/usb/gadget/u_rndis.h
+++ b/drivers/usb/gadget/function/u_rndis.h
diff --git a/drivers/usb/gadget/u_serial.c b/drivers/usb/gadget/function/u_serial.c
index ad0aca812002..ad0aca812002 100644
--- a/drivers/usb/gadget/u_serial.c
+++ b/drivers/usb/gadget/function/u_serial.c
diff --git a/drivers/usb/gadget/u_serial.h b/drivers/usb/gadget/function/u_serial.h
index c20210c0babd..c20210c0babd 100644
--- a/drivers/usb/gadget/u_serial.h
+++ b/drivers/usb/gadget/function/u_serial.h
diff --git a/drivers/usb/gadget/u_uac1.c b/drivers/usb/gadget/function/u_uac1.c
index 7a55fea43430..7a55fea43430 100644
--- a/drivers/usb/gadget/u_uac1.c
+++ b/drivers/usb/gadget/function/u_uac1.c
diff --git a/drivers/usb/gadget/u_uac1.h b/drivers/usb/gadget/function/u_uac1.h
index 18c2e729faf6..18c2e729faf6 100644
--- a/drivers/usb/gadget/u_uac1.h
+++ b/drivers/usb/gadget/function/u_uac1.h
diff --git a/drivers/usb/gadget/uvc.h b/drivers/usb/gadget/function/uvc.h
index 7a9111de8054..7a9111de8054 100644
--- a/drivers/usb/gadget/uvc.h
+++ b/drivers/usb/gadget/function/uvc.h
diff --git a/drivers/usb/gadget/uvc_queue.c b/drivers/usb/gadget/function/uvc_queue.c
index 1c29bc954db9..1c29bc954db9 100644
--- a/drivers/usb/gadget/uvc_queue.c
+++ b/drivers/usb/gadget/function/uvc_queue.c
diff --git a/drivers/usb/gadget/uvc_queue.h b/drivers/usb/gadget/function/uvc_queue.h
index 8e76ce982f1e..8e76ce982f1e 100644
--- a/drivers/usb/gadget/uvc_queue.h
+++ b/drivers/usb/gadget/function/uvc_queue.h
diff --git a/drivers/usb/gadget/uvc_v4l2.c b/drivers/usb/gadget/function/uvc_v4l2.c
index ad48e81155e2..ad48e81155e2 100644
--- a/drivers/usb/gadget/uvc_v4l2.c
+++ b/drivers/usb/gadget/function/uvc_v4l2.c
diff --git a/drivers/usb/gadget/uvc_video.c b/drivers/usb/gadget/function/uvc_video.c
index 71e896d4c5ae..71e896d4c5ae 100644
--- a/drivers/usb/gadget/uvc_video.c
+++ b/drivers/usb/gadget/function/uvc_video.c
diff --git a/drivers/usb/gadget/legacy/Kconfig b/drivers/usb/gadget/legacy/Kconfig
new file mode 100644
index 000000000000..aa376f006333
--- /dev/null
+++ b/drivers/usb/gadget/legacy/Kconfig
@@ -0,0 +1,475 @@
+#
+# USB Gadget support on a system involves
+# (a) a peripheral controller, and
+# (b) the gadget driver using it.
+#
+# NOTE: Gadget support ** DOES NOT ** depend on host-side CONFIG_USB !!
+#
+# - Host systems (like PCs) need CONFIG_USB (with "A" jacks).
+# - Peripherals (like PDAs) need CONFIG_USB_GADGET (with "B" jacks).
+# - Some systems have both kinds of controllers.
+#
+# With help from a special transceiver and a "Mini-AB" jack, systems with
+# both kinds of controller can also support "USB On-the-Go" (CONFIG_USB_OTG).
+#
+
+config USB_ZERO
+ tristate "Gadget Zero (DEVELOPMENT)"
+ select USB_LIBCOMPOSITE
+ select USB_F_SS_LB
+ help
+ Gadget Zero is a two-configuration device. It either sinks and
+ sources bulk data; or it loops back a configurable number of
+ transfers. It also implements control requests, for "chapter 9"
+ conformance. The driver needs only two bulk-capable endpoints, so
+ it can work on top of most device-side usb controllers. It's
+ useful for testing, and is also a working example showing how
+ USB "gadget drivers" can be written.
+
+ Make this be the first driver you try using on top of any new
+ USB peripheral controller driver. Then you can use host-side
+ test software, like the "usbtest" driver, to put your hardware
+ and its driver through a basic set of functional tests.
+
+ Gadget Zero also works with the host-side "usb-skeleton" driver,
+ and with many kinds of host-side test software. You may need
+ to tweak product and vendor IDs before host software knows about
+ this device, and arrange to select an appropriate configuration.
+
+ Say "y" to link the driver statically, or "m" to build a
+ dynamically linked module called "g_zero".
+
+config USB_ZERO_HNPTEST
+ boolean "HNP Test Device"
+ depends on USB_ZERO && USB_OTG
+ help
+ You can configure this device to enumerate using the device
+ identifiers of the USB-OTG test device. That means that when
+ this gadget connects to another OTG device, with this one using
+ the "B-Peripheral" role, that device will use HNP to let this
+ one serve as the USB host instead (in the "B-Host" role).
+
+config USB_AUDIO
+ tristate "Audio Gadget"
+ depends on SND
+ select USB_LIBCOMPOSITE
+ select SND_PCM
+ help
+ This Gadget Audio driver is compatible with USB Audio Class
+ specification 2.0. It implements 1 AudioControl interface,
+ 1 AudioStreaming Interface each for USB-OUT and USB-IN.
+ Number of channels, sample rate and sample size can be
+ specified as module parameters.
+ This driver doesn't expect any real Audio codec to be present
+ on the device - the audio streams are simply sinked to and
+ sourced from a virtual ALSA sound card created. The user-space
+ application may choose to do whatever it wants with the data
+ received from the USB Host and choose to provide whatever it
+ wants as audio data to the USB Host.
+
+ Say "y" to link the driver statically, or "m" to build a
+ dynamically linked module called "g_audio".
+
+config GADGET_UAC1
+ bool "UAC 1.0 (Legacy)"
+ depends on USB_AUDIO
+ help
+ If you instead want older UAC Spec-1.0 driver that also has audio
+ paths hardwired to the Audio codec chip on-board and doesn't work
+ without one.
+
+config USB_ETH
+ tristate "Ethernet Gadget (with CDC Ethernet support)"
+ depends on NET
+ select USB_LIBCOMPOSITE
+ select USB_U_ETHER
+ select USB_F_ECM
+ select USB_F_SUBSET
+ select CRC32
+ help
+ This driver implements Ethernet style communication, in one of
+ several ways:
+
+ - The "Communication Device Class" (CDC) Ethernet Control Model.
+ That protocol is often avoided with pure Ethernet adapters, in
+ favor of simpler vendor-specific hardware, but is widely
+ supported by firmware for smart network devices.
+
+ - On hardware can't implement that protocol, a simple CDC subset
+ is used, placing fewer demands on USB.
+
+ - CDC Ethernet Emulation Model (EEM) is a newer standard that has
+ a simpler interface that can be used by more USB hardware.
+
+ RNDIS support is an additional option, more demanding than than
+ subset.
+
+ Within the USB device, this gadget driver exposes a network device
+ "usbX", where X depends on what other networking devices you have.
+ Treat it like a two-node Ethernet link: host, and gadget.
+
+ The Linux-USB host-side "usbnet" driver interoperates with this
+ driver, so that deep I/O queues can be supported. On 2.4 kernels,
+ use "CDCEther" instead, if you're using the CDC option. That CDC
+ mode should also interoperate with standard CDC Ethernet class
+ drivers on other host operating systems.
+
+ Say "y" to link the driver statically, or "m" to build a
+ dynamically linked module called "g_ether".
+
+config USB_ETH_RNDIS
+ bool "RNDIS support"
+ depends on USB_ETH
+ select USB_LIBCOMPOSITE
+ select USB_F_RNDIS
+ default y
+ help
+ Microsoft Windows XP bundles the "Remote NDIS" (RNDIS) protocol,
+ and Microsoft provides redistributable binary RNDIS drivers for
+ older versions of Windows.
+
+ If you say "y" here, the Ethernet gadget driver will try to provide
+ a second device configuration, supporting RNDIS to talk to such
+ Microsoft USB hosts.
+
+ To make MS-Windows work with this, use Documentation/usb/linux.inf
+ as the "driver info file". For versions of MS-Windows older than
+ XP, you'll need to download drivers from Microsoft's website; a URL
+ is given in comments found in that info file.
+
+config USB_ETH_EEM
+ bool "Ethernet Emulation Model (EEM) support"
+ depends on USB_ETH
+ select USB_LIBCOMPOSITE
+ select USB_F_EEM
+ default n
+ help
+ CDC EEM is a newer USB standard that is somewhat simpler than CDC ECM
+ and therefore can be supported by more hardware. Technically ECM and
+ EEM are designed for different applications. The ECM model extends
+ the network interface to the target (e.g. a USB cable modem), and the
+ EEM model is for mobile devices to communicate with hosts using
+ ethernet over USB. For Linux gadgets, however, the interface with
+ the host is the same (a usbX device), so the differences are minimal.
+
+ If you say "y" here, the Ethernet gadget driver will use the EEM
+ protocol rather than ECM. If unsure, say "n".
+
+config USB_G_NCM
+ tristate "Network Control Model (NCM) support"
+ depends on NET
+ select USB_LIBCOMPOSITE
+ select USB_U_ETHER
+ select USB_F_NCM
+ select CRC32
+ help
+ This driver implements USB CDC NCM subclass standard. NCM is
+ an advanced protocol for Ethernet encapsulation, allows grouping
+ of several ethernet frames into one USB transfer and different
+ alignment possibilities.
+
+ Say "y" to link the driver statically, or "m" to build a
+ dynamically linked module called "g_ncm".
+
+config USB_GADGETFS
+ tristate "Gadget Filesystem"
+ help
+ This driver provides a filesystem based API that lets user mode
+ programs implement a single-configuration USB device, including
+ endpoint I/O and control requests that don't relate to enumeration.
+ All endpoints, transfer speeds, and transfer types supported by
+ the hardware are available, through read() and write() calls.
+
+ Say "y" to link the driver statically, or "m" to build a
+ dynamically linked module called "gadgetfs".
+
+config USB_FUNCTIONFS
+ tristate "Function Filesystem"
+ select USB_LIBCOMPOSITE
+ select USB_F_FS
+ select USB_FUNCTIONFS_GENERIC if !(USB_FUNCTIONFS_ETH || USB_FUNCTIONFS_RNDIS)
+ help
+ The Function Filesystem (FunctionFS) lets one create USB
+ composite functions in user space in the same way GadgetFS
+ lets one create USB gadgets in user space. This allows creation
+ of composite gadgets such that some of the functions are
+ implemented in kernel space (for instance Ethernet, serial or
+ mass storage) and other are implemented in user space.
+
+ If you say "y" or "m" here you will be able what kind of
+ configurations the gadget will provide.
+
+ Say "y" to link the driver statically, or "m" to build
+ a dynamically linked module called "g_ffs".
+
+config USB_FUNCTIONFS_ETH
+ bool "Include configuration with CDC ECM (Ethernet)"
+ depends on USB_FUNCTIONFS && NET
+ select USB_U_ETHER
+ select USB_F_ECM
+ select USB_F_SUBSET
+ help
+ Include a configuration with CDC ECM function (Ethernet) and the
+ Function Filesystem.
+
+config USB_FUNCTIONFS_RNDIS
+ bool "Include configuration with RNDIS (Ethernet)"
+ depends on USB_FUNCTIONFS && NET
+ select USB_U_ETHER
+ select USB_F_RNDIS
+ help
+ Include a configuration with RNDIS function (Ethernet) and the Filesystem.
+
+config USB_FUNCTIONFS_GENERIC
+ bool "Include 'pure' configuration"
+ depends on USB_FUNCTIONFS
+ help
+ Include a configuration with the Function Filesystem alone with
+ no Ethernet interface.
+
+config USB_MASS_STORAGE
+ tristate "Mass Storage Gadget"
+ depends on BLOCK
+ select USB_LIBCOMPOSITE
+ select USB_F_MASS_STORAGE
+ help
+ The Mass Storage Gadget acts as a USB Mass Storage disk drive.
+ As its storage repository it can use a regular file or a block
+ device (in much the same way as the "loop" device driver),
+ specified as a module parameter or sysfs option.
+
+ This driver is a replacement for now removed File-backed
+ Storage Gadget (g_file_storage).
+
+ Say "y" to link the driver statically, or "m" to build
+ a dynamically linked module called "g_mass_storage".
+
+config USB_GADGET_TARGET
+ tristate "USB Gadget Target Fabric Module"
+ depends on TARGET_CORE
+ select USB_LIBCOMPOSITE
+ help
+ This fabric is an USB gadget. Two USB protocols are supported that is
+ BBB or BOT (Bulk Only Transport) and UAS (USB Attached SCSI). BOT is
+ advertised on alternative interface 0 (primary) and UAS is on
+ alternative interface 1. Both protocols can work on USB2.0 and USB3.0.
+ UAS utilizes the USB 3.0 feature called streams support.
+
+config USB_G_SERIAL
+ tristate "Serial Gadget (with CDC ACM and CDC OBEX support)"
+ depends on TTY
+ select USB_U_SERIAL
+ select USB_F_ACM
+ select USB_F_SERIAL
+ select USB_F_OBEX
+ select USB_LIBCOMPOSITE
+ help
+ The Serial Gadget talks to the Linux-USB generic serial driver.
+ This driver supports a CDC-ACM module option, which can be used
+ to interoperate with MS-Windows hosts or with the Linux-USB
+ "cdc-acm" driver.
+
+ This driver also supports a CDC-OBEX option. You will need a
+ user space OBEX server talking to /dev/ttyGS*, since the kernel
+ itself doesn't implement the OBEX protocol.
+
+ Say "y" to link the driver statically, or "m" to build a
+ dynamically linked module called "g_serial".
+
+ For more information, see Documentation/usb/gadget_serial.txt
+ which includes instructions and a "driver info file" needed to
+ make MS-Windows work with CDC ACM.
+
+config USB_MIDI_GADGET
+ tristate "MIDI Gadget"
+ depends on SND
+ select USB_LIBCOMPOSITE
+ select SND_RAWMIDI
+ help
+ The MIDI Gadget acts as a USB Audio device, with one MIDI
+ input and one MIDI output. These MIDI jacks appear as
+ a sound "card" in the ALSA sound system. Other MIDI
+ connections can then be made on the gadget system, using
+ ALSA's aconnect utility etc.
+
+ Say "y" to link the driver statically, or "m" to build a
+ dynamically linked module called "g_midi".
+
+config USB_G_PRINTER
+ tristate "Printer Gadget"
+ select USB_LIBCOMPOSITE
+ help
+ The Printer Gadget channels data between the USB host and a
+ userspace program driving the print engine. The user space
+ program reads and writes the device file /dev/g_printer to
+ receive or send printer data. It can use ioctl calls to
+ the device file to get or set printer status.
+
+ Say "y" to link the driver statically, or "m" to build a
+ dynamically linked module called "g_printer".
+
+ For more information, see Documentation/usb/gadget_printer.txt
+ which includes sample code for accessing the device file.
+
+if TTY
+
+config USB_CDC_COMPOSITE
+ tristate "CDC Composite Device (Ethernet and ACM)"
+ depends on NET
+ select USB_LIBCOMPOSITE
+ select USB_U_SERIAL
+ select USB_U_ETHER
+ select USB_F_ACM
+ select USB_F_ECM
+ help
+ This driver provides two functions in one configuration:
+ a CDC Ethernet (ECM) link, and a CDC ACM (serial port) link.
+
+ This driver requires four bulk and two interrupt endpoints,
+ plus the ability to handle altsettings. Not all peripheral
+ controllers are that capable.
+
+ Say "y" to link the driver statically, or "m" to build a
+ dynamically linked module.
+
+config USB_G_NOKIA
+ tristate "Nokia composite gadget"
+ depends on PHONET
+ select USB_LIBCOMPOSITE
+ select USB_U_SERIAL
+ select USB_U_ETHER
+ select USB_F_ACM
+ select USB_F_OBEX
+ select USB_F_PHONET
+ select USB_F_ECM
+ help
+ The Nokia composite gadget provides support for acm, obex
+ and phonet in only one composite gadget driver.
+
+ It's only really useful for N900 hardware. If you're building
+ a kernel for N900, say Y or M here. If unsure, say N.
+
+config USB_G_ACM_MS
+ tristate "CDC Composite Device (ACM and mass storage)"
+ depends on BLOCK
+ select USB_LIBCOMPOSITE
+ select USB_U_SERIAL
+ select USB_F_ACM
+ select USB_F_MASS_STORAGE
+ help
+ This driver provides two functions in one configuration:
+ a mass storage, and a CDC ACM (serial port) link.
+
+ Say "y" to link the driver statically, or "m" to build a
+ dynamically linked module called "g_acm_ms".
+
+config USB_G_MULTI
+ tristate "Multifunction Composite Gadget"
+ depends on BLOCK && NET
+ select USB_G_MULTI_CDC if !USB_G_MULTI_RNDIS
+ select USB_LIBCOMPOSITE
+ select USB_U_SERIAL
+ select USB_U_ETHER
+ select USB_F_ACM
+ select USB_F_MASS_STORAGE
+ help
+ The Multifunction Composite Gadget provides Ethernet (RNDIS
+ and/or CDC Ethernet), mass storage and ACM serial link
+ interfaces.
+
+ You will be asked to choose which of the two configurations is
+ to be available in the gadget. At least one configuration must
+ be chosen to make the gadget usable. Selecting more than one
+ configuration will prevent Windows from automatically detecting
+ the gadget as a composite gadget, so an INF file will be needed to
+ use the gadget.
+
+ Say "y" to link the driver statically, or "m" to build a
+ dynamically linked module called "g_multi".
+
+config USB_G_MULTI_RNDIS
+ bool "RNDIS + CDC Serial + Storage configuration"
+ depends on USB_G_MULTI
+ select USB_F_RNDIS
+ default y
+ help
+ This option enables a configuration with RNDIS, CDC Serial and
+ Mass Storage functions available in the Multifunction Composite
+ Gadget. This is the configuration dedicated for Windows since RNDIS
+ is Microsoft's protocol.
+
+ If unsure, say "y".
+
+config USB_G_MULTI_CDC
+ bool "CDC Ethernet + CDC Serial + Storage configuration"
+ depends on USB_G_MULTI
+ default n
+ select USB_F_ECM
+ help
+ This option enables a configuration with CDC Ethernet (ECM), CDC
+ Serial and Mass Storage functions available in the Multifunction
+ Composite Gadget.
+
+ If unsure, say "y".
+
+endif # TTY
+
+config USB_G_HID
+ tristate "HID Gadget"
+ select USB_LIBCOMPOSITE
+ help
+ The HID gadget driver provides generic emulation of USB
+ Human Interface Devices (HID).
+
+ For more information, see Documentation/usb/gadget_hid.txt which
+ includes sample code for accessing the device files.
+
+ Say "y" to link the driver statically, or "m" to build a
+ dynamically linked module called "g_hid".
+
+# Standalone / single function gadgets
+config USB_G_DBGP
+ tristate "EHCI Debug Device Gadget"
+ depends on TTY
+ select USB_LIBCOMPOSITE
+ help
+ This gadget emulates an EHCI Debug device. This is useful when you want
+ to interact with an EHCI Debug Port.
+
+ Say "y" to link the driver statically, or "m" to build a
+ dynamically linked module called "g_dbgp".
+
+if USB_G_DBGP
+choice
+ prompt "EHCI Debug Device mode"
+ default USB_G_DBGP_SERIAL
+
+config USB_G_DBGP_PRINTK
+ depends on USB_G_DBGP
+ bool "printk"
+ help
+ Directly printk() received data. No interaction.
+
+config USB_G_DBGP_SERIAL
+ depends on USB_G_DBGP
+ select USB_U_SERIAL
+ bool "serial"
+ help
+ Userland can interact using /dev/ttyGSxxx.
+endchoice
+endif
+
+# put drivers that need isochronous transfer support (for audio
+# or video class gadget drivers), or specific hardware, here.
+config USB_G_WEBCAM
+ tristate "USB Webcam Gadget"
+ depends on VIDEO_DEV
+ select USB_LIBCOMPOSITE
+ select VIDEOBUF2_VMALLOC
+ help
+ The Webcam Gadget acts as a composite USB Audio and Video Class
+ device. It provides a userspace API to process UVC control requests
+ and stream video data to the host.
+
+ Say "y" to link the driver statically, or "m" to build a
+ dynamically linked module called "g_webcam".
diff --git a/drivers/usb/gadget/legacy/Makefile b/drivers/usb/gadget/legacy/Makefile
new file mode 100644
index 000000000000..a11aad5635df
--- /dev/null
+++ b/drivers/usb/gadget/legacy/Makefile
@@ -0,0 +1,44 @@
+#
+# USB gadget drivers
+#
+
+ccflags-y := -I$(PWD)/drivers/usb/gadget/
+ccflags-y += -I$(PWD)/drivers/usb/gadget/udc/
+ccflags-y += -I$(PWD)/drivers/usb/gadget/function/
+
+g_zero-y := zero.o
+g_audio-y := audio.o
+g_ether-y := ether.o
+g_serial-y := serial.o
+g_midi-y := gmidi.o
+gadgetfs-y := inode.o
+g_mass_storage-y := mass_storage.o
+g_printer-y := printer.o
+g_cdc-y := cdc2.o
+g_multi-y := multi.o
+g_hid-y := hid.o
+g_dbgp-y := dbgp.o
+g_nokia-y := nokia.o
+g_webcam-y := webcam.o
+g_ncm-y := ncm.o
+g_acm_ms-y := acm_ms.o
+g_tcm_usb_gadget-y := tcm_usb_gadget.o
+
+obj-$(CONFIG_USB_ZERO) += g_zero.o
+obj-$(CONFIG_USB_AUDIO) += g_audio.o
+obj-$(CONFIG_USB_ETH) += g_ether.o
+obj-$(CONFIG_USB_GADGETFS) += gadgetfs.o
+obj-$(CONFIG_USB_FUNCTIONFS) += g_ffs.o
+obj-$(CONFIG_USB_MASS_STORAGE) += g_mass_storage.o
+obj-$(CONFIG_USB_G_SERIAL) += g_serial.o
+obj-$(CONFIG_USB_G_PRINTER) += g_printer.o
+obj-$(CONFIG_USB_MIDI_GADGET) += g_midi.o
+obj-$(CONFIG_USB_CDC_COMPOSITE) += g_cdc.o
+obj-$(CONFIG_USB_G_HID) += g_hid.o
+obj-$(CONFIG_USB_G_DBGP) += g_dbgp.o
+obj-$(CONFIG_USB_G_MULTI) += g_multi.o
+obj-$(CONFIG_USB_G_NOKIA) += g_nokia.o
+obj-$(CONFIG_USB_G_WEBCAM) += g_webcam.o
+obj-$(CONFIG_USB_G_NCM) += g_ncm.o
+obj-$(CONFIG_USB_G_ACM_MS) += g_acm_ms.o
+obj-$(CONFIG_USB_GADGET_TARGET) += tcm_usb_gadget.o
diff --git a/drivers/usb/gadget/acm_ms.c b/drivers/usb/gadget/legacy/acm_ms.c
index a252444cc0a7..c30b7b572465 100644
--- a/drivers/usb/gadget/acm_ms.c
+++ b/drivers/usb/gadget/legacy/acm_ms.c
@@ -267,18 +267,8 @@ static __refdata struct usb_composite_driver acm_ms_driver = {
.unbind = __exit_p(acm_ms_unbind),
};
+module_usb_composite_driver(acm_ms_driver);
+
MODULE_DESCRIPTION(DRIVER_DESC);
MODULE_AUTHOR("Klaus Schwarzkopf <schwarzkopf@sensortherm.de>");
MODULE_LICENSE("GPL v2");
-
-static int __init init(void)
-{
- return usb_composite_probe(&acm_ms_driver);
-}
-module_init(init);
-
-static void __exit cleanup(void)
-{
- usb_composite_unregister(&acm_ms_driver);
-}
-module_exit(cleanup);
diff --git a/drivers/usb/gadget/audio.c b/drivers/usb/gadget/legacy/audio.c
index 231b0efe8fdc..6eb695e5e43a 100644
--- a/drivers/usb/gadget/audio.c
+++ b/drivers/usb/gadget/legacy/audio.c
@@ -172,17 +172,7 @@ static __refdata struct usb_composite_driver audio_driver = {
.unbind = __exit_p(audio_unbind),
};
-static int __init init(void)
-{
- return usb_composite_probe(&audio_driver);
-}
-module_init(init);
-
-static void __exit cleanup(void)
-{
- usb_composite_unregister(&audio_driver);
-}
-module_exit(cleanup);
+module_usb_composite_driver(audio_driver);
MODULE_DESCRIPTION(DRIVER_DESC);
MODULE_AUTHOR("Bryan Wu <cooloney@kernel.org>");
diff --git a/drivers/usb/gadget/cdc2.c b/drivers/usb/gadget/legacy/cdc2.c
index e126b6b248e6..2e85d9473478 100644
--- a/drivers/usb/gadget/cdc2.c
+++ b/drivers/usb/gadget/legacy/cdc2.c
@@ -231,18 +231,8 @@ static __refdata struct usb_composite_driver cdc_driver = {
.unbind = __exit_p(cdc_unbind),
};
+module_usb_composite_driver(cdc_driver);
+
MODULE_DESCRIPTION(DRIVER_DESC);
MODULE_AUTHOR("David Brownell");
MODULE_LICENSE("GPL");
-
-static int __init init(void)
-{
- return usb_composite_probe(&cdc_driver);
-}
-module_init(init);
-
-static void __exit cleanup(void)
-{
- usb_composite_unregister(&cdc_driver);
-}
-module_exit(cleanup);
diff --git a/drivers/usb/gadget/dbgp.c b/drivers/usb/gadget/legacy/dbgp.c
index 986fc511a2ed..986fc511a2ed 100644
--- a/drivers/usb/gadget/dbgp.c
+++ b/drivers/usb/gadget/legacy/dbgp.c
diff --git a/drivers/usb/gadget/ether.c b/drivers/usb/gadget/legacy/ether.c
index c1c113ef950c..c5fdc61cdc4a 100644
--- a/drivers/usb/gadget/ether.c
+++ b/drivers/usb/gadget/legacy/ether.c
@@ -475,18 +475,8 @@ static __refdata struct usb_composite_driver eth_driver = {
.unbind = __exit_p(eth_unbind),
};
+module_usb_composite_driver(eth_driver);
+
MODULE_DESCRIPTION(PREFIX DRIVER_DESC);
MODULE_AUTHOR("David Brownell, Benedikt Spanger");
MODULE_LICENSE("GPL");
-
-static int __init init(void)
-{
- return usb_composite_probe(&eth_driver);
-}
-module_init(init);
-
-static void __exit cleanup(void)
-{
- usb_composite_unregister(&eth_driver);
-}
-module_exit(cleanup);
diff --git a/drivers/usb/gadget/g_ffs.c b/drivers/usb/gadget/legacy/g_ffs.c
index fe12e6a27448..06acfa55864a 100644
--- a/drivers/usb/gadget/g_ffs.c
+++ b/drivers/usb/gadget/legacy/g_ffs.c
@@ -276,7 +276,7 @@ module_exit(gfs_exit);
static void *functionfs_acquire_dev(struct ffs_dev *dev)
{
if (!try_module_get(THIS_MODULE))
- return ERR_PTR(-ENODEV);
+ return ERR_PTR(-ENOENT);
return 0;
}
diff --git a/drivers/usb/gadget/gmidi.c b/drivers/usb/gadget/legacy/gmidi.c
index e879e2c9f461..3d696b86ff76 100644
--- a/drivers/usb/gadget/gmidi.c
+++ b/drivers/usb/gadget/legacy/gmidi.c
@@ -163,15 +163,4 @@ static __refdata struct usb_composite_driver midi_driver = {
.unbind = __exit_p(midi_unbind),
};
-static int __init midi_init(void)
-{
- return usb_composite_probe(&midi_driver);
-}
-module_init(midi_init);
-
-static void __exit midi_cleanup(void)
-{
- usb_composite_unregister(&midi_driver);
-}
-module_exit(midi_cleanup);
-
+module_usb_composite_driver(midi_driver);
diff --git a/drivers/usb/gadget/hid.c b/drivers/usb/gadget/legacy/hid.c
index 778613eb37af..778613eb37af 100644
--- a/drivers/usb/gadget/hid.c
+++ b/drivers/usb/gadget/legacy/hid.c
diff --git a/drivers/usb/gadget/inode.c b/drivers/usb/gadget/legacy/inode.c
index 2e4ce7704908..2e4ce7704908 100644
--- a/drivers/usb/gadget/inode.c
+++ b/drivers/usb/gadget/legacy/inode.c
diff --git a/drivers/usb/gadget/mass_storage.c b/drivers/usb/gadget/legacy/mass_storage.c
index 8e27a8c96444..8e27a8c96444 100644
--- a/drivers/usb/gadget/mass_storage.c
+++ b/drivers/usb/gadget/legacy/mass_storage.c
diff --git a/drivers/usb/gadget/multi.c b/drivers/usb/gadget/legacy/multi.c
index 940f6cde8e89..39d27bb343b4 100644
--- a/drivers/usb/gadget/multi.c
+++ b/drivers/usb/gadget/legacy/multi.c
@@ -507,15 +507,4 @@ static __refdata struct usb_composite_driver multi_driver = {
.needs_serial = 1,
};
-
-static int __init multi_init(void)
-{
- return usb_composite_probe(&multi_driver);
-}
-module_init(multi_init);
-
-static void __exit multi_exit(void)
-{
- usb_composite_unregister(&multi_driver);
-}
-module_exit(multi_exit);
+module_usb_composite_driver(multi_driver);
diff --git a/drivers/usb/gadget/ncm.c b/drivers/usb/gadget/legacy/ncm.c
index 81956feca1bd..e90e23db2acb 100644
--- a/drivers/usb/gadget/ncm.c
+++ b/drivers/usb/gadget/legacy/ncm.c
@@ -204,18 +204,8 @@ static __refdata struct usb_composite_driver ncm_driver = {
.unbind = __exit_p(gncm_unbind),
};
+module_usb_composite_driver(ncm_driver);
+
MODULE_DESCRIPTION(DRIVER_DESC);
MODULE_AUTHOR("Yauheni Kaliuta");
MODULE_LICENSE("GPL");
-
-static int __init init(void)
-{
- return usb_composite_probe(&ncm_driver);
-}
-module_init(init);
-
-static void __exit cleanup(void)
-{
- usb_composite_unregister(&ncm_driver);
-}
-module_exit(cleanup);
diff --git a/drivers/usb/gadget/nokia.c b/drivers/usb/gadget/legacy/nokia.c
index 3ab386167519..9b8fd701648c 100644
--- a/drivers/usb/gadget/nokia.c
+++ b/drivers/usb/gadget/legacy/nokia.c
@@ -347,14 +347,4 @@ static __refdata struct usb_composite_driver nokia_driver = {
.unbind = __exit_p(nokia_unbind),
};
-static int __init nokia_init(void)
-{
- return usb_composite_probe(&nokia_driver);
-}
-module_init(nokia_init);
-
-static void __exit nokia_cleanup(void)
-{
- usb_composite_unregister(&nokia_driver);
-}
-module_exit(nokia_cleanup);
+module_usb_composite_driver(nokia_driver);
diff --git a/drivers/usb/gadget/printer.c b/drivers/usb/gadget/legacy/printer.c
index 6474081dcbaf..6474081dcbaf 100644
--- a/drivers/usb/gadget/printer.c
+++ b/drivers/usb/gadget/legacy/printer.c
diff --git a/drivers/usb/gadget/serial.c b/drivers/usb/gadget/legacy/serial.c
index 1f5f978d35d5..1f5f978d35d5 100644
--- a/drivers/usb/gadget/serial.c
+++ b/drivers/usb/gadget/legacy/serial.c
diff --git a/drivers/usb/gadget/tcm_usb_gadget.c b/drivers/usb/gadget/legacy/tcm_usb_gadget.c
index 6cdb7a534f23..6cdb7a534f23 100644
--- a/drivers/usb/gadget/tcm_usb_gadget.c
+++ b/drivers/usb/gadget/legacy/tcm_usb_gadget.c
diff --git a/drivers/usb/gadget/tcm_usb_gadget.h b/drivers/usb/gadget/legacy/tcm_usb_gadget.h
index 8289219925b8..8289219925b8 100644
--- a/drivers/usb/gadget/tcm_usb_gadget.h
+++ b/drivers/usb/gadget/legacy/tcm_usb_gadget.h
diff --git a/drivers/usb/gadget/webcam.c b/drivers/usb/gadget/legacy/webcam.c
index 8cef1e658c29..a11d8e420bfe 100644
--- a/drivers/usb/gadget/webcam.c
+++ b/drivers/usb/gadget/legacy/webcam.c
@@ -390,20 +390,7 @@ static __refdata struct usb_composite_driver webcam_driver = {
.unbind = webcam_unbind,
};
-static int __init
-webcam_init(void)
-{
- return usb_composite_probe(&webcam_driver);
-}
-
-static void __exit
-webcam_cleanup(void)
-{
- usb_composite_unregister(&webcam_driver);
-}
-
-module_init(webcam_init);
-module_exit(webcam_cleanup);
+module_usb_composite_driver(webcam_driver);
MODULE_AUTHOR("Laurent Pinchart");
MODULE_DESCRIPTION("Webcam Video Gadget");
diff --git a/drivers/usb/gadget/zero.c b/drivers/usb/gadget/legacy/zero.c
index 134f354ede62..c3d496828b74 100644
--- a/drivers/usb/gadget/zero.c
+++ b/drivers/usb/gadget/legacy/zero.c
@@ -411,17 +411,7 @@ static __refdata struct usb_composite_driver zero_driver = {
.resume = zero_resume,
};
+module_usb_composite_driver(zero_driver);
+
MODULE_AUTHOR("David Brownell");
MODULE_LICENSE("GPL");
-
-static int __init init(void)
-{
- return usb_composite_probe(&zero_driver);
-}
-module_init(init);
-
-static void __exit cleanup(void)
-{
- usb_composite_unregister(&zero_driver);
-}
-module_exit(cleanup);
diff --git a/drivers/usb/gadget/net2280.c b/drivers/usb/gadget/net2280.c
deleted file mode 100644
index 300b3a71383b..000000000000
--- a/drivers/usb/gadget/net2280.c
+++ /dev/null
@@ -1,2905 +0,0 @@
-/*
- * Driver for the PLX NET2280 USB device controller.
- * Specs and errata are available from <http://www.plxtech.com>.
- *
- * PLX Technology Inc. (formerly NetChip Technology) supported the
- * development of this driver.
- *
- *
- * CODE STATUS HIGHLIGHTS
- *
- * This driver should work well with most "gadget" drivers, including
- * the Mass Storage, Serial, and Ethernet/RNDIS gadget drivers
- * as well as Gadget Zero and Gadgetfs.
- *
- * DMA is enabled by default. Drivers using transfer queues might use
- * DMA chaining to remove IRQ latencies between transfers. (Except when
- * short OUT transfers happen.) Drivers can use the req->no_interrupt
- * hint to completely eliminate some IRQs, if a later IRQ is guaranteed
- * and DMA chaining is enabled.
- *
- * Note that almost all the errata workarounds here are only needed for
- * rev1 chips. Rev1a silicon (0110) fixes almost all of them.
- */
-
-/*
- * Copyright (C) 2003 David Brownell
- * Copyright (C) 2003-2005 PLX Technology, Inc.
- *
- * Modified Seth Levy 2005 PLX Technology, Inc. to provide compatibility
- * with 2282 chip
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- */
-
-#undef DEBUG /* messages on error and most fault paths */
-#undef VERBOSE /* extra debug messages (success too) */
-
-#include <linux/module.h>
-#include <linux/pci.h>
-#include <linux/dma-mapping.h>
-#include <linux/kernel.h>
-#include <linux/delay.h>
-#include <linux/ioport.h>
-#include <linux/slab.h>
-#include <linux/errno.h>
-#include <linux/init.h>
-#include <linux/timer.h>
-#include <linux/list.h>
-#include <linux/interrupt.h>
-#include <linux/moduleparam.h>
-#include <linux/device.h>
-#include <linux/usb/ch9.h>
-#include <linux/usb/gadget.h>
-#include <linux/prefetch.h>
-
-#include <asm/byteorder.h>
-#include <asm/io.h>
-#include <asm/irq.h>
-#include <asm/unaligned.h>
-
-
-#define DRIVER_DESC "PLX NET228x USB Peripheral Controller"
-#define DRIVER_VERSION "2005 Sept 27"
-
-#define EP_DONTUSE 13 /* nonzero */
-
-#define USE_RDK_LEDS /* GPIO pins control three LEDs */
-
-
-static const char driver_name [] = "net2280";
-static const char driver_desc [] = DRIVER_DESC;
-
-static const char ep0name [] = "ep0";
-static const char *const ep_name [] = {
- ep0name,
- "ep-a", "ep-b", "ep-c", "ep-d",
- "ep-e", "ep-f",
-};
-
-/* use_dma -- general goodness, fewer interrupts, less cpu load (vs PIO)
- * use_dma_chaining -- dma descriptor queueing gives even more irq reduction
- *
- * The net2280 DMA engines are not tightly integrated with their FIFOs;
- * not all cases are (yet) handled well in this driver or the silicon.
- * Some gadget drivers work better with the dma support here than others.
- * These two parameters let you use PIO or more aggressive DMA.
- */
-static bool use_dma = 1;
-static bool use_dma_chaining = 0;
-
-/* "modprobe net2280 use_dma=n" etc */
-module_param (use_dma, bool, S_IRUGO);
-module_param (use_dma_chaining, bool, S_IRUGO);
-
-
-/* mode 0 == ep-{a,b,c,d} 1K fifo each
- * mode 1 == ep-{a,b} 2K fifo each, ep-{c,d} unavailable
- * mode 2 == ep-a 2K fifo, ep-{b,c} 1K each, ep-d unavailable
- */
-static ushort fifo_mode = 0;
-
-/* "modprobe net2280 fifo_mode=1" etc */
-module_param (fifo_mode, ushort, 0644);
-
-/* enable_suspend -- When enabled, the driver will respond to
- * USB suspend requests by powering down the NET2280. Otherwise,
- * USB suspend requests will be ignored. This is acceptable for
- * self-powered devices
- */
-static bool enable_suspend = 0;
-
-/* "modprobe net2280 enable_suspend=1" etc */
-module_param (enable_suspend, bool, S_IRUGO);
-
-/* force full-speed operation */
-static bool full_speed;
-module_param(full_speed, bool, 0444);
-MODULE_PARM_DESC(full_speed, "force full-speed mode -- for testing only!");
-
-#define DIR_STRING(bAddress) (((bAddress) & USB_DIR_IN) ? "in" : "out")
-
-#if defined(CONFIG_USB_GADGET_DEBUG_FILES) || defined (DEBUG)
-static char *type_string (u8 bmAttributes)
-{
- switch ((bmAttributes) & USB_ENDPOINT_XFERTYPE_MASK) {
- case USB_ENDPOINT_XFER_BULK: return "bulk";
- case USB_ENDPOINT_XFER_ISOC: return "iso";
- case USB_ENDPOINT_XFER_INT: return "intr";
- }
- return "control";
-}
-#endif
-
-#include "net2280.h"
-
-#define valid_bit cpu_to_le32 (1 << VALID_BIT)
-#define dma_done_ie cpu_to_le32 (1 << DMA_DONE_INTERRUPT_ENABLE)
-
-/*-------------------------------------------------------------------------*/
-
-static int
-net2280_enable (struct usb_ep *_ep, const struct usb_endpoint_descriptor *desc)
-{
- struct net2280 *dev;
- struct net2280_ep *ep;
- u32 max, tmp;
- unsigned long flags;
-
- ep = container_of (_ep, struct net2280_ep, ep);
- if (!_ep || !desc || ep->desc || _ep->name == ep0name
- || desc->bDescriptorType != USB_DT_ENDPOINT)
- return -EINVAL;
- dev = ep->dev;
- if (!dev->driver || dev->gadget.speed == USB_SPEED_UNKNOWN)
- return -ESHUTDOWN;
-
- /* erratum 0119 workaround ties up an endpoint number */
- if ((desc->bEndpointAddress & 0x0f) == EP_DONTUSE)
- return -EDOM;
-
- /* sanity check ep-e/ep-f since their fifos are small */
- max = usb_endpoint_maxp (desc) & 0x1fff;
- if (ep->num > 4 && max > 64)
- return -ERANGE;
-
- spin_lock_irqsave (&dev->lock, flags);
- _ep->maxpacket = max & 0x7ff;
- ep->desc = desc;
-
- /* ep_reset() has already been called */
- ep->stopped = 0;
- ep->wedged = 0;
- ep->out_overflow = 0;
-
- /* set speed-dependent max packet; may kick in high bandwidth */
- set_idx_reg (dev->regs, REG_EP_MAXPKT (dev, ep->num), max);
-
- /* FIFO lines can't go to different packets. PIO is ok, so
- * use it instead of troublesome (non-bulk) multi-packet DMA.
- */
- if (ep->dma && (max % 4) != 0 && use_dma_chaining) {
- DEBUG (ep->dev, "%s, no dma for maxpacket %d\n",
- ep->ep.name, ep->ep.maxpacket);
- ep->dma = NULL;
- }
-
- /* set type, direction, address; reset fifo counters */
- writel ((1 << FIFO_FLUSH), &ep->regs->ep_stat);
- tmp = (desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK);
- if (tmp == USB_ENDPOINT_XFER_INT) {
- /* erratum 0105 workaround prevents hs NYET */
- if (dev->chiprev == 0100
- && dev->gadget.speed == USB_SPEED_HIGH
- && !(desc->bEndpointAddress & USB_DIR_IN))
- writel ((1 << CLEAR_NAK_OUT_PACKETS_MODE),
- &ep->regs->ep_rsp);
- } else if (tmp == USB_ENDPOINT_XFER_BULK) {
- /* catch some particularly blatant driver bugs */
- if ((dev->gadget.speed == USB_SPEED_HIGH
- && max != 512)
- || (dev->gadget.speed == USB_SPEED_FULL
- && max > 64)) {
- spin_unlock_irqrestore (&dev->lock, flags);
- return -ERANGE;
- }
- }
- ep->is_iso = (tmp == USB_ENDPOINT_XFER_ISOC) ? 1 : 0;
- tmp <<= ENDPOINT_TYPE;
- tmp |= desc->bEndpointAddress;
- tmp |= (4 << ENDPOINT_BYTE_COUNT); /* default full fifo lines */
- tmp |= 1 << ENDPOINT_ENABLE;
- wmb ();
-
- /* for OUT transfers, block the rx fifo until a read is posted */
- ep->is_in = (tmp & USB_DIR_IN) != 0;
- if (!ep->is_in)
- writel ((1 << SET_NAK_OUT_PACKETS), &ep->regs->ep_rsp);
- else if (dev->pdev->device != 0x2280) {
- /* Added for 2282, Don't use nak packets on an in endpoint,
- * this was ignored on 2280
- */
- writel ((1 << CLEAR_NAK_OUT_PACKETS)
- | (1 << CLEAR_NAK_OUT_PACKETS_MODE), &ep->regs->ep_rsp);
- }
-
- writel (tmp, &ep->regs->ep_cfg);
-
- /* enable irqs */
- if (!ep->dma) { /* pio, per-packet */
- tmp = (1 << ep->num) | readl (&dev->regs->pciirqenb0);
- writel (tmp, &dev->regs->pciirqenb0);
-
- tmp = (1 << DATA_PACKET_RECEIVED_INTERRUPT_ENABLE)
- | (1 << DATA_PACKET_TRANSMITTED_INTERRUPT_ENABLE);
- if (dev->pdev->device == 0x2280)
- tmp |= readl (&ep->regs->ep_irqenb);
- writel (tmp, &ep->regs->ep_irqenb);
- } else { /* dma, per-request */
- tmp = (1 << (8 + ep->num)); /* completion */
- tmp |= readl (&dev->regs->pciirqenb1);
- writel (tmp, &dev->regs->pciirqenb1);
-
- /* for short OUT transfers, dma completions can't
- * advance the queue; do it pio-style, by hand.
- * NOTE erratum 0112 workaround #2
- */
- if ((desc->bEndpointAddress & USB_DIR_IN) == 0) {
- tmp = (1 << SHORT_PACKET_TRANSFERRED_INTERRUPT_ENABLE);
- writel (tmp, &ep->regs->ep_irqenb);
-
- tmp = (1 << ep->num) | readl (&dev->regs->pciirqenb0);
- writel (tmp, &dev->regs->pciirqenb0);
- }
- }
-
- tmp = desc->bEndpointAddress;
- DEBUG (dev, "enabled %s (ep%d%s-%s) %s max %04x\n",
- _ep->name, tmp & 0x0f, DIR_STRING (tmp),
- type_string (desc->bmAttributes),
- ep->dma ? "dma" : "pio", max);
-
- /* pci writes may still be posted */
- spin_unlock_irqrestore (&dev->lock, flags);
- return 0;
-}
-
-static int handshake (u32 __iomem *ptr, u32 mask, u32 done, int usec)
-{
- u32 result;
-
- do {
- result = readl (ptr);
- if (result == ~(u32)0) /* "device unplugged" */
- return -ENODEV;
- result &= mask;
- if (result == done)
- return 0;
- udelay (1);
- usec--;
- } while (usec > 0);
- return -ETIMEDOUT;
-}
-
-static const struct usb_ep_ops net2280_ep_ops;
-
-static void ep_reset (struct net2280_regs __iomem *regs, struct net2280_ep *ep)
-{
- u32 tmp;
-
- ep->desc = NULL;
- INIT_LIST_HEAD (&ep->queue);
-
- usb_ep_set_maxpacket_limit(&ep->ep, ~0);
- ep->ep.ops = &net2280_ep_ops;
-
- /* disable the dma, irqs, endpoint... */
- if (ep->dma) {
- writel (0, &ep->dma->dmactl);
- writel ( (1 << DMA_SCATTER_GATHER_DONE_INTERRUPT)
- | (1 << DMA_TRANSACTION_DONE_INTERRUPT)
- | (1 << DMA_ABORT)
- , &ep->dma->dmastat);
-
- tmp = readl (&regs->pciirqenb0);
- tmp &= ~(1 << ep->num);
- writel (tmp, &regs->pciirqenb0);
- } else {
- tmp = readl (&regs->pciirqenb1);
- tmp &= ~(1 << (8 + ep->num)); /* completion */
- writel (tmp, &regs->pciirqenb1);
- }
- writel (0, &ep->regs->ep_irqenb);
-
- /* init to our chosen defaults, notably so that we NAK OUT
- * packets until the driver queues a read (+note erratum 0112)
- */
- if (!ep->is_in || ep->dev->pdev->device == 0x2280) {
- tmp = (1 << SET_NAK_OUT_PACKETS_MODE)
- | (1 << SET_NAK_OUT_PACKETS)
- | (1 << CLEAR_EP_HIDE_STATUS_PHASE)
- | (1 << CLEAR_INTERRUPT_MODE);
- } else {
- /* added for 2282 */
- tmp = (1 << CLEAR_NAK_OUT_PACKETS_MODE)
- | (1 << CLEAR_NAK_OUT_PACKETS)
- | (1 << CLEAR_EP_HIDE_STATUS_PHASE)
- | (1 << CLEAR_INTERRUPT_MODE);
- }
-
- if (ep->num != 0) {
- tmp |= (1 << CLEAR_ENDPOINT_TOGGLE)
- | (1 << CLEAR_ENDPOINT_HALT);
- }
- writel (tmp, &ep->regs->ep_rsp);
-
- /* scrub most status bits, and flush any fifo state */
- if (ep->dev->pdev->device == 0x2280)
- tmp = (1 << FIFO_OVERFLOW)
- | (1 << FIFO_UNDERFLOW);
- else
- tmp = 0;
-
- writel (tmp | (1 << TIMEOUT)
- | (1 << USB_STALL_SENT)
- | (1 << USB_IN_NAK_SENT)
- | (1 << USB_IN_ACK_RCVD)
- | (1 << USB_OUT_PING_NAK_SENT)
- | (1 << USB_OUT_ACK_SENT)
- | (1 << FIFO_FLUSH)
- | (1 << SHORT_PACKET_OUT_DONE_INTERRUPT)
- | (1 << SHORT_PACKET_TRANSFERRED_INTERRUPT)
- | (1 << DATA_PACKET_RECEIVED_INTERRUPT)
- | (1 << DATA_PACKET_TRANSMITTED_INTERRUPT)
- | (1 << DATA_OUT_PING_TOKEN_INTERRUPT)
- | (1 << DATA_IN_TOKEN_INTERRUPT)
- , &ep->regs->ep_stat);
-
- /* fifo size is handled separately */
-}
-
-static void nuke (struct net2280_ep *);
-
-static int net2280_disable (struct usb_ep *_ep)
-{
- struct net2280_ep *ep;
- unsigned long flags;
-
- ep = container_of (_ep, struct net2280_ep, ep);
- if (!_ep || !ep->desc || _ep->name == ep0name)
- return -EINVAL;
-
- spin_lock_irqsave (&ep->dev->lock, flags);
- nuke (ep);
- ep_reset (ep->dev->regs, ep);
-
- VDEBUG (ep->dev, "disabled %s %s\n",
- ep->dma ? "dma" : "pio", _ep->name);
-
- /* synch memory views with the device */
- (void) readl (&ep->regs->ep_cfg);
-
- if (use_dma && !ep->dma && ep->num >= 1 && ep->num <= 4)
- ep->dma = &ep->dev->dma [ep->num - 1];
-
- spin_unlock_irqrestore (&ep->dev->lock, flags);
- return 0;
-}
-
-/*-------------------------------------------------------------------------*/
-
-static struct usb_request *
-net2280_alloc_request (struct usb_ep *_ep, gfp_t gfp_flags)
-{
- struct net2280_ep *ep;
- struct net2280_request *req;
-
- if (!_ep)
- return NULL;
- ep = container_of (_ep, struct net2280_ep, ep);
-
- req = kzalloc(sizeof(*req), gfp_flags);
- if (!req)
- return NULL;
-
- INIT_LIST_HEAD (&req->queue);
-
- /* this dma descriptor may be swapped with the previous dummy */
- if (ep->dma) {
- struct net2280_dma *td;
-
- td = pci_pool_alloc (ep->dev->requests, gfp_flags,
- &req->td_dma);
- if (!td) {
- kfree (req);
- return NULL;
- }
- td->dmacount = 0; /* not VALID */
- td->dmadesc = td->dmaaddr;
- req->td = td;
- }
- return &req->req;
-}
-
-static void
-net2280_free_request (struct usb_ep *_ep, struct usb_request *_req)
-{
- struct net2280_ep *ep;
- struct net2280_request *req;
-
- ep = container_of (_ep, struct net2280_ep, ep);
- if (!_ep || !_req)
- return;
-
- req = container_of (_req, struct net2280_request, req);
- WARN_ON (!list_empty (&req->queue));
- if (req->td)
- pci_pool_free (ep->dev->requests, req->td, req->td_dma);
- kfree (req);
-}
-
-/*-------------------------------------------------------------------------*/
-
-/* load a packet into the fifo we use for usb IN transfers.
- * works for all endpoints.
- *
- * NOTE: pio with ep-a..ep-d could stuff multiple packets into the fifo
- * at a time, but this code is simpler because it knows it only writes
- * one packet. ep-a..ep-d should use dma instead.
- */
-static void
-write_fifo (struct net2280_ep *ep, struct usb_request *req)
-{
- struct net2280_ep_regs __iomem *regs = ep->regs;
- u8 *buf;
- u32 tmp;
- unsigned count, total;
-
- /* INVARIANT: fifo is currently empty. (testable) */
-
- if (req) {
- buf = req->buf + req->actual;
- prefetch (buf);
- total = req->length - req->actual;
- } else {
- total = 0;
- buf = NULL;
- }
-
- /* write just one packet at a time */
- count = ep->ep.maxpacket;
- if (count > total) /* min() cannot be used on a bitfield */
- count = total;
-
- VDEBUG (ep->dev, "write %s fifo (IN) %d bytes%s req %p\n",
- ep->ep.name, count,
- (count != ep->ep.maxpacket) ? " (short)" : "",
- req);
- while (count >= 4) {
- /* NOTE be careful if you try to align these. fifo lines
- * should normally be full (4 bytes) and successive partial
- * lines are ok only in certain cases.
- */
- tmp = get_unaligned ((u32 *)buf);
- cpu_to_le32s (&tmp);
- writel (tmp, &regs->ep_data);
- buf += 4;
- count -= 4;
- }
-
- /* last fifo entry is "short" unless we wrote a full packet.
- * also explicitly validate last word in (periodic) transfers
- * when maxpacket is not a multiple of 4 bytes.
- */
- if (count || total < ep->ep.maxpacket) {
- tmp = count ? get_unaligned ((u32 *)buf) : count;
- cpu_to_le32s (&tmp);
- set_fifo_bytecount (ep, count & 0x03);
- writel (tmp, &regs->ep_data);
- }
-
- /* pci writes may still be posted */
-}
-
-/* work around erratum 0106: PCI and USB race over the OUT fifo.
- * caller guarantees chiprev 0100, out endpoint is NAKing, and
- * there's no real data in the fifo.
- *
- * NOTE: also used in cases where that erratum doesn't apply:
- * where the host wrote "too much" data to us.
- */
-static void out_flush (struct net2280_ep *ep)
-{
- u32 __iomem *statp;
- u32 tmp;
-
- ASSERT_OUT_NAKING (ep);
-
- statp = &ep->regs->ep_stat;
- writel ( (1 << DATA_OUT_PING_TOKEN_INTERRUPT)
- | (1 << DATA_PACKET_RECEIVED_INTERRUPT)
- , statp);
- writel ((1 << FIFO_FLUSH), statp);
- mb ();
- tmp = readl (statp);
- if (tmp & (1 << DATA_OUT_PING_TOKEN_INTERRUPT)
- /* high speed did bulk NYET; fifo isn't filling */
- && ep->dev->gadget.speed == USB_SPEED_FULL) {
- unsigned usec;
-
- usec = 50; /* 64 byte bulk/interrupt */
- handshake (statp, (1 << USB_OUT_PING_NAK_SENT),
- (1 << USB_OUT_PING_NAK_SENT), usec);
- /* NAK done; now CLEAR_NAK_OUT_PACKETS is safe */
- }
-}
-
-/* unload packet(s) from the fifo we use for usb OUT transfers.
- * returns true iff the request completed, because of short packet
- * or the request buffer having filled with full packets.
- *
- * for ep-a..ep-d this will read multiple packets out when they
- * have been accepted.
- */
-static int
-read_fifo (struct net2280_ep *ep, struct net2280_request *req)
-{
- struct net2280_ep_regs __iomem *regs = ep->regs;
- u8 *buf = req->req.buf + req->req.actual;
- unsigned count, tmp, is_short;
- unsigned cleanup = 0, prevent = 0;
-
- /* erratum 0106 ... packets coming in during fifo reads might
- * be incompletely rejected. not all cases have workarounds.
- */
- if (ep->dev->chiprev == 0x0100
- && ep->dev->gadget.speed == USB_SPEED_FULL) {
- udelay (1);
- tmp = readl (&ep->regs->ep_stat);
- if ((tmp & (1 << NAK_OUT_PACKETS)))
- cleanup = 1;
- else if ((tmp & (1 << FIFO_FULL))) {
- start_out_naking (ep);
- prevent = 1;
- }
- /* else: hope we don't see the problem */
- }
-
- /* never overflow the rx buffer. the fifo reads packets until
- * it sees a short one; we might not be ready for them all.
- */
- prefetchw (buf);
- count = readl (&regs->ep_avail);
- if (unlikely (count == 0)) {
- udelay (1);
- tmp = readl (&ep->regs->ep_stat);
- count = readl (&regs->ep_avail);
- /* handled that data already? */
- if (count == 0 && (tmp & (1 << NAK_OUT_PACKETS)) == 0)
- return 0;
- }
-
- tmp = req->req.length - req->req.actual;
- if (count > tmp) {
- /* as with DMA, data overflow gets flushed */
- if ((tmp % ep->ep.maxpacket) != 0) {
- ERROR (ep->dev,
- "%s out fifo %d bytes, expected %d\n",
- ep->ep.name, count, tmp);
- req->req.status = -EOVERFLOW;
- cleanup = 1;
- /* NAK_OUT_PACKETS will be set, so flushing is safe;
- * the next read will start with the next packet
- */
- } /* else it's a ZLP, no worries */
- count = tmp;
- }
- req->req.actual += count;
-
- is_short = (count == 0) || ((count % ep->ep.maxpacket) != 0);
-
- VDEBUG (ep->dev, "read %s fifo (OUT) %d bytes%s%s%s req %p %d/%d\n",
- ep->ep.name, count, is_short ? " (short)" : "",
- cleanup ? " flush" : "", prevent ? " nak" : "",
- req, req->req.actual, req->req.length);
-
- while (count >= 4) {
- tmp = readl (&regs->ep_data);
- cpu_to_le32s (&tmp);
- put_unaligned (tmp, (u32 *)buf);
- buf += 4;
- count -= 4;
- }
- if (count) {
- tmp = readl (&regs->ep_data);
- /* LE conversion is implicit here: */
- do {
- *buf++ = (u8) tmp;
- tmp >>= 8;
- } while (--count);
- }
- if (cleanup)
- out_flush (ep);
- if (prevent) {
- writel ((1 << CLEAR_NAK_OUT_PACKETS), &ep->regs->ep_rsp);
- (void) readl (&ep->regs->ep_rsp);
- }
-
- return is_short || ((req->req.actual == req->req.length)
- && !req->req.zero);
-}
-
-/* fill out dma descriptor to match a given request */
-static void
-fill_dma_desc (struct net2280_ep *ep, struct net2280_request *req, int valid)
-{
- struct net2280_dma *td = req->td;
- u32 dmacount = req->req.length;
-
- /* don't let DMA continue after a short OUT packet,
- * so overruns can't affect the next transfer.
- * in case of overruns on max-size packets, we can't
- * stop the fifo from filling but we can flush it.
- */
- if (ep->is_in)
- dmacount |= (1 << DMA_DIRECTION);
- if ((!ep->is_in && (dmacount % ep->ep.maxpacket) != 0)
- || ep->dev->pdev->device != 0x2280)
- dmacount |= (1 << END_OF_CHAIN);
-
- req->valid = valid;
- if (valid)
- dmacount |= (1 << VALID_BIT);
- if (likely(!req->req.no_interrupt || !use_dma_chaining))
- dmacount |= (1 << DMA_DONE_INTERRUPT_ENABLE);
-
- /* td->dmadesc = previously set by caller */
- td->dmaaddr = cpu_to_le32 (req->req.dma);
-
- /* 2280 may be polling VALID_BIT through ep->dma->dmadesc */
- wmb ();
- td->dmacount = cpu_to_le32(dmacount);
-}
-
-static const u32 dmactl_default =
- (1 << DMA_SCATTER_GATHER_DONE_INTERRUPT)
- | (1 << DMA_CLEAR_COUNT_ENABLE)
- /* erratum 0116 workaround part 1 (use POLLING) */
- | (POLL_100_USEC << DESCRIPTOR_POLLING_RATE)
- | (1 << DMA_VALID_BIT_POLLING_ENABLE)
- | (1 << DMA_VALID_BIT_ENABLE)
- | (1 << DMA_SCATTER_GATHER_ENABLE)
- /* erratum 0116 workaround part 2 (no AUTOSTART) */
- | (1 << DMA_ENABLE);
-
-static inline void spin_stop_dma (struct net2280_dma_regs __iomem *dma)
-{
- handshake (&dma->dmactl, (1 << DMA_ENABLE), 0, 50);
-}
-
-static inline void stop_dma (struct net2280_dma_regs __iomem *dma)
-{
- writel (readl (&dma->dmactl) & ~(1 << DMA_ENABLE), &dma->dmactl);
- spin_stop_dma (dma);
-}
-
-static void start_queue (struct net2280_ep *ep, u32 dmactl, u32 td_dma)
-{
- struct net2280_dma_regs __iomem *dma = ep->dma;
- unsigned int tmp = (1 << VALID_BIT) | (ep->is_in << DMA_DIRECTION);
-
- if (ep->dev->pdev->device != 0x2280)
- tmp |= (1 << END_OF_CHAIN);
-
- writel (tmp, &dma->dmacount);
- writel (readl (&dma->dmastat), &dma->dmastat);
-
- writel (td_dma, &dma->dmadesc);
- writel (dmactl, &dma->dmactl);
-
- /* erratum 0116 workaround part 3: pci arbiter away from net2280 */
- (void) readl (&ep->dev->pci->pcimstctl);
-
- writel ((1 << DMA_START), &dma->dmastat);
-
- if (!ep->is_in)
- stop_out_naking (ep);
-}
-
-static void start_dma (struct net2280_ep *ep, struct net2280_request *req)
-{
- u32 tmp;
- struct net2280_dma_regs __iomem *dma = ep->dma;
-
- /* FIXME can't use DMA for ZLPs */
-
- /* on this path we "know" there's no dma active (yet) */
- WARN_ON (readl (&dma->dmactl) & (1 << DMA_ENABLE));
- writel (0, &ep->dma->dmactl);
-
- /* previous OUT packet might have been short */
- if (!ep->is_in && ((tmp = readl (&ep->regs->ep_stat))
- & (1 << NAK_OUT_PACKETS)) != 0) {
- writel ((1 << SHORT_PACKET_TRANSFERRED_INTERRUPT),
- &ep->regs->ep_stat);
-
- tmp = readl (&ep->regs->ep_avail);
- if (tmp) {
- writel (readl (&dma->dmastat), &dma->dmastat);
-
- /* transfer all/some fifo data */
- writel (req->req.dma, &dma->dmaaddr);
- tmp = min (tmp, req->req.length);
-
- /* dma irq, faking scatterlist status */
- req->td->dmacount = cpu_to_le32 (req->req.length - tmp);
- writel ((1 << DMA_DONE_INTERRUPT_ENABLE)
- | tmp, &dma->dmacount);
- req->td->dmadesc = 0;
- req->valid = 1;
-
- writel ((1 << DMA_ENABLE), &dma->dmactl);
- writel ((1 << DMA_START), &dma->dmastat);
- return;
- }
- }
-
- tmp = dmactl_default;
-
- /* force packet boundaries between dma requests, but prevent the
- * controller from automagically writing a last "short" packet
- * (zero length) unless the driver explicitly said to do that.
- */
- if (ep->is_in) {
- if (likely ((req->req.length % ep->ep.maxpacket) != 0
- || req->req.zero)) {
- tmp |= (1 << DMA_FIFO_VALIDATE);
- ep->in_fifo_validate = 1;
- } else
- ep->in_fifo_validate = 0;
- }
-
- /* init req->td, pointing to the current dummy */
- req->td->dmadesc = cpu_to_le32 (ep->td_dma);
- fill_dma_desc (ep, req, 1);
-
- if (!use_dma_chaining)
- req->td->dmacount |= cpu_to_le32 (1 << END_OF_CHAIN);
-
- start_queue (ep, tmp, req->td_dma);
-}
-
-static inline void
-queue_dma (struct net2280_ep *ep, struct net2280_request *req, int valid)
-{
- struct net2280_dma *end;
- dma_addr_t tmp;
-
- /* swap new dummy for old, link; fill and maybe activate */
- end = ep->dummy;
- ep->dummy = req->td;
- req->td = end;
-
- tmp = ep->td_dma;
- ep->td_dma = req->td_dma;
- req->td_dma = tmp;
-
- end->dmadesc = cpu_to_le32 (ep->td_dma);
-
- fill_dma_desc (ep, req, valid);
-}
-
-static void
-done (struct net2280_ep *ep, struct net2280_request *req, int status)
-{
- struct net2280 *dev;
- unsigned stopped = ep->stopped;
-
- list_del_init (&req->queue);
-
- if (req->req.status == -EINPROGRESS)
- req->req.status = status;
- else
- status = req->req.status;
-
- dev = ep->dev;
- if (ep->dma)
- usb_gadget_unmap_request(&dev->gadget, &req->req, ep->is_in);
-
- if (status && status != -ESHUTDOWN)
- VDEBUG (dev, "complete %s req %p stat %d len %u/%u\n",
- ep->ep.name, &req->req, status,
- req->req.actual, req->req.length);
-
- /* don't modify queue heads during completion callback */
- ep->stopped = 1;
- spin_unlock (&dev->lock);
- req->req.complete (&ep->ep, &req->req);
- spin_lock (&dev->lock);
- ep->stopped = stopped;
-}
-
-/*-------------------------------------------------------------------------*/
-
-static int
-net2280_queue (struct usb_ep *_ep, struct usb_request *_req, gfp_t gfp_flags)
-{
- struct net2280_request *req;
- struct net2280_ep *ep;
- struct net2280 *dev;
- unsigned long flags;
-
- /* we always require a cpu-view buffer, so that we can
- * always use pio (as fallback or whatever).
- */
- req = container_of (_req, struct net2280_request, req);
- if (!_req || !_req->complete || !_req->buf
- || !list_empty (&req->queue))
- return -EINVAL;
- if (_req->length > (~0 & DMA_BYTE_COUNT_MASK))
- return -EDOM;
- ep = container_of (_ep, struct net2280_ep, ep);
- if (!_ep || (!ep->desc && ep->num != 0))
- return -EINVAL;
- dev = ep->dev;
- if (!dev->driver || dev->gadget.speed == USB_SPEED_UNKNOWN)
- return -ESHUTDOWN;
-
- /* FIXME implement PIO fallback for ZLPs with DMA */
- if (ep->dma && _req->length == 0)
- return -EOPNOTSUPP;
-
- /* set up dma mapping in case the caller didn't */
- if (ep->dma) {
- int ret;
-
- ret = usb_gadget_map_request(&dev->gadget, _req,
- ep->is_in);
- if (ret)
- return ret;
- }
-
-#if 0
- VDEBUG (dev, "%s queue req %p, len %d buf %p\n",
- _ep->name, _req, _req->length, _req->buf);
-#endif
-
- spin_lock_irqsave (&dev->lock, flags);
-
- _req->status = -EINPROGRESS;
- _req->actual = 0;
-
- /* kickstart this i/o queue? */
- if (list_empty (&ep->queue) && !ep->stopped) {
- /* use DMA if the endpoint supports it, else pio */
- if (ep->dma)
- start_dma (ep, req);
- else {
- /* maybe there's no control data, just status ack */
- if (ep->num == 0 && _req->length == 0) {
- allow_status (ep);
- done (ep, req, 0);
- VDEBUG (dev, "%s status ack\n", ep->ep.name);
- goto done;
- }
-
- /* PIO ... stuff the fifo, or unblock it. */
- if (ep->is_in)
- write_fifo (ep, _req);
- else if (list_empty (&ep->queue)) {
- u32 s;
-
- /* OUT FIFO might have packet(s) buffered */
- s = readl (&ep->regs->ep_stat);
- if ((s & (1 << FIFO_EMPTY)) == 0) {
- /* note: _req->short_not_ok is
- * ignored here since PIO _always_
- * stops queue advance here, and
- * _req->status doesn't change for
- * short reads (only _req->actual)
- */
- if (read_fifo (ep, req)) {
- done (ep, req, 0);
- if (ep->num == 0)
- allow_status (ep);
- /* don't queue it */
- req = NULL;
- } else
- s = readl (&ep->regs->ep_stat);
- }
-
- /* don't NAK, let the fifo fill */
- if (req && (s & (1 << NAK_OUT_PACKETS)))
- writel ((1 << CLEAR_NAK_OUT_PACKETS),
- &ep->regs->ep_rsp);
- }
- }
-
- } else if (ep->dma) {
- int valid = 1;
-
- if (ep->is_in) {
- int expect;
-
- /* preventing magic zlps is per-engine state, not
- * per-transfer; irq logic must recover hiccups.
- */
- expect = likely (req->req.zero
- || (req->req.length % ep->ep.maxpacket) != 0);
- if (expect != ep->in_fifo_validate)
- valid = 0;
- }
- queue_dma (ep, req, valid);
-
- } /* else the irq handler advances the queue. */
-
- ep->responded = 1;
- if (req)
- list_add_tail (&req->queue, &ep->queue);
-done:
- spin_unlock_irqrestore (&dev->lock, flags);
-
- /* pci writes may still be posted */
- return 0;
-}
-
-static inline void
-dma_done (
- struct net2280_ep *ep,
- struct net2280_request *req,
- u32 dmacount,
- int status
-)
-{
- req->req.actual = req->req.length - (DMA_BYTE_COUNT_MASK & dmacount);
- done (ep, req, status);
-}
-
-static void restart_dma (struct net2280_ep *ep);
-
-static void scan_dma_completions (struct net2280_ep *ep)
-{
- /* only look at descriptors that were "naturally" retired,
- * so fifo and list head state won't matter
- */
- while (!list_empty (&ep->queue)) {
- struct net2280_request *req;
- u32 tmp;
-
- req = list_entry (ep->queue.next,
- struct net2280_request, queue);
- if (!req->valid)
- break;
- rmb ();
- tmp = le32_to_cpup (&req->td->dmacount);
- if ((tmp & (1 << VALID_BIT)) != 0)
- break;
-
- /* SHORT_PACKET_TRANSFERRED_INTERRUPT handles "usb-short"
- * cases where DMA must be aborted; this code handles
- * all non-abort DMA completions.
- */
- if (unlikely (req->td->dmadesc == 0)) {
- /* paranoia */
- tmp = readl (&ep->dma->dmacount);
- if (tmp & DMA_BYTE_COUNT_MASK)
- break;
- /* single transfer mode */
- dma_done (ep, req, tmp, 0);
- break;
- } else if (!ep->is_in
- && (req->req.length % ep->ep.maxpacket) != 0) {
- tmp = readl (&ep->regs->ep_stat);
-
- /* AVOID TROUBLE HERE by not issuing short reads from
- * your gadget driver. That helps avoids errata 0121,
- * 0122, and 0124; not all cases trigger the warning.
- */
- if ((tmp & (1 << NAK_OUT_PACKETS)) == 0) {
- WARNING (ep->dev, "%s lost packet sync!\n",
- ep->ep.name);
- req->req.status = -EOVERFLOW;
- } else if ((tmp = readl (&ep->regs->ep_avail)) != 0) {
- /* fifo gets flushed later */
- ep->out_overflow = 1;
- DEBUG (ep->dev, "%s dma, discard %d len %d\n",
- ep->ep.name, tmp,
- req->req.length);
- req->req.status = -EOVERFLOW;
- }
- }
- dma_done (ep, req, tmp, 0);
- }
-}
-
-static void restart_dma (struct net2280_ep *ep)
-{
- struct net2280_request *req;
- u32 dmactl = dmactl_default;
-
- if (ep->stopped)
- return;
- req = list_entry (ep->queue.next, struct net2280_request, queue);
-
- if (!use_dma_chaining) {
- start_dma (ep, req);
- return;
- }
-
- /* the 2280 will be processing the queue unless queue hiccups after
- * the previous transfer:
- * IN: wanted automagic zlp, head doesn't (or vice versa)
- * DMA_FIFO_VALIDATE doesn't init from dma descriptors.
- * OUT: was "usb-short", we must restart.
- */
- if (ep->is_in && !req->valid) {
- struct net2280_request *entry, *prev = NULL;
- int reqmode, done = 0;
-
- DEBUG (ep->dev, "%s dma hiccup td %p\n", ep->ep.name, req->td);
- ep->in_fifo_validate = likely (req->req.zero
- || (req->req.length % ep->ep.maxpacket) != 0);
- if (ep->in_fifo_validate)
- dmactl |= (1 << DMA_FIFO_VALIDATE);
- list_for_each_entry (entry, &ep->queue, queue) {
- __le32 dmacount;
-
- if (entry == req)
- continue;
- dmacount = entry->td->dmacount;
- if (!done) {
- reqmode = likely (entry->req.zero
- || (entry->req.length
- % ep->ep.maxpacket) != 0);
- if (reqmode == ep->in_fifo_validate) {
- entry->valid = 1;
- dmacount |= valid_bit;
- entry->td->dmacount = dmacount;
- prev = entry;
- continue;
- } else {
- /* force a hiccup */
- prev->td->dmacount |= dma_done_ie;
- done = 1;
- }
- }
-
- /* walk the rest of the queue so unlinks behave */
- entry->valid = 0;
- dmacount &= ~valid_bit;
- entry->td->dmacount = dmacount;
- prev = entry;
- }
- }
-
- writel (0, &ep->dma->dmactl);
- start_queue (ep, dmactl, req->td_dma);
-}
-
-static void abort_dma (struct net2280_ep *ep)
-{
- /* abort the current transfer */
- if (likely (!list_empty (&ep->queue))) {
- /* FIXME work around errata 0121, 0122, 0124 */
- writel ((1 << DMA_ABORT), &ep->dma->dmastat);
- spin_stop_dma (ep->dma);
- } else
- stop_dma (ep->dma);
- scan_dma_completions (ep);
-}
-
-/* dequeue ALL requests */
-static void nuke (struct net2280_ep *ep)
-{
- struct net2280_request *req;
-
- /* called with spinlock held */
- ep->stopped = 1;
- if (ep->dma)
- abort_dma (ep);
- while (!list_empty (&ep->queue)) {
- req = list_entry (ep->queue.next,
- struct net2280_request,
- queue);
- done (ep, req, -ESHUTDOWN);
- }
-}
-
-/* dequeue JUST ONE request */
-static int net2280_dequeue (struct usb_ep *_ep, struct usb_request *_req)
-{
- struct net2280_ep *ep;
- struct net2280_request *req;
- unsigned long flags;
- u32 dmactl;
- int stopped;
-
- ep = container_of (_ep, struct net2280_ep, ep);
- if (!_ep || (!ep->desc && ep->num != 0) || !_req)
- return -EINVAL;
-
- spin_lock_irqsave (&ep->dev->lock, flags);
- stopped = ep->stopped;
-
- /* quiesce dma while we patch the queue */
- dmactl = 0;
- ep->stopped = 1;
- if (ep->dma) {
- dmactl = readl (&ep->dma->dmactl);
- /* WARNING erratum 0127 may kick in ... */
- stop_dma (ep->dma);
- scan_dma_completions (ep);
- }
-
- /* make sure it's still queued on this endpoint */
- list_for_each_entry (req, &ep->queue, queue) {
- if (&req->req == _req)
- break;
- }
- if (&req->req != _req) {
- spin_unlock_irqrestore (&ep->dev->lock, flags);
- return -EINVAL;
- }
-
- /* queue head may be partially complete. */
- if (ep->queue.next == &req->queue) {
- if (ep->dma) {
- DEBUG (ep->dev, "unlink (%s) dma\n", _ep->name);
- _req->status = -ECONNRESET;
- abort_dma (ep);
- if (likely (ep->queue.next == &req->queue)) {
- // NOTE: misreports single-transfer mode
- req->td->dmacount = 0; /* invalidate */
- dma_done (ep, req,
- readl (&ep->dma->dmacount),
- -ECONNRESET);
- }
- } else {
- DEBUG (ep->dev, "unlink (%s) pio\n", _ep->name);
- done (ep, req, -ECONNRESET);
- }
- req = NULL;
-
- /* patch up hardware chaining data */
- } else if (ep->dma && use_dma_chaining) {
- if (req->queue.prev == ep->queue.next) {
- writel (le32_to_cpu (req->td->dmadesc),
- &ep->dma->dmadesc);
- if (req->td->dmacount & dma_done_ie)
- writel (readl (&ep->dma->dmacount)
- | le32_to_cpu(dma_done_ie),
- &ep->dma->dmacount);
- } else {
- struct net2280_request *prev;
-
- prev = list_entry (req->queue.prev,
- struct net2280_request, queue);
- prev->td->dmadesc = req->td->dmadesc;
- if (req->td->dmacount & dma_done_ie)
- prev->td->dmacount |= dma_done_ie;
- }
- }
-
- if (req)
- done (ep, req, -ECONNRESET);
- ep->stopped = stopped;
-
- if (ep->dma) {
- /* turn off dma on inactive queues */
- if (list_empty (&ep->queue))
- stop_dma (ep->dma);
- else if (!ep->stopped) {
- /* resume current request, or start new one */
- if (req)
- writel (dmactl, &ep->dma->dmactl);
- else
- start_dma (ep, list_entry (ep->queue.next,
- struct net2280_request, queue));
- }
- }
-
- spin_unlock_irqrestore (&ep->dev->lock, flags);
- return 0;
-}
-
-/*-------------------------------------------------------------------------*/
-
-static int net2280_fifo_status (struct usb_ep *_ep);
-
-static int
-net2280_set_halt_and_wedge(struct usb_ep *_ep, int value, int wedged)
-{
- struct net2280_ep *ep;
- unsigned long flags;
- int retval = 0;
-
- ep = container_of (_ep, struct net2280_ep, ep);
- if (!_ep || (!ep->desc && ep->num != 0))
- return -EINVAL;
- if (!ep->dev->driver || ep->dev->gadget.speed == USB_SPEED_UNKNOWN)
- return -ESHUTDOWN;
- if (ep->desc /* not ep0 */ && (ep->desc->bmAttributes & 0x03)
- == USB_ENDPOINT_XFER_ISOC)
- return -EINVAL;
-
- spin_lock_irqsave (&ep->dev->lock, flags);
- if (!list_empty (&ep->queue))
- retval = -EAGAIN;
- else if (ep->is_in && value && net2280_fifo_status (_ep) != 0)
- retval = -EAGAIN;
- else {
- VDEBUG (ep->dev, "%s %s %s\n", _ep->name,
- value ? "set" : "clear",
- wedged ? "wedge" : "halt");
- /* set/clear, then synch memory views with the device */
- if (value) {
- if (ep->num == 0)
- ep->dev->protocol_stall = 1;
- else
- set_halt (ep);
- if (wedged)
- ep->wedged = 1;
- } else {
- clear_halt (ep);
- ep->wedged = 0;
- }
- (void) readl (&ep->regs->ep_rsp);
- }
- spin_unlock_irqrestore (&ep->dev->lock, flags);
-
- return retval;
-}
-
-static int
-net2280_set_halt(struct usb_ep *_ep, int value)
-{
- return net2280_set_halt_and_wedge(_ep, value, 0);
-}
-
-static int
-net2280_set_wedge(struct usb_ep *_ep)
-{
- if (!_ep || _ep->name == ep0name)
- return -EINVAL;
- return net2280_set_halt_and_wedge(_ep, 1, 1);
-}
-
-static int
-net2280_fifo_status (struct usb_ep *_ep)
-{
- struct net2280_ep *ep;
- u32 avail;
-
- ep = container_of (_ep, struct net2280_ep, ep);
- if (!_ep || (!ep->desc && ep->num != 0))
- return -ENODEV;
- if (!ep->dev->driver || ep->dev->gadget.speed == USB_SPEED_UNKNOWN)
- return -ESHUTDOWN;
-
- avail = readl (&ep->regs->ep_avail) & ((1 << 12) - 1);
- if (avail > ep->fifo_size)
- return -EOVERFLOW;
- if (ep->is_in)
- avail = ep->fifo_size - avail;
- return avail;
-}
-
-static void
-net2280_fifo_flush (struct usb_ep *_ep)
-{
- struct net2280_ep *ep;
-
- ep = container_of (_ep, struct net2280_ep, ep);
- if (!_ep || (!ep->desc && ep->num != 0))
- return;
- if (!ep->dev->driver || ep->dev->gadget.speed == USB_SPEED_UNKNOWN)
- return;
-
- writel ((1 << FIFO_FLUSH), &ep->regs->ep_stat);
- (void) readl (&ep->regs->ep_rsp);
-}
-
-static const struct usb_ep_ops net2280_ep_ops = {
- .enable = net2280_enable,
- .disable = net2280_disable,
-
- .alloc_request = net2280_alloc_request,
- .free_request = net2280_free_request,
-
- .queue = net2280_queue,
- .dequeue = net2280_dequeue,
-
- .set_halt = net2280_set_halt,
- .set_wedge = net2280_set_wedge,
- .fifo_status = net2280_fifo_status,
- .fifo_flush = net2280_fifo_flush,
-};
-
-/*-------------------------------------------------------------------------*/
-
-static int net2280_get_frame (struct usb_gadget *_gadget)
-{
- struct net2280 *dev;
- unsigned long flags;
- u16 retval;
-
- if (!_gadget)
- return -ENODEV;
- dev = container_of (_gadget, struct net2280, gadget);
- spin_lock_irqsave (&dev->lock, flags);
- retval = get_idx_reg (dev->regs, REG_FRAME) & 0x03ff;
- spin_unlock_irqrestore (&dev->lock, flags);
- return retval;
-}
-
-static int net2280_wakeup (struct usb_gadget *_gadget)
-{
- struct net2280 *dev;
- u32 tmp;
- unsigned long flags;
-
- if (!_gadget)
- return 0;
- dev = container_of (_gadget, struct net2280, gadget);
-
- spin_lock_irqsave (&dev->lock, flags);
- tmp = readl (&dev->usb->usbctl);
- if (tmp & (1 << DEVICE_REMOTE_WAKEUP_ENABLE))
- writel (1 << GENERATE_RESUME, &dev->usb->usbstat);
- spin_unlock_irqrestore (&dev->lock, flags);
-
- /* pci writes may still be posted */
- return 0;
-}
-
-static int net2280_set_selfpowered (struct usb_gadget *_gadget, int value)
-{
- struct net2280 *dev;
- u32 tmp;
- unsigned long flags;
-
- if (!_gadget)
- return 0;
- dev = container_of (_gadget, struct net2280, gadget);
-
- spin_lock_irqsave (&dev->lock, flags);
- tmp = readl (&dev->usb->usbctl);
- if (value)
- tmp |= (1 << SELF_POWERED_STATUS);
- else
- tmp &= ~(1 << SELF_POWERED_STATUS);
- writel (tmp, &dev->usb->usbctl);
- spin_unlock_irqrestore (&dev->lock, flags);
-
- return 0;
-}
-
-static int net2280_pullup(struct usb_gadget *_gadget, int is_on)
-{
- struct net2280 *dev;
- u32 tmp;
- unsigned long flags;
-
- if (!_gadget)
- return -ENODEV;
- dev = container_of (_gadget, struct net2280, gadget);
-
- spin_lock_irqsave (&dev->lock, flags);
- tmp = readl (&dev->usb->usbctl);
- dev->softconnect = (is_on != 0);
- if (is_on)
- tmp |= (1 << USB_DETECT_ENABLE);
- else
- tmp &= ~(1 << USB_DETECT_ENABLE);
- writel (tmp, &dev->usb->usbctl);
- spin_unlock_irqrestore (&dev->lock, flags);
-
- return 0;
-}
-
-static int net2280_start(struct usb_gadget *_gadget,
- struct usb_gadget_driver *driver);
-static int net2280_stop(struct usb_gadget *_gadget,
- struct usb_gadget_driver *driver);
-
-static const struct usb_gadget_ops net2280_ops = {
- .get_frame = net2280_get_frame,
- .wakeup = net2280_wakeup,
- .set_selfpowered = net2280_set_selfpowered,
- .pullup = net2280_pullup,
- .udc_start = net2280_start,
- .udc_stop = net2280_stop,
-};
-
-/*-------------------------------------------------------------------------*/
-
-#ifdef CONFIG_USB_GADGET_DEBUG_FILES
-
-/* FIXME move these into procfs, and use seq_file.
- * Sysfs _still_ doesn't behave for arbitrarily sized files,
- * and also doesn't help products using this with 2.4 kernels.
- */
-
-/* "function" sysfs attribute */
-static ssize_t function_show(struct device *_dev, struct device_attribute *attr,
- char *buf)
-{
- struct net2280 *dev = dev_get_drvdata (_dev);
-
- if (!dev->driver
- || !dev->driver->function
- || strlen (dev->driver->function) > PAGE_SIZE)
- return 0;
- return scnprintf (buf, PAGE_SIZE, "%s\n", dev->driver->function);
-}
-static DEVICE_ATTR_RO(function);
-
-static ssize_t registers_show(struct device *_dev,
- struct device_attribute *attr, char *buf)
-{
- struct net2280 *dev;
- char *next;
- unsigned size, t;
- unsigned long flags;
- int i;
- u32 t1, t2;
- const char *s;
-
- dev = dev_get_drvdata (_dev);
- next = buf;
- size = PAGE_SIZE;
- spin_lock_irqsave (&dev->lock, flags);
-
- if (dev->driver)
- s = dev->driver->driver.name;
- else
- s = "(none)";
-
- /* Main Control Registers */
- t = scnprintf (next, size, "%s version " DRIVER_VERSION
- ", chiprev %04x, dma %s\n\n"
- "devinit %03x fifoctl %08x gadget '%s'\n"
- "pci irqenb0 %02x irqenb1 %08x "
- "irqstat0 %04x irqstat1 %08x\n",
- driver_name, dev->chiprev,
- use_dma
- ? (use_dma_chaining ? "chaining" : "enabled")
- : "disabled",
- readl (&dev->regs->devinit),
- readl (&dev->regs->fifoctl),
- s,
- readl (&dev->regs->pciirqenb0),
- readl (&dev->regs->pciirqenb1),
- readl (&dev->regs->irqstat0),
- readl (&dev->regs->irqstat1));
- size -= t;
- next += t;
-
- /* USB Control Registers */
- t1 = readl (&dev->usb->usbctl);
- t2 = readl (&dev->usb->usbstat);
- if (t1 & (1 << VBUS_PIN)) {
- if (t2 & (1 << HIGH_SPEED))
- s = "high speed";
- else if (dev->gadget.speed == USB_SPEED_UNKNOWN)
- s = "powered";
- else
- s = "full speed";
- /* full speed bit (6) not working?? */
- } else
- s = "not attached";
- t = scnprintf (next, size,
- "stdrsp %08x usbctl %08x usbstat %08x "
- "addr 0x%02x (%s)\n",
- readl (&dev->usb->stdrsp), t1, t2,
- readl (&dev->usb->ouraddr), s);
- size -= t;
- next += t;
-
- /* PCI Master Control Registers */
-
- /* DMA Control Registers */
-
- /* Configurable EP Control Registers */
- for (i = 0; i < 7; i++) {
- struct net2280_ep *ep;
-
- ep = &dev->ep [i];
- if (i && !ep->desc)
- continue;
-
- t1 = readl (&ep->regs->ep_cfg);
- t2 = readl (&ep->regs->ep_rsp) & 0xff;
- t = scnprintf (next, size,
- "\n%s\tcfg %05x rsp (%02x) %s%s%s%s%s%s%s%s"
- "irqenb %02x\n",
- ep->ep.name, t1, t2,
- (t2 & (1 << CLEAR_NAK_OUT_PACKETS))
- ? "NAK " : "",
- (t2 & (1 << CLEAR_EP_HIDE_STATUS_PHASE))
- ? "hide " : "",
- (t2 & (1 << CLEAR_EP_FORCE_CRC_ERROR))
- ? "CRC " : "",
- (t2 & (1 << CLEAR_INTERRUPT_MODE))
- ? "interrupt " : "",
- (t2 & (1<<CLEAR_CONTROL_STATUS_PHASE_HANDSHAKE))
- ? "status " : "",
- (t2 & (1 << CLEAR_NAK_OUT_PACKETS_MODE))
- ? "NAKmode " : "",
- (t2 & (1 << CLEAR_ENDPOINT_TOGGLE))
- ? "DATA1 " : "DATA0 ",
- (t2 & (1 << CLEAR_ENDPOINT_HALT))
- ? "HALT " : "",
- readl (&ep->regs->ep_irqenb));
- size -= t;
- next += t;
-
- t = scnprintf (next, size,
- "\tstat %08x avail %04x "
- "(ep%d%s-%s)%s\n",
- readl (&ep->regs->ep_stat),
- readl (&ep->regs->ep_avail),
- t1 & 0x0f, DIR_STRING (t1),
- type_string (t1 >> 8),
- ep->stopped ? "*" : "");
- size -= t;
- next += t;
-
- if (!ep->dma)
- continue;
-
- t = scnprintf (next, size,
- " dma\tctl %08x stat %08x count %08x\n"
- "\taddr %08x desc %08x\n",
- readl (&ep->dma->dmactl),
- readl (&ep->dma->dmastat),
- readl (&ep->dma->dmacount),
- readl (&ep->dma->dmaaddr),
- readl (&ep->dma->dmadesc));
- size -= t;
- next += t;
-
- }
-
- /* Indexed Registers */
- // none yet
-
- /* Statistics */
- t = scnprintf (next, size, "\nirqs: ");
- size -= t;
- next += t;
- for (i = 0; i < 7; i++) {
- struct net2280_ep *ep;
-
- ep = &dev->ep [i];
- if (i && !ep->irqs)
- continue;
- t = scnprintf (next, size, " %s/%lu", ep->ep.name, ep->irqs);
- size -= t;
- next += t;
-
- }
- t = scnprintf (next, size, "\n");
- size -= t;
- next += t;
-
- spin_unlock_irqrestore (&dev->lock, flags);
-
- return PAGE_SIZE - size;
-}
-static DEVICE_ATTR_RO(registers);
-
-static ssize_t queues_show(struct device *_dev, struct device_attribute *attr,
- char *buf)
-{
- struct net2280 *dev;
- char *next;
- unsigned size;
- unsigned long flags;
- int i;
-
- dev = dev_get_drvdata (_dev);
- next = buf;
- size = PAGE_SIZE;
- spin_lock_irqsave (&dev->lock, flags);
-
- for (i = 0; i < 7; i++) {
- struct net2280_ep *ep = &dev->ep [i];
- struct net2280_request *req;
- int t;
-
- if (i != 0) {
- const struct usb_endpoint_descriptor *d;
-
- d = ep->desc;
- if (!d)
- continue;
- t = d->bEndpointAddress;
- t = scnprintf (next, size,
- "\n%s (ep%d%s-%s) max %04x %s fifo %d\n",
- ep->ep.name, t & USB_ENDPOINT_NUMBER_MASK,
- (t & USB_DIR_IN) ? "in" : "out",
- ({ char *val;
- switch (d->bmAttributes & 0x03) {
- case USB_ENDPOINT_XFER_BULK:
- val = "bulk"; break;
- case USB_ENDPOINT_XFER_INT:
- val = "intr"; break;
- default:
- val = "iso"; break;
- } val; }),
- usb_endpoint_maxp (d) & 0x1fff,
- ep->dma ? "dma" : "pio", ep->fifo_size
- );
- } else /* ep0 should only have one transfer queued */
- t = scnprintf (next, size, "ep0 max 64 pio %s\n",
- ep->is_in ? "in" : "out");
- if (t <= 0 || t > size)
- goto done;
- size -= t;
- next += t;
-
- if (list_empty (&ep->queue)) {
- t = scnprintf (next, size, "\t(nothing queued)\n");
- if (t <= 0 || t > size)
- goto done;
- size -= t;
- next += t;
- continue;
- }
- list_for_each_entry (req, &ep->queue, queue) {
- if (ep->dma && req->td_dma == readl (&ep->dma->dmadesc))
- t = scnprintf (next, size,
- "\treq %p len %d/%d "
- "buf %p (dmacount %08x)\n",
- &req->req, req->req.actual,
- req->req.length, req->req.buf,
- readl (&ep->dma->dmacount));
- else
- t = scnprintf (next, size,
- "\treq %p len %d/%d buf %p\n",
- &req->req, req->req.actual,
- req->req.length, req->req.buf);
- if (t <= 0 || t > size)
- goto done;
- size -= t;
- next += t;
-
- if (ep->dma) {
- struct net2280_dma *td;
-
- td = req->td;
- t = scnprintf (next, size, "\t td %08x "
- " count %08x buf %08x desc %08x\n",
- (u32) req->td_dma,
- le32_to_cpu (td->dmacount),
- le32_to_cpu (td->dmaaddr),
- le32_to_cpu (td->dmadesc));
- if (t <= 0 || t > size)
- goto done;
- size -= t;
- next += t;
- }
- }
- }
-
-done:
- spin_unlock_irqrestore (&dev->lock, flags);
- return PAGE_SIZE - size;
-}
-static DEVICE_ATTR_RO(queues);
-
-
-#else
-
-#define device_create_file(a,b) (0)
-#define device_remove_file(a,b) do { } while (0)
-
-#endif
-
-/*-------------------------------------------------------------------------*/
-
-/* another driver-specific mode might be a request type doing dma
- * to/from another device fifo instead of to/from memory.
- */
-
-static void set_fifo_mode (struct net2280 *dev, int mode)
-{
- /* keeping high bits preserves BAR2 */
- writel ((0xffff << PCI_BASE2_RANGE) | mode, &dev->regs->fifoctl);
-
- /* always ep-{a,b,e,f} ... maybe not ep-c or ep-d */
- INIT_LIST_HEAD (&dev->gadget.ep_list);
- list_add_tail (&dev->ep [1].ep.ep_list, &dev->gadget.ep_list);
- list_add_tail (&dev->ep [2].ep.ep_list, &dev->gadget.ep_list);
- switch (mode) {
- case 0:
- list_add_tail (&dev->ep [3].ep.ep_list, &dev->gadget.ep_list);
- list_add_tail (&dev->ep [4].ep.ep_list, &dev->gadget.ep_list);
- dev->ep [1].fifo_size = dev->ep [2].fifo_size = 1024;
- break;
- case 1:
- dev->ep [1].fifo_size = dev->ep [2].fifo_size = 2048;
- break;
- case 2:
- list_add_tail (&dev->ep [3].ep.ep_list, &dev->gadget.ep_list);
- dev->ep [1].fifo_size = 2048;
- dev->ep [2].fifo_size = 1024;
- break;
- }
- /* fifo sizes for ep0, ep-c, ep-d, ep-e, and ep-f never change */
- list_add_tail (&dev->ep [5].ep.ep_list, &dev->gadget.ep_list);
- list_add_tail (&dev->ep [6].ep.ep_list, &dev->gadget.ep_list);
-}
-
-/* keeping it simple:
- * - one bus driver, initted first;
- * - one function driver, initted second
- *
- * most of the work to support multiple net2280 controllers would
- * be to associate this gadget driver (yes?) with all of them, or
- * perhaps to bind specific drivers to specific devices.
- */
-
-static void usb_reset (struct net2280 *dev)
-{
- u32 tmp;
-
- dev->gadget.speed = USB_SPEED_UNKNOWN;
- (void) readl (&dev->usb->usbctl);
-
- net2280_led_init (dev);
-
- /* disable automatic responses, and irqs */
- writel (0, &dev->usb->stdrsp);
- writel (0, &dev->regs->pciirqenb0);
- writel (0, &dev->regs->pciirqenb1);
-
- /* clear old dma and irq state */
- for (tmp = 0; tmp < 4; tmp++) {
- struct net2280_ep *ep = &dev->ep [tmp + 1];
-
- if (ep->dma)
- abort_dma (ep);
- }
- writel (~0, &dev->regs->irqstat0),
- writel (~(1 << SUSPEND_REQUEST_INTERRUPT), &dev->regs->irqstat1),
-
- /* reset, and enable pci */
- tmp = readl (&dev->regs->devinit)
- | (1 << PCI_ENABLE)
- | (1 << FIFO_SOFT_RESET)
- | (1 << USB_SOFT_RESET)
- | (1 << M8051_RESET);
- writel (tmp, &dev->regs->devinit);
-
- /* standard fifo and endpoint allocations */
- set_fifo_mode (dev, (fifo_mode <= 2) ? fifo_mode : 0);
-}
-
-static void usb_reinit (struct net2280 *dev)
-{
- u32 tmp;
- int init_dma;
-
- /* use_dma changes are ignored till next device re-init */
- init_dma = use_dma;
-
- /* basic endpoint init */
- for (tmp = 0; tmp < 7; tmp++) {
- struct net2280_ep *ep = &dev->ep [tmp];
-
- ep->ep.name = ep_name [tmp];
- ep->dev = dev;
- ep->num = tmp;
-
- if (tmp > 0 && tmp <= 4) {
- ep->fifo_size = 1024;
- if (init_dma)
- ep->dma = &dev->dma [tmp - 1];
- } else
- ep->fifo_size = 64;
- ep->regs = &dev->epregs [tmp];
- ep_reset (dev->regs, ep);
- }
- usb_ep_set_maxpacket_limit(&dev->ep [0].ep, 64);
- usb_ep_set_maxpacket_limit(&dev->ep [5].ep, 64);
- usb_ep_set_maxpacket_limit(&dev->ep [6].ep, 64);
-
- dev->gadget.ep0 = &dev->ep [0].ep;
- dev->ep [0].stopped = 0;
- INIT_LIST_HEAD (&dev->gadget.ep0->ep_list);
-
- /* we want to prevent lowlevel/insecure access from the USB host,
- * but erratum 0119 means this enable bit is ignored
- */
- for (tmp = 0; tmp < 5; tmp++)
- writel (EP_DONTUSE, &dev->dep [tmp].dep_cfg);
-}
-
-static void ep0_start (struct net2280 *dev)
-{
- writel ( (1 << CLEAR_EP_HIDE_STATUS_PHASE)
- | (1 << CLEAR_NAK_OUT_PACKETS)
- | (1 << CLEAR_CONTROL_STATUS_PHASE_HANDSHAKE)
- , &dev->epregs [0].ep_rsp);
-
- /*
- * hardware optionally handles a bunch of standard requests
- * that the API hides from drivers anyway. have it do so.
- * endpoint status/features are handled in software, to
- * help pass tests for some dubious behavior.
- */
- writel ( (1 << SET_TEST_MODE)
- | (1 << SET_ADDRESS)
- | (1 << DEVICE_SET_CLEAR_DEVICE_REMOTE_WAKEUP)
- | (1 << GET_DEVICE_STATUS)
- | (1 << GET_INTERFACE_STATUS)
- , &dev->usb->stdrsp);
- writel ( (1 << USB_ROOT_PORT_WAKEUP_ENABLE)
- | (1 << SELF_POWERED_USB_DEVICE)
- | (1 << REMOTE_WAKEUP_SUPPORT)
- | (dev->softconnect << USB_DETECT_ENABLE)
- | (1 << SELF_POWERED_STATUS)
- , &dev->usb->usbctl);
-
- /* enable irqs so we can see ep0 and general operation */
- writel ( (1 << SETUP_PACKET_INTERRUPT_ENABLE)
- | (1 << ENDPOINT_0_INTERRUPT_ENABLE)
- , &dev->regs->pciirqenb0);
- writel ( (1 << PCI_INTERRUPT_ENABLE)
- | (1 << PCI_MASTER_ABORT_RECEIVED_INTERRUPT_ENABLE)
- | (1 << PCI_TARGET_ABORT_RECEIVED_INTERRUPT_ENABLE)
- | (1 << PCI_RETRY_ABORT_INTERRUPT_ENABLE)
- | (1 << VBUS_INTERRUPT_ENABLE)
- | (1 << ROOT_PORT_RESET_INTERRUPT_ENABLE)
- | (1 << SUSPEND_REQUEST_CHANGE_INTERRUPT_ENABLE)
- , &dev->regs->pciirqenb1);
-
- /* don't leave any writes posted */
- (void) readl (&dev->usb->usbctl);
-}
-
-/* when a driver is successfully registered, it will receive
- * control requests including set_configuration(), which enables
- * non-control requests. then usb traffic follows until a
- * disconnect is reported. then a host may connect again, or
- * the driver might get unbound.
- */
-static int net2280_start(struct usb_gadget *_gadget,
- struct usb_gadget_driver *driver)
-{
- struct net2280 *dev;
- int retval;
- unsigned i;
-
- /* insist on high speed support from the driver, since
- * (dev->usb->xcvrdiag & FORCE_FULL_SPEED_MODE)
- * "must not be used in normal operation"
- */
- if (!driver || driver->max_speed < USB_SPEED_HIGH
- || !driver->setup)
- return -EINVAL;
-
- dev = container_of (_gadget, struct net2280, gadget);
-
- for (i = 0; i < 7; i++)
- dev->ep [i].irqs = 0;
-
- /* hook up the driver ... */
- dev->softconnect = 1;
- driver->driver.bus = NULL;
- dev->driver = driver;
-
- retval = device_create_file (&dev->pdev->dev, &dev_attr_function);
- if (retval) goto err_unbind;
- retval = device_create_file (&dev->pdev->dev, &dev_attr_queues);
- if (retval) goto err_func;
-
- /* Enable force-full-speed testing mode, if desired */
- if (full_speed)
- writel(1 << FORCE_FULL_SPEED_MODE, &dev->usb->xcvrdiag);
-
- /* ... then enable host detection and ep0; and we're ready
- * for set_configuration as well as eventual disconnect.
- */
- net2280_led_active (dev, 1);
- ep0_start (dev);
-
- DEBUG (dev, "%s ready, usbctl %08x stdrsp %08x\n",
- driver->driver.name,
- readl (&dev->usb->usbctl),
- readl (&dev->usb->stdrsp));
-
- /* pci writes may still be posted */
- return 0;
-
-err_func:
- device_remove_file (&dev->pdev->dev, &dev_attr_function);
-err_unbind:
- dev->driver = NULL;
- return retval;
-}
-
-static void
-stop_activity (struct net2280 *dev, struct usb_gadget_driver *driver)
-{
- int i;
-
- /* don't disconnect if it's not connected */
- if (dev->gadget.speed == USB_SPEED_UNKNOWN)
- driver = NULL;
-
- /* stop hardware; prevent new request submissions;
- * and kill any outstanding requests.
- */
- usb_reset (dev);
- for (i = 0; i < 7; i++)
- nuke (&dev->ep [i]);
-
- /* report disconnect; the driver is already quiesced */
- if (driver) {
- spin_unlock(&dev->lock);
- driver->disconnect(&dev->gadget);
- spin_lock(&dev->lock);
- }
-
- usb_reinit (dev);
-}
-
-static int net2280_stop(struct usb_gadget *_gadget,
- struct usb_gadget_driver *driver)
-{
- struct net2280 *dev;
- unsigned long flags;
-
- dev = container_of (_gadget, struct net2280, gadget);
-
- spin_lock_irqsave (&dev->lock, flags);
- stop_activity (dev, driver);
- spin_unlock_irqrestore (&dev->lock, flags);
-
- dev->driver = NULL;
-
- net2280_led_active (dev, 0);
-
- /* Disable full-speed test mode */
- writel(0, &dev->usb->xcvrdiag);
-
- device_remove_file (&dev->pdev->dev, &dev_attr_function);
- device_remove_file (&dev->pdev->dev, &dev_attr_queues);
-
- DEBUG(dev, "unregistered driver '%s'\n",
- driver ? driver->driver.name : "");
-
- return 0;
-}
-
-/*-------------------------------------------------------------------------*/
-
-/* handle ep0, ep-e, ep-f with 64 byte packets: packet per irq.
- * also works for dma-capable endpoints, in pio mode or just
- * to manually advance the queue after short OUT transfers.
- */
-static void handle_ep_small (struct net2280_ep *ep)
-{
- struct net2280_request *req;
- u32 t;
- /* 0 error, 1 mid-data, 2 done */
- int mode = 1;
-
- if (!list_empty (&ep->queue))
- req = list_entry (ep->queue.next,
- struct net2280_request, queue);
- else
- req = NULL;
-
- /* ack all, and handle what we care about */
- t = readl (&ep->regs->ep_stat);
- ep->irqs++;
-#if 0
- VDEBUG (ep->dev, "%s ack ep_stat %08x, req %p\n",
- ep->ep.name, t, req ? &req->req : 0);
-#endif
- if (!ep->is_in || ep->dev->pdev->device == 0x2280)
- writel (t & ~(1 << NAK_OUT_PACKETS), &ep->regs->ep_stat);
- else
- /* Added for 2282 */
- writel (t, &ep->regs->ep_stat);
-
- /* for ep0, monitor token irqs to catch data stage length errors
- * and to synchronize on status.
- *
- * also, to defer reporting of protocol stalls ... here's where
- * data or status first appears, handling stalls here should never
- * cause trouble on the host side..
- *
- * control requests could be slightly faster without token synch for
- * status, but status can jam up that way.
- */
- if (unlikely (ep->num == 0)) {
- if (ep->is_in) {
- /* status; stop NAKing */
- if (t & (1 << DATA_OUT_PING_TOKEN_INTERRUPT)) {
- if (ep->dev->protocol_stall) {
- ep->stopped = 1;
- set_halt (ep);
- }
- if (!req)
- allow_status (ep);
- mode = 2;
- /* reply to extra IN data tokens with a zlp */
- } else if (t & (1 << DATA_IN_TOKEN_INTERRUPT)) {
- if (ep->dev->protocol_stall) {
- ep->stopped = 1;
- set_halt (ep);
- mode = 2;
- } else if (ep->responded &&
- !req && !ep->stopped)
- write_fifo (ep, NULL);
- }
- } else {
- /* status; stop NAKing */
- if (t & (1 << DATA_IN_TOKEN_INTERRUPT)) {
- if (ep->dev->protocol_stall) {
- ep->stopped = 1;
- set_halt (ep);
- }
- mode = 2;
- /* an extra OUT token is an error */
- } else if (((t & (1 << DATA_OUT_PING_TOKEN_INTERRUPT))
- && req
- && req->req.actual == req->req.length)
- || (ep->responded && !req)) {
- ep->dev->protocol_stall = 1;
- set_halt (ep);
- ep->stopped = 1;
- if (req)
- done (ep, req, -EOVERFLOW);
- req = NULL;
- }
- }
- }
-
- if (unlikely (!req))
- return;
-
- /* manual DMA queue advance after short OUT */
- if (likely (ep->dma)) {
- if (t & (1 << SHORT_PACKET_TRANSFERRED_INTERRUPT)) {
- u32 count;
- int stopped = ep->stopped;
-
- /* TRANSFERRED works around OUT_DONE erratum 0112.
- * we expect (N <= maxpacket) bytes; host wrote M.
- * iff (M < N) we won't ever see a DMA interrupt.
- */
- ep->stopped = 1;
- for (count = 0; ; t = readl (&ep->regs->ep_stat)) {
-
- /* any preceding dma transfers must finish.
- * dma handles (M >= N), may empty the queue
- */
- scan_dma_completions (ep);
- if (unlikely (list_empty (&ep->queue)
- || ep->out_overflow)) {
- req = NULL;
- break;
- }
- req = list_entry (ep->queue.next,
- struct net2280_request, queue);
-
- /* here either (M < N), a "real" short rx;
- * or (M == N) and the queue didn't empty
- */
- if (likely (t & (1 << FIFO_EMPTY))) {
- count = readl (&ep->dma->dmacount);
- count &= DMA_BYTE_COUNT_MASK;
- if (readl (&ep->dma->dmadesc)
- != req->td_dma)
- req = NULL;
- break;
- }
- udelay(1);
- }
-
- /* stop DMA, leave ep NAKing */
- writel ((1 << DMA_ABORT), &ep->dma->dmastat);
- spin_stop_dma (ep->dma);
-
- if (likely (req)) {
- req->td->dmacount = 0;
- t = readl (&ep->regs->ep_avail);
- dma_done (ep, req, count,
- (ep->out_overflow || t)
- ? -EOVERFLOW : 0);
- }
-
- /* also flush to prevent erratum 0106 trouble */
- if (unlikely (ep->out_overflow
- || (ep->dev->chiprev == 0x0100
- && ep->dev->gadget.speed
- == USB_SPEED_FULL))) {
- out_flush (ep);
- ep->out_overflow = 0;
- }
-
- /* (re)start dma if needed, stop NAKing */
- ep->stopped = stopped;
- if (!list_empty (&ep->queue))
- restart_dma (ep);
- } else
- DEBUG (ep->dev, "%s dma ep_stat %08x ??\n",
- ep->ep.name, t);
- return;
-
- /* data packet(s) received (in the fifo, OUT) */
- } else if (t & (1 << DATA_PACKET_RECEIVED_INTERRUPT)) {
- if (read_fifo (ep, req) && ep->num != 0)
- mode = 2;
-
- /* data packet(s) transmitted (IN) */
- } else if (t & (1 << DATA_PACKET_TRANSMITTED_INTERRUPT)) {
- unsigned len;
-
- len = req->req.length - req->req.actual;
- if (len > ep->ep.maxpacket)
- len = ep->ep.maxpacket;
- req->req.actual += len;
-
- /* if we wrote it all, we're usually done */
- if (req->req.actual == req->req.length) {
- if (ep->num == 0) {
- /* send zlps until the status stage */
- } else if (!req->req.zero || len != ep->ep.maxpacket)
- mode = 2;
- }
-
- /* there was nothing to do ... */
- } else if (mode == 1)
- return;
-
- /* done */
- if (mode == 2) {
- /* stream endpoints often resubmit/unlink in completion */
- done (ep, req, 0);
-
- /* maybe advance queue to next request */
- if (ep->num == 0) {
- /* NOTE: net2280 could let gadget driver start the
- * status stage later. since not all controllers let
- * them control that, the api doesn't (yet) allow it.
- */
- if (!ep->stopped)
- allow_status (ep);
- req = NULL;
- } else {
- if (!list_empty (&ep->queue) && !ep->stopped)
- req = list_entry (ep->queue.next,
- struct net2280_request, queue);
- else
- req = NULL;
- if (req && !ep->is_in)
- stop_out_naking (ep);
- }
- }
-
- /* is there a buffer for the next packet?
- * for best streaming performance, make sure there is one.
- */
- if (req && !ep->stopped) {
-
- /* load IN fifo with next packet (may be zlp) */
- if (t & (1 << DATA_PACKET_TRANSMITTED_INTERRUPT))
- write_fifo (ep, &req->req);
- }
-}
-
-static struct net2280_ep *
-get_ep_by_addr (struct net2280 *dev, u16 wIndex)
-{
- struct net2280_ep *ep;
-
- if ((wIndex & USB_ENDPOINT_NUMBER_MASK) == 0)
- return &dev->ep [0];
- list_for_each_entry (ep, &dev->gadget.ep_list, ep.ep_list) {
- u8 bEndpointAddress;
-
- if (!ep->desc)
- continue;
- bEndpointAddress = ep->desc->bEndpointAddress;
- if ((wIndex ^ bEndpointAddress) & USB_DIR_IN)
- continue;
- if ((wIndex & 0x0f) == (bEndpointAddress & 0x0f))
- return ep;
- }
- return NULL;
-}
-
-static void handle_stat0_irqs (struct net2280 *dev, u32 stat)
-{
- struct net2280_ep *ep;
- u32 num, scratch;
-
- /* most of these don't need individual acks */
- stat &= ~(1 << INTA_ASSERTED);
- if (!stat)
- return;
- // DEBUG (dev, "irqstat0 %04x\n", stat);
-
- /* starting a control request? */
- if (unlikely (stat & (1 << SETUP_PACKET_INTERRUPT))) {
- union {
- u32 raw [2];
- struct usb_ctrlrequest r;
- } u;
- int tmp;
- struct net2280_request *req;
-
- if (dev->gadget.speed == USB_SPEED_UNKNOWN) {
- if (readl (&dev->usb->usbstat) & (1 << HIGH_SPEED))
- dev->gadget.speed = USB_SPEED_HIGH;
- else
- dev->gadget.speed = USB_SPEED_FULL;
- net2280_led_speed (dev, dev->gadget.speed);
- DEBUG(dev, "%s\n", usb_speed_string(dev->gadget.speed));
- }
-
- ep = &dev->ep [0];
- ep->irqs++;
-
- /* make sure any leftover request state is cleared */
- stat &= ~(1 << ENDPOINT_0_INTERRUPT);
- while (!list_empty (&ep->queue)) {
- req = list_entry (ep->queue.next,
- struct net2280_request, queue);
- done (ep, req, (req->req.actual == req->req.length)
- ? 0 : -EPROTO);
- }
- ep->stopped = 0;
- dev->protocol_stall = 0;
-
- if (ep->dev->pdev->device == 0x2280)
- tmp = (1 << FIFO_OVERFLOW)
- | (1 << FIFO_UNDERFLOW);
- else
- tmp = 0;
-
- writel (tmp | (1 << TIMEOUT)
- | (1 << USB_STALL_SENT)
- | (1 << USB_IN_NAK_SENT)
- | (1 << USB_IN_ACK_RCVD)
- | (1 << USB_OUT_PING_NAK_SENT)
- | (1 << USB_OUT_ACK_SENT)
- | (1 << SHORT_PACKET_OUT_DONE_INTERRUPT)
- | (1 << SHORT_PACKET_TRANSFERRED_INTERRUPT)
- | (1 << DATA_PACKET_RECEIVED_INTERRUPT)
- | (1 << DATA_PACKET_TRANSMITTED_INTERRUPT)
- | (1 << DATA_OUT_PING_TOKEN_INTERRUPT)
- | (1 << DATA_IN_TOKEN_INTERRUPT)
- , &ep->regs->ep_stat);
- u.raw [0] = readl (&dev->usb->setup0123);
- u.raw [1] = readl (&dev->usb->setup4567);
-
- cpu_to_le32s (&u.raw [0]);
- cpu_to_le32s (&u.raw [1]);
-
- tmp = 0;
-
-#define w_value le16_to_cpu(u.r.wValue)
-#define w_index le16_to_cpu(u.r.wIndex)
-#define w_length le16_to_cpu(u.r.wLength)
-
- /* ack the irq */
- writel (1 << SETUP_PACKET_INTERRUPT, &dev->regs->irqstat0);
- stat ^= (1 << SETUP_PACKET_INTERRUPT);
-
- /* watch control traffic at the token level, and force
- * synchronization before letting the status stage happen.
- * FIXME ignore tokens we'll NAK, until driver responds.
- * that'll mean a lot less irqs for some drivers.
- */
- ep->is_in = (u.r.bRequestType & USB_DIR_IN) != 0;
- if (ep->is_in) {
- scratch = (1 << DATA_PACKET_TRANSMITTED_INTERRUPT)
- | (1 << DATA_OUT_PING_TOKEN_INTERRUPT)
- | (1 << DATA_IN_TOKEN_INTERRUPT);
- stop_out_naking (ep);
- } else
- scratch = (1 << DATA_PACKET_RECEIVED_INTERRUPT)
- | (1 << DATA_OUT_PING_TOKEN_INTERRUPT)
- | (1 << DATA_IN_TOKEN_INTERRUPT);
- writel (scratch, &dev->epregs [0].ep_irqenb);
-
- /* we made the hardware handle most lowlevel requests;
- * everything else goes uplevel to the gadget code.
- */
- ep->responded = 1;
- switch (u.r.bRequest) {
- case USB_REQ_GET_STATUS: {
- struct net2280_ep *e;
- __le32 status;
-
- /* hw handles device and interface status */
- if (u.r.bRequestType != (USB_DIR_IN|USB_RECIP_ENDPOINT))
- goto delegate;
- if ((e = get_ep_by_addr (dev, w_index)) == NULL
- || w_length > 2)
- goto do_stall;
-
- if (readl (&e->regs->ep_rsp)
- & (1 << SET_ENDPOINT_HALT))
- status = cpu_to_le32 (1);
- else
- status = cpu_to_le32 (0);
-
- /* don't bother with a request object! */
- writel (0, &dev->epregs [0].ep_irqenb);
- set_fifo_bytecount (ep, w_length);
- writel ((__force u32)status, &dev->epregs [0].ep_data);
- allow_status (ep);
- VDEBUG (dev, "%s stat %02x\n", ep->ep.name, status);
- goto next_endpoints;
- }
- break;
- case USB_REQ_CLEAR_FEATURE: {
- struct net2280_ep *e;
-
- /* hw handles device features */
- if (u.r.bRequestType != USB_RECIP_ENDPOINT)
- goto delegate;
- if (w_value != USB_ENDPOINT_HALT
- || w_length != 0)
- goto do_stall;
- if ((e = get_ep_by_addr (dev, w_index)) == NULL)
- goto do_stall;
- if (e->wedged) {
- VDEBUG(dev, "%s wedged, halt not cleared\n",
- ep->ep.name);
- } else {
- VDEBUG(dev, "%s clear halt\n", ep->ep.name);
- clear_halt(e);
- }
- allow_status (ep);
- goto next_endpoints;
- }
- break;
- case USB_REQ_SET_FEATURE: {
- struct net2280_ep *e;
-
- /* hw handles device features */
- if (u.r.bRequestType != USB_RECIP_ENDPOINT)
- goto delegate;
- if (w_value != USB_ENDPOINT_HALT
- || w_length != 0)
- goto do_stall;
- if ((e = get_ep_by_addr (dev, w_index)) == NULL)
- goto do_stall;
- if (e->ep.name == ep0name)
- goto do_stall;
- set_halt (e);
- allow_status (ep);
- VDEBUG (dev, "%s set halt\n", ep->ep.name);
- goto next_endpoints;
- }
- break;
- default:
-delegate:
- VDEBUG (dev, "setup %02x.%02x v%04x i%04x l%04x "
- "ep_cfg %08x\n",
- u.r.bRequestType, u.r.bRequest,
- w_value, w_index, w_length,
- readl (&ep->regs->ep_cfg));
- ep->responded = 0;
- spin_unlock (&dev->lock);
- tmp = dev->driver->setup (&dev->gadget, &u.r);
- spin_lock (&dev->lock);
- }
-
- /* stall ep0 on error */
- if (tmp < 0) {
-do_stall:
- VDEBUG (dev, "req %02x.%02x protocol STALL; stat %d\n",
- u.r.bRequestType, u.r.bRequest, tmp);
- dev->protocol_stall = 1;
- }
-
- /* some in/out token irq should follow; maybe stall then.
- * driver must queue a request (even zlp) or halt ep0
- * before the host times out.
- */
- }
-
-#undef w_value
-#undef w_index
-#undef w_length
-
-next_endpoints:
- /* endpoint data irq ? */
- scratch = stat & 0x7f;
- stat &= ~0x7f;
- for (num = 0; scratch; num++) {
- u32 t;
-
- /* do this endpoint's FIFO and queue need tending? */
- t = 1 << num;
- if ((scratch & t) == 0)
- continue;
- scratch ^= t;
-
- ep = &dev->ep [num];
- handle_ep_small (ep);
- }
-
- if (stat)
- DEBUG (dev, "unhandled irqstat0 %08x\n", stat);
-}
-
-#define DMA_INTERRUPTS ( \
- (1 << DMA_D_INTERRUPT) \
- | (1 << DMA_C_INTERRUPT) \
- | (1 << DMA_B_INTERRUPT) \
- | (1 << DMA_A_INTERRUPT))
-#define PCI_ERROR_INTERRUPTS ( \
- (1 << PCI_MASTER_ABORT_RECEIVED_INTERRUPT) \
- | (1 << PCI_TARGET_ABORT_RECEIVED_INTERRUPT) \
- | (1 << PCI_RETRY_ABORT_INTERRUPT))
-
-static void handle_stat1_irqs (struct net2280 *dev, u32 stat)
-{
- struct net2280_ep *ep;
- u32 tmp, num, mask, scratch;
-
- /* after disconnect there's nothing else to do! */
- tmp = (1 << VBUS_INTERRUPT) | (1 << ROOT_PORT_RESET_INTERRUPT);
- mask = (1 << HIGH_SPEED) | (1 << FULL_SPEED);
-
- /* VBUS disconnect is indicated by VBUS_PIN and VBUS_INTERRUPT set.
- * Root Port Reset is indicated by ROOT_PORT_RESET_INTERRUPT set and
- * both HIGH_SPEED and FULL_SPEED clear (as ROOT_PORT_RESET_INTERRUPT
- * only indicates a change in the reset state).
- */
- if (stat & tmp) {
- writel (tmp, &dev->regs->irqstat1);
- if ((((stat & (1 << ROOT_PORT_RESET_INTERRUPT))
- && ((readl (&dev->usb->usbstat) & mask)
- == 0))
- || ((readl (&dev->usb->usbctl)
- & (1 << VBUS_PIN)) == 0)
- ) && ( dev->gadget.speed != USB_SPEED_UNKNOWN)) {
- DEBUG (dev, "disconnect %s\n",
- dev->driver->driver.name);
- stop_activity (dev, dev->driver);
- ep0_start (dev);
- return;
- }
- stat &= ~tmp;
-
- /* vBUS can bounce ... one of many reasons to ignore the
- * notion of hotplug events on bus connect/disconnect!
- */
- if (!stat)
- return;
- }
-
- /* NOTE: chip stays in PCI D0 state for now, but it could
- * enter D1 to save more power
- */
- tmp = (1 << SUSPEND_REQUEST_CHANGE_INTERRUPT);
- if (stat & tmp) {
- writel (tmp, &dev->regs->irqstat1);
- if (stat & (1 << SUSPEND_REQUEST_INTERRUPT)) {
- if (dev->driver->suspend)
- dev->driver->suspend (&dev->gadget);
- if (!enable_suspend)
- stat &= ~(1 << SUSPEND_REQUEST_INTERRUPT);
- } else {
- if (dev->driver->resume)
- dev->driver->resume (&dev->gadget);
- /* at high speed, note erratum 0133 */
- }
- stat &= ~tmp;
- }
-
- /* clear any other status/irqs */
- if (stat)
- writel (stat, &dev->regs->irqstat1);
-
- /* some status we can just ignore */
- if (dev->pdev->device == 0x2280)
- stat &= ~((1 << CONTROL_STATUS_INTERRUPT)
- | (1 << SUSPEND_REQUEST_INTERRUPT)
- | (1 << RESUME_INTERRUPT)
- | (1 << SOF_INTERRUPT));
- else
- stat &= ~((1 << CONTROL_STATUS_INTERRUPT)
- | (1 << RESUME_INTERRUPT)
- | (1 << SOF_DOWN_INTERRUPT)
- | (1 << SOF_INTERRUPT));
-
- if (!stat)
- return;
- // DEBUG (dev, "irqstat1 %08x\n", stat);
-
- /* DMA status, for ep-{a,b,c,d} */
- scratch = stat & DMA_INTERRUPTS;
- stat &= ~DMA_INTERRUPTS;
- scratch >>= 9;
- for (num = 0; scratch; num++) {
- struct net2280_dma_regs __iomem *dma;
-
- tmp = 1 << num;
- if ((tmp & scratch) == 0)
- continue;
- scratch ^= tmp;
-
- ep = &dev->ep [num + 1];
- dma = ep->dma;
-
- if (!dma)
- continue;
-
- /* clear ep's dma status */
- tmp = readl (&dma->dmastat);
- writel (tmp, &dma->dmastat);
-
- /* chaining should stop on abort, short OUT from fifo,
- * or (stat0 codepath) short OUT transfer.
- */
- if (!use_dma_chaining) {
- if ((tmp & (1 << DMA_TRANSACTION_DONE_INTERRUPT))
- == 0) {
- DEBUG (ep->dev, "%s no xact done? %08x\n",
- ep->ep.name, tmp);
- continue;
- }
- stop_dma (ep->dma);
- }
-
- /* OUT transfers terminate when the data from the
- * host is in our memory. Process whatever's done.
- * On this path, we know transfer's last packet wasn't
- * less than req->length. NAK_OUT_PACKETS may be set,
- * or the FIFO may already be holding new packets.
- *
- * IN transfers can linger in the FIFO for a very
- * long time ... we ignore that for now, accounting
- * precisely (like PIO does) needs per-packet irqs
- */
- scan_dma_completions (ep);
-
- /* disable dma on inactive queues; else maybe restart */
- if (list_empty (&ep->queue)) {
- if (use_dma_chaining)
- stop_dma (ep->dma);
- } else {
- tmp = readl (&dma->dmactl);
- if (!use_dma_chaining
- || (tmp & (1 << DMA_ENABLE)) == 0)
- restart_dma (ep);
- else if (ep->is_in && use_dma_chaining) {
- struct net2280_request *req;
- __le32 dmacount;
-
- /* the descriptor at the head of the chain
- * may still have VALID_BIT clear; that's
- * used to trigger changing DMA_FIFO_VALIDATE
- * (affects automagic zlp writes).
- */
- req = list_entry (ep->queue.next,
- struct net2280_request, queue);
- dmacount = req->td->dmacount;
- dmacount &= cpu_to_le32 (
- (1 << VALID_BIT)
- | DMA_BYTE_COUNT_MASK);
- if (dmacount && (dmacount & valid_bit) == 0)
- restart_dma (ep);
- }
- }
- ep->irqs++;
- }
-
- /* NOTE: there are other PCI errors we might usefully notice.
- * if they appear very often, here's where to try recovering.
- */
- if (stat & PCI_ERROR_INTERRUPTS) {
- ERROR (dev, "pci dma error; stat %08x\n", stat);
- stat &= ~PCI_ERROR_INTERRUPTS;
- /* these are fatal errors, but "maybe" they won't
- * happen again ...
- */
- stop_activity (dev, dev->driver);
- ep0_start (dev);
- stat = 0;
- }
-
- if (stat)
- DEBUG (dev, "unhandled irqstat1 %08x\n", stat);
-}
-
-static irqreturn_t net2280_irq (int irq, void *_dev)
-{
- struct net2280 *dev = _dev;
-
- /* shared interrupt, not ours */
- if (!(readl(&dev->regs->irqstat0) & (1 << INTA_ASSERTED)))
- return IRQ_NONE;
-
- spin_lock (&dev->lock);
-
- /* handle disconnect, dma, and more */
- handle_stat1_irqs (dev, readl (&dev->regs->irqstat1));
-
- /* control requests and PIO */
- handle_stat0_irqs (dev, readl (&dev->regs->irqstat0));
-
- spin_unlock (&dev->lock);
-
- return IRQ_HANDLED;
-}
-
-/*-------------------------------------------------------------------------*/
-
-static void gadget_release (struct device *_dev)
-{
- struct net2280 *dev = dev_get_drvdata (_dev);
-
- kfree (dev);
-}
-
-/* tear down the binding between this driver and the pci device */
-
-static void net2280_remove (struct pci_dev *pdev)
-{
- struct net2280 *dev = pci_get_drvdata (pdev);
-
- usb_del_gadget_udc(&dev->gadget);
-
- BUG_ON(dev->driver);
-
- /* then clean up the resources we allocated during probe() */
- net2280_led_shutdown (dev);
- if (dev->requests) {
- int i;
- for (i = 1; i < 5; i++) {
- if (!dev->ep [i].dummy)
- continue;
- pci_pool_free (dev->requests, dev->ep [i].dummy,
- dev->ep [i].td_dma);
- }
- pci_pool_destroy (dev->requests);
- }
- if (dev->got_irq)
- free_irq (pdev->irq, dev);
- if (dev->regs)
- iounmap (dev->regs);
- if (dev->region)
- release_mem_region (pci_resource_start (pdev, 0),
- pci_resource_len (pdev, 0));
- if (dev->enabled)
- pci_disable_device (pdev);
- device_remove_file (&pdev->dev, &dev_attr_registers);
-
- INFO (dev, "unbind\n");
-}
-
-/* wrap this driver around the specified device, but
- * don't respond over USB until a gadget driver binds to us.
- */
-
-static int net2280_probe (struct pci_dev *pdev, const struct pci_device_id *id)
-{
- struct net2280 *dev;
- unsigned long resource, len;
- void __iomem *base = NULL;
- int retval, i;
-
- /* alloc, and start init */
- dev = kzalloc (sizeof *dev, GFP_KERNEL);
- if (dev == NULL){
- retval = -ENOMEM;
- goto done;
- }
-
- pci_set_drvdata (pdev, dev);
- spin_lock_init (&dev->lock);
- dev->pdev = pdev;
- dev->gadget.ops = &net2280_ops;
- dev->gadget.max_speed = USB_SPEED_HIGH;
-
- /* the "gadget" abstracts/virtualizes the controller */
- dev->gadget.name = driver_name;
-
- /* now all the pci goodies ... */
- if (pci_enable_device (pdev) < 0) {
- retval = -ENODEV;
- goto done;
- }
- dev->enabled = 1;
-
- /* BAR 0 holds all the registers
- * BAR 1 is 8051 memory; unused here (note erratum 0103)
- * BAR 2 is fifo memory; unused here
- */
- resource = pci_resource_start (pdev, 0);
- len = pci_resource_len (pdev, 0);
- if (!request_mem_region (resource, len, driver_name)) {
- DEBUG (dev, "controller already in use\n");
- retval = -EBUSY;
- goto done;
- }
- dev->region = 1;
-
- /* FIXME provide firmware download interface to put
- * 8051 code into the chip, e.g. to turn on PCI PM.
- */
-
- base = ioremap_nocache (resource, len);
- if (base == NULL) {
- DEBUG (dev, "can't map memory\n");
- retval = -EFAULT;
- goto done;
- }
- dev->regs = (struct net2280_regs __iomem *) base;
- dev->usb = (struct net2280_usb_regs __iomem *) (base + 0x0080);
- dev->pci = (struct net2280_pci_regs __iomem *) (base + 0x0100);
- dev->dma = (struct net2280_dma_regs __iomem *) (base + 0x0180);
- dev->dep = (struct net2280_dep_regs __iomem *) (base + 0x0200);
- dev->epregs = (struct net2280_ep_regs __iomem *) (base + 0x0300);
-
- /* put into initial config, link up all endpoints */
- writel (0, &dev->usb->usbctl);
- usb_reset (dev);
- usb_reinit (dev);
-
- /* irq setup after old hardware is cleaned up */
- if (!pdev->irq) {
- ERROR (dev, "No IRQ. Check PCI setup!\n");
- retval = -ENODEV;
- goto done;
- }
-
- if (request_irq (pdev->irq, net2280_irq, IRQF_SHARED, driver_name, dev)
- != 0) {
- ERROR (dev, "request interrupt %d failed\n", pdev->irq);
- retval = -EBUSY;
- goto done;
- }
- dev->got_irq = 1;
-
- /* DMA setup */
- /* NOTE: we know only the 32 LSBs of dma addresses may be nonzero */
- dev->requests = pci_pool_create ("requests", pdev,
- sizeof (struct net2280_dma),
- 0 /* no alignment requirements */,
- 0 /* or page-crossing issues */);
- if (!dev->requests) {
- DEBUG (dev, "can't get request pool\n");
- retval = -ENOMEM;
- goto done;
- }
- for (i = 1; i < 5; i++) {
- struct net2280_dma *td;
-
- td = pci_pool_alloc (dev->requests, GFP_KERNEL,
- &dev->ep [i].td_dma);
- if (!td) {
- DEBUG (dev, "can't get dummy %d\n", i);
- retval = -ENOMEM;
- goto done;
- }
- td->dmacount = 0; /* not VALID */
- td->dmadesc = td->dmaaddr;
- dev->ep [i].dummy = td;
- }
-
- /* enable lower-overhead pci memory bursts during DMA */
- writel ( (1 << DMA_MEMORY_WRITE_AND_INVALIDATE_ENABLE)
- // 256 write retries may not be enough...
- // | (1 << PCI_RETRY_ABORT_ENABLE)
- | (1 << DMA_READ_MULTIPLE_ENABLE)
- | (1 << DMA_READ_LINE_ENABLE)
- , &dev->pci->pcimstctl);
- /* erratum 0115 shouldn't appear: Linux inits PCI_LATENCY_TIMER */
- pci_set_master (pdev);
- pci_try_set_mwi (pdev);
-
- /* ... also flushes any posted pci writes */
- dev->chiprev = get_idx_reg (dev->regs, REG_CHIPREV) & 0xffff;
-
- /* done */
- INFO (dev, "%s\n", driver_desc);
- INFO (dev, "irq %d, pci mem %p, chip rev %04x\n",
- pdev->irq, base, dev->chiprev);
- INFO (dev, "version: " DRIVER_VERSION "; dma %s\n",
- use_dma
- ? (use_dma_chaining ? "chaining" : "enabled")
- : "disabled");
- retval = device_create_file (&pdev->dev, &dev_attr_registers);
- if (retval) goto done;
-
- retval = usb_add_gadget_udc_release(&pdev->dev, &dev->gadget,
- gadget_release);
- if (retval)
- goto done;
- return 0;
-
-done:
- if (dev)
- net2280_remove (pdev);
- return retval;
-}
-
-/* make sure the board is quiescent; otherwise it will continue
- * generating IRQs across the upcoming reboot.
- */
-
-static void net2280_shutdown (struct pci_dev *pdev)
-{
- struct net2280 *dev = pci_get_drvdata (pdev);
-
- /* disable IRQs */
- writel (0, &dev->regs->pciirqenb0);
- writel (0, &dev->regs->pciirqenb1);
-
- /* disable the pullup so the host will think we're gone */
- writel (0, &dev->usb->usbctl);
-
- /* Disable full-speed test mode */
- writel(0, &dev->usb->xcvrdiag);
-}
-
-
-/*-------------------------------------------------------------------------*/
-
-static const struct pci_device_id pci_ids [] = { {
- .class = ((PCI_CLASS_SERIAL_USB << 8) | 0xfe),
- .class_mask = ~0,
- .vendor = 0x17cc,
- .device = 0x2280,
- .subvendor = PCI_ANY_ID,
- .subdevice = PCI_ANY_ID,
-}, {
- .class = ((PCI_CLASS_SERIAL_USB << 8) | 0xfe),
- .class_mask = ~0,
- .vendor = 0x17cc,
- .device = 0x2282,
- .subvendor = PCI_ANY_ID,
- .subdevice = PCI_ANY_ID,
-
-}, { /* end: all zeroes */ }
-};
-MODULE_DEVICE_TABLE (pci, pci_ids);
-
-/* pci driver glue; this is a "new style" PCI driver module */
-static struct pci_driver net2280_pci_driver = {
- .name = (char *) driver_name,
- .id_table = pci_ids,
-
- .probe = net2280_probe,
- .remove = net2280_remove,
- .shutdown = net2280_shutdown,
-
- /* FIXME add power management support */
-};
-
-MODULE_DESCRIPTION (DRIVER_DESC);
-MODULE_AUTHOR ("David Brownell");
-MODULE_LICENSE ("GPL");
-
-static int __init init (void)
-{
- if (!use_dma)
- use_dma_chaining = 0;
- return pci_register_driver (&net2280_pci_driver);
-}
-module_init (init);
-
-static void __exit cleanup (void)
-{
- pci_unregister_driver (&net2280_pci_driver);
-}
-module_exit (cleanup);
diff --git a/drivers/usb/gadget/net2280.h b/drivers/usb/gadget/net2280.h
deleted file mode 100644
index a844be0d683a..000000000000
--- a/drivers/usb/gadget/net2280.h
+++ /dev/null
@@ -1,308 +0,0 @@
-/*
- * NetChip 2280 high/full speed USB device controller.
- * Unlike many such controllers, this one talks PCI.
- */
-
-/*
- * Copyright (C) 2002 NetChip Technology, Inc. (http://www.netchip.com)
- * Copyright (C) 2003 David Brownell
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- */
-
-#include <linux/usb/net2280.h>
-
-/*-------------------------------------------------------------------------*/
-
-#ifdef __KERNEL__
-
-/* indexed registers [11.10] are accessed indirectly
- * caller must own the device lock.
- */
-
-static inline u32
-get_idx_reg (struct net2280_regs __iomem *regs, u32 index)
-{
- writel (index, &regs->idxaddr);
- /* NOTE: synchs device/cpu memory views */
- return readl (&regs->idxdata);
-}
-
-static inline void
-set_idx_reg (struct net2280_regs __iomem *regs, u32 index, u32 value)
-{
- writel (index, &regs->idxaddr);
- writel (value, &regs->idxdata);
- /* posted, may not be visible yet */
-}
-
-#endif /* __KERNEL__ */
-
-
-#define REG_DIAG 0x0
-#define RETRY_COUNTER 16
-#define FORCE_PCI_SERR 11
-#define FORCE_PCI_INTERRUPT 10
-#define FORCE_USB_INTERRUPT 9
-#define FORCE_CPU_INTERRUPT 8
-#define ILLEGAL_BYTE_ENABLES 5
-#define FAST_TIMES 4
-#define FORCE_RECEIVE_ERROR 2
-#define FORCE_TRANSMIT_CRC_ERROR 0
-#define REG_FRAME 0x02 /* from last sof */
-#define REG_CHIPREV 0x03 /* in bcd */
-#define REG_HS_NAK_RATE 0x0a /* NAK per N uframes */
-
-#define CHIPREV_1 0x0100
-#define CHIPREV_1A 0x0110
-
-#ifdef __KERNEL__
-
-/* ep a-f highspeed and fullspeed maxpacket, addresses
- * computed from ep->num
- */
-#define REG_EP_MAXPKT(dev,num) (((num) + 1) * 0x10 + \
- (((dev)->gadget.speed == USB_SPEED_HIGH) ? 0 : 1))
-
-/*-------------------------------------------------------------------------*/
-
-/* [8.3] for scatter/gather i/o
- * use struct net2280_dma_regs bitfields
- */
-struct net2280_dma {
- __le32 dmacount;
- __le32 dmaaddr; /* the buffer */
- __le32 dmadesc; /* next dma descriptor */
- __le32 _reserved;
-} __attribute__ ((aligned (16)));
-
-/*-------------------------------------------------------------------------*/
-
-/* DRIVER DATA STRUCTURES and UTILITIES */
-
-struct net2280_ep {
- struct usb_ep ep;
- struct net2280_ep_regs __iomem *regs;
- struct net2280_dma_regs __iomem *dma;
- struct net2280_dma *dummy;
- dma_addr_t td_dma; /* of dummy */
- struct net2280 *dev;
- unsigned long irqs;
-
- /* analogous to a host-side qh */
- struct list_head queue;
- const struct usb_endpoint_descriptor *desc;
- unsigned num : 8,
- fifo_size : 12,
- in_fifo_validate : 1,
- out_overflow : 1,
- stopped : 1,
- wedged : 1,
- is_in : 1,
- is_iso : 1,
- responded : 1;
-};
-
-static inline void allow_status (struct net2280_ep *ep)
-{
- /* ep0 only */
- writel ( (1 << CLEAR_CONTROL_STATUS_PHASE_HANDSHAKE)
- | (1 << CLEAR_NAK_OUT_PACKETS)
- | (1 << CLEAR_NAK_OUT_PACKETS_MODE)
- , &ep->regs->ep_rsp);
- ep->stopped = 1;
-}
-
-/* count (<= 4) bytes in the next fifo write will be valid */
-static inline void set_fifo_bytecount (struct net2280_ep *ep, unsigned count)
-{
- writeb (count, 2 + (u8 __iomem *) &ep->regs->ep_cfg);
-}
-
-struct net2280_request {
- struct usb_request req;
- struct net2280_dma *td;
- dma_addr_t td_dma;
- struct list_head queue;
- unsigned mapped : 1,
- valid : 1;
-};
-
-struct net2280 {
- /* each pci device provides one gadget, several endpoints */
- struct usb_gadget gadget;
- spinlock_t lock;
- struct net2280_ep ep [7];
- struct usb_gadget_driver *driver;
- unsigned enabled : 1,
- protocol_stall : 1,
- softconnect : 1,
- got_irq : 1,
- region : 1;
- u16 chiprev;
-
- /* pci state used to access those endpoints */
- struct pci_dev *pdev;
- struct net2280_regs __iomem *regs;
- struct net2280_usb_regs __iomem *usb;
- struct net2280_pci_regs __iomem *pci;
- struct net2280_dma_regs __iomem *dma;
- struct net2280_dep_regs __iomem *dep;
- struct net2280_ep_regs __iomem *epregs;
-
- struct pci_pool *requests;
- // statistics...
-};
-
-static inline void set_halt (struct net2280_ep *ep)
-{
- /* ep0 and bulk/intr endpoints */
- writel ( (1 << CLEAR_CONTROL_STATUS_PHASE_HANDSHAKE)
- /* set NAK_OUT for erratum 0114 */
- | ((ep->dev->chiprev == CHIPREV_1) << SET_NAK_OUT_PACKETS)
- | (1 << SET_ENDPOINT_HALT)
- , &ep->regs->ep_rsp);
-}
-
-static inline void clear_halt (struct net2280_ep *ep)
-{
- /* ep0 and bulk/intr endpoints */
- writel ( (1 << CLEAR_ENDPOINT_HALT)
- | (1 << CLEAR_ENDPOINT_TOGGLE)
- /* unless the gadget driver left a short packet in the
- * fifo, this reverses the erratum 0114 workaround.
- */
- | ((ep->dev->chiprev == CHIPREV_1) << CLEAR_NAK_OUT_PACKETS)
- , &ep->regs->ep_rsp);
-}
-
-#ifdef USE_RDK_LEDS
-
-static inline void net2280_led_init (struct net2280 *dev)
-{
- /* LED3 (green) is on during USB activity. note erratum 0113. */
- writel ((1 << GPIO3_LED_SELECT)
- | (1 << GPIO3_OUTPUT_ENABLE)
- | (1 << GPIO2_OUTPUT_ENABLE)
- | (1 << GPIO1_OUTPUT_ENABLE)
- | (1 << GPIO0_OUTPUT_ENABLE)
- , &dev->regs->gpioctl);
-}
-
-/* indicate speed with bi-color LED 0/1 */
-static inline
-void net2280_led_speed (struct net2280 *dev, enum usb_device_speed speed)
-{
- u32 val = readl (&dev->regs->gpioctl);
- switch (speed) {
- case USB_SPEED_HIGH: /* green */
- val &= ~(1 << GPIO0_DATA);
- val |= (1 << GPIO1_DATA);
- break;
- case USB_SPEED_FULL: /* red */
- val &= ~(1 << GPIO1_DATA);
- val |= (1 << GPIO0_DATA);
- break;
- default: /* (off/black) */
- val &= ~((1 << GPIO1_DATA) | (1 << GPIO0_DATA));
- break;
- }
- writel (val, &dev->regs->gpioctl);
-}
-
-/* indicate power with LED 2 */
-static inline void net2280_led_active (struct net2280 *dev, int is_active)
-{
- u32 val = readl (&dev->regs->gpioctl);
-
- // FIXME this LED never seems to turn on.
- if (is_active)
- val |= GPIO2_DATA;
- else
- val &= ~GPIO2_DATA;
- writel (val, &dev->regs->gpioctl);
-}
-static inline void net2280_led_shutdown (struct net2280 *dev)
-{
- /* turn off all four GPIO*_DATA bits */
- writel (readl (&dev->regs->gpioctl) & ~0x0f,
- &dev->regs->gpioctl);
-}
-
-#else
-
-#define net2280_led_init(dev) do { } while (0)
-#define net2280_led_speed(dev, speed) do { } while (0)
-#define net2280_led_shutdown(dev) do { } while (0)
-
-#endif
-
-/*-------------------------------------------------------------------------*/
-
-#define xprintk(dev,level,fmt,args...) \
- printk(level "%s %s: " fmt , driver_name , \
- pci_name(dev->pdev) , ## args)
-
-#ifdef DEBUG
-#undef DEBUG
-#define DEBUG(dev,fmt,args...) \
- xprintk(dev , KERN_DEBUG , fmt , ## args)
-#else
-#define DEBUG(dev,fmt,args...) \
- do { } while (0)
-#endif /* DEBUG */
-
-#ifdef VERBOSE
-#define VDEBUG DEBUG
-#else
-#define VDEBUG(dev,fmt,args...) \
- do { } while (0)
-#endif /* VERBOSE */
-
-#define ERROR(dev,fmt,args...) \
- xprintk(dev , KERN_ERR , fmt , ## args)
-#define WARNING(dev,fmt,args...) \
- xprintk(dev , KERN_WARNING , fmt , ## args)
-#define INFO(dev,fmt,args...) \
- xprintk(dev , KERN_INFO , fmt , ## args)
-
-/*-------------------------------------------------------------------------*/
-
-static inline void start_out_naking (struct net2280_ep *ep)
-{
- /* NOTE: hardware races lurk here, and PING protocol issues */
- writel ((1 << SET_NAK_OUT_PACKETS), &ep->regs->ep_rsp);
- /* synch with device */
- readl (&ep->regs->ep_rsp);
-}
-
-#ifdef DEBUG
-static inline void assert_out_naking (struct net2280_ep *ep, const char *where)
-{
- u32 tmp = readl (&ep->regs->ep_stat);
-
- if ((tmp & (1 << NAK_OUT_PACKETS)) == 0) {
- DEBUG (ep->dev, "%s %s %08x !NAK\n",
- ep->ep.name, where, tmp);
- writel ((1 << SET_NAK_OUT_PACKETS),
- &ep->regs->ep_rsp);
- }
-}
-#define ASSERT_OUT_NAKING(ep) assert_out_naking(ep,__func__)
-#else
-#define ASSERT_OUT_NAKING(ep) do {} while (0)
-#endif
-
-static inline void stop_out_naking (struct net2280_ep *ep)
-{
- u32 tmp;
-
- tmp = readl (&ep->regs->ep_stat);
- if ((tmp & (1 << NAK_OUT_PACKETS)) != 0)
- writel ((1 << CLEAR_NAK_OUT_PACKETS), &ep->regs->ep_rsp);
-}
-
-#endif /* __KERNEL__ */
diff --git a/drivers/usb/gadget/u_os_desc.h b/drivers/usb/gadget/u_os_desc.h
index ea5cf8c2da28..947b7ddff691 100644
--- a/drivers/usb/gadget/u_os_desc.h
+++ b/drivers/usb/gadget/u_os_desc.h
@@ -35,27 +35,63 @@
#define USB_EXT_PROP_UNICODE_LINK 6
#define USB_EXT_PROP_UNICODE_MULTI 7
+static inline u8 *__usb_ext_prop_ptr(u8 *buf, size_t offset)
+{
+ return buf + offset;
+}
+
+static inline u8 *usb_ext_prop_size_ptr(u8 *buf)
+{
+ return __usb_ext_prop_ptr(buf, USB_EXT_PROP_DW_SIZE);
+}
+
+static inline u8 *usb_ext_prop_type_ptr(u8 *buf)
+{
+ return __usb_ext_prop_ptr(buf, USB_EXT_PROP_DW_PROPERTY_DATA_TYPE);
+}
+
+static inline u8 *usb_ext_prop_name_len_ptr(u8 *buf)
+{
+ return __usb_ext_prop_ptr(buf, USB_EXT_PROP_W_PROPERTY_NAME_LENGTH);
+}
+
+static inline u8 *usb_ext_prop_name_ptr(u8 *buf)
+{
+ return __usb_ext_prop_ptr(buf, USB_EXT_PROP_B_PROPERTY_NAME);
+}
+
+static inline u8 *usb_ext_prop_data_len_ptr(u8 *buf, size_t off)
+{
+ return __usb_ext_prop_ptr(buf,
+ USB_EXT_PROP_DW_PROPERTY_DATA_LENGTH + off);
+}
+
+static inline u8 *usb_ext_prop_data_ptr(u8 *buf, size_t off)
+{
+ return __usb_ext_prop_ptr(buf, USB_EXT_PROP_B_PROPERTY_DATA + off);
+}
+
static inline void usb_ext_prop_put_size(u8 *buf, int dw_size)
{
- put_unaligned_le32(dw_size, &buf[USB_EXT_PROP_DW_SIZE]);
+ put_unaligned_le32(dw_size, usb_ext_prop_size_ptr(buf));
}
static inline void usb_ext_prop_put_type(u8 *buf, int type)
{
- put_unaligned_le32(type, &buf[USB_EXT_PROP_DW_PROPERTY_DATA_TYPE]);
+ put_unaligned_le32(type, usb_ext_prop_type_ptr(buf));
}
static inline int usb_ext_prop_put_name(u8 *buf, const char *name, int pnl)
{
int result;
- put_unaligned_le16(pnl, &buf[USB_EXT_PROP_W_PROPERTY_NAME_LENGTH]);
+ put_unaligned_le16(pnl, usb_ext_prop_name_len_ptr(buf));
result = utf8s_to_utf16s(name, strlen(name), UTF16_LITTLE_ENDIAN,
- (wchar_t *) &buf[USB_EXT_PROP_B_PROPERTY_NAME], pnl - 2);
+ (wchar_t *) usb_ext_prop_name_ptr(buf), pnl - 2);
if (result < 0)
return result;
- put_unaligned_le16(0, &buf[USB_EXT_PROP_B_PROPERTY_NAME + pnl]);
+ put_unaligned_le16(0, &buf[USB_EXT_PROP_B_PROPERTY_NAME + pnl - 2]);
return pnl;
}
@@ -63,26 +99,23 @@ static inline int usb_ext_prop_put_name(u8 *buf, const char *name, int pnl)
static inline void usb_ext_prop_put_binary(u8 *buf, int pnl, const u8 *data,
int data_len)
{
- put_unaligned_le32(data_len,
- &buf[USB_EXT_PROP_DW_PROPERTY_DATA_LENGTH + pnl]);
- memcpy(&buf[USB_EXT_PROP_B_PROPERTY_DATA + pnl], data, data_len);
+ put_unaligned_le32(data_len, usb_ext_prop_data_len_ptr(buf, pnl));
+ memcpy(usb_ext_prop_data_ptr(buf, pnl), data, data_len);
}
static inline int usb_ext_prop_put_unicode(u8 *buf, int pnl, const char *string,
int data_len)
{
int result;
- put_unaligned_le32(data_len,
- &buf[USB_EXT_PROP_DW_PROPERTY_DATA_LENGTH + pnl]);
-
+ put_unaligned_le32(data_len, usb_ext_prop_data_len_ptr(buf, pnl));
result = utf8s_to_utf16s(string, data_len >> 1, UTF16_LITTLE_ENDIAN,
- (wchar_t *) &buf[USB_EXT_PROP_B_PROPERTY_DATA + pnl],
+ (wchar_t *) usb_ext_prop_data_ptr(buf, pnl),
data_len - 2);
if (result < 0)
return result;
put_unaligned_le16(0,
- &buf[USB_EXT_PROP_B_PROPERTY_DATA + pnl + data_len]);
+ &buf[USB_EXT_PROP_B_PROPERTY_DATA + pnl + data_len - 2]);
return data_len;
}
diff --git a/drivers/usb/gadget/udc/Kconfig b/drivers/usb/gadget/udc/Kconfig
new file mode 100644
index 000000000000..5151f947a4f5
--- /dev/null
+++ b/drivers/usb/gadget/udc/Kconfig
@@ -0,0 +1,385 @@
+#
+# USB Gadget support on a system involves
+# (a) a peripheral controller, and
+# (b) the gadget driver using it.
+#
+# NOTE: Gadget support ** DOES NOT ** depend on host-side CONFIG_USB !!
+#
+# - Host systems (like PCs) need CONFIG_USB (with "A" jacks).
+# - Peripherals (like PDAs) need CONFIG_USB_GADGET (with "B" jacks).
+# - Some systems have both kinds of controllers.
+#
+# With help from a special transceiver and a "Mini-AB" jack, systems with
+# both kinds of controller can also support "USB On-the-Go" (CONFIG_USB_OTG).
+#
+
+#
+# USB Peripheral Controller Support
+#
+# The order here is alphabetical, except that integrated controllers go
+# before discrete ones so they will be the initial/default value:
+# - integrated/SOC controllers first
+# - licensed IP used in both SOC and discrete versions
+# - discrete ones (including all PCI-only controllers)
+# - debug/dummy gadget+hcd is last.
+#
+menu "USB Peripheral Controller"
+
+#
+# Integrated controllers
+#
+
+config USB_AT91
+ tristate "Atmel AT91 USB Device Port"
+ depends on ARCH_AT91
+ help
+ Many Atmel AT91 processors (such as the AT91RM2000) have a
+ full speed USB Device Port with support for five configurable
+ endpoints (plus endpoint zero).
+
+ Say "y" to link the driver statically, or "m" to build a
+ dynamically linked module called "at91_udc" and force all
+ gadget drivers to also be dynamically linked.
+
+config USB_LPC32XX
+ tristate "LPC32XX USB Peripheral Controller"
+ depends on ARCH_LPC32XX && I2C
+ select USB_ISP1301
+ help
+ This option selects the USB device controller in the LPC32xx SoC.
+
+ Say "y" to link the driver statically, or "m" to build a
+ dynamically linked module called "lpc32xx_udc" and force all
+ gadget drivers to also be dynamically linked.
+
+config USB_ATMEL_USBA
+ tristate "Atmel USBA"
+ depends on AVR32 || ARCH_AT91
+ help
+ USBA is the integrated high-speed USB Device controller on
+ the AT32AP700x, some AT91SAM9 and AT91CAP9 processors from Atmel.
+
+config USB_BCM63XX_UDC
+ tristate "Broadcom BCM63xx Peripheral Controller"
+ depends on BCM63XX
+ help
+ Many Broadcom BCM63xx chipsets (such as the BCM6328) have a
+ high speed USB Device Port with support for four fixed endpoints
+ (plus endpoint zero).
+
+ Say "y" to link the driver statically, or "m" to build a
+ dynamically linked module called "bcm63xx_udc".
+
+config USB_FSL_USB2
+ tristate "Freescale Highspeed USB DR Peripheral Controller"
+ depends on FSL_SOC || ARCH_MXC
+ select USB_FSL_MPH_DR_OF if OF
+ help
+ Some of Freescale PowerPC and i.MX processors have a High Speed
+ Dual-Role(DR) USB controller, which supports device mode.
+
+ The number of programmable endpoints is different through
+ SOC revisions.
+
+ Say "y" to link the driver statically, or "m" to build a
+ dynamically linked module called "fsl_usb2_udc" and force
+ all gadget drivers to also be dynamically linked.
+
+config USB_FUSB300
+ tristate "Faraday FUSB300 USB Peripheral Controller"
+ depends on !PHYS_ADDR_T_64BIT && HAS_DMA
+ help
+ Faraday usb device controller FUSB300 driver
+
+config USB_FOTG210_UDC
+ depends on HAS_DMA
+ tristate "Faraday FOTG210 USB Peripheral Controller"
+ help
+ Faraday USB2.0 OTG controller which can be configured as
+ high speed or full speed USB device. This driver supppors
+ Bulk Transfer so far.
+
+ Say "y" to link the driver statically, or "m" to build a
+ dynamically linked module called "fotg210_udc".
+
+config USB_GR_UDC
+ tristate "Aeroflex Gaisler GRUSBDC USB Peripheral Controller Driver"
+ depends on HAS_DMA
+ help
+ Select this to support Aeroflex Gaisler GRUSBDC cores from the GRLIB
+ VHDL IP core library.
+
+config USB_OMAP
+ tristate "OMAP USB Device Controller"
+ depends on ARCH_OMAP1
+ depends on ISP1301_OMAP || !(MACH_OMAP_H2 || MACH_OMAP_H3)
+ help
+ Many Texas Instruments OMAP processors have flexible full
+ speed USB device controllers, with support for up to 30
+ endpoints (plus endpoint zero). This driver supports the
+ controller in the OMAP 1611, and should work with controllers
+ in other OMAP processors too, given minor tweaks.
+
+ Say "y" to link the driver statically, or "m" to build a
+ dynamically linked module called "omap_udc" and force all
+ gadget drivers to also be dynamically linked.
+
+config USB_PXA25X
+ tristate "PXA 25x or IXP 4xx"
+ depends on (ARCH_PXA && PXA25x) || ARCH_IXP4XX
+ help
+ Intel's PXA 25x series XScale ARM-5TE processors include
+ an integrated full speed USB 1.1 device controller. The
+ controller in the IXP 4xx series is register-compatible.
+
+ It has fifteen fixed-function endpoints, as well as endpoint
+ zero (for control transfers).
+
+ Say "y" to link the driver statically, or "m" to build a
+ dynamically linked module called "pxa25x_udc" and force all
+ gadget drivers to also be dynamically linked.
+
+# if there's only one gadget driver, using only two bulk endpoints,
+# don't waste memory for the other endpoints
+config USB_PXA25X_SMALL
+ depends on USB_PXA25X
+ bool
+ default n if USB_ETH_RNDIS
+ default y if USB_ZERO
+ default y if USB_ETH
+ default y if USB_G_SERIAL
+
+config USB_R8A66597
+ tristate "Renesas R8A66597 USB Peripheral Controller"
+ depends on HAS_DMA
+ help
+ R8A66597 is a discrete USB host and peripheral controller chip that
+ supports both full and high speed USB 2.0 data transfers.
+ It has nine configurable endpoints, and endpoint zero.
+
+ Say "y" to link the driver statically, or "m" to build a
+ dynamically linked module called "r8a66597_udc" and force all
+ gadget drivers to also be dynamically linked.
+
+config USB_RENESAS_USBHS_UDC
+ tristate 'Renesas USBHS controller'
+ depends on USB_RENESAS_USBHS
+ help
+ Renesas USBHS is a discrete USB host and peripheral controller chip
+ that supports both full and high speed USB 2.0 data transfers.
+ It has nine or more configurable endpoints, and endpoint zero.
+
+ Say "y" to link the driver statically, or "m" to build a
+ dynamically linked module called "renesas_usbhs" and force all
+ gadget drivers to also be dynamically linked.
+
+config USB_PXA27X
+ tristate "PXA 27x"
+ help
+ Intel's PXA 27x series XScale ARM v5TE processors include
+ an integrated full speed USB 1.1 device controller.
+
+ It has up to 23 endpoints, as well as endpoint zero (for
+ control transfers).
+
+ Say "y" to link the driver statically, or "m" to build a
+ dynamically linked module called "pxa27x_udc" and force all
+ gadget drivers to also be dynamically linked.
+
+config USB_S3C2410
+ tristate "S3C2410 USB Device Controller"
+ depends on ARCH_S3C24XX
+ help
+ Samsung's S3C2410 is an ARM-4 processor with an integrated
+ full speed USB 1.1 device controller. It has 4 configurable
+ endpoints, as well as endpoint zero (for control transfers).
+
+ This driver has been tested on the S3C2410, S3C2412, and
+ S3C2440 processors.
+
+config USB_S3C2410_DEBUG
+ boolean "S3C2410 udc debug messages"
+ depends on USB_S3C2410
+
+config USB_S3C_HSUDC
+ tristate "S3C2416, S3C2443 and S3C2450 USB Device Controller"
+ depends on ARCH_S3C24XX
+ help
+ Samsung's S3C2416, S3C2443 and S3C2450 is an ARM9 based SoC
+ integrated with dual speed USB 2.0 device controller. It has
+ 8 endpoints, as well as endpoint zero.
+
+ This driver has been tested on S3C2416 and S3C2450 processors.
+
+config USB_MV_UDC
+ tristate "Marvell USB2.0 Device Controller"
+ depends on HAS_DMA
+ help
+ Marvell Socs (including PXA and MMP series) include a high speed
+ USB2.0 OTG controller, which can be configured as high speed or
+ full speed USB peripheral.
+
+config USB_MV_U3D
+ depends on HAS_DMA
+ tristate "MARVELL PXA2128 USB 3.0 controller"
+ help
+ MARVELL PXA2128 Processor series include a super speed USB3.0 device
+ controller, which support super speed USB peripheral.
+
+#
+# Controllers available in both integrated and discrete versions
+#
+
+config USB_M66592
+ tristate "Renesas M66592 USB Peripheral Controller"
+ help
+ M66592 is a discrete USB peripheral controller chip that
+ supports both full and high speed USB 2.0 data transfers.
+ It has seven configurable endpoints, and endpoint zero.
+
+ Say "y" to link the driver statically, or "m" to build a
+ dynamically linked module called "m66592_udc" and force all
+ gadget drivers to also be dynamically linked.
+
+#
+# Controllers available only in discrete form (and all PCI controllers)
+#
+
+config USB_AMD5536UDC
+ tristate "AMD5536 UDC"
+ depends on PCI
+ help
+ The AMD5536 UDC is part of the AMD Geode CS5536, an x86 southbridge.
+ It is a USB Highspeed DMA capable USB device controller. Beside ep0
+ it provides 4 IN and 4 OUT endpoints (bulk or interrupt type).
+ The UDC port supports OTG operation, and may be used as a host port
+ if it's not being used to implement peripheral or OTG roles.
+
+ Say "y" to link the driver statically, or "m" to build a
+ dynamically linked module called "amd5536udc" and force all
+ gadget drivers to also be dynamically linked.
+
+config USB_FSL_QE
+ tristate "Freescale QE/CPM USB Device Controller"
+ depends on FSL_SOC && (QUICC_ENGINE || CPM)
+ help
+ Some of Freescale PowerPC processors have a Full Speed
+ QE/CPM2 USB controller, which support device mode with 4
+ programmable endpoints. This driver supports the
+ controller in the MPC8360 and MPC8272, and should work with
+ controllers having QE or CPM2, given minor tweaks.
+
+ Set CONFIG_USB_GADGET to "m" to build this driver as a
+ dynamically linked module called "fsl_qe_udc".
+
+config USB_NET2272
+ tristate "PLX NET2272"
+ help
+ PLX NET2272 is a USB peripheral controller which supports
+ both full and high speed USB 2.0 data transfers.
+
+ It has three configurable endpoints, as well as endpoint zero
+ (for control transfer).
+ Say "y" to link the driver statically, or "m" to build a
+ dynamically linked module called "net2272" and force all
+ gadget drivers to also be dynamically linked.
+
+config USB_NET2272_DMA
+ boolean "Support external DMA controller"
+ depends on USB_NET2272 && HAS_DMA
+ help
+ The NET2272 part can optionally support an external DMA
+ controller, but your board has to have support in the
+ driver itself.
+
+ If unsure, say "N" here. The driver works fine in PIO mode.
+
+config USB_NET2280
+ tristate "NetChip 228x / PLX USB338x"
+ depends on PCI
+ help
+ NetChip 2280 / 2282 is a PCI based USB peripheral controller which
+ supports both full and high speed USB 2.0 data transfers.
+
+ It has six configurable endpoints, as well as endpoint zero
+ (for control transfers) and several endpoints with dedicated
+ functions.
+
+ 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.
+
+ It has eight configurable endpoints, as well as endpoint zero
+ (for control transfers) and several endpoints with dedicated
+ functions.
+
+ Say "y" to link the driver statically, or "m" to build a
+ dynamically linked module called "net2280" and force all
+ gadget drivers to also be dynamically linked.
+
+config USB_GOKU
+ tristate "Toshiba TC86C001 'Goku-S'"
+ depends on PCI
+ help
+ The Toshiba TC86C001 is a PCI device which includes controllers
+ for full speed USB devices, IDE, I2C, SIO, plus a USB host (OHCI).
+
+ The device controller has three configurable (bulk or interrupt)
+ endpoints, plus endpoint zero (for control transfers).
+
+ Say "y" to link the driver statically, or "m" to build a
+ dynamically linked module called "goku_udc" and to force all
+ gadget drivers to also be dynamically linked.
+
+config USB_EG20T
+ tristate "Intel EG20T PCH/LAPIS Semiconductor IOH(ML7213/ML7831) UDC"
+ depends on PCI
+ help
+ This is a USB device driver for EG20T PCH.
+ EG20T PCH is the platform controller hub that is used in Intel's
+ general embedded platform. EG20T PCH has USB device interface.
+ Using this interface, it is able to access system devices connected
+ to USB device.
+ This driver enables USB device function.
+ USB device is a USB peripheral controller which
+ supports both full and high speed USB 2.0 data transfers.
+ This driver supports both control transfer and bulk transfer modes.
+ This driver dose not support interrupt transfer or isochronous
+ transfer modes.
+
+ This driver also can be used for LAPIS Semiconductor's ML7213 which is
+ for IVI(In-Vehicle Infotainment) use.
+ ML7831 is for general purpose use.
+ ML7213/ML7831 is companion chip for Intel Atom E6xx series.
+ ML7213/ML7831 is completely compatible for Intel EG20T PCH.
+
+#
+# LAST -- dummy/emulated controller
+#
+
+config USB_DUMMY_HCD
+ tristate "Dummy HCD (DEVELOPMENT)"
+ depends on USB=y || (USB=m && USB_GADGET=m)
+ help
+ This host controller driver emulates USB, looping all data transfer
+ requests back to a USB "gadget driver" in the same host. The host
+ side is the master; the gadget side is the slave. Gadget drivers
+ can be high, full, or low speed; and they have access to endpoints
+ like those from NET2280, PXA2xx, or SA1100 hardware.
+
+ This may help in some stages of creating a driver to embed in a
+ Linux device, since it lets you debug several parts of the gadget
+ driver without its hardware or drivers being involved.
+
+ Since such a gadget side driver needs to interoperate with a host
+ side Linux-USB device driver, this may help to debug both sides
+ of a USB protocol stack.
+
+ Say "y" to link the driver statically, or "m" to build a
+ dynamically linked module called "dummy_hcd" and force all
+ gadget drivers to also be dynamically linked.
+
+# NOTE: Please keep dummy_hcd LAST so that "real hardware" appears
+# first and will be selected by default.
+
+endmenu
diff --git a/drivers/usb/gadget/udc/Makefile b/drivers/usb/gadget/udc/Makefile
new file mode 100644
index 000000000000..4096122bb283
--- /dev/null
+++ b/drivers/usb/gadget/udc/Makefile
@@ -0,0 +1,31 @@
+#
+# USB peripheral controller drivers
+#
+obj-$(CONFIG_USB_GADGET) += udc-core.o
+obj-$(CONFIG_USB_DUMMY_HCD) += dummy_hcd.o
+obj-$(CONFIG_USB_NET2272) += net2272.o
+obj-$(CONFIG_USB_NET2280) += net2280.o
+obj-$(CONFIG_USB_AMD5536UDC) += amd5536udc.o
+obj-$(CONFIG_USB_PXA25X) += pxa25x_udc.o
+obj-$(CONFIG_USB_PXA27X) += pxa27x_udc.o
+obj-$(CONFIG_USB_GOKU) += goku_udc.o
+obj-$(CONFIG_USB_OMAP) += omap_udc.o
+obj-$(CONFIG_USB_S3C2410) += s3c2410_udc.o
+obj-$(CONFIG_USB_AT91) += at91_udc.o
+obj-$(CONFIG_USB_ATMEL_USBA) += atmel_usba_udc.o
+obj-$(CONFIG_USB_BCM63XX_UDC) += bcm63xx_udc.o
+obj-$(CONFIG_USB_FSL_USB2) += fsl_usb2_udc.o
+fsl_usb2_udc-y := fsl_udc_core.o
+fsl_usb2_udc-$(CONFIG_ARCH_MXC) += fsl_mxc_udc.o
+obj-$(CONFIG_USB_M66592) += m66592-udc.o
+obj-$(CONFIG_USB_R8A66597) += r8a66597-udc.o
+obj-$(CONFIG_USB_FSL_QE) += fsl_qe_udc.o
+obj-$(CONFIG_USB_S3C_HSUDC) += s3c-hsudc.o
+obj-$(CONFIG_USB_LPC32XX) += lpc32xx_udc.o
+obj-$(CONFIG_USB_EG20T) += pch_udc.o
+obj-$(CONFIG_USB_MV_UDC) += mv_udc.o
+mv_udc-y := mv_udc_core.o
+obj-$(CONFIG_USB_FUSB300) += fusb300_udc.o
+obj-$(CONFIG_USB_FOTG210_UDC) += fotg210-udc.o
+obj-$(CONFIG_USB_MV_U3D) += mv_u3d_core.o
+obj-$(CONFIG_USB_GR_UDC) += gr_udc.o
diff --git a/drivers/usb/gadget/amd5536udc.c b/drivers/usb/gadget/udc/amd5536udc.c
index 41b062eb4de0..41b062eb4de0 100644
--- a/drivers/usb/gadget/amd5536udc.c
+++ b/drivers/usb/gadget/udc/amd5536udc.c
diff --git a/drivers/usb/gadget/amd5536udc.h b/drivers/usb/gadget/udc/amd5536udc.h
index 6744d3b83109..6744d3b83109 100644
--- a/drivers/usb/gadget/amd5536udc.h
+++ b/drivers/usb/gadget/udc/amd5536udc.h
diff --git a/drivers/usb/gadget/at91_udc.c b/drivers/usb/gadget/udc/at91_udc.c
index cfd18bcca723..cfd18bcca723 100644
--- a/drivers/usb/gadget/at91_udc.c
+++ b/drivers/usb/gadget/udc/at91_udc.c
diff --git a/drivers/usb/gadget/at91_udc.h b/drivers/usb/gadget/udc/at91_udc.h
index 017524663381..017524663381 100644
--- a/drivers/usb/gadget/at91_udc.h
+++ b/drivers/usb/gadget/udc/at91_udc.h
diff --git a/drivers/usb/gadget/atmel_usba_udc.c b/drivers/usb/gadget/udc/atmel_usba_udc.c
index 76023ce449a3..906e65f0e4fa 100644
--- a/drivers/usb/gadget/atmel_usba_udc.c
+++ b/drivers/usb/gadget/udc/atmel_usba_udc.c
@@ -1979,7 +1979,7 @@ static struct usba_ep * usba_udc_pdata(struct platform_device *pdev,
return eps;
}
-static int __init usba_udc_probe(struct platform_device *pdev)
+static int usba_udc_probe(struct platform_device *pdev)
{
struct resource *regs, *fifo;
struct clk *pclk, *hclk;
diff --git a/drivers/usb/gadget/atmel_usba_udc.h b/drivers/usb/gadget/udc/atmel_usba_udc.h
index a70706e8cb02..a70706e8cb02 100644
--- a/drivers/usb/gadget/atmel_usba_udc.h
+++ b/drivers/usb/gadget/udc/atmel_usba_udc.h
diff --git a/drivers/usb/gadget/bcm63xx_udc.c b/drivers/usb/gadget/udc/bcm63xx_udc.c
index e969eb809a85..e969eb809a85 100644
--- a/drivers/usb/gadget/bcm63xx_udc.c
+++ b/drivers/usb/gadget/udc/bcm63xx_udc.c
diff --git a/drivers/usb/gadget/dummy_hcd.c b/drivers/usb/gadget/udc/dummy_hcd.c
index 2b54955d3166..2b54955d3166 100644
--- a/drivers/usb/gadget/dummy_hcd.c
+++ b/drivers/usb/gadget/udc/dummy_hcd.c
diff --git a/drivers/usb/gadget/fotg210-udc.c b/drivers/usb/gadget/udc/fotg210-udc.c
index e143d69f6017..e143d69f6017 100644
--- a/drivers/usb/gadget/fotg210-udc.c
+++ b/drivers/usb/gadget/udc/fotg210-udc.c
diff --git a/drivers/usb/gadget/fotg210.h b/drivers/usb/gadget/udc/fotg210.h
index bbf991bcbe7c..bbf991bcbe7c 100644
--- a/drivers/usb/gadget/fotg210.h
+++ b/drivers/usb/gadget/udc/fotg210.h
diff --git a/drivers/usb/gadget/fsl_mxc_udc.c b/drivers/usb/gadget/udc/fsl_mxc_udc.c
index 9b140fc4d3bc..f16e149c5b3e 100644
--- a/drivers/usb/gadget/fsl_mxc_udc.c
+++ b/drivers/usb/gadget/udc/fsl_mxc_udc.c
@@ -18,6 +18,8 @@
#include <linux/platform_device.h>
#include <linux/io.h>
+#include "fsl_usb2_udc.h"
+
static struct clk *mxc_ahb_clk;
static struct clk *mxc_per_clk;
static struct clk *mxc_ipg_clk;
diff --git a/drivers/usb/gadget/fsl_qe_udc.c b/drivers/usb/gadget/udc/fsl_qe_udc.c
index ad5483335167..732430804841 100644
--- a/drivers/usb/gadget/fsl_qe_udc.c
+++ b/drivers/usb/gadget/udc/fsl_qe_udc.c
@@ -2539,7 +2539,7 @@ static int qe_udc_probe(struct platform_device *ofdev)
goto err2;
/* create a buf for ZLP send, need to remain zeroed */
- udc->nullbuf = kzalloc(256, GFP_KERNEL);
+ udc->nullbuf = devm_kzalloc(&ofdev->dev, 256, GFP_KERNEL);
if (udc->nullbuf == NULL) {
dev_err(udc->dev, "cannot alloc nullbuf\n");
ret = -ENOMEM;
@@ -2547,10 +2547,10 @@ static int qe_udc_probe(struct platform_device *ofdev)
}
/* buffer for data of get_status request */
- udc->statusbuf = kzalloc(2, GFP_KERNEL);
+ udc->statusbuf = devm_kzalloc(&ofdev->dev, 2, GFP_KERNEL);
if (udc->statusbuf == NULL) {
ret = -ENOMEM;
- goto err4;
+ goto err3;
}
udc->nullp = virt_to_phys((void *)udc->nullbuf);
@@ -2581,13 +2581,13 @@ static int qe_udc_probe(struct platform_device *ofdev)
if (ret) {
dev_err(udc->dev, "cannot request irq %d err %d\n",
udc->usb_irq, ret);
- goto err5;
+ goto err4;
}
ret = usb_add_gadget_udc_release(&ofdev->dev, &udc->gadget,
qe_udc_release);
if (ret)
- goto err6;
+ goto err5;
platform_set_drvdata(ofdev, udc);
dev_info(udc->dev,
@@ -2595,9 +2595,9 @@ static int qe_udc_probe(struct platform_device *ofdev)
(udc->soc_type == PORT_QE) ? "QE" : "CPM");
return 0;
-err6:
- free_irq(udc->usb_irq, udc);
err5:
+ free_irq(udc->usb_irq, udc);
+err4:
irq_dispose_mapping(udc->usb_irq);
err_noirq:
if (udc->nullmap) {
@@ -2610,9 +2610,6 @@ err_noirq:
udc->nullp, 256,
DMA_TO_DEVICE);
}
- kfree(udc->statusbuf);
-err4:
- kfree(udc->nullbuf);
err3:
ep = &udc->eps[0];
cpm_muram_free(cpm_muram_offset(ep->rxbase));
@@ -2660,8 +2657,6 @@ static int qe_udc_remove(struct platform_device *ofdev)
udc->nullp, 256,
DMA_TO_DEVICE);
}
- kfree(udc->statusbuf);
- kfree(udc->nullbuf);
ep = &udc->eps[0];
cpm_muram_free(cpm_muram_offset(ep->rxbase));
diff --git a/drivers/usb/gadget/fsl_qe_udc.h b/drivers/usb/gadget/udc/fsl_qe_udc.h
index 7026919fc901..7026919fc901 100644
--- a/drivers/usb/gadget/fsl_qe_udc.h
+++ b/drivers/usb/gadget/udc/fsl_qe_udc.h
diff --git a/drivers/usb/gadget/fsl_udc_core.c b/drivers/usb/gadget/udc/fsl_udc_core.c
index 28e4fc957026..75b23ea077a7 100644
--- a/drivers/usb/gadget/fsl_udc_core.c
+++ b/drivers/usb/gadget/udc/fsl_udc_core.c
@@ -59,9 +59,9 @@
static const char driver_name[] = "fsl-usb2-udc";
static const char driver_desc[] = DRIVER_DESC;
-static struct usb_dr_device *dr_regs;
+static struct usb_dr_device __iomem *dr_regs;
-static struct usb_sys_interface *usb_sys_regs;
+static struct usb_sys_interface __iomem *usb_sys_regs;
/* it is initialized in probe() */
static struct fsl_udc *udc_controller = NULL;
@@ -159,6 +159,8 @@ static inline void fsl_set_accessors(struct fsl_usb2_platform_data *pdata) {}
* request is still in progress.
*--------------------------------------------------------------*/
static void done(struct fsl_ep *ep, struct fsl_req *req, int status)
+__releases(ep->udc->lock)
+__acquires(ep->udc->lock)
{
struct fsl_udc *udc = NULL;
unsigned char stopped = ep->stopped;
@@ -1392,6 +1394,8 @@ stall:
static void setup_received_irq(struct fsl_udc *udc,
struct usb_ctrlrequest *setup)
+__releases(udc->lock)
+__acquires(udc->lock)
{
u16 wValue = le16_to_cpu(setup->wValue);
u16 wIndex = le16_to_cpu(setup->wIndex);
@@ -1957,8 +1961,7 @@ static int fsl_udc_start(struct usb_gadget *g,
&udc_controller->gadget);
if (retval < 0) {
ERR("can't bind to transceiver\n");
- driver->unbind(&udc_controller->gadget);
- udc_controller->driver = 0;
+ udc_controller->driver = NULL;
return retval;
}
}
@@ -2246,7 +2249,7 @@ static void fsl_udc_release(struct device *dev)
* init resource for globle controller
* Return the udc handle on success or NULL on failure
------------------------------------------------------------------*/
-static int __init struct_udc_setup(struct fsl_udc *udc,
+static int struct_udc_setup(struct fsl_udc *udc,
struct platform_device *pdev)
{
struct fsl_usb2_platform_data *pdata;
@@ -2298,7 +2301,7 @@ static int __init struct_udc_setup(struct fsl_udc *udc,
* ep0out is not used so do nothing here
* ep0in should be taken care
*--------------------------------------------------------------*/
-static int __init struct_ep_setup(struct fsl_udc *udc, unsigned char index,
+static int struct_ep_setup(struct fsl_udc *udc, unsigned char index,
char *name, int link)
{
struct fsl_ep *ep = &udc->eps[index];
@@ -2331,7 +2334,7 @@ static int __init struct_ep_setup(struct fsl_udc *udc, unsigned char index,
* all intialization operations implemented here except enabling usb_intr reg
* board setup should have been done in the platform code
*/
-static int __init fsl_udc_probe(struct platform_device *pdev)
+static int fsl_udc_probe(struct platform_device *pdev)
{
struct fsl_usb2_platform_data *pdata;
struct resource *res;
@@ -2380,7 +2383,7 @@ static int __init fsl_udc_probe(struct platform_device *pdev)
goto err_release_mem_region;
}
- pdata->regs = (void *)dr_regs;
+ pdata->regs = (void __iomem *)dr_regs;
/*
* do platform specific init: check the clock, grab/config pins, etc.
diff --git a/drivers/usb/gadget/fsl_usb2_udc.h b/drivers/usb/gadget/udc/fsl_usb2_udc.h
index c6703bb07b23..84715625b2b3 100644
--- a/drivers/usb/gadget/fsl_usb2_udc.h
+++ b/drivers/usb/gadget/udc/fsl_usb2_udc.h
@@ -12,6 +12,9 @@
#ifndef __FSL_USB2_UDC_H
#define __FSL_USB2_UDC_H
+#include <linux/usb/ch9.h>
+#include <linux/usb/gadget.h>
+
/* ### define USB registers here
*/
#define USB_MAX_CTRL_PAYLOAD 64
diff --git a/drivers/usb/gadget/fusb300_udc.c b/drivers/usb/gadget/udc/fusb300_udc.c
index 3deb4e938071..d40255f784df 100644
--- a/drivers/usb/gadget/fusb300_udc.c
+++ b/drivers/usb/gadget/udc/fusb300_udc.c
@@ -1325,8 +1325,6 @@ static int fusb300_udc_stop(struct usb_gadget *g,
{
struct fusb300 *fusb300 = to_fusb300(g);
- driver->unbind(&fusb300->gadget);
-
init_controller(fusb300);
fusb300->driver = NULL;
@@ -1359,7 +1357,7 @@ static int __exit fusb300_remove(struct platform_device *pdev)
return 0;
}
-static int __init fusb300_probe(struct platform_device *pdev)
+static int fusb300_probe(struct platform_device *pdev)
{
struct resource *res, *ires, *ires1;
void __iomem *reg = NULL;
diff --git a/drivers/usb/gadget/fusb300_udc.h b/drivers/usb/gadget/udc/fusb300_udc.h
index ae811d8d38b4..ae811d8d38b4 100644
--- a/drivers/usb/gadget/fusb300_udc.h
+++ b/drivers/usb/gadget/udc/fusb300_udc.h
diff --git a/drivers/usb/gadget/gadget_chips.h b/drivers/usb/gadget/udc/gadget_chips.h
index bcd04bc66b98..bcd04bc66b98 100644
--- a/drivers/usb/gadget/gadget_chips.h
+++ b/drivers/usb/gadget/udc/gadget_chips.h
diff --git a/drivers/usb/gadget/goku_udc.c b/drivers/usb/gadget/udc/goku_udc.c
index 6c85839e15ad..6c85839e15ad 100644
--- a/drivers/usb/gadget/goku_udc.c
+++ b/drivers/usb/gadget/udc/goku_udc.c
diff --git a/drivers/usb/gadget/goku_udc.h b/drivers/usb/gadget/udc/goku_udc.h
index 86d2adafe149..86d2adafe149 100644
--- a/drivers/usb/gadget/goku_udc.h
+++ b/drivers/usb/gadget/udc/goku_udc.h
diff --git a/drivers/usb/gadget/gr_udc.c b/drivers/usb/gadget/udc/gr_udc.c
index c7004ee89c90..08df5c4f46ce 100644
--- a/drivers/usb/gadget/gr_udc.c
+++ b/drivers/usb/gadget/udc/gr_udc.c
@@ -2213,7 +2213,7 @@ out:
return retval;
}
-static struct of_device_id gr_match[] = {
+static const struct of_device_id gr_match[] = {
{.name = "GAISLER_USBDC"},
{.name = "01_021"},
{},
diff --git a/drivers/usb/gadget/gr_udc.h b/drivers/usb/gadget/udc/gr_udc.h
index 8388897d9ec3..8388897d9ec3 100644
--- a/drivers/usb/gadget/gr_udc.h
+++ b/drivers/usb/gadget/udc/gr_udc.h
diff --git a/drivers/usb/gadget/lpc32xx_udc.c b/drivers/usb/gadget/udc/lpc32xx_udc.c
index e471580a2a3b..1629ad7dcb80 100644
--- a/drivers/usb/gadget/lpc32xx_udc.c
+++ b/drivers/usb/gadget/udc/lpc32xx_udc.c
@@ -3036,7 +3036,7 @@ struct lpc32xx_usbd_cfg lpc32xx_usbddata = {
static u64 lpc32xx_usbd_dmamask = ~(u32) 0x7F;
-static int __init lpc32xx_udc_probe(struct platform_device *pdev)
+static int lpc32xx_udc_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct lpc32xx_udc *udc;
@@ -3045,11 +3045,10 @@ static int __init lpc32xx_udc_probe(struct platform_device *pdev)
dma_addr_t dma_handle;
struct device_node *isp1301_node;
- udc = kzalloc(sizeof(*udc), GFP_KERNEL);
+ udc = kmemdup(&controller_template, sizeof(*udc), GFP_KERNEL);
if (!udc)
return -ENOMEM;
- memcpy(udc, &controller_template, sizeof(*udc));
for (i = 0; i <= 15; i++)
udc->ep[i].udc = udc;
udc->gadget.ep0 = &udc->ep[0].ep;
@@ -3397,7 +3396,7 @@ static int lpc32xx_udc_resume(struct platform_device *pdev)
#endif
#ifdef CONFIG_OF
-static struct of_device_id lpc32xx_udc_of_match[] = {
+static const struct of_device_id lpc32xx_udc_of_match[] = {
{ .compatible = "nxp,lpc3220-udc", },
{ },
};
diff --git a/drivers/usb/gadget/m66592-udc.c b/drivers/usb/gadget/udc/m66592-udc.c
index 0d17174b86f8..de88d33b44b2 100644
--- a/drivers/usb/gadget/m66592-udc.c
+++ b/drivers/usb/gadget/udc/m66592-udc.c
@@ -1492,8 +1492,6 @@ static int m66592_udc_stop(struct usb_gadget *g,
m66592_bclr(m66592, M66592_VBSE | M66592_URST, M66592_INTENB0);
- driver->unbind(&m66592->gadget);
-
init_controller(m66592);
disable_controller(m66592);
@@ -1553,7 +1551,7 @@ static void nop_completion(struct usb_ep *ep, struct usb_request *r)
{
}
-static int __init m66592_probe(struct platform_device *pdev)
+static int m66592_probe(struct platform_device *pdev)
{
struct resource *res, *ires;
void __iomem *reg = NULL;
diff --git a/drivers/usb/gadget/m66592-udc.h b/drivers/usb/gadget/udc/m66592-udc.h
index 96d49d7bfb6b..96d49d7bfb6b 100644
--- a/drivers/usb/gadget/m66592-udc.h
+++ b/drivers/usb/gadget/udc/m66592-udc.h
diff --git a/drivers/usb/gadget/mv_u3d.h b/drivers/usb/gadget/udc/mv_u3d.h
index e32a787ac373..e32a787ac373 100644
--- a/drivers/usb/gadget/mv_u3d.h
+++ b/drivers/usb/gadget/udc/mv_u3d.h
diff --git a/drivers/usb/gadget/mv_u3d_core.c b/drivers/usb/gadget/udc/mv_u3d_core.c
index 16248711c152..16248711c152 100644
--- a/drivers/usb/gadget/mv_u3d_core.c
+++ b/drivers/usb/gadget/udc/mv_u3d_core.c
diff --git a/drivers/usb/gadget/mv_udc.h b/drivers/usb/gadget/udc/mv_udc.h
index be77f207dbaf..be77f207dbaf 100644
--- a/drivers/usb/gadget/mv_udc.h
+++ b/drivers/usb/gadget/udc/mv_udc.h
diff --git a/drivers/usb/gadget/mv_udc_core.c b/drivers/usb/gadget/udc/mv_udc_core.c
index 040fb169b162..040fb169b162 100644
--- a/drivers/usb/gadget/mv_udc_core.c
+++ b/drivers/usb/gadget/udc/mv_udc_core.c
diff --git a/drivers/usb/gadget/net2272.c b/drivers/usb/gadget/udc/net2272.c
index ca15405583e2..059cfe527982 100644
--- a/drivers/usb/gadget/net2272.c
+++ b/drivers/usb/gadget/udc/net2272.c
@@ -1453,7 +1453,7 @@ static int net2272_start(struct usb_gadget *_gadget,
struct net2272 *dev;
unsigned i;
- if (!driver || !driver->unbind || !driver->setup ||
+ if (!driver || !driver->setup ||
driver->max_speed != USB_SPEED_HIGH)
return -EINVAL;
diff --git a/drivers/usb/gadget/net2272.h b/drivers/usb/gadget/udc/net2272.h
index e59505789359..e59505789359 100644
--- a/drivers/usb/gadget/net2272.h
+++ b/drivers/usb/gadget/udc/net2272.h
diff --git a/drivers/usb/gadget/udc/net2280.c b/drivers/usb/gadget/udc/net2280.c
new file mode 100644
index 000000000000..f4eac113690e
--- /dev/null
+++ b/drivers/usb/gadget/udc/net2280.c
@@ -0,0 +1,3827 @@
+/*
+ * Driver for the PLX NET2280 USB device controller.
+ * Specs and errata are available from <http://www.plxtech.com>.
+ *
+ * PLX Technology Inc. (formerly NetChip Technology) supported the
+ * development of this driver.
+ *
+ *
+ * CODE STATUS HIGHLIGHTS
+ *
+ * This driver should work well with most "gadget" drivers, including
+ * the Mass Storage, Serial, and Ethernet/RNDIS gadget drivers
+ * as well as Gadget Zero and Gadgetfs.
+ *
+ * DMA is enabled by default. Drivers using transfer queues might use
+ * DMA chaining to remove IRQ latencies between transfers. (Except when
+ * short OUT transfers happen.) Drivers can use the req->no_interrupt
+ * hint to completely eliminate some IRQs, if a later IRQ is guaranteed
+ * and DMA chaining is enabled.
+ *
+ * MSI is enabled by default. The legacy IRQ is used if MSI couldn't
+ * be enabled.
+ *
+ * Note that almost all the errata workarounds here are only needed for
+ * rev1 chips. Rev1a silicon (0110) fixes almost all of them.
+ */
+
+/*
+ * Copyright (C) 2003 David Brownell
+ * Copyright (C) 2003-2005 PLX Technology, Inc.
+ * Copyright (C) 2014 Ricardo Ribalda - Qtechnology/AS
+ *
+ * Modified Seth Levy 2005 PLX Technology, Inc. to provide compatibility
+ * with 2282 chip
+ *
+ * Modified Ricardo Ribalda Qtechnology AS to provide compatibility
+ * with usb 338x chip. Based on PLX driver
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/dma-mapping.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/ioport.h>
+#include <linux/slab.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/timer.h>
+#include <linux/list.h>
+#include <linux/interrupt.h>
+#include <linux/moduleparam.h>
+#include <linux/device.h>
+#include <linux/usb/ch9.h>
+#include <linux/usb/gadget.h>
+#include <linux/prefetch.h>
+#include <linux/io.h>
+
+#include <asm/byteorder.h>
+#include <asm/irq.h>
+#include <asm/unaligned.h>
+
+#define DRIVER_DESC "PLX NET228x/USB338x USB Peripheral Controller"
+#define DRIVER_VERSION "2005 Sept 27/v3.0"
+
+#define EP_DONTUSE 13 /* nonzero */
+
+#define USE_RDK_LEDS /* GPIO pins control three LEDs */
+
+
+static const char driver_name[] = "net2280";
+static const char driver_desc[] = DRIVER_DESC;
+
+static const u32 ep_bit[9] = { 0, 17, 2, 19, 4, 1, 18, 3, 20 };
+static const char ep0name[] = "ep0";
+static const char *const ep_name[] = {
+ ep0name,
+ "ep-a", "ep-b", "ep-c", "ep-d",
+ "ep-e", "ep-f", "ep-g", "ep-h",
+};
+
+/* use_dma -- general goodness, fewer interrupts, less cpu load (vs PIO)
+ * use_dma_chaining -- dma descriptor queueing gives even more irq reduction
+ *
+ * The net2280 DMA engines are not tightly integrated with their FIFOs;
+ * not all cases are (yet) handled well in this driver or the silicon.
+ * Some gadget drivers work better with the dma support here than others.
+ * These two parameters let you use PIO or more aggressive DMA.
+ */
+static bool use_dma = true;
+static bool use_dma_chaining;
+static bool use_msi = true;
+
+/* "modprobe net2280 use_dma=n" etc */
+module_param(use_dma, bool, 0444);
+module_param(use_dma_chaining, bool, 0444);
+module_param(use_msi, bool, 0444);
+
+/* mode 0 == ep-{a,b,c,d} 1K fifo each
+ * mode 1 == ep-{a,b} 2K fifo each, ep-{c,d} unavailable
+ * mode 2 == ep-a 2K fifo, ep-{b,c} 1K each, ep-d unavailable
+ */
+static ushort fifo_mode;
+
+/* "modprobe net2280 fifo_mode=1" etc */
+module_param(fifo_mode, ushort, 0644);
+
+/* enable_suspend -- When enabled, the driver will respond to
+ * USB suspend requests by powering down the NET2280. Otherwise,
+ * USB suspend requests will be ignored. This is acceptable for
+ * self-powered devices
+ */
+static bool enable_suspend;
+
+/* "modprobe net2280 enable_suspend=1" etc */
+module_param(enable_suspend, bool, 0444);
+
+/* force full-speed operation */
+static bool full_speed;
+module_param(full_speed, bool, 0444);
+MODULE_PARM_DESC(full_speed, "force full-speed mode -- for testing only!");
+
+#define DIR_STRING(bAddress) (((bAddress) & USB_DIR_IN) ? "in" : "out")
+
+static char *type_string(u8 bmAttributes)
+{
+ switch ((bmAttributes) & USB_ENDPOINT_XFERTYPE_MASK) {
+ case USB_ENDPOINT_XFER_BULK: return "bulk";
+ case USB_ENDPOINT_XFER_ISOC: return "iso";
+ case USB_ENDPOINT_XFER_INT: return "intr";
+ }
+ return "control";
+}
+
+#include "net2280.h"
+
+#define valid_bit cpu_to_le32(BIT(VALID_BIT))
+#define dma_done_ie cpu_to_le32(BIT(DMA_DONE_INTERRUPT_ENABLE))
+
+/*-------------------------------------------------------------------------*/
+static inline void enable_pciirqenb(struct net2280_ep *ep)
+{
+ u32 tmp = readl(&ep->dev->regs->pciirqenb0);
+
+ if (ep->dev->quirks & PLX_LEGACY)
+ tmp |= BIT(ep->num);
+ else
+ tmp |= BIT(ep_bit[ep->num]);
+ writel(tmp, &ep->dev->regs->pciirqenb0);
+
+ return;
+}
+
+static int
+net2280_enable(struct usb_ep *_ep, const struct usb_endpoint_descriptor *desc)
+{
+ struct net2280 *dev;
+ struct net2280_ep *ep;
+ u32 max, tmp;
+ unsigned long flags;
+ static const u32 ep_key[9] = { 1, 0, 1, 0, 1, 1, 0, 1, 0 };
+
+ ep = container_of(_ep, struct net2280_ep, ep);
+ if (!_ep || !desc || ep->desc || _ep->name == ep0name ||
+ desc->bDescriptorType != USB_DT_ENDPOINT)
+ return -EINVAL;
+ dev = ep->dev;
+ if (!dev->driver || dev->gadget.speed == USB_SPEED_UNKNOWN)
+ return -ESHUTDOWN;
+
+ /* erratum 0119 workaround ties up an endpoint number */
+ if ((desc->bEndpointAddress & 0x0f) == EP_DONTUSE)
+ return -EDOM;
+
+ if (dev->quirks & PLX_SUPERSPEED) {
+ if ((desc->bEndpointAddress & 0x0f) >= 0x0c)
+ return -EDOM;
+ ep->is_in = !!usb_endpoint_dir_in(desc);
+ if (dev->enhanced_mode && ep->is_in && ep_key[ep->num])
+ return -EINVAL;
+ }
+
+ /* sanity check ep-e/ep-f since their fifos are small */
+ max = usb_endpoint_maxp(desc) & 0x1fff;
+ if (ep->num > 4 && max > 64 && (dev->quirks & PLX_LEGACY))
+ return -ERANGE;
+
+ spin_lock_irqsave(&dev->lock, flags);
+ _ep->maxpacket = max & 0x7ff;
+ ep->desc = desc;
+
+ /* ep_reset() has already been called */
+ ep->stopped = 0;
+ ep->wedged = 0;
+ ep->out_overflow = 0;
+
+ /* set speed-dependent max packet; may kick in high bandwidth */
+ set_max_speed(ep, max);
+
+ /* FIFO lines can't go to different packets. PIO is ok, so
+ * use it instead of troublesome (non-bulk) multi-packet DMA.
+ */
+ if (ep->dma && (max % 4) != 0 && use_dma_chaining) {
+ ep_dbg(ep->dev, "%s, no dma for maxpacket %d\n",
+ ep->ep.name, ep->ep.maxpacket);
+ ep->dma = NULL;
+ }
+
+ /* set type, direction, address; reset fifo counters */
+ writel(BIT(FIFO_FLUSH), &ep->regs->ep_stat);
+ tmp = (desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK);
+ if (tmp == USB_ENDPOINT_XFER_INT) {
+ /* erratum 0105 workaround prevents hs NYET */
+ if (dev->chiprev == 0100 &&
+ dev->gadget.speed == USB_SPEED_HIGH &&
+ !(desc->bEndpointAddress & USB_DIR_IN))
+ writel(BIT(CLEAR_NAK_OUT_PACKETS_MODE),
+ &ep->regs->ep_rsp);
+ } else if (tmp == USB_ENDPOINT_XFER_BULK) {
+ /* catch some particularly blatant driver bugs */
+ if ((dev->gadget.speed == USB_SPEED_SUPER && max != 1024) ||
+ (dev->gadget.speed == USB_SPEED_HIGH && max != 512) ||
+ (dev->gadget.speed == USB_SPEED_FULL && max > 64)) {
+ spin_unlock_irqrestore(&dev->lock, flags);
+ return -ERANGE;
+ }
+ }
+ ep->is_iso = (tmp == USB_ENDPOINT_XFER_ISOC);
+ /* Enable this endpoint */
+ if (dev->quirks & PLX_LEGACY) {
+ tmp <<= ENDPOINT_TYPE;
+ tmp |= desc->bEndpointAddress;
+ /* default full fifo lines */
+ tmp |= (4 << ENDPOINT_BYTE_COUNT);
+ tmp |= BIT(ENDPOINT_ENABLE);
+ ep->is_in = (tmp & USB_DIR_IN) != 0;
+ } else {
+ /* In Legacy mode, only OUT endpoints are used */
+ if (dev->enhanced_mode && ep->is_in) {
+ tmp <<= IN_ENDPOINT_TYPE;
+ tmp |= BIT(IN_ENDPOINT_ENABLE);
+ /* Not applicable to Legacy */
+ tmp |= BIT(ENDPOINT_DIRECTION);
+ } else {
+ tmp <<= OUT_ENDPOINT_TYPE;
+ tmp |= BIT(OUT_ENDPOINT_ENABLE);
+ tmp |= (ep->is_in << ENDPOINT_DIRECTION);
+ }
+
+ tmp |= usb_endpoint_num(desc);
+ tmp |= (ep->ep.maxburst << MAX_BURST_SIZE);
+ }
+
+ /* Make sure all the registers are written before ep_rsp*/
+ wmb();
+
+ /* for OUT transfers, block the rx fifo until a read is posted */
+ if (!ep->is_in)
+ writel(BIT(SET_NAK_OUT_PACKETS), &ep->regs->ep_rsp);
+ else if (!(dev->quirks & PLX_2280)) {
+ /* Added for 2282, Don't use nak packets on an in endpoint,
+ * this was ignored on 2280
+ */
+ writel(BIT(CLEAR_NAK_OUT_PACKETS) |
+ BIT(CLEAR_NAK_OUT_PACKETS_MODE), &ep->regs->ep_rsp);
+ }
+
+ writel(tmp, &ep->cfg->ep_cfg);
+
+ /* enable irqs */
+ if (!ep->dma) { /* pio, per-packet */
+ enable_pciirqenb(ep);
+
+ tmp = BIT(DATA_PACKET_RECEIVED_INTERRUPT_ENABLE) |
+ BIT(DATA_PACKET_TRANSMITTED_INTERRUPT_ENABLE);
+ if (dev->quirks & PLX_2280)
+ tmp |= readl(&ep->regs->ep_irqenb);
+ writel(tmp, &ep->regs->ep_irqenb);
+ } else { /* dma, per-request */
+ tmp = BIT((8 + ep->num)); /* completion */
+ tmp |= readl(&dev->regs->pciirqenb1);
+ writel(tmp, &dev->regs->pciirqenb1);
+
+ /* for short OUT transfers, dma completions can't
+ * advance the queue; do it pio-style, by hand.
+ * NOTE erratum 0112 workaround #2
+ */
+ if ((desc->bEndpointAddress & USB_DIR_IN) == 0) {
+ tmp = BIT(SHORT_PACKET_TRANSFERRED_INTERRUPT_ENABLE);
+ writel(tmp, &ep->regs->ep_irqenb);
+
+ enable_pciirqenb(ep);
+ }
+ }
+
+ tmp = desc->bEndpointAddress;
+ ep_dbg(dev, "enabled %s (ep%d%s-%s) %s max %04x\n",
+ _ep->name, tmp & 0x0f, DIR_STRING(tmp),
+ type_string(desc->bmAttributes),
+ ep->dma ? "dma" : "pio", max);
+
+ /* pci writes may still be posted */
+ spin_unlock_irqrestore(&dev->lock, flags);
+ return 0;
+}
+
+static int handshake(u32 __iomem *ptr, u32 mask, u32 done, int usec)
+{
+ u32 result;
+
+ do {
+ result = readl(ptr);
+ if (result == ~(u32)0) /* "device unplugged" */
+ return -ENODEV;
+ result &= mask;
+ if (result == done)
+ return 0;
+ udelay(1);
+ usec--;
+ } while (usec > 0);
+ return -ETIMEDOUT;
+}
+
+static const struct usb_ep_ops net2280_ep_ops;
+
+static void ep_reset_228x(struct net2280_regs __iomem *regs,
+ struct net2280_ep *ep)
+{
+ u32 tmp;
+
+ ep->desc = NULL;
+ INIT_LIST_HEAD(&ep->queue);
+
+ usb_ep_set_maxpacket_limit(&ep->ep, ~0);
+ ep->ep.ops = &net2280_ep_ops;
+
+ /* disable the dma, irqs, endpoint... */
+ if (ep->dma) {
+ writel(0, &ep->dma->dmactl);
+ writel(BIT(DMA_SCATTER_GATHER_DONE_INTERRUPT) |
+ BIT(DMA_TRANSACTION_DONE_INTERRUPT) |
+ BIT(DMA_ABORT),
+ &ep->dma->dmastat);
+
+ tmp = readl(&regs->pciirqenb0);
+ tmp &= ~BIT(ep->num);
+ writel(tmp, &regs->pciirqenb0);
+ } else {
+ tmp = readl(&regs->pciirqenb1);
+ tmp &= ~BIT((8 + ep->num)); /* completion */
+ writel(tmp, &regs->pciirqenb1);
+ }
+ writel(0, &ep->regs->ep_irqenb);
+
+ /* init to our chosen defaults, notably so that we NAK OUT
+ * packets until the driver queues a read (+note erratum 0112)
+ */
+ if (!ep->is_in || (ep->dev->quirks & PLX_2280)) {
+ tmp = BIT(SET_NAK_OUT_PACKETS_MODE) |
+ BIT(SET_NAK_OUT_PACKETS) |
+ BIT(CLEAR_EP_HIDE_STATUS_PHASE) |
+ BIT(CLEAR_INTERRUPT_MODE);
+ } else {
+ /* added for 2282 */
+ tmp = BIT(CLEAR_NAK_OUT_PACKETS_MODE) |
+ BIT(CLEAR_NAK_OUT_PACKETS) |
+ BIT(CLEAR_EP_HIDE_STATUS_PHASE) |
+ BIT(CLEAR_INTERRUPT_MODE);
+ }
+
+ if (ep->num != 0) {
+ tmp |= BIT(CLEAR_ENDPOINT_TOGGLE) |
+ BIT(CLEAR_ENDPOINT_HALT);
+ }
+ writel(tmp, &ep->regs->ep_rsp);
+
+ /* scrub most status bits, and flush any fifo state */
+ if (ep->dev->quirks & PLX_2280)
+ tmp = BIT(FIFO_OVERFLOW) |
+ BIT(FIFO_UNDERFLOW);
+ else
+ tmp = 0;
+
+ writel(tmp | BIT(TIMEOUT) |
+ BIT(USB_STALL_SENT) |
+ BIT(USB_IN_NAK_SENT) |
+ BIT(USB_IN_ACK_RCVD) |
+ BIT(USB_OUT_PING_NAK_SENT) |
+ BIT(USB_OUT_ACK_SENT) |
+ BIT(FIFO_FLUSH) |
+ BIT(SHORT_PACKET_OUT_DONE_INTERRUPT) |
+ BIT(SHORT_PACKET_TRANSFERRED_INTERRUPT) |
+ BIT(DATA_PACKET_RECEIVED_INTERRUPT) |
+ BIT(DATA_PACKET_TRANSMITTED_INTERRUPT) |
+ BIT(DATA_OUT_PING_TOKEN_INTERRUPT) |
+ BIT(DATA_IN_TOKEN_INTERRUPT),
+ &ep->regs->ep_stat);
+
+ /* fifo size is handled separately */
+}
+
+static void ep_reset_338x(struct net2280_regs __iomem *regs,
+ struct net2280_ep *ep)
+{
+ u32 tmp, dmastat;
+
+ ep->desc = NULL;
+ INIT_LIST_HEAD(&ep->queue);
+
+ usb_ep_set_maxpacket_limit(&ep->ep, ~0);
+ ep->ep.ops = &net2280_ep_ops;
+
+ /* disable the dma, irqs, endpoint... */
+ if (ep->dma) {
+ writel(0, &ep->dma->dmactl);
+ writel(BIT(DMA_ABORT_DONE_INTERRUPT) |
+ BIT(DMA_PAUSE_DONE_INTERRUPT) |
+ BIT(DMA_SCATTER_GATHER_DONE_INTERRUPT) |
+ BIT(DMA_TRANSACTION_DONE_INTERRUPT),
+ /* | BIT(DMA_ABORT), */
+ &ep->dma->dmastat);
+
+ dmastat = readl(&ep->dma->dmastat);
+ if (dmastat == 0x5002) {
+ ep_warn(ep->dev, "The dmastat return = %x!!\n",
+ dmastat);
+ writel(0x5a, &ep->dma->dmastat);
+ }
+
+ tmp = readl(&regs->pciirqenb0);
+ tmp &= ~BIT(ep_bit[ep->num]);
+ writel(tmp, &regs->pciirqenb0);
+ } else {
+ if (ep->num < 5) {
+ tmp = readl(&regs->pciirqenb1);
+ tmp &= ~BIT((8 + ep->num)); /* completion */
+ writel(tmp, &regs->pciirqenb1);
+ }
+ }
+ writel(0, &ep->regs->ep_irqenb);
+
+ writel(BIT(SHORT_PACKET_OUT_DONE_INTERRUPT) |
+ BIT(SHORT_PACKET_TRANSFERRED_INTERRUPT) |
+ BIT(FIFO_OVERFLOW) |
+ BIT(DATA_PACKET_RECEIVED_INTERRUPT) |
+ BIT(DATA_PACKET_TRANSMITTED_INTERRUPT) |
+ BIT(DATA_OUT_PING_TOKEN_INTERRUPT) |
+ BIT(DATA_IN_TOKEN_INTERRUPT), &ep->regs->ep_stat);
+}
+
+static void nuke(struct net2280_ep *);
+
+static int net2280_disable(struct usb_ep *_ep)
+{
+ struct net2280_ep *ep;
+ unsigned long flags;
+
+ ep = container_of(_ep, struct net2280_ep, ep);
+ if (!_ep || !ep->desc || _ep->name == ep0name)
+ return -EINVAL;
+
+ spin_lock_irqsave(&ep->dev->lock, flags);
+ nuke(ep);
+
+ if (ep->dev->quirks & PLX_SUPERSPEED)
+ ep_reset_338x(ep->dev->regs, ep);
+ else
+ ep_reset_228x(ep->dev->regs, ep);
+
+ ep_vdbg(ep->dev, "disabled %s %s\n",
+ ep->dma ? "dma" : "pio", _ep->name);
+
+ /* synch memory views with the device */
+ (void)readl(&ep->cfg->ep_cfg);
+
+ if (use_dma && !ep->dma && ep->num >= 1 && ep->num <= 4)
+ ep->dma = &ep->dev->dma[ep->num - 1];
+
+ spin_unlock_irqrestore(&ep->dev->lock, flags);
+ return 0;
+}
+
+/*-------------------------------------------------------------------------*/
+
+static struct usb_request
+*net2280_alloc_request(struct usb_ep *_ep, gfp_t gfp_flags)
+{
+ struct net2280_ep *ep;
+ struct net2280_request *req;
+
+ if (!_ep)
+ return NULL;
+ ep = container_of(_ep, struct net2280_ep, ep);
+
+ req = kzalloc(sizeof(*req), gfp_flags);
+ if (!req)
+ return NULL;
+
+ INIT_LIST_HEAD(&req->queue);
+
+ /* this dma descriptor may be swapped with the previous dummy */
+ if (ep->dma) {
+ struct net2280_dma *td;
+
+ td = pci_pool_alloc(ep->dev->requests, gfp_flags,
+ &req->td_dma);
+ if (!td) {
+ kfree(req);
+ return NULL;
+ }
+ td->dmacount = 0; /* not VALID */
+ td->dmadesc = td->dmaaddr;
+ req->td = td;
+ }
+ return &req->req;
+}
+
+static void net2280_free_request(struct usb_ep *_ep, struct usb_request *_req)
+{
+ struct net2280_ep *ep;
+ struct net2280_request *req;
+
+ ep = container_of(_ep, struct net2280_ep, ep);
+ if (!_ep || !_req)
+ return;
+
+ req = container_of(_req, struct net2280_request, req);
+ WARN_ON(!list_empty(&req->queue));
+ if (req->td)
+ pci_pool_free(ep->dev->requests, req->td, req->td_dma);
+ kfree(req);
+}
+
+/*-------------------------------------------------------------------------*/
+
+/* load a packet into the fifo we use for usb IN transfers.
+ * works for all endpoints.
+ *
+ * NOTE: pio with ep-a..ep-d could stuff multiple packets into the fifo
+ * at a time, but this code is simpler because it knows it only writes
+ * one packet. ep-a..ep-d should use dma instead.
+ */
+static void write_fifo(struct net2280_ep *ep, struct usb_request *req)
+{
+ struct net2280_ep_regs __iomem *regs = ep->regs;
+ u8 *buf;
+ u32 tmp;
+ unsigned count, total;
+
+ /* INVARIANT: fifo is currently empty. (testable) */
+
+ if (req) {
+ buf = req->buf + req->actual;
+ prefetch(buf);
+ total = req->length - req->actual;
+ } else {
+ total = 0;
+ buf = NULL;
+ }
+
+ /* write just one packet at a time */
+ count = ep->ep.maxpacket;
+ if (count > total) /* min() cannot be used on a bitfield */
+ count = total;
+
+ ep_vdbg(ep->dev, "write %s fifo (IN) %d bytes%s req %p\n",
+ ep->ep.name, count,
+ (count != ep->ep.maxpacket) ? " (short)" : "",
+ req);
+ while (count >= 4) {
+ /* NOTE be careful if you try to align these. fifo lines
+ * should normally be full (4 bytes) and successive partial
+ * lines are ok only in certain cases.
+ */
+ tmp = get_unaligned((u32 *)buf);
+ cpu_to_le32s(&tmp);
+ writel(tmp, &regs->ep_data);
+ buf += 4;
+ count -= 4;
+ }
+
+ /* last fifo entry is "short" unless we wrote a full packet.
+ * also explicitly validate last word in (periodic) transfers
+ * when maxpacket is not a multiple of 4 bytes.
+ */
+ if (count || total < ep->ep.maxpacket) {
+ tmp = count ? get_unaligned((u32 *)buf) : count;
+ cpu_to_le32s(&tmp);
+ set_fifo_bytecount(ep, count & 0x03);
+ writel(tmp, &regs->ep_data);
+ }
+
+ /* pci writes may still be posted */
+}
+
+/* work around erratum 0106: PCI and USB race over the OUT fifo.
+ * caller guarantees chiprev 0100, out endpoint is NAKing, and
+ * there's no real data in the fifo.
+ *
+ * NOTE: also used in cases where that erratum doesn't apply:
+ * where the host wrote "too much" data to us.
+ */
+static void out_flush(struct net2280_ep *ep)
+{
+ u32 __iomem *statp;
+ u32 tmp;
+
+ ASSERT_OUT_NAKING(ep);
+
+ statp = &ep->regs->ep_stat;
+ writel(BIT(DATA_OUT_PING_TOKEN_INTERRUPT) |
+ BIT(DATA_PACKET_RECEIVED_INTERRUPT),
+ statp);
+ writel(BIT(FIFO_FLUSH), statp);
+ /* Make sure that stap is written */
+ mb();
+ tmp = readl(statp);
+ if (tmp & BIT(DATA_OUT_PING_TOKEN_INTERRUPT) &&
+ /* high speed did bulk NYET; fifo isn't filling */
+ ep->dev->gadget.speed == USB_SPEED_FULL) {
+ unsigned usec;
+
+ usec = 50; /* 64 byte bulk/interrupt */
+ handshake(statp, BIT(USB_OUT_PING_NAK_SENT),
+ BIT(USB_OUT_PING_NAK_SENT), usec);
+ /* NAK done; now CLEAR_NAK_OUT_PACKETS is safe */
+ }
+}
+
+/* unload packet(s) from the fifo we use for usb OUT transfers.
+ * returns true iff the request completed, because of short packet
+ * or the request buffer having filled with full packets.
+ *
+ * for ep-a..ep-d this will read multiple packets out when they
+ * have been accepted.
+ */
+static int read_fifo(struct net2280_ep *ep, struct net2280_request *req)
+{
+ struct net2280_ep_regs __iomem *regs = ep->regs;
+ u8 *buf = req->req.buf + req->req.actual;
+ unsigned count, tmp, is_short;
+ unsigned cleanup = 0, prevent = 0;
+
+ /* erratum 0106 ... packets coming in during fifo reads might
+ * be incompletely rejected. not all cases have workarounds.
+ */
+ if (ep->dev->chiprev == 0x0100 &&
+ ep->dev->gadget.speed == USB_SPEED_FULL) {
+ udelay(1);
+ tmp = readl(&ep->regs->ep_stat);
+ if ((tmp & BIT(NAK_OUT_PACKETS)))
+ cleanup = 1;
+ else if ((tmp & BIT(FIFO_FULL))) {
+ start_out_naking(ep);
+ prevent = 1;
+ }
+ /* else: hope we don't see the problem */
+ }
+
+ /* never overflow the rx buffer. the fifo reads packets until
+ * it sees a short one; we might not be ready for them all.
+ */
+ prefetchw(buf);
+ count = readl(&regs->ep_avail);
+ if (unlikely(count == 0)) {
+ udelay(1);
+ tmp = readl(&ep->regs->ep_stat);
+ count = readl(&regs->ep_avail);
+ /* handled that data already? */
+ if (count == 0 && (tmp & BIT(NAK_OUT_PACKETS)) == 0)
+ return 0;
+ }
+
+ tmp = req->req.length - req->req.actual;
+ if (count > tmp) {
+ /* as with DMA, data overflow gets flushed */
+ if ((tmp % ep->ep.maxpacket) != 0) {
+ ep_err(ep->dev,
+ "%s out fifo %d bytes, expected %d\n",
+ ep->ep.name, count, tmp);
+ req->req.status = -EOVERFLOW;
+ cleanup = 1;
+ /* NAK_OUT_PACKETS will be set, so flushing is safe;
+ * the next read will start with the next packet
+ */
+ } /* else it's a ZLP, no worries */
+ count = tmp;
+ }
+ req->req.actual += count;
+
+ is_short = (count == 0) || ((count % ep->ep.maxpacket) != 0);
+
+ ep_vdbg(ep->dev, "read %s fifo (OUT) %d bytes%s%s%s req %p %d/%d\n",
+ ep->ep.name, count, is_short ? " (short)" : "",
+ cleanup ? " flush" : "", prevent ? " nak" : "",
+ req, req->req.actual, req->req.length);
+
+ while (count >= 4) {
+ tmp = readl(&regs->ep_data);
+ cpu_to_le32s(&tmp);
+ put_unaligned(tmp, (u32 *)buf);
+ buf += 4;
+ count -= 4;
+ }
+ if (count) {
+ tmp = readl(&regs->ep_data);
+ /* LE conversion is implicit here: */
+ do {
+ *buf++ = (u8) tmp;
+ tmp >>= 8;
+ } while (--count);
+ }
+ if (cleanup)
+ out_flush(ep);
+ if (prevent) {
+ writel(BIT(CLEAR_NAK_OUT_PACKETS), &ep->regs->ep_rsp);
+ (void) readl(&ep->regs->ep_rsp);
+ }
+
+ return is_short || ((req->req.actual == req->req.length) &&
+ !req->req.zero);
+}
+
+/* fill out dma descriptor to match a given request */
+static void fill_dma_desc(struct net2280_ep *ep,
+ struct net2280_request *req, int valid)
+{
+ struct net2280_dma *td = req->td;
+ u32 dmacount = req->req.length;
+
+ /* don't let DMA continue after a short OUT packet,
+ * so overruns can't affect the next transfer.
+ * in case of overruns on max-size packets, we can't
+ * stop the fifo from filling but we can flush it.
+ */
+ if (ep->is_in)
+ dmacount |= BIT(DMA_DIRECTION);
+ if ((!ep->is_in && (dmacount % ep->ep.maxpacket) != 0) ||
+ !(ep->dev->quirks & PLX_2280))
+ dmacount |= BIT(END_OF_CHAIN);
+
+ req->valid = valid;
+ if (valid)
+ dmacount |= BIT(VALID_BIT);
+ if (likely(!req->req.no_interrupt || !use_dma_chaining))
+ dmacount |= BIT(DMA_DONE_INTERRUPT_ENABLE);
+
+ /* td->dmadesc = previously set by caller */
+ td->dmaaddr = cpu_to_le32 (req->req.dma);
+
+ /* 2280 may be polling VALID_BIT through ep->dma->dmadesc */
+ wmb();
+ td->dmacount = cpu_to_le32(dmacount);
+}
+
+static const u32 dmactl_default =
+ BIT(DMA_SCATTER_GATHER_DONE_INTERRUPT) |
+ BIT(DMA_CLEAR_COUNT_ENABLE) |
+ /* erratum 0116 workaround part 1 (use POLLING) */
+ (POLL_100_USEC << DESCRIPTOR_POLLING_RATE) |
+ BIT(DMA_VALID_BIT_POLLING_ENABLE) |
+ BIT(DMA_VALID_BIT_ENABLE) |
+ BIT(DMA_SCATTER_GATHER_ENABLE) |
+ /* erratum 0116 workaround part 2 (no AUTOSTART) */
+ BIT(DMA_ENABLE);
+
+static inline void spin_stop_dma(struct net2280_dma_regs __iomem *dma)
+{
+ handshake(&dma->dmactl, BIT(DMA_ENABLE), 0, 50);
+}
+
+static inline void stop_dma(struct net2280_dma_regs __iomem *dma)
+{
+ writel(readl(&dma->dmactl) & ~BIT(DMA_ENABLE), &dma->dmactl);
+ spin_stop_dma(dma);
+}
+
+static void start_queue(struct net2280_ep *ep, u32 dmactl, u32 td_dma)
+{
+ struct net2280_dma_regs __iomem *dma = ep->dma;
+ unsigned int tmp = BIT(VALID_BIT) | (ep->is_in << DMA_DIRECTION);
+
+ if (!(ep->dev->quirks & PLX_2280))
+ tmp |= BIT(END_OF_CHAIN);
+
+ writel(tmp, &dma->dmacount);
+ writel(readl(&dma->dmastat), &dma->dmastat);
+
+ writel(td_dma, &dma->dmadesc);
+ if (ep->dev->quirks & PLX_SUPERSPEED)
+ dmactl |= BIT(DMA_REQUEST_OUTSTANDING);
+ writel(dmactl, &dma->dmactl);
+
+ /* erratum 0116 workaround part 3: pci arbiter away from net2280 */
+ (void) readl(&ep->dev->pci->pcimstctl);
+
+ writel(BIT(DMA_START), &dma->dmastat);
+
+ if (!ep->is_in)
+ stop_out_naking(ep);
+}
+
+static void start_dma(struct net2280_ep *ep, struct net2280_request *req)
+{
+ u32 tmp;
+ struct net2280_dma_regs __iomem *dma = ep->dma;
+
+ /* FIXME can't use DMA for ZLPs */
+
+ /* on this path we "know" there's no dma active (yet) */
+ WARN_ON(readl(&dma->dmactl) & BIT(DMA_ENABLE));
+ writel(0, &ep->dma->dmactl);
+
+ /* previous OUT packet might have been short */
+ if (!ep->is_in && (readl(&ep->regs->ep_stat) &
+ BIT(NAK_OUT_PACKETS))) {
+ writel(BIT(SHORT_PACKET_TRANSFERRED_INTERRUPT),
+ &ep->regs->ep_stat);
+
+ tmp = readl(&ep->regs->ep_avail);
+ if (tmp) {
+ writel(readl(&dma->dmastat), &dma->dmastat);
+
+ /* transfer all/some fifo data */
+ writel(req->req.dma, &dma->dmaaddr);
+ tmp = min(tmp, req->req.length);
+
+ /* dma irq, faking scatterlist status */
+ req->td->dmacount = cpu_to_le32(req->req.length - tmp);
+ writel(BIT(DMA_DONE_INTERRUPT_ENABLE) | tmp,
+ &dma->dmacount);
+ req->td->dmadesc = 0;
+ req->valid = 1;
+
+ writel(BIT(DMA_ENABLE), &dma->dmactl);
+ writel(BIT(DMA_START), &dma->dmastat);
+ return;
+ }
+ }
+
+ tmp = dmactl_default;
+
+ /* force packet boundaries between dma requests, but prevent the
+ * controller from automagically writing a last "short" packet
+ * (zero length) unless the driver explicitly said to do that.
+ */
+ if (ep->is_in) {
+ if (likely((req->req.length % ep->ep.maxpacket) ||
+ req->req.zero)){
+ tmp |= BIT(DMA_FIFO_VALIDATE);
+ ep->in_fifo_validate = 1;
+ } else
+ ep->in_fifo_validate = 0;
+ }
+
+ /* init req->td, pointing to the current dummy */
+ req->td->dmadesc = cpu_to_le32 (ep->td_dma);
+ fill_dma_desc(ep, req, 1);
+
+ if (!use_dma_chaining)
+ req->td->dmacount |= cpu_to_le32(BIT(END_OF_CHAIN));
+
+ start_queue(ep, tmp, req->td_dma);
+}
+
+static inline void resume_dma(struct net2280_ep *ep)
+{
+ writel(readl(&ep->dma->dmactl) | BIT(DMA_ENABLE), &ep->dma->dmactl);
+
+ ep->dma_started = true;
+}
+
+static inline void ep_stop_dma(struct net2280_ep *ep)
+{
+ writel(readl(&ep->dma->dmactl) & ~BIT(DMA_ENABLE), &ep->dma->dmactl);
+ spin_stop_dma(ep->dma);
+
+ ep->dma_started = false;
+}
+
+static inline void
+queue_dma(struct net2280_ep *ep, struct net2280_request *req, int valid)
+{
+ struct net2280_dma *end;
+ dma_addr_t tmp;
+
+ /* swap new dummy for old, link; fill and maybe activate */
+ end = ep->dummy;
+ ep->dummy = req->td;
+ req->td = end;
+
+ tmp = ep->td_dma;
+ ep->td_dma = req->td_dma;
+ req->td_dma = tmp;
+
+ end->dmadesc = cpu_to_le32 (ep->td_dma);
+
+ fill_dma_desc(ep, req, valid);
+}
+
+static void
+done(struct net2280_ep *ep, struct net2280_request *req, int status)
+{
+ struct net2280 *dev;
+ unsigned stopped = ep->stopped;
+
+ list_del_init(&req->queue);
+
+ if (req->req.status == -EINPROGRESS)
+ req->req.status = status;
+ else
+ status = req->req.status;
+
+ dev = ep->dev;
+ if (ep->dma)
+ usb_gadget_unmap_request(&dev->gadget, &req->req, ep->is_in);
+
+ if (status && status != -ESHUTDOWN)
+ ep_vdbg(dev, "complete %s req %p stat %d len %u/%u\n",
+ ep->ep.name, &req->req, status,
+ req->req.actual, req->req.length);
+
+ /* don't modify queue heads during completion callback */
+ ep->stopped = 1;
+ spin_unlock(&dev->lock);
+ req->req.complete(&ep->ep, &req->req);
+ spin_lock(&dev->lock);
+ ep->stopped = stopped;
+}
+
+/*-------------------------------------------------------------------------*/
+
+static int
+net2280_queue(struct usb_ep *_ep, struct usb_request *_req, gfp_t gfp_flags)
+{
+ struct net2280_request *req;
+ struct net2280_ep *ep;
+ struct net2280 *dev;
+ unsigned long flags;
+
+ /* we always require a cpu-view buffer, so that we can
+ * always use pio (as fallback or whatever).
+ */
+ req = container_of(_req, struct net2280_request, req);
+ if (!_req || !_req->complete || !_req->buf ||
+ !list_empty(&req->queue))
+ return -EINVAL;
+ if (_req->length > (~0 & DMA_BYTE_COUNT_MASK))
+ return -EDOM;
+ ep = container_of(_ep, struct net2280_ep, ep);
+ if (!_ep || (!ep->desc && ep->num != 0))
+ return -EINVAL;
+ dev = ep->dev;
+ if (!dev->driver || dev->gadget.speed == USB_SPEED_UNKNOWN)
+ return -ESHUTDOWN;
+
+ /* FIXME implement PIO fallback for ZLPs with DMA */
+ if (ep->dma && _req->length == 0)
+ return -EOPNOTSUPP;
+
+ /* set up dma mapping in case the caller didn't */
+ if (ep->dma) {
+ int ret;
+
+ ret = usb_gadget_map_request(&dev->gadget, _req,
+ ep->is_in);
+ if (ret)
+ return ret;
+ }
+
+#if 0
+ ep_vdbg(dev, "%s queue req %p, len %d buf %p\n",
+ _ep->name, _req, _req->length, _req->buf);
+#endif
+
+ spin_lock_irqsave(&dev->lock, flags);
+
+ _req->status = -EINPROGRESS;
+ _req->actual = 0;
+
+ /* kickstart this i/o queue? */
+ if (list_empty(&ep->queue) && !ep->stopped) {
+ /* DMA request while EP halted */
+ if (ep->dma &&
+ (readl(&ep->regs->ep_rsp) & BIT(CLEAR_ENDPOINT_HALT)) &&
+ (dev->quirks & PLX_SUPERSPEED)) {
+ int valid = 1;
+ if (ep->is_in) {
+ int expect;
+ expect = likely(req->req.zero ||
+ ((req->req.length %
+ ep->ep.maxpacket) != 0));
+ if (expect != ep->in_fifo_validate)
+ valid = 0;
+ }
+ queue_dma(ep, req, valid);
+ }
+ /* use DMA if the endpoint supports it, else pio */
+ else if (ep->dma)
+ start_dma(ep, req);
+ else {
+ /* maybe there's no control data, just status ack */
+ if (ep->num == 0 && _req->length == 0) {
+ allow_status(ep);
+ done(ep, req, 0);
+ ep_vdbg(dev, "%s status ack\n", ep->ep.name);
+ goto done;
+ }
+
+ /* PIO ... stuff the fifo, or unblock it. */
+ if (ep->is_in)
+ write_fifo(ep, _req);
+ else if (list_empty(&ep->queue)) {
+ u32 s;
+
+ /* OUT FIFO might have packet(s) buffered */
+ s = readl(&ep->regs->ep_stat);
+ if ((s & BIT(FIFO_EMPTY)) == 0) {
+ /* note: _req->short_not_ok is
+ * ignored here since PIO _always_
+ * stops queue advance here, and
+ * _req->status doesn't change for
+ * short reads (only _req->actual)
+ */
+ if (read_fifo(ep, req) &&
+ ep->num == 0) {
+ done(ep, req, 0);
+ allow_status(ep);
+ /* don't queue it */
+ req = NULL;
+ } else if (read_fifo(ep, req) &&
+ ep->num != 0) {
+ done(ep, req, 0);
+ req = NULL;
+ } else
+ s = readl(&ep->regs->ep_stat);
+ }
+
+ /* don't NAK, let the fifo fill */
+ if (req && (s & BIT(NAK_OUT_PACKETS)))
+ writel(BIT(CLEAR_NAK_OUT_PACKETS),
+ &ep->regs->ep_rsp);
+ }
+ }
+
+ } else if (ep->dma) {
+ int valid = 1;
+
+ if (ep->is_in) {
+ int expect;
+
+ /* preventing magic zlps is per-engine state, not
+ * per-transfer; irq logic must recover hiccups.
+ */
+ expect = likely(req->req.zero ||
+ (req->req.length % ep->ep.maxpacket));
+ if (expect != ep->in_fifo_validate)
+ valid = 0;
+ }
+ queue_dma(ep, req, valid);
+
+ } /* else the irq handler advances the queue. */
+
+ ep->responded = 1;
+ if (req)
+ list_add_tail(&req->queue, &ep->queue);
+done:
+ spin_unlock_irqrestore(&dev->lock, flags);
+
+ /* pci writes may still be posted */
+ return 0;
+}
+
+static inline void
+dma_done(struct net2280_ep *ep, struct net2280_request *req, u32 dmacount,
+ int status)
+{
+ req->req.actual = req->req.length - (DMA_BYTE_COUNT_MASK & dmacount);
+ done(ep, req, status);
+}
+
+static void restart_dma(struct net2280_ep *ep);
+
+static void scan_dma_completions(struct net2280_ep *ep)
+{
+ /* only look at descriptors that were "naturally" retired,
+ * so fifo and list head state won't matter
+ */
+ while (!list_empty(&ep->queue)) {
+ struct net2280_request *req;
+ u32 tmp;
+
+ req = list_entry(ep->queue.next,
+ struct net2280_request, queue);
+ if (!req->valid)
+ break;
+ rmb();
+ tmp = le32_to_cpup(&req->td->dmacount);
+ if ((tmp & BIT(VALID_BIT)) != 0)
+ break;
+
+ /* SHORT_PACKET_TRANSFERRED_INTERRUPT handles "usb-short"
+ * cases where DMA must be aborted; this code handles
+ * all non-abort DMA completions.
+ */
+ if (unlikely(req->td->dmadesc == 0)) {
+ /* paranoia */
+ tmp = readl(&ep->dma->dmacount);
+ if (tmp & DMA_BYTE_COUNT_MASK)
+ break;
+ /* single transfer mode */
+ dma_done(ep, req, tmp, 0);
+ break;
+ } else if (!ep->is_in &&
+ (req->req.length % ep->ep.maxpacket) != 0) {
+ tmp = readl(&ep->regs->ep_stat);
+ if (ep->dev->quirks & PLX_SUPERSPEED)
+ return dma_done(ep, req, tmp, 0);
+
+ /* AVOID TROUBLE HERE by not issuing short reads from
+ * your gadget driver. That helps avoids errata 0121,
+ * 0122, and 0124; not all cases trigger the warning.
+ */
+ if ((tmp & BIT(NAK_OUT_PACKETS)) == 0) {
+ ep_warn(ep->dev, "%s lost packet sync!\n",
+ ep->ep.name);
+ req->req.status = -EOVERFLOW;
+ } else {
+ tmp = readl(&ep->regs->ep_avail);
+ if (tmp) {
+ /* fifo gets flushed later */
+ ep->out_overflow = 1;
+ ep_dbg(ep->dev,
+ "%s dma, discard %d len %d\n",
+ ep->ep.name, tmp,
+ req->req.length);
+ req->req.status = -EOVERFLOW;
+ }
+ }
+ }
+ dma_done(ep, req, tmp, 0);
+ }
+}
+
+static void restart_dma(struct net2280_ep *ep)
+{
+ struct net2280_request *req;
+ u32 dmactl = dmactl_default;
+
+ if (ep->stopped)
+ return;
+ req = list_entry(ep->queue.next, struct net2280_request, queue);
+
+ if (!use_dma_chaining) {
+ start_dma(ep, req);
+ return;
+ }
+
+ /* the 2280 will be processing the queue unless queue hiccups after
+ * the previous transfer:
+ * IN: wanted automagic zlp, head doesn't (or vice versa)
+ * DMA_FIFO_VALIDATE doesn't init from dma descriptors.
+ * OUT: was "usb-short", we must restart.
+ */
+ if (ep->is_in && !req->valid) {
+ struct net2280_request *entry, *prev = NULL;
+ int reqmode, done = 0;
+
+ ep_dbg(ep->dev, "%s dma hiccup td %p\n", ep->ep.name, req->td);
+ ep->in_fifo_validate = likely(req->req.zero ||
+ (req->req.length % ep->ep.maxpacket) != 0);
+ if (ep->in_fifo_validate)
+ dmactl |= BIT(DMA_FIFO_VALIDATE);
+ list_for_each_entry(entry, &ep->queue, queue) {
+ __le32 dmacount;
+
+ if (entry == req)
+ continue;
+ dmacount = entry->td->dmacount;
+ if (!done) {
+ reqmode = likely(entry->req.zero ||
+ (entry->req.length % ep->ep.maxpacket));
+ if (reqmode == ep->in_fifo_validate) {
+ entry->valid = 1;
+ dmacount |= valid_bit;
+ entry->td->dmacount = dmacount;
+ prev = entry;
+ continue;
+ } else {
+ /* force a hiccup */
+ prev->td->dmacount |= dma_done_ie;
+ done = 1;
+ }
+ }
+
+ /* walk the rest of the queue so unlinks behave */
+ entry->valid = 0;
+ dmacount &= ~valid_bit;
+ entry->td->dmacount = dmacount;
+ prev = entry;
+ }
+ }
+
+ writel(0, &ep->dma->dmactl);
+ start_queue(ep, dmactl, req->td_dma);
+}
+
+static void abort_dma_228x(struct net2280_ep *ep)
+{
+ /* abort the current transfer */
+ if (likely(!list_empty(&ep->queue))) {
+ /* FIXME work around errata 0121, 0122, 0124 */
+ writel(BIT(DMA_ABORT), &ep->dma->dmastat);
+ spin_stop_dma(ep->dma);
+ } else
+ stop_dma(ep->dma);
+ scan_dma_completions(ep);
+}
+
+static void abort_dma_338x(struct net2280_ep *ep)
+{
+ writel(BIT(DMA_ABORT), &ep->dma->dmastat);
+ spin_stop_dma(ep->dma);
+}
+
+static void abort_dma(struct net2280_ep *ep)
+{
+ if (ep->dev->quirks & PLX_LEGACY)
+ return abort_dma_228x(ep);
+ return abort_dma_338x(ep);
+}
+
+/* dequeue ALL requests */
+static void nuke(struct net2280_ep *ep)
+{
+ struct net2280_request *req;
+
+ /* called with spinlock held */
+ ep->stopped = 1;
+ if (ep->dma)
+ abort_dma(ep);
+ while (!list_empty(&ep->queue)) {
+ req = list_entry(ep->queue.next,
+ struct net2280_request,
+ queue);
+ done(ep, req, -ESHUTDOWN);
+ }
+}
+
+/* dequeue JUST ONE request */
+static int net2280_dequeue(struct usb_ep *_ep, struct usb_request *_req)
+{
+ struct net2280_ep *ep;
+ struct net2280_request *req;
+ unsigned long flags;
+ u32 dmactl;
+ int stopped;
+
+ ep = container_of(_ep, struct net2280_ep, ep);
+ if (!_ep || (!ep->desc && ep->num != 0) || !_req)
+ return -EINVAL;
+
+ spin_lock_irqsave(&ep->dev->lock, flags);
+ stopped = ep->stopped;
+
+ /* quiesce dma while we patch the queue */
+ dmactl = 0;
+ ep->stopped = 1;
+ if (ep->dma) {
+ dmactl = readl(&ep->dma->dmactl);
+ /* WARNING erratum 0127 may kick in ... */
+ stop_dma(ep->dma);
+ scan_dma_completions(ep);
+ }
+
+ /* make sure it's still queued on this endpoint */
+ list_for_each_entry(req, &ep->queue, queue) {
+ if (&req->req == _req)
+ break;
+ }
+ if (&req->req != _req) {
+ spin_unlock_irqrestore(&ep->dev->lock, flags);
+ return -EINVAL;
+ }
+
+ /* queue head may be partially complete. */
+ if (ep->queue.next == &req->queue) {
+ if (ep->dma) {
+ ep_dbg(ep->dev, "unlink (%s) dma\n", _ep->name);
+ _req->status = -ECONNRESET;
+ abort_dma(ep);
+ if (likely(ep->queue.next == &req->queue)) {
+ /* NOTE: misreports single-transfer mode*/
+ req->td->dmacount = 0; /* invalidate */
+ dma_done(ep, req,
+ readl(&ep->dma->dmacount),
+ -ECONNRESET);
+ }
+ } else {
+ ep_dbg(ep->dev, "unlink (%s) pio\n", _ep->name);
+ done(ep, req, -ECONNRESET);
+ }
+ req = NULL;
+
+ /* patch up hardware chaining data */
+ } else if (ep->dma && use_dma_chaining) {
+ if (req->queue.prev == ep->queue.next) {
+ writel(le32_to_cpu(req->td->dmadesc),
+ &ep->dma->dmadesc);
+ if (req->td->dmacount & dma_done_ie)
+ writel(readl(&ep->dma->dmacount) |
+ le32_to_cpu(dma_done_ie),
+ &ep->dma->dmacount);
+ } else {
+ struct net2280_request *prev;
+
+ prev = list_entry(req->queue.prev,
+ struct net2280_request, queue);
+ prev->td->dmadesc = req->td->dmadesc;
+ if (req->td->dmacount & dma_done_ie)
+ prev->td->dmacount |= dma_done_ie;
+ }
+ }
+
+ if (req)
+ done(ep, req, -ECONNRESET);
+ ep->stopped = stopped;
+
+ if (ep->dma) {
+ /* turn off dma on inactive queues */
+ if (list_empty(&ep->queue))
+ stop_dma(ep->dma);
+ else if (!ep->stopped) {
+ /* resume current request, or start new one */
+ if (req)
+ writel(dmactl, &ep->dma->dmactl);
+ else
+ start_dma(ep, list_entry(ep->queue.next,
+ struct net2280_request, queue));
+ }
+ }
+
+ spin_unlock_irqrestore(&ep->dev->lock, flags);
+ return 0;
+}
+
+/*-------------------------------------------------------------------------*/
+
+static int net2280_fifo_status(struct usb_ep *_ep);
+
+static int
+net2280_set_halt_and_wedge(struct usb_ep *_ep, int value, int wedged)
+{
+ struct net2280_ep *ep;
+ unsigned long flags;
+ int retval = 0;
+
+ ep = container_of(_ep, struct net2280_ep, ep);
+ if (!_ep || (!ep->desc && ep->num != 0))
+ return -EINVAL;
+ if (!ep->dev->driver || ep->dev->gadget.speed == USB_SPEED_UNKNOWN)
+ return -ESHUTDOWN;
+ if (ep->desc /* not ep0 */ && (ep->desc->bmAttributes & 0x03)
+ == USB_ENDPOINT_XFER_ISOC)
+ return -EINVAL;
+
+ spin_lock_irqsave(&ep->dev->lock, flags);
+ if (!list_empty(&ep->queue))
+ retval = -EAGAIN;
+ else if (ep->is_in && value && net2280_fifo_status(_ep) != 0)
+ retval = -EAGAIN;
+ else {
+ ep_vdbg(ep->dev, "%s %s %s\n", _ep->name,
+ value ? "set" : "clear",
+ wedged ? "wedge" : "halt");
+ /* set/clear, then synch memory views with the device */
+ if (value) {
+ if (ep->num == 0)
+ ep->dev->protocol_stall = 1;
+ else
+ set_halt(ep);
+ if (wedged)
+ ep->wedged = 1;
+ } else {
+ clear_halt(ep);
+ if (ep->dev->quirks & PLX_SUPERSPEED &&
+ !list_empty(&ep->queue) && ep->td_dma)
+ restart_dma(ep);
+ ep->wedged = 0;
+ }
+ (void) readl(&ep->regs->ep_rsp);
+ }
+ spin_unlock_irqrestore(&ep->dev->lock, flags);
+
+ return retval;
+}
+
+static int net2280_set_halt(struct usb_ep *_ep, int value)
+{
+ return net2280_set_halt_and_wedge(_ep, value, 0);
+}
+
+static int net2280_set_wedge(struct usb_ep *_ep)
+{
+ if (!_ep || _ep->name == ep0name)
+ return -EINVAL;
+ return net2280_set_halt_and_wedge(_ep, 1, 1);
+}
+
+static int net2280_fifo_status(struct usb_ep *_ep)
+{
+ struct net2280_ep *ep;
+ u32 avail;
+
+ ep = container_of(_ep, struct net2280_ep, ep);
+ if (!_ep || (!ep->desc && ep->num != 0))
+ return -ENODEV;
+ if (!ep->dev->driver || ep->dev->gadget.speed == USB_SPEED_UNKNOWN)
+ return -ESHUTDOWN;
+
+ avail = readl(&ep->regs->ep_avail) & (BIT(12) - 1);
+ if (avail > ep->fifo_size)
+ return -EOVERFLOW;
+ if (ep->is_in)
+ avail = ep->fifo_size - avail;
+ return avail;
+}
+
+static void net2280_fifo_flush(struct usb_ep *_ep)
+{
+ struct net2280_ep *ep;
+
+ ep = container_of(_ep, struct net2280_ep, ep);
+ if (!_ep || (!ep->desc && ep->num != 0))
+ return;
+ if (!ep->dev->driver || ep->dev->gadget.speed == USB_SPEED_UNKNOWN)
+ return;
+
+ writel(BIT(FIFO_FLUSH), &ep->regs->ep_stat);
+ (void) readl(&ep->regs->ep_rsp);
+}
+
+static const struct usb_ep_ops net2280_ep_ops = {
+ .enable = net2280_enable,
+ .disable = net2280_disable,
+
+ .alloc_request = net2280_alloc_request,
+ .free_request = net2280_free_request,
+
+ .queue = net2280_queue,
+ .dequeue = net2280_dequeue,
+
+ .set_halt = net2280_set_halt,
+ .set_wedge = net2280_set_wedge,
+ .fifo_status = net2280_fifo_status,
+ .fifo_flush = net2280_fifo_flush,
+};
+
+/*-------------------------------------------------------------------------*/
+
+static int net2280_get_frame(struct usb_gadget *_gadget)
+{
+ struct net2280 *dev;
+ unsigned long flags;
+ u16 retval;
+
+ if (!_gadget)
+ return -ENODEV;
+ dev = container_of(_gadget, struct net2280, gadget);
+ spin_lock_irqsave(&dev->lock, flags);
+ retval = get_idx_reg(dev->regs, REG_FRAME) & 0x03ff;
+ spin_unlock_irqrestore(&dev->lock, flags);
+ return retval;
+}
+
+static int net2280_wakeup(struct usb_gadget *_gadget)
+{
+ struct net2280 *dev;
+ u32 tmp;
+ unsigned long flags;
+
+ if (!_gadget)
+ return 0;
+ dev = container_of(_gadget, struct net2280, gadget);
+
+ spin_lock_irqsave(&dev->lock, flags);
+ tmp = readl(&dev->usb->usbctl);
+ if (tmp & BIT(DEVICE_REMOTE_WAKEUP_ENABLE))
+ writel(BIT(GENERATE_RESUME), &dev->usb->usbstat);
+ spin_unlock_irqrestore(&dev->lock, flags);
+
+ /* pci writes may still be posted */
+ return 0;
+}
+
+static int net2280_set_selfpowered(struct usb_gadget *_gadget, int value)
+{
+ struct net2280 *dev;
+ u32 tmp;
+ unsigned long flags;
+
+ if (!_gadget)
+ return 0;
+ dev = container_of(_gadget, struct net2280, gadget);
+
+ spin_lock_irqsave(&dev->lock, flags);
+ tmp = readl(&dev->usb->usbctl);
+ if (value) {
+ tmp |= BIT(SELF_POWERED_STATUS);
+ dev->selfpowered = 1;
+ } else {
+ tmp &= ~BIT(SELF_POWERED_STATUS);
+ dev->selfpowered = 0;
+ }
+ writel(tmp, &dev->usb->usbctl);
+ spin_unlock_irqrestore(&dev->lock, flags);
+
+ return 0;
+}
+
+static int net2280_pullup(struct usb_gadget *_gadget, int is_on)
+{
+ struct net2280 *dev;
+ u32 tmp;
+ unsigned long flags;
+
+ if (!_gadget)
+ return -ENODEV;
+ dev = container_of(_gadget, struct net2280, gadget);
+
+ spin_lock_irqsave(&dev->lock, flags);
+ tmp = readl(&dev->usb->usbctl);
+ dev->softconnect = (is_on != 0);
+ if (is_on)
+ tmp |= BIT(USB_DETECT_ENABLE);
+ else
+ tmp &= ~BIT(USB_DETECT_ENABLE);
+ writel(tmp, &dev->usb->usbctl);
+ spin_unlock_irqrestore(&dev->lock, flags);
+
+ return 0;
+}
+
+static int net2280_start(struct usb_gadget *_gadget,
+ struct usb_gadget_driver *driver);
+static int net2280_stop(struct usb_gadget *_gadget,
+ struct usb_gadget_driver *driver);
+
+static const struct usb_gadget_ops net2280_ops = {
+ .get_frame = net2280_get_frame,
+ .wakeup = net2280_wakeup,
+ .set_selfpowered = net2280_set_selfpowered,
+ .pullup = net2280_pullup,
+ .udc_start = net2280_start,
+ .udc_stop = net2280_stop,
+};
+
+/*-------------------------------------------------------------------------*/
+
+#ifdef CONFIG_USB_GADGET_DEBUG_FILES
+
+/* FIXME move these into procfs, and use seq_file.
+ * Sysfs _still_ doesn't behave for arbitrarily sized files,
+ * and also doesn't help products using this with 2.4 kernels.
+ */
+
+/* "function" sysfs attribute */
+static ssize_t function_show(struct device *_dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct net2280 *dev = dev_get_drvdata(_dev);
+
+ if (!dev->driver || !dev->driver->function ||
+ strlen(dev->driver->function) > PAGE_SIZE)
+ return 0;
+ return scnprintf(buf, PAGE_SIZE, "%s\n", dev->driver->function);
+}
+static DEVICE_ATTR_RO(function);
+
+static ssize_t registers_show(struct device *_dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct net2280 *dev;
+ char *next;
+ unsigned size, t;
+ unsigned long flags;
+ int i;
+ u32 t1, t2;
+ const char *s;
+
+ dev = dev_get_drvdata(_dev);
+ next = buf;
+ size = PAGE_SIZE;
+ spin_lock_irqsave(&dev->lock, flags);
+
+ if (dev->driver)
+ s = dev->driver->driver.name;
+ else
+ s = "(none)";
+
+ /* Main Control Registers */
+ t = scnprintf(next, size, "%s version " DRIVER_VERSION
+ ", chiprev %04x, dma %s\n\n"
+ "devinit %03x fifoctl %08x gadget '%s'\n"
+ "pci irqenb0 %02x irqenb1 %08x "
+ "irqstat0 %04x irqstat1 %08x\n",
+ driver_name, dev->chiprev,
+ use_dma
+ ? (use_dma_chaining ? "chaining" : "enabled")
+ : "disabled",
+ readl(&dev->regs->devinit),
+ readl(&dev->regs->fifoctl),
+ s,
+ readl(&dev->regs->pciirqenb0),
+ readl(&dev->regs->pciirqenb1),
+ readl(&dev->regs->irqstat0),
+ readl(&dev->regs->irqstat1));
+ size -= t;
+ next += t;
+
+ /* USB Control Registers */
+ t1 = readl(&dev->usb->usbctl);
+ t2 = readl(&dev->usb->usbstat);
+ if (t1 & BIT(VBUS_PIN)) {
+ if (t2 & BIT(HIGH_SPEED))
+ s = "high speed";
+ else if (dev->gadget.speed == USB_SPEED_UNKNOWN)
+ s = "powered";
+ else
+ s = "full speed";
+ /* full speed bit (6) not working?? */
+ } else
+ s = "not attached";
+ t = scnprintf(next, size,
+ "stdrsp %08x usbctl %08x usbstat %08x "
+ "addr 0x%02x (%s)\n",
+ readl(&dev->usb->stdrsp), t1, t2,
+ readl(&dev->usb->ouraddr), s);
+ size -= t;
+ next += t;
+
+ /* PCI Master Control Registers */
+
+ /* DMA Control Registers */
+
+ /* Configurable EP Control Registers */
+ for (i = 0; i < dev->n_ep; i++) {
+ struct net2280_ep *ep;
+
+ ep = &dev->ep[i];
+ if (i && !ep->desc)
+ continue;
+
+ t1 = readl(&ep->cfg->ep_cfg);
+ t2 = readl(&ep->regs->ep_rsp) & 0xff;
+ t = scnprintf(next, size,
+ "\n%s\tcfg %05x rsp (%02x) %s%s%s%s%s%s%s%s"
+ "irqenb %02x\n",
+ ep->ep.name, t1, t2,
+ (t2 & BIT(CLEAR_NAK_OUT_PACKETS))
+ ? "NAK " : "",
+ (t2 & BIT(CLEAR_EP_HIDE_STATUS_PHASE))
+ ? "hide " : "",
+ (t2 & BIT(CLEAR_EP_FORCE_CRC_ERROR))
+ ? "CRC " : "",
+ (t2 & BIT(CLEAR_INTERRUPT_MODE))
+ ? "interrupt " : "",
+ (t2 & BIT(CLEAR_CONTROL_STATUS_PHASE_HANDSHAKE))
+ ? "status " : "",
+ (t2 & BIT(CLEAR_NAK_OUT_PACKETS_MODE))
+ ? "NAKmode " : "",
+ (t2 & BIT(CLEAR_ENDPOINT_TOGGLE))
+ ? "DATA1 " : "DATA0 ",
+ (t2 & BIT(CLEAR_ENDPOINT_HALT))
+ ? "HALT " : "",
+ readl(&ep->regs->ep_irqenb));
+ size -= t;
+ next += t;
+
+ t = scnprintf(next, size,
+ "\tstat %08x avail %04x "
+ "(ep%d%s-%s)%s\n",
+ readl(&ep->regs->ep_stat),
+ readl(&ep->regs->ep_avail),
+ t1 & 0x0f, DIR_STRING(t1),
+ type_string(t1 >> 8),
+ ep->stopped ? "*" : "");
+ size -= t;
+ next += t;
+
+ if (!ep->dma)
+ continue;
+
+ t = scnprintf(next, size,
+ " dma\tctl %08x stat %08x count %08x\n"
+ "\taddr %08x desc %08x\n",
+ readl(&ep->dma->dmactl),
+ readl(&ep->dma->dmastat),
+ readl(&ep->dma->dmacount),
+ readl(&ep->dma->dmaaddr),
+ readl(&ep->dma->dmadesc));
+ size -= t;
+ next += t;
+
+ }
+
+ /* Indexed Registers (none yet) */
+
+ /* Statistics */
+ t = scnprintf(next, size, "\nirqs: ");
+ size -= t;
+ next += t;
+ for (i = 0; i < dev->n_ep; i++) {
+ struct net2280_ep *ep;
+
+ ep = &dev->ep[i];
+ if (i && !ep->irqs)
+ continue;
+ t = scnprintf(next, size, " %s/%lu", ep->ep.name, ep->irqs);
+ size -= t;
+ next += t;
+
+ }
+ t = scnprintf(next, size, "\n");
+ size -= t;
+ next += t;
+
+ spin_unlock_irqrestore(&dev->lock, flags);
+
+ return PAGE_SIZE - size;
+}
+static DEVICE_ATTR_RO(registers);
+
+static ssize_t queues_show(struct device *_dev, struct device_attribute *attr,
+ char *buf)
+{
+ struct net2280 *dev;
+ char *next;
+ unsigned size;
+ unsigned long flags;
+ int i;
+
+ dev = dev_get_drvdata(_dev);
+ next = buf;
+ size = PAGE_SIZE;
+ spin_lock_irqsave(&dev->lock, flags);
+
+ for (i = 0; i < dev->n_ep; i++) {
+ struct net2280_ep *ep = &dev->ep[i];
+ struct net2280_request *req;
+ int t;
+
+ if (i != 0) {
+ const struct usb_endpoint_descriptor *d;
+
+ d = ep->desc;
+ if (!d)
+ continue;
+ t = d->bEndpointAddress;
+ t = scnprintf(next, size,
+ "\n%s (ep%d%s-%s) max %04x %s fifo %d\n",
+ ep->ep.name, t & USB_ENDPOINT_NUMBER_MASK,
+ (t & USB_DIR_IN) ? "in" : "out",
+ type_string(d->bmAttributes),
+ usb_endpoint_maxp(d) & 0x1fff,
+ ep->dma ? "dma" : "pio", ep->fifo_size
+ );
+ } else /* ep0 should only have one transfer queued */
+ t = scnprintf(next, size, "ep0 max 64 pio %s\n",
+ ep->is_in ? "in" : "out");
+ if (t <= 0 || t > size)
+ goto done;
+ size -= t;
+ next += t;
+
+ if (list_empty(&ep->queue)) {
+ t = scnprintf(next, size, "\t(nothing queued)\n");
+ if (t <= 0 || t > size)
+ goto done;
+ size -= t;
+ next += t;
+ continue;
+ }
+ list_for_each_entry(req, &ep->queue, queue) {
+ if (ep->dma && req->td_dma == readl(&ep->dma->dmadesc))
+ t = scnprintf(next, size,
+ "\treq %p len %d/%d "
+ "buf %p (dmacount %08x)\n",
+ &req->req, req->req.actual,
+ req->req.length, req->req.buf,
+ readl(&ep->dma->dmacount));
+ else
+ t = scnprintf(next, size,
+ "\treq %p len %d/%d buf %p\n",
+ &req->req, req->req.actual,
+ req->req.length, req->req.buf);
+ if (t <= 0 || t > size)
+ goto done;
+ size -= t;
+ next += t;
+
+ if (ep->dma) {
+ struct net2280_dma *td;
+
+ td = req->td;
+ t = scnprintf(next, size, "\t td %08x "
+ " count %08x buf %08x desc %08x\n",
+ (u32) req->td_dma,
+ le32_to_cpu(td->dmacount),
+ le32_to_cpu(td->dmaaddr),
+ le32_to_cpu(td->dmadesc));
+ if (t <= 0 || t > size)
+ goto done;
+ size -= t;
+ next += t;
+ }
+ }
+ }
+
+done:
+ spin_unlock_irqrestore(&dev->lock, flags);
+ return PAGE_SIZE - size;
+}
+static DEVICE_ATTR_RO(queues);
+
+
+#else
+
+#define device_create_file(a, b) (0)
+#define device_remove_file(a, b) do { } while (0)
+
+#endif
+
+/*-------------------------------------------------------------------------*/
+
+/* another driver-specific mode might be a request type doing dma
+ * to/from another device fifo instead of to/from memory.
+ */
+
+static void set_fifo_mode(struct net2280 *dev, int mode)
+{
+ /* keeping high bits preserves BAR2 */
+ writel((0xffff << PCI_BASE2_RANGE) | mode, &dev->regs->fifoctl);
+
+ /* always ep-{a,b,e,f} ... maybe not ep-c or ep-d */
+ INIT_LIST_HEAD(&dev->gadget.ep_list);
+ list_add_tail(&dev->ep[1].ep.ep_list, &dev->gadget.ep_list);
+ list_add_tail(&dev->ep[2].ep.ep_list, &dev->gadget.ep_list);
+ switch (mode) {
+ case 0:
+ list_add_tail(&dev->ep[3].ep.ep_list, &dev->gadget.ep_list);
+ list_add_tail(&dev->ep[4].ep.ep_list, &dev->gadget.ep_list);
+ dev->ep[1].fifo_size = dev->ep[2].fifo_size = 1024;
+ break;
+ case 1:
+ dev->ep[1].fifo_size = dev->ep[2].fifo_size = 2048;
+ break;
+ case 2:
+ list_add_tail(&dev->ep[3].ep.ep_list, &dev->gadget.ep_list);
+ dev->ep[1].fifo_size = 2048;
+ dev->ep[2].fifo_size = 1024;
+ break;
+ }
+ /* fifo sizes for ep0, ep-c, ep-d, ep-e, and ep-f never change */
+ list_add_tail(&dev->ep[5].ep.ep_list, &dev->gadget.ep_list);
+ list_add_tail(&dev->ep[6].ep.ep_list, &dev->gadget.ep_list);
+}
+
+static void defect7374_disable_data_eps(struct net2280 *dev)
+{
+ /*
+ * For Defect 7374, disable data EPs (and more):
+ * - This phase undoes the earlier phase of the Defect 7374 workaround,
+ * returing ep regs back to normal.
+ */
+ struct net2280_ep *ep;
+ int i;
+ unsigned char ep_sel;
+ u32 tmp_reg;
+
+ for (i = 1; i < 5; i++) {
+ ep = &dev->ep[i];
+ writel(0, &ep->cfg->ep_cfg);
+ }
+
+ /* CSROUT, CSRIN, PCIOUT, PCIIN, STATIN, RCIN */
+ for (i = 0; i < 6; i++)
+ writel(0, &dev->dep[i].dep_cfg);
+
+ for (ep_sel = 0; ep_sel <= 21; ep_sel++) {
+ /* Select an endpoint for subsequent operations: */
+ tmp_reg = readl(&dev->plregs->pl_ep_ctrl);
+ writel(((tmp_reg & ~0x1f) | ep_sel), &dev->plregs->pl_ep_ctrl);
+
+ if (ep_sel < 2 || (ep_sel > 9 && ep_sel < 14) ||
+ ep_sel == 18 || ep_sel == 20)
+ continue;
+
+ /* Change settings on some selected endpoints */
+ tmp_reg = readl(&dev->plregs->pl_ep_cfg_4);
+ tmp_reg &= ~BIT(NON_CTRL_IN_TOLERATE_BAD_DIR);
+ writel(tmp_reg, &dev->plregs->pl_ep_cfg_4);
+ tmp_reg = readl(&dev->plregs->pl_ep_ctrl);
+ tmp_reg |= BIT(EP_INITIALIZED);
+ writel(tmp_reg, &dev->plregs->pl_ep_ctrl);
+ }
+}
+
+static void defect7374_enable_data_eps_zero(struct net2280 *dev)
+{
+ u32 tmp = 0, tmp_reg;
+ u32 fsmvalue, scratch;
+ int i;
+ unsigned char ep_sel;
+
+ scratch = get_idx_reg(dev->regs, SCRATCH);
+ fsmvalue = scratch & (0xf << DEFECT7374_FSM_FIELD);
+ scratch &= ~(0xf << DEFECT7374_FSM_FIELD);
+
+ /*See if firmware needs to set up for workaround*/
+ if (fsmvalue != DEFECT7374_FSM_SS_CONTROL_READ) {
+ ep_warn(dev, "Operate Defect 7374 workaround soft this time");
+ ep_warn(dev, "It will operate on cold-reboot and SS connect");
+
+ /*GPEPs:*/
+ tmp = ((0 << ENDPOINT_NUMBER) | BIT(ENDPOINT_DIRECTION) |
+ (2 << OUT_ENDPOINT_TYPE) | (2 << IN_ENDPOINT_TYPE) |
+ ((dev->enhanced_mode) ?
+ BIT(OUT_ENDPOINT_ENABLE) : BIT(ENDPOINT_ENABLE)) |
+ BIT(IN_ENDPOINT_ENABLE));
+
+ for (i = 1; i < 5; i++)
+ writel(tmp, &dev->ep[i].cfg->ep_cfg);
+
+ /* CSRIN, PCIIN, STATIN, RCIN*/
+ tmp = ((0 << ENDPOINT_NUMBER) | BIT(ENDPOINT_ENABLE));
+ writel(tmp, &dev->dep[1].dep_cfg);
+ writel(tmp, &dev->dep[3].dep_cfg);
+ writel(tmp, &dev->dep[4].dep_cfg);
+ writel(tmp, &dev->dep[5].dep_cfg);
+
+ /*Implemented for development and debug.
+ * Can be refined/tuned later.*/
+ for (ep_sel = 0; ep_sel <= 21; ep_sel++) {
+ /* Select an endpoint for subsequent operations: */
+ tmp_reg = readl(&dev->plregs->pl_ep_ctrl);
+ writel(((tmp_reg & ~0x1f) | ep_sel),
+ &dev->plregs->pl_ep_ctrl);
+
+ if (ep_sel == 1) {
+ tmp =
+ (readl(&dev->plregs->pl_ep_ctrl) |
+ BIT(CLEAR_ACK_ERROR_CODE) | 0);
+ writel(tmp, &dev->plregs->pl_ep_ctrl);
+ continue;
+ }
+
+ if (ep_sel == 0 || (ep_sel > 9 && ep_sel < 14) ||
+ ep_sel == 18 || ep_sel == 20)
+ continue;
+
+ tmp = (readl(&dev->plregs->pl_ep_cfg_4) |
+ BIT(NON_CTRL_IN_TOLERATE_BAD_DIR) | 0);
+ writel(tmp, &dev->plregs->pl_ep_cfg_4);
+
+ tmp = readl(&dev->plregs->pl_ep_ctrl) &
+ ~BIT(EP_INITIALIZED);
+ writel(tmp, &dev->plregs->pl_ep_ctrl);
+
+ }
+
+ /* Set FSM to focus on the first Control Read:
+ * - Tip: Connection speed is known upon the first
+ * setup request.*/
+ scratch |= DEFECT7374_FSM_WAITING_FOR_CONTROL_READ;
+ set_idx_reg(dev->regs, SCRATCH, scratch);
+
+ } else{
+ ep_warn(dev, "Defect 7374 workaround soft will NOT operate");
+ ep_warn(dev, "It will operate on cold-reboot and SS connect");
+ }
+}
+
+/* keeping it simple:
+ * - one bus driver, initted first;
+ * - one function driver, initted second
+ *
+ * most of the work to support multiple net2280 controllers would
+ * be to associate this gadget driver (yes?) with all of them, or
+ * perhaps to bind specific drivers to specific devices.
+ */
+
+static void usb_reset_228x(struct net2280 *dev)
+{
+ u32 tmp;
+
+ dev->gadget.speed = USB_SPEED_UNKNOWN;
+ (void) readl(&dev->usb->usbctl);
+
+ net2280_led_init(dev);
+
+ /* disable automatic responses, and irqs */
+ writel(0, &dev->usb->stdrsp);
+ writel(0, &dev->regs->pciirqenb0);
+ writel(0, &dev->regs->pciirqenb1);
+
+ /* clear old dma and irq state */
+ for (tmp = 0; tmp < 4; tmp++) {
+ struct net2280_ep *ep = &dev->ep[tmp + 1];
+ if (ep->dma)
+ abort_dma(ep);
+ }
+
+ writel(~0, &dev->regs->irqstat0),
+ writel(~(u32)BIT(SUSPEND_REQUEST_INTERRUPT), &dev->regs->irqstat1),
+
+ /* reset, and enable pci */
+ tmp = readl(&dev->regs->devinit) |
+ BIT(PCI_ENABLE) |
+ BIT(FIFO_SOFT_RESET) |
+ BIT(USB_SOFT_RESET) |
+ BIT(M8051_RESET);
+ writel(tmp, &dev->regs->devinit);
+
+ /* standard fifo and endpoint allocations */
+ set_fifo_mode(dev, (fifo_mode <= 2) ? fifo_mode : 0);
+}
+
+static void usb_reset_338x(struct net2280 *dev)
+{
+ u32 tmp;
+ u32 fsmvalue;
+
+ dev->gadget.speed = USB_SPEED_UNKNOWN;
+ (void)readl(&dev->usb->usbctl);
+
+ net2280_led_init(dev);
+
+ fsmvalue = get_idx_reg(dev->regs, SCRATCH) &
+ (0xf << DEFECT7374_FSM_FIELD);
+
+ /* See if firmware needs to set up for workaround: */
+ if (fsmvalue != DEFECT7374_FSM_SS_CONTROL_READ) {
+ ep_info(dev, "%s: Defect 7374 FsmValue 0x%08x\n", __func__,
+ fsmvalue);
+ } else {
+ /* disable automatic responses, and irqs */
+ writel(0, &dev->usb->stdrsp);
+ writel(0, &dev->regs->pciirqenb0);
+ writel(0, &dev->regs->pciirqenb1);
+ }
+
+ /* clear old dma and irq state */
+ for (tmp = 0; tmp < 4; tmp++) {
+ struct net2280_ep *ep = &dev->ep[tmp + 1];
+
+ if (ep->dma)
+ abort_dma(ep);
+ }
+
+ writel(~0, &dev->regs->irqstat0), writel(~0, &dev->regs->irqstat1);
+
+ if (fsmvalue == DEFECT7374_FSM_SS_CONTROL_READ) {
+ /* reset, and enable pci */
+ tmp = readl(&dev->regs->devinit) |
+ BIT(PCI_ENABLE) |
+ BIT(FIFO_SOFT_RESET) |
+ BIT(USB_SOFT_RESET) |
+ BIT(M8051_RESET);
+
+ writel(tmp, &dev->regs->devinit);
+ }
+
+ /* always ep-{1,2,3,4} ... maybe not ep-3 or ep-4 */
+ INIT_LIST_HEAD(&dev->gadget.ep_list);
+
+ for (tmp = 1; tmp < dev->n_ep; tmp++)
+ list_add_tail(&dev->ep[tmp].ep.ep_list, &dev->gadget.ep_list);
+
+}
+
+static void usb_reset(struct net2280 *dev)
+{
+ if (dev->quirks & PLX_LEGACY)
+ return usb_reset_228x(dev);
+ return usb_reset_338x(dev);
+}
+
+static void usb_reinit_228x(struct net2280 *dev)
+{
+ u32 tmp;
+ int init_dma;
+
+ /* use_dma changes are ignored till next device re-init */
+ init_dma = use_dma;
+
+ /* basic endpoint init */
+ for (tmp = 0; tmp < 7; tmp++) {
+ struct net2280_ep *ep = &dev->ep[tmp];
+
+ ep->ep.name = ep_name[tmp];
+ ep->dev = dev;
+ ep->num = tmp;
+
+ if (tmp > 0 && tmp <= 4) {
+ ep->fifo_size = 1024;
+ if (init_dma)
+ ep->dma = &dev->dma[tmp - 1];
+ } else
+ ep->fifo_size = 64;
+ ep->regs = &dev->epregs[tmp];
+ ep->cfg = &dev->epregs[tmp];
+ ep_reset_228x(dev->regs, ep);
+ }
+ usb_ep_set_maxpacket_limit(&dev->ep[0].ep, 64);
+ usb_ep_set_maxpacket_limit(&dev->ep[5].ep, 64);
+ usb_ep_set_maxpacket_limit(&dev->ep[6].ep, 64);
+
+ dev->gadget.ep0 = &dev->ep[0].ep;
+ dev->ep[0].stopped = 0;
+ INIT_LIST_HEAD(&dev->gadget.ep0->ep_list);
+
+ /* we want to prevent lowlevel/insecure access from the USB host,
+ * but erratum 0119 means this enable bit is ignored
+ */
+ for (tmp = 0; tmp < 5; tmp++)
+ writel(EP_DONTUSE, &dev->dep[tmp].dep_cfg);
+}
+
+static void usb_reinit_338x(struct net2280 *dev)
+{
+ int init_dma;
+ int i;
+ u32 tmp, val;
+ u32 fsmvalue;
+ static const u32 ne[9] = { 0, 1, 2, 3, 4, 1, 2, 3, 4 };
+ static const u32 ep_reg_addr[9] = { 0x00, 0xC0, 0x00, 0xC0, 0x00,
+ 0x00, 0xC0, 0x00, 0xC0 };
+
+ /* use_dma changes are ignored till next device re-init */
+ init_dma = use_dma;
+
+ /* basic endpoint init */
+ for (i = 0; i < dev->n_ep; i++) {
+ struct net2280_ep *ep = &dev->ep[i];
+
+ ep->ep.name = ep_name[i];
+ ep->dev = dev;
+ ep->num = i;
+
+ if (i > 0 && i <= 4 && init_dma)
+ ep->dma = &dev->dma[i - 1];
+
+ if (dev->enhanced_mode) {
+ ep->cfg = &dev->epregs[ne[i]];
+ ep->regs = (struct net2280_ep_regs __iomem *)
+ (((void __iomem *)&dev->epregs[ne[i]]) +
+ ep_reg_addr[i]);
+ ep->fiforegs = &dev->fiforegs[i];
+ } else {
+ ep->cfg = &dev->epregs[i];
+ ep->regs = &dev->epregs[i];
+ ep->fiforegs = &dev->fiforegs[i];
+ }
+
+ ep->fifo_size = (i != 0) ? 2048 : 512;
+
+ ep_reset_338x(dev->regs, ep);
+ }
+ usb_ep_set_maxpacket_limit(&dev->ep[0].ep, 512);
+
+ dev->gadget.ep0 = &dev->ep[0].ep;
+ dev->ep[0].stopped = 0;
+
+ /* Link layer set up */
+ fsmvalue = get_idx_reg(dev->regs, SCRATCH) &
+ (0xf << DEFECT7374_FSM_FIELD);
+
+ /* See if driver needs to set up for workaround: */
+ if (fsmvalue != DEFECT7374_FSM_SS_CONTROL_READ)
+ ep_info(dev, "%s: Defect 7374 FsmValue %08x\n",
+ __func__, fsmvalue);
+ else {
+ tmp = readl(&dev->usb_ext->usbctl2) &
+ ~(BIT(U1_ENABLE) | BIT(U2_ENABLE) | BIT(LTM_ENABLE));
+ writel(tmp, &dev->usb_ext->usbctl2);
+ }
+
+ /* Hardware Defect and Workaround */
+ val = readl(&dev->ll_lfps_regs->ll_lfps_5);
+ val &= ~(0xf << TIMER_LFPS_6US);
+ val |= 0x5 << TIMER_LFPS_6US;
+ writel(val, &dev->ll_lfps_regs->ll_lfps_5);
+
+ val = readl(&dev->ll_lfps_regs->ll_lfps_6);
+ val &= ~(0xffff << TIMER_LFPS_80US);
+ val |= 0x0100 << TIMER_LFPS_80US;
+ writel(val, &dev->ll_lfps_regs->ll_lfps_6);
+
+ /*
+ * AA_AB Errata. Issue 4. Workaround for SuperSpeed USB
+ * Hot Reset Exit Handshake may Fail in Specific Case using
+ * Default Register Settings. Workaround for Enumeration test.
+ */
+ val = readl(&dev->ll_tsn_regs->ll_tsn_counters_2);
+ val &= ~(0x1f << HOT_TX_NORESET_TS2);
+ val |= 0x10 << HOT_TX_NORESET_TS2;
+ writel(val, &dev->ll_tsn_regs->ll_tsn_counters_2);
+
+ val = readl(&dev->ll_tsn_regs->ll_tsn_counters_3);
+ val &= ~(0x1f << HOT_RX_RESET_TS2);
+ val |= 0x3 << HOT_RX_RESET_TS2;
+ writel(val, &dev->ll_tsn_regs->ll_tsn_counters_3);
+
+ /*
+ * Set Recovery Idle to Recover bit:
+ * - On SS connections, setting Recovery Idle to Recover Fmw improves
+ * link robustness with various hosts and hubs.
+ * - It is safe to set for all connection speeds; all chip revisions.
+ * - R-M-W to leave other bits undisturbed.
+ * - Reference PLX TT-7372
+ */
+ val = readl(&dev->ll_chicken_reg->ll_tsn_chicken_bit);
+ val |= BIT(RECOVERY_IDLE_TO_RECOVER_FMW);
+ writel(val, &dev->ll_chicken_reg->ll_tsn_chicken_bit);
+
+ INIT_LIST_HEAD(&dev->gadget.ep0->ep_list);
+
+ /* disable dedicated endpoints */
+ writel(0x0D, &dev->dep[0].dep_cfg);
+ writel(0x0D, &dev->dep[1].dep_cfg);
+ writel(0x0E, &dev->dep[2].dep_cfg);
+ writel(0x0E, &dev->dep[3].dep_cfg);
+ writel(0x0F, &dev->dep[4].dep_cfg);
+ writel(0x0C, &dev->dep[5].dep_cfg);
+}
+
+static void usb_reinit(struct net2280 *dev)
+{
+ if (dev->quirks & PLX_LEGACY)
+ return usb_reinit_228x(dev);
+ return usb_reinit_338x(dev);
+}
+
+static void ep0_start_228x(struct net2280 *dev)
+{
+ writel(BIT(CLEAR_EP_HIDE_STATUS_PHASE) |
+ BIT(CLEAR_NAK_OUT_PACKETS) |
+ BIT(CLEAR_CONTROL_STATUS_PHASE_HANDSHAKE),
+ &dev->epregs[0].ep_rsp);
+
+ /*
+ * hardware optionally handles a bunch of standard requests
+ * that the API hides from drivers anyway. have it do so.
+ * endpoint status/features are handled in software, to
+ * help pass tests for some dubious behavior.
+ */
+ writel(BIT(SET_TEST_MODE) |
+ BIT(SET_ADDRESS) |
+ BIT(DEVICE_SET_CLEAR_DEVICE_REMOTE_WAKEUP) |
+ BIT(GET_DEVICE_STATUS) |
+ BIT(GET_INTERFACE_STATUS),
+ &dev->usb->stdrsp);
+ writel(BIT(USB_ROOT_PORT_WAKEUP_ENABLE) |
+ BIT(SELF_POWERED_USB_DEVICE) |
+ BIT(REMOTE_WAKEUP_SUPPORT) |
+ (dev->softconnect << USB_DETECT_ENABLE) |
+ BIT(SELF_POWERED_STATUS),
+ &dev->usb->usbctl);
+
+ /* enable irqs so we can see ep0 and general operation */
+ writel(BIT(SETUP_PACKET_INTERRUPT_ENABLE) |
+ BIT(ENDPOINT_0_INTERRUPT_ENABLE),
+ &dev->regs->pciirqenb0);
+ writel(BIT(PCI_INTERRUPT_ENABLE) |
+ BIT(PCI_MASTER_ABORT_RECEIVED_INTERRUPT_ENABLE) |
+ BIT(PCI_TARGET_ABORT_RECEIVED_INTERRUPT_ENABLE) |
+ BIT(PCI_RETRY_ABORT_INTERRUPT_ENABLE) |
+ BIT(VBUS_INTERRUPT_ENABLE) |
+ BIT(ROOT_PORT_RESET_INTERRUPT_ENABLE) |
+ BIT(SUSPEND_REQUEST_CHANGE_INTERRUPT_ENABLE),
+ &dev->regs->pciirqenb1);
+
+ /* don't leave any writes posted */
+ (void) readl(&dev->usb->usbctl);
+}
+
+static void ep0_start_338x(struct net2280 *dev)
+{
+ u32 fsmvalue;
+
+ fsmvalue = get_idx_reg(dev->regs, SCRATCH) &
+ (0xf << DEFECT7374_FSM_FIELD);
+
+ if (fsmvalue != DEFECT7374_FSM_SS_CONTROL_READ)
+ ep_info(dev, "%s: Defect 7374 FsmValue %08x\n", __func__,
+ fsmvalue);
+ else
+ writel(BIT(CLEAR_NAK_OUT_PACKETS_MODE) |
+ BIT(SET_EP_HIDE_STATUS_PHASE),
+ &dev->epregs[0].ep_rsp);
+
+ /*
+ * hardware optionally handles a bunch of standard requests
+ * that the API hides from drivers anyway. have it do so.
+ * endpoint status/features are handled in software, to
+ * help pass tests for some dubious behavior.
+ */
+ writel(BIT(SET_ISOCHRONOUS_DELAY) |
+ BIT(SET_SEL) |
+ BIT(SET_TEST_MODE) |
+ BIT(SET_ADDRESS) |
+ BIT(GET_INTERFACE_STATUS) |
+ BIT(GET_DEVICE_STATUS),
+ &dev->usb->stdrsp);
+ dev->wakeup_enable = 1;
+ writel(BIT(USB_ROOT_PORT_WAKEUP_ENABLE) |
+ (dev->softconnect << USB_DETECT_ENABLE) |
+ BIT(DEVICE_REMOTE_WAKEUP_ENABLE),
+ &dev->usb->usbctl);
+
+ /* enable irqs so we can see ep0 and general operation */
+ writel(BIT(SETUP_PACKET_INTERRUPT_ENABLE) |
+ BIT(ENDPOINT_0_INTERRUPT_ENABLE),
+ &dev->regs->pciirqenb0);
+ writel(BIT(PCI_INTERRUPT_ENABLE) |
+ BIT(ROOT_PORT_RESET_INTERRUPT_ENABLE) |
+ BIT(SUSPEND_REQUEST_CHANGE_INTERRUPT_ENABLE) |
+ BIT(VBUS_INTERRUPT_ENABLE),
+ &dev->regs->pciirqenb1);
+
+ /* don't leave any writes posted */
+ (void)readl(&dev->usb->usbctl);
+}
+
+static void ep0_start(struct net2280 *dev)
+{
+ if (dev->quirks & PLX_LEGACY)
+ return ep0_start_228x(dev);
+ return ep0_start_338x(dev);
+}
+
+/* when a driver is successfully registered, it will receive
+ * control requests including set_configuration(), which enables
+ * non-control requests. then usb traffic follows until a
+ * disconnect is reported. then a host may connect again, or
+ * the driver might get unbound.
+ */
+static int net2280_start(struct usb_gadget *_gadget,
+ struct usb_gadget_driver *driver)
+{
+ struct net2280 *dev;
+ int retval;
+ unsigned i;
+
+ /* insist on high speed support from the driver, since
+ * (dev->usb->xcvrdiag & FORCE_FULL_SPEED_MODE)
+ * "must not be used in normal operation"
+ */
+ if (!driver || driver->max_speed < USB_SPEED_HIGH ||
+ !driver->setup)
+ return -EINVAL;
+
+ dev = container_of(_gadget, struct net2280, gadget);
+
+ for (i = 0; i < dev->n_ep; i++)
+ dev->ep[i].irqs = 0;
+
+ /* hook up the driver ... */
+ dev->softconnect = 1;
+ driver->driver.bus = NULL;
+ dev->driver = driver;
+
+ retval = device_create_file(&dev->pdev->dev, &dev_attr_function);
+ if (retval)
+ goto err_unbind;
+ retval = device_create_file(&dev->pdev->dev, &dev_attr_queues);
+ if (retval)
+ goto err_func;
+
+ /* Enable force-full-speed testing mode, if desired */
+ if (full_speed && (dev->quirks & PLX_LEGACY))
+ writel(BIT(FORCE_FULL_SPEED_MODE), &dev->usb->xcvrdiag);
+
+ /* ... then enable host detection and ep0; and we're ready
+ * for set_configuration as well as eventual disconnect.
+ */
+ net2280_led_active(dev, 1);
+
+ if (dev->quirks & PLX_SUPERSPEED)
+ defect7374_enable_data_eps_zero(dev);
+
+ ep0_start(dev);
+
+ ep_dbg(dev, "%s ready, usbctl %08x stdrsp %08x\n",
+ driver->driver.name,
+ readl(&dev->usb->usbctl),
+ readl(&dev->usb->stdrsp));
+
+ /* pci writes may still be posted */
+ return 0;
+
+err_func:
+ device_remove_file(&dev->pdev->dev, &dev_attr_function);
+err_unbind:
+ dev->driver = NULL;
+ return retval;
+}
+
+static void stop_activity(struct net2280 *dev, struct usb_gadget_driver *driver)
+{
+ int i;
+
+ /* don't disconnect if it's not connected */
+ if (dev->gadget.speed == USB_SPEED_UNKNOWN)
+ driver = NULL;
+
+ /* stop hardware; prevent new request submissions;
+ * and kill any outstanding requests.
+ */
+ usb_reset(dev);
+ for (i = 0; i < dev->n_ep; i++)
+ nuke(&dev->ep[i]);
+
+ /* report disconnect; the driver is already quiesced */
+ if (driver) {
+ spin_unlock(&dev->lock);
+ driver->disconnect(&dev->gadget);
+ spin_lock(&dev->lock);
+ }
+
+ usb_reinit(dev);
+}
+
+static int net2280_stop(struct usb_gadget *_gadget,
+ struct usb_gadget_driver *driver)
+{
+ struct net2280 *dev;
+ unsigned long flags;
+
+ dev = container_of(_gadget, struct net2280, gadget);
+
+ spin_lock_irqsave(&dev->lock, flags);
+ stop_activity(dev, driver);
+ spin_unlock_irqrestore(&dev->lock, flags);
+
+ dev->driver = NULL;
+
+ net2280_led_active(dev, 0);
+
+ /* Disable full-speed test mode */
+ if (dev->quirks & PLX_LEGACY)
+ writel(0, &dev->usb->xcvrdiag);
+
+ device_remove_file(&dev->pdev->dev, &dev_attr_function);
+ device_remove_file(&dev->pdev->dev, &dev_attr_queues);
+
+ ep_dbg(dev, "unregistered driver '%s'\n",
+ driver ? driver->driver.name : "");
+
+ return 0;
+}
+
+/*-------------------------------------------------------------------------*/
+
+/* handle ep0, ep-e, ep-f with 64 byte packets: packet per irq.
+ * also works for dma-capable endpoints, in pio mode or just
+ * to manually advance the queue after short OUT transfers.
+ */
+static void handle_ep_small(struct net2280_ep *ep)
+{
+ struct net2280_request *req;
+ u32 t;
+ /* 0 error, 1 mid-data, 2 done */
+ int mode = 1;
+
+ if (!list_empty(&ep->queue))
+ req = list_entry(ep->queue.next,
+ struct net2280_request, queue);
+ else
+ req = NULL;
+
+ /* ack all, and handle what we care about */
+ t = readl(&ep->regs->ep_stat);
+ ep->irqs++;
+#if 0
+ ep_vdbg(ep->dev, "%s ack ep_stat %08x, req %p\n",
+ ep->ep.name, t, req ? &req->req : 0);
+#endif
+ if (!ep->is_in || (ep->dev->quirks & PLX_2280))
+ writel(t & ~BIT(NAK_OUT_PACKETS), &ep->regs->ep_stat);
+ else
+ /* Added for 2282 */
+ writel(t, &ep->regs->ep_stat);
+
+ /* for ep0, monitor token irqs to catch data stage length errors
+ * and to synchronize on status.
+ *
+ * also, to defer reporting of protocol stalls ... here's where
+ * data or status first appears, handling stalls here should never
+ * cause trouble on the host side..
+ *
+ * control requests could be slightly faster without token synch for
+ * status, but status can jam up that way.
+ */
+ if (unlikely(ep->num == 0)) {
+ if (ep->is_in) {
+ /* status; stop NAKing */
+ if (t & BIT(DATA_OUT_PING_TOKEN_INTERRUPT)) {
+ if (ep->dev->protocol_stall) {
+ ep->stopped = 1;
+ set_halt(ep);
+ }
+ if (!req)
+ allow_status(ep);
+ mode = 2;
+ /* reply to extra IN data tokens with a zlp */
+ } else if (t & BIT(DATA_IN_TOKEN_INTERRUPT)) {
+ if (ep->dev->protocol_stall) {
+ ep->stopped = 1;
+ set_halt(ep);
+ mode = 2;
+ } else if (ep->responded &&
+ !req && !ep->stopped)
+ write_fifo(ep, NULL);
+ }
+ } else {
+ /* status; stop NAKing */
+ if (t & BIT(DATA_IN_TOKEN_INTERRUPT)) {
+ if (ep->dev->protocol_stall) {
+ ep->stopped = 1;
+ set_halt(ep);
+ }
+ mode = 2;
+ /* an extra OUT token is an error */
+ } else if (((t & BIT(DATA_OUT_PING_TOKEN_INTERRUPT)) &&
+ req &&
+ req->req.actual == req->req.length) ||
+ (ep->responded && !req)) {
+ ep->dev->protocol_stall = 1;
+ set_halt(ep);
+ ep->stopped = 1;
+ if (req)
+ done(ep, req, -EOVERFLOW);
+ req = NULL;
+ }
+ }
+ }
+
+ if (unlikely(!req))
+ return;
+
+ /* manual DMA queue advance after short OUT */
+ if (likely(ep->dma)) {
+ if (t & BIT(SHORT_PACKET_TRANSFERRED_INTERRUPT)) {
+ u32 count;
+ int stopped = ep->stopped;
+
+ /* TRANSFERRED works around OUT_DONE erratum 0112.
+ * we expect (N <= maxpacket) bytes; host wrote M.
+ * iff (M < N) we won't ever see a DMA interrupt.
+ */
+ ep->stopped = 1;
+ for (count = 0; ; t = readl(&ep->regs->ep_stat)) {
+
+ /* any preceding dma transfers must finish.
+ * dma handles (M >= N), may empty the queue
+ */
+ scan_dma_completions(ep);
+ if (unlikely(list_empty(&ep->queue) ||
+ ep->out_overflow)) {
+ req = NULL;
+ break;
+ }
+ req = list_entry(ep->queue.next,
+ struct net2280_request, queue);
+
+ /* here either (M < N), a "real" short rx;
+ * or (M == N) and the queue didn't empty
+ */
+ if (likely(t & BIT(FIFO_EMPTY))) {
+ count = readl(&ep->dma->dmacount);
+ count &= DMA_BYTE_COUNT_MASK;
+ if (readl(&ep->dma->dmadesc)
+ != req->td_dma)
+ req = NULL;
+ break;
+ }
+ udelay(1);
+ }
+
+ /* stop DMA, leave ep NAKing */
+ writel(BIT(DMA_ABORT), &ep->dma->dmastat);
+ spin_stop_dma(ep->dma);
+
+ if (likely(req)) {
+ req->td->dmacount = 0;
+ t = readl(&ep->regs->ep_avail);
+ dma_done(ep, req, count,
+ (ep->out_overflow || t)
+ ? -EOVERFLOW : 0);
+ }
+
+ /* also flush to prevent erratum 0106 trouble */
+ if (unlikely(ep->out_overflow ||
+ (ep->dev->chiprev == 0x0100 &&
+ ep->dev->gadget.speed
+ == USB_SPEED_FULL))) {
+ out_flush(ep);
+ ep->out_overflow = 0;
+ }
+
+ /* (re)start dma if needed, stop NAKing */
+ ep->stopped = stopped;
+ if (!list_empty(&ep->queue))
+ restart_dma(ep);
+ } else
+ ep_dbg(ep->dev, "%s dma ep_stat %08x ??\n",
+ ep->ep.name, t);
+ return;
+
+ /* data packet(s) received (in the fifo, OUT) */
+ } else if (t & BIT(DATA_PACKET_RECEIVED_INTERRUPT)) {
+ if (read_fifo(ep, req) && ep->num != 0)
+ mode = 2;
+
+ /* data packet(s) transmitted (IN) */
+ } else if (t & BIT(DATA_PACKET_TRANSMITTED_INTERRUPT)) {
+ unsigned len;
+
+ len = req->req.length - req->req.actual;
+ if (len > ep->ep.maxpacket)
+ len = ep->ep.maxpacket;
+ req->req.actual += len;
+
+ /* if we wrote it all, we're usually done */
+ /* send zlps until the status stage */
+ if ((req->req.actual == req->req.length) &&
+ (!req->req.zero || len != ep->ep.maxpacket) && ep->num)
+ mode = 2;
+
+ /* there was nothing to do ... */
+ } else if (mode == 1)
+ return;
+
+ /* done */
+ if (mode == 2) {
+ /* stream endpoints often resubmit/unlink in completion */
+ done(ep, req, 0);
+
+ /* maybe advance queue to next request */
+ if (ep->num == 0) {
+ /* NOTE: net2280 could let gadget driver start the
+ * status stage later. since not all controllers let
+ * them control that, the api doesn't (yet) allow it.
+ */
+ if (!ep->stopped)
+ allow_status(ep);
+ req = NULL;
+ } else {
+ if (!list_empty(&ep->queue) && !ep->stopped)
+ req = list_entry(ep->queue.next,
+ struct net2280_request, queue);
+ else
+ req = NULL;
+ if (req && !ep->is_in)
+ stop_out_naking(ep);
+ }
+ }
+
+ /* is there a buffer for the next packet?
+ * for best streaming performance, make sure there is one.
+ */
+ if (req && !ep->stopped) {
+
+ /* load IN fifo with next packet (may be zlp) */
+ if (t & BIT(DATA_PACKET_TRANSMITTED_INTERRUPT))
+ write_fifo(ep, &req->req);
+ }
+}
+
+static struct net2280_ep *get_ep_by_addr(struct net2280 *dev, u16 wIndex)
+{
+ struct net2280_ep *ep;
+
+ if ((wIndex & USB_ENDPOINT_NUMBER_MASK) == 0)
+ return &dev->ep[0];
+ list_for_each_entry(ep, &dev->gadget.ep_list, ep.ep_list) {
+ u8 bEndpointAddress;
+
+ if (!ep->desc)
+ continue;
+ bEndpointAddress = ep->desc->bEndpointAddress;
+ if ((wIndex ^ bEndpointAddress) & USB_DIR_IN)
+ continue;
+ if ((wIndex & 0x0f) == (bEndpointAddress & 0x0f))
+ return ep;
+ }
+ return NULL;
+}
+
+static void defect7374_workaround(struct net2280 *dev, struct usb_ctrlrequest r)
+{
+ u32 scratch, fsmvalue;
+ u32 ack_wait_timeout, state;
+
+ /* Workaround for Defect 7374 (U1/U2 erroneously rejected): */
+ scratch = get_idx_reg(dev->regs, SCRATCH);
+ fsmvalue = scratch & (0xf << DEFECT7374_FSM_FIELD);
+ scratch &= ~(0xf << DEFECT7374_FSM_FIELD);
+
+ if (!((fsmvalue == DEFECT7374_FSM_WAITING_FOR_CONTROL_READ) &&
+ (r.bRequestType & USB_DIR_IN)))
+ return;
+
+ /* This is the first Control Read for this connection: */
+ if (!(readl(&dev->usb->usbstat) & BIT(SUPER_SPEED_MODE))) {
+ /*
+ * Connection is NOT SS:
+ * - Connection must be FS or HS.
+ * - This FSM state should allow workaround software to
+ * run after the next USB connection.
+ */
+ scratch |= DEFECT7374_FSM_NON_SS_CONTROL_READ;
+ goto restore_data_eps;
+ }
+
+ /* Connection is SS: */
+ for (ack_wait_timeout = 0;
+ ack_wait_timeout < DEFECT_7374_NUMBEROF_MAX_WAIT_LOOPS;
+ ack_wait_timeout++) {
+
+ state = readl(&dev->plregs->pl_ep_status_1)
+ & (0xff << STATE);
+ if ((state >= (ACK_GOOD_NORMAL << STATE)) &&
+ (state <= (ACK_GOOD_MORE_ACKS_TO_COME << STATE))) {
+ scratch |= DEFECT7374_FSM_SS_CONTROL_READ;
+ break;
+ }
+
+ /*
+ * We have not yet received host's Data Phase ACK
+ * - Wait and try again.
+ */
+ udelay(DEFECT_7374_PROCESSOR_WAIT_TIME);
+
+ continue;
+ }
+
+
+ if (ack_wait_timeout >= DEFECT_7374_NUMBEROF_MAX_WAIT_LOOPS) {
+ ep_err(dev, "FAIL: Defect 7374 workaround waited but failed "
+ "to detect SS host's data phase ACK.");
+ ep_err(dev, "PL_EP_STATUS_1(23:16):.Expected from 0x11 to 0x16"
+ "got 0x%2.2x.\n", state >> STATE);
+ } else {
+ ep_warn(dev, "INFO: Defect 7374 workaround waited about\n"
+ "%duSec for Control Read Data Phase ACK\n",
+ DEFECT_7374_PROCESSOR_WAIT_TIME * ack_wait_timeout);
+ }
+
+restore_data_eps:
+ /*
+ * Restore data EPs to their pre-workaround settings (disabled,
+ * initialized, and other details).
+ */
+ defect7374_disable_data_eps(dev);
+
+ set_idx_reg(dev->regs, SCRATCH, scratch);
+
+ return;
+}
+
+static void ep_stall(struct net2280_ep *ep, int stall)
+{
+ struct net2280 *dev = ep->dev;
+ u32 val;
+ static const u32 ep_pl[9] = { 0, 3, 4, 7, 8, 2, 5, 6, 9 };
+
+ if (stall) {
+ writel(BIT(SET_ENDPOINT_HALT) |
+ /* BIT(SET_NAK_PACKETS) | */
+ BIT(CLEAR_CONTROL_STATUS_PHASE_HANDSHAKE),
+ &ep->regs->ep_rsp);
+ ep->is_halt = 1;
+ } else {
+ if (dev->gadget.speed == USB_SPEED_SUPER) {
+ /*
+ * Workaround for SS SeqNum not cleared via
+ * Endpoint Halt (Clear) bit. select endpoint
+ */
+ val = readl(&dev->plregs->pl_ep_ctrl);
+ val = (val & ~0x1f) | ep_pl[ep->num];
+ writel(val, &dev->plregs->pl_ep_ctrl);
+
+ val |= BIT(SEQUENCE_NUMBER_RESET);
+ writel(val, &dev->plregs->pl_ep_ctrl);
+ }
+ val = readl(&ep->regs->ep_rsp);
+ val |= BIT(CLEAR_ENDPOINT_HALT) |
+ BIT(CLEAR_ENDPOINT_TOGGLE);
+ writel(val,
+ /* | BIT(CLEAR_NAK_PACKETS),*/
+ &ep->regs->ep_rsp);
+ ep->is_halt = 0;
+ val = readl(&ep->regs->ep_rsp);
+ }
+}
+
+static void ep_stdrsp(struct net2280_ep *ep, int value, int wedged)
+{
+ /* set/clear, then synch memory views with the device */
+ if (value) {
+ ep->stopped = 1;
+ if (ep->num == 0)
+ ep->dev->protocol_stall = 1;
+ else {
+ if (ep->dma)
+ ep_stop_dma(ep);
+ ep_stall(ep, true);
+ }
+
+ if (wedged)
+ ep->wedged = 1;
+ } else {
+ ep->stopped = 0;
+ ep->wedged = 0;
+
+ ep_stall(ep, false);
+
+ /* Flush the queue */
+ if (!list_empty(&ep->queue)) {
+ struct net2280_request *req =
+ list_entry(ep->queue.next, struct net2280_request,
+ queue);
+ if (ep->dma)
+ resume_dma(ep);
+ else {
+ if (ep->is_in)
+ write_fifo(ep, &req->req);
+ else {
+ if (read_fifo(ep, req))
+ done(ep, req, 0);
+ }
+ }
+ }
+ }
+}
+
+static void handle_stat0_irqs_superspeed(struct net2280 *dev,
+ struct net2280_ep *ep, struct usb_ctrlrequest r)
+{
+ int tmp = 0;
+
+#define w_value le16_to_cpu(r.wValue)
+#define w_index le16_to_cpu(r.wIndex)
+#define w_length le16_to_cpu(r.wLength)
+
+ switch (r.bRequest) {
+ struct net2280_ep *e;
+ u16 status;
+
+ case USB_REQ_SET_CONFIGURATION:
+ dev->addressed_state = !w_value;
+ goto usb3_delegate;
+
+ case USB_REQ_GET_STATUS:
+ switch (r.bRequestType) {
+ case (USB_DIR_IN | USB_TYPE_STANDARD | USB_RECIP_DEVICE):
+ status = dev->wakeup_enable ? 0x02 : 0x00;
+ if (dev->selfpowered)
+ status |= BIT(0);
+ status |= (dev->u1_enable << 2 | dev->u2_enable << 3 |
+ dev->ltm_enable << 4);
+ writel(0, &dev->epregs[0].ep_irqenb);
+ set_fifo_bytecount(ep, sizeof(status));
+ writel((__force u32) status, &dev->epregs[0].ep_data);
+ allow_status_338x(ep);
+ break;
+
+ case (USB_DIR_IN | USB_TYPE_STANDARD | USB_RECIP_ENDPOINT):
+ e = get_ep_by_addr(dev, w_index);
+ if (!e)
+ goto do_stall3;
+ status = readl(&e->regs->ep_rsp) &
+ BIT(CLEAR_ENDPOINT_HALT);
+ writel(0, &dev->epregs[0].ep_irqenb);
+ set_fifo_bytecount(ep, sizeof(status));
+ writel((__force u32) status, &dev->epregs[0].ep_data);
+ allow_status_338x(ep);
+ break;
+
+ default:
+ goto usb3_delegate;
+ }
+ break;
+
+ case USB_REQ_CLEAR_FEATURE:
+ switch (r.bRequestType) {
+ case (USB_DIR_OUT | USB_TYPE_STANDARD | USB_RECIP_DEVICE):
+ if (!dev->addressed_state) {
+ switch (w_value) {
+ case USB_DEVICE_U1_ENABLE:
+ dev->u1_enable = 0;
+ writel(readl(&dev->usb_ext->usbctl2) &
+ ~BIT(U1_ENABLE),
+ &dev->usb_ext->usbctl2);
+ allow_status_338x(ep);
+ goto next_endpoints3;
+
+ case USB_DEVICE_U2_ENABLE:
+ dev->u2_enable = 0;
+ writel(readl(&dev->usb_ext->usbctl2) &
+ ~BIT(U2_ENABLE),
+ &dev->usb_ext->usbctl2);
+ allow_status_338x(ep);
+ goto next_endpoints3;
+
+ case USB_DEVICE_LTM_ENABLE:
+ dev->ltm_enable = 0;
+ writel(readl(&dev->usb_ext->usbctl2) &
+ ~BIT(LTM_ENABLE),
+ &dev->usb_ext->usbctl2);
+ allow_status_338x(ep);
+ goto next_endpoints3;
+
+ default:
+ break;
+ }
+ }
+ if (w_value == USB_DEVICE_REMOTE_WAKEUP) {
+ dev->wakeup_enable = 0;
+ writel(readl(&dev->usb->usbctl) &
+ ~BIT(DEVICE_REMOTE_WAKEUP_ENABLE),
+ &dev->usb->usbctl);
+ allow_status_338x(ep);
+ break;
+ }
+ goto usb3_delegate;
+
+ case (USB_DIR_OUT | USB_TYPE_STANDARD | USB_RECIP_ENDPOINT):
+ e = get_ep_by_addr(dev, w_index);
+ if (!e)
+ goto do_stall3;
+ if (w_value != USB_ENDPOINT_HALT)
+ goto do_stall3;
+ ep_vdbg(dev, "%s clear halt\n", e->ep.name);
+ ep_stall(e, false);
+ if (!list_empty(&e->queue) && e->td_dma)
+ restart_dma(e);
+ allow_status(ep);
+ ep->stopped = 1;
+ break;
+
+ default:
+ goto usb3_delegate;
+ }
+ break;
+ case USB_REQ_SET_FEATURE:
+ switch (r.bRequestType) {
+ case (USB_DIR_OUT | USB_TYPE_STANDARD | USB_RECIP_DEVICE):
+ if (!dev->addressed_state) {
+ switch (w_value) {
+ case USB_DEVICE_U1_ENABLE:
+ dev->u1_enable = 1;
+ writel(readl(&dev->usb_ext->usbctl2) |
+ BIT(U1_ENABLE),
+ &dev->usb_ext->usbctl2);
+ allow_status_338x(ep);
+ goto next_endpoints3;
+
+ case USB_DEVICE_U2_ENABLE:
+ dev->u2_enable = 1;
+ writel(readl(&dev->usb_ext->usbctl2) |
+ BIT(U2_ENABLE),
+ &dev->usb_ext->usbctl2);
+ allow_status_338x(ep);
+ goto next_endpoints3;
+
+ case USB_DEVICE_LTM_ENABLE:
+ dev->ltm_enable = 1;
+ writel(readl(&dev->usb_ext->usbctl2) |
+ BIT(LTM_ENABLE),
+ &dev->usb_ext->usbctl2);
+ allow_status_338x(ep);
+ goto next_endpoints3;
+ default:
+ break;
+ }
+ }
+
+ if (w_value == USB_DEVICE_REMOTE_WAKEUP) {
+ dev->wakeup_enable = 1;
+ writel(readl(&dev->usb->usbctl) |
+ BIT(DEVICE_REMOTE_WAKEUP_ENABLE),
+ &dev->usb->usbctl);
+ allow_status_338x(ep);
+ break;
+ }
+ goto usb3_delegate;
+
+ case (USB_DIR_OUT | USB_TYPE_STANDARD | USB_RECIP_ENDPOINT):
+ e = get_ep_by_addr(dev, w_index);
+ if (!e || (w_value != USB_ENDPOINT_HALT))
+ goto do_stall3;
+ ep_stdrsp(e, true, false);
+ allow_status_338x(ep);
+ break;
+
+ default:
+ goto usb3_delegate;
+ }
+
+ break;
+ default:
+
+usb3_delegate:
+ ep_vdbg(dev, "setup %02x.%02x v%04x i%04x l%04x ep_cfg %08x\n",
+ r.bRequestType, r.bRequest,
+ w_value, w_index, w_length,
+ readl(&ep->cfg->ep_cfg));
+
+ ep->responded = 0;
+ spin_unlock(&dev->lock);
+ tmp = dev->driver->setup(&dev->gadget, &r);
+ spin_lock(&dev->lock);
+ }
+do_stall3:
+ if (tmp < 0) {
+ ep_vdbg(dev, "req %02x.%02x protocol STALL; stat %d\n",
+ r.bRequestType, r.bRequest, tmp);
+ dev->protocol_stall = 1;
+ /* TD 9.9 Halt Endpoint test. TD 9.22 Set feature test */
+ ep_stall(ep, true);
+ }
+
+next_endpoints3:
+
+#undef w_value
+#undef w_index
+#undef w_length
+
+ return;
+}
+
+static void handle_stat0_irqs(struct net2280 *dev, u32 stat)
+{
+ struct net2280_ep *ep;
+ u32 num, scratch;
+
+ /* most of these don't need individual acks */
+ stat &= ~BIT(INTA_ASSERTED);
+ if (!stat)
+ return;
+ /* ep_dbg(dev, "irqstat0 %04x\n", stat); */
+
+ /* starting a control request? */
+ if (unlikely(stat & BIT(SETUP_PACKET_INTERRUPT))) {
+ union {
+ u32 raw[2];
+ struct usb_ctrlrequest r;
+ } u;
+ int tmp;
+ struct net2280_request *req;
+
+ if (dev->gadget.speed == USB_SPEED_UNKNOWN) {
+ u32 val = readl(&dev->usb->usbstat);
+ if (val & BIT(SUPER_SPEED)) {
+ dev->gadget.speed = USB_SPEED_SUPER;
+ usb_ep_set_maxpacket_limit(&dev->ep[0].ep,
+ EP0_SS_MAX_PACKET_SIZE);
+ } else if (val & BIT(HIGH_SPEED)) {
+ dev->gadget.speed = USB_SPEED_HIGH;
+ usb_ep_set_maxpacket_limit(&dev->ep[0].ep,
+ EP0_HS_MAX_PACKET_SIZE);
+ } else {
+ dev->gadget.speed = USB_SPEED_FULL;
+ usb_ep_set_maxpacket_limit(&dev->ep[0].ep,
+ EP0_HS_MAX_PACKET_SIZE);
+ }
+ net2280_led_speed(dev, dev->gadget.speed);
+ ep_dbg(dev, "%s\n",
+ usb_speed_string(dev->gadget.speed));
+ }
+
+ ep = &dev->ep[0];
+ ep->irqs++;
+
+ /* make sure any leftover request state is cleared */
+ stat &= ~BIT(ENDPOINT_0_INTERRUPT);
+ while (!list_empty(&ep->queue)) {
+ req = list_entry(ep->queue.next,
+ struct net2280_request, queue);
+ done(ep, req, (req->req.actual == req->req.length)
+ ? 0 : -EPROTO);
+ }
+ ep->stopped = 0;
+ dev->protocol_stall = 0;
+ if (dev->quirks & PLX_SUPERSPEED)
+ ep->is_halt = 0;
+ else{
+ if (ep->dev->quirks & PLX_2280)
+ tmp = BIT(FIFO_OVERFLOW) |
+ BIT(FIFO_UNDERFLOW);
+ else
+ tmp = 0;
+
+ writel(tmp | BIT(TIMEOUT) |
+ BIT(USB_STALL_SENT) |
+ BIT(USB_IN_NAK_SENT) |
+ BIT(USB_IN_ACK_RCVD) |
+ BIT(USB_OUT_PING_NAK_SENT) |
+ BIT(USB_OUT_ACK_SENT) |
+ BIT(SHORT_PACKET_OUT_DONE_INTERRUPT) |
+ BIT(SHORT_PACKET_TRANSFERRED_INTERRUPT) |
+ BIT(DATA_PACKET_RECEIVED_INTERRUPT) |
+ BIT(DATA_PACKET_TRANSMITTED_INTERRUPT) |
+ BIT(DATA_OUT_PING_TOKEN_INTERRUPT) |
+ BIT(DATA_IN_TOKEN_INTERRUPT),
+ &ep->regs->ep_stat);
+ }
+ u.raw[0] = readl(&dev->usb->setup0123);
+ u.raw[1] = readl(&dev->usb->setup4567);
+
+ cpu_to_le32s(&u.raw[0]);
+ cpu_to_le32s(&u.raw[1]);
+
+ if (dev->quirks & PLX_SUPERSPEED)
+ defect7374_workaround(dev, u.r);
+
+ tmp = 0;
+
+#define w_value le16_to_cpu(u.r.wValue)
+#define w_index le16_to_cpu(u.r.wIndex)
+#define w_length le16_to_cpu(u.r.wLength)
+
+ /* ack the irq */
+ writel(BIT(SETUP_PACKET_INTERRUPT), &dev->regs->irqstat0);
+ stat ^= BIT(SETUP_PACKET_INTERRUPT);
+
+ /* watch control traffic at the token level, and force
+ * synchronization before letting the status stage happen.
+ * FIXME ignore tokens we'll NAK, until driver responds.
+ * that'll mean a lot less irqs for some drivers.
+ */
+ ep->is_in = (u.r.bRequestType & USB_DIR_IN) != 0;
+ if (ep->is_in) {
+ scratch = BIT(DATA_PACKET_TRANSMITTED_INTERRUPT) |
+ BIT(DATA_OUT_PING_TOKEN_INTERRUPT) |
+ BIT(DATA_IN_TOKEN_INTERRUPT);
+ stop_out_naking(ep);
+ } else
+ scratch = BIT(DATA_PACKET_RECEIVED_INTERRUPT) |
+ BIT(DATA_OUT_PING_TOKEN_INTERRUPT) |
+ BIT(DATA_IN_TOKEN_INTERRUPT);
+ writel(scratch, &dev->epregs[0].ep_irqenb);
+
+ /* we made the hardware handle most lowlevel requests;
+ * everything else goes uplevel to the gadget code.
+ */
+ ep->responded = 1;
+
+ if (dev->gadget.speed == USB_SPEED_SUPER) {
+ handle_stat0_irqs_superspeed(dev, ep, u.r);
+ goto next_endpoints;
+ }
+
+ switch (u.r.bRequest) {
+ case USB_REQ_GET_STATUS: {
+ struct net2280_ep *e;
+ __le32 status;
+
+ /* hw handles device and interface status */
+ if (u.r.bRequestType != (USB_DIR_IN|USB_RECIP_ENDPOINT))
+ goto delegate;
+ e = get_ep_by_addr(dev, w_index);
+ if (!e || w_length > 2)
+ goto do_stall;
+
+ if (readl(&e->regs->ep_rsp) & BIT(SET_ENDPOINT_HALT))
+ status = cpu_to_le32(1);
+ else
+ status = cpu_to_le32(0);
+
+ /* don't bother with a request object! */
+ writel(0, &dev->epregs[0].ep_irqenb);
+ set_fifo_bytecount(ep, w_length);
+ writel((__force u32)status, &dev->epregs[0].ep_data);
+ allow_status(ep);
+ ep_vdbg(dev, "%s stat %02x\n", ep->ep.name, status);
+ goto next_endpoints;
+ }
+ break;
+ case USB_REQ_CLEAR_FEATURE: {
+ struct net2280_ep *e;
+
+ /* hw handles device features */
+ if (u.r.bRequestType != USB_RECIP_ENDPOINT)
+ goto delegate;
+ if (w_value != USB_ENDPOINT_HALT || w_length != 0)
+ goto do_stall;
+ e = get_ep_by_addr(dev, w_index);
+ if (!e)
+ goto do_stall;
+ if (e->wedged) {
+ ep_vdbg(dev, "%s wedged, halt not cleared\n",
+ ep->ep.name);
+ } else {
+ ep_vdbg(dev, "%s clear halt\n", e->ep.name);
+ clear_halt(e);
+ if ((ep->dev->quirks & PLX_SUPERSPEED) &&
+ !list_empty(&e->queue) && e->td_dma)
+ restart_dma(e);
+ }
+ allow_status(ep);
+ goto next_endpoints;
+ }
+ break;
+ case USB_REQ_SET_FEATURE: {
+ struct net2280_ep *e;
+
+ /* hw handles device features */
+ if (u.r.bRequestType != USB_RECIP_ENDPOINT)
+ goto delegate;
+ if (w_value != USB_ENDPOINT_HALT || w_length != 0)
+ goto do_stall;
+ e = get_ep_by_addr(dev, w_index);
+ if (!e)
+ goto do_stall;
+ if (e->ep.name == ep0name)
+ goto do_stall;
+ set_halt(e);
+ if ((dev->quirks & PLX_SUPERSPEED) && e->dma)
+ abort_dma(e);
+ allow_status(ep);
+ ep_vdbg(dev, "%s set halt\n", ep->ep.name);
+ goto next_endpoints;
+ }
+ break;
+ default:
+delegate:
+ ep_vdbg(dev, "setup %02x.%02x v%04x i%04x l%04x "
+ "ep_cfg %08x\n",
+ u.r.bRequestType, u.r.bRequest,
+ w_value, w_index, w_length,
+ readl(&ep->cfg->ep_cfg));
+ ep->responded = 0;
+ spin_unlock(&dev->lock);
+ tmp = dev->driver->setup(&dev->gadget, &u.r);
+ spin_lock(&dev->lock);
+ }
+
+ /* stall ep0 on error */
+ if (tmp < 0) {
+do_stall:
+ ep_vdbg(dev, "req %02x.%02x protocol STALL; stat %d\n",
+ u.r.bRequestType, u.r.bRequest, tmp);
+ dev->protocol_stall = 1;
+ }
+
+ /* some in/out token irq should follow; maybe stall then.
+ * driver must queue a request (even zlp) or halt ep0
+ * before the host times out.
+ */
+ }
+
+#undef w_value
+#undef w_index
+#undef w_length
+
+next_endpoints:
+ /* endpoint data irq ? */
+ scratch = stat & 0x7f;
+ stat &= ~0x7f;
+ for (num = 0; scratch; num++) {
+ u32 t;
+
+ /* do this endpoint's FIFO and queue need tending? */
+ t = BIT(num);
+ if ((scratch & t) == 0)
+ continue;
+ scratch ^= t;
+
+ ep = &dev->ep[num];
+ handle_ep_small(ep);
+ }
+
+ if (stat)
+ ep_dbg(dev, "unhandled irqstat0 %08x\n", stat);
+}
+
+#define DMA_INTERRUPTS (BIT(DMA_D_INTERRUPT) | \
+ BIT(DMA_C_INTERRUPT) | \
+ BIT(DMA_B_INTERRUPT) | \
+ BIT(DMA_A_INTERRUPT))
+#define PCI_ERROR_INTERRUPTS ( \
+ BIT(PCI_MASTER_ABORT_RECEIVED_INTERRUPT) | \
+ BIT(PCI_TARGET_ABORT_RECEIVED_INTERRUPT) | \
+ BIT(PCI_RETRY_ABORT_INTERRUPT))
+
+static void handle_stat1_irqs(struct net2280 *dev, u32 stat)
+{
+ struct net2280_ep *ep;
+ u32 tmp, num, mask, scratch;
+
+ /* after disconnect there's nothing else to do! */
+ tmp = BIT(VBUS_INTERRUPT) | BIT(ROOT_PORT_RESET_INTERRUPT);
+ mask = BIT(SUPER_SPEED) | BIT(HIGH_SPEED) | BIT(FULL_SPEED);
+
+ /* VBUS disconnect is indicated by VBUS_PIN and VBUS_INTERRUPT set.
+ * Root Port Reset is indicated by ROOT_PORT_RESET_INTERRUPT set and
+ * both HIGH_SPEED and FULL_SPEED clear (as ROOT_PORT_RESET_INTERRUPT
+ * only indicates a change in the reset state).
+ */
+ if (stat & tmp) {
+ writel(tmp, &dev->regs->irqstat1);
+ if ((((stat & BIT(ROOT_PORT_RESET_INTERRUPT)) &&
+ (readl(&dev->usb->usbstat) & mask)) ||
+ ((readl(&dev->usb->usbctl) &
+ BIT(VBUS_PIN)) == 0)) &&
+ (dev->gadget.speed != USB_SPEED_UNKNOWN)) {
+ ep_dbg(dev, "disconnect %s\n",
+ dev->driver->driver.name);
+ stop_activity(dev, dev->driver);
+ ep0_start(dev);
+ return;
+ }
+ stat &= ~tmp;
+
+ /* vBUS can bounce ... one of many reasons to ignore the
+ * notion of hotplug events on bus connect/disconnect!
+ */
+ if (!stat)
+ return;
+ }
+
+ /* NOTE: chip stays in PCI D0 state for now, but it could
+ * enter D1 to save more power
+ */
+ tmp = BIT(SUSPEND_REQUEST_CHANGE_INTERRUPT);
+ if (stat & tmp) {
+ writel(tmp, &dev->regs->irqstat1);
+ if (stat & BIT(SUSPEND_REQUEST_INTERRUPT)) {
+ if (dev->driver->suspend)
+ dev->driver->suspend(&dev->gadget);
+ if (!enable_suspend)
+ stat &= ~BIT(SUSPEND_REQUEST_INTERRUPT);
+ } else {
+ if (dev->driver->resume)
+ dev->driver->resume(&dev->gadget);
+ /* at high speed, note erratum 0133 */
+ }
+ stat &= ~tmp;
+ }
+
+ /* clear any other status/irqs */
+ if (stat)
+ writel(stat, &dev->regs->irqstat1);
+
+ /* some status we can just ignore */
+ if (dev->quirks & PLX_2280)
+ stat &= ~(BIT(CONTROL_STATUS_INTERRUPT) |
+ BIT(SUSPEND_REQUEST_INTERRUPT) |
+ BIT(RESUME_INTERRUPT) |
+ BIT(SOF_INTERRUPT));
+ else
+ stat &= ~(BIT(CONTROL_STATUS_INTERRUPT) |
+ BIT(RESUME_INTERRUPT) |
+ BIT(SOF_DOWN_INTERRUPT) |
+ BIT(SOF_INTERRUPT));
+
+ if (!stat)
+ return;
+ /* ep_dbg(dev, "irqstat1 %08x\n", stat);*/
+
+ /* DMA status, for ep-{a,b,c,d} */
+ scratch = stat & DMA_INTERRUPTS;
+ stat &= ~DMA_INTERRUPTS;
+ scratch >>= 9;
+ for (num = 0; scratch; num++) {
+ struct net2280_dma_regs __iomem *dma;
+
+ tmp = BIT(num);
+ if ((tmp & scratch) == 0)
+ continue;
+ scratch ^= tmp;
+
+ ep = &dev->ep[num + 1];
+ dma = ep->dma;
+
+ if (!dma)
+ continue;
+
+ /* clear ep's dma status */
+ tmp = readl(&dma->dmastat);
+ writel(tmp, &dma->dmastat);
+
+ /* dma sync*/
+ if (dev->quirks & PLX_SUPERSPEED) {
+ u32 r_dmacount = readl(&dma->dmacount);
+ if (!ep->is_in && (r_dmacount & 0x00FFFFFF) &&
+ (tmp & BIT(DMA_TRANSACTION_DONE_INTERRUPT)))
+ continue;
+ }
+
+ /* chaining should stop on abort, short OUT from fifo,
+ * or (stat0 codepath) short OUT transfer.
+ */
+ if (!use_dma_chaining) {
+ if (!(tmp & BIT(DMA_TRANSACTION_DONE_INTERRUPT))) {
+ ep_dbg(ep->dev, "%s no xact done? %08x\n",
+ ep->ep.name, tmp);
+ continue;
+ }
+ stop_dma(ep->dma);
+ }
+
+ /* OUT transfers terminate when the data from the
+ * host is in our memory. Process whatever's done.
+ * On this path, we know transfer's last packet wasn't
+ * less than req->length. NAK_OUT_PACKETS may be set,
+ * or the FIFO may already be holding new packets.
+ *
+ * IN transfers can linger in the FIFO for a very
+ * long time ... we ignore that for now, accounting
+ * precisely (like PIO does) needs per-packet irqs
+ */
+ scan_dma_completions(ep);
+
+ /* disable dma on inactive queues; else maybe restart */
+ if (list_empty(&ep->queue)) {
+ if (use_dma_chaining)
+ stop_dma(ep->dma);
+ } else {
+ tmp = readl(&dma->dmactl);
+ if (!use_dma_chaining || (tmp & BIT(DMA_ENABLE)) == 0)
+ restart_dma(ep);
+ else if (ep->is_in && use_dma_chaining) {
+ struct net2280_request *req;
+ __le32 dmacount;
+
+ /* the descriptor at the head of the chain
+ * may still have VALID_BIT clear; that's
+ * used to trigger changing DMA_FIFO_VALIDATE
+ * (affects automagic zlp writes).
+ */
+ req = list_entry(ep->queue.next,
+ struct net2280_request, queue);
+ dmacount = req->td->dmacount;
+ dmacount &= cpu_to_le32(BIT(VALID_BIT) |
+ DMA_BYTE_COUNT_MASK);
+ if (dmacount && (dmacount & valid_bit) == 0)
+ restart_dma(ep);
+ }
+ }
+ ep->irqs++;
+ }
+
+ /* NOTE: there are other PCI errors we might usefully notice.
+ * if they appear very often, here's where to try recovering.
+ */
+ if (stat & PCI_ERROR_INTERRUPTS) {
+ ep_err(dev, "pci dma error; stat %08x\n", stat);
+ stat &= ~PCI_ERROR_INTERRUPTS;
+ /* these are fatal errors, but "maybe" they won't
+ * happen again ...
+ */
+ stop_activity(dev, dev->driver);
+ ep0_start(dev);
+ stat = 0;
+ }
+
+ if (stat)
+ ep_dbg(dev, "unhandled irqstat1 %08x\n", stat);
+}
+
+static irqreturn_t net2280_irq(int irq, void *_dev)
+{
+ struct net2280 *dev = _dev;
+
+ /* shared interrupt, not ours */
+ if ((dev->quirks & PLX_LEGACY) &&
+ (!(readl(&dev->regs->irqstat0) & BIT(INTA_ASSERTED))))
+ return IRQ_NONE;
+
+ spin_lock(&dev->lock);
+
+ /* handle disconnect, dma, and more */
+ handle_stat1_irqs(dev, readl(&dev->regs->irqstat1));
+
+ /* control requests and PIO */
+ handle_stat0_irqs(dev, readl(&dev->regs->irqstat0));
+
+ if (dev->quirks & PLX_SUPERSPEED) {
+ /* re-enable interrupt to trigger any possible new interrupt */
+ u32 pciirqenb1 = readl(&dev->regs->pciirqenb1);
+ writel(pciirqenb1 & 0x7FFFFFFF, &dev->regs->pciirqenb1);
+ writel(pciirqenb1, &dev->regs->pciirqenb1);
+ }
+
+ spin_unlock(&dev->lock);
+
+ return IRQ_HANDLED;
+}
+
+/*-------------------------------------------------------------------------*/
+
+static void gadget_release(struct device *_dev)
+{
+ struct net2280 *dev = dev_get_drvdata(_dev);
+
+ kfree(dev);
+}
+
+/* tear down the binding between this driver and the pci device */
+
+static void net2280_remove(struct pci_dev *pdev)
+{
+ struct net2280 *dev = pci_get_drvdata(pdev);
+
+ usb_del_gadget_udc(&dev->gadget);
+
+ BUG_ON(dev->driver);
+
+ /* then clean up the resources we allocated during probe() */
+ net2280_led_shutdown(dev);
+ if (dev->requests) {
+ int i;
+ for (i = 1; i < 5; i++) {
+ if (!dev->ep[i].dummy)
+ continue;
+ pci_pool_free(dev->requests, dev->ep[i].dummy,
+ dev->ep[i].td_dma);
+ }
+ pci_pool_destroy(dev->requests);
+ }
+ if (dev->got_irq)
+ free_irq(pdev->irq, dev);
+ if (use_msi && dev->quirks & PLX_SUPERSPEED)
+ pci_disable_msi(pdev);
+ if (dev->regs)
+ iounmap(dev->regs);
+ if (dev->region)
+ release_mem_region(pci_resource_start(pdev, 0),
+ pci_resource_len(pdev, 0));
+ if (dev->enabled)
+ pci_disable_device(pdev);
+ device_remove_file(&pdev->dev, &dev_attr_registers);
+
+ ep_info(dev, "unbind\n");
+}
+
+/* wrap this driver around the specified device, but
+ * don't respond over USB until a gadget driver binds to us.
+ */
+
+static int net2280_probe(struct pci_dev *pdev, const struct pci_device_id *id)
+{
+ struct net2280 *dev;
+ unsigned long resource, len;
+ void __iomem *base = NULL;
+ int retval, i;
+
+ if (!use_dma)
+ use_dma_chaining = 0;
+
+ /* alloc, and start init */
+ dev = kzalloc(sizeof(*dev), GFP_KERNEL);
+ if (dev == NULL) {
+ retval = -ENOMEM;
+ goto done;
+ }
+
+ pci_set_drvdata(pdev, dev);
+ spin_lock_init(&dev->lock);
+ dev->quirks = id->driver_data;
+ dev->pdev = pdev;
+ dev->gadget.ops = &net2280_ops;
+ dev->gadget.max_speed = (dev->quirks & PLX_SUPERSPEED) ?
+ USB_SPEED_SUPER : USB_SPEED_HIGH;
+
+ /* the "gadget" abstracts/virtualizes the controller */
+ dev->gadget.name = driver_name;
+
+ /* now all the pci goodies ... */
+ if (pci_enable_device(pdev) < 0) {
+ retval = -ENODEV;
+ goto done;
+ }
+ dev->enabled = 1;
+
+ /* BAR 0 holds all the registers
+ * BAR 1 is 8051 memory; unused here (note erratum 0103)
+ * BAR 2 is fifo memory; unused here
+ */
+ resource = pci_resource_start(pdev, 0);
+ len = pci_resource_len(pdev, 0);
+ if (!request_mem_region(resource, len, driver_name)) {
+ ep_dbg(dev, "controller already in use\n");
+ retval = -EBUSY;
+ goto done;
+ }
+ dev->region = 1;
+
+ /* FIXME provide firmware download interface to put
+ * 8051 code into the chip, e.g. to turn on PCI PM.
+ */
+
+ base = ioremap_nocache(resource, len);
+ if (base == NULL) {
+ ep_dbg(dev, "can't map memory\n");
+ retval = -EFAULT;
+ goto done;
+ }
+ dev->regs = (struct net2280_regs __iomem *) base;
+ dev->usb = (struct net2280_usb_regs __iomem *) (base + 0x0080);
+ dev->pci = (struct net2280_pci_regs __iomem *) (base + 0x0100);
+ dev->dma = (struct net2280_dma_regs __iomem *) (base + 0x0180);
+ dev->dep = (struct net2280_dep_regs __iomem *) (base + 0x0200);
+ dev->epregs = (struct net2280_ep_regs __iomem *) (base + 0x0300);
+
+ if (dev->quirks & PLX_SUPERSPEED) {
+ u32 fsmvalue;
+ u32 usbstat;
+ dev->usb_ext = (struct usb338x_usb_ext_regs __iomem *)
+ (base + 0x00b4);
+ dev->fiforegs = (struct usb338x_fifo_regs __iomem *)
+ (base + 0x0500);
+ dev->llregs = (struct usb338x_ll_regs __iomem *)
+ (base + 0x0700);
+ dev->ll_lfps_regs = (struct usb338x_ll_lfps_regs __iomem *)
+ (base + 0x0748);
+ dev->ll_tsn_regs = (struct usb338x_ll_tsn_regs __iomem *)
+ (base + 0x077c);
+ dev->ll_chicken_reg = (struct usb338x_ll_chi_regs __iomem *)
+ (base + 0x079c);
+ dev->plregs = (struct usb338x_pl_regs __iomem *)
+ (base + 0x0800);
+ usbstat = readl(&dev->usb->usbstat);
+ dev->enhanced_mode = !!(usbstat & BIT(11));
+ dev->n_ep = (dev->enhanced_mode) ? 9 : 5;
+ /* put into initial config, link up all endpoints */
+ fsmvalue = get_idx_reg(dev->regs, SCRATCH) &
+ (0xf << DEFECT7374_FSM_FIELD);
+ /* See if firmware needs to set up for workaround: */
+ if (fsmvalue == DEFECT7374_FSM_SS_CONTROL_READ)
+ writel(0, &dev->usb->usbctl);
+ } else{
+ dev->enhanced_mode = 0;
+ dev->n_ep = 7;
+ /* put into initial config, link up all endpoints */
+ writel(0, &dev->usb->usbctl);
+ }
+
+ usb_reset(dev);
+ usb_reinit(dev);
+
+ /* irq setup after old hardware is cleaned up */
+ if (!pdev->irq) {
+ ep_err(dev, "No IRQ. Check PCI setup!\n");
+ retval = -ENODEV;
+ goto done;
+ }
+
+ if (use_msi && (dev->quirks & PLX_SUPERSPEED))
+ if (pci_enable_msi(pdev))
+ ep_err(dev, "Failed to enable MSI mode\n");
+
+ if (request_irq(pdev->irq, net2280_irq, IRQF_SHARED,
+ driver_name, dev)) {
+ ep_err(dev, "request interrupt %d failed\n", pdev->irq);
+ retval = -EBUSY;
+ goto done;
+ }
+ dev->got_irq = 1;
+
+ /* DMA setup */
+ /* NOTE: we know only the 32 LSBs of dma addresses may be nonzero */
+ dev->requests = pci_pool_create("requests", pdev,
+ sizeof(struct net2280_dma),
+ 0 /* no alignment requirements */,
+ 0 /* or page-crossing issues */);
+ if (!dev->requests) {
+ ep_dbg(dev, "can't get request pool\n");
+ retval = -ENOMEM;
+ goto done;
+ }
+ for (i = 1; i < 5; i++) {
+ struct net2280_dma *td;
+
+ td = pci_pool_alloc(dev->requests, GFP_KERNEL,
+ &dev->ep[i].td_dma);
+ if (!td) {
+ ep_dbg(dev, "can't get dummy %d\n", i);
+ retval = -ENOMEM;
+ goto done;
+ }
+ td->dmacount = 0; /* not VALID */
+ td->dmadesc = td->dmaaddr;
+ dev->ep[i].dummy = td;
+ }
+
+ /* enable lower-overhead pci memory bursts during DMA */
+ if (dev->quirks & PLX_LEGACY)
+ writel(BIT(DMA_MEMORY_WRITE_AND_INVALIDATE_ENABLE) |
+ /*
+ * 256 write retries may not be enough...
+ BIT(PCI_RETRY_ABORT_ENABLE) |
+ */
+ BIT(DMA_READ_MULTIPLE_ENABLE) |
+ BIT(DMA_READ_LINE_ENABLE),
+ &dev->pci->pcimstctl);
+ /* erratum 0115 shouldn't appear: Linux inits PCI_LATENCY_TIMER */
+ pci_set_master(pdev);
+ pci_try_set_mwi(pdev);
+
+ /* ... also flushes any posted pci writes */
+ dev->chiprev = get_idx_reg(dev->regs, REG_CHIPREV) & 0xffff;
+
+ /* done */
+ ep_info(dev, "%s\n", driver_desc);
+ ep_info(dev, "irq %d, pci mem %p, chip rev %04x\n",
+ pdev->irq, base, dev->chiprev);
+ ep_info(dev, "version: " DRIVER_VERSION "; dma %s %s\n",
+ use_dma ? (use_dma_chaining ? "chaining" : "enabled")
+ : "disabled",
+ dev->enhanced_mode ? "enhanced mode" : "legacy mode");
+ retval = device_create_file(&pdev->dev, &dev_attr_registers);
+ if (retval)
+ goto done;
+
+ retval = usb_add_gadget_udc_release(&pdev->dev, &dev->gadget,
+ gadget_release);
+ if (retval)
+ goto done;
+ return 0;
+
+done:
+ if (dev)
+ net2280_remove(pdev);
+ return retval;
+}
+
+/* make sure the board is quiescent; otherwise it will continue
+ * generating IRQs across the upcoming reboot.
+ */
+
+static void net2280_shutdown(struct pci_dev *pdev)
+{
+ struct net2280 *dev = pci_get_drvdata(pdev);
+
+ /* disable IRQs */
+ writel(0, &dev->regs->pciirqenb0);
+ writel(0, &dev->regs->pciirqenb1);
+
+ /* disable the pullup so the host will think we're gone */
+ writel(0, &dev->usb->usbctl);
+
+ /* Disable full-speed test mode */
+ if (dev->quirks & PLX_LEGACY)
+ writel(0, &dev->usb->xcvrdiag);
+}
+
+
+/*-------------------------------------------------------------------------*/
+
+static const struct pci_device_id pci_ids[] = { {
+ .class = ((PCI_CLASS_SERIAL_USB << 8) | 0xfe),
+ .class_mask = ~0,
+ .vendor = PCI_VENDOR_ID_PLX_LEGACY,
+ .device = 0x2280,
+ .subvendor = PCI_ANY_ID,
+ .subdevice = PCI_ANY_ID,
+ .driver_data = PLX_LEGACY | PLX_2280,
+ }, {
+ .class = ((PCI_CLASS_SERIAL_USB << 8) | 0xfe),
+ .class_mask = ~0,
+ .vendor = PCI_VENDOR_ID_PLX_LEGACY,
+ .device = 0x2282,
+ .subvendor = PCI_ANY_ID,
+ .subdevice = PCI_ANY_ID,
+ .driver_data = PLX_LEGACY,
+ },
+ {
+ .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,
+ },
+ {
+ .class = ((PCI_CLASS_SERIAL_USB << 8) | 0xfe),
+ .class_mask = ~0,
+ .vendor = PCI_VENDOR_ID_PLX,
+ .device = 0x3382,
+ .subvendor = PCI_ANY_ID,
+ .subdevice = PCI_ANY_ID,
+ .driver_data = PLX_SUPERSPEED,
+ },
+{ /* end: all zeroes */ }
+};
+MODULE_DEVICE_TABLE(pci, pci_ids);
+
+/* pci driver glue; this is a "new style" PCI driver module */
+static struct pci_driver net2280_pci_driver = {
+ .name = (char *) driver_name,
+ .id_table = pci_ids,
+
+ .probe = net2280_probe,
+ .remove = net2280_remove,
+ .shutdown = net2280_shutdown,
+
+ /* FIXME add power management support */
+};
+
+module_pci_driver(net2280_pci_driver);
+
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_AUTHOR("David Brownell");
+MODULE_LICENSE("GPL");
diff --git a/drivers/usb/gadget/udc/net2280.h b/drivers/usb/gadget/udc/net2280.h
new file mode 100644
index 000000000000..03f15242d794
--- /dev/null
+++ b/drivers/usb/gadget/udc/net2280.h
@@ -0,0 +1,403 @@
+/*
+ * NetChip 2280 high/full speed USB device controller.
+ * Unlike many such controllers, this one talks PCI.
+ */
+
+/*
+ * Copyright (C) 2002 NetChip Technology, Inc. (http://www.netchip.com)
+ * Copyright (C) 2003 David Brownell
+ * Copyright (C) 2014 Ricardo Ribalda - Qtechnology/AS
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <linux/usb/net2280.h>
+#include <linux/usb/usb338x.h>
+
+/*-------------------------------------------------------------------------*/
+
+#ifdef __KERNEL__
+
+/* indexed registers [11.10] are accessed indirectly
+ * caller must own the device lock.
+ */
+
+static inline u32 get_idx_reg(struct net2280_regs __iomem *regs, u32 index)
+{
+ writel(index, &regs->idxaddr);
+ /* NOTE: synchs device/cpu memory views */
+ return readl(&regs->idxdata);
+}
+
+static inline void
+set_idx_reg(struct net2280_regs __iomem *regs, u32 index, u32 value)
+{
+ writel(index, &regs->idxaddr);
+ writel(value, &regs->idxdata);
+ /* posted, may not be visible yet */
+}
+
+#endif /* __KERNEL__ */
+
+#define PCI_VENDOR_ID_PLX_LEGACY 0x17cc
+
+#define PLX_LEGACY BIT(0)
+#define PLX_2280 BIT(1)
+#define PLX_SUPERSPEED BIT(2)
+
+#define REG_DIAG 0x0
+#define RETRY_COUNTER 16
+#define FORCE_PCI_SERR 11
+#define FORCE_PCI_INTERRUPT 10
+#define FORCE_USB_INTERRUPT 9
+#define FORCE_CPU_INTERRUPT 8
+#define ILLEGAL_BYTE_ENABLES 5
+#define FAST_TIMES 4
+#define FORCE_RECEIVE_ERROR 2
+#define FORCE_TRANSMIT_CRC_ERROR 0
+#define REG_FRAME 0x02 /* from last sof */
+#define REG_CHIPREV 0x03 /* in bcd */
+#define REG_HS_NAK_RATE 0x0a /* NAK per N uframes */
+
+#define CHIPREV_1 0x0100
+#define CHIPREV_1A 0x0110
+
+/* DEFECT 7374 */
+#define DEFECT_7374_NUMBEROF_MAX_WAIT_LOOPS 200
+#define DEFECT_7374_PROCESSOR_WAIT_TIME 10
+
+/* ep0 max packet size */
+#define EP0_SS_MAX_PACKET_SIZE 0x200
+#define EP0_HS_MAX_PACKET_SIZE 0x40
+#ifdef __KERNEL__
+
+/*-------------------------------------------------------------------------*/
+
+/* [8.3] for scatter/gather i/o
+ * use struct net2280_dma_regs bitfields
+ */
+struct net2280_dma {
+ __le32 dmacount;
+ __le32 dmaaddr; /* the buffer */
+ __le32 dmadesc; /* next dma descriptor */
+ __le32 _reserved;
+} __aligned(16);
+
+/*-------------------------------------------------------------------------*/
+
+/* DRIVER DATA STRUCTURES and UTILITIES */
+
+struct net2280_ep {
+ struct usb_ep ep;
+ struct net2280_ep_regs __iomem *cfg;
+ struct net2280_ep_regs __iomem *regs;
+ struct net2280_dma_regs __iomem *dma;
+ struct net2280_dma *dummy;
+ struct usb338x_fifo_regs __iomem *fiforegs;
+ dma_addr_t td_dma; /* of dummy */
+ struct net2280 *dev;
+ unsigned long irqs;
+ unsigned is_halt:1, dma_started:1;
+
+ /* analogous to a host-side qh */
+ struct list_head queue;
+ const struct usb_endpoint_descriptor *desc;
+ unsigned num : 8,
+ fifo_size : 12,
+ in_fifo_validate : 1,
+ out_overflow : 1,
+ stopped : 1,
+ wedged : 1,
+ is_in : 1,
+ is_iso : 1,
+ responded : 1;
+};
+
+static inline void allow_status(struct net2280_ep *ep)
+{
+ /* ep0 only */
+ writel(BIT(CLEAR_CONTROL_STATUS_PHASE_HANDSHAKE) |
+ BIT(CLEAR_NAK_OUT_PACKETS) |
+ BIT(CLEAR_NAK_OUT_PACKETS_MODE),
+ &ep->regs->ep_rsp);
+ ep->stopped = 1;
+}
+
+static void allow_status_338x(struct net2280_ep *ep)
+{
+ /*
+ * Control Status Phase Handshake was set by the chip when the setup
+ * packet arrived. While set, the chip automatically NAKs the host's
+ * Status Phase tokens.
+ */
+ writel(BIT(CLEAR_CONTROL_STATUS_PHASE_HANDSHAKE), &ep->regs->ep_rsp);
+
+ ep->stopped = 1;
+
+ /* TD 9.9 Halt Endpoint test. TD 9.22 set feature test. */
+ ep->responded = 0;
+}
+
+struct net2280_request {
+ struct usb_request req;
+ struct net2280_dma *td;
+ dma_addr_t td_dma;
+ struct list_head queue;
+ unsigned mapped : 1,
+ valid : 1;
+};
+
+struct net2280 {
+ /* each pci device provides one gadget, several endpoints */
+ struct usb_gadget gadget;
+ spinlock_t lock;
+ struct net2280_ep ep[9];
+ struct usb_gadget_driver *driver;
+ unsigned enabled : 1,
+ protocol_stall : 1,
+ softconnect : 1,
+ got_irq : 1,
+ region:1,
+ u1_enable:1,
+ u2_enable:1,
+ ltm_enable:1,
+ wakeup_enable:1,
+ selfpowered:1,
+ addressed_state:1;
+ u16 chiprev;
+ int enhanced_mode;
+ int n_ep;
+ kernel_ulong_t quirks;
+
+
+ /* pci state used to access those endpoints */
+ struct pci_dev *pdev;
+ struct net2280_regs __iomem *regs;
+ struct net2280_usb_regs __iomem *usb;
+ struct usb338x_usb_ext_regs __iomem *usb_ext;
+ struct net2280_pci_regs __iomem *pci;
+ struct net2280_dma_regs __iomem *dma;
+ struct net2280_dep_regs __iomem *dep;
+ struct net2280_ep_regs __iomem *epregs;
+ struct usb338x_fifo_regs __iomem *fiforegs;
+ struct usb338x_ll_regs __iomem *llregs;
+ struct usb338x_ll_lfps_regs __iomem *ll_lfps_regs;
+ struct usb338x_ll_tsn_regs __iomem *ll_tsn_regs;
+ struct usb338x_ll_chi_regs __iomem *ll_chicken_reg;
+ struct usb338x_pl_regs __iomem *plregs;
+
+ struct pci_pool *requests;
+ /* statistics...*/
+};
+
+static inline void set_halt(struct net2280_ep *ep)
+{
+ /* ep0 and bulk/intr endpoints */
+ writel(BIT(CLEAR_CONTROL_STATUS_PHASE_HANDSHAKE) |
+ /* set NAK_OUT for erratum 0114 */
+ ((ep->dev->chiprev == CHIPREV_1) << SET_NAK_OUT_PACKETS) |
+ BIT(SET_ENDPOINT_HALT),
+ &ep->regs->ep_rsp);
+}
+
+static inline void clear_halt(struct net2280_ep *ep)
+{
+ /* ep0 and bulk/intr endpoints */
+ writel(BIT(CLEAR_ENDPOINT_HALT) |
+ BIT(CLEAR_ENDPOINT_TOGGLE) |
+ /*
+ * unless the gadget driver left a short packet in the
+ * fifo, this reverses the erratum 0114 workaround.
+ */
+ ((ep->dev->chiprev == CHIPREV_1) << CLEAR_NAK_OUT_PACKETS),
+ &ep->regs->ep_rsp);
+}
+
+/*
+ * FSM value for Defect 7374 (U1U2 Test) is managed in
+ * chip's SCRATCH register:
+ */
+#define DEFECT7374_FSM_FIELD 28
+
+/* Waiting for Control Read:
+ * - A transition to this state indicates a fresh USB connection,
+ * before the first Setup Packet. The connection speed is not
+ * known. Firmware is waiting for the first Control Read.
+ * - Starting state: This state can be thought of as the FSM's typical
+ * starting state.
+ * - Tip: Upon the first SS Control Read the FSM never
+ * returns to this state.
+ */
+#define DEFECT7374_FSM_WAITING_FOR_CONTROL_READ BIT(DEFECT7374_FSM_FIELD)
+
+/* Non-SS Control Read:
+ * - A transition to this state indicates detection of the first HS
+ * or FS Control Read.
+ * - Tip: Upon the first SS Control Read the FSM never
+ * returns to this state.
+ */
+#define DEFECT7374_FSM_NON_SS_CONTROL_READ (2 << DEFECT7374_FSM_FIELD)
+
+/* SS Control Read:
+ * - A transition to this state indicates detection of the
+ * first SS Control Read.
+ * - This state indicates workaround completion. Workarounds no longer
+ * need to be applied (as long as the chip remains powered up).
+ * - Tip: Once in this state the FSM state does not change (until
+ * the chip's power is lost and restored).
+ * - This can be thought of as the final state of the FSM;
+ * the FSM 'locks-up' in this state until the chip loses power.
+ */
+#define DEFECT7374_FSM_SS_CONTROL_READ (3 << DEFECT7374_FSM_FIELD)
+
+#ifdef USE_RDK_LEDS
+
+static inline void net2280_led_init(struct net2280 *dev)
+{
+ /* LED3 (green) is on during USB activity. note erratum 0113. */
+ writel(BIT(GPIO3_LED_SELECT) |
+ BIT(GPIO3_OUTPUT_ENABLE) |
+ BIT(GPIO2_OUTPUT_ENABLE) |
+ BIT(GPIO1_OUTPUT_ENABLE) |
+ BIT(GPIO0_OUTPUT_ENABLE),
+ &dev->regs->gpioctl);
+}
+
+/* indicate speed with bi-color LED 0/1 */
+static inline
+void net2280_led_speed(struct net2280 *dev, enum usb_device_speed speed)
+{
+ u32 val = readl(&dev->regs->gpioctl);
+ switch (speed) {
+ case USB_SPEED_SUPER: /* green + red */
+ val |= BIT(GPIO0_DATA) | BIT(GPIO1_DATA);
+ break;
+ case USB_SPEED_HIGH: /* green */
+ val &= ~BIT(GPIO0_DATA);
+ val |= BIT(GPIO1_DATA);
+ break;
+ case USB_SPEED_FULL: /* red */
+ val &= ~BIT(GPIO1_DATA);
+ val |= BIT(GPIO0_DATA);
+ break;
+ default: /* (off/black) */
+ val &= ~(BIT(GPIO1_DATA) | BIT(GPIO0_DATA));
+ break;
+ }
+ writel(val, &dev->regs->gpioctl);
+}
+
+/* indicate power with LED 2 */
+static inline void net2280_led_active(struct net2280 *dev, int is_active)
+{
+ u32 val = readl(&dev->regs->gpioctl);
+
+ /* FIXME this LED never seems to turn on.*/
+ if (is_active)
+ val |= GPIO2_DATA;
+ else
+ val &= ~GPIO2_DATA;
+ writel(val, &dev->regs->gpioctl);
+}
+
+static inline void net2280_led_shutdown(struct net2280 *dev)
+{
+ /* turn off all four GPIO*_DATA bits */
+ writel(readl(&dev->regs->gpioctl) & ~0x0f,
+ &dev->regs->gpioctl);
+}
+
+#else
+
+#define net2280_led_init(dev) do { } while (0)
+#define net2280_led_speed(dev, speed) do { } while (0)
+#define net2280_led_shutdown(dev) do { } while (0)
+
+#endif
+
+/*-------------------------------------------------------------------------*/
+
+#define ep_dbg(ndev, fmt, args...) \
+ dev_dbg((&((ndev)->pdev->dev)), fmt, ##args)
+
+#define ep_vdbg(ndev, fmt, args...) \
+ dev_vdbg((&((ndev)->pdev->dev)), fmt, ##args)
+
+#define ep_info(ndev, fmt, args...) \
+ dev_info((&((ndev)->pdev->dev)), fmt, ##args)
+
+#define ep_warn(ndev, fmt, args...) \
+ dev_warn((&((ndev)->pdev->dev)), fmt, ##args)
+
+#define ep_err(ndev, fmt, args...) \
+ dev_err((&((ndev)->pdev->dev)), fmt, ##args)
+
+/*-------------------------------------------------------------------------*/
+
+static inline void set_fifo_bytecount(struct net2280_ep *ep, unsigned count)
+{
+ if (ep->dev->pdev->vendor == 0x17cc)
+ writeb(count, 2 + (u8 __iomem *) &ep->regs->ep_cfg);
+ else{
+ u32 tmp = readl(&ep->cfg->ep_cfg) &
+ (~(0x07 << EP_FIFO_BYTE_COUNT));
+ writel(tmp | (count << EP_FIFO_BYTE_COUNT), &ep->cfg->ep_cfg);
+ }
+}
+
+static inline void start_out_naking(struct net2280_ep *ep)
+{
+ /* NOTE: hardware races lurk here, and PING protocol issues */
+ writel(BIT(SET_NAK_OUT_PACKETS), &ep->regs->ep_rsp);
+ /* synch with device */
+ readl(&ep->regs->ep_rsp);
+}
+
+#ifdef DEBUG
+static inline void assert_out_naking(struct net2280_ep *ep, const char *where)
+{
+ u32 tmp = readl(&ep->regs->ep_stat);
+
+ if ((tmp & BIT(NAK_OUT_PACKETS)) == 0) {
+ ep_dbg(ep->dev, "%s %s %08x !NAK\n",
+ ep->ep.name, where, tmp);
+ writel(BIT(SET_NAK_OUT_PACKETS),
+ &ep->regs->ep_rsp);
+ }
+}
+#define ASSERT_OUT_NAKING(ep) assert_out_naking(ep, __func__)
+#else
+#define ASSERT_OUT_NAKING(ep) do {} while (0)
+#endif
+
+static inline void stop_out_naking(struct net2280_ep *ep)
+{
+ u32 tmp;
+
+ tmp = readl(&ep->regs->ep_stat);
+ if ((tmp & BIT(NAK_OUT_PACKETS)) != 0)
+ writel(BIT(CLEAR_NAK_OUT_PACKETS), &ep->regs->ep_rsp);
+}
+
+
+static inline void set_max_speed(struct net2280_ep *ep, u32 max)
+{
+ u32 reg;
+ static const u32 ep_enhanced[9] = { 0x10, 0x60, 0x30, 0x80,
+ 0x50, 0x20, 0x70, 0x40, 0x90 };
+
+ if (ep->dev->enhanced_mode)
+ reg = ep_enhanced[ep->num];
+ else{
+ reg = (ep->num + 1) * 0x10;
+ if (ep->dev->gadget.speed != USB_SPEED_HIGH)
+ reg += 1;
+ }
+
+ set_idx_reg(ep->dev->regs, reg, max);
+}
+
+#endif /* __KERNEL__ */
diff --git a/drivers/usb/gadget/omap_udc.c b/drivers/usb/gadget/udc/omap_udc.c
index 2ae4f6d69f74..e731373fd4d7 100644
--- a/drivers/usb/gadget/omap_udc.c
+++ b/drivers/usb/gadget/udc/omap_udc.c
@@ -2079,10 +2079,7 @@ static int omap_udc_start(struct usb_gadget *g,
&udc->gadget);
if (status < 0) {
ERR("can't bind to transceiver\n");
- if (driver->unbind) {
- driver->unbind(&udc->gadget);
- udc->driver = NULL;
- }
+ udc->driver = NULL;
goto done;
}
} else {
diff --git a/drivers/usb/gadget/omap_udc.h b/drivers/usb/gadget/udc/omap_udc.h
index cfadeb5fc5de..cfadeb5fc5de 100644
--- a/drivers/usb/gadget/omap_udc.h
+++ b/drivers/usb/gadget/udc/omap_udc.h
diff --git a/drivers/usb/gadget/pch_udc.c b/drivers/usb/gadget/udc/pch_udc.c
index eb8c3bedb57a..eb8c3bedb57a 100644
--- a/drivers/usb/gadget/pch_udc.c
+++ b/drivers/usb/gadget/udc/pch_udc.c
diff --git a/drivers/usb/gadget/pxa25x_udc.c b/drivers/usb/gadget/udc/pxa25x_udc.c
index 9984437d2952..251e4d5ee152 100644
--- a/drivers/usb/gadget/pxa25x_udc.c
+++ b/drivers/usb/gadget/udc/pxa25x_udc.c
@@ -16,6 +16,7 @@
/* #define VERBOSE_DEBUG */
#include <linux/device.h>
+#include <linux/gpio.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/ioport.h>
@@ -40,7 +41,6 @@
#include <asm/byteorder.h>
#include <asm/dma.h>
-#include <asm/gpio.h>
#include <asm/mach-types.h>
#include <asm/unaligned.h>
@@ -2105,11 +2105,9 @@ static int pxa25x_udc_probe(struct platform_device *pdev)
if (irq < 0)
return -ENODEV;
- dev->clk = clk_get(&pdev->dev, NULL);
- if (IS_ERR(dev->clk)) {
- retval = PTR_ERR(dev->clk);
- goto err_clk;
- }
+ dev->clk = devm_clk_get(&pdev->dev, NULL);
+ if (IS_ERR(dev->clk))
+ return PTR_ERR(dev->clk);
pr_debug("%s: IRQ %d%s%s\n", driver_name, irq,
dev->has_cfr ? "" : " (!cfr)",
@@ -2120,15 +2118,16 @@ static int pxa25x_udc_probe(struct platform_device *pdev)
dev->dev = &pdev->dev;
dev->mach = dev_get_platdata(&pdev->dev);
- dev->transceiver = usb_get_phy(USB_PHY_TYPE_USB2);
+ dev->transceiver = devm_usb_get_phy(&pdev->dev, USB_PHY_TYPE_USB2);
if (gpio_is_valid(dev->mach->gpio_pullup)) {
- if ((retval = gpio_request(dev->mach->gpio_pullup,
- "pca25x_udc GPIO PULLUP"))) {
+ retval = devm_gpio_request(&pdev->dev, dev->mach->gpio_pullup,
+ "pca25x_udc GPIO PULLUP");
+ if (retval) {
dev_dbg(&pdev->dev,
"can't get pullup gpio %d, err: %d\n",
dev->mach->gpio_pullup, retval);
- goto err_gpio_pullup;
+ goto err;
}
gpio_direction_output(dev->mach->gpio_pullup, 0);
}
@@ -2146,30 +2145,32 @@ static int pxa25x_udc_probe(struct platform_device *pdev)
dev->vbus = 0;
/* irq setup after old hardware state is cleaned up */
- retval = request_irq(irq, pxa25x_udc_irq,
- 0, driver_name, dev);
+ retval = devm_request_irq(&pdev->dev, irq, pxa25x_udc_irq, 0,
+ driver_name, dev);
if (retval != 0) {
pr_err("%s: can't get irq %d, err %d\n",
driver_name, irq, retval);
- goto err_irq1;
+ goto err;
}
dev->got_irq = 1;
#ifdef CONFIG_ARCH_LUBBOCK
if (machine_is_lubbock()) {
- retval = request_irq(LUBBOCK_USB_DISC_IRQ, lubbock_vbus_irq,
- 0, driver_name, dev);
+ retval = devm_request_irq(&pdev->dev, LUBBOCK_USB_DISC_IRQ,
+ lubbock_vbus_irq, 0, driver_name,
+ dev);
if (retval != 0) {
pr_err("%s: can't get irq %i, err %d\n",
driver_name, LUBBOCK_USB_DISC_IRQ, retval);
- goto err_irq_lub;
+ goto err;
}
- retval = request_irq(LUBBOCK_USB_IRQ, lubbock_vbus_irq,
- 0, driver_name, dev);
+ retval = devm_request_irq(&pdev->dev, LUBBOCK_USB_IRQ,
+ lubbock_vbus_irq, 0, driver_name,
+ dev);
if (retval != 0) {
pr_err("%s: can't get irq %i, err %d\n",
driver_name, LUBBOCK_USB_IRQ, retval);
- goto lubbock_fail0;
+ goto err;
}
} else
#endif
@@ -2180,22 +2181,9 @@ static int pxa25x_udc_probe(struct platform_device *pdev)
return retval;
remove_debug_files(dev);
-#ifdef CONFIG_ARCH_LUBBOCK
-lubbock_fail0:
- free_irq(LUBBOCK_USB_DISC_IRQ, dev);
- err_irq_lub:
- free_irq(irq, dev);
-#endif
- err_irq1:
- if (gpio_is_valid(dev->mach->gpio_pullup))
- gpio_free(dev->mach->gpio_pullup);
- err_gpio_pullup:
- if (!IS_ERR_OR_NULL(dev->transceiver)) {
- usb_put_phy(dev->transceiver);
+ err:
+ if (!IS_ERR_OR_NULL(dev->transceiver))
dev->transceiver = NULL;
- }
- clk_put(dev->clk);
- err_clk:
return retval;
}
@@ -2217,25 +2205,8 @@ static int pxa25x_udc_remove(struct platform_device *pdev)
remove_debug_files(dev);
- if (dev->got_irq) {
- free_irq(platform_get_irq(pdev, 0), dev);
- dev->got_irq = 0;
- }
-#ifdef CONFIG_ARCH_LUBBOCK
- if (machine_is_lubbock()) {
- free_irq(LUBBOCK_USB_DISC_IRQ, dev);
- free_irq(LUBBOCK_USB_IRQ, dev);
- }
-#endif
- if (gpio_is_valid(dev->mach->gpio_pullup))
- gpio_free(dev->mach->gpio_pullup);
-
- clk_put(dev->clk);
-
- if (!IS_ERR_OR_NULL(dev->transceiver)) {
- usb_put_phy(dev->transceiver);
+ if (!IS_ERR_OR_NULL(dev->transceiver))
dev->transceiver = NULL;
- }
the_controller = NULL;
return 0;
diff --git a/drivers/usb/gadget/pxa25x_udc.h b/drivers/usb/gadget/udc/pxa25x_udc.h
index 3fe5931dc21a..3fe5931dc21a 100644
--- a/drivers/usb/gadget/pxa25x_udc.h
+++ b/drivers/usb/gadget/udc/pxa25x_udc.h
diff --git a/drivers/usb/gadget/pxa27x_udc.c b/drivers/usb/gadget/udc/pxa27x_udc.c
index cdf4d678be96..597d39f89420 100644
--- a/drivers/usb/gadget/pxa27x_udc.c
+++ b/drivers/usb/gadget/udc/pxa27x_udc.c
@@ -2446,6 +2446,9 @@ static int pxa_udc_probe(struct platform_device *pdev)
retval = PTR_ERR(udc->clk);
goto err_clk;
}
+ retval = clk_prepare(udc->clk);
+ if (retval)
+ goto err_clk_prepare;
retval = -ENOMEM;
udc->regs = ioremap(regs->start, resource_size(regs));
@@ -2483,6 +2486,8 @@ err_add_udc:
err_irq:
iounmap(udc->regs);
err_map:
+ clk_unprepare(udc->clk);
+err_clk_prepare:
clk_put(udc->clk);
udc->clk = NULL;
err_clk:
@@ -2509,6 +2514,7 @@ static int pxa_udc_remove(struct platform_device *_dev)
udc->transceiver = NULL;
the_controller = NULL;
+ clk_unprepare(udc->clk);
clk_put(udc->clk);
iounmap(udc->regs);
diff --git a/drivers/usb/gadget/pxa27x_udc.h b/drivers/usb/gadget/udc/pxa27x_udc.h
index 28f2b53530f5..28f2b53530f5 100644
--- a/drivers/usb/gadget/pxa27x_udc.h
+++ b/drivers/usb/gadget/udc/pxa27x_udc.h
diff --git a/drivers/usb/gadget/r8a66597-udc.c b/drivers/usb/gadget/udc/r8a66597-udc.c
index b698a490cc7d..46008421c1ec 100644
--- a/drivers/usb/gadget/r8a66597-udc.c
+++ b/drivers/usb/gadget/udc/r8a66597-udc.c
@@ -1826,18 +1826,12 @@ static int __exit r8a66597_remove(struct platform_device *pdev)
usb_del_gadget_udc(&r8a66597->gadget);
del_timer_sync(&r8a66597->timer);
- iounmap(r8a66597->reg);
- if (r8a66597->pdata->sudmac)
- iounmap(r8a66597->sudmac_reg);
- free_irq(platform_get_irq(pdev, 0), r8a66597);
r8a66597_free_request(&r8a66597->ep[0].ep, r8a66597->ep0_req);
if (r8a66597->pdata->on_chip) {
clk_disable_unprepare(r8a66597->clk);
- clk_put(r8a66597->clk);
}
- kfree(r8a66597);
return 0;
}
@@ -1845,28 +1839,24 @@ static void nop_completion(struct usb_ep *ep, struct usb_request *r)
{
}
-static int __init r8a66597_sudmac_ioremap(struct r8a66597 *r8a66597,
+static int r8a66597_sudmac_ioremap(struct r8a66597 *r8a66597,
struct platform_device *pdev)
{
struct resource *res;
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "sudmac");
- if (!res) {
- dev_err(&pdev->dev, "platform_get_resource error(sudmac).\n");
- return -ENODEV;
- }
-
- r8a66597->sudmac_reg = ioremap(res->start, resource_size(res));
- if (r8a66597->sudmac_reg == NULL) {
+ r8a66597->sudmac_reg = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(r8a66597->sudmac_reg)) {
dev_err(&pdev->dev, "ioremap error(sudmac).\n");
- return -ENOMEM;
+ return PTR_ERR(r8a66597->sudmac_reg);
}
return 0;
}
-static int __init r8a66597_probe(struct platform_device *pdev)
+static int r8a66597_probe(struct platform_device *pdev)
{
+ struct device *dev = &pdev->dev;
char clk_name[8];
struct resource *res, *ires;
int irq;
@@ -1877,39 +1867,27 @@ static int __init r8a66597_probe(struct platform_device *pdev)
unsigned long irq_trigger;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- if (!res) {
- ret = -ENODEV;
- dev_err(&pdev->dev, "platform_get_resource error.\n");
- goto clean_up;
- }
+ reg = devm_ioremap_resource(&pdev->dev, res);
+ if (!reg)
+ return -ENODEV;
ires = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
irq = ires->start;
irq_trigger = ires->flags & IRQF_TRIGGER_MASK;
if (irq < 0) {
- ret = -ENODEV;
- dev_err(&pdev->dev, "platform_get_irq error.\n");
- goto clean_up;
- }
-
- reg = ioremap(res->start, resource_size(res));
- if (reg == NULL) {
- ret = -ENOMEM;
- dev_err(&pdev->dev, "ioremap error.\n");
- goto clean_up;
+ dev_err(dev, "platform_get_irq error.\n");
+ return -ENODEV;
}
/* initialize ucd */
- r8a66597 = kzalloc(sizeof(struct r8a66597), GFP_KERNEL);
- if (r8a66597 == NULL) {
- ret = -ENOMEM;
- goto clean_up;
- }
+ r8a66597 = devm_kzalloc(dev, sizeof(struct r8a66597), GFP_KERNEL);
+ if (r8a66597 == NULL)
+ return -ENOMEM;
spin_lock_init(&r8a66597->lock);
platform_set_drvdata(pdev, r8a66597);
- r8a66597->pdata = dev_get_platdata(&pdev->dev);
+ r8a66597->pdata = dev_get_platdata(dev);
r8a66597->irq_sense_low = irq_trigger == IRQF_TRIGGER_LOW;
r8a66597->gadget.ops = &r8a66597_gadget_ops;
@@ -1923,12 +1901,10 @@ static int __init r8a66597_probe(struct platform_device *pdev)
if (r8a66597->pdata->on_chip) {
snprintf(clk_name, sizeof(clk_name), "usb%d", pdev->id);
- r8a66597->clk = clk_get(&pdev->dev, clk_name);
+ r8a66597->clk = devm_clk_get(dev, clk_name);
if (IS_ERR(r8a66597->clk)) {
- dev_err(&pdev->dev, "cannot get clock \"%s\"\n",
- clk_name);
- ret = PTR_ERR(r8a66597->clk);
- goto clean_up;
+ dev_err(dev, "cannot get clock \"%s\"\n", clk_name);
+ return PTR_ERR(r8a66597->clk);
}
clk_prepare_enable(r8a66597->clk);
}
@@ -1941,10 +1917,10 @@ static int __init r8a66597_probe(struct platform_device *pdev)
disable_controller(r8a66597); /* make sure controller is disabled */
- ret = request_irq(irq, r8a66597_irq, IRQF_SHARED,
- udc_name, r8a66597);
+ ret = devm_request_irq(dev, irq, r8a66597_irq, IRQF_SHARED,
+ udc_name, r8a66597);
if (ret < 0) {
- dev_err(&pdev->dev, "request_irq error (%d)\n", ret);
+ dev_err(dev, "request_irq error (%d)\n", ret);
goto clean_up2;
}
@@ -1978,37 +1954,25 @@ static int __init r8a66597_probe(struct platform_device *pdev)
GFP_KERNEL);
if (r8a66597->ep0_req == NULL) {
ret = -ENOMEM;
- goto clean_up3;
+ goto clean_up2;
}
r8a66597->ep0_req->complete = nop_completion;
- ret = usb_add_gadget_udc(&pdev->dev, &r8a66597->gadget);
+ ret = usb_add_gadget_udc(dev, &r8a66597->gadget);
if (ret)
goto err_add_udc;
- dev_info(&pdev->dev, "version %s\n", DRIVER_VERSION);
+ dev_info(dev, "version %s\n", DRIVER_VERSION);
return 0;
err_add_udc:
r8a66597_free_request(&r8a66597->ep[0].ep, r8a66597->ep0_req);
-clean_up3:
- free_irq(irq, r8a66597);
clean_up2:
- if (r8a66597->pdata->on_chip) {
+ if (r8a66597->pdata->on_chip)
clk_disable_unprepare(r8a66597->clk);
- clk_put(r8a66597->clk);
- }
-clean_up:
- if (r8a66597) {
- if (r8a66597->sudmac_reg)
- iounmap(r8a66597->sudmac_reg);
- if (r8a66597->ep0_req)
- r8a66597_free_request(&r8a66597->ep[0].ep,
- r8a66597->ep0_req);
- kfree(r8a66597);
- }
- if (reg)
- iounmap(reg);
+
+ if (r8a66597->ep0_req)
+ r8a66597_free_request(&r8a66597->ep[0].ep, r8a66597->ep0_req);
return ret;
}
diff --git a/drivers/usb/gadget/r8a66597-udc.h b/drivers/usb/gadget/udc/r8a66597-udc.h
index 45c4b2df1785..45c4b2df1785 100644
--- a/drivers/usb/gadget/r8a66597-udc.h
+++ b/drivers/usb/gadget/udc/r8a66597-udc.h
diff --git a/drivers/usb/gadget/s3c-hsudc.c b/drivers/usb/gadget/udc/s3c-hsudc.c
index 10c6a128250c..10c6a128250c 100644
--- a/drivers/usb/gadget/s3c-hsudc.c
+++ b/drivers/usb/gadget/udc/s3c-hsudc.c
diff --git a/drivers/usb/gadget/s3c2410_udc.c b/drivers/usb/gadget/udc/s3c2410_udc.c
index 7987aa049fab..357b58e0087b 100644
--- a/drivers/usb/gadget/s3c2410_udc.c
+++ b/drivers/usb/gadget/udc/s3c2410_udc.c
@@ -1788,7 +1788,7 @@ static int s3c2410_udc_probe(struct platform_device *pdev)
return PTR_ERR(usb_bus_clock);
}
- clk_enable(usb_bus_clock);
+ clk_prepare_enable(usb_bus_clock);
udc_clock = clk_get(NULL, "usb-device");
if (IS_ERR(udc_clock)) {
@@ -1796,7 +1796,7 @@ static int s3c2410_udc_probe(struct platform_device *pdev)
return PTR_ERR(udc_clock);
}
- clk_enable(udc_clock);
+ clk_prepare_enable(udc_clock);
mdelay(10);
@@ -1952,13 +1952,13 @@ static int s3c2410_udc_remove(struct platform_device *pdev)
release_mem_region(rsrc_start, rsrc_len);
if (!IS_ERR(udc_clock) && udc_clock != NULL) {
- clk_disable(udc_clock);
+ clk_disable_unprepare(udc_clock);
clk_put(udc_clock);
udc_clock = NULL;
}
if (!IS_ERR(usb_bus_clock) && usb_bus_clock != NULL) {
- clk_disable(usb_bus_clock);
+ clk_disable_unprepare(usb_bus_clock);
clk_put(usb_bus_clock);
usb_bus_clock = NULL;
}
diff --git a/drivers/usb/gadget/s3c2410_udc.h b/drivers/usb/gadget/udc/s3c2410_udc.h
index 93bf225f1969..93bf225f1969 100644
--- a/drivers/usb/gadget/s3c2410_udc.h
+++ b/drivers/usb/gadget/udc/s3c2410_udc.h
diff --git a/drivers/usb/gadget/udc-core.c b/drivers/usb/gadget/udc/udc-core.c
index b0d98172bc07..b0d98172bc07 100644
--- a/drivers/usb/gadget/udc-core.c
+++ b/drivers/usb/gadget/udc/udc-core.c
diff --git a/drivers/usb/musb/blackfin.c b/drivers/usb/musb/blackfin.c
index d40d5f0b5528..ac4422b33dcd 100644
--- a/drivers/usb/musb/blackfin.c
+++ b/drivers/usb/musb/blackfin.c
@@ -455,7 +455,7 @@ static int bfin_probe(struct platform_device *pdev)
int ret = -ENOMEM;
- glue = kzalloc(sizeof(*glue), GFP_KERNEL);
+ glue = devm_kzalloc(&pdev->dev, sizeof(*glue), GFP_KERNEL);
if (!glue) {
dev_err(&pdev->dev, "failed to allocate glue context\n");
goto err0;
@@ -464,7 +464,7 @@ static int bfin_probe(struct platform_device *pdev)
musb = platform_device_alloc("musb-hdrc", PLATFORM_DEVID_AUTO);
if (!musb) {
dev_err(&pdev->dev, "failed to allocate musb device\n");
- goto err1;
+ goto err0;
}
musb->dev.parent = &pdev->dev;
@@ -478,7 +478,7 @@ static int bfin_probe(struct platform_device *pdev)
glue->phy = usb_phy_generic_register();
if (IS_ERR(glue->phy))
- goto err2;
+ goto err1;
platform_set_drvdata(pdev, glue);
memset(musb_resources, 0x00, sizeof(*musb_resources) *
@@ -498,31 +498,28 @@ static int bfin_probe(struct platform_device *pdev)
ARRAY_SIZE(musb_resources));
if (ret) {
dev_err(&pdev->dev, "failed to add resources\n");
- goto err3;
+ goto err2;
}
ret = platform_device_add_data(musb, pdata, sizeof(*pdata));
if (ret) {
dev_err(&pdev->dev, "failed to add platform_data\n");
- goto err3;
+ goto err2;
}
ret = platform_device_add(musb);
if (ret) {
dev_err(&pdev->dev, "failed to register musb device\n");
- goto err3;
+ goto err2;
}
return 0;
-err3:
- usb_phy_generic_unregister(glue->phy);
-
err2:
- platform_device_put(musb);
+ usb_phy_generic_unregister(glue->phy);
err1:
- kfree(glue);
+ platform_device_put(musb);
err0:
return ret;
@@ -534,7 +531,6 @@ static int bfin_remove(struct platform_device *pdev)
platform_device_unregister(glue->musb);
usb_phy_generic_unregister(glue->phy);
- kfree(glue);
return 0;
}
diff --git a/drivers/usb/musb/davinci.c b/drivers/usb/musb/davinci.c
index de8492b06e46..110b78415bf0 100644
--- a/drivers/usb/musb/davinci.c
+++ b/drivers/usb/musb/davinci.c
@@ -519,23 +519,23 @@ static int davinci_probe(struct platform_device *pdev)
int ret = -ENOMEM;
- glue = kzalloc(sizeof(*glue), GFP_KERNEL);
+ glue = devm_kzalloc(&pdev->dev, sizeof(*glue), GFP_KERNEL);
if (!glue) {
dev_err(&pdev->dev, "failed to allocate glue context\n");
goto err0;
}
- clk = clk_get(&pdev->dev, "usb");
+ clk = devm_clk_get(&pdev->dev, "usb");
if (IS_ERR(clk)) {
dev_err(&pdev->dev, "failed to get clock\n");
ret = PTR_ERR(clk);
- goto err3;
+ goto err0;
}
ret = clk_enable(clk);
if (ret) {
dev_err(&pdev->dev, "failed to enable clock\n");
- goto err4;
+ goto err0;
}
glue->dev = &pdev->dev;
@@ -579,20 +579,14 @@ static int davinci_probe(struct platform_device *pdev)
if (IS_ERR(musb)) {
ret = PTR_ERR(musb);
dev_err(&pdev->dev, "failed to register musb device: %d\n", ret);
- goto err5;
+ goto err1;
}
return 0;
-err5:
+err1:
clk_disable(clk);
-err4:
- clk_put(clk);
-
-err3:
- kfree(glue);
-
err0:
return ret;
}
@@ -604,8 +598,6 @@ static int davinci_remove(struct platform_device *pdev)
platform_device_unregister(glue->musb);
usb_phy_generic_unregister();
clk_disable(glue->clk);
- clk_put(glue->clk);
- kfree(glue);
return 0;
}
diff --git a/drivers/usb/musb/jz4740.c b/drivers/usb/musb/jz4740.c
index 5f30537f1927..d1187290d4e3 100644
--- a/drivers/usb/musb/jz4740.c
+++ b/drivers/usb/musb/jz4740.c
@@ -19,6 +19,7 @@
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/platform_device.h>
+#include <linux/usb/usb_phy_generic.h>
#include "musb_core.h"
@@ -80,6 +81,7 @@ static struct musb_hdrc_platform_data jz4740_musb_platform_data = {
static int jz4740_musb_init(struct musb *musb)
{
+ usb_phy_generic_register();
musb->xceiv = usb_get_phy(USB_PHY_TYPE_USB2);
if (!musb->xceiv) {
pr_err("HS UDC: no transceiver configured\n");
@@ -182,6 +184,7 @@ static int jz4740_remove(struct platform_device *pdev)
struct jz4740_glue *glue = platform_get_drvdata(pdev);
platform_device_unregister(glue->musb);
+ usb_phy_generic_unregister(pdev);
clk_disable_unprepare(glue->clk);
return 0;
diff --git a/drivers/usb/musb/musb_core.c b/drivers/usb/musb/musb_core.c
index eff3c5cf84f4..b841ee0bff06 100644
--- a/drivers/usb/musb/musb_core.c
+++ b/drivers/usb/musb/musb_core.c
@@ -850,7 +850,8 @@ b_host:
/* handle babble condition */
if (int_usb & MUSB_INTR_BABBLE && is_host_active(musb))
- schedule_work(&musb->recover_work);
+ schedule_delayed_work(&musb->recover_work,
+ msecs_to_jiffies(100));
#if 0
/* REVISIT ... this would be for multiplexing periodic endpoints, or
@@ -1517,7 +1518,7 @@ irqreturn_t musb_interrupt(struct musb *musb)
devctl = musb_readb(musb->mregs, MUSB_DEVCTL);
dev_dbg(musb->controller, "** IRQ %s usb%04x tx%04x rx%04x\n",
- (devctl & MUSB_DEVCTL_HM) ? "host" : "peripheral",
+ is_host_active(musb) ? "host" : "peripheral",
musb->int_usb, musb->int_tx, musb->int_rx);
/* the core can interrupt us for multiple reasons; docs have
@@ -1531,7 +1532,7 @@ irqreturn_t musb_interrupt(struct musb *musb)
/* handle endpoint 0 first */
if (musb->int_tx & 1) {
- if (devctl & MUSB_DEVCTL_HM)
+ if (is_host_active(musb))
retval |= musb_h_ep0_irq(musb);
else
retval |= musb_g_ep0_irq(musb);
@@ -1545,7 +1546,7 @@ irqreturn_t musb_interrupt(struct musb *musb)
/* musb_ep_select(musb->mregs, ep_num); */
/* REVISIT just retval = ep->rx_irq(...) */
retval = IRQ_HANDLED;
- if (devctl & MUSB_DEVCTL_HM)
+ if (is_host_active(musb))
musb_host_rx(musb, ep_num);
else
musb_g_rx(musb, ep_num);
@@ -1563,7 +1564,7 @@ irqreturn_t musb_interrupt(struct musb *musb)
/* musb_ep_select(musb->mregs, ep_num); */
/* REVISIT just retval |= ep->tx_irq(...) */
retval = IRQ_HANDLED;
- if (devctl & MUSB_DEVCTL_HM)
+ if (is_host_active(musb))
musb_host_tx(musb, ep_num);
else
musb_g_tx(musb, ep_num);
@@ -1585,15 +1586,13 @@ MODULE_PARM_DESC(use_dma, "enable/disable use of DMA");
void musb_dma_completion(struct musb *musb, u8 epnum, u8 transmit)
{
- u8 devctl = musb_readb(musb->mregs, MUSB_DEVCTL);
-
/* called with controller lock already held */
if (!epnum) {
#ifndef CONFIG_USB_TUSB_OMAP_DMA
if (!is_cppi_enabled()) {
/* endpoint 0 */
- if (devctl & MUSB_DEVCTL_HM)
+ if (is_host_active(musb))
musb_h_ep0_irq(musb);
else
musb_g_ep0_irq(musb);
@@ -1602,13 +1601,13 @@ void musb_dma_completion(struct musb *musb, u8 epnum, u8 transmit)
} else {
/* endpoints 1..15 */
if (transmit) {
- if (devctl & MUSB_DEVCTL_HM)
+ if (is_host_active(musb))
musb_host_tx(musb, epnum);
else
musb_g_tx(musb, epnum);
} else {
/* receive */
- if (devctl & MUSB_DEVCTL_HM)
+ if (is_host_active(musb))
musb_host_rx(musb, epnum);
else
musb_g_rx(musb, epnum);
@@ -1753,20 +1752,22 @@ static void musb_irq_work(struct work_struct *data)
/* Recover from babble interrupt conditions */
static void musb_recover_work(struct work_struct *data)
{
- struct musb *musb = container_of(data, struct musb, recover_work);
- int status;
+ struct musb *musb = container_of(data, struct musb, recover_work.work);
+ int status, ret;
- musb_platform_reset(musb);
+ ret = musb_platform_reset(musb);
+ if (ret)
+ return;
usb_phy_vbus_off(musb->xceiv);
- udelay(100);
+ usleep_range(100, 200);
usb_phy_vbus_on(musb->xceiv);
- udelay(100);
+ usleep_range(100, 200);
/*
- * When a babble condition occurs, the musb controller removes the
- * session bit and the endpoint config is lost.
+ * When a babble condition occurs, the musb controller
+ * removes the session bit and the endpoint config is lost.
*/
if (musb->dyn_fifo)
status = ep_config_from_table(musb);
@@ -1945,7 +1946,7 @@ musb_init_controller(struct device *dev, int nIrq, void __iomem *ctrl)
/* Init IRQ workqueue before request_irq */
INIT_WORK(&musb->irq_work, musb_irq_work);
- INIT_WORK(&musb->recover_work, musb_recover_work);
+ INIT_DELAYED_WORK(&musb->recover_work, musb_recover_work);
INIT_DELAYED_WORK(&musb->deassert_reset_work, musb_deassert_reset);
INIT_DELAYED_WORK(&musb->finish_resume_work, musb_host_finish_resume);
@@ -2041,7 +2042,7 @@ fail4:
fail3:
cancel_work_sync(&musb->irq_work);
- cancel_work_sync(&musb->recover_work);
+ cancel_delayed_work_sync(&musb->recover_work);
cancel_delayed_work_sync(&musb->finish_resume_work);
cancel_delayed_work_sync(&musb->deassert_reset_work);
if (musb->dma_controller)
@@ -2107,7 +2108,7 @@ static int musb_remove(struct platform_device *pdev)
dma_controller_destroy(musb->dma_controller);
cancel_work_sync(&musb->irq_work);
- cancel_work_sync(&musb->recover_work);
+ cancel_delayed_work_sync(&musb->recover_work);
cancel_delayed_work_sync(&musb->finish_resume_work);
cancel_delayed_work_sync(&musb->deassert_reset_work);
musb_free(musb);
diff --git a/drivers/usb/musb/musb_core.h b/drivers/usb/musb/musb_core.h
index d155a156f240..414e57a984bb 100644
--- a/drivers/usb/musb/musb_core.h
+++ b/drivers/usb/musb/musb_core.h
@@ -192,7 +192,7 @@ struct musb_platform_ops {
int (*set_mode)(struct musb *musb, u8 mode);
void (*try_idle)(struct musb *musb, unsigned long timeout);
- void (*reset)(struct musb *musb);
+ int (*reset)(struct musb *musb);
int (*vbus_status)(struct musb *musb);
void (*set_vbus)(struct musb *musb, int on);
@@ -297,7 +297,7 @@ struct musb {
irqreturn_t (*isr)(int, void *);
struct work_struct irq_work;
- struct work_struct recover_work;
+ struct delayed_work recover_work;
struct delayed_work deassert_reset_work;
struct delayed_work finish_resume_work;
u16 hwvers;
@@ -555,10 +555,12 @@ static inline void musb_platform_try_idle(struct musb *musb,
musb->ops->try_idle(musb, timeout);
}
-static inline void musb_platform_reset(struct musb *musb)
+static inline int musb_platform_reset(struct musb *musb)
{
- if (musb->ops->reset)
- musb->ops->reset(musb);
+ if (!musb->ops->reset)
+ return -EINVAL;
+
+ return musb->ops->reset(musb);
}
static inline int musb_platform_get_vbus_status(struct musb *musb)
diff --git a/drivers/usb/musb/musb_cppi41.c b/drivers/usb/musb/musb_cppi41.c
index 5341bb223b7c..47ae6455d073 100644
--- a/drivers/usb/musb/musb_cppi41.c
+++ b/drivers/usb/musb/musb_cppi41.c
@@ -39,7 +39,6 @@ struct cppi41_dma_channel {
u32 transferred;
u32 packet_sz;
struct list_head tx_check;
- struct work_struct dma_completion;
};
#define MUSB_DMA_NUM_CHANNELS 15
@@ -74,15 +73,18 @@ static void save_rx_toggle(struct cppi41_dma_channel *cppi41_channel)
static void update_rx_toggle(struct cppi41_dma_channel *cppi41_channel)
{
+ struct musb_hw_ep *hw_ep = cppi41_channel->hw_ep;
+ struct musb *musb = hw_ep->musb;
u16 csr;
u8 toggle;
if (cppi41_channel->is_tx)
return;
- if (!is_host_active(cppi41_channel->controller->musb))
+ if (!is_host_active(musb))
return;
- csr = musb_readw(cppi41_channel->hw_ep->regs, MUSB_RXCSR);
+ musb_ep_select(musb->mregs, hw_ep->epnum);
+ csr = musb_readw(hw_ep->regs, MUSB_RXCSR);
toggle = csr & MUSB_RXCSR_H_DATATOGGLE ? 1 : 0;
/*
@@ -107,24 +109,13 @@ static bool musb_is_tx_fifo_empty(struct musb_hw_ep *hw_ep)
void __iomem *epio = musb->endpoints[epnum].regs;
u16 csr;
+ musb_ep_select(musb->mregs, hw_ep->epnum);
csr = musb_readw(epio, MUSB_TXCSR);
if (csr & MUSB_TXCSR_TXPKTRDY)
return false;
return true;
}
-static bool is_isoc(struct musb_hw_ep *hw_ep, bool in)
-{
- if (in && hw_ep->in_qh) {
- if (hw_ep->in_qh->type == USB_ENDPOINT_XFER_ISOC)
- return true;
- } else if (hw_ep->out_qh) {
- if (hw_ep->out_qh->type == USB_ENDPOINT_XFER_ISOC)
- return true;
- }
- return false;
-}
-
static void cppi41_dma_callback(void *private_data);
static void cppi41_trans_done(struct cppi41_dma_channel *cppi41_channel)
@@ -139,6 +130,7 @@ static void cppi41_trans_done(struct cppi41_dma_channel *cppi41_channel)
cppi41_channel->channel.actual_len =
cppi41_channel->transferred;
cppi41_channel->channel.status = MUSB_DMA_STATUS_FREE;
+ cppi41_channel->channel.rx_packet_done = true;
musb_dma_completion(musb, hw_ep->epnum, cppi41_channel->is_tx);
} else {
/* next iteration, reload */
@@ -172,6 +164,7 @@ static void cppi41_trans_done(struct cppi41_dma_channel *cppi41_channel)
dma_async_issue_pending(dc);
if (!cppi41_channel->is_tx) {
+ musb_ep_select(musb->mregs, hw_ep->epnum);
csr = musb_readw(epio, MUSB_RXCSR);
csr |= MUSB_RXCSR_H_REQPKT;
musb_writew(epio, MUSB_RXCSR, csr);
@@ -179,32 +172,6 @@ static void cppi41_trans_done(struct cppi41_dma_channel *cppi41_channel)
}
}
-static void cppi_trans_done_work(struct work_struct *work)
-{
- unsigned long flags;
- struct cppi41_dma_channel *cppi41_channel =
- container_of(work, struct cppi41_dma_channel, dma_completion);
- struct cppi41_dma_controller *controller = cppi41_channel->controller;
- struct musb *musb = controller->musb;
- struct musb_hw_ep *hw_ep = cppi41_channel->hw_ep;
- bool empty;
-
- if (!cppi41_channel->is_tx && is_isoc(hw_ep, 1)) {
- spin_lock_irqsave(&musb->lock, flags);
- cppi41_trans_done(cppi41_channel);
- spin_unlock_irqrestore(&musb->lock, flags);
- } else {
- empty = musb_is_tx_fifo_empty(hw_ep);
- if (empty) {
- spin_lock_irqsave(&musb->lock, flags);
- cppi41_trans_done(cppi41_channel);
- spin_unlock_irqrestore(&musb->lock, flags);
- } else {
- schedule_work(&cppi41_channel->dma_completion);
- }
- }
-}
-
static enum hrtimer_restart cppi41_recheck_tx_req(struct hrtimer *timer)
{
struct cppi41_dma_controller *controller;
@@ -233,7 +200,7 @@ static enum hrtimer_restart cppi41_recheck_tx_req(struct hrtimer *timer)
if (!list_empty(&controller->early_tx_list)) {
ret = HRTIMER_RESTART;
hrtimer_forward_now(&controller->early_tx,
- ktime_set(0, 150 * NSEC_PER_USEC));
+ ktime_set(0, 50 * NSEC_PER_USEC));
}
spin_unlock_irqrestore(&musb->lock, flags);
@@ -268,14 +235,6 @@ static void cppi41_dma_callback(void *private_data)
transferred < cppi41_channel->packet_sz)
cppi41_channel->prog_len = 0;
- if (!cppi41_channel->is_tx) {
- if (is_isoc(hw_ep, 1))
- schedule_work(&cppi41_channel->dma_completion);
- else
- cppi41_trans_done(cppi41_channel);
- goto out;
- }
-
empty = musb_is_tx_fifo_empty(hw_ep);
if (empty) {
cppi41_trans_done(cppi41_channel);
@@ -312,15 +271,13 @@ static void cppi41_dma_callback(void *private_data)
goto out;
}
}
- if (is_isoc(hw_ep, 0)) {
- schedule_work(&cppi41_channel->dma_completion);
- goto out;
- }
list_add_tail(&cppi41_channel->tx_check,
&controller->early_tx_list);
if (!hrtimer_is_queued(&controller->early_tx)) {
+ unsigned long usecs = cppi41_channel->total_len / 10;
+
hrtimer_start_range_ns(&controller->early_tx,
- ktime_set(0, 140 * NSEC_PER_USEC),
+ ktime_set(0, usecs * NSEC_PER_USEC),
40 * NSEC_PER_USEC,
HRTIMER_MODE_REL);
}
@@ -450,6 +407,7 @@ static bool cppi41_configure_channel(struct dma_channel *channel,
dma_desc->callback = cppi41_dma_callback;
dma_desc->callback_param = channel;
cppi41_channel->cookie = dma_desc->tx_submit(dma_desc);
+ cppi41_channel->channel.rx_packet_done = false;
save_rx_toggle(cppi41_channel);
dma_async_issue_pending(dc);
@@ -672,8 +630,6 @@ static int cppi41_dma_controller_start(struct cppi41_dma_controller *controller)
cppi41_channel->port_num = port;
cppi41_channel->is_tx = is_tx;
INIT_LIST_HEAD(&cppi41_channel->tx_check);
- INIT_WORK(&cppi41_channel->dma_completion,
- cppi_trans_done_work);
musb_dma = &cppi41_channel->channel;
musb_dma->private_data = cppi41_channel;
diff --git a/drivers/usb/musb/musb_dma.h b/drivers/usb/musb/musb_dma.h
index 1345a4ff041a..1d44faa86252 100644
--- a/drivers/usb/musb/musb_dma.h
+++ b/drivers/usb/musb/musb_dma.h
@@ -129,6 +129,7 @@ struct dma_channel {
size_t actual_len;
enum dma_channel_status status;
bool desired_mode;
+ bool rx_packet_done;
};
/*
diff --git a/drivers/usb/musb/musb_dsps.c b/drivers/usb/musb/musb_dsps.c
index 09529f94e72d..c791ba5da91a 100644
--- a/drivers/usb/musb/musb_dsps.c
+++ b/drivers/usb/musb/musb_dsps.c
@@ -56,16 +56,24 @@ static const struct of_device_id musb_dsps_of_match[];
* dependent on musb core layer symbols.
*/
static inline u8 dsps_readb(const void __iomem *addr, unsigned offset)
- { return __raw_readb(addr + offset); }
+{
+ return __raw_readb(addr + offset);
+}
static inline u32 dsps_readl(const void __iomem *addr, unsigned offset)
- { return __raw_readl(addr + offset); }
+{
+ return __raw_readl(addr + offset);
+}
static inline void dsps_writeb(void __iomem *addr, unsigned offset, u8 data)
- { __raw_writeb(data, addr + offset); }
+{
+ __raw_writeb(data, addr + offset);
+}
static inline void dsps_writel(void __iomem *addr, unsigned offset, u32 data)
- { __raw_writel(data, addr + offset); }
+{
+ __raw_writel(data, addr + offset);
+}
/**
* DSPS musb wrapper register offset.
@@ -136,6 +144,7 @@ struct dsps_glue {
const struct dsps_musb_wrapper *wrp; /* wrapper register offsets */
struct timer_list timer; /* otg_workaround timer */
unsigned long last_timer; /* last timer data for each instance */
+ bool sw_babble_enabled;
struct dsps_context context;
struct debugfs_regset32 regset;
@@ -469,6 +478,19 @@ static int dsps_musb_init(struct musb *musb)
val &= ~(1 << wrp->otg_disable);
dsps_writel(musb->ctrl_base, wrp->phy_utmi, val);
+ /*
+ * Check whether the dsps version has babble control enabled.
+ * In latest silicon revision the babble control logic is enabled.
+ * If MUSB_BABBLE_CTL returns 0x4 then we have the babble control
+ * logic enabled.
+ */
+ val = dsps_readb(musb->mregs, MUSB_BABBLE_CTL);
+ if (val == MUSB_BABBLE_RCV_DISABLE) {
+ glue->sw_babble_enabled = true;
+ val |= MUSB_BABBLE_SW_SESSION_CTRL;
+ dsps_writeb(musb->mregs, MUSB_BABBLE_CTL, val);
+ }
+
ret = dsps_musb_dbg_init(musb, glue);
if (ret)
return ret;
@@ -535,14 +557,82 @@ static int dsps_musb_set_mode(struct musb *musb, u8 mode)
return 0;
}
-static void dsps_musb_reset(struct musb *musb)
+static bool sw_babble_control(struct musb *musb)
+{
+ u8 babble_ctl;
+ bool session_restart = false;
+
+ babble_ctl = dsps_readb(musb->mregs, MUSB_BABBLE_CTL);
+ dev_dbg(musb->controller, "babble: MUSB_BABBLE_CTL value %x\n",
+ babble_ctl);
+ /*
+ * check line monitor flag to check whether babble is
+ * due to noise
+ */
+ dev_dbg(musb->controller, "STUCK_J is %s\n",
+ babble_ctl & MUSB_BABBLE_STUCK_J ? "set" : "reset");
+
+ if (babble_ctl & MUSB_BABBLE_STUCK_J) {
+ int timeout = 10;
+
+ /*
+ * babble is due to noise, then set transmit idle (d7 bit)
+ * to resume normal operation
+ */
+ babble_ctl = dsps_readb(musb->mregs, MUSB_BABBLE_CTL);
+ babble_ctl |= MUSB_BABBLE_FORCE_TXIDLE;
+ dsps_writeb(musb->mregs, MUSB_BABBLE_CTL, babble_ctl);
+
+ /* wait till line monitor flag cleared */
+ dev_dbg(musb->controller, "Set TXIDLE, wait J to clear\n");
+ do {
+ babble_ctl = dsps_readb(musb->mregs, MUSB_BABBLE_CTL);
+ udelay(1);
+ } while ((babble_ctl & MUSB_BABBLE_STUCK_J) && timeout--);
+
+ /* check whether stuck_at_j bit cleared */
+ if (babble_ctl & MUSB_BABBLE_STUCK_J) {
+ /*
+ * real babble condition has occurred
+ * restart the controller to start the
+ * session again
+ */
+ dev_dbg(musb->controller, "J not cleared, misc (%x)\n",
+ babble_ctl);
+ session_restart = true;
+ }
+ } else {
+ session_restart = true;
+ }
+
+ return session_restart;
+}
+
+static int dsps_musb_reset(struct musb *musb)
{
struct device *dev = musb->controller;
struct dsps_glue *glue = dev_get_drvdata(dev->parent);
const struct dsps_musb_wrapper *wrp = glue->wrp;
+ int session_restart = 0;
+
+ if (glue->sw_babble_enabled)
+ session_restart = sw_babble_control(musb);
+ /*
+ * In case of new silicon version babble condition can be recovered
+ * without resetting the MUSB. But for older silicon versions, MUSB
+ * reset is needed
+ */
+ if (session_restart || !glue->sw_babble_enabled) {
+ dev_info(musb->controller, "Restarting MUSB to recover from Babble\n");
+ dsps_writel(musb->ctrl_base, wrp->control, (1 << wrp->reset));
+ usleep_range(100, 200);
+ usb_phy_shutdown(musb->xceiv);
+ usleep_range(100, 200);
+ usb_phy_init(musb->xceiv);
+ session_restart = 1;
+ }
- dsps_writel(musb->ctrl_base, wrp->control, (1 << wrp->reset));
- udelay(100);
+ return !session_restart;
}
static struct musb_platform_ops dsps_ops = {
diff --git a/drivers/usb/musb/musb_host.c b/drivers/usb/musb/musb_host.c
index eb06291a40c8..855793d701bb 100644
--- a/drivers/usb/musb/musb_host.c
+++ b/drivers/usb/musb/musb_host.c
@@ -120,7 +120,7 @@ static void musb_h_tx_flush_fifo(struct musb_hw_ep *ep)
if (csr != lastcsr)
dev_dbg(musb->controller, "Host TX FIFONOTEMPTY csr: %02x\n", csr);
lastcsr = csr;
- csr |= MUSB_TXCSR_FLUSHFIFO;
+ csr |= MUSB_TXCSR_FLUSHFIFO | MUSB_TXCSR_TXPKTRDY;
musb_writew(epio, MUSB_TXCSR, csr);
csr = musb_readw(epio, MUSB_TXCSR);
if (WARN(retries-- < 1,
@@ -1295,7 +1295,7 @@ done:
if (status) {
if (dma_channel_status(dma) == MUSB_DMA_STATUS_BUSY) {
dma->status = MUSB_DMA_STATUS_CORE_ABORT;
- (void) musb->dma_controller->channel_abort(dma);
+ musb->dma_controller->channel_abort(dma);
}
/* do the proper sequence to abort the transfer in the
@@ -1640,7 +1640,7 @@ void musb_host_rx(struct musb *musb, u8 epnum)
/* clean up dma and collect transfer count */
if (dma_channel_status(dma) == MUSB_DMA_STATUS_BUSY) {
dma->status = MUSB_DMA_STATUS_CORE_ABORT;
- (void) musb->dma_controller->channel_abort(dma);
+ musb->dma_controller->channel_abort(dma);
xfer_len = dma->actual_len;
}
musb_h_flush_rxfifo(hw_ep, MUSB_RXCSR_CLRDATATOG);
@@ -1671,7 +1671,7 @@ void musb_host_rx(struct musb *musb, u8 epnum)
*/
if (dma_channel_status(dma) == MUSB_DMA_STATUS_BUSY) {
dma->status = MUSB_DMA_STATUS_CORE_ABORT;
- (void) musb->dma_controller->channel_abort(dma);
+ musb->dma_controller->channel_abort(dma);
xfer_len = dma->actual_len;
done = true;
}
@@ -1734,10 +1734,11 @@ void musb_host_rx(struct musb *musb, u8 epnum)
}
} else {
- /* done if urb buffer is full or short packet is recd */
- done = (urb->actual_length + xfer_len >=
- urb->transfer_buffer_length
- || dma->actual_len < qh->maxpacket);
+ /* done if urb buffer is full or short packet is recd */
+ done = (urb->actual_length + xfer_len >=
+ urb->transfer_buffer_length
+ || dma->actual_len < qh->maxpacket
+ || dma->rx_packet_done);
}
/* send IN token for next packet, without AUTOREQ */
@@ -1957,7 +1958,7 @@ static int musb_schedule(
struct musb_qh *qh,
int is_in)
{
- int idle;
+ int idle = 0;
int best_diff;
int best_end, epnum;
struct musb_hw_ep *hw_ep = NULL;
diff --git a/drivers/usb/musb/musb_regs.h b/drivers/usb/musb/musb_regs.h
index 03f2655af290..b9bcda5e3945 100644
--- a/drivers/usb/musb/musb_regs.h
+++ b/drivers/usb/musb/musb_regs.h
@@ -72,6 +72,12 @@
#define MUSB_DEVCTL_HR 0x02
#define MUSB_DEVCTL_SESSION 0x01
+/* BABBLE_CTL */
+#define MUSB_BABBLE_FORCE_TXIDLE 0x80
+#define MUSB_BABBLE_SW_SESSION_CTRL 0x40
+#define MUSB_BABBLE_STUCK_J 0x20
+#define MUSB_BABBLE_RCV_DISABLE 0x04
+
/* MUSB ULPI VBUSCONTROL */
#define MUSB_ULPI_USE_EXTVBUS 0x01
#define MUSB_ULPI_USE_EXTVBUSIND 0x02
@@ -246,6 +252,7 @@
*/
#define MUSB_DEVCTL 0x60 /* 8 bit */
+#define MUSB_BABBLE_CTL 0x61 /* 8 bit */
/* These are always controlled through the INDEX register */
#define MUSB_TXFIFOSZ 0x62 /* 8-bit (see masks) */
diff --git a/drivers/usb/musb/tusb6010.c b/drivers/usb/musb/tusb6010.c
index 159ef4be1ef2..7dfc6cb732c9 100644
--- a/drivers/usb/musb/tusb6010.c
+++ b/drivers/usb/musb/tusb6010.c
@@ -22,6 +22,7 @@
#include <linux/usb.h>
#include <linux/irq.h>
#include <linux/io.h>
+#include <linux/device.h>
#include <linux/platform_device.h>
#include <linux/dma-mapping.h>
#include <linux/usb/usb_phy_generic.h>
@@ -1160,12 +1161,12 @@ static int tusb_probe(struct platform_device *pdev)
struct platform_device *musb;
struct tusb6010_glue *glue;
struct platform_device_info pinfo;
- int ret = -ENOMEM;
+ int ret;
- glue = kzalloc(sizeof(*glue), GFP_KERNEL);
+ glue = devm_kzalloc(&pdev->dev, sizeof(*glue), GFP_KERNEL);
if (!glue) {
dev_err(&pdev->dev, "failed to allocate glue context\n");
- goto err0;
+ return -ENOMEM;
}
glue->dev = &pdev->dev;
@@ -1204,16 +1205,10 @@ static int tusb_probe(struct platform_device *pdev)
if (IS_ERR(musb)) {
ret = PTR_ERR(musb);
dev_err(&pdev->dev, "failed to register musb device: %d\n", ret);
- goto err3;
+ return ret;
}
return 0;
-
-err3:
- kfree(glue);
-
-err0:
- return ret;
}
static int tusb_remove(struct platform_device *pdev)
@@ -1222,7 +1217,6 @@ static int tusb_remove(struct platform_device *pdev)
platform_device_unregister(glue->musb);
usb_phy_generic_unregister(glue->phy);
- kfree(glue);
return 0;
}
diff --git a/drivers/usb/musb/ux500.c b/drivers/usb/musb/ux500.c
index f202e5088461..dc666e96f45f 100644
--- a/drivers/usb/musb/ux500.c
+++ b/drivers/usb/musb/ux500.c
@@ -246,7 +246,7 @@ static int ux500_probe(struct platform_device *pdev)
}
}
- glue = kzalloc(sizeof(*glue), GFP_KERNEL);
+ glue = devm_kzalloc(&pdev->dev, sizeof(*glue), GFP_KERNEL);
if (!glue) {
dev_err(&pdev->dev, "failed to allocate glue context\n");
goto err0;
@@ -255,20 +255,20 @@ static int ux500_probe(struct platform_device *pdev)
musb = platform_device_alloc("musb-hdrc", PLATFORM_DEVID_AUTO);
if (!musb) {
dev_err(&pdev->dev, "failed to allocate musb device\n");
- goto err1;
+ goto err0;
}
- clk = clk_get(&pdev->dev, NULL);
+ clk = devm_clk_get(&pdev->dev, NULL);
if (IS_ERR(clk)) {
dev_err(&pdev->dev, "failed to get clock\n");
ret = PTR_ERR(clk);
- goto err3;
+ goto err1;
}
ret = clk_prepare_enable(clk);
if (ret) {
dev_err(&pdev->dev, "failed to enable clock\n");
- goto err4;
+ goto err1;
}
musb->dev.parent = &pdev->dev;
@@ -301,34 +301,28 @@ static int ux500_probe(struct platform_device *pdev)
ARRAY_SIZE(musb_resources));
if (ret) {
dev_err(&pdev->dev, "failed to add resources\n");
- goto err5;
+ goto err2;
}
ret = platform_device_add_data(musb, pdata, sizeof(*pdata));
if (ret) {
dev_err(&pdev->dev, "failed to add platform_data\n");
- goto err5;
+ goto err2;
}
ret = platform_device_add(musb);
if (ret) {
dev_err(&pdev->dev, "failed to register musb device\n");
- goto err5;
+ goto err2;
}
return 0;
-err5:
+err2:
clk_disable_unprepare(clk);
-err4:
- clk_put(clk);
-
-err3:
- platform_device_put(musb);
-
err1:
- kfree(glue);
+ platform_device_put(musb);
err0:
return ret;
@@ -340,8 +334,6 @@ static int ux500_remove(struct platform_device *pdev)
platform_device_unregister(glue->musb);
clk_disable_unprepare(glue->clk);
- clk_put(glue->clk);
- kfree(glue);
return 0;
}
diff --git a/drivers/usb/phy/phy-am335x.c b/drivers/usb/phy/phy-am335x.c
index 585e50cb1980..b70e05537180 100644
--- a/drivers/usb/phy/phy-am335x.c
+++ b/drivers/usb/phy/phy-am335x.c
@@ -122,16 +122,10 @@ static int am335x_phy_resume(struct device *dev)
return 0;
}
-
-static const struct dev_pm_ops am335x_pm_ops = {
- SET_SYSTEM_SLEEP_PM_OPS(am335x_phy_suspend, am335x_phy_resume)
-};
-
-#define DEV_PM_OPS (&am335x_pm_ops)
-#else
-#define DEV_PM_OPS NULL
#endif
+static SIMPLE_DEV_PM_OPS(am335x_pm_ops, am335x_phy_suspend, am335x_phy_resume);
+
static const struct of_device_id am335x_phy_ids[] = {
{ .compatible = "ti,am335x-usb-phy" },
{ }
@@ -144,7 +138,7 @@ static struct platform_driver am335x_phy_driver = {
.driver = {
.name = "am335x-phy-driver",
.owner = THIS_MODULE,
- .pm = DEV_PM_OPS,
+ .pm = &am335x_pm_ops,
.of_match_table = am335x_phy_ids,
},
};
diff --git a/drivers/usb/phy/phy-gpio-vbus-usb.c b/drivers/usb/phy/phy-gpio-vbus-usb.c
index 69462e09d014..ea9e705555df 100644
--- a/drivers/usb/phy/phy-gpio-vbus-usb.c
+++ b/drivers/usb/phy/phy-gpio-vbus-usb.c
@@ -253,11 +253,13 @@ static int gpio_vbus_probe(struct platform_device *pdev)
return -EINVAL;
gpio = pdata->gpio_vbus;
- gpio_vbus = kzalloc(sizeof(struct gpio_vbus_data), GFP_KERNEL);
+ gpio_vbus = devm_kzalloc(&pdev->dev, sizeof(struct gpio_vbus_data),
+ GFP_KERNEL);
if (!gpio_vbus)
return -ENOMEM;
- gpio_vbus->phy.otg = kzalloc(sizeof(struct usb_otg), GFP_KERNEL);
+ gpio_vbus->phy.otg = devm_kzalloc(&pdev->dev, sizeof(struct usb_otg),
+ GFP_KERNEL);
if (!gpio_vbus->phy.otg) {
kfree(gpio_vbus);
return -ENOMEM;
@@ -274,11 +276,11 @@ static int gpio_vbus_probe(struct platform_device *pdev)
gpio_vbus->phy.otg->phy = &gpio_vbus->phy;
gpio_vbus->phy.otg->set_peripheral = gpio_vbus_set_peripheral;
- err = gpio_request(gpio, "vbus_detect");
+ err = devm_gpio_request(&pdev->dev, gpio, "vbus_detect");
if (err) {
dev_err(&pdev->dev, "can't request vbus gpio %d, err: %d\n",
gpio, err);
- goto err_gpio;
+ return err;
}
gpio_direction_input(gpio);
@@ -296,27 +298,27 @@ static int gpio_vbus_probe(struct platform_device *pdev)
/* if data line pullup is in use, initialize it to "not pulling up" */
gpio = pdata->gpio_pullup;
if (gpio_is_valid(gpio)) {
- err = gpio_request(gpio, "udc_pullup");
+ err = devm_gpio_request(&pdev->dev, gpio, "udc_pullup");
if (err) {
dev_err(&pdev->dev,
"can't request pullup gpio %d, err: %d\n",
gpio, err);
- gpio_free(pdata->gpio_vbus);
- goto err_gpio;
+ return err;
}
gpio_direction_output(gpio, pdata->gpio_pullup_inverted);
}
- err = request_irq(irq, gpio_vbus_irq, irqflags, "vbus_detect", pdev);
+ err = devm_request_irq(&pdev->dev, irq, gpio_vbus_irq, irqflags,
+ "vbus_detect", pdev);
if (err) {
dev_err(&pdev->dev, "can't request irq %i, err: %d\n",
irq, err);
- goto err_irq;
+ return err;
}
INIT_DELAYED_WORK(&gpio_vbus->work, gpio_vbus_work);
- gpio_vbus->vbus_draw = regulator_get(&pdev->dev, "vbus_draw");
+ gpio_vbus->vbus_draw = devm_regulator_get(&pdev->dev, "vbus_draw");
if (IS_ERR(gpio_vbus->vbus_draw)) {
dev_dbg(&pdev->dev, "can't get vbus_draw regulator, err: %ld\n",
PTR_ERR(gpio_vbus->vbus_draw));
@@ -328,44 +330,23 @@ static int gpio_vbus_probe(struct platform_device *pdev)
if (err) {
dev_err(&pdev->dev, "can't register transceiver, err: %d\n",
err);
- goto err_otg;
+ return err;
}
device_init_wakeup(&pdev->dev, pdata->wakeup);
return 0;
-err_otg:
- regulator_put(gpio_vbus->vbus_draw);
- free_irq(irq, pdev);
-err_irq:
- if (gpio_is_valid(pdata->gpio_pullup))
- gpio_free(pdata->gpio_pullup);
- gpio_free(pdata->gpio_vbus);
-err_gpio:
- kfree(gpio_vbus->phy.otg);
- kfree(gpio_vbus);
- return err;
}
static int gpio_vbus_remove(struct platform_device *pdev)
{
struct gpio_vbus_data *gpio_vbus = platform_get_drvdata(pdev);
- struct gpio_vbus_mach_info *pdata = dev_get_platdata(&pdev->dev);
- int gpio = pdata->gpio_vbus;
device_init_wakeup(&pdev->dev, 0);
cancel_delayed_work_sync(&gpio_vbus->work);
- regulator_put(gpio_vbus->vbus_draw);
usb_remove_phy(&gpio_vbus->phy);
- free_irq(gpio_vbus->irq, pdev);
- if (gpio_is_valid(pdata->gpio_pullup))
- gpio_free(pdata->gpio_pullup);
- gpio_free(gpio);
- kfree(gpio_vbus->phy.otg);
- kfree(gpio_vbus);
-
return 0;
}
diff --git a/drivers/usb/phy/phy-msm-usb.c b/drivers/usb/phy/phy-msm-usb.c
index c929370cdaa6..e4108eec5ef4 100644
--- a/drivers/usb/phy/phy-msm-usb.c
+++ b/drivers/usb/phy/phy-msm-usb.c
@@ -279,11 +279,11 @@ static int msm_otg_link_clk_reset(struct msm_otg *motg, bool assert)
static int msm_otg_phy_clk_reset(struct msm_otg *motg)
{
- int ret;
+ int ret = 0;
- if (motg->pdata->phy_clk_reset)
+ if (motg->pdata->phy_clk_reset && motg->phy_reset_clk)
ret = motg->pdata->phy_clk_reset(motg->phy_reset_clk);
- else
+ else if (motg->phy_rst)
ret = reset_control_reset(motg->phy_rst);
if (ret)
@@ -1429,7 +1429,7 @@ static void msm_otg_debugfs_cleanup(void)
debugfs_remove(msm_otg_dbg_root);
}
-static struct of_device_id msm_otg_dt_match[] = {
+static const struct of_device_id msm_otg_dt_match[] = {
{
.compatible = "qcom,usb-otg-ci",
.data = (void *) CI_45NM_INTEGRATED_PHY
@@ -1466,7 +1466,7 @@ static int msm_otg_read_dt(struct platform_device *pdev, struct msm_otg *motg)
motg->phy_rst = devm_reset_control_get(&pdev->dev, "phy");
if (IS_ERR(motg->phy_rst))
- return PTR_ERR(motg->phy_rst);
+ motg->phy_rst = NULL;
pdata->mode = of_usb_get_dr_mode(node);
if (pdata->mode == USB_DR_MODE_UNKNOWN)
@@ -1558,7 +1558,7 @@ static int msm_otg_probe(struct platform_device *pdev)
np ? "phy" : "usb_phy_clk");
if (IS_ERR(motg->phy_reset_clk)) {
dev_err(&pdev->dev, "failed to get usb_phy_clk\n");
- return PTR_ERR(motg->phy_reset_clk);
+ motg->phy_reset_clk = NULL;
}
motg->clk = devm_clk_get(&pdev->dev, np ? "core" : "usb_hs_clk");
diff --git a/drivers/usb/phy/phy-tegra-usb.c b/drivers/usb/phy/phy-tegra-usb.c
index 50dc69e1666f..13b4fa287da8 100644
--- a/drivers/usb/phy/phy-tegra-usb.c
+++ b/drivers/usb/phy/phy-tegra-usb.c
@@ -962,7 +962,7 @@ static const struct tegra_phy_soc_config tegra30_soc_config = {
.requires_extra_tuning_parameters = true,
};
-static struct of_device_id tegra_usb_phy_id_table[] = {
+static const struct of_device_id tegra_usb_phy_id_table[] = {
{ .compatible = "nvidia,tegra30-usb-phy", .data = &tegra30_soc_config },
{ .compatible = "nvidia,tegra20-usb-phy", .data = &tegra20_soc_config },
{ },
diff --git a/drivers/usb/renesas_usbhs/Makefile b/drivers/usb/renesas_usbhs/Makefile
index bc8aef4311a1..9e47f477b6d2 100644
--- a/drivers/usb/renesas_usbhs/Makefile
+++ b/drivers/usb/renesas_usbhs/Makefile
@@ -4,7 +4,7 @@
obj-$(CONFIG_USB_RENESAS_USBHS) += renesas_usbhs.o
-renesas_usbhs-y := common.o mod.o pipe.o fifo.o
+renesas_usbhs-y := common.o mod.o pipe.o fifo.o rcar2.o
ifneq ($(CONFIG_USB_RENESAS_USBHS_HCD),)
renesas_usbhs-y += mod_host.o
diff --git a/drivers/usb/renesas_usbhs/common.c b/drivers/usb/renesas_usbhs/common.c
index 17267b0a2e95..1b9bf8d83235 100644
--- a/drivers/usb/renesas_usbhs/common.c
+++ b/drivers/usb/renesas_usbhs/common.c
@@ -15,12 +15,14 @@
*
*/
#include <linux/err.h>
+#include <linux/gpio.h>
#include <linux/io.h>
#include <linux/module.h>
#include <linux/pm_runtime.h>
#include <linux/slab.h>
#include <linux/sysfs.h>
#include "common.h"
+#include "rcar2.h"
/*
* image of renesas_usbhs
@@ -284,6 +286,8 @@ static void usbhsc_set_buswait(struct usbhs_priv *priv)
/*
* platform default param
*/
+
+/* commonly used on old SH-Mobile SoCs */
static u32 usbhsc_default_pipe_type[] = {
USB_ENDPOINT_XFER_CONTROL,
USB_ENDPOINT_XFER_ISOC,
@@ -297,6 +301,26 @@ static u32 usbhsc_default_pipe_type[] = {
USB_ENDPOINT_XFER_INT,
};
+/* commonly used on newer SH-Mobile and R-Car SoCs */
+static u32 usbhsc_new_pipe_type[] = {
+ USB_ENDPOINT_XFER_CONTROL,
+ USB_ENDPOINT_XFER_ISOC,
+ USB_ENDPOINT_XFER_ISOC,
+ USB_ENDPOINT_XFER_BULK,
+ USB_ENDPOINT_XFER_BULK,
+ USB_ENDPOINT_XFER_BULK,
+ USB_ENDPOINT_XFER_INT,
+ USB_ENDPOINT_XFER_INT,
+ USB_ENDPOINT_XFER_INT,
+ USB_ENDPOINT_XFER_BULK,
+ USB_ENDPOINT_XFER_BULK,
+ USB_ENDPOINT_XFER_BULK,
+ USB_ENDPOINT_XFER_BULK,
+ USB_ENDPOINT_XFER_BULK,
+ USB_ENDPOINT_XFER_BULK,
+ USB_ENDPOINT_XFER_BULK,
+};
+
/*
* power control
*/
@@ -423,8 +447,7 @@ static int usbhs_probe(struct platform_device *pdev)
int ret;
/* check platform information */
- if (!info ||
- !info->platform_callback.get_id) {
+ if (!info) {
dev_err(&pdev->dev, "no platform information\n");
return -EINVAL;
}
@@ -451,13 +474,32 @@ static int usbhs_probe(struct platform_device *pdev)
/*
* care platform info
*/
- memcpy(&priv->pfunc,
- &info->platform_callback,
- sizeof(struct renesas_usbhs_platform_callback));
+
memcpy(&priv->dparam,
&info->driver_param,
sizeof(struct renesas_usbhs_driver_param));
+ switch (priv->dparam.type) {
+ case USBHS_TYPE_R8A7790:
+ case USBHS_TYPE_R8A7791:
+ priv->pfunc = usbhs_rcar2_ops;
+ if (!priv->dparam.pipe_type) {
+ priv->dparam.pipe_type = usbhsc_new_pipe_type;
+ priv->dparam.pipe_size =
+ ARRAY_SIZE(usbhsc_new_pipe_type);
+ }
+ break;
+ default:
+ if (!info->platform_callback.get_id) {
+ dev_err(&pdev->dev, "no platform callbacks");
+ return -EINVAL;
+ }
+ memcpy(&priv->pfunc,
+ &info->platform_callback,
+ sizeof(struct renesas_usbhs_platform_callback));
+ break;
+ }
+
/* set driver callback functions for platform */
dfunc = &info->driver_callback;
dfunc->notify_hotplug = usbhsc_drvcllbck_notify_hotplug;
@@ -507,6 +549,20 @@ static int usbhs_probe(struct platform_device *pdev)
*/
usbhs_sys_clock_ctrl(priv, 0);
+ /* check GPIO determining if USB function should be enabled */
+ if (priv->dparam.enable_gpio) {
+ gpio_request_one(priv->dparam.enable_gpio, GPIOF_IN, NULL);
+ ret = !gpio_get_value(priv->dparam.enable_gpio);
+ gpio_free(priv->dparam.enable_gpio);
+ if (ret) {
+ dev_warn(&pdev->dev,
+ "USB function not selected (GPIO %d)\n",
+ priv->dparam.enable_gpio);
+ ret = -ENOTSUPP;
+ goto probe_end_mod_exit;
+ }
+ }
+
/*
* platform call
*
diff --git a/drivers/usb/renesas_usbhs/common.h b/drivers/usb/renesas_usbhs/common.h
index c69dd2fba360..a7996da6a1bd 100644
--- a/drivers/usb/renesas_usbhs/common.h
+++ b/drivers/usb/renesas_usbhs/common.h
@@ -268,6 +268,8 @@ struct usbhs_priv {
* fifo control
*/
struct usbhs_fifo_info fifo_info;
+
+ struct usb_phy *phy;
};
/*
diff --git a/drivers/usb/renesas_usbhs/mod_gadget.c b/drivers/usb/renesas_usbhs/mod_gadget.c
index 458f3766bef1..04e6505777d0 100644
--- a/drivers/usb/renesas_usbhs/mod_gadget.c
+++ b/drivers/usb/renesas_usbhs/mod_gadget.c
@@ -600,8 +600,10 @@ static int usbhsg_ep_enable(struct usb_ep *ep,
static int usbhsg_ep_disable(struct usb_ep *ep)
{
struct usbhsg_uep *uep = usbhsg_ep_to_uep(ep);
+ struct usbhs_pipe *pipe = usbhsg_uep_to_pipe(uep);
usbhsg_pipe_disable(uep);
+ usbhs_pipe_free(pipe);
uep->pipe->mod_private = NULL;
uep->pipe = NULL;
diff --git a/drivers/usb/renesas_usbhs/pipe.c b/drivers/usb/renesas_usbhs/pipe.c
index 7926e1c700f1..75fbcf6b102e 100644
--- a/drivers/usb/renesas_usbhs/pipe.c
+++ b/drivers/usb/renesas_usbhs/pipe.c
@@ -640,6 +640,11 @@ static struct usbhs_pipe *usbhsp_get_pipe(struct usbhs_priv *priv, u32 type)
return pipe;
}
+static void usbhsp_put_pipe(struct usbhs_pipe *pipe)
+{
+ usbhsp_flags_init(pipe);
+}
+
void usbhs_pipe_init(struct usbhs_priv *priv,
int (*dma_map_ctrl)(struct usbhs_pkt *pkt, int map))
{
@@ -710,6 +715,7 @@ struct usbhs_pipe *usbhs_pipe_malloc(struct usbhs_priv *priv,
usbhsp_pipe_select(pipe);
usbhsp_pipe_cfg_set(pipe, 0xFFFF, pipecfg);
usbhsp_pipe_buf_set(pipe, 0xFFFF, pipebuf);
+ usbhs_pipe_clear(pipe);
usbhs_pipe_sequence_data0(pipe);
@@ -726,6 +732,11 @@ struct usbhs_pipe *usbhs_pipe_malloc(struct usbhs_priv *priv,
return pipe;
}
+void usbhs_pipe_free(struct usbhs_pipe *pipe)
+{
+ usbhsp_put_pipe(pipe);
+}
+
void usbhs_pipe_select_fifo(struct usbhs_pipe *pipe, struct usbhs_fifo *fifo)
{
if (pipe->fifo)
diff --git a/drivers/usb/renesas_usbhs/pipe.h b/drivers/usb/renesas_usbhs/pipe.h
index 3e5349879838..406f36d050e4 100644
--- a/drivers/usb/renesas_usbhs/pipe.h
+++ b/drivers/usb/renesas_usbhs/pipe.h
@@ -75,6 +75,7 @@ struct usbhs_pipe_info {
char *usbhs_pipe_name(struct usbhs_pipe *pipe);
struct usbhs_pipe
*usbhs_pipe_malloc(struct usbhs_priv *priv, int endpoint_type, int dir_in);
+void usbhs_pipe_free(struct usbhs_pipe *pipe);
int usbhs_pipe_probe(struct usbhs_priv *priv);
void usbhs_pipe_remove(struct usbhs_priv *priv);
int usbhs_pipe_is_dir_in(struct usbhs_pipe *pipe);
diff --git a/drivers/usb/renesas_usbhs/rcar2.c b/drivers/usb/renesas_usbhs/rcar2.c
new file mode 100644
index 000000000000..e6b9dcc1c289
--- /dev/null
+++ b/drivers/usb/renesas_usbhs/rcar2.c
@@ -0,0 +1,77 @@
+/*
+ * Renesas USB driver R-Car Gen. 2 initialization and power control
+ *
+ * Copyright (C) 2014 Ulrich Hecht
+ *
+ * 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.
+ *
+ */
+
+#include <linux/gpio.h>
+#include <linux/of_gpio.h>
+#include <linux/platform_data/gpio-rcar.h>
+#include <linux/usb/phy.h>
+#include "common.h"
+#include "rcar2.h"
+
+static int usbhs_rcar2_hardware_init(struct platform_device *pdev)
+{
+ struct usbhs_priv *priv = usbhs_pdev_to_priv(pdev);
+ struct usb_phy *phy;
+
+ phy = usb_get_phy_dev(&pdev->dev, 0);
+ if (IS_ERR(phy))
+ return PTR_ERR(phy);
+
+ priv->phy = phy;
+ return 0;
+}
+
+static int usbhs_rcar2_hardware_exit(struct platform_device *pdev)
+{
+ struct usbhs_priv *priv = usbhs_pdev_to_priv(pdev);
+
+ if (!priv->phy)
+ return 0;
+
+ usb_put_phy(priv->phy);
+ priv->phy = NULL;
+
+ return 0;
+}
+
+static int usbhs_rcar2_power_ctrl(struct platform_device *pdev,
+ void __iomem *base, int enable)
+{
+ struct usbhs_priv *priv = usbhs_pdev_to_priv(pdev);
+
+ if (!priv->phy)
+ return -ENODEV;
+
+ if (enable) {
+ int retval = usb_phy_init(priv->phy);
+
+ if (!retval)
+ retval = usb_phy_set_suspend(priv->phy, 0);
+ return retval;
+ }
+
+ usb_phy_set_suspend(priv->phy, 1);
+ usb_phy_shutdown(priv->phy);
+ return 0;
+}
+
+static int usbhs_rcar2_get_id(struct platform_device *pdev)
+{
+ return USBHS_GADGET;
+}
+
+const struct renesas_usbhs_platform_callback usbhs_rcar2_ops = {
+ .hardware_init = usbhs_rcar2_hardware_init,
+ .hardware_exit = usbhs_rcar2_hardware_exit,
+ .power_ctrl = usbhs_rcar2_power_ctrl,
+ .get_id = usbhs_rcar2_get_id,
+};
diff --git a/drivers/usb/renesas_usbhs/rcar2.h b/drivers/usb/renesas_usbhs/rcar2.h
new file mode 100644
index 000000000000..f07f10d9b3b2
--- /dev/null
+++ b/drivers/usb/renesas_usbhs/rcar2.h
@@ -0,0 +1,4 @@
+#include "common.h"
+
+extern const struct renesas_usbhs_platform_callback
+ usbhs_rcar2_ops;
diff --git a/include/linux/usb/composite.h b/include/linux/usb/composite.h
index 7373203140e7..c330f5ef42cf 100644
--- a/include/linux/usb/composite.h
+++ b/include/linux/usb/composite.h
@@ -386,6 +386,21 @@ struct usb_composite_driver {
extern int usb_composite_probe(struct usb_composite_driver *driver);
extern void usb_composite_unregister(struct usb_composite_driver *driver);
+
+/**
+ * module_usb_composite_driver() - Helper macro for registering a USB gadget
+ * composite driver
+ * @__usb_composite_driver: usb_composite_driver struct
+ *
+ * Helper macro for USB gadget composite drivers which do not do anything
+ * special in module init/exit. This eliminates a lot of boilerplate. Each
+ * module may only use this macro once, and calling it replaces module_init()
+ * and module_exit()
+ */
+#define module_usb_composite_driver(__usb_composite_driver) \
+ module_driver(__usb_composite_driver, usb_composite_probe, \
+ usb_composite_unregister)
+
extern void usb_composite_setup_continue(struct usb_composite_dev *cdev);
extern int composite_dev_prepare(struct usb_composite_driver *composite,
struct usb_composite_dev *cdev);
diff --git a/include/linux/usb/renesas_usbhs.h b/include/linux/usb/renesas_usbhs.h
index e452ba6ec6bd..d5952bb66752 100644
--- a/include/linux/usb/renesas_usbhs.h
+++ b/include/linux/usb/renesas_usbhs.h
@@ -153,6 +153,9 @@ struct renesas_usbhs_driver_param {
*/
int pio_dma_border; /* default is 64byte */
+ u32 type;
+ u32 enable_gpio;
+
/*
* option:
*/
@@ -160,6 +163,9 @@ struct renesas_usbhs_driver_param {
u32 has_sudmac:1; /* for SUDMAC */
};
+#define USBHS_TYPE_R8A7790 1
+#define USBHS_TYPE_R8A7791 2
+
/*
* option:
*
diff --git a/include/linux/usb/usb338x.h b/include/linux/usb/usb338x.h
new file mode 100644
index 000000000000..f92eb635b9d3
--- /dev/null
+++ b/include/linux/usb/usb338x.h
@@ -0,0 +1,199 @@
+/*
+ * USB 338x super/high/full speed USB device controller.
+ * Unlike many such controllers, this one talks PCI.
+ *
+ * Copyright (C) 2002 NetChip Technology, Inc. (http://www.netchip.com)
+ * Copyright (C) 2003 David Brownell
+ * Copyright (C) 2014 Ricardo Ribalda - Qtechnology/AS
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * 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 __LINUX_USB_USB338X_H
+#define __LINUX_USB_USB338X_H
+
+#include <linux/usb/net2280.h>
+
+/*
+ * Extra defined bits for net2280 registers
+ */
+#define SCRATCH 0x0b
+
+#define DEFECT7374_FSM_FIELD 28
+#define SUPER_SPEED 8
+#define DMA_REQUEST_OUTSTANDING 5
+#define DMA_PAUSE_DONE_INTERRUPT 26
+#define SET_ISOCHRONOUS_DELAY 24
+#define SET_SEL 22
+#define SUPER_SPEED_MODE 8
+
+/*ep_cfg*/
+#define MAX_BURST_SIZE 24
+#define EP_FIFO_BYTE_COUNT 16
+#define IN_ENDPOINT_ENABLE 14
+#define IN_ENDPOINT_TYPE 12
+#define OUT_ENDPOINT_ENABLE 10
+#define OUT_ENDPOINT_TYPE 8
+
+struct usb338x_usb_ext_regs {
+ u32 usbclass;
+#define DEVICE_PROTOCOL 16
+#define DEVICE_SUB_CLASS 8
+#define DEVICE_CLASS 0
+ u32 ss_sel;
+#define U2_SYSTEM_EXIT_LATENCY 8
+#define U1_SYSTEM_EXIT_LATENCY 0
+ u32 ss_del;
+#define U2_DEVICE_EXIT_LATENCY 8
+#define U1_DEVICE_EXIT_LATENCY 0
+ u32 usb2lpm;
+#define USB_L1_LPM_HIRD 2
+#define USB_L1_LPM_REMOTE_WAKE 1
+#define USB_L1_LPM_SUPPORT 0
+ u32 usb3belt;
+#define BELT_MULTIPLIER 10
+#define BEST_EFFORT_LATENCY_TOLERANCE 0
+ u32 usbctl2;
+#define LTM_ENABLE 7
+#define U2_ENABLE 6
+#define U1_ENABLE 5
+#define FUNCTION_SUSPEND 4
+#define USB3_CORE_ENABLE 3
+#define USB2_CORE_ENABLE 2
+#define SERIAL_NUMBER_STRING_ENABLE 0
+ u32 in_timeout;
+#define GPEP3_TIMEOUT 19
+#define GPEP2_TIMEOUT 18
+#define GPEP1_TIMEOUT 17
+#define GPEP0_TIMEOUT 16
+#define GPEP3_TIMEOUT_VALUE 13
+#define GPEP3_TIMEOUT_ENABLE 12
+#define GPEP2_TIMEOUT_VALUE 9
+#define GPEP2_TIMEOUT_ENABLE 8
+#define GPEP1_TIMEOUT_VALUE 5
+#define GPEP1_TIMEOUT_ENABLE 4
+#define GPEP0_TIMEOUT_VALUE 1
+#define GPEP0_TIMEOUT_ENABLE 0
+ u32 isodelay;
+#define ISOCHRONOUS_DELAY 0
+} __packed;
+
+struct usb338x_fifo_regs {
+ /* offset 0x0500, 0x0520, 0x0540, 0x0560, 0x0580 */
+ u32 ep_fifo_size_base;
+#define IN_FIFO_BASE_ADDRESS 22
+#define IN_FIFO_SIZE 16
+#define OUT_FIFO_BASE_ADDRESS 6
+#define OUT_FIFO_SIZE 0
+ u32 ep_fifo_out_wrptr;
+ u32 ep_fifo_out_rdptr;
+ u32 ep_fifo_in_wrptr;
+ u32 ep_fifo_in_rdptr;
+ u32 unused[3];
+} __packed;
+
+
+/* Link layer */
+struct usb338x_ll_regs {
+ /* offset 0x700 */
+ u32 ll_ltssm_ctrl1;
+ u32 ll_ltssm_ctrl2;
+ u32 ll_ltssm_ctrl3;
+ u32 unused[2];
+ u32 ll_general_ctrl0;
+ u32 ll_general_ctrl1;
+#define PM_U3_AUTO_EXIT 29
+#define PM_U2_AUTO_EXIT 28
+#define PM_U1_AUTO_EXIT 27
+#define PM_FORCE_U2_ENTRY 26
+#define PM_FORCE_U1_ENTRY 25
+#define PM_LGO_COLLISION_SEND_LAU 24
+#define PM_DIR_LINK_REJECT 23
+#define PM_FORCE_LINK_ACCEPT 22
+#define PM_DIR_ENTRY_U3 20
+#define PM_DIR_ENTRY_U2 19
+#define PM_DIR_ENTRY_U1 18
+#define PM_U2_ENABLE 17
+#define PM_U1_ENABLE 16
+#define SKP_THRESHOLD_ADJUST_FMW 8
+#define RESEND_DPP_ON_LRTY_FMW 7
+#define DL_BIT_VALUE_FMW 6
+#define FORCE_DL_BIT 5
+ u32 ll_general_ctrl2;
+#define SELECT_INVERT_LANE_POLARITY 7
+#define FORCE_INVERT_LANE_POLARITY 6
+ u32 ll_general_ctrl3;
+ u32 ll_general_ctrl4;
+ u32 ll_error_gen;
+} __packed;
+
+struct usb338x_ll_lfps_regs {
+ /* offset 0x748 */
+ u32 ll_lfps_5;
+#define TIMER_LFPS_6US 16
+ u32 ll_lfps_6;
+#define TIMER_LFPS_80US 0
+} __packed;
+
+struct usb338x_ll_tsn_regs {
+ /* offset 0x77C */
+ u32 ll_tsn_counters_2;
+#define HOT_TX_NORESET_TS2 24
+ u32 ll_tsn_counters_3;
+#define HOT_RX_RESET_TS2 0
+} __packed;
+
+struct usb338x_ll_chi_regs {
+ /* offset 0x79C */
+ u32 ll_tsn_chicken_bit;
+#define RECOVERY_IDLE_TO_RECOVER_FMW 3
+} __packed;
+
+/* protocol layer */
+struct usb338x_pl_regs {
+ /* offset 0x800 */
+ u32 pl_reg_1;
+ u32 pl_reg_2;
+ u32 pl_reg_3;
+ u32 pl_reg_4;
+ u32 pl_ep_ctrl;
+ /* Protocol Layer Endpoint Control*/
+#define PL_EP_CTRL 0x810
+#define ENDPOINT_SELECT 0
+ /* [4:0] */
+#define EP_INITIALIZED 16
+#define SEQUENCE_NUMBER_RESET 17
+#define CLEAR_ACK_ERROR_CODE 20
+ u32 pl_reg_6;
+ u32 pl_reg_7;
+ u32 pl_reg_8;
+ u32 pl_ep_status_1;
+ /* Protocol Layer Endpoint Status 1*/
+#define PL_EP_STATUS_1 0x820
+#define STATE 16
+#define ACK_GOOD_NORMAL 0x11
+#define ACK_GOOD_MORE_ACKS_TO_COME 0x16
+ u32 pl_ep_status_2;
+ u32 pl_ep_status_3;
+ /* Protocol Layer Endpoint Status 3*/
+#define PL_EP_STATUS_3 0x828
+#define SEQUENCE_NUMBER 0
+ u32 pl_ep_status_4;
+ /* Protocol Layer Endpoint Status 4*/
+#define PL_EP_STATUS_4 0x82c
+ u32 pl_ep_cfg_4;
+ /* Protocol Layer Endpoint Configuration 4*/
+#define PL_EP_CFG_4 0x830
+#define NON_CTRL_IN_TOLERATE_BAD_DIR 6
+} __packed;
+
+#endif /* __LINUX_USB_USB338X_H */
diff --git a/include/uapi/linux/usb/functionfs.h b/include/uapi/linux/usb/functionfs.h
index 24b68c59dcf8..0154b2859fd7 100644
--- a/include/uapi/linux/usb/functionfs.h
+++ b/include/uapi/linux/usb/functionfs.h
@@ -18,10 +18,9 @@ enum functionfs_flags {
FUNCTIONFS_HAS_FS_DESC = 1,
FUNCTIONFS_HAS_HS_DESC = 2,
FUNCTIONFS_HAS_SS_DESC = 4,
+ FUNCTIONFS_HAS_MS_OS_DESC = 8,
};
-#ifndef __KERNEL__
-
/* Descriptor of an non-audio endpoint */
struct usb_endpoint_descriptor_no_audio {
__u8 bLength;
@@ -41,6 +40,37 @@ struct usb_functionfs_descs_head {
__le32 hs_count;
} __attribute__((packed, deprecated));
+/* MS OS Descriptor header */
+struct usb_os_desc_header {
+ __u8 interface;
+ __le32 dwLength;
+ __le16 bcdVersion;
+ __le16 wIndex;
+ union {
+ struct {
+ __u8 bCount;
+ __u8 Reserved;
+ };
+ __le16 wCount;
+ };
+} __attribute__((packed));
+
+struct usb_ext_compat_desc {
+ __u8 bFirstInterfaceNumber;
+ __u8 Reserved1;
+ __u8 CompatibleID[8];
+ __u8 SubCompatibleID[8];
+ __u8 Reserved2[6];
+};
+
+struct usb_ext_prop_desc {
+ __le32 dwSize;
+ __le32 dwPropertyDataType;
+ __le16 wPropertyNameLength;
+} __attribute__((packed));
+
+#ifndef __KERNEL__
+
/*
* Descriptors format:
*
@@ -52,9 +82,11 @@ struct usb_functionfs_descs_head {
* | | fs_count | LE32 | number of full-speed descriptors |
* | | hs_count | LE32 | number of high-speed descriptors |
* | | ss_count | LE32 | number of super-speed descriptors |
+ * | | os_count | LE32 | number of MS OS descriptors |
* | | fs_descrs | Descriptor[] | list of full-speed descriptors |
* | | hs_descrs | Descriptor[] | list of high-speed descriptors |
* | | ss_descrs | Descriptor[] | list of super-speed descriptors |
+ * | | os_descrs | OSDesc[] | list of MS OS descriptors |
*
* Depending on which flags are set, various fields may be missing in the
* structure. Any flags that are not recognised cause the whole block to be
@@ -81,6 +113,52 @@ struct usb_functionfs_descs_head {
* | 0 | bLength | U8 | length of the descriptor |
* | 1 | bDescriptorType | U8 | descriptor type |
* | 2 | payload | | descriptor's payload |
+ *
+ * OSDesc[] is an array of valid MS OS Feature Descriptors which have one of
+ * the following formats:
+ *
+ * | off | name | type | description |
+ * |-----+-----------------+------+--------------------------|
+ * | 0 | inteface | U8 | related interface number |
+ * | 1 | dwLength | U32 | length of the descriptor |
+ * | 5 | bcdVersion | U16 | currently supported: 1 |
+ * | 7 | wIndex | U16 | currently supported: 4 |
+ * | 9 | bCount | U8 | number of ext. compat. |
+ * | 10 | Reserved | U8 | 0 |
+ * | 11 | ExtCompat[] | | list of ext. compat. d. |
+ *
+ * | off | name | type | description |
+ * |-----+-----------------+------+--------------------------|
+ * | 0 | inteface | U8 | related interface number |
+ * | 1 | dwLength | U32 | length of the descriptor |
+ * | 5 | bcdVersion | U16 | currently supported: 1 |
+ * | 7 | wIndex | U16 | currently supported: 5 |
+ * | 9 | wCount | U16 | number of ext. compat. |
+ * | 11 | ExtProp[] | | list of ext. prop. d. |
+ *
+ * ExtCompat[] is an array of valid Extended Compatiblity descriptors
+ * which have the following format:
+ *
+ * | off | name | type | description |
+ * |-----+-----------------------+------+-------------------------------------|
+ * | 0 | bFirstInterfaceNumber | U8 | index of the interface or of the 1st|
+ * | | | | interface in an IAD group |
+ * | 1 | Reserved | U8 | 0 |
+ * | 2 | CompatibleID | U8[8]| compatible ID string |
+ * | 10 | SubCompatibleID | U8[8]| subcompatible ID string |
+ * | 18 | Reserved | U8[6]| 0 |
+ *
+ * ExtProp[] is an array of valid Extended Properties descriptors
+ * which have the following format:
+ *
+ * | off | name | type | description |
+ * |-----+-----------------------+------+-------------------------------------|
+ * | 0 | dwSize | U32 | length of the descriptor |
+ * | 4 | dwPropertyDataType | U32 | 1..7 |
+ * | 8 | wPropertyNameLength | U16 | bPropertyName length (NL) |
+ * | 10 | bPropertyName |U8[NL]| name of this property |
+ * |10+NL| dwPropertyDataLength | U32 | bPropertyData length (DL) |
+ * |14+NL| bProperty |U8[DL]| payload of this property |
*/
struct usb_functionfs_strings_head {
diff --git a/tools/usb/ffs-aio-example/multibuff/device_app/aio_multibuff.c b/tools/usb/ffs-aio-example/multibuff/device_app/aio_multibuff.c
index 87216a0c4a8b..af4b0508be77 100644
--- a/tools/usb/ffs-aio-example/multibuff/device_app/aio_multibuff.c
+++ b/tools/usb/ffs-aio-example/multibuff/device_app/aio_multibuff.c
@@ -1,3 +1,30 @@
+/*
+ * This is free and unencumbered software released into the public domain.
+ *
+ * Anyone is free to copy, modify, publish, use, compile, sell, or
+ * distribute this software, either in source code form or as a compiled
+ * binary, for any purpose, commercial or non-commercial, and by any
+ * means.
+ *
+ * In jurisdictions that recognize copyright laws, the author or authors
+ * of this software dedicate any and all copyright interest in the
+ * software to the public domain. We make this dedication for the benefit
+ * of the public at large and to the detriment of our heirs and
+ * successors. We intend this dedication to be an overt act of
+ * relinquishment in perpetuity of all present and future rights to this
+ * software under copyright law.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * For more information, please refer to <http://unlicense.org/>
+ */
+
#define _BSD_SOURCE /* for endian.h */
#include <endian.h>
@@ -27,7 +54,9 @@
/******************** Descriptors and Strings *******************************/
static const struct {
- struct usb_functionfs_descs_head header;
+ struct usb_functionfs_descs_head_v2 header;
+ __le32 fs_count;
+ __le32 hs_count;
struct {
struct usb_interface_descriptor intf;
struct usb_endpoint_descriptor_no_audio bulk_sink;
@@ -35,11 +64,12 @@ static const struct {
} __attribute__ ((__packed__)) fs_descs, hs_descs;
} __attribute__ ((__packed__)) descriptors = {
.header = {
- .magic = htole32(FUNCTIONFS_DESCRIPTORS_MAGIC),
+ .magic = htole32(FUNCTIONFS_DESCRIPTORS_MAGIC_V2),
+ .flags = htole32(FUNCTIONFS_HAS_FS_DESC |
+ FUNCTIONFS_HAS_HS_DESC),
.length = htole32(sizeof(descriptors)),
- .fs_count = 3,
- .hs_count = 3,
},
+ .fs_count = htole32(3),
.fs_descs = {
.intf = {
.bLength = sizeof(descriptors.fs_descs.intf),
@@ -61,6 +91,7 @@ static const struct {
.bmAttributes = USB_ENDPOINT_XFER_BULK,
},
},
+ .hs_count = htole32(3),
.hs_descs = {
.intf = {
.bLength = sizeof(descriptors.hs_descs.intf),
diff --git a/tools/usb/ffs-aio-example/multibuff/host_app/test.c b/tools/usb/ffs-aio-example/multibuff/host_app/test.c
index b0ad8747d03f..daa3abe6bebd 100644
--- a/tools/usb/ffs-aio-example/multibuff/host_app/test.c
+++ b/tools/usb/ffs-aio-example/multibuff/host_app/test.c
@@ -1,3 +1,30 @@
+/*
+ * This is free and unencumbered software released into the public domain.
+ *
+ * Anyone is free to copy, modify, publish, use, compile, sell, or
+ * distribute this software, either in source code form or as a compiled
+ * binary, for any purpose, commercial or non-commercial, and by any
+ * means.
+ *
+ * In jurisdictions that recognize copyright laws, the author or authors
+ * of this software dedicate any and all copyright interest in the
+ * software to the public domain. We make this dedication for the benefit
+ * of the public at large and to the detriment of our heirs and
+ * successors. We intend this dedication to be an overt act of
+ * relinquishment in perpetuity of all present and future rights to this
+ * software under copyright law.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * For more information, please refer to <http://unlicense.org/>
+ */
+
#include <libusb.h>
#include <stdio.h>
#include <string.h>
diff --git a/tools/usb/ffs-aio-example/simple/device_app/aio_simple.c b/tools/usb/ffs-aio-example/simple/device_app/aio_simple.c
index f558664a3317..adc310a6d489 100644
--- a/tools/usb/ffs-aio-example/simple/device_app/aio_simple.c
+++ b/tools/usb/ffs-aio-example/simple/device_app/aio_simple.c
@@ -1,3 +1,30 @@
+/*
+ * This is free and unencumbered software released into the public domain.
+ *
+ * Anyone is free to copy, modify, publish, use, compile, sell, or
+ * distribute this software, either in source code form or as a compiled
+ * binary, for any purpose, commercial or non-commercial, and by any
+ * means.
+ *
+ * In jurisdictions that recognize copyright laws, the author or authors
+ * of this software dedicate any and all copyright interest in the
+ * software to the public domain. We make this dedication for the benefit
+ * of the public at large and to the detriment of our heirs and
+ * successors. We intend this dedication to be an overt act of
+ * relinquishment in perpetuity of all present and future rights to this
+ * software under copyright law.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * For more information, please refer to <http://unlicense.org/>
+ */
+
#define _BSD_SOURCE /* for endian.h */
#include <endian.h>
@@ -25,7 +52,9 @@
/******************** Descriptors and Strings *******************************/
static const struct {
- struct usb_functionfs_descs_head header;
+ struct usb_functionfs_descs_head_v2 header;
+ __le32 fs_count;
+ __le32 hs_count;
struct {
struct usb_interface_descriptor intf;
struct usb_endpoint_descriptor_no_audio bulk_sink;
@@ -33,11 +62,12 @@ static const struct {
} __attribute__ ((__packed__)) fs_descs, hs_descs;
} __attribute__ ((__packed__)) descriptors = {
.header = {
- .magic = htole32(FUNCTIONFS_DESCRIPTORS_MAGIC),
+ .magic = htole32(FUNCTIONFS_DESCRIPTORS_MAGIC_V2),
+ .flags = htole32(FUNCTIONFS_HAS_FS_DESC |
+ FUNCTIONFS_HAS_HS_DESC),
.length = htole32(sizeof(descriptors)),
- .fs_count = 3,
- .hs_count = 3,
},
+ .fs_count = htole32(3),
.fs_descs = {
.intf = {
.bLength = sizeof(descriptors.fs_descs.intf),
@@ -59,6 +89,7 @@ static const struct {
.bmAttributes = USB_ENDPOINT_XFER_BULK,
},
},
+ .hs_count = htole32(3),
.hs_descs = {
.intf = {
.bLength = sizeof(descriptors.hs_descs.intf),
diff --git a/tools/usb/ffs-aio-example/simple/host_app/test.c b/tools/usb/ffs-aio-example/simple/host_app/test.c
index 64b6a57d8ca3..acd6332811f3 100644
--- a/tools/usb/ffs-aio-example/simple/host_app/test.c
+++ b/tools/usb/ffs-aio-example/simple/host_app/test.c
@@ -1,3 +1,30 @@
+/*
+ * This is free and unencumbered software released into the public domain.
+ *
+ * Anyone is free to copy, modify, publish, use, compile, sell, or
+ * distribute this software, either in source code form or as a compiled
+ * binary, for any purpose, commercial or non-commercial, and by any
+ * means.
+ *
+ * In jurisdictions that recognize copyright laws, the author or authors
+ * of this software dedicate any and all copyright interest in the
+ * software to the public domain. We make this dedication for the benefit
+ * of the public at large and to the detriment of our heirs and
+ * successors. We intend this dedication to be an overt act of
+ * relinquishment in perpetuity of all present and future rights to this
+ * software under copyright law.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+ * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+ * OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * For more information, please refer to <http://unlicense.org/>
+ */
+
#include <libusb.h>
#include <stdio.h>
#include <string.h>