Browse Source

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

Felipe writes:

usb: patches for v3.20 merge window

Here's the big pull request for Gadgets and PHYs. It's
a total of 217 non-merge commits with pretty much everything
being touched.

The most important bits are a ton of new documentation for
almost all usb gadget functions, a new isp1760 UDC driver,
several improvements to the old net2280 UDC driver, and
some minor tracepoint improvements to dwc3.

Other than that, a big list of minor cleanups, smaller bugfixes
and new features all over the place.

Signed-off-by: Felipe Balbi <balbi@ti.com>
Greg Kroah-Hartman 10 years ago
parent
commit
4d4bac4499
100 changed files with 8123 additions and 2231 deletions
  1. 265 0
      Documentation/ABI/testing/configfs-usb-gadget-uvc
  2. 4 1
      Documentation/devicetree/bindings/usb/atmel-usb.txt
  3. 4 0
      Documentation/devicetree/bindings/usb/dwc2.txt
  4. 2 0
      Documentation/devicetree/bindings/usb/renesas_usbhs.txt
  5. 9 1
      Documentation/devicetree/bindings/usb/usb-nop-xceiv.txt
  6. 728 0
      Documentation/usb/gadget-testing.txt
  7. 6 2
      Documentation/usb/gadget_serial.txt
  8. 1 1
      MAINTAINERS
  9. 4 4
      drivers/staging/emxx_udc/emxx_udc.c
  10. 0 1
      drivers/staging/emxx_udc/emxx_udc.h
  11. 2 0
      drivers/usb/Kconfig
  12. 1 1
      drivers/usb/Makefile
  13. 16 2
      drivers/usb/chipidea/udc.c
  14. 2 2
      drivers/usb/dwc2/Kconfig
  15. 1 1
      drivers/usb/dwc2/core.c
  16. 46 14
      drivers/usb/dwc2/core.h
  17. 831 360
      drivers/usb/dwc2/gadget.c
  18. 16 80
      drivers/usb/dwc2/hcd.c
  19. 2 0
      drivers/usb/dwc2/hw.h
  20. 34 2
      drivers/usb/dwc2/platform.c
  21. 0 6
      drivers/usb/dwc3/Kconfig
  22. 0 1
      drivers/usb/dwc3/Makefile
  23. 1 1
      drivers/usb/dwc3/core.c
  24. 0 5
      drivers/usb/dwc3/core.h
  25. 32 146
      drivers/usb/dwc3/dwc3-pci.c
  26. 1 1
      drivers/usb/dwc3/ep0.c
  27. 43 39
      drivers/usb/dwc3/gadget.c
  28. 10 0
      drivers/usb/dwc3/trace.h
  29. 11 0
      drivers/usb/gadget/Kconfig
  30. 1 1
      drivers/usb/gadget/composite.c
  31. 1 1
      drivers/usb/gadget/function/Makefile
  32. 99 20
      drivers/usb/gadget/function/f_fs.c
  33. 1 1
      drivers/usb/gadget/function/f_hid.c
  34. 10 10
      drivers/usb/gadget/function/f_sourcesink.c
  35. 10 6
      drivers/usb/gadget/function/f_uac1.c
  36. 128 8
      drivers/usb/gadget/function/f_uvc.c
  37. 1 3
      drivers/usb/gadget/function/u_ether.c
  38. 25 0
      drivers/usb/gadget/function/u_fs.h
  39. 1 2
      drivers/usb/gadget/function/u_uac1.c
  40. 0 1
      drivers/usb/gadget/function/u_uac1.h
  41. 51 1
      drivers/usb/gadget/function/u_uvc.h
  42. 2468 0
      drivers/usb/gadget/function/uvc_configfs.c
  43. 22 0
      drivers/usb/gadget/function/uvc_configfs.h
  44. 4 4
      drivers/usb/gadget/udc/at91_udc.c
  45. 0 1
      drivers/usb/gadget/udc/at91_udc.h
  46. 95 52
      drivers/usb/gadget/udc/atmel_usba_udc.c
  47. 9 0
      drivers/usb/gadget/udc/atmel_usba_udc.h
  48. 5 5
      drivers/usb/gadget/udc/bdc/bdc_ep.c
  49. 1 0
      drivers/usb/gadget/udc/bdc/bdc_udc.c
  50. 1 0
      drivers/usb/gadget/udc/dummy_hcd.c
  51. 1 1
      drivers/usb/gadget/udc/fsl_qe_udc.c
  52. 3 2
      drivers/usb/gadget/udc/fsl_udc_core.c
  53. 4 8
      drivers/usb/gadget/udc/lpc32xx_udc.c
  54. 0 3
      drivers/usb/gadget/udc/mv_udc_core.c
  55. 2 5
      drivers/usb/gadget/udc/net2272.c
  56. 0 1
      drivers/usb/gadget/udc/net2272.h
  57. 130 403
      drivers/usb/gadget/udc/net2280.c
  58. 3 21
      drivers/usb/gadget/udc/net2280.h
  59. 1 0
      drivers/usb/gadget/udc/omap_udc.c
  60. 1 0
      drivers/usb/gadget/udc/pch_udc.c
  61. 0 2
      drivers/usb/gadget/udc/pxa25x_udc.c
  62. 0 2
      drivers/usb/gadget/udc/pxa27x_udc.c
  63. 1 0
      drivers/usb/gadget/udc/r8a66597-udc.c
  64. 1 20
      drivers/usb/gadget/udc/s3c2410_udc.c
  65. 2 0
      drivers/usb/gadget/udc/udc-core.c
  66. 0 14
      drivers/usb/host/Kconfig
  67. 0 3
      drivers/usb/host/Makefile
  68. 0 208
      drivers/usb/host/isp1760-hcd.h
  69. 0 477
      drivers/usb/host/isp1760-if.c
  70. 59 0
      drivers/usb/isp1760/Kconfig
  71. 5 0
      drivers/usb/isp1760/Makefile
  72. 177 0
      drivers/usb/isp1760/isp1760-core.c
  73. 68 0
      drivers/usb/isp1760/isp1760-core.h
  74. 122 156
      drivers/usb/isp1760/isp1760-hcd.c
  75. 102 0
      drivers/usb/isp1760/isp1760-hcd.h
  76. 309 0
      drivers/usb/isp1760/isp1760-if.c
  77. 230 0
      drivers/usb/isp1760/isp1760-regs.h
  78. 1498 0
      drivers/usb/isp1760/isp1760-udc.c
  79. 106 0
      drivers/usb/isp1760/isp1760-udc.h
  80. 6 1
      drivers/usb/musb/Kconfig
  81. 1 1
      drivers/usb/musb/blackfin.c
  82. 7 0
      drivers/usb/musb/musb_core.c
  83. 15 9
      drivers/usb/musb/musb_cppi41.c
  84. 1 1
      drivers/usb/musb/musb_debugfs.c
  85. 1 3
      drivers/usb/musb/musb_gadget.c
  86. 1 1
      drivers/usb/musb/musb_gadget_ep0.c
  87. 1 3
      drivers/usb/musb/musb_virthub.c
  88. 0 35
      drivers/usb/phy/phy-fsl-usb.c
  89. 110 40
      drivers/usb/phy/phy-generic.c
  90. 8 2
      drivers/usb/phy/phy-generic.h
  91. 81 5
      drivers/usb/phy/phy-mxs-usb.c
  92. 19 6
      drivers/usb/renesas_usbhs/common.c
  93. 3 0
      drivers/usb/renesas_usbhs/common.h
  94. 22 3
      drivers/usb/renesas_usbhs/fifo.c
  95. 2 0
      drivers/usb/renesas_usbhs/mod_gadget.c
  96. 2 0
      include/linux/usb/gadget.h
  97. 2 2
      include/linux/usb/phy.h
  98. 2 0
      include/linux/usb/usb_phy_generic.h
  99. 1 0
      include/uapi/linux/usb/functionfs.h
  100. 8 6
      tools/usb/ffs-aio-example/multibuff/host_app/test.c

+ 265 - 0
Documentation/ABI/testing/configfs-usb-gadget-uvc

@@ -0,0 +1,265 @@
+What:		/config/usb-gadget/gadget/functions/uvc.name
+Date:		Dec 2014
+KernelVersion:	3.20
+Description:	UVC function directory
+
+		streaming_maxburst	- 0..15 (ss only)
+		streaming_maxpacket	- 1..1023 (fs), 1..3072 (hs/ss)
+		streaming_interval	- 1..16
+
+What:		/config/usb-gadget/gadget/functions/uvc.name/control
+Date:		Dec 2014
+KernelVersion:	3.20
+Description:	Control descriptors
+
+What:		/config/usb-gadget/gadget/functions/uvc.name/control/class
+Date:		Dec 2014
+KernelVersion:	3.20
+Description:	Class descriptors
+
+What:		/config/usb-gadget/gadget/functions/uvc.name/control/class/ss
+Date:		Dec 2014
+KernelVersion:	3.20
+Description:	Super speed control class descriptors
+
+What:		/config/usb-gadget/gadget/functions/uvc.name/control/class/fs
+Date:		Dec 2014
+KernelVersion:	3.20
+Description:	Full speed control class descriptors
+
+What:		/config/usb-gadget/gadget/functions/uvc.name/control/terminal
+Date:		Dec 2014
+KernelVersion:	3.20
+Description:	Terminal descriptors
+
+What:		/config/usb-gadget/gadget/functions/uvc.name/control/terminal/output
+Date:		Dec 2014
+KernelVersion:	3.20
+Description:	Output terminal descriptors
+
+What:		/config/usb-gadget/gadget/functions/uvc.name/control/terminal/output/default
+Date:		Dec 2014
+KernelVersion:	3.20
+Description:	Default output terminal descriptors
+
+		All attributes read only:
+		iTerminal	- index of string descriptor
+		bSourceID 	- id of the terminal to which this terminal
+				is connected
+		bAssocTerminal	- id of the input terminal to which this output
+				terminal is associated
+		wTerminalType	- terminal type
+		bTerminalID	- a non-zero id of this terminal
+
+What:		/config/usb-gadget/gadget/functions/uvc.name/control/terminal/camera
+Date:		Dec 2014
+KernelVersion:	3.20
+Description:	Camera terminal descriptors
+
+What:		/config/usb-gadget/gadget/functions/uvc.name/control/terminal/camera/default
+Date:		Dec 2014
+KernelVersion:	3.20
+Description:	Default camera terminal descriptors
+
+		All attributes read only:
+		bmControls		- bitmap specifying which controls are
+					supported for the video stream
+		wOcularFocalLength	- the value of Locular
+		wObjectiveFocalLengthMax- the value of Lmin
+		wObjectiveFocalLengthMin- the value of Lmax
+		iTerminal		- index of string descriptor
+		bAssocTerminal		- id of the output terminal to which
+					this terminal is connected
+		wTerminalType		- terminal type
+		bTerminalID		- a non-zero id of this terminal
+
+What:		/config/usb-gadget/gadget/functions/uvc.name/control/processing
+Date:		Dec 2014
+KernelVersion:	3.20
+Description:	Processing unit descriptors
+
+What:		/config/usb-gadget/gadget/functions/uvc.name/control/processing/default
+Date:		Dec 2014
+KernelVersion:	3.20
+Description:	Default processing unit descriptors
+
+		All attributes read only:
+		iProcessing	- index of string descriptor
+		bmControls	- bitmap specifying which controls are
+				supported for the video stream
+		wMaxMultiplier	- maximum digital magnification x100
+		bSourceID	- id of the terminal to which this unit is
+				connected
+		bUnitID		- a non-zero id of this unit
+
+What:		/config/usb-gadget/gadget/functions/uvc.name/control/header
+Date:		Dec 2014
+KernelVersion:	3.20
+Description:	Control header descriptors
+
+What:		/config/usb-gadget/gadget/functions/uvc.name/control/header/name
+Date:		Dec 2014
+KernelVersion:	3.20
+Description:	Specific control header descriptors
+
+dwClockFrequency
+bcdUVC
+What:		/config/usb-gadget/gadget/functions/uvc.name/streaming
+Date:		Dec 2014
+KernelVersion:	3.20
+Description:	Streaming descriptors
+
+What:		/config/usb-gadget/gadget/functions/uvc.name/streaming/class
+Date:		Dec 2014
+KernelVersion:	3.20
+Description:	Streaming class descriptors
+
+What:		/config/usb-gadget/gadget/functions/uvc.name/streaming/class/ss
+Date:		Dec 2014
+KernelVersion:	3.20
+Description:	Super speed streaming class descriptors
+
+What:		/config/usb-gadget/gadget/functions/uvc.name/streaming/class/hs
+Date:		Dec 2014
+KernelVersion:	3.20
+Description:	High speed streaming class descriptors
+
+What:		/config/usb-gadget/gadget/functions/uvc.name/streaming/class/fs
+Date:		Dec 2014
+KernelVersion:	3.20
+Description:	Full speed streaming class descriptors
+
+What:		/config/usb-gadget/gadget/functions/uvc.name/streaming/color_matching
+Date:		Dec 2014
+KernelVersion:	3.20
+Description:	Color matching descriptors
+
+What:		/config/usb-gadget/gadget/functions/uvc.name/streaming/color_matching/default
+Date:		Dec 2014
+KernelVersion:	3.20
+Description:	Default color matching descriptors
+
+		All attributes read only:
+		bMatrixCoefficients	- matrix used to compute luma and
+					chroma values from the color primaries
+		bTransferCharacteristics- optoelectronic transfer
+					characteristic of the source picutre,
+					also called the gamma function
+		bColorPrimaries		- color primaries and the reference
+					white
+
+What:		/config/usb-gadget/gadget/functions/uvc.name/streaming/mjpeg
+Date:		Dec 2014
+KernelVersion:	3.20
+Description:	MJPEG format descriptors
+
+What:		/config/usb-gadget/gadget/functions/uvc.name/streaming/mjpeg/name
+Date:		Dec 2014
+KernelVersion:	3.20
+Description:	Specific MJPEG format descriptors
+
+		All attributes read only,
+		except bmaControls and bDefaultFrameIndex:
+		bmaControls		- this format's data for bmaControls in
+					the streaming header
+		bmInterfaceFlags	- specifies interlace information,
+					read-only
+		bAspectRatioY		- the X dimension of the picture aspect
+					ratio, read-only
+		bAspectRatioX		- the Y dimension of the picture aspect
+					ratio, read-only
+		bmFlags			- characteristics of this format,
+					read-only
+		bDefaultFrameIndex	- optimum frame index for this stream
+
+What:		/config/usb-gadget/gadget/functions/uvc.name/streaming/mjpeg/name/name
+Date:		Dec 2014
+KernelVersion:	3.20
+Description:	Specific MJPEG frame descriptors
+
+		dwFrameInterval		- indicates how frame interval can be
+					programmed; a number of values
+					separated by newline can be specified
+		dwDefaultFrameInterval	- the frame interval the device would
+					like to use as default
+		dwMaxVideoFrameBufferSize- the maximum number of bytes the
+					compressor will produce for a video
+					frame or still image
+		dwMaxBitRate		- the maximum bit rate at the shortest
+					frame interval in bps
+		dwMinBitRate		- the minimum bit rate at the longest
+					frame interval in bps
+		wHeight			- height of decoded bitmap frame in px
+		wWidth			- width of decoded bitmam frame in px
+		bmCapabilities		- still image support, fixed frame-rate
+					support
+
+What:		/config/usb-gadget/gadget/functions/uvc.name/streaming/uncompressed
+Date:		Dec 2014
+KernelVersion:	3.20
+Description:	Uncompressed format descriptors
+
+What:		/config/usb-gadget/gadget/functions/uvc.name/streaming/uncompressed/name
+Date:		Dec 2014
+KernelVersion:	3.20
+Description:	Specific uncompressed format descriptors
+
+		bmaControls		- this format's data for bmaControls in
+					the streaming header
+		bmInterfaceFlags	- specifies interlace information,
+					read-only
+		bAspectRatioY		- the X dimension of the picture aspect
+					ratio, read-only
+		bAspectRatioX		- the Y dimension of the picture aspect
+					ratio, read-only
+		bDefaultFrameIndex	- optimum frame index for this stream
+		bBitsPerPixel		- number of bits per pixel used to
+					specify color in the decoded video
+					frame
+		guidFormat		- globally unique id used to identify
+					stream-encoding format
+
+What:		/config/usb-gadget/gadget/functions/uvc.name/streaming/uncompressed/name/name
+Date:		Dec 2014
+KernelVersion:	3.20
+Description:	Specific uncompressed frame descriptors
+
+		dwFrameInterval		- indicates how frame interval can be
+					programmed; a number of values
+					separated by newline can be specified
+		dwDefaultFrameInterval	- the frame interval the device would
+					like to use as default
+		dwMaxVideoFrameBufferSize- the maximum number of bytes the
+					compressor will produce for a video
+					frame or still image
+		dwMaxBitRate		- the maximum bit rate at the shortest
+					frame interval in bps
+		dwMinBitRate		- the minimum bit rate at the longest
+					frame interval in bps
+		wHeight			- height of decoded bitmap frame in px
+		wWidth			- width of decoded bitmam frame in px
+		bmCapabilities		- still image support, fixed frame-rate
+					support
+
+What:		/config/usb-gadget/gadget/functions/uvc.name/streaming/header
+Date:		Dec 2014
+KernelVersion:	3.20
+Description:	Streaming header descriptors
+
+What:		/config/usb-gadget/gadget/functions/uvc.name/streaming/header/name
+Date:		Dec 2014
+KernelVersion:	3.20
+Description:	Specific streaming header descriptors
+
+		All attributes read only:
+		bTriggerUsage		- how the host software will respond to
+					a hardware trigger interrupt event
+		bTriggerSupport		- flag specifying if hardware
+					triggering is supported
+		bStillCaptureMethod	- method of still image caputre
+					supported
+		bTerminalLink		- id of the output terminal to which
+					the video endpoint of this interface
+					is connected
+		bmInfo			- capabilities of this video streaming
+					interface

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

@@ -51,7 +51,10 @@ usb1: gadget@fffa4000 {
 Atmel High-Speed USB device controller
 Atmel High-Speed USB device controller
 
 
 Required properties:
 Required properties:
- - compatible: Should be "atmel,at91sam9rl-udc"
+ - compatible: Should be one of the following
+	       "at91sam9rl-udc"
+	       "at91sam9g45-udc"
+	       "sama5d3-udc"
  - reg: Address and length of the register set for the device
  - reg: Address and length of the register set for the device
  - interrupts: Should contain usba interrupt
  - interrupts: Should contain usba interrupt
  - ep childnode: To specify the number of endpoints and their properties.
  - ep childnode: To specify the number of endpoints and their properties.

+ 4 - 0
Documentation/devicetree/bindings/usb/dwc2.txt

@@ -20,6 +20,10 @@ Optional properties:
 Refer to phy/phy-bindings.txt for generic phy consumer properties
 Refer to phy/phy-bindings.txt for generic phy consumer properties
 - dr_mode: shall be one of "host", "peripheral" and "otg"
 - dr_mode: shall be one of "host", "peripheral" and "otg"
   Refer to usb/generic.txt
   Refer to usb/generic.txt
+- g-use-dma: enable dma usage in gadget driver.
+- g-rx-fifo-size: size of rx fifo size in gadget mode.
+- g-np-tx-fifo-size: size of non-periodic tx fifo size in gadget mode.
+- g-tx-fifo-size: size of periodic tx fifo per endpoint (except ep0) in gadget mode.
 
 
 Example:
 Example:
 
 

+ 2 - 0
Documentation/devicetree/bindings/usb/renesas_usbhs.txt

@@ -14,6 +14,8 @@ Optional properties:
 			 function should be enabled
 			 function should be enabled
   - 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.
+  - dma-names : Must contain a list of DMA names, "tx" or "rx".
 
 
 Example:
 Example:
 	usbhs: usb@e6590000 {
 	usbhs: usb@e6590000 {

+ 9 - 1
Documentation/devicetree/bindings/usb/usb-nop-xceiv.txt

@@ -13,10 +13,15 @@ Optional properties:
 - clock-frequency: the clock frequency (in Hz) that the PHY clock must
 - clock-frequency: the clock frequency (in Hz) that the PHY clock must
   be configured to.
   be configured to.
 
 
-- vcc-supply: phandle to the regulator that provides RESET to the PHY.
+- vcc-supply: phandle to the regulator that provides power to the PHY.
 
 
 - reset-gpios: Should specify the GPIO for reset.
 - reset-gpios: Should specify the GPIO for reset.
 
 
+- vbus-detect-gpio: should specify the GPIO detecting a VBus insertion
+                    (see Documentation/devicetree/bindings/gpio/gpio.txt)
+- vbus-regulator : should specifiy the regulator supplying current drawn from
+  the VBus line (see Documentation/devicetree/bindings/regulator/regulator.txt).
+
 Example:
 Example:
 
 
 	hsusb1_phy {
 	hsusb1_phy {
@@ -26,8 +31,11 @@ Example:
 		clock-names = "main_clk";
 		clock-names = "main_clk";
 		vcc-supply = <&hsusb1_vcc_regulator>;
 		vcc-supply = <&hsusb1_vcc_regulator>;
 		reset-gpios = <&gpio1 7 GPIO_ACTIVE_LOW>;
 		reset-gpios = <&gpio1 7 GPIO_ACTIVE_LOW>;
+		vbus-detect-gpio = <&gpio2 13 GPIO_ACTIVE_HIGH>;
+		vbus-regulator = <&vbus_regulator>;
 	};
 	};
 
 
 hsusb1_phy is a NOP USB PHY device that gets its clock from an oscillator
 hsusb1_phy is a NOP USB PHY device that gets its clock from an oscillator
 and expects that clock to be configured to 19.2MHz by the NOP PHY driver.
 and expects that clock to be configured to 19.2MHz by the NOP PHY driver.
 hsusb1_vcc_regulator provides power to the PHY and GPIO 7 controls RESET.
 hsusb1_vcc_regulator provides power to the PHY and GPIO 7 controls RESET.
+GPIO 13 detects VBus insertion, and accordingly notifies the vbus-regulator.

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

@@ -0,0 +1,728 @@
+This file summarizes information on basic testing of USB functions
+provided by gadgets.
+
+1. ACM function
+2. ECM function
+3. ECM subset function
+4. EEM function
+5. FFS function
+6. HID function
+7. LOOPBACK function
+8. MASS STORAGE function
+9. MIDI function
+10. NCM function
+11. OBEX function
+12. PHONET function
+13. RNDIS function
+14. SERIAL function
+15. SOURCESINK function
+16. UAC1 function
+17. UAC2 function
+18. UVC function
+
+
+1. ACM function
+===============
+
+The function is provided by usb_f_acm.ko module.
+
+Function-specific configfs interface
+------------------------------------
+
+The function name to use when creating the function directory is "acm".
+The ACM function provides just one attribute in its function directory:
+
+	port_num
+
+The attribute is read-only.
+
+There can be at most 4 ACM/generic serial/OBEX ports in the system.
+
+
+Testing the ACM function
+------------------------
+
+On the host: cat > /dev/ttyACM<X>
+On the device : cat /dev/ttyGS<Y>
+
+then the other way round
+
+On the device: cat > /dev/ttyGS<Y>
+On the host: cat /dev/ttyACM<X>
+
+2. ECM function
+===============
+
+The function is provided by usb_f_ecm.ko module.
+
+Function-specific configfs interface
+------------------------------------
+
+The function name to use when creating the function directory is "ecm".
+The ECM function provides these attributes in its function directory:
+
+	ifname		- network device interface name associated with this
+			function instance
+	qmult		- queue length multiplier for high and super speed
+	host_addr	- MAC address of host's end of this
+			Ethernet over USB link
+	dev_addr	- MAC address of device's end of this
+			Ethernet over USB link
+
+and after creating the functions/ecm.<instance name> they contain default
+values: qmult is 5, dev_addr and host_addr are randomly selected.
+Except for ifname they can be written to until the function is linked to a
+configuration. The ifname is read-only and contains the name of the interface
+which was assigned by the net core, e. g. usb0.
+
+Testing the ECM function
+------------------------
+
+Configure IP addresses of the device and the host. Then:
+
+On the device: ping <host's IP>
+On the host: ping <device's IP>
+
+3. ECM subset function
+======================
+
+The function is provided by usb_f_ecm_subset.ko module.
+
+Function-specific configfs interface
+------------------------------------
+
+The function name to use when creating the function directory is "geth".
+The ECM subset function provides these attributes in its function directory:
+
+	ifname		- network device interface name associated with this
+			function instance
+	qmult		- queue length multiplier for high and super speed
+	host_addr	- MAC address of host's end of this
+			Ethernet over USB link
+	dev_addr	- MAC address of device's end of this
+			Ethernet over USB link
+
+and after creating the functions/ecm.<instance name> they contain default
+values: qmult is 5, dev_addr and host_addr are randomly selected.
+Except for ifname they can be written to until the function is linked to a
+configuration. The ifname is read-only and contains the name of the interface
+which was assigned by the net core, e. g. usb0.
+
+Testing the ECM subset function
+-------------------------------
+
+Configure IP addresses of the device and the host. Then:
+
+On the device: ping <host's IP>
+On the host: ping <device's IP>
+
+4. EEM function
+===============
+
+The function is provided by usb_f_eem.ko module.
+
+Function-specific configfs interface
+------------------------------------
+
+The function name to use when creating the function directory is "eem".
+The EEM function provides these attributes in its function directory:
+
+	ifname		- network device interface name associated with this
+			function instance
+	qmult		- queue length multiplier for high and super speed
+	host_addr	- MAC address of host's end of this
+			Ethernet over USB link
+	dev_addr	- MAC address of device's end of this
+			Ethernet over USB link
+
+and after creating the functions/eem.<instance name> they contain default
+values: qmult is 5, dev_addr and host_addr are randomly selected.
+Except for ifname they can be written to until the function is linked to a
+configuration. The ifname is read-only and contains the name of the interface
+which was assigned by the net core, e. g. usb0.
+
+Testing the EEM function
+------------------------
+
+Configure IP addresses of the device and the host. Then:
+
+On the device: ping <host's IP>
+On the host: ping <device's IP>
+
+5. FFS function
+===============
+
+The function is provided by usb_f_fs.ko module.
+
+Function-specific configfs interface
+------------------------------------
+
+The function name to use when creating the function directory is "ffs".
+The function directory is intentionally empty and not modifiable.
+
+After creating the directory there is a new instance (a "device") of FunctionFS
+available in the system. Once a "device" is available, the user should follow
+the standard procedure for using FunctionFS (mount it, run the userspace
+process which implements the function proper). The gadget should be enabled
+by writing a suitable string to usb_gadget/<gadget>/UDC.
+
+Testing the FFS function
+------------------------
+
+On the device: start the function's userspace daemon, enable the gadget
+On the host: use the USB function provided by the device
+
+6. HID function
+===============
+
+The function is provided by usb_f_hid.ko module.
+
+Function-specific configfs interface
+------------------------------------
+
+The function name to use when creating the function directory is "hid".
+The HID function provides these attributes in its function directory:
+
+	protocol	- HID protocol to use
+	report_desc	- data to be used in HID reports, except data
+			passed with /dev/hidg<X>
+	report_length	- HID report length
+	subclass	- HID subclass to use
+
+For a keyboard the protocol and the subclass are 1, the report_length is 8,
+while the report_desc is:
+
+$ hd my_report_desc
+00000000  05 01 09 06 a1 01 05 07  19 e0 29 e7 15 00 25 01  |..........)...%.|
+00000010  75 01 95 08 81 02 95 01  75 08 81 03 95 05 75 01  |u.......u.....u.|
+00000020  05 08 19 01 29 05 91 02  95 01 75 03 91 03 95 06  |....).....u.....|
+00000030  75 08 15 00 25 65 05 07  19 00 29 65 81 00 c0     |u...%e....)e...|
+0000003f
+
+Such a sequence of bytes can be stored to the attribute with echo:
+
+$ echo -ne \\x05\\x01\\x09\\x06\\xa1.....
+
+Testing the HID function
+------------------------
+
+Device:
+- create the gadget
+- connect the gadget to a host, preferably not the one used
+to control the gadget
+- run a program which writes to /dev/hidg<N>, e.g.
+a userspace program found in Documentation/usb/gadget_hid.txt:
+
+$ ./hid_gadget_test /dev/hidg0 keyboard
+
+Host:
+- observe the keystrokes from the gadget
+
+7. LOOPBACK function
+====================
+
+The function is provided by usb_f_ss_lb.ko module.
+
+Function-specific configfs interface
+------------------------------------
+
+The function name to use when creating the function directory is "Loopback".
+The LOOPBACK function provides these attributes in its function directory:
+
+	qlen		- depth of loopback queue
+	bulk_buflen	- buffer length
+
+Testing the LOOPBACK function
+-----------------------------
+
+device: run the gadget
+host: test-usb
+
+http://www.linux-usb.org/usbtest/testusb.c
+
+8. MASS STORAGE function
+========================
+
+The function is provided by usb_f_mass_storage.ko module.
+
+Function-specific configfs interface
+------------------------------------
+
+The function name to use when creating the function directory is "mass_storage".
+The MASS STORAGE function provides these attributes in its directory:
+files:
+
+	stall		- Set to permit function to halt bulk endpoints.
+			Disabled on some USB devices known not to work
+			correctly. You should set it to true.
+	num_buffers	- Number of pipeline buffers. Valid numbers
+			are 2..4. Available only if
+			CONFIG_USB_GADGET_DEBUG_FILES is set.
+
+and a default lun.0 directory corresponding to SCSI LUN #0.
+
+A new lun can be added with mkdir:
+
+$ mkdir functions/mass_storage.0/partition.5
+
+Lun numbering does not have to be continuous, except for lun #0 which is
+created by default. A maximum of 8 luns can be specified and they all must be
+named following the <name>.<number> scheme. The numbers can be 0..8.
+Probably a good convention is to name the luns "lun.<number>",
+although it is not mandatory.
+
+In each lun directory there are the following attribute files:
+
+	file		- The path to the backing file for the LUN.
+			Required if LUN is not marked as removable.
+	ro		- Flag specifying access to the LUN shall be
+			read-only. This is implied if CD-ROM emulation
+			is enabled as well as when it was impossible
+			to open "filename" in R/W mode.
+	removable	- Flag specifying that LUN shall be indicated as
+			being removable.
+	cdrom		- Flag specifying that LUN shall be reported as
+			being a CD-ROM.
+	nofua		- Flag specifying that FUA flag
+			in SCSI WRITE(10,12)
+
+Testing the MASS STORAGE function
+---------------------------------
+
+device: connect the gadget, enable it
+host: dmesg, see the USB drives appear (if system configured to automatically
+mount)
+
+9. MIDI function
+================
+
+The function is provided by usb_f_midi.ko module.
+
+Function-specific configfs interface
+------------------------------------
+
+The function name to use when creating the function directory is "midi".
+The MIDI function provides these attributes in its function directory:
+
+	buflen		- MIDI buffer length
+	id		- ID string for the USB MIDI adapter
+	in_ports	- number of MIDI input ports
+	index		- index value for the USB MIDI adapter
+	out_ports	- number of MIDI output ports
+	qlen		- USB read request queue length
+
+Testing the MIDI function
+-------------------------
+
+There are two cases: playing a mid from the gadget to
+the host and playing a mid from the host to the gadget.
+
+1) Playing a mid from the gadget to the host
+host)
+
+$ arecordmidi -l
+ Port    Client name                      Port name
+ 14:0    Midi Through                     Midi Through Port-0
+ 24:0    MIDI Gadget                      MIDI Gadget MIDI 1
+$ arecordmidi -p 24:0 from_gadget.mid
+
+gadget)
+
+$ aplaymidi -l
+ Port    Client name                      Port name
+ 20:0    f_midi                           f_midi
+
+$ aplaymidi -p 20:0 to_host.mid
+
+2) Playing a mid from the host to the gadget
+gadget)
+
+$ arecordmidi -l
+ Port    Client name                      Port name
+ 20:0    f_midi                           f_midi
+
+$ arecordmidi -p 20:0 from_host.mid
+
+host)
+
+$ aplaymidi -l
+ Port    Client name                      Port name
+ 14:0    Midi Through                     Midi Through Port-0
+ 24:0    MIDI Gadget                      MIDI Gadget MIDI 1
+
+$ aplaymidi -p24:0 to_gadget.mid
+
+The from_gadget.mid should sound identical to the to_host.mid.
+The from_host.id should sound identical to the to_gadget.mid.
+
+MIDI files can be played to speakers/headphones with e.g. timidity installed
+
+$ aplaymidi -l
+ Port    Client name                      Port name
+ 14:0    Midi Through                     Midi Through Port-0
+ 24:0    MIDI Gadget                      MIDI Gadget MIDI 1
+128:0    TiMidity                         TiMidity port 0
+128:1    TiMidity                         TiMidity port 1
+128:2    TiMidity                         TiMidity port 2
+128:3    TiMidity                         TiMidity port 3
+
+$ aplaymidi -p 128:0 file.mid
+
+MIDI ports can be logically connected using the aconnect utility, e.g.:
+
+$ aconnect 24:0 128:0 # try it on the host
+
+After the gadget's MIDI port is connected to timidity's MIDI port,
+whatever is played at the gadget side with aplaymidi -l is audible
+in host's speakers/headphones.
+
+10. NCM function
+================
+
+The function is provided by usb_f_ncm.ko module.
+
+Function-specific configfs interface
+------------------------------------
+
+The function name to use when creating the function directory is "ncm".
+The NCM function provides these attributes in its function directory:
+
+	ifname		- network device interface name associated with this
+			function instance
+	qmult		- queue length multiplier for high and super speed
+	host_addr	- MAC address of host's end of this
+			Ethernet over USB link
+	dev_addr	- MAC address of device's end of this
+			Ethernet over USB link
+
+and after creating the functions/ncm.<instance name> they contain default
+values: qmult is 5, dev_addr and host_addr are randomly selected.
+Except for ifname they can be written to until the function is linked to a
+configuration. The ifname is read-only and contains the name of the interface
+which was assigned by the net core, e. g. usb0.
+
+Testing the NCM function
+------------------------
+
+Configure IP addresses of the device and the host. Then:
+
+On the device: ping <host's IP>
+On the host: ping <device's IP>
+
+11. OBEX function
+=================
+
+The function is provided by usb_f_obex.ko module.
+
+Function-specific configfs interface
+------------------------------------
+
+The function name to use when creating the function directory is "obex".
+The OBEX function provides just one attribute in its function directory:
+
+	port_num
+
+The attribute is read-only.
+
+There can be at most 4 ACM/generic serial/OBEX ports in the system.
+
+Testing the OBEX function
+-------------------------
+
+On device: seriald -f /dev/ttyGS<Y> -s 1024
+On host: serialc -v <vendorID> -p <productID> -i<interface#> -a1 -s1024 \
+             -t<out endpoint addr> -r<in endpoint addr>
+
+where seriald and serialc are Felipe's utilities found here:
+
+https://git.gitorious.org/usb/usb-tools.git master
+
+12. PHONET function
+===================
+
+The function is provided by usb_f_phonet.ko module.
+
+Function-specific configfs interface
+------------------------------------
+
+The function name to use when creating the function directory is "phonet".
+The PHONET function provides just one attribute in its function directory:
+
+	ifname		- network device interface name associated with this
+			function instance
+
+Testing the PHONET function
+---------------------------
+
+It is not possible to test the SOCK_STREAM protocol without a specific piece
+of hardware, so only SOCK_DGRAM has been tested. For the latter to work,
+in the past I had to apply the patch mentioned here:
+
+http://www.spinics.net/lists/linux-usb/msg85689.html
+
+These tools are required:
+
+git://git.gitorious.org/meego-cellular/phonet-utils.git
+
+On the host:
+
+$ ./phonet -a 0x10 -i usbpn0
+$ ./pnroute add 0x6c usbpn0
+$./pnroute add 0x10 usbpn0
+$ ifconfig usbpn0 up
+
+On the device:
+
+$ ./phonet -a 0x6c -i upnlink0
+$ ./pnroute add 0x10 upnlink0
+$ ifconfig upnlink0 up
+
+Then a test program can be used:
+
+http://www.spinics.net/lists/linux-usb/msg85690.html
+
+On the device:
+
+$ ./pnxmit -a 0x6c -r
+
+On the host:
+
+$ ./pnxmit -a 0x10 -s 0x6c
+
+As a result some data should be sent from host to device.
+Then the other way round:
+
+On the host:
+
+$ ./pnxmit -a 0x10 -r
+
+On the device:
+
+$ ./pnxmit -a 0x6c -s 0x10
+
+13. RNDIS function
+==================
+
+The function is provided by usb_f_rndis.ko module.
+
+Function-specific configfs interface
+------------------------------------
+
+The function name to use when creating the function directory is "rndis".
+The RNDIS function provides these attributes in its function directory:
+
+	ifname		- network device interface name associated with this
+			function instance
+	qmult		- queue length multiplier for high and super speed
+	host_addr	- MAC address of host's end of this
+			Ethernet over USB link
+	dev_addr	- MAC address of device's end of this
+			Ethernet over USB link
+
+and after creating the functions/rndis.<instance name> they contain default
+values: qmult is 5, dev_addr and host_addr are randomly selected.
+Except for ifname they can be written to until the function is linked to a
+configuration. The ifname is read-only and contains the name of the interface
+which was assigned by the net core, e. g. usb0.
+
+By default there can be only 1 RNDIS interface in the system.
+
+Testing the RNDIS function
+--------------------------
+
+Configure IP addresses of the device and the host. Then:
+
+On the device: ping <host's IP>
+On the host: ping <device's IP>
+
+14. SERIAL function
+===================
+
+The function is provided by usb_f_gser.ko module.
+
+Function-specific configfs interface
+------------------------------------
+
+The function name to use when creating the function directory is "gser".
+The SERIAL function provides just one attribute in its function directory:
+
+	port_num
+
+The attribute is read-only.
+
+There can be at most 4 ACM/generic serial/OBEX ports in the system.
+
+Testing the SERIAL function
+---------------------------
+
+On host: insmod usbserial
+	 echo VID PID >/sys/bus/usb-serial/drivers/generic/new_id
+On host: cat > /dev/ttyUSB<X>
+On target: cat /dev/ttyGS<Y>
+
+then the other way round
+
+On target: cat > /dev/ttyGS<Y>
+On host: cat /dev/ttyUSB<X>
+
+15. SOURCESINK function
+=======================
+
+The function is provided by usb_f_ss_lb.ko module.
+
+Function-specific configfs interface
+------------------------------------
+
+The function name to use when creating the function directory is "SourceSink".
+The SOURCESINK function provides these attributes in its function directory:
+
+	pattern		- 0 (all zeros), 1 (mod63), 2 (none)
+	isoc_interval	- 1..16
+	isoc_maxpacket	- 0 - 1023 (fs), 0 - 1024 (hs/ss)
+	isoc_mult	- 0..2 (hs/ss only)
+	isoc_maxburst	- 0..15 (ss only)
+	bulk_buflen	- buffer length
+
+Testing the SOURCESINK function
+-------------------------------
+
+device: run the gadget
+host: test-usb
+
+http://www.linux-usb.org/usbtest/testusb.c
+
+16. UAC1 function
+=================
+
+The function is provided by usb_f_uac1.ko module.
+
+Function-specific configfs interface
+------------------------------------
+
+The function name to use when creating the function directory is "uac1".
+The uac1 function provides these attributes in its function directory:
+
+	audio_buf_size - audio buffer size
+	fn_cap - capture pcm device file name
+	fn_cntl - control device file name
+	fn_play - playback pcm device file name
+	req_buf_size - ISO OUT endpoint request buffer size
+	req_count - ISO OUT endpoint request count
+
+The attributes have sane default values.
+
+Testing the UAC1 function
+-------------------------
+
+device: run the gadget
+host: aplay -l # should list our USB Audio Gadget
+
+17. UAC2 function
+=================
+
+The function is provided by usb_f_uac2.ko module.
+
+Function-specific configfs interface
+------------------------------------
+
+The function name to use when creating the function directory is "uac2".
+The uac2 function provides these attributes in its function directory:
+
+	chmask - capture channel mask
+	c_srate - capture sampling rate
+	c_ssize - capture sample size (bytes)
+	p_chmask - playback channel mask
+	p_srate - playback sampling rate
+	p_ssize - playback sample size (bytes)
+
+The attributes have sane default values.
+
+Testing the UAC2 function
+-------------------------
+
+device: run the gadget
+host: aplay -l # should list our USB Audio Gadget
+
+This function does not require real hardware support, it just
+sends a stream of audio data to/from the host. In order to
+actually hear something at the device side, a command similar
+to this must be used at the device side:
+
+$ arecord -f dat -t wav -D hw:2,0 | aplay -D hw:0,0 &
+
+e.g.:
+
+$ arecord -f dat -t wav -D hw:CARD=UAC2Gadget,DEV=0 | \
+aplay -D default:CARD=OdroidU3
+
+18. UVC function
+================
+
+The function is provided by usb_f_uvc.ko module.
+
+Function-specific configfs interface
+------------------------------------
+
+The function name to use when creating the function directory is "uvc".
+The uvc function provides these attributes in its function directory:
+
+	streaming_interval - interval for polling endpoint for data transfers
+	streaming_maxburst - bMaxBurst for super speed companion descriptor
+	streaming_maxpacket - maximum packet size this endpoint is capable of
+			      sending or receiving when this configuration is
+			      selected
+
+There are also "control" and "streaming" subdirectories, each of which contain
+a number of their subdirectories. There are some sane defaults provided, but
+the user must provide the following:
+
+	control header - create in control/header, link from control/class/fs
+			and/or control/class/ss
+	streaming header - create in streaming/header, link from
+			streaming/class/fs and/or streaming/class/hs and/or
+			streaming/class/ss
+	format description - create in streaming/mjpeg and/or
+			streaming/uncompressed
+	frame description - create in streaming/mjpeg/<format> and/or in
+			streaming/uncompressed/<format>
+
+Each frame description contains frame interval specification, and each
+such specification consists of a number of lines with an inverval value
+in each line. The rules stated above are best illustrated with an example:
+
+# mkdir functions/uvc.usb0/control/header/h
+# cd functions/uvc.usb0/control/header/h
+# ln -s header/h class/fs
+# ln -s header/h class/ss
+# mkdir -p functions/uvc.usb0/streaming/uncompressed/u/360p
+# cat <<EOF > functions/uvc.usb0/streaming/uncompressed/u/360p/dwFrameInterval
+666666
+1000000
+5000000
+EOF
+# cd $GADGET_CONFIGFS_ROOT
+# mkdir functions/uvc.usb0/streaming/header/h
+# cd functions/uvc.usb0/streaming/header/h
+# ln -s ../../uncompressed/u
+# cd ../../class/fs
+# ln -s ../../header/h
+# cd ../../class/hs
+# ln -s ../../header/h
+# cd ../../class/ss
+# ln -s ../../header/h
+
+
+Testing the UVC function
+------------------------
+
+device: run the gadget, modprobe vivid
+
+# uvc-gadget -u /dev/video<uvc video node #> -v /dev/video<vivid video node #>
+
+where uvc-gadget is this program:
+http://git.ideasonboard.org/uvc-gadget.git
+
+with these patches:
+http://www.spinics.net/lists/linux-usb/msg99220.html
+
+host: luvcview -f yuv

+ 6 - 2
Documentation/usb/gadget_serial.txt

@@ -236,8 +236,12 @@ I:  If#= 0 Alt= 0 #EPs= 2 Cls=0a(data ) Sub=00 Prot=00 Driver=serial
 E:  Ad=81(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms
 E:  Ad=81(I) Atr=02(Bulk) MxPS= 512 Ivl=0ms
 E:  Ad=02(O) Atr=02(Bulk) MxPS= 512 Ivl=0ms
 E:  Ad=02(O) Atr=02(Bulk) MxPS= 512 Ivl=0ms
 
 
-You must explicitly load the usbserial driver with parameters to
-configure it to recognize the gadget serial device, like this:
+You must load the usbserial driver and explicitly set its parameters
+to configure it to recognize the gadget serial device, like this:
+
+  echo 0x0525 0xA4A6 >/sys/bus/usb-serial/drivers/generic/new_id
+
+The legacy way is to use module parameters:
 
 
   modprobe usbserial vendor=0x0525 product=0xA4A6
   modprobe usbserial vendor=0x0525 product=0xA4A6
 
 

+ 1 - 1
MAINTAINERS

@@ -3033,7 +3033,7 @@ S:	Maintained
 F:	drivers/platform/x86/dell-wmi.c
 F:	drivers/platform/x86/dell-wmi.c
 
 
 DESIGNWARE USB2 DRD IP DRIVER
 DESIGNWARE USB2 DRD IP DRIVER
-M:	Paul Zimmerman <paulz@synopsys.com>
+M:	John Youn <johnyoun@synopsys.com>
 L:	linux-usb@vger.kernel.org
 L:	linux-usb@vger.kernel.org
 T:	git git://git.kernel.org/pub/scm/linux/kernel/git/balbi/usb.git
 T:	git git://git.kernel.org/pub/scm/linux/kernel/git/balbi/usb.git
 S:	Maintained
 S:	Maintained

+ 4 - 4
drivers/staging/emxx_udc/emxx_udc.c

@@ -1608,7 +1608,7 @@ static int std_req_get_status(struct nbu2ss_udc *udc)
 	switch (recipient) {
 	switch (recipient) {
 	case USB_RECIP_DEVICE:
 	case USB_RECIP_DEVICE:
 		if (udc->ctrl.wIndex == 0x0000) {
 		if (udc->ctrl.wIndex == 0x0000) {
-			if (udc->self_powered)
+			if (udc->gadget.is_selfpowered)
 				status_data |= (1 << USB_DEVICE_SELF_POWERED);
 				status_data |= (1 << USB_DEVICE_SELF_POWERED);
 
 
 			if (udc->remote_wakeup)
 			if (udc->remote_wakeup)
@@ -3117,7 +3117,7 @@ static int nbu2ss_gad_wakeup(struct usb_gadget *pgadget)
 static int nbu2ss_gad_set_selfpowered(struct usb_gadget *pgadget,
 static int nbu2ss_gad_set_selfpowered(struct usb_gadget *pgadget,
 					int is_selfpowered)
 					int is_selfpowered)
 {
 {
-	struct nbu2ss_udc	*udc;
+	struct nbu2ss_udc       *udc;
 	unsigned long		flags;
 	unsigned long		flags;
 
 
 /*	INFO("=== %s()\n", __func__); */
 /*	INFO("=== %s()\n", __func__); */
@@ -3130,7 +3130,7 @@ static int nbu2ss_gad_set_selfpowered(struct usb_gadget *pgadget,
 	udc = container_of(pgadget, struct nbu2ss_udc, gadget);
 	udc = container_of(pgadget, struct nbu2ss_udc, gadget);
 
 
 	spin_lock_irqsave(&udc->lock, flags);
 	spin_lock_irqsave(&udc->lock, flags);
-	udc->self_powered = (is_selfpowered != 0);
+	pgadget->is_selfpowered = (is_selfpowered != 0);
 	spin_unlock_irqrestore(&udc->lock, flags);
 	spin_unlock_irqrestore(&udc->lock, flags);
 
 
 	return 0;
 	return 0;
@@ -3308,7 +3308,7 @@ static int __init nbu2ss_drv_contest_init(
 	spin_lock_init(&udc->lock);
 	spin_lock_init(&udc->lock);
 	udc->dev = &pdev->dev;
 	udc->dev = &pdev->dev;
 
 
-	udc->self_powered = 1;
+	udc->gadget.is_selfpowered = 1;
 	udc->devstate = USB_STATE_NOTATTACHED;
 	udc->devstate = USB_STATE_NOTATTACHED;
 	udc->pdev = pdev;
 	udc->pdev = pdev;
 	udc->mA = 0;
 	udc->mA = 0;

+ 0 - 1
drivers/staging/emxx_udc/emxx_udc.h

@@ -624,7 +624,6 @@ struct nbu2ss_udc {
 	unsigned		linux_suspended:1;
 	unsigned		linux_suspended:1;
 	unsigned		linux_resume:1;
 	unsigned		linux_resume:1;
 	unsigned		usb_suspended:1;
 	unsigned		usb_suspended:1;
-	unsigned		self_powered:1;
 	unsigned		remote_wakeup:1;
 	unsigned		remote_wakeup:1;
 	unsigned		udc_enabled:1;
 	unsigned		udc_enabled:1;
 
 

+ 2 - 0
drivers/usb/Kconfig

@@ -104,6 +104,8 @@ source "drivers/usb/dwc2/Kconfig"
 
 
 source "drivers/usb/chipidea/Kconfig"
 source "drivers/usb/chipidea/Kconfig"
 
 
+source "drivers/usb/isp1760/Kconfig"
+
 comment "USB port drivers"
 comment "USB port drivers"
 
 
 if USB
 if USB

+ 1 - 1
drivers/usb/Makefile

@@ -8,6 +8,7 @@ obj-$(CONFIG_USB)		+= core/
 
 
 obj-$(CONFIG_USB_DWC3)		+= dwc3/
 obj-$(CONFIG_USB_DWC3)		+= dwc3/
 obj-$(CONFIG_USB_DWC2)		+= dwc2/
 obj-$(CONFIG_USB_DWC2)		+= dwc2/
+obj-$(CONFIG_USB_ISP1760)	+= isp1760/
 
 
 obj-$(CONFIG_USB_MON)		+= mon/
 obj-$(CONFIG_USB_MON)		+= mon/
 
 
@@ -23,7 +24,6 @@ obj-$(CONFIG_USB_ISP1362_HCD)	+= host/
 obj-$(CONFIG_USB_U132_HCD)	+= host/
 obj-$(CONFIG_USB_U132_HCD)	+= host/
 obj-$(CONFIG_USB_R8A66597_HCD)	+= host/
 obj-$(CONFIG_USB_R8A66597_HCD)	+= host/
 obj-$(CONFIG_USB_HWA_HCD)	+= host/
 obj-$(CONFIG_USB_HWA_HCD)	+= host/
-obj-$(CONFIG_USB_ISP1760_HCD)	+= host/
 obj-$(CONFIG_USB_IMX21_HCD)	+= host/
 obj-$(CONFIG_USB_IMX21_HCD)	+= host/
 obj-$(CONFIG_USB_FSL_MPH_DR_OF)	+= host/
 obj-$(CONFIG_USB_FSL_MPH_DR_OF)	+= host/
 obj-$(CONFIG_USB_FUSBH200_HCD)	+= host/
 obj-$(CONFIG_USB_FUSBH200_HCD)	+= host/

+ 16 - 2
drivers/usb/chipidea/udc.c

@@ -819,8 +819,8 @@ __acquires(hwep->lock)
 	}
 	}
 
 
 	if ((setup->bRequestType & USB_RECIP_MASK) == USB_RECIP_DEVICE) {
 	if ((setup->bRequestType & USB_RECIP_MASK) == USB_RECIP_DEVICE) {
-		/* Assume that device is bus powered for now. */
-		*(u16 *)req->buf = ci->remote_wakeup << 1;
+		*(u16 *)req->buf = (ci->remote_wakeup << 1) |
+			ci->gadget.is_selfpowered;
 	} else if ((setup->bRequestType & USB_RECIP_MASK) \
 	} else if ((setup->bRequestType & USB_RECIP_MASK) \
 		   == USB_RECIP_ENDPOINT) {
 		   == USB_RECIP_ENDPOINT) {
 		dir = (le16_to_cpu(setup->wIndex) & USB_ENDPOINT_DIR_MASK) ?
 		dir = (le16_to_cpu(setup->wIndex) & USB_ENDPOINT_DIR_MASK) ?
@@ -1520,6 +1520,19 @@ static int ci_udc_vbus_draw(struct usb_gadget *_gadget, unsigned ma)
 	return -ENOTSUPP;
 	return -ENOTSUPP;
 }
 }
 
 
+static int ci_udc_selfpowered(struct usb_gadget *_gadget, int is_on)
+{
+	struct ci_hdrc *ci = container_of(_gadget, struct ci_hdrc, gadget);
+	struct ci_hw_ep *hwep = ci->ep0in;
+	unsigned long flags;
+
+	spin_lock_irqsave(hwep->lock, flags);
+	_gadget->is_selfpowered = (is_on != 0);
+	spin_unlock_irqrestore(hwep->lock, flags);
+
+	return 0;
+}
+
 /* Change Data+ pullup status
 /* Change Data+ pullup status
  * this func is used by usb_gadget_connect/disconnet
  * this func is used by usb_gadget_connect/disconnet
  */
  */
@@ -1549,6 +1562,7 @@ static int ci_udc_stop(struct usb_gadget *gadget);
 static const struct usb_gadget_ops usb_gadget_ops = {
 static const struct usb_gadget_ops usb_gadget_ops = {
 	.vbus_session	= ci_udc_vbus_session,
 	.vbus_session	= ci_udc_vbus_session,
 	.wakeup		= ci_udc_wakeup,
 	.wakeup		= ci_udc_wakeup,
+	.set_selfpowered	= ci_udc_selfpowered,
 	.pullup		= ci_udc_pullup,
 	.pullup		= ci_udc_pullup,
 	.vbus_draw	= ci_udc_vbus_draw,
 	.vbus_draw	= ci_udc_vbus_draw,
 	.udc_start	= ci_udc_start,
 	.udc_start	= ci_udc_start,

+ 2 - 2
drivers/usb/dwc2/Kconfig

@@ -23,7 +23,7 @@ choice
 
 
 config USB_DWC2_HOST
 config USB_DWC2_HOST
 	bool "Host only mode"
 	bool "Host only mode"
-	depends on USB
+	depends on USB=y || (USB_DWC2=m && USB)
 	help
 	help
 	  The Designware USB2.0 high-speed host controller
 	  The Designware USB2.0 high-speed host controller
 	  integrated into many SoCs. Select this option if you want the
 	  integrated into many SoCs. Select this option if you want the
@@ -42,7 +42,7 @@ config USB_DWC2_PERIPHERAL
 
 
 config USB_DWC2_DUAL_ROLE
 config USB_DWC2_DUAL_ROLE
 	bool "Dual Role mode"
 	bool "Dual Role mode"
-	depends on (USB=y || USB=USB_DWC2) && (USB_GADGET=y || USB_GADGET=USB_DWC2)
+	depends on (USB=y && USB_GADGET=y) || (USB_DWC2=m && USB && USB_GADGET)
 	help
 	help
 	  Select this option if you want the driver to work in a dual-role
 	  Select this option if you want the driver to work in a dual-role
 	  mode. In this mode both host and gadget features are enabled, and
 	  mode. In this mode both host and gadget features are enabled, and

+ 1 - 1
drivers/usb/dwc2/core.c

@@ -462,7 +462,7 @@ int dwc2_core_init(struct dwc2_hsotg *hsotg, bool select_phy, int irq)
 	dwc2_enable_common_interrupts(hsotg);
 	dwc2_enable_common_interrupts(hsotg);
 
 
 	/*
 	/*
-	 * Do device or host intialization based on mode during PCD and
+	 * Do device or host initialization based on mode during PCD and
 	 * HCD initialization
 	 * HCD initialization
 	 */
 	 */
 	if (dwc2_is_host_mode(hsotg)) {
 	if (dwc2_is_host_mode(hsotg)) {

+ 46 - 14
drivers/usb/dwc2/core.h

@@ -108,7 +108,7 @@ struct s3c_hsotg_req;
  * @halted: Set if the endpoint has been halted.
  * @halted: Set if the endpoint has been halted.
  * @periodic: Set if this is a periodic ep, such as Interrupt
  * @periodic: Set if this is a periodic ep, such as Interrupt
  * @isochronous: Set if this is a isochronous ep
  * @isochronous: Set if this is a isochronous ep
- * @sent_zlp: Set if we've sent a zero-length packet.
+ * @send_zlp: Set if we need to send a zero-length packet.
  * @total_data: The total number of data bytes done.
  * @total_data: The total number of data bytes done.
  * @fifo_size: The size of the FIFO (for periodic IN endpoints)
  * @fifo_size: The size of the FIFO (for periodic IN endpoints)
  * @fifo_load: The amount of data loaded into the FIFO (periodic IN)
  * @fifo_load: The amount of data loaded into the FIFO (periodic IN)
@@ -149,7 +149,7 @@ struct s3c_hsotg_ep {
 	unsigned int            halted:1;
 	unsigned int            halted:1;
 	unsigned int            periodic:1;
 	unsigned int            periodic:1;
 	unsigned int            isochronous:1;
 	unsigned int            isochronous:1;
-	unsigned int            sent_zlp:1;
+	unsigned int            send_zlp:1;
 
 
 	char                    name[10];
 	char                    name[10];
 };
 };
@@ -158,14 +158,12 @@ struct s3c_hsotg_ep {
  * struct s3c_hsotg_req - data transfer request
  * struct s3c_hsotg_req - data transfer request
  * @req: The USB gadget request
  * @req: The USB gadget request
  * @queue: The list of requests for the endpoint this is queued for.
  * @queue: The list of requests for the endpoint this is queued for.
- * @in_progress: Has already had size/packets written to core
- * @mapped: DMA buffer for this request has been mapped via dma_map_single().
+ * @saved_req_buf: variable to save req.buf when bounce buffers are used.
  */
  */
 struct s3c_hsotg_req {
 struct s3c_hsotg_req {
 	struct usb_request      req;
 	struct usb_request      req;
 	struct list_head        queue;
 	struct list_head        queue;
-	unsigned char           in_progress;
-	unsigned char           mapped;
+	void *saved_req_buf;
 };
 };
 
 
 #if IS_ENABLED(CONFIG_USB_DWC2_PERIPHERAL) || IS_ENABLED(CONFIG_USB_DWC2_DUAL_ROLE)
 #if IS_ENABLED(CONFIG_USB_DWC2_PERIPHERAL) || IS_ENABLED(CONFIG_USB_DWC2_DUAL_ROLE)
@@ -193,6 +191,22 @@ enum dwc2_lx_state {
 	DWC2_L3,	/* Off state */
 	DWC2_L3,	/* Off state */
 };
 };
 
 
+/*
+ * Gadget periodic tx fifo sizes as used by legacy driver
+ * EP0 is not included
+ */
+#define DWC2_G_P_LEGACY_TX_FIFO_SIZE {256, 256, 256, 256, 768, 768, 768, \
+					   768, 0, 0, 0, 0, 0, 0, 0}
+
+/* Gadget ep0 states */
+enum dwc2_ep0_state {
+	DWC2_EP0_SETUP,
+	DWC2_EP0_DATA_IN,
+	DWC2_EP0_DATA_OUT,
+	DWC2_EP0_STATUS_IN,
+	DWC2_EP0_STATUS_OUT,
+};
+
 /**
 /**
  * struct dwc2_core_params - Parameters for configuring the core
  * struct dwc2_core_params - Parameters for configuring the core
  *
  *
@@ -381,7 +395,7 @@ struct dwc2_core_params {
  * @power_optimized     Are power optimizations enabled?
  * @power_optimized     Are power optimizations enabled?
  * @num_dev_ep          Number of device endpoints available
  * @num_dev_ep          Number of device endpoints available
  * @num_dev_perio_in_ep Number of device periodic IN endpoints
  * @num_dev_perio_in_ep Number of device periodic IN endpoints
- *                      avaialable
+ *                      available
  * @dev_token_q_depth   Device Mode IN Token Sequence Learning Queue
  * @dev_token_q_depth   Device Mode IN Token Sequence Learning Queue
  *                      Depth
  *                      Depth
  *                       0 to 30
  *                       0 to 30
@@ -434,6 +448,9 @@ struct dwc2_hw_params {
 	u32 snpsid;
 	u32 snpsid;
 };
 };
 
 
+/* Size of control and EP0 buffers */
+#define DWC2_CTRL_BUFF_SIZE 8
+
 /**
 /**
  * struct dwc2_hsotg - Holds the state of the driver, including the non-periodic
  * struct dwc2_hsotg - Holds the state of the driver, including the non-periodic
  * and periodic schedules
  * and periodic schedules
@@ -552,14 +569,20 @@ struct dwc2_hw_params {
  * @num_of_eps:         Number of available EPs (excluding EP0)
  * @num_of_eps:         Number of available EPs (excluding EP0)
  * @debug_root:         Root directrory for debugfs.
  * @debug_root:         Root directrory for debugfs.
  * @debug_file:         Main status file for debugfs.
  * @debug_file:         Main status file for debugfs.
+ * @debug_testmode:     Testmode status file for debugfs.
  * @debug_fifo:         FIFO status file for debugfs.
  * @debug_fifo:         FIFO status file for debugfs.
  * @ep0_reply:          Request used for ep0 reply.
  * @ep0_reply:          Request used for ep0 reply.
  * @ep0_buff:           Buffer for EP0 reply data, if needed.
  * @ep0_buff:           Buffer for EP0 reply data, if needed.
  * @ctrl_buff:          Buffer for EP0 control requests.
  * @ctrl_buff:          Buffer for EP0 control requests.
  * @ctrl_req:           Request for EP0 control packets.
  * @ctrl_req:           Request for EP0 control packets.
- * @setup:              NAK management for EP0 SETUP
+ * @ep0_state:          EP0 control transfers state
+ * @test_mode:          USB test mode requested by the host
  * @last_rst:           Time of last reset
  * @last_rst:           Time of last reset
  * @eps:                The endpoints being supplied to the gadget framework
  * @eps:                The endpoints being supplied to the gadget framework
+ * @g_using_dma:          Indicate if dma usage is enabled
+ * @g_rx_fifo_sz:         Contains rx fifo size value
+ * @g_np_g_tx_fifo_sz:      Contains Non-Periodic tx fifo size value
+ * @g_tx_fifo_sz:         Contains tx fifo size value per endpoints
  */
  */
 struct dwc2_hsotg {
 struct dwc2_hsotg {
 	struct device *dev;
 	struct device *dev;
@@ -591,6 +614,7 @@ struct dwc2_hsotg {
 
 
 	struct dentry *debug_root;
 	struct dentry *debug_root;
 	struct dentry *debug_file;
 	struct dentry *debug_file;
+	struct dentry *debug_testmode;
 	struct dentry *debug_fifo;
 	struct dentry *debug_fifo;
 
 
 	/* DWC OTG HW Release versions */
 	/* DWC OTG HW Release versions */
@@ -684,15 +708,21 @@ struct dwc2_hsotg {
 
 
 	struct usb_request *ep0_reply;
 	struct usb_request *ep0_reply;
 	struct usb_request *ctrl_req;
 	struct usb_request *ctrl_req;
-	u8 ep0_buff[8];
-	u8 ctrl_buff[8];
+	void *ep0_buff;
+	void *ctrl_buff;
+	enum dwc2_ep0_state ep0_state;
+	u8 test_mode;
 
 
 	struct usb_gadget gadget;
 	struct usb_gadget gadget;
 	unsigned int enabled:1;
 	unsigned int enabled:1;
 	unsigned int connected:1;
 	unsigned int connected:1;
-	unsigned int setup:1;
 	unsigned long last_rst;
 	unsigned long last_rst;
-	struct s3c_hsotg_ep *eps;
+	struct s3c_hsotg_ep *eps_in[MAX_EPS_CHANNELS];
+	struct s3c_hsotg_ep *eps_out[MAX_EPS_CHANNELS];
+	u32 g_using_dma;
+	u32 g_rx_fifo_sz;
+	u32 g_np_g_tx_fifo_sz;
+	u32 g_tx_fifo_sz[MAX_EPS_CHANNELS];
 #endif /* CONFIG_USB_DWC2_PERIPHERAL || CONFIG_USB_DWC2_DUAL_ROLE */
 #endif /* CONFIG_USB_DWC2_PERIPHERAL || CONFIG_USB_DWC2_DUAL_ROLE */
 };
 };
 
 
@@ -969,7 +999,8 @@ extern int s3c_hsotg_remove(struct dwc2_hsotg *hsotg);
 extern int s3c_hsotg_suspend(struct dwc2_hsotg *dwc2);
 extern int s3c_hsotg_suspend(struct dwc2_hsotg *dwc2);
 extern int s3c_hsotg_resume(struct dwc2_hsotg *dwc2);
 extern int s3c_hsotg_resume(struct dwc2_hsotg *dwc2);
 extern int dwc2_gadget_init(struct dwc2_hsotg *hsotg, int irq);
 extern int dwc2_gadget_init(struct dwc2_hsotg *hsotg, int irq);
-extern void s3c_hsotg_core_init_disconnected(struct dwc2_hsotg *dwc2);
+extern void s3c_hsotg_core_init_disconnected(struct dwc2_hsotg *dwc2,
+		bool reset);
 extern void s3c_hsotg_core_connect(struct dwc2_hsotg *hsotg);
 extern void s3c_hsotg_core_connect(struct dwc2_hsotg *hsotg);
 extern void s3c_hsotg_disconnect(struct dwc2_hsotg *dwc2);
 extern void s3c_hsotg_disconnect(struct dwc2_hsotg *dwc2);
 #else
 #else
@@ -981,7 +1012,8 @@ static inline int s3c_hsotg_resume(struct dwc2_hsotg *dwc2)
 { return 0; }
 { return 0; }
 static inline int dwc2_gadget_init(struct dwc2_hsotg *hsotg, int irq)
 static inline int dwc2_gadget_init(struct dwc2_hsotg *hsotg, int irq)
 { return 0; }
 { return 0; }
-static inline void s3c_hsotg_core_init_disconnected(struct dwc2_hsotg *dwc2) {}
+static inline void s3c_hsotg_core_init_disconnected(struct dwc2_hsotg *dwc2,
+		bool reset) {}
 static inline void s3c_hsotg_core_connect(struct dwc2_hsotg *hsotg) {}
 static inline void s3c_hsotg_core_connect(struct dwc2_hsotg *hsotg) {}
 static inline void s3c_hsotg_disconnect(struct dwc2_hsotg *dwc2) {}
 static inline void s3c_hsotg_disconnect(struct dwc2_hsotg *dwc2) {}
 #endif
 #endif

+ 831 - 360
drivers/usb/dwc2/gadget.c

@@ -35,6 +35,7 @@
 #include <linux/usb/gadget.h>
 #include <linux/usb/gadget.h>
 #include <linux/usb/phy.h>
 #include <linux/usb/phy.h>
 #include <linux/platform_data/s3c-hsotg.h>
 #include <linux/platform_data/s3c-hsotg.h>
+#include <linux/uaccess.h>
 
 
 #include "core.h"
 #include "core.h"
 #include "hw.h"
 #include "hw.h"
@@ -65,7 +66,16 @@ static inline void __bic32(void __iomem *ptr, u32 val)
 	writel(readl(ptr) & ~val, ptr);
 	writel(readl(ptr) & ~val, ptr);
 }
 }
 
 
-/* forward decleration of functions */
+static inline struct s3c_hsotg_ep *index_to_ep(struct dwc2_hsotg *hsotg,
+						u32 ep_index, u32 dir_in)
+{
+	if (dir_in)
+		return hsotg->eps_in[ep_index];
+	else
+		return hsotg->eps_out[ep_index];
+}
+
+/* forward declaration of functions */
 static void s3c_hsotg_dump(struct dwc2_hsotg *hsotg);
 static void s3c_hsotg_dump(struct dwc2_hsotg *hsotg);
 
 
 /**
 /**
@@ -85,11 +95,11 @@ static void s3c_hsotg_dump(struct dwc2_hsotg *hsotg);
  * a core reset. This means we either need to fix the gadgets to take
  * a core reset. This means we either need to fix the gadgets to take
  * account of DMA alignment, or add bounce buffers (yuerk).
  * account of DMA alignment, or add bounce buffers (yuerk).
  *
  *
- * Until this issue is sorted out, we always return 'false'.
+ * g_using_dma is set depending on dts flag.
  */
  */
 static inline bool using_dma(struct dwc2_hsotg *hsotg)
 static inline bool using_dma(struct dwc2_hsotg *hsotg)
 {
 {
-	return false;	/* support is not complete */
+	return hsotg->g_using_dma;
 }
 }
 
 
 /**
 /**
@@ -165,15 +175,18 @@ static void s3c_hsotg_init_fifo(struct dwc2_hsotg *hsotg)
 {
 {
 	unsigned int ep;
 	unsigned int ep;
 	unsigned int addr;
 	unsigned int addr;
-	unsigned int size;
 	int timeout;
 	int timeout;
 	u32 val;
 	u32 val;
 
 
-	/* set FIFO sizes to 2048/1024 */
+	/* Reset fifo map if not correctly cleared during previous session */
+	WARN_ON(hsotg->fifo_map);
+	hsotg->fifo_map = 0;
 
 
-	writel(2048, hsotg->regs + GRXFSIZ);
-	writel((2048 << FIFOSIZE_STARTADDR_SHIFT) |
-		(1024 << FIFOSIZE_DEPTH_SHIFT), hsotg->regs + GNPTXFSIZ);
+	/* set RX/NPTX FIFO sizes */
+	writel(hsotg->g_rx_fifo_sz, hsotg->regs + GRXFSIZ);
+	writel((hsotg->g_rx_fifo_sz << FIFOSIZE_STARTADDR_SHIFT) |
+		(hsotg->g_np_g_tx_fifo_sz << FIFOSIZE_DEPTH_SHIFT),
+		hsotg->regs + GNPTXFSIZ);
 
 
 	/*
 	/*
 	 * arange all the rest of the TX FIFOs, as some versions of this
 	 * arange all the rest of the TX FIFOs, as some versions of this
@@ -183,35 +196,21 @@ static void s3c_hsotg_init_fifo(struct dwc2_hsotg *hsotg)
 	 */
 	 */
 
 
 	/* start at the end of the GNPTXFSIZ, rounded up */
 	/* start at the end of the GNPTXFSIZ, rounded up */
-	addr = 2048 + 1024;
+	addr = hsotg->g_rx_fifo_sz + hsotg->g_np_g_tx_fifo_sz;
 
 
 	/*
 	/*
-	 * Because we have not enough memory to have each TX FIFO of size at
-	 * least 3072 bytes (the maximum single packet size), we create four
-	 * FIFOs of lenght 1024, and four of length 3072 bytes, and assing
+	 * Configure fifos sizes from provided configuration and assign
 	 * them to endpoints dynamically according to maxpacket size value of
 	 * them to endpoints dynamically according to maxpacket size value of
 	 * given endpoint.
 	 * given endpoint.
 	 */
 	 */
-
-	/* 256*4=1024 bytes FIFO length */
-	size = 256;
-	for (ep = 1; ep <= 4; ep++) {
-		val = addr;
-		val |= size << FIFOSIZE_DEPTH_SHIFT;
-		WARN_ONCE(addr + size > hsotg->fifo_mem,
-			  "insufficient fifo memory");
-		addr += size;
-
-		writel(val, hsotg->regs + DPTXFSIZN(ep));
-	}
-	/* 768*4=3072 bytes FIFO length */
-	size = 768;
-	for (ep = 5; ep <= 8; ep++) {
+	for (ep = 1; ep < MAX_EPS_CHANNELS; ep++) {
+		if (!hsotg->g_tx_fifo_sz[ep])
+			continue;
 		val = addr;
 		val = addr;
-		val |= size << FIFOSIZE_DEPTH_SHIFT;
-		WARN_ONCE(addr + size > hsotg->fifo_mem,
+		val |= hsotg->g_tx_fifo_sz[ep] << FIFOSIZE_DEPTH_SHIFT;
+		WARN_ONCE(addr + hsotg->g_tx_fifo_sz[ep] > hsotg->fifo_mem,
 			  "insufficient fifo memory");
 			  "insufficient fifo memory");
-		addr += size;
+		addr += hsotg->g_tx_fifo_sz[ep];
 
 
 		writel(val, hsotg->regs + DPTXFSIZN(ep));
 		writel(val, hsotg->regs + DPTXFSIZN(ep));
 	}
 	}
@@ -236,6 +235,7 @@ static void s3c_hsotg_init_fifo(struct dwc2_hsotg *hsotg)
 			dev_err(hsotg->dev,
 			dev_err(hsotg->dev,
 				"%s: timeout flushing fifos (GRSTCTL=%08x)\n",
 				"%s: timeout flushing fifos (GRSTCTL=%08x)\n",
 				__func__, val);
 				__func__, val);
+			break;
 		}
 		}
 
 
 		udelay(1);
 		udelay(1);
@@ -566,11 +566,6 @@ static void s3c_hsotg_start_req(struct dwc2_hsotg *hsotg,
 	length = ureq->length - ureq->actual;
 	length = ureq->length - ureq->actual;
 	dev_dbg(hsotg->dev, "ureq->length:%d ureq->actual:%d\n",
 	dev_dbg(hsotg->dev, "ureq->length:%d ureq->actual:%d\n",
 		ureq->length, ureq->actual);
 		ureq->length, ureq->actual);
-	if (0)
-		dev_dbg(hsotg->dev,
-			"REQ buf %p len %d dma %pad noi=%d zp=%d snok=%d\n",
-			ureq->buf, length, &ureq->dma,
-			ureq->no_interrupt, ureq->zero, ureq->short_not_ok);
 
 
 	maxreq = get_ep_limit(hs_ep);
 	maxreq = get_ep_limit(hs_ep);
 	if (length > maxreq) {
 	if (length > maxreq) {
@@ -604,14 +599,15 @@ static void s3c_hsotg_start_req(struct dwc2_hsotg *hsotg,
 	else
 	else
 		epsize = 0;
 		epsize = 0;
 
 
-	if (index != 0 && ureq->zero) {
-		/*
-		 * test for the packets being exactly right for the
-		 * transfer
-		 */
-
-		if (length == (packets * hs_ep->ep.maxpacket))
-			packets++;
+	/*
+	 * zero length packet should be programmed on its own and should not
+	 * be counted in DIEPTSIZ.PktCnt with other packets.
+	 */
+	if (dir_in && ureq->zero && !continuing) {
+		/* Test if zlp is actually required. */
+		if ((ureq->length >= hs_ep->ep.maxpacket) &&
+					!(ureq->length % hs_ep->ep.maxpacket))
+			hs_ep->send_zlp = 1;
 	}
 	}
 
 
 	epsize |= DXEPTSIZ_PKTCNT(packets);
 	epsize |= DXEPTSIZ_PKTCNT(packets);
@@ -644,15 +640,12 @@ static void s3c_hsotg_start_req(struct dwc2_hsotg *hsotg,
 	ctrl |= DXEPCTL_EPENA;	/* ensure ep enabled */
 	ctrl |= DXEPCTL_EPENA;	/* ensure ep enabled */
 	ctrl |= DXEPCTL_USBACTEP;
 	ctrl |= DXEPCTL_USBACTEP;
 
 
-	dev_dbg(hsotg->dev, "setup req:%d\n", hsotg->setup);
+	dev_dbg(hsotg->dev, "ep0 state:%d\n", hsotg->ep0_state);
 
 
 	/* For Setup request do not clear NAK */
 	/* For Setup request do not clear NAK */
-	if (hsotg->setup && index == 0)
-		hsotg->setup = 0;
-	else
+	if (!(index == 0 && hsotg->ep0_state == DWC2_EP0_SETUP))
 		ctrl |= DXEPCTL_CNAK;	/* clear NAK set by core */
 		ctrl |= DXEPCTL_CNAK;	/* clear NAK set by core */
 
 
-
 	dev_dbg(hsotg->dev, "%s: DxEPCTL=0x%08x\n", __func__, ctrl);
 	dev_dbg(hsotg->dev, "%s: DxEPCTL=0x%08x\n", __func__, ctrl);
 	writel(ctrl, hsotg->regs + epctrl_reg);
 	writel(ctrl, hsotg->regs + epctrl_reg);
 
 
@@ -686,7 +679,7 @@ static void s3c_hsotg_start_req(struct dwc2_hsotg *hsotg,
 
 
 	/* check ep is enabled */
 	/* check ep is enabled */
 	if (!(readl(hsotg->regs + epctrl_reg) & DXEPCTL_EPENA))
 	if (!(readl(hsotg->regs + epctrl_reg) & DXEPCTL_EPENA))
-		dev_warn(hsotg->dev,
+		dev_dbg(hsotg->dev,
 			 "ep%d: failed to become enabled (DXEPCTL=0x%08x)?\n",
 			 "ep%d: failed to become enabled (DXEPCTL=0x%08x)?\n",
 			 index, readl(hsotg->regs + epctrl_reg));
 			 index, readl(hsotg->regs + epctrl_reg));
 
 
@@ -733,6 +726,59 @@ dma_error:
 	return -EIO;
 	return -EIO;
 }
 }
 
 
+static int s3c_hsotg_handle_unaligned_buf_start(struct dwc2_hsotg *hsotg,
+	struct s3c_hsotg_ep *hs_ep, struct s3c_hsotg_req *hs_req)
+{
+	void *req_buf = hs_req->req.buf;
+
+	/* If dma is not being used or buffer is aligned */
+	if (!using_dma(hsotg) || !((long)req_buf & 3))
+		return 0;
+
+	WARN_ON(hs_req->saved_req_buf);
+
+	dev_dbg(hsotg->dev, "%s: %s: buf=%p length=%d\n", __func__,
+			hs_ep->ep.name, req_buf, hs_req->req.length);
+
+	hs_req->req.buf = kmalloc(hs_req->req.length, GFP_ATOMIC);
+	if (!hs_req->req.buf) {
+		hs_req->req.buf = req_buf;
+		dev_err(hsotg->dev,
+			"%s: unable to allocate memory for bounce buffer\n",
+			__func__);
+		return -ENOMEM;
+	}
+
+	/* Save actual buffer */
+	hs_req->saved_req_buf = req_buf;
+
+	if (hs_ep->dir_in)
+		memcpy(hs_req->req.buf, req_buf, hs_req->req.length);
+	return 0;
+}
+
+static void s3c_hsotg_handle_unaligned_buf_complete(struct dwc2_hsotg *hsotg,
+	struct s3c_hsotg_ep *hs_ep, struct s3c_hsotg_req *hs_req)
+{
+	/* If dma is not being used or buffer was aligned */
+	if (!using_dma(hsotg) || !hs_req->saved_req_buf)
+		return;
+
+	dev_dbg(hsotg->dev, "%s: %s: status=%d actual-length=%d\n", __func__,
+		hs_ep->ep.name, hs_req->req.status, hs_req->req.actual);
+
+	/* Copy data from bounce buffer on successful out transfer */
+	if (!hs_ep->dir_in && !hs_req->req.status)
+		memcpy(hs_req->saved_req_buf, hs_req->req.buf,
+							hs_req->req.actual);
+
+	/* Free bounce buffer */
+	kfree(hs_req->req.buf);
+
+	hs_req->req.buf = hs_req->saved_req_buf;
+	hs_req->saved_req_buf = NULL;
+}
+
 static int s3c_hsotg_ep_queue(struct usb_ep *ep, struct usb_request *req,
 static int s3c_hsotg_ep_queue(struct usb_ep *ep, struct usb_request *req,
 			      gfp_t gfp_flags)
 			      gfp_t gfp_flags)
 {
 {
@@ -740,6 +786,7 @@ static int s3c_hsotg_ep_queue(struct usb_ep *ep, struct usb_request *req,
 	struct s3c_hsotg_ep *hs_ep = our_ep(ep);
 	struct s3c_hsotg_ep *hs_ep = our_ep(ep);
 	struct dwc2_hsotg *hs = hs_ep->parent;
 	struct dwc2_hsotg *hs = hs_ep->parent;
 	bool first;
 	bool first;
+	int ret;
 
 
 	dev_dbg(hs->dev, "%s: req %p: %d@%p, noi=%d, zero=%d, snok=%d\n",
 	dev_dbg(hs->dev, "%s: req %p: %d@%p, noi=%d, zero=%d, snok=%d\n",
 		ep->name, req, req->length, req->buf, req->no_interrupt,
 		ep->name, req, req->length, req->buf, req->no_interrupt,
@@ -750,9 +797,13 @@ static int s3c_hsotg_ep_queue(struct usb_ep *ep, struct usb_request *req,
 	req->actual = 0;
 	req->actual = 0;
 	req->status = -EINPROGRESS;
 	req->status = -EINPROGRESS;
 
 
+	ret = s3c_hsotg_handle_unaligned_buf_start(hs, hs_ep, hs_req);
+	if (ret)
+		return ret;
+
 	/* if we're using DMA, sync the buffers as necessary */
 	/* if we're using DMA, sync the buffers as necessary */
 	if (using_dma(hs)) {
 	if (using_dma(hs)) {
-		int ret = s3c_hsotg_map_dma(hs, hs_ep, req);
+		ret = s3c_hsotg_map_dma(hs, hs_ep, req);
 		if (ret)
 		if (ret)
 			return ret;
 			return ret;
 	}
 	}
@@ -819,7 +870,7 @@ static void s3c_hsotg_complete_oursetup(struct usb_ep *ep,
 static struct s3c_hsotg_ep *ep_from_windex(struct dwc2_hsotg *hsotg,
 static struct s3c_hsotg_ep *ep_from_windex(struct dwc2_hsotg *hsotg,
 					   u32 windex)
 					   u32 windex)
 {
 {
-	struct s3c_hsotg_ep *ep = &hsotg->eps[windex & 0x7F];
+	struct s3c_hsotg_ep *ep;
 	int dir = (windex & USB_DIR_IN) ? 1 : 0;
 	int dir = (windex & USB_DIR_IN) ? 1 : 0;
 	int idx = windex & 0x7F;
 	int idx = windex & 0x7F;
 
 
@@ -829,12 +880,40 @@ static struct s3c_hsotg_ep *ep_from_windex(struct dwc2_hsotg *hsotg,
 	if (idx > hsotg->num_of_eps)
 	if (idx > hsotg->num_of_eps)
 		return NULL;
 		return NULL;
 
 
+	ep = index_to_ep(hsotg, idx, dir);
+
 	if (idx && ep->dir_in != dir)
 	if (idx && ep->dir_in != dir)
 		return NULL;
 		return NULL;
 
 
 	return ep;
 	return ep;
 }
 }
 
 
+/**
+ * s3c_hsotg_set_test_mode - Enable usb Test Modes
+ * @hsotg: The driver state.
+ * @testmode: requested usb test mode
+ * Enable usb Test Mode requested by the Host.
+ */
+static int s3c_hsotg_set_test_mode(struct dwc2_hsotg *hsotg, int testmode)
+{
+	int dctl = readl(hsotg->regs + DCTL);
+
+	dctl &= ~DCTL_TSTCTL_MASK;
+	switch (testmode) {
+	case TEST_J:
+	case TEST_K:
+	case TEST_SE0_NAK:
+	case TEST_PACKET:
+	case TEST_FORCE_EN:
+		dctl |= testmode << DCTL_TSTCTL_SHIFT;
+		break;
+	default:
+		return -EINVAL;
+	}
+	writel(dctl, hsotg->regs + DCTL);
+	return 0;
+}
+
 /**
 /**
  * s3c_hsotg_send_reply - send reply to control request
  * s3c_hsotg_send_reply - send reply to control request
  * @hsotg: The device state
  * @hsotg: The device state
@@ -864,13 +943,15 @@ static int s3c_hsotg_send_reply(struct dwc2_hsotg *hsotg,
 
 
 	req->buf = hsotg->ep0_buff;
 	req->buf = hsotg->ep0_buff;
 	req->length = length;
 	req->length = length;
-	req->zero = 1; /* always do zero-length final transfer */
+	/*
+	 * zero flag is for sending zlp in DATA IN stage. It has no impact on
+	 * STATUS stage.
+	 */
+	req->zero = 0;
 	req->complete = s3c_hsotg_complete_oursetup;
 	req->complete = s3c_hsotg_complete_oursetup;
 
 
 	if (length)
 	if (length)
 		memcpy(req->buf, buff, length);
 		memcpy(req->buf, buff, length);
-	else
-		ep->sent_zlp = 1;
 
 
 	ret = s3c_hsotg_ep_queue(&ep->ep, req, GFP_ATOMIC);
 	ret = s3c_hsotg_ep_queue(&ep->ep, req, GFP_ATOMIC);
 	if (ret) {
 	if (ret) {
@@ -889,7 +970,7 @@ static int s3c_hsotg_send_reply(struct dwc2_hsotg *hsotg,
 static int s3c_hsotg_process_req_status(struct dwc2_hsotg *hsotg,
 static int s3c_hsotg_process_req_status(struct dwc2_hsotg *hsotg,
 					struct usb_ctrlrequest *ctrl)
 					struct usb_ctrlrequest *ctrl)
 {
 {
-	struct s3c_hsotg_ep *ep0 = &hsotg->eps[0];
+	struct s3c_hsotg_ep *ep0 = hsotg->eps_out[0];
 	struct s3c_hsotg_ep *ep;
 	struct s3c_hsotg_ep *ep;
 	__le16 reply;
 	__le16 reply;
 	int ret;
 	int ret;
@@ -953,33 +1034,62 @@ static struct s3c_hsotg_req *get_ep_head(struct s3c_hsotg_ep *hs_ep)
 }
 }
 
 
 /**
 /**
- * s3c_hsotg_process_req_featire - process request {SET,CLEAR}_FEATURE
+ * s3c_hsotg_process_req_feature - process request {SET,CLEAR}_FEATURE
  * @hsotg: The device state
  * @hsotg: The device state
  * @ctrl: USB control request
  * @ctrl: USB control request
  */
  */
 static int s3c_hsotg_process_req_feature(struct dwc2_hsotg *hsotg,
 static int s3c_hsotg_process_req_feature(struct dwc2_hsotg *hsotg,
 					 struct usb_ctrlrequest *ctrl)
 					 struct usb_ctrlrequest *ctrl)
 {
 {
-	struct s3c_hsotg_ep *ep0 = &hsotg->eps[0];
+	struct s3c_hsotg_ep *ep0 = hsotg->eps_out[0];
 	struct s3c_hsotg_req *hs_req;
 	struct s3c_hsotg_req *hs_req;
 	bool restart;
 	bool restart;
 	bool set = (ctrl->bRequest == USB_REQ_SET_FEATURE);
 	bool set = (ctrl->bRequest == USB_REQ_SET_FEATURE);
 	struct s3c_hsotg_ep *ep;
 	struct s3c_hsotg_ep *ep;
 	int ret;
 	int ret;
 	bool halted;
 	bool halted;
+	u32 recip;
+	u32 wValue;
+	u32 wIndex;
 
 
 	dev_dbg(hsotg->dev, "%s: %s_FEATURE\n",
 	dev_dbg(hsotg->dev, "%s: %s_FEATURE\n",
 		__func__, set ? "SET" : "CLEAR");
 		__func__, set ? "SET" : "CLEAR");
 
 
-	if (ctrl->bRequestType == USB_RECIP_ENDPOINT) {
-		ep = ep_from_windex(hsotg, le16_to_cpu(ctrl->wIndex));
+	wValue = le16_to_cpu(ctrl->wValue);
+	wIndex = le16_to_cpu(ctrl->wIndex);
+	recip = ctrl->bRequestType & USB_RECIP_MASK;
+
+	switch (recip) {
+	case USB_RECIP_DEVICE:
+		switch (wValue) {
+		case USB_DEVICE_TEST_MODE:
+			if ((wIndex & 0xff) != 0)
+				return -EINVAL;
+			if (!set)
+				return -EINVAL;
+
+			hsotg->test_mode = wIndex >> 8;
+			ret = s3c_hsotg_send_reply(hsotg, ep0, NULL, 0);
+			if (ret) {
+				dev_err(hsotg->dev,
+					"%s: failed to send reply\n", __func__);
+				return ret;
+			}
+			break;
+		default:
+			return -ENOENT;
+		}
+		break;
+
+	case USB_RECIP_ENDPOINT:
+		ep = ep_from_windex(hsotg, wIndex);
 		if (!ep) {
 		if (!ep) {
 			dev_dbg(hsotg->dev, "%s: no endpoint for 0x%04x\n",
 			dev_dbg(hsotg->dev, "%s: no endpoint for 0x%04x\n",
-				__func__, le16_to_cpu(ctrl->wIndex));
+				__func__, wIndex);
 			return -ENOENT;
 			return -ENOENT;
 		}
 		}
 
 
-		switch (le16_to_cpu(ctrl->wValue)) {
+		switch (wValue) {
 		case USB_ENDPOINT_HALT:
 		case USB_ENDPOINT_HALT:
 			halted = ep->halted;
 			halted = ep->halted;
 
 
@@ -1006,16 +1116,22 @@ static int s3c_hsotg_process_req_feature(struct dwc2_hsotg *hsotg,
 					hs_req = ep->req;
 					hs_req = ep->req;
 					ep->req = NULL;
 					ep->req = NULL;
 					list_del_init(&hs_req->queue);
 					list_del_init(&hs_req->queue);
-					usb_gadget_giveback_request(&ep->ep,
-								    &hs_req->req);
+					if (hs_req->req.complete) {
+						spin_unlock(&hsotg->lock);
+						usb_gadget_giveback_request(
+							&ep->ep, &hs_req->req);
+						spin_lock(&hsotg->lock);
+					}
 				}
 				}
 
 
 				/* If we have pending request, then start it */
 				/* If we have pending request, then start it */
-				restart = !list_empty(&ep->queue);
-				if (restart) {
-					hs_req = get_ep_head(ep);
-					s3c_hsotg_start_req(hsotg, ep,
-							    hs_req, false);
+				if (!ep->req) {
+					restart = !list_empty(&ep->queue);
+					if (restart) {
+						hs_req = get_ep_head(ep);
+						s3c_hsotg_start_req(hsotg, ep,
+								hs_req, false);
+					}
 				}
 				}
 			}
 			}
 
 
@@ -1024,9 +1140,10 @@ static int s3c_hsotg_process_req_feature(struct dwc2_hsotg *hsotg,
 		default:
 		default:
 			return -ENOENT;
 			return -ENOENT;
 		}
 		}
-	} else
-		return -ENOENT;  /* currently only deal with endpoint */
-
+		break;
+	default:
+		return -ENOENT;
+	}
 	return 1;
 	return 1;
 }
 }
 
 
@@ -1040,7 +1157,7 @@ static void s3c_hsotg_enqueue_setup(struct dwc2_hsotg *hsotg);
  */
  */
 static void s3c_hsotg_stall_ep0(struct dwc2_hsotg *hsotg)
 static void s3c_hsotg_stall_ep0(struct dwc2_hsotg *hsotg)
 {
 {
-	struct s3c_hsotg_ep *ep0 = &hsotg->eps[0];
+	struct s3c_hsotg_ep *ep0 = hsotg->eps_out[0];
 	u32 reg;
 	u32 reg;
 	u32 ctrl;
 	u32 ctrl;
 
 
@@ -1080,34 +1197,29 @@ static void s3c_hsotg_stall_ep0(struct dwc2_hsotg *hsotg)
 static void s3c_hsotg_process_control(struct dwc2_hsotg *hsotg,
 static void s3c_hsotg_process_control(struct dwc2_hsotg *hsotg,
 				      struct usb_ctrlrequest *ctrl)
 				      struct usb_ctrlrequest *ctrl)
 {
 {
-	struct s3c_hsotg_ep *ep0 = &hsotg->eps[0];
+	struct s3c_hsotg_ep *ep0 = hsotg->eps_out[0];
 	int ret = 0;
 	int ret = 0;
 	u32 dcfg;
 	u32 dcfg;
 
 
-	ep0->sent_zlp = 0;
-
 	dev_dbg(hsotg->dev, "ctrl Req=%02x, Type=%02x, V=%04x, L=%04x\n",
 	dev_dbg(hsotg->dev, "ctrl Req=%02x, Type=%02x, V=%04x, L=%04x\n",
 		 ctrl->bRequest, ctrl->bRequestType,
 		 ctrl->bRequest, ctrl->bRequestType,
 		 ctrl->wValue, ctrl->wLength);
 		 ctrl->wValue, ctrl->wLength);
 
 
-	/*
-	 * record the direction of the request, for later use when enquing
-	 * packets onto EP0.
-	 */
-
-	ep0->dir_in = (ctrl->bRequestType & USB_DIR_IN) ? 1 : 0;
-	dev_dbg(hsotg->dev, "ctrl: dir_in=%d\n", ep0->dir_in);
-
-	/*
-	 * if we've no data with this request, then the last part of the
-	 * transaction is going to implicitly be IN.
-	 */
-	if (ctrl->wLength == 0)
+	if (ctrl->wLength == 0) {
 		ep0->dir_in = 1;
 		ep0->dir_in = 1;
+		hsotg->ep0_state = DWC2_EP0_STATUS_IN;
+	} else if (ctrl->bRequestType & USB_DIR_IN) {
+		ep0->dir_in = 1;
+		hsotg->ep0_state = DWC2_EP0_DATA_IN;
+	} else {
+		ep0->dir_in = 0;
+		hsotg->ep0_state = DWC2_EP0_DATA_OUT;
+	}
 
 
 	if ((ctrl->bRequestType & USB_TYPE_MASK) == USB_TYPE_STANDARD) {
 	if ((ctrl->bRequestType & USB_TYPE_MASK) == USB_TYPE_STANDARD) {
 		switch (ctrl->bRequest) {
 		switch (ctrl->bRequest) {
 		case USB_REQ_SET_ADDRESS:
 		case USB_REQ_SET_ADDRESS:
+			hsotg->connected = 1;
 			dcfg = readl(hsotg->regs + DCFG);
 			dcfg = readl(hsotg->regs + DCFG);
 			dcfg &= ~DCFG_DEVADDR_MASK;
 			dcfg &= ~DCFG_DEVADDR_MASK;
 			dcfg |= (le16_to_cpu(ctrl->wValue) <<
 			dcfg |= (le16_to_cpu(ctrl->wValue) <<
@@ -1201,9 +1313,11 @@ static void s3c_hsotg_enqueue_setup(struct dwc2_hsotg *hsotg)
 		return;
 		return;
 	}
 	}
 
 
-	hsotg->eps[0].dir_in = 0;
+	hsotg->eps_out[0]->dir_in = 0;
+	hsotg->eps_out[0]->send_zlp = 0;
+	hsotg->ep0_state = DWC2_EP0_SETUP;
 
 
-	ret = s3c_hsotg_ep_queue(&hsotg->eps[0].ep, req, GFP_ATOMIC);
+	ret = s3c_hsotg_ep_queue(&hsotg->eps_out[0]->ep, req, GFP_ATOMIC);
 	if (ret < 0) {
 	if (ret < 0) {
 		dev_err(hsotg->dev, "%s: failed queue (%d)\n", __func__, ret);
 		dev_err(hsotg->dev, "%s: failed queue (%d)\n", __func__, ret);
 		/*
 		/*
@@ -1213,6 +1327,32 @@ static void s3c_hsotg_enqueue_setup(struct dwc2_hsotg *hsotg)
 	}
 	}
 }
 }
 
 
+static void s3c_hsotg_program_zlp(struct dwc2_hsotg *hsotg,
+					struct s3c_hsotg_ep *hs_ep)
+{
+	u32 ctrl;
+	u8 index = hs_ep->index;
+	u32 epctl_reg = hs_ep->dir_in ? DIEPCTL(index) : DOEPCTL(index);
+	u32 epsiz_reg = hs_ep->dir_in ? DIEPTSIZ(index) : DOEPTSIZ(index);
+
+	if (hs_ep->dir_in)
+		dev_dbg(hsotg->dev, "Sending zero-length packet on ep%d\n",
+									index);
+	else
+		dev_dbg(hsotg->dev, "Receiving zero-length packet on ep%d\n",
+									index);
+
+	writel(DXEPTSIZ_MC(1) | DXEPTSIZ_PKTCNT(1) |
+			DXEPTSIZ_XFERSIZE(0), hsotg->regs +
+			epsiz_reg);
+
+	ctrl = readl(hsotg->regs + epctl_reg);
+	ctrl |= DXEPCTL_CNAK;  /* clear NAK set by core */
+	ctrl |= DXEPCTL_EPENA; /* ensure ep enabled */
+	ctrl |= DXEPCTL_USBACTEP;
+	writel(ctrl, hsotg->regs + epctl_reg);
+}
+
 /**
 /**
  * s3c_hsotg_complete_request - complete a request given to us
  * s3c_hsotg_complete_request - complete a request given to us
  * @hsotg: The device state.
  * @hsotg: The device state.
@@ -1249,6 +1389,8 @@ static void s3c_hsotg_complete_request(struct dwc2_hsotg *hsotg,
 	if (hs_req->req.status == -EINPROGRESS)
 	if (hs_req->req.status == -EINPROGRESS)
 		hs_req->req.status = result;
 		hs_req->req.status = result;
 
 
+	s3c_hsotg_handle_unaligned_buf_complete(hsotg, hs_ep, hs_req);
+
 	hs_ep->req = NULL;
 	hs_ep->req = NULL;
 	list_del_init(&hs_req->queue);
 	list_del_init(&hs_req->queue);
 
 
@@ -1293,7 +1435,7 @@ static void s3c_hsotg_complete_request(struct dwc2_hsotg *hsotg,
  */
  */
 static void s3c_hsotg_rx_data(struct dwc2_hsotg *hsotg, int ep_idx, int size)
 static void s3c_hsotg_rx_data(struct dwc2_hsotg *hsotg, int ep_idx, int size)
 {
 {
-	struct s3c_hsotg_ep *hs_ep = &hsotg->eps[ep_idx];
+	struct s3c_hsotg_ep *hs_ep = hsotg->eps_out[ep_idx];
 	struct s3c_hsotg_req *hs_req = hs_ep->req;
 	struct s3c_hsotg_req *hs_req = hs_ep->req;
 	void __iomem *fifo = hsotg->regs + EPFIFO(ep_idx);
 	void __iomem *fifo = hsotg->regs + EPFIFO(ep_idx);
 	int to_read;
 	int to_read;
@@ -1305,7 +1447,7 @@ static void s3c_hsotg_rx_data(struct dwc2_hsotg *hsotg, int ep_idx, int size)
 		u32 epctl = readl(hsotg->regs + DOEPCTL(ep_idx));
 		u32 epctl = readl(hsotg->regs + DOEPCTL(ep_idx));
 		int ptr;
 		int ptr;
 
 
-		dev_warn(hsotg->dev,
+		dev_dbg(hsotg->dev,
 			 "%s: FIFO %d bytes on ep%d but no req (DXEPCTl=0x%08x)\n",
 			 "%s: FIFO %d bytes on ep%d but no req (DXEPCTl=0x%08x)\n",
 			 __func__, size, ep_idx, epctl);
 			 __func__, size, ep_idx, epctl);
 
 
@@ -1345,9 +1487,9 @@ static void s3c_hsotg_rx_data(struct dwc2_hsotg *hsotg, int ep_idx, int size)
 }
 }
 
 
 /**
 /**
- * s3c_hsotg_send_zlp - send zero-length packet on control endpoint
+ * s3c_hsotg_ep0_zlp - send/receive zero-length packet on control endpoint
  * @hsotg: The device instance
  * @hsotg: The device instance
- * @req: The request currently on this endpoint
+ * @dir_in: If IN zlp
  *
  *
  * Generate a zero-length IN packet request for terminating a SETUP
  * Generate a zero-length IN packet request for terminating a SETUP
  * transaction.
  * transaction.
@@ -1356,53 +1498,28 @@ static void s3c_hsotg_rx_data(struct dwc2_hsotg *hsotg, int ep_idx, int size)
  * currently believed that we do not need to wait for any space in
  * currently believed that we do not need to wait for any space in
  * the TxFIFO.
  * the TxFIFO.
  */
  */
-static void s3c_hsotg_send_zlp(struct dwc2_hsotg *hsotg,
-			       struct s3c_hsotg_req *req)
+static void s3c_hsotg_ep0_zlp(struct dwc2_hsotg *hsotg, bool dir_in)
 {
 {
-	u32 ctrl;
+	/* eps_out[0] is used in both directions */
+	hsotg->eps_out[0]->dir_in = dir_in;
+	hsotg->ep0_state = dir_in ? DWC2_EP0_STATUS_IN : DWC2_EP0_STATUS_OUT;
 
 
-	if (!req) {
-		dev_warn(hsotg->dev, "%s: no request?\n", __func__);
-		return;
-	}
-
-	if (req->req.length == 0) {
-		hsotg->eps[0].sent_zlp = 1;
-		s3c_hsotg_enqueue_setup(hsotg);
-		return;
-	}
-
-	hsotg->eps[0].dir_in = 1;
-	hsotg->eps[0].sent_zlp = 1;
-
-	dev_dbg(hsotg->dev, "sending zero-length packet\n");
-
-	/* issue a zero-sized packet to terminate this */
-	writel(DXEPTSIZ_MC(1) | DXEPTSIZ_PKTCNT(1) |
-	       DXEPTSIZ_XFERSIZE(0), hsotg->regs + DIEPTSIZ(0));
-
-	ctrl = readl(hsotg->regs + DIEPCTL0);
-	ctrl |= DXEPCTL_CNAK;  /* clear NAK set by core */
-	ctrl |= DXEPCTL_EPENA; /* ensure ep enabled */
-	ctrl |= DXEPCTL_USBACTEP;
-	writel(ctrl, hsotg->regs + DIEPCTL0);
+	s3c_hsotg_program_zlp(hsotg, hsotg->eps_out[0]);
 }
 }
 
 
 /**
 /**
  * s3c_hsotg_handle_outdone - handle receiving OutDone/SetupDone from RXFIFO
  * s3c_hsotg_handle_outdone - handle receiving OutDone/SetupDone from RXFIFO
  * @hsotg: The device instance
  * @hsotg: The device instance
  * @epnum: The endpoint received from
  * @epnum: The endpoint received from
- * @was_setup: Set if processing a SetupDone event.
  *
  *
  * The RXFIFO has delivered an OutDone event, which means that the data
  * The RXFIFO has delivered an OutDone event, which means that the data
  * transfer for an OUT endpoint has been completed, either by a short
  * transfer for an OUT endpoint has been completed, either by a short
  * packet or by the finish of a transfer.
  * packet or by the finish of a transfer.
  */
  */
-static void s3c_hsotg_handle_outdone(struct dwc2_hsotg *hsotg,
-				     int epnum, bool was_setup)
+static void s3c_hsotg_handle_outdone(struct dwc2_hsotg *hsotg, int epnum)
 {
 {
 	u32 epsize = readl(hsotg->regs + DOEPTSIZ(epnum));
 	u32 epsize = readl(hsotg->regs + DOEPTSIZ(epnum));
-	struct s3c_hsotg_ep *hs_ep = &hsotg->eps[epnum];
+	struct s3c_hsotg_ep *hs_ep = hsotg->eps_out[epnum];
 	struct s3c_hsotg_req *hs_req = hs_ep->req;
 	struct s3c_hsotg_req *hs_req = hs_ep->req;
 	struct usb_request *req = &hs_req->req;
 	struct usb_request *req = &hs_req->req;
 	unsigned size_left = DXEPTSIZ_XFERSIZE_GET(epsize);
 	unsigned size_left = DXEPTSIZ_XFERSIZE_GET(epsize);
@@ -1413,6 +1530,13 @@ static void s3c_hsotg_handle_outdone(struct dwc2_hsotg *hsotg,
 		return;
 		return;
 	}
 	}
 
 
+	if (epnum == 0 && hsotg->ep0_state == DWC2_EP0_STATUS_OUT) {
+		dev_dbg(hsotg->dev, "zlp packet received\n");
+		s3c_hsotg_complete_request(hsotg, hs_ep, hs_req, 0);
+		s3c_hsotg_enqueue_setup(hsotg);
+		return;
+	}
+
 	if (using_dma(hsotg)) {
 	if (using_dma(hsotg)) {
 		unsigned size_done;
 		unsigned size_done;
 
 
@@ -1435,12 +1559,6 @@ static void s3c_hsotg_handle_outdone(struct dwc2_hsotg *hsotg,
 	if (req->actual < req->length && size_left == 0) {
 	if (req->actual < req->length && size_left == 0) {
 		s3c_hsotg_start_req(hsotg, hs_ep, hs_req, true);
 		s3c_hsotg_start_req(hsotg, hs_ep, hs_req, true);
 		return;
 		return;
-	} else if (epnum == 0) {
-		/*
-		 * After was_setup = 1 =>
-		 * set CNAK for non Setup requests
-		 */
-		hsotg->setup = was_setup ? 0 : 1;
 	}
 	}
 
 
 	if (req->actual < req->length && req->short_not_ok) {
 	if (req->actual < req->length && req->short_not_ok) {
@@ -1453,13 +1571,10 @@ static void s3c_hsotg_handle_outdone(struct dwc2_hsotg *hsotg,
 		 */
 		 */
 	}
 	}
 
 
-	if (epnum == 0) {
-		/*
-		 * Condition req->complete != s3c_hsotg_complete_setup says:
-		 * send ZLP when we have an asynchronous request from gadget
-		 */
-		if (!was_setup && req->complete != s3c_hsotg_complete_setup)
-			s3c_hsotg_send_zlp(hsotg, hs_req);
+	if (epnum == 0 && hsotg->ep0_state == DWC2_EP0_DATA_OUT) {
+		/* Move to STATUS IN */
+		s3c_hsotg_ep0_zlp(hsotg, true);
+		return;
 	}
 	}
 
 
 	s3c_hsotg_complete_request(hsotg, hs_ep, hs_req, result);
 	s3c_hsotg_complete_request(hsotg, hs_ep, hs_req, result);
@@ -1511,8 +1626,7 @@ static void s3c_hsotg_handle_rx(struct dwc2_hsotg *hsotg)
 	size = grxstsr & GRXSTS_BYTECNT_MASK;
 	size = grxstsr & GRXSTS_BYTECNT_MASK;
 	size >>= GRXSTS_BYTECNT_SHIFT;
 	size >>= GRXSTS_BYTECNT_SHIFT;
 
 
-	if (1)
-		dev_dbg(hsotg->dev, "%s: GRXSTSP=0x%08x (%d@%d)\n",
+	dev_dbg(hsotg->dev, "%s: GRXSTSP=0x%08x (%d@%d)\n",
 			__func__, grxstsr, size, epnum);
 			__func__, grxstsr, size, epnum);
 
 
 	switch ((status & GRXSTS_PKTSTS_MASK) >> GRXSTS_PKTSTS_SHIFT) {
 	switch ((status & GRXSTS_PKTSTS_MASK) >> GRXSTS_PKTSTS_SHIFT) {
@@ -1525,7 +1639,7 @@ static void s3c_hsotg_handle_rx(struct dwc2_hsotg *hsotg)
 			s3c_hsotg_read_frameno(hsotg));
 			s3c_hsotg_read_frameno(hsotg));
 
 
 		if (!using_dma(hsotg))
 		if (!using_dma(hsotg))
-			s3c_hsotg_handle_outdone(hsotg, epnum, false);
+			s3c_hsotg_handle_outdone(hsotg, epnum);
 		break;
 		break;
 
 
 	case GRXSTS_PKTSTS_SETUPDONE:
 	case GRXSTS_PKTSTS_SETUPDONE:
@@ -1533,8 +1647,13 @@ static void s3c_hsotg_handle_rx(struct dwc2_hsotg *hsotg)
 			"SetupDone (Frame=0x%08x, DOPEPCTL=0x%08x)\n",
 			"SetupDone (Frame=0x%08x, DOPEPCTL=0x%08x)\n",
 			s3c_hsotg_read_frameno(hsotg),
 			s3c_hsotg_read_frameno(hsotg),
 			readl(hsotg->regs + DOEPCTL(0)));
 			readl(hsotg->regs + DOEPCTL(0)));
-
-		s3c_hsotg_handle_outdone(hsotg, epnum, true);
+		/*
+		 * Call s3c_hsotg_handle_outdone here if it was not called from
+		 * GRXSTS_PKTSTS_OUTDONE. That is, if the core didn't
+		 * generate GRXSTS_PKTSTS_OUTDONE for setup packet.
+		 */
+		if (hsotg->ep0_state == DWC2_EP0_SETUP)
+			s3c_hsotg_handle_outdone(hsotg, epnum);
 		break;
 		break;
 
 
 	case GRXSTS_PKTSTS_OUTRX:
 	case GRXSTS_PKTSTS_OUTRX:
@@ -1547,6 +1666,8 @@ static void s3c_hsotg_handle_rx(struct dwc2_hsotg *hsotg)
 			s3c_hsotg_read_frameno(hsotg),
 			s3c_hsotg_read_frameno(hsotg),
 			readl(hsotg->regs + DOEPCTL(0)));
 			readl(hsotg->regs + DOEPCTL(0)));
 
 
+		WARN_ON(hsotg->ep0_state != DWC2_EP0_SETUP);
+
 		s3c_hsotg_rx_data(hsotg, epnum, size);
 		s3c_hsotg_rx_data(hsotg, epnum, size);
 		break;
 		break;
 
 
@@ -1591,14 +1712,18 @@ static u32 s3c_hsotg_ep0_mps(unsigned int mps)
  * the hardware control registers to reflect this.
  * the hardware control registers to reflect this.
  */
  */
 static void s3c_hsotg_set_ep_maxpacket(struct dwc2_hsotg *hsotg,
 static void s3c_hsotg_set_ep_maxpacket(struct dwc2_hsotg *hsotg,
-				       unsigned int ep, unsigned int mps)
+			unsigned int ep, unsigned int mps, unsigned int dir_in)
 {
 {
-	struct s3c_hsotg_ep *hs_ep = &hsotg->eps[ep];
+	struct s3c_hsotg_ep *hs_ep;
 	void __iomem *regs = hsotg->regs;
 	void __iomem *regs = hsotg->regs;
 	u32 mpsval;
 	u32 mpsval;
 	u32 mcval;
 	u32 mcval;
 	u32 reg;
 	u32 reg;
 
 
+	hs_ep = index_to_ep(hsotg, ep, dir_in);
+	if (!hs_ep)
+		return;
+
 	if (ep == 0) {
 	if (ep == 0) {
 		/* EP0 is a special case */
 		/* EP0 is a special case */
 		mpsval = s3c_hsotg_ep0_mps(mps);
 		mpsval = s3c_hsotg_ep0_mps(mps);
@@ -1617,17 +1742,12 @@ static void s3c_hsotg_set_ep_maxpacket(struct dwc2_hsotg *hsotg,
 		hs_ep->ep.maxpacket = mpsval;
 		hs_ep->ep.maxpacket = mpsval;
 	}
 	}
 
 
-	/*
-	 * update both the in and out endpoint controldir_ registers, even
-	 * if one of the directions may not be in use.
-	 */
-
-	reg = readl(regs + DIEPCTL(ep));
-	reg &= ~DXEPCTL_MPS_MASK;
-	reg |= mpsval;
-	writel(reg, regs + DIEPCTL(ep));
-
-	if (ep) {
+	if (dir_in) {
+		reg = readl(regs + DIEPCTL(ep));
+		reg &= ~DXEPCTL_MPS_MASK;
+		reg |= mpsval;
+		writel(reg, regs + DIEPCTL(ep));
+	} else {
 		reg = readl(regs + DOEPCTL(ep));
 		reg = readl(regs + DOEPCTL(ep));
 		reg &= ~DXEPCTL_MPS_MASK;
 		reg &= ~DXEPCTL_MPS_MASK;
 		reg |= mpsval;
 		reg |= mpsval;
@@ -1727,9 +1847,21 @@ static void s3c_hsotg_complete_in(struct dwc2_hsotg *hsotg,
 	}
 	}
 
 
 	/* Finish ZLP handling for IN EP0 transactions */
 	/* Finish ZLP handling for IN EP0 transactions */
-	if (hsotg->eps[0].sent_zlp) {
-		dev_dbg(hsotg->dev, "zlp packet received\n");
+	if (hs_ep->index == 0 && hsotg->ep0_state == DWC2_EP0_STATUS_IN) {
+		dev_dbg(hsotg->dev, "zlp packet sent\n");
 		s3c_hsotg_complete_request(hsotg, hs_ep, hs_req, 0);
 		s3c_hsotg_complete_request(hsotg, hs_ep, hs_req, 0);
+		if (hsotg->test_mode) {
+			int ret;
+
+			ret = s3c_hsotg_set_test_mode(hsotg, hsotg->test_mode);
+			if (ret < 0) {
+				dev_dbg(hsotg->dev, "Invalid Test #%d\n",
+						hsotg->test_mode);
+				s3c_hsotg_stall_ep0(hsotg);
+				return;
+			}
+		}
+		s3c_hsotg_enqueue_setup(hsotg);
 		return;
 		return;
 	}
 	}
 
 
@@ -1756,31 +1888,27 @@ static void s3c_hsotg_complete_in(struct dwc2_hsotg *hsotg,
 	dev_dbg(hsotg->dev, "req->length:%d req->actual:%d req->zero:%d\n",
 	dev_dbg(hsotg->dev, "req->length:%d req->actual:%d req->zero:%d\n",
 		hs_req->req.length, hs_req->req.actual, hs_req->req.zero);
 		hs_req->req.length, hs_req->req.actual, hs_req->req.zero);
 
 
-	/*
-	 * Check if dealing with Maximum Packet Size(MPS) IN transfer at EP0
-	 * When sent data is a multiple MPS size (e.g. 64B ,128B ,192B
-	 * ,256B ... ), after last MPS sized packet send IN ZLP packet to
-	 * inform the host that no more data is available.
-	 * The state of req.zero member is checked to be sure that the value to
-	 * send is smaller than wValue expected from host.
-	 * Check req.length to NOT send another ZLP when the current one is
-	 * under completion (the one for which this completion has been called).
-	 */
-	if (hs_req->req.length && hs_ep->index == 0 && hs_req->req.zero &&
-	    hs_req->req.length == hs_req->req.actual &&
-	    !(hs_req->req.length % hs_ep->ep.maxpacket)) {
+	if (!size_left && hs_req->req.actual < hs_req->req.length) {
+		dev_dbg(hsotg->dev, "%s trying more for req...\n", __func__);
+		s3c_hsotg_start_req(hsotg, hs_ep, hs_req, true);
+		return;
+	}
 
 
-		dev_dbg(hsotg->dev, "ep0 zlp IN packet sent\n");
-		s3c_hsotg_send_zlp(hsotg, hs_req);
+	/* Zlp for all endpoints, for ep0 only in DATA IN stage */
+	if (hs_ep->send_zlp) {
+		s3c_hsotg_program_zlp(hsotg, hs_ep);
+		hs_ep->send_zlp = 0;
+		/* transfer will be completed on next complete interrupt */
+		return;
+	}
 
 
+	if (hs_ep->index == 0 && hsotg->ep0_state == DWC2_EP0_DATA_IN) {
+		/* Move to STATUS OUT */
+		s3c_hsotg_ep0_zlp(hsotg, false);
 		return;
 		return;
 	}
 	}
 
 
-	if (!size_left && hs_req->req.actual < hs_req->req.length) {
-		dev_dbg(hsotg->dev, "%s trying more for req...\n", __func__);
-		s3c_hsotg_start_req(hsotg, hs_ep, hs_req, true);
-	} else
-		s3c_hsotg_complete_request(hsotg, hs_ep, hs_req, 0);
+	s3c_hsotg_complete_request(hsotg, hs_ep, hs_req, 0);
 }
 }
 
 
 /**
 /**
@@ -1794,7 +1922,7 @@ static void s3c_hsotg_complete_in(struct dwc2_hsotg *hsotg,
 static void s3c_hsotg_epint(struct dwc2_hsotg *hsotg, unsigned int idx,
 static void s3c_hsotg_epint(struct dwc2_hsotg *hsotg, unsigned int idx,
 			    int dir_in)
 			    int dir_in)
 {
 {
-	struct s3c_hsotg_ep *hs_ep = &hsotg->eps[idx];
+	struct s3c_hsotg_ep *hs_ep = index_to_ep(hsotg, idx, dir_in);
 	u32 epint_reg = dir_in ? DIEPINT(idx) : DOEPINT(idx);
 	u32 epint_reg = dir_in ? DIEPINT(idx) : DOEPINT(idx);
 	u32 epctl_reg = dir_in ? DIEPCTL(idx) : DOEPCTL(idx);
 	u32 epctl_reg = dir_in ? DIEPCTL(idx) : DOEPCTL(idx);
 	u32 epsiz_reg = dir_in ? DIEPTSIZ(idx) : DOEPTSIZ(idx);
 	u32 epsiz_reg = dir_in ? DIEPTSIZ(idx) : DOEPTSIZ(idx);
@@ -1807,9 +1935,19 @@ static void s3c_hsotg_epint(struct dwc2_hsotg *hsotg, unsigned int idx,
 	/* Clear endpoint interrupts */
 	/* Clear endpoint interrupts */
 	writel(ints, hsotg->regs + epint_reg);
 	writel(ints, hsotg->regs + epint_reg);
 
 
+	if (!hs_ep) {
+		dev_err(hsotg->dev, "%s:Interrupt for unconfigured ep%d(%s)\n",
+					__func__, idx, dir_in ? "in" : "out");
+		return;
+	}
+
 	dev_dbg(hsotg->dev, "%s: ep%d(%s) DxEPINT=0x%08x\n",
 	dev_dbg(hsotg->dev, "%s: ep%d(%s) DxEPINT=0x%08x\n",
 		__func__, idx, dir_in ? "in" : "out", ints);
 		__func__, idx, dir_in ? "in" : "out", ints);
 
 
+	/* Don't process XferCompl interrupt if it is a setup packet */
+	if (idx == 0 && (ints & (DXEPINT_SETUP | DXEPINT_SETUP_RCVD)))
+		ints &= ~DXEPINT_XFERCOMPL;
+
 	if (ints & DXEPINT_XFERCOMPL) {
 	if (ints & DXEPINT_XFERCOMPL) {
 		if (hs_ep->isochronous && hs_ep->interval == 1) {
 		if (hs_ep->isochronous && hs_ep->interval == 1) {
 			if (ctrl & DXEPCTL_EOFRNUM)
 			if (ctrl & DXEPCTL_EOFRNUM)
@@ -1839,7 +1977,7 @@ static void s3c_hsotg_epint(struct dwc2_hsotg *hsotg, unsigned int idx,
 			 * as we ignore the RXFIFO.
 			 * as we ignore the RXFIFO.
 			 */
 			 */
 
 
-			s3c_hsotg_handle_outdone(hsotg, idx, false);
+			s3c_hsotg_handle_outdone(hsotg, idx);
 		}
 		}
 	}
 	}
 
 
@@ -1878,7 +2016,7 @@ static void s3c_hsotg_epint(struct dwc2_hsotg *hsotg, unsigned int idx,
 			if (dir_in)
 			if (dir_in)
 				WARN_ON_ONCE(1);
 				WARN_ON_ONCE(1);
 			else
 			else
-				s3c_hsotg_handle_outdone(hsotg, 0, true);
+				s3c_hsotg_handle_outdone(hsotg, 0);
 		}
 		}
 	}
 	}
 
 
@@ -1969,9 +2107,15 @@ static void s3c_hsotg_irq_enumdone(struct dwc2_hsotg *hsotg)
 
 
 	if (ep0_mps) {
 	if (ep0_mps) {
 		int i;
 		int i;
-		s3c_hsotg_set_ep_maxpacket(hsotg, 0, ep0_mps);
-		for (i = 1; i < hsotg->num_of_eps; i++)
-			s3c_hsotg_set_ep_maxpacket(hsotg, i, ep_mps);
+		/* Initialize ep0 for both in and out directions */
+		s3c_hsotg_set_ep_maxpacket(hsotg, 0, ep0_mps, 1);
+		s3c_hsotg_set_ep_maxpacket(hsotg, 0, ep0_mps, 0);
+		for (i = 1; i < hsotg->num_of_eps; i++) {
+			if (hsotg->eps_in[i])
+				s3c_hsotg_set_ep_maxpacket(hsotg, i, ep_mps, 1);
+			if (hsotg->eps_out[i])
+				s3c_hsotg_set_ep_maxpacket(hsotg, i, ep_mps, 0);
+		}
 	}
 	}
 
 
 	/* ensure after enumeration our EP0 is active */
 	/* ensure after enumeration our EP0 is active */
@@ -1988,30 +2132,23 @@ static void s3c_hsotg_irq_enumdone(struct dwc2_hsotg *hsotg)
  * @hsotg: The device state.
  * @hsotg: The device state.
  * @ep: The endpoint the requests may be on.
  * @ep: The endpoint the requests may be on.
  * @result: The result code to use.
  * @result: The result code to use.
- * @force: Force removal of any current requests
  *
  *
  * Go through the requests on the given endpoint and mark them
  * Go through the requests on the given endpoint and mark them
  * completed with the given result code.
  * completed with the given result code.
  */
  */
 static void kill_all_requests(struct dwc2_hsotg *hsotg,
 static void kill_all_requests(struct dwc2_hsotg *hsotg,
 			      struct s3c_hsotg_ep *ep,
 			      struct s3c_hsotg_ep *ep,
-			      int result, bool force)
+			      int result)
 {
 {
 	struct s3c_hsotg_req *req, *treq;
 	struct s3c_hsotg_req *req, *treq;
 	unsigned size;
 	unsigned size;
 
 
-	list_for_each_entry_safe(req, treq, &ep->queue, queue) {
-		/*
-		 * currently, we can't do much about an already
-		 * running request on an in endpoint
-		 */
-
-		if (ep->req == req && ep->dir_in && !force)
-			continue;
+	ep->req = NULL;
 
 
+	list_for_each_entry_safe(req, treq, &ep->queue, queue)
 		s3c_hsotg_complete_request(hsotg, ep, req,
 		s3c_hsotg_complete_request(hsotg, ep, req,
 					   result);
 					   result);
-	}
+
 	if (!hsotg->dedicated_fifos)
 	if (!hsotg->dedicated_fifos)
 		return;
 		return;
 	size = (readl(hsotg->regs + DTXFSTS(ep->index)) & 0xffff) * 4;
 	size = (readl(hsotg->regs + DTXFSTS(ep->index)) & 0xffff) * 4;
@@ -2035,8 +2172,16 @@ void s3c_hsotg_disconnect(struct dwc2_hsotg *hsotg)
 		return;
 		return;
 
 
 	hsotg->connected = 0;
 	hsotg->connected = 0;
-	for (ep = 0; ep < hsotg->num_of_eps; ep++)
-		kill_all_requests(hsotg, &hsotg->eps[ep], -ESHUTDOWN, true);
+	hsotg->test_mode = 0;
+
+	for (ep = 0; ep < hsotg->num_of_eps; ep++) {
+		if (hsotg->eps_in[ep])
+			kill_all_requests(hsotg, hsotg->eps_in[ep],
+								-ESHUTDOWN);
+		if (hsotg->eps_out[ep])
+			kill_all_requests(hsotg, hsotg->eps_out[ep],
+								-ESHUTDOWN);
+	}
 
 
 	call_gadget(hsotg, disconnect);
 	call_gadget(hsotg, disconnect);
 }
 }
@@ -2053,9 +2198,11 @@ static void s3c_hsotg_irq_fifoempty(struct dwc2_hsotg *hsotg, bool periodic)
 	int epno, ret;
 	int epno, ret;
 
 
 	/* look through for any more data to transmit */
 	/* look through for any more data to transmit */
-
 	for (epno = 0; epno < hsotg->num_of_eps; epno++) {
 	for (epno = 0; epno < hsotg->num_of_eps; epno++) {
-		ep = &hsotg->eps[epno];
+		ep = index_to_ep(hsotg, epno, 1);
+
+		if (!ep)
+			continue;
 
 
 		if (!ep->dir_in)
 		if (!ep->dir_in)
 			continue;
 			continue;
@@ -2129,9 +2276,13 @@ static int s3c_hsotg_corereset(struct dwc2_hsotg *hsotg)
  *
  *
  * Issue a soft reset to the core, and await the core finishing it.
  * Issue a soft reset to the core, and await the core finishing it.
  */
  */
-void s3c_hsotg_core_init_disconnected(struct dwc2_hsotg *hsotg)
+void s3c_hsotg_core_init_disconnected(struct dwc2_hsotg *hsotg,
+						bool is_usb_reset)
 {
 {
-	s3c_hsotg_corereset(hsotg);
+	u32 val;
+
+	if (!is_usb_reset)
+		s3c_hsotg_corereset(hsotg);
 
 
 	/*
 	/*
 	 * we must now enable ep0 ready for host detection and then
 	 * we must now enable ep0 ready for host detection and then
@@ -2139,14 +2290,16 @@ void s3c_hsotg_core_init_disconnected(struct dwc2_hsotg *hsotg)
 	 */
 	 */
 
 
 	/* set the PLL on, remove the HNP/SRP and set the PHY */
 	/* set the PLL on, remove the HNP/SRP and set the PHY */
+	val = (hsotg->phyif == GUSBCFG_PHYIF8) ? 9 : 5;
 	writel(hsotg->phyif | GUSBCFG_TOUTCAL(7) |
 	writel(hsotg->phyif | GUSBCFG_TOUTCAL(7) |
-	       (0x5 << 10), hsotg->regs + GUSBCFG);
+	       (val << GUSBCFG_USBTRDTIM_SHIFT), hsotg->regs + GUSBCFG);
 
 
 	s3c_hsotg_init_fifo(hsotg);
 	s3c_hsotg_init_fifo(hsotg);
 
 
-	__orr32(hsotg->regs + DCTL, DCTL_SFTDISCON);
+	if (!is_usb_reset)
+		__orr32(hsotg->regs + DCTL, DCTL_SFTDISCON);
 
 
-	writel(1 << 18 | DCFG_DEVSPD_HS,  hsotg->regs + DCFG);
+	writel(DCFG_EPMISCNT(1) | DCFG_DEVSPD_HS,  hsotg->regs + DCFG);
 
 
 	/* Clear any pending OTG interrupts */
 	/* Clear any pending OTG interrupts */
 	writel(0xffffffff, hsotg->regs + GOTGINT);
 	writel(0xffffffff, hsotg->regs + GOTGINT);
@@ -2163,7 +2316,7 @@ void s3c_hsotg_core_init_disconnected(struct dwc2_hsotg *hsotg)
 
 
 	if (using_dma(hsotg))
 	if (using_dma(hsotg))
 		writel(GAHBCFG_GLBL_INTR_EN | GAHBCFG_DMA_EN |
 		writel(GAHBCFG_GLBL_INTR_EN | GAHBCFG_DMA_EN |
-		       GAHBCFG_HBSTLEN_INCR4,
+		       (GAHBCFG_HBSTLEN_INCR4 << GAHBCFG_HBSTLEN_SHIFT),
 		       hsotg->regs + GAHBCFG);
 		       hsotg->regs + GAHBCFG);
 	else
 	else
 		writel(((hsotg->dedicated_fifos) ? (GAHBCFG_NP_TXF_EMP_LVL |
 		writel(((hsotg->dedicated_fifos) ? (GAHBCFG_NP_TXF_EMP_LVL |
@@ -2177,8 +2330,8 @@ void s3c_hsotg_core_init_disconnected(struct dwc2_hsotg *hsotg)
 	 * interrupts.
 	 * interrupts.
 	 */
 	 */
 
 
-	writel(((hsotg->dedicated_fifos) ? DIEPMSK_TXFIFOEMPTY |
-		DIEPMSK_INTKNTXFEMPMSK : 0) |
+	writel(((hsotg->dedicated_fifos && !using_dma(hsotg)) ?
+		DIEPMSK_TXFIFOEMPTY | DIEPMSK_INTKNTXFEMPMSK : 0) |
 		DIEPMSK_EPDISBLDMSK | DIEPMSK_XFERCOMPLMSK |
 		DIEPMSK_EPDISBLDMSK | DIEPMSK_XFERCOMPLMSK |
 		DIEPMSK_TIMEOUTMSK | DIEPMSK_AHBERRMSK |
 		DIEPMSK_TIMEOUTMSK | DIEPMSK_AHBERRMSK |
 		DIEPMSK_INTKNEPMISMSK,
 		DIEPMSK_INTKNEPMISMSK,
@@ -2215,9 +2368,11 @@ void s3c_hsotg_core_init_disconnected(struct dwc2_hsotg *hsotg)
 	s3c_hsotg_ctrl_epint(hsotg, 0, 0, 1);
 	s3c_hsotg_ctrl_epint(hsotg, 0, 0, 1);
 	s3c_hsotg_ctrl_epint(hsotg, 0, 1, 1);
 	s3c_hsotg_ctrl_epint(hsotg, 0, 1, 1);
 
 
-	__orr32(hsotg->regs + DCTL, DCTL_PWRONPRGDONE);
-	udelay(10);  /* see openiboot */
-	__bic32(hsotg->regs + DCTL, DCTL_PWRONPRGDONE);
+	if (!is_usb_reset) {
+		__orr32(hsotg->regs + DCTL, DCTL_PWRONPRGDONE);
+		udelay(10);  /* see openiboot */
+		__bic32(hsotg->regs + DCTL, DCTL_PWRONPRGDONE);
+	}
 
 
 	dev_dbg(hsotg->dev, "DCTL=0x%08x\n", readl(hsotg->regs + DCTL));
 	dev_dbg(hsotg->dev, "DCTL=0x%08x\n", readl(hsotg->regs + DCTL));
 
 
@@ -2230,13 +2385,13 @@ void s3c_hsotg_core_init_disconnected(struct dwc2_hsotg *hsotg)
 	writel(DXEPTSIZ_MC(1) | DXEPTSIZ_PKTCNT(1) |
 	writel(DXEPTSIZ_MC(1) | DXEPTSIZ_PKTCNT(1) |
 	       DXEPTSIZ_XFERSIZE(8), hsotg->regs + DOEPTSIZ0);
 	       DXEPTSIZ_XFERSIZE(8), hsotg->regs + DOEPTSIZ0);
 
 
-	writel(s3c_hsotg_ep0_mps(hsotg->eps[0].ep.maxpacket) |
+	writel(s3c_hsotg_ep0_mps(hsotg->eps_out[0]->ep.maxpacket) |
 	       DXEPCTL_CNAK | DXEPCTL_EPENA |
 	       DXEPCTL_CNAK | DXEPCTL_EPENA |
 	       DXEPCTL_USBACTEP,
 	       DXEPCTL_USBACTEP,
 	       hsotg->regs + DOEPCTL0);
 	       hsotg->regs + DOEPCTL0);
 
 
 	/* enable, but don't activate EP0in */
 	/* enable, but don't activate EP0in */
-	writel(s3c_hsotg_ep0_mps(hsotg->eps[0].ep.maxpacket) |
+	writel(s3c_hsotg_ep0_mps(hsotg->eps_out[0]->ep.maxpacket) |
 	       DXEPCTL_USBACTEP, hsotg->regs + DIEPCTL0);
 	       DXEPCTL_USBACTEP, hsotg->regs + DIEPCTL0);
 
 
 	s3c_hsotg_enqueue_setup(hsotg);
 	s3c_hsotg_enqueue_setup(hsotg);
@@ -2246,8 +2401,10 @@ void s3c_hsotg_core_init_disconnected(struct dwc2_hsotg *hsotg)
 		readl(hsotg->regs + DOEPCTL0));
 		readl(hsotg->regs + DOEPCTL0));
 
 
 	/* clear global NAKs */
 	/* clear global NAKs */
-	writel(DCTL_CGOUTNAK | DCTL_CGNPINNAK | DCTL_SFTDISCON,
-	       hsotg->regs + DCTL);
+	val = DCTL_CGOUTNAK | DCTL_CGNPINNAK;
+	if (!is_usb_reset)
+		val |= DCTL_SFTDISCON;
+	__orr32(hsotg->regs + DCTL, val);
 
 
 	/* must be at-least 3ms to allow bus to see disconnect */
 	/* must be at-least 3ms to allow bus to see disconnect */
 	mdelay(3);
 	mdelay(3);
@@ -2293,7 +2450,6 @@ irq_retry:
 		writel(GINTSTS_ENUMDONE, hsotg->regs + GINTSTS);
 		writel(GINTSTS_ENUMDONE, hsotg->regs + GINTSTS);
 
 
 		s3c_hsotg_irq_enumdone(hsotg);
 		s3c_hsotg_irq_enumdone(hsotg);
-		hsotg->connected = 1;
 	}
 	}
 
 
 	if (gintsts & (GINTSTS_OEPINT | GINTSTS_IEPINT)) {
 	if (gintsts & (GINTSTS_OEPINT | GINTSTS_IEPINT)) {
@@ -2308,12 +2464,14 @@ irq_retry:
 
 
 		dev_dbg(hsotg->dev, "%s: daint=%08x\n", __func__, daint);
 		dev_dbg(hsotg->dev, "%s: daint=%08x\n", __func__, daint);
 
 
-		for (ep = 0; ep < 15 && daint_out; ep++, daint_out >>= 1) {
+		for (ep = 0; ep < hsotg->num_of_eps && daint_out;
+						ep++, daint_out >>= 1) {
 			if (daint_out & 1)
 			if (daint_out & 1)
 				s3c_hsotg_epint(hsotg, ep, 0);
 				s3c_hsotg_epint(hsotg, ep, 0);
 		}
 		}
 
 
-		for (ep = 0; ep < 15 && daint_in; ep++, daint_in >>= 1) {
+		for (ep = 0; ep < hsotg->num_of_eps  && daint_in;
+						ep++, daint_in >>= 1) {
 			if (daint_in & 1)
 			if (daint_in & 1)
 				s3c_hsotg_epint(hsotg, ep, 1);
 				s3c_hsotg_epint(hsotg, ep, 1);
 		}
 		}
@@ -2329,15 +2487,17 @@ irq_retry:
 
 
 		writel(GINTSTS_USBRST, hsotg->regs + GINTSTS);
 		writel(GINTSTS_USBRST, hsotg->regs + GINTSTS);
 
 
+		/* Report disconnection if it is not already done. */
+		s3c_hsotg_disconnect(hsotg);
+
 		if (usb_status & GOTGCTL_BSESVLD) {
 		if (usb_status & GOTGCTL_BSESVLD) {
 			if (time_after(jiffies, hsotg->last_rst +
 			if (time_after(jiffies, hsotg->last_rst +
 				       msecs_to_jiffies(200))) {
 				       msecs_to_jiffies(200))) {
 
 
-				kill_all_requests(hsotg, &hsotg->eps[0],
-							  -ECONNRESET, true);
+				kill_all_requests(hsotg, hsotg->eps_out[0],
+							  -ECONNRESET);
 
 
-				s3c_hsotg_core_init_disconnected(hsotg);
-				s3c_hsotg_core_connect(hsotg);
+				s3c_hsotg_core_init_disconnected(hsotg, true);
 			}
 			}
 		}
 		}
 	}
 	}
@@ -2429,12 +2589,12 @@ static int s3c_hsotg_ep_enable(struct usb_ep *ep,
 	struct s3c_hsotg_ep *hs_ep = our_ep(ep);
 	struct s3c_hsotg_ep *hs_ep = our_ep(ep);
 	struct dwc2_hsotg *hsotg = hs_ep->parent;
 	struct dwc2_hsotg *hsotg = hs_ep->parent;
 	unsigned long flags;
 	unsigned long flags;
-	int index = hs_ep->index;
+	unsigned int index = hs_ep->index;
 	u32 epctrl_reg;
 	u32 epctrl_reg;
 	u32 epctrl;
 	u32 epctrl;
 	u32 mps;
 	u32 mps;
-	int dir_in;
-	int i, val, size;
+	unsigned int dir_in;
+	unsigned int i, val, size;
 	int ret = 0;
 	int ret = 0;
 
 
 	dev_dbg(hsotg->dev,
 	dev_dbg(hsotg->dev,
@@ -2482,7 +2642,7 @@ static int s3c_hsotg_ep_enable(struct usb_ep *ep,
 	epctrl |= DXEPCTL_SNAK;
 	epctrl |= DXEPCTL_SNAK;
 
 
 	/* update the endpoint state */
 	/* update the endpoint state */
-	s3c_hsotg_set_ep_maxpacket(hsotg, hs_ep->index, mps);
+	s3c_hsotg_set_ep_maxpacket(hsotg, hs_ep->index, mps, dir_in);
 
 
 	/* default, set to non-periodic */
 	/* default, set to non-periodic */
 	hs_ep->isochronous = 0;
 	hs_ep->isochronous = 0;
@@ -2518,30 +2678,48 @@ static int s3c_hsotg_ep_enable(struct usb_ep *ep,
 		break;
 		break;
 	}
 	}
 
 
+	/* If fifo is already allocated for this ep */
+	if (hs_ep->fifo_index) {
+		size =  hs_ep->ep.maxpacket * hs_ep->mc;
+		/* If bigger fifo is required deallocate current one */
+		if (size > hs_ep->fifo_size) {
+			hsotg->fifo_map &= ~(1 << hs_ep->fifo_index);
+			hs_ep->fifo_index = 0;
+			hs_ep->fifo_size = 0;
+		}
+	}
+
 	/*
 	/*
 	 * if the hardware has dedicated fifos, we must give each IN EP
 	 * if the hardware has dedicated fifos, we must give each IN EP
 	 * a unique tx-fifo even if it is non-periodic.
 	 * a unique tx-fifo even if it is non-periodic.
 	 */
 	 */
-	if (dir_in && hsotg->dedicated_fifos) {
+	if (dir_in && hsotg->dedicated_fifos && !hs_ep->fifo_index) {
+		u32 fifo_index = 0;
+		u32 fifo_size = UINT_MAX;
 		size = hs_ep->ep.maxpacket*hs_ep->mc;
 		size = hs_ep->ep.maxpacket*hs_ep->mc;
-		for (i = 1; i <= 8; ++i) {
+		for (i = 1; i < hsotg->num_of_eps; ++i) {
 			if (hsotg->fifo_map & (1<<i))
 			if (hsotg->fifo_map & (1<<i))
 				continue;
 				continue;
 			val = readl(hsotg->regs + DPTXFSIZN(i));
 			val = readl(hsotg->regs + DPTXFSIZN(i));
 			val = (val >> FIFOSIZE_DEPTH_SHIFT)*4;
 			val = (val >> FIFOSIZE_DEPTH_SHIFT)*4;
 			if (val < size)
 			if (val < size)
 				continue;
 				continue;
-			hsotg->fifo_map |= 1<<i;
-
-			epctrl |= DXEPCTL_TXFNUM(i);
-			hs_ep->fifo_index = i;
-			hs_ep->fifo_size = val;
-			break;
+			/* Search for smallest acceptable fifo */
+			if (val < fifo_size) {
+				fifo_size = val;
+				fifo_index = i;
+			}
 		}
 		}
-		if (i == 8) {
+		if (!fifo_index) {
+			dev_err(hsotg->dev,
+				"%s: No suitable fifo found\n", __func__);
 			ret = -ENOMEM;
 			ret = -ENOMEM;
 			goto error;
 			goto error;
 		}
 		}
+		hsotg->fifo_map |= 1 << fifo_index;
+		epctrl |= DXEPCTL_TXFNUM(fifo_index);
+		hs_ep->fifo_index = fifo_index;
+		hs_ep->fifo_size = fifo_size;
 	}
 	}
 
 
 	/* for non control endpoints, set PID to D0 */
 	/* for non control endpoints, set PID to D0 */
@@ -2579,7 +2757,7 @@ static int s3c_hsotg_ep_disable_force(struct usb_ep *ep, bool force)
 
 
 	dev_dbg(hsotg->dev, "%s(ep %p)\n", __func__, ep);
 	dev_dbg(hsotg->dev, "%s(ep %p)\n", __func__, ep);
 
 
-	if (ep == &hsotg->eps[0].ep) {
+	if (ep == &hsotg->eps_out[0]->ep) {
 		dev_err(hsotg->dev, "%s: called for ep0\n", __func__);
 		dev_err(hsotg->dev, "%s: called for ep0\n", __func__);
 		return -EINVAL;
 		return -EINVAL;
 	}
 	}
@@ -2587,8 +2765,6 @@ static int s3c_hsotg_ep_disable_force(struct usb_ep *ep, bool force)
 	epctrl_reg = dir_in ? DIEPCTL(index) : DOEPCTL(index);
 	epctrl_reg = dir_in ? DIEPCTL(index) : DOEPCTL(index);
 
 
 	spin_lock_irqsave(&hsotg->lock, flags);
 	spin_lock_irqsave(&hsotg->lock, flags);
-	/* terminate all requests with shutdown */
-	kill_all_requests(hsotg, hs_ep, -ESHUTDOWN, force);
 
 
 	hsotg->fifo_map &= ~(1<<hs_ep->fifo_index);
 	hsotg->fifo_map &= ~(1<<hs_ep->fifo_index);
 	hs_ep->fifo_index = 0;
 	hs_ep->fifo_index = 0;
@@ -2605,6 +2781,9 @@ static int s3c_hsotg_ep_disable_force(struct usb_ep *ep, bool force)
 	/* disable endpoint interrupts */
 	/* disable endpoint interrupts */
 	s3c_hsotg_ctrl_epint(hsotg, hs_ep->index, hs_ep->dir_in, 0);
 	s3c_hsotg_ctrl_epint(hsotg, hs_ep->index, hs_ep->dir_in, 0);
 
 
+	/* terminate all requests with shutdown */
+	kill_all_requests(hsotg, hs_ep, -ESHUTDOWN);
+
 	spin_unlock_irqrestore(&hsotg->lock, flags);
 	spin_unlock_irqrestore(&hsotg->lock, flags);
 	return 0;
 	return 0;
 }
 }
@@ -2682,40 +2861,39 @@ static int s3c_hsotg_ep_sethalt(struct usb_ep *ep, int value)
 		return 0;
 		return 0;
 	}
 	}
 
 
-	/* write both IN and OUT control registers */
-
-	epreg = DIEPCTL(index);
-	epctl = readl(hs->regs + epreg);
-
-	if (value) {
-		epctl |= DXEPCTL_STALL + DXEPCTL_SNAK;
-		if (epctl & DXEPCTL_EPENA)
-			epctl |= DXEPCTL_EPDIS;
+	if (hs_ep->dir_in) {
+		epreg = DIEPCTL(index);
+		epctl = readl(hs->regs + epreg);
+
+		if (value) {
+			epctl |= DXEPCTL_STALL + DXEPCTL_SNAK;
+			if (epctl & DXEPCTL_EPENA)
+				epctl |= DXEPCTL_EPDIS;
+		} else {
+			epctl &= ~DXEPCTL_STALL;
+			xfertype = epctl & DXEPCTL_EPTYPE_MASK;
+			if (xfertype == DXEPCTL_EPTYPE_BULK ||
+				xfertype == DXEPCTL_EPTYPE_INTERRUPT)
+					epctl |= DXEPCTL_SETD0PID;
+		}
+		writel(epctl, hs->regs + epreg);
 	} else {
 	} else {
-		epctl &= ~DXEPCTL_STALL;
-		xfertype = epctl & DXEPCTL_EPTYPE_MASK;
-		if (xfertype == DXEPCTL_EPTYPE_BULK ||
-			xfertype == DXEPCTL_EPTYPE_INTERRUPT)
-				epctl |= DXEPCTL_SETD0PID;
-	}
-
-	writel(epctl, hs->regs + epreg);
 
 
-	epreg = DOEPCTL(index);
-	epctl = readl(hs->regs + epreg);
+		epreg = DOEPCTL(index);
+		epctl = readl(hs->regs + epreg);
 
 
-	if (value)
-		epctl |= DXEPCTL_STALL;
-	else {
-		epctl &= ~DXEPCTL_STALL;
-		xfertype = epctl & DXEPCTL_EPTYPE_MASK;
-		if (xfertype == DXEPCTL_EPTYPE_BULK ||
-			xfertype == DXEPCTL_EPTYPE_INTERRUPT)
-				epctl |= DXEPCTL_SETD0PID;
+		if (value)
+			epctl |= DXEPCTL_STALL;
+		else {
+			epctl &= ~DXEPCTL_STALL;
+			xfertype = epctl & DXEPCTL_EPTYPE_MASK;
+			if (xfertype == DXEPCTL_EPTYPE_BULK ||
+				xfertype == DXEPCTL_EPTYPE_INTERRUPT)
+					epctl |= DXEPCTL_SETD0PID;
+		}
+		writel(epctl, hs->regs + epreg);
 	}
 	}
 
 
-	writel(epctl, hs->regs + epreg);
-
 	hs_ep->halted = value;
 	hs_ep->halted = value;
 
 
 	return 0;
 	return 0;
@@ -2801,6 +2979,7 @@ static void s3c_hsotg_phy_disable(struct dwc2_hsotg *hsotg)
  */
  */
 static void s3c_hsotg_init(struct dwc2_hsotg *hsotg)
 static void s3c_hsotg_init(struct dwc2_hsotg *hsotg)
 {
 {
+	u32 trdtim;
 	/* unmask subset of endpoint interrupts */
 	/* unmask subset of endpoint interrupts */
 
 
 	writel(DIEPMSK_TIMEOUTMSK | DIEPMSK_AHBERRMSK |
 	writel(DIEPMSK_TIMEOUTMSK | DIEPMSK_AHBERRMSK |
@@ -2816,12 +2995,6 @@ static void s3c_hsotg_init(struct dwc2_hsotg *hsotg)
 	/* Be in disconnected state until gadget is registered */
 	/* Be in disconnected state until gadget is registered */
 	__orr32(hsotg->regs + DCTL, DCTL_SFTDISCON);
 	__orr32(hsotg->regs + DCTL, DCTL_SFTDISCON);
 
 
-	if (0) {
-		/* post global nak until we're ready */
-		writel(DCTL_SGNPINNAK | DCTL_SGOUTNAK,
-		       hsotg->regs + DCTL);
-	}
-
 	/* setup fifos */
 	/* setup fifos */
 
 
 	dev_dbg(hsotg->dev, "GRXFSIZ=0x%08x, GNPTXFSIZ=0x%08x\n",
 	dev_dbg(hsotg->dev, "GRXFSIZ=0x%08x, GNPTXFSIZ=0x%08x\n",
@@ -2831,11 +3004,13 @@ static void s3c_hsotg_init(struct dwc2_hsotg *hsotg)
 	s3c_hsotg_init_fifo(hsotg);
 	s3c_hsotg_init_fifo(hsotg);
 
 
 	/* set the PLL on, remove the HNP/SRP and set the PHY */
 	/* set the PLL on, remove the HNP/SRP and set the PHY */
-	writel(GUSBCFG_PHYIF16 | GUSBCFG_TOUTCAL(7) | (0x5 << 10),
-	       hsotg->regs + GUSBCFG);
+	trdtim = (hsotg->phyif == GUSBCFG_PHYIF8) ? 9 : 5;
+	writel(hsotg->phyif | GUSBCFG_TOUTCAL(7) |
+		(trdtim << GUSBCFG_USBTRDTIM_SHIFT),
+		hsotg->regs + GUSBCFG);
 
 
-	writel(using_dma(hsotg) ? GAHBCFG_DMA_EN : 0x0,
-	       hsotg->regs + GAHBCFG);
+	if (using_dma(hsotg))
+		__orr32(hsotg->regs + GAHBCFG, GAHBCFG_DMA_EN);
 }
 }
 
 
 /**
 /**
@@ -2889,10 +3064,12 @@ static int s3c_hsotg_udc_start(struct usb_gadget *gadget,
 	}
 	}
 
 
 	s3c_hsotg_phy_enable(hsotg);
 	s3c_hsotg_phy_enable(hsotg);
+	if (!IS_ERR_OR_NULL(hsotg->uphy))
+		otg_set_peripheral(hsotg->uphy->otg, &hsotg->gadget);
 
 
 	spin_lock_irqsave(&hsotg->lock, flags);
 	spin_lock_irqsave(&hsotg->lock, flags);
 	s3c_hsotg_init(hsotg);
 	s3c_hsotg_init(hsotg);
-	s3c_hsotg_core_init_disconnected(hsotg);
+	s3c_hsotg_core_init_disconnected(hsotg, false);
 	hsotg->enabled = 0;
 	hsotg->enabled = 0;
 	spin_unlock_irqrestore(&hsotg->lock, flags);
 	spin_unlock_irqrestore(&hsotg->lock, flags);
 
 
@@ -2927,8 +3104,12 @@ static int s3c_hsotg_udc_stop(struct usb_gadget *gadget)
 	mutex_lock(&hsotg->init_mutex);
 	mutex_lock(&hsotg->init_mutex);
 
 
 	/* all endpoints should be shutdown */
 	/* all endpoints should be shutdown */
-	for (ep = 1; ep < hsotg->num_of_eps; ep++)
-		s3c_hsotg_ep_disable_force(&hsotg->eps[ep].ep, true);
+	for (ep = 1; ep < hsotg->num_of_eps; ep++) {
+		if (hsotg->eps_in[ep])
+			s3c_hsotg_ep_disable(&hsotg->eps_in[ep]->ep);
+		if (hsotg->eps_out[ep])
+			s3c_hsotg_ep_disable(&hsotg->eps_out[ep]->ep);
+	}
 
 
 	spin_lock_irqsave(&hsotg->lock, flags);
 	spin_lock_irqsave(&hsotg->lock, flags);
 
 
@@ -2938,6 +3119,8 @@ static int s3c_hsotg_udc_stop(struct usb_gadget *gadget)
 
 
 	spin_unlock_irqrestore(&hsotg->lock, flags);
 	spin_unlock_irqrestore(&hsotg->lock, flags);
 
 
+	if (!IS_ERR_OR_NULL(hsotg->uphy))
+		otg_set_peripheral(hsotg->uphy->otg, NULL);
 	s3c_hsotg_phy_disable(hsotg);
 	s3c_hsotg_phy_disable(hsotg);
 
 
 	regulator_bulk_disable(ARRAY_SIZE(hsotg->supplies), hsotg->supplies);
 	regulator_bulk_disable(ARRAY_SIZE(hsotg->supplies), hsotg->supplies);
@@ -2979,9 +3162,11 @@ static int s3c_hsotg_pullup(struct usb_gadget *gadget, int is_on)
 	if (is_on) {
 	if (is_on) {
 		clk_enable(hsotg->clk);
 		clk_enable(hsotg->clk);
 		hsotg->enabled = 1;
 		hsotg->enabled = 1;
+		s3c_hsotg_core_init_disconnected(hsotg, false);
 		s3c_hsotg_core_connect(hsotg);
 		s3c_hsotg_core_connect(hsotg);
 	} else {
 	} else {
 		s3c_hsotg_core_disconnect(hsotg);
 		s3c_hsotg_core_disconnect(hsotg);
+		s3c_hsotg_disconnect(hsotg);
 		hsotg->enabled = 0;
 		hsotg->enabled = 0;
 		clk_disable(hsotg->clk);
 		clk_disable(hsotg->clk);
 	}
 	}
@@ -2993,11 +3178,52 @@ static int s3c_hsotg_pullup(struct usb_gadget *gadget, int is_on)
 	return 0;
 	return 0;
 }
 }
 
 
+static int s3c_hsotg_vbus_session(struct usb_gadget *gadget, int is_active)
+{
+	struct dwc2_hsotg *hsotg = to_hsotg(gadget);
+	unsigned long flags;
+
+	dev_dbg(hsotg->dev, "%s: is_active: %d\n", __func__, is_active);
+	spin_lock_irqsave(&hsotg->lock, flags);
+
+	if (is_active) {
+		/* Kill any ep0 requests as controller will be reinitialized */
+		kill_all_requests(hsotg, hsotg->eps_out[0], -ECONNRESET);
+		s3c_hsotg_core_init_disconnected(hsotg, false);
+		if (hsotg->enabled)
+			s3c_hsotg_core_connect(hsotg);
+	} else {
+		s3c_hsotg_core_disconnect(hsotg);
+		s3c_hsotg_disconnect(hsotg);
+	}
+
+	spin_unlock_irqrestore(&hsotg->lock, flags);
+	return 0;
+}
+
+/**
+ * s3c_hsotg_vbus_draw - report bMaxPower field
+ * @gadget: The usb gadget state
+ * @mA: Amount of current
+ *
+ * Report how much power the device may consume to the phy.
+ */
+static int s3c_hsotg_vbus_draw(struct usb_gadget *gadget, unsigned mA)
+{
+	struct dwc2_hsotg *hsotg = to_hsotg(gadget);
+
+	if (IS_ERR_OR_NULL(hsotg->uphy))
+		return -ENOTSUPP;
+	return usb_phy_set_power(hsotg->uphy, mA);
+}
+
 static const struct usb_gadget_ops s3c_hsotg_gadget_ops = {
 static const struct usb_gadget_ops s3c_hsotg_gadget_ops = {
 	.get_frame	= s3c_hsotg_gadget_getframe,
 	.get_frame	= s3c_hsotg_gadget_getframe,
 	.udc_start		= s3c_hsotg_udc_start,
 	.udc_start		= s3c_hsotg_udc_start,
 	.udc_stop		= s3c_hsotg_udc_stop,
 	.udc_stop		= s3c_hsotg_udc_stop,
 	.pullup                 = s3c_hsotg_pullup,
 	.pullup                 = s3c_hsotg_pullup,
+	.vbus_session		= s3c_hsotg_vbus_session,
+	.vbus_draw		= s3c_hsotg_vbus_draw,
 };
 };
 
 
 /**
 /**
@@ -3012,19 +3238,19 @@ static const struct usb_gadget_ops s3c_hsotg_gadget_ops = {
  */
  */
 static void s3c_hsotg_initep(struct dwc2_hsotg *hsotg,
 static void s3c_hsotg_initep(struct dwc2_hsotg *hsotg,
 				       struct s3c_hsotg_ep *hs_ep,
 				       struct s3c_hsotg_ep *hs_ep,
-				       int epnum)
+				       int epnum,
+				       bool dir_in)
 {
 {
 	char *dir;
 	char *dir;
 
 
 	if (epnum == 0)
 	if (epnum == 0)
 		dir = "";
 		dir = "";
-	else if ((epnum % 2) == 0) {
-		dir = "out";
-	} else {
+	else if (dir_in)
 		dir = "in";
 		dir = "in";
-		hs_ep->dir_in = 1;
-	}
+	else
+		dir = "out";
 
 
+	hs_ep->dir_in = dir_in;
 	hs_ep->index = epnum;
 	hs_ep->index = epnum;
 
 
 	snprintf(hs_ep->name, sizeof(hs_ep->name), "ep%d%s", epnum, dir);
 	snprintf(hs_ep->name, sizeof(hs_ep->name), "ep%d%s", epnum, dir);
@@ -3048,8 +3274,10 @@ static void s3c_hsotg_initep(struct dwc2_hsotg *hsotg,
 
 
 	if (using_dma(hsotg)) {
 	if (using_dma(hsotg)) {
 		u32 next = DXEPCTL_NEXTEP((epnum + 1) % 15);
 		u32 next = DXEPCTL_NEXTEP((epnum + 1) % 15);
-		writel(next, hsotg->regs + DIEPCTL(epnum));
-		writel(next, hsotg->regs + DOEPCTL(epnum));
+		if (dir_in)
+			writel(next, hsotg->regs + DIEPCTL(epnum));
+		else
+			writel(next, hsotg->regs + DOEPCTL(epnum));
 	}
 	}
 }
 }
 
 
@@ -3059,24 +3287,56 @@ static void s3c_hsotg_initep(struct dwc2_hsotg *hsotg,
  *
  *
  * Read the USB core HW configuration registers
  * Read the USB core HW configuration registers
  */
  */
-static void s3c_hsotg_hw_cfg(struct dwc2_hsotg *hsotg)
+static int s3c_hsotg_hw_cfg(struct dwc2_hsotg *hsotg)
 {
 {
-	u32 cfg2, cfg3, cfg4;
+	u32 cfg;
+	u32 ep_type;
+	u32 i;
+
 	/* check hardware configuration */
 	/* check hardware configuration */
 
 
-	cfg2 = readl(hsotg->regs + 0x48);
-	hsotg->num_of_eps = (cfg2 >> 10) & 0xF;
+	cfg = readl(hsotg->regs + GHWCFG2);
+	hsotg->num_of_eps = (cfg >> GHWCFG2_NUM_DEV_EP_SHIFT) & 0xF;
+	/* Add ep0 */
+	hsotg->num_of_eps++;
+
+	hsotg->eps_in[0] = devm_kzalloc(hsotg->dev, sizeof(struct s3c_hsotg_ep),
+								GFP_KERNEL);
+	if (!hsotg->eps_in[0])
+		return -ENOMEM;
+	/* Same s3c_hsotg_ep is used in both directions for ep0 */
+	hsotg->eps_out[0] = hsotg->eps_in[0];
+
+	cfg = readl(hsotg->regs + GHWCFG1);
+	for (i = 1, cfg >>= 2; i < hsotg->num_of_eps; i++, cfg >>= 2) {
+		ep_type = cfg & 3;
+		/* Direction in or both */
+		if (!(ep_type & 2)) {
+			hsotg->eps_in[i] = devm_kzalloc(hsotg->dev,
+				sizeof(struct s3c_hsotg_ep), GFP_KERNEL);
+			if (!hsotg->eps_in[i])
+				return -ENOMEM;
+		}
+		/* Direction out or both */
+		if (!(ep_type & 1)) {
+			hsotg->eps_out[i] = devm_kzalloc(hsotg->dev,
+				sizeof(struct s3c_hsotg_ep), GFP_KERNEL);
+			if (!hsotg->eps_out[i])
+				return -ENOMEM;
+		}
+	}
 
 
-	cfg3 = readl(hsotg->regs + 0x4C);
-	hsotg->fifo_mem = (cfg3 >> 16);
+	cfg = readl(hsotg->regs + GHWCFG3);
+	hsotg->fifo_mem = (cfg >> GHWCFG3_DFIFO_DEPTH_SHIFT);
 
 
-	cfg4 = readl(hsotg->regs + 0x50);
-	hsotg->dedicated_fifos = (cfg4 >> 25) & 1;
+	cfg = readl(hsotg->regs + GHWCFG4);
+	hsotg->dedicated_fifos = (cfg >> GHWCFG4_DED_FIFO_SHIFT) & 1;
 
 
 	dev_info(hsotg->dev, "EPs: %d, %s fifos, %d entries in SPRAM\n",
 	dev_info(hsotg->dev, "EPs: %d, %s fifos, %d entries in SPRAM\n",
 		 hsotg->num_of_eps,
 		 hsotg->num_of_eps,
 		 hsotg->dedicated_fifos ? "dedicated" : "shared",
 		 hsotg->dedicated_fifos ? "dedicated" : "shared",
 		 hsotg->fifo_mem);
 		 hsotg->fifo_mem);
+	return 0;
 }
 }
 
 
 /**
 /**
@@ -3095,22 +3355,22 @@ static void s3c_hsotg_dump(struct dwc2_hsotg *hsotg)
 		 readl(regs + DCFG), readl(regs + DCTL),
 		 readl(regs + DCFG), readl(regs + DCTL),
 		 readl(regs + DIEPMSK));
 		 readl(regs + DIEPMSK));
 
 
-	dev_info(dev, "GAHBCFG=0x%08x, 0x44=0x%08x\n",
-		 readl(regs + GAHBCFG), readl(regs + 0x44));
+	dev_info(dev, "GAHBCFG=0x%08x, GHWCFG1=0x%08x\n",
+		 readl(regs + GAHBCFG), readl(regs + GHWCFG1));
 
 
 	dev_info(dev, "GRXFSIZ=0x%08x, GNPTXFSIZ=0x%08x\n",
 	dev_info(dev, "GRXFSIZ=0x%08x, GNPTXFSIZ=0x%08x\n",
 		 readl(regs + GRXFSIZ), readl(regs + GNPTXFSIZ));
 		 readl(regs + GRXFSIZ), readl(regs + GNPTXFSIZ));
 
 
 	/* show periodic fifo settings */
 	/* show periodic fifo settings */
 
 
-	for (idx = 1; idx <= 15; idx++) {
+	for (idx = 1; idx < hsotg->num_of_eps; idx++) {
 		val = readl(regs + DPTXFSIZN(idx));
 		val = readl(regs + DPTXFSIZN(idx));
 		dev_info(dev, "DPTx[%d] FSize=%d, StAddr=0x%08x\n", idx,
 		dev_info(dev, "DPTx[%d] FSize=%d, StAddr=0x%08x\n", idx,
 			 val >> FIFOSIZE_DEPTH_SHIFT,
 			 val >> FIFOSIZE_DEPTH_SHIFT,
 			 val & FIFOSIZE_STARTADDR_MASK);
 			 val & FIFOSIZE_STARTADDR_MASK);
 	}
 	}
 
 
-	for (idx = 0; idx < 15; idx++) {
+	for (idx = 0; idx < hsotg->num_of_eps; idx++) {
 		dev_info(dev,
 		dev_info(dev,
 			 "ep%d-in: EPCTL=0x%08x, SIZ=0x%08x, DMA=0x%08x\n", idx,
 			 "ep%d-in: EPCTL=0x%08x, SIZ=0x%08x, DMA=0x%08x\n", idx,
 			 readl(regs + DIEPCTL(idx)),
 			 readl(regs + DIEPCTL(idx)),
@@ -3131,6 +3391,103 @@ static void s3c_hsotg_dump(struct dwc2_hsotg *hsotg)
 #endif
 #endif
 }
 }
 
 
+/**
+ * testmode_write - debugfs: change usb test mode
+ * @seq: The seq file to write to.
+ * @v: Unused parameter.
+ *
+ * This debugfs entry modify the current usb test mode.
+ */
+static ssize_t testmode_write(struct file *file, const char __user *ubuf, size_t
+		count, loff_t *ppos)
+{
+	struct seq_file		*s = file->private_data;
+	struct dwc2_hsotg	*hsotg = s->private;
+	unsigned long		flags;
+	u32			testmode = 0;
+	char			buf[32];
+
+	if (copy_from_user(&buf, ubuf, min_t(size_t, sizeof(buf) - 1, count)))
+		return -EFAULT;
+
+	if (!strncmp(buf, "test_j", 6))
+		testmode = TEST_J;
+	else if (!strncmp(buf, "test_k", 6))
+		testmode = TEST_K;
+	else if (!strncmp(buf, "test_se0_nak", 12))
+		testmode = TEST_SE0_NAK;
+	else if (!strncmp(buf, "test_packet", 11))
+		testmode = TEST_PACKET;
+	else if (!strncmp(buf, "test_force_enable", 17))
+		testmode = TEST_FORCE_EN;
+	else
+		testmode = 0;
+
+	spin_lock_irqsave(&hsotg->lock, flags);
+	s3c_hsotg_set_test_mode(hsotg, testmode);
+	spin_unlock_irqrestore(&hsotg->lock, flags);
+	return count;
+}
+
+/**
+ * testmode_show - debugfs: show usb test mode state
+ * @seq: The seq file to write to.
+ * @v: Unused parameter.
+ *
+ * This debugfs entry shows which usb test mode is currently enabled.
+ */
+static int testmode_show(struct seq_file *s, void *unused)
+{
+	struct dwc2_hsotg *hsotg = s->private;
+	unsigned long flags;
+	int dctl;
+
+	spin_lock_irqsave(&hsotg->lock, flags);
+	dctl = readl(hsotg->regs + DCTL);
+	dctl &= DCTL_TSTCTL_MASK;
+	dctl >>= DCTL_TSTCTL_SHIFT;
+	spin_unlock_irqrestore(&hsotg->lock, flags);
+
+	switch (dctl) {
+	case 0:
+		seq_puts(s, "no test\n");
+		break;
+	case TEST_J:
+		seq_puts(s, "test_j\n");
+		break;
+	case TEST_K:
+		seq_puts(s, "test_k\n");
+		break;
+	case TEST_SE0_NAK:
+		seq_puts(s, "test_se0_nak\n");
+		break;
+	case TEST_PACKET:
+		seq_puts(s, "test_packet\n");
+		break;
+	case TEST_FORCE_EN:
+		seq_puts(s, "test_force_enable\n");
+		break;
+	default:
+		seq_printf(s, "UNKNOWN %d\n", dctl);
+	}
+
+	return 0;
+}
+
+static int testmode_open(struct inode *inode, struct file *file)
+{
+	return single_open(file, testmode_show, inode->i_private);
+}
+
+static const struct file_operations testmode_fops = {
+	.owner		= THIS_MODULE,
+	.open		= testmode_open,
+	.write		= testmode_write,
+	.read		= seq_read,
+	.llseek		= seq_lseek,
+	.release	= single_release,
+};
+
 /**
 /**
  * state_show - debugfs: show overall driver and device state.
  * state_show - debugfs: show overall driver and device state.
  * @seq: The seq file to write to.
  * @seq: The seq file to write to.
@@ -3168,7 +3525,7 @@ static int state_show(struct seq_file *seq, void *v)
 
 
 	seq_puts(seq, "\nEndpoint status:\n");
 	seq_puts(seq, "\nEndpoint status:\n");
 
 
-	for (idx = 0; idx < 15; idx++) {
+	for (idx = 0; idx < hsotg->num_of_eps; idx++) {
 		u32 in, out;
 		u32 in, out;
 
 
 		in = readl(regs + DIEPCTL(idx));
 		in = readl(regs + DIEPCTL(idx));
@@ -3227,7 +3584,7 @@ static int fifo_show(struct seq_file *seq, void *v)
 
 
 	seq_puts(seq, "\nPeriodic TXFIFOs:\n");
 	seq_puts(seq, "\nPeriodic TXFIFOs:\n");
 
 
-	for (idx = 1; idx <= 15; idx++) {
+	for (idx = 1; idx < hsotg->num_of_eps; idx++) {
 		val = readl(regs + DPTXFSIZN(idx));
 		val = readl(regs + DPTXFSIZN(idx));
 
 
 		seq_printf(seq, "\tDPTXFIFO%2d: Size %d, Start 0x%08x\n", idx,
 		seq_printf(seq, "\tDPTXFIFO%2d: Size %d, Start 0x%08x\n", idx,
@@ -3359,29 +3716,53 @@ static void s3c_hsotg_create_debug(struct dwc2_hsotg *hsotg)
 
 
 	/* create general state file */
 	/* create general state file */
 
 
-	hsotg->debug_file = debugfs_create_file("state", 0444, root,
+	hsotg->debug_file = debugfs_create_file("state", S_IRUGO, root,
 						hsotg, &state_fops);
 						hsotg, &state_fops);
 
 
 	if (IS_ERR(hsotg->debug_file))
 	if (IS_ERR(hsotg->debug_file))
 		dev_err(hsotg->dev, "%s: failed to create state\n", __func__);
 		dev_err(hsotg->dev, "%s: failed to create state\n", __func__);
 
 
-	hsotg->debug_fifo = debugfs_create_file("fifo", 0444, root,
+	hsotg->debug_testmode = debugfs_create_file("testmode",
+					S_IRUGO | S_IWUSR, root,
+					hsotg, &testmode_fops);
+
+	if (IS_ERR(hsotg->debug_testmode))
+		dev_err(hsotg->dev, "%s: failed to create testmode\n",
+				__func__);
+
+	hsotg->debug_fifo = debugfs_create_file("fifo", S_IRUGO, root,
 						hsotg, &fifo_fops);
 						hsotg, &fifo_fops);
 
 
 	if (IS_ERR(hsotg->debug_fifo))
 	if (IS_ERR(hsotg->debug_fifo))
 		dev_err(hsotg->dev, "%s: failed to create fifo\n", __func__);
 		dev_err(hsotg->dev, "%s: failed to create fifo\n", __func__);
 
 
-	/* create one file for each endpoint */
-
+	/* Create one file for each out endpoint */
 	for (epidx = 0; epidx < hsotg->num_of_eps; epidx++) {
 	for (epidx = 0; epidx < hsotg->num_of_eps; epidx++) {
-		struct s3c_hsotg_ep *ep = &hsotg->eps[epidx];
+		struct s3c_hsotg_ep *ep;
 
 
-		ep->debugfs = debugfs_create_file(ep->name, 0444,
-						  root, ep, &ep_fops);
+		ep = hsotg->eps_out[epidx];
+		if (ep) {
+			ep->debugfs = debugfs_create_file(ep->name, S_IRUGO,
+							  root, ep, &ep_fops);
 
 
-		if (IS_ERR(ep->debugfs))
-			dev_err(hsotg->dev, "failed to create %s debug file\n",
-				ep->name);
+			if (IS_ERR(ep->debugfs))
+				dev_err(hsotg->dev, "failed to create %s debug file\n",
+					ep->name);
+		}
+	}
+	/* Create one file for each in endpoint. EP0 is handled with out eps */
+	for (epidx = 1; epidx < hsotg->num_of_eps; epidx++) {
+		struct s3c_hsotg_ep *ep;
+
+		ep = hsotg->eps_in[epidx];
+		if (ep) {
+			ep->debugfs = debugfs_create_file(ep->name, S_IRUGO,
+							  root, ep, &ep_fops);
+
+			if (IS_ERR(ep->debugfs))
+				dev_err(hsotg->dev, "failed to create %s debug file\n",
+					ep->name);
+		}
 	}
 	}
 }
 }
 
 
@@ -3396,15 +3777,63 @@ static void s3c_hsotg_delete_debug(struct dwc2_hsotg *hsotg)
 	unsigned epidx;
 	unsigned epidx;
 
 
 	for (epidx = 0; epidx < hsotg->num_of_eps; epidx++) {
 	for (epidx = 0; epidx < hsotg->num_of_eps; epidx++) {
-		struct s3c_hsotg_ep *ep = &hsotg->eps[epidx];
-		debugfs_remove(ep->debugfs);
+		if (hsotg->eps_in[epidx])
+			debugfs_remove(hsotg->eps_in[epidx]->debugfs);
+		if (hsotg->eps_out[epidx])
+			debugfs_remove(hsotg->eps_out[epidx]->debugfs);
 	}
 	}
 
 
 	debugfs_remove(hsotg->debug_file);
 	debugfs_remove(hsotg->debug_file);
+	debugfs_remove(hsotg->debug_testmode);
 	debugfs_remove(hsotg->debug_fifo);
 	debugfs_remove(hsotg->debug_fifo);
 	debugfs_remove(hsotg->debug_root);
 	debugfs_remove(hsotg->debug_root);
 }
 }
 
 
+#ifdef CONFIG_OF
+static void s3c_hsotg_of_probe(struct dwc2_hsotg *hsotg)
+{
+	struct device_node *np = hsotg->dev->of_node;
+	u32 len = 0;
+	u32 i = 0;
+
+	/* Enable dma if requested in device tree */
+	hsotg->g_using_dma = of_property_read_bool(np, "g-use-dma");
+
+	/*
+	* Register TX periodic fifo size per endpoint.
+	* EP0 is excluded since it has no fifo configuration.
+	*/
+	if (!of_find_property(np, "g-tx-fifo-size", &len))
+		goto rx_fifo;
+
+	len /= sizeof(u32);
+
+	/* Read tx fifo sizes other than ep0 */
+	if (of_property_read_u32_array(np, "g-tx-fifo-size",
+						&hsotg->g_tx_fifo_sz[1], len))
+		goto rx_fifo;
+
+	/* Add ep0 */
+	len++;
+
+	/* Make remaining TX fifos unavailable */
+	if (len < MAX_EPS_CHANNELS) {
+		for (i = len; i < MAX_EPS_CHANNELS; i++)
+			hsotg->g_tx_fifo_sz[i] = 0;
+	}
+
+rx_fifo:
+	/* Register RX fifo size */
+	of_property_read_u32(np, "g-rx-fifo-size", &hsotg->g_rx_fifo_sz);
+
+	/* Register NPTX fifo size */
+	of_property_read_u32(np, "g-np-tx-fifo-size",
+						&hsotg->g_np_g_tx_fifo_sz);
+}
+#else
+static inline void s3c_hsotg_of_probe(struct dwc2_hsotg *hsotg) { }
+#endif
+
 /**
 /**
  * dwc2_gadget_init - init function for gadget
  * dwc2_gadget_init - init function for gadget
  * @dwc2: The data structure for the DWC2 driver.
  * @dwc2: The data structure for the DWC2 driver.
@@ -3414,41 +3843,47 @@ int dwc2_gadget_init(struct dwc2_hsotg *hsotg, int irq)
 {
 {
 	struct device *dev = hsotg->dev;
 	struct device *dev = hsotg->dev;
 	struct s3c_hsotg_plat *plat = dev->platform_data;
 	struct s3c_hsotg_plat *plat = dev->platform_data;
-	struct phy *phy;
-	struct usb_phy *uphy;
-	struct s3c_hsotg_ep *eps;
 	int epnum;
 	int epnum;
 	int ret;
 	int ret;
 	int i;
 	int i;
+	u32 p_tx_fifo[] = DWC2_G_P_LEGACY_TX_FIFO_SIZE;
 
 
 	/* Set default UTMI width */
 	/* Set default UTMI width */
 	hsotg->phyif = GUSBCFG_PHYIF16;
 	hsotg->phyif = GUSBCFG_PHYIF16;
 
 
+	s3c_hsotg_of_probe(hsotg);
+
+	/* Initialize to legacy fifo configuration values */
+	hsotg->g_rx_fifo_sz = 2048;
+	hsotg->g_np_g_tx_fifo_sz = 1024;
+	memcpy(&hsotg->g_tx_fifo_sz[1], p_tx_fifo, sizeof(p_tx_fifo));
+	/* Device tree specific probe */
+	s3c_hsotg_of_probe(hsotg);
+	/* Dump fifo information */
+	dev_dbg(dev, "NonPeriodic TXFIFO size: %d\n",
+						hsotg->g_np_g_tx_fifo_sz);
+	dev_dbg(dev, "RXFIFO size: %d\n", hsotg->g_rx_fifo_sz);
+	for (i = 0; i < MAX_EPS_CHANNELS; i++)
+		dev_dbg(dev, "Periodic TXFIFO%2d size: %d\n", i,
+						hsotg->g_tx_fifo_sz[i]);
 	/*
 	/*
-	 * Attempt to find a generic PHY, then look for an old style
-	 * USB PHY, finally fall back to pdata
+	 * If platform probe couldn't find a generic PHY or an old style
+	 * USB PHY, fall back to pdata
 	 */
 	 */
-	phy = devm_phy_get(dev, "usb2-phy");
-	if (IS_ERR(phy)) {
-		uphy = devm_usb_get_phy(dev, USB_PHY_TYPE_USB2);
-		if (IS_ERR(uphy)) {
-			/* Fallback for pdata */
-			plat = dev_get_platdata(dev);
-			if (!plat) {
-				dev_err(dev,
-				"no platform data or transceiver defined\n");
-				return -EPROBE_DEFER;
-			}
-			hsotg->plat = plat;
-		} else
-			hsotg->uphy = uphy;
-	} else {
-		hsotg->phy = phy;
+	if (IS_ERR_OR_NULL(hsotg->phy) && IS_ERR_OR_NULL(hsotg->uphy)) {
+		plat = dev_get_platdata(dev);
+		if (!plat) {
+			dev_err(dev,
+			"no platform data or transceiver defined\n");
+			return -EPROBE_DEFER;
+		}
+		hsotg->plat = plat;
+	} else if (hsotg->phy) {
 		/*
 		/*
 		 * If using the generic PHY framework, check if the PHY bus
 		 * If using the generic PHY framework, check if the PHY bus
 		 * width is 8-bit and set the phyif appropriately.
 		 * width is 8-bit and set the phyif appropriately.
 		 */
 		 */
-		if (phy_get_bus_width(phy) == 8)
+		if (phy_get_bus_width(hsotg->phy) == 8)
 			hsotg->phyif = GUSBCFG_PHYIF8;
 			hsotg->phyif = GUSBCFG_PHYIF8;
 	}
 	}
 
 
@@ -3488,16 +3923,53 @@ int dwc2_gadget_init(struct dwc2_hsotg *hsotg, int irq)
 
 
 	if (ret) {
 	if (ret) {
 		dev_err(dev, "failed to enable supplies: %d\n", ret);
 		dev_err(dev, "failed to enable supplies: %d\n", ret);
-		goto err_supplies;
+		goto err_clk;
 	}
 	}
 
 
 	/* usb phy enable */
 	/* usb phy enable */
 	s3c_hsotg_phy_enable(hsotg);
 	s3c_hsotg_phy_enable(hsotg);
 
 
+	/*
+	 * Force Device mode before initialization.
+	 * This allows correctly configuring fifo for device mode.
+	 */
+	__bic32(hsotg->regs + GUSBCFG, GUSBCFG_FORCEHOSTMODE);
+	__orr32(hsotg->regs + GUSBCFG, GUSBCFG_FORCEDEVMODE);
+
+	/*
+	 * According to Synopsys databook, this sleep is needed for the force
+	 * device mode to take effect.
+	 */
+	msleep(25);
+
 	s3c_hsotg_corereset(hsotg);
 	s3c_hsotg_corereset(hsotg);
-	s3c_hsotg_hw_cfg(hsotg);
+	ret = s3c_hsotg_hw_cfg(hsotg);
+	if (ret) {
+		dev_err(hsotg->dev, "Hardware configuration failed: %d\n", ret);
+		goto err_clk;
+	}
+
 	s3c_hsotg_init(hsotg);
 	s3c_hsotg_init(hsotg);
 
 
+	/* Switch back to default configuration */
+	__bic32(hsotg->regs + GUSBCFG, GUSBCFG_FORCEDEVMODE);
+
+	hsotg->ctrl_buff = devm_kzalloc(hsotg->dev,
+			DWC2_CTRL_BUFF_SIZE, GFP_KERNEL);
+	if (!hsotg->ctrl_buff) {
+		dev_err(dev, "failed to allocate ctrl request buff\n");
+		ret = -ENOMEM;
+		goto err_supplies;
+	}
+
+	hsotg->ep0_buff = devm_kzalloc(hsotg->dev,
+			DWC2_CTRL_BUFF_SIZE, GFP_KERNEL);
+	if (!hsotg->ep0_buff) {
+		dev_err(dev, "failed to allocate ctrl reply buff\n");
+		ret = -ENOMEM;
+		goto err_supplies;
+	}
+
 	ret = devm_request_irq(hsotg->dev, irq, s3c_hsotg_irq, IRQF_SHARED,
 	ret = devm_request_irq(hsotg->dev, irq, s3c_hsotg_irq, IRQF_SHARED,
 				dev_name(hsotg->dev), hsotg);
 				dev_name(hsotg->dev), hsotg);
 	if (ret < 0) {
 	if (ret < 0) {
@@ -3506,7 +3978,7 @@ int dwc2_gadget_init(struct dwc2_hsotg *hsotg, int irq)
 		regulator_bulk_disable(ARRAY_SIZE(hsotg->supplies),
 		regulator_bulk_disable(ARRAY_SIZE(hsotg->supplies),
 				       hsotg->supplies);
 				       hsotg->supplies);
 		dev_err(dev, "cannot claim IRQ for gadget\n");
 		dev_err(dev, "cannot claim IRQ for gadget\n");
-		goto err_clk;
+		goto err_supplies;
 	}
 	}
 
 
 	/* hsotg->num_of_eps holds number of EPs other than ep0 */
 	/* hsotg->num_of_eps holds number of EPs other than ep0 */
@@ -3517,33 +3989,30 @@ int dwc2_gadget_init(struct dwc2_hsotg *hsotg, int irq)
 		goto err_supplies;
 		goto err_supplies;
 	}
 	}
 
 
-	eps = kcalloc(hsotg->num_of_eps + 1, sizeof(struct s3c_hsotg_ep),
-		      GFP_KERNEL);
-	if (!eps) {
-		ret = -ENOMEM;
-		goto err_supplies;
-	}
-
-	hsotg->eps = eps;
-
 	/* setup endpoint information */
 	/* setup endpoint information */
 
 
 	INIT_LIST_HEAD(&hsotg->gadget.ep_list);
 	INIT_LIST_HEAD(&hsotg->gadget.ep_list);
-	hsotg->gadget.ep0 = &hsotg->eps[0].ep;
+	hsotg->gadget.ep0 = &hsotg->eps_out[0]->ep;
 
 
 	/* allocate EP0 request */
 	/* allocate EP0 request */
 
 
-	hsotg->ctrl_req = s3c_hsotg_ep_alloc_request(&hsotg->eps[0].ep,
+	hsotg->ctrl_req = s3c_hsotg_ep_alloc_request(&hsotg->eps_out[0]->ep,
 						     GFP_KERNEL);
 						     GFP_KERNEL);
 	if (!hsotg->ctrl_req) {
 	if (!hsotg->ctrl_req) {
 		dev_err(dev, "failed to allocate ctrl req\n");
 		dev_err(dev, "failed to allocate ctrl req\n");
 		ret = -ENOMEM;
 		ret = -ENOMEM;
-		goto err_ep_mem;
+		goto err_supplies;
 	}
 	}
 
 
 	/* initialise the endpoints now the core has been initialised */
 	/* initialise the endpoints now the core has been initialised */
-	for (epnum = 0; epnum < hsotg->num_of_eps; epnum++)
-		s3c_hsotg_initep(hsotg, &hsotg->eps[epnum], epnum);
+	for (epnum = 0; epnum < hsotg->num_of_eps; epnum++) {
+		if (hsotg->eps_in[epnum])
+			s3c_hsotg_initep(hsotg, hsotg->eps_in[epnum],
+								epnum, 1);
+		if (hsotg->eps_out[epnum])
+			s3c_hsotg_initep(hsotg, hsotg->eps_out[epnum],
+								epnum, 0);
+	}
 
 
 	/* disable power and clock */
 	/* disable power and clock */
 	s3c_hsotg_phy_disable(hsotg);
 	s3c_hsotg_phy_disable(hsotg);
@@ -3552,12 +4021,12 @@ int dwc2_gadget_init(struct dwc2_hsotg *hsotg, int irq)
 				    hsotg->supplies);
 				    hsotg->supplies);
 	if (ret) {
 	if (ret) {
 		dev_err(dev, "failed to disable supplies: %d\n", ret);
 		dev_err(dev, "failed to disable supplies: %d\n", ret);
-		goto err_ep_mem;
+		goto err_supplies;
 	}
 	}
 
 
 	ret = usb_add_gadget_udc(dev, &hsotg->gadget);
 	ret = usb_add_gadget_udc(dev, &hsotg->gadget);
 	if (ret)
 	if (ret)
-		goto err_ep_mem;
+		goto err_supplies;
 
 
 	s3c_hsotg_create_debug(hsotg);
 	s3c_hsotg_create_debug(hsotg);
 
 
@@ -3565,8 +4034,6 @@ int dwc2_gadget_init(struct dwc2_hsotg *hsotg, int irq)
 
 
 	return 0;
 	return 0;
 
 
-err_ep_mem:
-	kfree(eps);
 err_supplies:
 err_supplies:
 	s3c_hsotg_phy_disable(hsotg);
 	s3c_hsotg_phy_disable(hsotg);
 err_clk:
 err_clk:
@@ -3612,8 +4079,12 @@ int s3c_hsotg_suspend(struct dwc2_hsotg *hsotg)
 
 
 		s3c_hsotg_phy_disable(hsotg);
 		s3c_hsotg_phy_disable(hsotg);
 
 
-		for (ep = 0; ep < hsotg->num_of_eps; ep++)
-			s3c_hsotg_ep_disable(&hsotg->eps[ep].ep);
+		for (ep = 0; ep < hsotg->num_of_eps; ep++) {
+			if (hsotg->eps_in[ep])
+				s3c_hsotg_ep_disable(&hsotg->eps_in[ep]->ep);
+			if (hsotg->eps_out[ep])
+				s3c_hsotg_ep_disable(&hsotg->eps_out[ep]->ep);
+		}
 
 
 		ret = regulator_bulk_disable(ARRAY_SIZE(hsotg->supplies),
 		ret = regulator_bulk_disable(ARRAY_SIZE(hsotg->supplies),
 					     hsotg->supplies);
 					     hsotg->supplies);
@@ -3644,7 +4115,7 @@ int s3c_hsotg_resume(struct dwc2_hsotg *hsotg)
 		s3c_hsotg_phy_enable(hsotg);
 		s3c_hsotg_phy_enable(hsotg);
 
 
 		spin_lock_irqsave(&hsotg->lock, flags);
 		spin_lock_irqsave(&hsotg->lock, flags);
-		s3c_hsotg_core_init_disconnected(hsotg);
+		s3c_hsotg_core_init_disconnected(hsotg, false);
 		if (hsotg->enabled)
 		if (hsotg->enabled)
 			s3c_hsotg_core_connect(hsotg);
 			s3c_hsotg_core_connect(hsotg);
 		spin_unlock_irqrestore(&hsotg->lock, flags);
 		spin_unlock_irqrestore(&hsotg->lock, flags);

+ 16 - 80
drivers/usb/dwc2/hcd.c

@@ -316,10 +316,12 @@ void dwc2_hcd_disconnect(struct dwc2_hsotg *hsotg)
  */
  */
 static void dwc2_hcd_rem_wakeup(struct dwc2_hsotg *hsotg)
 static void dwc2_hcd_rem_wakeup(struct dwc2_hsotg *hsotg)
 {
 {
-	if (hsotg->lx_state == DWC2_L2)
+	if (hsotg->lx_state == DWC2_L2) {
 		hsotg->flags.b.port_suspend_change = 1;
 		hsotg->flags.b.port_suspend_change = 1;
-	else
+		usb_hcd_resume_root_hub(hsotg->priv);
+	} else {
 		hsotg->flags.b.port_l1_change = 1;
 		hsotg->flags.b.port_l1_change = 1;
+	}
 }
 }
 
 
 /**
 /**
@@ -1371,7 +1373,7 @@ static void dwc2_conn_id_status_change(struct work_struct *work)
 		hsotg->op_state = OTG_STATE_B_PERIPHERAL;
 		hsotg->op_state = OTG_STATE_B_PERIPHERAL;
 		dwc2_core_init(hsotg, false, -1);
 		dwc2_core_init(hsotg, false, -1);
 		dwc2_enable_global_interrupts(hsotg);
 		dwc2_enable_global_interrupts(hsotg);
-		s3c_hsotg_core_init_disconnected(hsotg);
+		s3c_hsotg_core_init_disconnected(hsotg, false);
 		s3c_hsotg_core_connect(hsotg);
 		s3c_hsotg_core_connect(hsotg);
 	} else {
 	} else {
 		/* A-Device connector (Host Mode) */
 		/* A-Device connector (Host Mode) */
@@ -1473,30 +1475,6 @@ static void dwc2_port_suspend(struct dwc2_hsotg *hsotg, u16 windex)
 	}
 	}
 }
 }
 
 
-static void dwc2_port_resume(struct dwc2_hsotg *hsotg)
-{
-	u32 hprt0;
-
-	/* After clear the Stop PHY clock bit, we should wait for a moment
-	 * for PLL work stable with clock output.
-	 */
-	writel(0, hsotg->regs + PCGCTL);
-	usleep_range(2000, 4000);
-
-	hprt0 = dwc2_read_hprt0(hsotg);
-	hprt0 |= HPRT0_RES;
-	writel(hprt0, hsotg->regs + HPRT0);
-	hprt0 &= ~HPRT0_SUSP;
-	/* according to USB2.0 Spec 7.1.7.7, the host must send the resume
-	 * signal for at least 20ms
-	 */
-	usleep_range(20000, 25000);
-
-	hprt0 &= ~HPRT0_RES;
-	writel(hprt0, hsotg->regs + HPRT0);
-	hsotg->lx_state = DWC2_L0;
-}
-
 /* Handles hub class-specific requests */
 /* Handles hub class-specific requests */
 static int dwc2_hcd_hub_control(struct dwc2_hsotg *hsotg, u16 typereq,
 static int dwc2_hcd_hub_control(struct dwc2_hsotg *hsotg, u16 typereq,
 				u16 wvalue, u16 windex, char *buf, u16 wlength)
 				u16 wvalue, u16 windex, char *buf, u16 wlength)
@@ -1542,7 +1520,17 @@ static int dwc2_hcd_hub_control(struct dwc2_hsotg *hsotg, u16 typereq,
 		case USB_PORT_FEAT_SUSPEND:
 		case USB_PORT_FEAT_SUSPEND:
 			dev_dbg(hsotg->dev,
 			dev_dbg(hsotg->dev,
 				"ClearPortFeature USB_PORT_FEAT_SUSPEND\n");
 				"ClearPortFeature USB_PORT_FEAT_SUSPEND\n");
-			dwc2_port_resume(hsotg);
+			writel(0, hsotg->regs + PCGCTL);
+			usleep_range(20000, 40000);
+
+			hprt0 = dwc2_read_hprt0(hsotg);
+			hprt0 |= HPRT0_RES;
+			writel(hprt0, hsotg->regs + HPRT0);
+			hprt0 &= ~HPRT0_SUSP;
+			usleep_range(100000, 150000);
+
+			hprt0 &= ~HPRT0_RES;
+			writel(hprt0, hsotg->regs + HPRT0);
 			break;
 			break;
 
 
 		case USB_PORT_FEAT_POWER:
 		case USB_PORT_FEAT_POWER:
@@ -2317,55 +2305,6 @@ static void _dwc2_hcd_stop(struct usb_hcd *hcd)
 	usleep_range(1000, 3000);
 	usleep_range(1000, 3000);
 }
 }
 
 
-static int _dwc2_hcd_suspend(struct usb_hcd *hcd)
-{
-	struct dwc2_hsotg *hsotg = dwc2_hcd_to_hsotg(hcd);
-	u32 hprt0;
-
-	if (!((hsotg->op_state == OTG_STATE_B_HOST) ||
-		(hsotg->op_state == OTG_STATE_A_HOST)))
-		return 0;
-
-	/* TODO: We get into suspend from 'on' state, maybe we need to do
-	 * something if we get here from DWC2_L1(LPM sleep) state one day.
-	 */
-	if (hsotg->lx_state != DWC2_L0)
-		return 0;
-
-	hprt0 = dwc2_read_hprt0(hsotg);
-	if (hprt0 & HPRT0_CONNSTS) {
-		dwc2_port_suspend(hsotg, 1);
-	} else {
-		u32 pcgctl = readl(hsotg->regs + PCGCTL);
-
-		pcgctl |= PCGCTL_STOPPCLK;
-		writel(pcgctl, hsotg->regs + PCGCTL);
-	}
-
-	return 0;
-}
-
-static int _dwc2_hcd_resume(struct usb_hcd *hcd)
-{
-	struct dwc2_hsotg *hsotg = dwc2_hcd_to_hsotg(hcd);
-	u32 hprt0;
-
-	if (!((hsotg->op_state == OTG_STATE_B_HOST) ||
-		(hsotg->op_state == OTG_STATE_A_HOST)))
-		return 0;
-
-	if (hsotg->lx_state != DWC2_L2)
-		return 0;
-
-	hprt0 = dwc2_read_hprt0(hsotg);
-	if ((hprt0 & HPRT0_CONNSTS) && (hprt0 & HPRT0_SUSP))
-		dwc2_port_resume(hsotg);
-	else
-		writel(0, hsotg->regs + PCGCTL);
-
-	return 0;
-}
-
 /* Returns the current frame number */
 /* Returns the current frame number */
 static int _dwc2_hcd_get_frame_number(struct usb_hcd *hcd)
 static int _dwc2_hcd_get_frame_number(struct usb_hcd *hcd)
 {
 {
@@ -2736,9 +2675,6 @@ static struct hc_driver dwc2_hc_driver = {
 	.hub_status_data = _dwc2_hcd_hub_status_data,
 	.hub_status_data = _dwc2_hcd_hub_status_data,
 	.hub_control = _dwc2_hcd_hub_control,
 	.hub_control = _dwc2_hcd_hub_control,
 	.clear_tt_buffer_complete = _dwc2_hcd_clear_tt_buffer_complete,
 	.clear_tt_buffer_complete = _dwc2_hcd_clear_tt_buffer_complete,
-
-	.bus_suspend = _dwc2_hcd_suspend,
-	.bus_resume = _dwc2_hcd_resume,
 };
 };
 
 
 /*
 /*

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

@@ -294,6 +294,7 @@
 #define GHWCFG4_NUM_IN_EPS_MASK			(0xf << 26)
 #define GHWCFG4_NUM_IN_EPS_MASK			(0xf << 26)
 #define GHWCFG4_NUM_IN_EPS_SHIFT		26
 #define GHWCFG4_NUM_IN_EPS_SHIFT		26
 #define GHWCFG4_DED_FIFO_EN			(1 << 25)
 #define GHWCFG4_DED_FIFO_EN			(1 << 25)
+#define GHWCFG4_DED_FIFO_SHIFT		25
 #define GHWCFG4_SESSION_END_FILT_EN		(1 << 24)
 #define GHWCFG4_SESSION_END_FILT_EN		(1 << 24)
 #define GHWCFG4_B_VALID_FILT_EN			(1 << 23)
 #define GHWCFG4_B_VALID_FILT_EN			(1 << 23)
 #define GHWCFG4_A_VALID_FILT_EN			(1 << 22)
 #define GHWCFG4_A_VALID_FILT_EN			(1 << 22)
@@ -541,6 +542,7 @@
 
 
 #define DIEPINT(_a)			HSOTG_REG(0x908 + ((_a) * 0x20))
 #define DIEPINT(_a)			HSOTG_REG(0x908 + ((_a) * 0x20))
 #define DOEPINT(_a)			HSOTG_REG(0xB08 + ((_a) * 0x20))
 #define DOEPINT(_a)			HSOTG_REG(0xB08 + ((_a) * 0x20))
+#define DXEPINT_SETUP_RCVD		(1 << 15)
 #define DXEPINT_INEPNAKEFF		(1 << 6)
 #define DXEPINT_INEPNAKEFF		(1 << 6)
 #define DXEPINT_BACK2BACKSETUP		(1 << 6)
 #define DXEPINT_BACK2BACKSETUP		(1 << 6)
 #define DXEPINT_INTKNEPMIS		(1 << 5)
 #define DXEPINT_INTKNEPMIS		(1 << 5)

+ 34 - 2
drivers/usb/dwc2/platform.c

@@ -155,6 +155,8 @@ static int dwc2_driver_probe(struct platform_device *dev)
 	struct dwc2_core_params defparams;
 	struct dwc2_core_params defparams;
 	struct dwc2_hsotg *hsotg;
 	struct dwc2_hsotg *hsotg;
 	struct resource *res;
 	struct resource *res;
+	struct phy *phy;
+	struct usb_phy *uphy;
 	int retval;
 	int retval;
 	int irq;
 	int irq;
 
 
@@ -212,6 +214,24 @@ static int dwc2_driver_probe(struct platform_device *dev)
 
 
 	hsotg->dr_mode = of_usb_get_dr_mode(dev->dev.of_node);
 	hsotg->dr_mode = of_usb_get_dr_mode(dev->dev.of_node);
 
 
+	/*
+	 * Attempt to find a generic PHY, then look for an old style
+	 * USB PHY
+	 */
+	phy = devm_phy_get(&dev->dev, "usb2-phy");
+	if (IS_ERR(phy)) {
+		hsotg->phy = NULL;
+		uphy = devm_usb_get_phy(&dev->dev, USB_PHY_TYPE_USB2);
+		if (IS_ERR(uphy))
+			hsotg->uphy = NULL;
+		else
+			hsotg->uphy = uphy;
+	} else {
+		hsotg->phy = phy;
+		phy_power_on(hsotg->phy);
+		phy_init(hsotg->phy);
+	}
+
 	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);
 	retval = dwc2_gadget_init(hsotg, irq);
@@ -231,8 +251,15 @@ static int __maybe_unused dwc2_suspend(struct device *dev)
 	struct dwc2_hsotg *dwc2 = dev_get_drvdata(dev);
 	struct dwc2_hsotg *dwc2 = dev_get_drvdata(dev);
 	int ret = 0;
 	int ret = 0;
 
 
-	if (dwc2_is_device_mode(dwc2))
+	if (dwc2_is_device_mode(dwc2)) {
 		ret = s3c_hsotg_suspend(dwc2);
 		ret = s3c_hsotg_suspend(dwc2);
+	} else {
+		if (dwc2->lx_state == DWC2_L0)
+			return 0;
+		phy_exit(dwc2->phy);
+		phy_power_off(dwc2->phy);
+
+	}
 	return ret;
 	return ret;
 }
 }
 
 
@@ -241,8 +268,13 @@ static int __maybe_unused dwc2_resume(struct device *dev)
 	struct dwc2_hsotg *dwc2 = dev_get_drvdata(dev);
 	struct dwc2_hsotg *dwc2 = dev_get_drvdata(dev);
 	int ret = 0;
 	int ret = 0;
 
 
-	if (dwc2_is_device_mode(dwc2))
+	if (dwc2_is_device_mode(dwc2)) {
 		ret = s3c_hsotg_resume(dwc2);
 		ret = s3c_hsotg_resume(dwc2);
+	} else {
+		phy_power_on(dwc2->phy);
+		phy_init(dwc2->phy);
+
+	}
 	return ret;
 	return ret;
 }
 }
 
 

+ 0 - 6
drivers/usb/dwc3/Kconfig

@@ -104,12 +104,6 @@ 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 USB_DWC3_VERBOSE
-	bool "Enable Verbose Debugging Messages"
-	depends on USB_DWC3_DEBUG
-	help
-	  Say Y here to enable verbose debugging messages on DWC3 Driver.
-
 config DWC3_HOST_USB3_LPM_ENABLE
 config DWC3_HOST_USB3_LPM_ENABLE
 	bool "Enable USB3 LPM Capability"
 	bool "Enable USB3 LPM Capability"
 	depends on USB_DWC3_HOST=y || USB_DWC3_DUAL_ROLE=y
 	depends on USB_DWC3_HOST=y || USB_DWC3_DUAL_ROLE=y

+ 0 - 1
drivers/usb/dwc3/Makefile

@@ -2,7 +2,6 @@
 CFLAGS_trace.o				:= -I$(src)
 CFLAGS_trace.o				:= -I$(src)
 
 
 ccflags-$(CONFIG_USB_DWC3_DEBUG)	:= -DDEBUG
 ccflags-$(CONFIG_USB_DWC3_DEBUG)	:= -DDEBUG
-ccflags-$(CONFIG_USB_DWC3_VERBOSE)	+= -DVERBOSE_DEBUG
 
 
 obj-$(CONFIG_USB_DWC3)			+= dwc3.o
 obj-$(CONFIG_USB_DWC3)			+= dwc3.o
 
 

+ 1 - 1
drivers/usb/dwc3/core.c

@@ -345,7 +345,7 @@ static void dwc3_core_num_eps(struct dwc3 *dwc)
 	dwc->num_in_eps = DWC3_NUM_IN_EPS(parms);
 	dwc->num_in_eps = DWC3_NUM_IN_EPS(parms);
 	dwc->num_out_eps = DWC3_NUM_EPS(parms) - dwc->num_in_eps;
 	dwc->num_out_eps = DWC3_NUM_EPS(parms) - dwc->num_in_eps;
 
 
-	dev_vdbg(dwc->dev, "found %d IN and %d OUT endpoints\n",
+	dwc3_trace(trace_dwc3_core, "found %d IN and %d OUT endpoints",
 			dwc->num_in_eps, dwc->num_out_eps);
 			dwc->num_in_eps, dwc->num_out_eps);
 }
 }
 
 

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

@@ -431,7 +431,6 @@ struct dwc3_event_buffer {
  * @dwc: pointer to DWC controller
  * @dwc: pointer to DWC controller
  * @saved_state: ep state saved during hibernation
  * @saved_state: ep state saved during hibernation
  * @flags: endpoint flags (wedged, stalled, ...)
  * @flags: endpoint flags (wedged, stalled, ...)
- * @current_trb: index of current used trb
  * @number: endpoint number (1 - 15)
  * @number: endpoint number (1 - 15)
  * @type: set to bmAttributes & USB_ENDPOINT_XFERTYPE_MASK
  * @type: set to bmAttributes & USB_ENDPOINT_XFERTYPE_MASK
  * @resource_index: Resource transfer index
  * @resource_index: Resource transfer index
@@ -464,8 +463,6 @@ struct dwc3_ep {
 	/* This last one is specific to EP0 */
 	/* This last one is specific to EP0 */
 #define DWC3_EP0_DIR_IN		(1 << 31)
 #define DWC3_EP0_DIR_IN		(1 << 31)
 
 
-	unsigned		current_trb;
-
 	u8			number;
 	u8			number;
 	u8			type;
 	u8			type;
 	u8			resource_index;
 	u8			resource_index;
@@ -685,7 +682,6 @@ struct dwc3_scratchpad_array {
  * @is_utmi_l1_suspend: the core asserts output signal
  * @is_utmi_l1_suspend: the core asserts output signal
  * 	0	- utmi_sleep_n
  * 	0	- utmi_sleep_n
  * 	1	- utmi_l1_suspend_n
  * 	1	- utmi_l1_suspend_n
- * @is_selfpowered: true when we are selfpowered
  * @is_fpga: true when we are using the FPGA board
  * @is_fpga: true when we are using the FPGA board
  * @needs_fifo_resize: not all users might want fifo resizing, flag it
  * @needs_fifo_resize: not all users might want fifo resizing, flag it
  * @pullups_connected: true when Run/Stop bit is set
  * @pullups_connected: true when Run/Stop bit is set
@@ -809,7 +805,6 @@ struct dwc3 {
 	unsigned		has_hibernation:1;
 	unsigned		has_hibernation:1;
 	unsigned		has_lpm_erratum:1;
 	unsigned		has_lpm_erratum:1;
 	unsigned		is_utmi_l1_suspend:1;
 	unsigned		is_utmi_l1_suspend:1;
-	unsigned		is_selfpowered:1;
 	unsigned		is_fpga:1;
 	unsigned		is_fpga:1;
 	unsigned		needs_fifo_resize:1;
 	unsigned		needs_fifo_resize:1;
 	unsigned		pullups_connected:1;
 	unsigned		pullups_connected:1;

+ 32 - 146
drivers/usb/dwc3/dwc3-pci.c

@@ -22,9 +22,6 @@
 #include <linux/pci.h>
 #include <linux/pci.h>
 #include <linux/platform_device.h>
 #include <linux/platform_device.h>
 
 
-#include <linux/usb/otg.h>
-#include <linux/usb/usb_phy_generic.h>
-
 #include "platform_data.h"
 #include "platform_data.h"
 
 
 /* FIXME define these in <linux/pci_ids.h> */
 /* FIXME define these in <linux/pci_ids.h> */
@@ -36,66 +33,41 @@
 #define PCI_DEVICE_ID_INTEL_SPTLP	0x9d30
 #define PCI_DEVICE_ID_INTEL_SPTLP	0x9d30
 #define PCI_DEVICE_ID_INTEL_SPTH	0xa130
 #define PCI_DEVICE_ID_INTEL_SPTH	0xa130
 
 
-struct dwc3_pci {
-	struct device		*dev;
-	struct platform_device	*dwc3;
-	struct platform_device	*usb2_phy;
-	struct platform_device	*usb3_phy;
-};
-
-static int dwc3_pci_register_phys(struct dwc3_pci *glue)
+static int dwc3_pci_quirks(struct pci_dev *pdev)
 {
 {
-	struct usb_phy_generic_platform_data pdata;
-	struct platform_device	*pdev;
-	int			ret;
+	if (pdev->vendor == PCI_VENDOR_ID_AMD &&
+	    pdev->device == PCI_DEVICE_ID_AMD_NL_USB) {
+		struct dwc3_platform_data pdata;
 
 
-	memset(&pdata, 0x00, sizeof(pdata));
+		memset(&pdata, 0, sizeof(pdata));
 
 
-	pdev = platform_device_alloc("usb_phy_generic", 0);
-	if (!pdev)
-		return -ENOMEM;
-
-	glue->usb2_phy = pdev;
-	pdata.type = USB_PHY_TYPE_USB2;
-	pdata.gpio_reset = -1;
+		pdata.has_lpm_erratum = true;
+		pdata.lpm_nyet_threshold = 0xf;
 
 
-	ret = platform_device_add_data(glue->usb2_phy, &pdata, sizeof(pdata));
-	if (ret)
-		goto err1;
+		pdata.u2exit_lfps_quirk = true;
+		pdata.u2ss_inp3_quirk = true;
+		pdata.req_p1p2p3_quirk = true;
+		pdata.del_p1p2p3_quirk = true;
+		pdata.del_phy_power_chg_quirk = true;
+		pdata.lfps_filter_quirk = true;
+		pdata.rx_detect_poll_quirk = true;
 
 
-	pdev = platform_device_alloc("usb_phy_generic", 1);
-	if (!pdev) {
-		ret = -ENOMEM;
-		goto err1;
-	}
+		pdata.tx_de_emphasis_quirk = true;
+		pdata.tx_de_emphasis = 1;
 
 
-	glue->usb3_phy = pdev;
-	pdata.type = USB_PHY_TYPE_USB3;
-
-	ret = platform_device_add_data(glue->usb3_phy, &pdata, sizeof(pdata));
-	if (ret)
-		goto err2;
-
-	ret = platform_device_add(glue->usb2_phy);
-	if (ret)
-		goto err2;
+		/*
+		 * FIXME these quirks should be removed when AMD NL
+		 * taps out
+		 */
+		pdata.disable_scramble_quirk = true;
+		pdata.dis_u3_susphy_quirk = true;
+		pdata.dis_u2_susphy_quirk = true;
 
 
-	ret = platform_device_add(glue->usb3_phy);
-	if (ret)
-		goto err3;
+		return platform_device_add_data(pci_get_drvdata(pdev), &pdata,
+						sizeof(pdata));
+	}
 
 
 	return 0;
 	return 0;
-
-err3:
-	platform_device_del(glue->usb2_phy);
-
-err2:
-	platform_device_put(glue->usb3_phy);
-
-err1:
-	platform_device_put(glue->usb2_phy);
-
-	return ret;
 }
 }
 
 
 static int dwc3_pci_probe(struct pci_dev *pci,
 static int dwc3_pci_probe(struct pci_dev *pci,
@@ -103,18 +75,8 @@ static int dwc3_pci_probe(struct pci_dev *pci,
 {
 {
 	struct resource		res[2];
 	struct resource		res[2];
 	struct platform_device	*dwc3;
 	struct platform_device	*dwc3;
-	struct dwc3_pci		*glue;
 	int			ret;
 	int			ret;
 	struct device		*dev = &pci->dev;
 	struct device		*dev = &pci->dev;
-	struct dwc3_platform_data dwc3_pdata;
-
-	memset(&dwc3_pdata, 0x00, sizeof(dwc3_pdata));
-
-	glue = devm_kzalloc(dev, sizeof(*glue), GFP_KERNEL);
-	if (!glue)
-		return -ENOMEM;
-
-	glue->dev = dev;
 
 
 	ret = pcim_enable_device(pci);
 	ret = pcim_enable_device(pci);
 	if (ret) {
 	if (ret) {
@@ -124,12 +86,6 @@ static int dwc3_pci_probe(struct pci_dev *pci,
 
 
 	pci_set_master(pci);
 	pci_set_master(pci);
 
 
-	ret = dwc3_pci_register_phys(glue);
-	if (ret) {
-		dev_err(dev, "couldn't register PHYs\n");
-		return ret;
-	}
-
 	dwc3 = platform_device_alloc("dwc3", PLATFORM_DEVID_AUTO);
 	dwc3 = platform_device_alloc("dwc3", PLATFORM_DEVID_AUTO);
 	if (!dwc3) {
 	if (!dwc3) {
 		dev_err(dev, "couldn't allocate dwc3 device\n");
 		dev_err(dev, "couldn't allocate dwc3 device\n");
@@ -147,70 +103,34 @@ static int dwc3_pci_probe(struct pci_dev *pci,
 	res[1].name	= "dwc_usb3";
 	res[1].name	= "dwc_usb3";
 	res[1].flags	= IORESOURCE_IRQ;
 	res[1].flags	= IORESOURCE_IRQ;
 
 
-	if (pci->vendor == PCI_VENDOR_ID_AMD &&
-			pci->device == PCI_DEVICE_ID_AMD_NL_USB) {
-		dwc3_pdata.has_lpm_erratum = true;
-		dwc3_pdata.lpm_nyet_threshold = 0xf;
-
-		dwc3_pdata.u2exit_lfps_quirk = true;
-		dwc3_pdata.u2ss_inp3_quirk = true;
-		dwc3_pdata.req_p1p2p3_quirk = true;
-		dwc3_pdata.del_p1p2p3_quirk = true;
-		dwc3_pdata.del_phy_power_chg_quirk = true;
-		dwc3_pdata.lfps_filter_quirk = true;
-		dwc3_pdata.rx_detect_poll_quirk = true;
-
-		dwc3_pdata.tx_de_emphasis_quirk = true;
-		dwc3_pdata.tx_de_emphasis = 1;
-
-		/*
-		 * FIXME these quirks should be removed when AMD NL
-		 * taps out
-		 */
-		dwc3_pdata.disable_scramble_quirk = true;
-		dwc3_pdata.dis_u3_susphy_quirk = true;
-		dwc3_pdata.dis_u2_susphy_quirk = true;
-	}
-
 	ret = platform_device_add_resources(dwc3, res, ARRAY_SIZE(res));
 	ret = platform_device_add_resources(dwc3, res, ARRAY_SIZE(res));
 	if (ret) {
 	if (ret) {
 		dev_err(dev, "couldn't add resources to dwc3 device\n");
 		dev_err(dev, "couldn't add resources to dwc3 device\n");
 		return ret;
 		return ret;
 	}
 	}
 
 
-	pci_set_drvdata(pci, glue);
-
-	ret = platform_device_add_data(dwc3, &dwc3_pdata, sizeof(dwc3_pdata));
+	pci_set_drvdata(pci, dwc3);
+	ret = dwc3_pci_quirks(pci);
 	if (ret)
 	if (ret)
-		goto err3;
-
-	dma_set_coherent_mask(&dwc3->dev, dev->coherent_dma_mask);
+		goto err;
 
 
-	dwc3->dev.dma_mask = dev->dma_mask;
-	dwc3->dev.dma_parms = dev->dma_parms;
 	dwc3->dev.parent = dev;
 	dwc3->dev.parent = dev;
-	glue->dwc3 = dwc3;
 
 
 	ret = platform_device_add(dwc3);
 	ret = platform_device_add(dwc3);
 	if (ret) {
 	if (ret) {
 		dev_err(dev, "failed to register dwc3 device\n");
 		dev_err(dev, "failed to register dwc3 device\n");
-		goto err3;
+		goto err;
 	}
 	}
 
 
 	return 0;
 	return 0;
-
-err3:
+err:
 	platform_device_put(dwc3);
 	platform_device_put(dwc3);
 	return ret;
 	return ret;
 }
 }
 
 
 static void dwc3_pci_remove(struct pci_dev *pci)
 static void dwc3_pci_remove(struct pci_dev *pci)
 {
 {
-	struct dwc3_pci	*glue = pci_get_drvdata(pci);
-
-	platform_device_unregister(glue->dwc3);
-	platform_device_unregister(glue->usb2_phy);
-	platform_device_unregister(glue->usb3_phy);
+	platform_device_unregister(pci_get_drvdata(pci));
 }
 }
 
 
 static const struct pci_device_id dwc3_pci_id_table[] = {
 static const struct pci_device_id dwc3_pci_id_table[] = {
@@ -228,45 +148,11 @@ static const struct pci_device_id dwc3_pci_id_table[] = {
 };
 };
 MODULE_DEVICE_TABLE(pci, dwc3_pci_id_table);
 MODULE_DEVICE_TABLE(pci, dwc3_pci_id_table);
 
 
-#ifdef CONFIG_PM_SLEEP
-static int dwc3_pci_suspend(struct device *dev)
-{
-	struct pci_dev	*pci = to_pci_dev(dev);
-
-	pci_disable_device(pci);
-
-	return 0;
-}
-
-static int dwc3_pci_resume(struct device *dev)
-{
-	struct pci_dev	*pci = to_pci_dev(dev);
-	int		ret;
-
-	ret = pci_enable_device(pci);
-	if (ret) {
-		dev_err(dev, "can't re-enable device --> %d\n", ret);
-		return ret;
-	}
-
-	pci_set_master(pci);
-
-	return 0;
-}
-#endif /* CONFIG_PM_SLEEP */
-
-static const struct dev_pm_ops dwc3_pci_dev_pm_ops = {
-	SET_SYSTEM_SLEEP_PM_OPS(dwc3_pci_suspend, dwc3_pci_resume)
-};
-
 static struct pci_driver dwc3_pci_driver = {
 static struct pci_driver dwc3_pci_driver = {
 	.name		= "dwc3-pci",
 	.name		= "dwc3-pci",
 	.id_table	= dwc3_pci_id_table,
 	.id_table	= dwc3_pci_id_table,
 	.probe		= dwc3_pci_probe,
 	.probe		= dwc3_pci_probe,
 	.remove		= dwc3_pci_remove,
 	.remove		= dwc3_pci_remove,
-	.driver		= {
-		.pm	= &dwc3_pci_dev_pm_ops,
-	},
 };
 };
 
 
 MODULE_AUTHOR("Felipe Balbi <balbi@ti.com>");
 MODULE_AUTHOR("Felipe Balbi <balbi@ti.com>");

+ 1 - 1
drivers/usb/dwc3/ep0.c

@@ -344,7 +344,7 @@ static int dwc3_ep0_handle_status(struct dwc3 *dwc,
 		/*
 		/*
 		 * LTM will be set once we know how to set this in HW.
 		 * LTM will be set once we know how to set this in HW.
 		 */
 		 */
-		usb_status |= dwc->is_selfpowered << USB_DEVICE_SELF_POWERED;
+		usb_status |= dwc->gadget.is_selfpowered;
 
 
 		if (dwc->speed == DWC3_DSTS_SUPERSPEED) {
 		if (dwc->speed == DWC3_DSTS_SUPERSPEED) {
 			reg = dwc3_readl(dwc->regs, DWC3_DCTL);
 			reg = dwc3_readl(dwc->regs, DWC3_DCTL);

+ 43 - 39
drivers/usb/dwc3/gadget.c

@@ -139,7 +139,8 @@ int dwc3_gadget_set_link_state(struct dwc3 *dwc, enum dwc3_link_state state)
 		udelay(5);
 		udelay(5);
 	}
 	}
 
 
-	dev_vdbg(dwc->dev, "link state change request timed out\n");
+	dwc3_trace(trace_dwc3_gadget,
+			"link state change request timed out");
 
 
 	return -ETIMEDOUT;
 	return -ETIMEDOUT;
 }
 }
@@ -219,7 +220,7 @@ int dwc3_gadget_resize_tx_fifos(struct dwc3 *dwc)
 
 
 		fifo_size |= (last_fifo_depth << 16);
 		fifo_size |= (last_fifo_depth << 16);
 
 
-		dev_vdbg(dwc->dev, "%s: Fifo Addr %04x Size %d\n",
+		dwc3_trace(trace_dwc3_gadget, "%s: Fifo Addr %04x Size %d",
 				dep->name, last_fifo_depth, fifo_size & 0xffff);
 				dep->name, last_fifo_depth, fifo_size & 0xffff);
 
 
 		dwc3_writel(dwc->regs, DWC3_GTXFIFOSIZ(num), fifo_size);
 		dwc3_writel(dwc->regs, DWC3_GTXFIFOSIZ(num), fifo_size);
@@ -287,7 +288,8 @@ int dwc3_send_gadget_generic_command(struct dwc3 *dwc, unsigned cmd, u32 param)
 	do {
 	do {
 		reg = dwc3_readl(dwc->regs, DWC3_DGCMD);
 		reg = dwc3_readl(dwc->regs, DWC3_DGCMD);
 		if (!(reg & DWC3_DGCMD_CMDACT)) {
 		if (!(reg & DWC3_DGCMD_CMDACT)) {
-			dev_vdbg(dwc->dev, "Command Complete --> %d\n",
+			dwc3_trace(trace_dwc3_gadget,
+					"Command Complete --> %d",
 					DWC3_DGCMD_STATUS(reg));
 					DWC3_DGCMD_STATUS(reg));
 			return 0;
 			return 0;
 		}
 		}
@@ -297,8 +299,11 @@ int dwc3_send_gadget_generic_command(struct dwc3 *dwc, unsigned cmd, u32 param)
 		 * interrupt context.
 		 * interrupt context.
 		 */
 		 */
 		timeout--;
 		timeout--;
-		if (!timeout)
+		if (!timeout) {
+			dwc3_trace(trace_dwc3_gadget,
+					"Command Timed Out");
 			return -ETIMEDOUT;
 			return -ETIMEDOUT;
+		}
 		udelay(1);
 		udelay(1);
 	} while (1);
 	} while (1);
 }
 }
@@ -320,7 +325,8 @@ int dwc3_send_gadget_ep_cmd(struct dwc3 *dwc, unsigned ep,
 	do {
 	do {
 		reg = dwc3_readl(dwc->regs, DWC3_DEPCMD(ep));
 		reg = dwc3_readl(dwc->regs, DWC3_DEPCMD(ep));
 		if (!(reg & DWC3_DEPCMD_CMDACT)) {
 		if (!(reg & DWC3_DEPCMD_CMDACT)) {
-			dev_vdbg(dwc->dev, "Command Complete --> %d\n",
+			dwc3_trace(trace_dwc3_gadget,
+					"Command Complete --> %d",
 					DWC3_DEPCMD_STATUS(reg));
 					DWC3_DEPCMD_STATUS(reg));
 			return 0;
 			return 0;
 		}
 		}
@@ -330,8 +336,11 @@ int dwc3_send_gadget_ep_cmd(struct dwc3 *dwc, unsigned ep,
 		 * interrupt context.
 		 * interrupt context.
 		 */
 		 */
 		timeout--;
 		timeout--;
-		if (!timeout)
+		if (!timeout) {
+			dwc3_trace(trace_dwc3_gadget,
+					"Command Timed Out");
 			return -ETIMEDOUT;
 			return -ETIMEDOUT;
+		}
 
 
 		udelay(1);
 		udelay(1);
 	} while (1);
 	} while (1);
@@ -352,9 +361,6 @@ static int dwc3_alloc_trb_pool(struct dwc3_ep *dep)
 	if (dep->trb_pool)
 	if (dep->trb_pool)
 		return 0;
 		return 0;
 
 
-	if (dep->number == 0 || dep->number == 1)
-		return 0;
-
 	dep->trb_pool = dma_alloc_coherent(dwc->dev,
 	dep->trb_pool = dma_alloc_coherent(dwc->dev,
 			sizeof(struct dwc3_trb) * DWC3_TRB_NUM,
 			sizeof(struct dwc3_trb) * DWC3_TRB_NUM,
 			&dep->trb_pool_dma, GFP_KERNEL);
 			&dep->trb_pool_dma, GFP_KERNEL);
@@ -492,7 +498,7 @@ static int __dwc3_gadget_ep_enable(struct dwc3_ep *dep,
 	u32			reg;
 	u32			reg;
 	int			ret;
 	int			ret;
 
 
-	dev_vdbg(dwc->dev, "Enabling %s\n", dep->name);
+	dwc3_trace(trace_dwc3_gadget, "Enabling %s", dep->name);
 
 
 	if (!(dep->flags & DWC3_EP_ENABLED)) {
 	if (!(dep->flags & DWC3_EP_ENABLED)) {
 		ret = dwc3_gadget_start_config(dwc, dep);
 		ret = dwc3_gadget_start_config(dwc, dep);
@@ -729,10 +735,9 @@ static void dwc3_prepare_one_trb(struct dwc3_ep *dep,
 		struct dwc3_request *req, dma_addr_t dma,
 		struct dwc3_request *req, dma_addr_t dma,
 		unsigned length, unsigned last, unsigned chain, unsigned node)
 		unsigned length, unsigned last, unsigned chain, unsigned node)
 {
 {
-	struct dwc3		*dwc = dep->dwc;
 	struct dwc3_trb		*trb;
 	struct dwc3_trb		*trb;
 
 
-	dev_vdbg(dwc->dev, "%s: req %p dma %08llx length %d%s%s\n",
+	dwc3_trace(trace_dwc3_gadget, "%s: req %p dma %08llx length %d%s%s",
 			dep->name, req, (unsigned long long) dma,
 			dep->name, req, (unsigned long long) dma,
 			length, last ? " last" : "",
 			length, last ? " last" : "",
 			chain ? " chain" : "");
 			chain ? " chain" : "");
@@ -934,7 +939,7 @@ static int __dwc3_gadget_kick_transfer(struct dwc3_ep *dep, u16 cmd_param,
 	u32				cmd;
 	u32				cmd;
 
 
 	if (start_new && (dep->flags & DWC3_EP_BUSY)) {
 	if (start_new && (dep->flags & DWC3_EP_BUSY)) {
-		dev_vdbg(dwc->dev, "%s: endpoint busy\n", dep->name);
+		dwc3_trace(trace_dwc3_gadget, "%s: endpoint busy", dep->name);
 		return -EBUSY;
 		return -EBUSY;
 	}
 	}
 	dep->flags &= ~DWC3_EP_PENDING_REQUEST;
 	dep->flags &= ~DWC3_EP_PENDING_REQUEST;
@@ -1005,8 +1010,9 @@ static void __dwc3_gadget_start_isoc(struct dwc3 *dwc,
 	u32 uf;
 	u32 uf;
 
 
 	if (list_empty(&dep->request_list)) {
 	if (list_empty(&dep->request_list)) {
-		dev_vdbg(dwc->dev, "ISOC ep %s run out for requests.\n",
-			dep->name);
+		dwc3_trace(trace_dwc3_gadget,
+				"ISOC ep %s run out for requests",
+				dep->name);
 		dep->flags |= DWC3_EP_PENDING_REQUEST;
 		dep->flags |= DWC3_EP_PENDING_REQUEST;
 		return;
 		return;
 	}
 	}
@@ -1113,15 +1119,10 @@ static int __dwc3_gadget_ep_queue(struct dwc3_ep *dep, struct dwc3_request *req)
 	 * handled.
 	 * handled.
 	 */
 	 */
 	if (dep->stream_capable) {
 	if (dep->stream_capable) {
-		int	ret;
-
 		ret = __dwc3_gadget_kick_transfer(dep, 0, true);
 		ret = __dwc3_gadget_kick_transfer(dep, 0, true);
-		if (ret && ret != -EBUSY) {
-			struct dwc3	*dwc = dep->dwc;
-
+		if (ret && ret != -EBUSY)
 			dev_dbg(dwc->dev, "%s: failed to kick transfers\n",
 			dev_dbg(dwc->dev, "%s: failed to kick transfers\n",
 					dep->name);
 					dep->name);
-		}
 	}
 	}
 
 
 	return 0;
 	return 0;
@@ -1152,8 +1153,6 @@ static int dwc3_gadget_ep_queue(struct usb_ep *ep, struct usb_request *request,
 		goto out;
 		goto out;
 	}
 	}
 
 
-	dev_vdbg(dwc->dev, "queing request %p to %s length %d\n",
-			request, ep->name, request->length);
 	trace_dwc3_ep_queue(req);
 	trace_dwc3_ep_queue(req);
 
 
 	ret = __dwc3_gadget_ep_queue(dep, req);
 	ret = __dwc3_gadget_ep_queue(dep, req);
@@ -1416,7 +1415,7 @@ static int dwc3_gadget_set_selfpowered(struct usb_gadget *g,
 	unsigned long		flags;
 	unsigned long		flags;
 
 
 	spin_lock_irqsave(&dwc->lock, flags);
 	spin_lock_irqsave(&dwc->lock, flags);
-	dwc->is_selfpowered = !!is_selfpowered;
+	g->is_selfpowered = !!is_selfpowered;
 	spin_unlock_irqrestore(&dwc->lock, flags);
 	spin_unlock_irqrestore(&dwc->lock, flags);
 
 
 	return 0;
 	return 0;
@@ -1468,7 +1467,7 @@ static int dwc3_gadget_run_stop(struct dwc3 *dwc, int is_on, int suspend)
 		udelay(1);
 		udelay(1);
 	} while (1);
 	} while (1);
 
 
-	dev_vdbg(dwc->dev, "gadget %s data soft-%s\n",
+	dwc3_trace(trace_dwc3_gadget, "gadget %s data soft-%s",
 			dwc->gadget_driver
 			dwc->gadget_driver
 			? dwc->gadget_driver->function : "no-function",
 			? dwc->gadget_driver->function : "no-function",
 			is_on ? "connect" : "disconnect");
 			is_on ? "connect" : "disconnect");
@@ -1688,7 +1687,7 @@ static int dwc3_gadget_init_hw_endpoints(struct dwc3 *dwc,
 
 
 		dep->endpoint.name = dep->name;
 		dep->endpoint.name = dep->name;
 
 
-		dev_vdbg(dwc->dev, "initializing %s\n", dep->name);
+		dwc3_trace(trace_dwc3_gadget, "initializing %s", dep->name);
 
 
 		if (epnum == 0 || epnum == 1) {
 		if (epnum == 0 || epnum == 1) {
 			usb_ep_set_maxpacket_limit(&dep->endpoint, 512);
 			usb_ep_set_maxpacket_limit(&dep->endpoint, 512);
@@ -1725,13 +1724,15 @@ static int dwc3_gadget_init_endpoints(struct dwc3 *dwc)
 
 
 	ret = dwc3_gadget_init_hw_endpoints(dwc, dwc->num_out_eps, 0);
 	ret = dwc3_gadget_init_hw_endpoints(dwc, dwc->num_out_eps, 0);
 	if (ret < 0) {
 	if (ret < 0) {
-		dev_vdbg(dwc->dev, "failed to allocate OUT endpoints\n");
+		dwc3_trace(trace_dwc3_gadget,
+				"failed to allocate OUT endpoints");
 		return ret;
 		return ret;
 	}
 	}
 
 
 	ret = dwc3_gadget_init_hw_endpoints(dwc, dwc->num_in_eps, 1);
 	ret = dwc3_gadget_init_hw_endpoints(dwc, dwc->num_in_eps, 1);
 	if (ret < 0) {
 	if (ret < 0) {
-		dev_vdbg(dwc->dev, "failed to allocate IN endpoints\n");
+		dwc3_trace(trace_dwc3_gadget,
+				"failed to allocate IN endpoints");
 		return ret;
 		return ret;
 	}
 	}
 
 
@@ -1977,7 +1978,7 @@ static void dwc3_endpoint_interrupt(struct dwc3 *dwc,
 		} else {
 		} else {
 			int ret;
 			int ret;
 
 
-			dev_vdbg(dwc->dev, "%s: reason %s\n",
+			dwc3_trace(trace_dwc3_gadget, "%s: reason %s",
 					dep->name, event->status &
 					dep->name, event->status &
 					DEPEVT_STATUS_TRANSFER_ACTIVE
 					DEPEVT_STATUS_TRANSFER_ACTIVE
 					? "Transfer Active"
 					? "Transfer Active"
@@ -2001,7 +2002,8 @@ static void dwc3_endpoint_interrupt(struct dwc3 *dwc,
 
 
 		switch (event->status) {
 		switch (event->status) {
 		case DEPEVT_STREAMEVT_FOUND:
 		case DEPEVT_STREAMEVT_FOUND:
-			dev_vdbg(dwc->dev, "Stream %d found and started\n",
+			dwc3_trace(trace_dwc3_gadget,
+					"Stream %d found and started",
 					event->parameters);
 					event->parameters);
 
 
 			break;
 			break;
@@ -2015,7 +2017,7 @@ static void dwc3_endpoint_interrupt(struct dwc3 *dwc,
 		dev_dbg(dwc->dev, "%s FIFO Overrun\n", dep->name);
 		dev_dbg(dwc->dev, "%s FIFO Overrun\n", dep->name);
 		break;
 		break;
 	case DWC3_DEPEVT_EPCMDCMPLT:
 	case DWC3_DEPEVT_EPCMDCMPLT:
-		dev_vdbg(dwc->dev, "Endpoint Command Complete\n");
+		dwc3_trace(trace_dwc3_gadget, "Endpoint Command Complete");
 		break;
 		break;
 	}
 	}
 }
 }
@@ -2043,6 +2045,7 @@ static void dwc3_resume_gadget(struct dwc3 *dwc)
 	if (dwc->gadget_driver && dwc->gadget_driver->resume) {
 	if (dwc->gadget_driver && dwc->gadget_driver->resume) {
 		spin_unlock(&dwc->lock);
 		spin_unlock(&dwc->lock);
 		dwc->gadget_driver->resume(&dwc->gadget);
 		dwc->gadget_driver->resume(&dwc->gadget);
+		spin_lock(&dwc->lock);
 	}
 	}
 }
 }
 
 
@@ -2079,7 +2082,7 @@ static void dwc3_stop_active_transfer(struct dwc3 *dwc, u32 epnum, bool force)
 	 * We have discussed this with the IP Provider and it was
 	 * We have discussed this with the IP Provider and it was
 	 * suggested to giveback all requests here, but give HW some
 	 * suggested to giveback all requests here, but give HW some
 	 * extra time to synchronize with the interconnect. We're using
 	 * extra time to synchronize with the interconnect. We're using
-	 * an arbitraty 100us delay for that.
+	 * an arbitrary 100us delay for that.
 	 *
 	 *
 	 * Note also that a similar handling was tested by Synopsys
 	 * Note also that a similar handling was tested by Synopsys
 	 * (thanks a lot Paul) and nothing bad has come out of it.
 	 * (thanks a lot Paul) and nothing bad has come out of it.
@@ -2389,7 +2392,8 @@ static void dwc3_gadget_linksts_change_interrupt(struct dwc3 *dwc,
 			(pwropt != DWC3_GHWPARAMS1_EN_PWROPT_HIB)) {
 			(pwropt != DWC3_GHWPARAMS1_EN_PWROPT_HIB)) {
 		if ((dwc->link_state == DWC3_LINK_STATE_U3) &&
 		if ((dwc->link_state == DWC3_LINK_STATE_U3) &&
 				(next == DWC3_LINK_STATE_RESUME)) {
 				(next == DWC3_LINK_STATE_RESUME)) {
-			dev_vdbg(dwc->dev, "ignoring transition U3 -> Resume\n");
+			dwc3_trace(trace_dwc3_gadget,
+					"ignoring transition U3 -> Resume");
 			return;
 			return;
 		}
 		}
 	}
 	}
@@ -2511,22 +2515,22 @@ static void dwc3_gadget_interrupt(struct dwc3 *dwc,
 		dwc3_gadget_linksts_change_interrupt(dwc, event->event_info);
 		dwc3_gadget_linksts_change_interrupt(dwc, event->event_info);
 		break;
 		break;
 	case DWC3_DEVICE_EVENT_EOPF:
 	case DWC3_DEVICE_EVENT_EOPF:
-		dev_vdbg(dwc->dev, "End of Periodic Frame\n");
+		dwc3_trace(trace_dwc3_gadget, "End of Periodic Frame");
 		break;
 		break;
 	case DWC3_DEVICE_EVENT_SOF:
 	case DWC3_DEVICE_EVENT_SOF:
-		dev_vdbg(dwc->dev, "Start of Periodic Frame\n");
+		dwc3_trace(trace_dwc3_gadget, "Start of Periodic Frame");
 		break;
 		break;
 	case DWC3_DEVICE_EVENT_ERRATIC_ERROR:
 	case DWC3_DEVICE_EVENT_ERRATIC_ERROR:
-		dev_vdbg(dwc->dev, "Erratic Error\n");
+		dwc3_trace(trace_dwc3_gadget, "Erratic Error");
 		break;
 		break;
 	case DWC3_DEVICE_EVENT_CMD_CMPL:
 	case DWC3_DEVICE_EVENT_CMD_CMPL:
-		dev_vdbg(dwc->dev, "Command Complete\n");
+		dwc3_trace(trace_dwc3_gadget, "Command Complete");
 		break;
 		break;
 	case DWC3_DEVICE_EVENT_OVERFLOW:
 	case DWC3_DEVICE_EVENT_OVERFLOW:
-		dev_vdbg(dwc->dev, "Overflow\n");
+		dwc3_trace(trace_dwc3_gadget, "Overflow");
 		break;
 		break;
 	default:
 	default:
-		dev_dbg(dwc->dev, "UNKNOWN IRQ %d\n", event->type);
+		dev_WARN(dwc->dev, "UNKNOWN IRQ %d\n", event->type);
 	}
 	}
 }
 }
 
 

+ 10 - 0
drivers/usb/dwc3/trace.h

@@ -47,6 +47,16 @@ DEFINE_EVENT(dwc3_log_msg, dwc3_writel,
 	TP_ARGS(vaf)
 	TP_ARGS(vaf)
 );
 );
 
 
+DEFINE_EVENT(dwc3_log_msg, dwc3_gadget,
+	TP_PROTO(struct va_format *vaf),
+	TP_ARGS(vaf)
+);
+
+DEFINE_EVENT(dwc3_log_msg, dwc3_core,
+	TP_PROTO(struct va_format *vaf),
+	TP_ARGS(vaf)
+);
+
 DEFINE_EVENT(dwc3_log_msg, dwc3_ep0,
 DEFINE_EVENT(dwc3_log_msg, dwc3_ep0,
 	TP_PROTO(struct va_format *vaf),
 	TP_PROTO(struct va_format *vaf),
 	TP_ARGS(vaf)
 	TP_ARGS(vaf)

+ 11 - 0
drivers/usb/gadget/Kconfig

@@ -423,6 +423,17 @@ config USB_CONFIGFS_F_HID
 
 
 	  For more information, see Documentation/usb/gadget_hid.txt.
 	  For more information, see Documentation/usb/gadget_hid.txt.
 
 
+config USB_CONFIGFS_F_UVC
+	bool "USB Webcam function"
+	depends on USB_CONFIGFS
+	depends on VIDEO_DEV
+	select VIDEOBUF2_VMALLOC
+	select USB_F_UVC
+	help
+	  The Webcam function acts as a composite USB Audio and Video Class
+	  device. It provides a userspace API to process UVC control requests
+	  and stream video data to the host.
+
 source "drivers/usb/gadget/legacy/Kconfig"
 source "drivers/usb/gadget/legacy/Kconfig"
 
 
 endchoice
 endchoice

+ 1 - 1
drivers/usb/gadget/composite.c

@@ -1655,7 +1655,7 @@ unknown:
 		 * OS descriptors handling
 		 * OS descriptors handling
 		 */
 		 */
 		if (cdev->use_os_string && cdev->os_desc_config &&
 		if (cdev->use_os_string && cdev->os_desc_config &&
-		    (ctrl->bRequest & USB_TYPE_VENDOR) &&
+		    (ctrl->bRequestType & USB_TYPE_VENDOR) &&
 		    ctrl->bRequest == cdev->b_vendor_code) {
 		    ctrl->bRequest == cdev->b_vendor_code) {
 			struct usb_request		*req;
 			struct usb_request		*req;
 			struct usb_configuration	*os_desc_cfg;
 			struct usb_configuration	*os_desc_cfg;

+ 1 - 1
drivers/usb/gadget/function/Makefile

@@ -36,7 +36,7 @@ usb_f_uac1-y			:= f_uac1.o u_uac1.o
 obj-$(CONFIG_USB_F_UAC1)	+= usb_f_uac1.o
 obj-$(CONFIG_USB_F_UAC1)	+= usb_f_uac1.o
 usb_f_uac2-y			:= f_uac2.o
 usb_f_uac2-y			:= f_uac2.o
 obj-$(CONFIG_USB_F_UAC2)	+= usb_f_uac2.o
 obj-$(CONFIG_USB_F_UAC2)	+= usb_f_uac2.o
-usb_f_uvc-y			:= f_uvc.o uvc_queue.o uvc_v4l2.o uvc_video.o
+usb_f_uvc-y			:= f_uvc.o uvc_queue.o uvc_v4l2.o uvc_video.o uvc_configfs.o
 obj-$(CONFIG_USB_F_UVC)		+= usb_f_uvc.o
 obj-$(CONFIG_USB_F_UVC)		+= usb_f_uvc.o
 usb_f_midi-y			:= f_midi.o
 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

+ 99 - 20
drivers/usb/gadget/function/f_fs.c

@@ -31,6 +31,7 @@
 #include <linux/aio.h>
 #include <linux/aio.h>
 #include <linux/mmu_context.h>
 #include <linux/mmu_context.h>
 #include <linux/poll.h>
 #include <linux/poll.h>
+#include <linux/eventfd.h>
 
 
 #include "u_fs.h"
 #include "u_fs.h"
 #include "u_f.h"
 #include "u_f.h"
@@ -153,6 +154,8 @@ struct ffs_io_data {
 
 
 	struct usb_ep *ep;
 	struct usb_ep *ep;
 	struct usb_request *req;
 	struct usb_request *req;
+
+	struct ffs_data *ffs;
 };
 };
 
 
 struct ffs_desc_helper {
 struct ffs_desc_helper {
@@ -390,17 +393,20 @@ done_spin:
 	return ret;
 	return ret;
 }
 }
 
 
+/* Called with ffs->ev.waitq.lock and ffs->mutex held, both released on exit. */
 static ssize_t __ffs_ep0_read_events(struct ffs_data *ffs, char __user *buf,
 static ssize_t __ffs_ep0_read_events(struct ffs_data *ffs, char __user *buf,
 				     size_t n)
 				     size_t n)
 {
 {
 	/*
 	/*
-	 * We are holding ffs->ev.waitq.lock and ffs->mutex and we need
-	 * to release them.
+	 * n cannot be bigger than ffs->ev.count, which cannot be bigger than
+	 * size of ffs->ev.types array (which is four) so that's how much space
+	 * we reserve.
 	 */
 	 */
-	struct usb_functionfs_event events[n];
+	struct usb_functionfs_event events[ARRAY_SIZE(ffs->ev.types)];
+	const size_t size = n * sizeof *events;
 	unsigned i = 0;
 	unsigned i = 0;
 
 
-	memset(events, 0, sizeof events);
+	memset(events, 0, size);
 
 
 	do {
 	do {
 		events[i].type = ffs->ev.types[i];
 		events[i].type = ffs->ev.types[i];
@@ -410,19 +416,15 @@ static ssize_t __ffs_ep0_read_events(struct ffs_data *ffs, char __user *buf,
 		}
 		}
 	} while (++i < n);
 	} while (++i < n);
 
 
-	if (n < ffs->ev.count) {
-		ffs->ev.count -= n;
+	ffs->ev.count -= n;
+	if (ffs->ev.count)
 		memmove(ffs->ev.types, ffs->ev.types + n,
 		memmove(ffs->ev.types, ffs->ev.types + n,
 			ffs->ev.count * sizeof *ffs->ev.types);
 			ffs->ev.count * sizeof *ffs->ev.types);
-	} else {
-		ffs->ev.count = 0;
-	}
 
 
 	spin_unlock_irq(&ffs->ev.waitq.lock);
 	spin_unlock_irq(&ffs->ev.waitq.lock);
 	mutex_unlock(&ffs->mutex);
 	mutex_unlock(&ffs->mutex);
 
 
-	return unlikely(__copy_to_user(buf, events, sizeof events))
-		? -EFAULT : sizeof events;
+	return unlikely(__copy_to_user(buf, events, size)) ? -EFAULT : size;
 }
 }
 
 
 static ssize_t ffs_ep0_read(struct file *file, char __user *buf,
 static ssize_t ffs_ep0_read(struct file *file, char __user *buf,
@@ -606,6 +608,8 @@ static unsigned int ffs_ep0_poll(struct file *file, poll_table *wait)
 		}
 		}
 	case FFS_CLOSING:
 	case FFS_CLOSING:
 		break;
 		break;
+	case FFS_DEACTIVATED:
+		break;
 	}
 	}
 
 
 	mutex_unlock(&ffs->mutex);
 	mutex_unlock(&ffs->mutex);
@@ -673,6 +677,9 @@ static void ffs_user_copy_worker(struct work_struct *work)
 
 
 	aio_complete(io_data->kiocb, ret, ret);
 	aio_complete(io_data->kiocb, ret, ret);
 
 
+	if (io_data->ffs->ffs_eventfd && !io_data->kiocb->ki_eventfd)
+		eventfd_signal(io_data->ffs->ffs_eventfd, 1);
+
 	usb_ep_free_request(io_data->ep, io_data->req);
 	usb_ep_free_request(io_data->ep, io_data->req);
 
 
 	io_data->kiocb->private = NULL;
 	io_data->kiocb->private = NULL;
@@ -826,6 +833,7 @@ static ssize_t ffs_epfile_io(struct file *file, struct ffs_io_data *io_data)
 			io_data->buf = data;
 			io_data->buf = data;
 			io_data->ep = ep->ep;
 			io_data->ep = ep->ep;
 			io_data->req = req;
 			io_data->req = req;
+			io_data->ffs = epfile->ffs;
 
 
 			req->context  = io_data;
 			req->context  = io_data;
 			req->complete = ffs_epfile_async_io_complete;
 			req->complete = ffs_epfile_async_io_complete;
@@ -1180,6 +1188,7 @@ struct ffs_sb_fill_data {
 	struct ffs_file_perms perms;
 	struct ffs_file_perms perms;
 	umode_t root_mode;
 	umode_t root_mode;
 	const char *dev_name;
 	const char *dev_name;
+	bool no_disconnect;
 	struct ffs_data *ffs_data;
 	struct ffs_data *ffs_data;
 };
 };
 
 
@@ -1250,6 +1259,12 @@ static int ffs_fs_parse_opts(struct ffs_sb_fill_data *data, char *opts)
 
 
 		/* Interpret option */
 		/* Interpret option */
 		switch (eq - opts) {
 		switch (eq - opts) {
+		case 13:
+			if (!memcmp(opts, "no_disconnect", 13))
+				data->no_disconnect = !!value;
+			else
+				goto invalid;
+			break;
 		case 5:
 		case 5:
 			if (!memcmp(opts, "rmode", 5))
 			if (!memcmp(opts, "rmode", 5))
 				data->root_mode  = (value & 0555) | S_IFDIR;
 				data->root_mode  = (value & 0555) | S_IFDIR;
@@ -1314,6 +1329,7 @@ ffs_fs_mount(struct file_system_type *t, int flags,
 			.gid = GLOBAL_ROOT_GID,
 			.gid = GLOBAL_ROOT_GID,
 		},
 		},
 		.root_mode = S_IFDIR | 0500,
 		.root_mode = S_IFDIR | 0500,
+		.no_disconnect = false,
 	};
 	};
 	struct dentry *rv;
 	struct dentry *rv;
 	int ret;
 	int ret;
@@ -1330,6 +1346,7 @@ ffs_fs_mount(struct file_system_type *t, int flags,
 	if (unlikely(!ffs))
 	if (unlikely(!ffs))
 		return ERR_PTR(-ENOMEM);
 		return ERR_PTR(-ENOMEM);
 	ffs->file_perms = data.perms;
 	ffs->file_perms = data.perms;
+	ffs->no_disconnect = data.no_disconnect;
 
 
 	ffs->dev_name = kstrdup(dev_name, GFP_KERNEL);
 	ffs->dev_name = kstrdup(dev_name, GFP_KERNEL);
 	if (unlikely(!ffs->dev_name)) {
 	if (unlikely(!ffs->dev_name)) {
@@ -1361,6 +1378,7 @@ ffs_fs_kill_sb(struct super_block *sb)
 	kill_litter_super(sb);
 	kill_litter_super(sb);
 	if (sb->s_fs_info) {
 	if (sb->s_fs_info) {
 		ffs_release_dev(sb->s_fs_info);
 		ffs_release_dev(sb->s_fs_info);
+		ffs_data_closed(sb->s_fs_info);
 		ffs_data_put(sb->s_fs_info);
 		ffs_data_put(sb->s_fs_info);
 	}
 	}
 }
 }
@@ -1417,7 +1435,11 @@ static void ffs_data_opened(struct ffs_data *ffs)
 	ENTER();
 	ENTER();
 
 
 	atomic_inc(&ffs->ref);
 	atomic_inc(&ffs->ref);
-	atomic_inc(&ffs->opened);
+	if (atomic_add_return(1, &ffs->opened) == 1 &&
+			ffs->state == FFS_DEACTIVATED) {
+		ffs->state = FFS_CLOSING;
+		ffs_data_reset(ffs);
+	}
 }
 }
 
 
 static void ffs_data_put(struct ffs_data *ffs)
 static void ffs_data_put(struct ffs_data *ffs)
@@ -1439,6 +1461,21 @@ static void ffs_data_closed(struct ffs_data *ffs)
 	ENTER();
 	ENTER();
 
 
 	if (atomic_dec_and_test(&ffs->opened)) {
 	if (atomic_dec_and_test(&ffs->opened)) {
+		if (ffs->no_disconnect) {
+			ffs->state = FFS_DEACTIVATED;
+			if (ffs->epfiles) {
+				ffs_epfiles_destroy(ffs->epfiles,
+						   ffs->eps_count);
+				ffs->epfiles = NULL;
+			}
+			if (ffs->setup_state == FFS_SETUP_PENDING)
+				__ffs_ep0_stall(ffs);
+		} else {
+			ffs->state = FFS_CLOSING;
+			ffs_data_reset(ffs);
+		}
+	}
+	if (atomic_read(&ffs->opened) < 0) {
 		ffs->state = FFS_CLOSING;
 		ffs->state = FFS_CLOSING;
 		ffs_data_reset(ffs);
 		ffs_data_reset(ffs);
 	}
 	}
@@ -1480,6 +1517,9 @@ static void ffs_data_clear(struct ffs_data *ffs)
 	if (ffs->epfiles)
 	if (ffs->epfiles)
 		ffs_epfiles_destroy(ffs->epfiles, ffs->eps_count);
 		ffs_epfiles_destroy(ffs->epfiles, ffs->eps_count);
 
 
+	if (ffs->ffs_eventfd)
+		eventfd_ctx_put(ffs->ffs_eventfd);
+
 	kfree(ffs->raw_descs_data);
 	kfree(ffs->raw_descs_data);
 	kfree(ffs->raw_strings);
 	kfree(ffs->raw_strings);
 	kfree(ffs->stringtabs);
 	kfree(ffs->stringtabs);
@@ -1581,10 +1621,10 @@ static int ffs_epfiles_create(struct ffs_data *ffs)
 		mutex_init(&epfile->mutex);
 		mutex_init(&epfile->mutex);
 		init_waitqueue_head(&epfile->wait);
 		init_waitqueue_head(&epfile->wait);
 		if (ffs->user_flags & FUNCTIONFS_VIRTUAL_ADDR)
 		if (ffs->user_flags & FUNCTIONFS_VIRTUAL_ADDR)
-			sprintf(epfiles->name, "ep%02x", ffs->eps_addrmap[i]);
+			sprintf(epfile->name, "ep%02x", ffs->eps_addrmap[i]);
 		else
 		else
-			sprintf(epfiles->name, "ep%u", i);
-		epfile->dentry = ffs_sb_create_file(ffs->sb, epfiles->name,
+			sprintf(epfile->name, "ep%u", i);
+		epfile->dentry = ffs_sb_create_file(ffs->sb, epfile->name,
 						 epfile,
 						 epfile,
 						 &ffs_epfile_operations);
 						 &ffs_epfile_operations);
 		if (unlikely(!epfile->dentry)) {
 		if (unlikely(!epfile->dentry)) {
@@ -1616,7 +1656,6 @@ static void ffs_epfiles_destroy(struct ffs_epfile *epfiles, unsigned count)
 	kfree(epfiles);
 	kfree(epfiles);
 }
 }
 
 
-
 static void ffs_func_eps_disable(struct ffs_function *func)
 static void ffs_func_eps_disable(struct ffs_function *func)
 {
 {
 	struct ffs_ep *ep         = func->eps;
 	struct ffs_ep *ep         = func->eps;
@@ -1629,10 +1668,12 @@ static void ffs_func_eps_disable(struct ffs_function *func)
 		/* pending requests get nuked */
 		/* pending requests get nuked */
 		if (likely(ep->ep))
 		if (likely(ep->ep))
 			usb_ep_disable(ep->ep);
 			usb_ep_disable(ep->ep);
-		epfile->ep = NULL;
-
 		++ep;
 		++ep;
-		++epfile;
+
+		if (epfile) {
+			epfile->ep = NULL;
+			++epfile;
+		}
 	} while (--count);
 	} while (--count);
 	spin_unlock_irqrestore(&func->ffs->eps_lock, flags);
 	spin_unlock_irqrestore(&func->ffs->eps_lock, flags);
 }
 }
@@ -2138,7 +2179,8 @@ static int __ffs_data_got_descs(struct ffs_data *ffs,
 			      FUNCTIONFS_HAS_HS_DESC |
 			      FUNCTIONFS_HAS_HS_DESC |
 			      FUNCTIONFS_HAS_SS_DESC |
 			      FUNCTIONFS_HAS_SS_DESC |
 			      FUNCTIONFS_HAS_MS_OS_DESC |
 			      FUNCTIONFS_HAS_MS_OS_DESC |
-			      FUNCTIONFS_VIRTUAL_ADDR)) {
+			      FUNCTIONFS_VIRTUAL_ADDR |
+			      FUNCTIONFS_EVENTFD)) {
 			ret = -ENOSYS;
 			ret = -ENOSYS;
 			goto error;
 			goto error;
 		}
 		}
@@ -2149,6 +2191,20 @@ static int __ffs_data_got_descs(struct ffs_data *ffs,
 		goto error;
 		goto error;
 	}
 	}
 
 
+	if (flags & FUNCTIONFS_EVENTFD) {
+		if (len < 4)
+			goto error;
+		ffs->ffs_eventfd =
+			eventfd_ctx_fdget((int)get_unaligned_le32(data));
+		if (IS_ERR(ffs->ffs_eventfd)) {
+			ret = PTR_ERR(ffs->ffs_eventfd);
+			ffs->ffs_eventfd = NULL;
+			goto error;
+		}
+		data += 4;
+		len  -= 4;
+	}
+
 	/* Read fs_count, hs_count and ss_count (if present) */
 	/* Read fs_count, hs_count and ss_count (if present) */
 	for (i = 0; i < 3; ++i) {
 	for (i = 0; i < 3; ++i) {
 		if (!(flags & (1 << i))) {
 		if (!(flags & (1 << i))) {
@@ -2377,6 +2433,13 @@ static void __ffs_event_add(struct ffs_data *ffs,
 	if (ffs->setup_state == FFS_SETUP_PENDING)
 	if (ffs->setup_state == FFS_SETUP_PENDING)
 		ffs->setup_state = FFS_SETUP_CANCELLED;
 		ffs->setup_state = FFS_SETUP_CANCELLED;
 
 
+	/*
+	 * Logic of this function guarantees that there are at most four pending
+	 * evens on ffs->ev.types queue.  This is important because the queue
+	 * has space for four elements only and __ffs_ep0_read_events function
+	 * depends on that limit as well.  If more event types are added, those
+	 * limits have to be revisited or guaranteed to still hold.
+	 */
 	switch (type) {
 	switch (type) {
 	case FUNCTIONFS_RESUME:
 	case FUNCTIONFS_RESUME:
 		rem_type2 = FUNCTIONFS_SUSPEND;
 		rem_type2 = FUNCTIONFS_SUSPEND;
@@ -2416,6 +2479,8 @@ static void __ffs_event_add(struct ffs_data *ffs,
 	pr_vdebug("adding event %d\n", type);
 	pr_vdebug("adding event %d\n", type);
 	ffs->ev.types[ffs->ev.count++] = type;
 	ffs->ev.types[ffs->ev.count++] = type;
 	wake_up_locked(&ffs->ev.waitq);
 	wake_up_locked(&ffs->ev.waitq);
+	if (ffs->ffs_eventfd)
+		eventfd_signal(ffs->ffs_eventfd, 1);
 }
 }
 
 
 static void ffs_event_add(struct ffs_data *ffs,
 static void ffs_event_add(struct ffs_data *ffs,
@@ -2888,6 +2953,13 @@ static int ffs_func_bind(struct usb_configuration *c,
 
 
 /* Other USB function hooks *************************************************/
 /* Other USB function hooks *************************************************/
 
 
+static void ffs_reset_work(struct work_struct *work)
+{
+	struct ffs_data *ffs = container_of(work,
+		struct ffs_data, reset_work);
+	ffs_data_reset(ffs);
+}
+
 static int ffs_func_set_alt(struct usb_function *f,
 static int ffs_func_set_alt(struct usb_function *f,
 			    unsigned interface, unsigned alt)
 			    unsigned interface, unsigned alt)
 {
 {
@@ -2904,6 +2976,13 @@ static int ffs_func_set_alt(struct usb_function *f,
 	if (ffs->func)
 	if (ffs->func)
 		ffs_func_eps_disable(ffs->func);
 		ffs_func_eps_disable(ffs->func);
 
 
+	if (ffs->state == FFS_DEACTIVATED) {
+		ffs->state = FFS_CLOSING;
+		INIT_WORK(&ffs->reset_work, ffs_reset_work);
+		schedule_work(&ffs->reset_work);
+		return -ENODEV;
+	}
+
 	if (ffs->state != FFS_ACTIVE)
 	if (ffs->state != FFS_ACTIVE)
 		return -ENODEV;
 		return -ENODEV;
 
 

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

@@ -759,7 +759,7 @@ static struct f_hid_opts_attribute f_hid_opts_##name =			\
 
 
 F_HID_OPT(subclass, 8, 255);
 F_HID_OPT(subclass, 8, 255);
 F_HID_OPT(protocol, 8, 255);
 F_HID_OPT(protocol, 8, 255);
-F_HID_OPT(report_length, 16, 65536);
+F_HID_OPT(report_length, 16, 65535);
 
 
 static ssize_t f_hid_opts_report_desc_show(struct f_hid_opts *opts, char *page)
 static ssize_t f_hid_opts_report_desc_show(struct f_hid_opts *opts, char *page)
 {
 {

+ 10 - 10
drivers/usb/gadget/function/f_sourcesink.c

@@ -1214,7 +1214,7 @@ static ssize_t f_ss_opts_pattern_show(struct f_ss_opts *opts, char *page)
 	int result;
 	int result;
 
 
 	mutex_lock(&opts->lock);
 	mutex_lock(&opts->lock);
-	result = sprintf(page, "%d", opts->pattern);
+	result = sprintf(page, "%u", opts->pattern);
 	mutex_unlock(&opts->lock);
 	mutex_unlock(&opts->lock);
 
 
 	return result;
 	return result;
@@ -1258,7 +1258,7 @@ static ssize_t f_ss_opts_isoc_interval_show(struct f_ss_opts *opts, char *page)
 	int result;
 	int result;
 
 
 	mutex_lock(&opts->lock);
 	mutex_lock(&opts->lock);
-	result = sprintf(page, "%d", opts->isoc_interval);
+	result = sprintf(page, "%u", opts->isoc_interval);
 	mutex_unlock(&opts->lock);
 	mutex_unlock(&opts->lock);
 
 
 	return result;
 	return result;
@@ -1302,7 +1302,7 @@ static ssize_t f_ss_opts_isoc_maxpacket_show(struct f_ss_opts *opts, char *page)
 	int result;
 	int result;
 
 
 	mutex_lock(&opts->lock);
 	mutex_lock(&opts->lock);
-	result = sprintf(page, "%d", opts->isoc_maxpacket);
+	result = sprintf(page, "%u", opts->isoc_maxpacket);
 	mutex_unlock(&opts->lock);
 	mutex_unlock(&opts->lock);
 
 
 	return result;
 	return result;
@@ -1346,7 +1346,7 @@ static ssize_t f_ss_opts_isoc_mult_show(struct f_ss_opts *opts, char *page)
 	int result;
 	int result;
 
 
 	mutex_lock(&opts->lock);
 	mutex_lock(&opts->lock);
-	result = sprintf(page, "%d", opts->isoc_mult);
+	result = sprintf(page, "%u", opts->isoc_mult);
 	mutex_unlock(&opts->lock);
 	mutex_unlock(&opts->lock);
 
 
 	return result;
 	return result;
@@ -1390,7 +1390,7 @@ static ssize_t f_ss_opts_isoc_maxburst_show(struct f_ss_opts *opts, char *page)
 	int result;
 	int result;
 
 
 	mutex_lock(&opts->lock);
 	mutex_lock(&opts->lock);
-	result = sprintf(page, "%d", opts->isoc_maxburst);
+	result = sprintf(page, "%u", opts->isoc_maxburst);
 	mutex_unlock(&opts->lock);
 	mutex_unlock(&opts->lock);
 
 
 	return result;
 	return result;
@@ -1434,7 +1434,7 @@ static ssize_t f_ss_opts_bulk_buflen_show(struct f_ss_opts *opts, char *page)
 	int result;
 	int result;
 
 
 	mutex_lock(&opts->lock);
 	mutex_lock(&opts->lock);
-	result = sprintf(page, "%d", opts->bulk_buflen);
+	result = sprintf(page, "%u", opts->bulk_buflen);
 	mutex_unlock(&opts->lock);
 	mutex_unlock(&opts->lock);
 
 
 	return result;
 	return result;
@@ -1473,7 +1473,7 @@ static ssize_t f_ss_opts_int_interval_show(struct f_ss_opts *opts, char *page)
 	int result;
 	int result;
 
 
 	mutex_lock(&opts->lock);
 	mutex_lock(&opts->lock);
-	result = sprintf(page, "%d", opts->int_interval);
+	result = sprintf(page, "%u", opts->int_interval);
 	mutex_unlock(&opts->lock);
 	mutex_unlock(&opts->lock);
 
 
 	return result;
 	return result;
@@ -1517,7 +1517,7 @@ static ssize_t f_ss_opts_int_maxpacket_show(struct f_ss_opts *opts, char *page)
 	int result;
 	int result;
 
 
 	mutex_lock(&opts->lock);
 	mutex_lock(&opts->lock);
-	result = sprintf(page, "%d", opts->int_maxpacket);
+	result = sprintf(page, "%u", opts->int_maxpacket);
 	mutex_unlock(&opts->lock);
 	mutex_unlock(&opts->lock);
 
 
 	return result;
 	return result;
@@ -1561,7 +1561,7 @@ static ssize_t f_ss_opts_int_mult_show(struct f_ss_opts *opts, char *page)
 	int result;
 	int result;
 
 
 	mutex_lock(&opts->lock);
 	mutex_lock(&opts->lock);
-	result = sprintf(page, "%d", opts->int_mult);
+	result = sprintf(page, "%u", opts->int_mult);
 	mutex_unlock(&opts->lock);
 	mutex_unlock(&opts->lock);
 
 
 	return result;
 	return result;
@@ -1605,7 +1605,7 @@ static ssize_t f_ss_opts_int_maxburst_show(struct f_ss_opts *opts, char *page)
 	int result;
 	int result;
 
 
 	mutex_lock(&opts->lock);
 	mutex_lock(&opts->lock);
-	result = sprintf(page, "%d", opts->int_maxburst);
+	result = sprintf(page, "%u", opts->int_maxburst);
 	mutex_unlock(&opts->lock);
 	mutex_unlock(&opts->lock);
 
 
 	return result;
 	return result;

+ 10 - 6
drivers/usb/gadget/function/f_uac1.c

@@ -31,7 +31,7 @@ static int generic_get_cmd(struct usb_audio_control *con, u8 cmd);
  */
  */
 #define F_AUDIO_AC_INTERFACE	0
 #define F_AUDIO_AC_INTERFACE	0
 #define F_AUDIO_AS_INTERFACE	1
 #define F_AUDIO_AS_INTERFACE	1
-#define F_AUDIO_NUM_INTERFACES	2
+#define F_AUDIO_NUM_INTERFACES	1
 
 
 /* B.3.1  Standard AC Interface Descriptor */
 /* B.3.1  Standard AC Interface Descriptor */
 static struct usb_interface_descriptor ac_interface_desc = {
 static struct usb_interface_descriptor ac_interface_desc = {
@@ -42,14 +42,18 @@ static struct usb_interface_descriptor ac_interface_desc = {
 	.bInterfaceSubClass =	USB_SUBCLASS_AUDIOCONTROL,
 	.bInterfaceSubClass =	USB_SUBCLASS_AUDIOCONTROL,
 };
 };
 
 
-DECLARE_UAC_AC_HEADER_DESCRIPTOR(2);
+/*
+ * The number of AudioStreaming and MIDIStreaming interfaces
+ * in the Audio Interface Collection
+ */
+DECLARE_UAC_AC_HEADER_DESCRIPTOR(1);
 
 
 #define UAC_DT_AC_HEADER_LENGTH	UAC_DT_AC_HEADER_SIZE(F_AUDIO_NUM_INTERFACES)
 #define UAC_DT_AC_HEADER_LENGTH	UAC_DT_AC_HEADER_SIZE(F_AUDIO_NUM_INTERFACES)
 /* 1 input terminal, 1 output terminal and 1 feature unit */
 /* 1 input terminal, 1 output terminal and 1 feature unit */
 #define UAC_DT_TOTAL_LENGTH (UAC_DT_AC_HEADER_LENGTH + UAC_DT_INPUT_TERMINAL_SIZE \
 #define UAC_DT_TOTAL_LENGTH (UAC_DT_AC_HEADER_LENGTH + UAC_DT_INPUT_TERMINAL_SIZE \
 	+ UAC_DT_OUTPUT_TERMINAL_SIZE + UAC_DT_FEATURE_UNIT_SIZE(0))
 	+ UAC_DT_OUTPUT_TERMINAL_SIZE + UAC_DT_FEATURE_UNIT_SIZE(0))
 /* B.3.2  Class-Specific AC Interface Descriptor */
 /* B.3.2  Class-Specific AC Interface Descriptor */
-static struct uac1_ac_header_descriptor_2 ac_header_desc = {
+static struct uac1_ac_header_descriptor_1 ac_header_desc = {
 	.bLength =		UAC_DT_AC_HEADER_LENGTH,
 	.bLength =		UAC_DT_AC_HEADER_LENGTH,
 	.bDescriptorType =	USB_DT_CS_INTERFACE,
 	.bDescriptorType =	USB_DT_CS_INTERFACE,
 	.bDescriptorSubtype =	UAC_HEADER,
 	.bDescriptorSubtype =	UAC_HEADER,
@@ -57,8 +61,8 @@ static struct uac1_ac_header_descriptor_2 ac_header_desc = {
 	.wTotalLength =		__constant_cpu_to_le16(UAC_DT_TOTAL_LENGTH),
 	.wTotalLength =		__constant_cpu_to_le16(UAC_DT_TOTAL_LENGTH),
 	.bInCollection =	F_AUDIO_NUM_INTERFACES,
 	.bInCollection =	F_AUDIO_NUM_INTERFACES,
 	.baInterfaceNr = {
 	.baInterfaceNr = {
-		[0] =		F_AUDIO_AC_INTERFACE,
-		[1] =		F_AUDIO_AS_INTERFACE,
+	/* Interface number of the first AudioStream interface */
+		[0] =		1,
 	}
 	}
 };
 };
 
 
@@ -584,6 +588,7 @@ static int f_audio_set_alt(struct usb_function *f, unsigned intf, unsigned alt)
 
 
 	if (intf == 1) {
 	if (intf == 1) {
 		if (alt == 1) {
 		if (alt == 1) {
+			config_ep_by_speed(cdev->gadget, f, out_ep);
 			usb_ep_enable(out_ep);
 			usb_ep_enable(out_ep);
 			out_ep->driver_data = audio;
 			out_ep->driver_data = audio;
 			audio->copy_buf = f_audio_buffer_alloc(audio_buf_size);
 			audio->copy_buf = f_audio_buffer_alloc(audio_buf_size);
@@ -669,7 +674,6 @@ f_audio_bind(struct usb_configuration *c, struct usb_function *f)
 
 
 	audio_opts = container_of(f->fi, struct f_uac1_opts, func_inst);
 	audio_opts = container_of(f->fi, struct f_uac1_opts, func_inst);
 	audio->card.gadget = c->cdev->gadget;
 	audio->card.gadget = c->cdev->gadget;
-	audio_opts->card = &audio->card;
 	/* set up ASLA audio devices */
 	/* set up ASLA audio devices */
 	if (!audio_opts->bound) {
 	if (!audio_opts->bound) {
 		status = gaudio_setup(&audio->card);
 		status = gaudio_setup(&audio->card);

+ 128 - 8
drivers/usb/gadget/function/f_uvc.c

@@ -27,10 +27,11 @@
 #include <media/v4l2-dev.h>
 #include <media/v4l2-dev.h>
 #include <media/v4l2-event.h>
 #include <media/v4l2-event.h>
 
 
+#include "u_uvc.h"
 #include "uvc.h"
 #include "uvc.h"
+#include "uvc_configfs.h"
 #include "uvc_v4l2.h"
 #include "uvc_v4l2.h"
 #include "uvc_video.h"
 #include "uvc_video.h"
-#include "u_uvc.h"
 
 
 unsigned int uvc_gadget_trace_param;
 unsigned int uvc_gadget_trace_param;
 
 
@@ -509,6 +510,9 @@ uvc_copy_descriptors(struct uvc_device *uvc, enum usb_device_speed speed)
 		break;
 		break;
 	}
 	}
 
 
+	if (!uvc_control_desc || !uvc_streaming_cls)
+		return ERR_PTR(-ENODEV);
+
 	/* Descriptors layout
 	/* Descriptors layout
 	 *
 	 *
 	 * uvc_iad
 	 * uvc_iad
@@ -605,7 +609,7 @@ uvc_function_bind(struct usb_configuration *c, struct usb_function *f)
 
 
 	INFO(cdev, "uvc_function_bind\n");
 	INFO(cdev, "uvc_function_bind\n");
 
 
-	opts = to_f_uvc_opts(f->fi);
+	opts = fi_to_f_uvc_opts(f->fi);
 	/* Sanity check the streaming endpoint module parameters.
 	/* Sanity check the streaming endpoint module parameters.
 	 */
 	 */
 	opts->streaming_interval = clamp(opts->streaming_interval, 1U, 16U);
 	opts->streaming_interval = clamp(opts->streaming_interval, 1U, 16U);
@@ -700,10 +704,27 @@ uvc_function_bind(struct usb_configuration *c, struct usb_function *f)
 
 
 	/* Copy descriptors */
 	/* Copy descriptors */
 	f->fs_descriptors = uvc_copy_descriptors(uvc, USB_SPEED_FULL);
 	f->fs_descriptors = uvc_copy_descriptors(uvc, USB_SPEED_FULL);
-	if (gadget_is_dualspeed(cdev->gadget))
+	if (IS_ERR(f->fs_descriptors)) {
+		ret = PTR_ERR(f->fs_descriptors);
+		f->fs_descriptors = NULL;
+		goto error;
+	}
+	if (gadget_is_dualspeed(cdev->gadget)) {
 		f->hs_descriptors = uvc_copy_descriptors(uvc, USB_SPEED_HIGH);
 		f->hs_descriptors = uvc_copy_descriptors(uvc, USB_SPEED_HIGH);
-	if (gadget_is_superspeed(c->cdev->gadget))
+		if (IS_ERR(f->hs_descriptors)) {
+			ret = PTR_ERR(f->hs_descriptors);
+			f->hs_descriptors = NULL;
+			goto error;
+		}
+	}
+	if (gadget_is_superspeed(c->cdev->gadget)) {
 		f->ss_descriptors = uvc_copy_descriptors(uvc, USB_SPEED_SUPER);
 		f->ss_descriptors = uvc_copy_descriptors(uvc, USB_SPEED_SUPER);
+		if (IS_ERR(f->ss_descriptors)) {
+			ret = PTR_ERR(f->ss_descriptors);
+			f->ss_descriptors = NULL;
+			goto error;
+		}
+	}
 
 
 	/* Preallocate control endpoint request. */
 	/* Preallocate control endpoint request. */
 	uvc->control_req = usb_ep_alloc_request(cdev->gadget->ep0, GFP_KERNEL);
 	uvc->control_req = usb_ep_alloc_request(cdev->gadget->ep0, GFP_KERNEL);
@@ -766,27 +787,106 @@ error:
 
 
 static void uvc_free_inst(struct usb_function_instance *f)
 static void uvc_free_inst(struct usb_function_instance *f)
 {
 {
-	struct f_uvc_opts *opts = to_f_uvc_opts(f);
+	struct f_uvc_opts *opts = fi_to_f_uvc_opts(f);
 
 
+	mutex_destroy(&opts->lock);
 	kfree(opts);
 	kfree(opts);
 }
 }
 
 
 static struct usb_function_instance *uvc_alloc_inst(void)
 static struct usb_function_instance *uvc_alloc_inst(void)
 {
 {
 	struct f_uvc_opts *opts;
 	struct f_uvc_opts *opts;
+	struct uvc_camera_terminal_descriptor *cd;
+	struct uvc_processing_unit_descriptor *pd;
+	struct uvc_output_terminal_descriptor *od;
+	struct uvc_color_matching_descriptor *md;
+	struct uvc_descriptor_header **ctl_cls;
 
 
 	opts = kzalloc(sizeof(*opts), GFP_KERNEL);
 	opts = kzalloc(sizeof(*opts), GFP_KERNEL);
 	if (!opts)
 	if (!opts)
 		return ERR_PTR(-ENOMEM);
 		return ERR_PTR(-ENOMEM);
 	opts->func_inst.free_func_inst = uvc_free_inst;
 	opts->func_inst.free_func_inst = uvc_free_inst;
-
+	mutex_init(&opts->lock);
+
+	cd = &opts->uvc_camera_terminal;
+	cd->bLength			= UVC_DT_CAMERA_TERMINAL_SIZE(3);
+	cd->bDescriptorType		= USB_DT_CS_INTERFACE;
+	cd->bDescriptorSubType		= UVC_VC_INPUT_TERMINAL;
+	cd->bTerminalID			= 1;
+	cd->wTerminalType		= cpu_to_le16(0x0201);
+	cd->bAssocTerminal		= 0;
+	cd->iTerminal			= 0;
+	cd->wObjectiveFocalLengthMin	= cpu_to_le16(0);
+	cd->wObjectiveFocalLengthMax	= cpu_to_le16(0);
+	cd->wOcularFocalLength		= cpu_to_le16(0);
+	cd->bControlSize		= 3;
+	cd->bmControls[0]		= 2;
+	cd->bmControls[1]		= 0;
+	cd->bmControls[2]		= 0;
+
+	pd = &opts->uvc_processing;
+	pd->bLength			= UVC_DT_PROCESSING_UNIT_SIZE(2);
+	pd->bDescriptorType		= USB_DT_CS_INTERFACE;
+	pd->bDescriptorSubType		= UVC_VC_PROCESSING_UNIT;
+	pd->bUnitID			= 2;
+	pd->bSourceID			= 1;
+	pd->wMaxMultiplier		= cpu_to_le16(16*1024);
+	pd->bControlSize		= 2;
+	pd->bmControls[0]		= 1;
+	pd->bmControls[1]		= 0;
+	pd->iProcessing			= 0;
+
+	od = &opts->uvc_output_terminal;
+	od->bLength			= UVC_DT_OUTPUT_TERMINAL_SIZE;
+	od->bDescriptorType		= USB_DT_CS_INTERFACE;
+	od->bDescriptorSubType		= UVC_VC_OUTPUT_TERMINAL;
+	od->bTerminalID			= 3;
+	od->wTerminalType		= cpu_to_le16(0x0101);
+	od->bAssocTerminal		= 0;
+	od->bSourceID			= 2;
+	od->iTerminal			= 0;
+
+	md = &opts->uvc_color_matching;
+	md->bLength			= UVC_DT_COLOR_MATCHING_SIZE;
+	md->bDescriptorType		= USB_DT_CS_INTERFACE;
+	md->bDescriptorSubType		= UVC_VS_COLORFORMAT;
+	md->bColorPrimaries		= 1;
+	md->bTransferCharacteristics	= 1;
+	md->bMatrixCoefficients		= 4;
+
+	/* Prepare fs control class descriptors for configfs-based gadgets */
+	ctl_cls = opts->uvc_fs_control_cls;
+	ctl_cls[0] = NULL;	/* assigned elsewhere by configfs */
+	ctl_cls[1] = (struct uvc_descriptor_header *)cd;
+	ctl_cls[2] = (struct uvc_descriptor_header *)pd;
+	ctl_cls[3] = (struct uvc_descriptor_header *)od;
+	ctl_cls[4] = NULL;	/* NULL-terminate */
+	opts->fs_control =
+		(const struct uvc_descriptor_header * const *)ctl_cls;
+
+	/* Prepare hs control class descriptors for configfs-based gadgets */
+	ctl_cls = opts->uvc_ss_control_cls;
+	ctl_cls[0] = NULL;	/* assigned elsewhere by configfs */
+	ctl_cls[1] = (struct uvc_descriptor_header *)cd;
+	ctl_cls[2] = (struct uvc_descriptor_header *)pd;
+	ctl_cls[3] = (struct uvc_descriptor_header *)od;
+	ctl_cls[4] = NULL;	/* NULL-terminate */
+	opts->ss_control =
+		(const struct uvc_descriptor_header * const *)ctl_cls;
+
+	opts->streaming_interval = 1;
+	opts->streaming_maxpacket = 1024;
+
+	uvcg_attach_configfs(opts);
 	return &opts->func_inst;
 	return &opts->func_inst;
 }
 }
 
 
 static void uvc_free(struct usb_function *f)
 static void uvc_free(struct usb_function *f)
 {
 {
 	struct uvc_device *uvc = to_uvc(f);
 	struct uvc_device *uvc = to_uvc(f);
-
+	struct f_uvc_opts *opts = container_of(f->fi, struct f_uvc_opts,
+					       func_inst);
+	--opts->refcnt;
 	kfree(uvc);
 	kfree(uvc);
 }
 }
 
 
@@ -812,19 +912,39 @@ static struct usb_function *uvc_alloc(struct usb_function_instance *fi)
 {
 {
 	struct uvc_device *uvc;
 	struct uvc_device *uvc;
 	struct f_uvc_opts *opts;
 	struct f_uvc_opts *opts;
+	struct uvc_descriptor_header **strm_cls;
 
 
 	uvc = kzalloc(sizeof(*uvc), GFP_KERNEL);
 	uvc = kzalloc(sizeof(*uvc), GFP_KERNEL);
 	if (uvc == NULL)
 	if (uvc == NULL)
 		return ERR_PTR(-ENOMEM);
 		return ERR_PTR(-ENOMEM);
 
 
 	uvc->state = UVC_STATE_DISCONNECTED;
 	uvc->state = UVC_STATE_DISCONNECTED;
-	opts = to_f_uvc_opts(fi);
+	opts = fi_to_f_uvc_opts(fi);
+
+	mutex_lock(&opts->lock);
+	if (opts->uvc_fs_streaming_cls) {
+		strm_cls = opts->uvc_fs_streaming_cls;
+		opts->fs_streaming =
+			(const struct uvc_descriptor_header * const *)strm_cls;
+	}
+	if (opts->uvc_hs_streaming_cls) {
+		strm_cls = opts->uvc_hs_streaming_cls;
+		opts->hs_streaming =
+			(const struct uvc_descriptor_header * const *)strm_cls;
+	}
+	if (opts->uvc_ss_streaming_cls) {
+		strm_cls = opts->uvc_ss_streaming_cls;
+		opts->ss_streaming =
+			(const struct uvc_descriptor_header * const *)strm_cls;
+	}
 
 
 	uvc->desc.fs_control = opts->fs_control;
 	uvc->desc.fs_control = opts->fs_control;
 	uvc->desc.ss_control = opts->ss_control;
 	uvc->desc.ss_control = opts->ss_control;
 	uvc->desc.fs_streaming = opts->fs_streaming;
 	uvc->desc.fs_streaming = opts->fs_streaming;
 	uvc->desc.hs_streaming = opts->hs_streaming;
 	uvc->desc.hs_streaming = opts->hs_streaming;
 	uvc->desc.ss_streaming = opts->ss_streaming;
 	uvc->desc.ss_streaming = opts->ss_streaming;
+	++opts->refcnt;
+	mutex_unlock(&opts->lock);
 
 
 	/* Register the function. */
 	/* Register the function. */
 	uvc->func.name = "uvc";
 	uvc->func.name = "uvc";

+ 1 - 3
drivers/usb/gadget/function/u_ether.c

@@ -729,9 +729,7 @@ static int get_ether_addr_str(u8 dev_addr[ETH_ALEN], char *str, int len)
 	if (len < 18)
 	if (len < 18)
 		return -EINVAL;
 		return -EINVAL;
 
 
-	snprintf(str, len, "%02x:%02x:%02x:%02x:%02x:%02x",
-		 dev_addr[0], dev_addr[1], dev_addr[2],
-		 dev_addr[3], dev_addr[4], dev_addr[5]);
+	snprintf(str, len, "%pM", dev_addr);
 	return 18;
 	return 18;
 }
 }
 
 

+ 25 - 0
drivers/usb/gadget/function/u_fs.h

@@ -19,6 +19,7 @@
 #include <linux/usb/composite.h>
 #include <linux/usb/composite.h>
 #include <linux/list.h>
 #include <linux/list.h>
 #include <linux/mutex.h>
 #include <linux/mutex.h>
+#include <linux/workqueue.h>
 
 
 #ifdef VERBOSE_DEBUG
 #ifdef VERBOSE_DEBUG
 #ifndef pr_vdebug
 #ifndef pr_vdebug
@@ -92,6 +93,26 @@ enum ffs_state {
 	 */
 	 */
 	FFS_ACTIVE,
 	FFS_ACTIVE,
 
 
+	/*
+	 * Function is visible to host, but it's not functional. All
+	 * setup requests are stalled and transfers on another endpoints
+	 * are refused. All epfiles, except ep0, are deleted so there
+	 * is no way to perform any operations on them.
+	 *
+	 * This state is set after closing all functionfs files, when
+	 * mount parameter "no_disconnect=1" has been set. Function will
+	 * remain in deactivated state until filesystem is umounted or
+	 * ep0 is opened again. In the second case functionfs state will
+	 * be reset, and it will be ready for descriptors and strings
+	 * writing.
+	 *
+	 * This is useful only when functionfs is composed to gadget
+	 * with another function which can perform some critical
+	 * operations, and it's strongly desired to have this operations
+	 * completed, even after functionfs files closure.
+	 */
+	FFS_DEACTIVATED,
+
 	/*
 	/*
 	 * All endpoints have been closed.  This state is also set if
 	 * All endpoints have been closed.  This state is also set if
 	 * we encounter an unrecoverable error.  The only
 	 * we encounter an unrecoverable error.  The only
@@ -251,6 +272,10 @@ struct ffs_data {
 		kgid_t				gid;
 		kgid_t				gid;
 	}				file_perms;
 	}				file_perms;
 
 
+	struct eventfd_ctx *ffs_eventfd;
+	bool no_disconnect;
+	struct work_struct reset_work;
+
 	/*
 	/*
 	 * The endpoint files, filled by ffs_epfiles_create(),
 	 * The endpoint files, filled by ffs_epfiles_create(),
 	 * destroyed by ffs_epfiles_destroy().
 	 * destroyed by ffs_epfiles_destroy().

+ 1 - 2
drivers/usb/gadget/function/u_uac1.c

@@ -308,8 +308,7 @@ int gaudio_setup(struct gaudio *card)
  */
  */
 void gaudio_cleanup(struct gaudio *the_card)
 void gaudio_cleanup(struct gaudio *the_card)
 {
 {
-	if (the_card) {
+	if (the_card)
 		gaudio_close_snd_dev(the_card);
 		gaudio_close_snd_dev(the_card);
-	}
 }
 }
 
 

+ 0 - 1
drivers/usb/gadget/function/u_uac1.h

@@ -70,7 +70,6 @@ struct f_uac1_opts {
 	unsigned			fn_play_alloc:1;
 	unsigned			fn_play_alloc:1;
 	unsigned			fn_cap_alloc:1;
 	unsigned			fn_cap_alloc:1;
 	unsigned			fn_cntl_alloc:1;
 	unsigned			fn_cntl_alloc:1;
-	struct gaudio			*card;
 	struct mutex			lock;
 	struct mutex			lock;
 	int				refcnt;
 	int				refcnt;
 };
 };

+ 51 - 1
drivers/usb/gadget/function/u_uvc.h

@@ -17,8 +17,9 @@
 #define U_UVC_H
 #define U_UVC_H
 
 
 #include <linux/usb/composite.h>
 #include <linux/usb/composite.h>
+#include <linux/usb/video.h>
 
 
-#define to_f_uvc_opts(f)	container_of(f, struct f_uvc_opts, func_inst)
+#define fi_to_f_uvc_opts(f)	container_of(f, struct f_uvc_opts, func_inst)
 
 
 struct f_uvc_opts {
 struct f_uvc_opts {
 	struct usb_function_instance			func_inst;
 	struct usb_function_instance			func_inst;
@@ -26,11 +27,60 @@ struct f_uvc_opts {
 	unsigned int					streaming_interval;
 	unsigned int					streaming_interval;
 	unsigned int					streaming_maxpacket;
 	unsigned int					streaming_maxpacket;
 	unsigned int					streaming_maxburst;
 	unsigned int					streaming_maxburst;
+
+	/*
+	 * Control descriptors array pointers for full-/high-speed and
+	 * super-speed. They point by default to the uvc_fs_control_cls and
+	 * uvc_ss_control_cls arrays respectively. Legacy gadgets must
+	 * override them in their gadget bind callback.
+	 */
 	const struct uvc_descriptor_header * const	*fs_control;
 	const struct uvc_descriptor_header * const	*fs_control;
 	const struct uvc_descriptor_header * const	*ss_control;
 	const struct uvc_descriptor_header * const	*ss_control;
+
+	/*
+	 * Streaming descriptors array pointers for full-speed, high-speed and
+	 * super-speed. They will point to the uvc_[fhs]s_streaming_cls arrays
+	 * for configfs-based gadgets. Legacy gadgets must initialize them in
+	 * their gadget bind callback.
+	 */
 	const struct uvc_descriptor_header * const	*fs_streaming;
 	const struct uvc_descriptor_header * const	*fs_streaming;
 	const struct uvc_descriptor_header * const	*hs_streaming;
 	const struct uvc_descriptor_header * const	*hs_streaming;
 	const struct uvc_descriptor_header * const	*ss_streaming;
 	const struct uvc_descriptor_header * const	*ss_streaming;
+
+	/* Default control descriptors for configfs-based gadgets. */
+	struct uvc_camera_terminal_descriptor		uvc_camera_terminal;
+	struct uvc_processing_unit_descriptor		uvc_processing;
+	struct uvc_output_terminal_descriptor		uvc_output_terminal;
+	struct uvc_color_matching_descriptor		uvc_color_matching;
+
+	/*
+	 * Control descriptors pointers arrays for full-/high-speed and
+	 * super-speed. The first element is a configurable control header
+	 * descriptor, the other elements point to the fixed default control
+	 * descriptors. Used by configfs only, must not be touched by legacy
+	 * gadgets.
+	 */
+	struct uvc_descriptor_header			*uvc_fs_control_cls[5];
+	struct uvc_descriptor_header			*uvc_ss_control_cls[5];
+
+	/*
+	 * Streaming descriptors for full-speed, high-speed and super-speed.
+	 * Used by configfs only, must not be touched by legacy gadgets. The
+	 * arrays are allocated at runtime as the number of descriptors isn't
+	 * known in advance.
+	 */
+	struct uvc_descriptor_header			**uvc_fs_streaming_cls;
+	struct uvc_descriptor_header			**uvc_hs_streaming_cls;
+	struct uvc_descriptor_header			**uvc_ss_streaming_cls;
+
+	/*
+	 * Read/write access to configfs attributes is handled by configfs.
+	 *
+	 * This lock protects the descriptors from concurrent access by
+	 * read/write and symlink creation/removal.
+	 */
+	struct mutex			lock;
+	int				refcnt;
 };
 };
 
 
 void uvc_set_trace_param(unsigned int trace);
 void uvc_set_trace_param(unsigned int trace);

+ 2468 - 0
drivers/usb/gadget/function/uvc_configfs.c

@@ -0,0 +1,2468 @@
+/*
+ * uvc_configfs.c
+ *
+ * Configfs support for the uvc function.
+ *
+ * Copyright (c) 2014 Samsung Electronics Co., Ltd.
+ *		http://www.samsung.com
+ *
+ * Author: Andrzej Pietrasiewicz <andrzej.p@samsung.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+#include "u_uvc.h"
+#include "uvc_configfs.h"
+
+#define UVCG_STREAMING_CONTROL_SIZE	1
+
+#define CONFIGFS_ATTR_OPS_RO(_item)					\
+static ssize_t _item##_attr_show(struct config_item *item,		\
+				 struct configfs_attribute *attr,	\
+				 char *page)				\
+{									\
+	struct _item *_item = to_##_item(item);				\
+	struct _item##_attribute *_item##_attr =			\
+		container_of(attr, struct _item##_attribute, attr);	\
+	ssize_t ret = 0;						\
+									\
+	if (_item##_attr->show)						\
+		ret = _item##_attr->show(_item, page);			\
+	return ret;							\
+}
+
+static inline struct f_uvc_opts *to_f_uvc_opts(struct config_item *item);
+
+/* control/header/<NAME> */
+DECLARE_UVC_HEADER_DESCRIPTOR(1);
+
+struct uvcg_control_header {
+	struct config_item		item;
+	struct UVC_HEADER_DESCRIPTOR(1)	desc;
+	unsigned			linked;
+};
+
+static struct uvcg_control_header *to_uvcg_control_header(struct config_item *item)
+{
+	return container_of(item, struct uvcg_control_header, item);
+}
+
+CONFIGFS_ATTR_STRUCT(uvcg_control_header);
+CONFIGFS_ATTR_OPS(uvcg_control_header);
+
+static struct configfs_item_operations uvcg_control_header_item_ops = {
+	.show_attribute		= uvcg_control_header_attr_show,
+	.store_attribute	= uvcg_control_header_attr_store,
+};
+
+#define UVCG_CTRL_HDR_ATTR(cname, aname, conv, str2u, uxx, vnoc, limit)	\
+static ssize_t uvcg_control_header_##cname##_show(			\
+	struct uvcg_control_header *ch, char *page)			\
+{									\
+	struct f_uvc_opts *opts;					\
+	struct config_item *opts_item;					\
+	struct mutex *su_mutex = &ch->item.ci_group->cg_subsys->su_mutex;\
+	int result;							\
+									\
+	mutex_lock(su_mutex); /* for navigating configfs hierarchy */	\
+									\
+	opts_item = ch->item.ci_parent->ci_parent->ci_parent;		\
+	opts = to_f_uvc_opts(opts_item);				\
+									\
+	mutex_lock(&opts->lock);					\
+	result = sprintf(page, "%d\n", conv(ch->desc.aname));		\
+	mutex_unlock(&opts->lock);					\
+									\
+	mutex_unlock(su_mutex);						\
+	return result;							\
+}									\
+									\
+static ssize_t								\
+uvcg_control_header_##cname##_store(struct uvcg_control_header *ch,	\
+			   const char *page, size_t len)		\
+{									\
+	struct f_uvc_opts *opts;					\
+	struct config_item *opts_item;					\
+	struct mutex *su_mutex = &ch->item.ci_group->cg_subsys->su_mutex;\
+	int ret;							\
+	uxx num;							\
+									\
+	mutex_lock(su_mutex); /* for navigating configfs hierarchy */	\
+									\
+	opts_item = ch->item.ci_parent->ci_parent->ci_parent;		\
+	opts = to_f_uvc_opts(opts_item);				\
+									\
+	mutex_lock(&opts->lock);					\
+	if (ch->linked || opts->refcnt) {				\
+		ret = -EBUSY;						\
+		goto end;						\
+	}								\
+									\
+	ret = str2u(page, 0, &num);					\
+	if (ret)							\
+		goto end;						\
+									\
+	if (num > limit) {						\
+		ret = -EINVAL;						\
+		goto end;						\
+	}								\
+	ch->desc.aname = vnoc(num);					\
+	ret = len;							\
+end:									\
+	mutex_unlock(&opts->lock);					\
+	mutex_unlock(su_mutex);						\
+	return ret;							\
+}									\
+									\
+static struct uvcg_control_header_attribute				\
+	uvcg_control_header_##cname =					\
+	__CONFIGFS_ATTR(aname, S_IRUGO | S_IWUSR,			\
+			uvcg_control_header_##cname##_show,		\
+			uvcg_control_header_##cname##_store)
+
+UVCG_CTRL_HDR_ATTR(bcd_uvc, bcdUVC, le16_to_cpu, kstrtou16, u16, cpu_to_le16,
+		   0xffff);
+
+UVCG_CTRL_HDR_ATTR(dw_clock_frequency, dwClockFrequency, le32_to_cpu, kstrtou32,
+		   u32, cpu_to_le32, 0x7fffffff);
+
+#undef UVCG_CTRL_HDR_ATTR
+
+static struct configfs_attribute *uvcg_control_header_attrs[] = {
+	&uvcg_control_header_bcd_uvc.attr,
+	&uvcg_control_header_dw_clock_frequency.attr,
+	NULL,
+};
+
+static struct config_item_type uvcg_control_header_type = {
+	.ct_item_ops	= &uvcg_control_header_item_ops,
+	.ct_attrs	= uvcg_control_header_attrs,
+	.ct_owner	= THIS_MODULE,
+};
+
+static struct config_item *uvcg_control_header_make(struct config_group *group,
+						    const char *name)
+{
+	struct uvcg_control_header *h;
+
+	h = kzalloc(sizeof(*h), GFP_KERNEL);
+	if (!h)
+		return ERR_PTR(-ENOMEM);
+
+	h->desc.bLength			= UVC_DT_HEADER_SIZE(1);
+	h->desc.bDescriptorType		= USB_DT_CS_INTERFACE;
+	h->desc.bDescriptorSubType	= UVC_VC_HEADER;
+	h->desc.bcdUVC			= cpu_to_le16(0x0100);
+	h->desc.dwClockFrequency	= cpu_to_le32(48000000);
+
+	config_item_init_type_name(&h->item, name, &uvcg_control_header_type);
+
+	return &h->item;
+}
+
+static void uvcg_control_header_drop(struct config_group *group,
+			      struct config_item *item)
+{
+	struct uvcg_control_header *h = to_uvcg_control_header(item);
+
+	kfree(h);
+}
+
+/* control/header */
+static struct uvcg_control_header_grp {
+	struct config_group	group;
+} uvcg_control_header_grp;
+
+static struct configfs_group_operations uvcg_control_header_grp_ops = {
+	.make_item		= uvcg_control_header_make,
+	.drop_item		= uvcg_control_header_drop,
+};
+
+static struct config_item_type uvcg_control_header_grp_type = {
+	.ct_group_ops	= &uvcg_control_header_grp_ops,
+	.ct_owner	= THIS_MODULE,
+};
+
+/* control/processing/default */
+static struct uvcg_default_processing {
+	struct config_group	group;
+} uvcg_default_processing;
+
+static inline struct uvcg_default_processing
+*to_uvcg_default_processing(struct config_item *item)
+{
+	return container_of(to_config_group(item),
+			    struct uvcg_default_processing, group);
+}
+
+CONFIGFS_ATTR_STRUCT(uvcg_default_processing);
+CONFIGFS_ATTR_OPS_RO(uvcg_default_processing);
+
+static struct configfs_item_operations uvcg_default_processing_item_ops = {
+	.show_attribute		= uvcg_default_processing_attr_show,
+};
+
+#define UVCG_DEFAULT_PROCESSING_ATTR(cname, aname, conv)		\
+static ssize_t uvcg_default_processing_##cname##_show(			\
+	struct uvcg_default_processing *dp, char *page)			\
+{									\
+	struct f_uvc_opts *opts;					\
+	struct config_item *opts_item;					\
+	struct mutex *su_mutex = &dp->group.cg_subsys->su_mutex;	\
+	struct uvc_processing_unit_descriptor *pd;			\
+	int result;							\
+									\
+	mutex_lock(su_mutex); /* for navigating configfs hierarchy */	\
+									\
+	opts_item = dp->group.cg_item.ci_parent->ci_parent->ci_parent;	\
+	opts = to_f_uvc_opts(opts_item);				\
+	pd = &opts->uvc_processing;					\
+									\
+	mutex_lock(&opts->lock);					\
+	result = sprintf(page, "%d\n", conv(pd->aname));		\
+	mutex_unlock(&opts->lock);					\
+									\
+	mutex_unlock(su_mutex);						\
+	return result;							\
+}									\
+									\
+static struct uvcg_default_processing_attribute				\
+	uvcg_default_processing_##cname =				\
+	__CONFIGFS_ATTR_RO(aname, uvcg_default_processing_##cname##_show)
+
+#define identity_conv(x) (x)
+
+UVCG_DEFAULT_PROCESSING_ATTR(b_unit_id, bUnitID, identity_conv);
+UVCG_DEFAULT_PROCESSING_ATTR(b_source_id, bSourceID, identity_conv);
+UVCG_DEFAULT_PROCESSING_ATTR(w_max_multiplier, wMaxMultiplier, le16_to_cpu);
+UVCG_DEFAULT_PROCESSING_ATTR(i_processing, iProcessing, identity_conv);
+
+#undef identity_conv
+
+#undef UVCG_DEFAULT_PROCESSING_ATTR
+
+static ssize_t uvcg_default_processing_bm_controls_show(
+	struct uvcg_default_processing *dp, char *page)
+{
+	struct f_uvc_opts *opts;
+	struct config_item *opts_item;
+	struct mutex *su_mutex = &dp->group.cg_subsys->su_mutex;
+	struct uvc_processing_unit_descriptor *pd;
+	int result, i;
+	char *pg = page;
+
+	mutex_lock(su_mutex); /* for navigating configfs hierarchy */
+
+	opts_item = dp->group.cg_item.ci_parent->ci_parent->ci_parent;
+	opts = to_f_uvc_opts(opts_item);
+	pd = &opts->uvc_processing;
+
+	mutex_lock(&opts->lock);
+	for (result = 0, i = 0; i < pd->bControlSize; ++i) {
+		result += sprintf(pg, "%d\n", pd->bmControls[i]);
+		pg = page + result;
+	}
+	mutex_unlock(&opts->lock);
+
+	mutex_unlock(su_mutex);
+
+	return result;
+}
+
+static struct uvcg_default_processing_attribute
+	uvcg_default_processing_bm_controls =
+	__CONFIGFS_ATTR_RO(bmControls,
+		uvcg_default_processing_bm_controls_show);
+
+static struct configfs_attribute *uvcg_default_processing_attrs[] = {
+	&uvcg_default_processing_b_unit_id.attr,
+	&uvcg_default_processing_b_source_id.attr,
+	&uvcg_default_processing_w_max_multiplier.attr,
+	&uvcg_default_processing_bm_controls.attr,
+	&uvcg_default_processing_i_processing.attr,
+	NULL,
+};
+
+static struct config_item_type uvcg_default_processing_type = {
+	.ct_item_ops	= &uvcg_default_processing_item_ops,
+	.ct_attrs	= uvcg_default_processing_attrs,
+	.ct_owner	= THIS_MODULE,
+};
+
+/* struct uvcg_processing {}; */
+
+static struct config_group *uvcg_processing_default_groups[] = {
+	&uvcg_default_processing.group,
+	NULL,
+};
+
+/* control/processing */
+static struct uvcg_processing_grp {
+	struct config_group	group;
+} uvcg_processing_grp;
+
+static struct config_item_type uvcg_processing_grp_type = {
+	.ct_owner = THIS_MODULE,
+};
+
+/* control/terminal/camera/default */
+static struct uvcg_default_camera {
+	struct config_group	group;
+} uvcg_default_camera;
+
+static inline struct uvcg_default_camera
+*to_uvcg_default_camera(struct config_item *item)
+{
+	return container_of(to_config_group(item),
+			    struct uvcg_default_camera, group);
+}
+
+CONFIGFS_ATTR_STRUCT(uvcg_default_camera);
+CONFIGFS_ATTR_OPS_RO(uvcg_default_camera);
+
+static struct configfs_item_operations uvcg_default_camera_item_ops = {
+	.show_attribute		= uvcg_default_camera_attr_show,
+};
+
+#define UVCG_DEFAULT_CAMERA_ATTR(cname, aname, conv)			\
+static ssize_t uvcg_default_camera_##cname##_show(			\
+	struct uvcg_default_camera *dc, char *page)			\
+{									\
+	struct f_uvc_opts *opts;					\
+	struct config_item *opts_item;					\
+	struct mutex *su_mutex = &dc->group.cg_subsys->su_mutex;	\
+	struct uvc_camera_terminal_descriptor *cd;			\
+	int result;							\
+									\
+	mutex_lock(su_mutex); /* for navigating configfs hierarchy */	\
+									\
+	opts_item = dc->group.cg_item.ci_parent->ci_parent->ci_parent->	\
+			ci_parent;					\
+	opts = to_f_uvc_opts(opts_item);				\
+	cd = &opts->uvc_camera_terminal;				\
+									\
+	mutex_lock(&opts->lock);					\
+	result = sprintf(page, "%d\n", conv(cd->aname));		\
+	mutex_unlock(&opts->lock);					\
+									\
+	mutex_unlock(su_mutex);						\
+									\
+	return result;							\
+}									\
+									\
+static struct uvcg_default_camera_attribute				\
+	uvcg_default_camera_##cname =					\
+	__CONFIGFS_ATTR_RO(aname, uvcg_default_camera_##cname##_show)
+
+#define identity_conv(x) (x)
+
+UVCG_DEFAULT_CAMERA_ATTR(b_terminal_id, bTerminalID, identity_conv);
+UVCG_DEFAULT_CAMERA_ATTR(w_terminal_type, wTerminalType, le16_to_cpu);
+UVCG_DEFAULT_CAMERA_ATTR(b_assoc_terminal, bAssocTerminal, identity_conv);
+UVCG_DEFAULT_CAMERA_ATTR(i_terminal, iTerminal, identity_conv);
+UVCG_DEFAULT_CAMERA_ATTR(w_objective_focal_length_min, wObjectiveFocalLengthMin,
+			 le16_to_cpu);
+UVCG_DEFAULT_CAMERA_ATTR(w_objective_focal_length_max, wObjectiveFocalLengthMax,
+			 le16_to_cpu);
+UVCG_DEFAULT_CAMERA_ATTR(w_ocular_focal_length, wOcularFocalLength,
+			 le16_to_cpu);
+
+#undef identity_conv
+
+#undef UVCG_DEFAULT_CAMERA_ATTR
+
+static ssize_t uvcg_default_camera_bm_controls_show(
+	struct uvcg_default_camera *dc, char *page)
+{
+	struct f_uvc_opts *opts;
+	struct config_item *opts_item;
+	struct mutex *su_mutex = &dc->group.cg_subsys->su_mutex;
+	struct uvc_camera_terminal_descriptor *cd;
+	int result, i;
+	char *pg = page;
+
+	mutex_lock(su_mutex); /* for navigating configfs hierarchy */
+
+	opts_item = dc->group.cg_item.ci_parent->ci_parent->ci_parent->
+			ci_parent;
+	opts = to_f_uvc_opts(opts_item);
+	cd = &opts->uvc_camera_terminal;
+
+	mutex_lock(&opts->lock);
+	for (result = 0, i = 0; i < cd->bControlSize; ++i) {
+		result += sprintf(pg, "%d\n", cd->bmControls[i]);
+		pg = page + result;
+	}
+	mutex_unlock(&opts->lock);
+
+	mutex_unlock(su_mutex);
+	return result;
+}
+
+static struct uvcg_default_camera_attribute
+	uvcg_default_camera_bm_controls =
+	__CONFIGFS_ATTR_RO(bmControls, uvcg_default_camera_bm_controls_show);
+
+static struct configfs_attribute *uvcg_default_camera_attrs[] = {
+	&uvcg_default_camera_b_terminal_id.attr,
+	&uvcg_default_camera_w_terminal_type.attr,
+	&uvcg_default_camera_b_assoc_terminal.attr,
+	&uvcg_default_camera_i_terminal.attr,
+	&uvcg_default_camera_w_objective_focal_length_min.attr,
+	&uvcg_default_camera_w_objective_focal_length_max.attr,
+	&uvcg_default_camera_w_ocular_focal_length.attr,
+	&uvcg_default_camera_bm_controls.attr,
+	NULL,
+};
+
+static struct config_item_type uvcg_default_camera_type = {
+	.ct_item_ops	= &uvcg_default_camera_item_ops,
+	.ct_attrs	= uvcg_default_camera_attrs,
+	.ct_owner	= THIS_MODULE,
+};
+
+/* struct uvcg_camera {}; */
+
+static struct config_group *uvcg_camera_default_groups[] = {
+	&uvcg_default_camera.group,
+	NULL,
+};
+
+/* control/terminal/camera */
+static struct uvcg_camera_grp {
+	struct config_group	group;
+} uvcg_camera_grp;
+
+static struct config_item_type uvcg_camera_grp_type = {
+	.ct_owner = THIS_MODULE,
+};
+
+/* control/terminal/output/default */
+static struct uvcg_default_output {
+	struct config_group	group;
+} uvcg_default_output;
+
+static inline struct uvcg_default_output
+*to_uvcg_default_output(struct config_item *item)
+{
+	return container_of(to_config_group(item),
+			    struct uvcg_default_output, group);
+}
+
+CONFIGFS_ATTR_STRUCT(uvcg_default_output);
+CONFIGFS_ATTR_OPS_RO(uvcg_default_output);
+
+static struct configfs_item_operations uvcg_default_output_item_ops = {
+	.show_attribute		= uvcg_default_output_attr_show,
+};
+
+#define UVCG_DEFAULT_OUTPUT_ATTR(cname, aname, conv)			\
+static ssize_t uvcg_default_output_##cname##_show(			\
+	struct uvcg_default_output *dout, char *page)			\
+{									\
+	struct f_uvc_opts *opts;					\
+	struct config_item *opts_item;					\
+	struct mutex *su_mutex = &dout->group.cg_subsys->su_mutex;	\
+	struct uvc_output_terminal_descriptor *cd;			\
+	int result;							\
+									\
+	mutex_lock(su_mutex); /* for navigating configfs hierarchy */	\
+									\
+	opts_item = dout->group.cg_item.ci_parent->ci_parent->		\
+			ci_parent->ci_parent;				\
+	opts = to_f_uvc_opts(opts_item);				\
+	cd = &opts->uvc_output_terminal;				\
+									\
+	mutex_lock(&opts->lock);					\
+	result = sprintf(page, "%d\n", conv(cd->aname));		\
+	mutex_unlock(&opts->lock);					\
+									\
+	mutex_unlock(su_mutex);						\
+									\
+	return result;							\
+}									\
+									\
+static struct uvcg_default_output_attribute				\
+	uvcg_default_output_##cname =					\
+	__CONFIGFS_ATTR_RO(aname, uvcg_default_output_##cname##_show)
+
+#define identity_conv(x) (x)
+
+UVCG_DEFAULT_OUTPUT_ATTR(b_terminal_id, bTerminalID, identity_conv);
+UVCG_DEFAULT_OUTPUT_ATTR(w_terminal_type, wTerminalType, le16_to_cpu);
+UVCG_DEFAULT_OUTPUT_ATTR(b_assoc_terminal, bAssocTerminal, identity_conv);
+UVCG_DEFAULT_OUTPUT_ATTR(b_source_id, bSourceID, identity_conv);
+UVCG_DEFAULT_OUTPUT_ATTR(i_terminal, iTerminal, identity_conv);
+
+#undef identity_conv
+
+#undef UVCG_DEFAULT_OUTPUT_ATTR
+
+static struct configfs_attribute *uvcg_default_output_attrs[] = {
+	&uvcg_default_output_b_terminal_id.attr,
+	&uvcg_default_output_w_terminal_type.attr,
+	&uvcg_default_output_b_assoc_terminal.attr,
+	&uvcg_default_output_b_source_id.attr,
+	&uvcg_default_output_i_terminal.attr,
+	NULL,
+};
+
+static struct config_item_type uvcg_default_output_type = {
+	.ct_item_ops	= &uvcg_default_output_item_ops,
+	.ct_attrs	= uvcg_default_output_attrs,
+	.ct_owner	= THIS_MODULE,
+};
+
+/* struct uvcg_output {}; */
+
+static struct config_group *uvcg_output_default_groups[] = {
+	&uvcg_default_output.group,
+	NULL,
+};
+
+/* control/terminal/output */
+static struct uvcg_output_grp {
+	struct config_group	group;
+} uvcg_output_grp;
+
+static struct config_item_type uvcg_output_grp_type = {
+	.ct_owner = THIS_MODULE,
+};
+
+static struct config_group *uvcg_terminal_default_groups[] = {
+	&uvcg_camera_grp.group,
+	&uvcg_output_grp.group,
+	NULL,
+};
+
+/* control/terminal */
+static struct uvcg_terminal_grp {
+	struct config_group	group;
+} uvcg_terminal_grp;
+
+static struct config_item_type uvcg_terminal_grp_type = {
+	.ct_owner = THIS_MODULE,
+};
+
+/* control/class/{fs} */
+static struct uvcg_control_class {
+	struct config_group	group;
+} uvcg_control_class_fs, uvcg_control_class_ss;
+
+
+static inline struct uvc_descriptor_header
+**uvcg_get_ctl_class_arr(struct config_item *i, struct f_uvc_opts *o)
+{
+	struct uvcg_control_class *cl = container_of(to_config_group(i),
+		struct uvcg_control_class, group);
+
+	if (cl == &uvcg_control_class_fs)
+		return o->uvc_fs_control_cls;
+
+	if (cl == &uvcg_control_class_ss)
+		return o->uvc_ss_control_cls;
+
+	return NULL;
+}
+
+static int uvcg_control_class_allow_link(struct config_item *src,
+					 struct config_item *target)
+{
+	struct config_item *control, *header;
+	struct f_uvc_opts *opts;
+	struct mutex *su_mutex = &src->ci_group->cg_subsys->su_mutex;
+	struct uvc_descriptor_header **class_array;
+	struct uvcg_control_header *target_hdr;
+	int ret = -EINVAL;
+
+	mutex_lock(su_mutex); /* for navigating configfs hierarchy */
+
+	control = src->ci_parent->ci_parent;
+	header = config_group_find_item(to_config_group(control), "header");
+	if (!header || target->ci_parent != header)
+		goto out;
+
+	opts = to_f_uvc_opts(control->ci_parent);
+
+	mutex_lock(&opts->lock);
+
+	class_array = uvcg_get_ctl_class_arr(src, opts);
+	if (!class_array)
+		goto unlock;
+	if (opts->refcnt || class_array[0]) {
+		ret = -EBUSY;
+		goto unlock;
+	}
+
+	target_hdr = to_uvcg_control_header(target);
+	++target_hdr->linked;
+	class_array[0] = (struct uvc_descriptor_header *)&target_hdr->desc;
+	ret = 0;
+
+unlock:
+	mutex_unlock(&opts->lock);
+out:
+	mutex_unlock(su_mutex);
+	return ret;
+}
+
+static int uvcg_control_class_drop_link(struct config_item *src,
+					struct config_item *target)
+{
+	struct config_item *control, *header;
+	struct f_uvc_opts *opts;
+	struct mutex *su_mutex = &src->ci_group->cg_subsys->su_mutex;
+	struct uvc_descriptor_header **class_array;
+	struct uvcg_control_header *target_hdr;
+	int ret = -EINVAL;
+
+	mutex_lock(su_mutex); /* for navigating configfs hierarchy */
+
+	control = src->ci_parent->ci_parent;
+	header = config_group_find_item(to_config_group(control), "header");
+	if (!header || target->ci_parent != header)
+		goto out;
+
+	opts = to_f_uvc_opts(control->ci_parent);
+
+	mutex_lock(&opts->lock);
+
+	class_array = uvcg_get_ctl_class_arr(src, opts);
+	if (!class_array)
+		goto unlock;
+	if (opts->refcnt) {
+		ret = -EBUSY;
+		goto unlock;
+	}
+
+	target_hdr = to_uvcg_control_header(target);
+	--target_hdr->linked;
+	class_array[0] = NULL;
+	ret = 0;
+
+unlock:
+	mutex_unlock(&opts->lock);
+out:
+	mutex_unlock(su_mutex);
+	return ret;
+}
+
+static struct configfs_item_operations uvcg_control_class_item_ops = {
+	.allow_link	= uvcg_control_class_allow_link,
+	.drop_link	= uvcg_control_class_drop_link,
+};
+
+static struct config_item_type uvcg_control_class_type = {
+	.ct_item_ops	= &uvcg_control_class_item_ops,
+	.ct_owner	= THIS_MODULE,
+};
+
+static struct config_group *uvcg_control_class_default_groups[] = {
+	&uvcg_control_class_fs.group,
+	&uvcg_control_class_ss.group,
+	NULL,
+};
+
+/* control/class */
+static struct uvcg_control_class_grp {
+	struct config_group	group;
+} uvcg_control_class_grp;
+
+static struct config_item_type uvcg_control_class_grp_type = {
+	.ct_owner = THIS_MODULE,
+};
+
+static struct config_group *uvcg_control_default_groups[] = {
+	&uvcg_control_header_grp.group,
+	&uvcg_processing_grp.group,
+	&uvcg_terminal_grp.group,
+	&uvcg_control_class_grp.group,
+	NULL,
+};
+
+/* control */
+static struct uvcg_control_grp {
+	struct config_group	group;
+} uvcg_control_grp;
+
+static struct config_item_type uvcg_control_grp_type = {
+	.ct_owner = THIS_MODULE,
+};
+
+/* streaming/uncompressed */
+static struct uvcg_uncompressed_grp {
+	struct config_group	group;
+} uvcg_uncompressed_grp;
+
+/* streaming/mjpeg */
+static struct uvcg_mjpeg_grp {
+	struct config_group	group;
+} uvcg_mjpeg_grp;
+
+static struct config_item *fmt_parent[] = {
+	&uvcg_uncompressed_grp.group.cg_item,
+	&uvcg_mjpeg_grp.group.cg_item,
+};
+
+enum uvcg_format_type {
+	UVCG_UNCOMPRESSED = 0,
+	UVCG_MJPEG,
+};
+
+struct uvcg_format {
+	struct config_group	group;
+	enum uvcg_format_type	type;
+	unsigned		linked;
+	unsigned		num_frames;
+	__u8			bmaControls[UVCG_STREAMING_CONTROL_SIZE];
+};
+
+static struct uvcg_format *to_uvcg_format(struct config_item *item)
+{
+	return container_of(to_config_group(item), struct uvcg_format, group);
+}
+
+static ssize_t uvcg_format_bma_controls_show(struct uvcg_format *f, char *page)
+{
+	struct f_uvc_opts *opts;
+	struct config_item *opts_item;
+	struct mutex *su_mutex = &f->group.cg_subsys->su_mutex;
+	int result, i;
+	char *pg = page;
+
+	mutex_lock(su_mutex); /* for navigating configfs hierarchy */
+
+	opts_item = f->group.cg_item.ci_parent->ci_parent->ci_parent;
+	opts = to_f_uvc_opts(opts_item);
+
+	mutex_lock(&opts->lock);
+	result = sprintf(pg, "0x");
+	pg += result;
+	for (i = 0; i < UVCG_STREAMING_CONTROL_SIZE; ++i) {
+		result += sprintf(pg, "%x\n", f->bmaControls[i]);
+		pg = page + result;
+	}
+	mutex_unlock(&opts->lock);
+
+	mutex_unlock(su_mutex);
+	return result;
+}
+
+static ssize_t uvcg_format_bma_controls_store(struct uvcg_format *ch,
+					      const char *page, size_t len)
+{
+	struct f_uvc_opts *opts;
+	struct config_item *opts_item;
+	struct mutex *su_mutex = &ch->group.cg_subsys->su_mutex;
+	int ret = -EINVAL;
+
+	mutex_lock(su_mutex); /* for navigating configfs hierarchy */
+
+	opts_item = ch->group.cg_item.ci_parent->ci_parent->ci_parent;
+	opts = to_f_uvc_opts(opts_item);
+
+	mutex_lock(&opts->lock);
+	if (ch->linked || opts->refcnt) {
+		ret = -EBUSY;
+		goto end;
+	}
+
+	if (len < 4 || *page != '0' ||
+	    (*(page + 1) != 'x' && *(page + 1) != 'X'))
+		goto end;
+	ret = hex2bin(ch->bmaControls, page + 2, 1);
+	if (ret < 0)
+		goto end;
+	ret = len;
+end:
+	mutex_unlock(&opts->lock);
+	mutex_unlock(su_mutex);
+	return ret;
+}
+
+struct uvcg_format_ptr {
+	struct uvcg_format	*fmt;
+	struct list_head	entry;
+};
+
+/* streaming/header/<NAME> */
+struct uvcg_streaming_header {
+	struct config_item				item;
+	struct uvc_input_header_descriptor		desc;
+	unsigned					linked;
+	struct list_head				formats;
+	unsigned					num_fmt;
+};
+
+static struct uvcg_streaming_header *to_uvcg_streaming_header(struct config_item *item)
+{
+	return container_of(item, struct uvcg_streaming_header, item);
+}
+
+CONFIGFS_ATTR_STRUCT(uvcg_streaming_header);
+CONFIGFS_ATTR_OPS(uvcg_streaming_header);
+
+static int uvcg_streaming_header_allow_link(struct config_item *src,
+					    struct config_item *target)
+{
+	struct mutex *su_mutex = &src->ci_group->cg_subsys->su_mutex;
+	struct config_item *opts_item;
+	struct f_uvc_opts *opts;
+	struct uvcg_streaming_header *src_hdr;
+	struct uvcg_format *target_fmt = NULL;
+	struct uvcg_format_ptr *format_ptr;
+	int i, ret = -EINVAL;
+
+	src_hdr = to_uvcg_streaming_header(src);
+	mutex_lock(su_mutex); /* for navigating configfs hierarchy */
+
+	opts_item = src->ci_parent->ci_parent->ci_parent;
+	opts = to_f_uvc_opts(opts_item);
+
+	mutex_lock(&opts->lock);
+
+	if (src_hdr->linked) {
+		ret = -EBUSY;
+		goto out;
+	}
+
+	for (i = 0; i < ARRAY_SIZE(fmt_parent); ++i)
+		if (target->ci_parent == fmt_parent[i])
+			break;
+	if (i == ARRAY_SIZE(fmt_parent))
+		goto out;
+
+	target_fmt = container_of(to_config_group(target), struct uvcg_format,
+				  group);
+	if (!target_fmt)
+		goto out;
+
+	format_ptr = kzalloc(sizeof(*format_ptr), GFP_KERNEL);
+	if (!format_ptr) {
+		ret = -ENOMEM;
+		goto out;
+	}
+	ret = 0;
+	format_ptr->fmt = target_fmt;
+	list_add_tail(&format_ptr->entry, &src_hdr->formats);
+	++src_hdr->num_fmt;
+
+out:
+	mutex_unlock(&opts->lock);
+	mutex_unlock(su_mutex);
+	return ret;
+}
+
+static int uvcg_streaming_header_drop_link(struct config_item *src,
+					   struct config_item *target)
+{
+	struct mutex *su_mutex = &src->ci_group->cg_subsys->su_mutex;
+	struct config_item *opts_item;
+	struct f_uvc_opts *opts;
+	struct uvcg_streaming_header *src_hdr;
+	struct uvcg_format *target_fmt = NULL;
+	struct uvcg_format_ptr *format_ptr, *tmp;
+	int ret = -EINVAL;
+
+	src_hdr = to_uvcg_streaming_header(src);
+	mutex_lock(su_mutex); /* for navigating configfs hierarchy */
+
+	opts_item = src->ci_parent->ci_parent->ci_parent;
+	opts = to_f_uvc_opts(opts_item);
+
+	mutex_lock(&opts->lock);
+	target_fmt = container_of(to_config_group(target), struct uvcg_format,
+				  group);
+	if (!target_fmt)
+		goto out;
+
+	list_for_each_entry_safe(format_ptr, tmp, &src_hdr->formats, entry)
+		if (format_ptr->fmt == target_fmt) {
+			list_del(&format_ptr->entry);
+			kfree(format_ptr);
+			--src_hdr->num_fmt;
+			break;
+		}
+
+out:
+	mutex_unlock(&opts->lock);
+	mutex_unlock(su_mutex);
+	return ret;
+
+}
+
+static struct configfs_item_operations uvcg_streaming_header_item_ops = {
+	.show_attribute		= uvcg_streaming_header_attr_show,
+	.store_attribute	= uvcg_streaming_header_attr_store,
+	.allow_link		= uvcg_streaming_header_allow_link,
+	.drop_link		= uvcg_streaming_header_drop_link,
+};
+
+#define UVCG_STREAMING_HEADER_ATTR(cname, aname, conv)			\
+static ssize_t uvcg_streaming_header_##cname##_show(			\
+	struct uvcg_streaming_header *sh, char *page)			\
+{									\
+	struct f_uvc_opts *opts;					\
+	struct config_item *opts_item;					\
+	struct mutex *su_mutex = &sh->item.ci_group->cg_subsys->su_mutex;\
+	int result;							\
+									\
+	mutex_lock(su_mutex); /* for navigating configfs hierarchy */	\
+									\
+	opts_item = sh->item.ci_parent->ci_parent->ci_parent;		\
+	opts = to_f_uvc_opts(opts_item);				\
+									\
+	mutex_lock(&opts->lock);					\
+	result = sprintf(page, "%d\n", conv(sh->desc.aname));		\
+	mutex_unlock(&opts->lock);					\
+									\
+	mutex_unlock(su_mutex);						\
+	return result;							\
+}									\
+									\
+static struct uvcg_streaming_header_attribute				\
+	uvcg_streaming_header_##cname =					\
+	__CONFIGFS_ATTR_RO(aname, uvcg_streaming_header_##cname##_show)
+
+#define identity_conv(x) (x)
+
+UVCG_STREAMING_HEADER_ATTR(bm_info, bmInfo, identity_conv);
+UVCG_STREAMING_HEADER_ATTR(b_terminal_link, bTerminalLink, identity_conv);
+UVCG_STREAMING_HEADER_ATTR(b_still_capture_method, bStillCaptureMethod,
+			   identity_conv);
+UVCG_STREAMING_HEADER_ATTR(b_trigger_support, bTriggerSupport, identity_conv);
+UVCG_STREAMING_HEADER_ATTR(b_trigger_usage, bTriggerUsage, identity_conv);
+
+#undef identity_conv
+
+#undef UVCG_STREAMING_HEADER_ATTR
+
+static struct configfs_attribute *uvcg_streaming_header_attrs[] = {
+	&uvcg_streaming_header_bm_info.attr,
+	&uvcg_streaming_header_b_terminal_link.attr,
+	&uvcg_streaming_header_b_still_capture_method.attr,
+	&uvcg_streaming_header_b_trigger_support.attr,
+	&uvcg_streaming_header_b_trigger_usage.attr,
+	NULL,
+};
+
+static struct config_item_type uvcg_streaming_header_type = {
+	.ct_item_ops	= &uvcg_streaming_header_item_ops,
+	.ct_attrs	= uvcg_streaming_header_attrs,
+	.ct_owner	= THIS_MODULE,
+};
+
+static struct config_item
+*uvcg_streaming_header_make(struct config_group *group, const char *name)
+{
+	struct uvcg_streaming_header *h;
+
+	h = kzalloc(sizeof(*h), GFP_KERNEL);
+	if (!h)
+		return ERR_PTR(-ENOMEM);
+
+	INIT_LIST_HEAD(&h->formats);
+	h->desc.bDescriptorType		= USB_DT_CS_INTERFACE;
+	h->desc.bDescriptorSubType	= UVC_VS_INPUT_HEADER;
+	h->desc.bTerminalLink		= 3;
+	h->desc.bControlSize		= UVCG_STREAMING_CONTROL_SIZE;
+
+	config_item_init_type_name(&h->item, name, &uvcg_streaming_header_type);
+
+	return &h->item;
+}
+
+static void uvcg_streaming_header_drop(struct config_group *group,
+			      struct config_item *item)
+{
+	struct uvcg_streaming_header *h = to_uvcg_streaming_header(item);
+
+	kfree(h);
+}
+
+/* streaming/header */
+static struct uvcg_streaming_header_grp {
+	struct config_group	group;
+} uvcg_streaming_header_grp;
+
+static struct configfs_group_operations uvcg_streaming_header_grp_ops = {
+	.make_item		= uvcg_streaming_header_make,
+	.drop_item		= uvcg_streaming_header_drop,
+};
+
+static struct config_item_type uvcg_streaming_header_grp_type = {
+	.ct_group_ops	= &uvcg_streaming_header_grp_ops,
+	.ct_owner	= THIS_MODULE,
+};
+
+/* streaming/<mode>/<format>/<NAME> */
+struct uvcg_frame {
+	struct {
+		u8	b_length;
+		u8	b_descriptor_type;
+		u8	b_descriptor_subtype;
+		u8	b_frame_index;
+		u8	bm_capabilities;
+		u16	w_width;
+		u16	w_height;
+		u32	dw_min_bit_rate;
+		u32	dw_max_bit_rate;
+		u32	dw_max_video_frame_buffer_size;
+		u32	dw_default_frame_interval;
+		u8	b_frame_interval_type;
+	} __attribute__((packed)) frame;
+	u32 *dw_frame_interval;
+	enum uvcg_format_type	fmt_type;
+	struct config_item	item;
+};
+
+static struct uvcg_frame *to_uvcg_frame(struct config_item *item)
+{
+	return container_of(item, struct uvcg_frame, item);
+}
+
+CONFIGFS_ATTR_STRUCT(uvcg_frame);
+CONFIGFS_ATTR_OPS(uvcg_frame);
+
+static struct configfs_item_operations uvcg_frame_item_ops = {
+	.show_attribute		= uvcg_frame_attr_show,
+	.store_attribute	= uvcg_frame_attr_store,
+};
+
+#define UVCG_FRAME_ATTR(cname, aname, to_cpu_endian, to_little_endian, bits) \
+static ssize_t uvcg_frame_##cname##_show(struct uvcg_frame *f, char *page)\
+{									\
+	struct f_uvc_opts *opts;					\
+	struct config_item *opts_item;					\
+	struct mutex *su_mutex = &f->item.ci_group->cg_subsys->su_mutex;\
+	int result;							\
+									\
+	mutex_lock(su_mutex); /* for navigating configfs hierarchy */	\
+									\
+	opts_item = f->item.ci_parent->ci_parent->ci_parent->ci_parent;	\
+	opts = to_f_uvc_opts(opts_item);				\
+									\
+	mutex_lock(&opts->lock);					\
+	result = sprintf(page, "%d\n", to_cpu_endian(f->frame.cname));	\
+	mutex_unlock(&opts->lock);					\
+									\
+	mutex_unlock(su_mutex);						\
+	return result;							\
+}									\
+									\
+static ssize_t  uvcg_frame_##cname##_store(struct uvcg_frame *f,	\
+					   const char *page, size_t len)\
+{									\
+	struct f_uvc_opts *opts;					\
+	struct config_item *opts_item;					\
+	struct uvcg_format *fmt;					\
+	struct mutex *su_mutex = &f->item.ci_group->cg_subsys->su_mutex;\
+	int ret;							\
+	u##bits num;							\
+									\
+	ret = kstrtou##bits(page, 0, &num);				\
+	if (ret)							\
+		return ret;						\
+									\
+	mutex_lock(su_mutex); /* for navigating configfs hierarchy */	\
+									\
+	opts_item = f->item.ci_parent->ci_parent->ci_parent->ci_parent;	\
+	opts = to_f_uvc_opts(opts_item);				\
+	fmt = to_uvcg_format(f->item.ci_parent);			\
+									\
+	mutex_lock(&opts->lock);					\
+	if (fmt->linked || opts->refcnt) {				\
+		ret = -EBUSY;						\
+		goto end;						\
+	}								\
+									\
+	f->frame.cname = to_little_endian(num);				\
+	ret = len;							\
+end:									\
+	mutex_unlock(&opts->lock);					\
+	mutex_unlock(su_mutex);						\
+	return ret;							\
+}									\
+									\
+static struct uvcg_frame_attribute					\
+	uvcg_frame_##cname =						\
+	__CONFIGFS_ATTR(aname, S_IRUGO | S_IWUSR,			\
+			uvcg_frame_##cname##_show,			\
+			uvcg_frame_##cname##_store)
+
+#define noop_conversion(x) (x)
+
+UVCG_FRAME_ATTR(bm_capabilities, bmCapabilities, noop_conversion,
+		noop_conversion, 8);
+UVCG_FRAME_ATTR(w_width, wWidth, le16_to_cpu, cpu_to_le16, 16);
+UVCG_FRAME_ATTR(w_height, wHeight, le16_to_cpu, cpu_to_le16, 16);
+UVCG_FRAME_ATTR(dw_min_bit_rate, dwMinBitRate, le32_to_cpu, cpu_to_le32, 32);
+UVCG_FRAME_ATTR(dw_max_bit_rate, dwMaxBitRate, le32_to_cpu, cpu_to_le32, 32);
+UVCG_FRAME_ATTR(dw_max_video_frame_buffer_size, dwMaxVideoFrameBufferSize,
+		le32_to_cpu, cpu_to_le32, 32);
+UVCG_FRAME_ATTR(dw_default_frame_interval, dwDefaultFrameInterval,
+		le32_to_cpu, cpu_to_le32, 32);
+
+#undef noop_conversion
+
+#undef UVCG_FRAME_ATTR
+
+static ssize_t uvcg_frame_dw_frame_interval_show(struct uvcg_frame *frm,
+						 char *page)
+{
+	struct f_uvc_opts *opts;
+	struct config_item *opts_item;
+	struct mutex *su_mutex = &frm->item.ci_group->cg_subsys->su_mutex;
+	int result, i;
+	char *pg = page;
+
+	mutex_lock(su_mutex); /* for navigating configfs hierarchy */
+
+	opts_item = frm->item.ci_parent->ci_parent->ci_parent->ci_parent;
+	opts = to_f_uvc_opts(opts_item);
+
+	mutex_lock(&opts->lock);
+	for (result = 0, i = 0; i < frm->frame.b_frame_interval_type; ++i) {
+		result += sprintf(pg, "%d\n",
+				  le32_to_cpu(frm->dw_frame_interval[i]));
+		pg = page + result;
+	}
+	mutex_unlock(&opts->lock);
+
+	mutex_unlock(su_mutex);
+	return result;
+}
+
+static inline int __uvcg_count_frm_intrv(char *buf, void *priv)
+{
+	++*((int *)priv);
+	return 0;
+}
+
+static inline int __uvcg_fill_frm_intrv(char *buf, void *priv)
+{
+	u32 num, **interv;
+	int ret;
+
+	ret = kstrtou32(buf, 0, &num);
+	if (ret)
+		return ret;
+
+	interv = priv;
+	**interv = cpu_to_le32(num);
+	++*interv;
+
+	return 0;
+}
+
+static int __uvcg_iter_frm_intrv(const char *page, size_t len,
+				 int (*fun)(char *, void *), void *priv)
+{
+	/* sign, base 2 representation, newline, terminator */
+	char buf[1 + sizeof(u32) * 8 + 1 + 1];
+	const char *pg = page;
+	int i, ret;
+
+	if (!fun)
+		return -EINVAL;
+
+	while (pg - page < len) {
+		i = 0;
+		while (i < sizeof(buf) && (pg - page < len) &&
+				*pg != '\0' && *pg != '\n')
+			buf[i++] = *pg++;
+		if (i == sizeof(buf))
+			return -EINVAL;
+		while ((pg - page < len) && (*pg == '\0' || *pg == '\n'))
+			++pg;
+		buf[i] = '\0';
+		ret = fun(buf, priv);
+		if (ret)
+			return ret;
+	}
+
+	return 0;
+}
+
+static ssize_t uvcg_frame_dw_frame_interval_store(struct uvcg_frame *ch,
+						  const char *page, size_t len)
+{
+	struct f_uvc_opts *opts;
+	struct config_item *opts_item;
+	struct uvcg_format *fmt;
+	struct mutex *su_mutex = &ch->item.ci_group->cg_subsys->su_mutex;
+	int ret = 0, n = 0;
+	u32 *frm_intrv, *tmp;
+
+	mutex_lock(su_mutex); /* for navigating configfs hierarchy */
+
+	opts_item = ch->item.ci_parent->ci_parent->ci_parent->ci_parent;
+	opts = to_f_uvc_opts(opts_item);
+	fmt = to_uvcg_format(ch->item.ci_parent);
+
+	mutex_lock(&opts->lock);
+	if (fmt->linked || opts->refcnt) {
+		ret = -EBUSY;
+		goto end;
+	}
+
+	ret = __uvcg_iter_frm_intrv(page, len, __uvcg_count_frm_intrv, &n);
+	if (ret)
+		goto end;
+
+	tmp = frm_intrv = kcalloc(n, sizeof(u32), GFP_KERNEL);
+	if (!frm_intrv) {
+		ret = -ENOMEM;
+		goto end;
+	}
+
+	ret = __uvcg_iter_frm_intrv(page, len, __uvcg_fill_frm_intrv, &tmp);
+	if (ret) {
+		kfree(frm_intrv);
+		goto end;
+	}
+
+	kfree(ch->dw_frame_interval);
+	ch->dw_frame_interval = frm_intrv;
+	ch->frame.b_frame_interval_type = n;
+	ret = len;
+
+end:
+	mutex_unlock(&opts->lock);
+	mutex_unlock(su_mutex);
+	return ret;
+}
+
+static struct uvcg_frame_attribute
+	uvcg_frame_dw_frame_interval =
+	__CONFIGFS_ATTR(dwFrameInterval, S_IRUGO | S_IWUSR,
+			uvcg_frame_dw_frame_interval_show,
+			uvcg_frame_dw_frame_interval_store);
+
+static struct configfs_attribute *uvcg_frame_attrs[] = {
+	&uvcg_frame_bm_capabilities.attr,
+	&uvcg_frame_w_width.attr,
+	&uvcg_frame_w_height.attr,
+	&uvcg_frame_dw_min_bit_rate.attr,
+	&uvcg_frame_dw_max_bit_rate.attr,
+	&uvcg_frame_dw_max_video_frame_buffer_size.attr,
+	&uvcg_frame_dw_default_frame_interval.attr,
+	&uvcg_frame_dw_frame_interval.attr,
+	NULL,
+};
+
+static struct config_item_type uvcg_frame_type = {
+	.ct_item_ops	= &uvcg_frame_item_ops,
+	.ct_attrs	= uvcg_frame_attrs,
+	.ct_owner	= THIS_MODULE,
+};
+
+static struct config_item *uvcg_frame_make(struct config_group *group,
+					   const char *name)
+{
+	struct uvcg_frame *h;
+	struct uvcg_format *fmt;
+	struct f_uvc_opts *opts;
+	struct config_item *opts_item;
+
+	h = kzalloc(sizeof(*h), GFP_KERNEL);
+	if (!h)
+		return ERR_PTR(-ENOMEM);
+
+	h->frame.b_descriptor_type		= USB_DT_CS_INTERFACE;
+	h->frame.b_frame_index			= 1;
+	h->frame.w_width			= cpu_to_le16(640);
+	h->frame.w_height			= cpu_to_le16(360);
+	h->frame.dw_min_bit_rate		= cpu_to_le32(18432000);
+	h->frame.dw_max_bit_rate		= cpu_to_le32(55296000);
+	h->frame.dw_max_video_frame_buffer_size	= cpu_to_le32(460800);
+	h->frame.dw_default_frame_interval	= cpu_to_le32(666666);
+
+	opts_item = group->cg_item.ci_parent->ci_parent->ci_parent;
+	opts = to_f_uvc_opts(opts_item);
+
+	mutex_lock(&opts->lock);
+	fmt = to_uvcg_format(&group->cg_item);
+	if (fmt->type == UVCG_UNCOMPRESSED) {
+		h->frame.b_descriptor_subtype = UVC_VS_FRAME_UNCOMPRESSED;
+		h->fmt_type = UVCG_UNCOMPRESSED;
+	} else if (fmt->type == UVCG_MJPEG) {
+		h->frame.b_descriptor_subtype = UVC_VS_FRAME_MJPEG;
+		h->fmt_type = UVCG_MJPEG;
+	} else {
+		mutex_unlock(&opts->lock);
+		kfree(h);
+		return ERR_PTR(-EINVAL);
+	}
+	++fmt->num_frames;
+	mutex_unlock(&opts->lock);
+
+	config_item_init_type_name(&h->item, name, &uvcg_frame_type);
+
+	return &h->item;
+}
+
+static void uvcg_frame_drop(struct config_group *group, struct config_item *item)
+{
+	struct uvcg_frame *h = to_uvcg_frame(item);
+	struct uvcg_format *fmt;
+	struct f_uvc_opts *opts;
+	struct config_item *opts_item;
+
+	opts_item = group->cg_item.ci_parent->ci_parent->ci_parent;
+	opts = to_f_uvc_opts(opts_item);
+
+	mutex_lock(&opts->lock);
+	fmt = to_uvcg_format(&group->cg_item);
+	--fmt->num_frames;
+	kfree(h);
+	mutex_unlock(&opts->lock);
+}
+
+/* streaming/uncompressed/<NAME> */
+struct uvcg_uncompressed {
+	struct uvcg_format		fmt;
+	struct uvc_format_uncompressed	desc;
+};
+
+static struct uvcg_uncompressed *to_uvcg_uncompressed(struct config_item *item)
+{
+	return container_of(
+		container_of(to_config_group(item), struct uvcg_format, group),
+		struct uvcg_uncompressed, fmt);
+}
+
+CONFIGFS_ATTR_STRUCT(uvcg_uncompressed);
+CONFIGFS_ATTR_OPS(uvcg_uncompressed);
+
+static struct configfs_item_operations uvcg_uncompressed_item_ops = {
+	.show_attribute		= uvcg_uncompressed_attr_show,
+	.store_attribute	= uvcg_uncompressed_attr_store,
+};
+
+static struct configfs_group_operations uvcg_uncompressed_group_ops = {
+	.make_item		= uvcg_frame_make,
+	.drop_item		= uvcg_frame_drop,
+};
+
+static ssize_t uvcg_uncompressed_guid_format_show(struct uvcg_uncompressed *ch,
+							char *page)
+{
+	struct f_uvc_opts *opts;
+	struct config_item *opts_item;
+	struct mutex *su_mutex = &ch->fmt.group.cg_subsys->su_mutex;
+
+	mutex_lock(su_mutex); /* for navigating configfs hierarchy */
+
+	opts_item = ch->fmt.group.cg_item.ci_parent->ci_parent->ci_parent;
+	opts = to_f_uvc_opts(opts_item);
+
+	mutex_lock(&opts->lock);
+	memcpy(page, ch->desc.guidFormat, sizeof(ch->desc.guidFormat));
+	mutex_unlock(&opts->lock);
+
+	mutex_unlock(su_mutex);
+
+	return sizeof(ch->desc.guidFormat);
+}
+
+static ssize_t uvcg_uncompressed_guid_format_store(struct uvcg_uncompressed *ch,
+						   const char *page, size_t len)
+{
+	struct f_uvc_opts *opts;
+	struct config_item *opts_item;
+	struct mutex *su_mutex = &ch->fmt.group.cg_subsys->su_mutex;
+	int ret;
+
+	mutex_lock(su_mutex); /* for navigating configfs hierarchy */
+
+	opts_item = ch->fmt.group.cg_item.ci_parent->ci_parent->ci_parent;
+	opts = to_f_uvc_opts(opts_item);
+
+	mutex_lock(&opts->lock);
+	if (ch->fmt.linked || opts->refcnt) {
+		ret = -EBUSY;
+		goto end;
+	}
+
+	memcpy(ch->desc.guidFormat, page,
+	       min(sizeof(ch->desc.guidFormat), len));
+	ret = sizeof(ch->desc.guidFormat);
+
+end:
+	mutex_unlock(&opts->lock);
+	mutex_unlock(su_mutex);
+	return ret;
+}
+
+static struct uvcg_uncompressed_attribute uvcg_uncompressed_guid_format =
+	__CONFIGFS_ATTR(guidFormat, S_IRUGO | S_IWUSR,
+			uvcg_uncompressed_guid_format_show,
+			uvcg_uncompressed_guid_format_store);
+
+
+#define UVCG_UNCOMPRESSED_ATTR_RO(cname, aname, conv)			\
+static ssize_t uvcg_uncompressed_##cname##_show(			\
+	struct uvcg_uncompressed *u, char *page)			\
+{									\
+	struct f_uvc_opts *opts;					\
+	struct config_item *opts_item;					\
+	struct mutex *su_mutex = &u->fmt.group.cg_subsys->su_mutex;	\
+	int result;							\
+									\
+	mutex_lock(su_mutex); /* for navigating configfs hierarchy */	\
+									\
+	opts_item = u->fmt.group.cg_item.ci_parent->ci_parent->ci_parent;\
+	opts = to_f_uvc_opts(opts_item);				\
+									\
+	mutex_lock(&opts->lock);					\
+	result = sprintf(page, "%d\n", conv(u->desc.aname));		\
+	mutex_unlock(&opts->lock);					\
+									\
+	mutex_unlock(su_mutex);						\
+	return result;							\
+}									\
+									\
+static struct uvcg_uncompressed_attribute				\
+	uvcg_uncompressed_##cname =					\
+	__CONFIGFS_ATTR_RO(aname, uvcg_uncompressed_##cname##_show)
+
+#define UVCG_UNCOMPRESSED_ATTR(cname, aname, conv)			\
+static ssize_t uvcg_uncompressed_##cname##_show(			\
+	struct uvcg_uncompressed *u, char *page)			\
+{									\
+	struct f_uvc_opts *opts;					\
+	struct config_item *opts_item;					\
+	struct mutex *su_mutex = &u->fmt.group.cg_subsys->su_mutex;	\
+	int result;							\
+									\
+	mutex_lock(su_mutex); /* for navigating configfs hierarchy */	\
+									\
+	opts_item = u->fmt.group.cg_item.ci_parent->ci_parent->ci_parent;\
+	opts = to_f_uvc_opts(opts_item);				\
+									\
+	mutex_lock(&opts->lock);					\
+	result = sprintf(page, "%d\n", conv(u->desc.aname));		\
+	mutex_unlock(&opts->lock);					\
+									\
+	mutex_unlock(su_mutex);						\
+	return result;							\
+}									\
+									\
+static ssize_t								\
+uvcg_uncompressed_##cname##_store(struct uvcg_uncompressed *u,		\
+				    const char *page, size_t len)	\
+{									\
+	struct f_uvc_opts *opts;					\
+	struct config_item *opts_item;					\
+	struct mutex *su_mutex = &u->fmt.group.cg_subsys->su_mutex;	\
+	int ret;							\
+	u8 num;								\
+									\
+	mutex_lock(su_mutex); /* for navigating configfs hierarchy */	\
+									\
+	opts_item = u->fmt.group.cg_item.ci_parent->ci_parent->ci_parent;\
+	opts = to_f_uvc_opts(opts_item);				\
+									\
+	mutex_lock(&opts->lock);					\
+	if (u->fmt.linked || opts->refcnt) {				\
+		ret = -EBUSY;						\
+		goto end;						\
+	}								\
+									\
+	ret = kstrtou8(page, 0, &num);					\
+	if (ret)							\
+		goto end;						\
+									\
+	if (num > 255) {						\
+		ret = -EINVAL;						\
+		goto end;						\
+	}								\
+	u->desc.aname = num;						\
+	ret = len;							\
+end:									\
+	mutex_unlock(&opts->lock);					\
+	mutex_unlock(su_mutex);						\
+	return ret;							\
+}									\
+									\
+static struct uvcg_uncompressed_attribute				\
+	uvcg_uncompressed_##cname =					\
+	__CONFIGFS_ATTR(aname, S_IRUGO | S_IWUSR,			\
+			uvcg_uncompressed_##cname##_show,		\
+			uvcg_uncompressed_##cname##_store)
+
+#define identity_conv(x) (x)
+
+UVCG_UNCOMPRESSED_ATTR(b_bits_per_pixel, bBitsPerPixel, identity_conv);
+UVCG_UNCOMPRESSED_ATTR(b_default_frame_index, bDefaultFrameIndex,
+		       identity_conv);
+UVCG_UNCOMPRESSED_ATTR_RO(b_aspect_ratio_x, bAspectRatioX, identity_conv);
+UVCG_UNCOMPRESSED_ATTR_RO(b_aspect_ratio_y, bAspectRatioY, identity_conv);
+UVCG_UNCOMPRESSED_ATTR_RO(bm_interface_flags, bmInterfaceFlags, identity_conv);
+
+#undef identity_conv
+
+#undef UVCG_UNCOMPRESSED_ATTR
+#undef UVCG_UNCOMPRESSED_ATTR_RO
+
+static inline ssize_t
+uvcg_uncompressed_bma_controls_show(struct uvcg_uncompressed *unc, char *page)
+{
+	return uvcg_format_bma_controls_show(&unc->fmt, page);
+}
+
+static inline ssize_t
+uvcg_uncompressed_bma_controls_store(struct uvcg_uncompressed *ch,
+				     const char *page, size_t len)
+{
+	return uvcg_format_bma_controls_store(&ch->fmt, page, len);
+}
+
+static struct uvcg_uncompressed_attribute uvcg_uncompressed_bma_controls =
+	__CONFIGFS_ATTR(bmaControls, S_IRUGO | S_IWUSR,
+			uvcg_uncompressed_bma_controls_show,
+			uvcg_uncompressed_bma_controls_store);
+
+static struct configfs_attribute *uvcg_uncompressed_attrs[] = {
+	&uvcg_uncompressed_guid_format.attr,
+	&uvcg_uncompressed_b_bits_per_pixel.attr,
+	&uvcg_uncompressed_b_default_frame_index.attr,
+	&uvcg_uncompressed_b_aspect_ratio_x.attr,
+	&uvcg_uncompressed_b_aspect_ratio_y.attr,
+	&uvcg_uncompressed_bm_interface_flags.attr,
+	&uvcg_uncompressed_bma_controls.attr,
+	NULL,
+};
+
+static struct config_item_type uvcg_uncompressed_type = {
+	.ct_item_ops	= &uvcg_uncompressed_item_ops,
+	.ct_group_ops	= &uvcg_uncompressed_group_ops,
+	.ct_attrs	= uvcg_uncompressed_attrs,
+	.ct_owner	= THIS_MODULE,
+};
+
+static struct config_group *uvcg_uncompressed_make(struct config_group *group,
+						   const char *name)
+{
+	static char guid[] = {
+		'Y',  'U',  'Y',  '2', 0x00, 0x00, 0x10, 0x00,
+		 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71
+	};
+	struct uvcg_uncompressed *h;
+
+	h = kzalloc(sizeof(*h), GFP_KERNEL);
+	if (!h)
+		return ERR_PTR(-ENOMEM);
+
+	h->desc.bLength			= UVC_DT_FORMAT_UNCOMPRESSED_SIZE;
+	h->desc.bDescriptorType		= USB_DT_CS_INTERFACE;
+	h->desc.bDescriptorSubType	= UVC_VS_FORMAT_UNCOMPRESSED;
+	memcpy(h->desc.guidFormat, guid, sizeof(guid));
+	h->desc.bBitsPerPixel		= 16;
+	h->desc.bDefaultFrameIndex	= 1;
+	h->desc.bAspectRatioX		= 0;
+	h->desc.bAspectRatioY		= 0;
+	h->desc.bmInterfaceFlags	= 0;
+	h->desc.bCopyProtect		= 0;
+
+	h->fmt.type = UVCG_UNCOMPRESSED;
+	config_group_init_type_name(&h->fmt.group, name,
+				    &uvcg_uncompressed_type);
+
+	return &h->fmt.group;
+}
+
+static void uvcg_uncompressed_drop(struct config_group *group,
+			    struct config_item *item)
+{
+	struct uvcg_uncompressed *h = to_uvcg_uncompressed(item);
+
+	kfree(h);
+}
+
+static struct configfs_group_operations uvcg_uncompressed_grp_ops = {
+	.make_group		= uvcg_uncompressed_make,
+	.drop_item		= uvcg_uncompressed_drop,
+};
+
+static struct config_item_type uvcg_uncompressed_grp_type = {
+	.ct_group_ops	= &uvcg_uncompressed_grp_ops,
+	.ct_owner	= THIS_MODULE,
+};
+
+/* streaming/mjpeg/<NAME> */
+struct uvcg_mjpeg {
+	struct uvcg_format		fmt;
+	struct uvc_format_mjpeg		desc;
+};
+
+static struct uvcg_mjpeg *to_uvcg_mjpeg(struct config_item *item)
+{
+	return container_of(
+		container_of(to_config_group(item), struct uvcg_format, group),
+		struct uvcg_mjpeg, fmt);
+}
+
+CONFIGFS_ATTR_STRUCT(uvcg_mjpeg);
+CONFIGFS_ATTR_OPS(uvcg_mjpeg);
+
+static struct configfs_item_operations uvcg_mjpeg_item_ops = {
+	.show_attribute		= uvcg_mjpeg_attr_show,
+	.store_attribute	= uvcg_mjpeg_attr_store,
+};
+
+static struct configfs_group_operations uvcg_mjpeg_group_ops = {
+	.make_item		= uvcg_frame_make,
+	.drop_item		= uvcg_frame_drop,
+};
+
+#define UVCG_MJPEG_ATTR_RO(cname, aname, conv)				\
+static ssize_t uvcg_mjpeg_##cname##_show(struct uvcg_mjpeg *u, char *page)\
+{									\
+	struct f_uvc_opts *opts;					\
+	struct config_item *opts_item;					\
+	struct mutex *su_mutex = &u->fmt.group.cg_subsys->su_mutex;	\
+	int result;							\
+									\
+	mutex_lock(su_mutex); /* for navigating configfs hierarchy */	\
+									\
+	opts_item = u->fmt.group.cg_item.ci_parent->ci_parent->ci_parent;\
+	opts = to_f_uvc_opts(opts_item);				\
+									\
+	mutex_lock(&opts->lock);					\
+	result = sprintf(page, "%d\n", conv(u->desc.aname));		\
+	mutex_unlock(&opts->lock);					\
+									\
+	mutex_unlock(su_mutex);						\
+	return result;							\
+}									\
+									\
+static struct uvcg_mjpeg_attribute					\
+	uvcg_mjpeg_##cname =						\
+	__CONFIGFS_ATTR_RO(aname, uvcg_mjpeg_##cname##_show)
+
+#define UVCG_MJPEG_ATTR(cname, aname, conv)				\
+static ssize_t uvcg_mjpeg_##cname##_show(struct uvcg_mjpeg *u, char *page)\
+{									\
+	struct f_uvc_opts *opts;					\
+	struct config_item *opts_item;					\
+	struct mutex *su_mutex = &u->fmt.group.cg_subsys->su_mutex;	\
+	int result;							\
+									\
+	mutex_lock(su_mutex); /* for navigating configfs hierarchy */	\
+									\
+	opts_item = u->fmt.group.cg_item.ci_parent->ci_parent->ci_parent;\
+	opts = to_f_uvc_opts(opts_item);				\
+									\
+	mutex_lock(&opts->lock);					\
+	result = sprintf(page, "%d\n", conv(u->desc.aname));		\
+	mutex_unlock(&opts->lock);					\
+									\
+	mutex_unlock(su_mutex);						\
+	return result;							\
+}									\
+									\
+static ssize_t								\
+uvcg_mjpeg_##cname##_store(struct uvcg_mjpeg *u,			\
+			   const char *page, size_t len)		\
+{									\
+	struct f_uvc_opts *opts;					\
+	struct config_item *opts_item;					\
+	struct mutex *su_mutex = &u->fmt.group.cg_subsys->su_mutex;	\
+	int ret;							\
+	u8 num;								\
+									\
+	mutex_lock(su_mutex); /* for navigating configfs hierarchy */	\
+									\
+	opts_item = u->fmt.group.cg_item.ci_parent->ci_parent->ci_parent;\
+	opts = to_f_uvc_opts(opts_item);				\
+									\
+	mutex_lock(&opts->lock);					\
+	if (u->fmt.linked || opts->refcnt) {				\
+		ret = -EBUSY;						\
+		goto end;						\
+	}								\
+									\
+	ret = kstrtou8(page, 0, &num);					\
+	if (ret)							\
+		goto end;						\
+									\
+	if (num > 255) {						\
+		ret = -EINVAL;						\
+		goto end;						\
+	}								\
+	u->desc.aname = num;						\
+	ret = len;							\
+end:									\
+	mutex_unlock(&opts->lock);					\
+	mutex_unlock(su_mutex);						\
+	return ret;							\
+}									\
+									\
+static struct uvcg_mjpeg_attribute					\
+	uvcg_mjpeg_##cname =						\
+	__CONFIGFS_ATTR(aname, S_IRUGO | S_IWUSR,			\
+			uvcg_mjpeg_##cname##_show,			\
+			uvcg_mjpeg_##cname##_store)
+
+#define identity_conv(x) (x)
+
+UVCG_MJPEG_ATTR(b_default_frame_index, bDefaultFrameIndex,
+		       identity_conv);
+UVCG_MJPEG_ATTR_RO(bm_flags, bmFlags, identity_conv);
+UVCG_MJPEG_ATTR_RO(b_aspect_ratio_x, bAspectRatioX, identity_conv);
+UVCG_MJPEG_ATTR_RO(b_aspect_ratio_y, bAspectRatioY, identity_conv);
+UVCG_MJPEG_ATTR_RO(bm_interface_flags, bmInterfaceFlags, identity_conv);
+
+#undef identity_conv
+
+#undef UVCG_MJPEG_ATTR
+#undef UVCG_MJPEG_ATTR_RO
+
+static inline ssize_t
+uvcg_mjpeg_bma_controls_show(struct uvcg_mjpeg *unc, char *page)
+{
+	return uvcg_format_bma_controls_show(&unc->fmt, page);
+}
+
+static inline ssize_t
+uvcg_mjpeg_bma_controls_store(struct uvcg_mjpeg *ch,
+				     const char *page, size_t len)
+{
+	return uvcg_format_bma_controls_store(&ch->fmt, page, len);
+}
+
+static struct uvcg_mjpeg_attribute uvcg_mjpeg_bma_controls =
+	__CONFIGFS_ATTR(bmaControls, S_IRUGO | S_IWUSR,
+			uvcg_mjpeg_bma_controls_show,
+			uvcg_mjpeg_bma_controls_store);
+
+static struct configfs_attribute *uvcg_mjpeg_attrs[] = {
+	&uvcg_mjpeg_b_default_frame_index.attr,
+	&uvcg_mjpeg_bm_flags.attr,
+	&uvcg_mjpeg_b_aspect_ratio_x.attr,
+	&uvcg_mjpeg_b_aspect_ratio_y.attr,
+	&uvcg_mjpeg_bm_interface_flags.attr,
+	&uvcg_mjpeg_bma_controls.attr,
+	NULL,
+};
+
+static struct config_item_type uvcg_mjpeg_type = {
+	.ct_item_ops	= &uvcg_mjpeg_item_ops,
+	.ct_group_ops	= &uvcg_mjpeg_group_ops,
+	.ct_attrs	= uvcg_mjpeg_attrs,
+	.ct_owner	= THIS_MODULE,
+};
+
+static struct config_group *uvcg_mjpeg_make(struct config_group *group,
+						   const char *name)
+{
+	struct uvcg_mjpeg *h;
+
+	h = kzalloc(sizeof(*h), GFP_KERNEL);
+	if (!h)
+		return ERR_PTR(-ENOMEM);
+
+	h->desc.bLength			= UVC_DT_FORMAT_MJPEG_SIZE;
+	h->desc.bDescriptorType		= USB_DT_CS_INTERFACE;
+	h->desc.bDescriptorSubType	= UVC_VS_FORMAT_MJPEG;
+	h->desc.bDefaultFrameIndex	= 1;
+	h->desc.bAspectRatioX		= 0;
+	h->desc.bAspectRatioY		= 0;
+	h->desc.bmInterfaceFlags	= 0;
+	h->desc.bCopyProtect		= 0;
+
+	h->fmt.type = UVCG_MJPEG;
+	config_group_init_type_name(&h->fmt.group, name,
+				    &uvcg_mjpeg_type);
+
+	return &h->fmt.group;
+}
+
+static void uvcg_mjpeg_drop(struct config_group *group,
+			    struct config_item *item)
+{
+	struct uvcg_mjpeg *h = to_uvcg_mjpeg(item);
+
+	kfree(h);
+}
+
+static struct configfs_group_operations uvcg_mjpeg_grp_ops = {
+	.make_group		= uvcg_mjpeg_make,
+	.drop_item		= uvcg_mjpeg_drop,
+};
+
+static struct config_item_type uvcg_mjpeg_grp_type = {
+	.ct_group_ops	= &uvcg_mjpeg_grp_ops,
+	.ct_owner	= THIS_MODULE,
+};
+
+/* streaming/color_matching/default */
+static struct uvcg_default_color_matching {
+	struct config_group	group;
+} uvcg_default_color_matching;
+
+static inline struct uvcg_default_color_matching
+*to_uvcg_default_color_matching(struct config_item *item)
+{
+	return container_of(to_config_group(item),
+			    struct uvcg_default_color_matching, group);
+}
+
+CONFIGFS_ATTR_STRUCT(uvcg_default_color_matching);
+CONFIGFS_ATTR_OPS_RO(uvcg_default_color_matching);
+
+static struct configfs_item_operations uvcg_default_color_matching_item_ops = {
+	.show_attribute		= uvcg_default_color_matching_attr_show,
+};
+
+#define UVCG_DEFAULT_COLOR_MATCHING_ATTR(cname, aname, conv)		\
+static ssize_t uvcg_default_color_matching_##cname##_show(		\
+	struct uvcg_default_color_matching *dc, char *page)		\
+{									\
+	struct f_uvc_opts *opts;					\
+	struct config_item *opts_item;					\
+	struct mutex *su_mutex = &dc->group.cg_subsys->su_mutex;	\
+	struct uvc_color_matching_descriptor *cd;			\
+	int result;							\
+									\
+	mutex_lock(su_mutex); /* for navigating configfs hierarchy */	\
+									\
+	opts_item = dc->group.cg_item.ci_parent->ci_parent->ci_parent;	\
+	opts = to_f_uvc_opts(opts_item);				\
+	cd = &opts->uvc_color_matching;					\
+									\
+	mutex_lock(&opts->lock);					\
+	result = sprintf(page, "%d\n", conv(cd->aname));		\
+	mutex_unlock(&opts->lock);					\
+									\
+	mutex_unlock(su_mutex);						\
+	return result;							\
+}									\
+									\
+static struct uvcg_default_color_matching_attribute			\
+	uvcg_default_color_matching_##cname =				\
+	__CONFIGFS_ATTR_RO(aname, uvcg_default_color_matching_##cname##_show)
+
+#define identity_conv(x) (x)
+
+UVCG_DEFAULT_COLOR_MATCHING_ATTR(b_color_primaries, bColorPrimaries,
+				 identity_conv);
+UVCG_DEFAULT_COLOR_MATCHING_ATTR(b_transfer_characteristics,
+				 bTransferCharacteristics, identity_conv);
+UVCG_DEFAULT_COLOR_MATCHING_ATTR(b_matrix_coefficients, bMatrixCoefficients,
+				 identity_conv);
+
+#undef identity_conv
+
+#undef UVCG_DEFAULT_COLOR_MATCHING_ATTR
+
+static struct configfs_attribute *uvcg_default_color_matching_attrs[] = {
+	&uvcg_default_color_matching_b_color_primaries.attr,
+	&uvcg_default_color_matching_b_transfer_characteristics.attr,
+	&uvcg_default_color_matching_b_matrix_coefficients.attr,
+	NULL,
+};
+
+static struct config_item_type uvcg_default_color_matching_type = {
+	.ct_item_ops	= &uvcg_default_color_matching_item_ops,
+	.ct_attrs	= uvcg_default_color_matching_attrs,
+	.ct_owner	= THIS_MODULE,
+};
+
+/* struct uvcg_color_matching {}; */
+
+static struct config_group *uvcg_color_matching_default_groups[] = {
+	&uvcg_default_color_matching.group,
+	NULL,
+};
+
+/* streaming/color_matching */
+static struct uvcg_color_matching_grp {
+	struct config_group	group;
+} uvcg_color_matching_grp;
+
+static struct config_item_type uvcg_color_matching_grp_type = {
+	.ct_owner = THIS_MODULE,
+};
+
+/* streaming/class/{fs|hs|ss} */
+static struct uvcg_streaming_class {
+	struct config_group	group;
+} uvcg_streaming_class_fs, uvcg_streaming_class_hs, uvcg_streaming_class_ss;
+
+
+static inline struct uvc_descriptor_header
+***__uvcg_get_stream_class_arr(struct config_item *i, struct f_uvc_opts *o)
+{
+	struct uvcg_streaming_class *cl = container_of(to_config_group(i),
+		struct uvcg_streaming_class, group);
+
+	if (cl == &uvcg_streaming_class_fs)
+		return &o->uvc_fs_streaming_cls;
+
+	if (cl == &uvcg_streaming_class_hs)
+		return &o->uvc_hs_streaming_cls;
+
+	if (cl == &uvcg_streaming_class_ss)
+		return &o->uvc_ss_streaming_cls;
+
+	return NULL;
+}
+
+enum uvcg_strm_type {
+	UVCG_HEADER = 0,
+	UVCG_FORMAT,
+	UVCG_FRAME
+};
+
+/*
+ * Iterate over a hierarchy of streaming descriptors' config items.
+ * The items are created by the user with configfs.
+ *
+ * It "processes" the header pointed to by @priv1, then for each format
+ * that follows the header "processes" the format itself and then for
+ * each frame inside a format "processes" the frame.
+ *
+ * As a "processing" function the @fun is used.
+ *
+ * __uvcg_iter_strm_cls() is used in two context: first, to calculate
+ * the amount of memory needed for an array of streaming descriptors
+ * and second, to actually fill the array.
+ *
+ * @h: streaming header pointer
+ * @priv2: an "inout" parameter (the caller might want to see the changes to it)
+ * @priv3: an "inout" parameter (the caller might want to see the changes to it)
+ * @fun: callback function for processing each level of the hierarchy
+ */
+static int __uvcg_iter_strm_cls(struct uvcg_streaming_header *h,
+	void *priv2, void *priv3,
+	int (*fun)(void *, void *, void *, int, enum uvcg_strm_type type))
+{
+	struct uvcg_format_ptr *f;
+	struct config_group *grp;
+	struct config_item *item;
+	struct uvcg_frame *frm;
+	int ret, i, j;
+
+	if (!fun)
+		return -EINVAL;
+
+	i = j = 0;
+	ret = fun(h, priv2, priv3, 0, UVCG_HEADER);
+	if (ret)
+		return ret;
+	list_for_each_entry(f, &h->formats, entry) {
+		ret = fun(f->fmt, priv2, priv3, i++, UVCG_FORMAT);
+		if (ret)
+			return ret;
+		grp = &f->fmt->group;
+		list_for_each_entry(item, &grp->cg_children, ci_entry) {
+			frm = to_uvcg_frame(item);
+			ret = fun(frm, priv2, priv3, j++, UVCG_FRAME);
+			if (ret)
+				return ret;
+		}
+	}
+
+	return ret;
+}
+
+/*
+ * Count how many bytes are needed for an array of streaming descriptors.
+ *
+ * @priv1: pointer to a header, format or frame
+ * @priv2: inout parameter, accumulated size of the array
+ * @priv3: inout parameter, accumulated number of the array elements
+ * @n: unused, this function's prototype must match @fun in __uvcg_iter_strm_cls
+ */
+static int __uvcg_cnt_strm(void *priv1, void *priv2, void *priv3, int n,
+			   enum uvcg_strm_type type)
+{
+	size_t *size = priv2;
+	size_t *count = priv3;
+
+	switch (type) {
+	case UVCG_HEADER: {
+		struct uvcg_streaming_header *h = priv1;
+
+		*size += sizeof(h->desc);
+		/* bmaControls */
+		*size += h->num_fmt * UVCG_STREAMING_CONTROL_SIZE;
+	}
+	break;
+	case UVCG_FORMAT: {
+		struct uvcg_format *fmt = priv1;
+
+		if (fmt->type == UVCG_UNCOMPRESSED) {
+			struct uvcg_uncompressed *u =
+				container_of(fmt, struct uvcg_uncompressed,
+					     fmt);
+
+			*size += sizeof(u->desc);
+		} else if (fmt->type == UVCG_MJPEG) {
+			struct uvcg_mjpeg *m =
+				container_of(fmt, struct uvcg_mjpeg, fmt);
+
+			*size += sizeof(m->desc);
+		} else {
+			return -EINVAL;
+		}
+	}
+	break;
+	case UVCG_FRAME: {
+		struct uvcg_frame *frm = priv1;
+		int sz = sizeof(frm->dw_frame_interval);
+
+		*size += sizeof(frm->frame);
+		*size += frm->frame.b_frame_interval_type * sz;
+	}
+	break;
+	}
+
+	++*count;
+
+	return 0;
+}
+
+/*
+ * Fill an array of streaming descriptors.
+ *
+ * @priv1: pointer to a header, format or frame
+ * @priv2: inout parameter, pointer into a block of memory
+ * @priv3: inout parameter, pointer to a 2-dimensional array
+ */
+static int __uvcg_fill_strm(void *priv1, void *priv2, void *priv3, int n,
+			    enum uvcg_strm_type type)
+{
+	void **dest = priv2;
+	struct uvc_descriptor_header ***array = priv3;
+	size_t sz;
+
+	**array = *dest;
+	++*array;
+
+	switch (type) {
+	case UVCG_HEADER: {
+		struct uvc_input_header_descriptor *ihdr = *dest;
+		struct uvcg_streaming_header *h = priv1;
+		struct uvcg_format_ptr *f;
+
+		memcpy(*dest, &h->desc, sizeof(h->desc));
+		*dest += sizeof(h->desc);
+		sz = UVCG_STREAMING_CONTROL_SIZE;
+		list_for_each_entry(f, &h->formats, entry) {
+			memcpy(*dest, f->fmt->bmaControls, sz);
+			*dest += sz;
+		}
+		ihdr->bLength = sizeof(h->desc) + h->num_fmt * sz;
+		ihdr->bNumFormats = h->num_fmt;
+	}
+	break;
+	case UVCG_FORMAT: {
+		struct uvcg_format *fmt = priv1;
+
+		if (fmt->type == UVCG_UNCOMPRESSED) {
+			struct uvc_format_uncompressed *unc = *dest;
+			struct uvcg_uncompressed *u =
+				container_of(fmt, struct uvcg_uncompressed,
+					     fmt);
+
+			memcpy(*dest, &u->desc, sizeof(u->desc));
+			*dest += sizeof(u->desc);
+			unc->bNumFrameDescriptors = fmt->num_frames;
+			unc->bFormatIndex = n + 1;
+		} else if (fmt->type == UVCG_MJPEG) {
+			struct uvc_format_mjpeg *mjp = *dest;
+			struct uvcg_mjpeg *m =
+				container_of(fmt, struct uvcg_mjpeg, fmt);
+
+			memcpy(*dest, &m->desc, sizeof(m->desc));
+			*dest += sizeof(m->desc);
+			mjp->bNumFrameDescriptors = fmt->num_frames;
+			mjp->bFormatIndex = n + 1;
+		} else {
+			return -EINVAL;
+		}
+	}
+	break;
+	case UVCG_FRAME: {
+		struct uvcg_frame *frm = priv1;
+		struct uvc_descriptor_header *h = *dest;
+
+		sz = sizeof(frm->frame);
+		memcpy(*dest, &frm->frame, sz);
+		*dest += sz;
+		sz = frm->frame.b_frame_interval_type *
+			sizeof(*frm->dw_frame_interval);
+		memcpy(*dest, frm->dw_frame_interval, sz);
+		*dest += sz;
+		if (frm->fmt_type == UVCG_UNCOMPRESSED)
+			h->bLength = UVC_DT_FRAME_UNCOMPRESSED_SIZE(
+				frm->frame.b_frame_interval_type);
+		else if (frm->fmt_type == UVCG_MJPEG)
+			h->bLength = UVC_DT_FRAME_MJPEG_SIZE(
+				frm->frame.b_frame_interval_type);
+	}
+	break;
+	}
+
+	return 0;
+}
+
+static int uvcg_streaming_class_allow_link(struct config_item *src,
+					   struct config_item *target)
+{
+	struct config_item *streaming, *header;
+	struct f_uvc_opts *opts;
+	struct mutex *su_mutex = &src->ci_group->cg_subsys->su_mutex;
+	struct uvc_descriptor_header ***class_array, **cl_arr;
+	struct uvcg_streaming_header *target_hdr;
+	void *data, *data_save;
+	size_t size = 0, count = 0;
+	int ret = -EINVAL;
+
+	mutex_lock(su_mutex); /* for navigating configfs hierarchy */
+
+	streaming = src->ci_parent->ci_parent;
+	header = config_group_find_item(to_config_group(streaming), "header");
+	if (!header || target->ci_parent != header)
+		goto out;
+
+	opts = to_f_uvc_opts(streaming->ci_parent);
+
+	mutex_lock(&opts->lock);
+
+	class_array = __uvcg_get_stream_class_arr(src, opts);
+	if (!class_array || *class_array || opts->refcnt) {
+		ret = -EBUSY;
+		goto unlock;
+	}
+
+	target_hdr = to_uvcg_streaming_header(target);
+	ret = __uvcg_iter_strm_cls(target_hdr, &size, &count, __uvcg_cnt_strm);
+	if (ret)
+		goto unlock;
+
+	count += 2; /* color_matching, NULL */
+	*class_array = kcalloc(count, sizeof(void *), GFP_KERNEL);
+	if (!*class_array) {
+		ret = -ENOMEM;
+		goto unlock;
+	}
+
+	data = data_save = kzalloc(size, GFP_KERNEL);
+	if (!data) {
+		kfree(*class_array);
+		*class_array = NULL;
+		ret = PTR_ERR(data);
+		goto unlock;
+	}
+	cl_arr = *class_array;
+	ret = __uvcg_iter_strm_cls(target_hdr, &data, &cl_arr,
+				   __uvcg_fill_strm);
+	if (ret) {
+		kfree(*class_array);
+		*class_array = NULL;
+		/*
+		 * __uvcg_fill_strm() called from __uvcg_iter_stream_cls()
+		 * might have advanced the "data", so use a backup copy
+		 */
+		kfree(data_save);
+		goto unlock;
+	}
+	*cl_arr = (struct uvc_descriptor_header *)&opts->uvc_color_matching;
+
+	++target_hdr->linked;
+	ret = 0;
+
+unlock:
+	mutex_unlock(&opts->lock);
+out:
+	mutex_unlock(su_mutex);
+	return ret;
+}
+
+static int uvcg_streaming_class_drop_link(struct config_item *src,
+					  struct config_item *target)
+{
+	struct config_item *streaming, *header;
+	struct f_uvc_opts *opts;
+	struct mutex *su_mutex = &src->ci_group->cg_subsys->su_mutex;
+	struct uvc_descriptor_header ***class_array;
+	struct uvcg_streaming_header *target_hdr;
+	int ret = -EINVAL;
+
+	mutex_lock(su_mutex); /* for navigating configfs hierarchy */
+
+	streaming = src->ci_parent->ci_parent;
+	header = config_group_find_item(to_config_group(streaming), "header");
+	if (!header || target->ci_parent != header)
+		goto out;
+
+	opts = to_f_uvc_opts(streaming->ci_parent);
+
+	mutex_lock(&opts->lock);
+
+	class_array = __uvcg_get_stream_class_arr(src, opts);
+	if (!class_array || !*class_array)
+		goto unlock;
+
+	if (opts->refcnt) {
+		ret = -EBUSY;
+		goto unlock;
+	}
+
+	target_hdr = to_uvcg_streaming_header(target);
+	--target_hdr->linked;
+	kfree(**class_array);
+	kfree(*class_array);
+	*class_array = NULL;
+	ret = 0;
+
+unlock:
+	mutex_unlock(&opts->lock);
+out:
+	mutex_unlock(su_mutex);
+	return ret;
+}
+
+static struct configfs_item_operations uvcg_streaming_class_item_ops = {
+	.allow_link	= uvcg_streaming_class_allow_link,
+	.drop_link	= uvcg_streaming_class_drop_link,
+};
+
+static struct config_item_type uvcg_streaming_class_type = {
+	.ct_item_ops	= &uvcg_streaming_class_item_ops,
+	.ct_owner	= THIS_MODULE,
+};
+
+static struct config_group *uvcg_streaming_class_default_groups[] = {
+	&uvcg_streaming_class_fs.group,
+	&uvcg_streaming_class_hs.group,
+	&uvcg_streaming_class_ss.group,
+	NULL,
+};
+
+/* streaming/class */
+static struct uvcg_streaming_class_grp {
+	struct config_group	group;
+} uvcg_streaming_class_grp;
+
+static struct config_item_type uvcg_streaming_class_grp_type = {
+	.ct_owner = THIS_MODULE,
+};
+
+static struct config_group *uvcg_streaming_default_groups[] = {
+	&uvcg_streaming_header_grp.group,
+	&uvcg_uncompressed_grp.group,
+	&uvcg_mjpeg_grp.group,
+	&uvcg_color_matching_grp.group,
+	&uvcg_streaming_class_grp.group,
+	NULL,
+};
+
+/* streaming */
+static struct uvcg_streaming_grp {
+	struct config_group	group;
+} uvcg_streaming_grp;
+
+static struct config_item_type uvcg_streaming_grp_type = {
+	.ct_owner = THIS_MODULE,
+};
+
+static struct config_group *uvcg_default_groups[] = {
+	&uvcg_control_grp.group,
+	&uvcg_streaming_grp.group,
+	NULL,
+};
+
+static inline struct f_uvc_opts *to_f_uvc_opts(struct config_item *item)
+{
+	return container_of(to_config_group(item), struct f_uvc_opts,
+			    func_inst.group);
+}
+
+CONFIGFS_ATTR_STRUCT(f_uvc_opts);
+CONFIGFS_ATTR_OPS(f_uvc_opts);
+
+static void uvc_attr_release(struct config_item *item)
+{
+	struct f_uvc_opts *opts = to_f_uvc_opts(item);
+
+	usb_put_function_instance(&opts->func_inst);
+}
+
+static struct configfs_item_operations uvc_item_ops = {
+	.release		= uvc_attr_release,
+	.show_attribute		= f_uvc_opts_attr_show,
+	.store_attribute	= f_uvc_opts_attr_store,
+};
+
+#define UVCG_OPTS_ATTR(cname, conv, str2u, uxx, vnoc, limit)		\
+static ssize_t f_uvc_opts_##cname##_show(				\
+	struct f_uvc_opts *opts, char *page)				\
+{									\
+	int result;							\
+									\
+	mutex_lock(&opts->lock);					\
+	result = sprintf(page, "%d\n", conv(opts->cname));		\
+	mutex_unlock(&opts->lock);					\
+									\
+	return result;							\
+}									\
+									\
+static ssize_t								\
+f_uvc_opts_##cname##_store(struct f_uvc_opts *opts,			\
+			   const char *page, size_t len)		\
+{									\
+	int ret;							\
+	uxx num;							\
+									\
+	mutex_lock(&opts->lock);					\
+	if (opts->refcnt) {						\
+		ret = -EBUSY;						\
+		goto end;						\
+	}								\
+									\
+	ret = str2u(page, 0, &num);					\
+	if (ret)							\
+		goto end;						\
+									\
+	if (num > limit) {						\
+		ret = -EINVAL;						\
+		goto end;						\
+	}								\
+	opts->cname = vnoc(num);					\
+	ret = len;							\
+end:									\
+	mutex_unlock(&opts->lock);					\
+	return ret;							\
+}									\
+									\
+static struct f_uvc_opts_attribute					\
+	f_uvc_opts_attribute_##cname =					\
+	__CONFIGFS_ATTR(cname, S_IRUGO | S_IWUSR,			\
+			f_uvc_opts_##cname##_show,			\
+			f_uvc_opts_##cname##_store)
+
+#define identity_conv(x) (x)
+
+UVCG_OPTS_ATTR(streaming_interval, identity_conv, kstrtou8, u8, identity_conv,
+	       16);
+UVCG_OPTS_ATTR(streaming_maxpacket, le16_to_cpu, kstrtou16, u16, le16_to_cpu,
+	       3072);
+UVCG_OPTS_ATTR(streaming_maxburst, identity_conv, kstrtou8, u8, identity_conv,
+	       15);
+
+#undef identity_conv
+
+#undef UVCG_OPTS_ATTR
+
+static struct configfs_attribute *uvc_attrs[] = {
+	&f_uvc_opts_attribute_streaming_interval.attr,
+	&f_uvc_opts_attribute_streaming_maxpacket.attr,
+	&f_uvc_opts_attribute_streaming_maxburst.attr,
+	NULL,
+};
+
+static struct config_item_type uvc_func_type = {
+	.ct_item_ops	= &uvc_item_ops,
+	.ct_attrs	= uvc_attrs,
+	.ct_owner	= THIS_MODULE,
+};
+
+static inline void uvcg_init_group(struct config_group *g,
+				   struct config_group **default_groups,
+				   const char *name,
+				   struct config_item_type *type)
+{
+	g->default_groups = default_groups;
+	config_group_init_type_name(g, name, type);
+}
+
+int uvcg_attach_configfs(struct f_uvc_opts *opts)
+{
+	config_group_init_type_name(&uvcg_control_header_grp.group,
+				    "header",
+				    &uvcg_control_header_grp_type);
+	config_group_init_type_name(&uvcg_default_processing.group,
+				    "default",
+				    &uvcg_default_processing_type);
+	uvcg_init_group(&uvcg_processing_grp.group,
+			uvcg_processing_default_groups,
+			"processing",
+			&uvcg_processing_grp_type);
+	config_group_init_type_name(&uvcg_default_camera.group,
+				    "default",
+				    &uvcg_default_camera_type);
+	uvcg_init_group(&uvcg_camera_grp.group,
+			uvcg_camera_default_groups,
+			"camera",
+			&uvcg_camera_grp_type);
+	config_group_init_type_name(&uvcg_default_output.group,
+				    "default",
+				    &uvcg_default_output_type);
+	uvcg_init_group(&uvcg_output_grp.group,
+			uvcg_output_default_groups,
+			"output",
+			&uvcg_output_grp_type);
+	uvcg_init_group(&uvcg_terminal_grp.group,
+			uvcg_terminal_default_groups,
+			"terminal",
+			&uvcg_terminal_grp_type);
+	config_group_init_type_name(&uvcg_control_class_fs.group,
+				    "fs",
+				    &uvcg_control_class_type);
+	config_group_init_type_name(&uvcg_control_class_ss.group,
+				    "ss",
+				    &uvcg_control_class_type);
+	uvcg_init_group(&uvcg_control_class_grp.group,
+			uvcg_control_class_default_groups,
+			"class",
+			&uvcg_control_class_grp_type);
+	uvcg_init_group(&uvcg_control_grp.group,
+			uvcg_control_default_groups,
+			"control",
+			&uvcg_control_grp_type);
+	config_group_init_type_name(&uvcg_streaming_header_grp.group,
+				    "header",
+				    &uvcg_streaming_header_grp_type);
+	config_group_init_type_name(&uvcg_uncompressed_grp.group,
+				    "uncompressed",
+				    &uvcg_uncompressed_grp_type);
+	config_group_init_type_name(&uvcg_mjpeg_grp.group,
+				    "mjpeg",
+				    &uvcg_mjpeg_grp_type);
+	config_group_init_type_name(&uvcg_default_color_matching.group,
+				    "default",
+				    &uvcg_default_color_matching_type);
+	uvcg_init_group(&uvcg_color_matching_grp.group,
+			uvcg_color_matching_default_groups,
+			"color_matching",
+			&uvcg_color_matching_grp_type);
+	config_group_init_type_name(&uvcg_streaming_class_fs.group,
+				    "fs",
+				    &uvcg_streaming_class_type);
+	config_group_init_type_name(&uvcg_streaming_class_hs.group,
+				    "hs",
+				    &uvcg_streaming_class_type);
+	config_group_init_type_name(&uvcg_streaming_class_ss.group,
+				    "ss",
+				    &uvcg_streaming_class_type);
+	uvcg_init_group(&uvcg_streaming_class_grp.group,
+			uvcg_streaming_class_default_groups,
+			"class",
+			&uvcg_streaming_class_grp_type);
+	uvcg_init_group(&uvcg_streaming_grp.group,
+			uvcg_streaming_default_groups,
+			"streaming",
+			&uvcg_streaming_grp_type);
+	uvcg_init_group(&opts->func_inst.group,
+			uvcg_default_groups,
+			"",
+			&uvc_func_type);
+	return 0;
+}

+ 22 - 0
drivers/usb/gadget/function/uvc_configfs.h

@@ -0,0 +1,22 @@
+/*
+ * uvc_configfs.h
+ *
+ * Configfs support for the uvc function.
+ *
+ * Copyright (c) 2014 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 UVC_CONFIGFS_H
+#define UVC_CONFIGFS_H
+
+struct f_uvc_opts;
+
+int uvcg_attach_configfs(struct f_uvc_opts *opts);
+
+#endif /* UVC_CONFIGFS_H */

+ 4 - 4
drivers/usb/gadget/udc/at91_udc.c

@@ -176,7 +176,7 @@ static int proc_udc_show(struct seq_file *s, void *unused)
 		udc->enabled
 		udc->enabled
 			? (udc->vbus ? "active" : "enabled")
 			? (udc->vbus ? "active" : "enabled")
 			: "disabled",
 			: "disabled",
-		udc->selfpowered ? "self" : "VBUS",
+		udc->gadget.is_selfpowered ? "self" : "VBUS",
 		udc->suspended ? ", suspended" : "",
 		udc->suspended ? ", suspended" : "",
 		udc->driver ? udc->driver->driver.name : "(none)");
 		udc->driver ? udc->driver->driver.name : "(none)");
 
 
@@ -1000,7 +1000,7 @@ static int at91_set_selfpowered(struct usb_gadget *gadget, int is_on)
 	unsigned long	flags;
 	unsigned long	flags;
 
 
 	spin_lock_irqsave(&udc->lock, flags);
 	spin_lock_irqsave(&udc->lock, flags);
-	udc->selfpowered = (is_on != 0);
+	gadget->is_selfpowered = (is_on != 0);
 	spin_unlock_irqrestore(&udc->lock, flags);
 	spin_unlock_irqrestore(&udc->lock, flags);
 	return 0;
 	return 0;
 }
 }
@@ -1149,7 +1149,7 @@ static void handle_setup(struct at91_udc *udc, struct at91_ep *ep, u32 csr)
 	 */
 	 */
 	case ((USB_DIR_IN|USB_TYPE_STANDARD|USB_RECIP_DEVICE) << 8)
 	case ((USB_DIR_IN|USB_TYPE_STANDARD|USB_RECIP_DEVICE) << 8)
 			| USB_REQ_GET_STATUS:
 			| USB_REQ_GET_STATUS:
-		tmp = (udc->selfpowered << USB_DEVICE_SELF_POWERED);
+		tmp = (udc->gadget.is_selfpowered << USB_DEVICE_SELF_POWERED);
 		if (at91_udp_read(udc, AT91_UDP_GLB_STAT) & AT91_UDP_ESR)
 		if (at91_udp_read(udc, AT91_UDP_GLB_STAT) & AT91_UDP_ESR)
 			tmp |= (1 << USB_DEVICE_REMOTE_WAKEUP);
 			tmp |= (1 << USB_DEVICE_REMOTE_WAKEUP);
 		PACKET("get device status\n");
 		PACKET("get device status\n");
@@ -1653,7 +1653,7 @@ static int at91_start(struct usb_gadget *gadget,
 	udc->driver = driver;
 	udc->driver = driver;
 	udc->gadget.dev.of_node = udc->pdev->dev.of_node;
 	udc->gadget.dev.of_node = udc->pdev->dev.of_node;
 	udc->enabled = 1;
 	udc->enabled = 1;
-	udc->selfpowered = 1;
+	udc->gadget.is_selfpowered = 1;
 
 
 	return 0;
 	return 0;
 }
 }

+ 0 - 1
drivers/usb/gadget/udc/at91_udc.h

@@ -122,7 +122,6 @@ struct at91_udc {
 	unsigned			req_pending:1;
 	unsigned			req_pending:1;
 	unsigned			wait_for_addr_ack:1;
 	unsigned			wait_for_addr_ack:1;
 	unsigned			wait_for_config_ack:1;
 	unsigned			wait_for_config_ack:1;
-	unsigned			selfpowered:1;
 	unsigned			active_suspend:1;
 	unsigned			active_suspend:1;
 	u8				addr;
 	u8				addr;
 	struct at91_udc_data		board;
 	struct at91_udc_data		board;

+ 95 - 52
drivers/usb/gadget/udc/atmel_usba_udc.c

@@ -8,6 +8,7 @@
  * published by the Free Software Foundation.
  * published by the Free Software Foundation.
  */
  */
 #include <linux/clk.h>
 #include <linux/clk.h>
+#include <linux/clk/at91_pmc.h>
 #include <linux/module.h>
 #include <linux/module.h>
 #include <linux/init.h>
 #include <linux/init.h>
 #include <linux/interrupt.h>
 #include <linux/interrupt.h>
@@ -315,6 +316,17 @@ static inline void usba_cleanup_debugfs(struct usba_udc *udc)
 }
 }
 #endif
 #endif
 
 
+static inline u32 usba_int_enb_get(struct usba_udc *udc)
+{
+	return udc->int_enb_cache;
+}
+
+static inline void usba_int_enb_set(struct usba_udc *udc, u32 val)
+{
+	usba_writel(udc, INT_ENB, val);
+	udc->int_enb_cache = val;
+}
+
 static int vbus_is_present(struct usba_udc *udc)
 static int vbus_is_present(struct usba_udc *udc)
 {
 {
 	if (gpio_is_valid(udc->vbus_pin))
 	if (gpio_is_valid(udc->vbus_pin))
@@ -324,27 +336,22 @@ static int vbus_is_present(struct usba_udc *udc)
 	return 1;
 	return 1;
 }
 }
 
 
-#if defined(CONFIG_ARCH_AT91SAM9RL)
-
-#include <linux/clk/at91_pmc.h>
-
-static void toggle_bias(int is_on)
+static void toggle_bias(struct usba_udc *udc, int is_on)
 {
 {
-	unsigned int uckr = at91_pmc_read(AT91_CKGR_UCKR);
-
-	if (is_on)
-		at91_pmc_write(AT91_CKGR_UCKR, uckr | AT91_PMC_BIASEN);
-	else
-		at91_pmc_write(AT91_CKGR_UCKR, uckr & ~(AT91_PMC_BIASEN));
+	if (udc->errata && udc->errata->toggle_bias)
+		udc->errata->toggle_bias(udc, is_on);
 }
 }
 
 
-#else
-
-static void toggle_bias(int is_on)
+static void generate_bias_pulse(struct usba_udc *udc)
 {
 {
-}
+	if (!udc->bias_pulse_needed)
+		return;
+
+	if (udc->errata && udc->errata->pulse_bias)
+		udc->errata->pulse_bias(udc);
 
 
-#endif /* CONFIG_ARCH_AT91SAM9RL */
+	udc->bias_pulse_needed = false;
+}
 
 
 static void next_fifo_transaction(struct usba_ep *ep, struct usba_request *req)
 static void next_fifo_transaction(struct usba_ep *ep, struct usba_request *req)
 {
 {
@@ -601,16 +608,14 @@ usba_ep_enable(struct usb_ep *_ep, const struct usb_endpoint_descriptor *desc)
 	if (ep->can_dma) {
 	if (ep->can_dma) {
 		u32 ctrl;
 		u32 ctrl;
 
 
-		usba_writel(udc, INT_ENB,
-				(usba_readl(udc, INT_ENB)
-					| USBA_BF(EPT_INT, 1 << ep->index)
-					| USBA_BF(DMA_INT, 1 << ep->index)));
+		usba_int_enb_set(udc, usba_int_enb_get(udc) |
+				      USBA_BF(EPT_INT, 1 << ep->index) |
+				      USBA_BF(DMA_INT, 1 << ep->index));
 		ctrl = USBA_AUTO_VALID | USBA_INTDIS_DMA;
 		ctrl = USBA_AUTO_VALID | USBA_INTDIS_DMA;
 		usba_ep_writel(ep, CTL_ENB, ctrl);
 		usba_ep_writel(ep, CTL_ENB, ctrl);
 	} else {
 	} else {
-		usba_writel(udc, INT_ENB,
-				(usba_readl(udc, INT_ENB)
-					| USBA_BF(EPT_INT, 1 << ep->index)));
+		usba_int_enb_set(udc, usba_int_enb_get(udc) |
+				      USBA_BF(EPT_INT, 1 << ep->index));
 	}
 	}
 
 
 	spin_unlock_irqrestore(&udc->lock, flags);
 	spin_unlock_irqrestore(&udc->lock, flags);
@@ -618,7 +623,7 @@ usba_ep_enable(struct usb_ep *_ep, const struct usb_endpoint_descriptor *desc)
 	DBG(DBG_HW, "EPT_CFG%d after init: %#08lx\n", ep->index,
 	DBG(DBG_HW, "EPT_CFG%d after init: %#08lx\n", ep->index,
 			(unsigned long)usba_ep_readl(ep, CFG));
 			(unsigned long)usba_ep_readl(ep, CFG));
 	DBG(DBG_HW, "INT_ENB after init: %#08lx\n",
 	DBG(DBG_HW, "INT_ENB after init: %#08lx\n",
-			(unsigned long)usba_readl(udc, INT_ENB));
+			(unsigned long)usba_int_enb_get(udc));
 
 
 	return 0;
 	return 0;
 }
 }
@@ -654,9 +659,8 @@ static int usba_ep_disable(struct usb_ep *_ep)
 		usba_dma_readl(ep, STATUS);
 		usba_dma_readl(ep, STATUS);
 	}
 	}
 	usba_ep_writel(ep, CTL_DIS, USBA_EPT_ENABLE);
 	usba_ep_writel(ep, CTL_DIS, USBA_EPT_ENABLE);
-	usba_writel(udc, INT_ENB,
-			usba_readl(udc, INT_ENB)
-			& ~USBA_BF(EPT_INT, 1 << ep->index));
+	usba_int_enb_set(udc, usba_int_enb_get(udc) &
+			      ~USBA_BF(EPT_INT, 1 << ep->index));
 
 
 	request_complete_list(ep, &req_list, -ESHUTDOWN);
 	request_complete_list(ep, &req_list, -ESHUTDOWN);
 
 
@@ -985,6 +989,7 @@ usba_udc_set_selfpowered(struct usb_gadget *gadget, int is_selfpowered)
 	struct usba_udc *udc = to_usba_udc(gadget);
 	struct usba_udc *udc = to_usba_udc(gadget);
 	unsigned long flags;
 	unsigned long flags;
 
 
+	gadget->is_selfpowered = (is_selfpowered != 0);
 	spin_lock_irqsave(&udc->lock, flags);
 	spin_lock_irqsave(&udc->lock, flags);
 	if (is_selfpowered)
 	if (is_selfpowered)
 		udc->devstatus |= 1 << USB_DEVICE_SELF_POWERED;
 		udc->devstatus |= 1 << USB_DEVICE_SELF_POWERED;
@@ -1619,18 +1624,21 @@ static void usba_dma_irq(struct usba_udc *udc, struct usba_ep *ep)
 static irqreturn_t usba_udc_irq(int irq, void *devid)
 static irqreturn_t usba_udc_irq(int irq, void *devid)
 {
 {
 	struct usba_udc *udc = devid;
 	struct usba_udc *udc = devid;
-	u32 status;
+	u32 status, int_enb;
 	u32 dma_status;
 	u32 dma_status;
 	u32 ep_status;
 	u32 ep_status;
 
 
 	spin_lock(&udc->lock);
 	spin_lock(&udc->lock);
 
 
-	status = usba_readl(udc, INT_STA);
+	int_enb = usba_int_enb_get(udc);
+	status = usba_readl(udc, INT_STA) & int_enb;
 	DBG(DBG_INT, "irq, status=%#08x\n", status);
 	DBG(DBG_INT, "irq, status=%#08x\n", status);
 
 
 	if (status & USBA_DET_SUSPEND) {
 	if (status & USBA_DET_SUSPEND) {
-		toggle_bias(0);
+		toggle_bias(udc, 0);
 		usba_writel(udc, INT_CLR, USBA_DET_SUSPEND);
 		usba_writel(udc, INT_CLR, USBA_DET_SUSPEND);
+		usba_int_enb_set(udc, int_enb | USBA_WAKE_UP);
+		udc->bias_pulse_needed = true;
 		DBG(DBG_BUS, "Suspend detected\n");
 		DBG(DBG_BUS, "Suspend detected\n");
 		if (udc->gadget.speed != USB_SPEED_UNKNOWN
 		if (udc->gadget.speed != USB_SPEED_UNKNOWN
 				&& udc->driver && udc->driver->suspend) {
 				&& udc->driver && udc->driver->suspend) {
@@ -1641,13 +1649,15 @@ static irqreturn_t usba_udc_irq(int irq, void *devid)
 	}
 	}
 
 
 	if (status & USBA_WAKE_UP) {
 	if (status & USBA_WAKE_UP) {
-		toggle_bias(1);
+		toggle_bias(udc, 1);
 		usba_writel(udc, INT_CLR, USBA_WAKE_UP);
 		usba_writel(udc, INT_CLR, USBA_WAKE_UP);
+		usba_int_enb_set(udc, int_enb & ~USBA_WAKE_UP);
 		DBG(DBG_BUS, "Wake Up CPU detected\n");
 		DBG(DBG_BUS, "Wake Up CPU detected\n");
 	}
 	}
 
 
 	if (status & USBA_END_OF_RESUME) {
 	if (status & USBA_END_OF_RESUME) {
 		usba_writel(udc, INT_CLR, USBA_END_OF_RESUME);
 		usba_writel(udc, INT_CLR, USBA_END_OF_RESUME);
+		generate_bias_pulse(udc);
 		DBG(DBG_BUS, "Resume detected\n");
 		DBG(DBG_BUS, "Resume detected\n");
 		if (udc->gadget.speed != USB_SPEED_UNKNOWN
 		if (udc->gadget.speed != USB_SPEED_UNKNOWN
 				&& udc->driver && udc->driver->resume) {
 				&& udc->driver && udc->driver->resume) {
@@ -1683,6 +1693,7 @@ static irqreturn_t usba_udc_irq(int irq, void *devid)
 		struct usba_ep *ep0;
 		struct usba_ep *ep0;
 
 
 		usba_writel(udc, INT_CLR, USBA_END_OF_RESET);
 		usba_writel(udc, INT_CLR, USBA_END_OF_RESET);
+		generate_bias_pulse(udc);
 		reset_all_endpoints(udc);
 		reset_all_endpoints(udc);
 
 
 		if (udc->gadget.speed != USB_SPEED_UNKNOWN && udc->driver) {
 		if (udc->gadget.speed != USB_SPEED_UNKNOWN && udc->driver) {
@@ -1708,11 +1719,8 @@ static irqreturn_t usba_udc_irq(int irq, void *devid)
 				| USBA_BF(BK_NUMBER, USBA_BK_NUMBER_ONE)));
 				| USBA_BF(BK_NUMBER, USBA_BK_NUMBER_ONE)));
 		usba_ep_writel(ep0, CTL_ENB,
 		usba_ep_writel(ep0, CTL_ENB,
 				USBA_EPT_ENABLE | USBA_RX_SETUP);
 				USBA_EPT_ENABLE | USBA_RX_SETUP);
-		usba_writel(udc, INT_ENB,
-				(usba_readl(udc, INT_ENB)
-				| USBA_BF(EPT_INT, 1)
-				| USBA_DET_SUSPEND
-				| USBA_END_OF_RESUME));
+		usba_int_enb_set(udc, int_enb | USBA_BF(EPT_INT, 1) |
+				      USBA_DET_SUSPEND | USBA_END_OF_RESUME);
 
 
 		/*
 		/*
 		 * Unclear why we hit this irregularly, e.g. in usbtest,
 		 * Unclear why we hit this irregularly, e.g. in usbtest,
@@ -1745,13 +1753,13 @@ static irqreturn_t usba_vbus_irq(int irq, void *devid)
 	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(1);
+			toggle_bias(udc, 1);
 			usba_writel(udc, CTRL, USBA_ENABLE_MASK);
 			usba_writel(udc, CTRL, USBA_ENABLE_MASK);
-			usba_writel(udc, INT_ENB, USBA_END_OF_RESET);
+			usba_int_enb_set(udc, USBA_END_OF_RESET);
 		} else {
 		} else {
 			udc->gadget.speed = USB_SPEED_UNKNOWN;
 			udc->gadget.speed = USB_SPEED_UNKNOWN;
 			reset_all_endpoints(udc);
 			reset_all_endpoints(udc);
-			toggle_bias(0);
+			toggle_bias(udc, 0);
 			usba_writel(udc, CTRL, USBA_DISABLE_MASK);
 			usba_writel(udc, CTRL, USBA_DISABLE_MASK);
 			if (udc->driver->disconnect) {
 			if (udc->driver->disconnect) {
 				spin_unlock(&udc->lock);
 				spin_unlock(&udc->lock);
@@ -1797,9 +1805,9 @@ static int atmel_usba_start(struct usb_gadget *gadget,
 	/* 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);
 	spin_lock_irqsave(&udc->lock, flags);
 	if (vbus_is_present(udc) && udc->vbus_prev == 0) {
 	if (vbus_is_present(udc) && udc->vbus_prev == 0) {
-		toggle_bias(1);
+		toggle_bias(udc, 1);
 		usba_writel(udc, CTRL, USBA_ENABLE_MASK);
 		usba_writel(udc, CTRL, USBA_ENABLE_MASK);
-		usba_writel(udc, INT_ENB, USBA_END_OF_RESET);
+		usba_int_enb_set(udc, USBA_END_OF_RESET);
 	}
 	}
 	spin_unlock_irqrestore(&udc->lock, flags);
 	spin_unlock_irqrestore(&udc->lock, flags);
 
 
@@ -1820,7 +1828,7 @@ static int atmel_usba_stop(struct usb_gadget *gadget)
 	spin_unlock_irqrestore(&udc->lock, flags);
 	spin_unlock_irqrestore(&udc->lock, flags);
 
 
 	/* This will also disable the DP pullup */
 	/* This will also disable the DP pullup */
-	toggle_bias(0);
+	toggle_bias(udc, 0);
 	usba_writel(udc, CTRL, USBA_DISABLE_MASK);
 	usba_writel(udc, CTRL, USBA_DISABLE_MASK);
 
 
 	clk_disable_unprepare(udc->hclk);
 	clk_disable_unprepare(udc->hclk);
@@ -1832,6 +1840,41 @@ static int atmel_usba_stop(struct usb_gadget *gadget)
 }
 }
 
 
 #ifdef CONFIG_OF
 #ifdef CONFIG_OF
+static void at91sam9rl_toggle_bias(struct usba_udc *udc, int is_on)
+{
+	unsigned int uckr = at91_pmc_read(AT91_CKGR_UCKR);
+
+	if (is_on)
+		at91_pmc_write(AT91_CKGR_UCKR, uckr | AT91_PMC_BIASEN);
+	else
+		at91_pmc_write(AT91_CKGR_UCKR, uckr & ~(AT91_PMC_BIASEN));
+}
+
+static void at91sam9g45_pulse_bias(struct usba_udc *udc)
+{
+	unsigned int uckr = at91_pmc_read(AT91_CKGR_UCKR);
+
+	at91_pmc_write(AT91_CKGR_UCKR, uckr & ~(AT91_PMC_BIASEN));
+	at91_pmc_write(AT91_CKGR_UCKR, uckr | AT91_PMC_BIASEN);
+}
+
+static const struct usba_udc_errata at91sam9rl_errata = {
+	.toggle_bias = at91sam9rl_toggle_bias,
+};
+
+static const struct usba_udc_errata at91sam9g45_errata = {
+	.pulse_bias = at91sam9g45_pulse_bias,
+};
+
+static const struct of_device_id atmel_udc_dt_ids[] = {
+	{ .compatible = "atmel,at91sam9rl-udc", .data = &at91sam9rl_errata },
+	{ .compatible = "atmel,at91sam9g45-udc", .data = &at91sam9g45_errata },
+	{ .compatible = "atmel,sama5d3-udc" },
+	{ /* sentinel */ }
+};
+
+MODULE_DEVICE_TABLE(of, atmel_udc_dt_ids);
+
 static struct usba_ep * atmel_udc_of_init(struct platform_device *pdev,
 static struct usba_ep * atmel_udc_of_init(struct platform_device *pdev,
 						    struct usba_udc *udc)
 						    struct usba_udc *udc)
 {
 {
@@ -1839,10 +1882,17 @@ static struct usba_ep * atmel_udc_of_init(struct platform_device *pdev,
 	const char *name;
 	const char *name;
 	enum of_gpio_flags flags;
 	enum of_gpio_flags flags;
 	struct device_node *np = pdev->dev.of_node;
 	struct device_node *np = pdev->dev.of_node;
+	const struct of_device_id *match;
 	struct device_node *pp;
 	struct device_node *pp;
 	int i, ret;
 	int i, ret;
 	struct usba_ep *eps, *ep;
 	struct usba_ep *eps, *ep;
 
 
+	match = of_match_node(atmel_udc_dt_ids, np);
+	if (!match)
+		return ERR_PTR(-EINVAL);
+
+	udc->errata = match->data;
+
 	udc->num_ep = 0;
 	udc->num_ep = 0;
 
 
 	udc->vbus_pin = of_get_named_gpio_flags(np, "atmel,vbus-gpio", 0,
 	udc->vbus_pin = of_get_named_gpio_flags(np, "atmel,vbus-gpio", 0,
@@ -2033,7 +2083,7 @@ static int usba_udc_probe(struct platform_device *pdev)
 		dev_err(&pdev->dev, "Unable to enable pclk, aborting.\n");
 		dev_err(&pdev->dev, "Unable to enable pclk, aborting.\n");
 		return ret;
 		return ret;
 	}
 	}
-	toggle_bias(0);
+
 	usba_writel(udc, CTRL, USBA_DISABLE_MASK);
 	usba_writel(udc, CTRL, USBA_DISABLE_MASK);
 	clk_disable_unprepare(pclk);
 	clk_disable_unprepare(pclk);
 
 
@@ -2042,6 +2092,8 @@ static int usba_udc_probe(struct platform_device *pdev)
 	else
 	else
 		udc->usba_ep = usba_udc_pdata(pdev, udc);
 		udc->usba_ep = usba_udc_pdata(pdev, udc);
 
 
+	toggle_bias(udc, 0);
+
 	if (IS_ERR(udc->usba_ep))
 	if (IS_ERR(udc->usba_ep))
 		return PTR_ERR(udc->usba_ep);
 		return PTR_ERR(udc->usba_ep);
 
 
@@ -2101,15 +2153,6 @@ static int __exit usba_udc_remove(struct platform_device *pdev)
 	return 0;
 	return 0;
 }
 }
 
 
-#if defined(CONFIG_OF)
-static const struct of_device_id atmel_udc_dt_ids[] = {
-	{ .compatible = "atmel,at91sam9rl-udc" },
-	{ /* sentinel */ }
-};
-
-MODULE_DEVICE_TABLE(of, atmel_udc_dt_ids);
-#endif
-
 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		= {

+ 9 - 0
drivers/usb/gadget/udc/atmel_usba_udc.h

@@ -304,6 +304,11 @@ struct usba_request {
 	unsigned int				mapped:1;
 	unsigned int				mapped:1;
 };
 };
 
 
+struct usba_udc_errata {
+	void (*toggle_bias)(struct usba_udc *udc, int is_on);
+	void (*pulse_bias)(struct usba_udc *udc);
+};
+
 struct usba_udc {
 struct usba_udc {
 	/* Protect hw registers from concurrent modifications */
 	/* Protect hw registers from concurrent modifications */
 	spinlock_t lock;
 	spinlock_t lock;
@@ -314,6 +319,7 @@ struct usba_udc {
 	struct usb_gadget gadget;
 	struct usb_gadget gadget;
 	struct usb_gadget_driver *driver;
 	struct usb_gadget_driver *driver;
 	struct platform_device *pdev;
 	struct platform_device *pdev;
+	const struct usba_udc_errata *errata;
 	int irq;
 	int irq;
 	int vbus_pin;
 	int vbus_pin;
 	int vbus_pin_inverted;
 	int vbus_pin_inverted;
@@ -321,12 +327,15 @@ struct usba_udc {
 	struct clk *pclk;
 	struct clk *pclk;
 	struct clk *hclk;
 	struct clk *hclk;
 	struct usba_ep *usba_ep;
 	struct usba_ep *usba_ep;
+	bool bias_pulse_needed;
 
 
 	u16 devstatus;
 	u16 devstatus;
 
 
 	u16 test_mode;
 	u16 test_mode;
 	int vbus_prev;
 	int vbus_prev;
 
 
+	u32 int_enb_cache;
+
 #ifdef CONFIG_USB_GADGET_DEBUG_FS
 #ifdef CONFIG_USB_GADGET_DEBUG_FS
 	struct dentry *debugfs_root;
 	struct dentry *debugfs_root;
 	struct dentry *debugfs_regs;
 	struct dentry *debugfs_regs;

+ 5 - 5
drivers/usb/gadget/udc/bdc/bdc_ep.c

@@ -718,7 +718,7 @@ static int ep_queue(struct bdc_ep *ep, struct bdc_req *req)
 	struct bdc *bdc;
 	struct bdc *bdc;
 	int ret = 0;
 	int ret = 0;
 
 
-	if (!req || !ep || !ep->usb_ep.desc)
+	if (!req || !ep->usb_ep.desc)
 		return -EINVAL;
 		return -EINVAL;
 
 
 	bdc = ep->bdc;
 	bdc = ep->bdc;
@@ -882,8 +882,8 @@ static int ep_set_halt(struct bdc_ep *ep, u32 value)
 
 
 		ret = bdc_ep_set_stall(bdc, ep->ep_num);
 		ret = bdc_ep_set_stall(bdc, ep->ep_num);
 		if (ret)
 		if (ret)
-			dev_err(bdc->dev, "failed to %s STALL on %s\n",
-				value ? "set" : "clear", ep->name);
+			dev_err(bdc->dev, "failed to set STALL on %s\n",
+				ep->name);
 		else
 		else
 			ep->flags |= BDC_EP_STALL;
 			ep->flags |= BDC_EP_STALL;
 	} else {
 	} else {
@@ -891,8 +891,8 @@ static int ep_set_halt(struct bdc_ep *ep, u32 value)
 		dev_dbg(bdc->dev, "Before Clear\n");
 		dev_dbg(bdc->dev, "Before Clear\n");
 		ret = bdc_ep_clear_stall(bdc, ep->ep_num);
 		ret = bdc_ep_clear_stall(bdc, ep->ep_num);
 		if (ret)
 		if (ret)
-			dev_err(bdc->dev, "failed to %s STALL on %s\n",
-				value ? "set" : "clear", ep->name);
+			dev_err(bdc->dev, "failed to clear STALL on %s\n",
+				ep->name);
 		else
 		else
 			ep->flags &= ~BDC_EP_STALL;
 			ep->flags &= ~BDC_EP_STALL;
 		dev_dbg(bdc->dev, "After  Clear\n");
 		dev_dbg(bdc->dev, "After  Clear\n");

+ 1 - 0
drivers/usb/gadget/udc/bdc/bdc_udc.c

@@ -454,6 +454,7 @@ static int bdc_udc_set_selfpowered(struct usb_gadget *gadget,
 	unsigned long           flags;
 	unsigned long           flags;
 
 
 	dev_dbg(bdc->dev, "%s()\n", __func__);
 	dev_dbg(bdc->dev, "%s()\n", __func__);
+	gadget->is_selfpowered = (is_self != 0);
 	spin_lock_irqsave(&bdc->lock, flags);
 	spin_lock_irqsave(&bdc->lock, flags);
 	if (!is_self)
 	if (!is_self)
 		bdc->devstatus |= 1 << USB_DEVICE_SELF_POWERED;
 		bdc->devstatus |= 1 << USB_DEVICE_SELF_POWERED;

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

@@ -802,6 +802,7 @@ static int dummy_set_selfpowered(struct usb_gadget *_gadget, int value)
 {
 {
 	struct dummy	*dum;
 	struct dummy	*dum;
 
 
+	_gadget->is_selfpowered = (value != 0);
 	dum = gadget_to_dummy_hcd(_gadget)->dum;
 	dum = gadget_to_dummy_hcd(_gadget)->dum;
 	if (value)
 	if (value)
 		dum->devstatus |= (1 << USB_DEVICE_SELF_POWERED);
 		dum->devstatus |= (1 << USB_DEVICE_SELF_POWERED);

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

@@ -2630,7 +2630,7 @@ static int qe_udc_remove(struct platform_device *ofdev)
 	struct qe_udc *udc = platform_get_drvdata(ofdev);
 	struct qe_udc *udc = platform_get_drvdata(ofdev);
 	struct qe_ep *ep;
 	struct qe_ep *ep;
 	unsigned int size;
 	unsigned int size;
-	DECLARE_COMPLETION(done);
+	DECLARE_COMPLETION_ONSTACK(done);
 
 
 	usb_del_gadget_udc(&udc->gadget);
 	usb_del_gadget_udc(&udc->gadget);
 
 

+ 3 - 2
drivers/usb/gadget/udc/fsl_udc_core.c

@@ -1337,7 +1337,7 @@ static void ch9getstatus(struct fsl_udc *udc, u8 request_type, u16 value,
 
 
 	if ((request_type & USB_RECIP_MASK) == USB_RECIP_DEVICE) {
 	if ((request_type & USB_RECIP_MASK) == USB_RECIP_DEVICE) {
 		/* Get device status */
 		/* Get device status */
-		tmp = 1 << USB_DEVICE_SELF_POWERED;
+		tmp = udc->gadget.is_selfpowered;
 		tmp |= udc->remote_wakeup << USB_DEVICE_REMOTE_WAKEUP;
 		tmp |= udc->remote_wakeup << USB_DEVICE_REMOTE_WAKEUP;
 	} else if ((request_type & USB_RECIP_MASK) == USB_RECIP_INTERFACE) {
 	} else if ((request_type & USB_RECIP_MASK) == USB_RECIP_INTERFACE) {
 		/* Get interface status */
 		/* Get interface status */
@@ -1948,6 +1948,7 @@ static int fsl_udc_start(struct usb_gadget *g,
 	/* hook up the driver */
 	/* hook up the driver */
 	udc_controller->driver = driver;
 	udc_controller->driver = driver;
 	spin_unlock_irqrestore(&udc_controller->lock, flags);
 	spin_unlock_irqrestore(&udc_controller->lock, flags);
+	g->is_selfpowered = 1;
 
 
 	if (!IS_ERR_OR_NULL(udc_controller->transceiver)) {
 	if (!IS_ERR_OR_NULL(udc_controller->transceiver)) {
 		/* Suspend the controller until OTG enable it */
 		/* Suspend the controller until OTG enable it */
@@ -2529,7 +2530,7 @@ static int __exit fsl_udc_remove(struct platform_device *pdev)
 	struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 	struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 	struct fsl_usb2_platform_data *pdata = dev_get_platdata(&pdev->dev);
 	struct fsl_usb2_platform_data *pdata = dev_get_platdata(&pdev->dev);
 
 
-	DECLARE_COMPLETION(done);
+	DECLARE_COMPLETION_ONSTACK(done);
 
 
 	if (!udc_controller)
 	if (!udc_controller)
 		return -ENODEV;
 		return -ENODEV;

+ 4 - 8
drivers/usb/gadget/udc/lpc32xx_udc.c

@@ -191,7 +191,6 @@ struct lpc32xx_udc {
 	bool			enabled;
 	bool			enabled;
 	bool			clocked;
 	bool			clocked;
 	bool			suspended;
 	bool			suspended;
-	bool			selfpowered;
 	int                     ep0state;
 	int                     ep0state;
 	atomic_t                enabled_ep_cnt;
 	atomic_t                enabled_ep_cnt;
 	wait_queue_head_t       ep_disable_wait_queue;
 	wait_queue_head_t       ep_disable_wait_queue;
@@ -547,7 +546,7 @@ static int proc_udc_show(struct seq_file *s, void *unused)
 		   udc->vbus ? "present" : "off",
 		   udc->vbus ? "present" : "off",
 		   udc->enabled ? (udc->vbus ? "active" : "enabled") :
 		   udc->enabled ? (udc->vbus ? "active" : "enabled") :
 		   "disabled",
 		   "disabled",
-		   udc->selfpowered ? "self" : "VBUS",
+		   udc->gadget.is_selfpowered ? "self" : "VBUS",
 		   udc->suspended ? ", suspended" : "",
 		   udc->suspended ? ", suspended" : "",
 		   udc->driver ? udc->driver->driver.name : "(none)");
 		   udc->driver ? udc->driver->driver.name : "(none)");
 
 
@@ -2212,7 +2211,7 @@ static int udc_get_status(struct lpc32xx_udc *udc, u16 reqtype, u16 wIndex)
 		break; /* Not supported */
 		break; /* Not supported */
 
 
 	case USB_RECIP_DEVICE:
 	case USB_RECIP_DEVICE:
-		ep0buff = (udc->selfpowered << USB_DEVICE_SELF_POWERED);
+		ep0buff = udc->gadget.is_selfpowered;
 		if (udc->dev_status & (1 << USB_DEVICE_REMOTE_WAKEUP))
 		if (udc->dev_status & (1 << USB_DEVICE_REMOTE_WAKEUP))
 			ep0buff |= (1 << USB_DEVICE_REMOTE_WAKEUP);
 			ep0buff |= (1 << USB_DEVICE_REMOTE_WAKEUP);
 		break;
 		break;
@@ -2498,10 +2497,7 @@ static int lpc32xx_wakeup(struct usb_gadget *gadget)
 
 
 static int lpc32xx_set_selfpowered(struct usb_gadget *gadget, int is_on)
 static int lpc32xx_set_selfpowered(struct usb_gadget *gadget, int is_on)
 {
 {
-	struct lpc32xx_udc *udc = to_udc(gadget);
-
-	/* Always self-powered */
-	udc->selfpowered = (is_on != 0);
+	gadget->is_selfpowered = (is_on != 0);
 
 
 	return 0;
 	return 0;
 }
 }
@@ -2946,7 +2942,7 @@ static int lpc32xx_start(struct usb_gadget *gadget,
 	udc->driver = driver;
 	udc->driver = driver;
 	udc->gadget.dev.of_node = udc->dev->of_node;
 	udc->gadget.dev.of_node = udc->dev->of_node;
 	udc->enabled = 1;
 	udc->enabled = 1;
-	udc->selfpowered = 1;
+	udc->gadget.is_selfpowered = 1;
 	udc->vbus = 0;
 	udc->vbus = 0;
 
 
 	/* Force VBUS process once to check for cable insertion */
 	/* Force VBUS process once to check for cable insertion */

+ 0 - 3
drivers/usb/gadget/udc/mv_udc_core.c

@@ -1378,9 +1378,6 @@ static int mv_udc_start(struct usb_gadget *gadget,
 		}
 		}
 	}
 	}
 
 
-	/* pullup is always on */
-	mv_udc_pullup(&udc->gadget, 1);
-
 	/* When boot with cable attached, there will be no vbus irq occurred */
 	/* When boot with cable attached, there will be no vbus irq occurred */
 	if (udc->qwork)
 	if (udc->qwork)
 		queue_work(udc->qwork, &udc->vbus_work);
 		queue_work(udc->qwork, &udc->vbus_work);

+ 2 - 5
drivers/usb/gadget/udc/net2272.c

@@ -1132,13 +1132,10 @@ net2272_wakeup(struct usb_gadget *_gadget)
 static int
 static int
 net2272_set_selfpowered(struct usb_gadget *_gadget, int value)
 net2272_set_selfpowered(struct usb_gadget *_gadget, int value)
 {
 {
-	struct net2272 *dev;
-
 	if (!_gadget)
 	if (!_gadget)
 		return -ENODEV;
 		return -ENODEV;
-	dev = container_of(_gadget, struct net2272, gadget);
 
 
-	dev->is_selfpowered = value;
+	_gadget->is_selfpowered = (value != 0);
 
 
 	return 0;
 	return 0;
 }
 }
@@ -1844,7 +1841,7 @@ net2272_handle_stat0_irqs(struct net2272 *dev, u8 stat)
 			case USB_RECIP_DEVICE:
 			case USB_RECIP_DEVICE:
 				if (u.r.wLength > 2)
 				if (u.r.wLength > 2)
 					goto do_stall;
 					goto do_stall;
-				if (dev->is_selfpowered)
+				if (dev->gadget.is_selfpowered)
 					status = (1 << USB_DEVICE_SELF_POWERED);
 					status = (1 << USB_DEVICE_SELF_POWERED);
 
 
 				/* don't bother with a request object! */
 				/* don't bother with a request object! */

+ 0 - 1
drivers/usb/gadget/udc/net2272.h

@@ -458,7 +458,6 @@ struct net2272 {
 	struct usb_gadget_driver *driver;
 	struct usb_gadget_driver *driver;
 	unsigned protocol_stall:1,
 	unsigned protocol_stall:1,
 	         softconnect:1,
 	         softconnect:1,
-	         is_selfpowered:1,
 	         wakeup:1,
 	         wakeup:1,
 	         dma_eot_polarity:1,
 	         dma_eot_polarity:1,
 	         dma_dack_polarity:1,
 	         dma_dack_polarity:1,

+ 130 - 403
drivers/usb/gadget/udc/net2280.c

@@ -12,11 +12,7 @@
  * the Mass Storage, Serial, and Ethernet/RNDIS gadget drivers
  * the Mass Storage, Serial, and Ethernet/RNDIS gadget drivers
  * as well as Gadget Zero and Gadgetfs.
  * as well as Gadget Zero and Gadgetfs.
  *
  *
- * DMA is enabled by default.  Drivers using transfer queues might use
- * DMA chaining to remove IRQ latencies between transfers.  (Except when
- * short OUT transfers happen.)  Drivers can use the req->no_interrupt
- * hint to completely eliminate some IRQs, if a later IRQ is guaranteed
- * and DMA chaining is enabled.
+ * DMA is enabled by default.
  *
  *
  * MSI is enabled by default.  The legacy IRQ is used if MSI couldn't
  * MSI is enabled by default.  The legacy IRQ is used if MSI couldn't
  * be enabled.
  * be enabled.
@@ -84,23 +80,6 @@ static const char *const ep_name[] = {
 	"ep-e", "ep-f", "ep-g", "ep-h",
 	"ep-e", "ep-f", "ep-g", "ep-h",
 };
 };
 
 
-/* use_dma -- general goodness, fewer interrupts, less cpu load (vs PIO)
- * use_dma_chaining -- dma descriptor queueing gives even more irq reduction
- *
- * The net2280 DMA engines are not tightly integrated with their FIFOs;
- * not all cases are (yet) handled well in this driver or the silicon.
- * Some gadget drivers work better with the dma support here than others.
- * These two parameters let you use PIO or more aggressive DMA.
- */
-static bool use_dma = true;
-static bool use_dma_chaining;
-static bool use_msi = true;
-
-/* "modprobe net2280 use_dma=n" etc */
-module_param(use_dma, bool, 0444);
-module_param(use_dma_chaining, bool, 0444);
-module_param(use_msi, bool, 0444);
-
 /* mode 0 == ep-{a,b,c,d} 1K fifo each
 /* mode 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
@@ -120,11 +99,6 @@ static bool enable_suspend;
 /* "modprobe net2280 enable_suspend=1" etc */
 /* "modprobe net2280 enable_suspend=1" etc */
 module_param(enable_suspend, bool, 0444);
 module_param(enable_suspend, bool, 0444);
 
 
-/* force full-speed operation */
-static bool full_speed;
-module_param(full_speed, bool, 0444);
-MODULE_PARM_DESC(full_speed, "force full-speed mode -- for testing only!");
-
 #define	DIR_STRING(bAddress) (((bAddress) & USB_DIR_IN) ? "in" : "out")
 #define	DIR_STRING(bAddress) (((bAddress) & USB_DIR_IN) ? "in" : "out")
 
 
 static char *type_string(u8 bmAttributes)
 static char *type_string(u8 bmAttributes)
@@ -202,15 +176,6 @@ net2280_enable(struct usb_ep *_ep, const struct usb_endpoint_descriptor *desc)
 	/* set speed-dependent max packet; may kick in high bandwidth */
 	/* set speed-dependent max packet; may kick in high bandwidth */
 	set_max_speed(ep, max);
 	set_max_speed(ep, max);
 
 
-	/* FIFO lines can't go to different packets.  PIO is ok, so
-	 * use it instead of troublesome (non-bulk) multi-packet DMA.
-	 */
-	if (ep->dma && (max % 4) != 0 && use_dma_chaining) {
-		ep_dbg(ep->dev, "%s, no dma for maxpacket %d\n",
-			ep->ep.name, ep->ep.maxpacket);
-		ep->dma = NULL;
-	}
-
 	/* set type, direction, address; reset fifo counters */
 	/* set type, direction, address; reset fifo counters */
 	writel(BIT(FIFO_FLUSH), &ep->regs->ep_stat);
 	writel(BIT(FIFO_FLUSH), &ep->regs->ep_stat);
 	tmp = (desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK);
 	tmp = (desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK);
@@ -478,7 +443,7 @@ static int net2280_disable(struct usb_ep *_ep)
 	/* synch memory views with the device */
 	/* synch memory views with the device */
 	(void)readl(&ep->cfg->ep_cfg);
 	(void)readl(&ep->cfg->ep_cfg);
 
 
-	if (use_dma && !ep->dma && ep->num >= 1 && ep->num <= 4)
+	if (!ep->dma && ep->num >= 1 && ep->num <= 4)
 		ep->dma = &ep->dev->dma[ep->num - 1];
 		ep->dma = &ep->dev->dma[ep->num - 1];
 
 
 	spin_unlock_irqrestore(&ep->dev->lock, flags);
 	spin_unlock_irqrestore(&ep->dev->lock, flags);
@@ -610,9 +575,15 @@ static void out_flush(struct net2280_ep *ep)
 	u32	__iomem *statp;
 	u32	__iomem *statp;
 	u32	tmp;
 	u32	tmp;
 
 
-	ASSERT_OUT_NAKING(ep);
-
 	statp = &ep->regs->ep_stat;
 	statp = &ep->regs->ep_stat;
+
+	tmp = readl(statp);
+	if (tmp & BIT(NAK_OUT_PACKETS)) {
+		ep_dbg(ep->dev, "%s %s %08x !NAK\n",
+			ep->ep.name, __func__, tmp);
+		writel(BIT(SET_NAK_OUT_PACKETS), &ep->regs->ep_rsp);
+	}
+
 	writel(BIT(DATA_OUT_PING_TOKEN_INTERRUPT) |
 	writel(BIT(DATA_OUT_PING_TOKEN_INTERRUPT) |
 		BIT(DATA_PACKET_RECEIVED_INTERRUPT),
 		BIT(DATA_PACKET_RECEIVED_INTERRUPT),
 		statp);
 		statp);
@@ -747,8 +718,7 @@ static void fill_dma_desc(struct net2280_ep *ep,
 	req->valid = valid;
 	req->valid = valid;
 	if (valid)
 	if (valid)
 		dmacount |= BIT(VALID_BIT);
 		dmacount |= BIT(VALID_BIT);
-	if (likely(!req->req.no_interrupt || !use_dma_chaining))
-		dmacount |= BIT(DMA_DONE_INTERRUPT_ENABLE);
+	dmacount |= BIT(DMA_DONE_INTERRUPT_ENABLE);
 
 
 	/* td->dmadesc = previously set by caller */
 	/* td->dmadesc = previously set by caller */
 	td->dmaaddr = cpu_to_le32 (req->req.dma);
 	td->dmaaddr = cpu_to_le32 (req->req.dma);
@@ -862,27 +832,11 @@ static void start_dma(struct net2280_ep *ep, struct net2280_request *req)
 	req->td->dmadesc = cpu_to_le32 (ep->td_dma);
 	req->td->dmadesc = cpu_to_le32 (ep->td_dma);
 	fill_dma_desc(ep, req, 1);
 	fill_dma_desc(ep, req, 1);
 
 
-	if (!use_dma_chaining)
-		req->td->dmacount |= cpu_to_le32(BIT(END_OF_CHAIN));
+	req->td->dmacount |= cpu_to_le32(BIT(END_OF_CHAIN));
 
 
 	start_queue(ep, tmp, req->td_dma);
 	start_queue(ep, tmp, req->td_dma);
 }
 }
 
 
-static inline void resume_dma(struct net2280_ep *ep)
-{
-	writel(readl(&ep->dma->dmactl) | BIT(DMA_ENABLE), &ep->dma->dmactl);
-
-	ep->dma_started = true;
-}
-
-static inline void ep_stop_dma(struct net2280_ep *ep)
-{
-	writel(readl(&ep->dma->dmactl) & ~BIT(DMA_ENABLE), &ep->dma->dmactl);
-	spin_stop_dma(ep->dma);
-
-	ep->dma_started = false;
-}
-
 static inline void
 static inline void
 queue_dma(struct net2280_ep *ep, struct net2280_request *req, int valid)
 queue_dma(struct net2280_ep *ep, struct net2280_request *req, int valid)
 {
 {
@@ -973,10 +927,8 @@ net2280_queue(struct usb_ep *_ep, struct usb_request *_req, gfp_t gfp_flags)
 			return ret;
 			return ret;
 	}
 	}
 
 
-#if 0
 	ep_vdbg(dev, "%s queue req %p, len %d buf %p\n",
 	ep_vdbg(dev, "%s queue req %p, len %d buf %p\n",
 			_ep->name, _req, _req->length, _req->buf);
 			_ep->name, _req, _req->length, _req->buf);
-#endif
 
 
 	spin_lock_irqsave(&dev->lock, flags);
 	spin_lock_irqsave(&dev->lock, flags);
 
 
@@ -984,24 +936,12 @@ net2280_queue(struct usb_ep *_ep, struct usb_request *_req, gfp_t gfp_flags)
 	_req->actual = 0;
 	_req->actual = 0;
 
 
 	/* kickstart this i/o queue? */
 	/* kickstart this i/o queue? */
-	if (list_empty(&ep->queue) && !ep->stopped) {
-		/* DMA request while EP halted */
-		if (ep->dma &&
-		    (readl(&ep->regs->ep_rsp) & BIT(CLEAR_ENDPOINT_HALT)) &&
-			(dev->quirks & PLX_SUPERSPEED)) {
-			int valid = 1;
-			if (ep->is_in) {
-				int expect;
-				expect = likely(req->req.zero ||
-						((req->req.length %
-						  ep->ep.maxpacket) != 0));
-				if (expect != ep->in_fifo_validate)
-					valid = 0;
-			}
-			queue_dma(ep, req, valid);
-		}
+	if  (list_empty(&ep->queue) && !ep->stopped &&
+		!((dev->quirks & PLX_SUPERSPEED) && ep->dma &&
+		  (readl(&ep->regs->ep_rsp) & BIT(CLEAR_ENDPOINT_HALT)))) {
+
 		/* use DMA if the endpoint supports it, else pio */
 		/* use DMA if the endpoint supports it, else pio */
-		else if (ep->dma)
+		if (ep->dma)
 			start_dma(ep, req);
 			start_dma(ep, req);
 		else {
 		else {
 			/* maybe there's no control data, just status ack */
 			/* maybe there's no control data, just status ack */
@@ -1084,8 +1024,6 @@ dma_done(struct net2280_ep *ep,	struct net2280_request *req, u32 dmacount,
 	done(ep, req, status);
 	done(ep, req, status);
 }
 }
 
 
-static void restart_dma(struct net2280_ep *ep);
-
 static void scan_dma_completions(struct net2280_ep *ep)
 static void scan_dma_completions(struct net2280_ep *ep)
 {
 {
 	/* only look at descriptors that were "naturally" retired,
 	/* only look at descriptors that were "naturally" retired,
@@ -1117,9 +1055,8 @@ static void scan_dma_completions(struct net2280_ep *ep)
 			dma_done(ep, req, tmp, 0);
 			dma_done(ep, req, tmp, 0);
 			break;
 			break;
 		} else if (!ep->is_in &&
 		} else if (!ep->is_in &&
-				(req->req.length % ep->ep.maxpacket) != 0) {
-			if (ep->dev->quirks & PLX_SUPERSPEED)
-				return dma_done(ep, req, tmp, 0);
+			   (req->req.length % ep->ep.maxpacket) &&
+			   !(ep->dev->quirks & PLX_SUPERSPEED)) {
 
 
 			tmp = readl(&ep->regs->ep_stat);
 			tmp = readl(&ep->regs->ep_stat);
 			/* AVOID TROUBLE HERE by not issuing short reads from
 			/* AVOID TROUBLE HERE by not issuing short reads from
@@ -1150,67 +1087,15 @@ static void scan_dma_completions(struct net2280_ep *ep)
 static void restart_dma(struct net2280_ep *ep)
 static void restart_dma(struct net2280_ep *ep)
 {
 {
 	struct net2280_request	*req;
 	struct net2280_request	*req;
-	u32			dmactl = dmactl_default;
 
 
 	if (ep->stopped)
 	if (ep->stopped)
 		return;
 		return;
 	req = list_entry(ep->queue.next, struct net2280_request, queue);
 	req = list_entry(ep->queue.next, struct net2280_request, queue);
 
 
-	if (!use_dma_chaining) {
-		start_dma(ep, req);
-		return;
-	}
-
-	/* the 2280 will be processing the queue unless queue hiccups after
-	 * the previous transfer:
-	 *  IN:   wanted automagic zlp, head doesn't (or vice versa)
-	 *        DMA_FIFO_VALIDATE doesn't init from dma descriptors.
-	 *  OUT:  was "usb-short", we must restart.
-	 */
-	if (ep->is_in && !req->valid) {
-		struct net2280_request	*entry, *prev = NULL;
-		int			reqmode, done = 0;
-
-		ep_dbg(ep->dev, "%s dma hiccup td %p\n", ep->ep.name, req->td);
-		ep->in_fifo_validate = likely(req->req.zero ||
-				(req->req.length % ep->ep.maxpacket) != 0);
-		if (ep->in_fifo_validate)
-			dmactl |= BIT(DMA_FIFO_VALIDATE);
-		list_for_each_entry(entry, &ep->queue, queue) {
-			__le32		dmacount;
-
-			if (entry == req)
-				continue;
-			dmacount = entry->td->dmacount;
-			if (!done) {
-				reqmode = likely(entry->req.zero ||
-				   (entry->req.length % ep->ep.maxpacket));
-				if (reqmode == ep->in_fifo_validate) {
-					entry->valid = 1;
-					dmacount |= valid_bit;
-					entry->td->dmacount = dmacount;
-					prev = entry;
-					continue;
-				} else {
-					/* force a hiccup */
-					prev->td->dmacount |= dma_done_ie;
-					done = 1;
-				}
-			}
-
-			/* walk the rest of the queue so unlinks behave */
-			entry->valid = 0;
-			dmacount &= ~valid_bit;
-			entry->td->dmacount = dmacount;
-			prev = entry;
-		}
-	}
-
-	writel(0, &ep->dma->dmactl);
-	start_queue(ep, dmactl, req->td_dma);
+	start_dma(ep, req);
 }
 }
 
 
-static void abort_dma_228x(struct net2280_ep *ep)
+static void abort_dma(struct net2280_ep *ep)
 {
 {
 	/* abort the current transfer */
 	/* abort the current transfer */
 	if (likely(!list_empty(&ep->queue))) {
 	if (likely(!list_empty(&ep->queue))) {
@@ -1222,19 +1107,6 @@ static void abort_dma_228x(struct net2280_ep *ep)
 	scan_dma_completions(ep);
 	scan_dma_completions(ep);
 }
 }
 
 
-static void abort_dma_338x(struct net2280_ep *ep)
-{
-	writel(BIT(DMA_ABORT), &ep->dma->dmastat);
-	spin_stop_dma(ep->dma);
-}
-
-static void abort_dma(struct net2280_ep *ep)
-{
-	if (ep->dev->quirks & PLX_LEGACY)
-		return abort_dma_228x(ep);
-	return abort_dma_338x(ep);
-}
-
 /* dequeue ALL requests */
 /* dequeue ALL requests */
 static void nuke(struct net2280_ep *ep)
 static void nuke(struct net2280_ep *ep)
 {
 {
@@ -1306,25 +1178,6 @@ static int net2280_dequeue(struct usb_ep *_ep, struct usb_request *_req)
 			done(ep, req, -ECONNRESET);
 			done(ep, req, -ECONNRESET);
 		}
 		}
 		req = NULL;
 		req = NULL;
-
-	/* patch up hardware chaining data */
-	} else if (ep->dma && use_dma_chaining) {
-		if (req->queue.prev == ep->queue.next) {
-			writel(le32_to_cpu(req->td->dmadesc),
-				&ep->dma->dmadesc);
-			if (req->td->dmacount & dma_done_ie)
-				writel(readl(&ep->dma->dmacount) |
-						le32_to_cpu(dma_done_ie),
-					&ep->dma->dmacount);
-		} else {
-			struct net2280_request	*prev;
-
-			prev = list_entry(req->queue.prev,
-				struct net2280_request, queue);
-			prev->td->dmadesc = req->td->dmadesc;
-			if (req->td->dmacount & dma_done_ie)
-				prev->td->dmacount |= dma_done_ie;
-		}
 	}
 	}
 
 
 	if (req)
 	if (req)
@@ -1512,10 +1365,10 @@ static int net2280_set_selfpowered(struct usb_gadget *_gadget, int value)
 	tmp = readl(&dev->usb->usbctl);
 	tmp = readl(&dev->usb->usbctl);
 	if (value) {
 	if (value) {
 		tmp |= BIT(SELF_POWERED_STATUS);
 		tmp |= BIT(SELF_POWERED_STATUS);
-		dev->selfpowered = 1;
+		_gadget->is_selfpowered = 1;
 	} else {
 	} else {
 		tmp &= ~BIT(SELF_POWERED_STATUS);
 		tmp &= ~BIT(SELF_POWERED_STATUS);
-		dev->selfpowered = 0;
+		_gadget->is_selfpowered = 0;
 	}
 	}
 	writel(tmp, &dev->usb->usbctl);
 	writel(tmp, &dev->usb->usbctl);
 	spin_unlock_irqrestore(&dev->lock, flags);
 	spin_unlock_irqrestore(&dev->lock, flags);
@@ -1604,14 +1457,11 @@ static ssize_t registers_show(struct device *_dev,
 
 
 	/* Main Control Registers */
 	/* Main Control Registers */
 	t = scnprintf(next, size, "%s version " DRIVER_VERSION
 	t = scnprintf(next, size, "%s version " DRIVER_VERSION
-			", chiprev %04x, dma %s\n\n"
+			", chiprev %04x\n\n"
 			"devinit %03x fifoctl %08x gadget '%s'\n"
 			"devinit %03x fifoctl %08x gadget '%s'\n"
 			"pci irqenb0 %02x irqenb1 %08x "
 			"pci irqenb0 %02x irqenb1 %08x "
 			"irqstat0 %04x irqstat1 %08x\n",
 			"irqstat0 %04x irqstat1 %08x\n",
 			driver_name, dev->chiprev,
 			driver_name, dev->chiprev,
-			use_dma
-				? (use_dma_chaining ? "chaining" : "enabled")
-				: "disabled",
 			readl(&dev->regs->devinit),
 			readl(&dev->regs->devinit),
 			readl(&dev->regs->fifoctl),
 			readl(&dev->regs->fifoctl),
 			s,
 			s,
@@ -1913,76 +1763,73 @@ static void defect7374_disable_data_eps(struct net2280 *dev)
 static void defect7374_enable_data_eps_zero(struct net2280 *dev)
 static void defect7374_enable_data_eps_zero(struct net2280 *dev)
 {
 {
 	u32 tmp = 0, tmp_reg;
 	u32 tmp = 0, tmp_reg;
-	u32 fsmvalue, scratch;
+	u32 scratch;
 	int i;
 	int i;
 	unsigned char ep_sel;
 	unsigned char ep_sel;
 
 
 	scratch = get_idx_reg(dev->regs, SCRATCH);
 	scratch = get_idx_reg(dev->regs, SCRATCH);
-	fsmvalue = scratch & (0xf << DEFECT7374_FSM_FIELD);
+
+	WARN_ON((scratch & (0xf << DEFECT7374_FSM_FIELD))
+		== DEFECT7374_FSM_SS_CONTROL_READ);
+
 	scratch &= ~(0xf << DEFECT7374_FSM_FIELD);
 	scratch &= ~(0xf << DEFECT7374_FSM_FIELD);
 
 
-	/*See if firmware needs to set up for workaround*/
-	if (fsmvalue != DEFECT7374_FSM_SS_CONTROL_READ) {
-		ep_warn(dev, "Operate Defect 7374 workaround soft this time");
-		ep_warn(dev, "It will operate on cold-reboot and SS connect");
-
-		/*GPEPs:*/
-		tmp = ((0 << ENDPOINT_NUMBER) | BIT(ENDPOINT_DIRECTION) |
-		       (2 << OUT_ENDPOINT_TYPE) | (2 << IN_ENDPOINT_TYPE) |
-		       ((dev->enhanced_mode) ?
-		       BIT(OUT_ENDPOINT_ENABLE) : BIT(ENDPOINT_ENABLE)) |
-		       BIT(IN_ENDPOINT_ENABLE));
-
-		for (i = 1; i < 5; i++)
-			writel(tmp, &dev->ep[i].cfg->ep_cfg);
-
-		/* CSRIN, PCIIN, STATIN, RCIN*/
-		tmp = ((0 << ENDPOINT_NUMBER) | BIT(ENDPOINT_ENABLE));
-		writel(tmp, &dev->dep[1].dep_cfg);
-		writel(tmp, &dev->dep[3].dep_cfg);
-		writel(tmp, &dev->dep[4].dep_cfg);
-		writel(tmp, &dev->dep[5].dep_cfg);
-
-		/*Implemented for development and debug.
-		 * Can be refined/tuned later.*/
-		for (ep_sel = 0; ep_sel <= 21; ep_sel++) {
-			/* Select an endpoint for subsequent operations: */
-			tmp_reg = readl(&dev->plregs->pl_ep_ctrl);
-			writel(((tmp_reg & ~0x1f) | ep_sel),
-			       &dev->plregs->pl_ep_ctrl);
-
-			if (ep_sel == 1) {
-				tmp =
-				    (readl(&dev->plregs->pl_ep_ctrl) |
-				     BIT(CLEAR_ACK_ERROR_CODE) | 0);
-				writel(tmp, &dev->plregs->pl_ep_ctrl);
-				continue;
-			}
+	ep_warn(dev, "Operate Defect 7374 workaround soft this time");
+	ep_warn(dev, "It will operate on cold-reboot and SS connect");
 
 
-			if (ep_sel == 0 || (ep_sel > 9 && ep_sel < 14) ||
-					ep_sel == 18  || ep_sel == 20)
-				continue;
+	/*GPEPs:*/
+	tmp = ((0 << ENDPOINT_NUMBER) | BIT(ENDPOINT_DIRECTION) |
+			(2 << OUT_ENDPOINT_TYPE) | (2 << IN_ENDPOINT_TYPE) |
+			((dev->enhanced_mode) ?
+			 BIT(OUT_ENDPOINT_ENABLE) : BIT(ENDPOINT_ENABLE)) |
+			BIT(IN_ENDPOINT_ENABLE));
 
 
-			tmp = (readl(&dev->plregs->pl_ep_cfg_4) |
-				 BIT(NON_CTRL_IN_TOLERATE_BAD_DIR) | 0);
-			writel(tmp, &dev->plregs->pl_ep_cfg_4);
+	for (i = 1; i < 5; i++)
+		writel(tmp, &dev->ep[i].cfg->ep_cfg);
 
 
-			tmp = readl(&dev->plregs->pl_ep_ctrl) &
-				~BIT(EP_INITIALIZED);
-			writel(tmp, &dev->plregs->pl_ep_ctrl);
+	/* CSRIN, PCIIN, STATIN, RCIN*/
+	tmp = ((0 << ENDPOINT_NUMBER) | BIT(ENDPOINT_ENABLE));
+	writel(tmp, &dev->dep[1].dep_cfg);
+	writel(tmp, &dev->dep[3].dep_cfg);
+	writel(tmp, &dev->dep[4].dep_cfg);
+	writel(tmp, &dev->dep[5].dep_cfg);
+
+	/*Implemented for development and debug.
+	 * Can be refined/tuned later.*/
+	for (ep_sel = 0; ep_sel <= 21; ep_sel++) {
+		/* Select an endpoint for subsequent operations: */
+		tmp_reg = readl(&dev->plregs->pl_ep_ctrl);
+		writel(((tmp_reg & ~0x1f) | ep_sel),
+				&dev->plregs->pl_ep_ctrl);
 
 
+		if (ep_sel == 1) {
+			tmp =
+				(readl(&dev->plregs->pl_ep_ctrl) |
+				 BIT(CLEAR_ACK_ERROR_CODE) | 0);
+			writel(tmp, &dev->plregs->pl_ep_ctrl);
+			continue;
 		}
 		}
 
 
-		/* Set FSM to focus on the first Control Read:
-		 * - Tip: Connection speed is known upon the first
-		 * setup request.*/
-		scratch |= DEFECT7374_FSM_WAITING_FOR_CONTROL_READ;
-		set_idx_reg(dev->regs, SCRATCH, scratch);
+		if (ep_sel == 0 || (ep_sel > 9 && ep_sel < 14) ||
+				ep_sel == 18  || ep_sel == 20)
+			continue;
+
+		tmp = (readl(&dev->plregs->pl_ep_cfg_4) |
+				BIT(NON_CTRL_IN_TOLERATE_BAD_DIR) | 0);
+		writel(tmp, &dev->plregs->pl_ep_cfg_4);
+
+		tmp = readl(&dev->plregs->pl_ep_ctrl) &
+			~BIT(EP_INITIALIZED);
+		writel(tmp, &dev->plregs->pl_ep_ctrl);
 
 
-	} else{
-		ep_warn(dev, "Defect 7374 workaround soft will NOT operate");
-		ep_warn(dev, "It will operate on cold-reboot and SS connect");
 	}
 	}
+
+	/* Set FSM to focus on the first Control Read:
+	 * - Tip: Connection speed is known upon the first
+	 * setup request.*/
+	scratch |= DEFECT7374_FSM_WAITING_FOR_CONTROL_READ;
+	set_idx_reg(dev->regs, SCRATCH, scratch);
+
 }
 }
 
 
 /* keeping it simple:
 /* keeping it simple:
@@ -2033,21 +1880,13 @@ static void usb_reset_228x(struct net2280 *dev)
 static void usb_reset_338x(struct net2280 *dev)
 static void usb_reset_338x(struct net2280 *dev)
 {
 {
 	u32 tmp;
 	u32 tmp;
-	u32 fsmvalue;
 
 
 	dev->gadget.speed = USB_SPEED_UNKNOWN;
 	dev->gadget.speed = USB_SPEED_UNKNOWN;
 	(void)readl(&dev->usb->usbctl);
 	(void)readl(&dev->usb->usbctl);
 
 
 	net2280_led_init(dev);
 	net2280_led_init(dev);
 
 
-	fsmvalue = get_idx_reg(dev->regs, SCRATCH) &
-			(0xf << DEFECT7374_FSM_FIELD);
-
-	/* See if firmware needs to set up for workaround: */
-	if (fsmvalue != DEFECT7374_FSM_SS_CONTROL_READ) {
-		ep_info(dev, "%s: Defect 7374 FsmValue 0x%08x\n", __func__,
-		     fsmvalue);
-	} else {
+	if (dev->bug7734_patched) {
 		/* disable automatic responses, and irqs */
 		/* disable automatic responses, and irqs */
 		writel(0, &dev->usb->stdrsp);
 		writel(0, &dev->usb->stdrsp);
 		writel(0, &dev->regs->pciirqenb0);
 		writel(0, &dev->regs->pciirqenb0);
@@ -2064,7 +1903,7 @@ static void usb_reset_338x(struct net2280 *dev)
 
 
 	writel(~0, &dev->regs->irqstat0), writel(~0, &dev->regs->irqstat1);
 	writel(~0, &dev->regs->irqstat0), writel(~0, &dev->regs->irqstat1);
 
 
-	if (fsmvalue == DEFECT7374_FSM_SS_CONTROL_READ) {
+	if (dev->bug7734_patched) {
 		/* reset, and enable pci */
 		/* reset, and enable pci */
 		tmp = readl(&dev->regs->devinit) |
 		tmp = readl(&dev->regs->devinit) |
 		    BIT(PCI_ENABLE) |
 		    BIT(PCI_ENABLE) |
@@ -2093,10 +1932,6 @@ static void usb_reset(struct net2280 *dev)
 static void usb_reinit_228x(struct net2280 *dev)
 static void usb_reinit_228x(struct net2280 *dev)
 {
 {
 	u32	tmp;
 	u32	tmp;
-	int	init_dma;
-
-	/* use_dma changes are ignored till next device re-init */
-	init_dma = use_dma;
 
 
 	/* basic endpoint init */
 	/* basic endpoint init */
 	for (tmp = 0; tmp < 7; tmp++) {
 	for (tmp = 0; tmp < 7; tmp++) {
@@ -2108,8 +1943,7 @@ static void usb_reinit_228x(struct net2280 *dev)
 
 
 		if (tmp > 0 && tmp <= 4) {
 		if (tmp > 0 && tmp <= 4) {
 			ep->fifo_size = 1024;
 			ep->fifo_size = 1024;
-			if (init_dma)
-				ep->dma = &dev->dma[tmp - 1];
+			ep->dma = &dev->dma[tmp - 1];
 		} else
 		} else
 			ep->fifo_size = 64;
 			ep->fifo_size = 64;
 		ep->regs = &dev->epregs[tmp];
 		ep->regs = &dev->epregs[tmp];
@@ -2133,17 +1967,12 @@ static void usb_reinit_228x(struct net2280 *dev)
 
 
 static void usb_reinit_338x(struct net2280 *dev)
 static void usb_reinit_338x(struct net2280 *dev)
 {
 {
-	int init_dma;
 	int i;
 	int i;
 	u32 tmp, val;
 	u32 tmp, val;
-	u32 fsmvalue;
 	static const u32 ne[9] = { 0, 1, 2, 3, 4, 1, 2, 3, 4 };
 	static const u32 ne[9] = { 0, 1, 2, 3, 4, 1, 2, 3, 4 };
 	static const u32 ep_reg_addr[9] = { 0x00, 0xC0, 0x00, 0xC0, 0x00,
 	static const u32 ep_reg_addr[9] = { 0x00, 0xC0, 0x00, 0xC0, 0x00,
 						0x00, 0xC0, 0x00, 0xC0 };
 						0x00, 0xC0, 0x00, 0xC0 };
 
 
-	/* use_dma changes are ignored till next device re-init */
-	init_dma = use_dma;
-
 	/* basic endpoint init */
 	/* basic endpoint init */
 	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];
@@ -2152,7 +1981,7 @@ static void usb_reinit_338x(struct net2280 *dev)
 		ep->dev = dev;
 		ep->dev = dev;
 		ep->num = i;
 		ep->num = i;
 
 
-		if (i > 0 && i <= 4 && init_dma)
+		if (i > 0 && i <= 4)
 			ep->dma = &dev->dma[i - 1];
 			ep->dma = &dev->dma[i - 1];
 
 
 		if (dev->enhanced_mode) {
 		if (dev->enhanced_mode) {
@@ -2177,14 +2006,7 @@ static void usb_reinit_338x(struct net2280 *dev)
 	dev->ep[0].stopped = 0;
 	dev->ep[0].stopped = 0;
 
 
 	/* Link layer set up */
 	/* Link layer set up */
-	fsmvalue = get_idx_reg(dev->regs, SCRATCH) &
-				(0xf << DEFECT7374_FSM_FIELD);
-
-	/* See if driver needs to set up for workaround: */
-	if (fsmvalue != DEFECT7374_FSM_SS_CONTROL_READ)
-		ep_info(dev, "%s: Defect 7374 FsmValue %08x\n",
-						__func__, fsmvalue);
-	else {
+	if (dev->bug7734_patched) {
 		tmp = readl(&dev->usb_ext->usbctl2) &
 		tmp = readl(&dev->usb_ext->usbctl2) &
 		    ~(BIT(U1_ENABLE) | BIT(U2_ENABLE) | BIT(LTM_ENABLE));
 		    ~(BIT(U1_ENABLE) | BIT(U2_ENABLE) | BIT(LTM_ENABLE));
 		writel(tmp, &dev->usb_ext->usbctl2);
 		writel(tmp, &dev->usb_ext->usbctl2);
@@ -2291,15 +2113,8 @@ static void ep0_start_228x(struct net2280 *dev)
 
 
 static void ep0_start_338x(struct net2280 *dev)
 static void ep0_start_338x(struct net2280 *dev)
 {
 {
-	u32 fsmvalue;
-
-	fsmvalue = get_idx_reg(dev->regs, SCRATCH) &
-			(0xf << DEFECT7374_FSM_FIELD);
 
 
-	if (fsmvalue != DEFECT7374_FSM_SS_CONTROL_READ)
-		ep_info(dev, "%s: Defect 7374 FsmValue %08x\n", __func__,
-		     fsmvalue);
-	else
+	if (dev->bug7734_patched)
 		writel(BIT(CLEAR_NAK_OUT_PACKETS_MODE) |
 		writel(BIT(CLEAR_NAK_OUT_PACKETS_MODE) |
 		       BIT(SET_EP_HIDE_STATUS_PHASE),
 		       BIT(SET_EP_HIDE_STATUS_PHASE),
 		       &dev->epregs[0].ep_rsp);
 		       &dev->epregs[0].ep_rsp);
@@ -2382,16 +2197,12 @@ static int net2280_start(struct usb_gadget *_gadget,
 	if (retval)
 	if (retval)
 		goto err_func;
 		goto err_func;
 
 
-	/* Enable force-full-speed testing mode, if desired */
-	if (full_speed && (dev->quirks & PLX_LEGACY))
-		writel(BIT(FORCE_FULL_SPEED_MODE), &dev->usb->xcvrdiag);
-
-	/* ... then enable host detection and ep0; and we're ready
+	/* enable host detection and ep0; and we're ready
 	 * for set_configuration as well as eventual disconnect.
 	 * for set_configuration as well as eventual disconnect.
 	 */
 	 */
 	net2280_led_active(dev, 1);
 	net2280_led_active(dev, 1);
 
 
-	if (dev->quirks & PLX_SUPERSPEED)
+	if ((dev->quirks & PLX_SUPERSPEED) && !dev->bug7734_patched)
 		defect7374_enable_data_eps_zero(dev);
 		defect7374_enable_data_eps_zero(dev);
 
 
 	ep0_start(dev);
 	ep0_start(dev);
@@ -2444,10 +2255,6 @@ static int net2280_stop(struct usb_gadget *_gadget)
 
 
 	net2280_led_active(dev, 0);
 	net2280_led_active(dev, 0);
 
 
-	/* Disable full-speed test mode */
-	if (dev->quirks & PLX_LEGACY)
-		writel(0, &dev->usb->xcvrdiag);
-
 	device_remove_file(&dev->pdev->dev, &dev_attr_function);
 	device_remove_file(&dev->pdev->dev, &dev_attr_function);
 	device_remove_file(&dev->pdev->dev, &dev_attr_queues);
 	device_remove_file(&dev->pdev->dev, &dev_attr_queues);
 
 
@@ -2478,10 +2285,10 @@ static void handle_ep_small(struct net2280_ep *ep)
 	/* ack all, and handle what we care about */
 	/* ack all, and handle what we care about */
 	t = readl(&ep->regs->ep_stat);
 	t = readl(&ep->regs->ep_stat);
 	ep->irqs++;
 	ep->irqs++;
-#if 0
+
 	ep_vdbg(ep->dev, "%s ack ep_stat %08x, req %p\n",
 	ep_vdbg(ep->dev, "%s ack ep_stat %08x, req %p\n",
-			ep->ep.name, t, req ? &req->req : 0);
-#endif
+			ep->ep.name, t, req ? &req->req : NULL);
+
 	if (!ep->is_in || (ep->dev->quirks & PLX_2280))
 	if (!ep->is_in || (ep->dev->quirks & PLX_2280))
 		writel(t & ~BIT(NAK_OUT_PACKETS), &ep->regs->ep_stat);
 		writel(t & ~BIT(NAK_OUT_PACKETS), &ep->regs->ep_stat);
 	else
 	else
@@ -2717,6 +2524,7 @@ static void defect7374_workaround(struct net2280 *dev, struct usb_ctrlrequest r)
 		 * run after the next USB connection.
 		 * run after the next USB connection.
 		 */
 		 */
 		scratch |= DEFECT7374_FSM_NON_SS_CONTROL_READ;
 		scratch |= DEFECT7374_FSM_NON_SS_CONTROL_READ;
+		dev->bug7734_patched = 1;
 		goto restore_data_eps;
 		goto restore_data_eps;
 	}
 	}
 
 
@@ -2730,6 +2538,7 @@ static void defect7374_workaround(struct net2280 *dev, struct usb_ctrlrequest r)
 		if ((state >= (ACK_GOOD_NORMAL << STATE)) &&
 		if ((state >= (ACK_GOOD_NORMAL << STATE)) &&
 			(state <= (ACK_GOOD_MORE_ACKS_TO_COME << STATE))) {
 			(state <= (ACK_GOOD_MORE_ACKS_TO_COME << STATE))) {
 			scratch |= DEFECT7374_FSM_SS_CONTROL_READ;
 			scratch |= DEFECT7374_FSM_SS_CONTROL_READ;
+			dev->bug7734_patched = 1;
 			break;
 			break;
 		}
 		}
 
 
@@ -2766,80 +2575,19 @@ restore_data_eps:
 	return;
 	return;
 }
 }
 
 
-static void ep_stall(struct net2280_ep *ep, int stall)
+static void ep_clear_seqnum(struct net2280_ep *ep)
 {
 {
 	struct net2280 *dev = ep->dev;
 	struct net2280 *dev = ep->dev;
 	u32 val;
 	u32 val;
 	static const u32 ep_pl[9] = { 0, 3, 4, 7, 8, 2, 5, 6, 9 };
 	static const u32 ep_pl[9] = { 0, 3, 4, 7, 8, 2, 5, 6, 9 };
 
 
-	if (stall) {
-		writel(BIT(SET_ENDPOINT_HALT) |
-		       /* BIT(SET_NAK_PACKETS) | */
-		       BIT(CLEAR_CONTROL_STATUS_PHASE_HANDSHAKE),
-		       &ep->regs->ep_rsp);
-		ep->is_halt = 1;
-	} else {
-		if (dev->gadget.speed == USB_SPEED_SUPER) {
-			/*
-			 * Workaround for SS SeqNum not cleared via
-			 * Endpoint Halt (Clear) bit. select endpoint
-			 */
-			val = readl(&dev->plregs->pl_ep_ctrl);
-			val = (val & ~0x1f) | ep_pl[ep->num];
-			writel(val, &dev->plregs->pl_ep_ctrl);
-
-			val |= BIT(SEQUENCE_NUMBER_RESET);
-			writel(val, &dev->plregs->pl_ep_ctrl);
-		}
-		val = readl(&ep->regs->ep_rsp);
-		val |= BIT(CLEAR_ENDPOINT_HALT) |
-			BIT(CLEAR_ENDPOINT_TOGGLE);
-		writel(val,
-		       /* | BIT(CLEAR_NAK_PACKETS),*/
-		       &ep->regs->ep_rsp);
-		ep->is_halt = 0;
-		val = readl(&ep->regs->ep_rsp);
-	}
-}
-
-static void ep_stdrsp(struct net2280_ep *ep, int value, int wedged)
-{
-	/* set/clear, then synch memory views with the device */
-	if (value) {
-		ep->stopped = 1;
-		if (ep->num == 0)
-			ep->dev->protocol_stall = 1;
-		else {
-			if (ep->dma)
-				ep_stop_dma(ep);
-			ep_stall(ep, true);
-		}
-
-		if (wedged)
-			ep->wedged = 1;
-	} else {
-		ep->stopped = 0;
-		ep->wedged = 0;
-
-		ep_stall(ep, false);
+	val = readl(&dev->plregs->pl_ep_ctrl) & ~0x1f;
+	val |= ep_pl[ep->num];
+	writel(val, &dev->plregs->pl_ep_ctrl);
+	val |= BIT(SEQUENCE_NUMBER_RESET);
+	writel(val, &dev->plregs->pl_ep_ctrl);
 
 
-		/* Flush the queue */
-		if (!list_empty(&ep->queue)) {
-			struct net2280_request *req =
-			    list_entry(ep->queue.next, struct net2280_request,
-				       queue);
-			if (ep->dma)
-				resume_dma(ep);
-			else {
-				if (ep->is_in)
-					write_fifo(ep, &req->req);
-				else {
-					if (read_fifo(ep, req))
-						done(ep, req, 0);
-				}
-			}
-		}
-	}
+	return;
 }
 }
 
 
 static void handle_stat0_irqs_superspeed(struct net2280 *dev,
 static void handle_stat0_irqs_superspeed(struct net2280 *dev,
@@ -2863,7 +2611,7 @@ static void handle_stat0_irqs_superspeed(struct net2280 *dev,
 		switch (r.bRequestType) {
 		switch (r.bRequestType) {
 		case (USB_DIR_IN | USB_TYPE_STANDARD | USB_RECIP_DEVICE):
 		case (USB_DIR_IN | USB_TYPE_STANDARD | USB_RECIP_DEVICE):
 			status = dev->wakeup_enable ? 0x02 : 0x00;
 			status = dev->wakeup_enable ? 0x02 : 0x00;
-			if (dev->selfpowered)
+			if (dev->gadget.is_selfpowered)
 				status |= BIT(0);
 				status |= BIT(0);
 			status |= (dev->u1_enable << 2 | dev->u2_enable << 3 |
 			status |= (dev->u1_enable << 2 | dev->u2_enable << 3 |
 							dev->ltm_enable << 4);
 							dev->ltm_enable << 4);
@@ -2940,7 +2688,12 @@ static void handle_stat0_irqs_superspeed(struct net2280 *dev,
 			if (w_value != USB_ENDPOINT_HALT)
 			if (w_value != USB_ENDPOINT_HALT)
 				goto do_stall3;
 				goto do_stall3;
 			ep_vdbg(dev, "%s clear halt\n", e->ep.name);
 			ep_vdbg(dev, "%s clear halt\n", e->ep.name);
-			ep_stall(e, false);
+			/*
+			 * Workaround for SS SeqNum not cleared via
+			 * Endpoint Halt (Clear) bit. select endpoint
+			 */
+			ep_clear_seqnum(e);
+			clear_halt(e);
 			if (!list_empty(&e->queue) && e->td_dma)
 			if (!list_empty(&e->queue) && e->td_dma)
 				restart_dma(e);
 				restart_dma(e);
 			allow_status(ep);
 			allow_status(ep);
@@ -2998,7 +2751,14 @@ static void handle_stat0_irqs_superspeed(struct net2280 *dev,
 			e = get_ep_by_addr(dev,	w_index);
 			e = get_ep_by_addr(dev,	w_index);
 			if (!e || (w_value != USB_ENDPOINT_HALT))
 			if (!e || (w_value != USB_ENDPOINT_HALT))
 				goto do_stall3;
 				goto do_stall3;
-			ep_stdrsp(e, true, false);
+			ep->stopped = 1;
+			if (ep->num == 0)
+				ep->dev->protocol_stall = 1;
+			else {
+				if (ep->dma)
+					abort_dma(ep);
+				set_halt(ep);
+			}
 			allow_status_338x(ep);
 			allow_status_338x(ep);
 			break;
 			break;
 
 
@@ -3026,7 +2786,7 @@ do_stall3:
 				r.bRequestType, r.bRequest, tmp);
 				r.bRequestType, r.bRequest, tmp);
 		dev->protocol_stall = 1;
 		dev->protocol_stall = 1;
 		/* TD 9.9 Halt Endpoint test. TD 9.22 Set feature test */
 		/* TD 9.9 Halt Endpoint test. TD 9.22 Set feature test */
-		ep_stall(ep, true);
+		set_halt(ep);
 	}
 	}
 
 
 next_endpoints3:
 next_endpoints3:
@@ -3091,9 +2851,7 @@ static void handle_stat0_irqs(struct net2280 *dev, u32 stat)
 		}
 		}
 		ep->stopped = 0;
 		ep->stopped = 0;
 		dev->protocol_stall = 0;
 		dev->protocol_stall = 0;
-		if (dev->quirks & PLX_SUPERSPEED)
-			ep->is_halt = 0;
-		else{
+		if (!(dev->quirks & PLX_SUPERSPEED)) {
 			if (ep->dev->quirks & PLX_2280)
 			if (ep->dev->quirks & PLX_2280)
 				tmp = BIT(FIFO_OVERFLOW) |
 				tmp = BIT(FIFO_OVERFLOW) |
 				    BIT(FIFO_UNDERFLOW);
 				    BIT(FIFO_UNDERFLOW);
@@ -3120,7 +2878,7 @@ static void handle_stat0_irqs(struct net2280 *dev, u32 stat)
 		cpu_to_le32s(&u.raw[0]);
 		cpu_to_le32s(&u.raw[0]);
 		cpu_to_le32s(&u.raw[1]);
 		cpu_to_le32s(&u.raw[1]);
 
 
-		if (dev->quirks & PLX_SUPERSPEED)
+		if ((dev->quirks & PLX_SUPERSPEED) && !dev->bug7734_patched)
 			defect7374_workaround(dev, u.r);
 			defect7374_workaround(dev, u.r);
 
 
 		tmp = 0;
 		tmp = 0;
@@ -3423,17 +3181,12 @@ static void handle_stat1_irqs(struct net2280 *dev, u32 stat)
 				continue;
 				continue;
 		}
 		}
 
 
-		/* chaining should stop on abort, short OUT from fifo,
-		 * or (stat0 codepath) short OUT transfer.
-		 */
-		if (!use_dma_chaining) {
-			if (!(tmp & BIT(DMA_TRANSACTION_DONE_INTERRUPT))) {
-				ep_dbg(ep->dev, "%s no xact done? %08x\n",
-					ep->ep.name, tmp);
-				continue;
-			}
-			stop_dma(ep->dma);
+		if (!(tmp & BIT(DMA_TRANSACTION_DONE_INTERRUPT))) {
+			ep_dbg(ep->dev, "%s no xact done? %08x\n",
+				ep->ep.name, tmp);
+			continue;
 		}
 		}
+		stop_dma(ep->dma);
 
 
 		/* OUT transfers terminate when the data from the
 		/* OUT transfers terminate when the data from the
 		 * host is in our memory.  Process whatever's done.
 		 * host is in our memory.  Process whatever's done.
@@ -3448,30 +3201,9 @@ static void handle_stat1_irqs(struct net2280 *dev, u32 stat)
 		scan_dma_completions(ep);
 		scan_dma_completions(ep);
 
 
 		/* disable dma on inactive queues; else maybe restart */
 		/* disable dma on inactive queues; else maybe restart */
-		if (list_empty(&ep->queue)) {
-			if (use_dma_chaining)
-				stop_dma(ep->dma);
-		} else {
+		if (!list_empty(&ep->queue)) {
 			tmp = readl(&dma->dmactl);
 			tmp = readl(&dma->dmactl);
-			if (!use_dma_chaining || (tmp & BIT(DMA_ENABLE)) == 0)
-				restart_dma(ep);
-			else if (ep->is_in && use_dma_chaining) {
-				struct net2280_request	*req;
-				__le32			dmacount;
-
-				/* the descriptor at the head of the chain
-				 * may still have VALID_BIT clear; that's
-				 * used to trigger changing DMA_FIFO_VALIDATE
-				 * (affects automagic zlp writes).
-				 */
-				req = list_entry(ep->queue.next,
-						struct net2280_request, queue);
-				dmacount = req->td->dmacount;
-				dmacount &= cpu_to_le32(BIT(VALID_BIT) |
-						DMA_BYTE_COUNT_MASK);
-				if (dmacount && (dmacount & valid_bit) == 0)
-					restart_dma(ep);
-			}
+			restart_dma(ep);
 		}
 		}
 		ep->irqs++;
 		ep->irqs++;
 	}
 	}
@@ -3556,7 +3288,7 @@ static void net2280_remove(struct pci_dev *pdev)
 	}
 	}
 	if (dev->got_irq)
 	if (dev->got_irq)
 		free_irq(pdev->irq, dev);
 		free_irq(pdev->irq, dev);
-	if (use_msi && dev->quirks & PLX_SUPERSPEED)
+	if (dev->quirks & PLX_SUPERSPEED)
 		pci_disable_msi(pdev);
 		pci_disable_msi(pdev);
 	if (dev->regs)
 	if (dev->regs)
 		iounmap(dev->regs);
 		iounmap(dev->regs);
@@ -3581,9 +3313,6 @@ static int net2280_probe(struct pci_dev *pdev, const struct pci_device_id *id)
 	void			__iomem *base = NULL;
 	void			__iomem *base = NULL;
 	int			retval, i;
 	int			retval, i;
 
 
-	if (!use_dma)
-		use_dma_chaining = 0;
-
 	/* alloc, and start init */
 	/* alloc, and start init */
 	dev = kzalloc(sizeof(*dev), GFP_KERNEL);
 	dev = kzalloc(sizeof(*dev), GFP_KERNEL);
 	if (dev == NULL) {
 	if (dev == NULL) {
@@ -3663,9 +3392,12 @@ static int net2280_probe(struct pci_dev *pdev, const struct pci_device_id *id)
 		fsmvalue = get_idx_reg(dev->regs, SCRATCH) &
 		fsmvalue = get_idx_reg(dev->regs, SCRATCH) &
 					(0xf << DEFECT7374_FSM_FIELD);
 					(0xf << DEFECT7374_FSM_FIELD);
 		/* See if firmware needs to set up for workaround: */
 		/* See if firmware needs to set up for workaround: */
-		if (fsmvalue == DEFECT7374_FSM_SS_CONTROL_READ)
+		if (fsmvalue == DEFECT7374_FSM_SS_CONTROL_READ) {
+			dev->bug7734_patched = 1;
 			writel(0, &dev->usb->usbctl);
 			writel(0, &dev->usb->usbctl);
-	} else{
+		} else
+			dev->bug7734_patched = 0;
+	} else {
 		dev->enhanced_mode = 0;
 		dev->enhanced_mode = 0;
 		dev->n_ep = 7;
 		dev->n_ep = 7;
 		/* put into initial config, link up all endpoints */
 		/* put into initial config, link up all endpoints */
@@ -3682,7 +3414,7 @@ static int net2280_probe(struct pci_dev *pdev, const struct pci_device_id *id)
 		goto done;
 		goto done;
 	}
 	}
 
 
-	if (use_msi && (dev->quirks & PLX_SUPERSPEED))
+	if (dev->quirks & PLX_SUPERSPEED)
 		if (pci_enable_msi(pdev))
 		if (pci_enable_msi(pdev))
 			ep_err(dev, "Failed to enable MSI mode\n");
 			ep_err(dev, "Failed to enable MSI mode\n");
 
 
@@ -3741,9 +3473,7 @@ static int net2280_probe(struct pci_dev *pdev, const struct pci_device_id *id)
 	ep_info(dev, "%s\n", driver_desc);
 	ep_info(dev, "%s\n", driver_desc);
 	ep_info(dev, "irq %d, pci mem %p, chip rev %04x\n",
 	ep_info(dev, "irq %d, pci mem %p, chip rev %04x\n",
 			pdev->irq, base, dev->chiprev);
 			pdev->irq, base, dev->chiprev);
-	ep_info(dev, "version: " DRIVER_VERSION "; dma %s %s\n",
-		use_dma	? (use_dma_chaining ? "chaining" : "enabled")
-			: "disabled",
+	ep_info(dev, "version: " DRIVER_VERSION "; %s\n",
 		dev->enhanced_mode ? "enhanced mode" : "legacy mode");
 		dev->enhanced_mode ? "enhanced mode" : "legacy mode");
 	retval = device_create_file(&pdev->dev, &dev_attr_registers);
 	retval = device_create_file(&pdev->dev, &dev_attr_registers);
 	if (retval)
 	if (retval)
@@ -3776,9 +3506,6 @@ static void net2280_shutdown(struct pci_dev *pdev)
 	/* disable the pullup so the host will think we're gone */
 	/* disable the pullup so the host will think we're gone */
 	writel(0, &dev->usb->usbctl);
 	writel(0, &dev->usb->usbctl);
 
 
-	/* Disable full-speed test mode */
-	if (dev->quirks & PLX_LEGACY)
-		writel(0, &dev->usb->xcvrdiag);
 }
 }
 
 
 
 

+ 3 - 21
drivers/usb/gadget/udc/net2280.h

@@ -100,7 +100,6 @@ struct net2280_ep {
 	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;
-	unsigned is_halt:1, dma_started:1;
 
 
 	/* analogous to a host-side qh */
 	/* analogous to a host-side qh */
 	struct list_head			queue;
 	struct list_head			queue;
@@ -126,7 +125,7 @@ static inline void allow_status(struct net2280_ep *ep)
 	ep->stopped = 1;
 	ep->stopped = 1;
 }
 }
 
 
-static void allow_status_338x(struct net2280_ep *ep)
+static inline void allow_status_338x(struct net2280_ep *ep)
 {
 {
 	/*
 	/*
 	 * Control Status Phase Handshake was set by the chip when the setup
 	 * Control Status Phase Handshake was set by the chip when the setup
@@ -165,8 +164,8 @@ struct net2280 {
 					u2_enable:1,
 					u2_enable:1,
 					ltm_enable:1,
 					ltm_enable:1,
 					wakeup_enable:1,
 					wakeup_enable:1,
-					selfpowered:1,
-					addressed_state:1;
+					addressed_state:1,
+					bug7734_patched:1;
 	u16				chiprev;
 	u16				chiprev;
 	int enhanced_mode;
 	int enhanced_mode;
 	int n_ep;
 	int n_ep;
@@ -356,23 +355,6 @@ static inline void start_out_naking(struct net2280_ep *ep)
 	readl(&ep->regs->ep_rsp);
 	readl(&ep->regs->ep_rsp);
 }
 }
 
 
-#ifdef DEBUG
-static inline void assert_out_naking(struct net2280_ep *ep, const char *where)
-{
-	u32	tmp = readl(&ep->regs->ep_stat);
-
-	if ((tmp & BIT(NAK_OUT_PACKETS)) == 0) {
-		ep_dbg(ep->dev, "%s %s %08x !NAK\n",
-				ep->ep.name, where, tmp);
-		writel(BIT(SET_NAK_OUT_PACKETS),
-			&ep->regs->ep_rsp);
-	}
-}
-#define ASSERT_OUT_NAKING(ep) assert_out_naking(ep, __func__)
-#else
-#define ASSERT_OUT_NAKING(ep) do {} while (0)
-#endif
-
 static inline void stop_out_naking(struct net2280_ep *ep)
 static inline void stop_out_naking(struct net2280_ep *ep)
 {
 {
 	u32	tmp;
 	u32	tmp;

+ 1 - 0
drivers/usb/gadget/udc/omap_udc.c

@@ -1171,6 +1171,7 @@ omap_set_selfpowered(struct usb_gadget *gadget, int is_selfpowered)
 	unsigned long	flags;
 	unsigned long	flags;
 	u16		syscon1;
 	u16		syscon1;
 
 
+	gadget->is_selfpowered = (is_selfpowered != 0);
 	udc = container_of(gadget, struct omap_udc, gadget);
 	udc = container_of(gadget, struct omap_udc, gadget);
 	spin_lock_irqsave(&udc->lock, flags);
 	spin_lock_irqsave(&udc->lock, flags);
 	syscon1 = omap_readw(UDC_SYSCON1);
 	syscon1 = omap_readw(UDC_SYSCON1);

+ 1 - 0
drivers/usb/gadget/udc/pch_udc.c

@@ -1161,6 +1161,7 @@ static int pch_udc_pcd_selfpowered(struct usb_gadget *gadget, int value)
 
 
 	if (!gadget)
 	if (!gadget)
 		return -EINVAL;
 		return -EINVAL;
+	gadget->is_selfpowered = (value != 0);
 	dev = container_of(gadget, struct pch_udc_dev, gadget);
 	dev = container_of(gadget, struct pch_udc_dev, gadget);
 	if (value)
 	if (value)
 		pch_udc_set_selfpowered(dev);
 		pch_udc_set_selfpowered(dev);

+ 0 - 2
drivers/usb/gadget/udc/pxa25x_udc.c

@@ -1272,7 +1272,6 @@ static int pxa25x_udc_start(struct usb_gadget *g,
 			goto bind_fail;
 			goto bind_fail;
 	}
 	}
 
 
-	pullup(dev);
 	dump_state(dev);
 	dump_state(dev);
 	return 0;
 	return 0;
 bind_fail:
 bind_fail:
@@ -1339,7 +1338,6 @@ static int pxa25x_udc_stop(struct usb_gadget*g)
 
 
 	local_irq_disable();
 	local_irq_disable();
 	dev->pullup = 0;
 	dev->pullup = 0;
-	pullup(dev);
 	stop_activity(dev, NULL);
 	stop_activity(dev, NULL);
 	local_irq_enable();
 	local_irq_enable();
 
 

+ 0 - 2
drivers/usb/gadget/udc/pxa27x_udc.c

@@ -1809,7 +1809,6 @@ static int pxa27x_udc_start(struct usb_gadget *g,
 
 
 	/* first hook up the driver ... */
 	/* first hook up the driver ... */
 	udc->driver = driver;
 	udc->driver = driver;
-	dplus_pullup(udc, 1);
 
 
 	if (!IS_ERR_OR_NULL(udc->transceiver)) {
 	if (!IS_ERR_OR_NULL(udc->transceiver)) {
 		retval = otg_set_peripheral(udc->transceiver->otg,
 		retval = otg_set_peripheral(udc->transceiver->otg,
@@ -1862,7 +1861,6 @@ static int pxa27x_udc_stop(struct usb_gadget *g)
 
 
 	stop_activity(udc, NULL);
 	stop_activity(udc, NULL);
 	udc_disable(udc);
 	udc_disable(udc);
-	dplus_pullup(udc, 0);
 
 
 	udc->driver = NULL;
 	udc->driver = NULL;
 
 

+ 1 - 0
drivers/usb/gadget/udc/r8a66597-udc.c

@@ -1803,6 +1803,7 @@ static int r8a66597_set_selfpowered(struct usb_gadget *gadget, int is_self)
 {
 {
 	struct r8a66597 *r8a66597 = gadget_to_r8a66597(gadget);
 	struct r8a66597 *r8a66597 = gadget_to_r8a66597(gadget);
 
 
+	gadget->is_selfpowered = (is_self != 0);
 	if (is_self)
 	if (is_self)
 		r8a66597->device_status |= 1 << USB_DEVICE_SELF_POWERED;
 		r8a66597->device_status |= 1 << USB_DEVICE_SELF_POWERED;
 	else
 	else

+ 1 - 20
drivers/usb/gadget/udc/s3c2410_udc.c

@@ -238,14 +238,6 @@ static inline void s3c2410_udc_set_ep0_de_out(void __iomem *base)
 			S3C2410_UDC_EP0_CSR_REG);
 			S3C2410_UDC_EP0_CSR_REG);
 }
 }
 
 
-static inline void s3c2410_udc_set_ep0_sse_out(void __iomem *base)
-{
-	udc_writeb(base, S3C2410_UDC_INDEX_EP0, S3C2410_UDC_INDEX_REG);
-	udc_writeb(base, (S3C2410_UDC_EP0_CSR_SOPKTRDY
-				| S3C2410_UDC_EP0_CSR_SSE),
-			S3C2410_UDC_EP0_CSR_REG);
-}
-
 static inline void s3c2410_udc_set_ep0_de_in(void __iomem *base)
 static inline void s3c2410_udc_set_ep0_de_in(void __iomem *base)
 {
 {
 	udc_writeb(base, S3C2410_UDC_INDEX_EP0, S3C2410_UDC_INDEX_REG);
 	udc_writeb(base, S3C2410_UDC_INDEX_EP0, S3C2410_UDC_INDEX_REG);
@@ -291,18 +283,6 @@ static void s3c2410_udc_nuke(struct s3c2410_udc *udc,
 	}
 	}
 }
 }
 
 
-static inline void s3c2410_udc_clear_ep_state(struct s3c2410_udc *dev)
-{
-	unsigned i;
-
-	/* hardware SET_{CONFIGURATION,INTERFACE} automagic resets endpoint
-	 * fifos, and pending transactions mustn't be continued in any case.
-	 */
-
-	for (i = 1; i < S3C2410_ENDPOINTS; i++)
-		s3c2410_udc_nuke(dev, &dev->ep[i], -ECONNABORTED);
-}
-
 static inline int s3c2410_udc_fifo_count_out(void)
 static inline int s3c2410_udc_fifo_count_out(void)
 {
 {
 	int tmp;
 	int tmp;
@@ -1454,6 +1434,7 @@ static int s3c2410_udc_set_selfpowered(struct usb_gadget *gadget, int value)
 
 
 	dprintk(DEBUG_NORMAL, "%s()\n", __func__);
 	dprintk(DEBUG_NORMAL, "%s()\n", __func__);
 
 
+	gadget->is_selfpowered = (value != 0);
 	if (value)
 	if (value)
 		udc->devstatus |= (1 << USB_DEVICE_SELF_POWERED);
 		udc->devstatus |= (1 << USB_DEVICE_SELF_POWERED);
 	else
 	else

+ 2 - 0
drivers/usb/gadget/udc/udc-core.c

@@ -564,6 +564,7 @@ static USB_UDC_ATTR(is_a_peripheral);
 static USB_UDC_ATTR(b_hnp_enable);
 static USB_UDC_ATTR(b_hnp_enable);
 static USB_UDC_ATTR(a_hnp_support);
 static USB_UDC_ATTR(a_hnp_support);
 static USB_UDC_ATTR(a_alt_hnp_support);
 static USB_UDC_ATTR(a_alt_hnp_support);
+static USB_UDC_ATTR(is_selfpowered);
 
 
 static struct attribute *usb_udc_attrs[] = {
 static struct attribute *usb_udc_attrs[] = {
 	&dev_attr_srp.attr,
 	&dev_attr_srp.attr,
@@ -577,6 +578,7 @@ static struct attribute *usb_udc_attrs[] = {
 	&dev_attr_b_hnp_enable.attr,
 	&dev_attr_b_hnp_enable.attr,
 	&dev_attr_a_hnp_support.attr,
 	&dev_attr_a_hnp_support.attr,
 	&dev_attr_a_alt_hnp_support.attr,
 	&dev_attr_a_alt_hnp_support.attr,
+	&dev_attr_is_selfpowered.attr,
 	NULL,
 	NULL,
 };
 };
 
 

+ 0 - 14
drivers/usb/host/Kconfig

@@ -331,20 +331,6 @@ config USB_ISP116X_HCD
 	  To compile this driver as a module, choose M here: the
 	  To compile this driver as a module, choose M here: the
 	  module will be called isp116x-hcd.
 	  module will be called isp116x-hcd.
 
 
-config USB_ISP1760_HCD
-	tristate "ISP 1760 HCD support"
-	---help---
-	  The ISP1760 chip is a USB 2.0 host controller.
-
-	  This driver does not support isochronous transfers or OTG.
-	  This USB controller is usually attached to a non-DMA-Master
-	  capable bus. NXP's eval kit brings this chip on PCI card
-	  where the chip itself is behind a PLB to simulate such
-	  a bus.
-
-	  To compile this driver as a module, choose M here: the
-	  module will be called isp1760.
-
 config USB_ISP1362_HCD
 config USB_ISP1362_HCD
 	tristate "ISP1362 HCD support"
 	tristate "ISP1362 HCD support"
 	---help---
 	---help---

+ 0 - 3
drivers/usb/host/Makefile

@@ -5,8 +5,6 @@
 # tell define_trace.h where to find the xhci trace header
 # tell define_trace.h where to find the xhci trace header
 CFLAGS_xhci-trace.o := -I$(src)
 CFLAGS_xhci-trace.o := -I$(src)
 
 
-isp1760-y := isp1760-hcd.o isp1760-if.o
-
 fhci-y := fhci-hcd.o fhci-hub.o fhci-q.o
 fhci-y := fhci-hcd.o fhci-hub.o fhci-q.o
 fhci-y += fhci-mem.o fhci-tds.o fhci-sched.o
 fhci-y += fhci-mem.o fhci-tds.o fhci-sched.o
 
 
@@ -69,7 +67,6 @@ obj-$(CONFIG_USB_SL811_HCD)	+= sl811-hcd.o
 obj-$(CONFIG_USB_SL811_CS)	+= sl811_cs.o
 obj-$(CONFIG_USB_SL811_CS)	+= sl811_cs.o
 obj-$(CONFIG_USB_U132_HCD)	+= u132-hcd.o
 obj-$(CONFIG_USB_U132_HCD)	+= u132-hcd.o
 obj-$(CONFIG_USB_R8A66597_HCD)	+= r8a66597-hcd.o
 obj-$(CONFIG_USB_R8A66597_HCD)	+= r8a66597-hcd.o
-obj-$(CONFIG_USB_ISP1760_HCD)	+= isp1760.o
 obj-$(CONFIG_USB_HWA_HCD)	+= hwa-hc.o
 obj-$(CONFIG_USB_HWA_HCD)	+= hwa-hc.o
 obj-$(CONFIG_USB_IMX21_HCD)	+= imx21-hcd.o
 obj-$(CONFIG_USB_IMX21_HCD)	+= imx21-hcd.o
 obj-$(CONFIG_USB_FSL_MPH_DR_OF)	+= fsl-mph-dr-of.o
 obj-$(CONFIG_USB_FSL_MPH_DR_OF)	+= fsl-mph-dr-of.o

+ 0 - 208
drivers/usb/host/isp1760-hcd.h

@@ -1,208 +0,0 @@
-#ifndef _ISP1760_HCD_H_
-#define _ISP1760_HCD_H_
-
-/* exports for if */
-struct usb_hcd *isp1760_register(phys_addr_t res_start, resource_size_t res_len,
-				 int irq, unsigned long irqflags,
-				 int rst_gpio,
-				 struct device *dev, const char *busname,
-				 unsigned int devflags);
-int init_kmem_once(void);
-void deinit_kmem_cache(void);
-
-/* EHCI capability registers */
-#define HC_CAPLENGTH		0x00
-#define HC_HCSPARAMS		0x04
-#define HC_HCCPARAMS		0x08
-
-/* EHCI operational registers */
-#define HC_USBCMD		0x20
-#define HC_USBSTS		0x24
-#define HC_FRINDEX		0x2c
-#define HC_CONFIGFLAG		0x60
-#define HC_PORTSC1		0x64
-#define HC_ISO_PTD_DONEMAP_REG	0x130
-#define HC_ISO_PTD_SKIPMAP_REG	0x134
-#define HC_ISO_PTD_LASTPTD_REG	0x138
-#define HC_INT_PTD_DONEMAP_REG	0x140
-#define HC_INT_PTD_SKIPMAP_REG	0x144
-#define HC_INT_PTD_LASTPTD_REG	0x148
-#define HC_ATL_PTD_DONEMAP_REG	0x150
-#define HC_ATL_PTD_SKIPMAP_REG	0x154
-#define HC_ATL_PTD_LASTPTD_REG	0x158
-
-/* Configuration Register */
-#define HC_HW_MODE_CTRL		0x300
-#define ALL_ATX_RESET		(1 << 31)
-#define HW_ANA_DIGI_OC		(1 << 15)
-#define HW_DATA_BUS_32BIT	(1 << 8)
-#define HW_DACK_POL_HIGH	(1 << 6)
-#define HW_DREQ_POL_HIGH	(1 << 5)
-#define HW_INTR_HIGH_ACT	(1 << 2)
-#define HW_INTR_EDGE_TRIG	(1 << 1)
-#define HW_GLOBAL_INTR_EN	(1 << 0)
-
-#define HC_CHIP_ID_REG		0x304
-#define HC_SCRATCH_REG		0x308
-
-#define HC_RESET_REG		0x30c
-#define SW_RESET_RESET_HC	(1 << 1)
-#define SW_RESET_RESET_ALL	(1 << 0)
-
-#define HC_BUFFER_STATUS_REG	0x334
-#define ISO_BUF_FILL		(1 << 2)
-#define INT_BUF_FILL		(1 << 1)
-#define ATL_BUF_FILL		(1 << 0)
-
-#define HC_MEMORY_REG		0x33c
-#define ISP_BANK(x)		((x) << 16)
-
-#define HC_PORT1_CTRL		0x374
-#define PORT1_POWER		(3 << 3)
-#define PORT1_INIT1		(1 << 7)
-#define PORT1_INIT2		(1 << 23)
-#define HW_OTG_CTRL_SET		0x374
-#define HW_OTG_CTRL_CLR		0x376
-
-/* Interrupt Register */
-#define HC_INTERRUPT_REG	0x310
-
-#define HC_INTERRUPT_ENABLE	0x314
-#define HC_ISO_INT		(1 << 9)
-#define HC_ATL_INT		(1 << 8)
-#define HC_INTL_INT		(1 << 7)
-#define HC_EOT_INT		(1 << 3)
-#define HC_SOT_INT		(1 << 1)
-#define INTERRUPT_ENABLE_MASK	(HC_INTL_INT | HC_ATL_INT)
-
-#define HC_ISO_IRQ_MASK_OR_REG	0x318
-#define HC_INT_IRQ_MASK_OR_REG	0x31C
-#define HC_ATL_IRQ_MASK_OR_REG	0x320
-#define HC_ISO_IRQ_MASK_AND_REG	0x324
-#define HC_INT_IRQ_MASK_AND_REG	0x328
-#define HC_ATL_IRQ_MASK_AND_REG	0x32C
-
-/* urb state*/
-#define DELETE_URB		(0x0008)
-#define NO_TRANSFER_ACTIVE	(0xffffffff)
-
-/* Philips Proprietary Transfer Descriptor (PTD) */
-typedef __u32 __bitwise __dw;
-struct ptd {
-	__dw dw0;
-	__dw dw1;
-	__dw dw2;
-	__dw dw3;
-	__dw dw4;
-	__dw dw5;
-	__dw dw6;
-	__dw dw7;
-};
-#define PTD_OFFSET		0x0400
-#define ISO_PTD_OFFSET		0x0400
-#define INT_PTD_OFFSET		0x0800
-#define ATL_PTD_OFFSET		0x0c00
-#define PAYLOAD_OFFSET		0x1000
-
-struct slotinfo {
-	struct isp1760_qh *qh;
-	struct isp1760_qtd *qtd;
-	unsigned long timestamp;
-};
-
-
-typedef void (packet_enqueue)(struct usb_hcd *hcd, struct isp1760_qh *qh,
-		struct isp1760_qtd *qtd);
-
-/*
- * Device flags that can vary from board to board.  All of these
- * indicate the most "atypical" case, so that a devflags of 0 is
- * a sane default configuration.
- */
-#define ISP1760_FLAG_BUS_WIDTH_16	0x00000002 /* 16-bit data bus width */
-#define ISP1760_FLAG_OTG_EN		0x00000004 /* Port 1 supports OTG */
-#define ISP1760_FLAG_ANALOG_OC		0x00000008 /* Analog overcurrent */
-#define ISP1760_FLAG_DACK_POL_HIGH	0x00000010 /* DACK active high */
-#define ISP1760_FLAG_DREQ_POL_HIGH	0x00000020 /* DREQ active high */
-#define ISP1760_FLAG_ISP1761		0x00000040 /* Chip is ISP1761 */
-#define ISP1760_FLAG_INTR_POL_HIGH	0x00000080 /* Interrupt polarity active high */
-#define ISP1760_FLAG_INTR_EDGE_TRIG	0x00000100 /* Interrupt edge triggered */
-#define ISP1760_FLAG_RESET_ACTIVE_HIGH	0x80000000 /* RESET GPIO active high */
-
-/* chip memory management */
-struct memory_chunk {
-	unsigned int start;
-	unsigned int size;
-	unsigned int free;
-};
-
-/*
- * 60kb divided in:
- * - 32 blocks @ 256  bytes
- * - 20 blocks @ 1024 bytes
- * -  4 blocks @ 8192 bytes
- */
-
-#define BLOCK_1_NUM 32
-#define BLOCK_2_NUM 20
-#define BLOCK_3_NUM 4
-
-#define BLOCK_1_SIZE 256
-#define BLOCK_2_SIZE 1024
-#define BLOCK_3_SIZE 8192
-#define BLOCKS (BLOCK_1_NUM + BLOCK_2_NUM + BLOCK_3_NUM)
-#define MAX_PAYLOAD_SIZE BLOCK_3_SIZE
-#define PAYLOAD_AREA_SIZE 0xf000
-
-/* ATL */
-/* DW0 */
-#define DW0_VALID_BIT			1
-#define FROM_DW0_VALID(x)		((x) & 0x01)
-#define TO_DW0_LENGTH(x)		(((u32) x) << 3)
-#define TO_DW0_MAXPACKET(x)		(((u32) x) << 18)
-#define TO_DW0_MULTI(x)			(((u32) x) << 29)
-#define TO_DW0_ENDPOINT(x)		(((u32)	x) << 31)
-/* DW1 */
-#define TO_DW1_DEVICE_ADDR(x)		(((u32) x) << 3)
-#define TO_DW1_PID_TOKEN(x)		(((u32) x) << 10)
-#define DW1_TRANS_BULK			((u32) 2 << 12)
-#define DW1_TRANS_INT			((u32) 3 << 12)
-#define DW1_TRANS_SPLIT			((u32) 1 << 14)
-#define DW1_SE_USB_LOSPEED		((u32) 2 << 16)
-#define TO_DW1_PORT_NUM(x)		(((u32) x) << 18)
-#define TO_DW1_HUB_NUM(x)		(((u32) x) << 25)
-/* DW2 */
-#define TO_DW2_DATA_START_ADDR(x)	(((u32) x) << 8)
-#define TO_DW2_RL(x)			((x) << 25)
-#define FROM_DW2_RL(x)			(((x) >> 25) & 0xf)
-/* DW3 */
-#define FROM_DW3_NRBYTESTRANSFERRED(x)		((x) & 0x7fff)
-#define FROM_DW3_SCS_NRBYTESTRANSFERRED(x)	((x) & 0x07ff)
-#define TO_DW3_NAKCOUNT(x)		((x) << 19)
-#define FROM_DW3_NAKCOUNT(x)		(((x) >> 19) & 0xf)
-#define TO_DW3_CERR(x)			((x) << 23)
-#define FROM_DW3_CERR(x)		(((x) >> 23) & 0x3)
-#define TO_DW3_DATA_TOGGLE(x)		((x) << 25)
-#define FROM_DW3_DATA_TOGGLE(x)		(((x) >> 25) & 0x1)
-#define TO_DW3_PING(x)			((x) << 26)
-#define FROM_DW3_PING(x)		(((x) >> 26) & 0x1)
-#define DW3_ERROR_BIT			(1 << 28)
-#define DW3_BABBLE_BIT			(1 << 29)
-#define DW3_HALT_BIT			(1 << 30)
-#define DW3_ACTIVE_BIT			(1 << 31)
-#define FROM_DW3_ACTIVE(x)		(((x) >> 31) & 0x01)
-
-#define INT_UNDERRUN			(1 << 2)
-#define INT_BABBLE			(1 << 1)
-#define INT_EXACT			(1 << 0)
-
-#define SETUP_PID	(2)
-#define IN_PID		(1)
-#define OUT_PID		(0)
-
-/* Errata 1 */
-#define RL_COUNTER	(0)
-#define NAK_COUNTER	(0)
-#define ERR_COUNTER	(2)
-
-#endif /* _ISP1760_HCD_H_ */

+ 0 - 477
drivers/usb/host/isp1760-if.c

@@ -1,477 +0,0 @@
-/*
- * Glue code for the ISP1760 driver and bus
- * Currently there is support for
- * - OpenFirmware
- * - PCI
- * - PDEV (generic platform device centralized driver model)
- *
- * (c) 2007 Sebastian Siewior <bigeasy@linutronix.de>
- *
- */
-
-#include <linux/usb.h>
-#include <linux/io.h>
-#include <linux/module.h>
-#include <linux/platform_device.h>
-#include <linux/usb/isp1760.h>
-#include <linux/usb/hcd.h>
-
-#include "isp1760-hcd.h"
-
-#if defined(CONFIG_OF) && defined(CONFIG_OF_IRQ)
-#include <linux/slab.h>
-#include <linux/of.h>
-#include <linux/of_platform.h>
-#include <linux/of_address.h>
-#include <linux/of_irq.h>
-#include <linux/of_gpio.h>
-#endif
-
-#ifdef CONFIG_PCI
-#include <linux/pci.h>
-#endif
-
-#if defined(CONFIG_OF) && defined(CONFIG_OF_IRQ)
-struct isp1760 {
-	struct usb_hcd *hcd;
-	int rst_gpio;
-};
-
-static int of_isp1760_probe(struct platform_device *dev)
-{
-	struct isp1760 *drvdata;
-	struct device_node *dp = dev->dev.of_node;
-	struct resource *res;
-	struct resource memory;
-	int virq;
-	resource_size_t res_len;
-	int ret;
-	unsigned int devflags = 0;
-	enum of_gpio_flags gpio_flags;
-	u32 bus_width = 0;
-
-	drvdata = kzalloc(sizeof(*drvdata), GFP_KERNEL);
-	if (!drvdata)
-		return -ENOMEM;
-
-	ret = of_address_to_resource(dp, 0, &memory);
-	if (ret) {
-		ret = -ENXIO;
-		goto free_data;
-	}
-
-	res_len = resource_size(&memory);
-
-	res = request_mem_region(memory.start, res_len, dev_name(&dev->dev));
-	if (!res) {
-		ret = -EBUSY;
-		goto free_data;
-	}
-
-	virq = irq_of_parse_and_map(dp, 0);
-	if (!virq) {
-		ret = -ENODEV;
-		goto release_reg;
-	}
-
-	if (of_device_is_compatible(dp, "nxp,usb-isp1761"))
-		devflags |= ISP1760_FLAG_ISP1761;
-
-	/* Some systems wire up only 16 of the 32 data lines */
-	of_property_read_u32(dp, "bus-width", &bus_width);
-	if (bus_width == 16)
-		devflags |= ISP1760_FLAG_BUS_WIDTH_16;
-
-	if (of_get_property(dp, "port1-otg", NULL) != NULL)
-		devflags |= ISP1760_FLAG_OTG_EN;
-
-	if (of_get_property(dp, "analog-oc", NULL) != NULL)
-		devflags |= ISP1760_FLAG_ANALOG_OC;
-
-	if (of_get_property(dp, "dack-polarity", NULL) != NULL)
-		devflags |= ISP1760_FLAG_DACK_POL_HIGH;
-
-	if (of_get_property(dp, "dreq-polarity", NULL) != NULL)
-		devflags |= ISP1760_FLAG_DREQ_POL_HIGH;
-
-	drvdata->rst_gpio = of_get_gpio_flags(dp, 0, &gpio_flags);
-	if (gpio_is_valid(drvdata->rst_gpio)) {
-		ret = gpio_request(drvdata->rst_gpio, dev_name(&dev->dev));
-		if (!ret) {
-			if (!(gpio_flags & OF_GPIO_ACTIVE_LOW)) {
-				devflags |= ISP1760_FLAG_RESET_ACTIVE_HIGH;
-				gpio_direction_output(drvdata->rst_gpio, 0);
-			} else {
-				gpio_direction_output(drvdata->rst_gpio, 1);
-			}
-		} else {
-			drvdata->rst_gpio = ret;
-		}
-	}
-
-	drvdata->hcd = isp1760_register(memory.start, res_len, virq,
-					IRQF_SHARED, drvdata->rst_gpio,
-					&dev->dev, dev_name(&dev->dev),
-					devflags);
-	if (IS_ERR(drvdata->hcd)) {
-		ret = PTR_ERR(drvdata->hcd);
-		goto free_gpio;
-	}
-
-	platform_set_drvdata(dev, drvdata);
-	return ret;
-
-free_gpio:
-	if (gpio_is_valid(drvdata->rst_gpio))
-		gpio_free(drvdata->rst_gpio);
-release_reg:
-	release_mem_region(memory.start, res_len);
-free_data:
-	kfree(drvdata);
-	return ret;
-}
-
-static int of_isp1760_remove(struct platform_device *dev)
-{
-	struct isp1760 *drvdata = platform_get_drvdata(dev);
-
-	usb_remove_hcd(drvdata->hcd);
-	iounmap(drvdata->hcd->regs);
-	release_mem_region(drvdata->hcd->rsrc_start, drvdata->hcd->rsrc_len);
-	usb_put_hcd(drvdata->hcd);
-
-	if (gpio_is_valid(drvdata->rst_gpio))
-		gpio_free(drvdata->rst_gpio);
-
-	kfree(drvdata);
-	return 0;
-}
-
-static const struct of_device_id of_isp1760_match[] = {
-	{
-		.compatible = "nxp,usb-isp1760",
-	},
-	{
-		.compatible = "nxp,usb-isp1761",
-	},
-	{ },
-};
-MODULE_DEVICE_TABLE(of, of_isp1760_match);
-
-static struct platform_driver isp1760_of_driver = {
-	.driver = {
-		.name = "nxp-isp1760",
-		.of_match_table = of_isp1760_match,
-	},
-	.probe          = of_isp1760_probe,
-	.remove         = of_isp1760_remove,
-};
-#endif
-
-#ifdef CONFIG_PCI
-static int isp1761_pci_probe(struct pci_dev *dev,
-		const struct pci_device_id *id)
-{
-	u8 latency, limit;
-	__u32 reg_data;
-	int retry_count;
-	struct usb_hcd *hcd;
-	unsigned int devflags = 0;
-	int ret_status = 0;
-
-	resource_size_t pci_mem_phy0;
-	resource_size_t memlength;
-
-	u8 __iomem *chip_addr;
-	u8 __iomem *iobase;
-	resource_size_t nxp_pci_io_base;
-	resource_size_t iolength;
-
-	if (usb_disabled())
-		return -ENODEV;
-
-	if (pci_enable_device(dev) < 0)
-		return -ENODEV;
-
-	if (!dev->irq)
-		return -ENODEV;
-
-	/* Grab the PLX PCI mem maped port start address we need  */
-	nxp_pci_io_base = pci_resource_start(dev, 0);
-	iolength = pci_resource_len(dev, 0);
-
-	if (!request_mem_region(nxp_pci_io_base, iolength, "ISP1761 IO MEM")) {
-		printk(KERN_ERR "request region #1\n");
-		return -EBUSY;
-	}
-
-	iobase = ioremap_nocache(nxp_pci_io_base, iolength);
-	if (!iobase) {
-		printk(KERN_ERR "ioremap #1\n");
-		ret_status = -ENOMEM;
-		goto cleanup1;
-	}
-	/* Grab the PLX PCI shared memory of the ISP 1761 we need  */
-	pci_mem_phy0 = pci_resource_start(dev, 3);
-	memlength = pci_resource_len(dev, 3);
-	if (memlength < 0xffff) {
-		printk(KERN_ERR "memory length for this resource is wrong\n");
-		ret_status = -ENOMEM;
-		goto cleanup2;
-	}
-
-	if (!request_mem_region(pci_mem_phy0, memlength, "ISP-PCI")) {
-		printk(KERN_ERR "host controller already in use\n");
-		ret_status = -EBUSY;
-		goto cleanup2;
-	}
-
-	/* map available memory */
-	chip_addr = ioremap_nocache(pci_mem_phy0,memlength);
-	if (!chip_addr) {
-		printk(KERN_ERR "Error ioremap failed\n");
-		ret_status = -ENOMEM;
-		goto cleanup3;
-	}
-
-	/* bad pci latencies can contribute to overruns */
-	pci_read_config_byte(dev, PCI_LATENCY_TIMER, &latency);
-	if (latency) {
-		pci_read_config_byte(dev, PCI_MAX_LAT, &limit);
-		if (limit && limit < latency)
-			pci_write_config_byte(dev, PCI_LATENCY_TIMER, limit);
-	}
-
-	/* Try to check whether we can access Scratch Register of
-	 * Host Controller or not. The initial PCI access is retried until
-	 * local init for the PCI bridge is completed
-	 */
-	retry_count = 20;
-	reg_data = 0;
-	while ((reg_data != 0xFACE) && retry_count) {
-		/*by default host is in 16bit mode, so
-		 * io operations at this stage must be 16 bit
-		 * */
-		writel(0xface, chip_addr + HC_SCRATCH_REG);
-		udelay(100);
-		reg_data = readl(chip_addr + HC_SCRATCH_REG) & 0x0000ffff;
-		retry_count--;
-	}
-
-	iounmap(chip_addr);
-
-	/* Host Controller presence is detected by writing to scratch register
-	 * and reading back and checking the contents are same or not
-	 */
-	if (reg_data != 0xFACE) {
-		dev_err(&dev->dev, "scratch register mismatch %x\n", reg_data);
-		ret_status = -ENOMEM;
-		goto cleanup3;
-	}
-
-	pci_set_master(dev);
-
-	/* configure PLX PCI chip to pass interrupts */
-#define PLX_INT_CSR_REG 0x68
-	reg_data = readl(iobase + PLX_INT_CSR_REG);
-	reg_data |= 0x900;
-	writel(reg_data, iobase + PLX_INT_CSR_REG);
-
-	dev->dev.dma_mask = NULL;
-	hcd = isp1760_register(pci_mem_phy0, memlength, dev->irq,
-		IRQF_SHARED, -ENOENT, &dev->dev, dev_name(&dev->dev),
-		devflags);
-	if (IS_ERR(hcd)) {
-		ret_status = -ENODEV;
-		goto cleanup3;
-	}
-
-	/* done with PLX IO access */
-	iounmap(iobase);
-	release_mem_region(nxp_pci_io_base, iolength);
-
-	pci_set_drvdata(dev, hcd);
-	return 0;
-
-cleanup3:
-	release_mem_region(pci_mem_phy0, memlength);
-cleanup2:
-	iounmap(iobase);
-cleanup1:
-	release_mem_region(nxp_pci_io_base, iolength);
-	return ret_status;
-}
-
-static void isp1761_pci_remove(struct pci_dev *dev)
-{
-	struct usb_hcd *hcd;
-
-	hcd = pci_get_drvdata(dev);
-
-	usb_remove_hcd(hcd);
-	iounmap(hcd->regs);
-	release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
-	usb_put_hcd(hcd);
-
-	pci_disable_device(dev);
-}
-
-static void isp1761_pci_shutdown(struct pci_dev *dev)
-{
-	printk(KERN_ERR "ips1761_pci_shutdown\n");
-}
-
-static const struct pci_device_id isp1760_plx [] = {
-	{
-		.class          = PCI_CLASS_BRIDGE_OTHER << 8,
-		.class_mask     = ~0,
-		.vendor		= PCI_VENDOR_ID_PLX,
-		.device		= 0x5406,
-		.subvendor	= PCI_VENDOR_ID_PLX,
-		.subdevice	= 0x9054,
-	},
-	{ }
-};
-MODULE_DEVICE_TABLE(pci, isp1760_plx);
-
-static struct pci_driver isp1761_pci_driver = {
-	.name =         "isp1760",
-	.id_table =     isp1760_plx,
-	.probe =        isp1761_pci_probe,
-	.remove =       isp1761_pci_remove,
-	.shutdown =     isp1761_pci_shutdown,
-};
-#endif
-
-static int isp1760_plat_probe(struct platform_device *pdev)
-{
-	int ret = 0;
-	struct usb_hcd *hcd;
-	struct resource *mem_res;
-	struct resource *irq_res;
-	resource_size_t mem_size;
-	struct isp1760_platform_data *priv = dev_get_platdata(&pdev->dev);
-	unsigned int devflags = 0;
-	unsigned long irqflags = IRQF_SHARED;
-
-	mem_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-	if (!mem_res) {
-		pr_warning("isp1760: Memory resource not available\n");
-		ret = -ENODEV;
-		goto out;
-	}
-	mem_size = resource_size(mem_res);
-	if (!request_mem_region(mem_res->start, mem_size, "isp1760")) {
-		pr_warning("isp1760: Cannot reserve the memory resource\n");
-		ret = -EBUSY;
-		goto out;
-	}
-
-	irq_res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
-	if (!irq_res) {
-		pr_warning("isp1760: IRQ resource not available\n");
-		ret = -ENODEV;
-		goto cleanup;
-	}
-
-	irqflags |= irq_res->flags & IRQF_TRIGGER_MASK;
-
-	if (priv) {
-		if (priv->is_isp1761)
-			devflags |= ISP1760_FLAG_ISP1761;
-		if (priv->bus_width_16)
-			devflags |= ISP1760_FLAG_BUS_WIDTH_16;
-		if (priv->port1_otg)
-			devflags |= ISP1760_FLAG_OTG_EN;
-		if (priv->analog_oc)
-			devflags |= ISP1760_FLAG_ANALOG_OC;
-		if (priv->dack_polarity_high)
-			devflags |= ISP1760_FLAG_DACK_POL_HIGH;
-		if (priv->dreq_polarity_high)
-			devflags |= ISP1760_FLAG_DREQ_POL_HIGH;
-	}
-
-	hcd = isp1760_register(mem_res->start, mem_size, irq_res->start,
-			       irqflags, -ENOENT,
-			       &pdev->dev, dev_name(&pdev->dev), devflags);
-
-	platform_set_drvdata(pdev, hcd);
-
-	if (IS_ERR(hcd)) {
-		pr_warning("isp1760: Failed to register the HCD device\n");
-		ret = -ENODEV;
-		goto cleanup;
-	}
-
-	pr_info("ISP1760 USB device initialised\n");
-	return ret;
-
-cleanup:
-	release_mem_region(mem_res->start, mem_size);
-out:
-	return ret;
-}
-
-static int isp1760_plat_remove(struct platform_device *pdev)
-{
-	struct resource *mem_res;
-	resource_size_t mem_size;
-	struct usb_hcd *hcd = platform_get_drvdata(pdev);
-
-	usb_remove_hcd(hcd);
-
-	mem_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-	mem_size = resource_size(mem_res);
-	release_mem_region(mem_res->start, mem_size);
-
-	usb_put_hcd(hcd);
-
-	return 0;
-}
-
-static struct platform_driver isp1760_plat_driver = {
-	.probe	= isp1760_plat_probe,
-	.remove	= isp1760_plat_remove,
-	.driver	= {
-		.name	= "isp1760",
-	},
-};
-
-static int __init isp1760_init(void)
-{
-	int ret, any_ret = -ENODEV;
-
-	init_kmem_once();
-
-	ret = platform_driver_register(&isp1760_plat_driver);
-	if (!ret)
-		any_ret = 0;
-#if defined(CONFIG_OF) && defined(CONFIG_OF_IRQ)
-	ret = platform_driver_register(&isp1760_of_driver);
-	if (!ret)
-		any_ret = 0;
-#endif
-#ifdef CONFIG_PCI
-	ret = pci_register_driver(&isp1761_pci_driver);
-	if (!ret)
-		any_ret = 0;
-#endif
-
-	if (any_ret)
-		deinit_kmem_cache();
-	return any_ret;
-}
-module_init(isp1760_init);
-
-static void __exit isp1760_exit(void)
-{
-	platform_driver_unregister(&isp1760_plat_driver);
-#if defined(CONFIG_OF) && defined(CONFIG_OF_IRQ)
-	platform_driver_unregister(&isp1760_of_driver);
-#endif
-#ifdef CONFIG_PCI
-	pci_unregister_driver(&isp1761_pci_driver);
-#endif
-	deinit_kmem_cache();
-}
-module_exit(isp1760_exit);

+ 59 - 0
drivers/usb/isp1760/Kconfig

@@ -0,0 +1,59 @@
+config USB_ISP1760
+	tristate "NXP ISP 1760/1761 support"
+	depends on USB || USB_GADGET
+	help
+	  Say Y or M here if your system as an ISP1760 USB host controller
+	  or an ISP1761 USB dual-role controller.
+
+	  This driver does not support isochronous transfers or OTG.
+	  This USB controller is usually attached to a non-DMA-Master
+	  capable bus. NXP's eval kit brings this chip on PCI card
+	  where the chip itself is behind a PLB to simulate such
+	  a bus.
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called isp1760.
+
+config USB_ISP1760_HCD
+	bool
+
+config USB_ISP1761_UDC
+	bool
+
+if USB_ISP1760
+
+choice
+	bool "ISP1760 Mode Selection"
+	default USB_ISP1760_DUAL_ROLE if (USB && USB_GADGET)
+	default USB_ISP1760_HOST_ROLE if (USB && !USB_GADGET)
+	default USB_ISP1760_GADGET_ROLE if (!USB && USB_GADGET)
+
+config USB_ISP1760_HOST_ROLE
+	bool "Host only mode"
+	depends on USB=y || USB=USB_ISP1760
+	select USB_ISP1760_HCD
+	help
+	  Select this if you want to use the ISP1760 in host mode only. The
+	  gadget function will be disabled.
+
+config USB_ISP1760_GADGET_ROLE
+	bool "Gadget only mode"
+	depends on USB_GADGET=y || USB_GADGET=USB_ISP1760
+	select USB_ISP1761_UDC
+	help
+	  Select this if you want to use the ISP1760 in peripheral mode only.
+	  The host function will be disabled.
+
+config USB_ISP1760_DUAL_ROLE
+	bool "Dual Role mode"
+	depends on USB=y || USB=USB_ISP1760
+	depends on USB_GADGET=y || USB_GADGET=USB_ISP1760
+	select USB_ISP1760_HCD
+	select USB_ISP1761_UDC
+	help
+	  Select this if you want to use the ISP1760 in both host and
+	  peripheral modes.
+
+endchoice
+
+endif

+ 5 - 0
drivers/usb/isp1760/Makefile

@@ -0,0 +1,5 @@
+isp1760-y := isp1760-core.o isp1760-if.o
+isp1760-$(CONFIG_USB_ISP1760_HCD) += isp1760-hcd.o
+isp1760-$(CONFIG_USB_ISP1761_UDC) += isp1760-udc.o
+
+obj-$(CONFIG_USB_ISP1760)	+= isp1760.o

+ 177 - 0
drivers/usb/isp1760/isp1760-core.c

@@ -0,0 +1,177 @@
+/*
+ * Driver for the NXP ISP1760 chip
+ *
+ * Copyright 2014 Laurent Pinchart
+ * Copyright 2007 Sebastian Siewior
+ *
+ * Contacts:
+ *	Sebastian Siewior <bigeasy@linutronix.de>
+ *	Laurent Pinchart <laurent.pinchart@ideasonboard.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ */
+
+#include <linux/delay.h>
+#include <linux/gpio/consumer.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/usb.h>
+
+#include "isp1760-core.h"
+#include "isp1760-hcd.h"
+#include "isp1760-regs.h"
+#include "isp1760-udc.h"
+
+static void isp1760_init_core(struct isp1760_device *isp)
+{
+	u32 otgctrl;
+	u32 hwmode;
+
+	/* Low-level chip reset */
+	if (isp->rst_gpio) {
+		gpiod_set_value_cansleep(isp->rst_gpio, 1);
+		mdelay(50);
+		gpiod_set_value_cansleep(isp->rst_gpio, 0);
+	}
+
+	/*
+	 * Reset the host controller, including the CPU interface
+	 * configuration.
+	 */
+	isp1760_write32(isp->regs, HC_RESET_REG, SW_RESET_RESET_ALL);
+	msleep(100);
+
+	/* Setup HW Mode Control: This assumes a level active-low interrupt */
+	hwmode = HW_DATA_BUS_32BIT;
+
+	if (isp->devflags & ISP1760_FLAG_BUS_WIDTH_16)
+		hwmode &= ~HW_DATA_BUS_32BIT;
+	if (isp->devflags & ISP1760_FLAG_ANALOG_OC)
+		hwmode |= HW_ANA_DIGI_OC;
+	if (isp->devflags & ISP1760_FLAG_DACK_POL_HIGH)
+		hwmode |= HW_DACK_POL_HIGH;
+	if (isp->devflags & ISP1760_FLAG_DREQ_POL_HIGH)
+		hwmode |= HW_DREQ_POL_HIGH;
+	if (isp->devflags & ISP1760_FLAG_INTR_POL_HIGH)
+		hwmode |= HW_INTR_HIGH_ACT;
+	if (isp->devflags & ISP1760_FLAG_INTR_EDGE_TRIG)
+		hwmode |= HW_INTR_EDGE_TRIG;
+
+	/*
+	 * The ISP1761 has a dedicated DC IRQ line but supports sharing the HC
+	 * IRQ line for both the host and device controllers. Hardcode IRQ
+	 * sharing for now and disable the DC interrupts globally to avoid
+	 * spurious interrupts during HCD registration.
+	 */
+	if (isp->devflags & ISP1760_FLAG_ISP1761) {
+		isp1760_write32(isp->regs, DC_MODE, 0);
+		hwmode |= HW_COMN_IRQ;
+	}
+
+	/*
+	 * We have to set this first in case we're in 16-bit mode.
+	 * Write it twice to ensure correct upper bits if switching
+	 * to 16-bit mode.
+	 */
+	isp1760_write32(isp->regs, HC_HW_MODE_CTRL, hwmode);
+	isp1760_write32(isp->regs, HC_HW_MODE_CTRL, hwmode);
+
+	/*
+	 * PORT 1 Control register of the ISP1760 is the OTG control register
+	 * on ISP1761.
+	 *
+	 * TODO: Really support OTG. For now we configure port 1 in device mode
+	 * when OTG is requested.
+	 */
+	if ((isp->devflags & ISP1760_FLAG_ISP1761) &&
+	    (isp->devflags & ISP1760_FLAG_OTG_EN))
+		otgctrl = ((HW_DM_PULLDOWN | HW_DP_PULLDOWN) << 16)
+			| HW_OTG_DISABLE;
+	else
+		otgctrl = (HW_SW_SEL_HC_DC << 16)
+			| (HW_VBUS_DRV | HW_SEL_CP_EXT);
+
+	isp1760_write32(isp->regs, HC_PORT1_CTRL, otgctrl);
+
+	dev_info(isp->dev, "bus width: %u, oc: %s\n",
+		 isp->devflags & ISP1760_FLAG_BUS_WIDTH_16 ? 16 : 32,
+		 isp->devflags & ISP1760_FLAG_ANALOG_OC ? "analog" : "digital");
+}
+
+void isp1760_set_pullup(struct isp1760_device *isp, bool enable)
+{
+	isp1760_write32(isp->regs, HW_OTG_CTRL_SET,
+			enable ? HW_DP_PULLUP : HW_DP_PULLUP << 16);
+}
+
+int isp1760_register(struct resource *mem, int irq, unsigned long irqflags,
+		     struct device *dev, unsigned int devflags)
+{
+	struct isp1760_device *isp;
+	bool udc_disabled = !(devflags & ISP1760_FLAG_ISP1761);
+	int ret;
+
+	/*
+	 * If neither the HCD not the UDC is enabled return an error, as no
+	 * device would be registered.
+	 */
+	if ((!IS_ENABLED(CONFIG_USB_ISP1760_HCD) || usb_disabled()) &&
+	    (!IS_ENABLED(CONFIG_USB_ISP1761_UDC) || udc_disabled))
+		return -ENODEV;
+
+	/* prevent usb-core allocating DMA pages */
+	dev->dma_mask = NULL;
+
+	isp = devm_kzalloc(dev, sizeof(*isp), GFP_KERNEL);
+	if (!isp)
+		return -ENOMEM;
+
+	isp->dev = dev;
+	isp->devflags = devflags;
+
+	isp->rst_gpio = devm_gpiod_get_optional(dev, NULL, GPIOD_OUT_HIGH);
+	if (IS_ERR(isp->rst_gpio))
+		return PTR_ERR(isp->rst_gpio);
+
+	isp->regs = devm_ioremap_resource(dev, mem);
+	if (IS_ERR(isp->regs))
+		return PTR_ERR(isp->regs);
+
+	isp1760_init_core(isp);
+
+	if (IS_ENABLED(CONFIG_USB_ISP1760_HCD) && !usb_disabled()) {
+		ret = isp1760_hcd_register(&isp->hcd, isp->regs, mem, irq,
+					   irqflags | IRQF_SHARED, dev);
+		if (ret < 0)
+			return ret;
+	}
+
+	if (IS_ENABLED(CONFIG_USB_ISP1761_UDC) && !udc_disabled) {
+		ret = isp1760_udc_register(isp, irq, irqflags | IRQF_SHARED |
+					   IRQF_DISABLED);
+		if (ret < 0) {
+			isp1760_hcd_unregister(&isp->hcd);
+			return ret;
+		}
+	}
+
+	dev_set_drvdata(dev, isp);
+
+	return 0;
+}
+
+void isp1760_unregister(struct device *dev)
+{
+	struct isp1760_device *isp = dev_get_drvdata(dev);
+
+	isp1760_udc_unregister(isp);
+	isp1760_hcd_unregister(&isp->hcd);
+}
+
+MODULE_DESCRIPTION("Driver for the ISP1760 USB-controller from NXP");
+MODULE_AUTHOR("Sebastian Siewior <bigeasy@linuxtronix.de>");
+MODULE_LICENSE("GPL v2");

+ 68 - 0
drivers/usb/isp1760/isp1760-core.h

@@ -0,0 +1,68 @@
+/*
+ * Driver for the NXP ISP1760 chip
+ *
+ * Copyright 2014 Laurent Pinchart
+ * Copyright 2007 Sebastian Siewior
+ *
+ * Contacts:
+ *	Sebastian Siewior <bigeasy@linutronix.de>
+ *	Laurent Pinchart <laurent.pinchart@ideasonboard.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 _ISP1760_CORE_H_
+#define _ISP1760_CORE_H_
+
+#include <linux/ioport.h>
+
+#include "isp1760-hcd.h"
+#include "isp1760-udc.h"
+
+struct device;
+struct gpio_desc;
+
+/*
+ * Device flags that can vary from board to board.  All of these
+ * indicate the most "atypical" case, so that a devflags of 0 is
+ * a sane default configuration.
+ */
+#define ISP1760_FLAG_BUS_WIDTH_16	0x00000002 /* 16-bit data bus width */
+#define ISP1760_FLAG_OTG_EN		0x00000004 /* Port 1 supports OTG */
+#define ISP1760_FLAG_ANALOG_OC		0x00000008 /* Analog overcurrent */
+#define ISP1760_FLAG_DACK_POL_HIGH	0x00000010 /* DACK active high */
+#define ISP1760_FLAG_DREQ_POL_HIGH	0x00000020 /* DREQ active high */
+#define ISP1760_FLAG_ISP1761		0x00000040 /* Chip is ISP1761 */
+#define ISP1760_FLAG_INTR_POL_HIGH	0x00000080 /* Interrupt polarity active high */
+#define ISP1760_FLAG_INTR_EDGE_TRIG	0x00000100 /* Interrupt edge triggered */
+
+struct isp1760_device {
+	struct device *dev;
+
+	void __iomem *regs;
+	unsigned int devflags;
+	struct gpio_desc *rst_gpio;
+
+	struct isp1760_hcd hcd;
+	struct isp1760_udc udc;
+};
+
+int isp1760_register(struct resource *mem, int irq, unsigned long irqflags,
+		     struct device *dev, unsigned int devflags);
+void isp1760_unregister(struct device *dev);
+
+void isp1760_set_pullup(struct isp1760_device *isp, bool enable);
+
+static inline u32 isp1760_read32(void __iomem *base, u32 reg)
+{
+	return readl(base + reg);
+}
+
+static inline void isp1760_write32(void __iomem *base, u32 reg, u32 val)
+{
+	writel(val, base + reg);
+}
+
+#endif

+ 122 - 156
drivers/usb/host/isp1760-hcd.c → drivers/usb/isp1760/isp1760-hcd.c

@@ -11,6 +11,7 @@
  * (c) 2011 Arvid Brodin <arvid.brodin@enea.com>
  * (c) 2011 Arvid Brodin <arvid.brodin@enea.com>
  *
  *
  */
  */
+#include <linux/gpio/consumer.h>
 #include <linux/module.h>
 #include <linux/module.h>
 #include <linux/kernel.h>
 #include <linux/kernel.h>
 #include <linux/slab.h>
 #include <linux/slab.h>
@@ -24,73 +25,96 @@
 #include <linux/timer.h>
 #include <linux/timer.h>
 #include <asm/unaligned.h>
 #include <asm/unaligned.h>
 #include <asm/cacheflush.h>
 #include <asm/cacheflush.h>
-#include <linux/gpio.h>
 
 
+#include "isp1760-core.h"
 #include "isp1760-hcd.h"
 #include "isp1760-hcd.h"
+#include "isp1760-regs.h"
 
 
 static struct kmem_cache *qtd_cachep;
 static struct kmem_cache *qtd_cachep;
 static struct kmem_cache *qh_cachep;
 static struct kmem_cache *qh_cachep;
 static struct kmem_cache *urb_listitem_cachep;
 static struct kmem_cache *urb_listitem_cachep;
 
 
-enum queue_head_types {
-	QH_CONTROL,
-	QH_BULK,
-	QH_INTERRUPT,
-	QH_END
-};
-
-struct isp1760_hcd {
-	u32 hcs_params;
-	spinlock_t		lock;
-	struct slotinfo		atl_slots[32];
-	int			atl_done_map;
-	struct slotinfo		int_slots[32];
-	int			int_done_map;
-	struct memory_chunk memory_pool[BLOCKS];
-	struct list_head	qh_list[QH_END];
-
-	/* periodic schedule support */
-#define	DEFAULT_I_TDPS		1024
-	unsigned		periodic_size;
-	unsigned		i_thresh;
-	unsigned long		reset_done;
-	unsigned long		next_statechange;
-	unsigned int		devflags;
-
-	int			rst_gpio;
-};
+typedef void (packet_enqueue)(struct usb_hcd *hcd, struct isp1760_qh *qh,
+		struct isp1760_qtd *qtd);
 
 
 static inline struct isp1760_hcd *hcd_to_priv(struct usb_hcd *hcd)
 static inline struct isp1760_hcd *hcd_to_priv(struct usb_hcd *hcd)
 {
 {
-	return (struct isp1760_hcd *) (hcd->hcd_priv);
+	return *(struct isp1760_hcd **)hcd->hcd_priv;
 }
 }
 
 
-/* Section 2.2 Host Controller Capability Registers */
-#define HC_LENGTH(p)		(((p)>>00)&0x00ff)	/* bits 7:0 */
-#define HC_VERSION(p)		(((p)>>16)&0xffff)	/* bits 31:16 */
-#define HCS_INDICATOR(p)	((p)&(1 << 16))	/* true: has port indicators */
-#define HCS_PPC(p)		((p)&(1 << 4))	/* true: port power control */
-#define HCS_N_PORTS(p)		(((p)>>0)&0xf)	/* bits 3:0, ports on HC */
-#define HCC_ISOC_CACHE(p)       ((p)&(1 << 7))  /* true: can cache isoc frame */
-#define HCC_ISOC_THRES(p)       (((p)>>4)&0x7)  /* bits 6:4, uframes cached */
-
-/* Section 2.3 Host Controller Operational Registers */
-#define CMD_LRESET	(1<<7)		/* partial reset (no ports, etc) */
-#define CMD_RESET	(1<<1)		/* reset HC not bus */
-#define CMD_RUN		(1<<0)		/* start/stop HC */
-#define STS_PCD		(1<<2)		/* port change detect */
-#define FLAG_CF		(1<<0)		/* true: we'll support "high speed" */
-
-#define PORT_OWNER	(1<<13)		/* true: companion hc owns this port */
-#define PORT_POWER	(1<<12)		/* true: has power (see PPC) */
-#define PORT_USB11(x) (((x) & (3 << 10)) == (1 << 10))	/* USB 1.1 device */
-#define PORT_RESET	(1<<8)		/* reset port */
-#define PORT_SUSPEND	(1<<7)		/* suspend port */
-#define PORT_RESUME	(1<<6)		/* resume it */
-#define PORT_PE		(1<<2)		/* port enable */
-#define PORT_CSC	(1<<1)		/* connect status change */
-#define PORT_CONNECT	(1<<0)		/* device connected */
-#define PORT_RWC_BITS   (PORT_CSC)
+/* urb state*/
+#define DELETE_URB		(0x0008)
+#define NO_TRANSFER_ACTIVE	(0xffffffff)
+
+/* Philips Proprietary Transfer Descriptor (PTD) */
+typedef __u32 __bitwise __dw;
+struct ptd {
+	__dw dw0;
+	__dw dw1;
+	__dw dw2;
+	__dw dw3;
+	__dw dw4;
+	__dw dw5;
+	__dw dw6;
+	__dw dw7;
+};
+#define PTD_OFFSET		0x0400
+#define ISO_PTD_OFFSET		0x0400
+#define INT_PTD_OFFSET		0x0800
+#define ATL_PTD_OFFSET		0x0c00
+#define PAYLOAD_OFFSET		0x1000
+
+
+/* ATL */
+/* DW0 */
+#define DW0_VALID_BIT			1
+#define FROM_DW0_VALID(x)		((x) & 0x01)
+#define TO_DW0_LENGTH(x)		(((u32) x) << 3)
+#define TO_DW0_MAXPACKET(x)		(((u32) x) << 18)
+#define TO_DW0_MULTI(x)			(((u32) x) << 29)
+#define TO_DW0_ENDPOINT(x)		(((u32)	x) << 31)
+/* DW1 */
+#define TO_DW1_DEVICE_ADDR(x)		(((u32) x) << 3)
+#define TO_DW1_PID_TOKEN(x)		(((u32) x) << 10)
+#define DW1_TRANS_BULK			((u32) 2 << 12)
+#define DW1_TRANS_INT			((u32) 3 << 12)
+#define DW1_TRANS_SPLIT			((u32) 1 << 14)
+#define DW1_SE_USB_LOSPEED		((u32) 2 << 16)
+#define TO_DW1_PORT_NUM(x)		(((u32) x) << 18)
+#define TO_DW1_HUB_NUM(x)		(((u32) x) << 25)
+/* DW2 */
+#define TO_DW2_DATA_START_ADDR(x)	(((u32) x) << 8)
+#define TO_DW2_RL(x)			((x) << 25)
+#define FROM_DW2_RL(x)			(((x) >> 25) & 0xf)
+/* DW3 */
+#define FROM_DW3_NRBYTESTRANSFERRED(x)		((x) & 0x7fff)
+#define FROM_DW3_SCS_NRBYTESTRANSFERRED(x)	((x) & 0x07ff)
+#define TO_DW3_NAKCOUNT(x)		((x) << 19)
+#define FROM_DW3_NAKCOUNT(x)		(((x) >> 19) & 0xf)
+#define TO_DW3_CERR(x)			((x) << 23)
+#define FROM_DW3_CERR(x)		(((x) >> 23) & 0x3)
+#define TO_DW3_DATA_TOGGLE(x)		((x) << 25)
+#define FROM_DW3_DATA_TOGGLE(x)		(((x) >> 25) & 0x1)
+#define TO_DW3_PING(x)			((x) << 26)
+#define FROM_DW3_PING(x)		(((x) >> 26) & 0x1)
+#define DW3_ERROR_BIT			(1 << 28)
+#define DW3_BABBLE_BIT			(1 << 29)
+#define DW3_HALT_BIT			(1 << 30)
+#define DW3_ACTIVE_BIT			(1 << 31)
+#define FROM_DW3_ACTIVE(x)		(((x) >> 31) & 0x01)
+
+#define INT_UNDERRUN			(1 << 2)
+#define INT_BABBLE			(1 << 1)
+#define INT_EXACT			(1 << 0)
+
+#define SETUP_PID	(2)
+#define IN_PID		(1)
+#define OUT_PID		(0)
+
+/* Errata 1 */
+#define RL_COUNTER	(0)
+#define NAK_COUNTER	(0)
+#define ERR_COUNTER	(2)
 
 
 struct isp1760_qtd {
 struct isp1760_qtd {
 	u8 packet_type;
 	u8 packet_type;
@@ -137,12 +161,12 @@ struct urb_listitem {
  */
  */
 static u32 reg_read32(void __iomem *base, u32 reg)
 static u32 reg_read32(void __iomem *base, u32 reg)
 {
 {
-	return readl(base + reg);
+	return isp1760_read32(base, reg);
 }
 }
 
 
 static void reg_write32(void __iomem *base, u32 reg, u32 val)
 static void reg_write32(void __iomem *base, u32 reg, u32 val)
 {
 {
-	writel(val, base + reg);
+	isp1760_write32(base, reg, val);
 }
 }
 
 
 /*
 /*
@@ -443,42 +467,6 @@ static int isp1760_hc_setup(struct usb_hcd *hcd)
 	int result;
 	int result;
 	u32 scratch, hwmode;
 	u32 scratch, hwmode;
 
 
-	/* low-level chip reset */
-	if (gpio_is_valid(priv->rst_gpio)) {
-		unsigned int rst_lvl;
-
-		rst_lvl = (priv->devflags &
-			   ISP1760_FLAG_RESET_ACTIVE_HIGH) ? 1 : 0;
-
-		gpio_set_value(priv->rst_gpio, rst_lvl);
-		mdelay(50);
-		gpio_set_value(priv->rst_gpio, !rst_lvl);
-	}
-
-	/* Setup HW Mode Control: This assumes a level active-low interrupt */
-	hwmode = HW_DATA_BUS_32BIT;
-
-	if (priv->devflags & ISP1760_FLAG_BUS_WIDTH_16)
-		hwmode &= ~HW_DATA_BUS_32BIT;
-	if (priv->devflags & ISP1760_FLAG_ANALOG_OC)
-		hwmode |= HW_ANA_DIGI_OC;
-	if (priv->devflags & ISP1760_FLAG_DACK_POL_HIGH)
-		hwmode |= HW_DACK_POL_HIGH;
-	if (priv->devflags & ISP1760_FLAG_DREQ_POL_HIGH)
-		hwmode |= HW_DREQ_POL_HIGH;
-	if (priv->devflags & ISP1760_FLAG_INTR_POL_HIGH)
-		hwmode |= HW_INTR_HIGH_ACT;
-	if (priv->devflags & ISP1760_FLAG_INTR_EDGE_TRIG)
-		hwmode |= HW_INTR_EDGE_TRIG;
-
-	/*
-	 * We have to set this first in case we're in 16-bit mode.
-	 * Write it twice to ensure correct upper bits if switching
-	 * to 16-bit mode.
-	 */
-	reg_write32(hcd->regs, HC_HW_MODE_CTRL, hwmode);
-	reg_write32(hcd->regs, HC_HW_MODE_CTRL, hwmode);
-
 	reg_write32(hcd->regs, HC_SCRATCH_REG, 0xdeadbabe);
 	reg_write32(hcd->regs, HC_SCRATCH_REG, 0xdeadbabe);
 	/* Change bus pattern */
 	/* Change bus pattern */
 	scratch = reg_read32(hcd->regs, HC_CHIP_ID_REG);
 	scratch = reg_read32(hcd->regs, HC_CHIP_ID_REG);
@@ -488,46 +476,33 @@ static int isp1760_hc_setup(struct usb_hcd *hcd)
 		return -ENODEV;
 		return -ENODEV;
 	}
 	}
 
 
-	/* pre reset */
+	/*
+	 * The RESET_HC bit in the SW_RESET register is supposed to reset the
+	 * host controller without touching the CPU interface registers, but at
+	 * least on the ISP1761 it seems to behave as the RESET_ALL bit and
+	 * reset the whole device. We thus can't use it here, so let's reset
+	 * the host controller through the EHCI USB Command register. The device
+	 * has been reset in core code anyway, so this shouldn't matter.
+	 */
 	reg_write32(hcd->regs, HC_BUFFER_STATUS_REG, 0);
 	reg_write32(hcd->regs, HC_BUFFER_STATUS_REG, 0);
 	reg_write32(hcd->regs, HC_ATL_PTD_SKIPMAP_REG, NO_TRANSFER_ACTIVE);
 	reg_write32(hcd->regs, HC_ATL_PTD_SKIPMAP_REG, NO_TRANSFER_ACTIVE);
 	reg_write32(hcd->regs, HC_INT_PTD_SKIPMAP_REG, NO_TRANSFER_ACTIVE);
 	reg_write32(hcd->regs, HC_INT_PTD_SKIPMAP_REG, NO_TRANSFER_ACTIVE);
 	reg_write32(hcd->regs, HC_ISO_PTD_SKIPMAP_REG, NO_TRANSFER_ACTIVE);
 	reg_write32(hcd->regs, HC_ISO_PTD_SKIPMAP_REG, NO_TRANSFER_ACTIVE);
 
 
-	/* reset */
-	reg_write32(hcd->regs, HC_RESET_REG, SW_RESET_RESET_ALL);
-	mdelay(100);
-
-	reg_write32(hcd->regs, HC_RESET_REG, SW_RESET_RESET_HC);
-	mdelay(100);
-
 	result = ehci_reset(hcd);
 	result = ehci_reset(hcd);
 	if (result)
 	if (result)
 		return result;
 		return result;
 
 
 	/* Step 11 passed */
 	/* Step 11 passed */
 
 
-	dev_info(hcd->self.controller, "bus width: %d, oc: %s\n",
-			   (priv->devflags & ISP1760_FLAG_BUS_WIDTH_16) ?
-			   16 : 32, (priv->devflags & ISP1760_FLAG_ANALOG_OC) ?
-			   "analog" : "digital");
-
 	/* ATL reset */
 	/* ATL reset */
+	hwmode = reg_read32(hcd->regs, HC_HW_MODE_CTRL) & ~ALL_ATX_RESET;
 	reg_write32(hcd->regs, HC_HW_MODE_CTRL, hwmode | ALL_ATX_RESET);
 	reg_write32(hcd->regs, HC_HW_MODE_CTRL, hwmode | ALL_ATX_RESET);
 	mdelay(10);
 	mdelay(10);
 	reg_write32(hcd->regs, HC_HW_MODE_CTRL, hwmode);
 	reg_write32(hcd->regs, HC_HW_MODE_CTRL, hwmode);
 
 
 	reg_write32(hcd->regs, HC_INTERRUPT_ENABLE, INTERRUPT_ENABLE_MASK);
 	reg_write32(hcd->regs, HC_INTERRUPT_ENABLE, INTERRUPT_ENABLE_MASK);
 
 
-	/*
-	 * PORT 1 Control register of the ISP1760 is the OTG control
-	 * register on ISP1761. Since there is no OTG or device controller
-	 * support in this driver, we use port 1 as a "normal" USB host port on
-	 * both chips.
-	 */
-	reg_write32(hcd->regs, HC_PORT1_CTRL, PORT1_POWER | PORT1_INIT2);
-	mdelay(10);
-
 	priv->hcs_params = reg_read32(hcd->regs, HC_HCSPARAMS);
 	priv->hcs_params = reg_read32(hcd->regs, HC_HCSPARAMS);
 
 
 	return priv_init(hcd);
 	return priv_init(hcd);
@@ -743,8 +718,9 @@ static void qtd_free(struct isp1760_qtd *qtd)
 }
 }
 
 
 static void start_bus_transfer(struct usb_hcd *hcd, u32 ptd_offset, int slot,
 static void start_bus_transfer(struct usb_hcd *hcd, u32 ptd_offset, int slot,
-				struct slotinfo *slots, struct isp1760_qtd *qtd,
-				struct isp1760_qh *qh, struct ptd *ptd)
+				struct isp1760_slotinfo *slots,
+				struct isp1760_qtd *qtd, struct isp1760_qh *qh,
+				struct ptd *ptd)
 {
 {
 	struct isp1760_hcd *priv = hcd_to_priv(hcd);
 	struct isp1760_hcd *priv = hcd_to_priv(hcd);
 	int skip_map;
 	int skip_map;
@@ -857,7 +833,7 @@ static void enqueue_qtds(struct usb_hcd *hcd, struct isp1760_qh *qh)
 {
 {
 	struct isp1760_hcd *priv = hcd_to_priv(hcd);
 	struct isp1760_hcd *priv = hcd_to_priv(hcd);
 	int ptd_offset;
 	int ptd_offset;
-	struct slotinfo *slots;
+	struct isp1760_slotinfo *slots;
 	int curr_slot, free_slot;
 	int curr_slot, free_slot;
 	int n;
 	int n;
 	struct ptd ptd;
 	struct ptd ptd;
@@ -1097,7 +1073,7 @@ static void handle_done_ptds(struct usb_hcd *hcd)
 	struct isp1760_qh *qh;
 	struct isp1760_qh *qh;
 	int slot;
 	int slot;
 	int state;
 	int state;
-	struct slotinfo *slots;
+	struct isp1760_slotinfo *slots;
 	u32 ptd_offset;
 	u32 ptd_offset;
 	struct isp1760_qtd *qtd;
 	struct isp1760_qtd *qtd;
 	int modified;
 	int modified;
@@ -2161,7 +2137,7 @@ static void isp1760_clear_tt_buffer_complete(struct usb_hcd *hcd,
 static const struct hc_driver isp1760_hc_driver = {
 static const struct hc_driver isp1760_hc_driver = {
 	.description		= "isp1760-hcd",
 	.description		= "isp1760-hcd",
 	.product_desc		= "NXP ISP1760 USB Host Controller",
 	.product_desc		= "NXP ISP1760 USB Host Controller",
-	.hcd_priv_size		= sizeof(struct isp1760_hcd),
+	.hcd_priv_size		= sizeof(struct isp1760_hcd *),
 	.irq			= isp1760_irq,
 	.irq			= isp1760_irq,
 	.flags			= HCD_MEMORY | HCD_USB2,
 	.flags			= HCD_MEMORY | HCD_USB2,
 	.reset			= isp1760_hc_setup,
 	.reset			= isp1760_hc_setup,
@@ -2177,7 +2153,7 @@ static const struct hc_driver isp1760_hc_driver = {
 	.clear_tt_buffer_complete	= isp1760_clear_tt_buffer_complete,
 	.clear_tt_buffer_complete	= isp1760_clear_tt_buffer_complete,
 };
 };
 
 
-int __init init_kmem_once(void)
+int __init isp1760_init_kmem_once(void)
 {
 {
 	urb_listitem_cachep = kmem_cache_create("isp1760_urb_listitem",
 	urb_listitem_cachep = kmem_cache_create("isp1760_urb_listitem",
 			sizeof(struct urb_listitem), 0, SLAB_TEMPORARY |
 			sizeof(struct urb_listitem), 0, SLAB_TEMPORARY |
@@ -2204,66 +2180,56 @@ int __init init_kmem_once(void)
 	return 0;
 	return 0;
 }
 }
 
 
-void deinit_kmem_cache(void)
+void isp1760_deinit_kmem_cache(void)
 {
 {
 	kmem_cache_destroy(qtd_cachep);
 	kmem_cache_destroy(qtd_cachep);
 	kmem_cache_destroy(qh_cachep);
 	kmem_cache_destroy(qh_cachep);
 	kmem_cache_destroy(urb_listitem_cachep);
 	kmem_cache_destroy(urb_listitem_cachep);
 }
 }
 
 
-struct usb_hcd *isp1760_register(phys_addr_t res_start, resource_size_t res_len,
-				 int irq, unsigned long irqflags,
-				 int rst_gpio,
-				 struct device *dev, const char *busname,
-				 unsigned int devflags)
+int isp1760_hcd_register(struct isp1760_hcd *priv, void __iomem *regs,
+			 struct resource *mem, int irq, unsigned long irqflags,
+			 struct device *dev)
 {
 {
 	struct usb_hcd *hcd;
 	struct usb_hcd *hcd;
-	struct isp1760_hcd *priv;
 	int ret;
 	int ret;
 
 
-	if (usb_disabled())
-		return ERR_PTR(-ENODEV);
-
-	/* prevent usb-core allocating DMA pages */
-	dev->dma_mask = NULL;
-
 	hcd = usb_create_hcd(&isp1760_hc_driver, dev, dev_name(dev));
 	hcd = usb_create_hcd(&isp1760_hc_driver, dev, dev_name(dev));
 	if (!hcd)
 	if (!hcd)
-		return ERR_PTR(-ENOMEM);
+		return -ENOMEM;
+
+	*(struct isp1760_hcd **)hcd->hcd_priv = priv;
+
+	priv->hcd = hcd;
 
 
-	priv = hcd_to_priv(hcd);
-	priv->devflags = devflags;
-	priv->rst_gpio = rst_gpio;
 	init_memory(priv);
 	init_memory(priv);
-	hcd->regs = ioremap(res_start, res_len);
-	if (!hcd->regs) {
-		ret = -EIO;
-		goto err_put;
-	}
 
 
 	hcd->irq = irq;
 	hcd->irq = irq;
-	hcd->rsrc_start = res_start;
-	hcd->rsrc_len = res_len;
+	hcd->regs = regs;
+	hcd->rsrc_start = mem->start;
+	hcd->rsrc_len = resource_size(mem);
 
 
 	/* This driver doesn't support wakeup requests */
 	/* This driver doesn't support wakeup requests */
 	hcd->cant_recv_wakeups = 1;
 	hcd->cant_recv_wakeups = 1;
 
 
 	ret = usb_add_hcd(hcd, irq, irqflags);
 	ret = usb_add_hcd(hcd, irq, irqflags);
 	if (ret)
 	if (ret)
-		goto err_unmap;
+		goto error;
+
 	device_wakeup_enable(hcd->self.controller);
 	device_wakeup_enable(hcd->self.controller);
 
 
-	return hcd;
+	return 0;
 
 
-err_unmap:
-	 iounmap(hcd->regs);
+error:
+	usb_put_hcd(hcd);
+	return ret;
+}
 
 
-err_put:
-	 usb_put_hcd(hcd);
+void isp1760_hcd_unregister(struct isp1760_hcd *priv)
+{
+	if (!priv->hcd)
+		return;
 
 
-	 return ERR_PTR(ret);
+	usb_remove_hcd(priv->hcd);
+	usb_put_hcd(priv->hcd);
 }
 }
-
-MODULE_DESCRIPTION("Driver for the ISP1760 USB-controller from NXP");
-MODULE_AUTHOR("Sebastian Siewior <bigeasy@linuxtronix.de>");
-MODULE_LICENSE("GPL v2");

+ 102 - 0
drivers/usb/isp1760/isp1760-hcd.h

@@ -0,0 +1,102 @@
+#ifndef _ISP1760_HCD_H_
+#define _ISP1760_HCD_H_
+
+#include <linux/spinlock.h>
+
+struct isp1760_qh;
+struct isp1760_qtd;
+struct resource;
+struct usb_hcd;
+
+/*
+ * 60kb divided in:
+ * - 32 blocks @ 256  bytes
+ * - 20 blocks @ 1024 bytes
+ * -  4 blocks @ 8192 bytes
+ */
+
+#define BLOCK_1_NUM 32
+#define BLOCK_2_NUM 20
+#define BLOCK_3_NUM 4
+
+#define BLOCK_1_SIZE 256
+#define BLOCK_2_SIZE 1024
+#define BLOCK_3_SIZE 8192
+#define BLOCKS (BLOCK_1_NUM + BLOCK_2_NUM + BLOCK_3_NUM)
+#define MAX_PAYLOAD_SIZE BLOCK_3_SIZE
+#define PAYLOAD_AREA_SIZE 0xf000
+
+struct isp1760_slotinfo {
+	struct isp1760_qh *qh;
+	struct isp1760_qtd *qtd;
+	unsigned long timestamp;
+};
+
+/* chip memory management */
+struct isp1760_memory_chunk {
+	unsigned int start;
+	unsigned int size;
+	unsigned int free;
+};
+
+enum isp1760_queue_head_types {
+	QH_CONTROL,
+	QH_BULK,
+	QH_INTERRUPT,
+	QH_END
+};
+
+struct isp1760_hcd {
+#ifdef CONFIG_USB_ISP1760_HCD
+	struct usb_hcd		*hcd;
+
+	u32 hcs_params;
+	spinlock_t		lock;
+	struct isp1760_slotinfo	atl_slots[32];
+	int			atl_done_map;
+	struct isp1760_slotinfo	int_slots[32];
+	int			int_done_map;
+	struct isp1760_memory_chunk memory_pool[BLOCKS];
+	struct list_head	qh_list[QH_END];
+
+	/* periodic schedule support */
+#define	DEFAULT_I_TDPS		1024
+	unsigned		periodic_size;
+	unsigned		i_thresh;
+	unsigned long		reset_done;
+	unsigned long		next_statechange;
+#endif
+};
+
+#ifdef CONFIG_USB_ISP1760_HCD
+int isp1760_hcd_register(struct isp1760_hcd *priv, void __iomem *regs,
+			 struct resource *mem, int irq, unsigned long irqflags,
+			 struct device *dev);
+void isp1760_hcd_unregister(struct isp1760_hcd *priv);
+
+int isp1760_init_kmem_once(void);
+void isp1760_deinit_kmem_cache(void);
+#else
+static inline int isp1760_hcd_register(struct isp1760_hcd *priv,
+				       void __iomem *regs, struct resource *mem,
+				       int irq, unsigned long irqflags,
+				       struct device *dev)
+{
+	return 0;
+}
+
+static inline void isp1760_hcd_unregister(struct isp1760_hcd *priv)
+{
+}
+
+static inline int isp1760_init_kmem_once(void)
+{
+	return 0;
+}
+
+static inline void isp1760_deinit_kmem_cache(void)
+{
+}
+#endif
+
+#endif /* _ISP1760_HCD_H_ */

+ 309 - 0
drivers/usb/isp1760/isp1760-if.c

@@ -0,0 +1,309 @@
+/*
+ * Glue code for the ISP1760 driver and bus
+ * Currently there is support for
+ * - OpenFirmware
+ * - PCI
+ * - PDEV (generic platform device centralized driver model)
+ *
+ * (c) 2007 Sebastian Siewior <bigeasy@linutronix.de>
+ *
+ */
+
+#include <linux/usb.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/usb/isp1760.h>
+#include <linux/usb/hcd.h>
+
+#include "isp1760-core.h"
+#include "isp1760-regs.h"
+
+#ifdef CONFIG_PCI
+#include <linux/pci.h>
+#endif
+
+#ifdef CONFIG_PCI
+static int isp1761_pci_init(struct pci_dev *dev)
+{
+	resource_size_t mem_start;
+	resource_size_t mem_length;
+	u8 __iomem *iobase;
+	u8 latency, limit;
+	int retry_count;
+	u32 reg_data;
+
+	/* Grab the PLX PCI shared memory of the ISP 1761 we need  */
+	mem_start = pci_resource_start(dev, 3);
+	mem_length = pci_resource_len(dev, 3);
+	if (mem_length < 0xffff) {
+		printk(KERN_ERR "memory length for this resource is wrong\n");
+		return -ENOMEM;
+	}
+
+	if (!request_mem_region(mem_start, mem_length, "ISP-PCI")) {
+		printk(KERN_ERR "host controller already in use\n");
+		return -EBUSY;
+	}
+
+	/* map available memory */
+	iobase = ioremap_nocache(mem_start, mem_length);
+	if (!iobase) {
+		printk(KERN_ERR "Error ioremap failed\n");
+		release_mem_region(mem_start, mem_length);
+		return -ENOMEM;
+	}
+
+	/* bad pci latencies can contribute to overruns */
+	pci_read_config_byte(dev, PCI_LATENCY_TIMER, &latency);
+	if (latency) {
+		pci_read_config_byte(dev, PCI_MAX_LAT, &limit);
+		if (limit && limit < latency)
+			pci_write_config_byte(dev, PCI_LATENCY_TIMER, limit);
+	}
+
+	/* Try to check whether we can access Scratch Register of
+	 * Host Controller or not. The initial PCI access is retried until
+	 * local init for the PCI bridge is completed
+	 */
+	retry_count = 20;
+	reg_data = 0;
+	while ((reg_data != 0xFACE) && retry_count) {
+		/*by default host is in 16bit mode, so
+		 * io operations at this stage must be 16 bit
+		 * */
+		writel(0xface, iobase + HC_SCRATCH_REG);
+		udelay(100);
+		reg_data = readl(iobase + HC_SCRATCH_REG) & 0x0000ffff;
+		retry_count--;
+	}
+
+	iounmap(iobase);
+	release_mem_region(mem_start, mem_length);
+
+	/* Host Controller presence is detected by writing to scratch register
+	 * and reading back and checking the contents are same or not
+	 */
+	if (reg_data != 0xFACE) {
+		dev_err(&dev->dev, "scratch register mismatch %x\n", reg_data);
+		return -ENOMEM;
+	}
+
+	/* Grab the PLX PCI mem maped port start address we need  */
+	mem_start = pci_resource_start(dev, 0);
+	mem_length = pci_resource_len(dev, 0);
+
+	if (!request_mem_region(mem_start, mem_length, "ISP1761 IO MEM")) {
+		printk(KERN_ERR "request region #1\n");
+		return -EBUSY;
+	}
+
+	iobase = ioremap_nocache(mem_start, mem_length);
+	if (!iobase) {
+		printk(KERN_ERR "ioremap #1\n");
+		release_mem_region(mem_start, mem_length);
+		return -ENOMEM;
+	}
+
+	/* configure PLX PCI chip to pass interrupts */
+#define PLX_INT_CSR_REG 0x68
+	reg_data = readl(iobase + PLX_INT_CSR_REG);
+	reg_data |= 0x900;
+	writel(reg_data, iobase + PLX_INT_CSR_REG);
+
+	/* done with PLX IO access */
+	iounmap(iobase);
+	release_mem_region(mem_start, mem_length);
+
+	return 0;
+}
+
+static int isp1761_pci_probe(struct pci_dev *dev,
+		const struct pci_device_id *id)
+{
+	unsigned int devflags = 0;
+	int ret;
+
+	if (!dev->irq)
+		return -ENODEV;
+
+	if (pci_enable_device(dev) < 0)
+		return -ENODEV;
+
+	ret = isp1761_pci_init(dev);
+	if (ret < 0)
+		goto error;
+
+	pci_set_master(dev);
+
+	dev->dev.dma_mask = NULL;
+	ret = isp1760_register(&dev->resource[3], dev->irq, 0, &dev->dev,
+			       devflags);
+	if (ret < 0)
+		goto error;
+
+	return 0;
+
+error:
+	pci_disable_device(dev);
+	return ret;
+}
+
+static void isp1761_pci_remove(struct pci_dev *dev)
+{
+	isp1760_unregister(&dev->dev);
+
+	pci_disable_device(dev);
+}
+
+static void isp1761_pci_shutdown(struct pci_dev *dev)
+{
+	printk(KERN_ERR "ips1761_pci_shutdown\n");
+}
+
+static const struct pci_device_id isp1760_plx [] = {
+	{
+		.class          = PCI_CLASS_BRIDGE_OTHER << 8,
+		.class_mask     = ~0,
+		.vendor		= PCI_VENDOR_ID_PLX,
+		.device		= 0x5406,
+		.subvendor	= PCI_VENDOR_ID_PLX,
+		.subdevice	= 0x9054,
+	},
+	{ }
+};
+MODULE_DEVICE_TABLE(pci, isp1760_plx);
+
+static struct pci_driver isp1761_pci_driver = {
+	.name =         "isp1760",
+	.id_table =     isp1760_plx,
+	.probe =        isp1761_pci_probe,
+	.remove =       isp1761_pci_remove,
+	.shutdown =     isp1761_pci_shutdown,
+};
+#endif
+
+static int isp1760_plat_probe(struct platform_device *pdev)
+{
+	unsigned long irqflags;
+	unsigned int devflags = 0;
+	struct resource *mem_res;
+	struct resource *irq_res;
+	int ret;
+
+	mem_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+
+	irq_res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
+	if (!irq_res) {
+		pr_warning("isp1760: IRQ resource not available\n");
+		return -ENODEV;
+	}
+	irqflags = irq_res->flags & IRQF_TRIGGER_MASK;
+
+	if (IS_ENABLED(CONFIG_OF) && pdev->dev.of_node) {
+		struct device_node *dp = pdev->dev.of_node;
+		u32 bus_width = 0;
+
+		if (of_device_is_compatible(dp, "nxp,usb-isp1761"))
+			devflags |= ISP1760_FLAG_ISP1761;
+
+		/* Some systems wire up only 16 of the 32 data lines */
+		of_property_read_u32(dp, "bus-width", &bus_width);
+		if (bus_width == 16)
+			devflags |= ISP1760_FLAG_BUS_WIDTH_16;
+
+		if (of_property_read_bool(dp, "port1-otg"))
+			devflags |= ISP1760_FLAG_OTG_EN;
+
+		if (of_property_read_bool(dp, "analog-oc"))
+			devflags |= ISP1760_FLAG_ANALOG_OC;
+
+		if (of_property_read_bool(dp, "dack-polarity"))
+			devflags |= ISP1760_FLAG_DACK_POL_HIGH;
+
+		if (of_property_read_bool(dp, "dreq-polarity"))
+			devflags |= ISP1760_FLAG_DREQ_POL_HIGH;
+	} else if (dev_get_platdata(&pdev->dev)) {
+		struct isp1760_platform_data *pdata =
+			dev_get_platdata(&pdev->dev);
+
+		if (pdata->is_isp1761)
+			devflags |= ISP1760_FLAG_ISP1761;
+		if (pdata->bus_width_16)
+			devflags |= ISP1760_FLAG_BUS_WIDTH_16;
+		if (pdata->port1_otg)
+			devflags |= ISP1760_FLAG_OTG_EN;
+		if (pdata->analog_oc)
+			devflags |= ISP1760_FLAG_ANALOG_OC;
+		if (pdata->dack_polarity_high)
+			devflags |= ISP1760_FLAG_DACK_POL_HIGH;
+		if (pdata->dreq_polarity_high)
+			devflags |= ISP1760_FLAG_DREQ_POL_HIGH;
+	}
+
+	ret = isp1760_register(mem_res, irq_res->start, irqflags, &pdev->dev,
+			       devflags);
+	if (ret < 0)
+		return ret;
+
+	pr_info("ISP1760 USB device initialised\n");
+	return 0;
+}
+
+static int isp1760_plat_remove(struct platform_device *pdev)
+{
+	isp1760_unregister(&pdev->dev);
+
+	return 0;
+}
+
+#ifdef CONFIG_OF
+static const struct of_device_id isp1760_of_match[] = {
+	{ .compatible = "nxp,usb-isp1760", },
+	{ .compatible = "nxp,usb-isp1761", },
+	{ },
+};
+MODULE_DEVICE_TABLE(of, isp1760_of_match);
+#endif
+
+static struct platform_driver isp1760_plat_driver = {
+	.probe	= isp1760_plat_probe,
+	.remove	= isp1760_plat_remove,
+	.driver	= {
+		.name	= "isp1760",
+		.of_match_table = of_match_ptr(isp1760_of_match),
+	},
+};
+
+static int __init isp1760_init(void)
+{
+	int ret, any_ret = -ENODEV;
+
+	isp1760_init_kmem_once();
+
+	ret = platform_driver_register(&isp1760_plat_driver);
+	if (!ret)
+		any_ret = 0;
+#ifdef CONFIG_PCI
+	ret = pci_register_driver(&isp1761_pci_driver);
+	if (!ret)
+		any_ret = 0;
+#endif
+
+	if (any_ret)
+		isp1760_deinit_kmem_cache();
+	return any_ret;
+}
+module_init(isp1760_init);
+
+static void __exit isp1760_exit(void)
+{
+	platform_driver_unregister(&isp1760_plat_driver);
+#ifdef CONFIG_PCI
+	pci_unregister_driver(&isp1761_pci_driver);
+#endif
+	isp1760_deinit_kmem_cache();
+}
+module_exit(isp1760_exit);

+ 230 - 0
drivers/usb/isp1760/isp1760-regs.h

@@ -0,0 +1,230 @@
+/*
+ * Driver for the NXP ISP1760 chip
+ *
+ * Copyright 2014 Laurent Pinchart
+ * Copyright 2007 Sebastian Siewior
+ *
+ * Contacts:
+ *     Sebastian Siewior <bigeasy@linutronix.de>
+ *     Laurent Pinchart <laurent.pinchart@ideasonboard.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 _ISP1760_REGS_H_
+#define _ISP1760_REGS_H_
+
+/* -----------------------------------------------------------------------------
+ * Host Controller
+ */
+
+/* EHCI capability registers */
+#define HC_CAPLENGTH		0x000
+#define HC_LENGTH(p)		(((p) >> 00) & 0x00ff)	/* bits 7:0 */
+#define HC_VERSION(p)		(((p) >> 16) & 0xffff)	/* bits 31:16 */
+
+#define HC_HCSPARAMS		0x004
+#define HCS_INDICATOR(p)	((p) & (1 << 16))	/* true: has port indicators */
+#define HCS_PPC(p)		((p) & (1 << 4))	/* true: port power control */
+#define HCS_N_PORTS(p)		(((p) >> 0) & 0xf)	/* bits 3:0, ports on HC */
+
+#define HC_HCCPARAMS		0x008
+#define HCC_ISOC_CACHE(p)       ((p) & (1 << 7))	/* true: can cache isoc frame */
+#define HCC_ISOC_THRES(p)       (((p) >> 4) & 0x7)	/* bits 6:4, uframes cached */
+
+/* EHCI operational registers */
+#define HC_USBCMD		0x020
+#define CMD_LRESET		(1 << 7)		/* partial reset (no ports, etc) */
+#define CMD_RESET		(1 << 1)		/* reset HC not bus */
+#define CMD_RUN			(1 << 0)		/* start/stop HC */
+
+#define HC_USBSTS		0x024
+#define STS_PCD			(1 << 2)		/* port change detect */
+
+#define HC_FRINDEX		0x02c
+
+#define HC_CONFIGFLAG		0x060
+#define FLAG_CF			(1 << 0)		/* true: we'll support "high speed" */
+
+#define HC_PORTSC1		0x064
+#define PORT_OWNER		(1 << 13)		/* true: companion hc owns this port */
+#define PORT_POWER		(1 << 12)		/* true: has power (see PPC) */
+#define PORT_USB11(x)		(((x) & (3 << 10)) == (1 << 10))	/* USB 1.1 device */
+#define PORT_RESET		(1 << 8)		/* reset port */
+#define PORT_SUSPEND		(1 << 7)		/* suspend port */
+#define PORT_RESUME		(1 << 6)		/* resume it */
+#define PORT_PE			(1 << 2)		/* port enable */
+#define PORT_CSC		(1 << 1)		/* connect status change */
+#define PORT_CONNECT		(1 << 0)		/* device connected */
+#define PORT_RWC_BITS		(PORT_CSC)
+
+#define HC_ISO_PTD_DONEMAP_REG	0x130
+#define HC_ISO_PTD_SKIPMAP_REG	0x134
+#define HC_ISO_PTD_LASTPTD_REG	0x138
+#define HC_INT_PTD_DONEMAP_REG	0x140
+#define HC_INT_PTD_SKIPMAP_REG	0x144
+#define HC_INT_PTD_LASTPTD_REG	0x148
+#define HC_ATL_PTD_DONEMAP_REG	0x150
+#define HC_ATL_PTD_SKIPMAP_REG	0x154
+#define HC_ATL_PTD_LASTPTD_REG	0x158
+
+/* Configuration Register */
+#define HC_HW_MODE_CTRL		0x300
+#define ALL_ATX_RESET		(1 << 31)
+#define HW_ANA_DIGI_OC		(1 << 15)
+#define HW_DEV_DMA		(1 << 11)
+#define HW_COMN_IRQ		(1 << 10)
+#define HW_COMN_DMA		(1 << 9)
+#define HW_DATA_BUS_32BIT	(1 << 8)
+#define HW_DACK_POL_HIGH	(1 << 6)
+#define HW_DREQ_POL_HIGH	(1 << 5)
+#define HW_INTR_HIGH_ACT	(1 << 2)
+#define HW_INTR_EDGE_TRIG	(1 << 1)
+#define HW_GLOBAL_INTR_EN	(1 << 0)
+
+#define HC_CHIP_ID_REG		0x304
+#define HC_SCRATCH_REG		0x308
+
+#define HC_RESET_REG		0x30c
+#define SW_RESET_RESET_HC	(1 << 1)
+#define SW_RESET_RESET_ALL	(1 << 0)
+
+#define HC_BUFFER_STATUS_REG	0x334
+#define ISO_BUF_FILL		(1 << 2)
+#define INT_BUF_FILL		(1 << 1)
+#define ATL_BUF_FILL		(1 << 0)
+
+#define HC_MEMORY_REG		0x33c
+#define ISP_BANK(x)		((x) << 16)
+
+#define HC_PORT1_CTRL		0x374
+#define PORT1_POWER		(3 << 3)
+#define PORT1_INIT1		(1 << 7)
+#define PORT1_INIT2		(1 << 23)
+#define HW_OTG_CTRL_SET		0x374
+#define HW_OTG_CTRL_CLR		0x376
+#define HW_OTG_DISABLE		(1 << 10)
+#define HW_OTG_SE0_EN		(1 << 9)
+#define HW_BDIS_ACON_EN		(1 << 8)
+#define HW_SW_SEL_HC_DC		(1 << 7)
+#define HW_VBUS_CHRG		(1 << 6)
+#define HW_VBUS_DISCHRG		(1 << 5)
+#define HW_VBUS_DRV		(1 << 4)
+#define HW_SEL_CP_EXT		(1 << 3)
+#define HW_DM_PULLDOWN		(1 << 2)
+#define HW_DP_PULLDOWN		(1 << 1)
+#define HW_DP_PULLUP		(1 << 0)
+
+/* Interrupt Register */
+#define HC_INTERRUPT_REG	0x310
+
+#define HC_INTERRUPT_ENABLE	0x314
+#define HC_ISO_INT		(1 << 9)
+#define HC_ATL_INT		(1 << 8)
+#define HC_INTL_INT		(1 << 7)
+#define HC_EOT_INT		(1 << 3)
+#define HC_SOT_INT		(1 << 1)
+#define INTERRUPT_ENABLE_MASK	(HC_INTL_INT | HC_ATL_INT)
+
+#define HC_ISO_IRQ_MASK_OR_REG	0x318
+#define HC_INT_IRQ_MASK_OR_REG	0x31c
+#define HC_ATL_IRQ_MASK_OR_REG	0x320
+#define HC_ISO_IRQ_MASK_AND_REG	0x324
+#define HC_INT_IRQ_MASK_AND_REG	0x328
+#define HC_ATL_IRQ_MASK_AND_REG	0x32c
+
+/* -----------------------------------------------------------------------------
+ * Peripheral Controller
+ */
+
+/* Initialization Registers */
+#define DC_ADDRESS			0x0200
+#define DC_DEVEN			(1 << 7)
+
+#define DC_MODE				0x020c
+#define DC_DMACLKON			(1 << 9)
+#define DC_VBUSSTAT			(1 << 8)
+#define DC_CLKAON			(1 << 7)
+#define DC_SNDRSU			(1 << 6)
+#define DC_GOSUSP			(1 << 5)
+#define DC_SFRESET			(1 << 4)
+#define DC_GLINTENA			(1 << 3)
+#define DC_WKUPCS			(1 << 2)
+
+#define DC_INTCONF			0x0210
+#define DC_CDBGMOD_ACK_NAK		(0 << 6)
+#define DC_CDBGMOD_ACK			(1 << 6)
+#define DC_CDBGMOD_ACK_1NAK		(2 << 6)
+#define DC_DDBGMODIN_ACK_NAK		(0 << 4)
+#define DC_DDBGMODIN_ACK		(1 << 4)
+#define DC_DDBGMODIN_ACK_1NAK		(2 << 4)
+#define DC_DDBGMODOUT_ACK_NYET_NAK	(0 << 2)
+#define DC_DDBGMODOUT_ACK_NYET		(1 << 2)
+#define DC_DDBGMODOUT_ACK_NYET_1NAK	(2 << 2)
+#define DC_INTLVL			(1 << 1)
+#define DC_INTPOL			(1 << 0)
+
+#define DC_DEBUG			0x0212
+#define DC_INTENABLE			0x0214
+#define DC_IEPTX(n)			(1 << (11 + 2 * (n)))
+#define DC_IEPRX(n)			(1 << (10 + 2 * (n)))
+#define DC_IEPRXTX(n)			(3 << (10 + 2 * (n)))
+#define DC_IEP0SETUP			(1 << 8)
+#define DC_IEVBUS			(1 << 7)
+#define DC_IEDMA			(1 << 6)
+#define DC_IEHS_STA			(1 << 5)
+#define DC_IERESM			(1 << 4)
+#define DC_IESUSP			(1 << 3)
+#define DC_IEPSOF			(1 << 2)
+#define DC_IESOF			(1 << 1)
+#define DC_IEBRST			(1 << 0)
+
+/* Data Flow Registers */
+#define DC_EPINDEX			0x022c
+#define DC_EP0SETUP			(1 << 5)
+#define DC_ENDPIDX(n)			((n) << 1)
+#define DC_EPDIR			(1 << 0)
+
+#define DC_CTRLFUNC			0x0228
+#define DC_CLBUF			(1 << 4)
+#define DC_VENDP			(1 << 3)
+#define DC_DSEN				(1 << 2)
+#define DC_STATUS			(1 << 1)
+#define DC_STALL			(1 << 0)
+
+#define DC_DATAPORT			0x0220
+#define DC_BUFLEN			0x021c
+#define DC_DATACOUNT_MASK		0xffff
+#define DC_BUFSTAT			0x021e
+#define DC_EPMAXPKTSZ			0x0204
+
+#define DC_EPTYPE			0x0208
+#define DC_NOEMPKT			(1 << 4)
+#define DC_EPENABLE			(1 << 3)
+#define DC_DBLBUF			(1 << 2)
+#define DC_ENDPTYP_ISOC			(1 << 0)
+#define DC_ENDPTYP_BULK			(2 << 0)
+#define DC_ENDPTYP_INTERRUPT		(3 << 0)
+
+/* DMA Registers */
+#define DC_DMACMD			0x0230
+#define DC_DMATXCOUNT			0x0234
+#define DC_DMACONF			0x0238
+#define DC_DMAHW			0x023c
+#define DC_DMAINTREASON			0x0250
+#define DC_DMAINTEN			0x0254
+#define DC_DMAEP			0x0258
+#define DC_DMABURSTCOUNT		0x0264
+
+/* General Registers */
+#define DC_INTERRUPT			0x0218
+#define DC_CHIPID			0x0270
+#define DC_FRAMENUM			0x0274
+#define DC_SCRATCH			0x0278
+#define DC_UNLOCKDEV			0x027c
+#define DC_INTPULSEWIDTH		0x0280
+#define DC_TESTMODE			0x0284
+
+#endif

+ 1498 - 0
drivers/usb/isp1760/isp1760-udc.c

@@ -0,0 +1,1498 @@
+/*
+ * Driver for the NXP ISP1761 device controller
+ *
+ * Copyright 2014 Ideas on Board Oy
+ *
+ * Contacts:
+ *	Laurent Pinchart <laurent.pinchart@ideasonboard.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ */
+
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/list.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/timer.h>
+#include <linux/usb.h>
+
+#include "isp1760-core.h"
+#include "isp1760-regs.h"
+#include "isp1760-udc.h"
+
+#define ISP1760_VBUS_POLL_INTERVAL	msecs_to_jiffies(500)
+
+struct isp1760_request {
+	struct usb_request req;
+	struct list_head queue;
+	struct isp1760_ep *ep;
+	unsigned int packet_size;
+};
+
+static inline struct isp1760_udc *gadget_to_udc(struct usb_gadget *gadget)
+{
+	return container_of(gadget, struct isp1760_udc, gadget);
+}
+
+static inline struct isp1760_ep *ep_to_udc_ep(struct usb_ep *ep)
+{
+	return container_of(ep, struct isp1760_ep, ep);
+}
+
+static inline struct isp1760_request *req_to_udc_req(struct usb_request *req)
+{
+	return container_of(req, struct isp1760_request, req);
+}
+
+static inline u32 isp1760_udc_read(struct isp1760_udc *udc, u16 reg)
+{
+	return isp1760_read32(udc->regs, reg);
+}
+
+static inline void isp1760_udc_write(struct isp1760_udc *udc, u16 reg, u32 val)
+{
+	isp1760_write32(udc->regs, reg, val);
+}
+
+/* -----------------------------------------------------------------------------
+ * Endpoint Management
+ */
+
+static struct isp1760_ep *isp1760_udc_find_ep(struct isp1760_udc *udc,
+					      u16 index)
+{
+	unsigned int i;
+
+	if (index == 0)
+		return &udc->ep[0];
+
+	for (i = 1; i < ARRAY_SIZE(udc->ep); ++i) {
+		if (udc->ep[i].addr == index)
+			return udc->ep[i].desc ? &udc->ep[i] : NULL;
+	}
+
+	return NULL;
+}
+
+static void __isp1760_udc_select_ep(struct isp1760_ep *ep, int dir)
+{
+	isp1760_udc_write(ep->udc, DC_EPINDEX,
+			  DC_ENDPIDX(ep->addr & USB_ENDPOINT_NUMBER_MASK) |
+			  (dir == USB_DIR_IN ? DC_EPDIR : 0));
+}
+
+/**
+ * isp1760_udc_select_ep - Select an endpoint for register access
+ * @ep: The endpoint
+ *
+ * The ISP1761 endpoint registers are banked. This function selects the target
+ * endpoint for banked register access. The selection remains valid until the
+ * next call to this function, the next direct access to the EPINDEX register
+ * or the next reset, whichever comes first.
+ *
+ * Called with the UDC spinlock held.
+ */
+static void isp1760_udc_select_ep(struct isp1760_ep *ep)
+{
+	__isp1760_udc_select_ep(ep, ep->addr & USB_ENDPOINT_DIR_MASK);
+}
+
+/* Called with the UDC spinlock held. */
+static void isp1760_udc_ctrl_send_status(struct isp1760_ep *ep, int dir)
+{
+	struct isp1760_udc *udc = ep->udc;
+
+	/*
+	 * Proceed to the status stage. The status stage data packet flows in
+	 * the direction opposite to the data stage data packets, we thus need
+	 * to select the OUT/IN endpoint for IN/OUT transfers.
+	 */
+	isp1760_udc_write(udc, DC_EPINDEX, DC_ENDPIDX(0) |
+			  (dir == USB_DIR_IN ? 0 : DC_EPDIR));
+	isp1760_udc_write(udc, DC_CTRLFUNC, DC_STATUS);
+
+	/*
+	 * The hardware will terminate the request automatically and go back to
+	 * the setup stage without notifying us.
+	 */
+	udc->ep0_state = ISP1760_CTRL_SETUP;
+}
+
+/* Called without the UDC spinlock held. */
+static void isp1760_udc_request_complete(struct isp1760_ep *ep,
+					 struct isp1760_request *req,
+					 int status)
+{
+	struct isp1760_udc *udc = ep->udc;
+	unsigned long flags;
+
+	dev_dbg(ep->udc->isp->dev, "completing request %p with status %d\n",
+		req, status);
+
+	req->ep = NULL;
+	req->req.status = status;
+	req->req.complete(&ep->ep, &req->req);
+
+	spin_lock_irqsave(&udc->lock, flags);
+
+	/*
+	 * When completing control OUT requests, move to the status stage after
+	 * calling the request complete callback. This gives the gadget an
+	 * opportunity to stall the control transfer if needed.
+	 */
+	if (status == 0 && ep->addr == 0 && udc->ep0_dir == USB_DIR_OUT)
+		isp1760_udc_ctrl_send_status(ep, USB_DIR_OUT);
+
+	spin_unlock_irqrestore(&udc->lock, flags);
+}
+
+static void isp1760_udc_ctrl_send_stall(struct isp1760_ep *ep)
+{
+	struct isp1760_udc *udc = ep->udc;
+	unsigned long flags;
+
+	dev_dbg(ep->udc->isp->dev, "%s(ep%02x)\n", __func__, ep->addr);
+
+	spin_lock_irqsave(&udc->lock, flags);
+
+	/* Stall both the IN and OUT endpoints. */
+	__isp1760_udc_select_ep(ep, USB_DIR_OUT);
+	isp1760_udc_write(udc, DC_CTRLFUNC, DC_STALL);
+	__isp1760_udc_select_ep(ep, USB_DIR_IN);
+	isp1760_udc_write(udc, DC_CTRLFUNC, DC_STALL);
+
+	/* A protocol stall completes the control transaction. */
+	udc->ep0_state = ISP1760_CTRL_SETUP;
+
+	spin_unlock_irqrestore(&udc->lock, flags);
+}
+
+/* -----------------------------------------------------------------------------
+ * Data Endpoints
+ */
+
+/* Called with the UDC spinlock held. */
+static bool isp1760_udc_receive(struct isp1760_ep *ep,
+				struct isp1760_request *req)
+{
+	struct isp1760_udc *udc = ep->udc;
+	unsigned int len;
+	u32 *buf;
+	int i;
+
+	isp1760_udc_select_ep(ep);
+	len = isp1760_udc_read(udc, DC_BUFLEN) & DC_DATACOUNT_MASK;
+
+	dev_dbg(udc->isp->dev, "%s: received %u bytes (%u/%u done)\n",
+		__func__, len, req->req.actual, req->req.length);
+
+	len = min(len, req->req.length - req->req.actual);
+
+	if (!len) {
+		/*
+		 * There's no data to be read from the FIFO, acknowledge the RX
+		 * interrupt by clearing the buffer.
+		 *
+		 * TODO: What if another packet arrives in the meantime ? The
+		 * datasheet doesn't clearly document how this should be
+		 * handled.
+		 */
+		isp1760_udc_write(udc, DC_CTRLFUNC, DC_CLBUF);
+		return false;
+	}
+
+	buf = req->req.buf + req->req.actual;
+
+	/*
+	 * Make sure not to read more than one extra byte, otherwise data from
+	 * the next packet might be removed from the FIFO.
+	 */
+	for (i = len; i > 2; i -= 4, ++buf)
+		*buf = le32_to_cpu(isp1760_udc_read(udc, DC_DATAPORT));
+	if (i > 0)
+		*(u16 *)buf = le16_to_cpu(readw(udc->regs + DC_DATAPORT));
+
+	req->req.actual += len;
+
+	/*
+	 * TODO: The short_not_ok flag isn't supported yet, but isn't used by
+	 * any gadget driver either.
+	 */
+
+	dev_dbg(udc->isp->dev,
+		"%s: req %p actual/length %u/%u maxpacket %u packet size %u\n",
+		__func__, req, req->req.actual, req->req.length, ep->maxpacket,
+		len);
+
+	ep->rx_pending = false;
+
+	/*
+	 * Complete the request if all data has been received or if a short
+	 * packet has been received.
+	 */
+	if (req->req.actual == req->req.length || len < ep->maxpacket) {
+		list_del(&req->queue);
+		return true;
+	}
+
+	return false;
+}
+
+static void isp1760_udc_transmit(struct isp1760_ep *ep,
+				 struct isp1760_request *req)
+{
+	struct isp1760_udc *udc = ep->udc;
+	u32 *buf = req->req.buf + req->req.actual;
+	int i;
+
+	req->packet_size = min(req->req.length - req->req.actual,
+			       ep->maxpacket);
+
+	dev_dbg(udc->isp->dev, "%s: transferring %u bytes (%u/%u done)\n",
+		__func__, req->packet_size, req->req.actual,
+		req->req.length);
+
+	__isp1760_udc_select_ep(ep, USB_DIR_IN);
+
+	if (req->packet_size)
+		isp1760_udc_write(udc, DC_BUFLEN, req->packet_size);
+
+	/*
+	 * Make sure not to write more than one extra byte, otherwise extra data
+	 * will stay in the FIFO and will be transmitted during the next control
+	 * request. The endpoint control CLBUF bit is supposed to allow flushing
+	 * the FIFO for this kind of conditions, but doesn't seem to work.
+	 */
+	for (i = req->packet_size; i > 2; i -= 4, ++buf)
+		isp1760_udc_write(udc, DC_DATAPORT, cpu_to_le32(*buf));
+	if (i > 0)
+		writew(cpu_to_le16(*(u16 *)buf), udc->regs + DC_DATAPORT);
+
+	if (ep->addr == 0)
+		isp1760_udc_write(udc, DC_CTRLFUNC, DC_DSEN);
+	if (!req->packet_size)
+		isp1760_udc_write(udc, DC_CTRLFUNC, DC_VENDP);
+}
+
+static void isp1760_ep_rx_ready(struct isp1760_ep *ep)
+{
+	struct isp1760_udc *udc = ep->udc;
+	struct isp1760_request *req;
+	bool complete;
+
+	spin_lock(&udc->lock);
+
+	if (ep->addr == 0 && udc->ep0_state != ISP1760_CTRL_DATA_OUT) {
+		spin_unlock(&udc->lock);
+		dev_dbg(udc->isp->dev, "%s: invalid ep0 state %u\n", __func__,
+			udc->ep0_state);
+		return;
+	}
+
+	if (ep->addr != 0 && !ep->desc) {
+		spin_unlock(&udc->lock);
+		dev_dbg(udc->isp->dev, "%s: ep%02x is disabled\n", __func__,
+			ep->addr);
+		return;
+	}
+
+	if (list_empty(&ep->queue)) {
+		ep->rx_pending = true;
+		spin_unlock(&udc->lock);
+		dev_dbg(udc->isp->dev, "%s: ep%02x (%p) has no request queued\n",
+			__func__, ep->addr, ep);
+		return;
+	}
+
+	req = list_first_entry(&ep->queue, struct isp1760_request,
+			       queue);
+	complete = isp1760_udc_receive(ep, req);
+
+	spin_unlock(&udc->lock);
+
+	if (complete)
+		isp1760_udc_request_complete(ep, req, 0);
+}
+
+static void isp1760_ep_tx_complete(struct isp1760_ep *ep)
+{
+	struct isp1760_udc *udc = ep->udc;
+	struct isp1760_request *complete = NULL;
+	struct isp1760_request *req;
+	bool need_zlp;
+
+	spin_lock(&udc->lock);
+
+	if (ep->addr == 0 && udc->ep0_state != ISP1760_CTRL_DATA_IN) {
+		spin_unlock(&udc->lock);
+		dev_dbg(udc->isp->dev, "TX IRQ: invalid endpoint state %u\n",
+			udc->ep0_state);
+		return;
+	}
+
+	if (list_empty(&ep->queue)) {
+		/*
+		 * This can happen for the control endpoint when the reply to
+		 * the GET_STATUS IN control request is sent directly by the
+		 * setup IRQ handler. Just proceed to the status stage.
+		 */
+		if (ep->addr == 0) {
+			isp1760_udc_ctrl_send_status(ep, USB_DIR_IN);
+			spin_unlock(&udc->lock);
+			return;
+		}
+
+		spin_unlock(&udc->lock);
+		dev_dbg(udc->isp->dev, "%s: ep%02x has no request queued\n",
+			__func__, ep->addr);
+		return;
+	}
+
+	req = list_first_entry(&ep->queue, struct isp1760_request,
+			       queue);
+	req->req.actual += req->packet_size;
+
+	need_zlp = req->req.actual == req->req.length &&
+		   !(req->req.length % ep->maxpacket) &&
+		   req->packet_size && req->req.zero;
+
+	dev_dbg(udc->isp->dev,
+		"TX IRQ: req %p actual/length %u/%u maxpacket %u packet size %u zero %u need zlp %u\n",
+		 req, req->req.actual, req->req.length, ep->maxpacket,
+		 req->packet_size, req->req.zero, need_zlp);
+
+	/*
+	 * Complete the request if all data has been sent and we don't need to
+	 * transmit a zero length packet.
+	 */
+	if (req->req.actual == req->req.length && !need_zlp) {
+		complete = req;
+		list_del(&req->queue);
+
+		if (ep->addr == 0)
+			isp1760_udc_ctrl_send_status(ep, USB_DIR_IN);
+
+		if (!list_empty(&ep->queue))
+			req = list_first_entry(&ep->queue,
+					       struct isp1760_request, queue);
+		else
+			req = NULL;
+	}
+
+	/*
+	 * Transmit the next packet or start the next request, if any.
+	 *
+	 * TODO: If the endpoint is stalled the next request shouldn't be
+	 * started, but what about the next packet ?
+	 */
+	if (req)
+		isp1760_udc_transmit(ep, req);
+
+	spin_unlock(&udc->lock);
+
+	if (complete)
+		isp1760_udc_request_complete(ep, complete, 0);
+}
+
+static int __isp1760_udc_set_halt(struct isp1760_ep *ep, bool halt)
+{
+	struct isp1760_udc *udc = ep->udc;
+
+	dev_dbg(udc->isp->dev, "%s: %s halt on ep%02x\n", __func__,
+		halt ? "set" : "clear", ep->addr);
+
+	if (ep->desc && usb_endpoint_xfer_isoc(ep->desc)) {
+		dev_dbg(udc->isp->dev, "%s: ep%02x is isochronous\n", __func__,
+			ep->addr);
+		return -EINVAL;
+	}
+
+	isp1760_udc_select_ep(ep);
+	isp1760_udc_write(udc, DC_CTRLFUNC, halt ? DC_STALL : 0);
+
+	if (ep->addr == 0) {
+		/* When halting the control endpoint, stall both IN and OUT. */
+		__isp1760_udc_select_ep(ep, USB_DIR_IN);
+		isp1760_udc_write(udc, DC_CTRLFUNC, halt ? DC_STALL : 0);
+	} else if (!halt) {
+		/* Reset the data PID by cycling the endpoint enable bit. */
+		u16 eptype = isp1760_udc_read(udc, DC_EPTYPE);
+
+		isp1760_udc_write(udc, DC_EPTYPE, eptype & ~DC_EPENABLE);
+		isp1760_udc_write(udc, DC_EPTYPE, eptype);
+
+		/*
+		 * Disabling the endpoint emptied the transmit FIFO, fill it
+		 * again if a request is pending.
+		 *
+		 * TODO: Does the gadget framework require synchronizatino with
+		 * the TX IRQ handler ?
+		 */
+		if ((ep->addr & USB_DIR_IN) && !list_empty(&ep->queue)) {
+			struct isp1760_request *req;
+
+			req = list_first_entry(&ep->queue,
+					       struct isp1760_request, queue);
+			isp1760_udc_transmit(ep, req);
+		}
+	}
+
+	ep->halted = halt;
+
+	return 0;
+}
+
+/* -----------------------------------------------------------------------------
+ * Control Endpoint
+ */
+
+static int isp1760_udc_get_status(struct isp1760_udc *udc,
+				  const struct usb_ctrlrequest *req)
+{
+	struct isp1760_ep *ep;
+	u16 status;
+
+	if (req->wLength != cpu_to_le16(2) || req->wValue != cpu_to_le16(0))
+		return -EINVAL;
+
+	switch (req->bRequestType) {
+	case USB_DIR_IN | USB_RECIP_DEVICE:
+		status = udc->devstatus;
+		break;
+
+	case USB_DIR_IN | USB_RECIP_INTERFACE:
+		status = 0;
+		break;
+
+	case USB_DIR_IN | USB_RECIP_ENDPOINT:
+		ep = isp1760_udc_find_ep(udc, le16_to_cpu(req->wIndex));
+		if (!ep)
+			return -EINVAL;
+
+		status = 0;
+		if (ep->halted)
+			status |= 1 << USB_ENDPOINT_HALT;
+		break;
+
+	default:
+		return -EINVAL;
+	}
+
+	isp1760_udc_write(udc, DC_EPINDEX, DC_ENDPIDX(0) | DC_EPDIR);
+	isp1760_udc_write(udc, DC_BUFLEN, 2);
+
+	writew(cpu_to_le16(status), udc->regs + DC_DATAPORT);
+
+	isp1760_udc_write(udc, DC_CTRLFUNC, DC_DSEN);
+
+	dev_dbg(udc->isp->dev, "%s: status 0x%04x\n", __func__, status);
+
+	return 0;
+}
+
+static int isp1760_udc_set_address(struct isp1760_udc *udc, u16 addr)
+{
+	if (addr > 127) {
+		dev_dbg(udc->isp->dev, "invalid device address %u\n", addr);
+		return -EINVAL;
+	}
+
+	if (udc->gadget.state != USB_STATE_DEFAULT &&
+	    udc->gadget.state != USB_STATE_ADDRESS) {
+		dev_dbg(udc->isp->dev, "can't set address in state %u\n",
+			udc->gadget.state);
+		return -EINVAL;
+	}
+
+	usb_gadget_set_state(&udc->gadget, addr ? USB_STATE_ADDRESS :
+			     USB_STATE_DEFAULT);
+
+	isp1760_udc_write(udc, DC_ADDRESS, DC_DEVEN | addr);
+
+	spin_lock(&udc->lock);
+	isp1760_udc_ctrl_send_status(&udc->ep[0], USB_DIR_OUT);
+	spin_unlock(&udc->lock);
+
+	return 0;
+}
+
+static bool isp1760_ep0_setup_standard(struct isp1760_udc *udc,
+				       struct usb_ctrlrequest *req)
+{
+	bool stall;
+
+	switch (req->bRequest) {
+	case USB_REQ_GET_STATUS:
+		return isp1760_udc_get_status(udc, req);
+
+	case USB_REQ_CLEAR_FEATURE:
+		switch (req->bRequestType) {
+		case USB_DIR_OUT | USB_RECIP_DEVICE: {
+			/* TODO: Handle remote wakeup feature. */
+			return true;
+		}
+
+		case USB_DIR_OUT | USB_RECIP_ENDPOINT: {
+			u16 index = le16_to_cpu(req->wIndex);
+			struct isp1760_ep *ep;
+
+			if (req->wLength != cpu_to_le16(0) ||
+			    req->wValue != cpu_to_le16(USB_ENDPOINT_HALT))
+				return true;
+
+			ep = isp1760_udc_find_ep(udc, index);
+			if (!ep)
+				return true;
+
+			spin_lock(&udc->lock);
+
+			/*
+			 * If the endpoint is wedged only the gadget can clear
+			 * the halt feature. Pretend success in that case, but
+			 * keep the endpoint halted.
+			 */
+			if (!ep->wedged)
+				stall = __isp1760_udc_set_halt(ep, false);
+			else
+				stall = false;
+
+			if (!stall)
+				isp1760_udc_ctrl_send_status(&udc->ep[0],
+							     USB_DIR_OUT);
+
+			spin_unlock(&udc->lock);
+			return stall;
+		}
+
+		default:
+			return true;
+		}
+		break;
+
+	case USB_REQ_SET_FEATURE:
+		switch (req->bRequestType) {
+		case USB_DIR_OUT | USB_RECIP_DEVICE: {
+			/* TODO: Handle remote wakeup and test mode features */
+			return true;
+		}
+
+		case USB_DIR_OUT | USB_RECIP_ENDPOINT: {
+			u16 index = le16_to_cpu(req->wIndex);
+			struct isp1760_ep *ep;
+
+			if (req->wLength != cpu_to_le16(0) ||
+			    req->wValue != cpu_to_le16(USB_ENDPOINT_HALT))
+				return true;
+
+			ep = isp1760_udc_find_ep(udc, index);
+			if (!ep)
+				return true;
+
+			spin_lock(&udc->lock);
+
+			stall = __isp1760_udc_set_halt(ep, true);
+			if (!stall)
+				isp1760_udc_ctrl_send_status(&udc->ep[0],
+							     USB_DIR_OUT);
+
+			spin_unlock(&udc->lock);
+			return stall;
+		}
+
+		default:
+			return true;
+		}
+		break;
+
+	case USB_REQ_SET_ADDRESS:
+		if (req->bRequestType != (USB_DIR_OUT | USB_RECIP_DEVICE))
+			return true;
+
+		return isp1760_udc_set_address(udc, le16_to_cpu(req->wValue));
+
+	case USB_REQ_SET_CONFIGURATION:
+		if (req->bRequestType != (USB_DIR_OUT | USB_RECIP_DEVICE))
+			return true;
+
+		if (udc->gadget.state != USB_STATE_ADDRESS &&
+		    udc->gadget.state != USB_STATE_CONFIGURED)
+			return true;
+
+		stall = udc->driver->setup(&udc->gadget, req) < 0;
+		if (stall)
+			return true;
+
+		usb_gadget_set_state(&udc->gadget, req->wValue ?
+				     USB_STATE_CONFIGURED : USB_STATE_ADDRESS);
+
+		/*
+		 * SET_CONFIGURATION (and SET_INTERFACE) must reset the halt
+		 * feature on all endpoints. There is however no need to do so
+		 * explicitly here as the gadget driver will disable and
+		 * reenable endpoints, clearing the halt feature.
+		 */
+		return false;
+
+	default:
+		return udc->driver->setup(&udc->gadget, req) < 0;
+	}
+}
+
+static void isp1760_ep0_setup(struct isp1760_udc *udc)
+{
+	union {
+		struct usb_ctrlrequest r;
+		u32 data[2];
+	} req;
+	unsigned int count;
+	bool stall = false;
+
+	spin_lock(&udc->lock);
+
+	isp1760_udc_write(udc, DC_EPINDEX, DC_EP0SETUP);
+
+	count = isp1760_udc_read(udc, DC_BUFLEN) & DC_DATACOUNT_MASK;
+	if (count != sizeof(req)) {
+		spin_unlock(&udc->lock);
+
+		dev_err(udc->isp->dev, "invalid length %u for setup packet\n",
+			count);
+
+		isp1760_udc_ctrl_send_stall(&udc->ep[0]);
+		return;
+	}
+
+	req.data[0] = isp1760_udc_read(udc, DC_DATAPORT);
+	req.data[1] = isp1760_udc_read(udc, DC_DATAPORT);
+
+	if (udc->ep0_state != ISP1760_CTRL_SETUP) {
+		spin_unlock(&udc->lock);
+		dev_dbg(udc->isp->dev, "unexpected SETUP packet\n");
+		return;
+	}
+
+	/* Move to the data stage. */
+	if (!req.r.wLength)
+		udc->ep0_state = ISP1760_CTRL_STATUS;
+	else if (req.r.bRequestType & USB_DIR_IN)
+		udc->ep0_state = ISP1760_CTRL_DATA_IN;
+	else
+		udc->ep0_state = ISP1760_CTRL_DATA_OUT;
+
+	udc->ep0_dir = req.r.bRequestType & USB_DIR_IN;
+	udc->ep0_length = le16_to_cpu(req.r.wLength);
+
+	spin_unlock(&udc->lock);
+
+	dev_dbg(udc->isp->dev,
+		"%s: bRequestType 0x%02x bRequest 0x%02x wValue 0x%04x wIndex 0x%04x wLength 0x%04x\n",
+		__func__, req.r.bRequestType, req.r.bRequest,
+		le16_to_cpu(req.r.wValue), le16_to_cpu(req.r.wIndex),
+		le16_to_cpu(req.r.wLength));
+
+	if ((req.r.bRequestType & USB_TYPE_MASK) == USB_TYPE_STANDARD)
+		stall = isp1760_ep0_setup_standard(udc, &req.r);
+	else
+		stall = udc->driver->setup(&udc->gadget, &req.r) < 0;
+
+	if (stall)
+		isp1760_udc_ctrl_send_stall(&udc->ep[0]);
+}
+
+/* -----------------------------------------------------------------------------
+ * Gadget Endpoint Operations
+ */
+
+static int isp1760_ep_enable(struct usb_ep *ep,
+			     const struct usb_endpoint_descriptor *desc)
+{
+	struct isp1760_ep *uep = ep_to_udc_ep(ep);
+	struct isp1760_udc *udc = uep->udc;
+	unsigned long flags;
+	unsigned int type;
+
+	dev_dbg(uep->udc->isp->dev, "%s\n", __func__);
+
+	/*
+	 * Validate the descriptor. The control endpoint can't be enabled
+	 * manually.
+	 */
+	if (desc->bDescriptorType != USB_DT_ENDPOINT ||
+	    desc->bEndpointAddress == 0 ||
+	    desc->bEndpointAddress != uep->addr ||
+	    le16_to_cpu(desc->wMaxPacketSize) > ep->maxpacket) {
+		dev_dbg(udc->isp->dev,
+			"%s: invalid descriptor type %u addr %02x ep addr %02x max packet size %u/%u\n",
+			__func__, desc->bDescriptorType,
+			desc->bEndpointAddress, uep->addr,
+			le16_to_cpu(desc->wMaxPacketSize), ep->maxpacket);
+		return -EINVAL;
+	}
+
+	switch (usb_endpoint_type(desc)) {
+	case USB_ENDPOINT_XFER_ISOC:
+		type = DC_ENDPTYP_ISOC;
+		break;
+	case USB_ENDPOINT_XFER_BULK:
+		type = DC_ENDPTYP_BULK;
+		break;
+	case USB_ENDPOINT_XFER_INT:
+		type = DC_ENDPTYP_INTERRUPT;
+		break;
+	case USB_ENDPOINT_XFER_CONTROL:
+	default:
+		dev_dbg(udc->isp->dev, "%s: control endpoints unsupported\n",
+			__func__);
+		return -EINVAL;
+	}
+
+	spin_lock_irqsave(&udc->lock, flags);
+
+	uep->desc = desc;
+	uep->maxpacket = le16_to_cpu(desc->wMaxPacketSize);
+	uep->rx_pending = false;
+	uep->halted = false;
+	uep->wedged = false;
+
+	isp1760_udc_select_ep(uep);
+	isp1760_udc_write(udc, DC_EPMAXPKTSZ, uep->maxpacket);
+	isp1760_udc_write(udc, DC_BUFLEN, uep->maxpacket);
+	isp1760_udc_write(udc, DC_EPTYPE, DC_EPENABLE | type);
+
+	spin_unlock_irqrestore(&udc->lock, flags);
+
+	return 0;
+}
+
+static int isp1760_ep_disable(struct usb_ep *ep)
+{
+	struct isp1760_ep *uep = ep_to_udc_ep(ep);
+	struct isp1760_udc *udc = uep->udc;
+	struct isp1760_request *req, *nreq;
+	LIST_HEAD(req_list);
+	unsigned long flags;
+
+	dev_dbg(udc->isp->dev, "%s\n", __func__);
+
+	spin_lock_irqsave(&udc->lock, flags);
+
+	if (!uep->desc) {
+		dev_dbg(udc->isp->dev, "%s: endpoint not enabled\n", __func__);
+		spin_unlock_irqrestore(&udc->lock, flags);
+		return -EINVAL;
+	}
+
+	uep->desc = NULL;
+	uep->maxpacket = 0;
+
+	isp1760_udc_select_ep(uep);
+	isp1760_udc_write(udc, DC_EPTYPE, 0);
+
+	/* TODO Synchronize with the IRQ handler */
+
+	list_splice_init(&uep->queue, &req_list);
+
+	spin_unlock_irqrestore(&udc->lock, flags);
+
+	list_for_each_entry_safe(req, nreq, &req_list, queue) {
+		list_del(&req->queue);
+		isp1760_udc_request_complete(uep, req, -ESHUTDOWN);
+	}
+
+	return 0;
+}
+
+static struct usb_request *isp1760_ep_alloc_request(struct usb_ep *ep,
+						    gfp_t gfp_flags)
+{
+	struct isp1760_request *req;
+
+	req = kzalloc(sizeof(*req), gfp_flags);
+
+	return &req->req;
+}
+
+static void isp1760_ep_free_request(struct usb_ep *ep, struct usb_request *_req)
+{
+	struct isp1760_request *req = req_to_udc_req(_req);
+
+	kfree(req);
+}
+
+static int isp1760_ep_queue(struct usb_ep *ep, struct usb_request *_req,
+			    gfp_t gfp_flags)
+{
+	struct isp1760_request *req = req_to_udc_req(_req);
+	struct isp1760_ep *uep = ep_to_udc_ep(ep);
+	struct isp1760_udc *udc = uep->udc;
+	bool complete = false;
+	unsigned long flags;
+	int ret = 0;
+
+	_req->status = -EINPROGRESS;
+	_req->actual = 0;
+
+	spin_lock_irqsave(&udc->lock, flags);
+
+	dev_dbg(udc->isp->dev,
+		"%s: req %p (%u bytes%s) ep %p(0x%02x)\n", __func__, _req,
+		_req->length, _req->zero ? " (zlp)" : "", uep, uep->addr);
+
+	req->ep = uep;
+
+	if (uep->addr == 0) {
+		if (_req->length != udc->ep0_length &&
+		    udc->ep0_state != ISP1760_CTRL_DATA_IN) {
+			dev_dbg(udc->isp->dev,
+				"%s: invalid length %u for req %p\n",
+				__func__, _req->length, req);
+			ret = -EINVAL;
+			goto done;
+		}
+
+		switch (udc->ep0_state) {
+		case ISP1760_CTRL_DATA_IN:
+			dev_dbg(udc->isp->dev, "%s: transmitting req %p\n",
+				__func__, req);
+
+			list_add_tail(&req->queue, &uep->queue);
+			isp1760_udc_transmit(uep, req);
+			break;
+
+		case ISP1760_CTRL_DATA_OUT:
+			list_add_tail(&req->queue, &uep->queue);
+			__isp1760_udc_select_ep(uep, USB_DIR_OUT);
+			isp1760_udc_write(udc, DC_CTRLFUNC, DC_DSEN);
+			break;
+
+		case ISP1760_CTRL_STATUS:
+			complete = true;
+			break;
+
+		default:
+			dev_dbg(udc->isp->dev, "%s: invalid ep0 state\n",
+				__func__);
+			ret = -EINVAL;
+			break;
+		}
+	} else if (uep->desc) {
+		bool empty = list_empty(&uep->queue);
+
+		list_add_tail(&req->queue, &uep->queue);
+		if ((uep->addr & USB_DIR_IN) && !uep->halted && empty)
+			isp1760_udc_transmit(uep, req);
+		else if (!(uep->addr & USB_DIR_IN) && uep->rx_pending)
+			complete = isp1760_udc_receive(uep, req);
+	} else {
+		dev_dbg(udc->isp->dev,
+			"%s: can't queue request to disabled ep%02x\n",
+			__func__, uep->addr);
+		ret = -ESHUTDOWN;
+	}
+
+done:
+	if (ret < 0)
+		req->ep = NULL;
+
+	spin_unlock_irqrestore(&udc->lock, flags);
+
+	if (complete)
+		isp1760_udc_request_complete(uep, req, 0);
+
+	return ret;
+}
+
+static int isp1760_ep_dequeue(struct usb_ep *ep, struct usb_request *_req)
+{
+	struct isp1760_request *req = req_to_udc_req(_req);
+	struct isp1760_ep *uep = ep_to_udc_ep(ep);
+	struct isp1760_udc *udc = uep->udc;
+	unsigned long flags;
+
+	dev_dbg(uep->udc->isp->dev, "%s(ep%02x)\n", __func__, uep->addr);
+
+	spin_lock_irqsave(&udc->lock, flags);
+
+	if (req->ep != uep)
+		req = NULL;
+	else
+		list_del(&req->queue);
+
+	spin_unlock_irqrestore(&udc->lock, flags);
+
+	if (!req)
+		return -EINVAL;
+
+	isp1760_udc_request_complete(uep, req, -ECONNRESET);
+	return 0;
+}
+
+static int __isp1760_ep_set_halt(struct isp1760_ep *uep, bool stall, bool wedge)
+{
+	struct isp1760_udc *udc = uep->udc;
+	int ret;
+
+	if (!uep->addr) {
+		/*
+		 * Halting the control endpoint is only valid as a delayed error
+		 * response to a SETUP packet. Make sure EP0 is in the right
+		 * stage and that the gadget isn't trying to clear the halt
+		 * condition.
+		 */
+		if (WARN_ON(udc->ep0_state == ISP1760_CTRL_SETUP || !stall ||
+			     wedge)) {
+			return -EINVAL;
+		}
+	}
+
+	if (uep->addr && !uep->desc) {
+		dev_dbg(udc->isp->dev, "%s: ep%02x is disabled\n", __func__,
+			uep->addr);
+		return -EINVAL;
+	}
+
+	if (uep->addr & USB_DIR_IN) {
+		/* Refuse to halt IN endpoints with active transfers. */
+		if (!list_empty(&uep->queue)) {
+			dev_dbg(udc->isp->dev,
+				"%s: ep%02x has request pending\n", __func__,
+				uep->addr);
+			return -EAGAIN;
+		}
+	}
+
+	ret = __isp1760_udc_set_halt(uep, stall);
+	if (ret < 0)
+		return ret;
+
+	if (!uep->addr) {
+		/*
+		 * Stalling EP0 completes the control transaction, move back to
+		 * the SETUP state.
+		 */
+		udc->ep0_state = ISP1760_CTRL_SETUP;
+		return 0;
+	}
+
+	if (wedge)
+		uep->wedged = true;
+	else if (!stall)
+		uep->wedged = false;
+
+	return 0;
+}
+
+static int isp1760_ep_set_halt(struct usb_ep *ep, int value)
+{
+	struct isp1760_ep *uep = ep_to_udc_ep(ep);
+	unsigned long flags;
+	int ret;
+
+	dev_dbg(uep->udc->isp->dev, "%s: %s halt on ep%02x\n", __func__,
+		value ? "set" : "clear", uep->addr);
+
+	spin_lock_irqsave(&uep->udc->lock, flags);
+	ret = __isp1760_ep_set_halt(uep, value, false);
+	spin_unlock_irqrestore(&uep->udc->lock, flags);
+
+	return ret;
+}
+
+static int isp1760_ep_set_wedge(struct usb_ep *ep)
+{
+	struct isp1760_ep *uep = ep_to_udc_ep(ep);
+	unsigned long flags;
+	int ret;
+
+	dev_dbg(uep->udc->isp->dev, "%s: set wedge on ep%02x)\n", __func__,
+		uep->addr);
+
+	spin_lock_irqsave(&uep->udc->lock, flags);
+	ret = __isp1760_ep_set_halt(uep, true, true);
+	spin_unlock_irqrestore(&uep->udc->lock, flags);
+
+	return ret;
+}
+
+static void isp1760_ep_fifo_flush(struct usb_ep *ep)
+{
+	struct isp1760_ep *uep = ep_to_udc_ep(ep);
+	struct isp1760_udc *udc = uep->udc;
+	unsigned long flags;
+
+	spin_lock_irqsave(&udc->lock, flags);
+
+	isp1760_udc_select_ep(uep);
+
+	/*
+	 * Set the CLBUF bit twice to flush both buffers in case double
+	 * buffering is enabled.
+	 */
+	isp1760_udc_write(udc, DC_CTRLFUNC, DC_CLBUF);
+	isp1760_udc_write(udc, DC_CTRLFUNC, DC_CLBUF);
+
+	spin_unlock_irqrestore(&udc->lock, flags);
+}
+
+static const struct usb_ep_ops isp1760_ep_ops = {
+	.enable = isp1760_ep_enable,
+	.disable = isp1760_ep_disable,
+	.alloc_request = isp1760_ep_alloc_request,
+	.free_request = isp1760_ep_free_request,
+	.queue = isp1760_ep_queue,
+	.dequeue = isp1760_ep_dequeue,
+	.set_halt = isp1760_ep_set_halt,
+	.set_wedge = isp1760_ep_set_wedge,
+	.fifo_flush = isp1760_ep_fifo_flush,
+};
+
+/* -----------------------------------------------------------------------------
+ * Device States
+ */
+
+/* Called with the UDC spinlock held. */
+static void isp1760_udc_connect(struct isp1760_udc *udc)
+{
+	usb_gadget_set_state(&udc->gadget, USB_STATE_POWERED);
+	mod_timer(&udc->vbus_timer, jiffies + ISP1760_VBUS_POLL_INTERVAL);
+}
+
+/* Called with the UDC spinlock held. */
+static void isp1760_udc_disconnect(struct isp1760_udc *udc)
+{
+	if (udc->gadget.state < USB_STATE_POWERED)
+		return;
+
+	dev_dbg(udc->isp->dev, "Device disconnected in state %u\n",
+		 udc->gadget.state);
+
+	udc->gadget.speed = USB_SPEED_UNKNOWN;
+	usb_gadget_set_state(&udc->gadget, USB_STATE_ATTACHED);
+
+	if (udc->driver->disconnect)
+		udc->driver->disconnect(&udc->gadget);
+
+	del_timer(&udc->vbus_timer);
+
+	/* TODO Reset all endpoints ? */
+}
+
+static void isp1760_udc_init_hw(struct isp1760_udc *udc)
+{
+	/*
+	 * The device controller currently shares its interrupt with the host
+	 * controller, the DC_IRQ polarity and signaling mode are ignored. Set
+	 * the to active-low level-triggered.
+	 *
+	 * Configure the control, in and out pipes to generate interrupts on
+	 * ACK tokens only (and NYET for the out pipe). The default
+	 * configuration also generates an interrupt on the first NACK token.
+	 */
+	isp1760_udc_write(udc, DC_INTCONF, DC_CDBGMOD_ACK | DC_DDBGMODIN_ACK |
+			  DC_DDBGMODOUT_ACK_NYET);
+
+	isp1760_udc_write(udc, DC_INTENABLE, DC_IEPRXTX(7) | DC_IEPRXTX(6) |
+			  DC_IEPRXTX(5) | DC_IEPRXTX(4) | DC_IEPRXTX(3) |
+			  DC_IEPRXTX(2) | DC_IEPRXTX(1) | DC_IEPRXTX(0) |
+			  DC_IEP0SETUP | DC_IEVBUS | DC_IERESM | DC_IESUSP |
+			  DC_IEHS_STA | DC_IEBRST);
+
+	if (udc->connected)
+		isp1760_set_pullup(udc->isp, true);
+
+	isp1760_udc_write(udc, DC_ADDRESS, DC_DEVEN);
+}
+
+static void isp1760_udc_reset(struct isp1760_udc *udc)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&udc->lock, flags);
+
+	/*
+	 * The bus reset has reset most registers to their default value,
+	 * reinitialize the UDC hardware.
+	 */
+	isp1760_udc_init_hw(udc);
+
+	udc->ep0_state = ISP1760_CTRL_SETUP;
+	udc->gadget.speed = USB_SPEED_FULL;
+
+	usb_gadget_udc_reset(&udc->gadget, udc->driver);
+
+	spin_unlock_irqrestore(&udc->lock, flags);
+}
+
+static void isp1760_udc_suspend(struct isp1760_udc *udc)
+{
+	if (udc->gadget.state < USB_STATE_DEFAULT)
+		return;
+
+	if (udc->driver->suspend)
+		udc->driver->suspend(&udc->gadget);
+}
+
+static void isp1760_udc_resume(struct isp1760_udc *udc)
+{
+	if (udc->gadget.state < USB_STATE_DEFAULT)
+		return;
+
+	if (udc->driver->resume)
+		udc->driver->resume(&udc->gadget);
+}
+
+/* -----------------------------------------------------------------------------
+ * Gadget Operations
+ */
+
+static int isp1760_udc_get_frame(struct usb_gadget *gadget)
+{
+	struct isp1760_udc *udc = gadget_to_udc(gadget);
+
+	return isp1760_udc_read(udc, DC_FRAMENUM) & ((1 << 11) - 1);
+}
+
+static int isp1760_udc_wakeup(struct usb_gadget *gadget)
+{
+	struct isp1760_udc *udc = gadget_to_udc(gadget);
+
+	dev_dbg(udc->isp->dev, "%s\n", __func__);
+	return -ENOTSUPP;
+}
+
+static int isp1760_udc_set_selfpowered(struct usb_gadget *gadget,
+				       int is_selfpowered)
+{
+	struct isp1760_udc *udc = gadget_to_udc(gadget);
+
+	if (is_selfpowered)
+		udc->devstatus |= 1 << USB_DEVICE_SELF_POWERED;
+	else
+		udc->devstatus &= ~(1 << USB_DEVICE_SELF_POWERED);
+
+	return 0;
+}
+
+static int isp1760_udc_pullup(struct usb_gadget *gadget, int is_on)
+{
+	struct isp1760_udc *udc = gadget_to_udc(gadget);
+
+	isp1760_set_pullup(udc->isp, is_on);
+	udc->connected = is_on;
+
+	return 0;
+}
+
+static int isp1760_udc_start(struct usb_gadget *gadget,
+			     struct usb_gadget_driver *driver)
+{
+	struct isp1760_udc *udc = gadget_to_udc(gadget);
+
+	/* The hardware doesn't support low speed. */
+	if (driver->max_speed < USB_SPEED_FULL) {
+		dev_err(udc->isp->dev, "Invalid gadget driver\n");
+		return -EINVAL;
+	}
+
+	spin_lock(&udc->lock);
+
+	if (udc->driver) {
+		dev_err(udc->isp->dev, "UDC already has a gadget driver\n");
+		spin_unlock(&udc->lock);
+		return -EBUSY;
+	}
+
+	udc->driver = driver;
+
+	spin_unlock(&udc->lock);
+
+	dev_dbg(udc->isp->dev, "starting UDC with driver %s\n",
+		driver->function);
+
+	udc->devstatus = 0;
+	udc->connected = true;
+
+	usb_gadget_set_state(&udc->gadget, USB_STATE_ATTACHED);
+
+	/* DMA isn't supported yet, don't enable the DMA clock. */
+	isp1760_udc_write(udc, DC_MODE, DC_GLINTENA);
+
+	isp1760_udc_init_hw(udc);
+
+	dev_dbg(udc->isp->dev, "UDC started with driver %s\n",
+		driver->function);
+
+	return 0;
+}
+
+static int isp1760_udc_stop(struct usb_gadget *gadget)
+{
+	struct isp1760_udc *udc = gadget_to_udc(gadget);
+
+	dev_dbg(udc->isp->dev, "%s\n", __func__);
+
+	del_timer_sync(&udc->vbus_timer);
+
+	isp1760_udc_write(udc, DC_MODE, 0);
+
+	spin_lock(&udc->lock);
+	udc->driver = NULL;
+	spin_unlock(&udc->lock);
+
+	return 0;
+}
+
+static struct usb_gadget_ops isp1760_udc_ops = {
+	.get_frame = isp1760_udc_get_frame,
+	.wakeup = isp1760_udc_wakeup,
+	.set_selfpowered = isp1760_udc_set_selfpowered,
+	.pullup = isp1760_udc_pullup,
+	.udc_start = isp1760_udc_start,
+	.udc_stop = isp1760_udc_stop,
+};
+
+/* -----------------------------------------------------------------------------
+ * Interrupt Handling
+ */
+
+static irqreturn_t isp1760_udc_irq(int irq, void *dev)
+{
+	struct isp1760_udc *udc = dev;
+	unsigned int i;
+	u32 status;
+
+	status = isp1760_udc_read(udc, DC_INTERRUPT)
+	       & isp1760_udc_read(udc, DC_INTENABLE);
+	isp1760_udc_write(udc, DC_INTERRUPT, status);
+
+	if (status & DC_IEVBUS) {
+		dev_dbg(udc->isp->dev, "%s(VBUS)\n", __func__);
+		/* The VBUS interrupt is only triggered when VBUS appears. */
+		spin_lock(&udc->lock);
+		isp1760_udc_connect(udc);
+		spin_unlock(&udc->lock);
+	}
+
+	if (status & DC_IEBRST) {
+		dev_dbg(udc->isp->dev, "%s(BRST)\n", __func__);
+
+		isp1760_udc_reset(udc);
+	}
+
+	for (i = 0; i <= 7; ++i) {
+		struct isp1760_ep *ep = &udc->ep[i*2];
+
+		if (status & DC_IEPTX(i)) {
+			dev_dbg(udc->isp->dev, "%s(EPTX%u)\n", __func__, i);
+			isp1760_ep_tx_complete(ep);
+		}
+
+		if (status & DC_IEPRX(i)) {
+			dev_dbg(udc->isp->dev, "%s(EPRX%u)\n", __func__, i);
+			isp1760_ep_rx_ready(i ? ep - 1 : ep);
+		}
+	}
+
+	if (status & DC_IEP0SETUP) {
+		dev_dbg(udc->isp->dev, "%s(EP0SETUP)\n", __func__);
+
+		isp1760_ep0_setup(udc);
+	}
+
+	if (status & DC_IERESM) {
+		dev_dbg(udc->isp->dev, "%s(RESM)\n", __func__);
+		isp1760_udc_resume(udc);
+	}
+
+	if (status & DC_IESUSP) {
+		dev_dbg(udc->isp->dev, "%s(SUSP)\n", __func__);
+
+		spin_lock(&udc->lock);
+		if (!(isp1760_udc_read(udc, DC_MODE) & DC_VBUSSTAT))
+			isp1760_udc_disconnect(udc);
+		else
+			isp1760_udc_suspend(udc);
+		spin_unlock(&udc->lock);
+	}
+
+	if (status & DC_IEHS_STA) {
+		dev_dbg(udc->isp->dev, "%s(HS_STA)\n", __func__);
+		udc->gadget.speed = USB_SPEED_HIGH;
+	}
+
+	return status ? IRQ_HANDLED : IRQ_NONE;
+}
+
+static void isp1760_udc_vbus_poll(unsigned long data)
+{
+	struct isp1760_udc *udc = (struct isp1760_udc *)data;
+	unsigned long flags;
+
+	spin_lock_irqsave(&udc->lock, flags);
+
+	if (!(isp1760_udc_read(udc, DC_MODE) & DC_VBUSSTAT))
+		isp1760_udc_disconnect(udc);
+	else if (udc->gadget.state >= USB_STATE_POWERED)
+		mod_timer(&udc->vbus_timer,
+			  jiffies + ISP1760_VBUS_POLL_INTERVAL);
+
+	spin_unlock_irqrestore(&udc->lock, flags);
+}
+
+/* -----------------------------------------------------------------------------
+ * Registration
+ */
+
+static void isp1760_udc_init_eps(struct isp1760_udc *udc)
+{
+	unsigned int i;
+
+	INIT_LIST_HEAD(&udc->gadget.ep_list);
+
+	for (i = 0; i < ARRAY_SIZE(udc->ep); ++i) {
+		struct isp1760_ep *ep = &udc->ep[i];
+		unsigned int ep_num = (i + 1) / 2;
+		bool is_in = !(i & 1);
+
+		ep->udc = udc;
+
+		INIT_LIST_HEAD(&ep->queue);
+
+		ep->addr = (ep_num && is_in ? USB_DIR_IN : USB_DIR_OUT)
+			 | ep_num;
+		ep->desc = NULL;
+
+		sprintf(ep->name, "ep%u%s", ep_num,
+			ep_num ? (is_in ? "in" : "out") : "");
+
+		ep->ep.ops = &isp1760_ep_ops;
+		ep->ep.name = ep->name;
+
+		/*
+		 * Hardcode the maximum packet sizes for now, to 64 bytes for
+		 * the control endpoint and 512 bytes for all other endpoints.
+		 * This fits in the 8kB FIFO without double-buffering.
+		 */
+		if (ep_num == 0) {
+			ep->ep.maxpacket = 64;
+			ep->maxpacket = 64;
+			udc->gadget.ep0 = &ep->ep;
+		} else {
+			ep->ep.maxpacket = 512;
+			ep->maxpacket = 0;
+			list_add_tail(&ep->ep.ep_list, &udc->gadget.ep_list);
+		}
+	}
+}
+
+static int isp1760_udc_init(struct isp1760_udc *udc)
+{
+	u16 scratch;
+	u32 chipid;
+
+	/*
+	 * Check that the controller is present by writing to the scratch
+	 * register, modifying the bus pattern by reading from the chip ID
+	 * register, and reading the scratch register value back. The chip ID
+	 * and scratch register contents must match the expected values.
+	 */
+	isp1760_udc_write(udc, DC_SCRATCH, 0xbabe);
+	chipid = isp1760_udc_read(udc, DC_CHIPID);
+	scratch = isp1760_udc_read(udc, DC_SCRATCH);
+
+	if (scratch != 0xbabe) {
+		dev_err(udc->isp->dev,
+			"udc: scratch test failed (0x%04x/0x%08x)\n",
+			scratch, chipid);
+		return -ENODEV;
+	}
+
+	if (chipid != 0x00011582) {
+		dev_err(udc->isp->dev, "udc: invalid chip ID 0x%08x\n", chipid);
+		return -ENODEV;
+	}
+
+	/* Reset the device controller. */
+	isp1760_udc_write(udc, DC_MODE, DC_SFRESET);
+	usleep_range(10000, 11000);
+	isp1760_udc_write(udc, DC_MODE, 0);
+	usleep_range(10000, 11000);
+
+	return 0;
+}
+
+int isp1760_udc_register(struct isp1760_device *isp, int irq,
+			 unsigned long irqflags)
+{
+	struct isp1760_udc *udc = &isp->udc;
+	const char *devname;
+	int ret;
+
+	udc->irq = -1;
+	udc->isp = isp;
+	udc->regs = isp->regs;
+
+	spin_lock_init(&udc->lock);
+	setup_timer(&udc->vbus_timer, isp1760_udc_vbus_poll,
+		    (unsigned long)udc);
+
+	ret = isp1760_udc_init(udc);
+	if (ret < 0)
+		return ret;
+
+	devname = dev_name(isp->dev);
+	udc->irqname = kmalloc(strlen(devname) + 7, GFP_KERNEL);
+	if (!udc->irqname)
+		return -ENOMEM;
+
+	sprintf(udc->irqname, "%s (udc)", devname);
+
+	ret = request_irq(irq, isp1760_udc_irq, IRQF_SHARED | IRQF_DISABLED |
+			  irqflags, udc->irqname, udc);
+	if (ret < 0)
+		goto error;
+
+	udc->irq = irq;
+
+	/*
+	 * Initialize the gadget static fields and register its device. Gadget
+	 * fields that vary during the life time of the gadget are initialized
+	 * by the UDC core.
+	 */
+	udc->gadget.ops = &isp1760_udc_ops;
+	udc->gadget.speed = USB_SPEED_UNKNOWN;
+	udc->gadget.max_speed = USB_SPEED_HIGH;
+	udc->gadget.name = "isp1761_udc";
+
+	isp1760_udc_init_eps(udc);
+
+	ret = usb_add_gadget_udc(isp->dev, &udc->gadget);
+	if (ret < 0)
+		goto error;
+
+	return 0;
+
+error:
+	if (udc->irq >= 0)
+		free_irq(udc->irq, udc);
+	kfree(udc->irqname);
+
+	return ret;
+}
+
+void isp1760_udc_unregister(struct isp1760_device *isp)
+{
+	struct isp1760_udc *udc = &isp->udc;
+
+	if (!udc->isp)
+		return;
+
+	usb_del_gadget_udc(&udc->gadget);
+
+	free_irq(udc->irq, udc);
+	kfree(udc->irqname);
+}

+ 106 - 0
drivers/usb/isp1760/isp1760-udc.h

@@ -0,0 +1,106 @@
+/*
+ * Driver for the NXP ISP1761 device controller
+ *
+ * Copyright 2014 Ideas on Board Oy
+ *
+ * Contacts:
+ *	Laurent Pinchart <laurent.pinchart@ideasonboard.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 _ISP1760_UDC_H_
+#define _ISP1760_UDC_H_
+
+#include <linux/ioport.h>
+#include <linux/list.h>
+#include <linux/spinlock.h>
+#include <linux/timer.h>
+#include <linux/usb/gadget.h>
+
+struct isp1760_device;
+struct isp1760_udc;
+
+enum isp1760_ctrl_state {
+	ISP1760_CTRL_SETUP,		/* Waiting for a SETUP transaction */
+	ISP1760_CTRL_DATA_IN,		/* Setup received, data IN stage */
+	ISP1760_CTRL_DATA_OUT,		/* Setup received, data OUT stage */
+	ISP1760_CTRL_STATUS,		/* 0-length request in status stage */
+};
+
+struct isp1760_ep {
+	struct isp1760_udc *udc;
+	struct usb_ep ep;
+
+	struct list_head queue;
+
+	unsigned int addr;
+	unsigned int maxpacket;
+	char name[7];
+
+	const struct usb_endpoint_descriptor *desc;
+
+	bool rx_pending;
+	bool halted;
+	bool wedged;
+};
+
+/**
+ * struct isp1760_udc - UDC state information
+ * irq: IRQ number
+ * irqname: IRQ name (as passed to request_irq)
+ * regs: Base address of the UDC registers
+ * driver: Gadget driver
+ * gadget: Gadget device
+ * lock: Protects driver, vbus_timer, ep, ep0_*, DC_EPINDEX register
+ * ep: Array of endpoints
+ * ep0_state: Control request state for endpoint 0
+ * ep0_dir: Direction of the current control request
+ * ep0_length: Length of the current control request
+ * connected: Tracks gadget driver bus connection state
+ */
+struct isp1760_udc {
+#ifdef CONFIG_USB_ISP1761_UDC
+	struct isp1760_device *isp;
+
+	int irq;
+	char *irqname;
+	void __iomem *regs;
+
+	struct usb_gadget_driver *driver;
+	struct usb_gadget gadget;
+
+	spinlock_t lock;
+	struct timer_list vbus_timer;
+
+	struct isp1760_ep ep[15];
+
+	enum isp1760_ctrl_state ep0_state;
+	u8 ep0_dir;
+	u16 ep0_length;
+
+	bool connected;
+
+	unsigned int devstatus;
+#endif
+};
+
+#ifdef CONFIG_USB_ISP1761_UDC
+int isp1760_udc_register(struct isp1760_device *isp, int irq,
+			 unsigned long irqflags);
+void isp1760_udc_unregister(struct isp1760_device *isp);
+#else
+static inline int isp1760_udc_register(struct isp1760_device *isp, int irq,
+				       unsigned long irqflags)
+{
+	return 0;
+}
+
+static inline void isp1760_udc_unregister(struct isp1760_device *isp)
+{
+}
+#endif
+
+#endif

+ 6 - 1
drivers/usb/musb/Kconfig

@@ -63,11 +63,13 @@ comment "Platform Glue Layer"
 config USB_MUSB_DAVINCI
 config USB_MUSB_DAVINCI
 	tristate "DaVinci"
 	tristate "DaVinci"
 	depends on ARCH_DAVINCI_DMx
 	depends on ARCH_DAVINCI_DMx
+	depends on NOP_USB_XCEIV
 	depends on BROKEN
 	depends on BROKEN
 
 
 config USB_MUSB_DA8XX
 config USB_MUSB_DA8XX
 	tristate "DA8xx/OMAP-L1x"
 	tristate "DA8xx/OMAP-L1x"
 	depends on ARCH_DAVINCI_DA8XX
 	depends on ARCH_DAVINCI_DA8XX
+	depends on NOP_USB_XCEIV
 	depends on BROKEN
 	depends on BROKEN
 
 
 config USB_MUSB_TUSB6010
 config USB_MUSB_TUSB6010
@@ -77,12 +79,13 @@ config USB_MUSB_TUSB6010
 
 
 config USB_MUSB_OMAP2PLUS
 config USB_MUSB_OMAP2PLUS
 	tristate "OMAP2430 and onwards"
 	tristate "OMAP2430 and onwards"
-	depends on ARCH_OMAP2PLUS && USB
+	depends on ARCH_OMAP2PLUS && USB && OMAP_CONTROL_PHY
 	select GENERIC_PHY
 	select GENERIC_PHY
 
 
 config USB_MUSB_AM35X
 config USB_MUSB_AM35X
 	tristate "AM35x"
 	tristate "AM35x"
 	depends on ARCH_OMAP
 	depends on ARCH_OMAP
+	depends on NOP_USB_XCEIV
 
 
 config USB_MUSB_DSPS
 config USB_MUSB_DSPS
 	tristate "TI DSPS platforms"
 	tristate "TI DSPS platforms"
@@ -93,6 +96,7 @@ config USB_MUSB_DSPS
 config USB_MUSB_BLACKFIN
 config USB_MUSB_BLACKFIN
 	tristate "Blackfin"
 	tristate "Blackfin"
 	depends on (BF54x && !BF544) || (BF52x && ! BF522 && !BF523)
 	depends on (BF54x && !BF544) || (BF52x && ! BF522 && !BF523)
+	depends on NOP_USB_XCEIV
 
 
 config USB_MUSB_UX500
 config USB_MUSB_UX500
 	tristate "Ux500 platforms"
 	tristate "Ux500 platforms"
@@ -100,6 +104,7 @@ config USB_MUSB_UX500
 
 
 config USB_MUSB_JZ4740
 config USB_MUSB_JZ4740
 	tristate "JZ4740"
 	tristate "JZ4740"
+	depends on NOP_USB_XCEIV
 	depends on MACH_JZ4740 || COMPILE_TEST
 	depends on MACH_JZ4740 || COMPILE_TEST
 	depends on USB_MUSB_GADGET
 	depends on USB_MUSB_GADGET
 	depends on USB_OTG_BLACKLIST_HUB
 	depends on USB_OTG_BLACKLIST_HUB

+ 1 - 1
drivers/usb/musb/blackfin.c

@@ -608,7 +608,7 @@ static SIMPLE_DEV_PM_OPS(bfin_pm_ops, bfin_suspend, bfin_resume);
 
 
 static struct platform_driver bfin_driver = {
 static struct platform_driver bfin_driver = {
 	.probe		= bfin_probe,
 	.probe		= bfin_probe,
-	.remove		= __exit_p(bfin_remove),
+	.remove		= bfin_remove,
 	.driver		= {
 	.driver		= {
 		.name	= "musb-blackfin",
 		.name	= "musb-blackfin",
 		.pm	= &bfin_pm_ops,
 		.pm	= &bfin_pm_ops,

+ 7 - 0
drivers/usb/musb/musb_core.c

@@ -567,6 +567,7 @@ static irqreturn_t musb_stage0_irq(struct musb *musb, u8 int_usb,
 
 
 				musb->xceiv->otg->state = OTG_STATE_A_HOST;
 				musb->xceiv->otg->state = OTG_STATE_A_HOST;
 				musb->is_active = 1;
 				musb->is_active = 1;
+				musb_host_resume_root_hub(musb);
 				break;
 				break;
 			case OTG_STATE_B_WAIT_ACON:
 			case OTG_STATE_B_WAIT_ACON:
 				musb->xceiv->otg->state = OTG_STATE_B_PERIPHERAL;
 				musb->xceiv->otg->state = OTG_STATE_B_PERIPHERAL;
@@ -2500,6 +2501,12 @@ static int musb_runtime_resume(struct device *dev)
 		musb_restore_context(musb);
 		musb_restore_context(musb);
 	first = 0;
 	first = 0;
 
 
+	if (musb->need_finish_resume) {
+		musb->need_finish_resume = 0;
+		schedule_delayed_work(&musb->finish_resume_work,
+				msecs_to_jiffies(20));
+	}
+
 	return 0;
 	return 0;
 }
 }
 
 

+ 15 - 9
drivers/usb/musb/musb_cppi41.c

@@ -9,9 +9,9 @@
 
 
 #define RNDIS_REG(x) (0x80 + ((x - 1) * 4))
 #define RNDIS_REG(x) (0x80 + ((x - 1) * 4))
 
 
-#define EP_MODE_AUTOREG_NONE		0
-#define EP_MODE_AUTOREG_ALL_NEOP	1
-#define EP_MODE_AUTOREG_ALWAYS		3
+#define EP_MODE_AUTOREQ_NONE		0
+#define EP_MODE_AUTOREQ_ALL_NEOP	1
+#define EP_MODE_AUTOREQ_ALWAYS		3
 
 
 #define EP_MODE_DMA_TRANSPARENT		0
 #define EP_MODE_DMA_TRANSPARENT		0
 #define EP_MODE_DMA_RNDIS		1
 #define EP_MODE_DMA_RNDIS		1
@@ -404,19 +404,19 @@ static bool cppi41_configure_channel(struct dma_channel *channel,
 
 
 			/* auto req */
 			/* auto req */
 			cppi41_set_autoreq_mode(cppi41_channel,
 			cppi41_set_autoreq_mode(cppi41_channel,
-					EP_MODE_AUTOREG_ALL_NEOP);
+					EP_MODE_AUTOREQ_ALL_NEOP);
 		} else {
 		} else {
 			musb_writel(musb->ctrl_base,
 			musb_writel(musb->ctrl_base,
 					RNDIS_REG(cppi41_channel->port_num), 0);
 					RNDIS_REG(cppi41_channel->port_num), 0);
 			cppi41_set_dma_mode(cppi41_channel,
 			cppi41_set_dma_mode(cppi41_channel,
 					EP_MODE_DMA_TRANSPARENT);
 					EP_MODE_DMA_TRANSPARENT);
 			cppi41_set_autoreq_mode(cppi41_channel,
 			cppi41_set_autoreq_mode(cppi41_channel,
-					EP_MODE_AUTOREG_NONE);
+					EP_MODE_AUTOREQ_NONE);
 		}
 		}
 	} else {
 	} else {
 		/* fallback mode */
 		/* fallback mode */
 		cppi41_set_dma_mode(cppi41_channel, EP_MODE_DMA_TRANSPARENT);
 		cppi41_set_dma_mode(cppi41_channel, EP_MODE_DMA_TRANSPARENT);
-		cppi41_set_autoreq_mode(cppi41_channel, EP_MODE_AUTOREG_NONE);
+		cppi41_set_autoreq_mode(cppi41_channel, EP_MODE_AUTOREQ_NONE);
 		len = min_t(u32, packet_sz, len);
 		len = min_t(u32, packet_sz, len);
 	}
 	}
 	cppi41_channel->prog_len = len;
 	cppi41_channel->prog_len = len;
@@ -549,10 +549,15 @@ static int cppi41_dma_channel_abort(struct dma_channel *channel)
 		csr &= ~MUSB_TXCSR_DMAENAB;
 		csr &= ~MUSB_TXCSR_DMAENAB;
 		musb_writew(epio, MUSB_TXCSR, csr);
 		musb_writew(epio, MUSB_TXCSR, csr);
 	} else {
 	} else {
+		cppi41_set_autoreq_mode(cppi41_channel, EP_MODE_AUTOREQ_NONE);
+
 		csr = musb_readw(epio, MUSB_RXCSR);
 		csr = musb_readw(epio, MUSB_RXCSR);
 		csr &= ~(MUSB_RXCSR_H_REQPKT | MUSB_RXCSR_DMAENAB);
 		csr &= ~(MUSB_RXCSR_H_REQPKT | MUSB_RXCSR_DMAENAB);
 		musb_writew(epio, MUSB_RXCSR, csr);
 		musb_writew(epio, MUSB_RXCSR, csr);
 
 
+		/* wait to drain cppi dma pipe line */
+		udelay(50);
+
 		csr = musb_readw(epio, MUSB_RXCSR);
 		csr = musb_readw(epio, MUSB_RXCSR);
 		if (csr & MUSB_RXCSR_RXPKTRDY) {
 		if (csr & MUSB_RXCSR_RXPKTRDY) {
 			csr |= MUSB_RXCSR_FLUSHFIFO;
 			csr |= MUSB_RXCSR_FLUSHFIFO;
@@ -566,13 +571,14 @@ static int cppi41_dma_channel_abort(struct dma_channel *channel)
 		tdbit <<= 16;
 		tdbit <<= 16;
 
 
 	do {
 	do {
-		musb_writel(musb->ctrl_base, USB_TDOWN, tdbit);
+		if (is_tx)
+			musb_writel(musb->ctrl_base, USB_TDOWN, tdbit);
 		ret = dmaengine_terminate_all(cppi41_channel->dc);
 		ret = dmaengine_terminate_all(cppi41_channel->dc);
 	} while (ret == -EAGAIN);
 	} while (ret == -EAGAIN);
 
 
-	musb_writel(musb->ctrl_base, USB_TDOWN, tdbit);
-
 	if (is_tx) {
 	if (is_tx) {
+		musb_writel(musb->ctrl_base, USB_TDOWN, tdbit);
+
 		csr = musb_readw(epio, MUSB_TXCSR);
 		csr = musb_readw(epio, MUSB_TXCSR);
 		if (csr & MUSB_TXCSR_TXPKTRDY) {
 		if (csr & MUSB_TXCSR_TXPKTRDY) {
 			csr |= MUSB_TXCSR_FLUSHFIFO;
 			csr |= MUSB_TXCSR_FLUSHFIFO;

+ 1 - 1
drivers/usb/musb/musb_debugfs.c

@@ -196,7 +196,7 @@ static ssize_t musb_test_mode_write(struct file *file,
 
 
 	memset(buf, 0x00, sizeof(buf));
 	memset(buf, 0x00, sizeof(buf));
 
 
-	if (copy_from_user(&buf, ubuf, min_t(size_t, sizeof(buf) - 1, count)))
+	if (copy_from_user(buf, ubuf, min_t(size_t, sizeof(buf) - 1, count)))
 		return -EFAULT;
 		return -EFAULT;
 
 
 	if (strstarts(buf, "force host"))
 	if (strstarts(buf, "force host"))

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

@@ -1612,9 +1612,7 @@ done:
 static int
 static int
 musb_gadget_set_self_powered(struct usb_gadget *gadget, int is_selfpowered)
 musb_gadget_set_self_powered(struct usb_gadget *gadget, int is_selfpowered)
 {
 {
-	struct musb	*musb = gadget_to_musb(gadget);
-
-	musb->is_self_powered = !!is_selfpowered;
+	gadget->is_selfpowered = !!is_selfpowered;
 	return 0;
 	return 0;
 }
 }
 
 

+ 1 - 1
drivers/usb/musb/musb_gadget_ep0.c

@@ -85,7 +85,7 @@ static int service_tx_status_request(
 
 
 	switch (recip) {
 	switch (recip) {
 	case USB_RECIP_DEVICE:
 	case USB_RECIP_DEVICE:
-		result[0] = musb->is_self_powered << USB_DEVICE_SELF_POWERED;
+		result[0] = musb->g.is_selfpowered << USB_DEVICE_SELF_POWERED;
 		result[0] |= musb->may_wakeup << USB_DEVICE_REMOTE_WAKEUP;
 		result[0] |= musb->may_wakeup << USB_DEVICE_REMOTE_WAKEUP;
 		if (musb->g.is_otg) {
 		if (musb->g.is_otg) {
 			result[0] |= musb->g.b_hnp_enable
 			result[0] |= musb->g.b_hnp_enable

+ 1 - 3
drivers/usb/musb/musb_virthub.c

@@ -72,7 +72,6 @@ void musb_host_finish_resume(struct work_struct *work)
 	musb->xceiv->otg->state = OTG_STATE_A_HOST;
 	musb->xceiv->otg->state = OTG_STATE_A_HOST;
 
 
 	spin_unlock_irqrestore(&musb->lock, flags);
 	spin_unlock_irqrestore(&musb->lock, flags);
-	musb_host_resume_root_hub(musb);
 }
 }
 
 
 void musb_port_suspend(struct musb *musb, bool do_suspend)
 void musb_port_suspend(struct musb *musb, bool do_suspend)
@@ -349,8 +348,7 @@ int musb_hub_control(
 		desc->bDescriptorType = 0x29;
 		desc->bDescriptorType = 0x29;
 		desc->bNbrPorts = 1;
 		desc->bNbrPorts = 1;
 		desc->wHubCharacteristics = cpu_to_le16(
 		desc->wHubCharacteristics = cpu_to_le16(
-			  HUB_CHAR_INDV_PORT_LPSM /* per-port power switching */
-
+			HUB_CHAR_INDV_PORT_LPSM /* per-port power switching */
 			| HUB_CHAR_NO_OCPM	/* no overcurrent reporting */
 			| HUB_CHAR_NO_OCPM	/* no overcurrent reporting */
 			);
 			);
 		desc->bPwrOn2PwrGood = 5;	/* msec/2 */
 		desc->bPwrOn2PwrGood = 5;	/* msec/2 */

+ 0 - 35
drivers/usb/phy/phy-fsl-usb.c

@@ -107,19 +107,6 @@ static void (*_fsl_writel)(u32 v, unsigned __iomem *p);
 #define fsl_writel(val, addr)	writel(val, addr)
 #define fsl_writel(val, addr)	writel(val, addr)
 #endif /* CONFIG_PPC32 */
 #endif /* CONFIG_PPC32 */
 
 
-/* Routines to access transceiver ULPI registers */
-u8 view_ulpi(u8 addr)
-{
-	u32 temp;
-
-	temp = 0x40000000 | (addr << 16);
-	fsl_writel(temp, &usb_dr_regs->ulpiview);
-	udelay(1000);
-	while (temp & 0x40)
-		temp = fsl_readl(&usb_dr_regs->ulpiview);
-	return (le32_to_cpu(temp) & 0x0000ff00) >> 8;
-}
-
 int write_ulpi(u8 addr, u8 data)
 int write_ulpi(u8 addr, u8 data)
 {
 {
 	u32 temp;
 	u32 temp;
@@ -460,28 +447,6 @@ static void fsl_otg_fsm_del_timer(struct otg_fsm *fsm, enum otg_fsm_timer t)
 	fsl_otg_del_timer(fsm, timer);
 	fsl_otg_del_timer(fsm, timer);
 }
 }
 
 
-/*
- * Reduce timer count by 1, and find timeout conditions.
- * Called by fsl_otg 1ms timer interrupt
- */
-int fsl_otg_tick_timer(void)
-{
-	struct fsl_otg_timer *tmp_timer, *del_tmp;
-	int expired = 0;
-
-	list_for_each_entry_safe(tmp_timer, del_tmp, &active_timers, list) {
-		tmp_timer->count--;
-		/* check if timer expires */
-		if (!tmp_timer->count) {
-			list_del(&tmp_timer->list);
-			tmp_timer->function(tmp_timer->data);
-			expired = 1;
-		}
-	}
-
-	return expired;
-}
-
 /* Reset controller, not reset the bus */
 /* Reset controller, not reset the bus */
 void otg_reset_controller(void)
 void otg_reset_controller(void)
 {
 {

+ 110 - 40
drivers/usb/phy/phy-generic.c

@@ -27,6 +27,7 @@
 #include <linux/module.h>
 #include <linux/module.h>
 #include <linux/platform_device.h>
 #include <linux/platform_device.h>
 #include <linux/dma-mapping.h>
 #include <linux/dma-mapping.h>
+#include <linux/usb/gadget.h>
 #include <linux/usb/otg.h>
 #include <linux/usb/otg.h>
 #include <linux/usb/usb_phy_generic.h>
 #include <linux/usb/usb_phy_generic.h>
 #include <linux/slab.h>
 #include <linux/slab.h>
@@ -39,6 +40,10 @@
 
 
 #include "phy-generic.h"
 #include "phy-generic.h"
 
 
+#define VBUS_IRQ_FLAGS \
+	(IRQF_SHARED | IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING | \
+		IRQF_ONESHOT)
+
 struct platform_device *usb_phy_generic_register(void)
 struct platform_device *usb_phy_generic_register(void)
 {
 {
 	return platform_device_register_simple("usb_phy_generic",
 	return platform_device_register_simple("usb_phy_generic",
@@ -59,19 +64,79 @@ static int nop_set_suspend(struct usb_phy *x, int suspend)
 
 
 static void nop_reset_set(struct usb_phy_generic *nop, int asserted)
 static void nop_reset_set(struct usb_phy_generic *nop, int asserted)
 {
 {
-	int value;
+	if (!nop->gpiod_reset)
+		return;
+
+	gpiod_direction_output(nop->gpiod_reset, !asserted);
+	usleep_range(10000, 20000);
+	gpiod_set_value(nop->gpiod_reset, asserted);
+}
+
+/* interface to regulator framework */
+static void nop_set_vbus_draw(struct usb_phy_generic *nop, unsigned mA)
+{
+	struct regulator *vbus_draw = nop->vbus_draw;
+	int enabled;
+	int ret;
 
 
-	if (!gpio_is_valid(nop->gpio_reset))
+	if (!vbus_draw)
 		return;
 		return;
 
 
-	value = asserted;
-	if (nop->reset_active_low)
-		value = !value;
+	enabled = nop->vbus_draw_enabled;
+	if (mA) {
+		regulator_set_current_limit(vbus_draw, 0, 1000 * mA);
+		if (!enabled) {
+			ret = regulator_enable(vbus_draw);
+			if (ret < 0)
+				return;
+			nop->vbus_draw_enabled = 1;
+		}
+	} else {
+		if (enabled) {
+			ret = regulator_disable(vbus_draw);
+			if (ret < 0)
+				return;
+			nop->vbus_draw_enabled = 0;
+		}
+	}
+	nop->mA = mA;
+}
+
+
+static irqreturn_t nop_gpio_vbus_thread(int irq, void *data)
+{
+	struct usb_phy_generic *nop = data;
+	struct usb_otg *otg = nop->phy.otg;
+	int vbus, status;
+
+	vbus = gpiod_get_value(nop->gpiod_vbus);
+	if ((vbus ^ nop->vbus) == 0)
+		return IRQ_HANDLED;
+	nop->vbus = vbus;
+
+	if (vbus) {
+		status = USB_EVENT_VBUS;
+		otg->state = OTG_STATE_B_PERIPHERAL;
+		nop->phy.last_event = status;
+		usb_gadget_vbus_connect(otg->gadget);
+
+		/* drawing a "unit load" is *always* OK, except for OTG */
+		nop_set_vbus_draw(nop, 100);
+
+		atomic_notifier_call_chain(&nop->phy.notifier, status,
+					   otg->gadget);
+	} else {
+		nop_set_vbus_draw(nop, 0);
 
 
-	gpio_set_value_cansleep(nop->gpio_reset, value);
+		usb_gadget_vbus_disconnect(otg->gadget);
+		status = USB_EVENT_NONE;
+		otg->state = OTG_STATE_B_IDLE;
+		nop->phy.last_event = status;
 
 
-	if (!asserted)
-		usleep_range(10000, 20000);
+		atomic_notifier_call_chain(&nop->phy.notifier, status,
+					   otg->gadget);
+	}
+	return IRQ_HANDLED;
 }
 }
 
 
 int usb_gen_phy_init(struct usb_phy *phy)
 int usb_gen_phy_init(struct usb_phy *phy)
@@ -143,36 +208,47 @@ int usb_phy_gen_create_phy(struct device *dev, struct usb_phy_generic *nop,
 		struct usb_phy_generic_platform_data *pdata)
 		struct usb_phy_generic_platform_data *pdata)
 {
 {
 	enum usb_phy_type type = USB_PHY_TYPE_USB2;
 	enum usb_phy_type type = USB_PHY_TYPE_USB2;
-	int err;
+	int err = 0;
 
 
 	u32 clk_rate = 0;
 	u32 clk_rate = 0;
 	bool needs_vcc = false;
 	bool needs_vcc = false;
 
 
-	nop->reset_active_low = true;	/* default behaviour */
-
 	if (dev->of_node) {
 	if (dev->of_node) {
 		struct device_node *node = dev->of_node;
 		struct device_node *node = dev->of_node;
-		enum of_gpio_flags flags = 0;
 
 
 		if (of_property_read_u32(node, "clock-frequency", &clk_rate))
 		if (of_property_read_u32(node, "clock-frequency", &clk_rate))
 			clk_rate = 0;
 			clk_rate = 0;
 
 
 		needs_vcc = of_property_read_bool(node, "vcc-supply");
 		needs_vcc = of_property_read_bool(node, "vcc-supply");
-		nop->gpio_reset = of_get_named_gpio_flags(node, "reset-gpios",
-								0, &flags);
-		if (nop->gpio_reset == -EPROBE_DEFER)
-			return -EPROBE_DEFER;
-
-		nop->reset_active_low = flags & OF_GPIO_ACTIVE_LOW;
-
+		nop->gpiod_reset = devm_gpiod_get_optional(dev, "reset");
+		err = PTR_ERR_OR_ZERO(nop->gpiod_reset);
+		if (!err) {
+			nop->gpiod_vbus = devm_gpiod_get_optional(dev,
+							 "vbus-detect");
+			err = PTR_ERR_OR_ZERO(nop->gpiod_vbus);
+		}
 	} else if (pdata) {
 	} else if (pdata) {
 		type = pdata->type;
 		type = pdata->type;
 		clk_rate = pdata->clk_rate;
 		clk_rate = pdata->clk_rate;
 		needs_vcc = pdata->needs_vcc;
 		needs_vcc = pdata->needs_vcc;
-		nop->gpio_reset = pdata->gpio_reset;
-	} else {
-		nop->gpio_reset = -1;
+		if (gpio_is_valid(pdata->gpio_reset)) {
+			err = devm_gpio_request_one(dev, pdata->gpio_reset, 0,
+						    dev_name(dev));
+			if (!err)
+				nop->gpiod_reset =
+					gpio_to_desc(pdata->gpio_reset);
+		}
+		nop->gpiod_vbus = pdata->gpiod_vbus;
+	}
+
+	if (err == -EPROBE_DEFER)
+		return -EPROBE_DEFER;
+	if (err) {
+		dev_err(dev, "Error requesting RESET or VBUS GPIO\n");
+		return err;
 	}
 	}
+	if (nop->gpiod_reset)
+		gpiod_direction_output(nop->gpiod_reset, 1);
 
 
 	nop->phy.otg = devm_kzalloc(dev, sizeof(*nop->phy.otg),
 	nop->phy.otg = devm_kzalloc(dev, sizeof(*nop->phy.otg),
 			GFP_KERNEL);
 			GFP_KERNEL);
@@ -201,24 +277,6 @@ int usb_phy_gen_create_phy(struct device *dev, struct usb_phy_generic *nop,
 			return -EPROBE_DEFER;
 			return -EPROBE_DEFER;
 	}
 	}
 
 
-	if (gpio_is_valid(nop->gpio_reset)) {
-		unsigned long gpio_flags;
-
-		/* Assert RESET */
-		if (nop->reset_active_low)
-			gpio_flags = GPIOF_OUT_INIT_LOW;
-		else
-			gpio_flags = GPIOF_OUT_INIT_HIGH;
-
-		err = devm_gpio_request_one(dev, nop->gpio_reset,
-						gpio_flags, dev_name(dev));
-		if (err) {
-			dev_err(dev, "Error requesting RESET GPIO %d\n",
-					nop->gpio_reset);
-			return err;
-		}
-	}
-
 	nop->dev		= dev;
 	nop->dev		= dev;
 	nop->phy.dev		= nop->dev;
 	nop->phy.dev		= nop->dev;
 	nop->phy.label		= "nop-xceiv";
 	nop->phy.label		= "nop-xceiv";
@@ -247,6 +305,18 @@ static int usb_phy_generic_probe(struct platform_device *pdev)
 	err = usb_phy_gen_create_phy(dev, nop, dev_get_platdata(&pdev->dev));
 	err = usb_phy_gen_create_phy(dev, nop, dev_get_platdata(&pdev->dev));
 	if (err)
 	if (err)
 		return err;
 		return err;
+	if (nop->gpiod_vbus) {
+		err = devm_request_threaded_irq(&pdev->dev,
+						gpiod_to_irq(nop->gpiod_vbus),
+						NULL, nop_gpio_vbus_thread,
+						VBUS_IRQ_FLAGS, "vbus_detect",
+						nop);
+		if (err) {
+			dev_err(&pdev->dev, "can't request irq %i, err: %d\n",
+				gpiod_to_irq(nop->gpiod_vbus), err);
+			return err;
+		}
+	}
 
 
 	nop->phy.init		= usb_gen_phy_init;
 	nop->phy.init		= usb_gen_phy_init;
 	nop->phy.shutdown	= usb_gen_phy_shutdown;
 	nop->phy.shutdown	= usb_gen_phy_shutdown;

+ 8 - 2
drivers/usb/phy/phy-generic.h

@@ -2,14 +2,20 @@
 #define _PHY_GENERIC_H_
 #define _PHY_GENERIC_H_
 
 
 #include <linux/usb/usb_phy_generic.h>
 #include <linux/usb/usb_phy_generic.h>
+#include <linux/gpio/consumer.h>
+#include <linux/regulator/consumer.h>
 
 
 struct usb_phy_generic {
 struct usb_phy_generic {
 	struct usb_phy phy;
 	struct usb_phy phy;
 	struct device *dev;
 	struct device *dev;
 	struct clk *clk;
 	struct clk *clk;
 	struct regulator *vcc;
 	struct regulator *vcc;
-	int gpio_reset;
-	bool reset_active_low;
+	struct gpio_desc *gpiod_reset;
+	struct gpio_desc *gpiod_vbus;
+	struct regulator *vbus_draw;
+	bool vbus_draw_enabled;
+	unsigned long mA;
+	unsigned int vbus;
 };
 };
 
 
 int usb_gen_phy_init(struct usb_phy *phy);
 int usb_gen_phy_init(struct usb_phy *phy);

+ 81 - 5
drivers/usb/phy/phy-mxs-usb.c

@@ -40,6 +40,7 @@
 
 
 #define BM_USBPHY_CTRL_SFTRST			BIT(31)
 #define BM_USBPHY_CTRL_SFTRST			BIT(31)
 #define BM_USBPHY_CTRL_CLKGATE			BIT(30)
 #define BM_USBPHY_CTRL_CLKGATE			BIT(30)
+#define BM_USBPHY_CTRL_OTG_ID_VALUE		BIT(27)
 #define BM_USBPHY_CTRL_ENAUTOSET_USBCLKS	BIT(26)
 #define BM_USBPHY_CTRL_ENAUTOSET_USBCLKS	BIT(26)
 #define BM_USBPHY_CTRL_ENAUTOCLR_USBCLKGATE	BIT(25)
 #define BM_USBPHY_CTRL_ENAUTOCLR_USBCLKGATE	BIT(25)
 #define BM_USBPHY_CTRL_ENVBUSCHG_WKUP		BIT(23)
 #define BM_USBPHY_CTRL_ENVBUSCHG_WKUP		BIT(23)
@@ -69,6 +70,9 @@
 #define ANADIG_USB2_LOOPBACK_SET		0x244
 #define ANADIG_USB2_LOOPBACK_SET		0x244
 #define ANADIG_USB2_LOOPBACK_CLR		0x248
 #define ANADIG_USB2_LOOPBACK_CLR		0x248
 
 
+#define ANADIG_USB1_MISC			0x1f0
+#define ANADIG_USB2_MISC			0x250
+
 #define BM_ANADIG_ANA_MISC0_STOP_MODE_CONFIG	BIT(12)
 #define BM_ANADIG_ANA_MISC0_STOP_MODE_CONFIG	BIT(12)
 #define BM_ANADIG_ANA_MISC0_STOP_MODE_CONFIG_SL BIT(11)
 #define BM_ANADIG_ANA_MISC0_STOP_MODE_CONFIG_SL BIT(11)
 
 
@@ -80,6 +84,11 @@
 #define BM_ANADIG_USB2_LOOPBACK_UTMI_DIG_TST1	BIT(2)
 #define BM_ANADIG_USB2_LOOPBACK_UTMI_DIG_TST1	BIT(2)
 #define BM_ANADIG_USB2_LOOPBACK_TSTI_TX_EN	BIT(5)
 #define BM_ANADIG_USB2_LOOPBACK_TSTI_TX_EN	BIT(5)
 
 
+#define BM_ANADIG_USB1_MISC_RX_VPIN_FS		BIT(29)
+#define BM_ANADIG_USB1_MISC_RX_VMIN_FS		BIT(28)
+#define BM_ANADIG_USB2_MISC_RX_VPIN_FS		BIT(29)
+#define BM_ANADIG_USB2_MISC_RX_VMIN_FS		BIT(28)
+
 #define to_mxs_phy(p) container_of((p), struct mxs_phy, phy)
 #define to_mxs_phy(p) container_of((p), struct mxs_phy, phy)
 
 
 /* Do disconnection between PHY and controller without vbus */
 /* Do disconnection between PHY and controller without vbus */
@@ -131,8 +140,7 @@ static const struct mxs_phy_data vf610_phy_data = {
 };
 };
 
 
 static const struct mxs_phy_data imx6sx_phy_data = {
 static const struct mxs_phy_data imx6sx_phy_data = {
-	.flags = MXS_PHY_DISCONNECT_LINE_WITHOUT_VBUS |
-		MXS_PHY_NEED_IP_FIX,
+	.flags = MXS_PHY_DISCONNECT_LINE_WITHOUT_VBUS,
 };
 };
 
 
 static const struct of_device_id mxs_phy_dt_ids[] = {
 static const struct of_device_id mxs_phy_dt_ids[] = {
@@ -256,6 +264,18 @@ static void __mxs_phy_disconnect_line(struct mxs_phy *mxs_phy, bool disconnect)
 		usleep_range(500, 1000);
 		usleep_range(500, 1000);
 }
 }
 
 
+static bool mxs_phy_is_otg_host(struct mxs_phy *mxs_phy)
+{
+	void __iomem *base = mxs_phy->phy.io_priv;
+	u32 phyctrl = readl(base + HW_USBPHY_CTRL);
+
+	if (IS_ENABLED(CONFIG_USB_OTG) &&
+			!(phyctrl & BM_USBPHY_CTRL_OTG_ID_VALUE))
+		return true;
+
+	return false;
+}
+
 static void mxs_phy_disconnect_line(struct mxs_phy *mxs_phy, bool on)
 static void mxs_phy_disconnect_line(struct mxs_phy *mxs_phy, bool on)
 {
 {
 	bool vbus_is_on = false;
 	bool vbus_is_on = false;
@@ -270,7 +290,7 @@ static void mxs_phy_disconnect_line(struct mxs_phy *mxs_phy, bool on)
 
 
 	vbus_is_on = mxs_phy_get_vbus_status(mxs_phy);
 	vbus_is_on = mxs_phy_get_vbus_status(mxs_phy);
 
 
-	if (on && !vbus_is_on)
+	if (on && !vbus_is_on && !mxs_phy_is_otg_host(mxs_phy))
 		__mxs_phy_disconnect_line(mxs_phy, true);
 		__mxs_phy_disconnect_line(mxs_phy, true);
 	else
 	else
 		__mxs_phy_disconnect_line(mxs_phy, false);
 		__mxs_phy_disconnect_line(mxs_phy, false);
@@ -293,6 +313,17 @@ static int mxs_phy_init(struct usb_phy *phy)
 static void mxs_phy_shutdown(struct usb_phy *phy)
 static void mxs_phy_shutdown(struct usb_phy *phy)
 {
 {
 	struct mxs_phy *mxs_phy = to_mxs_phy(phy);
 	struct mxs_phy *mxs_phy = to_mxs_phy(phy);
+	u32 value = BM_USBPHY_CTRL_ENVBUSCHG_WKUP |
+			BM_USBPHY_CTRL_ENDPDMCHG_WKUP |
+			BM_USBPHY_CTRL_ENIDCHG_WKUP |
+			BM_USBPHY_CTRL_ENAUTOSET_USBCLKS |
+			BM_USBPHY_CTRL_ENAUTOCLR_USBCLKGATE |
+			BM_USBPHY_CTRL_ENAUTOCLR_PHY_PWD |
+			BM_USBPHY_CTRL_ENAUTOCLR_CLKGATE |
+			BM_USBPHY_CTRL_ENAUTO_PWRON_PLL;
+
+	writel(value, phy->io_priv + HW_USBPHY_CTRL_CLR);
+	writel(0xffffffff, phy->io_priv + HW_USBPHY_PWD);
 
 
 	writel(BM_USBPHY_CTRL_CLKGATE,
 	writel(BM_USBPHY_CTRL_CLKGATE,
 	       phy->io_priv + HW_USBPHY_CTRL_SET);
 	       phy->io_priv + HW_USBPHY_CTRL_SET);
@@ -300,13 +331,56 @@ static void mxs_phy_shutdown(struct usb_phy *phy)
 	clk_disable_unprepare(mxs_phy->clk);
 	clk_disable_unprepare(mxs_phy->clk);
 }
 }
 
 
+static bool mxs_phy_is_low_speed_connection(struct mxs_phy *mxs_phy)
+{
+	unsigned int line_state;
+	/* bit definition is the same for all controllers */
+	unsigned int dp_bit = BM_ANADIG_USB1_MISC_RX_VPIN_FS,
+		     dm_bit = BM_ANADIG_USB1_MISC_RX_VMIN_FS;
+	unsigned int reg = ANADIG_USB1_MISC;
+
+	/* If the SoCs don't have anatop, quit */
+	if (!mxs_phy->regmap_anatop)
+		return false;
+
+	if (mxs_phy->port_id == 0)
+		reg = ANADIG_USB1_MISC;
+	else if (mxs_phy->port_id == 1)
+		reg = ANADIG_USB2_MISC;
+
+	regmap_read(mxs_phy->regmap_anatop, reg, &line_state);
+
+	if ((line_state & (dp_bit | dm_bit)) ==  dm_bit)
+		return true;
+	else
+		return false;
+}
+
 static int mxs_phy_suspend(struct usb_phy *x, int suspend)
 static int mxs_phy_suspend(struct usb_phy *x, int suspend)
 {
 {
 	int ret;
 	int ret;
 	struct mxs_phy *mxs_phy = to_mxs_phy(x);
 	struct mxs_phy *mxs_phy = to_mxs_phy(x);
+	bool low_speed_connection, vbus_is_on;
+
+	low_speed_connection = mxs_phy_is_low_speed_connection(mxs_phy);
+	vbus_is_on = mxs_phy_get_vbus_status(mxs_phy);
 
 
 	if (suspend) {
 	if (suspend) {
-		writel(0xffffffff, x->io_priv + HW_USBPHY_PWD);
+		/*
+		 * FIXME: Do not power down RXPWD1PT1 bit for low speed
+		 * connect. The low speed connection will have problem at
+		 * very rare cases during usb suspend and resume process.
+		 */
+		if (low_speed_connection & vbus_is_on) {
+			/*
+			 * If value to be set as pwd value is not 0xffffffff,
+			 * several 32Khz cycles are needed.
+			 */
+			mxs_phy_clock_switch_delay();
+			writel(0xffbfffff, x->io_priv + HW_USBPHY_PWD);
+		} else {
+			writel(0xffffffff, x->io_priv + HW_USBPHY_PWD);
+		}
 		writel(BM_USBPHY_CTRL_CLKGATE,
 		writel(BM_USBPHY_CTRL_CLKGATE,
 		       x->io_priv + HW_USBPHY_CTRL_SET);
 		       x->io_priv + HW_USBPHY_CTRL_SET);
 		clk_disable_unprepare(mxs_phy->clk);
 		clk_disable_unprepare(mxs_phy->clk);
@@ -359,7 +433,9 @@ static int mxs_phy_on_disconnect(struct usb_phy *phy,
 	dev_dbg(phy->dev, "%s device has disconnected\n",
 	dev_dbg(phy->dev, "%s device has disconnected\n",
 		(speed == USB_SPEED_HIGH) ? "HS" : "FS/LS");
 		(speed == USB_SPEED_HIGH) ? "HS" : "FS/LS");
 
 
-	if (speed == USB_SPEED_HIGH)
+	/* Sometimes, the speed is not high speed when the error occurs */
+	if (readl(phy->io_priv + HW_USBPHY_CTRL) &
+			BM_USBPHY_CTRL_ENHOSTDISCONDETECT)
 		writel(BM_USBPHY_CTRL_ENHOSTDISCONDETECT,
 		writel(BM_USBPHY_CTRL_ENHOSTDISCONDETECT,
 		       phy->io_priv + HW_USBPHY_CTRL_CLR);
 		       phy->io_priv + HW_USBPHY_CTRL_CLR);
 
 

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

@@ -363,6 +363,7 @@ static void usbhsc_hotplug(struct usbhs_priv *priv)
 	struct usbhs_mod *mod = usbhs_mod_get_current(priv);
 	struct usbhs_mod *mod = usbhs_mod_get_current(priv);
 	int id;
 	int id;
 	int enable;
 	int enable;
+	int cable;
 	int ret;
 	int ret;
 
 
 	/*
 	/*
@@ -376,6 +377,16 @@ static void usbhsc_hotplug(struct usbhs_priv *priv)
 	id = usbhs_platform_call(priv, get_id, pdev);
 	id = usbhs_platform_call(priv, get_id, pdev);
 
 
 	if (enable && !mod) {
 	if (enable && !mod) {
+		if (priv->edev) {
+			cable = extcon_get_cable_state(priv->edev, "USB-HOST");
+			if ((cable > 0 && id != USBHS_HOST) ||
+			    (!cable && id != USBHS_GADGET)) {
+				dev_info(&pdev->dev,
+					 "USB cable plugged in doesn't match the selected role!\n");
+				return;
+			}
+		}
+
 		ret = usbhs_mod_change(priv, id);
 		ret = usbhs_mod_change(priv, id);
 		if (ret < 0)
 		if (ret < 0)
 			return;
 			return;
@@ -514,6 +525,12 @@ static int usbhs_probe(struct platform_device *pdev)
 	if (IS_ERR(priv->base))
 	if (IS_ERR(priv->base))
 		return PTR_ERR(priv->base);
 		return PTR_ERR(priv->base);
 
 
+	if (of_property_read_bool(pdev->dev.of_node, "extcon")) {
+		priv->edev = extcon_get_edev_by_phandle(&pdev->dev, 0);
+		if (IS_ERR(priv->edev))
+			return PTR_ERR(priv->edev);
+	}
+
 	/*
 	/*
 	 * care platform info
 	 * care platform info
 	 */
 	 */
@@ -615,7 +632,7 @@ static int usbhs_probe(struct platform_device *pdev)
 	 */
 	 */
 	ret = usbhs_platform_call(priv, hardware_init, pdev);
 	ret = usbhs_platform_call(priv, hardware_init, pdev);
 	if (ret < 0) {
 	if (ret < 0) {
-		dev_err(&pdev->dev, "platform prove failed.\n");
+		dev_err(&pdev->dev, "platform init failed.\n");
 		goto probe_end_mod_exit;
 		goto probe_end_mod_exit;
 	}
 	}
 
 
@@ -632,16 +649,12 @@ static int usbhs_probe(struct platform_device *pdev)
 	/*
 	/*
 	 * manual call notify_hotplug for cold plug
 	 * manual call notify_hotplug for cold plug
 	 */
 	 */
-	ret = usbhsc_drvcllbck_notify_hotplug(pdev);
-	if (ret < 0)
-		goto probe_end_call_remove;
+	usbhsc_drvcllbck_notify_hotplug(pdev);
 
 
 	dev_info(&pdev->dev, "probed\n");
 	dev_info(&pdev->dev, "probed\n");
 
 
 	return ret;
 	return ret;
 
 
-probe_end_call_remove:
-	usbhs_platform_call(priv, hardware_exit, pdev);
 probe_end_mod_exit:
 probe_end_mod_exit:
 	usbhs_mod_remove(priv);
 	usbhs_mod_remove(priv);
 probe_end_fifo_exit:
 probe_end_fifo_exit:

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

@@ -17,6 +17,7 @@
 #ifndef RENESAS_USB_DRIVER_H
 #ifndef RENESAS_USB_DRIVER_H
 #define RENESAS_USB_DRIVER_H
 #define RENESAS_USB_DRIVER_H
 
 
+#include <linux/extcon.h>
 #include <linux/platform_device.h>
 #include <linux/platform_device.h>
 #include <linux/usb/renesas_usbhs.h>
 #include <linux/usb/renesas_usbhs.h>
 
 
@@ -254,6 +255,8 @@ struct usbhs_priv {
 	struct delayed_work notify_hotplug_work;
 	struct delayed_work notify_hotplug_work;
 	struct platform_device *pdev;
 	struct platform_device *pdev;
 
 
+	struct extcon_dev *edev;
+
 	spinlock_t		lock;
 	spinlock_t		lock;
 
 
 	u32 flags;
 	u32 flags;

+ 22 - 3
drivers/usb/renesas_usbhs/fifo.c

@@ -1054,10 +1054,8 @@ static void usbhsf_dma_quit(struct usbhs_priv *priv, struct usbhs_fifo *fifo)
 	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_pdev(struct usbhs_fifo *fifo)
 {
 {
-	struct device *dev = usbhs_priv_to_dev(priv);
 	dma_cap_mask_t mask;
 	dma_cap_mask_t mask;
 
 
 	dma_cap_zero(mask);
 	dma_cap_zero(mask);
@@ -1069,6 +1067,27 @@ static void usbhsf_dma_init(struct usbhs_priv *priv,
 	dma_cap_set(DMA_SLAVE, mask);
 	dma_cap_set(DMA_SLAVE, mask);
 	fifo->rx_chan = dma_request_channel(mask, usbhsf_dma_filter,
 	fifo->rx_chan = dma_request_channel(mask, usbhsf_dma_filter,
 					    &fifo->rx_slave);
 					    &fifo->rx_slave);
+}
+
+static void usbhsf_dma_init_dt(struct device *dev, struct usbhs_fifo *fifo)
+{
+	fifo->tx_chan = dma_request_slave_channel_reason(dev, "tx");
+	if (IS_ERR(fifo->tx_chan))
+		fifo->tx_chan = NULL;
+	fifo->rx_chan = dma_request_slave_channel_reason(dev, "rx");
+	if (IS_ERR(fifo->rx_chan))
+		fifo->rx_chan = NULL;
+}
+
+static void usbhsf_dma_init(struct usbhs_priv *priv,
+			    struct usbhs_fifo *fifo)
+{
+	struct device *dev = usbhs_priv_to_dev(priv);
+
+	if (dev->of_node)
+		usbhsf_dma_init_dt(dev, fifo);
+	else
+		usbhsf_dma_init_pdev(fifo);
 
 
 	if (fifo->tx_chan || fifo->rx_chan)
 	if (fifo->tx_chan || fifo->rx_chan)
 		dev_dbg(dev, "enable DMAEngine (%s%s%s)\n",
 		dev_dbg(dev, "enable DMAEngine (%s%s%s)\n",

+ 2 - 0
drivers/usb/renesas_usbhs/mod_gadget.c

@@ -926,6 +926,8 @@ static int usbhsg_set_selfpowered(struct usb_gadget *gadget, int is_self)
 	else
 	else
 		usbhsg_status_clr(gpriv, USBHSG_STATUS_SELF_POWERED);
 		usbhsg_status_clr(gpriv, USBHSG_STATUS_SELF_POWERED);
 
 
+	gadget->is_selfpowered = (is_self != 0);
+
 	return 0;
 	return 0;
 }
 }
 
 

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

@@ -523,6 +523,7 @@ struct usb_gadget_ops {
  *	enabled HNP support.
  *	enabled HNP support.
  * @quirk_ep_out_aligned_size: epout requires buffer size to be aligned to
  * @quirk_ep_out_aligned_size: epout requires buffer size to be aligned to
  *	MaxPacketSize.
  *	MaxPacketSize.
+ * @is_selfpowered: if the gadget is self-powered.
  *
  *
  * Gadgets have a mostly-portable "gadget driver" implementing device
  * Gadgets have a mostly-portable "gadget driver" implementing device
  * functions, handling all usb configurations and interfaces.  Gadget
  * functions, handling all usb configurations and interfaces.  Gadget
@@ -563,6 +564,7 @@ struct usb_gadget {
 	unsigned			a_hnp_support:1;
 	unsigned			a_hnp_support:1;
 	unsigned			a_alt_hnp_support:1;
 	unsigned			a_alt_hnp_support:1;
 	unsigned			quirk_ep_out_aligned_size:1;
 	unsigned			quirk_ep_out_aligned_size:1;
+	unsigned			is_selfpowered:1;
 };
 };
 #define work_to_gadget(w)	(container_of((w), struct usb_gadget, work))
 #define work_to_gadget(w)	(container_of((w), struct usb_gadget, work))
 
 

+ 2 - 2
include/linux/usb/phy.h

@@ -1,5 +1,5 @@
-/* USB OTG (On The Go) defines */
 /*
 /*
+ * USB PHY defines
  *
  *
  * These APIs may be used between USB controllers.  USB device drivers
  * These APIs may be used between USB controllers.  USB device drivers
  * (for either host or peripheral roles) don't use these calls; they
  * (for either host or peripheral roles) don't use these calls; they
@@ -106,7 +106,7 @@ struct usb_phy {
 	int	(*set_power)(struct usb_phy *x,
 	int	(*set_power)(struct usb_phy *x,
 				unsigned mA);
 				unsigned mA);
 
 
-	/* for non-OTG B devices: set transceiver into suspend mode */
+	/* Set transceiver into suspend mode */
 	int	(*set_suspend)(struct usb_phy *x,
 	int	(*set_suspend)(struct usb_phy *x,
 				int suspend);
 				int suspend);
 
 

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

@@ -2,6 +2,7 @@
 #define __LINUX_USB_NOP_XCEIV_H
 #define __LINUX_USB_NOP_XCEIV_H
 
 
 #include <linux/usb/otg.h>
 #include <linux/usb/otg.h>
+#include <linux/gpio/consumer.h>
 
 
 struct usb_phy_generic_platform_data {
 struct usb_phy_generic_platform_data {
 	enum usb_phy_type type;
 	enum usb_phy_type type;
@@ -11,6 +12,7 @@ struct usb_phy_generic_platform_data {
 	unsigned int needs_vcc:1;
 	unsigned int needs_vcc:1;
 	unsigned int needs_reset:1;	/* deprecated */
 	unsigned int needs_reset:1;	/* deprecated */
 	int gpio_reset;
 	int gpio_reset;
+	struct gpio_desc *gpiod_vbus;
 };
 };
 
 
 #if IS_ENABLED(CONFIG_NOP_USB_XCEIV)
 #if IS_ENABLED(CONFIG_NOP_USB_XCEIV)

+ 1 - 0
include/uapi/linux/usb/functionfs.h

@@ -20,6 +20,7 @@ enum functionfs_flags {
 	FUNCTIONFS_HAS_SS_DESC = 4,
 	FUNCTIONFS_HAS_SS_DESC = 4,
 	FUNCTIONFS_HAS_MS_OS_DESC = 8,
 	FUNCTIONFS_HAS_MS_OS_DESC = 8,
 	FUNCTIONFS_VIRTUAL_ADDR = 16,
 	FUNCTIONFS_VIRTUAL_ADDR = 16,
+	FUNCTIONFS_EVENTFD = 32,
 };
 };
 
 
 /* Descriptor of an non-audio endpoint */
 /* Descriptor of an non-audio endpoint */

+ 8 - 6
tools/usb/ffs-aio-example/multibuff/host_app/test.c

@@ -33,11 +33,6 @@
 #define VENDOR	0x1d6b
 #define VENDOR	0x1d6b
 #define PRODUCT	0x0105
 #define PRODUCT	0x0105
 
 
-/* endpoints indexes */
-
-#define EP_BULK_IN	(1 | LIBUSB_ENDPOINT_IN)
-#define EP_BULK_OUT	(2 | LIBUSB_ENDPOINT_OUT)
-
 #define BUF_LEN		8192
 #define BUF_LEN		8192
 
 
 /*
 /*
@@ -159,14 +154,21 @@ void test_exit(struct test_state *state)
 int main(void)
 int main(void)
 {
 {
 	struct test_state state;
 	struct test_state state;
+	struct libusb_config_descriptor *conf;
+	struct libusb_interface_descriptor const *iface;
+	unsigned char addr;
 
 
 	if (test_init(&state))
 	if (test_init(&state))
 		return 1;
 		return 1;
 
 
+	libusb_get_config_descriptor(state.found, 0, &conf);
+	iface = &conf->interface[0].altsetting[0];
+	addr = iface->endpoint[0].bEndpointAddress;
+
 	while (1) {
 	while (1) {
 		static unsigned char buffer[BUF_LEN];
 		static unsigned char buffer[BUF_LEN];
 		int bytes;
 		int bytes;
-		libusb_bulk_transfer(state.handle, EP_BULK_IN, buffer, BUF_LEN,
+		libusb_bulk_transfer(state.handle, addr, buffer, BUF_LEN,
 				     &bytes, 500);
 				     &bytes, 500);
 	}
 	}
 	test_exit(&state);
 	test_exit(&state);

Some files were not shown because too many files changed in this diff