OSDN Git Service

Merge tag 'usb-for-v3.20' of git://git.kernel.org/pub/scm/linux/kernel/git/balbi...
authorGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Wed, 4 Feb 2015 19:03:20 +0000 (11:03 -0800)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Wed, 4 Feb 2015 19:03:20 +0000 (11:03 -0800)
Felipe writes:

usb: patches for v3.20 merge window

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

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

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

Signed-off-by: Felipe Balbi <balbi@ti.com>
102 files changed:
Documentation/ABI/testing/configfs-usb-gadget-uvc [new file with mode: 0644]
Documentation/devicetree/bindings/usb/atmel-usb.txt
Documentation/devicetree/bindings/usb/dwc2.txt
Documentation/devicetree/bindings/usb/renesas_usbhs.txt
Documentation/devicetree/bindings/usb/usb-nop-xceiv.txt
Documentation/usb/gadget-testing.txt [new file with mode: 0644]
Documentation/usb/gadget_serial.txt
MAINTAINERS
drivers/staging/emxx_udc/emxx_udc.c
drivers/staging/emxx_udc/emxx_udc.h
drivers/usb/Kconfig
drivers/usb/Makefile
drivers/usb/chipidea/udc.c
drivers/usb/dwc2/Kconfig
drivers/usb/dwc2/core.c
drivers/usb/dwc2/core.h
drivers/usb/dwc2/gadget.c
drivers/usb/dwc2/hcd.c
drivers/usb/dwc2/hw.h
drivers/usb/dwc2/platform.c
drivers/usb/dwc3/Kconfig
drivers/usb/dwc3/Makefile
drivers/usb/dwc3/core.c
drivers/usb/dwc3/core.h
drivers/usb/dwc3/dwc3-pci.c
drivers/usb/dwc3/ep0.c
drivers/usb/dwc3/gadget.c
drivers/usb/dwc3/trace.h
drivers/usb/gadget/Kconfig
drivers/usb/gadget/composite.c
drivers/usb/gadget/function/Makefile
drivers/usb/gadget/function/f_fs.c
drivers/usb/gadget/function/f_hid.c
drivers/usb/gadget/function/f_sourcesink.c
drivers/usb/gadget/function/f_uac1.c
drivers/usb/gadget/function/f_uvc.c
drivers/usb/gadget/function/u_ether.c
drivers/usb/gadget/function/u_fs.h
drivers/usb/gadget/function/u_uac1.c
drivers/usb/gadget/function/u_uac1.h
drivers/usb/gadget/function/u_uvc.h
drivers/usb/gadget/function/uvc_configfs.c [new file with mode: 0644]
drivers/usb/gadget/function/uvc_configfs.h [new file with mode: 0644]
drivers/usb/gadget/udc/at91_udc.c
drivers/usb/gadget/udc/at91_udc.h
drivers/usb/gadget/udc/atmel_usba_udc.c
drivers/usb/gadget/udc/atmel_usba_udc.h
drivers/usb/gadget/udc/bdc/bdc_ep.c
drivers/usb/gadget/udc/bdc/bdc_udc.c
drivers/usb/gadget/udc/dummy_hcd.c
drivers/usb/gadget/udc/fsl_qe_udc.c
drivers/usb/gadget/udc/fsl_udc_core.c
drivers/usb/gadget/udc/lpc32xx_udc.c
drivers/usb/gadget/udc/mv_udc_core.c
drivers/usb/gadget/udc/net2272.c
drivers/usb/gadget/udc/net2272.h
drivers/usb/gadget/udc/net2280.c
drivers/usb/gadget/udc/net2280.h
drivers/usb/gadget/udc/omap_udc.c
drivers/usb/gadget/udc/pch_udc.c
drivers/usb/gadget/udc/pxa25x_udc.c
drivers/usb/gadget/udc/pxa27x_udc.c
drivers/usb/gadget/udc/r8a66597-udc.c
drivers/usb/gadget/udc/s3c2410_udc.c
drivers/usb/gadget/udc/udc-core.c
drivers/usb/host/Kconfig
drivers/usb/host/Makefile
drivers/usb/host/isp1760-hcd.h [deleted file]
drivers/usb/host/isp1760-if.c [deleted file]
drivers/usb/isp1760/Kconfig [new file with mode: 0644]
drivers/usb/isp1760/Makefile [new file with mode: 0644]
drivers/usb/isp1760/isp1760-core.c [new file with mode: 0644]
drivers/usb/isp1760/isp1760-core.h [new file with mode: 0644]
drivers/usb/isp1760/isp1760-hcd.c [moved from drivers/usb/host/isp1760-hcd.c with 91% similarity]
drivers/usb/isp1760/isp1760-hcd.h [new file with mode: 0644]
drivers/usb/isp1760/isp1760-if.c [new file with mode: 0644]
drivers/usb/isp1760/isp1760-regs.h [new file with mode: 0644]
drivers/usb/isp1760/isp1760-udc.c [new file with mode: 0644]
drivers/usb/isp1760/isp1760-udc.h [new file with mode: 0644]
drivers/usb/musb/Kconfig
drivers/usb/musb/blackfin.c
drivers/usb/musb/musb_core.c
drivers/usb/musb/musb_cppi41.c
drivers/usb/musb/musb_debugfs.c
drivers/usb/musb/musb_gadget.c
drivers/usb/musb/musb_gadget_ep0.c
drivers/usb/musb/musb_virthub.c
drivers/usb/phy/phy-fsl-usb.c
drivers/usb/phy/phy-generic.c
drivers/usb/phy/phy-generic.h
drivers/usb/phy/phy-mxs-usb.c
drivers/usb/renesas_usbhs/common.c
drivers/usb/renesas_usbhs/common.h
drivers/usb/renesas_usbhs/fifo.c
drivers/usb/renesas_usbhs/mod_gadget.c
include/linux/usb/gadget.h
include/linux/usb/phy.h
include/linux/usb/usb_phy_generic.h
include/uapi/linux/usb/functionfs.h
tools/usb/ffs-aio-example/multibuff/host_app/test.c
tools/usb/ffs-aio-example/simple/device_app/aio_simple.c
tools/usb/ffs-aio-example/simple/host_app/test.c

diff --git a/Documentation/ABI/testing/configfs-usb-gadget-uvc b/Documentation/ABI/testing/configfs-usb-gadget-uvc
new file mode 100644 (file)
index 0000000..2f4a005
--- /dev/null
@@ -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
index bc2222c..38fee0f 100644 (file)
@@ -51,7 +51,10 @@ usb1: gadget@fffa4000 {
 Atmel High-Speed USB device controller
 
 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
  - interrupts: Should contain usba interrupt
  - ep childnode: To specify the number of endpoints and their properties.
index 482f815..fd132cb 100644 (file)
@@ -20,6 +20,10 @@ Optional properties:
 Refer to phy/phy-bindings.txt for generic phy consumer properties
 - dr_mode: shall be one of "host", "peripheral" and "otg"
   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:
 
index b08c903..61b045b 100644 (file)
@@ -14,6 +14,8 @@ Optional properties:
                         function should be enabled
   - phys: phandle + phy specifier pair
   - 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:
        usbhs: usb@e6590000 {
index 1bd37fa..5be01c8 100644 (file)
@@ -13,10 +13,15 @@ Optional properties:
 - clock-frequency: the clock frequency (in Hz) that the PHY clock must
   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.
 
+- 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:
 
        hsusb1_phy {
@@ -26,8 +31,11 @@ Example:
                clock-names = "main_clk";
                vcc-supply = <&hsusb1_vcc_regulator>;
                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
 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.
+GPIO 13 detects VBus insertion, and accordingly notifies the vbus-regulator.
diff --git a/Documentation/usb/gadget-testing.txt b/Documentation/usb/gadget-testing.txt
new file mode 100644 (file)
index 0000000..076ac7b
--- /dev/null
@@ -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
index 61e67f6..6b4a88a 100644 (file)
@@ -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=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
 
index aaa039d..f25de35 100644 (file)
@@ -3033,7 +3033,7 @@ S:        Maintained
 F:     drivers/platform/x86/dell-wmi.c
 
 DESIGNWARE USB2 DRD IP DRIVER
-M:     Paul Zimmerman <paulz@synopsys.com>
+M:     John Youn <johnyoun@synopsys.com>
 L:     linux-usb@vger.kernel.org
 T:     git git://git.kernel.org/pub/scm/linux/kernel/git/balbi/usb.git
 S:     Maintained
index eb178fc..bd70ea0 100644 (file)
@@ -1608,7 +1608,7 @@ static int std_req_get_status(struct nbu2ss_udc *udc)
        switch (recipient) {
        case USB_RECIP_DEVICE:
                if (udc->ctrl.wIndex == 0x0000) {
-                       if (udc->self_powered)
+                       if (udc->gadget.is_selfpowered)
                                status_data |= (1 << USB_DEVICE_SELF_POWERED);
 
                        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,
                                        int is_selfpowered)
 {
-       struct nbu2ss_udc       *udc;
+       struct nbu2ss_udc       *udc;
        unsigned long           flags;
 
 /*     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);
 
        spin_lock_irqsave(&udc->lock, flags);
-       udc->self_powered = (is_selfpowered != 0);
+       pgadget->is_selfpowered = (is_selfpowered != 0);
        spin_unlock_irqrestore(&udc->lock, flags);
 
        return 0;
@@ -3308,7 +3308,7 @@ static int __init nbu2ss_drv_contest_init(
        spin_lock_init(&udc->lock);
        udc->dev = &pdev->dev;
 
-       udc->self_powered = 1;
+       udc->gadget.is_selfpowered = 1;
        udc->devstate = USB_STATE_NOTATTACHED;
        udc->pdev = pdev;
        udc->mA = 0;
index ee1b80d..202e2dc 100644 (file)
@@ -624,7 +624,6 @@ struct nbu2ss_udc {
        unsigned                linux_suspended:1;
        unsigned                linux_resume:1;
        unsigned                usb_suspended:1;
-       unsigned                self_powered:1;
        unsigned                remote_wakeup:1;
        unsigned                udc_enabled:1;
 
index ae481c3..8ed451d 100644 (file)
@@ -104,6 +104,8 @@ source "drivers/usb/dwc2/Kconfig"
 
 source "drivers/usb/chipidea/Kconfig"
 
+source "drivers/usb/isp1760/Kconfig"
+
 comment "USB port drivers"
 
 if USB
index d7be717..2f1e2aa 100644 (file)
@@ -8,6 +8,7 @@ obj-$(CONFIG_USB)               += core/
 
 obj-$(CONFIG_USB_DWC3)         += dwc3/
 obj-$(CONFIG_USB_DWC2)         += dwc2/
+obj-$(CONFIG_USB_ISP1760)      += isp1760/
 
 obj-$(CONFIG_USB_MON)          += mon/
 
@@ -23,7 +24,6 @@ obj-$(CONFIG_USB_ISP1362_HCD) += host/
 obj-$(CONFIG_USB_U132_HCD)     += host/
 obj-$(CONFIG_USB_R8A66597_HCD) += host/
 obj-$(CONFIG_USB_HWA_HCD)      += host/
-obj-$(CONFIG_USB_ISP1760_HCD)  += host/
 obj-$(CONFIG_USB_IMX21_HCD)    += host/
 obj-$(CONFIG_USB_FSL_MPH_DR_OF)        += host/
 obj-$(CONFIG_USB_FUSBH200_HCD) += host/
index 4fe18ce..ff45104 100644 (file)
@@ -819,8 +819,8 @@ __acquires(hwep->lock)
        }
 
        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) \
                   == USB_RECIP_ENDPOINT) {
                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;
 }
 
+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
  * 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 = {
        .vbus_session   = ci_udc_vbus_session,
        .wakeup         = ci_udc_wakeup,
+       .set_selfpowered        = ci_udc_selfpowered,
        .pullup         = ci_udc_pullup,
        .vbus_draw      = ci_udc_vbus_draw,
        .udc_start      = ci_udc_start,
index b323c4c..76b9ba4 100644 (file)
@@ -23,7 +23,7 @@ choice
 
 config USB_DWC2_HOST
        bool "Host only mode"
-       depends on USB
+       depends on USB=y || (USB_DWC2=m && USB)
        help
          The Designware USB2.0 high-speed host controller
          integrated into many SoCs. Select this option if you want the
@@ -42,7 +42,7 @@ config USB_DWC2_PERIPHERAL
 
 config USB_DWC2_DUAL_ROLE
        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
          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
index 7605850..d5197d4 100644 (file)
@@ -462,7 +462,7 @@ int dwc2_core_init(struct dwc2_hsotg *hsotg, bool select_phy, int irq)
        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
         */
        if (dwc2_is_host_mode(hsotg)) {
index 7a70a13..f74304b 100644 (file)
@@ -108,7 +108,7 @@ struct s3c_hsotg_req;
  * @halted: Set if the endpoint has been halted.
  * @periodic: Set if this is a periodic ep, such as Interrupt
  * @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.
  * @fifo_size: The size of the FIFO (for periodic IN endpoints)
  * @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            periodic:1;
        unsigned int            isochronous:1;
-       unsigned int            sent_zlp:1;
+       unsigned int            send_zlp:1;
 
        char                    name[10];
 };
@@ -158,14 +158,12 @@ struct s3c_hsotg_ep {
  * struct s3c_hsotg_req - data transfer request
  * @req: The USB gadget request
  * @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 usb_request      req;
        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)
@@ -193,6 +191,22 @@ enum dwc2_lx_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
  *
@@ -381,7 +395,7 @@ struct dwc2_core_params {
  * @power_optimized     Are power optimizations enabled?
  * @num_dev_ep          Number of device endpoints available
  * @num_dev_perio_in_ep Number of device periodic IN endpoints
- *                      avaialable
+ *                      available
  * @dev_token_q_depth   Device Mode IN Token Sequence Learning Queue
  *                      Depth
  *                       0 to 30
@@ -434,6 +448,9 @@ struct dwc2_hw_params {
        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
  * and periodic schedules
@@ -552,14 +569,20 @@ struct dwc2_hw_params {
  * @num_of_eps:         Number of available EPs (excluding EP0)
  * @debug_root:         Root directrory for debugfs.
  * @debug_file:         Main status file for debugfs.
+ * @debug_testmode:     Testmode status file for debugfs.
  * @debug_fifo:         FIFO status file for debugfs.
  * @ep0_reply:          Request used for ep0 reply.
  * @ep0_buff:           Buffer for EP0 reply data, if needed.
  * @ctrl_buff:          Buffer for EP0 control requests.
  * @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
  * @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 device *dev;
@@ -591,6 +614,7 @@ struct dwc2_hsotg {
 
        struct dentry *debug_root;
        struct dentry *debug_file;
+       struct dentry *debug_testmode;
        struct dentry *debug_fifo;
 
        /* DWC OTG HW Release versions */
@@ -684,15 +708,21 @@ struct dwc2_hsotg {
 
        struct usb_request *ep0_reply;
        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;
        unsigned int enabled:1;
        unsigned int connected:1;
-       unsigned int setup:1;
        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 */
 };
 
@@ -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_resume(struct dwc2_hsotg *dwc2);
 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_disconnect(struct dwc2_hsotg *dwc2);
 #else
@@ -981,7 +1012,8 @@ static inline int s3c_hsotg_resume(struct dwc2_hsotg *dwc2)
 { return 0; }
 static inline int dwc2_gadget_init(struct dwc2_hsotg *hsotg, int irq)
 { 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_disconnect(struct dwc2_hsotg *dwc2) {}
 #endif
index 7924200..6a30887 100644 (file)
@@ -35,6 +35,7 @@
 #include <linux/usb/gadget.h>
 #include <linux/usb/phy.h>
 #include <linux/platform_data/s3c-hsotg.h>
+#include <linux/uaccess.h>
 
 #include "core.h"
 #include "hw.h"
@@ -65,7 +66,16 @@ static inline void __bic32(void __iomem *ptr, u32 val)
        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);
 
 /**
@@ -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
  * 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)
 {
-       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 addr;
-       unsigned int size;
        int timeout;
        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
@@ -183,35 +196,21 @@ static void s3c_hsotg_init_fifo(struct dwc2_hsotg *hsotg)
         */
 
        /* 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
         * 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 |= 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");
-               addr += size;
+               addr += hsotg->g_tx_fifo_sz[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,
                                "%s: timeout flushing fifos (GRSTCTL=%08x)\n",
                                __func__, val);
+                       break;
                }
 
                udelay(1);
@@ -566,11 +566,6 @@ static void s3c_hsotg_start_req(struct dwc2_hsotg *hsotg,
        length = ureq->length - ureq->actual;
        dev_dbg(hsotg->dev, "ureq->length:%d ureq->actual:%d\n",
                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);
        if (length > maxreq) {
@@ -604,14 +599,15 @@ static void s3c_hsotg_start_req(struct dwc2_hsotg *hsotg,
        else
                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);
@@ -644,15 +640,12 @@ static void s3c_hsotg_start_req(struct dwc2_hsotg *hsotg,
        ctrl |= DXEPCTL_EPENA;  /* ensure ep enabled */
        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 */
-       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 */
 
-
        dev_dbg(hsotg->dev, "%s: DxEPCTL=0x%08x\n", __func__, ctrl);
        writel(ctrl, hsotg->regs + epctrl_reg);
 
@@ -686,7 +679,7 @@ static void s3c_hsotg_start_req(struct dwc2_hsotg *hsotg,
 
        /* check ep is enabled */
        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",
                         index, readl(hsotg->regs + epctrl_reg));
 
@@ -733,6 +726,59 @@ dma_error:
        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,
                              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 dwc2_hsotg *hs = hs_ep->parent;
        bool first;
+       int ret;
 
        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,
@@ -750,9 +797,13 @@ static int s3c_hsotg_ep_queue(struct usb_ep *ep, struct usb_request *req,
        req->actual = 0;
        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 (using_dma(hs)) {
-               int ret = s3c_hsotg_map_dma(hs, hs_ep, req);
+               ret = s3c_hsotg_map_dma(hs, hs_ep, req);
                if (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,
                                           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 idx = windex & 0x7F;
 
@@ -829,6 +880,8 @@ static struct s3c_hsotg_ep *ep_from_windex(struct dwc2_hsotg *hsotg,
        if (idx > hsotg->num_of_eps)
                return NULL;
 
+       ep = index_to_ep(hsotg, idx, dir);
+
        if (idx && ep->dir_in != dir)
                return NULL;
 
@@ -836,6 +889,32 @@ static struct s3c_hsotg_ep *ep_from_windex(struct dwc2_hsotg *hsotg,
 }
 
 /**
+ * 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
  * @hsotg: The device state
  * @ep: Endpoint 0
@@ -864,13 +943,15 @@ static int s3c_hsotg_send_reply(struct dwc2_hsotg *hsotg,
 
        req->buf = hsotg->ep0_buff;
        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;
 
        if (length)
                memcpy(req->buf, buff, length);
-       else
-               ep->sent_zlp = 1;
 
        ret = s3c_hsotg_ep_queue(&ep->ep, req, GFP_ATOMIC);
        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,
                                        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;
        __le16 reply;
        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
  * @ctrl: USB control request
  */
 static int s3c_hsotg_process_req_feature(struct dwc2_hsotg *hsotg,
                                         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;
        bool restart;
        bool set = (ctrl->bRequest == USB_REQ_SET_FEATURE);
        struct s3c_hsotg_ep *ep;
        int ret;
        bool halted;
+       u32 recip;
+       u32 wValue;
+       u32 wIndex;
 
        dev_dbg(hsotg->dev, "%s: %s_FEATURE\n",
                __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) {
                        dev_dbg(hsotg->dev, "%s: no endpoint for 0x%04x\n",
-                               __func__, le16_to_cpu(ctrl->wIndex));
+                               __func__, wIndex);
                        return -ENOENT;
                }
 
-               switch (le16_to_cpu(ctrl->wValue)) {
+               switch (wValue) {
                case USB_ENDPOINT_HALT:
                        halted = ep->halted;
 
@@ -1006,16 +1116,22 @@ static int s3c_hsotg_process_req_feature(struct dwc2_hsotg *hsotg,
                                        hs_req = ep->req;
                                        ep->req = NULL;
                                        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 */
-                               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:
                        return -ENOENT;
                }
-       } else
-               return -ENOENT;  /* currently only deal with endpoint */
-
+               break;
+       default:
+               return -ENOENT;
+       }
        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)
 {
-       struct s3c_hsotg_ep *ep0 = &hsotg->eps[0];
+       struct s3c_hsotg_ep *ep0 = hsotg->eps_out[0];
        u32 reg;
        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,
                                      struct usb_ctrlrequest *ctrl)
 {
-       struct s3c_hsotg_ep *ep0 = &hsotg->eps[0];
+       struct s3c_hsotg_ep *ep0 = hsotg->eps_out[0];
        int ret = 0;
        u32 dcfg;
 
-       ep0->sent_zlp = 0;
-
        dev_dbg(hsotg->dev, "ctrl Req=%02x, Type=%02x, V=%04x, L=%04x\n",
                 ctrl->bRequest, ctrl->bRequestType,
                 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;
+               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) {
                switch (ctrl->bRequest) {
                case USB_REQ_SET_ADDRESS:
+                       hsotg->connected = 1;
                        dcfg = readl(hsotg->regs + DCFG);
                        dcfg &= ~DCFG_DEVADDR_MASK;
                        dcfg |= (le16_to_cpu(ctrl->wValue) <<
@@ -1201,9 +1313,11 @@ static void s3c_hsotg_enqueue_setup(struct dwc2_hsotg *hsotg)
                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) {
                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
  * @hsotg: The device state.
@@ -1249,6 +1389,8 @@ static void s3c_hsotg_complete_request(struct dwc2_hsotg *hsotg,
        if (hs_req->req.status == -EINPROGRESS)
                hs_req->req.status = result;
 
+       s3c_hsotg_handle_unaligned_buf_complete(hsotg, hs_ep, hs_req);
+
        hs_ep->req = NULL;
        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)
 {
-       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;
        void __iomem *fifo = hsotg->regs + EPFIFO(ep_idx);
        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));
                int ptr;
 
-               dev_warn(hsotg->dev,
+               dev_dbg(hsotg->dev,
                         "%s: FIFO %d bytes on ep%d but no req (DXEPCTl=0x%08x)\n",
                         __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
- * @req: The request currently on this endpoint
+ * @dir_in: If IN zlp
  *
  * Generate a zero-length IN packet request for terminating a SETUP
  * 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
  * 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
  * @hsotg: The device instance
  * @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
  * transfer for an OUT endpoint has been completed, either by a short
  * 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));
-       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 usb_request *req = &hs_req->req;
        unsigned size_left = DXEPTSIZ_XFERSIZE_GET(epsize);
@@ -1413,6 +1530,13 @@ static void s3c_hsotg_handle_outdone(struct dwc2_hsotg *hsotg,
                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)) {
                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) {
                s3c_hsotg_start_req(hsotg, hs_ep, hs_req, true);
                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) {
@@ -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);
@@ -1511,8 +1626,7 @@ static void s3c_hsotg_handle_rx(struct dwc2_hsotg *hsotg)
        size = grxstsr & GRXSTS_BYTECNT_MASK;
        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);
 
        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));
 
                if (!using_dma(hsotg))
-                       s3c_hsotg_handle_outdone(hsotg, epnum, false);
+                       s3c_hsotg_handle_outdone(hsotg, epnum);
                break;
 
        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",
                        s3c_hsotg_read_frameno(hsotg),
                        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;
 
        case GRXSTS_PKTSTS_OUTRX:
@@ -1547,6 +1666,8 @@ static void s3c_hsotg_handle_rx(struct dwc2_hsotg *hsotg)
                        s3c_hsotg_read_frameno(hsotg),
                        readl(hsotg->regs + DOEPCTL(0)));
 
+               WARN_ON(hsotg->ep0_state != DWC2_EP0_SETUP);
+
                s3c_hsotg_rx_data(hsotg, epnum, size);
                break;
 
@@ -1591,14 +1712,18 @@ static u32 s3c_hsotg_ep0_mps(unsigned int mps)
  * the hardware control registers to reflect this.
  */
 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;
        u32 mpsval;
        u32 mcval;
        u32 reg;
 
+       hs_ep = index_to_ep(hsotg, ep, dir_in);
+       if (!hs_ep)
+               return;
+
        if (ep == 0) {
                /* EP0 is a special case */
                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;
        }
 
-       /*
-        * 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 &= ~DXEPCTL_MPS_MASK;
                reg |= mpsval;
@@ -1727,9 +1847,21 @@ static void s3c_hsotg_complete_in(struct dwc2_hsotg *hsotg,
        }
 
        /* 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);
+               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;
        }
 
@@ -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",
                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;
        }
 
-       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,
                            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 epctl_reg = dir_in ? DIEPCTL(idx) : DOEPCTL(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 */
        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",
                __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 (hs_ep->isochronous && hs_ep->interval == 1) {
                        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.
                         */
 
-                       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)
                                WARN_ON_ONCE(1);
                        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) {
                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 */
@@ -1988,30 +2132,23 @@ static void s3c_hsotg_irq_enumdone(struct dwc2_hsotg *hsotg)
  * @hsotg: The device state.
  * @ep: The endpoint the requests may be on.
  * @result: The result code to use.
- * @force: Force removal of any current requests
  *
  * Go through the requests on the given endpoint and mark them
  * completed with the given result code.
  */
 static void kill_all_requests(struct dwc2_hsotg *hsotg,
                              struct s3c_hsotg_ep *ep,
-                             int result, bool force)
+                             int result)
 {
        struct s3c_hsotg_req *req, *treq;
        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,
                                           result);
-       }
+
        if (!hsotg->dedicated_fifos)
                return;
        size = (readl(hsotg->regs + DTXFSTS(ep->index)) & 0xffff) * 4;
@@ -2035,8 +2172,16 @@ void s3c_hsotg_disconnect(struct dwc2_hsotg *hsotg)
                return;
 
        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);
 }
@@ -2053,9 +2198,11 @@ static void s3c_hsotg_irq_fifoempty(struct dwc2_hsotg *hsotg, bool periodic)
        int epno, ret;
 
        /* look through for any more data to transmit */
-
        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)
                        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.
  */
-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
@@ -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 */
+       val = (hsotg->phyif == GUSBCFG_PHYIF8) ? 9 : 5;
        writel(hsotg->phyif | GUSBCFG_TOUTCAL(7) |
-              (0x5 << 10), hsotg->regs + GUSBCFG);
+              (val << GUSBCFG_USBTRDTIM_SHIFT), hsotg->regs + GUSBCFG);
 
        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 */
        writel(0xffffffff, hsotg->regs + GOTGINT);
@@ -2163,7 +2316,7 @@ void s3c_hsotg_core_init_disconnected(struct dwc2_hsotg *hsotg)
 
        if (using_dma(hsotg))
                writel(GAHBCFG_GLBL_INTR_EN | GAHBCFG_DMA_EN |
-                      GAHBCFG_HBSTLEN_INCR4,
+                      (GAHBCFG_HBSTLEN_INCR4 << GAHBCFG_HBSTLEN_SHIFT),
                       hsotg->regs + GAHBCFG);
        else
                writel(((hsotg->dedicated_fifos) ? (GAHBCFG_NP_TXF_EMP_LVL |
@@ -2177,8 +2330,8 @@ void s3c_hsotg_core_init_disconnected(struct dwc2_hsotg *hsotg)
         * 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_TIMEOUTMSK | DIEPMSK_AHBERRMSK |
                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, 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));
 
@@ -2230,13 +2385,13 @@ void s3c_hsotg_core_init_disconnected(struct dwc2_hsotg *hsotg)
        writel(DXEPTSIZ_MC(1) | DXEPTSIZ_PKTCNT(1) |
               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_USBACTEP,
               hsotg->regs + DOEPCTL0);
 
        /* 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);
 
        s3c_hsotg_enqueue_setup(hsotg);
@@ -2246,8 +2401,10 @@ void s3c_hsotg_core_init_disconnected(struct dwc2_hsotg *hsotg)
                readl(hsotg->regs + DOEPCTL0));
 
        /* 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 */
        mdelay(3);
@@ -2293,7 +2450,6 @@ irq_retry:
                writel(GINTSTS_ENUMDONE, hsotg->regs + GINTSTS);
 
                s3c_hsotg_irq_enumdone(hsotg);
-               hsotg->connected = 1;
        }
 
        if (gintsts & (GINTSTS_OEPINT | GINTSTS_IEPINT)) {
@@ -2308,12 +2464,14 @@ irq_retry:
 
                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)
                                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)
                                s3c_hsotg_epint(hsotg, ep, 1);
                }
@@ -2329,15 +2487,17 @@ irq_retry:
 
                writel(GINTSTS_USBRST, hsotg->regs + GINTSTS);
 
+               /* Report disconnection if it is not already done. */
+               s3c_hsotg_disconnect(hsotg);
+
                if (usb_status & GOTGCTL_BSESVLD) {
                        if (time_after(jiffies, hsotg->last_rst +
                                       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 dwc2_hsotg *hsotg = hs_ep->parent;
        unsigned long flags;
-       int index = hs_ep->index;
+       unsigned int index = hs_ep->index;
        u32 epctrl_reg;
        u32 epctrl;
        u32 mps;
-       int dir_in;
-       int i, val, size;
+       unsigned int dir_in;
+       unsigned int i, val, size;
        int ret = 0;
 
        dev_dbg(hsotg->dev,
@@ -2482,7 +2642,7 @@ static int s3c_hsotg_ep_enable(struct usb_ep *ep,
        epctrl |= DXEPCTL_SNAK;
 
        /* 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 */
        hs_ep->isochronous = 0;
@@ -2518,30 +2678,48 @@ static int s3c_hsotg_ep_enable(struct usb_ep *ep,
                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
         * 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;
-               for (i = 1; i <= 8; ++i) {
+               for (i = 1; i < hsotg->num_of_eps; ++i) {
                        if (hsotg->fifo_map & (1<<i))
                                continue;
                        val = readl(hsotg->regs + DPTXFSIZN(i));
                        val = (val >> FIFOSIZE_DEPTH_SHIFT)*4;
                        if (val < size)
                                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;
                        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 */
@@ -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);
 
-       if (ep == &hsotg->eps[0].ep) {
+       if (ep == &hsotg->eps_out[0]->ep) {
                dev_err(hsotg->dev, "%s: called for ep0\n", __func__);
                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);
 
        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);
        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 */
        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);
        return 0;
 }
@@ -2682,40 +2861,39 @@ static int s3c_hsotg_ep_sethalt(struct usb_ep *ep, int value)
                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 {
-               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;
 
        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)
 {
+       u32 trdtim;
        /* unmask subset of endpoint interrupts */
 
        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 */
        __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 */
 
        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);
 
        /* 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);
+       if (!IS_ERR_OR_NULL(hsotg->uphy))
+               otg_set_peripheral(hsotg->uphy->otg, &hsotg->gadget);
 
        spin_lock_irqsave(&hsotg->lock, flags);
        s3c_hsotg_init(hsotg);
-       s3c_hsotg_core_init_disconnected(hsotg);
+       s3c_hsotg_core_init_disconnected(hsotg, false);
        hsotg->enabled = 0;
        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);
 
        /* 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);
 
@@ -2938,6 +3119,8 @@ static int s3c_hsotg_udc_stop(struct usb_gadget *gadget)
 
        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);
 
        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) {
                clk_enable(hsotg->clk);
                hsotg->enabled = 1;
+               s3c_hsotg_core_init_disconnected(hsotg, false);
                s3c_hsotg_core_connect(hsotg);
        } else {
                s3c_hsotg_core_disconnect(hsotg);
+               s3c_hsotg_disconnect(hsotg);
                hsotg->enabled = 0;
                clk_disable(hsotg->clk);
        }
@@ -2993,11 +3178,52 @@ static int s3c_hsotg_pullup(struct usb_gadget *gadget, int is_on)
        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 = {
        .get_frame      = s3c_hsotg_gadget_getframe,
        .udc_start              = s3c_hsotg_udc_start,
        .udc_stop               = s3c_hsotg_udc_stop,
        .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,
                                       struct s3c_hsotg_ep *hs_ep,
-                                      int epnum)
+                                      int epnum,
+                                      bool dir_in)
 {
        char *dir;
 
        if (epnum == 0)
                dir = "";
-       else if ((epnum % 2) == 0) {
-               dir = "out";
-       } else {
+       else if (dir_in)
                dir = "in";
-               hs_ep->dir_in = 1;
-       }
+       else
+               dir = "out";
 
+       hs_ep->dir_in = dir_in;
        hs_ep->index = epnum;
 
        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)) {
                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
  */
-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 */
 
-       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",
                 hsotg->num_of_eps,
                 hsotg->dedicated_fifos ? "dedicated" : "shared",
                 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 + 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",
                 readl(regs + GRXFSIZ), readl(regs + GNPTXFSIZ));
 
        /* show periodic fifo settings */
 
-       for (idx = 1; idx <= 15; idx++) {
+       for (idx = 1; idx < hsotg->num_of_eps; idx++) {
                val = readl(regs + DPTXFSIZN(idx));
                dev_info(dev, "DPTx[%d] FSize=%d, StAddr=0x%08x\n", idx,
                         val >> FIFOSIZE_DEPTH_SHIFT,
                         val & FIFOSIZE_STARTADDR_MASK);
        }
 
-       for (idx = 0; idx < 15; idx++) {
+       for (idx = 0; idx < hsotg->num_of_eps; idx++) {
                dev_info(dev,
                         "ep%d-in: EPCTL=0x%08x, SIZ=0x%08x, DMA=0x%08x\n", idx,
                         readl(regs + DIEPCTL(idx)),
@@ -3132,6 +3392,103 @@ static void s3c_hsotg_dump(struct dwc2_hsotg *hsotg)
 }
 
 /**
+ * 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.
  * @seq: The seq file to write to.
  * @v: Unused parameter.
@@ -3168,7 +3525,7 @@ static int state_show(struct seq_file *seq, void *v)
 
        seq_puts(seq, "\nEndpoint status:\n");
 
-       for (idx = 0; idx < 15; idx++) {
+       for (idx = 0; idx < hsotg->num_of_eps; idx++) {
                u32 in, out;
 
                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");
 
-       for (idx = 1; idx <= 15; idx++) {
+       for (idx = 1; idx < hsotg->num_of_eps; idx++) {
                val = readl(regs + DPTXFSIZN(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 */
 
-       hsotg->debug_file = debugfs_create_file("state", 0444, root,
+       hsotg->debug_file = debugfs_create_file("state", S_IRUGO, root,
                                                hsotg, &state_fops);
 
        if (IS_ERR(hsotg->debug_file))
                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);
 
        if (IS_ERR(hsotg->debug_fifo))
                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++) {
-               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;
 
        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_testmode);
        debugfs_remove(hsotg->debug_fifo);
        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: 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 s3c_hsotg_plat *plat = dev->platform_data;
-       struct phy *phy;
-       struct usb_phy *uphy;
-       struct s3c_hsotg_ep *eps;
        int epnum;
        int ret;
        int i;
+       u32 p_tx_fifo[] = DWC2_G_P_LEGACY_TX_FIFO_SIZE;
 
        /* Set default UTMI width */
        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
                 * 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;
        }
 
@@ -3488,16 +3923,53 @@ int dwc2_gadget_init(struct dwc2_hsotg *hsotg, int irq)
 
        if (ret) {
                dev_err(dev, "failed to enable supplies: %d\n", ret);
-               goto err_supplies;
+               goto err_clk;
        }
 
        /* usb phy enable */
        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_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);
 
+       /* 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,
                                dev_name(hsotg->dev), hsotg);
        if (ret < 0) {
@@ -3506,7 +3978,7 @@ int dwc2_gadget_init(struct dwc2_hsotg *hsotg, int irq)
                regulator_bulk_disable(ARRAY_SIZE(hsotg->supplies),
                                       hsotg->supplies);
                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 */
@@ -3517,33 +3989,30 @@ int dwc2_gadget_init(struct dwc2_hsotg *hsotg, int irq)
                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 */
 
        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 */
 
-       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);
        if (!hsotg->ctrl_req) {
                dev_err(dev, "failed to allocate ctrl req\n");
                ret = -ENOMEM;
-               goto err_ep_mem;
+               goto err_supplies;
        }
 
        /* 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 */
        s3c_hsotg_phy_disable(hsotg);
@@ -3552,12 +4021,12 @@ int dwc2_gadget_init(struct dwc2_hsotg *hsotg, int irq)
                                    hsotg->supplies);
        if (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);
        if (ret)
-               goto err_ep_mem;
+               goto err_supplies;
 
        s3c_hsotg_create_debug(hsotg);
 
@@ -3565,8 +4034,6 @@ int dwc2_gadget_init(struct dwc2_hsotg *hsotg, int irq)
 
        return 0;
 
-err_ep_mem:
-       kfree(eps);
 err_supplies:
        s3c_hsotg_phy_disable(hsotg);
 err_clk:
@@ -3612,8 +4079,12 @@ int s3c_hsotg_suspend(struct dwc2_hsotg *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),
                                             hsotg->supplies);
@@ -3644,7 +4115,7 @@ int s3c_hsotg_resume(struct dwc2_hsotg *hsotg)
                s3c_hsotg_phy_enable(hsotg);
 
                spin_lock_irqsave(&hsotg->lock, flags);
-               s3c_hsotg_core_init_disconnected(hsotg);
+               s3c_hsotg_core_init_disconnected(hsotg, false);
                if (hsotg->enabled)
                        s3c_hsotg_core_connect(hsotg);
                spin_unlock_irqrestore(&hsotg->lock, flags);
index c5fd43d..c78c874 100644 (file)
@@ -316,10 +316,12 @@ void dwc2_hcd_disconnect(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;
-       else
+               usb_hcd_resume_root_hub(hsotg->priv);
+       } else {
                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;
                dwc2_core_init(hsotg, false, -1);
                dwc2_enable_global_interrupts(hsotg);
-               s3c_hsotg_core_init_disconnected(hsotg);
+               s3c_hsotg_core_init_disconnected(hsotg, false);
                s3c_hsotg_core_connect(hsotg);
        } else {
                /* 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 */
 static int dwc2_hcd_hub_control(struct dwc2_hsotg *hsotg, u16 typereq,
                                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:
                        dev_dbg(hsotg->dev,
                                "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;
 
                case USB_PORT_FEAT_POWER:
@@ -2317,55 +2305,6 @@ static void _dwc2_hcd_stop(struct usb_hcd *hcd)
        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 */
 static int _dwc2_hcd_get_frame_number(struct usb_hcd *hcd)
 {
@@ -2736,9 +2675,6 @@ static struct hc_driver dwc2_hc_driver = {
        .hub_status_data = _dwc2_hcd_hub_status_data,
        .hub_control = _dwc2_hcd_hub_control,
        .clear_tt_buffer_complete = _dwc2_hcd_clear_tt_buffer_complete,
-
-       .bus_suspend = _dwc2_hcd_suspend,
-       .bus_resume = _dwc2_hcd_resume,
 };
 
 /*
index 51248b9..d0a5ed8 100644 (file)
 #define GHWCFG4_NUM_IN_EPS_MASK                        (0xf << 26)
 #define GHWCFG4_NUM_IN_EPS_SHIFT               26
 #define GHWCFG4_DED_FIFO_EN                    (1 << 25)
+#define GHWCFG4_DED_FIFO_SHIFT         25
 #define GHWCFG4_SESSION_END_FILT_EN            (1 << 24)
 #define GHWCFG4_B_VALID_FILT_EN                        (1 << 23)
 #define GHWCFG4_A_VALID_FILT_EN                        (1 << 22)
 
 #define DIEPINT(_a)                    HSOTG_REG(0x908 + ((_a) * 0x20))
 #define DOEPINT(_a)                    HSOTG_REG(0xB08 + ((_a) * 0x20))
+#define DXEPINT_SETUP_RCVD             (1 << 15)
 #define DXEPINT_INEPNAKEFF             (1 << 6)
 #define DXEPINT_BACK2BACKSETUP         (1 << 6)
 #define DXEPINT_INTKNEPMIS             (1 << 5)
index 6a795aa..ae095f0 100644 (file)
@@ -155,6 +155,8 @@ static int dwc2_driver_probe(struct platform_device *dev)
        struct dwc2_core_params defparams;
        struct dwc2_hsotg *hsotg;
        struct resource *res;
+       struct phy *phy;
+       struct usb_phy *uphy;
        int retval;
        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);
 
+       /*
+        * 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);
        mutex_init(&hsotg->init_mutex);
        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);
        int ret = 0;
 
-       if (dwc2_is_device_mode(dwc2))
+       if (dwc2_is_device_mode(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;
 }
 
@@ -241,8 +268,13 @@ static int __maybe_unused dwc2_resume(struct device *dev)
        struct dwc2_hsotg *dwc2 = dev_get_drvdata(dev);
        int ret = 0;
 
-       if (dwc2_is_device_mode(dwc2))
+       if (dwc2_is_device_mode(dwc2)) {
                ret = s3c_hsotg_resume(dwc2);
+       } else {
+               phy_power_on(dwc2->phy);
+               phy_init(dwc2->phy);
+
+       }
        return ret;
 }
 
index 58b5b2c..edbf9c8 100644 (file)
@@ -104,12 +104,6 @@ config USB_DWC3_DEBUG
        help
          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
        bool "Enable USB3 LPM Capability"
        depends on USB_DWC3_HOST=y || USB_DWC3_DUAL_ROLE=y
index bb34fbc..46172f4 100644 (file)
@@ -2,7 +2,6 @@
 CFLAGS_trace.o                         := -I$(src)
 
 ccflags-$(CONFIG_USB_DWC3_DEBUG)       := -DDEBUG
-ccflags-$(CONFIG_USB_DWC3_VERBOSE)     += -DVERBOSE_DEBUG
 
 obj-$(CONFIG_USB_DWC3)                 += dwc3.o
 
index 25ddc39..9f0e209 100644 (file)
@@ -345,7 +345,7 @@ static void dwc3_core_num_eps(struct dwc3 *dwc)
        dwc->num_in_eps = DWC3_NUM_IN_EPS(parms);
        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);
 }
 
index 4bb9aa6..d201910 100644 (file)
@@ -431,7 +431,6 @@ struct dwc3_event_buffer {
  * @dwc: pointer to DWC controller
  * @saved_state: ep state saved during hibernation
  * @flags: endpoint flags (wedged, stalled, ...)
- * @current_trb: index of current used trb
  * @number: endpoint number (1 - 15)
  * @type: set to bmAttributes & USB_ENDPOINT_XFERTYPE_MASK
  * @resource_index: Resource transfer index
@@ -464,8 +463,6 @@ struct dwc3_ep {
        /* This last one is specific to EP0 */
 #define DWC3_EP0_DIR_IN                (1 << 31)
 
-       unsigned                current_trb;
-
        u8                      number;
        u8                      type;
        u8                      resource_index;
@@ -685,7 +682,6 @@ struct dwc3_scratchpad_array {
  * @is_utmi_l1_suspend: the core asserts output signal
  *     0       - utmi_sleep_n
  *     1       - utmi_l1_suspend_n
- * @is_selfpowered: true when we are selfpowered
  * @is_fpga: true when we are using the FPGA board
  * @needs_fifo_resize: not all users might want fifo resizing, flag it
  * @pullups_connected: true when Run/Stop bit is set
@@ -809,7 +805,6 @@ struct dwc3 {
        unsigned                has_hibernation:1;
        unsigned                has_lpm_erratum:1;
        unsigned                is_utmi_l1_suspend:1;
-       unsigned                is_selfpowered:1;
        unsigned                is_fpga:1;
        unsigned                needs_fifo_resize:1;
        unsigned                pullups_connected:1;
index b642a2f..8d95056 100644 (file)
@@ -22,9 +22,6 @@
 #include <linux/pci.h>
 #include <linux/platform_device.h>
 
-#include <linux/usb/otg.h>
-#include <linux/usb/usb_phy_generic.h>
-
 #include "platform_data.h"
 
 /* FIXME define these in <linux/pci_ids.h> */
 #define PCI_DEVICE_ID_INTEL_SPTLP      0x9d30
 #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;
-
-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,
@@ -103,18 +75,8 @@ static int dwc3_pci_probe(struct pci_dev *pci,
 {
        struct resource         res[2];
        struct platform_device  *dwc3;
-       struct dwc3_pci         *glue;
        int                     ret;
        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);
        if (ret) {
@@ -124,12 +86,6 @@ static int dwc3_pci_probe(struct pci_dev *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);
        if (!dwc3) {
                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].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));
        if (ret) {
                dev_err(dev, "couldn't add resources to dwc3 device\n");
                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)
-               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;
-       glue->dwc3 = dwc3;
 
        ret = platform_device_add(dwc3);
        if (ret) {
                dev_err(dev, "failed to register dwc3 device\n");
-               goto err3;
+               goto err;
        }
 
        return 0;
-
-err3:
+err:
        platform_device_put(dwc3);
        return ret;
 }
 
 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[] = {
@@ -228,45 +148,11 @@ static const struct pci_device_id 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 = {
        .name           = "dwc3-pci",
        .id_table       = dwc3_pci_id_table,
        .probe          = dwc3_pci_probe,
        .remove         = dwc3_pci_remove,
-       .driver         = {
-               .pm     = &dwc3_pci_dev_pm_ops,
-       },
 };
 
 MODULE_AUTHOR("Felipe Balbi <balbi@ti.com>");
index 1bc77a3..2ef3c8d 100644 (file)
@@ -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.
                 */
-               usb_status |= dwc->is_selfpowered << USB_DEVICE_SELF_POWERED;
+               usb_status |= dwc->gadget.is_selfpowered;
 
                if (dwc->speed == DWC3_DSTS_SUPERSPEED) {
                        reg = dwc3_readl(dwc->regs, DWC3_DCTL);
index 8f65ab3..a03a485 100644 (file)
@@ -139,7 +139,8 @@ int dwc3_gadget_set_link_state(struct dwc3 *dwc, enum dwc3_link_state state)
                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;
 }
@@ -219,7 +220,7 @@ int dwc3_gadget_resize_tx_fifos(struct dwc3 *dwc)
 
                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);
 
                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 {
                reg = dwc3_readl(dwc->regs, DWC3_DGCMD);
                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));
                        return 0;
                }
@@ -297,8 +299,11 @@ int dwc3_send_gadget_generic_command(struct dwc3 *dwc, unsigned cmd, u32 param)
                 * interrupt context.
                 */
                timeout--;
-               if (!timeout)
+               if (!timeout) {
+                       dwc3_trace(trace_dwc3_gadget,
+                                       "Command Timed Out");
                        return -ETIMEDOUT;
+               }
                udelay(1);
        } while (1);
 }
@@ -320,7 +325,8 @@ int dwc3_send_gadget_ep_cmd(struct dwc3 *dwc, unsigned ep,
        do {
                reg = dwc3_readl(dwc->regs, DWC3_DEPCMD(ep));
                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));
                        return 0;
                }
@@ -330,8 +336,11 @@ int dwc3_send_gadget_ep_cmd(struct dwc3 *dwc, unsigned ep,
                 * interrupt context.
                 */
                timeout--;
-               if (!timeout)
+               if (!timeout) {
+                       dwc3_trace(trace_dwc3_gadget,
+                                       "Command Timed Out");
                        return -ETIMEDOUT;
+               }
 
                udelay(1);
        } while (1);
@@ -352,9 +361,6 @@ static int dwc3_alloc_trb_pool(struct dwc3_ep *dep)
        if (dep->trb_pool)
                return 0;
 
-       if (dep->number == 0 || dep->number == 1)
-               return 0;
-
        dep->trb_pool = dma_alloc_coherent(dwc->dev,
                        sizeof(struct dwc3_trb) * DWC3_TRB_NUM,
                        &dep->trb_pool_dma, GFP_KERNEL);
@@ -492,7 +498,7 @@ static int __dwc3_gadget_ep_enable(struct dwc3_ep *dep,
        u32                     reg;
        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)) {
                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,
                unsigned length, unsigned last, unsigned chain, unsigned node)
 {
-       struct dwc3             *dwc = dep->dwc;
        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,
                        length, last ? " last" : "",
                        chain ? " chain" : "");
@@ -934,7 +939,7 @@ static int __dwc3_gadget_kick_transfer(struct dwc3_ep *dep, u16 cmd_param,
        u32                             cmd;
 
        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;
        }
        dep->flags &= ~DWC3_EP_PENDING_REQUEST;
@@ -1005,8 +1010,9 @@ static void __dwc3_gadget_start_isoc(struct dwc3 *dwc,
        u32 uf;
 
        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;
                return;
        }
@@ -1113,15 +1119,10 @@ static int __dwc3_gadget_ep_queue(struct dwc3_ep *dep, struct dwc3_request *req)
         * handled.
         */
        if (dep->stream_capable) {
-               int     ret;
-
                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",
                                        dep->name);
-               }
        }
 
        return 0;
