summaryrefslogtreecommitdiff
path: root/drivers
diff options
context:
space:
mode:
Diffstat (limited to 'drivers')
-rw-r--r--drivers/usb/Kconfig4
-rw-r--r--drivers/usb/chipidea/udc.c4
-rw-r--r--drivers/usb/dwc3/Kconfig7
-rw-r--r--drivers/usb/dwc3/Makefile1
-rw-r--r--drivers/usb/dwc3/dwc3-exynos.c1
-rw-r--r--drivers/usb/dwc3/dwc3-keystone.c202
-rw-r--r--drivers/usb/dwc3/dwc3-omap.c12
-rw-r--r--drivers/usb/dwc3/dwc3-pci.c3
-rw-r--r--drivers/usb/dwc3/gadget.c10
-rw-r--r--drivers/usb/gadget/Kconfig39
-rw-r--r--drivers/usb/gadget/Makefile8
-rw-r--r--drivers/usb/gadget/acm_ms.c2
-rw-r--r--drivers/usb/gadget/amd5536udc.c17
-rw-r--r--drivers/usb/gadget/at91_udc.c16
-rw-r--r--drivers/usb/gadget/atmel_usba_udc.c71
-rw-r--r--drivers/usb/gadget/bcm63xx_udc.c4
-rw-r--r--drivers/usb/gadget/composite.c18
-rw-r--r--drivers/usb/gadget/configfs.c8
-rw-r--r--drivers/usb/gadget/dummy_hcd.c2
-rw-r--r--drivers/usb/gadget/epautoconf.c8
-rw-r--r--drivers/usb/gadget/f_ecm.c73
-rw-r--r--drivers/usb/gadget/f_fs.c1012
-rw-r--r--drivers/usb/gadget/f_hid.c18
-rw-r--r--drivers/usb/gadget/f_loopback.c144
-rw-r--r--drivers/usb/gadget/f_midi.c22
-rw-r--r--drivers/usb/gadget/f_ncm.c2
-rw-r--r--drivers/usb/gadget/f_obex.c2
-rw-r--r--drivers/usb/gadget/f_phonet.c2
-rw-r--r--drivers/usb/gadget/f_rndis.c94
-rw-r--r--drivers/usb/gadget/f_serial.c2
-rw-r--r--drivers/usb/gadget/f_sourcesink.c349
-rw-r--r--drivers/usb/gadget/f_subset.c60
-rw-r--r--drivers/usb/gadget/fotg210-udc.c3
-rw-r--r--drivers/usb/gadget/fsl_qe_udc.c4
-rw-r--r--drivers/usb/gadget/fsl_udc_core.c7
-rw-r--r--drivers/usb/gadget/fusb300_udc.c4
-rw-r--r--drivers/usb/gadget/g_ffs.c466
-rw-r--r--drivers/usb/gadget/g_zero.h24
-rw-r--r--drivers/usb/gadget/goku_udc.c16
-rw-r--r--drivers/usb/gadget/lpc32xx_udc.c2
-rw-r--r--drivers/usb/gadget/m66592-udc.c4
-rw-r--r--drivers/usb/gadget/multi.c2
-rw-r--r--drivers/usb/gadget/mv_u3d_core.c4
-rw-r--r--drivers/usb/gadget/mv_udc_core.c4
-rw-r--r--drivers/usb/gadget/net2272.c4
-rw-r--r--drivers/usb/gadget/net2280.c8
-rw-r--r--drivers/usb/gadget/nokia.c6
-rw-r--r--drivers/usb/gadget/omap_udc.c3
-rw-r--r--drivers/usb/gadget/pch_udc.c8
-rw-r--r--drivers/usb/gadget/pxa25x_udc.c1
-rw-r--r--drivers/usb/gadget/pxa27x_udc.c5
-rw-r--r--drivers/usb/gadget/r8a66597-udc.c10
-rw-r--r--drivers/usb/gadget/rndis.c7
-rw-r--r--drivers/usb/gadget/s3c-hsotg.c9
-rw-r--r--drivers/usb/gadget/s3c-hsudc.c2
-rw-r--r--drivers/usb/gadget/s3c2410_udc.c1
-rw-r--r--drivers/usb/gadget/u_ether.c2
-rw-r--r--drivers/usb/gadget/u_ether.h43
-rw-r--r--drivers/usb/gadget/u_f.c32
-rw-r--r--drivers/usb/gadget/u_f.h26
-rw-r--r--drivers/usb/gadget/u_fs.h267
-rw-r--r--drivers/usb/gadget/u_rndis.h2
-rw-r--r--drivers/usb/gadget/zero.c8
-rw-r--r--drivers/usb/musb/Kconfig16
-rw-r--r--drivers/usb/musb/Makefile1
-rw-r--r--drivers/usb/musb/jz4740.c201
-rw-r--r--drivers/usb/musb/musb_core.c50
-rw-r--r--drivers/usb/musb/musb_core.h3
-rw-r--r--drivers/usb/musb/musb_dsps.c117
-rw-r--r--drivers/usb/musb/musb_gadget.c16
-rw-r--r--drivers/usb/musb/musb_host.c9
-rw-r--r--drivers/usb/musb/musb_host.h6
-rw-r--r--drivers/usb/musb/musb_virthub.c69
-rw-r--r--drivers/usb/musb/ux500_dma.c4
-rw-r--r--drivers/usb/phy/Kconfig52
-rw-r--r--drivers/usb/phy/Makefile7
-rw-r--r--drivers/usb/phy/phy-ab8500-usb.c2
-rw-r--r--drivers/usb/phy/phy-am335x-control.c6
-rw-r--r--drivers/usb/phy/phy-am335x.c39
-rw-r--r--drivers/usb/phy/phy-fsl-usb.c7
-rw-r--r--drivers/usb/phy/phy-fsl-usb.h2
-rw-r--r--drivers/usb/phy/phy-fsm-usb.c14
-rw-r--r--drivers/usb/phy/phy-fsm-usb.h236
-rw-r--r--drivers/usb/phy/phy-generic.c1
-rw-r--r--drivers/usb/phy/phy-gpio-vbus-usb.c2
-rw-r--r--drivers/usb/phy/phy-isp1301-omap.c9
-rw-r--r--drivers/usb/phy/phy-keystone.c141
-rw-r--r--drivers/usb/phy/phy-mxs-usb.c13
-rw-r--r--drivers/usb/phy/phy-omap-control.c19
-rw-r--r--drivers/usb/phy/phy-omap-otg.c169
-rw-r--r--drivers/usb/phy/phy-rcar-gen2-usb.c2
-rw-r--r--drivers/usb/phy/phy-tahvo.c457
-rw-r--r--drivers/usb/phy/phy-twl6030-usb.c2
-rw-r--r--drivers/usb/phy/phy.c4
-rw-r--r--drivers/usb/renesas_usbhs/fifo.c18
-rw-r--r--drivers/usb/renesas_usbhs/mod_gadget.c4
96 files changed, 3500 insertions, 1399 deletions
diff --git a/drivers/usb/Kconfig b/drivers/usb/Kconfig
index 2642b8a11e05..a34fb9846417 100644
--- a/drivers/usb/Kconfig
+++ b/drivers/usb/Kconfig
@@ -94,8 +94,6 @@ source "drivers/usb/wusbcore/Kconfig"
source "drivers/usb/host/Kconfig"
-source "drivers/usb/musb/Kconfig"
-
source "drivers/usb/renesas_usbhs/Kconfig"
source "drivers/usb/class/Kconfig"
@@ -106,6 +104,8 @@ source "drivers/usb/image/Kconfig"
endif
+source "drivers/usb/musb/Kconfig"
+
source "drivers/usb/dwc3/Kconfig"
source "drivers/usb/chipidea/Kconfig"
diff --git a/drivers/usb/chipidea/udc.c b/drivers/usb/chipidea/udc.c
index 69d20fbb38a2..73a39ef93ec5 100644
--- a/drivers/usb/chipidea/udc.c
+++ b/drivers/usb/chipidea/udc.c
@@ -1566,7 +1566,7 @@ static int init_eps(struct ci_hdrc *ci)
* eps, maxP is set by epautoconfig() called
* by gadget layer
*/
- hwep->ep.maxpacket = (unsigned short)~0;
+ usb_ep_set_maxpacket_limit(&hwep->ep, (unsigned short)~0);
INIT_LIST_HEAD(&hwep->qh.queue);
hwep->qh.ptr = dma_pool_alloc(ci->qh_pool, GFP_KERNEL,
@@ -1586,7 +1586,7 @@ static int init_eps(struct ci_hdrc *ci)
else
ci->ep0in = hwep;
- hwep->ep.maxpacket = CTRL_PAYLOAD_MAX;
+ usb_ep_set_maxpacket_limit(&hwep->ep, CTRL_PAYLOAD_MAX);
continue;
}
diff --git a/drivers/usb/dwc3/Kconfig b/drivers/usb/dwc3/Kconfig
index 70fc43027a5c..e2c730fc9a90 100644
--- a/drivers/usb/dwc3/Kconfig
+++ b/drivers/usb/dwc3/Kconfig
@@ -70,6 +70,13 @@ config USB_DWC3_PCI
One such PCIe-based platform is Synopsys' PCIe HAPS model of
this IP.
+config USB_DWC3_KEYSTONE
+ tristate "Texas Instruments Keystone2 Platforms"
+ default USB_DWC3
+ help
+ Support of USB2/3 functionality in TI Keystone2 platforms.
+ Say 'Y' or 'M' here if you have one such device
+
comment "Debugging features"
config USB_DWC3_DEBUG
diff --git a/drivers/usb/dwc3/Makefile b/drivers/usb/dwc3/Makefile
index dd1760145c46..10ac3e72482e 100644
--- a/drivers/usb/dwc3/Makefile
+++ b/drivers/usb/dwc3/Makefile
@@ -32,3 +32,4 @@ endif
obj-$(CONFIG_USB_DWC3_OMAP) += dwc3-omap.o
obj-$(CONFIG_USB_DWC3_EXYNOS) += dwc3-exynos.o
obj-$(CONFIG_USB_DWC3_PCI) += dwc3-pci.o
+obj-$(CONFIG_USB_DWC3_KEYSTONE) += dwc3-keystone.o
diff --git a/drivers/usb/dwc3/dwc3-exynos.c b/drivers/usb/dwc3/dwc3-exynos.c
index 8b20c70d91e7..28c8ad79f5e6 100644
--- a/drivers/usb/dwc3/dwc3-exynos.c
+++ b/drivers/usb/dwc3/dwc3-exynos.c
@@ -50,6 +50,7 @@ static int dwc3_exynos_register_phys(struct dwc3_exynos *exynos)
exynos->usb2_phy = pdev;
pdata.type = USB_PHY_TYPE_USB2;
+ pdata.gpio_reset = -1;
ret = platform_device_add_data(exynos->usb2_phy, &pdata, sizeof(pdata));
if (ret)
diff --git a/drivers/usb/dwc3/dwc3-keystone.c b/drivers/usb/dwc3/dwc3-keystone.c
new file mode 100644
index 000000000000..1fad1618df6e
--- /dev/null
+++ b/drivers/usb/dwc3/dwc3-keystone.c
@@ -0,0 +1,202 @@
+/**
+ * dwc3-keystone.c - Keystone Specific Glue layer
+ *
+ * Copyright (C) 2010-2013 Texas Instruments Incorporated - http://www.ti.com
+ *
+ * Author: WingMan Kwok <w-kwok2@ti.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 of
+ * the License as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/clk.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <linux/dma-mapping.h>
+#include <linux/io.h>
+#include <linux/of_platform.h>
+
+/* USBSS register offsets */
+#define USBSS_REVISION 0x0000
+#define USBSS_SYSCONFIG 0x0010
+#define USBSS_IRQ_EOI 0x0018
+#define USBSS_IRQSTATUS_RAW_0 0x0020
+#define USBSS_IRQSTATUS_0 0x0024
+#define USBSS_IRQENABLE_SET_0 0x0028
+#define USBSS_IRQENABLE_CLR_0 0x002c
+
+/* IRQ register bits */
+#define USBSS_IRQ_EOI_LINE(n) BIT(n)
+#define USBSS_IRQ_EVENT_ST BIT(0)
+#define USBSS_IRQ_COREIRQ_EN BIT(0)
+#define USBSS_IRQ_COREIRQ_CLR BIT(0)
+
+static u64 kdwc3_dma_mask;
+
+struct dwc3_keystone {
+ struct device *dev;
+ struct clk *clk;
+ void __iomem *usbss;
+};
+
+static inline u32 kdwc3_readl(void __iomem *base, u32 offset)
+{
+ return readl(base + offset);
+}
+
+static inline void kdwc3_writel(void __iomem *base, u32 offset, u32 value)
+{
+ writel(value, base + offset);
+}
+
+static void kdwc3_enable_irqs(struct dwc3_keystone *kdwc)
+{
+ u32 val;
+
+ val = kdwc3_readl(kdwc->usbss, USBSS_IRQENABLE_SET_0);
+ val |= USBSS_IRQ_COREIRQ_EN;
+ kdwc3_writel(kdwc->usbss, USBSS_IRQENABLE_SET_0, val);
+}
+
+static void kdwc3_disable_irqs(struct dwc3_keystone *kdwc)
+{
+ u32 val;
+
+ val = kdwc3_readl(kdwc->usbss, USBSS_IRQENABLE_SET_0);
+ val &= ~USBSS_IRQ_COREIRQ_EN;
+ kdwc3_writel(kdwc->usbss, USBSS_IRQENABLE_SET_0, val);
+}
+
+static irqreturn_t dwc3_keystone_interrupt(int irq, void *_kdwc)
+{
+ struct dwc3_keystone *kdwc = _kdwc;
+
+ kdwc3_writel(kdwc->usbss, USBSS_IRQENABLE_CLR_0, USBSS_IRQ_COREIRQ_CLR);
+ kdwc3_writel(kdwc->usbss, USBSS_IRQSTATUS_0, USBSS_IRQ_EVENT_ST);
+ kdwc3_writel(kdwc->usbss, USBSS_IRQENABLE_SET_0, USBSS_IRQ_COREIRQ_EN);
+ kdwc3_writel(kdwc->usbss, USBSS_IRQ_EOI, USBSS_IRQ_EOI_LINE(0));
+
+ return IRQ_HANDLED;
+}
+
+static int kdwc3_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct device_node *node = pdev->dev.of_node;
+ struct dwc3_keystone *kdwc;
+ struct resource *res;
+ int error, irq;
+
+ kdwc = devm_kzalloc(dev, sizeof(*kdwc), GFP_KERNEL);
+ if (!kdwc)
+ return -ENOMEM;
+
+ platform_set_drvdata(pdev, kdwc);
+
+ kdwc->dev = dev;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res) {
+ dev_err(dev, "missing usbss resource\n");
+ return -EINVAL;
+ }
+
+ kdwc->usbss = devm_ioremap_resource(dev, res);
+ if (IS_ERR(kdwc->usbss))
+ return PTR_ERR(kdwc->usbss);
+
+ kdwc3_dma_mask = dma_get_mask(dev);
+ dev->dma_mask = &kdwc3_dma_mask;
+
+ kdwc->clk = devm_clk_get(kdwc->dev, "usb");
+
+ error = clk_prepare_enable(kdwc->clk);
+ if (error < 0) {
+ dev_dbg(kdwc->dev, "unable to enable usb clock, err %d\n",
+ error);
+ return error;
+ }
+
+ irq = platform_get_irq(pdev, 0);
+ if (irq < 0) {
+ dev_err(&pdev->dev, "missing irq\n");
+ goto err_irq;
+ }
+
+ error = devm_request_irq(dev, irq, dwc3_keystone_interrupt, IRQF_SHARED,
+ dev_name(dev), kdwc);
+ if (error) {
+ dev_err(dev, "failed to request IRQ #%d --> %d\n",
+ irq, error);
+ goto err_irq;
+ }
+
+ kdwc3_enable_irqs(kdwc);
+
+ error = of_platform_populate(node, NULL, NULL, dev);
+ if (error) {
+ dev_err(&pdev->dev, "failed to create dwc3 core\n");
+ goto err_core;
+ }
+
+ return 0;
+
+err_core:
+ kdwc3_disable_irqs(kdwc);
+err_irq:
+ clk_disable_unprepare(kdwc->clk);
+
+ return error;
+}
+
+static int kdwc3_remove_core(struct device *dev, void *c)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+
+ platform_device_unregister(pdev);
+
+ return 0;
+}
+
+static int kdwc3_remove(struct platform_device *pdev)
+{
+ struct dwc3_keystone *kdwc = platform_get_drvdata(pdev);
+
+ kdwc3_disable_irqs(kdwc);
+ device_for_each_child(&pdev->dev, NULL, kdwc3_remove_core);
+ clk_disable_unprepare(kdwc->clk);
+ platform_set_drvdata(pdev, NULL);
+
+ return 0;
+}
+
+static const struct of_device_id kdwc3_of_match[] = {
+ { .compatible = "ti,keystone-dwc3", },
+ {},
+};
+MODULE_DEVICE_TABLE(of, kdwc3_of_match);
+
+static struct platform_driver kdwc3_driver = {
+ .probe = kdwc3_probe,
+ .remove = kdwc3_remove,
+ .driver = {
+ .name = "keystone-dwc3",
+ .owner = THIS_MODULE,
+ .of_match_table = kdwc3_of_match,
+ },
+};
+
+module_platform_driver(kdwc3_driver);
+
+MODULE_ALIAS("platform:keystone-dwc3");
+MODULE_AUTHOR("WingMan Kwok <w-kwok2@ti.com>");
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("DesignWare USB3 KEYSTONE Glue Layer");
diff --git a/drivers/usb/dwc3/dwc3-omap.c b/drivers/usb/dwc3/dwc3-omap.c
index 7f7ea62e961b..b269dbd47fc4 100644
--- a/drivers/usb/dwc3/dwc3-omap.c
+++ b/drivers/usb/dwc3/dwc3-omap.c
@@ -20,7 +20,6 @@
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/interrupt.h>
-#include <linux/spinlock.h>
#include <linux/platform_device.h>
#include <linux/platform_data/dwc3-omap.h>
#include <linux/pm_runtime.h>
@@ -120,9 +119,6 @@
#define USBOTGSS_UTMI_OTG_STATUS_VBUSVALID (1 << 1)
struct dwc3_omap {
- /* device lock */
- spinlock_t lock;
-
struct device *dev;
int irq;
@@ -280,8 +276,6 @@ static irqreturn_t dwc3_omap_interrupt(int irq, void *_omap)
struct dwc3_omap *omap = _omap;
u32 reg;
- spin_lock(&omap->lock);
-
reg = dwc3_omap_read_irqmisc_status(omap);
if (reg & USBOTGSS_IRQMISC_DMADISABLECLR) {
@@ -322,8 +316,6 @@ static irqreturn_t dwc3_omap_interrupt(int irq, void *_omap)
dwc3_omap_write_irq0_status(omap, reg);
- spin_unlock(&omap->lock);
-
return IRQ_HANDLED;
}
@@ -449,8 +441,6 @@ static int dwc3_omap_probe(struct platform_device *pdev)
}
}
- spin_lock_init(&omap->lock);
-
omap->dev = dev;
omap->irq = irq;
omap->base = base;
@@ -535,7 +525,7 @@ static int dwc3_omap_probe(struct platform_device *pdev)
edev = of_extcon_get_extcon_dev(dev, 0);
if (IS_ERR(edev)) {
dev_vdbg(dev, "couldn't get extcon device\n");
- ret = PTR_ERR(edev);
+ ret = -EPROBE_DEFER;
goto err2;
}
diff --git a/drivers/usb/dwc3/dwc3-pci.c b/drivers/usb/dwc3/dwc3-pci.c
index 31443aeedcdb..f393c183cc69 100644
--- a/drivers/usb/dwc3/dwc3-pci.c
+++ b/drivers/usb/dwc3/dwc3-pci.c
@@ -52,6 +52,7 @@ static int dwc3_pci_register_phys(struct dwc3_pci *glue)
glue->usb2_phy = pdev;
pdata.type = USB_PHY_TYPE_USB2;
+ pdata.gpio_reset = -1;
ret = platform_device_add_data(glue->usb2_phy, &pdata, sizeof(pdata));
if (ret)
@@ -182,7 +183,7 @@ static void dwc3_pci_remove(struct pci_dev *pci)
pci_disable_device(pci);
}
-static DEFINE_PCI_DEVICE_TABLE(dwc3_pci_id_table) = {
+static const struct pci_device_id dwc3_pci_id_table[] = {
{
PCI_DEVICE(PCI_VENDOR_ID_SYNOPSYS,
PCI_DEVICE_ID_SYNOPSYS_HAPSUSB3),
diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c
index 02e44fcaf205..2da0a5a2803a 100644
--- a/drivers/usb/dwc3/gadget.c
+++ b/drivers/usb/dwc3/gadget.c
@@ -1650,7 +1650,7 @@ static int dwc3_gadget_init_hw_endpoints(struct dwc3 *dwc,
dev_vdbg(dwc->dev, "initializing %s\n", dep->name);
if (epnum == 0 || epnum == 1) {
- dep->endpoint.maxpacket = 512;
+ usb_ep_set_maxpacket_limit(&dep->endpoint, 512);
dep->endpoint.maxburst = 1;
dep->endpoint.ops = &dwc3_gadget_ep0_ops;
if (!epnum)
@@ -1658,7 +1658,7 @@ static int dwc3_gadget_init_hw_endpoints(struct dwc3 *dwc,
} else {
int ret;
- dep->endpoint.maxpacket = 1024;
+ usb_ep_set_maxpacket_limit(&dep->endpoint, 1024);
dep->endpoint.max_streams = 15;
dep->endpoint.ops = &dwc3_gadget_ep_ops;
list_add_tail(&dep->endpoint.ep_list,
@@ -2597,6 +2597,12 @@ int dwc3_gadget_init(struct dwc3 *dwc)
dwc->gadget.name = "dwc3-gadget";
/*
+ * Per databook, DWC3 needs buffer size to be aligned to MaxPacketSize
+ * on ep out.
+ */
+ dwc->gadget.quirk_ep_out_aligned_size = true;
+
+ /*
* REVISIT: Here we should clear all pending IRQs to be
* sure we're starting from a well known location.
*/
diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig
index f66d96ad1f51..ef5075eaaa7a 100644
--- a/drivers/usb/gadget/Kconfig
+++ b/drivers/usb/gadget/Kconfig
@@ -512,9 +512,6 @@ config USB_U_SERIAL
config USB_U_ETHER
tristate
-config USB_U_RNDIS
- tristate
-
config USB_F_SERIAL
tristate
@@ -542,6 +539,9 @@ config USB_F_RNDIS
config USB_F_MASS_STORAGE
tristate
+config USB_F_FS
+ tristate
+
choice
tristate "USB Gadget Drivers"
default USB_ETH
@@ -642,7 +642,6 @@ config USB_CONFIGFS_RNDIS
depends on USB_CONFIGFS
depends on NET
select USB_U_ETHER
- select USB_U_RNDIS
select USB_F_RNDIS
help
Microsoft Windows XP bundles the "Remote NDIS" (RNDIS) protocol,
@@ -690,6 +689,31 @@ config USB_CONFIGFS_MASS_STORAGE
device (in much the same way as the "loop" device driver),
specified as a module parameter or sysfs option.
+config USB_CONFIGFS_F_LB_SS
+ boolean "Loopback and sourcesink function (for testing)"
+ depends on USB_CONFIGFS
+ select USB_F_SS_LB
+ help
+ Loopback function loops back a configurable number of transfers.
+ Sourcesink function either sinks and sources bulk data.
+ It also implements control requests, for "chapter 9" conformance.
+ 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.
+
+config USB_CONFIGFS_F_FS
+ boolean "Function filesystem (FunctionFS)"
+ depends on USB_CONFIGFS
+ select USB_F_FS
+ 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.
+
config USB_ZERO
tristate "Gadget Zero (DEVELOPMENT)"
select USB_LIBCOMPOSITE
@@ -760,7 +784,6 @@ config USB_ETH
depends on NET
select USB_LIBCOMPOSITE
select USB_U_ETHER
- select USB_U_RNDIS
select USB_F_ECM
select USB_F_SUBSET
select CRC32
@@ -864,6 +887,7 @@ config USB_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
@@ -883,6 +907,8 @@ 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.
@@ -891,7 +917,7 @@ config USB_FUNCTIONFS_RNDIS
bool "Include configuration with RNDIS (Ethernet)"
depends on USB_FUNCTIONFS && NET
select USB_U_ETHER
- select USB_U_RNDIS
+ select USB_F_RNDIS
help
Include a configuration with RNDIS function (Ethernet) and the Filesystem.
@@ -1065,7 +1091,6 @@ config USB_G_MULTI
config USB_G_MULTI_RNDIS
bool "RNDIS + CDC Serial + Storage configuration"
depends on USB_G_MULTI
- select USB_U_RNDIS
select USB_F_RNDIS
default y
help
diff --git a/drivers/usb/gadget/Makefile b/drivers/usb/gadget/Makefile
index f1af39603d4d..6cccdfed140c 100644
--- a/drivers/usb/gadget/Makefile
+++ b/drivers/usb/gadget/Makefile
@@ -7,7 +7,7 @@ ccflags-$(CONFIG_USB_GADGET_VERBOSE) += -DVERBOSE_DEBUG
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
+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
@@ -47,8 +47,6 @@ 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
-u_rndis-y := rndis.o
-obj-$(CONFIG_USB_U_RNDIS) += u_rndis.o
usb_f_ncm-y := f_ncm.o
obj-$(CONFIG_USB_F_NCM) += usb_f_ncm.o
usb_f_ecm-y := f_ecm.o
@@ -59,10 +57,12 @@ 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
+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
diff --git a/drivers/usb/gadget/acm_ms.c b/drivers/usb/gadget/acm_ms.c
index 7bfa134fe0e3..a252444cc0a7 100644
--- a/drivers/usb/gadget/acm_ms.c
+++ b/drivers/usb/gadget/acm_ms.c
@@ -107,7 +107,7 @@ static unsigned int fsg_num_buffers = CONFIG_USB_GADGET_STORAGE_NUM_BUFFERS;
*/
#define fsg_num_buffers CONFIG_USB_GADGET_STORAGE_NUM_BUFFERS
-#endif /* CONFIG_USB_DEBUG */
+#endif /* CONFIG_USB_GADGET_DEBUG_FILES */
FSG_MODULE_PARAMETERS(/* no prefix */, fsg_mod_data);
diff --git a/drivers/usb/gadget/amd5536udc.c b/drivers/usb/gadget/amd5536udc.c
index 54a1e2954cea..a04aa8b64472 100644
--- a/drivers/usb/gadget/amd5536udc.c
+++ b/drivers/usb/gadget/amd5536udc.c
@@ -446,7 +446,7 @@ static void ep_init(struct udc_regs __iomem *regs, struct udc_ep *ep)
ep->ep.ops = &udc_ep_ops;
INIT_LIST_HEAD(&ep->queue);
- ep->ep.maxpacket = (u16) ~0;
+ usb_ep_set_maxpacket_limit(&ep->ep,(u16) ~0);
/* set NAK */
tmp = readl(&ep->regs->ctl);
tmp |= AMD_BIT(UDC_EPCTL_SNAK);
@@ -1564,12 +1564,15 @@ static void udc_setup_endpoints(struct udc *dev)
}
/* EP0 max packet */
if (dev->gadget.speed == USB_SPEED_FULL) {
- dev->ep[UDC_EP0IN_IX].ep.maxpacket = UDC_FS_EP0IN_MAX_PKT_SIZE;
- dev->ep[UDC_EP0OUT_IX].ep.maxpacket =
- UDC_FS_EP0OUT_MAX_PKT_SIZE;
+ usb_ep_set_maxpacket_limit(&dev->ep[UDC_EP0IN_IX].ep,
+ UDC_FS_EP0IN_MAX_PKT_SIZE);
+ usb_ep_set_maxpacket_limit(&dev->ep[UDC_EP0OUT_IX].ep,
+ UDC_FS_EP0OUT_MAX_PKT_SIZE);
} else if (dev->gadget.speed == USB_SPEED_HIGH) {
- dev->ep[UDC_EP0IN_IX].ep.maxpacket = UDC_EP0IN_MAX_PKT_SIZE;
- dev->ep[UDC_EP0OUT_IX].ep.maxpacket = UDC_EP0OUT_MAX_PKT_SIZE;
+ usb_ep_set_maxpacket_limit(&dev->ep[UDC_EP0IN_IX].ep,
+ UDC_EP0IN_MAX_PKT_SIZE);
+ usb_ep_set_maxpacket_limit(&dev->ep[UDC_EP0OUT_IX].ep,
+ UDC_EP0OUT_MAX_PKT_SIZE);
}
/*
@@ -3338,7 +3341,7 @@ static int udc_remote_wakeup(struct udc *dev)
}
/* PCI device parameters */
-static DEFINE_PCI_DEVICE_TABLE(pci_id) = {
+static const struct pci_device_id pci_id[] = {
{
PCI_DEVICE(PCI_VENDOR_ID_AMD, 0x2096),
.class = (PCI_CLASS_SERIAL_USB << 8) | 0xfe,
diff --git a/drivers/usb/gadget/at91_udc.c b/drivers/usb/gadget/at91_udc.c
index 4cc4fd6d1473..0353b6471bde 100644
--- a/drivers/usb/gadget/at91_udc.c
+++ b/drivers/usb/gadget/at91_udc.c
@@ -834,7 +834,7 @@ static void udc_reinit(struct at91_udc *udc)
ep->ep.desc = NULL;
ep->stopped = 0;
ep->fifo_bank = 0;
- ep->ep.maxpacket = ep->maxpacket;
+ usb_ep_set_maxpacket_limit(&ep->ep, ep->maxpacket);
ep->creg = (void __iomem *) udc->udp_baseaddr + AT91_UDP_CSR(i);
/* initialize one queue per endpoint */
INIT_LIST_HEAD(&ep->queue);
@@ -1759,15 +1759,15 @@ static int at91udc_probe(struct platform_device *pdev)
/* newer chips have more FIFO memory than rm9200 */
if (cpu_is_at91sam9260() || cpu_is_at91sam9g20()) {
- udc->ep[0].maxpacket = 64;
- udc->ep[3].maxpacket = 64;
- udc->ep[4].maxpacket = 512;
- udc->ep[5].maxpacket = 512;
+ usb_ep_set_maxpacket_limit(&udc->ep[0], 64);
+ usb_ep_set_maxpacket_limit(&udc->ep[3], 64);
+ usb_ep_set_maxpacket_limit(&udc->ep[4], 512);
+ usb_ep_set_maxpacket_limit(&udc->ep[5], 512);
} else if (cpu_is_at91sam9261() || cpu_is_at91sam9g10()) {
- udc->ep[3].maxpacket = 64;
+ usb_ep_set_maxpacket_limit(&udc->ep[3], 64);
} else if (cpu_is_at91sam9263()) {
- udc->ep[0].maxpacket = 64;
- udc->ep[3].maxpacket = 64;
+ usb_ep_set_maxpacket_limit(&udc->ep[0], 64);
+ usb_ep_set_maxpacket_limit(&udc->ep[3], 64);
}
udc->udp_baseaddr = ioremap(res->start, resource_size(res));
diff --git a/drivers/usb/gadget/atmel_usba_udc.c b/drivers/usb/gadget/atmel_usba_udc.c
index 2cb52e0438df..38bf67b1a97d 100644
--- a/drivers/usb/gadget/atmel_usba_udc.c
+++ b/drivers/usb/gadget/atmel_usba_udc.c
@@ -1012,7 +1012,7 @@ static void nop_release(struct device *dev)
}
-struct usb_gadget usba_gadget_template = {
+static struct usb_gadget usba_gadget_template = {
.ops = &usba_udc_ops,
.max_speed = USB_SPEED_HIGH,
.name = "atmel_usba_udc",
@@ -1904,7 +1904,7 @@ static struct usba_ep * atmel_udc_of_init(struct platform_device *pdev,
ep->dma_regs = udc->regs + USBA_DMA_BASE(i);
ep->fifo = udc->fifo + USBA_FIFO_BASE(i);
ep->ep.ops = &usba_ep_ops;
- ep->ep.maxpacket = ep->fifo_size;
+ usb_ep_set_maxpacket_limit(&ep->ep, ep->fifo_size);
ep->udc = udc;
INIT_LIST_HEAD(&ep->queue);
@@ -1957,7 +1957,8 @@ static struct usba_ep * usba_udc_pdata(struct platform_device *pdev,
ep->fifo = udc->fifo + USBA_FIFO_BASE(i);
ep->ep.ops = &usba_ep_ops;
ep->ep.name = pdata->ep[i].name;
- ep->fifo_size = ep->ep.maxpacket = pdata->ep[i].fifo_size;
+ ep->fifo_size = pdata->ep[i].fifo_size;
+ usb_ep_set_maxpacket_limit(&ep->ep, ep->fifo_size);
ep->udc = udc;
INIT_LIST_HEAD(&ep->queue);
ep->nr_banks = pdata->ep[i].nr_banks;
@@ -1995,14 +1996,12 @@ static int __init usba_udc_probe(struct platform_device *pdev)
if (irq < 0)
return irq;
- pclk = clk_get(&pdev->dev, "pclk");
+ pclk = devm_clk_get(&pdev->dev, "pclk");
if (IS_ERR(pclk))
return PTR_ERR(pclk);
- hclk = clk_get(&pdev->dev, "hclk");
- if (IS_ERR(hclk)) {
- ret = PTR_ERR(hclk);
- goto err_get_hclk;
- }
+ hclk = devm_clk_get(&pdev->dev, "hclk");
+ if (IS_ERR(hclk))
+ return PTR_ERR(hclk);
spin_lock_init(&udc->lock);
udc->pdev = pdev;
@@ -2011,17 +2010,17 @@ static int __init usba_udc_probe(struct platform_device *pdev)
udc->vbus_pin = -ENODEV;
ret = -ENOMEM;
- udc->regs = ioremap(regs->start, resource_size(regs));
+ udc->regs = devm_ioremap(&pdev->dev, regs->start, resource_size(regs));
if (!udc->regs) {
dev_err(&pdev->dev, "Unable to map I/O memory, aborting.\n");
- goto err_map_regs;
+ return ret;
}
dev_info(&pdev->dev, "MMIO registers at 0x%08lx mapped at %p\n",
(unsigned long)regs->start, udc->regs);
- udc->fifo = ioremap(fifo->start, resource_size(fifo));
+ udc->fifo = devm_ioremap(&pdev->dev, fifo->start, resource_size(fifo));
if (!udc->fifo) {
dev_err(&pdev->dev, "Unable to map FIFO, aborting.\n");
- goto err_map_fifo;
+ return ret;
}
dev_info(&pdev->dev, "FIFO at 0x%08lx mapped at %p\n",
(unsigned long)fifo->start, udc->fifo);
@@ -2032,7 +2031,7 @@ static int __init usba_udc_probe(struct platform_device *pdev)
ret = clk_prepare_enable(pclk);
if (ret) {
dev_err(&pdev->dev, "Unable to enable pclk, aborting.\n");
- goto err_clk_enable;
+ return ret;
}
toggle_bias(0);
usba_writel(udc, CTRL, USBA_DISABLE_MASK);
@@ -2043,22 +2042,22 @@ static int __init usba_udc_probe(struct platform_device *pdev)
else
udc->usba_ep = usba_udc_pdata(pdev, udc);
- if (IS_ERR(udc->usba_ep)) {
- ret = PTR_ERR(udc->usba_ep);
- goto err_alloc_ep;
- }
+ if (IS_ERR(udc->usba_ep))
+ return PTR_ERR(udc->usba_ep);
- ret = request_irq(irq, usba_udc_irq, 0, "atmel_usba_udc", udc);
+ ret = devm_request_irq(&pdev->dev, irq, usba_udc_irq, 0,
+ "atmel_usba_udc", udc);
if (ret) {
dev_err(&pdev->dev, "Cannot request irq %d (error %d)\n",
irq, ret);
- goto err_request_irq;
+ return ret;
}
udc->irq = irq;
if (gpio_is_valid(udc->vbus_pin)) {
if (!devm_gpio_request(&pdev->dev, udc->vbus_pin, "atmel_usba_udc")) {
- ret = request_irq(gpio_to_irq(udc->vbus_pin),
+ ret = devm_request_irq(&pdev->dev,
+ gpio_to_irq(udc->vbus_pin),
usba_vbus_irq, 0,
"atmel_usba_udc", udc);
if (ret) {
@@ -2077,31 +2076,13 @@ static int __init usba_udc_probe(struct platform_device *pdev)
ret = usb_add_gadget_udc(&pdev->dev, &udc->gadget);
if (ret)
- goto err_add_udc;
+ return ret;
usba_init_debugfs(udc);
for (i = 1; i < udc->num_ep; i++)
usba_ep_init_debugfs(udc, &udc->usba_ep[i]);
return 0;
-
-err_add_udc:
- if (gpio_is_valid(udc->vbus_pin))
- free_irq(gpio_to_irq(udc->vbus_pin), udc);
-
- free_irq(irq, udc);
-err_request_irq:
-err_alloc_ep:
-err_clk_enable:
- iounmap(udc->fifo);
-err_map_fifo:
- iounmap(udc->regs);
-err_map_regs:
- clk_put(hclk);
-err_get_hclk:
- clk_put(pclk);
-
- return ret;
}
static int __exit usba_udc_remove(struct platform_device *pdev)
@@ -2117,16 +2098,6 @@ static int __exit usba_udc_remove(struct platform_device *pdev)
usba_ep_cleanup_debugfs(&udc->usba_ep[i]);
usba_cleanup_debugfs(udc);
- if (gpio_is_valid(udc->vbus_pin)) {
- free_irq(gpio_to_irq(udc->vbus_pin), udc);
- }
-
- free_irq(udc->irq, udc);
- iounmap(udc->fifo);
- iounmap(udc->regs);
- clk_put(udc->hclk);
- clk_put(udc->pclk);
-
return 0;
}
diff --git a/drivers/usb/gadget/bcm63xx_udc.c b/drivers/usb/gadget/bcm63xx_udc.c
index c58fcf1ebe41..2ac7a8f4bfff 100644
--- a/drivers/usb/gadget/bcm63xx_udc.c
+++ b/drivers/usb/gadget/bcm63xx_udc.c
@@ -549,7 +549,7 @@ static void bcm63xx_ep_setup(struct bcm63xx_udc *udc)
if (idx < 0)
continue;
- udc->bep[idx].ep.maxpacket = max_pkt;
+ usb_ep_set_maxpacket_limit(&udc->bep[idx].ep, max_pkt);
val = (idx << USBD_CSR_EP_LOG_SHIFT) |
(cfg->dir << USBD_CSR_EP_DIR_SHIFT) |
@@ -943,7 +943,7 @@ static int bcm63xx_init_udc_hw(struct bcm63xx_udc *udc)
bep->ep.ops = &bcm63xx_udc_ep_ops;
list_add_tail(&bep->ep.ep_list, &udc->gadget.ep_list);
bep->halted = 0;
- bep->ep.maxpacket = BCM63XX_MAX_CTRL_PKT;
+ usb_ep_set_maxpacket_limit(&bep->ep, BCM63XX_MAX_CTRL_PKT);
bep->udc = udc;
bep->ep.desc = NULL;
INIT_LIST_HEAD(&bep->queue);
diff --git a/drivers/usb/gadget/composite.c b/drivers/usb/gadget/composite.c
index 2018ba1a2172..d742bed7a5fa 100644
--- a/drivers/usb/gadget/composite.c
+++ b/drivers/usb/gadget/composite.c
@@ -1452,8 +1452,22 @@ unknown:
struct usb_configuration *c;
c = cdev->config;
- if (c && c->setup)
+ if (!c)
+ goto done;
+
+ /* try current config's setup */
+ if (c->setup) {
value = c->setup(c, ctrl);
+ goto done;
+ }
+
+ /* try the only function in the current config */
+ if (!list_is_singular(&c->functions))
+ goto done;
+ f = list_first_entry(&c->functions, struct usb_function,
+ list);
+ if (f->setup)
+ value = f->setup(f, ctrl);
}
goto done;
@@ -1714,7 +1728,7 @@ composite_resume(struct usb_gadget *gadget)
{
struct usb_composite_dev *cdev = get_gadget_data(gadget);
struct usb_function *f;
- u8 maxpower;
+ u16 maxpower;
/* REVISIT: should we have config level
* suspend/resume callbacks?
diff --git a/drivers/usb/gadget/configfs.c b/drivers/usb/gadget/configfs.c
index 25885112fa35..7d1cc01796b6 100644
--- a/drivers/usb/gadget/configfs.c
+++ b/drivers/usb/gadget/configfs.c
@@ -4,6 +4,7 @@
#include <linux/device.h>
#include <linux/usb/composite.h>
#include <linux/usb/gadget_configfs.h>
+#include "configfs.h"
int check_user_usb_string(const char *name,
struct usb_gadget_strings *stringtab_dev)
@@ -564,6 +565,13 @@ static struct config_group *function_make(
usb_put_function_instance(fi);
return ERR_PTR(ret);
}
+ if (fi->set_inst_name) {
+ ret = fi->set_inst_name(fi, instance_name);
+ if (ret) {
+ usb_put_function_instance(fi);
+ return ERR_PTR(ret);
+ }
+ }
gi = container_of(group, struct gadget_info, functions_group);
diff --git a/drivers/usb/gadget/dummy_hcd.c b/drivers/usb/gadget/dummy_hcd.c
index 8f4dae310923..8c06430dcc47 100644
--- a/drivers/usb/gadget/dummy_hcd.c
+++ b/drivers/usb/gadget/dummy_hcd.c
@@ -951,7 +951,7 @@ static void init_dummy_udc_hw(struct dummy *dum)
list_add_tail(&ep->ep.ep_list, &dum->gadget.ep_list);
ep->halted = ep->wedged = ep->already_seen =
ep->setup_stage = 0;
- ep->ep.maxpacket = ~0;
+ usb_ep_set_maxpacket_limit(&ep->ep, ~0);
ep->ep.max_streams = 16;
ep->last_io = jiffies;
ep->gadget = &dum->gadget;
diff --git a/drivers/usb/gadget/epautoconf.c b/drivers/usb/gadget/epautoconf.c
index a777f7bd11b4..358de320afb0 100644
--- a/drivers/usb/gadget/epautoconf.c
+++ b/drivers/usb/gadget/epautoconf.c
@@ -58,7 +58,7 @@ ep_matches (
return 0;
/* only support ep0 for portable CONTROL traffic */
- type = desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK;
+ type = usb_endpoint_type(desc);
if (USB_ENDPOINT_XFER_CONTROL == type)
return 0;
@@ -129,7 +129,7 @@ ep_matches (
* and wants to know the maximum possible, provide the info.
*/
if (desc->wMaxPacketSize == 0)
- desc->wMaxPacketSize = cpu_to_le16(ep->maxpacket);
+ desc->wMaxPacketSize = cpu_to_le16(ep->maxpacket_limit);
/* endpoint maxpacket size is an input parameter, except for bulk
* where it's an output parameter representing the full speed limit.
@@ -145,7 +145,7 @@ ep_matches (
case USB_ENDPOINT_XFER_ISOC:
/* ISO: limit 1023 bytes full speed, 1024 high/super speed */
- if (ep->maxpacket < max)
+ if (ep->maxpacket_limit < max)
return 0;
if (!gadget_is_dualspeed(gadget) && max > 1023)
return 0;
@@ -178,7 +178,7 @@ ep_matches (
/* report (variable) full speed bulk maxpacket */
if ((USB_ENDPOINT_XFER_BULK == type) && !ep_comp) {
- int size = ep->maxpacket;
+ int size = ep->maxpacket_limit;
/* min() doesn't work on bitfields with gcc-3.5 */
if (size > 64)
diff --git a/drivers/usb/gadget/f_ecm.c b/drivers/usb/gadget/f_ecm.c
index 8d9e6f7e8f1a..798760fa7e70 100644
--- a/drivers/usb/gadget/f_ecm.c
+++ b/drivers/usb/gadget/f_ecm.c
@@ -691,7 +691,6 @@ ecm_bind(struct usb_configuration *c, struct usb_function *f)
int status;
struct usb_ep *ep;
-#ifndef USBF_ECM_INCLUDED
struct f_ecm_opts *ecm_opts;
if (!can_support_ecm(cdev->gadget))
@@ -715,7 +714,7 @@ ecm_bind(struct usb_configuration *c, struct usb_function *f)
return status;
ecm_opts->bound = true;
}
-#endif
+
us = usb_gstrings_attach(cdev, ecm_strings,
ARRAY_SIZE(ecm_string_defs));
if (IS_ERR(us))
@@ -834,74 +833,6 @@ fail:
return status;
}
-#ifdef USBF_ECM_INCLUDED
-
-static void
-ecm_old_unbind(struct usb_configuration *c, struct usb_function *f)
-{
- struct f_ecm *ecm = func_to_ecm(f);
-
- DBG(c->cdev, "ecm unbind\n");
-
- usb_free_all_descriptors(f);
-
- kfree(ecm->notify_req->buf);
- usb_ep_free_request(ecm->notify, ecm->notify_req);
- kfree(ecm);
-}
-
-/**
- * ecm_bind_config - add CDC Ethernet network link to a configuration
- * @c: the configuration to support the network link
- * @ethaddr: a buffer in which the ethernet address of the host side
- * side of the link was recorded
- * @dev: eth_dev structure
- * Context: single threaded during gadget setup
- *
- * Returns zero on success, else negative errno.
- *
- * Caller must have called @gether_setup(). Caller is also responsible
- * for calling @gether_cleanup() before module unload.
- */
-int
-ecm_bind_config(struct usb_configuration *c, u8 ethaddr[ETH_ALEN],
- struct eth_dev *dev)
-{
- struct f_ecm *ecm;
- int status;
-
- if (!can_support_ecm(c->cdev->gadget) || !ethaddr)
- return -EINVAL;
-
- /* allocate and initialize one new instance */
- ecm = kzalloc(sizeof *ecm, GFP_KERNEL);
- if (!ecm)
- return -ENOMEM;
-
- /* export host's Ethernet address in CDC format */
- snprintf(ecm->ethaddr, sizeof ecm->ethaddr, "%pm", ethaddr);
- ecm_string_defs[1].s = ecm->ethaddr;
-
- ecm->port.ioport = dev;
- ecm->port.cdc_filter = DEFAULT_FILTER;
-
- ecm->port.func.name = "cdc_ethernet";
- /* descriptors are per-instance copies */
- ecm->port.func.bind = ecm_bind;
- ecm->port.func.unbind = ecm_old_unbind;
- ecm->port.func.set_alt = ecm_set_alt;
- ecm->port.func.get_alt = ecm_get_alt;
- ecm->port.func.setup = ecm_setup;
- ecm->port.func.disable = ecm_disable;
-
- status = usb_add_function(c, &ecm->port.func);
- if (status)
- kfree(ecm);
- return status;
-}
-
-#else
-
static inline struct f_ecm_opts *to_f_ecm_opts(struct config_item *item)
{
return container_of(to_config_group(item), struct f_ecm_opts,
@@ -1040,5 +971,3 @@ static struct usb_function *ecm_alloc(struct usb_function_instance *fi)
DECLARE_USB_FUNCTION_INIT(ecm, ecm_alloc_inst, ecm_alloc);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("David Brownell");
-
-#endif
diff --git a/drivers/usb/gadget/f_fs.c b/drivers/usb/gadget/f_fs.c
index 241fc873ffa4..306a2b52125c 100644
--- a/drivers/usb/gadget/f_fs.c
+++ b/drivers/usb/gadget/f_fs.c
@@ -22,218 +22,42 @@
#include <linux/pagemap.h>
#include <linux/export.h>
#include <linux/hid.h>
+#include <linux/module.h>
#include <asm/unaligned.h>
#include <linux/usb/composite.h>
#include <linux/usb/functionfs.h>
+#include "u_fs.h"
+#include "configfs.h"
#define FUNCTIONFS_MAGIC 0xa647361 /* Chosen by a honest dice roll ;) */
-
-/* Debugging ****************************************************************/
-
-#ifdef VERBOSE_DEBUG
-#ifndef pr_vdebug
-# define pr_vdebug pr_debug
-#endif /* pr_vdebug */
-# define ffs_dump_mem(prefix, ptr, len) \
- print_hex_dump_bytes(pr_fmt(prefix ": "), DUMP_PREFIX_NONE, ptr, len)
-#else
-#ifndef pr_vdebug
-# define pr_vdebug(...) do { } while (0)
-#endif /* pr_vdebug */
-# define ffs_dump_mem(prefix, ptr, len) do { } while (0)
-#endif /* VERBOSE_DEBUG */
-
-#define ENTER() pr_vdebug("%s()\n", __func__)
-
-
-/* The data structure and setup file ****************************************/
-
-enum ffs_state {
- /*
- * Waiting for descriptors and strings.
- *
- * In this state no open(2), read(2) or write(2) on epfiles
- * may succeed (which should not be the problem as there
- * should be no such files opened in the first place).
- */
- FFS_READ_DESCRIPTORS,
- FFS_READ_STRINGS,
-
- /*
- * We've got descriptors and strings. We are or have called
- * functionfs_ready_callback(). functionfs_bind() may have
- * been called but we don't know.
- *
- * This is the only state in which operations on epfiles may
- * succeed.
- */
- FFS_ACTIVE,
-
- /*
- * All endpoints have been closed. This state is also set if
- * we encounter an unrecoverable error. The only
- * unrecoverable error is situation when after reading strings
- * from user space we fail to initialise epfiles or
- * functionfs_ready_callback() returns with error (<0).
- *
- * In this state no open(2), read(2) or write(2) (both on ep0
- * as well as epfile) may succeed (at this point epfiles are
- * unlinked and all closed so this is not a problem; ep0 is
- * also closed but ep0 file exists and so open(2) on ep0 must
- * fail).
- */
- FFS_CLOSING
-};
-
-
-enum ffs_setup_state {
- /* There is no setup request pending. */
- FFS_NO_SETUP,
- /*
- * User has read events and there was a setup request event
- * there. The next read/write on ep0 will handle the
- * request.
- */
- FFS_SETUP_PENDING,
- /*
- * There was event pending but before user space handled it
- * some other event was introduced which canceled existing
- * setup. If this state is set read/write on ep0 return
- * -EIDRM. This state is only set when adding event.
- */
- FFS_SETUP_CANCELED
-};
-
-
-
-struct ffs_epfile;
-struct ffs_function;
-
-struct ffs_data {
- struct usb_gadget *gadget;
-
- /*
- * Protect access read/write operations, only one read/write
- * at a time. As a consequence protects ep0req and company.
- * While setup request is being processed (queued) this is
- * held.
- */
- struct mutex mutex;
-
- /*
- * Protect access to endpoint related structures (basically
- * usb_ep_queue(), usb_ep_dequeue(), etc. calls) except for
- * endpoint zero.
- */
- spinlock_t eps_lock;
-
- /*
- * XXX REVISIT do we need our own request? Since we are not
- * handling setup requests immediately user space may be so
- * slow that another setup will be sent to the gadget but this
- * time not to us but another function and then there could be
- * a race. Is that the case? Or maybe we can use cdev->req
- * after all, maybe we just need some spinlock for that?
- */
- struct usb_request *ep0req; /* P: mutex */
- struct completion ep0req_completion; /* P: mutex */
- int ep0req_status; /* P: mutex */
-
- /* reference counter */
- atomic_t ref;
- /* how many files are opened (EP0 and others) */
- atomic_t opened;
-
- /* EP0 state */
- enum ffs_state state;
-
- /*
- * Possible transitions:
- * + FFS_NO_SETUP -> FFS_SETUP_PENDING -- P: ev.waitq.lock
- * happens only in ep0 read which is P: mutex
- * + FFS_SETUP_PENDING -> FFS_NO_SETUP -- P: ev.waitq.lock
- * happens only in ep0 i/o which is P: mutex
- * + FFS_SETUP_PENDING -> FFS_SETUP_CANCELED -- P: ev.waitq.lock
- * + FFS_SETUP_CANCELED -> FFS_NO_SETUP -- cmpxchg
- */
- enum ffs_setup_state setup_state;
-
-#define FFS_SETUP_STATE(ffs) \
- ((enum ffs_setup_state)cmpxchg(&(ffs)->setup_state, \
- FFS_SETUP_CANCELED, FFS_NO_SETUP))
-
- /* Events & such. */
- struct {
- u8 types[4];
- unsigned short count;
- /* XXX REVISIT need to update it in some places, or do we? */
- unsigned short can_stall;
- struct usb_ctrlrequest setup;
-
- wait_queue_head_t waitq;
- } ev; /* the whole structure, P: ev.waitq.lock */
-
- /* Flags */
- unsigned long flags;
-#define FFS_FL_CALL_CLOSED_CALLBACK 0
-#define FFS_FL_BOUND 1
-
- /* Active function */
- struct ffs_function *func;
-
- /*
- * Device name, write once when file system is mounted.
- * Intended for user to read if she wants.
- */
- const char *dev_name;
- /* Private data for our user (ie. gadget). Managed by user. */
- void *private_data;
-
- /* filled by __ffs_data_got_descs() */
- /*
- * Real descriptors are 16 bytes after raw_descs (so you need
- * to skip 16 bytes (ie. ffs->raw_descs + 16) to get to the
- * first full speed descriptor). raw_descs_length and
- * raw_fs_descs_length do not have those 16 bytes added.
- */
- const void *raw_descs;
- unsigned raw_descs_length;
- unsigned raw_fs_descs_length;
- unsigned fs_descs_count;
- unsigned hs_descs_count;
-
- unsigned short strings_count;
- unsigned short interfaces_count;
- unsigned short eps_count;
- unsigned short _pad1;
-
- /* filled by __ffs_data_got_strings() */
- /* ids in stringtabs are set in functionfs_bind() */
- const void *raw_strings;
- struct usb_gadget_strings **stringtabs;
-
- /*
- * File system's super block, write once when file system is
- * mounted.
- */
- struct super_block *sb;
-
- /* File permissions, written once when fs is mounted */
- struct ffs_file_perms {
- umode_t mode;
- kuid_t uid;
- kgid_t gid;
- } file_perms;
-
- /*
- * The endpoint files, filled by ffs_epfiles_create(),
- * destroyed by ffs_epfiles_destroy().
- */
- struct ffs_epfile *epfiles;
-};
+/* Variable Length Array Macros **********************************************/
+#define vla_group(groupname) size_t groupname##__next = 0
+#define vla_group_size(groupname) groupname##__next
+
+#define vla_item(groupname, type, name, n) \
+ size_t groupname##_##name##__offset = ({ \
+ size_t align_mask = __alignof__(type) - 1; \
+ size_t offset = (groupname##__next + align_mask) & ~align_mask;\
+ size_t size = (n) * sizeof(type); \
+ groupname##__next = offset + size; \
+ offset; \
+ })
+
+#define vla_item_with_sz(groupname, type, name, n) \
+ size_t groupname##_##name##__sz = (n) * sizeof(type); \
+ size_t groupname##_##name##__offset = ({ \
+ size_t align_mask = __alignof__(type) - 1; \
+ size_t offset = (groupname##__next + align_mask) & ~align_mask;\
+ size_t size = groupname##_##name##__sz; \
+ groupname##__next = offset + size; \
+ offset; \
+ })
+
+#define vla_ptr(ptr, groupname, name) \
+ ((void *) ((char *)ptr + groupname##_##name##__offset))
/* Reference counter handling */
static void ffs_data_get(struct ffs_data *ffs);
@@ -274,15 +98,12 @@ static struct ffs_function *ffs_func_from_usb(struct usb_function *f)
return container_of(f, struct ffs_function, function);
}
-static void ffs_func_free(struct ffs_function *func);
static void ffs_func_eps_disable(struct ffs_function *func);
static int __must_check ffs_func_eps_enable(struct ffs_function *func);
static int ffs_func_bind(struct usb_configuration *,
struct usb_function *);
-static void ffs_func_unbind(struct usb_configuration *,
- struct usb_function *);
static int ffs_func_set_alt(struct usb_function *, unsigned, unsigned);
static void ffs_func_disable(struct usb_function *);
static int ffs_func_setup(struct usb_function *,
@@ -335,6 +156,17 @@ ffs_sb_create_file(struct super_block *sb, const char *name, void *data,
const struct file_operations *fops,
struct dentry **dentry_p);
+/* Devices management *******************************************************/
+
+DEFINE_MUTEX(ffs_lock);
+EXPORT_SYMBOL(ffs_lock);
+
+static struct ffs_dev *ffs_find_dev(const char *name);
+static int _ffs_name_dev(struct ffs_dev *dev, const char *name);
+static void *ffs_acquire_dev(const char *dev_name);
+static void ffs_release_dev(struct ffs_data *ffs_data);
+static int ffs_ready(struct ffs_data *ffs);
+static void ffs_closed(struct ffs_data *ffs);
/* Misc helper functions ****************************************************/
@@ -460,7 +292,7 @@ static ssize_t ffs_ep0_write(struct file *file, const char __user *buf,
ffs->state = FFS_ACTIVE;
mutex_unlock(&ffs->mutex);
- ret = functionfs_ready_callback(ffs);
+ ret = ffs_ready(ffs);
if (unlikely(ret < 0)) {
ffs->state = FFS_CLOSING;
return ret;
@@ -753,78 +585,71 @@ static ssize_t ffs_epfile_io(struct file *file,
char __user *buf, size_t len, int read)
{
struct ffs_epfile *epfile = file->private_data;
+ struct usb_gadget *gadget = epfile->ffs->gadget;
struct ffs_ep *ep;
char *data = NULL;
- ssize_t ret;
+ ssize_t ret, data_len;
int halt;
- goto first_try;
- do {
- spin_unlock_irq(&epfile->ffs->eps_lock);
- mutex_unlock(&epfile->mutex);
+ /* Are we still active? */
+ if (WARN_ON(epfile->ffs->state != FFS_ACTIVE)) {
+ ret = -ENODEV;
+ goto error;
+ }
-first_try:
- /* Are we still active? */
- if (WARN_ON(epfile->ffs->state != FFS_ACTIVE)) {
- ret = -ENODEV;
+ /* Wait for endpoint to be enabled */
+ ep = epfile->ep;
+ if (!ep) {
+ if (file->f_flags & O_NONBLOCK) {
+ ret = -EAGAIN;
goto error;
}
- /* Wait for endpoint to be enabled */
- ep = epfile->ep;
- if (!ep) {
- if (file->f_flags & O_NONBLOCK) {
- ret = -EAGAIN;
- goto error;
- }
-
- if (wait_event_interruptible(epfile->wait,
- (ep = epfile->ep))) {
- ret = -EINTR;
- goto error;
- }
- }
-
- /* Do we halt? */
- halt = !read == !epfile->in;
- if (halt && epfile->isoc) {
- ret = -EINVAL;
+ ret = wait_event_interruptible(epfile->wait, (ep = epfile->ep));
+ if (ret) {
+ ret = -EINTR;
goto error;
}
+ }
- /* Allocate & copy */
- if (!halt && !data) {
- data = kzalloc(len, GFP_KERNEL);
- if (unlikely(!data))
- return -ENOMEM;
+ /* Do we halt? */
+ halt = !read == !epfile->in;
+ if (halt && epfile->isoc) {
+ ret = -EINVAL;
+ goto error;
+ }
- if (!read &&
- unlikely(__copy_from_user(data, buf, len))) {
- ret = -EFAULT;
- goto error;
- }
- }
+ /* Allocate & copy */
+ if (!halt) {
+ /*
+ * Controller may require buffer size to be aligned to
+ * maxpacketsize of an out endpoint.
+ */
+ data_len = read ? usb_ep_align_maybe(gadget, ep->ep, len) : len;
+
+ data = kmalloc(data_len, GFP_KERNEL);
+ if (unlikely(!data))
+ return -ENOMEM;
- /* We will be using request */
- ret = ffs_mutex_lock(&epfile->mutex,
- file->f_flags & O_NONBLOCK);
- if (unlikely(ret))
+ if (!read && unlikely(copy_from_user(data, buf, len))) {
+ ret = -EFAULT;
goto error;
+ }
+ }
- /*
- * We're called from user space, we can use _irq rather then
- * _irqsave
- */
- spin_lock_irq(&epfile->ffs->eps_lock);
+ /* We will be using request */
+ ret = ffs_mutex_lock(&epfile->mutex, file->f_flags & O_NONBLOCK);
+ if (unlikely(ret))
+ goto error;
- /*
- * While we were acquiring mutex endpoint got disabled
- * or changed?
- */
- } while (unlikely(epfile->ep != ep));
+ spin_lock_irq(&epfile->ffs->eps_lock);
- /* Halt */
- if (unlikely(halt)) {
+ if (epfile->ep != ep) {
+ /* In the meantime, endpoint got disabled or changed. */
+ ret = -ESHUTDOWN;
+ spin_unlock_irq(&epfile->ffs->eps_lock);
+ } else if (halt) {
+ /* Halt */
if (likely(epfile->ep == ep) && !WARN_ON(!ep->ep))
usb_ep_set_halt(ep->ep);
spin_unlock_irq(&epfile->ffs->eps_lock);
@@ -837,7 +662,7 @@ first_try:
req->context = &done;
req->complete = ffs_epfile_io_complete;
req->buf = data;
- req->length = len;
+ req->length = data_len;
ret = usb_ep_queue(ep->ep, req, GFP_ATOMIC);
@@ -849,9 +674,17 @@ first_try:
ret = -EINTR;
usb_ep_dequeue(ep->ep, req);
} else {
+ /*
+ * XXX We may end up silently droping data here.
+ * Since data_len (i.e. req->length) may be bigger
+ * than len (after being rounded up to maxpacketsize),
+ * we may end up with more data then user space has
+ * space for.
+ */
ret = ep->status;
if (read && ret > 0 &&
- unlikely(copy_to_user(buf, data, ret)))
+ unlikely(copy_to_user(buf, data,
+ min_t(size_t, ret, len))))
ret = -EFAULT;
}
}
@@ -1191,7 +1024,7 @@ ffs_fs_mount(struct file_system_type *t, int flags,
return ERR_PTR(-ENOMEM);
}
- ffs_dev = functionfs_acquire_dev_callback(dev_name);
+ ffs_dev = ffs_acquire_dev(dev_name);
if (IS_ERR(ffs_dev)) {
ffs_data_put(ffs);
return ERR_CAST(ffs_dev);
@@ -1201,7 +1034,7 @@ ffs_fs_mount(struct file_system_type *t, int flags,
rv = mount_nodev(t, flags, &data, ffs_sb_fill);
if (IS_ERR(rv) && data.ffs_data) {
- functionfs_release_dev_callback(data.ffs_data);
+ ffs_release_dev(data.ffs_data);
ffs_data_put(data.ffs_data);
}
return rv;
@@ -1214,7 +1047,7 @@ ffs_fs_kill_sb(struct super_block *sb)
kill_litter_super(sb);
if (sb->s_fs_info) {
- functionfs_release_dev_callback(sb->s_fs_info);
+ ffs_release_dev(sb->s_fs_info);
ffs_data_put(sb->s_fs_info);
}
}
@@ -1327,7 +1160,7 @@ static void ffs_data_clear(struct ffs_data *ffs)
ENTER();
if (test_and_clear_bit(FFS_FL_CALL_CLOSED_CALLBACK, &ffs->flags))
- functionfs_closed_callback(ffs);
+ ffs_closed(ffs);
BUG_ON(ffs->gadget);
@@ -1463,71 +1296,6 @@ static void ffs_epfiles_destroy(struct ffs_epfile *epfiles, unsigned count)
kfree(epfiles);
}
-static int functionfs_bind_config(struct usb_composite_dev *cdev,
- struct usb_configuration *c,
- struct ffs_data *ffs)
-{
- struct ffs_function *func;
- int ret;
-
- ENTER();
-
- func = kzalloc(sizeof *func, GFP_KERNEL);
- if (unlikely(!func))
- return -ENOMEM;
-
- func->function.name = "Function FS Gadget";
- func->function.strings = ffs->stringtabs;
-
- func->function.bind = ffs_func_bind;
- func->function.unbind = ffs_func_unbind;
- func->function.set_alt = ffs_func_set_alt;
- func->function.disable = ffs_func_disable;
- func->function.setup = ffs_func_setup;
- func->function.suspend = ffs_func_suspend;
- func->function.resume = ffs_func_resume;
-
- func->conf = c;
- func->gadget = cdev->gadget;
- func->ffs = ffs;
- ffs_data_get(ffs);
-
- ret = usb_add_function(c, &func->function);
- if (unlikely(ret))
- ffs_func_free(func);
-
- return ret;
-}
-
-static void ffs_func_free(struct ffs_function *func)
-{
- struct ffs_ep *ep = func->eps;
- unsigned count = func->ffs->eps_count;
- unsigned long flags;
-
- ENTER();
-
- /* cleanup after autoconfig */
- spin_lock_irqsave(&func->ffs->eps_lock, flags);
- do {
- if (ep->ep && ep->req)
- usb_ep_free_request(ep->ep, ep->req);
- ep->req = NULL;
- ++ep;
- } while (--count);
- spin_unlock_irqrestore(&func->ffs->eps_lock, flags);
-
- ffs_data_put(func->ffs);
-
- kfree(func->eps);
- /*
- * eps and interfaces_nums are allocated in the same chunk so
- * only one free is required. Descriptors are also allocated
- * in the same chunk.
- */
-
- kfree(func);
-}
static void ffs_func_eps_disable(struct ffs_function *func)
{
@@ -1901,30 +1669,34 @@ static int __ffs_data_got_strings(struct ffs_data *ffs,
/* Allocate everything in one chunk so there's less maintenance. */
{
- struct {
- struct usb_gadget_strings *stringtabs[lang_count + 1];
- struct usb_gadget_strings stringtab[lang_count];
- struct usb_string strings[lang_count*(needed_count+1)];
- } *d;
unsigned i = 0;
+ vla_group(d);
+ vla_item(d, struct usb_gadget_strings *, stringtabs,
+ lang_count + 1);
+ vla_item(d, struct usb_gadget_strings, stringtab, lang_count);
+ vla_item(d, struct usb_string, strings,
+ lang_count*(needed_count+1));
+
+ char *vlabuf = kmalloc(vla_group_size(d), GFP_KERNEL);
- d = kmalloc(sizeof *d, GFP_KERNEL);
- if (unlikely(!d)) {
+ if (unlikely(!vlabuf)) {
kfree(_data);
return -ENOMEM;
}
- stringtabs = d->stringtabs;
- t = d->stringtab;
+ /* Initialize the VLA pointers */
+ stringtabs = vla_ptr(vlabuf, d, stringtabs);
+ t = vla_ptr(vlabuf, d, stringtab);
i = lang_count;
do {
*stringtabs++ = t++;
} while (--i);
*stringtabs = NULL;
- stringtabs = d->stringtabs;
- t = d->stringtab;
- s = d->strings;
+ /* stringtabs = vlabuf = d_stringtabs for later kfree */
+ stringtabs = vla_ptr(vlabuf, d, stringtabs);
+ t = vla_ptr(vlabuf, d, stringtab);
+ s = vla_ptr(vlabuf, d, strings);
strings = s;
}
@@ -2187,8 +1959,57 @@ static int __ffs_func_bind_do_nums(enum ffs_entity_type type, u8 *valuep,
return 0;
}
-static int ffs_func_bind(struct usb_configuration *c,
- struct usb_function *f)
+static inline struct f_fs_opts *ffs_do_functionfs_bind(struct usb_function *f,
+ struct usb_configuration *c)
+{
+ struct ffs_function *func = ffs_func_from_usb(f);
+ struct f_fs_opts *ffs_opts =
+ container_of(f->fi, struct f_fs_opts, func_inst);
+ int ret;
+
+ ENTER();
+
+ /*
+ * Legacy gadget triggers binding in functionfs_ready_callback,
+ * which already uses locking; taking the same lock here would
+ * cause a deadlock.
+ *
+ * Configfs-enabled gadgets however do need ffs_dev_lock.
+ */
+ if (!ffs_opts->no_configfs)
+ ffs_dev_lock();
+ ret = ffs_opts->dev->desc_ready ? 0 : -ENODEV;
+ func->ffs = ffs_opts->dev->ffs_data;
+ if (!ffs_opts->no_configfs)
+ ffs_dev_unlock();
+ if (ret)
+ return ERR_PTR(ret);
+
+ func->conf = c;
+ func->gadget = c->cdev->gadget;
+
+ ffs_data_get(func->ffs);
+
+ /*
+ * in drivers/usb/gadget/configfs.c:configfs_composite_bind()
+ * configurations are bound in sequence with list_for_each_entry,
+ * in each configuration its functions are bound in sequence
+ * with list_for_each_entry, so we assume no race condition
+ * with regard to ffs_opts->bound access
+ */
+ if (!ffs_opts->refcnt) {
+ ret = functionfs_bind(func->ffs, c->cdev);
+ if (ret)
+ return ERR_PTR(ret);
+ }
+ ffs_opts->refcnt++;
+ func->function.strings = func->ffs->stringtabs;
+
+ return ffs_opts;
+}
+
+static int _ffs_func_bind(struct usb_configuration *c,
+ struct usb_function *f)
{
struct ffs_function *func = ffs_func_from_usb(f);
struct ffs_data *ffs = func->ffs;
@@ -2200,16 +2021,16 @@ static int ffs_func_bind(struct usb_configuration *c,
int ret;
/* Make it a single chunk, less management later on */
- struct {
- struct ffs_ep eps[ffs->eps_count];
- struct usb_descriptor_header
- *fs_descs[full ? ffs->fs_descs_count + 1 : 0];
- struct usb_descriptor_header
- *hs_descs[high ? ffs->hs_descs_count + 1 : 0];
- short inums[ffs->interfaces_count];
- char raw_descs[high ? ffs->raw_descs_length
- : ffs->raw_fs_descs_length];
- } *data;
+ vla_group(d);
+ vla_item_with_sz(d, struct ffs_ep, eps, ffs->eps_count);
+ vla_item_with_sz(d, struct usb_descriptor_header *, fs_descs,
+ full ? ffs->fs_descs_count + 1 : 0);
+ vla_item_with_sz(d, struct usb_descriptor_header *, hs_descs,
+ high ? ffs->hs_descs_count + 1 : 0);
+ vla_item_with_sz(d, short, inums, ffs->interfaces_count);
+ vla_item_with_sz(d, char, raw_descs,
+ high ? ffs->raw_descs_length : ffs->raw_fs_descs_length);
+ char *vlabuf;
ENTER();
@@ -2217,21 +2038,28 @@ static int ffs_func_bind(struct usb_configuration *c,
if (unlikely(!(full | high)))
return -ENOTSUPP;
- /* Allocate */
- data = kmalloc(sizeof *data, GFP_KERNEL);
- if (unlikely(!data))
+ /* Allocate a single chunk, less management later on */
+ vlabuf = kmalloc(vla_group_size(d), GFP_KERNEL);
+ if (unlikely(!vlabuf))
return -ENOMEM;
/* Zero */
- memset(data->eps, 0, sizeof data->eps);
- memcpy(data->raw_descs, ffs->raw_descs + 16, sizeof data->raw_descs);
- memset(data->inums, 0xff, sizeof data->inums);
- for (ret = ffs->eps_count; ret; --ret)
- data->eps[ret].num = -1;
+ memset(vla_ptr(vlabuf, d, eps), 0, d_eps__sz);
+ memcpy(vla_ptr(vlabuf, d, raw_descs), ffs->raw_descs + 16,
+ d_raw_descs__sz);
+ memset(vla_ptr(vlabuf, d, inums), 0xff, d_inums__sz);
+ for (ret = ffs->eps_count; ret; --ret) {
+ struct ffs_ep *ptr;
+
+ ptr = vla_ptr(vlabuf, d, eps);
+ ptr[ret].num = -1;
+ }
- /* Save pointers */
- func->eps = data->eps;
- func->interfaces_nums = data->inums;
+ /* Save pointers
+ * d_eps == vlabuf, func->eps used to kfree vlabuf later
+ */
+ func->eps = vla_ptr(vlabuf, d, eps);
+ func->interfaces_nums = vla_ptr(vlabuf, d, inums);
/*
* Go through all the endpoint descriptors and allocate
@@ -2239,10 +2067,10 @@ static int ffs_func_bind(struct usb_configuration *c,
* numbers without worrying that it may be described later on.
*/
if (likely(full)) {
- func->function.fs_descriptors = data->fs_descs;
+ func->function.fs_descriptors = vla_ptr(vlabuf, d, fs_descs);
ret = ffs_do_descs(ffs->fs_descs_count,
- data->raw_descs,
- sizeof data->raw_descs,
+ vla_ptr(vlabuf, d, raw_descs),
+ d_raw_descs__sz,
__ffs_func_bind_do_descs, func);
if (unlikely(ret < 0))
goto error;
@@ -2251,10 +2079,10 @@ static int ffs_func_bind(struct usb_configuration *c,
}
if (likely(high)) {
- func->function.hs_descriptors = data->hs_descs;
+ func->function.hs_descriptors = vla_ptr(vlabuf, d, hs_descs);
ret = ffs_do_descs(ffs->hs_descs_count,
- data->raw_descs + ret,
- (sizeof data->raw_descs) - ret,
+ vla_ptr(vlabuf, d, raw_descs) + ret,
+ d_raw_descs__sz - ret,
__ffs_func_bind_do_descs, func);
if (unlikely(ret < 0))
goto error;
@@ -2267,7 +2095,7 @@ static int ffs_func_bind(struct usb_configuration *c,
*/
ret = ffs_do_descs(ffs->fs_descs_count +
(high ? ffs->hs_descs_count : 0),
- data->raw_descs, sizeof data->raw_descs,
+ vla_ptr(vlabuf, d, raw_descs), d_raw_descs__sz,
__ffs_func_bind_do_nums, func);
if (unlikely(ret < 0))
goto error;
@@ -2281,26 +2109,19 @@ error:
return ret;
}
-
-/* Other USB function hooks *************************************************/
-
-static void ffs_func_unbind(struct usb_configuration *c,
- struct usb_function *f)
+static int ffs_func_bind(struct usb_configuration *c,
+ struct usb_function *f)
{
- struct ffs_function *func = ffs_func_from_usb(f);
- struct ffs_data *ffs = func->ffs;
+ struct f_fs_opts *ffs_opts = ffs_do_functionfs_bind(f, c);
- ENTER();
+ if (IS_ERR(ffs_opts))
+ return PTR_ERR(ffs_opts);
- if (ffs->func == func) {
- ffs_func_eps_disable(func);
- ffs->func = NULL;
- }
+ return _ffs_func_bind(c, f);
+}
- ffs_event_add(ffs, FUNCTIONFS_UNBIND);
- ffs_func_free(func);
-}
+/* Other USB function hooks *************************************************/
static int ffs_func_set_alt(struct usb_function *f,
unsigned interface, unsigned alt)
@@ -2428,6 +2249,411 @@ static int ffs_func_revmap_intf(struct ffs_function *func, u8 intf)
}
+/* Devices management *******************************************************/
+
+static LIST_HEAD(ffs_devices);
+
+static struct ffs_dev *_ffs_find_dev(const char *name)
+{
+ struct ffs_dev *dev;
+
+ list_for_each_entry(dev, &ffs_devices, entry) {
+ if (!dev->name || !name)
+ continue;
+ if (strcmp(dev->name, name) == 0)
+ return dev;
+ }
+
+ return NULL;
+}
+
+/*
+ * ffs_lock must be taken by the caller of this function
+ */
+static struct ffs_dev *ffs_get_single_dev(void)
+{
+ struct ffs_dev *dev;
+
+ if (list_is_singular(&ffs_devices)) {
+ dev = list_first_entry(&ffs_devices, struct ffs_dev, entry);
+ if (dev->single)
+ return dev;
+ }
+
+ return NULL;
+}
+
+/*
+ * ffs_lock must be taken by the caller of this function
+ */
+static struct ffs_dev *ffs_find_dev(const char *name)
+{
+ struct ffs_dev *dev;
+
+ dev = ffs_get_single_dev();
+ if (dev)
+ return dev;
+
+ return _ffs_find_dev(name);
+}
+
+/* Configfs support *********************************************************/
+
+static inline struct f_fs_opts *to_ffs_opts(struct config_item *item)
+{
+ return container_of(to_config_group(item), struct f_fs_opts,
+ func_inst.group);
+}
+
+static void ffs_attr_release(struct config_item *item)
+{
+ struct f_fs_opts *opts = to_ffs_opts(item);
+
+ usb_put_function_instance(&opts->func_inst);
+}
+
+static struct configfs_item_operations ffs_item_ops = {
+ .release = ffs_attr_release,
+};
+
+static struct config_item_type ffs_func_type = {
+ .ct_item_ops = &ffs_item_ops,
+ .ct_owner = THIS_MODULE,
+};
+
+
+/* Function registration interface ******************************************/
+
+static void ffs_free_inst(struct usb_function_instance *f)
+{
+ struct f_fs_opts *opts;
+
+ opts = to_f_fs_opts(f);
+ ffs_dev_lock();
+ ffs_free_dev(opts->dev);
+ ffs_dev_unlock();
+ kfree(opts);
+}
+
+#define MAX_INST_NAME_LEN 40
+
+static int ffs_set_inst_name(struct usb_function_instance *fi, const char *name)
+{
+ struct f_fs_opts *opts;
+ char *ptr;
+ const char *tmp;
+ int name_len, ret;
+
+ name_len = strlen(name) + 1;
+ if (name_len > MAX_INST_NAME_LEN)
+ return -ENAMETOOLONG;
+
+ ptr = kstrndup(name, name_len, GFP_KERNEL);
+ if (!ptr)
+ return -ENOMEM;
+
+ opts = to_f_fs_opts(fi);
+ tmp = NULL;
+
+ ffs_dev_lock();
+
+ tmp = opts->dev->name_allocated ? opts->dev->name : NULL;
+ ret = _ffs_name_dev(opts->dev, ptr);
+ if (ret) {
+ kfree(ptr);
+ ffs_dev_unlock();
+ return ret;
+ }
+ opts->dev->name_allocated = true;
+
+ ffs_dev_unlock();
+
+ kfree(tmp);
+
+ return 0;
+}
+
+static struct usb_function_instance *ffs_alloc_inst(void)
+{
+ struct f_fs_opts *opts;
+ struct ffs_dev *dev;
+
+ opts = kzalloc(sizeof(*opts), GFP_KERNEL);
+ if (!opts)
+ return ERR_PTR(-ENOMEM);
+
+ opts->func_inst.set_inst_name = ffs_set_inst_name;
+ opts->func_inst.free_func_inst = ffs_free_inst;
+ ffs_dev_lock();
+ dev = ffs_alloc_dev();
+ ffs_dev_unlock();
+ if (IS_ERR(dev)) {
+ kfree(opts);
+ return ERR_CAST(dev);
+ }
+ opts->dev = dev;
+ dev->opts = opts;
+
+ config_group_init_type_name(&opts->func_inst.group, "",
+ &ffs_func_type);
+ return &opts->func_inst;
+}
+
+static void ffs_free(struct usb_function *f)
+{
+ kfree(ffs_func_from_usb(f));
+}
+
+static void ffs_func_unbind(struct usb_configuration *c,
+ struct usb_function *f)
+{
+ struct ffs_function *func = ffs_func_from_usb(f);
+ struct ffs_data *ffs = func->ffs;
+ struct f_fs_opts *opts =
+ container_of(f->fi, struct f_fs_opts, func_inst);
+ struct ffs_ep *ep = func->eps;
+ unsigned count = ffs->eps_count;
+ unsigned long flags;
+
+ ENTER();
+ if (ffs->func == func) {
+ ffs_func_eps_disable(func);
+ ffs->func = NULL;
+ }
+
+ if (!--opts->refcnt)
+ functionfs_unbind(ffs);
+
+ /* cleanup after autoconfig */
+ spin_lock_irqsave(&func->ffs->eps_lock, flags);
+ do {
+ if (ep->ep && ep->req)
+ usb_ep_free_request(ep->ep, ep->req);
+ ep->req = NULL;
+ ++ep;
+ } while (--count);
+ spin_unlock_irqrestore(&func->ffs->eps_lock, flags);
+ kfree(func->eps);
+ func->eps = NULL;
+ /*
+ * eps, descriptors and interfaces_nums are allocated in the
+ * same chunk so only one free is required.
+ */
+ func->function.fs_descriptors = NULL;
+ func->function.hs_descriptors = NULL;
+ func->interfaces_nums = NULL;
+
+ ffs_event_add(ffs, FUNCTIONFS_UNBIND);
+}
+
+static struct usb_function *ffs_alloc(struct usb_function_instance *fi)
+{
+ struct ffs_function *func;
+
+ ENTER();
+
+ func = kzalloc(sizeof(*func), GFP_KERNEL);
+ if (unlikely(!func))
+ return ERR_PTR(-ENOMEM);
+
+ func->function.name = "Function FS Gadget";
+
+ func->function.bind = ffs_func_bind;
+ func->function.unbind = ffs_func_unbind;
+ func->function.set_alt = ffs_func_set_alt;
+ func->function.disable = ffs_func_disable;
+ func->function.setup = ffs_func_setup;
+ func->function.suspend = ffs_func_suspend;
+ func->function.resume = ffs_func_resume;
+ func->function.free_func = ffs_free;
+
+ return &func->function;
+}
+
+/*
+ * ffs_lock must be taken by the caller of this function
+ */
+struct ffs_dev *ffs_alloc_dev(void)
+{
+ struct ffs_dev *dev;
+ int ret;
+
+ if (ffs_get_single_dev())
+ return ERR_PTR(-EBUSY);
+
+ dev = kzalloc(sizeof(*dev), GFP_KERNEL);
+ if (!dev)
+ return ERR_PTR(-ENOMEM);
+
+ if (list_empty(&ffs_devices)) {
+ ret = functionfs_init();
+ if (ret) {
+ kfree(dev);
+ return ERR_PTR(ret);
+ }
+ }
+
+ list_add(&dev->entry, &ffs_devices);
+
+ return dev;
+}
+
+/*
+ * ffs_lock must be taken by the caller of this function
+ * The caller is responsible for "name" being available whenever f_fs needs it
+ */
+static int _ffs_name_dev(struct ffs_dev *dev, const char *name)
+{
+ struct ffs_dev *existing;
+
+ existing = _ffs_find_dev(name);
+ if (existing)
+ return -EBUSY;
+
+ dev->name = name;
+
+ return 0;
+}
+
+/*
+ * The caller is responsible for "name" being available whenever f_fs needs it
+ */
+int ffs_name_dev(struct ffs_dev *dev, const char *name)
+{
+ int ret;
+
+ ffs_dev_lock();
+ ret = _ffs_name_dev(dev, name);
+ ffs_dev_unlock();
+
+ return ret;
+}
+EXPORT_SYMBOL(ffs_name_dev);
+
+int ffs_single_dev(struct ffs_dev *dev)
+{
+ int ret;
+
+ ret = 0;
+ ffs_dev_lock();
+
+ if (!list_is_singular(&ffs_devices))
+ ret = -EBUSY;
+ else
+ dev->single = true;
+
+ ffs_dev_unlock();
+ return ret;
+}
+EXPORT_SYMBOL(ffs_single_dev);
+
+/*
+ * ffs_lock must be taken by the caller of this function
+ */
+void ffs_free_dev(struct ffs_dev *dev)
+{
+ list_del(&dev->entry);
+ if (dev->name_allocated)
+ kfree(dev->name);
+ kfree(dev);
+ if (list_empty(&ffs_devices))
+ functionfs_cleanup();
+}
+
+static void *ffs_acquire_dev(const char *dev_name)
+{
+ struct ffs_dev *ffs_dev;
+
+ ENTER();
+ ffs_dev_lock();
+
+ ffs_dev = ffs_find_dev(dev_name);
+ if (!ffs_dev)
+ ffs_dev = ERR_PTR(-ENODEV);
+ 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);
+ else
+ ffs_dev->mounted = true;
+
+ ffs_dev_unlock();
+ return ffs_dev;
+}
+
+static void ffs_release_dev(struct ffs_data *ffs_data)
+{
+ struct ffs_dev *ffs_dev;
+
+ ENTER();
+ ffs_dev_lock();
+
+ ffs_dev = ffs_data->private_data;
+ if (ffs_dev)
+ ffs_dev->mounted = false;
+
+ if (ffs_dev->ffs_release_dev_callback)
+ ffs_dev->ffs_release_dev_callback(ffs_dev);
+
+ ffs_dev_unlock();
+}
+
+static int ffs_ready(struct ffs_data *ffs)
+{
+ struct ffs_dev *ffs_obj;
+ int ret = 0;
+
+ ENTER();
+ ffs_dev_lock();
+
+ ffs_obj = ffs->private_data;
+ if (!ffs_obj) {
+ ret = -EINVAL;
+ goto done;
+ }
+ if (WARN_ON(ffs_obj->desc_ready)) {
+ ret = -EBUSY;
+ goto done;
+ }
+
+ ffs_obj->desc_ready = true;
+ ffs_obj->ffs_data = ffs;
+
+ if (ffs_obj->ffs_ready_callback)
+ ret = ffs_obj->ffs_ready_callback(ffs);
+
+done:
+ ffs_dev_unlock();
+ return ret;
+}
+
+static void ffs_closed(struct ffs_data *ffs)
+{
+ struct ffs_dev *ffs_obj;
+
+ ENTER();
+ ffs_dev_lock();
+
+ ffs_obj = ffs->private_data;
+ if (!ffs_obj)
+ goto done;
+
+ ffs_obj->desc_ready = false;
+
+ if (ffs_obj->ffs_closed_callback)
+ ffs_obj->ffs_closed_callback(ffs);
+
+ if (!ffs_obj->opts || ffs_obj->opts->no_configfs
+ || !ffs_obj->opts->func_inst.group.cg_item.ci_parent)
+ goto done;
+
+ unregister_gadget_item(ffs_obj->opts->
+ func_inst.group.cg_item.ci_parent->ci_parent);
+done:
+ ffs_dev_unlock();
+}
+
/* Misc helper functions ****************************************************/
static int ffs_mutex_lock(struct mutex *mutex, unsigned nonblock)
@@ -2458,3 +2684,7 @@ static char *ffs_prepare_buffer(const char __user *buf, size_t len)
return data;
}
+
+DECLARE_USB_FUNCTION_INIT(ffs, ffs_alloc_inst, ffs_alloc);
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Michal Nazarewicz");
diff --git a/drivers/usb/gadget/f_hid.c b/drivers/usb/gadget/f_hid.c
index 6e69a8e8d22a..a95290a1289f 100644
--- a/drivers/usb/gadget/f_hid.c
+++ b/drivers/usb/gadget/f_hid.c
@@ -20,6 +20,8 @@
#include <linux/sched.h>
#include <linux/usb/g_hid.h>
+#include "u_f.h"
+
static int major, minors;
static struct class *hidg_class;
@@ -334,20 +336,10 @@ static int f_hidg_open(struct inode *inode, struct file *fd)
/*-------------------------------------------------------------------------*/
/* usb_function */
-static struct usb_request *hidg_alloc_ep_req(struct usb_ep *ep, unsigned length)
+static inline struct usb_request *hidg_alloc_ep_req(struct usb_ep *ep,
+ unsigned length)
{
- struct usb_request *req;
-
- req = usb_ep_alloc_request(ep, GFP_ATOMIC);
- if (req) {
- req->length = length;
- req->buf = kmalloc(length, GFP_ATOMIC);
- if (!req->buf) {
- usb_ep_free_request(ep, req);
- req = NULL;
- }
- }
- return req;
+ return alloc_ep_req(ep, length, length);
}
static void hidg_set_report_complete(struct usb_ep *ep, struct usb_request *req)
diff --git a/drivers/usb/gadget/f_loopback.c b/drivers/usb/gadget/f_loopback.c
index 4a3873a0f2d0..4557cd03f0b1 100644
--- a/drivers/usb/gadget/f_loopback.c
+++ b/drivers/usb/gadget/f_loopback.c
@@ -20,6 +20,7 @@
#include <linux/usb/composite.h>
#include "g_zero.h"
+#include "u_f.h"
/*
* LOOPBACK FUNCTION ... a testing vehicle for USB peripherals,
@@ -119,7 +120,7 @@ static struct usb_endpoint_descriptor ss_loop_source_desc = {
.wMaxPacketSize = cpu_to_le16(1024),
};
-struct usb_ss_ep_comp_descriptor ss_loop_source_comp_desc = {
+static struct usb_ss_ep_comp_descriptor ss_loop_source_comp_desc = {
.bLength = USB_DT_SS_EP_COMP_SIZE,
.bDescriptorType = USB_DT_SS_ENDPOINT_COMP,
.bMaxBurst = 0,
@@ -135,7 +136,7 @@ static struct usb_endpoint_descriptor ss_loop_sink_desc = {
.wMaxPacketSize = cpu_to_le16(1024),
};
-struct usb_ss_ep_comp_descriptor ss_loop_sink_comp_desc = {
+static struct usb_ss_ep_comp_descriptor ss_loop_sink_comp_desc = {
.bLength = USB_DT_SS_EP_COMP_SIZE,
.bDescriptorType = USB_DT_SS_ENDPOINT_COMP,
.bMaxBurst = 0,
@@ -230,6 +231,14 @@ autoconf_fail:
static void lb_free_func(struct usb_function *f)
{
+ struct f_lb_opts *opts;
+
+ opts = container_of(f->fi, struct f_lb_opts, func_inst);
+
+ mutex_lock(&opts->lock);
+ opts->refcnt--;
+ mutex_unlock(&opts->lock);
+
usb_free_all_descriptors(f);
kfree(func_to_loop(f));
}
@@ -293,6 +302,11 @@ static void disable_loopback(struct f_loopback *loop)
VDBG(cdev, "%s disabled\n", loop->function.name);
}
+static inline struct usb_request *lb_alloc_ep_req(struct usb_ep *ep, int len)
+{
+ return alloc_ep_req(ep, len, buflen);
+}
+
static int
enable_loopback(struct usb_composite_dev *cdev, struct f_loopback *loop)
{
@@ -332,7 +346,7 @@ fail0:
* than 'buflen' bytes each.
*/
for (i = 0; i < qlen && result == 0; i++) {
- req = alloc_ep_req(ep, 0);
+ req = lb_alloc_ep_req(ep, 0);
if (req) {
req->complete = loopback_complete;
result = usb_ep_queue(ep, req, GFP_ATOMIC);
@@ -380,6 +394,11 @@ static struct usb_function *loopback_alloc(struct usb_function_instance *fi)
return ERR_PTR(-ENOMEM);
lb_opts = container_of(fi, struct f_lb_opts, func_inst);
+
+ mutex_lock(&lb_opts->lock);
+ lb_opts->refcnt++;
+ mutex_unlock(&lb_opts->lock);
+
buflen = lb_opts->bulk_buflen;
qlen = lb_opts->qlen;
if (!qlen)
@@ -396,6 +415,118 @@ static struct usb_function *loopback_alloc(struct usb_function_instance *fi)
return &loop->function;
}
+static inline struct f_lb_opts *to_f_lb_opts(struct config_item *item)
+{
+ return container_of(to_config_group(item), struct f_lb_opts,
+ func_inst.group);
+}
+
+CONFIGFS_ATTR_STRUCT(f_lb_opts);
+CONFIGFS_ATTR_OPS(f_lb_opts);
+
+static void lb_attr_release(struct config_item *item)
+{
+ struct f_lb_opts *lb_opts = to_f_lb_opts(item);
+
+ usb_put_function_instance(&lb_opts->func_inst);
+}
+
+static struct configfs_item_operations lb_item_ops = {
+ .release = lb_attr_release,
+ .show_attribute = f_lb_opts_attr_show,
+ .store_attribute = f_lb_opts_attr_store,
+};
+
+static ssize_t f_lb_opts_qlen_show(struct f_lb_opts *opts, char *page)
+{
+ int result;
+
+ mutex_lock(&opts->lock);
+ result = sprintf(page, "%d", opts->qlen);
+ mutex_unlock(&opts->lock);
+
+ return result;
+}
+
+static ssize_t f_lb_opts_qlen_store(struct f_lb_opts *opts,
+ const char *page, size_t len)
+{
+ int ret;
+ u32 num;
+
+ mutex_lock(&opts->lock);
+ if (opts->refcnt) {
+ ret = -EBUSY;
+ goto end;
+ }
+
+ ret = kstrtou32(page, 0, &num);
+ if (ret)
+ goto end;
+
+ opts->qlen = num;
+ ret = len;
+end:
+ mutex_unlock(&opts->lock);
+ return ret;
+}
+
+static struct f_lb_opts_attribute f_lb_opts_qlen =
+ __CONFIGFS_ATTR(qlen, S_IRUGO | S_IWUSR,
+ f_lb_opts_qlen_show,
+ f_lb_opts_qlen_store);
+
+static ssize_t f_lb_opts_bulk_buflen_show(struct f_lb_opts *opts, char *page)
+{
+ int result;
+
+ mutex_lock(&opts->lock);
+ result = sprintf(page, "%d", opts->bulk_buflen);
+ mutex_unlock(&opts->lock);
+
+ return result;
+}
+
+static ssize_t f_lb_opts_bulk_buflen_store(struct f_lb_opts *opts,
+ const char *page, size_t len)
+{
+ int ret;
+ u32 num;
+
+ mutex_lock(&opts->lock);
+ if (opts->refcnt) {
+ ret = -EBUSY;
+ goto end;
+ }
+
+ ret = kstrtou32(page, 0, &num);
+ if (ret)
+ goto end;
+
+ opts->bulk_buflen = num;
+ ret = len;
+end:
+ mutex_unlock(&opts->lock);
+ return ret;
+}
+
+static struct f_lb_opts_attribute f_lb_opts_bulk_buflen =
+ __CONFIGFS_ATTR(buflen, S_IRUGO | S_IWUSR,
+ f_lb_opts_bulk_buflen_show,
+ f_lb_opts_bulk_buflen_store);
+
+static struct configfs_attribute *lb_attrs[] = {
+ &f_lb_opts_qlen.attr,
+ &f_lb_opts_bulk_buflen.attr,
+ NULL,
+};
+
+static struct config_item_type lb_func_type = {
+ .ct_item_ops = &lb_item_ops,
+ .ct_attrs = lb_attrs,
+ .ct_owner = THIS_MODULE,
+};
+
static void lb_free_instance(struct usb_function_instance *fi)
{
struct f_lb_opts *lb_opts;
@@ -411,7 +542,14 @@ static struct usb_function_instance *loopback_alloc_instance(void)
lb_opts = kzalloc(sizeof(*lb_opts), GFP_KERNEL);
if (!lb_opts)
return ERR_PTR(-ENOMEM);
+ mutex_init(&lb_opts->lock);
lb_opts->func_inst.free_func_inst = lb_free_instance;
+ lb_opts->bulk_buflen = GZERO_BULK_BUFLEN;
+ lb_opts->qlen = GZERO_QLEN;
+
+ config_group_init_type_name(&lb_opts->func_inst.group, "",
+ &lb_func_type);
+
return &lb_opts->func_inst;
}
DECLARE_USB_FUNCTION(Loopback, loopback_alloc_instance, loopback_alloc);
diff --git a/drivers/usb/gadget/f_midi.c b/drivers/usb/gadget/f_midi.c
index 263e721c2694..36d4bb23087f 100644
--- a/drivers/usb/gadget/f_midi.c
+++ b/drivers/usb/gadget/f_midi.c
@@ -32,6 +32,8 @@
#include <linux/usb/audio.h>
#include <linux/usb/midi.h>
+#include "u_f.h"
+
MODULE_AUTHOR("Ben Williamson");
MODULE_LICENSE("GPL v2");
@@ -191,20 +193,10 @@ static struct usb_gadget_strings *midi_strings[] = {
NULL,
};
-static struct usb_request *alloc_ep_req(struct usb_ep *ep, unsigned length)
+static inline struct usb_request *midi_alloc_ep_req(struct usb_ep *ep,
+ unsigned length)
{
- struct usb_request *req;
-
- req = usb_ep_alloc_request(ep, GFP_ATOMIC);
- if (req) {
- req->length = length;
- req->buf = kmalloc(length, GFP_ATOMIC);
- if (!req->buf) {
- usb_ep_free_request(ep, req);
- req = NULL;
- }
- }
- return req;
+ return alloc_ep_req(ep, length, length);
}
static void free_ep_req(struct usb_ep *ep, struct usb_request *req)
@@ -365,7 +357,7 @@ static int f_midi_set_alt(struct usb_function *f, unsigned intf, unsigned alt)
/* allocate a bunch of read buffers and queue them all at once. */
for (i = 0; i < midi->qlen && err == 0; i++) {
struct usb_request *req =
- alloc_ep_req(midi->out_ep, midi->buflen);
+ midi_alloc_ep_req(midi->out_ep, midi->buflen);
if (req == NULL)
return -ENOMEM;
@@ -546,7 +538,7 @@ static void f_midi_transmit(struct f_midi *midi, struct usb_request *req)
return;
if (!req)
- req = alloc_ep_req(ep, midi->buflen);
+ req = midi_alloc_ep_req(ep, midi->buflen);
if (!req) {
ERROR(midi, "gmidi_transmit: alloc_ep_request failed\n");
diff --git a/drivers/usb/gadget/f_ncm.c b/drivers/usb/gadget/f_ncm.c
index 1c28fe13328a..a9499fd30792 100644
--- a/drivers/usb/gadget/f_ncm.c
+++ b/drivers/usb/gadget/f_ncm.c
@@ -1386,7 +1386,7 @@ static void ncm_unbind(struct usb_configuration *c, struct usb_function *f)
usb_ep_free_request(ncm->notify, ncm->notify_req);
}
-struct usb_function *ncm_alloc(struct usb_function_instance *fi)
+static struct usb_function *ncm_alloc(struct usb_function_instance *fi)
{
struct f_ncm *ncm;
struct f_ncm_opts *opts;
diff --git a/drivers/usb/gadget/f_obex.c b/drivers/usb/gadget/f_obex.c
index ad39f1dacba3..aebae1853bce 100644
--- a/drivers/usb/gadget/f_obex.c
+++ b/drivers/usb/gadget/f_obex.c
@@ -499,7 +499,7 @@ static void obex_unbind(struct usb_configuration *c, struct usb_function *f)
usb_free_all_descriptors(f);
}
-struct usb_function *obex_alloc(struct usb_function_instance *fi)
+static struct usb_function *obex_alloc(struct usb_function_instance *fi)
{
struct f_obex *obex;
struct f_serial_opts *opts;
diff --git a/drivers/usb/gadget/f_phonet.c b/drivers/usb/gadget/f_phonet.c
index eb3aa817a662..f2b781773eed 100644
--- a/drivers/usb/gadget/f_phonet.c
+++ b/drivers/usb/gadget/f_phonet.c
@@ -689,7 +689,7 @@ static void pn_unbind(struct usb_configuration *c, struct usb_function *f)
usb_free_all_descriptors(f);
}
-struct usb_function *phonet_alloc(struct usb_function_instance *fi)
+static struct usb_function *phonet_alloc(struct usb_function_instance *fi)
{
struct f_phonet *fp;
struct f_phonet_opts *opts;
diff --git a/drivers/usb/gadget/f_rndis.c b/drivers/usb/gadget/f_rndis.c
index 717ed7f95639..c11761ce5113 100644
--- a/drivers/usb/gadget/f_rndis.c
+++ b/drivers/usb/gadget/f_rndis.c
@@ -675,7 +675,6 @@ rndis_bind(struct usb_configuration *c, struct usb_function *f)
int status;
struct usb_ep *ep;
-#ifndef USB_FRNDIS_INCLUDED
struct f_rndis_opts *rndis_opts;
if (!can_support_rndis(c))
@@ -697,7 +696,7 @@ rndis_bind(struct usb_configuration *c, struct usb_function *f)
return status;
rndis_opts->bound = true;
}
-#endif
+
us = usb_gstrings_attach(cdev, rndis_strings,
ARRAY_SIZE(rndis_string_defs));
if (IS_ERR(us))
@@ -782,13 +781,6 @@ rndis_bind(struct usb_configuration *c, struct usb_function *f)
rndis->port.open = rndis_open;
rndis->port.close = rndis_close;
-#ifdef USB_FRNDIS_INCLUDED
- status = rndis_register(rndis_response_available, rndis);
- if (status < 0)
- goto fail;
- rndis->config = status;
-#endif
-
rndis_set_param_medium(rndis->config, RNDIS_MEDIUM_802_3, 0);
rndis_set_host_mac(rndis->config, rndis->ethaddr);
@@ -830,66 +822,6 @@ fail:
return status;
}
-#ifdef USB_FRNDIS_INCLUDED
-
-static void
-rndis_old_unbind(struct usb_configuration *c, struct usb_function *f)
-{
- struct f_rndis *rndis = func_to_rndis(f);
-
- rndis_deregister(rndis->config);
-
- usb_free_all_descriptors(f);
-
- kfree(rndis->notify_req->buf);
- usb_ep_free_request(rndis->notify, rndis->notify_req);
-
- kfree(rndis);
-}
-
-int
-rndis_bind_config_vendor(struct usb_configuration *c, u8 ethaddr[ETH_ALEN],
- u32 vendorID, const char *manufacturer, struct eth_dev *dev)
-{
- struct f_rndis *rndis;
- int status;
-
- /* allocate and initialize one new instance */
- status = -ENOMEM;
- rndis = kzalloc(sizeof *rndis, GFP_KERNEL);
- if (!rndis)
- goto fail;
-
- memcpy(rndis->ethaddr, ethaddr, ETH_ALEN);
- rndis->vendorID = vendorID;
- rndis->manufacturer = manufacturer;
-
- rndis->port.ioport = dev;
- /* RNDIS activates when the host changes this filter */
- rndis->port.cdc_filter = 0;
-
- /* RNDIS has special (and complex) framing */
- rndis->port.header_len = sizeof(struct rndis_packet_msg_type);
- rndis->port.wrap = rndis_add_header;
- rndis->port.unwrap = rndis_rm_hdr;
-
- rndis->port.func.name = "rndis";
- /* descriptors are per-instance copies */
- rndis->port.func.bind = rndis_bind;
- rndis->port.func.unbind = rndis_old_unbind;
- rndis->port.func.set_alt = rndis_set_alt;
- rndis->port.func.setup = rndis_setup;
- rndis->port.func.disable = rndis_disable;
-
- status = usb_add_function(c, &rndis->port.func);
- if (status)
- kfree(rndis);
-fail:
- return status;
-}
-
-#else
-
void rndis_borrow_net(struct usb_function_instance *f, struct net_device *net)
{
struct f_rndis_opts *opts;
@@ -1047,8 +979,26 @@ static struct usb_function *rndis_alloc(struct usb_function_instance *fi)
return &rndis->port.func;
}
-DECLARE_USB_FUNCTION_INIT(rndis, rndis_alloc_inst, rndis_alloc);
+DECLARE_USB_FUNCTION(rndis, rndis_alloc_inst, rndis_alloc);
+
+static int __init rndis_mod_init(void)
+{
+ int ret;
+
+ ret = rndis_init();
+ if (ret)
+ return ret;
+
+ return usb_function_register(&rndisusb_func);
+}
+module_init(rndis_mod_init);
+
+static void __exit rndis_mod_exit(void)
+{
+ usb_function_unregister(&rndisusb_func);
+ rndis_exit();
+}
+module_exit(rndis_mod_exit);
+
MODULE_LICENSE("GPL");
MODULE_AUTHOR("David Brownell");
-
-#endif
diff --git a/drivers/usb/gadget/f_serial.c b/drivers/usb/gadget/f_serial.c
index 981113c9924d..9ecbcbf36a45 100644
--- a/drivers/usb/gadget/f_serial.c
+++ b/drivers/usb/gadget/f_serial.c
@@ -354,7 +354,7 @@ static void gser_unbind(struct usb_configuration *c, struct usb_function *f)
usb_free_all_descriptors(f);
}
-struct usb_function *gser_alloc(struct usb_function_instance *fi)
+static struct usb_function *gser_alloc(struct usb_function_instance *fi)
{
struct f_gser *gser;
struct f_serial_opts *opts;
diff --git a/drivers/usb/gadget/f_sourcesink.c b/drivers/usb/gadget/f_sourcesink.c
index a8895859a221..d3cd52db78fe 100644
--- a/drivers/usb/gadget/f_sourcesink.c
+++ b/drivers/usb/gadget/f_sourcesink.c
@@ -21,6 +21,7 @@
#include "g_zero.h"
#include "gadget_chips.h"
+#include "u_f.h"
/*
* SOURCE/SINK FUNCTION ... a primary testing vehicle for USB peripheral
@@ -201,7 +202,7 @@ static struct usb_endpoint_descriptor ss_source_desc = {
.wMaxPacketSize = cpu_to_le16(1024),
};
-struct usb_ss_ep_comp_descriptor ss_source_comp_desc = {
+static struct usb_ss_ep_comp_descriptor ss_source_comp_desc = {
.bLength = USB_DT_SS_EP_COMP_SIZE,
.bDescriptorType = USB_DT_SS_ENDPOINT_COMP,
@@ -218,7 +219,7 @@ static struct usb_endpoint_descriptor ss_sink_desc = {
.wMaxPacketSize = cpu_to_le16(1024),
};
-struct usb_ss_ep_comp_descriptor ss_sink_comp_desc = {
+static struct usb_ss_ep_comp_descriptor ss_sink_comp_desc = {
.bLength = USB_DT_SS_EP_COMP_SIZE,
.bDescriptorType = USB_DT_SS_ENDPOINT_COMP,
@@ -236,7 +237,7 @@ static struct usb_endpoint_descriptor ss_iso_source_desc = {
.bInterval = 4,
};
-struct usb_ss_ep_comp_descriptor ss_iso_source_comp_desc = {
+static struct usb_ss_ep_comp_descriptor ss_iso_source_comp_desc = {
.bLength = USB_DT_SS_EP_COMP_SIZE,
.bDescriptorType = USB_DT_SS_ENDPOINT_COMP,
@@ -254,7 +255,7 @@ static struct usb_endpoint_descriptor ss_iso_sink_desc = {
.bInterval = 4,
};
-struct usb_ss_ep_comp_descriptor ss_iso_sink_comp_desc = {
+static struct usb_ss_ep_comp_descriptor ss_iso_sink_comp_desc = {
.bLength = USB_DT_SS_EP_COMP_SIZE,
.bDescriptorType = USB_DT_SS_ENDPOINT_COMP,
@@ -301,23 +302,9 @@ static struct usb_gadget_strings *sourcesink_strings[] = {
/*-------------------------------------------------------------------------*/
-struct usb_request *alloc_ep_req(struct usb_ep *ep, int len)
+static inline struct usb_request *ss_alloc_ep_req(struct usb_ep *ep, int len)
{
- struct usb_request *req;
-
- req = usb_ep_alloc_request(ep, GFP_ATOMIC);
- if (req) {
- if (len)
- req->length = len;
- else
- req->length = buflen;
- req->buf = kmalloc(req->length, GFP_ATOMIC);
- if (!req->buf) {
- usb_ep_free_request(ep, req);
- req = NULL;
- }
- }
- return req;
+ return alloc_ep_req(ep, len, buflen);
}
void free_ep_req(struct usb_ep *ep, struct usb_request *req)
@@ -490,6 +477,14 @@ no_iso:
static void
sourcesink_free_func(struct usb_function *f)
{
+ struct f_ss_opts *opts;
+
+ opts = container_of(f->fi, struct f_ss_opts, func_inst);
+
+ mutex_lock(&opts->lock);
+ opts->refcnt--;
+ mutex_unlock(&opts->lock);
+
usb_free_all_descriptors(f);
kfree(func_to_ss(f));
}
@@ -628,10 +623,10 @@ static int source_sink_start_ep(struct f_sourcesink *ss, bool is_in,
break;
}
ep = is_in ? ss->iso_in_ep : ss->iso_out_ep;
- req = alloc_ep_req(ep, size);
+ req = ss_alloc_ep_req(ep, size);
} else {
ep = is_in ? ss->in_ep : ss->out_ep;
- req = alloc_ep_req(ep, 0);
+ req = ss_alloc_ep_req(ep, 0);
}
if (!req)
@@ -878,6 +873,11 @@ static struct usb_function *source_sink_alloc_func(
return NULL;
ss_opts = container_of(fi, struct f_ss_opts, func_inst);
+
+ mutex_lock(&ss_opts->lock);
+ ss_opts->refcnt++;
+ mutex_unlock(&ss_opts->lock);
+
pattern = ss_opts->pattern;
isoc_interval = ss_opts->isoc_interval;
isoc_maxpacket = ss_opts->isoc_maxpacket;
@@ -898,6 +898,303 @@ static struct usb_function *source_sink_alloc_func(
return &ss->function;
}
+static inline struct f_ss_opts *to_f_ss_opts(struct config_item *item)
+{
+ return container_of(to_config_group(item), struct f_ss_opts,
+ func_inst.group);
+}
+
+CONFIGFS_ATTR_STRUCT(f_ss_opts);
+CONFIGFS_ATTR_OPS(f_ss_opts);
+
+static void ss_attr_release(struct config_item *item)
+{
+ struct f_ss_opts *ss_opts = to_f_ss_opts(item);
+
+ usb_put_function_instance(&ss_opts->func_inst);
+}
+
+static struct configfs_item_operations ss_item_ops = {
+ .release = ss_attr_release,
+ .show_attribute = f_ss_opts_attr_show,
+ .store_attribute = f_ss_opts_attr_store,
+};
+
+static ssize_t f_ss_opts_pattern_show(struct f_ss_opts *opts, char *page)
+{
+ int result;
+
+ mutex_lock(&opts->lock);
+ result = sprintf(page, "%d", opts->pattern);
+ mutex_unlock(&opts->lock);
+
+ return result;
+}
+
+static ssize_t f_ss_opts_pattern_store(struct f_ss_opts *opts,
+ const char *page, size_t len)
+{
+ int ret;
+ u8 num;
+
+ mutex_lock(&opts->lock);
+ if (opts->refcnt) {
+ ret = -EBUSY;
+ goto end;
+ }
+
+ ret = kstrtou8(page, 0, &num);
+ if (ret)
+ goto end;
+
+ if (num != 0 && num != 1 && num != 2) {
+ ret = -EINVAL;
+ goto end;
+ }
+
+ opts->pattern = num;
+ ret = len;
+end:
+ mutex_unlock(&opts->lock);
+ return ret;
+}
+
+static struct f_ss_opts_attribute f_ss_opts_pattern =
+ __CONFIGFS_ATTR(pattern, S_IRUGO | S_IWUSR,
+ f_ss_opts_pattern_show,
+ f_ss_opts_pattern_store);
+
+static ssize_t f_ss_opts_isoc_interval_show(struct f_ss_opts *opts, char *page)
+{
+ int result;
+
+ mutex_lock(&opts->lock);
+ result = sprintf(page, "%d", opts->isoc_interval);
+ mutex_unlock(&opts->lock);
+
+ return result;
+}
+
+static ssize_t f_ss_opts_isoc_interval_store(struct f_ss_opts *opts,
+ const char *page, size_t len)
+{
+ int ret;
+ u8 num;
+
+ mutex_lock(&opts->lock);
+ if (opts->refcnt) {
+ ret = -EBUSY;
+ goto end;
+ }
+
+ ret = kstrtou8(page, 0, &num);
+ if (ret)
+ goto end;
+
+ if (num > 16) {
+ ret = -EINVAL;
+ goto end;
+ }
+
+ opts->isoc_interval = num;
+ ret = len;
+end:
+ mutex_unlock(&opts->lock);
+ return ret;
+}
+
+static struct f_ss_opts_attribute f_ss_opts_isoc_interval =
+ __CONFIGFS_ATTR(isoc_interval, S_IRUGO | S_IWUSR,
+ f_ss_opts_isoc_interval_show,
+ f_ss_opts_isoc_interval_store);
+
+static ssize_t f_ss_opts_isoc_maxpacket_show(struct f_ss_opts *opts, char *page)
+{
+ int result;
+
+ mutex_lock(&opts->lock);
+ result = sprintf(page, "%d", opts->isoc_maxpacket);
+ mutex_unlock(&opts->lock);
+
+ return result;
+}
+
+static ssize_t f_ss_opts_isoc_maxpacket_store(struct f_ss_opts *opts,
+ const char *page, size_t len)
+{
+ int ret;
+ u16 num;
+
+ mutex_lock(&opts->lock);
+ if (opts->refcnt) {
+ ret = -EBUSY;
+ goto end;
+ }
+
+ ret = kstrtou16(page, 0, &num);
+ if (ret)
+ goto end;
+
+ if (num > 1024) {
+ ret = -EINVAL;
+ goto end;
+ }
+
+ opts->isoc_maxpacket = num;
+ ret = len;
+end:
+ mutex_unlock(&opts->lock);
+ return ret;
+}
+
+static struct f_ss_opts_attribute f_ss_opts_isoc_maxpacket =
+ __CONFIGFS_ATTR(isoc_maxpacket, S_IRUGO | S_IWUSR,
+ f_ss_opts_isoc_maxpacket_show,
+ f_ss_opts_isoc_maxpacket_store);
+
+static ssize_t f_ss_opts_isoc_mult_show(struct f_ss_opts *opts, char *page)
+{
+ int result;
+
+ mutex_lock(&opts->lock);
+ result = sprintf(page, "%d", opts->isoc_mult);
+ mutex_unlock(&opts->lock);
+
+ return result;
+}
+
+static ssize_t f_ss_opts_isoc_mult_store(struct f_ss_opts *opts,
+ const char *page, size_t len)
+{
+ int ret;
+ u8 num;
+
+ mutex_lock(&opts->lock);
+ if (opts->refcnt) {
+ ret = -EBUSY;
+ goto end;
+ }
+
+ ret = kstrtou8(page, 0, &num);
+ if (ret)
+ goto end;
+
+ if (num > 2) {
+ ret = -EINVAL;
+ goto end;
+ }
+
+ opts->isoc_mult = num;
+ ret = len;
+end:
+ mutex_unlock(&opts->lock);
+ return ret;
+}
+
+static struct f_ss_opts_attribute f_ss_opts_isoc_mult =
+ __CONFIGFS_ATTR(isoc_mult, S_IRUGO | S_IWUSR,
+ f_ss_opts_isoc_mult_show,
+ f_ss_opts_isoc_mult_store);
+
+static ssize_t f_ss_opts_isoc_maxburst_show(struct f_ss_opts *opts, char *page)
+{
+ int result;
+
+ mutex_lock(&opts->lock);
+ result = sprintf(page, "%d", opts->isoc_maxburst);
+ mutex_unlock(&opts->lock);
+
+ return result;
+}
+
+static ssize_t f_ss_opts_isoc_maxburst_store(struct f_ss_opts *opts,
+ const char *page, size_t len)
+{
+ int ret;
+ u8 num;
+
+ mutex_lock(&opts->lock);
+ if (opts->refcnt) {
+ ret = -EBUSY;
+ goto end;
+ }
+
+ ret = kstrtou8(page, 0, &num);
+ if (ret)
+ goto end;
+
+ if (num > 15) {
+ ret = -EINVAL;
+ goto end;
+ }
+
+ opts->isoc_maxburst = num;
+ ret = len;
+end:
+ mutex_unlock(&opts->lock);
+ return ret;
+}
+
+static struct f_ss_opts_attribute f_ss_opts_isoc_maxburst =
+ __CONFIGFS_ATTR(isoc_maxburst, S_IRUGO | S_IWUSR,
+ f_ss_opts_isoc_maxburst_show,
+ f_ss_opts_isoc_maxburst_store);
+
+static ssize_t f_ss_opts_bulk_buflen_show(struct f_ss_opts *opts, char *page)
+{
+ int result;
+
+ mutex_lock(&opts->lock);
+ result = sprintf(page, "%d", opts->bulk_buflen);
+ mutex_unlock(&opts->lock);
+
+ return result;
+}
+
+static ssize_t f_ss_opts_bulk_buflen_store(struct f_ss_opts *opts,
+ const char *page, size_t len)
+{
+ int ret;
+ u32 num;
+
+ mutex_lock(&opts->lock);
+ if (opts->refcnt) {
+ ret = -EBUSY;
+ goto end;
+ }
+
+ ret = kstrtou32(page, 0, &num);
+ if (ret)
+ goto end;
+
+ opts->bulk_buflen = num;
+ ret = len;
+end:
+ mutex_unlock(&opts->lock);
+ return ret;
+}
+
+static struct f_ss_opts_attribute f_ss_opts_bulk_buflen =
+ __CONFIGFS_ATTR(buflen, S_IRUGO | S_IWUSR,
+ f_ss_opts_bulk_buflen_show,
+ f_ss_opts_bulk_buflen_store);
+
+static struct configfs_attribute *ss_attrs[] = {
+ &f_ss_opts_pattern.attr,
+ &f_ss_opts_isoc_interval.attr,
+ &f_ss_opts_isoc_maxpacket.attr,
+ &f_ss_opts_isoc_mult.attr,
+ &f_ss_opts_isoc_maxburst.attr,
+ &f_ss_opts_bulk_buflen.attr,
+ NULL,
+};
+
+static struct config_item_type ss_func_type = {
+ .ct_item_ops = &ss_item_ops,
+ .ct_attrs = ss_attrs,
+ .ct_owner = THIS_MODULE,
+};
+
static void source_sink_free_instance(struct usb_function_instance *fi)
{
struct f_ss_opts *ss_opts;
@@ -913,7 +1210,15 @@ static struct usb_function_instance *source_sink_alloc_inst(void)
ss_opts = kzalloc(sizeof(*ss_opts), GFP_KERNEL);
if (!ss_opts)
return ERR_PTR(-ENOMEM);
+ mutex_init(&ss_opts->lock);
ss_opts->func_inst.free_func_inst = source_sink_free_instance;
+ ss_opts->isoc_interval = GZERO_ISOC_INTERVAL;
+ ss_opts->isoc_maxpacket = GZERO_ISOC_MAXPACKET;
+ ss_opts->bulk_buflen = GZERO_BULK_BUFLEN;
+
+ config_group_init_type_name(&ss_opts->func_inst.group, "",
+ &ss_func_type);
+
return &ss_opts->func_inst;
}
DECLARE_USB_FUNCTION(SourceSink, source_sink_alloc_inst,
diff --git a/drivers/usb/gadget/f_subset.c b/drivers/usb/gadget/f_subset.c
index 7c8674fa7e80..f1a59190ac9a 100644
--- a/drivers/usb/gadget/f_subset.c
+++ b/drivers/usb/gadget/f_subset.c
@@ -301,7 +301,6 @@ geth_bind(struct usb_configuration *c, struct usb_function *f)
int status;
struct usb_ep *ep;
-#ifndef USB_FSUBSET_INCLUDED
struct f_gether_opts *gether_opts;
gether_opts = container_of(f->fi, struct f_gether_opts, func_inst);
@@ -322,7 +321,7 @@ geth_bind(struct usb_configuration *c, struct usb_function *f)
return status;
gether_opts->bound = true;
}
-#endif
+
us = usb_gstrings_attach(cdev, geth_strings,
ARRAY_SIZE(geth_string_defs));
if (IS_ERR(us))
@@ -393,61 +392,6 @@ fail:
return status;
}
-#ifdef USB_FSUBSET_INCLUDED
-
-static void
-geth_old_unbind(struct usb_configuration *c, struct usb_function *f)
-{
- geth_string_defs[0].id = 0;
- usb_free_all_descriptors(f);
- kfree(func_to_geth(f));
-}
-
-/**
- * geth_bind_config - add CDC Subset network link to a configuration
- * @c: the configuration to support the network link
- * @ethaddr: a buffer in which the ethernet address of the host side
- * side of the link was recorded
- * @dev: eth_dev structure
- * Context: single threaded during gadget setup
- *
- * Returns zero on success, else negative errno.
- *
- * Caller must have called @gether_setup(). Caller is also responsible
- * for calling @gether_cleanup() before module unload.
- */
-int geth_bind_config(struct usb_configuration *c, u8 ethaddr[ETH_ALEN],
- struct eth_dev *dev)
-{
- struct f_gether *geth;
- int status;
-
- /* allocate and initialize one new instance */
- geth = kzalloc(sizeof *geth, GFP_KERNEL);
- if (!geth)
- return -ENOMEM;
-
- /* export host's Ethernet address in CDC format */
- snprintf(geth->ethaddr, sizeof geth->ethaddr, "%pm", ethaddr);
- geth_string_defs[1].s = geth->ethaddr;
-
- geth->port.ioport = dev;
- geth->port.cdc_filter = DEFAULT_FILTER;
-
- geth->port.func.name = "cdc_subset";
- geth->port.func.bind = geth_bind;
- geth->port.func.unbind = geth_old_unbind;
- geth->port.func.set_alt = geth_set_alt;
- geth->port.func.disable = geth_disable;
-
- status = usb_add_function(c, &geth->port.func);
- if (status)
- kfree(geth);
- return status;
-}
-
-#else
-
static inline struct f_gether_opts *to_f_gether_opts(struct config_item *item)
{
return container_of(to_config_group(item), struct f_gether_opts,
@@ -573,5 +517,3 @@ static struct usb_function *geth_alloc(struct usb_function_instance *fi)
DECLARE_USB_FUNCTION_INIT(geth, geth_alloc_inst, geth_alloc);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("David Brownell");
-
-#endif
diff --git a/drivers/usb/gadget/fotg210-udc.c b/drivers/usb/gadget/fotg210-udc.c
index bbbfd1948778..2d0305280e8c 100644
--- a/drivers/usb/gadget/fotg210-udc.c
+++ b/drivers/usb/gadget/fotg210-udc.c
@@ -1157,8 +1157,9 @@ static int fotg210_udc_probe(struct platform_device *pdev)
INIT_LIST_HEAD(&ep->queue);
ep->ep.name = fotg210_ep_name[i];
ep->ep.ops = &fotg210_ep_ops;
+ usb_ep_set_maxpacket_limit(&ep->ep, (unsigned short) ~0);
}
- fotg210->ep[0]->ep.maxpacket = 0x40;
+ usb_ep_set_maxpacket_limit(&fotg210->ep[0]->ep, 0x40);
fotg210->gadget.ep0 = &fotg210->ep[0]->ep;
INIT_LIST_HEAD(&fotg210->gadget.ep0->ep_list);
diff --git a/drivers/usb/gadget/fsl_qe_udc.c b/drivers/usb/gadget/fsl_qe_udc.c
index 807127d56fa3..f60d4da8f2c0 100644
--- a/drivers/usb/gadget/fsl_qe_udc.c
+++ b/drivers/usb/gadget/fsl_qe_udc.c
@@ -2429,7 +2429,7 @@ static int qe_ep_config(struct qe_udc *udc, unsigned char pipe_num)
ep->ep.ops = &qe_ep_ops;
ep->stopped = 1;
- ep->ep.maxpacket = (unsigned short) ~0;
+ usb_ep_set_maxpacket_limit(&ep->ep, (unsigned short) ~0);
ep->ep.desc = NULL;
ep->dir = 0xff;
ep->epnum = (u8)pipe_num;
@@ -2717,7 +2717,7 @@ MODULE_DEVICE_TABLE(of, qe_udc_match);
static struct platform_driver udc_driver = {
.driver = {
- .name = (char *)driver_name,
+ .name = driver_name,
.owner = THIS_MODULE,
.of_match_table = qe_udc_match,
},
diff --git a/drivers/usb/gadget/fsl_udc_core.c b/drivers/usb/gadget/fsl_udc_core.c
index 36ac7cfba91d..15960af0f67e 100644
--- a/drivers/usb/gadget/fsl_udc_core.c
+++ b/drivers/usb/gadget/fsl_udc_core.c
@@ -2311,7 +2311,7 @@ static int __init struct_ep_setup(struct fsl_udc *udc, unsigned char index,
/* for ep0: maxP defined in desc
* for other eps, maxP is set by epautoconfig() called by gadget layer
*/
- ep->ep.maxpacket = (unsigned short) ~0;
+ usb_ep_set_maxpacket_limit(&ep->ep, (unsigned short) ~0);
/* the queue lists any req for this ep */
INIT_LIST_HEAD(&ep->queue);
@@ -2469,7 +2469,8 @@ static int __init fsl_udc_probe(struct platform_device *pdev)
* for other eps, gadget layer called ep_enable with defined desc
*/
udc_controller->eps[0].ep.desc = &fsl_ep0_desc;
- udc_controller->eps[0].ep.maxpacket = USB_MAX_CTRL_PAYLOAD;
+ usb_ep_set_maxpacket_limit(&udc_controller->eps[0].ep,
+ USB_MAX_CTRL_PAYLOAD);
/* setup the udc->eps[] for non-control endpoints and link
* to gadget.ep_list */
@@ -2666,7 +2667,7 @@ static struct platform_driver udc_driver = {
.suspend = fsl_udc_suspend,
.resume = fsl_udc_resume,
.driver = {
- .name = (char *)driver_name,
+ .name = driver_name,
.owner = THIS_MODULE,
/* udc suspend/resume called from OTG driver */
.suspend = fsl_udc_otg_suspend,
diff --git a/drivers/usb/gadget/fusb300_udc.c b/drivers/usb/gadget/fusb300_udc.c
index b278abe52453..6423f1840ed9 100644
--- a/drivers/usb/gadget/fusb300_udc.c
+++ b/drivers/usb/gadget/fusb300_udc.c
@@ -1452,9 +1452,9 @@ static int __init fusb300_probe(struct platform_device *pdev)
INIT_LIST_HEAD(&ep->queue);
ep->ep.name = fusb300_ep_name[i];
ep->ep.ops = &fusb300_ep_ops;
- ep->ep.maxpacket = HS_BULK_MAX_PACKET_SIZE;
+ usb_ep_set_maxpacket_limit(&ep->ep, HS_BULK_MAX_PACKET_SIZE);
}
- fusb300->ep[0]->ep.maxpacket = HS_CTL_MAX_PACKET_SIZE;
+ usb_ep_set_maxpacket_limit(&fusb300->ep[0]->ep, HS_CTL_MAX_PACKET_SIZE);
fusb300->ep[0]->epnum = 0;
fusb300->gadget.ep0 = &fusb300->ep[0]->ep;
INIT_LIST_HEAD(&fusb300->gadget.ep0->ep_list);
diff --git a/drivers/usb/gadget/g_ffs.c b/drivers/usb/gadget/g_ffs.c
index 2344efe4f4ce..fe12e6a27448 100644
--- a/drivers/usb/gadget/g_ffs.c
+++ b/drivers/usb/gadget/g_ffs.c
@@ -13,14 +13,10 @@
#define pr_fmt(fmt) "g_ffs: " fmt
#include <linux/module.h>
-/*
- * kbuild is not very cooperative with respect to linking separately
- * compiled library objects into one module. So for now we won't use
- * separate compilation ... ensuring init/exit sections work to shrink
- * the runtime footprint, and giving us at least some parts of what
- * a "gcc --combine ... part1.c part2.c part3.c ... " build would.
- */
+
#if defined CONFIG_USB_FUNCTIONFS_ETH || defined CONFIG_USB_FUNCTIONFS_RNDIS
+#include <linux/netdevice.h>
+
# if defined USB_ETH_RNDIS
# undef USB_ETH_RNDIS
# endif
@@ -28,31 +24,31 @@
# define USB_ETH_RNDIS y
# endif
-#define USBF_ECM_INCLUDED
-# include "f_ecm.c"
-#define USB_FSUBSET_INCLUDED
-# include "f_subset.c"
+# include "u_ecm.h"
+# include "u_gether.h"
# ifdef USB_ETH_RNDIS
-# define USB_FRNDIS_INCLUDED
-# include "f_rndis.c"
+# include "u_rndis.h"
# include "rndis.h"
# endif
# include "u_ether.h"
-static u8 gfs_host_mac[ETH_ALEN];
-static struct eth_dev *the_dev;
+USB_ETHERNET_MODULE_PARAMETERS();
+
# ifdef CONFIG_USB_FUNCTIONFS_ETH
-static int eth_bind_config(struct usb_configuration *c, u8 ethaddr[ETH_ALEN],
- struct eth_dev *dev);
+static int eth_bind_config(struct usb_configuration *c);
+static struct usb_function_instance *fi_ecm;
+static struct usb_function *f_ecm;
+static struct usb_function_instance *fi_geth;
+static struct usb_function *f_geth;
+# endif
+# ifdef CONFIG_USB_FUNCTIONFS_RNDIS
+static int bind_rndis_config(struct usb_configuration *c);
+static struct usb_function_instance *fi_rndis;
+static struct usb_function *f_rndis;
# endif
-#else
-# define the_dev NULL
-# define gether_cleanup(dev) do { } while (0)
-# define gfs_host_mac NULL
-struct eth_dev;
#endif
-#include "f_fs.c"
+#include "u_fs.h"
#define DRIVER_NAME "g_ffs"
#define DRIVER_DESC "USB Function Filesystem"
@@ -67,19 +63,8 @@ MODULE_LICENSE("GPL");
#define GFS_MAX_DEVS 10
-struct gfs_ffs_obj {
- const char *name;
- bool mounted;
- bool desc_ready;
- struct ffs_data *ffs_data;
-};
-
USB_GADGET_COMPOSITE_OPTIONS();
-#if defined CONFIG_USB_FUNCTIONFS_ETH || defined CONFIG_USB_FUNCTIONFS_RNDIS
-USB_ETHERNET_MODULE_PARAMETERS();
-#endif
-
static struct usb_device_descriptor gfs_dev_desc = {
.bLength = sizeof gfs_dev_desc,
.bDescriptorType = USB_DT_DEVICE,
@@ -146,12 +131,12 @@ static struct usb_gadget_strings *gfs_dev_strings[] = {
struct gfs_configuration {
struct usb_configuration c;
- int (*eth)(struct usb_configuration *c, u8 *ethaddr,
- struct eth_dev *dev);
+ int (*eth)(struct usb_configuration *c);
+ int num;
} gfs_configurations[] = {
#ifdef CONFIG_USB_FUNCTIONFS_RNDIS
{
- .eth = rndis_bind_config,
+ .eth = bind_rndis_config,
},
#endif
@@ -167,10 +152,15 @@ struct gfs_configuration {
#endif
};
+static void *functionfs_acquire_dev(struct ffs_dev *dev);
+static void functionfs_release_dev(struct ffs_dev *dev);
+static int functionfs_ready_callback(struct ffs_data *ffs);
+static void functionfs_closed_callback(struct ffs_data *ffs);
static int gfs_bind(struct usb_composite_dev *cdev);
static int gfs_unbind(struct usb_composite_dev *cdev);
static int gfs_do_config(struct usb_configuration *c);
+
static __refdata struct usb_composite_driver gfs_driver = {
.name = DRIVER_NAME,
.dev = &gfs_dev_desc,
@@ -180,206 +170,244 @@ static __refdata struct usb_composite_driver gfs_driver = {
.unbind = gfs_unbind,
};
-static DEFINE_MUTEX(gfs_lock);
static unsigned int missing_funcs;
-static bool gfs_ether_setup;
static bool gfs_registered;
static bool gfs_single_func;
-static struct gfs_ffs_obj *ffs_tab;
+static struct usb_function_instance **fi_ffs;
+static struct usb_function **f_ffs[] = {
+#ifdef CONFIG_USB_FUNCTIONFS_RNDIS
+ NULL,
+#endif
+
+#ifdef CONFIG_USB_FUNCTIONFS_ETH
+ NULL,
+#endif
+
+#ifdef CONFIG_USB_FUNCTIONFS_GENERIC
+ NULL,
+#endif
+};
+
+#define N_CONF ARRAY_SIZE(f_ffs)
static int __init gfs_init(void)
{
+ struct f_fs_opts *opts;
int i;
+ int ret = 0;
ENTER();
- if (!func_num) {
+ if (func_num < 2) {
gfs_single_func = true;
func_num = 1;
}
- ffs_tab = kcalloc(func_num, sizeof *ffs_tab, GFP_KERNEL);
- if (!ffs_tab)
- return -ENOMEM;
+ /*
+ * Allocate in one chunk for easier maintenance
+ */
+ f_ffs[0] = kcalloc(func_num * N_CONF, sizeof(*f_ffs), GFP_KERNEL);
+ if (!f_ffs[0]) {
+ ret = -ENOMEM;
+ goto no_func;
+ }
+ for (i = 1; i < N_CONF; ++i)
+ f_ffs[i] = f_ffs[0] + i * func_num;
- if (!gfs_single_func)
- for (i = 0; i < func_num; i++)
- ffs_tab[i].name = func_names[i];
+ fi_ffs = kcalloc(func_num, sizeof(*fi_ffs), GFP_KERNEL);
+ if (!fi_ffs) {
+ ret = -ENOMEM;
+ goto no_func;
+ }
+
+ for (i = 0; i < func_num; i++) {
+ fi_ffs[i] = usb_get_function_instance("ffs");
+ if (IS_ERR(fi_ffs[i])) {
+ ret = PTR_ERR(fi_ffs[i]);
+ --i;
+ goto no_dev;
+ }
+ opts = to_f_fs_opts(fi_ffs[i]);
+ if (gfs_single_func)
+ ret = ffs_single_dev(opts->dev);
+ else
+ ret = ffs_name_dev(opts->dev, func_names[i]);
+ if (ret)
+ goto no_dev;
+ opts->dev->ffs_ready_callback = functionfs_ready_callback;
+ opts->dev->ffs_closed_callback = functionfs_closed_callback;
+ opts->dev->ffs_acquire_dev_callback = functionfs_acquire_dev;
+ opts->dev->ffs_release_dev_callback = functionfs_release_dev;
+ opts->no_configfs = true;
+ }
missing_funcs = func_num;
- return functionfs_init();
+ return 0;
+no_dev:
+ while (i >= 0)
+ usb_put_function_instance(fi_ffs[i--]);
+ kfree(fi_ffs);
+no_func:
+ kfree(f_ffs[0]);
+ return ret;
}
module_init(gfs_init);
static void __exit gfs_exit(void)
{
+ int i;
+
ENTER();
- mutex_lock(&gfs_lock);
if (gfs_registered)
usb_composite_unregister(&gfs_driver);
gfs_registered = false;
- functionfs_cleanup();
+ kfree(f_ffs[0]);
+
+ for (i = 0; i < func_num; i++)
+ usb_put_function_instance(fi_ffs[i]);
- mutex_unlock(&gfs_lock);
- kfree(ffs_tab);
+ kfree(fi_ffs);
}
module_exit(gfs_exit);
-static struct gfs_ffs_obj *gfs_find_dev(const char *dev_name)
+static void *functionfs_acquire_dev(struct ffs_dev *dev)
{
- int i;
-
- ENTER();
-
- if (gfs_single_func)
- return &ffs_tab[0];
-
- for (i = 0; i < func_num; i++)
- if (strcmp(ffs_tab[i].name, dev_name) == 0)
- return &ffs_tab[i];
+ if (!try_module_get(THIS_MODULE))
+ return ERR_PTR(-ENODEV);
+
+ return 0;
+}
- return NULL;
+static void functionfs_release_dev(struct ffs_dev *dev)
+{
+ module_put(THIS_MODULE);
}
+/*
+ * The caller of this function takes ffs_lock
+ */
static int functionfs_ready_callback(struct ffs_data *ffs)
{
- struct gfs_ffs_obj *ffs_obj;
- int ret;
-
- ENTER();
- mutex_lock(&gfs_lock);
+ int ret = 0;
- ffs_obj = ffs->private_data;
- if (!ffs_obj) {
- ret = -EINVAL;
- goto done;
- }
+ if (--missing_funcs)
+ return 0;
- if (WARN_ON(ffs_obj->desc_ready)) {
- ret = -EBUSY;
- goto done;
- }
- ffs_obj->desc_ready = true;
- ffs_obj->ffs_data = ffs;
-
- if (--missing_funcs) {
- ret = 0;
- goto done;
- }
+ if (gfs_registered)
+ return -EBUSY;
- if (gfs_registered) {
- ret = -EBUSY;
- goto done;
- }
gfs_registered = true;
ret = usb_composite_probe(&gfs_driver);
if (unlikely(ret < 0))
gfs_registered = false;
-
-done:
- mutex_unlock(&gfs_lock);
+
return ret;
}
+/*
+ * The caller of this function takes ffs_lock
+ */
static void functionfs_closed_callback(struct ffs_data *ffs)
{
- struct gfs_ffs_obj *ffs_obj;
-
- ENTER();
- mutex_lock(&gfs_lock);
-
- ffs_obj = ffs->private_data;
- if (!ffs_obj)
- goto done;
-
- ffs_obj->desc_ready = false;
missing_funcs++;
if (gfs_registered)
usb_composite_unregister(&gfs_driver);
gfs_registered = false;
-
-done:
- mutex_unlock(&gfs_lock);
}
-static void *functionfs_acquire_dev_callback(const char *dev_name)
+/*
+ * It is assumed that gfs_bind is called from a context where ffs_lock is held
+ */
+static int gfs_bind(struct usb_composite_dev *cdev)
{
- struct gfs_ffs_obj *ffs_dev;
+#if defined CONFIG_USB_FUNCTIONFS_ETH || defined CONFIG_USB_FUNCTIONFS_RNDIS
+ struct net_device *net;
+#endif
+ int ret, i;
ENTER();
- mutex_lock(&gfs_lock);
-
- ffs_dev = gfs_find_dev(dev_name);
- if (!ffs_dev) {
- ffs_dev = ERR_PTR(-ENODEV);
- goto done;
- }
- if (ffs_dev->mounted) {
- ffs_dev = ERR_PTR(-EBUSY);
- goto done;
+ if (missing_funcs)
+ return -ENODEV;
+#if defined CONFIG_USB_FUNCTIONFS_ETH
+ if (can_support_ecm(cdev->gadget)) {
+ struct f_ecm_opts *ecm_opts;
+
+ fi_ecm = usb_get_function_instance("ecm");
+ if (IS_ERR(fi_ecm))
+ return PTR_ERR(fi_ecm);
+ ecm_opts = container_of(fi_ecm, struct f_ecm_opts, func_inst);
+ net = ecm_opts->net;
+ } else {
+ struct f_gether_opts *geth_opts;
+
+ fi_geth = usb_get_function_instance("geth");
+ if (IS_ERR(fi_geth))
+ return PTR_ERR(fi_geth);
+ geth_opts = container_of(fi_geth, struct f_gether_opts,
+ func_inst);
+ net = geth_opts->net;
}
- ffs_dev->mounted = true;
+#endif
-done:
- mutex_unlock(&gfs_lock);
- return ffs_dev;
-}
+#ifdef CONFIG_USB_FUNCTIONFS_RNDIS
+ {
+ struct f_rndis_opts *rndis_opts;
-static void functionfs_release_dev_callback(struct ffs_data *ffs_data)
-{
- struct gfs_ffs_obj *ffs_dev;
+ fi_rndis = usb_get_function_instance("rndis");
+ if (IS_ERR(fi_rndis)) {
+ ret = PTR_ERR(fi_rndis);
+ goto error;
+ }
+ rndis_opts = container_of(fi_rndis, struct f_rndis_opts,
+ func_inst);
+#ifndef CONFIG_USB_FUNCTIONFS_ETH
+ net = rndis_opts->net;
+#endif
+ }
+#endif
- ENTER();
- mutex_lock(&gfs_lock);
+#if defined CONFIG_USB_FUNCTIONFS_ETH || defined CONFIG_USB_FUNCTIONFS_RNDIS
+ gether_set_qmult(net, qmult);
+ if (!gether_set_host_addr(net, host_addr))
+ pr_info("using host ethernet address: %s", host_addr);
+ if (!gether_set_dev_addr(net, dev_addr))
+ pr_info("using self ethernet address: %s", dev_addr);
+#endif
- ffs_dev = ffs_data->private_data;
- if (ffs_dev)
- ffs_dev->mounted = false;
+#if defined CONFIG_USB_FUNCTIONFS_RNDIS && defined CONFIG_USB_FUNCTIONFS_ETH
+ gether_set_gadget(net, cdev->gadget);
+ ret = gether_register_netdev(net);
+ if (ret)
+ goto error_rndis;
- mutex_unlock(&gfs_lock);
-}
+ if (can_support_ecm(cdev->gadget)) {
+ struct f_ecm_opts *ecm_opts;
-/*
- * It is assumed that gfs_bind is called from a context where gfs_lock is held
- */
-static int gfs_bind(struct usb_composite_dev *cdev)
-{
- int ret, i;
+ ecm_opts = container_of(fi_ecm, struct f_ecm_opts, func_inst);
+ ecm_opts->bound = true;
+ } else {
+ struct f_gether_opts *geth_opts;
- ENTER();
+ geth_opts = container_of(fi_geth, struct f_gether_opts,
+ func_inst);
+ geth_opts->bound = true;
+ }
- if (missing_funcs)
- return -ENODEV;
-#if defined CONFIG_USB_FUNCTIONFS_ETH || defined CONFIG_USB_FUNCTIONFS_RNDIS
- the_dev = gether_setup(cdev->gadget, dev_addr, host_addr, gfs_host_mac,
- qmult);
+ rndis_borrow_net(fi_rndis, net);
#endif
- if (IS_ERR(the_dev)) {
- ret = PTR_ERR(the_dev);
- goto error_quick;
- }
- gfs_ether_setup = true;
+ /* TODO: gstrings_attach? */
ret = usb_string_ids_tab(cdev, gfs_strings);
if (unlikely(ret < 0))
- goto error;
+ goto error_rndis;
gfs_dev_desc.iProduct = gfs_strings[USB_GADGET_PRODUCT_IDX].id;
- for (i = func_num; i--; ) {
- ret = functionfs_bind(ffs_tab[i].ffs_data, cdev);
- if (unlikely(ret < 0)) {
- while (++i < func_num)
- functionfs_unbind(ffs_tab[i].ffs_data);
- goto error;
- }
- }
-
for (i = 0; i < ARRAY_SIZE(gfs_configurations); ++i) {
struct gfs_configuration *c = gfs_configurations + i;
int sid = USB_GADGET_FIRST_AVAIL_IDX + i;
@@ -389,6 +417,8 @@ static int gfs_bind(struct usb_composite_dev *cdev)
c->c.bConfigurationValue = 1 + i;
c->c.bmAttributes = USB_CONFIG_ATT_SELFPOWER;
+ c->num = i;
+
ret = usb_add_config(cdev, &c->c, gfs_do_config);
if (unlikely(ret < 0))
goto error_unbind;
@@ -396,18 +426,24 @@ static int gfs_bind(struct usb_composite_dev *cdev)
usb_composite_overwrite_options(cdev, &coverwrite);
return 0;
+/* TODO */
error_unbind:
- for (i = 0; i < func_num; i++)
- functionfs_unbind(ffs_tab[i].ffs_data);
+error_rndis:
+#ifdef CONFIG_USB_FUNCTIONFS_RNDIS
+ usb_put_function_instance(fi_rndis);
error:
- gether_cleanup(the_dev);
-error_quick:
- gfs_ether_setup = false;
+#endif
+#if defined CONFIG_USB_FUNCTIONFS_ETH
+ if (can_support_ecm(cdev->gadget))
+ usb_put_function_instance(fi_ecm);
+ else
+ usb_put_function_instance(fi_geth);
+#endif
return ret;
}
/*
- * It is assumed that gfs_unbind is called from a context where gfs_lock is held
+ * It is assumed that gfs_unbind is called from a context where ffs_lock is held
*/
static int gfs_unbind(struct usb_composite_dev *cdev)
{
@@ -415,28 +451,30 @@ static int gfs_unbind(struct usb_composite_dev *cdev)
ENTER();
- /*
- * We may have been called in an error recovery from
- * composite_bind() after gfs_unbind() failure so we need to
- * check if gfs_ffs_data is not NULL since gfs_bind() handles
- * all error recovery itself. I'd rather we werent called
- * from composite on orror recovery, but what you're gonna
- * do...?
- */
- if (gfs_ether_setup)
- gether_cleanup(the_dev);
- gfs_ether_setup = false;
- for (i = func_num; i--; )
- if (ffs_tab[i].ffs_data)
- functionfs_unbind(ffs_tab[i].ffs_data);
+#ifdef CONFIG_USB_FUNCTIONFS_RNDIS
+ usb_put_function(f_rndis);
+ usb_put_function_instance(fi_rndis);
+#endif
+
+#if defined CONFIG_USB_FUNCTIONFS_ETH
+ if (can_support_ecm(cdev->gadget)) {
+ usb_put_function(f_ecm);
+ usb_put_function_instance(fi_ecm);
+ } else {
+ usb_put_function(f_geth);
+ usb_put_function_instance(fi_geth);
+ }
+#endif
+ for (i = 0; i < N_CONF * func_num; ++i)
+ usb_put_function(*(f_ffs[0] + i));
return 0;
}
/*
* It is assumed that gfs_do_config is called from a context where
- * gfs_lock is held
+ * ffs_lock is held
*/
static int gfs_do_config(struct usb_configuration *c)
{
@@ -454,15 +492,22 @@ static int gfs_do_config(struct usb_configuration *c)
}
if (gc->eth) {
- ret = gc->eth(c, gfs_host_mac, the_dev);
+ ret = gc->eth(c);
if (unlikely(ret < 0))
return ret;
}
for (i = 0; i < func_num; i++) {
- ret = functionfs_bind_config(c->cdev, c, ffs_tab[i].ffs_data);
- if (unlikely(ret < 0))
- return ret;
+ f_ffs[gc->num][i] = usb_get_function(fi_ffs[i]);
+ if (IS_ERR(f_ffs[gc->num][i])) {
+ ret = PTR_ERR(f_ffs[gc->num][i]);
+ goto error;
+ }
+ ret = usb_add_function(c, f_ffs[gc->num][i]);
+ if (ret < 0) {
+ usb_put_function(f_ffs[gc->num][i]);
+ goto error;
+ }
}
/*
@@ -479,16 +524,59 @@ static int gfs_do_config(struct usb_configuration *c)
c->interface[c->next_interface_id] = NULL;
return 0;
+error:
+ while (--i >= 0) {
+ if (!IS_ERR(f_ffs[gc->num][i]))
+ usb_remove_function(c, f_ffs[gc->num][i]);
+ usb_put_function(f_ffs[gc->num][i]);
+ }
+ return ret;
}
#ifdef CONFIG_USB_FUNCTIONFS_ETH
-static int eth_bind_config(struct usb_configuration *c, u8 ethaddr[ETH_ALEN],
- struct eth_dev *dev)
+static int eth_bind_config(struct usb_configuration *c)
+{
+ int status = 0;
+
+ if (can_support_ecm(c->cdev->gadget)) {
+ f_ecm = usb_get_function(fi_ecm);
+ if (IS_ERR(f_ecm))
+ return PTR_ERR(f_ecm);
+
+ status = usb_add_function(c, f_ecm);
+ if (status < 0)
+ usb_put_function(f_ecm);
+
+ } else {
+ f_geth = usb_get_function(fi_geth);
+ if (IS_ERR(f_geth))
+ return PTR_ERR(f_geth);
+
+ status = usb_add_function(c, f_geth);
+ if (status < 0)
+ usb_put_function(f_geth);
+ }
+ return status;
+}
+
+#endif
+
+#ifdef CONFIG_USB_FUNCTIONFS_RNDIS
+
+static int bind_rndis_config(struct usb_configuration *c)
{
- return can_support_ecm(c->cdev->gadget)
- ? ecm_bind_config(c, ethaddr, dev)
- : geth_bind_config(c, ethaddr, dev);
+ int status = 0;
+
+ f_rndis = usb_get_function(fi_rndis);
+ if (IS_ERR(f_rndis))
+ return PTR_ERR(f_rndis);
+
+ status = usb_add_function(c, f_rndis);
+ if (status < 0)
+ usb_put_function(f_rndis);
+
+ return status;
}
#endif
diff --git a/drivers/usb/gadget/g_zero.h b/drivers/usb/gadget/g_zero.h
index ef3e8515272b..15f180904f8a 100644
--- a/drivers/usb/gadget/g_zero.h
+++ b/drivers/usb/gadget/g_zero.h
@@ -6,6 +6,11 @@
#ifndef __G_ZERO_H
#define __G_ZERO_H
+#define GZERO_BULK_BUFLEN 4096
+#define GZERO_QLEN 32
+#define GZERO_ISOC_INTERVAL 4
+#define GZERO_ISOC_MAXPACKET 1024
+
struct usb_zero_options {
unsigned pattern;
unsigned isoc_interval;
@@ -24,19 +29,36 @@ struct f_ss_opts {
unsigned isoc_mult;
unsigned isoc_maxburst;
unsigned bulk_buflen;
+
+ /*
+ * Read/write access to configfs attributes is handled by configfs.
+ *
+ * This is to protect the data from concurrent access by read/write
+ * and create symlink/remove symlink.
+ */
+ struct mutex lock;
+ int refcnt;
};
struct f_lb_opts {
struct usb_function_instance func_inst;
unsigned bulk_buflen;
unsigned qlen;
+
+ /*
+ * Read/write access to configfs attributes is handled by configfs.
+ *
+ * This is to protect the data from concurrent access by read/write
+ * and create symlink/remove symlink.
+ */
+ struct mutex lock;
+ int refcnt;
};
void lb_modexit(void);
int lb_modinit(void);
/* common utilities */
-struct usb_request *alloc_ep_req(struct usb_ep *ep, int len);
void free_ep_req(struct usb_ep *ep, struct usb_request *req);
void disable_endpoints(struct usb_composite_dev *cdev,
struct usb_ep *in, struct usb_ep *out,
diff --git a/drivers/usb/gadget/goku_udc.c b/drivers/usb/gadget/goku_udc.c
index f82768015715..f66f3a7a35ef 100644
--- a/drivers/usb/gadget/goku_udc.c
+++ b/drivers/usb/gadget/goku_udc.c
@@ -231,7 +231,7 @@ static void ep_reset(struct goku_udc_regs __iomem *regs, struct goku_ep *ep)
}
}
- ep->ep.maxpacket = MAX_FIFO_SIZE;
+ usb_ep_set_maxpacket_limit(&ep->ep, MAX_FIFO_SIZE);
ep->ep.desc = NULL;
ep->stopped = 1;
ep->irqs = 0;
@@ -1251,7 +1251,7 @@ static void udc_reinit (struct goku_udc *dev)
}
dev->ep[0].reg_mode = NULL;
- dev->ep[0].ep.maxpacket = MAX_EP0_SIZE;
+ usb_ep_set_maxpacket_limit(&dev->ep[0].ep, MAX_EP0_SIZE);
list_del_init (&dev->ep[0].ep.ep_list);
}
@@ -1350,16 +1350,12 @@ static int goku_udc_start(struct usb_gadget *g,
return 0;
}
-static void
-stop_activity(struct goku_udc *dev, struct usb_gadget_driver *driver)
+static void stop_activity(struct goku_udc *dev)
{
unsigned i;
DBG (dev, "%s\n", __func__);
- if (dev->gadget.speed == USB_SPEED_UNKNOWN)
- driver = NULL;
-
/* disconnect gadget driver after quiesceing hw and the driver */
udc_reset (dev);
for (i = 0; i < 4; i++)
@@ -1377,7 +1373,7 @@ static int goku_udc_stop(struct usb_gadget *g,
spin_lock_irqsave(&dev->lock, flags);
dev->driver = NULL;
- stop_activity(dev, driver);
+ stop_activity(dev);
spin_unlock_irqrestore(&dev->lock, flags);
return 0;
@@ -1521,7 +1517,7 @@ rescan:
if (unlikely(stat & INT_DEVWIDE)) {
if (stat & INT_SYSERROR) {
ERROR(dev, "system error\n");
- stop_activity(dev, dev->driver);
+ stop_activity(dev);
stat = 0;
handled = 1;
// FIXME have a neater way to prevent re-enumeration
@@ -1536,7 +1532,7 @@ rescan:
} else {
DBG(dev, "disconnect\n");
if (dev->gadget.speed == USB_SPEED_FULL)
- stop_activity(dev, dev->driver);
+ stop_activity(dev);
dev->ep0state = EP0_DISCONNECT;
dev->int_enable = INT_DEVWIDE;
writel(dev->int_enable, &dev->regs->int_enable);
diff --git a/drivers/usb/gadget/lpc32xx_udc.c b/drivers/usb/gadget/lpc32xx_udc.c
index 6a2a65aa0057..049ebab0d360 100644
--- a/drivers/usb/gadget/lpc32xx_udc.c
+++ b/drivers/usb/gadget/lpc32xx_udc.c
@@ -1449,7 +1449,7 @@ static void udc_reinit(struct lpc32xx_udc *udc)
if (i != 0)
list_add_tail(&ep->ep.ep_list, &udc->gadget.ep_list);
- ep->ep.maxpacket = ep->maxpacket;
+ usb_ep_set_maxpacket_limit(&ep->ep, ep->maxpacket);
INIT_LIST_HEAD(&ep->queue);
ep->req_pending = 0;
}
diff --git a/drivers/usb/gadget/m66592-udc.c b/drivers/usb/gadget/m66592-udc.c
index d5f050d30edf..8cae01d88597 100644
--- a/drivers/usb/gadget/m66592-udc.c
+++ b/drivers/usb/gadget/m66592-udc.c
@@ -1647,9 +1647,9 @@ static int __init m66592_probe(struct platform_device *pdev)
INIT_LIST_HEAD(&ep->queue);
ep->ep.name = m66592_ep_name[i];
ep->ep.ops = &m66592_ep_ops;
- ep->ep.maxpacket = 512;
+ usb_ep_set_maxpacket_limit(&ep->ep, 512);
}
- m66592->ep[0].ep.maxpacket = 64;
+ usb_ep_set_maxpacket_limit(&m66592->ep[0].ep, 64);
m66592->ep[0].pipenum = 0;
m66592->ep[0].fifoaddr = M66592_CFIFO;
m66592->ep[0].fifosel = M66592_CFIFOSEL;
diff --git a/drivers/usb/gadget/multi.c b/drivers/usb/gadget/multi.c
index 4fdaa54a2a2a..940f6cde8e89 100644
--- a/drivers/usb/gadget/multi.c
+++ b/drivers/usb/gadget/multi.c
@@ -134,7 +134,7 @@ static unsigned int fsg_num_buffers = CONFIG_USB_GADGET_STORAGE_NUM_BUFFERS;
*/
#define fsg_num_buffers CONFIG_USB_GADGET_STORAGE_NUM_BUFFERS
-#endif /* CONFIG_USB_DEBUG */
+#endif /* CONFIG_USB_GADGET_DEBUG_FILES */
FSG_MODULE_PARAMETERS(/* no prefix */, fsg_mod_data);
diff --git a/drivers/usb/gadget/mv_u3d_core.c b/drivers/usb/gadget/mv_u3d_core.c
index 234711eabea1..9fe31d7dded6 100644
--- a/drivers/usb/gadget/mv_u3d_core.c
+++ b/drivers/usb/gadget/mv_u3d_core.c
@@ -1336,7 +1336,7 @@ static int mv_u3d_eps_init(struct mv_u3d *u3d)
ep->ep.name = ep->name;
ep->ep.ops = &mv_u3d_ep_ops;
ep->wedge = 0;
- ep->ep.maxpacket = MV_U3D_EP0_MAX_PKT_SIZE;
+ usb_ep_set_maxpacket_limit(&ep->ep, MV_U3D_EP0_MAX_PKT_SIZE);
ep->ep_num = 0;
ep->ep.desc = &mv_u3d_ep0_desc;
INIT_LIST_HEAD(&ep->queue);
@@ -1361,7 +1361,7 @@ static int mv_u3d_eps_init(struct mv_u3d *u3d)
ep->ep.name = ep->name;
ep->ep.ops = &mv_u3d_ep_ops;
- ep->ep.maxpacket = (unsigned short) ~0;
+ usb_ep_set_maxpacket_limit(&ep->ep, (unsigned short) ~0);
ep->ep_num = i / 2;
INIT_LIST_HEAD(&ep->queue);
diff --git a/drivers/usb/gadget/mv_udc_core.c b/drivers/usb/gadget/mv_udc_core.c
index 104cdbea635a..d43ce95fc4bd 100644
--- a/drivers/usb/gadget/mv_udc_core.c
+++ b/drivers/usb/gadget/mv_udc_core.c
@@ -1261,7 +1261,7 @@ static int eps_init(struct mv_udc *udc)
ep->ep.ops = &mv_ep_ops;
ep->wedge = 0;
ep->stopped = 0;
- ep->ep.maxpacket = EP0_MAX_PKT_SIZE;
+ usb_ep_set_maxpacket_limit(&ep->ep, EP0_MAX_PKT_SIZE);
ep->ep_num = 0;
ep->ep.desc = &mv_ep0_desc;
INIT_LIST_HEAD(&ep->queue);
@@ -1284,7 +1284,7 @@ static int eps_init(struct mv_udc *udc)
ep->ep.ops = &mv_ep_ops;
ep->stopped = 0;
- ep->ep.maxpacket = (unsigned short) ~0;
+ usb_ep_set_maxpacket_limit(&ep->ep, (unsigned short) ~0);
ep->ep_num = i / 2;
INIT_LIST_HEAD(&ep->queue);
diff --git a/drivers/usb/gadget/net2272.c b/drivers/usb/gadget/net2272.c
index bf2bb39f35a2..ca15405583e2 100644
--- a/drivers/usb/gadget/net2272.c
+++ b/drivers/usb/gadget/net2272.c
@@ -266,7 +266,7 @@ static void net2272_ep_reset(struct net2272_ep *ep)
ep->desc = NULL;
INIT_LIST_HEAD(&ep->queue);
- ep->ep.maxpacket = ~0;
+ usb_ep_set_maxpacket_limit(&ep->ep, ~0);
ep->ep.ops = &net2272_ep_ops;
/* disable irqs, endpoint */
@@ -1409,7 +1409,7 @@ net2272_usb_reinit(struct net2272 *dev)
ep->fifo_size = 64;
net2272_ep_reset(ep);
}
- dev->ep[0].ep.maxpacket = 64;
+ usb_ep_set_maxpacket_limit(&dev->ep[0].ep, 64);
dev->gadget.ep0 = &dev->ep[0].ep;
dev->ep[0].stopped = 0;
diff --git a/drivers/usb/gadget/net2280.c b/drivers/usb/gadget/net2280.c
index fc852177c087..43e5e2f9888f 100644
--- a/drivers/usb/gadget/net2280.c
+++ b/drivers/usb/gadget/net2280.c
@@ -293,7 +293,7 @@ static void ep_reset (struct net2280_regs __iomem *regs, struct net2280_ep *ep)
ep->desc = NULL;
INIT_LIST_HEAD (&ep->queue);
- ep->ep.maxpacket = ~0;
+ usb_ep_set_maxpacket_limit(&ep->ep, ~0);
ep->ep.ops = &net2280_ep_ops;
/* disable the dma, irqs, endpoint... */
@@ -1805,9 +1805,9 @@ static void usb_reinit (struct net2280 *dev)
ep->regs = &dev->epregs [tmp];
ep_reset (dev->regs, ep);
}
- dev->ep [0].ep.maxpacket = 64;
- dev->ep [5].ep.maxpacket = 64;
- dev->ep [6].ep.maxpacket = 64;
+ 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;
diff --git a/drivers/usb/gadget/nokia.c b/drivers/usb/gadget/nokia.c
index 0a8099a488c4..3ab386167519 100644
--- a/drivers/usb/gadget/nokia.c
+++ b/drivers/usb/gadget/nokia.c
@@ -126,9 +126,9 @@ static int __init nokia_bind_config(struct usb_configuration *c)
struct usb_function *f_ecm;
struct usb_function *f_obex2 = NULL;
int status = 0;
- int obex1_stat = 0;
- int obex2_stat = 0;
- int phonet_stat = 0;
+ int obex1_stat = -1;
+ int obex2_stat = -1;
+ int phonet_stat = -1;
if (!IS_ERR(fi_phonet)) {
f_phonet = usb_get_function(fi_phonet);
diff --git a/drivers/usb/gadget/omap_udc.c b/drivers/usb/gadget/omap_udc.c
index 83957cc225d9..34bd713065c5 100644
--- a/drivers/usb/gadget/omap_udc.c
+++ b/drivers/usb/gadget/omap_udc.c
@@ -2586,7 +2586,8 @@ omap_ep_setup(char *name, u8 addr, u8 type,
ep->ep.name = ep->name;
ep->ep.ops = &omap_ep_ops;
- ep->ep.maxpacket = ep->maxpacket = maxp;
+ ep->maxpacket = maxp;
+ usb_ep_set_maxpacket_limit(&ep->ep, ep->maxpacket);
list_add_tail(&ep->ep.ep_list, &udc->gadget.ep_list);
return buf;
diff --git a/drivers/usb/gadget/pch_udc.c b/drivers/usb/gadget/pch_udc.c
index 32d5e923750b..eb8c3bedb57a 100644
--- a/drivers/usb/gadget/pch_udc.c
+++ b/drivers/usb/gadget/pch_udc.c
@@ -2896,12 +2896,12 @@ static void pch_udc_pcd_reinit(struct pch_udc_dev *dev)
ep->offset_addr = (UDC_EPINT_OUT_SHIFT + ep->num) *
UDC_EP_REG_SHIFT;
/* need to set ep->ep.maxpacket and set Default Configuration?*/
- ep->ep.maxpacket = UDC_BULK_MAX_PKT_SIZE;
+ usb_ep_set_maxpacket_limit(&ep->ep, UDC_BULK_MAX_PKT_SIZE);
list_add_tail(&ep->ep.ep_list, &dev->gadget.ep_list);
INIT_LIST_HEAD(&ep->queue);
}
- dev->ep[UDC_EP0IN_IDX].ep.maxpacket = UDC_EP0IN_MAX_PKT_SIZE;
- dev->ep[UDC_EP0OUT_IDX].ep.maxpacket = UDC_EP0OUT_MAX_PKT_SIZE;
+ usb_ep_set_maxpacket_limit(&dev->ep[UDC_EP0IN_IDX].ep, UDC_EP0IN_MAX_PKT_SIZE);
+ usb_ep_set_maxpacket_limit(&dev->ep[UDC_EP0OUT_IDX].ep, UDC_EP0OUT_MAX_PKT_SIZE);
/* remove ep0 in and out from the list. They have own pointer */
list_del_init(&dev->ep[UDC_EP0IN_IDX].ep.ep_list);
@@ -3210,7 +3210,7 @@ finished:
return retval;
}
-static DEFINE_PCI_DEVICE_TABLE(pch_udc_pcidev_id) = {
+static const struct pci_device_id pch_udc_pcidev_id[] = {
{
PCI_DEVICE(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_EG20T_UDC),
.class = (PCI_CLASS_SERIAL_USB << 8) | 0xfe,
diff --git a/drivers/usb/gadget/pxa25x_udc.c b/drivers/usb/gadget/pxa25x_udc.c
index 409a3c45a36a..33b09a7b626c 100644
--- a/drivers/usb/gadget/pxa25x_udc.c
+++ b/drivers/usb/gadget/pxa25x_udc.c
@@ -1194,6 +1194,7 @@ static void udc_reinit(struct pxa25x_udc *dev)
ep->stopped = 0;
INIT_LIST_HEAD (&ep->queue);
ep->pio_irqs = 0;
+ usb_ep_set_maxpacket_limit(&ep->ep, ep->ep.maxpacket);
}
/* the rest was statically initialized, and is read-only */
diff --git a/drivers/usb/gadget/pxa27x_udc.c b/drivers/usb/gadget/pxa27x_udc.c
index 3c97da7760da..cdf4d678be96 100644
--- a/drivers/usb/gadget/pxa27x_udc.c
+++ b/drivers/usb/gadget/pxa27x_udc.c
@@ -1737,9 +1737,12 @@ static void udc_init_data(struct pxa_udc *dev)
}
/* USB endpoints init */
- for (i = 1; i < NR_USB_ENDPOINTS; i++)
+ for (i = 1; i < NR_USB_ENDPOINTS; i++) {
list_add_tail(&dev->udc_usb_ep[i].usb_ep.ep_list,
&dev->gadget.ep_list);
+ usb_ep_set_maxpacket_limit(&dev->udc_usb_ep[i].usb_ep,
+ dev->udc_usb_ep[i].usb_ep.maxpacket);
+ }
}
/**
diff --git a/drivers/usb/gadget/r8a66597-udc.c b/drivers/usb/gadget/r8a66597-udc.c
index 68be48d33404..aff0a6718bc6 100644
--- a/drivers/usb/gadget/r8a66597-udc.c
+++ b/drivers/usb/gadget/r8a66597-udc.c
@@ -1833,7 +1833,7 @@ static int __exit r8a66597_remove(struct platform_device *pdev)
r8a66597_free_request(&r8a66597->ep[0].ep, r8a66597->ep0_req);
if (r8a66597->pdata->on_chip) {
- clk_disable(r8a66597->clk);
+ clk_disable_unprepare(r8a66597->clk);
clk_put(r8a66597->clk);
}
@@ -1931,7 +1931,7 @@ static int __init r8a66597_probe(struct platform_device *pdev)
ret = PTR_ERR(r8a66597->clk);
goto clean_up;
}
- clk_enable(r8a66597->clk);
+ clk_prepare_enable(r8a66597->clk);
}
if (r8a66597->pdata->sudmac) {
@@ -1964,9 +1964,9 @@ static int __init r8a66597_probe(struct platform_device *pdev)
INIT_LIST_HEAD(&ep->queue);
ep->ep.name = r8a66597_ep_name[i];
ep->ep.ops = &r8a66597_ep_ops;
- ep->ep.maxpacket = 512;
+ usb_ep_set_maxpacket_limit(&ep->ep, 512);
}
- r8a66597->ep[0].ep.maxpacket = 64;
+ usb_ep_set_maxpacket_limit(&r8a66597->ep[0].ep, 64);
r8a66597->ep[0].pipenum = 0;
r8a66597->ep[0].fifoaddr = CFIFO;
r8a66597->ep[0].fifosel = CFIFOSEL;
@@ -1996,7 +1996,7 @@ clean_up3:
free_irq(irq, r8a66597);
clean_up2:
if (r8a66597->pdata->on_chip) {
- clk_disable(r8a66597->clk);
+ clk_disable_unprepare(r8a66597->clk);
clk_put(r8a66597->clk);
}
clean_up:
diff --git a/drivers/usb/gadget/rndis.c b/drivers/usb/gadget/rndis.c
index a3ad732bc812..1ffbdb4659f2 100644
--- a/drivers/usb/gadget/rndis.c
+++ b/drivers/usb/gadget/rndis.c
@@ -1142,7 +1142,7 @@ static struct proc_dir_entry *rndis_connect_state [RNDIS_MAX_CONFIGS];
#endif /* CONFIG_USB_GADGET_DEBUG_FILES */
-static int rndis_init(void)
+int rndis_init(void)
{
u8 i;
@@ -1174,9 +1174,8 @@ static int rndis_init(void)
return 0;
}
-module_init(rndis_init);
-static void rndis_exit(void)
+void rndis_exit(void)
{
#ifdef CONFIG_USB_GADGET_DEBUG_FILES
u8 i;
@@ -1188,6 +1187,4 @@ static void rndis_exit(void)
}
#endif
}
-module_exit(rndis_exit);
-MODULE_LICENSE("GPL");
diff --git a/drivers/usb/gadget/s3c-hsotg.c b/drivers/usb/gadget/s3c-hsotg.c
index e20bc109fdd7..83bacf4b409b 100644
--- a/drivers/usb/gadget/s3c-hsotg.c
+++ b/drivers/usb/gadget/s3c-hsotg.c
@@ -2086,13 +2086,13 @@ static void s3c_hsotg_irq_enumdone(struct s3c_hsotg *hsotg)
case DSTS_EnumSpd_FS48:
hsotg->gadget.speed = USB_SPEED_FULL;
ep0_mps = EP0_MPS_LIMIT;
- ep_mps = 64;
+ ep_mps = 1023;
break;
case DSTS_EnumSpd_HS:
hsotg->gadget.speed = USB_SPEED_HIGH;
ep0_mps = EP0_MPS_LIMIT;
- ep_mps = 512;
+ ep_mps = 1024;
break;
case DSTS_EnumSpd_LS:
@@ -2156,6 +2156,9 @@ static void kill_all_requests(struct s3c_hsotg *hsotg,
s3c_hsotg_complete_request(hsotg, ep, req,
result);
}
+ if(hsotg->dedicated_fifos)
+ if ((readl(hsotg->regs + DTXFSTS(ep->index)) & 0xffff) * 4 < 3072)
+ s3c_hsotg_txfifo_flush(hsotg, ep->index);
}
#define call_gadget(_hs, _entry) \
@@ -3152,7 +3155,7 @@ static void s3c_hsotg_initep(struct s3c_hsotg *hsotg,
hs_ep->parent = hsotg;
hs_ep->ep.name = hs_ep->name;
- hs_ep->ep.maxpacket = epnum ? 1024 : EP0_MPS_LIMIT;
+ usb_ep_set_maxpacket_limit(&hs_ep->ep, epnum ? 1024 : EP0_MPS_LIMIT);
hs_ep->ep.ops = &s3c_hsotg_ep_ops;
/*
diff --git a/drivers/usb/gadget/s3c-hsudc.c b/drivers/usb/gadget/s3c-hsudc.c
index 1a1a41498db2..ea4bbfe72ec0 100644
--- a/drivers/usb/gadget/s3c-hsudc.c
+++ b/drivers/usb/gadget/s3c-hsudc.c
@@ -999,7 +999,7 @@ static void s3c_hsudc_initep(struct s3c_hsudc *hsudc,
hsep->dev = hsudc;
hsep->ep.name = hsep->name;
- hsep->ep.maxpacket = epnum ? 512 : 64;
+ usb_ep_set_maxpacket_limit(&hsep->ep, epnum ? 512 : 64);
hsep->ep.ops = &s3c_hsudc_ep_ops;
hsep->fifo = hsudc->regs + S3C_BR(epnum);
hsep->ep.desc = NULL;
diff --git a/drivers/usb/gadget/s3c2410_udc.c b/drivers/usb/gadget/s3c2410_udc.c
index c72d810e6b36..f04b2c3154de 100644
--- a/drivers/usb/gadget/s3c2410_udc.c
+++ b/drivers/usb/gadget/s3c2410_udc.c
@@ -1629,6 +1629,7 @@ static void s3c2410_udc_reinit(struct s3c2410_udc *dev)
ep->ep.desc = NULL;
ep->halted = 0;
INIT_LIST_HEAD(&ep->queue);
+ usb_ep_set_maxpacket_limit(&ep->ep, &ep->ep.maxpacket);
}
}
diff --git a/drivers/usb/gadget/u_ether.c b/drivers/usb/gadget/u_ether.c
index 2aae0d61bb19..b7d4f82872b7 100644
--- a/drivers/usb/gadget/u_ether.c
+++ b/drivers/usb/gadget/u_ether.c
@@ -753,7 +753,7 @@ static struct device_type gadget_type = {
* gadget driver using this framework. The link layer addresses are
* set up using module parameters.
*
- * Returns negative errno, or zero on success
+ * Returns an eth_dev pointer on success, or an ERR_PTR on failure.
*/
struct eth_dev *gether_setup_name(struct usb_gadget *g,
const char *dev_addr, const char *host_addr,
diff --git a/drivers/usb/gadget/u_ether.h b/drivers/usb/gadget/u_ether.h
index fb23d1fde8eb..0f0290acea7e 100644
--- a/drivers/usb/gadget/u_ether.h
+++ b/drivers/usb/gadget/u_ether.h
@@ -106,7 +106,7 @@ struct eth_dev *gether_setup_name(struct usb_gadget *g,
* gadget driver using this framework. The link layer addresses are
* set up using module parameters.
*
- * Returns negative errno, or zero on success
+ * Returns a eth_dev pointer on success, or an ERR_PTR on failure
*/
static inline struct eth_dev *gether_setup(struct usb_gadget *g,
const char *dev_addr, const char *host_addr,
@@ -267,45 +267,4 @@ static inline bool can_support_ecm(struct usb_gadget *gadget)
return true;
}
-/* each configuration may bind one instance of an ethernet link */
-int geth_bind_config(struct usb_configuration *c, u8 ethaddr[ETH_ALEN],
- struct eth_dev *dev);
-int ecm_bind_config(struct usb_configuration *c, u8 ethaddr[ETH_ALEN],
- struct eth_dev *dev);
-
-#ifdef USB_ETH_RNDIS
-
-int rndis_bind_config_vendor(struct usb_configuration *c, u8 ethaddr[ETH_ALEN],
- u32 vendorID, const char *manufacturer, struct eth_dev *dev);
-
-#else
-
-static inline int
-rndis_bind_config_vendor(struct usb_configuration *c, u8 ethaddr[ETH_ALEN],
- u32 vendorID, const char *manufacturer, struct eth_dev *dev)
-{
- return 0;
-}
-
-#endif
-
-/**
- * rndis_bind_config - add RNDIS network link to a configuration
- * @c: the configuration to support the network link
- * @ethaddr: a buffer in which the ethernet address of the host side
- * side of the link was recorded
- * Context: single threaded during gadget setup
- *
- * Returns zero on success, else negative errno.
- *
- * Caller must have called @gether_setup(). Caller is also responsible
- * for calling @gether_cleanup() before module unload.
- */
-static inline int rndis_bind_config(struct usb_configuration *c,
- u8 ethaddr[ETH_ALEN], struct eth_dev *dev)
-{
- return rndis_bind_config_vendor(c, ethaddr, 0, NULL, dev);
-}
-
-
#endif /* __U_ETHER_H */
diff --git a/drivers/usb/gadget/u_f.c b/drivers/usb/gadget/u_f.c
new file mode 100644
index 000000000000..63b6642c162b
--- /dev/null
+++ b/drivers/usb/gadget/u_f.c
@@ -0,0 +1,32 @@
+/*
+ * u_f.c -- USB function utilities for Gadget stack
+ *
+ * Copyright (c) 2013 Samsung Electronics Co., Ltd.
+ * http://www.samsung.com
+ *
+ * Author: Andrzej Pietrasiewicz <andrzej.p@samsung.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/usb/gadget.h>
+#include "u_f.h"
+
+struct usb_request *alloc_ep_req(struct usb_ep *ep, int len, int default_len)
+{
+ struct usb_request *req;
+
+ req = usb_ep_alloc_request(ep, GFP_ATOMIC);
+ if (req) {
+ req->length = len ?: default_len;
+ req->buf = kmalloc(req->length, GFP_ATOMIC);
+ if (!req->buf) {
+ usb_ep_free_request(ep, req);
+ req = NULL;
+ }
+ }
+ return req;
+}
+EXPORT_SYMBOL(alloc_ep_req);
diff --git a/drivers/usb/gadget/u_f.h b/drivers/usb/gadget/u_f.h
new file mode 100644
index 000000000000..71034c061fca
--- /dev/null
+++ b/drivers/usb/gadget/u_f.h
@@ -0,0 +1,26 @@
+/*
+ * u_f.h
+ *
+ * Utility definitions for USB functions
+ *
+ * Copyright (c) 2013 Samsung Electronics Co., Ltd.
+ * http://www.samsung.com
+ *
+ * Author: Andrzej Pietrasiewicz <andrzej.p@samsung.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef __U_F_H__
+#define __U_F_H__
+
+struct usb_ep;
+struct usb_request;
+
+struct usb_request *alloc_ep_req(struct usb_ep *ep, int len, int default_len);
+
+#endif /* __U_F_H__ */
+
+
diff --git a/drivers/usb/gadget/u_fs.h b/drivers/usb/gadget/u_fs.h
new file mode 100644
index 000000000000..bc2d3718219b
--- /dev/null
+++ b/drivers/usb/gadget/u_fs.h
@@ -0,0 +1,267 @@
+/*
+ * u_fs.h
+ *
+ * Utility definitions for the FunctionFS
+ *
+ * Copyright (c) 2013 Samsung Electronics Co., Ltd.
+ * http://www.samsung.com
+ *
+ * Author: Andrzej Pietrasiewicz <andrzej.p@samsung.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef U_FFS_H
+#define U_FFS_H
+
+#include <linux/usb/composite.h>
+#include <linux/list.h>
+#include <linux/mutex.h>
+
+#ifdef VERBOSE_DEBUG
+#ifndef pr_vdebug
+# define pr_vdebug pr_debug
+#endif /* pr_vdebug */
+# define ffs_dump_mem(prefix, ptr, len) \
+ print_hex_dump_bytes(pr_fmt(prefix ": "), DUMP_PREFIX_NONE, ptr, len)
+#else
+#ifndef pr_vdebug
+# define pr_vdebug(...) do { } while (0)
+#endif /* pr_vdebug */
+# define ffs_dump_mem(prefix, ptr, len) do { } while (0)
+#endif /* VERBOSE_DEBUG */
+
+#define ENTER() pr_vdebug("%s()\n", __func__)
+
+struct f_fs_opts;
+
+struct ffs_dev {
+ const char *name;
+ bool name_allocated;
+ bool mounted;
+ bool desc_ready;
+ bool single;
+ struct ffs_data *ffs_data;
+ struct f_fs_opts *opts;
+ struct list_head entry;
+
+ int (*ffs_ready_callback)(struct ffs_data *ffs);
+ void (*ffs_closed_callback)(struct ffs_data *ffs);
+ void *(*ffs_acquire_dev_callback)(struct ffs_dev *dev);
+ void (*ffs_release_dev_callback)(struct ffs_dev *dev);
+};
+
+extern struct mutex ffs_lock;
+
+static inline void ffs_dev_lock(void)
+{
+ mutex_lock(&ffs_lock);
+}
+
+static inline void ffs_dev_unlock(void)
+{
+ mutex_unlock(&ffs_lock);
+}
+
+struct ffs_dev *ffs_alloc_dev(void);
+int ffs_name_dev(struct ffs_dev *dev, const char *name);
+int ffs_single_dev(struct ffs_dev *dev);
+void ffs_free_dev(struct ffs_dev *dev);
+
+struct ffs_epfile;
+struct ffs_function;
+
+enum ffs_state {
+ /*
+ * Waiting for descriptors and strings.
+ *
+ * In this state no open(2), read(2) or write(2) on epfiles
+ * may succeed (which should not be the problem as there
+ * should be no such files opened in the first place).
+ */
+ FFS_READ_DESCRIPTORS,
+ FFS_READ_STRINGS,
+
+ /*
+ * We've got descriptors and strings. We are or have called
+ * functionfs_ready_callback(). functionfs_bind() may have
+ * been called but we don't know.
+ *
+ * This is the only state in which operations on epfiles may
+ * succeed.
+ */
+ FFS_ACTIVE,
+
+ /*
+ * All endpoints have been closed. This state is also set if
+ * we encounter an unrecoverable error. The only
+ * unrecoverable error is situation when after reading strings
+ * from user space we fail to initialise epfiles or
+ * functionfs_ready_callback() returns with error (<0).
+ *
+ * In this state no open(2), read(2) or write(2) (both on ep0
+ * as well as epfile) may succeed (at this point epfiles are
+ * unlinked and all closed so this is not a problem; ep0 is
+ * also closed but ep0 file exists and so open(2) on ep0 must
+ * fail).
+ */
+ FFS_CLOSING
+};
+
+enum ffs_setup_state {
+ /* There is no setup request pending. */
+ FFS_NO_SETUP,
+ /*
+ * User has read events and there was a setup request event
+ * there. The next read/write on ep0 will handle the
+ * request.
+ */
+ FFS_SETUP_PENDING,
+ /*
+ * There was event pending but before user space handled it
+ * some other event was introduced which canceled existing
+ * setup. If this state is set read/write on ep0 return
+ * -EIDRM. This state is only set when adding event.
+ */
+ FFS_SETUP_CANCELED
+};
+
+struct ffs_data {
+ struct usb_gadget *gadget;
+
+ /*
+ * Protect access read/write operations, only one read/write
+ * at a time. As a consequence protects ep0req and company.
+ * While setup request is being processed (queued) this is
+ * held.
+ */
+ struct mutex mutex;
+
+ /*
+ * Protect access to endpoint related structures (basically
+ * usb_ep_queue(), usb_ep_dequeue(), etc. calls) except for
+ * endpoint zero.
+ */
+ spinlock_t eps_lock;
+
+ /*
+ * XXX REVISIT do we need our own request? Since we are not
+ * handling setup requests immediately user space may be so
+ * slow that another setup will be sent to the gadget but this
+ * time not to us but another function and then there could be
+ * a race. Is that the case? Or maybe we can use cdev->req
+ * after all, maybe we just need some spinlock for that?
+ */
+ struct usb_request *ep0req; /* P: mutex */
+ struct completion ep0req_completion; /* P: mutex */
+ int ep0req_status; /* P: mutex */
+
+ /* reference counter */
+ atomic_t ref;
+ /* how many files are opened (EP0 and others) */
+ atomic_t opened;
+
+ /* EP0 state */
+ enum ffs_state state;
+
+ /*
+ * Possible transitions:
+ * + FFS_NO_SETUP -> FFS_SETUP_PENDING -- P: ev.waitq.lock
+ * happens only in ep0 read which is P: mutex
+ * + FFS_SETUP_PENDING -> FFS_NO_SETUP -- P: ev.waitq.lock
+ * happens only in ep0 i/o which is P: mutex
+ * + FFS_SETUP_PENDING -> FFS_SETUP_CANCELED -- P: ev.waitq.lock
+ * + FFS_SETUP_CANCELED -> FFS_NO_SETUP -- cmpxchg
+ */
+ enum ffs_setup_state setup_state;
+
+#define FFS_SETUP_STATE(ffs) \
+ ((enum ffs_setup_state)cmpxchg(&(ffs)->setup_state, \
+ FFS_SETUP_CANCELED, FFS_NO_SETUP))
+
+ /* Events & such. */
+ struct {
+ u8 types[4];
+ unsigned short count;
+ /* XXX REVISIT need to update it in some places, or do we? */
+ unsigned short can_stall;
+ struct usb_ctrlrequest setup;
+
+ wait_queue_head_t waitq;
+ } ev; /* the whole structure, P: ev.waitq.lock */
+
+ /* Flags */
+ unsigned long flags;
+#define FFS_FL_CALL_CLOSED_CALLBACK 0
+#define FFS_FL_BOUND 1
+
+ /* Active function */
+ struct ffs_function *func;
+
+ /*
+ * Device name, write once when file system is mounted.
+ * Intended for user to read if she wants.
+ */
+ const char *dev_name;
+ /* Private data for our user (ie. gadget). Managed by user. */
+ void *private_data;
+
+ /* filled by __ffs_data_got_descs() */
+ /*
+ * Real descriptors are 16 bytes after raw_descs (so you need
+ * to skip 16 bytes (ie. ffs->raw_descs + 16) to get to the
+ * first full speed descriptor). raw_descs_length and
+ * raw_fs_descs_length do not have those 16 bytes added.
+ */
+ const void *raw_descs;
+ unsigned raw_descs_length;
+ unsigned raw_fs_descs_length;
+ unsigned fs_descs_count;
+ unsigned hs_descs_count;
+
+ unsigned short strings_count;
+ unsigned short interfaces_count;
+ unsigned short eps_count;
+ unsigned short _pad1;
+
+ /* filled by __ffs_data_got_strings() */
+ /* ids in stringtabs are set in functionfs_bind() */
+ const void *raw_strings;
+ struct usb_gadget_strings **stringtabs;
+
+ /*
+ * File system's super block, write once when file system is
+ * mounted.
+ */
+ struct super_block *sb;
+
+ /* File permissions, written once when fs is mounted */
+ struct ffs_file_perms {
+ umode_t mode;
+ kuid_t uid;
+ kgid_t gid;
+ } file_perms;
+
+ /*
+ * The endpoint files, filled by ffs_epfiles_create(),
+ * destroyed by ffs_epfiles_destroy().
+ */
+ struct ffs_epfile *epfiles;
+};
+
+
+struct f_fs_opts {
+ struct usb_function_instance func_inst;
+ struct ffs_dev *dev;
+ unsigned refcnt;
+ bool no_configfs;
+};
+
+static inline struct f_fs_opts *to_f_fs_opts(struct usb_function_instance *fi)
+{
+ return container_of(fi, struct f_fs_opts, func_inst);
+}
+
+#endif /* U_FFS_H */
diff --git a/drivers/usb/gadget/u_rndis.h b/drivers/usb/gadget/u_rndis.h
index c62ba82e9600..7291b15c9dce 100644
--- a/drivers/usb/gadget/u_rndis.h
+++ b/drivers/usb/gadget/u_rndis.h
@@ -36,6 +36,8 @@ struct f_rndis_opts {
int refcnt;
};
+int rndis_init(void);
+void rndis_exit(void);
void rndis_borrow_net(struct usb_function_instance *f, struct net_device *net);
#endif /* U_RNDIS_H */
diff --git a/drivers/usb/gadget/zero.c b/drivers/usb/gadget/zero.c
index f49b0b61ecc8..9f170c53e3d9 100644
--- a/drivers/usb/gadget/zero.c
+++ b/drivers/usb/gadget/zero.c
@@ -64,10 +64,10 @@ static bool loopdefault = 0;
module_param(loopdefault, bool, S_IRUGO|S_IWUSR);
static struct usb_zero_options gzero_options = {
- .isoc_interval = 4,
- .isoc_maxpacket = 1024,
- .bulk_buflen = 4096,
- .qlen = 32,
+ .isoc_interval = GZERO_ISOC_INTERVAL,
+ .isoc_maxpacket = GZERO_ISOC_MAXPACKET,
+ .bulk_buflen = GZERO_BULK_BUFLEN,
+ .qlen = GZERO_QLEN,
};
/*-------------------------------------------------------------------------*/
diff --git a/drivers/usb/musb/Kconfig b/drivers/usb/musb/Kconfig
index 57dfc0cedb00..688dc8bb192d 100644
--- a/drivers/usb/musb/Kconfig
+++ b/drivers/usb/musb/Kconfig
@@ -6,7 +6,7 @@
# (M)HDRC = (Multipoint) Highspeed Dual-Role Controller
config USB_MUSB_HDRC
tristate 'Inventra Highspeed Dual Role Controller (TI, ADI, ...)'
- depends on USB_GADGET
+ depends on (USB || USB_GADGET)
help
Say Y here if your system has a dual role high speed USB
controller based on the Mentor Graphics silicon IP. Then
@@ -35,21 +35,21 @@ choice
config USB_MUSB_HOST
bool "Host only mode"
- depends on USB
+ depends on USB=y || USB=USB_MUSB_HDRC
help
Select this when you want to use MUSB in host mode only,
thereby the gadget feature will be regressed.
config USB_MUSB_GADGET
bool "Gadget only mode"
- depends on USB_GADGET
+ depends on USB_GADGET=y || USB_GADGET=USB_MUSB_HDRC
help
Select this when you want to use MUSB in gadget mode only,
thereby the host feature will be regressed.
config USB_MUSB_DUAL_ROLE
bool "Dual Role mode"
- depends on (USB && USB_GADGET)
+ depends on ((USB=y || USB=USB_MUSB_HDRC) && (USB_GADGET=y || USB_GADGET=USB_MUSB_HDRC))
help
This is the default mode of working of MUSB controller where
both host and gadget features are enabled.
@@ -93,6 +93,12 @@ config USB_MUSB_BLACKFIN
config USB_MUSB_UX500
tristate "Ux500 platforms"
+config USB_MUSB_JZ4740
+ tristate "JZ4740"
+ depends on MACH_JZ4740 || COMPILE_TEST
+ depends on USB_MUSB_GADGET
+ depends on USB_OTG_BLACKLIST_HUB
+
endchoice
config USB_MUSB_AM335X_CHILD
@@ -100,7 +106,7 @@ config USB_MUSB_AM335X_CHILD
choice
prompt 'MUSB DMA mode'
- default MUSB_PIO_ONLY if ARCH_MULTIPLATFORM
+ default MUSB_PIO_ONLY if ARCH_MULTIPLATFORM || USB_MUSB_JZ4740
default USB_UX500_DMA if USB_MUSB_UX500
default USB_INVENTRA_DMA if USB_MUSB_OMAP2PLUS || USB_MUSB_BLACKFIN
default USB_TI_CPPI_DMA if USB_MUSB_DAVINCI
diff --git a/drivers/usb/musb/Makefile b/drivers/usb/musb/Makefile
index c5ea5c6dc169..ba495018b416 100644
--- a/drivers/usb/musb/Makefile
+++ b/drivers/usb/musb/Makefile
@@ -19,6 +19,7 @@ obj-$(CONFIG_USB_MUSB_DAVINCI) += davinci.o
obj-$(CONFIG_USB_MUSB_DA8XX) += da8xx.o
obj-$(CONFIG_USB_MUSB_BLACKFIN) += blackfin.o
obj-$(CONFIG_USB_MUSB_UX500) += ux500.o
+obj-$(CONFIG_USB_MUSB_JZ4740) += jz4740.o
obj-$(CONFIG_USB_MUSB_AM335X_CHILD) += musb_am335x.o
diff --git a/drivers/usb/musb/jz4740.c b/drivers/usb/musb/jz4740.c
new file mode 100644
index 000000000000..5f30537f1927
--- /dev/null
+++ b/drivers/usb/musb/jz4740.c
@@ -0,0 +1,201 @@
+/*
+ * Ingenic JZ4740 "glue layer"
+ *
+ * Copyright (C) 2013, Apelete Seketeli <apelete@seketeli.net>
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/clk.h>
+#include <linux/dma-mapping.h>
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+
+#include "musb_core.h"
+
+struct jz4740_glue {
+ struct device *dev;
+ struct platform_device *musb;
+ struct clk *clk;
+};
+
+static irqreturn_t jz4740_musb_interrupt(int irq, void *__hci)
+{
+ unsigned long flags;
+ irqreturn_t retval = IRQ_NONE;
+ struct musb *musb = __hci;
+
+ spin_lock_irqsave(&musb->lock, flags);
+
+ musb->int_usb = musb_readb(musb->mregs, MUSB_INTRUSB);
+ musb->int_tx = musb_readw(musb->mregs, MUSB_INTRTX);
+ musb->int_rx = musb_readw(musb->mregs, MUSB_INTRRX);
+
+ /*
+ * The controller is gadget only, the state of the host mode IRQ bits is
+ * undefined. Mask them to make sure that the musb driver core will
+ * never see them set
+ */
+ musb->int_usb &= MUSB_INTR_SUSPEND | MUSB_INTR_RESUME |
+ MUSB_INTR_RESET | MUSB_INTR_SOF;
+
+ if (musb->int_usb || musb->int_tx || musb->int_rx)
+ retval = musb_interrupt(musb);
+
+ spin_unlock_irqrestore(&musb->lock, flags);
+
+ return retval;
+}
+
+static struct musb_fifo_cfg jz4740_musb_fifo_cfg[] = {
+{ .hw_ep_num = 1, .style = FIFO_TX, .maxpacket = 512, },
+{ .hw_ep_num = 1, .style = FIFO_RX, .maxpacket = 512, },
+{ .hw_ep_num = 2, .style = FIFO_TX, .maxpacket = 64, },
+};
+
+static struct musb_hdrc_config jz4740_musb_config = {
+ /* Silicon does not implement USB OTG. */
+ .multipoint = 0,
+ /* Max EPs scanned, driver will decide which EP can be used. */
+ .num_eps = 4,
+ /* RAMbits needed to configure EPs from table */
+ .ram_bits = 9,
+ .fifo_cfg = jz4740_musb_fifo_cfg,
+ .fifo_cfg_size = ARRAY_SIZE(jz4740_musb_fifo_cfg),
+};
+
+static struct musb_hdrc_platform_data jz4740_musb_platform_data = {
+ .mode = MUSB_PERIPHERAL,
+ .config = &jz4740_musb_config,
+};
+
+static int jz4740_musb_init(struct musb *musb)
+{
+ musb->xceiv = usb_get_phy(USB_PHY_TYPE_USB2);
+ if (!musb->xceiv) {
+ pr_err("HS UDC: no transceiver configured\n");
+ return -ENODEV;
+ }
+
+ /* Silicon does not implement ConfigData register.
+ * Set dyn_fifo to avoid reading EP config from hardware.
+ */
+ musb->dyn_fifo = true;
+
+ musb->isr = jz4740_musb_interrupt;
+
+ return 0;
+}
+
+static int jz4740_musb_exit(struct musb *musb)
+{
+ usb_put_phy(musb->xceiv);
+
+ return 0;
+}
+
+static const struct musb_platform_ops jz4740_musb_ops = {
+ .init = jz4740_musb_init,
+ .exit = jz4740_musb_exit,
+};
+
+static int jz4740_probe(struct platform_device *pdev)
+{
+ struct musb_hdrc_platform_data *pdata = &jz4740_musb_platform_data;
+ struct platform_device *musb;
+ struct jz4740_glue *glue;
+ struct clk *clk;
+ int ret;
+
+ glue = devm_kzalloc(&pdev->dev, sizeof(*glue), GFP_KERNEL);
+ if (!glue)
+ return -ENOMEM;
+
+ musb = platform_device_alloc("musb-hdrc", PLATFORM_DEVID_AUTO);
+ if (!musb) {
+ dev_err(&pdev->dev, "failed to allocate musb device\n");
+ return -ENOMEM;
+ }
+
+ clk = devm_clk_get(&pdev->dev, "udc");
+ if (IS_ERR(clk)) {
+ dev_err(&pdev->dev, "failed to get clock\n");
+ ret = PTR_ERR(clk);
+ goto err_platform_device_put;
+ }
+
+ ret = clk_prepare_enable(clk);
+ if (ret) {
+ dev_err(&pdev->dev, "failed to enable clock\n");
+ goto err_platform_device_put;
+ }
+
+ musb->dev.parent = &pdev->dev;
+
+ glue->dev = &pdev->dev;
+ glue->musb = musb;
+ glue->clk = clk;
+
+ pdata->platform_ops = &jz4740_musb_ops;
+
+ platform_set_drvdata(pdev, glue);
+
+ ret = platform_device_add_resources(musb, pdev->resource,
+ pdev->num_resources);
+ if (ret) {
+ dev_err(&pdev->dev, "failed to add resources\n");
+ goto err_clk_disable;
+ }
+
+ ret = platform_device_add_data(musb, pdata, sizeof(*pdata));
+ if (ret) {
+ dev_err(&pdev->dev, "failed to add platform_data\n");
+ goto err_clk_disable;
+ }
+
+ ret = platform_device_add(musb);
+ if (ret) {
+ dev_err(&pdev->dev, "failed to register musb device\n");
+ goto err_clk_disable;
+ }
+
+ return 0;
+
+err_clk_disable:
+ clk_disable_unprepare(clk);
+err_platform_device_put:
+ platform_device_put(musb);
+ return ret;
+}
+
+static int jz4740_remove(struct platform_device *pdev)
+{
+ struct jz4740_glue *glue = platform_get_drvdata(pdev);
+
+ platform_device_unregister(glue->musb);
+ clk_disable_unprepare(glue->clk);
+
+ return 0;
+}
+
+static struct platform_driver jz4740_driver = {
+ .probe = jz4740_probe,
+ .remove = jz4740_remove,
+ .driver = {
+ .name = "musb-jz4740",
+ },
+};
+
+MODULE_DESCRIPTION("JZ4740 MUSB Glue Layer");
+MODULE_AUTHOR("Apelete Seketeli <apelete@seketeli.net>");
+MODULE_LICENSE("GPL v2");
+module_platform_driver(jz4740_driver);
diff --git a/drivers/usb/musb/musb_core.c b/drivers/usb/musb/musb_core.c
index 4d4499b80449..0e7f4a00ca7b 100644
--- a/drivers/usb/musb/musb_core.c
+++ b/drivers/usb/musb/musb_core.c
@@ -478,8 +478,8 @@ static irqreturn_t musb_stage0_irq(struct musb *musb, u8 int_usb,
musb->port1_status |=
(USB_PORT_STAT_C_SUSPEND << 16)
| MUSB_PORT_STAT_RESUME;
- musb->rh_timer = jiffies
- + msecs_to_jiffies(20);
+ schedule_delayed_work(
+ &musb->finish_resume_work, 20);
musb->xceiv->state = OTG_STATE_A_HOST;
musb->is_active = 1;
@@ -1813,6 +1813,21 @@ static void musb_free(struct musb *musb)
musb_host_free(musb);
}
+static void musb_deassert_reset(struct work_struct *work)
+{
+ struct musb *musb;
+ unsigned long flags;
+
+ musb = container_of(work, struct musb, deassert_reset_work.work);
+
+ spin_lock_irqsave(&musb->lock, flags);
+
+ if (musb->port1_status & USB_PORT_STAT_RESET)
+ musb_port_reset(musb, false);
+
+ spin_unlock_irqrestore(&musb->lock, flags);
+}
+
/*
* Perform generic per-controller initialization.
*
@@ -1897,6 +1912,8 @@ 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_DELAYED_WORK(&musb->deassert_reset_work, musb_deassert_reset);
+ INIT_DELAYED_WORK(&musb->finish_resume_work, musb_host_finish_resume);
/* setup musb parts of the core (especially endpoints) */
status = musb_core_init(plat->config->multipoint
@@ -1940,17 +1957,26 @@ musb_init_controller(struct device *dev, int nIrq, void __iomem *ctrl)
switch (musb->port_mode) {
case MUSB_PORT_MODE_HOST:
status = musb_host_setup(musb, plat->power);
+ if (status < 0)
+ goto fail3;
+ status = musb_platform_set_mode(musb, MUSB_HOST);
break;
case MUSB_PORT_MODE_GADGET:
status = musb_gadget_setup(musb);
+ if (status < 0)
+ goto fail3;
+ status = musb_platform_set_mode(musb, MUSB_PERIPHERAL);
break;
case MUSB_PORT_MODE_DUAL_ROLE:
status = musb_host_setup(musb, plat->power);
if (status < 0)
goto fail3;
status = musb_gadget_setup(musb);
- if (status)
+ if (status) {
musb_host_cleanup(musb);
+ goto fail3;
+ }
+ status = musb_platform_set_mode(musb, MUSB_OTG);
break;
default:
dev_err(dev, "unsupported port mode %d\n", musb->port_mode);
@@ -1981,6 +2007,8 @@ fail4:
fail3:
cancel_work_sync(&musb->irq_work);
+ cancel_delayed_work_sync(&musb->finish_resume_work);
+ cancel_delayed_work_sync(&musb->deassert_reset_work);
if (musb->dma_controller)
dma_controller_destroy(musb->dma_controller);
fail2_5:
@@ -2044,6 +2072,8 @@ static int musb_remove(struct platform_device *pdev)
dma_controller_destroy(musb->dma_controller);
cancel_work_sync(&musb->irq_work);
+ cancel_delayed_work_sync(&musb->finish_resume_work);
+ cancel_delayed_work_sync(&musb->deassert_reset_work);
musb_free(musb);
device_init_wakeup(dev, 0);
return 0;
@@ -2216,16 +2246,28 @@ static int musb_suspend(struct device *dev)
*/
}
+ musb_save_context(musb);
+
spin_unlock_irqrestore(&musb->lock, flags);
return 0;
}
static int musb_resume_noirq(struct device *dev)
{
- /* for static cmos like DaVinci, register values were preserved
+ struct musb *musb = dev_to_musb(dev);
+
+ /*
+ * For static cmos like DaVinci, register values were preserved
* unless for some reason the whole soc powered down or the USB
* module got reset through the PSC (vs just being disabled).
+ *
+ * For the DSPS glue layer though, a full register restore has to
+ * be done. As it shouldn't harm other platforms, we do it
+ * unconditionally.
*/
+
+ musb_restore_context(musb);
+
return 0;
}
diff --git a/drivers/usb/musb/musb_core.h b/drivers/usb/musb/musb_core.h
index 29f7cd7c7964..7083e82776ff 100644
--- a/drivers/usb/musb/musb_core.h
+++ b/drivers/usb/musb/musb_core.h
@@ -47,6 +47,7 @@
#include <linux/usb/otg.h>
#include <linux/usb/musb.h>
#include <linux/phy/phy.h>
+#include <linux/workqueue.h>
struct musb;
struct musb_hw_ep;
@@ -295,6 +296,8 @@ struct musb {
irqreturn_t (*isr)(int, void *);
struct work_struct irq_work;
+ struct delayed_work deassert_reset_work;
+ struct delayed_work finish_resume_work;
u16 hwvers;
u16 intrrxe;
diff --git a/drivers/usb/musb/musb_dsps.c b/drivers/usb/musb/musb_dsps.c
index 1901f6fe5807..593d3c962565 100644
--- a/drivers/usb/musb/musb_dsps.c
+++ b/drivers/usb/musb/musb_dsps.c
@@ -83,6 +83,8 @@ struct dsps_musb_wrapper {
u16 coreintr_status;
u16 phy_utmi;
u16 mode;
+ u16 tx_mode;
+ u16 rx_mode;
/* bit positions for control */
unsigned reset:5;
@@ -106,10 +108,24 @@ struct dsps_musb_wrapper {
/* bit positions for mode */
unsigned iddig:5;
+ unsigned iddig_mux:5;
/* miscellaneous stuff */
u8 poll_seconds;
};
+/*
+ * register shadow for suspend
+ */
+struct dsps_context {
+ u32 control;
+ u32 epintr;
+ u32 coreintr;
+ u32 phy_utmi;
+ u32 mode;
+ u32 tx_mode;
+ u32 rx_mode;
+};
+
/**
* DSPS glue structure.
*/
@@ -119,6 +135,8 @@ 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 */
+
+ struct dsps_context context;
};
static void dsps_musb_try_idle(struct musb *musb, unsigned long timeout)
@@ -341,8 +359,9 @@ static irqreturn_t dsps_interrupt(int irq, void *hci)
if (musb->int_tx || musb->int_rx || musb->int_usb)
ret |= musb_interrupt(musb);
- /* Poll for ID change */
- if (musb->xceiv->state == OTG_STATE_B_IDLE)
+ /* Poll for ID change in OTG port mode */
+ if (musb->xceiv->state == OTG_STATE_B_IDLE &&
+ musb->port_mode == MUSB_PORT_MODE_DUAL_ROLE)
mod_timer(&glue->timer, jiffies + wrp->poll_seconds * HZ);
out:
spin_unlock_irqrestore(&musb->lock, flags);
@@ -406,6 +425,54 @@ static int dsps_musb_exit(struct musb *musb)
return 0;
}
+static int dsps_musb_set_mode(struct musb *musb, u8 mode)
+{
+ struct device *dev = musb->controller;
+ struct dsps_glue *glue = dev_get_drvdata(dev->parent);
+ const struct dsps_musb_wrapper *wrp = glue->wrp;
+ void __iomem *ctrl_base = musb->ctrl_base;
+ void __iomem *base = musb->mregs;
+ u32 reg;
+
+ reg = dsps_readl(base, wrp->mode);
+
+ switch (mode) {
+ case MUSB_HOST:
+ reg &= ~(1 << wrp->iddig);
+
+ /*
+ * if we're setting mode to host-only or device-only, we're
+ * going to ignore whatever the PHY sends us and just force
+ * ID pin status by SW
+ */
+ reg |= (1 << wrp->iddig_mux);
+
+ dsps_writel(base, wrp->mode, reg);
+ dsps_writel(ctrl_base, wrp->phy_utmi, 0x02);
+ break;
+ case MUSB_PERIPHERAL:
+ reg |= (1 << wrp->iddig);
+
+ /*
+ * if we're setting mode to host-only or device-only, we're
+ * going to ignore whatever the PHY sends us and just force
+ * ID pin status by SW
+ */
+ reg |= (1 << wrp->iddig_mux);
+
+ dsps_writel(base, wrp->mode, reg);
+ break;
+ case MUSB_OTG:
+ dsps_writel(base, wrp->phy_utmi, 0x02);
+ break;
+ default:
+ dev_err(glue->dev, "unsupported mode %d\n", mode);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
static struct musb_platform_ops dsps_ops = {
.init = dsps_musb_init,
.exit = dsps_musb_exit,
@@ -414,6 +481,7 @@ static struct musb_platform_ops dsps_ops = {
.disable = dsps_musb_disable,
.try_idle = dsps_musb_try_idle,
+ .set_mode = dsps_musb_set_mode,
};
static u64 musb_dmamask = DMA_BIT_MASK(32);
@@ -507,6 +575,7 @@ static int dsps_create_musb_pdev(struct dsps_glue *glue,
config->num_eps = get_int_prop(dn, "mentor,num-eps");
config->ram_bits = get_int_prop(dn, "mentor,ram-bits");
+ config->host_port_deassert_reset_at_resume = 1;
pdata.mode = get_musb_port_mode(dev);
/* DT keeps this entry in mA, musb expects it as per USB spec */
pdata.power = get_int_prop(dn, "mentor,power") / 2;
@@ -605,9 +674,12 @@ static const struct dsps_musb_wrapper am33xx_driver_data = {
.coreintr_status = 0x34,
.phy_utmi = 0xe0,
.mode = 0xe8,
+ .tx_mode = 0x70,
+ .rx_mode = 0x74,
.reset = 0,
.otg_disable = 21,
.iddig = 8,
+ .iddig_mux = 7,
.usb_shift = 0,
.usb_mask = 0x1ff,
.usb_bitmap = (0x1ff << 0),
@@ -628,11 +700,52 @@ static const struct of_device_id musb_dsps_of_match[] = {
};
MODULE_DEVICE_TABLE(of, musb_dsps_of_match);
+#ifdef CONFIG_PM
+static int dsps_suspend(struct device *dev)
+{
+ struct dsps_glue *glue = dev_get_drvdata(dev);
+ const struct dsps_musb_wrapper *wrp = glue->wrp;
+ struct musb *musb = platform_get_drvdata(glue->musb);
+ void __iomem *mbase = musb->ctrl_base;
+
+ glue->context.control = dsps_readl(mbase, wrp->control);
+ glue->context.epintr = dsps_readl(mbase, wrp->epintr_set);
+ glue->context.coreintr = dsps_readl(mbase, wrp->coreintr_set);
+ glue->context.phy_utmi = dsps_readl(mbase, wrp->phy_utmi);
+ glue->context.mode = dsps_readl(mbase, wrp->mode);
+ glue->context.tx_mode = dsps_readl(mbase, wrp->tx_mode);
+ glue->context.rx_mode = dsps_readl(mbase, wrp->rx_mode);
+
+ return 0;
+}
+
+static int dsps_resume(struct device *dev)
+{
+ struct dsps_glue *glue = dev_get_drvdata(dev);
+ const struct dsps_musb_wrapper *wrp = glue->wrp;
+ struct musb *musb = platform_get_drvdata(glue->musb);
+ void __iomem *mbase = musb->ctrl_base;
+
+ dsps_writel(mbase, wrp->control, glue->context.control);
+ dsps_writel(mbase, wrp->epintr_set, glue->context.epintr);
+ dsps_writel(mbase, wrp->coreintr_set, glue->context.coreintr);
+ dsps_writel(mbase, wrp->phy_utmi, glue->context.phy_utmi);
+ dsps_writel(mbase, wrp->mode, glue->context.mode);
+ dsps_writel(mbase, wrp->tx_mode, glue->context.tx_mode);
+ dsps_writel(mbase, wrp->rx_mode, glue->context.rx_mode);
+
+ return 0;
+}
+#endif
+
+static SIMPLE_DEV_PM_OPS(dsps_pm_ops, dsps_suspend, dsps_resume);
+
static struct platform_driver dsps_usbss_driver = {
.probe = dsps_probe,
.remove = dsps_remove,
.driver = {
.name = "musb-dsps",
+ .pm = &dsps_pm_ops,
.of_match_table = musb_dsps_of_match,
},
};
diff --git a/drivers/usb/musb/musb_gadget.c b/drivers/usb/musb/musb_gadget.c
index 32fb057c03f5..d4aa779339f1 100644
--- a/drivers/usb/musb/musb_gadget.c
+++ b/drivers/usb/musb/musb_gadget.c
@@ -1727,14 +1727,14 @@ init_peripheral_ep(struct musb *musb, struct musb_ep *ep, u8 epnum, int is_in)
ep->end_point.name = ep->name;
INIT_LIST_HEAD(&ep->end_point.ep_list);
if (!epnum) {
- ep->end_point.maxpacket = 64;
+ usb_ep_set_maxpacket_limit(&ep->end_point, 64);
ep->end_point.ops = &musb_g_ep0_ops;
musb->g.ep0 = &ep->end_point;
} else {
if (is_in)
- ep->end_point.maxpacket = hw_ep->max_packet_sz_tx;
+ usb_ep_set_maxpacket_limit(&ep->end_point, hw_ep->max_packet_sz_tx);
else
- ep->end_point.maxpacket = hw_ep->max_packet_sz_rx;
+ usb_ep_set_maxpacket_limit(&ep->end_point, hw_ep->max_packet_sz_rx);
ep->end_point.ops = &musb_ep_ops;
list_add_tail(&ep->end_point.ep_list, &musb->g.ep_list);
}
@@ -2119,7 +2119,15 @@ __acquires(musb->lock)
/* Normal reset, as B-Device;
* or else after HNP, as A-Device
*/
- if (devctl & MUSB_DEVCTL_BDEVICE) {
+ if (!musb->g.is_otg) {
+ /* USB device controllers that are not OTG compatible
+ * may not have DEVCTL register in silicon.
+ * In that case, do not rely on devctl for setting
+ * peripheral mode.
+ */
+ musb->xceiv->state = OTG_STATE_B_PERIPHERAL;
+ musb->g.is_a_peripheral = 0;
+ } else if (devctl & MUSB_DEVCTL_BDEVICE) {
musb->xceiv->state = OTG_STATE_B_PERIPHERAL;
musb->g.is_a_peripheral = 0;
} else {
diff --git a/drivers/usb/musb/musb_host.c b/drivers/usb/musb/musb_host.c
index 6582a20bec05..822413899260 100644
--- a/drivers/usb/musb/musb_host.c
+++ b/drivers/usb/musb/musb_host.c
@@ -2433,6 +2433,8 @@ static int musb_bus_suspend(struct usb_hcd *hcd)
struct musb *musb = hcd_to_musb(hcd);
u8 devctl;
+ musb_port_suspend(musb, true);
+
if (!is_host_active(musb))
return 0;
@@ -2462,7 +2464,12 @@ static int musb_bus_suspend(struct usb_hcd *hcd)
static int musb_bus_resume(struct usb_hcd *hcd)
{
- /* resuming child port does the work */
+ struct musb *musb = hcd_to_musb(hcd);
+
+ if (musb->config &&
+ musb->config->host_port_deassert_reset_at_resume)
+ musb_port_reset(musb, false);
+
return 0;
}
diff --git a/drivers/usb/musb/musb_host.h b/drivers/usb/musb/musb_host.h
index 960d73570b2f..7bbf01bf4bb0 100644
--- a/drivers/usb/musb/musb_host.h
+++ b/drivers/usb/musb/musb_host.h
@@ -92,6 +92,9 @@ extern void musb_host_rx(struct musb *, u8);
extern void musb_root_disconnect(struct musb *musb);
extern void musb_host_resume_root_hub(struct musb *musb);
extern void musb_host_poke_root_hub(struct musb *musb);
+extern void musb_port_suspend(struct musb *musb, bool do_suspend);
+extern void musb_port_reset(struct musb *musb, bool do_reset);
+extern void musb_host_finish_resume(struct work_struct *work);
#else
static inline struct musb *hcd_to_musb(struct usb_hcd *hcd)
{
@@ -121,6 +124,9 @@ static inline void musb_root_disconnect(struct musb *musb) {}
static inline void musb_host_resume_root_hub(struct musb *musb) {}
static inline void musb_host_poll_rh_status(struct musb *musb) {}
static inline void musb_host_poke_root_hub(struct musb *musb) {}
+static inline void musb_port_suspend(struct musb *musb, bool do_suspend) {}
+static inline void musb_port_reset(struct musb *musb, bool do_reset) {}
+static inline void musb_host_finish_resume(struct work_struct *work) {}
#endif
struct usb_hcd;
diff --git a/drivers/usb/musb/musb_virthub.c b/drivers/usb/musb/musb_virthub.c
index 9af6bba5eac9..966cf95bb453 100644
--- a/drivers/usb/musb/musb_virthub.c
+++ b/drivers/usb/musb/musb_virthub.c
@@ -44,7 +44,38 @@
#include "musb_core.h"
-static void musb_port_suspend(struct musb *musb, bool do_suspend)
+void musb_host_finish_resume(struct work_struct *work)
+{
+ struct musb *musb;
+ unsigned long flags;
+ u8 power;
+
+ musb = container_of(work, struct musb, finish_resume_work.work);
+
+ spin_lock_irqsave(&musb->lock, flags);
+
+ power = musb_readb(musb->mregs, MUSB_POWER);
+ power &= ~MUSB_POWER_RESUME;
+ dev_dbg(musb->controller, "root port resume stopped, power %02x\n",
+ power);
+ musb_writeb(musb->mregs, MUSB_POWER, power);
+
+ /*
+ * ISSUE: DaVinci (RTL 1.300) disconnects after
+ * resume of high speed peripherals (but not full
+ * speed ones).
+ */
+ musb->is_active = 1;
+ musb->port1_status &= ~(USB_PORT_STAT_SUSPEND | MUSB_PORT_STAT_RESUME);
+ musb->port1_status |= USB_PORT_STAT_C_SUSPEND << 16;
+ usb_hcd_poll_rh_status(musb->hcd);
+ /* NOTE: it might really be A_WAIT_BCON ... */
+ musb->xceiv->state = OTG_STATE_A_HOST;
+
+ spin_unlock_irqrestore(&musb->lock, flags);
+}
+
+void musb_port_suspend(struct musb *musb, bool do_suspend)
{
struct usb_otg *otg = musb->xceiv->otg;
u8 power;
@@ -105,11 +136,11 @@ static void musb_port_suspend(struct musb *musb, bool do_suspend)
/* later, GetPortStatus will stop RESUME signaling */
musb->port1_status |= MUSB_PORT_STAT_RESUME;
- musb->rh_timer = jiffies + msecs_to_jiffies(20);
+ schedule_delayed_work(&musb->finish_resume_work, 20);
}
}
-static void musb_port_reset(struct musb *musb, bool do_reset)
+void musb_port_reset(struct musb *musb, bool do_reset)
{
u8 power;
void __iomem *mbase = musb->mregs;
@@ -150,7 +181,7 @@ static void musb_port_reset(struct musb *musb, bool do_reset)
musb->port1_status |= USB_PORT_STAT_RESET;
musb->port1_status &= ~USB_PORT_STAT_ENABLE;
- musb->rh_timer = jiffies + msecs_to_jiffies(50);
+ schedule_delayed_work(&musb->deassert_reset_work, 50);
} else {
dev_dbg(musb->controller, "root port reset stopped\n");
musb_writeb(mbase, MUSB_POWER,
@@ -325,36 +356,6 @@ int musb_hub_control(
if (wIndex != 1)
goto error;
- /* finish RESET signaling? */
- if ((musb->port1_status & USB_PORT_STAT_RESET)
- && time_after_eq(jiffies, musb->rh_timer))
- musb_port_reset(musb, false);
-
- /* finish RESUME signaling? */
- if ((musb->port1_status & MUSB_PORT_STAT_RESUME)
- && time_after_eq(jiffies, musb->rh_timer)) {
- u8 power;
-
- power = musb_readb(musb->mregs, MUSB_POWER);
- power &= ~MUSB_POWER_RESUME;
- dev_dbg(musb->controller, "root port resume stopped, power %02x\n",
- power);
- musb_writeb(musb->mregs, MUSB_POWER, power);
-
- /* ISSUE: DaVinci (RTL 1.300) disconnects after
- * resume of high speed peripherals (but not full
- * speed ones).
- */
-
- musb->is_active = 1;
- musb->port1_status &= ~(USB_PORT_STAT_SUSPEND
- | MUSB_PORT_STAT_RESUME);
- musb->port1_status |= USB_PORT_STAT_C_SUSPEND << 16;
- usb_hcd_poll_rh_status(musb->hcd);
- /* NOTE: it might really be A_WAIT_BCON ... */
- musb->xceiv->state = OTG_STATE_A_HOST;
- }
-
put_unaligned(cpu_to_le32(musb->port1_status
& ~MUSB_PORT_STAT_RESUME),
(__le32 *) buf);
diff --git a/drivers/usb/musb/ux500_dma.c b/drivers/usb/musb/ux500_dma.c
index 3700e9713258..9aad00f11bd5 100644
--- a/drivers/usb/musb/ux500_dma.c
+++ b/drivers/usb/musb/ux500_dma.c
@@ -336,7 +336,9 @@ static int ux500_dma_controller_start(struct ux500_dma_controller *controller)
data ?
data->dma_filter :
NULL,
- param_array[ch_num]);
+ param_array ?
+ param_array[ch_num] :
+ NULL);
if (!ux500_channel->dma_chan) {
ERR("Dma pipe allocation error dir=%d ch=%d\n",
diff --git a/drivers/usb/phy/Kconfig b/drivers/usb/phy/Kconfig
index 2b41c636a52a..7d1451d5bbea 100644
--- a/drivers/usb/phy/Kconfig
+++ b/drivers/usb/phy/Kconfig
@@ -6,6 +6,15 @@ menu "USB Physical Layer drivers"
config USB_PHY
def_bool n
+config USB_OTG_FSM
+ tristate "USB 2.0 OTG FSM implementation"
+ depends on USB
+ select USB_OTG
+ select USB_PHY
+ help
+ Implements OTG Final State Machine as specified in On-The-Go
+ and Embedded Host Supplement to the USB Revision 2.0 Specification.
+
#
# USB Transceiver Drivers
#
@@ -19,9 +28,8 @@ config AB8500_USB
in host mode, low speed.
config FSL_USB2_OTG
- tristate "Freescale USB OTG Transceiver Driver"
- depends on USB_EHCI_FSL && USB_FSL_USB2 && PM_RUNTIME
- depends on USB
+ bool "Freescale USB OTG Transceiver Driver"
+ depends on USB_EHCI_FSL && USB_FSL_USB2 && USB_OTG_FSM && PM_RUNTIME
select USB_OTG
select USB_PHY
help
@@ -40,7 +48,16 @@ config ISP1301_OMAP
Instruments OMAP processors.
This driver can also be built as a module. If so, the module
- will be called isp1301_omap.
+ will be called phy-isp1301-omap.
+
+config KEYSTONE_USB_PHY
+ tristate "Keystone USB PHY Driver"
+ depends on ARCH_KEYSTONE || COMPILE_TEST
+ select NOP_USB_XCEIV
+ help
+ Enable this to support Keystone USB phy. This driver provides
+ interface to interact with USB 2.0 and USB 3.0 PHY that is part
+ of the Keystone SOC.
config MV_U3D_PHY
bool "Marvell USB 3.0 PHY controller Driver"
@@ -136,6 +153,31 @@ config USB_GPIO_VBUS
optionally control of a D+ pullup GPIO as well as a VBUS
current limit regulator.
+config OMAP_OTG
+ tristate "OMAP USB OTG controller driver"
+ depends on ARCH_OMAP_OTG && EXTCON
+ help
+ Enable this to support some transceivers on OMAP1 platforms. OTG
+ controller is needed to switch between host and peripheral modes.
+
+ This driver can also be built as a module. If so, the module
+ will be called phy-omap-otg.
+
+config TAHVO_USB
+ tristate "Tahvo USB transceiver driver"
+ depends on MFD_RETU && EXTCON
+ select USB_PHY
+ help
+ Enable this to support USB transceiver on Tahvo. This is used
+ at least on Nokia 770.
+
+config TAHVO_USB_HOST_BY_DEFAULT
+ depends on TAHVO_USB
+ boolean "Device in USB host mode by default"
+ help
+ Say Y here, if you want the device to enter USB host mode
+ by default on bootup.
+
config USB_ISP1301
tristate "NXP ISP1301 USB transceiver support"
depends on USB || USB_GADGET
@@ -147,7 +189,7 @@ config USB_ISP1301
and OTG drivers (to be selected separately).
To compile this driver as a module, choose M here: the
- module will be called isp1301.
+ module will be called phy-isp1301.
config USB_MSM_OTG
tristate "OTG support for Qualcomm on-chip USB controller"
diff --git a/drivers/usb/phy/Makefile b/drivers/usb/phy/Makefile
index 022c1da7fb78..be58adae3496 100644
--- a/drivers/usb/phy/Makefile
+++ b/drivers/usb/phy/Makefile
@@ -3,18 +3,20 @@
#
obj-$(CONFIG_USB_PHY) += phy.o
obj-$(CONFIG_OF) += of.o
+obj-$(CONFIG_USB_OTG_FSM) += phy-fsm-usb.o
# transceiver drivers, keep the list sorted
obj-$(CONFIG_AB8500_USB) += phy-ab8500-usb.o
-phy-fsl-usb2-objs := phy-fsl-usb.o phy-fsm-usb.o
-obj-$(CONFIG_FSL_USB2_OTG) += phy-fsl-usb2.o
+obj-$(CONFIG_FSL_USB2_OTG) += phy-fsl-usb.o
obj-$(CONFIG_ISP1301_OMAP) += phy-isp1301-omap.o
obj-$(CONFIG_MV_U3D_PHY) += phy-mv-u3d-usb.o
obj-$(CONFIG_NOP_USB_XCEIV) += phy-generic.o
+obj-$(CONFIG_TAHVO_USB) += phy-tahvo.o
obj-$(CONFIG_OMAP_CONTROL_USB) += phy-omap-control.o
obj-$(CONFIG_AM335X_CONTROL_USB) += phy-am335x-control.o
obj-$(CONFIG_AM335X_PHY_USB) += phy-am335x.o
+obj-$(CONFIG_OMAP_OTG) += phy-omap-otg.o
obj-$(CONFIG_OMAP_USB3) += phy-omap-usb3.o
obj-$(CONFIG_SAMSUNG_USBPHY) += phy-samsung-usb.o
obj-$(CONFIG_SAMSUNG_USB2PHY) += phy-samsung-usb2.o
@@ -30,3 +32,4 @@ obj-$(CONFIG_USB_RCAR_PHY) += phy-rcar-usb.o
obj-$(CONFIG_USB_RCAR_GEN2_PHY) += phy-rcar-gen2-usb.o
obj-$(CONFIG_USB_ULPI) += phy-ulpi.o
obj-$(CONFIG_USB_ULPI_VIEWPORT) += phy-ulpi-viewport.o
+obj-$(CONFIG_KEYSTONE_USB_PHY) += phy-keystone.o
diff --git a/drivers/usb/phy/phy-ab8500-usb.c b/drivers/usb/phy/phy-ab8500-usb.c
index 087402350b6d..11ab2c45e462 100644
--- a/drivers/usb/phy/phy-ab8500-usb.c
+++ b/drivers/usb/phy/phy-ab8500-usb.c
@@ -1415,8 +1415,6 @@ static int ab8500_usb_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, ab);
- ATOMIC_INIT_NOTIFIER_HEAD(&ab->phy.notifier);
-
/* all: Disable phy when called from set_host and set_peripheral */
INIT_WORK(&ab->phy_dis_work, ab8500_usb_phy_disable_work);
diff --git a/drivers/usb/phy/phy-am335x-control.c b/drivers/usb/phy/phy-am335x-control.c
index 634f49acd20e..d75196ad5f2f 100644
--- a/drivers/usb/phy/phy-am335x-control.c
+++ b/drivers/usb/phy/phy-am335x-control.c
@@ -3,11 +3,7 @@
#include <linux/err.h>
#include <linux/of.h>
#include <linux/io.h>
-
-struct phy_control {
- void (*phy_power)(struct phy_control *phy_ctrl, u32 id, bool on);
- void (*phy_wkup)(struct phy_control *phy_ctrl, u32 id, bool on);
-};
+#include "am35x-phy-control.h"
struct am335x_control_usb {
struct device *dev;
diff --git a/drivers/usb/phy/phy-am335x.c b/drivers/usb/phy/phy-am335x.c
index 0e3c60cb669a..12fc3468a01e 100644
--- a/drivers/usb/phy/phy-am335x.c
+++ b/drivers/usb/phy/phy-am335x.c
@@ -63,6 +63,19 @@ static int am335x_phy_probe(struct platform_device *pdev)
am_phy->usb_phy_gen.phy.shutdown = am335x_shutdown;
platform_set_drvdata(pdev, am_phy);
+ device_init_wakeup(dev, true);
+
+ /*
+ * If we leave PHY wakeup enabled then AM33XX wakes up
+ * immediately from DS0. To avoid this we mark dev->power.can_wakeup
+ * to false. The same is checked in suspend routine to decide
+ * on whether to enable PHY wakeup or not.
+ * PHY wakeup works fine in standby mode, there by allowing us to
+ * handle remote wakeup, wakeup on disconnect and connect.
+ */
+
+ device_set_wakeup_enable(dev, false);
+ phy_ctrl_power(am_phy->phy_ctrl, am_phy->id, false);
return 0;
}
@@ -75,38 +88,48 @@ static int am335x_phy_remove(struct platform_device *pdev)
return 0;
}
-#ifdef CONFIG_PM_RUNTIME
-
-static int am335x_phy_runtime_suspend(struct device *dev)
+#ifdef CONFIG_PM_SLEEP
+static int am335x_phy_suspend(struct device *dev)
{
struct platform_device *pdev = to_platform_device(dev);
struct am335x_phy *am_phy = platform_get_drvdata(pdev);
+ /*
+ * Enable phy wakeup only if dev->power.can_wakeup is true.
+ * Make sure to enable wakeup to support remote wakeup in
+ * standby mode ( same is not supported in OFF(DS0) mode).
+ * Enable it by doing
+ * echo enabled > /sys/bus/platform/devices/<usb-phy-id>/power/wakeup
+ */
+
if (device_may_wakeup(dev))
phy_ctrl_wkup(am_phy->phy_ctrl, am_phy->id, true);
+
phy_ctrl_power(am_phy->phy_ctrl, am_phy->id, false);
+
return 0;
}
-static int am335x_phy_runtime_resume(struct device *dev)
+static int am335x_phy_resume(struct device *dev)
{
struct platform_device *pdev = to_platform_device(dev);
struct am335x_phy *am_phy = platform_get_drvdata(pdev);
phy_ctrl_power(am_phy->phy_ctrl, am_phy->id, true);
+
if (device_may_wakeup(dev))
phy_ctrl_wkup(am_phy->phy_ctrl, am_phy->id, false);
+
return 0;
}
static const struct dev_pm_ops am335x_pm_ops = {
- SET_RUNTIME_PM_OPS(am335x_phy_runtime_suspend,
- am335x_phy_runtime_resume, NULL)
+ SET_SYSTEM_SLEEP_PM_OPS(am335x_phy_suspend, am335x_phy_resume)
};
-#define DEV_PM_OPS (&am335x_pm_ops)
+#define DEV_PM_OPS (&am335x_pm_ops)
#else
-#define DEV_PM_OPS NULL
+#define DEV_PM_OPS NULL
#endif
static const struct of_device_id am335x_phy_ids[] = {
diff --git a/drivers/usb/phy/phy-fsl-usb.c b/drivers/usb/phy/phy-fsl-usb.c
index 7f3c73b967ce..62d5af22efaf 100644
--- a/drivers/usb/phy/phy-fsl-usb.c
+++ b/drivers/usb/phy/phy-fsl-usb.c
@@ -848,7 +848,7 @@ static int fsl_otg_conf(struct platform_device *pdev)
pr_info("Couldn't init OTG timers\n");
goto err;
}
- spin_lock_init(&fsl_otg_tc->fsm.lock);
+ mutex_init(&fsl_otg_tc->fsm.lock);
/* Set OTG state machine operations */
fsl_otg_tc->fsm.ops = &fsl_otg_ops;
@@ -1017,10 +1017,9 @@ static int show_fsl_usb2_otg_state(struct device *dev,
struct otg_fsm *fsm = &fsl_otg_dev->fsm;
char *next = buf;
unsigned size = PAGE_SIZE;
- unsigned long flags;
int t;
- spin_lock_irqsave(&fsm->lock, flags);
+ mutex_lock(&fsm->lock);
/* basic driver infomation */
t = scnprintf(next, size,
@@ -1088,7 +1087,7 @@ static int show_fsl_usb2_otg_state(struct device *dev,
size -= t;
next += t;
- spin_unlock_irqrestore(&fsm->lock, flags);
+ mutex_unlock(&fsm->lock);
return PAGE_SIZE - size;
}
diff --git a/drivers/usb/phy/phy-fsl-usb.h b/drivers/usb/phy/phy-fsl-usb.h
index 7365170a2f23..5986c96354df 100644
--- a/drivers/usb/phy/phy-fsl-usb.h
+++ b/drivers/usb/phy/phy-fsl-usb.h
@@ -15,7 +15,7 @@
* 675 Mass Ave, Cambridge, MA 02139, USA.
*/
-#include "phy-fsm-usb.h"
+#include <linux/usb/otg-fsm.h>
#include <linux/usb/otg.h>
#include <linux/ioctl.h>
diff --git a/drivers/usb/phy/phy-fsm-usb.c b/drivers/usb/phy/phy-fsm-usb.c
index 329c2d2f8595..7aa314ef4a8a 100644
--- a/drivers/usb/phy/phy-fsm-usb.c
+++ b/drivers/usb/phy/phy-fsm-usb.c
@@ -23,13 +23,12 @@
#include <linux/kernel.h>
#include <linux/types.h>
-#include <linux/spinlock.h>
+#include <linux/mutex.h>
#include <linux/delay.h>
#include <linux/usb.h>
#include <linux/usb/gadget.h>
#include <linux/usb/otg.h>
-
-#include "phy-fsm-usb.h"
+#include <linux/usb/otg-fsm.h>
/* Change USB protocol when there is a protocol change */
static int otg_set_protocol(struct otg_fsm *fsm, int protocol)
@@ -65,7 +64,7 @@ static int otg_set_protocol(struct otg_fsm *fsm, int protocol)
static int state_changed;
/* Called when leaving a state. Do state clean up jobs here */
-void otg_leave_state(struct otg_fsm *fsm, enum usb_otg_state old_state)
+static void otg_leave_state(struct otg_fsm *fsm, enum usb_otg_state old_state)
{
switch (old_state) {
case OTG_STATE_B_IDLE:
@@ -122,7 +121,7 @@ void otg_leave_state(struct otg_fsm *fsm, enum usb_otg_state old_state)
}
/* Called when entering a state */
-int otg_set_state(struct otg_fsm *fsm, enum usb_otg_state new_state)
+static int otg_set_state(struct otg_fsm *fsm, enum usb_otg_state new_state)
{
state_changed = 1;
if (fsm->otg->phy->state == new_state)
@@ -245,9 +244,8 @@ int otg_set_state(struct otg_fsm *fsm, enum usb_otg_state new_state)
int otg_statemachine(struct otg_fsm *fsm)
{
enum usb_otg_state state;
- unsigned long flags;
- spin_lock_irqsave(&fsm->lock, flags);
+ mutex_lock(&fsm->lock);
state = fsm->otg->phy->state;
state_changed = 0;
@@ -359,7 +357,7 @@ int otg_statemachine(struct otg_fsm *fsm)
default:
break;
}
- spin_unlock_irqrestore(&fsm->lock, flags);
+ mutex_unlock(&fsm->lock);
VDBG("quit statemachine, changed = %d\n", state_changed);
return state_changed;
diff --git a/drivers/usb/phy/phy-fsm-usb.h b/drivers/usb/phy/phy-fsm-usb.h
deleted file mode 100644
index 7441b46a27f1..000000000000
--- a/drivers/usb/phy/phy-fsm-usb.h
+++ /dev/null
@@ -1,236 +0,0 @@
-/* Copyright (C) 2007,2008 Freescale Semiconductor, Inc.
- *
- * 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.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 675 Mass Ave, Cambridge, MA 02139, USA.
- */
-
-#undef VERBOSE
-
-#ifdef VERBOSE
-#define VDBG(fmt, args...) pr_debug("[%s] " fmt , \
- __func__, ## args)
-#else
-#define VDBG(stuff...) do {} while (0)
-#endif
-
-#ifdef VERBOSE
-#define MPC_LOC printk("Current Location [%s]:[%d]\n", __FILE__, __LINE__)
-#else
-#define MPC_LOC do {} while (0)
-#endif
-
-#define PROTO_UNDEF (0)
-#define PROTO_HOST (1)
-#define PROTO_GADGET (2)
-
-enum otg_fsm_timer {
- /* Standard OTG timers */
- A_WAIT_VRISE,
- A_WAIT_VFALL,
- A_WAIT_BCON,
- A_AIDL_BDIS,
- B_ASE0_BRST,
- A_BIDL_ADIS,
-
- /* Auxiliary timers */
- B_SE0_SRP,
- B_SRP_FAIL,
- A_WAIT_ENUM,
-
- NUM_OTG_FSM_TIMERS,
-};
-
-/* OTG state machine according to the OTG spec */
-struct otg_fsm {
- /* Input */
- int id;
- int adp_change;
- int power_up;
- int test_device;
- int a_bus_drop;
- int a_bus_req;
- int a_srp_det;
- int a_vbus_vld;
- int b_conn;
- int a_bus_resume;
- int a_bus_suspend;
- int a_conn;
- int b_bus_req;
- int b_se0_srp;
- int b_ssend_srp;
- int b_sess_vld;
- /* Auxilary inputs */
- int a_sess_vld;
- int b_bus_resume;
- int b_bus_suspend;
-
- /* Output */
- int data_pulse;
- int drv_vbus;
- int loc_conn;
- int loc_sof;
- int adp_prb;
- int adp_sns;
-
- /* Internal variables */
- int a_set_b_hnp_en;
- int b_srp_done;
- int b_hnp_enable;
- int a_clr_err;
-
- /* Informative variables */
- int a_bus_drop_inf;
- int a_bus_req_inf;
- int a_clr_err_inf;
- int b_bus_req_inf;
- /* Auxilary informative variables */
- int a_suspend_req_inf;
-
- /* Timeout indicator for timers */
- int a_wait_vrise_tmout;
- int a_wait_vfall_tmout;
- int a_wait_bcon_tmout;
- int a_aidl_bdis_tmout;
- int b_ase0_brst_tmout;
- int a_bidl_adis_tmout;
-
- struct otg_fsm_ops *ops;
- struct usb_otg *otg;
-
- /* Current usb protocol used: 0:undefine; 1:host; 2:client */
- int protocol;
- spinlock_t lock;
-};
-
-struct otg_fsm_ops {
- void (*chrg_vbus)(struct otg_fsm *fsm, int on);
- void (*drv_vbus)(struct otg_fsm *fsm, int on);
- void (*loc_conn)(struct otg_fsm *fsm, int on);
- void (*loc_sof)(struct otg_fsm *fsm, int on);
- void (*start_pulse)(struct otg_fsm *fsm);
- void (*start_adp_prb)(struct otg_fsm *fsm);
- void (*start_adp_sns)(struct otg_fsm *fsm);
- void (*add_timer)(struct otg_fsm *fsm, enum otg_fsm_timer timer);
- void (*del_timer)(struct otg_fsm *fsm, enum otg_fsm_timer timer);
- int (*start_host)(struct otg_fsm *fsm, int on);
- int (*start_gadget)(struct otg_fsm *fsm, int on);
-};
-
-
-static inline int otg_chrg_vbus(struct otg_fsm *fsm, int on)
-{
- if (!fsm->ops->chrg_vbus)
- return -EOPNOTSUPP;
- fsm->ops->chrg_vbus(fsm, on);
- return 0;
-}
-
-static inline int otg_drv_vbus(struct otg_fsm *fsm, int on)
-{
- if (!fsm->ops->drv_vbus)
- return -EOPNOTSUPP;
- if (fsm->drv_vbus != on) {
- fsm->drv_vbus = on;
- fsm->ops->drv_vbus(fsm, on);
- }
- return 0;
-}
-
-static inline int otg_loc_conn(struct otg_fsm *fsm, int on)
-{
- if (!fsm->ops->loc_conn)
- return -EOPNOTSUPP;
- if (fsm->loc_conn != on) {
- fsm->loc_conn = on;
- fsm->ops->loc_conn(fsm, on);
- }
- return 0;
-}
-
-static inline int otg_loc_sof(struct otg_fsm *fsm, int on)
-{
- if (!fsm->ops->loc_sof)
- return -EOPNOTSUPP;
- if (fsm->loc_sof != on) {
- fsm->loc_sof = on;
- fsm->ops->loc_sof(fsm, on);
- }
- return 0;
-}
-
-static inline int otg_start_pulse(struct otg_fsm *fsm)
-{
- if (!fsm->ops->start_pulse)
- return -EOPNOTSUPP;
- if (!fsm->data_pulse) {
- fsm->data_pulse = 1;
- fsm->ops->start_pulse(fsm);
- }
- return 0;
-}
-
-static inline int otg_start_adp_prb(struct otg_fsm *fsm)
-{
- if (!fsm->ops->start_adp_prb)
- return -EOPNOTSUPP;
- if (!fsm->adp_prb) {
- fsm->adp_sns = 0;
- fsm->adp_prb = 1;
- fsm->ops->start_adp_prb(fsm);
- }
- return 0;
-}
-
-static inline int otg_start_adp_sns(struct otg_fsm *fsm)
-{
- if (!fsm->ops->start_adp_sns)
- return -EOPNOTSUPP;
- if (!fsm->adp_sns) {
- fsm->adp_sns = 1;
- fsm->ops->start_adp_sns(fsm);
- }
- return 0;
-}
-
-static inline int otg_add_timer(struct otg_fsm *fsm, enum otg_fsm_timer timer)
-{
- if (!fsm->ops->add_timer)
- return -EOPNOTSUPP;
- fsm->ops->add_timer(fsm, timer);
- return 0;
-}
-
-static inline int otg_del_timer(struct otg_fsm *fsm, enum otg_fsm_timer timer)
-{
- if (!fsm->ops->del_timer)
- return -EOPNOTSUPP;
- fsm->ops->del_timer(fsm, timer);
- return 0;
-}
-
-static inline int otg_start_host(struct otg_fsm *fsm, int on)
-{
- if (!fsm->ops->start_host)
- return -EOPNOTSUPP;
- return fsm->ops->start_host(fsm, on);
-}
-
-static inline int otg_start_gadget(struct otg_fsm *fsm, int on)
-{
- if (!fsm->ops->start_gadget)
- return -EOPNOTSUPP;
- return fsm->ops->start_gadget(fsm, on);
-}
-
-int otg_statemachine(struct otg_fsm *fsm);
diff --git a/drivers/usb/phy/phy-generic.c b/drivers/usb/phy/phy-generic.c
index aa6d37b3378a..bb394980532b 100644
--- a/drivers/usb/phy/phy-generic.c
+++ b/drivers/usb/phy/phy-generic.c
@@ -241,7 +241,6 @@ int usb_phy_gen_create_phy(struct device *dev, struct usb_phy_gen_xceiv *nop,
nop->phy.otg->set_host = nop_set_host;
nop->phy.otg->set_peripheral = nop_set_peripheral;
- ATOMIC_INIT_NOTIFIER_HEAD(&nop->phy.notifier);
return 0;
}
EXPORT_SYMBOL_GPL(usb_phy_gen_create_phy);
diff --git a/drivers/usb/phy/phy-gpio-vbus-usb.c b/drivers/usb/phy/phy-gpio-vbus-usb.c
index 02799a5efcd4..69462e09d014 100644
--- a/drivers/usb/phy/phy-gpio-vbus-usb.c
+++ b/drivers/usb/phy/phy-gpio-vbus-usb.c
@@ -314,8 +314,6 @@ static int gpio_vbus_probe(struct platform_device *pdev)
goto err_irq;
}
- ATOMIC_INIT_NOTIFIER_HEAD(&gpio_vbus->phy.notifier);
-
INIT_DELAYED_WORK(&gpio_vbus->work, gpio_vbus_work);
gpio_vbus->vbus_draw = regulator_get(&pdev->dev, "vbus_draw");
diff --git a/drivers/usb/phy/phy-isp1301-omap.c b/drivers/usb/phy/phy-isp1301-omap.c
index d3a5160e4cc7..6e146d723b37 100644
--- a/drivers/usb/phy/phy-isp1301-omap.c
+++ b/drivers/usb/phy/phy-isp1301-omap.c
@@ -1277,7 +1277,7 @@ isp1301_set_host(struct usb_otg *otg, struct usb_bus *host)
{
struct isp1301 *isp = container_of(otg->phy, struct isp1301, phy);
- if (!otg || isp != the_transceiver)
+ if (isp != the_transceiver)
return -ENODEV;
if (!host) {
@@ -1333,7 +1333,7 @@ isp1301_set_peripheral(struct usb_otg *otg, struct usb_gadget *gadget)
{
struct isp1301 *isp = container_of(otg->phy, struct isp1301, phy);
- if (!otg || isp != the_transceiver)
+ if (isp != the_transceiver)
return -ENODEV;
if (!gadget) {
@@ -1414,8 +1414,7 @@ isp1301_start_srp(struct usb_otg *otg)
struct isp1301 *isp = container_of(otg->phy, struct isp1301, phy);
u32 otg_ctrl;
- if (!otg || isp != the_transceiver
- || isp->phy.state != OTG_STATE_B_IDLE)
+ if (isp != the_transceiver || isp->phy.state != OTG_STATE_B_IDLE)
return -ENODEV;
otg_ctrl = omap_readl(OTG_CTRL);
@@ -1442,7 +1441,7 @@ isp1301_start_hnp(struct usb_otg *otg)
struct isp1301 *isp = container_of(otg->phy, struct isp1301, phy);
u32 l;
- if (!otg || isp != the_transceiver)
+ if (isp != the_transceiver)
return -ENODEV;
if (otg->default_a && (otg->host == NULL || !otg->host->b_hnp_enable))
return -ENOTCONN;
diff --git a/drivers/usb/phy/phy-keystone.c b/drivers/usb/phy/phy-keystone.c
new file mode 100644
index 000000000000..ee1d03b802e1
--- /dev/null
+++ b/drivers/usb/phy/phy-keystone.c
@@ -0,0 +1,141 @@
+/*
+ * phy-keystone - USB PHY, talking to dwc3 controller in Keystone.
+ *
+ * Copyright (C) 2013 Texas Instruments Incorporated - http://www.ti.com
+ * 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.
+ *
+ * Author: WingMan Kwok <w-kwok2@ti.com>
+ *
+ * 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/module.h>
+#include <linux/platform_device.h>
+#include <linux/usb/usb_phy_gen_xceiv.h>
+#include <linux/io.h>
+#include <linux/of.h>
+
+#include "phy-generic.h"
+
+/* USB PHY control register offsets */
+#define USB_PHY_CTL_UTMI 0x0000
+#define USB_PHY_CTL_PIPE 0x0004
+#define USB_PHY_CTL_PARAM_1 0x0008
+#define USB_PHY_CTL_PARAM_2 0x000c
+#define USB_PHY_CTL_CLOCK 0x0010
+#define USB_PHY_CTL_PLL 0x0014
+
+#define PHY_REF_SSP_EN BIT(29)
+
+struct keystone_usbphy {
+ struct usb_phy_gen_xceiv usb_phy_gen;
+ void __iomem *phy_ctrl;
+};
+
+static inline u32 keystone_usbphy_readl(void __iomem *base, u32 offset)
+{
+ return readl(base + offset);
+}
+
+static inline void keystone_usbphy_writel(void __iomem *base,
+ u32 offset, u32 value)
+{
+ writel(value, base + offset);
+}
+
+static int keystone_usbphy_init(struct usb_phy *phy)
+{
+ struct keystone_usbphy *k_phy = dev_get_drvdata(phy->dev);
+ u32 val;
+
+ val = keystone_usbphy_readl(k_phy->phy_ctrl, USB_PHY_CTL_CLOCK);
+ keystone_usbphy_writel(k_phy->phy_ctrl, USB_PHY_CTL_CLOCK,
+ val | PHY_REF_SSP_EN);
+ return 0;
+}
+
+static void keystone_usbphy_shutdown(struct usb_phy *phy)
+{
+ struct keystone_usbphy *k_phy = dev_get_drvdata(phy->dev);
+ u32 val;
+
+ val = keystone_usbphy_readl(k_phy->phy_ctrl, USB_PHY_CTL_CLOCK);
+ keystone_usbphy_writel(k_phy->phy_ctrl, USB_PHY_CTL_CLOCK,
+ val &= ~PHY_REF_SSP_EN);
+}
+
+static int keystone_usbphy_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct keystone_usbphy *k_phy;
+ struct resource *res;
+ int ret;
+
+ k_phy = devm_kzalloc(dev, sizeof(*k_phy), GFP_KERNEL);
+ if (!k_phy)
+ return -ENOMEM;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res) {
+ dev_err(dev, "missing usb phy resource\n");
+ return -EINVAL;
+ }
+
+ k_phy->phy_ctrl = devm_ioremap_resource(dev, res);
+ if (IS_ERR(k_phy->phy_ctrl))
+ return PTR_ERR(k_phy->phy_ctrl);
+
+ ret = usb_phy_gen_create_phy(dev, &k_phy->usb_phy_gen, NULL);
+ if (ret)
+ return ret;
+
+ k_phy->usb_phy_gen.phy.init = keystone_usbphy_init;
+ k_phy->usb_phy_gen.phy.shutdown = keystone_usbphy_shutdown;
+
+ platform_set_drvdata(pdev, k_phy);
+
+ ret = usb_add_phy_dev(&k_phy->usb_phy_gen.phy);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+static int keystone_usbphy_remove(struct platform_device *pdev)
+{
+ struct keystone_usbphy *k_phy = platform_get_drvdata(pdev);
+
+ usb_remove_phy(&k_phy->usb_phy_gen.phy);
+
+ return 0;
+}
+
+static const struct of_device_id keystone_usbphy_ids[] = {
+ { .compatible = "ti,keystone-usbphy" },
+ { }
+};
+MODULE_DEVICE_TABLE(of, keystone_usbphy_ids);
+
+static struct platform_driver keystone_usbphy_driver = {
+ .probe = keystone_usbphy_probe,
+ .remove = keystone_usbphy_remove,
+ .driver = {
+ .name = "keystone-usbphy",
+ .owner = THIS_MODULE,
+ .of_match_table = keystone_usbphy_ids,
+ },
+};
+
+module_platform_driver(keystone_usbphy_driver);
+
+MODULE_ALIAS("platform:keystone-usbphy");
+MODULE_AUTHOR("Texas Instruments Inc.");
+MODULE_DESCRIPTION("Keystone USB phy driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/usb/phy/phy-mxs-usb.c b/drivers/usb/phy/phy-mxs-usb.c
index 545844b7e796..b42897b6474c 100644
--- a/drivers/usb/phy/phy-mxs-usb.c
+++ b/drivers/usb/phy/phy-mxs-usb.c
@@ -63,9 +63,13 @@ static int mxs_phy_hw_init(struct mxs_phy *mxs_phy)
static int mxs_phy_init(struct usb_phy *phy)
{
+ int ret;
struct mxs_phy *mxs_phy = to_mxs_phy(phy);
- clk_prepare_enable(mxs_phy->clk);
+ ret = clk_prepare_enable(mxs_phy->clk);
+ if (ret)
+ return ret;
+
return mxs_phy_hw_init(mxs_phy);
}
@@ -81,6 +85,7 @@ static void mxs_phy_shutdown(struct usb_phy *phy)
static int mxs_phy_suspend(struct usb_phy *x, int suspend)
{
+ int ret;
struct mxs_phy *mxs_phy = to_mxs_phy(x);
if (suspend) {
@@ -89,7 +94,9 @@ static int mxs_phy_suspend(struct usb_phy *x, int suspend)
x->io_priv + HW_USBPHY_CTRL_SET);
clk_disable_unprepare(mxs_phy->clk);
} else {
- clk_prepare_enable(mxs_phy->clk);
+ ret = clk_prepare_enable(mxs_phy->clk);
+ if (ret)
+ return ret;
writel(BM_USBPHY_CTRL_CLKGATE,
x->io_priv + HW_USBPHY_CTRL_CLR);
writel(0, x->io_priv + HW_USBPHY_PWD);
@@ -160,8 +167,6 @@ static int mxs_phy_probe(struct platform_device *pdev)
mxs_phy->phy.notify_disconnect = mxs_phy_on_disconnect;
mxs_phy->phy.type = USB_PHY_TYPE_USB2;
- ATOMIC_INIT_NOTIFIER_HEAD(&mxs_phy->phy.notifier);
-
mxs_phy->clk = clk;
platform_set_drvdata(pdev, mxs_phy);
diff --git a/drivers/usb/phy/phy-omap-control.c b/drivers/usb/phy/phy-omap-control.c
index 09c5ace1edd8..e7253182e47d 100644
--- a/drivers/usb/phy/phy-omap-control.c
+++ b/drivers/usb/phy/phy-omap-control.c
@@ -84,6 +84,20 @@ void omap_control_usb_phy_power(struct device *dev, int on)
else
val |= OMAP_CTRL_USB2_PHY_PD;
break;
+
+ case OMAP_CTRL_TYPE_AM437USB2:
+ if (on) {
+ val &= ~(AM437X_CTRL_USB2_PHY_PD |
+ AM437X_CTRL_USB2_OTG_PD);
+ val |= (AM437X_CTRL_USB2_OTGVDET_EN |
+ AM437X_CTRL_USB2_OTGSESSEND_EN);
+ } else {
+ val &= ~(AM437X_CTRL_USB2_OTGVDET_EN |
+ AM437X_CTRL_USB2_OTGSESSEND_EN);
+ val |= (AM437X_CTRL_USB2_PHY_PD |
+ AM437X_CTRL_USB2_OTG_PD);
+ }
+ break;
default:
dev_err(dev, "%s: type %d not recognized\n",
__func__, control_usb->type);
@@ -197,6 +211,7 @@ static const enum omap_control_usb_type otghs_data = OMAP_CTRL_TYPE_OTGHS;
static const enum omap_control_usb_type usb2_data = OMAP_CTRL_TYPE_USB2;
static const enum omap_control_usb_type pipe3_data = OMAP_CTRL_TYPE_PIPE3;
static const enum omap_control_usb_type dra7usb2_data = OMAP_CTRL_TYPE_DRA7USB2;
+static const enum omap_control_usb_type am437usb2_data = OMAP_CTRL_TYPE_AM437USB2;
static const struct of_device_id omap_control_usb_id_table[] = {
{
@@ -215,6 +230,10 @@ static const struct of_device_id omap_control_usb_id_table[] = {
.compatible = "ti,control-phy-dra7usb2",
.data = &dra7usb2_data,
},
+ {
+ .compatible = "ti,control-phy-am437usb2",
+ .data = &am437usb2_data,
+ },
{},
};
MODULE_DEVICE_TABLE(of, omap_control_usb_id_table);
diff --git a/drivers/usb/phy/phy-omap-otg.c b/drivers/usb/phy/phy-omap-otg.c
new file mode 100644
index 000000000000..11598cdb3189
--- /dev/null
+++ b/drivers/usb/phy/phy-omap-otg.c
@@ -0,0 +1,169 @@
+/*
+ * OMAP OTG controller driver
+ *
+ * Based on code from tahvo-usb.c and isp1301_omap.c drivers.
+ *
+ * Copyright (C) 2005-2006 Nokia Corporation
+ * Copyright (C) 2004 Texas Instruments
+ * Copyright (C) 2004 David Brownell
+ *
+ * This file is subject to the terms and conditions of the GNU General
+ * Public License. See the file "COPYING" in the main directory of this
+ * archive for more details.
+ *
+ * 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/io.h>
+#include <linux/err.h>
+#include <linux/extcon.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <linux/platform_data/usb-omap1.h>
+
+struct otg_device {
+ void __iomem *base;
+ bool id;
+ bool vbus;
+ struct extcon_specific_cable_nb vbus_dev;
+ struct extcon_specific_cable_nb id_dev;
+ struct notifier_block vbus_nb;
+ struct notifier_block id_nb;
+};
+
+#define OMAP_OTG_CTRL 0x0c
+#define OMAP_OTG_ASESSVLD (1 << 20)
+#define OMAP_OTG_BSESSEND (1 << 19)
+#define OMAP_OTG_BSESSVLD (1 << 18)
+#define OMAP_OTG_VBUSVLD (1 << 17)
+#define OMAP_OTG_ID (1 << 16)
+#define OMAP_OTG_XCEIV_OUTPUTS \
+ (OMAP_OTG_ASESSVLD | OMAP_OTG_BSESSEND | OMAP_OTG_BSESSVLD | \
+ OMAP_OTG_VBUSVLD | OMAP_OTG_ID)
+
+static void omap_otg_ctrl(struct otg_device *otg_dev, u32 outputs)
+{
+ u32 l;
+
+ l = readl(otg_dev->base + OMAP_OTG_CTRL);
+ l &= ~OMAP_OTG_XCEIV_OUTPUTS;
+ l |= outputs;
+ writel(l, otg_dev->base + OMAP_OTG_CTRL);
+}
+
+static void omap_otg_set_mode(struct otg_device *otg_dev)
+{
+ if (!otg_dev->id && otg_dev->vbus)
+ /* Set B-session valid. */
+ omap_otg_ctrl(otg_dev, OMAP_OTG_ID | OMAP_OTG_BSESSVLD);
+ else if (otg_dev->vbus)
+ /* Set A-session valid. */
+ omap_otg_ctrl(otg_dev, OMAP_OTG_ASESSVLD);
+ else if (!otg_dev->id)
+ /* Set B-session end to indicate no VBUS. */
+ omap_otg_ctrl(otg_dev, OMAP_OTG_ID | OMAP_OTG_BSESSEND);
+}
+
+static int omap_otg_id_notifier(struct notifier_block *nb,
+ unsigned long event, void *ptr)
+{
+ struct otg_device *otg_dev = container_of(nb, struct otg_device, id_nb);
+
+ otg_dev->id = event;
+ omap_otg_set_mode(otg_dev);
+
+ return NOTIFY_DONE;
+}
+
+static int omap_otg_vbus_notifier(struct notifier_block *nb,
+ unsigned long event, void *ptr)
+{
+ struct otg_device *otg_dev = container_of(nb, struct otg_device,
+ vbus_nb);
+
+ otg_dev->vbus = event;
+ omap_otg_set_mode(otg_dev);
+
+ return NOTIFY_DONE;
+}
+
+static int omap_otg_probe(struct platform_device *pdev)
+{
+ const struct omap_usb_config *config = pdev->dev.platform_data;
+ struct otg_device *otg_dev;
+ struct extcon_dev *extcon;
+ int ret;
+ u32 rev;
+
+ if (!config || !config->extcon)
+ return -ENODEV;
+
+ extcon = extcon_get_extcon_dev(config->extcon);
+ if (!extcon)
+ return -EPROBE_DEFER;
+
+ otg_dev = devm_kzalloc(&pdev->dev, sizeof(*otg_dev), GFP_KERNEL);
+ if (!otg_dev)
+ return -ENOMEM;
+
+ otg_dev->base = devm_ioremap_resource(&pdev->dev, &pdev->resource[0]);
+ if (IS_ERR(otg_dev->base))
+ return PTR_ERR(otg_dev->base);
+
+ otg_dev->id_nb.notifier_call = omap_otg_id_notifier;
+ otg_dev->vbus_nb.notifier_call = omap_otg_vbus_notifier;
+
+ ret = extcon_register_interest(&otg_dev->id_dev, config->extcon,
+ "USB-HOST", &otg_dev->id_nb);
+ if (ret)
+ return ret;
+
+ ret = extcon_register_interest(&otg_dev->vbus_dev, config->extcon,
+ "USB", &otg_dev->vbus_nb);
+ if (ret) {
+ extcon_unregister_interest(&otg_dev->id_dev);
+ return ret;
+ }
+
+ otg_dev->id = extcon_get_cable_state(extcon, "USB-HOST");
+ otg_dev->vbus = extcon_get_cable_state(extcon, "USB");
+ omap_otg_set_mode(otg_dev);
+
+ rev = readl(otg_dev->base);
+
+ dev_info(&pdev->dev,
+ "OMAP USB OTG controller rev %d.%d (%s, id=%d, vbus=%d)\n",
+ (rev >> 4) & 0xf, rev & 0xf, config->extcon, otg_dev->id,
+ otg_dev->vbus);
+
+ return 0;
+}
+
+static int omap_otg_remove(struct platform_device *pdev)
+{
+ struct otg_device *otg_dev = platform_get_drvdata(pdev);
+
+ extcon_unregister_interest(&otg_dev->id_dev);
+ extcon_unregister_interest(&otg_dev->vbus_dev);
+
+ return 0;
+}
+
+static struct platform_driver omap_otg_driver = {
+ .probe = omap_otg_probe,
+ .remove = omap_otg_remove,
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = "omap_otg",
+ },
+};
+module_platform_driver(omap_otg_driver);
+
+MODULE_DESCRIPTION("OMAP USB OTG controller driver");
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Aaro Koskinen <aaro.koskinen@iki.fi>");
diff --git a/drivers/usb/phy/phy-rcar-gen2-usb.c b/drivers/usb/phy/phy-rcar-gen2-usb.c
index db3ab34cddb4..551e0a6c0e22 100644
--- a/drivers/usb/phy/phy-rcar-gen2-usb.c
+++ b/drivers/usb/phy/phy-rcar-gen2-usb.c
@@ -213,7 +213,7 @@ static int rcar_gen2_usb_phy_probe(struct platform_device *pdev)
priv->phy.shutdown = rcar_gen2_usb_phy_shutdown;
priv->phy.set_suspend = rcar_gen2_usb_phy_set_suspend;
- retval = usb_add_phy(&priv->phy, USB_PHY_TYPE_USB2);
+ retval = usb_add_phy_dev(&priv->phy);
if (retval < 0) {
dev_err(dev, "Failed to add USB phy\n");
return retval;
diff --git a/drivers/usb/phy/phy-tahvo.c b/drivers/usb/phy/phy-tahvo.c
new file mode 100644
index 000000000000..cc61ee44b911
--- /dev/null
+++ b/drivers/usb/phy/phy-tahvo.c
@@ -0,0 +1,457 @@
+/*
+ * Tahvo USB transceiver driver
+ *
+ * Copyright (C) 2005-2006 Nokia Corporation
+ *
+ * Parts copied from isp1301_omap.c.
+ * Copyright (C) 2004 Texas Instruments
+ * Copyright (C) 2004 David Brownell
+ *
+ * Original driver written by Juha Yrjölä, Tony Lindgren and Timo Teräs.
+ * Modified for Retu/Tahvo MFD by Aaro Koskinen.
+ *
+ * This file is subject to the terms and conditions of the GNU General
+ * Public License. See the file "COPYING" in the main directory of this
+ * archive for more details.
+ *
+ * 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/io.h>
+#include <linux/clk.h>
+#include <linux/usb.h>
+#include <linux/extcon.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/usb/otg.h>
+#include <linux/mfd/retu.h>
+#include <linux/usb/gadget.h>
+#include <linux/platform_device.h>
+
+#define DRIVER_NAME "tahvo-usb"
+
+#define TAHVO_REG_IDSR 0x02
+#define TAHVO_REG_USBR 0x06
+
+#define USBR_SLAVE_CONTROL (1 << 8)
+#define USBR_VPPVIO_SW (1 << 7)
+#define USBR_SPEED (1 << 6)
+#define USBR_REGOUT (1 << 5)
+#define USBR_MASTER_SW2 (1 << 4)
+#define USBR_MASTER_SW1 (1 << 3)
+#define USBR_SLAVE_SW (1 << 2)
+#define USBR_NSUSPEND (1 << 1)
+#define USBR_SEMODE (1 << 0)
+
+#define TAHVO_MODE_HOST 0
+#define TAHVO_MODE_PERIPHERAL 1
+
+struct tahvo_usb {
+ struct platform_device *pt_dev;
+ struct usb_phy phy;
+ int vbus_state;
+ struct mutex serialize;
+ struct clk *ick;
+ int irq;
+ int tahvo_mode;
+ struct extcon_dev extcon;
+};
+
+static const char *tahvo_cable[] = {
+ "USB-HOST",
+ "USB",
+ NULL,
+};
+
+static ssize_t vbus_state_show(struct device *device,
+ struct device_attribute *attr, char *buf)
+{
+ struct tahvo_usb *tu = dev_get_drvdata(device);
+ return sprintf(buf, "%s\n", tu->vbus_state ? "on" : "off");
+}
+static DEVICE_ATTR(vbus, 0444, vbus_state_show, NULL);
+
+static void check_vbus_state(struct tahvo_usb *tu)
+{
+ struct retu_dev *rdev = dev_get_drvdata(tu->pt_dev->dev.parent);
+ int reg, prev_state;
+
+ reg = retu_read(rdev, TAHVO_REG_IDSR);
+ if (reg & TAHVO_STAT_VBUS) {
+ switch (tu->phy.state) {
+ case OTG_STATE_B_IDLE:
+ /* Enable the gadget driver */
+ if (tu->phy.otg->gadget)
+ usb_gadget_vbus_connect(tu->phy.otg->gadget);
+ tu->phy.state = OTG_STATE_B_PERIPHERAL;
+ break;
+ case OTG_STATE_A_IDLE:
+ /*
+ * Session is now valid assuming the USB hub is driving
+ * Vbus.
+ */
+ tu->phy.state = OTG_STATE_A_HOST;
+ break;
+ default:
+ break;
+ }
+ dev_info(&tu->pt_dev->dev, "USB cable connected\n");
+ } else {
+ switch (tu->phy.state) {
+ case OTG_STATE_B_PERIPHERAL:
+ if (tu->phy.otg->gadget)
+ usb_gadget_vbus_disconnect(tu->phy.otg->gadget);
+ tu->phy.state = OTG_STATE_B_IDLE;
+ break;
+ case OTG_STATE_A_HOST:
+ tu->phy.state = OTG_STATE_A_IDLE;
+ break;
+ default:
+ break;
+ }
+ dev_info(&tu->pt_dev->dev, "USB cable disconnected\n");
+ }
+
+ prev_state = tu->vbus_state;
+ tu->vbus_state = reg & TAHVO_STAT_VBUS;
+ if (prev_state != tu->vbus_state) {
+ extcon_set_cable_state(&tu->extcon, "USB", tu->vbus_state);
+ sysfs_notify(&tu->pt_dev->dev.kobj, NULL, "vbus_state");
+ }
+}
+
+static void tahvo_usb_become_host(struct tahvo_usb *tu)
+{
+ struct retu_dev *rdev = dev_get_drvdata(tu->pt_dev->dev.parent);
+
+ extcon_set_cable_state(&tu->extcon, "USB-HOST", true);
+
+ /* Power up the transceiver in USB host mode */
+ retu_write(rdev, TAHVO_REG_USBR, USBR_REGOUT | USBR_NSUSPEND |
+ USBR_MASTER_SW2 | USBR_MASTER_SW1);
+ tu->phy.state = OTG_STATE_A_IDLE;
+
+ check_vbus_state(tu);
+}
+
+static void tahvo_usb_stop_host(struct tahvo_usb *tu)
+{
+ tu->phy.state = OTG_STATE_A_IDLE;
+}
+
+static void tahvo_usb_become_peripheral(struct tahvo_usb *tu)
+{
+ struct retu_dev *rdev = dev_get_drvdata(tu->pt_dev->dev.parent);
+
+ extcon_set_cable_state(&tu->extcon, "USB-HOST", false);
+
+ /* Power up transceiver and set it in USB peripheral mode */
+ retu_write(rdev, TAHVO_REG_USBR, USBR_SLAVE_CONTROL | USBR_REGOUT |
+ USBR_NSUSPEND | USBR_SLAVE_SW);
+ tu->phy.state = OTG_STATE_B_IDLE;
+
+ check_vbus_state(tu);
+}
+
+static void tahvo_usb_stop_peripheral(struct tahvo_usb *tu)
+{
+ if (tu->phy.otg->gadget)
+ usb_gadget_vbus_disconnect(tu->phy.otg->gadget);
+ tu->phy.state = OTG_STATE_B_IDLE;
+}
+
+static void tahvo_usb_power_off(struct tahvo_usb *tu)
+{
+ struct retu_dev *rdev = dev_get_drvdata(tu->pt_dev->dev.parent);
+
+ /* Disable gadget controller if any */
+ if (tu->phy.otg->gadget)
+ usb_gadget_vbus_disconnect(tu->phy.otg->gadget);
+
+ /* Power off transceiver */
+ retu_write(rdev, TAHVO_REG_USBR, 0);
+ tu->phy.state = OTG_STATE_UNDEFINED;
+}
+
+static int tahvo_usb_set_suspend(struct usb_phy *dev, int suspend)
+{
+ struct tahvo_usb *tu = container_of(dev, struct tahvo_usb, phy);
+ struct retu_dev *rdev = dev_get_drvdata(tu->pt_dev->dev.parent);
+ u16 w;
+
+ dev_dbg(&tu->pt_dev->dev, "%s\n", __func__);
+
+ w = retu_read(rdev, TAHVO_REG_USBR);
+ if (suspend)
+ w &= ~USBR_NSUSPEND;
+ else
+ w |= USBR_NSUSPEND;
+ retu_write(rdev, TAHVO_REG_USBR, w);
+
+ return 0;
+}
+
+static int tahvo_usb_set_host(struct usb_otg *otg, struct usb_bus *host)
+{
+ struct tahvo_usb *tu = container_of(otg->phy, struct tahvo_usb, phy);
+
+ dev_dbg(&tu->pt_dev->dev, "%s %p\n", __func__, host);
+
+ mutex_lock(&tu->serialize);
+
+ if (host == NULL) {
+ if (tu->tahvo_mode == TAHVO_MODE_HOST)
+ tahvo_usb_power_off(tu);
+ otg->host = NULL;
+ mutex_unlock(&tu->serialize);
+ return 0;
+ }
+
+ if (tu->tahvo_mode == TAHVO_MODE_HOST) {
+ otg->host = NULL;
+ tahvo_usb_become_host(tu);
+ }
+
+ otg->host = host;
+
+ mutex_unlock(&tu->serialize);
+
+ return 0;
+}
+
+static int tahvo_usb_set_peripheral(struct usb_otg *otg,
+ struct usb_gadget *gadget)
+{
+ struct tahvo_usb *tu = container_of(otg->phy, struct tahvo_usb, phy);
+
+ dev_dbg(&tu->pt_dev->dev, "%s %p\n", __func__, gadget);
+
+ mutex_lock(&tu->serialize);
+
+ if (!gadget) {
+ if (tu->tahvo_mode == TAHVO_MODE_PERIPHERAL)
+ tahvo_usb_power_off(tu);
+ tu->phy.otg->gadget = NULL;
+ mutex_unlock(&tu->serialize);
+ return 0;
+ }
+
+ tu->phy.otg->gadget = gadget;
+ if (tu->tahvo_mode == TAHVO_MODE_PERIPHERAL)
+ tahvo_usb_become_peripheral(tu);
+
+ mutex_unlock(&tu->serialize);
+
+ return 0;
+}
+
+static irqreturn_t tahvo_usb_vbus_interrupt(int irq, void *_tu)
+{
+ struct tahvo_usb *tu = _tu;
+
+ mutex_lock(&tu->serialize);
+ check_vbus_state(tu);
+ mutex_unlock(&tu->serialize);
+
+ return IRQ_HANDLED;
+}
+
+static ssize_t otg_mode_show(struct device *device,
+ struct device_attribute *attr, char *buf)
+{
+ struct tahvo_usb *tu = dev_get_drvdata(device);
+
+ switch (tu->tahvo_mode) {
+ case TAHVO_MODE_HOST:
+ return sprintf(buf, "host\n");
+ case TAHVO_MODE_PERIPHERAL:
+ return sprintf(buf, "peripheral\n");
+ }
+
+ return -EINVAL;
+}
+
+static ssize_t otg_mode_store(struct device *device,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct tahvo_usb *tu = dev_get_drvdata(device);
+ int r;
+
+ mutex_lock(&tu->serialize);
+ if (count >= 4 && strncmp(buf, "host", 4) == 0) {
+ if (tu->tahvo_mode == TAHVO_MODE_PERIPHERAL)
+ tahvo_usb_stop_peripheral(tu);
+ tu->tahvo_mode = TAHVO_MODE_HOST;
+ if (tu->phy.otg->host) {
+ dev_info(device, "HOST mode: host controller present\n");
+ tahvo_usb_become_host(tu);
+ } else {
+ dev_info(device, "HOST mode: no host controller, powering off\n");
+ tahvo_usb_power_off(tu);
+ }
+ r = strlen(buf);
+ } else if (count >= 10 && strncmp(buf, "peripheral", 10) == 0) {
+ if (tu->tahvo_mode == TAHVO_MODE_HOST)
+ tahvo_usb_stop_host(tu);
+ tu->tahvo_mode = TAHVO_MODE_PERIPHERAL;
+ if (tu->phy.otg->gadget) {
+ dev_info(device, "PERIPHERAL mode: gadget driver present\n");
+ tahvo_usb_become_peripheral(tu);
+ } else {
+ dev_info(device, "PERIPHERAL mode: no gadget driver, powering off\n");
+ tahvo_usb_power_off(tu);
+ }
+ r = strlen(buf);
+ } else {
+ r = -EINVAL;
+ }
+ mutex_unlock(&tu->serialize);
+
+ return r;
+}
+static DEVICE_ATTR(otg_mode, 0644, otg_mode_show, otg_mode_store);
+
+static struct attribute *tahvo_attributes[] = {
+ &dev_attr_vbus.attr,
+ &dev_attr_otg_mode.attr,
+ NULL
+};
+
+static struct attribute_group tahvo_attr_group = {
+ .attrs = tahvo_attributes,
+};
+
+static int tahvo_usb_probe(struct platform_device *pdev)
+{
+ struct retu_dev *rdev = dev_get_drvdata(pdev->dev.parent);
+ struct tahvo_usb *tu;
+ int ret;
+
+ tu = devm_kzalloc(&pdev->dev, sizeof(*tu), GFP_KERNEL);
+ if (!tu)
+ return -ENOMEM;
+
+ tu->phy.otg = devm_kzalloc(&pdev->dev, sizeof(*tu->phy.otg),
+ GFP_KERNEL);
+ if (!tu->phy.otg)
+ return -ENOMEM;
+
+ tu->pt_dev = pdev;
+
+ /* Default mode */
+#ifdef CONFIG_TAHVO_USB_HOST_BY_DEFAULT
+ tu->tahvo_mode = TAHVO_MODE_HOST;
+#else
+ tu->tahvo_mode = TAHVO_MODE_PERIPHERAL;
+#endif
+
+ mutex_init(&tu->serialize);
+
+ tu->ick = devm_clk_get(&pdev->dev, "usb_l4_ick");
+ if (!IS_ERR(tu->ick))
+ clk_enable(tu->ick);
+
+ /*
+ * Set initial state, so that we generate kevents only on state changes.
+ */
+ tu->vbus_state = retu_read(rdev, TAHVO_REG_IDSR) & TAHVO_STAT_VBUS;
+
+ tu->extcon.name = DRIVER_NAME;
+ tu->extcon.supported_cable = tahvo_cable;
+ tu->extcon.dev.parent = &pdev->dev;
+
+ ret = extcon_dev_register(&tu->extcon);
+ if (ret) {
+ dev_err(&pdev->dev, "could not register extcon device: %d\n",
+ ret);
+ goto err_disable_clk;
+ }
+
+ /* Set the initial cable state. */
+ extcon_set_cable_state(&tu->extcon, "USB-HOST",
+ tu->tahvo_mode == TAHVO_MODE_HOST);
+ extcon_set_cable_state(&tu->extcon, "USB", tu->vbus_state);
+
+ /* Create OTG interface */
+ tahvo_usb_power_off(tu);
+ tu->phy.dev = &pdev->dev;
+ tu->phy.state = OTG_STATE_UNDEFINED;
+ tu->phy.label = DRIVER_NAME;
+ tu->phy.set_suspend = tahvo_usb_set_suspend;
+
+ tu->phy.otg->phy = &tu->phy;
+ tu->phy.otg->set_host = tahvo_usb_set_host;
+ tu->phy.otg->set_peripheral = tahvo_usb_set_peripheral;
+
+ ret = usb_add_phy(&tu->phy, USB_PHY_TYPE_USB2);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "cannot register USB transceiver: %d\n",
+ ret);
+ goto err_extcon_unreg;
+ }
+
+ dev_set_drvdata(&pdev->dev, tu);
+
+ tu->irq = platform_get_irq(pdev, 0);
+ ret = request_threaded_irq(tu->irq, NULL, tahvo_usb_vbus_interrupt, 0,
+ "tahvo-vbus", tu);
+ if (ret) {
+ dev_err(&pdev->dev, "could not register tahvo-vbus irq: %d\n",
+ ret);
+ goto err_remove_phy;
+ }
+
+ /* Attributes */
+ ret = sysfs_create_group(&pdev->dev.kobj, &tahvo_attr_group);
+ if (ret) {
+ dev_err(&pdev->dev, "cannot create sysfs group: %d\n", ret);
+ goto err_free_irq;
+ }
+
+ return 0;
+
+err_free_irq:
+ free_irq(tu->irq, tu);
+err_remove_phy:
+ usb_remove_phy(&tu->phy);
+err_extcon_unreg:
+ extcon_dev_unregister(&tu->extcon);
+err_disable_clk:
+ if (!IS_ERR(tu->ick))
+ clk_disable(tu->ick);
+
+ return ret;
+}
+
+static int tahvo_usb_remove(struct platform_device *pdev)
+{
+ struct tahvo_usb *tu = platform_get_drvdata(pdev);
+
+ sysfs_remove_group(&pdev->dev.kobj, &tahvo_attr_group);
+ free_irq(tu->irq, tu);
+ usb_remove_phy(&tu->phy);
+ extcon_dev_unregister(&tu->extcon);
+ if (!IS_ERR(tu->ick))
+ clk_disable(tu->ick);
+
+ return 0;
+}
+
+static struct platform_driver tahvo_usb_driver = {
+ .probe = tahvo_usb_probe,
+ .remove = tahvo_usb_remove,
+ .driver = {
+ .name = "tahvo-usb",
+ .owner = THIS_MODULE,
+ },
+};
+module_platform_driver(tahvo_usb_driver);
+
+MODULE_DESCRIPTION("Tahvo USB transceiver driver");
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Juha Yrjölä, Tony Lindgren, and Timo Teräs");
+MODULE_AUTHOR("Aaro Koskinen <aaro.koskinen@iki.fi>");
diff --git a/drivers/usb/phy/phy-twl6030-usb.c b/drivers/usb/phy/phy-twl6030-usb.c
index bad57ce77ba5..214172b68d5d 100644
--- a/drivers/usb/phy/phy-twl6030-usb.c
+++ b/drivers/usb/phy/phy-twl6030-usb.c
@@ -328,7 +328,7 @@ static int twl6030_usb_probe(struct platform_device *pdev)
struct device *dev = &pdev->dev;
struct twl4030_usb_data *pdata = dev_get_platdata(dev);
- twl = devm_kzalloc(dev, sizeof *twl, GFP_KERNEL);
+ twl = devm_kzalloc(dev, sizeof(*twl), GFP_KERNEL);
if (!twl)
return -ENOMEM;
diff --git a/drivers/usb/phy/phy.c b/drivers/usb/phy/phy.c
index 1b74523e1fee..e6f61e4361df 100644
--- a/drivers/usb/phy/phy.c
+++ b/drivers/usb/phy/phy.c
@@ -329,6 +329,8 @@ int usb_add_phy(struct usb_phy *x, enum usb_phy_type type)
return -EINVAL;
}
+ ATOMIC_INIT_NOTIFIER_HEAD(&x->notifier);
+
spin_lock_irqsave(&phy_lock, flags);
list_for_each_entry(phy, &phy_list, head) {
@@ -367,6 +369,8 @@ int usb_add_phy_dev(struct usb_phy *x)
return -EINVAL;
}
+ ATOMIC_INIT_NOTIFIER_HEAD(&x->notifier);
+
spin_lock_irqsave(&phy_lock, flags);
list_for_each_entry(phy_bind, &phy_bind_list, list)
if (!(strcmp(phy_bind->phy_dev_name, dev_name(x->dev))))
diff --git a/drivers/usb/renesas_usbhs/fifo.c b/drivers/usb/renesas_usbhs/fifo.c
index 45b94019aec8..d49f9c326035 100644
--- a/drivers/usb/renesas_usbhs/fifo.c
+++ b/drivers/usb/renesas_usbhs/fifo.c
@@ -1124,19 +1124,8 @@ void usbhs_fifo_init(struct usbhs_priv *priv)
mod->irq_brdysts = 0;
cfifo->pipe = NULL;
- cfifo->tx_chan = NULL;
- cfifo->rx_chan = NULL;
-
d0fifo->pipe = NULL;
- d0fifo->tx_chan = NULL;
- d0fifo->rx_chan = NULL;
-
d1fifo->pipe = NULL;
- d1fifo->tx_chan = NULL;
- d1fifo->rx_chan = NULL;
-
- usbhsf_dma_init(priv, usbhsf_get_d0fifo(priv));
- usbhsf_dma_init(priv, usbhsf_get_d1fifo(priv));
}
void usbhs_fifo_quit(struct usbhs_priv *priv)
@@ -1147,9 +1136,6 @@ void usbhs_fifo_quit(struct usbhs_priv *priv)
mod->irq_ready = NULL;
mod->irq_bempsts = 0;
mod->irq_brdysts = 0;
-
- usbhsf_dma_quit(priv, usbhsf_get_d0fifo(priv));
- usbhsf_dma_quit(priv, usbhsf_get_d1fifo(priv));
}
int usbhs_fifo_probe(struct usbhs_priv *priv)
@@ -1171,6 +1157,7 @@ int usbhs_fifo_probe(struct usbhs_priv *priv)
fifo->ctr = D0FIFOCTR;
fifo->tx_slave.shdma_slave.slave_id = usbhs_get_dparam(priv, d0_tx_id);
fifo->rx_slave.shdma_slave.slave_id = usbhs_get_dparam(priv, d0_rx_id);
+ usbhsf_dma_init(priv, fifo);
/* D1FIFO */
fifo = usbhsf_get_d1fifo(priv);
@@ -1180,10 +1167,13 @@ int usbhs_fifo_probe(struct usbhs_priv *priv)
fifo->ctr = D1FIFOCTR;
fifo->tx_slave.shdma_slave.slave_id = usbhs_get_dparam(priv, d1_tx_id);
fifo->rx_slave.shdma_slave.slave_id = usbhs_get_dparam(priv, d1_rx_id);
+ usbhsf_dma_init(priv, fifo);
return 0;
}
void usbhs_fifo_remove(struct usbhs_priv *priv)
{
+ usbhsf_dma_quit(priv, usbhsf_get_d0fifo(priv));
+ usbhsf_dma_quit(priv, usbhsf_get_d1fifo(priv));
}
diff --git a/drivers/usb/renesas_usbhs/mod_gadget.c b/drivers/usb/renesas_usbhs/mod_gadget.c
index 3385aeb5a364..458f3766bef1 100644
--- a/drivers/usb/renesas_usbhs/mod_gadget.c
+++ b/drivers/usb/renesas_usbhs/mod_gadget.c
@@ -987,11 +987,11 @@ int usbhs_mod_gadget_probe(struct usbhs_priv *priv)
/* init DCP */
if (usbhsg_is_dcp(uep)) {
gpriv->gadget.ep0 = &uep->ep;
- uep->ep.maxpacket = 64;
+ usb_ep_set_maxpacket_limit(&uep->ep, 64);
}
/* init normal pipe */
else {
- uep->ep.maxpacket = 512;
+ usb_ep_set_maxpacket_limit(&uep->ep, 512);
list_add_tail(&uep->ep.ep_list, &gpriv->gadget.ep_list);
}
}