浏览代码

Merge tag 'usb-for-v4.1' of git://git.kernel.org/pub/scm/linux/kernel/git/balbi/usb into usb-next

Felipe writes:

usb: patches for v4.1 merge window

As usual, a big pile of commits. This time a total
of 111 non-merge commits.

Other than the usual set of cleanups and non-critical
fixes, we have some interesting work for AM335x's MUSB
babble recovery. Now that takes a lot less time and we
don't have to Reset MUSB all the time.

The printer gadget has been converted to configfs interface
and the atmel udc has learned suspend/resume with wakeup.

Signed-off-by: Felipe Balbi <balbi@ti.com>
Greg Kroah-Hartman 10 年之前
父节点
当前提交
1c41a9570a
共有 59 个文件被更改,包括 2847 次插入1995 次删除
  1. 9 0
      Documentation/ABI/testing/configfs-usb-gadget-printer
  2. 1 0
      Documentation/devicetree/bindings/usb/dwc3.txt
  3. 4 1
      Documentation/devicetree/bindings/usb/renesas_usbhs.txt
  4. 47 0
      Documentation/usb/gadget-testing.txt
  5. 5 5
      drivers/usb/chipidea/udc.c
  6. 5 3
      drivers/usb/dwc2/Kconfig
  7. 2 4
      drivers/usb/dwc2/Makefile
  8. 2 0
      drivers/usb/dwc2/core.h
  9. 8 0
      drivers/usb/dwc2/hcd.c
  10. 72 88
      drivers/usb/dwc2/pci.c
  11. 21 8
      drivers/usb/dwc2/platform.c
  12. 0 7
      drivers/usb/dwc3/Kconfig
  13. 37 22
      drivers/usb/dwc3/core.c
  14. 2 0
      drivers/usb/dwc3/core.h
  15. 1 10
      drivers/usb/dwc3/dwc3-omap.c
  16. 0 2
      drivers/usb/dwc3/dwc3-pci.c
  17. 16 21
      drivers/usb/dwc3/gadget.c
  18. 1 3
      drivers/usb/dwc3/host.c
  19. 1 0
      drivers/usb/dwc3/platform_data.h
  20. 17 0
      drivers/usb/gadget/Kconfig
  21. 14 3
      drivers/usb/gadget/composite.c
  22. 2 0
      drivers/usb/gadget/function/Makefile
  23. 0 1
      drivers/usb/gadget/function/f_hid.c
  24. 1 1
      drivers/usb/gadget/function/f_mass_storage.c
  25. 1471 0
      drivers/usb/gadget/function/f_printer.c
  26. 37 0
      drivers/usb/gadget/function/u_printer.h
  27. 1 1
      drivers/usb/gadget/function/u_serial.c
  28. 1 0
      drivers/usb/gadget/legacy/Kconfig
  29. 48 1191
      drivers/usb/gadget/legacy/printer.c
  30. 157 55
      drivers/usb/gadget/udc/atmel_usba_udc.c
  31. 20 6
      drivers/usb/gadget/udc/atmel_usba_udc.h
  32. 1 1
      drivers/usb/gadget/udc/dummy_hcd.c
  33. 122 111
      drivers/usb/gadget/udc/goku_udc.c
  34. 3 12
      drivers/usb/gadget/udc/lpc32xx_udc.c
  35. 129 53
      drivers/usb/gadget/udc/net2280.c
  36. 0 2
      drivers/usb/gadget/udc/net2280.h
  37. 60 72
      drivers/usb/gadget/udc/pxa27x_udc.c
  38. 42 26
      drivers/usb/gadget/udc/udc-core.c
  39. 107 107
      drivers/usb/musb/musb_core.c
  40. 7 7
      drivers/usb/musb/musb_core.h
  41. 42 42
      drivers/usb/musb/musb_cppi41.c
  42. 44 61
      drivers/usb/musb/musb_dsps.c
  43. 1 39
      drivers/usb/musb/musb_gadget.c
  44. 1 1
      drivers/usb/phy/Kconfig
  45. 1 1
      drivers/usb/phy/of.c
  46. 1 1
      drivers/usb/phy/phy-ab8500-usb.c
  47. 5 7
      drivers/usb/phy/phy-generic.c
  48. 3 1
      drivers/usb/phy/phy.c
  49. 19 0
      drivers/usb/renesas_usbhs/common.c
  50. 7 0
      drivers/usb/renesas_usbhs/common.h
  51. 174 15
      drivers/usb/renesas_usbhs/fifo.c
  52. 1 0
      drivers/usb/renesas_usbhs/fifo.h
  53. 19 3
      drivers/usb/renesas_usbhs/mod_gadget.c
  54. 39 0
      drivers/usb/renesas_usbhs/pipe.c
  55. 1 0
      drivers/usb/renesas_usbhs/pipe.h
  56. 2 0
      include/linux/pci_ids.h
  57. 3 0
      include/linux/usb/composite.h
  58. 8 1
      include/linux/usb/gadget.h
  59. 2 0
      include/linux/usb/renesas_usbhs.h

+ 9 - 0
Documentation/ABI/testing/configfs-usb-gadget-printer

@@ -0,0 +1,9 @@
+What:		/config/usb-gadget/gadget/functions/printer.name
+Date:		Apr 2015
+KernelVersion:	4.1
+Description:
+		The attributes:
+
+		pnp_string	- Data to be passed to the host in pnp string
+		q_len		- Number of requests per endpoint
+

+ 1 - 0
Documentation/devicetree/bindings/usb/dwc3.txt

@@ -14,6 +14,7 @@ Optional properties:
  - phys: from the *Generic PHY* bindings
  - phys: from the *Generic PHY* bindings
  - phy-names: from the *Generic PHY* bindings
  - phy-names: from the *Generic PHY* bindings
  - tx-fifo-resize: determines if the FIFO *has* to be reallocated.
  - tx-fifo-resize: determines if the FIFO *has* to be reallocated.
+ - snps,usb3_lpm_capable: determines if platform is USB3 LPM capable
  - snps,disable_scramble_quirk: true when SW should disable data scrambling.
  - snps,disable_scramble_quirk: true when SW should disable data scrambling.
 	Only really useful for FPGA builds.
 	Only really useful for FPGA builds.
  - snps,has-lpm-erratum: true when DWC3 was configured with LPM Erratum enabled
  - snps,has-lpm-erratum: true when DWC3 was configured with LPM Erratum enabled

+ 4 - 1
Documentation/devicetree/bindings/usb/renesas_usbhs.txt

@@ -15,7 +15,10 @@ Optional properties:
   - phys: phandle + phy specifier pair
   - phys: phandle + phy specifier pair
   - phy-names: must be "usb"
   - phy-names: must be "usb"
   - dmas: Must contain a list of references to DMA specifiers.
   - dmas: Must contain a list of references to DMA specifiers.
-  - dma-names : Must contain a list of DMA names, "tx" or "rx".
+  - dma-names : Must contain a list of DMA names:
+   - tx0 ... tx<n>
+   - rx0 ... rx<n>
+    - This <n> means DnFIFO in USBHS module.
 
 
 Example:
 Example:
 	usbhs: usb@e6590000 {
 	usbhs: usb@e6590000 {

+ 47 - 0
Documentation/usb/gadget-testing.txt

@@ -19,6 +19,7 @@ provided by gadgets.
 16. UAC1 function
 16. UAC1 function
 17. UAC2 function
 17. UAC2 function
 18. UVC function
 18. UVC function
+19. PRINTER function
 
 
 
 
 1. ACM function
 1. ACM function
@@ -726,3 +727,49 @@ with these patches:
 http://www.spinics.net/lists/linux-usb/msg99220.html
 http://www.spinics.net/lists/linux-usb/msg99220.html
 
 
 host: luvcview -f yuv
 host: luvcview -f yuv
+
+19. PRINTER function
+====================
+
+The function is provided by usb_f_printer.ko module.
+
+Function-specific configfs interface
+------------------------------------
+
+The function name to use when creating the function directory is "printer".
+The printer function provides these attributes in its function directory:
+
+	pnp_string	- Data to be passed to the host in pnp string
+	q_len		- Number of requests per endpoint
+
+Testing the PRINTER function
+----------------------------
+
+The most basic testing:
+
+device: run the gadget
+# ls -l /devices/virtual/usb_printer_gadget/
+
+should show g_printer<number>.
+
+If udev is active, then /dev/g_printer<number> should appear automatically.
+
+host:
+
+If udev is active, then e.g. /dev/usb/lp0 should appear.
+
+host->device transmission:
+
+device:
+# cat /dev/g_printer<number>
+host:
+# cat > /dev/usb/lp0
+
+device->host transmission:
+
+# cat > /dev/g_printer<number>
+host:
+# cat /dev/usb/lp0
+
+More advanced testing can be done with the prn_example
+described in Documentation/usb/gadget-printer.txt.

+ 5 - 5
drivers/usb/chipidea/udc.c

@@ -86,10 +86,8 @@ static int hw_device_state(struct ci_hdrc *ci, u32 dma)
 		/* interrupt, error, port change, reset, sleep/suspend */
 		/* interrupt, error, port change, reset, sleep/suspend */
 		hw_write(ci, OP_USBINTR, ~0,
 		hw_write(ci, OP_USBINTR, ~0,
 			     USBi_UI|USBi_UEI|USBi_PCI|USBi_URI|USBi_SLI);
 			     USBi_UI|USBi_UEI|USBi_PCI|USBi_URI|USBi_SLI);
-		hw_write(ci, OP_USBCMD, USBCMD_RS, USBCMD_RS);
 	} else {
 	} else {
 		hw_write(ci, OP_USBINTR, ~0, 0);
 		hw_write(ci, OP_USBINTR, ~0, 0);
-		hw_write(ci, OP_USBCMD, USBCMD_RS, 0);
 	}
 	}
 	return 0;
 	return 0;
 }
 }
@@ -1508,7 +1506,9 @@ static int ci_udc_vbus_session(struct usb_gadget *_gadget, int is_active)
 			hw_device_reset(ci);
 			hw_device_reset(ci);
 			hw_device_state(ci, ci->ep0out->qh.dma);
 			hw_device_state(ci, ci->ep0out->qh.dma);
 			usb_gadget_set_state(_gadget, USB_STATE_POWERED);
 			usb_gadget_set_state(_gadget, USB_STATE_POWERED);
+			usb_udc_vbus_handler(_gadget, true);
 		} else {
 		} else {
+			usb_udc_vbus_handler(_gadget, false);
 			if (ci->driver)
 			if (ci->driver)
 				ci->driver->disconnect(&ci->gadget);
 				ci->driver->disconnect(&ci->gadget);
 			hw_device_state(ci, 0);
 			hw_device_state(ci, 0);
@@ -1574,13 +1574,12 @@ static int ci_udc_pullup(struct usb_gadget *_gadget, int is_on)
 {
 {
 	struct ci_hdrc *ci = container_of(_gadget, struct ci_hdrc, gadget);
 	struct ci_hdrc *ci = container_of(_gadget, struct ci_hdrc, gadget);
 
 
-	if (!ci->vbus_active)
-		return -EOPNOTSUPP;
-
+	pm_runtime_get_sync(&ci->gadget.dev);
 	if (is_on)
 	if (is_on)
 		hw_write(ci, OP_USBCMD, USBCMD_RS, USBCMD_RS);
 		hw_write(ci, OP_USBCMD, USBCMD_RS, USBCMD_RS);
 	else
 	else
 		hw_write(ci, OP_USBCMD, USBCMD_RS, 0);
 		hw_write(ci, OP_USBCMD, USBCMD_RS, 0);
+	pm_runtime_put_sync(&ci->gadget.dev);
 
 
 	return 0;
 	return 0;
 }
 }
@@ -1710,6 +1709,7 @@ static int ci_udc_start(struct usb_gadget *gadget,
 		spin_lock_irqsave(&ci->lock, flags);
 		spin_lock_irqsave(&ci->lock, flags);
 		hw_device_reset(ci);
 		hw_device_reset(ci);
 	} else {
 	} else {
+		usb_udc_vbus_handler(&ci->gadget, false);
 		pm_runtime_put_sync(&ci->gadget.dev);
 		pm_runtime_put_sync(&ci->gadget.dev);
 		return retval;
 		return retval;
 	}
 	}

+ 5 - 3
drivers/usb/dwc2/Kconfig

@@ -59,11 +59,13 @@ config USB_DWC2_PLATFORM
 
 
 config USB_DWC2_PCI
 config USB_DWC2_PCI
 	tristate "DWC2 PCI"
 	tristate "DWC2 PCI"
-	depends on USB_DWC2_HOST && PCI
-	default USB_DWC2_HOST
+	depends on PCI
+	default n
+	select USB_DWC2_PLATFORM
+	select NOP_USB_XCEIV
 	help
 	help
 	  The Designware USB2.0 PCI interface module for controllers
 	  The Designware USB2.0 PCI interface module for controllers
-	  connected to a PCI bus. This is only used for host mode.
+	  connected to a PCI bus.
 
 
 config USB_DWC2_DEBUG
 config USB_DWC2_DEBUG
 	bool "Enable Debugging Messages"
 	bool "Enable Debugging Messages"

+ 2 - 4
drivers/usb/dwc2/Makefile

@@ -19,10 +19,8 @@ endif
 # mode. The PCI bus interface module will called dwc2_pci.ko and the platform
 # mode. The PCI bus interface module will called dwc2_pci.ko and the platform
 # interface module will be called dwc2_platform.ko.
 # interface module will be called dwc2_platform.ko.
 
 
-ifneq ($(CONFIG_USB_DWC2_PCI),)
-	obj-$(CONFIG_USB_DWC2)		+= dwc2_pci.o
-	dwc2_pci-y			:= pci.o
-endif
+obj-$(CONFIG_USB_DWC2_PCI)		+= dwc2_pci.o
+dwc2_pci-y				:= pci.o
 
 
 obj-$(CONFIG_USB_DWC2_PLATFORM)		+= dwc2_platform.o
 obj-$(CONFIG_USB_DWC2_PLATFORM)		+= dwc2_platform.o
 dwc2_platform-y				:= platform.o
 dwc2_platform-y				:= platform.o

+ 2 - 0
drivers/usb/dwc2/core.h

@@ -593,6 +593,8 @@ struct dwc2_hsotg {
 	struct dwc2_core_params *core_params;
 	struct dwc2_core_params *core_params;
 	enum usb_otg_state op_state;
 	enum usb_otg_state op_state;
 	enum usb_dr_mode dr_mode;
 	enum usb_dr_mode dr_mode;
+	unsigned int hcd_enabled:1;
+	unsigned int gadget_enabled:1;
 
 
 	struct phy *phy;
 	struct phy *phy;
 	struct usb_phy *uphy;
 	struct usb_phy *uphy;

+ 8 - 0
drivers/usb/dwc2/hcd.c

@@ -257,6 +257,14 @@ static void dwc2_hcd_cleanup_channels(struct dwc2_hsotg *hsotg)
 		 */
 		 */
 		channel->qh = NULL;
 		channel->qh = NULL;
 	}
 	}
+	/* All channels have been freed, mark them available */
+	if (hsotg->core_params->uframe_sched > 0) {
+		hsotg->available_host_channels =
+			hsotg->core_params->host_channels;
+	} else {
+		hsotg->non_periodic_channels = 0;
+		hsotg->periodic_channels = 0;
+	}
 }
 }
 
 
 /**
 /**

+ 72 - 88
drivers/usb/dwc2/pci.c

@@ -50,113 +50,97 @@
 
 
 #include <linux/usb/hcd.h>
 #include <linux/usb/hcd.h>
 #include <linux/usb/ch11.h>
 #include <linux/usb/ch11.h>
+#include <linux/platform_device.h>
+#include <linux/usb/usb_phy_generic.h>
 
 
-#include "core.h"
-#include "hcd.h"
-
-#define PCI_VENDOR_ID_SYNOPSYS		0x16c3
 #define PCI_PRODUCT_ID_HAPS_HSOTG	0xabc0
 #define PCI_PRODUCT_ID_HAPS_HSOTG	0xabc0
 
 
-static const char dwc2_driver_name[] = "dwc2";
-
-static const struct dwc2_core_params dwc2_module_params = {
-	.otg_cap			= -1,
-	.otg_ver			= -1,
-	.dma_enable			= -1,
-	.dma_desc_enable		= 0,
-	.speed				= -1,
-	.enable_dynamic_fifo		= -1,
-	.en_multiple_tx_fifo		= -1,
-	.host_rx_fifo_size		= 1024,
-	.host_nperio_tx_fifo_size	= 256,
-	.host_perio_tx_fifo_size	= 1024,
-	.max_transfer_size		= 65535,
-	.max_packet_count		= 511,
-	.host_channels			= -1,
-	.phy_type			= -1,
-	.phy_utmi_width			= -1,
-	.phy_ulpi_ddr			= -1,
-	.phy_ulpi_ext_vbus		= -1,
-	.i2c_enable			= -1,
-	.ulpi_fs_ls			= -1,
-	.host_support_fs_ls_low_power	= -1,
-	.host_ls_low_power_phy_clk	= -1,
-	.ts_dline			= -1,
-	.reload_ctl			= -1,
-	.ahbcfg				= -1,
-	.uframe_sched			= -1,
+static const char dwc2_driver_name[] = "dwc2-pci";
+
+struct dwc2_pci_glue {
+	struct platform_device *dwc2;
+	struct platform_device *phy;
 };
 };
 
 
-/**
- * dwc2_driver_remove() - Called when the DWC_otg core is unregistered with the
- * DWC_otg driver
- *
- * @dev: Bus device
- *
- * This routine is called, for example, when the rmmod command is executed. The
- * device may or may not be electrically present. If it is present, the driver
- * stops device processing. Any resources used on behalf of this device are
- * freed.
- */
-static void dwc2_driver_remove(struct pci_dev *dev)
+static void dwc2_pci_remove(struct pci_dev *pci)
 {
 {
-	struct dwc2_hsotg *hsotg = pci_get_drvdata(dev);
+	struct dwc2_pci_glue *glue = pci_get_drvdata(pci);
 
 
-	dwc2_hcd_remove(hsotg);
-	pci_disable_device(dev);
+	platform_device_unregister(glue->dwc2);
+	usb_phy_generic_unregister(glue->phy);
+	kfree(glue);
+	pci_set_drvdata(pci, NULL);
 }
 }
 
 
-/**
- * dwc2_driver_probe() - Called when the DWC_otg core is bound to the DWC_otg
- * driver
- *
- * @dev: Bus device
- *
- * This routine creates the driver components required to control the device
- * (core, HCD, and PCD) and initializes the device. The driver components are
- * stored in a dwc2_hsotg structure. A reference to the dwc2_hsotg is saved
- * in the device private data. This allows the driver to access the dwc2_hsotg
- * structure on subsequent calls to driver methods for this device.
- */
-static int dwc2_driver_probe(struct pci_dev *dev,
-			     const struct pci_device_id *id)
+static int dwc2_pci_probe(struct pci_dev *pci,
+		const struct pci_device_id *id)
 {
 {
-	struct dwc2_hsotg *hsotg;
-	int retval;
+	struct resource		res[2];
+	struct platform_device	*dwc2;
+	struct platform_device	*phy;
+	int			ret;
+	struct device		*dev = &pci->dev;
+	struct dwc2_pci_glue	*glue;
+
+	ret = pcim_enable_device(pci);
+	if (ret) {
+		dev_err(dev, "failed to enable pci device\n");
+		return -ENODEV;
+	}
+
+	pci_set_master(pci);
 
 
-	hsotg = devm_kzalloc(&dev->dev, sizeof(*hsotg), GFP_KERNEL);
-	if (!hsotg)
+	dwc2 = platform_device_alloc("dwc2", PLATFORM_DEVID_AUTO);
+	if (!dwc2) {
+		dev_err(dev, "couldn't allocate dwc2 device\n");
 		return -ENOMEM;
 		return -ENOMEM;
+	}
 
 
-	hsotg->dev = &dev->dev;
-	hsotg->regs = devm_ioremap_resource(&dev->dev, &dev->resource[0]);
-	if (IS_ERR(hsotg->regs))
-		return PTR_ERR(hsotg->regs);
+	memset(res, 0x00, sizeof(struct resource) * ARRAY_SIZE(res));
 
 
-	dev_dbg(&dev->dev, "mapped PA %08lx to VA %p\n",
-		(unsigned long)pci_resource_start(dev, 0), hsotg->regs);
+	res[0].start	= pci_resource_start(pci, 0);
+	res[0].end	= pci_resource_end(pci, 0);
+	res[0].name	= "dwc2";
+	res[0].flags	= IORESOURCE_MEM;
 
 
-	if (pci_enable_device(dev) < 0)
-		return -ENODEV;
+	res[1].start	= pci->irq;
+	res[1].name	= "dwc2";
+	res[1].flags	= IORESOURCE_IRQ;
 
 
-	pci_set_master(dev);
+	ret = platform_device_add_resources(dwc2, res, ARRAY_SIZE(res));
+	if (ret) {
+		dev_err(dev, "couldn't add resources to dwc2 device\n");
+		return ret;
+	}
 
 
-	retval = devm_request_irq(hsotg->dev, dev->irq,
-				  dwc2_handle_common_intr, IRQF_SHARED,
-				  dev_name(hsotg->dev), hsotg);
-	if (retval)
-		return retval;
+	dwc2->dev.parent = dev;
 
 
-	spin_lock_init(&hsotg->lock);
-	retval = dwc2_hcd_init(hsotg, dev->irq, &dwc2_module_params);
-	if (retval) {
-		pci_disable_device(dev);
-		return retval;
+	phy = usb_phy_generic_register();
+	if (IS_ERR(phy)) {
+		dev_err(dev, "error registering generic PHY (%ld)\n",
+			PTR_ERR(phy));
+		return PTR_ERR(phy);
 	}
 	}
 
 
-	pci_set_drvdata(dev, hsotg);
+	ret = platform_device_add(dwc2);
+	if (ret) {
+		dev_err(dev, "failed to register dwc2 device\n");
+		goto err;
+	}
+
+	glue = kzalloc(sizeof(*glue), GFP_KERNEL);
+	if (!glue)
+		return -ENOMEM;
+
+	glue->phy = phy;
+	glue->dwc2 = dwc2;
+	pci_set_drvdata(pci, glue);
 
 
-	return retval;
+	return 0;
+err:
+	usb_phy_generic_unregister(phy);
+	platform_device_put(dwc2);
+	return ret;
 }
 }
 
 
 static const struct pci_device_id dwc2_pci_ids[] = {
 static const struct pci_device_id dwc2_pci_ids[] = {
@@ -174,8 +158,8 @@ MODULE_DEVICE_TABLE(pci, dwc2_pci_ids);
 static struct pci_driver dwc2_pci_driver = {
 static struct pci_driver dwc2_pci_driver = {
 	.name = dwc2_driver_name,
 	.name = dwc2_driver_name,
 	.id_table = dwc2_pci_ids,
 	.id_table = dwc2_pci_ids,
-	.probe = dwc2_driver_probe,
-	.remove = dwc2_driver_remove,
+	.probe = dwc2_pci_probe,
+	.remove = dwc2_pci_remove,
 };
 };
 
 
 module_pci_driver(dwc2_pci_driver);
 module_pci_driver(dwc2_pci_driver);

+ 21 - 8
drivers/usb/dwc2/platform.c

@@ -121,8 +121,10 @@ static int dwc2_driver_remove(struct platform_device *dev)
 {
 {
 	struct dwc2_hsotg *hsotg = platform_get_drvdata(dev);
 	struct dwc2_hsotg *hsotg = platform_get_drvdata(dev);
 
 
-	dwc2_hcd_remove(hsotg);
-	s3c_hsotg_remove(hsotg);
+	if (hsotg->hcd_enabled)
+		dwc2_hcd_remove(hsotg);
+	if (hsotg->gadget_enabled)
+		s3c_hsotg_remove(hsotg);
 
 
 	return 0;
 	return 0;
 }
 }
@@ -234,12 +236,23 @@ static int dwc2_driver_probe(struct platform_device *dev)
 
 
 	spin_lock_init(&hsotg->lock);
 	spin_lock_init(&hsotg->lock);
 	mutex_init(&hsotg->init_mutex);
 	mutex_init(&hsotg->init_mutex);
-	retval = dwc2_gadget_init(hsotg, irq);
-	if (retval)
-		return retval;
-	retval = dwc2_hcd_init(hsotg, irq, params);
-	if (retval)
-		return retval;
+
+	if (hsotg->dr_mode != USB_DR_MODE_HOST) {
+		retval = dwc2_gadget_init(hsotg, irq);
+		if (retval)
+			return retval;
+		hsotg->gadget_enabled = 1;
+	}
+
+	if (hsotg->dr_mode != USB_DR_MODE_PERIPHERAL) {
+		retval = dwc2_hcd_init(hsotg, irq, params);
+		if (retval) {
+			if (hsotg->gadget_enabled)
+				s3c_hsotg_remove(hsotg);
+			return retval;
+		}
+		hsotg->hcd_enabled = 1;
+	}
 
 
 	platform_set_drvdata(dev, hsotg);
 	platform_set_drvdata(dev, hsotg);
 
 

+ 0 - 7
drivers/usb/dwc3/Kconfig

@@ -104,11 +104,4 @@ config USB_DWC3_DEBUG
 	help
 	help
 	  Say Y here to enable debugging messages on DWC3 Driver.
 	  Say Y here to enable debugging messages on DWC3 Driver.
 
 
-config DWC3_HOST_USB3_LPM_ENABLE
-	bool "Enable USB3 LPM Capability"
-	depends on USB_DWC3_HOST=y || USB_DWC3_DUAL_ROLE=y
-	default n
-	help
-	  Select this when you want to enable USB3 LPM with dwc3 xhci host.
-
 endif
 endif

+ 37 - 22
drivers/usb/dwc3/core.c

@@ -774,17 +774,13 @@ static int dwc3_probe(struct platform_device *pdev)
 	 * since it will be requested by the xhci-plat driver.
 	 * since it will be requested by the xhci-plat driver.
 	 */
 	 */
 	regs = devm_ioremap_resource(dev, res);
 	regs = devm_ioremap_resource(dev, res);
-	if (IS_ERR(regs))
-		return PTR_ERR(regs);
+	if (IS_ERR(regs)) {
+		ret = PTR_ERR(regs);
+		goto err0;
+	}
 
 
 	dwc->regs	= regs;
 	dwc->regs	= regs;
 	dwc->regs_size	= resource_size(res);
 	dwc->regs_size	= resource_size(res);
-	/*
-	 * restore res->start back to its original value so that,
-	 * in case the probe is deferred, we don't end up getting error in
-	 * request the memory region the next time probe is called.
-	 */
-	res->start -= DWC3_GLOBALS_REGS_START;
 
 
 	/* default to highest possible threshold */
 	/* default to highest possible threshold */
 	lpm_nyet_threshold = 0xff;
 	lpm_nyet_threshold = 0xff;
@@ -808,6 +804,8 @@ static int dwc3_probe(struct platform_device *pdev)
 				"snps,is-utmi-l1-suspend");
 				"snps,is-utmi-l1-suspend");
 		of_property_read_u8(node, "snps,hird-threshold",
 		of_property_read_u8(node, "snps,hird-threshold",
 				&hird_threshold);
 				&hird_threshold);
+		dwc->usb3_lpm_capable = of_property_read_bool(node,
+				"snps,usb3_lpm_capable");
 
 
 		dwc->needs_fifo_resize = of_property_read_bool(node,
 		dwc->needs_fifo_resize = of_property_read_bool(node,
 				"tx-fifo-resize");
 				"tx-fifo-resize");
@@ -848,6 +846,7 @@ static int dwc3_probe(struct platform_device *pdev)
 			hird_threshold = pdata->hird_threshold;
 			hird_threshold = pdata->hird_threshold;
 
 
 		dwc->needs_fifo_resize = pdata->tx_fifo_resize;
 		dwc->needs_fifo_resize = pdata->tx_fifo_resize;
+		dwc->usb3_lpm_capable = pdata->usb3_lpm_capable;
 		dwc->dr_mode = pdata->dr_mode;
 		dwc->dr_mode = pdata->dr_mode;
 
 
 		dwc->disable_scramble_quirk = pdata->disable_scramble_quirk;
 		dwc->disable_scramble_quirk = pdata->disable_scramble_quirk;
@@ -878,7 +877,7 @@ static int dwc3_probe(struct platform_device *pdev)
 
 
 	ret = dwc3_core_get_phy(dwc);
 	ret = dwc3_core_get_phy(dwc);
 	if (ret)
 	if (ret)
-		return ret;
+		goto err0;
 
 
 	spin_lock_init(&dwc->lock);
 	spin_lock_init(&dwc->lock);
 	platform_set_drvdata(pdev, dwc);
 	platform_set_drvdata(pdev, dwc);
@@ -899,7 +898,7 @@ static int dwc3_probe(struct platform_device *pdev)
 	if (ret) {
 	if (ret) {
 		dev_err(dwc->dev, "failed to allocate event buffers\n");
 		dev_err(dwc->dev, "failed to allocate event buffers\n");
 		ret = -ENOMEM;
 		ret = -ENOMEM;
-		goto err0;
+		goto err1;
 	}
 	}
 
 
 	if (IS_ENABLED(CONFIG_USB_DWC3_HOST))
 	if (IS_ENABLED(CONFIG_USB_DWC3_HOST))
@@ -913,65 +912,81 @@ static int dwc3_probe(struct platform_device *pdev)
 	ret = dwc3_core_init(dwc);
 	ret = dwc3_core_init(dwc);
 	if (ret) {
 	if (ret) {
 		dev_err(dev, "failed to initialize core\n");
 		dev_err(dev, "failed to initialize core\n");
-		goto err0;
+		goto err1;
 	}
 	}
 
 
 	usb_phy_set_suspend(dwc->usb2_phy, 0);
 	usb_phy_set_suspend(dwc->usb2_phy, 0);
 	usb_phy_set_suspend(dwc->usb3_phy, 0);
 	usb_phy_set_suspend(dwc->usb3_phy, 0);
 	ret = phy_power_on(dwc->usb2_generic_phy);
 	ret = phy_power_on(dwc->usb2_generic_phy);
 	if (ret < 0)
 	if (ret < 0)
-		goto err1;
+		goto err2;
 
 
 	ret = phy_power_on(dwc->usb3_generic_phy);
 	ret = phy_power_on(dwc->usb3_generic_phy);
 	if (ret < 0)
 	if (ret < 0)
-		goto err_usb2phy_power;
+		goto err3;
 
 
 	ret = dwc3_event_buffers_setup(dwc);
 	ret = dwc3_event_buffers_setup(dwc);
 	if (ret) {
 	if (ret) {
 		dev_err(dwc->dev, "failed to setup event buffers\n");
 		dev_err(dwc->dev, "failed to setup event buffers\n");
-		goto err_usb3phy_power;
+		goto err4;
 	}
 	}
 
 
 	ret = dwc3_core_init_mode(dwc);
 	ret = dwc3_core_init_mode(dwc);
 	if (ret)
 	if (ret)
-		goto err2;
+		goto err5;
 
 
 	ret = dwc3_debugfs_init(dwc);
 	ret = dwc3_debugfs_init(dwc);
 	if (ret) {
 	if (ret) {
 		dev_err(dev, "failed to initialize debugfs\n");
 		dev_err(dev, "failed to initialize debugfs\n");
-		goto err3;
+		goto err6;
 	}
 	}
 
 
 	pm_runtime_allow(dev);
 	pm_runtime_allow(dev);
 
 
 	return 0;
 	return 0;
 
 
-err3:
+err6:
 	dwc3_core_exit_mode(dwc);
 	dwc3_core_exit_mode(dwc);
 
 
-err2:
+err5:
 	dwc3_event_buffers_cleanup(dwc);
 	dwc3_event_buffers_cleanup(dwc);
 
 
-err_usb3phy_power:
+err4:
 	phy_power_off(dwc->usb3_generic_phy);
 	phy_power_off(dwc->usb3_generic_phy);
 
 
-err_usb2phy_power:
+err3:
 	phy_power_off(dwc->usb2_generic_phy);
 	phy_power_off(dwc->usb2_generic_phy);
 
 
-err1:
+err2:
 	usb_phy_set_suspend(dwc->usb2_phy, 1);
 	usb_phy_set_suspend(dwc->usb2_phy, 1);
 	usb_phy_set_suspend(dwc->usb3_phy, 1);
 	usb_phy_set_suspend(dwc->usb3_phy, 1);
 	dwc3_core_exit(dwc);
 	dwc3_core_exit(dwc);
 
 
-err0:
+err1:
 	dwc3_free_event_buffers(dwc);
 	dwc3_free_event_buffers(dwc);
 
 