@@ -1152,8 +1153,6 @@ static int dwc3_gadget_ep_queue(struct usb_ep *ep, struct usb_request *request,
                goto out;
        }
 
-       dev_vdbg(dwc->dev, "queing request %p to %s length %d\n",
-                       request, ep->name, request->length);
        trace_dwc3_ep_queue(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;
 
        spin_lock_irqsave(&dwc->lock, flags);
-       dwc->is_selfpowered = !!is_selfpowered;
+       g->is_selfpowered = !!is_selfpowered;
        spin_unlock_irqrestore(&dwc->lock, flags);
 
        return 0;
@@ -1468,7 +1467,7 @@ static int dwc3_gadget_run_stop(struct dwc3 *dwc, int is_on, int suspend)
                udelay(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->function : "no-function",
                        is_on ? "connect" : "disconnect");
@@ -1688,7 +1687,7 @@ static int dwc3_gadget_init_hw_endpoints(struct dwc3 *dwc,
 
                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) {
                        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);
        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;
        }
 
        ret = dwc3_gadget_init_hw_endpoints(dwc, dwc->num_in_eps, 1);
        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;
        }
 
@@ -1977,7 +1978,7 @@ static void dwc3_endpoint_interrupt(struct dwc3 *dwc,
                } else {
                        int ret;
 
-                       dev_vdbg(dwc->dev, "%s: reason %s\n",
+                       dwc3_trace(trace_dwc3_gadget, "%s: reason %s",
                                        dep->name, event->status &
                                        DEPEVT_STATUS_TRANSFER_ACTIVE
                                        ? "Transfer Active"
@@ -2001,7 +2002,8 @@ static void dwc3_endpoint_interrupt(struct dwc3 *dwc,
 
                switch (event->status) {
                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);
 
                        break;
@@ -2015,7 +2017,7 @@ static void dwc3_endpoint_interrupt(struct dwc3 *dwc,
                dev_dbg(dwc->dev, "%s FIFO Overrun\n", dep->name);
                break;
        case DWC3_DEPEVT_EPCMDCMPLT:
-               dev_vdbg(dwc->dev, "Endpoint Command Complete\n");
+               dwc3_trace(trace_dwc3_gadget, "Endpoint Command Complete");
                break;
        }
 }
@@ -2043,6 +2045,7 @@ static void dwc3_resume_gadget(struct dwc3 *dwc)
        if (dwc->gadget_driver && dwc->gadget_driver->resume) {
                spin_unlock(&dwc->lock);
                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
         * suggested to giveback all requests here, but give HW some
         * 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
         * (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)) {
                if ((dwc->link_state == DWC3_LINK_STATE_U3) &&
                                (next == DWC3_LINK_STATE_RESUME)) {
-                       dev_vdbg(dwc->dev, "ignoring transition U3 -> Resume\n");
+                       dwc3_trace(trace_dwc3_gadget,
+                                       "ignoring transition U3 -> Resume");
                        return;
                }
        }
@@ -2511,22 +2515,22 @@ static void dwc3_gadget_interrupt(struct dwc3 *dwc,
                dwc3_gadget_linksts_change_interrupt(dwc, event->event_info);
                break;
        case DWC3_DEVICE_EVENT_EOPF:
-               dev_vdbg(dwc->dev, "End of Periodic Frame\n");
+               dwc3_trace(trace_dwc3_gadget, "End of Periodic Frame");
                break;
        case DWC3_DEVICE_EVENT_SOF:
-               dev_vdbg(dwc->dev, "Start of Periodic Frame\n");
+               dwc3_trace(trace_dwc3_gadget, "Start of Periodic Frame");
                break;
        case DWC3_DEVICE_EVENT_ERRATIC_ERROR:
-               dev_vdbg(dwc->dev, "Erratic Error\n");
+               dwc3_trace(trace_dwc3_gadget, "Erratic Error");
                break;
        case DWC3_DEVICE_EVENT_CMD_CMPL:
-               dev_vdbg(dwc->dev, "Command Complete\n");
+               dwc3_trace(trace_dwc3_gadget, "Command Complete");
                break;
        case DWC3_DEVICE_EVENT_OVERFLOW:
-               dev_vdbg(dwc->dev, "Overflow\n");
+               dwc3_trace(trace_dwc3_gadget, "Overflow");
                break;
        default:
-               dev_dbg(dwc->dev, "UNKNOWN IRQ %d\n", event->type);
+               dev_WARN(dwc->dev, "UNKNOWN IRQ %d\n", event->type);
        }
 }
 
index 9fc20b3..9c10669 100644 (file)
@@ -47,6 +47,16 @@ DEFINE_EVENT(dwc3_log_msg, dwc3_writel,
        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,
        TP_PROTO(struct va_format *vaf),
        TP_ARGS(vaf)
index 747ef53..9653903 100644 (file)
@@ -423,6 +423,17 @@ config USB_CONFIGFS_F_HID
 
          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"
 
 endchoice
index 6178353..13adfd1 100644 (file)
@@ -1655,7 +1655,7 @@ unknown:
                 * OS descriptors handling
                 */
                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) {
                        struct usb_request              *req;
                        struct usb_configuration        *os_desc_cfg;
index dd68091..f71b1aa 100644 (file)
@@ -36,7 +36,7 @@ usb_f_uac1-y                  := f_uac1.o u_uac1.o
 obj-$(CONFIG_USB_F_UAC1)       += usb_f_uac1.o
 usb_f_uac2-y                   := 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
 usb_f_midi-y                   := f_midi.o
 obj-$(CONFIG_USB_F_MIDI)       += usb_f_midi.o
index 63314ed..af98b09 100644 (file)
@@ -31,6 +31,7 @@
 #include <linux/aio.h>
 #include <linux/mmu_context.h>
 #include <linux/poll.h>
+#include <linux/eventfd.h>
 
 #include "u_fs.h"
 #include "u_f.h"
@@ -153,6 +154,8 @@ struct ffs_io_data {
 
        struct usb_ep *ep;
        struct usb_request *req;
+
+       struct ffs_data *ffs;
 };
 
 struct ffs_desc_helper {
@@ -390,17 +393,20 @@ done_spin:
        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,
                                     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;
 
-       memset(events, 0, sizeof events);
+       memset(events, 0, size);
 
        do {
                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);
 
-       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,
                        ffs->ev.count * sizeof *ffs->ev.types);
-       } else {
-               ffs->ev.count = 0;
-       }
 
        spin_unlock_irq(&ffs->ev.waitq.lock);
        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,
@@ -606,6 +608,8 @@ static unsigned int ffs_ep0_poll(struct file *file, poll_table *wait)
                }
        case FFS_CLOSING:
                break;
+       case FFS_DEACTIVATED:
+               break;
        }
 
        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);
 
+       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);
 
        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->ep = ep->ep;
                        io_data->req = req;
+                       io_data->ffs = epfile->ffs;
 
                        req->context  = io_data;
                        req->complete = ffs_epfile_async_io_complete;
@@ -1180,6 +1188,7 @@ struct ffs_sb_fill_data {
        struct ffs_file_perms perms;
        umode_t root_mode;
        const char *dev_name;
+       bool no_disconnect;
        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 */
                switch (eq - opts) {
+               case 13:
+                       if (!memcmp(opts, "no_disconnect", 13))
+                               data->no_disconnect = !!value;
+                       else
+                               goto invalid;
+                       break;
                case 5:
                        if (!memcmp(opts, "rmode", 5))
                                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,
                },
                .root_mode = S_IFDIR | 0500,
+               .no_disconnect = false,
        };
        struct dentry *rv;
        int ret;
@@ -1330,6 +1346,7 @@ ffs_fs_mount(struct file_system_type *t, int flags,
        if (unlikely(!ffs))
                return ERR_PTR(-ENOMEM);
        ffs->file_perms = data.perms;
+       ffs->no_disconnect = data.no_disconnect;
 
        ffs->dev_name = kstrdup(dev_name, GFP_KERNEL);
        if (unlikely(!ffs->dev_name)) {
@@ -1361,6 +1378,7 @@ ffs_fs_kill_sb(struct super_block *sb)
        kill_litter_super(sb);
        if (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);
        }
 }
@@ -1417,7 +1435,11 @@ static void ffs_data_opened(struct ffs_data *ffs)
        ENTER();
 
        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)
@@ -1439,6 +1461,21 @@ static void ffs_data_closed(struct ffs_data *ffs)
        ENTER();
 
        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_data_reset(ffs);
        }
@@ -1480,6 +1517,9 @@ static void ffs_data_clear(struct ffs_data *ffs)
        if (ffs->epfiles)
                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_strings);
        kfree(ffs->stringtabs);
@@ -1581,10 +1621,10 @@ static int ffs_epfiles_create(struct ffs_data *ffs)
                mutex_init(&epfile->mutex);
                init_waitqueue_head(&epfile->wait);
                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
-                       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,
                                                 &ffs_epfile_operations);
                if (unlikely(!epfile->dentry)) {
@@ -1616,7 +1656,6 @@ static void ffs_epfiles_destroy(struct ffs_epfile *epfiles, unsigned count)
        kfree(epfiles);
 }
 
-
 static void ffs_func_eps_disable(struct ffs_function *func)
 {
        struct ffs_ep *ep         = func->eps;
@@ -1629,10 +1668,12 @@ static void ffs_func_eps_disable(struct ffs_function *func)
                /* pending requests get nuked */
                if (likely(ep->ep))
                        usb_ep_disable(ep->ep);
-               epfile->ep = NULL;
-
                ++ep;
-               ++epfile;
+
+               if (epfile) {
+                       epfile->ep = NULL;
+                       ++epfile;
+               }
        } while (--count);
        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_SS_DESC |
                              FUNCTIONFS_HAS_MS_OS_DESC |
-                             FUNCTIONFS_VIRTUAL_ADDR)) {
+                             FUNCTIONFS_VIRTUAL_ADDR |
+                             FUNCTIONFS_EVENTFD)) {
                        ret = -ENOSYS;
                        goto error;
                }
@@ -2149,6 +2191,20 @@ static int __ffs_data_got_descs(struct ffs_data *ffs,
                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) */
        for (i = 0; i < 3; ++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)
                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) {
        case FUNCTIONFS_RESUME:
                rem_type2 = FUNCTIONFS_SUSPEND;
@@ -2416,6 +2479,8 @@ static void __ffs_event_add(struct ffs_data *ffs,
        pr_vdebug("adding event %d\n", type);
        ffs->ev.types[ffs->ev.count++] = type;
        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,
@@ -2888,6 +2953,13 @@ static int ffs_func_bind(struct usb_configuration *c,
 
 /* 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,
                            unsigned interface, unsigned alt)
 {
@@ -2904,6 +2976,13 @@ static int ffs_func_set_alt(struct usb_function *f,
        if (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)
                return -ENODEV;
 
index a1bc3e3..426d69a 100644 (file)
@@ -759,7 +759,7 @@ static struct f_hid_opts_attribute f_hid_opts_##name =                      \
 
 F_HID_OPT(subclass, 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)
 {
index 80be25b..e07c50c 100644 (file)
@@ -1214,7 +1214,7 @@ static ssize_t f_ss_opts_pattern_show(struct f_ss_opts *opts, char *page)
        int result;
 
        mutex_lock(&opts->lock);
-       result = sprintf(page, "%d", opts->pattern);
+       result = sprintf(page, "%u", opts->pattern);
        mutex_unlock(&opts->lock);
 
        return result;
@@ -1258,7 +1258,7 @@ static ssize_t f_ss_opts_isoc_interval_show(struct f_ss_opts *opts, char *page)
        int result;
 
        mutex_lock(&opts->lock);
-       result = sprintf(page, "%d", opts->isoc_interval);
+       result = sprintf(page, "%u", opts->isoc_interval);
        mutex_unlock(&opts->lock);
 
        return result;
@@ -1302,7 +1302,7 @@ static ssize_t f_ss_opts_isoc_maxpacket_show(struct f_ss_opts *opts, char *page)
        int result;
 
        mutex_lock(&opts->lock);
-       result = sprintf(page, "%d", opts->isoc_maxpacket);
+       result = sprintf(page, "%u", opts->isoc_maxpacket);
        mutex_unlock(&opts->lock);
 
        return result;
@@ -1346,7 +1346,7 @@ static ssize_t f_ss_opts_isoc_mult_show(struct f_ss_opts *opts, char *page)
        int result;
 
        mutex_lock(&opts->lock);
-       result = sprintf(page, "%d", opts->isoc_mult);
+       result = sprintf(page, "%u", opts->isoc_mult);
        mutex_unlock(&opts->lock);
 
        return result;
@@ -1390,7 +1390,7 @@ static ssize_t f_ss_opts_isoc_maxburst_show(struct f_ss_opts *opts, char *page)
        int result;
 
        mutex_lock(&opts->lock);
-       result = sprintf(page, "%d", opts->isoc_maxburst);
+       result = sprintf(page, "%u", opts->isoc_maxburst);
        mutex_unlock(&opts->lock);
 
        return result;
@@ -1434,7 +1434,7 @@ static ssize_t f_ss_opts_bulk_buflen_show(struct f_ss_opts *opts, char *page)
        int result;
 
        mutex_lock(&opts->lock);
-       result = sprintf(page, "%d", opts->bulk_buflen);
+       result = sprintf(page, "%u", opts->bulk_buflen);
        mutex_unlock(&opts->lock);
 
        return result;
@@ -1473,7 +1473,7 @@ static ssize_t f_ss_opts_int_interval_show(struct f_ss_opts *opts, char *page)
        int result;
 
        mutex_lock(&opts->lock);
-       result = sprintf(page, "%d", opts->int_interval);
+       result = sprintf(page, "%u", opts->int_interval);
        mutex_unlock(&opts->lock);
 
        return result;
@@ -1517,7 +1517,7 @@ static ssize_t f_ss_opts_int_maxpacket_show(struct f_ss_opts *opts, char *page)
        int result;
 
        mutex_lock(&opts->lock);
-       result = sprintf(page, "%d", opts->int_maxpacket);
+       result = sprintf(page, "%u", opts->int_maxpacket);
        mutex_unlock(&opts->lock);
 
        return result;
@@ -1561,7 +1561,7 @@ static ssize_t f_ss_opts_int_mult_show(struct f_ss_opts *opts, char *page)
        int result;
 
        mutex_lock(&opts->lock);
-       result = sprintf(page, "%d", opts->int_mult);
+       result = sprintf(page, "%u", opts->int_mult);
        mutex_unlock(&opts->lock);
 
        return result;
@@ -1605,7 +1605,7 @@ static ssize_t f_ss_opts_int_maxburst_show(struct f_ss_opts *opts, char *page)
        int result;
 
        mutex_lock(&opts->lock);
-       result = sprintf(page, "%d", opts->int_maxburst);
+       result = sprintf(page, "%u", opts->int_maxburst);
        mutex_unlock(&opts->lock);
 
        return result;
index e971584..9719abf 100644 (file)
@@ -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_AS_INTERFACE   1
-#define F_AUDIO_NUM_INTERFACES 2
+#define F_AUDIO_NUM_INTERFACES 1
 
 /* B.3.1  Standard AC Interface Descriptor */
 static struct usb_interface_descriptor ac_interface_desc = {
@@ -42,14 +42,18 @@ static struct usb_interface_descriptor ac_interface_desc = {
        .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)
 /* 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 \
        + UAC_DT_OUTPUT_TERMINAL_SIZE + UAC_DT_FEATURE_UNIT_SIZE(0))
 /* 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,
        .bDescriptorType =      USB_DT_CS_INTERFACE,
        .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),
        .bInCollection =        F_AUDIO_NUM_INTERFACES,
        .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 (alt == 1) {
+                       config_ep_by_speed(cdev->gadget, f, out_ep);
                        usb_ep_enable(out_ep);
                        out_ep->driver_data = audio;
                        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->card.gadget = c->cdev->gadget;
-       audio_opts->card = &audio->card;
        /* set up ASLA audio devices */
        if (!audio_opts->bound) {
                status = gaudio_setup(&audio->card);
index 945b3bd..76891ad 100644 (file)
 #include <media/v4l2-dev.h>
 #include <media/v4l2-event.h>
 
+#include "u_uvc.h"
 #include "uvc.h"
+#include "uvc_configfs.h"
 #include "uvc_v4l2.h"
 #include "uvc_video.h"
-#include "u_uvc.h"
 
 unsigned int uvc_gadget_trace_param;
 
@@ -509,6 +510,9 @@ uvc_copy_descriptors(struct uvc_device *uvc, enum usb_device_speed speed)
                break;
        }
 
+       if (!uvc_control_desc || !uvc_streaming_cls)
+               return ERR_PTR(-ENODEV);
+
        /* Descriptors layout
         *
         * uvc_iad
@@ -605,7 +609,7 @@ uvc_function_bind(struct usb_configuration *c, struct usb_function *f)
 
        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.
         */
        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 */
        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);
-       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);
+               if (IS_ERR(f->ss_descriptors)) {
+                       ret = PTR_ERR(f->ss_descriptors);
+                       f->ss_descriptors = NULL;
+                       goto error;
+               }
+       }
 
        /* Preallocate control endpoint request. */
        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)
 {
-       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);
 }
 
 static struct usb_function_instance *uvc_alloc_inst(void)
 {
        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);
        if (!opts)
                return ERR_PTR(-ENOMEM);
        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;
 }
 
 static void uvc_free(struct usb_function *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);
 }
 
@@ -812,19 +912,39 @@ static struct usb_function *uvc_alloc(struct usb_function_instance *fi)
 {
        struct uvc_device *uvc;
        struct f_uvc_opts *opts;
+       struct uvc_descriptor_header **strm_cls;
 
        uvc = kzalloc(sizeof(*uvc), GFP_KERNEL);
        if (uvc == NULL)
                return ERR_PTR(-ENOMEM);
 
        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.ss_control = opts->ss_control;
        uvc->desc.fs_streaming = opts->fs_streaming;
        uvc->desc.hs_streaming = opts->hs_streaming;
        uvc->desc.ss_streaming = opts->ss_streaming;
+       ++opts->refcnt;
+       mutex_unlock(&opts->lock);
 
        /* Register the function. */
        uvc->func.name = "uvc";
index 6e6f876..f1fd777 100644 (file)
@@ -729,9 +729,7 @@ static int get_ether_addr_str(u8 dev_addr[ETH_ALEN], char *str, int len)
        if (len < 18)
                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;
 }
 
index cd128e3..6013985 100644 (file)
@@ -19,6 +19,7 @@
 #include <linux/usb/composite.h>
 #include <linux/list.h>
 #include <linux/mutex.h>
+#include <linux/workqueue.h>
 
 #ifdef VERBOSE_DEBUG
 #ifndef pr_vdebug
@@ -93,6 +94,26 @@ enum ffs_state {
        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
         * we encounter an unrecoverable error.  The only
         * unrecoverable error is situation when after reading strings
@@ -251,6 +272,10 @@ struct ffs_data {
                kgid_t                          gid;
        }                               file_perms;
 
+       struct eventfd_ctx *ffs_eventfd;
+       bool no_disconnect;
+       struct work_struct reset_work;
+
        /*
         * The endpoint files, filled by ffs_epfiles_create(),
         * destroyed by ffs_epfiles_destroy().
index 53842a1..c78c841 100644 (file)
@@ -308,8 +308,7 @@ int gaudio_setup(struct gaudio *card)
  */
 void gaudio_cleanup(struct gaudio *the_card)
 {
-       if (the_card) {
+       if (the_card)
                gaudio_close_snd_dev(the_card);
-       }
 }
 
index f8b17fe..fe386df 100644 (file)
@@ -70,7 +70,6 @@ struct f_uac1_opts {
        unsigned                        fn_play_alloc:1;
        unsigned                        fn_cap_alloc:1;
        unsigned                        fn_cntl_alloc:1;
-       struct gaudio                   *card;
        struct mutex                    lock;
        int                             refcnt;
 };
index 2a8dfdf..4676b60 100644 (file)
@@ -17,8 +17,9 @@
 #define U_UVC_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 usb_function_instance                    func_inst;
@@ -26,11 +27,60 @@ struct f_uvc_opts {
        unsigned int                                    streaming_interval;
        unsigned int                                    streaming_maxpacket;
        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      *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      *hs_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);
diff --git a/drivers/usb/gadget/function/uvc_configfs.c b/drivers/usb/gadget/function/uvc_configfs.c
new file mode 100644 (file)
index 0000000..3c0467b
--- /dev/null
@@ -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;
+}
diff --git a/drivers/usb/gadget/function/uvc_configfs.h b/drivers/usb/gadget/function/uvc_configfs.h
new file mode 100644 (file)
index 0000000..085e67b
--- /dev/null
@@ -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 */
index c862656..c0ec5f7 100644 (file)
@@ -176,7 +176,7 @@ static int proc_udc_show(struct seq_file *s, void *unused)
                udc->enabled
                        ? (udc->vbus ? "active" : "enabled")
                        : "disabled",
-               udc->selfpowered ? "self" : "VBUS",
+               udc->gadget.is_selfpowered ? "self" : "VBUS",
                udc->suspended ? ", suspended" : "",
                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;
 
        spin_lock_irqsave(&udc->lock, flags);
-       udc->selfpowered = (is_on != 0);
+       gadget->is_selfpowered = (is_on != 0);
        spin_unlock_irqrestore(&udc->lock, flags);
        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)
                        | 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)
                        tmp |= (1 << USB_DEVICE_REMOTE_WAKEUP);
                PACKET("get device status\n");
@@ -1653,7 +1653,7 @@ static int at91_start(struct usb_gadget *gadget,
        udc->driver = driver;
        udc->gadget.dev.of_node = udc->pdev->dev.of_node;
        udc->enabled = 1;
-       udc->selfpowered = 1;
+       udc->gadget.is_selfpowered = 1;
 
        return 0;
 }
index 0175246..467dcfb 100644 (file)
@@ -122,7 +122,6 @@ struct at91_udc {
        unsigned                        req_pending:1;
        unsigned                        wait_for_addr_ack:1;
        unsigned                        wait_for_config_ack:1;
-       unsigned                        selfpowered:1;
        unsigned                        active_suspend:1;
        u8                              addr;
        struct at91_udc_data            board;
index 9f93bed..c041086 100644 (file)
@@ -8,6 +8,7 @@
  * published by the Free Software Foundation.
  */
 #include <linux/clk.h>
+#include <linux/clk/at91_pmc.h>
 #include <linux/module.h>
 #include <linux/init.h>
 #include <linux/interrupt.h>
@@ -315,6 +316,17 @@ static inline void usba_cleanup_debugfs(struct usba_udc *udc)
 }
 #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)
 {
        if (gpio_is_valid(udc->vbus_pin))
@@ -324,27 +336,22 @@ static int vbus_is_present(struct usba_udc *udc)
        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)
 {
@@ -601,16 +608,14 @@ usba_ep_enable(struct usb_ep *_ep, const struct usb_endpoint_descriptor *desc)
        if (ep->can_dma) {
                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;
                usba_ep_writel(ep, CTL_ENB, ctrl);
        } 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);
@@ -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,
                        (unsigned long)usba_ep_readl(ep, CFG));
        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;
 }
