瀏覽代碼

Merge tag 'usb-3.20-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/usb

Pull USB patches from Greg KH:
 "Here's the big pull request for the USB driver tree for 3.20-rc1.

  Nothing major happening here, just lots of gadget driver updates, new
  device ids, and a bunch of cleanups.

  All of these have been in linux-next for a while with no reported
  issues"

* tag 'usb-3.20-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/usb: (299 commits)
  usb: musb: fix device hotplug behind hub
  usb: dwc2: Fix a bug in reading the endpoint directions from reg.
  staging: emxx_udc: fix the build error
  usb: Retry port status check on resume to work around RH bugs
  Revert "usb: Reset USB-3 devices on USB-3 link bounce"
  uhci-hub: use HUB_CHAR_*
  usb: kconfig: replace PPC_OF with PPC
  ehci-pci: disable for Intel MID platforms (update)
  usb: gadget: Kconfig: use bool instead of boolean
  usb: musb: blackfin: remove incorrect __exit_p()
  USB: fix use-after-free bug in usb_hcd_unlink_urb()
  ehci-pci: disable for Intel MID platforms
  usb: host: pci_quirks: joing string literals
  USB: add flag for HCDs that can't receive wakeup requests (isp1760-hcd)
  USB: usbfs: allow URBs to be reaped after disconnection
  cdc-acm: kill unnecessary messages
  cdc-acm: add sanity checks
  usb: phy: phy-generic: Fix USB PHY gpio reset
  usb: dwc2: fix USB core dependencies
  usb: renesas_usbhs: fix NULL pointer dereference in dma_release_channel()
  ...
Linus Torvalds 10 年之前
父節點
當前提交
e29876723f
共有 100 個文件被更改,包括 5975 次插入1709 次删除
  1. 265 0
      Documentation/ABI/testing/configfs-usb-gadget-uvc
  2. 16 27
      Documentation/devicetree/bindings/phy/phy-miphy28lp.txt
  3. 37 0
      Documentation/devicetree/bindings/phy/rockchip-usb-phy.txt
  4. 1 1
      Documentation/devicetree/bindings/phy/samsung-phy.txt
  5. 4 1
      Documentation/devicetree/bindings/usb/atmel-usb.txt
  6. 4 0
      Documentation/devicetree/bindings/usb/dwc2.txt
  7. 2 0
      Documentation/devicetree/bindings/usb/renesas_usbhs.txt
  8. 1 0
      Documentation/devicetree/bindings/usb/usb-ehci.txt
  9. 9 1
      Documentation/devicetree/bindings/usb/usb-nop-xceiv.txt
  10. 728 0
      Documentation/usb/gadget-testing.txt
  11. 6 2
      Documentation/usb/gadget_serial.txt
  12. 1 1
      Documentation/usb/usbmon.txt
  13. 1 1
      MAINTAINERS
  14. 63 89
      arch/mips/cavium-octeon/octeon-platform.c
  15. 7 0
      drivers/phy/Kconfig
  16. 1 0
      drivers/phy/Makefile
  17. 2 2
      drivers/phy/phy-armada375-usb2.c
  18. 57 32
      drivers/phy/phy-exynos-mipi-video.c
  19. 32 29
      drivers/phy/phy-miphy28lp.c
  20. 158 0
      drivers/phy/phy-rockchip-usb.c
  21. 112 31
      drivers/phy/phy-ti-pipe3.c
  22. 4 4
      drivers/staging/emxx_udc/emxx_udc.c
  23. 0 1
      drivers/staging/emxx_udc/emxx_udc.h
  24. 2 0
      drivers/usb/Kconfig
  25. 1 1
      drivers/usb/Makefile
  26. 3 0
      drivers/usb/chipidea/ci_hdrc_pci.c
  27. 16 2
      drivers/usb/chipidea/udc.c
  28. 25 29
      drivers/usb/class/cdc-acm.c
  29. 17 9
      drivers/usb/core/buffer.c
  30. 37 26
      drivers/usb/core/devio.c
  31. 12 17
      drivers/usb/core/driver.c
  32. 8 8
      drivers/usb/core/hcd.c
  33. 36 46
      drivers/usb/core/hub.c
  34. 3 20
      drivers/usb/core/message.c
  35. 1 0
      drivers/usb/core/usb.c
  36. 2 2
      drivers/usb/dwc2/Kconfig
  37. 1 1
      drivers/usb/dwc2/core.c
  38. 46 14
      drivers/usb/dwc2/core.h
  39. 831 360
      drivers/usb/dwc2/gadget.c
  40. 19 81
      drivers/usb/dwc2/hcd.c
  41. 2 0
      drivers/usb/dwc2/hw.h
  42. 34 2
      drivers/usb/dwc2/platform.c
  43. 0 6
      drivers/usb/dwc3/Kconfig
  44. 0 1
      drivers/usb/dwc3/Makefile
  45. 1 1
      drivers/usb/dwc3/core.c
  46. 0 5
      drivers/usb/dwc3/core.h
  47. 32 146
      drivers/usb/dwc3/dwc3-pci.c
  48. 1 1
      drivers/usb/dwc3/ep0.c
  49. 43 39
      drivers/usb/dwc3/gadget.c
  50. 10 0
      drivers/usb/dwc3/trace.h
  51. 11 0
      drivers/usb/gadget/Kconfig
  52. 1 1
      drivers/usb/gadget/composite.c
  53. 1 1
      drivers/usb/gadget/function/Makefile
  54. 99 20
      drivers/usb/gadget/function/f_fs.c
  55. 1 1
      drivers/usb/gadget/function/f_hid.c
  56. 10 10
      drivers/usb/gadget/function/f_sourcesink.c
  57. 10 6
      drivers/usb/gadget/function/f_uac1.c
  58. 128 8
      drivers/usb/gadget/function/f_uvc.c
  59. 1 3
      drivers/usb/gadget/function/u_ether.c
  60. 25 0
      drivers/usb/gadget/function/u_fs.h
  61. 1 2
      drivers/usb/gadget/function/u_uac1.c
  62. 0 1
      drivers/usb/gadget/function/u_uac1.h
  63. 51 1
      drivers/usb/gadget/function/u_uvc.h
  64. 2468 0
      drivers/usb/gadget/function/uvc_configfs.c
  65. 22 0
      drivers/usb/gadget/function/uvc_configfs.h
  66. 4 4
      drivers/usb/gadget/udc/at91_udc.c
  67. 0 1
      drivers/usb/gadget/udc/at91_udc.h
  68. 95 52
      drivers/usb/gadget/udc/atmel_usba_udc.c
  69. 9 0
      drivers/usb/gadget/udc/atmel_usba_udc.h
  70. 0 1
      drivers/usb/gadget/udc/bdc/bdc_core.c
  71. 5 5
      drivers/usb/gadget/udc/bdc/bdc_ep.c
  72. 1 0
      drivers/usb/gadget/udc/bdc/bdc_udc.c
  73. 7 2
      drivers/usb/gadget/udc/dummy_hcd.c
  74. 1 1
      drivers/usb/gadget/udc/fsl_qe_udc.c
  75. 3 2
      drivers/usb/gadget/udc/fsl_udc_core.c
  76. 4 8
      drivers/usb/gadget/udc/lpc32xx_udc.c
  77. 0 3
      drivers/usb/gadget/udc/mv_udc_core.c
  78. 2 5
      drivers/usb/gadget/udc/net2272.c
  79. 0 1
      drivers/usb/gadget/udc/net2272.h
  80. 130 403
      drivers/usb/gadget/udc/net2280.c
  81. 3 21
      drivers/usb/gadget/udc/net2280.h
  82. 1 0
      drivers/usb/gadget/udc/omap_udc.c
  83. 1 0
      drivers/usb/gadget/udc/pch_udc.c
  84. 0 2
      drivers/usb/gadget/udc/pxa25x_udc.c
  85. 0 2
      drivers/usb/gadget/udc/pxa27x_udc.c
  86. 1 0
      drivers/usb/gadget/udc/r8a66597-udc.c
  87. 1 20
      drivers/usb/gadget/udc/s3c2410_udc.c
  88. 2 0
      drivers/usb/gadget/udc/udc-core.c
  89. 4 18
      drivers/usb/host/Kconfig
  90. 0 3
      drivers/usb/host/Makefile
  91. 77 25
      drivers/usb/host/ehci-atmel.c
  92. 0 1
      drivers/usb/host/ehci-fsl.c
  93. 0 1
      drivers/usb/host/ehci-grlib.c
  94. 3 3
      drivers/usb/host/ehci-hcd.c
  95. 4 4
      drivers/usb/host/ehci-hub.c
  96. 26 1
      drivers/usb/host/ehci-pci.c
  97. 68 24
      drivers/usb/host/ehci-platform.c
  98. 0 1
      drivers/usb/host/ehci-pmcmsp.c
  99. 0 1
      drivers/usb/host/ehci-ppc-of.c
  100. 0 1
      drivers/usb/host/ehci-sead3.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

+ 16 - 27
Documentation/devicetree/bindings/phy/phy-miphy28lp.txt

@@ -26,6 +26,7 @@ Required properties (port (child) node):
 		  filled in "reg". It can also contain the offset of the system configuration
 		  filled in "reg". It can also contain the offset of the system configuration
 		  registers used as glue-logic to setup the device for SATA/PCIe or USB3
 		  registers used as glue-logic to setup the device for SATA/PCIe or USB3
 		  devices.
 		  devices.
+- st,syscfg	: Offset of the parent configuration register.
 - resets	: phandle to the parent reset controller.
 - resets	: phandle to the parent reset controller.
 - reset-names	: Associated name must be "miphy-sw-rst".
 - reset-names	: Associated name must be "miphy-sw-rst".
 
 