+err0:
+	/*
+	 * restore res->start back to its original value so that, in case the
+	 * probe is deferred, we don't end up getting error in request the
+	 * memory region the next time probe is called.
+	 */
+	res->start -= DWC3_GLOBALS_REGS_START;
+
 	return ret;
 	return ret;
 }
 }
 
 
 static int dwc3_remove(struct platform_device *pdev)
 static int dwc3_remove(struct platform_device *pdev)
 {
 {
 	struct dwc3	*dwc = platform_get_drvdata(pdev);
 	struct dwc3	*dwc = platform_get_drvdata(pdev);
+	struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+
+	/*
+	 * restore res->start back to its original value so that, in case the
+	 * probe is deferred, we don't end up getting error in request the
+	 * memory region the next time probe is called.
+	 */
+	res->start -= DWC3_GLOBALS_REGS_START;
 
 
 	dwc3_debugfs_exit(dwc);
 	dwc3_debugfs_exit(dwc);
 	dwc3_core_exit_mode(dwc);
 	dwc3_core_exit_mode(dwc);

+ 2 - 0
drivers/usb/dwc3/core.h

@@ -689,6 +689,7 @@ struct dwc3_scratchpad_array {
  * @setup_packet_pending: true when there's a Setup Packet in FIFO. Workaround
  * @setup_packet_pending: true when there's a Setup Packet in FIFO. Workaround
  * @start_config_issued: true when StartConfig command has been issued
  * @start_config_issued: true when StartConfig command has been issued
  * @three_stage_setup: set if we perform a three phase setup
  * @three_stage_setup: set if we perform a three phase setup
+ * @usb3_lpm_capable: set if hadrware supports Link Power Management
  * @disable_scramble_quirk: set if we enable the disable scramble quirk
  * @disable_scramble_quirk: set if we enable the disable scramble quirk
  * @u2exit_lfps_quirk: set if we enable u2exit lfps quirk
  * @u2exit_lfps_quirk: set if we enable u2exit lfps quirk
  * @u2ss_inp3_quirk: set if we enable P3 OK for U2/SS Inactive quirk
  * @u2ss_inp3_quirk: set if we enable P3 OK for U2/SS Inactive quirk
@@ -812,6 +813,7 @@ struct dwc3 {
 	unsigned		setup_packet_pending:1;
 	unsigned		setup_packet_pending:1;
 	unsigned		start_config_issued:1;
 	unsigned		start_config_issued:1;
 	unsigned		three_stage_setup:1;
 	unsigned		three_stage_setup:1;
+	unsigned		usb3_lpm_capable:1;
 
 
 	unsigned		disable_scramble_quirk:1;
 	unsigned		disable_scramble_quirk:1;
 	unsigned		u2exit_lfps_quirk:1;
 	unsigned		u2exit_lfps_quirk:1;

+ 1 - 10
drivers/usb/dwc3/dwc3-omap.c

@@ -325,15 +325,6 @@ static irqreturn_t dwc3_omap_interrupt(int irq, void *_omap)
 	return IRQ_HANDLED;
 	return IRQ_HANDLED;
 }
 }
 
 
-static int dwc3_omap_remove_core(struct device *dev, void *c)
-{
-	struct platform_device *pdev = to_platform_device(dev);
-
-	of_device_unregister(pdev);
-
-	return 0;
-}
-
 static void dwc3_omap_enable_irqs(struct dwc3_omap *omap)
 static void dwc3_omap_enable_irqs(struct dwc3_omap *omap)
 {
 {
 	u32			reg;
 	u32			reg;
@@ -600,7 +591,7 @@ static int dwc3_omap_remove(struct platform_device *pdev)
 	if (omap->extcon_id_dev.edev)
 	if (omap->extcon_id_dev.edev)
 		extcon_unregister_interest(&omap->extcon_id_dev);
 		extcon_unregister_interest(&omap->extcon_id_dev);
 	dwc3_omap_disable_irqs(omap);
 	dwc3_omap_disable_irqs(omap);
-	device_for_each_child(&pdev->dev, NULL, dwc3_omap_remove_core);
+	of_platform_depopulate(omap->dev);
 	pm_runtime_put_sync(&pdev->dev);
 	pm_runtime_put_sync(&pdev->dev);
 	pm_runtime_disable(&pdev->dev);
 	pm_runtime_disable(&pdev->dev);
 
 

+ 0 - 2
drivers/usb/dwc3/dwc3-pci.c

@@ -24,8 +24,6 @@
 
 
 #include "platform_data.h"
 #include "platform_data.h"
 
 
-/* FIXME define these in <linux/pci_ids.h> */
-#define PCI_VENDOR_ID_SYNOPSYS		0x16c3
 #define PCI_DEVICE_ID_SYNOPSYS_HAPSUSB3	0xabcd
 #define PCI_DEVICE_ID_SYNOPSYS_HAPSUSB3	0xabcd
 #define PCI_DEVICE_ID_INTEL_BYT		0x0f37
 #define PCI_DEVICE_ID_INTEL_BYT		0x0f37
 #define PCI_DEVICE_ID_INTEL_MRFLD	0x119e
 #define PCI_DEVICE_ID_INTEL_MRFLD	0x119e

+ 16 - 21
drivers/usb/dwc3/gadget.c

@@ -1855,32 +1855,27 @@ static int dwc3_cleanup_done_reqs(struct dwc3 *dwc, struct dwc3_ep *dep,
 	unsigned int		i;
 	unsigned int		i;
 	int			ret;
 	int			ret;
 
 
+	req = next_request(&dep->req_queued);
+	if (!req) {
+		WARN_ON_ONCE(1);
+		return 1;
+	}
+	i = 0;
 	do {
 	do {
-		req = next_request(&dep->req_queued);
-		if (!req) {
-			WARN_ON_ONCE(1);
-			return 1;
-		}
-		i = 0;
-		do {
-			slot = req->start_slot + i;
-			if ((slot == DWC3_TRB_NUM - 1) &&
+		slot = req->start_slot + i;
+		if ((slot == DWC3_TRB_NUM - 1) &&
 				usb_endpoint_xfer_isoc(dep->endpoint.desc))
 				usb_endpoint_xfer_isoc(dep->endpoint.desc))
-				slot++;
-			slot %= DWC3_TRB_NUM;
-			trb = &dep->trb_pool[slot];
-
-			ret = __dwc3_cleanup_done_trbs(dwc, dep, req, trb,
-					event, status);
-			if (ret)
-				break;
-		}while (++i < req->request.num_mapped_sgs);
-
-		dwc3_gadget_giveback(dep, req, status);
+			slot++;
+		slot %= DWC3_TRB_NUM;
+		trb = &dep->trb_pool[slot];
 
 
+		ret = __dwc3_cleanup_done_trbs(dwc, dep, req, trb,
+				event, status);
 		if (ret)
 		if (ret)
 			break;
 			break;
-	} while (1);
+	} while (++i < req->request.num_mapped_sgs);
+
+	dwc3_gadget_giveback(dep, req, status);
 
 
 	if (usb_endpoint_xfer_isoc(dep->endpoint.desc) &&
 	if (usb_endpoint_xfer_isoc(dep->endpoint.desc) &&
 			list_empty(&dep->req_queued)) {
 			list_empty(&dep->req_queued)) {

+ 1 - 3
drivers/usb/dwc3/host.c

@@ -49,9 +49,7 @@ int dwc3_host_init(struct dwc3 *dwc)
 
 
 	memset(&pdata, 0, sizeof(pdata));
 	memset(&pdata, 0, sizeof(pdata));
 
 
-#ifdef CONFIG_DWC3_HOST_USB3_LPM_ENABLE
-	pdata.usb3_lpm_capable = 1;
-#endif
+	pdata.usb3_lpm_capable = dwc->usb3_lpm_capable;
 
 
 	ret = platform_device_add_data(xhci, &pdata, sizeof(pdata));
 	ret = platform_device_add_data(xhci, &pdata, sizeof(pdata));
 	if (ret) {
 	if (ret) {

+ 1 - 0
drivers/usb/dwc3/platform_data.h

@@ -24,6 +24,7 @@ struct dwc3_platform_data {
 	enum usb_device_speed maximum_speed;
 	enum usb_device_speed maximum_speed;
 	enum usb_dr_mode dr_mode;
 	enum usb_dr_mode dr_mode;
 	bool tx_fifo_resize;
 	bool tx_fifo_resize;
+	bool usb3_lpm_capable;
 
 
 	unsigned is_utmi_l1_suspend:1;
 	unsigned is_utmi_l1_suspend:1;
 	u8 hird_threshold;
 	u8 hird_threshold;

+ 17 - 0
drivers/usb/gadget/Kconfig

@@ -196,6 +196,9 @@ config USB_F_MIDI
 config USB_F_HID
 config USB_F_HID
 	tristate
 	tristate
 
 
+config USB_F_PRINTER
+	tristate
+
 choice
 choice
 	tristate "USB Gadget Drivers"
 	tristate "USB Gadget Drivers"
 	default USB_ETH
 	default USB_ETH
@@ -434,6 +437,20 @@ config USB_CONFIGFS_F_UVC
 	  device. It provides a userspace API to process UVC control requests
 	  device. It provides a userspace API to process UVC control requests
 	  and stream video data to the host.
 	  and stream video data to the host.
 
 
+config USB_CONFIGFS_F_PRINTER
+	bool "Printer function"
+	select USB_F_PRINTER
+	depends on USB_CONFIGFS
+	help
+	  The Printer function channels data between the USB host and a
+	  userspace program driving the print engine. The user space
+	  program reads and writes the device file /dev/g_printer<X> to
+	  receive or send printer data. It can use ioctl calls to
+	  the device file to get or set printer status.
+
+	  For more information, see Documentation/usb/gadget_printer.txt
+	  which includes sample code for accessing the device file.
+
 source "drivers/usb/gadget/legacy/Kconfig"
 source "drivers/usb/gadget/legacy/Kconfig"
 
 
 endchoice
 endchoice

+ 14 - 3
drivers/usb/gadget/composite.c

@@ -1161,11 +1161,11 @@ static struct usb_gadget_string_container *copy_gadget_strings(
  * This function will create a deep copy of usb_gadget_strings and usb_string
  * This function will create a deep copy of usb_gadget_strings and usb_string
  * and attach it to the cdev. The actual string (usb_string.s) will not be
  * and attach it to the cdev. The actual string (usb_string.s) will not be
  * copied but only a referenced will be made. The struct usb_gadget_strings
  * copied but only a referenced will be made. The struct usb_gadget_strings
- * array may contain multiple languges and should be NULL terminated.
+ * array may contain multiple languages and should be NULL terminated.
  * The ->language pointer of each struct usb_gadget_strings has to contain the
  * The ->language pointer of each struct usb_gadget_strings has to contain the
  * same amount of entries.
  * same amount of entries.
  * For instance: sp[0] is en-US, sp[1] is es-ES. It is expected that the first
  * For instance: sp[0] is en-US, sp[1] is es-ES. It is expected that the first
- * usb_string entry of es-ES containts the translation of the first usb_string
+ * usb_string entry of es-ES contains the translation of the first usb_string
  * entry of en-US. Therefore both entries become the same id assign.
  * entry of en-US. Therefore both entries become the same id assign.
  */
  */
 struct usb_string *usb_gstrings_attach(struct usb_composite_dev *cdev,
 struct usb_string *usb_gstrings_attach(struct usb_composite_dev *cdev,
@@ -1472,6 +1472,13 @@ composite_setup(struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl)
 	req->length = 0;
 	req->length = 0;
 	gadget->ep0->driver_data = cdev;
 	gadget->ep0->driver_data = cdev;
 
 
+	/*
+	 * Don't let non-standard requests match any of the cases below
+	 * by accident.
+	 */
+	if ((ctrl->bRequestType & USB_TYPE_MASK) != USB_TYPE_STANDARD)
+		goto unknown;
+
 	switch (ctrl->bRequest) {
 	switch (ctrl->bRequest) {
 
 
 	/* we handle all standard USB descriptors */
 	/* we handle all standard USB descriptors */
@@ -1751,6 +1758,10 @@ unknown:
 		 * take such requests too, if that's ever needed:  to work
 		 * take such requests too, if that's ever needed:  to work
 		 * in config 0, etc.
 		 * in config 0, etc.
 		 */
 		 */
+		list_for_each_entry(f, &cdev->config->functions, list)
+			if (f->req_match && f->req_match(f, ctrl))
+				goto try_fun_setup;
+		f = NULL;
 		switch (ctrl->bRequestType & USB_RECIP_MASK) {
 		switch (ctrl->bRequestType & USB_RECIP_MASK) {
 		case USB_RECIP_INTERFACE:
 		case USB_RECIP_INTERFACE:
 			if (!cdev->config || intf >= MAX_CONFIG_INTERFACES)
 			if (!cdev->config || intf >= MAX_CONFIG_INTERFACES)
@@ -1768,7 +1779,7 @@ unknown:
 				f = NULL;
 				f = NULL;
 			break;
 			break;
 		}
 		}
-
+try_fun_setup:
 		if (f && f->setup)
 		if (f && f->setup)
 			value = f->setup(f, ctrl);
 			value = f->setup(f, ctrl);
 		else {
 		else {

+ 2 - 0
drivers/usb/gadget/function/Makefile

@@ -42,3 +42,5 @@ usb_f_midi-y			:= f_midi.o
 obj-$(CONFIG_USB_F_MIDI)	+= usb_f_midi.o
 obj-$(CONFIG_USB_F_MIDI)	+= usb_f_midi.o
 usb_f_hid-y			:= f_hid.o
 usb_f_hid-y			:= f_hid.o
 obj-$(CONFIG_USB_F_HID)		+= usb_f_hid.o
 obj-$(CONFIG_USB_F_HID)		+= usb_f_hid.o
+usb_f_printer-y			:= f_printer.o
+obj-$(CONFIG_USB_F_PRINTER)	+= usb_f_printer.o

+ 0 - 1
drivers/usb/gadget/function/f_hid.c

@@ -908,7 +908,6 @@ static void hidg_unbind(struct usb_configuration *c, struct usb_function *f)
 
 
 	/* disable/free request and end point */
 	/* disable/free request and end point */
 	usb_ep_disable(hidg->in_ep);
 	usb_ep_disable(hidg->in_ep);
-	usb_ep_dequeue(hidg->in_ep, hidg->req);
 	kfree(hidg->req->buf);
 	kfree(hidg->req->buf);
 	usb_ep_free_request(hidg->in_ep, hidg->req);
 	usb_ep_free_request(hidg->in_ep, hidg->req);
 
 

+ 1 - 1
drivers/usb/gadget/function/f_mass_storage.c

@@ -1085,7 +1085,7 @@ static int do_inquiry(struct fsg_common *common, struct fsg_buffhd *bh)
 	if (!curlun) {		/* Unsupported LUNs are okay */
 	if (!curlun) {		/* Unsupported LUNs are okay */
 		common->bad_lun_okay = 1;
 		common->bad_lun_okay = 1;
 		memset(buf, 0, 36);
 		memset(buf, 0, 36);
-		buf[0] = 0x7f;		/* Unsupported, no device-type */
+		buf[0] = TYPE_NO_LUN;	/* Unsupported, no device-type */
 		buf[4] = 31;		/* Additional length */
 		buf[4] = 31;		/* Additional length */
 		return 36;
 		return 36;
 	}
 	}

+ 1471 - 0
drivers/usb/gadget/function/f_printer.c

@@ -0,0 +1,1471 @@
+/*
+ * f_printer.c - USB printer function driver
+ *
+ * Copied from drivers/usb/gadget/legacy/printer.c,
+ * which was:
+ *
+ * printer.c -- Printer gadget driver
+ *
+ * Copyright (C) 2003-2005 David Brownell
+ * Copyright (C) 2006 Craig W. Nadler
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/ioport.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/mutex.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/idr.h>
+#include <linux/timer.h>
+#include <linux/list.h>
+#include <linux/interrupt.h>
+#include <linux/device.h>
+#include <linux/moduleparam.h>
+#include <linux/fs.h>
+#include <linux/poll.h>
+#include <linux/types.h>
+#include <linux/ctype.h>
+#include <linux/cdev.h>
+
+#include <asm/byteorder.h>
+#include <linux/io.h>
+#include <linux/irq.h>
+#include <linux/uaccess.h>
+#include <asm/unaligned.h>
+
+#include <linux/usb/ch9.h>
+#include <linux/usb/composite.h>
+#include <linux/usb/gadget.h>
+#include <linux/usb/g_printer.h>
+
+#include "u_printer.h"
+
+#define PNP_STRING_LEN		1024
+#define PRINTER_MINORS		4
+#define GET_DEVICE_ID		0
+#define GET_PORT_STATUS		1
+#define SOFT_RESET		2
+
+static int major, minors;
+static struct class *usb_gadget_class;
+static DEFINE_IDA(printer_ida);
+static DEFINE_MUTEX(printer_ida_lock); /* protects access do printer_ida */
+
+/*-------------------------------------------------------------------------*/
+
+struct printer_dev {
+	spinlock_t		lock;		/* lock this structure */
+	/* lock buffer lists during read/write calls */
+	struct mutex		lock_printer_io;
+	struct usb_gadget	*gadget;
+	s8			interface;
+	struct usb_ep		*in_ep, *out_ep;
+
+	struct list_head	rx_reqs;	/* List of free RX structs */
+	struct list_head	rx_reqs_active;	/* List of Active RX xfers */
+	struct list_head	rx_buffers;	/* List of completed xfers */
+	/* wait until there is data to be read. */
+	wait_queue_head_t	rx_wait;
+	struct list_head	tx_reqs;	/* List of free TX structs */
+	struct list_head	tx_reqs_active; /* List of Active TX xfers */
+	/* Wait until there are write buffers available to use. */
+	wait_queue_head_t	tx_wait;
+	/* Wait until all write buffers have been sent. */
+	wait_queue_head_t	tx_flush_wait;
+	struct usb_request	*current_rx_req;
+	size_t			current_rx_bytes;
+	u8			*current_rx_buf;
+	u8			printer_status;
+	u8			reset_printer;
+	int			minor;
+	struct cdev		printer_cdev;
+	u8			printer_cdev_open;
+	wait_queue_head_t	wait;
+	unsigned		q_len;
+	char			*pnp_string;	/* We don't own memory! */
+	struct usb_function	function;
+};
+
+static inline struct printer_dev *func_to_printer(struct usb_function *f)
+{
+	return container_of(f, struct printer_dev, function);
+}
+
+/*-------------------------------------------------------------------------*/
+
+/*
+ * DESCRIPTORS ... most are static, but strings and (full) configuration
+ * descriptors are built on demand.
+ */
+
+/* holds our biggest descriptor */
+#define USB_DESC_BUFSIZE		256
+#define USB_BUFSIZE			8192
+
+static struct usb_interface_descriptor intf_desc = {
+	.bLength =		sizeof(intf_desc),
+	.bDescriptorType =	USB_DT_INTERFACE,
+	.bNumEndpoints =	2,
+	.bInterfaceClass =	USB_CLASS_PRINTER,
+	.bInterfaceSubClass =	1,	/* Printer Sub-Class */
+	.bInterfaceProtocol =	2,	/* Bi-Directional */
+	.iInterface =		0
+};
+
+static struct usb_endpoint_descriptor fs_ep_in_desc = {
+	.bLength =		USB_DT_ENDPOINT_SIZE,
+	.bDescriptorType =	USB_DT_ENDPOINT,
+	.bEndpointAddress =	USB_DIR_IN,
+	.bmAttributes =		USB_ENDPOINT_XFER_BULK
+};
+
+static struct usb_endpoint_descriptor fs_ep_out_desc = {
+	.bLength =		USB_DT_ENDPOINT_SIZE,
+	.bDescriptorType =	USB_DT_ENDPOINT,
+	.bEndpointAddress =	USB_DIR_OUT,
+	.bmAttributes =		USB_ENDPOINT_XFER_BULK
+};
+
+static struct usb_descriptor_header *fs_printer_function[] = {
+	(struct usb_descriptor_header *) &intf_desc,
+	(struct usb_descriptor_header *) &fs_ep_in_desc,
+	(struct usb_descriptor_header *) &fs_ep_out_desc,
+	NULL
+};
+
+/*
+ * usb 2.0 devices need to expose both high speed and full speed
+ * descriptors, unless they only run at full speed.
+ */
+
+static struct usb_endpoint_descriptor hs_ep_in_desc = {
+	.bLength =		USB_DT_ENDPOINT_SIZE,
+	.bDescriptorType =	USB_DT_ENDPOINT,
+	.bmAttributes =		USB_ENDPOINT_XFER_BULK,
+	.wMaxPacketSize =	cpu_to_le16(512)
+};
+
+static struct usb_endpoint_descriptor hs_ep_out_desc = {
+	.bLength =		USB_DT_ENDPOINT_SIZE,
+	.bDescriptorType =	USB_DT_ENDPOINT,
+	.bmAttributes =		USB_ENDPOINT_XFER_BULK,
+	.wMaxPacketSize =	cpu_to_le16(512)
+};
+
+static struct usb_qualifier_descriptor dev_qualifier = {
+	.bLength =		sizeof(dev_qualifier),
+	.bDescriptorType =	USB_DT_DEVICE_QUALIFIER,
+	.bcdUSB =		cpu_to_le16(0x0200),
+	.bDeviceClass =		USB_CLASS_PRINTER,
+	.bNumConfigurations =	1
+};
+
+static struct usb_descriptor_header *hs_printer_function[] = {
+	(struct usb_descriptor_header *) &intf_desc,
+	(struct usb_descriptor_header *) &hs_ep_in_desc,
+	(struct usb_descriptor_header *) &hs_ep_out_desc,
+	NULL
+};
+
+/*
+ * Added endpoint descriptors for 3.0 devices
+ */
+
+static struct usb_endpoint_descriptor ss_ep_in_desc = {
+	.bLength =              USB_DT_ENDPOINT_SIZE,
+	.bDescriptorType =      USB_DT_ENDPOINT,
+	.bmAttributes =         USB_ENDPOINT_XFER_BULK,
+	.wMaxPacketSize =       cpu_to_le16(1024),
+};
+
+static struct usb_ss_ep_comp_descriptor ss_ep_in_comp_desc = {
+	.bLength =              sizeof(ss_ep_in_comp_desc),
+	.bDescriptorType =      USB_DT_SS_ENDPOINT_COMP,
+};
+
+static struct usb_endpoint_descriptor ss_ep_out_desc = {
+	.bLength =              USB_DT_ENDPOINT_SIZE,
+	.bDescriptorType =      USB_DT_ENDPOINT,
+	.bmAttributes =         USB_ENDPOINT_XFER_BULK,
+	.wMaxPacketSize =       cpu_to_le16(1024),
+};
+
+static struct usb_ss_ep_comp_descriptor ss_ep_out_comp_desc = {
+	.bLength =              sizeof(ss_ep_out_comp_desc),
+	.bDescriptorType =      USB_DT_SS_ENDPOINT_COMP,
+};
+
+static struct usb_descriptor_header *ss_printer_function[] = {
+	(struct usb_descriptor_header *) &intf_desc,
+	(struct usb_descriptor_header *) &ss_ep_in_desc,
+	(struct usb_descriptor_header *) &ss_ep_in_comp_desc,
+	(struct usb_descriptor_header *) &ss_ep_out_desc,
+	(struct usb_descriptor_header *) &ss_ep_out_comp_desc,
+	NULL
+};
+
+/* maxpacket and other transfer characteristics vary by speed. */
+static inline struct usb_endpoint_descriptor *ep_desc(struct usb_gadget *gadget,
+					struct usb_endpoint_descriptor *fs,
+					struct usb_endpoint_descriptor *hs,
+					struct usb_endpoint_descriptor *ss)
+{
+	switch (gadget->speed) {
+	case USB_SPEED_SUPER:
+		return ss;
+	case USB_SPEED_HIGH:
+		return hs;
+	default:
+		return fs;
+	}
+}
+
+/*-------------------------------------------------------------------------*/
+
+static struct usb_request *
+printer_req_alloc(struct usb_ep *ep, unsigned len, gfp_t gfp_flags)
+{
+	struct usb_request	*req;
+
+	req = usb_ep_alloc_request(ep, gfp_flags);
+
+	if (req != NULL) {
+		req->length = len;
+		req->buf = kmalloc(len, gfp_flags);
+		if (req->buf == NULL) {
+			usb_ep_free_request(ep, req);
+			return NULL;
+		}
+	}
+
+	return req;
+}
+
+static void
+printer_req_free(struct usb_ep *ep, struct usb_request *req)
+{
+	if (ep != NULL && req != NULL) {
+		kfree(req->buf);
+		usb_ep_free_request(ep, req);
+	}
+}
+
+/*-------------------------------------------------------------------------*/
+
+static void rx_complete(struct usb_ep *ep, struct usb_request *req)
+{
+	struct printer_dev	*dev = ep->driver_data;
+	int			status = req->status;
+	unsigned long		flags;
+
+	spin_lock_irqsave(&dev->lock, flags);
+
+	list_del_init(&req->list);	/* Remode from Active List */
+
+	switch (status) {
+
+	/* normal completion */
+	case 0:
+		if (req->actual > 0) {
+			list_add_tail(&req->list, &dev->rx_buffers);
+			DBG(dev, "G_Printer : rx length %d\n", req->actual);
+		} else {
+			list_add(&req->list, &dev->rx_reqs);
+		}
+		break;
+
+	/* software-driven interface shutdown */
+	case -ECONNRESET:		/* unlink */
+	case -ESHUTDOWN:		/* disconnect etc */
+		VDBG(dev, "rx shutdown, code %d\n", status);
+		list_add(&req->list, &dev->rx_reqs);
+		break;
+
+	/* for hardware automagic (such as pxa) */
+	case -ECONNABORTED:		/* endpoint reset */
+		DBG(dev, "rx %s reset\n", ep->name);
+		list_add(&req->list, &dev->rx_reqs);
+		break;
+
+	/* data overrun */
+	case -EOVERFLOW:
+		/* FALLTHROUGH */
+
+	default:
+		DBG(dev, "rx status %d\n", status);
+		list_add(&req->list, &dev->rx_reqs);
+		break;
+	}
+
+	wake_up_interruptible(&dev->rx_wait);
+	spin_unlock_irqrestore(&dev->lock, flags);
+}
+
+static void tx_complete(struct usb_ep *ep, struct usb_request *req)
+{
+	struct printer_dev	*dev = ep->driver_data;
+
+	switch (req->status) {
+	default:
+		VDBG(dev, "tx err %d\n", req->status);
+		/* FALLTHROUGH */
+	case -ECONNRESET:		/* unlink */
+	case -ESHUTDOWN:		/* disconnect etc */
+		break;
+	case 0:
+		break;
+	}
+
+	spin_lock(&dev->lock);
+	/* Take the request struct off the active list and put it on the
+	 * free list.
+	 */
+	list_del_init(&req->list);
+	list_add(&req->list, &dev->tx_reqs);
+	wake_up_interruptible(&dev->tx_wait);
+	if (likely(list_empty(&dev->tx_reqs_active)))
+		wake_up_interruptible(&dev->tx_flush_wait);
+
+	spin_unlock(&dev->lock);
+}
+
+/*-------------------------------------------------------------------------*/
+
+static int
+printer_open(struct inode *inode, struct file *fd)
+{
+	struct printer_dev	*dev;
+	unsigned long		flags;
+	int			ret = -EBUSY;
+
+	dev = container_of(inode->i_cdev, struct printer_dev, printer_cdev);
+
+	spin_lock_irqsave(&dev->lock, flags);
+
+	if (!dev->printer_cdev_open) {
+		dev->printer_cdev_open = 1;
+		fd->private_data = dev;
+		ret = 0;
+		/* Change the printer status to show that it's on-line. */
+		dev->printer_status |= PRINTER_SELECTED;
+	}
+
+	spin_unlock_irqrestore(&dev->lock, flags);
+
+	DBG(dev, "printer_open returned %x\n", ret);
+	return ret;
+}
+
+static int
+printer_close(struct inode *inode, struct file *fd)
+{
+	struct printer_dev	*dev = fd->private_data;
+	unsigned long		flags;
+
+	spin_lock_irqsave(&dev->lock, flags);
+	dev->printer_cdev_open = 0;
+	fd->private_data = NULL;
+	/* Change printer status to show that the printer is off-line. */
+	dev->printer_status &= ~PRINTER_SELECTED;
+	spin_unlock_irqrestore(&dev->lock, flags);
+
+	DBG(dev, "printer_close\n");
+
+	return 0;
+}
+
+/* This function must be called with interrupts turned off. */
+static void
+setup_rx_reqs(struct printer_dev *dev)
+{
+	struct usb_request              *req;
+
+	while (likely(!list_empty(&dev->rx_reqs))) {
+		int error;
+
+		req = container_of(dev->rx_reqs.next,
+				struct usb_request, list);
+		list_del_init(&req->list);
+
+		/* The USB Host sends us whatever amount of data it wants to
+		 * so we always set the length field to the full USB_BUFSIZE.
+		 * If the amount of data is more than the read() caller asked
+		 * for it will be stored in the request buffer until it is
+		 * asked for by read().
+		 */
+		req->length = USB_BUFSIZE;
+		req->complete = rx_complete;
+
+		/* here, we unlock, and only unlock, to avoid deadlock. */
+		spin_unlock(&dev->lock);
+		error = usb_ep_queue(dev->out_ep, req, GFP_ATOMIC);
+		spin_lock(&dev->lock);
+		if (error) {
+			DBG(dev, "rx submit --> %d\n", error);
+			list_add(&req->list, &dev->rx_reqs);
+			break;
+		}
+		/* if the req is empty, then add it into dev->rx_reqs_active. */
+		else if (list_empty(&req->list))
+			list_add(&req->list, &dev->rx_reqs_active);
+	}
+}
+
+static ssize_t
+printer_read(struct file *fd, char __user *buf, size_t len, loff_t *ptr)
+{
+	struct printer_dev		*dev = fd->private_data;
+	unsigned long			flags;
+	size_t				size;
+	size_t				bytes_copied;
+	struct usb_request		*req;
+	/* This is a pointer to the current USB rx request. */
+	struct usb_request		*current_rx_req;
+	/* This is the number of bytes in the current rx buffer. */
+	size_t				current_rx_bytes;
+	/* This is a pointer to the current rx buffer. */
+	u8				*current_rx_buf;
+
+	if (len == 0)
+		return -EINVAL;
+
+	DBG(dev, "printer_read trying to read %d bytes\n", (int)len);
+
+	mutex_lock(&dev->lock_printer_io);
+	spin_lock_irqsave(&dev->lock, flags);
+
+	/* We will use this flag later to check if a printer reset happened
+	 * after we turn interrupts back on.
+	 */
+	dev->reset_printer = 0;
+
+	setup_rx_reqs(dev);
+
+	bytes_copied = 0;
+	current_rx_req = dev->current_rx_req;
+	current_rx_bytes = dev->current_rx_bytes;
+	current_rx_buf = dev->current_rx_buf;
+	dev->current_rx_req = NULL;
+	dev->current_rx_bytes = 0;
+	dev->current_rx_buf = NULL;
+
+	/* Check if there is any data in the read buffers. Please note that
+	 * current_rx_bytes is the number of bytes in the current rx buffer.
+	 * If it is zero then check if there are any other rx_buffers that
+	 * are on the completed list. We are only out of data if all rx
+	 * buffers are empty.
+	 */
+	if ((current_rx_bytes == 0) &&
+			(likely(list_empty(&dev->rx_buffers)))) {
+		/* Turn interrupts back on before sleeping. */
+		spin_unlock_irqrestore(&dev->lock, flags);
+
+		/*
+		 * If no data is available check if this is a NON-Blocking
+		 * call or not.
+		 */
+		if (fd->f_flags & (O_NONBLOCK|O_NDELAY)) {
+			mutex_unlock(&dev->lock_printer_io);
+			return -EAGAIN;
+		}
+
+		/* Sleep until data is available */
+		wait_event_interruptible(dev->rx_wait,
+				(likely(!list_empty(&dev->rx_buffers))));
+		spin_lock_irqsave(&dev->lock, flags);
+	}
+
+	/* We have data to return then copy it to the caller's buffer.*/
+	while ((current_rx_bytes || likely(!list_empty(&dev->rx_buffers)))
+			&& len) {
+		if (current_rx_bytes == 0) {
+			req = container_of(dev->rx_buffers.next,
+					struct usb_request, list);
+			list_del_init(&req->list);
+
+			if (req->actual && req->buf) {
+				current_rx_req = req;
+				current_rx_bytes = req->actual;
+				current_rx_buf = req->buf;
+			} else {
+				list_add(&req->list, &dev->rx_reqs);
+				continue;
+			}
+		}
+
+		/* Don't leave irqs off while doing memory copies */
+		spin_unlock_irqrestore(&dev->lock, flags);
+
+		if (len > current_rx_bytes)
+			size = current_rx_bytes;
+		else
+			size = len;
+
+		size -= copy_to_user(buf, current_rx_buf, size);
+		bytes_copied += size;
+		len -= size;
+		buf += size;
+
+		spin_lock_irqsave(&dev->lock, flags);
+
+		/* We've disconnected or reset so return. */
+		if (dev->reset_printer) {
+			list_add(&current_rx_req->list, &dev->rx_reqs);
+			spin_unlock_irqrestore(&dev->lock, flags);
+			mutex_unlock(&dev->lock_printer_io);
+			return -EAGAIN;
+		}
+
+		/* If we not returning all the data left in this RX request
+		 * buffer then adjust the amount of data left in the buffer.
+		 * Othewise if we are done with this RX request buffer then
+		 * requeue it to get any incoming data from the USB host.
+		 */
+		if (size < current_rx_bytes) {
+			current_rx_bytes -= size;
+			current_rx_buf += size;
+		} else {
+			list_add(&current_rx_req->list, &dev->rx_reqs);
+			current_rx_bytes = 0;
+			current_rx_buf = NULL;
+			current_rx_req = NULL;
+		}
+	}
+
+	dev->current_rx_req = current_rx_req;
+	dev->current_rx_bytes = current_rx_bytes;
+	dev->current_rx_buf = current_rx_buf;
+
+	spin_unlock_irqrestore(&dev->lock, flags);
+	mutex_unlock(&dev->lock_printer_io);
+
+	DBG(dev, "printer_read returned %d bytes\n", (int)bytes_copied);
+
+	if (bytes_copied)
+		return bytes_copied;
+	else
+		return -EAGAIN;
+}
+
+static ssize_t
+printer_write(struct file *fd, const char __user *buf, size_t len, loff_t *ptr)
+{
+	struct printer_dev	*dev = fd->private_data;
+	unsigned long		flags;
+	size_t			size;	/* Amount of data in a TX request. */
+	size_t			bytes_copied = 0;
+	struct usb_request	*req;
+
+	DBG(dev, "printer_write trying to send %d bytes\n", (int)len);
+
+	if (len == 0)
+		return -EINVAL;
+
+	mutex_lock(&dev->lock_printer_io);
+	spin_lock_irqsave(&dev->lock, flags);
+
+	/* Check if a printer reset happens while we have interrupts on */
+	dev->reset_printer = 0;
+
+	/* Check if there is any available write buffers */
+	if (likely(list_empty(&dev->tx_reqs))) {
+		/* Turn interrupts back on before sleeping. */
+		spin_unlock_irqrestore(&dev->lock, flags);
+
+		/*
+		 * If write buffers are available check if this is
+		 * a NON-Blocking call or not.
+		 */
+		if (fd->f_flags & (O_NONBLOCK|O_NDELAY)) {
+			mutex_unlock(&dev->lock_printer_io);
+			return -EAGAIN;
+		}
+
+		/* Sleep until a write buffer is available */
+		wait_event_interruptible(dev->tx_wait,
+				(likely(!list_empty(&dev->tx_reqs))));
+		spin_lock_irqsave(&dev->lock, flags);
+	}
+
+	while (likely(!list_empty(&dev->tx_reqs)) && len) {
+
+		if (len > USB_BUFSIZE)
+			size = USB_BUFSIZE;
+		else
+			size = len;
+
+		req = container_of(dev->tx_reqs.next, struct usb_request,
+				list);
+		list_del_init(&req->list);
+
+		req->complete = tx_complete;
+		req->length = size;
+
+		/* Check if we need to send a zero length packet. */
+		if (len > size)
+			/* They will be more TX requests so no yet. */
+			req->zero = 0;
+		else
+			/* If the data amount is not a multiple of the
+			 * maxpacket size then send a zero length packet.
+			 */
+			req->zero = ((len % dev->in_ep->maxpacket) == 0);
+
+		/* Don't leave irqs off while doing memory copies */
+		spin_unlock_irqrestore(&dev->lock, flags);
+
+		if (copy_from_user(req->buf, buf, size)) {
+			list_add(&req->list, &dev->tx_reqs);
+			mutex_unlock(&dev->lock_printer_io);
+			return bytes_copied;
+		}
+
+		bytes_copied += size;
+		len -= size;
+		buf += size;
+
+		spin_lock_irqsave(&dev->lock, flags);
+
+		/* We've disconnected or reset so free the req and buffer */
+		if (dev->reset_printer) {
+			list_add(&req->list, &dev->tx_reqs);
+			spin_unlock_irqrestore(&dev->lock, flags);
+			mutex_unlock(&dev->lock_printer_io);
+			return -EAGAIN;
+		}
+
+		if (usb_ep_queue(dev->in_ep, req, GFP_ATOMIC)) {
+			list_add(&req->list, &dev->tx_reqs);
+			spin_unlock_irqrestore(&dev->lock, flags);
+			mutex_unlock(&dev->lock_printer_io);
+			return -EAGAIN;
+		}
+
+		list_add(&req->list, &dev->tx_reqs_active);
+
+	}
+
+	spin_unlock_irqrestore(&dev->lock, flags);
+	mutex_unlock(&dev->lock_printer_io);
+
+	DBG(dev, "printer_write sent %d bytes\n", (int)bytes_copied);
+
+	if (bytes_copied)
+		return bytes_copied;
+	else
+		return -EAGAIN;
+}
+
+static int
+printer_fsync(struct file *fd, loff_t start, loff_t end, int datasync)
+{
+	struct printer_dev	*dev = fd->private_data;
+	struct inode *inode = file_inode(fd);
+	unsigned long		flags;
+	int			tx_list_empty;
+
+	mutex_lock(&inode->i_mutex);
+	spin_lock_irqsave(&dev->lock, flags);
+	tx_list_empty = (likely(list_empty(&dev->tx_reqs)));
+	spin_unlock_irqrestore(&dev->lock, flags);
+
+	if (!tx_list_empty) {
+		/* Sleep until all data has been sent */
+		wait_event_interruptible(dev->tx_flush_wait,
+				(likely(list_empty(&dev->tx_reqs_active))));
+	}
+	mutex_unlock(&inode->i_mutex);
+
+	return 0;
+}
+
+static unsigned int
+printer_poll(struct file *fd, poll_table *wait)
+{
+	struct printer_dev	*dev = fd->private_data;
+	unsigned long		flags;
+	int			status = 0;
+
+	mutex_lock(&dev->lock_printer_io);
+	spin_lock_irqsave(&dev->lock, flags);
+	setup_rx_reqs(dev);
+	spin_unlock_irqrestore(&dev->lock, flags);
+	mutex_unlock(&dev->lock_printer_io);
+
+	poll_wait(fd, &dev->rx_wait, wait);
+	poll_wait(fd, &dev->tx_wait, wait);
+
+	spin_lock_irqsave(&dev->lock, flags);
+	if (likely(!list_empty(&dev->tx_reqs)))
+		status |= POLLOUT | POLLWRNORM;
+
+	if (likely(dev->current_rx_bytes) ||
+			likely(!list_empty(&dev->rx_buffers)))
+		status |= POLLIN | POLLRDNORM;
+
+	spin_unlock_irqrestore(&dev->lock, flags);
+
+	return status;
+}
+
+static long
+printer_ioctl(struct file *fd, unsigned int code, unsigned long arg)
+{
+	struct printer_dev	*dev = fd->private_data;
+	unsigned long		flags;
+	int			status = 0;
+
+	DBG(dev, "printer_ioctl: cmd=0x%4.4x, arg=%lu\n", code, arg);
+
+	/* handle ioctls */
+
+	spin_lock_irqsave(&dev->lock, flags);
+
+	switch (code) {
+	case GADGET_GET_PRINTER_STATUS:
+		status = (int)dev->printer_status;
+		break;
+	case GADGET_SET_PRINTER_STATUS:
+		dev->printer_status = (u8)arg;
+		break;
+	default:
+		/* could not handle ioctl */
+		DBG(dev, "printer_ioctl: ERROR cmd=0x%4.4xis not supported\n",
+				code);
+		status = -ENOTTY;
+	}
+
+	spin_unlock_irqrestore(&dev->lock, flags);
+
+	return status;
+}
+
+/* used after endpoint configuration */
+static const struct file_operations printer_io_operations = {
+	.owner =	THIS_MODULE,
+	.open =		printer_open,
+	.read =		printer_read,
+	.write =	printer_write,
+	.fsync =	printer_fsync,
+	.poll =		printer_poll,
+	.unlocked_ioctl = printer_ioctl,
+	.release =	printer_close,
+	.llseek =	noop_llseek,
+};
+
+/*-------------------------------------------------------------------------*/
+
+static int
+set_printer_interface(struct printer_dev *dev)
+{
+	int			result = 0;
+
+	dev->in_ep->desc = ep_desc(dev->gadget, &fs_ep_in_desc, &hs_ep_in_desc,
+				&ss_ep_in_desc);
+	dev->in_ep->driver_data = dev;
+
+	dev->out_ep->desc = ep_desc(dev->gadget, &fs_ep_out_desc,
+				    &hs_ep_out_desc, &ss_ep_out_desc);
+	dev->out_ep->driver_data = dev;
+
+	result = usb_ep_enable(dev->in_ep);
+	if (result != 0) {
+		DBG(dev, "enable %s --> %d\n", dev->in_ep->name, result);
+		goto done;
+	}
+
+	result = usb_ep_enable(dev->out_ep);
+	if (result != 0) {
+		DBG(dev, "enable %s --> %d\n", dev->in_ep->name, result);
+		goto done;
+	}
+
+done:
+	/* on error, disable any endpoints  */
+	if (result != 0) {
+		(void) usb_ep_disable(dev->in_ep);
+		(void) usb_ep_disable(dev->out_ep);
+		dev->in_ep->desc = NULL;
+		dev->out_ep->desc = NULL;
+	}
+
+	/* caller is responsible for cleanup on error */
+	return result;
+}
+
+static void printer_reset_interface(struct printer_dev *dev)
+{
+	if (dev->interface < 0)
+		return;
+
+	DBG(dev, "%s\n", __func__);
+
+	if (dev->in_ep->desc)
+		usb_ep_disable(dev->in_ep);
+
+	if (dev->out_ep->desc)
+		usb_ep_disable(dev->out_ep);
+
+	dev->in_ep->desc = NULL;
+	dev->out_ep->desc = NULL;
+	dev->interface = -1;
+}
+
+/* Change our operational Interface. */
+static int set_interface(struct printer_dev *dev, unsigned number)
+{
+	int			result = 0;
+
+	/* Free the current interface */
+	printer_reset_interface(dev);
+
+	result = set_printer_interface(dev);
+	if (result)
+		printer_reset_interface(dev);
+	else
+		dev->interface = number;
+
+	if (!result)
+		INFO(dev, "Using interface %x\n", number);
+
+	return result;
+}
+
+static void printer_soft_reset(struct printer_dev *dev)
+{
+	struct usb_request	*req;
+
+	INFO(dev, "Received Printer Reset Request\n");
+
+	if (usb_ep_disable(dev->in_ep))
+		DBG(dev, "Failed to disable USB in_ep\n");
+	if (usb_ep_disable(dev->out_ep))
+		DBG(dev, "Failed to disable USB out_ep\n");
+
+	if (dev->current_rx_req != NULL) {
+		list_add(&dev->current_rx_req->list, &dev->rx_reqs);
+		dev->current_rx_req = NULL;
+	}
+	dev->current_rx_bytes = 0;
+	dev->current_rx_buf = NULL;
+	dev->reset_printer = 1;
+
+	while (likely(!(list_empty(&dev->rx_buffers)))) {
+		req = container_of(dev->rx_buffers.next, struct usb_request,
+				list);
+		list_del_init(&req->list);
+		list_add(&req->list, &dev->rx_reqs);
+	}
+
+	while (likely(!(list_empty(&dev->rx_reqs_active)))) {
+		req = container_of(dev->rx_buffers.next, struct usb_request,
+				list);
+		list_del_init(&req->list);
+		list_add(&req->list, &dev->rx_reqs);
+	}
+
+	while (likely(!(list_empty(&dev->tx_reqs_active)))) {
+		req = container_of(dev->tx_reqs_active.next,
+				struct usb_request, list);
+		list_del_init(&req->list);
+		list_add(&req->list, &dev->tx_reqs);
+	}
+
+	if (usb_ep_enable(dev->in_ep))
+		DBG(dev, "Failed to enable USB in_ep\n");
+	if (usb_ep_enable(dev->out_ep))
+		DBG(dev, "Failed to enable USB out_ep\n");
+
+	wake_up_interruptible(&dev->rx_wait);
+	wake_up_interruptible(&dev->tx_wait);
+	wake_up_interruptible(&dev->tx_flush_wait);
+}
+
+/*-------------------------------------------------------------------------*/
+
+static bool gprinter_req_match(struct usb_function *f,
+			       const struct usb_ctrlrequest *ctrl)
+{
+	struct printer_dev	*dev = func_to_printer(f);
+	u16			w_index = le16_to_cpu(ctrl->wIndex);
+	u16			w_value = le16_to_cpu(ctrl->wValue);
+	u16			w_length = le16_to_cpu(ctrl->wLength);
+
+	if ((ctrl->bRequestType & USB_RECIP_MASK) != USB_RECIP_INTERFACE ||
+	    (ctrl->bRequestType & USB_TYPE_MASK) != USB_TYPE_CLASS)
+		return false;
+
+	switch (ctrl->bRequest) {
+	case GET_DEVICE_ID:
+		w_index >>= 8;
+		if (w_length <= PNP_STRING_LEN &&
+		    (USB_DIR_IN & ctrl->bRequestType))
+			break;
+		return false;
+	case GET_PORT_STATUS:
+		if (!w_value && w_length == 1 &&
+		    (USB_DIR_IN & ctrl->bRequestType))
+			break;
+		return false;
+	case SOFT_RESET:
+		if (!w_value && !w_length &&
+		   !(USB_DIR_IN & ctrl->bRequestType))
+			break;
+		/* fall through */
+	default:
+		return false;
+	}
+	return w_index == dev->interface;
+}
+
+/*
+ * The setup() callback implements all the ep0 functionality that's not
+ * handled lower down.
+ */
+static int printer_func_setup(struct usb_function *f,
+		const struct usb_ctrlrequest *ctrl)
+{
+	struct printer_dev *dev = func_to_printer(f);
+	struct usb_composite_dev *cdev = f->config->cdev;
+	struct usb_request	*req = cdev->req;
+	int			value = -EOPNOTSUPP;
+	u16			wIndex = le16_to_cpu(ctrl->wIndex);
+	u16			wValue = le16_to_cpu(ctrl->wValue);
+	u16			wLength = le16_to_cpu(ctrl->wLength);
+
+	DBG(dev, "ctrl req%02x.%02x v%04x i%04x l%d\n",
+		ctrl->bRequestType, ctrl->bRequest, wValue, wIndex, wLength);
+
+	switch (ctrl->bRequestType&USB_TYPE_MASK) {
+	case USB_TYPE_CLASS:
+		switch (ctrl->bRequest) {
+		case GET_DEVICE_ID: /* Get the IEEE-1284 PNP String */
+			/* Only one printer interface is supported. */
+			if ((wIndex>>8) != dev->interface)
+				break;
+
+			value = (dev->pnp_string[0] << 8) | dev->pnp_string[1];
+			memcpy(req->buf, dev->pnp_string, value);
+			DBG(dev, "1284 PNP String: %x %s\n", value,
+					&dev->pnp_string[2]);
+			break;
+
+		case GET_PORT_STATUS: /* Get Port Status */
+			/* Only one printer interface is supported. */
+			if (wIndex != dev->interface)
+				break;
+
+			*(u8 *)req->buf = dev->printer_status;
+			value = min_t(u16, wLength, 1);
+			break;
+
+		case SOFT_RESET: /* Soft Reset */
+			/* Only one printer interface is supported. */
+			if (wIndex != dev->interface)
+				break;
+
+			printer_soft_reset(dev);
+
+			value = 0;
+			break;
+
+		default:
+			goto unknown;
+		}
+		break;
+
+	default:
+unknown:
+		VDBG(dev,
+			"unknown ctrl req%02x.%02x v%04x i%04x l%d\n",
+			ctrl->bRequestType, ctrl->bRequest,
+			wValue, wIndex, wLength);
+		break;
+	}
+	/* host either stalls (value < 0) or reports success */
+	if (value >= 0) {
+		req->length = value;
+		req->zero = value < wLength;
+		value = usb_ep_queue(cdev->gadget->ep0, req, GFP_ATOMIC);
+		if (value < 0) {
+			ERROR(dev, "%s:%d Error!\n", __func__, __LINE__);
+			req->status = 0;
+		}
+	}
+	return value;
+}
+
+static int printer_func_bind(struct usb_configuration *c,
+		struct usb_function *f)
+{
+	struct usb_gadget *gadget = c->cdev->gadget;
+	struct printer_dev *dev = func_to_printer(f);
+	struct device *pdev;
+	struct usb_composite_dev *cdev = c->cdev;
+	struct usb_ep *in_ep;
+	struct usb_ep *out_ep = NULL;
+	struct usb_request *req;
+	dev_t devt;
+	int id;
+	int ret;
+	u32 i;
+
+	id = usb_interface_id(c, f);
+	if (id < 0)
+		return id;
+	intf_desc.bInterfaceNumber = id;
+
+	/* finish hookup to lower layer ... */
+	dev->gadget = gadget;
+
+	/* all we really need is bulk IN/OUT */
+	in_ep = usb_ep_autoconfig(cdev->gadget, &fs_ep_in_desc);
+	if (!in_ep) {
+autoconf_fail:
+		dev_err(&cdev->gadget->dev, "can't autoconfigure on %s\n",
+			cdev->gadget->name);
+		return -ENODEV;
+	}
+	in_ep->driver_data = in_ep;	/* claim */
+
+	out_ep = usb_ep_autoconfig(cdev->gadget, &fs_ep_out_desc);
+	if (!out_ep)
+		goto autoconf_fail;
+	out_ep->driver_data = out_ep;	/* claim */
+
+	/* assumes that all endpoints are dual-speed */
+	hs_ep_in_desc.bEndpointAddress = fs_ep_in_desc.bEndpointAddress;
+	hs_ep_out_desc.bEndpointAddress = fs_ep_out_desc.bEndpointAddress;
+	ss_ep_in_desc.bEndpointAddress = fs_ep_in_desc.bEndpointAddress;
+	ss_ep_out_desc.bEndpointAddress = fs_ep_out_desc.bEndpointAddress;
+
+	ret = usb_assign_descriptors(f, fs_printer_function,
+			hs_printer_function, ss_printer_function);
+	if (ret)
+		return ret;
+
+	dev->in_ep = in_ep;
+	dev->out_ep = out_ep;
+
+	ret = -ENOMEM;
+	for (i = 0; i < dev->q_len; i++) {
+		req = printer_req_alloc(dev->in_ep, USB_BUFSIZE, GFP_KERNEL);
+		if (!req)
+			goto fail_tx_reqs;
+		list_add(&req->list, &dev->tx_reqs);
+	}
+
+	for (i = 0; i < dev->q_len; i++) {
+		req = printer_req_alloc(dev->out_ep, USB_BUFSIZE, GFP_KERNEL);
+		if (!req)
+			goto fail_rx_reqs;
+		list_add(&req->list, &dev->rx_reqs);
+	}
+
+	/* Setup the sysfs files for the printer gadget. */
+	devt = MKDEV(major, dev->minor);
+	pdev = device_create(usb_gadget_class, NULL, devt,
+				  NULL, "g_printer%d", dev->minor);
+	if (IS_ERR(pdev)) {
+		ERROR(dev, "Failed to create device: g_printer\n");
+		ret = PTR_ERR(pdev);
+		goto fail_rx_reqs;
+	}
+
+	/*
+	 * Register a character device as an interface to a user mode
+	 * program that handles the printer specific functionality.
+	 */
+	cdev_init(&dev->printer_cdev, &printer_io_operations);
+	dev->printer_cdev.owner = THIS_MODULE;
+	ret = cdev_add(&dev->printer_cdev, devt, 1);
+	if (ret) {
+		ERROR(dev, "Failed to open char device\n");
+		goto fail_cdev_add;
+	}
+
+	return 0;
+
+fail_cdev_add:
+	device_destroy(usb_gadget_class, devt);
+
+fail_rx_reqs:
+	while (!list_empty(&dev->rx_reqs)) {
+		req = container_of(dev->rx_reqs.next, struct usb_request, list);
+		list_del(&req->list);
+		printer_req_free(dev->out_ep, req);
+	}
+
+fail_tx_reqs:
+	while (!list_empty(&dev->tx_reqs)) {
+		req = container_of(dev->tx_reqs.next, struct usb_request, list);
+		list_del(&req->list);
+		printer_req_free(dev->in_ep, req);
+	}
+
+	return ret;
+
+}
+
+static int printer_func_set_alt(struct usb_function *f,
+		unsigned intf, unsigned alt)
+{
+	struct printer_dev *dev = func_to_printer(f);
+	int ret = -ENOTSUPP;
+
+	if (!alt)
+		ret = set_interface(dev, intf);
+
+	return ret;
+}
+
+static void printer_func_disable(struct usb_function *f)
+{
+	struct printer_dev *dev = func_to_printer(f);
+	unsigned long		flags;
+
+	DBG(dev, "%s\n", __func__);
+
+	spin_lock_irqsave(&dev->lock, flags);
+	printer_reset_interface(dev);
+	spin_unlock_irqrestore(&dev->lock, flags);
+}
+
+static inline struct f_printer_opts
+*to_f_printer_opts(struct config_item *item)
+{
+	return container_of(to_config_group(item), struct f_printer_opts,
+			    func_inst.group);
+}
+
+CONFIGFS_ATTR_STRUCT(f_printer_opts);
+CONFIGFS_ATTR_OPS(f_printer_opts);
+
+static void printer_attr_release(struct config_item *item)
+{
+	struct f_printer_opts *opts = to_f_printer_opts(item);
+
+	usb_put_function_instance(&opts->func_inst);
+}
+
+static struct configfs_item_operations printer_item_ops = {
+	.release	= printer_attr_release,
+	.show_attribute	= f_printer_opts_attr_show,
+	.store_attribute = f_printer_opts_attr_store,
+};
+
+static ssize_t f_printer_opts_pnp_string_show(struct f_printer_opts *opts,
+					      char *page)
+{
+	int result;
+
+	mutex_lock(&opts->lock);
+	result = strlcpy(page, opts->pnp_string + 2, PNP_STRING_LEN - 2);
+	mutex_unlock(&opts->lock);
+
+	return result;
+}
+
+static ssize_t f_printer_opts_pnp_string_store(struct f_printer_opts *opts,
+					       const char *page, size_t len)
+{
+	int result, l;
+
+	mutex_lock(&opts->lock);
+	result = strlcpy(opts->pnp_string + 2, page, PNP_STRING_LEN - 2);
+	l = strlen(opts->pnp_string + 2) + 2;
+	opts->pnp_string[0] = (l >> 8) & 0xFF;
+	opts->pnp_string[1] = l & 0xFF;
+	mutex_unlock(&opts->lock);
+
+	return result;
+}
+
+static struct f_printer_opts_attribute f_printer_opts_pnp_string =
+	__CONFIGFS_ATTR(pnp_string, S_IRUGO | S_IWUSR,
+			f_printer_opts_pnp_string_show,
+			f_printer_opts_pnp_string_store);
+
+static ssize_t f_printer_opts_q_len_show(struct f_printer_opts *opts,
+					 char *page)
+{
+	int result;
+
+	mutex_lock(&opts->lock);
+	result = sprintf(page, "%d\n", opts->q_len);
+	mutex_unlock(&opts->lock);
+
+	return result;
+}
+
+static ssize_t f_printer_opts_q_len_store(struct f_printer_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;
+
+	opts->q_len = (unsigned)num;
+	ret = len;
+end:
+	mutex_unlock(&opts->lock);
+	return ret;
+}
+
+static struct f_printer_opts_attribute f_printer_opts_q_len =
+	__CONFIGFS_ATTR(q_len, S_IRUGO | S_IWUSR, f_printer_opts_q_len_show,
+			f_printer_opts_q_len_store);
+
+static struct configfs_attribute *printer_attrs[] = {
+	&f_printer_opts_pnp_string.attr,
+	&f_printer_opts_q_len.attr,
+	NULL,
+};
+
+static struct config_item_type printer_func_type = {
+	.ct_item_ops	= &printer_item_ops,
+	.ct_attrs	= printer_attrs,
+	.ct_owner	= THIS_MODULE,
+};
+
+static inline int gprinter_get_minor(void)
+{
+	return ida_simple_get(&printer_ida, 0, 0, GFP_KERNEL);
+}
+
+static inline void gprinter_put_minor(int minor)
+{
+	ida_simple_remove(&printer_ida, minor);
+}
+
+static int gprinter_setup(int);
+static void gprinter_cleanup(void);
+
+static void gprinter_free_inst(struct usb_function_instance *f)
+{
+	struct f_printer_opts *opts;
+
+	opts = container_of(f, struct f_printer_opts, func_inst);
+
+	mutex_lock(&printer_ida_lock);
+
+	gprinter_put_minor(opts->minor);
+	if (idr_is_empty(&printer_ida.idr))
+		gprinter_cleanup();
+
+	mutex_unlock(&printer_ida_lock);
+
+	kfree(opts);
+}
+
+static struct usb_function_instance *gprinter_alloc_inst(void)
+{
+	struct f_printer_opts *opts;
+	struct usb_function_instance *ret;
+	int status = 0;
+
+	opts = kzalloc(sizeof(*opts), GFP_KERNEL);
+	if (!opts)
+		return ERR_PTR(-ENOMEM);
+
+	mutex_init(&opts->lock);
+	opts->func_inst.free_func_inst = gprinter_free_inst;
+	ret = &opts->func_inst;
+
+	mutex_lock(&printer_ida_lock);
+
+	if (idr_is_empty(&printer_ida.idr)) {
+		status = gprinter_setup(PRINTER_MINORS);
+		if (status) {
+			ret = ERR_PTR(status);
+			kfree(opts);
+			goto unlock;
+		}
+	}
+
+	opts->minor = gprinter_get_minor();
+	if (opts->minor < 0) {
+		ret = ERR_PTR(opts->minor);
+		kfree(opts);
+		if (idr_is_empty(&printer_ida.idr))
+			gprinter_cleanup();
+		goto unlock;
+	}
+	config_group_init_type_name(&opts->func_inst.group, "",
+				    &printer_func_type);
+
+unlock:
+	mutex_unlock(&printer_ida_lock);
+	return ret;
+}
+
+static void gprinter_free(struct usb_function *f)
+{
+	struct printer_dev *dev = func_to_printer(f);
+	struct f_printer_opts *opts;
+
+	opts = container_of(f->fi, struct f_printer_opts, func_inst);
+	kfree(dev);
+	mutex_lock(&opts->lock);
+	--opts->refcnt;
+	mutex_unlock(&opts->lock);
+}
+
+static void printer_func_unbind(struct usb_configuration *c,
+		struct usb_function *f)
+{
+	struct printer_dev	*dev;
+	struct usb_request	*req;
+
+	dev = func_to_printer(f);
+
+	device_destroy(usb_gadget_class, MKDEV(major, dev->minor));
+
+	/* Remove Character Device */
+	cdev_del(&dev->printer_cdev);
+
+	/* we must already have been disconnected ... no i/o may be active */
+	WARN_ON(!list_empty(&dev->tx_reqs_active));
+	WARN_ON(!list_empty(&dev->rx_reqs_active));
+
+	/* Free all memory for this driver. */
+	while (!list_empty(&dev->tx_reqs)) {
+		req = container_of(dev->tx_reqs.next, struct usb_request,
+				list);
+		list_del(&req->list);
+		printer_req_free(dev->in_ep, req);
+	}
+
+	if (dev->current_rx_req != NULL)
+		printer_req_free(dev->out_ep, dev->current_rx_req);
+
+	while (!list_empty(&dev->rx_reqs)) {
+		req = container_of(dev->rx_reqs.next,
+				struct usb_request, list);
+		list_del(&req->list);
+		printer_req_free(dev->out_ep, req);
+	}
+
+	while (!list_empty(&dev->rx_buffers)) {
+		req = container_of(dev->rx_buffers.next,
+				struct usb_request, list);
+		list_del(&req->list);
+		printer_req_free(dev->out_ep, req);
+	}
+	usb_free_all_descriptors(f);
+}
+
+static struct usb_function *gprinter_alloc(struct usb_function_instance *fi)
+{
+	struct printer_dev	*dev;
+	struct f_printer_opts	*opts;
+
+	opts = container_of(fi, struct f_printer_opts, func_inst);
+
+	mutex_lock(&opts->lock);
+	if (opts->minor >= minors) {
+		mutex_unlock(&opts->lock);
+		return ERR_PTR(-ENOENT);
+	}
+
+	dev = kzalloc(sizeof(*dev), GFP_KERNEL);
+	if (!dev) {
+		mutex_unlock(&opts->lock);
+		return ERR_PTR(-ENOMEM);
+	}
+
+	++opts->refcnt;
+	dev->minor = opts->minor;
+	dev->pnp_string = opts->pnp_string;
+	dev->q_len = opts->q_len;
+	mutex_unlock(&opts->lock);
+
+	dev->function.name = "printer";
+	dev->function.bind = printer_func_bind;
+	dev->function.setup = printer_func_setup;
+	dev->function.unbind = printer_func_unbind;
+	dev->function.set_alt = printer_func_set_alt;
+	dev->function.disable = printer_func_disable;
+	dev->function.req_match = gprinter_req_match;
+	dev->function.free_func = gprinter_free;
+
+	INIT_LIST_HEAD(&dev->tx_reqs);
+	INIT_LIST_HEAD(&dev->rx_reqs);
+	INIT_LIST_HEAD(&dev->rx_buffers);
+	INIT_LIST_HEAD(&dev->tx_reqs_active);
+	INIT_LIST_HEAD(&dev->rx_reqs_active);
+
+	spin_lock_init(&dev->lock);
+	mutex_init(&dev->lock_printer_io);
+	init_waitqueue_head(&dev->rx_wait);
+	init_waitqueue_head(&dev->tx_wait);
+	init_waitqueue_head(&dev->tx_flush_wait);
+
+	dev->interface = -1;
+	dev->printer_cdev_open = 0;
+	dev->printer_status = PRINTER_NOT_ERROR;
+	dev->current_rx_req = NULL;
+	dev->current_rx_bytes = 0;
+	dev->current_rx_buf = NULL;
+
+	return &dev->function;
+}
+
+DECLARE_USB_FUNCTION_INIT(printer, gprinter_alloc_inst, gprinter_alloc);
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Craig Nadler");
+
+static int gprinter_setup(int count)
+{
+	int status;
+	dev_t devt;
+
+	usb_gadget_class = class_create(THIS_MODULE, "usb_printer_gadget");
+	if (IS_ERR(usb_gadget_class)) {
+		status = PTR_ERR(usb_gadget_class);
+		usb_gadget_class = NULL;
+		pr_err("unable to create usb_gadget class %d\n", status);
+		return status;
+	}
+
+	status = alloc_chrdev_region(&devt, 0, count, "USB printer gadget");
+	if (status) {
+		pr_err("alloc_chrdev_region %d\n", status);
+		class_destroy(usb_gadget_class);
+		usb_gadget_class = NULL;
+		return status;
+	}
+
+	major = MAJOR(devt);
+	minors = count;
+
+	return status;
+}
+
+static void gprinter_cleanup(void)
+{
+	if (major) {
+		unregister_chrdev_region(MKDEV(major, 0), minors);
+		major = minors = 0;
+	}
+	class_destroy(usb_gadget_class);
+	usb_gadget_class = NULL;
+}

+ 37 - 0
drivers/usb/gadget/function/u_printer.h

@@ -0,0 +1,37 @@
+/*
+ * u_printer.h
+ *
+ * Utility definitions for the printer function
+ *
+ * Copyright (c) 2015 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_PRINTER_H
+#define U_PRINTER_H
+
+#include <linux/usb/composite.h>
+
+#define PNP_STRING_LEN			1024
+
+struct f_printer_opts {
+	struct usb_function_instance	func_inst;
+	int				minor;
+	char				pnp_string[PNP_STRING_LEN];
+	unsigned			q_len;
+
+	/*
+	 * Protect the data from concurrent access by read/write
+	 * and create symlink/remove symlink
+	 */
+	struct mutex			lock;
+	int				refcnt;
+};
+
+#endif /* U_PRINTER_H */

+ 1 - 1
drivers/usb/gadget/function/u_serial.c

@@ -912,7 +912,7 @@ static int gs_put_char(struct tty_struct *tty, unsigned char ch)
 	unsigned long	flags;
 	unsigned long	flags;
 	int		status;
 	int		status;
 
 
-	pr_vdebug("gs_put_char: (%d,%p) char=0x%x, called from %pf\n",
+	pr_vdebug("gs_put_char: (%d,%p) char=0x%x, called from %ps\n",
 		port->port_num, tty, ch, __builtin_return_address(0));
 		port->port_num, tty, ch, __builtin_return_address(0));
 
 
 	spin_lock_irqsave(&port->port_lock, flags);
 	spin_lock_irqsave(&port->port_lock, flags);

+ 1 - 0
drivers/usb/gadget/legacy/Kconfig

@@ -301,6 +301,7 @@ config USB_MIDI_GADGET
 config USB_G_PRINTER
 config USB_G_PRINTER
 	tristate "Printer Gadget"
 	tristate "Printer Gadget"
 	select USB_LIBCOMPOSITE
 	select USB_LIBCOMPOSITE
+	select USB_F_PRINTER
 	help
 	help
 	  The Printer Gadget channels data between the USB host and a
 	  The Printer Gadget channels data between the USB host and a
 	  userspace program driving the print engine. The user space
 	  userspace program driving the print engine. The user space

+ 48 - 1191
drivers/usb/gadget/legacy/printer.c

@@ -12,29 +12,7 @@
 
 
 #include <linux/module.h>
 #include <linux/module.h>
 #include <linux/kernel.h>
 #include <linux/kernel.h>
-#include <linux/delay.h>
-#include <linux/ioport.h>
-#include <linux/sched.h>
-#include <linux/slab.h>
-#include <linux/mutex.h>
-#include <linux/errno.h>
-#include <linux/init.h>
-#include <linux/timer.h>
-#include <linux/list.h>
-#include <linux/interrupt.h>
-#include <linux/device.h>
-#include <linux/moduleparam.h>
-#include <linux/fs.h>
-#include <linux/poll.h>
-#include <linux/types.h>
-#include <linux/ctype.h>
-#include <linux/cdev.h>
-
 #include <asm/byteorder.h>
 #include <asm/byteorder.h>
-#include <linux/io.h>
-#include <linux/irq.h>
-#include <linux/uaccess.h>
-#include <asm/unaligned.h>
 
 
 #include <linux/usb/ch9.h>
 #include <linux/usb/ch9.h>
 #include <linux/usb/composite.h>
 #include <linux/usb/composite.h>
@@ -46,50 +24,12 @@
 USB_GADGET_COMPOSITE_OPTIONS();
 USB_GADGET_COMPOSITE_OPTIONS();
 
 
 #define DRIVER_DESC		"Printer Gadget"
 #define DRIVER_DESC		"Printer Gadget"
-#define DRIVER_VERSION		"2007 OCT 06"
+#define DRIVER_VERSION		"2015 FEB 17"
 
 
-static DEFINE_MUTEX(printer_mutex);
 static const char shortname [] = "printer";
 static const char shortname [] = "printer";
 static const char driver_desc [] = DRIVER_DESC;
 static const char driver_desc [] = DRIVER_DESC;
 
 
-static dev_t g_printer_devno;
-
-static struct class *usb_gadget_class;
-
-/*-------------------------------------------------------------------------*/
-
-struct printer_dev {
-	spinlock_t		lock;		/* lock this structure */
-	/* lock buffer lists during read/write calls */
-	struct mutex		lock_printer_io;
-	struct usb_gadget	*gadget;
-	s8			interface;
-	struct usb_ep		*in_ep, *out_ep;
-
-	struct list_head	rx_reqs;	/* List of free RX structs */
-	struct list_head	rx_reqs_active;	/* List of Active RX xfers */
-	struct list_head	rx_buffers;	/* List of completed xfers */
-	/* wait until there is data to be read. */
-	wait_queue_head_t	rx_wait;
-	struct list_head	tx_reqs;	/* List of free TX structs */
-	struct list_head	tx_reqs_active; /* List of Active TX xfers */
-	/* Wait until there are write buffers available to use. */
-	wait_queue_head_t	tx_wait;
-	/* Wait until all write buffers have been sent. */
-	wait_queue_head_t	tx_flush_wait;
-	struct usb_request	*current_rx_req;
-	size_t			current_rx_bytes;
-	u8			*current_rx_buf;
-	u8			printer_status;
-	u8			reset_printer;
-	struct cdev		printer_cdev;
-	struct device		*pdev;
-	u8			printer_cdev_open;
-	wait_queue_head_t	wait;
-	struct usb_function	function;
-};
-
-static struct printer_dev usb_printer_gadget;
+#include "u_printer.h"
 
 
 /*-------------------------------------------------------------------------*/
 /*-------------------------------------------------------------------------*/
 
 
@@ -120,6 +60,9 @@ module_param(qlen, uint, S_IRUGO|S_IWUSR);
 
 
 #define QLEN	qlen
 #define QLEN	qlen
 
 
+static struct usb_function_instance *fi_printer;
+static struct usb_function *f_printer;
+
 /*-------------------------------------------------------------------------*/
 /*-------------------------------------------------------------------------*/
 
 
 /*
 /*
@@ -127,10 +70,6 @@ module_param(qlen, uint, S_IRUGO|S_IWUSR);
  * descriptors are built on demand.
  * descriptors are built on demand.
  */
  */
 
 
-/* holds our biggest descriptor */
-#define USB_DESC_BUFSIZE		256
-#define USB_BUFSIZE			8192
-
 static struct usb_device_descriptor device_desc = {
 static struct usb_device_descriptor device_desc = {
 	.bLength =		sizeof device_desc,
 	.bLength =		sizeof device_desc,
 	.bDescriptorType =	USB_DT_DEVICE,
 	.bDescriptorType =	USB_DT_DEVICE,
@@ -143,108 +82,6 @@ static struct usb_device_descriptor device_desc = {
 	.bNumConfigurations =	1
 	.bNumConfigurations =	1
 };
 };
 
 
-static struct usb_interface_descriptor intf_desc = {
-	.bLength =		sizeof intf_desc,
-	.bDescriptorType =	USB_DT_INTERFACE,
-	.bNumEndpoints =	2,
-	.bInterfaceClass =	USB_CLASS_PRINTER,
-	.bInterfaceSubClass =	1,	/* Printer Sub-Class */
-	.bInterfaceProtocol =	2,	/* Bi-Directional */
-	.iInterface =		0
-};
-
-static struct usb_endpoint_descriptor fs_ep_in_desc = {
-	.bLength =		USB_DT_ENDPOINT_SIZE,
-	.bDescriptorType =	USB_DT_ENDPOINT,
-	.bEndpointAddress =	USB_DIR_IN,
-	.bmAttributes =		USB_ENDPOINT_XFER_BULK
-};
-
-static struct usb_endpoint_descriptor fs_ep_out_desc = {
-	.bLength =		USB_DT_ENDPOINT_SIZE,
-	.bDescriptorType =	USB_DT_ENDPOINT,
-	.bEndpointAddress =	USB_DIR_OUT,
-	.bmAttributes =		USB_ENDPOINT_XFER_BULK
-};
-
-static struct usb_descriptor_header *fs_printer_function[] = {
-	(struct usb_descriptor_header *) &intf_desc,
-	(struct usb_descriptor_header *) &fs_ep_in_desc,
-	(struct usb_descriptor_header *) &fs_ep_out_desc,
-	NULL
-};
-
-/*
- * usb 2.0 devices need to expose both high speed and full speed
- * descriptors, unless they only run at full speed.
- */
-
-static struct usb_endpoint_descriptor hs_ep_in_desc = {
-	.bLength =		USB_DT_ENDPOINT_SIZE,
-	.bDescriptorType =	USB_DT_ENDPOINT,
-	.bmAttributes =		USB_ENDPOINT_XFER_BULK,
-	.wMaxPacketSize =	cpu_to_le16(512)
-};
-
-static struct usb_endpoint_descriptor hs_ep_out_desc = {
-	.bLength =		USB_DT_ENDPOINT_SIZE,
-	.bDescriptorType =	USB_DT_ENDPOINT,
-	.bmAttributes =		USB_ENDPOINT_XFER_BULK,
-	.wMaxPacketSize =	cpu_to_le16(512)
-};
-
-static struct usb_qualifier_descriptor dev_qualifier = {
-	.bLength =		sizeof dev_qualifier,
-	.bDescriptorType =	USB_DT_DEVICE_QUALIFIER,
-	.bcdUSB =		cpu_to_le16(0x0200),
-	.bDeviceClass =		USB_CLASS_PRINTER,
-	.bNumConfigurations =	1
-};
-
-static struct usb_descriptor_header *hs_printer_function[] = {
-	(struct usb_descriptor_header *) &intf_desc,
-	(struct usb_descriptor_header *) &hs_ep_in_desc,
-	(struct usb_descriptor_header *) &hs_ep_out_desc,
-	NULL
-};
-
-/*
- * Added endpoint descriptors for 3.0 devices
- */
-
-static struct usb_endpoint_descriptor ss_ep_in_desc = {
-	.bLength =              USB_DT_ENDPOINT_SIZE,
-	.bDescriptorType =      USB_DT_ENDPOINT,
-	.bmAttributes =         USB_ENDPOINT_XFER_BULK,
-	.wMaxPacketSize =       cpu_to_le16(1024),
-};
-
-static struct usb_ss_ep_comp_descriptor ss_ep_in_comp_desc = {
-	.bLength =              sizeof(ss_ep_in_comp_desc),
-	.bDescriptorType =      USB_DT_SS_ENDPOINT_COMP,
-};
-
-static struct usb_endpoint_descriptor ss_ep_out_desc = {
-	.bLength =              USB_DT_ENDPOINT_SIZE,
-	.bDescriptorType =      USB_DT_ENDPOINT,
-	.bmAttributes =         USB_ENDPOINT_XFER_BULK,
-	.wMaxPacketSize =       cpu_to_le16(1024),
-};
-
-static struct usb_ss_ep_comp_descriptor ss_ep_out_comp_desc = {
-	.bLength =              sizeof(ss_ep_out_comp_desc),
-	.bDescriptorType =      USB_DT_SS_ENDPOINT_COMP,
-};
-
-static struct usb_descriptor_header *ss_printer_function[] = {
-	(struct usb_descriptor_header *) &intf_desc,
-	(struct usb_descriptor_header *) &ss_ep_in_desc,
-	(struct usb_descriptor_header *) &ss_ep_in_comp_desc,
-	(struct usb_descriptor_header *) &ss_ep_out_desc,
-	(struct usb_descriptor_header *) &ss_ep_out_comp_desc,
-	NULL
-};
-
 static struct usb_otg_descriptor otg_descriptor = {
 static struct usb_otg_descriptor otg_descriptor = {
 	.bLength =              sizeof otg_descriptor,
 	.bLength =              sizeof otg_descriptor,
 	.bDescriptorType =      USB_DT_OTG,
 	.bDescriptorType =      USB_DT_OTG,
@@ -256,29 +93,13 @@ static const struct usb_descriptor_header *otg_desc[] = {
 	NULL,
 	NULL,
 };
 };
 
 
-/* maxpacket and other transfer characteristics vary by speed. */
-static inline struct usb_endpoint_descriptor *ep_desc(struct usb_gadget *gadget,
-					struct usb_endpoint_descriptor *fs,
-					struct usb_endpoint_descriptor *hs,
-					struct usb_endpoint_descriptor *ss)
-{
-	switch (gadget->speed) {
-	case USB_SPEED_SUPER:
-		return ss;
-	case USB_SPEED_HIGH:
-		return hs;
-	default:
-		return fs;
-	}
-}
-
 /*-------------------------------------------------------------------------*/
 /*-------------------------------------------------------------------------*/
 
 
 /* descriptors that are built on-demand */
 /* descriptors that are built on-demand */
 
 
 static char				product_desc [40] = DRIVER_DESC;
 static char				product_desc [40] = DRIVER_DESC;
 static char				serial_num [40] = "1";
 static char				serial_num [40] = "1";
-static char				pnp_string [1024] =
+static char				pnp_string[PNP_STRING_LEN] =
 	"XXMFG:linux;MDL:g_printer;CLS:PRINTER;SN:1;";
 	"XXMFG:linux;MDL:g_printer;CLS:PRINTER;SN:1;";
 
 
 /* static strings, in UTF-8 */
 /* static strings, in UTF-8 */
@@ -299,921 +120,19 @@ static struct usb_gadget_strings *dev_strings[] = {
 	NULL,
 	NULL,
 };
 };
 
 
-/*-------------------------------------------------------------------------*/
-
-static struct usb_request *
-printer_req_alloc(struct usb_ep *ep, unsigned len, gfp_t gfp_flags)
-{
-	struct usb_request	*req;
-
-	req = usb_ep_alloc_request(ep, gfp_flags);
-
-	if (req != NULL) {
-		req->length = len;
-		req->buf = kmalloc(len, gfp_flags);
-		if (req->buf == NULL) {
-			usb_ep_free_request(ep, req);
-			return NULL;
-		}
-	}
-
-	return req;
-}
-
-static void
-printer_req_free(struct usb_ep *ep, struct usb_request *req)
-{
-	if (ep != NULL && req != NULL) {
-		kfree(req->buf);
-		usb_ep_free_request(ep, req);
-	}
-}
-
-/*-------------------------------------------------------------------------*/
-
-static void rx_complete(struct usb_ep *ep, struct usb_request *req)
-{
-	struct printer_dev	*dev = ep->driver_data;
-	int			status = req->status;
-	unsigned long		flags;
-
-	spin_lock_irqsave(&dev->lock, flags);
-
-	list_del_init(&req->list);	/* Remode from Active List */
-
-	switch (status) {
-
-	/* normal completion */
-	case 0:
-		if (req->actual > 0) {
-			list_add_tail(&req->list, &dev->rx_buffers);
-			DBG(dev, "G_Printer : rx length %d\n", req->actual);
-		} else {
-			list_add(&req->list, &dev->rx_reqs);
-		}
-		break;
-
-	/* software-driven interface shutdown */
-	case -ECONNRESET:		/* unlink */
-	case -ESHUTDOWN:		/* disconnect etc */
-		VDBG(dev, "rx shutdown, code %d\n", status);
-		list_add(&req->list, &dev->rx_reqs);
-		break;
-
-	/* for hardware automagic (such as pxa) */
-	case -ECONNABORTED:		/* endpoint reset */
-		DBG(dev, "rx %s reset\n", ep->name);
-		list_add(&req->list, &dev->rx_reqs);
-		break;
-
-	/* data overrun */
-	case -EOVERFLOW:
-		/* FALLTHROUGH */
-
-	default:
-		DBG(dev, "rx status %d\n", status);
-		list_add(&req->list, &dev->rx_reqs);
-		break;
-	}
-
-	wake_up_interruptible(&dev->rx_wait);
-	spin_unlock_irqrestore(&dev->lock, flags);
-}
-
-static void tx_complete(struct usb_ep *ep, struct usb_request *req)
-{
-	struct printer_dev	*dev = ep->driver_data;
-
-	switch (req->status) {
-	default:
-		VDBG(dev, "tx err %d\n", req->status);
-		/* FALLTHROUGH */
-	case -ECONNRESET:		/* unlink */
-	case -ESHUTDOWN:		/* disconnect etc */
-		break;
-	case 0:
-		break;
-	}
-
-	spin_lock(&dev->lock);
-	/* Take the request struct off the active list and put it on the
-	 * free list.
-	 */
-	list_del_init(&req->list);
-	list_add(&req->list, &dev->tx_reqs);
-	wake_up_interruptible(&dev->tx_wait);
-	if (likely(list_empty(&dev->tx_reqs_active)))
-		wake_up_interruptible(&dev->tx_flush_wait);
-
-	spin_unlock(&dev->lock);
-}
-
-/*-------------------------------------------------------------------------*/
-
-static int
-printer_open(struct inode *inode, struct file *fd)
-{
-	struct printer_dev	*dev;
-	unsigned long		flags;
-	int			ret = -EBUSY;
-
-	mutex_lock(&printer_mutex);
-	dev = container_of(inode->i_cdev, struct printer_dev, printer_cdev);
-
-	spin_lock_irqsave(&dev->lock, flags);
-
-	if (!dev->printer_cdev_open) {
-		dev->printer_cdev_open = 1;
-		fd->private_data = dev;
-		ret = 0;
-		/* Change the printer status to show that it's on-line. */
-		dev->printer_status |= PRINTER_SELECTED;
-	}
-
-	spin_unlock_irqrestore(&dev->lock, flags);
-
-	DBG(dev, "printer_open returned %x\n", ret);
-	mutex_unlock(&printer_mutex);
-	return ret;
-}
-
-static int
-printer_close(struct inode *inode, struct file *fd)
-{
-	struct printer_dev	*dev = fd->private_data;
-	unsigned long		flags;
-
-	spin_lock_irqsave(&dev->lock, flags);
-	dev->printer_cdev_open = 0;
-	fd->private_data = NULL;
-	/* Change printer status to show that the printer is off-line. */
-	dev->printer_status &= ~PRINTER_SELECTED;
-	spin_unlock_irqrestore(&dev->lock, flags);
-
-	DBG(dev, "printer_close\n");
-
-	return 0;
-}
-
-/* This function must be called with interrupts turned off. */
-static void
-setup_rx_reqs(struct printer_dev *dev)
-{
-	struct usb_request              *req;
-
-	while (likely(!list_empty(&dev->rx_reqs))) {
-		int error;
-
-		req = container_of(dev->rx_reqs.next,
-				struct usb_request, list);
-		list_del_init(&req->list);
-
-		/* The USB Host sends us whatever amount of data it wants to
-		 * so we always set the length field to the full USB_BUFSIZE.
-		 * If the amount of data is more than the read() caller asked
-		 * for it will be stored in the request buffer until it is
-		 * asked for by read().
-		 */
-		req->length = USB_BUFSIZE;
-		req->complete = rx_complete;
-
-		/* here, we unlock, and only unlock, to avoid deadlock. */
-		spin_unlock(&dev->lock);
-		error = usb_ep_queue(dev->out_ep, req, GFP_ATOMIC);
-		spin_lock(&dev->lock);
-		if (error) {
-			DBG(dev, "rx submit --> %d\n", error);
-			list_add(&req->list, &dev->rx_reqs);
-			break;
-		}
-		/* if the req is empty, then add it into dev->rx_reqs_active. */
-		else if (list_empty(&req->list)) {
-			list_add(&req->list, &dev->rx_reqs_active);
-		}
-	}
-}
-
-static ssize_t
-printer_read(struct file *fd, char __user *buf, size_t len, loff_t *ptr)
-{
-	struct printer_dev		*dev = fd->private_data;
-	unsigned long			flags;
-	size_t				size;
-	size_t				bytes_copied;
-	struct usb_request		*req;
-	/* This is a pointer to the current USB rx request. */
-	struct usb_request		*current_rx_req;
-	/* This is the number of bytes in the current rx buffer. */
-	size_t				current_rx_bytes;
-	/* This is a pointer to the current rx buffer. */
-	u8				*current_rx_buf;
-
-	if (len == 0)
-		return -EINVAL;
-
-	DBG(dev, "printer_read trying to read %d bytes\n", (int)len);
-
-	mutex_lock(&dev->lock_printer_io);
-	spin_lock_irqsave(&dev->lock, flags);
-
-	/* We will use this flag later to check if a printer reset happened
-	 * after we turn interrupts back on.
-	 */
-	dev->reset_printer = 0;
-
-	setup_rx_reqs(dev);
-
-	bytes_copied = 0;
-	current_rx_req = dev->current_rx_req;
-	current_rx_bytes = dev->current_rx_bytes;
-	current_rx_buf = dev->current_rx_buf;
-	dev->current_rx_req = NULL;
-	dev->current_rx_bytes = 0;
-	dev->current_rx_buf = NULL;
-
-	/* Check if there is any data in the read buffers. Please note that
-	 * current_rx_bytes is the number of bytes in the current rx buffer.
-	 * If it is zero then check if there are any other rx_buffers that
-	 * are on the completed list. We are only out of data if all rx
-	 * buffers are empty.
-	 */
-	if ((current_rx_bytes == 0) &&
-			(likely(list_empty(&dev->rx_buffers)))) {
-		/* Turn interrupts back on before sleeping. */
-		spin_unlock_irqrestore(&dev->lock, flags);
-
-		/*
-		 * If no data is available check if this is a NON-Blocking
-		 * call or not.
-		 */
-		if (fd->f_flags & (O_NONBLOCK|O_NDELAY)) {
-			mutex_unlock(&dev->lock_printer_io);
-			return -EAGAIN;
-		}
-
-		/* Sleep until data is available */
-		wait_event_interruptible(dev->rx_wait,
-				(likely(!list_empty(&dev->rx_buffers))));
-		spin_lock_irqsave(&dev->lock, flags);
-	}
-
-	/* We have data to return then copy it to the caller's buffer.*/
-	while ((current_rx_bytes || likely(!list_empty(&dev->rx_buffers)))
-			&& len) {
-		if (current_rx_bytes == 0) {
-			req = container_of(dev->rx_buffers.next,
-					struct usb_request, list);
-			list_del_init(&req->list);
-
-			if (req->actual && req->buf) {
-				current_rx_req = req;
-				current_rx_bytes = req->actual;
-				current_rx_buf = req->buf;
-			} else {
-				list_add(&req->list, &dev->rx_reqs);
-				continue;
-			}
-		}
-
-		/* Don't leave irqs off while doing memory copies */
-		spin_unlock_irqrestore(&dev->lock, flags);
-
-		if (len > current_rx_bytes)
-			size = current_rx_bytes;
-		else
-			size = len;
-
-		size -= copy_to_user(buf, current_rx_buf, size);
-		bytes_copied += size;
-		len -= size;
-		buf += size;
-
-		spin_lock_irqsave(&dev->lock, flags);
-
-		/* We've disconnected or reset so return. */
-		if (dev->reset_printer) {
-			list_add(&current_rx_req->list, &dev->rx_reqs);
-			spin_unlock_irqrestore(&dev->lock, flags);
-			mutex_unlock(&dev->lock_printer_io);
-			return -EAGAIN;
-		}
-
-		/* If we not returning all the data left in this RX request
-		 * buffer then adjust the amount of data left in the buffer.
-		 * Othewise if we are done with this RX request buffer then
-		 * requeue it to get any incoming data from the USB host.
-		 */
-		if (size < current_rx_bytes) {
-			current_rx_bytes -= size;
-			current_rx_buf += size;
-		} else {
-			list_add(&current_rx_req->list, &dev->rx_reqs);
-			current_rx_bytes = 0;
-			current_rx_buf = NULL;
-			current_rx_req = NULL;
-		}
-	}
-
-	dev->current_rx_req = current_rx_req;
-	dev->current_rx_bytes = current_rx_bytes;
-	dev->current_rx_buf = current_rx_buf;
-
-	spin_unlock_irqrestore(&dev->lock, flags);
-	mutex_unlock(&dev->lock_printer_io);
-
-	DBG(dev, "printer_read returned %d bytes\n", (int)bytes_copied);
-
-	if (bytes_copied)
-		return bytes_copied;
-	else
-		return -EAGAIN;
-}
-
-static ssize_t
-printer_write(struct file *fd, const char __user *buf, size_t len, loff_t *ptr)
-{
-	struct printer_dev	*dev = fd->private_data;
-	unsigned long		flags;
-	size_t			size;	/* Amount of data in a TX request. */
-	size_t			bytes_copied = 0;
-	struct usb_request	*req;
-
-	DBG(dev, "printer_write trying to send %d bytes\n", (int)len);
-
-	if (len == 0)
-		return -EINVAL;
-
-	mutex_lock(&dev->lock_printer_io);
-	spin_lock_irqsave(&dev->lock, flags);
-
-	/* Check if a printer reset happens while we have interrupts on */
-	dev->reset_printer = 0;
-
-	/* Check if there is any available write buffers */
-	if (likely(list_empty(&dev->tx_reqs))) {
-		/* Turn interrupts back on before sleeping. */
-		spin_unlock_irqrestore(&dev->lock, flags);
-
-		/*
-		 * If write buffers are available check if this is
-		 * a NON-Blocking call or not.
-		 */
-		if (fd->f_flags & (O_NONBLOCK|O_NDELAY)) {
-			mutex_unlock(&dev->lock_printer_io);
-			return -EAGAIN;
-		}
-
-		/* Sleep until a write buffer is available */
-		wait_event_interruptible(dev->tx_wait,
-				(likely(!list_empty(&dev->tx_reqs))));
-		spin_lock_irqsave(&dev->lock, flags);
-	}
-
-	while (likely(!list_empty(&dev->tx_reqs)) && len) {
-
-		if (len > USB_BUFSIZE)
-			size = USB_BUFSIZE;
-		else
-			size = len;
-
-		req = container_of(dev->tx_reqs.next, struct usb_request,
-				list);
-		list_del_init(&req->list);
-
-		req->complete = tx_complete;
-		req->length = size;
-
-		/* Check if we need to send a zero length packet. */
-		if (len > size)
-			/* They will be more TX requests so no yet. */
-			req->zero = 0;
-		else
-			/* If the data amount is not a multple of the
-			 * maxpacket size then send a zero length packet.
-			 */
-			req->zero = ((len % dev->in_ep->maxpacket) == 0);
-
-		/* Don't leave irqs off while doing memory copies */
-		spin_unlock_irqrestore(&dev->lock, flags);
-
-		if (copy_from_user(req->buf, buf, size)) {
-			list_add(&req->list, &dev->tx_reqs);
-			mutex_unlock(&dev->lock_printer_io);
-			return bytes_copied;
-		}
-
-		bytes_copied += size;
-		len -= size;
-		buf += size;
-
-		spin_lock_irqsave(&dev->lock, flags);
-
-		/* We've disconnected or reset so free the req and buffer */
-		if (dev->reset_printer) {
-			list_add(&req->list, &dev->tx_reqs);
-			spin_unlock_irqrestore(&dev->lock, flags);
-			mutex_unlock(&dev->lock_printer_io);
-			return -EAGAIN;
-		}
-
-		if (usb_ep_queue(dev->in_ep, req, GFP_ATOMIC)) {
-			list_add(&req->list, &dev->tx_reqs);
-			spin_unlock_irqrestore(&dev->lock, flags);
-			mutex_unlock(&dev->lock_printer_io);
-			return -EAGAIN;
-		}
-
-		list_add(&req->list, &dev->tx_reqs_active);
-
-	}
-
-	spin_unlock_irqrestore(&dev->lock, flags);
-	mutex_unlock(&dev->lock_printer_io);
-
-	DBG(dev, "printer_write sent %d bytes\n", (int)bytes_copied);
-
-	if (bytes_copied) {
-		return bytes_copied;
-	} else {
-		return -EAGAIN;
-	}
-}
-
-static int
-printer_fsync(struct file *fd, loff_t start, loff_t end, int datasync)
-{
-	struct printer_dev	*dev = fd->private_data;
-	struct inode *inode = file_inode(fd);
-	unsigned long		flags;
-	int			tx_list_empty;
-
-	mutex_lock(&inode->i_mutex);
-	spin_lock_irqsave(&dev->lock, flags);
-	tx_list_empty = (likely(list_empty(&dev->tx_reqs)));
-	spin_unlock_irqrestore(&dev->lock, flags);
-
-	if (!tx_list_empty) {
-		/* Sleep until all data has been sent */
-		wait_event_interruptible(dev->tx_flush_wait,
-				(likely(list_empty(&dev->tx_reqs_active))));
-	}
-	mutex_unlock(&inode->i_mutex);
-
-	return 0;
-}
-
-static unsigned int
-printer_poll(struct file *fd, poll_table *wait)
-{
-	struct printer_dev	*dev = fd->private_data;
-	unsigned long		flags;
-	int			status = 0;
-
-	mutex_lock(&dev->lock_printer_io);
-	spin_lock_irqsave(&dev->lock, flags);
-	setup_rx_reqs(dev);
-	spin_unlock_irqrestore(&dev->lock, flags);
-	mutex_unlock(&dev->lock_printer_io);
-
-	poll_wait(fd, &dev->rx_wait, wait);
-	poll_wait(fd, &dev->tx_wait, wait);
-
-	spin_lock_irqsave(&dev->lock, flags);
-	if (likely(!list_empty(&dev->tx_reqs)))
-		status |= POLLOUT | POLLWRNORM;
-
-	if (likely(dev->current_rx_bytes) ||
-			likely(!list_empty(&dev->rx_buffers)))
-		status |= POLLIN | POLLRDNORM;
-
-	spin_unlock_irqrestore(&dev->lock, flags);
-
-	return status;
-}
-
-static long
-printer_ioctl(struct file *fd, unsigned int code, unsigned long arg)
-{
-	struct printer_dev	*dev = fd->private_data;
-	unsigned long		flags;
-	int			status = 0;
-
-	DBG(dev, "printer_ioctl: cmd=0x%4.4x, arg=%lu\n", code, arg);
-
-	/* handle ioctls */
-
-	spin_lock_irqsave(&dev->lock, flags);
-
-	switch (code) {
-	case GADGET_GET_PRINTER_STATUS:
-		status = (int)dev->printer_status;
-		break;
-	case GADGET_SET_PRINTER_STATUS:
-		dev->printer_status = (u8)arg;
-		break;
-	default:
-		/* could not handle ioctl */
-		DBG(dev, "printer_ioctl: ERROR cmd=0x%4.4xis not supported\n",
-				code);
-		status = -ENOTTY;
-	}
-
-	spin_unlock_irqrestore(&dev->lock, flags);
-
-	return status;
-}
-
-/* used after endpoint configuration */
-static const struct file_operations printer_io_operations = {
-	.owner =	THIS_MODULE,
-	.open =		printer_open,
-	.read =		printer_read,
-	.write =	printer_write,
-	.fsync =	printer_fsync,
-	.poll =		printer_poll,
-	.unlocked_ioctl = printer_ioctl,
-	.release =	printer_close,
-	.llseek =	noop_llseek,
-};
-
-/*-------------------------------------------------------------------------*/
-
-static int
-set_printer_interface(struct printer_dev *dev)
-{
-	int			result = 0;
-
-	dev->in_ep->desc = ep_desc(dev->gadget, &fs_ep_in_desc, &hs_ep_in_desc,
-				&ss_ep_in_desc);
-	dev->in_ep->driver_data = dev;
-
-	dev->out_ep->desc = ep_desc(dev->gadget, &fs_ep_out_desc,
-				    &hs_ep_out_desc, &ss_ep_out_desc);
-	dev->out_ep->driver_data = dev;
-
-	result = usb_ep_enable(dev->in_ep);
-	if (result != 0) {
-		DBG(dev, "enable %s --> %d\n", dev->in_ep->name, result);
-		goto done;
-	}
-
-	result = usb_ep_enable(dev->out_ep);
-	if (result != 0) {
-		DBG(dev, "enable %s --> %d\n", dev->in_ep->name, result);
-		goto done;
-	}
-
-done:
-	/* on error, disable any endpoints  */
-	if (result != 0) {
-		(void) usb_ep_disable(dev->in_ep);
-		(void) usb_ep_disable(dev->out_ep);
-		dev->in_ep->desc = NULL;
-		dev->out_ep->desc = NULL;
-	}
-
-	/* caller is responsible for cleanup on error */
-	return result;
-}
-
-static void printer_reset_interface(struct printer_dev *dev)
-{
-	if (dev->interface < 0)
-		return;
-
-	DBG(dev, "%s\n", __func__);
-
-	if (dev->in_ep->desc)
-		usb_ep_disable(dev->in_ep);
-
-	if (dev->out_ep->desc)
-		usb_ep_disable(dev->out_ep);
-
-	dev->in_ep->desc = NULL;
-	dev->out_ep->desc = NULL;
-	dev->interface = -1;
-}
-
-/* Change our operational Interface. */
-static int set_interface(struct printer_dev *dev, unsigned number)
-{
-	int			result = 0;
-
-	/* Free the current interface */
-	printer_reset_interface(dev);
-
-	result = set_printer_interface(dev);
-	if (result)
-		printer_reset_interface(dev);
-	else
-		dev->interface = number;
-
-	if (!result)
-		INFO(dev, "Using interface %x\n", number);
-
-	return result;
-}
-
-static void printer_soft_reset(struct printer_dev *dev)
-{
-	struct usb_request	*req;
-
-	INFO(dev, "Received Printer Reset Request\n");
-
-	if (usb_ep_disable(dev->in_ep))
-		DBG(dev, "Failed to disable USB in_ep\n");
-	if (usb_ep_disable(dev->out_ep))
-		DBG(dev, "Failed to disable USB out_ep\n");
-
-	if (dev->current_rx_req != NULL) {
-		list_add(&dev->current_rx_req->list, &dev->rx_reqs);
-		dev->current_rx_req = NULL;
-	}
-	dev->current_rx_bytes = 0;
-	dev->current_rx_buf = NULL;
-	dev->reset_printer = 1;
-
-	while (likely(!(list_empty(&dev->rx_buffers)))) {
-		req = container_of(dev->rx_buffers.next, struct usb_request,
-				list);
-		list_del_init(&req->list);
-		list_add(&req->list, &dev->rx_reqs);
-	}
-
-	while (likely(!(list_empty(&dev->rx_reqs_active)))) {
-		req = container_of(dev->rx_buffers.next, struct usb_request,
-				list);
-		list_del_init(&req->list);
-		list_add(&req->list, &dev->rx_reqs);
-	}
-
-	while (likely(!(list_empty(&dev->tx_reqs_active)))) {
-		req = container_of(dev->tx_reqs_active.next,
-				struct usb_request, list);
-		list_del_init(&req->list);
-		list_add(&req->list, &dev->tx_reqs);
-	}
-
-	if (usb_ep_enable(dev->in_ep))
-		DBG(dev, "Failed to enable USB in_ep\n");
-	if (usb_ep_enable(dev->out_ep))
-		DBG(dev, "Failed to enable USB out_ep\n");
-
-	wake_up_interruptible(&dev->rx_wait);
-	wake_up_interruptible(&dev->tx_wait);
-	wake_up_interruptible(&dev->tx_flush_wait);
-}
-
-/*-------------------------------------------------------------------------*/
-
-/*
- * The setup() callback implements all the ep0 functionality that's not
- * handled lower down.
- */
-static int printer_func_setup(struct usb_function *f,
-		const struct usb_ctrlrequest *ctrl)
-{
-	struct printer_dev *dev = container_of(f, struct printer_dev, function);
-	struct usb_composite_dev *cdev = f->config->cdev;
-	struct usb_request	*req = cdev->req;
-	int			value = -EOPNOTSUPP;
-	u16			wIndex = le16_to_cpu(ctrl->wIndex);
-	u16			wValue = le16_to_cpu(ctrl->wValue);
-	u16			wLength = le16_to_cpu(ctrl->wLength);
-
-	DBG(dev, "ctrl req%02x.%02x v%04x i%04x l%d\n",
-		ctrl->bRequestType, ctrl->bRequest, wValue, wIndex, wLength);
-
-	switch (ctrl->bRequestType&USB_TYPE_MASK) {
-	case USB_TYPE_CLASS:
-		switch (ctrl->bRequest) {
-		case 0: /* Get the IEEE-1284 PNP String */
-			/* Only one printer interface is supported. */
-			if ((wIndex>>8) != dev->interface)
-				break;
-
-			value = (pnp_string[0]<<8)|pnp_string[1];
-			memcpy(req->buf, pnp_string, value);
-			DBG(dev, "1284 PNP String: %x %s\n", value,
-					&pnp_string[2]);
-			break;
-
-		case 1: /* Get Port Status */
-			/* Only one printer interface is supported. */
-			if (wIndex != dev->interface)
-				break;
-
-			*(u8 *)req->buf = dev->printer_status;
-			value = min(wLength, (u16) 1);
-			break;
-
-		case 2: /* Soft Reset */
-			/* Only one printer interface is supported. */
-			if (wIndex != dev->interface)
-				break;
-
-			printer_soft_reset(dev);
-
-			value = 0;
-			break;
-
-		default:
-			goto unknown;
-		}
-		break;
-
-	default:
-unknown:
-		VDBG(dev,
-			"unknown ctrl req%02x.%02x v%04x i%04x l%d\n",
-			ctrl->bRequestType, ctrl->bRequest,
-			wValue, wIndex, wLength);
-		break;
-	}
-	/* host either stalls (value < 0) or reports success */
-	return value;
-}
-
-static int __init printer_func_bind(struct usb_configuration *c,
-		struct usb_function *f)
-{
-	struct printer_dev *dev = container_of(f, struct printer_dev, function);
-	struct usb_composite_dev *cdev = c->cdev;
-	struct usb_ep *in_ep;
-	struct usb_ep *out_ep = NULL;
-	int id;
-	int ret;
-
-	id = usb_interface_id(c, f);
-	if (id < 0)
-		return id;
-	intf_desc.bInterfaceNumber = id;
-
-	/* all we really need is bulk IN/OUT */
-	in_ep = usb_ep_autoconfig(cdev->gadget, &fs_ep_in_desc);
-	if (!in_ep) {
-autoconf_fail:
-		dev_err(&cdev->gadget->dev, "can't autoconfigure on %s\n",
-			cdev->gadget->name);
-		return -ENODEV;
-	}
-	in_ep->driver_data = in_ep;	/* claim */
-
-	out_ep = usb_ep_autoconfig(cdev->gadget, &fs_ep_out_desc);
-	if (!out_ep)
-		goto autoconf_fail;
-	out_ep->driver_data = out_ep;	/* claim */
-
-	/* assumes that all endpoints are dual-speed */
-	hs_ep_in_desc.bEndpointAddress = fs_ep_in_desc.bEndpointAddress;
-	hs_ep_out_desc.bEndpointAddress = fs_ep_out_desc.bEndpointAddress;
-	ss_ep_in_desc.bEndpointAddress = fs_ep_in_desc.bEndpointAddress;
-	ss_ep_out_desc.bEndpointAddress = fs_ep_out_desc.bEndpointAddress;
-
-	ret = usb_assign_descriptors(f, fs_printer_function,
-			hs_printer_function, ss_printer_function);
-	if (ret)
-		return ret;
-
-	dev->in_ep = in_ep;
-	dev->out_ep = out_ep;
-	return 0;
-}
-
-static void printer_func_unbind(struct usb_configuration *c,
-		struct usb_function *f)
-{
-	usb_free_all_descriptors(f);
-}
-
-static int printer_func_set_alt(struct usb_function *f,
-		unsigned intf, unsigned alt)
-{
-	struct printer_dev *dev = container_of(f, struct printer_dev, function);
-	int ret = -ENOTSUPP;
-
-	if (!alt)
-		ret = set_interface(dev, intf);
-
-	return ret;
-}
-
-static void printer_func_disable(struct usb_function *f)
-{
-	struct printer_dev *dev = container_of(f, struct printer_dev, function);
-	unsigned long		flags;
-
-	DBG(dev, "%s\n", __func__);
-
-	spin_lock_irqsave(&dev->lock, flags);
-	printer_reset_interface(dev);
-	spin_unlock_irqrestore(&dev->lock, flags);
-}
-
-static void printer_cfg_unbind(struct usb_configuration *c)
-{
-	struct printer_dev	*dev;
-	struct usb_request	*req;
-
-	dev = &usb_printer_gadget;
-
-	DBG(dev, "%s\n", __func__);
-
-	/* Remove sysfs files */
-	device_destroy(usb_gadget_class, g_printer_devno);
-
-	/* Remove Character Device */
-	cdev_del(&dev->printer_cdev);
-
-	/* we must already have been disconnected ... no i/o may be active */
-	WARN_ON(!list_empty(&dev->tx_reqs_active));
-	WARN_ON(!list_empty(&dev->rx_reqs_active));
-
-	/* Free all memory for this driver. */
-	while (!list_empty(&dev->tx_reqs)) {
-		req = container_of(dev->tx_reqs.next, struct usb_request,
-				list);
-		list_del(&req->list);
-		printer_req_free(dev->in_ep, req);
-	}
-
-	if (dev->current_rx_req != NULL)
-		printer_req_free(dev->out_ep, dev->current_rx_req);
-
-	while (!list_empty(&dev->rx_reqs)) {
-		req = container_of(dev->rx_reqs.next,
-				struct usb_request, list);
-		list_del(&req->list);
-		printer_req_free(dev->out_ep, req);
-	}
-
-	while (!list_empty(&dev->rx_buffers)) {
-		req = container_of(dev->rx_buffers.next,
-				struct usb_request, list);
-		list_del(&req->list);
-		printer_req_free(dev->out_ep, req);
-	}
-}
-
 static struct usb_configuration printer_cfg_driver = {
 static struct usb_configuration printer_cfg_driver = {
 	.label			= "printer",
 	.label			= "printer",
-	.unbind			= printer_cfg_unbind,
 	.bConfigurationValue	= 1,
 	.bConfigurationValue	= 1,
 	.bmAttributes		= USB_CONFIG_ATT_ONE | USB_CONFIG_ATT_SELFPOWER,
 	.bmAttributes		= USB_CONFIG_ATT_ONE | USB_CONFIG_ATT_SELFPOWER,
 };
 };
 
 
-static int __init printer_bind_config(struct usb_configuration *c)
+static int __init printer_do_config(struct usb_configuration *c)
 {
 {
 	struct usb_gadget	*gadget = c->cdev->gadget;
 	struct usb_gadget	*gadget = c->cdev->gadget;
-	struct printer_dev	*dev;
-	int			status = -ENOMEM;
-	size_t			len;
-	u32			i;
-	struct usb_request	*req;
+	int			status = 0;
 
 
 	usb_ep_autoconfig_reset(gadget);
 	usb_ep_autoconfig_reset(gadget);
 
 
-	dev = &usb_printer_gadget;
-
-	dev->function.name = shortname;
-	dev->function.bind = printer_func_bind;
-	dev->function.setup = printer_func_setup;
-	dev->function.unbind = printer_func_unbind;
-	dev->function.set_alt = printer_func_set_alt;
-	dev->function.disable = printer_func_disable;
-
-	status = usb_add_function(c, &dev->function);
-	if (status)
-		return status;
-
-	/* Setup the sysfs files for the printer gadget. */
-	dev->pdev = device_create(usb_gadget_class, NULL, g_printer_devno,
-				  NULL, "g_printer");
-	if (IS_ERR(dev->pdev)) {
-		ERROR(dev, "Failed to create device: g_printer\n");
-		status = PTR_ERR(dev->pdev);
-		goto fail;
-	}
-
-	/*
-	 * Register a character device as an interface to a user mode
-	 * program that handles the printer specific functionality.
-	 */
-	cdev_init(&dev->printer_cdev, &printer_io_operations);
-	dev->printer_cdev.owner = THIS_MODULE;
-	status = cdev_add(&dev->printer_cdev, g_printer_devno, 1);
-	if (status) {
-		ERROR(dev, "Failed to open char device\n");
-		goto fail;
-	}
-
-	if (iPNPstring)
-		strlcpy(&pnp_string[2], iPNPstring, (sizeof pnp_string)-2);
-
-	len = strlen(pnp_string);
-	pnp_string[0] = (len >> 8) & 0xFF;
-	pnp_string[1] = len & 0xFF;
-
 	usb_gadget_set_selfpowered(gadget);
 	usb_gadget_set_selfpowered(gadget);
 
 
 	if (gadget_is_otg(gadget)) {
 	if (gadget_is_otg(gadget)) {
@@ -1222,86 +141,64 @@ static int __init printer_bind_config(struct usb_configuration *c)
 		printer_cfg_driver.bmAttributes |= USB_CONFIG_ATT_WAKEUP;
 		printer_cfg_driver.bmAttributes |= USB_CONFIG_ATT_WAKEUP;
 	}
 	}
 
 
-	spin_lock_init(&dev->lock);
-	mutex_init(&dev->lock_printer_io);
-	INIT_LIST_HEAD(&dev->tx_reqs);
-	INIT_LIST_HEAD(&dev->tx_reqs_active);
-	INIT_LIST_HEAD(&dev->rx_reqs);
-	INIT_LIST_HEAD(&dev->rx_reqs_active);
-	INIT_LIST_HEAD(&dev->rx_buffers);
-	init_waitqueue_head(&dev->rx_wait);
-	init_waitqueue_head(&dev->tx_wait);
-	init_waitqueue_head(&dev->tx_flush_wait);
-
-	dev->interface = -1;
-	dev->printer_cdev_open = 0;
-	dev->printer_status = PRINTER_NOT_ERROR;
-	dev->current_rx_req = NULL;
-	dev->current_rx_bytes = 0;
-	dev->current_rx_buf = NULL;
-
-	for (i = 0; i < QLEN; i++) {
-		req = printer_req_alloc(dev->in_ep, USB_BUFSIZE, GFP_KERNEL);
-		if (!req) {
-			while (!list_empty(&dev->tx_reqs)) {
-				req = container_of(dev->tx_reqs.next,
-						struct usb_request, list);
-				list_del(&req->list);
-				printer_req_free(dev->in_ep, req);
-			}
-			return -ENOMEM;
-		}
-		list_add(&req->list, &dev->tx_reqs);
-	}
-
-	for (i = 0; i < QLEN; i++) {
-		req = printer_req_alloc(dev->out_ep, USB_BUFSIZE, GFP_KERNEL);
-		if (!req) {
-			while (!list_empty(&dev->rx_reqs)) {
-				req = container_of(dev->rx_reqs.next,
-						struct usb_request, list);
-				list_del(&req->list);
-				printer_req_free(dev->out_ep, req);
-			}
-			return -ENOMEM;
-		}
-		list_add(&req->list, &dev->rx_reqs);
-	}
-
-	/* finish hookup to lower layer ... */
-	dev->gadget = gadget;
+	f_printer = usb_get_function(fi_printer);
+	if (IS_ERR(f_printer))
+		return PTR_ERR(f_printer);
 
 
-	INFO(dev, "%s, version: " DRIVER_VERSION "\n", driver_desc);
-	return 0;
+	status = usb_add_function(c, f_printer);
+	if (status < 0)
+		usb_put_function(f_printer);
 
 
-fail:
-	printer_cfg_unbind(c);
 	return status;
 	return status;
 }
 }
 
 
-static int printer_unbind(struct usb_composite_dev *cdev)
-{
-	return 0;
-}
-
 static int __init printer_bind(struct usb_composite_dev *cdev)
 static int __init printer_bind(struct usb_composite_dev *cdev)
 {
 {
-	int ret;
+	struct f_printer_opts *opts;
+	int ret, len;
+
+	fi_printer = usb_get_function_instance("printer");
+	if (IS_ERR(fi_printer))
+		return PTR_ERR(fi_printer);
+
+	if (iPNPstring)
+		strlcpy(&pnp_string[2], iPNPstring, PNP_STRING_LEN - 2);
+
+	len = strlen(pnp_string);
+	pnp_string[0] = (len >> 8) & 0xFF;
+	pnp_string[1] = len & 0xFF;
+
+	opts = container_of(fi_printer, struct f_printer_opts, func_inst);
+	opts->minor = 0;
+	memcpy(opts->pnp_string, pnp_string, PNP_STRING_LEN);
+	opts->q_len = QLEN;
 
 
 	ret = usb_string_ids_tab(cdev, strings);
 	ret = usb_string_ids_tab(cdev, strings);
-	if (ret < 0)
+	if (ret < 0) {
+		usb_put_function_instance(fi_printer);
 		return ret;
 		return ret;
+	}
 	device_desc.iManufacturer = strings[USB_GADGET_MANUFACTURER_IDX].id;
 	device_desc.iManufacturer = strings[USB_GADGET_MANUFACTURER_IDX].id;
 	device_desc.iProduct = strings[USB_GADGET_PRODUCT_IDX].id;
 	device_desc.iProduct = strings[USB_GADGET_PRODUCT_IDX].id;
 	device_desc.iSerialNumber = strings[USB_GADGET_SERIAL_IDX].id;
 	device_desc.iSerialNumber = strings[USB_GADGET_SERIAL_IDX].id;
 
 
-	ret = usb_add_config(cdev, &printer_cfg_driver, printer_bind_config);
-	if (ret)
+	ret = usb_add_config(cdev, &printer_cfg_driver, printer_do_config);
+	if (ret) {
+		usb_put_function_instance(fi_printer);
 		return ret;
 		return ret;
+	}
 	usb_composite_overwrite_options(cdev, &coverwrite);
 	usb_composite_overwrite_options(cdev, &coverwrite);
 	return ret;
 	return ret;
 }
 }
 
 
+static int __exit printer_unbind(struct usb_composite_dev *cdev)
+{
+	usb_put_function(f_printer);
+	usb_put_function_instance(fi_printer);
+
+	return 0;
+}
+
 static __refdata struct usb_composite_driver printer_driver = {
 static __refdata struct usb_composite_driver printer_driver = {
 	.name           = shortname,
 	.name           = shortname,
 	.dev            = &device_desc,
 	.dev            = &device_desc,
@@ -1311,47 +208,7 @@ static __refdata struct usb_composite_driver printer_driver = {
 	.unbind		= printer_unbind,
 	.unbind		= printer_unbind,
 };
 };
 
 
-static int __init
-init(void)
-{
-	int status;
-
-	usb_gadget_class = class_create(THIS_MODULE, "usb_printer_gadget");
-	if (IS_ERR(usb_gadget_class)) {
-		status = PTR_ERR(usb_gadget_class);
-		pr_err("unable to create usb_gadget class %d\n", status);
-		return status;
-	}
-
-	status = alloc_chrdev_region(&g_printer_devno, 0, 1,
-			"USB printer gadget");
-	if (status) {
-		pr_err("alloc_chrdev_region %d\n", status);
-		class_destroy(usb_gadget_class);
-		return status;
-	}
-
-	status = usb_composite_probe(&printer_driver);
-	if (status) {
-		class_destroy(usb_gadget_class);
-		unregister_chrdev_region(g_printer_devno, 1);
-		pr_err("usb_gadget_probe_driver %x\n", status);
-	}
-
-	return status;
-}
-module_init(init);
-
-static void __exit
-cleanup(void)
-{
-	mutex_lock(&usb_printer_gadget.lock_printer_io);
-	usb_composite_unregister(&printer_driver);
-	unregister_chrdev_region(g_printer_devno, 1);
-	class_destroy(usb_gadget_class);
-	mutex_unlock(&usb_printer_gadget.lock_printer_io);
-}
-module_exit(cleanup);
+module_usb_composite_driver(printer_driver);
 
 
 MODULE_DESCRIPTION(DRIVER_DESC);
 MODULE_DESCRIPTION(DRIVER_DESC);
 MODULE_AUTHOR("Craig Nadler");
 MODULE_AUTHOR("Craig Nadler");

+ 157 - 55
drivers/usb/gadget/udc/atmel_usba_udc.c

@@ -152,7 +152,7 @@ static int regs_dbg_open(struct inode *inode, struct file *file)
 
 
 	spin_lock_irq(&udc->lock);
 	spin_lock_irq(&udc->lock);
 	for (i = 0; i < inode->i_size / 4; i++)
 	for (i = 0; i < inode->i_size / 4; i++)
-		data[i] = __raw_readl(udc->regs + i * 4);
+		data[i] = usba_io_readl(udc->regs + i * 4);
 	spin_unlock_irq(&udc->lock);
 	spin_unlock_irq(&udc->lock);
 
 
 	file->private_data = data;
 	file->private_data = data;
@@ -1249,7 +1249,7 @@ static int handle_ep0_setup(struct usba_udc *udc, struct usba_ep *ep,
 		if (crq->wLength != cpu_to_le16(sizeof(status)))
 		if (crq->wLength != cpu_to_le16(sizeof(status)))
 			goto stall;
 			goto stall;
 		ep->state = DATA_STAGE_IN;
 		ep->state = DATA_STAGE_IN;
-		__raw_writew(status, ep->fifo);
+		usba_io_writew(status, ep->fifo);
 		usba_ep_writel(ep, SET_STA, USBA_TX_PK_RDY);
 		usba_ep_writel(ep, SET_STA, USBA_TX_PK_RDY);
 		break;
 		break;
 	}
 	}
@@ -1739,7 +1739,72 @@ static irqreturn_t usba_udc_irq(int irq, void *devid)
 	return IRQ_HANDLED;
 	return IRQ_HANDLED;
 }
 }
 
 
-static irqreturn_t usba_vbus_irq(int irq, void *devid)
+static int start_clock(struct usba_udc *udc)
+{
+	int ret;
+
+	if (udc->clocked)
+		return 0;
+
+	ret = clk_prepare_enable(udc->pclk);
+	if (ret)
+		return ret;
+	ret = clk_prepare_enable(udc->hclk);
+	if (ret) {
+		clk_disable_unprepare(udc->pclk);
+		return ret;
+	}
+
+	udc->clocked = true;
+	return 0;
+}
+
+static void stop_clock(struct usba_udc *udc)
+{
+	if (!udc->clocked)
+		return;
+
+	clk_disable_unprepare(udc->hclk);
+	clk_disable_unprepare(udc->pclk);
+
+	udc->clocked = false;
+}
+
+static int usba_start(struct usba_udc *udc)
+{
+	unsigned long flags;
+	int ret;
+
+	ret = start_clock(udc);
+	if (ret)
+		return ret;
+
+	spin_lock_irqsave(&udc->lock, flags);
+	toggle_bias(udc, 1);
+	usba_writel(udc, CTRL, USBA_ENABLE_MASK);
+	usba_int_enb_set(udc, USBA_END_OF_RESET);
+	spin_unlock_irqrestore(&udc->lock, flags);
+
+	return 0;
+}
+
+static void usba_stop(struct usba_udc *udc)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&udc->lock, flags);
+	udc->gadget.speed = USB_SPEED_UNKNOWN;
+	reset_all_endpoints(udc);
+
+	/* This will also disable the DP pullup */
+	toggle_bias(udc, 0);
+	usba_writel(udc, CTRL, USBA_DISABLE_MASK);
+	spin_unlock_irqrestore(&udc->lock, flags);
+
+	stop_clock(udc);
+}
+
+static irqreturn_t usba_vbus_irq_thread(int irq, void *devid)
 {
 {
 	struct usba_udc *udc = devid;
 	struct usba_udc *udc = devid;
 	int vbus;
 	int vbus;
@@ -1747,35 +1812,22 @@ static irqreturn_t usba_vbus_irq(int irq, void *devid)
 	/* debounce */
 	/* debounce */
 	udelay(10);
 	udelay(10);
 
 
-	spin_lock(&udc->lock);
-
-	/* May happen if Vbus pin toggles during probe() */
-	if (!udc->driver)
-		goto out;
+	mutex_lock(&udc->vbus_mutex);
 
 
 	vbus = vbus_is_present(udc);
 	vbus = vbus_is_present(udc);
 	if (vbus != udc->vbus_prev) {
 	if (vbus != udc->vbus_prev) {
 		if (vbus) {
 		if (vbus) {
-			toggle_bias(udc, 1);
-			usba_writel(udc, CTRL, USBA_ENABLE_MASK);
-			usba_int_enb_set(udc, USBA_END_OF_RESET);
+			usba_start(udc);
 		} else {
 		} else {
-			udc->gadget.speed = USB_SPEED_UNKNOWN;
-			reset_all_endpoints(udc);
-			toggle_bias(udc, 0);
-			usba_writel(udc, CTRL, USBA_DISABLE_MASK);
-			if (udc->driver->disconnect) {
-				spin_unlock(&udc->lock);
+			usba_stop(udc);
+
+			if (udc->driver->disconnect)
 				udc->driver->disconnect(&udc->gadget);
 				udc->driver->disconnect(&udc->gadget);
-				spin_lock(&udc->lock);
-			}
 		}
 		}
 		udc->vbus_prev = vbus;
 		udc->vbus_prev = vbus;
 	}
 	}
 
 
-out:
-	spin_unlock(&udc->lock);
-
+	mutex_unlock(&udc->vbus_mutex);
 	return IRQ_HANDLED;
 	return IRQ_HANDLED;
 }
 }
 
 
@@ -1787,55 +1839,47 @@ static int atmel_usba_start(struct usb_gadget *gadget,
 	unsigned long flags;
 	unsigned long flags;
 
 
 	spin_lock_irqsave(&udc->lock, flags);
 	spin_lock_irqsave(&udc->lock, flags);
-
 	udc->devstatus = 1 << USB_DEVICE_SELF_POWERED;
 	udc->devstatus = 1 << USB_DEVICE_SELF_POWERED;
 	udc->driver = driver;
 	udc->driver = driver;
 	spin_unlock_irqrestore(&udc->lock, flags);
 	spin_unlock_irqrestore(&udc->lock, flags);
 
 
-	ret = clk_prepare_enable(udc->pclk);
-	if (ret)
-		return ret;
-	ret = clk_prepare_enable(udc->hclk);
-	if (ret) {
-		clk_disable_unprepare(udc->pclk);
-		return ret;
-	}
+	mutex_lock(&udc->vbus_mutex);
 
 
-	udc->vbus_prev = 0;
 	if (gpio_is_valid(udc->vbus_pin))
 	if (gpio_is_valid(udc->vbus_pin))
 		enable_irq(gpio_to_irq(udc->vbus_pin));
 		enable_irq(gpio_to_irq(udc->vbus_pin));
 
 
 	/* If Vbus is present, enable the controller and wait for reset */
 	/* If Vbus is present, enable the controller and wait for reset */
-	spin_lock_irqsave(&udc->lock, flags);
-	if (vbus_is_present(udc) && udc->vbus_prev == 0) {
-		toggle_bias(udc, 1);
-		usba_writel(udc, CTRL, USBA_ENABLE_MASK);
-		usba_int_enb_set(udc, USBA_END_OF_RESET);
+	udc->vbus_prev = vbus_is_present(udc);
+	if (udc->vbus_prev) {
+		ret = usba_start(udc);
+		if (ret)
+			goto err;
 	}
 	}
-	spin_unlock_irqrestore(&udc->lock, flags);
 
 
+	mutex_unlock(&udc->vbus_mutex);
 	return 0;
 	return 0;
+
+err:
+	if (gpio_is_valid(udc->vbus_pin))
+		disable_irq(gpio_to_irq(udc->vbus_pin));
+
+	mutex_unlock(&udc->vbus_mutex);
+
+	spin_lock_irqsave(&udc->lock, flags);
+	udc->devstatus &= ~(1 << USB_DEVICE_SELF_POWERED);
+	udc->driver = NULL;
+	spin_unlock_irqrestore(&udc->lock, flags);
+	return ret;
 }
 }
 
 
 static int atmel_usba_stop(struct usb_gadget *gadget)
 static int atmel_usba_stop(struct usb_gadget *gadget)
 {
 {
 	struct usba_udc *udc = container_of(gadget, struct usba_udc, gadget);
 	struct usba_udc *udc = container_of(gadget, struct usba_udc, gadget);
-	unsigned long flags;
 
 
 	if (gpio_is_valid(udc->vbus_pin))
 	if (gpio_is_valid(udc->vbus_pin))
 		disable_irq(gpio_to_irq(udc->vbus_pin));
 		disable_irq(gpio_to_irq(udc->vbus_pin));
 
 
-	spin_lock_irqsave(&udc->lock, flags);
-	udc->gadget.speed = USB_SPEED_UNKNOWN;
-	reset_all_endpoints(udc);
-	spin_unlock_irqrestore(&udc->lock, flags);
-
-	/* This will also disable the DP pullup */
-	toggle_bias(udc, 0);
-	usba_writel(udc, CTRL, USBA_DISABLE_MASK);
-
-	clk_disable_unprepare(udc->hclk);
-	clk_disable_unprepare(udc->pclk);
+	usba_stop(udc);
 
 
 	udc->driver = NULL;
 	udc->driver = NULL;
 
 
@@ -2057,6 +2101,7 @@ static int usba_udc_probe(struct platform_device *pdev)
 		return PTR_ERR(hclk);
 		return PTR_ERR(hclk);
 
 
 	spin_lock_init(&udc->lock);
 	spin_lock_init(&udc->lock);
+	mutex_init(&udc->vbus_mutex);
 	udc->pdev = pdev;
 	udc->pdev = pdev;
 	udc->pclk = pclk;
 	udc->pclk = pclk;
 	udc->hclk = hclk;
 	udc->hclk = hclk;
@@ -2111,17 +2156,17 @@ static int usba_udc_probe(struct platform_device *pdev)
 
 
 	if (gpio_is_valid(udc->vbus_pin)) {
 	if (gpio_is_valid(udc->vbus_pin)) {
 		if (!devm_gpio_request(&pdev->dev, udc->vbus_pin, "atmel_usba_udc")) {
 		if (!devm_gpio_request(&pdev->dev, udc->vbus_pin, "atmel_usba_udc")) {
-			ret = devm_request_irq(&pdev->dev,
-					gpio_to_irq(udc->vbus_pin),
-					usba_vbus_irq, 0,
+			irq_set_status_flags(gpio_to_irq(udc->vbus_pin),
+					IRQ_NOAUTOEN);
+			ret = devm_request_threaded_irq(&pdev->dev,
+					gpio_to_irq(udc->vbus_pin), NULL,
+					usba_vbus_irq_thread, IRQF_ONESHOT,
 					"atmel_usba_udc", udc);
 					"atmel_usba_udc", udc);
 			if (ret) {
 			if (ret) {
 				udc->vbus_pin = -ENODEV;
 				udc->vbus_pin = -ENODEV;
 				dev_warn(&udc->pdev->dev,
 				dev_warn(&udc->pdev->dev,
 					 "failed to request vbus irq; "
 					 "failed to request vbus irq; "
 					 "assuming always on\n");
 					 "assuming always on\n");
-			} else {
-				disable_irq(gpio_to_irq(udc->vbus_pin));
 			}
 			}
 		} else {
 		} else {
 			/* gpio_request fail so use -EINVAL for gpio_is_valid */
 			/* gpio_request fail so use -EINVAL for gpio_is_valid */
@@ -2132,6 +2177,7 @@ static int usba_udc_probe(struct platform_device *pdev)
 	ret = usb_add_gadget_udc(&pdev->dev, &udc->gadget);
 	ret = usb_add_gadget_udc(&pdev->dev, &udc->gadget);
 	if (ret)
 	if (ret)
 		return ret;
 		return ret;
+	device_init_wakeup(&pdev->dev, 1);
 
 
 	usba_init_debugfs(udc);
 	usba_init_debugfs(udc);
 	for (i = 1; i < udc->num_ep; i++)
 	for (i = 1; i < udc->num_ep; i++)
@@ -2147,6 +2193,7 @@ static int __exit usba_udc_remove(struct platform_device *pdev)
 
 
 	udc = platform_get_drvdata(pdev);
 	udc = platform_get_drvdata(pdev);
 
 
+	device_init_wakeup(&pdev->dev, 0);
 	usb_del_gadget_udc(&udc->gadget);
 	usb_del_gadget_udc(&udc->gadget);
 
 
 	for (i = 1; i < udc->num_ep; i++)
 	for (i = 1; i < udc->num_ep; i++)
@@ -2156,10 +2203,65 @@ static int __exit usba_udc_remove(struct platform_device *pdev)
 	return 0;
 	return 0;
 }
 }
 
 
+#ifdef CONFIG_PM
+static int usba_udc_suspend(struct device *dev)
+{
+	struct usba_udc *udc = dev_get_drvdata(dev);
+
+	/* Not started */
+	if (!udc->driver)
+		return 0;
+
+	mutex_lock(&udc->vbus_mutex);
+
+	if (!device_may_wakeup(dev)) {
+		usba_stop(udc);
+		goto out;
+	}
+
+	/*
+	 * Device may wake up. We stay clocked if we failed
+	 * to request vbus irq, assuming always on.
+	 */
+	if (gpio_is_valid(udc->vbus_pin)) {
+		usba_stop(udc);
+		enable_irq_wake(gpio_to_irq(udc->vbus_pin));
+	}
+
+out:
+	mutex_unlock(&udc->vbus_mutex);
+	return 0;
+}
+
+static int usba_udc_resume(struct device *dev)
+{
+	struct usba_udc *udc = dev_get_drvdata(dev);
+
+	/* Not started */
+	if (!udc->driver)
+		return 0;
+
+	if (device_may_wakeup(dev) && gpio_is_valid(udc->vbus_pin))
+		disable_irq_wake(gpio_to_irq(udc->vbus_pin));
+
+	/* If Vbus is present, enable the controller and wait for reset */
+	mutex_lock(&udc->vbus_mutex);
+	udc->vbus_prev = vbus_is_present(udc);
+	if (udc->vbus_prev)
+		usba_start(udc);
+	mutex_unlock(&udc->vbus_mutex);
+
+	return 0;
+}
+#endif
+
+static SIMPLE_DEV_PM_OPS(usba_udc_pm_ops, usba_udc_suspend, usba_udc_resume);
+
 static struct platform_driver udc_driver = {
 static struct platform_driver udc_driver = {
 	.remove		= __exit_p(usba_udc_remove),
 	.remove		= __exit_p(usba_udc_remove),
 	.driver		= {
 	.driver		= {
 		.name		= "atmel_usba_udc",
 		.name		= "atmel_usba_udc",
+		.pm		= &usba_udc_pm_ops,
 		.of_match_table	= of_match_ptr(atmel_udc_dt_ids),
 		.of_match_table	= of_match_ptr(atmel_udc_dt_ids),
 	},
 	},
 };
 };

+ 20 - 6
drivers/usb/gadget/udc/atmel_usba_udc.h

@@ -191,18 +191,28 @@
 	 | USBA_BF(name, value))
 	 | USBA_BF(name, value))
 
 
 /* Register access macros */
 /* Register access macros */
+#ifdef CONFIG_AVR32
+#define usba_io_readl	__raw_readl
+#define usba_io_writel	__raw_writel
+#define usba_io_writew	__raw_writew
+#else
+#define usba_io_readl	readl_relaxed
+#define usba_io_writel	writel_relaxed
+#define usba_io_writew	writew_relaxed
+#endif
+
 #define usba_readl(udc, reg)					\
 #define usba_readl(udc, reg)					\
-	__raw_readl((udc)->regs + USBA_##reg)
+	usba_io_readl((udc)->regs + USBA_##reg)
 #define usba_writel(udc, reg, value)				\
 #define usba_writel(udc, reg, value)				\
-	__raw_writel((value), (udc)->regs + USBA_##reg)
+	usba_io_writel((value), (udc)->regs + USBA_##reg)
 #define usba_ep_readl(ep, reg)					\
 #define usba_ep_readl(ep, reg)					\
-	__raw_readl((ep)->ep_regs + USBA_EPT_##reg)
+	usba_io_readl((ep)->ep_regs + USBA_EPT_##reg)
 #define usba_ep_writel(ep, reg, value)				\
 #define usba_ep_writel(ep, reg, value)				\
-	__raw_writel((value), (ep)->ep_regs + USBA_EPT_##reg)
+	usba_io_writel((value), (ep)->ep_regs + USBA_EPT_##reg)
 #define usba_dma_readl(ep, reg)					\
 #define usba_dma_readl(ep, reg)					\
-	__raw_readl((ep)->dma_regs + USBA_DMA_##reg)
+	usba_io_readl((ep)->dma_regs + USBA_DMA_##reg)
 #define usba_dma_writel(ep, reg, value)				\
 #define usba_dma_writel(ep, reg, value)				\
-	__raw_writel((value), (ep)->dma_regs + USBA_DMA_##reg)
+	usba_io_writel((value), (ep)->dma_regs + USBA_DMA_##reg)
 
 
 /* Calculate base address for a given endpoint or DMA controller */
 /* Calculate base address for a given endpoint or DMA controller */
 #define USBA_EPT_BASE(x)	(0x100 + (x) * 0x20)
 #define USBA_EPT_BASE(x)	(0x100 + (x) * 0x20)
@@ -313,6 +323,9 @@ struct usba_udc {
 	/* Protect hw registers from concurrent modifications */
 	/* Protect hw registers from concurrent modifications */
 	spinlock_t lock;
 	spinlock_t lock;
 
 
+	/* Mutex to prevent concurrent start or stop */
+	struct mutex vbus_mutex;
+
 	void __iomem *regs;
 	void __iomem *regs;
 	void __iomem *fifo;
 	void __iomem *fifo;
 
 
@@ -328,6 +341,7 @@ struct usba_udc {
 	struct clk *hclk;
 	struct clk *hclk;
 	struct usba_ep *usba_ep;
 	struct usba_ep *usba_ep;
 	bool bias_pulse_needed;
 	bool bias_pulse_needed;
+	bool clocked;
 
 
 	u16 devstatus;
 	u16 devstatus;
 
 

+ 1 - 1
drivers/usb/gadget/udc/dummy_hcd.c

@@ -2631,7 +2631,7 @@ static int __init init(void)
 		return -EINVAL;
 		return -EINVAL;
 
 
 	if (mod_data.num < 1 || mod_data.num > MAX_NUM_UDC) {
 	if (mod_data.num < 1 || mod_data.num > MAX_NUM_UDC) {
-		pr_err("Number of emulated UDC must be in range of 1%d\n",
+		pr_err("Number of emulated UDC must be in range of 1...%d\n",
 				MAX_NUM_UDC);
 				MAX_NUM_UDC);
 		return -EINVAL;
 		return -EINVAL;
 	}
 	}

+ 122 - 111
drivers/usb/gadget/udc/goku_udc.c

@@ -1024,35 +1024,79 @@ static const char proc_node_name [] = "driver/udc";
 static void dump_intmask(struct seq_file *m, const char *label, u32 mask)
 static void dump_intmask(struct seq_file *m, const char *label, u32 mask)
 {
 {
 	/* int_status is the same format ... */
 	/* int_status is the same format ... */
-	seq_printf(m,
-		"%s %05X =" FOURBITS EIGHTBITS EIGHTBITS "\n",
-		label, mask,
-		(mask & INT_PWRDETECT) ? " power" : "",
-		(mask & INT_SYSERROR) ? " sys" : "",
-		(mask & INT_MSTRDEND) ? " in-dma" : "",
-		(mask & INT_MSTWRTMOUT) ? " wrtmo" : "",
-
-		(mask & INT_MSTWREND) ? " out-dma" : "",
-		(mask & INT_MSTWRSET) ? " wrset" : "",
-		(mask & INT_ERR) ? " err" : "",
-		(mask & INT_SOF) ? " sof" : "",
-
-		(mask & INT_EP3NAK) ? " ep3nak" : "",
-		(mask & INT_EP2NAK) ? " ep2nak" : "",
-		(mask & INT_EP1NAK) ? " ep1nak" : "",
-		(mask & INT_EP3DATASET) ? " ep3" : "",
-
-		(mask & INT_EP2DATASET) ? " ep2" : "",
-		(mask & INT_EP1DATASET) ? " ep1" : "",
-		(mask & INT_STATUSNAK) ? " ep0snak" : "",
-		(mask & INT_STATUS) ? " ep0status" : "",
-
-		(mask & INT_SETUP) ? " setup" : "",
-		(mask & INT_ENDPOINT0) ? " ep0" : "",
-		(mask & INT_USBRESET) ? " reset" : "",
-		(mask & INT_SUSPEND) ? " suspend" : "");
+	seq_printf(m, "%s %05X =" FOURBITS EIGHTBITS EIGHTBITS "\n",
+		   label, mask,
+		   (mask & INT_PWRDETECT) ? " power" : "",
+		   (mask & INT_SYSERROR) ? " sys" : "",
+		   (mask & INT_MSTRDEND) ? " in-dma" : "",
+		   (mask & INT_MSTWRTMOUT) ? " wrtmo" : "",
+
+		   (mask & INT_MSTWREND) ? " out-dma" : "",
+		   (mask & INT_MSTWRSET) ? " wrset" : "",
+		   (mask & INT_ERR) ? " err" : "",
+		   (mask & INT_SOF) ? " sof" : "",
+
+		   (mask & INT_EP3NAK) ? " ep3nak" : "",
+		   (mask & INT_EP2NAK) ? " ep2nak" : "",
+		   (mask & INT_EP1NAK) ? " ep1nak" : "",
+		   (mask & INT_EP3DATASET) ? " ep3" : "",
+
+		   (mask & INT_EP2DATASET) ? " ep2" : "",
+		   (mask & INT_EP1DATASET) ? " ep1" : "",
+		   (mask & INT_STATUSNAK) ? " ep0snak" : "",
+		   (mask & INT_STATUS) ? " ep0status" : "",
+
+		   (mask & INT_SETUP) ? " setup" : "",
+		   (mask & INT_ENDPOINT0) ? " ep0" : "",
+		   (mask & INT_USBRESET) ? " reset" : "",
+		   (mask & INT_SUSPEND) ? " suspend" : "");
+}
+
+static const char *udc_ep_state(enum ep0state state)
+{
+	switch (state) {
+	case EP0_DISCONNECT:
+		return "ep0_disconnect";
+	case EP0_IDLE:
+		return "ep0_idle";
+	case EP0_IN:
+		return "ep0_in";
+	case EP0_OUT:
+		return "ep0_out";
+	case EP0_STATUS:
+		return "ep0_status";
+	case EP0_STALL:
+		return "ep0_stall";
+	case EP0_SUSPEND:
+		return "ep0_suspend";
+	}
+
+	return "ep0_?";
 }
 }
 
 
+static const char *udc_ep_status(u32 status)
+{
+	switch (status & EPxSTATUS_EP_MASK) {
+	case EPxSTATUS_EP_READY:
+		return "ready";
+	case EPxSTATUS_EP_DATAIN:
+		return "packet";
+	case EPxSTATUS_EP_FULL:
+		return "full";
+	case EPxSTATUS_EP_TX_ERR:	/* host will retry */
+		return "tx_err";
+	case EPxSTATUS_EP_RX_ERR:
+		return "rx_err";
+	case EPxSTATUS_EP_BUSY:		/* ep0 only */
+		return "busy";
+	case EPxSTATUS_EP_STALL:
+		return "stall";
+	case EPxSTATUS_EP_INVALID:	/* these "can't happen" */
+		return "invalid";
+	}
+
+	return "?";
+}
 
 
 static int udc_proc_read(struct seq_file *m, void *v)
 static int udc_proc_read(struct seq_file *m, void *v)
 {
 {
@@ -1068,29 +1112,18 @@ static int udc_proc_read(struct seq_file *m, void *v)
 	tmp = readl(&regs->power_detect);
 	tmp = readl(&regs->power_detect);
 	is_usb_connected = tmp & PW_DETECT;
 	is_usb_connected = tmp & PW_DETECT;
 	seq_printf(m,
 	seq_printf(m,
-		"%s - %s\n"
-		"%s version: %s %s\n"
-		"Gadget driver: %s\n"
-		"Host %s, %s\n"
-		"\n",
-		pci_name(dev->pdev), driver_desc,
-		driver_name, DRIVER_VERSION, dmastr(),
-		dev->driver ? dev->driver->driver.name : "(none)",
-		is_usb_connected
-			? ((tmp & PW_PULLUP) ? "full speed" : "powered")
-			: "disconnected",
-		({const char *state;
-		switch(dev->ep0state){
-		case EP0_DISCONNECT:	state = "ep0_disconnect"; break;
-		case EP0_IDLE:		state = "ep0_idle"; break;
-		case EP0_IN:		state = "ep0_in"; break;
-		case EP0_OUT:		state = "ep0_out"; break;
-		case EP0_STATUS:	state = "ep0_status"; break;
-		case EP0_STALL:		state = "ep0_stall"; break;
-		case EP0_SUSPEND:	state = "ep0_suspend"; break;
-		default:		state = "ep0_?"; break;
-		} state; })
-		);
+		   "%s - %s\n"
+		   "%s version: %s %s\n"
+		   "Gadget driver: %s\n"
+		   "Host %s, %s\n"
+		   "\n",
+		   pci_name(dev->pdev), driver_desc,
+		   driver_name, DRIVER_VERSION, dmastr(),
+		   dev->driver ? dev->driver->driver.name : "(none)",
+		   is_usb_connected
+			   ? ((tmp & PW_PULLUP) ? "full speed" : "powered")
+			   : "disconnected",
+		   udc_ep_state(dev->ep0state));
 
 
 	dump_intmask(m, "int_status", readl(&regs->int_status));
 	dump_intmask(m, "int_status", readl(&regs->int_status));
 	dump_intmask(m, "int_enable", readl(&regs->int_enable));
 	dump_intmask(m, "int_enable", readl(&regs->int_enable));
@@ -1099,31 +1132,30 @@ static int udc_proc_read(struct seq_file *m, void *v)
 		goto done;
 		goto done;
 
 
 	/* registers for (active) device and ep0 */
 	/* registers for (active) device and ep0 */
-	if (seq_printf(m, "\nirqs %lu\ndataset %02x "
-			"single.bcs %02x.%02x state %x addr %u\n",
-			dev->irqs, readl(&regs->DataSet),
-			readl(&regs->EPxSingle), readl(&regs->EPxBCS),
-			readl(&regs->UsbState),
-			readl(&regs->address)) < 0)
+	seq_printf(m, "\nirqs %lu\ndataset %02x single.bcs %02x.%02x state %x addr %u\n",
+		   dev->irqs, readl(&regs->DataSet),
+		   readl(&regs->EPxSingle), readl(&regs->EPxBCS),
+		   readl(&regs->UsbState),
+		   readl(&regs->address));
+	if (seq_has_overflowed(m))
 		goto done;
 		goto done;
 
 
 	tmp = readl(&regs->dma_master);
 	tmp = readl(&regs->dma_master);
-	if (seq_printf(m,
-		"dma %03X =" EIGHTBITS "%s %s\n", tmp,
-		(tmp & MST_EOPB_DIS) ? " eopb-" : "",
-		(tmp & MST_EOPB_ENA) ? " eopb+" : "",
-		(tmp & MST_TIMEOUT_DIS) ? " tmo-" : "",
-		(tmp & MST_TIMEOUT_ENA) ? " tmo+" : "",
-
-		(tmp & MST_RD_EOPB) ? " eopb" : "",
-		(tmp & MST_RD_RESET) ? " in_reset" : "",
-		(tmp & MST_WR_RESET) ? " out_reset" : "",
-		(tmp & MST_RD_ENA) ? " IN" : "",
-
-		(tmp & MST_WR_ENA) ? " OUT" : "",
-		(tmp & MST_CONNECTION)
-			? "ep1in/ep2out"
-			: "ep1out/ep2in") < 0)
+	seq_printf(m, "dma %03X =" EIGHTBITS "%s %s\n",
+		   tmp,
+		   (tmp & MST_EOPB_DIS) ? " eopb-" : "",
+		   (tmp & MST_EOPB_ENA) ? " eopb+" : "",
+		   (tmp & MST_TIMEOUT_DIS) ? " tmo-" : "",
+		   (tmp & MST_TIMEOUT_ENA) ? " tmo+" : "",
+
+		   (tmp & MST_RD_EOPB) ? " eopb" : "",
+		   (tmp & MST_RD_RESET) ? " in_reset" : "",
+		   (tmp & MST_WR_RESET) ? " out_reset" : "",
+		   (tmp & MST_RD_ENA) ? " IN" : "",
+
+		   (tmp & MST_WR_ENA) ? " OUT" : "",
+		   (tmp & MST_CONNECTION) ? "ep1in/ep2out" : "ep1out/ep2in");
+	if (seq_has_overflowed(m))
 		goto done;
 		goto done;
 
 
 	/* dump endpoint queues */
 	/* dump endpoint queues */
@@ -1135,44 +1167,23 @@ static int udc_proc_read(struct seq_file *m, void *v)
 			continue;
 			continue;
 
 
 		tmp = readl(ep->reg_status);
 		tmp = readl(ep->reg_status);
-		if (seq_printf(m,
-			"%s %s max %u %s, irqs %lu, "
-			"status %02x (%s) " FOURBITS "\n",
-			ep->ep.name,
-			ep->is_in ? "in" : "out",
-			ep->ep.maxpacket,
-			ep->dma ? "dma" : "pio",
-			ep->irqs,
-			tmp, ({ char *s;
-			switch (tmp & EPxSTATUS_EP_MASK) {
-			case EPxSTATUS_EP_READY:
-				s = "ready"; break;
-			case EPxSTATUS_EP_DATAIN:
-				s = "packet"; break;
-			case EPxSTATUS_EP_FULL:
-				s = "full"; break;
-			case EPxSTATUS_EP_TX_ERR:	// host will retry
-				s = "tx_err"; break;
-			case EPxSTATUS_EP_RX_ERR:
-				s = "rx_err"; break;
-			case EPxSTATUS_EP_BUSY:		/* ep0 only */
-				s = "busy"; break;
-			case EPxSTATUS_EP_STALL:
-				s = "stall"; break;
-			case EPxSTATUS_EP_INVALID:	// these "can't happen"
-				s = "invalid"; break;
-			default:
-				s = "?"; break;
-			} s; }),
-			(tmp & EPxSTATUS_TOGGLE) ? "data1" : "data0",
-			(tmp & EPxSTATUS_SUSPEND) ? " suspend" : "",
-			(tmp & EPxSTATUS_FIFO_DISABLE) ? " disable" : "",
-			(tmp & EPxSTATUS_STAGE_ERROR) ? " ep0stat" : ""
-			) < 0)
+		seq_printf(m, "%s %s max %u %s, irqs %lu, status %02x (%s) " FOURBITS "\n",
+			   ep->ep.name,
+			   ep->is_in ? "in" : "out",
+			   ep->ep.maxpacket,
+			   ep->dma ? "dma" : "pio",
+			   ep->irqs,
+			   tmp, udc_ep_status(tmp),
+			   (tmp & EPxSTATUS_TOGGLE) ? "data1" : "data0",
+			   (tmp & EPxSTATUS_SUSPEND) ? " suspend" : "",
+			   (tmp & EPxSTATUS_FIFO_DISABLE) ? " disable" : "",
+			   (tmp & EPxSTATUS_STAGE_ERROR) ? " ep0stat" : "");
+		if (seq_has_overflowed(m))
 			goto done;
 			goto done;
 
 
 		if (list_empty(&ep->queue)) {
 		if (list_empty(&ep->queue)) {
-			if (seq_puts(m, "\t(nothing queued)\n") < 0)
+			seq_puts(m, "\t(nothing queued)\n");
+			if (seq_has_overflowed(m))
 				goto done;
 				goto done;
 			continue;
 			continue;
 		}
 		}
@@ -1187,10 +1198,10 @@ static int udc_proc_read(struct seq_file *m, void *v)
 			} else
 			} else
 				tmp = req->req.actual;
 				tmp = req->req.actual;
 
 
-			if (seq_printf(m,
-				"\treq %p len %u/%u buf %p\n",
-				&req->req, tmp, req->req.length,
-				req->req.buf) < 0)
+			seq_printf(m, "\treq %p len %u/%u buf %p\n",
+				   &req->req, tmp, req->req.length,
+				   req->req.buf);
+			if (seq_has_overflowed(m))
 				goto done;
 				goto done;
 		}
 		}
 	}
 	}

+ 3 - 12
drivers/usb/gadget/udc/lpc32xx_udc.c

@@ -1803,23 +1803,14 @@ static int lpc32xx_ep_queue(struct usb_ep *_ep,
 	req = container_of(_req, struct lpc32xx_request, req);
 	req = container_of(_req, struct lpc32xx_request, req);
 	ep = container_of(_ep, struct lpc32xx_ep, ep);
 	ep = container_of(_ep, struct lpc32xx_ep, ep);
 
 
-	if (!_req || !_req->complete || !_req->buf ||
+	if (!_ep || !_req || !_req->complete || !_req->buf ||
 	    !list_empty(&req->queue))
 	    !list_empty(&req->queue))
 		return -EINVAL;
 		return -EINVAL;
 
 
 	udc = ep->udc;
 	udc = ep->udc;
 
 
-	if (!_ep) {
-		dev_dbg(udc->dev, "invalid ep\n");
-		return -EINVAL;
-	}
-
-
-	if ((!udc) || (!udc->driver) ||
-	    (udc->gadget.speed == USB_SPEED_UNKNOWN)) {
-		dev_dbg(udc->dev, "invalid device\n");
-		return -EINVAL;
-	}
+	if (udc->gadget.speed == USB_SPEED_UNKNOWN)
+		return -EPIPE;
 
 
 	if (ep->lep) {
 	if (ep->lep) {
 		struct lpc32xx_usbd_dd_gad *dd;
 		struct lpc32xx_usbd_dd_gad *dd;

+ 129 - 53
drivers/usb/gadget/udc/net2280.c

@@ -80,6 +80,13 @@ static const char *const ep_name[] = {
 	"ep-e", "ep-f", "ep-g", "ep-h",
 	"ep-e", "ep-f", "ep-g", "ep-h",
 };
 };
 
 
+/* Endpoint names for usb3380 advance mode */
+static const char *const ep_name_adv[] = {
+	ep0name,
+	"ep1in", "ep2out", "ep3in", "ep4out",
+	"ep1out", "ep2in", "ep3out", "ep4in",
+};
+
 /* mode 0 == ep-{a,b,c,d} 1K fifo each
 /* mode 0 == ep-{a,b,c,d} 1K fifo each
  * mode 1 == ep-{a,b} 2K fifo each, ep-{c,d} unavailable
  * mode 1 == ep-{a,b} 2K fifo each, ep-{c,d} unavailable
  * mode 2 == ep-a 2K fifo, ep-{b,c} 1K each, ep-d unavailable
  * mode 2 == ep-a 2K fifo, ep-{b,c} 1K each, ep-d unavailable
@@ -138,31 +145,44 @@ net2280_enable(struct usb_ep *_ep, const struct usb_endpoint_descriptor *desc)
 	u32			max, tmp;
 	u32			max, tmp;
 	unsigned long		flags;
 	unsigned long		flags;
 	static const u32 ep_key[9] = { 1, 0, 1, 0, 1, 1, 0, 1, 0 };
 	static const u32 ep_key[9] = { 1, 0, 1, 0, 1, 1, 0, 1, 0 };
+	int ret = 0;
 
 
 	ep = container_of(_ep, struct net2280_ep, ep);
 	ep = container_of(_ep, struct net2280_ep, ep);
 	if (!_ep || !desc || ep->desc || _ep->name == ep0name ||
 	if (!_ep || !desc || ep->desc || _ep->name == ep0name ||
-			desc->bDescriptorType != USB_DT_ENDPOINT)
+			desc->bDescriptorType != USB_DT_ENDPOINT) {
+		pr_err("%s: failed at line=%d\n", __func__, __LINE__);
 		return -EINVAL;
 		return -EINVAL;
+	}
 	dev = ep->dev;
 	dev = ep->dev;
-	if (!dev->driver || dev->gadget.speed == USB_SPEED_UNKNOWN)
-		return -ESHUTDOWN;
+	if (!dev->driver || dev->gadget.speed == USB_SPEED_UNKNOWN) {
+		ret = -ESHUTDOWN;
+		goto print_err;
+	}
 
 
 	/* erratum 0119 workaround ties up an endpoint number */
 	/* erratum 0119 workaround ties up an endpoint number */
-	if ((desc->bEndpointAddress & 0x0f) == EP_DONTUSE)
-		return -EDOM;
+	if ((desc->bEndpointAddress & 0x0f) == EP_DONTUSE) {
+		ret = -EDOM;
+		goto print_err;
+	}
 
 
 	if (dev->quirks & PLX_SUPERSPEED) {
 	if (dev->quirks & PLX_SUPERSPEED) {
-		if ((desc->bEndpointAddress & 0x0f) >= 0x0c)
-			return -EDOM;
+		if ((desc->bEndpointAddress & 0x0f) >= 0x0c) {
+			ret = -EDOM;
+			goto print_err;
+		}
 		ep->is_in = !!usb_endpoint_dir_in(desc);
 		ep->is_in = !!usb_endpoint_dir_in(desc);
-		if (dev->enhanced_mode && ep->is_in && ep_key[ep->num])
-			return -EINVAL;
+		if (dev->enhanced_mode && ep->is_in && ep_key[ep->num]) {
+			ret = -EINVAL;
+			goto print_err;
+		}
 	}
 	}
 
 
 	/* sanity check ep-e/ep-f since their fifos are small */
 	/* sanity check ep-e/ep-f since their fifos are small */
 	max = usb_endpoint_maxp(desc) & 0x1fff;
 	max = usb_endpoint_maxp(desc) & 0x1fff;
-	if (ep->num > 4 && max > 64 && (dev->quirks & PLX_LEGACY))
-		return -ERANGE;
+	if (ep->num > 4 && max > 64 && (dev->quirks & PLX_LEGACY)) {
+		ret = -ERANGE;
+		goto print_err;
+	}
 
 
 	spin_lock_irqsave(&dev->lock, flags);
 	spin_lock_irqsave(&dev->lock, flags);
 	_ep->maxpacket = max & 0x7ff;
 	_ep->maxpacket = max & 0x7ff;
@@ -192,7 +212,8 @@ net2280_enable(struct usb_ep *_ep, const struct usb_endpoint_descriptor *desc)
 		    (dev->gadget.speed == USB_SPEED_HIGH && max != 512) ||
 		    (dev->gadget.speed == USB_SPEED_HIGH && max != 512) ||
 		    (dev->gadget.speed == USB_SPEED_FULL && max > 64)) {
 		    (dev->gadget.speed == USB_SPEED_FULL && max > 64)) {
 			spin_unlock_irqrestore(&dev->lock, flags);
 			spin_unlock_irqrestore(&dev->lock, flags);
-			return -ERANGE;
+			ret = -ERANGE;
+			goto print_err;
 		}
 		}
 	}
 	}
 	ep->is_iso = (tmp == USB_ENDPOINT_XFER_ISOC);
 	ep->is_iso = (tmp == USB_ENDPOINT_XFER_ISOC);
@@ -271,7 +292,11 @@ net2280_enable(struct usb_ep *_ep, const struct usb_endpoint_descriptor *desc)
 
 
 	/* pci writes may still be posted */
 	/* pci writes may still be posted */
 	spin_unlock_irqrestore(&dev->lock, flags);
 	spin_unlock_irqrestore(&dev->lock, flags);
-	return 0;
+	return ret;
+
+print_err:
+	dev_err(&ep->dev->pdev->dev, "%s: error=%d\n", __func__, ret);
+	return ret;
 }
 }
 
 
 static int handshake(u32 __iomem *ptr, u32 mask, u32 done, int usec)
 static int handshake(u32 __iomem *ptr, u32 mask, u32 done, int usec)
@@ -426,9 +451,10 @@ static int net2280_disable(struct usb_ep *_ep)
 	unsigned long		flags;
 	unsigned long		flags;
 
 
 	ep = container_of(_ep, struct net2280_ep, ep);
 	ep = container_of(_ep, struct net2280_ep, ep);
-	if (!_ep || !ep->desc || _ep->name == ep0name)
+	if (!_ep || !ep->desc || _ep->name == ep0name) {
+		pr_err("%s: Invalid ep=%p or ep->desc\n", __func__, _ep);
 		return -EINVAL;
 		return -EINVAL;
-
+	}
 	spin_lock_irqsave(&ep->dev->lock, flags);
 	spin_lock_irqsave(&ep->dev->lock, flags);
 	nuke(ep);
 	nuke(ep);
 
 
@@ -458,8 +484,10 @@ static struct usb_request
 	struct net2280_ep	*ep;
 	struct net2280_ep	*ep;
 	struct net2280_request	*req;
 	struct net2280_request	*req;
 
 
-	if (!_ep)
+	if (!_ep) {
+		pr_err("%s: Invalid ep\n", __func__);
 		return NULL;
 		return NULL;
+	}
 	ep = container_of(_ep, struct net2280_ep, ep);
 	ep = container_of(_ep, struct net2280_ep, ep);
 
 
 	req = kzalloc(sizeof(*req), gfp_flags);
 	req = kzalloc(sizeof(*req), gfp_flags);
@@ -491,8 +519,11 @@ static void net2280_free_request(struct usb_ep *_ep, struct usb_request *_req)
 	struct net2280_request	*req;
 	struct net2280_request	*req;
 
 
 	ep = container_of(_ep, struct net2280_ep, ep);
 	ep = container_of(_ep, struct net2280_ep, ep);
-	if (!_ep || !_req)
+	if (!_ep || !_req) {
+		dev_err(&ep->dev->pdev->dev, "%s: Inavlid ep=%p or req=%p\n",
+							__func__, _ep, _req);
 		return;
 		return;
+	}
 
 
 	req = container_of(_req, struct net2280_request, req);
 	req = container_of(_req, struct net2280_request, req);
 	WARN_ON(!list_empty(&req->queue));
 	WARN_ON(!list_empty(&req->queue));
@@ -896,35 +927,44 @@ net2280_queue(struct usb_ep *_ep, struct usb_request *_req, gfp_t gfp_flags)
 	struct net2280_ep	*ep;
 	struct net2280_ep	*ep;
 	struct net2280		*dev;
 	struct net2280		*dev;
 	unsigned long		flags;
 	unsigned long		flags;
+	int ret = 0;
 
 
 	/* we always require a cpu-view buffer, so that we can
 	/* we always require a cpu-view buffer, so that we can
 	 * always use pio (as fallback or whatever).
 	 * always use pio (as fallback or whatever).
 	 */
 	 */
-	req = container_of(_req, struct net2280_request, req);
-	if (!_req || !_req->complete || !_req->buf ||
-				!list_empty(&req->queue))
-		return -EINVAL;
-	if (_req->length > (~0 & DMA_BYTE_COUNT_MASK))
-		return -EDOM;
 	ep = container_of(_ep, struct net2280_ep, ep);
 	ep = container_of(_ep, struct net2280_ep, ep);
-	if (!_ep || (!ep->desc && ep->num != 0))
+	if (!_ep || (!ep->desc && ep->num != 0)) {
+		pr_err("%s: Invalid ep=%p or ep->desc\n", __func__, _ep);
 		return -EINVAL;
 		return -EINVAL;
+	}
+	req = container_of(_req, struct net2280_request, req);
+	if (!_req || !_req->complete || !_req->buf ||
+				!list_empty(&req->queue)) {
+		ret = -EINVAL;
+		goto print_err;
+	}
+	if (_req->length > (~0 & DMA_BYTE_COUNT_MASK)) {
+		ret = -EDOM;
+		goto print_err;
+	}
 	dev = ep->dev;
 	dev = ep->dev;
-	if (!dev->driver || dev->gadget.speed == USB_SPEED_UNKNOWN)
-		return -ESHUTDOWN;
+	if (!dev->driver || dev->gadget.speed == USB_SPEED_UNKNOWN) {
+		ret = -ESHUTDOWN;
+		goto print_err;
+	}
 
 
 	/* FIXME implement PIO fallback for ZLPs with DMA */
 	/* FIXME implement PIO fallback for ZLPs with DMA */
-	if (ep->dma && _req->length == 0)
-		return -EOPNOTSUPP;
+	if (ep->dma && _req->length == 0) {
+		ret = -EOPNOTSUPP;
+		goto print_err;
+	}
 
 
 	/* set up dma mapping in case the caller didn't */
 	/* set up dma mapping in case the caller didn't */
 	if (ep->dma) {
 	if (ep->dma) {
-		int ret;
-
 		ret = usb_gadget_map_request(&dev->gadget, _req,
 		ret = usb_gadget_map_request(&dev->gadget, _req,
 				ep->is_in);
 				ep->is_in);
 		if (ret)
 		if (ret)
-			return ret;
+			goto print_err;
 	}
 	}
 
 
 	ep_vdbg(dev, "%s queue req %p, len %d buf %p\n",
 	ep_vdbg(dev, "%s queue req %p, len %d buf %p\n",
@@ -1013,7 +1053,11 @@ done:
 	spin_unlock_irqrestore(&dev->lock, flags);
 	spin_unlock_irqrestore(&dev->lock, flags);
 
 
 	/* pci writes may still be posted */
 	/* pci writes may still be posted */
-	return 0;
+	return ret;
+
+print_err:
+	dev_err(&ep->dev->pdev->dev, "%s: error=%d\n", __func__, ret);
+	return ret;
 }
 }
 
 
 static inline void
 static inline void
@@ -1134,8 +1178,11 @@ static int net2280_dequeue(struct usb_ep *_ep, struct usb_request *_req)
 	int			stopped;
 	int			stopped;
 
 
 	ep = container_of(_ep, struct net2280_ep, ep);
 	ep = container_of(_ep, struct net2280_ep, ep);
-	if (!_ep || (!ep->desc && ep->num != 0) || !_req)
+	if (!_ep || (!ep->desc && ep->num != 0) || !_req) {
+		pr_err("%s: Invalid ep=%p or ep->desc or req=%p\n",
+						__func__, _ep, _req);
 		return -EINVAL;
 		return -EINVAL;
+	}
 
 
 	spin_lock_irqsave(&ep->dev->lock, flags);
 	spin_lock_irqsave(&ep->dev->lock, flags);
 	stopped = ep->stopped;
 	stopped = ep->stopped;
@@ -1157,6 +1204,8 @@ static int net2280_dequeue(struct usb_ep *_ep, struct usb_request *_req)
 	}
 	}
 	if (&req->req != _req) {
 	if (&req->req != _req) {
 		spin_unlock_irqrestore(&ep->dev->lock, flags);
 		spin_unlock_irqrestore(&ep->dev->lock, flags);
+		dev_err(&ep->dev->pdev->dev, "%s: Request mismatch\n",
+								__func__);
 		return -EINVAL;
 		return -EINVAL;
 	}
 	}
 
 
@@ -1214,20 +1263,28 @@ net2280_set_halt_and_wedge(struct usb_ep *_ep, int value, int wedged)
 	int			retval = 0;
 	int			retval = 0;
 
 
 	ep = container_of(_ep, struct net2280_ep, ep);
 	ep = container_of(_ep, struct net2280_ep, ep);
-	if (!_ep || (!ep->desc && ep->num != 0))
+	if (!_ep || (!ep->desc && ep->num != 0)) {
+		pr_err("%s: Invalid ep=%p or ep->desc\n", __func__, _ep);
 		return -EINVAL;
 		return -EINVAL;
-	if (!ep->dev->driver || ep->dev->gadget.speed == USB_SPEED_UNKNOWN)
-		return -ESHUTDOWN;
+	}
+	if (!ep->dev->driver || ep->dev->gadget.speed == USB_SPEED_UNKNOWN) {
+		retval = -ESHUTDOWN;
+		goto print_err;
+	}
 	if (ep->desc /* not ep0 */ && (ep->desc->bmAttributes & 0x03)
 	if (ep->desc /* not ep0 */ && (ep->desc->bmAttributes & 0x03)
-						== USB_ENDPOINT_XFER_ISOC)
-		return -EINVAL;
+						== USB_ENDPOINT_XFER_ISOC) {
+		retval = -EINVAL;
+		goto print_err;
+	}
 
 
 	spin_lock_irqsave(&ep->dev->lock, flags);
 	spin_lock_irqsave(&ep->dev->lock, flags);
-	if (!list_empty(&ep->queue))
+	if (!list_empty(&ep->queue)) {
 		retval = -EAGAIN;
 		retval = -EAGAIN;
-	else if (ep->is_in && value && net2280_fifo_status(_ep) != 0)
+		goto print_unlock;
+	} else if (ep->is_in && value && net2280_fifo_status(_ep) != 0) {
 		retval = -EAGAIN;
 		retval = -EAGAIN;
-	else {
+		goto print_unlock;
+	} else {
 		ep_vdbg(ep->dev, "%s %s %s\n", _ep->name,
 		ep_vdbg(ep->dev, "%s %s %s\n", _ep->name,
 				value ? "set" : "clear",
 				value ? "set" : "clear",
 				wedged ? "wedge" : "halt");
 				wedged ? "wedge" : "halt");
@@ -1251,6 +1308,12 @@ net2280_set_halt_and_wedge(struct usb_ep *_ep, int value, int wedged)
 	spin_unlock_irqrestore(&ep->dev->lock, flags);
 	spin_unlock_irqrestore(&ep->dev->lock, flags);
 
 
 	return retval;
 	return retval;
+
+print_unlock:
+	spin_unlock_irqrestore(&ep->dev->lock, flags);
+print_err:
+	dev_err(&ep->dev->pdev->dev, "%s: error=%d\n", __func__, retval);
+	return retval;
 }
 }
 
 
 static int net2280_set_halt(struct usb_ep *_ep, int value)
 static int net2280_set_halt(struct usb_ep *_ep, int value)
@@ -1260,8 +1323,10 @@ static int net2280_set_halt(struct usb_ep *_ep, int value)
 
 
 static int net2280_set_wedge(struct usb_ep *_ep)
 static int net2280_set_wedge(struct usb_ep *_ep)
 {
 {
-	if (!_ep || _ep->name == ep0name)
+	if (!_ep || _ep->name == ep0name) {
+		pr_err("%s: Invalid ep=%p or ep0\n", __func__, _ep);
 		return -EINVAL;
 		return -EINVAL;
+	}
 	return net2280_set_halt_and_wedge(_ep, 1, 1);
 	return net2280_set_halt_and_wedge(_ep, 1, 1);
 }
 }
 
 
@@ -1271,14 +1336,22 @@ static int net2280_fifo_status(struct usb_ep *_ep)
 	u32			avail;
 	u32			avail;
 
 
 	ep = container_of(_ep, struct net2280_ep, ep);
 	ep = container_of(_ep, struct net2280_ep, ep);
-	if (!_ep || (!ep->desc && ep->num != 0))
+	if (!_ep || (!ep->desc && ep->num != 0)) {
+		pr_err("%s: Invalid ep=%p or ep->desc\n", __func__, _ep);
 		return -ENODEV;
 		return -ENODEV;
-	if (!ep->dev->driver || ep->dev->gadget.speed == USB_SPEED_UNKNOWN)
+	}
+	if (!ep->dev->driver || ep->dev->gadget.speed == USB_SPEED_UNKNOWN) {
+		dev_err(&ep->dev->pdev->dev,
+			"%s: Invalid driver=%p or speed=%d\n",
+			__func__, ep->dev->driver, ep->dev->gadget.speed);
 		return -ESHUTDOWN;
 		return -ESHUTDOWN;
+	}
 
 
 	avail = readl(&ep->regs->ep_avail) & (BIT(12) - 1);
 	avail = readl(&ep->regs->ep_avail) & (BIT(12) - 1);
-	if (avail > ep->fifo_size)
+	if (avail > ep->fifo_size) {
+		dev_err(&ep->dev->pdev->dev, "%s: Fifo overflow\n", __func__);
 		return -EOVERFLOW;
 		return -EOVERFLOW;
+	}
 	if (ep->is_in)
 	if (ep->is_in)
 		avail = ep->fifo_size - avail;
 		avail = ep->fifo_size - avail;
 	return avail;
 	return avail;
@@ -1289,10 +1362,16 @@ static void net2280_fifo_flush(struct usb_ep *_ep)
 	struct net2280_ep	*ep;
 	struct net2280_ep	*ep;
 
 
 	ep = container_of(_ep, struct net2280_ep, ep);
 	ep = container_of(_ep, struct net2280_ep, ep);
-	if (!_ep || (!ep->desc && ep->num != 0))
+	if (!_ep || (!ep->desc && ep->num != 0)) {
+		pr_err("%s: Invalid ep=%p or ep->desc\n", __func__, _ep);
 		return;
 		return;
-	if (!ep->dev->driver || ep->dev->gadget.speed == USB_SPEED_UNKNOWN)
+	}
+	if (!ep->dev->driver || ep->dev->gadget.speed == USB_SPEED_UNKNOWN) {
+		dev_err(&ep->dev->pdev->dev,
+			"%s: Invalid driver=%p or speed=%d\n",
+			__func__, ep->dev->driver, ep->dev->gadget.speed);
 		return;
 		return;
+	}
 
 
 	writel(BIT(FIFO_FLUSH), &ep->regs->ep_stat);
 	writel(BIT(FIFO_FLUSH), &ep->regs->ep_stat);
 	(void) readl(&ep->regs->ep_rsp);
 	(void) readl(&ep->regs->ep_rsp);
@@ -1977,7 +2056,7 @@ static void usb_reinit_338x(struct net2280 *dev)
 	for (i = 0; i < dev->n_ep; i++) {
 	for (i = 0; i < dev->n_ep; i++) {
 		struct net2280_ep *ep = &dev->ep[i];
 		struct net2280_ep *ep = &dev->ep[i];
 
 
-		ep->ep.name = ep_name[i];
+		ep->ep.name = dev->enhanced_mode ? ep_name_adv[i] : ep_name[i];
 		ep->dev = dev;
 		ep->dev = dev;
 		ep->num = i;
 		ep->num = i;
 
 
@@ -1989,11 +2068,9 @@ static void usb_reinit_338x(struct net2280 *dev)
 			ep->regs = (struct net2280_ep_regs __iomem *)
 			ep->regs = (struct net2280_ep_regs __iomem *)
 				(((void __iomem *)&dev->epregs[ne[i]]) +
 				(((void __iomem *)&dev->epregs[ne[i]]) +
 				ep_reg_addr[i]);
 				ep_reg_addr[i]);
-			ep->fiforegs = &dev->fiforegs[i];
 		} else {
 		} else {
 			ep->cfg = &dev->epregs[i];
 			ep->cfg = &dev->epregs[i];
 			ep->regs = &dev->epregs[i];
 			ep->regs = &dev->epregs[i];
-			ep->fiforegs = &dev->fiforegs[i];
 		}
 		}
 
 
 		ep->fifo_size = (i != 0) ? 2048 : 512;
 		ep->fifo_size = (i != 0) ? 2048 : 512;
@@ -2186,7 +2263,6 @@ static int net2280_start(struct usb_gadget *_gadget,
 		dev->ep[i].irqs = 0;
 		dev->ep[i].irqs = 0;
 
 
 	/* hook up the driver ... */
 	/* hook up the driver ... */
-	dev->softconnect = 1;
 	driver->driver.bus = NULL;
 	driver->driver.bus = NULL;
 	dev->driver = driver;
 	dev->driver = driver;
 
 
@@ -3052,6 +3128,8 @@ next_endpoints:
 		BIT(PCI_RETRY_ABORT_INTERRUPT))
 		BIT(PCI_RETRY_ABORT_INTERRUPT))
 
 
 static void handle_stat1_irqs(struct net2280 *dev, u32 stat)
 static void handle_stat1_irqs(struct net2280 *dev, u32 stat)
+__releases(dev->lock)
+__acquires(dev->lock)
 {
 {
 	struct net2280_ep	*ep;
 	struct net2280_ep	*ep;
 	u32			tmp, num, mask, scratch;
 	u32			tmp, num, mask, scratch;
@@ -3373,8 +3451,6 @@ static int net2280_probe(struct pci_dev *pdev, const struct pci_device_id *id)
 		u32 usbstat;
 		u32 usbstat;
 		dev->usb_ext = (struct usb338x_usb_ext_regs __iomem *)
 		dev->usb_ext = (struct usb338x_usb_ext_regs __iomem *)
 							(base + 0x00b4);
 							(base + 0x00b4);
-		dev->fiforegs = (struct usb338x_fifo_regs __iomem *)
-							(base + 0x0500);
 		dev->llregs = (struct usb338x_ll_regs __iomem *)
 		dev->llregs = (struct usb338x_ll_regs __iomem *)
 							(base + 0x0700);
 							(base + 0x0700);
 		dev->ll_lfps_regs = (struct usb338x_ll_lfps_regs __iomem *)
 		dev->ll_lfps_regs = (struct usb338x_ll_lfps_regs __iomem *)

+ 0 - 2
drivers/usb/gadget/udc/net2280.h

@@ -96,7 +96,6 @@ struct net2280_ep {
 	struct net2280_ep_regs			__iomem *regs;
 	struct net2280_ep_regs			__iomem *regs;
 	struct net2280_dma_regs			__iomem *dma;
 	struct net2280_dma_regs			__iomem *dma;
 	struct net2280_dma			*dummy;
 	struct net2280_dma			*dummy;
-	struct usb338x_fifo_regs __iomem *fiforegs;
 	dma_addr_t				td_dma;	/* of dummy */
 	dma_addr_t				td_dma;	/* of dummy */
 	struct net2280				*dev;
 	struct net2280				*dev;
 	unsigned long				irqs;
 	unsigned long				irqs;
@@ -181,7 +180,6 @@ struct net2280 {
 	struct net2280_dma_regs		__iomem *dma;
 	struct net2280_dma_regs		__iomem *dma;
 	struct net2280_dep_regs		__iomem *dep;
 	struct net2280_dep_regs		__iomem *dep;
 	struct net2280_ep_regs		__iomem *epregs;
 	struct net2280_ep_regs		__iomem *epregs;
-	struct usb338x_fifo_regs	__iomem *fiforegs;
 	struct usb338x_ll_regs		__iomem *llregs;
 	struct usb338x_ll_regs		__iomem *llregs;
 	struct usb338x_ll_lfps_regs	__iomem *ll_lfps_regs;
 	struct usb338x_ll_lfps_regs	__iomem *ll_lfps_regs;
 	struct usb338x_ll_tsn_regs	__iomem *ll_tsn_regs;
 	struct usb338x_ll_tsn_regs	__iomem *ll_tsn_regs;

+ 60 - 72
drivers/usb/gadget/udc/pxa27x_udc.c

@@ -93,50 +93,46 @@ static void handle_ep(struct pxa_ep *ep);
 static int state_dbg_show(struct seq_file *s, void *p)
 static int state_dbg_show(struct seq_file *s, void *p)
 {
 {
 	struct pxa_udc *udc = s->private;
 	struct pxa_udc *udc = s->private;
-	int pos = 0, ret;
 	u32 tmp;
 	u32 tmp;
 
 
-	ret = -ENODEV;
 	if (!udc->driver)
 	if (!udc->driver)
-		goto out;
+		return -ENODEV;
 
 
 	/* basic device status */
 	/* basic device status */
-	pos += seq_printf(s, DRIVER_DESC "\n"
-			 "%s version: %s\nGadget driver: %s\n",
-			 driver_name, DRIVER_VERSION,
-			 udc->driver ? udc->driver->driver.name : "(none)");
+	seq_printf(s, DRIVER_DESC "\n"
+		   "%s version: %s\n"
+		   "Gadget driver: %s\n",
+		   driver_name, DRIVER_VERSION,
+		   udc->driver ? udc->driver->driver.name : "(none)");
 
 
 	tmp = udc_readl(udc, UDCCR);
 	tmp = udc_readl(udc, UDCCR);
-	pos += seq_printf(s,
-			 "udccr=0x%0x(%s%s%s%s%s%s%s%s%s%s), "
-			 "con=%d,inter=%d,altinter=%d\n", tmp,
-			 (tmp & UDCCR_OEN) ? " oen":"",
-			 (tmp & UDCCR_AALTHNP) ? " aalthnp":"",
-			 (tmp & UDCCR_AHNP) ? " rem" : "",
-			 (tmp & UDCCR_BHNP) ? " rstir" : "",
-			 (tmp & UDCCR_DWRE) ? " dwre" : "",
-			 (tmp & UDCCR_SMAC) ? " smac" : "",
-			 (tmp & UDCCR_EMCE) ? " emce" : "",
-			 (tmp & UDCCR_UDR) ? " udr" : "",
-			 (tmp & UDCCR_UDA) ? " uda" : "",
-			 (tmp & UDCCR_UDE) ? " ude" : "",
-			 (tmp & UDCCR_ACN) >> UDCCR_ACN_S,
-			 (tmp & UDCCR_AIN) >> UDCCR_AIN_S,
-			 (tmp & UDCCR_AAISN) >> UDCCR_AAISN_S);
+	seq_printf(s,
+		   "udccr=0x%0x(%s%s%s%s%s%s%s%s%s%s), con=%d,inter=%d,altinter=%d\n",
+		   tmp,
+		   (tmp & UDCCR_OEN) ? " oen":"",
+		   (tmp & UDCCR_AALTHNP) ? " aalthnp":"",
+		   (tmp & UDCCR_AHNP) ? " rem" : "",
+		   (tmp & UDCCR_BHNP) ? " rstir" : "",
+		   (tmp & UDCCR_DWRE) ? " dwre" : "",
+		   (tmp & UDCCR_SMAC) ? " smac" : "",
+		   (tmp & UDCCR_EMCE) ? " emce" : "",
+		   (tmp & UDCCR_UDR) ? " udr" : "",
+		   (tmp & UDCCR_UDA) ? " uda" : "",
+		   (tmp & UDCCR_UDE) ? " ude" : "",
+		   (tmp & UDCCR_ACN) >> UDCCR_ACN_S,
+		   (tmp & UDCCR_AIN) >> UDCCR_AIN_S,
+		   (tmp & UDCCR_AAISN) >> UDCCR_AAISN_S);
 	/* registers for device and ep0 */
 	/* registers for device and ep0 */
-	pos += seq_printf(s, "udcicr0=0x%08x udcicr1=0x%08x\n",
-			udc_readl(udc, UDCICR0), udc_readl(udc, UDCICR1));
-	pos += seq_printf(s, "udcisr0=0x%08x udcisr1=0x%08x\n",
-			udc_readl(udc, UDCISR0), udc_readl(udc, UDCISR1));
-	pos += seq_printf(s, "udcfnr=%d\n", udc_readl(udc, UDCFNR));
-	pos += seq_printf(s, "irqs: reset=%lu, suspend=%lu, resume=%lu, "
-			"reconfig=%lu\n",
-			udc->stats.irqs_reset, udc->stats.irqs_suspend,
-			udc->stats.irqs_resume, udc->stats.irqs_reconfig);
-
-	ret = 0;
-out:
-	return ret;
+	seq_printf(s, "udcicr0=0x%08x udcicr1=0x%08x\n",
+		   udc_readl(udc, UDCICR0), udc_readl(udc, UDCICR1));
+	seq_printf(s, "udcisr0=0x%08x udcisr1=0x%08x\n",
+		   udc_readl(udc, UDCISR0), udc_readl(udc, UDCISR1));
+	seq_printf(s, "udcfnr=%d\n", udc_readl(udc, UDCFNR));
+	seq_printf(s, "irqs: reset=%lu, suspend=%lu, resume=%lu, reconfig=%lu\n",
+		   udc->stats.irqs_reset, udc->stats.irqs_suspend,
+		   udc->stats.irqs_resume, udc->stats.irqs_reconfig);
+
+	return 0;
 }
 }
 
 
 static int queues_dbg_show(struct seq_file *s, void *p)
 static int queues_dbg_show(struct seq_file *s, void *p)
@@ -144,75 +140,67 @@ static int queues_dbg_show(struct seq_file *s, void *p)
 	struct pxa_udc *udc = s->private;
 	struct pxa_udc *udc = s->private;
 	struct pxa_ep *ep;
 	struct pxa_ep *ep;
 	struct pxa27x_request *req;
 	struct pxa27x_request *req;
-	int pos = 0, i, maxpkt, ret;
+	int i, maxpkt;
 
 
-	ret = -ENODEV;
 	if (!udc->driver)
 	if (!udc->driver)
-		goto out;
+		return -ENODEV;
 
 
 	/* dump endpoint queues */
 	/* dump endpoint queues */
 	for (i = 0; i < NR_PXA_ENDPOINTS; i++) {
 	for (i = 0; i < NR_PXA_ENDPOINTS; i++) {
 		ep = &udc->pxa_ep[i];
 		ep = &udc->pxa_ep[i];
 		maxpkt = ep->fifo_size;
 		maxpkt = ep->fifo_size;
-		pos += seq_printf(s,  "%-12s max_pkt=%d %s\n",
-				EPNAME(ep), maxpkt, "pio");
+		seq_printf(s,  "%-12s max_pkt=%d %s\n",
+			   EPNAME(ep), maxpkt, "pio");
 
 
 		if (list_empty(&ep->queue)) {
 		if (list_empty(&ep->queue)) {
-			pos += seq_printf(s, "\t(nothing queued)\n");
+			seq_puts(s, "\t(nothing queued)\n");
 			continue;
 			continue;
 		}
 		}
 
 
 		list_for_each_entry(req, &ep->queue, queue) {
 		list_for_each_entry(req, &ep->queue, queue) {
-			pos += seq_printf(s,  "\treq %p len %d/%d buf %p\n",
-					&req->req, req->req.actual,
-					req->req.length, req->req.buf);
+			seq_printf(s,  "\treq %p len %d/%d buf %p\n",
+				   &req->req, req->req.actual,
+				   req->req.length, req->req.buf);
 		}
 		}
 	}
 	}
 
 
-	ret = 0;
-out:
-	return ret;
+	return 0;
 }
 }
 
 
 static int eps_dbg_show(struct seq_file *s, void *p)
 static int eps_dbg_show(struct seq_file *s, void *p)
 {
 {
 	struct pxa_udc *udc = s->private;
 	struct pxa_udc *udc = s->private;
 	struct pxa_ep *ep;
 	struct pxa_ep *ep;
-	int pos = 0, i, ret;
+	int i;
 	u32 tmp;
 	u32 tmp;
 
 
-	ret = -ENODEV;
 	if (!udc->driver)
 	if (!udc->driver)
-		goto out;
+		return -ENODEV;
 
 
 	ep = &udc->pxa_ep[0];
 	ep = &udc->pxa_ep[0];
 	tmp = udc_ep_readl(ep, UDCCSR);
 	tmp = udc_ep_readl(ep, UDCCSR);
-	pos += seq_printf(s, "udccsr0=0x%03x(%s%s%s%s%s%s%s)\n", tmp,
-			 (tmp & UDCCSR0_SA) ? " sa" : "",
-			 (tmp & UDCCSR0_RNE) ? " rne" : "",
-			 (tmp & UDCCSR0_FST) ? " fst" : "",
-			 (tmp & UDCCSR0_SST) ? " sst" : "",
-			 (tmp & UDCCSR0_DME) ? " dme" : "",
-			 (tmp & UDCCSR0_IPR) ? " ipr" : "",
-			 (tmp & UDCCSR0_OPC) ? " opc" : "");
+	seq_printf(s, "udccsr0=0x%03x(%s%s%s%s%s%s%s)\n",
+		   tmp,
+		   (tmp & UDCCSR0_SA) ? " sa" : "",
+		   (tmp & UDCCSR0_RNE) ? " rne" : "",
+		   (tmp & UDCCSR0_FST) ? " fst" : "",
+		   (tmp & UDCCSR0_SST) ? " sst" : "",
+		   (tmp & UDCCSR0_DME) ? " dme" : "",
+		   (tmp & UDCCSR0_IPR) ? " ipr" : "",
+		   (tmp & UDCCSR0_OPC) ? " opc" : "");
 	for (i = 0; i < NR_PXA_ENDPOINTS; i++) {
 	for (i = 0; i < NR_PXA_ENDPOINTS; i++) {
 		ep = &udc->pxa_ep[i];
 		ep = &udc->pxa_ep[i];
 		tmp = i? udc_ep_readl(ep, UDCCR) : udc_readl(udc, UDCCR);
 		tmp = i? udc_ep_readl(ep, UDCCR) : udc_readl(udc, UDCCR);
-		pos += seq_printf(s, "%-12s: "
-				"IN %lu(%lu reqs), OUT %lu(%lu reqs), "
-				"irqs=%lu, udccr=0x%08x, udccsr=0x%03x, "
-				"udcbcr=%d\n",
-				EPNAME(ep),
-				ep->stats.in_bytes, ep->stats.in_ops,
-				ep->stats.out_bytes, ep->stats.out_ops,
-				ep->stats.irqs,
-				tmp, udc_ep_readl(ep, UDCCSR),
-				udc_ep_readl(ep, UDCBCR));
+		seq_printf(s, "%-12s: IN %lu(%lu reqs), OUT %lu(%lu reqs), irqs=%lu, udccr=0x%08x, udccsr=0x%03x, udcbcr=%d\n",
+			   EPNAME(ep),
+			   ep->stats.in_bytes, ep->stats.in_ops,
+			   ep->stats.out_bytes, ep->stats.out_ops,
+			   ep->stats.irqs,
+			   tmp, udc_ep_readl(ep, UDCCSR),
+			   udc_ep_readl(ep, UDCBCR));
 	}
 	}
 
 
-	ret = 0;
-out:
-	return ret;
+	return 0;
 }
 }
 
 
 static int eps_dbg_open(struct inode *inode, struct file *file)
 static int eps_dbg_open(struct inode *inode, struct file *file)

+ 42 - 26
drivers/usb/gadget/udc/udc-core.c

@@ -35,6 +35,8 @@
  * @dev - the child device to the actual controller
  * @dev - the child device to the actual controller
  * @gadget - the gadget. For use by the class code
  * @gadget - the gadget. For use by the class code
  * @list - for use by the udc class driver
  * @list - for use by the udc class driver
+ * @vbus - for udcs who care about vbus status, this value is real vbus status;
+ * for udcs who do not care about vbus status, this value is always true
  *
  *
  * This represents the internal data structure which is used by the UDC-class
  * This represents the internal data structure which is used by the UDC-class
  * to hold information about udc driver and gadget together.
  * to hold information about udc driver and gadget together.
@@ -44,6 +46,7 @@ struct usb_udc {
 	struct usb_gadget		*gadget;
 	struct usb_gadget		*gadget;
 	struct device			dev;
 	struct device			dev;
 	struct list_head		list;
 	struct list_head		list;
+	bool				vbus;
 };
 };
 
 
 static struct class *udc_class;
 static struct class *udc_class;
@@ -128,21 +131,11 @@ EXPORT_SYMBOL_GPL(usb_gadget_giveback_request);
 
 
 static void usb_gadget_state_work(struct work_struct *work)
 static void usb_gadget_state_work(struct work_struct *work)
 {
 {
-	struct usb_gadget	*gadget = work_to_gadget(work);
-	struct usb_udc		*udc = NULL;
-
-	mutex_lock(&udc_lock);
-	list_for_each_entry(udc, &udc_list, list)
-		if (udc->gadget == gadget)
-			goto found;
-	mutex_unlock(&udc_lock);
-
-	return;
-
-found:
-	mutex_unlock(&udc_lock);
+	struct usb_gadget *gadget = work_to_gadget(work);
+	struct usb_udc *udc = gadget->udc;
 
 
-	sysfs_notify(&udc->dev.kobj, NULL, "state");
+	if (udc)
+		sysfs_notify(&udc->dev.kobj, NULL, "state");
 }
 }
 
 
 void usb_gadget_set_state(struct usb_gadget *gadget,
 void usb_gadget_set_state(struct usb_gadget *gadget,
@@ -155,6 +148,34 @@ EXPORT_SYMBOL_GPL(usb_gadget_set_state);
 
 
 /* ------------------------------------------------------------------------- */
 /* ------------------------------------------------------------------------- */
 
 
+static void usb_udc_connect_control(struct usb_udc *udc)
+{
+	if (udc->vbus)
+		usb_gadget_connect(udc->gadget);
+	else
+		usb_gadget_disconnect(udc->gadget);
+}
+
+/**
+ * usb_udc_vbus_handler - updates the udc core vbus status, and try to
+ * connect or disconnect gadget
+ * @gadget: The gadget which vbus change occurs
+ * @status: The vbus status
+ *
+ * The udc driver calls it when it wants to connect or disconnect gadget
+ * according to vbus status.
+ */
+void usb_udc_vbus_handler(struct usb_gadget *gadget, bool status)
+{
+	struct usb_udc *udc = gadget->udc;
+
+	if (udc) {
+		udc->vbus = status;
+		usb_udc_connect_control(udc);
+	}
+}
+EXPORT_SYMBOL_GPL(usb_udc_vbus_handler);
+
 /**
 /**
  * usb_gadget_udc_reset - notifies the udc core that bus reset occurs
  * usb_gadget_udc_reset - notifies the udc core that bus reset occurs
  * @gadget: The gadget which bus reset occurs
  * @gadget: The gadget which bus reset occurs
@@ -278,6 +299,7 @@ int usb_add_gadget_udc_release(struct device *parent, struct usb_gadget *gadget,
 		goto err3;
 		goto err3;
 
 
 	udc->gadget = gadget;
 	udc->gadget = gadget;
+	gadget->udc = udc;
 
 
 	mutex_lock(&udc_lock);
 	mutex_lock(&udc_lock);
 	list_add_tail(&udc->list, &udc_list);
 	list_add_tail(&udc->list, &udc_list);
@@ -287,6 +309,7 @@ int usb_add_gadget_udc_release(struct device *parent, struct usb_gadget *gadget,
 		goto err4;
 		goto err4;
 
 
 	usb_gadget_set_state(gadget, USB_STATE_NOTATTACHED);
 	usb_gadget_set_state(gadget, USB_STATE_NOTATTACHED);
+	udc->vbus = true;
 
 
 	mutex_unlock(&udc_lock);
 	mutex_unlock(&udc_lock);
 
 
@@ -348,21 +371,14 @@ static void usb_gadget_remove_driver(struct usb_udc *udc)
  */
  */
 void usb_del_gadget_udc(struct usb_gadget *gadget)
 void usb_del_gadget_udc(struct usb_gadget *gadget)
 {
 {
-	struct usb_udc		*udc = NULL;
+	struct usb_udc *udc = gadget->udc;
 
 
-	mutex_lock(&udc_lock);
-	list_for_each_entry(udc, &udc_list, list)
-		if (udc->gadget == gadget)
-			goto found;
-
-	dev_err(gadget->dev.parent, "gadget not registered.\n");
-	mutex_unlock(&udc_lock);
-
-	return;
+	if (!udc)
+		return;
 
 
-found:
 	dev_vdbg(gadget->dev.parent, "unregistering gadget\n");
 	dev_vdbg(gadget->dev.parent, "unregistering gadget\n");
 
 
+	mutex_lock(&udc_lock);
 	list_del(&udc->list);
 	list_del(&udc->list);
 	mutex_unlock(&udc_lock);
 	mutex_unlock(&udc_lock);
 
 
@@ -397,7 +413,7 @@ static int udc_bind_to_driver(struct usb_udc *udc, struct usb_gadget_driver *dri
 		driver->unbind(udc->gadget);
 		driver->unbind(udc->gadget);
 		goto err1;
 		goto err1;
 	}
 	}
-	usb_gadget_connect(udc->gadget);
+	usb_udc_connect_control(udc);
 
 
 	kobject_uevent(&udc->dev.kobj, KOBJ_CHANGE);
 	kobject_uevent(&udc->dev.kobj, KOBJ_CHANGE);
 	return 0;
 	return 0;

+ 107 - 107
drivers/usb/musb/musb_core.c

@@ -507,7 +507,8 @@ void musb_hnp_stop(struct musb *musb)
 	musb->port1_status &= ~(USB_PORT_STAT_C_CONNECTION << 16);
 	musb->port1_status &= ~(USB_PORT_STAT_C_CONNECTION << 16);
 }
 }
 
 
-static void musb_generic_disable(struct musb *musb);
+static void musb_recover_from_babble(struct musb *musb);
+
 /*
 /*
  * Interrupt Service Routine to record USB "global" interrupts.
  * Interrupt Service Routine to record USB "global" interrupts.
  * Since these do not happen often and signify things of
  * Since these do not happen often and signify things of
@@ -534,30 +535,16 @@ static irqreturn_t musb_stage0_irq(struct musb *musb, u8 int_usb,
 	 */
 	 */
 	if (int_usb & MUSB_INTR_RESUME) {
 	if (int_usb & MUSB_INTR_RESUME) {
 		handled = IRQ_HANDLED;
 		handled = IRQ_HANDLED;
-		dev_dbg(musb->controller, "RESUME (%s)\n", usb_otg_state_string(musb->xceiv->otg->state));
+		dev_dbg(musb->controller, "RESUME (%s)\n",
+				usb_otg_state_string(musb->xceiv->otg->state));
 
 
 		if (devctl & MUSB_DEVCTL_HM) {
 		if (devctl & MUSB_DEVCTL_HM) {
-			void __iomem *mbase = musb->mregs;
-			u8 power;
-
 			switch (musb->xceiv->otg->state) {
 			switch (musb->xceiv->otg->state) {
 			case OTG_STATE_A_SUSPEND:
 			case OTG_STATE_A_SUSPEND:
 				/* remote wakeup?  later, GetPortStatus
 				/* remote wakeup?  later, GetPortStatus
 				 * will stop RESUME signaling
 				 * will stop RESUME signaling
 				 */
 				 */
 
 
-				power = musb_readb(musb->mregs, MUSB_POWER);
-				if (power & MUSB_POWER_SUSPENDM) {
-					/* spurious */
-					musb->int_usb &= ~MUSB_INTR_SUSPEND;
-					dev_dbg(musb->controller, "Spurious SUSPENDM\n");
-					break;
-				}
-
-				power &= ~MUSB_POWER_SUSPENDM;
-				musb_writeb(mbase, MUSB_POWER,
-						power | MUSB_POWER_RESUME);
-
 				musb->port1_status |=
 				musb->port1_status |=
 						(USB_PORT_STAT_C_SUSPEND << 16)
 						(USB_PORT_STAT_C_SUSPEND << 16)
 						| MUSB_PORT_STAT_RESUME;
 						| MUSB_PORT_STAT_RESUME;
@@ -775,10 +762,6 @@ static irqreturn_t musb_stage0_irq(struct musb *musb, u8 int_usb,
 
 
 		musb->ep0_stage = MUSB_EP0_START;
 		musb->ep0_stage = MUSB_EP0_START;
 
 
-		/* flush endpoints when transitioning from Device Mode */
-		if (is_peripheral_active(musb)) {
-			/* REVISIT HNP; just force disconnect */
-		}
 		musb->intrtxe = musb->epmask;
 		musb->intrtxe = musb->epmask;
 		musb_writew(musb->mregs, MUSB_INTRTXE, musb->intrtxe);
 		musb_writew(musb->mregs, MUSB_INTRTXE, musb->intrtxe);
 		musb->intrrxe = musb->epmask & 0xfffe;
 		musb->intrrxe = musb->epmask & 0xfffe;
@@ -879,20 +862,19 @@ b_host:
 	 */
 	 */
 	if (int_usb & MUSB_INTR_RESET) {
 	if (int_usb & MUSB_INTR_RESET) {
 		handled = IRQ_HANDLED;
 		handled = IRQ_HANDLED;
-		if ((devctl & MUSB_DEVCTL_HM) != 0) {
+		if (devctl & MUSB_DEVCTL_HM) {
 			/*
 			/*
-			 * Looks like non-HS BABBLE can be ignored, but
-			 * HS BABBLE is an error condition. For HS the solution
-			 * is to avoid babble in the first place and fix what
-			 * caused BABBLE. When HS BABBLE happens we can only
-			 * stop the session.
+			 * When BABBLE happens what we can depends on which
+			 * platform MUSB is running, because some platforms
+			 * implemented proprietary means for 'recovering' from
+			 * Babble conditions. One such platform is AM335x. In
+			 * most cases, however, the only thing we can do is
+			 * drop the session.
 			 */
 			 */
-			if (devctl & (MUSB_DEVCTL_FSDEV | MUSB_DEVCTL_LSDEV))
-				dev_dbg(musb->controller, "BABBLE devctl: %02x\n", devctl);
-			else {
-				ERR("Stopping host session -- babble\n");
-				musb_writeb(musb->mregs, MUSB_DEVCTL, 0);
-			}
+			dev_err(musb->controller, "Babble\n");
+
+			if (is_host_active(musb))
+				musb_recover_from_babble(musb);
 		} else {
 		} else {
 			dev_dbg(musb->controller, "BUS RESET as %s\n",
 			dev_dbg(musb->controller, "BUS RESET as %s\n",
 				usb_otg_state_string(musb->xceiv->otg->state));
 				usb_otg_state_string(musb->xceiv->otg->state));
@@ -931,13 +913,6 @@ b_host:
 		}
 		}
 	}
 	}
 
 
-	/* handle babble condition */
-	if (int_usb & MUSB_INTR_BABBLE && is_host_active(musb)) {
-		musb_generic_disable(musb);
-		schedule_delayed_work(&musb->recover_work,
-				      msecs_to_jiffies(100));
-	}
-
 #if 0
 #if 0
 /* REVISIT ... this would be for multiplexing periodic endpoints, or
 /* REVISIT ... this would be for multiplexing periodic endpoints, or
  * supporting transfer phasing to prevent exceeding ISO bandwidth
  * supporting transfer phasing to prevent exceeding ISO bandwidth
@@ -990,7 +965,7 @@ b_host:
 
 
 /*-------------------------------------------------------------------------*/
 /*-------------------------------------------------------------------------*/
 
 
-static void musb_generic_disable(struct musb *musb)
+static void musb_disable_interrupts(struct musb *musb)
 {
 {
 	void __iomem	*mbase = musb->mregs;
 	void __iomem	*mbase = musb->mregs;
 	u16	temp;
 	u16	temp;
@@ -1002,14 +977,33 @@ static void musb_generic_disable(struct musb *musb)
 	musb->intrrxe = 0;
 	musb->intrrxe = 0;
 	musb_writew(mbase, MUSB_INTRRXE, 0);
 	musb_writew(mbase, MUSB_INTRRXE, 0);
 
 
-	/* off */
-	musb_writeb(mbase, MUSB_DEVCTL, 0);
-
 	/*  flush pending interrupts */
 	/*  flush pending interrupts */
 	temp = musb_readb(mbase, MUSB_INTRUSB);
 	temp = musb_readb(mbase, MUSB_INTRUSB);
 	temp = musb_readw(mbase, MUSB_INTRTX);
 	temp = musb_readw(mbase, MUSB_INTRTX);
 	temp = musb_readw(mbase, MUSB_INTRRX);
 	temp = musb_readw(mbase, MUSB_INTRRX);
+}
+
+static void musb_enable_interrupts(struct musb *musb)
+{
+	void __iomem    *regs = musb->mregs;
+
+	/*  Set INT enable registers, enable interrupts */
+	musb->intrtxe = musb->epmask;
+	musb_writew(regs, MUSB_INTRTXE, musb->intrtxe);
+	musb->intrrxe = musb->epmask & 0xfffe;
+	musb_writew(regs, MUSB_INTRRXE, musb->intrrxe);
+	musb_writeb(regs, MUSB_INTRUSBE, 0xf7);
+
+}
+
+static void musb_generic_disable(struct musb *musb)
+{
+	void __iomem	*mbase = musb->mregs;
 
 
+	musb_disable_interrupts(musb);
+
+	/* off */
+	musb_writeb(mbase, MUSB_DEVCTL, 0);
 }
 }
 
 
 /*
 /*
@@ -1022,13 +1016,7 @@ void musb_start(struct musb *musb)
 
 
 	dev_dbg(musb->controller, "<== devctl %02x\n", devctl);
 	dev_dbg(musb->controller, "<== devctl %02x\n", devctl);
 
 
-	/*  Set INT enable registers, enable interrupts */
-	musb->intrtxe = musb->epmask;
-	musb_writew(regs, MUSB_INTRTXE, musb->intrtxe);
-	musb->intrrxe = musb->epmask & 0xfffe;
-	musb_writew(regs, MUSB_INTRRXE, musb->intrrxe);
-	musb_writeb(regs, MUSB_INTRUSBE, 0xf7);
-
+	musb_enable_interrupts(musb);
 	musb_writeb(regs, MUSB_TESTMODE, 0);
 	musb_writeb(regs, MUSB_TESTMODE, 0);
 
 
 	/* put into basic highspeed mode and start session */
 	/* put into basic highspeed mode and start session */
@@ -1587,9 +1575,12 @@ static int musb_core_init(u16 musb_type, struct musb *musb)
 irqreturn_t musb_interrupt(struct musb *musb)
 irqreturn_t musb_interrupt(struct musb *musb)
 {
 {
 	irqreturn_t	retval = IRQ_NONE;
 	irqreturn_t	retval = IRQ_NONE;
+	unsigned long	status;
+	unsigned long	epnum;
 	u8		devctl;
 	u8		devctl;
-	int		ep_num;
-	u32		reg;
+
+	if (!musb->int_usb && !musb->int_tx && !musb->int_rx)
+		return IRQ_NONE;
 
 
 	devctl = musb_readb(musb->mregs, MUSB_DEVCTL);
 	devctl = musb_readb(musb->mregs, MUSB_DEVCTL);
 
 
@@ -1597,56 +1588,57 @@ irqreturn_t musb_interrupt(struct musb *musb)
 		is_host_active(musb) ? "host" : "peripheral",
 		is_host_active(musb) ? "host" : "peripheral",
 		musb->int_usb, musb->int_tx, musb->int_rx);
 		musb->int_usb, musb->int_tx, musb->int_rx);
 
 
-	/* the core can interrupt us for multiple reasons; docs have
-	 * a generic interrupt flowchart to follow
+	/**
+	 * According to Mentor Graphics' documentation, flowchart on page 98,
+	 * IRQ should be handled as follows:
+	 *
+	 * . Resume IRQ
+	 * . Session Request IRQ
+	 * . VBUS Error IRQ
+	 * . Suspend IRQ
+	 * . Connect IRQ
+	 * . Disconnect IRQ
+	 * . Reset/Babble IRQ
+	 * . SOF IRQ (we're not using this one)
+	 * . Endpoint 0 IRQ
+	 * . TX Endpoints
+	 * . RX Endpoints
+	 *
+	 * We will be following that flowchart in order to avoid any problems
+	 * that might arise with internal Finite State Machine.
 	 */
 	 */
-	if (musb->int_usb)
-		retval |= musb_stage0_irq(musb, musb->int_usb,
-				devctl);
 
 
-	/* "stage 1" is handling endpoint irqs */
+	if (musb->int_usb)
+		retval |= musb_stage0_irq(musb, musb->int_usb, devctl);
 
 
-	/* handle endpoint 0 first */
 	if (musb->int_tx & 1) {
 	if (musb->int_tx & 1) {
 		if (is_host_active(musb))
 		if (is_host_active(musb))
 			retval |= musb_h_ep0_irq(musb);
 			retval |= musb_h_ep0_irq(musb);
 		else
 		else
 			retval |= musb_g_ep0_irq(musb);
 			retval |= musb_g_ep0_irq(musb);
+
+		/* we have just handled endpoint 0 IRQ, clear it */
+		musb->int_tx &= ~BIT(0);
 	}
 	}
 
 
-	/* RX on endpoints 1-15 */
-	reg = musb->int_rx >> 1;
-	ep_num = 1;
-	while (reg) {
-		if (reg & 1) {
-			/* musb_ep_select(musb->mregs, ep_num); */
-			/* REVISIT just retval = ep->rx_irq(...) */
-			retval = IRQ_HANDLED;
-			if (is_host_active(musb))
-				musb_host_rx(musb, ep_num);
-			else
-				musb_g_rx(musb, ep_num);
-		}
+	status = musb->int_tx;
 
 
-		reg >>= 1;
-		ep_num++;
+	for_each_set_bit(epnum, &status, 16) {
+		retval = IRQ_HANDLED;
+		if (is_host_active(musb))
+			musb_host_tx(musb, epnum);
+		else
+			musb_g_tx(musb, epnum);
 	}
 	}
 
 
-	/* TX on endpoints 1-15 */
-	reg = musb->int_tx >> 1;
-	ep_num = 1;
-	while (reg) {
-		if (reg & 1) {
-			/* musb_ep_select(musb->mregs, ep_num); */
-			/* REVISIT just retval |= ep->tx_irq(...) */
-			retval = IRQ_HANDLED;
-			if (is_host_active(musb))
-				musb_host_tx(musb, ep_num);
-			else
-				musb_g_tx(musb, ep_num);
-		}
-		reg >>= 1;
-		ep_num++;
+	status = musb->int_rx;
+
+	for_each_set_bit(epnum, &status, 16) {
+		retval = IRQ_HANDLED;
+		if (is_host_active(musb))
+			musb_host_rx(musb, epnum);
+		else
+			musb_g_rx(musb, epnum);
 	}
 	}
 
 
 	return retval;
 	return retval;
@@ -1825,33 +1817,44 @@ static void musb_irq_work(struct work_struct *data)
 	}
 	}
 }
 }
 
 
-/* Recover from babble interrupt conditions */
-static void musb_recover_work(struct work_struct *data)
+static void musb_recover_from_babble(struct musb *musb)
 {
 {
-	struct musb *musb = container_of(data, struct musb, recover_work.work);
-	int status, ret;
+	int ret;
+	u8 devctl;
 
 
-	ret  = musb_platform_reset(musb);
-	if (ret)
+	musb_disable_interrupts(musb);
+
+	/*
+	 * wait at least 320 cycles of 60MHz clock. That's 5.3us, we will give
+	 * it some slack and wait for 10us.
+	 */
+	udelay(10);
+
+	ret  = musb_platform_recover(musb);
+	if (ret) {
+		musb_enable_interrupts(musb);
 		return;
 		return;
+	}
 
 
-	usb_phy_vbus_off(musb->xceiv);
-	usleep_range(100, 200);
+	/* drop session bit */
+	devctl = musb_readb(musb->mregs, MUSB_DEVCTL);
+	devctl &= ~MUSB_DEVCTL_SESSION;
+	musb_writeb(musb->mregs, MUSB_DEVCTL, devctl);
 
 
-	usb_phy_vbus_on(musb->xceiv);
-	usleep_range(100, 200);
+	/* tell usbcore about it */
+	musb_root_disconnect(musb);
 
 
 	/*
 	/*
 	 * When a babble condition occurs, the musb controller
 	 * When a babble condition occurs, the musb controller
 	 * removes the session bit and the endpoint config is lost.
 	 * removes the session bit and the endpoint config is lost.
 	 */
 	 */
 	if (musb->dyn_fifo)
 	if (musb->dyn_fifo)
-		status = ep_config_from_table(musb);
+		ret = ep_config_from_table(musb);
 	else
 	else
-		status = ep_config_from_hw(musb);
+		ret = ep_config_from_hw(musb);
 
 
-	/* start the session again */
-	if (status == 0)
+	/* restart session */
+	if (ret == 0)
 		musb_start(musb);
 		musb_start(musb);
 }
 }
 
 
@@ -2087,7 +2090,6 @@ musb_init_controller(struct device *dev, int nIrq, void __iomem *ctrl)
 
 
 	/* Init IRQ workqueue before request_irq */
 	/* Init IRQ workqueue before request_irq */
 	INIT_WORK(&musb->irq_work, musb_irq_work);
 	INIT_WORK(&musb->irq_work, musb_irq_work);
-	INIT_DELAYED_WORK(&musb->recover_work, musb_recover_work);
 	INIT_DELAYED_WORK(&musb->deassert_reset_work, musb_deassert_reset);
 	INIT_DELAYED_WORK(&musb->deassert_reset_work, musb_deassert_reset);
 	INIT_DELAYED_WORK(&musb->finish_resume_work, musb_host_finish_resume);
 	INIT_DELAYED_WORK(&musb->finish_resume_work, musb_host_finish_resume);
 
 
@@ -2183,7 +2185,6 @@ fail4:
 
 
 fail3:
 fail3:
 	cancel_work_sync(&musb->irq_work);
 	cancel_work_sync(&musb->irq_work);
-	cancel_delayed_work_sync(&musb->recover_work);
 	cancel_delayed_work_sync(&musb->finish_resume_work);
 	cancel_delayed_work_sync(&musb->finish_resume_work);
 	cancel_delayed_work_sync(&musb->deassert_reset_work);
 	cancel_delayed_work_sync(&musb->deassert_reset_work);
 	if (musb->dma_controller)
 	if (musb->dma_controller)
@@ -2249,7 +2250,6 @@ static int musb_remove(struct platform_device *pdev)
 		dma_controller_destroy(musb->dma_controller);
 		dma_controller_destroy(musb->dma_controller);
 
 
 	cancel_work_sync(&musb->irq_work);
 	cancel_work_sync(&musb->irq_work);
-	cancel_delayed_work_sync(&musb->recover_work);
 	cancel_delayed_work_sync(&musb->finish_resume_work);
 	cancel_delayed_work_sync(&musb->finish_resume_work);
 	cancel_delayed_work_sync(&musb->deassert_reset_work);
 	cancel_delayed_work_sync(&musb->deassert_reset_work);
 	musb_free(musb);
 	musb_free(musb);

+ 7 - 7
drivers/usb/musb/musb_core.h

@@ -160,7 +160,8 @@ struct musb_io;
  * @init:	turns on clocks, sets up platform-specific registers, etc
  * @init:	turns on clocks, sets up platform-specific registers, etc
  * @exit:	undoes @init
  * @exit:	undoes @init
  * @set_mode:	forcefully changes operating mode
  * @set_mode:	forcefully changes operating mode
- * @try_ilde:	tries to idle the IP
+ * @try_idle:	tries to idle the IP
+ * @recover:	platform-specific babble recovery
  * @vbus_status: returns vbus status if possible
  * @vbus_status: returns vbus status if possible
  * @set_vbus:	forces vbus status
  * @set_vbus:	forces vbus status
  * @adjust_channel_params: pre check for standard dma channel_program func
  * @adjust_channel_params: pre check for standard dma channel_program func
@@ -196,7 +197,7 @@ struct musb_platform_ops {
 	void	(*write_fifo)(struct musb_hw_ep *hw_ep, u16 len, const u8 *buf);
 	void	(*write_fifo)(struct musb_hw_ep *hw_ep, u16 len, const u8 *buf);
 	int	(*set_mode)(struct musb *musb, u8 mode);
 	int	(*set_mode)(struct musb *musb, u8 mode);
 	void	(*try_idle)(struct musb *musb, unsigned long timeout);
 	void	(*try_idle)(struct musb *musb, unsigned long timeout);
-	int	(*reset)(struct musb *musb);
+	int	(*recover)(struct musb *musb);
 
 
 	int	(*vbus_status)(struct musb *musb);
 	int	(*vbus_status)(struct musb *musb);
 	void	(*set_vbus)(struct musb *musb, int on);
 	void	(*set_vbus)(struct musb *musb, int on);
@@ -300,7 +301,6 @@ struct musb {
 
 
 	irqreturn_t		(*isr)(int, void *);
 	irqreturn_t		(*isr)(int, void *);
 	struct work_struct	irq_work;
 	struct work_struct	irq_work;
-	struct delayed_work	recover_work;
 	struct delayed_work	deassert_reset_work;
 	struct delayed_work	deassert_reset_work;
 	struct delayed_work	finish_resume_work;
 	struct delayed_work	finish_resume_work;
 	u16			hwvers;
 	u16			hwvers;
@@ -558,12 +558,12 @@ static inline void musb_platform_try_idle(struct musb *musb,
 		musb->ops->try_idle(musb, timeout);
 		musb->ops->try_idle(musb, timeout);
 }
 }
 
 
-static inline int  musb_platform_reset(struct musb *musb)
+static inline int  musb_platform_recover(struct musb *musb)
 {
 {
-	if (!musb->ops->reset)
-		return -EINVAL;
+	if (!musb->ops->recover)
+		return 0;
 
 
-	return musb->ops->reset(musb);
+	return musb->ops->recover(musb);
 }
 }
 
 
 static inline int musb_platform_get_vbus_status(struct musb *musb)
 static inline int musb_platform_get_vbus_status(struct musb *musb)

+ 42 - 42
drivers/usb/musb/musb_cppi41.c

@@ -225,10 +225,12 @@ static void cppi41_dma_callback(void *private_data)
 	struct dma_channel *channel = private_data;
 	struct dma_channel *channel = private_data;
 	struct cppi41_dma_channel *cppi41_channel = channel->private_data;
 	struct cppi41_dma_channel *cppi41_channel = channel->private_data;
 	struct musb_hw_ep *hw_ep = cppi41_channel->hw_ep;
 	struct musb_hw_ep *hw_ep = cppi41_channel->hw_ep;
+	struct cppi41_dma_controller *controller;
 	struct musb *musb = hw_ep->musb;
 	struct musb *musb = hw_ep->musb;
 	unsigned long flags;
 	unsigned long flags;
 	struct dma_tx_state txstate;
 	struct dma_tx_state txstate;
 	u32 transferred;
 	u32 transferred;
+	int is_hs = 0;
 	bool empty;
 	bool empty;
 
 
 	spin_lock_irqsave(&musb->lock, flags);
 	spin_lock_irqsave(&musb->lock, flags);
@@ -248,61 +250,59 @@ static void cppi41_dma_callback(void *private_data)
 			transferred < cppi41_channel->packet_sz)
 			transferred < cppi41_channel->packet_sz)
 		cppi41_channel->prog_len = 0;
 		cppi41_channel->prog_len = 0;
 
 
-	empty = musb_is_tx_fifo_empty(hw_ep);
-	if (empty) {
+	if (cppi41_channel->is_tx)
+		empty = musb_is_tx_fifo_empty(hw_ep);
+
+	if (!cppi41_channel->is_tx || empty) {
 		cppi41_trans_done(cppi41_channel);
 		cppi41_trans_done(cppi41_channel);
-	} else {
-		struct cppi41_dma_controller *controller;
-		int is_hs = 0;
-		/*
-		 * On AM335x it has been observed that the TX interrupt fires
-		 * too early that means the TXFIFO is not yet empty but the DMA
-		 * engine says that it is done with the transfer. We don't
-		 * receive a FIFO empty interrupt so the only thing we can do is
-		 * to poll for the bit. On HS it usually takes 2us, on FS around
-		 * 110us - 150us depending on the transfer size.
-		 * We spin on HS (no longer than than 25us and setup a timer on
-		 * FS to check for the bit and complete the transfer.
-		 */
-		controller = cppi41_channel->controller;
+		goto out;
+	}
 
 
-		if (is_host_active(musb)) {
-			if (musb->port1_status & USB_PORT_STAT_HIGH_SPEED)
-				is_hs = 1;
-		} else {
-			if (musb->g.speed == USB_SPEED_HIGH)
-				is_hs = 1;
-		}
-		if (is_hs) {
-			unsigned wait = 25;
-
-			do {
-				empty = musb_is_tx_fifo_empty(hw_ep);
-				if (empty)
-					break;
-				wait--;
-				if (!wait)
-					break;
-				udelay(1);
-			} while (1);
+	/*
+	 * On AM335x it has been observed that the TX interrupt fires
+	 * too early that means the TXFIFO is not yet empty but the DMA
+	 * engine says that it is done with the transfer. We don't
+	 * receive a FIFO empty interrupt so the only thing we can do is
+	 * to poll for the bit. On HS it usually takes 2us, on FS around
+	 * 110us - 150us depending on the transfer size.
+	 * We spin on HS (no longer than than 25us and setup a timer on
+	 * FS to check for the bit and complete the transfer.
+	 */
+	controller = cppi41_channel->controller;
+
+	if (is_host_active(musb)) {
+		if (musb->port1_status & USB_PORT_STAT_HIGH_SPEED)
+			is_hs = 1;
+	} else {
+		if (musb->g.speed == USB_SPEED_HIGH)
+			is_hs = 1;
+	}
+	if (is_hs) {
+		unsigned wait = 25;
 
 
+		do {
 			empty = musb_is_tx_fifo_empty(hw_ep);
 			empty = musb_is_tx_fifo_empty(hw_ep);
 			if (empty) {
 			if (empty) {
 				cppi41_trans_done(cppi41_channel);
 				cppi41_trans_done(cppi41_channel);
 				goto out;
 				goto out;
 			}
 			}
-		}
-		list_add_tail(&cppi41_channel->tx_check,
-				&controller->early_tx_list);
-		if (!hrtimer_is_queued(&controller->early_tx)) {
-			unsigned long usecs = cppi41_channel->total_len / 10;
+			wait--;
+			if (!wait)
+				break;
+			cpu_relax();
+		} while (1);
+	}
+	list_add_tail(&cppi41_channel->tx_check,
+			&controller->early_tx_list);
+	if (!hrtimer_is_queued(&controller->early_tx)) {
+		unsigned long usecs = cppi41_channel->total_len / 10;
 
 
-			hrtimer_start_range_ns(&controller->early_tx,
+		hrtimer_start_range_ns(&controller->early_tx,
 				ktime_set(0, usecs * NSEC_PER_USEC),
 				ktime_set(0, usecs * NSEC_PER_USEC),
 				20 * NSEC_PER_USEC,
 				20 * NSEC_PER_USEC,
 				HRTIMER_MODE_REL);
 				HRTIMER_MODE_REL);
-		}
 	}
 	}
+
 out:
 out:
 	spin_unlock_irqrestore(&musb->lock, flags);
 	spin_unlock_irqrestore(&musb->lock, flags);
 }
 }

+ 44 - 61
drivers/usb/musb/musb_dsps.c

@@ -119,7 +119,7 @@ struct dsps_musb_wrapper {
 	unsigned	iddig:5;
 	unsigned	iddig:5;
 	unsigned	iddig_mux:5;
 	unsigned	iddig_mux:5;
 	/* miscellaneous stuff */
 	/* miscellaneous stuff */
-	u8		poll_seconds;
+	unsigned	poll_timeout;
 };
 };
 
 
 /*
 /*
@@ -225,9 +225,8 @@ static void dsps_musb_enable(struct musb *musb)
 
 
 	dsps_writel(reg_base, wrp->epintr_set, epmask);
 	dsps_writel(reg_base, wrp->epintr_set, epmask);
 	dsps_writel(reg_base, wrp->coreintr_set, coremask);
 	dsps_writel(reg_base, wrp->coreintr_set, coremask);
-	/* Force the DRVVBUS IRQ so we can start polling for ID change. */
-	dsps_writel(reg_base, wrp->coreintr_set,
-		    (1 << wrp->drvvbus) << wrp->usb_shift);
+	/* start polling for ID change. */
+	mod_timer(&glue->timer, jiffies + msecs_to_jiffies(wrp->poll_timeout));
 	dsps_musb_try_idle(musb, 0);
 	dsps_musb_try_idle(musb, 0);
 }
 }
 
 
@@ -285,7 +284,8 @@ static void otg_timer(unsigned long _musb)
 		}
 		}
 		if (!(devctl & MUSB_DEVCTL_SESSION) && !skip_session)
 		if (!(devctl & MUSB_DEVCTL_SESSION) && !skip_session)
 			dsps_writeb(mregs, MUSB_DEVCTL, MUSB_DEVCTL_SESSION);
 			dsps_writeb(mregs, MUSB_DEVCTL, MUSB_DEVCTL_SESSION);
-		mod_timer(&glue->timer, jiffies + wrp->poll_seconds * HZ);
+		mod_timer(&glue->timer, jiffies +
+				msecs_to_jiffies(wrp->poll_timeout));
 		break;
 		break;
 	case OTG_STATE_A_WAIT_VFALL:
 	case OTG_STATE_A_WAIT_VFALL:
 		musb->xceiv->otg->state = OTG_STATE_A_WAIT_VRISE;
 		musb->xceiv->otg->state = OTG_STATE_A_WAIT_VRISE;
@@ -330,28 +330,6 @@ static irqreturn_t dsps_interrupt(int irq, void *hci)
 
 
 	dev_dbg(musb->controller, "usbintr (%x) epintr(%x)\n",
 	dev_dbg(musb->controller, "usbintr (%x) epintr(%x)\n",
 			usbintr, epintr);
 			usbintr, epintr);
-	/*
-	 * DRVVBUS IRQs are the only proxy we have (a very poor one!) for
-	 * DSPS IP's missing ID change IRQ.  We need an ID change IRQ to
-	 * switch appropriately between halves of the OTG state machine.
-	 * Managing DEVCTL.SESSION per Mentor docs requires that we know its
-	 * value but DEVCTL.BDEVICE is invalid without DEVCTL.SESSION set.
-	 * Also, DRVVBUS pulses for SRP (but not at 5V) ...
-	 */
-	if (is_host_active(musb) && usbintr & MUSB_INTR_BABBLE) {
-		pr_info("CAUTION: musb: Babble Interrupt Occurred\n");
-
-		/*
-		 * When a babble condition occurs, the musb controller removes
-		 * the session and is no longer in host mode. Hence, all
-		 * devices connected to its root hub get disconnected.
-		 *
-		 * Hand this error down to the musb core isr, so it can
-		 * recover.
-		 */
-		musb->int_usb = MUSB_INTR_BABBLE | MUSB_INTR_DISCONNECT;
-		musb->int_tx = musb->int_rx = 0;
-	}
 
 
 	if (usbintr & ((1 << wrp->drvvbus) << wrp->usb_shift)) {
 	if (usbintr & ((1 << wrp->drvvbus) << wrp->usb_shift)) {
 		int drvvbus = dsps_readl(reg_base, wrp->status);
 		int drvvbus = dsps_readl(reg_base, wrp->status);
@@ -374,8 +352,8 @@ static irqreturn_t dsps_interrupt(int irq, void *hci)
 			 */
 			 */
 			musb->int_usb &= ~MUSB_INTR_VBUSERROR;
 			musb->int_usb &= ~MUSB_INTR_VBUSERROR;
 			musb->xceiv->otg->state = OTG_STATE_A_WAIT_VFALL;
 			musb->xceiv->otg->state = OTG_STATE_A_WAIT_VFALL;
-			mod_timer(&glue->timer,
-					jiffies + wrp->poll_seconds * HZ);
+			mod_timer(&glue->timer, jiffies +
+					msecs_to_jiffies(wrp->poll_timeout));
 			WARNING("VBUS error workaround (delay coming)\n");
 			WARNING("VBUS error workaround (delay coming)\n");
 		} else if (drvvbus) {
 		} else if (drvvbus) {
 			MUSB_HST_MODE(musb);
 			MUSB_HST_MODE(musb);
@@ -404,7 +382,8 @@ static irqreturn_t dsps_interrupt(int irq, void *hci)
 	/* Poll for ID change in OTG port mode */
 	/* Poll for ID change in OTG port mode */
 	if (musb->xceiv->otg->state == OTG_STATE_B_IDLE &&
 	if (musb->xceiv->otg->state == OTG_STATE_B_IDLE &&
 			musb->port_mode == MUSB_PORT_MODE_DUAL_ROLE)
 			musb->port_mode == MUSB_PORT_MODE_DUAL_ROLE)
-		mod_timer(&glue->timer, jiffies + wrp->poll_seconds * HZ);
+		mod_timer(&glue->timer, jiffies +
+				msecs_to_jiffies(wrp->poll_timeout));
 out:
 out:
 	spin_unlock_irqrestore(&musb->lock, flags);
 	spin_unlock_irqrestore(&musb->lock, flags);
 
 
@@ -453,7 +432,7 @@ static int dsps_musb_init(struct musb *musb)
 	musb->ctrl_base = reg_base;
 	musb->ctrl_base = reg_base;
 
 
 	/* NOP driver needs change if supporting dual instance */
 	/* NOP driver needs change if supporting dual instance */
-	musb->xceiv = devm_usb_get_phy_by_phandle(dev, "phys", 0);
+	musb->xceiv = devm_usb_get_phy_by_phandle(dev->parent, "phys", 0);
 	if (IS_ERR(musb->xceiv))
 	if (IS_ERR(musb->xceiv))
 		return PTR_ERR(musb->xceiv);
 		return PTR_ERR(musb->xceiv);
 
 
@@ -497,7 +476,7 @@ static int dsps_musb_init(struct musb *musb)
 	 * logic enabled.
 	 * logic enabled.
 	 */
 	 */
 	val = dsps_readb(musb->mregs, MUSB_BABBLE_CTL);
 	val = dsps_readb(musb->mregs, MUSB_BABBLE_CTL);
-	if (val == MUSB_BABBLE_RCV_DISABLE) {
+	if (val & MUSB_BABBLE_RCV_DISABLE) {
 		glue->sw_babble_enabled = true;
 		glue->sw_babble_enabled = true;
 		val |= MUSB_BABBLE_SW_SESSION_CTRL;
 		val |= MUSB_BABBLE_SW_SESSION_CTRL;
 		dsps_writeb(musb->mregs, MUSB_BABBLE_CTL, val);
 		dsps_writeb(musb->mregs, MUSB_BABBLE_CTL, val);
@@ -571,7 +550,7 @@ static int dsps_musb_set_mode(struct musb *musb, u8 mode)
 	return 0;
 	return 0;
 }
 }
 
 
-static bool  sw_babble_control(struct musb *musb)
+static bool dsps_sw_babble_control(struct musb *musb)
 {
 {
 	u8 babble_ctl;
 	u8 babble_ctl;
 	bool session_restart =  false;
 	bool session_restart =  false;
@@ -622,37 +601,36 @@ static bool  sw_babble_control(struct musb *musb)
 	return session_restart;
 	return session_restart;
 }
 }
 
 
-static int dsps_musb_reset(struct musb *musb)
+static int dsps_musb_recover(struct musb *musb)
 {
 {
 	struct device *dev = musb->controller;
 	struct device *dev = musb->controller;
 	struct dsps_glue *glue = dev_get_drvdata(dev->parent);
 	struct dsps_glue *glue = dev_get_drvdata(dev->parent);
-	const struct dsps_musb_wrapper *wrp = glue->wrp;
-	int session_restart = 0, error;
+	int session_restart = 0;
 
 
 	if (glue->sw_babble_enabled)
 	if (glue->sw_babble_enabled)
-		session_restart = sw_babble_control(musb);
-	/*
-	 * In case of new silicon version babble condition can be recovered
-	 * without resetting the MUSB. But for older silicon versions, MUSB
-	 * reset is needed
-	 */
-	if (session_restart || !glue->sw_babble_enabled) {
-		dev_info(musb->controller, "Restarting MUSB to recover from Babble\n");
-		dsps_writel(musb->ctrl_base, wrp->control, (1 << wrp->reset));
-		usleep_range(100, 200);
-		usb_phy_shutdown(musb->xceiv);
-		error = phy_power_off(musb->phy);
-		if (error)
-			dev_err(dev, "phy shutdown failed: %i\n", error);
-		usleep_range(100, 200);
-		usb_phy_init(musb->xceiv);
-		error = phy_power_on(musb->phy);
-		if (error)
-			dev_err(dev, "phy powerup failed: %i\n", error);
+		session_restart = dsps_sw_babble_control(musb);
+	else
 		session_restart = 1;
 		session_restart = 1;
+
+	return session_restart ? 0 : -EPIPE;
+}
+
+/* Similar to am35x, dm81xx support only 32-bit read operation */
+static void dsps_read_fifo32(struct musb_hw_ep *hw_ep, u16 len, u8 *dst)
+{
+	void __iomem *fifo = hw_ep->fifo;
+
+	if (len >= 4) {
+		readsl(fifo, dst, len >> 2);
+		dst += len & ~0x03;
+		len &= 0x03;
 	}
 	}
 
 
-	return !session_restart;
+	/* Read any remaining 1 to 3 bytes */
+	if (len > 0) {
+		u32 val = musb_readl(fifo, 0);
+		memcpy(dst, &val, len);
+	}
 }
 }
 
 
 static struct musb_platform_ops dsps_ops = {
 static struct musb_platform_ops dsps_ops = {
@@ -665,7 +643,7 @@ static struct musb_platform_ops dsps_ops = {
 
 
 	.try_idle	= dsps_musb_try_idle,
 	.try_idle	= dsps_musb_try_idle,
 	.set_mode	= dsps_musb_set_mode,
 	.set_mode	= dsps_musb_set_mode,
-	.reset		= dsps_musb_reset,
+	.recover	= dsps_musb_recover,
 };
 };
 
 
 static u64 musb_dmamask = DMA_BIT_MASK(32);
 static u64 musb_dmamask = DMA_BIT_MASK(32);
@@ -737,7 +715,6 @@ static int dsps_create_musb_pdev(struct dsps_glue *glue,
 	musb->dev.parent		= dev;
 	musb->dev.parent		= dev;
 	musb->dev.dma_mask		= &musb_dmamask;
 	musb->dev.dma_mask		= &musb_dmamask;
 	musb->dev.coherent_dma_mask	= musb_dmamask;
 	musb->dev.coherent_dma_mask	= musb_dmamask;
-	musb->dev.of_node		= of_node_get(dn);
 
 
 	glue->musb = musb;
 	glue->musb = musb;
 
 
@@ -802,6 +779,9 @@ static int dsps_probe(struct platform_device *pdev)
 	}
 	}
 	wrp = match->data;
 	wrp = match->data;
 
 
+	if (of_device_is_compatible(pdev->dev.of_node, "ti,musb-dm816"))
+		dsps_ops.read_fifo = dsps_read_fifo32;
+
 	/* allocate glue */
 	/* allocate glue */
 	glue = devm_kzalloc(&pdev->dev, sizeof(*glue), GFP_KERNEL);
 	glue = devm_kzalloc(&pdev->dev, sizeof(*glue), GFP_KERNEL);
 	if (!glue)
 	if (!glue)
@@ -873,12 +853,14 @@ static const struct dsps_musb_wrapper am33xx_driver_data = {
 	.rxep_shift		= 16,
 	.rxep_shift		= 16,
 	.rxep_mask		= 0xfffe,
 	.rxep_mask		= 0xfffe,
 	.rxep_bitmap		= (0xfffe << 16),
 	.rxep_bitmap		= (0xfffe << 16),
-	.poll_seconds		= 2,
+	.poll_timeout		= 2000, /* ms */
 };
 };
 
 
 static const struct of_device_id musb_dsps_of_match[] = {
 static const struct of_device_id musb_dsps_of_match[] = {
 	{ .compatible = "ti,musb-am33xx",
 	{ .compatible = "ti,musb-am33xx",
-		.data = (void *) &am33xx_driver_data, },
+		.data = &am33xx_driver_data, },
+	{ .compatible = "ti,musb-dm816",
+		.data = &am33xx_driver_data, },
 	{  },
 	{  },
 };
 };
 MODULE_DEVICE_TABLE(of, musb_dsps_of_match);
 MODULE_DEVICE_TABLE(of, musb_dsps_of_match);
@@ -929,7 +911,8 @@ static int dsps_resume(struct device *dev)
 	dsps_writel(mbase, wrp->rx_mode, glue->context.rx_mode);
 	dsps_writel(mbase, wrp->rx_mode, glue->context.rx_mode);
 	if (musb->xceiv->otg->state == OTG_STATE_B_IDLE &&
 	if (musb->xceiv->otg->state == OTG_STATE_B_IDLE &&
 	    musb->port_mode == MUSB_PORT_MODE_DUAL_ROLE)
 	    musb->port_mode == MUSB_PORT_MODE_DUAL_ROLE)
-		mod_timer(&glue->timer, jiffies + wrp->poll_seconds * HZ);
+		mod_timer(&glue->timer, jiffies +
+				msecs_to_jiffies(wrp->poll_timeout));
 
 
 	return 0;
 	return 0;
 }
 }

+ 1 - 39
drivers/usb/musb/musb_gadget.c

@@ -1876,44 +1876,6 @@ err:
 	return retval;
 	return retval;
 }
 }
 
 
-static void stop_activity(struct musb *musb, struct usb_gadget_driver *driver)
-{
-	int			i;
-	struct musb_hw_ep	*hw_ep;
-
-	/* don't disconnect if it's not connected */
-	if (musb->g.speed == USB_SPEED_UNKNOWN)
-		driver = NULL;
-	else
-		musb->g.speed = USB_SPEED_UNKNOWN;
-
-	/* deactivate the hardware */
-	if (musb->softconnect) {
-		musb->softconnect = 0;
-		musb_pullup(musb, 0);
-	}
-	musb_stop(musb);
-
-	/* killing any outstanding requests will quiesce the driver;
-	 * then report disconnect
-	 */
-	if (driver) {
-		for (i = 0, hw_ep = musb->endpoints;
-				i < musb->nr_endpoints;
-				i++, hw_ep++) {
-			musb_ep_select(musb->mregs, i);
-			if (hw_ep->is_shared_fifo /* || !epnum */) {
-				nuke(&hw_ep->ep_in, -ESHUTDOWN);
-			} else {
-				if (hw_ep->max_packet_sz_tx)
-					nuke(&hw_ep->ep_in, -ESHUTDOWN);
-				if (hw_ep->max_packet_sz_rx)
-					nuke(&hw_ep->ep_out, -ESHUTDOWN);
-			}
-		}
-	}
-}
-
 /*
 /*
  * Unregister the gadget driver. Used by gadget drivers when
  * Unregister the gadget driver. Used by gadget drivers when
  * unregistering themselves from the controller.
  * unregistering themselves from the controller.
@@ -1940,7 +1902,7 @@ static int musb_gadget_stop(struct usb_gadget *g)
 	(void) musb_gadget_vbus_draw(&musb->g, 0);
 	(void) musb_gadget_vbus_draw(&musb->g, 0);
 
 
 	musb->xceiv->otg->state = OTG_STATE_UNDEFINED;
 	musb->xceiv->otg->state = OTG_STATE_UNDEFINED;
-	stop_activity(musb, NULL);
+	musb_stop(musb);
 	otg_set_peripheral(musb->xceiv->otg, NULL);
 	otg_set_peripheral(musb->xceiv->otg, NULL);
 
 
 	musb->is_active = 0;
 	musb->is_active = 0;

+ 1 - 1
drivers/usb/phy/Kconfig

@@ -202,13 +202,13 @@ config USB_RCAR_GEN2_PHY
 config USB_ULPI
 config USB_ULPI
 	bool "Generic ULPI Transceiver Driver"
 	bool "Generic ULPI Transceiver Driver"
 	depends on ARM || ARM64
 	depends on ARM || ARM64
+	select USB_ULPI_VIEWPORT
 	help
 	help
 	  Enable this to support ULPI connected USB OTG transceivers which
 	  Enable this to support ULPI connected USB OTG transceivers which
 	  are likely found on embedded boards.
 	  are likely found on embedded boards.
 
 
 config USB_ULPI_VIEWPORT
 config USB_ULPI_VIEWPORT
 	bool
 	bool
-	depends on USB_ULPI
 	help
 	help
 	  Provides read/write operations to the ULPI phy register set for
 	  Provides read/write operations to the ULPI phy register set for
 	  controllers with a viewport register (e.g. Chipidea/ARC controllers).
 	  controllers with a viewport register (e.g. Chipidea/ARC controllers).

+ 1 - 1
drivers/usb/phy/of.c

@@ -27,7 +27,7 @@ static const char *const usbphy_modes[] = {
  * @np:	Pointer to the given device_node
  * @np:	Pointer to the given device_node
  *
  *
  * The function gets phy interface string from property 'phy_type',
  * The function gets phy interface string from property 'phy_type',
- * and returns the correspondig enum usb_phy_interface
+ * and returns the corresponding enum usb_phy_interface
  */
  */
 enum usb_phy_interface of_usb_get_phy_mode(struct device_node *np)
 enum usb_phy_interface of_usb_get_phy_mode(struct device_node *np)
 {
 {

+ 1 - 1
drivers/usb/phy/phy-ab8500-usb.c

@@ -893,7 +893,7 @@ static int abx500_usb_link_status_update(struct ab8500_usb *ab)
 
 
 /*
 /*
  * Disconnection Sequence:
  * Disconnection Sequence:
- *   1. Disconect Interrupt
+ *   1. Disconnect Interrupt
  *   2. Disable regulators
  *   2. Disable regulators
  *   3. Disable AB clock
  *   3. Disable AB clock
  *   4. Disable the Phy
  *   4. Disable the Phy

+ 5 - 7
drivers/usb/phy/phy-generic.c

@@ -62,14 +62,14 @@ static int nop_set_suspend(struct usb_phy *x, int suspend)
 	return 0;
 	return 0;
 }
 }
 
 
-static void nop_reset_set(struct usb_phy_generic *nop, int asserted)
+static void nop_reset(struct usb_phy_generic *nop)
 {
 {
 	if (!nop->gpiod_reset)
 	if (!nop->gpiod_reset)
 		return;
 		return;
 
 
-	gpiod_direction_output(nop->gpiod_reset, !asserted);
+	gpiod_set_value(nop->gpiod_reset, 1);
 	usleep_range(10000, 20000);
 	usleep_range(10000, 20000);
-	gpiod_set_value(nop->gpiod_reset, asserted);
+	gpiod_set_value(nop->gpiod_reset, 0);
 }
 }
 
 
 /* interface to regulator framework */
 /* interface to regulator framework */
@@ -151,8 +151,7 @@ int usb_gen_phy_init(struct usb_phy *phy)
 	if (!IS_ERR(nop->clk))
 	if (!IS_ERR(nop->clk))
 		clk_prepare_enable(nop->clk);
 		clk_prepare_enable(nop->clk);
 
 
-	/* De-assert RESET */
-	nop_reset_set(nop, 0);
+	nop_reset(nop);
 
 
 	return 0;
 	return 0;
 }
 }
@@ -162,8 +161,7 @@ void usb_gen_phy_shutdown(struct usb_phy *phy)
 {
 {
 	struct usb_phy_generic *nop = dev_get_drvdata(phy->dev);
 	struct usb_phy_generic *nop = dev_get_drvdata(phy->dev);
 
 
-	/* Assert RESET */
-	nop_reset_set(nop, 1);
+	gpiod_set_value(nop->gpiod_reset, 1);
 
 
 	if (!IS_ERR(nop->clk))
 	if (!IS_ERR(nop->clk))
 		clk_disable_unprepare(nop->clk);
 		clk_disable_unprepare(nop->clk);

+ 3 - 1
drivers/usb/phy/phy.c

@@ -81,7 +81,9 @@ static void devm_usb_phy_release(struct device *dev, void *res)
 
 
 static int devm_usb_phy_match(struct device *dev, void *res, void *match_data)
 static int devm_usb_phy_match(struct device *dev, void *res, void *match_data)
 {
 {
-	return res == match_data;
+	struct usb_phy **phy = res;
+
+	return *phy == match_data;
 }
 }
 
 
 /**
 /**

+ 19 - 0
drivers/usb/renesas_usbhs/common.c

@@ -275,6 +275,16 @@ int usbhs_set_device_config(struct usbhs_priv *priv, int devnum,
 	return 0;
 	return 0;
 }
 }
 
 
+/*
+ *		interrupt functions
+ */
+void usbhs_xxxsts_clear(struct usbhs_priv *priv, u16 sts_reg, u16 bit)
+{
+	u16 pipe_mask = (u16)GENMASK(usbhs_get_dparam(priv, pipe_size), 0);
+
+	usbhs_write(priv, sts_reg, ~(1 << bit) & pipe_mask);
+}
+
 /*
 /*
  *		local functions
  *		local functions
  */
  */
@@ -487,6 +497,15 @@ static struct renesas_usbhs_platform_info *usbhs_parse_dt(struct device *dev)
 	if (gpio > 0)
 	if (gpio > 0)
 		dparam->enable_gpio = gpio;
 		dparam->enable_gpio = gpio;
 
 
+	switch (dparam->type) {
+	case USBHS_TYPE_R8A7790:
+	case USBHS_TYPE_R8A7791:
+		dparam->has_usb_dmac = 1;
+		break;
+	default:
+		break;
+	}
+
 	return info;
 	return info;
 }
 }
 
 

+ 7 - 0
drivers/usb/renesas_usbhs/common.h

@@ -193,6 +193,7 @@ struct usbhs_priv;
 #define TYPE_BULK	(1 << 14)
 #define TYPE_BULK	(1 << 14)
 #define TYPE_INT	(2 << 14)
 #define TYPE_INT	(2 << 14)
 #define TYPE_ISO	(3 << 14)
 #define TYPE_ISO	(3 << 14)
+#define BFRE		(1 << 10)	/* BRDY Interrupt Operation Spec. */
 #define DBLB		(1 << 9)	/* Double Buffer Mode */
 #define DBLB		(1 << 9)	/* Double Buffer Mode */
 #define SHTNAK		(1 << 7)	/* Pipe Disable in Transfer End */
 #define SHTNAK		(1 << 7)	/* Pipe Disable in Transfer End */
 #define DIR_OUT		(1 << 4)	/* Transfer Direction */
 #define DIR_OUT		(1 << 4)	/* Transfer Direction */
@@ -216,6 +217,7 @@ struct usbhs_priv;
 #define	ACLRM		(1 << 9)	/* Buffer Auto-Clear Mode */
 #define	ACLRM		(1 << 9)	/* Buffer Auto-Clear Mode */
 #define SQCLR		(1 << 8)	/* Toggle Bit Clear */
 #define SQCLR		(1 << 8)	/* Toggle Bit Clear */
 #define SQSET		(1 << 7)	/* Toggle Bit Set */
 #define SQSET		(1 << 7)	/* Toggle Bit Set */
+#define SQMON		(1 << 6)	/* Toggle Bit Check */
 #define PBUSY		(1 << 5)	/* Pipe Busy */
 #define PBUSY		(1 << 5)	/* Pipe Busy */
 #define PID_MASK	(0x3)		/* Response PID */
 #define PID_MASK	(0x3)		/* Response PID */
 #define  PID_NAK	0
 #define  PID_NAK	0
@@ -323,6 +325,11 @@ int usbhs_frame_get_num(struct usbhs_priv *priv);
 int usbhs_set_device_config(struct usbhs_priv *priv, int devnum, u16 upphub,
 int usbhs_set_device_config(struct usbhs_priv *priv, int devnum, u16 upphub,
 			   u16 hubport, u16 speed);
 			   u16 hubport, u16 speed);
 
 
+/*
+ * interrupt functions
+ */
+void usbhs_xxxsts_clear(struct usbhs_priv *priv, u16 sts_reg, u16 bit);
+
 /*
 /*
  * data
  * data
  */
  */

+ 174 - 15
drivers/usb/renesas_usbhs/fifo.c

@@ -813,7 +813,8 @@ static void xfer_work(struct work_struct *work)
 	desc->callback		= usbhsf_dma_complete;
 	desc->callback		= usbhsf_dma_complete;
 	desc->callback_param	= pipe;
 	desc->callback_param	= pipe;
 
 
-	if (dmaengine_submit(desc) < 0) {
+	pkt->cookie = dmaengine_submit(desc);
+	if (pkt->cookie < 0) {
 		dev_err(dev, "Failed to submit dma descriptor\n");
 		dev_err(dev, "Failed to submit dma descriptor\n");
 		return;
 		return;
 	}
 	}
@@ -822,10 +823,10 @@ static void xfer_work(struct work_struct *work)
 		fifo->name, usbhs_pipe_number(pipe), pkt->length, pkt->zero);
 		fifo->name, usbhs_pipe_number(pipe), pkt->length, pkt->zero);
 
 
 	usbhs_pipe_running(pipe, 1);
 	usbhs_pipe_running(pipe, 1);
-	usbhs_pipe_set_trans_count_if_bulk(pipe, pkt->trans);
-	usbhs_pipe_enable(pipe);
 	usbhsf_dma_start(pipe, fifo);
 	usbhsf_dma_start(pipe, fifo);
+	usbhs_pipe_set_trans_count_if_bulk(pipe, pkt->trans);
 	dma_async_issue_pending(chan);
 	dma_async_issue_pending(chan);
+	usbhs_pipe_enable(pipe);
 }
 }
 
 
 /*
 /*
@@ -838,6 +839,7 @@ static int usbhsf_dma_prepare_push(struct usbhs_pkt *pkt, int *is_done)
 	struct usbhs_fifo *fifo;
 	struct usbhs_fifo *fifo;
 	int len = pkt->length - pkt->actual;
 	int len = pkt->length - pkt->actual;
 	int ret;
 	int ret;
+	uintptr_t align_mask;
 
 
 	if (usbhs_pipe_is_busy(pipe))
 	if (usbhs_pipe_is_busy(pipe))
 		return 0;
 		return 0;
@@ -847,10 +849,14 @@ static int usbhsf_dma_prepare_push(struct usbhs_pkt *pkt, int *is_done)
 	    usbhs_pipe_is_dcp(pipe))
 	    usbhs_pipe_is_dcp(pipe))
 		goto usbhsf_pio_prepare_push;
 		goto usbhsf_pio_prepare_push;
 
 
-	if (len & 0x7) /* 8byte alignment */
+	/* check data length if this driver don't use USB-DMAC */
+	if (!usbhs_get_dparam(priv, has_usb_dmac) && len & 0x7)
 		goto usbhsf_pio_prepare_push;
 		goto usbhsf_pio_prepare_push;
 
 
-	if ((uintptr_t)(pkt->buf + pkt->actual) & 0x7) /* 8byte alignment */
+	/* check buffer alignment */
+	align_mask = usbhs_get_dparam(priv, has_usb_dmac) ?
+					USBHS_USB_DMAC_XFER_SIZE - 1 : 0x7;
+	if ((uintptr_t)(pkt->buf + pkt->actual) & align_mask)
 		goto usbhsf_pio_prepare_push;
 		goto usbhsf_pio_prepare_push;
 
 
 	/* return at this time if the pipe is running */
 	/* return at this time if the pipe is running */
@@ -924,7 +930,85 @@ struct usbhs_pkt_handle usbhs_fifo_dma_push_handler = {
 /*
 /*
  *		DMA pop handler
  *		DMA pop handler
  */
  */
-static int usbhsf_dma_try_pop(struct usbhs_pkt *pkt, int *is_done)
+
+static int usbhsf_dma_prepare_pop_with_rx_irq(struct usbhs_pkt *pkt,
+					      int *is_done)
+{
+	return usbhsf_prepare_pop(pkt, is_done);
+}
+
+static int usbhsf_dma_prepare_pop_with_usb_dmac(struct usbhs_pkt *pkt,
+						int *is_done)
+{
+	struct usbhs_pipe *pipe = pkt->pipe;
+	struct usbhs_priv *priv = usbhs_pipe_to_priv(pipe);
+	struct usbhs_fifo *fifo;
+	int ret;
+
+	if (usbhs_pipe_is_busy(pipe))
+		return 0;
+
+	/* use PIO if packet is less than pio_dma_border or pipe is DCP */
+	if ((pkt->length < usbhs_get_dparam(priv, pio_dma_border)) ||
+	    usbhs_pipe_is_dcp(pipe))
+		goto usbhsf_pio_prepare_pop;
+
+	fifo = usbhsf_get_dma_fifo(priv, pkt);
+	if (!fifo)
+		goto usbhsf_pio_prepare_pop;
+
+	if ((uintptr_t)pkt->buf & (USBHS_USB_DMAC_XFER_SIZE - 1))
+		goto usbhsf_pio_prepare_pop;
+
+	usbhs_pipe_config_change_bfre(pipe, 1);
+
+	ret = usbhsf_fifo_select(pipe, fifo, 0);
+	if (ret < 0)
+		goto usbhsf_pio_prepare_pop;
+
+	if (usbhsf_dma_map(pkt) < 0)
+		goto usbhsf_pio_prepare_pop_unselect;
+
+	/* DMA */
+
+	/*
+	 * usbhs_fifo_dma_pop_handler :: prepare
+	 * enabled irq to come here.
+	 * but it is no longer needed for DMA. disable it.
+	 */
+	usbhsf_rx_irq_ctrl(pipe, 0);
+
+	pkt->trans = pkt->length;
+
+	INIT_WORK(&pkt->work, xfer_work);
+	schedule_work(&pkt->work);
+
+	return 0;
+
+usbhsf_pio_prepare_pop_unselect:
+	usbhsf_fifo_unselect(pipe, fifo);
+usbhsf_pio_prepare_pop:
+
+	/*
+	 * change handler to PIO
+	 */
+	pkt->handler = &usbhs_fifo_pio_pop_handler;
+	usbhs_pipe_config_change_bfre(pipe, 0);
+
+	return pkt->handler->prepare(pkt, is_done);
+}
+
+static int usbhsf_dma_prepare_pop(struct usbhs_pkt *pkt, int *is_done)
+{
+	struct usbhs_priv *priv = usbhs_pipe_to_priv(pkt->pipe);
+
+	if (usbhs_get_dparam(priv, has_usb_dmac))
+		return usbhsf_dma_prepare_pop_with_usb_dmac(pkt, is_done);
+	else
+		return usbhsf_dma_prepare_pop_with_rx_irq(pkt, is_done);
+}
+
+static int usbhsf_dma_try_pop_with_rx_irq(struct usbhs_pkt *pkt, int *is_done)
 {
 {
 	struct usbhs_pipe *pipe = pkt->pipe;
 	struct usbhs_pipe *pipe = pkt->pipe;
 	struct usbhs_priv *priv = usbhs_pipe_to_priv(pipe);
 	struct usbhs_priv *priv = usbhs_pipe_to_priv(pipe);
@@ -993,7 +1077,16 @@ usbhsf_pio_prepare_pop:
 	return pkt->handler->try_run(pkt, is_done);
 	return pkt->handler->try_run(pkt, is_done);
 }
 }
 
 
-static int usbhsf_dma_pop_done(struct usbhs_pkt *pkt, int *is_done)
+static int usbhsf_dma_try_pop(struct usbhs_pkt *pkt, int *is_done)
+{
+	struct usbhs_priv *priv = usbhs_pipe_to_priv(pkt->pipe);
+
+	BUG_ON(usbhs_get_dparam(priv, has_usb_dmac));
+
+	return usbhsf_dma_try_pop_with_rx_irq(pkt, is_done);
+}
+
+static int usbhsf_dma_pop_done_with_rx_irq(struct usbhs_pkt *pkt, int *is_done)
 {
 {
 	struct usbhs_pipe *pipe = pkt->pipe;
 	struct usbhs_pipe *pipe = pkt->pipe;
 	int maxp = usbhs_pipe_get_maxpacket(pipe);
 	int maxp = usbhs_pipe_get_maxpacket(pipe);
@@ -1017,8 +1110,68 @@ static int usbhsf_dma_pop_done(struct usbhs_pkt *pkt, int *is_done)
 	return 0;
 	return 0;
 }
 }
 
 
+static size_t usbhs_dma_calc_received_size(struct usbhs_pkt *pkt,
+					   struct dma_chan *chan, int dtln)
+{
+	struct usbhs_pipe *pipe = pkt->pipe;
+	struct dma_tx_state state;
+	size_t received_size;
+	int maxp = usbhs_pipe_get_maxpacket(pipe);
+
+	dmaengine_tx_status(chan, pkt->cookie, &state);
+	received_size = pkt->length - state.residue;
+
+	if (dtln) {
+		received_size -= USBHS_USB_DMAC_XFER_SIZE;
+		received_size &= ~(maxp - 1);
+		received_size += dtln;
+	}
+
+	return received_size;
+}
+
+static int usbhsf_dma_pop_done_with_usb_dmac(struct usbhs_pkt *pkt,
+					     int *is_done)
+{
+	struct usbhs_pipe *pipe = pkt->pipe;
+	struct usbhs_priv *priv = usbhs_pipe_to_priv(pipe);
+	struct usbhs_fifo *fifo = usbhs_pipe_to_fifo(pipe);
+	struct dma_chan *chan = usbhsf_dma_chan_get(fifo, pkt);
+	int rcv_len;
+
+	/*
+	 * Since the driver disables rx_irq in DMA mode, the interrupt handler
+	 * cannot the BRDYSTS. So, the function clears it here because the
+	 * driver may use PIO mode next time.
+	 */
+	usbhs_xxxsts_clear(priv, BRDYSTS, usbhs_pipe_number(pipe));
+
+	rcv_len = usbhsf_fifo_rcv_len(priv, fifo);
+	usbhsf_fifo_clear(pipe, fifo);
+	pkt->actual = usbhs_dma_calc_received_size(pkt, chan, rcv_len);
+
+	usbhsf_dma_stop(pipe, fifo);
+	usbhsf_dma_unmap(pkt);
+	usbhsf_fifo_unselect(pipe, pipe->fifo);
+
+	/* The driver can assume the rx transaction is always "done" */
+	*is_done = 1;
+
+	return 0;
+}
+
+static int usbhsf_dma_pop_done(struct usbhs_pkt *pkt, int *is_done)
+{
+	struct usbhs_priv *priv = usbhs_pipe_to_priv(pkt->pipe);
+
+	if (usbhs_get_dparam(priv, has_usb_dmac))
+		return usbhsf_dma_pop_done_with_usb_dmac(pkt, is_done);
+	else
+		return usbhsf_dma_pop_done_with_rx_irq(pkt, is_done);
+}
+
 struct usbhs_pkt_handle usbhs_fifo_dma_pop_handler = {
 struct usbhs_pkt_handle usbhs_fifo_dma_pop_handler = {
-	.prepare	= usbhsf_prepare_pop,
+	.prepare	= usbhsf_dma_prepare_pop,
 	.try_run	= usbhsf_dma_try_pop,
 	.try_run	= usbhsf_dma_try_pop,
 	.dma_done	= usbhsf_dma_pop_done
 	.dma_done	= usbhsf_dma_pop_done
 };
 };
@@ -1069,23 +1222,29 @@ static void usbhsf_dma_init_pdev(struct usbhs_fifo *fifo)
 					    &fifo->rx_slave);
 					    &fifo->rx_slave);
 }
 }
 
 
-static void usbhsf_dma_init_dt(struct device *dev, struct usbhs_fifo *fifo)
+static void usbhsf_dma_init_dt(struct device *dev, struct usbhs_fifo *fifo,
+			       int channel)
 {
 {
-	fifo->tx_chan = dma_request_slave_channel_reason(dev, "tx");
+	char name[16];
+
+	snprintf(name, sizeof(name), "tx%d", channel);
+	fifo->tx_chan = dma_request_slave_channel_reason(dev, name);
 	if (IS_ERR(fifo->tx_chan))
 	if (IS_ERR(fifo->tx_chan))
 		fifo->tx_chan = NULL;
 		fifo->tx_chan = NULL;
-	fifo->rx_chan = dma_request_slave_channel_reason(dev, "rx");
+
+	snprintf(name, sizeof(name), "rx%d", channel);
+	fifo->rx_chan = dma_request_slave_channel_reason(dev, name);
 	if (IS_ERR(fifo->rx_chan))
 	if (IS_ERR(fifo->rx_chan))
 		fifo->rx_chan = NULL;
 		fifo->rx_chan = NULL;
 }
 }
 
 
-static void usbhsf_dma_init(struct usbhs_priv *priv,
-			    struct usbhs_fifo *fifo)
+static void usbhsf_dma_init(struct usbhs_priv *priv, struct usbhs_fifo *fifo,
+			    int channel)
 {
 {
 	struct device *dev = usbhs_priv_to_dev(priv);
 	struct device *dev = usbhs_priv_to_dev(priv);
 
 
 	if (dev->of_node)
 	if (dev->of_node)
-		usbhsf_dma_init_dt(dev, fifo);
+		usbhsf_dma_init_dt(dev, fifo, channel);
 	else
 	else
 		usbhsf_dma_init_pdev(fifo);
 		usbhsf_dma_init_pdev(fifo);
 
 
@@ -1231,7 +1390,7 @@ do {									\
 			usbhs_get_dparam(priv, d##channel##_tx_id);	\
 			usbhs_get_dparam(priv, d##channel##_tx_id);	\
 	fifo->rx_slave.shdma_slave.slave_id =				\
 	fifo->rx_slave.shdma_slave.slave_id =				\
 			usbhs_get_dparam(priv, d##channel##_rx_id);	\
 			usbhs_get_dparam(priv, d##channel##_rx_id);	\
-	usbhsf_dma_init(priv, fifo);					\
+	usbhsf_dma_init(priv, fifo, channel);				\
 } while (0)
 } while (0)
 
 
 #define USBHS_DFIFO_INIT(priv, fifo, channel)				\
 #define USBHS_DFIFO_INIT(priv, fifo, channel)				\

+ 1 - 0
drivers/usb/renesas_usbhs/fifo.h

@@ -58,6 +58,7 @@ struct usbhs_pkt {
 		     struct usbhs_pkt *pkt);
 		     struct usbhs_pkt *pkt);
 	struct work_struct work;
 	struct work_struct work;
 	dma_addr_t dma;
 	dma_addr_t dma;
+	dma_cookie_t cookie;
 	void *buf;
 	void *buf;
 	int length;
 	int length;
 	int trans;
 	int trans;

+ 19 - 3
drivers/usb/renesas_usbhs/mod_gadget.c

@@ -119,18 +119,34 @@ struct usbhsg_recip_handle {
 /*
 /*
  *		queue push/pop
  *		queue push/pop
  */
  */
-static void usbhsg_queue_pop(struct usbhsg_uep *uep,
-			     struct usbhsg_request *ureq,
-			     int status)
+static void __usbhsg_queue_pop(struct usbhsg_uep *uep,
+			       struct usbhsg_request *ureq,
+			       int status)
 {
 {
 	struct usbhsg_gpriv *gpriv = usbhsg_uep_to_gpriv(uep);
 	struct usbhsg_gpriv *gpriv = usbhsg_uep_to_gpriv(uep);
 	struct usbhs_pipe *pipe = usbhsg_uep_to_pipe(uep);
 	struct usbhs_pipe *pipe = usbhsg_uep_to_pipe(uep);
 	struct device *dev = usbhsg_gpriv_to_dev(gpriv);
 	struct device *dev = usbhsg_gpriv_to_dev(gpriv);
+	struct usbhs_priv *priv = usbhsg_gpriv_to_priv(gpriv);
 
 
 	dev_dbg(dev, "pipe %d : queue pop\n", usbhs_pipe_number(pipe));
 	dev_dbg(dev, "pipe %d : queue pop\n", usbhs_pipe_number(pipe));
 
 
 	ureq->req.status = status;
 	ureq->req.status = status;
+	spin_unlock(usbhs_priv_to_lock(priv));
 	usb_gadget_giveback_request(&uep->ep, &ureq->req);
 	usb_gadget_giveback_request(&uep->ep, &ureq->req);
+	spin_lock(usbhs_priv_to_lock(priv));
+}
+
+static void usbhsg_queue_pop(struct usbhsg_uep *uep,
+			     struct usbhsg_request *ureq,
+			     int status)
+{
+	struct usbhsg_gpriv *gpriv = usbhsg_uep_to_gpriv(uep);
+	struct usbhs_priv *priv = usbhsg_gpriv_to_priv(gpriv);
+	unsigned long flags;
+
+	usbhs_lock(priv, flags);
+	__usbhsg_queue_pop(uep, ureq, status);
+	usbhs_unlock(priv, flags);
 }
 }
 
 
 static void usbhsg_queue_done(struct usbhs_priv *priv, struct usbhs_pkt *pkt)
 static void usbhsg_queue_done(struct usbhs_priv *priv, struct usbhs_pkt *pkt)

+ 39 - 0
drivers/usb/renesas_usbhs/pipe.c

@@ -84,6 +84,17 @@ static void __usbhsp_pipe_xxx_set(struct usbhs_pipe *pipe,
 		usbhs_bset(priv, pipe_reg, mask, val);
 		usbhs_bset(priv, pipe_reg, mask, val);
 }
 }
 
 
+static u16 __usbhsp_pipe_xxx_get(struct usbhs_pipe *pipe,
+				 u16 dcp_reg, u16 pipe_reg)
+{
+	struct usbhs_priv *priv = usbhs_pipe_to_priv(pipe);
+
+	if (usbhs_pipe_is_dcp(pipe))
+		return usbhs_read(priv, dcp_reg);
+	else
+		return usbhs_read(priv, pipe_reg);
+}
+
 /*
 /*
  *		DCPCFG/PIPECFG functions
  *		DCPCFG/PIPECFG functions
  */
  */
@@ -92,6 +103,11 @@ static void usbhsp_pipe_cfg_set(struct usbhs_pipe *pipe, u16 mask, u16 val)
 	__usbhsp_pipe_xxx_set(pipe, DCPCFG, PIPECFG, mask, val);
 	__usbhsp_pipe_xxx_set(pipe, DCPCFG, PIPECFG, mask, val);
 }
 }
 
 
+static u16 usbhsp_pipe_cfg_get(struct usbhs_pipe *pipe)
+{
+	return __usbhsp_pipe_xxx_get(pipe, DCPCFG, PIPECFG);
+}
+
 /*
 /*
  *		PIPEnTRN/PIPEnTRE functions
  *		PIPEnTRN/PIPEnTRE functions
  */
  */
@@ -616,6 +632,11 @@ void usbhs_pipe_data_sequence(struct usbhs_pipe *pipe, int sequence)
 	usbhsp_pipectrl_set(pipe, mask, val);
 	usbhsp_pipectrl_set(pipe, mask, val);
 }
 }
 
 
+static int usbhs_pipe_get_data_sequence(struct usbhs_pipe *pipe)
+{
+	return !!(usbhsp_pipectrl_get(pipe) & SQMON);
+}
+
 void usbhs_pipe_clear(struct usbhs_pipe *pipe)
 void usbhs_pipe_clear(struct usbhs_pipe *pipe)
 {
 {
 	if (usbhs_pipe_is_dcp(pipe)) {
 	if (usbhs_pipe_is_dcp(pipe)) {
@@ -626,6 +647,24 @@ void usbhs_pipe_clear(struct usbhs_pipe *pipe)
 	}
 	}
 }
 }
 
 
+void usbhs_pipe_config_change_bfre(struct usbhs_pipe *pipe, int enable)
+{
+	int sequence;
+
+	if (usbhs_pipe_is_dcp(pipe))
+		return;
+
+	usbhsp_pipe_select(pipe);
+	/* check if the driver needs to change the BFRE value */
+	if (!(enable ^ !!(usbhsp_pipe_cfg_get(pipe) & BFRE)))
+		return;
+
+	sequence = usbhs_pipe_get_data_sequence(pipe);
+	usbhsp_pipe_cfg_set(pipe, BFRE, enable ? BFRE : 0);
+	usbhs_pipe_clear(pipe);
+	usbhs_pipe_data_sequence(pipe, sequence);
+}
+
 static struct usbhs_pipe *usbhsp_get_pipe(struct usbhs_priv *priv, u32 type)
 static struct usbhs_pipe *usbhsp_get_pipe(struct usbhs_priv *priv, u32 type)
 {
 {
 	struct usbhs_pipe *pos, *pipe;
 	struct usbhs_pipe *pos, *pipe;

+ 1 - 0
drivers/usb/renesas_usbhs/pipe.h

@@ -97,6 +97,7 @@ void usbhs_pipe_set_trans_count_if_bulk(struct usbhs_pipe *pipe, int len);
 void usbhs_pipe_select_fifo(struct usbhs_pipe *pipe, struct usbhs_fifo *fifo);
 void usbhs_pipe_select_fifo(struct usbhs_pipe *pipe, struct usbhs_fifo *fifo);
 void usbhs_pipe_config_update(struct usbhs_pipe *pipe, u16 devsel,
 void usbhs_pipe_config_update(struct usbhs_pipe *pipe, u16 devsel,
 			      u16 epnum, u16 maxp);
 			      u16 epnum, u16 maxp);
+void usbhs_pipe_config_change_bfre(struct usbhs_pipe *pipe, int enable);
 
 
 #define usbhs_pipe_sequence_data0(pipe)	usbhs_pipe_data_sequence(pipe, 0)
 #define usbhs_pipe_sequence_data0(pipe)	usbhs_pipe_data_sequence(pipe, 0)
 #define usbhs_pipe_sequence_data1(pipe)	usbhs_pipe_data_sequence(pipe, 1)
 #define usbhs_pipe_sequence_data1(pipe)	usbhs_pipe_data_sequence(pipe, 1)

+ 2 - 0
include/linux/pci_ids.h

@@ -2315,6 +2315,8 @@
 #define PCI_VENDOR_ID_CENATEK		0x16CA
 #define PCI_VENDOR_ID_CENATEK		0x16CA
 #define PCI_DEVICE_ID_CENATEK_IDE	0x0001
 #define PCI_DEVICE_ID_CENATEK_IDE	0x0001
 
 
+#define PCI_VENDOR_ID_SYNOPSYS		0x16c3
+
 #define PCI_VENDOR_ID_VITESSE		0x1725
 #define PCI_VENDOR_ID_VITESSE		0x1725
 #define PCI_DEVICE_ID_VITESSE_VSC7174	0x7174
 #define PCI_DEVICE_ID_VITESSE_VSC7174	0x7174
 
 

+ 3 - 0
include/linux/usb/composite.h

@@ -148,6 +148,7 @@ struct usb_os_desc_table {
  * @disable: (REQUIRED) Indicates the function should be disabled.  Reasons
  * @disable: (REQUIRED) Indicates the function should be disabled.  Reasons
  *	include host resetting or reconfiguring the gadget, and disconnection.
  *	include host resetting or reconfiguring the gadget, and disconnection.
  * @setup: Used for interface-specific control requests.
  * @setup: Used for interface-specific control requests.
+ * @req_match: Tests if a given class request can be handled by this function.
  * @suspend: Notifies functions when the host stops sending USB traffic.
  * @suspend: Notifies functions when the host stops sending USB traffic.
  * @resume: Notifies functions when the host restarts USB traffic.
  * @resume: Notifies functions when the host restarts USB traffic.
  * @get_status: Returns function status as a reply to
  * @get_status: Returns function status as a reply to
@@ -213,6 +214,8 @@ struct usb_function {
 	void			(*disable)(struct usb_function *);
 	void			(*disable)(struct usb_function *);
 	int			(*setup)(struct usb_function *,
 	int			(*setup)(struct usb_function *,
 					const struct usb_ctrlrequest *);
 					const struct usb_ctrlrequest *);
+	bool			(*req_match)(struct usb_function *,
+					const struct usb_ctrlrequest *);
 	void			(*suspend)(struct usb_function *);
 	void			(*suspend)(struct usb_function *);
 	void			(*resume)(struct usb_function *);
 	void			(*resume)(struct usb_function *);
 
 

+ 8 - 1
include/linux/usb/gadget.h

@@ -190,7 +190,7 @@ struct usb_ep {
  * @ep:the endpoint being configured
  * @ep:the endpoint being configured
  * @maxpacket_limit:value of maximum packet size limit
  * @maxpacket_limit:value of maximum packet size limit
  *
  *
- * This function shoud be used only in UDC drivers to initialize endpoint
+ * This function should be used only in UDC drivers to initialize endpoint
  * (usually in probe function).
  * (usually in probe function).
  */
  */
 static inline void usb_ep_set_maxpacket_limit(struct usb_ep *ep,
 static inline void usb_ep_set_maxpacket_limit(struct usb_ep *ep,
@@ -474,6 +474,7 @@ struct usb_dcd_config_params {
 
 
 struct usb_gadget;
 struct usb_gadget;
 struct usb_gadget_driver;
 struct usb_gadget_driver;
+struct usb_udc;
 
 
 /* the rest of the api to the controller hardware: device operations,
 /* the rest of the api to the controller hardware: device operations,
  * which don't involve endpoints (or i/o).
  * which don't involve endpoints (or i/o).
@@ -496,6 +497,7 @@ struct usb_gadget_ops {
 /**
 /**
  * struct usb_gadget - represents a usb slave device
  * struct usb_gadget - represents a usb slave device
  * @work: (internal use) Workqueue to be used for sysfs_notify()
  * @work: (internal use) Workqueue to be used for sysfs_notify()
+ * @udc: struct usb_udc pointer for this gadget
  * @ops: Function pointers used to access hardware-specific operations.
  * @ops: Function pointers used to access hardware-specific operations.
  * @ep0: Endpoint zero, used when reading or writing responses to
  * @ep0: Endpoint zero, used when reading or writing responses to
  *	driver setup() requests
  *	driver setup() requests
@@ -545,6 +547,7 @@ struct usb_gadget_ops {
  */
  */
 struct usb_gadget {
 struct usb_gadget {
 	struct work_struct		work;
 	struct work_struct		work;
+	struct usb_udc			*udc;
 	/* readonly to gadget driver */
 	/* readonly to gadget driver */
 	const struct usb_gadget_ops	*ops;
 	const struct usb_gadget_ops	*ops;
 	struct usb_ep			*ep0;
 	struct usb_ep			*ep0;
@@ -1029,6 +1032,10 @@ extern void usb_gadget_udc_reset(struct usb_gadget *gadget,
 extern void usb_gadget_giveback_request(struct usb_ep *ep,
 extern void usb_gadget_giveback_request(struct usb_ep *ep,
 		struct usb_request *req);
 		struct usb_request *req);
 
 
+/*-------------------------------------------------------------------------*/
+
+/* utility to update vbus status for udc core, it may be scheduled */
+extern void usb_udc_vbus_handler(struct usb_gadget *gadget, bool status);
 
 
 /*-------------------------------------------------------------------------*/
 /*-------------------------------------------------------------------------*/
 
 

+ 2 - 0
include/linux/usb/renesas_usbhs.h

@@ -165,6 +165,8 @@ struct renesas_usbhs_driver_param {
 	 */
 	 */
 	u32 has_otg:1; /* for controlling PWEN/EXTLP */
 	u32 has_otg:1; /* for controlling PWEN/EXTLP */
 	u32 has_sudmac:1; /* for SUDMAC */
 	u32 has_sudmac:1; /* for SUDMAC */
+	u32 has_usb_dmac:1; /* for USB-DMAC */
+#define USBHS_USB_DMAC_XFER_SIZE	32	/* hardcode the xfer size */
 };
 };
 
 
 #define USBHS_TYPE_R8A7790 1
 #define USBHS_TYPE_R8A7790 1