@@ -654,9 +659,8 @@ static int usba_ep_disable(struct usb_ep *_ep)
                usba_dma_readl(ep, STATUS);
        }
        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);
 
@@ -985,6 +989,7 @@ usba_udc_set_selfpowered(struct usb_gadget *gadget, int is_selfpowered)
        struct usba_udc *udc = to_usba_udc(gadget);
        unsigned long flags;
 
+       gadget->is_selfpowered = (is_selfpowered != 0);
        spin_lock_irqsave(&udc->lock, flags);
        if (is_selfpowered)
                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)
 {
        struct usba_udc *udc = devid;
-       u32 status;
+       u32 status, int_enb;
        u32 dma_status;
        u32 ep_status;
 
        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);
 
        if (status & USBA_DET_SUSPEND) {
-               toggle_bias(0);
+               toggle_bias(udc, 0);
                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");
                if (udc->gadget.speed != USB_SPEED_UNKNOWN
                                && udc->driver && udc->driver->suspend) {
@@ -1641,13 +1649,15 @@ static irqreturn_t usba_udc_irq(int irq, void *devid)
        }
 
        if (status & USBA_WAKE_UP) {
-               toggle_bias(1);
+               toggle_bias(udc, 1);
                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");
        }
 
        if (status & USBA_END_OF_RESUME) {
                usba_writel(udc, INT_CLR, USBA_END_OF_RESUME);
+               generate_bias_pulse(udc);
                DBG(DBG_BUS, "Resume detected\n");
                if (udc->gadget.speed != USB_SPEED_UNKNOWN
                                && udc->driver && udc->driver->resume) {
@@ -1683,6 +1693,7 @@ static irqreturn_t usba_udc_irq(int irq, void *devid)
                struct usba_ep *ep0;
 
                usba_writel(udc, INT_CLR, USBA_END_OF_RESET);
+               generate_bias_pulse(udc);
                reset_all_endpoints(udc);
 
                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_ep_writel(ep0, CTL_ENB,
                                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,
@@ -1745,13 +1753,13 @@ static irqreturn_t usba_vbus_irq(int irq, void *devid)
        vbus = vbus_is_present(udc);
        if (vbus != udc->vbus_prev) {
                if (vbus) {
-                       toggle_bias(1);
+                       toggle_bias(udc, 1);
                        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 {
                        udc->gadget.speed = USB_SPEED_UNKNOWN;
                        reset_all_endpoints(udc);
-                       toggle_bias(0);
+                       toggle_bias(udc, 0);
                        usba_writel(udc, CTRL, USBA_DISABLE_MASK);
                        if (udc->driver->disconnect) {
                                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 */
        spin_lock_irqsave(&udc->lock, flags);
        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, INT_ENB, USBA_END_OF_RESET);
+               usba_int_enb_set(udc, USBA_END_OF_RESET);
        }
        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);
 
        /* This will also disable the DP pullup */
-       toggle_bias(0);
+       toggle_bias(udc, 0);
        usba_writel(udc, CTRL, USBA_DISABLE_MASK);
 
        clk_disable_unprepare(udc->hclk);
@@ -1832,6 +1840,41 @@ static int atmel_usba_stop(struct usb_gadget *gadget)
 }
 
 #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,
                                                    struct usba_udc *udc)
 {
@@ -1839,10 +1882,17 @@ static struct usba_ep * atmel_udc_of_init(struct platform_device *pdev,
        const char *name;
        enum of_gpio_flags flags;
        struct device_node *np = pdev->dev.of_node;
+       const struct of_device_id *match;
        struct device_node *pp;
        int i, ret;
        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->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");
                return ret;
        }
-       toggle_bias(0);
+
        usba_writel(udc, CTRL, USBA_DISABLE_MASK);
        clk_disable_unprepare(pclk);
 
@@ -2042,6 +2092,8 @@ static int usba_udc_probe(struct platform_device *pdev)
        else
                udc->usba_ep = usba_udc_pdata(pdev, udc);
 
+       toggle_bias(udc, 0);
+
        if (IS_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;
 }
 
-#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 = {
        .remove         = __exit_p(usba_udc_remove),
        .driver         = {
index a70706e..497cd18 100644 (file)
@@ -304,6 +304,11 @@ struct usba_request {
        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 {
        /* Protect hw registers from concurrent modifications */
        spinlock_t lock;
@@ -314,6 +319,7 @@ struct usba_udc {
        struct usb_gadget gadget;
        struct usb_gadget_driver *driver;
        struct platform_device *pdev;
+       const struct usba_udc_errata *errata;
        int irq;
        int vbus_pin;
        int vbus_pin_inverted;
@@ -321,12 +327,15 @@ struct usba_udc {
        struct clk *pclk;
        struct clk *hclk;
        struct usba_ep *usba_ep;
+       bool bias_pulse_needed;
 
        u16 devstatus;
 
        u16 test_mode;
        int vbus_prev;
 
+       u32 int_enb_cache;
+
 #ifdef CONFIG_USB_GADGET_DEBUG_FS
        struct dentry *debugfs_root;
        struct dentry *debugfs_regs;
index d4fe8d7..b04980c 100644 (file)
@@ -718,7 +718,7 @@ static int ep_queue(struct bdc_ep *ep, struct bdc_req *req)
        struct bdc *bdc;
        int ret = 0;
 
-       if (!req || !ep || !ep->usb_ep.desc)
+       if (!req || !ep->usb_ep.desc)
                return -EINVAL;
 
        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);
                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
                        ep->flags |= BDC_EP_STALL;
        } else {
@@ -891,8 +891,8 @@ static int ep_set_halt(struct bdc_ep *ep, u32 value)
                dev_dbg(bdc->dev, "Before Clear\n");
                ret = bdc_ep_clear_stall(bdc, ep->ep_num);
                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
                        ep->flags &= ~BDC_EP_STALL;
                dev_dbg(bdc->dev, "After  Clear\n");
index 3700ce7..7f77db5 100644 (file)
@@ -454,6 +454,7 @@ static int bdc_udc_set_selfpowered(struct usb_gadget *gadget,
        unsigned long           flags;
 
        dev_dbg(bdc->dev, "%s()\n", __func__);
+       gadget->is_selfpowered = (is_self != 0);
        spin_lock_irqsave(&bdc->lock, flags);
        if (!is_self)
                bdc->devstatus |= 1 << USB_DEVICE_SELF_POWERED;
index 270c1ec..8dda484 100644 (file)
@@ -802,6 +802,7 @@ static int dummy_set_selfpowered(struct usb_gadget *_gadget, int value)
 {
        struct dummy    *dum;
 
+       _gadget->is_selfpowered = (value != 0);
        dum = gadget_to_dummy_hcd(_gadget)->dum;
        if (value)
                dum->devstatus |= (1 << USB_DEVICE_SELF_POWERED);
index 795c99c..e0822f1 100644 (file)
@@ -2630,7 +2630,7 @@ static int qe_udc_remove(struct platform_device *ofdev)
        struct qe_udc *udc = platform_get_drvdata(ofdev);
        struct qe_ep *ep;
        unsigned int size;
-       DECLARE_COMPLETION(done);
+       DECLARE_COMPLETION_ONSTACK(done);
 
        usb_del_gadget_udc(&udc->gadget);
 
index 2df8074..55fcb93 100644 (file)
@@ -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) {
                /* Get device status */
-               tmp = 1 << USB_DEVICE_SELF_POWERED;
+               tmp = udc->gadget.is_selfpowered;
                tmp |= udc->remote_wakeup << USB_DEVICE_REMOTE_WAKEUP;
        } else if ((request_type & USB_RECIP_MASK) == USB_RECIP_INTERFACE) {
                /* Get interface status */
@@ -1948,6 +1948,7 @@ static int fsl_udc_start(struct usb_gadget *g,
        /* hook up the driver */
        udc_controller->driver = driver;
        spin_unlock_irqrestore(&udc_controller->lock, flags);
+       g->is_selfpowered = 1;
 
        if (!IS_ERR_OR_NULL(udc_controller->transceiver)) {
                /* 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 fsl_usb2_platform_data *pdata = dev_get_platdata(&pdev->dev);
 
-       DECLARE_COMPLETION(done);
+       DECLARE_COMPLETION_ONSTACK(done);
 
        if (!udc_controller)
                return -ENODEV;
index 34d9b7b..27fd413 100644 (file)
@@ -191,7 +191,6 @@ struct lpc32xx_udc {
        bool                    enabled;
        bool                    clocked;
        bool                    suspended;
-       bool                    selfpowered;
        int                     ep0state;
        atomic_t                enabled_ep_cnt;
        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->enabled ? (udc->vbus ? "active" : "enabled") :
                   "disabled",
-                  udc->selfpowered ? "self" : "VBUS",
+                  udc->gadget.is_selfpowered ? "self" : "VBUS",
                   udc->suspended ? ", suspended" : "",
                   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 */
 
        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))
                        ep0buff |= (1 << USB_DEVICE_REMOTE_WAKEUP);
                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)
 {
-       struct lpc32xx_udc *udc = to_udc(gadget);
-
-       /* Always self-powered */
-       udc->selfpowered = (is_on != 0);
+       gadget->is_selfpowered = (is_on != 0);
 
        return 0;
 }
@@ -2946,7 +2942,7 @@ static int lpc32xx_start(struct usb_gadget *gadget,
        udc->driver = driver;
        udc->gadget.dev.of_node = udc->dev->of_node;
        udc->enabled = 1;
-       udc->selfpowered = 1;
+       udc->gadget.is_selfpowered = 1;
        udc->vbus = 0;
 
        /* Force VBUS process once to check for cable insertion */
index 253f3df..d32160d 100644 (file)
@@ -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 */
        if (udc->qwork)
                queue_work(udc->qwork, &udc->vbus_work);
index d20de1f..195baf3 100644 (file)
@@ -1132,13 +1132,10 @@ net2272_wakeup(struct usb_gadget *_gadget)
 static int
 net2272_set_selfpowered(struct usb_gadget *_gadget, int value)
 {
-       struct net2272 *dev;
-
        if (!_gadget)
                return -ENODEV;
-       dev = container_of(_gadget, struct net2272, gadget);
 
-       dev->is_selfpowered = value;
+       _gadget->is_selfpowered = (value != 0);
 
        return 0;
 }
@@ -1844,7 +1841,7 @@ net2272_handle_stat0_irqs(struct net2272 *dev, u8 stat)
                        case USB_RECIP_DEVICE:
                                if (u.r.wLength > 2)
                                        goto do_stall;
-                               if (dev->is_selfpowered)
+                               if (dev->gadget.is_selfpowered)
                                        status = (1 << USB_DEVICE_SELF_POWERED);
 
                                /* don't bother with a request object! */
index e595057..127ab03 100644 (file)
@@ -458,7 +458,6 @@ struct net2272 {
        struct usb_gadget_driver *driver;
        unsigned protocol_stall:1,
                 softconnect:1,
-                is_selfpowered:1,
                 wakeup:1,
                 dma_eot_polarity:1,
                 dma_dack_polarity:1,
index d6411e0..d2c0bf6 100644 (file)
  * the Mass Storage, Serial, and Ethernet/RNDIS gadget drivers
  * as well as Gadget Zero and Gadgetfs.
  *
- * DMA is enabled by default.  Drivers using transfer queues might use
- * DMA chaining to remove IRQ latencies between transfers.  (Except when
- * short OUT transfers happen.)  Drivers can use the req->no_interrupt
- * hint to completely eliminate some IRQs, if a later IRQ is guaranteed
- * and DMA chaining is enabled.
+ * DMA is enabled by default.
  *
  * MSI is enabled by default.  The legacy IRQ is used if MSI couldn't
  * be enabled.
@@ -84,23 +80,6 @@ static const char *const ep_name[] = {
        "ep-e", "ep-f", "ep-g", "ep-h",
 };
 
-/* use_dma -- general goodness, fewer interrupts, less cpu load (vs PIO)
- * use_dma_chaining -- dma descriptor queueing gives even more irq reduction
- *
- * The net2280 DMA engines are not tightly integrated with their FIFOs;
- * not all cases are (yet) handled well in this driver or the silicon.
- * Some gadget drivers work better with the dma support here than others.
- * These two parameters let you use PIO or more aggressive DMA.
- */
-static bool use_dma = true;
-static bool use_dma_chaining;
-static bool use_msi = true;
-
-/* "modprobe net2280 use_dma=n" etc */
-module_param(use_dma, bool, 0444);
-module_param(use_dma_chaining, bool, 0444);
-module_param(use_msi, bool, 0444);
-
 /* mode 0 == ep-{a,b,c,d} 1K fifo each
  * mode 1 == ep-{a,b} 2K fifo each, ep-{c,d} unavailable
  * mode 2 == ep-a 2K fifo, ep-{b,c} 1K each, ep-d unavailable
@@ -120,11 +99,6 @@ static bool enable_suspend;
 /* "modprobe net2280 enable_suspend=1" etc */
 module_param(enable_suspend, bool, 0444);
 
-/* force full-speed operation */
-static bool full_speed;
-module_param(full_speed, bool, 0444);
-MODULE_PARM_DESC(full_speed, "force full-speed mode -- for testing only!");
-
 #define        DIR_STRING(bAddress) (((bAddress) & USB_DIR_IN) ? "in" : "out")
 
 static char *type_string(u8 bmAttributes)
@@ -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_max_speed(ep, max);
 
-       /* FIFO lines can't go to different packets.  PIO is ok, so
-        * use it instead of troublesome (non-bulk) multi-packet DMA.
-        */
-       if (ep->dma && (max % 4) != 0 && use_dma_chaining) {
-               ep_dbg(ep->dev, "%s, no dma for maxpacket %d\n",
-                       ep->ep.name, ep->ep.maxpacket);
-               ep->dma = NULL;
-       }
-
        /* set type, direction, address; reset fifo counters */
        writel(BIT(FIFO_FLUSH), &ep->regs->ep_stat);
        tmp = (desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK);
@@ -478,7 +443,7 @@ static int net2280_disable(struct usb_ep *_ep)
        /* synch memory views with the device */
        (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];
 
        spin_unlock_irqrestore(&ep->dev->lock, flags);
@@ -610,9 +575,15 @@ static void out_flush(struct net2280_ep *ep)
        u32     __iomem *statp;
        u32     tmp;
 
-       ASSERT_OUT_NAKING(ep);
-
        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) |
                BIT(DATA_PACKET_RECEIVED_INTERRUPT),
                statp);
@@ -747,8 +718,7 @@ static void fill_dma_desc(struct net2280_ep *ep,
        req->valid = valid;
        if (valid)
                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->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);
        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);
 }
 
-static inline void resume_dma(struct net2280_ep *ep)
-{
-       writel(readl(&ep->dma->dmactl) | BIT(DMA_ENABLE), &ep->dma->dmactl);
-
-       ep->dma_started = true;
-}
-
-static inline void ep_stop_dma(struct net2280_ep *ep)
-{
-       writel(readl(&ep->dma->dmactl) & ~BIT(DMA_ENABLE), &ep->dma->dmactl);
-       spin_stop_dma(ep->dma);
-
-       ep->dma_started = false;
-}
-
 static inline void
 queue_dma(struct net2280_ep *ep, struct net2280_request *req, int valid)
 {
@@ -973,10 +927,8 @@ net2280_queue(struct usb_ep *_ep, struct usb_request *_req, gfp_t gfp_flags)
                        return ret;
        }
 
-#if 0
        ep_vdbg(dev, "%s queue req %p, len %d buf %p\n",
                        _ep->name, _req, _req->length, _req->buf);
-#endif
 
        spin_lock_irqsave(&dev->lock, flags);
 
@@ -984,24 +936,12 @@ net2280_queue(struct usb_ep *_ep, struct usb_request *_req, gfp_t gfp_flags)
        _req->actual = 0;
 
        /* kickstart this i/o queue? */
-       if (list_empty(&ep->queue) && !ep->stopped) {
-               /* DMA request while EP halted */
-               if (ep->dma &&
-                   (readl(&ep->regs->ep_rsp) & BIT(CLEAR_ENDPOINT_HALT)) &&
-                       (dev->quirks & PLX_SUPERSPEED)) {
-                       int valid = 1;
-                       if (ep->is_in) {
-                               int expect;
-                               expect = likely(req->req.zero ||
-                                               ((req->req.length %
-                                                 ep->ep.maxpacket) != 0));
-                               if (expect != ep->in_fifo_validate)
-                                       valid = 0;
-                       }
-                       queue_dma(ep, req, valid);
-               }
+       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 */
-               else if (ep->dma)
+               if (ep->dma)
                        start_dma(ep, req);
                else {
                        /* 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);
 }
 
-static void restart_dma(struct net2280_ep *ep);
-
 static void scan_dma_completions(struct net2280_ep *ep)
 {
        /* only look at descriptors that were "naturally" retired,
@@ -1117,9 +1055,8 @@ static void scan_dma_completions(struct net2280_ep *ep)
                        dma_done(ep, req, tmp, 0);
                        break;
                } 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);
                        /* 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)
 {
        struct net2280_request  *req;
-       u32                     dmactl = dmactl_default;
 
        if (ep->stopped)
                return;
        req = list_entry(ep->queue.next, struct net2280_request, queue);
 
-       if (!use_dma_chaining) {
-               start_dma(ep, req);
-               return;
-       }
-
-       /* the 2280 will be processing the queue unless queue hiccups after
-        * the previous transfer:
-        *  IN:   wanted automagic zlp, head doesn't (or vice versa)
-        *        DMA_FIFO_VALIDATE doesn't init from dma descriptors.
-        *  OUT:  was "usb-short", we must restart.
-        */
-       if (ep->is_in && !req->valid) {
-               struct net2280_request  *entry, *prev = NULL;
-               int                     reqmode, done = 0;
-
-               ep_dbg(ep->dev, "%s dma hiccup td %p\n", ep->ep.name, req->td);
-               ep->in_fifo_validate = likely(req->req.zero ||
-                               (req->req.length % ep->ep.maxpacket) != 0);
-               if (ep->in_fifo_validate)
-                       dmactl |= BIT(DMA_FIFO_VALIDATE);
-               list_for_each_entry(entry, &ep->queue, queue) {
-                       __le32          dmacount;
-
-                       if (entry == req)
-                               continue;
-                       dmacount = entry->td->dmacount;
-                       if (!done) {
-                               reqmode = likely(entry->req.zero ||
-                                  (entry->req.length % ep->ep.maxpacket));
-                               if (reqmode == ep->in_fifo_validate) {
-                                       entry->valid = 1;
-                                       dmacount |= valid_bit;
-                                       entry->td->dmacount = dmacount;
-                                       prev = entry;
-                                       continue;
-                               } else {
-                                       /* force a hiccup */
-                                       prev->td->dmacount |= dma_done_ie;
-                                       done = 1;
-                               }
-                       }
-
-                       /* walk the rest of the queue so unlinks behave */
-                       entry->valid = 0;
-                       dmacount &= ~valid_bit;
-                       entry->td->dmacount = dmacount;
-                       prev = entry;
-               }
-       }
-
-       writel(0, &ep->dma->dmactl);
-       start_queue(ep, dmactl, req->td_dma);
+       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 */
        if (likely(!list_empty(&ep->queue))) {
@@ -1222,19 +1107,6 @@ static void abort_dma_228x(struct net2280_ep *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 */
 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);
                }
                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)
@@ -1512,10 +1365,10 @@ static int net2280_set_selfpowered(struct usb_gadget *_gadget, int value)
        tmp = readl(&dev->usb->usbctl);
        if (value) {
                tmp |= BIT(SELF_POWERED_STATUS);
-               dev->selfpowered = 1;
+               _gadget->is_selfpowered = 1;
        } else {
                tmp &= ~BIT(SELF_POWERED_STATUS);
-               dev->selfpowered = 0;
+               _gadget->is_selfpowered = 0;
        }
        writel(tmp, &dev->usb->usbctl);
        spin_unlock_irqrestore(&dev->lock, flags);
@@ -1604,14 +1457,11 @@ static ssize_t registers_show(struct device *_dev,
 
        /* Main Control Registers */
        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"
                        "pci irqenb0 %02x irqenb1 %08x "
                        "irqstat0 %04x irqstat1 %08x\n",
                        driver_name, dev->chiprev,
-                       use_dma
-                               ? (use_dma_chaining ? "chaining" : "enabled")
-                               : "disabled",
                        readl(&dev->regs->devinit),
                        readl(&dev->regs->fifoctl),
                        s,
@@ -1913,76 +1763,73 @@ static void defect7374_disable_data_eps(struct net2280 *dev)
 static void defect7374_enable_data_eps_zero(struct net2280 *dev)
 {
        u32 tmp = 0, tmp_reg;
-       u32 fsmvalue, scratch;
+       u32 scratch;
        int i;
        unsigned char ep_sel;
 
        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);
 
-       /*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:
@@ -2033,21 +1880,13 @@ static void usb_reset_228x(struct net2280 *dev)
 static void usb_reset_338x(struct net2280 *dev)
 {
        u32 tmp;
-       u32 fsmvalue;
 
        dev->gadget.speed = USB_SPEED_UNKNOWN;
        (void)readl(&dev->usb->usbctl);
 
        net2280_led_init(dev);
 
-       fsmvalue = get_idx_reg(dev->regs, SCRATCH) &
-                       (0xf << DEFECT7374_FSM_FIELD);
-
-       /* See if firmware needs to set up for workaround: */
-       if (fsmvalue != DEFECT7374_FSM_SS_CONTROL_READ) {
-               ep_info(dev, "%s: Defect 7374 FsmValue 0x%08x\n", __func__,
-                    fsmvalue);
-       } else {
+       if (dev->bug7734_patched) {
                /* disable automatic responses, and irqs */
                writel(0, &dev->usb->stdrsp);
                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);
 
-       if (fsmvalue == DEFECT7374_FSM_SS_CONTROL_READ) {
+       if (dev->bug7734_patched) {
                /* reset, and enable pci */
                tmp = readl(&dev->regs->devinit) |
                    BIT(PCI_ENABLE) |
@@ -2093,10 +1932,6 @@ static void usb_reset(struct net2280 *dev)
 static void usb_reinit_228x(struct net2280 *dev)
 {
        u32     tmp;
-       int     init_dma;
-
-       /* use_dma changes are ignored till next device re-init */
-       init_dma = use_dma;
 
        /* basic endpoint init */
        for (tmp = 0; tmp < 7; tmp++) {
@@ -2108,8 +1943,7 @@ static void usb_reinit_228x(struct net2280 *dev)
 
                if (tmp > 0 && tmp <= 4) {
                        ep->fifo_size = 1024;
-                       if (init_dma)
-                               ep->dma = &dev->dma[tmp - 1];
+                       ep->dma = &dev->dma[tmp - 1];
                } else
                        ep->fifo_size = 64;
                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)
 {
-       int init_dma;
        int i;
        u32 tmp, val;
-       u32 fsmvalue;
        static const u32 ne[9] = { 0, 1, 2, 3, 4, 1, 2, 3, 4 };
        static const u32 ep_reg_addr[9] = { 0x00, 0xC0, 0x00, 0xC0, 0x00,
                                                0x00, 0xC0, 0x00, 0xC0 };
 
-       /* use_dma changes are ignored till next device re-init */
-       init_dma = use_dma;
-
        /* basic endpoint init */
        for (i = 0; i < dev->n_ep; i++) {
                struct net2280_ep *ep = &dev->ep[i];
@@ -2152,7 +1981,7 @@ static void usb_reinit_338x(struct net2280 *dev)
                ep->dev = dev;
                ep->num = i;
 
-               if (i > 0 && i <= 4 && init_dma)
+               if (i > 0 && i <= 4)
                        ep->dma = &dev->dma[i - 1];
 
                if (dev->enhanced_mode) {
@@ -2177,14 +2006,7 @@ static void usb_reinit_338x(struct net2280 *dev)
        dev->ep[0].stopped = 0;
 
        /* Link layer set up */
-       fsmvalue = get_idx_reg(dev->regs, SCRATCH) &
-                               (0xf << DEFECT7374_FSM_FIELD);
-
-       /* See if driver needs to set up for workaround: */
-       if (fsmvalue != DEFECT7374_FSM_SS_CONTROL_READ)
-               ep_info(dev, "%s: Defect 7374 FsmValue %08x\n",
-                                               __func__, fsmvalue);
-       else {
+       if (dev->bug7734_patched) {
                tmp = readl(&dev->usb_ext->usbctl2) &
                    ~(BIT(U1_ENABLE) | BIT(U2_ENABLE) | BIT(LTM_ENABLE));
                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)
 {
-       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) |
                       BIT(SET_EP_HIDE_STATUS_PHASE),
                       &dev->epregs[0].ep_rsp);
@@ -2382,16 +2197,12 @@ static int net2280_start(struct usb_gadget *_gadget,
        if (retval)
                goto err_func;
 
-       /* Enable force-full-speed testing mode, if desired */
-       if (full_speed && (dev->quirks & PLX_LEGACY))
-               writel(BIT(FORCE_FULL_SPEED_MODE), &dev->usb->xcvrdiag);
-
-       /* ... then enable host detection and ep0; and we're ready
+       /* enable host detection and ep0; and we're ready
         * for set_configuration as well as eventual disconnect.
         */
        net2280_led_active(dev, 1);
 
-       if (dev->quirks & PLX_SUPERSPEED)
+       if ((dev->quirks & PLX_SUPERSPEED) && !dev->bug7734_patched)
                defect7374_enable_data_eps_zero(dev);
 
        ep0_start(dev);
@@ -2444,10 +2255,6 @@ static int net2280_stop(struct usb_gadget *_gadget)
 
        net2280_led_active(dev, 0);
 
-       /* Disable full-speed test mode */
-       if (dev->quirks & PLX_LEGACY)
-               writel(0, &dev->usb->xcvrdiag);
-
        device_remove_file(&dev->pdev->dev, &dev_attr_function);
        device_remove_file(&dev->pdev->dev, &dev_attr_queues);
 
@@ -2478,10 +2285,10 @@ static void handle_ep_small(struct net2280_ep *ep)
        /* ack all, and handle what we care about */
        t = readl(&ep->regs->ep_stat);
        ep->irqs++;
-#if 0
+
        ep_vdbg(ep->dev, "%s ack ep_stat %08x, req %p\n",
-                       ep->ep.name, t, req ? &req->req : 0);
-#endif
+                       ep->ep.name, t, req ? &req->req : NULL);
+
        if (!ep->is_in || (ep->dev->quirks & PLX_2280))
                writel(t & ~BIT(NAK_OUT_PACKETS), &ep->regs->ep_stat);
        else
@@ -2717,6 +2524,7 @@ static void defect7374_workaround(struct net2280 *dev, struct usb_ctrlrequest r)
                 * run after the next USB connection.
                 */
                scratch |= DEFECT7374_FSM_NON_SS_CONTROL_READ;
+               dev->bug7734_patched = 1;
                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)) &&
                        (state <= (ACK_GOOD_MORE_ACKS_TO_COME << STATE))) {
                        scratch |= DEFECT7374_FSM_SS_CONTROL_READ;
+                       dev->bug7734_patched = 1;
                        break;
                }
 
@@ -2766,80 +2575,19 @@ restore_data_eps:
        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;
        u32 val;
        static const u32 ep_pl[9] = { 0, 3, 4, 7, 8, 2, 5, 6, 9 };
 
-       if (stall) {
-               writel(BIT(SET_ENDPOINT_HALT) |
-                      /* BIT(SET_NAK_PACKETS) | */
-                      BIT(CLEAR_CONTROL_STATUS_PHASE_HANDSHAKE),
-                      &ep->regs->ep_rsp);
-               ep->is_halt = 1;
-       } else {
-               if (dev->gadget.speed == USB_SPEED_SUPER) {
-                       /*
-                        * Workaround for SS SeqNum not cleared via
-                        * Endpoint Halt (Clear) bit. select endpoint
-                        */
-                       val = readl(&dev->plregs->pl_ep_ctrl);
-                       val = (val & ~0x1f) | ep_pl[ep->num];
-                       writel(val, &dev->plregs->pl_ep_ctrl);
-
-                       val |= BIT(SEQUENCE_NUMBER_RESET);
-                       writel(val, &dev->plregs->pl_ep_ctrl);
-               }
-               val = readl(&ep->regs->ep_rsp);
-               val |= BIT(CLEAR_ENDPOINT_HALT) |
-                       BIT(CLEAR_ENDPOINT_TOGGLE);
-               writel(val,
-                      /* | BIT(CLEAR_NAK_PACKETS),*/
-                      &ep->regs->ep_rsp);
-               ep->is_halt = 0;
-               val = readl(&ep->regs->ep_rsp);
-       }
-}
-
-static void ep_stdrsp(struct net2280_ep *ep, int value, int wedged)
-{
-       /* set/clear, then synch memory views with the device */
-       if (value) {
-               ep->stopped = 1;
-               if (ep->num == 0)
-                       ep->dev->protocol_stall = 1;
-               else {
-                       if (ep->dma)
-                               ep_stop_dma(ep);
-                       ep_stall(ep, true);
-               }
-
-               if (wedged)
-                       ep->wedged = 1;
-       } else {
-               ep->stopped = 0;
-               ep->wedged = 0;
-
-               ep_stall(ep, false);
+       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,
@@ -2863,7 +2611,7 @@ static void handle_stat0_irqs_superspeed(struct net2280 *dev,
                switch (r.bRequestType) {
                case (USB_DIR_IN | USB_TYPE_STANDARD | USB_RECIP_DEVICE):
                        status = dev->wakeup_enable ? 0x02 : 0x00;
-                       if (dev->selfpowered)
+                       if (dev->gadget.is_selfpowered)
                                status |= BIT(0);
                        status |= (dev->u1_enable << 2 | dev->u2_enable << 3 |
                                                        dev->ltm_enable << 4);
@@ -2940,7 +2688,12 @@ static void handle_stat0_irqs_superspeed(struct net2280 *dev,
                        if (w_value != USB_ENDPOINT_HALT)
                                goto do_stall3;
                        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)
                                restart_dma(e);
                        allow_status(ep);
@@ -2998,7 +2751,14 @@ static void handle_stat0_irqs_superspeed(struct net2280 *dev,
                        e = get_ep_by_addr(dev, w_index);
                        if (!e || (w_value != USB_ENDPOINT_HALT))
                                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);
                        break;
 
@@ -3026,7 +2786,7 @@ do_stall3:
                                r.bRequestType, r.bRequest, tmp);
                dev->protocol_stall = 1;
                /* TD 9.9 Halt Endpoint test. TD 9.22 Set feature test */
-               ep_stall(ep, true);
+               set_halt(ep);
        }
 
 next_endpoints3:
@@ -3091,9 +2851,7 @@ static void handle_stat0_irqs(struct net2280 *dev, u32 stat)
                }
                ep->stopped = 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)
                                tmp = BIT(FIFO_OVERFLOW) |
                                    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[1]);
 
-               if (dev->quirks & PLX_SUPERSPEED)
+               if ((dev->quirks & PLX_SUPERSPEED) && !dev->bug7734_patched)
                        defect7374_workaround(dev, u.r);
 
                tmp = 0;
@@ -3423,17 +3181,12 @@ static void handle_stat1_irqs(struct net2280 *dev, u32 stat)
                                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
                 * 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);
 
                /* 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);
-                       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++;
        }
@@ -3556,7 +3288,7 @@ static void net2280_remove(struct pci_dev *pdev)
        }
        if (dev->got_irq)
                free_irq(pdev->irq, dev);
-       if (use_msi && dev->quirks & PLX_SUPERSPEED)
+       if (dev->quirks & PLX_SUPERSPEED)
                pci_disable_msi(pdev);
        if (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;
        int                     retval, i;
 
-       if (!use_dma)
-               use_dma_chaining = 0;
-
        /* alloc, and start init */
        dev = kzalloc(sizeof(*dev), GFP_KERNEL);
        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) &
                                        (0xf << DEFECT7374_FSM_FIELD);
                /* 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);
-       } else{
+               } else
+                       dev->bug7734_patched = 0;
+       } else {
                dev->enhanced_mode = 0;
                dev->n_ep = 7;
                /* 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;
        }
 
-       if (use_msi && (dev->quirks & PLX_SUPERSPEED))
+       if (dev->quirks & PLX_SUPERSPEED)
                if (pci_enable_msi(pdev))
                        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, "irq %d, pci mem %p, chip rev %04x\n",
                        pdev->irq, base, dev->chiprev);
-       ep_info(dev, "version: " DRIVER_VERSION "; dma %s %s\n",
-               use_dma ? (use_dma_chaining ? "chaining" : "enabled")
-                       : "disabled",
+       ep_info(dev, "version: " DRIVER_VERSION "; %s\n",
                dev->enhanced_mode ? "enhanced mode" : "legacy mode");
        retval = device_create_file(&pdev->dev, &dev_attr_registers);
        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 */
        writel(0, &dev->usb->usbctl);
 
-       /* Disable full-speed test mode */
-       if (dev->quirks & PLX_LEGACY)
-               writel(0, &dev->usb->xcvrdiag);
 }
 
 
index 03f1524..ac8d5a2 100644 (file)
@@ -100,7 +100,6 @@ struct net2280_ep {
        dma_addr_t                              td_dma; /* of dummy */
        struct net2280                          *dev;
        unsigned long                           irqs;
-       unsigned is_halt:1, dma_started:1;
 
        /* analogous to a host-side qh */
        struct list_head                        queue;
@@ -126,7 +125,7 @@ static inline void allow_status(struct net2280_ep *ep)
        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
@@ -165,8 +164,8 @@ struct net2280 {
                                        u2_enable:1,
                                        ltm_enable:1,
                                        wakeup_enable:1,
-                                       selfpowered:1,
-                                       addressed_state:1;
+                                       addressed_state:1,
+                                       bug7734_patched:1;
        u16                             chiprev;
        int enhanced_mode;
        int n_ep;
@@ -356,23 +355,6 @@ static inline void start_out_naking(struct net2280_ep *ep)
        readl(&ep->regs->ep_rsp);
 }
 
-#ifdef DEBUG
-static inline void assert_out_naking(struct net2280_ep *ep, const char *where)
-{
-       u32     tmp = readl(&ep->regs->ep_stat);
-
-       if ((tmp & BIT(NAK_OUT_PACKETS)) == 0) {
-               ep_dbg(ep->dev, "%s %s %08x !NAK\n",
-                               ep->ep.name, where, tmp);
-               writel(BIT(SET_NAK_OUT_PACKETS),
-                       &ep->regs->ep_rsp);
-       }
-}
-#define ASSERT_OUT_NAKING(ep) assert_out_naking(ep, __func__)
-#else
-#define ASSERT_OUT_NAKING(ep) do {} while (0)
-#endif
-
 static inline void stop_out_naking(struct net2280_ep *ep)
 {
        u32     tmp;
index 288c087..e2fcdb8 100644 (file)
@@ -1171,6 +1171,7 @@ omap_set_selfpowered(struct usb_gadget *gadget, int is_selfpowered)
        unsigned long   flags;
        u16             syscon1;
 
+       gadget->is_selfpowered = (is_selfpowered != 0);
        udc = container_of(gadget, struct omap_udc, gadget);
        spin_lock_irqsave(&udc->lock, flags);
        syscon1 = omap_readw(UDC_SYSCON1);
index 1c7379a..613547f 100644 (file)
@@ -1161,6 +1161,7 @@ static int pch_udc_pcd_selfpowered(struct usb_gadget *gadget, int value)
 
        if (!gadget)
                return -EINVAL;
+       gadget->is_selfpowered = (value != 0);
        dev = container_of(gadget, struct pch_udc_dev, gadget);
        if (value)
                pch_udc_set_selfpowered(dev);
index 8550b2d..f6cbe66 100644 (file)
@@ -1272,7 +1272,6 @@ static int pxa25x_udc_start(struct usb_gadget *g,
                        goto bind_fail;
        }
 
-       pullup(dev);
        dump_state(dev);
        return 0;
 bind_fail:
@@ -1339,7 +1338,6 @@ static int pxa25x_udc_stop(struct usb_gadget*g)
 
        local_irq_disable();
        dev->pullup = 0;
-       pullup(dev);
        stop_activity(dev, NULL);
        local_irq_enable();
 
index c61a896..6a855fc 100644 (file)
@@ -1809,7 +1809,6 @@ static int pxa27x_udc_start(struct usb_gadget *g,
 
        /* first hook up the driver ... */
        udc->driver = driver;
-       dplus_pullup(udc, 1);
 
        if (!IS_ERR_OR_NULL(udc->transceiver)) {
                retval = otg_set_peripheral(udc->transceiver->otg,
@@ -1862,7 +1861,6 @@ static int pxa27x_udc_stop(struct usb_gadget *g)
 
        stop_activity(udc, NULL);
        udc_disable(udc);
-       dplus_pullup(udc, 0);
 
        udc->driver = NULL;
 
index 06870da..2495fe9 100644 (file)
@@ -1803,6 +1803,7 @@ static int r8a66597_set_selfpowered(struct usb_gadget *gadget, int is_self)
 {
        struct r8a66597 *r8a66597 = gadget_to_r8a66597(gadget);
 
+       gadget->is_selfpowered = (is_self != 0);
        if (is_self)
                r8a66597->device_status |= 1 << USB_DEVICE_SELF_POWERED;
        else
index 824cf12..b808951 100644 (file)
@@ -238,14 +238,6 @@ static inline void s3c2410_udc_set_ep0_de_out(void __iomem *base)
                        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)
 {
        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)
 {
        int tmp;
@@ -1454,6 +1434,7 @@ static int s3c2410_udc_set_selfpowered(struct usb_gadget *gadget, int value)
 
        dprintk(DEBUG_NORMAL, "%s()\n", __func__);
 
+       gadget->is_selfpowered = (value != 0);
        if (value)
                udc->devstatus |= (1 << USB_DEVICE_SELF_POWERED);
        else
index e31d574..5a81cb0 100644 (file)
@@ -564,6 +564,7 @@ static USB_UDC_ATTR(is_a_peripheral);
 static USB_UDC_ATTR(b_hnp_enable);
 static USB_UDC_ATTR(a_hnp_support);
 static USB_UDC_ATTR(a_alt_hnp_support);
+static USB_UDC_ATTR(is_selfpowered);
 
 static struct attribute *usb_udc_attrs[] = {
        &dev_attr_srp.attr,
@@ -577,6 +578,7 @@ static struct attribute *usb_udc_attrs[] = {
        &dev_attr_b_hnp_enable.attr,
        &dev_attr_a_hnp_support.attr,
        &dev_attr_a_alt_hnp_support.attr,
+       &dev_attr_is_selfpowered.attr,
        NULL,
 };
 
index 688ba86..5ad60e4 100644 (file)
@@ -331,20 +331,6 @@ config USB_ISP116X_HCD
          To compile this driver as a module, choose M here: the
          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
        tristate "ISP1362 HCD support"
        ---help---
index d6216a4..65b0b6a 100644 (file)
@@ -5,8 +5,6 @@
 # tell define_trace.h where to find the xhci trace header
 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-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_U132_HCD)     += u132-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_IMX21_HCD)    += imx21-hcd.o
 obj-$(CONFIG_USB_FSL_MPH_DR_OF)        += fsl-mph-dr-of.o
diff --git a/drivers/usb/host/isp1760-hcd.h b/drivers/usb/host/isp1760-hcd.h
deleted file mode 100644 (file)
index 33dc79c..0000000
+++ /dev/null
@@ -1,208 +0,0 @@
-#ifndef _ISP1760_HCD_H_
-#define _ISP1760_HCD_H_
-
-/* exports for if */
-struct usb_hcd *isp1760_register(phys_addr_t res_start, resource_size_t res_len,
-                                int irq, unsigned long irqflags,
-                                int rst_gpio,
-                                struct device *dev, const char *busname,
-                                unsigned int devflags);
-int init_kmem_once(void);
-void deinit_kmem_cache(void);
-
-/* EHCI capability registers */
-#define HC_CAPLENGTH           0x00
-#define HC_HCSPARAMS           0x04
-#define HC_HCCPARAMS           0x08
-
-/* EHCI operational registers */
-#define HC_USBCMD              0x20
-#define HC_USBSTS              0x24
-#define HC_FRINDEX             0x2c
-#define HC_CONFIGFLAG          0x60
-#define HC_PORTSC1             0x64
-#define HC_ISO_PTD_DONEMAP_REG 0x130
-#define HC_ISO_PTD_SKIPMAP_REG 0x134
-#define HC_ISO_PTD_LASTPTD_REG 0x138
-#define HC_INT_PTD_DONEMAP_REG 0x140
-#define HC_INT_PTD_SKIPMAP_REG 0x144
-#define HC_INT_PTD_LASTPTD_REG 0x148
-#define HC_ATL_PTD_DONEMAP_REG 0x150
-#define HC_ATL_PTD_SKIPMAP_REG 0x154
-#define HC_ATL_PTD_LASTPTD_REG 0x158
-
-/* Configuration Register */
-#define HC_HW_MODE_CTRL                0x300
-#define ALL_ATX_RESET          (1 << 31)
-#define HW_ANA_DIGI_OC         (1 << 15)
-#define HW_DATA_BUS_32BIT      (1 << 8)
-#define HW_DACK_POL_HIGH       (1 << 6)
-#define HW_DREQ_POL_HIGH       (1 << 5)
-#define HW_INTR_HIGH_ACT       (1 << 2)
-#define HW_INTR_EDGE_TRIG      (1 << 1)
-#define HW_GLOBAL_INTR_EN      (1 << 0)
-
-#define HC_CHIP_ID_REG         0x304
-#define HC_SCRATCH_REG         0x308
-
-#define HC_RESET_REG           0x30c
-#define SW_RESET_RESET_HC      (1 << 1)
-#define SW_RESET_RESET_ALL     (1 << 0)
-
-#define HC_BUFFER_STATUS_REG   0x334
-#define ISO_BUF_FILL           (1 << 2)
-#define INT_BUF_FILL           (1 << 1)
-#define ATL_BUF_FILL           (1 << 0)
-
-#define HC_MEMORY_REG          0x33c
-#define ISP_BANK(x)            ((x) << 16)
-
-#define HC_PORT1_CTRL          0x374
-#define PORT1_POWER            (3 << 3)
-#define PORT1_INIT1            (1 << 7)
-#define PORT1_INIT2            (1 << 23)
-#define HW_OTG_CTRL_SET                0x374
-#define HW_OTG_CTRL_CLR                0x376
-
-/* Interrupt Register */
-#define HC_INTERRUPT_REG       0x310
-
-#define HC_INTERRUPT_ENABLE    0x314
-#define HC_ISO_INT             (1 << 9)
-#define HC_ATL_INT             (1 << 8)
-#define HC_INTL_INT            (1 << 7)
-#define HC_EOT_INT             (1 << 3)
-#define HC_SOT_INT             (1 << 1)
-#define INTERRUPT_ENABLE_MASK  (HC_INTL_INT | HC_ATL_INT)
-
-#define HC_ISO_IRQ_MASK_OR_REG 0x318
-#define HC_INT_IRQ_MASK_OR_REG 0x31C
-#define HC_ATL_IRQ_MASK_OR_REG 0x320
-#define HC_ISO_IRQ_MASK_AND_REG        0x324
-#define HC_INT_IRQ_MASK_AND_REG        0x328
-#define HC_ATL_IRQ_MASK_AND_REG        0x32C
-
-/* urb state*/
-#define DELETE_URB             (0x0008)
-#define NO_TRANSFER_ACTIVE     (0xffffffff)
-
-/* Philips Proprietary Transfer Descriptor (PTD) */
-typedef __u32 __bitwise __dw;
-struct ptd {
-       __dw dw0;
-       __dw dw1;
-       __dw dw2;
-       __dw dw3;
-       __dw dw4;
-       __dw dw5;
-       __dw dw6;
-       __dw dw7;
-};
-#define PTD_OFFSET             0x0400
-#define ISO_PTD_OFFSET         0x0400
-#define INT_PTD_OFFSET         0x0800
-#define ATL_PTD_OFFSET         0x0c00
-#define PAYLOAD_OFFSET         0x1000
-
-struct slotinfo {
-       struct isp1760_qh *qh;
-       struct isp1760_qtd *qtd;
-       unsigned long timestamp;
-};
-
-
-typedef void (packet_enqueue)(struct usb_hcd *hcd, struct isp1760_qh *qh,
-               struct isp1760_qtd *qtd);
-
-/*
- * Device flags that can vary from board to board.  All of these
- * indicate the most "atypical" case, so that a devflags of 0 is
- * a sane default configuration.
- */
-#define ISP1760_FLAG_BUS_WIDTH_16      0x00000002 /* 16-bit data bus width */
-#define ISP1760_FLAG_OTG_EN            0x00000004 /* Port 1 supports OTG */
-#define ISP1760_FLAG_ANALOG_OC         0x00000008 /* Analog overcurrent */
-#define ISP1760_FLAG_DACK_POL_HIGH     0x00000010 /* DACK active high */
-#define ISP1760_FLAG_DREQ_POL_HIGH     0x00000020 /* DREQ active high */
-#define ISP1760_FLAG_ISP1761           0x00000040 /* Chip is ISP1761 */
-#define ISP1760_FLAG_INTR_POL_HIGH     0x00000080 /* Interrupt polarity active high */
-#define ISP1760_FLAG_INTR_EDGE_TRIG    0x00000100 /* Interrupt edge triggered */
-#define ISP1760_FLAG_RESET_ACTIVE_HIGH 0x80000000 /* RESET GPIO active high */
-
-/* chip memory management */
-struct memory_chunk {
-       unsigned int start;
-       unsigned int size;
-       unsigned int free;
-};
-
-/*
- * 60kb divided in:
- * - 32 blocks @ 256  bytes
- * - 20 blocks @ 1024 bytes
- * -  4 blocks @ 8192 bytes
- */
-
-#define BLOCK_1_NUM 32
-#define BLOCK_2_NUM 20
-#define BLOCK_3_NUM 4
-
-#define BLOCK_1_SIZE 256
-#define BLOCK_2_SIZE 1024
-#define BLOCK_3_SIZE 8192
-#define BLOCKS (BLOCK_1_NUM + BLOCK_2_NUM + BLOCK_3_NUM)
-#define MAX_PAYLOAD_SIZE BLOCK_3_SIZE
-#define PAYLOAD_AREA_SIZE 0xf000
-
-/* ATL */
-/* DW0 */
-#define DW0_VALID_BIT                  1
-#define FROM_DW0_VALID(x)              ((x) & 0x01)
-#define TO_DW0_LENGTH(x)               (((u32) x) << 3)
-#define TO_DW0_MAXPACKET(x)            (((u32) x) << 18)
-#define TO_DW0_MULTI(x)                        (((u32) x) << 29)
-#define TO_DW0_ENDPOINT(x)             (((u32) x) << 31)
-/* DW1 */
-#define TO_DW1_DEVICE_ADDR(x)          (((u32) x) << 3)
-#define TO_DW1_PID_TOKEN(x)            (((u32) x) << 10)
-#define DW1_TRANS_BULK                 ((u32) 2 << 12)
-#define DW1_TRANS_INT                  ((u32) 3 << 12)
-#define DW1_TRANS_SPLIT                        ((u32) 1 << 14)
-#define DW1_SE_USB_LOSPEED             ((u32) 2 << 16)
-#define TO_DW1_PORT_NUM(x)             (((u32) x) << 18)
-#define TO_DW1_HUB_NUM(x)              (((u32) x) << 25)
-/* DW2 */
-#define TO_DW2_DATA_START_ADDR(x)      (((u32) x) << 8)
-#define TO_DW2_RL(x)                   ((x) << 25)
-#define FROM_DW2_RL(x)                 (((x) >> 25) & 0xf)
-/* DW3 */
-#define FROM_DW3_NRBYTESTRANSFERRED(x)         ((x) & 0x7fff)
-#define FROM_DW3_SCS_NRBYTESTRANSFERRED(x)     ((x) & 0x07ff)
-#define TO_DW3_NAKCOUNT(x)             ((x) << 19)
-#define FROM_DW3_NAKCOUNT(x)           (((x) >> 19) & 0xf)
-#define TO_DW3_CERR(x)                 ((x) << 23)
-#define FROM_DW3_CERR(x)               (((x) >> 23) & 0x3)
-#define TO_DW3_DATA_TOGGLE(x)          ((x) << 25)
-#define FROM_DW3_DATA_TOGGLE(x)                (((x) >> 25) & 0x1)
-#define TO_DW3_PING(x)                 ((x) << 26)
-#define FROM_DW3_PING(x)               (((x) >> 26) & 0x1)
-#define DW3_ERROR_BIT                  (1 << 28)
-#define DW3_BABBLE_BIT                 (1 << 29)
-#define DW3_HALT_BIT                   (1 << 30)
-#define DW3_ACTIVE_BIT                 (1 << 31)
-#define FROM_DW3_ACTIVE(x)             (((x) >> 31) & 0x01)
-
-#define INT_UNDERRUN                   (1 << 2)
-#define INT_BABBLE                     (1 << 1)
-#define INT_EXACT                      (1 << 0)
-
-#define SETUP_PID      (2)
-#define IN_PID         (1)
-#define OUT_PID                (0)
-
-/* Errata 1 */
-#define RL_COUNTER     (0)
-#define NAK_COUNTER    (0)
-#define ERR_COUNTER    (2)
-
-#endif /* _ISP1760_HCD_H_ */
diff --git a/drivers/usb/host/isp1760-if.c b/drivers/usb/host/isp1760-if.c
deleted file mode 100644 (file)
index 09254a4..0000000
+++ /dev/null
@@ -1,477 +0,0 @@
-/*
- * Glue code for the ISP1760 driver and bus
- * Currently there is support for
- * - OpenFirmware
- * - PCI
- * - PDEV (generic platform device centralized driver model)
- *
- * (c) 2007 Sebastian Siewior <bigeasy@linutronix.de>
- *
- */
-
-#include <linux/usb.h>
-#include <linux/io.h>
-#include <linux/module.h>
-#include <linux/platform_device.h>
-#include <linux/usb/isp1760.h>
-#include <linux/usb/hcd.h>
-
-#include "isp1760-hcd.h"
-
-#if defined(CONFIG_OF) && defined(CONFIG_OF_IRQ)
-#include <linux/slab.h>
-#include <linux/of.h>
-#include <linux/of_platform.h>
-#include <linux/of_address.h>
-#include <linux/of_irq.h>
-#include <linux/of_gpio.h>
-#endif
-
-#ifdef CONFIG_PCI
-#include <linux/pci.h>
-#endif
-
-#if defined(CONFIG_OF) && defined(CONFIG_OF_IRQ)
-struct isp1760 {
-       struct usb_hcd *hcd;
-       int rst_gpio;
-};
-
-static int of_isp1760_probe(struct platform_device *dev)
-{
-       struct isp1760 *drvdata;
-       struct device_node *dp = dev->dev.of_node;
-       struct resource *res;
-       struct resource memory;
-       int virq;
-       resource_size_t res_len;
-       int ret;
-       unsigned int devflags = 0;
-       enum of_gpio_flags gpio_flags;
-       u32 bus_width = 0;
-
-       drvdata = kzalloc(sizeof(*drvdata), GFP_KERNEL);
-       if (!drvdata)
-               return -ENOMEM;
-
-       ret = of_address_to_resource(dp, 0, &memory);
-       if (ret) {
-               ret = -ENXIO;
-               goto free_data;
-       }
-
-       res_len = resource_size(&memory);
-
-       res = request_mem_region(memory.start, res_len, dev_name(&dev->dev));
-       if (!res) {
-               ret = -EBUSY;
-               goto free_data;
-       }
-
-       virq = irq_of_parse_and_map(dp, 0);
-       if (!virq) {
-               ret = -ENODEV;
-               goto release_reg;
-       }
-
-       if (of_device_is_compatible(dp, "nxp,usb-isp1761"))
-               devflags |= ISP1760_FLAG_ISP1761;
-
-       /* Some systems wire up only 16 of the 32 data lines */
-       of_property_read_u32(dp, "bus-width", &bus_width);
-       if (bus_width == 16)
-               devflags |= ISP1760_FLAG_BUS_WIDTH_16;
-
-       if (of_get_property(dp, "port1-otg", NULL) != NULL)
-               devflags |= ISP1760_FLAG_OTG_EN;
-
-       if (of_get_property(dp, "analog-oc", NULL) != NULL)
-               devflags |= ISP1760_FLAG_ANALOG_OC;
-
-       if (of_get_property(dp, "dack-polarity", NULL) != NULL)
-               devflags |= ISP1760_FLAG_DACK_POL_HIGH;
-
-       if (of_get_property(dp, "dreq-polarity", NULL) != NULL)
-               devflags |= ISP1760_FLAG_DREQ_POL_HIGH;
-
-       drvdata->rst_gpio = of_get_gpio_flags(dp, 0, &gpio_flags);
-       if (gpio_is_valid(drvdata->rst_gpio)) {
-               ret = gpio_request(drvdata->rst_gpio, dev_name(&dev->dev));
-               if (!ret) {
-                       if (!(gpio_flags & OF_GPIO_ACTIVE_LOW)) {
-                               devflags |= ISP1760_FLAG_RESET_ACTIVE_HIGH;
-                               gpio_direction_output(drvdata->rst_gpio, 0);
-                       } else {
-                               gpio_direction_output(drvdata->rst_gpio, 1);
-                       }
-               } else {
-                       drvdata->rst_gpio = ret;
-               }
-       }
-
-       drvdata->hcd = isp1760_register(memory.start, res_len, virq,
-                                       IRQF_SHARED, drvdata->rst_gpio,
-                                       &dev->dev, dev_name(&dev->dev),
-                                       devflags);
-       if (IS_ERR(drvdata->hcd)) {
-               ret = PTR_ERR(drvdata->hcd);
-               goto free_gpio;
-       }
-
-       platform_set_drvdata(dev, drvdata);
-       return ret;
-
-free_gpio:
-       if (gpio_is_valid(drvdata->rst_gpio))
-               gpio_free(drvdata->rst_gpio);
-release_reg:
-       release_mem_region(memory.start, res_len);
-free_data:
-       kfree(drvdata);
-       return ret;
-}
-
-static int of_isp1760_remove(struct platform_device *dev)
-{
-       struct isp1760 *drvdata = platform_get_drvdata(dev);
-
-       usb_remove_hcd(drvdata->hcd);
-       iounmap(drvdata->hcd->regs);
-       release_mem_region(drvdata->hcd->rsrc_start, drvdata->hcd->rsrc_len);
-       usb_put_hcd(drvdata->hcd);
-
-       if (gpio_is_valid(drvdata->rst_gpio))
-               gpio_free(drvdata->rst_gpio);
-
-       kfree(drvdata);
-       return 0;
-}
-
-static const struct of_device_id of_isp1760_match[] = {
-       {
-               .compatible = "nxp,usb-isp1760",
-       },
-       {
-               .compatible = "nxp,usb-isp1761",
-       },
-       { },
-};
-MODULE_DEVICE_TABLE(of, of_isp1760_match);
-
-static struct platform_driver isp1760_of_driver = {
-       .driver = {
-               .name = "nxp-isp1760",
-               .of_match_table = of_isp1760_match,
-       },
-       .probe          = of_isp1760_probe,
-       .remove         = of_isp1760_remove,
-};
-#endif
-
-#ifdef CONFIG_PCI
-static int isp1761_pci_probe(struct pci_dev *dev,
-               const struct pci_device_id *id)
-{
-       u8 latency, limit;
-       __u32 reg_data;
-       int retry_count;
-       struct usb_hcd *hcd;
-       unsigned int devflags = 0;
-       int ret_status = 0;
-
-       resource_size_t pci_mem_phy0;
-       resource_size_t memlength;
-
-       u8 __iomem *chip_addr;
-       u8 __iomem *iobase;
-       resource_size_t nxp_pci_io_base;
-       resource_size_t iolength;
-
-       if (usb_disabled())
-               return -ENODEV;
-
-       if (pci_enable_device(dev) < 0)
-               return -ENODEV;
-
-       if (!dev->irq)
-               return -ENODEV;
-
-       /* Grab the PLX PCI mem maped port start address we need  */
-       nxp_pci_io_base = pci_resource_start(dev, 0);
-       iolength = pci_resource_len(dev, 0);
-
-       if (!request_mem_region(nxp_pci_io_base, iolength, "ISP1761 IO MEM")) {
-               printk(KERN_ERR "request region #1\n");
-               return -EBUSY;
-       }
-
-       iobase = ioremap_nocache(nxp_pci_io_base, iolength);
-       if (!iobase) {
-               printk(KERN_ERR "ioremap #1\n");
-               ret_status = -ENOMEM;
-               goto cleanup1;
-       }
-       /* Grab the PLX PCI shared memory of the ISP 1761 we need  */
-       pci_mem_phy0 = pci_resource_start(dev, 3);
-       memlength = pci_resource_len(dev, 3);
-       if (memlength < 0xffff) {
-               printk(KERN_ERR "memory length for this resource is wrong\n");
-               ret_status = -ENOMEM;
-               goto cleanup2;
-       }
-
-       if (!request_mem_region(pci_mem_phy0, memlength, "ISP-PCI")) {
-               printk(KERN_ERR "host controller already in use\n");
-               ret_status = -EBUSY;
-               goto cleanup2;
-       }
-
-       /* map available memory */
-       chip_addr = ioremap_nocache(pci_mem_phy0,memlength);
-       if (!chip_addr) {
-               printk(KERN_ERR "Error ioremap failed\n");
-               ret_status = -ENOMEM;
-               goto cleanup3;
-       }
-
-       /* bad pci latencies can contribute to overruns */
-       pci_read_config_byte(dev, PCI_LATENCY_TIMER, &latency);
-       if (latency) {
-               pci_read_config_byte(dev, PCI_MAX_LAT, &limit);
-               if (limit && limit < latency)
-                       pci_write_config_byte(dev, PCI_LATENCY_TIMER, limit);
-       }
-
-       /* Try to check whether we can access Scratch Register of
-        * Host Controller or not. The initial PCI access is retried until
-        * local init for the PCI bridge is completed
-        */
-       retry_count = 20;
-       reg_data = 0;
-       while ((reg_data != 0xFACE) && retry_count) {
-               /*by default host is in 16bit mode, so
-                * io operations at this stage must be 16 bit
-                * */
-               writel(0xface, chip_addr + HC_SCRATCH_REG);
-               udelay(100);
-               reg_data = readl(chip_addr + HC_SCRATCH_REG) & 0x0000ffff;
-               retry_count--;
-       }
-
-       iounmap(chip_addr);
-
-       /* Host Controller presence is detected by writing to scratch register
-        * and reading back and checking the contents are same or not
-        */
-       if (reg_data != 0xFACE) {
-               dev_err(&dev->dev, "scratch register mismatch %x\n", reg_data);
-               ret_status = -ENOMEM;
-               goto cleanup3;
-       }
-
-       pci_set_master(dev);
-
-       /* configure PLX PCI chip to pass interrupts */
-#define PLX_INT_CSR_REG 0x68
-       reg_data = readl(iobase + PLX_INT_CSR_REG);
-       reg_data |= 0x900;
-       writel(reg_data, iobase + PLX_INT_CSR_REG);
-
-       dev->dev.dma_mask = NULL;
-       hcd = isp1760_register(pci_mem_phy0, memlength, dev->irq,
-               IRQF_SHARED, -ENOENT, &dev->dev, dev_name(&dev->dev),
-               devflags);
-       if (IS_ERR(hcd)) {
-               ret_status = -ENODEV;
-               goto cleanup3;
-       }
-
-       /* done with PLX IO access */
-       iounmap(iobase);
-       release_mem_region(nxp_pci_io_base, iolength);
-
-       pci_set_drvdata(dev, hcd);
-       return 0;
-
-cleanup3:
-       release_mem_region(pci_mem_phy0, memlength);
-cleanup2:
-       iounmap(iobase);
-cleanup1:
-       release_mem_region(nxp_pci_io_base, iolength);
-       return ret_status;
-}
-
-static void isp1761_pci_remove(struct pci_dev *dev)
-{
-       struct usb_hcd *hcd;
-
-       hcd = pci_get_drvdata(dev);
-
-       usb_remove_hcd(hcd);
-       iounmap(hcd->regs);
-       release_mem_region(hcd->rsrc_start, hcd->rsrc_len);
-       usb_put_hcd(hcd);
-
-       pci_disable_device(dev);
-}
-
-static void isp1761_pci_shutdown(struct pci_dev *dev)
-{
-       printk(KERN_ERR "ips1761_pci_shutdown\n");
-}
-
-static const struct pci_device_id isp1760_plx [] = {
-       {
-               .class          = PCI_CLASS_BRIDGE_OTHER << 8,
-               .class_mask     = ~0,
-               .vendor         = PCI_VENDOR_ID_PLX,
-               .device         = 0x5406,
-               .subvendor      = PCI_VENDOR_ID_PLX,
-               .subdevice      = 0x9054,
-       },
-       { }
-};
-MODULE_DEVICE_TABLE(pci, isp1760_plx);
-
-static struct pci_driver isp1761_pci_driver = {
-       .name =         "isp1760",
-       .id_table =     isp1760_plx,
-       .probe =        isp1761_pci_probe,
-       .remove =       isp1761_pci_remove,
-       .shutdown =     isp1761_pci_shutdown,
-};
-#endif
-
-static int isp1760_plat_probe(struct platform_device *pdev)
-{
-       int ret = 0;
-       struct usb_hcd *hcd;
-       struct resource *mem_res;
-       struct resource *irq_res;
-       resource_size_t mem_size;
-       struct isp1760_platform_data *priv = dev_get_platdata(&pdev->dev);
-       unsigned int devflags = 0;
-       unsigned long irqflags = IRQF_SHARED;
-
-       mem_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-       if (!mem_res) {
-               pr_warning("isp1760: Memory resource not available\n");
-               ret = -ENODEV;
-               goto out;
-       }
-       mem_size = resource_size(mem_res);
-       if (!request_mem_region(mem_res->start, mem_size, "isp1760")) {
-               pr_warning("isp1760: Cannot reserve the memory resource\n");
-               ret = -EBUSY;
-               goto out;
-       }
-
-       irq_res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
-       if (!irq_res) {
-               pr_warning("isp1760: IRQ resource not available\n");
-               ret = -ENODEV;
-               goto cleanup;
-       }
-
-       irqflags |= irq_res->flags & IRQF_TRIGGER_MASK;
-
-       if (priv) {
-               if (priv->is_isp1761)
-                       devflags |= ISP1760_FLAG_ISP1761;
-               if (priv->bus_width_16)
-                       devflags |= ISP1760_FLAG_BUS_WIDTH_16;
-               if (priv->port1_otg)
-                       devflags |= ISP1760_FLAG_OTG_EN;
-               if (priv->analog_oc)
-                       devflags |= ISP1760_FLAG_ANALOG_OC;
-               if (priv->dack_polarity_high)
-                       devflags |= ISP1760_FLAG_DACK_POL_HIGH;
-               if (priv->dreq_polarity_high)
-                       devflags |= ISP1760_FLAG_DREQ_POL_HIGH;
-       }
-
-       hcd = isp1760_register(mem_res->start, mem_size, irq_res->start,
-                              irqflags, -ENOENT,
-                              &pdev->dev, dev_name(&pdev->dev), devflags);
-
-       platform_set_drvdata(pdev, hcd);
-
-       if (IS_ERR(hcd)) {
-               pr_warning("isp1760: Failed to register the HCD device\n");
-               ret = -ENODEV;
-               goto cleanup;
-       }
-
-       pr_info("ISP1760 USB device initialised\n");
-       return ret;
-
-cleanup:
-       release_mem_region(mem_res->start, mem_size);
-out:
-       return ret;
-}
-
-static int isp1760_plat_remove(struct platform_device *pdev)
-{
-       struct resource *mem_res;
-       resource_size_t mem_size;
-       struct usb_hcd *hcd = platform_get_drvdata(pdev);
-
-       usb_remove_hcd(hcd);
-
-       mem_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-       mem_size = resource_size(mem_res);
-       release_mem_region(mem_res->start, mem_size);
-
-       usb_put_hcd(hcd);
-
-       return 0;
-}
-
-static struct platform_driver isp1760_plat_driver = {
-       .probe  = isp1760_plat_probe,
-       .remove = isp1760_plat_remove,
-       .driver = {
-               .name   = "isp1760",
-       },
-};
-
-static int __init isp1760_init(void)
-{
-       int ret, any_ret = -ENODEV;
-
-       init_kmem_once();
-
-       ret = platform_driver_register(&isp1760_plat_driver);
-       if (!ret)
-               any_ret = 0;
-#if defined(CONFIG_OF) && defined(CONFIG_OF_IRQ)
-       ret = platform_driver_register(&isp1760_of_driver);
-       if (!ret)
-               any_ret = 0;
-#endif
-#ifdef CONFIG_PCI
-       ret = pci_register_driver(&isp1761_pci_driver);
-       if (!ret)
-               any_ret = 0;
-#endif
-
-       if (any_ret)
-               deinit_kmem_cache();
-       return any_ret;
-}
-module_init(isp1760_init);
-
-static void __exit isp1760_exit(void)
-{
-       platform_driver_unregister(&isp1760_plat_driver);
-#if defined(CONFIG_OF) && defined(CONFIG_OF_IRQ)
-       platform_driver_unregister(&isp1760_of_driver);
-#endif
-#ifdef CONFIG_PCI
-       pci_unregister_driver(&isp1761_pci_driver);
-#endif
-       deinit_kmem_cache();
-}
-module_exit(isp1760_exit);
diff --git a/drivers/usb/isp1760/Kconfig b/drivers/usb/isp1760/Kconfig
new file mode 100644 (file)
index 0000000..c94b7d9
--- /dev/null
@@ -0,0 +1,59 @@
+config USB_ISP1760
+       tristate "NXP ISP 1760/1761 support"
+       depends on USB || USB_GADGET
+       help
+         Say Y or M here if your system as an ISP1760 USB host controller
+         or an ISP1761 USB dual-role controller.
+
+         This driver does not support isochronous transfers or OTG.
+         This USB controller is usually attached to a non-DMA-Master
+         capable bus. NXP's eval kit brings this chip on PCI card
+         where the chip itself is behind a PLB to simulate such
+         a bus.
+
+         To compile this driver as a module, choose M here: the
+         module will be called isp1760.
+
+config USB_ISP1760_HCD
+       bool
+
+config USB_ISP1761_UDC
+       bool
+
+if USB_ISP1760
+
+choice
+       bool "ISP1760 Mode Selection"
+       default USB_ISP1760_DUAL_ROLE if (USB && USB_GADGET)
+       default USB_ISP1760_HOST_ROLE if (USB && !USB_GADGET)
+       default USB_ISP1760_GADGET_ROLE if (!USB && USB_GADGET)
+
+config USB_ISP1760_HOST_ROLE
+       bool "Host only mode"
+       depends on USB=y || USB=USB_ISP1760
+       select USB_ISP1760_HCD
+       help
+         Select this if you want to use the ISP1760 in host mode only. The
+         gadget function will be disabled.
+
+config USB_ISP1760_GADGET_ROLE
+       bool "Gadget only mode"
+       depends on USB_GADGET=y || USB_GADGET=USB_ISP1760
+       select USB_ISP1761_UDC
+       help
+         Select this if you want to use the ISP1760 in peripheral mode only.
+         The host function will be disabled.
+
+config USB_ISP1760_DUAL_ROLE
+       bool "Dual Role mode"
+       depends on USB=y || USB=USB_ISP1760
+       depends on USB_GADGET=y || USB_GADGET=USB_ISP1760
+       select USB_ISP1760_HCD
+       select USB_ISP1761_UDC
+       help
+         Select this if you want to use the ISP1760 in both host and
+         peripheral modes.
+
+endchoice
+
+endif
diff --git a/drivers/usb/isp1760/Makefile b/drivers/usb/isp1760/Makefile
new file mode 100644 (file)
index 0000000..2b74107
--- /dev/null
@@ -0,0 +1,5 @@
+isp1760-y := isp1760-core.o isp1760-if.o
+isp1760-$(CONFIG_USB_ISP1760_HCD) += isp1760-hcd.o
+isp1760-$(CONFIG_USB_ISP1761_UDC) += isp1760-udc.o
+
+obj-$(CONFIG_USB_ISP1760)      += isp1760.o
diff --git a/drivers/usb/isp1760/isp1760-core.c b/drivers/usb/isp1760/isp1760-core.c
new file mode 100644 (file)
index 0000000..b982755
--- /dev/null
@@ -0,0 +1,177 @@
+/*
+ * Driver for the NXP ISP1760 chip
+ *
+ * Copyright 2014 Laurent Pinchart
+ * Copyright 2007 Sebastian Siewior
+ *
+ * Contacts:
+ *     Sebastian Siewior <bigeasy@linutronix.de>
+ *     Laurent Pinchart <laurent.pinchart@ideasonboard.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ */
+
+#include <linux/delay.h>
+#include <linux/gpio/consumer.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/usb.h>
+
+#include "isp1760-core.h"
+#include "isp1760-hcd.h"
+#include "isp1760-regs.h"
+#include "isp1760-udc.h"
+
+static void isp1760_init_core(struct isp1760_device *isp)
+{
+       u32 otgctrl;
+       u32 hwmode;
+
+       /* Low-level chip reset */
+       if (isp->rst_gpio) {
+               gpiod_set_value_cansleep(isp->rst_gpio, 1);
+               mdelay(50);
+               gpiod_set_value_cansleep(isp->rst_gpio, 0);
+       }
+
+       /*
+        * Reset the host controller, including the CPU interface
+        * configuration.
+        */
+       isp1760_write32(isp->regs, HC_RESET_REG, SW_RESET_RESET_ALL);
+       msleep(100);
+
+       /* Setup HW Mode Control: This assumes a level active-low interrupt */
+       hwmode = HW_DATA_BUS_32BIT;
+
+       if (isp->devflags & ISP1760_FLAG_BUS_WIDTH_16)
+               hwmode &= ~HW_DATA_BUS_32BIT;
+       if (isp->devflags & ISP1760_FLAG_ANALOG_OC)
+               hwmode |= HW_ANA_DIGI_OC;
+       if (isp->devflags & ISP1760_FLAG_DACK_POL_HIGH)
+               hwmode |= HW_DACK_POL_HIGH;
+       if (isp->devflags & ISP1760_FLAG_DREQ_POL_HIGH)
+               hwmode |= HW_DREQ_POL_HIGH;
+       if (isp->devflags & ISP1760_FLAG_INTR_POL_HIGH)
+               hwmode |= HW_INTR_HIGH_ACT;
+       if (isp->devflags & ISP1760_FLAG_INTR_EDGE_TRIG)
+               hwmode |= HW_INTR_EDGE_TRIG;
+
+       /*
+        * The ISP1761 has a dedicated DC IRQ line but supports sharing the HC
+        * IRQ line for both the host and device controllers. Hardcode IRQ
+        * sharing for now and disable the DC interrupts globally to avoid
+        * spurious interrupts during HCD registration.
+        */
+       if (isp->devflags & ISP1760_FLAG_ISP1761) {
+               isp1760_write32(isp->regs, DC_MODE, 0);
+               hwmode |= HW_COMN_IRQ;
+       }
+
+       /*
+        * We have to set this first in case we're in 16-bit mode.
+        * Write it twice to ensure correct upper bits if switching
+        * to 16-bit mode.
+        */
+       isp1760_write32(isp->regs, HC_HW_MODE_CTRL, hwmode);
+       isp1760_write32(isp->regs, HC_HW_MODE_CTRL, hwmode);
+
+       /*
+        * PORT 1 Control register of the ISP1760 is the OTG control register
+        * on ISP1761.
+        *
+        * TODO: Really support OTG. For now we configure port 1 in device mode
+        * when OTG is requested.
+        */
+       if ((isp->devflags & ISP1760_FLAG_ISP1761) &&
+           (isp->devflags & ISP1760_FLAG_OTG_EN))
+               otgctrl = ((HW_DM_PULLDOWN | HW_DP_PULLDOWN) << 16)
+                       | HW_OTG_DISABLE;
+       else
+               otgctrl = (HW_SW_SEL_HC_DC << 16)
+                       | (HW_VBUS_DRV | HW_SEL_CP_EXT);
+
+       isp1760_write32(isp->regs, HC_PORT1_CTRL, otgctrl);
+
+       dev_info(isp->dev, "bus width: %u, oc: %s\n",
+                isp->devflags & ISP1760_FLAG_BUS_WIDTH_16 ? 16 : 32,
+                isp->devflags & ISP1760_FLAG_ANALOG_OC ? "analog" : "digital");
+}
+
+void isp1760_set_pullup(struct isp1760_device *isp, bool enable)
+{
+       isp1760_write32(isp->regs, HW_OTG_CTRL_SET,
+                       enable ? HW_DP_PULLUP : HW_DP_PULLUP << 16);
+}
+
+int isp1760_register(struct resource *mem, int irq, unsigned long irqflags,
+                    struct device *dev, unsigned int devflags)
+{
+       struct isp1760_device *isp;
+       bool udc_disabled = !(devflags & ISP1760_FLAG_ISP1761);
+       int ret;
+
+       /*
+        * If neither the HCD not the UDC is enabled return an error, as no
+        * device would be registered.
+        */
+       if ((!IS_ENABLED(CONFIG_USB_ISP1760_HCD) || usb_disabled()) &&
+           (!IS_ENABLED(CONFIG_USB_ISP1761_UDC) || udc_disabled))
+               return -ENODEV;
+
+       /* prevent usb-core allocating DMA pages */
+       dev->dma_mask = NULL;
+
+       isp = devm_kzalloc(dev, sizeof(*isp), GFP_KERNEL);
+       if (!isp)
+               return -ENOMEM;
+
+       isp->dev = dev;
+       isp->devflags = devflags;
+
+       isp->rst_gpio = devm_gpiod_get_optional(dev, NULL, GPIOD_OUT_HIGH);
+       if (IS_ERR(isp->rst_gpio))
+               return PTR_ERR(isp->rst_gpio);
+
+       isp->regs = devm_ioremap_resource(dev, mem);
+       if (IS_ERR(isp->regs))
+               return PTR_ERR(isp->regs);
+
+       isp1760_init_core(isp);
+
+       if (IS_ENABLED(CONFIG_USB_ISP1760_HCD) && !usb_disabled()) {
+               ret = isp1760_hcd_register(&isp->hcd, isp->regs, mem, irq,
+                                          irqflags | IRQF_SHARED, dev);
+               if (ret < 0)
+                       return ret;
+       }
+
+       if (IS_ENABLED(CONFIG_USB_ISP1761_UDC) && !udc_disabled) {
+               ret = isp1760_udc_register(isp, irq, irqflags | IRQF_SHARED |
+                                          IRQF_DISABLED);
+               if (ret < 0) {
+                       isp1760_hcd_unregister(&isp->hcd);
+                       return ret;
+               }
+       }
+
+       dev_set_drvdata(dev, isp);
+
+       return 0;
+}
+
+void isp1760_unregister(struct device *dev)
+{
+       struct isp1760_device *isp = dev_get_drvdata(dev);
+
+       isp1760_udc_unregister(isp);
+       isp1760_hcd_unregister(&isp->hcd);
+}
+
+MODULE_DESCRIPTION("Driver for the ISP1760 USB-controller from NXP");
+MODULE_AUTHOR("Sebastian Siewior <bigeasy@linuxtronix.de>");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/usb/isp1760/isp1760-core.h b/drivers/usb/isp1760/isp1760-core.h
new file mode 100644 (file)
index 0000000..c70f836
--- /dev/null
@@ -0,0 +1,68 @@
+/*
+ * Driver for the NXP ISP1760 chip
+ *
+ * Copyright 2014 Laurent Pinchart
+ * Copyright 2007 Sebastian Siewior
+ *
+ * Contacts:
+ *     Sebastian Siewior <bigeasy@linutronix.de>
+ *     Laurent Pinchart <laurent.pinchart@ideasonboard.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ */
+
+#ifndef _ISP1760_CORE_H_
+#define _ISP1760_CORE_H_
+
+#include <linux/ioport.h>
+
+#include "isp1760-hcd.h"
+#include "isp1760-udc.h"
+
+struct device;
+struct gpio_desc;
+
+/*
+ * Device flags that can vary from board to board.  All of these
+ * indicate the most "atypical" case, so that a devflags of 0 is
+ * a sane default configuration.
+ */
+#define ISP1760_FLAG_BUS_WIDTH_16      0x00000002 /* 16-bit data bus width */
+#define ISP1760_FLAG_OTG_EN            0x00000004 /* Port 1 supports OTG */
+#define ISP1760_FLAG_ANALOG_OC         0x00000008 /* Analog overcurrent */
+#define ISP1760_FLAG_DACK_POL_HIGH     0x00000010 /* DACK active high */
+#define ISP1760_FLAG_DREQ_POL_HIGH     0x00000020 /* DREQ active high */
+#define ISP1760_FLAG_ISP1761           0x00000040 /* Chip is ISP1761 */
+#define ISP1760_FLAG_INTR_POL_HIGH     0x00000080 /* Interrupt polarity active high */
+#define ISP1760_FLAG_INTR_EDGE_TRIG    0x00000100 /* Interrupt edge triggered */
+
+struct isp1760_device {
+       struct device *dev;
+
+       void __iomem *regs;
+       unsigned int devflags;
+       struct gpio_desc *rst_gpio;
+
+       struct isp1760_hcd hcd;
+       struct isp1760_udc udc;
+};
+
+int isp1760_register(struct resource *mem, int irq, unsigned long irqflags,
+                    struct device *dev, unsigned int devflags);
+void isp1760_unregister(struct device *dev);
+
+void isp1760_set_pullup(struct isp1760_device *isp, bool enable);
+
+static inline u32 isp1760_read32(void __iomem *base, u32 reg)
+{
+       return readl(base + reg);
+}
+
+static inline void isp1760_write32(void __iomem *base, u32 reg, u32 val)
+{
+       writel(val, base + reg);
+}
+
+#endif
similarity index 91%
rename from drivers/usb/host/isp1760-hcd.c
rename to drivers/usb/isp1760/isp1760-hcd.c
index cecf39a..eba9b82 100644 (file)
@@ -11,6 +11,7 @@
  * (c) 2011 Arvid Brodin <arvid.brodin@enea.com>
  *
  */
+#include <linux/gpio/consumer.h>
 #include <linux/module.h>
 #include <linux/kernel.h>
 #include <linux/slab.h>
 #include <linux/timer.h>
 #include <asm/unaligned.h>
 #include <asm/cacheflush.h>
-#include <linux/gpio.h>
 
+#include "isp1760-core.h"
 #include "isp1760-hcd.h"
+#include "isp1760-regs.h"
 
 static struct kmem_cache *qtd_cachep;
 static struct kmem_cache *qh_cachep;
 static struct kmem_cache *urb_listitem_cachep;
 
-enum queue_head_types {
-       QH_CONTROL,
-       QH_BULK,
-       QH_INTERRUPT,
-       QH_END
-};
-
-struct isp1760_hcd {
-       u32 hcs_params;
-       spinlock_t              lock;
-       struct slotinfo         atl_slots[32];
-       int                     atl_done_map;
-       struct slotinfo         int_slots[32];
-       int                     int_done_map;
-       struct memory_chunk memory_pool[BLOCKS];
-       struct list_head        qh_list[QH_END];
-
-       /* periodic schedule support */
-#define        DEFAULT_I_TDPS          1024
-       unsigned                periodic_size;
-       unsigned                i_thresh;
-       unsigned long           reset_done;
-       unsigned long           next_statechange;
-       unsigned int            devflags;
-
-       int                     rst_gpio;
-};
+typedef void (packet_enqueue)(struct usb_hcd *hcd, struct isp1760_qh *qh,
+               struct isp1760_qtd *qtd);
 
 static inline struct isp1760_hcd *hcd_to_priv(struct usb_hcd *hcd)
 {
-       return (struct isp1760_hcd *) (hcd->hcd_priv);
+       return *(struct isp1760_hcd **)hcd->hcd_priv;
 }
 
-/* Section 2.2 Host Controller Capability Registers */
-#define HC_LENGTH(p)           (((p)>>00)&0x00ff)      /* bits 7:0 */
-#define HC_VERSION(p)          (((p)>>16)&0xffff)      /* bits 31:16 */
-#define HCS_INDICATOR(p)       ((p)&(1 << 16)) /* true: has port indicators */
-#define HCS_PPC(p)             ((p)&(1 << 4))  /* true: port power control */
-#define HCS_N_PORTS(p)         (((p)>>0)&0xf)  /* bits 3:0, ports on HC */
-#define HCC_ISOC_CACHE(p)       ((p)&(1 << 7))  /* true: can cache isoc frame */
-#define HCC_ISOC_THRES(p)       (((p)>>4)&0x7)  /* bits 6:4, uframes cached */
-
-/* Section 2.3 Host Controller Operational Registers */
-#define CMD_LRESET     (1<<7)          /* partial reset (no ports, etc) */
-#define CMD_RESET      (1<<1)          /* reset HC not bus */
-#define CMD_RUN                (1<<0)          /* start/stop HC */
-#define STS_PCD                (1<<2)          /* port change detect */
-#define FLAG_CF                (1<<0)          /* true: we'll support "high speed" */
-
-#define PORT_OWNER     (1<<13)         /* true: companion hc owns this port */
-#define PORT_POWER     (1<<12)         /* true: has power (see PPC) */
-#define PORT_USB11(x) (((x) & (3 << 10)) == (1 << 10)) /* USB 1.1 device */
-#define PORT_RESET     (1<<8)          /* reset port */
-#define PORT_SUSPEND   (1<<7)          /* suspend port */
-#define PORT_RESUME    (1<<6)          /* resume it */
-#define PORT_PE                (1<<2)          /* port enable */
-#define PORT_CSC       (1<<1)          /* connect status change */
-#define PORT_CONNECT   (1<<0)          /* device connected */
-#define PORT_RWC_BITS   (PORT_CSC)
+/* urb state*/
+#define DELETE_URB             (0x0008)
+#define NO_TRANSFER_ACTIVE     (0xffffffff)
+
+/* Philips Proprietary Transfer Descriptor (PTD) */
+typedef __u32 __bitwise __dw;
+struct ptd {
+       __dw dw0;
+       __dw dw1;
+       __dw dw2;
+       __dw dw3;
+       __dw dw4;
+       __dw dw5;
+       __dw dw6;
+       __dw dw7;
+};
+#define PTD_OFFSET             0x0400
+#define ISO_PTD_OFFSET         0x0400
+#define INT_PTD_OFFSET         0x0800
+#define ATL_PTD_OFFSET         0x0c00
+#define PAYLOAD_OFFSET         0x1000
+
+
+/* ATL */
+/* DW0 */
+#define DW0_VALID_BIT                  1
+#define FROM_DW0_VALID(x)              ((x) & 0x01)
+#define TO_DW0_LENGTH(x)               (((u32) x) << 3)
+#define TO_DW0_MAXPACKET(x)            (((u32) x) << 18)
+#define TO_DW0_MULTI(x)                        (((u32) x) << 29)
+#define TO_DW0_ENDPOINT(x)             (((u32) x) << 31)
+/* DW1 */
+#define TO_DW1_DEVICE_ADDR(x)          (((u32) x) << 3)
+#define TO_DW1_PID_TOKEN(x)            (((u32) x) << 10)
+#define DW1_TRANS_BULK                 ((u32) 2 << 12)
+#define DW1_TRANS_INT                  ((u32) 3 << 12)
+#define DW1_TRANS_SPLIT                        ((u32) 1 << 14)
+#define DW1_SE_USB_LOSPEED             ((u32) 2 << 16)
+#define TO_DW1_PORT_NUM(x)             (((u32) x) << 18)
+#define TO_DW1_HUB_NUM(x)              (((u32) x) << 25)
+/* DW2 */
+#define TO_DW2_DATA_START_ADDR(x)      (((u32) x) << 8)
+#define TO_DW2_RL(x)                   ((x) << 25)
+#define FROM_DW2_RL(x)                 (((x) >> 25) & 0xf)
+/* DW3 */
+#define FROM_DW3_NRBYTESTRANSFERRED(x)         ((x) & 0x7fff)
+#define FROM_DW3_SCS_NRBYTESTRANSFERRED(x)     ((x) & 0x07ff)
+#define TO_DW3_NAKCOUNT(x)             ((x) << 19)
+#define FROM_DW3_NAKCOUNT(x)           (((x) >> 19) & 0xf)
+#define TO_DW3_CERR(x)                 ((x) << 23)
+#define FROM_DW3_CERR(x)               (((x) >> 23) & 0x3)
+#define TO_DW3_DATA_TOGGLE(x)          ((x) << 25)
+#define FROM_DW3_DATA_TOGGLE(x)                (((x) >> 25) & 0x1)
+#define TO_DW3_PING(x)                 ((x) << 26)
+#define FROM_DW3_PING(x)               (((x) >> 26) & 0x1)
+#define DW3_ERROR_BIT                  (1 << 28)
+#define DW3_BABBLE_BIT                 (1 << 29)
+#define DW3_HALT_BIT                   (1 << 30)
+#define DW3_ACTIVE_BIT                 (1 << 31)
+#define FROM_DW3_ACTIVE(x)             (((x) >> 31) & 0x01)
+
+#define INT_UNDERRUN                   (1 << 2)
+#define INT_BABBLE                     (1 << 1)
+#define INT_EXACT                      (1 << 0)
+
+#define SETUP_PID      (2)
+#define IN_PID         (1)
+#define OUT_PID                (0)
+
+/* Errata 1 */
+#define RL_COUNTER     (0)
+#define NAK_COUNTER    (0)
+#define ERR_COUNTER    (2)
 
 struct isp1760_qtd {
        u8 packet_type;
@@ -137,12 +161,12 @@ struct urb_listitem {
  */
 static u32 reg_read32(void __iomem *base, u32 reg)
 {
-       return readl(base + reg);
+       return isp1760_read32(base, reg);
 }
 
 static void reg_write32(void __iomem *base, u32 reg, u32 val)
 {
-       writel(val, base + reg);
+       isp1760_write32(base, reg, val);
 }
 
 /*
@@ -443,42 +467,6 @@ static int isp1760_hc_setup(struct usb_hcd *hcd)
        int result;
        u32 scratch, hwmode;
 
-       /* low-level chip reset */
-       if (gpio_is_valid(priv->rst_gpio)) {
-               unsigned int rst_lvl;
-
-               rst_lvl = (priv->devflags &
-                          ISP1760_FLAG_RESET_ACTIVE_HIGH) ? 1 : 0;
-
-               gpio_set_value(priv->rst_gpio, rst_lvl);
-               mdelay(50);
-               gpio_set_value(priv->rst_gpio, !rst_lvl);
-       }
-
-       /* Setup HW Mode Control: This assumes a level active-low interrupt */
-       hwmode = HW_DATA_BUS_32BIT;
-
-       if (priv->devflags & ISP1760_FLAG_BUS_WIDTH_16)
-               hwmode &= ~HW_DATA_BUS_32BIT;
-       if (priv->devflags & ISP1760_FLAG_ANALOG_OC)
-               hwmode |= HW_ANA_DIGI_OC;
-       if (priv->devflags & ISP1760_FLAG_DACK_POL_HIGH)
-               hwmode |= HW_DACK_POL_HIGH;
-       if (priv->devflags & ISP1760_FLAG_DREQ_POL_HIGH)
-               hwmode |= HW_DREQ_POL_HIGH;
-       if (priv->devflags & ISP1760_FLAG_INTR_POL_HIGH)
-               hwmode |= HW_INTR_HIGH_ACT;
-       if (priv->devflags & ISP1760_FLAG_INTR_EDGE_TRIG)
-               hwmode |= HW_INTR_EDGE_TRIG;
-
-       /*
-        * We have to set this first in case we're in 16-bit mode.
-        * Write it twice to ensure correct upper bits if switching
-        * to 16-bit mode.
-        */
-       reg_write32(hcd->regs, HC_HW_MODE_CTRL, hwmode);
-       reg_write32(hcd->regs, HC_HW_MODE_CTRL, hwmode);
-
        reg_write32(hcd->regs, HC_SCRATCH_REG, 0xdeadbabe);
        /* Change bus pattern */
        scratch = reg_read32(hcd->regs, HC_CHIP_ID_REG);
@@ -488,46 +476,33 @@ static int isp1760_hc_setup(struct usb_hcd *hcd)
                return -ENODEV;
        }
 
-       /* pre reset */
+       /*
+        * The RESET_HC bit in the SW_RESET register is supposed to reset the
+        * host controller without touching the CPU interface registers, but at
+        * least on the ISP1761 it seems to behave as the RESET_ALL bit and
+        * reset the whole device. We thus can't use it here, so let's reset
+        * the host controller through the EHCI USB Command register. The device
+        * has been reset in core code anyway, so this shouldn't matter.
+        */
        reg_write32(hcd->regs, HC_BUFFER_STATUS_REG, 0);
        reg_write32(hcd->regs, HC_ATL_PTD_SKIPMAP_REG, NO_TRANSFER_ACTIVE);
        reg_write32(hcd->regs, HC_INT_PTD_SKIPMAP_REG, NO_TRANSFER_ACTIVE);
        reg_write32(hcd->regs, HC_ISO_PTD_SKIPMAP_REG, NO_TRANSFER_ACTIVE);
 
-       /* reset */
-       reg_write32(hcd->regs, HC_RESET_REG, SW_RESET_RESET_ALL);
-       mdelay(100);
-
-       reg_write32(hcd->regs, HC_RESET_REG, SW_RESET_RESET_HC);
-       mdelay(100);
-
        result = ehci_reset(hcd);
        if (result)
                return result;
 
        /* Step 11 passed */
 
-       dev_info(hcd->self.controller, "bus width: %d, oc: %s\n",
-                          (priv->devflags & ISP1760_FLAG_BUS_WIDTH_16) ?
-                          16 : 32, (priv->devflags & ISP1760_FLAG_ANALOG_OC) ?
-                          "analog" : "digital");
-
        /* ATL reset */
+       hwmode = reg_read32(hcd->regs, HC_HW_MODE_CTRL) & ~ALL_ATX_RESET;
        reg_write32(hcd->regs, HC_HW_MODE_CTRL, hwmode | ALL_ATX_RESET);
        mdelay(10);
        reg_write32(hcd->regs, HC_HW_MODE_CTRL, hwmode);
 
        reg_write32(hcd->regs, HC_INTERRUPT_ENABLE, INTERRUPT_ENABLE_MASK);
 
-       /*
-        * PORT 1 Control register of the ISP1760 is the OTG control
-        * register on ISP1761. Since there is no OTG or device controller
-        * support in this driver, we use port 1 as a "normal" USB host port on
-        * both chips.
-        */
-       reg_write32(hcd->regs, HC_PORT1_CTRL, PORT1_POWER | PORT1_INIT2);
-       mdelay(10);
-
        priv->hcs_params = reg_read32(hcd->regs, HC_HCSPARAMS);
 
        return priv_init(hcd);
@@ -743,8 +718,9 @@ static void qtd_free(struct isp1760_qtd *qtd)
 }
 
 static void start_bus_transfer(struct usb_hcd *hcd, u32 ptd_offset, int slot,
-                               struct slotinfo *slots, struct isp1760_qtd *qtd,
-                               struct isp1760_qh *qh, struct ptd *ptd)
+                               struct isp1760_slotinfo *slots,
+                               struct isp1760_qtd *qtd, struct isp1760_qh *qh,
+                               struct ptd *ptd)
 {
        struct isp1760_hcd *priv = hcd_to_priv(hcd);
        int skip_map;
@@ -857,7 +833,7 @@ static void enqueue_qtds(struct usb_hcd *hcd, struct isp1760_qh *qh)
 {
        struct isp1760_hcd *priv = hcd_to_priv(hcd);
        int ptd_offset;
-       struct slotinfo *slots;
+       struct isp1760_slotinfo *slots;
        int curr_slot, free_slot;
        int n;
        struct ptd ptd;
@@ -1097,7 +1073,7 @@ static void handle_done_ptds(struct usb_hcd *hcd)
        struct isp1760_qh *qh;
        int slot;
        int state;
-       struct slotinfo *slots;
+       struct isp1760_slotinfo *slots;
        u32 ptd_offset;
        struct isp1760_qtd *qtd;
        int modified;
@@ -2161,7 +2137,7 @@ static void isp1760_clear_tt_buffer_complete(struct usb_hcd *hcd,
 static const struct hc_driver isp1760_hc_driver = {
        .description            = "isp1760-hcd",
        .product_desc           = "NXP ISP1760 USB Host Controller",
-       .hcd_priv_size          = sizeof(struct isp1760_hcd),
+       .hcd_priv_size          = sizeof(struct isp1760_hcd *),
        .irq                    = isp1760_irq,
        .flags                  = HCD_MEMORY | HCD_USB2,
        .reset                  = isp1760_hc_setup,
@@ -2177,7 +2153,7 @@ static const struct hc_driver isp1760_hc_driver = {
        .clear_tt_buffer_complete       = isp1760_clear_tt_buffer_complete,
 };
 
-int __init init_kmem_once(void)
+int __init isp1760_init_kmem_once(void)
 {
        urb_listitem_cachep = kmem_cache_create("isp1760_urb_listitem",
                        sizeof(struct urb_listitem), 0, SLAB_TEMPORARY |
@@ -2204,66 +2180,56 @@ int __init init_kmem_once(void)
        return 0;
 }
 
-void deinit_kmem_cache(void)
+void isp1760_deinit_kmem_cache(void)
 {
        kmem_cache_destroy(qtd_cachep);
        kmem_cache_destroy(qh_cachep);
        kmem_cache_destroy(urb_listitem_cachep);
 }
 
-struct usb_hcd *isp1760_register(phys_addr_t res_start, resource_size_t res_len,
-                                int irq, unsigned long irqflags,
-                                int rst_gpio,
-                                struct device *dev, const char *busname,
-                                unsigned int devflags)
+int isp1760_hcd_register(struct isp1760_hcd *priv, void __iomem *regs,
+                        struct resource *mem, int irq, unsigned long irqflags,
+                        struct device *dev)
 {
        struct usb_hcd *hcd;
-       struct isp1760_hcd *priv;
        int ret;
 
-       if (usb_disabled())
-               return ERR_PTR(-ENODEV);
-
-       /* prevent usb-core allocating DMA pages */
-       dev->dma_mask = NULL;
-
        hcd = usb_create_hcd(&isp1760_hc_driver, dev, dev_name(dev));
        if (!hcd)
-               return ERR_PTR(-ENOMEM);
+               return -ENOMEM;
+
+       *(struct isp1760_hcd **)hcd->hcd_priv = priv;
+
+       priv->hcd = hcd;
 
-       priv = hcd_to_priv(hcd);
-       priv->devflags = devflags;
-       priv->rst_gpio = rst_gpio;
        init_memory(priv);
-       hcd->regs = ioremap(res_start, res_len);
-       if (!hcd->regs) {
-               ret = -EIO;
-               goto err_put;
-       }
 
        hcd->irq = irq;
-       hcd->rsrc_start = res_start;
-       hcd->rsrc_len = res_len;
+       hcd->regs = regs;
+       hcd->rsrc_start = mem->start;
+       hcd->rsrc_len = resource_size(mem);
 
        /* This driver doesn't support wakeup requests */
        hcd->cant_recv_wakeups = 1;
 
        ret = usb_add_hcd(hcd, irq, irqflags);
        if (ret)
-               goto err_unmap;
+               goto error;
+
        device_wakeup_enable(hcd->self.controller);
 
-       return hcd;
+       return 0;
 
-err_unmap:
-        iounmap(hcd->regs);
+error:
+       usb_put_hcd(hcd);
+       return ret;
+}
 
-err_put:
-        usb_put_hcd(hcd);
+void isp1760_hcd_unregister(struct isp1760_hcd *priv)
+{
+       if (!priv->hcd)
+               return;
 
-        return ERR_PTR(ret);
+       usb_remove_hcd(priv->hcd);
+       usb_put_hcd(priv->hcd);
 }
-
-MODULE_DESCRIPTION("Driver for the ISP1760 USB-controller from NXP");
-MODULE_AUTHOR("Sebastian Siewior <bigeasy@linuxtronix.de>");
-MODULE_LICENSE("GPL v2");
diff --git a/drivers/usb/isp1760/isp1760-hcd.h b/drivers/usb/isp1760/isp1760-hcd.h
new file mode 100644 (file)
index 0000000..0c1c98d
--- /dev/null
@@ -0,0 +1,102 @@
+#ifndef _ISP1760_HCD_H_
+#define _ISP1760_HCD_H_
+
+#include <linux/spinlock.h>
+
+struct isp1760_qh;
+struct isp1760_qtd;
+struct resource;
+struct usb_hcd;
+
+/*
+ * 60kb divided in:
+ * - 32 blocks @ 256  bytes
+ * - 20 blocks @ 1024 bytes
+ * -  4 blocks @ 8192 bytes
+ */
+
+#define BLOCK_1_NUM 32
+#define BLOCK_2_NUM 20
+#define BLOCK_3_NUM 4
+
+#define BLOCK_1_SIZE 256
+#define BLOCK_2_SIZE 1024
+#define BLOCK_3_SIZE 8192
+#define BLOCKS (BLOCK_1_NUM + BLOCK_2_NUM + BLOCK_3_NUM)
+#define MAX_PAYLOAD_SIZE BLOCK_3_SIZE
+#define PAYLOAD_AREA_SIZE 0xf000
+
+struct isp1760_slotinfo {
+       struct isp1760_qh *qh;
+       struct isp1760_qtd *qtd;
+       unsigned long timestamp;
+};
+
+/* chip memory management */
+struct isp1760_memory_chunk {
+       unsigned int start;
+       unsigned int size;
+       unsigned int free;
+};
+
+enum isp1760_queue_head_types {
+       QH_CONTROL,
+       QH_BULK,
+       QH_INTERRUPT,
+       QH_END
+};
+
+struct isp1760_hcd {
+#ifdef CONFIG_USB_ISP1760_HCD
+       struct usb_hcd          *hcd;
+
+       u32 hcs_params;
+       spinlock_t              lock;
+       struct isp1760_slotinfo atl_slots[32];
+       int                     atl_done_map;
+       struct isp1760_slotinfo int_slots[32];
+       int                     int_done_map;
+       struct isp1760_memory_chunk memory_pool[BLOCKS];
+       struct list_head        qh_list[QH_END];
+
+       /* periodic schedule support */
+#define        DEFAULT_I_TDPS          1024
+       unsigned                periodic_size;
+       unsigned                i_thresh;
+       unsigned long           reset_done;
+       unsigned long           next_statechange;
+#endif
+};
+
+#ifdef CONFIG_USB_ISP1760_HCD
+int isp1760_hcd_register(struct isp1760_hcd *priv, void __iomem *regs,
+                        struct resource *mem, int irq, unsigned long irqflags,
+                        struct device *dev);
+void isp1760_hcd_unregister(struct isp1760_hcd *priv);
+
+int isp1760_init_kmem_once(void);
+void isp1760_deinit_kmem_cache(void);
+#else
+static inline int isp1760_hcd_register(struct isp1760_hcd *priv,
+                                      void __iomem *regs, struct resource *mem,
+                                      int irq, unsigned long irqflags,
+                                      struct device *dev)
+{
+       return 0;
+}
+
+static inline void isp1760_hcd_unregister(struct isp1760_hcd *priv)
+{
+}
+
+static inline int isp1760_init_kmem_once(void)
+{
+       return 0;
+}
+
+static inline void isp1760_deinit_kmem_cache(void)
+{
+}
+#endif
+
+#endif /* _ISP1760_HCD_H_ */
diff --git a/drivers/usb/isp1760/isp1760-if.c b/drivers/usb/isp1760/isp1760-if.c
new file mode 100644 (file)
index 0000000..264be4d
--- /dev/null
@@ -0,0 +1,309 @@
+/*
+ * Glue code for the ISP1760 driver and bus
+ * Currently there is support for
+ * - OpenFirmware
+ * - PCI
+ * - PDEV (generic platform device centralized driver model)
+ *
+ * (c) 2007 Sebastian Siewior <bigeasy@linutronix.de>
+ *
+ */
+
+#include <linux/usb.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/usb/isp1760.h>
+#include <linux/usb/hcd.h>
+
+#include "isp1760-core.h"
+#include "isp1760-regs.h"
+
+#ifdef CONFIG_PCI
+#include <linux/pci.h>
+#endif
+
+#ifdef CONFIG_PCI
+static int isp1761_pci_init(struct pci_dev *dev)
+{
+       resource_size_t mem_start;
+       resource_size_t mem_length;
+       u8 __iomem *iobase;
+       u8 latency, limit;
+       int retry_count;
+       u32 reg_data;
+
+       /* Grab the PLX PCI shared memory of the ISP 1761 we need  */
+       mem_start = pci_resource_start(dev, 3);
+       mem_length = pci_resource_len(dev, 3);
+       if (mem_length < 0xffff) {
+               printk(KERN_ERR "memory length for this resource is wrong\n");
+               return -ENOMEM;
+       }
+
+       if (!request_mem_region(mem_start, mem_length, "ISP-PCI")) {
+               printk(KERN_ERR "host controller already in use\n");
+               return -EBUSY;
+       }
+
+       /* map available memory */
+       iobase = ioremap_nocache(mem_start, mem_length);
+       if (!iobase) {
+               printk(KERN_ERR "Error ioremap failed\n");
+               release_mem_region(mem_start, mem_length);
+               return -ENOMEM;
+       }
+
+       /* bad pci latencies can contribute to overruns */
+       pci_read_config_byte(dev, PCI_LATENCY_TIMER, &latency);
+       if (latency) {
+               pci_read_config_byte(dev, PCI_MAX_LAT, &limit);
+               if (limit && limit < latency)
+                       pci_write_config_byte(dev, PCI_LATENCY_TIMER, limit);
+       }
+
+       /* Try to check whether we can access Scratch Register of
+        * Host Controller or not. The initial PCI access is retried until
+        * local init for the PCI bridge is completed
+        */
+       retry_count = 20;
+       reg_data = 0;
+       while ((reg_data != 0xFACE) && retry_count) {
+               /*by default host is in 16bit mode, so
+                * io operations at this stage must be 16 bit
+                * */
+               writel(0xface, iobase + HC_SCRATCH_REG);
+               udelay(100);
+               reg_data = readl(iobase + HC_SCRATCH_REG) & 0x0000ffff;
+               retry_count--;
+       }
+
+       iounmap(iobase);
+       release_mem_region(mem_start, mem_length);
+
+       /* Host Controller presence is detected by writing to scratch register
+        * and reading back and checking the contents are same or not
+        */
+       if (reg_data != 0xFACE) {
+               dev_err(&dev->dev, "scratch register mismatch %x\n", reg_data);
+               return -ENOMEM;
+       }
+
+       /* Grab the PLX PCI mem maped port start address we need  */
+       mem_start = pci_resource_start(dev, 0);
+       mem_length = pci_resource_len(dev, 0);
+
+       if (!request_mem_region(mem_start, mem_length, "ISP1761 IO MEM")) {
+               printk(KERN_ERR "request region #1\n");
+               return -EBUSY;
+       }
+
+       iobase = ioremap_nocache(mem_start, mem_length);
+       if (!iobase) {
+               printk(KERN_ERR "ioremap #1\n");
+               release_mem_region(mem_start, mem_length);
+               return -ENOMEM;
+       }
+
+       /* configure PLX PCI chip to pass interrupts */
+#define PLX_INT_CSR_REG 0x68
+       reg_data = readl(iobase + PLX_INT_CSR_REG);
+       reg_data |= 0x900;
+       writel(reg_data, iobase + PLX_INT_CSR_REG);
+
+       /* done with PLX IO access */
+       iounmap(iobase);
+       release_mem_region(mem_start, mem_length);
+
+       return 0;
+}
+
+static int isp1761_pci_probe(struct pci_dev *dev,
+               const struct pci_device_id *id)
+{
+       unsigned int devflags = 0;
+       int ret;
+
+       if (!dev->irq)
+               return -ENODEV;
+
+       if (pci_enable_device(dev) < 0)
+               return -ENODEV;
+
+       ret = isp1761_pci_init(dev);
+       if (ret < 0)
+               goto error;
+
+       pci_set_master(dev);
+
+       dev->dev.dma_mask = NULL;
+       ret = isp1760_register(&dev->resource[3], dev->irq, 0, &dev->dev,
+                              devflags);
+       if (ret < 0)
+               goto error;
+
+       return 0;
+
+error:
+       pci_disable_device(dev);
+       return ret;
+}
+
+static void isp1761_pci_remove(struct pci_dev *dev)
+{
+       isp1760_unregister(&dev->dev);
+
+       pci_disable_device(dev);
+}
+
+static void isp1761_pci_shutdown(struct pci_dev *dev)
+{
+       printk(KERN_ERR "ips1761_pci_shutdown\n");
+}
+
+static const struct pci_device_id isp1760_plx [] = {
+       {
+               .class          = PCI_CLASS_BRIDGE_OTHER << 8,
+               .class_mask     = ~0,
+               .vendor         = PCI_VENDOR_ID_PLX,
+               .device         = 0x5406,
+               .subvendor      = PCI_VENDOR_ID_PLX,
+               .subdevice      = 0x9054,
+       },
+       { }
+};
+MODULE_DEVICE_TABLE(pci, isp1760_plx);
+
+static struct pci_driver isp1761_pci_driver = {
+       .name =         "isp1760",
+       .id_table =     isp1760_plx,
+       .probe =        isp1761_pci_probe,
+       .remove =       isp1761_pci_remove,
+       .shutdown =     isp1761_pci_shutdown,
+};
+#endif
+
+static int isp1760_plat_probe(struct platform_device *pdev)
+{
+       unsigned long irqflags;
+       unsigned int devflags = 0;
+       struct resource *mem_res;
+       struct resource *irq_res;
+       int ret;
+
+       mem_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+
+       irq_res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
+       if (!irq_res) {
+               pr_warning("isp1760: IRQ resource not available\n");
+               return -ENODEV;
+       }
+       irqflags = irq_res->flags & IRQF_TRIGGER_MASK;
+
+       if (IS_ENABLED(CONFIG_OF) && pdev->dev.of_node) {
+               struct device_node *dp = pdev->dev.of_node;
+               u32 bus_width = 0;
+
+               if (of_device_is_compatible(dp, "nxp,usb-isp1761"))
+                       devflags |= ISP1760_FLAG_ISP1761;
+
+               /* Some systems wire up only 16 of the 32 data lines */
+               of_property_read_u32(dp, "bus-width", &bus_width);
+               if (bus_width == 16)
+                       devflags |= ISP1760_FLAG_BUS_WIDTH_16;
+
+               if (of_property_read_bool(dp, "port1-otg"))
+                       devflags |= ISP1760_FLAG_OTG_EN;
+
+               if (of_property_read_bool(dp, "analog-oc"))
+                       devflags |= ISP1760_FLAG_ANALOG_OC;
+
+               if (of_property_read_bool(dp, "dack-polarity"))
+                       devflags |= ISP1760_FLAG_DACK_POL_HIGH;
+
+               if (of_property_read_bool(dp, "dreq-polarity"))
+                       devflags |= ISP1760_FLAG_DREQ_POL_HIGH;
+       } else if (dev_get_platdata(&pdev->dev)) {
+               struct isp1760_platform_data *pdata =
+                       dev_get_platdata(&pdev->dev);
+
+               if (pdata->is_isp1761)
+                       devflags |= ISP1760_FLAG_ISP1761;
+               if (pdata->bus_width_16)
+                       devflags |= ISP1760_FLAG_BUS_WIDTH_16;
+               if (pdata->port1_otg)
+                       devflags |= ISP1760_FLAG_OTG_EN;
+               if (pdata->analog_oc)
+                       devflags |= ISP1760_FLAG_ANALOG_OC;
+               if (pdata->dack_polarity_high)
+                       devflags |= ISP1760_FLAG_DACK_POL_HIGH;
+               if (pdata->dreq_polarity_high)
+                       devflags |= ISP1760_FLAG_DREQ_POL_HIGH;
+       }
+
+       ret = isp1760_register(mem_res, irq_res->start, irqflags, &pdev->dev,
+                              devflags);
+       if (ret < 0)
+               return ret;
+
+       pr_info("ISP1760 USB device initialised\n");
+       return 0;
+}
+
+static int isp1760_plat_remove(struct platform_device *pdev)
+{
+       isp1760_unregister(&pdev->dev);
+
+       return 0;
+}
+
+#ifdef CONFIG_OF
+static const struct of_device_id isp1760_of_match[] = {
+       { .compatible = "nxp,usb-isp1760", },
+       { .compatible = "nxp,usb-isp1761", },
+       { },
+};
+MODULE_DEVICE_TABLE(of, isp1760_of_match);
+#endif
+
+static struct platform_driver isp1760_plat_driver = {
+       .probe  = isp1760_plat_probe,
+       .remove = isp1760_plat_remove,
+       .driver = {
+               .name   = "isp1760",
+               .of_match_table = of_match_ptr(isp1760_of_match),
+       },
+};
+
+static int __init isp1760_init(void)
+{
+       int ret, any_ret = -ENODEV;
+
+       isp1760_init_kmem_once();
+
+       ret = platform_driver_register(&isp1760_plat_driver);
+       if (!ret)
+               any_ret = 0;
+#ifdef CONFIG_PCI
+       ret = pci_register_driver(&isp1761_pci_driver);
+       if (!ret)
+               any_ret = 0;
+#endif
+
+       if (any_ret)
+               isp1760_deinit_kmem_cache();
+       return any_ret;
+}
+module_init(isp1760_init);
+
+static void __exit isp1760_exit(void)
+{
+       platform_driver_unregister(&isp1760_plat_driver);
+#ifdef CONFIG_PCI
+       pci_unregister_driver(&isp1761_pci_driver);
+#endif
+       isp1760_deinit_kmem_cache();
+}
+module_exit(isp1760_exit);
diff --git a/drivers/usb/isp1760/isp1760-regs.h b/drivers/usb/isp1760/isp1760-regs.h
new file mode 100644 (file)
index 0000000..b67095c
--- /dev/null
@@ -0,0 +1,230 @@
+/*
+ * Driver for the NXP ISP1760 chip
+ *
+ * Copyright 2014 Laurent Pinchart
+ * Copyright 2007 Sebastian Siewior
+ *
+ * Contacts:
+ *     Sebastian Siewior <bigeasy@linutronix.de>
+ *     Laurent Pinchart <laurent.pinchart@ideasonboard.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ */
+
+#ifndef _ISP1760_REGS_H_
+#define _ISP1760_REGS_H_
+
+/* -----------------------------------------------------------------------------
+ * Host Controller
+ */
+
+/* EHCI capability registers */
+#define HC_CAPLENGTH           0x000
+#define HC_LENGTH(p)           (((p) >> 00) & 0x00ff)  /* bits 7:0 */
+#define HC_VERSION(p)          (((p) >> 16) & 0xffff)  /* bits 31:16 */
+
+#define HC_HCSPARAMS           0x004
+#define HCS_INDICATOR(p)       ((p) & (1 << 16))       /* true: has port indicators */
+#define HCS_PPC(p)             ((p) & (1 << 4))        /* true: port power control */
+#define HCS_N_PORTS(p)         (((p) >> 0) & 0xf)      /* bits 3:0, ports on HC */
+
+#define HC_HCCPARAMS           0x008
+#define HCC_ISOC_CACHE(p)       ((p) & (1 << 7))       /* true: can cache isoc frame */
+#define HCC_ISOC_THRES(p)       (((p) >> 4) & 0x7)     /* bits 6:4, uframes cached */
+
+/* EHCI operational registers */
+#define HC_USBCMD              0x020
+#define CMD_LRESET             (1 << 7)                /* partial reset (no ports, etc) */
+#define CMD_RESET              (1 << 1)                /* reset HC not bus */
+#define CMD_RUN                        (1 << 0)                /* start/stop HC */
+
+#define HC_USBSTS              0x024
+#define STS_PCD                        (1 << 2)                /* port change detect */
+
+#define HC_FRINDEX             0x02c
+
+#define HC_CONFIGFLAG          0x060
+#define FLAG_CF                        (1 << 0)                /* true: we'll support "high speed" */
+
+#define HC_PORTSC1             0x064
+#define PORT_OWNER             (1 << 13)               /* true: companion hc owns this port */
+#define PORT_POWER             (1 << 12)               /* true: has power (see PPC) */
+#define PORT_USB11(x)          (((x) & (3 << 10)) == (1 << 10))        /* USB 1.1 device */
+#define PORT_RESET             (1 << 8)                /* reset port */
+#define PORT_SUSPEND           (1 << 7)                /* suspend port */
+#define PORT_RESUME            (1 << 6)                /* resume it */
+#define PORT_PE                        (1 << 2)                /* port enable */
+#define PORT_CSC               (1 << 1)                /* connect status change */
+#define PORT_CONNECT           (1 << 0)                /* device connected */
+#define PORT_RWC_BITS          (PORT_CSC)
+
+#define HC_ISO_PTD_DONEMAP_REG 0x130
+#define HC_ISO_PTD_SKIPMAP_REG 0x134
+#define HC_ISO_PTD_LASTPTD_REG 0x138
+#define HC_INT_PTD_DONEMAP_REG 0x140
+#define HC_INT_PTD_SKIPMAP_REG 0x144
+#define HC_INT_PTD_LASTPTD_REG 0x148
+#define HC_ATL_PTD_DONEMAP_REG 0x150
+#define HC_ATL_PTD_SKIPMAP_REG 0x154
+#define HC_ATL_PTD_LASTPTD_REG 0x158
+
+/* Configuration Register */
+#define HC_HW_MODE_CTRL                0x300
+#define ALL_ATX_RESET          (1 << 31)
+#define HW_ANA_DIGI_OC         (1 << 15)
+#define HW_DEV_DMA             (1 << 11)
+#define HW_COMN_IRQ            (1 << 10)
+#define HW_COMN_DMA            (1 << 9)
+#define HW_DATA_BUS_32BIT      (1 << 8)
+#define HW_DACK_POL_HIGH       (1 << 6)
+#define HW_DREQ_POL_HIGH       (1 << 5)
+#define HW_INTR_HIGH_ACT       (1 << 2)
+#define HW_INTR_EDGE_TRIG      (1 << 1)
+#define HW_GLOBAL_INTR_EN      (1 << 0)
+
+#define HC_CHIP_ID_REG         0x304
+#define HC_SCRATCH_REG         0x308
+
+#define HC_RESET_REG           0x30c
+#define SW_RESET_RESET_HC      (1 << 1)
+#define SW_RESET_RESET_ALL     (1 << 0)
+
+#define HC_BUFFER_STATUS_REG   0x334
+#define ISO_BUF_FILL           (1 << 2)
+#define INT_BUF_FILL           (1 << 1)
+#define ATL_BUF_FILL           (1 << 0)
+
+#define HC_MEMORY_REG          0x33c
+#define ISP_BANK(x)            ((x) << 16)
+
+#define HC_PORT1_CTRL          0x374
+#define PORT1_POWER            (3 << 3)
+#define PORT1_INIT1            (1 << 7)
+#define PORT1_INIT2            (1 << 23)
+#define HW_OTG_CTRL_SET                0x374
+#define HW_OTG_CTRL_CLR                0x376
+#define HW_OTG_DISABLE         (1 << 10)
+#define HW_OTG_SE0_EN          (1 << 9)
+#define HW_BDIS_ACON_EN                (1 << 8)
+#define HW_SW_SEL_HC_DC                (1 << 7)
+#define HW_VBUS_CHRG           (1 << 6)
+#define HW_VBUS_DISCHRG                (1 << 5)
+#define HW_VBUS_DRV            (1 << 4)
+#define HW_SEL_CP_EXT          (1 << 3)
+#define HW_DM_PULLDOWN         (1 << 2)
+#define HW_DP_PULLDOWN         (1 << 1)
+#define HW_DP_PULLUP           (1 << 0)
+
+/* Interrupt Register */
+#define HC_INTERRUPT_REG       0x310
+
+#define HC_INTERRUPT_ENABLE    0x314
+#define HC_ISO_INT             (1 << 9)
+#define HC_ATL_INT             (1 << 8)
+#define HC_INTL_INT            (1 << 7)
+#define HC_EOT_INT             (1 << 3)
+#define HC_SOT_INT             (1 << 1)
+#define INTERRUPT_ENABLE_MASK  (HC_INTL_INT | HC_ATL_INT)
+
+#define HC_ISO_IRQ_MASK_OR_REG 0x318
+#define HC_INT_IRQ_MASK_OR_REG 0x31c
+#define HC_ATL_IRQ_MASK_OR_REG 0x320
+#define HC_ISO_IRQ_MASK_AND_REG        0x324
+#define HC_INT_IRQ_MASK_AND_REG        0x328
+#define HC_ATL_IRQ_MASK_AND_REG        0x32c
+
+/* -----------------------------------------------------------------------------
+ * Peripheral Controller
+ */
+
+/* Initialization Registers */
+#define DC_ADDRESS                     0x0200
+#define DC_DEVEN                       (1 << 7)
+
+#define DC_MODE                                0x020c
+#define DC_DMACLKON                    (1 << 9)
+#define DC_VBUSSTAT                    (1 << 8)
+#define DC_CLKAON                      (1 << 7)
+#define DC_SNDRSU                      (1 << 6)
+#define DC_GOSUSP                      (1 << 5)
+#define DC_SFRESET                     (1 << 4)
+#define DC_GLINTENA                    (1 << 3)
+#define DC_WKUPCS                      (1 << 2)
+
+#define DC_INTCONF                     0x0210
+#define DC_CDBGMOD_ACK_NAK             (0 << 6)
+#define DC_CDBGMOD_ACK                 (1 << 6)
+#define DC_CDBGMOD_ACK_1NAK            (2 << 6)
+#define DC_DDBGMODIN_ACK_NAK           (0 << 4)
+#define DC_DDBGMODIN_ACK               (1 << 4)
+#define DC_DDBGMODIN_ACK_1NAK          (2 << 4)
+#define DC_DDBGMODOUT_ACK_NYET_NAK     (0 << 2)
+#define DC_DDBGMODOUT_ACK_NYET         (1 << 2)
+#define DC_DDBGMODOUT_ACK_NYET_1NAK    (2 << 2)
+#define DC_INTLVL                      (1 << 1)
+#define DC_INTPOL                      (1 << 0)
+
+#define DC_DEBUG                       0x0212
+#define DC_INTENABLE                   0x0214
+#define DC_IEPTX(n)                    (1 << (11 + 2 * (n)))
+#define DC_IEPRX(n)                    (1 << (10 + 2 * (n)))
+#define DC_IEPRXTX(n)                  (3 << (10 + 2 * (n)))
+#define DC_IEP0SETUP                   (1 << 8)
+#define DC_IEVBUS                      (1 << 7)
+#define DC_IEDMA                       (1 << 6)
+#define DC_IEHS_STA                    (1 << 5)
+#define DC_IERESM                      (1 << 4)
+#define DC_IESUSP                      (1 << 3)
+#define DC_IEPSOF                      (1 << 2)
+#define DC_IESOF                       (1 << 1)
+#define DC_IEBRST                      (1 << 0)
+
+/* Data Flow Registers */
+#define DC_EPINDEX                     0x022c
+#define DC_EP0SETUP                    (1 << 5)
+#define DC_ENDPIDX(n)                  ((n) << 1)
+#define DC_EPDIR                       (1 << 0)
+
+#define DC_CTRLFUNC                    0x0228
+#define DC_CLBUF                       (1 << 4)
+#define DC_VENDP                       (1 << 3)
+#define DC_DSEN                                (1 << 2)
+#define DC_STATUS                      (1 << 1)
+#define DC_STALL                       (1 << 0)
+
+#define DC_DATAPORT                    0x0220
+#define DC_BUFLEN                      0x021c
+#define DC_DATACOUNT_MASK              0xffff
+#define DC_BUFSTAT                     0x021e
+#define DC_EPMAXPKTSZ                  0x0204
+
+#define DC_EPTYPE                      0x0208
+#define DC_NOEMPKT                     (1 << 4)
+#define DC_EPENABLE                    (1 << 3)
+#define DC_DBLBUF                      (1 << 2)
+#define DC_ENDPTYP_ISOC                        (1 << 0)
+#define DC_ENDPTYP_BULK                        (2 << 0)
+#define DC_ENDPTYP_INTERRUPT           (3 << 0)
+
+/* DMA Registers */
+#define DC_DMACMD                      0x0230
+#define DC_DMATXCOUNT                  0x0234
+#define DC_DMACONF                     0x0238
+#define DC_DMAHW                       0x023c
+#define DC_DMAINTREASON                        0x0250
+#define DC_DMAINTEN                    0x0254
+#define DC_DMAEP                       0x0258
+#define DC_DMABURSTCOUNT               0x0264
+
+/* General Registers */
+#define DC_INTERRUPT                   0x0218
+#define DC_CHIPID                      0x0270
+#define DC_FRAMENUM                    0x0274
+#define DC_SCRATCH                     0x0278
+#define DC_UNLOCKDEV                   0x027c
+#define DC_INTPULSEWIDTH               0x0280
+#define DC_TESTMODE                    0x0284
+
+#endif
diff --git a/drivers/usb/isp1760/isp1760-udc.c b/drivers/usb/isp1760/isp1760-udc.c
new file mode 100644 (file)
index 0000000..9612d79
--- /dev/null
@@ -0,0 +1,1498 @@
+/*
+ * Driver for the NXP ISP1761 device controller
+ *
+ * Copyright 2014 Ideas on Board Oy
+ *
+ * Contacts:
+ *     Laurent Pinchart <laurent.pinchart@ideasonboard.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ */
+
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/list.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/timer.h>
+#include <linux/usb.h>
+
+#include "isp1760-core.h"
+#include "isp1760-regs.h"
+#include "isp1760-udc.h"
+
+#define ISP1760_VBUS_POLL_INTERVAL     msecs_to_jiffies(500)
+
+struct isp1760_request {
+       struct usb_request req;
+       struct list_head queue;
+       struct isp1760_ep *ep;
+       unsigned int packet_size;
+};
+
+static inline struct isp1760_udc *gadget_to_udc(struct usb_gadget *gadget)
+{
+       return container_of(gadget, struct isp1760_udc, gadget);
+}
+
+static inline struct isp1760_ep *ep_to_udc_ep(struct usb_ep *ep)
+{
+       return container_of(ep, struct isp1760_ep, ep);
+}
+
+static inline struct isp1760_request *req_to_udc_req(struct usb_request *req)
+{
+       return container_of(req, struct isp1760_request, req);
+}
+
+static inline u32 isp1760_udc_read(struct isp1760_udc *udc, u16 reg)
+{
+       return isp1760_read32(udc->regs, reg);
+}
+
+static inline void isp1760_udc_write(struct isp1760_udc *udc, u16 reg, u32 val)
+{
+       isp1760_write32(udc->regs, reg, val);
+}
+
+/* -----------------------------------------------------------------------------
+ * Endpoint Management
+ */
+
+static struct isp1760_ep *isp1760_udc_find_ep(struct isp1760_udc *udc,
+                                             u16 index)
+{
+       unsigned int i;
+
+       if (index == 0)
+               return &udc->ep[0];
+
+       for (i = 1; i < ARRAY_SIZE(udc->ep); ++i) {
+               if (udc->ep[i].addr == index)
+                       return udc->ep[i].desc ? &udc->ep[i] : NULL;
+       }
+
+       return NULL;
+}
+
+static void __isp1760_udc_select_ep(struct isp1760_ep *ep, int dir)
+{
+       isp1760_udc_write(ep->udc, DC_EPINDEX,
+                         DC_ENDPIDX(ep->addr & USB_ENDPOINT_NUMBER_MASK) |
+                         (dir == USB_DIR_IN ? DC_EPDIR : 0));
+}
+
+/**
+ * isp1760_udc_select_ep - Select an endpoint for register access
+ * @ep: The endpoint
+ *
+ * The ISP1761 endpoint registers are banked. This function selects the target
+ * endpoint for banked register access. The selection remains valid until the
+ * next call to this function, the next direct access to the EPINDEX register
+ * or the next reset, whichever comes first.
+ *
+ * Called with the UDC spinlock held.
+ */
+static void isp1760_udc_select_ep(struct isp1760_ep *ep)
+{
+       __isp1760_udc_select_ep(ep, ep->addr & USB_ENDPOINT_DIR_MASK);
+}
+
+/* Called with the UDC spinlock held. */
+static void isp1760_udc_ctrl_send_status(struct isp1760_ep *ep, int dir)
+{
+       struct isp1760_udc *udc = ep->udc;
+
+       /*
+        * Proceed to the status stage. The status stage data packet flows in
+        * the direction opposite to the data stage data packets, we thus need
+        * to select the OUT/IN endpoint for IN/OUT transfers.
+        */
+       isp1760_udc_write(udc, DC_EPINDEX, DC_ENDPIDX(0) |
+                         (dir == USB_DIR_IN ? 0 : DC_EPDIR));
+       isp1760_udc_write(udc, DC_CTRLFUNC, DC_STATUS);
+
+       /*
+        * The hardware will terminate the request automatically and go back to
+        * the setup stage without notifying us.
+        */
+       udc->ep0_state = ISP1760_CTRL_SETUP;
+}
+
+/* Called without the UDC spinlock held. */
+static void isp1760_udc_request_complete(struct isp1760_ep *ep,
+                                        struct isp1760_request *req,
+                                        int status)
+{
+       struct isp1760_udc *udc = ep->udc;
+       unsigned long flags;
+
+       dev_dbg(ep->udc->isp->dev, "completing request %p with status %d\n",
+               req, status);
+
+       req->ep = NULL;
+       req->req.status = status;
+       req->req.complete(&ep->ep, &req->req);
+
+       spin_lock_irqsave(&udc->lock, flags);
+
+       /*
+        * When completing control OUT requests, move to the status stage after
+        * calling the request complete callback. This gives the gadget an
+        * opportunity to stall the control transfer if needed.
+        */
+       if (status == 0 && ep->addr == 0 && udc->ep0_dir == USB_DIR_OUT)
+               isp1760_udc_ctrl_send_status(ep, USB_DIR_OUT);
+
+       spin_unlock_irqrestore(&udc->lock, flags);
+}
+
+static void isp1760_udc_ctrl_send_stall(struct isp1760_ep *ep)
+{
+       struct isp1760_udc *udc = ep->udc;
+       unsigned long flags;
+
+       dev_dbg(ep->udc->isp->dev, "%s(ep%02x)\n", __func__, ep->addr);
+
+       spin_lock_irqsave(&udc->lock, flags);
+
+       /* Stall both the IN and OUT endpoints. */
+       __isp1760_udc_select_ep(ep, USB_DIR_OUT);
+       isp1760_udc_write(udc, DC_CTRLFUNC, DC_STALL);
+       __isp1760_udc_select_ep(ep, USB_DIR_IN);
+       isp1760_udc_write(udc, DC_CTRLFUNC, DC_STALL);
+
+       /* A protocol stall completes the control transaction. */
+       udc->ep0_state = ISP1760_CTRL_SETUP;
+
+       spin_unlock_irqrestore(&udc->lock, flags);
+}
+
+/* -----------------------------------------------------------------------------
+ * Data Endpoints
+ */
+
+/* Called with the UDC spinlock held. */
+static bool isp1760_udc_receive(struct isp1760_ep *ep,
+                               struct isp1760_request *req)
+{
+       struct isp1760_udc *udc = ep->udc;
+       unsigned int len;
+       u32 *buf;
+       int i;
+
+       isp1760_udc_select_ep(ep);
+       len = isp1760_udc_read(udc, DC_BUFLEN) & DC_DATACOUNT_MASK;
+
+       dev_dbg(udc->isp->dev, "%s: received %u bytes (%u/%u done)\n",
+               __func__, len, req->req.actual, req->req.length);
+
+       len = min(len, req->req.length - req->req.actual);
+
+       if (!len) {
+               /*
+                * There's no data to be read from the FIFO, acknowledge the RX
+                * interrupt by clearing the buffer.
+                *
+                * TODO: What if another packet arrives in the meantime ? The
+                * datasheet doesn't clearly document how this should be
+                * handled.
+                */
+               isp1760_udc_write(udc, DC_CTRLFUNC, DC_CLBUF);
+               return false;
+       }
+
+       buf = req->req.buf + req->req.actual;
+
+       /*
+        * Make sure not to read more than one extra byte, otherwise data from
+        * the next packet might be removed from the FIFO.
+        */
+       for (i = len; i > 2; i -= 4, ++buf)
+               *buf = le32_to_cpu(isp1760_udc_read(udc, DC_DATAPORT));
+       if (i > 0)
+               *(u16 *)buf = le16_to_cpu(readw(udc->regs + DC_DATAPORT));
+
+       req->req.actual += len;
+
+       /*
+        * TODO: The short_not_ok flag isn't supported yet, but isn't used by
+        * any gadget driver either.
+        */
+
+       dev_dbg(udc->isp->dev,
+               "%s: req %p actual/length %u/%u maxpacket %u packet size %u\n",
+               __func__, req, req->req.actual, req->req.length, ep->maxpacket,
+               len);
+
+       ep->rx_pending = false;
+
+       /*
+        * Complete the request if all data has been received or if a short
+        * packet has been received.
+        */
+       if (req->req.actual == req->req.length || len < ep->maxpacket) {
+               list_del(&req->queue);
+               return true;
+       }
+
+       return false;
+}
+
+static void isp1760_udc_transmit(struct isp1760_ep *ep,
+                                struct isp1760_request *req)
+{
+       struct isp1760_udc *udc = ep->udc;
+       u32 *buf = req->req.buf + req->req.actual;
+       int i;
+
+       req->packet_size = min(req->req.length - req->req.actual,
+                              ep->maxpacket);
+
+       dev_dbg(udc->isp->dev, "%s: transferring %u bytes (%u/%u done)\n",
+               __func__, req->packet_size, req->req.actual,
+               req->req.length);
+
+       __isp1760_udc_select_ep(ep, USB_DIR_IN);
+
+       if (req->packet_size)
+               isp1760_udc_write(udc, DC_BUFLEN, req->packet_size);
+
+       /*
+        * Make sure not to write more than one extra byte, otherwise extra data
+        * will stay in the FIFO and will be transmitted during the next control
+        * request. The endpoint control CLBUF bit is supposed to allow flushing
+        * the FIFO for this kind of conditions, but doesn't seem to work.
+        */
+       for (i = req->packet_size; i > 2; i -= 4, ++buf)
+               isp1760_udc_write(udc, DC_DATAPORT, cpu_to_le32(*buf));
+       if (i > 0)
+               writew(cpu_to_le16(*(u16 *)buf), udc->regs + DC_DATAPORT);
+
+       if (ep->addr == 0)
+               isp1760_udc_write(udc, DC_CTRLFUNC, DC_DSEN);
+       if (!req->packet_size)
+               isp1760_udc_write(udc, DC_CTRLFUNC, DC_VENDP);
+}
+
+static void isp1760_ep_rx_ready(struct isp1760_ep *ep)
+{
+       struct isp1760_udc *udc = ep->udc;
+       struct isp1760_request *req;
+       bool complete;
+
+       spin_lock(&udc->lock);
+
+       if (ep->addr == 0 && udc->ep0_state != ISP1760_CTRL_DATA_OUT) {
+               spin_unlock(&udc->lock);
+               dev_dbg(udc->isp->dev, "%s: invalid ep0 state %u\n", __func__,
+                       udc->ep0_state);
+               return;
+       }
+
+       if (ep->addr != 0 && !ep->desc) {
+               spin_unlock(&udc->lock);
+               dev_dbg(udc->isp->dev, "%s: ep%02x is disabled\n", __func__,
+                       ep->addr);
+               return;
+       }
+
+       if (list_empty(&ep->queue)) {
+               ep->rx_pending = true;
+               spin_unlock(&udc->lock);
+               dev_dbg(udc->isp->dev, "%s: ep%02x (%p) has no request queued\n",
+                       __func__, ep->addr, ep);
+               return;
+       }
+
+       req = list_first_entry(&ep->queue, struct isp1760_request,
+                              queue);
+       complete = isp1760_udc_receive(ep, req);
+
+       spin_unlock(&udc->lock);
+
+       if (complete)
+               isp1760_udc_request_complete(ep, req, 0);
+}
+
+static void isp1760_ep_tx_complete(struct isp1760_ep *ep)
+{
+       struct isp1760_udc *udc = ep->udc;
+       struct isp1760_request *complete = NULL;
+       struct isp1760_request *req;
+       bool need_zlp;
+
+       spin_lock(&udc->lock);
+
+       if (ep->addr == 0 && udc->ep0_state != ISP1760_CTRL_DATA_IN) {
+               spin_unlock(&udc->lock);
+               dev_dbg(udc->isp->dev, "TX IRQ: invalid endpoint state %u\n",
+                       udc->ep0_state);
+               return;
+       }
+
+       if (list_empty(&ep->queue)) {
+               /*
+                * This can happen for the control endpoint when the reply to
+                * the GET_STATUS IN control request is sent directly by the
+                * setup IRQ handler. Just proceed to the status stage.
+                */
+               if (ep->addr == 0) {
+                       isp1760_udc_ctrl_send_status(ep, USB_DIR_IN);
+                       spin_unlock(&udc->lock);
+                       return;
+               }
+
+               spin_unlock(&udc->lock);
+               dev_dbg(udc->isp->dev, "%s: ep%02x has no request queued\n",
+                       __func__, ep->addr);
+               return;
+       }
+
+       req = list_first_entry(&ep->queue, struct isp1760_request,
+                              queue);
+       req->req.actual += req->packet_size;
+
+       need_zlp = req->req.actual == req->req.length &&
+                  !(req->req.length % ep->maxpacket) &&
+                  req->packet_size && req->req.zero;
+
+       dev_dbg(udc->isp->dev,
+               "TX IRQ: req %p actual/length %u/%u maxpacket %u packet size %u zero %u need zlp %u\n",
+                req, req->req.actual, req->req.length, ep->maxpacket,
+                req->packet_size, req->req.zero, need_zlp);
+
+       /*
+        * Complete the request if all data has been sent and we don't need to
+        * transmit a zero length packet.
+        */
+       if (req->req.actual == req->req.length && !need_zlp) {
+               complete = req;
+               list_del(&req->queue);
+
+               if (ep->addr == 0)
+                       isp1760_udc_ctrl_send_status(ep, USB_DIR_IN);
+
+               if (!list_empty(&ep->queue))
+                       req = list_first_entry(&ep->queue,
+                                              struct isp1760_request, queue);
+               else
+                       req = NULL;
+       }
+
+       /*
+        * Transmit the next packet or start the next request, if any.
+        *
+        * TODO: If the endpoint is stalled the next request shouldn't be
+        * started, but what about the next packet ?
+        */
+       if (req)
+               isp1760_udc_transmit(ep, req);
+
+       spin_unlock(&udc->lock);
+
+       if (complete)
+               isp1760_udc_request_complete(ep, complete, 0);
+}
+
+static int __isp1760_udc_set_halt(struct isp1760_ep *ep, bool halt)
+{
+       struct isp1760_udc *udc = ep->udc;
+
+       dev_dbg(udc->isp->dev, "%s: %s halt on ep%02x\n", __func__,
+               halt ? "set" : "clear", ep->addr);
+
+       if (ep->desc && usb_endpoint_xfer_isoc(ep->desc)) {
+               dev_dbg(udc->isp->dev, "%s: ep%02x is isochronous\n", __func__,
+                       ep->addr);
+               return -EINVAL;
+       }
+
+       isp1760_udc_select_ep(ep);
+       isp1760_udc_write(udc, DC_CTRLFUNC, halt ? DC_STALL : 0);
+
+       if (ep->addr == 0) {
+               /* When halting the control endpoint, stall both IN and OUT. */
+               __isp1760_udc_select_ep(ep, USB_DIR_IN);
+               isp1760_udc_write(udc, DC_CTRLFUNC, halt ? DC_STALL : 0);
+       } else if (!halt) {
+               /* Reset the data PID by cycling the endpoint enable bit. */
+               u16 eptype = isp1760_udc_read(udc, DC_EPTYPE);
+
+               isp1760_udc_write(udc, DC_EPTYPE, eptype & ~DC_EPENABLE);
+               isp1760_udc_write(udc, DC_EPTYPE, eptype);
+
+               /*
+                * Disabling the endpoint emptied the transmit FIFO, fill it
+                * again if a request is pending.
+                *
+                * TODO: Does the gadget framework require synchronizatino with
+                * the TX IRQ handler ?
+                */
+               if ((ep->addr & USB_DIR_IN) && !list_empty(&ep->queue)) {
+                       struct isp1760_request *req;
+
+                       req = list_first_entry(&ep->queue,
+                                              struct isp1760_request, queue);
+                       isp1760_udc_transmit(ep, req);
+               }
+       }
+
+       ep->halted = halt;
+
+       return 0;
+}
+
+/* -----------------------------------------------------------------------------
+ * Control Endpoint
+ */
+
+static int isp1760_udc_get_status(struct isp1760_udc *udc,
+                                 const struct usb_ctrlrequest *req)
+{
+       struct isp1760_ep *ep;
+       u16 status;
+
+       if (req->wLength != cpu_to_le16(2) || req->wValue != cpu_to_le16(0))
+               return -EINVAL;
+
+       switch (req->bRequestType) {
+       case USB_DIR_IN | USB_RECIP_DEVICE:
+               status = udc->devstatus;
+               break;
+
+       case USB_DIR_IN | USB_RECIP_INTERFACE:
+               status = 0;
+               break;
+
+       case USB_DIR_IN | USB_RECIP_ENDPOINT:
+               ep = isp1760_udc_find_ep(udc, le16_to_cpu(req->wIndex));
+               if (!ep)
+                       return -EINVAL;
+
+               status = 0;
+               if (ep->halted)
+                       status |= 1 << USB_ENDPOINT_HALT;
+               break;
+
+       default:
+               return -EINVAL;
+       }
+
+       isp1760_udc_write(udc, DC_EPINDEX, DC_ENDPIDX(0) | DC_EPDIR);
+       isp1760_udc_write(udc, DC_BUFLEN, 2);
+
+       writew(cpu_to_le16(status), udc->regs + DC_DATAPORT);
+
+       isp1760_udc_write(udc, DC_CTRLFUNC, DC_DSEN);
+
+       dev_dbg(udc->isp->dev, "%s: status 0x%04x\n", __func__, status);
+
+       return 0;
+}
+
+static int isp1760_udc_set_address(struct isp1760_udc *udc, u16 addr)
+{
+       if (addr > 127) {
+               dev_dbg(udc->isp->dev, "invalid device address %u\n", addr);
+               return -EINVAL;
+       }
+
+       if (udc->gadget.state != USB_STATE_DEFAULT &&
+           udc->gadget.state != USB_STATE_ADDRESS) {
+               dev_dbg(udc->isp->dev, "can't set address in state %u\n",
+                       udc->gadget.state);
+               return -EINVAL;
+       }
+
+       usb_gadget_set_state(&udc->gadget, addr ? USB_STATE_ADDRESS :
+                            USB_STATE_DEFAULT);
+
+       isp1760_udc_write(udc, DC_ADDRESS, DC_DEVEN | addr);
+
+       spin_lock(&udc->lock);
+       isp1760_udc_ctrl_send_status(&udc->ep[0], USB_DIR_OUT);
+       spin_unlock(&udc->lock);
+
+       return 0;
+}
+
+static bool isp1760_ep0_setup_standard(struct isp1760_udc *udc,
+                                      struct usb_ctrlrequest *req)
+{
+       bool stall;
+
+       switch (req->bRequest) {
+       case USB_REQ_GET_STATUS:
+               return isp1760_udc_get_status(udc, req);
+
+       case USB_REQ_CLEAR_FEATURE:
+               switch (req->bRequestType) {
+               case USB_DIR_OUT | USB_RECIP_DEVICE: {
+                       /* TODO: Handle remote wakeup feature. */
+                       return true;
+               }
+
+               case USB_DIR_OUT | USB_RECIP_ENDPOINT: {
+                       u16 index = le16_to_cpu(req->wIndex);
+                       struct isp1760_ep *ep;
+
+                       if (req->wLength != cpu_to_le16(0) ||
+                           req->wValue != cpu_to_le16(USB_ENDPOINT_HALT))
+                               return true;
+
+                       ep = isp1760_udc_find_ep(udc, index);
+                       if (!ep)
+                               return true;
+
+                       spin_lock(&udc->lock);
+
+                       /*
+                        * If the endpoint is wedged only the gadget can clear
+                        * the halt feature. Pretend success in that case, but
+                        * keep the endpoint halted.
+                        */
+                       if (!ep->wedged)
+                               stall = __isp1760_udc_set_halt(ep, false);
+                       else
+                               stall = false;
+
+                       if (!stall)
+                               isp1760_udc_ctrl_send_status(&udc->ep[0],
+                                                            USB_DIR_OUT);
+
+                       spin_unlock(&udc->lock);
+                       return stall;
+               }
+
+               default:
+                       return true;
+               }
+               break;
+
+       case USB_REQ_SET_FEATURE:
+               switch (req->bRequestType) {
+               case USB_DIR_OUT | USB_RECIP_DEVICE: {
+                       /* TODO: Handle remote wakeup and test mode features */
+                       return true;
+               }
+
+               case USB_DIR_OUT | USB_RECIP_ENDPOINT: {
+                       u16 index = le16_to_cpu(req->wIndex);
+                       struct isp1760_ep *ep;
+
+                       if (req->wLength != cpu_to_le16(0) ||
+                           req->wValue != cpu_to_le16(USB_ENDPOINT_HALT))
+                               return true;
+
+                       ep = isp1760_udc_find_ep(udc, index);
+                       if (!ep)
+                               return true;
+
+                       spin_lock(&udc->lock);
+
+                       stall = __isp1760_udc_set_halt(ep, true);
+                       if (!stall)
+                               isp1760_udc_ctrl_send_status(&udc->ep[0],
+                                                            USB_DIR_OUT);
+
+                       spin_unlock(&udc->lock);
+                       return stall;
+               }
+
+               default:
+                       return true;
+               }
+               break;
+
+       case USB_REQ_SET_ADDRESS:
+               if (req->bRequestType != (USB_DIR_OUT | USB_RECIP_DEVICE))
+                       return true;
+
+               return isp1760_udc_set_address(udc, le16_to_cpu(req->wValue));
+
+       case USB_REQ_SET_CONFIGURATION:
+               if (req->bRequestType != (USB_DIR_OUT | USB_RECIP_DEVICE))
+                       return true;
+
+               if (udc->gadget.state != USB_STATE_ADDRESS &&
+                   udc->gadget.state != USB_STATE_CONFIGURED)
+                       return true;
+
+               stall = udc->driver->setup(&udc->gadget, req) < 0;
+               if (stall)
+                       return true;
+
+               usb_gadget_set_state(&udc->gadget, req->wValue ?
+                                    USB_STATE_CONFIGURED : USB_STATE_ADDRESS);
+
+               /*
+                * SET_CONFIGURATION (and SET_INTERFACE) must reset the halt
+                * feature on all endpoints. There is however no need to do so
+                * explicitly here as the gadget driver will disable and
+                * reenable endpoints, clearing the halt feature.
+                */
+               return false;
+
+       default:
+               return udc->driver->setup(&udc->gadget, req) < 0;
+       }
+}
+
+static void isp1760_ep0_setup(struct isp1760_udc *udc)
+{
+       union {
+               struct usb_ctrlrequest r;
+               u32 data[2];
+       } req;
+       unsigned int count;
+       bool stall = false;
+
+       spin_lock(&udc->lock);
+
+       isp1760_udc_write(udc, DC_EPINDEX, DC_EP0SETUP);
+
+       count = isp1760_udc_read(udc, DC_BUFLEN) & DC_DATACOUNT_MASK;
+       if (count != sizeof(req)) {
+               spin_unlock(&udc->lock);
+
+               dev_err(udc->isp->dev, "invalid length %u for setup packet\n",
+                       count);
+
+               isp1760_udc_ctrl_send_stall(&udc->ep[0]);
+               return;
+       }
+
+       req.data[0] = isp1760_udc_read(udc, DC_DATAPORT);
+       req.data[1] = isp1760_udc_read(udc, DC_DATAPORT);
+
+       if (udc->ep0_state != ISP1760_CTRL_SETUP) {
+               spin_unlock(&udc->lock);
+               dev_dbg(udc->isp->dev, "unexpected SETUP packet\n");
+               return;
+       }
+
+       /* Move to the data stage. */
+       if (!req.r.wLength)
+               udc->ep0_state = ISP1760_CTRL_STATUS;
+       else if (req.r.bRequestType & USB_DIR_IN)
+               udc->ep0_state = ISP1760_CTRL_DATA_IN;
+       else
+               udc->ep0_state = ISP1760_CTRL_DATA_OUT;
+
+       udc->ep0_dir = req.r.bRequestType & USB_DIR_IN;
+       udc->ep0_length = le16_to_cpu(req.r.wLength);
+
+       spin_unlock(&udc->lock);
+
+       dev_dbg(udc->isp->dev,
+               "%s: bRequestType 0x%02x bRequest 0x%02x wValue 0x%04x wIndex 0x%04x wLength 0x%04x\n",
+               __func__, req.r.bRequestType, req.r.bRequest,
+               le16_to_cpu(req.r.wValue), le16_to_cpu(req.r.wIndex),
+               le16_to_cpu(req.r.wLength));
+
+       if ((req.r.bRequestType & USB_TYPE_MASK) == USB_TYPE_STANDARD)
+               stall = isp1760_ep0_setup_standard(udc, &req.r);
+       else
+               stall = udc->driver->setup(&udc->gadget, &req.r) < 0;
+
+       if (stall)
+               isp1760_udc_ctrl_send_stall(&udc->ep[0]);
+}
+
+/* -----------------------------------------------------------------------------
+ * Gadget Endpoint Operations
+ */
+
+static int isp1760_ep_enable(struct usb_ep *ep,
+                            const struct usb_endpoint_descriptor *desc)
+{
+       struct isp1760_ep *uep = ep_to_udc_ep(ep);
+       struct isp1760_udc *udc = uep->udc;
+       unsigned long flags;
+       unsigned int type;
+
+       dev_dbg(uep->udc->isp->dev, "%s\n", __func__);
+
+       /*
+        * Validate the descriptor. The control endpoint can't be enabled
+        * manually.
+        */
+       if (desc->bDescriptorType != USB_DT_ENDPOINT ||
+           desc->bEndpointAddress == 0 ||
+           desc->bEndpointAddress != uep->addr ||
+           le16_to_cpu(desc->wMaxPacketSize) > ep->maxpacket) {
+               dev_dbg(udc->isp->dev,
+                       "%s: invalid descriptor type %u addr %02x ep addr %02x max packet size %u/%u\n",
+                       __func__, desc->bDescriptorType,
+                       desc->bEndpointAddress, uep->addr,
+                       le16_to_cpu(desc->wMaxPacketSize), ep->maxpacket);
+               return -EINVAL;
+       }
+
+       switch (usb_endpoint_type(desc)) {
+       case USB_ENDPOINT_XFER_ISOC:
+               type = DC_ENDPTYP_ISOC;
+               break;
+       case USB_ENDPOINT_XFER_BULK:
+               type = DC_ENDPTYP_BULK;
+               break;
+       case USB_ENDPOINT_XFER_INT:
+               type = DC_ENDPTYP_INTERRUPT;
+               break;
+       case USB_ENDPOINT_XFER_CONTROL:
+       default:
+               dev_dbg(udc->isp->dev, "%s: control endpoints unsupported\n",
+                       __func__);
+               return -EINVAL;
+       }
+
+       spin_lock_irqsave(&udc->lock, flags);
+
+       uep->desc = desc;
+       uep->maxpacket = le16_to_cpu(desc->wMaxPacketSize);
+       uep->rx_pending = false;
+       uep->halted = false;
+       uep->wedged = false;
+
+       isp1760_udc_select_ep(uep);
+       isp1760_udc_write(udc, DC_EPMAXPKTSZ, uep->maxpacket);
+       isp1760_udc_write(udc, DC_BUFLEN, uep->maxpacket);
+       isp1760_udc_write(udc, DC_EPTYPE, DC_EPENABLE | type);
+
+       spin_unlock_irqrestore(&udc->lock, flags);
+
+       return 0;
+}
+
+static int isp1760_ep_disable(struct usb_ep *ep)
+{
+       struct isp1760_ep *uep = ep_to_udc_ep(ep);
+       struct isp1760_udc *udc = uep->udc;
+       struct isp1760_request *req, *nreq;
+       LIST_HEAD(req_list);
+       unsigned long flags;
+
+       dev_dbg(udc->isp->dev, "%s\n", __func__);
+
+       spin_lock_irqsave(&udc->lock, flags);
+
+       if (!uep->desc) {
+               dev_dbg(udc->isp->dev, "%s: endpoint not enabled\n", __func__);
+               spin_unlock_irqrestore(&udc->lock, flags);
+               return -EINVAL;
+       }
+
+       uep->desc = NULL;
+       uep->maxpacket = 0;
+
+       isp1760_udc_select_ep(uep);
+       isp1760_udc_write(udc, DC_EPTYPE, 0);
+
+       /* TODO Synchronize with the IRQ handler */
+
+       list_splice_init(&uep->queue, &req_list);
+
+       spin_unlock_irqrestore(&udc->lock, flags);
+
+       list_for_each_entry_safe(req, nreq, &req_list, queue) {
+               list_del(&req->queue);
+               isp1760_udc_request_complete(uep, req, -ESHUTDOWN);
+       }
+
+       return 0;
+}
+
+static struct usb_request *isp1760_ep_alloc_request(struct usb_ep *ep,
+                                                   gfp_t gfp_flags)
+{
+       struct isp1760_request *req;
+
+       req = kzalloc(sizeof(*req), gfp_flags);
+
+       return &req->req;
+}
+
+static void isp1760_ep_free_request(struct usb_ep *ep, struct usb_request *_req)
+{
+       struct isp1760_request *req = req_to_udc_req(_req);
+
+       kfree(req);
+}
+
+static int isp1760_ep_queue(struct usb_ep *ep, struct usb_request *_req,
+                           gfp_t gfp_flags)
+{
+       struct isp1760_request *req = req_to_udc_req(_req);
+       struct isp1760_ep *uep = ep_to_udc_ep(ep);
+       struct isp1760_udc *udc = uep->udc;
+       bool complete = false;
+       unsigned long flags;
+       int ret = 0;
+
+       _req->status = -EINPROGRESS;
+       _req->actual = 0;
+
+       spin_lock_irqsave(&udc->lock, flags);
+
+       dev_dbg(udc->isp->dev,
+               "%s: req %p (%u bytes%s) ep %p(0x%02x)\n", __func__, _req,
+               _req->length, _req->zero ? " (zlp)" : "", uep, uep->addr);
+
+       req->ep = uep;
+
+       if (uep->addr == 0) {
+               if (_req->length != udc->ep0_length &&
+                   udc->ep0_state != ISP1760_CTRL_DATA_IN) {
+                       dev_dbg(udc->isp->dev,
+                               "%s: invalid length %u for req %p\n",
+                               __func__, _req->length, req);
+                       ret = -EINVAL;
+                       goto done;
+               }
+
+               switch (udc->ep0_state) {
+               case ISP1760_CTRL_DATA_IN:
+                       dev_dbg(udc->isp->dev, "%s: transmitting req %p\n",
+                               __func__, req);
+
+                       list_add_tail(&req->queue, &uep->queue);
+                       isp1760_udc_transmit(uep, req);
+                       break;
+
+               case ISP1760_CTRL_DATA_OUT:
+                       list_add_tail(&req->queue, &uep->queue);
+                       __isp1760_udc_select_ep(uep, USB_DIR_OUT);
+                       isp1760_udc_write(udc, DC_CTRLFUNC, DC_DSEN);
+                       break;
+
+               case ISP1760_CTRL_STATUS:
+                       complete = true;
+                       break;
+
+               default:
+                       dev_dbg(udc->isp->dev, "%s: invalid ep0 state\n",
+                               __func__);
+                       ret = -EINVAL;
+                       break;
+               }
+       } else if (uep->desc) {
+               bool empty = list_empty(&uep->queue);
+
+               list_add_tail(&req->queue, &uep->queue);
+               if ((uep->addr & USB_DIR_IN) && !uep->halted && empty)
+                       isp1760_udc_transmit(uep, req);
+               else if (!(uep->addr & USB_DIR_IN) && uep->rx_pending)
+                       complete = isp1760_udc_receive(uep, req);
+       } else {
+               dev_dbg(udc->isp->dev,
+                       "%s: can't queue request to disabled ep%02x\n",
+                       __func__, uep->addr);
+               ret = -ESHUTDOWN;
+       }
+
+done:
+       if (ret < 0)
+               req->ep = NULL;
+
+       spin_unlock_irqrestore(&udc->lock, flags);
+
+       if (complete)
+               isp1760_udc_request_complete(uep, req, 0);
+
+       return ret;
+}
+
+static int isp1760_ep_dequeue(struct usb_ep *ep, struct usb_request *_req)
+{
+       struct isp1760_request *req = req_to_udc_req(_req);
+       struct isp1760_ep *uep = ep_to_udc_ep(ep);
+       struct isp1760_udc *udc = uep->udc;
+       unsigned long flags;
+
+       dev_dbg(uep->udc->isp->dev, "%s(ep%02x)\n", __func__, uep->addr);
+
+       spin_lock_irqsave(&udc->lock, flags);
+
+       if (req->ep != uep)
+               req = NULL;
+       else
+               list_del(&req->queue);
+
+       spin_unlock_irqrestore(&udc->lock, flags);
+
+       if (!req)
+               return -EINVAL;
+
+       isp1760_udc_request_complete(uep, req, -ECONNRESET);
+       return 0;
+}
+
+static int __isp1760_ep_set_halt(struct isp1760_ep *uep, bool stall, bool wedge)
+{
+       struct isp1760_udc *udc = uep->udc;
+       int ret;
+
+       if (!uep->addr) {
+               /*
+                * Halting the control endpoint is only valid as a delayed error
+                * response to a SETUP packet. Make sure EP0 is in the right
+                * stage and that the gadget isn't trying to clear the halt
+                * condition.
+                */
+               if (WARN_ON(udc->ep0_state == ISP1760_CTRL_SETUP || !stall ||
+                            wedge)) {
+                       return -EINVAL;
+               }
+       }
+
+       if (uep->addr && !uep->desc) {
+               dev_dbg(udc->isp->dev, "%s: ep%02x is disabled\n", __func__,
+                       uep->addr);
+               return -EINVAL;
+       }
+
+       if (uep->addr & USB_DIR_IN) {
+               /* Refuse to halt IN endpoints with active transfers. */
+               if (!list_empty(&uep->queue)) {
+                       dev_dbg(udc->isp->dev,
+                               "%s: ep%02x has request pending\n", __func__,
+                               uep->addr);
+                       return -EAGAIN;
+               }
+       }
+
+       ret = __isp1760_udc_set_halt(uep, stall);
+       if (ret < 0)
+               return ret;
+
+       if (!uep->addr) {
+               /*
+                * Stalling EP0 completes the control transaction, move back to
+                * the SETUP state.
+                */
+               udc->ep0_state = ISP1760_CTRL_SETUP;
+               return 0;
+       }
+
+       if (wedge)
+               uep->wedged = true;
+       else if (!stall)
+               uep->wedged = false;
+
+       return 0;
+}
+
+static int isp1760_ep_set_halt(struct usb_ep *ep, int value)
+{
+       struct isp1760_ep *uep = ep_to_udc_ep(ep);
+       unsigned long flags;
+       int ret;
+
+       dev_dbg(uep->udc->isp->dev, "%s: %s halt on ep%02x\n", __func__,
+               value ? "set" : "clear", uep->addr);
+
+       spin_lock_irqsave(&uep->udc->lock, flags);
+       ret = __isp1760_ep_set_halt(uep, value, false);
+       spin_unlock_irqrestore(&uep->udc->lock, flags);
+
+       return ret;
+}
+
+static int isp1760_ep_set_wedge(struct usb_ep *ep)
+{
+       struct isp1760_ep *uep = ep_to_udc_ep(ep);
+       unsigned long flags;
+       int ret;
+
+       dev_dbg(uep->udc->isp->dev, "%s: set wedge on ep%02x)\n", __func__,
+               uep->addr);
+
+       spin_lock_irqsave(&uep->udc->lock, flags);
+       ret = __isp1760_ep_set_halt(uep, true, true);
+       spin_unlock_irqrestore(&uep->udc->lock, flags);
+
+       return ret;
+}
+
+static void isp1760_ep_fifo_flush(struct usb_ep *ep)
+{
+       struct isp1760_ep *uep = ep_to_udc_ep(ep);
+       struct isp1760_udc *udc = uep->udc;
+       unsigned long flags;
+
+       spin_lock_irqsave(&udc->lock, flags);
+
+       isp1760_udc_select_ep(uep);
+
+       /*
+        * Set the CLBUF bit twice to flush both buffers in case double
+        * buffering is enabled.
+        */
+       isp1760_udc_write(udc, DC_CTRLFUNC, DC_CLBUF);
+       isp1760_udc_write(udc, DC_CTRLFUNC, DC_CLBUF);
+
+       spin_unlock_irqrestore(&udc->lock, flags);
+}
+
+static const struct usb_ep_ops isp1760_ep_ops = {
+       .enable = isp1760_ep_enable,
+       .disable = isp1760_ep_disable,
+       .alloc_request = isp1760_ep_alloc_request,
+       .free_request = isp1760_ep_free_request,
+       .queue = isp1760_ep_queue,
+       .dequeue = isp1760_ep_dequeue,
+       .set_halt = isp1760_ep_set_halt,
+       .set_wedge = isp1760_ep_set_wedge,
+       .fifo_flush = isp1760_ep_fifo_flush,
+};
+
+/* -----------------------------------------------------------------------------
+ * Device States
+ */
+
+/* Called with the UDC spinlock held. */
+static void isp1760_udc_connect(struct isp1760_udc *udc)
+{
+       usb_gadget_set_state(&udc->gadget, USB_STATE_POWERED);
+       mod_timer(&udc->vbus_timer, jiffies + ISP1760_VBUS_POLL_INTERVAL);
+}
+
+/* Called with the UDC spinlock held. */
+static void isp1760_udc_disconnect(struct isp1760_udc *udc)
+{
+       if (udc->gadget.state < USB_STATE_POWERED)
+               return;
+
+       dev_dbg(udc->isp->dev, "Device disconnected in state %u\n",
+                udc->gadget.state);
+
+       udc->gadget.speed = USB_SPEED_UNKNOWN;
+       usb_gadget_set_state(&udc->gadget, USB_STATE_ATTACHED);
+
+       if (udc->driver->disconnect)
+               udc->driver->disconnect(&udc->gadget);
+
+       del_timer(&udc->vbus_timer);
+
+       /* TODO Reset all endpoints ? */
+}
+
+static void isp1760_udc_init_hw(struct isp1760_udc *udc)
+{
+       /*
+        * The device controller currently shares its interrupt with the host
+        * controller, the DC_IRQ polarity and signaling mode are ignored. Set
+        * the to active-low level-triggered.
+        *
+        * Configure the control, in and out pipes to generate interrupts on
+        * ACK tokens only (and NYET for the out pipe). The default
+        * configuration also generates an interrupt on the first NACK token.
+        */
+       isp1760_udc_write(udc, DC_INTCONF, DC_CDBGMOD_ACK | DC_DDBGMODIN_ACK |
+                         DC_DDBGMODOUT_ACK_NYET);
+
+       isp1760_udc_write(udc, DC_INTENABLE, DC_IEPRXTX(7) | DC_IEPRXTX(6) |
+                         DC_IEPRXTX(5) | DC_IEPRXTX(4) | DC_IEPRXTX(3) |
+                         DC_IEPRXTX(2) | DC_IEPRXTX(1) | DC_IEPRXTX(0) |
+                         DC_IEP0SETUP | DC_IEVBUS | DC_IERESM | DC_IESUSP |
+                         DC_IEHS_STA | DC_IEBRST);
+
+       if (udc->connected)
+               isp1760_set_pullup(udc->isp, true);
+
+       isp1760_udc_write(udc, DC_ADDRESS, DC_DEVEN);
+}
+
+static void isp1760_udc_reset(struct isp1760_udc *udc)
+{
+       unsigned long flags;
+
+       spin_lock_irqsave(&udc->lock, flags);
+
+       /*
+        * The bus reset has reset most registers to their default value,
+        * reinitialize the UDC hardware.
+        */
+       isp1760_udc_init_hw(udc);
+
+       udc->ep0_state = ISP1760_CTRL_SETUP;
+       udc->gadget.speed = USB_SPEED_FULL;
+
+       usb_gadget_udc_reset(&udc->gadget, udc->driver);
+
+       spin_unlock_irqrestore(&udc->lock, flags);
+}
+
+static void isp1760_udc_suspend(struct isp1760_udc *udc)
+{
+       if (udc->gadget.state < USB_STATE_DEFAULT)
+               return;
+
+       if (udc->driver->suspend)
+               udc->driver->suspend(&udc->gadget);
+}
+
+static void isp1760_udc_resume(struct isp1760_udc *udc)
+{
+       if (udc->gadget.state < USB_STATE_DEFAULT)
+               return;
+
+       if (udc->driver->resume)
+               udc->driver->resume(&udc->gadget);
+}
+
+/* -----------------------------------------------------------------------------
+ * Gadget Operations
+ */
+
+static int isp1760_udc_get_frame(struct usb_gadget *gadget)
+{
+       struct isp1760_udc *udc = gadget_to_udc(gadget);
+
+       return isp1760_udc_read(udc, DC_FRAMENUM) & ((1 << 11) - 1);
+}
+
+static int isp1760_udc_wakeup(struct usb_gadget *gadget)
+{
+       struct isp1760_udc *udc = gadget_to_udc(gadget);
+
+       dev_dbg(udc->isp->dev, "%s\n", __func__);
+       return -ENOTSUPP;
+}
+
+static int isp1760_udc_set_selfpowered(struct usb_gadget *gadget,
+                                      int is_selfpowered)
+{
+       struct isp1760_udc *udc = gadget_to_udc(gadget);
+
+       if (is_selfpowered)
+               udc->devstatus |= 1 << USB_DEVICE_SELF_POWERED;
+       else
+               udc->devstatus &= ~(1 << USB_DEVICE_SELF_POWERED);
+
+       return 0;
+}
+
+static int isp1760_udc_pullup(struct usb_gadget *gadget, int is_on)
+{
+       struct isp1760_udc *udc = gadget_to_udc(gadget);
+
+       isp1760_set_pullup(udc->isp, is_on);
+       udc->connected = is_on;
+
+       return 0;
+}
+
+static int isp1760_udc_start(struct usb_gadget *gadget,
+                            struct usb_gadget_driver *driver)
+{
+       struct isp1760_udc *udc = gadget_to_udc(gadget);
+
+       /* The hardware doesn't support low speed. */
+       if (driver->max_speed < USB_SPEED_FULL) {
+               dev_err(udc->isp->dev, "Invalid gadget driver\n");
+               return -EINVAL;
+       }
+
+       spin_lock(&udc->lock);
+
+       if (udc->driver) {
+               dev_err(udc->isp->dev, "UDC already has a gadget driver\n");
+               spin_unlock(&udc->lock);
+               return -EBUSY;
+       }
+
+       udc->driver = driver;
+
+       spin_unlock(&udc->lock);
+
+       dev_dbg(udc->isp->dev, "starting UDC with driver %s\n",
+               driver->function);
+
+       udc->devstatus = 0;
+       udc->connected = true;
+
+       usb_gadget_set_state(&udc->gadget, USB_STATE_ATTACHED);
+
+       /* DMA isn't supported yet, don't enable the DMA clock. */
+       isp1760_udc_write(udc, DC_MODE, DC_GLINTENA);
+
+       isp1760_udc_init_hw(udc);
+
+       dev_dbg(udc->isp->dev, "UDC started with driver %s\n",
+               driver->function);
+
+       return 0;
+}
+
+static int isp1760_udc_stop(struct usb_gadget *gadget)
+{
+       struct isp1760_udc *udc = gadget_to_udc(gadget);
+
+       dev_dbg(udc->isp->dev, "%s\n", __func__);
+
+       del_timer_sync(&udc->vbus_timer);
+
+       isp1760_udc_write(udc, DC_MODE, 0);
+
+       spin_lock(&udc->lock);
+       udc->driver = NULL;
+       spin_unlock(&udc->lock);
+
+       return 0;
+}
+
+static struct usb_gadget_ops isp1760_udc_ops = {
+       .get_frame = isp1760_udc_get_frame,
+       .wakeup = isp1760_udc_wakeup,
+       .set_selfpowered = isp1760_udc_set_selfpowered,
+       .pullup = isp1760_udc_pullup,
+       .udc_start = isp1760_udc_start,
+       .udc_stop = isp1760_udc_stop,
+};
+
+/* -----------------------------------------------------------------------------
+ * Interrupt Handling
+ */
+
+static irqreturn_t isp1760_udc_irq(int irq, void *dev)
+{
+       struct isp1760_udc *udc = dev;
+       unsigned int i;
+       u32 status;
+
+       status = isp1760_udc_read(udc, DC_INTERRUPT)
+              & isp1760_udc_read(udc, DC_INTENABLE);
+       isp1760_udc_write(udc, DC_INTERRUPT, status);
+
+       if (status & DC_IEVBUS) {
+               dev_dbg(udc->isp->dev, "%s(VBUS)\n", __func__);
+               /* The VBUS interrupt is only triggered when VBUS appears. */
+               spin_lock(&udc->lock);
+               isp1760_udc_connect(udc);
+               spin_unlock(&udc->lock);
+       }
+
+       if (status & DC_IEBRST) {
+               dev_dbg(udc->isp->dev, "%s(BRST)\n", __func__);
+
+               isp1760_udc_reset(udc);
+       }
+
+       for (i = 0; i <= 7; ++i) {
+               struct isp1760_ep *ep = &udc->ep[i*2];
+
+               if (status & DC_IEPTX(i)) {
+                       dev_dbg(udc->isp->dev, "%s(EPTX%u)\n", __func__, i);
+                       isp1760_ep_tx_complete(ep);
+               }
+
+               if (status & DC_IEPRX(i)) {
+                       dev_dbg(udc->isp->dev, "%s(EPRX%u)\n", __func__, i);
+                       isp1760_ep_rx_ready(i ? ep - 1 : ep);
+               }
+       }
+
+       if (status & DC_IEP0SETUP) {
+               dev_dbg(udc->isp->dev, "%s(EP0SETUP)\n", __func__);
+
+               isp1760_ep0_setup(udc);
+       }
+
+       if (status & DC_IERESM) {
+               dev_dbg(udc->isp->dev, "%s(RESM)\n", __func__);
+               isp1760_udc_resume(udc);
+       }
+
+       if (status & DC_IESUSP) {
+               dev_dbg(udc->isp->dev, "%s(SUSP)\n", __func__);
+
+               spin_lock(&udc->lock);
+               if (!(isp1760_udc_read(udc, DC_MODE) & DC_VBUSSTAT))
+                       isp1760_udc_disconnect(udc);
+               else
+                       isp1760_udc_suspend(udc);
+               spin_unlock(&udc->lock);
+       }
+
+       if (status & DC_IEHS_STA) {
+               dev_dbg(udc->isp->dev, "%s(HS_STA)\n", __func__);
+               udc->gadget.speed = USB_SPEED_HIGH;
+       }
+
+       return status ? IRQ_HANDLED : IRQ_NONE;
+}
+
+static void isp1760_udc_vbus_poll(unsigned long data)
+{
+       struct isp1760_udc *udc = (struct isp1760_udc *)data;
+       unsigned long flags;
+
+       spin_lock_irqsave(&udc->lock, flags);
+
+       if (!(isp1760_udc_read(udc, DC_MODE) & DC_VBUSSTAT))
+               isp1760_udc_disconnect(udc);
+       else if (udc->gadget.state >= USB_STATE_POWERED)
+               mod_timer(&udc->vbus_timer,
+                         jiffies + ISP1760_VBUS_POLL_INTERVAL);
+
+       spin_unlock_irqrestore(&udc->lock, flags);
+}
+
+/* -----------------------------------------------------------------------------
+ * Registration
+ */
+
+static void isp1760_udc_init_eps(struct isp1760_udc *udc)
+{
+       unsigned int i;
+
+       INIT_LIST_HEAD(&udc->gadget.ep_list);
+
+       for (i = 0; i < ARRAY_SIZE(udc->ep); ++i) {
+               struct isp1760_ep *ep = &udc->ep[i];
+               unsigned int ep_num = (i + 1) / 2;
+               bool is_in = !(i & 1);
+
+               ep->udc = udc;
+
+               INIT_LIST_HEAD(&ep->queue);
+
+               ep->addr = (ep_num && is_in ? USB_DIR_IN : USB_DIR_OUT)
+                        | ep_num;
+               ep->desc = NULL;
+
+               sprintf(ep->name, "ep%u%s", ep_num,
+                       ep_num ? (is_in ? "in" : "out") : "");
+
+               ep->ep.ops = &isp1760_ep_ops;
+               ep->ep.name = ep->name;
+
+               /*
+                * Hardcode the maximum packet sizes for now, to 64 bytes for
+                * the control endpoint and 512 bytes for all other endpoints.
+                * This fits in the 8kB FIFO without double-buffering.
+                */
+               if (ep_num == 0) {
+                       ep->ep.maxpacket = 64;
+                       ep->maxpacket = 64;
+                       udc->gadget.ep0 = &ep->ep;
+               } else {
+                       ep->ep.maxpacket = 512;
+                       ep->maxpacket = 0;
+                       list_add_tail(&ep->ep.ep_list, &udc->gadget.ep_list);
+               }
+       }
+}
+
+static int isp1760_udc_init(struct isp1760_udc *udc)
+{
+       u16 scratch;
+       u32 chipid;
+
+       /*
+        * Check that the controller is present by writing to the scratch
+        * register, modifying the bus pattern by reading from the chip ID
+        * register, and reading the scratch register value back. The chip ID
+        * and scratch register contents must match the expected values.
+        */
+       isp1760_udc_write(udc, DC_SCRATCH, 0xbabe);
+       chipid = isp1760_udc_read(udc, DC_CHIPID);
+       scratch = isp1760_udc_read(udc, DC_SCRATCH);
+
+       if (scratch != 0xbabe) {
+               dev_err(udc->isp->dev,
+                       "udc: scratch test failed (0x%04x/0x%08x)\n",
+                       scratch, chipid);
+               return -ENODEV;
+       }
+
+       if (chipid != 0x00011582) {
+               dev_err(udc->isp->dev, "udc: invalid chip ID 0x%08x\n", chipid);
+               return -ENODEV;
+       }
+
+       /* Reset the device controller. */
+       isp1760_udc_write(udc, DC_MODE, DC_SFRESET);
+       usleep_range(10000, 11000);
+       isp1760_udc_write(udc, DC_MODE, 0);
+       usleep_range(10000, 11000);
+
+       return 0;
+}
+
+int isp1760_udc_register(struct isp1760_device *isp, int irq,
+                        unsigned long irqflags)
+{
+       struct isp1760_udc *udc = &isp->udc;
+       const char *devname;
+       int ret;
+
+       udc->irq = -1;
+       udc->isp = isp;
+       udc->regs = isp->regs;
+
+       spin_lock_init(&udc->lock);
+       setup_timer(&udc->vbus_timer, isp1760_udc_vbus_poll,
+                   (unsigned long)udc);
+
+       ret = isp1760_udc_init(udc);
+       if (ret < 0)
+               return ret;
+
+       devname = dev_name(isp->dev);
+       udc->irqname = kmalloc(strlen(devname) + 7, GFP_KERNEL);
+       if (!udc->irqname)
+               return -ENOMEM;
+
+       sprintf(udc->irqname, "%s (udc)", devname);
+
+       ret = request_irq(irq, isp1760_udc_irq, IRQF_SHARED | IRQF_DISABLED |
+                         irqflags, udc->irqname, udc);
+       if (ret < 0)
+               goto error;
+
+       udc->irq = irq;
+
+       /*
+        * Initialize the gadget static fields and register its device. Gadget
+        * fields that vary during the life time of the gadget are initialized
+        * by the UDC core.
+        */
+       udc->gadget.ops = &isp1760_udc_ops;
+       udc->gadget.speed = USB_SPEED_UNKNOWN;
+       udc->gadget.max_speed = USB_SPEED_HIGH;
+       udc->gadget.name = "isp1761_udc";
+
+       isp1760_udc_init_eps(udc);
+
+       ret = usb_add_gadget_udc(isp->dev, &udc->gadget);
+       if (ret < 0)
+               goto error;
+
+       return 0;
+
+error:
+       if (udc->irq >= 0)
+               free_irq(udc->irq, udc);
+       kfree(udc->irqname);
+
+       return ret;
+}
+
+void isp1760_udc_unregister(struct isp1760_device *isp)
+{
+       struct isp1760_udc *udc = &isp->udc;
+
+       if (!udc->isp)
+               return;
+
+       usb_del_gadget_udc(&udc->gadget);
+
+       free_irq(udc->irq, udc);
+       kfree(udc->irqname);
+}
diff --git a/drivers/usb/isp1760/isp1760-udc.h b/drivers/usb/isp1760/isp1760-udc.h
new file mode 100644 (file)
index 0000000..26899ed
--- /dev/null
@@ -0,0 +1,106 @@
+/*
+ * Driver for the NXP ISP1761 device controller
+ *
+ * Copyright 2014 Ideas on Board Oy
+ *
+ * Contacts:
+ *     Laurent Pinchart <laurent.pinchart@ideasonboard.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ */
+
+#ifndef _ISP1760_UDC_H_
+#define _ISP1760_UDC_H_
+
+#include <linux/ioport.h>
+#include <linux/list.h>
+#include <linux/spinlock.h>
+#include <linux/timer.h>
+#include <linux/usb/gadget.h>
+
+struct isp1760_device;
+struct isp1760_udc;
+
+enum isp1760_ctrl_state {
+       ISP1760_CTRL_SETUP,             /* Waiting for a SETUP transaction */
+       ISP1760_CTRL_DATA_IN,           /* Setup received, data IN stage */
+       ISP1760_CTRL_DATA_OUT,          /* Setup received, data OUT stage */
+       ISP1760_CTRL_STATUS,            /* 0-length request in status stage */
+};
+
+struct isp1760_ep {
+       struct isp1760_udc *udc;
+       struct usb_ep ep;
+
+       struct list_head queue;
+
+       unsigned int addr;
+       unsigned int maxpacket;
+       char name[7];
+
+       const struct usb_endpoint_descriptor *desc;
+
+       bool rx_pending;
+       bool halted;
+       bool wedged;
+};
+
+/**
+ * struct isp1760_udc - UDC state information
+ * irq: IRQ number
+ * irqname: IRQ name (as passed to request_irq)
+ * regs: Base address of the UDC registers
+ * driver: Gadget driver
+ * gadget: Gadget device
+ * lock: Protects driver, vbus_timer, ep, ep0_*, DC_EPINDEX register
+ * ep: Array of endpoints
+ * ep0_state: Control request state for endpoint 0
+ * ep0_dir: Direction of the current control request
+ * ep0_length: Length of the current control request
+ * connected: Tracks gadget driver bus connection state
+ */
+struct isp1760_udc {
+#ifdef CONFIG_USB_ISP1761_UDC
+       struct isp1760_device *isp;
+
+       int irq;
+       char *irqname;
+       void __iomem *regs;
+
+       struct usb_gadget_driver *driver;
+       struct usb_gadget gadget;
+
+       spinlock_t lock;
+       struct timer_list vbus_timer;
+
+       struct isp1760_ep ep[15];
+
+       enum isp1760_ctrl_state ep0_state;
+       u8 ep0_dir;
+       u16 ep0_length;
+
+       bool connected;
+
+       unsigned int devstatus;
+#endif
+};
+
+#ifdef CONFIG_USB_ISP1761_UDC
+int isp1760_udc_register(struct isp1760_device *isp, int irq,
+                        unsigned long irqflags);
+void isp1760_udc_unregister(struct isp1760_device *isp);
+#else
+static inline int isp1760_udc_register(struct isp1760_device *isp, int irq,
+                                      unsigned long irqflags)
+{
+       return 0;
+}
+
+static inline void isp1760_udc_unregister(struct isp1760_device *isp)
+{
+}
+#endif
+
+#endif
index b005010..14e1628 100644 (file)
@@ -63,11 +63,13 @@ comment "Platform Glue Layer"
 config USB_MUSB_DAVINCI
        tristate "DaVinci"
        depends on ARCH_DAVINCI_DMx
+       depends on NOP_USB_XCEIV
        depends on BROKEN
 
 config USB_MUSB_DA8XX
        tristate "DA8xx/OMAP-L1x"
        depends on ARCH_DAVINCI_DA8XX
+       depends on NOP_USB_XCEIV
        depends on BROKEN
 
 config USB_MUSB_TUSB6010
@@ -77,12 +79,13 @@ config USB_MUSB_TUSB6010
 
 config USB_MUSB_OMAP2PLUS
        tristate "OMAP2430 and onwards"
-       depends on ARCH_OMAP2PLUS && USB
+       depends on ARCH_OMAP2PLUS && USB && OMAP_CONTROL_PHY
        select GENERIC_PHY
 
 config USB_MUSB_AM35X
        tristate "AM35x"
        depends on ARCH_OMAP
+       depends on NOP_USB_XCEIV
 
 config USB_MUSB_DSPS
        tristate "TI DSPS platforms"
@@ -93,6 +96,7 @@ config USB_MUSB_DSPS
 config USB_MUSB_BLACKFIN
        tristate "Blackfin"
        depends on (BF54x && !BF544) || (BF52x && ! BF522 && !BF523)
+       depends on NOP_USB_XCEIV
 
 config USB_MUSB_UX500
        tristate "Ux500 platforms"
@@ -100,6 +104,7 @@ config USB_MUSB_UX500
 
 config USB_MUSB_JZ4740
        tristate "JZ4740"
+       depends on NOP_USB_XCEIV
        depends on MACH_JZ4740 || COMPILE_TEST
        depends on USB_MUSB_GADGET
        depends on USB_OTG_BLACKLIST_HUB
index 1782501..6123b74 100644 (file)
@@ -608,7 +608,7 @@ static SIMPLE_DEV_PM_OPS(bfin_pm_ops, bfin_suspend, bfin_resume);
 
 static struct platform_driver bfin_driver = {
        .probe          = bfin_probe,
-       .remove         = __exit_p(bfin_remove),
+       .remove         = bfin_remove,
        .driver         = {
                .name   = "musb-blackfin",
                .pm     = &bfin_pm_ops,
index 34cce3e..e6f4cbf 100644 (file)
@@ -567,6 +567,7 @@ static irqreturn_t musb_stage0_irq(struct musb *musb, u8 int_usb,
 
                                musb->xceiv->otg->state = OTG_STATE_A_HOST;
                                musb->is_active = 1;
+                               musb_host_resume_root_hub(musb);
                                break;
                        case OTG_STATE_B_WAIT_ACON:
                                musb->xceiv->otg->state = OTG_STATE_B_PERIPHERAL;
@@ -2500,6 +2501,12 @@ static int musb_runtime_resume(struct device *dev)
                musb_restore_context(musb);
        first = 0;
 
+       if (musb->need_finish_resume) {
+               musb->need_finish_resume = 0;
+               schedule_delayed_work(&musb->finish_resume_work,
+                               msecs_to_jiffies(20));
+       }
+
        return 0;
 }
 
index c39a16a..be84562 100644 (file)
@@ -9,9 +9,9 @@
 
 #define RNDIS_REG(x) (0x80 + ((x - 1) * 4))
 
-#define EP_MODE_AUTOREG_NONE           0
-#define EP_MODE_AUTOREG_ALL_NEOP       1
-#define EP_MODE_AUTOREG_ALWAYS         3
+#define EP_MODE_AUTOREQ_NONE           0
+#define EP_MODE_AUTOREQ_ALL_NEOP       1
+#define EP_MODE_AUTOREQ_ALWAYS         3
 
 #define EP_MODE_DMA_TRANSPARENT                0
 #define EP_MODE_DMA_RNDIS              1
@@ -404,19 +404,19 @@ static bool cppi41_configure_channel(struct dma_channel *channel,
 
                        /* auto req */
                        cppi41_set_autoreq_mode(cppi41_channel,
-                                       EP_MODE_AUTOREG_ALL_NEOP);
+                                       EP_MODE_AUTOREQ_ALL_NEOP);
                } else {
                        musb_writel(musb->ctrl_base,
                                        RNDIS_REG(cppi41_channel->port_num), 0);
                        cppi41_set_dma_mode(cppi41_channel,
                                        EP_MODE_DMA_TRANSPARENT);
                        cppi41_set_autoreq_mode(cppi41_channel,
-                                       EP_MODE_AUTOREG_NONE);
+                                       EP_MODE_AUTOREQ_NONE);
                }
        } else {
                /* fallback mode */
                cppi41_set_dma_mode(cppi41_channel, EP_MODE_DMA_TRANSPARENT);
-               cppi41_set_autoreq_mode(cppi41_channel, EP_MODE_AUTOREG_NONE);
+               cppi41_set_autoreq_mode(cppi41_channel, EP_MODE_AUTOREQ_NONE);
                len = min_t(u32, packet_sz, len);
        }
        cppi41_channel->prog_len = len;
@@ -549,10 +549,15 @@ static int cppi41_dma_channel_abort(struct dma_channel *channel)
                csr &= ~MUSB_TXCSR_DMAENAB;
                musb_writew(epio, MUSB_TXCSR, csr);
        } else {
+               cppi41_set_autoreq_mode(cppi41_channel, EP_MODE_AUTOREQ_NONE);
+
                csr = musb_readw(epio, MUSB_RXCSR);
                csr &= ~(MUSB_RXCSR_H_REQPKT | MUSB_RXCSR_DMAENAB);
                musb_writew(epio, MUSB_RXCSR, csr);
 
+               /* wait to drain cppi dma pipe line */
+               udelay(50);
+
                csr = musb_readw(epio, MUSB_RXCSR);
                if (csr & MUSB_RXCSR_RXPKTRDY) {
                        csr |= MUSB_RXCSR_FLUSHFIFO;
@@ -566,13 +571,14 @@ static int cppi41_dma_channel_abort(struct dma_channel *channel)
                tdbit <<= 16;
 
        do {
-               musb_writel(musb->ctrl_base, USB_TDOWN, tdbit);
+               if (is_tx)
+                       musb_writel(musb->ctrl_base, USB_TDOWN, tdbit);
                ret = dmaengine_terminate_all(cppi41_channel->dc);
        } while (ret == -EAGAIN);
 
-       musb_writel(musb->ctrl_base, USB_TDOWN, tdbit);
-
        if (is_tx) {
+               musb_writel(musb->ctrl_base, USB_TDOWN, tdbit);
+
                csr = musb_readw(epio, MUSB_TXCSR);
                if (csr & MUSB_TXCSR_TXPKTRDY) {
                        csr |= MUSB_TXCSR_FLUSHFIFO;
index 48131aa..78a283e 100644 (file)
@@ -196,7 +196,7 @@ static ssize_t musb_test_mode_write(struct file *file,
 
        memset(buf, 0x00, sizeof(buf));
 
-       if (copy_from_user(&buf, ubuf, min_t(size_t, sizeof(buf) - 1, count)))
+       if (copy_from_user(buf, ubuf, min_t(size_t, sizeof(buf) - 1, count)))
                return -EFAULT;
 
        if (strstarts(buf, "force host"))
index 49b04cb..b2d9040 100644 (file)
@@ -1612,9 +1612,7 @@ done:
 static int
 musb_gadget_set_self_powered(struct usb_gadget *gadget, int is_selfpowered)
 {
-       struct musb     *musb = gadget_to_musb(gadget);
-
-       musb->is_self_powered = !!is_selfpowered;
+       gadget->is_selfpowered = !!is_selfpowered;
        return 0;
 }
 
index 2af45a0..10d30af 100644 (file)
@@ -85,7 +85,7 @@ static int service_tx_status_request(
 
        switch (recip) {
        case USB_RECIP_DEVICE:
-               result[0] = musb->is_self_powered << USB_DEVICE_SELF_POWERED;
+               result[0] = musb->g.is_selfpowered << USB_DEVICE_SELF_POWERED;
                result[0] |= musb->may_wakeup << USB_DEVICE_REMOTE_WAKEUP;
                if (musb->g.is_otg) {
                        result[0] |= musb->g.b_hnp_enable
index b79e5be..294e159 100644 (file)
@@ -72,7 +72,6 @@ void musb_host_finish_resume(struct work_struct *work)
        musb->xceiv->otg->state = OTG_STATE_A_HOST;
 
        spin_unlock_irqrestore(&musb->lock, flags);
-       musb_host_resume_root_hub(musb);
 }
 
 void musb_port_suspend(struct musb *musb, bool do_suspend)
@@ -349,8 +348,7 @@ int musb_hub_control(
                desc->bDescriptorType = 0x29;
                desc->bNbrPorts = 1;
                desc->wHubCharacteristics = cpu_to_le16(
-                         HUB_CHAR_INDV_PORT_LPSM /* per-port power switching */
-
+                       HUB_CHAR_INDV_PORT_LPSM /* per-port power switching */
                        | HUB_CHAR_NO_OCPM      /* no overcurrent reporting */
                        );
                desc->bPwrOn2PwrGood = 5;       /* msec/2 */
index ab38aa3..94eb292 100644 (file)
@@ -107,19 +107,6 @@ static void (*_fsl_writel)(u32 v, unsigned __iomem *p);
 #define fsl_writel(val, addr)  writel(val, addr)
 #endif /* CONFIG_PPC32 */
 
-/* Routines to access transceiver ULPI registers */
-u8 view_ulpi(u8 addr)
-{
-       u32 temp;
-
-       temp = 0x40000000 | (addr << 16);
-       fsl_writel(temp, &usb_dr_regs->ulpiview);
-       udelay(1000);
-       while (temp & 0x40)
-               temp = fsl_readl(&usb_dr_regs->ulpiview);
-       return (le32_to_cpu(temp) & 0x0000ff00) >> 8;
-}
-
 int write_ulpi(u8 addr, u8 data)
 {
        u32 temp;
@@ -460,28 +447,6 @@ static void fsl_otg_fsm_del_timer(struct otg_fsm *fsm, enum otg_fsm_timer t)
        fsl_otg_del_timer(fsm, timer);
 }
 
-/*
- * Reduce timer count by 1, and find timeout conditions.
- * Called by fsl_otg 1ms timer interrupt
- */
-int fsl_otg_tick_timer(void)
-{
-       struct fsl_otg_timer *tmp_timer, *del_tmp;
-       int expired = 0;
-
-       list_for_each_entry_safe(tmp_timer, del_tmp, &active_timers, list) {
-               tmp_timer->count--;
-               /* check if timer expires */
-               if (!tmp_timer->count) {
-                       list_del(&tmp_timer->list);
-                       tmp_timer->function(tmp_timer->data);
-                       expired = 1;
-               }
-       }
-
-       return expired;
-}
-
 /* Reset controller, not reset the bus */
 void otg_reset_controller(void)
 {
index f1b719b..70be50b 100644 (file)
@@ -27,6 +27,7 @@
 #include <linux/module.h>
 #include <linux/platform_device.h>
 #include <linux/dma-mapping.h>
+#include <linux/usb/gadget.h>
 #include <linux/usb/otg.h>
 #include <linux/usb/usb_phy_generic.h>
 #include <linux/slab.h>
 
 #include "phy-generic.h"
 
+#define VBUS_IRQ_FLAGS \
+       (IRQF_SHARED | IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING | \
+               IRQF_ONESHOT)
+
 struct platform_device *usb_phy_generic_register(void)
 {
        return platform_device_register_simple("usb_phy_generic",
@@ -59,19 +64,79 @@ static int nop_set_suspend(struct usb_phy *x, int suspend)
 
 static void nop_reset_set(struct usb_phy_generic *nop, int asserted)
 {
-       int value;
+       if (!nop->gpiod_reset)
+               return;
+
+       gpiod_direction_output(nop->gpiod_reset, !asserted);
+       usleep_range(10000, 20000);
+       gpiod_set_value(nop->gpiod_reset, asserted);
+}
+
+/* interface to regulator framework */
+static void nop_set_vbus_draw(struct usb_phy_generic *nop, unsigned mA)
+{
+       struct regulator *vbus_draw = nop->vbus_draw;
+       int enabled;
+       int ret;
 
-       if (!gpio_is_valid(nop->gpio_reset))
+       if (!vbus_draw)
                return;
 
-       value = asserted;
-       if (nop->reset_active_low)
-               value = !value;
+       enabled = nop->vbus_draw_enabled;
+       if (mA) {
+               regulator_set_current_limit(vbus_draw, 0, 1000 * mA);
+               if (!enabled) {
+                       ret = regulator_enable(vbus_draw);
+                       if (ret < 0)
+                               return;
+                       nop->vbus_draw_enabled = 1;
+               }
+       } else {
+               if (enabled) {
+                       ret = regulator_disable(vbus_draw);
+                       if (ret < 0)
+                               return;
+                       nop->vbus_draw_enabled = 0;
+               }
+       }
+       nop->mA = mA;
+}
+
+
+static irqreturn_t nop_gpio_vbus_thread(int irq, void *data)
+{
+       struct usb_phy_generic *nop = data;
+       struct usb_otg *otg = nop->phy.otg;
+       int vbus, status;
+
+       vbus = gpiod_get_value(nop->gpiod_vbus);
+       if ((vbus ^ nop->vbus) == 0)
+               return IRQ_HANDLED;
+       nop->vbus = vbus;
+
+       if (vbus) {
+               status = USB_EVENT_VBUS;
+               otg->state = OTG_STATE_B_PERIPHERAL;
+               nop->phy.last_event = status;
+               usb_gadget_vbus_connect(otg->gadget);
+
+               /* drawing a "unit load" is *always* OK, except for OTG */
+               nop_set_vbus_draw(nop, 100);
+
+               atomic_notifier_call_chain(&nop->phy.notifier, status,
+                                          otg->gadget);
+       } else {
+               nop_set_vbus_draw(nop, 0);
 
-       gpio_set_value_cansleep(nop->gpio_reset, value);
+               usb_gadget_vbus_disconnect(otg->gadget);
+               status = USB_EVENT_NONE;
+               otg->state = OTG_STATE_B_IDLE;
+               nop->phy.last_event = status;
 
-       if (!asserted)
-               usleep_range(10000, 20000);
+               atomic_notifier_call_chain(&nop->phy.notifier, status,
+                                          otg->gadget);
+       }
+       return IRQ_HANDLED;
 }
 
 int usb_gen_phy_init(struct usb_phy *phy)
@@ -143,36 +208,47 @@ int usb_phy_gen_create_phy(struct device *dev, struct usb_phy_generic *nop,
                struct usb_phy_generic_platform_data *pdata)
 {
        enum usb_phy_type type = USB_PHY_TYPE_USB2;
-       int err;
+       int err = 0;
 
        u32 clk_rate = 0;
        bool needs_vcc = false;
 
-       nop->reset_active_low = true;   /* default behaviour */
-
        if (dev->of_node) {
                struct device_node *node = dev->of_node;
-               enum of_gpio_flags flags = 0;
 
                if (of_property_read_u32(node, "clock-frequency", &clk_rate))
                        clk_rate = 0;
 
                needs_vcc = of_property_read_bool(node, "vcc-supply");
-               nop->gpio_reset = of_get_named_gpio_flags(node, "reset-gpios",
-                                                               0, &flags);
-               if (nop->gpio_reset == -EPROBE_DEFER)
-                       return -EPROBE_DEFER;
-
-               nop->reset_active_low = flags & OF_GPIO_ACTIVE_LOW;
-
+               nop->gpiod_reset = devm_gpiod_get_optional(dev, "reset");
+               err = PTR_ERR_OR_ZERO(nop->gpiod_reset);
+               if (!err) {
+                       nop->gpiod_vbus = devm_gpiod_get_optional(dev,
+                                                        "vbus-detect");
+                       err = PTR_ERR_OR_ZERO(nop->gpiod_vbus);
+               }
        } else if (pdata) {
                type = pdata->type;
                clk_rate = pdata->clk_rate;
                needs_vcc = pdata->needs_vcc;
-               nop->gpio_reset = pdata->gpio_reset;
-       } else {
-               nop->gpio_reset = -1;
+               if (gpio_is_valid(pdata->gpio_reset)) {
+                       err = devm_gpio_request_one(dev, pdata->gpio_reset, 0,
+                                                   dev_name(dev));
+                       if (!err)
+                               nop->gpiod_reset =
+                                       gpio_to_desc(pdata->gpio_reset);
+               }
+               nop->gpiod_vbus = pdata->gpiod_vbus;
+       }
+
+       if (err == -EPROBE_DEFER)
+               return -EPROBE_DEFER;
+       if (err) {
+               dev_err(dev, "Error requesting RESET or VBUS GPIO\n");
+               return err;
        }
+       if (nop->gpiod_reset)
+               gpiod_direction_output(nop->gpiod_reset, 1);
 
        nop->phy.otg = devm_kzalloc(dev, sizeof(*nop->phy.otg),
                        GFP_KERNEL);
@@ -201,24 +277,6 @@ int usb_phy_gen_create_phy(struct device *dev, struct usb_phy_generic *nop,
                        return -EPROBE_DEFER;
        }
 
-       if (gpio_is_valid(nop->gpio_reset)) {
-               unsigned long gpio_flags;
-
-               /* Assert RESET */
-               if (nop->reset_active_low)
-                       gpio_flags = GPIOF_OUT_INIT_LOW;
-               else
-                       gpio_flags = GPIOF_OUT_INIT_HIGH;
-
-               err = devm_gpio_request_one(dev, nop->gpio_reset,
-                                               gpio_flags, dev_name(dev));
-               if (err) {
-                       dev_err(dev, "Error requesting RESET GPIO %d\n",
-                                       nop->gpio_reset);
-                       return err;
-               }
-       }
-
        nop->dev                = dev;
        nop->phy.dev            = nop->dev;
        nop->phy.label          = "nop-xceiv";
@@ -247,6 +305,18 @@ static int usb_phy_generic_probe(struct platform_device *pdev)
        err = usb_phy_gen_create_phy(dev, nop, dev_get_platdata(&pdev->dev));
        if (err)
                return err;
+       if (nop->gpiod_vbus) {
+               err = devm_request_threaded_irq(&pdev->dev,
+                                               gpiod_to_irq(nop->gpiod_vbus),
+                                               NULL, nop_gpio_vbus_thread,
+                                               VBUS_IRQ_FLAGS, "vbus_detect",
+                                               nop);
+               if (err) {
+                       dev_err(&pdev->dev, "can't request irq %i, err: %d\n",
+                               gpiod_to_irq(nop->gpiod_vbus), err);
+                       return err;
+               }
+       }
 
        nop->phy.init           = usb_gen_phy_init;
        nop->phy.shutdown       = usb_gen_phy_shutdown;
index d8feacc..0d0eadd 100644 (file)
@@ -2,14 +2,20 @@
 #define _PHY_GENERIC_H_
 
 #include <linux/usb/usb_phy_generic.h>
+#include <linux/gpio/consumer.h>
+#include <linux/regulator/consumer.h>
 
 struct usb_phy_generic {
        struct usb_phy phy;
        struct device *dev;
        struct clk *clk;
        struct regulator *vcc;
-       int gpio_reset;
-       bool reset_active_low;
+       struct gpio_desc *gpiod_reset;
+       struct gpio_desc *gpiod_vbus;
+       struct regulator *vbus_draw;
+       bool vbus_draw_enabled;
+       unsigned long mA;
+       unsigned int vbus;
 };
 
 int usb_gen_phy_init(struct usb_phy *phy);
index b9589f6..8f7cb06 100644 (file)
@@ -40,6 +40,7 @@
 
 #define BM_USBPHY_CTRL_SFTRST                  BIT(31)
 #define BM_USBPHY_CTRL_CLKGATE                 BIT(30)
+#define BM_USBPHY_CTRL_OTG_ID_VALUE            BIT(27)
 #define BM_USBPHY_CTRL_ENAUTOSET_USBCLKS       BIT(26)
 #define BM_USBPHY_CTRL_ENAUTOCLR_USBCLKGATE    BIT(25)
 #define BM_USBPHY_CTRL_ENVBUSCHG_WKUP          BIT(23)
@@ -69,6 +70,9 @@
 #define ANADIG_USB2_LOOPBACK_SET               0x244
 #define ANADIG_USB2_LOOPBACK_CLR               0x248
 
+#define ANADIG_USB1_MISC                       0x1f0
+#define ANADIG_USB2_MISC                       0x250
+
 #define BM_ANADIG_ANA_MISC0_STOP_MODE_CONFIG   BIT(12)
 #define BM_ANADIG_ANA_MISC0_STOP_MODE_CONFIG_SL BIT(11)
 
 #define BM_ANADIG_USB2_LOOPBACK_UTMI_DIG_TST1  BIT(2)
 #define BM_ANADIG_USB2_LOOPBACK_TSTI_TX_EN     BIT(5)
 
+#define BM_ANADIG_USB1_MISC_RX_VPIN_FS         BIT(29)
+#define BM_ANADIG_USB1_MISC_RX_VMIN_FS         BIT(28)
+#define BM_ANADIG_USB2_MISC_RX_VPIN_FS         BIT(29)
+#define BM_ANADIG_USB2_MISC_RX_VMIN_FS         BIT(28)
+
 #define to_mxs_phy(p) container_of((p), struct mxs_phy, phy)
 
 /* Do disconnection between PHY and controller without vbus */
@@ -131,8 +140,7 @@ static const struct mxs_phy_data vf610_phy_data = {
 };
 
 static const struct mxs_phy_data imx6sx_phy_data = {
-       .flags = MXS_PHY_DISCONNECT_LINE_WITHOUT_VBUS |
-               MXS_PHY_NEED_IP_FIX,
+       .flags = MXS_PHY_DISCONNECT_LINE_WITHOUT_VBUS,
 };
 
 static const struct of_device_id mxs_phy_dt_ids[] = {
@@ -256,6 +264,18 @@ static void __mxs_phy_disconnect_line(struct mxs_phy *mxs_phy, bool disconnect)
                usleep_range(500, 1000);
 }
 
+static bool mxs_phy_is_otg_host(struct mxs_phy *mxs_phy)
+{
+       void __iomem *base = mxs_phy->phy.io_priv;
+       u32 phyctrl = readl(base + HW_USBPHY_CTRL);
+
+       if (IS_ENABLED(CONFIG_USB_OTG) &&
+                       !(phyctrl & BM_USBPHY_CTRL_OTG_ID_VALUE))
+               return true;
+
+       return false;
+}
+
 static void mxs_phy_disconnect_line(struct mxs_phy *mxs_phy, bool on)
 {
        bool vbus_is_on = false;
@@ -270,7 +290,7 @@ static void mxs_phy_disconnect_line(struct mxs_phy *mxs_phy, bool on)
 
        vbus_is_on = mxs_phy_get_vbus_status(mxs_phy);
 
-       if (on && !vbus_is_on)
+       if (on && !vbus_is_on && !mxs_phy_is_otg_host(mxs_phy))
                __mxs_phy_disconnect_line(mxs_phy, true);
        else
                __mxs_phy_disconnect_line(mxs_phy, false);
@@ -293,6 +313,17 @@ static int mxs_phy_init(struct usb_phy *phy)
 static void mxs_phy_shutdown(struct usb_phy *phy)
 {
        struct mxs_phy *mxs_phy = to_mxs_phy(phy);
+       u32 value = BM_USBPHY_CTRL_ENVBUSCHG_WKUP |
+                       BM_USBPHY_CTRL_ENDPDMCHG_WKUP |
+                       BM_USBPHY_CTRL_ENIDCHG_WKUP |
+                       BM_USBPHY_CTRL_ENAUTOSET_USBCLKS |
+                       BM_USBPHY_CTRL_ENAUTOCLR_USBCLKGATE |
+                       BM_USBPHY_CTRL_ENAUTOCLR_PHY_PWD |
+                       BM_USBPHY_CTRL_ENAUTOCLR_CLKGATE |
+                       BM_USBPHY_CTRL_ENAUTO_PWRON_PLL;
+
+       writel(value, phy->io_priv + HW_USBPHY_CTRL_CLR);
+       writel(0xffffffff, phy->io_priv + HW_USBPHY_PWD);
 
        writel(BM_USBPHY_CTRL_CLKGATE,
               phy->io_priv + HW_USBPHY_CTRL_SET);
@@ -300,13 +331,56 @@ static void mxs_phy_shutdown(struct usb_phy *phy)
        clk_disable_unprepare(mxs_phy->clk);
 }
 
+static bool mxs_phy_is_low_speed_connection(struct mxs_phy *mxs_phy)
+{
+       unsigned int line_state;
+       /* bit definition is the same for all controllers */
+       unsigned int dp_bit = BM_ANADIG_USB1_MISC_RX_VPIN_FS,
+                    dm_bit = BM_ANADIG_USB1_MISC_RX_VMIN_FS;
+       unsigned int reg = ANADIG_USB1_MISC;
+
+       /* If the SoCs don't have anatop, quit */
+       if (!mxs_phy->regmap_anatop)
+               return false;
+
+       if (mxs_phy->port_id == 0)
+               reg = ANADIG_USB1_MISC;
+       else if (mxs_phy->port_id == 1)
+               reg = ANADIG_USB2_MISC;
+
+       regmap_read(mxs_phy->regmap_anatop, reg, &line_state);
+
+       if ((line_state & (dp_bit | dm_bit)) ==  dm_bit)
+               return true;
+       else
+               return false;
+}
+
 static int mxs_phy_suspend(struct usb_phy *x, int suspend)
 {
        int ret;
        struct mxs_phy *mxs_phy = to_mxs_phy(x);
+       bool low_speed_connection, vbus_is_on;
+
+       low_speed_connection = mxs_phy_is_low_speed_connection(mxs_phy);
+       vbus_is_on = mxs_phy_get_vbus_status(mxs_phy);
 
        if (suspend) {
-               writel(0xffffffff, x->io_priv + HW_USBPHY_PWD);
+               /*
+                * FIXME: Do not power down RXPWD1PT1 bit for low speed
+                * connect. The low speed connection will have problem at
+                * very rare cases during usb suspend and resume process.
+                */
+               if (low_speed_connection & vbus_is_on) {
+                       /*
+                        * If value to be set as pwd value is not 0xffffffff,
+                        * several 32Khz cycles are needed.
+                        */
+                       mxs_phy_clock_switch_delay();
+                       writel(0xffbfffff, x->io_priv + HW_USBPHY_PWD);
+               } else {
+                       writel(0xffffffff, x->io_priv + HW_USBPHY_PWD);
+               }
                writel(BM_USBPHY_CTRL_CLKGATE,
                       x->io_priv + HW_USBPHY_CTRL_SET);
                clk_disable_unprepare(mxs_phy->clk);
@@ -359,7 +433,9 @@ static int mxs_phy_on_disconnect(struct usb_phy *phy,
        dev_dbg(phy->dev, "%s device has disconnected\n",
                (speed == USB_SPEED_HIGH) ? "HS" : "FS/LS");
 
-       if (speed == USB_SPEED_HIGH)
+       /* Sometimes, the speed is not high speed when the error occurs */
+       if (readl(phy->io_priv + HW_USBPHY_CTRL) &
+                       BM_USBPHY_CTRL_ENHOSTDISCONDETECT)
                writel(BM_USBPHY_CTRL_ENHOSTDISCONDETECT,
                       phy->io_priv + HW_USBPHY_CTRL_CLR);
 
index 3714787..4cf77d3 100644 (file)
@@ -363,6 +363,7 @@ static void usbhsc_hotplug(struct usbhs_priv *priv)
        struct usbhs_mod *mod = usbhs_mod_get_current(priv);
        int id;
        int enable;
+       int cable;
        int ret;
 
        /*
@@ -376,6 +377,16 @@ static void usbhsc_hotplug(struct usbhs_priv *priv)
        id = usbhs_platform_call(priv, get_id, pdev);
 
        if (enable && !mod) {
+               if (priv->edev) {
+                       cable = extcon_get_cable_state(priv->edev, "USB-HOST");
+                       if ((cable > 0 && id != USBHS_HOST) ||
+                           (!cable && id != USBHS_GADGET)) {
+                               dev_info(&pdev->dev,
+                                        "USB cable plugged in doesn't match the selected role!\n");
+                               return;
+                       }
+               }
+
                ret = usbhs_mod_change(priv, id);
                if (ret < 0)
                        return;
@@ -514,6 +525,12 @@ static int usbhs_probe(struct platform_device *pdev)
        if (IS_ERR(priv->base))
                return PTR_ERR(priv->base);
 
+       if (of_property_read_bool(pdev->dev.of_node, "extcon")) {
+               priv->edev = extcon_get_edev_by_phandle(&pdev->dev, 0);
+               if (IS_ERR(priv->edev))
+                       return PTR_ERR(priv->edev);
+       }
+
        /*
         * care platform info
         */
@@ -615,7 +632,7 @@ static int usbhs_probe(struct platform_device *pdev)
         */
        ret = usbhs_platform_call(priv, hardware_init, pdev);
        if (ret < 0) {
-               dev_err(&pdev->dev, "platform prove failed.\n");
+               dev_err(&pdev->dev, "platform init failed.\n");
                goto probe_end_mod_exit;
        }
 
@@ -632,16 +649,12 @@ static int usbhs_probe(struct platform_device *pdev)
        /*
         * manual call notify_hotplug for cold plug
         */
-       ret = usbhsc_drvcllbck_notify_hotplug(pdev);
-       if (ret < 0)
-               goto probe_end_call_remove;
+       usbhsc_drvcllbck_notify_hotplug(pdev);
 
        dev_info(&pdev->dev, "probed\n");
 
        return ret;
 
-probe_end_call_remove:
-       usbhs_platform_call(priv, hardware_exit, pdev);
 probe_end_mod_exit:
        usbhs_mod_remove(priv);
 probe_end_fifo_exit:
index 0427cdd..fc96e92 100644 (file)
@@ -17,6 +17,7 @@
 #ifndef RENESAS_USB_DRIVER_H
 #define RENESAS_USB_DRIVER_H
 
+#include <linux/extcon.h>
 #include <linux/platform_device.h>
 #include <linux/usb/renesas_usbhs.h>
 
@@ -254,6 +255,8 @@ struct usbhs_priv {
        struct delayed_work notify_hotplug_work;
        struct platform_device *pdev;
 
+       struct extcon_dev *edev;
+
        spinlock_t              lock;
 
        u32 flags;
index f46271c..d891bff 100644 (file)
@@ -1054,10 +1054,8 @@ static void usbhsf_dma_quit(struct usbhs_priv *priv, struct usbhs_fifo *fifo)
        fifo->rx_chan = NULL;
 }
 
-static void usbhsf_dma_init(struct usbhs_priv *priv,
-                           struct usbhs_fifo *fifo)
+static void usbhsf_dma_init_pdev(struct usbhs_fifo *fifo)
 {
-       struct device *dev = usbhs_priv_to_dev(priv);
        dma_cap_mask_t mask;
 
        dma_cap_zero(mask);
@@ -1069,6 +1067,27 @@ static void usbhsf_dma_init(struct usbhs_priv *priv,
        dma_cap_set(DMA_SLAVE, mask);
        fifo->rx_chan = dma_request_channel(mask, usbhsf_dma_filter,
                                            &fifo->rx_slave);
+}
+
+static void usbhsf_dma_init_dt(struct device *dev, struct usbhs_fifo *fifo)
+{
+       fifo->tx_chan = dma_request_slave_channel_reason(dev, "tx");
+       if (IS_ERR(fifo->tx_chan))
+               fifo->tx_chan = NULL;
+       fifo->rx_chan = dma_request_slave_channel_reason(dev, "rx");
+       if (IS_ERR(fifo->rx_chan))
+               fifo->rx_chan = NULL;
+}
+
+static void usbhsf_dma_init(struct usbhs_priv *priv,
+                           struct usbhs_fifo *fifo)
+{
+       struct device *dev = usbhs_priv_to_dev(priv);
+
+       if (dev->of_node)
+               usbhsf_dma_init_dt(dev, fifo);
+       else
+               usbhsf_dma_init_pdev(fifo);
 
        if (fifo->tx_chan || fifo->rx_chan)
                dev_dbg(dev, "enable DMAEngine (%s%s%s)\n",
index 8697e6e..e0384af 100644 (file)
@@ -926,6 +926,8 @@ static int usbhsg_set_selfpowered(struct usb_gadget *gadget, int is_self)
        else
                usbhsg_status_clr(gpriv, USBHSG_STATUS_SELF_POWERED);
 
+       gadget->is_selfpowered = (is_self != 0);
+
        return 0;
 }
 
index 70ddb39..e2f00fd 100644 (file)
@@ -523,6 +523,7 @@ struct usb_gadget_ops {
  *     enabled HNP support.
  * @quirk_ep_out_aligned_size: epout requires buffer size to be aligned to
  *     MaxPacketSize.
+ * @is_selfpowered: if the gadget is self-powered.
  *
  * Gadgets have a mostly-portable "gadget driver" implementing device
  * functions, handling all usb configurations and interfaces.  Gadget
@@ -563,6 +564,7 @@ struct usb_gadget {
        unsigned                        a_hnp_support:1;
        unsigned                        a_alt_hnp_support:1;
        unsigned                        quirk_ep_out_aligned_size:1;
+       unsigned                        is_selfpowered:1;
 };
 #define work_to_gadget(w)      (container_of((w), struct usb_gadget, work))
 
index f499c23..bc91b5d 100644 (file)
@@ -1,5 +1,5 @@
-/* USB OTG (On The Go) defines */
 /*
+ * USB PHY defines
  *
  * These APIs may be used between USB controllers.  USB device drivers
  * (for either host or peripheral roles) don't use these calls; they
@@ -106,7 +106,7 @@ struct usb_phy {
        int     (*set_power)(struct usb_phy *x,
                                unsigned mA);
 
-       /* for non-OTG B devices: set transceiver into suspend mode */
+       /* Set transceiver into suspend mode */
        int     (*set_suspend)(struct usb_phy *x,
                                int suspend);
 
index 68adae8..c13632d 100644 (file)
@@ -2,6 +2,7 @@
 #define __LINUX_USB_NOP_XCEIV_H
 
 #include <linux/usb/otg.h>
+#include <linux/gpio/consumer.h>
 
 struct usb_phy_generic_platform_data {
        enum usb_phy_type type;
@@ -11,6 +12,7 @@ struct usb_phy_generic_platform_data {
        unsigned int needs_vcc:1;
        unsigned int needs_reset:1;     /* deprecated */
        int gpio_reset;
+       struct gpio_desc *gpiod_vbus;
 };
 
 #if IS_ENABLED(CONFIG_NOP_USB_XCEIV)
index 295ba29..108dd79 100644 (file)
@@ -20,6 +20,7 @@ enum functionfs_flags {
        FUNCTIONFS_HAS_SS_DESC = 4,
        FUNCTIONFS_HAS_MS_OS_DESC = 8,
        FUNCTIONFS_VIRTUAL_ADDR = 16,
+       FUNCTIONFS_EVENTFD = 32,
 };
 
 /* Descriptor of an non-audio endpoint */
index daa3abe..2cbcce6 100644 (file)
 #define VENDOR 0x1d6b
 #define PRODUCT        0x0105
 
-/* endpoints indexes */
-
-#define EP_BULK_IN     (1 | LIBUSB_ENDPOINT_IN)
-#define EP_BULK_OUT    (2 | LIBUSB_ENDPOINT_OUT)
-
 #define BUF_LEN                8192
 
 /*
@@ -159,14 +154,21 @@ void test_exit(struct test_state *state)
 int main(void)
 {
        struct test_state state;
+       struct libusb_config_descriptor *conf;
+       struct libusb_interface_descriptor const *iface;
+       unsigned char addr;
 
        if (test_init(&state))
                return 1;
 
+       libusb_get_config_descriptor(state.found, 0, &conf);
+       iface = &conf->interface[0].altsetting[0];
+       addr = iface->endpoint[0].bEndpointAddress;
+
        while (1) {
                static unsigned char buffer[BUF_LEN];
                int bytes;
-               libusb_bulk_transfer(state.handle, EP_BULK_IN, buffer, BUF_LEN,
+               libusb_bulk_transfer(state.handle, addr, buffer, BUF_LEN,
                                     &bytes, 500);
        }
        test_exit(&state);
index adc310a..1f44a29 100644 (file)
@@ -103,12 +103,14 @@ static const struct {
                        .bDescriptorType = USB_DT_ENDPOINT,
                        .bEndpointAddress = 1 | USB_DIR_IN,
                        .bmAttributes = USB_ENDPOINT_XFER_BULK,
+                       .wMaxPacketSize = htole16(512),
                },
                .bulk_source = {
                        .bLength = sizeof(descriptors.hs_descs.bulk_source),
                        .bDescriptorType = USB_DT_ENDPOINT,
                        .bEndpointAddress = 2 | USB_DIR_OUT,
                        .bmAttributes = USB_ENDPOINT_XFER_BULK,
+                       .wMaxPacketSize = htole16(512),
                },
        },
 };
index acd6332..aed86ff 100644 (file)
 #define VENDOR 0x1d6b
 #define PRODUCT        0x0105
 
-/* endpoints indexes */
-
-#define EP_BULK_IN     (1 | LIBUSB_ENDPOINT_IN)
-#define EP_BULK_OUT    (2 | LIBUSB_ENDPOINT_OUT)
-
 #define BUF_LEN                8192
 
 /*
@@ -159,16 +154,24 @@ void test_exit(struct test_state *state)
 int main(void)
 {
        struct test_state state;
+       struct libusb_config_descriptor *conf;
+       struct libusb_interface_descriptor const *iface;
+       unsigned char in_addr, out_addr;
 
        if (test_init(&state))
                return 1;
 
+       libusb_get_config_descriptor(state.found, 0, &conf);
+       iface = &conf->interface[0].altsetting[0];
+       in_addr = iface->endpoint[0].bEndpointAddress;
+       out_addr = iface->endpoint[1].bEndpointAddress;
+
        while (1) {
                static unsigned char buffer[BUF_LEN];
                int bytes;
-               libusb_bulk_transfer(state.handle, EP_BULK_IN, buffer, BUF_LEN,
+               libusb_bulk_transfer(state.handle, in_addr, buffer, BUF_LEN,
                                     &bytes, 500);
-               libusb_bulk_transfer(state.handle, EP_BULK_OUT, buffer, BUF_LEN,
+               libusb_bulk_transfer(state.handle, out_addr, buffer, BUF_LEN,
                                     &bytes, 500);
        }
        test_exit(&state);