@@ -54,18 +55,12 @@ example:
 			phy_port0: port@9b22000 {
 			phy_port0: port@9b22000 {
 				reg = <0x9b22000 0xff>,
 				reg = <0x9b22000 0xff>,
 				      <0x9b09000 0xff>,
 				      <0x9b09000 0xff>,
-				      <0x9b04000 0xff>,
-				      <0x114 0x4>, /* sysctrl MiPHY cntrl */
-				      <0x818 0x4>, /* sysctrl MiPHY status*/
-				      <0xe0  0x4>, /* sysctrl PCIe */
-				      <0xec  0x4>; /* sysctrl SATA */
+				      <0x9b04000 0xff>;
 				reg-names = "sata-up",
 				reg-names = "sata-up",
 					    "pcie-up",
 					    "pcie-up",
-					    "pipew",
-					    "miphy-ctrl-glue",
-					    "miphy-status-glue",
-					    "pcie-glue",
-					    "sata-glue";
+					    "pipew";
+
+				st,syscfg = <0x114 0x818 0xe0 0xec>;
 				#phy-cells = <1>;
 				#phy-cells = <1>;
 				st,osc-rdy;
 				st,osc-rdy;
 				reset-names = "miphy-sw-rst";
 				reset-names = "miphy-sw-rst";
@@ -75,18 +70,13 @@ example:
 			phy_port1: port@9b2a000 {
 			phy_port1: port@9b2a000 {
 				reg = <0x9b2a000 0xff>,
 				reg = <0x9b2a000 0xff>,
 				      <0x9b19000 0xff>,
 				      <0x9b19000 0xff>,
-				      <0x9b14000 0xff>,
-				      <0x118 0x4>,
-				      <0x81c 0x4>,
-				      <0xe4  0x4>,
-				      <0xf0  0x4>;
+				      <0x9b14000 0xff>;
 				reg-names = "sata-up",
 				reg-names = "sata-up",
 					    "pcie-up",
 					    "pcie-up",
-					    "pipew",
-					    "miphy-ctrl-glue",
-					    "miphy-status-glue",
-					    "pcie-glue",
-					    "sata-glue";
+					    "pipew";
+
+				st,syscfg = <0x118 0x81c 0xe4 0xf0>;
+
 				#phy-cells = <1>;
 				#phy-cells = <1>;
 				st,osc-force-ext;
 				st,osc-force-ext;
 				reset-names = "miphy-sw-rst";
 				reset-names = "miphy-sw-rst";
@@ -95,13 +85,12 @@ example:
 
 
 			phy_port2: port@8f95000 {
 			phy_port2: port@8f95000 {
 				reg = <0x8f95000 0xff>,
 				reg = <0x8f95000 0xff>,
-				      <0x8f90000 0xff>,
-				      <0x11c 0x4>,
-				      <0x820 0x4>;
+				      <0x8f90000 0xff>;
 				reg-names = "pipew",
 				reg-names = "pipew",
-				    "usb3-up",
-				    "miphy-ctrl-glue",
-				    "miphy-status-glue";
+					    "usb3-up";
+
+				st,syscfg = <0x11c 0x820>;
+
 				#phy-cells = <1>;
 				#phy-cells = <1>;
 				reset-names = "miphy-sw-rst";
 				reset-names = "miphy-sw-rst";
 				resets = <&softreset STIH407_MIPHY2_SOFTRESET>;
 				resets = <&softreset STIH407_MIPHY2_SOFTRESET>;
@@ -125,4 +114,4 @@ example:
 
 
 Macro definitions for the supported miphy configuration can be found in:
 Macro definitions for the supported miphy configuration can be found in:
 
 
-include/dt-bindings/phy/phy-miphy28lp.h
+include/dt-bindings/phy/phy.h

+ 37 - 0
Documentation/devicetree/bindings/phy/rockchip-usb-phy.txt

@@ -0,0 +1,37 @@
+ROCKCHIP USB2 PHY
+
+Required properties:
+ - compatible: rockchip,rk3288-usb-phy
+ - rockchip,grf : phandle to the syscon managing the "general
+   register files"
+ - #address-cells: should be 1
+ - #size-cells: should be 0
+
+Sub-nodes:
+Each PHY should be represented as a sub-node.
+
+Sub-nodes
+required properties:
+- #phy-cells: should be 0
+- reg: PHY configure reg address offset in GRF
+		"0x320" - for PHY attach to OTG controller
+		"0x334" - for PHY attach to HOST0 controller
+		"0x348" - for PHY attach to HOST1 controller
+
+Optional Properties:
+- clocks : phandle + clock specifier for the phy clocks
+- clock-names: string, clock name, must be "phyclk"
+
+Example:
+
+usbphy: phy {
+	compatible = "rockchip,rk3288-usb-phy";
+	rockchip,grf = <&grf>;
+	#address-cells = <1>;
+	#size-cells = <0>;
+
+	usbphy0: usb-phy0 {
+		#phy-cells = <0>;
+		reg = <0x320>;
+	};
+};

+ 1 - 1
Documentation/devicetree/bindings/phy/samsung-phy.txt

@@ -3,8 +3,8 @@ Samsung S5P/EXYNOS SoC series MIPI CSIS/DSIM DPHY
 
 
 Required properties:
 Required properties:
 - compatible : should be "samsung,s5pv210-mipi-video-phy";
 - compatible : should be "samsung,s5pv210-mipi-video-phy";
-- reg : offset and length of the MIPI DPHY register set;
 - #phy-cells : from the generic phy bindings, must be 1;
 - #phy-cells : from the generic phy bindings, must be 1;
+- syscon - phandle to the PMU system controller;
 
 
 For "samsung,s5pv210-mipi-video-phy" compatible PHYs the second cell in
 For "samsung,s5pv210-mipi-video-phy" compatible PHYs the second cell in
 the PHY specifier identifies the PHY and its meaning is as follows:
 the PHY specifier identifies the PHY and its meaning is as follows:

+ 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 {

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

@@ -12,6 +12,7 @@ Optional properties:
  - big-endian-regs : boolean, set this for hcds with big-endian registers
  - big-endian-regs : boolean, set this for hcds with big-endian registers
  - big-endian-desc : boolean, set this for hcds with big-endian descriptors
  - big-endian-desc : boolean, set this for hcds with big-endian descriptors
  - big-endian : boolean, for hcds with big-endian-regs + big-endian-desc
  - big-endian : boolean, for hcds with big-endian-regs + big-endian-desc
+ - needs-reset-on-resume : boolean, set this to force EHCI reset after resume
  - clocks : a list of phandle + clock specifier pairs
  - clocks : a list of phandle + clock specifier pairs
  - phys : phandle + phy specifier pair
  - phys : phandle + phy specifier pair
  - phy-names : "usb"
  - phy-names : "usb"

+ 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
Documentation/usb/usbmon.txt

@@ -72,7 +72,7 @@ to listen on a single bus, otherwise, to listen on all buses, type:
 
 
 # cat /sys/kernel/debug/usb/usbmon/0u > /tmp/1.mon.out
 # cat /sys/kernel/debug/usb/usbmon/0u > /tmp/1.mon.out
 
 
-This process will be reading until killed. Naturally, the output can be
+This process will read until it is killed. Naturally, the output can be
 redirected to a desirable location. This is preferred, because it is going
 redirected to a desirable location. This is preferred, because it is going
 to be quite long.
 to be quite long.
 
 

+ 1 - 1
MAINTAINERS

@@ -3048,7 +3048,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

+ 63 - 89
arch/mips/cavium-octeon/octeon-platform.c

@@ -77,7 +77,7 @@ static DEFINE_MUTEX(octeon2_usb_clocks_mutex);
 
 
 static int octeon2_usb_clock_start_cnt;
 static int octeon2_usb_clock_start_cnt;
 
 
-static void octeon2_usb_clocks_start(void)
+static void octeon2_usb_clocks_start(struct device *dev)
 {
 {
 	u64 div;
 	u64 div;
 	union cvmx_uctlx_if_ena if_ena;
 	union cvmx_uctlx_if_ena if_ena;
@@ -86,6 +86,8 @@ static void octeon2_usb_clocks_start(void)
 	union cvmx_uctlx_uphy_portx_ctl_status port_ctl_status;
 	union cvmx_uctlx_uphy_portx_ctl_status port_ctl_status;
 	int i;
 	int i;
 	unsigned long io_clk_64_to_ns;
 	unsigned long io_clk_64_to_ns;
+	u32 clock_rate = 12000000;
+	bool is_crystal_clock = false;
 
 
 
 
 	mutex_lock(&octeon2_usb_clocks_mutex);
 	mutex_lock(&octeon2_usb_clocks_mutex);
@@ -96,6 +98,28 @@ static void octeon2_usb_clocks_start(void)
 
 
 	io_clk_64_to_ns = 64000000000ull / octeon_get_io_clock_rate();
 	io_clk_64_to_ns = 64000000000ull / octeon_get_io_clock_rate();
 
 
+	if (dev->of_node) {
+		struct device_node *uctl_node;
+		const char *clock_type;
+
+		uctl_node = of_get_parent(dev->of_node);
+		if (!uctl_node) {
+			dev_err(dev, "No UCTL device node\n");
+			goto exit;
+		}
+		i = of_property_read_u32(uctl_node,
+					 "refclk-frequency", &clock_rate);
+		if (i) {
+			dev_err(dev, "No UCTL \"refclk-frequency\"\n");
+			goto exit;
+		}
+		i = of_property_read_string(uctl_node,
+					    "refclk-type", &clock_type);
+
+		if (!i && strcmp("crystal", clock_type) == 0)
+			is_crystal_clock = true;
+	}
+
 	/*
 	/*
 	 * Step 1: Wait for voltages stable.  That surely happened
 	 * Step 1: Wait for voltages stable.  That surely happened
 	 * before starting the kernel.
 	 * before starting the kernel.
@@ -126,9 +150,22 @@ static void octeon2_usb_clocks_start(void)
 	cvmx_write_csr(CVMX_UCTLX_CLK_RST_CTL(0), clk_rst_ctl.u64);
 	cvmx_write_csr(CVMX_UCTLX_CLK_RST_CTL(0), clk_rst_ctl.u64);
 
 
 	/* 3b */
 	/* 3b */
-	/* 12MHz crystal. */
-	clk_rst_ctl.s.p_refclk_sel = 0;
-	clk_rst_ctl.s.p_refclk_div = 0;
+	clk_rst_ctl.s.p_refclk_sel = is_crystal_clock ? 0 : 1;
+	switch (clock_rate) {
+	default:
+		pr_err("Invalid UCTL clock rate of %u, using 12000000 instead\n",
+			clock_rate);
+		/* Fall through */
+	case 12000000:
+		clk_rst_ctl.s.p_refclk_div = 0;
+		break;
+	case 24000000:
+		clk_rst_ctl.s.p_refclk_div = 1;
+		break;
+	case 48000000:
+		clk_rst_ctl.s.p_refclk_div = 2;
+		break;
+	}
 	cvmx_write_csr(CVMX_UCTLX_CLK_RST_CTL(0), clk_rst_ctl.u64);
 	cvmx_write_csr(CVMX_UCTLX_CLK_RST_CTL(0), clk_rst_ctl.u64);
 
 
 	/* 3c */
 	/* 3c */
@@ -259,7 +296,7 @@ static void octeon2_usb_clocks_stop(void)
 
 
 static int octeon_ehci_power_on(struct platform_device *pdev)
 static int octeon_ehci_power_on(struct platform_device *pdev)
 {
 {
-	octeon2_usb_clocks_start();
+	octeon2_usb_clocks_start(&pdev->dev);
 	return 0;
 	return 0;
 }
 }
 
 
@@ -273,15 +310,16 @@ static struct usb_ehci_pdata octeon_ehci_pdata = {
 #ifdef __BIG_ENDIAN
 #ifdef __BIG_ENDIAN
 	.big_endian_mmio	= 1,
 	.big_endian_mmio	= 1,
 #endif
 #endif
+	.dma_mask_64	= 1,
 	.power_on	= octeon_ehci_power_on,
 	.power_on	= octeon_ehci_power_on,
 	.power_off	= octeon_ehci_power_off,
 	.power_off	= octeon_ehci_power_off,
 };
 };
 
 
-static void __init octeon_ehci_hw_start(void)
+static void __init octeon_ehci_hw_start(struct device *dev)
 {
 {
 	union cvmx_uctlx_ehci_ctl ehci_ctl;
 	union cvmx_uctlx_ehci_ctl ehci_ctl;
 
 
-	octeon2_usb_clocks_start();
+	octeon2_usb_clocks_start(dev);
 
 
 	ehci_ctl.u64 = cvmx_read_csr(CVMX_UCTLX_EHCI_CTL(0));
 	ehci_ctl.u64 = cvmx_read_csr(CVMX_UCTLX_EHCI_CTL(0));
 	/* Use 64-bit addressing. */
 	/* Use 64-bit addressing. */
@@ -294,64 +332,30 @@ static void __init octeon_ehci_hw_start(void)
 	octeon2_usb_clocks_stop();
 	octeon2_usb_clocks_stop();
 }
 }
 
 
-static u64 octeon_ehci_dma_mask = DMA_BIT_MASK(64);
-
 static int __init octeon_ehci_device_init(void)
 static int __init octeon_ehci_device_init(void)
 {
 {
 	struct platform_device *pd;
 	struct platform_device *pd;
+	struct device_node *ehci_node;
 	int ret = 0;
 	int ret = 0;
 
 
-	struct resource usb_resources[] = {
-		{
-			.flags	= IORESOURCE_MEM,
-		}, {
-			.flags	= IORESOURCE_IRQ,
-		}
-	};
-
-	/* Only Octeon2 has ehci/ohci */
-	if (!OCTEON_IS_MODEL(OCTEON_CN63XX))
+	ehci_node = of_find_node_by_name(NULL, "ehci");
+	if (!ehci_node)
 		return 0;
 		return 0;
 
 
-	if (octeon_is_simulation() || usb_disabled())
-		return 0; /* No USB in the simulator. */
-
-	pd = platform_device_alloc("ehci-platform", 0);
-	if (!pd) {
-		ret = -ENOMEM;
-		goto out;
-	}
-
-	usb_resources[0].start = 0x00016F0000000000ULL;
-	usb_resources[0].end = usb_resources[0].start + 0x100;
-
-	usb_resources[1].start = OCTEON_IRQ_USB0;
-	usb_resources[1].end = OCTEON_IRQ_USB0;
-
-	ret = platform_device_add_resources(pd, usb_resources,
-					    ARRAY_SIZE(usb_resources));
-	if (ret)
-		goto fail;
+	pd = of_find_device_by_node(ehci_node);
+	if (!pd)
+		return 0;
 
 
-	pd->dev.dma_mask = &octeon_ehci_dma_mask;
 	pd->dev.platform_data = &octeon_ehci_pdata;
 	pd->dev.platform_data = &octeon_ehci_pdata;
-	octeon_ehci_hw_start();
+	octeon_ehci_hw_start(&pd->dev);
 
 
-	ret = platform_device_add(pd);
-	if (ret)
-		goto fail;
-
-	return ret;
-fail:
-	platform_device_put(pd);
-out:
 	return ret;
 	return ret;
 }
 }
 device_initcall(octeon_ehci_device_init);
 device_initcall(octeon_ehci_device_init);
 
 
 static int octeon_ohci_power_on(struct platform_device *pdev)
 static int octeon_ohci_power_on(struct platform_device *pdev)
 {
 {
-	octeon2_usb_clocks_start();
+	octeon2_usb_clocks_start(&pdev->dev);
 	return 0;
 	return 0;
 }
 }
 
 
@@ -369,11 +373,11 @@ static struct usb_ohci_pdata octeon_ohci_pdata = {
 	.power_off	= octeon_ohci_power_off,
 	.power_off	= octeon_ohci_power_off,
 };
 };
 
 
-static void __init octeon_ohci_hw_start(void)
+static void __init octeon_ohci_hw_start(struct device *dev)
 {
 {
 	union cvmx_uctlx_ohci_ctl ohci_ctl;
 	union cvmx_uctlx_ohci_ctl ohci_ctl;
 
 
-	octeon2_usb_clocks_start();
+	octeon2_usb_clocks_start(dev);
 
 
 	ohci_ctl.u64 = cvmx_read_csr(CVMX_UCTLX_OHCI_CTL(0));
 	ohci_ctl.u64 = cvmx_read_csr(CVMX_UCTLX_OHCI_CTL(0));
 	ohci_ctl.s.l2c_addr_msb = 0;
 	ohci_ctl.s.l2c_addr_msb = 0;
@@ -387,57 +391,27 @@ static void __init octeon_ohci_hw_start(void)
 static int __init octeon_ohci_device_init(void)
 static int __init octeon_ohci_device_init(void)
 {
 {
 	struct platform_device *pd;
 	struct platform_device *pd;
+	struct device_node *ohci_node;
 	int ret = 0;
 	int ret = 0;
 
 
-	struct resource usb_resources[] = {
-		{
-			.flags	= IORESOURCE_MEM,
-		}, {
-			.flags	= IORESOURCE_IRQ,
-		}
-	};
-
-	/* Only Octeon2 has ehci/ohci */
-	if (!OCTEON_IS_MODEL(OCTEON_CN63XX))
+	ohci_node = of_find_node_by_name(NULL, "ohci");
+	if (!ohci_node)
 		return 0;
 		return 0;
 
 
-	if (octeon_is_simulation() || usb_disabled())
-		return 0; /* No USB in the simulator. */
-
-	pd = platform_device_alloc("ohci-platform", 0);
-	if (!pd) {
-		ret = -ENOMEM;
-		goto out;
-	}
-
-	usb_resources[0].start = 0x00016F0000000400ULL;
-	usb_resources[0].end = usb_resources[0].start + 0x100;
-
-	usb_resources[1].start = OCTEON_IRQ_USB0;
-	usb_resources[1].end = OCTEON_IRQ_USB0;
-
-	ret = platform_device_add_resources(pd, usb_resources,
-					    ARRAY_SIZE(usb_resources));
-	if (ret)
-		goto fail;
+	pd = of_find_device_by_node(ohci_node);
+	if (!pd)
+		return 0;
 
 
 	pd->dev.platform_data = &octeon_ohci_pdata;
 	pd->dev.platform_data = &octeon_ohci_pdata;
-	octeon_ohci_hw_start();
-
-	ret = platform_device_add(pd);
-	if (ret)
-		goto fail;
+	octeon_ohci_hw_start(&pd->dev);
 
 
-	return ret;
-fail:
-	platform_device_put(pd);
-out:
 	return ret;
 	return ret;
 }
 }
 device_initcall(octeon_ohci_device_init);
 device_initcall(octeon_ohci_device_init);
 
 
 #endif /* CONFIG_USB */
 #endif /* CONFIG_USB */
 
 
+
 static struct of_device_id __initdata octeon_ids[] = {
 static struct of_device_id __initdata octeon_ids[] = {
 	{ .compatible = "simple-bus", },
 	{ .compatible = "simple-bus", },
 	{ .compatible = "cavium,octeon-6335-uctl", },
 	{ .compatible = "cavium,octeon-6335-uctl", },

+ 7 - 0
drivers/phy/Kconfig

@@ -239,6 +239,13 @@ config PHY_QCOM_IPQ806X_SATA
 	depends on OF
 	depends on OF
 	select GENERIC_PHY
 	select GENERIC_PHY
 
 
+config PHY_ROCKCHIP_USB
+	tristate "Rockchip USB2 PHY Driver"
+	depends on ARCH_ROCKCHIP && OF
+	select GENERIC_PHY
+	help
+	  Enable this to support the Rockchip USB 2.0 PHY.
+
 config PHY_ST_SPEAR1310_MIPHY
 config PHY_ST_SPEAR1310_MIPHY
 	tristate "ST SPEAR1310-MIPHY driver"
 	tristate "ST SPEAR1310-MIPHY driver"
 	select GENERIC_PHY
 	select GENERIC_PHY

+ 1 - 0
drivers/phy/Makefile

@@ -28,6 +28,7 @@ phy-exynos-usb2-$(CONFIG_PHY_EXYNOS5250_USB2)	+= phy-exynos5250-usb2.o
 phy-exynos-usb2-$(CONFIG_PHY_S5PV210_USB2)	+= phy-s5pv210-usb2.o
 phy-exynos-usb2-$(CONFIG_PHY_S5PV210_USB2)	+= phy-s5pv210-usb2.o
 obj-$(CONFIG_PHY_EXYNOS5_USBDRD)	+= phy-exynos5-usbdrd.o
 obj-$(CONFIG_PHY_EXYNOS5_USBDRD)	+= phy-exynos5-usbdrd.o
 obj-$(CONFIG_PHY_QCOM_APQ8064_SATA)	+= phy-qcom-apq8064-sata.o
 obj-$(CONFIG_PHY_QCOM_APQ8064_SATA)	+= phy-qcom-apq8064-sata.o
+obj-$(CONFIG_PHY_ROCKCHIP_USB) += phy-rockchip-usb.o
 obj-$(CONFIG_PHY_QCOM_IPQ806X_SATA)	+= phy-qcom-ipq806x-sata.o
 obj-$(CONFIG_PHY_QCOM_IPQ806X_SATA)	+= phy-qcom-ipq806x-sata.o
 obj-$(CONFIG_PHY_ST_SPEAR1310_MIPHY)	+= phy-spear1310-miphy.o
 obj-$(CONFIG_PHY_ST_SPEAR1310_MIPHY)	+= phy-spear1310-miphy.o
 obj-$(CONFIG_PHY_ST_SPEAR1340_MIPHY)	+= phy-spear1340-miphy.o
 obj-$(CONFIG_PHY_ST_SPEAR1340_MIPHY)	+= phy-spear1340-miphy.o

+ 2 - 2
drivers/phy/phy-armada375-usb2.c

@@ -118,8 +118,8 @@ static int armada375_usb_phy_probe(struct platform_device *pdev)
 
 
 	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 	usb_cluster_base = devm_ioremap_resource(&pdev->dev, res);
 	usb_cluster_base = devm_ioremap_resource(&pdev->dev, res);
-	if (!usb_cluster_base)
-		return -ENOMEM;
+	if (IS_ERR(usb_cluster_base))
+		return PTR_ERR(usb_cluster_base);
 
 
 	phy = devm_phy_create(dev, NULL, &armada375_usb_phy_ops);
 	phy = devm_phy_create(dev, NULL, &armada375_usb_phy_ops);
 	if (IS_ERR(phy)) {
 	if (IS_ERR(phy)) {

+ 57 - 32
drivers/phy/phy-exynos-mipi-video.c

@@ -12,19 +12,18 @@
 #include <linux/err.h>
 #include <linux/err.h>
 #include <linux/io.h>
 #include <linux/io.h>
 #include <linux/kernel.h>
 #include <linux/kernel.h>
+#include <linux/mfd/syscon/exynos4-pmu.h>
 #include <linux/module.h>
 #include <linux/module.h>
 #include <linux/of.h>
 #include <linux/of.h>
 #include <linux/of_address.h>
 #include <linux/of_address.h>
 #include <linux/phy/phy.h>
 #include <linux/phy/phy.h>
 #include <linux/platform_device.h>
 #include <linux/platform_device.h>
+#include <linux/regmap.h>
 #include <linux/spinlock.h>
 #include <linux/spinlock.h>
+#include <linux/mfd/syscon.h>
 
 
-/* MIPI_PHYn_CONTROL register offset: n = 0..1 */
+/* MIPI_PHYn_CONTROL reg. offset (for base address from ioremap): n = 0..1 */
 #define EXYNOS_MIPI_PHY_CONTROL(n)	((n) * 4)
 #define EXYNOS_MIPI_PHY_CONTROL(n)	((n) * 4)
-#define EXYNOS_MIPI_PHY_ENABLE		(1 << 0)
-#define EXYNOS_MIPI_PHY_SRESETN		(1 << 1)
-#define EXYNOS_MIPI_PHY_MRESETN		(1 << 2)
-#define EXYNOS_MIPI_PHY_RESET_MASK	(3 << 1)
 
 
 enum exynos_mipi_phy_id {
 enum exynos_mipi_phy_id {
 	EXYNOS_MIPI_PHY_ID_CSIS0,
 	EXYNOS_MIPI_PHY_ID_CSIS0,
@@ -38,43 +37,62 @@ enum exynos_mipi_phy_id {
 	((id) == EXYNOS_MIPI_PHY_ID_DSIM0 || (id) == EXYNOS_MIPI_PHY_ID_DSIM1)
 	((id) == EXYNOS_MIPI_PHY_ID_DSIM0 || (id) == EXYNOS_MIPI_PHY_ID_DSIM1)
 
 
 struct exynos_mipi_video_phy {
 struct exynos_mipi_video_phy {
-	spinlock_t slock;
 	struct video_phy_desc {
 	struct video_phy_desc {
 		struct phy *phy;
 		struct phy *phy;
 		unsigned int index;
 		unsigned int index;
 	} phys[EXYNOS_MIPI_PHYS_NUM];
 	} phys[EXYNOS_MIPI_PHYS_NUM];
+	spinlock_t slock;
 	void __iomem *regs;
 	void __iomem *regs;
+	struct mutex mutex;
+	struct regmap *regmap;
 };
 };
 
 
 static int __set_phy_state(struct exynos_mipi_video_phy *state,
 static int __set_phy_state(struct exynos_mipi_video_phy *state,
 			enum exynos_mipi_phy_id id, unsigned int on)
 			enum exynos_mipi_phy_id id, unsigned int on)
 {
 {
+	const unsigned int offset = EXYNOS4_MIPI_PHY_CONTROL(id / 2);
 	void __iomem *addr;
 	void __iomem *addr;
-	u32 reg, reset;
-
-	addr = state->regs + EXYNOS_MIPI_PHY_CONTROL(id / 2);
+	u32 val, reset;
 
 
 	if (is_mipi_dsim_phy_id(id))
 	if (is_mipi_dsim_phy_id(id))
-		reset = EXYNOS_MIPI_PHY_MRESETN;
-	else
-		reset = EXYNOS_MIPI_PHY_SRESETN;
-
-	spin_lock(&state->slock);
-	reg = readl(addr);
-	if (on)
-		reg |= reset;
+		reset = EXYNOS4_MIPI_PHY_MRESETN;
 	else
 	else
-		reg &= ~reset;
-	writel(reg, addr);
-
-	/* Clear ENABLE bit only if MRESETN, SRESETN bits are not set. */
-	if (on)
-		reg |= EXYNOS_MIPI_PHY_ENABLE;
-	else if (!(reg & EXYNOS_MIPI_PHY_RESET_MASK))
-		reg &= ~EXYNOS_MIPI_PHY_ENABLE;
+		reset = EXYNOS4_MIPI_PHY_SRESETN;
+
+	if (state->regmap) {
+		mutex_lock(&state->mutex);
+		regmap_read(state->regmap, offset, &val);
+		if (on)
+			val |= reset;
+		else
+			val &= ~reset;
+		regmap_write(state->regmap, offset, val);
+		if (on)
+			val |= EXYNOS4_MIPI_PHY_ENABLE;
+		else if (!(val & EXYNOS4_MIPI_PHY_RESET_MASK))
+			val &= ~EXYNOS4_MIPI_PHY_ENABLE;
+		regmap_write(state->regmap, offset, val);
+		mutex_unlock(&state->mutex);
+	} else {
+		addr = state->regs + EXYNOS_MIPI_PHY_CONTROL(id / 2);
+
+		spin_lock(&state->slock);
+		val = readl(addr);
+		if (on)
+			val |= reset;
+		else
+			val &= ~reset;
+		writel(val, addr);
+		/* Clear ENABLE bit only if MRESETN, SRESETN bits are not set */
+		if (on)
+			val |= EXYNOS4_MIPI_PHY_ENABLE;
+		else if (!(val & EXYNOS4_MIPI_PHY_RESET_MASK))
+			val &= ~EXYNOS4_MIPI_PHY_ENABLE;
+
+		writel(val, addr);
+		spin_unlock(&state->slock);
+	}
 
 
-	writel(reg, addr);
-	spin_unlock(&state->slock);
 	return 0;
 	return 0;
 }
 }
 
 
@@ -118,7 +136,6 @@ static int exynos_mipi_video_phy_probe(struct platform_device *pdev)
 {
 {
 	struct exynos_mipi_video_phy *state;
 	struct exynos_mipi_video_phy *state;
 	struct device *dev = &pdev->dev;
 	struct device *dev = &pdev->dev;
-	struct resource *res;
 	struct phy_provider *phy_provider;
 	struct phy_provider *phy_provider;
 	unsigned int i;
 	unsigned int i;
 
 
@@ -126,14 +143,22 @@ static int exynos_mipi_video_phy_probe(struct platform_device *pdev)
 	if (!state)
 	if (!state)
 		return -ENOMEM;
 		return -ENOMEM;
 
 
-	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	state->regmap = syscon_regmap_lookup_by_phandle(dev->of_node, "syscon");
+	if (IS_ERR(state->regmap)) {
+		struct resource *res;
 
 
-	state->regs = devm_ioremap_resource(dev, res);
-	if (IS_ERR(state->regs))
-		return PTR_ERR(state->regs);
+		dev_info(dev, "regmap lookup failed: %ld\n",
+			 PTR_ERR(state->regmap));
+
+		res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+		state->regs = devm_ioremap_resource(dev, res);
+		if (IS_ERR(state->regs))
+			return PTR_ERR(state->regs);
+	}
 
 
 	dev_set_drvdata(dev, state);
 	dev_set_drvdata(dev, state);
 	spin_lock_init(&state->slock);
 	spin_lock_init(&state->slock);
+	mutex_init(&state->mutex);
 
 
 	for (i = 0; i < EXYNOS_MIPI_PHYS_NUM; i++) {
 	for (i = 0; i < EXYNOS_MIPI_PHYS_NUM; i++) {
 		struct phy *phy = devm_phy_create(dev, NULL,
 		struct phy *phy = devm_phy_create(dev, NULL,

+ 32 - 29
drivers/phy/phy-miphy28lp.c

@@ -194,6 +194,14 @@
 #define MIPHY_SATA_BANK_NB	3
 #define MIPHY_SATA_BANK_NB	3
 #define MIPHY_PCIE_BANK_NB	2
 #define MIPHY_PCIE_BANK_NB	2
 
 
+enum {
+	SYSCFG_CTRL,
+	SYSCFG_STATUS,
+	SYSCFG_PCI,
+	SYSCFG_SATA,
+	SYSCFG_REG_MAX,
+};
+
 struct miphy28lp_phy {
 struct miphy28lp_phy {
 	struct phy *phy;
 	struct phy *phy;
 	struct miphy28lp_dev *phydev;
 	struct miphy28lp_dev *phydev;
@@ -211,10 +219,7 @@ struct miphy28lp_phy {
 	u32 sata_gen;
 	u32 sata_gen;
 
 
 	/* Sysconfig registers offsets needed to configure the device */
 	/* Sysconfig registers offsets needed to configure the device */
-	u32 syscfg_miphy_ctrl;
-	u32 syscfg_miphy_status;
-	u32 syscfg_pci;
-	u32 syscfg_sata;
+	u32 syscfg_reg[SYSCFG_REG_MAX];
 	u8 type;
 	u8 type;
 };
 };
 
 
@@ -834,12 +839,12 @@ static int miphy_osc_is_ready(struct miphy28lp_phy *miphy_phy)
 	if (!miphy_phy->osc_rdy)
 	if (!miphy_phy->osc_rdy)
 		return 0;
 		return 0;
 
 
-	if (!miphy_phy->syscfg_miphy_status)
+	if (!miphy_phy->syscfg_reg[SYSCFG_STATUS])
 		return -EINVAL;
 		return -EINVAL;
 
 
 	do {
 	do {
-		regmap_read(miphy_dev->regmap, miphy_phy->syscfg_miphy_status,
-			    &val);
+		regmap_read(miphy_dev->regmap,
+				miphy_phy->syscfg_reg[SYSCFG_STATUS], &val);
 
 
 		if ((val & MIPHY_OSC_RDY) != MIPHY_OSC_RDY)
 		if ((val & MIPHY_OSC_RDY) != MIPHY_OSC_RDY)
 			cpu_relax();
 			cpu_relax();
@@ -888,7 +893,7 @@ static int miphy28lp_setup(struct miphy28lp_phy *miphy_phy, u32 miphy_val)
 	int err;
 	int err;
 	struct miphy28lp_dev *miphy_dev = miphy_phy->phydev;
 	struct miphy28lp_dev *miphy_dev = miphy_phy->phydev;
 
 
-	if (!miphy_phy->syscfg_miphy_ctrl)
+	if (!miphy_phy->syscfg_reg[SYSCFG_CTRL])
 		return -EINVAL;
 		return -EINVAL;
 
 
 	err = reset_control_assert(miphy_phy->miphy_rst);
 	err = reset_control_assert(miphy_phy->miphy_rst);
@@ -900,7 +905,8 @@ static int miphy28lp_setup(struct miphy28lp_phy *miphy_phy, u32 miphy_val)
 	if (miphy_phy->osc_force_ext)
 	if (miphy_phy->osc_force_ext)
 		miphy_val |= MIPHY_OSC_FORCE_EXT;
 		miphy_val |= MIPHY_OSC_FORCE_EXT;
 
 
-	regmap_update_bits(miphy_dev->regmap, miphy_phy->syscfg_miphy_ctrl,
+	regmap_update_bits(miphy_dev->regmap,
+			   miphy_phy->syscfg_reg[SYSCFG_CTRL],
 			   MIPHY_CTRL_MASK, miphy_val);
 			   MIPHY_CTRL_MASK, miphy_val);
 
 
 	err = reset_control_deassert(miphy_phy->miphy_rst);
 	err = reset_control_deassert(miphy_phy->miphy_rst);
@@ -917,8 +923,9 @@ static int miphy28lp_init_sata(struct miphy28lp_phy *miphy_phy)
 	struct miphy28lp_dev *miphy_dev = miphy_phy->phydev;
 	struct miphy28lp_dev *miphy_dev = miphy_phy->phydev;
 	int err, sata_conf = SATA_CTRL_SELECT_SATA;
 	int err, sata_conf = SATA_CTRL_SELECT_SATA;
 
 
-	if ((!miphy_phy->syscfg_sata) || (!miphy_phy->syscfg_pci)
-		|| (!miphy_phy->base))
+	if ((!miphy_phy->syscfg_reg[SYSCFG_SATA]) ||
+			(!miphy_phy->syscfg_reg[SYSCFG_PCI]) ||
+			(!miphy_phy->base))
 		return -EINVAL;
 		return -EINVAL;
 
 
 	dev_info(miphy_dev->dev, "sata-up mode, addr 0x%p\n", miphy_phy->base);
 	dev_info(miphy_dev->dev, "sata-up mode, addr 0x%p\n", miphy_phy->base);
@@ -926,10 +933,11 @@ static int miphy28lp_init_sata(struct miphy28lp_phy *miphy_phy)
 	/* Configure the glue-logic */
 	/* Configure the glue-logic */
 	sata_conf |= ((miphy_phy->sata_gen - SATA_GEN1) << SATA_SPDMODE);
 	sata_conf |= ((miphy_phy->sata_gen - SATA_GEN1) << SATA_SPDMODE);
 
 
-	regmap_update_bits(miphy_dev->regmap, miphy_phy->syscfg_sata,
+	regmap_update_bits(miphy_dev->regmap,
+			   miphy_phy->syscfg_reg[SYSCFG_SATA],
 			   SATA_CTRL_MASK, sata_conf);
 			   SATA_CTRL_MASK, sata_conf);
 
 
-	regmap_update_bits(miphy_dev->regmap, miphy_phy->syscfg_pci,
+	regmap_update_bits(miphy_dev->regmap, miphy_phy->syscfg_reg[SYSCFG_PCI],
 			   PCIE_CTRL_MASK, SATA_CTRL_SELECT_PCIE);
 			   PCIE_CTRL_MASK, SATA_CTRL_SELECT_PCIE);
 
 
 	/* MiPHY path and clocking init */
 	/* MiPHY path and clocking init */
@@ -951,17 +959,19 @@ static int miphy28lp_init_pcie(struct miphy28lp_phy *miphy_phy)
 	struct miphy28lp_dev *miphy_dev = miphy_phy->phydev;
 	struct miphy28lp_dev *miphy_dev = miphy_phy->phydev;
 	int err;
 	int err;
 
 
-	if ((!miphy_phy->syscfg_sata) || (!miphy_phy->syscfg_pci)
+	if ((!miphy_phy->syscfg_reg[SYSCFG_SATA]) ||
+			(!miphy_phy->syscfg_reg[SYSCFG_PCI])
 		|| (!miphy_phy->base) || (!miphy_phy->pipebase))
 		|| (!miphy_phy->base) || (!miphy_phy->pipebase))
 		return -EINVAL;
 		return -EINVAL;
 
 
 	dev_info(miphy_dev->dev, "pcie-up mode, addr 0x%p\n", miphy_phy->base);
 	dev_info(miphy_dev->dev, "pcie-up mode, addr 0x%p\n", miphy_phy->base);
 
 
 	/* Configure the glue-logic */
 	/* Configure the glue-logic */
-	regmap_update_bits(miphy_dev->regmap, miphy_phy->syscfg_sata,
+	regmap_update_bits(miphy_dev->regmap,
+			   miphy_phy->syscfg_reg[SYSCFG_SATA],
 			   SATA_CTRL_MASK, SATA_CTRL_SELECT_PCIE);
 			   SATA_CTRL_MASK, SATA_CTRL_SELECT_PCIE);
 
 
-	regmap_update_bits(miphy_dev->regmap, miphy_phy->syscfg_pci,
+	regmap_update_bits(miphy_dev->regmap, miphy_phy->syscfg_reg[SYSCFG_PCI],
 			   PCIE_CTRL_MASK, SYSCFG_PCIE_PCIE_VAL);
 			   PCIE_CTRL_MASK, SYSCFG_PCIE_PCIE_VAL);
 
 
 	/* MiPHY path and clocking init */
 	/* MiPHY path and clocking init */
@@ -1156,7 +1166,8 @@ static int miphy28lp_probe_resets(struct device_node *node,
 static int miphy28lp_of_probe(struct device_node *np,
 static int miphy28lp_of_probe(struct device_node *np,
 			      struct miphy28lp_phy *miphy_phy)
 			      struct miphy28lp_phy *miphy_phy)
 {
 {
-	struct resource res;
+	int i;
+	u32 ctrlreg;
 
 
 	miphy_phy->osc_force_ext =
 	miphy_phy->osc_force_ext =
 		of_property_read_bool(np, "st,osc-force-ext");
 		of_property_read_bool(np, "st,osc-force-ext");
@@ -1175,18 +1186,10 @@ static int miphy28lp_of_probe(struct device_node *np,
 	if (!miphy_phy->sata_gen)
 	if (!miphy_phy->sata_gen)
 		miphy_phy->sata_gen = SATA_GEN1;
 		miphy_phy->sata_gen = SATA_GEN1;
 
 
-	if (!miphy28lp_get_resource_byname(np, "miphy-ctrl-glue", &res))
-		miphy_phy->syscfg_miphy_ctrl = res.start;
-
-	if (!miphy28lp_get_resource_byname(np, "miphy-status-glue", &res))
-		miphy_phy->syscfg_miphy_status = res.start;
-
-	if (!miphy28lp_get_resource_byname(np, "pcie-glue", &res))
-		miphy_phy->syscfg_pci = res.start;
-
-	if (!miphy28lp_get_resource_byname(np, "sata-glue", &res))
-		miphy_phy->syscfg_sata = res.start;
-
+	for (i = 0; i < SYSCFG_REG_MAX; i++) {
+		if (!of_property_read_u32_index(np, "st,syscfg", i, &ctrlreg))
+			miphy_phy->syscfg_reg[i] = ctrlreg;
+	}
 
 
 	return 0;
 	return 0;
 }
 }

+ 158 - 0
drivers/phy/phy-rockchip-usb.c

@@ -0,0 +1,158 @@
+/*
+ * Rockchip usb PHY driver
+ *
+ * Copyright (C) 2014 Yunzhi Li <lyz@rock-chips.com>
+ * Copyright (C) 2014 ROCKCHIP, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/clk.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/phy/phy.h>
+#include <linux/platform_device.h>
+#include <linux/regulator/consumer.h>
+#include <linux/reset.h>
+#include <linux/regmap.h>
+#include <linux/mfd/syscon.h>
+
+/*
+ * The higher 16-bit of this register is used for write protection
+ * only if BIT(13 + 16) set to 1 the BIT(13) can be written.
+ */
+#define SIDDQ_WRITE_ENA	BIT(29)
+#define SIDDQ_ON		BIT(13)
+#define SIDDQ_OFF		(0 << 13)
+
+struct rockchip_usb_phy {
+	unsigned int	reg_offset;
+	struct regmap	*reg_base;
+	struct clk	*clk;
+	struct phy	*phy;
+};
+
+static int rockchip_usb_phy_power(struct rockchip_usb_phy *phy,
+					   bool siddq)
+{
+	return regmap_write(phy->reg_base, phy->reg_offset,
+			    SIDDQ_WRITE_ENA | (siddq ? SIDDQ_ON : SIDDQ_OFF));
+}
+
+static int rockchip_usb_phy_power_off(struct phy *_phy)
+{
+	struct rockchip_usb_phy *phy = phy_get_drvdata(_phy);
+	int ret = 0;
+
+	/* Power down usb phy analog blocks by set siddq 1 */
+	ret = rockchip_usb_phy_power(phy, 1);
+	if (ret)
+		return ret;
+
+	clk_disable_unprepare(phy->clk);
+	if (ret)
+		return ret;
+
+	return 0;
+}
+
+static int rockchip_usb_phy_power_on(struct phy *_phy)
+{
+	struct rockchip_usb_phy *phy = phy_get_drvdata(_phy);
+	int ret = 0;
+
+	ret = clk_prepare_enable(phy->clk);
+	if (ret)
+		return ret;
+
+	/* Power up usb phy analog blocks by set siddq 0 */
+	ret = rockchip_usb_phy_power(phy, 0);
+	if (ret)
+		return ret;
+
+	return 0;
+}
+
+static struct phy_ops ops = {
+	.power_on	= rockchip_usb_phy_power_on,
+	.power_off	= rockchip_usb_phy_power_off,
+	.owner		= THIS_MODULE,
+};
+
+static int rockchip_usb_phy_probe(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	struct rockchip_usb_phy *rk_phy;
+	struct phy_provider *phy_provider;
+	struct device_node *child;
+	struct regmap *grf;
+	unsigned int reg_offset;
+
+	grf = syscon_regmap_lookup_by_phandle(dev->of_node, "rockchip,grf");
+	if (IS_ERR(grf)) {
+		dev_err(&pdev->dev, "Missing rockchip,grf property\n");
+		return PTR_ERR(grf);
+	}
+
+	for_each_available_child_of_node(dev->of_node, child) {
+		rk_phy = devm_kzalloc(dev, sizeof(*rk_phy), GFP_KERNEL);
+		if (!rk_phy)
+			return -ENOMEM;
+
+		if (of_property_read_u32(child, "reg", &reg_offset)) {
+			dev_err(dev, "missing reg property in node %s\n",
+				child->name);
+			return -EINVAL;
+		}
+
+		rk_phy->reg_offset = reg_offset;
+		rk_phy->reg_base = grf;
+
+		rk_phy->clk = of_clk_get_by_name(child, "phyclk");
+		if (IS_ERR(rk_phy->clk))
+			rk_phy->clk = NULL;
+
+		rk_phy->phy = devm_phy_create(dev, child, &ops);
+		if (IS_ERR(rk_phy->phy)) {
+			dev_err(dev, "failed to create PHY\n");
+			return PTR_ERR(rk_phy->phy);
+		}
+		phy_set_drvdata(rk_phy->phy, rk_phy);
+	}
+
+	phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate);
+	return PTR_ERR_OR_ZERO(phy_provider);
+}
+
+static const struct of_device_id rockchip_usb_phy_dt_ids[] = {
+	{ .compatible = "rockchip,rk3288-usb-phy" },
+	{}
+};
+
+MODULE_DEVICE_TABLE(of, rockchip_usb_phy_dt_ids);
+
+static struct platform_driver rockchip_usb_driver = {
+	.probe		= rockchip_usb_phy_probe,
+	.driver		= {
+		.name	= "rockchip-usb-phy",
+		.owner	= THIS_MODULE,
+		.of_match_table = rockchip_usb_phy_dt_ids,
+	},
+};
+
+module_platform_driver(rockchip_usb_driver);
+
+MODULE_AUTHOR("Yunzhi Li <lyz@rock-chips.com>");
+MODULE_DESCRIPTION("Rockchip USB 2.0 PHY driver");
+MODULE_LICENSE("GPL v2");

+ 112 - 31
drivers/phy/phy-ti-pipe3.c

@@ -28,6 +28,7 @@
 #include <linux/delay.h>
 #include <linux/delay.h>
 #include <linux/phy/omap_control_phy.h>
 #include <linux/phy/omap_control_phy.h>
 #include <linux/of_platform.h>
 #include <linux/of_platform.h>
+#include <linux/spinlock.h>
 
 
 #define	PLL_STATUS		0x00000004
 #define	PLL_STATUS		0x00000004
 #define	PLL_GO			0x00000008
 #define	PLL_GO			0x00000008
@@ -82,6 +83,10 @@ struct ti_pipe3 {
 	struct clk		*refclk;
 	struct clk		*refclk;
 	struct clk		*div_clk;
 	struct clk		*div_clk;
 	struct pipe3_dpll_map	*dpll_map;
 	struct pipe3_dpll_map	*dpll_map;
+	bool			enabled;
+	spinlock_t		lock;	/* serialize clock enable/disable */
+	/* the below flag is needed specifically for SATA */
+	bool			refclk_enabled;
 };
 };
 
 
 static struct pipe3_dpll_map dpll_map_usb[] = {
 static struct pipe3_dpll_map dpll_map_usb[] = {
@@ -307,6 +312,7 @@ static int ti_pipe3_probe(struct platform_device *pdev)
 		return -ENOMEM;
 		return -ENOMEM;
 
 
 	phy->dev		= &pdev->dev;
 	phy->dev		= &pdev->dev;
+	spin_lock_init(&phy->lock);
 
 
 	if (!of_device_is_compatible(node, "ti,phy-pipe3-pcie")) {
 	if (!of_device_is_compatible(node, "ti,phy-pipe3-pcie")) {
 		match = of_match_device(of_match_ptr(ti_pipe3_id_table),
 		match = of_match_device(of_match_ptr(ti_pipe3_id_table),
@@ -333,21 +339,24 @@ static int ti_pipe3_probe(struct platform_device *pdev)
 		}
 		}
 	}
 	}
 
 
+	phy->refclk = devm_clk_get(phy->dev, "refclk");
+	if (IS_ERR(phy->refclk)) {
+		dev_err(&pdev->dev, "unable to get refclk\n");
+		/* older DTBs have missing refclk in SATA PHY
+		 * so don't bail out in case of SATA PHY.
+		 */
+		if (!of_device_is_compatible(node, "ti,phy-pipe3-sata"))
+			return PTR_ERR(phy->refclk);
+	}
+
 	if (!of_device_is_compatible(node, "ti,phy-pipe3-sata")) {
 	if (!of_device_is_compatible(node, "ti,phy-pipe3-sata")) {
 		phy->wkupclk = devm_clk_get(phy->dev, "wkupclk");
 		phy->wkupclk = devm_clk_get(phy->dev, "wkupclk");
 		if (IS_ERR(phy->wkupclk)) {
 		if (IS_ERR(phy->wkupclk)) {
 			dev_err(&pdev->dev, "unable to get wkupclk\n");
 			dev_err(&pdev->dev, "unable to get wkupclk\n");
 			return PTR_ERR(phy->wkupclk);
 			return PTR_ERR(phy->wkupclk);
 		}
 		}
-
-		phy->refclk = devm_clk_get(phy->dev, "refclk");
-		if (IS_ERR(phy->refclk)) {
-			dev_err(&pdev->dev, "unable to get refclk\n");
-			return PTR_ERR(phy->refclk);
-		}
 	} else {
 	} else {
 		phy->wkupclk = ERR_PTR(-ENODEV);
 		phy->wkupclk = ERR_PTR(-ENODEV);
-		phy->refclk = ERR_PTR(-ENODEV);
 	}
 	}
 
 
 	if (of_device_is_compatible(node, "ti,phy-pipe3-pcie")) {
 	if (of_device_is_compatible(node, "ti,phy-pipe3-pcie")) {
@@ -426,33 +435,42 @@ static int ti_pipe3_remove(struct platform_device *pdev)
 }
 }
 
 
 #ifdef CONFIG_PM
 #ifdef CONFIG_PM
-
-static int ti_pipe3_runtime_suspend(struct device *dev)
+static int ti_pipe3_enable_refclk(struct ti_pipe3 *phy)
 {
 {
-	struct ti_pipe3	*phy = dev_get_drvdata(dev);
+	if (!IS_ERR(phy->refclk) && !phy->refclk_enabled) {
+		int ret;
 
 
-	if (!IS_ERR(phy->wkupclk))
-		clk_disable_unprepare(phy->wkupclk);
+		ret = clk_prepare_enable(phy->refclk);
+		if (ret) {
+			dev_err(phy->dev, "Failed to enable refclk %d\n", ret);
+			return ret;
+		}
+		phy->refclk_enabled = true;
+	}
+
+	return 0;
+}
+
+static void ti_pipe3_disable_refclk(struct ti_pipe3 *phy)
+{
 	if (!IS_ERR(phy->refclk))
 	if (!IS_ERR(phy->refclk))
 		clk_disable_unprepare(phy->refclk);
 		clk_disable_unprepare(phy->refclk);
-	if (!IS_ERR(phy->div_clk))
-		clk_disable_unprepare(phy->div_clk);
 
 
-	return 0;
+	phy->refclk_enabled = false;
 }
 }
 
 
-static int ti_pipe3_runtime_resume(struct device *dev)
+static int ti_pipe3_enable_clocks(struct ti_pipe3 *phy)
 {
 {
-	u32 ret = 0;
-	struct ti_pipe3	*phy = dev_get_drvdata(dev);
+	int ret = 0;
+	unsigned long flags;
 
 
-	if (!IS_ERR(phy->refclk)) {
-		ret = clk_prepare_enable(phy->refclk);
-		if (ret) {
-			dev_err(phy->dev, "Failed to enable refclk %d\n", ret);
-			goto err1;
-		}
-	}
+	spin_lock_irqsave(&phy->lock, flags);
+	if (phy->enabled)
+		goto err1;
+
+	ret = ti_pipe3_enable_refclk(phy);
+	if (ret)
+		goto err1;
 
 
 	if (!IS_ERR(phy->wkupclk)) {
 	if (!IS_ERR(phy->wkupclk)) {
 		ret = clk_prepare_enable(phy->wkupclk);
 		ret = clk_prepare_enable(phy->wkupclk);
@@ -469,6 +487,9 @@ static int ti_pipe3_runtime_resume(struct device *dev)
 			goto err3;
 			goto err3;
 		}
 		}
 	}
 	}
+
+	phy->enabled = true;
+	spin_unlock_irqrestore(&phy->lock, flags);
 	return 0;
 	return 0;
 
 
 err3:
 err3:
@@ -479,20 +500,80 @@ err2:
 	if (!IS_ERR(phy->refclk))
 	if (!IS_ERR(phy->refclk))
 		clk_disable_unprepare(phy->refclk);
 		clk_disable_unprepare(phy->refclk);
 
 
+	ti_pipe3_disable_refclk(phy);
 err1:
 err1:
+	spin_unlock_irqrestore(&phy->lock, flags);
 	return ret;
 	return ret;
 }
 }
 
 
+static void ti_pipe3_disable_clocks(struct ti_pipe3 *phy)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&phy->lock, flags);
+	if (!phy->enabled) {
+		spin_unlock_irqrestore(&phy->lock, flags);
+		return;
+	}
+
+	if (!IS_ERR(phy->wkupclk))
+		clk_disable_unprepare(phy->wkupclk);
+	/* Don't disable refclk for SATA PHY due to Errata i783 */
+	if (!of_device_is_compatible(phy->dev->of_node, "ti,phy-pipe3-sata"))
+		ti_pipe3_disable_refclk(phy);
+	if (!IS_ERR(phy->div_clk))
+		clk_disable_unprepare(phy->div_clk);
+	phy->enabled = false;
+	spin_unlock_irqrestore(&phy->lock, flags);
+}
+
+static int ti_pipe3_runtime_suspend(struct device *dev)
+{
+	struct ti_pipe3	*phy = dev_get_drvdata(dev);
+
+	ti_pipe3_disable_clocks(phy);
+	return 0;
+}
+
+static int ti_pipe3_runtime_resume(struct device *dev)
+{
+	struct ti_pipe3	*phy = dev_get_drvdata(dev);
+	int ret = 0;
+
+	ret = ti_pipe3_enable_clocks(phy);
+	return ret;
+}
+
+static int ti_pipe3_suspend(struct device *dev)
+{
+	struct ti_pipe3	*phy = dev_get_drvdata(dev);
+
+	ti_pipe3_disable_clocks(phy);
+	return 0;
+}
+
+static int ti_pipe3_resume(struct device *dev)
+{
+	struct ti_pipe3	*phy = dev_get_drvdata(dev);
+	int ret;
+
+	ret = ti_pipe3_enable_clocks(phy);
+	if (ret)
+		return ret;
+
+	pm_runtime_disable(dev);
+	pm_runtime_set_active(dev);
+	pm_runtime_enable(dev);
+	return 0;
+}
+#endif
+
 static const struct dev_pm_ops ti_pipe3_pm_ops = {
 static const struct dev_pm_ops ti_pipe3_pm_ops = {
 	SET_RUNTIME_PM_OPS(ti_pipe3_runtime_suspend,
 	SET_RUNTIME_PM_OPS(ti_pipe3_runtime_suspend,
 			   ti_pipe3_runtime_resume, NULL)
 			   ti_pipe3_runtime_resume, NULL)
+	SET_SYSTEM_SLEEP_PM_OPS(ti_pipe3_suspend, ti_pipe3_resume)
 };
 };
 
 
-#define DEV_PM_OPS     (&ti_pipe3_pm_ops)
-#else
-#define DEV_PM_OPS     NULL
-#endif
-
 #ifdef CONFIG_OF
 #ifdef CONFIG_OF
 static const struct of_device_id ti_pipe3_id_table[] = {
 static const struct of_device_id ti_pipe3_id_table[] = {
 	{
 	{
@@ -520,7 +601,7 @@ static struct platform_driver ti_pipe3_driver = {
 	.remove		= ti_pipe3_remove,
 	.remove		= ti_pipe3_remove,
 	.driver		= {
 	.driver		= {
 		.name	= "ti-pipe3",
 		.name	= "ti-pipe3",
-		.pm	= DEV_PM_OPS,
+		.pm	= &ti_pipe3_pm_ops,
 		.of_match_table = of_match_ptr(ti_pipe3_id_table),
 		.of_match_table = of_match_ptr(ti_pipe3_id_table),
 	},
 	},
 };
 };

+ 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/

+ 3 - 0
drivers/usb/chipidea/ci_hdrc_pci.c

@@ -111,6 +111,9 @@ static void ci_hdrc_pci_remove(struct pci_dev *pdev)
  * PCI device structure
  * PCI device structure
  *
  *
  * Check "pci.h" for details
  * Check "pci.h" for details
+ *
+ * Note: ehci-pci driver may try to probe the device first. You have to add an
+ * ID to the bypass_pci_id_table in ehci-pci driver to prevent this.
  */
  */
 static const struct pci_device_id ci_hdrc_pci_id_table[] = {
 static const struct pci_device_id ci_hdrc_pci_id_table[] = {
 	{
 	{

+ 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,

+ 25 - 29
drivers/usb/class/cdc-acm.c

@@ -1091,6 +1091,7 @@ static int acm_probe(struct usb_interface *intf,
 	unsigned long quirks;
 	unsigned long quirks;
 	int num_rx_buf;
 	int num_rx_buf;
 	int i;
 	int i;
+	unsigned int elength = 0;
 	int combined_interfaces = 0;
 	int combined_interfaces = 0;
 	struct device *tty_dev;
 	struct device *tty_dev;
 	int rv = -ENOMEM;
 	int rv = -ENOMEM;
@@ -1136,9 +1137,12 @@ static int acm_probe(struct usb_interface *intf,
 			dev_err(&intf->dev, "skipping garbage\n");
 			dev_err(&intf->dev, "skipping garbage\n");
 			goto next_desc;
 			goto next_desc;
 		}
 		}
+		elength = buffer[0];
 
 
 		switch (buffer[2]) {
 		switch (buffer[2]) {
 		case USB_CDC_UNION_TYPE: /* we've found it */
 		case USB_CDC_UNION_TYPE: /* we've found it */
+			if (elength < sizeof(struct usb_cdc_union_desc))
+				goto next_desc;
 			if (union_header) {
 			if (union_header) {
 				dev_err(&intf->dev, "More than one "
 				dev_err(&intf->dev, "More than one "
 					"union descriptor, skipping ...\n");
 					"union descriptor, skipping ...\n");
@@ -1147,29 +1151,36 @@ static int acm_probe(struct usb_interface *intf,
 			union_header = (struct usb_cdc_union_desc *)buffer;
 			union_header = (struct usb_cdc_union_desc *)buffer;
 			break;
 			break;
 		case USB_CDC_COUNTRY_TYPE: /* export through sysfs*/
 		case USB_CDC_COUNTRY_TYPE: /* export through sysfs*/
+			if (elength < sizeof(struct usb_cdc_country_functional_desc))
+				goto next_desc;
 			cfd = (struct usb_cdc_country_functional_desc *)buffer;
 			cfd = (struct usb_cdc_country_functional_desc *)buffer;
 			break;
 			break;
 		case USB_CDC_HEADER_TYPE: /* maybe check version */
 		case USB_CDC_HEADER_TYPE: /* maybe check version */
 			break; /* for now we ignore it */
 			break; /* for now we ignore it */
 		case USB_CDC_ACM_TYPE:
 		case USB_CDC_ACM_TYPE:
+			if (elength < 4)
+				goto next_desc;
 			ac_management_function = buffer[3];
 			ac_management_function = buffer[3];
 			break;
 			break;
 		case USB_CDC_CALL_MANAGEMENT_TYPE:
 		case USB_CDC_CALL_MANAGEMENT_TYPE:
+			if (elength < 5)
+				goto next_desc;
 			call_management_function = buffer[3];
 			call_management_function = buffer[3];
 			call_interface_num = buffer[4];
 			call_interface_num = buffer[4];
 			break;
 			break;
 		default:
 		default:
-			/* there are LOTS more CDC descriptors that
+			/*
+			 * there are LOTS more CDC descriptors that
 			 * could legitimately be found here.
 			 * could legitimately be found here.
 			 */
 			 */
 			dev_dbg(&intf->dev, "Ignoring descriptor: "
 			dev_dbg(&intf->dev, "Ignoring descriptor: "
-					"type %02x, length %d\n",
-					buffer[2], buffer[0]);
+					"type %02x, length %ud\n",
+					buffer[2], elength);
 			break;
 			break;
 		}
 		}
 next_desc:
 next_desc:
-		buflen -= buffer[0];
-		buffer += buffer[0];
+		buflen -= elength;
+		buffer += elength;
 	}
 	}
 
 
 	if (!union_header) {
 	if (!union_header) {
@@ -1287,10 +1298,8 @@ made_compressed_probe:
 	dev_dbg(&intf->dev, "interfaces are valid\n");
 	dev_dbg(&intf->dev, "interfaces are valid\n");
 
 
 	acm = kzalloc(sizeof(struct acm), GFP_KERNEL);
 	acm = kzalloc(sizeof(struct acm), GFP_KERNEL);
-	if (acm == NULL) {
-		dev_err(&intf->dev, "out of memory (acm kzalloc)\n");
+	if (acm == NULL)
 		goto alloc_fail;
 		goto alloc_fail;
-	}
 
 
 	minor = acm_alloc_minor(acm);
 	minor = acm_alloc_minor(acm);
 	if (minor == ACM_TTY_MINORS) {
 	if (minor == ACM_TTY_MINORS) {
@@ -1329,42 +1338,32 @@ made_compressed_probe:
 	acm->quirks = quirks;
 	acm->quirks = quirks;
 
 
 	buf = usb_alloc_coherent(usb_dev, ctrlsize, GFP_KERNEL, &acm->ctrl_dma);
 	buf = usb_alloc_coherent(usb_dev, ctrlsize, GFP_KERNEL, &acm->ctrl_dma);
-	if (!buf) {
-		dev_err(&intf->dev, "out of memory (ctrl buffer alloc)\n");
+	if (!buf)
 		goto alloc_fail2;
 		goto alloc_fail2;
-	}
 	acm->ctrl_buffer = buf;
 	acm->ctrl_buffer = buf;
 
 
-	if (acm_write_buffers_alloc(acm) < 0) {
-		dev_err(&intf->dev, "out of memory (write buffer alloc)\n");
+	if (acm_write_buffers_alloc(acm) < 0)
 		goto alloc_fail4;
 		goto alloc_fail4;
-	}
 
 
 	acm->ctrlurb = usb_alloc_urb(0, GFP_KERNEL);
 	acm->ctrlurb = usb_alloc_urb(0, GFP_KERNEL);
-	if (!acm->ctrlurb) {
-		dev_err(&intf->dev, "out of memory (ctrlurb kmalloc)\n");
+	if (!acm->ctrlurb)
 		goto alloc_fail5;
 		goto alloc_fail5;
-	}
+
 	for (i = 0; i < num_rx_buf; i++) {
 	for (i = 0; i < num_rx_buf; i++) {
 		struct acm_rb *rb = &(acm->read_buffers[i]);
 		struct acm_rb *rb = &(acm->read_buffers[i]);
 		struct urb *urb;
 		struct urb *urb;
 
 
 		rb->base = usb_alloc_coherent(acm->dev, readsize, GFP_KERNEL,
 		rb->base = usb_alloc_coherent(acm->dev, readsize, GFP_KERNEL,
 								&rb->dma);
 								&rb->dma);
-		if (!rb->base) {
-			dev_err(&intf->dev, "out of memory "
-					"(read bufs usb_alloc_coherent)\n");
+		if (!rb->base)
 			goto alloc_fail6;
 			goto alloc_fail6;
-		}
 		rb->index = i;
 		rb->index = i;
 		rb->instance = acm;
 		rb->instance = acm;
 
 
 		urb = usb_alloc_urb(0, GFP_KERNEL);
 		urb = usb_alloc_urb(0, GFP_KERNEL);
-		if (!urb) {
-			dev_err(&intf->dev,
-				"out of memory (read urbs usb_alloc_urb)\n");
+		if (!urb)
 			goto alloc_fail6;
 			goto alloc_fail6;
-		}
+
 		urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
 		urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
 		urb->transfer_dma = rb->dma;
 		urb->transfer_dma = rb->dma;
 		if (acm->is_int_ep) {
 		if (acm->is_int_ep) {
@@ -1389,11 +1388,8 @@ made_compressed_probe:
 		struct acm_wb *snd = &(acm->wb[i]);
 		struct acm_wb *snd = &(acm->wb[i]);
 
 
 		snd->urb = usb_alloc_urb(0, GFP_KERNEL);
 		snd->urb = usb_alloc_urb(0, GFP_KERNEL);
-		if (snd->urb == NULL) {
-			dev_err(&intf->dev,
-				"out of memory (write urbs usb_alloc_urb)\n");
+		if (snd->urb == NULL)
 			goto alloc_fail7;
 			goto alloc_fail7;
-		}
 
 
 		if (usb_endpoint_xfer_int(epwrite))
 		if (usb_endpoint_xfer_int(epwrite))
 			usb_fill_int_urb(snd->urb, usb_dev,
 			usb_fill_int_urb(snd->urb, usb_dev,

+ 17 - 9
drivers/usb/core/buffer.c

@@ -22,17 +22,25 @@
  */
  */
 
 
 /* FIXME tune these based on pool statistics ... */
 /* FIXME tune these based on pool statistics ... */
-static const size_t	pool_max[HCD_BUFFER_POOLS] = {
-	/* platforms without dma-friendly caches might need to
-	 * prevent cacheline sharing...
-	 */
-	32,
-	128,
-	512,
-	PAGE_SIZE / 2
-	/* bigger --> allocate pages */
+static size_t pool_max[HCD_BUFFER_POOLS] = {
+	32, 128, 512, 2048,
 };
 };
 
 
+void __init usb_init_pool_max(void)
+{
+	/*
+	 * The pool_max values must never be smaller than
+	 * ARCH_KMALLOC_MINALIGN.
+	 */
+	if (ARCH_KMALLOC_MINALIGN <= 32)
+		;			/* Original value is okay */
+	else if (ARCH_KMALLOC_MINALIGN <= 64)
+		pool_max[0] = 64;
+	else if (ARCH_KMALLOC_MINALIGN <= 128)
+		pool_max[0] = 0;	/* Don't use this pool */
+	else
+		BUILD_BUG();		/* We don't allow this */
+}
 
 
 /* SETUP primitives */
 /* SETUP primitives */
 
 

+ 37 - 26
drivers/usb/core/devio.c

@@ -1689,7 +1689,7 @@ static struct async *reap_as(struct usb_dev_state *ps)
 	for (;;) {
 	for (;;) {
 		__set_current_state(TASK_INTERRUPTIBLE);
 		__set_current_state(TASK_INTERRUPTIBLE);
 		as = async_getcompleted(ps);
 		as = async_getcompleted(ps);
-		if (as)
+		if (as || !connected(ps))
 			break;
 			break;
 		if (signal_pending(current))
 		if (signal_pending(current))
 			break;
 			break;
@@ -1712,7 +1712,7 @@ static int proc_reapurb(struct usb_dev_state *ps, void __user *arg)
 	}
 	}
 	if (signal_pending(current))
 	if (signal_pending(current))
 		return -EINTR;
 		return -EINTR;
-	return -EIO;
+	return -ENODEV;
 }
 }
 
 
 static int proc_reapurbnonblock(struct usb_dev_state *ps, void __user *arg)
 static int proc_reapurbnonblock(struct usb_dev_state *ps, void __user *arg)
@@ -1721,10 +1721,11 @@ static int proc_reapurbnonblock(struct usb_dev_state *ps, void __user *arg)
 	struct async *as;
 	struct async *as;
 
 
 	as = async_getcompleted(ps);
 	as = async_getcompleted(ps);
-	retval = -EAGAIN;
 	if (as) {
 	if (as) {
 		retval = processcompl(as, (void __user * __user *)arg);
 		retval = processcompl(as, (void __user * __user *)arg);
 		free_async(as);
 		free_async(as);
+	} else {
+		retval = (connected(ps) ? -EAGAIN : -ENODEV);
 	}
 	}
 	return retval;
 	return retval;
 }
 }
@@ -1854,7 +1855,7 @@ static int proc_reapurb_compat(struct usb_dev_state *ps, void __user *arg)
 	}
 	}
 	if (signal_pending(current))
 	if (signal_pending(current))
 		return -EINTR;
 		return -EINTR;
-	return -EIO;
+	return -ENODEV;
 }
 }
 
 
 static int proc_reapurbnonblock_compat(struct usb_dev_state *ps, void __user *arg)
 static int proc_reapurbnonblock_compat(struct usb_dev_state *ps, void __user *arg)
@@ -1862,11 +1863,12 @@ static int proc_reapurbnonblock_compat(struct usb_dev_state *ps, void __user *ar
 	int retval;
 	int retval;
 	struct async *as;
 	struct async *as;
 
 
-	retval = -EAGAIN;
 	as = async_getcompleted(ps);
 	as = async_getcompleted(ps);
 	if (as) {
 	if (as) {
 		retval = processcompl_compat(as, (void __user * __user *)arg);
 		retval = processcompl_compat(as, (void __user * __user *)arg);
 		free_async(as);
 		free_async(as);
+	} else {
+		retval = (connected(ps) ? -EAGAIN : -ENODEV);
 	}
 	}
 	return retval;
 	return retval;
 }
 }
@@ -2038,7 +2040,8 @@ static int proc_get_capabilities(struct usb_dev_state *ps, void __user *arg)
 {
 {
 	__u32 caps;
 	__u32 caps;
 
 
-	caps = USBDEVFS_CAP_ZERO_PACKET | USBDEVFS_CAP_NO_PACKET_SIZE_LIM;
+	caps = USBDEVFS_CAP_ZERO_PACKET | USBDEVFS_CAP_NO_PACKET_SIZE_LIM |
+			USBDEVFS_CAP_REAP_AFTER_DISCONNECT;
 	if (!ps->dev->bus->no_stop_on_short)
 	if (!ps->dev->bus->no_stop_on_short)
 		caps |= USBDEVFS_CAP_BULK_CONTINUATION;
 		caps |= USBDEVFS_CAP_BULK_CONTINUATION;
 	if (ps->dev->bus->sg_tablesize)
 	if (ps->dev->bus->sg_tablesize)
@@ -2138,6 +2141,32 @@ static long usbdev_do_ioctl(struct file *file, unsigned int cmd,
 		return -EPERM;
 		return -EPERM;
 
 
 	usb_lock_device(dev);
 	usb_lock_device(dev);
+
+	/* Reap operations are allowed even after disconnection */
+	switch (cmd) {
+	case USBDEVFS_REAPURB:
+		snoop(&dev->dev, "%s: REAPURB\n", __func__);
+		ret = proc_reapurb(ps, p);
+		goto done;
+
+	case USBDEVFS_REAPURBNDELAY:
+		snoop(&dev->dev, "%s: REAPURBNDELAY\n", __func__);
+		ret = proc_reapurbnonblock(ps, p);
+		goto done;
+
+#ifdef CONFIG_COMPAT
+	case USBDEVFS_REAPURB32:
+		snoop(&dev->dev, "%s: REAPURB32\n", __func__);
+		ret = proc_reapurb_compat(ps, p);
+		goto done;
+
+	case USBDEVFS_REAPURBNDELAY32:
+		snoop(&dev->dev, "%s: REAPURBNDELAY32\n", __func__);
+		ret = proc_reapurbnonblock_compat(ps, p);
+		goto done;
+#endif
+	}
+
 	if (!connected(ps)) {
 	if (!connected(ps)) {
 		usb_unlock_device(dev);
 		usb_unlock_device(dev);
 		return -ENODEV;
 		return -ENODEV;
@@ -2231,16 +2260,6 @@ static long usbdev_do_ioctl(struct file *file, unsigned int cmd,
 			inode->i_mtime = CURRENT_TIME;
 			inode->i_mtime = CURRENT_TIME;
 		break;
 		break;
 
 
-	case USBDEVFS_REAPURB32:
-		snoop(&dev->dev, "%s: REAPURB32\n", __func__);
-		ret = proc_reapurb_compat(ps, p);
-		break;
-
-	case USBDEVFS_REAPURBNDELAY32:
-		snoop(&dev->dev, "%s: REAPURBNDELAY32\n", __func__);
-		ret = proc_reapurbnonblock_compat(ps, p);
-		break;
-
 	case USBDEVFS_IOCTL32:
 	case USBDEVFS_IOCTL32:
 		snoop(&dev->dev, "%s: IOCTL32\n", __func__);
 		snoop(&dev->dev, "%s: IOCTL32\n", __func__);
 		ret = proc_ioctl_compat(ps, ptr_to_compat(p));
 		ret = proc_ioctl_compat(ps, ptr_to_compat(p));
@@ -2252,16 +2271,6 @@ static long usbdev_do_ioctl(struct file *file, unsigned int cmd,
 		ret = proc_unlinkurb(ps, p);
 		ret = proc_unlinkurb(ps, p);
 		break;
 		break;
 
 
-	case USBDEVFS_REAPURB:
-		snoop(&dev->dev, "%s: REAPURB\n", __func__);
-		ret = proc_reapurb(ps, p);
-		break;
-
-	case USBDEVFS_REAPURBNDELAY:
-		snoop(&dev->dev, "%s: REAPURBNDELAY\n", __func__);
-		ret = proc_reapurbnonblock(ps, p);
-		break;
-
 	case USBDEVFS_DISCSIGNAL:
 	case USBDEVFS_DISCSIGNAL:
 		snoop(&dev->dev, "%s: DISCSIGNAL\n", __func__);
 		snoop(&dev->dev, "%s: DISCSIGNAL\n", __func__);
 		ret = proc_disconnectsignal(ps, p);
 		ret = proc_disconnectsignal(ps, p);
@@ -2304,6 +2313,8 @@ static long usbdev_do_ioctl(struct file *file, unsigned int cmd,
 		ret = proc_free_streams(ps, p);
 		ret = proc_free_streams(ps, p);
 		break;
 		break;
 	}
 	}
+
+ done:
 	usb_unlock_device(dev);
 	usb_unlock_device(dev);
 	if (ret >= 0)
 	if (ret >= 0)
 		inode->i_atime = CURRENT_TIME;
 		inode->i_atime = CURRENT_TIME;

+ 12 - 17
drivers/usb/core/driver.c

@@ -275,21 +275,6 @@ static int usb_unbind_device(struct device *dev)
 	return 0;
 	return 0;
 }
 }
 
 
-/*
- * Cancel any pending scheduled resets
- *
- * [see usb_queue_reset_device()]
- *
- * Called after unconfiguring / when releasing interfaces. See
- * comments in __usb_queue_reset_device() regarding
- * udev->reset_running.
- */
-static void usb_cancel_queued_reset(struct usb_interface *iface)
-{
-	if (iface->reset_running == 0)
-		cancel_work_sync(&iface->reset_ws);
-}
-
 /* called from driver core with dev locked */
 /* called from driver core with dev locked */
 static int usb_probe_interface(struct device *dev)
 static int usb_probe_interface(struct device *dev)
 {
 {
@@ -380,7 +365,6 @@ static int usb_probe_interface(struct device *dev)
 	usb_set_intfdata(intf, NULL);
 	usb_set_intfdata(intf, NULL);
 	intf->needs_remote_wakeup = 0;
 	intf->needs_remote_wakeup = 0;
 	intf->condition = USB_INTERFACE_UNBOUND;
 	intf->condition = USB_INTERFACE_UNBOUND;
-	usb_cancel_queued_reset(intf);
 
 
 	/* If the LPM disable succeeded, balance the ref counts. */
 	/* If the LPM disable succeeded, balance the ref counts. */
 	if (!lpm_disable_error)
 	if (!lpm_disable_error)
@@ -425,7 +409,6 @@ static int usb_unbind_interface(struct device *dev)
 		usb_disable_interface(udev, intf, false);
 		usb_disable_interface(udev, intf, false);
 
 
 	driver->disconnect(intf);
 	driver->disconnect(intf);
-	usb_cancel_queued_reset(intf);
 
 
 	/* Free streams */
 	/* Free streams */
 	for (i = 0, j = 0; i < intf->cur_altsetting->desc.bNumEndpoints; i++) {
 	for (i = 0, j = 0; i < intf->cur_altsetting->desc.bNumEndpoints; i++) {
@@ -1797,6 +1780,18 @@ static int autosuspend_check(struct usb_device *udev)
 		dev_dbg(&udev->dev, "remote wakeup needed for autosuspend\n");
 		dev_dbg(&udev->dev, "remote wakeup needed for autosuspend\n");
 		return -EOPNOTSUPP;
 		return -EOPNOTSUPP;
 	}
 	}
+
+	/*
+	 * If the device is a direct child of the root hub and the HCD
+	 * doesn't handle wakeup requests, don't allow autosuspend when
+	 * wakeup is needed.
+	 */
+	if (w && udev->parent == udev->bus->root_hub &&
+			bus_to_hcd(udev->bus)->cant_recv_wakeups) {
+		dev_dbg(&udev->dev, "HCD doesn't handle wakeup requests\n");
+		return -EOPNOTSUPP;
+	}
+
 	udev->do_remote_wakeup = w;
 	udev->do_remote_wakeup = w;
 	return 0;
 	return 0;
 }
 }

+ 8 - 8
drivers/usb/core/hcd.c

@@ -1618,6 +1618,7 @@ static int unlink1(struct usb_hcd *hcd, struct urb *urb, int status)
 int usb_hcd_unlink_urb (struct urb *urb, int status)
 int usb_hcd_unlink_urb (struct urb *urb, int status)
 {
 {
 	struct usb_hcd		*hcd;
 	struct usb_hcd		*hcd;
+	struct usb_device	*udev = urb->dev;
 	int			retval = -EIDRM;
 	int			retval = -EIDRM;
 	unsigned long		flags;
 	unsigned long		flags;
 
 
@@ -1629,20 +1630,19 @@ int usb_hcd_unlink_urb (struct urb *urb, int status)
 	spin_lock_irqsave(&hcd_urb_unlink_lock, flags);
 	spin_lock_irqsave(&hcd_urb_unlink_lock, flags);
 	if (atomic_read(&urb->use_count) > 0) {
 	if (atomic_read(&urb->use_count) > 0) {
 		retval = 0;
 		retval = 0;
-		usb_get_dev(urb->dev);
+		usb_get_dev(udev);
 	}
 	}
 	spin_unlock_irqrestore(&hcd_urb_unlink_lock, flags);
 	spin_unlock_irqrestore(&hcd_urb_unlink_lock, flags);
 	if (retval == 0) {
 	if (retval == 0) {
 		hcd = bus_to_hcd(urb->dev->bus);
 		hcd = bus_to_hcd(urb->dev->bus);
 		retval = unlink1(hcd, urb, status);
 		retval = unlink1(hcd, urb, status);
-		usb_put_dev(urb->dev);
+		if (retval == 0)
+			retval = -EINPROGRESS;
+		else if (retval != -EIDRM && retval != -EBUSY)
+			dev_dbg(&udev->dev, "hcd_unlink_urb %p fail %d\n",
+					urb, retval);
+		usb_put_dev(udev);
 	}
 	}
-
-	if (retval == 0)
-		retval = -EINPROGRESS;
-	else if (retval != -EIDRM && retval != -EBUSY)
-		dev_dbg(&urb->dev->dev, "hcd_unlink_urb %p fail %d\n",
-				urb, retval);
 	return retval;
 	return retval;
 }
 }
 
 

+ 36 - 46
drivers/usb/core/hub.c

@@ -2896,10 +2896,12 @@ static int port_is_suspended(struct usb_hub *hub, unsigned portstatus)
  */
  */
 static int check_port_resume_type(struct usb_device *udev,
 static int check_port_resume_type(struct usb_device *udev,
 		struct usb_hub *hub, int port1,
 		struct usb_hub *hub, int port1,
-		int status, unsigned portchange, unsigned portstatus)
+		int status, u16 portchange, u16 portstatus)
 {
 {
 	struct usb_port *port_dev = hub->ports[port1 - 1];
 	struct usb_port *port_dev = hub->ports[port1 - 1];
+	int retries = 3;
 
 
+ retry:
 	/* Is a warm reset needed to recover the connection? */
 	/* Is a warm reset needed to recover the connection? */
 	if (status == 0 && udev->reset_resume
 	if (status == 0 && udev->reset_resume
 		&& hub_port_warm_reset_required(hub, port1, portstatus)) {
 		&& hub_port_warm_reset_required(hub, port1, portstatus)) {
@@ -2907,10 +2909,17 @@ static int check_port_resume_type(struct usb_device *udev,
 	}
 	}
 	/* Is the device still present? */
 	/* Is the device still present? */
 	else if (status || port_is_suspended(hub, portstatus) ||
 	else if (status || port_is_suspended(hub, portstatus) ||
-			!port_is_power_on(hub, portstatus) ||
-			!(portstatus & USB_PORT_STAT_CONNECTION)) {
+			!port_is_power_on(hub, portstatus)) {
 		if (status >= 0)
 		if (status >= 0)
 			status = -ENODEV;
 			status = -ENODEV;
+	} else if (!(portstatus & USB_PORT_STAT_CONNECTION)) {
+		if (retries--) {
+			usleep_range(200, 300);
+			status = hub_port_status(hub, port1, &portstatus,
+							     &portchange);
+			goto retry;
+		}
+		status = -ENODEV;
 	}
 	}
 
 
 	/* Can't do a normal resume if the port isn't enabled,
 	/* Can't do a normal resume if the port isn't enabled,
@@ -4643,9 +4652,13 @@ static void hub_port_connect(struct usb_hub *hub, int port1, u16 portstatus,
 	if (!(portstatus & USB_PORT_STAT_CONNECTION) ||
 	if (!(portstatus & USB_PORT_STAT_CONNECTION) ||
 			test_bit(port1, hub->removed_bits)) {
 			test_bit(port1, hub->removed_bits)) {
 
 
-		/* maybe switch power back on (e.g. root hub was reset) */
+		/*
+		 * maybe switch power back on (e.g. root hub was reset)
+		 * but only if the port isn't owned by someone else.
+		 */
 		if (hub_is_port_power_switchable(hub)
 		if (hub_is_port_power_switchable(hub)
-				&& !port_is_power_on(hub, portstatus))
+				&& !port_is_power_on(hub, portstatus)
+				&& !port_dev->port_owner)
 			set_port_feature(hdev, port1, USB_PORT_FEAT_POWER);
 			set_port_feature(hdev, port1, USB_PORT_FEAT_POWER);
 
 
 		if (portstatus & USB_PORT_STAT_ENABLE)
 		if (portstatus & USB_PORT_STAT_ENABLE)
@@ -4870,7 +4883,7 @@ static void hub_port_connect_change(struct usb_hub *hub, int port1,
 static void port_event(struct usb_hub *hub, int port1)
 static void port_event(struct usb_hub *hub, int port1)
 		__must_hold(&port_dev->status_lock)
 		__must_hold(&port_dev->status_lock)
 {
 {
-	int connect_change, reset_device = 0;
+	int connect_change;
 	struct usb_port *port_dev = hub->ports[port1 - 1];
 	struct usb_port *port_dev = hub->ports[port1 - 1];
 	struct usb_device *udev = port_dev->child;
 	struct usb_device *udev = port_dev->child;
 	struct usb_device *hdev = hub->hdev;
 	struct usb_device *hdev = hub->hdev;
@@ -4958,30 +4971,14 @@ static void port_event(struct usb_hub *hub, int port1)
 			if (hub_port_reset(hub, port1, NULL,
 			if (hub_port_reset(hub, port1, NULL,
 					HUB_BH_RESET_TIME, true) < 0)
 					HUB_BH_RESET_TIME, true) < 0)
 				hub_port_disable(hub, port1, 1);
 				hub_port_disable(hub, port1, 1);
-		} else
-			reset_device = 1;
-	}
-
-	/*
-	 * On disconnect USB3 protocol ports transit from U0 to
-	 * SS.Inactive to Rx.Detect. If this happens a warm-
-	 * reset is not needed, but a (re)connect may happen
-	 * before hub_wq runs and sees the disconnect, and the
-	 * device may be an unknown state.
-	 *
-	 * If the port went through SS.Inactive without hub_wq
-	 * seeing it the C_LINK_STATE change flag will be set,
-	 * and we reset the dev to put it in a known state.
-	 */
-	if (reset_device || (udev && hub_is_superspeed(hub->hdev)
-				&& (portchange & USB_PORT_STAT_C_LINK_STATE)
-				&& (portstatus & USB_PORT_STAT_CONNECTION))) {
-		usb_unlock_port(port_dev);
-		usb_lock_device(udev);
-		usb_reset_device(udev);
-		usb_unlock_device(udev);
-		usb_lock_port(port_dev);
-		connect_change = 0;
+		} else {
+			usb_unlock_port(port_dev);
+			usb_lock_device(udev);
+			usb_reset_device(udev);
+			usb_unlock_device(udev);
+			usb_lock_port(port_dev);
+			connect_change = 0;
+		}
 	}
 	}
 
 
 	if (connect_change)
 	if (connect_change)
@@ -5577,26 +5574,19 @@ EXPORT_SYMBOL_GPL(usb_reset_device);
  *   possible; depending on how the driver attached to each interface
  *   possible; depending on how the driver attached to each interface
  *   handles ->pre_reset(), the second reset might happen or not.
  *   handles ->pre_reset(), the second reset might happen or not.
  *
  *
- * - If a driver is unbound and it had a pending reset, the reset will
- *   be cancelled.
- *
- * - This function can be called during .probe() or .disconnect()
- *   times. On return from .disconnect(), any pending resets will be
- *   cancelled.
- *
- * There is no no need to lock/unlock the @reset_ws as schedule_work()
- * does its own.
+ * - If the reset is delayed so long that the interface is unbound from
+ *   its driver, the reset will be skipped.
  *
  *
- * NOTE: We don't do any reference count tracking because it is not
- *     needed. The lifecycle of the work_struct is tied to the
- *     usb_interface. Before destroying the interface we cancel the
- *     work_struct, so the fact that work_struct is queued and or
- *     running means the interface (and thus, the device) exist and
- *     are referenced.
+ * - This function can be called during .probe().  It can also be called
+ *   during .disconnect(), but doing so is pointless because the reset
+ *   will not occur.  If you really want to reset the device during
+ *   .disconnect(), call usb_reset_device() directly -- but watch out
+ *   for nested unbinding issues!
  */
  */
 void usb_queue_reset_device(struct usb_interface *iface)
 void usb_queue_reset_device(struct usb_interface *iface)
 {
 {
-	schedule_work(&iface->reset_ws);
+	if (schedule_work(&iface->reset_ws))
+		usb_get_intf(iface);
 }
 }
 EXPORT_SYMBOL_GPL(usb_queue_reset_device);
 EXPORT_SYMBOL_GPL(usb_queue_reset_device);
 
 

+ 3 - 20
drivers/usb/core/message.c

@@ -1551,6 +1551,7 @@ static void usb_release_interface(struct device *dev)
 			altsetting_to_usb_interface_cache(intf->altsetting);
 			altsetting_to_usb_interface_cache(intf->altsetting);
 
 
 	kref_put(&intfc->ref, usb_release_interface_cache);
 	kref_put(&intfc->ref, usb_release_interface_cache);
+	usb_put_dev(interface_to_usbdev(intf));
 	kfree(intf);
 	kfree(intf);
 }
 }
 
 
@@ -1626,24 +1627,6 @@ static struct usb_interface_assoc_descriptor *find_iad(struct usb_device *dev,
 
 
 /*
 /*
  * Internal function to queue a device reset
  * Internal function to queue a device reset
- *
- * This is initialized into the workstruct in 'struct
- * usb_device->reset_ws' that is launched by
- * message.c:usb_set_configuration() when initializing each 'struct
- * usb_interface'.
- *
- * It is safe to get the USB device without reference counts because
- * the life cycle of @iface is bound to the life cycle of @udev. Then,
- * this function will be ran only if @iface is alive (and before
- * freeing it any scheduled instances of it will have been cancelled).
- *
- * We need to set a flag (usb_dev->reset_running) because when we call
- * the reset, the interfaces might be unbound. The current interface
- * cannot try to remove the queued work as it would cause a deadlock
- * (you cannot remove your work from within your executing
- * workqueue). This flag lets it know, so that
- * usb_cancel_queued_reset() doesn't try to do it.
- *
  * See usb_queue_reset_device() for more details
  * See usb_queue_reset_device() for more details
  */
  */
 static void __usb_queue_reset_device(struct work_struct *ws)
 static void __usb_queue_reset_device(struct work_struct *ws)
@@ -1655,11 +1638,10 @@ static void __usb_queue_reset_device(struct work_struct *ws)
 
 
 	rc = usb_lock_device_for_reset(udev, iface);
 	rc = usb_lock_device_for_reset(udev, iface);
 	if (rc >= 0) {
 	if (rc >= 0) {
-		iface->reset_running = 1;
 		usb_reset_device(udev);
 		usb_reset_device(udev);
-		iface->reset_running = 0;
 		usb_unlock_device(udev);
 		usb_unlock_device(udev);
 	}
 	}
+	usb_put_intf(iface);	/* Undo _get_ in usb_queue_reset_device() */
 }
 }
 
 
 
 
@@ -1854,6 +1836,7 @@ free_interfaces:
 		dev_set_name(&intf->dev, "%d-%s:%d.%d",
 		dev_set_name(&intf->dev, "%d-%s:%d.%d",
 			dev->bus->busnum, dev->devpath,
 			dev->bus->busnum, dev->devpath,
 			configuration, alt->desc.bInterfaceNumber);
 			configuration, alt->desc.bInterfaceNumber);
+		usb_get_dev(dev);
 	}
 	}
 	kfree(new_interfaces);
 	kfree(new_interfaces);
 
 

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

@@ -1049,6 +1049,7 @@ static int __init usb_init(void)
 		pr_info("%s: USB support disabled\n", usbcore_name);
 		pr_info("%s: USB support disabled\n", usbcore_name);
 		return 0;
 		return 0;
 	}
 	}
+	usb_init_pool_max();
 
 
 	retval = usb_debugfs_init();
 	retval = usb_debugfs_init();
 	if (retval)
 	if (retval)

+ 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);

+ 19 - 81
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:
@@ -1622,7 +1610,9 @@ static int dwc2_hcd_hub_control(struct dwc2_hsotg *hsotg, u16 typereq,
 		hub_desc->bDescLength = 9;
 		hub_desc->bDescLength = 9;
 		hub_desc->bDescriptorType = 0x29;
 		hub_desc->bDescriptorType = 0x29;
 		hub_desc->bNbrPorts = 1;
 		hub_desc->bNbrPorts = 1;
-		hub_desc->wHubCharacteristics = cpu_to_le16(0x08);
+		hub_desc->wHubCharacteristics =
+			cpu_to_le16(HUB_CHAR_COMMON_LPSM |
+				    HUB_CHAR_INDV_PORT_OCPM);
 		hub_desc->bPwrOn2PwrGood = 1;
 		hub_desc->bPwrOn2PwrGood = 1;
 		hub_desc->bHubContrCurrent = 0;
 		hub_desc->bHubContrCurrent = 0;
 		hub_desc->u.hs.DeviceRemovable[0] = 0;
 		hub_desc->u.hs.DeviceRemovable[0] = 0;
@@ -2315,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)
 {
 {
@@ -2734,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;

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

@@ -521,7 +521,6 @@ static int bdc_remove(struct platform_device *pdev)
 static struct platform_driver bdc_driver = {
 static struct platform_driver bdc_driver = {
 	.driver		= {
 	.driver		= {
 		.name	= BRCM_BDC_NAME,
 		.name	= BRCM_BDC_NAME,
-		.owner	= THIS_MODULE
 	},
 	},
 	.probe		= bdc_probe,
 	.probe		= bdc_probe,
 	.remove		= bdc_remove,
 	.remove		= bdc_remove,

+ 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;

+ 7 - 2
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);
@@ -1924,7 +1925,9 @@ ss_hub_descriptor(struct usb_hub_descriptor *desc)
 	memset(desc, 0, sizeof *desc);
 	memset(desc, 0, sizeof *desc);
 	desc->bDescriptorType = 0x2a;
 	desc->bDescriptorType = 0x2a;
 	desc->bDescLength = 12;
 	desc->bDescLength = 12;
-	desc->wHubCharacteristics = cpu_to_le16(0x0001);
+	desc->wHubCharacteristics = cpu_to_le16(
+			HUB_CHAR_INDV_PORT_LPSM |
+			HUB_CHAR_COMMON_OCPM);
 	desc->bNbrPorts = 1;
 	desc->bNbrPorts = 1;
 	desc->u.ss.bHubHdrDecLat = 0x04; /* Worst case: 0.4 micro sec*/
 	desc->u.ss.bHubHdrDecLat = 0x04; /* Worst case: 0.4 micro sec*/
 	desc->u.ss.DeviceRemovable = 0xffff;
 	desc->u.ss.DeviceRemovable = 0xffff;
@@ -1935,7 +1938,9 @@ static inline void hub_descriptor(struct usb_hub_descriptor *desc)
 	memset(desc, 0, sizeof *desc);
 	memset(desc, 0, sizeof *desc);
 	desc->bDescriptorType = 0x29;
 	desc->bDescriptorType = 0x29;
 	desc->bDescLength = 9;
 	desc->bDescLength = 9;
-	desc->wHubCharacteristics = cpu_to_le16(0x0001);
+	desc->wHubCharacteristics = cpu_to_le16(
+			HUB_CHAR_INDV_PORT_LPSM |
+			HUB_CHAR_COMMON_OCPM);
 	desc->bNbrPorts = 1;
 	desc->bNbrPorts = 1;
 	desc->u.hs.DeviceRemovable[0] = 0xff;
 	desc->u.hs.DeviceRemovable[0] = 0xff;
 	desc->u.hs.DeviceRemovable[1] = 0xff;
 	desc->u.hs.DeviceRemovable[1] = 0xff;

+ 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,
 };
 };
 
 

+ 4 - 18
drivers/usb/host/Kconfig

@@ -219,7 +219,7 @@ config USB_EHCI_TEGRA
 
 
 config USB_EHCI_HCD_PPC_OF
 config USB_EHCI_HCD_PPC_OF
 	bool "EHCI support for PPC USB controller on OF platform bus"
 	bool "EHCI support for PPC USB controller on OF platform bus"
-	depends on PPC_OF
+	depends on PPC
 	default y
 	default y
 	---help---
 	---help---
 	  Enables support for the USB controller present on the PowerPC
 	  Enables support for the USB controller present on the PowerPC
@@ -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---
@@ -494,7 +480,7 @@ config USB_OHCI_ATH79
 
 
 config USB_OHCI_HCD_PPC_OF_BE
 config USB_OHCI_HCD_PPC_OF_BE
 	bool "OHCI support for OF platform bus (big endian)"
 	bool "OHCI support for OF platform bus (big endian)"
-	depends on PPC_OF
+	depends on PPC
 	select USB_OHCI_BIG_ENDIAN_DESC
 	select USB_OHCI_BIG_ENDIAN_DESC
 	select USB_OHCI_BIG_ENDIAN_MMIO
 	select USB_OHCI_BIG_ENDIAN_MMIO
 	---help---
 	---help---
@@ -503,7 +489,7 @@ config USB_OHCI_HCD_PPC_OF_BE
 
 
 config USB_OHCI_HCD_PPC_OF_LE
 config USB_OHCI_HCD_PPC_OF_LE
 	bool "OHCI support for OF platform bus (little endian)"
 	bool "OHCI support for OF platform bus (little endian)"
-	depends on PPC_OF
+	depends on PPC
 	select USB_OHCI_LITTLE_ENDIAN
 	select USB_OHCI_LITTLE_ENDIAN
 	---help---
 	---help---
 	  Enables support for little-endian USB controllers present on the
 	  Enables support for little-endian USB controllers present on the
@@ -511,7 +497,7 @@ config USB_OHCI_HCD_PPC_OF_LE
 
 
 config USB_OHCI_HCD_PPC_OF
 config USB_OHCI_HCD_PPC_OF
 	bool
 	bool
-	depends on PPC_OF
+	depends on PPC
 	default USB_OHCI_HCD_PPC_OF_BE || USB_OHCI_HCD_PPC_OF_LE
 	default USB_OHCI_HCD_PPC_OF_BE || USB_OHCI_HCD_PPC_OF_LE
 
 
 config USB_OHCI_HCD_PCI
 config USB_OHCI_HCD_PCI

+ 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

+ 77 - 25
drivers/usb/host/ehci-atmel.c

@@ -27,44 +27,66 @@
 #define DRIVER_DESC "EHCI Atmel driver"
 #define DRIVER_DESC "EHCI Atmel driver"
 
 
 static const char hcd_name[] = "ehci-atmel";
 static const char hcd_name[] = "ehci-atmel";
-static struct hc_driver __read_mostly ehci_atmel_hc_driver;
 
 
 /* interface and function clocks */
 /* interface and function clocks */
-static struct clk *iclk, *fclk, *uclk;
-static int clocked;
+#define hcd_to_atmel_ehci_priv(h) \
+	((struct atmel_ehci_priv *)hcd_to_ehci(h)->priv)
+
+struct atmel_ehci_priv {
+	struct clk *iclk;
+	struct clk *fclk;
+	struct clk *uclk;
+	bool clocked;
+};
+
+static struct hc_driver __read_mostly ehci_atmel_hc_driver;
+
+static const struct ehci_driver_overrides ehci_atmel_drv_overrides __initconst = {
+	.extra_priv_size = sizeof(struct atmel_ehci_priv),
+};
 
 
 /*-------------------------------------------------------------------------*/
 /*-------------------------------------------------------------------------*/
 
 
-static void atmel_start_clock(void)
+static void atmel_start_clock(struct atmel_ehci_priv *atmel_ehci)
 {
 {
+	if (atmel_ehci->clocked)
+		return;
 	if (IS_ENABLED(CONFIG_COMMON_CLK)) {
 	if (IS_ENABLED(CONFIG_COMMON_CLK)) {
-		clk_set_rate(uclk, 48000000);
-		clk_prepare_enable(uclk);
+		clk_set_rate(atmel_ehci->uclk, 48000000);
+		clk_prepare_enable(atmel_ehci->uclk);
 	}
 	}
-	clk_prepare_enable(iclk);
-	clk_prepare_enable(fclk);
-	clocked = 1;
+	clk_prepare_enable(atmel_ehci->iclk);
+	clk_prepare_enable(atmel_ehci->fclk);
+	atmel_ehci->clocked = true;
 }
 }
 
 
-static void atmel_stop_clock(void)
+static void atmel_stop_clock(struct atmel_ehci_priv *atmel_ehci)
 {
 {
-	clk_disable_unprepare(fclk);
-	clk_disable_unprepare(iclk);
+	if (!atmel_ehci->clocked)
+		return;
+	clk_disable_unprepare(atmel_ehci->fclk);
+	clk_disable_unprepare(atmel_ehci->iclk);
 	if (IS_ENABLED(CONFIG_COMMON_CLK))
 	if (IS_ENABLED(CONFIG_COMMON_CLK))
-		clk_disable_unprepare(uclk);
-	clocked = 0;
+		clk_disable_unprepare(atmel_ehci->uclk);
+	atmel_ehci->clocked = false;
 }
 }
 
 
 static void atmel_start_ehci(struct platform_device *pdev)
 static void atmel_start_ehci(struct platform_device *pdev)
 {
 {
+	struct usb_hcd *hcd = platform_get_drvdata(pdev);
+	struct atmel_ehci_priv *atmel_ehci = hcd_to_atmel_ehci_priv(hcd);
+
 	dev_dbg(&pdev->dev, "start\n");
 	dev_dbg(&pdev->dev, "start\n");
-	atmel_start_clock();
+	atmel_start_clock(atmel_ehci);
 }
 }
 
 
 static void atmel_stop_ehci(struct platform_device *pdev)
 static void atmel_stop_ehci(struct platform_device *pdev)
 {
 {
+	struct usb_hcd *hcd = platform_get_drvdata(pdev);
+	struct atmel_ehci_priv *atmel_ehci = hcd_to_atmel_ehci_priv(hcd);
+
 	dev_dbg(&pdev->dev, "stop\n");
 	dev_dbg(&pdev->dev, "stop\n");
-	atmel_stop_clock();
+	atmel_stop_clock(atmel_ehci);
 }
 }
 
 
 /*-------------------------------------------------------------------------*/
 /*-------------------------------------------------------------------------*/
@@ -75,6 +97,7 @@ static int ehci_atmel_drv_probe(struct platform_device *pdev)
 	const struct hc_driver *driver = &ehci_atmel_hc_driver;
 	const struct hc_driver *driver = &ehci_atmel_hc_driver;
 	struct resource *res;
 	struct resource *res;
 	struct ehci_hcd *ehci;
 	struct ehci_hcd *ehci;
+	struct atmel_ehci_priv *atmel_ehci;
 	int irq;
 	int irq;
 	int retval;
 	int retval;
 
 
@@ -105,6 +128,7 @@ static int ehci_atmel_drv_probe(struct platform_device *pdev)
 		retval = -ENOMEM;
 		retval = -ENOMEM;
 		goto fail_create_hcd;
 		goto fail_create_hcd;
 	}
 	}
+	atmel_ehci = hcd_to_atmel_ehci_priv(hcd);
 
 
 	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 	hcd->regs = devm_ioremap_resource(&pdev->dev, res);
 	hcd->regs = devm_ioremap_resource(&pdev->dev, res);
@@ -116,23 +140,23 @@ static int ehci_atmel_drv_probe(struct platform_device *pdev)
 	hcd->rsrc_start = res->start;
 	hcd->rsrc_start = res->start;
 	hcd->rsrc_len = resource_size(res);
 	hcd->rsrc_len = resource_size(res);
 
 
-	iclk = devm_clk_get(&pdev->dev, "ehci_clk");
-	if (IS_ERR(iclk)) {
+	atmel_ehci->iclk = devm_clk_get(&pdev->dev, "ehci_clk");
+	if (IS_ERR(atmel_ehci->iclk)) {
 		dev_err(&pdev->dev, "Error getting interface clock\n");
 		dev_err(&pdev->dev, "Error getting interface clock\n");
 		retval = -ENOENT;
 		retval = -ENOENT;
 		goto fail_request_resource;
 		goto fail_request_resource;
 	}
 	}
-	fclk = devm_clk_get(&pdev->dev, "uhpck");
-	if (IS_ERR(fclk)) {
+	atmel_ehci->fclk = devm_clk_get(&pdev->dev, "uhpck");
+	if (IS_ERR(atmel_ehci->fclk)) {
 		dev_err(&pdev->dev, "Error getting function clock\n");
 		dev_err(&pdev->dev, "Error getting function clock\n");
 		retval = -ENOENT;
 		retval = -ENOENT;
 		goto fail_request_resource;
 		goto fail_request_resource;
 	}
 	}
 	if (IS_ENABLED(CONFIG_COMMON_CLK)) {
 	if (IS_ENABLED(CONFIG_COMMON_CLK)) {
-		uclk = devm_clk_get(&pdev->dev, "usb_clk");
-		if (IS_ERR(uclk)) {
+		atmel_ehci->uclk = devm_clk_get(&pdev->dev, "usb_clk");
+		if (IS_ERR(atmel_ehci->uclk)) {
 			dev_err(&pdev->dev, "failed to get uclk\n");
 			dev_err(&pdev->dev, "failed to get uclk\n");
-			retval = PTR_ERR(uclk);
+			retval = PTR_ERR(atmel_ehci->uclk);
 			goto fail_request_resource;
 			goto fail_request_resource;
 		}
 		}
 	}
 	}
@@ -169,11 +193,35 @@ static int ehci_atmel_drv_remove(struct platform_device *pdev)
 	usb_put_hcd(hcd);
 	usb_put_hcd(hcd);
 
 
 	atmel_stop_ehci(pdev);
 	atmel_stop_ehci(pdev);
-	fclk = iclk = NULL;
 
 
 	return 0;
 	return 0;
 }
 }
 
 
+#ifdef CONFIG_PM
+static int ehci_atmel_drv_suspend(struct device *dev)
+{
+	struct usb_hcd *hcd = dev_get_drvdata(dev);
+	struct atmel_ehci_priv *atmel_ehci = hcd_to_atmel_ehci_priv(hcd);
+	int ret;
+
+	ret = ehci_suspend(hcd, false);
+	if (ret)
+		return ret;
+
+	atmel_stop_clock(atmel_ehci);
+	return 0;
+}
+
+static int ehci_atmel_drv_resume(struct device *dev)
+{
+	struct usb_hcd *hcd = dev_get_drvdata(dev);
+	struct atmel_ehci_priv *atmel_ehci = hcd_to_atmel_ehci_priv(hcd);
+
+	atmel_start_clock(atmel_ehci);
+	return ehci_resume(hcd, false);
+}
+#endif
+
 #ifdef CONFIG_OF
 #ifdef CONFIG_OF
 static const struct of_device_id atmel_ehci_dt_ids[] = {
 static const struct of_device_id atmel_ehci_dt_ids[] = {
 	{ .compatible = "atmel,at91sam9g45-ehci" },
 	{ .compatible = "atmel,at91sam9g45-ehci" },
@@ -183,12 +231,16 @@ static const struct of_device_id atmel_ehci_dt_ids[] = {
 MODULE_DEVICE_TABLE(of, atmel_ehci_dt_ids);
 MODULE_DEVICE_TABLE(of, atmel_ehci_dt_ids);
 #endif
 #endif
 
 
+static SIMPLE_DEV_PM_OPS(ehci_atmel_pm_ops, ehci_atmel_drv_suspend,
+					ehci_atmel_drv_resume);
+
 static struct platform_driver ehci_atmel_driver = {
 static struct platform_driver ehci_atmel_driver = {
 	.probe		= ehci_atmel_drv_probe,
 	.probe		= ehci_atmel_drv_probe,
 	.remove		= ehci_atmel_drv_remove,
 	.remove		= ehci_atmel_drv_remove,
 	.shutdown	= usb_hcd_platform_shutdown,
 	.shutdown	= usb_hcd_platform_shutdown,
 	.driver		= {
 	.driver		= {
 		.name	= "atmel-ehci",
 		.name	= "atmel-ehci",
+		.pm	= &ehci_atmel_pm_ops,
 		.of_match_table	= of_match_ptr(atmel_ehci_dt_ids),
 		.of_match_table	= of_match_ptr(atmel_ehci_dt_ids),
 	},
 	},
 };
 };
@@ -199,7 +251,7 @@ static int __init ehci_atmel_init(void)
 		return -ENODEV;
 		return -ENODEV;
 
 
 	pr_info("%s: " DRIVER_DESC "\n", hcd_name);
 	pr_info("%s: " DRIVER_DESC "\n", hcd_name);
-	ehci_init_driver(&ehci_atmel_hc_driver, NULL);
+	ehci_init_driver(&ehci_atmel_hc_driver, &ehci_atmel_drv_overrides);
 	return platform_driver_register(&ehci_atmel_driver);
 	return platform_driver_register(&ehci_atmel_driver);
 }
 }
 module_init(ehci_atmel_init);
 module_init(ehci_atmel_init);

+ 0 - 1
drivers/usb/host/ehci-fsl.c

@@ -709,7 +709,6 @@ static struct platform_driver ehci_fsl_driver = {
 	.shutdown = usb_hcd_platform_shutdown,
 	.shutdown = usb_hcd_platform_shutdown,
 	.driver = {
 	.driver = {
 		.name = "fsl-ehci",
 		.name = "fsl-ehci",
-		.owner	= THIS_MODULE,
 		.pm = EHCI_FSL_PM_OPS,
 		.pm = EHCI_FSL_PM_OPS,
 	},
 	},
 };
 };

+ 0 - 1
drivers/usb/host/ehci-grlib.c

@@ -187,7 +187,6 @@ static struct platform_driver ehci_grlib_driver = {
 	.shutdown	= usb_hcd_platform_shutdown,
 	.shutdown	= usb_hcd_platform_shutdown,
 	.driver = {
 	.driver = {
 		.name = "grlib-ehci",
 		.name = "grlib-ehci",
-		.owner = THIS_MODULE,
 		.of_match_table = ehci_hcd_grlib_of_match,
 		.of_match_table = ehci_hcd_grlib_of_match,
 	},
 	},
 };
 };

+ 3 - 3
drivers/usb/host/ehci-hcd.c

@@ -1110,7 +1110,7 @@ int ehci_suspend(struct usb_hcd *hcd, bool do_wakeup)
 EXPORT_SYMBOL_GPL(ehci_suspend);
 EXPORT_SYMBOL_GPL(ehci_suspend);
 
 
 /* Returns 0 if power was preserved, 1 if power was lost */
 /* Returns 0 if power was preserved, 1 if power was lost */
-int ehci_resume(struct usb_hcd *hcd, bool hibernated)
+int ehci_resume(struct usb_hcd *hcd, bool force_reset)
 {
 {
 	struct ehci_hcd		*ehci = hcd_to_ehci(hcd);
 	struct ehci_hcd		*ehci = hcd_to_ehci(hcd);
 
 
@@ -1124,12 +1124,12 @@ int ehci_resume(struct usb_hcd *hcd, bool hibernated)
 		return 0;		/* Controller is dead */
 		return 0;		/* Controller is dead */
 
 
 	/*
 	/*
-	 * If CF is still set and we aren't resuming from hibernation
+	 * If CF is still set and reset isn't forced
 	 * then we maintained suspend power.
 	 * then we maintained suspend power.
 	 * Just undo the effect of ehci_suspend().
 	 * Just undo the effect of ehci_suspend().
 	 */
 	 */
 	if (ehci_readl(ehci, &ehci->regs->configured_flag) == FLAG_CF &&
 	if (ehci_readl(ehci, &ehci->regs->configured_flag) == FLAG_CF &&
-			!hibernated) {
+			!force_reset) {
 		int	mask = INTR_MASK;
 		int	mask = INTR_MASK;
 
 
 		ehci_prepare_ports_for_controller_resume(ehci);
 		ehci_prepare_ports_for_controller_resume(ehci);

+ 4 - 4
drivers/usb/host/ehci-hub.c

@@ -700,15 +700,15 @@ ehci_hub_descriptor (
 	memset(&desc->u.hs.DeviceRemovable[0], 0, temp);
 	memset(&desc->u.hs.DeviceRemovable[0], 0, temp);
 	memset(&desc->u.hs.DeviceRemovable[temp], 0xff, temp);
 	memset(&desc->u.hs.DeviceRemovable[temp], 0xff, temp);
 
 
-	temp = 0x0008;			/* per-port overcurrent reporting */
+	temp = HUB_CHAR_INDV_PORT_OCPM;	/* per-port overcurrent reporting */
 	if (HCS_PPC (ehci->hcs_params))
 	if (HCS_PPC (ehci->hcs_params))
-		temp |= 0x0001;		/* per-port power control */
+		temp |= HUB_CHAR_INDV_PORT_LPSM; /* per-port power control */
 	else
 	else
-		temp |= 0x0002;		/* no power switching */
+		temp |= HUB_CHAR_NO_LPSM; /* no power switching */
 #if 0
 #if 0
 // re-enable when we support USB_PORT_FEAT_INDICATOR below.
 // re-enable when we support USB_PORT_FEAT_INDICATOR below.
 	if (HCS_INDICATOR (ehci->hcs_params))
 	if (HCS_INDICATOR (ehci->hcs_params))
-		temp |= 0x0080;		/* per-port indicators (LEDs) */
+		temp |= HUB_CHAR_PORTIND; /* per-port indicators (LEDs) */
 #endif
 #endif
 	desc->wHubCharacteristics = cpu_to_le16(temp);
 	desc->wHubCharacteristics = cpu_to_le16(temp);
 }
 }

+ 26 - 1
drivers/usb/host/ehci-pci.c

@@ -42,6 +42,24 @@ static inline bool is_intel_quark_x1000(struct pci_dev *pdev)
 		pdev->device == PCI_DEVICE_ID_INTEL_QUARK_X1000_SOC;
 		pdev->device == PCI_DEVICE_ID_INTEL_QUARK_X1000_SOC;
 }
 }
 
 
+/*
+ * This is the list of PCI IDs for the devices that have EHCI USB class and
+ * specific drivers for that. One of the example is a ChipIdea device installed
+ * on some Intel MID platforms.
+ */
+static const struct pci_device_id bypass_pci_id_table[] = {
+	/* ChipIdea on Intel MID platform */
+	{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x0811), },
+	{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x0829), },
+	{ PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0xe006), },
+	{}
+};
+
+static inline bool is_bypassed_id(struct pci_dev *pdev)
+{
+	return !!pci_match_id(bypass_pci_id_table, pdev);
+}
+
 /*
 /*
  * 0x84 is the offset of in/out threshold register,
  * 0x84 is the offset of in/out threshold register,
  * and it is the same offset as the register of 'hostpc'.
  * and it is the same offset as the register of 'hostpc'.
@@ -352,6 +370,13 @@ static const struct ehci_driver_overrides pci_overrides __initconst = {
 
 
 /*-------------------------------------------------------------------------*/
 /*-------------------------------------------------------------------------*/
 
 
+static int ehci_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
+{
+	if (is_bypassed_id(pdev))
+		return -ENODEV;
+	return usb_hcd_pci_probe(pdev, id);
+}
+
 /* PCI driver selection metadata; PCI hotplugging uses this */
 /* PCI driver selection metadata; PCI hotplugging uses this */
 static const struct pci_device_id pci_ids [] = { {
 static const struct pci_device_id pci_ids [] = { {
 	/* handle any USB 2.0 EHCI controller */
 	/* handle any USB 2.0 EHCI controller */
@@ -370,7 +395,7 @@ static struct pci_driver ehci_pci_driver = {
 	.name =		(char *) hcd_name,
 	.name =		(char *) hcd_name,
 	.id_table =	pci_ids,
 	.id_table =	pci_ids,
 
 
-	.probe =	usb_hcd_pci_probe,
+	.probe =	ehci_pci_probe,
 	.remove =	usb_hcd_pci_remove,
 	.remove =	usb_hcd_pci_remove,
 	.shutdown = 	usb_hcd_pci_shutdown,
 	.shutdown = 	usb_hcd_pci_shutdown,
 
 

+ 68 - 24
drivers/usb/host/ehci-platform.c

@@ -43,7 +43,8 @@
 struct ehci_platform_priv {
 struct ehci_platform_priv {
 	struct clk *clks[EHCI_MAX_CLKS];
 	struct clk *clks[EHCI_MAX_CLKS];
 	struct reset_control *rst;
 	struct reset_control *rst;
-	struct phy *phy;
+	struct phy **phys;
+	int num_phys;
 };
 };
 
 
 static const char hcd_name[] = "ehci-platform";
 static const char hcd_name[] = "ehci-platform";
@@ -78,7 +79,7 @@ static int ehci_platform_power_on(struct platform_device *dev)
 {
 {
 	struct usb_hcd *hcd = platform_get_drvdata(dev);
 	struct usb_hcd *hcd = platform_get_drvdata(dev);
 	struct ehci_platform_priv *priv = hcd_to_ehci_priv(hcd);
 	struct ehci_platform_priv *priv = hcd_to_ehci_priv(hcd);
-	int clk, ret;
+	int clk, ret, phy_num;
 
 
 	for (clk = 0; clk < EHCI_MAX_CLKS && priv->clks[clk]; clk++) {
 	for (clk = 0; clk < EHCI_MAX_CLKS && priv->clks[clk]; clk++) {
 		ret = clk_prepare_enable(priv->clks[clk]);
 		ret = clk_prepare_enable(priv->clks[clk]);
@@ -86,20 +87,28 @@ static int ehci_platform_power_on(struct platform_device *dev)
 			goto err_disable_clks;
 			goto err_disable_clks;
 	}
 	}
 
 
-	if (priv->phy) {
-		ret = phy_init(priv->phy);
-		if (ret)
-			goto err_disable_clks;
-
-		ret = phy_power_on(priv->phy);
-		if (ret)
-			goto err_exit_phy;
+	for (phy_num = 0; phy_num < priv->num_phys; phy_num++) {
+		if (priv->phys[phy_num]) {
+			ret = phy_init(priv->phys[phy_num]);
+			if (ret)
+				goto err_exit_phy;
+			ret = phy_power_on(priv->phys[phy_num]);
+			if (ret) {
+				phy_exit(priv->phys[phy_num]);
+				goto err_exit_phy;
+			}
+		}
 	}
 	}
 
 
 	return 0;
 	return 0;
 
 
 err_exit_phy:
 err_exit_phy:
-	phy_exit(priv->phy);
+	while (--phy_num >= 0) {
+		if (priv->phys[phy_num]) {
+			phy_power_off(priv->phys[phy_num]);
+			phy_exit(priv->phys[phy_num]);
+		}
+	}
 err_disable_clks:
 err_disable_clks:
 	while (--clk >= 0)
 	while (--clk >= 0)
 		clk_disable_unprepare(priv->clks[clk]);
 		clk_disable_unprepare(priv->clks[clk]);
@@ -111,11 +120,13 @@ static void ehci_platform_power_off(struct platform_device *dev)
 {
 {
 	struct usb_hcd *hcd = platform_get_drvdata(dev);
 	struct usb_hcd *hcd = platform_get_drvdata(dev);
 	struct ehci_platform_priv *priv = hcd_to_ehci_priv(hcd);
 	struct ehci_platform_priv *priv = hcd_to_ehci_priv(hcd);
-	int clk;
+	int clk, phy_num;
 
 
-	if (priv->phy) {
-		phy_power_off(priv->phy);
-		phy_exit(priv->phy);
+	for (phy_num = 0; phy_num < priv->num_phys; phy_num++) {
+		if (priv->phys[phy_num]) {
+			phy_power_off(priv->phys[phy_num]);
+			phy_exit(priv->phys[phy_num]);
+		}
 	}
 	}
 
 
 	for (clk = EHCI_MAX_CLKS - 1; clk >= 0; clk--)
 	for (clk = EHCI_MAX_CLKS - 1; clk >= 0; clk--)
@@ -143,7 +154,8 @@ static int ehci_platform_probe(struct platform_device *dev)
 	struct usb_ehci_pdata *pdata = dev_get_platdata(&dev->dev);
 	struct usb_ehci_pdata *pdata = dev_get_platdata(&dev->dev);
 	struct ehci_platform_priv *priv;
 	struct ehci_platform_priv *priv;
 	struct ehci_hcd *ehci;
 	struct ehci_hcd *ehci;
-	int err, irq, clk = 0;
+	const char *phy_name;
+	int err, irq, phy_num, clk = 0;
 
 
 	if (usb_disabled())
 	if (usb_disabled())
 		return -ENODEV;
 		return -ENODEV;
@@ -155,7 +167,8 @@ static int ehci_platform_probe(struct platform_device *dev)
 	if (!pdata)
 	if (!pdata)
 		pdata = &ehci_platform_defaults;
 		pdata = &ehci_platform_defaults;
 
 
-	err = dma_coerce_mask_and_coherent(&dev->dev, DMA_BIT_MASK(32));
+	err = dma_coerce_mask_and_coherent(&dev->dev,
+		pdata->dma_mask_64 ? DMA_BIT_MASK(64) : DMA_BIT_MASK(32));
 	if (err)
 	if (err)
 		return err;
 		return err;
 
 
@@ -185,12 +198,42 @@ static int ehci_platform_probe(struct platform_device *dev)
 		if (of_property_read_bool(dev->dev.of_node, "big-endian"))
 		if (of_property_read_bool(dev->dev.of_node, "big-endian"))
 			ehci->big_endian_mmio = ehci->big_endian_desc = 1;
 			ehci->big_endian_mmio = ehci->big_endian_desc = 1;
 
 
-		priv->phy = devm_phy_get(&dev->dev, "usb");
-		if (IS_ERR(priv->phy)) {
-			err = PTR_ERR(priv->phy);
-			if (err == -EPROBE_DEFER)
-				goto err_put_hcd;
-			priv->phy = NULL;
+		if (of_property_read_bool(dev->dev.of_node,
+					  "needs-reset-on-resume"))
+			pdata->reset_on_resume = 1;
+
+		priv->num_phys = of_count_phandle_with_args(dev->dev.of_node,
+				"phys", "#phy-cells");
+		priv->num_phys = priv->num_phys > 0 ? priv->num_phys : 1;
+
+		priv->phys = devm_kcalloc(&dev->dev, priv->num_phys,
+				sizeof(struct phy *), GFP_KERNEL);
+		if (!priv->phys)
+			return -ENOMEM;
+
+		for (phy_num = 0; phy_num < priv->num_phys; phy_num++) {
+				err = of_property_read_string_index(
+						dev->dev.of_node,
+						"phy-names", phy_num,
+						&phy_name);
+
+				if (err < 0) {
+					if (priv->num_phys > 1) {
+						dev_err(&dev->dev, "phy-names not provided");
+						goto err_put_hcd;
+					} else
+						phy_name = "usb";
+				}
+
+				priv->phys[phy_num] = devm_phy_get(&dev->dev,
+						phy_name);
+				if (IS_ERR(priv->phys[phy_num])) {
+					err = PTR_ERR(priv->phys[phy_num]);
+					if ((priv->num_phys > 1) ||
+					    (err == -EPROBE_DEFER))
+						goto err_put_hcd;
+					priv->phys[phy_num] = NULL;
+				}
 		}
 		}
 
 
 		for (clk = 0; clk < EHCI_MAX_CLKS; clk++) {
 		for (clk = 0; clk < EHCI_MAX_CLKS; clk++) {
@@ -340,7 +383,7 @@ static int ehci_platform_resume(struct device *dev)
 			return err;
 			return err;
 	}
 	}
 
 
-	ehci_resume(hcd, false);
+	ehci_resume(hcd, pdata->reset_on_resume);
 	return 0;
 	return 0;
 }
 }
 #endif /* CONFIG_PM_SLEEP */
 #endif /* CONFIG_PM_SLEEP */
@@ -349,6 +392,7 @@ static const struct of_device_id vt8500_ehci_ids[] = {
 	{ .compatible = "via,vt8500-ehci", },
 	{ .compatible = "via,vt8500-ehci", },
 	{ .compatible = "wm,prizm-ehci", },
 	{ .compatible = "wm,prizm-ehci", },
 	{ .compatible = "generic-ehci", },
 	{ .compatible = "generic-ehci", },
+	{ .compatible = "cavium,octeon-6335-ehci", },
 	{}
 	{}
 };
 };
 MODULE_DEVICE_TABLE(of, vt8500_ehci_ids);
 MODULE_DEVICE_TABLE(of, vt8500_ehci_ids);

+ 0 - 1
drivers/usb/host/ehci-pmcmsp.c

@@ -325,6 +325,5 @@ static struct platform_driver ehci_hcd_msp_driver = {
 	.remove		= ehci_hcd_msp_drv_remove,
 	.remove		= ehci_hcd_msp_drv_remove,
 	.driver		= {
 	.driver		= {
 		.name	= "pmcmsp-ehci",
 		.name	= "pmcmsp-ehci",
-		.owner	= THIS_MODULE,
 	},
 	},
 };
 };

+ 0 - 1
drivers/usb/host/ehci-ppc-of.c

@@ -234,7 +234,6 @@ static struct platform_driver ehci_hcd_ppc_of_driver = {
 	.shutdown	= usb_hcd_platform_shutdown,
 	.shutdown	= usb_hcd_platform_shutdown,
 	.driver = {
 	.driver = {
 		.name = "ppc-of-ehci",
 		.name = "ppc-of-ehci",
-		.owner = THIS_MODULE,
 		.of_match_table = ehci_hcd_ppc_of_match,
 		.of_match_table = ehci_hcd_ppc_of_match,
 	},
 	},
 };
 };

+ 0 - 1
drivers/usb/host/ehci-sead3.c

@@ -178,7 +178,6 @@ static struct platform_driver ehci_hcd_sead3_driver = {
 	.shutdown	= usb_hcd_platform_shutdown,
 	.shutdown	= usb_hcd_platform_shutdown,
 	.driver = {
 	.driver = {
 		.name	= "sead3-ehci",
 		.name	= "sead3-ehci",
-		.owner	= THIS_MODULE,
 		.pm	= SEAD3_EHCI_PMOPS,
 		.pm	= SEAD3_EHCI_PMOPS,
 	}
 	}
 };
 };

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