All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH v2 0/5] Introduced new Cadence USBSS DRD Driver.
@ 2018-12-23 15:13 ` Pawel Laszczak
  0 siblings, 0 replies; 49+ messages in thread
From: Pawel Laszczak @ 2018-12-23 15:13 UTC (permalink / raw)
  To: devicetree
  Cc: gregkh, mark.rutland, linux-usb, hdegoede, heikki.krogerus,
	andy.shevchenko, robh+dt, rogerq, linux-kernel, adouglas,
	jbergsagel, nsekhar, nm, sureshp, peter.chen, pjez, kurahul,
	Pawel Laszczak

This patch set introduce new Cadence USBSS DRD driver
to linux kernel.

The Cadence USBSS DRD Driver s a highly
configurable IP Core which can be
instantiated as Dual-Role Device (DRD),
Peripheral Only and Host Only (XHCI)
configurations.

The current driver has been validated with
FPGA burned. We have support for PCIe
bus, which is used on FPGA prototyping.

The host site of USBSS controller is compliance
with XHCI specification, so it works with
standard XHCI linux driver.

Changes since v1:
 - Removed not implemented Susped/Resume functions.
 - Fixed some issues in debugging related functions.
 - Added trace_cdns3_request_handled marker.
 - Added support for Isochronouse transfer. 
 - Added some additional descriptions.
 - Fixed compilation error in cdns3_gadget_ep_disable.
 - Added detection of device controller version at runtime.
 - Upgraded dt-binding documentation.
 - Deleted ENOSYS from phy initialization section. It should be also removed
   from generic PHY driver.
 - added ep0_stage flag used during enumeration process.
 - Fixed issue with TEST MODE.
 - Added one common function for finish control transfer.
 - Separated some decoding function from dwc3 driver to common library file,
   and removed equivalents function from debug.h file as suggested  by Felipe.
 - replaced function name cdns3_gadget_unconfig with cdns3_hw_reset_eps_config.
 - Improved algorithm fixing hardware issue related to blocking endpoints.
   This issue is related to on-chip shared FIFO buffers for OUT packets. 
   Problem was reported by Peter Chan.
 - Changed organization of endpoint array in cdns3_device object.  
      - added ep0 to common eps array
      - removed cdns3_free_trb_pool and cdns3_ep_addr_to_bit_pos macros.
      - removed ep0_trb_dma, ep0_trb fields from cdns3_device.
 - Removed ep0_request and ep_nums fields from cdns3_device.
 - Other minor changes according with Felipe suggestion.

---

Pawel Laszczak (5):
  dt-bindings: add binding for USBSS-DRD controller.
  usb:common Separated decoding functions from dwc3 driver.
  usb:common Patch simplify usb_decode_set_clear_feature function.
  usb:common Simplify usb_decode_get_set_descriptor function.
  usb:cdns3 Add Cadence USB3 DRD Driver

 .../devicetree/bindings/usb/cdns3-usb.txt     |   30 +
 drivers/usb/Kconfig                           |    2 +
 drivers/usb/Makefile                          |    2 +
 drivers/usb/cdns3/Kconfig                     |   44 +
 drivers/usb/cdns3/Makefile                    |   16 +
 drivers/usb/cdns3/cdns3-pci-wrap.c            |  157 ++
 drivers/usb/cdns3/core.c                      |  406 ++++
 drivers/usb/cdns3/core.h                      |  116 +
 drivers/usb/cdns3/debug.h                     |  166 ++
 drivers/usb/cdns3/debugfs.c                   |  168 ++
 drivers/usb/cdns3/drd.c                       |  350 +++
 drivers/usb/cdns3/drd.h                       |  162 ++
 drivers/usb/cdns3/ep0.c                       |  896 +++++++
 drivers/usb/cdns3/gadget-export.h             |   28 +
 drivers/usb/cdns3/gadget.c                    | 2102 +++++++++++++++++
 drivers/usb/cdns3/gadget.h                    | 1206 ++++++++++
 drivers/usb/cdns3/host-export.h               |   28 +
 drivers/usb/cdns3/host.c                      |   72 +
 drivers/usb/cdns3/trace.c                     |   11 +
 drivers/usb/cdns3/trace.h                     |  389 +++
 drivers/usb/common/Makefile                   |    2 +-
 drivers/usb/common/debug.c                    |  265 +++
 drivers/usb/dwc3/debug.h                      |  243 --
 drivers/usb/dwc3/trace.h                      |    2 +-
 include/linux/usb/ch9.h                       |   19 +
 25 files changed, 6637 insertions(+), 245 deletions(-)
 create mode 100644 Documentation/devicetree/bindings/usb/cdns3-usb.txt
 create mode 100644 drivers/usb/cdns3/Kconfig
 create mode 100644 drivers/usb/cdns3/Makefile
 create mode 100644 drivers/usb/cdns3/cdns3-pci-wrap.c
 create mode 100644 drivers/usb/cdns3/core.c
 create mode 100644 drivers/usb/cdns3/core.h
 create mode 100644 drivers/usb/cdns3/debug.h
 create mode 100644 drivers/usb/cdns3/debugfs.c
 create mode 100644 drivers/usb/cdns3/drd.c
 create mode 100644 drivers/usb/cdns3/drd.h
 create mode 100644 drivers/usb/cdns3/ep0.c
 create mode 100644 drivers/usb/cdns3/gadget-export.h
 create mode 100644 drivers/usb/cdns3/gadget.c
 create mode 100644 drivers/usb/cdns3/gadget.h
 create mode 100644 drivers/usb/cdns3/host-export.h
 create mode 100644 drivers/usb/cdns3/host.c
 create mode 100644 drivers/usb/cdns3/trace.c
 create mode 100644 drivers/usb/cdns3/trace.h
 create mode 100644 drivers/usb/common/debug.c

-- 
2.17.1


^ permalink raw reply	[flat|nested] 49+ messages in thread

* [PATCH v2 0/5] Introduced new Cadence USBSS DRD Driver.
@ 2018-12-23 15:13 ` Pawel Laszczak
  0 siblings, 0 replies; 49+ messages in thread
From: Pawel Laszczak @ 2018-12-23 15:13 UTC (permalink / raw)
  To: devicetree
  Cc: gregkh, mark.rutland, linux-usb, hdegoede, heikki.krogerus,
	andy.shevchenko, robh+dt, rogerq, linux-kernel, adouglas,
	jbergsagel, nsekhar, nm, sureshp, peter.chen, pjez, kurahul,
	Pawel Laszczak

This patch set introduce new Cadence USBSS DRD driver
to linux kernel.

The Cadence USBSS DRD Driver s a highly
configurable IP Core which can be
instantiated as Dual-Role Device (DRD),
Peripheral Only and Host Only (XHCI)
configurations.

The current driver has been validated with
FPGA burned. We have support for PCIe
bus, which is used on FPGA prototyping.

The host site of USBSS controller is compliance
with XHCI specification, so it works with
standard XHCI linux driver.

Changes since v1:
 - Removed not implemented Susped/Resume functions.
 - Fixed some issues in debugging related functions.
 - Added trace_cdns3_request_handled marker.
 - Added support for Isochronouse transfer. 
 - Added some additional descriptions.
 - Fixed compilation error in cdns3_gadget_ep_disable.
 - Added detection of device controller version at runtime.
 - Upgraded dt-binding documentation.
 - Deleted ENOSYS from phy initialization section. It should be also removed
   from generic PHY driver.
 - added ep0_stage flag used during enumeration process.
 - Fixed issue with TEST MODE.
 - Added one common function for finish control transfer.
 - Separated some decoding function from dwc3 driver to common library file,
   and removed equivalents function from debug.h file as suggested  by Felipe.
 - replaced function name cdns3_gadget_unconfig with cdns3_hw_reset_eps_config.
 - Improved algorithm fixing hardware issue related to blocking endpoints.
   This issue is related to on-chip shared FIFO buffers for OUT packets. 
   Problem was reported by Peter Chan.
 - Changed organization of endpoint array in cdns3_device object.  
      - added ep0 to common eps array
      - removed cdns3_free_trb_pool and cdns3_ep_addr_to_bit_pos macros.
      - removed ep0_trb_dma, ep0_trb fields from cdns3_device.
 - Removed ep0_request and ep_nums fields from cdns3_device.
 - Other minor changes according with Felipe suggestion.

---

Pawel Laszczak (5):
  dt-bindings: add binding for USBSS-DRD controller.
  usb:common Separated decoding functions from dwc3 driver.
  usb:common Patch simplify usb_decode_set_clear_feature function.
  usb:common Simplify usb_decode_get_set_descriptor function.
  usb:cdns3 Add Cadence USB3 DRD Driver

 .../devicetree/bindings/usb/cdns3-usb.txt     |   30 +
 drivers/usb/Kconfig                           |    2 +
 drivers/usb/Makefile                          |    2 +
 drivers/usb/cdns3/Kconfig                     |   44 +
 drivers/usb/cdns3/Makefile                    |   16 +
 drivers/usb/cdns3/cdns3-pci-wrap.c            |  157 ++
 drivers/usb/cdns3/core.c                      |  406 ++++
 drivers/usb/cdns3/core.h                      |  116 +
 drivers/usb/cdns3/debug.h                     |  166 ++
 drivers/usb/cdns3/debugfs.c                   |  168 ++
 drivers/usb/cdns3/drd.c                       |  350 +++
 drivers/usb/cdns3/drd.h                       |  162 ++
 drivers/usb/cdns3/ep0.c                       |  896 +++++++
 drivers/usb/cdns3/gadget-export.h             |   28 +
 drivers/usb/cdns3/gadget.c                    | 2102 +++++++++++++++++
 drivers/usb/cdns3/gadget.h                    | 1206 ++++++++++
 drivers/usb/cdns3/host-export.h               |   28 +
 drivers/usb/cdns3/host.c                      |   72 +
 drivers/usb/cdns3/trace.c                     |   11 +
 drivers/usb/cdns3/trace.h                     |  389 +++
 drivers/usb/common/Makefile                   |    2 +-
 drivers/usb/common/debug.c                    |  265 +++
 drivers/usb/dwc3/debug.h                      |  243 --
 drivers/usb/dwc3/trace.h                      |    2 +-
 include/linux/usb/ch9.h                       |   19 +
 25 files changed, 6637 insertions(+), 245 deletions(-)
 create mode 100644 Documentation/devicetree/bindings/usb/cdns3-usb.txt
 create mode 100644 drivers/usb/cdns3/Kconfig
 create mode 100644 drivers/usb/cdns3/Makefile
 create mode 100644 drivers/usb/cdns3/cdns3-pci-wrap.c
 create mode 100644 drivers/usb/cdns3/core.c
 create mode 100644 drivers/usb/cdns3/core.h
 create mode 100644 drivers/usb/cdns3/debug.h
 create mode 100644 drivers/usb/cdns3/debugfs.c
 create mode 100644 drivers/usb/cdns3/drd.c
 create mode 100644 drivers/usb/cdns3/drd.h
 create mode 100644 drivers/usb/cdns3/ep0.c
 create mode 100644 drivers/usb/cdns3/gadget-export.h
 create mode 100644 drivers/usb/cdns3/gadget.c
 create mode 100644 drivers/usb/cdns3/gadget.h
 create mode 100644 drivers/usb/cdns3/host-export.h
 create mode 100644 drivers/usb/cdns3/host.c
 create mode 100644 drivers/usb/cdns3/trace.c
 create mode 100644 drivers/usb/cdns3/trace.h
 create mode 100644 drivers/usb/common/debug.c

-- 
2.17.1

^ permalink raw reply	[flat|nested] 49+ messages in thread

* [PATCH v2 1/5] dt-bindings: add binding for USBSS-DRD controller.
  2018-12-23 15:13 ` Pawel Laszczak
  (?)
@ 2018-12-23 15:13   ` Pawel Laszczak
  -1 siblings, 0 replies; 49+ messages in thread
From: Pawel Laszczak @ 2018-12-23 15:13 UTC (permalink / raw)
  To: devicetree
  Cc: gregkh, mark.rutland, linux-usb, hdegoede, heikki.krogerus,
	andy.shevchenko, robh+dt, rogerq, linux-kernel, adouglas,
	jbergsagel, nsekhar, nm, sureshp, peter.chen, pjez, kurahul,
	Pawel Laszczak

This patch aim at documenting USB related dt-bindings for the
Cadence USBSS-DRD controller.

Signed-off-by: Pawel Laszczak <pawell@cadence.com>
---
 .../devicetree/bindings/usb/cdns3-usb.txt     | 30 +++++++++++++++++++
 1 file changed, 30 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/usb/cdns3-usb.txt

diff --git a/Documentation/devicetree/bindings/usb/cdns3-usb.txt b/Documentation/devicetree/bindings/usb/cdns3-usb.txt
new file mode 100644
index 000000000000..cf93bd0de5cb
--- /dev/null
+++ b/Documentation/devicetree/bindings/usb/cdns3-usb.txt
@@ -0,0 +1,30 @@
+Binding for the Cadence USBSS-DRD controller
+
+Required properties:
+  - reg: Physical base address and size of the controller's register areas.
+	 Controller has 3 different regions:
+	 region 1 - HOST registers area
+	 region 2 - DEVICE registers area
+	 region 3 - OTG/DRD registers area
+  - compatible: Should contain: "cdns,usb3-1.0.0" or "cdns,usb3-1.0.1"
+  - interrupts: Interrupt specifier. Refer to interrupt bindings.
+		Driver supports only single interrupt line.
+                This single interrupt is shared between Device,
+		host and OTG/DRD part of driver.
+
+Optional properties:
+ - maximum-speed : valid arguments are "super-speed", "high-speed" and
+                   "full-speed"; refer to usb/generic.txt
+ - dr_mode: Should be one of "host", "peripheral" or "otg".
+ - phys: reference to the USB PHY
+ - phy-names: name of the USB PHY, should be "cdns3,usbphy"
+
+
+Example:
+	usb@f3000000 {
+		compatible = "cdns,usb3";
+		interrupts = <USB_IRQ  7 IRQ_TYPE_LEVEL_HIGH>;
+		reg = <0xf3000000 0x10000	/* memory area for HOST registers */
+			0xf3010000 0x10000	/* memory area for DEVICE registers */
+			0xf3020000 0x10000>;	/* memory area for OTG/DRD registers */
+	};
-- 
2.17.1


^ permalink raw reply related	[flat|nested] 49+ messages in thread

* [PATCH v2 1/5] dt-bindings: add binding for USBSS-DRD controller.
@ 2018-12-23 15:13   ` Pawel Laszczak
  0 siblings, 0 replies; 49+ messages in thread
From: Pawel Laszczak @ 2018-12-23 15:13 UTC (permalink / raw)
  To: devicetree
  Cc: gregkh, mark.rutland, linux-usb, hdegoede, heikki.krogerus,
	andy.shevchenko, robh+dt, rogerq, linux-kernel, adouglas,
	jbergsagel, nsekhar, nm, sureshp, peter.chen, pjez, kurahul,
	Pawel Laszczak

This patch aim at documenting USB related dt-bindings for the
Cadence USBSS-DRD controller.

Signed-off-by: Pawel Laszczak <pawell@cadence.com>
---
 .../devicetree/bindings/usb/cdns3-usb.txt     | 30 +++++++++++++++++++
 1 file changed, 30 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/usb/cdns3-usb.txt

diff --git a/Documentation/devicetree/bindings/usb/cdns3-usb.txt b/Documentation/devicetree/bindings/usb/cdns3-usb.txt
new file mode 100644
index 000000000000..cf93bd0de5cb
--- /dev/null
+++ b/Documentation/devicetree/bindings/usb/cdns3-usb.txt
@@ -0,0 +1,30 @@
+Binding for the Cadence USBSS-DRD controller
+
+Required properties:
+  - reg: Physical base address and size of the controller's register areas.
+	 Controller has 3 different regions:
+	 region 1 - HOST registers area
+	 region 2 - DEVICE registers area
+	 region 3 - OTG/DRD registers area
+  - compatible: Should contain: "cdns,usb3-1.0.0" or "cdns,usb3-1.0.1"
+  - interrupts: Interrupt specifier. Refer to interrupt bindings.
+		Driver supports only single interrupt line.
+                This single interrupt is shared between Device,
+		host and OTG/DRD part of driver.
+
+Optional properties:
+ - maximum-speed : valid arguments are "super-speed", "high-speed" and
+                   "full-speed"; refer to usb/generic.txt
+ - dr_mode: Should be one of "host", "peripheral" or "otg".
+ - phys: reference to the USB PHY
+ - phy-names: name of the USB PHY, should be "cdns3,usbphy"
+
+
+Example:
+	usb@f3000000 {
+		compatible = "cdns,usb3";
+		interrupts = <USB_IRQ  7 IRQ_TYPE_LEVEL_HIGH>;
+		reg = <0xf3000000 0x10000	/* memory area for HOST registers */
+			0xf3010000 0x10000	/* memory area for DEVICE registers */
+			0xf3020000 0x10000>;	/* memory area for OTG/DRD registers */
+	};
-- 
2.17.1

^ permalink raw reply related	[flat|nested] 49+ messages in thread

* [v2,1/5] dt-bindings: add binding for USBSS-DRD controller.
@ 2018-12-23 15:13   ` Pawel Laszczak
  0 siblings, 0 replies; 49+ messages in thread
From: Pawel Laszczak @ 2018-12-23 15:13 UTC (permalink / raw)
  To: devicetree
  Cc: gregkh, mark.rutland, linux-usb, hdegoede, heikki.krogerus,
	andy.shevchenko, robh+dt, rogerq, linux-kernel, adouglas,
	jbergsagel, nsekhar, nm, sureshp, peter.chen, pjez, kurahul,
	Pawel Laszczak

This patch aim at documenting USB related dt-bindings for the
Cadence USBSS-DRD controller.

Signed-off-by: Pawel Laszczak <pawell@cadence.com>
---
 .../devicetree/bindings/usb/cdns3-usb.txt     | 30 +++++++++++++++++++
 1 file changed, 30 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/usb/cdns3-usb.txt

diff --git a/Documentation/devicetree/bindings/usb/cdns3-usb.txt b/Documentation/devicetree/bindings/usb/cdns3-usb.txt
new file mode 100644
index 000000000000..cf93bd0de5cb
--- /dev/null
+++ b/Documentation/devicetree/bindings/usb/cdns3-usb.txt
@@ -0,0 +1,30 @@
+Binding for the Cadence USBSS-DRD controller
+
+Required properties:
+  - reg: Physical base address and size of the controller's register areas.
+	 Controller has 3 different regions:
+	 region 1 - HOST registers area
+	 region 2 - DEVICE registers area
+	 region 3 - OTG/DRD registers area
+  - compatible: Should contain: "cdns,usb3-1.0.0" or "cdns,usb3-1.0.1"
+  - interrupts: Interrupt specifier. Refer to interrupt bindings.
+		Driver supports only single interrupt line.
+                This single interrupt is shared between Device,
+		host and OTG/DRD part of driver.
+
+Optional properties:
+ - maximum-speed : valid arguments are "super-speed", "high-speed" and
+                   "full-speed"; refer to usb/generic.txt
+ - dr_mode: Should be one of "host", "peripheral" or "otg".
+ - phys: reference to the USB PHY
+ - phy-names: name of the USB PHY, should be "cdns3,usbphy"
+
+
+Example:
+	usb@f3000000 {
+		compatible = "cdns,usb3";
+		interrupts = <USB_IRQ  7 IRQ_TYPE_LEVEL_HIGH>;
+		reg = <0xf3000000 0x10000	/* memory area for HOST registers */
+			0xf3010000 0x10000	/* memory area for DEVICE registers */
+			0xf3020000 0x10000>;	/* memory area for OTG/DRD registers */
+	};

^ permalink raw reply related	[flat|nested] 49+ messages in thread

* [PATCH v2 2/5] usb:common Separated decoding functions from dwc3 driver.
  2018-12-23 15:13 ` Pawel Laszczak
  (?)
@ 2018-12-23 15:13   ` Pawel Laszczak
  -1 siblings, 0 replies; 49+ messages in thread
From: Pawel Laszczak @ 2018-12-23 15:13 UTC (permalink / raw)
  To: devicetree
  Cc: gregkh, mark.rutland, linux-usb, hdegoede, heikki.krogerus,
	andy.shevchenko, robh+dt, rogerq, linux-kernel, adouglas,
	jbergsagel, nsekhar, nm, sureshp, peter.chen, pjez, kurahul,
	Pawel Laszczak

Patch moves some decoding functions from driver/usb/dwc3/debug.h driver
to driver/usb/common/debug.c file. These moved functions include:
    dwc3_decode_get_status
    dwc3_decode_set_clear_feature
    dwc3_decode_set_address
    dwc3_decode_get_set_descriptor
    dwc3_decode_get_configuration
    dwc3_decode_set_configuration
    dwc3_decode_get_intf
    dwc3_decode_set_intf
    dwc3_decode_synch_frame
    dwc3_decode_set_sel
    dwc3_decode_set_isoch_delay
    dwc3_decode_ctrl

These functions are used also in inroduced cdns3 driver.

All functions prefixes were changed from dwc3 to usb.
Also, function's parameters has been extended according to the name
of fields in standard SETUP packet.
Additionally, patch adds usb_decode_ctrl function to
include/linux/usb/ch9.h file.

mend

Signed-off-by: Pawel Laszczak <pawell@cadence.com>
---
 drivers/usb/common/Makefile |   2 +-
 drivers/usb/common/debug.c  | 265 ++++++++++++++++++++++++++++++++++++
 drivers/usb/dwc3/debug.h    | 243 ---------------------------------
 drivers/usb/dwc3/trace.h    |   2 +-
 include/linux/usb/ch9.h     |  19 +++
 5 files changed, 286 insertions(+), 245 deletions(-)
 create mode 100644 drivers/usb/common/debug.c

diff --git a/drivers/usb/common/Makefile b/drivers/usb/common/Makefile
index fb4d5ef4165c..3d3d2962ea4b 100644
--- a/drivers/usb/common/Makefile
+++ b/drivers/usb/common/Makefile
@@ -4,7 +4,7 @@
 #
 
 obj-$(CONFIG_USB_COMMON)	  += usb-common.o
-usb-common-y			  += common.o
+usb-common-y			  += common.o debug.o
 usb-common-$(CONFIG_USB_LED_TRIG) += led.o
 
 obj-$(CONFIG_USB_OTG_FSM) += usb-otg-fsm.o
diff --git a/drivers/usb/common/debug.c b/drivers/usb/common/debug.c
new file mode 100644
index 000000000000..374fb9e94f37
--- /dev/null
+++ b/drivers/usb/common/debug.c
@@ -0,0 +1,265 @@
+// SPDX-License-Identifier: GPL-2.0
+/**
+ * Common USB debugging functions
+ *
+ * Copyright (C) 2010-2011 Texas Instruments Incorporated - http://www.ti.com
+ *
+ * Authors: Felipe Balbi <balbi@ti.com>,
+ *	    Sebastian Andrzej Siewior <bigeasy@linutronix.de>
+ */
+
+#ifndef __LINUX_USB_COMMON_DEBUG
+#define __LINUX_USB_COMMON_DEBUG
+
+#include <linux/usb/ch9.h>
+
+static void usb_decode_get_status(__u8 bRequestType, __u16 wIndex,
+				  __u16 wLength, char *str)
+{
+	switch (bRequestType & USB_RECIP_MASK) {
+	case USB_RECIP_INTERFACE:
+		sprintf(str, "Get Interface Status(Intf = %d, Length = %d)",
+			wIndex, wLength);
+		break;
+	case USB_RECIP_ENDPOINT:
+		sprintf(str, "Get Endpoint Status(ep%d%s)",
+			wIndex & ~USB_DIR_IN,
+			wIndex & USB_DIR_IN ? "in" : "out");
+		break;
+	}
+}
+
+static void usb_decode_set_clear_feature(__u8 bRequestType,
+					 __u8 bRequest, __u16 wValue,
+					 __u16 wIndex, char *str)
+{
+	switch (bRequestType & USB_RECIP_MASK) {
+	case USB_RECIP_DEVICE:
+		sprintf(str, "%s Device Feature(%s%s)",
+			bRequest == USB_REQ_CLEAR_FEATURE ? "Clear" : "Set",
+			({char *s;
+				switch (wValue) {
+				case USB_DEVICE_SELF_POWERED:
+					s = "Self Powered";
+					break;
+				case USB_DEVICE_REMOTE_WAKEUP:
+					s = "Remote Wakeup";
+					break;
+				case USB_DEVICE_TEST_MODE:
+					s = "Test Mode";
+					break;
+				case USB_DEVICE_U1_ENABLE:
+					s = "U1 Enable";
+					break;
+				case USB_DEVICE_U2_ENABLE:
+					s = "U2 Enable";
+					break;
+				case USB_DEVICE_LTM_ENABLE:
+					s = "LTM Enable";
+					break;
+				default:
+					s = "UNKNOWN";
+				} s; }),
+			wValue == USB_DEVICE_TEST_MODE ?
+			({ char *s;
+				switch (wIndex) {
+				case TEST_J:
+					s = ": TEST_J";
+					break;
+				case TEST_K:
+					s = ": TEST_K";
+					break;
+				case TEST_SE0_NAK:
+					s = ": TEST_SE0_NAK";
+					break;
+				case TEST_PACKET:
+					s = ": TEST_PACKET";
+					break;
+				case TEST_FORCE_EN:
+					s = ": TEST_FORCE_EN";
+					break;
+				default:
+					s = ": UNKNOWN";
+				} s; }) : "");
+		break;
+	case USB_RECIP_INTERFACE:
+		sprintf(str, "%s Interface Feature(%s)",
+			bRequest == USB_REQ_CLEAR_FEATURE ? "Clear" : "Set",
+			wValue == USB_INTRF_FUNC_SUSPEND ?
+			"Function Suspend" : "UNKNOWN");
+		break;
+	case USB_RECIP_ENDPOINT:
+		sprintf(str, "%s Endpoint Feature(%s ep%d%s)",
+			bRequest == USB_REQ_CLEAR_FEATURE ? "Clear" : "Set",
+			wValue == USB_ENDPOINT_HALT ? "Halt" : "UNKNOWN",
+			wIndex & ~USB_DIR_IN,
+			wIndex & USB_DIR_IN ? "in" : "out");
+		break;
+	}
+}
+
+static void usb_decode_set_address(__u16 wValue, char *str)
+{
+	sprintf(str, "Set Address(Addr = %02x)", wValue);
+}
+
+static void usb_decode_get_set_descriptor(__u8 bRequestType, __u8 bRequest,
+					  __u16 wValue, __u16 wIndex,
+					  __u16 wLength, char *str)
+{
+	sprintf(str, "%s %s Descriptor(Index = %d, Length = %d)",
+		bRequest == USB_REQ_GET_DESCRIPTOR ? "Get" : "Set",
+		({ char *s;
+			switch (wValue >> 8) {
+			case USB_DT_DEVICE:
+				s = "Device";
+				break;
+			case USB_DT_CONFIG:
+				s = "Configuration";
+				break;
+			case USB_DT_STRING:
+				s = "String";
+				break;
+			case USB_DT_INTERFACE:
+				s = "Interface";
+				break;
+			case USB_DT_ENDPOINT:
+				s = "Endpoint";
+				break;
+			case USB_DT_DEVICE_QUALIFIER:
+				s = "Device Qualifier";
+				break;
+			case USB_DT_OTHER_SPEED_CONFIG:
+				s = "Other Speed Config";
+				break;
+			case USB_DT_INTERFACE_POWER:
+				s = "Interface Power";
+				break;
+			case USB_DT_OTG:
+				s = "OTG";
+				break;
+			case USB_DT_DEBUG:
+				s = "Debug";
+				break;
+			case USB_DT_INTERFACE_ASSOCIATION:
+				s = "Interface Association";
+				break;
+			case USB_DT_BOS:
+				s = "BOS";
+				break;
+			case USB_DT_DEVICE_CAPABILITY:
+				s = "Device Capability";
+				break;
+			case USB_DT_PIPE_USAGE:
+				s = "Pipe Usage";
+				break;
+			case USB_DT_SS_ENDPOINT_COMP:
+				s = "SS Endpoint Companion";
+				break;
+			case USB_DT_SSP_ISOC_ENDPOINT_COMP:
+				s = "SSP Isochronous Endpoint Companion";
+				break;
+			default:
+				s = "UNKNOWN";
+				break;
+			} s; }), wValue & 0xff, wLength);
+}
+
+static void usb_decode_get_configuration(__u16 wLength, char *str)
+{
+	sprintf(str, "Get Configuration(Length = %d)", wLength);
+}
+
+static inline void usb_decode_set_configuration(__u8 wValue, char *str)
+{
+	sprintf(str, "Set Configuration(Config = %d)", wValue);
+}
+
+static void usb_decode_get_intf(__u16 wIndex, __u16 wLength, char *str)
+{
+	sprintf(str, "Get Interface(Intf = %d, Length = %d)", wIndex, wLength);
+}
+
+static void usb_decode_set_intf(__u8 wValue, __u16 wIndex, char *str)
+{
+	sprintf(str, "Set Interface(Intf = %d, Alt.Setting = %d)",
+		wIndex, wValue);
+}
+
+static void usb_decode_synch_frame(__u16 wIndex, __u16 wLength,
+				   char *str)
+{
+	sprintf(str, "Synch Frame(Endpoint = %d, Length = %d)",
+		wIndex, wLength);
+}
+
+static void usb_decode_set_sel(__u16 wLength, char *str)
+{
+	sprintf(str, "Set SEL(Length = %d)", wLength);
+}
+
+static void usb_decode_set_isoch_delay(__u8 wValue, char *str)
+{
+	sprintf(str, "Set Isochronous Delay(Delay = %d ns)", wValue);
+}
+
+/**
+ * usb_decode_ctrl - returns a string representation of ctrl request
+ */
+const char *usb_decode_ctrl(char *str, __u8 bRequestType, __u8 bRequest,
+			    __u16 wValue,  __u16 wIndex, __u16 wLength)
+{
+	switch (bRequest) {
+	case USB_REQ_GET_STATUS:
+		usb_decode_get_status(bRequestType, wIndex, wLength, str);
+		break;
+	case USB_REQ_CLEAR_FEATURE:
+	case USB_REQ_SET_FEATURE:
+		usb_decode_set_clear_feature(bRequestType, bRequest, wValue,
+					     wIndex, str);
+		break;
+	case USB_REQ_SET_ADDRESS:
+		usb_decode_set_address(wValue, str);
+		break;
+	case USB_REQ_GET_DESCRIPTOR:
+	case USB_REQ_SET_DESCRIPTOR:
+		usb_decode_get_set_descriptor(bRequestType, bRequest, wValue,
+					      wIndex, wLength, str);
+		break;
+	case USB_REQ_GET_CONFIGURATION:
+		usb_decode_get_configuration(wLength, str);
+		break;
+	case USB_REQ_SET_CONFIGURATION:
+		usb_decode_set_configuration(wValue, str);
+		break;
+	case USB_REQ_GET_INTERFACE:
+		usb_decode_get_intf(wIndex, wLength, str);
+		break;
+	case USB_REQ_SET_INTERFACE:
+		usb_decode_set_intf(wValue, wIndex, str);
+		break;
+	case USB_REQ_SYNCH_FRAME:
+		usb_decode_synch_frame(wIndex, wLength, str);
+		break;
+	case USB_REQ_SET_SEL:
+		usb_decode_set_sel(wLength, str);
+		break;
+	case USB_REQ_SET_ISOCH_DELAY:
+		usb_decode_set_isoch_delay(wValue, str);
+		break;
+	default:
+		sprintf(str, "%02x %02x %02x %02x %02x %02x %02x %02x",
+			bRequestType, bRequest,
+			cpu_to_le16(wValue) & 0xff,
+			cpu_to_le16(wValue) >> 8,
+			cpu_to_le16(wIndex) & 0xff,
+			cpu_to_le16(wIndex) >> 8,
+			cpu_to_le16(wLength) & 0xff,
+			cpu_to_le16(wLength) >> 8);
+	}
+
+	return str;
+}
+EXPORT_SYMBOL_GPL(usb_decode_ctrl);
+
+#endif /* __LINUX_USB_COMMON_DEBUG */
diff --git a/drivers/usb/dwc3/debug.h b/drivers/usb/dwc3/debug.h
index c66d216dcc30..9db2b0fb8f22 100644
--- a/drivers/usb/dwc3/debug.h
+++ b/drivers/usb/dwc3/debug.h
@@ -214,249 +214,6 @@ dwc3_gadget_event_string(char *str, const struct dwc3_event_devt *event)
 	return str;
 }
 
-static inline void dwc3_decode_get_status(__u8 t, __u16 i, __u16 l, char *str)
-{
-	switch (t & USB_RECIP_MASK) {
-	case USB_RECIP_INTERFACE:
-		sprintf(str, "Get Interface Status(Intf = %d, Length = %d)",
-			i, l);
-		break;
-	case USB_RECIP_ENDPOINT:
-		sprintf(str, "Get Endpoint Status(ep%d%s)",
-			i & ~USB_DIR_IN,
-			i & USB_DIR_IN ? "in" : "out");
-		break;
-	}
-}
-
-static inline void dwc3_decode_set_clear_feature(__u8 t, __u8 b, __u16 v,
-						 __u16 i, char *str)
-{
-	switch (t & USB_RECIP_MASK) {
-	case USB_RECIP_DEVICE:
-		sprintf(str, "%s Device Feature(%s%s)",
-			b == USB_REQ_CLEAR_FEATURE ? "Clear" : "Set",
-			({char *s;
-				switch (v) {
-				case USB_DEVICE_SELF_POWERED:
-					s = "Self Powered";
-					break;
-				case USB_DEVICE_REMOTE_WAKEUP:
-					s = "Remote Wakeup";
-					break;
-				case USB_DEVICE_TEST_MODE:
-					s = "Test Mode";
-					break;
-				case USB_DEVICE_U1_ENABLE:
-					s = "U1 Enable";
-					break;
-				case USB_DEVICE_U2_ENABLE:
-					s = "U2 Enable";
-					break;
-				case USB_DEVICE_LTM_ENABLE:
-					s = "LTM Enable";
-					break;
-				default:
-					s = "UNKNOWN";
-				} s; }),
-			v == USB_DEVICE_TEST_MODE ?
-			({ char *s;
-				switch (i) {
-				case TEST_J:
-					s = ": TEST_J";
-					break;
-				case TEST_K:
-					s = ": TEST_K";
-					break;
-				case TEST_SE0_NAK:
-					s = ": TEST_SE0_NAK";
-					break;
-				case TEST_PACKET:
-					s = ": TEST_PACKET";
-					break;
-				case TEST_FORCE_EN:
-					s = ": TEST_FORCE_EN";
-					break;
-				default:
-					s = ": UNKNOWN";
-				} s; }) : "");
-		break;
-	case USB_RECIP_INTERFACE:
-		sprintf(str, "%s Interface Feature(%s)",
-			b == USB_REQ_CLEAR_FEATURE ? "Clear" : "Set",
-			v == USB_INTRF_FUNC_SUSPEND ?
-			"Function Suspend" : "UNKNOWN");
-		break;
-	case USB_RECIP_ENDPOINT:
-		sprintf(str, "%s Endpoint Feature(%s ep%d%s)",
-			b == USB_REQ_CLEAR_FEATURE ? "Clear" : "Set",
-			v == USB_ENDPOINT_HALT ? "Halt" : "UNKNOWN",
-			i & ~USB_DIR_IN,
-			i & USB_DIR_IN ? "in" : "out");
-		break;
-	}
-}
-
-static inline void dwc3_decode_set_address(__u16 v, char *str)
-{
-	sprintf(str, "Set Address(Addr = %02x)", v);
-}
-
-static inline void dwc3_decode_get_set_descriptor(__u8 t, __u8 b, __u16 v,
-						  __u16 i, __u16 l, char *str)
-{
-	sprintf(str, "%s %s Descriptor(Index = %d, Length = %d)",
-		b == USB_REQ_GET_DESCRIPTOR ? "Get" : "Set",
-		({ char *s;
-			switch (v >> 8) {
-			case USB_DT_DEVICE:
-				s = "Device";
-				break;
-			case USB_DT_CONFIG:
-				s = "Configuration";
-				break;
-			case USB_DT_STRING:
-				s = "String";
-				break;
-			case USB_DT_INTERFACE:
-				s = "Interface";
-				break;
-			case USB_DT_ENDPOINT:
-				s = "Endpoint";
-				break;
-			case USB_DT_DEVICE_QUALIFIER:
-				s = "Device Qualifier";
-				break;
-			case USB_DT_OTHER_SPEED_CONFIG:
-				s = "Other Speed Config";
-				break;
-			case USB_DT_INTERFACE_POWER:
-				s = "Interface Power";
-				break;
-			case USB_DT_OTG:
-				s = "OTG";
-				break;
-			case USB_DT_DEBUG:
-				s = "Debug";
-				break;
-			case USB_DT_INTERFACE_ASSOCIATION:
-				s = "Interface Association";
-				break;
-			case USB_DT_BOS:
-				s = "BOS";
-				break;
-			case USB_DT_DEVICE_CAPABILITY:
-				s = "Device Capability";
-				break;
-			case USB_DT_PIPE_USAGE:
-				s = "Pipe Usage";
-				break;
-			case USB_DT_SS_ENDPOINT_COMP:
-				s = "SS Endpoint Companion";
-				break;
-			case USB_DT_SSP_ISOC_ENDPOINT_COMP:
-				s = "SSP Isochronous Endpoint Companion";
-				break;
-			default:
-				s = "UNKNOWN";
-				break;
-			} s; }), v & 0xff, l);
-}
-
-
-static inline void dwc3_decode_get_configuration(__u16 l, char *str)
-{
-	sprintf(str, "Get Configuration(Length = %d)", l);
-}
-
-static inline void dwc3_decode_set_configuration(__u8 v, char *str)
-{
-	sprintf(str, "Set Configuration(Config = %d)", v);
-}
-
-static inline void dwc3_decode_get_intf(__u16 i, __u16 l, char *str)
-{
-	sprintf(str, "Get Interface(Intf = %d, Length = %d)", i, l);
-}
-
-static inline void dwc3_decode_set_intf(__u8 v, __u16 i, char *str)
-{
-	sprintf(str, "Set Interface(Intf = %d, Alt.Setting = %d)", i, v);
-}
-
-static inline void dwc3_decode_synch_frame(__u16 i, __u16 l, char *str)
-{
-	sprintf(str, "Synch Frame(Endpoint = %d, Length = %d)", i, l);
-}
-
-static inline void dwc3_decode_set_sel(__u16 l, char *str)
-{
-	sprintf(str, "Set SEL(Length = %d)", l);
-}
-
-static inline void dwc3_decode_set_isoch_delay(__u8 v, char *str)
-{
-	sprintf(str, "Set Isochronous Delay(Delay = %d ns)", v);
-}
-
-/**
- * dwc3_decode_ctrl - returns a string represetion of ctrl request
- */
-static inline const char *dwc3_decode_ctrl(char *str, __u8 bRequestType,
-		__u8 bRequest, __u16 wValue, __u16 wIndex, __u16 wLength)
-{
-	switch (bRequest) {
-	case USB_REQ_GET_STATUS:
-		dwc3_decode_get_status(bRequestType, wIndex, wLength, str);
-		break;
-	case USB_REQ_CLEAR_FEATURE:
-	case USB_REQ_SET_FEATURE:
-		dwc3_decode_set_clear_feature(bRequestType, bRequest, wValue,
-					      wIndex, str);
-		break;
-	case USB_REQ_SET_ADDRESS:
-		dwc3_decode_set_address(wValue, str);
-		break;
-	case USB_REQ_GET_DESCRIPTOR:
-	case USB_REQ_SET_DESCRIPTOR:
-		dwc3_decode_get_set_descriptor(bRequestType, bRequest, wValue,
-					       wIndex, wLength, str);
-		break;
-	case USB_REQ_GET_CONFIGURATION:
-		dwc3_decode_get_configuration(wLength, str);
-		break;
-	case USB_REQ_SET_CONFIGURATION:
-		dwc3_decode_set_configuration(wValue, str);
-		break;
-	case USB_REQ_GET_INTERFACE:
-		dwc3_decode_get_intf(wIndex, wLength, str);
-		break;
-	case USB_REQ_SET_INTERFACE:
-		dwc3_decode_set_intf(wValue, wIndex, str);
-		break;
-	case USB_REQ_SYNCH_FRAME:
-		dwc3_decode_synch_frame(wIndex, wLength, str);
-		break;
-	case USB_REQ_SET_SEL:
-		dwc3_decode_set_sel(wLength, str);
-		break;
-	case USB_REQ_SET_ISOCH_DELAY:
-		dwc3_decode_set_isoch_delay(wValue, str);
-		break;
-	default:
-		sprintf(str, "%02x %02x %02x %02x %02x %02x %02x %02x",
-			bRequestType, bRequest,
-			cpu_to_le16(wValue) & 0xff,
-			cpu_to_le16(wValue) >> 8,
-			cpu_to_le16(wIndex) & 0xff,
-			cpu_to_le16(wIndex) >> 8,
-			cpu_to_le16(wLength) & 0xff,
-			cpu_to_le16(wLength) >> 8);
-	}
-
-	return str;
-}
-
 /**
  * dwc3_ep_event_string - returns event name
  * @event: then event code
diff --git a/drivers/usb/dwc3/trace.h b/drivers/usb/dwc3/trace.h
index f22714cce070..6236f0657583 100644
--- a/drivers/usb/dwc3/trace.h
+++ b/drivers/usb/dwc3/trace.h
@@ -86,7 +86,7 @@ DECLARE_EVENT_CLASS(dwc3_log_ctrl,
 		__entry->wIndex = le16_to_cpu(ctrl->wIndex);
 		__entry->wLength = le16_to_cpu(ctrl->wLength);
 	),
-	TP_printk("%s", dwc3_decode_ctrl(__get_str(str), __entry->bRequestType,
+	TP_printk("%s", usb_decode_ctrl(__get_str(str), __entry->bRequestType,
 					__entry->bRequest, __entry->wValue,
 					__entry->wIndex, __entry->wLength)
 	)
diff --git a/include/linux/usb/ch9.h b/include/linux/usb/ch9.h
index 523aa088f6ab..a33cd2b9fbba 100644
--- a/include/linux/usb/ch9.h
+++ b/include/linux/usb/ch9.h
@@ -62,4 +62,23 @@ extern enum usb_device_speed usb_get_maximum_speed(struct device *dev);
  */
 extern const char *usb_state_string(enum usb_device_state state);
 
+/**
+ * usb_decode_ctrl - Returns human readable representation of control request.
+ * @str: buffer to return a human-readable representation of control request.
+ *       This buffer should have about 200 bytes.
+ * @bRequestType: matches the USB bmRequestType field
+ * @bRequest: matches the USB bRequest field
+ * @wValue: matches the USB wValue field (CPU byte order)
+ * @wIndex: matches the USB wIndex field (CPU byte order)
+ * @wLength: matches the USB wLength field (CPU byte order)
+ *
+ * Function returns decoded, formatted and human-readable description of
+ * control request packet.
+ *
+ * Important: wValue, wIndex, wLength parameters before invoking this function
+ * should be processed by le16_to_cpu macro.
+ */
+const char *usb_decode_ctrl(char *str, __u8 bRequestType, __u8 bRequest,
+			    __u16 wValue,  __u16 wIndex, __u16 wLength);
+
 #endif /* __LINUX_USB_CH9_H */
-- 
2.17.1


^ permalink raw reply related	[flat|nested] 49+ messages in thread

* [PATCH v2 2/5] usb:common Separated decoding functions from dwc3 driver.
@ 2018-12-23 15:13   ` Pawel Laszczak
  0 siblings, 0 replies; 49+ messages in thread
From: Pawel Laszczak @ 2018-12-23 15:13 UTC (permalink / raw)
  To: devicetree
  Cc: gregkh, mark.rutland, linux-usb, hdegoede, heikki.krogerus,
	andy.shevchenko, robh+dt, rogerq, linux-kernel, adouglas,
	jbergsagel, nsekhar, nm, sureshp, peter.chen, pjez, kurahul,
	Pawel Laszczak

Patch moves some decoding functions from driver/usb/dwc3/debug.h driver
to driver/usb/common/debug.c file. These moved functions include:
    dwc3_decode_get_status
    dwc3_decode_set_clear_feature
    dwc3_decode_set_address
    dwc3_decode_get_set_descriptor
    dwc3_decode_get_configuration
    dwc3_decode_set_configuration
    dwc3_decode_get_intf
    dwc3_decode_set_intf
    dwc3_decode_synch_frame
    dwc3_decode_set_sel
    dwc3_decode_set_isoch_delay
    dwc3_decode_ctrl

These functions are used also in inroduced cdns3 driver.

All functions prefixes were changed from dwc3 to usb.
Also, function's parameters has been extended according to the name
of fields in standard SETUP packet.
Additionally, patch adds usb_decode_ctrl function to
include/linux/usb/ch9.h file.

mend

Signed-off-by: Pawel Laszczak <pawell@cadence.com>
---
 drivers/usb/common/Makefile |   2 +-
 drivers/usb/common/debug.c  | 265 ++++++++++++++++++++++++++++++++++++
 drivers/usb/dwc3/debug.h    | 243 ---------------------------------
 drivers/usb/dwc3/trace.h    |   2 +-
 include/linux/usb/ch9.h     |  19 +++
 5 files changed, 286 insertions(+), 245 deletions(-)
 create mode 100644 drivers/usb/common/debug.c

diff --git a/drivers/usb/common/Makefile b/drivers/usb/common/Makefile
index fb4d5ef4165c..3d3d2962ea4b 100644
--- a/drivers/usb/common/Makefile
+++ b/drivers/usb/common/Makefile
@@ -4,7 +4,7 @@
 #
 
 obj-$(CONFIG_USB_COMMON)	  += usb-common.o
-usb-common-y			  += common.o
+usb-common-y			  += common.o debug.o
 usb-common-$(CONFIG_USB_LED_TRIG) += led.o
 
 obj-$(CONFIG_USB_OTG_FSM) += usb-otg-fsm.o
diff --git a/drivers/usb/common/debug.c b/drivers/usb/common/debug.c
new file mode 100644
index 000000000000..374fb9e94f37
--- /dev/null
+++ b/drivers/usb/common/debug.c
@@ -0,0 +1,265 @@
+// SPDX-License-Identifier: GPL-2.0
+/**
+ * Common USB debugging functions
+ *
+ * Copyright (C) 2010-2011 Texas Instruments Incorporated - http://www.ti.com
+ *
+ * Authors: Felipe Balbi <balbi@ti.com>,
+ *	    Sebastian Andrzej Siewior <bigeasy@linutronix.de>
+ */
+
+#ifndef __LINUX_USB_COMMON_DEBUG
+#define __LINUX_USB_COMMON_DEBUG
+
+#include <linux/usb/ch9.h>
+
+static void usb_decode_get_status(__u8 bRequestType, __u16 wIndex,
+				  __u16 wLength, char *str)
+{
+	switch (bRequestType & USB_RECIP_MASK) {
+	case USB_RECIP_INTERFACE:
+		sprintf(str, "Get Interface Status(Intf = %d, Length = %d)",
+			wIndex, wLength);
+		break;
+	case USB_RECIP_ENDPOINT:
+		sprintf(str, "Get Endpoint Status(ep%d%s)",
+			wIndex & ~USB_DIR_IN,
+			wIndex & USB_DIR_IN ? "in" : "out");
+		break;
+	}
+}
+
+static void usb_decode_set_clear_feature(__u8 bRequestType,
+					 __u8 bRequest, __u16 wValue,
+					 __u16 wIndex, char *str)
+{
+	switch (bRequestType & USB_RECIP_MASK) {
+	case USB_RECIP_DEVICE:
+		sprintf(str, "%s Device Feature(%s%s)",
+			bRequest == USB_REQ_CLEAR_FEATURE ? "Clear" : "Set",
+			({char *s;
+				switch (wValue) {
+				case USB_DEVICE_SELF_POWERED:
+					s = "Self Powered";
+					break;
+				case USB_DEVICE_REMOTE_WAKEUP:
+					s = "Remote Wakeup";
+					break;
+				case USB_DEVICE_TEST_MODE:
+					s = "Test Mode";
+					break;
+				case USB_DEVICE_U1_ENABLE:
+					s = "U1 Enable";
+					break;
+				case USB_DEVICE_U2_ENABLE:
+					s = "U2 Enable";
+					break;
+				case USB_DEVICE_LTM_ENABLE:
+					s = "LTM Enable";
+					break;
+				default:
+					s = "UNKNOWN";
+				} s; }),
+			wValue == USB_DEVICE_TEST_MODE ?
+			({ char *s;
+				switch (wIndex) {
+				case TEST_J:
+					s = ": TEST_J";
+					break;
+				case TEST_K:
+					s = ": TEST_K";
+					break;
+				case TEST_SE0_NAK:
+					s = ": TEST_SE0_NAK";
+					break;
+				case TEST_PACKET:
+					s = ": TEST_PACKET";
+					break;
+				case TEST_FORCE_EN:
+					s = ": TEST_FORCE_EN";
+					break;
+				default:
+					s = ": UNKNOWN";
+				} s; }) : "");
+		break;
+	case USB_RECIP_INTERFACE:
+		sprintf(str, "%s Interface Feature(%s)",
+			bRequest == USB_REQ_CLEAR_FEATURE ? "Clear" : "Set",
+			wValue == USB_INTRF_FUNC_SUSPEND ?
+			"Function Suspend" : "UNKNOWN");
+		break;
+	case USB_RECIP_ENDPOINT:
+		sprintf(str, "%s Endpoint Feature(%s ep%d%s)",
+			bRequest == USB_REQ_CLEAR_FEATURE ? "Clear" : "Set",
+			wValue == USB_ENDPOINT_HALT ? "Halt" : "UNKNOWN",
+			wIndex & ~USB_DIR_IN,
+			wIndex & USB_DIR_IN ? "in" : "out");
+		break;
+	}
+}
+
+static void usb_decode_set_address(__u16 wValue, char *str)
+{
+	sprintf(str, "Set Address(Addr = %02x)", wValue);
+}
+
+static void usb_decode_get_set_descriptor(__u8 bRequestType, __u8 bRequest,
+					  __u16 wValue, __u16 wIndex,
+					  __u16 wLength, char *str)
+{
+	sprintf(str, "%s %s Descriptor(Index = %d, Length = %d)",
+		bRequest == USB_REQ_GET_DESCRIPTOR ? "Get" : "Set",
+		({ char *s;
+			switch (wValue >> 8) {
+			case USB_DT_DEVICE:
+				s = "Device";
+				break;
+			case USB_DT_CONFIG:
+				s = "Configuration";
+				break;
+			case USB_DT_STRING:
+				s = "String";
+				break;
+			case USB_DT_INTERFACE:
+				s = "Interface";
+				break;
+			case USB_DT_ENDPOINT:
+				s = "Endpoint";
+				break;
+			case USB_DT_DEVICE_QUALIFIER:
+				s = "Device Qualifier";
+				break;
+			case USB_DT_OTHER_SPEED_CONFIG:
+				s = "Other Speed Config";
+				break;
+			case USB_DT_INTERFACE_POWER:
+				s = "Interface Power";
+				break;
+			case USB_DT_OTG:
+				s = "OTG";
+				break;
+			case USB_DT_DEBUG:
+				s = "Debug";
+				break;
+			case USB_DT_INTERFACE_ASSOCIATION:
+				s = "Interface Association";
+				break;
+			case USB_DT_BOS:
+				s = "BOS";
+				break;
+			case USB_DT_DEVICE_CAPABILITY:
+				s = "Device Capability";
+				break;
+			case USB_DT_PIPE_USAGE:
+				s = "Pipe Usage";
+				break;
+			case USB_DT_SS_ENDPOINT_COMP:
+				s = "SS Endpoint Companion";
+				break;
+			case USB_DT_SSP_ISOC_ENDPOINT_COMP:
+				s = "SSP Isochronous Endpoint Companion";
+				break;
+			default:
+				s = "UNKNOWN";
+				break;
+			} s; }), wValue & 0xff, wLength);
+}
+
+static void usb_decode_get_configuration(__u16 wLength, char *str)
+{
+	sprintf(str, "Get Configuration(Length = %d)", wLength);
+}
+
+static inline void usb_decode_set_configuration(__u8 wValue, char *str)
+{
+	sprintf(str, "Set Configuration(Config = %d)", wValue);
+}
+
+static void usb_decode_get_intf(__u16 wIndex, __u16 wLength, char *str)
+{
+	sprintf(str, "Get Interface(Intf = %d, Length = %d)", wIndex, wLength);
+}
+
+static void usb_decode_set_intf(__u8 wValue, __u16 wIndex, char *str)
+{
+	sprintf(str, "Set Interface(Intf = %d, Alt.Setting = %d)",
+		wIndex, wValue);
+}
+
+static void usb_decode_synch_frame(__u16 wIndex, __u16 wLength,
+				   char *str)
+{
+	sprintf(str, "Synch Frame(Endpoint = %d, Length = %d)",
+		wIndex, wLength);
+}
+
+static void usb_decode_set_sel(__u16 wLength, char *str)
+{
+	sprintf(str, "Set SEL(Length = %d)", wLength);
+}
+
+static void usb_decode_set_isoch_delay(__u8 wValue, char *str)
+{
+	sprintf(str, "Set Isochronous Delay(Delay = %d ns)", wValue);
+}
+
+/**
+ * usb_decode_ctrl - returns a string representation of ctrl request
+ */
+const char *usb_decode_ctrl(char *str, __u8 bRequestType, __u8 bRequest,
+			    __u16 wValue,  __u16 wIndex, __u16 wLength)
+{
+	switch (bRequest) {
+	case USB_REQ_GET_STATUS:
+		usb_decode_get_status(bRequestType, wIndex, wLength, str);
+		break;
+	case USB_REQ_CLEAR_FEATURE:
+	case USB_REQ_SET_FEATURE:
+		usb_decode_set_clear_feature(bRequestType, bRequest, wValue,
+					     wIndex, str);
+		break;
+	case USB_REQ_SET_ADDRESS:
+		usb_decode_set_address(wValue, str);
+		break;
+	case USB_REQ_GET_DESCRIPTOR:
+	case USB_REQ_SET_DESCRIPTOR:
+		usb_decode_get_set_descriptor(bRequestType, bRequest, wValue,
+					      wIndex, wLength, str);
+		break;
+	case USB_REQ_GET_CONFIGURATION:
+		usb_decode_get_configuration(wLength, str);
+		break;
+	case USB_REQ_SET_CONFIGURATION:
+		usb_decode_set_configuration(wValue, str);
+		break;
+	case USB_REQ_GET_INTERFACE:
+		usb_decode_get_intf(wIndex, wLength, str);
+		break;
+	case USB_REQ_SET_INTERFACE:
+		usb_decode_set_intf(wValue, wIndex, str);
+		break;
+	case USB_REQ_SYNCH_FRAME:
+		usb_decode_synch_frame(wIndex, wLength, str);
+		break;
+	case USB_REQ_SET_SEL:
+		usb_decode_set_sel(wLength, str);
+		break;
+	case USB_REQ_SET_ISOCH_DELAY:
+		usb_decode_set_isoch_delay(wValue, str);
+		break;
+	default:
+		sprintf(str, "%02x %02x %02x %02x %02x %02x %02x %02x",
+			bRequestType, bRequest,
+			cpu_to_le16(wValue) & 0xff,
+			cpu_to_le16(wValue) >> 8,
+			cpu_to_le16(wIndex) & 0xff,
+			cpu_to_le16(wIndex) >> 8,
+			cpu_to_le16(wLength) & 0xff,
+			cpu_to_le16(wLength) >> 8);
+	}
+
+	return str;
+}
+EXPORT_SYMBOL_GPL(usb_decode_ctrl);
+
+#endif /* __LINUX_USB_COMMON_DEBUG */
diff --git a/drivers/usb/dwc3/debug.h b/drivers/usb/dwc3/debug.h
index c66d216dcc30..9db2b0fb8f22 100644
--- a/drivers/usb/dwc3/debug.h
+++ b/drivers/usb/dwc3/debug.h
@@ -214,249 +214,6 @@ dwc3_gadget_event_string(char *str, const struct dwc3_event_devt *event)
 	return str;
 }
 
-static inline void dwc3_decode_get_status(__u8 t, __u16 i, __u16 l, char *str)
-{
-	switch (t & USB_RECIP_MASK) {
-	case USB_RECIP_INTERFACE:
-		sprintf(str, "Get Interface Status(Intf = %d, Length = %d)",
-			i, l);
-		break;
-	case USB_RECIP_ENDPOINT:
-		sprintf(str, "Get Endpoint Status(ep%d%s)",
-			i & ~USB_DIR_IN,
-			i & USB_DIR_IN ? "in" : "out");
-		break;
-	}
-}
-
-static inline void dwc3_decode_set_clear_feature(__u8 t, __u8 b, __u16 v,
-						 __u16 i, char *str)
-{
-	switch (t & USB_RECIP_MASK) {
-	case USB_RECIP_DEVICE:
-		sprintf(str, "%s Device Feature(%s%s)",
-			b == USB_REQ_CLEAR_FEATURE ? "Clear" : "Set",
-			({char *s;
-				switch (v) {
-				case USB_DEVICE_SELF_POWERED:
-					s = "Self Powered";
-					break;
-				case USB_DEVICE_REMOTE_WAKEUP:
-					s = "Remote Wakeup";
-					break;
-				case USB_DEVICE_TEST_MODE:
-					s = "Test Mode";
-					break;
-				case USB_DEVICE_U1_ENABLE:
-					s = "U1 Enable";
-					break;
-				case USB_DEVICE_U2_ENABLE:
-					s = "U2 Enable";
-					break;
-				case USB_DEVICE_LTM_ENABLE:
-					s = "LTM Enable";
-					break;
-				default:
-					s = "UNKNOWN";
-				} s; }),
-			v == USB_DEVICE_TEST_MODE ?
-			({ char *s;
-				switch (i) {
-				case TEST_J:
-					s = ": TEST_J";
-					break;
-				case TEST_K:
-					s = ": TEST_K";
-					break;
-				case TEST_SE0_NAK:
-					s = ": TEST_SE0_NAK";
-					break;
-				case TEST_PACKET:
-					s = ": TEST_PACKET";
-					break;
-				case TEST_FORCE_EN:
-					s = ": TEST_FORCE_EN";
-					break;
-				default:
-					s = ": UNKNOWN";
-				} s; }) : "");
-		break;
-	case USB_RECIP_INTERFACE:
-		sprintf(str, "%s Interface Feature(%s)",
-			b == USB_REQ_CLEAR_FEATURE ? "Clear" : "Set",
-			v == USB_INTRF_FUNC_SUSPEND ?
-			"Function Suspend" : "UNKNOWN");
-		break;
-	case USB_RECIP_ENDPOINT:
-		sprintf(str, "%s Endpoint Feature(%s ep%d%s)",
-			b == USB_REQ_CLEAR_FEATURE ? "Clear" : "Set",
-			v == USB_ENDPOINT_HALT ? "Halt" : "UNKNOWN",
-			i & ~USB_DIR_IN,
-			i & USB_DIR_IN ? "in" : "out");
-		break;
-	}
-}
-
-static inline void dwc3_decode_set_address(__u16 v, char *str)
-{
-	sprintf(str, "Set Address(Addr = %02x)", v);
-}
-
-static inline void dwc3_decode_get_set_descriptor(__u8 t, __u8 b, __u16 v,
-						  __u16 i, __u16 l, char *str)
-{
-	sprintf(str, "%s %s Descriptor(Index = %d, Length = %d)",
-		b == USB_REQ_GET_DESCRIPTOR ? "Get" : "Set",
-		({ char *s;
-			switch (v >> 8) {
-			case USB_DT_DEVICE:
-				s = "Device";
-				break;
-			case USB_DT_CONFIG:
-				s = "Configuration";
-				break;
-			case USB_DT_STRING:
-				s = "String";
-				break;
-			case USB_DT_INTERFACE:
-				s = "Interface";
-				break;
-			case USB_DT_ENDPOINT:
-				s = "Endpoint";
-				break;
-			case USB_DT_DEVICE_QUALIFIER:
-				s = "Device Qualifier";
-				break;
-			case USB_DT_OTHER_SPEED_CONFIG:
-				s = "Other Speed Config";
-				break;
-			case USB_DT_INTERFACE_POWER:
-				s = "Interface Power";
-				break;
-			case USB_DT_OTG:
-				s = "OTG";
-				break;
-			case USB_DT_DEBUG:
-				s = "Debug";
-				break;
-			case USB_DT_INTERFACE_ASSOCIATION:
-				s = "Interface Association";
-				break;
-			case USB_DT_BOS:
-				s = "BOS";
-				break;
-			case USB_DT_DEVICE_CAPABILITY:
-				s = "Device Capability";
-				break;
-			case USB_DT_PIPE_USAGE:
-				s = "Pipe Usage";
-				break;
-			case USB_DT_SS_ENDPOINT_COMP:
-				s = "SS Endpoint Companion";
-				break;
-			case USB_DT_SSP_ISOC_ENDPOINT_COMP:
-				s = "SSP Isochronous Endpoint Companion";
-				break;
-			default:
-				s = "UNKNOWN";
-				break;
-			} s; }), v & 0xff, l);
-}
-
-
-static inline void dwc3_decode_get_configuration(__u16 l, char *str)
-{
-	sprintf(str, "Get Configuration(Length = %d)", l);
-}
-
-static inline void dwc3_decode_set_configuration(__u8 v, char *str)
-{
-	sprintf(str, "Set Configuration(Config = %d)", v);
-}
-
-static inline void dwc3_decode_get_intf(__u16 i, __u16 l, char *str)
-{
-	sprintf(str, "Get Interface(Intf = %d, Length = %d)", i, l);
-}
-
-static inline void dwc3_decode_set_intf(__u8 v, __u16 i, char *str)
-{
-	sprintf(str, "Set Interface(Intf = %d, Alt.Setting = %d)", i, v);
-}
-
-static inline void dwc3_decode_synch_frame(__u16 i, __u16 l, char *str)
-{
-	sprintf(str, "Synch Frame(Endpoint = %d, Length = %d)", i, l);
-}
-
-static inline void dwc3_decode_set_sel(__u16 l, char *str)
-{
-	sprintf(str, "Set SEL(Length = %d)", l);
-}
-
-static inline void dwc3_decode_set_isoch_delay(__u8 v, char *str)
-{
-	sprintf(str, "Set Isochronous Delay(Delay = %d ns)", v);
-}
-
-/**
- * dwc3_decode_ctrl - returns a string represetion of ctrl request
- */
-static inline const char *dwc3_decode_ctrl(char *str, __u8 bRequestType,
-		__u8 bRequest, __u16 wValue, __u16 wIndex, __u16 wLength)
-{
-	switch (bRequest) {
-	case USB_REQ_GET_STATUS:
-		dwc3_decode_get_status(bRequestType, wIndex, wLength, str);
-		break;
-	case USB_REQ_CLEAR_FEATURE:
-	case USB_REQ_SET_FEATURE:
-		dwc3_decode_set_clear_feature(bRequestType, bRequest, wValue,
-					      wIndex, str);
-		break;
-	case USB_REQ_SET_ADDRESS:
-		dwc3_decode_set_address(wValue, str);
-		break;
-	case USB_REQ_GET_DESCRIPTOR:
-	case USB_REQ_SET_DESCRIPTOR:
-		dwc3_decode_get_set_descriptor(bRequestType, bRequest, wValue,
-					       wIndex, wLength, str);
-		break;
-	case USB_REQ_GET_CONFIGURATION:
-		dwc3_decode_get_configuration(wLength, str);
-		break;
-	case USB_REQ_SET_CONFIGURATION:
-		dwc3_decode_set_configuration(wValue, str);
-		break;
-	case USB_REQ_GET_INTERFACE:
-		dwc3_decode_get_intf(wIndex, wLength, str);
-		break;
-	case USB_REQ_SET_INTERFACE:
-		dwc3_decode_set_intf(wValue, wIndex, str);
-		break;
-	case USB_REQ_SYNCH_FRAME:
-		dwc3_decode_synch_frame(wIndex, wLength, str);
-		break;
-	case USB_REQ_SET_SEL:
-		dwc3_decode_set_sel(wLength, str);
-		break;
-	case USB_REQ_SET_ISOCH_DELAY:
-		dwc3_decode_set_isoch_delay(wValue, str);
-		break;
-	default:
-		sprintf(str, "%02x %02x %02x %02x %02x %02x %02x %02x",
-			bRequestType, bRequest,
-			cpu_to_le16(wValue) & 0xff,
-			cpu_to_le16(wValue) >> 8,
-			cpu_to_le16(wIndex) & 0xff,
-			cpu_to_le16(wIndex) >> 8,
-			cpu_to_le16(wLength) & 0xff,
-			cpu_to_le16(wLength) >> 8);
-	}
-
-	return str;
-}
-
 /**
  * dwc3_ep_event_string - returns event name
  * @event: then event code
diff --git a/drivers/usb/dwc3/trace.h b/drivers/usb/dwc3/trace.h
index f22714cce070..6236f0657583 100644
--- a/drivers/usb/dwc3/trace.h
+++ b/drivers/usb/dwc3/trace.h
@@ -86,7 +86,7 @@ DECLARE_EVENT_CLASS(dwc3_log_ctrl,
 		__entry->wIndex = le16_to_cpu(ctrl->wIndex);
 		__entry->wLength = le16_to_cpu(ctrl->wLength);
 	),
-	TP_printk("%s", dwc3_decode_ctrl(__get_str(str), __entry->bRequestType,
+	TP_printk("%s", usb_decode_ctrl(__get_str(str), __entry->bRequestType,
 					__entry->bRequest, __entry->wValue,
 					__entry->wIndex, __entry->wLength)
 	)
diff --git a/include/linux/usb/ch9.h b/include/linux/usb/ch9.h
index 523aa088f6ab..a33cd2b9fbba 100644
--- a/include/linux/usb/ch9.h
+++ b/include/linux/usb/ch9.h
@@ -62,4 +62,23 @@ extern enum usb_device_speed usb_get_maximum_speed(struct device *dev);
  */
 extern const char *usb_state_string(enum usb_device_state state);
 
+/**
+ * usb_decode_ctrl - Returns human readable representation of control request.
+ * @str: buffer to return a human-readable representation of control request.
+ *       This buffer should have about 200 bytes.
+ * @bRequestType: matches the USB bmRequestType field
+ * @bRequest: matches the USB bRequest field
+ * @wValue: matches the USB wValue field (CPU byte order)
+ * @wIndex: matches the USB wIndex field (CPU byte order)
+ * @wLength: matches the USB wLength field (CPU byte order)
+ *
+ * Function returns decoded, formatted and human-readable description of
+ * control request packet.
+ *
+ * Important: wValue, wIndex, wLength parameters before invoking this function
+ * should be processed by le16_to_cpu macro.
+ */
+const char *usb_decode_ctrl(char *str, __u8 bRequestType, __u8 bRequest,
+			    __u16 wValue,  __u16 wIndex, __u16 wLength);
+
 #endif /* __LINUX_USB_CH9_H */
-- 
2.17.1

^ permalink raw reply related	[flat|nested] 49+ messages in thread

* [v2,2/5] usb:common Separated decoding functions from dwc3 driver.
@ 2018-12-23 15:13   ` Pawel Laszczak
  0 siblings, 0 replies; 49+ messages in thread
From: Pawel Laszczak @ 2018-12-23 15:13 UTC (permalink / raw)
  To: devicetree
  Cc: gregkh, mark.rutland, linux-usb, hdegoede, heikki.krogerus,
	andy.shevchenko, robh+dt, rogerq, linux-kernel, adouglas,
	jbergsagel, nsekhar, nm, sureshp, peter.chen, pjez, kurahul,
	Pawel Laszczak

Patch moves some decoding functions from driver/usb/dwc3/debug.h driver
to driver/usb/common/debug.c file. These moved functions include:
    dwc3_decode_get_status
    dwc3_decode_set_clear_feature
    dwc3_decode_set_address
    dwc3_decode_get_set_descriptor
    dwc3_decode_get_configuration
    dwc3_decode_set_configuration
    dwc3_decode_get_intf
    dwc3_decode_set_intf
    dwc3_decode_synch_frame
    dwc3_decode_set_sel
    dwc3_decode_set_isoch_delay
    dwc3_decode_ctrl

These functions are used also in inroduced cdns3 driver.

All functions prefixes were changed from dwc3 to usb.
Also, function's parameters has been extended according to the name
of fields in standard SETUP packet.
Additionally, patch adds usb_decode_ctrl function to
include/linux/usb/ch9.h file.

mend

Signed-off-by: Pawel Laszczak <pawell@cadence.com>
---
 drivers/usb/common/Makefile |   2 +-
 drivers/usb/common/debug.c  | 265 ++++++++++++++++++++++++++++++++++++
 drivers/usb/dwc3/debug.h    | 243 ---------------------------------
 drivers/usb/dwc3/trace.h    |   2 +-
 include/linux/usb/ch9.h     |  19 +++
 5 files changed, 286 insertions(+), 245 deletions(-)
 create mode 100644 drivers/usb/common/debug.c

diff --git a/drivers/usb/common/Makefile b/drivers/usb/common/Makefile
index fb4d5ef4165c..3d3d2962ea4b 100644
--- a/drivers/usb/common/Makefile
+++ b/drivers/usb/common/Makefile
@@ -4,7 +4,7 @@
 #
 
 obj-$(CONFIG_USB_COMMON)	  += usb-common.o
-usb-common-y			  += common.o
+usb-common-y			  += common.o debug.o
 usb-common-$(CONFIG_USB_LED_TRIG) += led.o
 
 obj-$(CONFIG_USB_OTG_FSM) += usb-otg-fsm.o
diff --git a/drivers/usb/common/debug.c b/drivers/usb/common/debug.c
new file mode 100644
index 000000000000..374fb9e94f37
--- /dev/null
+++ b/drivers/usb/common/debug.c
@@ -0,0 +1,265 @@
+// SPDX-License-Identifier: GPL-2.0
+/**
+ * Common USB debugging functions
+ *
+ * Copyright (C) 2010-2011 Texas Instruments Incorporated - http://www.ti.com
+ *
+ * Authors: Felipe Balbi <balbi@ti.com>,
+ *	    Sebastian Andrzej Siewior <bigeasy@linutronix.de>
+ */
+
+#ifndef __LINUX_USB_COMMON_DEBUG
+#define __LINUX_USB_COMMON_DEBUG
+
+#include <linux/usb/ch9.h>
+
+static void usb_decode_get_status(__u8 bRequestType, __u16 wIndex,
+				  __u16 wLength, char *str)
+{
+	switch (bRequestType & USB_RECIP_MASK) {
+	case USB_RECIP_INTERFACE:
+		sprintf(str, "Get Interface Status(Intf = %d, Length = %d)",
+			wIndex, wLength);
+		break;
+	case USB_RECIP_ENDPOINT:
+		sprintf(str, "Get Endpoint Status(ep%d%s)",
+			wIndex & ~USB_DIR_IN,
+			wIndex & USB_DIR_IN ? "in" : "out");
+		break;
+	}
+}
+
+static void usb_decode_set_clear_feature(__u8 bRequestType,
+					 __u8 bRequest, __u16 wValue,
+					 __u16 wIndex, char *str)
+{
+	switch (bRequestType & USB_RECIP_MASK) {
+	case USB_RECIP_DEVICE:
+		sprintf(str, "%s Device Feature(%s%s)",
+			bRequest == USB_REQ_CLEAR_FEATURE ? "Clear" : "Set",
+			({char *s;
+				switch (wValue) {
+				case USB_DEVICE_SELF_POWERED:
+					s = "Self Powered";
+					break;
+				case USB_DEVICE_REMOTE_WAKEUP:
+					s = "Remote Wakeup";
+					break;
+				case USB_DEVICE_TEST_MODE:
+					s = "Test Mode";
+					break;
+				case USB_DEVICE_U1_ENABLE:
+					s = "U1 Enable";
+					break;
+				case USB_DEVICE_U2_ENABLE:
+					s = "U2 Enable";
+					break;
+				case USB_DEVICE_LTM_ENABLE:
+					s = "LTM Enable";
+					break;
+				default:
+					s = "UNKNOWN";
+				} s; }),
+			wValue == USB_DEVICE_TEST_MODE ?
+			({ char *s;
+				switch (wIndex) {
+				case TEST_J:
+					s = ": TEST_J";
+					break;
+				case TEST_K:
+					s = ": TEST_K";
+					break;
+				case TEST_SE0_NAK:
+					s = ": TEST_SE0_NAK";
+					break;
+				case TEST_PACKET:
+					s = ": TEST_PACKET";
+					break;
+				case TEST_FORCE_EN:
+					s = ": TEST_FORCE_EN";
+					break;
+				default:
+					s = ": UNKNOWN";
+				} s; }) : "");
+		break;
+	case USB_RECIP_INTERFACE:
+		sprintf(str, "%s Interface Feature(%s)",
+			bRequest == USB_REQ_CLEAR_FEATURE ? "Clear" : "Set",
+			wValue == USB_INTRF_FUNC_SUSPEND ?
+			"Function Suspend" : "UNKNOWN");
+		break;
+	case USB_RECIP_ENDPOINT:
+		sprintf(str, "%s Endpoint Feature(%s ep%d%s)",
+			bRequest == USB_REQ_CLEAR_FEATURE ? "Clear" : "Set",
+			wValue == USB_ENDPOINT_HALT ? "Halt" : "UNKNOWN",
+			wIndex & ~USB_DIR_IN,
+			wIndex & USB_DIR_IN ? "in" : "out");
+		break;
+	}
+}
+
+static void usb_decode_set_address(__u16 wValue, char *str)
+{
+	sprintf(str, "Set Address(Addr = %02x)", wValue);
+}
+
+static void usb_decode_get_set_descriptor(__u8 bRequestType, __u8 bRequest,
+					  __u16 wValue, __u16 wIndex,
+					  __u16 wLength, char *str)
+{
+	sprintf(str, "%s %s Descriptor(Index = %d, Length = %d)",
+		bRequest == USB_REQ_GET_DESCRIPTOR ? "Get" : "Set",
+		({ char *s;
+			switch (wValue >> 8) {
+			case USB_DT_DEVICE:
+				s = "Device";
+				break;
+			case USB_DT_CONFIG:
+				s = "Configuration";
+				break;
+			case USB_DT_STRING:
+				s = "String";
+				break;
+			case USB_DT_INTERFACE:
+				s = "Interface";
+				break;
+			case USB_DT_ENDPOINT:
+				s = "Endpoint";
+				break;
+			case USB_DT_DEVICE_QUALIFIER:
+				s = "Device Qualifier";
+				break;
+			case USB_DT_OTHER_SPEED_CONFIG:
+				s = "Other Speed Config";
+				break;
+			case USB_DT_INTERFACE_POWER:
+				s = "Interface Power";
+				break;
+			case USB_DT_OTG:
+				s = "OTG";
+				break;
+			case USB_DT_DEBUG:
+				s = "Debug";
+				break;
+			case USB_DT_INTERFACE_ASSOCIATION:
+				s = "Interface Association";
+				break;
+			case USB_DT_BOS:
+				s = "BOS";
+				break;
+			case USB_DT_DEVICE_CAPABILITY:
+				s = "Device Capability";
+				break;
+			case USB_DT_PIPE_USAGE:
+				s = "Pipe Usage";
+				break;
+			case USB_DT_SS_ENDPOINT_COMP:
+				s = "SS Endpoint Companion";
+				break;
+			case USB_DT_SSP_ISOC_ENDPOINT_COMP:
+				s = "SSP Isochronous Endpoint Companion";
+				break;
+			default:
+				s = "UNKNOWN";
+				break;
+			} s; }), wValue & 0xff, wLength);
+}
+
+static void usb_decode_get_configuration(__u16 wLength, char *str)
+{
+	sprintf(str, "Get Configuration(Length = %d)", wLength);
+}
+
+static inline void usb_decode_set_configuration(__u8 wValue, char *str)
+{
+	sprintf(str, "Set Configuration(Config = %d)", wValue);
+}
+
+static void usb_decode_get_intf(__u16 wIndex, __u16 wLength, char *str)
+{
+	sprintf(str, "Get Interface(Intf = %d, Length = %d)", wIndex, wLength);
+}
+
+static void usb_decode_set_intf(__u8 wValue, __u16 wIndex, char *str)
+{
+	sprintf(str, "Set Interface(Intf = %d, Alt.Setting = %d)",
+		wIndex, wValue);
+}
+
+static void usb_decode_synch_frame(__u16 wIndex, __u16 wLength,
+				   char *str)
+{
+	sprintf(str, "Synch Frame(Endpoint = %d, Length = %d)",
+		wIndex, wLength);
+}
+
+static void usb_decode_set_sel(__u16 wLength, char *str)
+{
+	sprintf(str, "Set SEL(Length = %d)", wLength);
+}
+
+static void usb_decode_set_isoch_delay(__u8 wValue, char *str)
+{
+	sprintf(str, "Set Isochronous Delay(Delay = %d ns)", wValue);
+}
+
+/**
+ * usb_decode_ctrl - returns a string representation of ctrl request
+ */
+const char *usb_decode_ctrl(char *str, __u8 bRequestType, __u8 bRequest,
+			    __u16 wValue,  __u16 wIndex, __u16 wLength)
+{
+	switch (bRequest) {
+	case USB_REQ_GET_STATUS:
+		usb_decode_get_status(bRequestType, wIndex, wLength, str);
+		break;
+	case USB_REQ_CLEAR_FEATURE:
+	case USB_REQ_SET_FEATURE:
+		usb_decode_set_clear_feature(bRequestType, bRequest, wValue,
+					     wIndex, str);
+		break;
+	case USB_REQ_SET_ADDRESS:
+		usb_decode_set_address(wValue, str);
+		break;
+	case USB_REQ_GET_DESCRIPTOR:
+	case USB_REQ_SET_DESCRIPTOR:
+		usb_decode_get_set_descriptor(bRequestType, bRequest, wValue,
+					      wIndex, wLength, str);
+		break;
+	case USB_REQ_GET_CONFIGURATION:
+		usb_decode_get_configuration(wLength, str);
+		break;
+	case USB_REQ_SET_CONFIGURATION:
+		usb_decode_set_configuration(wValue, str);
+		break;
+	case USB_REQ_GET_INTERFACE:
+		usb_decode_get_intf(wIndex, wLength, str);
+		break;
+	case USB_REQ_SET_INTERFACE:
+		usb_decode_set_intf(wValue, wIndex, str);
+		break;
+	case USB_REQ_SYNCH_FRAME:
+		usb_decode_synch_frame(wIndex, wLength, str);
+		break;
+	case USB_REQ_SET_SEL:
+		usb_decode_set_sel(wLength, str);
+		break;
+	case USB_REQ_SET_ISOCH_DELAY:
+		usb_decode_set_isoch_delay(wValue, str);
+		break;
+	default:
+		sprintf(str, "%02x %02x %02x %02x %02x %02x %02x %02x",
+			bRequestType, bRequest,
+			cpu_to_le16(wValue) & 0xff,
+			cpu_to_le16(wValue) >> 8,
+			cpu_to_le16(wIndex) & 0xff,
+			cpu_to_le16(wIndex) >> 8,
+			cpu_to_le16(wLength) & 0xff,
+			cpu_to_le16(wLength) >> 8);
+	}
+
+	return str;
+}
+EXPORT_SYMBOL_GPL(usb_decode_ctrl);
+
+#endif /* __LINUX_USB_COMMON_DEBUG */
diff --git a/drivers/usb/dwc3/debug.h b/drivers/usb/dwc3/debug.h
index c66d216dcc30..9db2b0fb8f22 100644
--- a/drivers/usb/dwc3/debug.h
+++ b/drivers/usb/dwc3/debug.h
@@ -214,249 +214,6 @@ dwc3_gadget_event_string(char *str, const struct dwc3_event_devt *event)
 	return str;
 }
 
-static inline void dwc3_decode_get_status(__u8 t, __u16 i, __u16 l, char *str)
-{
-	switch (t & USB_RECIP_MASK) {
-	case USB_RECIP_INTERFACE:
-		sprintf(str, "Get Interface Status(Intf = %d, Length = %d)",
-			i, l);
-		break;
-	case USB_RECIP_ENDPOINT:
-		sprintf(str, "Get Endpoint Status(ep%d%s)",
-			i & ~USB_DIR_IN,
-			i & USB_DIR_IN ? "in" : "out");
-		break;
-	}
-}
-
-static inline void dwc3_decode_set_clear_feature(__u8 t, __u8 b, __u16 v,
-						 __u16 i, char *str)
-{
-	switch (t & USB_RECIP_MASK) {
-	case USB_RECIP_DEVICE:
-		sprintf(str, "%s Device Feature(%s%s)",
-			b == USB_REQ_CLEAR_FEATURE ? "Clear" : "Set",
-			({char *s;
-				switch (v) {
-				case USB_DEVICE_SELF_POWERED:
-					s = "Self Powered";
-					break;
-				case USB_DEVICE_REMOTE_WAKEUP:
-					s = "Remote Wakeup";
-					break;
-				case USB_DEVICE_TEST_MODE:
-					s = "Test Mode";
-					break;
-				case USB_DEVICE_U1_ENABLE:
-					s = "U1 Enable";
-					break;
-				case USB_DEVICE_U2_ENABLE:
-					s = "U2 Enable";
-					break;
-				case USB_DEVICE_LTM_ENABLE:
-					s = "LTM Enable";
-					break;
-				default:
-					s = "UNKNOWN";
-				} s; }),
-			v == USB_DEVICE_TEST_MODE ?
-			({ char *s;
-				switch (i) {
-				case TEST_J:
-					s = ": TEST_J";
-					break;
-				case TEST_K:
-					s = ": TEST_K";
-					break;
-				case TEST_SE0_NAK:
-					s = ": TEST_SE0_NAK";
-					break;
-				case TEST_PACKET:
-					s = ": TEST_PACKET";
-					break;
-				case TEST_FORCE_EN:
-					s = ": TEST_FORCE_EN";
-					break;
-				default:
-					s = ": UNKNOWN";
-				} s; }) : "");
-		break;
-	case USB_RECIP_INTERFACE:
-		sprintf(str, "%s Interface Feature(%s)",
-			b == USB_REQ_CLEAR_FEATURE ? "Clear" : "Set",
-			v == USB_INTRF_FUNC_SUSPEND ?
-			"Function Suspend" : "UNKNOWN");
-		break;
-	case USB_RECIP_ENDPOINT:
-		sprintf(str, "%s Endpoint Feature(%s ep%d%s)",
-			b == USB_REQ_CLEAR_FEATURE ? "Clear" : "Set",
-			v == USB_ENDPOINT_HALT ? "Halt" : "UNKNOWN",
-			i & ~USB_DIR_IN,
-			i & USB_DIR_IN ? "in" : "out");
-		break;
-	}
-}
-
-static inline void dwc3_decode_set_address(__u16 v, char *str)
-{
-	sprintf(str, "Set Address(Addr = %02x)", v);
-}
-
-static inline void dwc3_decode_get_set_descriptor(__u8 t, __u8 b, __u16 v,
-						  __u16 i, __u16 l, char *str)
-{
-	sprintf(str, "%s %s Descriptor(Index = %d, Length = %d)",
-		b == USB_REQ_GET_DESCRIPTOR ? "Get" : "Set",
-		({ char *s;
-			switch (v >> 8) {
-			case USB_DT_DEVICE:
-				s = "Device";
-				break;
-			case USB_DT_CONFIG:
-				s = "Configuration";
-				break;
-			case USB_DT_STRING:
-				s = "String";
-				break;
-			case USB_DT_INTERFACE:
-				s = "Interface";
-				break;
-			case USB_DT_ENDPOINT:
-				s = "Endpoint";
-				break;
-			case USB_DT_DEVICE_QUALIFIER:
-				s = "Device Qualifier";
-				break;
-			case USB_DT_OTHER_SPEED_CONFIG:
-				s = "Other Speed Config";
-				break;
-			case USB_DT_INTERFACE_POWER:
-				s = "Interface Power";
-				break;
-			case USB_DT_OTG:
-				s = "OTG";
-				break;
-			case USB_DT_DEBUG:
-				s = "Debug";
-				break;
-			case USB_DT_INTERFACE_ASSOCIATION:
-				s = "Interface Association";
-				break;
-			case USB_DT_BOS:
-				s = "BOS";
-				break;
-			case USB_DT_DEVICE_CAPABILITY:
-				s = "Device Capability";
-				break;
-			case USB_DT_PIPE_USAGE:
-				s = "Pipe Usage";
-				break;
-			case USB_DT_SS_ENDPOINT_COMP:
-				s = "SS Endpoint Companion";
-				break;
-			case USB_DT_SSP_ISOC_ENDPOINT_COMP:
-				s = "SSP Isochronous Endpoint Companion";
-				break;
-			default:
-				s = "UNKNOWN";
-				break;
-			} s; }), v & 0xff, l);
-}
-
-
-static inline void dwc3_decode_get_configuration(__u16 l, char *str)
-{
-	sprintf(str, "Get Configuration(Length = %d)", l);
-}
-
-static inline void dwc3_decode_set_configuration(__u8 v, char *str)
-{
-	sprintf(str, "Set Configuration(Config = %d)", v);
-}
-
-static inline void dwc3_decode_get_intf(__u16 i, __u16 l, char *str)
-{
-	sprintf(str, "Get Interface(Intf = %d, Length = %d)", i, l);
-}
-
-static inline void dwc3_decode_set_intf(__u8 v, __u16 i, char *str)
-{
-	sprintf(str, "Set Interface(Intf = %d, Alt.Setting = %d)", i, v);
-}
-
-static inline void dwc3_decode_synch_frame(__u16 i, __u16 l, char *str)
-{
-	sprintf(str, "Synch Frame(Endpoint = %d, Length = %d)", i, l);
-}
-
-static inline void dwc3_decode_set_sel(__u16 l, char *str)
-{
-	sprintf(str, "Set SEL(Length = %d)", l);
-}
-
-static inline void dwc3_decode_set_isoch_delay(__u8 v, char *str)
-{
-	sprintf(str, "Set Isochronous Delay(Delay = %d ns)", v);
-}
-
-/**
- * dwc3_decode_ctrl - returns a string represetion of ctrl request
- */
-static inline const char *dwc3_decode_ctrl(char *str, __u8 bRequestType,
-		__u8 bRequest, __u16 wValue, __u16 wIndex, __u16 wLength)
-{
-	switch (bRequest) {
-	case USB_REQ_GET_STATUS:
-		dwc3_decode_get_status(bRequestType, wIndex, wLength, str);
-		break;
-	case USB_REQ_CLEAR_FEATURE:
-	case USB_REQ_SET_FEATURE:
-		dwc3_decode_set_clear_feature(bRequestType, bRequest, wValue,
-					      wIndex, str);
-		break;
-	case USB_REQ_SET_ADDRESS:
-		dwc3_decode_set_address(wValue, str);
-		break;
-	case USB_REQ_GET_DESCRIPTOR:
-	case USB_REQ_SET_DESCRIPTOR:
-		dwc3_decode_get_set_descriptor(bRequestType, bRequest, wValue,
-					       wIndex, wLength, str);
-		break;
-	case USB_REQ_GET_CONFIGURATION:
-		dwc3_decode_get_configuration(wLength, str);
-		break;
-	case USB_REQ_SET_CONFIGURATION:
-		dwc3_decode_set_configuration(wValue, str);
-		break;
-	case USB_REQ_GET_INTERFACE:
-		dwc3_decode_get_intf(wIndex, wLength, str);
-		break;
-	case USB_REQ_SET_INTERFACE:
-		dwc3_decode_set_intf(wValue, wIndex, str);
-		break;
-	case USB_REQ_SYNCH_FRAME:
-		dwc3_decode_synch_frame(wIndex, wLength, str);
-		break;
-	case USB_REQ_SET_SEL:
-		dwc3_decode_set_sel(wLength, str);
-		break;
-	case USB_REQ_SET_ISOCH_DELAY:
-		dwc3_decode_set_isoch_delay(wValue, str);
-		break;
-	default:
-		sprintf(str, "%02x %02x %02x %02x %02x %02x %02x %02x",
-			bRequestType, bRequest,
-			cpu_to_le16(wValue) & 0xff,
-			cpu_to_le16(wValue) >> 8,
-			cpu_to_le16(wIndex) & 0xff,
-			cpu_to_le16(wIndex) >> 8,
-			cpu_to_le16(wLength) & 0xff,
-			cpu_to_le16(wLength) >> 8);
-	}
-
-	return str;
-}
-
 /**
  * dwc3_ep_event_string - returns event name
  * @event: then event code
diff --git a/drivers/usb/dwc3/trace.h b/drivers/usb/dwc3/trace.h
index f22714cce070..6236f0657583 100644
--- a/drivers/usb/dwc3/trace.h
+++ b/drivers/usb/dwc3/trace.h
@@ -86,7 +86,7 @@ DECLARE_EVENT_CLASS(dwc3_log_ctrl,
 		__entry->wIndex = le16_to_cpu(ctrl->wIndex);
 		__entry->wLength = le16_to_cpu(ctrl->wLength);
 	),
-	TP_printk("%s", dwc3_decode_ctrl(__get_str(str), __entry->bRequestType,
+	TP_printk("%s", usb_decode_ctrl(__get_str(str), __entry->bRequestType,
 					__entry->bRequest, __entry->wValue,
 					__entry->wIndex, __entry->wLength)
 	)
diff --git a/include/linux/usb/ch9.h b/include/linux/usb/ch9.h
index 523aa088f6ab..a33cd2b9fbba 100644
--- a/include/linux/usb/ch9.h
+++ b/include/linux/usb/ch9.h
@@ -62,4 +62,23 @@ extern enum usb_device_speed usb_get_maximum_speed(struct device *dev);
  */
 extern const char *usb_state_string(enum usb_device_state state);
 
+/**
+ * usb_decode_ctrl - Returns human readable representation of control request.
+ * @str: buffer to return a human-readable representation of control request.
+ *       This buffer should have about 200 bytes.
+ * @bRequestType: matches the USB bmRequestType field
+ * @bRequest: matches the USB bRequest field
+ * @wValue: matches the USB wValue field (CPU byte order)
+ * @wIndex: matches the USB wIndex field (CPU byte order)
+ * @wLength: matches the USB wLength field (CPU byte order)
+ *
+ * Function returns decoded, formatted and human-readable description of
+ * control request packet.
+ *
+ * Important: wValue, wIndex, wLength parameters before invoking this function
+ * should be processed by le16_to_cpu macro.
+ */
+const char *usb_decode_ctrl(char *str, __u8 bRequestType, __u8 bRequest,
+			    __u16 wValue,  __u16 wIndex, __u16 wLength);
+
 #endif /* __LINUX_USB_CH9_H */

^ permalink raw reply related	[flat|nested] 49+ messages in thread

* [PATCH v2 3/5] usb:common Patch simplify usb_decode_set_clear_feature function.
  2018-12-23 15:13 ` Pawel Laszczak
  (?)
@ 2018-12-23 15:13   ` Pawel Laszczak
  -1 siblings, 0 replies; 49+ messages in thread
From: Pawel Laszczak @ 2018-12-23 15:13 UTC (permalink / raw)
  To: devicetree
  Cc: gregkh, mark.rutland, linux-usb, hdegoede, heikki.krogerus,
	andy.shevchenko, robh+dt, rogerq, linux-kernel, adouglas,
	jbergsagel, nsekhar, nm, sureshp, peter.chen, pjez, kurahul,
	Pawel Laszczak

Patch adds usb_decode_test_mode and usb_decode_device_feature functions,
which allow to make more readable and simplify the
usb_decode_set_clear_feature function.

Signed-off-by: Pawel Laszczak <pawell@cadence.com>
---
 drivers/usb/common/debug.c | 83 ++++++++++++++++++--------------------
 1 file changed, 40 insertions(+), 43 deletions(-)

diff --git a/drivers/usb/common/debug.c b/drivers/usb/common/debug.c
index 374fb9e94f37..77e565a38f29 100644
--- a/drivers/usb/common/debug.c
+++ b/drivers/usb/common/debug.c
@@ -29,6 +29,44 @@ static void usb_decode_get_status(__u8 bRequestType, __u16 wIndex,
 	}
 }
 
+static const char *usb_decode_device_feature(u16 wValue)
+{
+	switch (wValue) {
+	case USB_DEVICE_SELF_POWERED:
+		return "Self Powered";
+	case USB_DEVICE_REMOTE_WAKEUP:
+		return "Remote Wakeup";
+	case USB_DEVICE_TEST_MODE:
+		return "Test Mode";
+	case USB_DEVICE_U1_ENABLE:
+		return "U1 Enable";
+	case USB_DEVICE_U2_ENABLE:
+		return "U2 Enable";
+	case USB_DEVICE_LTM_ENABLE:
+		return "LTM Enable";
+	default:
+		return "UNKNOWN";
+	}
+}
+
+static const char *usb_decode_test_mode(u16 wIndex)
+{
+	switch (wIndex) {
+	case TEST_J:
+		return ": TEST_J";
+	case TEST_K:
+		return ": TEST_K";
+	case TEST_SE0_NAK:
+		return ": TEST_SE0_NAK";
+	case TEST_PACKET:
+		return ": TEST_PACKET";
+	case TEST_FORCE_EN:
+		return ": TEST_FORCE_EN";
+	default:
+		return ": UNKNOWN";
+	}
+}
+
 static void usb_decode_set_clear_feature(__u8 bRequestType,
 					 __u8 bRequest, __u16 wValue,
 					 __u16 wIndex, char *str)
@@ -37,50 +75,9 @@ static void usb_decode_set_clear_feature(__u8 bRequestType,
 	case USB_RECIP_DEVICE:
 		sprintf(str, "%s Device Feature(%s%s)",
 			bRequest == USB_REQ_CLEAR_FEATURE ? "Clear" : "Set",
-			({char *s;
-				switch (wValue) {
-				case USB_DEVICE_SELF_POWERED:
-					s = "Self Powered";
-					break;
-				case USB_DEVICE_REMOTE_WAKEUP:
-					s = "Remote Wakeup";
-					break;
-				case USB_DEVICE_TEST_MODE:
-					s = "Test Mode";
-					break;
-				case USB_DEVICE_U1_ENABLE:
-					s = "U1 Enable";
-					break;
-				case USB_DEVICE_U2_ENABLE:
-					s = "U2 Enable";
-					break;
-				case USB_DEVICE_LTM_ENABLE:
-					s = "LTM Enable";
-					break;
-				default:
-					s = "UNKNOWN";
-				} s; }),
+			usb_decode_device_feature(wValue),
 			wValue == USB_DEVICE_TEST_MODE ?
-			({ char *s;
-				switch (wIndex) {
-				case TEST_J:
-					s = ": TEST_J";
-					break;
-				case TEST_K:
-					s = ": TEST_K";
-					break;
-				case TEST_SE0_NAK:
-					s = ": TEST_SE0_NAK";
-					break;
-				case TEST_PACKET:
-					s = ": TEST_PACKET";
-					break;
-				case TEST_FORCE_EN:
-					s = ": TEST_FORCE_EN";
-					break;
-				default:
-					s = ": UNKNOWN";
-				} s; }) : "");
+			usb_decode_test_mode(wIndex) : "");
 		break;
 	case USB_RECIP_INTERFACE:
 		sprintf(str, "%s Interface Feature(%s)",
-- 
2.17.1


^ permalink raw reply related	[flat|nested] 49+ messages in thread

* [PATCH v2 3/5] usb:common Patch simplify usb_decode_set_clear_feature function.
@ 2018-12-23 15:13   ` Pawel Laszczak
  0 siblings, 0 replies; 49+ messages in thread
From: Pawel Laszczak @ 2018-12-23 15:13 UTC (permalink / raw)
  To: devicetree
  Cc: gregkh, mark.rutland, linux-usb, hdegoede, heikki.krogerus,
	andy.shevchenko, robh+dt, rogerq, linux-kernel, adouglas,
	jbergsagel, nsekhar, nm, sureshp, peter.chen, pjez, kurahul,
	Pawel Laszczak

Patch adds usb_decode_test_mode and usb_decode_device_feature functions,
which allow to make more readable and simplify the
usb_decode_set_clear_feature function.

Signed-off-by: Pawel Laszczak <pawell@cadence.com>
---
 drivers/usb/common/debug.c | 83 ++++++++++++++++++--------------------
 1 file changed, 40 insertions(+), 43 deletions(-)

diff --git a/drivers/usb/common/debug.c b/drivers/usb/common/debug.c
index 374fb9e94f37..77e565a38f29 100644
--- a/drivers/usb/common/debug.c
+++ b/drivers/usb/common/debug.c
@@ -29,6 +29,44 @@ static void usb_decode_get_status(__u8 bRequestType, __u16 wIndex,
 	}
 }
 
+static const char *usb_decode_device_feature(u16 wValue)
+{
+	switch (wValue) {
+	case USB_DEVICE_SELF_POWERED:
+		return "Self Powered";
+	case USB_DEVICE_REMOTE_WAKEUP:
+		return "Remote Wakeup";
+	case USB_DEVICE_TEST_MODE:
+		return "Test Mode";
+	case USB_DEVICE_U1_ENABLE:
+		return "U1 Enable";
+	case USB_DEVICE_U2_ENABLE:
+		return "U2 Enable";
+	case USB_DEVICE_LTM_ENABLE:
+		return "LTM Enable";
+	default:
+		return "UNKNOWN";
+	}
+}
+
+static const char *usb_decode_test_mode(u16 wIndex)
+{
+	switch (wIndex) {
+	case TEST_J:
+		return ": TEST_J";
+	case TEST_K:
+		return ": TEST_K";
+	case TEST_SE0_NAK:
+		return ": TEST_SE0_NAK";
+	case TEST_PACKET:
+		return ": TEST_PACKET";
+	case TEST_FORCE_EN:
+		return ": TEST_FORCE_EN";
+	default:
+		return ": UNKNOWN";
+	}
+}
+
 static void usb_decode_set_clear_feature(__u8 bRequestType,
 					 __u8 bRequest, __u16 wValue,
 					 __u16 wIndex, char *str)
@@ -37,50 +75,9 @@ static void usb_decode_set_clear_feature(__u8 bRequestType,
 	case USB_RECIP_DEVICE:
 		sprintf(str, "%s Device Feature(%s%s)",
 			bRequest == USB_REQ_CLEAR_FEATURE ? "Clear" : "Set",
-			({char *s;
-				switch (wValue) {
-				case USB_DEVICE_SELF_POWERED:
-					s = "Self Powered";
-					break;
-				case USB_DEVICE_REMOTE_WAKEUP:
-					s = "Remote Wakeup";
-					break;
-				case USB_DEVICE_TEST_MODE:
-					s = "Test Mode";
-					break;
-				case USB_DEVICE_U1_ENABLE:
-					s = "U1 Enable";
-					break;
-				case USB_DEVICE_U2_ENABLE:
-					s = "U2 Enable";
-					break;
-				case USB_DEVICE_LTM_ENABLE:
-					s = "LTM Enable";
-					break;
-				default:
-					s = "UNKNOWN";
-				} s; }),
+			usb_decode_device_feature(wValue),
 			wValue == USB_DEVICE_TEST_MODE ?
-			({ char *s;
-				switch (wIndex) {
-				case TEST_J:
-					s = ": TEST_J";
-					break;
-				case TEST_K:
-					s = ": TEST_K";
-					break;
-				case TEST_SE0_NAK:
-					s = ": TEST_SE0_NAK";
-					break;
-				case TEST_PACKET:
-					s = ": TEST_PACKET";
-					break;
-				case TEST_FORCE_EN:
-					s = ": TEST_FORCE_EN";
-					break;
-				default:
-					s = ": UNKNOWN";
-				} s; }) : "");
+			usb_decode_test_mode(wIndex) : "");
 		break;
 	case USB_RECIP_INTERFACE:
 		sprintf(str, "%s Interface Feature(%s)",
-- 
2.17.1

^ permalink raw reply related	[flat|nested] 49+ messages in thread

* [v2,3/5] usb:common Patch simplify usb_decode_set_clear_feature function.
@ 2018-12-23 15:13   ` Pawel Laszczak
  0 siblings, 0 replies; 49+ messages in thread
From: Pawel Laszczak @ 2018-12-23 15:13 UTC (permalink / raw)
  To: devicetree
  Cc: gregkh, mark.rutland, linux-usb, hdegoede, heikki.krogerus,
	andy.shevchenko, robh+dt, rogerq, linux-kernel, adouglas,
	jbergsagel, nsekhar, nm, sureshp, peter.chen, pjez, kurahul,
	Pawel Laszczak

Patch adds usb_decode_test_mode and usb_decode_device_feature functions,
which allow to make more readable and simplify the
usb_decode_set_clear_feature function.

Signed-off-by: Pawel Laszczak <pawell@cadence.com>
---
 drivers/usb/common/debug.c | 83 ++++++++++++++++++--------------------
 1 file changed, 40 insertions(+), 43 deletions(-)

diff --git a/drivers/usb/common/debug.c b/drivers/usb/common/debug.c
index 374fb9e94f37..77e565a38f29 100644
--- a/drivers/usb/common/debug.c
+++ b/drivers/usb/common/debug.c
@@ -29,6 +29,44 @@ static void usb_decode_get_status(__u8 bRequestType, __u16 wIndex,
 	}
 }
 
+static const char *usb_decode_device_feature(u16 wValue)
+{
+	switch (wValue) {
+	case USB_DEVICE_SELF_POWERED:
+		return "Self Powered";
+	case USB_DEVICE_REMOTE_WAKEUP:
+		return "Remote Wakeup";
+	case USB_DEVICE_TEST_MODE:
+		return "Test Mode";
+	case USB_DEVICE_U1_ENABLE:
+		return "U1 Enable";
+	case USB_DEVICE_U2_ENABLE:
+		return "U2 Enable";
+	case USB_DEVICE_LTM_ENABLE:
+		return "LTM Enable";
+	default:
+		return "UNKNOWN";
+	}
+}
+
+static const char *usb_decode_test_mode(u16 wIndex)
+{
+	switch (wIndex) {
+	case TEST_J:
+		return ": TEST_J";
+	case TEST_K:
+		return ": TEST_K";
+	case TEST_SE0_NAK:
+		return ": TEST_SE0_NAK";
+	case TEST_PACKET:
+		return ": TEST_PACKET";
+	case TEST_FORCE_EN:
+		return ": TEST_FORCE_EN";
+	default:
+		return ": UNKNOWN";
+	}
+}
+
 static void usb_decode_set_clear_feature(__u8 bRequestType,
 					 __u8 bRequest, __u16 wValue,
 					 __u16 wIndex, char *str)
@@ -37,50 +75,9 @@ static void usb_decode_set_clear_feature(__u8 bRequestType,
 	case USB_RECIP_DEVICE:
 		sprintf(str, "%s Device Feature(%s%s)",
 			bRequest == USB_REQ_CLEAR_FEATURE ? "Clear" : "Set",
-			({char *s;
-				switch (wValue) {
-				case USB_DEVICE_SELF_POWERED:
-					s = "Self Powered";
-					break;
-				case USB_DEVICE_REMOTE_WAKEUP:
-					s = "Remote Wakeup";
-					break;
-				case USB_DEVICE_TEST_MODE:
-					s = "Test Mode";
-					break;
-				case USB_DEVICE_U1_ENABLE:
-					s = "U1 Enable";
-					break;
-				case USB_DEVICE_U2_ENABLE:
-					s = "U2 Enable";
-					break;
-				case USB_DEVICE_LTM_ENABLE:
-					s = "LTM Enable";
-					break;
-				default:
-					s = "UNKNOWN";
-				} s; }),
+			usb_decode_device_feature(wValue),
 			wValue == USB_DEVICE_TEST_MODE ?
-			({ char *s;
-				switch (wIndex) {
-				case TEST_J:
-					s = ": TEST_J";
-					break;
-				case TEST_K:
-					s = ": TEST_K";
-					break;
-				case TEST_SE0_NAK:
-					s = ": TEST_SE0_NAK";
-					break;
-				case TEST_PACKET:
-					s = ": TEST_PACKET";
-					break;
-				case TEST_FORCE_EN:
-					s = ": TEST_FORCE_EN";
-					break;
-				default:
-					s = ": UNKNOWN";
-				} s; }) : "");
+			usb_decode_test_mode(wIndex) : "");
 		break;
 	case USB_RECIP_INTERFACE:
 		sprintf(str, "%s Interface Feature(%s)",

^ permalink raw reply related	[flat|nested] 49+ messages in thread

* [PATCH v2 4/5] usb:common Simplify usb_decode_get_set_descriptor function.
  2018-12-23 15:13 ` Pawel Laszczak
  (?)
@ 2018-12-23 15:13   ` Pawel Laszczak
  -1 siblings, 0 replies; 49+ messages in thread
From: Pawel Laszczak @ 2018-12-23 15:13 UTC (permalink / raw)
  To: devicetree
  Cc: gregkh, mark.rutland, linux-usb, hdegoede, heikki.krogerus,
	andy.shevchenko, robh+dt, rogerq, linux-kernel, adouglas,
	jbergsagel, nsekhar, nm, sureshp, peter.chen, pjez, kurahul,
	Pawel Laszczak

Patch moves switch responsible for decoding descriptor type
outside snprintf. It's little improves code readability.

Signed-off-by: Pawel Laszczak <pawell@cadence.com>
---
 drivers/usb/common/debug.c | 111 +++++++++++++++++++------------------
 1 file changed, 57 insertions(+), 54 deletions(-)

diff --git a/drivers/usb/common/debug.c b/drivers/usb/common/debug.c
index 77e565a38f29..c15fa066339c 100644
--- a/drivers/usb/common/debug.c
+++ b/drivers/usb/common/debug.c
@@ -104,62 +104,65 @@ static void usb_decode_get_set_descriptor(__u8 bRequestType, __u8 bRequest,
 					  __u16 wValue, __u16 wIndex,
 					  __u16 wLength, char *str)
 {
+	char *s;
+
+	switch (wValue >> 8) {
+	case USB_DT_DEVICE:
+		s = "Device";
+		break;
+	case USB_DT_CONFIG:
+		s = "Configuration";
+		break;
+	case USB_DT_STRING:
+		s = "String";
+		break;
+	case USB_DT_INTERFACE:
+		s = "Interface";
+		break;
+	case USB_DT_ENDPOINT:
+		s = "Endpoint";
+		break;
+	case USB_DT_DEVICE_QUALIFIER:
+		s = "Device Qualifier";
+		break;
+	case USB_DT_OTHER_SPEED_CONFIG:
+		s = "Other Speed Config";
+		break;
+	case USB_DT_INTERFACE_POWER:
+		s = "Interface Power";
+		break;
+	case USB_DT_OTG:
+		s = "OTG";
+		break;
+	case USB_DT_DEBUG:
+		s = "Debug";
+		break;
+	case USB_DT_INTERFACE_ASSOCIATION:
+		s = "Interface Association";
+		break;
+	case USB_DT_BOS:
+		s = "BOS";
+		break;
+	case USB_DT_DEVICE_CAPABILITY:
+		s = "Device Capability";
+		break;
+	case USB_DT_PIPE_USAGE:
+		s = "Pipe Usage";
+		break;
+	case USB_DT_SS_ENDPOINT_COMP:
+		s = "SS Endpoint Companion";
+		break;
+	case USB_DT_SSP_ISOC_ENDPOINT_COMP:
+		s = "SSP Isochronous Endpoint Companion";
+		break;
+	default:
+		s = "UNKNOWN";
+		break;
+	}
+
 	sprintf(str, "%s %s Descriptor(Index = %d, Length = %d)",
 		bRequest == USB_REQ_GET_DESCRIPTOR ? "Get" : "Set",
-		({ char *s;
-			switch (wValue >> 8) {
-			case USB_DT_DEVICE:
-				s = "Device";
-				break;
-			case USB_DT_CONFIG:
-				s = "Configuration";
-				break;
-			case USB_DT_STRING:
-				s = "String";
-				break;
-			case USB_DT_INTERFACE:
-				s = "Interface";
-				break;
-			case USB_DT_ENDPOINT:
-				s = "Endpoint";
-				break;
-			case USB_DT_DEVICE_QUALIFIER:
-				s = "Device Qualifier";
-				break;
-			case USB_DT_OTHER_SPEED_CONFIG:
-				s = "Other Speed Config";
-				break;
-			case USB_DT_INTERFACE_POWER:
-				s = "Interface Power";
-				break;
-			case USB_DT_OTG:
-				s = "OTG";
-				break;
-			case USB_DT_DEBUG:
-				s = "Debug";
-				break;
-			case USB_DT_INTERFACE_ASSOCIATION:
-				s = "Interface Association";
-				break;
-			case USB_DT_BOS:
-				s = "BOS";
-				break;
-			case USB_DT_DEVICE_CAPABILITY:
-				s = "Device Capability";
-				break;
-			case USB_DT_PIPE_USAGE:
-				s = "Pipe Usage";
-				break;
-			case USB_DT_SS_ENDPOINT_COMP:
-				s = "SS Endpoint Companion";
-				break;
-			case USB_DT_SSP_ISOC_ENDPOINT_COMP:
-				s = "SSP Isochronous Endpoint Companion";
-				break;
-			default:
-				s = "UNKNOWN";
-				break;
-			} s; }), wValue & 0xff, wLength);
+		s, wValue & 0xff, wLength);
 }
 
 static void usb_decode_get_configuration(__u16 wLength, char *str)
-- 
2.17.1


^ permalink raw reply related	[flat|nested] 49+ messages in thread

* [PATCH v2 4/5] usb:common Simplify usb_decode_get_set_descriptor function.
@ 2018-12-23 15:13   ` Pawel Laszczak
  0 siblings, 0 replies; 49+ messages in thread
From: Pawel Laszczak @ 2018-12-23 15:13 UTC (permalink / raw)
  To: devicetree
  Cc: gregkh, mark.rutland, linux-usb, hdegoede, heikki.krogerus,
	andy.shevchenko, robh+dt, rogerq, linux-kernel, adouglas,
	jbergsagel, nsekhar, nm, sureshp, peter.chen, pjez, kurahul,
	Pawel Laszczak

Patch moves switch responsible for decoding descriptor type
outside snprintf. It's little improves code readability.

Signed-off-by: Pawel Laszczak <pawell@cadence.com>
---
 drivers/usb/common/debug.c | 111 +++++++++++++++++++------------------
 1 file changed, 57 insertions(+), 54 deletions(-)

diff --git a/drivers/usb/common/debug.c b/drivers/usb/common/debug.c
index 77e565a38f29..c15fa066339c 100644
--- a/drivers/usb/common/debug.c
+++ b/drivers/usb/common/debug.c
@@ -104,62 +104,65 @@ static void usb_decode_get_set_descriptor(__u8 bRequestType, __u8 bRequest,
 					  __u16 wValue, __u16 wIndex,
 					  __u16 wLength, char *str)
 {
+	char *s;
+
+	switch (wValue >> 8) {
+	case USB_DT_DEVICE:
+		s = "Device";
+		break;
+	case USB_DT_CONFIG:
+		s = "Configuration";
+		break;
+	case USB_DT_STRING:
+		s = "String";
+		break;
+	case USB_DT_INTERFACE:
+		s = "Interface";
+		break;
+	case USB_DT_ENDPOINT:
+		s = "Endpoint";
+		break;
+	case USB_DT_DEVICE_QUALIFIER:
+		s = "Device Qualifier";
+		break;
+	case USB_DT_OTHER_SPEED_CONFIG:
+		s = "Other Speed Config";
+		break;
+	case USB_DT_INTERFACE_POWER:
+		s = "Interface Power";
+		break;
+	case USB_DT_OTG:
+		s = "OTG";
+		break;
+	case USB_DT_DEBUG:
+		s = "Debug";
+		break;
+	case USB_DT_INTERFACE_ASSOCIATION:
+		s = "Interface Association";
+		break;
+	case USB_DT_BOS:
+		s = "BOS";
+		break;
+	case USB_DT_DEVICE_CAPABILITY:
+		s = "Device Capability";
+		break;
+	case USB_DT_PIPE_USAGE:
+		s = "Pipe Usage";
+		break;
+	case USB_DT_SS_ENDPOINT_COMP:
+		s = "SS Endpoint Companion";
+		break;
+	case USB_DT_SSP_ISOC_ENDPOINT_COMP:
+		s = "SSP Isochronous Endpoint Companion";
+		break;
+	default:
+		s = "UNKNOWN";
+		break;
+	}
+
 	sprintf(str, "%s %s Descriptor(Index = %d, Length = %d)",
 		bRequest == USB_REQ_GET_DESCRIPTOR ? "Get" : "Set",
-		({ char *s;
-			switch (wValue >> 8) {
-			case USB_DT_DEVICE:
-				s = "Device";
-				break;
-			case USB_DT_CONFIG:
-				s = "Configuration";
-				break;
-			case USB_DT_STRING:
-				s = "String";
-				break;
-			case USB_DT_INTERFACE:
-				s = "Interface";
-				break;
-			case USB_DT_ENDPOINT:
-				s = "Endpoint";
-				break;
-			case USB_DT_DEVICE_QUALIFIER:
-				s = "Device Qualifier";
-				break;
-			case USB_DT_OTHER_SPEED_CONFIG:
-				s = "Other Speed Config";
-				break;
-			case USB_DT_INTERFACE_POWER:
-				s = "Interface Power";
-				break;
-			case USB_DT_OTG:
-				s = "OTG";
-				break;
-			case USB_DT_DEBUG:
-				s = "Debug";
-				break;
-			case USB_DT_INTERFACE_ASSOCIATION:
-				s = "Interface Association";
-				break;
-			case USB_DT_BOS:
-				s = "BOS";
-				break;
-			case USB_DT_DEVICE_CAPABILITY:
-				s = "Device Capability";
-				break;
-			case USB_DT_PIPE_USAGE:
-				s = "Pipe Usage";
-				break;
-			case USB_DT_SS_ENDPOINT_COMP:
-				s = "SS Endpoint Companion";
-				break;
-			case USB_DT_SSP_ISOC_ENDPOINT_COMP:
-				s = "SSP Isochronous Endpoint Companion";
-				break;
-			default:
-				s = "UNKNOWN";
-				break;
-			} s; }), wValue & 0xff, wLength);
+		s, wValue & 0xff, wLength);
 }
 
 static void usb_decode_get_configuration(__u16 wLength, char *str)
-- 
2.17.1

^ permalink raw reply related	[flat|nested] 49+ messages in thread

* [v2,4/5] usb:common Simplify usb_decode_get_set_descriptor function.
@ 2018-12-23 15:13   ` Pawel Laszczak
  0 siblings, 0 replies; 49+ messages in thread
From: Pawel Laszczak @ 2018-12-23 15:13 UTC (permalink / raw)
  To: devicetree
  Cc: gregkh, mark.rutland, linux-usb, hdegoede, heikki.krogerus,
	andy.shevchenko, robh+dt, rogerq, linux-kernel, adouglas,
	jbergsagel, nsekhar, nm, sureshp, peter.chen, pjez, kurahul,
	Pawel Laszczak

Patch moves switch responsible for decoding descriptor type
outside snprintf. It's little improves code readability.

Signed-off-by: Pawel Laszczak <pawell@cadence.com>
---
 drivers/usb/common/debug.c | 111 +++++++++++++++++++------------------
 1 file changed, 57 insertions(+), 54 deletions(-)

diff --git a/drivers/usb/common/debug.c b/drivers/usb/common/debug.c
index 77e565a38f29..c15fa066339c 100644
--- a/drivers/usb/common/debug.c
+++ b/drivers/usb/common/debug.c
@@ -104,62 +104,65 @@ static void usb_decode_get_set_descriptor(__u8 bRequestType, __u8 bRequest,
 					  __u16 wValue, __u16 wIndex,
 					  __u16 wLength, char *str)
 {
+	char *s;
+
+	switch (wValue >> 8) {
+	case USB_DT_DEVICE:
+		s = "Device";
+		break;
+	case USB_DT_CONFIG:
+		s = "Configuration";
+		break;
+	case USB_DT_STRING:
+		s = "String";
+		break;
+	case USB_DT_INTERFACE:
+		s = "Interface";
+		break;
+	case USB_DT_ENDPOINT:
+		s = "Endpoint";
+		break;
+	case USB_DT_DEVICE_QUALIFIER:
+		s = "Device Qualifier";
+		break;
+	case USB_DT_OTHER_SPEED_CONFIG:
+		s = "Other Speed Config";
+		break;
+	case USB_DT_INTERFACE_POWER:
+		s = "Interface Power";
+		break;
+	case USB_DT_OTG:
+		s = "OTG";
+		break;
+	case USB_DT_DEBUG:
+		s = "Debug";
+		break;
+	case USB_DT_INTERFACE_ASSOCIATION:
+		s = "Interface Association";
+		break;
+	case USB_DT_BOS:
+		s = "BOS";
+		break;
+	case USB_DT_DEVICE_CAPABILITY:
+		s = "Device Capability";
+		break;
+	case USB_DT_PIPE_USAGE:
+		s = "Pipe Usage";
+		break;
+	case USB_DT_SS_ENDPOINT_COMP:
+		s = "SS Endpoint Companion";
+		break;
+	case USB_DT_SSP_ISOC_ENDPOINT_COMP:
+		s = "SSP Isochronous Endpoint Companion";
+		break;
+	default:
+		s = "UNKNOWN";
+		break;
+	}
+
 	sprintf(str, "%s %s Descriptor(Index = %d, Length = %d)",
 		bRequest == USB_REQ_GET_DESCRIPTOR ? "Get" : "Set",
-		({ char *s;
-			switch (wValue >> 8) {
-			case USB_DT_DEVICE:
-				s = "Device";
-				break;
-			case USB_DT_CONFIG:
-				s = "Configuration";
-				break;
-			case USB_DT_STRING:
-				s = "String";
-				break;
-			case USB_DT_INTERFACE:
-				s = "Interface";
-				break;
-			case USB_DT_ENDPOINT:
-				s = "Endpoint";
-				break;
-			case USB_DT_DEVICE_QUALIFIER:
-				s = "Device Qualifier";
-				break;
-			case USB_DT_OTHER_SPEED_CONFIG:
-				s = "Other Speed Config";
-				break;
-			case USB_DT_INTERFACE_POWER:
-				s = "Interface Power";
-				break;
-			case USB_DT_OTG:
-				s = "OTG";
-				break;
-			case USB_DT_DEBUG:
-				s = "Debug";
-				break;
-			case USB_DT_INTERFACE_ASSOCIATION:
-				s = "Interface Association";
-				break;
-			case USB_DT_BOS:
-				s = "BOS";
-				break;
-			case USB_DT_DEVICE_CAPABILITY:
-				s = "Device Capability";
-				break;
-			case USB_DT_PIPE_USAGE:
-				s = "Pipe Usage";
-				break;
-			case USB_DT_SS_ENDPOINT_COMP:
-				s = "SS Endpoint Companion";
-				break;
-			case USB_DT_SSP_ISOC_ENDPOINT_COMP:
-				s = "SSP Isochronous Endpoint Companion";
-				break;
-			default:
-				s = "UNKNOWN";
-				break;
-			} s; }), wValue & 0xff, wLength);
+		s, wValue & 0xff, wLength);
 }
 
 static void usb_decode_get_configuration(__u16 wLength, char *str)

^ permalink raw reply related	[flat|nested] 49+ messages in thread

* [PATCH v2 5/5] usb:cdns3 Add Cadence USB3 DRD Driver
  2018-12-23 15:13 ` Pawel Laszczak
  (?)
@ 2018-12-23 15:13   ` Pawel Laszczak
  -1 siblings, 0 replies; 49+ messages in thread
From: Pawel Laszczak @ 2018-12-23 15:13 UTC (permalink / raw)
  To: devicetree
  Cc: gregkh, mark.rutland, linux-usb, hdegoede, heikki.krogerus,
	andy.shevchenko, robh+dt, rogerq, linux-kernel, adouglas,
	jbergsagel, nsekhar, nm, sureshp, peter.chen, pjez, kurahul,
	Pawel Laszczak

This patch introduce new Cadence USBSS DRD driver
to linux kernel.

The Cadence USBSS DRD Driver is a highly
configurable IP Core which can be
instantiated as Dual-Role Device (DRD),
Peripheral Only and Host Only (XHCI)
configurations.

The current driver has been validated with
FPGA burned. We have support for PCIe
bus, which is used on FPGA prototyping.

The host side of USBSS-DRD controller is compliance
with XHCI specification, so it works with
standard XHCI linux driver.

Signed-off-by: Pawel Laszczak <pawell@cadence.com>
---
 drivers/usb/Kconfig                |    2 +
 drivers/usb/Makefile               |    2 +
 drivers/usb/cdns3/Kconfig          |   44 +
 drivers/usb/cdns3/Makefile         |   16 +
 drivers/usb/cdns3/cdns3-pci-wrap.c |  157 +++
 drivers/usb/cdns3/core.c           |  406 ++++++
 drivers/usb/cdns3/core.h           |  116 ++
 drivers/usb/cdns3/debug.h          |  166 +++
 drivers/usb/cdns3/debugfs.c        |  168 +++
 drivers/usb/cdns3/drd.c            |  350 +++++
 drivers/usb/cdns3/drd.h            |  162 +++
 drivers/usb/cdns3/ep0.c            |  896 ++++++++++++
 drivers/usb/cdns3/gadget-export.h  |   28 +
 drivers/usb/cdns3/gadget.c         | 2102 ++++++++++++++++++++++++++++
 drivers/usb/cdns3/gadget.h         | 1206 ++++++++++++++++
 drivers/usb/cdns3/host-export.h    |   28 +
 drivers/usb/cdns3/host.c           |   72 +
 drivers/usb/cdns3/trace.c          |   11 +
 drivers/usb/cdns3/trace.h          |  389 +++++
 19 files changed, 6321 insertions(+)
 create mode 100644 drivers/usb/cdns3/Kconfig
 create mode 100644 drivers/usb/cdns3/Makefile
 create mode 100644 drivers/usb/cdns3/cdns3-pci-wrap.c
 create mode 100644 drivers/usb/cdns3/core.c
 create mode 100644 drivers/usb/cdns3/core.h
 create mode 100644 drivers/usb/cdns3/debug.h
 create mode 100644 drivers/usb/cdns3/debugfs.c
 create mode 100644 drivers/usb/cdns3/drd.c
 create mode 100644 drivers/usb/cdns3/drd.h
 create mode 100644 drivers/usb/cdns3/ep0.c
 create mode 100644 drivers/usb/cdns3/gadget-export.h
 create mode 100644 drivers/usb/cdns3/gadget.c
 create mode 100644 drivers/usb/cdns3/gadget.h
 create mode 100644 drivers/usb/cdns3/host-export.h
 create mode 100644 drivers/usb/cdns3/host.c
 create mode 100644 drivers/usb/cdns3/trace.c
 create mode 100644 drivers/usb/cdns3/trace.h

diff --git a/drivers/usb/Kconfig b/drivers/usb/Kconfig
index 987fc5ba6321..5f9334019d04 100644
--- a/drivers/usb/Kconfig
+++ b/drivers/usb/Kconfig
@@ -112,6 +112,8 @@ source "drivers/usb/usbip/Kconfig"
 
 endif
 
+source "drivers/usb/cdns3/Kconfig"
+
 source "drivers/usb/mtu3/Kconfig"
 
 source "drivers/usb/musb/Kconfig"
diff --git a/drivers/usb/Makefile b/drivers/usb/Makefile
index 7d1b8c82b208..ab125b966cac 100644
--- a/drivers/usb/Makefile
+++ b/drivers/usb/Makefile
@@ -12,6 +12,8 @@ obj-$(CONFIG_USB_DWC3)		+= dwc3/
 obj-$(CONFIG_USB_DWC2)		+= dwc2/
 obj-$(CONFIG_USB_ISP1760)	+= isp1760/
 
+obj-$(CONFIG_USB_CDNS3)		+= cdns3/
+
 obj-$(CONFIG_USB_MON)		+= mon/
 obj-$(CONFIG_USB_MTU3)		+= mtu3/
 
diff --git a/drivers/usb/cdns3/Kconfig b/drivers/usb/cdns3/Kconfig
new file mode 100644
index 000000000000..4adfd87811e8
--- /dev/null
+++ b/drivers/usb/cdns3/Kconfig
@@ -0,0 +1,44 @@
+config USB_CDNS3
+	tristate "Cadence USB3 Dual-Role Controller"
+	depends on USB_SUPPORT && (USB || USB_GADGET) && HAS_DMA
+	help
+	  Say Y here if your system has a cadence USB3 dual-role controller.
+	  It supports: dual-role switch, Host-only, and Peripheral-only.
+
+	  If you choose to build this driver is a dynamically linked
+	  module, the module will be called cdns3.ko.
+
+if USB_CDNS3
+
+config USB_CDNS3_GADGET
+        bool "Cadence USB3 device controller"
+        depends on USB_GADGET
+        help
+          Say Y here to enable device controller functionality of the
+          cadence USBSS-DEV driver.
+
+          This controller supports FF, HS and SS mode. It doesn't support
+          LS and SSP mode
+
+config USB_CDNS3_HOST
+        bool "Cadence USB3 host controller"
+        depends on USB_XHCI_HCD
+        help
+          Say Y here to enable host controller functionality of the
+          cadence driver.
+
+          Host controller is compliance with XHCI so it will use
+          standard XHCI driver.
+
+config USB_CDNS3_PCI_WRAP
+	tristate "Cadence USB3 support on PCIe-based platforms"
+	depends on USB_PCI && ACPI
+	default USB_CDNS3
+	help
+	  If you're using the USBSS Core IP with a PCIe, please say
+	  'Y' or 'M' here.
+
+	  If you choose to build this driver as module it will
+	  be dynamically linked and module will be called cdns3-pci.ko
+
+endif
diff --git a/drivers/usb/cdns3/Makefile b/drivers/usb/cdns3/Makefile
new file mode 100644
index 000000000000..3f63baa24294
--- /dev/null
+++ b/drivers/usb/cdns3/Makefile
@@ -0,0 +1,16 @@
+# SPDX-License-Identifier: GPL-2.0
+# define_trace.h needs to know how to find our header
+CFLAGS_trace.o				:= -I$(src)
+
+obj-$(CONFIG_USB_CDNS3)			+= cdns3.o
+obj-$(CONFIG_USB_CDNS3_PCI_WRAP)	+= cdns3-pci.o
+
+cdns3-y					:= core.o drd.o trace.o
+
+ifneq ($(CONFIG_DEBUG_FS),)
+	cdns3-y				+= debugfs.o
+endif
+
+cdns3-$(CONFIG_USB_CDNS3_GADGET)	+= gadget.o ep0.o
+cdns3-$(CONFIG_USB_CDNS3_HOST)		+= host.o
+cdns3-pci-y		 		:= cdns3-pci-wrap.o
diff --git a/drivers/usb/cdns3/cdns3-pci-wrap.c b/drivers/usb/cdns3/cdns3-pci-wrap.c
new file mode 100644
index 000000000000..e93179c45ece
--- /dev/null
+++ b/drivers/usb/cdns3/cdns3-pci-wrap.c
@@ -0,0 +1,157 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Cadence USBSS PCI Glue driver
+ *
+ * Copyright (C) 2018 Cadence.
+ *
+ * Author: Pawel Laszczak <pawell@cadence.com>
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/platform_device.h>
+#include <linux/dma-mapping.h>
+#include <linux/slab.h>
+
+struct cdns3_wrap {
+	struct platform_device *plat_dev;
+	struct pci_dev *hg_dev;
+	struct resource dev_res[4];
+};
+
+struct cdns3_wrap wrap;
+
+#define RES_IRQ_ID		0
+#define RES_HOST_ID		1
+#define RES_DEV_ID		2
+#define RES_DRD_ID		3
+
+#define PCI_BAR_HOST		0
+#define PCI_BAR_DEV		2
+#define PCI_BAR_OTG		4
+
+#define PCI_DEV_FN_HOST_DEVICE	0
+#define PCI_DEV_FN_OTG		1
+
+#define PCI_DRIVER_NAME		"cdns3-pci-usbss"
+#define PLAT_DRIVER_NAME	"cdns-usb3"
+
+#define CDNS_VENDOR_ID 0x17cd
+#define CDNS_DEVICE_ID 0x0100
+
+/**
+ * cdns3_pci_probe - Probe function for Cadence USB wrapper driver
+ * @pdev: platform device object
+ * @id: pci device id
+ *
+ * Returns 0 on success otherwise negative errno
+ */
+static int cdns3_pci_probe(struct pci_dev *pdev,
+			   const struct pci_device_id *id)
+{
+	struct platform_device_info plat_info;
+	struct cdns3_wrap *wrap;
+	struct resource *res;
+	int err;
+
+	/*
+	 * for GADGET/HOST PCI (devfn) function number is 0,
+	 * for OTG PCI (devfn) function number is 1
+	 */
+	if (!id || pdev->devfn != PCI_DEV_FN_HOST_DEVICE)
+		return -EINVAL;
+
+	err = pcim_enable_device(pdev);
+	if (err) {
+		dev_err(&pdev->dev, "Enabling PCI device has failed %d\n", err);
+		return err;
+	}
+
+	pci_set_master(pdev);
+	wrap = devm_kzalloc(&pdev->dev, sizeof(*wrap), GFP_KERNEL);
+	if (!wrap) {
+		dev_err(&pdev->dev, "Failed to allocate memory\n");
+		return -ENOMEM;
+	}
+
+	/* function 0: host(BAR_0) + device(BAR_1) + otg(BAR_2)). */
+	memset(wrap->dev_res, 0x00,
+	       sizeof(struct resource) * ARRAY_SIZE(wrap->dev_res));
+	dev_dbg(&pdev->dev, "Initialize Device resources\n");
+	res = wrap->dev_res;
+
+	res[RES_DEV_ID].start = pci_resource_start(pdev, PCI_BAR_DEV);
+	res[RES_DEV_ID].end =   pci_resource_end(pdev, PCI_BAR_DEV);
+	res[RES_DEV_ID].name = "cdns3-dev-regs";
+	res[RES_DEV_ID].flags = IORESOURCE_MEM;
+	dev_dbg(&pdev->dev, "USBSS-DEV physical base addr: %pa\n",
+		&res[RES_DEV_ID].start);
+
+	res[RES_HOST_ID].start = pci_resource_start(pdev, PCI_BAR_HOST);
+	res[RES_HOST_ID].end = pci_resource_end(pdev, PCI_BAR_HOST);
+	res[RES_HOST_ID].name = "cdns3-xhci-regs";
+	res[RES_HOST_ID].flags = IORESOURCE_MEM;
+	dev_dbg(&pdev->dev, "USBSS-XHCI physical base addr: %pa\n",
+		&res[RES_HOST_ID].start);
+
+	res[RES_DRD_ID].start = pci_resource_start(pdev, PCI_BAR_OTG);
+	res[RES_DRD_ID].end =   pci_resource_end(pdev, PCI_BAR_OTG);
+	res[RES_DRD_ID].name = "cdns3-otg";
+	res[RES_DRD_ID].flags = IORESOURCE_MEM;
+	dev_dbg(&pdev->dev, "USBSS-DRD physical base addr: %pa\n",
+		&res[RES_DRD_ID].start);
+
+	/* Interrupt common for both device and XHCI */
+	wrap->dev_res[RES_IRQ_ID].start = pdev->irq;
+	wrap->dev_res[RES_IRQ_ID].name = "cdns3-irq";
+	wrap->dev_res[RES_IRQ_ID].flags = IORESOURCE_IRQ;
+
+	/* set up platform device info */
+	memset(&plat_info, 0, sizeof(plat_info));
+	plat_info.parent = &pdev->dev;
+	plat_info.fwnode = pdev->dev.fwnode;
+	plat_info.name = PLAT_DRIVER_NAME;
+	plat_info.id = pdev->devfn;
+	plat_info.res = wrap->dev_res;
+	plat_info.num_res = ARRAY_SIZE(wrap->dev_res);
+	plat_info.dma_mask = pdev->dma_mask;
+
+	/* register platform device */
+	wrap->plat_dev = platform_device_register_full(&plat_info);
+	if (IS_ERR(wrap->plat_dev)) {
+		err = PTR_ERR(wrap->plat_dev);
+		return err;
+	}
+
+	pci_set_drvdata(pdev, wrap);
+
+	return err;
+}
+
+void cdns3_pci_remove(struct pci_dev *pdev)
+{
+	struct cdns3_wrap *wrap = (struct cdns3_wrap *)pci_get_drvdata(pdev);
+
+	platform_device_unregister(wrap->plat_dev);
+}
+
+static const struct pci_device_id cdns3_pci_ids[] = {
+	{ PCI_DEVICE(CDNS_VENDOR_ID, CDNS_DEVICE_ID), },
+	{ 0, }
+};
+
+static struct pci_driver cdns3_pci_driver = {
+	.name = PCI_DRIVER_NAME,
+	.id_table = cdns3_pci_ids,
+	.probe = cdns3_pci_probe,
+	.remove = cdns3_pci_remove,
+};
+
+module_pci_driver(cdns3_pci_driver);
+MODULE_DEVICE_TABLE(pci, cdns3_pci_ids);
+
+MODULE_AUTHOR("Pawel Laszczak <pawell@cadence.com>");
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("Cadence USBSS PCI wrapperr");
+
diff --git a/drivers/usb/cdns3/core.c b/drivers/usb/cdns3/core.c
new file mode 100644
index 000000000000..d274586aca36
--- /dev/null
+++ b/drivers/usb/cdns3/core.c
@@ -0,0 +1,406 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Cadence USBSS DRD Driver.
+ *
+ * Copyright (C) 2018 Cadence.
+ * Copyright (C) 2017-2018 NXP
+ *
+ * Author: Peter Chen <peter.chen@nxp.com>
+ *         Pawel Laszczak <pawell@cadence.com>
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/platform_device.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/pm_runtime.h>
+
+#include "gadget.h"
+#include "core.h"
+#include "host-export.h"
+#include "gadget-export.h"
+#include "drd.h"
+#include "debug.h"
+
+static inline
+struct cdns3_role_driver *cdns3_get_current_role_driver(struct cdns3 *cdns)
+{
+	WARN_ON(cdns->role >= CDNS3_ROLE_END || !cdns->roles[cdns->role]);
+	return cdns->roles[cdns->role];
+}
+
+static inline int cdns3_role_start(struct cdns3 *cdns, enum cdns3_roles role)
+{
+	int ret;
+
+	if (WARN_ON(role >= CDNS3_ROLE_END))
+		return 0;
+
+	if (!cdns->roles[role])
+		return -ENXIO;
+
+	if (cdns->roles[role]->state == CDNS3_ROLE_STATE_ACTIVE)
+		return 0;
+
+	mutex_lock(&cdns->mutex);
+	cdns->role = role;
+	ret = cdns->roles[role]->start(cdns);
+	if (!ret)
+		cdns->roles[role]->state = CDNS3_ROLE_STATE_ACTIVE;
+	mutex_unlock(&cdns->mutex);
+	return ret;
+}
+
+void cdns3_role_stop(struct cdns3 *cdns)
+{
+	enum cdns3_roles role = cdns->role;
+
+	if (role >= CDNS3_ROLE_END) {
+		WARN_ON(role > CDNS3_ROLE_END);
+		return;
+	}
+
+	if (cdns->roles[role]->state == CDNS3_ROLE_STATE_INACTIVE)
+		return;
+
+	mutex_lock(&cdns->mutex);
+	cdns->roles[role]->stop(cdns);
+	cdns->roles[role]->state = CDNS3_ROLE_STATE_INACTIVE;
+	mutex_unlock(&cdns->mutex);
+}
+
+/*
+ * cdns->role gets from cdns3_get_initial_role, and this API tells role at the
+ * runtime.
+ * If both roles are supported, the role is selected based on vbus/id.
+ * It could be read from OTG register or external connector.
+ * If only single role is supported, only one role structure
+ * is allocated, cdns->roles[CDNS3_ROLE_HOST] or cdns->roles[CDNS3_ROLE_GADGET].
+ */
+static enum cdns3_roles cdns3_get_initial_role(struct cdns3 *cdns)
+{
+	if (cdns->roles[CDNS3_ROLE_HOST] && cdns->roles[CDNS3_ROLE_GADGET]) {
+		if (cdns3_is_host(cdns))
+			return CDNS3_ROLE_HOST;
+		if (cdns3_is_device(cdns))
+			return CDNS3_ROLE_GADGET;
+	}
+	return cdns->roles[CDNS3_ROLE_HOST]
+		? CDNS3_ROLE_HOST
+		: CDNS3_ROLE_GADGET;
+}
+
+static void cdns3_exit_roles(struct cdns3 *cdns)
+{
+	cdns3_role_stop(cdns);
+	cdns3_drd_exit(cdns);
+}
+
+/**
+ * cdns3_core_init_role - initialize role of operation
+ * @cdns: Pointer to cdns3 structure
+ *
+ * Returns 0 on success otherwise negative errno
+ */
+static int cdns3_core_init_role(struct cdns3 *cdns)
+{
+	struct device *dev = cdns->dev;
+	enum usb_dr_mode best_dr_mode;
+	enum usb_dr_mode dr_mode;
+	int ret = 0;
+
+	dr_mode = usb_get_dr_mode(dev);
+	cdns->role = CDNS3_ROLE_END;
+
+	/*
+	 * If driver can't read mode by means of usb_get_dr_mdoe function then
+	 * chooses mode according with Kernel configuration. This setting
+	 * can be restricted later depending on strap pin configuration.
+	 */
+	if (dr_mode == USB_DR_MODE_UNKNOWN) {
+		if (IS_ENABLED(CONFIG_USB_CDNS3_HOST) &&
+		    IS_ENABLED(CONFIG_USB_CDNS3_GADGET))
+			dr_mode = USB_DR_MODE_OTG;
+		else if (IS_ENABLED(CONFIG_USB_CDNS3_HOST))
+			dr_mode = USB_DR_MODE_HOST;
+		else if (IS_ENABLED(CONFIG_USB_CDNS3_GADGET))
+			dr_mode = USB_DR_MODE_PERIPHERAL;
+	}
+
+	best_dr_mode = USB_DR_MODE_OTG;
+
+	if (dr_mode == USB_DR_MODE_OTG) {
+		best_dr_mode = cdns->dr_mode;
+	} else if (cdns->dr_mode == USB_DR_MODE_OTG) {
+		best_dr_mode = dr_mode;
+	} else if (cdns->dr_mode != dr_mode) {
+		dev_err(dev, "Incorrect DRD configuration\n");
+		return -EINVAL;
+	}
+
+	dr_mode = best_dr_mode;
+
+	if (dr_mode == USB_DR_MODE_OTG || dr_mode == USB_DR_MODE_HOST) {
+		ret = cdns3_host_init(cdns);
+		if (ret) {
+			dev_err(dev, "Host initialization failed with %d\n",
+				ret);
+			goto err;
+		}
+	}
+
+	if (dr_mode == USB_DR_MODE_OTG || dr_mode == USB_DR_MODE_PERIPHERAL) {
+		ret = cdns3_gadget_init(cdns);
+		if (ret) {
+			dev_err(dev, "Device initialization failed with %d\n",
+				ret);
+			goto err;
+		}
+	}
+
+	cdns->desired_dr_mode = dr_mode;
+	cdns->dr_mode = dr_mode;
+	/*
+	 * dr_mode could be change so DRD must update controller
+	 * configuration
+	 */
+	ret = cdns3_drd_update_mode(cdns);
+
+	cdns->role = cdns3_get_initial_role(cdns);
+
+	ret = cdns3_role_start(cdns, cdns->role);
+	if (ret) {
+		dev_err(dev, "can't start %s role\n",
+			cdns3_get_current_role_driver(cdns)->name);
+		goto err;
+	}
+
+	return ret;
+err:
+	cdns3_exit_roles(cdns);
+	return ret;
+}
+
+/**
+ * cdsn3_get_real_role - get real role of controller based on hardware settings.
+ * @cdns: Pointer to cdns3 structure
+ *
+ * Returns role
+ */
+enum cdns3_roles cdsn3_get_real_role(struct cdns3 *cdns)
+{
+	enum cdns3_roles role = CDNS3_ROLE_END;
+
+	if (cdns->current_dr_mode == USB_DR_MODE_OTG) {
+		if (cdns3_get_id(cdns))
+			role = CDNS3_ROLE_GADGET;
+		else
+			role = CDNS3_ROLE_HOST;
+	} else {
+		if (cdns3_is_host(cdns))
+			role = CDNS3_ROLE_HOST;
+		if (cdns3_is_device(cdns))
+			role = CDNS3_ROLE_GADGET;
+	}
+
+	return role;
+}
+
+/**
+ * cdns3_role_switch - work queue handler for role switch
+ *
+ * @work: work queue item structure
+ *
+ * Handles below events:
+ * - Role switch for dual-role devices
+ * - CDNS3_ROLE_GADGET <--> CDNS3_ROLE_END for peripheral-only devices
+ */
+static void cdns3_role_switch(struct work_struct *work)
+{
+	enum cdns3_roles role = CDNS3_ROLE_END;
+	struct cdns3_role_driver *role_drv;
+	enum cdns3_roles current_role;
+	struct cdns3 *cdns;
+	int ret = 0;
+
+	cdns = container_of(work, struct cdns3, role_switch_wq);
+
+	/* During switching cdns->role can be different then role */
+	role = cdsn3_get_real_role(cdns);
+
+	role_drv = cdns3_get_current_role_driver(cdns);
+
+	pm_runtime_get_sync(cdns->dev);
+
+	/* Disable current role. This state can be forced from user space. */
+	if (cdns->debug_disable && role_drv->state == CDNS3_ROLE_STATE_ACTIVE) {
+		cdns3_role_stop(cdns);
+		goto exit;
+	}
+
+	/* Do nothing if nothing changed */
+	if (cdns->role == role && role_drv->state == CDNS3_ROLE_STATE_ACTIVE)
+		goto exit;
+
+	cdns3_role_stop(cdns);
+
+	role = cdsn3_get_real_role(cdns);
+
+	current_role = cdns->role;
+	dev_dbg(cdns->dev, "Switching role");
+
+	ret = cdns3_role_start(cdns, role);
+
+	if (ret) {
+		/* Back to current role */
+		dev_err(cdns->dev, "set %d has failed, back to %d\n",
+			role, current_role);
+		cdns3_role_start(cdns, current_role);
+	}
+exit:
+	pm_runtime_put_sync(cdns->dev);
+}
+
+/**
+ * cdns3_probe - probe for cdns3 core device
+ * @pdev: Pointer to cdns3 core platform device
+ *
+ * Returns 0 on success otherwise negative errno
+ */
+static int cdns3_probe(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	struct resource	*res;
+	struct cdns3 *cdns;
+	void __iomem *regs;
+	int ret;
+
+	cdns = devm_kzalloc(dev, sizeof(*cdns), GFP_KERNEL);
+	if (!cdns)
+		return -ENOMEM;
+
+	cdns->dev = dev;
+
+	platform_set_drvdata(pdev, cdns);
+
+	res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
+	if (!res) {
+		dev_err(dev, "missing IRQ\n");
+		return -ENODEV;
+	}
+	cdns->irq = res->start;
+
+	cdns->xhci_res[0] = *res;
+
+	/*
+	 * Request memory region
+	 * region-0: xHCI
+	 * region-1: Peripheral
+	 * region-2: OTG registers
+	 */
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	cdns->xhci_res[1] = *res;
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+	regs = devm_ioremap_resource(dev, res);
+	if (IS_ERR(regs))
+		return PTR_ERR(regs);
+	cdns->dev_regs	= regs;
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 2);
+	cdns->otg_res = *res;
+
+	mutex_init(&cdns->mutex);
+
+	cdns->phy = devm_phy_get(dev, "cdns3,usbphy");
+	if (IS_ERR(cdns->phy)) {
+		ret = PTR_ERR(cdns->phy);
+		if (ret == -ENODEV) {
+			cdns->phy = NULL;
+		} else if (ret == -EPROBE_DEFER) {
+			return ret;
+		} else {
+			dev_err(dev, "no phy found\n");
+			goto err0;
+		}
+	}
+
+	phy_init(cdns->phy);
+
+	INIT_WORK(&cdns->role_switch_wq, cdns3_role_switch);
+
+	ret = cdns3_drd_init(cdns);
+	if (ret)
+		goto err1;
+
+	ret = cdns3_core_init_role(cdns);
+	if (ret)
+		goto err1;
+
+	cdns3_debugfs_init(cdns);
+	device_set_wakeup_capable(dev, true);
+	pm_runtime_set_active(dev);
+	pm_runtime_enable(dev);
+
+	/*
+	 * The controller needs less time between bus and controller suspend,
+	 * and we also needs a small delay to avoid frequently entering low
+	 * power mode.
+	 */
+	pm_runtime_set_autosuspend_delay(dev, 20);
+	pm_runtime_mark_last_busy(dev);
+	pm_runtime_use_autosuspend(dev);
+	dev_dbg(dev, "Cadence USB3 core: probe succeed\n");
+
+	return 0;
+
+err1:
+	phy_exit(cdns->phy);
+err0:
+	return ret;
+}
+
+/**
+ * cdns3_remove - unbind drd driver and clean up
+ * @pdev: Pointer to Linux platform device
+ *
+ * Returns 0 on success otherwise negative errno
+ */
+static int cdns3_remove(struct platform_device *pdev)
+{
+	struct cdns3 *cdns = platform_get_drvdata(pdev);
+
+	pm_runtime_get_sync(&pdev->dev);
+	pm_runtime_disable(&pdev->dev);
+	pm_runtime_put_noidle(&pdev->dev);
+	cdns3_debugfs_exit(cdns);
+	cdns3_exit_roles(cdns);
+	phy_exit(cdns->phy);
+	return 0;
+}
+
+#ifdef CONFIG_OF
+static const struct of_device_id of_cdns3_match[] = {
+	{ .compatible = "cdns,usb3-1.0.0" },
+	{ .compatible = "cdns,usb3-1.0.1" },
+	{ },
+};
+MODULE_DEVICE_TABLE(of, of_cdns3_match);
+#endif
+
+static struct platform_driver cdns3_driver = {
+	.probe		= cdns3_probe,
+	.remove		= cdns3_remove,
+	.driver		= {
+		.name	= "cdns-usb3",
+		.of_match_table	= of_match_ptr(of_cdns3_match),
+	},
+};
+
+module_platform_driver(cdns3_driver);
+
+MODULE_ALIAS("platform:cdns3");
+MODULE_AUTHOR("Pawel Laszczak <pawell@cadence.com>");
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("Cadence USB3 DRD Controller Driver");
diff --git a/drivers/usb/cdns3/core.h b/drivers/usb/cdns3/core.h
new file mode 100644
index 000000000000..fb4b39206158
--- /dev/null
+++ b/drivers/usb/cdns3/core.h
@@ -0,0 +1,116 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Cadence USBSS DRD Header File.
+ *
+ * Copyright (C) 2017-2018 NXP
+ * Copyright (C) 2018 Cadence.
+ *
+ * Authors: Peter Chen <peter.chen@nxp.com>
+ *          Pawel Laszczak <pawell@cadence.com>
+ */
+#include <linux/usb/otg.h>
+
+#ifndef __LINUX_CDNS3_CORE_H
+#define __LINUX_CDNS3_CORE_H
+
+struct cdns3;
+enum cdns3_roles {
+	CDNS3_ROLE_HOST = 0,
+	CDNS3_ROLE_GADGET,
+	CDNS3_ROLE_END,
+};
+
+/**
+ * struct cdns3_role_driver - host/gadget role driver
+ * @start: start this role
+ * @stop: stop this role
+ * @suspend: suspend callback for this role
+ * @resume: resume callback for this role
+ * @irq: irq handler for this role
+ * @name: role name string (host/gadget)
+ * @state: current state
+ */
+struct cdns3_role_driver {
+	int (*start)(struct cdns3 *cdns);
+	void (*stop)(struct cdns3 *cdns);
+	int (*suspend)(struct cdns3 *cdns, bool do_wakeup);
+	int (*resume)(struct cdns3 *cdns, bool hibernated);
+	const char *name;
+#define CDNS3_ROLE_STATE_INACTIVE	0
+#define CDNS3_ROLE_STATE_ACTIVE		1
+	int state;
+};
+
+#define CDNS3_XHCI_RESOURCES_NUM	2
+/**
+ * struct cdns3 - Representation of Cadence USB3 DRD controller.
+ * @dev: pointer to Cadence device struct
+ * @xhci_regs: pointer to base of xhci registers
+ * @xhci_res: the resource for xhci
+ * @dev_regs: pointer to base of dev registers
+ * @otg_regs: pointer to base of otg registers
+ * @irq: irq number for controller
+ * @roles: array of supported roles for this controller
+ * @role: current role
+ * @host_dev: the child host device pointer for cdns3 core
+ * @gadget_dev: the child gadget device pointer for cdns3 core
+ * @usb: phy for this controller
+ * @role_switch_wq: work queue item for role switch
+ * @in_lpm: the controller in low power mode
+ * @wakeup_int: the wakeup interrupt
+ * @mutex: the mutex for concurrent code at driver
+ * @dr_mode: supported mode of operation it can be only Host, only Device
+ *           or OTG mode that allow to switch between Device and Host mode.
+ *           This field based on firmware setting, kernel configuration
+ *           and hardware configuration.
+ * @current_dr_mode: current mode of operation when in dual-role mode
+ * @desired_dr_mode: desired mode of operation when in dual-role mode.
+ *           This value can be changed during runtime.
+ *           Available options depends on  dr_mode:
+ *           dr_mode                 |  desired_dr_mode and current_dr_mode
+ *           ----------------------------------------------------------------
+ *           USB_DR_MODE_HOST        | only USB_DR_MODE_HOST
+ *           USB_DR_MODE_PERIPHERAL  | only USB_DR_MODE_PERIPHERAL
+ *           USB_DR_MODE_OTG         | only USB_DR_MODE_HOST
+ *           USB_DR_MODE_OTG         | only USB_DR_MODE_PERIPHERAL
+ *           USB_DR_MODE_OTG         | USB_DR_MODE_OTG
+ *
+ *           Desired_dr_role can be changed by means of debugfs.
+ * @root: debugfs root folder pointer
+ * @debug_disable:
+ */
+struct cdns3 {
+	struct device			*dev;
+	void __iomem			*xhci_regs;
+	struct resource			xhci_res[CDNS3_XHCI_RESOURCES_NUM];
+	struct cdns3_usb_regs __iomem	*dev_regs;
+
+	struct resource			otg_res;
+	struct cdns3_otg_legacy_regs	*otg_v0_regs;
+	struct cdns3_otg_regs		*otg_v1_regs;
+	struct cdns3_otg_common_regs	*otg_regs;
+#define CDNS3_CONTROLLER_V0	0
+#define CDNS3_CONTROLLER_V1	1
+	u32				version;
+
+	int				irq;
+	struct cdns3_role_driver	*roles[CDNS3_ROLE_END];
+	enum cdns3_roles		role;
+	struct platform_device		*host_dev;
+	struct cdns3_device		*gadget_dev;
+	struct phy			*phy;
+	struct work_struct		role_switch_wq;
+	int				in_lpm:1;
+	int				wakeup_int:1;
+	/* mutext used in workqueue*/
+	struct mutex			mutex;
+	enum usb_dr_mode		dr_mode;
+	enum usb_dr_mode		current_dr_mode;
+	enum usb_dr_mode		desired_dr_mode;
+	struct dentry			*root;
+	int				debug_disable:1;
+};
+
+void cdns3_role_stop(struct cdns3 *cdns);
+
+#endif /* __LINUX_CDNS3_CORE_H */
diff --git a/drivers/usb/cdns3/debug.h b/drivers/usb/cdns3/debug.h
new file mode 100644
index 000000000000..94f9ef15f899
--- /dev/null
+++ b/drivers/usb/cdns3/debug.h
@@ -0,0 +1,166 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Cadence USBSS DRD Driver.
+ * Debug header file.
+ *
+ * Copyright (C) 2018 Cadence.
+ *
+ * Author: Pawel Laszczak <pawell@cadence.com>
+ */
+#ifndef __LINUX_CDNS3_DEBUG
+#define __LINUX_CDNS3_DEBUG
+
+#include "core.h"
+
+static inline char *cdns3_decode_usb_irq(char *str,
+					 enum usb_device_speed speed,
+					 u32 usb_ists)
+{
+	int ret;
+
+	ret = sprintf(str, "IRQ %08x = ", usb_ists);
+
+	if (usb_ists & (USB_ISTS_CON2I | USB_ISTS_CONI)) {
+		ret += sprintf(str + ret, "Connection %s\n",
+			       usb_speed_string(speed));
+	}
+	if (usb_ists & USB_ISTS_CON2I || usb_ists & USB_ISTS_CONI)
+		ret += sprintf(str + ret, "Disconnection ");
+	if (usb_ists & USB_ISTS_L2ENTI)
+		ret += sprintf(str + ret, "suspended ");
+
+	if (usb_ists & USB_ISTS_L2EXTI)
+		ret += sprintf(str + ret, "L2 exit ");
+	if (usb_ists & USB_ISTS_U3EXTI)
+		ret += sprintf(str + ret, "U3 exit ");
+	if (usb_ists & USB_ISTS_UWRESI)
+		ret += sprintf(str + ret, "Warm Reset ");
+	if (usb_ists & USB_ISTS_UHRESI)
+		ret += sprintf(str + ret, "Hot Reset ");
+	if (usb_ists & USB_ISTS_U2RESI)
+		ret += sprintf(str + ret, "Reset");
+
+	return str;
+}
+
+static inline  char *cdns3_decode_ep_irq(char *str,
+					 u32 ep_sts,
+					 const char *ep_name)
+{
+	int ret;
+
+	ret = sprintf(str, "IRQ for %s: %08x ", ep_name, ep_sts);
+
+	if (ep_sts & EP_STS_SETUP)
+		ret += sprintf(str + ret, "SETUP ");
+	if (ep_sts & EP_STS_IOC)
+		ret += sprintf(str + ret, "IOC ");
+	if (ep_sts & EP_STS_ISP)
+		ret += sprintf(str + ret, "ISP ");
+	if (ep_sts & EP_STS_DESCMIS)
+		ret += sprintf(str + ret, "DESCMIS ");
+	if (ep_sts & EP_STS_STREAMR)
+		ret += sprintf(str + ret, "STREAMR ");
+	if (ep_sts & EP_STS_MD_EXIT)
+		ret += sprintf(str + ret, "MD_EXIT ");
+	if (ep_sts & EP_STS_TRBERR)
+		ret += sprintf(str + ret, "TRBERR ");
+	if (ep_sts & EP_STS_NRDY)
+		ret += sprintf(str + ret, "NRDY ");
+	if (ep_sts & EP_STS_PRIME)
+		ret += sprintf(str + ret, "PRIME ");
+	if (ep_sts & EP_STS_SIDERR)
+		ret += sprintf(str + ret, "SIDERRT ");
+	if (ep_sts & EP_STS_OUTSMM)
+		ret += sprintf(str + ret, "OUTSMM ");
+	if (ep_sts & EP_STS_ISOERR)
+		ret += sprintf(str + ret, "ISOERR ");
+	if (ep_sts & EP_STS_IOT)
+		ret += sprintf(str + ret, "IOT ");
+
+	return str;
+}
+
+static inline char *cdns3_decode_epx_irq(char *str,
+					 char *ep_name,
+					 u32 ep_sts)
+{
+	return cdns3_decode_ep_irq(str, ep_sts, ep_name);
+}
+
+static inline char *cdns3_decode_ep0_irq(char *str,
+					 int dir,
+					 u32 ep_sts)
+{
+	return cdns3_decode_ep_irq(str, ep_sts,
+				   dir ? "ep0IN" : "ep0OUT");
+}
+
+/**
+ * Debug a transfer ring.
+ *
+ * Prints out all TRBs in the endpoint ring, even those after the Link TRB.
+ *.
+ */
+static inline char *cdns3_dbg_ring(struct cdns3_endpoint *priv_ep,
+				   struct cdns3_trb *ring, char *str)
+{
+	dma_addr_t addr = priv_ep->trb_pool_dma;
+	struct cdns3_trb *trb;
+	int trb_per_sector;
+	int ret = 0;
+	int i;
+
+	trb_per_sector = GET_TRBS_PER_SEGMENT(priv_ep->type);
+
+	trb = &priv_ep->trb_pool[priv_ep->dequeue];
+	ret += sprintf(str + ret, "\n\t\tRing contents for %s:", priv_ep->name);
+
+	ret += sprintf(str + ret,
+		       "\n\t\tRing deq index: %d, trb: %p (virt), 0x%llx (dma)\n",
+		       priv_ep->dequeue, trb,
+		       (unsigned long long)cdns3_trb_virt_to_dma(priv_ep, trb));
+
+	trb = &priv_ep->trb_pool[priv_ep->enqueue];
+	ret += sprintf(str + ret,
+		       "\t\tRing enq index: %d, trb: %p (virt), 0x%llx (dma)\n",
+		       priv_ep->enqueue, trb,
+		       (unsigned long long)cdns3_trb_virt_to_dma(priv_ep, trb));
+
+	ret += sprintf(str + ret,
+		       "\t\tfree trbs: %d, CCS=%d, PCS=%d\n",
+		       priv_ep->free_trbs, priv_ep->ccs, priv_ep->pcs);
+
+	if (trb_per_sector > TRBS_PER_SEGMENT)
+		trb_per_sector = TRBS_PER_SEGMENT;
+
+	if (trb_per_sector > TRBS_PER_SEGMENT) {
+		sprintf(str + ret, "\t\tTo big transfer ring %d\n",
+			trb_per_sector);
+		return str;
+	}
+
+	for (i = 0; i < trb_per_sector; ++i) {
+		trb = &ring[i];
+		ret += sprintf(str + ret,
+			"\t\t@%pad %08x %08x %08x\n", &addr,
+			le32_to_cpu(trb->buffer),
+			le32_to_cpu(trb->length),
+			le32_to_cpu(trb->control));
+		addr += sizeof(*trb);
+	}
+
+	return str;
+}
+
+#ifdef CONFIG_DEBUG_FS
+void cdns3_debugfs_init(struct cdns3 *cdns);
+void cdns3_debugfs_exit(struct cdns3 *cdns);
+#else
+void cdns3_debugfs_init(struct cdns3 *cdns);
+{  }
+void cdns3_debugfs_exit(struct cdns3 *cdns);
+{  }
+#endif
+
+#endif /*__LINUX_CDNS3_DEBUG*/
diff --git a/drivers/usb/cdns3/debugfs.c b/drivers/usb/cdns3/debugfs.c
new file mode 100644
index 000000000000..d7919f5c1d90
--- /dev/null
+++ b/drivers/usb/cdns3/debugfs.c
@@ -0,0 +1,168 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Cadence USBSS DRD Controller DebugFS filer.
+ *
+ * Copyright (C) 2018 Cadence.
+ *
+ * Author: Pawel Laszczak <pawell@cadence.com>
+ */
+
+#include <linux/types.h>
+#include <linux/debugfs.h>
+#include <linux/seq_file.h>
+#include <linux/uaccess.h>
+
+#include "core.h"
+#include "gadget.h"
+#include "drd.h"
+
+static int cdns3_mode_show(struct seq_file *s, void *unused)
+{
+	struct cdns3 *cdns = s->private;
+
+	switch (cdns->current_dr_mode) {
+	case USB_DR_MODE_HOST:
+		seq_puts(s, "host\n");
+		break;
+	case USB_DR_MODE_PERIPHERAL:
+		seq_puts(s, "device\n");
+		break;
+	case USB_DR_MODE_OTG:
+		seq_puts(s, "otg\n");
+		break;
+	default:
+		seq_puts(s, "UNKNOWN mode\n");
+	}
+
+	return 0;
+}
+
+static int cdns3_mode_open(struct inode *inode, struct file *file)
+{
+	return single_open(file, cdns3_mode_show, inode->i_private);
+}
+
+static ssize_t cdns3_mode_write(struct file *file,
+				const char __user *ubuf,
+				size_t count, loff_t *ppos)
+{
+	struct seq_file	 *s = file->private_data;
+	struct cdns3 *cdns = s->private;
+	u32 mode = USB_DR_MODE_UNKNOWN;
+	char buf[32];
+
+	if (copy_from_user(&buf, ubuf, min_t(size_t, sizeof(buf) - 1, count)))
+		return -EFAULT;
+
+	if (!strncmp(buf, "host", 4)) {
+		if (cdns->dr_mode == USB_DR_MODE_HOST ||
+		    cdns->dr_mode == USB_DR_MODE_OTG) {
+			mode = USB_DR_MODE_HOST;
+		}
+	}
+
+	if (!strncmp(buf, "device", 6))
+		if (cdns->dr_mode == USB_DR_MODE_PERIPHERAL ||
+		    cdns->dr_mode == USB_DR_MODE_OTG)
+			mode = USB_DR_MODE_PERIPHERAL;
+
+	if (!strncmp(buf, "otg", 3) && cdns->dr_mode == USB_DR_MODE_OTG)
+		mode = USB_DR_MODE_OTG;
+
+	if (mode == USB_DR_MODE_UNKNOWN) {
+		dev_err(cdns->dev, "Failed: incorrect mode setting\n");
+		return -EFAULT;
+	}
+
+	if (cdns->current_dr_mode != mode) {
+		cdns->desired_dr_mode = mode;
+		cdns->debug_disable = 0;
+		cdns3_role_stop(cdns);
+		cdns3_drd_update_mode(cdns);
+		queue_work(system_freezable_wq, &cdns->role_switch_wq);
+	}
+
+	return count;
+}
+
+static const struct file_operations cdns3_mode_fops = {
+	.open			= cdns3_mode_open,
+	.write			= cdns3_mode_write,
+	.read			= seq_read,
+	.llseek			= seq_lseek,
+	.release		= single_release,
+};
+
+static int cdns3_disable_show(struct seq_file *s, void *unused)
+{
+	struct cdns3 *cdns = s->private;
+
+	if (!cdns->debug_disable)
+		seq_puts(s, "0\n");
+	else
+		seq_puts(s, "1\n");
+
+	return 0;
+}
+
+static ssize_t cdns3_disable_write(struct file *file,
+				   const char __user *ubuf,
+				   size_t count, loff_t *ppos)
+{
+	struct seq_file	 *s = file->private_data;
+	struct cdns3 *cdns = s->private;
+	int disable;
+	char buf[32];
+
+	if (copy_from_user(&buf, ubuf, min_t(size_t, sizeof(buf) - 1, count)))
+		return -EFAULT;
+
+	if (!strncmp(buf, "1", 1) || !strncmp(buf, "yes", 3)) {
+		disable = 1;
+	} else if (!strncmp(buf, "0", 1) || !strncmp(buf, "no", 2)) {
+		disable = 0;
+	} else {
+		dev_err(cdns->dev, "Failed: incorrect disable setting\n");
+		return -EFAULT;
+	}
+
+	if (disable != cdns->debug_disable) {
+		cdns->debug_disable = disable;
+		queue_work(system_freezable_wq, &cdns->role_switch_wq);
+	}
+
+	return count;
+}
+
+static int cdns3_disable_open(struct inode *inode, struct file *file)
+{
+	return single_open(file, cdns3_disable_show, inode->i_private);
+}
+
+static const struct file_operations cdns3_disable_fops = {
+	.open			= cdns3_disable_open,
+	.write			= cdns3_disable_write,
+	.read			= seq_read,
+	.llseek			= seq_lseek,
+	.release		= single_release,
+};
+
+void cdns3_debugfs_init(struct cdns3 *cdns)
+{
+	struct dentry *root;
+
+	root = debugfs_create_dir(dev_name(cdns->dev), NULL);
+	cdns->root = root;
+	if (IS_ENABLED(CONFIG_USB_CDNS3_GADGET) &&
+	    IS_ENABLED(CONFIG_USB_CDNS3_HOST))
+		debugfs_create_file("mode", 0644, root, cdns,
+				    &cdns3_mode_fops);
+
+	debugfs_create_file("disable", 0644, root, cdns,
+			    &cdns3_disable_fops);
+}
+
+void cdns3_debugfs_exit(struct cdns3 *cdns)
+{
+	debugfs_remove_recursive(cdns->root);
+}
diff --git a/drivers/usb/cdns3/drd.c b/drivers/usb/cdns3/drd.c
new file mode 100644
index 000000000000..b0c32302eb0b
--- /dev/null
+++ b/drivers/usb/cdns3/drd.c
@@ -0,0 +1,350 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Cadence USBSS DRD Driver.
+ *
+ * Copyright (C) 2018 Cadence.
+ *
+ * Author: Pawel Laszczak <pawell@cadence.com
+ *
+ */
+#include <linux/kernel.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <linux/usb/otg.h>
+
+#include "gadget.h"
+#include "drd.h"
+#include "core.h"
+
+static int cdns3_drd_switch_gadget(struct cdns3 *cdns, int on);
+static int cdns3_drd_switch_host(struct cdns3 *cdns, int on);
+
+/**
+ * cdns3_set_mode - change mode of OTG Core
+ * @cdns: pointer to context structure
+ * @mode: selected mode from cdns_role
+ */
+void cdns3_set_mode(struct cdns3 *cdns, enum usb_dr_mode mode)
+{
+	u32 reg;
+
+	cdns->current_dr_mode = mode;
+
+	switch (mode) {
+	case USB_DR_MODE_PERIPHERAL:
+		dev_info(cdns->dev, "Set controller to Gadget mode\n");
+		cdns3_drd_switch_gadget(cdns, 1);
+		break;
+	case USB_DR_MODE_HOST:
+		dev_info(cdns->dev, "Set controller to Host mode\n");
+		cdns3_drd_switch_host(cdns, 1);
+		break;
+	case USB_DR_MODE_OTG:
+		dev_info(cdns->dev, "Set controller to OTG mode\n");
+		if (cdns->version == CDNS3_CONTROLLER_V1) {
+			reg = readl(&cdns->otg_v1_regs->override);
+			reg |= OVERRIDE_IDPULLUP;
+			writel(reg, &cdns->otg_v1_regs->override);
+		} else {
+			reg = readl(&cdns->otg_v0_regs->ctrl1);
+			reg |= OVERRIDE_IDPULLUP_V0;
+			writel(reg, &cdns->otg_v0_regs->ctrl1);
+		}
+
+		/*
+		 * Hardware specification says: "ID_VALUE must be valid within
+		 * 50ms after idpullup is set to '1" so driver must wait
+		 * 50ms before reading this pin.
+		 */
+		usleep_range(50000, 60000);
+		break;
+	default:
+		cdns->current_dr_mode = USB_DR_MODE_UNKNOWN;
+		dev_err(cdns->dev, "Unsupported mode of operation %d\n", mode);
+		return;
+	}
+}
+
+int cdns3_get_id(struct cdns3 *cdns)
+{
+	int id;
+
+	id = readl(&cdns->otg_regs->sts) & OTGSTS_ID_VALUE;
+	dev_dbg(cdns->dev, "OTG ID: %d", id);
+	return  1 ; //id;
+}
+
+int cdns3_is_host(struct cdns3 *cdns)
+{
+	if (cdns->current_dr_mode == USB_DR_MODE_HOST)
+		return 1;
+	else if (!cdns3_get_id(cdns))
+		return 1;
+
+	return 0;
+}
+
+int cdns3_is_device(struct cdns3 *cdns)
+{
+	if (cdns->current_dr_mode == USB_DR_MODE_PERIPHERAL)
+		return 1;
+	else if (cdns->current_dr_mode == USB_DR_MODE_OTG)
+		if (cdns3_get_id(cdns))
+			return 1;
+
+	return 0;
+}
+
+/**
+ * cdns3_otg_disable_irq - Disable all OTG interrupts
+ * @cdns: Pointer to controller context structure
+ */
+static void cdns3_otg_disable_irq(struct cdns3 *cdns)
+{
+	writel(0, &cdns->otg_regs->ien);
+}
+
+/**
+ * cdns3_otg_enable_irq - enable id and sess_valid interrupts
+ * @cdns: Pointer to controller context structure
+ */
+static void cdns3_otg_enable_irq(struct cdns3 *cdns)
+{
+	writel(OTGIEN_ID_CHANGE_INT | OTGIEN_VBUSVALID_RISE_INT |
+	       OTGIEN_VBUSVALID_FALL_INT, &cdns->otg_regs->ien);
+}
+
+/**
+ * cdns3_drd_switch_host - start/stop host
+ * @cdns: Pointer to controller context structure
+ * @on: 1 for start, 0 for stop
+ *
+ * Returns 0 on success otherwise negative errno
+ */
+static int cdns3_drd_switch_host(struct cdns3 *cdns, int on)
+{
+	int ret;
+	u32 reg = OTGCMD_OTG_DIS;
+
+	/* switch OTG core */
+	if (on) {
+		writel(OTGCMD_HOST_BUS_REQ | reg, &cdns->otg_regs->cmd);
+
+		dev_dbg(cdns->dev, "Waiting for Host mode is turned on\n");
+		ret = cdns3_handshake(&cdns->otg_regs->sts, OTGSTS_XHCI_READY,
+				      OTGSTS_XHCI_READY, 100000);
+
+		if (ret)
+			return ret;
+	} else {
+		usleep_range(30, 40);
+		writel(OTGCMD_HOST_BUS_DROP | OTGCMD_DEV_BUS_DROP |
+		       OTGCMD_DEV_POWER_OFF | OTGCMD_HOST_POWER_OFF,
+		       &cdns->otg_regs->cmd);
+	}
+
+	return 0;
+}
+
+/**
+ * cdns3_drd_switch_gadget - start/stop gadget
+ * @cdns: Pointer to controller context structure
+ * @on: 1 for start, 0 for stop
+ *
+ * Returns 0 on success otherwise negative errno
+ */
+static int cdns3_drd_switch_gadget(struct cdns3 *cdns, int on)
+{
+	int ret;
+	u32 reg = OTGCMD_OTG_DIS;
+
+	/* switch OTG core */
+	if (on) {
+		writel(OTGCMD_DEV_BUS_REQ | reg, &cdns->otg_regs->cmd);
+
+		dev_dbg(cdns->dev, "Waiting for Device mode is turned on\n");
+
+		ret = cdns3_handshake(&cdns->otg_regs->sts, OTGSTS_DEV_READY,
+				      OTGSTS_DEV_READY, 100000);
+
+		if (ret)
+			return ret;
+	} else {
+		/*
+		 * driver should wait at least 10us after disabling Device
+		 * before turning-off Device (DEV_BUS_DROP)
+		 */
+		usleep_range(20, 30);
+		writel(OTGCMD_HOST_BUS_DROP | OTGCMD_DEV_BUS_DROP |
+		       OTGCMD_DEV_POWER_OFF | OTGCMD_HOST_POWER_OFF,
+		       &cdns->otg_regs->cmd);
+	}
+
+	return 0;
+}
+
+/**
+ * cdns3_init_otg_mode - initialize drd controller
+ * @cdns: Pointer to controller context structure
+ *
+ * Returns 0 on success otherwise negative errno
+ */
+static void cdns3_init_otg_mode(struct cdns3 *cdns)
+{
+	cdns3_otg_disable_irq(cdns);
+	/* clear all interrupts */
+	writel(~0, &cdns->otg_regs->ivect);
+
+	cdns3_set_mode(cdns, USB_DR_MODE_OTG);
+
+	if (cdns3_is_host(cdns))
+		cdns3_drd_switch_host(cdns, 1);
+	else
+		cdns3_drd_switch_gadget(cdns, 1);
+
+	cdns3_otg_enable_irq(cdns);
+}
+
+/**
+ * cdns3_drd_update_mode - initialize mode of operation
+ * @cdns: Pointer to controller context structure
+ *
+ * Returns 0 on success otherwise negative errno
+ */
+int cdns3_drd_update_mode(struct cdns3 *cdns)
+{
+	int ret = 0;
+
+	if (cdns->desired_dr_mode == cdns->current_dr_mode)
+		return ret;
+
+	cdns3_drd_switch_gadget(cdns, 0);
+	cdns3_drd_switch_host(cdns, 0);
+
+	switch (cdns->desired_dr_mode) {
+	case USB_DR_MODE_PERIPHERAL:
+		cdns3_set_mode(cdns, USB_DR_MODE_PERIPHERAL);
+		break;
+	case USB_DR_MODE_HOST:
+		cdns3_set_mode(cdns, USB_DR_MODE_HOST);
+		break;
+	case USB_DR_MODE_OTG:
+		cdns3_init_otg_mode(cdns);
+		break;
+	default:
+		dev_err(cdns->dev, "Unsupported mode of operation %d\n",
+			cdns->dr_mode);
+		return -EINVAL;
+	}
+
+	return ret;
+}
+
+/**
+ * cdns3_drd_irq - interrupt handler for OTG events
+ *
+ * @irq: irq number for cdns3 core device
+ * @data: structure of cdns3
+ *
+ * Returns IRQ_HANDLED or IRQ_NONE
+ */
+static irqreturn_t cdns3_drd_irq(int irq, void *data)
+{
+	irqreturn_t ret = IRQ_NONE;
+	struct cdns3 *cdns = data;
+	u32 reg;
+
+	if (cdns->dr_mode != USB_DR_MODE_OTG)
+		return ret;
+
+	reg = readl(&cdns->otg_regs->ivect);
+
+	if (!reg)
+		return ret;
+
+	if (reg & OTGIEN_ID_CHANGE_INT) {
+		dev_dbg(cdns->dev, "OTG IRQ: new ID: %d\n",
+			cdns3_get_id(cdns));
+
+		queue_work(system_freezable_wq, &cdns->role_switch_wq);
+
+		ret = IRQ_HANDLED;
+	}
+
+	writel(~0, &cdns->otg_regs->ivect);
+	return ret;
+}
+
+int cdns3_drd_init(struct cdns3 *cdns)
+{
+	void __iomem *regs;
+	int ret = 0;
+	u32 state;
+
+	regs = devm_ioremap_resource(cdns->dev, &cdns->otg_res);
+	if (IS_ERR(regs))
+		return PTR_ERR(regs);
+
+	/* Detection of DRD version. Controller has been released
+	 * in two versions. Both are similar, but they have same changes
+	 * in register maps.
+	 * The first register in old version is command register and it's read
+	 * only, so driver should read 0 from it. On the other hand, in v1
+	 * the first register contains device ID number which is not set to 0.
+	 * Driver uses this fact to detect the proper version of
+	 * controller.
+	 */
+	cdns->otg_v0_regs = regs;
+	if (!readl(&cdns->otg_v0_regs->cmd)) {
+		cdns->version  = CDNS3_CONTROLLER_V0;
+		cdns->otg_v1_regs = NULL;
+		cdns->otg_regs = regs;
+		dev_info(cdns->dev, "DRD version v0 (%08x)\n",
+			 readl(&cdns->otg_v0_regs->version));
+	} else {
+		cdns->otg_v0_regs = NULL;
+		cdns->otg_v1_regs = regs;
+		cdns->otg_regs = (void *)&cdns->otg_v1_regs->cmd;
+		cdns->version  = CDNS3_CONTROLLER_V1;
+		dev_info(cdns->dev, "DRD version v1 (ID: %08x, rev: %08x)\n",
+			 readl(&cdns->otg_v1_regs->did),
+			 readl(&cdns->otg_v1_regs->rid));
+	}
+
+	state = OTGSTS_STRAP(readl(&cdns->otg_regs->sts));
+
+	/* Update dr_mode according to STRAP configuration. */
+	cdns->dr_mode = USB_DR_MODE_OTG;
+	if (state == OTGSTS_STRAP_HOST) {
+		dev_info(cdns->dev, "Controller strapped to HOST\n");
+		cdns->dr_mode = USB_DR_MODE_HOST;
+	} else if (state == OTGSTS_STRAP_GADGET) {
+		dev_info(cdns->dev, "Controller strapped to PERIPHERAL\n");
+		cdns->dr_mode = USB_DR_MODE_PERIPHERAL;
+	}
+
+	cdns->desired_dr_mode = cdns->dr_mode;
+	cdns->current_dr_mode = USB_DR_MODE_UNKNOWN;
+
+	ret = devm_request_threaded_irq(cdns->dev, cdns->irq, cdns3_drd_irq,
+					NULL, IRQF_SHARED,
+					dev_name(cdns->dev), cdns);
+
+	if (ret)
+		return ret;
+
+	state = readl(&cdns->otg_regs->sts);
+	if (OTGSTS_OTG_NRDY(state) != 0) {
+		dev_err(cdns->dev, "Cadence USB3 OTG device not ready\n");
+		return -ENODEV;
+	}
+
+	ret = cdns3_drd_update_mode(cdns);
+
+	return ret;
+}
+
+int cdns3_drd_exit(struct cdns3 *cdns)
+{
+	return cdns3_drd_switch_host(cdns, 0);
+}
diff --git a/drivers/usb/cdns3/drd.h b/drivers/usb/cdns3/drd.h
new file mode 100644
index 000000000000..6a29cdcb492d
--- /dev/null
+++ b/drivers/usb/cdns3/drd.h
@@ -0,0 +1,162 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Cadence USB3 DRD header file.
+ *
+ * Copyright (C) 2018 Cadence.
+ *
+ * Author: Pawel Laszczak <pawell@cadence.com>
+ */
+#ifndef __LINUX_CDNS3_DRD
+#define __LINUX_CDNS3_DRD
+
+#include <linux/usb/otg.h>
+#include <linux/phy/phy.h>
+#include "core.h"
+
+/*  DRD register interface for version v1. */
+struct cdns3_otg_regs {
+	__le32 did;
+	__le32 rid;
+	__le32 capabilities;
+	__le32 reserved1;
+	__le32 cmd;
+	__le32 sts;
+	__le32 state;
+	__le32 reserved2;
+	__le32 ien;
+	__le32 ivect;
+	__le32 refclk;
+	__le32 tmr;
+	__le32 reserved3[4];
+	__le32 simulate;
+	__le32 override;
+	__le32 susp_ctrl;
+	__le32 reserved4;
+	__le32 anasts;
+	__le32 adp_ramp_time;
+	__le32 ctrl1;
+	__le32 ctrl2;
+};
+
+/*  DRD register interface for version v0. */
+struct cdns3_otg_legacy_regs {
+	__le32 cmd;
+	__le32 sts;
+	__le32 state;
+	__le32 refclk;
+	__le32 ien;
+	__le32 ivect;
+	__le32 reserved1[3];
+	__le32 tmr;
+	__le32 reserved2[2];
+	__le32 version;
+	__le32 capabilities;
+	__le32 reserved3[2];
+	__le32 simulate;
+	__le32 reserved4[5];
+	__le32 ctrl1;
+};
+
+/*
+ * Common registers interface for both version of DRD.
+ */
+struct cdns3_otg_common_regs {
+	__le32 cmd;
+	__le32 sts;
+	__le32 state;
+	__le32 different1;
+	__le32 ien;
+	__le32 ivect;
+};
+
+/* CDNS_RID - bitmasks */
+#define CDNS_RID(p)			((p) & GENMASK(15, 0))
+
+/* CDNS_VID - bitmasks */
+#define CDNS_DID(p)			((p) & GENMASK(31, 0))
+
+/* OTGCMD - bitmasks */
+/* "Request the bus for Device mode. */
+#define OTGCMD_DEV_BUS_REQ		BIT(0)
+/* Request the bus for Host mode */
+#define OTGCMD_HOST_BUS_REQ		BIT(1)
+/* Enable OTG mode. */
+#define OTGCMD_OTG_EN			BIT(2)
+/* Disable OTG mode */
+#define OTGCMD_OTG_DIS			BIT(3)
+/*"Configure OTG as A-Device. */
+#define OTGCMD_A_DEV_EN			BIT(4)
+/*"Configure OTG as A-Device. */
+#define OTGCMD_A_DEV_DIS		BIT(5)
+/* Drop the bus for Device mod	e. */
+#define OTGCMD_DEV_BUS_DROP		BIT(8)
+/* Drop the bus for Host mode*/
+#define OTGCMD_HOST_BUS_DROP		BIT(9)
+/* Power Down USBSS-DEV. */
+#define OTGCMD_DEV_POWER_OFF		BIT(11)
+/* Power Down CDNSXHCI. */
+#define OTGCMD_HOST_POWER_OFF		BIT(12)
+
+/* OTGIEN - bitmasks */
+/* ID change interrupt enable */
+#define OTGIEN_ID_CHANGE_INT		BIT(0)
+/* Vbusvalid fall detected interrupt enable.*/
+#define OTGIEN_VBUSVALID_RISE_INT	BIT(4)
+/* Vbusvalid fall detected interrupt enable */
+#define OTGIEN_VBUSVALID_FALL_INT	BIT(5)
+
+/* OTGSTS - bitmasks */
+/*
+ * Current value of the ID pin. It is only valid when idpullup in
+ *  OTGCTRL1_TYPE register is set to '1'.
+ */
+#define OTGSTS_ID_VALUE			BIT(0)
+/* Current value of the vbus_valid */
+#define OTGSTS_VBUS_VALID		BIT(1)
+/* Current value of the b_sess_vld */
+#define OTGSTS_SESSION_VALID		BIT(2)
+/*Device mode is active*/
+#define OTGSTS_DEV_ACTIVE		BIT(3)
+/* Host mode is active. */
+#define OTGSTS_HOST_ACTIVE		BIT(4)
+/* OTG Controller not ready. */
+#define OTGSTS_OTG_NRDY_MASK		BIT(11)
+#define OTGSTS_OTG_NRDY(p)		((p) & OTGSTS_OTG_NRDY_MASK)
+/*
+ * Value of the strap pins.
+ * 000 - no default configuration
+ * 010 - Controller initiall configured as Host
+ * 100 - Controller initially configured as Device
+ */
+#define OTGSTS_STRAP(p)			(((p) & GENMASK(14, 12)) >> 12)
+#define OTGSTS_STRAP_NO_DEFAULT_CFG	0x00
+#define OTGSTS_STRAP_HOST_OTG		0x01
+#define OTGSTS_STRAP_HOST		0x02
+#define OTGSTS_STRAP_GADGET		0x04
+/* Host mode is turned on. */
+#define OTGSTS_XHCI_READY		BIT(26)
+/* "Device mode is turned on .*/
+#define OTGSTS_DEV_READY		BIT(27)
+
+/* OTGSTATE- bitmasks */
+#define OTGSTATE_HOST_STATE_MASK	GENMASK(5, 3)
+#define OTGSTATE_HOST_STATE_IDLE	0x0
+#define OTGSTATE_HOST_STATE_VBUS_FALL   0x7
+#define OTGSTATE_HOST_STATE(p)		(((p) & OTGSTATE_HOST_STATE_MASK) >> 3)
+
+/* OTGREFCLK - bitmasks */
+#define OTGREFCLK_STB_CLK_SWITCH_EN	BIT(31)
+
+/* OVERRIDE - bitmasks */
+#define OVERRIDE_IDPULLUP		BIT(0)
+/* Only for CDNS3_CONTROLLER_V0 version */
+#define OVERRIDE_IDPULLUP_V0		BIT(24)
+
+int cdns3_is_host(struct cdns3 *cdns);
+int cdns3_is_device(struct cdns3 *cdns);
+int cdns3_get_id(struct cdns3 *cdns);
+int cdns3_drd_init(struct cdns3 *cdns);
+int cdns3_drd_exit(struct cdns3 *cdns);
+int cdns3_drd_update_mode(struct cdns3 *cdns);
+
+#endif /* __LINUX_CDNS3_DRD */
diff --git a/drivers/usb/cdns3/ep0.c b/drivers/usb/cdns3/ep0.c
new file mode 100644
index 000000000000..89cf1cde1555
--- /dev/null
+++ b/drivers/usb/cdns3/ep0.c
@@ -0,0 +1,896 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Cadence USBSS DRD Driver - gadget side.
+ *
+ * Copyright (C) 2018 Cadence Design Systems.
+ * Copyright (C) 2017-2018 NXP
+ *
+ * Authors: Pawel Jez <pjez@cadence.com>,
+ *          Pawel Laszczak <pawell@cadence.com>
+ *	    Peter Chen <peter.chen@nxp.com>
+ */
+
+#include <linux/usb/composite.h>
+
+#include "gadget.h"
+#include "trace.h"
+
+static struct usb_endpoint_descriptor cdns3_gadget_ep0_desc = {
+	.bLength = USB_DT_ENDPOINT_SIZE,
+	.bDescriptorType = USB_DT_ENDPOINT,
+	.bmAttributes =	USB_ENDPOINT_XFER_CONTROL,
+};
+
+/**
+ * cdns3_ep0_run_transfer - Do transfer on default endpoint hardware
+ * @priv_dev: extended gadget object
+ * @dma_addr: physical address where data is/will be stored
+ * @length: data length
+ * @erdy: set it to 1 when ERDY packet should be sent -
+ *        exit from flow control state
+ */
+static void cdns3_ep0_run_transfer(struct cdns3_device *priv_dev,
+				   dma_addr_t dma_addr,
+				   unsigned int length, int erdy)
+{
+	struct cdns3_usb_regs __iomem *regs = priv_dev->regs;
+	struct cdns3_endpoint *priv_ep = priv_dev->eps[0];
+
+	priv_ep->trb_pool->buffer = TRB_BUFFER(dma_addr);
+	priv_ep->trb_pool->length = TRB_LEN(length);
+	priv_ep->trb_pool->control = TRB_CYCLE | TRB_IOC | TRB_TYPE(TRB_NORMAL);
+
+	trace_cdns3_prepare_trb(priv_ep, priv_ep->trb_pool);
+
+	cdns3_select_ep(priv_dev, priv_dev->ep0_data_dir);
+
+	writel(EP_STS_TRBERR, &regs->ep_sts);
+	writel(EP_TRADDR_TRADDR(priv_ep->trb_pool_dma), &regs->ep_traddr);
+	trace_cdns3_doorbell_ep0(priv_dev->ep0_data_dir ? "ep0in" : "ep0out",
+				 readl(&regs->ep_traddr));
+
+	/* TRB should be prepared before starting transfer */
+	writel(EP_CMD_DRDY, &regs->ep_cmd);
+
+	if (erdy)
+		writel(EP_CMD_ERDY, &priv_dev->regs->ep_cmd);
+}
+
+/**
+ * cdns3_ep0_delegate_req - Returns status of handling setup packet
+ * Setup is handled by gadget driver
+ * @priv_dev: extended gadget object
+ * @ctrl_req: pointer to received setup packet
+ *
+ * Returns zero on success or negative value on failure
+ */
+static int cdns3_ep0_delegate_req(struct cdns3_device *priv_dev,
+				  struct usb_ctrlrequest *ctrl_req)
+{
+	int ret;
+
+	spin_unlock(&priv_dev->lock);
+	priv_dev->setup_pending = 1;
+	ret = priv_dev->gadget_driver->setup(&priv_dev->gadget, ctrl_req);
+	priv_dev->setup_pending = 0;
+	spin_lock(&priv_dev->lock);
+	return ret;
+}
+
+static void cdns3_prepare_setup_packet(struct cdns3_device *priv_dev)
+{
+	priv_dev->ep0_data_dir = 0;
+	priv_dev->ep0_stage = CDNS3_SETUP_STAGE;
+	cdns3_ep0_run_transfer(priv_dev, priv_dev->setup_dma,
+			       sizeof(struct usb_ctrlrequest), 0);
+}
+
+static void cdns3_ep0_complete_setup(struct cdns3_device *priv_dev,
+				     u8 send_stall, u8 send_erdy)
+{
+	struct cdns3_endpoint *priv_ep = priv_dev->eps[0];
+	struct usb_request *request;
+
+	request = cdns3_next_request(&priv_ep->pending_req_list);
+	if (request)
+		list_del_init(&request->list);
+
+	if (send_stall) {
+		dev_dbg(priv_dev->dev, "STALL for ep0\n");
+		/* set_stall on ep0 */
+		cdns3_select_ep(priv_dev, 0x00);
+		writel(EP_CMD_SSTALL, &priv_dev->regs->ep_cmd);
+	} else {
+		cdns3_prepare_setup_packet(priv_dev);
+	}
+
+	priv_dev->ep0_stage = CDNS3_SETUP_STAGE;
+	writel((send_erdy ? EP_CMD_ERDY : 0) | EP_CMD_REQ_CMPL,
+	       &priv_dev->regs->ep_cmd);
+}
+
+/**
+ * cdns3_req_ep0_set_configuration - Handling of SET_CONFIG standard USB request
+ * @priv_dev: extended gadget object
+ * @ctrl_req: pointer to received setup packet
+ *
+ * Returns 0 if success, USB_GADGET_DELAYED_STATUS on deferred status stage,
+ * error code on error
+ */
+static int cdns3_req_ep0_set_configuration(struct cdns3_device *priv_dev,
+					   struct usb_ctrlrequest *ctrl_req)
+{
+	enum usb_device_state device_state = priv_dev->gadget.state;
+	struct cdns3_endpoint *priv_ep;
+	u32 config = le16_to_cpu(ctrl_req->wValue);
+	int result = 0;
+	int i;
+
+	switch (device_state) {
+	case USB_STATE_ADDRESS:
+		/* Configure non-control EPs */
+		for (i = 0; i < CDNS3_ENDPOINTS_MAX_COUNT; i++) {
+			priv_ep = priv_dev->eps[i];
+			if (!priv_ep)
+				continue;
+
+			if (priv_ep->flags & EP_CLAIMED)
+				cdns3_ep_config(priv_ep);
+		}
+
+		result = cdns3_ep0_delegate_req(priv_dev, ctrl_req);
+
+		if (result)
+			return result;
+
+		if (config) {
+			cdns3_set_hw_configuration(priv_dev);
+		} else {
+			cdns3_hw_reset_eps_config(priv_dev);
+			usb_gadget_set_state(&priv_dev->gadget,
+					     USB_STATE_ADDRESS);
+		}
+		break;
+	case USB_STATE_CONFIGURED:
+		result = cdns3_ep0_delegate_req(priv_dev, ctrl_req);
+
+		if (!config && !result) {
+			cdns3_hw_reset_eps_config(priv_dev);
+			usb_gadget_set_state(&priv_dev->gadget,
+					     USB_STATE_ADDRESS);
+		}
+		break;
+	default:
+		result = -EINVAL;
+	}
+
+	return result;
+}
+
+/**
+ * cdns3_req_ep0_set_address - Handling of SET_ADDRESS standard USB request
+ * @priv_dev: extended gadget object
+ * @ctrl_req: pointer to received setup packet
+ *
+ * Returns 0 if success, error code on error
+ */
+static int cdns3_req_ep0_set_address(struct cdns3_device *priv_dev,
+				     struct usb_ctrlrequest *ctrl_req)
+{
+	enum usb_device_state device_state = priv_dev->gadget.state;
+	u32 reg;
+	u32 addr;
+
+	addr = le16_to_cpu(ctrl_req->wValue);
+
+	if (addr > USB_DEVICE_MAX_ADDRESS) {
+		dev_err(priv_dev->dev,
+			"Device address (%d) cannot be greater than %d\n",
+			addr, USB_DEVICE_MAX_ADDRESS);
+		return -EINVAL;
+	}
+
+	if (device_state == USB_STATE_CONFIGURED) {
+		dev_err(priv_dev->dev,
+			"can't set_address from configured state\n");
+		return -EINVAL;
+	}
+
+	reg = readl(&priv_dev->regs->usb_cmd);
+
+	writel(reg | USB_CMD_FADDR(addr) | USB_CMD_SET_ADDR,
+	       &priv_dev->regs->usb_cmd);
+
+	usb_gadget_set_state(&priv_dev->gadget,
+			     (addr ? USB_STATE_ADDRESS : USB_STATE_DEFAULT));
+
+	return 0;
+}
+
+/**
+ * cdns3_req_ep0_get_status - Handling of GET_STATUS standard USB request
+ * @priv_dev: extended gadget object
+ * @ctrl_req: pointer to received setup packet
+ *
+ * Returns 0 if success, error code on error
+ */
+static int cdns3_req_ep0_get_status(struct cdns3_device *priv_dev,
+				    struct usb_ctrlrequest *ctrl)
+{
+	__le16 *response_pkt;
+	u16 usb_status = 0;
+	u32 recip;
+	u32 reg;
+
+	recip = ctrl->bRequestType & USB_RECIP_MASK;
+
+	switch (recip) {
+	case USB_RECIP_DEVICE:
+		/* self powered */
+		if (priv_dev->is_selfpowered)
+			usb_status = BIT(USB_DEVICE_SELF_POWERED);
+
+		if (priv_dev->wake_up_flag)
+			usb_status |= BIT(USB_DEVICE_REMOTE_WAKEUP);
+
+		if (priv_dev->gadget.speed != USB_SPEED_SUPER)
+			break;
+
+		reg = readl(&priv_dev->regs->usb_sts);
+
+		if (priv_dev->u1_allowed)
+			usb_status |= BIT(USB_DEV_STAT_U1_ENABLED);
+
+		if (priv_dev->u2_allowed)
+			usb_status |= BIT(USB_DEV_STAT_U2_ENABLED);
+
+		break;
+	case USB_RECIP_INTERFACE:
+		return cdns3_ep0_delegate_req(priv_dev, ctrl);
+	case USB_RECIP_ENDPOINT:
+		/* check if endpoint is stalled */
+		cdns3_select_ep(priv_dev, ctrl->wIndex);
+		if (EP_STS_STALL(readl(&priv_dev->regs->ep_sts)))
+			usb_status =  BIT(USB_ENDPOINT_HALT);
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	response_pkt = (__le16 *)priv_dev->setup_buf;
+	*response_pkt = cpu_to_le16(usb_status);
+
+	cdns3_ep0_run_transfer(priv_dev, priv_dev->setup_dma,
+			       sizeof(*response_pkt), 1);
+	return 0;
+}
+
+static int cdns3_ep0_feature_handle_device(struct cdns3_device *priv_dev,
+					   struct usb_ctrlrequest *ctrl,
+					   int set)
+{
+	enum usb_device_state state;
+	enum usb_device_speed speed;
+	int ret = 0;
+	u32 wValue;
+	u32 wIndex;
+	u16 tmode;
+
+	wValue = le16_to_cpu(ctrl->wValue);
+	wIndex = le16_to_cpu(ctrl->wIndex);
+	state = priv_dev->gadget.state;
+	speed = priv_dev->gadget.speed;
+
+	switch (ctrl->wValue) {
+	case USB_DEVICE_REMOTE_WAKEUP:
+		priv_dev->wake_up_flag = !!set;
+		break;
+	case USB_DEVICE_U1_ENABLE:
+		if (state != USB_STATE_CONFIGURED || speed != USB_SPEED_SUPER)
+			return -EINVAL;
+
+		priv_dev->u1_allowed = !!set;
+		break;
+	case USB_DEVICE_U2_ENABLE:
+		if (state != USB_STATE_CONFIGURED || speed != USB_SPEED_SUPER)
+			return -EINVAL;
+
+		priv_dev->u2_allowed = !!set;
+		break;
+	case USB_DEVICE_LTM_ENABLE:
+		ret = -EINVAL;
+		break;
+	case USB_DEVICE_TEST_MODE:
+		if (state != USB_STATE_CONFIGURED || speed > USB_SPEED_HIGH)
+			return -EINVAL;
+
+		tmode = le16_to_cpu(ctrl->wIndex);
+
+		if (!set || (tmode & 0xff) != 0)
+			return -EINVAL;
+
+		switch (tmode >> 8) {
+		case TEST_J:
+		case TEST_K:
+		case TEST_SE0_NAK:
+		case TEST_PACKET:
+			cdns3_ep0_complete_setup(priv_dev, 0, 1);
+			/**
+			 *  Little delay to give the controller some time
+			 * for sending status stage.
+			 * This time should be less then 3ms.
+			 */
+			usleep_range(1000, 2000);
+			cdns3_set_register_bit(&priv_dev->regs->usb_cmd,
+					       USB_CMD_STMODE |
+					       USB_STS_TMODE_SEL(tmode - 1));
+			break;
+		default:
+			ret = -EINVAL;
+		}
+		break;
+	default:
+		ret = -EINVAL;
+	}
+
+	return ret;
+}
+
+static int cdns3_ep0_feature_handle_intf(struct cdns3_device *priv_dev,
+					 struct usb_ctrlrequest *ctrl,
+					 int set)
+{
+	u32 wValue;
+	int ret = 0;
+
+	wValue = le16_to_cpu(ctrl->wValue);
+
+	switch (wValue) {
+	case USB_INTRF_FUNC_SUSPEND:
+		break;
+	default:
+		ret = -EINVAL;
+	}
+
+	return ret;
+}
+
+static int cdns3_ep0_feature_handle_endpoint(struct cdns3_device *priv_dev,
+					     struct usb_ctrlrequest *ctrl,
+					     int set)
+{
+	struct cdns3_endpoint *priv_ep;
+	int ret = 0;
+	u8 index;
+
+	if (le16_to_cpu(ctrl->wValue) != USB_ENDPOINT_HALT)
+		return -EINVAL;
+
+	if (!(ctrl->wIndex & ~USB_DIR_IN))
+		return 0;
+
+	index = cdns3_ep_addr_to_index(ctrl->wIndex);
+	priv_ep = priv_dev->eps[index];
+
+	cdns3_select_ep(priv_dev, ctrl->wIndex);
+
+	if (set) {
+		writel(EP_CMD_SSTALL, &priv_dev->regs->ep_cmd);
+		priv_ep->flags |= EP_STALL;
+	} else {
+		struct usb_request *request;
+
+		if (priv_dev->eps[index]->flags & EP_WEDGE) {
+			cdns3_select_ep(priv_dev, 0x00);
+			return 0;
+		}
+
+		writel(EP_CMD_CSTALL | EP_CMD_EPRST, &priv_dev->regs->ep_cmd);
+
+		/* wait for EPRST cleared */
+		ret = cdns3_handshake(&priv_dev->regs->ep_cmd,
+				      EP_CMD_EPRST, 0, 100);
+		if (ret)
+			return -EINVAL;
+
+		priv_ep->flags &= ~EP_STALL;
+
+		request = cdns3_next_request(&priv_ep->pending_req_list);
+		if (request)
+			cdns3_ep_run_transfer(priv_ep, request);
+	}
+	return ret;
+}
+
+/**
+ * cdns3_req_ep0_handle_feature -
+ * Handling of GET/SET_FEATURE standard USB request
+ *
+ * @priv_dev: extended gadget object
+ * @ctrl_req: pointer to received setup packet
+ * @set: must be set to 1 for SET_FEATURE request
+ *
+ * Returns 0 if success, error code on error
+ */
+static int cdns3_req_ep0_handle_feature(struct cdns3_device *priv_dev,
+					struct usb_ctrlrequest *ctrl,
+					int set)
+{
+	int ret = 0;
+	u32 recip;
+
+	recip = ctrl->bRequestType & USB_RECIP_MASK;
+
+	switch (recip) {
+	case USB_RECIP_DEVICE:
+		ret = cdns3_ep0_feature_handle_device(priv_dev, ctrl, set);
+		break;
+	case USB_RECIP_INTERFACE:
+		ret = cdns3_ep0_feature_handle_intf(priv_dev, ctrl, set);
+		break;
+	case USB_RECIP_ENDPOINT:
+		ret = cdns3_ep0_feature_handle_endpoint(priv_dev, ctrl, set);
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	return ret;
+}
+
+/**
+ * cdns3_req_ep0_set_sel - Handling of SET_SEL standard USB request
+ * @priv_dev: extended gadget object
+ * @ctrl_req: pointer to received setup packet
+ *
+ * Returns 0 if success, error code on error
+ */
+static int cdns3_req_ep0_set_sel(struct cdns3_device *priv_dev,
+				 struct usb_ctrlrequest *ctrl_req)
+{
+	if (priv_dev->gadget.state < USB_STATE_ADDRESS)
+		return -EINVAL;
+
+	if (ctrl_req->wLength != 6) {
+		dev_err(priv_dev->dev, "Set SEL should be 6 bytes, got %d\n",
+			ctrl_req->wLength);
+		return -EINVAL;
+	}
+
+	cdns3_ep0_run_transfer(priv_dev, priv_dev->setup_dma, 6, 1);
+	return 0;
+}
+
+/**
+ * cdns3_req_ep0_set_isoch_delay -
+ * Handling of GET_ISOCH_DELAY standard USB request
+ * @priv_dev: extended gadget object
+ * @ctrl_req: pointer to received setup packet
+ *
+ * Returns 0 if success, error code on error
+ */
+static int cdns3_req_ep0_set_isoch_delay(struct cdns3_device *priv_dev,
+					 struct usb_ctrlrequest *ctrl_req)
+{
+	if (ctrl_req->wIndex || ctrl_req->wLength)
+		return -EINVAL;
+
+	priv_dev->isoch_delay = ctrl_req->wValue;
+
+	return 0;
+}
+
+/**
+ * cdns3_ep0_standard_request - Handling standard USB requests
+ * @priv_dev: extended gadget object
+ * @ctrl_req: pointer to received setup packet
+ *
+ * Returns 0 if success, error code on error
+ */
+static int cdns3_ep0_standard_request(struct cdns3_device *priv_dev,
+				      struct usb_ctrlrequest *ctrl_req)
+{
+	int ret;
+
+	switch (ctrl_req->bRequest) {
+	case USB_REQ_SET_ADDRESS:
+		ret = cdns3_req_ep0_set_address(priv_dev, ctrl_req);
+		break;
+	case USB_REQ_SET_CONFIGURATION:
+		ret = cdns3_req_ep0_set_configuration(priv_dev, ctrl_req);
+		break;
+	case USB_REQ_GET_STATUS:
+		ret = cdns3_req_ep0_get_status(priv_dev, ctrl_req);
+		break;
+	case USB_REQ_CLEAR_FEATURE:
+		ret = cdns3_req_ep0_handle_feature(priv_dev, ctrl_req, 0);
+		break;
+	case USB_REQ_SET_FEATURE:
+		ret = cdns3_req_ep0_handle_feature(priv_dev, ctrl_req, 1);
+		break;
+	case USB_REQ_SET_SEL:
+		ret = cdns3_req_ep0_set_sel(priv_dev, ctrl_req);
+		break;
+	case USB_REQ_SET_ISOCH_DELAY:
+		ret = cdns3_req_ep0_set_isoch_delay(priv_dev, ctrl_req);
+		break;
+	default:
+		ret = cdns3_ep0_delegate_req(priv_dev, ctrl_req);
+		break;
+	}
+
+	return ret;
+}
+
+static void __pending_setup_status_handler(struct cdns3_device *priv_dev)
+{
+	struct usb_request *request = priv_dev->pending_status_request;
+
+	if (priv_dev->status_completion_no_call && request &&
+	    request->complete) {
+		request->complete(&priv_dev->eps[0]->endpoint, request);
+		priv_dev->status_completion_no_call = 0;
+	}
+}
+
+void cdns3_pending_setup_status_handler(struct work_struct *work)
+{
+	struct cdns3_device *priv_dev = container_of(work, struct cdns3_device,
+			pending_status_wq);
+	unsigned long flags;
+
+	spin_lock_irqsave(&priv_dev->lock, flags);
+	__pending_setup_status_handler(priv_dev);
+	spin_unlock_irqrestore(&priv_dev->lock, flags);
+}
+
+/**
+ * cdns3_gadget_ep_giveback - call struct usb_request's ->complete callback
+ * @priv_ep: The endpoint to whom the request belongs to
+ * @priv_req: The request we're giving back
+ * @status: completion code for the request
+ *
+ * Must be called with controller's lock held and interrupts disabled. This
+ * function will unmap @req and call its ->complete() callback to notify upper
+ * layers that it has completed.
+ */
+
+void cdns3_gadget_ep0_giveback(struct cdns3_device *priv_dev,
+			       int status)
+{
+	struct cdns3_endpoint *priv_ep;
+	struct usb_request *request;
+
+	priv_ep = priv_dev->eps[0];
+	request = cdns3_next_request(&priv_ep->pending_req_list);
+
+	priv_ep->dir = priv_dev->ep0_data_dir;
+	cdns3_gadget_giveback(priv_ep, to_cdns3_request(request), status);
+}
+
+/**
+ * cdns3_ep0_setup_phase - Handling setup USB requests
+ * @priv_dev: extended gadget object
+ */
+static void cdns3_ep0_setup_phase(struct cdns3_device *priv_dev)
+{
+	struct usb_ctrlrequest *ctrl = priv_dev->setup_buf;
+	struct cdns3_endpoint *priv_ep = priv_dev->eps[0];
+	int result;
+
+	priv_dev->ep0_data_dir = ctrl->bRequestType & USB_DIR_IN;
+
+	trace_cdns3_ctrl_req(ctrl);
+
+	if (!list_empty(&priv_ep->pending_req_list)) {
+		struct usb_request *request;
+
+		request = cdns3_next_request(&priv_ep->pending_req_list);
+		priv_ep->dir = priv_dev->ep0_data_dir;
+		cdns3_gadget_giveback(priv_ep, to_cdns3_request(request),
+				      -ECONNRESET);
+	}
+
+	if (le16_to_cpu(ctrl->wLength))
+		priv_dev->ep0_stage = CDNS3_DATA_STAGE;
+	else
+		priv_dev->ep0_stage = CDNS3_STATUS_STAGE;
+
+	if ((ctrl->bRequestType & USB_TYPE_MASK) == USB_TYPE_STANDARD)
+		result = cdns3_ep0_standard_request(priv_dev, ctrl);
+	else
+		result = cdns3_ep0_delegate_req(priv_dev, ctrl);
+
+	if (priv_dev->ep0_stage == CDNS3_STATUS_STAGE &&
+	    result != USB_GADGET_DELAYED_STATUS) {
+		cdns3_ep0_complete_setup(priv_dev, 0, 1);
+	} else if (result < 0) {
+		cdns3_ep0_complete_setup(priv_dev, 1, 1);
+	}
+}
+
+static void cdns3_transfer_completed(struct cdns3_device *priv_dev)
+{
+	struct cdns3_endpoint *priv_ep = priv_dev->eps[0];
+
+	if (!list_empty(&priv_ep->pending_req_list)) {
+		struct usb_request *request;
+
+		trace_cdns3_complete_trb(priv_ep, priv_ep->trb_pool);
+		request = cdns3_next_request(&priv_ep->pending_req_list);
+
+		request->actual =
+			TRB_LEN(le32_to_cpu(priv_ep->trb_pool->length));
+
+		priv_ep->dir = priv_dev->ep0_data_dir;
+		cdns3_gadget_giveback(priv_ep, to_cdns3_request(request), 0);
+	}
+
+	cdns3_ep0_complete_setup(priv_dev, 0, 0);
+}
+
+/**
+ * cdns3_check_new_setup - Check if controller receive new SETUP packet.
+ * @priv_dev: extended gadget object
+ *
+ * The SETUP packet can be kept in on-chip memory or in system memory.
+ */
+static bool cdns3_check_new_setup(struct cdns3_device *priv_dev)
+{
+	u32 ep_sts_reg;
+
+	cdns3_select_ep(priv_dev, 0 | USB_DIR_OUT);
+	ep_sts_reg = readl(&priv_dev->regs->ep_sts);
+
+	return !!(ep_sts_reg & (EP_STS_SETUP | EP_STS_STPWAIT));
+}
+
+/**
+ * cdns3_check_ep0_interrupt_proceed - Processes interrupt related to endpoint 0
+ * @priv_dev: extended gadget object
+ * @dir: USB_DIR_IN for IN direction, USB_DIR_OUT for OUT direction
+ */
+void cdns3_check_ep0_interrupt_proceed(struct cdns3_device *priv_dev, int dir)
+{
+	u32 ep_sts_reg;
+
+	cdns3_select_ep(priv_dev, dir);
+
+	ep_sts_reg = readl(&priv_dev->regs->ep_sts);
+	writel(ep_sts_reg, &priv_dev->regs->ep_sts);
+
+	trace_cdns3_ep0_irq(priv_dev, ep_sts_reg);
+
+	__pending_setup_status_handler(priv_dev);
+
+	if ((ep_sts_reg & EP_STS_SETUP)) {
+		cdns3_ep0_setup_phase(priv_dev);
+	} else if ((ep_sts_reg & EP_STS_IOC) || (ep_sts_reg & EP_STS_ISP)) {
+		priv_dev->ep0_data_dir = dir;
+		cdns3_transfer_completed(priv_dev);
+	}
+
+	if (ep_sts_reg & EP_STS_DESCMIS) {
+		if (dir == 0 && !priv_dev->setup_pending)
+			cdns3_prepare_setup_packet(priv_dev);
+	}
+}
+
+/**
+ * cdns3_gadget_ep0_enable
+ * Function shouldn't be called by gadget driver,
+ * endpoint 0 is allways active
+ */
+static int cdns3_gadget_ep0_enable(struct usb_ep *ep,
+				   const struct usb_endpoint_descriptor *desc)
+{
+	return -EINVAL;
+}
+
+/**
+ * cdns3_gadget_ep0_disable
+ * Function shouldn't be called by gadget driver,
+ * endpoint 0 is allways active
+ */
+static int cdns3_gadget_ep0_disable(struct usb_ep *ep)
+{
+	return -EINVAL;
+}
+
+/**
+ * cdns3_gadget_ep0_set_halt
+ * @ep: pointer to endpoint zero object
+ * @value: 1 for set stall, 0 for clear stall
+ *
+ * Returns 0
+ */
+static int cdns3_gadget_ep0_set_halt(struct usb_ep *ep, int value)
+{
+	/* TODO */
+	return 0;
+}
+
+/**
+ * cdns3_gadget_ep0_queue Transfer data on endpoint zero
+ * @ep: pointer to endpoint zero object
+ * @request: pointer to request object
+ * @gfp_flags: gfp flags
+ *
+ * Returns 0 on success, error code elsewhere
+ */
+static int cdns3_gadget_ep0_queue(struct usb_ep *ep,
+				  struct usb_request *request,
+				  gfp_t gfp_flags)
+{
+	struct cdns3_endpoint *priv_ep = ep_to_cdns3_ep(ep);
+	struct cdns3_device *priv_dev = priv_ep->cdns3_dev;
+	unsigned long flags;
+	int erdy_sent = 0;
+	int ret = 0;
+
+	dev_dbg(priv_dev->dev, "Queue to Ep0%s L: %d\n",
+		priv_dev->ep0_data_dir ? "IN" : "OUT",
+		request->length);
+
+	/* cancel the request if controller receive new SETUP packet. */
+	if (cdns3_check_new_setup(priv_dev))
+		return -ECONNRESET;
+
+	/* send STATUS stage. Should be called only for SET_CONFIGURATION */
+	if (priv_dev->ep0_stage == CDNS3_STATUS_STAGE) {
+		spin_lock_irqsave(&priv_dev->lock, flags);
+		cdns3_select_ep(priv_dev, 0x00);
+
+		erdy_sent = !priv_dev->hw_configured_flag;
+		cdns3_set_hw_configuration(priv_dev);
+
+		if (!erdy_sent)
+			cdns3_ep0_complete_setup(priv_dev, 0, 1);
+
+		request->actual = 0;
+		priv_dev->status_completion_no_call = true;
+		priv_dev->pending_status_request = request;
+		spin_unlock_irqrestore(&priv_dev->lock, flags);
+
+		/*
+		 * Since there is no completion interrupt for status stage,
+		 * it needs to call ->completion in software after
+		 * ep0_queue is back.
+		 */
+		queue_work(system_freezable_wq, &priv_dev->pending_status_wq);
+		return 0;
+	}
+
+	spin_lock_irqsave(&priv_dev->lock, flags);
+	if (!list_empty(&priv_ep->pending_req_list)) {
+		dev_err(priv_dev->dev,
+			"can't handle multiple requests for ep0\n");
+		spin_unlock_irqrestore(&priv_dev->lock, flags);
+		return -EBUSY;
+	}
+
+	ret = usb_gadget_map_request_by_dev(priv_dev->sysdev, request,
+					    priv_dev->ep0_data_dir);
+	if (ret) {
+		spin_unlock_irqrestore(&priv_dev->lock, flags);
+		dev_err(priv_dev->dev, "failed to map request\n");
+		return -EINVAL;
+	}
+
+	request->status = -EINPROGRESS;
+	list_add_tail(&request->list, &priv_ep->pending_req_list);
+	cdns3_ep0_run_transfer(priv_dev, request->dma, request->length, 1);
+	spin_unlock_irqrestore(&priv_dev->lock, flags);
+
+	return ret;
+}
+
+/**
+ * cdns3_gadget_ep_set_wedge Set wedge on selected endpoint
+ * @ep: endpoint object
+ *
+ * Returns 0
+ */
+int cdns3_gadget_ep_set_wedge(struct usb_ep *ep)
+{
+	struct cdns3_endpoint *priv_ep = ep_to_cdns3_ep(ep);
+	struct cdns3_device *priv_dev = priv_ep->cdns3_dev;
+
+	dev_dbg(priv_dev->dev, "Wedge for %s\n", ep->name);
+	cdns3_gadget_ep_set_halt(ep, 1);
+	priv_ep->flags |= EP_WEDGE;
+
+	return 0;
+}
+
+const struct usb_ep_ops cdns3_gadget_ep0_ops = {
+	.enable = cdns3_gadget_ep0_enable,
+	.disable = cdns3_gadget_ep0_disable,
+	.alloc_request = cdns3_gadget_ep_alloc_request,
+	.free_request = cdns3_gadget_ep_free_request,
+	.queue = cdns3_gadget_ep0_queue,
+	.dequeue = cdns3_gadget_ep_dequeue,
+	.set_halt = cdns3_gadget_ep0_set_halt,
+	.set_wedge = cdns3_gadget_ep_set_wedge,
+};
+
+/**
+ * cdns3_ep0_config - Configures default endpoint
+ * @priv_dev: extended gadget object
+ *
+ * Functions sets parameters: maximal packet size and enables interrupts
+ */
+void cdns3_ep0_config(struct cdns3_device *priv_dev)
+{
+	struct cdns3_usb_regs __iomem *regs;
+	struct cdns3_endpoint *priv_ep;
+	u32 max_packet_size = 64;
+
+	regs = priv_dev->regs;
+
+	if (priv_dev->gadget.speed == USB_SPEED_SUPER)
+		max_packet_size = 512;
+
+	priv_ep = priv_dev->eps[0];
+
+	if (!list_empty(&priv_ep->pending_req_list)) {
+		struct usb_request *request;
+
+		request = cdns3_next_request(&priv_ep->pending_req_list);
+		list_del_init(&request->list);
+	}
+
+	priv_dev->u1_allowed = 0;
+	priv_dev->u2_allowed = 0;
+
+	priv_dev->gadget.ep0->maxpacket = max_packet_size;
+	cdns3_gadget_ep0_desc.wMaxPacketSize = cpu_to_le16(max_packet_size);
+
+	/* init ep out */
+	cdns3_select_ep(priv_dev, USB_DIR_OUT);
+
+	writel(EP_CFG_ENABLE | EP_CFG_MAXPKTSIZE(max_packet_size),
+	       &regs->ep_cfg);
+
+	writel(EP_STS_EN_SETUPEN | EP_STS_EN_DESCMISEN | EP_STS_EN_TRBERREN,
+	       &regs->ep_sts_en);
+
+	/* init ep in */
+	cdns3_select_ep(priv_dev, USB_DIR_IN);
+
+	writel(EP_CFG_ENABLE | EP_CFG_MAXPKTSIZE(max_packet_size),
+	       &regs->ep_cfg);
+
+	writel(EP_STS_EN_SETUPEN | EP_STS_EN_TRBERREN, &regs->ep_sts_en);
+
+	cdns3_set_register_bit(&regs->usb_conf, USB_CONF_U1DS | USB_CONF_U2DS);
+}
+
+/**
+ * cdns3_init_ep0 Initializes software endpoint 0 of gadget
+ * @priv_dev: extended gadget object
+ * @ep_priv: extended endpoint object
+ *
+ * Returns 0 on success else error code.
+ */
+int cdns3_init_ep0(struct cdns3_device *priv_dev,
+		   struct cdns3_endpoint *priv_ep)
+{
+	sprintf(priv_ep->name, "ep0");
+
+	/* fill linux fields */
+	priv_ep->endpoint.ops = &cdns3_gadget_ep0_ops;
+	priv_ep->endpoint.maxburst = 1;
+	usb_ep_set_maxpacket_limit(&priv_ep->endpoint,
+				   CDNS3_EP0_MAX_PACKET_LIMIT);
+	priv_ep->endpoint.address = 0;
+	priv_ep->endpoint.caps.type_control = 1;
+	priv_ep->endpoint.caps.dir_in = 1;
+	priv_ep->endpoint.caps.dir_out = 1;
+	priv_ep->endpoint.name = priv_ep->name;
+	priv_ep->endpoint.desc = &cdns3_gadget_ep0_desc;
+	priv_dev->gadget.ep0 = &priv_ep->endpoint;
+	priv_ep->type = USB_ENDPOINT_XFER_CONTROL;
+
+	return cdns3_allocate_trb_pool(priv_ep);
+}
diff --git a/drivers/usb/cdns3/gadget-export.h b/drivers/usb/cdns3/gadget-export.h
new file mode 100644
index 000000000000..577469eee961
--- /dev/null
+++ b/drivers/usb/cdns3/gadget-export.h
@@ -0,0 +1,28 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Cadence USBSS DRD Driver - Gadget Export APIs.
+ *
+ * Copyright (C) 2017 NXP
+ * Copyright (C) 2017-2018 NXP
+ *
+ * Authors: Peter Chen <peter.chen@nxp.com>
+ */
+#ifndef __LINUX_CDNS3_GADGET_EXPORT
+#define __LINUX_CDNS3_GADGET_EXPORT
+
+#ifdef CONFIG_USB_CDNS3_GADGET
+
+int cdns3_gadget_init(struct cdns3 *cdns);
+void cdns3_gadget_exit(struct cdns3 *cdns);
+#else
+
+static inline int cdns3_gadget_init(struct cdns3 *cdns)
+{
+	return -ENXIO;
+}
+
+static inline void cdns3_gadget_exit(struct cdns3 *cdns) { }
+
+#endif
+
+#endif /* __LINUX_CDNS3_GADGET_EXPORT */
diff --git a/drivers/usb/cdns3/gadget.c b/drivers/usb/cdns3/gadget.c
new file mode 100644
index 000000000000..0d95eb00be37
--- /dev/null
+++ b/drivers/usb/cdns3/gadget.c
@@ -0,0 +1,2102 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Cadence USBSS DRD Driver - gadget side.
+ *
+ * Copyright (C) 2018 Cadence Design Systems.
+ * Copyright (C) 2017-2018 NXP
+ *
+ * Authors: Pawel Jez <pjez@cadence.com>,
+ *          Pawel Laszczak <pawell@cadence.com>
+ *	    Peter Chen <peter.chen@nxp.com>
+ */
+
+#include <linux/dma-mapping.h>
+#include <linux/usb/gadget.h>
+
+#include "core.h"
+#include "gadget-export.h"
+#include "gadget.h"
+
+#include "trace.h"
+
+static int __cdns3_gadget_ep_queue(struct usb_ep *ep,
+				   struct usb_request *request,
+				   gfp_t gfp_flags);
+
+/**
+ * cdns3_handshake - spin reading  until handshake completes or fails
+ * @ptr: address of device controller register to be read
+ * @mask: bits to look at in result of read
+ * @done: value of those bits when handshake succeeds
+ * @usec: timeout in microseconds
+ *
+ * Returns negative errno, or zero on success
+ *
+ * Success happens when the "mask" bits have the specified value (hardware
+ * handshake done). There are two failure modes: "usec" have passed (major
+ * hardware flakeout), or the register reads as all-ones (hardware removed).
+ */
+int cdns3_handshake(void __iomem *ptr, u32 mask, u32 done, int usec)
+{
+	u32	result;
+
+	do {
+		result = readl(ptr);
+		if (result == ~(u32)0)	/* card removed */
+			return -ENODEV;
+		result &= mask;
+		if (result == done)
+			return 0;
+		udelay(1);
+		usec--;
+	} while (usec > 0);
+	return -ETIMEDOUT;
+}
+
+/**
+ * cdns3_set_register_bit - set bit in given register.
+ * @ptr: address of device controller register to be read and changed
+ * @mask: bits requested to set
+ */
+void cdns3_set_register_bit(void __iomem *ptr, u32 mask)
+{
+	mask = readl(ptr) | mask;
+	writel(mask, ptr);
+}
+
+/**
+ * cdns3_ep_addr_to_index - Macro converts endpoint address to
+ * index of endpoint object in cdns3_device.eps[] container
+ * @ep_addr: endpoint address for which endpoint object is required
+ *
+ */
+u8 cdns3_ep_addr_to_index(u8 ep_addr)
+{
+	return (((ep_addr & 0x7F)) + ((ep_addr & USB_DIR_IN) ? 16 : 0));
+}
+
+/**
+ * cdns3_next_request - returns next request from list
+ * @list: list containing requests
+ *
+ * Returns request or NULL if no requests in list
+ */
+struct usb_request *cdns3_next_request(struct list_head *list)
+{
+	if (list_empty(list))
+		return NULL;
+	return list_first_entry_or_null(list, struct usb_request, list);
+}
+
+/**
+ * cdns3_next_priv_request - returns next request from list
+ * @list: list containing requests
+ *
+ * Returns request or NULL if no requests in list
+ */
+struct cdns3_request *cdns3_next_priv_request(struct list_head *list)
+{
+	if (list_empty(list))
+		return NULL;
+	return list_first_entry_or_null(list, struct cdns3_request, list);
+}
+
+/**
+ * select_ep - selects endpoint
+ * @priv_dev:  extended gadget object
+ * @ep: endpoint address
+ */
+void cdns3_select_ep(struct cdns3_device *priv_dev, u32 ep)
+{
+	if (priv_dev->selected_ep == ep)
+		return;
+
+	priv_dev->selected_ep = ep;
+	writel(ep, &priv_dev->regs->ep_sel);
+}
+
+dma_addr_t cdns3_trb_virt_to_dma(struct cdns3_endpoint *priv_ep,
+				 struct cdns3_trb *trb)
+{
+	u32 offset = (char *)trb - (char *)priv_ep->trb_pool;
+
+	return priv_ep->trb_pool_dma + offset;
+}
+
+int cdns3_ring_size(struct cdns3_endpoint *priv_ep)
+{
+	switch (priv_ep->type) {
+	case USB_ENDPOINT_XFER_ISOC:
+		return TRB_ISO_RING_SIZE;
+	case USB_ENDPOINT_XFER_CONTROL:
+		return TRB_CTRL_RING_SIZE;
+	default:
+		return TRB_RING_SIZE;
+	}
+}
+
+/**
+ * cdns3_allocate_trb_pool - Allocates TRB's pool for selected endpoint
+ * @priv_ep:  endpoint object
+ *
+ * Function will return 0 on success or -ENOMEM on allocation error
+ */
+int cdns3_allocate_trb_pool(struct cdns3_endpoint *priv_ep)
+{
+	struct cdns3_device *priv_dev = priv_ep->cdns3_dev;
+	int ring_size = cdns3_ring_size(priv_ep);
+	struct cdns3_trb *link_trb;
+
+	if (!priv_ep->trb_pool) {
+		priv_ep->trb_pool = dma_zalloc_coherent(priv_dev->sysdev,
+							ring_size,
+							&priv_ep->trb_pool_dma,
+							GFP_DMA);
+		if (!priv_ep->trb_pool)
+			return -ENOMEM;
+	} else {
+		memset(priv_ep->trb_pool, 0, ring_size);
+	}
+
+	if (!priv_ep->num)
+		return 0;
+
+	if (!priv_ep->aligned_buff) {
+		void *buff = dma_alloc_coherent(priv_dev->sysdev,
+						CDNS3_ALIGNED_BUF_SIZE,
+						&priv_ep->aligned_dma_addr,
+						GFP_DMA);
+
+		priv_ep->aligned_buff  = buff;
+		if (!priv_ep->aligned_buff) {
+			dma_free_coherent(priv_dev->sysdev,
+					  ring_size,
+					  priv_ep->trb_pool,
+					  priv_ep->trb_pool_dma);
+			priv_ep->trb_pool = NULL;
+
+			return -ENOMEM;
+		}
+	}
+
+	priv_ep->num_trbs = ring_size / TRB_SIZE;
+	/* Initialize the last TRB as Link TRB */
+	link_trb = (priv_ep->trb_pool + (priv_ep->num_trbs - 1));
+	link_trb->buffer = TRB_BUFFER(priv_ep->trb_pool_dma);
+	link_trb->control = TRB_CYCLE | TRB_TYPE(TRB_LINK) |
+			    TRB_CHAIN | TRB_TOGGLE;
+
+	return 0;
+}
+
+static void cdns3_free_trb_pool(struct cdns3_endpoint *priv_ep)
+{
+	struct cdns3_device *priv_dev = priv_ep->cdns3_dev;
+
+	if (priv_ep->trb_pool) {
+		dma_free_coherent(priv_dev->sysdev,
+				  cdns3_ring_size(priv_ep),
+				  priv_ep->trb_pool, priv_ep->trb_pool_dma);
+		priv_ep->trb_pool = NULL;
+	}
+
+	if (priv_ep->aligned_buff) {
+		dma_free_coherent(priv_dev->sysdev, CDNS3_ALIGNED_BUF_SIZE,
+				  priv_ep->aligned_buff,
+				  priv_ep->aligned_dma_addr);
+		priv_ep->aligned_buff = NULL;
+	}
+}
+
+/**
+ * cdns3_data_flush - flush data at onchip buffer
+ * @priv_ep: endpoint object
+ *
+ * Endpoint must be selected before call to this function
+ *
+ * Returns zero on success or negative value on failure
+ */
+static int cdns3_data_flush(struct cdns3_endpoint *priv_ep)
+{
+	struct cdns3_device *priv_dev = priv_ep->cdns3_dev;
+
+	writel(EP_CMD_DFLUSH, &priv_dev->regs->ep_cmd);
+
+	/* wait for DFLUSH cleared */
+	return cdns3_handshake(&priv_dev->regs->ep_cmd, EP_CMD_DFLUSH, 0, 100);
+}
+
+/**
+ * cdns3_ep_stall_flush - Stalls and flushes selected endpoint
+ * @priv_ep: endpoint object
+ *
+ * Endpoint must be selected before call to this function
+ */
+static void cdns3_ep_stall_flush(struct cdns3_endpoint *priv_ep)
+{
+	struct cdns3_device *priv_dev = priv_ep->cdns3_dev;
+
+	writel(EP_CMD_DFLUSH | EP_CMD_ERDY | EP_CMD_SSTALL,
+	       &priv_dev->regs->ep_cmd);
+
+	/* wait for DFLUSH cleared */
+	cdns3_handshake(&priv_dev->regs->ep_cmd, EP_CMD_DFLUSH, 0, 100);
+	priv_ep->flags |= EP_STALL;
+}
+
+/**
+ * cdns3_hw_reset_eps_config - reset endpoints configuration kept by controller.
+ * @priv_dev: extended gadget object
+ */
+void cdns3_hw_reset_eps_config(struct cdns3_device *priv_dev)
+{
+	writel(USB_CONF_CFGRST, &priv_dev->regs->usb_conf);
+
+	cdns3_allow_enable_l1(priv_dev, 0);
+	priv_dev->hw_configured_flag = 0;
+	priv_dev->onchip_mem_allocated_size = 0;
+}
+
+/**
+ * cdns3_ep_inc_trb - increment a trb index.
+ * @index: Pointer to the TRB index to increment.
+ * @cs: Cycle state
+ * @trb_in_seg: number of TRBs in segment
+ *
+ * The index should never point to the link TRB. After incrementing,
+ * if it is point to the link TRB, wrap around to the beginning and revert
+ * cycle state bit The
+ * link TRB is always at the last TRB entry.
+ */
+static void cdns3_ep_inc_trb(int *index, u8 *cs, int trb_in_seg)
+{
+	(*index)++;
+	if (*index == (trb_in_seg - 1)) {
+		*index = 0;
+		*cs ^=  1;
+	}
+}
+
+/**
+ * cdns3_ep_inc_enq - increment endpoint's enqueue pointer
+ * @priv_ep: The endpoint whose enqueue pointer we're incrementing
+ */
+static void cdns3_ep_inc_enq(struct cdns3_endpoint *priv_ep)
+{
+	priv_ep->free_trbs--;
+	cdns3_ep_inc_trb(&priv_ep->enqueue, &priv_ep->pcs, priv_ep->num_trbs);
+}
+
+/**
+ * cdns3_ep_inc_deq - increment endpoint's dequeue pointer
+ * @priv_ep: The endpoint whose dequeue pointer we're incrementing
+ */
+static void cdns3_ep_inc_deq(struct cdns3_endpoint *priv_ep)
+{
+	priv_ep->free_trbs++;
+	cdns3_ep_inc_trb(&priv_ep->dequeue, &priv_ep->ccs, priv_ep->num_trbs);
+}
+
+/**
+ * cdns3_allow_enable_l1 - enable/disable permits to transition to L1.
+ * @priv_dev: Extended gadget object
+ * @enable: Enable/disable permit to transition to L1.
+ *
+ * If bit USB_CONF_L1EN is set and device receive Extended Token packet,
+ * then controller answer with ACK handshake.
+ * If bit USB_CONF_L1DS is set and device receive Extended Token packet,
+ * then controller answer with NYET handshake.
+ */
+void cdns3_allow_enable_l1(struct cdns3_device *priv_dev, int enable)
+{
+	if (enable)
+		writel(USB_CONF_L1EN, &priv_dev->regs->usb_conf);
+	else
+		writel(USB_CONF_L1DS, &priv_dev->regs->usb_conf);
+}
+
+enum usb_device_speed cdns3_get_speed(struct cdns3_device *priv_dev)
+{
+	u32 reg;
+
+	reg = readl(&priv_dev->regs->usb_sts);
+
+	if (DEV_SUPERSPEED(reg))
+		return USB_SPEED_SUPER;
+	else if (DEV_HIGHSPEED(reg))
+		return USB_SPEED_HIGH;
+	else if (DEV_FULLSPEED(reg))
+		return USB_SPEED_FULL;
+	else if (DEV_LOWSPEED(reg))
+		return USB_SPEED_LOW;
+	return USB_SPEED_UNKNOWN;
+}
+
+/**
+ * cdns3_start_all_request - add to ring all request not started
+ * @priv_dev: Extended gadget object
+ * @priv_ep: The endpoint for whom request will be started.
+ *
+ * Returns return ENOMEM if transfer ring i not enough TRBs to start
+ *         all requests.
+ */
+static int cdns3_start_all_request(struct cdns3_device *priv_dev,
+				   struct cdns3_endpoint *priv_ep)
+{
+	struct cdns3_request *priv_req;
+	struct usb_request *request;
+	int ret = 0;
+
+	while (!list_empty(&priv_ep->deferred_req_list)) {
+		request = cdns3_next_request(&priv_ep->deferred_req_list);
+		priv_req = to_cdns3_request(request);
+
+		ret = cdns3_ep_run_transfer(priv_ep, request);
+		if (ret)
+			return ret;
+
+		list_del(&request->list);
+		list_add_tail(&request->list,
+			      &priv_ep->pending_req_list);
+	}
+
+	priv_ep->flags &= ~EP_RING_FULL;
+	return ret;
+}
+
+/**
+ * cdns3_descmiss_copy_data copy data from internal requests to request queued
+ * by class driver.
+ * @priv_ep: extended endpoint object
+ * @request: request object
+ */
+static void cdns3_descmiss_copy_data(struct cdns3_endpoint *priv_ep,
+				     struct usb_request *request)
+{
+	struct usb_request *descmiss_req;
+	struct cdns3_request *descmiss_priv_req;
+
+	while (!list_empty(&priv_ep->descmiss_req_list)) {
+		int chunk_end;
+		int length;
+
+		descmiss_priv_req =
+			cdns3_next_priv_request(&priv_ep->descmiss_req_list);
+		descmiss_req = &descmiss_priv_req->request;
+
+		/* driver can't touch pending request */
+		if (descmiss_priv_req->flags & REQUEST_PENDING)
+			break;
+
+		chunk_end = descmiss_priv_req->flags & REQUEST_INTERNAL_CH;
+		length = request->actual + descmiss_req->actual;
+
+		if (length <= request->length) {
+			memcpy(&((u8 *)request->buf)[request->actual],
+			       descmiss_req->buf,
+			       descmiss_req->actual);
+			request->actual = length;
+		} else {
+			/* It should never occures */
+			request->status = -ENOMEM;
+		}
+
+		list_del_init(&descmiss_priv_req->list);
+
+		kfree(descmiss_req->buf);
+		cdns3_gadget_ep_free_request(&priv_ep->endpoint, descmiss_req);
+
+		if (!chunk_end)
+			break;
+	}
+}
+
+/**
+ * cdns3_gadget_giveback - call struct usb_request's ->complete callback
+ * @priv_ep: The endpoint to whom the request belongs to
+ * @priv_req: The request we're giving back
+ * @status: completion code for the request
+ *
+ * Must be called with controller's lock held and interrupts disabled. This
+ * function will unmap @req and call its ->complete() callback to notify upper
+ * layers that it has completed.
+ */
+void cdns3_gadget_giveback(struct cdns3_endpoint *priv_ep,
+			   struct cdns3_request *priv_req,
+			   int status)
+{
+	struct cdns3_device *priv_dev = priv_ep->cdns3_dev;
+	struct usb_request *request = &priv_req->request;
+
+	list_del_init(&request->list);
+
+	if (request->status == -EINPROGRESS)
+		request->status = status;
+
+	usb_gadget_unmap_request_by_dev(priv_dev->sysdev, request,
+					priv_ep->dir);
+
+	priv_req->flags &= ~REQUEST_PENDING;
+	trace_cdns3_gadget_giveback(priv_req);
+
+	/* WA1: */
+	if (priv_ep->flags & EP_QUIRK_EXTRA_BUF_EN &&
+	    priv_req->flags & REQUEST_INTERNAL) {
+		struct usb_request *req;
+
+		req = cdns3_next_request(&priv_ep->deferred_req_list);
+		request = req;
+		priv_ep->descmis_req = NULL;
+
+		if (!req)
+			return;
+
+		cdns3_descmiss_copy_data(priv_ep, req);
+		if (!(priv_ep->flags & EP_QUIRK_END_TRANSFER) &&
+		    req->length != req->actual) {
+			/* wait for next part of transfer */
+			return;
+		}
+
+		if (req->status == -EINPROGRESS)
+			req->status = 0;
+
+		list_del_init(&req->list);
+		cdns3_start_all_request(priv_dev, priv_ep);
+	}
+
+	/* Start all not pending request */
+	if (priv_ep->flags & EP_RING_FULL)
+		cdns3_start_all_request(priv_dev, priv_ep);
+
+	if (request->complete) {
+		spin_unlock(&priv_dev->lock);
+		usb_gadget_giveback_request(&priv_ep->endpoint,
+					    request);
+		spin_lock(&priv_dev->lock);
+	}
+
+	if (request->buf == priv_dev->zlp_buf)
+		cdns3_gadget_ep_free_request(&priv_ep->endpoint, request);
+}
+
+/**
+ * cdns3_ep_run_transfer - start transfer on no-default endpoint hardware
+ * @priv_ep: endpoint object
+ *
+ * Returns zero on success or negative value on failure
+ */
+int cdns3_ep_run_transfer(struct cdns3_endpoint *priv_ep,
+			  struct usb_request *request)
+{
+	struct cdns3_device *priv_dev = priv_ep->cdns3_dev;
+	struct cdns3_request *priv_req;
+	struct cdns3_trb *trb;
+	dma_addr_t trb_dma;
+	int sg_iter = 0;
+	u32 first_pcs;
+	int  num_trb;
+	int address;
+	int pcs;
+
+	if (priv_ep->type == USB_ENDPOINT_XFER_ISOC)
+		num_trb = priv_ep->interval;
+	else
+		num_trb = request->num_sgs ? request->num_sgs : 1;
+
+	if (num_trb > priv_ep->free_trbs) {
+		priv_ep->flags |= EP_RING_FULL;
+		return -ENOBUFS;
+	}
+
+	priv_req = to_cdns3_request(request);
+	address = priv_ep->endpoint.desc->bEndpointAddress;
+
+	priv_ep->flags |= EP_PENDING_REQUEST;
+	trb_dma = request->dma;
+
+	/* must allocate buffer aligned to 8 */
+	if ((request->dma % 8)) {
+		if (request->length <= CDNS3_ALIGNED_BUF_SIZE) {
+			memcpy(priv_ep->aligned_buff, request->buf,
+			       request->length);
+			trb_dma = priv_ep->aligned_dma_addr;
+		} else {
+			return -ENOMEM;
+		}
+	}
+
+	trb = priv_ep->trb_pool + priv_ep->enqueue;
+	priv_req->start_trb = priv_ep->enqueue;
+	priv_req->trb = trb;
+
+	/* prepare ring */
+	if ((priv_ep->enqueue + num_trb)  >= (priv_ep->num_trbs - 1)) {
+		/*updating C bt in  Link TRB before starting DMA*/
+		struct cdns3_trb *link_trb = priv_ep->trb_pool +
+					     (priv_ep->num_trbs - 1);
+		link_trb->control = ((priv_ep->pcs) ? TRB_CYCLE : 0) |
+				    TRB_TYPE(TRB_LINK) | TRB_CHAIN |
+				    TRB_TOGGLE;
+	}
+
+	first_pcs = priv_ep->pcs ? TRB_CYCLE : 0;
+
+	do {
+	/* fill TRB */
+		trb->buffer = TRB_BUFFER(request->num_sgs == 0
+				? trb_dma : request->sg[sg_iter].dma_address);
+
+		trb->length = TRB_BURST_LEN(16) |
+		    TRB_LEN(request->num_sgs == 0 ?
+				request->length : request->sg[sg_iter].length);
+
+		trb->control = TRB_TYPE(TRB_NORMAL);
+		pcs = priv_ep->pcs ? TRB_CYCLE : 0;
+
+		/*
+		 * first trb should be prepared as last to avoid processing
+		 *  transfer to early
+		 */
+		if (sg_iter != 0)
+			trb->control |= pcs;
+
+		if (priv_ep->type == USB_ENDPOINT_XFER_ISOC  && !priv_ep->dir) {
+			trb->control |= TRB_IOC | TRB_ISP;
+		} else {
+			/* for last element in TD or in SG list */
+			if (sg_iter == (num_trb - 1) && sg_iter != 0)
+				trb->control |= pcs | TRB_IOC | TRB_ISP;
+		}
+		++sg_iter;
+		priv_req->end_trb = priv_ep->enqueue;
+		cdns3_ep_inc_enq(priv_ep);
+		trb = priv_ep->trb_pool + priv_ep->enqueue;
+	} while (sg_iter < num_trb);
+
+	trb = priv_req->trb;
+
+	/*
+	 * Memory barrier = Cycle Bit must be set before trb->length  and
+	 * trb->buffer fields.
+	 */
+	wmb();
+
+	priv_req->flags |= REQUEST_PENDING;
+
+	/* give the TD to the consumer*/
+	if (sg_iter == 1)
+		trb->control |= first_pcs | TRB_IOC | TRB_ISP;
+	else
+		trb->control |= first_pcs;
+
+	trace_cdns3_prepare_trb(priv_ep, priv_req->trb);
+	trace_cdns3_ring(priv_ep);
+
+	/* arm transfer on selected endpoint */
+	cdns3_select_ep(priv_ep->cdns3_dev, address);
+
+	/*
+	 * For DMULT mode we can set address to transfer ring only once after
+	 * enabling endpoint.
+	 */
+	if (priv_ep->flags & EP_UPDATE_EP_TRBADDR) {
+		writel(EP_TRADDR_TRADDR(priv_ep->trb_pool_dma),
+		       &priv_dev->regs->ep_traddr);
+		priv_ep->flags &= ~EP_UPDATE_EP_TRBADDR;
+	}
+
+	/*clearing TRBERR and EP_STS_DESCMIS before seting DRDY*/
+	writel(EP_STS_TRBERR | EP_STS_DESCMIS, &priv_dev->regs->ep_sts);
+	trace_cdns3_doorbell_epx(priv_ep->name,
+				 readl(&priv_dev->regs->ep_traddr));
+	writel(EP_CMD_DRDY, &priv_dev->regs->ep_cmd);
+
+	return 0;
+}
+
+void cdns3_set_hw_configuration(struct cdns3_device *priv_dev)
+{
+	struct cdns3_endpoint *priv_ep;
+	struct usb_ep *ep;
+	int result = 0;
+
+	if (priv_dev->hw_configured_flag)
+		return;
+
+	writel(USB_CONF_CFGSET, &priv_dev->regs->usb_conf);
+	writel(EP_CMD_ERDY | EP_CMD_REQ_CMPL, &priv_dev->regs->ep_cmd);
+
+	cdns3_set_register_bit(&priv_dev->regs->usb_conf,
+			       USB_CONF_U1EN | USB_CONF_U2EN);
+
+	/* wait until configuration set */
+	result = cdns3_handshake(&priv_dev->regs->usb_sts,
+				 USB_STS_CFGSTS_MASK, 1, 100);
+
+	priv_dev->hw_configured_flag = 1;
+	cdns3_allow_enable_l1(priv_dev, 1);
+
+	list_for_each_entry(ep, &priv_dev->gadget.ep_list, ep_list) {
+		if (ep->enabled) {
+			priv_ep = ep_to_cdns3_ep(ep);
+			cdns3_start_all_request(priv_dev, priv_ep);
+		}
+	}
+}
+
+/**
+ * cdns3_request_handled - check whether request has been handled by DMA
+ *
+ * @priv_ep: extended endpoint object.
+ * @priv_req: request object for checking
+ *
+ * Endpoint must be selected before invoking this function.
+ *
+ * Returns false if request has not been handled by DMA, else returns true.
+ *
+ * SR - start ring
+ * ER -  end ring
+ * DQ = priv_ep->dequeue - dequeue position
+ * EQ = priv_ep->enqueue -  enqueue position
+ * ST = priv_req->start_trb - index of first TRB in transfer ring
+ * ET = priv_req->end_trb - index of last TRB in transfer ring
+ * CI = current_index - index of processed TRB by DMA.
+ *
+ * As first step, function checks if cycle bit for priv_req->start_trb is
+ * correct.
+ *
+ * some rules:
+ * 1. priv_ep->dequeue never exceed current_index.
+ * 2  priv_ep->enqueue never exceed priv_ep->dequeue
+ *
+ * Then We can split recognition into two parts:
+ * Case 1 - priv_ep->dequeue < current_index
+ *      SR ... EQ ... DQ ... CI ... ER
+ *      SR ... DQ ... CI ... EQ ... ER
+ *
+ *      Request has been handled by DMA if ST and ET is between DQ and CI.
+ *
+ * Case 2 - priv_ep->dequeue > current_index
+ * This situation take place when CI go through the LINK TRB at the end of
+ * transfer ring.
+ *      SR ... CI ... EQ ... DQ ... ER
+ *
+ *      Request has been handled by DMA if ET is less then CI or
+ *      ET is greater or equal DQ.
+ */
+static bool cdns3_request_handled(struct cdns3_endpoint *priv_ep,
+				  struct cdns3_request *priv_req)
+{
+	struct cdns3_device *priv_dev = priv_ep->cdns3_dev;
+	struct cdns3_trb *trb = priv_req->trb;
+	int current_index = 0;
+	int handled = 0;
+
+	current_index = (readl(&priv_dev->regs->ep_traddr) -
+			 priv_ep->trb_pool_dma) / TRB_SIZE;
+
+	trb = &priv_ep->trb_pool[priv_req->start_trb];
+
+	if ((trb->control  & TRB_CYCLE) != priv_ep->ccs)
+		goto finish;
+
+	if (priv_ep->dequeue < current_index) {
+		if ((current_index == (priv_ep->num_trbs - 1)) &&
+		    !priv_ep->dequeue)
+			goto finish;
+
+		if (priv_req->end_trb >= priv_ep->dequeue &&
+		    priv_req->end_trb < current_index)
+			handled = 1;
+	} else if (priv_ep->dequeue  > current_index) {
+		if (priv_req->end_trb  < current_index ||
+		    priv_req->end_trb >= priv_ep->dequeue)
+			handled = 1;
+	}
+
+finish:
+	trace_cdns3_request_handled(priv_req, current_index, handled);
+
+	return handled;
+}
+
+static void cdns3_transfer_completed(struct cdns3_device *priv_dev,
+				     struct cdns3_endpoint *priv_ep)
+{
+	struct cdns3_request *priv_req;
+	struct usb_request *request;
+	struct cdns3_trb *trb;
+	int current_trb;
+
+	while (!list_empty(&priv_ep->pending_req_list)) {
+		request = cdns3_next_request(&priv_ep->pending_req_list);
+		priv_req = to_cdns3_request(request);
+
+		if (!cdns3_request_handled(priv_ep, priv_req))
+			return;
+
+		if (request->dma % 8 && priv_ep->dir == USB_DIR_OUT)
+			memcpy(request->buf, priv_ep->aligned_buff,
+			       request->length);
+
+		trb = priv_ep->trb_pool + priv_ep->dequeue;
+		trace_cdns3_complete_trb(priv_ep, trb);
+
+		if (trb != priv_req->trb)
+			dev_warn(priv_dev->dev,
+				 "request_trb=0x%p, queue_trb=0x%p\n",
+				 priv_req->trb, trb);
+
+		request->actual = TRB_LEN(le32_to_cpu(trb->length));
+		current_trb = priv_req->start_trb;
+
+		while (current_trb != priv_req->end_trb) {
+			cdns3_ep_inc_deq(priv_ep);
+			current_trb = priv_ep->dequeue;
+		}
+
+		cdns3_ep_inc_deq(priv_ep);
+		cdns3_gadget_giveback(priv_ep, priv_req, 0);
+	}
+	priv_ep->flags &= ~EP_PENDING_REQUEST;
+}
+
+/**
+ * cdns3_descmissing_packet - handles descriptor missing event.
+ * @priv_dev: extended gadget object
+ *
+ * WA1: Controller for OUT endpoints has shared on-chip buffers for all incoming
+ * packets, including ep0out. It's FIFO buffer, so packets must be handle by DMA
+ * in correct order. If the first packet in the buffer will not be handled,
+ * then the following packets directed for other endpoints and  functions
+ * will be blocked.
+ * Additionally the packets directed to one endpoint can block entire on-chip
+ * buffers. In this case transfer to other endpoints also will blocked.
+ *
+ * To resolve this issue after raising the descriptor missing interrupt
+ * driver prepares internal usb_request object and use it to arm DMA transfer.
+ *
+ * The problematic situation was observed in case when endpoint has been enabled
+ * but no usb_request were queued. Driver try detects such endpoints and will
+ * use this workaround only for these endpoint.
+ *
+ * Driver use limited number of buffer. This number can be set by macro
+ * CDNS_WA1_NUM_BUFFERS.
+ *
+ * Such blocking situation was observed on ACM gadget. For this function
+ * host send OUT data packet but ACM function is not prepared for this packet.
+ *
+ * It's limitation of controller but maybe this issues should be fixed in
+ * function driver.
+ */
+static int cdns3_descmissing_packet(struct cdns3_endpoint *priv_ep)
+{
+	struct cdns3_request *priv_req;
+	struct usb_request *request;
+
+	if (priv_ep->flags & EP_QUIRK_EXTRA_BUF_DET) {
+		priv_ep->flags &= ~EP_QUIRK_EXTRA_BUF_DET;
+		priv_ep->flags |= EP_QUIRK_EXTRA_BUF_EN;
+	}
+
+	request = cdns3_gadget_ep_alloc_request(&priv_ep->endpoint,
+						GFP_ATOMIC);
+	if (!request)
+		return -ENOMEM;
+
+	priv_req = to_cdns3_request(request);
+	priv_req->flags |= REQUEST_INTERNAL;
+
+	/* if this field is still assigned it indicate that transfer related
+	 * with this request has not been finished yet. Driver in this
+	 * case simply allocate next request and assign flag REQUEST_INTERNAL_CH
+	 * flag to previous one. It will indicate that current request is
+	 * part of the previous one.
+	 */
+	if (priv_ep->descmis_req)
+		priv_ep->descmis_req->flags |= REQUEST_INTERNAL_CH;
+
+	priv_req->request.buf = kzalloc(CDNS3_DESCMIS_BUF_SIZE,
+					GFP_ATOMIC);
+	if (!priv_req) {
+		cdns3_gadget_ep_free_request(&priv_ep->endpoint, request);
+		return -ENOMEM;
+	}
+
+	priv_req->request.length = CDNS3_DESCMIS_BUF_SIZE;
+	priv_ep->descmis_req = priv_req;
+
+	__cdns3_gadget_ep_queue(&priv_ep->endpoint,
+				&priv_ep->descmis_req->request,
+				GFP_ATOMIC);
+
+	return 0;
+}
+
+/**
+ * cdns3_check_ep_interrupt_proceed - Processes interrupt related to endpoint
+ * @priv_ep: endpoint object
+ *
+ * Returns 0
+ */
+static int cdns3_check_ep_interrupt_proceed(struct cdns3_endpoint *priv_ep)
+{
+	struct cdns3_device *priv_dev = priv_ep->cdns3_dev;
+	u32 ep_sts_reg;
+
+	cdns3_select_ep(priv_dev, priv_ep->endpoint.address);
+
+	trace_cdns3_epx_irq(priv_dev, priv_ep);
+
+	ep_sts_reg = readl(&priv_dev->regs->ep_sts);
+	writel(ep_sts_reg, &priv_dev->regs->ep_sts);
+
+	if ((ep_sts_reg & EP_STS_IOC) || (ep_sts_reg & EP_STS_ISP)) {
+		if (priv_ep->flags & EP_QUIRK_EXTRA_BUF_EN) {
+			if (ep_sts_reg & EP_STS_ISP)
+				priv_ep->flags |= EP_QUIRK_END_TRANSFER;
+			else
+				priv_ep->flags &= ~EP_QUIRK_END_TRANSFER;
+		}
+		cdns3_transfer_completed(priv_dev, priv_ep);
+	}
+
+	/*
+	 * For isochronous transfer driver completes request on IOC or on
+	 * TRBERR. IOC appears only when device receive OUT data packet.
+	 * If host disable stream or lost some packet then the only way to
+	 * finish all queued transfer is to do it on TRBERR event.
+	 */
+	if ((ep_sts_reg & EP_STS_TRBERR) &&
+	    priv_ep->type == USB_ENDPOINT_XFER_ISOC)
+		cdns3_transfer_completed(priv_dev, priv_ep);
+
+	/*
+	 * WA1: this condition should only be meet when
+	 * priv_ep->flags & EP_QUIRK_EXTRA_BUF_DET or
+	 * priv_ep->flags & EP_QUIRK_EXTRA_BUF_EN.
+	 * In other cases this interrupt will be disabled/
+	 */
+	if (ep_sts_reg & EP_STS_DESCMIS) {
+		int err;
+
+		err = cdns3_descmissing_packet(priv_ep);
+		if (err)
+			dev_err(priv_dev->dev,
+				"Failed: No sufficient memory for DESCMIS\n");
+	}
+
+	return 0;
+}
+
+/**
+ * cdns3_check_usb_interrupt_proceed - Processes interrupt related to device
+ * @priv_dev: extended gadget object
+ * @usb_ists: bitmap representation of device's reported interrupts
+ * (usb_ists register value)
+ */
+static void cdns3_check_usb_interrupt_proceed(struct cdns3_device *priv_dev,
+					      u32 usb_ists)
+{
+	int speed = 0;
+
+	trace_cdns3_usb_irq(priv_dev, usb_ists);
+	/* Connection detected */
+	if (usb_ists & (USB_ISTS_CON2I | USB_ISTS_CONI)) {
+		speed = cdns3_get_speed(priv_dev);
+		priv_dev->gadget.speed = speed;
+		usb_gadget_set_state(&priv_dev->gadget, USB_STATE_POWERED);
+		cdns3_ep0_config(priv_dev);
+	}
+
+	/* Disconnection detected */
+	if (usb_ists & (USB_ISTS_DIS2I | USB_ISTS_DISI)) {
+		if (priv_dev->gadget_driver &&
+		    priv_dev->gadget_driver->disconnect) {
+			spin_unlock(&priv_dev->lock);
+			priv_dev->gadget_driver->disconnect(&priv_dev->gadget);
+			spin_lock(&priv_dev->lock);
+		}
+
+		priv_dev->gadget.speed = USB_SPEED_UNKNOWN;
+		usb_gadget_set_state(&priv_dev->gadget, USB_STATE_NOTATTACHED);
+		cdns3_hw_reset_eps_config(priv_dev);
+	}
+
+	/* reset*/
+	if (usb_ists & (USB_ISTS_UWRESI | USB_ISTS_UHRESI | USB_ISTS_U2RESI)) {
+		/*read again to check the actuall speed*/
+		speed = cdns3_get_speed(priv_dev);
+		usb_gadget_set_state(&priv_dev->gadget, USB_STATE_DEFAULT);
+		priv_dev->gadget.speed = speed;
+		cdns3_hw_reset_eps_config(priv_dev);
+		cdns3_ep0_config(priv_dev);
+	}
+}
+
+/**
+ * cdns3_device_irq_handler- interrupt handler for device part of controller
+ *
+ * @irq: irq number for cdns3 core device
+ * @data: structure of cdns3
+ *
+ * Returns IRQ_HANDLED or IRQ_NONE
+ */
+static irqreturn_t cdns3_device_irq_handler(int irq, void *data)
+{
+	struct cdns3_device *priv_dev;
+	struct cdns3 *cdns = data;
+	irqreturn_t ret = IRQ_NONE;
+	unsigned long flags;
+	u32 reg;
+
+	priv_dev = cdns->gadget_dev;
+	spin_lock_irqsave(&priv_dev->lock, flags);
+
+	/* check USB device interrupt */
+	reg = readl(&priv_dev->regs->usb_ists);
+	writel(reg, &priv_dev->regs->usb_ists);
+
+	if (reg) {
+		cdns3_check_usb_interrupt_proceed(priv_dev, reg);
+		ret = IRQ_HANDLED;
+	}
+
+	/* check endpoint interrupt */
+	reg = readl(&priv_dev->regs->ep_ists);
+
+	if (reg) {
+		reg = ~reg & readl(&priv_dev->regs->ep_ien);
+		/* mask deferred interrupt. */
+		writel(reg, &priv_dev->regs->ep_ien);
+		ret = IRQ_WAKE_THREAD;
+	}
+
+	spin_unlock_irqrestore(&priv_dev->lock, flags);
+	return ret;
+}
+
+/**
+ * cdns3_device_thread_irq_handler- interrupt handler for device part
+ * of controller
+ *
+ * @irq: irq number for cdns3 core device
+ * @data: structure of cdns3
+ *
+ * Returns IRQ_HANDLED or IRQ_NONE
+ */
+static irqreturn_t cdns3_device_thread_irq_handler(int irq, void *data)
+{
+	struct cdns3_device *priv_dev;
+	struct cdns3 *cdns = data;
+	irqreturn_t ret = IRQ_NONE;
+	unsigned long flags;
+	u32 ep_ien;
+	int bit;
+	u32 reg;
+
+	priv_dev = cdns->gadget_dev;
+	spin_lock_irqsave(&priv_dev->lock, flags);
+
+	reg = readl(&priv_dev->regs->ep_ists);
+	ep_ien = reg;
+
+	/* handle default endpoint OUT */
+	if (reg & EP_ISTS_EP_OUT0) {
+		cdns3_check_ep0_interrupt_proceed(priv_dev, USB_DIR_OUT);
+		ret = IRQ_HANDLED;
+	}
+
+	/* handle default endpoint IN */
+	if (reg & EP_ISTS_EP_IN0) {
+		cdns3_check_ep0_interrupt_proceed(priv_dev, USB_DIR_IN);
+		ret = IRQ_HANDLED;
+	}
+
+	/* check if interrupt from non default endpoint, if no exit */
+	reg &= ~(EP_ISTS_EP_OUT0 | EP_ISTS_EP_IN0);
+	if (!reg)
+		goto irqend;
+
+	for_each_set_bit(bit, (unsigned long *)&reg,
+			 sizeof(u32) * BITS_PER_BYTE) {
+		cdns3_check_ep_interrupt_proceed(priv_dev->eps[bit]);
+		ret = IRQ_HANDLED;
+	}
+
+irqend:
+	ep_ien |= readl(&priv_dev->regs->ep_ien);
+	/* Unmask all handled EP interrupts */
+	writel(ep_ien, &priv_dev->regs->ep_ien);
+	spin_unlock_irqrestore(&priv_dev->lock, flags);
+	return ret;
+}
+
+/**
+ * cdns3_ep_onchip_buffer_reserve - Try to reserve onchip buf for EP
+ *
+ * The real reservation will occur during write to EP_CFG register,
+ * this function is used to check if the 'size' reservation is allowed.
+ *
+ * @priv_dev: extended gadget object
+ * @size: the size (KB) for EP would like to allocate
+ *
+ * Return 0 if the required size can met or negative value on failure
+ */
+static int cdns3_ep_onchip_buffer_reserve(struct cdns3_device *priv_dev,
+					  int size)
+{
+	u32 onchip_mem;
+
+	priv_dev->onchip_mem_allocated_size += size;
+
+	onchip_mem = USB_CAP2_ACTUAL_MEM_SIZE(readl(&priv_dev->regs->usb_cap2));
+	if (!onchip_mem)
+		onchip_mem = 256;
+
+	/* 2KB is reserved for EP0*/
+	onchip_mem -= 2;
+	if (priv_dev->onchip_mem_allocated_size > onchip_mem) {
+		priv_dev->onchip_mem_allocated_size -= size;
+		return -EPERM;
+	}
+
+	return 0;
+}
+
+/**
+ * cdns3_ep_config Configure hardware endpoint
+ * @priv_ep: extended endpoint object
+ */
+void cdns3_ep_config(struct cdns3_endpoint *priv_ep)
+{
+	bool is_iso_ep = (priv_ep->type == USB_ENDPOINT_XFER_ISOC);
+	struct cdns3_device *priv_dev = priv_ep->cdns3_dev;
+	u32 bEndpointAddress = priv_ep->num | priv_ep->dir;
+	u32 max_packet_size = 0;
+	u32 ep_cfg = 0;
+	int ret;
+
+	switch (priv_ep->type) {
+	case USB_ENDPOINT_XFER_INT:
+		ep_cfg = EP_CFG_EPTYPE(USB_ENDPOINT_XFER_INT);
+		break;
+	case USB_ENDPOINT_XFER_BULK:
+		ep_cfg = EP_CFG_EPTYPE(USB_ENDPOINT_XFER_BULK);
+		break;
+	default:
+		ep_cfg = EP_CFG_EPTYPE(USB_ENDPOINT_XFER_ISOC);
+	}
+
+	switch (priv_dev->gadget.speed) {
+	case USB_SPEED_FULL:
+		max_packet_size = is_iso_ep ? 1023 : 64;
+		break;
+	case USB_SPEED_HIGH:
+		max_packet_size = is_iso_ep ? 1024 : 512;
+		break;
+	case USB_SPEED_SUPER:
+		max_packet_size = 1024;
+		break;
+	default:
+		/* all other speed are not supported */
+		return;
+	}
+
+	ret = cdns3_ep_onchip_buffer_reserve(priv_dev, CDNS3_EP_BUF_SIZE);
+	if (ret) {
+		dev_err(priv_dev->dev, "onchip mem is full, ep is invalid\n");
+		return;
+	}
+
+	ep_cfg |= EP_CFG_MAXPKTSIZE(max_packet_size) |
+		  EP_CFG_BUFFERING(CDNS3_EP_BUF_SIZE - 1) |
+		  EP_CFG_MAXBURST(priv_ep->endpoint.maxburst);
+
+	cdns3_select_ep(priv_dev, bEndpointAddress);
+	writel(ep_cfg, &priv_dev->regs->ep_cfg);
+
+	dev_dbg(priv_dev->dev, "Configure %s: with val %08x\n",
+		priv_ep->name, ep_cfg);
+}
+
+/* Find correct direction for HW endpoint according to description */
+static int cdns3_ep_dir_is_correct(struct usb_endpoint_descriptor *desc,
+				   struct cdns3_endpoint *priv_ep)
+{
+	return (priv_ep->endpoint.caps.dir_in && usb_endpoint_dir_in(desc)) ||
+	       (priv_ep->endpoint.caps.dir_out && usb_endpoint_dir_out(desc));
+}
+
+static struct
+cdns3_endpoint *cdns3_find_available_ep(struct cdns3_device *priv_dev,
+					struct usb_endpoint_descriptor *desc)
+{
+	struct usb_ep *ep;
+	struct cdns3_endpoint *priv_ep;
+
+	list_for_each_entry(ep, &priv_dev->gadget.ep_list, ep_list) {
+		unsigned long num;
+		int ret;
+		/* ep name pattern likes epXin or epXout */
+		char c[2] = {ep->name[2], '\0'};
+
+		ret = kstrtoul(c, 10, &num);
+		if (ret)
+			return ERR_PTR(ret);
+
+		priv_ep = ep_to_cdns3_ep(ep);
+		if (cdns3_ep_dir_is_correct(desc, priv_ep)) {
+			if (!(priv_ep->flags & EP_CLAIMED)) {
+				priv_ep->num  = num;
+				return priv_ep;
+			}
+		}
+	}
+
+	return ERR_PTR(-ENOENT);
+}
+
+/*
+ *  Cadence IP has one limitation that all endpoints must be configured
+ * (Type & MaxPacketSize) before setting configuration through hardware
+ * register, it means we can't change endpoints configuration after
+ * set_configuration.
+ *
+ * This function set EP_CLAIMED flag which is added when the gadget driver
+ * uses usb_ep_autoconfig to configure specific endpoint;
+ * When the udc driver receives set_configurion request,
+ * it goes through all claimed endpoints, and configure all endpoints
+ * accordingly.
+ *
+ * At usb_ep_ops.enable/disable, we only enable and disable endpoint through
+ * ep_cfg register which can be changed after set_configuration, and do
+ * some software operation accordingly.
+ */
+static struct
+usb_ep *cdns3_gadget_match_ep(struct usb_gadget *gadget,
+			      struct usb_endpoint_descriptor *desc,
+			      struct usb_ss_ep_comp_descriptor *comp_desc)
+{
+	struct cdns3_device *priv_dev = gadget_to_cdns3_device(gadget);
+	struct cdns3_endpoint *priv_ep;
+	unsigned long flags;
+
+	priv_ep = cdns3_find_available_ep(priv_dev, desc);
+	if (IS_ERR(priv_ep)) {
+		dev_err(priv_dev->dev, "no available ep\n");
+		return NULL;
+	}
+
+	dev_dbg(priv_dev->dev, "match endpoint: %s\n", priv_ep->name);
+
+	spin_lock_irqsave(&priv_dev->lock, flags);
+	priv_ep->endpoint.desc = desc;
+	priv_ep->dir  = usb_endpoint_dir_in(desc) ? USB_DIR_IN : USB_DIR_OUT;
+	priv_ep->type = usb_endpoint_type(desc);
+	priv_ep->flags |= EP_CLAIMED;
+	spin_unlock_irqrestore(&priv_dev->lock, flags);
+	return &priv_ep->endpoint;
+}
+
+/**
+ * cdns3_gadget_ep_alloc_request Allocates request
+ * @ep: endpoint object associated with request
+ * @gfp_flags: gfp flags
+ *
+ * Returns allocated request address, NULL on allocation error
+ */
+struct usb_request *cdns3_gadget_ep_alloc_request(struct usb_ep *ep,
+						  gfp_t gfp_flags)
+{
+	struct cdns3_endpoint *priv_ep = ep_to_cdns3_ep(ep);
+	struct cdns3_request *priv_req;
+
+	priv_req = kzalloc(sizeof(*priv_req), gfp_flags);
+	if (!priv_req)
+		return NULL;
+
+	priv_req->priv_ep = priv_ep;
+
+	trace_cdns3_alloc_request(priv_req);
+	return &priv_req->request;
+}
+
+/**
+ * cdns3_gadget_ep_free_request Free memory occupied by request
+ * @ep: endpoint object associated with request
+ * @request: request to free memory
+ */
+void cdns3_gadget_ep_free_request(struct usb_ep *ep,
+				  struct usb_request *request)
+{
+	struct cdns3_request *priv_req = to_cdns3_request(request);
+
+	trace_cdns3_free_request(priv_req);
+	kfree(priv_req);
+}
+
+/**
+ * cdns3_gadget_ep_enable Enable endpoint
+ * @ep: endpoint object
+ * @desc: endpoint descriptor
+ *
+ * Returns 0 on success, error code elsewhere
+ */
+static int cdns3_gadget_ep_enable(struct usb_ep *ep,
+				  const struct usb_endpoint_descriptor *desc)
+{
+	struct cdns3_endpoint *priv_ep;
+	struct cdns3_device *priv_dev;
+	u32 reg = EP_STS_EN_TRBERREN;
+	u32 bEndpointAddress;
+	unsigned long flags;
+	int ret;
+
+	priv_ep = ep_to_cdns3_ep(ep);
+	priv_dev = priv_ep->cdns3_dev;
+
+	if (!ep || !desc || desc->bDescriptorType != USB_DT_ENDPOINT) {
+		dev_dbg(priv_dev->dev, "usbss: invalid parameters\n");
+		return -EINVAL;
+	}
+
+	if (!desc->wMaxPacketSize) {
+		dev_err(priv_dev->dev, "usbss: missing wMaxPacketSize\n");
+		return -EINVAL;
+	}
+
+	if (dev_WARN_ONCE(priv_dev->dev, priv_ep->flags & EP_ENABLED,
+			  "%s is already enabled\n", priv_ep->name))
+		return 0;
+
+	spin_lock_irqsave(&priv_dev->lock, flags);
+
+	priv_ep->endpoint.desc = desc;
+	priv_ep->type = usb_endpoint_type(desc);
+	priv_ep->interval = desc->bInterval ? BIT(desc->bInterval - 1) : 0;
+
+	if (priv_ep->interval > ISO_MAX_INTERVAL &&
+	    priv_ep->type == USB_ENDPOINT_XFER_ISOC) {
+		dev_err(priv_dev->dev, "Driver is limited to %d period\n",
+			ISO_MAX_INTERVAL);
+
+		ret =  -EINVAL;
+		goto exit;
+	}
+
+	ret = cdns3_allocate_trb_pool(priv_ep);
+
+	if (ret)
+		goto exit;
+
+	bEndpointAddress = priv_ep->num | priv_ep->dir;
+	cdns3_select_ep(priv_dev, bEndpointAddress);
+
+	trace_cdns3_gadget_ep_enable(priv_ep);
+
+	writel(EP_CMD_EPRST, &priv_dev->regs->ep_cmd);
+
+	ret = cdns3_handshake(&priv_dev->regs->ep_cmd,
+			      EP_CMD_CSTALL | EP_CMD_EPRST, 0, 100);
+
+	/* enable interrupt for selected endpoint */
+	cdns3_set_register_bit(&priv_dev->regs->ep_ien,
+			       BIT(cdns3_ep_addr_to_index(bEndpointAddress)));
+	/*
+	 * WA1: Set flag for all not ISOC OUT endpoints. If this flag is set
+	 * driver try to detect whether endpoint need additional internal
+	 * buffer for unblocking on-chip FIFO buffer. This flag will be cleared
+	 * if before first DESCMISS interrupt the DMA will be armed.
+	 */
+	if (!priv_ep->dir && priv_ep->type != USB_ENDPOINT_XFER_ISOC) {
+		priv_ep->flags |= EP_QUIRK_EXTRA_BUF_DET;
+		reg |= EP_STS_EN_DESCMISEN;
+	}
+
+	writel(reg, &priv_dev->regs->ep_sts_en);
+
+	cdns3_set_register_bit(&priv_dev->regs->ep_cfg, EP_CFG_ENABLE);
+
+	ep->desc = desc;
+	priv_ep->flags &= ~(EP_PENDING_REQUEST | EP_STALL |
+			    EP_QUIRK_EXTRA_BUF_EN);
+	priv_ep->flags |= EP_ENABLED | EP_UPDATE_EP_TRBADDR;
+	priv_ep->enqueue = 0;
+	priv_ep->dequeue = 0;
+	reg = readl(&priv_dev->regs->ep_sts);
+	priv_ep->pcs = !!EP_STS_CCS(reg);
+	priv_ep->ccs = !!EP_STS_CCS(reg);
+	/* one TRB is reserved for link TRB used in DMULT mode*/
+	priv_ep->free_trbs = priv_ep->num_trbs - 1;
+exit:
+	spin_unlock_irqrestore(&priv_dev->lock, flags);
+
+	return ret;
+}
+
+/**
+ * cdns3_gadget_ep_disable Disable endpoint
+ * @ep: endpoint object
+ *
+ * Returns 0 on success, error code elsewhere
+ */
+static int cdns3_gadget_ep_disable(struct usb_ep *ep)
+{
+	struct cdns3_endpoint *priv_ep;
+	struct cdns3_request *priv_req;
+	struct cdns3_device *priv_dev;
+	struct usb_request *request;
+	unsigned long flags;
+	int ret = 0;
+	u32 ep_cfg;
+
+	if (!ep) {
+		pr_err("usbss: invalid parameters\n");
+		return -EINVAL;
+	}
+
+	priv_ep = ep_to_cdns3_ep(ep);
+	priv_dev = priv_ep->cdns3_dev;
+
+	if (dev_WARN_ONCE(priv_dev->dev, !(priv_ep->flags & EP_ENABLED),
+			  "%s is already disabled\n", priv_ep->name))
+		return 0;
+
+	spin_lock_irqsave(&priv_dev->lock, flags);
+
+	trace_cdns3_gadget_ep_disable(priv_ep);
+
+	cdns3_select_ep(priv_dev, ep->desc->bEndpointAddress);
+	ret = cdns3_data_flush(priv_ep);
+
+	ep_cfg = readl(&priv_dev->regs->ep_cfg);
+	ep_cfg &= ~EP_CFG_ENABLE;
+	writel(ep_cfg, &priv_dev->regs->ep_cfg);
+
+	while (!list_empty(&priv_ep->pending_req_list)) {
+		request = cdns3_next_request(&priv_ep->pending_req_list);
+
+		cdns3_gadget_giveback(priv_ep, to_cdns3_request(request),
+				      -ESHUTDOWN);
+	}
+
+	while (!list_empty(&priv_ep->descmiss_req_list)) {
+		priv_req = cdns3_next_priv_request(&priv_ep->descmiss_req_list);
+
+		kfree(priv_req->request.buf);
+		cdns3_gadget_ep_free_request(&priv_ep->endpoint,
+					     &priv_req->request);
+	}
+
+	while (!list_empty(&priv_ep->deferred_req_list)) {
+		request = cdns3_next_request(&priv_ep->deferred_req_list);
+
+		cdns3_gadget_giveback(priv_ep, to_cdns3_request(request),
+				      -ESHUTDOWN);
+	}
+
+	priv_ep->descmis_req = NULL;
+
+	ep->desc = NULL;
+	priv_ep->flags &= ~EP_ENABLED;
+
+	spin_unlock_irqrestore(&priv_dev->lock, flags);
+
+	return ret;
+}
+
+/**
+ * cdns3_gadget_ep_queue Transfer data on endpoint
+ * @ep: endpoint object
+ * @request: request object
+ * @gfp_flags: gfp flags
+ *
+ * Returns 0 on success, error code elsewhere
+ */
+static int __cdns3_gadget_ep_queue(struct usb_ep *ep,
+				   struct usb_request *request,
+				   gfp_t gfp_flags)
+{
+	struct cdns3_endpoint *priv_ep = ep_to_cdns3_ep(ep);
+	struct cdns3_device *priv_dev = priv_ep->cdns3_dev;
+	struct cdns3_request *priv_req;
+	int deferred = 0;
+	int ret = 0;
+
+	request->actual = 0;
+	request->status = -EINPROGRESS;
+	priv_req = to_cdns3_request(request);
+	trace_cdns3_ep_queue(priv_req);
+
+	/*
+	 * WA1: if transfer was queued before DESCMISS appear than we
+	 * can disable handling of DESCMISS interrupt. Driver assumes that it
+	 * can disable special treatment for this endpoint.
+	 */
+	if (priv_ep->flags & EP_QUIRK_EXTRA_BUF_DET) {
+		u32 reg;
+
+		cdns3_select_ep(priv_dev, priv_ep->num | priv_ep->dir);
+		reg = readl(&priv_dev->regs->ep_sts_en);
+		priv_ep->flags &= ~EP_QUIRK_EXTRA_BUF_DET;
+		reg &= EP_STS_EN_DESCMISEN;
+		writel(reg, &priv_dev->regs->ep_sts_en);
+	}
+
+	/* WA1 */
+	if (priv_ep->flags & EP_QUIRK_EXTRA_BUF_EN) {
+		u8 pending_empty = list_empty(&priv_ep->pending_req_list);
+		u8 descmiss_empty = list_empty(&priv_ep->descmiss_req_list);
+
+		/*
+		 *  DESCMISS transfer has been finished, so data will be
+		 *  directly copied from internal allocated usb_request
+		 *  objects.
+		 */
+		if (pending_empty && !descmiss_empty &&
+		    !(priv_req->flags & REQUEST_INTERNAL)) {
+			cdns3_descmiss_copy_data(priv_ep, request);
+			list_add_tail(&request->list,
+				      &priv_ep->pending_req_list);
+			cdns3_gadget_giveback(priv_ep, priv_req,
+					      request->status);
+			return ret;
+		}
+
+		/*
+		 * WA1 driver will wait for completion DESCMISS transfer,
+		 * before starts new, not DESCMISS transfer.
+		 */
+		if (!pending_empty && !descmiss_empty)
+			deferred = 1;
+
+		if (priv_req->flags & REQUEST_INTERNAL)
+			list_add_tail(&priv_req->list,
+				      &priv_ep->descmiss_req_list);
+	}
+
+	ret = usb_gadget_map_request_by_dev(priv_dev->sysdev, request,
+					    usb_endpoint_dir_in(ep->desc));
+	if (ret)
+		return ret;
+
+	/*
+	 * If hardware endpoint configuration has not been set yet then
+	 * just queue request in deferred list. Transfer will be started in
+	 * cdns3_set_hw_configuration.
+	 */
+	if (!priv_dev->hw_configured_flag)
+		deferred = 1;
+	else
+		ret = cdns3_ep_run_transfer(priv_ep, request);
+
+	if (ret || deferred)
+		list_add_tail(&request->list, &priv_ep->deferred_req_list);
+	else
+		list_add_tail(&request->list, &priv_ep->pending_req_list);
+
+	return ret;
+}
+
+static int cdns3_gadget_ep_queue(struct usb_ep *ep, struct usb_request *request,
+				 gfp_t gfp_flags)
+{
+	struct usb_request *zlp_request;
+	struct cdns3_endpoint *priv_ep;
+	struct cdns3_device *priv_dev;
+	unsigned long flags;
+	int ret;
+
+	if (!request || !ep)
+		return -EINVAL;
+
+	priv_ep = ep_to_cdns3_ep(ep);
+	priv_dev = priv_ep->cdns3_dev;
+
+	spin_lock_irqsave(&priv_dev->lock, flags);
+
+	ret = __cdns3_gadget_ep_queue(ep, request, gfp_flags);
+
+	if (ret == 0 && request->zero && request->length &&
+	    (request->length % ep->maxpacket == 0)) {
+		struct cdns3_request *priv_req;
+
+		zlp_request = cdns3_gadget_ep_alloc_request(ep, GFP_ATOMIC);
+		zlp_request->buf = priv_dev->zlp_buf;
+		zlp_request->length = 0;
+
+		priv_req = to_cdns3_request(zlp_request);
+		priv_req->flags |= REQUEST_ZLP;
+
+		dev_dbg(priv_dev->dev, "Queuing ZLP for endpoint: %s\n",
+			priv_ep->name);
+		ret = __cdns3_gadget_ep_queue(ep, zlp_request, gfp_flags);
+	}
+
+	spin_unlock_irqrestore(&priv_dev->lock, flags);
+	return ret;
+}
+
+/**
+ * cdns3_gadget_ep_dequeue Remove request from transfer queue
+ * @ep: endpoint object associated with request
+ * @request: request object
+ *
+ * Returns 0 on success, error code elsewhere
+ */
+int cdns3_gadget_ep_dequeue(struct usb_ep *ep,
+			    struct usb_request *request)
+{
+	struct cdns3_endpoint *priv_ep = ep_to_cdns3_ep(ep);
+	struct cdns3_device *priv_dev = priv_ep->cdns3_dev;
+	struct usb_request *req, *req_temp;
+	struct cdns3_request *priv_req;
+	unsigned long flags;
+	int ret = 0;
+
+	if (!ep || !request || !ep->desc)
+		return -EINVAL;
+
+	spin_lock_irqsave(&priv_dev->lock, flags);
+
+	priv_req = to_cdns3_request(request);
+
+	trace_cdns3_ep_dequeue(priv_req);
+
+	cdns3_select_ep(priv_dev, ep->desc->bEndpointAddress);
+
+	list_for_each_entry_safe(req, req_temp, &priv_ep->pending_req_list,
+				 list) {
+		if (request == req)
+			goto found;
+	}
+
+	list_for_each_entry_safe(req, req_temp, &priv_ep->deferred_req_list,
+				 list) {
+		if (request == req)
+			goto found;
+	}
+
+found:
+	if (request == req)
+		cdns3_gadget_giveback(priv_ep, priv_req, -ECONNRESET);
+
+	spin_unlock_irqrestore(&priv_dev->lock, flags);
+	return ret;
+}
+
+/**
+ * cdns3_gadget_ep_set_halt Sets/clears stall on selected endpoint
+ * @ep: endpoint object to set/clear stall on
+ * @value: 1 for set stall, 0 for clear stall
+ *
+ * Returns 0 on success, error code elsewhere
+ */
+int cdns3_gadget_ep_set_halt(struct usb_ep *ep, int value)
+{
+	struct cdns3_endpoint *priv_ep = ep_to_cdns3_ep(ep);
+	struct cdns3_device *priv_dev = priv_ep->cdns3_dev;
+	unsigned long flags;
+	int ret = 0;
+
+	if (!(priv_ep->flags & EP_ENABLED))
+		return -EPERM;
+
+	spin_lock_irqsave(&priv_dev->lock, flags);
+
+	/* if actual transfer is pending defer setting stall on this endpoint */
+	if ((priv_ep->flags & EP_PENDING_REQUEST) && value) {
+		priv_ep->flags |= EP_STALL;
+		goto finish;
+	}
+
+	dev_dbg(priv_dev->dev, "Halt endpoint %s\n", priv_ep->name);
+
+	cdns3_select_ep(priv_dev, ep->desc->bEndpointAddress);
+	if (value) {
+		cdns3_ep_stall_flush(priv_ep);
+	} else {
+		priv_ep->flags &= ~EP_WEDGE;
+		writel(EP_CMD_CSTALL | EP_CMD_EPRST, &priv_dev->regs->ep_cmd);
+
+		/* wait for EPRST cleared */
+		ret = cdns3_handshake(&priv_dev->regs->ep_cmd,
+				      EP_CMD_EPRST, 0, 100);
+		if (unlikely(ret)) {
+			dev_err(priv_dev->dev,
+				"Clearing halt condition failed for %s\n",
+				priv_ep->name);
+			goto finish;
+
+		} else {
+			priv_ep->flags &= ~EP_STALL;
+		}
+	}
+
+	priv_ep->flags &= ~EP_PENDING_REQUEST;
+finish:
+	spin_unlock_irqrestore(&priv_dev->lock, flags);
+
+	return ret;
+}
+
+extern const struct usb_ep_ops cdns3_gadget_ep0_ops;
+
+static const struct usb_ep_ops cdns3_gadget_ep_ops = {
+	.enable = cdns3_gadget_ep_enable,
+	.disable = cdns3_gadget_ep_disable,
+	.alloc_request = cdns3_gadget_ep_alloc_request,
+	.free_request = cdns3_gadget_ep_free_request,
+	.queue = cdns3_gadget_ep_queue,
+	.dequeue = cdns3_gadget_ep_dequeue,
+	.set_halt = cdns3_gadget_ep_set_halt,
+	.set_wedge = cdns3_gadget_ep_set_wedge,
+};
+
+/**
+ * cdns3_gadget_get_frame Returns number of actual ITP frame
+ * @gadget: gadget object
+ *
+ * Returns number of actual ITP frame
+ */
+static int cdns3_gadget_get_frame(struct usb_gadget *gadget)
+{
+	struct cdns3_device *priv_dev = gadget_to_cdns3_device(gadget);
+
+	return readl(&priv_dev->regs->usb_iptn);
+}
+
+static int cdns3_gadget_wakeup(struct usb_gadget *gadget)
+{
+	return 0;
+}
+
+static int cdns3_gadget_set_selfpowered(struct usb_gadget *gadget,
+					int is_selfpowered)
+{
+	struct cdns3_device *priv_dev = gadget_to_cdns3_device(gadget);
+	unsigned long flags;
+
+	spin_lock_irqsave(&priv_dev->lock, flags);
+	priv_dev->is_selfpowered = !!is_selfpowered;
+	spin_unlock_irqrestore(&priv_dev->lock, flags);
+	return 0;
+}
+
+static int cdns3_gadget_pullup(struct usb_gadget *gadget, int is_on)
+{
+	struct cdns3_device *priv_dev = gadget_to_cdns3_device(gadget);
+
+	if (is_on)
+		writel(USB_CONF_DEVEN, &priv_dev->regs->usb_conf);
+	else
+		writel(USB_CONF_DEVDS, &priv_dev->regs->usb_conf);
+
+	return 0;
+}
+
+static void cdns3_gadget_config(struct cdns3_device *priv_dev)
+{
+	struct cdns3_usb_regs __iomem *regs = priv_dev->regs;
+
+	cdns3_ep0_config(priv_dev);
+
+	/* enable interrupts for endpoint 0 (in and out) */
+	writel(EP_IEN_EP_OUT0 | EP_IEN_EP_IN0, &regs->ep_ien);
+
+	/* enable generic interrupt*/
+	writel(USB_IEN_INIT, &regs->usb_ien);
+	writel(USB_CONF_CLK2OFFDS | USB_CONF_L1DS, &regs->usb_conf);
+	writel(USB_CONF_DMULT, &regs->usb_conf);
+	cdns3_gadget_pullup(&priv_dev->gadget, 1);
+}
+
+/**
+ * cdns3_gadget_udc_start Gadget start
+ * @gadget: gadget object
+ * @driver: driver which operates on this gadget
+ *
+ * Returns 0 on success, error code elsewhere
+ */
+static int cdns3_gadget_udc_start(struct usb_gadget *gadget,
+				  struct usb_gadget_driver *driver)
+{
+	struct cdns3_device *priv_dev = gadget_to_cdns3_device(gadget);
+	unsigned long flags;
+
+	spin_lock_irqsave(&priv_dev->lock, flags);
+	priv_dev->gadget_driver = driver;
+	cdns3_gadget_config(priv_dev);
+	spin_unlock_irqrestore(&priv_dev->lock, flags);
+	return 0;
+}
+
+/**
+ * cdns3_gadget_udc_stop Stops gadget
+ * @gadget: gadget object
+ *
+ * Returns 0
+ */
+static int cdns3_gadget_udc_stop(struct usb_gadget *gadget)
+{
+	struct cdns3_device *priv_dev = gadget_to_cdns3_device(gadget);
+	struct cdns3_endpoint *priv_ep;
+	u32 bEndpointAddress;
+	struct usb_ep *ep;
+	int ret = 0;
+
+	priv_dev->gadget_driver = NULL;
+
+	priv_dev->onchip_mem_allocated_size = 0;
+	priv_dev->gadget.speed = USB_SPEED_UNKNOWN;
+
+	list_for_each_entry(ep, &priv_dev->gadget.ep_list, ep_list) {
+		priv_ep = ep_to_cdns3_ep(ep);
+		bEndpointAddress = priv_ep->num | priv_ep->dir;
+		cdns3_select_ep(priv_dev, bEndpointAddress);
+		writel(EP_CMD_EPRST, &priv_dev->regs->ep_cmd);
+		ret = cdns3_handshake(&priv_dev->regs->ep_cmd,
+				      EP_CMD_EPRST, 0, 100);
+		cdns3_free_trb_pool(priv_ep);
+	}
+
+	/* disable interrupt for device */
+	writel(0, &priv_dev->regs->usb_ien);
+	writel(USB_CONF_DEVDS, &priv_dev->regs->usb_conf);
+
+	return ret;
+}
+
+static const struct usb_gadget_ops cdns3_gadget_ops = {
+	.get_frame = cdns3_gadget_get_frame,
+	.wakeup = cdns3_gadget_wakeup,
+	.set_selfpowered = cdns3_gadget_set_selfpowered,
+	.pullup = cdns3_gadget_pullup,
+	.udc_start = cdns3_gadget_udc_start,
+	.udc_stop = cdns3_gadget_udc_stop,
+	.match_ep = cdns3_gadget_match_ep,
+};
+
+static void cdns3_free_all_eps(struct cdns3_device *priv_dev)
+{
+	int i;
+
+	/*ep0 OUT point to ep0 IN*/
+	priv_dev->eps[16] = NULL;
+
+	cdns3_free_trb_pool(priv_dev->eps[0]);
+
+	for (i = 0; i < CDNS3_ENDPOINTS_MAX_COUNT; i++)
+		if (priv_dev->eps[i])
+			devm_kfree(priv_dev->dev, priv_dev->eps[i]);
+}
+
+/**
+ * cdns3_init_eps Initializes software endpoints of gadget
+ * @cdns3: extended gadget object
+ *
+ * Returns 0 on success, error code elsewhere
+ */
+static int cdns3_init_eps(struct cdns3_device *priv_dev)
+{
+	u32 ep_enabled_reg, iso_ep_reg;
+	struct cdns3_endpoint *priv_ep;
+	int ep_dir, ep_number;
+	u32 ep_mask;
+	int ret = 0;
+	int i;
+
+	/* Read it from USB_CAP3 to USB_CAP5 */
+	ep_enabled_reg = readl(&priv_dev->regs->usb_cap3);
+	iso_ep_reg = readl(&priv_dev->regs->usb_cap4);
+
+	dev_dbg(priv_dev->dev, "Initializing non-zero endpoints\n");
+
+	for (i = 0; i < CDNS3_ENDPOINTS_MAX_COUNT; i++) {
+		ep_dir = i >> 4;	/* i div 16 */
+		ep_number = i & 0xF;	/* i % 16 */
+		ep_mask = BIT(i);
+
+		if (!(ep_enabled_reg & ep_mask))
+			continue;
+
+		if (ep_dir && !ep_number) {
+			priv_dev->eps[i] = priv_dev->eps[0];
+			continue;
+		}
+
+		priv_ep = devm_kzalloc(priv_dev->dev, sizeof(*priv_ep),
+				       GFP_KERNEL);
+		if (!priv_ep) {
+			ret = -ENOMEM;
+			goto err;
+		}
+
+		/* set parent of endpoint object */
+		priv_ep->cdns3_dev = priv_dev;
+		priv_dev->eps[i] = priv_ep;
+		priv_ep->num = ep_number;
+		priv_ep->dir = ep_dir ? USB_DIR_IN : USB_DIR_OUT;
+
+		if (!ep_number) {
+			ret = cdns3_init_ep0(priv_dev, priv_ep);
+			if (ret) {
+				dev_err(priv_dev->dev, "Failed to init ep0\n");
+				goto err;
+			}
+		} else {
+			snprintf(priv_ep->name, sizeof(priv_ep->name), "ep%d%s",
+				 ep_number, !!ep_dir ? "in" : "out");
+			priv_ep->endpoint.name = priv_ep->name;
+
+			usb_ep_set_maxpacket_limit(&priv_ep->endpoint,
+						   CDNS3_EP_MAX_PACKET_LIMIT);
+			priv_ep->endpoint.max_streams = CDNS3_EP_MAX_STREAMS;
+			priv_ep->endpoint.ops = &cdns3_gadget_ep_ops;
+			if (ep_dir)
+				priv_ep->endpoint.caps.dir_in = 1;
+			else
+				priv_ep->endpoint.caps.dir_out = 1;
+
+			if (iso_ep_reg & ep_mask)
+				priv_ep->endpoint.caps.type_iso = 1;
+
+			priv_ep->endpoint.caps.type_bulk = 1;
+			priv_ep->endpoint.caps.type_int = 1;
+			priv_ep->endpoint.maxburst = CDNS3_EP_BUF_SIZE - 1;
+
+			list_add_tail(&priv_ep->endpoint.ep_list,
+				      &priv_dev->gadget.ep_list);
+		}
+
+		priv_ep->flags = 0;
+
+		dev_info(priv_dev->dev, "Initialized  %s support: %s %s\n",
+			 priv_ep->name,
+			 priv_ep->endpoint.caps.type_bulk ? "BULK, INT" : "",
+			 priv_ep->endpoint.caps.type_iso ? "ISO" : "");
+
+		INIT_LIST_HEAD(&priv_ep->pending_req_list);
+		INIT_LIST_HEAD(&priv_ep->deferred_req_list);
+		INIT_LIST_HEAD(&priv_ep->descmiss_req_list);
+	}
+
+	return 0;
+err:
+	cdns3_free_all_eps(priv_dev);
+	return -ENOMEM;
+}
+
+static void cdns3_gadget_disable(struct cdns3 *cdns)
+{
+	struct cdns3_device *priv_dev;
+
+	priv_dev = cdns->gadget_dev;
+
+	if (priv_dev->gadget_driver)
+		priv_dev->gadget_driver->disconnect(&priv_dev->gadget);
+
+	usb_gadget_disconnect(&priv_dev->gadget);
+	priv_dev->gadget.speed = USB_SPEED_UNKNOWN;
+}
+
+void cdns3_gadget_exit(struct cdns3 *cdns)
+{
+	struct cdns3_device *priv_dev;
+
+	priv_dev = cdns->gadget_dev;
+
+	cdns3_gadget_disable(cdns);
+
+	devm_free_irq(cdns->dev, cdns->irq, cdns);
+
+	pm_runtime_mark_last_busy(cdns->dev);
+	pm_runtime_put_autosuspend(cdns->dev);
+
+	usb_del_gadget_udc(&priv_dev->gadget);
+
+	cdns3_free_all_eps(priv_dev);
+
+	dma_free_coherent(priv_dev->sysdev, 8, priv_dev->setup_buf,
+			  priv_dev->setup_dma);
+
+	kfree(priv_dev->zlp_buf);
+	kfree(priv_dev);
+	cdns->gadget_dev = NULL;
+}
+
+static int cdns3_gadget_start(struct cdns3 *cdns)
+{
+	struct cdns3_device *priv_dev;
+	u32 max_speed;
+	int ret;
+
+	priv_dev = kzalloc(sizeof(*priv_dev), GFP_KERNEL);
+	if (!priv_dev)
+		return -ENOMEM;
+
+	cdns->gadget_dev = priv_dev;
+	priv_dev->sysdev = cdns->dev;
+	priv_dev->dev = cdns->dev;
+	priv_dev->regs = cdns->dev_regs;
+
+	max_speed = usb_get_maximum_speed(cdns->dev);
+
+	/* Check the maximum_speed parameter */
+	switch (max_speed) {
+	case USB_SPEED_FULL:
+	case USB_SPEED_HIGH:
+	case USB_SPEED_SUPER:
+		break;
+	default:
+		dev_err(cdns->dev, "invalid maximum_speed parameter %d\n",
+			max_speed);
+		/* fall through */
+	case USB_SPEED_UNKNOWN:
+		/* default to superspeed */
+		max_speed = USB_SPEED_SUPER;
+		break;
+	}
+
+	/* fill gadget fields */
+	priv_dev->gadget.max_speed = max_speed;
+	priv_dev->gadget.speed = USB_SPEED_UNKNOWN;
+	priv_dev->gadget.ops = &cdns3_gadget_ops;
+	priv_dev->gadget.name = "usb-ss-gadget";
+	priv_dev->gadget.sg_supported = 1;
+
+	spin_lock_init(&priv_dev->lock);
+	INIT_WORK(&priv_dev->pending_status_wq,
+		  cdns3_pending_setup_status_handler);
+
+	/* initialize endpoint container */
+	INIT_LIST_HEAD(&priv_dev->gadget.ep_list);
+
+	ret = cdns3_init_eps(priv_dev);
+	if (ret) {
+		dev_err(priv_dev->dev, "Failed to create endpoints\n");
+		goto err1;
+	}
+
+	/* allocate memory for setup packet buffer */
+	priv_dev->setup_buf = dma_alloc_coherent(priv_dev->sysdev, 8,
+						 &priv_dev->setup_dma, GFP_DMA);
+	if (!priv_dev->setup_buf) {
+		dev_err(priv_dev->dev, "Failed to allocate memory for SETUP buffer\n");
+		ret = -ENOMEM;
+		goto err2;
+	}
+
+	priv_dev->dev_ver = readl(&priv_dev->regs->usb_cap6);
+	dev_dbg(priv_dev->dev, "Device Controller version: %08x\n",
+		readl(&priv_dev->regs->usb_cap6));
+	dev_dbg(priv_dev->dev, "USB Capabilities:: %08x\n",
+		readl(&priv_dev->regs->usb_cap1));
+	dev_dbg(priv_dev->dev, "On-Chip memory cnfiguration: %08x\n",
+		readl(&priv_dev->regs->usb_cap2));
+
+	priv_dev->zlp_buf = kzalloc(CDNS3_EP_ZLP_BUF_SIZE, GFP_KERNEL);
+	if (!priv_dev->zlp_buf) {
+		ret = -ENOMEM;
+		goto err3;
+	}
+
+	/* add USB gadget device */
+	ret = usb_add_gadget_udc(priv_dev->dev, &priv_dev->gadget);
+	if (ret < 0) {
+		dev_err(priv_dev->dev,
+			"Failed to register USB device controller\n");
+		goto err4;
+	}
+
+	return 0;
+err4:
+	kfree(priv_dev->zlp_buf);
+err3:
+	dma_free_coherent(priv_dev->sysdev, 8, priv_dev->setup_buf,
+			  priv_dev->setup_dma);
+err2:
+	cdns3_free_all_eps(priv_dev);
+err1:
+	cdns->gadget_dev = NULL;
+	return ret;
+}
+
+static int __cdns3_gadget_init(struct cdns3 *cdns)
+{
+	struct cdns3_device *priv_dev;
+	unsigned long flags;
+	int ret = 0;
+
+	ret = cdns3_gadget_start(cdns);
+	if (ret)
+		return ret;
+
+	priv_dev = cdns->gadget_dev;
+	ret = devm_request_threaded_irq(cdns->dev, cdns->irq,
+					cdns3_device_irq_handler,
+					cdns3_device_thread_irq_handler,
+					IRQF_SHARED, dev_name(cdns->dev), cdns);
+	if (ret)
+		goto err0;
+
+	pm_runtime_get_sync(cdns->dev);
+	spin_lock_irqsave(&priv_dev->lock, flags);
+	spin_unlock_irqrestore(&priv_dev->lock, flags);
+	return 0;
+err0:
+	cdns3_gadget_exit(cdns);
+	return ret;
+}
+
+static int cdns3_gadget_suspend(struct cdns3 *cdns, bool do_wakeup)
+{
+	cdns3_gadget_disable(cdns);
+	return 0;
+}
+
+static int cdns3_gadget_resume(struct cdns3 *cdns, bool hibernated)
+{
+	struct cdns3_device *priv_dev;
+	unsigned long flags;
+
+	priv_dev = cdns->gadget_dev;
+	spin_lock_irqsave(&priv_dev->lock, flags);
+
+	if (!priv_dev->gadget_driver) {
+		spin_unlock_irqrestore(&priv_dev->lock, flags);
+		return 0;
+	}
+
+	cdns3_gadget_config(priv_dev);
+	spin_unlock_irqrestore(&priv_dev->lock, flags);
+	return 0;
+}
+
+/**
+ * cdns3_gadget_init - initialize device structure
+ *
+ * cdns: cdns3 instance
+ *
+ * This function initializes the gadget.
+ */
+int cdns3_gadget_init(struct cdns3 *cdns)
+{
+	struct cdns3_role_driver *rdrv;
+
+	rdrv = devm_kzalloc(cdns->dev, sizeof(*rdrv), GFP_KERNEL);
+	if (!rdrv)
+		return -ENOMEM;
+
+	rdrv->start	= __cdns3_gadget_init;
+	rdrv->stop	= cdns3_gadget_exit;
+	rdrv->suspend	= cdns3_gadget_suspend;
+	rdrv->resume	= cdns3_gadget_resume;
+	rdrv->state	= CDNS3_ROLE_STATE_INACTIVE;
+	rdrv->name	= "gadget";
+	cdns->roles[CDNS3_ROLE_GADGET] = rdrv;
+
+	return 0;
+}
diff --git a/drivers/usb/cdns3/gadget.h b/drivers/usb/cdns3/gadget.h
new file mode 100644
index 000000000000..41cec7f085ad
--- /dev/null
+++ b/drivers/usb/cdns3/gadget.h
@@ -0,0 +1,1206 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * USBSS device controller driver header file
+ *
+ * Copyright (C) 2018 Cadence.
+ * Copyright (C) 2017-2018 NXP
+ *
+ * Author: Pawel Laszczak <pawell@cadence.com>
+ *         Pawel Jez <pjez@cadence.com>
+ *         Peter Chen <peter.chen@nxp.com>
+ */
+#ifndef __LINUX_CDNS3_GADGET
+#define __LINUX_CDNS3_GADGET
+#include <linux/usb/gadget.h>
+
+/*
+ * USBSS-DEV register interface.
+ * This corresponds to the USBSS Device Controller Interface
+ */
+
+/**
+ * struct cdns3_usb_regs - device controller registers.
+ * @usb_conf:      Global Configuration Register.
+ * @usb_sts:       Global Status Register.
+ * @usb_cmd:       Global Command Register.
+ * @usb_iptn:      ITP/SOF number Register.
+ * @usb_lpm:       Global Command Register.
+ * @usb_ien:       USB Interrupt Enable Register.
+ * @usb_ists:      USB Interrupt Status Register.
+ * @ep_sel:        Endpoint Select Register.
+ * @ep_traddr:     Endpoint Transfer Ring Address Register.
+ * @ep_cfg:        Endpoint Configuration Register.
+ * @ep_cmd:        Endpoint Command Register.
+ * @ep_sts:        Endpoint Status Register.
+ * @ep_sts_sid:    Endpoint Status Register.
+ * @ep_sts_en:     Endpoint Status Register Enable.
+ * @drbl:          Doorbell Register.
+ * @ep_ien:        EP Interrupt Enable Register.
+ * @ep_ists:       EP Interrupt Status Register.
+ * @usb_pwr:       Global Power Configuration Register.
+ * @usb_conf2:     Global Configuration Register 2.
+ * @usb_cap1:      Capability Register 1.
+ * @usb_cap2:      Capability Register 2.
+ * @usb_cap3:      Capability Register 3.
+ * @usb_cap4:      Capability Register 4.
+ * @usb_cap5:      Capability Register 5.
+ * @usb_cap6:      Capability Register 6.
+ * @usb_cpkt1:     Custom Packet Register 1.
+ * @usb_cpkt2:     Custom Packet Register 2.
+ * @usb_cpkt3:     Custom Packet Register 3.
+ * @reserved1:     Reserved.
+ * @cfg_regs:      Configuration registers.
+ * @reserved2:     Reserved.
+ * @dma_axi_ctrl:  AXI Control register.
+ * @dma_axi_id:    AXI ID register.
+ * @dma_axi_cap:   AXI Capability register.
+ * @dma_axi_ctrl0: AXI Control 0 register.
+ * @dma_axi_ctrl1: AXI Control 1 register.
+ */
+struct cdns3_usb_regs {
+	__le32 usb_conf;
+	__le32 usb_sts;
+	__le32 usb_cmd;
+	__le32 usb_iptn;
+	__le32 usb_lpm;
+	__le32 usb_ien;
+	__le32 usb_ists;
+	__le32 ep_sel;
+	__le32 ep_traddr;
+	__le32 ep_cfg;
+	__le32 ep_cmd;
+	__le32 ep_sts;
+	__le32 ep_sts_sid;
+	__le32 ep_sts_en;
+	__le32 drbl;
+	__le32 ep_ien;
+	__le32 ep_ists;
+	__le32 usb_pwr;
+	__le32 usb_conf2;
+	__le32 usb_cap1;
+	__le32 usb_cap2;
+	__le32 usb_cap3;
+	__le32 usb_cap4;
+	__le32 usb_cap5;
+	__le32 usb_cap6;
+	__le32 usb_cpkt1;
+	__le32 usb_cpkt2;
+	__le32 usb_cpkt3;
+	__le32 reserved1[36];
+	__le32 cfg_reg1;
+	__le32 dbg_link1;
+	__le32 dbg_link2;
+	__le32 cfg_regs[74];
+	__le32 reserved2[34];
+	__le32 dma_axi_ctrl;
+	__le32 dma_axi_id;
+	__le32 dma_axi_cap;
+	__le32 dma_axi_ctrl0;
+	__le32 dma_axi_ctrl1;
+};
+
+/* USB_CONF - bitmasks */
+/* Reset USB device configuration. */
+#define USB_CONF_CFGRST		BIT(0)
+/* Set Configuration. */
+#define USB_CONF_CFGSET		BIT(1)
+/* Disconnect USB device in SuperSpeed. */
+#define USB_CONF_USB3DIS	BIT(3)
+/* Disconnect USB device in HS/FS */
+#define USB_CONF_USB2DIS	BIT(4)
+/* Little Endian access - default */
+#define USB_CONF_LENDIAN	BIT(5)
+/*
+ * Big Endian access. Driver assume that byte order for
+ * SFRs access always is as Little Endian so this bit
+ * is not used.
+ */
+#define USB_CONF_BENDIAN	BIT(6)
+/* Device software reset. */
+#define USB_CONF_SWRST		BIT(7)
+/* Singular DMA transfer mode. */
+#define USB_CONF_DSING		BIT(8)
+/* Multiple DMA transfers mode. */
+#define USB_CONF_DMULT		BIT(9)
+/* DMA clock turn-off enable. */
+#define USB_CONF_DMAOFFEN	BIT(10)
+/* DMA clock turn-off disable. */
+#define USB_CONF_DMAOFFDS	BIT(11)
+/* Clear Force Full Speed. */
+#define USB_CONF_CFORCE_FS	BIT(12)
+/* Set Force Full Speed. */
+#define USB_CONF_SFORCE_FS	BIT(13)
+/* Device enable. */
+#define USB_CONF_DEVEN		BIT(14)
+/* Device disable. */
+#define USB_CONF_DEVDS		BIT(15)
+/* L1 LPM state entry enable (used in HS/FS mode). */
+#define USB_CONF_L1EN		BIT(16)
+/* L1 LPM state entry disable (used in HS/FS mode). */
+#define USB_CONF_L1DS		BIT(17)
+/* USB 2.0 clock gate disable. */
+#define USB_CONF_CLK2OFFEN	BIT(18)
+/* USB 2.0 clock gate enable. */
+#define USB_CONF_CLK2OFFDS	BIT(19)
+/* L0 LPM state entry request (used in HS/FS mode). */
+#define USB_CONF_LGO_L0		BIT(20)
+/* USB 3.0 clock gate disable. */
+#define USB_CONF_CLK3OFFEN	BIT(21)
+/* USB 3.0 clock gate enable. */
+#define USB_CONF_CLK3OFFDS	BIT(22)
+/* Bit 23 is reserved*/
+/* U1 state entry enable (used in SS mode). */
+#define USB_CONF_U1EN		BIT(24)
+/* U1 state entry disable (used in SS mode). */
+#define USB_CONF_U1DS		BIT(25)
+/* U2 state entry enable (used in SS mode). */
+#define USB_CONF_U2EN		BIT(26)
+/* U2 state entry disable (used in SS mode). */
+#define USB_CONF_U2DS		BIT(27)
+/* U0 state entry request (used in SS mode). */
+#define USB_CONF_LGO_U0		BIT(28)
+/* U1 state entry request (used in SS mode). */
+#define USB_CONF_LGO_U1		BIT(29)
+/* U2 state entry request (used in SS mode). */
+#define USB_CONF_LGO_U2		BIT(30)
+/* SS.Inactive state entry request (used in SS mode) */
+#define USB_CONF_LGO_SSINACT	BIT(31)
+
+/* USB_STS - bitmasks */
+/*
+ * Configuration status.
+ * 1 - device is in the configured state.
+ * 0 - device is not configured.
+ */
+#define USB_STS_CFGSTS_MASK	BIT(0)
+#define USB_STS_CFGSTS(p)	((p) & USB_STS_CFGSTS_MASK)
+/*
+ * On-chip memory overflow.
+ * 0 - On-chip memory status OK.
+ * 1 - On-chip memory overflow.
+ */
+#define USB_STS_OV_MASK		BIT(1)
+#define USB_STS_OV(p)		((p) & USB_STS_OV_MASK)
+/*
+ * SuperSpeed connection status.
+ * 0 - USB in SuperSpeed mode disconnected.
+ * 1 - USB in SuperSpeed mode connected.
+ */
+#define USB_STS_USB3CONS_MASK	BIT(2)
+#define USB_STS_USB3CONS(p)	((p) & USB_STS_USB3CONS_MASK)
+/*
+ * DMA transfer configuration status.
+ * 0 - single request.
+ * 1 - multiple TRB chain
+ */
+#define USB_STS_DTRANS_MASK	BIT(3)
+#define USB_STS_DTRANS(p)	((p) & USB_STS_DTRANS_MASK)
+/*
+ * Device speed.
+ * 0 - Undefined (value after reset).
+ * 1 - Low speed
+ * 2 - Full speed
+ * 3 - High speed
+ * 4 - Super speed
+ */
+#define USB_STS_USBSPEED_MASK	GENMASK(6, 4)
+#define USB_STS_USBSPEED(p)	(((p) & USB_STS_USBSPEED_MASK) >> 4)
+#define USB_STS_LS		(0x1 << 4)
+#define USB_STS_FS		(0x2 << 4)
+#define USB_STS_HS		(0x3 << 4)
+#define USB_STS_SS		(0x4 << 4)
+#define DEV_UNDEFSPEED(p)	(((p) & USB_STS_USBSPEED_MASK) == (0x0 << 4))
+#define DEV_LOWSPEED(p)		(((p) & USB_STS_USBSPEED_MASK) == USB_STS_LS)
+#define DEV_FULLSPEED(p)	(((p) & USB_STS_USBSPEED_MASK) == USB_STS_FS)
+#define DEV_HIGHSPEED(p)	(((p) & USB_STS_USBSPEED_MASK) == USB_STS_HS)
+#define DEV_SUPERSPEED(p)	(((p) & USB_STS_USBSPEED_MASK) == USB_STS_SS)
+/*
+ * Endianness for SFR access.
+ * 0 - Little Endian order (default after hardware reset).
+ * 1 - Big Endian order
+ */
+#define USB_STS_ENDIAN_MASK	BIT(7)
+#define USB_STS_ENDIAN(p)	((p) & USB_STS_ENDIAN_MASK)
+/*
+ * HS/FS clock turn-off status.
+ * 0 - hsfs clock is always on.
+ * 1 - hsfs clock turn-off in L2 (HS/FS mode) is enabled
+ *          (default after hardware reset).
+ */
+#define USB_STS_CLK2OFF_MASK	BIT(8)
+#define USB_STS_CLK2OFF(p)	((p) & USB_STS_CLK2OFF_MASK)
+/*
+ * PCLK clock turn-off status.
+ * 0 - pclk clock is always on.
+ * 1 - pclk clock turn-off in U3 (SS mode) is enabled
+ *          (default after hardware reset).
+ */
+#define USB_STS_CLK3OFF_MASK	BIT(9)
+#define USB_STS_CLK3OFF(p)	((p) & USB_STS_CLK3OFF_MASK)
+/*
+ * Controller in reset state.
+ * 0 - Internal reset is active.
+ * 1 - Internal reset is not active and controller is fully operational.
+ */
+#define USB_STS_IN_RST_MASK	BIT(10)
+#define USB_STS_IN_RST(p)	((p) & USB_STS_IN_RST_MASK)
+/*
+ * Device enable Status.
+ * 0 - USB device is disabled (VBUS input is disconnected from internal logic).
+ * 1 - USB device is enabled (VBUS input is connected to the internal logic).
+ */
+#define USB_STS_DEVS_MASK	BIT(14)
+#define USB_STS_DEVS(p)		((p) & USB_STS_DEVS_MASK)
+/*
+ * DAddress statuss.
+ * 0 - USB device is default state.
+ * 1 - USB device is at least in address state.
+ */
+#define USB_STS_ADDRESSED_MASK	BIT(15)
+#define USB_STS_ADDRESSED(p)	((p) & USB_STS_ADDRESSED_MASK)
+/*
+ * L1 LPM state enable status (used in HS/FS mode).
+ * 0 - Entering to L1 LPM state disabled.
+ * 1 - Entering to L1 LPM state enabled.
+ */
+#define USB_STS_L1ENS_MASK	BIT(16)
+#define USB_STS_L1ENS(p)	((p) & USB_STS_L1ENS_MASK)
+/*
+ * Internal VBUS connection status (used both in HS/FS  and SS mode).
+ * 0 - internal VBUS is not detected.
+ * 1 - internal VBUS is detected.
+ */
+#define USB_STS_VBUSS_MASK	BIT(17)
+#define USB_STS_VBUSS(p)	((p) & USB_STS_VBUSS_MASK)
+/*
+ * HS/FS LPM  state (used in FS/HS mode).
+ * 0 - L0 State
+ * 1 - L1 State
+ * 2 - L2 State
+ * 3 - L3 State
+ */
+#define USB_STS_LPMST_MASK	GENMASK(19, 18)
+#define DEV_L0_STATE(p)		(((p) & USB_STS_LPMST_MASK) == (0x0 << 18))
+#define DEV_L1_STATE(p)		(((p) & USB_STS_LPMST_MASK) == (0x1 << 18))
+#define DEV_L2_STATE(p)		(((p) & USB_STS_LPMST_MASK) == (0x2 << 18))
+#define DEV_L3_STATE(p)		(((p) & USB_STS_LPMST_MASK) == (0x3 << 18))
+/*
+ * Disable HS status (used in FS/HS mode).
+ * 0 - the disconnect bit for HS/FS mode is set .
+ * 1 - the disconnect bit for HS/FS mode is not set.
+ */
+#define USB_STS_USB2CONS_MASK	BIT(20)
+#define USB_STS_USB2CONS(p)	((p) & USB_STS_USB2CONS_MASK)
+/*
+ * HS/FS mode connection status (used in FS/HS mode).
+ * 0 - High Speed operations in USB2.0 (FS/HS) mode not disabled.
+ * 1 - High Speed operations in USB2.0 (FS/HS).
+ */
+#define USB_STS_DISABLE_HS_MASK	BIT(21)
+#define USB_STS_DISABLE_HS(p)	((p) & USB_STS_DISABLE_HS_MASK)
+/*
+ * U1 state enable status (used in SS mode).
+ * 0 - Entering to  U1 state disabled.
+ * 1 - Entering to  U1 state enabled.
+ */
+#define USB_STS_U1ENS_MASK	BIT(24)
+#define USB_STS_U1ENS(p)	((p) & USB_STS_U1ENS_MASK)
+/*
+ * U2 state enable status (used in SS mode).
+ * 0 - Entering to  U2 state disabled.
+ * 1 - Entering to  U2 state enabled.
+ */
+#define USB_STS_U2ENS_MASK	BIT(25)
+#define USB_STS_U2ENS(p)	((p) & USB_STS_U2ENS_MASK)
+/*
+ * SuperSpeed Link LTSSM state. This field reflects USBSS-DEV current
+ * SuperSpeed link state
+ */
+#define USB_STS_LST_MASK	GENMASK(29, 26)
+#define DEV_LST_U0		(((p) & USB_STS_LST_MASK) == (0x0 << 26))
+#define DEV_LST_U1		(((p) & USB_STS_LST_MASK) == (0x1 << 26))
+#define DEV_LST_U2		(((p) & USB_STS_LST_MASK) == (0x2 << 26))
+#define DEV_LST_U3		(((p) & USB_STS_LST_MASK) == (0x3 << 26))
+#define DEV_LST_DISABLED	(((p) & USB_STS_LST_MASK) == (0x4 << 26))
+#define DEV_LST_RXDETECT	(((p) & USB_STS_LST_MASK) == (0x5 << 26))
+#define DEV_LST_INACTIVE	(((p) & USB_STS_LST_MASK) == (0x6 << 26))
+#define DEV_LST_POLLING		(((p) & USB_STS_LST_MASK) == (0x7 << 26))
+#define DEV_LST_RECOVERY	(((p) & USB_STS_LST_MASK) == (0x8 << 26))
+#define DEV_LST_HOT_RESET	(((p) & USB_STS_LST_MASK) == (0x9 << 26))
+#define DEV_LST_COMP_MODE	(((p) & USB_STS_LST_MASK) == (0xa << 26))
+#define DEV_LST_LB_STATE	(((p) & USB_STS_LST_MASK) == (0xb << 26))
+/*
+ * DMA clock turn-off status.
+ * 0 - DMA clock is always on (default after hardware reset).
+ * 1 - DMA clock turn-off in U1, U2 and U3 (SS mode) is enabled.
+ */
+#define USB_STS_DMAOFF_MASK	BIT(30)
+#define USB_STS_DMAOFF(p)	((p) & USB_STS_DMAOFF_MASK)
+/*
+ * SFR Endian statuss.
+ * 0 - Little Endian order (default after hardware reset).
+ * 1 - Big Endian order.
+ */
+#define USB_STS_ENDIAN2_MASK	BIT(31)
+#define USB_STS_ENDIAN2(p)	((p) & USB_STS_ENDIAN2_MASK)
+
+/* USB_CMD -  bitmasks */
+/* Set Function Address */
+#define USB_CMD_SET_ADDR	BIT(0)
+/*
+ * Function Address This field is saved to the device only when the field
+ * SET_ADDR is set '1 ' during write to USB_CMD register.
+ * Software is responsible for entering the address of the device during
+ * SET_ADDRESS request service. This field should be set immediately after
+ * the SETUP packet is decoded, and prior to confirmation of the status phase
+ */
+#define USB_CMD_FADDR_MASK	GENMASK(7, 1)
+#define USB_CMD_FADDR(p)	(((p) << 1) & USB_CMD_FADDR_MASK)
+/* Send Function Wake Device Notification TP (used only in SS mode). */
+#define USB_CMD_SDNFW		BIT(8)
+/* Set Test Mode (used only in HS/FS mode). */
+#define USB_CMD_STMODE		BIT(9)
+/* Test mode selector (used only in HS/FS mode) */
+#define USB_STS_TMODE_SEL_MASK	GENMASK(11, 10)
+#define USB_STS_TMODE_SEL(p)	(((p) << 10) & USB_STS_TMODE_SEL_MASK)
+/*
+ *  Send Latency Tolerance Message Device Notification TP (used only
+ *  in SS mode).
+ */
+#define USB_CMD_SDNLTM		BIT(12)
+/* Send Custom Transaction Packet (used only in SS mode) */
+#define USB_CMD_SPKT		BIT(13)
+/*Device Notification 'Function Wake' - Interface value (only in SS mode. */
+#define USB_CMD_DNFW_INT_MASK	GENMASK(23, 16)
+#define USB_STS_DNFW_INT(p)	(((p) << 16) & USB_CMD_DNFW_INT_MASK)
+/*
+ * Device Notification 'Latency Tolerance Message' -373 BELT value [7:0]
+ * (used only in SS mode).
+ */
+#define USB_CMD_DNLTM_BELT_MASK	GENMASK(27, 16)
+#define USB_STS_DNLTM_BELT(p)	(((p) << 16) & USB_CMD_DNLTM_BELT_MASK)
+
+/* USB_ITPN - bitmasks */
+/*
+ * ITP(SS) / SOF (HS/FS) number
+ * In SS mode this field represent number of last ITP received from host.
+ * In HS/FS mode this field represent number of last SOF received from host.
+ */
+#define USB_ITPN_MASK		GENMASK(13, 0)
+#define USB_ITPN(p)		((p) & USB_ITPN_MASK)
+
+/* USB_LPM - bitmasks */
+/* Host Initiated Resume Duration. */
+#define USB_LPM_HIRD_MASK	GENMASK(3, 0)
+#define USB_LPM_HIRD(p)		((p) & USB_LPM_HIRD_MASK)
+/* Remote Wakeup Enable (bRemoteWake). */
+#define USB_LPM_BRW		BIT(4)
+
+/* USB_IEN - bitmasks */
+/* SS connection interrupt enable */
+#define USB_IEN_CONIEN		BIT(0)
+/* SS disconnection interrupt enable. */
+#define USB_IEN_DISIEN		BIT(1)
+/* USB SS warm reset interrupt enable. */
+#define USB_IEN_UWRESIEN	BIT(2)
+/* USB SS hot reset interrupt enable */
+#define USB_IEN_UHRESIEN	BIT(3)
+/* SS link U3 state enter interrupt enable (suspend).*/
+#define USB_IEN_U3ENTIEN	BIT(4)
+/* SS link U3 state exit interrupt enable (wakeup). */
+#define USB_IEN_U3EXTIEN	BIT(5)
+/* SS link U2 state enter interrupt enable.*/
+#define USB_IEN_U2ENTIEN	BIT(6)
+/* SS link U2 state exit interrupt enable.*/
+#define USB_IEN_U2EXTIEN	BIT(7)
+/* SS link U1 state enter interrupt enable.*/
+#define USB_IEN_U1ENTIEN	BIT(8)
+/* SS link U1 state exit interrupt enable.*/
+#define USB_IEN_U1EXTIEN	BIT(9)
+/* ITP/SOF packet detected interrupt enable.*/
+#define USB_IEN_ITPIEN		BIT(10)
+/* Wakeup interrupt enable.*/
+#define USB_IEN_WAKEIEN		BIT(11)
+/* Send Custom Packet interrupt enable.*/
+#define USB_IEN_SPKTIEN		BIT(12)
+/* HS/FS mode connection interrupt enable.*/
+#define USB_IEN_CON2IEN		BIT(16)
+/* HS/FS mode disconnection interrupt enable.*/
+#define USB_IEN_DIS2IEN		BIT(17)
+/* USB reset (HS/FS mode) interrupt enable.*/
+#define USB_IEN_U2RESIEN	BIT(18)
+/* LPM L2 state enter interrupt enable.*/
+#define USB_IEN_L2ENTIEN	BIT(20)
+/* LPM  L2 state exit interrupt enable.*/
+#define USB_IEN_L2EXTIEN	BIT(21)
+/* LPM L1 state enter interrupt enable.*/
+#define USB_IEN_L1ENTIEN	BIT(24)
+/* LPM  L1 state exit interrupt enable.*/
+#define USB_IEN_L1EXTIEN	BIT(25)
+/* Configuration reset interrupt enable.*/
+#define USB_IEN_CFGRESIEN	BIT(26)
+/* Start of the USB SS warm reset interrupt enable.*/
+#define USB_IEN_UWRESSIEN	BIT(28)
+/* End of the USB SS warm reset interrupt enable.*/
+#define USB_IEN_UWRESEIEN	BIT(29)
+
+#define USB_IEN_INIT  (USB_IEN_U2RESIEN | USB_ISTS_DIS2I | USB_IEN_CON2IEN \
+		       | USB_IEN_UHRESIEN | USB_IEN_UWRESIEN | USB_IEN_DISIEN \
+		       | USB_IEN_CONIEN | USB_IEN_U3EXTIEN | USB_IEN_L2ENTIEN \
+		       | USB_IEN_L2EXTIEN)
+
+/* USB_ISTS - bitmasks */
+/* SS Connection detected. */
+#define USB_ISTS_CONI		BIT(0)
+/* SS Disconnection detected. */
+#define USB_ISTS_DISI		BIT(1)
+/* UUSB warm reset detectede. */
+#define USB_ISTS_UWRESI		BIT(2)
+/* USB hot reset detected. */
+#define USB_ISTS_UHRESI		BIT(3)
+/* U3 link state enter detected (suspend).*/
+#define USB_ISTS_U3ENTI		BIT(4)
+/* U3 link state exit detected (wakeup). */
+#define USB_ISTS_U3EXTI		BIT(5)
+/* U2 link state enter detected.*/
+#define USB_ISTS_U2ENTI		BIT(6)
+/* U2 link state exit detected.*/
+#define USB_ISTS_U2EXTI		BIT(7)
+/* U1 link state enter detected.*/
+#define USB_ISTS_U1ENTI		BIT(8)
+/* U1 link state exit detected.*/
+#define USB_ISTS_U1EXTI		BIT(9)
+/* ITP/SOF packet detected.*/
+#define USB_ISTS_ITPI		BIT(10)
+/* Wakeup detected.*/
+#define USB_ISTS_WAKEI		BIT(11)
+/* Send Custom Packet detected.*/
+#define USB_ISTS_SPKTI		BIT(12)
+/* HS/FS mode connection detected.*/
+#define USB_ISTS_CON2I		BIT(16)
+/* HS/FS mode disconnection detected.*/
+#define USB_ISTS_DIS2I		BIT(17)
+/* USB reset (HS/FS mode) detected.*/
+#define USB_ISTS_U2RESI		BIT(18)
+/* LPM L2 state enter detected.*/
+#define USB_ISTS_L2ENTI		BIT(20)
+/* LPM  L2 state exit detected.*/
+#define USB_ISTS_L2EXTI		BIT(21)
+/* LPM L1 state enter detected.*/
+#define USB_ISTS_L1ENTI		BIT(24)
+/* LPM L1 state exit detected.*/
+#define USB_ISTS_L1EXTI		BIT(25)
+/* USB configuration reset detected.*/
+#define USB_ISTS_CFGRESI	BIT(26)
+/* Start of the USB warm reset detected.*/
+#define USB_ISTS_UWRESSI	BIT(28)
+/* End of the USB warm reset detected.*/
+#define USB_ISTS_UWRESEI	BIT(29)
+
+/* USB_SEL - bitmasks */
+#define EP_SEL_EPNO_MASK	GENMASK(3, 0)
+/* Endpoint number. */
+#define EP_SEL_EPNO(p)		((p) & EP_SEL_EPNO_MASK)
+/* Endpoint direction bit - 0 - OUT, 1 - IN. */
+#define EP_SEL_DIR		BIT(7)
+
+#define select_ep_in(nr)	(EP_SEL_EPNO(p) | EP_SEL_DIR)
+#define select_ep_out		(EP_SEL_EPNO(p))
+
+/* EP_TRADDR - bitmasks */
+/* Transfer Ring address. */
+#define EP_TRADDR_TRADDR(p)	((p))
+
+/* EP_CFG - bitmasks */
+/* Endpoint enable */
+#define EP_CFG_ENABLE		BIT(0)
+/*
+ *  Endpoint type.
+ * 1 - isochronous
+ * 2 - bulk
+ * 3 - interrupt
+ */
+#define EP_CFG_EPTYPE_MASK	GENMASK(2, 1)
+#define EP_CFG_EPTYPE(p)	(((p) << 1)  & EP_CFG_EPTYPE_MASK)
+/* Stream support enable (only in SS mode). */
+#define EP_CFG_STREAM_EN	BIT(3)
+/* TDL check (only in SS mode for BULK EP). */
+#define EP_CFG_TDL_CHK		BIT(4)
+/* SID check (only in SS mode for BULK OUT EP). */
+#define EP_CFG_SID_CHK		BIT(5)
+/* DMA transfer endianness. */
+#define EP_CFG_EPENDIAN		BIT(7)
+/* Max burst size (used only in SS mode). */
+#define EP_CFG_MAXBURST_MASK	GENMASK(11, 8)
+#define EP_CFG_MAXBURST(p)	(((p) << 8) & EP_CFG_MAXBURST_MASK)
+/* ISO max burst. */
+#define EP_CFG_MULT_MASK	GENMASK(15, 14)
+#define EP_CFG_MULT(p)		(((p) << 14) & EP_CFG_MULT)
+/* ISO max burst. */
+#define EP_CFG_MAXPKTSIZE_MASK	GENMASK(26, 16)
+#define EP_CFG_MAXPKTSIZE(p)	(((p) << 16) & EP_CFG_MAXPKTSIZE_MASK)
+/* Max number of buffered packets. */
+#define EP_CFG_BUFFERING_MASK	GENMASK(31, 27)
+#define EP_CFG_BUFFERING(p)	(((p) << 27) & EP_CFG_BUFFERING_MASK)
+
+/* EP_CMD - bitmasks */
+/* Endpoint reset. */
+#define EP_CMD_EPRST		BIT(0)
+/* Endpoint STALL set. */
+#define EP_CMD_SSTALL		BIT(1)
+/* Endpoint STALL clear. */
+#define EP_CMD_CSTALL		BIT(2)
+/* Send ERDY TP. */
+#define EP_CMD_ERDY		BIT(3)
+/* Request complete. */
+#define EP_CMD_REQ_CMPL		BIT(5)
+/* Transfer descriptor ready. */
+#define EP_CMD_DRDY		BIT(6)
+/* Data flush. */
+#define EP_CMD_DFLUSH		BIT(7)
+/*
+ * Transfer Descriptor Length write  (used only for Bulk Stream capable
+ * endpoints in SS mode).
+ */
+#define EP_CMD_STDL		BIT(8)
+/* Transfer Descriptor Length (used only in SS mode for bulk endpoints). */
+#define EP_CMD_TDL_MASK		GENMASK(15, 9)
+#define EP_CMD_TDL(p)		(((p) << 9) & EP_CMD_TDL_MASK)
+/* ERDY Stream ID value (used in SS mode). */
+#define EP_CMD_ERDY_SID_MASK	GENMASK(31, 16)
+#define EP_CMD_ERDY_SID(p)	(((p) << 16) & EP_CMD_SID_MASK)
+
+/* EP_STS - bitmasks */
+/* Setup transfer complete. */
+#define EP_STS_SETUP		BIT(0)
+/* Endpoint STALL status. */
+#define EP_STS_STALL(p)		((p) & BIT(1))
+/* Interrupt On Complete. */
+#define EP_STS_IOC		BIT(2)
+/* Interrupt on Short Packet. */
+#define EP_STS_ISP		BIT(3)
+/* Transfer descriptor missing. */
+#define EP_STS_DESCMIS		BIT(4)
+/* Stream Rejected (used only in SS mode) */
+#define EP_STS_STREAMR		BIT(5)
+/* EXIT from MOVE DATA State (used only for stream transfers in SS mode). */
+#define EP_STS_MD_EXIT		BIT(6)
+/* TRB error. */
+#define EP_STS_TRBERR		BIT(7)
+/* Not ready (used only in SS mode). */
+#define EP_STS_NRDY		BIT(8)
+/* DMA busy. */
+#define EP_STS_DBUSY(p)		((p) & BIT(9))
+/* Endpoint Buffer Empty */
+#define EP_STS_BUFFEMPTY(p)	((p) & BIT(10))
+/* Current Cycle Status */
+#define EP_STS_CCS(p)		((p) & BIT(11))
+/* Prime (used only in SS mode. */
+#define EP_STS_PRIME		BIT(12)
+/* Stream error (used only in SS mode). */
+#define EP_STS_SIDERR		BIT(13)
+/* OUT size mismatch. */
+#define EP_STS_OUTSMM		BIT(14)
+/* ISO transmission error. */
+#define EP_STS_ISOERR		BIT(15)
+/* Host Packet Pending (only for SS mode). */
+#define EP_STS_HOSTPP(p)	((p) & BIT(16))
+/* Stream Protocol State Machine State (only for Bulk stream endpoints). */
+#define EP_STS_SPSMST_MASK		GENMASK(18, 17)
+#define EP_STS_SPSMST_DISABLED(p)	(((p) & EP_STS_SPSMST_MASK) >> 17)
+#define EP_STS_SPSMST_IDLE(p)		(((p) & EP_STS_SPSMST_MASK) >> 17)
+#define EP_STS_SPSMST_START_STREAM(p)	(((p) & EP_STS_SPSMST_MASK) >> 17)
+#define EP_STS_SPSMST_MOVE_DATA(p)	(((p) & EP_STS_SPSMST_MASK) >> 17)
+/* Interrupt On Transfer complete. */
+#define EP_STS_IOT		BIT(19)
+/* OUT queue endpoint number. */
+#define EP_STS_OUTQ_NO_MASK	GENMASK(27, 24)
+#define EP_STS_OUTQ_NO(p)	(((p) & EP_STS_OUTQ_NO_MASK) >> 24)
+/* OUT queue valid flag. */
+#define EP_STS_OUTQ_VAL_MASK	BIT(28)
+#define EP_STS_OUTQ_VAL(p)	((p) & EP_STS_OUTQ_VAL_MASK)
+/* SETUP WAIT. */
+#define EP_STS_STPWAIT		BIT(31)
+
+/* EP_STS_SID - bitmasks */
+/* Stream ID (used only in SS mode). */
+#define EP_STS_SID_MASK		GENMASK(15, 0)
+#define EP_STS_SID(p)		((p) & EP_STS_SID_MASK)
+
+/* EP_STS_EN - bitmasks */
+/* SETUP interrupt enable. */
+#define EP_STS_EN_SETUPEN	BIT(0)
+/* OUT transfer missing descriptor enable. */
+#define EP_STS_EN_DESCMISEN	BIT(4)
+/* Stream Rejected enable. */
+#define EP_STS_EN_STREAMREN	BIT(5)
+/* Move Data Exit enable.*/
+#define EP_STS_EN_MD_EXITEN	BIT(6)
+/* TRB enable. */
+#define EP_STS_EN_TRBERREN	BIT(7)
+/* NRDY enable. */
+#define EP_STS_EN_NRDYEN	BIT(8)
+/* Prime enable. */
+#define EP_STS_EN_PRIMEEEN	BIT(12)
+/* Stream error enable. */
+#define EP_STS_EN_SIDERREN	BIT(13)
+/* OUT size mismatch enable. */
+#define EP_STS_EN_OUTSMMEN	BIT(14)
+/* ISO transmission error enable. */
+#define EP_STS_EN_ISOERREN	BIT(15)
+/* Interrupt on Transmission complete enable. */
+#define EP_STS_EN_IOTEN		BIT(19)
+/* Setup Wait interrupt enable. */
+#define EP_STS_EN_STPWAITEN	BIT(31)
+
+/* DRBL- bitmasks */
+#define DB_VALUE_BY_INDEX(index) (1 << (index))
+#define DB_VALUE_EP0_OUT	BIT(0)
+#define DB_VALUE_EP0_IN		BIT(16)
+
+/* EP_IEN - bitmasks */
+#define EP_IEN(index)		(1 << (index))
+#define EP_IEN_EP_OUT0		BIT(0)
+#define EP_IEN_EP_IN0		BIT(16)
+
+/* EP_ISTS - bitmasks */
+#define EP_ISTS(index)		(1 << (index))
+#define EP_ISTS_EP_OUT0		BIT(0)
+#define EP_ISTS_EP_IN0		BIT(16)
+
+/* EP_PWR- bitmasks */
+/*Power Shut Off capability enable*/
+#define PUSB_PWR_PSO_EN		BIT(0)
+/*Power Shut Off capability disable*/
+#define PUSB_PWR_PSO_DS		BIT(1)
+/*
+ * Enables turning-off Reference Clock.
+ * This bit is optional and implemented only when support for OTG is
+ * implemented (indicated by OTG_READY bit set to '1').
+ */
+#define PUSB_PWR_STB_CLK_SWITCH_EN	BIT(8)
+/*
+ * Status bit indicating that operation required by STB_CLK_SWITCH_EN write
+ * is completed
+ */
+#define PUSB_PWR_STB_CLK_SWITCH_DONE	BIT(9)
+/* This bit informs if Fast Registers Access is enabled. */
+#define PUSB_PWR_FST_REG_ACCESS_STAT	BIT(30)
+/* Fast Registers Access Enable. */
+#define PUSB_PWR_FST_REG_ACCESS	BIT(31)
+
+/* USB_CAP1- bitmasks */
+/*
+ * SFR Interface type
+ * These field reflects type of SFR interface implemented:
+ * 0x0 - OCP
+ * 0x1 - AHB,
+ * 0x2 - PLB
+ * 0x3 - AXI
+ * 0x4-0xF - reserved
+ */
+#define USB_CAP1_SFR_TYPE_MASK	GENMASK(3, 0)
+#define DEV_SFR_TYPE_OCP(p)	(((p) & USB_CAP1_SFR_TYPE_MASK) == 0x0)
+#define DEV_SFR_TYPE_AHB(p)	(((p) & USB_CAP1_SFR_TYPE_MASK) == 0x1)
+#define DEV_SFR_TYPE_PLB(p)	(((p) & USB_CAP1_SFR_TYPE_MASK) == 0x2)
+#define DEV_SFR_TYPE_AXI(p)	(((p) & USB_CAP1_SFR_TYPE_MASK) == 0x3)
+/*
+ * SFR Interface width
+ * These field reflects width of SFR interface implemented:
+ * 0x0 - 8 bit interface,
+ * 0x1 - 16 bit interface,
+ * 0x2 - 32 bit interface
+ * 0x3 - 64 bit interface
+ * 0x4-0xF - reserved
+ */
+#define USB_CAP1_SFR_WIDTH_MASK	GENMASK(7, 4)
+#define DEV_SFR_WIDTH_8(p)	(((p) & USB_CAP1_SFR_WIDTH_MASK) == (0x0 << 4))
+#define DEV_SFR_WIDTH_16(p)	(((p) & USB_CAP1_SFR_WIDTH_MASK) == (0x1 << 4))
+#define DEV_SFR_WIDTH_32(p)	(((p) & USB_CAP1_SFR_WIDTH_MASK) == (0x2 << 4))
+#define DEV_SFR_WIDTH_64(p)	(((p) & USB_CAP1_SFR_WIDTH_MASK) == (0x3 << 4))
+/*
+ * DMA Interface type
+ * These field reflects type of DMA interface implemented:
+ * 0x0 - OCP
+ * 0x1 - AHB,
+ * 0x2 - PLB
+ * 0x3 - AXI
+ * 0x4-0xF - reserved
+ */
+#define USB_CAP1_DMA_TYPE_MASK	GENMASK(11, 8)
+#define DEV_DMA_TYPE_OCP(p)	(((p) & USB_CAP1_DMA_TYPE_MASK) == (0x0 << 8))
+#define DEV_DMA_TYPE_AHB(p)	(((p) & USB_CAP1_DMA_TYPE_MASK) == (0x1 << 8))
+#define DEV_DMA_TYPE_PLB(p)	(((p) & USB_CAP1_DMA_TYPE_MASK) == (0x2 << 8))
+#define DEV_DMA_TYPE_AXI(p)	(((p) & USB_CAP1_DMA_TYPE_MASK) == (0x3 << 8))
+/*
+ * DMA Interface width
+ * These field reflects width of DMA interface implemented:
+ * 0x0 - reserved,
+ * 0x1 - reserved,
+ * 0x2 - 32 bit interface
+ * 0x3 - 64 bit interface
+ * 0x4-0xF - reserved
+ */
+#define USB_CAP1_DMA_WIDTH_MASK	GENMASK(15, 12)
+#define DEV_DMA_WIDTH_32(p)	(((p) & USB_CAP1_DMA_WIDTH_MASK) == (0x2 << 12))
+#define DEV_DMA_WIDTH_64(p)	(((p) & USB_CAP1_DMA_WIDTH_MASK) == (0x3 << 12))
+/*
+ * USB3 PHY Interface type
+ * These field reflects type of USB3 PHY interface implemented:
+ * 0x0 - USB PIPE,
+ * 0x1 - RMMI,
+ * 0x2-0xF - reserved
+ */
+#define USB_CAP1_U3PHY_TYPE_MASK GENMASK(19, 16)
+#define DEV_U3PHY_PIPE(p) (((p) & USB_CAP1_U3PHY_TYPE_MASK) == (0x0 << 16))
+#define DEV_U3PHY_RMMI(p) (((p) & USB_CAP1_U3PHY_TYPE_MASK) == (0x1 << 16))
+/*
+ * USB3 PHY Interface width
+ * These field reflects width of USB3 PHY interface implemented:
+ * 0x0 - 8 bit PIPE interface,
+ * 0x1 - 16 bit PIPE interface,
+ * 0x2 - 32 bit PIPE interface,
+ * 0x3 - 64 bit PIPE interface
+ * 0x4-0xF - reserved
+ * Note: When SSIC interface is implemented this field shows the width of
+ * internal PIPE interface. The RMMI interface is always 20bit wide.
+ */
+#define USB_CAP1_U3PHY_WIDTH_MASK GENMASK(23, 20)
+#define DEV_U3PHY_WIDTH_8(p) \
+	(((p) & USB_CAP1_U3PHY_WIDTH_MASK) == (0x0 << 20))
+#define DEV_U3PHY_WIDTH_16(p) \
+	(((p) & USB_CAP1_U3PHY_WIDTH_MASK) == (0x1 << 16))
+#define DEV_U3PHY_WIDTH_32(p) \
+	(((p) & USB_CAP1_U3PHY_WIDTH_MASK) == (0x2 << 20))
+#define DEV_U3PHY_WIDTH_64(p) \
+	(((p) & USB_CAP1_U3PHY_WIDTH_MASK) == (0x3 << 16))
+
+/*
+ * USB2 PHY Interface enable
+ * These field informs if USB2 PHY interface is implemented:
+ * 0x0 - interface NOT implemented,
+ * 0x1 - interface implemented
+ */
+#define USB_CAP1_U2PHY_EN(p)	((p) & BIT(24))
+/*
+ * USB2 PHY Interface type
+ * These field reflects type of USB2 PHY interface implemented:
+ * 0x0 - UTMI,
+ * 0x1 - ULPI
+ */
+#define DEV_U2PHY_ULPI(p)	((p) & BIT(25))
+/*
+ * USB2 PHY Interface width
+ * These field reflects width of USB2 PHY interface implemented:
+ * 0x0 - 8 bit interface,
+ * 0x1 - 16 bit interface,
+ * Note: The ULPI interface is always 8bit wide.
+ */
+#define DEV_U2PHY_WIDTH_16(p)	((p) & BIT(26))
+/*
+ * OTG Ready
+ * 0x0 - pure device mode
+ * 0x1 - some features and ports for CDNS USB OTG controller are implemented.
+ */
+#define USB_CAP1_OTG_READY(p)	((p) & BIT(27))
+
+/* USB_CAP2- bitmasks */
+/*
+ * The actual size of the connected On-chip RAM memory in kB:
+ * - 0 means 256 kB (max supported mem size)
+ * - value other than 0 reflects the mem size in kB
+ */
+#define USB_CAP2_ACTUAL_MEM_SIZE(p) ((p) & GENMASK(7, 0))
+/*
+ * Max supported mem size
+ * These field reflects width of on-chip RAM address bus width,
+ * which determines max supported mem size:
+ * 0x0-0x7 - reserved,
+ * 0x8 - support for 4kB mem,
+ * 0x9 - support for 8kB mem,
+ * 0xA - support for 16kB mem,
+ * 0xB - support for 32kB mem,
+ * 0xC - support for 64kB mem,
+ * 0xD - support for 128kB mem,
+ * 0xE - support for 256kB mem,
+ * 0xF - reserved
+ */
+#define USB_CAP2_MAX_MEM_SIZE(p) ((p) & GENMASK(11, 8))
+
+/* USB_CAP3- bitmasks */
+#define EP_IS_IMPLEMENTED(reg, index) ((reg) & (1 << (index)))
+
+/* USB_CAP4- bitmasks */
+#define EP_SUPPORT_ISO(reg, index) ((reg) & (1 << (index)))
+
+/* USB_CAP5- bitmasks */
+#define EP_SUPPORT_STREAM(reg, index) ((reg) & (1 << (index)))
+
+/* USB_CAP6- bitmasks */
+/* The USBSS-DEV Controller  Internal build number. */
+#define GET_DEV_VERSION__INTERNAL_NUMBER(p) ((p) & GENMASK(7, 0))
+/* The USBSS-DEV Controller  version number. */
+#define GET_DEV_VERSION(p) ((p) & GENMASK(31, 8))
+
+/* DBG_LINK1- bitmasks */
+/*
+ * LFPS_MIN_DET_U1_EXIT value This parameter configures the minimum
+ * time required for decoding the received LFPS as an LFPS.U1_Exit.
+ */
+#define DBG_LINK1_LFPS_MIN_DET_U1_EXIT(p)	((p) & GENMASK(7, 0))
+/*
+ * LFPS_MIN_GEN_U1_EXIT value This parameter configures the minimum time for
+ * phytxelecidle deassertion when LFPS.U1_Exit
+ */
+#define DBG_LINK1_LFPS_MIN_GEN_U1_EXIT(p)	(((p) << 8) & GENMASK(15, 8))
+/*
+ * RXDET_BREAK_DIS value This parameter configures terminating the Far-end
+ * Receiver termination detection sequence:
+ * 0: it is possible that USBSS_DEV will terminate Farend receiver
+ *    termination detection sequence
+ * 1: USBSS_DEV will not terminate Far-end receiver termination
+ *    detection sequence
+ */
+#define DBG_LINK1_RXDET_BREAK_DIS		BIT(16)
+/* LFPS_GEN_PING value This parameter configures the LFPS.Ping generation */
+#define DBG_LINK1_LFPS_GEN_PING(p)		(((p) << 17) & GENMASK(21, 17))
+/*
+ * Set the LFPS_MIN_DET_U1_EXIT value Writing '1' to this bit writes the
+ * LFPS_MIN_DET_U1_EXIT field value to the device. This bit is automatically
+ * cleared. Writing '0' has no effect
+ */
+#define DBG_LINK1_LFPS_MIN_DET_U1_EXIT_SET	BIT(24)
+/*
+ * Set the LFPS_MIN_GEN_U1_EXIT value. Writing '1' to this bit writes the
+ * LFPS_MIN_GEN_U1_EXIT field value to the device. This bit is automatically
+ * cleared. Writing '0' has no effect
+ */
+#define DBG_LINK1_LFPS_MIN_GEN_U1_EXIT_SET	BIT(25)
+/*
+ * Set the RXDET_BREAK_DIS value Writing '1' to this bit writes
+ * the RXDET_BREAK_DIS field value to the device. This bit is automatically
+ * cleared. Writing '0' has no effect
+ */
+#define DBG_LINK1_RXDET_BREAK_DIS_SET		BIT(26)
+/*
+ * Set the LFPS_GEN_PING_SET value Writing '1' to this bit writes
+ * the LFPS_GEN_PING field value to the device. This bit is automatically
+ * cleared. Writing '0' has no effect."
+ */
+#define DBG_LINK1_LFPS_GEN_PING_SET		BIT(27)
+
+#define gadget_to_cdns3_device(g) (container_of(g, struct cdns3_device, gadget))
+
+#define ep_to_cdns3_ep(ep) (container_of(ep, struct cdns3_endpoint, endpoint))
+
+/*-------------------------------------------------------------------------*/
+/*
+ * USBSS-DEV DMA interface.
+ */
+#define TRBS_PER_SEGMENT	40
+
+#define ISO_MAX_INTERVAL	10
+
+/*
+ *Only for ISOC endpoints - maximum number of TRBs is calculated as
+ * pow(2, bInterval-1) * number of usb requests. It is limitation made by
+ * driver to save memory. Controller must prepare TRB for each ITP even
+ * if bInterval > 1. It's the reason why driver needs so many TRBs for
+ * isochronous endpoints.
+ */
+#define TRBS_PER_ISOC_SEGMENT	(ISO_MAX_INTERVAL * 8)
+
+#define GET_TRBS_PER_SEGMENT(ep_type) ((ep_type) == USB_ENDPOINT_XFER_ISOC ? \
+				      TRBS_PER_ISOC_SEGMENT : TRBS_PER_SEGMENT)
+/**
+ * struct cdns3_trb - represent Transfer Descriptor block.
+ * @buffer:	pointer to buffer data
+ * @length:	length of data
+ * @control:	control flags.
+ *
+ * This structure describes transfer block serviced by DMA module.
+ */
+struct cdns3_trb {
+	__le32 buffer;
+	__le32 length;
+	__le32 control;
+};
+
+#define TRB_SIZE		(sizeof(struct cdns3_trb))
+#define TRB_RING_SIZE		(TRB_SIZE * TRBS_PER_SEGMENT)
+#define TRB_ISO_RING_SIZE	(TRB_SIZE * TRBS_PER_ISOC_SEGMENT)
+#define TRB_CTRL_RING_SIZE	(TRB_SIZE * 2)
+
+/* TRB bit mask */
+#define TRB_TYPE_BITMASK	GENMASK(15, 10)
+#define TRB_TYPE(p)		((p) << 10)
+#define TRB_FIELD_TO_TYPE(p)	(((p) & TRB_TYPE_BITMASK) >> 10)
+
+/* TRB type IDs */
+/* bulk, interrupt, isoc , and control data stage */
+#define TRB_NORMAL		1
+/* TRB for linking ring segments */
+#define TRB_LINK		6
+
+/* Cycle bit - indicates TRB ownership by driver or hw*/
+#define TRB_CYCLE		BIT(0)
+/*
+ * When set to '1', the device will toggle its interpretation of the Cycle bit
+ */
+#define TRB_TOGGLE		BIT(1)
+
+/* Interrupt on short packet*/
+#define TRB_ISP			BIT(2)
+/*Setting this bit enables FIFO DMA operation mode*/
+#define TRB_FIFO_MODE		BIT(3)
+/* Set PCIe no snoop attribute */
+#define TRB_CHAIN		BIT(4)
+/* Interrupt on completion */
+#define TRB_IOC			BIT(5)
+
+/* stream ID bitmasks. */
+#define TRB_STREAM_ID(p)	((p) & GENMASK(31, 16))
+
+/* transfer_len bitmasks. */
+#define TRB_LEN(p)		((p) & GENMASK(16, 0))
+
+/* transfer_len bitmasks - bits 31:24 */
+#define TRB_BURST_LEN(p)	((p) & GENMASK(31, 24))
+
+/* Data buffer pointer bitmasks*/
+#define TRB_BUFFER(p)		((p) & GENMASK(31, 0))
+
+/*-------------------------------------------------------------------------*/
+/* Driver numeric constants */
+
+/* Such declaration should be added to ch9.h */
+#define USB_DEVICE_MAX_ADDRESS		127
+
+/* Endpoint init values */
+#define CDNS3_EP_MAX_PACKET_LIMIT	1024
+#define CDNS3_EP_MAX_STREAMS		15
+#define CDNS3_EP0_MAX_PACKET_LIMIT	512
+
+/* All endpoints including EP0 */
+#define CDNS3_ENDPOINTS_MAX_COUNT	32
+#define CDNS3_EP_ZLP_BUF_SIZE		1024
+
+#define CDNS3_EP_BUF_SIZE		2	/* KB */
+#define CDNS3_ALIGNED_BUF_SIZE		16384	/* Bytes */
+#define CDNS3_MAX_NUM_DESCMISS_BUF	32
+#define CDNS3_DESCMIS_BUF_SIZE		2048	/* Bytes */
+/*-------------------------------------------------------------------------*/
+/* Used structs */
+
+struct cdns3_device;
+
+/**
+ * struct cdns3_endpoint - extended device side representation of USB endpoint.
+ * @endpoint: usb endpoint
+ * @pending_req_list: list of requests queuing on transfer ring.
+ * @deferred_req_list: list of requests waiting for queuing on transfer ring.
+ * @descmiss_req_list: list of requests internally allocated by driver (WA1).
+ * @trb_pool: transfer ring - array of transaction buffers
+ * @trb_pool_dma: dma address of transfer ring
+ * @cdns3_dev: device associated with this endpoint
+ * @name: a human readable name e.g. ep1out
+ * @flags: specify the current state of endpoint
+ * @descmis_req: internal transfer object used for getting data from on-chip
+ *     buffer. It can happen only if function driver doesn't send usb_request
+ *     object on time.
+ * @aligned_buff: aligned to 8 bytes data buffer. Buffer address used in
+ *     TRB shall be aligned to 8.
+ * @aligned_dma_addr: dma address of aligned_buff
+ * @dir: endpoint direction
+ * @num: endpoint number (1 - 15)
+ * @type: set to bmAttributes & USB_ENDPOINT_XFERTYPE_MASK
+ * @interval: interval between packets used for ISOC endpoint.
+ * @free_trbs: number of free TRBs in transfer ring
+ * @num_trbs: number of all TRBs in transfer ring
+ * @pcs: producer cycle state
+ * @ccs: consumer cycle state
+ * @enqueue: enqueue index in transfer ring
+ * @dequeue: dequeue index in transfer ring
+ */
+struct cdns3_endpoint {
+	struct usb_ep		endpoint;
+	struct list_head	pending_req_list;
+	struct list_head	deferred_req_list;
+	struct list_head	descmiss_req_list;
+
+	struct cdns3_trb	*trb_pool;
+	dma_addr_t		trb_pool_dma;
+
+	struct cdns3_device	*cdns3_dev;
+	char			name[20];
+
+#define EP_ENABLED		BIT(0)
+#define EP_STALL		BIT(1)
+#define EP_WEDGE		BIT(2)
+#define EP_TRANSFER_STARTED	BIT(3)
+#define EP_UPDATE_EP_TRBADDR	BIT(4)
+#define EP_PENDING_REQUEST	BIT(5)
+#define EP_RING_FULL		BIT(6)
+#define EP_CLAIMED		BIT(7)
+#define EP_QUIRK_EXTRA_BUF_DET	BIT(8)
+#define EP_QUIRK_EXTRA_BUF_EN	BIT(9)
+#define EP_QUIRK_END_TRANSFER	BIT(10)
+
+	u32			flags;
+
+	struct cdns3_request	*descmis_req;
+
+	void			*aligned_buff;
+	dma_addr_t		aligned_dma_addr;
+	u8			dir;
+	u8			num;
+	u8			type;
+	int			interval;
+
+	int			free_trbs;
+	int			num_trbs;
+	u8			pcs;
+	u8			ccs;
+	int			enqueue;
+	int			dequeue;
+};
+
+/**
+ * struct cdns3_request - extended device side representation of usb_request
+ *                        object .
+ * @request: generic usb_request object describing single I/O request.
+ * @priv_ep: extended representation of usb_ep object
+ * @trb: the first TRB association with this request
+ * @start_trb: number of the first TRB in transfer ring
+ * @end_trb: number of the last TRB in transfer ring
+ * @flags: flag specifying special usage of request
+ * @list: used by internally allocated request to add to descmiss_req_list.
+ */
+struct cdns3_request {
+	struct usb_request	request;
+	struct cdns3_endpoint	*priv_ep;
+	struct cdns3_trb	*trb;
+	int			start_trb;
+	int			end_trb;
+#define REQUEST_PENDING		BIT(0)
+#define REQUEST_INTERNAL	BIT(1)
+#define REQUEST_INTERNAL_CH	BIT(2)
+#define REQUEST_ZLP		BIT(3)
+	u32			flags;
+	struct list_head	list;
+};
+
+#define to_cdns3_request(r) (container_of(r, struct cdns3_request, request))
+
+/*Stages used during enumeration process.*/
+#define CDNS3_SETUP_STAGE		0x0
+#define CDNS3_DATA_STAGE		0x1
+#define CDNS3_STATUS_STAGE		0x2
+
+/**
+ * struct cdns3_device - represent USB device.
+ * @dev: pointer to device structure associated whit this controller
+ * @sysdev: pointer to the DMA capable device
+ * @gadget: device side representation of the peripheral controller
+ * @gadget_driver: pointer to the gadget driver
+ * @dev_ver: device controller version.
+ * @lock: for synchronizing
+ * @regs: base address for device side registers
+ * @setup_buf: used while processing usb control requests
+ * @setup_dma: dma address for setup_buf
+ * @zlp_buf - zlp buffer
+ * @ep0_stage: ep0 stage during enumeration process.
+ * @ep0_data_dir: direction for control transfer
+ * @eps: array of pointers to all endpoints with exclusion ep0
+ * @selected_ep: actually selected endpoint. It's used only to improve
+ *               performance.
+ * @isoch_delay: value from Set Isoch Delay request. Only valid on SS/SSP.
+ * @u1_allowed: allow device transition to u1 state
+ * @u2_allowed: allow device transition to u2 state
+ * @is_selfpowered: device is self powered
+ * @setup_pending: setup packet is processing by gadget driver
+ * @hw_configured_flag: hardware endpoint configuration was set.
+ * @wake_up_flag: allow device to remote up the host
+ * @status_completion_no_call: indicate that driver is waiting for status s
+ *     stage completion. It's used in deferred SET_CONFIGURATION request.
+ * @onchip_mem_allocated_size: actual size of on-chip memory assigned
+ *     to endpoints
+ * @pending_status_wq: workqueue handling status stage for deferred requests.
+ * @pending_status_request: request for which status stage was deferred
+ */
+struct cdns3_device {
+	struct device			*dev;
+	struct device			*sysdev;
+
+	struct usb_gadget		gadget;
+	struct usb_gadget_driver	*gadget_driver;
+
+#define CDNS_REVISION_V0		0x00024501
+#define CDNS_REVISION_V1		0x00024509
+	u32				dev_ver;
+
+	/* generic spin-lock for drivers */
+	spinlock_t			lock;
+
+	struct cdns3_usb_regs		__iomem *regs;
+
+	struct usb_ctrlrequest		*setup_buf;
+	dma_addr_t			setup_dma;
+	void				*zlp_buf;
+
+	u8				ep0_stage;
+	int				ep0_data_dir;
+
+	struct cdns3_endpoint		*eps[CDNS3_ENDPOINTS_MAX_COUNT];
+
+	u32				selected_ep;
+	u16				isoch_delay;
+
+	unsigned			u1_allowed:1;
+	unsigned			u2_allowed:1;
+	unsigned			is_selfpowered:1;
+	unsigned			setup_pending:1;
+	int				hw_configured_flag:1;
+	int				wake_up_flag:1;
+	unsigned			status_completion_no_call:1;
+
+	struct work_struct		pending_status_wq;
+	struct usb_request		*pending_status_request;
+
+	/*in KB */
+	int				onchip_mem_allocated_size;
+};
+
+int cdns3_handshake(void __iomem *ptr, u32 mask, u32 done, int usec);
+void cdns3_set_register_bit(void __iomem *ptr, u32 mask);
+dma_addr_t cdns3_trb_virt_to_dma(struct cdns3_endpoint *priv_ep,
+				 struct cdns3_trb *trb);
+enum usb_device_speed cdns3_get_speed(struct cdns3_device *priv_dev);
+void cdns3_pending_setup_status_handler(struct work_struct *work);
+void cdns3_hw_reset_eps_config(struct cdns3_device *priv_dev);
+void cdns3_set_hw_configuration(struct cdns3_device *priv_dev);
+void cdns3_select_ep(struct cdns3_device *priv_dev, u32 ep);
+void cdns3_allow_enable_l1(struct cdns3_device *priv_dev, int enable);
+struct usb_request *cdns3_next_request(struct list_head *list);
+int cdns3_ep_run_transfer(struct cdns3_endpoint *priv_ep,
+			  struct usb_request *request);
+int cdns3_allocate_trb_pool(struct cdns3_endpoint *priv_ep);
+u8 cdns3_ep_addr_to_index(u8 ep_addr);
+int cdns3_gadget_ep_set_wedge(struct usb_ep *ep);
+int cdns3_gadget_ep_set_halt(struct usb_ep *ep, int value);
+struct usb_request *cdns3_gadget_ep_alloc_request(struct usb_ep *ep,
+						  gfp_t gfp_flags);
+void cdns3_gadget_ep_free_request(struct usb_ep *ep,
+				  struct usb_request *request);
+int cdns3_gadget_ep_dequeue(struct usb_ep *ep, struct usb_request *request);
+void cdns3_gadget_giveback(struct cdns3_endpoint *priv_ep,
+			   struct cdns3_request *priv_req,
+			   int status);
+
+int cdns3_init_ep0(struct cdns3_device *priv_dev,
+		   struct cdns3_endpoint *priv_ep);
+void cdns3_ep0_config(struct cdns3_device *priv_dev);
+void cdns3_ep_config(struct cdns3_endpoint *priv_ep);
+void cdns3_check_ep0_interrupt_proceed(struct cdns3_device *priv_dev, int dir);
+
+#endif /* __LINUX_CDNS3_GADGET */
diff --git a/drivers/usb/cdns3/host-export.h b/drivers/usb/cdns3/host-export.h
new file mode 100644
index 000000000000..b498a170b7e8
--- /dev/null
+++ b/drivers/usb/cdns3/host-export.h
@@ -0,0 +1,28 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Cadence USBSS DRD Driver - Host Export APIs
+ *
+ * Copyright (C) 2017-2018 NXP
+ *
+ * Authors: Peter Chen <peter.chen@nxp.com>
+ */
+#ifndef __LINUX_CDNS3_HOST_EXPORT
+#define __LINUX_CDNS3_HOST_EXPORT
+
+#ifdef CONFIG_USB_CDNS3_HOST
+
+int cdns3_host_init(struct cdns3 *cdns);
+void cdns3_host_exit(struct cdns3 *cdns);
+
+#else
+
+static inline int cdns3_host_init(struct cdns3 *cdns)
+{
+	return -ENXIO;
+}
+
+static inline void cdns3_host_exit(struct cdns3 *cdns) { }
+
+#endif /* CONFIG_USB_CDNS3_HOST */
+
+#endif /* __LINUX_CDNS3_HOST_EXPORT */
diff --git a/drivers/usb/cdns3/host.c b/drivers/usb/cdns3/host.c
new file mode 100644
index 000000000000..b43b0236a885
--- /dev/null
+++ b/drivers/usb/cdns3/host.c
@@ -0,0 +1,72 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Cadence USBSS DRD Driver - host side
+ *
+ * Copyright (C) 2018 Cadence Design Systems.
+ * Copyright (C) 2017-2018 NXP
+ *
+ * Authors: Peter Chen <peter.chen@nxp.com>
+ *	    Pawel Laszczak <pawell@cadence.com>
+ */
+
+#include <linux/platform_device.h>
+#include "core.h"
+
+static int __cdns3_host_init(struct cdns3 *cdns)
+{
+	struct platform_device *xhci;
+	int ret;
+
+	xhci = platform_device_alloc("xhci-hcd", PLATFORM_DEVID_AUTO);
+	if (!xhci) {
+		dev_err(cdns->dev, "couldn't allocate xHCI device\n");
+		return -ENOMEM;
+	}
+
+	xhci->dev.parent = cdns->dev;
+	cdns->host_dev = xhci;
+
+	ret = platform_device_add_resources(xhci, cdns->xhci_res,
+					    CDNS3_XHCI_RESOURCES_NUM);
+	if (ret) {
+		dev_err(cdns->dev, "couldn't add resources to xHCI device\n");
+		goto err1;
+	}
+
+	ret = platform_device_add(xhci);
+	if (ret) {
+		dev_err(cdns->dev, "failed to register xHCI device\n");
+		goto err1;
+	}
+
+	return 0;
+err1:
+	platform_device_put(xhci);
+	return ret;
+}
+
+static void cdns3_host_exit(struct cdns3 *cdns)
+{
+	platform_device_unregister(cdns->host_dev);
+	cdns->host_dev = NULL;
+}
+
+int cdns3_host_init(struct cdns3 *cdns)
+{
+	struct cdns3_role_driver *rdrv;
+
+	rdrv = devm_kzalloc(cdns->dev, sizeof(*rdrv), GFP_KERNEL);
+	if (!rdrv)
+		return -ENOMEM;
+
+	rdrv->start	= __cdns3_host_init;
+	rdrv->stop	= cdns3_host_exit;
+	rdrv->state	= CDNS3_ROLE_STATE_INACTIVE;
+	rdrv->suspend	= NULL;
+	rdrv->resume	= NULL;
+	rdrv->name	= "host";
+
+	cdns->roles[CDNS3_ROLE_HOST] = rdrv;
+
+	return 0;
+}
diff --git a/drivers/usb/cdns3/trace.c b/drivers/usb/cdns3/trace.c
new file mode 100644
index 000000000000..587ae08e019d
--- /dev/null
+++ b/drivers/usb/cdns3/trace.c
@@ -0,0 +1,11 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * USBSS device controller driver Trace Support
+ *
+ * Copyright (C) 2018 Cadence.
+ *
+ * Author: Pawel Laszczak <pawell@cadence.com>
+ */
+
+#define CREATE_TRACE_POINTS
+#include "trace.h"
diff --git a/drivers/usb/cdns3/trace.h b/drivers/usb/cdns3/trace.h
new file mode 100644
index 000000000000..f3d7a91fae86
--- /dev/null
+++ b/drivers/usb/cdns3/trace.h
@@ -0,0 +1,389 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * USBSS device controller driver.
+ * Trace support header file.
+ *
+ * Copyright (C) 2018 Cadence.
+ *
+ * Author: Pawel Laszczak <pawell@cadence.com>
+ */
+
+#undef TRACE_SYSTEM
+#define TRACE_SYSTEM cdns3
+
+#if !defined(__LINUX_CDNS3_TRACE) || defined(TRACE_HEADER_MULTI_READ)
+#define __LINUX_CDNS3_TRACE
+
+#include <linux/types.h>
+#include <linux/tracepoint.h>
+#include <asm/byteorder.h>
+#include <linux/usb/ch9.h>
+#include "core.h"
+#include "gadget.h"
+#include "debug.h"
+
+#define CDNS3_MSG_MAX	500
+
+DECLARE_EVENT_CLASS(cdns3_log_doorbell,
+	TP_PROTO(const char *ep_name, u32 ep_trbaddr),
+	TP_ARGS(ep_name, ep_trbaddr),
+	TP_STRUCT__entry(
+		__string(name, ep_name)
+		__field(u32, ep_trbaddr)
+	),
+	TP_fast_assign(
+		__assign_str(name, ep_name);
+		__entry->ep_trbaddr = ep_trbaddr;
+	),
+	TP_printk("//Ding Dong %s, ep_trbaddr %08x", __get_str(name),
+		  __entry->ep_trbaddr)
+);
+
+DEFINE_EVENT(cdns3_log_doorbell, cdns3_doorbell_ep0,
+	TP_PROTO(const char *ep_name, u32 ep_trbaddr),
+	TP_ARGS(ep_name, ep_trbaddr)
+);
+
+DEFINE_EVENT(cdns3_log_doorbell, cdns3_doorbell_epx,
+	TP_PROTO(const char *ep_name, u32 ep_trbaddr),
+	TP_ARGS(ep_name, ep_trbaddr)
+);
+
+DECLARE_EVENT_CLASS(cdns3_log_usb_irq,
+	TP_PROTO(struct cdns3_device *priv_dev, u32 usb_ists),
+	TP_ARGS(priv_dev, usb_ists),
+	TP_STRUCT__entry(
+		__field(enum usb_device_speed, speed)
+		__field(u32, usb_ists)
+		__dynamic_array(char, str, CDNS3_MSG_MAX)
+	),
+	TP_fast_assign(
+		__entry->speed = cdns3_get_speed(priv_dev);
+		__entry->usb_ists = usb_ists;
+	),
+	TP_printk("%s", cdns3_decode_usb_irq(__get_str(str), __entry->speed,
+					     __entry->usb_ists))
+);
+
+DEFINE_EVENT(cdns3_log_usb_irq, cdns3_usb_irq,
+	TP_PROTO(struct cdns3_device *priv_dev, u32 usb_ists),
+	TP_ARGS(priv_dev, usb_ists)
+);
+
+DECLARE_EVENT_CLASS(cdns3_log_epx_irq,
+	TP_PROTO(struct cdns3_device *priv_dev, struct cdns3_endpoint *priv_ep),
+	TP_ARGS(priv_dev, priv_ep),
+	TP_STRUCT__entry(
+		__string(ep_name, priv_ep->name)
+		__field(u32, ep_sts)
+		__field(u32, ep_traddr)
+		__dynamic_array(char, str, CDNS3_MSG_MAX)
+	),
+	TP_fast_assign(
+		__assign_str(ep_name, priv_ep->name);
+		__entry->ep_sts = readl(&priv_dev->regs->ep_sts);
+		__entry->ep_traddr = readl(&priv_dev->regs->ep_traddr);
+	),
+	TP_printk("%s, ep_traddr: %08x",
+		  cdns3_decode_epx_irq(__get_str(str),
+				       __get_str(ep_name),
+				       __entry->ep_sts),
+		  __entry->ep_traddr)
+);
+
+DEFINE_EVENT(cdns3_log_epx_irq, cdns3_epx_irq,
+	TP_PROTO(struct cdns3_device *priv_dev, struct cdns3_endpoint *priv_ep),
+	TP_ARGS(priv_dev, priv_ep)
+);
+
+DECLARE_EVENT_CLASS(cdns3_log_ep0_irq,
+	TP_PROTO(struct cdns3_device *priv_dev,  u32 ep_sts),
+	TP_ARGS(priv_dev, ep_sts),
+	TP_STRUCT__entry(
+		__field(int, ep_dir)
+		__field(u32, ep_sts)
+		__dynamic_array(char, str, CDNS3_MSG_MAX)
+	),
+	TP_fast_assign(
+		__entry->ep_dir = priv_dev->ep0_data_dir;
+		__entry->ep_sts = ep_sts;
+	),
+	TP_printk("%s", cdns3_decode_ep0_irq(__get_str(str),
+					     __entry->ep_dir,
+					     __entry->ep_sts))
+);
+
+DEFINE_EVENT(cdns3_log_ep0_irq, cdns3_ep0_irq,
+	TP_PROTO(struct cdns3_device *priv_dev, u32 ep_sts),
+	TP_ARGS(priv_dev, ep_sts)
+);
+
+DECLARE_EVENT_CLASS(cdns3_log_ctrl,
+	TP_PROTO(struct usb_ctrlrequest *ctrl),
+	TP_ARGS(ctrl),
+	TP_STRUCT__entry(
+		__field(u8, bRequestType)
+		__field(u8, bRequest)
+		__field(u16, wValue)
+		__field(u16, wIndex)
+		__field(u16, wLength)
+		__dynamic_array(char, str, CDNS3_MSG_MAX)
+	),
+	TP_fast_assign(
+		__entry->bRequestType = ctrl->bRequestType;
+		__entry->bRequest = ctrl->bRequest;
+		__entry->wValue = le16_to_cpu(ctrl->wValue);
+		__entry->wIndex = le16_to_cpu(ctrl->wIndex);
+		__entry->wLength = le16_to_cpu(ctrl->wLength);
+	),
+	TP_printk("%s", usb_decode_ctrl(__get_str(str), __entry->bRequestType,
+					__entry->bRequest, __entry->wValue,
+					__entry->wIndex, __entry->wLength)
+	)
+);
+
+DEFINE_EVENT(cdns3_log_ctrl, cdns3_ctrl_req,
+	TP_PROTO(struct usb_ctrlrequest *ctrl),
+	TP_ARGS(ctrl)
+);
+
+DECLARE_EVENT_CLASS(cdns3_log_request,
+	TP_PROTO(struct cdns3_request *req),
+	TP_ARGS(req),
+	TP_STRUCT__entry(
+		__string(name, req->priv_ep->name)
+		__field(struct cdns3_request *, req)
+		__field(unsigned int, actual)
+		__field(unsigned int, length)
+		__field(int, status)
+		__field(int, zero)
+		__field(int, short_not_ok)
+		__field(int, no_interrupt)
+		__field(int, start_trb)
+		__field(int, end_trb)
+		__field(struct cdns3_trb *, start_trb_addr)
+		__field(int, flags)
+	),
+	TP_fast_assign(
+		__assign_str(name, req->priv_ep->name);
+		__entry->req = req;
+		__entry->actual = req->request.actual;
+		__entry->length = req->request.length;
+		__entry->status = req->request.status;
+		__entry->zero = req->request.zero;
+		__entry->short_not_ok = req->request.short_not_ok;
+		__entry->no_interrupt = req->request.no_interrupt;
+		__entry->start_trb = req->start_trb;
+		__entry->end_trb = req->end_trb;
+		__entry->start_trb_addr = req->trb;
+		__entry->flags = req->flags;
+	),
+	TP_printk("%s: req: %p, length: %u/%u %s%s%s, status: %d,"
+		  " trb: [start:%d, end:%d: virt addr %pa], flags:%x ",
+		__get_str(name), __entry->req, __entry->actual, __entry->length,
+		__entry->zero ? "zero | " : "",
+		__entry->short_not_ok ? "short | " : "",
+		__entry->no_interrupt ? "no int" : "",
+		__entry->status,
+		__entry->start_trb,
+		__entry->end_trb,
+		__entry->start_trb_addr,
+		__entry->flags
+	)
+);
+
+DEFINE_EVENT(cdns3_log_request, cdns3_alloc_request,
+	TP_PROTO(struct cdns3_request *req),
+	TP_ARGS(req)
+);
+
+DEFINE_EVENT(cdns3_log_request, cdns3_free_request,
+	TP_PROTO(struct cdns3_request *req),
+	TP_ARGS(req)
+);
+
+DEFINE_EVENT(cdns3_log_request, cdns3_ep_queue,
+	TP_PROTO(struct cdns3_request *req),
+	TP_ARGS(req)
+);
+
+DEFINE_EVENT(cdns3_log_request, cdns3_ep_dequeue,
+	TP_PROTO(struct cdns3_request *req),
+	TP_ARGS(req)
+);
+
+DEFINE_EVENT(cdns3_log_request, cdns3_gadget_giveback,
+	TP_PROTO(struct cdns3_request *req),
+	TP_ARGS(req)
+);
+
+DECLARE_EVENT_CLASS(cdns3_log_trb,
+	TP_PROTO(struct cdns3_endpoint *priv_ep, struct cdns3_trb *trb),
+	TP_ARGS(priv_ep, trb),
+	TP_STRUCT__entry(
+		__string(name, priv_ep->name)
+		__field(struct cdns3_trb *, trb)
+		__field(u32, buffer)
+		__field(u32, length)
+		__field(u32, control)
+		__field(u32, type)
+	),
+	TP_fast_assign(
+		__assign_str(name, priv_ep->name);
+		__entry->trb = trb;
+		__entry->buffer = trb->buffer;
+		__entry->length = trb->length;
+		__entry->control = trb->control;
+		__entry->type = usb_endpoint_type(priv_ep->endpoint.desc);
+	),
+	TP_printk("%s: trb 0x%pa, dma buf: 0x%08x, size: %ld, ctrl: 0x%08x (%s%s%s%s%s%s%s)",
+		__get_str(name), __entry->trb, __entry->buffer,
+		TRB_LEN(__entry->length), __entry->control,
+		__entry->control & TRB_CYCLE ? "C=1, " : "C=0, ",
+		__entry->control & TRB_TOGGLE ? "T=1, " : "T=0, ",
+		__entry->control & TRB_ISP ? "ISP, " : "",
+		__entry->control & TRB_FIFO_MODE ? "FIFO, " : "",
+		__entry->control & TRB_CHAIN ? "CHAIN, " : "",
+		__entry->control & TRB_IOC ? "IOC, " : "",
+		TRB_FIELD_TO_TYPE(__entry->control) == TRB_NORMAL ? "Normal" : "LINK"
+	)
+);
+
+DEFINE_EVENT(cdns3_log_trb, cdns3_prepare_trb,
+	TP_PROTO(struct cdns3_endpoint *priv_ep, struct cdns3_trb *trb),
+	TP_ARGS(priv_ep, trb)
+);
+
+DEFINE_EVENT(cdns3_log_trb, cdns3_complete_trb,
+	TP_PROTO(struct cdns3_endpoint *priv_ep, struct cdns3_trb *trb),
+	TP_ARGS(priv_ep, trb)
+);
+
+DECLARE_EVENT_CLASS(cdns3_log_ring,
+	TP_PROTO(struct cdns3_endpoint *priv_ep),
+	TP_ARGS(priv_ep),
+	TP_STRUCT__entry(
+		__dynamic_array(u8, ring, TRB_RING_SIZE)
+		__dynamic_array(u8, priv_ep, sizeof(struct cdns3_endpoint))
+		__dynamic_array(char, buffer,
+				(TRBS_PER_SEGMENT * 65) + CDNS3_MSG_MAX)
+	),
+	TP_fast_assign(
+		memcpy(__get_dynamic_array(priv_ep), priv_ep,
+		       sizeof(struct cdns3_endpoint));
+		memcpy(__get_dynamic_array(ring), priv_ep->trb_pool,
+		       TRB_RING_SIZE);
+	),
+
+	TP_printk("%s",
+		  cdns3_dbg_ring((struct cdns3_endpoint *)__get_str(priv_ep),
+				 (struct cdns3_trb *)__get_str(ring),
+				 __get_str(buffer)))
+);
+
+DEFINE_EVENT(cdns3_log_ring, cdns3_ring,
+	TP_PROTO(struct cdns3_endpoint *priv_ep),
+	TP_ARGS(priv_ep)
+);
+
+DECLARE_EVENT_CLASS(cdns3_log_ep,
+	TP_PROTO(struct cdns3_endpoint *priv_ep),
+	TP_ARGS(priv_ep),
+	TP_STRUCT__entry(
+		__string(name, priv_ep->name)
+		__field(unsigned int, maxpacket)
+		__field(unsigned int, maxpacket_limit)
+		__field(unsigned int, max_streams)
+		__field(unsigned int, maxburst)
+		__field(unsigned int, flags)
+		__field(unsigned int, dir)
+		__field(u8, enqueue)
+		__field(u8, dequeue)
+	),
+	TP_fast_assign(
+		__assign_str(name, priv_ep->name);
+		__entry->maxpacket = priv_ep->endpoint.maxpacket;
+		__entry->maxpacket_limit = priv_ep->endpoint.maxpacket_limit;
+		__entry->max_streams = priv_ep->endpoint.max_streams;
+		__entry->maxburst = priv_ep->endpoint.maxburst;
+		__entry->flags = priv_ep->flags;
+		__entry->dir = priv_ep->dir;
+		__entry->enqueue = priv_ep->enqueue;
+		__entry->dequeue = priv_ep->dequeue;
+	),
+	TP_printk("%s: mps: %d/%d. streams: %d, burst: %d, enq idx: %d, "
+		  "deq idx: %d, flags %s%s%s%s%s%s%s%s, dir: %s",
+		__get_str(name), __entry->maxpacket,
+		__entry->maxpacket_limit, __entry->max_streams,
+		__entry->maxburst, __entry->enqueue,
+		__entry->dequeue,
+		__entry->flags & EP_ENABLED ? "EN | " : "",
+		__entry->flags & EP_STALL ? "STALL | " : "",
+		__entry->flags & EP_WEDGE ? "WEDGE | " : "",
+		__entry->flags & EP_TRANSFER_STARTED ? "STARTED | " : "",
+		__entry->flags & EP_UPDATE_EP_TRBADDR ? "UPD TRB | " : "",
+		__entry->flags & EP_PENDING_REQUEST ? "REQ PEN | " : "",
+		__entry->flags & EP_RING_FULL ? "RING FULL |" : "",
+		__entry->flags & EP_CLAIMED ?  "CLAIMED " : "",
+		__entry->dir ? "IN" : "OUT"
+	)
+);
+
+DEFINE_EVENT(cdns3_log_ep, cdns3_gadget_ep_enable,
+	TP_PROTO(struct cdns3_endpoint *priv_ep),
+	TP_ARGS(priv_ep)
+);
+
+DEFINE_EVENT(cdns3_log_ep, cdns3_gadget_ep_disable,
+	TP_PROTO(struct cdns3_endpoint *priv_ep),
+	TP_ARGS(priv_ep)
+);
+
+DECLARE_EVENT_CLASS(cdns3_log_request_handled,
+	TP_PROTO(struct cdns3_request *priv_req, int current_index,
+		 int handled),
+	TP_ARGS(priv_req, current_index, handled),
+	TP_STRUCT__entry(
+		__field(struct cdns3_request *, priv_req)
+		__field(unsigned int, dma_position)
+		__field(unsigned int, handled)
+		__field(unsigned int, dequeue_idx)
+		__field(unsigned int, enqueue_idx)
+		__field(unsigned int, start_trb)
+		__field(unsigned int, end_trb)
+	),
+	TP_fast_assign(
+		__entry->priv_req = priv_req;
+		__entry->dma_position = current_index;
+		__entry->handled = handled;
+		__entry->dequeue_idx = priv_req->priv_ep->dequeue;
+		__entry->enqueue_idx = priv_req->priv_ep->enqueue;
+		__entry->start_trb = priv_req->start_trb;
+		__entry->end_trb = priv_req->end_trb;
+	),
+	TP_printk("Req: %p %s, DMA pos: %d, ep deq: %d, ep enq: %d,"
+		  " start trb: %d, end trb: %d",
+		__entry->priv_req,
+		__entry->handled ? "handled" : "not handled",
+		__entry->dma_position, __entry->dequeue_idx,
+		__entry->enqueue_idx, __entry->start_trb,
+		__entry->end_trb
+	)
+);
+
+DEFINE_EVENT(cdns3_log_request_handled, cdns3_request_handled,
+	TP_PROTO(struct cdns3_request *priv_req, int current_index,
+		 int handled),
+	TP_ARGS(priv_req, current_index, handled)
+);
+#endif /* __LINUX_CDNS3_TRACE */
+
+/* this part must be outside header guard */
+
+#undef TRACE_INCLUDE_PATH
+#define TRACE_INCLUDE_PATH .
+
+#undef TRACE_INCLUDE_FILE
+#define TRACE_INCLUDE_FILE trace
+
+#include <trace/define_trace.h>
-- 
2.17.1


^ permalink raw reply related	[flat|nested] 49+ messages in thread

* [PATCH v2 5/5] usb:cdns3 Add Cadence USB3 DRD Driver
@ 2018-12-23 15:13   ` Pawel Laszczak
  0 siblings, 0 replies; 49+ messages in thread
From: Pawel Laszczak @ 2018-12-23 15:13 UTC (permalink / raw)
  To: devicetree
  Cc: gregkh, mark.rutland, linux-usb, hdegoede, heikki.krogerus,
	andy.shevchenko, robh+dt, rogerq, linux-kernel, adouglas,
	jbergsagel, nsekhar, nm, sureshp, peter.chen, pjez, kurahul,
	Pawel Laszczak

This patch introduce new Cadence USBSS DRD driver
to linux kernel.

The Cadence USBSS DRD Driver is a highly
configurable IP Core which can be
instantiated as Dual-Role Device (DRD),
Peripheral Only and Host Only (XHCI)
configurations.

The current driver has been validated with
FPGA burned. We have support for PCIe
bus, which is used on FPGA prototyping.

The host side of USBSS-DRD controller is compliance
with XHCI specification, so it works with
standard XHCI linux driver.

Signed-off-by: Pawel Laszczak <pawell@cadence.com>
---
 drivers/usb/Kconfig                |    2 +
 drivers/usb/Makefile               |    2 +
 drivers/usb/cdns3/Kconfig          |   44 +
 drivers/usb/cdns3/Makefile         |   16 +
 drivers/usb/cdns3/cdns3-pci-wrap.c |  157 +++
 drivers/usb/cdns3/core.c           |  406 ++++++
 drivers/usb/cdns3/core.h           |  116 ++
 drivers/usb/cdns3/debug.h          |  166 +++
 drivers/usb/cdns3/debugfs.c        |  168 +++
 drivers/usb/cdns3/drd.c            |  350 +++++
 drivers/usb/cdns3/drd.h            |  162 +++
 drivers/usb/cdns3/ep0.c            |  896 ++++++++++++
 drivers/usb/cdns3/gadget-export.h  |   28 +
 drivers/usb/cdns3/gadget.c         | 2102 ++++++++++++++++++++++++++++
 drivers/usb/cdns3/gadget.h         | 1206 ++++++++++++++++
 drivers/usb/cdns3/host-export.h    |   28 +
 drivers/usb/cdns3/host.c           |   72 +
 drivers/usb/cdns3/trace.c          |   11 +
 drivers/usb/cdns3/trace.h          |  389 +++++
 19 files changed, 6321 insertions(+)
 create mode 100644 drivers/usb/cdns3/Kconfig
 create mode 100644 drivers/usb/cdns3/Makefile
 create mode 100644 drivers/usb/cdns3/cdns3-pci-wrap.c
 create mode 100644 drivers/usb/cdns3/core.c
 create mode 100644 drivers/usb/cdns3/core.h
 create mode 100644 drivers/usb/cdns3/debug.h
 create mode 100644 drivers/usb/cdns3/debugfs.c
 create mode 100644 drivers/usb/cdns3/drd.c
 create mode 100644 drivers/usb/cdns3/drd.h
 create mode 100644 drivers/usb/cdns3/ep0.c
 create mode 100644 drivers/usb/cdns3/gadget-export.h
 create mode 100644 drivers/usb/cdns3/gadget.c
 create mode 100644 drivers/usb/cdns3/gadget.h
 create mode 100644 drivers/usb/cdns3/host-export.h
 create mode 100644 drivers/usb/cdns3/host.c
 create mode 100644 drivers/usb/cdns3/trace.c
 create mode 100644 drivers/usb/cdns3/trace.h

diff --git a/drivers/usb/Kconfig b/drivers/usb/Kconfig
index 987fc5ba6321..5f9334019d04 100644
--- a/drivers/usb/Kconfig
+++ b/drivers/usb/Kconfig
@@ -112,6 +112,8 @@ source "drivers/usb/usbip/Kconfig"
 
 endif
 
+source "drivers/usb/cdns3/Kconfig"
+
 source "drivers/usb/mtu3/Kconfig"
 
 source "drivers/usb/musb/Kconfig"
diff --git a/drivers/usb/Makefile b/drivers/usb/Makefile
index 7d1b8c82b208..ab125b966cac 100644
--- a/drivers/usb/Makefile
+++ b/drivers/usb/Makefile
@@ -12,6 +12,8 @@ obj-$(CONFIG_USB_DWC3)		+= dwc3/
 obj-$(CONFIG_USB_DWC2)		+= dwc2/
 obj-$(CONFIG_USB_ISP1760)	+= isp1760/
 
+obj-$(CONFIG_USB_CDNS3)		+= cdns3/
+
 obj-$(CONFIG_USB_MON)		+= mon/
 obj-$(CONFIG_USB_MTU3)		+= mtu3/
 
diff --git a/drivers/usb/cdns3/Kconfig b/drivers/usb/cdns3/Kconfig
new file mode 100644
index 000000000000..4adfd87811e8
--- /dev/null
+++ b/drivers/usb/cdns3/Kconfig
@@ -0,0 +1,44 @@
+config USB_CDNS3
+	tristate "Cadence USB3 Dual-Role Controller"
+	depends on USB_SUPPORT && (USB || USB_GADGET) && HAS_DMA
+	help
+	  Say Y here if your system has a cadence USB3 dual-role controller.
+	  It supports: dual-role switch, Host-only, and Peripheral-only.
+
+	  If you choose to build this driver is a dynamically linked
+	  module, the module will be called cdns3.ko.
+
+if USB_CDNS3
+
+config USB_CDNS3_GADGET
+        bool "Cadence USB3 device controller"
+        depends on USB_GADGET
+        help
+          Say Y here to enable device controller functionality of the
+          cadence USBSS-DEV driver.
+
+          This controller supports FF, HS and SS mode. It doesn't support
+          LS and SSP mode
+
+config USB_CDNS3_HOST
+        bool "Cadence USB3 host controller"
+        depends on USB_XHCI_HCD
+        help
+          Say Y here to enable host controller functionality of the
+          cadence driver.
+
+          Host controller is compliance with XHCI so it will use
+          standard XHCI driver.
+
+config USB_CDNS3_PCI_WRAP
+	tristate "Cadence USB3 support on PCIe-based platforms"
+	depends on USB_PCI && ACPI
+	default USB_CDNS3
+	help
+	  If you're using the USBSS Core IP with a PCIe, please say
+	  'Y' or 'M' here.
+
+	  If you choose to build this driver as module it will
+	  be dynamically linked and module will be called cdns3-pci.ko
+
+endif
diff --git a/drivers/usb/cdns3/Makefile b/drivers/usb/cdns3/Makefile
new file mode 100644
index 000000000000..3f63baa24294
--- /dev/null
+++ b/drivers/usb/cdns3/Makefile
@@ -0,0 +1,16 @@
+# SPDX-License-Identifier: GPL-2.0
+# define_trace.h needs to know how to find our header
+CFLAGS_trace.o				:= -I$(src)
+
+obj-$(CONFIG_USB_CDNS3)			+= cdns3.o
+obj-$(CONFIG_USB_CDNS3_PCI_WRAP)	+= cdns3-pci.o
+
+cdns3-y					:= core.o drd.o trace.o
+
+ifneq ($(CONFIG_DEBUG_FS),)
+	cdns3-y				+= debugfs.o
+endif
+
+cdns3-$(CONFIG_USB_CDNS3_GADGET)	+= gadget.o ep0.o
+cdns3-$(CONFIG_USB_CDNS3_HOST)		+= host.o
+cdns3-pci-y		 		:= cdns3-pci-wrap.o
diff --git a/drivers/usb/cdns3/cdns3-pci-wrap.c b/drivers/usb/cdns3/cdns3-pci-wrap.c
new file mode 100644
index 000000000000..e93179c45ece
--- /dev/null
+++ b/drivers/usb/cdns3/cdns3-pci-wrap.c
@@ -0,0 +1,157 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Cadence USBSS PCI Glue driver
+ *
+ * Copyright (C) 2018 Cadence.
+ *
+ * Author: Pawel Laszczak <pawell@cadence.com>
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/platform_device.h>
+#include <linux/dma-mapping.h>
+#include <linux/slab.h>
+
+struct cdns3_wrap {
+	struct platform_device *plat_dev;
+	struct pci_dev *hg_dev;
+	struct resource dev_res[4];
+};
+
+struct cdns3_wrap wrap;
+
+#define RES_IRQ_ID		0
+#define RES_HOST_ID		1
+#define RES_DEV_ID		2
+#define RES_DRD_ID		3
+
+#define PCI_BAR_HOST		0
+#define PCI_BAR_DEV		2
+#define PCI_BAR_OTG		4
+
+#define PCI_DEV_FN_HOST_DEVICE	0
+#define PCI_DEV_FN_OTG		1
+
+#define PCI_DRIVER_NAME		"cdns3-pci-usbss"
+#define PLAT_DRIVER_NAME	"cdns-usb3"
+
+#define CDNS_VENDOR_ID 0x17cd
+#define CDNS_DEVICE_ID 0x0100
+
+/**
+ * cdns3_pci_probe - Probe function for Cadence USB wrapper driver
+ * @pdev: platform device object
+ * @id: pci device id
+ *
+ * Returns 0 on success otherwise negative errno
+ */
+static int cdns3_pci_probe(struct pci_dev *pdev,
+			   const struct pci_device_id *id)
+{
+	struct platform_device_info plat_info;
+	struct cdns3_wrap *wrap;
+	struct resource *res;
+	int err;
+
+	/*
+	 * for GADGET/HOST PCI (devfn) function number is 0,
+	 * for OTG PCI (devfn) function number is 1
+	 */
+	if (!id || pdev->devfn != PCI_DEV_FN_HOST_DEVICE)
+		return -EINVAL;
+
+	err = pcim_enable_device(pdev);
+	if (err) {
+		dev_err(&pdev->dev, "Enabling PCI device has failed %d\n", err);
+		return err;
+	}
+
+	pci_set_master(pdev);
+	wrap = devm_kzalloc(&pdev->dev, sizeof(*wrap), GFP_KERNEL);
+	if (!wrap) {
+		dev_err(&pdev->dev, "Failed to allocate memory\n");
+		return -ENOMEM;
+	}
+
+	/* function 0: host(BAR_0) + device(BAR_1) + otg(BAR_2)). */
+	memset(wrap->dev_res, 0x00,
+	       sizeof(struct resource) * ARRAY_SIZE(wrap->dev_res));
+	dev_dbg(&pdev->dev, "Initialize Device resources\n");
+	res = wrap->dev_res;
+
+	res[RES_DEV_ID].start = pci_resource_start(pdev, PCI_BAR_DEV);
+	res[RES_DEV_ID].end =   pci_resource_end(pdev, PCI_BAR_DEV);
+	res[RES_DEV_ID].name = "cdns3-dev-regs";
+	res[RES_DEV_ID].flags = IORESOURCE_MEM;
+	dev_dbg(&pdev->dev, "USBSS-DEV physical base addr: %pa\n",
+		&res[RES_DEV_ID].start);
+
+	res[RES_HOST_ID].start = pci_resource_start(pdev, PCI_BAR_HOST);
+	res[RES_HOST_ID].end = pci_resource_end(pdev, PCI_BAR_HOST);
+	res[RES_HOST_ID].name = "cdns3-xhci-regs";
+	res[RES_HOST_ID].flags = IORESOURCE_MEM;
+	dev_dbg(&pdev->dev, "USBSS-XHCI physical base addr: %pa\n",
+		&res[RES_HOST_ID].start);
+
+	res[RES_DRD_ID].start = pci_resource_start(pdev, PCI_BAR_OTG);
+	res[RES_DRD_ID].end =   pci_resource_end(pdev, PCI_BAR_OTG);
+	res[RES_DRD_ID].name = "cdns3-otg";
+	res[RES_DRD_ID].flags = IORESOURCE_MEM;
+	dev_dbg(&pdev->dev, "USBSS-DRD physical base addr: %pa\n",
+		&res[RES_DRD_ID].start);
+
+	/* Interrupt common for both device and XHCI */
+	wrap->dev_res[RES_IRQ_ID].start = pdev->irq;
+	wrap->dev_res[RES_IRQ_ID].name = "cdns3-irq";
+	wrap->dev_res[RES_IRQ_ID].flags = IORESOURCE_IRQ;
+
+	/* set up platform device info */
+	memset(&plat_info, 0, sizeof(plat_info));
+	plat_info.parent = &pdev->dev;
+	plat_info.fwnode = pdev->dev.fwnode;
+	plat_info.name = PLAT_DRIVER_NAME;
+	plat_info.id = pdev->devfn;
+	plat_info.res = wrap->dev_res;
+	plat_info.num_res = ARRAY_SIZE(wrap->dev_res);
+	plat_info.dma_mask = pdev->dma_mask;
+
+	/* register platform device */
+	wrap->plat_dev = platform_device_register_full(&plat_info);
+	if (IS_ERR(wrap->plat_dev)) {
+		err = PTR_ERR(wrap->plat_dev);
+		return err;
+	}
+
+	pci_set_drvdata(pdev, wrap);
+
+	return err;
+}
+
+void cdns3_pci_remove(struct pci_dev *pdev)
+{
+	struct cdns3_wrap *wrap = (struct cdns3_wrap *)pci_get_drvdata(pdev);
+
+	platform_device_unregister(wrap->plat_dev);
+}
+
+static const struct pci_device_id cdns3_pci_ids[] = {
+	{ PCI_DEVICE(CDNS_VENDOR_ID, CDNS_DEVICE_ID), },
+	{ 0, }
+};
+
+static struct pci_driver cdns3_pci_driver = {
+	.name = PCI_DRIVER_NAME,
+	.id_table = cdns3_pci_ids,
+	.probe = cdns3_pci_probe,
+	.remove = cdns3_pci_remove,
+};
+
+module_pci_driver(cdns3_pci_driver);
+MODULE_DEVICE_TABLE(pci, cdns3_pci_ids);
+
+MODULE_AUTHOR("Pawel Laszczak <pawell@cadence.com>");
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("Cadence USBSS PCI wrapperr");
+
diff --git a/drivers/usb/cdns3/core.c b/drivers/usb/cdns3/core.c
new file mode 100644
index 000000000000..d274586aca36
--- /dev/null
+++ b/drivers/usb/cdns3/core.c
@@ -0,0 +1,406 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Cadence USBSS DRD Driver.
+ *
+ * Copyright (C) 2018 Cadence.
+ * Copyright (C) 2017-2018 NXP
+ *
+ * Author: Peter Chen <peter.chen@nxp.com>
+ *         Pawel Laszczak <pawell@cadence.com>
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/platform_device.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/pm_runtime.h>
+
+#include "gadget.h"
+#include "core.h"
+#include "host-export.h"
+#include "gadget-export.h"
+#include "drd.h"
+#include "debug.h"
+
+static inline
+struct cdns3_role_driver *cdns3_get_current_role_driver(struct cdns3 *cdns)
+{
+	WARN_ON(cdns->role >= CDNS3_ROLE_END || !cdns->roles[cdns->role]);
+	return cdns->roles[cdns->role];
+}
+
+static inline int cdns3_role_start(struct cdns3 *cdns, enum cdns3_roles role)
+{
+	int ret;
+
+	if (WARN_ON(role >= CDNS3_ROLE_END))
+		return 0;
+
+	if (!cdns->roles[role])
+		return -ENXIO;
+
+	if (cdns->roles[role]->state == CDNS3_ROLE_STATE_ACTIVE)
+		return 0;
+
+	mutex_lock(&cdns->mutex);
+	cdns->role = role;
+	ret = cdns->roles[role]->start(cdns);
+	if (!ret)
+		cdns->roles[role]->state = CDNS3_ROLE_STATE_ACTIVE;
+	mutex_unlock(&cdns->mutex);
+	return ret;
+}
+
+void cdns3_role_stop(struct cdns3 *cdns)
+{
+	enum cdns3_roles role = cdns->role;
+
+	if (role >= CDNS3_ROLE_END) {
+		WARN_ON(role > CDNS3_ROLE_END);
+		return;
+	}
+
+	if (cdns->roles[role]->state == CDNS3_ROLE_STATE_INACTIVE)
+		return;
+
+	mutex_lock(&cdns->mutex);
+	cdns->roles[role]->stop(cdns);
+	cdns->roles[role]->state = CDNS3_ROLE_STATE_INACTIVE;
+	mutex_unlock(&cdns->mutex);
+}
+
+/*
+ * cdns->role gets from cdns3_get_initial_role, and this API tells role at the
+ * runtime.
+ * If both roles are supported, the role is selected based on vbus/id.
+ * It could be read from OTG register or external connector.
+ * If only single role is supported, only one role structure
+ * is allocated, cdns->roles[CDNS3_ROLE_HOST] or cdns->roles[CDNS3_ROLE_GADGET].
+ */
+static enum cdns3_roles cdns3_get_initial_role(struct cdns3 *cdns)
+{
+	if (cdns->roles[CDNS3_ROLE_HOST] && cdns->roles[CDNS3_ROLE_GADGET]) {
+		if (cdns3_is_host(cdns))
+			return CDNS3_ROLE_HOST;
+		if (cdns3_is_device(cdns))
+			return CDNS3_ROLE_GADGET;
+	}
+	return cdns->roles[CDNS3_ROLE_HOST]
+		? CDNS3_ROLE_HOST
+		: CDNS3_ROLE_GADGET;
+}
+
+static void cdns3_exit_roles(struct cdns3 *cdns)
+{
+	cdns3_role_stop(cdns);
+	cdns3_drd_exit(cdns);
+}
+
+/**
+ * cdns3_core_init_role - initialize role of operation
+ * @cdns: Pointer to cdns3 structure
+ *
+ * Returns 0 on success otherwise negative errno
+ */
+static int cdns3_core_init_role(struct cdns3 *cdns)
+{
+	struct device *dev = cdns->dev;
+	enum usb_dr_mode best_dr_mode;
+	enum usb_dr_mode dr_mode;
+	int ret = 0;
+
+	dr_mode = usb_get_dr_mode(dev);
+	cdns->role = CDNS3_ROLE_END;
+
+	/*
+	 * If driver can't read mode by means of usb_get_dr_mdoe function then
+	 * chooses mode according with Kernel configuration. This setting
+	 * can be restricted later depending on strap pin configuration.
+	 */
+	if (dr_mode == USB_DR_MODE_UNKNOWN) {
+		if (IS_ENABLED(CONFIG_USB_CDNS3_HOST) &&
+		    IS_ENABLED(CONFIG_USB_CDNS3_GADGET))
+			dr_mode = USB_DR_MODE_OTG;
+		else if (IS_ENABLED(CONFIG_USB_CDNS3_HOST))
+			dr_mode = USB_DR_MODE_HOST;
+		else if (IS_ENABLED(CONFIG_USB_CDNS3_GADGET))
+			dr_mode = USB_DR_MODE_PERIPHERAL;
+	}
+
+	best_dr_mode = USB_DR_MODE_OTG;
+
+	if (dr_mode == USB_DR_MODE_OTG) {
+		best_dr_mode = cdns->dr_mode;
+	} else if (cdns->dr_mode == USB_DR_MODE_OTG) {
+		best_dr_mode = dr_mode;
+	} else if (cdns->dr_mode != dr_mode) {
+		dev_err(dev, "Incorrect DRD configuration\n");
+		return -EINVAL;
+	}
+
+	dr_mode = best_dr_mode;
+
+	if (dr_mode == USB_DR_MODE_OTG || dr_mode == USB_DR_MODE_HOST) {
+		ret = cdns3_host_init(cdns);
+		if (ret) {
+			dev_err(dev, "Host initialization failed with %d\n",
+				ret);
+			goto err;
+		}
+	}
+
+	if (dr_mode == USB_DR_MODE_OTG || dr_mode == USB_DR_MODE_PERIPHERAL) {
+		ret = cdns3_gadget_init(cdns);
+		if (ret) {
+			dev_err(dev, "Device initialization failed with %d\n",
+				ret);
+			goto err;
+		}
+	}
+
+	cdns->desired_dr_mode = dr_mode;
+	cdns->dr_mode = dr_mode;
+	/*
+	 * dr_mode could be change so DRD must update controller
+	 * configuration
+	 */
+	ret = cdns3_drd_update_mode(cdns);
+
+	cdns->role = cdns3_get_initial_role(cdns);
+
+	ret = cdns3_role_start(cdns, cdns->role);
+	if (ret) {
+		dev_err(dev, "can't start %s role\n",
+			cdns3_get_current_role_driver(cdns)->name);
+		goto err;
+	}
+
+	return ret;
+err:
+	cdns3_exit_roles(cdns);
+	return ret;
+}
+
+/**
+ * cdsn3_get_real_role - get real role of controller based on hardware settings.
+ * @cdns: Pointer to cdns3 structure
+ *
+ * Returns role
+ */
+enum cdns3_roles cdsn3_get_real_role(struct cdns3 *cdns)
+{
+	enum cdns3_roles role = CDNS3_ROLE_END;
+
+	if (cdns->current_dr_mode == USB_DR_MODE_OTG) {
+		if (cdns3_get_id(cdns))
+			role = CDNS3_ROLE_GADGET;
+		else
+			role = CDNS3_ROLE_HOST;
+	} else {
+		if (cdns3_is_host(cdns))
+			role = CDNS3_ROLE_HOST;
+		if (cdns3_is_device(cdns))
+			role = CDNS3_ROLE_GADGET;
+	}
+
+	return role;
+}
+
+/**
+ * cdns3_role_switch - work queue handler for role switch
+ *
+ * @work: work queue item structure
+ *
+ * Handles below events:
+ * - Role switch for dual-role devices
+ * - CDNS3_ROLE_GADGET <--> CDNS3_ROLE_END for peripheral-only devices
+ */
+static void cdns3_role_switch(struct work_struct *work)
+{
+	enum cdns3_roles role = CDNS3_ROLE_END;
+	struct cdns3_role_driver *role_drv;
+	enum cdns3_roles current_role;
+	struct cdns3 *cdns;
+	int ret = 0;
+
+	cdns = container_of(work, struct cdns3, role_switch_wq);
+
+	/* During switching cdns->role can be different then role */
+	role = cdsn3_get_real_role(cdns);
+
+	role_drv = cdns3_get_current_role_driver(cdns);
+
+	pm_runtime_get_sync(cdns->dev);
+
+	/* Disable current role. This state can be forced from user space. */
+	if (cdns->debug_disable && role_drv->state == CDNS3_ROLE_STATE_ACTIVE) {
+		cdns3_role_stop(cdns);
+		goto exit;
+	}
+
+	/* Do nothing if nothing changed */
+	if (cdns->role == role && role_drv->state == CDNS3_ROLE_STATE_ACTIVE)
+		goto exit;
+
+	cdns3_role_stop(cdns);
+
+	role = cdsn3_get_real_role(cdns);
+
+	current_role = cdns->role;
+	dev_dbg(cdns->dev, "Switching role");
+
+	ret = cdns3_role_start(cdns, role);
+
+	if (ret) {
+		/* Back to current role */
+		dev_err(cdns->dev, "set %d has failed, back to %d\n",
+			role, current_role);
+		cdns3_role_start(cdns, current_role);
+	}
+exit:
+	pm_runtime_put_sync(cdns->dev);
+}
+
+/**
+ * cdns3_probe - probe for cdns3 core device
+ * @pdev: Pointer to cdns3 core platform device
+ *
+ * Returns 0 on success otherwise negative errno
+ */
+static int cdns3_probe(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	struct resource	*res;
+	struct cdns3 *cdns;
+	void __iomem *regs;
+	int ret;
+
+	cdns = devm_kzalloc(dev, sizeof(*cdns), GFP_KERNEL);
+	if (!cdns)
+		return -ENOMEM;
+
+	cdns->dev = dev;
+
+	platform_set_drvdata(pdev, cdns);
+
+	res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
+	if (!res) {
+		dev_err(dev, "missing IRQ\n");
+		return -ENODEV;
+	}
+	cdns->irq = res->start;
+
+	cdns->xhci_res[0] = *res;
+
+	/*
+	 * Request memory region
+	 * region-0: xHCI
+	 * region-1: Peripheral
+	 * region-2: OTG registers
+	 */
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	cdns->xhci_res[1] = *res;
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+	regs = devm_ioremap_resource(dev, res);
+	if (IS_ERR(regs))
+		return PTR_ERR(regs);
+	cdns->dev_regs	= regs;
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 2);
+	cdns->otg_res = *res;
+
+	mutex_init(&cdns->mutex);
+
+	cdns->phy = devm_phy_get(dev, "cdns3,usbphy");
+	if (IS_ERR(cdns->phy)) {
+		ret = PTR_ERR(cdns->phy);
+		if (ret == -ENODEV) {
+			cdns->phy = NULL;
+		} else if (ret == -EPROBE_DEFER) {
+			return ret;
+		} else {
+			dev_err(dev, "no phy found\n");
+			goto err0;
+		}
+	}
+
+	phy_init(cdns->phy);
+
+	INIT_WORK(&cdns->role_switch_wq, cdns3_role_switch);
+
+	ret = cdns3_drd_init(cdns);
+	if (ret)
+		goto err1;
+
+	ret = cdns3_core_init_role(cdns);
+	if (ret)
+		goto err1;
+
+	cdns3_debugfs_init(cdns);
+	device_set_wakeup_capable(dev, true);
+	pm_runtime_set_active(dev);
+	pm_runtime_enable(dev);
+
+	/*
+	 * The controller needs less time between bus and controller suspend,
+	 * and we also needs a small delay to avoid frequently entering low
+	 * power mode.
+	 */
+	pm_runtime_set_autosuspend_delay(dev, 20);
+	pm_runtime_mark_last_busy(dev);
+	pm_runtime_use_autosuspend(dev);
+	dev_dbg(dev, "Cadence USB3 core: probe succeed\n");
+
+	return 0;
+
+err1:
+	phy_exit(cdns->phy);
+err0:
+	return ret;
+}
+
+/**
+ * cdns3_remove - unbind drd driver and clean up
+ * @pdev: Pointer to Linux platform device
+ *
+ * Returns 0 on success otherwise negative errno
+ */
+static int cdns3_remove(struct platform_device *pdev)
+{
+	struct cdns3 *cdns = platform_get_drvdata(pdev);
+
+	pm_runtime_get_sync(&pdev->dev);
+	pm_runtime_disable(&pdev->dev);
+	pm_runtime_put_noidle(&pdev->dev);
+	cdns3_debugfs_exit(cdns);
+	cdns3_exit_roles(cdns);
+	phy_exit(cdns->phy);
+	return 0;
+}
+
+#ifdef CONFIG_OF
+static const struct of_device_id of_cdns3_match[] = {
+	{ .compatible = "cdns,usb3-1.0.0" },
+	{ .compatible = "cdns,usb3-1.0.1" },
+	{ },
+};
+MODULE_DEVICE_TABLE(of, of_cdns3_match);
+#endif
+
+static struct platform_driver cdns3_driver = {
+	.probe		= cdns3_probe,
+	.remove		= cdns3_remove,
+	.driver		= {
+		.name	= "cdns-usb3",
+		.of_match_table	= of_match_ptr(of_cdns3_match),
+	},
+};
+
+module_platform_driver(cdns3_driver);
+
+MODULE_ALIAS("platform:cdns3");
+MODULE_AUTHOR("Pawel Laszczak <pawell@cadence.com>");
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("Cadence USB3 DRD Controller Driver");
diff --git a/drivers/usb/cdns3/core.h b/drivers/usb/cdns3/core.h
new file mode 100644
index 000000000000..fb4b39206158
--- /dev/null
+++ b/drivers/usb/cdns3/core.h
@@ -0,0 +1,116 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Cadence USBSS DRD Header File.
+ *
+ * Copyright (C) 2017-2018 NXP
+ * Copyright (C) 2018 Cadence.
+ *
+ * Authors: Peter Chen <peter.chen@nxp.com>
+ *          Pawel Laszczak <pawell@cadence.com>
+ */
+#include <linux/usb/otg.h>
+
+#ifndef __LINUX_CDNS3_CORE_H
+#define __LINUX_CDNS3_CORE_H
+
+struct cdns3;
+enum cdns3_roles {
+	CDNS3_ROLE_HOST = 0,
+	CDNS3_ROLE_GADGET,
+	CDNS3_ROLE_END,
+};
+
+/**
+ * struct cdns3_role_driver - host/gadget role driver
+ * @start: start this role
+ * @stop: stop this role
+ * @suspend: suspend callback for this role
+ * @resume: resume callback for this role
+ * @irq: irq handler for this role
+ * @name: role name string (host/gadget)
+ * @state: current state
+ */
+struct cdns3_role_driver {
+	int (*start)(struct cdns3 *cdns);
+	void (*stop)(struct cdns3 *cdns);
+	int (*suspend)(struct cdns3 *cdns, bool do_wakeup);
+	int (*resume)(struct cdns3 *cdns, bool hibernated);
+	const char *name;
+#define CDNS3_ROLE_STATE_INACTIVE	0
+#define CDNS3_ROLE_STATE_ACTIVE		1
+	int state;
+};
+
+#define CDNS3_XHCI_RESOURCES_NUM	2
+/**
+ * struct cdns3 - Representation of Cadence USB3 DRD controller.
+ * @dev: pointer to Cadence device struct
+ * @xhci_regs: pointer to base of xhci registers
+ * @xhci_res: the resource for xhci
+ * @dev_regs: pointer to base of dev registers
+ * @otg_regs: pointer to base of otg registers
+ * @irq: irq number for controller
+ * @roles: array of supported roles for this controller
+ * @role: current role
+ * @host_dev: the child host device pointer for cdns3 core
+ * @gadget_dev: the child gadget device pointer for cdns3 core
+ * @usb: phy for this controller
+ * @role_switch_wq: work queue item for role switch
+ * @in_lpm: the controller in low power mode
+ * @wakeup_int: the wakeup interrupt
+ * @mutex: the mutex for concurrent code at driver
+ * @dr_mode: supported mode of operation it can be only Host, only Device
+ *           or OTG mode that allow to switch between Device and Host mode.
+ *           This field based on firmware setting, kernel configuration
+ *           and hardware configuration.
+ * @current_dr_mode: current mode of operation when in dual-role mode
+ * @desired_dr_mode: desired mode of operation when in dual-role mode.
+ *           This value can be changed during runtime.
+ *           Available options depends on  dr_mode:
+ *           dr_mode                 |  desired_dr_mode and current_dr_mode
+ *           ----------------------------------------------------------------
+ *           USB_DR_MODE_HOST        | only USB_DR_MODE_HOST
+ *           USB_DR_MODE_PERIPHERAL  | only USB_DR_MODE_PERIPHERAL
+ *           USB_DR_MODE_OTG         | only USB_DR_MODE_HOST
+ *           USB_DR_MODE_OTG         | only USB_DR_MODE_PERIPHERAL
+ *           USB_DR_MODE_OTG         | USB_DR_MODE_OTG
+ *
+ *           Desired_dr_role can be changed by means of debugfs.
+ * @root: debugfs root folder pointer
+ * @debug_disable:
+ */
+struct cdns3 {
+	struct device			*dev;
+	void __iomem			*xhci_regs;
+	struct resource			xhci_res[CDNS3_XHCI_RESOURCES_NUM];
+	struct cdns3_usb_regs __iomem	*dev_regs;
+
+	struct resource			otg_res;
+	struct cdns3_otg_legacy_regs	*otg_v0_regs;
+	struct cdns3_otg_regs		*otg_v1_regs;
+	struct cdns3_otg_common_regs	*otg_regs;
+#define CDNS3_CONTROLLER_V0	0
+#define CDNS3_CONTROLLER_V1	1
+	u32				version;
+
+	int				irq;
+	struct cdns3_role_driver	*roles[CDNS3_ROLE_END];
+	enum cdns3_roles		role;
+	struct platform_device		*host_dev;
+	struct cdns3_device		*gadget_dev;
+	struct phy			*phy;
+	struct work_struct		role_switch_wq;
+	int				in_lpm:1;
+	int				wakeup_int:1;
+	/* mutext used in workqueue*/
+	struct mutex			mutex;
+	enum usb_dr_mode		dr_mode;
+	enum usb_dr_mode		current_dr_mode;
+	enum usb_dr_mode		desired_dr_mode;
+	struct dentry			*root;
+	int				debug_disable:1;
+};
+
+void cdns3_role_stop(struct cdns3 *cdns);
+
+#endif /* __LINUX_CDNS3_CORE_H */
diff --git a/drivers/usb/cdns3/debug.h b/drivers/usb/cdns3/debug.h
new file mode 100644
index 000000000000..94f9ef15f899
--- /dev/null
+++ b/drivers/usb/cdns3/debug.h
@@ -0,0 +1,166 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Cadence USBSS DRD Driver.
+ * Debug header file.
+ *
+ * Copyright (C) 2018 Cadence.
+ *
+ * Author: Pawel Laszczak <pawell@cadence.com>
+ */
+#ifndef __LINUX_CDNS3_DEBUG
+#define __LINUX_CDNS3_DEBUG
+
+#include "core.h"
+
+static inline char *cdns3_decode_usb_irq(char *str,
+					 enum usb_device_speed speed,
+					 u32 usb_ists)
+{
+	int ret;
+
+	ret = sprintf(str, "IRQ %08x = ", usb_ists);
+
+	if (usb_ists & (USB_ISTS_CON2I | USB_ISTS_CONI)) {
+		ret += sprintf(str + ret, "Connection %s\n",
+			       usb_speed_string(speed));
+	}
+	if (usb_ists & USB_ISTS_CON2I || usb_ists & USB_ISTS_CONI)
+		ret += sprintf(str + ret, "Disconnection ");
+	if (usb_ists & USB_ISTS_L2ENTI)
+		ret += sprintf(str + ret, "suspended ");
+
+	if (usb_ists & USB_ISTS_L2EXTI)
+		ret += sprintf(str + ret, "L2 exit ");
+	if (usb_ists & USB_ISTS_U3EXTI)
+		ret += sprintf(str + ret, "U3 exit ");
+	if (usb_ists & USB_ISTS_UWRESI)
+		ret += sprintf(str + ret, "Warm Reset ");
+	if (usb_ists & USB_ISTS_UHRESI)
+		ret += sprintf(str + ret, "Hot Reset ");
+	if (usb_ists & USB_ISTS_U2RESI)
+		ret += sprintf(str + ret, "Reset");
+
+	return str;
+}
+
+static inline  char *cdns3_decode_ep_irq(char *str,
+					 u32 ep_sts,
+					 const char *ep_name)
+{
+	int ret;
+
+	ret = sprintf(str, "IRQ for %s: %08x ", ep_name, ep_sts);
+
+	if (ep_sts & EP_STS_SETUP)
+		ret += sprintf(str + ret, "SETUP ");
+	if (ep_sts & EP_STS_IOC)
+		ret += sprintf(str + ret, "IOC ");
+	if (ep_sts & EP_STS_ISP)
+		ret += sprintf(str + ret, "ISP ");
+	if (ep_sts & EP_STS_DESCMIS)
+		ret += sprintf(str + ret, "DESCMIS ");
+	if (ep_sts & EP_STS_STREAMR)
+		ret += sprintf(str + ret, "STREAMR ");
+	if (ep_sts & EP_STS_MD_EXIT)
+		ret += sprintf(str + ret, "MD_EXIT ");
+	if (ep_sts & EP_STS_TRBERR)
+		ret += sprintf(str + ret, "TRBERR ");
+	if (ep_sts & EP_STS_NRDY)
+		ret += sprintf(str + ret, "NRDY ");
+	if (ep_sts & EP_STS_PRIME)
+		ret += sprintf(str + ret, "PRIME ");
+	if (ep_sts & EP_STS_SIDERR)
+		ret += sprintf(str + ret, "SIDERRT ");
+	if (ep_sts & EP_STS_OUTSMM)
+		ret += sprintf(str + ret, "OUTSMM ");
+	if (ep_sts & EP_STS_ISOERR)
+		ret += sprintf(str + ret, "ISOERR ");
+	if (ep_sts & EP_STS_IOT)
+		ret += sprintf(str + ret, "IOT ");
+
+	return str;
+}
+
+static inline char *cdns3_decode_epx_irq(char *str,
+					 char *ep_name,
+					 u32 ep_sts)
+{
+	return cdns3_decode_ep_irq(str, ep_sts, ep_name);
+}
+
+static inline char *cdns3_decode_ep0_irq(char *str,
+					 int dir,
+					 u32 ep_sts)
+{
+	return cdns3_decode_ep_irq(str, ep_sts,
+				   dir ? "ep0IN" : "ep0OUT");
+}
+
+/**
+ * Debug a transfer ring.
+ *
+ * Prints out all TRBs in the endpoint ring, even those after the Link TRB.
+ *.
+ */
+static inline char *cdns3_dbg_ring(struct cdns3_endpoint *priv_ep,
+				   struct cdns3_trb *ring, char *str)
+{
+	dma_addr_t addr = priv_ep->trb_pool_dma;
+	struct cdns3_trb *trb;
+	int trb_per_sector;
+	int ret = 0;
+	int i;
+
+	trb_per_sector = GET_TRBS_PER_SEGMENT(priv_ep->type);
+
+	trb = &priv_ep->trb_pool[priv_ep->dequeue];
+	ret += sprintf(str + ret, "\n\t\tRing contents for %s:", priv_ep->name);
+
+	ret += sprintf(str + ret,
+		       "\n\t\tRing deq index: %d, trb: %p (virt), 0x%llx (dma)\n",
+		       priv_ep->dequeue, trb,
+		       (unsigned long long)cdns3_trb_virt_to_dma(priv_ep, trb));
+
+	trb = &priv_ep->trb_pool[priv_ep->enqueue];
+	ret += sprintf(str + ret,
+		       "\t\tRing enq index: %d, trb: %p (virt), 0x%llx (dma)\n",
+		       priv_ep->enqueue, trb,
+		       (unsigned long long)cdns3_trb_virt_to_dma(priv_ep, trb));
+
+	ret += sprintf(str + ret,
+		       "\t\tfree trbs: %d, CCS=%d, PCS=%d\n",
+		       priv_ep->free_trbs, priv_ep->ccs, priv_ep->pcs);
+
+	if (trb_per_sector > TRBS_PER_SEGMENT)
+		trb_per_sector = TRBS_PER_SEGMENT;
+
+	if (trb_per_sector > TRBS_PER_SEGMENT) {
+		sprintf(str + ret, "\t\tTo big transfer ring %d\n",
+			trb_per_sector);
+		return str;
+	}
+
+	for (i = 0; i < trb_per_sector; ++i) {
+		trb = &ring[i];
+		ret += sprintf(str + ret,
+			"\t\t@%pad %08x %08x %08x\n", &addr,
+			le32_to_cpu(trb->buffer),
+			le32_to_cpu(trb->length),
+			le32_to_cpu(trb->control));
+		addr += sizeof(*trb);
+	}
+
+	return str;
+}
+
+#ifdef CONFIG_DEBUG_FS
+void cdns3_debugfs_init(struct cdns3 *cdns);
+void cdns3_debugfs_exit(struct cdns3 *cdns);
+#else
+void cdns3_debugfs_init(struct cdns3 *cdns);
+{  }
+void cdns3_debugfs_exit(struct cdns3 *cdns);
+{  }
+#endif
+
+#endif /*__LINUX_CDNS3_DEBUG*/
diff --git a/drivers/usb/cdns3/debugfs.c b/drivers/usb/cdns3/debugfs.c
new file mode 100644
index 000000000000..d7919f5c1d90
--- /dev/null
+++ b/drivers/usb/cdns3/debugfs.c
@@ -0,0 +1,168 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Cadence USBSS DRD Controller DebugFS filer.
+ *
+ * Copyright (C) 2018 Cadence.
+ *
+ * Author: Pawel Laszczak <pawell@cadence.com>
+ */
+
+#include <linux/types.h>
+#include <linux/debugfs.h>
+#include <linux/seq_file.h>
+#include <linux/uaccess.h>
+
+#include "core.h"
+#include "gadget.h"
+#include "drd.h"
+
+static int cdns3_mode_show(struct seq_file *s, void *unused)
+{
+	struct cdns3 *cdns = s->private;
+
+	switch (cdns->current_dr_mode) {
+	case USB_DR_MODE_HOST:
+		seq_puts(s, "host\n");
+		break;
+	case USB_DR_MODE_PERIPHERAL:
+		seq_puts(s, "device\n");
+		break;
+	case USB_DR_MODE_OTG:
+		seq_puts(s, "otg\n");
+		break;
+	default:
+		seq_puts(s, "UNKNOWN mode\n");
+	}
+
+	return 0;
+}
+
+static int cdns3_mode_open(struct inode *inode, struct file *file)
+{
+	return single_open(file, cdns3_mode_show, inode->i_private);
+}
+
+static ssize_t cdns3_mode_write(struct file *file,
+				const char __user *ubuf,
+				size_t count, loff_t *ppos)
+{
+	struct seq_file	 *s = file->private_data;
+	struct cdns3 *cdns = s->private;
+	u32 mode = USB_DR_MODE_UNKNOWN;
+	char buf[32];
+
+	if (copy_from_user(&buf, ubuf, min_t(size_t, sizeof(buf) - 1, count)))
+		return -EFAULT;
+
+	if (!strncmp(buf, "host", 4)) {
+		if (cdns->dr_mode == USB_DR_MODE_HOST ||
+		    cdns->dr_mode == USB_DR_MODE_OTG) {
+			mode = USB_DR_MODE_HOST;
+		}
+	}
+
+	if (!strncmp(buf, "device", 6))
+		if (cdns->dr_mode == USB_DR_MODE_PERIPHERAL ||
+		    cdns->dr_mode == USB_DR_MODE_OTG)
+			mode = USB_DR_MODE_PERIPHERAL;
+
+	if (!strncmp(buf, "otg", 3) && cdns->dr_mode == USB_DR_MODE_OTG)
+		mode = USB_DR_MODE_OTG;
+
+	if (mode == USB_DR_MODE_UNKNOWN) {
+		dev_err(cdns->dev, "Failed: incorrect mode setting\n");
+		return -EFAULT;
+	}
+
+	if (cdns->current_dr_mode != mode) {
+		cdns->desired_dr_mode = mode;
+		cdns->debug_disable = 0;
+		cdns3_role_stop(cdns);
+		cdns3_drd_update_mode(cdns);
+		queue_work(system_freezable_wq, &cdns->role_switch_wq);
+	}
+
+	return count;
+}
+
+static const struct file_operations cdns3_mode_fops = {
+	.open			= cdns3_mode_open,
+	.write			= cdns3_mode_write,
+	.read			= seq_read,
+	.llseek			= seq_lseek,
+	.release		= single_release,
+};
+
+static int cdns3_disable_show(struct seq_file *s, void *unused)
+{
+	struct cdns3 *cdns = s->private;
+
+	if (!cdns->debug_disable)
+		seq_puts(s, "0\n");
+	else
+		seq_puts(s, "1\n");
+
+	return 0;
+}
+
+static ssize_t cdns3_disable_write(struct file *file,
+				   const char __user *ubuf,
+				   size_t count, loff_t *ppos)
+{
+	struct seq_file	 *s = file->private_data;
+	struct cdns3 *cdns = s->private;
+	int disable;
+	char buf[32];
+
+	if (copy_from_user(&buf, ubuf, min_t(size_t, sizeof(buf) - 1, count)))
+		return -EFAULT;
+
+	if (!strncmp(buf, "1", 1) || !strncmp(buf, "yes", 3)) {
+		disable = 1;
+	} else if (!strncmp(buf, "0", 1) || !strncmp(buf, "no", 2)) {
+		disable = 0;
+	} else {
+		dev_err(cdns->dev, "Failed: incorrect disable setting\n");
+		return -EFAULT;
+	}
+
+	if (disable != cdns->debug_disable) {
+		cdns->debug_disable = disable;
+		queue_work(system_freezable_wq, &cdns->role_switch_wq);
+	}
+
+	return count;
+}
+
+static int cdns3_disable_open(struct inode *inode, struct file *file)
+{
+	return single_open(file, cdns3_disable_show, inode->i_private);
+}
+
+static const struct file_operations cdns3_disable_fops = {
+	.open			= cdns3_disable_open,
+	.write			= cdns3_disable_write,
+	.read			= seq_read,
+	.llseek			= seq_lseek,
+	.release		= single_release,
+};
+
+void cdns3_debugfs_init(struct cdns3 *cdns)
+{
+	struct dentry *root;
+
+	root = debugfs_create_dir(dev_name(cdns->dev), NULL);
+	cdns->root = root;
+	if (IS_ENABLED(CONFIG_USB_CDNS3_GADGET) &&
+	    IS_ENABLED(CONFIG_USB_CDNS3_HOST))
+		debugfs_create_file("mode", 0644, root, cdns,
+				    &cdns3_mode_fops);
+
+	debugfs_create_file("disable", 0644, root, cdns,
+			    &cdns3_disable_fops);
+}
+
+void cdns3_debugfs_exit(struct cdns3 *cdns)
+{
+	debugfs_remove_recursive(cdns->root);
+}
diff --git a/drivers/usb/cdns3/drd.c b/drivers/usb/cdns3/drd.c
new file mode 100644
index 000000000000..b0c32302eb0b
--- /dev/null
+++ b/drivers/usb/cdns3/drd.c
@@ -0,0 +1,350 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Cadence USBSS DRD Driver.
+ *
+ * Copyright (C) 2018 Cadence.
+ *
+ * Author: Pawel Laszczak <pawell@cadence.com
+ *
+ */
+#include <linux/kernel.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <linux/usb/otg.h>
+
+#include "gadget.h"
+#include "drd.h"
+#include "core.h"
+
+static int cdns3_drd_switch_gadget(struct cdns3 *cdns, int on);
+static int cdns3_drd_switch_host(struct cdns3 *cdns, int on);
+
+/**
+ * cdns3_set_mode - change mode of OTG Core
+ * @cdns: pointer to context structure
+ * @mode: selected mode from cdns_role
+ */
+void cdns3_set_mode(struct cdns3 *cdns, enum usb_dr_mode mode)
+{
+	u32 reg;
+
+	cdns->current_dr_mode = mode;
+
+	switch (mode) {
+	case USB_DR_MODE_PERIPHERAL:
+		dev_info(cdns->dev, "Set controller to Gadget mode\n");
+		cdns3_drd_switch_gadget(cdns, 1);
+		break;
+	case USB_DR_MODE_HOST:
+		dev_info(cdns->dev, "Set controller to Host mode\n");
+		cdns3_drd_switch_host(cdns, 1);
+		break;
+	case USB_DR_MODE_OTG:
+		dev_info(cdns->dev, "Set controller to OTG mode\n");
+		if (cdns->version == CDNS3_CONTROLLER_V1) {
+			reg = readl(&cdns->otg_v1_regs->override);
+			reg |= OVERRIDE_IDPULLUP;
+			writel(reg, &cdns->otg_v1_regs->override);
+		} else {
+			reg = readl(&cdns->otg_v0_regs->ctrl1);
+			reg |= OVERRIDE_IDPULLUP_V0;
+			writel(reg, &cdns->otg_v0_regs->ctrl1);
+		}
+
+		/*
+		 * Hardware specification says: "ID_VALUE must be valid within
+		 * 50ms after idpullup is set to '1" so driver must wait
+		 * 50ms before reading this pin.
+		 */
+		usleep_range(50000, 60000);
+		break;
+	default:
+		cdns->current_dr_mode = USB_DR_MODE_UNKNOWN;
+		dev_err(cdns->dev, "Unsupported mode of operation %d\n", mode);
+		return;
+	}
+}
+
+int cdns3_get_id(struct cdns3 *cdns)
+{
+	int id;
+
+	id = readl(&cdns->otg_regs->sts) & OTGSTS_ID_VALUE;
+	dev_dbg(cdns->dev, "OTG ID: %d", id);
+	return  1 ; //id;
+}
+
+int cdns3_is_host(struct cdns3 *cdns)
+{
+	if (cdns->current_dr_mode == USB_DR_MODE_HOST)
+		return 1;
+	else if (!cdns3_get_id(cdns))
+		return 1;
+
+	return 0;
+}
+
+int cdns3_is_device(struct cdns3 *cdns)
+{
+	if (cdns->current_dr_mode == USB_DR_MODE_PERIPHERAL)
+		return 1;
+	else if (cdns->current_dr_mode == USB_DR_MODE_OTG)
+		if (cdns3_get_id(cdns))
+			return 1;
+
+	return 0;
+}
+
+/**
+ * cdns3_otg_disable_irq - Disable all OTG interrupts
+ * @cdns: Pointer to controller context structure
+ */
+static void cdns3_otg_disable_irq(struct cdns3 *cdns)
+{
+	writel(0, &cdns->otg_regs->ien);
+}
+
+/**
+ * cdns3_otg_enable_irq - enable id and sess_valid interrupts
+ * @cdns: Pointer to controller context structure
+ */
+static void cdns3_otg_enable_irq(struct cdns3 *cdns)
+{
+	writel(OTGIEN_ID_CHANGE_INT | OTGIEN_VBUSVALID_RISE_INT |
+	       OTGIEN_VBUSVALID_FALL_INT, &cdns->otg_regs->ien);
+}
+
+/**
+ * cdns3_drd_switch_host - start/stop host
+ * @cdns: Pointer to controller context structure
+ * @on: 1 for start, 0 for stop
+ *
+ * Returns 0 on success otherwise negative errno
+ */
+static int cdns3_drd_switch_host(struct cdns3 *cdns, int on)
+{
+	int ret;
+	u32 reg = OTGCMD_OTG_DIS;
+
+	/* switch OTG core */
+	if (on) {
+		writel(OTGCMD_HOST_BUS_REQ | reg, &cdns->otg_regs->cmd);
+
+		dev_dbg(cdns->dev, "Waiting for Host mode is turned on\n");
+		ret = cdns3_handshake(&cdns->otg_regs->sts, OTGSTS_XHCI_READY,
+				      OTGSTS_XHCI_READY, 100000);
+
+		if (ret)
+			return ret;
+	} else {
+		usleep_range(30, 40);
+		writel(OTGCMD_HOST_BUS_DROP | OTGCMD_DEV_BUS_DROP |
+		       OTGCMD_DEV_POWER_OFF | OTGCMD_HOST_POWER_OFF,
+		       &cdns->otg_regs->cmd);
+	}
+
+	return 0;
+}
+
+/**
+ * cdns3_drd_switch_gadget - start/stop gadget
+ * @cdns: Pointer to controller context structure
+ * @on: 1 for start, 0 for stop
+ *
+ * Returns 0 on success otherwise negative errno
+ */
+static int cdns3_drd_switch_gadget(struct cdns3 *cdns, int on)
+{
+	int ret;
+	u32 reg = OTGCMD_OTG_DIS;
+
+	/* switch OTG core */
+	if (on) {
+		writel(OTGCMD_DEV_BUS_REQ | reg, &cdns->otg_regs->cmd);
+
+		dev_dbg(cdns->dev, "Waiting for Device mode is turned on\n");
+
+		ret = cdns3_handshake(&cdns->otg_regs->sts, OTGSTS_DEV_READY,
+				      OTGSTS_DEV_READY, 100000);
+
+		if (ret)
+			return ret;
+	} else {
+		/*
+		 * driver should wait at least 10us after disabling Device
+		 * before turning-off Device (DEV_BUS_DROP)
+		 */
+		usleep_range(20, 30);
+		writel(OTGCMD_HOST_BUS_DROP | OTGCMD_DEV_BUS_DROP |
+		       OTGCMD_DEV_POWER_OFF | OTGCMD_HOST_POWER_OFF,
+		       &cdns->otg_regs->cmd);
+	}
+
+	return 0;
+}
+
+/**
+ * cdns3_init_otg_mode - initialize drd controller
+ * @cdns: Pointer to controller context structure
+ *
+ * Returns 0 on success otherwise negative errno
+ */
+static void cdns3_init_otg_mode(struct cdns3 *cdns)
+{
+	cdns3_otg_disable_irq(cdns);
+	/* clear all interrupts */
+	writel(~0, &cdns->otg_regs->ivect);
+
+	cdns3_set_mode(cdns, USB_DR_MODE_OTG);
+
+	if (cdns3_is_host(cdns))
+		cdns3_drd_switch_host(cdns, 1);
+	else
+		cdns3_drd_switch_gadget(cdns, 1);
+
+	cdns3_otg_enable_irq(cdns);
+}
+
+/**
+ * cdns3_drd_update_mode - initialize mode of operation
+ * @cdns: Pointer to controller context structure
+ *
+ * Returns 0 on success otherwise negative errno
+ */
+int cdns3_drd_update_mode(struct cdns3 *cdns)
+{
+	int ret = 0;
+
+	if (cdns->desired_dr_mode == cdns->current_dr_mode)
+		return ret;
+
+	cdns3_drd_switch_gadget(cdns, 0);
+	cdns3_drd_switch_host(cdns, 0);
+
+	switch (cdns->desired_dr_mode) {
+	case USB_DR_MODE_PERIPHERAL:
+		cdns3_set_mode(cdns, USB_DR_MODE_PERIPHERAL);
+		break;
+	case USB_DR_MODE_HOST:
+		cdns3_set_mode(cdns, USB_DR_MODE_HOST);
+		break;
+	case USB_DR_MODE_OTG:
+		cdns3_init_otg_mode(cdns);
+		break;
+	default:
+		dev_err(cdns->dev, "Unsupported mode of operation %d\n",
+			cdns->dr_mode);
+		return -EINVAL;
+	}
+
+	return ret;
+}
+
+/**
+ * cdns3_drd_irq - interrupt handler for OTG events
+ *
+ * @irq: irq number for cdns3 core device
+ * @data: structure of cdns3
+ *
+ * Returns IRQ_HANDLED or IRQ_NONE
+ */
+static irqreturn_t cdns3_drd_irq(int irq, void *data)
+{
+	irqreturn_t ret = IRQ_NONE;
+	struct cdns3 *cdns = data;
+	u32 reg;
+
+	if (cdns->dr_mode != USB_DR_MODE_OTG)
+		return ret;
+
+	reg = readl(&cdns->otg_regs->ivect);
+
+	if (!reg)
+		return ret;
+
+	if (reg & OTGIEN_ID_CHANGE_INT) {
+		dev_dbg(cdns->dev, "OTG IRQ: new ID: %d\n",
+			cdns3_get_id(cdns));
+
+		queue_work(system_freezable_wq, &cdns->role_switch_wq);
+
+		ret = IRQ_HANDLED;
+	}
+
+	writel(~0, &cdns->otg_regs->ivect);
+	return ret;
+}
+
+int cdns3_drd_init(struct cdns3 *cdns)
+{
+	void __iomem *regs;
+	int ret = 0;
+	u32 state;
+
+	regs = devm_ioremap_resource(cdns->dev, &cdns->otg_res);
+	if (IS_ERR(regs))
+		return PTR_ERR(regs);
+
+	/* Detection of DRD version. Controller has been released
+	 * in two versions. Both are similar, but they have same changes
+	 * in register maps.
+	 * The first register in old version is command register and it's read
+	 * only, so driver should read 0 from it. On the other hand, in v1
+	 * the first register contains device ID number which is not set to 0.
+	 * Driver uses this fact to detect the proper version of
+	 * controller.
+	 */
+	cdns->otg_v0_regs = regs;
+	if (!readl(&cdns->otg_v0_regs->cmd)) {
+		cdns->version  = CDNS3_CONTROLLER_V0;
+		cdns->otg_v1_regs = NULL;
+		cdns->otg_regs = regs;
+		dev_info(cdns->dev, "DRD version v0 (%08x)\n",
+			 readl(&cdns->otg_v0_regs->version));
+	} else {
+		cdns->otg_v0_regs = NULL;
+		cdns->otg_v1_regs = regs;
+		cdns->otg_regs = (void *)&cdns->otg_v1_regs->cmd;
+		cdns->version  = CDNS3_CONTROLLER_V1;
+		dev_info(cdns->dev, "DRD version v1 (ID: %08x, rev: %08x)\n",
+			 readl(&cdns->otg_v1_regs->did),
+			 readl(&cdns->otg_v1_regs->rid));
+	}
+
+	state = OTGSTS_STRAP(readl(&cdns->otg_regs->sts));
+
+	/* Update dr_mode according to STRAP configuration. */
+	cdns->dr_mode = USB_DR_MODE_OTG;
+	if (state == OTGSTS_STRAP_HOST) {
+		dev_info(cdns->dev, "Controller strapped to HOST\n");
+		cdns->dr_mode = USB_DR_MODE_HOST;
+	} else if (state == OTGSTS_STRAP_GADGET) {
+		dev_info(cdns->dev, "Controller strapped to PERIPHERAL\n");
+		cdns->dr_mode = USB_DR_MODE_PERIPHERAL;
+	}
+
+	cdns->desired_dr_mode = cdns->dr_mode;
+	cdns->current_dr_mode = USB_DR_MODE_UNKNOWN;
+
+	ret = devm_request_threaded_irq(cdns->dev, cdns->irq, cdns3_drd_irq,
+					NULL, IRQF_SHARED,
+					dev_name(cdns->dev), cdns);
+
+	if (ret)
+		return ret;
+
+	state = readl(&cdns->otg_regs->sts);
+	if (OTGSTS_OTG_NRDY(state) != 0) {
+		dev_err(cdns->dev, "Cadence USB3 OTG device not ready\n");
+		return -ENODEV;
+	}
+
+	ret = cdns3_drd_update_mode(cdns);
+
+	return ret;
+}
+
+int cdns3_drd_exit(struct cdns3 *cdns)
+{
+	return cdns3_drd_switch_host(cdns, 0);
+}
diff --git a/drivers/usb/cdns3/drd.h b/drivers/usb/cdns3/drd.h
new file mode 100644
index 000000000000..6a29cdcb492d
--- /dev/null
+++ b/drivers/usb/cdns3/drd.h
@@ -0,0 +1,162 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Cadence USB3 DRD header file.
+ *
+ * Copyright (C) 2018 Cadence.
+ *
+ * Author: Pawel Laszczak <pawell@cadence.com>
+ */
+#ifndef __LINUX_CDNS3_DRD
+#define __LINUX_CDNS3_DRD
+
+#include <linux/usb/otg.h>
+#include <linux/phy/phy.h>
+#include "core.h"
+
+/*  DRD register interface for version v1. */
+struct cdns3_otg_regs {
+	__le32 did;
+	__le32 rid;
+	__le32 capabilities;
+	__le32 reserved1;
+	__le32 cmd;
+	__le32 sts;
+	__le32 state;
+	__le32 reserved2;
+	__le32 ien;
+	__le32 ivect;
+	__le32 refclk;
+	__le32 tmr;
+	__le32 reserved3[4];
+	__le32 simulate;
+	__le32 override;
+	__le32 susp_ctrl;
+	__le32 reserved4;
+	__le32 anasts;
+	__le32 adp_ramp_time;
+	__le32 ctrl1;
+	__le32 ctrl2;
+};
+
+/*  DRD register interface for version v0. */
+struct cdns3_otg_legacy_regs {
+	__le32 cmd;
+	__le32 sts;
+	__le32 state;
+	__le32 refclk;
+	__le32 ien;
+	__le32 ivect;
+	__le32 reserved1[3];
+	__le32 tmr;
+	__le32 reserved2[2];
+	__le32 version;
+	__le32 capabilities;
+	__le32 reserved3[2];
+	__le32 simulate;
+	__le32 reserved4[5];
+	__le32 ctrl1;
+};
+
+/*
+ * Common registers interface for both version of DRD.
+ */
+struct cdns3_otg_common_regs {
+	__le32 cmd;
+	__le32 sts;
+	__le32 state;
+	__le32 different1;
+	__le32 ien;
+	__le32 ivect;
+};
+
+/* CDNS_RID - bitmasks */
+#define CDNS_RID(p)			((p) & GENMASK(15, 0))
+
+/* CDNS_VID - bitmasks */
+#define CDNS_DID(p)			((p) & GENMASK(31, 0))
+
+/* OTGCMD - bitmasks */
+/* "Request the bus for Device mode. */
+#define OTGCMD_DEV_BUS_REQ		BIT(0)
+/* Request the bus for Host mode */
+#define OTGCMD_HOST_BUS_REQ		BIT(1)
+/* Enable OTG mode. */
+#define OTGCMD_OTG_EN			BIT(2)
+/* Disable OTG mode */
+#define OTGCMD_OTG_DIS			BIT(3)
+/*"Configure OTG as A-Device. */
+#define OTGCMD_A_DEV_EN			BIT(4)
+/*"Configure OTG as A-Device. */
+#define OTGCMD_A_DEV_DIS		BIT(5)
+/* Drop the bus for Device mod	e. */
+#define OTGCMD_DEV_BUS_DROP		BIT(8)
+/* Drop the bus for Host mode*/
+#define OTGCMD_HOST_BUS_DROP		BIT(9)
+/* Power Down USBSS-DEV. */
+#define OTGCMD_DEV_POWER_OFF		BIT(11)
+/* Power Down CDNSXHCI. */
+#define OTGCMD_HOST_POWER_OFF		BIT(12)
+
+/* OTGIEN - bitmasks */
+/* ID change interrupt enable */
+#define OTGIEN_ID_CHANGE_INT		BIT(0)
+/* Vbusvalid fall detected interrupt enable.*/
+#define OTGIEN_VBUSVALID_RISE_INT	BIT(4)
+/* Vbusvalid fall detected interrupt enable */
+#define OTGIEN_VBUSVALID_FALL_INT	BIT(5)
+
+/* OTGSTS - bitmasks */
+/*
+ * Current value of the ID pin. It is only valid when idpullup in
+ *  OTGCTRL1_TYPE register is set to '1'.
+ */
+#define OTGSTS_ID_VALUE			BIT(0)
+/* Current value of the vbus_valid */
+#define OTGSTS_VBUS_VALID		BIT(1)
+/* Current value of the b_sess_vld */
+#define OTGSTS_SESSION_VALID		BIT(2)
+/*Device mode is active*/
+#define OTGSTS_DEV_ACTIVE		BIT(3)
+/* Host mode is active. */
+#define OTGSTS_HOST_ACTIVE		BIT(4)
+/* OTG Controller not ready. */
+#define OTGSTS_OTG_NRDY_MASK		BIT(11)
+#define OTGSTS_OTG_NRDY(p)		((p) & OTGSTS_OTG_NRDY_MASK)
+/*
+ * Value of the strap pins.
+ * 000 - no default configuration
+ * 010 - Controller initiall configured as Host
+ * 100 - Controller initially configured as Device
+ */
+#define OTGSTS_STRAP(p)			(((p) & GENMASK(14, 12)) >> 12)
+#define OTGSTS_STRAP_NO_DEFAULT_CFG	0x00
+#define OTGSTS_STRAP_HOST_OTG		0x01
+#define OTGSTS_STRAP_HOST		0x02
+#define OTGSTS_STRAP_GADGET		0x04
+/* Host mode is turned on. */
+#define OTGSTS_XHCI_READY		BIT(26)
+/* "Device mode is turned on .*/
+#define OTGSTS_DEV_READY		BIT(27)
+
+/* OTGSTATE- bitmasks */
+#define OTGSTATE_HOST_STATE_MASK	GENMASK(5, 3)
+#define OTGSTATE_HOST_STATE_IDLE	0x0
+#define OTGSTATE_HOST_STATE_VBUS_FALL   0x7
+#define OTGSTATE_HOST_STATE(p)		(((p) & OTGSTATE_HOST_STATE_MASK) >> 3)
+
+/* OTGREFCLK - bitmasks */
+#define OTGREFCLK_STB_CLK_SWITCH_EN	BIT(31)
+
+/* OVERRIDE - bitmasks */
+#define OVERRIDE_IDPULLUP		BIT(0)
+/* Only for CDNS3_CONTROLLER_V0 version */
+#define OVERRIDE_IDPULLUP_V0		BIT(24)
+
+int cdns3_is_host(struct cdns3 *cdns);
+int cdns3_is_device(struct cdns3 *cdns);
+int cdns3_get_id(struct cdns3 *cdns);
+int cdns3_drd_init(struct cdns3 *cdns);
+int cdns3_drd_exit(struct cdns3 *cdns);
+int cdns3_drd_update_mode(struct cdns3 *cdns);
+
+#endif /* __LINUX_CDNS3_DRD */
diff --git a/drivers/usb/cdns3/ep0.c b/drivers/usb/cdns3/ep0.c
new file mode 100644
index 000000000000..89cf1cde1555
--- /dev/null
+++ b/drivers/usb/cdns3/ep0.c
@@ -0,0 +1,896 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Cadence USBSS DRD Driver - gadget side.
+ *
+ * Copyright (C) 2018 Cadence Design Systems.
+ * Copyright (C) 2017-2018 NXP
+ *
+ * Authors: Pawel Jez <pjez@cadence.com>,
+ *          Pawel Laszczak <pawell@cadence.com>
+ *	    Peter Chen <peter.chen@nxp.com>
+ */
+
+#include <linux/usb/composite.h>
+
+#include "gadget.h"
+#include "trace.h"
+
+static struct usb_endpoint_descriptor cdns3_gadget_ep0_desc = {
+	.bLength = USB_DT_ENDPOINT_SIZE,
+	.bDescriptorType = USB_DT_ENDPOINT,
+	.bmAttributes =	USB_ENDPOINT_XFER_CONTROL,
+};
+
+/**
+ * cdns3_ep0_run_transfer - Do transfer on default endpoint hardware
+ * @priv_dev: extended gadget object
+ * @dma_addr: physical address where data is/will be stored
+ * @length: data length
+ * @erdy: set it to 1 when ERDY packet should be sent -
+ *        exit from flow control state
+ */
+static void cdns3_ep0_run_transfer(struct cdns3_device *priv_dev,
+				   dma_addr_t dma_addr,
+				   unsigned int length, int erdy)
+{
+	struct cdns3_usb_regs __iomem *regs = priv_dev->regs;
+	struct cdns3_endpoint *priv_ep = priv_dev->eps[0];
+
+	priv_ep->trb_pool->buffer = TRB_BUFFER(dma_addr);
+	priv_ep->trb_pool->length = TRB_LEN(length);
+	priv_ep->trb_pool->control = TRB_CYCLE | TRB_IOC | TRB_TYPE(TRB_NORMAL);
+
+	trace_cdns3_prepare_trb(priv_ep, priv_ep->trb_pool);
+
+	cdns3_select_ep(priv_dev, priv_dev->ep0_data_dir);
+
+	writel(EP_STS_TRBERR, &regs->ep_sts);
+	writel(EP_TRADDR_TRADDR(priv_ep->trb_pool_dma), &regs->ep_traddr);
+	trace_cdns3_doorbell_ep0(priv_dev->ep0_data_dir ? "ep0in" : "ep0out",
+				 readl(&regs->ep_traddr));
+
+	/* TRB should be prepared before starting transfer */
+	writel(EP_CMD_DRDY, &regs->ep_cmd);
+
+	if (erdy)
+		writel(EP_CMD_ERDY, &priv_dev->regs->ep_cmd);
+}
+
+/**
+ * cdns3_ep0_delegate_req - Returns status of handling setup packet
+ * Setup is handled by gadget driver
+ * @priv_dev: extended gadget object
+ * @ctrl_req: pointer to received setup packet
+ *
+ * Returns zero on success or negative value on failure
+ */
+static int cdns3_ep0_delegate_req(struct cdns3_device *priv_dev,
+				  struct usb_ctrlrequest *ctrl_req)
+{
+	int ret;
+
+	spin_unlock(&priv_dev->lock);
+	priv_dev->setup_pending = 1;
+	ret = priv_dev->gadget_driver->setup(&priv_dev->gadget, ctrl_req);
+	priv_dev->setup_pending = 0;
+	spin_lock(&priv_dev->lock);
+	return ret;
+}
+
+static void cdns3_prepare_setup_packet(struct cdns3_device *priv_dev)
+{
+	priv_dev->ep0_data_dir = 0;
+	priv_dev->ep0_stage = CDNS3_SETUP_STAGE;
+	cdns3_ep0_run_transfer(priv_dev, priv_dev->setup_dma,
+			       sizeof(struct usb_ctrlrequest), 0);
+}
+
+static void cdns3_ep0_complete_setup(struct cdns3_device *priv_dev,
+				     u8 send_stall, u8 send_erdy)
+{
+	struct cdns3_endpoint *priv_ep = priv_dev->eps[0];
+	struct usb_request *request;
+
+	request = cdns3_next_request(&priv_ep->pending_req_list);
+	if (request)
+		list_del_init(&request->list);
+
+	if (send_stall) {
+		dev_dbg(priv_dev->dev, "STALL for ep0\n");
+		/* set_stall on ep0 */
+		cdns3_select_ep(priv_dev, 0x00);
+		writel(EP_CMD_SSTALL, &priv_dev->regs->ep_cmd);
+	} else {
+		cdns3_prepare_setup_packet(priv_dev);
+	}
+
+	priv_dev->ep0_stage = CDNS3_SETUP_STAGE;
+	writel((send_erdy ? EP_CMD_ERDY : 0) | EP_CMD_REQ_CMPL,
+	       &priv_dev->regs->ep_cmd);
+}
+
+/**
+ * cdns3_req_ep0_set_configuration - Handling of SET_CONFIG standard USB request
+ * @priv_dev: extended gadget object
+ * @ctrl_req: pointer to received setup packet
+ *
+ * Returns 0 if success, USB_GADGET_DELAYED_STATUS on deferred status stage,
+ * error code on error
+ */
+static int cdns3_req_ep0_set_configuration(struct cdns3_device *priv_dev,
+					   struct usb_ctrlrequest *ctrl_req)
+{
+	enum usb_device_state device_state = priv_dev->gadget.state;
+	struct cdns3_endpoint *priv_ep;
+	u32 config = le16_to_cpu(ctrl_req->wValue);
+	int result = 0;
+	int i;
+
+	switch (device_state) {
+	case USB_STATE_ADDRESS:
+		/* Configure non-control EPs */
+		for (i = 0; i < CDNS3_ENDPOINTS_MAX_COUNT; i++) {
+			priv_ep = priv_dev->eps[i];
+			if (!priv_ep)
+				continue;
+
+			if (priv_ep->flags & EP_CLAIMED)
+				cdns3_ep_config(priv_ep);
+		}
+
+		result = cdns3_ep0_delegate_req(priv_dev, ctrl_req);
+
+		if (result)
+			return result;
+
+		if (config) {
+			cdns3_set_hw_configuration(priv_dev);
+		} else {
+			cdns3_hw_reset_eps_config(priv_dev);
+			usb_gadget_set_state(&priv_dev->gadget,
+					     USB_STATE_ADDRESS);
+		}
+		break;
+	case USB_STATE_CONFIGURED:
+		result = cdns3_ep0_delegate_req(priv_dev, ctrl_req);
+
+		if (!config && !result) {
+			cdns3_hw_reset_eps_config(priv_dev);
+			usb_gadget_set_state(&priv_dev->gadget,
+					     USB_STATE_ADDRESS);
+		}
+		break;
+	default:
+		result = -EINVAL;
+	}
+
+	return result;
+}
+
+/**
+ * cdns3_req_ep0_set_address - Handling of SET_ADDRESS standard USB request
+ * @priv_dev: extended gadget object
+ * @ctrl_req: pointer to received setup packet
+ *
+ * Returns 0 if success, error code on error
+ */
+static int cdns3_req_ep0_set_address(struct cdns3_device *priv_dev,
+				     struct usb_ctrlrequest *ctrl_req)
+{
+	enum usb_device_state device_state = priv_dev->gadget.state;
+	u32 reg;
+	u32 addr;
+
+	addr = le16_to_cpu(ctrl_req->wValue);
+
+	if (addr > USB_DEVICE_MAX_ADDRESS) {
+		dev_err(priv_dev->dev,
+			"Device address (%d) cannot be greater than %d\n",
+			addr, USB_DEVICE_MAX_ADDRESS);
+		return -EINVAL;
+	}
+
+	if (device_state == USB_STATE_CONFIGURED) {
+		dev_err(priv_dev->dev,
+			"can't set_address from configured state\n");
+		return -EINVAL;
+	}
+
+	reg = readl(&priv_dev->regs->usb_cmd);
+
+	writel(reg | USB_CMD_FADDR(addr) | USB_CMD_SET_ADDR,
+	       &priv_dev->regs->usb_cmd);
+
+	usb_gadget_set_state(&priv_dev->gadget,
+			     (addr ? USB_STATE_ADDRESS : USB_STATE_DEFAULT));
+
+	return 0;
+}
+
+/**
+ * cdns3_req_ep0_get_status - Handling of GET_STATUS standard USB request
+ * @priv_dev: extended gadget object
+ * @ctrl_req: pointer to received setup packet
+ *
+ * Returns 0 if success, error code on error
+ */
+static int cdns3_req_ep0_get_status(struct cdns3_device *priv_dev,
+				    struct usb_ctrlrequest *ctrl)
+{
+	__le16 *response_pkt;
+	u16 usb_status = 0;
+	u32 recip;
+	u32 reg;
+
+	recip = ctrl->bRequestType & USB_RECIP_MASK;
+
+	switch (recip) {
+	case USB_RECIP_DEVICE:
+		/* self powered */
+		if (priv_dev->is_selfpowered)
+			usb_status = BIT(USB_DEVICE_SELF_POWERED);
+
+		if (priv_dev->wake_up_flag)
+			usb_status |= BIT(USB_DEVICE_REMOTE_WAKEUP);
+
+		if (priv_dev->gadget.speed != USB_SPEED_SUPER)
+			break;
+
+		reg = readl(&priv_dev->regs->usb_sts);
+
+		if (priv_dev->u1_allowed)
+			usb_status |= BIT(USB_DEV_STAT_U1_ENABLED);
+
+		if (priv_dev->u2_allowed)
+			usb_status |= BIT(USB_DEV_STAT_U2_ENABLED);
+
+		break;
+	case USB_RECIP_INTERFACE:
+		return cdns3_ep0_delegate_req(priv_dev, ctrl);
+	case USB_RECIP_ENDPOINT:
+		/* check if endpoint is stalled */
+		cdns3_select_ep(priv_dev, ctrl->wIndex);
+		if (EP_STS_STALL(readl(&priv_dev->regs->ep_sts)))
+			usb_status =  BIT(USB_ENDPOINT_HALT);
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	response_pkt = (__le16 *)priv_dev->setup_buf;
+	*response_pkt = cpu_to_le16(usb_status);
+
+	cdns3_ep0_run_transfer(priv_dev, priv_dev->setup_dma,
+			       sizeof(*response_pkt), 1);
+	return 0;
+}
+
+static int cdns3_ep0_feature_handle_device(struct cdns3_device *priv_dev,
+					   struct usb_ctrlrequest *ctrl,
+					   int set)
+{
+	enum usb_device_state state;
+	enum usb_device_speed speed;
+	int ret = 0;
+	u32 wValue;
+	u32 wIndex;
+	u16 tmode;
+
+	wValue = le16_to_cpu(ctrl->wValue);
+	wIndex = le16_to_cpu(ctrl->wIndex);
+	state = priv_dev->gadget.state;
+	speed = priv_dev->gadget.speed;
+
+	switch (ctrl->wValue) {
+	case USB_DEVICE_REMOTE_WAKEUP:
+		priv_dev->wake_up_flag = !!set;
+		break;
+	case USB_DEVICE_U1_ENABLE:
+		if (state != USB_STATE_CONFIGURED || speed != USB_SPEED_SUPER)
+			return -EINVAL;
+
+		priv_dev->u1_allowed = !!set;
+		break;
+	case USB_DEVICE_U2_ENABLE:
+		if (state != USB_STATE_CONFIGURED || speed != USB_SPEED_SUPER)
+			return -EINVAL;
+
+		priv_dev->u2_allowed = !!set;
+		break;
+	case USB_DEVICE_LTM_ENABLE:
+		ret = -EINVAL;
+		break;
+	case USB_DEVICE_TEST_MODE:
+		if (state != USB_STATE_CONFIGURED || speed > USB_SPEED_HIGH)
+			return -EINVAL;
+
+		tmode = le16_to_cpu(ctrl->wIndex);
+
+		if (!set || (tmode & 0xff) != 0)
+			return -EINVAL;
+
+		switch (tmode >> 8) {
+		case TEST_J:
+		case TEST_K:
+		case TEST_SE0_NAK:
+		case TEST_PACKET:
+			cdns3_ep0_complete_setup(priv_dev, 0, 1);
+			/**
+			 *  Little delay to give the controller some time
+			 * for sending status stage.
+			 * This time should be less then 3ms.
+			 */
+			usleep_range(1000, 2000);
+			cdns3_set_register_bit(&priv_dev->regs->usb_cmd,
+					       USB_CMD_STMODE |
+					       USB_STS_TMODE_SEL(tmode - 1));
+			break;
+		default:
+			ret = -EINVAL;
+		}
+		break;
+	default:
+		ret = -EINVAL;
+	}
+
+	return ret;
+}
+
+static int cdns3_ep0_feature_handle_intf(struct cdns3_device *priv_dev,
+					 struct usb_ctrlrequest *ctrl,
+					 int set)
+{
+	u32 wValue;
+	int ret = 0;
+
+	wValue = le16_to_cpu(ctrl->wValue);
+
+	switch (wValue) {
+	case USB_INTRF_FUNC_SUSPEND:
+		break;
+	default:
+		ret = -EINVAL;
+	}
+
+	return ret;
+}
+
+static int cdns3_ep0_feature_handle_endpoint(struct cdns3_device *priv_dev,
+					     struct usb_ctrlrequest *ctrl,
+					     int set)
+{
+	struct cdns3_endpoint *priv_ep;
+	int ret = 0;
+	u8 index;
+
+	if (le16_to_cpu(ctrl->wValue) != USB_ENDPOINT_HALT)
+		return -EINVAL;
+
+	if (!(ctrl->wIndex & ~USB_DIR_IN))
+		return 0;
+
+	index = cdns3_ep_addr_to_index(ctrl->wIndex);
+	priv_ep = priv_dev->eps[index];
+
+	cdns3_select_ep(priv_dev, ctrl->wIndex);
+
+	if (set) {
+		writel(EP_CMD_SSTALL, &priv_dev->regs->ep_cmd);
+		priv_ep->flags |= EP_STALL;
+	} else {
+		struct usb_request *request;
+
+		if (priv_dev->eps[index]->flags & EP_WEDGE) {
+			cdns3_select_ep(priv_dev, 0x00);
+			return 0;
+		}
+
+		writel(EP_CMD_CSTALL | EP_CMD_EPRST, &priv_dev->regs->ep_cmd);
+
+		/* wait for EPRST cleared */
+		ret = cdns3_handshake(&priv_dev->regs->ep_cmd,
+				      EP_CMD_EPRST, 0, 100);
+		if (ret)
+			return -EINVAL;
+
+		priv_ep->flags &= ~EP_STALL;
+
+		request = cdns3_next_request(&priv_ep->pending_req_list);
+		if (request)
+			cdns3_ep_run_transfer(priv_ep, request);
+	}
+	return ret;
+}
+
+/**
+ * cdns3_req_ep0_handle_feature -
+ * Handling of GET/SET_FEATURE standard USB request
+ *
+ * @priv_dev: extended gadget object
+ * @ctrl_req: pointer to received setup packet
+ * @set: must be set to 1 for SET_FEATURE request
+ *
+ * Returns 0 if success, error code on error
+ */
+static int cdns3_req_ep0_handle_feature(struct cdns3_device *priv_dev,
+					struct usb_ctrlrequest *ctrl,
+					int set)
+{
+	int ret = 0;
+	u32 recip;
+
+	recip = ctrl->bRequestType & USB_RECIP_MASK;
+
+	switch (recip) {
+	case USB_RECIP_DEVICE:
+		ret = cdns3_ep0_feature_handle_device(priv_dev, ctrl, set);
+		break;
+	case USB_RECIP_INTERFACE:
+		ret = cdns3_ep0_feature_handle_intf(priv_dev, ctrl, set);
+		break;
+	case USB_RECIP_ENDPOINT:
+		ret = cdns3_ep0_feature_handle_endpoint(priv_dev, ctrl, set);
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	return ret;
+}
+
+/**
+ * cdns3_req_ep0_set_sel - Handling of SET_SEL standard USB request
+ * @priv_dev: extended gadget object
+ * @ctrl_req: pointer to received setup packet
+ *
+ * Returns 0 if success, error code on error
+ */
+static int cdns3_req_ep0_set_sel(struct cdns3_device *priv_dev,
+				 struct usb_ctrlrequest *ctrl_req)
+{
+	if (priv_dev->gadget.state < USB_STATE_ADDRESS)
+		return -EINVAL;
+
+	if (ctrl_req->wLength != 6) {
+		dev_err(priv_dev->dev, "Set SEL should be 6 bytes, got %d\n",
+			ctrl_req->wLength);
+		return -EINVAL;
+	}
+
+	cdns3_ep0_run_transfer(priv_dev, priv_dev->setup_dma, 6, 1);
+	return 0;
+}
+
+/**
+ * cdns3_req_ep0_set_isoch_delay -
+ * Handling of GET_ISOCH_DELAY standard USB request
+ * @priv_dev: extended gadget object
+ * @ctrl_req: pointer to received setup packet
+ *
+ * Returns 0 if success, error code on error
+ */
+static int cdns3_req_ep0_set_isoch_delay(struct cdns3_device *priv_dev,
+					 struct usb_ctrlrequest *ctrl_req)
+{
+	if (ctrl_req->wIndex || ctrl_req->wLength)
+		return -EINVAL;
+
+	priv_dev->isoch_delay = ctrl_req->wValue;
+
+	return 0;
+}
+
+/**
+ * cdns3_ep0_standard_request - Handling standard USB requests
+ * @priv_dev: extended gadget object
+ * @ctrl_req: pointer to received setup packet
+ *
+ * Returns 0 if success, error code on error
+ */
+static int cdns3_ep0_standard_request(struct cdns3_device *priv_dev,
+				      struct usb_ctrlrequest *ctrl_req)
+{
+	int ret;
+
+	switch (ctrl_req->bRequest) {
+	case USB_REQ_SET_ADDRESS:
+		ret = cdns3_req_ep0_set_address(priv_dev, ctrl_req);
+		break;
+	case USB_REQ_SET_CONFIGURATION:
+		ret = cdns3_req_ep0_set_configuration(priv_dev, ctrl_req);
+		break;
+	case USB_REQ_GET_STATUS:
+		ret = cdns3_req_ep0_get_status(priv_dev, ctrl_req);
+		break;
+	case USB_REQ_CLEAR_FEATURE:
+		ret = cdns3_req_ep0_handle_feature(priv_dev, ctrl_req, 0);
+		break;
+	case USB_REQ_SET_FEATURE:
+		ret = cdns3_req_ep0_handle_feature(priv_dev, ctrl_req, 1);
+		break;
+	case USB_REQ_SET_SEL:
+		ret = cdns3_req_ep0_set_sel(priv_dev, ctrl_req);
+		break;
+	case USB_REQ_SET_ISOCH_DELAY:
+		ret = cdns3_req_ep0_set_isoch_delay(priv_dev, ctrl_req);
+		break;
+	default:
+		ret = cdns3_ep0_delegate_req(priv_dev, ctrl_req);
+		break;
+	}
+
+	return ret;
+}
+
+static void __pending_setup_status_handler(struct cdns3_device *priv_dev)
+{
+	struct usb_request *request = priv_dev->pending_status_request;
+
+	if (priv_dev->status_completion_no_call && request &&
+	    request->complete) {
+		request->complete(&priv_dev->eps[0]->endpoint, request);
+		priv_dev->status_completion_no_call = 0;
+	}
+}
+
+void cdns3_pending_setup_status_handler(struct work_struct *work)
+{
+	struct cdns3_device *priv_dev = container_of(work, struct cdns3_device,
+			pending_status_wq);
+	unsigned long flags;
+
+	spin_lock_irqsave(&priv_dev->lock, flags);
+	__pending_setup_status_handler(priv_dev);
+	spin_unlock_irqrestore(&priv_dev->lock, flags);
+}
+
+/**
+ * cdns3_gadget_ep_giveback - call struct usb_request's ->complete callback
+ * @priv_ep: The endpoint to whom the request belongs to
+ * @priv_req: The request we're giving back
+ * @status: completion code for the request
+ *
+ * Must be called with controller's lock held and interrupts disabled. This
+ * function will unmap @req and call its ->complete() callback to notify upper
+ * layers that it has completed.
+ */
+
+void cdns3_gadget_ep0_giveback(struct cdns3_device *priv_dev,
+			       int status)
+{
+	struct cdns3_endpoint *priv_ep;
+	struct usb_request *request;
+
+	priv_ep = priv_dev->eps[0];
+	request = cdns3_next_request(&priv_ep->pending_req_list);
+
+	priv_ep->dir = priv_dev->ep0_data_dir;
+	cdns3_gadget_giveback(priv_ep, to_cdns3_request(request), status);
+}
+
+/**
+ * cdns3_ep0_setup_phase - Handling setup USB requests
+ * @priv_dev: extended gadget object
+ */
+static void cdns3_ep0_setup_phase(struct cdns3_device *priv_dev)
+{
+	struct usb_ctrlrequest *ctrl = priv_dev->setup_buf;
+	struct cdns3_endpoint *priv_ep = priv_dev->eps[0];
+	int result;
+
+	priv_dev->ep0_data_dir = ctrl->bRequestType & USB_DIR_IN;
+
+	trace_cdns3_ctrl_req(ctrl);
+
+	if (!list_empty(&priv_ep->pending_req_list)) {
+		struct usb_request *request;
+
+		request = cdns3_next_request(&priv_ep->pending_req_list);
+		priv_ep->dir = priv_dev->ep0_data_dir;
+		cdns3_gadget_giveback(priv_ep, to_cdns3_request(request),
+				      -ECONNRESET);
+	}
+
+	if (le16_to_cpu(ctrl->wLength))
+		priv_dev->ep0_stage = CDNS3_DATA_STAGE;
+	else
+		priv_dev->ep0_stage = CDNS3_STATUS_STAGE;
+
+	if ((ctrl->bRequestType & USB_TYPE_MASK) == USB_TYPE_STANDARD)
+		result = cdns3_ep0_standard_request(priv_dev, ctrl);
+	else
+		result = cdns3_ep0_delegate_req(priv_dev, ctrl);
+
+	if (priv_dev->ep0_stage == CDNS3_STATUS_STAGE &&
+	    result != USB_GADGET_DELAYED_STATUS) {
+		cdns3_ep0_complete_setup(priv_dev, 0, 1);
+	} else if (result < 0) {
+		cdns3_ep0_complete_setup(priv_dev, 1, 1);
+	}
+}
+
+static void cdns3_transfer_completed(struct cdns3_device *priv_dev)
+{
+	struct cdns3_endpoint *priv_ep = priv_dev->eps[0];
+
+	if (!list_empty(&priv_ep->pending_req_list)) {
+		struct usb_request *request;
+
+		trace_cdns3_complete_trb(priv_ep, priv_ep->trb_pool);
+		request = cdns3_next_request(&priv_ep->pending_req_list);
+
+		request->actual =
+			TRB_LEN(le32_to_cpu(priv_ep->trb_pool->length));
+
+		priv_ep->dir = priv_dev->ep0_data_dir;
+		cdns3_gadget_giveback(priv_ep, to_cdns3_request(request), 0);
+	}
+
+	cdns3_ep0_complete_setup(priv_dev, 0, 0);
+}
+
+/**
+ * cdns3_check_new_setup - Check if controller receive new SETUP packet.
+ * @priv_dev: extended gadget object
+ *
+ * The SETUP packet can be kept in on-chip memory or in system memory.
+ */
+static bool cdns3_check_new_setup(struct cdns3_device *priv_dev)
+{
+	u32 ep_sts_reg;
+
+	cdns3_select_ep(priv_dev, 0 | USB_DIR_OUT);
+	ep_sts_reg = readl(&priv_dev->regs->ep_sts);
+
+	return !!(ep_sts_reg & (EP_STS_SETUP | EP_STS_STPWAIT));
+}
+
+/**
+ * cdns3_check_ep0_interrupt_proceed - Processes interrupt related to endpoint 0
+ * @priv_dev: extended gadget object
+ * @dir: USB_DIR_IN for IN direction, USB_DIR_OUT for OUT direction
+ */
+void cdns3_check_ep0_interrupt_proceed(struct cdns3_device *priv_dev, int dir)
+{
+	u32 ep_sts_reg;
+
+	cdns3_select_ep(priv_dev, dir);
+
+	ep_sts_reg = readl(&priv_dev->regs->ep_sts);
+	writel(ep_sts_reg, &priv_dev->regs->ep_sts);
+
+	trace_cdns3_ep0_irq(priv_dev, ep_sts_reg);
+
+	__pending_setup_status_handler(priv_dev);
+
+	if ((ep_sts_reg & EP_STS_SETUP)) {
+		cdns3_ep0_setup_phase(priv_dev);
+	} else if ((ep_sts_reg & EP_STS_IOC) || (ep_sts_reg & EP_STS_ISP)) {
+		priv_dev->ep0_data_dir = dir;
+		cdns3_transfer_completed(priv_dev);
+	}
+
+	if (ep_sts_reg & EP_STS_DESCMIS) {
+		if (dir == 0 && !priv_dev->setup_pending)
+			cdns3_prepare_setup_packet(priv_dev);
+	}
+}
+
+/**
+ * cdns3_gadget_ep0_enable
+ * Function shouldn't be called by gadget driver,
+ * endpoint 0 is allways active
+ */
+static int cdns3_gadget_ep0_enable(struct usb_ep *ep,
+				   const struct usb_endpoint_descriptor *desc)
+{
+	return -EINVAL;
+}
+
+/**
+ * cdns3_gadget_ep0_disable
+ * Function shouldn't be called by gadget driver,
+ * endpoint 0 is allways active
+ */
+static int cdns3_gadget_ep0_disable(struct usb_ep *ep)
+{
+	return -EINVAL;
+}
+
+/**
+ * cdns3_gadget_ep0_set_halt
+ * @ep: pointer to endpoint zero object
+ * @value: 1 for set stall, 0 for clear stall
+ *
+ * Returns 0
+ */
+static int cdns3_gadget_ep0_set_halt(struct usb_ep *ep, int value)
+{
+	/* TODO */
+	return 0;
+}
+
+/**
+ * cdns3_gadget_ep0_queue Transfer data on endpoint zero
+ * @ep: pointer to endpoint zero object
+ * @request: pointer to request object
+ * @gfp_flags: gfp flags
+ *
+ * Returns 0 on success, error code elsewhere
+ */
+static int cdns3_gadget_ep0_queue(struct usb_ep *ep,
+				  struct usb_request *request,
+				  gfp_t gfp_flags)
+{
+	struct cdns3_endpoint *priv_ep = ep_to_cdns3_ep(ep);
+	struct cdns3_device *priv_dev = priv_ep->cdns3_dev;
+	unsigned long flags;
+	int erdy_sent = 0;
+	int ret = 0;
+
+	dev_dbg(priv_dev->dev, "Queue to Ep0%s L: %d\n",
+		priv_dev->ep0_data_dir ? "IN" : "OUT",
+		request->length);
+
+	/* cancel the request if controller receive new SETUP packet. */
+	if (cdns3_check_new_setup(priv_dev))
+		return -ECONNRESET;
+
+	/* send STATUS stage. Should be called only for SET_CONFIGURATION */
+	if (priv_dev->ep0_stage == CDNS3_STATUS_STAGE) {
+		spin_lock_irqsave(&priv_dev->lock, flags);
+		cdns3_select_ep(priv_dev, 0x00);
+
+		erdy_sent = !priv_dev->hw_configured_flag;
+		cdns3_set_hw_configuration(priv_dev);
+
+		if (!erdy_sent)
+			cdns3_ep0_complete_setup(priv_dev, 0, 1);
+
+		request->actual = 0;
+		priv_dev->status_completion_no_call = true;
+		priv_dev->pending_status_request = request;
+		spin_unlock_irqrestore(&priv_dev->lock, flags);
+
+		/*
+		 * Since there is no completion interrupt for status stage,
+		 * it needs to call ->completion in software after
+		 * ep0_queue is back.
+		 */
+		queue_work(system_freezable_wq, &priv_dev->pending_status_wq);
+		return 0;
+	}
+
+	spin_lock_irqsave(&priv_dev->lock, flags);
+	if (!list_empty(&priv_ep->pending_req_list)) {
+		dev_err(priv_dev->dev,
+			"can't handle multiple requests for ep0\n");
+		spin_unlock_irqrestore(&priv_dev->lock, flags);
+		return -EBUSY;
+	}
+
+	ret = usb_gadget_map_request_by_dev(priv_dev->sysdev, request,
+					    priv_dev->ep0_data_dir);
+	if (ret) {
+		spin_unlock_irqrestore(&priv_dev->lock, flags);
+		dev_err(priv_dev->dev, "failed to map request\n");
+		return -EINVAL;
+	}
+
+	request->status = -EINPROGRESS;
+	list_add_tail(&request->list, &priv_ep->pending_req_list);
+	cdns3_ep0_run_transfer(priv_dev, request->dma, request->length, 1);
+	spin_unlock_irqrestore(&priv_dev->lock, flags);
+
+	return ret;
+}
+
+/**
+ * cdns3_gadget_ep_set_wedge Set wedge on selected endpoint
+ * @ep: endpoint object
+ *
+ * Returns 0
+ */
+int cdns3_gadget_ep_set_wedge(struct usb_ep *ep)
+{
+	struct cdns3_endpoint *priv_ep = ep_to_cdns3_ep(ep);
+	struct cdns3_device *priv_dev = priv_ep->cdns3_dev;
+
+	dev_dbg(priv_dev->dev, "Wedge for %s\n", ep->name);
+	cdns3_gadget_ep_set_halt(ep, 1);
+	priv_ep->flags |= EP_WEDGE;
+
+	return 0;
+}
+
+const struct usb_ep_ops cdns3_gadget_ep0_ops = {
+	.enable = cdns3_gadget_ep0_enable,
+	.disable = cdns3_gadget_ep0_disable,
+	.alloc_request = cdns3_gadget_ep_alloc_request,
+	.free_request = cdns3_gadget_ep_free_request,
+	.queue = cdns3_gadget_ep0_queue,
+	.dequeue = cdns3_gadget_ep_dequeue,
+	.set_halt = cdns3_gadget_ep0_set_halt,
+	.set_wedge = cdns3_gadget_ep_set_wedge,
+};
+
+/**
+ * cdns3_ep0_config - Configures default endpoint
+ * @priv_dev: extended gadget object
+ *
+ * Functions sets parameters: maximal packet size and enables interrupts
+ */
+void cdns3_ep0_config(struct cdns3_device *priv_dev)
+{
+	struct cdns3_usb_regs __iomem *regs;
+	struct cdns3_endpoint *priv_ep;
+	u32 max_packet_size = 64;
+
+	regs = priv_dev->regs;
+
+	if (priv_dev->gadget.speed == USB_SPEED_SUPER)
+		max_packet_size = 512;
+
+	priv_ep = priv_dev->eps[0];
+
+	if (!list_empty(&priv_ep->pending_req_list)) {
+		struct usb_request *request;
+
+		request = cdns3_next_request(&priv_ep->pending_req_list);
+		list_del_init(&request->list);
+	}
+
+	priv_dev->u1_allowed = 0;
+	priv_dev->u2_allowed = 0;
+
+	priv_dev->gadget.ep0->maxpacket = max_packet_size;
+	cdns3_gadget_ep0_desc.wMaxPacketSize = cpu_to_le16(max_packet_size);
+
+	/* init ep out */
+	cdns3_select_ep(priv_dev, USB_DIR_OUT);
+
+	writel(EP_CFG_ENABLE | EP_CFG_MAXPKTSIZE(max_packet_size),
+	       &regs->ep_cfg);
+
+	writel(EP_STS_EN_SETUPEN | EP_STS_EN_DESCMISEN | EP_STS_EN_TRBERREN,
+	       &regs->ep_sts_en);
+
+	/* init ep in */
+	cdns3_select_ep(priv_dev, USB_DIR_IN);
+
+	writel(EP_CFG_ENABLE | EP_CFG_MAXPKTSIZE(max_packet_size),
+	       &regs->ep_cfg);
+
+	writel(EP_STS_EN_SETUPEN | EP_STS_EN_TRBERREN, &regs->ep_sts_en);
+
+	cdns3_set_register_bit(&regs->usb_conf, USB_CONF_U1DS | USB_CONF_U2DS);
+}
+
+/**
+ * cdns3_init_ep0 Initializes software endpoint 0 of gadget
+ * @priv_dev: extended gadget object
+ * @ep_priv: extended endpoint object
+ *
+ * Returns 0 on success else error code.
+ */
+int cdns3_init_ep0(struct cdns3_device *priv_dev,
+		   struct cdns3_endpoint *priv_ep)
+{
+	sprintf(priv_ep->name, "ep0");
+
+	/* fill linux fields */
+	priv_ep->endpoint.ops = &cdns3_gadget_ep0_ops;
+	priv_ep->endpoint.maxburst = 1;
+	usb_ep_set_maxpacket_limit(&priv_ep->endpoint,
+				   CDNS3_EP0_MAX_PACKET_LIMIT);
+	priv_ep->endpoint.address = 0;
+	priv_ep->endpoint.caps.type_control = 1;
+	priv_ep->endpoint.caps.dir_in = 1;
+	priv_ep->endpoint.caps.dir_out = 1;
+	priv_ep->endpoint.name = priv_ep->name;
+	priv_ep->endpoint.desc = &cdns3_gadget_ep0_desc;
+	priv_dev->gadget.ep0 = &priv_ep->endpoint;
+	priv_ep->type = USB_ENDPOINT_XFER_CONTROL;
+
+	return cdns3_allocate_trb_pool(priv_ep);
+}
diff --git a/drivers/usb/cdns3/gadget-export.h b/drivers/usb/cdns3/gadget-export.h
new file mode 100644
index 000000000000..577469eee961
--- /dev/null
+++ b/drivers/usb/cdns3/gadget-export.h
@@ -0,0 +1,28 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Cadence USBSS DRD Driver - Gadget Export APIs.
+ *
+ * Copyright (C) 2017 NXP
+ * Copyright (C) 2017-2018 NXP
+ *
+ * Authors: Peter Chen <peter.chen@nxp.com>
+ */
+#ifndef __LINUX_CDNS3_GADGET_EXPORT
+#define __LINUX_CDNS3_GADGET_EXPORT
+
+#ifdef CONFIG_USB_CDNS3_GADGET
+
+int cdns3_gadget_init(struct cdns3 *cdns);
+void cdns3_gadget_exit(struct cdns3 *cdns);
+#else
+
+static inline int cdns3_gadget_init(struct cdns3 *cdns)
+{
+	return -ENXIO;
+}
+
+static inline void cdns3_gadget_exit(struct cdns3 *cdns) { }
+
+#endif
+
+#endif /* __LINUX_CDNS3_GADGET_EXPORT */
diff --git a/drivers/usb/cdns3/gadget.c b/drivers/usb/cdns3/gadget.c
new file mode 100644
index 000000000000..0d95eb00be37
--- /dev/null
+++ b/drivers/usb/cdns3/gadget.c
@@ -0,0 +1,2102 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Cadence USBSS DRD Driver - gadget side.
+ *
+ * Copyright (C) 2018 Cadence Design Systems.
+ * Copyright (C) 2017-2018 NXP
+ *
+ * Authors: Pawel Jez <pjez@cadence.com>,
+ *          Pawel Laszczak <pawell@cadence.com>
+ *	    Peter Chen <peter.chen@nxp.com>
+ */
+
+#include <linux/dma-mapping.h>
+#include <linux/usb/gadget.h>
+
+#include "core.h"
+#include "gadget-export.h"
+#include "gadget.h"
+
+#include "trace.h"
+
+static int __cdns3_gadget_ep_queue(struct usb_ep *ep,
+				   struct usb_request *request,
+				   gfp_t gfp_flags);
+
+/**
+ * cdns3_handshake - spin reading  until handshake completes or fails
+ * @ptr: address of device controller register to be read
+ * @mask: bits to look at in result of read
+ * @done: value of those bits when handshake succeeds
+ * @usec: timeout in microseconds
+ *
+ * Returns negative errno, or zero on success
+ *
+ * Success happens when the "mask" bits have the specified value (hardware
+ * handshake done). There are two failure modes: "usec" have passed (major
+ * hardware flakeout), or the register reads as all-ones (hardware removed).
+ */
+int cdns3_handshake(void __iomem *ptr, u32 mask, u32 done, int usec)
+{
+	u32	result;
+
+	do {
+		result = readl(ptr);
+		if (result == ~(u32)0)	/* card removed */
+			return -ENODEV;
+		result &= mask;
+		if (result == done)
+			return 0;
+		udelay(1);
+		usec--;
+	} while (usec > 0);
+	return -ETIMEDOUT;
+}
+
+/**
+ * cdns3_set_register_bit - set bit in given register.
+ * @ptr: address of device controller register to be read and changed
+ * @mask: bits requested to set
+ */
+void cdns3_set_register_bit(void __iomem *ptr, u32 mask)
+{
+	mask = readl(ptr) | mask;
+	writel(mask, ptr);
+}
+
+/**
+ * cdns3_ep_addr_to_index - Macro converts endpoint address to
+ * index of endpoint object in cdns3_device.eps[] container
+ * @ep_addr: endpoint address for which endpoint object is required
+ *
+ */
+u8 cdns3_ep_addr_to_index(u8 ep_addr)
+{
+	return (((ep_addr & 0x7F)) + ((ep_addr & USB_DIR_IN) ? 16 : 0));
+}
+
+/**
+ * cdns3_next_request - returns next request from list
+ * @list: list containing requests
+ *
+ * Returns request or NULL if no requests in list
+ */
+struct usb_request *cdns3_next_request(struct list_head *list)
+{
+	if (list_empty(list))
+		return NULL;
+	return list_first_entry_or_null(list, struct usb_request, list);
+}
+
+/**
+ * cdns3_next_priv_request - returns next request from list
+ * @list: list containing requests
+ *
+ * Returns request or NULL if no requests in list
+ */
+struct cdns3_request *cdns3_next_priv_request(struct list_head *list)
+{
+	if (list_empty(list))
+		return NULL;
+	return list_first_entry_or_null(list, struct cdns3_request, list);
+}
+
+/**
+ * select_ep - selects endpoint
+ * @priv_dev:  extended gadget object
+ * @ep: endpoint address
+ */
+void cdns3_select_ep(struct cdns3_device *priv_dev, u32 ep)
+{
+	if (priv_dev->selected_ep == ep)
+		return;
+
+	priv_dev->selected_ep = ep;
+	writel(ep, &priv_dev->regs->ep_sel);
+}
+
+dma_addr_t cdns3_trb_virt_to_dma(struct cdns3_endpoint *priv_ep,
+				 struct cdns3_trb *trb)
+{
+	u32 offset = (char *)trb - (char *)priv_ep->trb_pool;
+
+	return priv_ep->trb_pool_dma + offset;
+}
+
+int cdns3_ring_size(struct cdns3_endpoint *priv_ep)
+{
+	switch (priv_ep->type) {
+	case USB_ENDPOINT_XFER_ISOC:
+		return TRB_ISO_RING_SIZE;
+	case USB_ENDPOINT_XFER_CONTROL:
+		return TRB_CTRL_RING_SIZE;
+	default:
+		return TRB_RING_SIZE;
+	}
+}
+
+/**
+ * cdns3_allocate_trb_pool - Allocates TRB's pool for selected endpoint
+ * @priv_ep:  endpoint object
+ *
+ * Function will return 0 on success or -ENOMEM on allocation error
+ */
+int cdns3_allocate_trb_pool(struct cdns3_endpoint *priv_ep)
+{
+	struct cdns3_device *priv_dev = priv_ep->cdns3_dev;
+	int ring_size = cdns3_ring_size(priv_ep);
+	struct cdns3_trb *link_trb;
+
+	if (!priv_ep->trb_pool) {
+		priv_ep->trb_pool = dma_zalloc_coherent(priv_dev->sysdev,
+							ring_size,
+							&priv_ep->trb_pool_dma,
+							GFP_DMA);
+		if (!priv_ep->trb_pool)
+			return -ENOMEM;
+	} else {
+		memset(priv_ep->trb_pool, 0, ring_size);
+	}
+
+	if (!priv_ep->num)
+		return 0;
+
+	if (!priv_ep->aligned_buff) {
+		void *buff = dma_alloc_coherent(priv_dev->sysdev,
+						CDNS3_ALIGNED_BUF_SIZE,
+						&priv_ep->aligned_dma_addr,
+						GFP_DMA);
+
+		priv_ep->aligned_buff  = buff;
+		if (!priv_ep->aligned_buff) {
+			dma_free_coherent(priv_dev->sysdev,
+					  ring_size,
+					  priv_ep->trb_pool,
+					  priv_ep->trb_pool_dma);
+			priv_ep->trb_pool = NULL;
+
+			return -ENOMEM;
+		}
+	}
+
+	priv_ep->num_trbs = ring_size / TRB_SIZE;
+	/* Initialize the last TRB as Link TRB */
+	link_trb = (priv_ep->trb_pool + (priv_ep->num_trbs - 1));
+	link_trb->buffer = TRB_BUFFER(priv_ep->trb_pool_dma);
+	link_trb->control = TRB_CYCLE | TRB_TYPE(TRB_LINK) |
+			    TRB_CHAIN | TRB_TOGGLE;
+
+	return 0;
+}
+
+static void cdns3_free_trb_pool(struct cdns3_endpoint *priv_ep)
+{
+	struct cdns3_device *priv_dev = priv_ep->cdns3_dev;
+
+	if (priv_ep->trb_pool) {
+		dma_free_coherent(priv_dev->sysdev,
+				  cdns3_ring_size(priv_ep),
+				  priv_ep->trb_pool, priv_ep->trb_pool_dma);
+		priv_ep->trb_pool = NULL;
+	}
+
+	if (priv_ep->aligned_buff) {
+		dma_free_coherent(priv_dev->sysdev, CDNS3_ALIGNED_BUF_SIZE,
+				  priv_ep->aligned_buff,
+				  priv_ep->aligned_dma_addr);
+		priv_ep->aligned_buff = NULL;
+	}
+}
+
+/**
+ * cdns3_data_flush - flush data at onchip buffer
+ * @priv_ep: endpoint object
+ *
+ * Endpoint must be selected before call to this function
+ *
+ * Returns zero on success or negative value on failure
+ */
+static int cdns3_data_flush(struct cdns3_endpoint *priv_ep)
+{
+	struct cdns3_device *priv_dev = priv_ep->cdns3_dev;
+
+	writel(EP_CMD_DFLUSH, &priv_dev->regs->ep_cmd);
+
+	/* wait for DFLUSH cleared */
+	return cdns3_handshake(&priv_dev->regs->ep_cmd, EP_CMD_DFLUSH, 0, 100);
+}
+
+/**
+ * cdns3_ep_stall_flush - Stalls and flushes selected endpoint
+ * @priv_ep: endpoint object
+ *
+ * Endpoint must be selected before call to this function
+ */
+static void cdns3_ep_stall_flush(struct cdns3_endpoint *priv_ep)
+{
+	struct cdns3_device *priv_dev = priv_ep->cdns3_dev;
+
+	writel(EP_CMD_DFLUSH | EP_CMD_ERDY | EP_CMD_SSTALL,
+	       &priv_dev->regs->ep_cmd);
+
+	/* wait for DFLUSH cleared */
+	cdns3_handshake(&priv_dev->regs->ep_cmd, EP_CMD_DFLUSH, 0, 100);
+	priv_ep->flags |= EP_STALL;
+}
+
+/**
+ * cdns3_hw_reset_eps_config - reset endpoints configuration kept by controller.
+ * @priv_dev: extended gadget object
+ */
+void cdns3_hw_reset_eps_config(struct cdns3_device *priv_dev)
+{
+	writel(USB_CONF_CFGRST, &priv_dev->regs->usb_conf);
+
+	cdns3_allow_enable_l1(priv_dev, 0);
+	priv_dev->hw_configured_flag = 0;
+	priv_dev->onchip_mem_allocated_size = 0;
+}
+
+/**
+ * cdns3_ep_inc_trb - increment a trb index.
+ * @index: Pointer to the TRB index to increment.
+ * @cs: Cycle state
+ * @trb_in_seg: number of TRBs in segment
+ *
+ * The index should never point to the link TRB. After incrementing,
+ * if it is point to the link TRB, wrap around to the beginning and revert
+ * cycle state bit The
+ * link TRB is always at the last TRB entry.
+ */
+static void cdns3_ep_inc_trb(int *index, u8 *cs, int trb_in_seg)
+{
+	(*index)++;
+	if (*index == (trb_in_seg - 1)) {
+		*index = 0;
+		*cs ^=  1;
+	}
+}
+
+/**
+ * cdns3_ep_inc_enq - increment endpoint's enqueue pointer
+ * @priv_ep: The endpoint whose enqueue pointer we're incrementing
+ */
+static void cdns3_ep_inc_enq(struct cdns3_endpoint *priv_ep)
+{
+	priv_ep->free_trbs--;
+	cdns3_ep_inc_trb(&priv_ep->enqueue, &priv_ep->pcs, priv_ep->num_trbs);
+}
+
+/**
+ * cdns3_ep_inc_deq - increment endpoint's dequeue pointer
+ * @priv_ep: The endpoint whose dequeue pointer we're incrementing
+ */
+static void cdns3_ep_inc_deq(struct cdns3_endpoint *priv_ep)
+{
+	priv_ep->free_trbs++;
+	cdns3_ep_inc_trb(&priv_ep->dequeue, &priv_ep->ccs, priv_ep->num_trbs);
+}
+
+/**
+ * cdns3_allow_enable_l1 - enable/disable permits to transition to L1.
+ * @priv_dev: Extended gadget object
+ * @enable: Enable/disable permit to transition to L1.
+ *
+ * If bit USB_CONF_L1EN is set and device receive Extended Token packet,
+ * then controller answer with ACK handshake.
+ * If bit USB_CONF_L1DS is set and device receive Extended Token packet,
+ * then controller answer with NYET handshake.
+ */
+void cdns3_allow_enable_l1(struct cdns3_device *priv_dev, int enable)
+{
+	if (enable)
+		writel(USB_CONF_L1EN, &priv_dev->regs->usb_conf);
+	else
+		writel(USB_CONF_L1DS, &priv_dev->regs->usb_conf);
+}
+
+enum usb_device_speed cdns3_get_speed(struct cdns3_device *priv_dev)
+{
+	u32 reg;
+
+	reg = readl(&priv_dev->regs->usb_sts);
+
+	if (DEV_SUPERSPEED(reg))
+		return USB_SPEED_SUPER;
+	else if (DEV_HIGHSPEED(reg))
+		return USB_SPEED_HIGH;
+	else if (DEV_FULLSPEED(reg))
+		return USB_SPEED_FULL;
+	else if (DEV_LOWSPEED(reg))
+		return USB_SPEED_LOW;
+	return USB_SPEED_UNKNOWN;
+}
+
+/**
+ * cdns3_start_all_request - add to ring all request not started
+ * @priv_dev: Extended gadget object
+ * @priv_ep: The endpoint for whom request will be started.
+ *
+ * Returns return ENOMEM if transfer ring i not enough TRBs to start
+ *         all requests.
+ */
+static int cdns3_start_all_request(struct cdns3_device *priv_dev,
+				   struct cdns3_endpoint *priv_ep)
+{
+	struct cdns3_request *priv_req;
+	struct usb_request *request;
+	int ret = 0;
+
+	while (!list_empty(&priv_ep->deferred_req_list)) {
+		request = cdns3_next_request(&priv_ep->deferred_req_list);
+		priv_req = to_cdns3_request(request);
+
+		ret = cdns3_ep_run_transfer(priv_ep, request);
+		if (ret)
+			return ret;
+
+		list_del(&request->list);
+		list_add_tail(&request->list,
+			      &priv_ep->pending_req_list);
+	}
+
+	priv_ep->flags &= ~EP_RING_FULL;
+	return ret;
+}
+
+/**
+ * cdns3_descmiss_copy_data copy data from internal requests to request queued
+ * by class driver.
+ * @priv_ep: extended endpoint object
+ * @request: request object
+ */
+static void cdns3_descmiss_copy_data(struct cdns3_endpoint *priv_ep,
+				     struct usb_request *request)
+{
+	struct usb_request *descmiss_req;
+	struct cdns3_request *descmiss_priv_req;
+
+	while (!list_empty(&priv_ep->descmiss_req_list)) {
+		int chunk_end;
+		int length;
+
+		descmiss_priv_req =
+			cdns3_next_priv_request(&priv_ep->descmiss_req_list);
+		descmiss_req = &descmiss_priv_req->request;
+
+		/* driver can't touch pending request */
+		if (descmiss_priv_req->flags & REQUEST_PENDING)
+			break;
+
+		chunk_end = descmiss_priv_req->flags & REQUEST_INTERNAL_CH;
+		length = request->actual + descmiss_req->actual;
+
+		if (length <= request->length) {
+			memcpy(&((u8 *)request->buf)[request->actual],
+			       descmiss_req->buf,
+			       descmiss_req->actual);
+			request->actual = length;
+		} else {
+			/* It should never occures */
+			request->status = -ENOMEM;
+		}
+
+		list_del_init(&descmiss_priv_req->list);
+
+		kfree(descmiss_req->buf);
+		cdns3_gadget_ep_free_request(&priv_ep->endpoint, descmiss_req);
+
+		if (!chunk_end)
+			break;
+	}
+}
+
+/**
+ * cdns3_gadget_giveback - call struct usb_request's ->complete callback
+ * @priv_ep: The endpoint to whom the request belongs to
+ * @priv_req: The request we're giving back
+ * @status: completion code for the request
+ *
+ * Must be called with controller's lock held and interrupts disabled. This
+ * function will unmap @req and call its ->complete() callback to notify upper
+ * layers that it has completed.
+ */
+void cdns3_gadget_giveback(struct cdns3_endpoint *priv_ep,
+			   struct cdns3_request *priv_req,
+			   int status)
+{
+	struct cdns3_device *priv_dev = priv_ep->cdns3_dev;
+	struct usb_request *request = &priv_req->request;
+
+	list_del_init(&request->list);
+
+	if (request->status == -EINPROGRESS)
+		request->status = status;
+
+	usb_gadget_unmap_request_by_dev(priv_dev->sysdev, request,
+					priv_ep->dir);
+
+	priv_req->flags &= ~REQUEST_PENDING;
+	trace_cdns3_gadget_giveback(priv_req);
+
+	/* WA1: */
+	if (priv_ep->flags & EP_QUIRK_EXTRA_BUF_EN &&
+	    priv_req->flags & REQUEST_INTERNAL) {
+		struct usb_request *req;
+
+		req = cdns3_next_request(&priv_ep->deferred_req_list);
+		request = req;
+		priv_ep->descmis_req = NULL;
+
+		if (!req)
+			return;
+
+		cdns3_descmiss_copy_data(priv_ep, req);
+		if (!(priv_ep->flags & EP_QUIRK_END_TRANSFER) &&
+		    req->length != req->actual) {
+			/* wait for next part of transfer */
+			return;
+		}
+
+		if (req->status == -EINPROGRESS)
+			req->status = 0;
+
+		list_del_init(&req->list);
+		cdns3_start_all_request(priv_dev, priv_ep);
+	}
+
+	/* Start all not pending request */
+	if (priv_ep->flags & EP_RING_FULL)
+		cdns3_start_all_request(priv_dev, priv_ep);
+
+	if (request->complete) {
+		spin_unlock(&priv_dev->lock);
+		usb_gadget_giveback_request(&priv_ep->endpoint,
+					    request);
+		spin_lock(&priv_dev->lock);
+	}
+
+	if (request->buf == priv_dev->zlp_buf)
+		cdns3_gadget_ep_free_request(&priv_ep->endpoint, request);
+}
+
+/**
+ * cdns3_ep_run_transfer - start transfer on no-default endpoint hardware
+ * @priv_ep: endpoint object
+ *
+ * Returns zero on success or negative value on failure
+ */
+int cdns3_ep_run_transfer(struct cdns3_endpoint *priv_ep,
+			  struct usb_request *request)
+{
+	struct cdns3_device *priv_dev = priv_ep->cdns3_dev;
+	struct cdns3_request *priv_req;
+	struct cdns3_trb *trb;
+	dma_addr_t trb_dma;
+	int sg_iter = 0;
+	u32 first_pcs;
+	int  num_trb;
+	int address;
+	int pcs;
+
+	if (priv_ep->type == USB_ENDPOINT_XFER_ISOC)
+		num_trb = priv_ep->interval;
+	else
+		num_trb = request->num_sgs ? request->num_sgs : 1;
+
+	if (num_trb > priv_ep->free_trbs) {
+		priv_ep->flags |= EP_RING_FULL;
+		return -ENOBUFS;
+	}
+
+	priv_req = to_cdns3_request(request);
+	address = priv_ep->endpoint.desc->bEndpointAddress;
+
+	priv_ep->flags |= EP_PENDING_REQUEST;
+	trb_dma = request->dma;
+
+	/* must allocate buffer aligned to 8 */
+	if ((request->dma % 8)) {
+		if (request->length <= CDNS3_ALIGNED_BUF_SIZE) {
+			memcpy(priv_ep->aligned_buff, request->buf,
+			       request->length);
+			trb_dma = priv_ep->aligned_dma_addr;
+		} else {
+			return -ENOMEM;
+		}
+	}
+
+	trb = priv_ep->trb_pool + priv_ep->enqueue;
+	priv_req->start_trb = priv_ep->enqueue;
+	priv_req->trb = trb;
+
+	/* prepare ring */
+	if ((priv_ep->enqueue + num_trb)  >= (priv_ep->num_trbs - 1)) {
+		/*updating C bt in  Link TRB before starting DMA*/
+		struct cdns3_trb *link_trb = priv_ep->trb_pool +
+					     (priv_ep->num_trbs - 1);
+		link_trb->control = ((priv_ep->pcs) ? TRB_CYCLE : 0) |
+				    TRB_TYPE(TRB_LINK) | TRB_CHAIN |
+				    TRB_TOGGLE;
+	}
+
+	first_pcs = priv_ep->pcs ? TRB_CYCLE : 0;
+
+	do {
+	/* fill TRB */
+		trb->buffer = TRB_BUFFER(request->num_sgs == 0
+				? trb_dma : request->sg[sg_iter].dma_address);
+
+		trb->length = TRB_BURST_LEN(16) |
+		    TRB_LEN(request->num_sgs == 0 ?
+				request->length : request->sg[sg_iter].length);
+
+		trb->control = TRB_TYPE(TRB_NORMAL);
+		pcs = priv_ep->pcs ? TRB_CYCLE : 0;
+
+		/*
+		 * first trb should be prepared as last to avoid processing
+		 *  transfer to early
+		 */
+		if (sg_iter != 0)
+			trb->control |= pcs;
+
+		if (priv_ep->type == USB_ENDPOINT_XFER_ISOC  && !priv_ep->dir) {
+			trb->control |= TRB_IOC | TRB_ISP;
+		} else {
+			/* for last element in TD or in SG list */
+			if (sg_iter == (num_trb - 1) && sg_iter != 0)
+				trb->control |= pcs | TRB_IOC | TRB_ISP;
+		}
+		++sg_iter;
+		priv_req->end_trb = priv_ep->enqueue;
+		cdns3_ep_inc_enq(priv_ep);
+		trb = priv_ep->trb_pool + priv_ep->enqueue;
+	} while (sg_iter < num_trb);
+
+	trb = priv_req->trb;
+
+	/*
+	 * Memory barrier = Cycle Bit must be set before trb->length  and
+	 * trb->buffer fields.
+	 */
+	wmb();
+
+	priv_req->flags |= REQUEST_PENDING;
+
+	/* give the TD to the consumer*/
+	if (sg_iter == 1)
+		trb->control |= first_pcs | TRB_IOC | TRB_ISP;
+	else
+		trb->control |= first_pcs;
+
+	trace_cdns3_prepare_trb(priv_ep, priv_req->trb);
+	trace_cdns3_ring(priv_ep);
+
+	/* arm transfer on selected endpoint */
+	cdns3_select_ep(priv_ep->cdns3_dev, address);
+
+	/*
+	 * For DMULT mode we can set address to transfer ring only once after
+	 * enabling endpoint.
+	 */
+	if (priv_ep->flags & EP_UPDATE_EP_TRBADDR) {
+		writel(EP_TRADDR_TRADDR(priv_ep->trb_pool_dma),
+		       &priv_dev->regs->ep_traddr);
+		priv_ep->flags &= ~EP_UPDATE_EP_TRBADDR;
+	}
+
+	/*clearing TRBERR and EP_STS_DESCMIS before seting DRDY*/
+	writel(EP_STS_TRBERR | EP_STS_DESCMIS, &priv_dev->regs->ep_sts);
+	trace_cdns3_doorbell_epx(priv_ep->name,
+				 readl(&priv_dev->regs->ep_traddr));
+	writel(EP_CMD_DRDY, &priv_dev->regs->ep_cmd);
+
+	return 0;
+}
+
+void cdns3_set_hw_configuration(struct cdns3_device *priv_dev)
+{
+	struct cdns3_endpoint *priv_ep;
+	struct usb_ep *ep;
+	int result = 0;
+
+	if (priv_dev->hw_configured_flag)
+		return;
+
+	writel(USB_CONF_CFGSET, &priv_dev->regs->usb_conf);
+	writel(EP_CMD_ERDY | EP_CMD_REQ_CMPL, &priv_dev->regs->ep_cmd);
+
+	cdns3_set_register_bit(&priv_dev->regs->usb_conf,
+			       USB_CONF_U1EN | USB_CONF_U2EN);
+
+	/* wait until configuration set */
+	result = cdns3_handshake(&priv_dev->regs->usb_sts,
+				 USB_STS_CFGSTS_MASK, 1, 100);
+
+	priv_dev->hw_configured_flag = 1;
+	cdns3_allow_enable_l1(priv_dev, 1);
+
+	list_for_each_entry(ep, &priv_dev->gadget.ep_list, ep_list) {
+		if (ep->enabled) {
+			priv_ep = ep_to_cdns3_ep(ep);
+			cdns3_start_all_request(priv_dev, priv_ep);
+		}
+	}
+}
+
+/**
+ * cdns3_request_handled - check whether request has been handled by DMA
+ *
+ * @priv_ep: extended endpoint object.
+ * @priv_req: request object for checking
+ *
+ * Endpoint must be selected before invoking this function.
+ *
+ * Returns false if request has not been handled by DMA, else returns true.
+ *
+ * SR - start ring
+ * ER -  end ring
+ * DQ = priv_ep->dequeue - dequeue position
+ * EQ = priv_ep->enqueue -  enqueue position
+ * ST = priv_req->start_trb - index of first TRB in transfer ring
+ * ET = priv_req->end_trb - index of last TRB in transfer ring
+ * CI = current_index - index of processed TRB by DMA.
+ *
+ * As first step, function checks if cycle bit for priv_req->start_trb is
+ * correct.
+ *
+ * some rules:
+ * 1. priv_ep->dequeue never exceed current_index.
+ * 2  priv_ep->enqueue never exceed priv_ep->dequeue
+ *
+ * Then We can split recognition into two parts:
+ * Case 1 - priv_ep->dequeue < current_index
+ *      SR ... EQ ... DQ ... CI ... ER
+ *      SR ... DQ ... CI ... EQ ... ER
+ *
+ *      Request has been handled by DMA if ST and ET is between DQ and CI.
+ *
+ * Case 2 - priv_ep->dequeue > current_index
+ * This situation take place when CI go through the LINK TRB at the end of
+ * transfer ring.
+ *      SR ... CI ... EQ ... DQ ... ER
+ *
+ *      Request has been handled by DMA if ET is less then CI or
+ *      ET is greater or equal DQ.
+ */
+static bool cdns3_request_handled(struct cdns3_endpoint *priv_ep,
+				  struct cdns3_request *priv_req)
+{
+	struct cdns3_device *priv_dev = priv_ep->cdns3_dev;
+	struct cdns3_trb *trb = priv_req->trb;
+	int current_index = 0;
+	int handled = 0;
+
+	current_index = (readl(&priv_dev->regs->ep_traddr) -
+			 priv_ep->trb_pool_dma) / TRB_SIZE;
+
+	trb = &priv_ep->trb_pool[priv_req->start_trb];
+
+	if ((trb->control  & TRB_CYCLE) != priv_ep->ccs)
+		goto finish;
+
+	if (priv_ep->dequeue < current_index) {
+		if ((current_index == (priv_ep->num_trbs - 1)) &&
+		    !priv_ep->dequeue)
+			goto finish;
+
+		if (priv_req->end_trb >= priv_ep->dequeue &&
+		    priv_req->end_trb < current_index)
+			handled = 1;
+	} else if (priv_ep->dequeue  > current_index) {
+		if (priv_req->end_trb  < current_index ||
+		    priv_req->end_trb >= priv_ep->dequeue)
+			handled = 1;
+	}
+
+finish:
+	trace_cdns3_request_handled(priv_req, current_index, handled);
+
+	return handled;
+}
+
+static void cdns3_transfer_completed(struct cdns3_device *priv_dev,
+				     struct cdns3_endpoint *priv_ep)
+{
+	struct cdns3_request *priv_req;
+	struct usb_request *request;
+	struct cdns3_trb *trb;
+	int current_trb;
+
+	while (!list_empty(&priv_ep->pending_req_list)) {
+		request = cdns3_next_request(&priv_ep->pending_req_list);
+		priv_req = to_cdns3_request(request);
+
+		if (!cdns3_request_handled(priv_ep, priv_req))
+			return;
+
+		if (request->dma % 8 && priv_ep->dir == USB_DIR_OUT)
+			memcpy(request->buf, priv_ep->aligned_buff,
+			       request->length);
+
+		trb = priv_ep->trb_pool + priv_ep->dequeue;
+		trace_cdns3_complete_trb(priv_ep, trb);
+
+		if (trb != priv_req->trb)
+			dev_warn(priv_dev->dev,
+				 "request_trb=0x%p, queue_trb=0x%p\n",
+				 priv_req->trb, trb);
+
+		request->actual = TRB_LEN(le32_to_cpu(trb->length));
+		current_trb = priv_req->start_trb;
+
+		while (current_trb != priv_req->end_trb) {
+			cdns3_ep_inc_deq(priv_ep);
+			current_trb = priv_ep->dequeue;
+		}
+
+		cdns3_ep_inc_deq(priv_ep);
+		cdns3_gadget_giveback(priv_ep, priv_req, 0);
+	}
+	priv_ep->flags &= ~EP_PENDING_REQUEST;
+}
+
+/**
+ * cdns3_descmissing_packet - handles descriptor missing event.
+ * @priv_dev: extended gadget object
+ *
+ * WA1: Controller for OUT endpoints has shared on-chip buffers for all incoming
+ * packets, including ep0out. It's FIFO buffer, so packets must be handle by DMA
+ * in correct order. If the first packet in the buffer will not be handled,
+ * then the following packets directed for other endpoints and  functions
+ * will be blocked.
+ * Additionally the packets directed to one endpoint can block entire on-chip
+ * buffers. In this case transfer to other endpoints also will blocked.
+ *
+ * To resolve this issue after raising the descriptor missing interrupt
+ * driver prepares internal usb_request object and use it to arm DMA transfer.
+ *
+ * The problematic situation was observed in case when endpoint has been enabled
+ * but no usb_request were queued. Driver try detects such endpoints and will
+ * use this workaround only for these endpoint.
+ *
+ * Driver use limited number of buffer. This number can be set by macro
+ * CDNS_WA1_NUM_BUFFERS.
+ *
+ * Such blocking situation was observed on ACM gadget. For this function
+ * host send OUT data packet but ACM function is not prepared for this packet.
+ *
+ * It's limitation of controller but maybe this issues should be fixed in
+ * function driver.
+ */
+static int cdns3_descmissing_packet(struct cdns3_endpoint *priv_ep)
+{
+	struct cdns3_request *priv_req;
+	struct usb_request *request;
+
+	if (priv_ep->flags & EP_QUIRK_EXTRA_BUF_DET) {
+		priv_ep->flags &= ~EP_QUIRK_EXTRA_BUF_DET;
+		priv_ep->flags |= EP_QUIRK_EXTRA_BUF_EN;
+	}
+
+	request = cdns3_gadget_ep_alloc_request(&priv_ep->endpoint,
+						GFP_ATOMIC);
+	if (!request)
+		return -ENOMEM;
+
+	priv_req = to_cdns3_request(request);
+	priv_req->flags |= REQUEST_INTERNAL;
+
+	/* if this field is still assigned it indicate that transfer related
+	 * with this request has not been finished yet. Driver in this
+	 * case simply allocate next request and assign flag REQUEST_INTERNAL_CH
+	 * flag to previous one. It will indicate that current request is
+	 * part of the previous one.
+	 */
+	if (priv_ep->descmis_req)
+		priv_ep->descmis_req->flags |= REQUEST_INTERNAL_CH;
+
+	priv_req->request.buf = kzalloc(CDNS3_DESCMIS_BUF_SIZE,
+					GFP_ATOMIC);
+	if (!priv_req) {
+		cdns3_gadget_ep_free_request(&priv_ep->endpoint, request);
+		return -ENOMEM;
+	}
+
+	priv_req->request.length = CDNS3_DESCMIS_BUF_SIZE;
+	priv_ep->descmis_req = priv_req;
+
+	__cdns3_gadget_ep_queue(&priv_ep->endpoint,
+				&priv_ep->descmis_req->request,
+				GFP_ATOMIC);
+
+	return 0;
+}
+
+/**
+ * cdns3_check_ep_interrupt_proceed - Processes interrupt related to endpoint
+ * @priv_ep: endpoint object
+ *
+ * Returns 0
+ */
+static int cdns3_check_ep_interrupt_proceed(struct cdns3_endpoint *priv_ep)
+{
+	struct cdns3_device *priv_dev = priv_ep->cdns3_dev;
+	u32 ep_sts_reg;
+
+	cdns3_select_ep(priv_dev, priv_ep->endpoint.address);
+
+	trace_cdns3_epx_irq(priv_dev, priv_ep);
+
+	ep_sts_reg = readl(&priv_dev->regs->ep_sts);
+	writel(ep_sts_reg, &priv_dev->regs->ep_sts);
+
+	if ((ep_sts_reg & EP_STS_IOC) || (ep_sts_reg & EP_STS_ISP)) {
+		if (priv_ep->flags & EP_QUIRK_EXTRA_BUF_EN) {
+			if (ep_sts_reg & EP_STS_ISP)
+				priv_ep->flags |= EP_QUIRK_END_TRANSFER;
+			else
+				priv_ep->flags &= ~EP_QUIRK_END_TRANSFER;
+		}
+		cdns3_transfer_completed(priv_dev, priv_ep);
+	}
+
+	/*
+	 * For isochronous transfer driver completes request on IOC or on
+	 * TRBERR. IOC appears only when device receive OUT data packet.
+	 * If host disable stream or lost some packet then the only way to
+	 * finish all queued transfer is to do it on TRBERR event.
+	 */
+	if ((ep_sts_reg & EP_STS_TRBERR) &&
+	    priv_ep->type == USB_ENDPOINT_XFER_ISOC)
+		cdns3_transfer_completed(priv_dev, priv_ep);
+
+	/*
+	 * WA1: this condition should only be meet when
+	 * priv_ep->flags & EP_QUIRK_EXTRA_BUF_DET or
+	 * priv_ep->flags & EP_QUIRK_EXTRA_BUF_EN.
+	 * In other cases this interrupt will be disabled/
+	 */
+	if (ep_sts_reg & EP_STS_DESCMIS) {
+		int err;
+
+		err = cdns3_descmissing_packet(priv_ep);
+		if (err)
+			dev_err(priv_dev->dev,
+				"Failed: No sufficient memory for DESCMIS\n");
+	}
+
+	return 0;
+}
+
+/**
+ * cdns3_check_usb_interrupt_proceed - Processes interrupt related to device
+ * @priv_dev: extended gadget object
+ * @usb_ists: bitmap representation of device's reported interrupts
+ * (usb_ists register value)
+ */
+static void cdns3_check_usb_interrupt_proceed(struct cdns3_device *priv_dev,
+					      u32 usb_ists)
+{
+	int speed = 0;
+
+	trace_cdns3_usb_irq(priv_dev, usb_ists);
+	/* Connection detected */
+	if (usb_ists & (USB_ISTS_CON2I | USB_ISTS_CONI)) {
+		speed = cdns3_get_speed(priv_dev);
+		priv_dev->gadget.speed = speed;
+		usb_gadget_set_state(&priv_dev->gadget, USB_STATE_POWERED);
+		cdns3_ep0_config(priv_dev);
+	}
+
+	/* Disconnection detected */
+	if (usb_ists & (USB_ISTS_DIS2I | USB_ISTS_DISI)) {
+		if (priv_dev->gadget_driver &&
+		    priv_dev->gadget_driver->disconnect) {
+			spin_unlock(&priv_dev->lock);
+			priv_dev->gadget_driver->disconnect(&priv_dev->gadget);
+			spin_lock(&priv_dev->lock);
+		}
+
+		priv_dev->gadget.speed = USB_SPEED_UNKNOWN;
+		usb_gadget_set_state(&priv_dev->gadget, USB_STATE_NOTATTACHED);
+		cdns3_hw_reset_eps_config(priv_dev);
+	}
+
+	/* reset*/
+	if (usb_ists & (USB_ISTS_UWRESI | USB_ISTS_UHRESI | USB_ISTS_U2RESI)) {
+		/*read again to check the actuall speed*/
+		speed = cdns3_get_speed(priv_dev);
+		usb_gadget_set_state(&priv_dev->gadget, USB_STATE_DEFAULT);
+		priv_dev->gadget.speed = speed;
+		cdns3_hw_reset_eps_config(priv_dev);
+		cdns3_ep0_config(priv_dev);
+	}
+}
+
+/**
+ * cdns3_device_irq_handler- interrupt handler for device part of controller
+ *
+ * @irq: irq number for cdns3 core device
+ * @data: structure of cdns3
+ *
+ * Returns IRQ_HANDLED or IRQ_NONE
+ */
+static irqreturn_t cdns3_device_irq_handler(int irq, void *data)
+{
+	struct cdns3_device *priv_dev;
+	struct cdns3 *cdns = data;
+	irqreturn_t ret = IRQ_NONE;
+	unsigned long flags;
+	u32 reg;
+
+	priv_dev = cdns->gadget_dev;
+	spin_lock_irqsave(&priv_dev->lock, flags);
+
+	/* check USB device interrupt */
+	reg = readl(&priv_dev->regs->usb_ists);
+	writel(reg, &priv_dev->regs->usb_ists);
+
+	if (reg) {
+		cdns3_check_usb_interrupt_proceed(priv_dev, reg);
+		ret = IRQ_HANDLED;
+	}
+
+	/* check endpoint interrupt */
+	reg = readl(&priv_dev->regs->ep_ists);
+
+	if (reg) {
+		reg = ~reg & readl(&priv_dev->regs->ep_ien);
+		/* mask deferred interrupt. */
+		writel(reg, &priv_dev->regs->ep_ien);
+		ret = IRQ_WAKE_THREAD;
+	}
+
+	spin_unlock_irqrestore(&priv_dev->lock, flags);
+	return ret;
+}
+
+/**
+ * cdns3_device_thread_irq_handler- interrupt handler for device part
+ * of controller
+ *
+ * @irq: irq number for cdns3 core device
+ * @data: structure of cdns3
+ *
+ * Returns IRQ_HANDLED or IRQ_NONE
+ */
+static irqreturn_t cdns3_device_thread_irq_handler(int irq, void *data)
+{
+	struct cdns3_device *priv_dev;
+	struct cdns3 *cdns = data;
+	irqreturn_t ret = IRQ_NONE;
+	unsigned long flags;
+	u32 ep_ien;
+	int bit;
+	u32 reg;
+
+	priv_dev = cdns->gadget_dev;
+	spin_lock_irqsave(&priv_dev->lock, flags);
+
+	reg = readl(&priv_dev->regs->ep_ists);
+	ep_ien = reg;
+
+	/* handle default endpoint OUT */
+	if (reg & EP_ISTS_EP_OUT0) {
+		cdns3_check_ep0_interrupt_proceed(priv_dev, USB_DIR_OUT);
+		ret = IRQ_HANDLED;
+	}
+
+	/* handle default endpoint IN */
+	if (reg & EP_ISTS_EP_IN0) {
+		cdns3_check_ep0_interrupt_proceed(priv_dev, USB_DIR_IN);
+		ret = IRQ_HANDLED;
+	}
+
+	/* check if interrupt from non default endpoint, if no exit */
+	reg &= ~(EP_ISTS_EP_OUT0 | EP_ISTS_EP_IN0);
+	if (!reg)
+		goto irqend;
+
+	for_each_set_bit(bit, (unsigned long *)&reg,
+			 sizeof(u32) * BITS_PER_BYTE) {
+		cdns3_check_ep_interrupt_proceed(priv_dev->eps[bit]);
+		ret = IRQ_HANDLED;
+	}
+
+irqend:
+	ep_ien |= readl(&priv_dev->regs->ep_ien);
+	/* Unmask all handled EP interrupts */
+	writel(ep_ien, &priv_dev->regs->ep_ien);
+	spin_unlock_irqrestore(&priv_dev->lock, flags);
+	return ret;
+}
+
+/**
+ * cdns3_ep_onchip_buffer_reserve - Try to reserve onchip buf for EP
+ *
+ * The real reservation will occur during write to EP_CFG register,
+ * this function is used to check if the 'size' reservation is allowed.
+ *
+ * @priv_dev: extended gadget object
+ * @size: the size (KB) for EP would like to allocate
+ *
+ * Return 0 if the required size can met or negative value on failure
+ */
+static int cdns3_ep_onchip_buffer_reserve(struct cdns3_device *priv_dev,
+					  int size)
+{
+	u32 onchip_mem;
+
+	priv_dev->onchip_mem_allocated_size += size;
+
+	onchip_mem = USB_CAP2_ACTUAL_MEM_SIZE(readl(&priv_dev->regs->usb_cap2));
+	if (!onchip_mem)
+		onchip_mem = 256;
+
+	/* 2KB is reserved for EP0*/
+	onchip_mem -= 2;
+	if (priv_dev->onchip_mem_allocated_size > onchip_mem) {
+		priv_dev->onchip_mem_allocated_size -= size;
+		return -EPERM;
+	}
+
+	return 0;
+}
+
+/**
+ * cdns3_ep_config Configure hardware endpoint
+ * @priv_ep: extended endpoint object
+ */
+void cdns3_ep_config(struct cdns3_endpoint *priv_ep)
+{
+	bool is_iso_ep = (priv_ep->type == USB_ENDPOINT_XFER_ISOC);
+	struct cdns3_device *priv_dev = priv_ep->cdns3_dev;
+	u32 bEndpointAddress = priv_ep->num | priv_ep->dir;
+	u32 max_packet_size = 0;
+	u32 ep_cfg = 0;
+	int ret;
+
+	switch (priv_ep->type) {
+	case USB_ENDPOINT_XFER_INT:
+		ep_cfg = EP_CFG_EPTYPE(USB_ENDPOINT_XFER_INT);
+		break;
+	case USB_ENDPOINT_XFER_BULK:
+		ep_cfg = EP_CFG_EPTYPE(USB_ENDPOINT_XFER_BULK);
+		break;
+	default:
+		ep_cfg = EP_CFG_EPTYPE(USB_ENDPOINT_XFER_ISOC);
+	}
+
+	switch (priv_dev->gadget.speed) {
+	case USB_SPEED_FULL:
+		max_packet_size = is_iso_ep ? 1023 : 64;
+		break;
+	case USB_SPEED_HIGH:
+		max_packet_size = is_iso_ep ? 1024 : 512;
+		break;
+	case USB_SPEED_SUPER:
+		max_packet_size = 1024;
+		break;
+	default:
+		/* all other speed are not supported */
+		return;
+	}
+
+	ret = cdns3_ep_onchip_buffer_reserve(priv_dev, CDNS3_EP_BUF_SIZE);
+	if (ret) {
+		dev_err(priv_dev->dev, "onchip mem is full, ep is invalid\n");
+		return;
+	}
+
+	ep_cfg |= EP_CFG_MAXPKTSIZE(max_packet_size) |
+		  EP_CFG_BUFFERING(CDNS3_EP_BUF_SIZE - 1) |
+		  EP_CFG_MAXBURST(priv_ep->endpoint.maxburst);
+
+	cdns3_select_ep(priv_dev, bEndpointAddress);
+	writel(ep_cfg, &priv_dev->regs->ep_cfg);
+
+	dev_dbg(priv_dev->dev, "Configure %s: with val %08x\n",
+		priv_ep->name, ep_cfg);
+}
+
+/* Find correct direction for HW endpoint according to description */
+static int cdns3_ep_dir_is_correct(struct usb_endpoint_descriptor *desc,
+				   struct cdns3_endpoint *priv_ep)
+{
+	return (priv_ep->endpoint.caps.dir_in && usb_endpoint_dir_in(desc)) ||
+	       (priv_ep->endpoint.caps.dir_out && usb_endpoint_dir_out(desc));
+}
+
+static struct
+cdns3_endpoint *cdns3_find_available_ep(struct cdns3_device *priv_dev,
+					struct usb_endpoint_descriptor *desc)
+{
+	struct usb_ep *ep;
+	struct cdns3_endpoint *priv_ep;
+
+	list_for_each_entry(ep, &priv_dev->gadget.ep_list, ep_list) {
+		unsigned long num;
+		int ret;
+		/* ep name pattern likes epXin or epXout */
+		char c[2] = {ep->name[2], '\0'};
+
+		ret = kstrtoul(c, 10, &num);
+		if (ret)
+			return ERR_PTR(ret);
+
+		priv_ep = ep_to_cdns3_ep(ep);
+		if (cdns3_ep_dir_is_correct(desc, priv_ep)) {
+			if (!(priv_ep->flags & EP_CLAIMED)) {
+				priv_ep->num  = num;
+				return priv_ep;
+			}
+		}
+	}
+
+	return ERR_PTR(-ENOENT);
+}
+
+/*
+ *  Cadence IP has one limitation that all endpoints must be configured
+ * (Type & MaxPacketSize) before setting configuration through hardware
+ * register, it means we can't change endpoints configuration after
+ * set_configuration.
+ *
+ * This function set EP_CLAIMED flag which is added when the gadget driver
+ * uses usb_ep_autoconfig to configure specific endpoint;
+ * When the udc driver receives set_configurion request,
+ * it goes through all claimed endpoints, and configure all endpoints
+ * accordingly.
+ *
+ * At usb_ep_ops.enable/disable, we only enable and disable endpoint through
+ * ep_cfg register which can be changed after set_configuration, and do
+ * some software operation accordingly.
+ */
+static struct
+usb_ep *cdns3_gadget_match_ep(struct usb_gadget *gadget,
+			      struct usb_endpoint_descriptor *desc,
+			      struct usb_ss_ep_comp_descriptor *comp_desc)
+{
+	struct cdns3_device *priv_dev = gadget_to_cdns3_device(gadget);
+	struct cdns3_endpoint *priv_ep;
+	unsigned long flags;
+
+	priv_ep = cdns3_find_available_ep(priv_dev, desc);
+	if (IS_ERR(priv_ep)) {
+		dev_err(priv_dev->dev, "no available ep\n");
+		return NULL;
+	}
+
+	dev_dbg(priv_dev->dev, "match endpoint: %s\n", priv_ep->name);
+
+	spin_lock_irqsave(&priv_dev->lock, flags);
+	priv_ep->endpoint.desc = desc;
+	priv_ep->dir  = usb_endpoint_dir_in(desc) ? USB_DIR_IN : USB_DIR_OUT;
+	priv_ep->type = usb_endpoint_type(desc);
+	priv_ep->flags |= EP_CLAIMED;
+	spin_unlock_irqrestore(&priv_dev->lock, flags);
+	return &priv_ep->endpoint;
+}
+
+/**
+ * cdns3_gadget_ep_alloc_request Allocates request
+ * @ep: endpoint object associated with request
+ * @gfp_flags: gfp flags
+ *
+ * Returns allocated request address, NULL on allocation error
+ */
+struct usb_request *cdns3_gadget_ep_alloc_request(struct usb_ep *ep,
+						  gfp_t gfp_flags)
+{
+	struct cdns3_endpoint *priv_ep = ep_to_cdns3_ep(ep);
+	struct cdns3_request *priv_req;
+
+	priv_req = kzalloc(sizeof(*priv_req), gfp_flags);
+	if (!priv_req)
+		return NULL;
+
+	priv_req->priv_ep = priv_ep;
+
+	trace_cdns3_alloc_request(priv_req);
+	return &priv_req->request;
+}
+
+/**
+ * cdns3_gadget_ep_free_request Free memory occupied by request
+ * @ep: endpoint object associated with request
+ * @request: request to free memory
+ */
+void cdns3_gadget_ep_free_request(struct usb_ep *ep,
+				  struct usb_request *request)
+{
+	struct cdns3_request *priv_req = to_cdns3_request(request);
+
+	trace_cdns3_free_request(priv_req);
+	kfree(priv_req);
+}
+
+/**
+ * cdns3_gadget_ep_enable Enable endpoint
+ * @ep: endpoint object
+ * @desc: endpoint descriptor
+ *
+ * Returns 0 on success, error code elsewhere
+ */
+static int cdns3_gadget_ep_enable(struct usb_ep *ep,
+				  const struct usb_endpoint_descriptor *desc)
+{
+	struct cdns3_endpoint *priv_ep;
+	struct cdns3_device *priv_dev;
+	u32 reg = EP_STS_EN_TRBERREN;
+	u32 bEndpointAddress;
+	unsigned long flags;
+	int ret;
+
+	priv_ep = ep_to_cdns3_ep(ep);
+	priv_dev = priv_ep->cdns3_dev;
+
+	if (!ep || !desc || desc->bDescriptorType != USB_DT_ENDPOINT) {
+		dev_dbg(priv_dev->dev, "usbss: invalid parameters\n");
+		return -EINVAL;
+	}
+
+	if (!desc->wMaxPacketSize) {
+		dev_err(priv_dev->dev, "usbss: missing wMaxPacketSize\n");
+		return -EINVAL;
+	}
+
+	if (dev_WARN_ONCE(priv_dev->dev, priv_ep->flags & EP_ENABLED,
+			  "%s is already enabled\n", priv_ep->name))
+		return 0;
+
+	spin_lock_irqsave(&priv_dev->lock, flags);
+
+	priv_ep->endpoint.desc = desc;
+	priv_ep->type = usb_endpoint_type(desc);
+	priv_ep->interval = desc->bInterval ? BIT(desc->bInterval - 1) : 0;
+
+	if (priv_ep->interval > ISO_MAX_INTERVAL &&
+	    priv_ep->type == USB_ENDPOINT_XFER_ISOC) {
+		dev_err(priv_dev->dev, "Driver is limited to %d period\n",
+			ISO_MAX_INTERVAL);
+
+		ret =  -EINVAL;
+		goto exit;
+	}
+
+	ret = cdns3_allocate_trb_pool(priv_ep);
+
+	if (ret)
+		goto exit;
+
+	bEndpointAddress = priv_ep->num | priv_ep->dir;
+	cdns3_select_ep(priv_dev, bEndpointAddress);
+
+	trace_cdns3_gadget_ep_enable(priv_ep);
+
+	writel(EP_CMD_EPRST, &priv_dev->regs->ep_cmd);
+
+	ret = cdns3_handshake(&priv_dev->regs->ep_cmd,
+			      EP_CMD_CSTALL | EP_CMD_EPRST, 0, 100);
+
+	/* enable interrupt for selected endpoint */
+	cdns3_set_register_bit(&priv_dev->regs->ep_ien,
+			       BIT(cdns3_ep_addr_to_index(bEndpointAddress)));
+	/*
+	 * WA1: Set flag for all not ISOC OUT endpoints. If this flag is set
+	 * driver try to detect whether endpoint need additional internal
+	 * buffer for unblocking on-chip FIFO buffer. This flag will be cleared
+	 * if before first DESCMISS interrupt the DMA will be armed.
+	 */
+	if (!priv_ep->dir && priv_ep->type != USB_ENDPOINT_XFER_ISOC) {
+		priv_ep->flags |= EP_QUIRK_EXTRA_BUF_DET;
+		reg |= EP_STS_EN_DESCMISEN;
+	}
+
+	writel(reg, &priv_dev->regs->ep_sts_en);
+
+	cdns3_set_register_bit(&priv_dev->regs->ep_cfg, EP_CFG_ENABLE);
+
+	ep->desc = desc;
+	priv_ep->flags &= ~(EP_PENDING_REQUEST | EP_STALL |
+			    EP_QUIRK_EXTRA_BUF_EN);
+	priv_ep->flags |= EP_ENABLED | EP_UPDATE_EP_TRBADDR;
+	priv_ep->enqueue = 0;
+	priv_ep->dequeue = 0;
+	reg = readl(&priv_dev->regs->ep_sts);
+	priv_ep->pcs = !!EP_STS_CCS(reg);
+	priv_ep->ccs = !!EP_STS_CCS(reg);
+	/* one TRB is reserved for link TRB used in DMULT mode*/
+	priv_ep->free_trbs = priv_ep->num_trbs - 1;
+exit:
+	spin_unlock_irqrestore(&priv_dev->lock, flags);
+
+	return ret;
+}
+
+/**
+ * cdns3_gadget_ep_disable Disable endpoint
+ * @ep: endpoint object
+ *
+ * Returns 0 on success, error code elsewhere
+ */
+static int cdns3_gadget_ep_disable(struct usb_ep *ep)
+{
+	struct cdns3_endpoint *priv_ep;
+	struct cdns3_request *priv_req;
+	struct cdns3_device *priv_dev;
+	struct usb_request *request;
+	unsigned long flags;
+	int ret = 0;
+	u32 ep_cfg;
+
+	if (!ep) {
+		pr_err("usbss: invalid parameters\n");
+		return -EINVAL;
+	}
+
+	priv_ep = ep_to_cdns3_ep(ep);
+	priv_dev = priv_ep->cdns3_dev;
+
+	if (dev_WARN_ONCE(priv_dev->dev, !(priv_ep->flags & EP_ENABLED),
+			  "%s is already disabled\n", priv_ep->name))
+		return 0;
+
+	spin_lock_irqsave(&priv_dev->lock, flags);
+
+	trace_cdns3_gadget_ep_disable(priv_ep);
+
+	cdns3_select_ep(priv_dev, ep->desc->bEndpointAddress);
+	ret = cdns3_data_flush(priv_ep);
+
+	ep_cfg = readl(&priv_dev->regs->ep_cfg);
+	ep_cfg &= ~EP_CFG_ENABLE;
+	writel(ep_cfg, &priv_dev->regs->ep_cfg);
+
+	while (!list_empty(&priv_ep->pending_req_list)) {
+		request = cdns3_next_request(&priv_ep->pending_req_list);
+
+		cdns3_gadget_giveback(priv_ep, to_cdns3_request(request),
+				      -ESHUTDOWN);
+	}
+
+	while (!list_empty(&priv_ep->descmiss_req_list)) {
+		priv_req = cdns3_next_priv_request(&priv_ep->descmiss_req_list);
+
+		kfree(priv_req->request.buf);
+		cdns3_gadget_ep_free_request(&priv_ep->endpoint,
+					     &priv_req->request);
+	}
+
+	while (!list_empty(&priv_ep->deferred_req_list)) {
+		request = cdns3_next_request(&priv_ep->deferred_req_list);
+
+		cdns3_gadget_giveback(priv_ep, to_cdns3_request(request),
+				      -ESHUTDOWN);
+	}
+
+	priv_ep->descmis_req = NULL;
+
+	ep->desc = NULL;
+	priv_ep->flags &= ~EP_ENABLED;
+
+	spin_unlock_irqrestore(&priv_dev->lock, flags);
+
+	return ret;
+}
+
+/**
+ * cdns3_gadget_ep_queue Transfer data on endpoint
+ * @ep: endpoint object
+ * @request: request object
+ * @gfp_flags: gfp flags
+ *
+ * Returns 0 on success, error code elsewhere
+ */
+static int __cdns3_gadget_ep_queue(struct usb_ep *ep,
+				   struct usb_request *request,
+				   gfp_t gfp_flags)
+{
+	struct cdns3_endpoint *priv_ep = ep_to_cdns3_ep(ep);
+	struct cdns3_device *priv_dev = priv_ep->cdns3_dev;
+	struct cdns3_request *priv_req;
+	int deferred = 0;
+	int ret = 0;
+
+	request->actual = 0;
+	request->status = -EINPROGRESS;
+	priv_req = to_cdns3_request(request);
+	trace_cdns3_ep_queue(priv_req);
+
+	/*
+	 * WA1: if transfer was queued before DESCMISS appear than we
+	 * can disable handling of DESCMISS interrupt. Driver assumes that it
+	 * can disable special treatment for this endpoint.
+	 */
+	if (priv_ep->flags & EP_QUIRK_EXTRA_BUF_DET) {
+		u32 reg;
+
+		cdns3_select_ep(priv_dev, priv_ep->num | priv_ep->dir);
+		reg = readl(&priv_dev->regs->ep_sts_en);
+		priv_ep->flags &= ~EP_QUIRK_EXTRA_BUF_DET;
+		reg &= EP_STS_EN_DESCMISEN;
+		writel(reg, &priv_dev->regs->ep_sts_en);
+	}
+
+	/* WA1 */
+	if (priv_ep->flags & EP_QUIRK_EXTRA_BUF_EN) {
+		u8 pending_empty = list_empty(&priv_ep->pending_req_list);
+		u8 descmiss_empty = list_empty(&priv_ep->descmiss_req_list);
+
+		/*
+		 *  DESCMISS transfer has been finished, so data will be
+		 *  directly copied from internal allocated usb_request
+		 *  objects.
+		 */
+		if (pending_empty && !descmiss_empty &&
+		    !(priv_req->flags & REQUEST_INTERNAL)) {
+			cdns3_descmiss_copy_data(priv_ep, request);
+			list_add_tail(&request->list,
+				      &priv_ep->pending_req_list);
+			cdns3_gadget_giveback(priv_ep, priv_req,
+					      request->status);
+			return ret;
+		}
+
+		/*
+		 * WA1 driver will wait for completion DESCMISS transfer,
+		 * before starts new, not DESCMISS transfer.
+		 */
+		if (!pending_empty && !descmiss_empty)
+			deferred = 1;
+
+		if (priv_req->flags & REQUEST_INTERNAL)
+			list_add_tail(&priv_req->list,
+				      &priv_ep->descmiss_req_list);
+	}
+
+	ret = usb_gadget_map_request_by_dev(priv_dev->sysdev, request,
+					    usb_endpoint_dir_in(ep->desc));
+	if (ret)
+		return ret;
+
+	/*
+	 * If hardware endpoint configuration has not been set yet then
+	 * just queue request in deferred list. Transfer will be started in
+	 * cdns3_set_hw_configuration.
+	 */
+	if (!priv_dev->hw_configured_flag)
+		deferred = 1;
+	else
+		ret = cdns3_ep_run_transfer(priv_ep, request);
+
+	if (ret || deferred)
+		list_add_tail(&request->list, &priv_ep->deferred_req_list);
+	else
+		list_add_tail(&request->list, &priv_ep->pending_req_list);
+
+	return ret;
+}
+
+static int cdns3_gadget_ep_queue(struct usb_ep *ep, struct usb_request *request,
+				 gfp_t gfp_flags)
+{
+	struct usb_request *zlp_request;
+	struct cdns3_endpoint *priv_ep;
+	struct cdns3_device *priv_dev;
+	unsigned long flags;
+	int ret;
+
+	if (!request || !ep)
+		return -EINVAL;
+
+	priv_ep = ep_to_cdns3_ep(ep);
+	priv_dev = priv_ep->cdns3_dev;
+
+	spin_lock_irqsave(&priv_dev->lock, flags);
+
+	ret = __cdns3_gadget_ep_queue(ep, request, gfp_flags);
+
+	if (ret == 0 && request->zero && request->length &&
+	    (request->length % ep->maxpacket == 0)) {
+		struct cdns3_request *priv_req;
+
+		zlp_request = cdns3_gadget_ep_alloc_request(ep, GFP_ATOMIC);
+		zlp_request->buf = priv_dev->zlp_buf;
+		zlp_request->length = 0;
+
+		priv_req = to_cdns3_request(zlp_request);
+		priv_req->flags |= REQUEST_ZLP;
+
+		dev_dbg(priv_dev->dev, "Queuing ZLP for endpoint: %s\n",
+			priv_ep->name);
+		ret = __cdns3_gadget_ep_queue(ep, zlp_request, gfp_flags);
+	}
+
+	spin_unlock_irqrestore(&priv_dev->lock, flags);
+	return ret;
+}
+
+/**
+ * cdns3_gadget_ep_dequeue Remove request from transfer queue
+ * @ep: endpoint object associated with request
+ * @request: request object
+ *
+ * Returns 0 on success, error code elsewhere
+ */
+int cdns3_gadget_ep_dequeue(struct usb_ep *ep,
+			    struct usb_request *request)
+{
+	struct cdns3_endpoint *priv_ep = ep_to_cdns3_ep(ep);
+	struct cdns3_device *priv_dev = priv_ep->cdns3_dev;
+	struct usb_request *req, *req_temp;
+	struct cdns3_request *priv_req;
+	unsigned long flags;
+	int ret = 0;
+
+	if (!ep || !request || !ep->desc)
+		return -EINVAL;
+
+	spin_lock_irqsave(&priv_dev->lock, flags);
+
+	priv_req = to_cdns3_request(request);
+
+	trace_cdns3_ep_dequeue(priv_req);
+
+	cdns3_select_ep(priv_dev, ep->desc->bEndpointAddress);
+
+	list_for_each_entry_safe(req, req_temp, &priv_ep->pending_req_list,
+				 list) {
+		if (request == req)
+			goto found;
+	}
+
+	list_for_each_entry_safe(req, req_temp, &priv_ep->deferred_req_list,
+				 list) {
+		if (request == req)
+			goto found;
+	}
+
+found:
+	if (request == req)
+		cdns3_gadget_giveback(priv_ep, priv_req, -ECONNRESET);
+
+	spin_unlock_irqrestore(&priv_dev->lock, flags);
+	return ret;
+}
+
+/**
+ * cdns3_gadget_ep_set_halt Sets/clears stall on selected endpoint
+ * @ep: endpoint object to set/clear stall on
+ * @value: 1 for set stall, 0 for clear stall
+ *
+ * Returns 0 on success, error code elsewhere
+ */
+int cdns3_gadget_ep_set_halt(struct usb_ep *ep, int value)
+{
+	struct cdns3_endpoint *priv_ep = ep_to_cdns3_ep(ep);
+	struct cdns3_device *priv_dev = priv_ep->cdns3_dev;
+	unsigned long flags;
+	int ret = 0;
+
+	if (!(priv_ep->flags & EP_ENABLED))
+		return -EPERM;
+
+	spin_lock_irqsave(&priv_dev->lock, flags);
+
+	/* if actual transfer is pending defer setting stall on this endpoint */
+	if ((priv_ep->flags & EP_PENDING_REQUEST) && value) {
+		priv_ep->flags |= EP_STALL;
+		goto finish;
+	}
+
+	dev_dbg(priv_dev->dev, "Halt endpoint %s\n", priv_ep->name);
+
+	cdns3_select_ep(priv_dev, ep->desc->bEndpointAddress);
+	if (value) {
+		cdns3_ep_stall_flush(priv_ep);
+	} else {
+		priv_ep->flags &= ~EP_WEDGE;
+		writel(EP_CMD_CSTALL | EP_CMD_EPRST, &priv_dev->regs->ep_cmd);
+
+		/* wait for EPRST cleared */
+		ret = cdns3_handshake(&priv_dev->regs->ep_cmd,
+				      EP_CMD_EPRST, 0, 100);
+		if (unlikely(ret)) {
+			dev_err(priv_dev->dev,
+				"Clearing halt condition failed for %s\n",
+				priv_ep->name);
+			goto finish;
+
+		} else {
+			priv_ep->flags &= ~EP_STALL;
+		}
+	}
+
+	priv_ep->flags &= ~EP_PENDING_REQUEST;
+finish:
+	spin_unlock_irqrestore(&priv_dev->lock, flags);
+
+	return ret;
+}
+
+extern const struct usb_ep_ops cdns3_gadget_ep0_ops;
+
+static const struct usb_ep_ops cdns3_gadget_ep_ops = {
+	.enable = cdns3_gadget_ep_enable,
+	.disable = cdns3_gadget_ep_disable,
+	.alloc_request = cdns3_gadget_ep_alloc_request,
+	.free_request = cdns3_gadget_ep_free_request,
+	.queue = cdns3_gadget_ep_queue,
+	.dequeue = cdns3_gadget_ep_dequeue,
+	.set_halt = cdns3_gadget_ep_set_halt,
+	.set_wedge = cdns3_gadget_ep_set_wedge,
+};
+
+/**
+ * cdns3_gadget_get_frame Returns number of actual ITP frame
+ * @gadget: gadget object
+ *
+ * Returns number of actual ITP frame
+ */
+static int cdns3_gadget_get_frame(struct usb_gadget *gadget)
+{
+	struct cdns3_device *priv_dev = gadget_to_cdns3_device(gadget);
+
+	return readl(&priv_dev->regs->usb_iptn);
+}
+
+static int cdns3_gadget_wakeup(struct usb_gadget *gadget)
+{
+	return 0;
+}
+
+static int cdns3_gadget_set_selfpowered(struct usb_gadget *gadget,
+					int is_selfpowered)
+{
+	struct cdns3_device *priv_dev = gadget_to_cdns3_device(gadget);
+	unsigned long flags;
+
+	spin_lock_irqsave(&priv_dev->lock, flags);
+	priv_dev->is_selfpowered = !!is_selfpowered;
+	spin_unlock_irqrestore(&priv_dev->lock, flags);
+	return 0;
+}
+
+static int cdns3_gadget_pullup(struct usb_gadget *gadget, int is_on)
+{
+	struct cdns3_device *priv_dev = gadget_to_cdns3_device(gadget);
+
+	if (is_on)
+		writel(USB_CONF_DEVEN, &priv_dev->regs->usb_conf);
+	else
+		writel(USB_CONF_DEVDS, &priv_dev->regs->usb_conf);
+
+	return 0;
+}
+
+static void cdns3_gadget_config(struct cdns3_device *priv_dev)
+{
+	struct cdns3_usb_regs __iomem *regs = priv_dev->regs;
+
+	cdns3_ep0_config(priv_dev);
+
+	/* enable interrupts for endpoint 0 (in and out) */
+	writel(EP_IEN_EP_OUT0 | EP_IEN_EP_IN0, &regs->ep_ien);
+
+	/* enable generic interrupt*/
+	writel(USB_IEN_INIT, &regs->usb_ien);
+	writel(USB_CONF_CLK2OFFDS | USB_CONF_L1DS, &regs->usb_conf);
+	writel(USB_CONF_DMULT, &regs->usb_conf);
+	cdns3_gadget_pullup(&priv_dev->gadget, 1);
+}
+
+/**
+ * cdns3_gadget_udc_start Gadget start
+ * @gadget: gadget object
+ * @driver: driver which operates on this gadget
+ *
+ * Returns 0 on success, error code elsewhere
+ */
+static int cdns3_gadget_udc_start(struct usb_gadget *gadget,
+				  struct usb_gadget_driver *driver)
+{
+	struct cdns3_device *priv_dev = gadget_to_cdns3_device(gadget);
+	unsigned long flags;
+
+	spin_lock_irqsave(&priv_dev->lock, flags);
+	priv_dev->gadget_driver = driver;
+	cdns3_gadget_config(priv_dev);
+	spin_unlock_irqrestore(&priv_dev->lock, flags);
+	return 0;
+}
+
+/**
+ * cdns3_gadget_udc_stop Stops gadget
+ * @gadget: gadget object
+ *
+ * Returns 0
+ */
+static int cdns3_gadget_udc_stop(struct usb_gadget *gadget)
+{
+	struct cdns3_device *priv_dev = gadget_to_cdns3_device(gadget);
+	struct cdns3_endpoint *priv_ep;
+	u32 bEndpointAddress;
+	struct usb_ep *ep;
+	int ret = 0;
+
+	priv_dev->gadget_driver = NULL;
+
+	priv_dev->onchip_mem_allocated_size = 0;
+	priv_dev->gadget.speed = USB_SPEED_UNKNOWN;
+
+	list_for_each_entry(ep, &priv_dev->gadget.ep_list, ep_list) {
+		priv_ep = ep_to_cdns3_ep(ep);
+		bEndpointAddress = priv_ep->num | priv_ep->dir;
+		cdns3_select_ep(priv_dev, bEndpointAddress);
+		writel(EP_CMD_EPRST, &priv_dev->regs->ep_cmd);
+		ret = cdns3_handshake(&priv_dev->regs->ep_cmd,
+				      EP_CMD_EPRST, 0, 100);
+		cdns3_free_trb_pool(priv_ep);
+	}
+
+	/* disable interrupt for device */
+	writel(0, &priv_dev->regs->usb_ien);
+	writel(USB_CONF_DEVDS, &priv_dev->regs->usb_conf);
+
+	return ret;
+}
+
+static const struct usb_gadget_ops cdns3_gadget_ops = {
+	.get_frame = cdns3_gadget_get_frame,
+	.wakeup = cdns3_gadget_wakeup,
+	.set_selfpowered = cdns3_gadget_set_selfpowered,
+	.pullup = cdns3_gadget_pullup,
+	.udc_start = cdns3_gadget_udc_start,
+	.udc_stop = cdns3_gadget_udc_stop,
+	.match_ep = cdns3_gadget_match_ep,
+};
+
+static void cdns3_free_all_eps(struct cdns3_device *priv_dev)
+{
+	int i;
+
+	/*ep0 OUT point to ep0 IN*/
+	priv_dev->eps[16] = NULL;
+
+	cdns3_free_trb_pool(priv_dev->eps[0]);
+
+	for (i = 0; i < CDNS3_ENDPOINTS_MAX_COUNT; i++)
+		if (priv_dev->eps[i])
+			devm_kfree(priv_dev->dev, priv_dev->eps[i]);
+}
+
+/**
+ * cdns3_init_eps Initializes software endpoints of gadget
+ * @cdns3: extended gadget object
+ *
+ * Returns 0 on success, error code elsewhere
+ */
+static int cdns3_init_eps(struct cdns3_device *priv_dev)
+{
+	u32 ep_enabled_reg, iso_ep_reg;
+	struct cdns3_endpoint *priv_ep;
+	int ep_dir, ep_number;
+	u32 ep_mask;
+	int ret = 0;
+	int i;
+
+	/* Read it from USB_CAP3 to USB_CAP5 */
+	ep_enabled_reg = readl(&priv_dev->regs->usb_cap3);
+	iso_ep_reg = readl(&priv_dev->regs->usb_cap4);
+
+	dev_dbg(priv_dev->dev, "Initializing non-zero endpoints\n");
+
+	for (i = 0; i < CDNS3_ENDPOINTS_MAX_COUNT; i++) {
+		ep_dir = i >> 4;	/* i div 16 */
+		ep_number = i & 0xF;	/* i % 16 */
+		ep_mask = BIT(i);
+
+		if (!(ep_enabled_reg & ep_mask))
+			continue;
+
+		if (ep_dir && !ep_number) {
+			priv_dev->eps[i] = priv_dev->eps[0];
+			continue;
+		}
+
+		priv_ep = devm_kzalloc(priv_dev->dev, sizeof(*priv_ep),
+				       GFP_KERNEL);
+		if (!priv_ep) {
+			ret = -ENOMEM;
+			goto err;
+		}
+
+		/* set parent of endpoint object */
+		priv_ep->cdns3_dev = priv_dev;
+		priv_dev->eps[i] = priv_ep;
+		priv_ep->num = ep_number;
+		priv_ep->dir = ep_dir ? USB_DIR_IN : USB_DIR_OUT;
+
+		if (!ep_number) {
+			ret = cdns3_init_ep0(priv_dev, priv_ep);
+			if (ret) {
+				dev_err(priv_dev->dev, "Failed to init ep0\n");
+				goto err;
+			}
+		} else {
+			snprintf(priv_ep->name, sizeof(priv_ep->name), "ep%d%s",
+				 ep_number, !!ep_dir ? "in" : "out");
+			priv_ep->endpoint.name = priv_ep->name;
+
+			usb_ep_set_maxpacket_limit(&priv_ep->endpoint,
+						   CDNS3_EP_MAX_PACKET_LIMIT);
+			priv_ep->endpoint.max_streams = CDNS3_EP_MAX_STREAMS;
+			priv_ep->endpoint.ops = &cdns3_gadget_ep_ops;
+			if (ep_dir)
+				priv_ep->endpoint.caps.dir_in = 1;
+			else
+				priv_ep->endpoint.caps.dir_out = 1;
+
+			if (iso_ep_reg & ep_mask)
+				priv_ep->endpoint.caps.type_iso = 1;
+
+			priv_ep->endpoint.caps.type_bulk = 1;
+			priv_ep->endpoint.caps.type_int = 1;
+			priv_ep->endpoint.maxburst = CDNS3_EP_BUF_SIZE - 1;
+
+			list_add_tail(&priv_ep->endpoint.ep_list,
+				      &priv_dev->gadget.ep_list);
+		}
+
+		priv_ep->flags = 0;
+
+		dev_info(priv_dev->dev, "Initialized  %s support: %s %s\n",
+			 priv_ep->name,
+			 priv_ep->endpoint.caps.type_bulk ? "BULK, INT" : "",
+			 priv_ep->endpoint.caps.type_iso ? "ISO" : "");
+
+		INIT_LIST_HEAD(&priv_ep->pending_req_list);
+		INIT_LIST_HEAD(&priv_ep->deferred_req_list);
+		INIT_LIST_HEAD(&priv_ep->descmiss_req_list);
+	}
+
+	return 0;
+err:
+	cdns3_free_all_eps(priv_dev);
+	return -ENOMEM;
+}
+
+static void cdns3_gadget_disable(struct cdns3 *cdns)
+{
+	struct cdns3_device *priv_dev;
+
+	priv_dev = cdns->gadget_dev;
+
+	if (priv_dev->gadget_driver)
+		priv_dev->gadget_driver->disconnect(&priv_dev->gadget);
+
+	usb_gadget_disconnect(&priv_dev->gadget);
+	priv_dev->gadget.speed = USB_SPEED_UNKNOWN;
+}
+
+void cdns3_gadget_exit(struct cdns3 *cdns)
+{
+	struct cdns3_device *priv_dev;
+
+	priv_dev = cdns->gadget_dev;
+
+	cdns3_gadget_disable(cdns);
+
+	devm_free_irq(cdns->dev, cdns->irq, cdns);
+
+	pm_runtime_mark_last_busy(cdns->dev);
+	pm_runtime_put_autosuspend(cdns->dev);
+
+	usb_del_gadget_udc(&priv_dev->gadget);
+
+	cdns3_free_all_eps(priv_dev);
+
+	dma_free_coherent(priv_dev->sysdev, 8, priv_dev->setup_buf,
+			  priv_dev->setup_dma);
+
+	kfree(priv_dev->zlp_buf);
+	kfree(priv_dev);
+	cdns->gadget_dev = NULL;
+}
+
+static int cdns3_gadget_start(struct cdns3 *cdns)
+{
+	struct cdns3_device *priv_dev;
+	u32 max_speed;
+	int ret;
+
+	priv_dev = kzalloc(sizeof(*priv_dev), GFP_KERNEL);
+	if (!priv_dev)
+		return -ENOMEM;
+
+	cdns->gadget_dev = priv_dev;
+	priv_dev->sysdev = cdns->dev;
+	priv_dev->dev = cdns->dev;
+	priv_dev->regs = cdns->dev_regs;
+
+	max_speed = usb_get_maximum_speed(cdns->dev);
+
+	/* Check the maximum_speed parameter */
+	switch (max_speed) {
+	case USB_SPEED_FULL:
+	case USB_SPEED_HIGH:
+	case USB_SPEED_SUPER:
+		break;
+	default:
+		dev_err(cdns->dev, "invalid maximum_speed parameter %d\n",
+			max_speed);
+		/* fall through */
+	case USB_SPEED_UNKNOWN:
+		/* default to superspeed */
+		max_speed = USB_SPEED_SUPER;
+		break;
+	}
+
+	/* fill gadget fields */
+	priv_dev->gadget.max_speed = max_speed;
+	priv_dev->gadget.speed = USB_SPEED_UNKNOWN;
+	priv_dev->gadget.ops = &cdns3_gadget_ops;
+	priv_dev->gadget.name = "usb-ss-gadget";
+	priv_dev->gadget.sg_supported = 1;
+
+	spin_lock_init(&priv_dev->lock);
+	INIT_WORK(&priv_dev->pending_status_wq,
+		  cdns3_pending_setup_status_handler);
+
+	/* initialize endpoint container */
+	INIT_LIST_HEAD(&priv_dev->gadget.ep_list);
+
+	ret = cdns3_init_eps(priv_dev);
+	if (ret) {
+		dev_err(priv_dev->dev, "Failed to create endpoints\n");
+		goto err1;
+	}
+
+	/* allocate memory for setup packet buffer */
+	priv_dev->setup_buf = dma_alloc_coherent(priv_dev->sysdev, 8,
+						 &priv_dev->setup_dma, GFP_DMA);
+	if (!priv_dev->setup_buf) {
+		dev_err(priv_dev->dev, "Failed to allocate memory for SETUP buffer\n");
+		ret = -ENOMEM;
+		goto err2;
+	}
+
+	priv_dev->dev_ver = readl(&priv_dev->regs->usb_cap6);
+	dev_dbg(priv_dev->dev, "Device Controller version: %08x\n",
+		readl(&priv_dev->regs->usb_cap6));
+	dev_dbg(priv_dev->dev, "USB Capabilities:: %08x\n",
+		readl(&priv_dev->regs->usb_cap1));
+	dev_dbg(priv_dev->dev, "On-Chip memory cnfiguration: %08x\n",
+		readl(&priv_dev->regs->usb_cap2));
+
+	priv_dev->zlp_buf = kzalloc(CDNS3_EP_ZLP_BUF_SIZE, GFP_KERNEL);
+	if (!priv_dev->zlp_buf) {
+		ret = -ENOMEM;
+		goto err3;
+	}
+
+	/* add USB gadget device */
+	ret = usb_add_gadget_udc(priv_dev->dev, &priv_dev->gadget);
+	if (ret < 0) {
+		dev_err(priv_dev->dev,
+			"Failed to register USB device controller\n");
+		goto err4;
+	}
+
+	return 0;
+err4:
+	kfree(priv_dev->zlp_buf);
+err3:
+	dma_free_coherent(priv_dev->sysdev, 8, priv_dev->setup_buf,
+			  priv_dev->setup_dma);
+err2:
+	cdns3_free_all_eps(priv_dev);
+err1:
+	cdns->gadget_dev = NULL;
+	return ret;
+}
+
+static int __cdns3_gadget_init(struct cdns3 *cdns)
+{
+	struct cdns3_device *priv_dev;
+	unsigned long flags;
+	int ret = 0;
+
+	ret = cdns3_gadget_start(cdns);
+	if (ret)
+		return ret;
+
+	priv_dev = cdns->gadget_dev;
+	ret = devm_request_threaded_irq(cdns->dev, cdns->irq,
+					cdns3_device_irq_handler,
+					cdns3_device_thread_irq_handler,
+					IRQF_SHARED, dev_name(cdns->dev), cdns);
+	if (ret)
+		goto err0;
+
+	pm_runtime_get_sync(cdns->dev);
+	spin_lock_irqsave(&priv_dev->lock, flags);
+	spin_unlock_irqrestore(&priv_dev->lock, flags);
+	return 0;
+err0:
+	cdns3_gadget_exit(cdns);
+	return ret;
+}
+
+static int cdns3_gadget_suspend(struct cdns3 *cdns, bool do_wakeup)
+{
+	cdns3_gadget_disable(cdns);
+	return 0;
+}
+
+static int cdns3_gadget_resume(struct cdns3 *cdns, bool hibernated)
+{
+	struct cdns3_device *priv_dev;
+	unsigned long flags;
+
+	priv_dev = cdns->gadget_dev;
+	spin_lock_irqsave(&priv_dev->lock, flags);
+
+	if (!priv_dev->gadget_driver) {
+		spin_unlock_irqrestore(&priv_dev->lock, flags);
+		return 0;
+	}
+
+	cdns3_gadget_config(priv_dev);
+	spin_unlock_irqrestore(&priv_dev->lock, flags);
+	return 0;
+}
+
+/**
+ * cdns3_gadget_init - initialize device structure
+ *
+ * cdns: cdns3 instance
+ *
+ * This function initializes the gadget.
+ */
+int cdns3_gadget_init(struct cdns3 *cdns)
+{
+	struct cdns3_role_driver *rdrv;
+
+	rdrv = devm_kzalloc(cdns->dev, sizeof(*rdrv), GFP_KERNEL);
+	if (!rdrv)
+		return -ENOMEM;
+
+	rdrv->start	= __cdns3_gadget_init;
+	rdrv->stop	= cdns3_gadget_exit;
+	rdrv->suspend	= cdns3_gadget_suspend;
+	rdrv->resume	= cdns3_gadget_resume;
+	rdrv->state	= CDNS3_ROLE_STATE_INACTIVE;
+	rdrv->name	= "gadget";
+	cdns->roles[CDNS3_ROLE_GADGET] = rdrv;
+
+	return 0;
+}
diff --git a/drivers/usb/cdns3/gadget.h b/drivers/usb/cdns3/gadget.h
new file mode 100644
index 000000000000..41cec7f085ad
--- /dev/null
+++ b/drivers/usb/cdns3/gadget.h
@@ -0,0 +1,1206 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * USBSS device controller driver header file
+ *
+ * Copyright (C) 2018 Cadence.
+ * Copyright (C) 2017-2018 NXP
+ *
+ * Author: Pawel Laszczak <pawell@cadence.com>
+ *         Pawel Jez <pjez@cadence.com>
+ *         Peter Chen <peter.chen@nxp.com>
+ */
+#ifndef __LINUX_CDNS3_GADGET
+#define __LINUX_CDNS3_GADGET
+#include <linux/usb/gadget.h>
+
+/*
+ * USBSS-DEV register interface.
+ * This corresponds to the USBSS Device Controller Interface
+ */
+
+/**
+ * struct cdns3_usb_regs - device controller registers.
+ * @usb_conf:      Global Configuration Register.
+ * @usb_sts:       Global Status Register.
+ * @usb_cmd:       Global Command Register.
+ * @usb_iptn:      ITP/SOF number Register.
+ * @usb_lpm:       Global Command Register.
+ * @usb_ien:       USB Interrupt Enable Register.
+ * @usb_ists:      USB Interrupt Status Register.
+ * @ep_sel:        Endpoint Select Register.
+ * @ep_traddr:     Endpoint Transfer Ring Address Register.
+ * @ep_cfg:        Endpoint Configuration Register.
+ * @ep_cmd:        Endpoint Command Register.
+ * @ep_sts:        Endpoint Status Register.
+ * @ep_sts_sid:    Endpoint Status Register.
+ * @ep_sts_en:     Endpoint Status Register Enable.
+ * @drbl:          Doorbell Register.
+ * @ep_ien:        EP Interrupt Enable Register.
+ * @ep_ists:       EP Interrupt Status Register.
+ * @usb_pwr:       Global Power Configuration Register.
+ * @usb_conf2:     Global Configuration Register 2.
+ * @usb_cap1:      Capability Register 1.
+ * @usb_cap2:      Capability Register 2.
+ * @usb_cap3:      Capability Register 3.
+ * @usb_cap4:      Capability Register 4.
+ * @usb_cap5:      Capability Register 5.
+ * @usb_cap6:      Capability Register 6.
+ * @usb_cpkt1:     Custom Packet Register 1.
+ * @usb_cpkt2:     Custom Packet Register 2.
+ * @usb_cpkt3:     Custom Packet Register 3.
+ * @reserved1:     Reserved.
+ * @cfg_regs:      Configuration registers.
+ * @reserved2:     Reserved.
+ * @dma_axi_ctrl:  AXI Control register.
+ * @dma_axi_id:    AXI ID register.
+ * @dma_axi_cap:   AXI Capability register.
+ * @dma_axi_ctrl0: AXI Control 0 register.
+ * @dma_axi_ctrl1: AXI Control 1 register.
+ */
+struct cdns3_usb_regs {
+	__le32 usb_conf;
+	__le32 usb_sts;
+	__le32 usb_cmd;
+	__le32 usb_iptn;
+	__le32 usb_lpm;
+	__le32 usb_ien;
+	__le32 usb_ists;
+	__le32 ep_sel;
+	__le32 ep_traddr;
+	__le32 ep_cfg;
+	__le32 ep_cmd;
+	__le32 ep_sts;
+	__le32 ep_sts_sid;
+	__le32 ep_sts_en;
+	__le32 drbl;
+	__le32 ep_ien;
+	__le32 ep_ists;
+	__le32 usb_pwr;
+	__le32 usb_conf2;
+	__le32 usb_cap1;
+	__le32 usb_cap2;
+	__le32 usb_cap3;
+	__le32 usb_cap4;
+	__le32 usb_cap5;
+	__le32 usb_cap6;
+	__le32 usb_cpkt1;
+	__le32 usb_cpkt2;
+	__le32 usb_cpkt3;
+	__le32 reserved1[36];
+	__le32 cfg_reg1;
+	__le32 dbg_link1;
+	__le32 dbg_link2;
+	__le32 cfg_regs[74];
+	__le32 reserved2[34];
+	__le32 dma_axi_ctrl;
+	__le32 dma_axi_id;
+	__le32 dma_axi_cap;
+	__le32 dma_axi_ctrl0;
+	__le32 dma_axi_ctrl1;
+};
+
+/* USB_CONF - bitmasks */
+/* Reset USB device configuration. */
+#define USB_CONF_CFGRST		BIT(0)
+/* Set Configuration. */
+#define USB_CONF_CFGSET		BIT(1)
+/* Disconnect USB device in SuperSpeed. */
+#define USB_CONF_USB3DIS	BIT(3)
+/* Disconnect USB device in HS/FS */
+#define USB_CONF_USB2DIS	BIT(4)
+/* Little Endian access - default */
+#define USB_CONF_LENDIAN	BIT(5)
+/*
+ * Big Endian access. Driver assume that byte order for
+ * SFRs access always is as Little Endian so this bit
+ * is not used.
+ */
+#define USB_CONF_BENDIAN	BIT(6)
+/* Device software reset. */
+#define USB_CONF_SWRST		BIT(7)
+/* Singular DMA transfer mode. */
+#define USB_CONF_DSING		BIT(8)
+/* Multiple DMA transfers mode. */
+#define USB_CONF_DMULT		BIT(9)
+/* DMA clock turn-off enable. */
+#define USB_CONF_DMAOFFEN	BIT(10)
+/* DMA clock turn-off disable. */
+#define USB_CONF_DMAOFFDS	BIT(11)
+/* Clear Force Full Speed. */
+#define USB_CONF_CFORCE_FS	BIT(12)
+/* Set Force Full Speed. */
+#define USB_CONF_SFORCE_FS	BIT(13)
+/* Device enable. */
+#define USB_CONF_DEVEN		BIT(14)
+/* Device disable. */
+#define USB_CONF_DEVDS		BIT(15)
+/* L1 LPM state entry enable (used in HS/FS mode). */
+#define USB_CONF_L1EN		BIT(16)
+/* L1 LPM state entry disable (used in HS/FS mode). */
+#define USB_CONF_L1DS		BIT(17)
+/* USB 2.0 clock gate disable. */
+#define USB_CONF_CLK2OFFEN	BIT(18)
+/* USB 2.0 clock gate enable. */
+#define USB_CONF_CLK2OFFDS	BIT(19)
+/* L0 LPM state entry request (used in HS/FS mode). */
+#define USB_CONF_LGO_L0		BIT(20)
+/* USB 3.0 clock gate disable. */
+#define USB_CONF_CLK3OFFEN	BIT(21)
+/* USB 3.0 clock gate enable. */
+#define USB_CONF_CLK3OFFDS	BIT(22)
+/* Bit 23 is reserved*/
+/* U1 state entry enable (used in SS mode). */
+#define USB_CONF_U1EN		BIT(24)
+/* U1 state entry disable (used in SS mode). */
+#define USB_CONF_U1DS		BIT(25)
+/* U2 state entry enable (used in SS mode). */
+#define USB_CONF_U2EN		BIT(26)
+/* U2 state entry disable (used in SS mode). */
+#define USB_CONF_U2DS		BIT(27)
+/* U0 state entry request (used in SS mode). */
+#define USB_CONF_LGO_U0		BIT(28)
+/* U1 state entry request (used in SS mode). */
+#define USB_CONF_LGO_U1		BIT(29)
+/* U2 state entry request (used in SS mode). */
+#define USB_CONF_LGO_U2		BIT(30)
+/* SS.Inactive state entry request (used in SS mode) */
+#define USB_CONF_LGO_SSINACT	BIT(31)
+
+/* USB_STS - bitmasks */
+/*
+ * Configuration status.
+ * 1 - device is in the configured state.
+ * 0 - device is not configured.
+ */
+#define USB_STS_CFGSTS_MASK	BIT(0)
+#define USB_STS_CFGSTS(p)	((p) & USB_STS_CFGSTS_MASK)
+/*
+ * On-chip memory overflow.
+ * 0 - On-chip memory status OK.
+ * 1 - On-chip memory overflow.
+ */
+#define USB_STS_OV_MASK		BIT(1)
+#define USB_STS_OV(p)		((p) & USB_STS_OV_MASK)
+/*
+ * SuperSpeed connection status.
+ * 0 - USB in SuperSpeed mode disconnected.
+ * 1 - USB in SuperSpeed mode connected.
+ */
+#define USB_STS_USB3CONS_MASK	BIT(2)
+#define USB_STS_USB3CONS(p)	((p) & USB_STS_USB3CONS_MASK)
+/*
+ * DMA transfer configuration status.
+ * 0 - single request.
+ * 1 - multiple TRB chain
+ */
+#define USB_STS_DTRANS_MASK	BIT(3)
+#define USB_STS_DTRANS(p)	((p) & USB_STS_DTRANS_MASK)
+/*
+ * Device speed.
+ * 0 - Undefined (value after reset).
+ * 1 - Low speed
+ * 2 - Full speed
+ * 3 - High speed
+ * 4 - Super speed
+ */
+#define USB_STS_USBSPEED_MASK	GENMASK(6, 4)
+#define USB_STS_USBSPEED(p)	(((p) & USB_STS_USBSPEED_MASK) >> 4)
+#define USB_STS_LS		(0x1 << 4)
+#define USB_STS_FS		(0x2 << 4)
+#define USB_STS_HS		(0x3 << 4)
+#define USB_STS_SS		(0x4 << 4)
+#define DEV_UNDEFSPEED(p)	(((p) & USB_STS_USBSPEED_MASK) == (0x0 << 4))
+#define DEV_LOWSPEED(p)		(((p) & USB_STS_USBSPEED_MASK) == USB_STS_LS)
+#define DEV_FULLSPEED(p)	(((p) & USB_STS_USBSPEED_MASK) == USB_STS_FS)
+#define DEV_HIGHSPEED(p)	(((p) & USB_STS_USBSPEED_MASK) == USB_STS_HS)
+#define DEV_SUPERSPEED(p)	(((p) & USB_STS_USBSPEED_MASK) == USB_STS_SS)
+/*
+ * Endianness for SFR access.
+ * 0 - Little Endian order (default after hardware reset).
+ * 1 - Big Endian order
+ */
+#define USB_STS_ENDIAN_MASK	BIT(7)
+#define USB_STS_ENDIAN(p)	((p) & USB_STS_ENDIAN_MASK)
+/*
+ * HS/FS clock turn-off status.
+ * 0 - hsfs clock is always on.
+ * 1 - hsfs clock turn-off in L2 (HS/FS mode) is enabled
+ *          (default after hardware reset).
+ */
+#define USB_STS_CLK2OFF_MASK	BIT(8)
+#define USB_STS_CLK2OFF(p)	((p) & USB_STS_CLK2OFF_MASK)
+/*
+ * PCLK clock turn-off status.
+ * 0 - pclk clock is always on.
+ * 1 - pclk clock turn-off in U3 (SS mode) is enabled
+ *          (default after hardware reset).
+ */
+#define USB_STS_CLK3OFF_MASK	BIT(9)
+#define USB_STS_CLK3OFF(p)	((p) & USB_STS_CLK3OFF_MASK)
+/*
+ * Controller in reset state.
+ * 0 - Internal reset is active.
+ * 1 - Internal reset is not active and controller is fully operational.
+ */
+#define USB_STS_IN_RST_MASK	BIT(10)
+#define USB_STS_IN_RST(p)	((p) & USB_STS_IN_RST_MASK)
+/*
+ * Device enable Status.
+ * 0 - USB device is disabled (VBUS input is disconnected from internal logic).
+ * 1 - USB device is enabled (VBUS input is connected to the internal logic).
+ */
+#define USB_STS_DEVS_MASK	BIT(14)
+#define USB_STS_DEVS(p)		((p) & USB_STS_DEVS_MASK)
+/*
+ * DAddress statuss.
+ * 0 - USB device is default state.
+ * 1 - USB device is at least in address state.
+ */
+#define USB_STS_ADDRESSED_MASK	BIT(15)
+#define USB_STS_ADDRESSED(p)	((p) & USB_STS_ADDRESSED_MASK)
+/*
+ * L1 LPM state enable status (used in HS/FS mode).
+ * 0 - Entering to L1 LPM state disabled.
+ * 1 - Entering to L1 LPM state enabled.
+ */
+#define USB_STS_L1ENS_MASK	BIT(16)
+#define USB_STS_L1ENS(p)	((p) & USB_STS_L1ENS_MASK)
+/*
+ * Internal VBUS connection status (used both in HS/FS  and SS mode).
+ * 0 - internal VBUS is not detected.
+ * 1 - internal VBUS is detected.
+ */
+#define USB_STS_VBUSS_MASK	BIT(17)
+#define USB_STS_VBUSS(p)	((p) & USB_STS_VBUSS_MASK)
+/*
+ * HS/FS LPM  state (used in FS/HS mode).
+ * 0 - L0 State
+ * 1 - L1 State
+ * 2 - L2 State
+ * 3 - L3 State
+ */
+#define USB_STS_LPMST_MASK	GENMASK(19, 18)
+#define DEV_L0_STATE(p)		(((p) & USB_STS_LPMST_MASK) == (0x0 << 18))
+#define DEV_L1_STATE(p)		(((p) & USB_STS_LPMST_MASK) == (0x1 << 18))
+#define DEV_L2_STATE(p)		(((p) & USB_STS_LPMST_MASK) == (0x2 << 18))
+#define DEV_L3_STATE(p)		(((p) & USB_STS_LPMST_MASK) == (0x3 << 18))
+/*
+ * Disable HS status (used in FS/HS mode).
+ * 0 - the disconnect bit for HS/FS mode is set .
+ * 1 - the disconnect bit for HS/FS mode is not set.
+ */
+#define USB_STS_USB2CONS_MASK	BIT(20)
+#define USB_STS_USB2CONS(p)	((p) & USB_STS_USB2CONS_MASK)
+/*
+ * HS/FS mode connection status (used in FS/HS mode).
+ * 0 - High Speed operations in USB2.0 (FS/HS) mode not disabled.
+ * 1 - High Speed operations in USB2.0 (FS/HS).
+ */
+#define USB_STS_DISABLE_HS_MASK	BIT(21)
+#define USB_STS_DISABLE_HS(p)	((p) & USB_STS_DISABLE_HS_MASK)
+/*
+ * U1 state enable status (used in SS mode).
+ * 0 - Entering to  U1 state disabled.
+ * 1 - Entering to  U1 state enabled.
+ */
+#define USB_STS_U1ENS_MASK	BIT(24)
+#define USB_STS_U1ENS(p)	((p) & USB_STS_U1ENS_MASK)
+/*
+ * U2 state enable status (used in SS mode).
+ * 0 - Entering to  U2 state disabled.
+ * 1 - Entering to  U2 state enabled.
+ */
+#define USB_STS_U2ENS_MASK	BIT(25)
+#define USB_STS_U2ENS(p)	((p) & USB_STS_U2ENS_MASK)
+/*
+ * SuperSpeed Link LTSSM state. This field reflects USBSS-DEV current
+ * SuperSpeed link state
+ */
+#define USB_STS_LST_MASK	GENMASK(29, 26)
+#define DEV_LST_U0		(((p) & USB_STS_LST_MASK) == (0x0 << 26))
+#define DEV_LST_U1		(((p) & USB_STS_LST_MASK) == (0x1 << 26))
+#define DEV_LST_U2		(((p) & USB_STS_LST_MASK) == (0x2 << 26))
+#define DEV_LST_U3		(((p) & USB_STS_LST_MASK) == (0x3 << 26))
+#define DEV_LST_DISABLED	(((p) & USB_STS_LST_MASK) == (0x4 << 26))
+#define DEV_LST_RXDETECT	(((p) & USB_STS_LST_MASK) == (0x5 << 26))
+#define DEV_LST_INACTIVE	(((p) & USB_STS_LST_MASK) == (0x6 << 26))
+#define DEV_LST_POLLING		(((p) & USB_STS_LST_MASK) == (0x7 << 26))
+#define DEV_LST_RECOVERY	(((p) & USB_STS_LST_MASK) == (0x8 << 26))
+#define DEV_LST_HOT_RESET	(((p) & USB_STS_LST_MASK) == (0x9 << 26))
+#define DEV_LST_COMP_MODE	(((p) & USB_STS_LST_MASK) == (0xa << 26))
+#define DEV_LST_LB_STATE	(((p) & USB_STS_LST_MASK) == (0xb << 26))
+/*
+ * DMA clock turn-off status.
+ * 0 - DMA clock is always on (default after hardware reset).
+ * 1 - DMA clock turn-off in U1, U2 and U3 (SS mode) is enabled.
+ */
+#define USB_STS_DMAOFF_MASK	BIT(30)
+#define USB_STS_DMAOFF(p)	((p) & USB_STS_DMAOFF_MASK)
+/*
+ * SFR Endian statuss.
+ * 0 - Little Endian order (default after hardware reset).
+ * 1 - Big Endian order.
+ */
+#define USB_STS_ENDIAN2_MASK	BIT(31)
+#define USB_STS_ENDIAN2(p)	((p) & USB_STS_ENDIAN2_MASK)
+
+/* USB_CMD -  bitmasks */
+/* Set Function Address */
+#define USB_CMD_SET_ADDR	BIT(0)
+/*
+ * Function Address This field is saved to the device only when the field
+ * SET_ADDR is set '1 ' during write to USB_CMD register.
+ * Software is responsible for entering the address of the device during
+ * SET_ADDRESS request service. This field should be set immediately after
+ * the SETUP packet is decoded, and prior to confirmation of the status phase
+ */
+#define USB_CMD_FADDR_MASK	GENMASK(7, 1)
+#define USB_CMD_FADDR(p)	(((p) << 1) & USB_CMD_FADDR_MASK)
+/* Send Function Wake Device Notification TP (used only in SS mode). */
+#define USB_CMD_SDNFW		BIT(8)
+/* Set Test Mode (used only in HS/FS mode). */
+#define USB_CMD_STMODE		BIT(9)
+/* Test mode selector (used only in HS/FS mode) */
+#define USB_STS_TMODE_SEL_MASK	GENMASK(11, 10)
+#define USB_STS_TMODE_SEL(p)	(((p) << 10) & USB_STS_TMODE_SEL_MASK)
+/*
+ *  Send Latency Tolerance Message Device Notification TP (used only
+ *  in SS mode).
+ */
+#define USB_CMD_SDNLTM		BIT(12)
+/* Send Custom Transaction Packet (used only in SS mode) */
+#define USB_CMD_SPKT		BIT(13)
+/*Device Notification 'Function Wake' - Interface value (only in SS mode. */
+#define USB_CMD_DNFW_INT_MASK	GENMASK(23, 16)
+#define USB_STS_DNFW_INT(p)	(((p) << 16) & USB_CMD_DNFW_INT_MASK)
+/*
+ * Device Notification 'Latency Tolerance Message' -373 BELT value [7:0]
+ * (used only in SS mode).
+ */
+#define USB_CMD_DNLTM_BELT_MASK	GENMASK(27, 16)
+#define USB_STS_DNLTM_BELT(p)	(((p) << 16) & USB_CMD_DNLTM_BELT_MASK)
+
+/* USB_ITPN - bitmasks */
+/*
+ * ITP(SS) / SOF (HS/FS) number
+ * In SS mode this field represent number of last ITP received from host.
+ * In HS/FS mode this field represent number of last SOF received from host.
+ */
+#define USB_ITPN_MASK		GENMASK(13, 0)
+#define USB_ITPN(p)		((p) & USB_ITPN_MASK)
+
+/* USB_LPM - bitmasks */
+/* Host Initiated Resume Duration. */
+#define USB_LPM_HIRD_MASK	GENMASK(3, 0)
+#define USB_LPM_HIRD(p)		((p) & USB_LPM_HIRD_MASK)
+/* Remote Wakeup Enable (bRemoteWake). */
+#define USB_LPM_BRW		BIT(4)
+
+/* USB_IEN - bitmasks */
+/* SS connection interrupt enable */
+#define USB_IEN_CONIEN		BIT(0)
+/* SS disconnection interrupt enable. */
+#define USB_IEN_DISIEN		BIT(1)
+/* USB SS warm reset interrupt enable. */
+#define USB_IEN_UWRESIEN	BIT(2)
+/* USB SS hot reset interrupt enable */
+#define USB_IEN_UHRESIEN	BIT(3)
+/* SS link U3 state enter interrupt enable (suspend).*/
+#define USB_IEN_U3ENTIEN	BIT(4)
+/* SS link U3 state exit interrupt enable (wakeup). */
+#define USB_IEN_U3EXTIEN	BIT(5)
+/* SS link U2 state enter interrupt enable.*/
+#define USB_IEN_U2ENTIEN	BIT(6)
+/* SS link U2 state exit interrupt enable.*/
+#define USB_IEN_U2EXTIEN	BIT(7)
+/* SS link U1 state enter interrupt enable.*/
+#define USB_IEN_U1ENTIEN	BIT(8)
+/* SS link U1 state exit interrupt enable.*/
+#define USB_IEN_U1EXTIEN	BIT(9)
+/* ITP/SOF packet detected interrupt enable.*/
+#define USB_IEN_ITPIEN		BIT(10)
+/* Wakeup interrupt enable.*/
+#define USB_IEN_WAKEIEN		BIT(11)
+/* Send Custom Packet interrupt enable.*/
+#define USB_IEN_SPKTIEN		BIT(12)
+/* HS/FS mode connection interrupt enable.*/
+#define USB_IEN_CON2IEN		BIT(16)
+/* HS/FS mode disconnection interrupt enable.*/
+#define USB_IEN_DIS2IEN		BIT(17)
+/* USB reset (HS/FS mode) interrupt enable.*/
+#define USB_IEN_U2RESIEN	BIT(18)
+/* LPM L2 state enter interrupt enable.*/
+#define USB_IEN_L2ENTIEN	BIT(20)
+/* LPM  L2 state exit interrupt enable.*/
+#define USB_IEN_L2EXTIEN	BIT(21)
+/* LPM L1 state enter interrupt enable.*/
+#define USB_IEN_L1ENTIEN	BIT(24)
+/* LPM  L1 state exit interrupt enable.*/
+#define USB_IEN_L1EXTIEN	BIT(25)
+/* Configuration reset interrupt enable.*/
+#define USB_IEN_CFGRESIEN	BIT(26)
+/* Start of the USB SS warm reset interrupt enable.*/
+#define USB_IEN_UWRESSIEN	BIT(28)
+/* End of the USB SS warm reset interrupt enable.*/
+#define USB_IEN_UWRESEIEN	BIT(29)
+
+#define USB_IEN_INIT  (USB_IEN_U2RESIEN | USB_ISTS_DIS2I | USB_IEN_CON2IEN \
+		       | USB_IEN_UHRESIEN | USB_IEN_UWRESIEN | USB_IEN_DISIEN \
+		       | USB_IEN_CONIEN | USB_IEN_U3EXTIEN | USB_IEN_L2ENTIEN \
+		       | USB_IEN_L2EXTIEN)
+
+/* USB_ISTS - bitmasks */
+/* SS Connection detected. */
+#define USB_ISTS_CONI		BIT(0)
+/* SS Disconnection detected. */
+#define USB_ISTS_DISI		BIT(1)
+/* UUSB warm reset detectede. */
+#define USB_ISTS_UWRESI		BIT(2)
+/* USB hot reset detected. */
+#define USB_ISTS_UHRESI		BIT(3)
+/* U3 link state enter detected (suspend).*/
+#define USB_ISTS_U3ENTI		BIT(4)
+/* U3 link state exit detected (wakeup). */
+#define USB_ISTS_U3EXTI		BIT(5)
+/* U2 link state enter detected.*/
+#define USB_ISTS_U2ENTI		BIT(6)
+/* U2 link state exit detected.*/
+#define USB_ISTS_U2EXTI		BIT(7)
+/* U1 link state enter detected.*/
+#define USB_ISTS_U1ENTI		BIT(8)
+/* U1 link state exit detected.*/
+#define USB_ISTS_U1EXTI		BIT(9)
+/* ITP/SOF packet detected.*/
+#define USB_ISTS_ITPI		BIT(10)
+/* Wakeup detected.*/
+#define USB_ISTS_WAKEI		BIT(11)
+/* Send Custom Packet detected.*/
+#define USB_ISTS_SPKTI		BIT(12)
+/* HS/FS mode connection detected.*/
+#define USB_ISTS_CON2I		BIT(16)
+/* HS/FS mode disconnection detected.*/
+#define USB_ISTS_DIS2I		BIT(17)
+/* USB reset (HS/FS mode) detected.*/
+#define USB_ISTS_U2RESI		BIT(18)
+/* LPM L2 state enter detected.*/
+#define USB_ISTS_L2ENTI		BIT(20)
+/* LPM  L2 state exit detected.*/
+#define USB_ISTS_L2EXTI		BIT(21)
+/* LPM L1 state enter detected.*/
+#define USB_ISTS_L1ENTI		BIT(24)
+/* LPM L1 state exit detected.*/
+#define USB_ISTS_L1EXTI		BIT(25)
+/* USB configuration reset detected.*/
+#define USB_ISTS_CFGRESI	BIT(26)
+/* Start of the USB warm reset detected.*/
+#define USB_ISTS_UWRESSI	BIT(28)
+/* End of the USB warm reset detected.*/
+#define USB_ISTS_UWRESEI	BIT(29)
+
+/* USB_SEL - bitmasks */
+#define EP_SEL_EPNO_MASK	GENMASK(3, 0)
+/* Endpoint number. */
+#define EP_SEL_EPNO(p)		((p) & EP_SEL_EPNO_MASK)
+/* Endpoint direction bit - 0 - OUT, 1 - IN. */
+#define EP_SEL_DIR		BIT(7)
+
+#define select_ep_in(nr)	(EP_SEL_EPNO(p) | EP_SEL_DIR)
+#define select_ep_out		(EP_SEL_EPNO(p))
+
+/* EP_TRADDR - bitmasks */
+/* Transfer Ring address. */
+#define EP_TRADDR_TRADDR(p)	((p))
+
+/* EP_CFG - bitmasks */
+/* Endpoint enable */
+#define EP_CFG_ENABLE		BIT(0)
+/*
+ *  Endpoint type.
+ * 1 - isochronous
+ * 2 - bulk
+ * 3 - interrupt
+ */
+#define EP_CFG_EPTYPE_MASK	GENMASK(2, 1)
+#define EP_CFG_EPTYPE(p)	(((p) << 1)  & EP_CFG_EPTYPE_MASK)
+/* Stream support enable (only in SS mode). */
+#define EP_CFG_STREAM_EN	BIT(3)
+/* TDL check (only in SS mode for BULK EP). */
+#define EP_CFG_TDL_CHK		BIT(4)
+/* SID check (only in SS mode for BULK OUT EP). */
+#define EP_CFG_SID_CHK		BIT(5)
+/* DMA transfer endianness. */
+#define EP_CFG_EPENDIAN		BIT(7)
+/* Max burst size (used only in SS mode). */
+#define EP_CFG_MAXBURST_MASK	GENMASK(11, 8)
+#define EP_CFG_MAXBURST(p)	(((p) << 8) & EP_CFG_MAXBURST_MASK)
+/* ISO max burst. */
+#define EP_CFG_MULT_MASK	GENMASK(15, 14)
+#define EP_CFG_MULT(p)		(((p) << 14) & EP_CFG_MULT)
+/* ISO max burst. */
+#define EP_CFG_MAXPKTSIZE_MASK	GENMASK(26, 16)
+#define EP_CFG_MAXPKTSIZE(p)	(((p) << 16) & EP_CFG_MAXPKTSIZE_MASK)
+/* Max number of buffered packets. */
+#define EP_CFG_BUFFERING_MASK	GENMASK(31, 27)
+#define EP_CFG_BUFFERING(p)	(((p) << 27) & EP_CFG_BUFFERING_MASK)
+
+/* EP_CMD - bitmasks */
+/* Endpoint reset. */
+#define EP_CMD_EPRST		BIT(0)
+/* Endpoint STALL set. */
+#define EP_CMD_SSTALL		BIT(1)
+/* Endpoint STALL clear. */
+#define EP_CMD_CSTALL		BIT(2)
+/* Send ERDY TP. */
+#define EP_CMD_ERDY		BIT(3)
+/* Request complete. */
+#define EP_CMD_REQ_CMPL		BIT(5)
+/* Transfer descriptor ready. */
+#define EP_CMD_DRDY		BIT(6)
+/* Data flush. */
+#define EP_CMD_DFLUSH		BIT(7)
+/*
+ * Transfer Descriptor Length write  (used only for Bulk Stream capable
+ * endpoints in SS mode).
+ */
+#define EP_CMD_STDL		BIT(8)
+/* Transfer Descriptor Length (used only in SS mode for bulk endpoints). */
+#define EP_CMD_TDL_MASK		GENMASK(15, 9)
+#define EP_CMD_TDL(p)		(((p) << 9) & EP_CMD_TDL_MASK)
+/* ERDY Stream ID value (used in SS mode). */
+#define EP_CMD_ERDY_SID_MASK	GENMASK(31, 16)
+#define EP_CMD_ERDY_SID(p)	(((p) << 16) & EP_CMD_SID_MASK)
+
+/* EP_STS - bitmasks */
+/* Setup transfer complete. */
+#define EP_STS_SETUP		BIT(0)
+/* Endpoint STALL status. */
+#define EP_STS_STALL(p)		((p) & BIT(1))
+/* Interrupt On Complete. */
+#define EP_STS_IOC		BIT(2)
+/* Interrupt on Short Packet. */
+#define EP_STS_ISP		BIT(3)
+/* Transfer descriptor missing. */
+#define EP_STS_DESCMIS		BIT(4)
+/* Stream Rejected (used only in SS mode) */
+#define EP_STS_STREAMR		BIT(5)
+/* EXIT from MOVE DATA State (used only for stream transfers in SS mode). */
+#define EP_STS_MD_EXIT		BIT(6)
+/* TRB error. */
+#define EP_STS_TRBERR		BIT(7)
+/* Not ready (used only in SS mode). */
+#define EP_STS_NRDY		BIT(8)
+/* DMA busy. */
+#define EP_STS_DBUSY(p)		((p) & BIT(9))
+/* Endpoint Buffer Empty */
+#define EP_STS_BUFFEMPTY(p)	((p) & BIT(10))
+/* Current Cycle Status */
+#define EP_STS_CCS(p)		((p) & BIT(11))
+/* Prime (used only in SS mode. */
+#define EP_STS_PRIME		BIT(12)
+/* Stream error (used only in SS mode). */
+#define EP_STS_SIDERR		BIT(13)
+/* OUT size mismatch. */
+#define EP_STS_OUTSMM		BIT(14)
+/* ISO transmission error. */
+#define EP_STS_ISOERR		BIT(15)
+/* Host Packet Pending (only for SS mode). */
+#define EP_STS_HOSTPP(p)	((p) & BIT(16))
+/* Stream Protocol State Machine State (only for Bulk stream endpoints). */
+#define EP_STS_SPSMST_MASK		GENMASK(18, 17)
+#define EP_STS_SPSMST_DISABLED(p)	(((p) & EP_STS_SPSMST_MASK) >> 17)
+#define EP_STS_SPSMST_IDLE(p)		(((p) & EP_STS_SPSMST_MASK) >> 17)
+#define EP_STS_SPSMST_START_STREAM(p)	(((p) & EP_STS_SPSMST_MASK) >> 17)
+#define EP_STS_SPSMST_MOVE_DATA(p)	(((p) & EP_STS_SPSMST_MASK) >> 17)
+/* Interrupt On Transfer complete. */
+#define EP_STS_IOT		BIT(19)
+/* OUT queue endpoint number. */
+#define EP_STS_OUTQ_NO_MASK	GENMASK(27, 24)
+#define EP_STS_OUTQ_NO(p)	(((p) & EP_STS_OUTQ_NO_MASK) >> 24)
+/* OUT queue valid flag. */
+#define EP_STS_OUTQ_VAL_MASK	BIT(28)
+#define EP_STS_OUTQ_VAL(p)	((p) & EP_STS_OUTQ_VAL_MASK)
+/* SETUP WAIT. */
+#define EP_STS_STPWAIT		BIT(31)
+
+/* EP_STS_SID - bitmasks */
+/* Stream ID (used only in SS mode). */
+#define EP_STS_SID_MASK		GENMASK(15, 0)
+#define EP_STS_SID(p)		((p) & EP_STS_SID_MASK)
+
+/* EP_STS_EN - bitmasks */
+/* SETUP interrupt enable. */
+#define EP_STS_EN_SETUPEN	BIT(0)
+/* OUT transfer missing descriptor enable. */
+#define EP_STS_EN_DESCMISEN	BIT(4)
+/* Stream Rejected enable. */
+#define EP_STS_EN_STREAMREN	BIT(5)
+/* Move Data Exit enable.*/
+#define EP_STS_EN_MD_EXITEN	BIT(6)
+/* TRB enable. */
+#define EP_STS_EN_TRBERREN	BIT(7)
+/* NRDY enable. */
+#define EP_STS_EN_NRDYEN	BIT(8)
+/* Prime enable. */
+#define EP_STS_EN_PRIMEEEN	BIT(12)
+/* Stream error enable. */
+#define EP_STS_EN_SIDERREN	BIT(13)
+/* OUT size mismatch enable. */
+#define EP_STS_EN_OUTSMMEN	BIT(14)
+/* ISO transmission error enable. */
+#define EP_STS_EN_ISOERREN	BIT(15)
+/* Interrupt on Transmission complete enable. */
+#define EP_STS_EN_IOTEN		BIT(19)
+/* Setup Wait interrupt enable. */
+#define EP_STS_EN_STPWAITEN	BIT(31)
+
+/* DRBL- bitmasks */
+#define DB_VALUE_BY_INDEX(index) (1 << (index))
+#define DB_VALUE_EP0_OUT	BIT(0)
+#define DB_VALUE_EP0_IN		BIT(16)
+
+/* EP_IEN - bitmasks */
+#define EP_IEN(index)		(1 << (index))
+#define EP_IEN_EP_OUT0		BIT(0)
+#define EP_IEN_EP_IN0		BIT(16)
+
+/* EP_ISTS - bitmasks */
+#define EP_ISTS(index)		(1 << (index))
+#define EP_ISTS_EP_OUT0		BIT(0)
+#define EP_ISTS_EP_IN0		BIT(16)
+
+/* EP_PWR- bitmasks */
+/*Power Shut Off capability enable*/
+#define PUSB_PWR_PSO_EN		BIT(0)
+/*Power Shut Off capability disable*/
+#define PUSB_PWR_PSO_DS		BIT(1)
+/*
+ * Enables turning-off Reference Clock.
+ * This bit is optional and implemented only when support for OTG is
+ * implemented (indicated by OTG_READY bit set to '1').
+ */
+#define PUSB_PWR_STB_CLK_SWITCH_EN	BIT(8)
+/*
+ * Status bit indicating that operation required by STB_CLK_SWITCH_EN write
+ * is completed
+ */
+#define PUSB_PWR_STB_CLK_SWITCH_DONE	BIT(9)
+/* This bit informs if Fast Registers Access is enabled. */
+#define PUSB_PWR_FST_REG_ACCESS_STAT	BIT(30)
+/* Fast Registers Access Enable. */
+#define PUSB_PWR_FST_REG_ACCESS	BIT(31)
+
+/* USB_CAP1- bitmasks */
+/*
+ * SFR Interface type
+ * These field reflects type of SFR interface implemented:
+ * 0x0 - OCP
+ * 0x1 - AHB,
+ * 0x2 - PLB
+ * 0x3 - AXI
+ * 0x4-0xF - reserved
+ */
+#define USB_CAP1_SFR_TYPE_MASK	GENMASK(3, 0)
+#define DEV_SFR_TYPE_OCP(p)	(((p) & USB_CAP1_SFR_TYPE_MASK) == 0x0)
+#define DEV_SFR_TYPE_AHB(p)	(((p) & USB_CAP1_SFR_TYPE_MASK) == 0x1)
+#define DEV_SFR_TYPE_PLB(p)	(((p) & USB_CAP1_SFR_TYPE_MASK) == 0x2)
+#define DEV_SFR_TYPE_AXI(p)	(((p) & USB_CAP1_SFR_TYPE_MASK) == 0x3)
+/*
+ * SFR Interface width
+ * These field reflects width of SFR interface implemented:
+ * 0x0 - 8 bit interface,
+ * 0x1 - 16 bit interface,
+ * 0x2 - 32 bit interface
+ * 0x3 - 64 bit interface
+ * 0x4-0xF - reserved
+ */
+#define USB_CAP1_SFR_WIDTH_MASK	GENMASK(7, 4)
+#define DEV_SFR_WIDTH_8(p)	(((p) & USB_CAP1_SFR_WIDTH_MASK) == (0x0 << 4))
+#define DEV_SFR_WIDTH_16(p)	(((p) & USB_CAP1_SFR_WIDTH_MASK) == (0x1 << 4))
+#define DEV_SFR_WIDTH_32(p)	(((p) & USB_CAP1_SFR_WIDTH_MASK) == (0x2 << 4))
+#define DEV_SFR_WIDTH_64(p)	(((p) & USB_CAP1_SFR_WIDTH_MASK) == (0x3 << 4))
+/*
+ * DMA Interface type
+ * These field reflects type of DMA interface implemented:
+ * 0x0 - OCP
+ * 0x1 - AHB,
+ * 0x2 - PLB
+ * 0x3 - AXI
+ * 0x4-0xF - reserved
+ */
+#define USB_CAP1_DMA_TYPE_MASK	GENMASK(11, 8)
+#define DEV_DMA_TYPE_OCP(p)	(((p) & USB_CAP1_DMA_TYPE_MASK) == (0x0 << 8))
+#define DEV_DMA_TYPE_AHB(p)	(((p) & USB_CAP1_DMA_TYPE_MASK) == (0x1 << 8))
+#define DEV_DMA_TYPE_PLB(p)	(((p) & USB_CAP1_DMA_TYPE_MASK) == (0x2 << 8))
+#define DEV_DMA_TYPE_AXI(p)	(((p) & USB_CAP1_DMA_TYPE_MASK) == (0x3 << 8))
+/*
+ * DMA Interface width
+ * These field reflects width of DMA interface implemented:
+ * 0x0 - reserved,
+ * 0x1 - reserved,
+ * 0x2 - 32 bit interface
+ * 0x3 - 64 bit interface
+ * 0x4-0xF - reserved
+ */
+#define USB_CAP1_DMA_WIDTH_MASK	GENMASK(15, 12)
+#define DEV_DMA_WIDTH_32(p)	(((p) & USB_CAP1_DMA_WIDTH_MASK) == (0x2 << 12))
+#define DEV_DMA_WIDTH_64(p)	(((p) & USB_CAP1_DMA_WIDTH_MASK) == (0x3 << 12))
+/*
+ * USB3 PHY Interface type
+ * These field reflects type of USB3 PHY interface implemented:
+ * 0x0 - USB PIPE,
+ * 0x1 - RMMI,
+ * 0x2-0xF - reserved
+ */
+#define USB_CAP1_U3PHY_TYPE_MASK GENMASK(19, 16)
+#define DEV_U3PHY_PIPE(p) (((p) & USB_CAP1_U3PHY_TYPE_MASK) == (0x0 << 16))
+#define DEV_U3PHY_RMMI(p) (((p) & USB_CAP1_U3PHY_TYPE_MASK) == (0x1 << 16))
+/*
+ * USB3 PHY Interface width
+ * These field reflects width of USB3 PHY interface implemented:
+ * 0x0 - 8 bit PIPE interface,
+ * 0x1 - 16 bit PIPE interface,
+ * 0x2 - 32 bit PIPE interface,
+ * 0x3 - 64 bit PIPE interface
+ * 0x4-0xF - reserved
+ * Note: When SSIC interface is implemented this field shows the width of
+ * internal PIPE interface. The RMMI interface is always 20bit wide.
+ */
+#define USB_CAP1_U3PHY_WIDTH_MASK GENMASK(23, 20)
+#define DEV_U3PHY_WIDTH_8(p) \
+	(((p) & USB_CAP1_U3PHY_WIDTH_MASK) == (0x0 << 20))
+#define DEV_U3PHY_WIDTH_16(p) \
+	(((p) & USB_CAP1_U3PHY_WIDTH_MASK) == (0x1 << 16))
+#define DEV_U3PHY_WIDTH_32(p) \
+	(((p) & USB_CAP1_U3PHY_WIDTH_MASK) == (0x2 << 20))
+#define DEV_U3PHY_WIDTH_64(p) \
+	(((p) & USB_CAP1_U3PHY_WIDTH_MASK) == (0x3 << 16))
+
+/*
+ * USB2 PHY Interface enable
+ * These field informs if USB2 PHY interface is implemented:
+ * 0x0 - interface NOT implemented,
+ * 0x1 - interface implemented
+ */
+#define USB_CAP1_U2PHY_EN(p)	((p) & BIT(24))
+/*
+ * USB2 PHY Interface type
+ * These field reflects type of USB2 PHY interface implemented:
+ * 0x0 - UTMI,
+ * 0x1 - ULPI
+ */
+#define DEV_U2PHY_ULPI(p)	((p) & BIT(25))
+/*
+ * USB2 PHY Interface width
+ * These field reflects width of USB2 PHY interface implemented:
+ * 0x0 - 8 bit interface,
+ * 0x1 - 16 bit interface,
+ * Note: The ULPI interface is always 8bit wide.
+ */
+#define DEV_U2PHY_WIDTH_16(p)	((p) & BIT(26))
+/*
+ * OTG Ready
+ * 0x0 - pure device mode
+ * 0x1 - some features and ports for CDNS USB OTG controller are implemented.
+ */
+#define USB_CAP1_OTG_READY(p)	((p) & BIT(27))
+
+/* USB_CAP2- bitmasks */
+/*
+ * The actual size of the connected On-chip RAM memory in kB:
+ * - 0 means 256 kB (max supported mem size)
+ * - value other than 0 reflects the mem size in kB
+ */
+#define USB_CAP2_ACTUAL_MEM_SIZE(p) ((p) & GENMASK(7, 0))
+/*
+ * Max supported mem size
+ * These field reflects width of on-chip RAM address bus width,
+ * which determines max supported mem size:
+ * 0x0-0x7 - reserved,
+ * 0x8 - support for 4kB mem,
+ * 0x9 - support for 8kB mem,
+ * 0xA - support for 16kB mem,
+ * 0xB - support for 32kB mem,
+ * 0xC - support for 64kB mem,
+ * 0xD - support for 128kB mem,
+ * 0xE - support for 256kB mem,
+ * 0xF - reserved
+ */
+#define USB_CAP2_MAX_MEM_SIZE(p) ((p) & GENMASK(11, 8))
+
+/* USB_CAP3- bitmasks */
+#define EP_IS_IMPLEMENTED(reg, index) ((reg) & (1 << (index)))
+
+/* USB_CAP4- bitmasks */
+#define EP_SUPPORT_ISO(reg, index) ((reg) & (1 << (index)))
+
+/* USB_CAP5- bitmasks */
+#define EP_SUPPORT_STREAM(reg, index) ((reg) & (1 << (index)))
+
+/* USB_CAP6- bitmasks */
+/* The USBSS-DEV Controller  Internal build number. */
+#define GET_DEV_VERSION__INTERNAL_NUMBER(p) ((p) & GENMASK(7, 0))
+/* The USBSS-DEV Controller  version number. */
+#define GET_DEV_VERSION(p) ((p) & GENMASK(31, 8))
+
+/* DBG_LINK1- bitmasks */
+/*
+ * LFPS_MIN_DET_U1_EXIT value This parameter configures the minimum
+ * time required for decoding the received LFPS as an LFPS.U1_Exit.
+ */
+#define DBG_LINK1_LFPS_MIN_DET_U1_EXIT(p)	((p) & GENMASK(7, 0))
+/*
+ * LFPS_MIN_GEN_U1_EXIT value This parameter configures the minimum time for
+ * phytxelecidle deassertion when LFPS.U1_Exit
+ */
+#define DBG_LINK1_LFPS_MIN_GEN_U1_EXIT(p)	(((p) << 8) & GENMASK(15, 8))
+/*
+ * RXDET_BREAK_DIS value This parameter configures terminating the Far-end
+ * Receiver termination detection sequence:
+ * 0: it is possible that USBSS_DEV will terminate Farend receiver
+ *    termination detection sequence
+ * 1: USBSS_DEV will not terminate Far-end receiver termination
+ *    detection sequence
+ */
+#define DBG_LINK1_RXDET_BREAK_DIS		BIT(16)
+/* LFPS_GEN_PING value This parameter configures the LFPS.Ping generation */
+#define DBG_LINK1_LFPS_GEN_PING(p)		(((p) << 17) & GENMASK(21, 17))
+/*
+ * Set the LFPS_MIN_DET_U1_EXIT value Writing '1' to this bit writes the
+ * LFPS_MIN_DET_U1_EXIT field value to the device. This bit is automatically
+ * cleared. Writing '0' has no effect
+ */
+#define DBG_LINK1_LFPS_MIN_DET_U1_EXIT_SET	BIT(24)
+/*
+ * Set the LFPS_MIN_GEN_U1_EXIT value. Writing '1' to this bit writes the
+ * LFPS_MIN_GEN_U1_EXIT field value to the device. This bit is automatically
+ * cleared. Writing '0' has no effect
+ */
+#define DBG_LINK1_LFPS_MIN_GEN_U1_EXIT_SET	BIT(25)
+/*
+ * Set the RXDET_BREAK_DIS value Writing '1' to this bit writes
+ * the RXDET_BREAK_DIS field value to the device. This bit is automatically
+ * cleared. Writing '0' has no effect
+ */
+#define DBG_LINK1_RXDET_BREAK_DIS_SET		BIT(26)
+/*
+ * Set the LFPS_GEN_PING_SET value Writing '1' to this bit writes
+ * the LFPS_GEN_PING field value to the device. This bit is automatically
+ * cleared. Writing '0' has no effect."
+ */
+#define DBG_LINK1_LFPS_GEN_PING_SET		BIT(27)
+
+#define gadget_to_cdns3_device(g) (container_of(g, struct cdns3_device, gadget))
+
+#define ep_to_cdns3_ep(ep) (container_of(ep, struct cdns3_endpoint, endpoint))
+
+/*-------------------------------------------------------------------------*/
+/*
+ * USBSS-DEV DMA interface.
+ */
+#define TRBS_PER_SEGMENT	40
+
+#define ISO_MAX_INTERVAL	10
+
+/*
+ *Only for ISOC endpoints - maximum number of TRBs is calculated as
+ * pow(2, bInterval-1) * number of usb requests. It is limitation made by
+ * driver to save memory. Controller must prepare TRB for each ITP even
+ * if bInterval > 1. It's the reason why driver needs so many TRBs for
+ * isochronous endpoints.
+ */
+#define TRBS_PER_ISOC_SEGMENT	(ISO_MAX_INTERVAL * 8)
+
+#define GET_TRBS_PER_SEGMENT(ep_type) ((ep_type) == USB_ENDPOINT_XFER_ISOC ? \
+				      TRBS_PER_ISOC_SEGMENT : TRBS_PER_SEGMENT)
+/**
+ * struct cdns3_trb - represent Transfer Descriptor block.
+ * @buffer:	pointer to buffer data
+ * @length:	length of data
+ * @control:	control flags.
+ *
+ * This structure describes transfer block serviced by DMA module.
+ */
+struct cdns3_trb {
+	__le32 buffer;
+	__le32 length;
+	__le32 control;
+};
+
+#define TRB_SIZE		(sizeof(struct cdns3_trb))
+#define TRB_RING_SIZE		(TRB_SIZE * TRBS_PER_SEGMENT)
+#define TRB_ISO_RING_SIZE	(TRB_SIZE * TRBS_PER_ISOC_SEGMENT)
+#define TRB_CTRL_RING_SIZE	(TRB_SIZE * 2)
+
+/* TRB bit mask */
+#define TRB_TYPE_BITMASK	GENMASK(15, 10)
+#define TRB_TYPE(p)		((p) << 10)
+#define TRB_FIELD_TO_TYPE(p)	(((p) & TRB_TYPE_BITMASK) >> 10)
+
+/* TRB type IDs */
+/* bulk, interrupt, isoc , and control data stage */
+#define TRB_NORMAL		1
+/* TRB for linking ring segments */
+#define TRB_LINK		6
+
+/* Cycle bit - indicates TRB ownership by driver or hw*/
+#define TRB_CYCLE		BIT(0)
+/*
+ * When set to '1', the device will toggle its interpretation of the Cycle bit
+ */
+#define TRB_TOGGLE		BIT(1)
+
+/* Interrupt on short packet*/
+#define TRB_ISP			BIT(2)
+/*Setting this bit enables FIFO DMA operation mode*/
+#define TRB_FIFO_MODE		BIT(3)
+/* Set PCIe no snoop attribute */
+#define TRB_CHAIN		BIT(4)
+/* Interrupt on completion */
+#define TRB_IOC			BIT(5)
+
+/* stream ID bitmasks. */
+#define TRB_STREAM_ID(p)	((p) & GENMASK(31, 16))
+
+/* transfer_len bitmasks. */
+#define TRB_LEN(p)		((p) & GENMASK(16, 0))
+
+/* transfer_len bitmasks - bits 31:24 */
+#define TRB_BURST_LEN(p)	((p) & GENMASK(31, 24))
+
+/* Data buffer pointer bitmasks*/
+#define TRB_BUFFER(p)		((p) & GENMASK(31, 0))
+
+/*-------------------------------------------------------------------------*/
+/* Driver numeric constants */
+
+/* Such declaration should be added to ch9.h */
+#define USB_DEVICE_MAX_ADDRESS		127
+
+/* Endpoint init values */
+#define CDNS3_EP_MAX_PACKET_LIMIT	1024
+#define CDNS3_EP_MAX_STREAMS		15
+#define CDNS3_EP0_MAX_PACKET_LIMIT	512
+
+/* All endpoints including EP0 */
+#define CDNS3_ENDPOINTS_MAX_COUNT	32
+#define CDNS3_EP_ZLP_BUF_SIZE		1024
+
+#define CDNS3_EP_BUF_SIZE		2	/* KB */
+#define CDNS3_ALIGNED_BUF_SIZE		16384	/* Bytes */
+#define CDNS3_MAX_NUM_DESCMISS_BUF	32
+#define CDNS3_DESCMIS_BUF_SIZE		2048	/* Bytes */
+/*-------------------------------------------------------------------------*/
+/* Used structs */
+
+struct cdns3_device;
+
+/**
+ * struct cdns3_endpoint - extended device side representation of USB endpoint.
+ * @endpoint: usb endpoint
+ * @pending_req_list: list of requests queuing on transfer ring.
+ * @deferred_req_list: list of requests waiting for queuing on transfer ring.
+ * @descmiss_req_list: list of requests internally allocated by driver (WA1).
+ * @trb_pool: transfer ring - array of transaction buffers
+ * @trb_pool_dma: dma address of transfer ring
+ * @cdns3_dev: device associated with this endpoint
+ * @name: a human readable name e.g. ep1out
+ * @flags: specify the current state of endpoint
+ * @descmis_req: internal transfer object used for getting data from on-chip
+ *     buffer. It can happen only if function driver doesn't send usb_request
+ *     object on time.
+ * @aligned_buff: aligned to 8 bytes data buffer. Buffer address used in
+ *     TRB shall be aligned to 8.
+ * @aligned_dma_addr: dma address of aligned_buff
+ * @dir: endpoint direction
+ * @num: endpoint number (1 - 15)
+ * @type: set to bmAttributes & USB_ENDPOINT_XFERTYPE_MASK
+ * @interval: interval between packets used for ISOC endpoint.
+ * @free_trbs: number of free TRBs in transfer ring
+ * @num_trbs: number of all TRBs in transfer ring
+ * @pcs: producer cycle state
+ * @ccs: consumer cycle state
+ * @enqueue: enqueue index in transfer ring
+ * @dequeue: dequeue index in transfer ring
+ */
+struct cdns3_endpoint {
+	struct usb_ep		endpoint;
+	struct list_head	pending_req_list;
+	struct list_head	deferred_req_list;
+	struct list_head	descmiss_req_list;
+
+	struct cdns3_trb	*trb_pool;
+	dma_addr_t		trb_pool_dma;
+
+	struct cdns3_device	*cdns3_dev;
+	char			name[20];
+
+#define EP_ENABLED		BIT(0)
+#define EP_STALL		BIT(1)
+#define EP_WEDGE		BIT(2)
+#define EP_TRANSFER_STARTED	BIT(3)
+#define EP_UPDATE_EP_TRBADDR	BIT(4)
+#define EP_PENDING_REQUEST	BIT(5)
+#define EP_RING_FULL		BIT(6)
+#define EP_CLAIMED		BIT(7)
+#define EP_QUIRK_EXTRA_BUF_DET	BIT(8)
+#define EP_QUIRK_EXTRA_BUF_EN	BIT(9)
+#define EP_QUIRK_END_TRANSFER	BIT(10)
+
+	u32			flags;
+
+	struct cdns3_request	*descmis_req;
+
+	void			*aligned_buff;
+	dma_addr_t		aligned_dma_addr;
+	u8			dir;
+	u8			num;
+	u8			type;
+	int			interval;
+
+	int			free_trbs;
+	int			num_trbs;
+	u8			pcs;
+	u8			ccs;
+	int			enqueue;
+	int			dequeue;
+};
+
+/**
+ * struct cdns3_request - extended device side representation of usb_request
+ *                        object .
+ * @request: generic usb_request object describing single I/O request.
+ * @priv_ep: extended representation of usb_ep object
+ * @trb: the first TRB association with this request
+ * @start_trb: number of the first TRB in transfer ring
+ * @end_trb: number of the last TRB in transfer ring
+ * @flags: flag specifying special usage of request
+ * @list: used by internally allocated request to add to descmiss_req_list.
+ */
+struct cdns3_request {
+	struct usb_request	request;
+	struct cdns3_endpoint	*priv_ep;
+	struct cdns3_trb	*trb;
+	int			start_trb;
+	int			end_trb;
+#define REQUEST_PENDING		BIT(0)
+#define REQUEST_INTERNAL	BIT(1)
+#define REQUEST_INTERNAL_CH	BIT(2)
+#define REQUEST_ZLP		BIT(3)
+	u32			flags;
+	struct list_head	list;
+};
+
+#define to_cdns3_request(r) (container_of(r, struct cdns3_request, request))
+
+/*Stages used during enumeration process.*/
+#define CDNS3_SETUP_STAGE		0x0
+#define CDNS3_DATA_STAGE		0x1
+#define CDNS3_STATUS_STAGE		0x2
+
+/**
+ * struct cdns3_device - represent USB device.
+ * @dev: pointer to device structure associated whit this controller
+ * @sysdev: pointer to the DMA capable device
+ * @gadget: device side representation of the peripheral controller
+ * @gadget_driver: pointer to the gadget driver
+ * @dev_ver: device controller version.
+ * @lock: for synchronizing
+ * @regs: base address for device side registers
+ * @setup_buf: used while processing usb control requests
+ * @setup_dma: dma address for setup_buf
+ * @zlp_buf - zlp buffer
+ * @ep0_stage: ep0 stage during enumeration process.
+ * @ep0_data_dir: direction for control transfer
+ * @eps: array of pointers to all endpoints with exclusion ep0
+ * @selected_ep: actually selected endpoint. It's used only to improve
+ *               performance.
+ * @isoch_delay: value from Set Isoch Delay request. Only valid on SS/SSP.
+ * @u1_allowed: allow device transition to u1 state
+ * @u2_allowed: allow device transition to u2 state
+ * @is_selfpowered: device is self powered
+ * @setup_pending: setup packet is processing by gadget driver
+ * @hw_configured_flag: hardware endpoint configuration was set.
+ * @wake_up_flag: allow device to remote up the host
+ * @status_completion_no_call: indicate that driver is waiting for status s
+ *     stage completion. It's used in deferred SET_CONFIGURATION request.
+ * @onchip_mem_allocated_size: actual size of on-chip memory assigned
+ *     to endpoints
+ * @pending_status_wq: workqueue handling status stage for deferred requests.
+ * @pending_status_request: request for which status stage was deferred
+ */
+struct cdns3_device {
+	struct device			*dev;
+	struct device			*sysdev;
+
+	struct usb_gadget		gadget;
+	struct usb_gadget_driver	*gadget_driver;
+
+#define CDNS_REVISION_V0		0x00024501
+#define CDNS_REVISION_V1		0x00024509
+	u32				dev_ver;
+
+	/* generic spin-lock for drivers */
+	spinlock_t			lock;
+
+	struct cdns3_usb_regs		__iomem *regs;
+
+	struct usb_ctrlrequest		*setup_buf;
+	dma_addr_t			setup_dma;
+	void				*zlp_buf;
+
+	u8				ep0_stage;
+	int				ep0_data_dir;
+
+	struct cdns3_endpoint		*eps[CDNS3_ENDPOINTS_MAX_COUNT];
+
+	u32				selected_ep;
+	u16				isoch_delay;
+
+	unsigned			u1_allowed:1;
+	unsigned			u2_allowed:1;
+	unsigned			is_selfpowered:1;
+	unsigned			setup_pending:1;
+	int				hw_configured_flag:1;
+	int				wake_up_flag:1;
+	unsigned			status_completion_no_call:1;
+
+	struct work_struct		pending_status_wq;
+	struct usb_request		*pending_status_request;
+
+	/*in KB */
+	int				onchip_mem_allocated_size;
+};
+
+int cdns3_handshake(void __iomem *ptr, u32 mask, u32 done, int usec);
+void cdns3_set_register_bit(void __iomem *ptr, u32 mask);
+dma_addr_t cdns3_trb_virt_to_dma(struct cdns3_endpoint *priv_ep,
+				 struct cdns3_trb *trb);
+enum usb_device_speed cdns3_get_speed(struct cdns3_device *priv_dev);
+void cdns3_pending_setup_status_handler(struct work_struct *work);
+void cdns3_hw_reset_eps_config(struct cdns3_device *priv_dev);
+void cdns3_set_hw_configuration(struct cdns3_device *priv_dev);
+void cdns3_select_ep(struct cdns3_device *priv_dev, u32 ep);
+void cdns3_allow_enable_l1(struct cdns3_device *priv_dev, int enable);
+struct usb_request *cdns3_next_request(struct list_head *list);
+int cdns3_ep_run_transfer(struct cdns3_endpoint *priv_ep,
+			  struct usb_request *request);
+int cdns3_allocate_trb_pool(struct cdns3_endpoint *priv_ep);
+u8 cdns3_ep_addr_to_index(u8 ep_addr);
+int cdns3_gadget_ep_set_wedge(struct usb_ep *ep);
+int cdns3_gadget_ep_set_halt(struct usb_ep *ep, int value);
+struct usb_request *cdns3_gadget_ep_alloc_request(struct usb_ep *ep,
+						  gfp_t gfp_flags);
+void cdns3_gadget_ep_free_request(struct usb_ep *ep,
+				  struct usb_request *request);
+int cdns3_gadget_ep_dequeue(struct usb_ep *ep, struct usb_request *request);
+void cdns3_gadget_giveback(struct cdns3_endpoint *priv_ep,
+			   struct cdns3_request *priv_req,
+			   int status);
+
+int cdns3_init_ep0(struct cdns3_device *priv_dev,
+		   struct cdns3_endpoint *priv_ep);
+void cdns3_ep0_config(struct cdns3_device *priv_dev);
+void cdns3_ep_config(struct cdns3_endpoint *priv_ep);
+void cdns3_check_ep0_interrupt_proceed(struct cdns3_device *priv_dev, int dir);
+
+#endif /* __LINUX_CDNS3_GADGET */
diff --git a/drivers/usb/cdns3/host-export.h b/drivers/usb/cdns3/host-export.h
new file mode 100644
index 000000000000..b498a170b7e8
--- /dev/null
+++ b/drivers/usb/cdns3/host-export.h
@@ -0,0 +1,28 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Cadence USBSS DRD Driver - Host Export APIs
+ *
+ * Copyright (C) 2017-2018 NXP
+ *
+ * Authors: Peter Chen <peter.chen@nxp.com>
+ */
+#ifndef __LINUX_CDNS3_HOST_EXPORT
+#define __LINUX_CDNS3_HOST_EXPORT
+
+#ifdef CONFIG_USB_CDNS3_HOST
+
+int cdns3_host_init(struct cdns3 *cdns);
+void cdns3_host_exit(struct cdns3 *cdns);
+
+#else
+
+static inline int cdns3_host_init(struct cdns3 *cdns)
+{
+	return -ENXIO;
+}
+
+static inline void cdns3_host_exit(struct cdns3 *cdns) { }
+
+#endif /* CONFIG_USB_CDNS3_HOST */
+
+#endif /* __LINUX_CDNS3_HOST_EXPORT */
diff --git a/drivers/usb/cdns3/host.c b/drivers/usb/cdns3/host.c
new file mode 100644
index 000000000000..b43b0236a885
--- /dev/null
+++ b/drivers/usb/cdns3/host.c
@@ -0,0 +1,72 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Cadence USBSS DRD Driver - host side
+ *
+ * Copyright (C) 2018 Cadence Design Systems.
+ * Copyright (C) 2017-2018 NXP
+ *
+ * Authors: Peter Chen <peter.chen@nxp.com>
+ *	    Pawel Laszczak <pawell@cadence.com>
+ */
+
+#include <linux/platform_device.h>
+#include "core.h"
+
+static int __cdns3_host_init(struct cdns3 *cdns)
+{
+	struct platform_device *xhci;
+	int ret;
+
+	xhci = platform_device_alloc("xhci-hcd", PLATFORM_DEVID_AUTO);
+	if (!xhci) {
+		dev_err(cdns->dev, "couldn't allocate xHCI device\n");
+		return -ENOMEM;
+	}
+
+	xhci->dev.parent = cdns->dev;
+	cdns->host_dev = xhci;
+
+	ret = platform_device_add_resources(xhci, cdns->xhci_res,
+					    CDNS3_XHCI_RESOURCES_NUM);
+	if (ret) {
+		dev_err(cdns->dev, "couldn't add resources to xHCI device\n");
+		goto err1;
+	}
+
+	ret = platform_device_add(xhci);
+	if (ret) {
+		dev_err(cdns->dev, "failed to register xHCI device\n");
+		goto err1;
+	}
+
+	return 0;
+err1:
+	platform_device_put(xhci);
+	return ret;
+}
+
+static void cdns3_host_exit(struct cdns3 *cdns)
+{
+	platform_device_unregister(cdns->host_dev);
+	cdns->host_dev = NULL;
+}
+
+int cdns3_host_init(struct cdns3 *cdns)
+{
+	struct cdns3_role_driver *rdrv;
+
+	rdrv = devm_kzalloc(cdns->dev, sizeof(*rdrv), GFP_KERNEL);
+	if (!rdrv)
+		return -ENOMEM;
+
+	rdrv->start	= __cdns3_host_init;
+	rdrv->stop	= cdns3_host_exit;
+	rdrv->state	= CDNS3_ROLE_STATE_INACTIVE;
+	rdrv->suspend	= NULL;
+	rdrv->resume	= NULL;
+	rdrv->name	= "host";
+
+	cdns->roles[CDNS3_ROLE_HOST] = rdrv;
+
+	return 0;
+}
diff --git a/drivers/usb/cdns3/trace.c b/drivers/usb/cdns3/trace.c
new file mode 100644
index 000000000000..587ae08e019d
--- /dev/null
+++ b/drivers/usb/cdns3/trace.c
@@ -0,0 +1,11 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * USBSS device controller driver Trace Support
+ *
+ * Copyright (C) 2018 Cadence.
+ *
+ * Author: Pawel Laszczak <pawell@cadence.com>
+ */
+
+#define CREATE_TRACE_POINTS
+#include "trace.h"
diff --git a/drivers/usb/cdns3/trace.h b/drivers/usb/cdns3/trace.h
new file mode 100644
index 000000000000..f3d7a91fae86
--- /dev/null
+++ b/drivers/usb/cdns3/trace.h
@@ -0,0 +1,389 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * USBSS device controller driver.
+ * Trace support header file.
+ *
+ * Copyright (C) 2018 Cadence.
+ *
+ * Author: Pawel Laszczak <pawell@cadence.com>
+ */
+
+#undef TRACE_SYSTEM
+#define TRACE_SYSTEM cdns3
+
+#if !defined(__LINUX_CDNS3_TRACE) || defined(TRACE_HEADER_MULTI_READ)
+#define __LINUX_CDNS3_TRACE
+
+#include <linux/types.h>
+#include <linux/tracepoint.h>
+#include <asm/byteorder.h>
+#include <linux/usb/ch9.h>
+#include "core.h"
+#include "gadget.h"
+#include "debug.h"
+
+#define CDNS3_MSG_MAX	500
+
+DECLARE_EVENT_CLASS(cdns3_log_doorbell,
+	TP_PROTO(const char *ep_name, u32 ep_trbaddr),
+	TP_ARGS(ep_name, ep_trbaddr),
+	TP_STRUCT__entry(
+		__string(name, ep_name)
+		__field(u32, ep_trbaddr)
+	),
+	TP_fast_assign(
+		__assign_str(name, ep_name);
+		__entry->ep_trbaddr = ep_trbaddr;
+	),
+	TP_printk("//Ding Dong %s, ep_trbaddr %08x", __get_str(name),
+		  __entry->ep_trbaddr)
+);
+
+DEFINE_EVENT(cdns3_log_doorbell, cdns3_doorbell_ep0,
+	TP_PROTO(const char *ep_name, u32 ep_trbaddr),
+	TP_ARGS(ep_name, ep_trbaddr)
+);
+
+DEFINE_EVENT(cdns3_log_doorbell, cdns3_doorbell_epx,
+	TP_PROTO(const char *ep_name, u32 ep_trbaddr),
+	TP_ARGS(ep_name, ep_trbaddr)
+);
+
+DECLARE_EVENT_CLASS(cdns3_log_usb_irq,
+	TP_PROTO(struct cdns3_device *priv_dev, u32 usb_ists),
+	TP_ARGS(priv_dev, usb_ists),
+	TP_STRUCT__entry(
+		__field(enum usb_device_speed, speed)
+		__field(u32, usb_ists)
+		__dynamic_array(char, str, CDNS3_MSG_MAX)
+	),
+	TP_fast_assign(
+		__entry->speed = cdns3_get_speed(priv_dev);
+		__entry->usb_ists = usb_ists;
+	),
+	TP_printk("%s", cdns3_decode_usb_irq(__get_str(str), __entry->speed,
+					     __entry->usb_ists))
+);
+
+DEFINE_EVENT(cdns3_log_usb_irq, cdns3_usb_irq,
+	TP_PROTO(struct cdns3_device *priv_dev, u32 usb_ists),
+	TP_ARGS(priv_dev, usb_ists)
+);
+
+DECLARE_EVENT_CLASS(cdns3_log_epx_irq,
+	TP_PROTO(struct cdns3_device *priv_dev, struct cdns3_endpoint *priv_ep),
+	TP_ARGS(priv_dev, priv_ep),
+	TP_STRUCT__entry(
+		__string(ep_name, priv_ep->name)
+		__field(u32, ep_sts)
+		__field(u32, ep_traddr)
+		__dynamic_array(char, str, CDNS3_MSG_MAX)
+	),
+	TP_fast_assign(
+		__assign_str(ep_name, priv_ep->name);
+		__entry->ep_sts = readl(&priv_dev->regs->ep_sts);
+		__entry->ep_traddr = readl(&priv_dev->regs->ep_traddr);
+	),
+	TP_printk("%s, ep_traddr: %08x",
+		  cdns3_decode_epx_irq(__get_str(str),
+				       __get_str(ep_name),
+				       __entry->ep_sts),
+		  __entry->ep_traddr)
+);
+
+DEFINE_EVENT(cdns3_log_epx_irq, cdns3_epx_irq,
+	TP_PROTO(struct cdns3_device *priv_dev, struct cdns3_endpoint *priv_ep),
+	TP_ARGS(priv_dev, priv_ep)
+);
+
+DECLARE_EVENT_CLASS(cdns3_log_ep0_irq,
+	TP_PROTO(struct cdns3_device *priv_dev,  u32 ep_sts),
+	TP_ARGS(priv_dev, ep_sts),
+	TP_STRUCT__entry(
+		__field(int, ep_dir)
+		__field(u32, ep_sts)
+		__dynamic_array(char, str, CDNS3_MSG_MAX)
+	),
+	TP_fast_assign(
+		__entry->ep_dir = priv_dev->ep0_data_dir;
+		__entry->ep_sts = ep_sts;
+	),
+	TP_printk("%s", cdns3_decode_ep0_irq(__get_str(str),
+					     __entry->ep_dir,
+					     __entry->ep_sts))
+);
+
+DEFINE_EVENT(cdns3_log_ep0_irq, cdns3_ep0_irq,
+	TP_PROTO(struct cdns3_device *priv_dev, u32 ep_sts),
+	TP_ARGS(priv_dev, ep_sts)
+);
+
+DECLARE_EVENT_CLASS(cdns3_log_ctrl,
+	TP_PROTO(struct usb_ctrlrequest *ctrl),
+	TP_ARGS(ctrl),
+	TP_STRUCT__entry(
+		__field(u8, bRequestType)
+		__field(u8, bRequest)
+		__field(u16, wValue)
+		__field(u16, wIndex)
+		__field(u16, wLength)
+		__dynamic_array(char, str, CDNS3_MSG_MAX)
+	),
+	TP_fast_assign(
+		__entry->bRequestType = ctrl->bRequestType;
+		__entry->bRequest = ctrl->bRequest;
+		__entry->wValue = le16_to_cpu(ctrl->wValue);
+		__entry->wIndex = le16_to_cpu(ctrl->wIndex);
+		__entry->wLength = le16_to_cpu(ctrl->wLength);
+	),
+	TP_printk("%s", usb_decode_ctrl(__get_str(str), __entry->bRequestType,
+					__entry->bRequest, __entry->wValue,
+					__entry->wIndex, __entry->wLength)
+	)
+);
+
+DEFINE_EVENT(cdns3_log_ctrl, cdns3_ctrl_req,
+	TP_PROTO(struct usb_ctrlrequest *ctrl),
+	TP_ARGS(ctrl)
+);
+
+DECLARE_EVENT_CLASS(cdns3_log_request,
+	TP_PROTO(struct cdns3_request *req),
+	TP_ARGS(req),
+	TP_STRUCT__entry(
+		__string(name, req->priv_ep->name)
+		__field(struct cdns3_request *, req)
+		__field(unsigned int, actual)
+		__field(unsigned int, length)
+		__field(int, status)
+		__field(int, zero)
+		__field(int, short_not_ok)
+		__field(int, no_interrupt)
+		__field(int, start_trb)
+		__field(int, end_trb)
+		__field(struct cdns3_trb *, start_trb_addr)
+		__field(int, flags)
+	),
+	TP_fast_assign(
+		__assign_str(name, req->priv_ep->name);
+		__entry->req = req;
+		__entry->actual = req->request.actual;
+		__entry->length = req->request.length;
+		__entry->status = req->request.status;
+		__entry->zero = req->request.zero;
+		__entry->short_not_ok = req->request.short_not_ok;
+		__entry->no_interrupt = req->request.no_interrupt;
+		__entry->start_trb = req->start_trb;
+		__entry->end_trb = req->end_trb;
+		__entry->start_trb_addr = req->trb;
+		__entry->flags = req->flags;
+	),
+	TP_printk("%s: req: %p, length: %u/%u %s%s%s, status: %d,"
+		  " trb: [start:%d, end:%d: virt addr %pa], flags:%x ",
+		__get_str(name), __entry->req, __entry->actual, __entry->length,
+		__entry->zero ? "zero | " : "",
+		__entry->short_not_ok ? "short | " : "",
+		__entry->no_interrupt ? "no int" : "",
+		__entry->status,
+		__entry->start_trb,
+		__entry->end_trb,
+		__entry->start_trb_addr,
+		__entry->flags
+	)
+);
+
+DEFINE_EVENT(cdns3_log_request, cdns3_alloc_request,
+	TP_PROTO(struct cdns3_request *req),
+	TP_ARGS(req)
+);
+
+DEFINE_EVENT(cdns3_log_request, cdns3_free_request,
+	TP_PROTO(struct cdns3_request *req),
+	TP_ARGS(req)
+);
+
+DEFINE_EVENT(cdns3_log_request, cdns3_ep_queue,
+	TP_PROTO(struct cdns3_request *req),
+	TP_ARGS(req)
+);
+
+DEFINE_EVENT(cdns3_log_request, cdns3_ep_dequeue,
+	TP_PROTO(struct cdns3_request *req),
+	TP_ARGS(req)
+);
+
+DEFINE_EVENT(cdns3_log_request, cdns3_gadget_giveback,
+	TP_PROTO(struct cdns3_request *req),
+	TP_ARGS(req)
+);
+
+DECLARE_EVENT_CLASS(cdns3_log_trb,
+	TP_PROTO(struct cdns3_endpoint *priv_ep, struct cdns3_trb *trb),
+	TP_ARGS(priv_ep, trb),
+	TP_STRUCT__entry(
+		__string(name, priv_ep->name)
+		__field(struct cdns3_trb *, trb)
+		__field(u32, buffer)
+		__field(u32, length)
+		__field(u32, control)
+		__field(u32, type)
+	),
+	TP_fast_assign(
+		__assign_str(name, priv_ep->name);
+		__entry->trb = trb;
+		__entry->buffer = trb->buffer;
+		__entry->length = trb->length;
+		__entry->control = trb->control;
+		__entry->type = usb_endpoint_type(priv_ep->endpoint.desc);
+	),
+	TP_printk("%s: trb 0x%pa, dma buf: 0x%08x, size: %ld, ctrl: 0x%08x (%s%s%s%s%s%s%s)",
+		__get_str(name), __entry->trb, __entry->buffer,
+		TRB_LEN(__entry->length), __entry->control,
+		__entry->control & TRB_CYCLE ? "C=1, " : "C=0, ",
+		__entry->control & TRB_TOGGLE ? "T=1, " : "T=0, ",
+		__entry->control & TRB_ISP ? "ISP, " : "",
+		__entry->control & TRB_FIFO_MODE ? "FIFO, " : "",
+		__entry->control & TRB_CHAIN ? "CHAIN, " : "",
+		__entry->control & TRB_IOC ? "IOC, " : "",
+		TRB_FIELD_TO_TYPE(__entry->control) == TRB_NORMAL ? "Normal" : "LINK"
+	)
+);
+
+DEFINE_EVENT(cdns3_log_trb, cdns3_prepare_trb,
+	TP_PROTO(struct cdns3_endpoint *priv_ep, struct cdns3_trb *trb),
+	TP_ARGS(priv_ep, trb)
+);
+
+DEFINE_EVENT(cdns3_log_trb, cdns3_complete_trb,
+	TP_PROTO(struct cdns3_endpoint *priv_ep, struct cdns3_trb *trb),
+	TP_ARGS(priv_ep, trb)
+);
+
+DECLARE_EVENT_CLASS(cdns3_log_ring,
+	TP_PROTO(struct cdns3_endpoint *priv_ep),
+	TP_ARGS(priv_ep),
+	TP_STRUCT__entry(
+		__dynamic_array(u8, ring, TRB_RING_SIZE)
+		__dynamic_array(u8, priv_ep, sizeof(struct cdns3_endpoint))
+		__dynamic_array(char, buffer,
+				(TRBS_PER_SEGMENT * 65) + CDNS3_MSG_MAX)
+	),
+	TP_fast_assign(
+		memcpy(__get_dynamic_array(priv_ep), priv_ep,
+		       sizeof(struct cdns3_endpoint));
+		memcpy(__get_dynamic_array(ring), priv_ep->trb_pool,
+		       TRB_RING_SIZE);
+	),
+
+	TP_printk("%s",
+		  cdns3_dbg_ring((struct cdns3_endpoint *)__get_str(priv_ep),
+				 (struct cdns3_trb *)__get_str(ring),
+				 __get_str(buffer)))
+);
+
+DEFINE_EVENT(cdns3_log_ring, cdns3_ring,
+	TP_PROTO(struct cdns3_endpoint *priv_ep),
+	TP_ARGS(priv_ep)
+);
+
+DECLARE_EVENT_CLASS(cdns3_log_ep,
+	TP_PROTO(struct cdns3_endpoint *priv_ep),
+	TP_ARGS(priv_ep),
+	TP_STRUCT__entry(
+		__string(name, priv_ep->name)
+		__field(unsigned int, maxpacket)
+		__field(unsigned int, maxpacket_limit)
+		__field(unsigned int, max_streams)
+		__field(unsigned int, maxburst)
+		__field(unsigned int, flags)
+		__field(unsigned int, dir)
+		__field(u8, enqueue)
+		__field(u8, dequeue)
+	),
+	TP_fast_assign(
+		__assign_str(name, priv_ep->name);
+		__entry->maxpacket = priv_ep->endpoint.maxpacket;
+		__entry->maxpacket_limit = priv_ep->endpoint.maxpacket_limit;
+		__entry->max_streams = priv_ep->endpoint.max_streams;
+		__entry->maxburst = priv_ep->endpoint.maxburst;
+		__entry->flags = priv_ep->flags;
+		__entry->dir = priv_ep->dir;
+		__entry->enqueue = priv_ep->enqueue;
+		__entry->dequeue = priv_ep->dequeue;
+	),
+	TP_printk("%s: mps: %d/%d. streams: %d, burst: %d, enq idx: %d, "
+		  "deq idx: %d, flags %s%s%s%s%s%s%s%s, dir: %s",
+		__get_str(name), __entry->maxpacket,
+		__entry->maxpacket_limit, __entry->max_streams,
+		__entry->maxburst, __entry->enqueue,
+		__entry->dequeue,
+		__entry->flags & EP_ENABLED ? "EN | " : "",
+		__entry->flags & EP_STALL ? "STALL | " : "",
+		__entry->flags & EP_WEDGE ? "WEDGE | " : "",
+		__entry->flags & EP_TRANSFER_STARTED ? "STARTED | " : "",
+		__entry->flags & EP_UPDATE_EP_TRBADDR ? "UPD TRB | " : "",
+		__entry->flags & EP_PENDING_REQUEST ? "REQ PEN | " : "",
+		__entry->flags & EP_RING_FULL ? "RING FULL |" : "",
+		__entry->flags & EP_CLAIMED ?  "CLAIMED " : "",
+		__entry->dir ? "IN" : "OUT"
+	)
+);
+
+DEFINE_EVENT(cdns3_log_ep, cdns3_gadget_ep_enable,
+	TP_PROTO(struct cdns3_endpoint *priv_ep),
+	TP_ARGS(priv_ep)
+);
+
+DEFINE_EVENT(cdns3_log_ep, cdns3_gadget_ep_disable,
+	TP_PROTO(struct cdns3_endpoint *priv_ep),
+	TP_ARGS(priv_ep)
+);
+
+DECLARE_EVENT_CLASS(cdns3_log_request_handled,
+	TP_PROTO(struct cdns3_request *priv_req, int current_index,
+		 int handled),
+	TP_ARGS(priv_req, current_index, handled),
+	TP_STRUCT__entry(
+		__field(struct cdns3_request *, priv_req)
+		__field(unsigned int, dma_position)
+		__field(unsigned int, handled)
+		__field(unsigned int, dequeue_idx)
+		__field(unsigned int, enqueue_idx)
+		__field(unsigned int, start_trb)
+		__field(unsigned int, end_trb)
+	),
+	TP_fast_assign(
+		__entry->priv_req = priv_req;
+		__entry->dma_position = current_index;
+		__entry->handled = handled;
+		__entry->dequeue_idx = priv_req->priv_ep->dequeue;
+		__entry->enqueue_idx = priv_req->priv_ep->enqueue;
+		__entry->start_trb = priv_req->start_trb;
+		__entry->end_trb = priv_req->end_trb;
+	),
+	TP_printk("Req: %p %s, DMA pos: %d, ep deq: %d, ep enq: %d,"
+		  " start trb: %d, end trb: %d",
+		__entry->priv_req,
+		__entry->handled ? "handled" : "not handled",
+		__entry->dma_position, __entry->dequeue_idx,
+		__entry->enqueue_idx, __entry->start_trb,
+		__entry->end_trb
+	)
+);
+
+DEFINE_EVENT(cdns3_log_request_handled, cdns3_request_handled,
+	TP_PROTO(struct cdns3_request *priv_req, int current_index,
+		 int handled),
+	TP_ARGS(priv_req, current_index, handled)
+);
+#endif /* __LINUX_CDNS3_TRACE */
+
+/* this part must be outside header guard */
+
+#undef TRACE_INCLUDE_PATH
+#define TRACE_INCLUDE_PATH .
+
+#undef TRACE_INCLUDE_FILE
+#define TRACE_INCLUDE_FILE trace
+
+#include <trace/define_trace.h>
-- 
2.17.1

^ permalink raw reply related	[flat|nested] 49+ messages in thread

* [v2,5/5] usb:cdns3 Add Cadence USB3 DRD Driver
@ 2018-12-23 15:13   ` Pawel Laszczak
  0 siblings, 0 replies; 49+ messages in thread
From: Pawel Laszczak @ 2018-12-23 15:13 UTC (permalink / raw)
  To: devicetree
  Cc: gregkh, mark.rutland, linux-usb, hdegoede, heikki.krogerus,
	andy.shevchenko, robh+dt, rogerq, linux-kernel, adouglas,
	jbergsagel, nsekhar, nm, sureshp, peter.chen, pjez, kurahul,
	Pawel Laszczak

This patch introduce new Cadence USBSS DRD driver
to linux kernel.

The Cadence USBSS DRD Driver is a highly
configurable IP Core which can be
instantiated as Dual-Role Device (DRD),
Peripheral Only and Host Only (XHCI)
configurations.

The current driver has been validated with
FPGA burned. We have support for PCIe
bus, which is used on FPGA prototyping.

The host side of USBSS-DRD controller is compliance
with XHCI specification, so it works with
standard XHCI linux driver.

Signed-off-by: Pawel Laszczak <pawell@cadence.com>
---
 drivers/usb/Kconfig                |    2 +
 drivers/usb/Makefile               |    2 +
 drivers/usb/cdns3/Kconfig          |   44 +
 drivers/usb/cdns3/Makefile         |   16 +
 drivers/usb/cdns3/cdns3-pci-wrap.c |  157 +++
 drivers/usb/cdns3/core.c           |  406 ++++++
 drivers/usb/cdns3/core.h           |  116 ++
 drivers/usb/cdns3/debug.h          |  166 +++
 drivers/usb/cdns3/debugfs.c        |  168 +++
 drivers/usb/cdns3/drd.c            |  350 +++++
 drivers/usb/cdns3/drd.h            |  162 +++
 drivers/usb/cdns3/ep0.c            |  896 ++++++++++++
 drivers/usb/cdns3/gadget-export.h  |   28 +
 drivers/usb/cdns3/gadget.c         | 2102 ++++++++++++++++++++++++++++
 drivers/usb/cdns3/gadget.h         | 1206 ++++++++++++++++
 drivers/usb/cdns3/host-export.h    |   28 +
 drivers/usb/cdns3/host.c           |   72 +
 drivers/usb/cdns3/trace.c          |   11 +
 drivers/usb/cdns3/trace.h          |  389 +++++
 19 files changed, 6321 insertions(+)
 create mode 100644 drivers/usb/cdns3/Kconfig
 create mode 100644 drivers/usb/cdns3/Makefile
 create mode 100644 drivers/usb/cdns3/cdns3-pci-wrap.c
 create mode 100644 drivers/usb/cdns3/core.c
 create mode 100644 drivers/usb/cdns3/core.h
 create mode 100644 drivers/usb/cdns3/debug.h
 create mode 100644 drivers/usb/cdns3/debugfs.c
 create mode 100644 drivers/usb/cdns3/drd.c
 create mode 100644 drivers/usb/cdns3/drd.h
 create mode 100644 drivers/usb/cdns3/ep0.c
 create mode 100644 drivers/usb/cdns3/gadget-export.h
 create mode 100644 drivers/usb/cdns3/gadget.c
 create mode 100644 drivers/usb/cdns3/gadget.h
 create mode 100644 drivers/usb/cdns3/host-export.h
 create mode 100644 drivers/usb/cdns3/host.c
 create mode 100644 drivers/usb/cdns3/trace.c
 create mode 100644 drivers/usb/cdns3/trace.h

diff --git a/drivers/usb/Kconfig b/drivers/usb/Kconfig
index 987fc5ba6321..5f9334019d04 100644
--- a/drivers/usb/Kconfig
+++ b/drivers/usb/Kconfig
@@ -112,6 +112,8 @@ source "drivers/usb/usbip/Kconfig"
 
 endif
 
+source "drivers/usb/cdns3/Kconfig"
+
 source "drivers/usb/mtu3/Kconfig"
 
 source "drivers/usb/musb/Kconfig"
diff --git a/drivers/usb/Makefile b/drivers/usb/Makefile
index 7d1b8c82b208..ab125b966cac 100644
--- a/drivers/usb/Makefile
+++ b/drivers/usb/Makefile
@@ -12,6 +12,8 @@ obj-$(CONFIG_USB_DWC3)		+= dwc3/
 obj-$(CONFIG_USB_DWC2)		+= dwc2/
 obj-$(CONFIG_USB_ISP1760)	+= isp1760/
 
+obj-$(CONFIG_USB_CDNS3)		+= cdns3/
+
 obj-$(CONFIG_USB_MON)		+= mon/
 obj-$(CONFIG_USB_MTU3)		+= mtu3/
 
diff --git a/drivers/usb/cdns3/Kconfig b/drivers/usb/cdns3/Kconfig
new file mode 100644
index 000000000000..4adfd87811e8
--- /dev/null
+++ b/drivers/usb/cdns3/Kconfig
@@ -0,0 +1,44 @@
+config USB_CDNS3
+	tristate "Cadence USB3 Dual-Role Controller"
+	depends on USB_SUPPORT && (USB || USB_GADGET) && HAS_DMA
+	help
+	  Say Y here if your system has a cadence USB3 dual-role controller.
+	  It supports: dual-role switch, Host-only, and Peripheral-only.
+
+	  If you choose to build this driver is a dynamically linked
+	  module, the module will be called cdns3.ko.
+
+if USB_CDNS3
+
+config USB_CDNS3_GADGET
+        bool "Cadence USB3 device controller"
+        depends on USB_GADGET
+        help
+          Say Y here to enable device controller functionality of the
+          cadence USBSS-DEV driver.
+
+          This controller supports FF, HS and SS mode. It doesn't support
+          LS and SSP mode
+
+config USB_CDNS3_HOST
+        bool "Cadence USB3 host controller"
+        depends on USB_XHCI_HCD
+        help
+          Say Y here to enable host controller functionality of the
+          cadence driver.
+
+          Host controller is compliance with XHCI so it will use
+          standard XHCI driver.
+
+config USB_CDNS3_PCI_WRAP
+	tristate "Cadence USB3 support on PCIe-based platforms"
+	depends on USB_PCI && ACPI
+	default USB_CDNS3
+	help
+	  If you're using the USBSS Core IP with a PCIe, please say
+	  'Y' or 'M' here.
+
+	  If you choose to build this driver as module it will
+	  be dynamically linked and module will be called cdns3-pci.ko
+
+endif
diff --git a/drivers/usb/cdns3/Makefile b/drivers/usb/cdns3/Makefile
new file mode 100644
index 000000000000..3f63baa24294
--- /dev/null
+++ b/drivers/usb/cdns3/Makefile
@@ -0,0 +1,16 @@
+# SPDX-License-Identifier: GPL-2.0
+# define_trace.h needs to know how to find our header
+CFLAGS_trace.o				:= -I$(src)
+
+obj-$(CONFIG_USB_CDNS3)			+= cdns3.o
+obj-$(CONFIG_USB_CDNS3_PCI_WRAP)	+= cdns3-pci.o
+
+cdns3-y					:= core.o drd.o trace.o
+
+ifneq ($(CONFIG_DEBUG_FS),)
+	cdns3-y				+= debugfs.o
+endif
+
+cdns3-$(CONFIG_USB_CDNS3_GADGET)	+= gadget.o ep0.o
+cdns3-$(CONFIG_USB_CDNS3_HOST)		+= host.o
+cdns3-pci-y		 		:= cdns3-pci-wrap.o
diff --git a/drivers/usb/cdns3/cdns3-pci-wrap.c b/drivers/usb/cdns3/cdns3-pci-wrap.c
new file mode 100644
index 000000000000..e93179c45ece
--- /dev/null
+++ b/drivers/usb/cdns3/cdns3-pci-wrap.c
@@ -0,0 +1,157 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Cadence USBSS PCI Glue driver
+ *
+ * Copyright (C) 2018 Cadence.
+ *
+ * Author: Pawel Laszczak <pawell@cadence.com>
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/platform_device.h>
+#include <linux/dma-mapping.h>
+#include <linux/slab.h>
+
+struct cdns3_wrap {
+	struct platform_device *plat_dev;
+	struct pci_dev *hg_dev;
+	struct resource dev_res[4];
+};
+
+struct cdns3_wrap wrap;
+
+#define RES_IRQ_ID		0
+#define RES_HOST_ID		1
+#define RES_DEV_ID		2
+#define RES_DRD_ID		3
+
+#define PCI_BAR_HOST		0
+#define PCI_BAR_DEV		2
+#define PCI_BAR_OTG		4
+
+#define PCI_DEV_FN_HOST_DEVICE	0
+#define PCI_DEV_FN_OTG		1
+
+#define PCI_DRIVER_NAME		"cdns3-pci-usbss"
+#define PLAT_DRIVER_NAME	"cdns-usb3"
+
+#define CDNS_VENDOR_ID 0x17cd
+#define CDNS_DEVICE_ID 0x0100
+
+/**
+ * cdns3_pci_probe - Probe function for Cadence USB wrapper driver
+ * @pdev: platform device object
+ * @id: pci device id
+ *
+ * Returns 0 on success otherwise negative errno
+ */
+static int cdns3_pci_probe(struct pci_dev *pdev,
+			   const struct pci_device_id *id)
+{
+	struct platform_device_info plat_info;
+	struct cdns3_wrap *wrap;
+	struct resource *res;
+	int err;
+
+	/*
+	 * for GADGET/HOST PCI (devfn) function number is 0,
+	 * for OTG PCI (devfn) function number is 1
+	 */
+	if (!id || pdev->devfn != PCI_DEV_FN_HOST_DEVICE)
+		return -EINVAL;
+
+	err = pcim_enable_device(pdev);
+	if (err) {
+		dev_err(&pdev->dev, "Enabling PCI device has failed %d\n", err);
+		return err;
+	}
+
+	pci_set_master(pdev);
+	wrap = devm_kzalloc(&pdev->dev, sizeof(*wrap), GFP_KERNEL);
+	if (!wrap) {
+		dev_err(&pdev->dev, "Failed to allocate memory\n");
+		return -ENOMEM;
+	}
+
+	/* function 0: host(BAR_0) + device(BAR_1) + otg(BAR_2)). */
+	memset(wrap->dev_res, 0x00,
+	       sizeof(struct resource) * ARRAY_SIZE(wrap->dev_res));
+	dev_dbg(&pdev->dev, "Initialize Device resources\n");
+	res = wrap->dev_res;
+
+	res[RES_DEV_ID].start = pci_resource_start(pdev, PCI_BAR_DEV);
+	res[RES_DEV_ID].end =   pci_resource_end(pdev, PCI_BAR_DEV);
+	res[RES_DEV_ID].name = "cdns3-dev-regs";
+	res[RES_DEV_ID].flags = IORESOURCE_MEM;
+	dev_dbg(&pdev->dev, "USBSS-DEV physical base addr: %pa\n",
+		&res[RES_DEV_ID].start);
+
+	res[RES_HOST_ID].start = pci_resource_start(pdev, PCI_BAR_HOST);
+	res[RES_HOST_ID].end = pci_resource_end(pdev, PCI_BAR_HOST);
+	res[RES_HOST_ID].name = "cdns3-xhci-regs";
+	res[RES_HOST_ID].flags = IORESOURCE_MEM;
+	dev_dbg(&pdev->dev, "USBSS-XHCI physical base addr: %pa\n",
+		&res[RES_HOST_ID].start);
+
+	res[RES_DRD_ID].start = pci_resource_start(pdev, PCI_BAR_OTG);
+	res[RES_DRD_ID].end =   pci_resource_end(pdev, PCI_BAR_OTG);
+	res[RES_DRD_ID].name = "cdns3-otg";
+	res[RES_DRD_ID].flags = IORESOURCE_MEM;
+	dev_dbg(&pdev->dev, "USBSS-DRD physical base addr: %pa\n",
+		&res[RES_DRD_ID].start);
+
+	/* Interrupt common for both device and XHCI */
+	wrap->dev_res[RES_IRQ_ID].start = pdev->irq;
+	wrap->dev_res[RES_IRQ_ID].name = "cdns3-irq";
+	wrap->dev_res[RES_IRQ_ID].flags = IORESOURCE_IRQ;
+
+	/* set up platform device info */
+	memset(&plat_info, 0, sizeof(plat_info));
+	plat_info.parent = &pdev->dev;
+	plat_info.fwnode = pdev->dev.fwnode;
+	plat_info.name = PLAT_DRIVER_NAME;
+	plat_info.id = pdev->devfn;
+	plat_info.res = wrap->dev_res;
+	plat_info.num_res = ARRAY_SIZE(wrap->dev_res);
+	plat_info.dma_mask = pdev->dma_mask;
+
+	/* register platform device */
+	wrap->plat_dev = platform_device_register_full(&plat_info);
+	if (IS_ERR(wrap->plat_dev)) {
+		err = PTR_ERR(wrap->plat_dev);
+		return err;
+	}
+
+	pci_set_drvdata(pdev, wrap);
+
+	return err;
+}
+
+void cdns3_pci_remove(struct pci_dev *pdev)
+{
+	struct cdns3_wrap *wrap = (struct cdns3_wrap *)pci_get_drvdata(pdev);
+
+	platform_device_unregister(wrap->plat_dev);
+}
+
+static const struct pci_device_id cdns3_pci_ids[] = {
+	{ PCI_DEVICE(CDNS_VENDOR_ID, CDNS_DEVICE_ID), },
+	{ 0, }
+};
+
+static struct pci_driver cdns3_pci_driver = {
+	.name = PCI_DRIVER_NAME,
+	.id_table = cdns3_pci_ids,
+	.probe = cdns3_pci_probe,
+	.remove = cdns3_pci_remove,
+};
+
+module_pci_driver(cdns3_pci_driver);
+MODULE_DEVICE_TABLE(pci, cdns3_pci_ids);
+
+MODULE_AUTHOR("Pawel Laszczak <pawell@cadence.com>");
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("Cadence USBSS PCI wrapperr");
+
diff --git a/drivers/usb/cdns3/core.c b/drivers/usb/cdns3/core.c
new file mode 100644
index 000000000000..d274586aca36
--- /dev/null
+++ b/drivers/usb/cdns3/core.c
@@ -0,0 +1,406 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Cadence USBSS DRD Driver.
+ *
+ * Copyright (C) 2018 Cadence.
+ * Copyright (C) 2017-2018 NXP
+ *
+ * Author: Peter Chen <peter.chen@nxp.com>
+ *         Pawel Laszczak <pawell@cadence.com>
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/platform_device.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/pm_runtime.h>
+
+#include "gadget.h"
+#include "core.h"
+#include "host-export.h"
+#include "gadget-export.h"
+#include "drd.h"
+#include "debug.h"
+
+static inline
+struct cdns3_role_driver *cdns3_get_current_role_driver(struct cdns3 *cdns)
+{
+	WARN_ON(cdns->role >= CDNS3_ROLE_END || !cdns->roles[cdns->role]);
+	return cdns->roles[cdns->role];
+}
+
+static inline int cdns3_role_start(struct cdns3 *cdns, enum cdns3_roles role)
+{
+	int ret;
+
+	if (WARN_ON(role >= CDNS3_ROLE_END))
+		return 0;
+
+	if (!cdns->roles[role])
+		return -ENXIO;
+
+	if (cdns->roles[role]->state == CDNS3_ROLE_STATE_ACTIVE)
+		return 0;
+
+	mutex_lock(&cdns->mutex);
+	cdns->role = role;
+	ret = cdns->roles[role]->start(cdns);
+	if (!ret)
+		cdns->roles[role]->state = CDNS3_ROLE_STATE_ACTIVE;
+	mutex_unlock(&cdns->mutex);
+	return ret;
+}
+
+void cdns3_role_stop(struct cdns3 *cdns)
+{
+	enum cdns3_roles role = cdns->role;
+
+	if (role >= CDNS3_ROLE_END) {
+		WARN_ON(role > CDNS3_ROLE_END);
+		return;
+	}
+
+	if (cdns->roles[role]->state == CDNS3_ROLE_STATE_INACTIVE)
+		return;
+
+	mutex_lock(&cdns->mutex);
+	cdns->roles[role]->stop(cdns);
+	cdns->roles[role]->state = CDNS3_ROLE_STATE_INACTIVE;
+	mutex_unlock(&cdns->mutex);
+}
+
+/*
+ * cdns->role gets from cdns3_get_initial_role, and this API tells role at the
+ * runtime.
+ * If both roles are supported, the role is selected based on vbus/id.
+ * It could be read from OTG register or external connector.
+ * If only single role is supported, only one role structure
+ * is allocated, cdns->roles[CDNS3_ROLE_HOST] or cdns->roles[CDNS3_ROLE_GADGET].
+ */
+static enum cdns3_roles cdns3_get_initial_role(struct cdns3 *cdns)
+{
+	if (cdns->roles[CDNS3_ROLE_HOST] && cdns->roles[CDNS3_ROLE_GADGET]) {
+		if (cdns3_is_host(cdns))
+			return CDNS3_ROLE_HOST;
+		if (cdns3_is_device(cdns))
+			return CDNS3_ROLE_GADGET;
+	}
+	return cdns->roles[CDNS3_ROLE_HOST]
+		? CDNS3_ROLE_HOST
+		: CDNS3_ROLE_GADGET;
+}
+
+static void cdns3_exit_roles(struct cdns3 *cdns)
+{
+	cdns3_role_stop(cdns);
+	cdns3_drd_exit(cdns);
+}
+
+/**
+ * cdns3_core_init_role - initialize role of operation
+ * @cdns: Pointer to cdns3 structure
+ *
+ * Returns 0 on success otherwise negative errno
+ */
+static int cdns3_core_init_role(struct cdns3 *cdns)
+{
+	struct device *dev = cdns->dev;
+	enum usb_dr_mode best_dr_mode;
+	enum usb_dr_mode dr_mode;
+	int ret = 0;
+
+	dr_mode = usb_get_dr_mode(dev);
+	cdns->role = CDNS3_ROLE_END;
+
+	/*
+	 * If driver can't read mode by means of usb_get_dr_mdoe function then
+	 * chooses mode according with Kernel configuration. This setting
+	 * can be restricted later depending on strap pin configuration.
+	 */
+	if (dr_mode == USB_DR_MODE_UNKNOWN) {
+		if (IS_ENABLED(CONFIG_USB_CDNS3_HOST) &&
+		    IS_ENABLED(CONFIG_USB_CDNS3_GADGET))
+			dr_mode = USB_DR_MODE_OTG;
+		else if (IS_ENABLED(CONFIG_USB_CDNS3_HOST))
+			dr_mode = USB_DR_MODE_HOST;
+		else if (IS_ENABLED(CONFIG_USB_CDNS3_GADGET))
+			dr_mode = USB_DR_MODE_PERIPHERAL;
+	}
+
+	best_dr_mode = USB_DR_MODE_OTG;
+
+	if (dr_mode == USB_DR_MODE_OTG) {
+		best_dr_mode = cdns->dr_mode;
+	} else if (cdns->dr_mode == USB_DR_MODE_OTG) {
+		best_dr_mode = dr_mode;
+	} else if (cdns->dr_mode != dr_mode) {
+		dev_err(dev, "Incorrect DRD configuration\n");
+		return -EINVAL;
+	}
+
+	dr_mode = best_dr_mode;
+
+	if (dr_mode == USB_DR_MODE_OTG || dr_mode == USB_DR_MODE_HOST) {
+		ret = cdns3_host_init(cdns);
+		if (ret) {
+			dev_err(dev, "Host initialization failed with %d\n",
+				ret);
+			goto err;
+		}
+	}
+
+	if (dr_mode == USB_DR_MODE_OTG || dr_mode == USB_DR_MODE_PERIPHERAL) {
+		ret = cdns3_gadget_init(cdns);
+		if (ret) {
+			dev_err(dev, "Device initialization failed with %d\n",
+				ret);
+			goto err;
+		}
+	}
+
+	cdns->desired_dr_mode = dr_mode;
+	cdns->dr_mode = dr_mode;
+	/*
+	 * dr_mode could be change so DRD must update controller
+	 * configuration
+	 */
+	ret = cdns3_drd_update_mode(cdns);
+
+	cdns->role = cdns3_get_initial_role(cdns);
+
+	ret = cdns3_role_start(cdns, cdns->role);
+	if (ret) {
+		dev_err(dev, "can't start %s role\n",
+			cdns3_get_current_role_driver(cdns)->name);
+		goto err;
+	}
+
+	return ret;
+err:
+	cdns3_exit_roles(cdns);
+	return ret;
+}
+
+/**
+ * cdsn3_get_real_role - get real role of controller based on hardware settings.
+ * @cdns: Pointer to cdns3 structure
+ *
+ * Returns role
+ */
+enum cdns3_roles cdsn3_get_real_role(struct cdns3 *cdns)
+{
+	enum cdns3_roles role = CDNS3_ROLE_END;
+
+	if (cdns->current_dr_mode == USB_DR_MODE_OTG) {
+		if (cdns3_get_id(cdns))
+			role = CDNS3_ROLE_GADGET;
+		else
+			role = CDNS3_ROLE_HOST;
+	} else {
+		if (cdns3_is_host(cdns))
+			role = CDNS3_ROLE_HOST;
+		if (cdns3_is_device(cdns))
+			role = CDNS3_ROLE_GADGET;
+	}
+
+	return role;
+}
+
+/**
+ * cdns3_role_switch - work queue handler for role switch
+ *
+ * @work: work queue item structure
+ *
+ * Handles below events:
+ * - Role switch for dual-role devices
+ * - CDNS3_ROLE_GADGET <--> CDNS3_ROLE_END for peripheral-only devices
+ */
+static void cdns3_role_switch(struct work_struct *work)
+{
+	enum cdns3_roles role = CDNS3_ROLE_END;
+	struct cdns3_role_driver *role_drv;
+	enum cdns3_roles current_role;
+	struct cdns3 *cdns;
+	int ret = 0;
+
+	cdns = container_of(work, struct cdns3, role_switch_wq);
+
+	/* During switching cdns->role can be different then role */
+	role = cdsn3_get_real_role(cdns);
+
+	role_drv = cdns3_get_current_role_driver(cdns);
+
+	pm_runtime_get_sync(cdns->dev);
+
+	/* Disable current role. This state can be forced from user space. */
+	if (cdns->debug_disable && role_drv->state == CDNS3_ROLE_STATE_ACTIVE) {
+		cdns3_role_stop(cdns);
+		goto exit;
+	}
+
+	/* Do nothing if nothing changed */
+	if (cdns->role == role && role_drv->state == CDNS3_ROLE_STATE_ACTIVE)
+		goto exit;
+
+	cdns3_role_stop(cdns);
+
+	role = cdsn3_get_real_role(cdns);
+
+	current_role = cdns->role;
+	dev_dbg(cdns->dev, "Switching role");
+
+	ret = cdns3_role_start(cdns, role);
+
+	if (ret) {
+		/* Back to current role */
+		dev_err(cdns->dev, "set %d has failed, back to %d\n",
+			role, current_role);
+		cdns3_role_start(cdns, current_role);
+	}
+exit:
+	pm_runtime_put_sync(cdns->dev);
+}
+
+/**
+ * cdns3_probe - probe for cdns3 core device
+ * @pdev: Pointer to cdns3 core platform device
+ *
+ * Returns 0 on success otherwise negative errno
+ */
+static int cdns3_probe(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	struct resource	*res;
+	struct cdns3 *cdns;
+	void __iomem *regs;
+	int ret;
+
+	cdns = devm_kzalloc(dev, sizeof(*cdns), GFP_KERNEL);
+	if (!cdns)
+		return -ENOMEM;
+
+	cdns->dev = dev;
+
+	platform_set_drvdata(pdev, cdns);
+
+	res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
+	if (!res) {
+		dev_err(dev, "missing IRQ\n");
+		return -ENODEV;
+	}
+	cdns->irq = res->start;
+
+	cdns->xhci_res[0] = *res;
+
+	/*
+	 * Request memory region
+	 * region-0: xHCI
+	 * region-1: Peripheral
+	 * region-2: OTG registers
+	 */
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	cdns->xhci_res[1] = *res;
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+	regs = devm_ioremap_resource(dev, res);
+	if (IS_ERR(regs))
+		return PTR_ERR(regs);
+	cdns->dev_regs	= regs;
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 2);
+	cdns->otg_res = *res;
+
+	mutex_init(&cdns->mutex);
+
+	cdns->phy = devm_phy_get(dev, "cdns3,usbphy");
+	if (IS_ERR(cdns->phy)) {
+		ret = PTR_ERR(cdns->phy);
+		if (ret == -ENODEV) {
+			cdns->phy = NULL;
+		} else if (ret == -EPROBE_DEFER) {
+			return ret;
+		} else {
+			dev_err(dev, "no phy found\n");
+			goto err0;
+		}
+	}
+
+	phy_init(cdns->phy);
+
+	INIT_WORK(&cdns->role_switch_wq, cdns3_role_switch);
+
+	ret = cdns3_drd_init(cdns);
+	if (ret)
+		goto err1;
+
+	ret = cdns3_core_init_role(cdns);
+	if (ret)
+		goto err1;
+
+	cdns3_debugfs_init(cdns);
+	device_set_wakeup_capable(dev, true);
+	pm_runtime_set_active(dev);
+	pm_runtime_enable(dev);
+
+	/*
+	 * The controller needs less time between bus and controller suspend,
+	 * and we also needs a small delay to avoid frequently entering low
+	 * power mode.
+	 */
+	pm_runtime_set_autosuspend_delay(dev, 20);
+	pm_runtime_mark_last_busy(dev);
+	pm_runtime_use_autosuspend(dev);
+	dev_dbg(dev, "Cadence USB3 core: probe succeed\n");
+
+	return 0;
+
+err1:
+	phy_exit(cdns->phy);
+err0:
+	return ret;
+}
+
+/**
+ * cdns3_remove - unbind drd driver and clean up
+ * @pdev: Pointer to Linux platform device
+ *
+ * Returns 0 on success otherwise negative errno
+ */
+static int cdns3_remove(struct platform_device *pdev)
+{
+	struct cdns3 *cdns = platform_get_drvdata(pdev);
+
+	pm_runtime_get_sync(&pdev->dev);
+	pm_runtime_disable(&pdev->dev);
+	pm_runtime_put_noidle(&pdev->dev);
+	cdns3_debugfs_exit(cdns);
+	cdns3_exit_roles(cdns);
+	phy_exit(cdns->phy);
+	return 0;
+}
+
+#ifdef CONFIG_OF
+static const struct of_device_id of_cdns3_match[] = {
+	{ .compatible = "cdns,usb3-1.0.0" },
+	{ .compatible = "cdns,usb3-1.0.1" },
+	{ },
+};
+MODULE_DEVICE_TABLE(of, of_cdns3_match);
+#endif
+
+static struct platform_driver cdns3_driver = {
+	.probe		= cdns3_probe,
+	.remove		= cdns3_remove,
+	.driver		= {
+		.name	= "cdns-usb3",
+		.of_match_table	= of_match_ptr(of_cdns3_match),
+	},
+};
+
+module_platform_driver(cdns3_driver);
+
+MODULE_ALIAS("platform:cdns3");
+MODULE_AUTHOR("Pawel Laszczak <pawell@cadence.com>");
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("Cadence USB3 DRD Controller Driver");
diff --git a/drivers/usb/cdns3/core.h b/drivers/usb/cdns3/core.h
new file mode 100644
index 000000000000..fb4b39206158
--- /dev/null
+++ b/drivers/usb/cdns3/core.h
@@ -0,0 +1,116 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Cadence USBSS DRD Header File.
+ *
+ * Copyright (C) 2017-2018 NXP
+ * Copyright (C) 2018 Cadence.
+ *
+ * Authors: Peter Chen <peter.chen@nxp.com>
+ *          Pawel Laszczak <pawell@cadence.com>
+ */
+#include <linux/usb/otg.h>
+
+#ifndef __LINUX_CDNS3_CORE_H
+#define __LINUX_CDNS3_CORE_H
+
+struct cdns3;
+enum cdns3_roles {
+	CDNS3_ROLE_HOST = 0,
+	CDNS3_ROLE_GADGET,
+	CDNS3_ROLE_END,
+};
+
+/**
+ * struct cdns3_role_driver - host/gadget role driver
+ * @start: start this role
+ * @stop: stop this role
+ * @suspend: suspend callback for this role
+ * @resume: resume callback for this role
+ * @irq: irq handler for this role
+ * @name: role name string (host/gadget)
+ * @state: current state
+ */
+struct cdns3_role_driver {
+	int (*start)(struct cdns3 *cdns);
+	void (*stop)(struct cdns3 *cdns);
+	int (*suspend)(struct cdns3 *cdns, bool do_wakeup);
+	int (*resume)(struct cdns3 *cdns, bool hibernated);
+	const char *name;
+#define CDNS3_ROLE_STATE_INACTIVE	0
+#define CDNS3_ROLE_STATE_ACTIVE		1
+	int state;
+};
+
+#define CDNS3_XHCI_RESOURCES_NUM	2
+/**
+ * struct cdns3 - Representation of Cadence USB3 DRD controller.
+ * @dev: pointer to Cadence device struct
+ * @xhci_regs: pointer to base of xhci registers
+ * @xhci_res: the resource for xhci
+ * @dev_regs: pointer to base of dev registers
+ * @otg_regs: pointer to base of otg registers
+ * @irq: irq number for controller
+ * @roles: array of supported roles for this controller
+ * @role: current role
+ * @host_dev: the child host device pointer for cdns3 core
+ * @gadget_dev: the child gadget device pointer for cdns3 core
+ * @usb: phy for this controller
+ * @role_switch_wq: work queue item for role switch
+ * @in_lpm: the controller in low power mode
+ * @wakeup_int: the wakeup interrupt
+ * @mutex: the mutex for concurrent code at driver
+ * @dr_mode: supported mode of operation it can be only Host, only Device
+ *           or OTG mode that allow to switch between Device and Host mode.
+ *           This field based on firmware setting, kernel configuration
+ *           and hardware configuration.
+ * @current_dr_mode: current mode of operation when in dual-role mode
+ * @desired_dr_mode: desired mode of operation when in dual-role mode.
+ *           This value can be changed during runtime.
+ *           Available options depends on  dr_mode:
+ *           dr_mode                 |  desired_dr_mode and current_dr_mode
+ *           ----------------------------------------------------------------
+ *           USB_DR_MODE_HOST        | only USB_DR_MODE_HOST
+ *           USB_DR_MODE_PERIPHERAL  | only USB_DR_MODE_PERIPHERAL
+ *           USB_DR_MODE_OTG         | only USB_DR_MODE_HOST
+ *           USB_DR_MODE_OTG         | only USB_DR_MODE_PERIPHERAL
+ *           USB_DR_MODE_OTG         | USB_DR_MODE_OTG
+ *
+ *           Desired_dr_role can be changed by means of debugfs.
+ * @root: debugfs root folder pointer
+ * @debug_disable:
+ */
+struct cdns3 {
+	struct device			*dev;
+	void __iomem			*xhci_regs;
+	struct resource			xhci_res[CDNS3_XHCI_RESOURCES_NUM];
+	struct cdns3_usb_regs __iomem	*dev_regs;
+
+	struct resource			otg_res;
+	struct cdns3_otg_legacy_regs	*otg_v0_regs;
+	struct cdns3_otg_regs		*otg_v1_regs;
+	struct cdns3_otg_common_regs	*otg_regs;
+#define CDNS3_CONTROLLER_V0	0
+#define CDNS3_CONTROLLER_V1	1
+	u32				version;
+
+	int				irq;
+	struct cdns3_role_driver	*roles[CDNS3_ROLE_END];
+	enum cdns3_roles		role;
+	struct platform_device		*host_dev;
+	struct cdns3_device		*gadget_dev;
+	struct phy			*phy;
+	struct work_struct		role_switch_wq;
+	int				in_lpm:1;
+	int				wakeup_int:1;
+	/* mutext used in workqueue*/
+	struct mutex			mutex;
+	enum usb_dr_mode		dr_mode;
+	enum usb_dr_mode		current_dr_mode;
+	enum usb_dr_mode		desired_dr_mode;
+	struct dentry			*root;
+	int				debug_disable:1;
+};
+
+void cdns3_role_stop(struct cdns3 *cdns);
+
+#endif /* __LINUX_CDNS3_CORE_H */
diff --git a/drivers/usb/cdns3/debug.h b/drivers/usb/cdns3/debug.h
new file mode 100644
index 000000000000..94f9ef15f899
--- /dev/null
+++ b/drivers/usb/cdns3/debug.h
@@ -0,0 +1,166 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Cadence USBSS DRD Driver.
+ * Debug header file.
+ *
+ * Copyright (C) 2018 Cadence.
+ *
+ * Author: Pawel Laszczak <pawell@cadence.com>
+ */
+#ifndef __LINUX_CDNS3_DEBUG
+#define __LINUX_CDNS3_DEBUG
+
+#include "core.h"
+
+static inline char *cdns3_decode_usb_irq(char *str,
+					 enum usb_device_speed speed,
+					 u32 usb_ists)
+{
+	int ret;
+
+	ret = sprintf(str, "IRQ %08x = ", usb_ists);
+
+	if (usb_ists & (USB_ISTS_CON2I | USB_ISTS_CONI)) {
+		ret += sprintf(str + ret, "Connection %s\n",
+			       usb_speed_string(speed));
+	}
+	if (usb_ists & USB_ISTS_CON2I || usb_ists & USB_ISTS_CONI)
+		ret += sprintf(str + ret, "Disconnection ");
+	if (usb_ists & USB_ISTS_L2ENTI)
+		ret += sprintf(str + ret, "suspended ");
+
+	if (usb_ists & USB_ISTS_L2EXTI)
+		ret += sprintf(str + ret, "L2 exit ");
+	if (usb_ists & USB_ISTS_U3EXTI)
+		ret += sprintf(str + ret, "U3 exit ");
+	if (usb_ists & USB_ISTS_UWRESI)
+		ret += sprintf(str + ret, "Warm Reset ");
+	if (usb_ists & USB_ISTS_UHRESI)
+		ret += sprintf(str + ret, "Hot Reset ");
+	if (usb_ists & USB_ISTS_U2RESI)
+		ret += sprintf(str + ret, "Reset");
+
+	return str;
+}
+
+static inline  char *cdns3_decode_ep_irq(char *str,
+					 u32 ep_sts,
+					 const char *ep_name)
+{
+	int ret;
+
+	ret = sprintf(str, "IRQ for %s: %08x ", ep_name, ep_sts);
+
+	if (ep_sts & EP_STS_SETUP)
+		ret += sprintf(str + ret, "SETUP ");
+	if (ep_sts & EP_STS_IOC)
+		ret += sprintf(str + ret, "IOC ");
+	if (ep_sts & EP_STS_ISP)
+		ret += sprintf(str + ret, "ISP ");
+	if (ep_sts & EP_STS_DESCMIS)
+		ret += sprintf(str + ret, "DESCMIS ");
+	if (ep_sts & EP_STS_STREAMR)
+		ret += sprintf(str + ret, "STREAMR ");
+	if (ep_sts & EP_STS_MD_EXIT)
+		ret += sprintf(str + ret, "MD_EXIT ");
+	if (ep_sts & EP_STS_TRBERR)
+		ret += sprintf(str + ret, "TRBERR ");
+	if (ep_sts & EP_STS_NRDY)
+		ret += sprintf(str + ret, "NRDY ");
+	if (ep_sts & EP_STS_PRIME)
+		ret += sprintf(str + ret, "PRIME ");
+	if (ep_sts & EP_STS_SIDERR)
+		ret += sprintf(str + ret, "SIDERRT ");
+	if (ep_sts & EP_STS_OUTSMM)
+		ret += sprintf(str + ret, "OUTSMM ");
+	if (ep_sts & EP_STS_ISOERR)
+		ret += sprintf(str + ret, "ISOERR ");
+	if (ep_sts & EP_STS_IOT)
+		ret += sprintf(str + ret, "IOT ");
+
+	return str;
+}
+
+static inline char *cdns3_decode_epx_irq(char *str,
+					 char *ep_name,
+					 u32 ep_sts)
+{
+	return cdns3_decode_ep_irq(str, ep_sts, ep_name);
+}
+
+static inline char *cdns3_decode_ep0_irq(char *str,
+					 int dir,
+					 u32 ep_sts)
+{
+	return cdns3_decode_ep_irq(str, ep_sts,
+				   dir ? "ep0IN" : "ep0OUT");
+}
+
+/**
+ * Debug a transfer ring.
+ *
+ * Prints out all TRBs in the endpoint ring, even those after the Link TRB.
+ *.
+ */
+static inline char *cdns3_dbg_ring(struct cdns3_endpoint *priv_ep,
+				   struct cdns3_trb *ring, char *str)
+{
+	dma_addr_t addr = priv_ep->trb_pool_dma;
+	struct cdns3_trb *trb;
+	int trb_per_sector;
+	int ret = 0;
+	int i;
+
+	trb_per_sector = GET_TRBS_PER_SEGMENT(priv_ep->type);
+
+	trb = &priv_ep->trb_pool[priv_ep->dequeue];
+	ret += sprintf(str + ret, "\n\t\tRing contents for %s:", priv_ep->name);
+
+	ret += sprintf(str + ret,
+		       "\n\t\tRing deq index: %d, trb: %p (virt), 0x%llx (dma)\n",
+		       priv_ep->dequeue, trb,
+		       (unsigned long long)cdns3_trb_virt_to_dma(priv_ep, trb));
+
+	trb = &priv_ep->trb_pool[priv_ep->enqueue];
+	ret += sprintf(str + ret,
+		       "\t\tRing enq index: %d, trb: %p (virt), 0x%llx (dma)\n",
+		       priv_ep->enqueue, trb,
+		       (unsigned long long)cdns3_trb_virt_to_dma(priv_ep, trb));
+
+	ret += sprintf(str + ret,
+		       "\t\tfree trbs: %d, CCS=%d, PCS=%d\n",
+		       priv_ep->free_trbs, priv_ep->ccs, priv_ep->pcs);
+
+	if (trb_per_sector > TRBS_PER_SEGMENT)
+		trb_per_sector = TRBS_PER_SEGMENT;
+
+	if (trb_per_sector > TRBS_PER_SEGMENT) {
+		sprintf(str + ret, "\t\tTo big transfer ring %d\n",
+			trb_per_sector);
+		return str;
+	}
+
+	for (i = 0; i < trb_per_sector; ++i) {
+		trb = &ring[i];
+		ret += sprintf(str + ret,
+			"\t\t@%pad %08x %08x %08x\n", &addr,
+			le32_to_cpu(trb->buffer),
+			le32_to_cpu(trb->length),
+			le32_to_cpu(trb->control));
+		addr += sizeof(*trb);
+	}
+
+	return str;
+}
+
+#ifdef CONFIG_DEBUG_FS
+void cdns3_debugfs_init(struct cdns3 *cdns);
+void cdns3_debugfs_exit(struct cdns3 *cdns);
+#else
+void cdns3_debugfs_init(struct cdns3 *cdns);
+{  }
+void cdns3_debugfs_exit(struct cdns3 *cdns);
+{  }
+#endif
+
+#endif /*__LINUX_CDNS3_DEBUG*/
diff --git a/drivers/usb/cdns3/debugfs.c b/drivers/usb/cdns3/debugfs.c
new file mode 100644
index 000000000000..d7919f5c1d90
--- /dev/null
+++ b/drivers/usb/cdns3/debugfs.c
@@ -0,0 +1,168 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Cadence USBSS DRD Controller DebugFS filer.
+ *
+ * Copyright (C) 2018 Cadence.
+ *
+ * Author: Pawel Laszczak <pawell@cadence.com>
+ */
+
+#include <linux/types.h>
+#include <linux/debugfs.h>
+#include <linux/seq_file.h>
+#include <linux/uaccess.h>
+
+#include "core.h"
+#include "gadget.h"
+#include "drd.h"
+
+static int cdns3_mode_show(struct seq_file *s, void *unused)
+{
+	struct cdns3 *cdns = s->private;
+
+	switch (cdns->current_dr_mode) {
+	case USB_DR_MODE_HOST:
+		seq_puts(s, "host\n");
+		break;
+	case USB_DR_MODE_PERIPHERAL:
+		seq_puts(s, "device\n");
+		break;
+	case USB_DR_MODE_OTG:
+		seq_puts(s, "otg\n");
+		break;
+	default:
+		seq_puts(s, "UNKNOWN mode\n");
+	}
+
+	return 0;
+}
+
+static int cdns3_mode_open(struct inode *inode, struct file *file)
+{
+	return single_open(file, cdns3_mode_show, inode->i_private);
+}
+
+static ssize_t cdns3_mode_write(struct file *file,
+				const char __user *ubuf,
+				size_t count, loff_t *ppos)
+{
+	struct seq_file	 *s = file->private_data;
+	struct cdns3 *cdns = s->private;
+	u32 mode = USB_DR_MODE_UNKNOWN;
+	char buf[32];
+
+	if (copy_from_user(&buf, ubuf, min_t(size_t, sizeof(buf) - 1, count)))
+		return -EFAULT;
+
+	if (!strncmp(buf, "host", 4)) {
+		if (cdns->dr_mode == USB_DR_MODE_HOST ||
+		    cdns->dr_mode == USB_DR_MODE_OTG) {
+			mode = USB_DR_MODE_HOST;
+		}
+	}
+
+	if (!strncmp(buf, "device", 6))
+		if (cdns->dr_mode == USB_DR_MODE_PERIPHERAL ||
+		    cdns->dr_mode == USB_DR_MODE_OTG)
+			mode = USB_DR_MODE_PERIPHERAL;
+
+	if (!strncmp(buf, "otg", 3) && cdns->dr_mode == USB_DR_MODE_OTG)
+		mode = USB_DR_MODE_OTG;
+
+	if (mode == USB_DR_MODE_UNKNOWN) {
+		dev_err(cdns->dev, "Failed: incorrect mode setting\n");
+		return -EFAULT;
+	}
+
+	if (cdns->current_dr_mode != mode) {
+		cdns->desired_dr_mode = mode;
+		cdns->debug_disable = 0;
+		cdns3_role_stop(cdns);
+		cdns3_drd_update_mode(cdns);
+		queue_work(system_freezable_wq, &cdns->role_switch_wq);
+	}
+
+	return count;
+}
+
+static const struct file_operations cdns3_mode_fops = {
+	.open			= cdns3_mode_open,
+	.write			= cdns3_mode_write,
+	.read			= seq_read,
+	.llseek			= seq_lseek,
+	.release		= single_release,
+};
+
+static int cdns3_disable_show(struct seq_file *s, void *unused)
+{
+	struct cdns3 *cdns = s->private;
+
+	if (!cdns->debug_disable)
+		seq_puts(s, "0\n");
+	else
+		seq_puts(s, "1\n");
+
+	return 0;
+}
+
+static ssize_t cdns3_disable_write(struct file *file,
+				   const char __user *ubuf,
+				   size_t count, loff_t *ppos)
+{
+	struct seq_file	 *s = file->private_data;
+	struct cdns3 *cdns = s->private;
+	int disable;
+	char buf[32];
+
+	if (copy_from_user(&buf, ubuf, min_t(size_t, sizeof(buf) - 1, count)))
+		return -EFAULT;
+
+	if (!strncmp(buf, "1", 1) || !strncmp(buf, "yes", 3)) {
+		disable = 1;
+	} else if (!strncmp(buf, "0", 1) || !strncmp(buf, "no", 2)) {
+		disable = 0;
+	} else {
+		dev_err(cdns->dev, "Failed: incorrect disable setting\n");
+		return -EFAULT;
+	}
+
+	if (disable != cdns->debug_disable) {
+		cdns->debug_disable = disable;
+		queue_work(system_freezable_wq, &cdns->role_switch_wq);
+	}
+
+	return count;
+}
+
+static int cdns3_disable_open(struct inode *inode, struct file *file)
+{
+	return single_open(file, cdns3_disable_show, inode->i_private);
+}
+
+static const struct file_operations cdns3_disable_fops = {
+	.open			= cdns3_disable_open,
+	.write			= cdns3_disable_write,
+	.read			= seq_read,
+	.llseek			= seq_lseek,
+	.release		= single_release,
+};
+
+void cdns3_debugfs_init(struct cdns3 *cdns)
+{
+	struct dentry *root;
+
+	root = debugfs_create_dir(dev_name(cdns->dev), NULL);
+	cdns->root = root;
+	if (IS_ENABLED(CONFIG_USB_CDNS3_GADGET) &&
+	    IS_ENABLED(CONFIG_USB_CDNS3_HOST))
+		debugfs_create_file("mode", 0644, root, cdns,
+				    &cdns3_mode_fops);
+
+	debugfs_create_file("disable", 0644, root, cdns,
+			    &cdns3_disable_fops);
+}
+
+void cdns3_debugfs_exit(struct cdns3 *cdns)
+{
+	debugfs_remove_recursive(cdns->root);
+}
diff --git a/drivers/usb/cdns3/drd.c b/drivers/usb/cdns3/drd.c
new file mode 100644
index 000000000000..b0c32302eb0b
--- /dev/null
+++ b/drivers/usb/cdns3/drd.c
@@ -0,0 +1,350 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Cadence USBSS DRD Driver.
+ *
+ * Copyright (C) 2018 Cadence.
+ *
+ * Author: Pawel Laszczak <pawell@cadence.com
+ *
+ */
+#include <linux/kernel.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <linux/usb/otg.h>
+
+#include "gadget.h"
+#include "drd.h"
+#include "core.h"
+
+static int cdns3_drd_switch_gadget(struct cdns3 *cdns, int on);
+static int cdns3_drd_switch_host(struct cdns3 *cdns, int on);
+
+/**
+ * cdns3_set_mode - change mode of OTG Core
+ * @cdns: pointer to context structure
+ * @mode: selected mode from cdns_role
+ */
+void cdns3_set_mode(struct cdns3 *cdns, enum usb_dr_mode mode)
+{
+	u32 reg;
+
+	cdns->current_dr_mode = mode;
+
+	switch (mode) {
+	case USB_DR_MODE_PERIPHERAL:
+		dev_info(cdns->dev, "Set controller to Gadget mode\n");
+		cdns3_drd_switch_gadget(cdns, 1);
+		break;
+	case USB_DR_MODE_HOST:
+		dev_info(cdns->dev, "Set controller to Host mode\n");
+		cdns3_drd_switch_host(cdns, 1);
+		break;
+	case USB_DR_MODE_OTG:
+		dev_info(cdns->dev, "Set controller to OTG mode\n");
+		if (cdns->version == CDNS3_CONTROLLER_V1) {
+			reg = readl(&cdns->otg_v1_regs->override);
+			reg |= OVERRIDE_IDPULLUP;
+			writel(reg, &cdns->otg_v1_regs->override);
+		} else {
+			reg = readl(&cdns->otg_v0_regs->ctrl1);
+			reg |= OVERRIDE_IDPULLUP_V0;
+			writel(reg, &cdns->otg_v0_regs->ctrl1);
+		}
+
+		/*
+		 * Hardware specification says: "ID_VALUE must be valid within
+		 * 50ms after idpullup is set to '1" so driver must wait
+		 * 50ms before reading this pin.
+		 */
+		usleep_range(50000, 60000);
+		break;
+	default:
+		cdns->current_dr_mode = USB_DR_MODE_UNKNOWN;
+		dev_err(cdns->dev, "Unsupported mode of operation %d\n", mode);
+		return;
+	}
+}
+
+int cdns3_get_id(struct cdns3 *cdns)
+{
+	int id;
+
+	id = readl(&cdns->otg_regs->sts) & OTGSTS_ID_VALUE;
+	dev_dbg(cdns->dev, "OTG ID: %d", id);
+	return  1 ; //id;
+}
+
+int cdns3_is_host(struct cdns3 *cdns)
+{
+	if (cdns->current_dr_mode == USB_DR_MODE_HOST)
+		return 1;
+	else if (!cdns3_get_id(cdns))
+		return 1;
+
+	return 0;
+}
+
+int cdns3_is_device(struct cdns3 *cdns)
+{
+	if (cdns->current_dr_mode == USB_DR_MODE_PERIPHERAL)
+		return 1;
+	else if (cdns->current_dr_mode == USB_DR_MODE_OTG)
+		if (cdns3_get_id(cdns))
+			return 1;
+
+	return 0;
+}
+
+/**
+ * cdns3_otg_disable_irq - Disable all OTG interrupts
+ * @cdns: Pointer to controller context structure
+ */
+static void cdns3_otg_disable_irq(struct cdns3 *cdns)
+{
+	writel(0, &cdns->otg_regs->ien);
+}
+
+/**
+ * cdns3_otg_enable_irq - enable id and sess_valid interrupts
+ * @cdns: Pointer to controller context structure
+ */
+static void cdns3_otg_enable_irq(struct cdns3 *cdns)
+{
+	writel(OTGIEN_ID_CHANGE_INT | OTGIEN_VBUSVALID_RISE_INT |
+	       OTGIEN_VBUSVALID_FALL_INT, &cdns->otg_regs->ien);
+}
+
+/**
+ * cdns3_drd_switch_host - start/stop host
+ * @cdns: Pointer to controller context structure
+ * @on: 1 for start, 0 for stop
+ *
+ * Returns 0 on success otherwise negative errno
+ */
+static int cdns3_drd_switch_host(struct cdns3 *cdns, int on)
+{
+	int ret;
+	u32 reg = OTGCMD_OTG_DIS;
+
+	/* switch OTG core */
+	if (on) {
+		writel(OTGCMD_HOST_BUS_REQ | reg, &cdns->otg_regs->cmd);
+
+		dev_dbg(cdns->dev, "Waiting for Host mode is turned on\n");
+		ret = cdns3_handshake(&cdns->otg_regs->sts, OTGSTS_XHCI_READY,
+				      OTGSTS_XHCI_READY, 100000);
+
+		if (ret)
+			return ret;
+	} else {
+		usleep_range(30, 40);
+		writel(OTGCMD_HOST_BUS_DROP | OTGCMD_DEV_BUS_DROP |
+		       OTGCMD_DEV_POWER_OFF | OTGCMD_HOST_POWER_OFF,
+		       &cdns->otg_regs->cmd);
+	}
+
+	return 0;
+}
+
+/**
+ * cdns3_drd_switch_gadget - start/stop gadget
+ * @cdns: Pointer to controller context structure
+ * @on: 1 for start, 0 for stop
+ *
+ * Returns 0 on success otherwise negative errno
+ */
+static int cdns3_drd_switch_gadget(struct cdns3 *cdns, int on)
+{
+	int ret;
+	u32 reg = OTGCMD_OTG_DIS;
+
+	/* switch OTG core */
+	if (on) {
+		writel(OTGCMD_DEV_BUS_REQ | reg, &cdns->otg_regs->cmd);
+
+		dev_dbg(cdns->dev, "Waiting for Device mode is turned on\n");
+
+		ret = cdns3_handshake(&cdns->otg_regs->sts, OTGSTS_DEV_READY,
+				      OTGSTS_DEV_READY, 100000);
+
+		if (ret)
+			return ret;
+	} else {
+		/*
+		 * driver should wait at least 10us after disabling Device
+		 * before turning-off Device (DEV_BUS_DROP)
+		 */
+		usleep_range(20, 30);
+		writel(OTGCMD_HOST_BUS_DROP | OTGCMD_DEV_BUS_DROP |
+		       OTGCMD_DEV_POWER_OFF | OTGCMD_HOST_POWER_OFF,
+		       &cdns->otg_regs->cmd);
+	}
+
+	return 0;
+}
+
+/**
+ * cdns3_init_otg_mode - initialize drd controller
+ * @cdns: Pointer to controller context structure
+ *
+ * Returns 0 on success otherwise negative errno
+ */
+static void cdns3_init_otg_mode(struct cdns3 *cdns)
+{
+	cdns3_otg_disable_irq(cdns);
+	/* clear all interrupts */
+	writel(~0, &cdns->otg_regs->ivect);
+
+	cdns3_set_mode(cdns, USB_DR_MODE_OTG);
+
+	if (cdns3_is_host(cdns))
+		cdns3_drd_switch_host(cdns, 1);
+	else
+		cdns3_drd_switch_gadget(cdns, 1);
+
+	cdns3_otg_enable_irq(cdns);
+}
+
+/**
+ * cdns3_drd_update_mode - initialize mode of operation
+ * @cdns: Pointer to controller context structure
+ *
+ * Returns 0 on success otherwise negative errno
+ */
+int cdns3_drd_update_mode(struct cdns3 *cdns)
+{
+	int ret = 0;
+
+	if (cdns->desired_dr_mode == cdns->current_dr_mode)
+		return ret;
+
+	cdns3_drd_switch_gadget(cdns, 0);
+	cdns3_drd_switch_host(cdns, 0);
+
+	switch (cdns->desired_dr_mode) {
+	case USB_DR_MODE_PERIPHERAL:
+		cdns3_set_mode(cdns, USB_DR_MODE_PERIPHERAL);
+		break;
+	case USB_DR_MODE_HOST:
+		cdns3_set_mode(cdns, USB_DR_MODE_HOST);
+		break;
+	case USB_DR_MODE_OTG:
+		cdns3_init_otg_mode(cdns);
+		break;
+	default:
+		dev_err(cdns->dev, "Unsupported mode of operation %d\n",
+			cdns->dr_mode);
+		return -EINVAL;
+	}
+
+	return ret;
+}
+
+/**
+ * cdns3_drd_irq - interrupt handler for OTG events
+ *
+ * @irq: irq number for cdns3 core device
+ * @data: structure of cdns3
+ *
+ * Returns IRQ_HANDLED or IRQ_NONE
+ */
+static irqreturn_t cdns3_drd_irq(int irq, void *data)
+{
+	irqreturn_t ret = IRQ_NONE;
+	struct cdns3 *cdns = data;
+	u32 reg;
+
+	if (cdns->dr_mode != USB_DR_MODE_OTG)
+		return ret;
+
+	reg = readl(&cdns->otg_regs->ivect);
+
+	if (!reg)
+		return ret;
+
+	if (reg & OTGIEN_ID_CHANGE_INT) {
+		dev_dbg(cdns->dev, "OTG IRQ: new ID: %d\n",
+			cdns3_get_id(cdns));
+
+		queue_work(system_freezable_wq, &cdns->role_switch_wq);
+
+		ret = IRQ_HANDLED;
+	}
+
+	writel(~0, &cdns->otg_regs->ivect);
+	return ret;
+}
+
+int cdns3_drd_init(struct cdns3 *cdns)
+{
+	void __iomem *regs;
+	int ret = 0;
+	u32 state;
+
+	regs = devm_ioremap_resource(cdns->dev, &cdns->otg_res);
+	if (IS_ERR(regs))
+		return PTR_ERR(regs);
+
+	/* Detection of DRD version. Controller has been released
+	 * in two versions. Both are similar, but they have same changes
+	 * in register maps.
+	 * The first register in old version is command register and it's read
+	 * only, so driver should read 0 from it. On the other hand, in v1
+	 * the first register contains device ID number which is not set to 0.
+	 * Driver uses this fact to detect the proper version of
+	 * controller.
+	 */
+	cdns->otg_v0_regs = regs;
+	if (!readl(&cdns->otg_v0_regs->cmd)) {
+		cdns->version  = CDNS3_CONTROLLER_V0;
+		cdns->otg_v1_regs = NULL;
+		cdns->otg_regs = regs;
+		dev_info(cdns->dev, "DRD version v0 (%08x)\n",
+			 readl(&cdns->otg_v0_regs->version));
+	} else {
+		cdns->otg_v0_regs = NULL;
+		cdns->otg_v1_regs = regs;
+		cdns->otg_regs = (void *)&cdns->otg_v1_regs->cmd;
+		cdns->version  = CDNS3_CONTROLLER_V1;
+		dev_info(cdns->dev, "DRD version v1 (ID: %08x, rev: %08x)\n",
+			 readl(&cdns->otg_v1_regs->did),
+			 readl(&cdns->otg_v1_regs->rid));
+	}
+
+	state = OTGSTS_STRAP(readl(&cdns->otg_regs->sts));
+
+	/* Update dr_mode according to STRAP configuration. */
+	cdns->dr_mode = USB_DR_MODE_OTG;
+	if (state == OTGSTS_STRAP_HOST) {
+		dev_info(cdns->dev, "Controller strapped to HOST\n");
+		cdns->dr_mode = USB_DR_MODE_HOST;
+	} else if (state == OTGSTS_STRAP_GADGET) {
+		dev_info(cdns->dev, "Controller strapped to PERIPHERAL\n");
+		cdns->dr_mode = USB_DR_MODE_PERIPHERAL;
+	}
+
+	cdns->desired_dr_mode = cdns->dr_mode;
+	cdns->current_dr_mode = USB_DR_MODE_UNKNOWN;
+
+	ret = devm_request_threaded_irq(cdns->dev, cdns->irq, cdns3_drd_irq,
+					NULL, IRQF_SHARED,
+					dev_name(cdns->dev), cdns);
+
+	if (ret)
+		return ret;
+
+	state = readl(&cdns->otg_regs->sts);
+	if (OTGSTS_OTG_NRDY(state) != 0) {
+		dev_err(cdns->dev, "Cadence USB3 OTG device not ready\n");
+		return -ENODEV;
+	}
+
+	ret = cdns3_drd_update_mode(cdns);
+
+	return ret;
+}
+
+int cdns3_drd_exit(struct cdns3 *cdns)
+{
+	return cdns3_drd_switch_host(cdns, 0);
+}
diff --git a/drivers/usb/cdns3/drd.h b/drivers/usb/cdns3/drd.h
new file mode 100644
index 000000000000..6a29cdcb492d
--- /dev/null
+++ b/drivers/usb/cdns3/drd.h
@@ -0,0 +1,162 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Cadence USB3 DRD header file.
+ *
+ * Copyright (C) 2018 Cadence.
+ *
+ * Author: Pawel Laszczak <pawell@cadence.com>
+ */
+#ifndef __LINUX_CDNS3_DRD
+#define __LINUX_CDNS3_DRD
+
+#include <linux/usb/otg.h>
+#include <linux/phy/phy.h>
+#include "core.h"
+
+/*  DRD register interface for version v1. */
+struct cdns3_otg_regs {
+	__le32 did;
+	__le32 rid;
+	__le32 capabilities;
+	__le32 reserved1;
+	__le32 cmd;
+	__le32 sts;
+	__le32 state;
+	__le32 reserved2;
+	__le32 ien;
+	__le32 ivect;
+	__le32 refclk;
+	__le32 tmr;
+	__le32 reserved3[4];
+	__le32 simulate;
+	__le32 override;
+	__le32 susp_ctrl;
+	__le32 reserved4;
+	__le32 anasts;
+	__le32 adp_ramp_time;
+	__le32 ctrl1;
+	__le32 ctrl2;
+};
+
+/*  DRD register interface for version v0. */
+struct cdns3_otg_legacy_regs {
+	__le32 cmd;
+	__le32 sts;
+	__le32 state;
+	__le32 refclk;
+	__le32 ien;
+	__le32 ivect;
+	__le32 reserved1[3];
+	__le32 tmr;
+	__le32 reserved2[2];
+	__le32 version;
+	__le32 capabilities;
+	__le32 reserved3[2];
+	__le32 simulate;
+	__le32 reserved4[5];
+	__le32 ctrl1;
+};
+
+/*
+ * Common registers interface for both version of DRD.
+ */
+struct cdns3_otg_common_regs {
+	__le32 cmd;
+	__le32 sts;
+	__le32 state;
+	__le32 different1;
+	__le32 ien;
+	__le32 ivect;
+};
+
+/* CDNS_RID - bitmasks */
+#define CDNS_RID(p)			((p) & GENMASK(15, 0))
+
+/* CDNS_VID - bitmasks */
+#define CDNS_DID(p)			((p) & GENMASK(31, 0))
+
+/* OTGCMD - bitmasks */
+/* "Request the bus for Device mode. */
+#define OTGCMD_DEV_BUS_REQ		BIT(0)
+/* Request the bus for Host mode */
+#define OTGCMD_HOST_BUS_REQ		BIT(1)
+/* Enable OTG mode. */
+#define OTGCMD_OTG_EN			BIT(2)
+/* Disable OTG mode */
+#define OTGCMD_OTG_DIS			BIT(3)
+/*"Configure OTG as A-Device. */
+#define OTGCMD_A_DEV_EN			BIT(4)
+/*"Configure OTG as A-Device. */
+#define OTGCMD_A_DEV_DIS		BIT(5)
+/* Drop the bus for Device mod	e. */
+#define OTGCMD_DEV_BUS_DROP		BIT(8)
+/* Drop the bus for Host mode*/
+#define OTGCMD_HOST_BUS_DROP		BIT(9)
+/* Power Down USBSS-DEV. */
+#define OTGCMD_DEV_POWER_OFF		BIT(11)
+/* Power Down CDNSXHCI. */
+#define OTGCMD_HOST_POWER_OFF		BIT(12)
+
+/* OTGIEN - bitmasks */
+/* ID change interrupt enable */
+#define OTGIEN_ID_CHANGE_INT		BIT(0)
+/* Vbusvalid fall detected interrupt enable.*/
+#define OTGIEN_VBUSVALID_RISE_INT	BIT(4)
+/* Vbusvalid fall detected interrupt enable */
+#define OTGIEN_VBUSVALID_FALL_INT	BIT(5)
+
+/* OTGSTS - bitmasks */
+/*
+ * Current value of the ID pin. It is only valid when idpullup in
+ *  OTGCTRL1_TYPE register is set to '1'.
+ */
+#define OTGSTS_ID_VALUE			BIT(0)
+/* Current value of the vbus_valid */
+#define OTGSTS_VBUS_VALID		BIT(1)
+/* Current value of the b_sess_vld */
+#define OTGSTS_SESSION_VALID		BIT(2)
+/*Device mode is active*/
+#define OTGSTS_DEV_ACTIVE		BIT(3)
+/* Host mode is active. */
+#define OTGSTS_HOST_ACTIVE		BIT(4)
+/* OTG Controller not ready. */
+#define OTGSTS_OTG_NRDY_MASK		BIT(11)
+#define OTGSTS_OTG_NRDY(p)		((p) & OTGSTS_OTG_NRDY_MASK)
+/*
+ * Value of the strap pins.
+ * 000 - no default configuration
+ * 010 - Controller initiall configured as Host
+ * 100 - Controller initially configured as Device
+ */
+#define OTGSTS_STRAP(p)			(((p) & GENMASK(14, 12)) >> 12)
+#define OTGSTS_STRAP_NO_DEFAULT_CFG	0x00
+#define OTGSTS_STRAP_HOST_OTG		0x01
+#define OTGSTS_STRAP_HOST		0x02
+#define OTGSTS_STRAP_GADGET		0x04
+/* Host mode is turned on. */
+#define OTGSTS_XHCI_READY		BIT(26)
+/* "Device mode is turned on .*/
+#define OTGSTS_DEV_READY		BIT(27)
+
+/* OTGSTATE- bitmasks */
+#define OTGSTATE_HOST_STATE_MASK	GENMASK(5, 3)
+#define OTGSTATE_HOST_STATE_IDLE	0x0
+#define OTGSTATE_HOST_STATE_VBUS_FALL   0x7
+#define OTGSTATE_HOST_STATE(p)		(((p) & OTGSTATE_HOST_STATE_MASK) >> 3)
+
+/* OTGREFCLK - bitmasks */
+#define OTGREFCLK_STB_CLK_SWITCH_EN	BIT(31)
+
+/* OVERRIDE - bitmasks */
+#define OVERRIDE_IDPULLUP		BIT(0)
+/* Only for CDNS3_CONTROLLER_V0 version */
+#define OVERRIDE_IDPULLUP_V0		BIT(24)
+
+int cdns3_is_host(struct cdns3 *cdns);
+int cdns3_is_device(struct cdns3 *cdns);
+int cdns3_get_id(struct cdns3 *cdns);
+int cdns3_drd_init(struct cdns3 *cdns);
+int cdns3_drd_exit(struct cdns3 *cdns);
+int cdns3_drd_update_mode(struct cdns3 *cdns);
+
+#endif /* __LINUX_CDNS3_DRD */
diff --git a/drivers/usb/cdns3/ep0.c b/drivers/usb/cdns3/ep0.c
new file mode 100644
index 000000000000..89cf1cde1555
--- /dev/null
+++ b/drivers/usb/cdns3/ep0.c
@@ -0,0 +1,896 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Cadence USBSS DRD Driver - gadget side.
+ *
+ * Copyright (C) 2018 Cadence Design Systems.
+ * Copyright (C) 2017-2018 NXP
+ *
+ * Authors: Pawel Jez <pjez@cadence.com>,
+ *          Pawel Laszczak <pawell@cadence.com>
+ *	    Peter Chen <peter.chen@nxp.com>
+ */
+
+#include <linux/usb/composite.h>
+
+#include "gadget.h"
+#include "trace.h"
+
+static struct usb_endpoint_descriptor cdns3_gadget_ep0_desc = {
+	.bLength = USB_DT_ENDPOINT_SIZE,
+	.bDescriptorType = USB_DT_ENDPOINT,
+	.bmAttributes =	USB_ENDPOINT_XFER_CONTROL,
+};
+
+/**
+ * cdns3_ep0_run_transfer - Do transfer on default endpoint hardware
+ * @priv_dev: extended gadget object
+ * @dma_addr: physical address where data is/will be stored
+ * @length: data length
+ * @erdy: set it to 1 when ERDY packet should be sent -
+ *        exit from flow control state
+ */
+static void cdns3_ep0_run_transfer(struct cdns3_device *priv_dev,
+				   dma_addr_t dma_addr,
+				   unsigned int length, int erdy)
+{
+	struct cdns3_usb_regs __iomem *regs = priv_dev->regs;
+	struct cdns3_endpoint *priv_ep = priv_dev->eps[0];
+
+	priv_ep->trb_pool->buffer = TRB_BUFFER(dma_addr);
+	priv_ep->trb_pool->length = TRB_LEN(length);
+	priv_ep->trb_pool->control = TRB_CYCLE | TRB_IOC | TRB_TYPE(TRB_NORMAL);
+
+	trace_cdns3_prepare_trb(priv_ep, priv_ep->trb_pool);
+
+	cdns3_select_ep(priv_dev, priv_dev->ep0_data_dir);
+
+	writel(EP_STS_TRBERR, &regs->ep_sts);
+	writel(EP_TRADDR_TRADDR(priv_ep->trb_pool_dma), &regs->ep_traddr);
+	trace_cdns3_doorbell_ep0(priv_dev->ep0_data_dir ? "ep0in" : "ep0out",
+				 readl(&regs->ep_traddr));
+
+	/* TRB should be prepared before starting transfer */
+	writel(EP_CMD_DRDY, &regs->ep_cmd);
+
+	if (erdy)
+		writel(EP_CMD_ERDY, &priv_dev->regs->ep_cmd);
+}
+
+/**
+ * cdns3_ep0_delegate_req - Returns status of handling setup packet
+ * Setup is handled by gadget driver
+ * @priv_dev: extended gadget object
+ * @ctrl_req: pointer to received setup packet
+ *
+ * Returns zero on success or negative value on failure
+ */
+static int cdns3_ep0_delegate_req(struct cdns3_device *priv_dev,
+				  struct usb_ctrlrequest *ctrl_req)
+{
+	int ret;
+
+	spin_unlock(&priv_dev->lock);
+	priv_dev->setup_pending = 1;
+	ret = priv_dev->gadget_driver->setup(&priv_dev->gadget, ctrl_req);
+	priv_dev->setup_pending = 0;
+	spin_lock(&priv_dev->lock);
+	return ret;
+}
+
+static void cdns3_prepare_setup_packet(struct cdns3_device *priv_dev)
+{
+	priv_dev->ep0_data_dir = 0;
+	priv_dev->ep0_stage = CDNS3_SETUP_STAGE;
+	cdns3_ep0_run_transfer(priv_dev, priv_dev->setup_dma,
+			       sizeof(struct usb_ctrlrequest), 0);
+}
+
+static void cdns3_ep0_complete_setup(struct cdns3_device *priv_dev,
+				     u8 send_stall, u8 send_erdy)
+{
+	struct cdns3_endpoint *priv_ep = priv_dev->eps[0];
+	struct usb_request *request;
+
+	request = cdns3_next_request(&priv_ep->pending_req_list);
+	if (request)
+		list_del_init(&request->list);
+
+	if (send_stall) {
+		dev_dbg(priv_dev->dev, "STALL for ep0\n");
+		/* set_stall on ep0 */
+		cdns3_select_ep(priv_dev, 0x00);
+		writel(EP_CMD_SSTALL, &priv_dev->regs->ep_cmd);
+	} else {
+		cdns3_prepare_setup_packet(priv_dev);
+	}
+
+	priv_dev->ep0_stage = CDNS3_SETUP_STAGE;
+	writel((send_erdy ? EP_CMD_ERDY : 0) | EP_CMD_REQ_CMPL,
+	       &priv_dev->regs->ep_cmd);
+}
+
+/**
+ * cdns3_req_ep0_set_configuration - Handling of SET_CONFIG standard USB request
+ * @priv_dev: extended gadget object
+ * @ctrl_req: pointer to received setup packet
+ *
+ * Returns 0 if success, USB_GADGET_DELAYED_STATUS on deferred status stage,
+ * error code on error
+ */
+static int cdns3_req_ep0_set_configuration(struct cdns3_device *priv_dev,
+					   struct usb_ctrlrequest *ctrl_req)
+{
+	enum usb_device_state device_state = priv_dev->gadget.state;
+	struct cdns3_endpoint *priv_ep;
+	u32 config = le16_to_cpu(ctrl_req->wValue);
+	int result = 0;
+	int i;
+
+	switch (device_state) {
+	case USB_STATE_ADDRESS:
+		/* Configure non-control EPs */
+		for (i = 0; i < CDNS3_ENDPOINTS_MAX_COUNT; i++) {
+			priv_ep = priv_dev->eps[i];
+			if (!priv_ep)
+				continue;
+
+			if (priv_ep->flags & EP_CLAIMED)
+				cdns3_ep_config(priv_ep);
+		}
+
+		result = cdns3_ep0_delegate_req(priv_dev, ctrl_req);
+
+		if (result)
+			return result;
+
+		if (config) {
+			cdns3_set_hw_configuration(priv_dev);
+		} else {
+			cdns3_hw_reset_eps_config(priv_dev);
+			usb_gadget_set_state(&priv_dev->gadget,
+					     USB_STATE_ADDRESS);
+		}
+		break;
+	case USB_STATE_CONFIGURED:
+		result = cdns3_ep0_delegate_req(priv_dev, ctrl_req);
+
+		if (!config && !result) {
+			cdns3_hw_reset_eps_config(priv_dev);
+			usb_gadget_set_state(&priv_dev->gadget,
+					     USB_STATE_ADDRESS);
+		}
+		break;
+	default:
+		result = -EINVAL;
+	}
+
+	return result;
+}
+
+/**
+ * cdns3_req_ep0_set_address - Handling of SET_ADDRESS standard USB request
+ * @priv_dev: extended gadget object
+ * @ctrl_req: pointer to received setup packet
+ *
+ * Returns 0 if success, error code on error
+ */
+static int cdns3_req_ep0_set_address(struct cdns3_device *priv_dev,
+				     struct usb_ctrlrequest *ctrl_req)
+{
+	enum usb_device_state device_state = priv_dev->gadget.state;
+	u32 reg;
+	u32 addr;
+
+	addr = le16_to_cpu(ctrl_req->wValue);
+
+	if (addr > USB_DEVICE_MAX_ADDRESS) {
+		dev_err(priv_dev->dev,
+			"Device address (%d) cannot be greater than %d\n",
+			addr, USB_DEVICE_MAX_ADDRESS);
+		return -EINVAL;
+	}
+
+	if (device_state == USB_STATE_CONFIGURED) {
+		dev_err(priv_dev->dev,
+			"can't set_address from configured state\n");
+		return -EINVAL;
+	}
+
+	reg = readl(&priv_dev->regs->usb_cmd);
+
+	writel(reg | USB_CMD_FADDR(addr) | USB_CMD_SET_ADDR,
+	       &priv_dev->regs->usb_cmd);
+
+	usb_gadget_set_state(&priv_dev->gadget,
+			     (addr ? USB_STATE_ADDRESS : USB_STATE_DEFAULT));
+
+	return 0;
+}
+
+/**
+ * cdns3_req_ep0_get_status - Handling of GET_STATUS standard USB request
+ * @priv_dev: extended gadget object
+ * @ctrl_req: pointer to received setup packet
+ *
+ * Returns 0 if success, error code on error
+ */
+static int cdns3_req_ep0_get_status(struct cdns3_device *priv_dev,
+				    struct usb_ctrlrequest *ctrl)
+{
+	__le16 *response_pkt;
+	u16 usb_status = 0;
+	u32 recip;
+	u32 reg;
+
+	recip = ctrl->bRequestType & USB_RECIP_MASK;
+
+	switch (recip) {
+	case USB_RECIP_DEVICE:
+		/* self powered */
+		if (priv_dev->is_selfpowered)
+			usb_status = BIT(USB_DEVICE_SELF_POWERED);
+
+		if (priv_dev->wake_up_flag)
+			usb_status |= BIT(USB_DEVICE_REMOTE_WAKEUP);
+
+		if (priv_dev->gadget.speed != USB_SPEED_SUPER)
+			break;
+
+		reg = readl(&priv_dev->regs->usb_sts);
+
+		if (priv_dev->u1_allowed)
+			usb_status |= BIT(USB_DEV_STAT_U1_ENABLED);
+
+		if (priv_dev->u2_allowed)
+			usb_status |= BIT(USB_DEV_STAT_U2_ENABLED);
+
+		break;
+	case USB_RECIP_INTERFACE:
+		return cdns3_ep0_delegate_req(priv_dev, ctrl);
+	case USB_RECIP_ENDPOINT:
+		/* check if endpoint is stalled */
+		cdns3_select_ep(priv_dev, ctrl->wIndex);
+		if (EP_STS_STALL(readl(&priv_dev->regs->ep_sts)))
+			usb_status =  BIT(USB_ENDPOINT_HALT);
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	response_pkt = (__le16 *)priv_dev->setup_buf;
+	*response_pkt = cpu_to_le16(usb_status);
+
+	cdns3_ep0_run_transfer(priv_dev, priv_dev->setup_dma,
+			       sizeof(*response_pkt), 1);
+	return 0;
+}
+
+static int cdns3_ep0_feature_handle_device(struct cdns3_device *priv_dev,
+					   struct usb_ctrlrequest *ctrl,
+					   int set)
+{
+	enum usb_device_state state;
+	enum usb_device_speed speed;
+	int ret = 0;
+	u32 wValue;
+	u32 wIndex;
+	u16 tmode;
+
+	wValue = le16_to_cpu(ctrl->wValue);
+	wIndex = le16_to_cpu(ctrl->wIndex);
+	state = priv_dev->gadget.state;
+	speed = priv_dev->gadget.speed;
+
+	switch (ctrl->wValue) {
+	case USB_DEVICE_REMOTE_WAKEUP:
+		priv_dev->wake_up_flag = !!set;
+		break;
+	case USB_DEVICE_U1_ENABLE:
+		if (state != USB_STATE_CONFIGURED || speed != USB_SPEED_SUPER)
+			return -EINVAL;
+
+		priv_dev->u1_allowed = !!set;
+		break;
+	case USB_DEVICE_U2_ENABLE:
+		if (state != USB_STATE_CONFIGURED || speed != USB_SPEED_SUPER)
+			return -EINVAL;
+
+		priv_dev->u2_allowed = !!set;
+		break;
+	case USB_DEVICE_LTM_ENABLE:
+		ret = -EINVAL;
+		break;
+	case USB_DEVICE_TEST_MODE:
+		if (state != USB_STATE_CONFIGURED || speed > USB_SPEED_HIGH)
+			return -EINVAL;
+
+		tmode = le16_to_cpu(ctrl->wIndex);
+
+		if (!set || (tmode & 0xff) != 0)
+			return -EINVAL;
+
+		switch (tmode >> 8) {
+		case TEST_J:
+		case TEST_K:
+		case TEST_SE0_NAK:
+		case TEST_PACKET:
+			cdns3_ep0_complete_setup(priv_dev, 0, 1);
+			/**
+			 *  Little delay to give the controller some time
+			 * for sending status stage.
+			 * This time should be less then 3ms.
+			 */
+			usleep_range(1000, 2000);
+			cdns3_set_register_bit(&priv_dev->regs->usb_cmd,
+					       USB_CMD_STMODE |
+					       USB_STS_TMODE_SEL(tmode - 1));
+			break;
+		default:
+			ret = -EINVAL;
+		}
+		break;
+	default:
+		ret = -EINVAL;
+	}
+
+	return ret;
+}
+
+static int cdns3_ep0_feature_handle_intf(struct cdns3_device *priv_dev,
+					 struct usb_ctrlrequest *ctrl,
+					 int set)
+{
+	u32 wValue;
+	int ret = 0;
+
+	wValue = le16_to_cpu(ctrl->wValue);
+
+	switch (wValue) {
+	case USB_INTRF_FUNC_SUSPEND:
+		break;
+	default:
+		ret = -EINVAL;
+	}
+
+	return ret;
+}
+
+static int cdns3_ep0_feature_handle_endpoint(struct cdns3_device *priv_dev,
+					     struct usb_ctrlrequest *ctrl,
+					     int set)
+{
+	struct cdns3_endpoint *priv_ep;
+	int ret = 0;
+	u8 index;
+
+	if (le16_to_cpu(ctrl->wValue) != USB_ENDPOINT_HALT)
+		return -EINVAL;
+
+	if (!(ctrl->wIndex & ~USB_DIR_IN))
+		return 0;
+
+	index = cdns3_ep_addr_to_index(ctrl->wIndex);
+	priv_ep = priv_dev->eps[index];
+
+	cdns3_select_ep(priv_dev, ctrl->wIndex);
+
+	if (set) {
+		writel(EP_CMD_SSTALL, &priv_dev->regs->ep_cmd);
+		priv_ep->flags |= EP_STALL;
+	} else {
+		struct usb_request *request;
+
+		if (priv_dev->eps[index]->flags & EP_WEDGE) {
+			cdns3_select_ep(priv_dev, 0x00);
+			return 0;
+		}
+
+		writel(EP_CMD_CSTALL | EP_CMD_EPRST, &priv_dev->regs->ep_cmd);
+
+		/* wait for EPRST cleared */
+		ret = cdns3_handshake(&priv_dev->regs->ep_cmd,
+				      EP_CMD_EPRST, 0, 100);
+		if (ret)
+			return -EINVAL;
+
+		priv_ep->flags &= ~EP_STALL;
+
+		request = cdns3_next_request(&priv_ep->pending_req_list);
+		if (request)
+			cdns3_ep_run_transfer(priv_ep, request);
+	}
+	return ret;
+}
+
+/**
+ * cdns3_req_ep0_handle_feature -
+ * Handling of GET/SET_FEATURE standard USB request
+ *
+ * @priv_dev: extended gadget object
+ * @ctrl_req: pointer to received setup packet
+ * @set: must be set to 1 for SET_FEATURE request
+ *
+ * Returns 0 if success, error code on error
+ */
+static int cdns3_req_ep0_handle_feature(struct cdns3_device *priv_dev,
+					struct usb_ctrlrequest *ctrl,
+					int set)
+{
+	int ret = 0;
+	u32 recip;
+
+	recip = ctrl->bRequestType & USB_RECIP_MASK;
+
+	switch (recip) {
+	case USB_RECIP_DEVICE:
+		ret = cdns3_ep0_feature_handle_device(priv_dev, ctrl, set);
+		break;
+	case USB_RECIP_INTERFACE:
+		ret = cdns3_ep0_feature_handle_intf(priv_dev, ctrl, set);
+		break;
+	case USB_RECIP_ENDPOINT:
+		ret = cdns3_ep0_feature_handle_endpoint(priv_dev, ctrl, set);
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	return ret;
+}
+
+/**
+ * cdns3_req_ep0_set_sel - Handling of SET_SEL standard USB request
+ * @priv_dev: extended gadget object
+ * @ctrl_req: pointer to received setup packet
+ *
+ * Returns 0 if success, error code on error
+ */
+static int cdns3_req_ep0_set_sel(struct cdns3_device *priv_dev,
+				 struct usb_ctrlrequest *ctrl_req)
+{
+	if (priv_dev->gadget.state < USB_STATE_ADDRESS)
+		return -EINVAL;
+
+	if (ctrl_req->wLength != 6) {
+		dev_err(priv_dev->dev, "Set SEL should be 6 bytes, got %d\n",
+			ctrl_req->wLength);
+		return -EINVAL;
+	}
+
+	cdns3_ep0_run_transfer(priv_dev, priv_dev->setup_dma, 6, 1);
+	return 0;
+}
+
+/**
+ * cdns3_req_ep0_set_isoch_delay -
+ * Handling of GET_ISOCH_DELAY standard USB request
+ * @priv_dev: extended gadget object
+ * @ctrl_req: pointer to received setup packet
+ *
+ * Returns 0 if success, error code on error
+ */
+static int cdns3_req_ep0_set_isoch_delay(struct cdns3_device *priv_dev,
+					 struct usb_ctrlrequest *ctrl_req)
+{
+	if (ctrl_req->wIndex || ctrl_req->wLength)
+		return -EINVAL;
+
+	priv_dev->isoch_delay = ctrl_req->wValue;
+
+	return 0;
+}
+
+/**
+ * cdns3_ep0_standard_request - Handling standard USB requests
+ * @priv_dev: extended gadget object
+ * @ctrl_req: pointer to received setup packet
+ *
+ * Returns 0 if success, error code on error
+ */
+static int cdns3_ep0_standard_request(struct cdns3_device *priv_dev,
+				      struct usb_ctrlrequest *ctrl_req)
+{
+	int ret;
+
+	switch (ctrl_req->bRequest) {
+	case USB_REQ_SET_ADDRESS:
+		ret = cdns3_req_ep0_set_address(priv_dev, ctrl_req);
+		break;
+	case USB_REQ_SET_CONFIGURATION:
+		ret = cdns3_req_ep0_set_configuration(priv_dev, ctrl_req);
+		break;
+	case USB_REQ_GET_STATUS:
+		ret = cdns3_req_ep0_get_status(priv_dev, ctrl_req);
+		break;
+	case USB_REQ_CLEAR_FEATURE:
+		ret = cdns3_req_ep0_handle_feature(priv_dev, ctrl_req, 0);
+		break;
+	case USB_REQ_SET_FEATURE:
+		ret = cdns3_req_ep0_handle_feature(priv_dev, ctrl_req, 1);
+		break;
+	case USB_REQ_SET_SEL:
+		ret = cdns3_req_ep0_set_sel(priv_dev, ctrl_req);
+		break;
+	case USB_REQ_SET_ISOCH_DELAY:
+		ret = cdns3_req_ep0_set_isoch_delay(priv_dev, ctrl_req);
+		break;
+	default:
+		ret = cdns3_ep0_delegate_req(priv_dev, ctrl_req);
+		break;
+	}
+
+	return ret;
+}
+
+static void __pending_setup_status_handler(struct cdns3_device *priv_dev)
+{
+	struct usb_request *request = priv_dev->pending_status_request;
+
+	if (priv_dev->status_completion_no_call && request &&
+	    request->complete) {
+		request->complete(&priv_dev->eps[0]->endpoint, request);
+		priv_dev->status_completion_no_call = 0;
+	}
+}
+
+void cdns3_pending_setup_status_handler(struct work_struct *work)
+{
+	struct cdns3_device *priv_dev = container_of(work, struct cdns3_device,
+			pending_status_wq);
+	unsigned long flags;
+
+	spin_lock_irqsave(&priv_dev->lock, flags);
+	__pending_setup_status_handler(priv_dev);
+	spin_unlock_irqrestore(&priv_dev->lock, flags);
+}
+
+/**
+ * cdns3_gadget_ep_giveback - call struct usb_request's ->complete callback
+ * @priv_ep: The endpoint to whom the request belongs to
+ * @priv_req: The request we're giving back
+ * @status: completion code for the request
+ *
+ * Must be called with controller's lock held and interrupts disabled. This
+ * function will unmap @req and call its ->complete() callback to notify upper
+ * layers that it has completed.
+ */
+
+void cdns3_gadget_ep0_giveback(struct cdns3_device *priv_dev,
+			       int status)
+{
+	struct cdns3_endpoint *priv_ep;
+	struct usb_request *request;
+
+	priv_ep = priv_dev->eps[0];
+	request = cdns3_next_request(&priv_ep->pending_req_list);
+
+	priv_ep->dir = priv_dev->ep0_data_dir;
+	cdns3_gadget_giveback(priv_ep, to_cdns3_request(request), status);
+}
+
+/**
+ * cdns3_ep0_setup_phase - Handling setup USB requests
+ * @priv_dev: extended gadget object
+ */
+static void cdns3_ep0_setup_phase(struct cdns3_device *priv_dev)
+{
+	struct usb_ctrlrequest *ctrl = priv_dev->setup_buf;
+	struct cdns3_endpoint *priv_ep = priv_dev->eps[0];
+	int result;
+
+	priv_dev->ep0_data_dir = ctrl->bRequestType & USB_DIR_IN;
+
+	trace_cdns3_ctrl_req(ctrl);
+
+	if (!list_empty(&priv_ep->pending_req_list)) {
+		struct usb_request *request;
+
+		request = cdns3_next_request(&priv_ep->pending_req_list);
+		priv_ep->dir = priv_dev->ep0_data_dir;
+		cdns3_gadget_giveback(priv_ep, to_cdns3_request(request),
+				      -ECONNRESET);
+	}
+
+	if (le16_to_cpu(ctrl->wLength))
+		priv_dev->ep0_stage = CDNS3_DATA_STAGE;
+	else
+		priv_dev->ep0_stage = CDNS3_STATUS_STAGE;
+
+	if ((ctrl->bRequestType & USB_TYPE_MASK) == USB_TYPE_STANDARD)
+		result = cdns3_ep0_standard_request(priv_dev, ctrl);
+	else
+		result = cdns3_ep0_delegate_req(priv_dev, ctrl);
+
+	if (priv_dev->ep0_stage == CDNS3_STATUS_STAGE &&
+	    result != USB_GADGET_DELAYED_STATUS) {
+		cdns3_ep0_complete_setup(priv_dev, 0, 1);
+	} else if (result < 0) {
+		cdns3_ep0_complete_setup(priv_dev, 1, 1);
+	}
+}
+
+static void cdns3_transfer_completed(struct cdns3_device *priv_dev)
+{
+	struct cdns3_endpoint *priv_ep = priv_dev->eps[0];
+
+	if (!list_empty(&priv_ep->pending_req_list)) {
+		struct usb_request *request;
+
+		trace_cdns3_complete_trb(priv_ep, priv_ep->trb_pool);
+		request = cdns3_next_request(&priv_ep->pending_req_list);
+
+		request->actual =
+			TRB_LEN(le32_to_cpu(priv_ep->trb_pool->length));
+
+		priv_ep->dir = priv_dev->ep0_data_dir;
+		cdns3_gadget_giveback(priv_ep, to_cdns3_request(request), 0);
+	}
+
+	cdns3_ep0_complete_setup(priv_dev, 0, 0);
+}
+
+/**
+ * cdns3_check_new_setup - Check if controller receive new SETUP packet.
+ * @priv_dev: extended gadget object
+ *
+ * The SETUP packet can be kept in on-chip memory or in system memory.
+ */
+static bool cdns3_check_new_setup(struct cdns3_device *priv_dev)
+{
+	u32 ep_sts_reg;
+
+	cdns3_select_ep(priv_dev, 0 | USB_DIR_OUT);
+	ep_sts_reg = readl(&priv_dev->regs->ep_sts);
+
+	return !!(ep_sts_reg & (EP_STS_SETUP | EP_STS_STPWAIT));
+}
+
+/**
+ * cdns3_check_ep0_interrupt_proceed - Processes interrupt related to endpoint 0
+ * @priv_dev: extended gadget object
+ * @dir: USB_DIR_IN for IN direction, USB_DIR_OUT for OUT direction
+ */
+void cdns3_check_ep0_interrupt_proceed(struct cdns3_device *priv_dev, int dir)
+{
+	u32 ep_sts_reg;
+
+	cdns3_select_ep(priv_dev, dir);
+
+	ep_sts_reg = readl(&priv_dev->regs->ep_sts);
+	writel(ep_sts_reg, &priv_dev->regs->ep_sts);
+
+	trace_cdns3_ep0_irq(priv_dev, ep_sts_reg);
+
+	__pending_setup_status_handler(priv_dev);
+
+	if ((ep_sts_reg & EP_STS_SETUP)) {
+		cdns3_ep0_setup_phase(priv_dev);
+	} else if ((ep_sts_reg & EP_STS_IOC) || (ep_sts_reg & EP_STS_ISP)) {
+		priv_dev->ep0_data_dir = dir;
+		cdns3_transfer_completed(priv_dev);
+	}
+
+	if (ep_sts_reg & EP_STS_DESCMIS) {
+		if (dir == 0 && !priv_dev->setup_pending)
+			cdns3_prepare_setup_packet(priv_dev);
+	}
+}
+
+/**
+ * cdns3_gadget_ep0_enable
+ * Function shouldn't be called by gadget driver,
+ * endpoint 0 is allways active
+ */
+static int cdns3_gadget_ep0_enable(struct usb_ep *ep,
+				   const struct usb_endpoint_descriptor *desc)
+{
+	return -EINVAL;
+}
+
+/**
+ * cdns3_gadget_ep0_disable
+ * Function shouldn't be called by gadget driver,
+ * endpoint 0 is allways active
+ */
+static int cdns3_gadget_ep0_disable(struct usb_ep *ep)
+{
+	return -EINVAL;
+}
+
+/**
+ * cdns3_gadget_ep0_set_halt
+ * @ep: pointer to endpoint zero object
+ * @value: 1 for set stall, 0 for clear stall
+ *
+ * Returns 0
+ */
+static int cdns3_gadget_ep0_set_halt(struct usb_ep *ep, int value)
+{
+	/* TODO */
+	return 0;
+}
+
+/**
+ * cdns3_gadget_ep0_queue Transfer data on endpoint zero
+ * @ep: pointer to endpoint zero object
+ * @request: pointer to request object
+ * @gfp_flags: gfp flags
+ *
+ * Returns 0 on success, error code elsewhere
+ */
+static int cdns3_gadget_ep0_queue(struct usb_ep *ep,
+				  struct usb_request *request,
+				  gfp_t gfp_flags)
+{
+	struct cdns3_endpoint *priv_ep = ep_to_cdns3_ep(ep);
+	struct cdns3_device *priv_dev = priv_ep->cdns3_dev;
+	unsigned long flags;
+	int erdy_sent = 0;
+	int ret = 0;
+
+	dev_dbg(priv_dev->dev, "Queue to Ep0%s L: %d\n",
+		priv_dev->ep0_data_dir ? "IN" : "OUT",
+		request->length);
+
+	/* cancel the request if controller receive new SETUP packet. */
+	if (cdns3_check_new_setup(priv_dev))
+		return -ECONNRESET;
+
+	/* send STATUS stage. Should be called only for SET_CONFIGURATION */
+	if (priv_dev->ep0_stage == CDNS3_STATUS_STAGE) {
+		spin_lock_irqsave(&priv_dev->lock, flags);
+		cdns3_select_ep(priv_dev, 0x00);
+
+		erdy_sent = !priv_dev->hw_configured_flag;
+		cdns3_set_hw_configuration(priv_dev);
+
+		if (!erdy_sent)
+			cdns3_ep0_complete_setup(priv_dev, 0, 1);
+
+		request->actual = 0;
+		priv_dev->status_completion_no_call = true;
+		priv_dev->pending_status_request = request;
+		spin_unlock_irqrestore(&priv_dev->lock, flags);
+
+		/*
+		 * Since there is no completion interrupt for status stage,
+		 * it needs to call ->completion in software after
+		 * ep0_queue is back.
+		 */
+		queue_work(system_freezable_wq, &priv_dev->pending_status_wq);
+		return 0;
+	}
+
+	spin_lock_irqsave(&priv_dev->lock, flags);
+	if (!list_empty(&priv_ep->pending_req_list)) {
+		dev_err(priv_dev->dev,
+			"can't handle multiple requests for ep0\n");
+		spin_unlock_irqrestore(&priv_dev->lock, flags);
+		return -EBUSY;
+	}
+
+	ret = usb_gadget_map_request_by_dev(priv_dev->sysdev, request,
+					    priv_dev->ep0_data_dir);
+	if (ret) {
+		spin_unlock_irqrestore(&priv_dev->lock, flags);
+		dev_err(priv_dev->dev, "failed to map request\n");
+		return -EINVAL;
+	}
+
+	request->status = -EINPROGRESS;
+	list_add_tail(&request->list, &priv_ep->pending_req_list);
+	cdns3_ep0_run_transfer(priv_dev, request->dma, request->length, 1);
+	spin_unlock_irqrestore(&priv_dev->lock, flags);
+
+	return ret;
+}
+
+/**
+ * cdns3_gadget_ep_set_wedge Set wedge on selected endpoint
+ * @ep: endpoint object
+ *
+ * Returns 0
+ */
+int cdns3_gadget_ep_set_wedge(struct usb_ep *ep)
+{
+	struct cdns3_endpoint *priv_ep = ep_to_cdns3_ep(ep);
+	struct cdns3_device *priv_dev = priv_ep->cdns3_dev;
+
+	dev_dbg(priv_dev->dev, "Wedge for %s\n", ep->name);
+	cdns3_gadget_ep_set_halt(ep, 1);
+	priv_ep->flags |= EP_WEDGE;
+
+	return 0;
+}
+
+const struct usb_ep_ops cdns3_gadget_ep0_ops = {
+	.enable = cdns3_gadget_ep0_enable,
+	.disable = cdns3_gadget_ep0_disable,
+	.alloc_request = cdns3_gadget_ep_alloc_request,
+	.free_request = cdns3_gadget_ep_free_request,
+	.queue = cdns3_gadget_ep0_queue,
+	.dequeue = cdns3_gadget_ep_dequeue,
+	.set_halt = cdns3_gadget_ep0_set_halt,
+	.set_wedge = cdns3_gadget_ep_set_wedge,
+};
+
+/**
+ * cdns3_ep0_config - Configures default endpoint
+ * @priv_dev: extended gadget object
+ *
+ * Functions sets parameters: maximal packet size and enables interrupts
+ */
+void cdns3_ep0_config(struct cdns3_device *priv_dev)
+{
+	struct cdns3_usb_regs __iomem *regs;
+	struct cdns3_endpoint *priv_ep;
+	u32 max_packet_size = 64;
+
+	regs = priv_dev->regs;
+
+	if (priv_dev->gadget.speed == USB_SPEED_SUPER)
+		max_packet_size = 512;
+
+	priv_ep = priv_dev->eps[0];
+
+	if (!list_empty(&priv_ep->pending_req_list)) {
+		struct usb_request *request;
+
+		request = cdns3_next_request(&priv_ep->pending_req_list);
+		list_del_init(&request->list);
+	}
+
+	priv_dev->u1_allowed = 0;
+	priv_dev->u2_allowed = 0;
+
+	priv_dev->gadget.ep0->maxpacket = max_packet_size;
+	cdns3_gadget_ep0_desc.wMaxPacketSize = cpu_to_le16(max_packet_size);
+
+	/* init ep out */
+	cdns3_select_ep(priv_dev, USB_DIR_OUT);
+
+	writel(EP_CFG_ENABLE | EP_CFG_MAXPKTSIZE(max_packet_size),
+	       &regs->ep_cfg);
+
+	writel(EP_STS_EN_SETUPEN | EP_STS_EN_DESCMISEN | EP_STS_EN_TRBERREN,
+	       &regs->ep_sts_en);
+
+	/* init ep in */
+	cdns3_select_ep(priv_dev, USB_DIR_IN);
+
+	writel(EP_CFG_ENABLE | EP_CFG_MAXPKTSIZE(max_packet_size),
+	       &regs->ep_cfg);
+
+	writel(EP_STS_EN_SETUPEN | EP_STS_EN_TRBERREN, &regs->ep_sts_en);
+
+	cdns3_set_register_bit(&regs->usb_conf, USB_CONF_U1DS | USB_CONF_U2DS);
+}
+
+/**
+ * cdns3_init_ep0 Initializes software endpoint 0 of gadget
+ * @priv_dev: extended gadget object
+ * @ep_priv: extended endpoint object
+ *
+ * Returns 0 on success else error code.
+ */
+int cdns3_init_ep0(struct cdns3_device *priv_dev,
+		   struct cdns3_endpoint *priv_ep)
+{
+	sprintf(priv_ep->name, "ep0");
+
+	/* fill linux fields */
+	priv_ep->endpoint.ops = &cdns3_gadget_ep0_ops;
+	priv_ep->endpoint.maxburst = 1;
+	usb_ep_set_maxpacket_limit(&priv_ep->endpoint,
+				   CDNS3_EP0_MAX_PACKET_LIMIT);
+	priv_ep->endpoint.address = 0;
+	priv_ep->endpoint.caps.type_control = 1;
+	priv_ep->endpoint.caps.dir_in = 1;
+	priv_ep->endpoint.caps.dir_out = 1;
+	priv_ep->endpoint.name = priv_ep->name;
+	priv_ep->endpoint.desc = &cdns3_gadget_ep0_desc;
+	priv_dev->gadget.ep0 = &priv_ep->endpoint;
+	priv_ep->type = USB_ENDPOINT_XFER_CONTROL;
+
+	return cdns3_allocate_trb_pool(priv_ep);
+}
diff --git a/drivers/usb/cdns3/gadget-export.h b/drivers/usb/cdns3/gadget-export.h
new file mode 100644
index 000000000000..577469eee961
--- /dev/null
+++ b/drivers/usb/cdns3/gadget-export.h
@@ -0,0 +1,28 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Cadence USBSS DRD Driver - Gadget Export APIs.
+ *
+ * Copyright (C) 2017 NXP
+ * Copyright (C) 2017-2018 NXP
+ *
+ * Authors: Peter Chen <peter.chen@nxp.com>
+ */
+#ifndef __LINUX_CDNS3_GADGET_EXPORT
+#define __LINUX_CDNS3_GADGET_EXPORT
+
+#ifdef CONFIG_USB_CDNS3_GADGET
+
+int cdns3_gadget_init(struct cdns3 *cdns);
+void cdns3_gadget_exit(struct cdns3 *cdns);
+#else
+
+static inline int cdns3_gadget_init(struct cdns3 *cdns)
+{
+	return -ENXIO;
+}
+
+static inline void cdns3_gadget_exit(struct cdns3 *cdns) { }
+
+#endif
+
+#endif /* __LINUX_CDNS3_GADGET_EXPORT */
diff --git a/drivers/usb/cdns3/gadget.c b/drivers/usb/cdns3/gadget.c
new file mode 100644
index 000000000000..0d95eb00be37
--- /dev/null
+++ b/drivers/usb/cdns3/gadget.c
@@ -0,0 +1,2102 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Cadence USBSS DRD Driver - gadget side.
+ *
+ * Copyright (C) 2018 Cadence Design Systems.
+ * Copyright (C) 2017-2018 NXP
+ *
+ * Authors: Pawel Jez <pjez@cadence.com>,
+ *          Pawel Laszczak <pawell@cadence.com>
+ *	    Peter Chen <peter.chen@nxp.com>
+ */
+
+#include <linux/dma-mapping.h>
+#include <linux/usb/gadget.h>
+
+#include "core.h"
+#include "gadget-export.h"
+#include "gadget.h"
+
+#include "trace.h"
+
+static int __cdns3_gadget_ep_queue(struct usb_ep *ep,
+				   struct usb_request *request,
+				   gfp_t gfp_flags);
+
+/**
+ * cdns3_handshake - spin reading  until handshake completes or fails
+ * @ptr: address of device controller register to be read
+ * @mask: bits to look at in result of read
+ * @done: value of those bits when handshake succeeds
+ * @usec: timeout in microseconds
+ *
+ * Returns negative errno, or zero on success
+ *
+ * Success happens when the "mask" bits have the specified value (hardware
+ * handshake done). There are two failure modes: "usec" have passed (major
+ * hardware flakeout), or the register reads as all-ones (hardware removed).
+ */
+int cdns3_handshake(void __iomem *ptr, u32 mask, u32 done, int usec)
+{
+	u32	result;
+
+	do {
+		result = readl(ptr);
+		if (result == ~(u32)0)	/* card removed */
+			return -ENODEV;
+		result &= mask;
+		if (result == done)
+			return 0;
+		udelay(1);
+		usec--;
+	} while (usec > 0);
+	return -ETIMEDOUT;
+}
+
+/**
+ * cdns3_set_register_bit - set bit in given register.
+ * @ptr: address of device controller register to be read and changed
+ * @mask: bits requested to set
+ */
+void cdns3_set_register_bit(void __iomem *ptr, u32 mask)
+{
+	mask = readl(ptr) | mask;
+	writel(mask, ptr);
+}
+
+/**
+ * cdns3_ep_addr_to_index - Macro converts endpoint address to
+ * index of endpoint object in cdns3_device.eps[] container
+ * @ep_addr: endpoint address for which endpoint object is required
+ *
+ */
+u8 cdns3_ep_addr_to_index(u8 ep_addr)
+{
+	return (((ep_addr & 0x7F)) + ((ep_addr & USB_DIR_IN) ? 16 : 0));
+}
+
+/**
+ * cdns3_next_request - returns next request from list
+ * @list: list containing requests
+ *
+ * Returns request or NULL if no requests in list
+ */
+struct usb_request *cdns3_next_request(struct list_head *list)
+{
+	if (list_empty(list))
+		return NULL;
+	return list_first_entry_or_null(list, struct usb_request, list);
+}
+
+/**
+ * cdns3_next_priv_request - returns next request from list
+ * @list: list containing requests
+ *
+ * Returns request or NULL if no requests in list
+ */
+struct cdns3_request *cdns3_next_priv_request(struct list_head *list)
+{
+	if (list_empty(list))
+		return NULL;
+	return list_first_entry_or_null(list, struct cdns3_request, list);
+}
+
+/**
+ * select_ep - selects endpoint
+ * @priv_dev:  extended gadget object
+ * @ep: endpoint address
+ */
+void cdns3_select_ep(struct cdns3_device *priv_dev, u32 ep)
+{
+	if (priv_dev->selected_ep == ep)
+		return;
+
+	priv_dev->selected_ep = ep;
+	writel(ep, &priv_dev->regs->ep_sel);
+}
+
+dma_addr_t cdns3_trb_virt_to_dma(struct cdns3_endpoint *priv_ep,
+				 struct cdns3_trb *trb)
+{
+	u32 offset = (char *)trb - (char *)priv_ep->trb_pool;
+
+	return priv_ep->trb_pool_dma + offset;
+}
+
+int cdns3_ring_size(struct cdns3_endpoint *priv_ep)
+{
+	switch (priv_ep->type) {
+	case USB_ENDPOINT_XFER_ISOC:
+		return TRB_ISO_RING_SIZE;
+	case USB_ENDPOINT_XFER_CONTROL:
+		return TRB_CTRL_RING_SIZE;
+	default:
+		return TRB_RING_SIZE;
+	}
+}
+
+/**
+ * cdns3_allocate_trb_pool - Allocates TRB's pool for selected endpoint
+ * @priv_ep:  endpoint object
+ *
+ * Function will return 0 on success or -ENOMEM on allocation error
+ */
+int cdns3_allocate_trb_pool(struct cdns3_endpoint *priv_ep)
+{
+	struct cdns3_device *priv_dev = priv_ep->cdns3_dev;
+	int ring_size = cdns3_ring_size(priv_ep);
+	struct cdns3_trb *link_trb;
+
+	if (!priv_ep->trb_pool) {
+		priv_ep->trb_pool = dma_zalloc_coherent(priv_dev->sysdev,
+							ring_size,
+							&priv_ep->trb_pool_dma,
+							GFP_DMA);
+		if (!priv_ep->trb_pool)
+			return -ENOMEM;
+	} else {
+		memset(priv_ep->trb_pool, 0, ring_size);
+	}
+
+	if (!priv_ep->num)
+		return 0;
+
+	if (!priv_ep->aligned_buff) {
+		void *buff = dma_alloc_coherent(priv_dev->sysdev,
+						CDNS3_ALIGNED_BUF_SIZE,
+						&priv_ep->aligned_dma_addr,
+						GFP_DMA);
+
+		priv_ep->aligned_buff  = buff;
+		if (!priv_ep->aligned_buff) {
+			dma_free_coherent(priv_dev->sysdev,
+					  ring_size,
+					  priv_ep->trb_pool,
+					  priv_ep->trb_pool_dma);
+			priv_ep->trb_pool = NULL;
+
+			return -ENOMEM;
+		}
+	}
+
+	priv_ep->num_trbs = ring_size / TRB_SIZE;
+	/* Initialize the last TRB as Link TRB */
+	link_trb = (priv_ep->trb_pool + (priv_ep->num_trbs - 1));
+	link_trb->buffer = TRB_BUFFER(priv_ep->trb_pool_dma);
+	link_trb->control = TRB_CYCLE | TRB_TYPE(TRB_LINK) |
+			    TRB_CHAIN | TRB_TOGGLE;
+
+	return 0;
+}
+
+static void cdns3_free_trb_pool(struct cdns3_endpoint *priv_ep)
+{
+	struct cdns3_device *priv_dev = priv_ep->cdns3_dev;
+
+	if (priv_ep->trb_pool) {
+		dma_free_coherent(priv_dev->sysdev,
+				  cdns3_ring_size(priv_ep),
+				  priv_ep->trb_pool, priv_ep->trb_pool_dma);
+		priv_ep->trb_pool = NULL;
+	}
+
+	if (priv_ep->aligned_buff) {
+		dma_free_coherent(priv_dev->sysdev, CDNS3_ALIGNED_BUF_SIZE,
+				  priv_ep->aligned_buff,
+				  priv_ep->aligned_dma_addr);
+		priv_ep->aligned_buff = NULL;
+	}
+}
+
+/**
+ * cdns3_data_flush - flush data at onchip buffer
+ * @priv_ep: endpoint object
+ *
+ * Endpoint must be selected before call to this function
+ *
+ * Returns zero on success or negative value on failure
+ */
+static int cdns3_data_flush(struct cdns3_endpoint *priv_ep)
+{
+	struct cdns3_device *priv_dev = priv_ep->cdns3_dev;
+
+	writel(EP_CMD_DFLUSH, &priv_dev->regs->ep_cmd);
+
+	/* wait for DFLUSH cleared */
+	return cdns3_handshake(&priv_dev->regs->ep_cmd, EP_CMD_DFLUSH, 0, 100);
+}
+
+/**
+ * cdns3_ep_stall_flush - Stalls and flushes selected endpoint
+ * @priv_ep: endpoint object
+ *
+ * Endpoint must be selected before call to this function
+ */
+static void cdns3_ep_stall_flush(struct cdns3_endpoint *priv_ep)
+{
+	struct cdns3_device *priv_dev = priv_ep->cdns3_dev;
+
+	writel(EP_CMD_DFLUSH | EP_CMD_ERDY | EP_CMD_SSTALL,
+	       &priv_dev->regs->ep_cmd);
+
+	/* wait for DFLUSH cleared */
+	cdns3_handshake(&priv_dev->regs->ep_cmd, EP_CMD_DFLUSH, 0, 100);
+	priv_ep->flags |= EP_STALL;
+}
+
+/**
+ * cdns3_hw_reset_eps_config - reset endpoints configuration kept by controller.
+ * @priv_dev: extended gadget object
+ */
+void cdns3_hw_reset_eps_config(struct cdns3_device *priv_dev)
+{
+	writel(USB_CONF_CFGRST, &priv_dev->regs->usb_conf);
+
+	cdns3_allow_enable_l1(priv_dev, 0);
+	priv_dev->hw_configured_flag = 0;
+	priv_dev->onchip_mem_allocated_size = 0;
+}
+
+/**
+ * cdns3_ep_inc_trb - increment a trb index.
+ * @index: Pointer to the TRB index to increment.
+ * @cs: Cycle state
+ * @trb_in_seg: number of TRBs in segment
+ *
+ * The index should never point to the link TRB. After incrementing,
+ * if it is point to the link TRB, wrap around to the beginning and revert
+ * cycle state bit The
+ * link TRB is always at the last TRB entry.
+ */
+static void cdns3_ep_inc_trb(int *index, u8 *cs, int trb_in_seg)
+{
+	(*index)++;
+	if (*index == (trb_in_seg - 1)) {
+		*index = 0;
+		*cs ^=  1;
+	}
+}
+
+/**
+ * cdns3_ep_inc_enq - increment endpoint's enqueue pointer
+ * @priv_ep: The endpoint whose enqueue pointer we're incrementing
+ */
+static void cdns3_ep_inc_enq(struct cdns3_endpoint *priv_ep)
+{
+	priv_ep->free_trbs--;
+	cdns3_ep_inc_trb(&priv_ep->enqueue, &priv_ep->pcs, priv_ep->num_trbs);
+}
+
+/**
+ * cdns3_ep_inc_deq - increment endpoint's dequeue pointer
+ * @priv_ep: The endpoint whose dequeue pointer we're incrementing
+ */
+static void cdns3_ep_inc_deq(struct cdns3_endpoint *priv_ep)
+{
+	priv_ep->free_trbs++;
+	cdns3_ep_inc_trb(&priv_ep->dequeue, &priv_ep->ccs, priv_ep->num_trbs);
+}
+
+/**
+ * cdns3_allow_enable_l1 - enable/disable permits to transition to L1.
+ * @priv_dev: Extended gadget object
+ * @enable: Enable/disable permit to transition to L1.
+ *
+ * If bit USB_CONF_L1EN is set and device receive Extended Token packet,
+ * then controller answer with ACK handshake.
+ * If bit USB_CONF_L1DS is set and device receive Extended Token packet,
+ * then controller answer with NYET handshake.
+ */
+void cdns3_allow_enable_l1(struct cdns3_device *priv_dev, int enable)
+{
+	if (enable)
+		writel(USB_CONF_L1EN, &priv_dev->regs->usb_conf);
+	else
+		writel(USB_CONF_L1DS, &priv_dev->regs->usb_conf);
+}
+
+enum usb_device_speed cdns3_get_speed(struct cdns3_device *priv_dev)
+{
+	u32 reg;
+
+	reg = readl(&priv_dev->regs->usb_sts);
+
+	if (DEV_SUPERSPEED(reg))
+		return USB_SPEED_SUPER;
+	else if (DEV_HIGHSPEED(reg))
+		return USB_SPEED_HIGH;
+	else if (DEV_FULLSPEED(reg))
+		return USB_SPEED_FULL;
+	else if (DEV_LOWSPEED(reg))
+		return USB_SPEED_LOW;
+	return USB_SPEED_UNKNOWN;
+}
+
+/**
+ * cdns3_start_all_request - add to ring all request not started
+ * @priv_dev: Extended gadget object
+ * @priv_ep: The endpoint for whom request will be started.
+ *
+ * Returns return ENOMEM if transfer ring i not enough TRBs to start
+ *         all requests.
+ */
+static int cdns3_start_all_request(struct cdns3_device *priv_dev,
+				   struct cdns3_endpoint *priv_ep)
+{
+	struct cdns3_request *priv_req;
+	struct usb_request *request;
+	int ret = 0;
+
+	while (!list_empty(&priv_ep->deferred_req_list)) {
+		request = cdns3_next_request(&priv_ep->deferred_req_list);
+		priv_req = to_cdns3_request(request);
+
+		ret = cdns3_ep_run_transfer(priv_ep, request);
+		if (ret)
+			return ret;
+
+		list_del(&request->list);
+		list_add_tail(&request->list,
+			      &priv_ep->pending_req_list);
+	}
+
+	priv_ep->flags &= ~EP_RING_FULL;
+	return ret;
+}
+
+/**
+ * cdns3_descmiss_copy_data copy data from internal requests to request queued
+ * by class driver.
+ * @priv_ep: extended endpoint object
+ * @request: request object
+ */
+static void cdns3_descmiss_copy_data(struct cdns3_endpoint *priv_ep,
+				     struct usb_request *request)
+{
+	struct usb_request *descmiss_req;
+	struct cdns3_request *descmiss_priv_req;
+
+	while (!list_empty(&priv_ep->descmiss_req_list)) {
+		int chunk_end;
+		int length;
+
+		descmiss_priv_req =
+			cdns3_next_priv_request(&priv_ep->descmiss_req_list);
+		descmiss_req = &descmiss_priv_req->request;
+
+		/* driver can't touch pending request */
+		if (descmiss_priv_req->flags & REQUEST_PENDING)
+			break;
+
+		chunk_end = descmiss_priv_req->flags & REQUEST_INTERNAL_CH;
+		length = request->actual + descmiss_req->actual;
+
+		if (length <= request->length) {
+			memcpy(&((u8 *)request->buf)[request->actual],
+			       descmiss_req->buf,
+			       descmiss_req->actual);
+			request->actual = length;
+		} else {
+			/* It should never occures */
+			request->status = -ENOMEM;
+		}
+
+		list_del_init(&descmiss_priv_req->list);
+
+		kfree(descmiss_req->buf);
+		cdns3_gadget_ep_free_request(&priv_ep->endpoint, descmiss_req);
+
+		if (!chunk_end)
+			break;
+	}
+}
+
+/**
+ * cdns3_gadget_giveback - call struct usb_request's ->complete callback
+ * @priv_ep: The endpoint to whom the request belongs to
+ * @priv_req: The request we're giving back
+ * @status: completion code for the request
+ *
+ * Must be called with controller's lock held and interrupts disabled. This
+ * function will unmap @req and call its ->complete() callback to notify upper
+ * layers that it has completed.
+ */
+void cdns3_gadget_giveback(struct cdns3_endpoint *priv_ep,
+			   struct cdns3_request *priv_req,
+			   int status)
+{
+	struct cdns3_device *priv_dev = priv_ep->cdns3_dev;
+	struct usb_request *request = &priv_req->request;
+
+	list_del_init(&request->list);
+
+	if (request->status == -EINPROGRESS)
+		request->status = status;
+
+	usb_gadget_unmap_request_by_dev(priv_dev->sysdev, request,
+					priv_ep->dir);
+
+	priv_req->flags &= ~REQUEST_PENDING;
+	trace_cdns3_gadget_giveback(priv_req);
+
+	/* WA1: */
+	if (priv_ep->flags & EP_QUIRK_EXTRA_BUF_EN &&
+	    priv_req->flags & REQUEST_INTERNAL) {
+		struct usb_request *req;
+
+		req = cdns3_next_request(&priv_ep->deferred_req_list);
+		request = req;
+		priv_ep->descmis_req = NULL;
+
+		if (!req)
+			return;
+
+		cdns3_descmiss_copy_data(priv_ep, req);
+		if (!(priv_ep->flags & EP_QUIRK_END_TRANSFER) &&
+		    req->length != req->actual) {
+			/* wait for next part of transfer */
+			return;
+		}
+
+		if (req->status == -EINPROGRESS)
+			req->status = 0;
+
+		list_del_init(&req->list);
+		cdns3_start_all_request(priv_dev, priv_ep);
+	}
+
+	/* Start all not pending request */
+	if (priv_ep->flags & EP_RING_FULL)
+		cdns3_start_all_request(priv_dev, priv_ep);
+
+	if (request->complete) {
+		spin_unlock(&priv_dev->lock);
+		usb_gadget_giveback_request(&priv_ep->endpoint,
+					    request);
+		spin_lock(&priv_dev->lock);
+	}
+
+	if (request->buf == priv_dev->zlp_buf)
+		cdns3_gadget_ep_free_request(&priv_ep->endpoint, request);
+}
+
+/**
+ * cdns3_ep_run_transfer - start transfer on no-default endpoint hardware
+ * @priv_ep: endpoint object
+ *
+ * Returns zero on success or negative value on failure
+ */
+int cdns3_ep_run_transfer(struct cdns3_endpoint *priv_ep,
+			  struct usb_request *request)
+{
+	struct cdns3_device *priv_dev = priv_ep->cdns3_dev;
+	struct cdns3_request *priv_req;
+	struct cdns3_trb *trb;
+	dma_addr_t trb_dma;
+	int sg_iter = 0;
+	u32 first_pcs;
+	int  num_trb;
+	int address;
+	int pcs;
+
+	if (priv_ep->type == USB_ENDPOINT_XFER_ISOC)
+		num_trb = priv_ep->interval;
+	else
+		num_trb = request->num_sgs ? request->num_sgs : 1;
+
+	if (num_trb > priv_ep->free_trbs) {
+		priv_ep->flags |= EP_RING_FULL;
+		return -ENOBUFS;
+	}
+
+	priv_req = to_cdns3_request(request);
+	address = priv_ep->endpoint.desc->bEndpointAddress;
+
+	priv_ep->flags |= EP_PENDING_REQUEST;
+	trb_dma = request->dma;
+
+	/* must allocate buffer aligned to 8 */
+	if ((request->dma % 8)) {
+		if (request->length <= CDNS3_ALIGNED_BUF_SIZE) {
+			memcpy(priv_ep->aligned_buff, request->buf,
+			       request->length);
+			trb_dma = priv_ep->aligned_dma_addr;
+		} else {
+			return -ENOMEM;
+		}
+	}
+
+	trb = priv_ep->trb_pool + priv_ep->enqueue;
+	priv_req->start_trb = priv_ep->enqueue;
+	priv_req->trb = trb;
+
+	/* prepare ring */
+	if ((priv_ep->enqueue + num_trb)  >= (priv_ep->num_trbs - 1)) {
+		/*updating C bt in  Link TRB before starting DMA*/
+		struct cdns3_trb *link_trb = priv_ep->trb_pool +
+					     (priv_ep->num_trbs - 1);
+		link_trb->control = ((priv_ep->pcs) ? TRB_CYCLE : 0) |
+				    TRB_TYPE(TRB_LINK) | TRB_CHAIN |
+				    TRB_TOGGLE;
+	}
+
+	first_pcs = priv_ep->pcs ? TRB_CYCLE : 0;
+
+	do {
+	/* fill TRB */
+		trb->buffer = TRB_BUFFER(request->num_sgs == 0
+				? trb_dma : request->sg[sg_iter].dma_address);
+
+		trb->length = TRB_BURST_LEN(16) |
+		    TRB_LEN(request->num_sgs == 0 ?
+				request->length : request->sg[sg_iter].length);
+
+		trb->control = TRB_TYPE(TRB_NORMAL);
+		pcs = priv_ep->pcs ? TRB_CYCLE : 0;
+
+		/*
+		 * first trb should be prepared as last to avoid processing
+		 *  transfer to early
+		 */
+		if (sg_iter != 0)
+			trb->control |= pcs;
+
+		if (priv_ep->type == USB_ENDPOINT_XFER_ISOC  && !priv_ep->dir) {
+			trb->control |= TRB_IOC | TRB_ISP;
+		} else {
+			/* for last element in TD or in SG list */
+			if (sg_iter == (num_trb - 1) && sg_iter != 0)
+				trb->control |= pcs | TRB_IOC | TRB_ISP;
+		}
+		++sg_iter;
+		priv_req->end_trb = priv_ep->enqueue;
+		cdns3_ep_inc_enq(priv_ep);
+		trb = priv_ep->trb_pool + priv_ep->enqueue;
+	} while (sg_iter < num_trb);
+
+	trb = priv_req->trb;
+
+	/*
+	 * Memory barrier = Cycle Bit must be set before trb->length  and
+	 * trb->buffer fields.
+	 */
+	wmb();
+
+	priv_req->flags |= REQUEST_PENDING;
+
+	/* give the TD to the consumer*/
+	if (sg_iter == 1)
+		trb->control |= first_pcs | TRB_IOC | TRB_ISP;
+	else
+		trb->control |= first_pcs;
+
+	trace_cdns3_prepare_trb(priv_ep, priv_req->trb);
+	trace_cdns3_ring(priv_ep);
+
+	/* arm transfer on selected endpoint */
+	cdns3_select_ep(priv_ep->cdns3_dev, address);
+
+	/*
+	 * For DMULT mode we can set address to transfer ring only once after
+	 * enabling endpoint.
+	 */
+	if (priv_ep->flags & EP_UPDATE_EP_TRBADDR) {
+		writel(EP_TRADDR_TRADDR(priv_ep->trb_pool_dma),
+		       &priv_dev->regs->ep_traddr);
+		priv_ep->flags &= ~EP_UPDATE_EP_TRBADDR;
+	}
+
+	/*clearing TRBERR and EP_STS_DESCMIS before seting DRDY*/
+	writel(EP_STS_TRBERR | EP_STS_DESCMIS, &priv_dev->regs->ep_sts);
+	trace_cdns3_doorbell_epx(priv_ep->name,
+				 readl(&priv_dev->regs->ep_traddr));
+	writel(EP_CMD_DRDY, &priv_dev->regs->ep_cmd);
+
+	return 0;
+}
+
+void cdns3_set_hw_configuration(struct cdns3_device *priv_dev)
+{
+	struct cdns3_endpoint *priv_ep;
+	struct usb_ep *ep;
+	int result = 0;
+
+	if (priv_dev->hw_configured_flag)
+		return;
+
+	writel(USB_CONF_CFGSET, &priv_dev->regs->usb_conf);
+	writel(EP_CMD_ERDY | EP_CMD_REQ_CMPL, &priv_dev->regs->ep_cmd);
+
+	cdns3_set_register_bit(&priv_dev->regs->usb_conf,
+			       USB_CONF_U1EN | USB_CONF_U2EN);
+
+	/* wait until configuration set */
+	result = cdns3_handshake(&priv_dev->regs->usb_sts,
+				 USB_STS_CFGSTS_MASK, 1, 100);
+
+	priv_dev->hw_configured_flag = 1;
+	cdns3_allow_enable_l1(priv_dev, 1);
+
+	list_for_each_entry(ep, &priv_dev->gadget.ep_list, ep_list) {
+		if (ep->enabled) {
+			priv_ep = ep_to_cdns3_ep(ep);
+			cdns3_start_all_request(priv_dev, priv_ep);
+		}
+	}
+}
+
+/**
+ * cdns3_request_handled - check whether request has been handled by DMA
+ *
+ * @priv_ep: extended endpoint object.
+ * @priv_req: request object for checking
+ *
+ * Endpoint must be selected before invoking this function.
+ *
+ * Returns false if request has not been handled by DMA, else returns true.
+ *
+ * SR - start ring
+ * ER -  end ring
+ * DQ = priv_ep->dequeue - dequeue position
+ * EQ = priv_ep->enqueue -  enqueue position
+ * ST = priv_req->start_trb - index of first TRB in transfer ring
+ * ET = priv_req->end_trb - index of last TRB in transfer ring
+ * CI = current_index - index of processed TRB by DMA.
+ *
+ * As first step, function checks if cycle bit for priv_req->start_trb is
+ * correct.
+ *
+ * some rules:
+ * 1. priv_ep->dequeue never exceed current_index.
+ * 2  priv_ep->enqueue never exceed priv_ep->dequeue
+ *
+ * Then We can split recognition into two parts:
+ * Case 1 - priv_ep->dequeue < current_index
+ *      SR ... EQ ... DQ ... CI ... ER
+ *      SR ... DQ ... CI ... EQ ... ER
+ *
+ *      Request has been handled by DMA if ST and ET is between DQ and CI.
+ *
+ * Case 2 - priv_ep->dequeue > current_index
+ * This situation take place when CI go through the LINK TRB at the end of
+ * transfer ring.
+ *      SR ... CI ... EQ ... DQ ... ER
+ *
+ *      Request has been handled by DMA if ET is less then CI or
+ *      ET is greater or equal DQ.
+ */
+static bool cdns3_request_handled(struct cdns3_endpoint *priv_ep,
+				  struct cdns3_request *priv_req)
+{
+	struct cdns3_device *priv_dev = priv_ep->cdns3_dev;
+	struct cdns3_trb *trb = priv_req->trb;
+	int current_index = 0;
+	int handled = 0;
+
+	current_index = (readl(&priv_dev->regs->ep_traddr) -
+			 priv_ep->trb_pool_dma) / TRB_SIZE;
+
+	trb = &priv_ep->trb_pool[priv_req->start_trb];
+
+	if ((trb->control  & TRB_CYCLE) != priv_ep->ccs)
+		goto finish;
+
+	if (priv_ep->dequeue < current_index) {
+		if ((current_index == (priv_ep->num_trbs - 1)) &&
+		    !priv_ep->dequeue)
+			goto finish;
+
+		if (priv_req->end_trb >= priv_ep->dequeue &&
+		    priv_req->end_trb < current_index)
+			handled = 1;
+	} else if (priv_ep->dequeue  > current_index) {
+		if (priv_req->end_trb  < current_index ||
+		    priv_req->end_trb >= priv_ep->dequeue)
+			handled = 1;
+	}
+
+finish:
+	trace_cdns3_request_handled(priv_req, current_index, handled);
+
+	return handled;
+}
+
+static void cdns3_transfer_completed(struct cdns3_device *priv_dev,
+				     struct cdns3_endpoint *priv_ep)
+{
+	struct cdns3_request *priv_req;
+	struct usb_request *request;
+	struct cdns3_trb *trb;
+	int current_trb;
+
+	while (!list_empty(&priv_ep->pending_req_list)) {
+		request = cdns3_next_request(&priv_ep->pending_req_list);
+		priv_req = to_cdns3_request(request);
+
+		if (!cdns3_request_handled(priv_ep, priv_req))
+			return;
+
+		if (request->dma % 8 && priv_ep->dir == USB_DIR_OUT)
+			memcpy(request->buf, priv_ep->aligned_buff,
+			       request->length);
+
+		trb = priv_ep->trb_pool + priv_ep->dequeue;
+		trace_cdns3_complete_trb(priv_ep, trb);
+
+		if (trb != priv_req->trb)
+			dev_warn(priv_dev->dev,
+				 "request_trb=0x%p, queue_trb=0x%p\n",
+				 priv_req->trb, trb);
+
+		request->actual = TRB_LEN(le32_to_cpu(trb->length));
+		current_trb = priv_req->start_trb;
+
+		while (current_trb != priv_req->end_trb) {
+			cdns3_ep_inc_deq(priv_ep);
+			current_trb = priv_ep->dequeue;
+		}
+
+		cdns3_ep_inc_deq(priv_ep);
+		cdns3_gadget_giveback(priv_ep, priv_req, 0);
+	}
+	priv_ep->flags &= ~EP_PENDING_REQUEST;
+}
+
+/**
+ * cdns3_descmissing_packet - handles descriptor missing event.
+ * @priv_dev: extended gadget object
+ *
+ * WA1: Controller for OUT endpoints has shared on-chip buffers for all incoming
+ * packets, including ep0out. It's FIFO buffer, so packets must be handle by DMA
+ * in correct order. If the first packet in the buffer will not be handled,
+ * then the following packets directed for other endpoints and  functions
+ * will be blocked.
+ * Additionally the packets directed to one endpoint can block entire on-chip
+ * buffers. In this case transfer to other endpoints also will blocked.
+ *
+ * To resolve this issue after raising the descriptor missing interrupt
+ * driver prepares internal usb_request object and use it to arm DMA transfer.
+ *
+ * The problematic situation was observed in case when endpoint has been enabled
+ * but no usb_request were queued. Driver try detects such endpoints and will
+ * use this workaround only for these endpoint.
+ *
+ * Driver use limited number of buffer. This number can be set by macro
+ * CDNS_WA1_NUM_BUFFERS.
+ *
+ * Such blocking situation was observed on ACM gadget. For this function
+ * host send OUT data packet but ACM function is not prepared for this packet.
+ *
+ * It's limitation of controller but maybe this issues should be fixed in
+ * function driver.
+ */
+static int cdns3_descmissing_packet(struct cdns3_endpoint *priv_ep)
+{
+	struct cdns3_request *priv_req;
+	struct usb_request *request;
+
+	if (priv_ep->flags & EP_QUIRK_EXTRA_BUF_DET) {
+		priv_ep->flags &= ~EP_QUIRK_EXTRA_BUF_DET;
+		priv_ep->flags |= EP_QUIRK_EXTRA_BUF_EN;
+	}
+
+	request = cdns3_gadget_ep_alloc_request(&priv_ep->endpoint,
+						GFP_ATOMIC);
+	if (!request)
+		return -ENOMEM;
+
+	priv_req = to_cdns3_request(request);
+	priv_req->flags |= REQUEST_INTERNAL;
+
+	/* if this field is still assigned it indicate that transfer related
+	 * with this request has not been finished yet. Driver in this
+	 * case simply allocate next request and assign flag REQUEST_INTERNAL_CH
+	 * flag to previous one. It will indicate that current request is
+	 * part of the previous one.
+	 */
+	if (priv_ep->descmis_req)
+		priv_ep->descmis_req->flags |= REQUEST_INTERNAL_CH;
+
+	priv_req->request.buf = kzalloc(CDNS3_DESCMIS_BUF_SIZE,
+					GFP_ATOMIC);
+	if (!priv_req) {
+		cdns3_gadget_ep_free_request(&priv_ep->endpoint, request);
+		return -ENOMEM;
+	}
+
+	priv_req->request.length = CDNS3_DESCMIS_BUF_SIZE;
+	priv_ep->descmis_req = priv_req;
+
+	__cdns3_gadget_ep_queue(&priv_ep->endpoint,
+				&priv_ep->descmis_req->request,
+				GFP_ATOMIC);
+
+	return 0;
+}
+
+/**
+ * cdns3_check_ep_interrupt_proceed - Processes interrupt related to endpoint
+ * @priv_ep: endpoint object
+ *
+ * Returns 0
+ */
+static int cdns3_check_ep_interrupt_proceed(struct cdns3_endpoint *priv_ep)
+{
+	struct cdns3_device *priv_dev = priv_ep->cdns3_dev;
+	u32 ep_sts_reg;
+
+	cdns3_select_ep(priv_dev, priv_ep->endpoint.address);
+
+	trace_cdns3_epx_irq(priv_dev, priv_ep);
+
+	ep_sts_reg = readl(&priv_dev->regs->ep_sts);
+	writel(ep_sts_reg, &priv_dev->regs->ep_sts);
+
+	if ((ep_sts_reg & EP_STS_IOC) || (ep_sts_reg & EP_STS_ISP)) {
+		if (priv_ep->flags & EP_QUIRK_EXTRA_BUF_EN) {
+			if (ep_sts_reg & EP_STS_ISP)
+				priv_ep->flags |= EP_QUIRK_END_TRANSFER;
+			else
+				priv_ep->flags &= ~EP_QUIRK_END_TRANSFER;
+		}
+		cdns3_transfer_completed(priv_dev, priv_ep);
+	}
+
+	/*
+	 * For isochronous transfer driver completes request on IOC or on
+	 * TRBERR. IOC appears only when device receive OUT data packet.
+	 * If host disable stream or lost some packet then the only way to
+	 * finish all queued transfer is to do it on TRBERR event.
+	 */
+	if ((ep_sts_reg & EP_STS_TRBERR) &&
+	    priv_ep->type == USB_ENDPOINT_XFER_ISOC)
+		cdns3_transfer_completed(priv_dev, priv_ep);
+
+	/*
+	 * WA1: this condition should only be meet when
+	 * priv_ep->flags & EP_QUIRK_EXTRA_BUF_DET or
+	 * priv_ep->flags & EP_QUIRK_EXTRA_BUF_EN.
+	 * In other cases this interrupt will be disabled/
+	 */
+	if (ep_sts_reg & EP_STS_DESCMIS) {
+		int err;
+
+		err = cdns3_descmissing_packet(priv_ep);
+		if (err)
+			dev_err(priv_dev->dev,
+				"Failed: No sufficient memory for DESCMIS\n");
+	}
+
+	return 0;
+}
+
+/**
+ * cdns3_check_usb_interrupt_proceed - Processes interrupt related to device
+ * @priv_dev: extended gadget object
+ * @usb_ists: bitmap representation of device's reported interrupts
+ * (usb_ists register value)
+ */
+static void cdns3_check_usb_interrupt_proceed(struct cdns3_device *priv_dev,
+					      u32 usb_ists)
+{
+	int speed = 0;
+
+	trace_cdns3_usb_irq(priv_dev, usb_ists);
+	/* Connection detected */
+	if (usb_ists & (USB_ISTS_CON2I | USB_ISTS_CONI)) {
+		speed = cdns3_get_speed(priv_dev);
+		priv_dev->gadget.speed = speed;
+		usb_gadget_set_state(&priv_dev->gadget, USB_STATE_POWERED);
+		cdns3_ep0_config(priv_dev);
+	}
+
+	/* Disconnection detected */
+	if (usb_ists & (USB_ISTS_DIS2I | USB_ISTS_DISI)) {
+		if (priv_dev->gadget_driver &&
+		    priv_dev->gadget_driver->disconnect) {
+			spin_unlock(&priv_dev->lock);
+			priv_dev->gadget_driver->disconnect(&priv_dev->gadget);
+			spin_lock(&priv_dev->lock);
+		}
+
+		priv_dev->gadget.speed = USB_SPEED_UNKNOWN;
+		usb_gadget_set_state(&priv_dev->gadget, USB_STATE_NOTATTACHED);
+		cdns3_hw_reset_eps_config(priv_dev);
+	}
+
+	/* reset*/
+	if (usb_ists & (USB_ISTS_UWRESI | USB_ISTS_UHRESI | USB_ISTS_U2RESI)) {
+		/*read again to check the actuall speed*/
+		speed = cdns3_get_speed(priv_dev);
+		usb_gadget_set_state(&priv_dev->gadget, USB_STATE_DEFAULT);
+		priv_dev->gadget.speed = speed;
+		cdns3_hw_reset_eps_config(priv_dev);
+		cdns3_ep0_config(priv_dev);
+	}
+}
+
+/**
+ * cdns3_device_irq_handler- interrupt handler for device part of controller
+ *
+ * @irq: irq number for cdns3 core device
+ * @data: structure of cdns3
+ *
+ * Returns IRQ_HANDLED or IRQ_NONE
+ */
+static irqreturn_t cdns3_device_irq_handler(int irq, void *data)
+{
+	struct cdns3_device *priv_dev;
+	struct cdns3 *cdns = data;
+	irqreturn_t ret = IRQ_NONE;
+	unsigned long flags;
+	u32 reg;
+
+	priv_dev = cdns->gadget_dev;
+	spin_lock_irqsave(&priv_dev->lock, flags);
+
+	/* check USB device interrupt */
+	reg = readl(&priv_dev->regs->usb_ists);
+	writel(reg, &priv_dev->regs->usb_ists);
+
+	if (reg) {
+		cdns3_check_usb_interrupt_proceed(priv_dev, reg);
+		ret = IRQ_HANDLED;
+	}
+
+	/* check endpoint interrupt */
+	reg = readl(&priv_dev->regs->ep_ists);
+
+	if (reg) {
+		reg = ~reg & readl(&priv_dev->regs->ep_ien);
+		/* mask deferred interrupt. */
+		writel(reg, &priv_dev->regs->ep_ien);
+		ret = IRQ_WAKE_THREAD;
+	}
+
+	spin_unlock_irqrestore(&priv_dev->lock, flags);
+	return ret;
+}
+
+/**
+ * cdns3_device_thread_irq_handler- interrupt handler for device part
+ * of controller
+ *
+ * @irq: irq number for cdns3 core device
+ * @data: structure of cdns3
+ *
+ * Returns IRQ_HANDLED or IRQ_NONE
+ */
+static irqreturn_t cdns3_device_thread_irq_handler(int irq, void *data)
+{
+	struct cdns3_device *priv_dev;
+	struct cdns3 *cdns = data;
+	irqreturn_t ret = IRQ_NONE;
+	unsigned long flags;
+	u32 ep_ien;
+	int bit;
+	u32 reg;
+
+	priv_dev = cdns->gadget_dev;
+	spin_lock_irqsave(&priv_dev->lock, flags);
+
+	reg = readl(&priv_dev->regs->ep_ists);
+	ep_ien = reg;
+
+	/* handle default endpoint OUT */
+	if (reg & EP_ISTS_EP_OUT0) {
+		cdns3_check_ep0_interrupt_proceed(priv_dev, USB_DIR_OUT);
+		ret = IRQ_HANDLED;
+	}
+
+	/* handle default endpoint IN */
+	if (reg & EP_ISTS_EP_IN0) {
+		cdns3_check_ep0_interrupt_proceed(priv_dev, USB_DIR_IN);
+		ret = IRQ_HANDLED;
+	}
+
+	/* check if interrupt from non default endpoint, if no exit */
+	reg &= ~(EP_ISTS_EP_OUT0 | EP_ISTS_EP_IN0);
+	if (!reg)
+		goto irqend;
+
+	for_each_set_bit(bit, (unsigned long *)&reg,
+			 sizeof(u32) * BITS_PER_BYTE) {
+		cdns3_check_ep_interrupt_proceed(priv_dev->eps[bit]);
+		ret = IRQ_HANDLED;
+	}
+
+irqend:
+	ep_ien |= readl(&priv_dev->regs->ep_ien);
+	/* Unmask all handled EP interrupts */
+	writel(ep_ien, &priv_dev->regs->ep_ien);
+	spin_unlock_irqrestore(&priv_dev->lock, flags);
+	return ret;
+}
+
+/**
+ * cdns3_ep_onchip_buffer_reserve - Try to reserve onchip buf for EP
+ *
+ * The real reservation will occur during write to EP_CFG register,
+ * this function is used to check if the 'size' reservation is allowed.
+ *
+ * @priv_dev: extended gadget object
+ * @size: the size (KB) for EP would like to allocate
+ *
+ * Return 0 if the required size can met or negative value on failure
+ */
+static int cdns3_ep_onchip_buffer_reserve(struct cdns3_device *priv_dev,
+					  int size)
+{
+	u32 onchip_mem;
+
+	priv_dev->onchip_mem_allocated_size += size;
+
+	onchip_mem = USB_CAP2_ACTUAL_MEM_SIZE(readl(&priv_dev->regs->usb_cap2));
+	if (!onchip_mem)
+		onchip_mem = 256;
+
+	/* 2KB is reserved for EP0*/
+	onchip_mem -= 2;
+	if (priv_dev->onchip_mem_allocated_size > onchip_mem) {
+		priv_dev->onchip_mem_allocated_size -= size;
+		return -EPERM;
+	}
+
+	return 0;
+}
+
+/**
+ * cdns3_ep_config Configure hardware endpoint
+ * @priv_ep: extended endpoint object
+ */
+void cdns3_ep_config(struct cdns3_endpoint *priv_ep)
+{
+	bool is_iso_ep = (priv_ep->type == USB_ENDPOINT_XFER_ISOC);
+	struct cdns3_device *priv_dev = priv_ep->cdns3_dev;
+	u32 bEndpointAddress = priv_ep->num | priv_ep->dir;
+	u32 max_packet_size = 0;
+	u32 ep_cfg = 0;
+	int ret;
+
+	switch (priv_ep->type) {
+	case USB_ENDPOINT_XFER_INT:
+		ep_cfg = EP_CFG_EPTYPE(USB_ENDPOINT_XFER_INT);
+		break;
+	case USB_ENDPOINT_XFER_BULK:
+		ep_cfg = EP_CFG_EPTYPE(USB_ENDPOINT_XFER_BULK);
+		break;
+	default:
+		ep_cfg = EP_CFG_EPTYPE(USB_ENDPOINT_XFER_ISOC);
+	}
+
+	switch (priv_dev->gadget.speed) {
+	case USB_SPEED_FULL:
+		max_packet_size = is_iso_ep ? 1023 : 64;
+		break;
+	case USB_SPEED_HIGH:
+		max_packet_size = is_iso_ep ? 1024 : 512;
+		break;
+	case USB_SPEED_SUPER:
+		max_packet_size = 1024;
+		break;
+	default:
+		/* all other speed are not supported */
+		return;
+	}
+
+	ret = cdns3_ep_onchip_buffer_reserve(priv_dev, CDNS3_EP_BUF_SIZE);
+	if (ret) {
+		dev_err(priv_dev->dev, "onchip mem is full, ep is invalid\n");
+		return;
+	}
+
+	ep_cfg |= EP_CFG_MAXPKTSIZE(max_packet_size) |
+		  EP_CFG_BUFFERING(CDNS3_EP_BUF_SIZE - 1) |
+		  EP_CFG_MAXBURST(priv_ep->endpoint.maxburst);
+
+	cdns3_select_ep(priv_dev, bEndpointAddress);
+	writel(ep_cfg, &priv_dev->regs->ep_cfg);
+
+	dev_dbg(priv_dev->dev, "Configure %s: with val %08x\n",
+		priv_ep->name, ep_cfg);
+}
+
+/* Find correct direction for HW endpoint according to description */
+static int cdns3_ep_dir_is_correct(struct usb_endpoint_descriptor *desc,
+				   struct cdns3_endpoint *priv_ep)
+{
+	return (priv_ep->endpoint.caps.dir_in && usb_endpoint_dir_in(desc)) ||
+	       (priv_ep->endpoint.caps.dir_out && usb_endpoint_dir_out(desc));
+}
+
+static struct
+cdns3_endpoint *cdns3_find_available_ep(struct cdns3_device *priv_dev,
+					struct usb_endpoint_descriptor *desc)
+{
+	struct usb_ep *ep;
+	struct cdns3_endpoint *priv_ep;
+
+	list_for_each_entry(ep, &priv_dev->gadget.ep_list, ep_list) {
+		unsigned long num;
+		int ret;
+		/* ep name pattern likes epXin or epXout */
+		char c[2] = {ep->name[2], '\0'};
+
+		ret = kstrtoul(c, 10, &num);
+		if (ret)
+			return ERR_PTR(ret);
+
+		priv_ep = ep_to_cdns3_ep(ep);
+		if (cdns3_ep_dir_is_correct(desc, priv_ep)) {
+			if (!(priv_ep->flags & EP_CLAIMED)) {
+				priv_ep->num  = num;
+				return priv_ep;
+			}
+		}
+	}
+
+	return ERR_PTR(-ENOENT);
+}
+
+/*
+ *  Cadence IP has one limitation that all endpoints must be configured
+ * (Type & MaxPacketSize) before setting configuration through hardware
+ * register, it means we can't change endpoints configuration after
+ * set_configuration.
+ *
+ * This function set EP_CLAIMED flag which is added when the gadget driver
+ * uses usb_ep_autoconfig to configure specific endpoint;
+ * When the udc driver receives set_configurion request,
+ * it goes through all claimed endpoints, and configure all endpoints
+ * accordingly.
+ *
+ * At usb_ep_ops.enable/disable, we only enable and disable endpoint through
+ * ep_cfg register which can be changed after set_configuration, and do
+ * some software operation accordingly.
+ */
+static struct
+usb_ep *cdns3_gadget_match_ep(struct usb_gadget *gadget,
+			      struct usb_endpoint_descriptor *desc,
+			      struct usb_ss_ep_comp_descriptor *comp_desc)
+{
+	struct cdns3_device *priv_dev = gadget_to_cdns3_device(gadget);
+	struct cdns3_endpoint *priv_ep;
+	unsigned long flags;
+
+	priv_ep = cdns3_find_available_ep(priv_dev, desc);
+	if (IS_ERR(priv_ep)) {
+		dev_err(priv_dev->dev, "no available ep\n");
+		return NULL;
+	}
+
+	dev_dbg(priv_dev->dev, "match endpoint: %s\n", priv_ep->name);
+
+	spin_lock_irqsave(&priv_dev->lock, flags);
+	priv_ep->endpoint.desc = desc;
+	priv_ep->dir  = usb_endpoint_dir_in(desc) ? USB_DIR_IN : USB_DIR_OUT;
+	priv_ep->type = usb_endpoint_type(desc);
+	priv_ep->flags |= EP_CLAIMED;
+	spin_unlock_irqrestore(&priv_dev->lock, flags);
+	return &priv_ep->endpoint;
+}
+
+/**
+ * cdns3_gadget_ep_alloc_request Allocates request
+ * @ep: endpoint object associated with request
+ * @gfp_flags: gfp flags
+ *
+ * Returns allocated request address, NULL on allocation error
+ */
+struct usb_request *cdns3_gadget_ep_alloc_request(struct usb_ep *ep,
+						  gfp_t gfp_flags)
+{
+	struct cdns3_endpoint *priv_ep = ep_to_cdns3_ep(ep);
+	struct cdns3_request *priv_req;
+
+	priv_req = kzalloc(sizeof(*priv_req), gfp_flags);
+	if (!priv_req)
+		return NULL;
+
+	priv_req->priv_ep = priv_ep;
+
+	trace_cdns3_alloc_request(priv_req);
+	return &priv_req->request;
+}
+
+/**
+ * cdns3_gadget_ep_free_request Free memory occupied by request
+ * @ep: endpoint object associated with request
+ * @request: request to free memory
+ */
+void cdns3_gadget_ep_free_request(struct usb_ep *ep,
+				  struct usb_request *request)
+{
+	struct cdns3_request *priv_req = to_cdns3_request(request);
+
+	trace_cdns3_free_request(priv_req);
+	kfree(priv_req);
+}
+
+/**
+ * cdns3_gadget_ep_enable Enable endpoint
+ * @ep: endpoint object
+ * @desc: endpoint descriptor
+ *
+ * Returns 0 on success, error code elsewhere
+ */
+static int cdns3_gadget_ep_enable(struct usb_ep *ep,
+				  const struct usb_endpoint_descriptor *desc)
+{
+	struct cdns3_endpoint *priv_ep;
+	struct cdns3_device *priv_dev;
+	u32 reg = EP_STS_EN_TRBERREN;
+	u32 bEndpointAddress;
+	unsigned long flags;
+	int ret;
+
+	priv_ep = ep_to_cdns3_ep(ep);
+	priv_dev = priv_ep->cdns3_dev;
+
+	if (!ep || !desc || desc->bDescriptorType != USB_DT_ENDPOINT) {
+		dev_dbg(priv_dev->dev, "usbss: invalid parameters\n");
+		return -EINVAL;
+	}
+
+	if (!desc->wMaxPacketSize) {
+		dev_err(priv_dev->dev, "usbss: missing wMaxPacketSize\n");
+		return -EINVAL;
+	}
+
+	if (dev_WARN_ONCE(priv_dev->dev, priv_ep->flags & EP_ENABLED,
+			  "%s is already enabled\n", priv_ep->name))
+		return 0;
+
+	spin_lock_irqsave(&priv_dev->lock, flags);
+
+	priv_ep->endpoint.desc = desc;
+	priv_ep->type = usb_endpoint_type(desc);
+	priv_ep->interval = desc->bInterval ? BIT(desc->bInterval - 1) : 0;
+
+	if (priv_ep->interval > ISO_MAX_INTERVAL &&
+	    priv_ep->type == USB_ENDPOINT_XFER_ISOC) {
+		dev_err(priv_dev->dev, "Driver is limited to %d period\n",
+			ISO_MAX_INTERVAL);
+
+		ret =  -EINVAL;
+		goto exit;
+	}
+
+	ret = cdns3_allocate_trb_pool(priv_ep);
+
+	if (ret)
+		goto exit;
+
+	bEndpointAddress = priv_ep->num | priv_ep->dir;
+	cdns3_select_ep(priv_dev, bEndpointAddress);
+
+	trace_cdns3_gadget_ep_enable(priv_ep);
+
+	writel(EP_CMD_EPRST, &priv_dev->regs->ep_cmd);
+
+	ret = cdns3_handshake(&priv_dev->regs->ep_cmd,
+			      EP_CMD_CSTALL | EP_CMD_EPRST, 0, 100);
+
+	/* enable interrupt for selected endpoint */
+	cdns3_set_register_bit(&priv_dev->regs->ep_ien,
+			       BIT(cdns3_ep_addr_to_index(bEndpointAddress)));
+	/*
+	 * WA1: Set flag for all not ISOC OUT endpoints. If this flag is set
+	 * driver try to detect whether endpoint need additional internal
+	 * buffer for unblocking on-chip FIFO buffer. This flag will be cleared
+	 * if before first DESCMISS interrupt the DMA will be armed.
+	 */
+	if (!priv_ep->dir && priv_ep->type != USB_ENDPOINT_XFER_ISOC) {
+		priv_ep->flags |= EP_QUIRK_EXTRA_BUF_DET;
+		reg |= EP_STS_EN_DESCMISEN;
+	}
+
+	writel(reg, &priv_dev->regs->ep_sts_en);
+
+	cdns3_set_register_bit(&priv_dev->regs->ep_cfg, EP_CFG_ENABLE);
+
+	ep->desc = desc;
+	priv_ep->flags &= ~(EP_PENDING_REQUEST | EP_STALL |
+			    EP_QUIRK_EXTRA_BUF_EN);
+	priv_ep->flags |= EP_ENABLED | EP_UPDATE_EP_TRBADDR;
+	priv_ep->enqueue = 0;
+	priv_ep->dequeue = 0;
+	reg = readl(&priv_dev->regs->ep_sts);
+	priv_ep->pcs = !!EP_STS_CCS(reg);
+	priv_ep->ccs = !!EP_STS_CCS(reg);
+	/* one TRB is reserved for link TRB used in DMULT mode*/
+	priv_ep->free_trbs = priv_ep->num_trbs - 1;
+exit:
+	spin_unlock_irqrestore(&priv_dev->lock, flags);
+
+	return ret;
+}
+
+/**
+ * cdns3_gadget_ep_disable Disable endpoint
+ * @ep: endpoint object
+ *
+ * Returns 0 on success, error code elsewhere
+ */
+static int cdns3_gadget_ep_disable(struct usb_ep *ep)
+{
+	struct cdns3_endpoint *priv_ep;
+	struct cdns3_request *priv_req;
+	struct cdns3_device *priv_dev;
+	struct usb_request *request;
+	unsigned long flags;
+	int ret = 0;
+	u32 ep_cfg;
+
+	if (!ep) {
+		pr_err("usbss: invalid parameters\n");
+		return -EINVAL;
+	}
+
+	priv_ep = ep_to_cdns3_ep(ep);
+	priv_dev = priv_ep->cdns3_dev;
+
+	if (dev_WARN_ONCE(priv_dev->dev, !(priv_ep->flags & EP_ENABLED),
+			  "%s is already disabled\n", priv_ep->name))
+		return 0;
+
+	spin_lock_irqsave(&priv_dev->lock, flags);
+
+	trace_cdns3_gadget_ep_disable(priv_ep);
+
+	cdns3_select_ep(priv_dev, ep->desc->bEndpointAddress);
+	ret = cdns3_data_flush(priv_ep);
+
+	ep_cfg = readl(&priv_dev->regs->ep_cfg);
+	ep_cfg &= ~EP_CFG_ENABLE;
+	writel(ep_cfg, &priv_dev->regs->ep_cfg);
+
+	while (!list_empty(&priv_ep->pending_req_list)) {
+		request = cdns3_next_request(&priv_ep->pending_req_list);
+
+		cdns3_gadget_giveback(priv_ep, to_cdns3_request(request),
+				      -ESHUTDOWN);
+	}
+
+	while (!list_empty(&priv_ep->descmiss_req_list)) {
+		priv_req = cdns3_next_priv_request(&priv_ep->descmiss_req_list);
+
+		kfree(priv_req->request.buf);
+		cdns3_gadget_ep_free_request(&priv_ep->endpoint,
+					     &priv_req->request);
+	}
+
+	while (!list_empty(&priv_ep->deferred_req_list)) {
+		request = cdns3_next_request(&priv_ep->deferred_req_list);
+
+		cdns3_gadget_giveback(priv_ep, to_cdns3_request(request),
+				      -ESHUTDOWN);
+	}
+
+	priv_ep->descmis_req = NULL;
+
+	ep->desc = NULL;
+	priv_ep->flags &= ~EP_ENABLED;
+
+	spin_unlock_irqrestore(&priv_dev->lock, flags);
+
+	return ret;
+}
+
+/**
+ * cdns3_gadget_ep_queue Transfer data on endpoint
+ * @ep: endpoint object
+ * @request: request object
+ * @gfp_flags: gfp flags
+ *
+ * Returns 0 on success, error code elsewhere
+ */
+static int __cdns3_gadget_ep_queue(struct usb_ep *ep,
+				   struct usb_request *request,
+				   gfp_t gfp_flags)
+{
+	struct cdns3_endpoint *priv_ep = ep_to_cdns3_ep(ep);
+	struct cdns3_device *priv_dev = priv_ep->cdns3_dev;
+	struct cdns3_request *priv_req;
+	int deferred = 0;
+	int ret = 0;
+
+	request->actual = 0;
+	request->status = -EINPROGRESS;
+	priv_req = to_cdns3_request(request);
+	trace_cdns3_ep_queue(priv_req);
+
+	/*
+	 * WA1: if transfer was queued before DESCMISS appear than we
+	 * can disable handling of DESCMISS interrupt. Driver assumes that it
+	 * can disable special treatment for this endpoint.
+	 */
+	if (priv_ep->flags & EP_QUIRK_EXTRA_BUF_DET) {
+		u32 reg;
+
+		cdns3_select_ep(priv_dev, priv_ep->num | priv_ep->dir);
+		reg = readl(&priv_dev->regs->ep_sts_en);
+		priv_ep->flags &= ~EP_QUIRK_EXTRA_BUF_DET;
+		reg &= EP_STS_EN_DESCMISEN;
+		writel(reg, &priv_dev->regs->ep_sts_en);
+	}
+
+	/* WA1 */
+	if (priv_ep->flags & EP_QUIRK_EXTRA_BUF_EN) {
+		u8 pending_empty = list_empty(&priv_ep->pending_req_list);
+		u8 descmiss_empty = list_empty(&priv_ep->descmiss_req_list);
+
+		/*
+		 *  DESCMISS transfer has been finished, so data will be
+		 *  directly copied from internal allocated usb_request
+		 *  objects.
+		 */
+		if (pending_empty && !descmiss_empty &&
+		    !(priv_req->flags & REQUEST_INTERNAL)) {
+			cdns3_descmiss_copy_data(priv_ep, request);
+			list_add_tail(&request->list,
+				      &priv_ep->pending_req_list);
+			cdns3_gadget_giveback(priv_ep, priv_req,
+					      request->status);
+			return ret;
+		}
+
+		/*
+		 * WA1 driver will wait for completion DESCMISS transfer,
+		 * before starts new, not DESCMISS transfer.
+		 */
+		if (!pending_empty && !descmiss_empty)
+			deferred = 1;
+
+		if (priv_req->flags & REQUEST_INTERNAL)
+			list_add_tail(&priv_req->list,
+				      &priv_ep->descmiss_req_list);
+	}
+
+	ret = usb_gadget_map_request_by_dev(priv_dev->sysdev, request,
+					    usb_endpoint_dir_in(ep->desc));
+	if (ret)
+		return ret;
+
+	/*
+	 * If hardware endpoint configuration has not been set yet then
+	 * just queue request in deferred list. Transfer will be started in
+	 * cdns3_set_hw_configuration.
+	 */
+	if (!priv_dev->hw_configured_flag)
+		deferred = 1;
+	else
+		ret = cdns3_ep_run_transfer(priv_ep, request);
+
+	if (ret || deferred)
+		list_add_tail(&request->list, &priv_ep->deferred_req_list);
+	else
+		list_add_tail(&request->list, &priv_ep->pending_req_list);
+
+	return ret;
+}
+
+static int cdns3_gadget_ep_queue(struct usb_ep *ep, struct usb_request *request,
+				 gfp_t gfp_flags)
+{
+	struct usb_request *zlp_request;
+	struct cdns3_endpoint *priv_ep;
+	struct cdns3_device *priv_dev;
+	unsigned long flags;
+	int ret;
+
+	if (!request || !ep)
+		return -EINVAL;
+
+	priv_ep = ep_to_cdns3_ep(ep);
+	priv_dev = priv_ep->cdns3_dev;
+
+	spin_lock_irqsave(&priv_dev->lock, flags);
+
+	ret = __cdns3_gadget_ep_queue(ep, request, gfp_flags);
+
+	if (ret == 0 && request->zero && request->length &&
+	    (request->length % ep->maxpacket == 0)) {
+		struct cdns3_request *priv_req;
+
+		zlp_request = cdns3_gadget_ep_alloc_request(ep, GFP_ATOMIC);
+		zlp_request->buf = priv_dev->zlp_buf;
+		zlp_request->length = 0;
+
+		priv_req = to_cdns3_request(zlp_request);
+		priv_req->flags |= REQUEST_ZLP;
+
+		dev_dbg(priv_dev->dev, "Queuing ZLP for endpoint: %s\n",
+			priv_ep->name);
+		ret = __cdns3_gadget_ep_queue(ep, zlp_request, gfp_flags);
+	}
+
+	spin_unlock_irqrestore(&priv_dev->lock, flags);
+	return ret;
+}
+
+/**
+ * cdns3_gadget_ep_dequeue Remove request from transfer queue
+ * @ep: endpoint object associated with request
+ * @request: request object
+ *
+ * Returns 0 on success, error code elsewhere
+ */
+int cdns3_gadget_ep_dequeue(struct usb_ep *ep,
+			    struct usb_request *request)
+{
+	struct cdns3_endpoint *priv_ep = ep_to_cdns3_ep(ep);
+	struct cdns3_device *priv_dev = priv_ep->cdns3_dev;
+	struct usb_request *req, *req_temp;
+	struct cdns3_request *priv_req;
+	unsigned long flags;
+	int ret = 0;
+
+	if (!ep || !request || !ep->desc)
+		return -EINVAL;
+
+	spin_lock_irqsave(&priv_dev->lock, flags);
+
+	priv_req = to_cdns3_request(request);
+
+	trace_cdns3_ep_dequeue(priv_req);
+
+	cdns3_select_ep(priv_dev, ep->desc->bEndpointAddress);
+
+	list_for_each_entry_safe(req, req_temp, &priv_ep->pending_req_list,
+				 list) {
+		if (request == req)
+			goto found;
+	}
+
+	list_for_each_entry_safe(req, req_temp, &priv_ep->deferred_req_list,
+				 list) {
+		if (request == req)
+			goto found;
+	}
+
+found:
+	if (request == req)
+		cdns3_gadget_giveback(priv_ep, priv_req, -ECONNRESET);
+
+	spin_unlock_irqrestore(&priv_dev->lock, flags);
+	return ret;
+}
+
+/**
+ * cdns3_gadget_ep_set_halt Sets/clears stall on selected endpoint
+ * @ep: endpoint object to set/clear stall on
+ * @value: 1 for set stall, 0 for clear stall
+ *
+ * Returns 0 on success, error code elsewhere
+ */
+int cdns3_gadget_ep_set_halt(struct usb_ep *ep, int value)
+{
+	struct cdns3_endpoint *priv_ep = ep_to_cdns3_ep(ep);
+	struct cdns3_device *priv_dev = priv_ep->cdns3_dev;
+	unsigned long flags;
+	int ret = 0;
+
+	if (!(priv_ep->flags & EP_ENABLED))
+		return -EPERM;
+
+	spin_lock_irqsave(&priv_dev->lock, flags);
+
+	/* if actual transfer is pending defer setting stall on this endpoint */
+	if ((priv_ep->flags & EP_PENDING_REQUEST) && value) {
+		priv_ep->flags |= EP_STALL;
+		goto finish;
+	}
+
+	dev_dbg(priv_dev->dev, "Halt endpoint %s\n", priv_ep->name);
+
+	cdns3_select_ep(priv_dev, ep->desc->bEndpointAddress);
+	if (value) {
+		cdns3_ep_stall_flush(priv_ep);
+	} else {
+		priv_ep->flags &= ~EP_WEDGE;
+		writel(EP_CMD_CSTALL | EP_CMD_EPRST, &priv_dev->regs->ep_cmd);
+
+		/* wait for EPRST cleared */
+		ret = cdns3_handshake(&priv_dev->regs->ep_cmd,
+				      EP_CMD_EPRST, 0, 100);
+		if (unlikely(ret)) {
+			dev_err(priv_dev->dev,
+				"Clearing halt condition failed for %s\n",
+				priv_ep->name);
+			goto finish;
+
+		} else {
+			priv_ep->flags &= ~EP_STALL;
+		}
+	}
+
+	priv_ep->flags &= ~EP_PENDING_REQUEST;
+finish:
+	spin_unlock_irqrestore(&priv_dev->lock, flags);
+
+	return ret;
+}
+
+extern const struct usb_ep_ops cdns3_gadget_ep0_ops;
+
+static const struct usb_ep_ops cdns3_gadget_ep_ops = {
+	.enable = cdns3_gadget_ep_enable,
+	.disable = cdns3_gadget_ep_disable,
+	.alloc_request = cdns3_gadget_ep_alloc_request,
+	.free_request = cdns3_gadget_ep_free_request,
+	.queue = cdns3_gadget_ep_queue,
+	.dequeue = cdns3_gadget_ep_dequeue,
+	.set_halt = cdns3_gadget_ep_set_halt,
+	.set_wedge = cdns3_gadget_ep_set_wedge,
+};
+
+/**
+ * cdns3_gadget_get_frame Returns number of actual ITP frame
+ * @gadget: gadget object
+ *
+ * Returns number of actual ITP frame
+ */
+static int cdns3_gadget_get_frame(struct usb_gadget *gadget)
+{
+	struct cdns3_device *priv_dev = gadget_to_cdns3_device(gadget);
+
+	return readl(&priv_dev->regs->usb_iptn);
+}
+
+static int cdns3_gadget_wakeup(struct usb_gadget *gadget)
+{
+	return 0;
+}
+
+static int cdns3_gadget_set_selfpowered(struct usb_gadget *gadget,
+					int is_selfpowered)
+{
+	struct cdns3_device *priv_dev = gadget_to_cdns3_device(gadget);
+	unsigned long flags;
+
+	spin_lock_irqsave(&priv_dev->lock, flags);
+	priv_dev->is_selfpowered = !!is_selfpowered;
+	spin_unlock_irqrestore(&priv_dev->lock, flags);
+	return 0;
+}
+
+static int cdns3_gadget_pullup(struct usb_gadget *gadget, int is_on)
+{
+	struct cdns3_device *priv_dev = gadget_to_cdns3_device(gadget);
+
+	if (is_on)
+		writel(USB_CONF_DEVEN, &priv_dev->regs->usb_conf);
+	else
+		writel(USB_CONF_DEVDS, &priv_dev->regs->usb_conf);
+
+	return 0;
+}
+
+static void cdns3_gadget_config(struct cdns3_device *priv_dev)
+{
+	struct cdns3_usb_regs __iomem *regs = priv_dev->regs;
+
+	cdns3_ep0_config(priv_dev);
+
+	/* enable interrupts for endpoint 0 (in and out) */
+	writel(EP_IEN_EP_OUT0 | EP_IEN_EP_IN0, &regs->ep_ien);
+
+	/* enable generic interrupt*/
+	writel(USB_IEN_INIT, &regs->usb_ien);
+	writel(USB_CONF_CLK2OFFDS | USB_CONF_L1DS, &regs->usb_conf);
+	writel(USB_CONF_DMULT, &regs->usb_conf);
+	cdns3_gadget_pullup(&priv_dev->gadget, 1);
+}
+
+/**
+ * cdns3_gadget_udc_start Gadget start
+ * @gadget: gadget object
+ * @driver: driver which operates on this gadget
+ *
+ * Returns 0 on success, error code elsewhere
+ */
+static int cdns3_gadget_udc_start(struct usb_gadget *gadget,
+				  struct usb_gadget_driver *driver)
+{
+	struct cdns3_device *priv_dev = gadget_to_cdns3_device(gadget);
+	unsigned long flags;
+
+	spin_lock_irqsave(&priv_dev->lock, flags);
+	priv_dev->gadget_driver = driver;
+	cdns3_gadget_config(priv_dev);
+	spin_unlock_irqrestore(&priv_dev->lock, flags);
+	return 0;
+}
+
+/**
+ * cdns3_gadget_udc_stop Stops gadget
+ * @gadget: gadget object
+ *
+ * Returns 0
+ */
+static int cdns3_gadget_udc_stop(struct usb_gadget *gadget)
+{
+	struct cdns3_device *priv_dev = gadget_to_cdns3_device(gadget);
+	struct cdns3_endpoint *priv_ep;
+	u32 bEndpointAddress;
+	struct usb_ep *ep;
+	int ret = 0;
+
+	priv_dev->gadget_driver = NULL;
+
+	priv_dev->onchip_mem_allocated_size = 0;
+	priv_dev->gadget.speed = USB_SPEED_UNKNOWN;
+
+	list_for_each_entry(ep, &priv_dev->gadget.ep_list, ep_list) {
+		priv_ep = ep_to_cdns3_ep(ep);
+		bEndpointAddress = priv_ep->num | priv_ep->dir;
+		cdns3_select_ep(priv_dev, bEndpointAddress);
+		writel(EP_CMD_EPRST, &priv_dev->regs->ep_cmd);
+		ret = cdns3_handshake(&priv_dev->regs->ep_cmd,
+				      EP_CMD_EPRST, 0, 100);
+		cdns3_free_trb_pool(priv_ep);
+	}
+
+	/* disable interrupt for device */
+	writel(0, &priv_dev->regs->usb_ien);
+	writel(USB_CONF_DEVDS, &priv_dev->regs->usb_conf);
+
+	return ret;
+}
+
+static const struct usb_gadget_ops cdns3_gadget_ops = {
+	.get_frame = cdns3_gadget_get_frame,
+	.wakeup = cdns3_gadget_wakeup,
+	.set_selfpowered = cdns3_gadget_set_selfpowered,
+	.pullup = cdns3_gadget_pullup,
+	.udc_start = cdns3_gadget_udc_start,
+	.udc_stop = cdns3_gadget_udc_stop,
+	.match_ep = cdns3_gadget_match_ep,
+};
+
+static void cdns3_free_all_eps(struct cdns3_device *priv_dev)
+{
+	int i;
+
+	/*ep0 OUT point to ep0 IN*/
+	priv_dev->eps[16] = NULL;
+
+	cdns3_free_trb_pool(priv_dev->eps[0]);
+
+	for (i = 0; i < CDNS3_ENDPOINTS_MAX_COUNT; i++)
+		if (priv_dev->eps[i])
+			devm_kfree(priv_dev->dev, priv_dev->eps[i]);
+}
+
+/**
+ * cdns3_init_eps Initializes software endpoints of gadget
+ * @cdns3: extended gadget object
+ *
+ * Returns 0 on success, error code elsewhere
+ */
+static int cdns3_init_eps(struct cdns3_device *priv_dev)
+{
+	u32 ep_enabled_reg, iso_ep_reg;
+	struct cdns3_endpoint *priv_ep;
+	int ep_dir, ep_number;
+	u32 ep_mask;
+	int ret = 0;
+	int i;
+
+	/* Read it from USB_CAP3 to USB_CAP5 */
+	ep_enabled_reg = readl(&priv_dev->regs->usb_cap3);
+	iso_ep_reg = readl(&priv_dev->regs->usb_cap4);
+
+	dev_dbg(priv_dev->dev, "Initializing non-zero endpoints\n");
+
+	for (i = 0; i < CDNS3_ENDPOINTS_MAX_COUNT; i++) {
+		ep_dir = i >> 4;	/* i div 16 */
+		ep_number = i & 0xF;	/* i % 16 */
+		ep_mask = BIT(i);
+
+		if (!(ep_enabled_reg & ep_mask))
+			continue;
+
+		if (ep_dir && !ep_number) {
+			priv_dev->eps[i] = priv_dev->eps[0];
+			continue;
+		}
+
+		priv_ep = devm_kzalloc(priv_dev->dev, sizeof(*priv_ep),
+				       GFP_KERNEL);
+		if (!priv_ep) {
+			ret = -ENOMEM;
+			goto err;
+		}
+
+		/* set parent of endpoint object */
+		priv_ep->cdns3_dev = priv_dev;
+		priv_dev->eps[i] = priv_ep;
+		priv_ep->num = ep_number;
+		priv_ep->dir = ep_dir ? USB_DIR_IN : USB_DIR_OUT;
+
+		if (!ep_number) {
+			ret = cdns3_init_ep0(priv_dev, priv_ep);
+			if (ret) {
+				dev_err(priv_dev->dev, "Failed to init ep0\n");
+				goto err;
+			}
+		} else {
+			snprintf(priv_ep->name, sizeof(priv_ep->name), "ep%d%s",
+				 ep_number, !!ep_dir ? "in" : "out");
+			priv_ep->endpoint.name = priv_ep->name;
+
+			usb_ep_set_maxpacket_limit(&priv_ep->endpoint,
+						   CDNS3_EP_MAX_PACKET_LIMIT);
+			priv_ep->endpoint.max_streams = CDNS3_EP_MAX_STREAMS;
+			priv_ep->endpoint.ops = &cdns3_gadget_ep_ops;
+			if (ep_dir)
+				priv_ep->endpoint.caps.dir_in = 1;
+			else
+				priv_ep->endpoint.caps.dir_out = 1;
+
+			if (iso_ep_reg & ep_mask)
+				priv_ep->endpoint.caps.type_iso = 1;
+
+			priv_ep->endpoint.caps.type_bulk = 1;
+			priv_ep->endpoint.caps.type_int = 1;
+			priv_ep->endpoint.maxburst = CDNS3_EP_BUF_SIZE - 1;
+
+			list_add_tail(&priv_ep->endpoint.ep_list,
+				      &priv_dev->gadget.ep_list);
+		}
+
+		priv_ep->flags = 0;
+
+		dev_info(priv_dev->dev, "Initialized  %s support: %s %s\n",
+			 priv_ep->name,
+			 priv_ep->endpoint.caps.type_bulk ? "BULK, INT" : "",
+			 priv_ep->endpoint.caps.type_iso ? "ISO" : "");
+
+		INIT_LIST_HEAD(&priv_ep->pending_req_list);
+		INIT_LIST_HEAD(&priv_ep->deferred_req_list);
+		INIT_LIST_HEAD(&priv_ep->descmiss_req_list);
+	}
+
+	return 0;
+err:
+	cdns3_free_all_eps(priv_dev);
+	return -ENOMEM;
+}
+
+static void cdns3_gadget_disable(struct cdns3 *cdns)
+{
+	struct cdns3_device *priv_dev;
+
+	priv_dev = cdns->gadget_dev;
+
+	if (priv_dev->gadget_driver)
+		priv_dev->gadget_driver->disconnect(&priv_dev->gadget);
+
+	usb_gadget_disconnect(&priv_dev->gadget);
+	priv_dev->gadget.speed = USB_SPEED_UNKNOWN;
+}
+
+void cdns3_gadget_exit(struct cdns3 *cdns)
+{
+	struct cdns3_device *priv_dev;
+
+	priv_dev = cdns->gadget_dev;
+
+	cdns3_gadget_disable(cdns);
+
+	devm_free_irq(cdns->dev, cdns->irq, cdns);
+
+	pm_runtime_mark_last_busy(cdns->dev);
+	pm_runtime_put_autosuspend(cdns->dev);
+
+	usb_del_gadget_udc(&priv_dev->gadget);
+
+	cdns3_free_all_eps(priv_dev);
+
+	dma_free_coherent(priv_dev->sysdev, 8, priv_dev->setup_buf,
+			  priv_dev->setup_dma);
+
+	kfree(priv_dev->zlp_buf);
+	kfree(priv_dev);
+	cdns->gadget_dev = NULL;
+}
+
+static int cdns3_gadget_start(struct cdns3 *cdns)
+{
+	struct cdns3_device *priv_dev;
+	u32 max_speed;
+	int ret;
+
+	priv_dev = kzalloc(sizeof(*priv_dev), GFP_KERNEL);
+	if (!priv_dev)
+		return -ENOMEM;
+
+	cdns->gadget_dev = priv_dev;
+	priv_dev->sysdev = cdns->dev;
+	priv_dev->dev = cdns->dev;
+	priv_dev->regs = cdns->dev_regs;
+
+	max_speed = usb_get_maximum_speed(cdns->dev);
+
+	/* Check the maximum_speed parameter */
+	switch (max_speed) {
+	case USB_SPEED_FULL:
+	case USB_SPEED_HIGH:
+	case USB_SPEED_SUPER:
+		break;
+	default:
+		dev_err(cdns->dev, "invalid maximum_speed parameter %d\n",
+			max_speed);
+		/* fall through */
+	case USB_SPEED_UNKNOWN:
+		/* default to superspeed */
+		max_speed = USB_SPEED_SUPER;
+		break;
+	}
+
+	/* fill gadget fields */
+	priv_dev->gadget.max_speed = max_speed;
+	priv_dev->gadget.speed = USB_SPEED_UNKNOWN;
+	priv_dev->gadget.ops = &cdns3_gadget_ops;
+	priv_dev->gadget.name = "usb-ss-gadget";
+	priv_dev->gadget.sg_supported = 1;
+
+	spin_lock_init(&priv_dev->lock);
+	INIT_WORK(&priv_dev->pending_status_wq,
+		  cdns3_pending_setup_status_handler);
+
+	/* initialize endpoint container */
+	INIT_LIST_HEAD(&priv_dev->gadget.ep_list);
+
+	ret = cdns3_init_eps(priv_dev);
+	if (ret) {
+		dev_err(priv_dev->dev, "Failed to create endpoints\n");
+		goto err1;
+	}
+
+	/* allocate memory for setup packet buffer */
+	priv_dev->setup_buf = dma_alloc_coherent(priv_dev->sysdev, 8,
+						 &priv_dev->setup_dma, GFP_DMA);
+	if (!priv_dev->setup_buf) {
+		dev_err(priv_dev->dev, "Failed to allocate memory for SETUP buffer\n");
+		ret = -ENOMEM;
+		goto err2;
+	}
+
+	priv_dev->dev_ver = readl(&priv_dev->regs->usb_cap6);
+	dev_dbg(priv_dev->dev, "Device Controller version: %08x\n",
+		readl(&priv_dev->regs->usb_cap6));
+	dev_dbg(priv_dev->dev, "USB Capabilities:: %08x\n",
+		readl(&priv_dev->regs->usb_cap1));
+	dev_dbg(priv_dev->dev, "On-Chip memory cnfiguration: %08x\n",
+		readl(&priv_dev->regs->usb_cap2));
+
+	priv_dev->zlp_buf = kzalloc(CDNS3_EP_ZLP_BUF_SIZE, GFP_KERNEL);
+	if (!priv_dev->zlp_buf) {
+		ret = -ENOMEM;
+		goto err3;
+	}
+
+	/* add USB gadget device */
+	ret = usb_add_gadget_udc(priv_dev->dev, &priv_dev->gadget);
+	if (ret < 0) {
+		dev_err(priv_dev->dev,
+			"Failed to register USB device controller\n");
+		goto err4;
+	}
+
+	return 0;
+err4:
+	kfree(priv_dev->zlp_buf);
+err3:
+	dma_free_coherent(priv_dev->sysdev, 8, priv_dev->setup_buf,
+			  priv_dev->setup_dma);
+err2:
+	cdns3_free_all_eps(priv_dev);
+err1:
+	cdns->gadget_dev = NULL;
+	return ret;
+}
+
+static int __cdns3_gadget_init(struct cdns3 *cdns)
+{
+	struct cdns3_device *priv_dev;
+	unsigned long flags;
+	int ret = 0;
+
+	ret = cdns3_gadget_start(cdns);
+	if (ret)
+		return ret;
+
+	priv_dev = cdns->gadget_dev;
+	ret = devm_request_threaded_irq(cdns->dev, cdns->irq,
+					cdns3_device_irq_handler,
+					cdns3_device_thread_irq_handler,
+					IRQF_SHARED, dev_name(cdns->dev), cdns);
+	if (ret)
+		goto err0;
+
+	pm_runtime_get_sync(cdns->dev);
+	spin_lock_irqsave(&priv_dev->lock, flags);
+	spin_unlock_irqrestore(&priv_dev->lock, flags);
+	return 0;
+err0:
+	cdns3_gadget_exit(cdns);
+	return ret;
+}
+
+static int cdns3_gadget_suspend(struct cdns3 *cdns, bool do_wakeup)
+{
+	cdns3_gadget_disable(cdns);
+	return 0;
+}
+
+static int cdns3_gadget_resume(struct cdns3 *cdns, bool hibernated)
+{
+	struct cdns3_device *priv_dev;
+	unsigned long flags;
+
+	priv_dev = cdns->gadget_dev;
+	spin_lock_irqsave(&priv_dev->lock, flags);
+
+	if (!priv_dev->gadget_driver) {
+		spin_unlock_irqrestore(&priv_dev->lock, flags);
+		return 0;
+	}
+
+	cdns3_gadget_config(priv_dev);
+	spin_unlock_irqrestore(&priv_dev->lock, flags);
+	return 0;
+}
+
+/**
+ * cdns3_gadget_init - initialize device structure
+ *
+ * cdns: cdns3 instance
+ *
+ * This function initializes the gadget.
+ */
+int cdns3_gadget_init(struct cdns3 *cdns)
+{
+	struct cdns3_role_driver *rdrv;
+
+	rdrv = devm_kzalloc(cdns->dev, sizeof(*rdrv), GFP_KERNEL);
+	if (!rdrv)
+		return -ENOMEM;
+
+	rdrv->start	= __cdns3_gadget_init;
+	rdrv->stop	= cdns3_gadget_exit;
+	rdrv->suspend	= cdns3_gadget_suspend;
+	rdrv->resume	= cdns3_gadget_resume;
+	rdrv->state	= CDNS3_ROLE_STATE_INACTIVE;
+	rdrv->name	= "gadget";
+	cdns->roles[CDNS3_ROLE_GADGET] = rdrv;
+
+	return 0;
+}
diff --git a/drivers/usb/cdns3/gadget.h b/drivers/usb/cdns3/gadget.h
new file mode 100644
index 000000000000..41cec7f085ad
--- /dev/null
+++ b/drivers/usb/cdns3/gadget.h
@@ -0,0 +1,1206 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * USBSS device controller driver header file
+ *
+ * Copyright (C) 2018 Cadence.
+ * Copyright (C) 2017-2018 NXP
+ *
+ * Author: Pawel Laszczak <pawell@cadence.com>
+ *         Pawel Jez <pjez@cadence.com>
+ *         Peter Chen <peter.chen@nxp.com>
+ */
+#ifndef __LINUX_CDNS3_GADGET
+#define __LINUX_CDNS3_GADGET
+#include <linux/usb/gadget.h>
+
+/*
+ * USBSS-DEV register interface.
+ * This corresponds to the USBSS Device Controller Interface
+ */
+
+/**
+ * struct cdns3_usb_regs - device controller registers.
+ * @usb_conf:      Global Configuration Register.
+ * @usb_sts:       Global Status Register.
+ * @usb_cmd:       Global Command Register.
+ * @usb_iptn:      ITP/SOF number Register.
+ * @usb_lpm:       Global Command Register.
+ * @usb_ien:       USB Interrupt Enable Register.
+ * @usb_ists:      USB Interrupt Status Register.
+ * @ep_sel:        Endpoint Select Register.
+ * @ep_traddr:     Endpoint Transfer Ring Address Register.
+ * @ep_cfg:        Endpoint Configuration Register.
+ * @ep_cmd:        Endpoint Command Register.
+ * @ep_sts:        Endpoint Status Register.
+ * @ep_sts_sid:    Endpoint Status Register.
+ * @ep_sts_en:     Endpoint Status Register Enable.
+ * @drbl:          Doorbell Register.
+ * @ep_ien:        EP Interrupt Enable Register.
+ * @ep_ists:       EP Interrupt Status Register.
+ * @usb_pwr:       Global Power Configuration Register.
+ * @usb_conf2:     Global Configuration Register 2.
+ * @usb_cap1:      Capability Register 1.
+ * @usb_cap2:      Capability Register 2.
+ * @usb_cap3:      Capability Register 3.
+ * @usb_cap4:      Capability Register 4.
+ * @usb_cap5:      Capability Register 5.
+ * @usb_cap6:      Capability Register 6.
+ * @usb_cpkt1:     Custom Packet Register 1.
+ * @usb_cpkt2:     Custom Packet Register 2.
+ * @usb_cpkt3:     Custom Packet Register 3.
+ * @reserved1:     Reserved.
+ * @cfg_regs:      Configuration registers.
+ * @reserved2:     Reserved.
+ * @dma_axi_ctrl:  AXI Control register.
+ * @dma_axi_id:    AXI ID register.
+ * @dma_axi_cap:   AXI Capability register.
+ * @dma_axi_ctrl0: AXI Control 0 register.
+ * @dma_axi_ctrl1: AXI Control 1 register.
+ */
+struct cdns3_usb_regs {
+	__le32 usb_conf;
+	__le32 usb_sts;
+	__le32 usb_cmd;
+	__le32 usb_iptn;
+	__le32 usb_lpm;
+	__le32 usb_ien;
+	__le32 usb_ists;
+	__le32 ep_sel;
+	__le32 ep_traddr;
+	__le32 ep_cfg;
+	__le32 ep_cmd;
+	__le32 ep_sts;
+	__le32 ep_sts_sid;
+	__le32 ep_sts_en;
+	__le32 drbl;
+	__le32 ep_ien;
+	__le32 ep_ists;
+	__le32 usb_pwr;
+	__le32 usb_conf2;
+	__le32 usb_cap1;
+	__le32 usb_cap2;
+	__le32 usb_cap3;
+	__le32 usb_cap4;
+	__le32 usb_cap5;
+	__le32 usb_cap6;
+	__le32 usb_cpkt1;
+	__le32 usb_cpkt2;
+	__le32 usb_cpkt3;
+	__le32 reserved1[36];
+	__le32 cfg_reg1;
+	__le32 dbg_link1;
+	__le32 dbg_link2;
+	__le32 cfg_regs[74];
+	__le32 reserved2[34];
+	__le32 dma_axi_ctrl;
+	__le32 dma_axi_id;
+	__le32 dma_axi_cap;
+	__le32 dma_axi_ctrl0;
+	__le32 dma_axi_ctrl1;
+};
+
+/* USB_CONF - bitmasks */
+/* Reset USB device configuration. */
+#define USB_CONF_CFGRST		BIT(0)
+/* Set Configuration. */
+#define USB_CONF_CFGSET		BIT(1)
+/* Disconnect USB device in SuperSpeed. */
+#define USB_CONF_USB3DIS	BIT(3)
+/* Disconnect USB device in HS/FS */
+#define USB_CONF_USB2DIS	BIT(4)
+/* Little Endian access - default */
+#define USB_CONF_LENDIAN	BIT(5)
+/*
+ * Big Endian access. Driver assume that byte order for
+ * SFRs access always is as Little Endian so this bit
+ * is not used.
+ */
+#define USB_CONF_BENDIAN	BIT(6)
+/* Device software reset. */
+#define USB_CONF_SWRST		BIT(7)
+/* Singular DMA transfer mode. */
+#define USB_CONF_DSING		BIT(8)
+/* Multiple DMA transfers mode. */
+#define USB_CONF_DMULT		BIT(9)
+/* DMA clock turn-off enable. */
+#define USB_CONF_DMAOFFEN	BIT(10)
+/* DMA clock turn-off disable. */
+#define USB_CONF_DMAOFFDS	BIT(11)
+/* Clear Force Full Speed. */
+#define USB_CONF_CFORCE_FS	BIT(12)
+/* Set Force Full Speed. */
+#define USB_CONF_SFORCE_FS	BIT(13)
+/* Device enable. */
+#define USB_CONF_DEVEN		BIT(14)
+/* Device disable. */
+#define USB_CONF_DEVDS		BIT(15)
+/* L1 LPM state entry enable (used in HS/FS mode). */
+#define USB_CONF_L1EN		BIT(16)
+/* L1 LPM state entry disable (used in HS/FS mode). */
+#define USB_CONF_L1DS		BIT(17)
+/* USB 2.0 clock gate disable. */
+#define USB_CONF_CLK2OFFEN	BIT(18)
+/* USB 2.0 clock gate enable. */
+#define USB_CONF_CLK2OFFDS	BIT(19)
+/* L0 LPM state entry request (used in HS/FS mode). */
+#define USB_CONF_LGO_L0		BIT(20)
+/* USB 3.0 clock gate disable. */
+#define USB_CONF_CLK3OFFEN	BIT(21)
+/* USB 3.0 clock gate enable. */
+#define USB_CONF_CLK3OFFDS	BIT(22)
+/* Bit 23 is reserved*/
+/* U1 state entry enable (used in SS mode). */
+#define USB_CONF_U1EN		BIT(24)
+/* U1 state entry disable (used in SS mode). */
+#define USB_CONF_U1DS		BIT(25)
+/* U2 state entry enable (used in SS mode). */
+#define USB_CONF_U2EN		BIT(26)
+/* U2 state entry disable (used in SS mode). */
+#define USB_CONF_U2DS		BIT(27)
+/* U0 state entry request (used in SS mode). */
+#define USB_CONF_LGO_U0		BIT(28)
+/* U1 state entry request (used in SS mode). */
+#define USB_CONF_LGO_U1		BIT(29)
+/* U2 state entry request (used in SS mode). */
+#define USB_CONF_LGO_U2		BIT(30)
+/* SS.Inactive state entry request (used in SS mode) */
+#define USB_CONF_LGO_SSINACT	BIT(31)
+
+/* USB_STS - bitmasks */
+/*
+ * Configuration status.
+ * 1 - device is in the configured state.
+ * 0 - device is not configured.
+ */
+#define USB_STS_CFGSTS_MASK	BIT(0)
+#define USB_STS_CFGSTS(p)	((p) & USB_STS_CFGSTS_MASK)
+/*
+ * On-chip memory overflow.
+ * 0 - On-chip memory status OK.
+ * 1 - On-chip memory overflow.
+ */
+#define USB_STS_OV_MASK		BIT(1)
+#define USB_STS_OV(p)		((p) & USB_STS_OV_MASK)
+/*
+ * SuperSpeed connection status.
+ * 0 - USB in SuperSpeed mode disconnected.
+ * 1 - USB in SuperSpeed mode connected.
+ */
+#define USB_STS_USB3CONS_MASK	BIT(2)
+#define USB_STS_USB3CONS(p)	((p) & USB_STS_USB3CONS_MASK)
+/*
+ * DMA transfer configuration status.
+ * 0 - single request.
+ * 1 - multiple TRB chain
+ */
+#define USB_STS_DTRANS_MASK	BIT(3)
+#define USB_STS_DTRANS(p)	((p) & USB_STS_DTRANS_MASK)
+/*
+ * Device speed.
+ * 0 - Undefined (value after reset).
+ * 1 - Low speed
+ * 2 - Full speed
+ * 3 - High speed
+ * 4 - Super speed
+ */
+#define USB_STS_USBSPEED_MASK	GENMASK(6, 4)
+#define USB_STS_USBSPEED(p)	(((p) & USB_STS_USBSPEED_MASK) >> 4)
+#define USB_STS_LS		(0x1 << 4)
+#define USB_STS_FS		(0x2 << 4)
+#define USB_STS_HS		(0x3 << 4)
+#define USB_STS_SS		(0x4 << 4)
+#define DEV_UNDEFSPEED(p)	(((p) & USB_STS_USBSPEED_MASK) == (0x0 << 4))
+#define DEV_LOWSPEED(p)		(((p) & USB_STS_USBSPEED_MASK) == USB_STS_LS)
+#define DEV_FULLSPEED(p)	(((p) & USB_STS_USBSPEED_MASK) == USB_STS_FS)
+#define DEV_HIGHSPEED(p)	(((p) & USB_STS_USBSPEED_MASK) == USB_STS_HS)
+#define DEV_SUPERSPEED(p)	(((p) & USB_STS_USBSPEED_MASK) == USB_STS_SS)
+/*
+ * Endianness for SFR access.
+ * 0 - Little Endian order (default after hardware reset).
+ * 1 - Big Endian order
+ */
+#define USB_STS_ENDIAN_MASK	BIT(7)
+#define USB_STS_ENDIAN(p)	((p) & USB_STS_ENDIAN_MASK)
+/*
+ * HS/FS clock turn-off status.
+ * 0 - hsfs clock is always on.
+ * 1 - hsfs clock turn-off in L2 (HS/FS mode) is enabled
+ *          (default after hardware reset).
+ */
+#define USB_STS_CLK2OFF_MASK	BIT(8)
+#define USB_STS_CLK2OFF(p)	((p) & USB_STS_CLK2OFF_MASK)
+/*
+ * PCLK clock turn-off status.
+ * 0 - pclk clock is always on.
+ * 1 - pclk clock turn-off in U3 (SS mode) is enabled
+ *          (default after hardware reset).
+ */
+#define USB_STS_CLK3OFF_MASK	BIT(9)
+#define USB_STS_CLK3OFF(p)	((p) & USB_STS_CLK3OFF_MASK)
+/*
+ * Controller in reset state.
+ * 0 - Internal reset is active.
+ * 1 - Internal reset is not active and controller is fully operational.
+ */
+#define USB_STS_IN_RST_MASK	BIT(10)
+#define USB_STS_IN_RST(p)	((p) & USB_STS_IN_RST_MASK)
+/*
+ * Device enable Status.
+ * 0 - USB device is disabled (VBUS input is disconnected from internal logic).
+ * 1 - USB device is enabled (VBUS input is connected to the internal logic).
+ */
+#define USB_STS_DEVS_MASK	BIT(14)
+#define USB_STS_DEVS(p)		((p) & USB_STS_DEVS_MASK)
+/*
+ * DAddress statuss.
+ * 0 - USB device is default state.
+ * 1 - USB device is at least in address state.
+ */
+#define USB_STS_ADDRESSED_MASK	BIT(15)
+#define USB_STS_ADDRESSED(p)	((p) & USB_STS_ADDRESSED_MASK)
+/*
+ * L1 LPM state enable status (used in HS/FS mode).
+ * 0 - Entering to L1 LPM state disabled.
+ * 1 - Entering to L1 LPM state enabled.
+ */
+#define USB_STS_L1ENS_MASK	BIT(16)
+#define USB_STS_L1ENS(p)	((p) & USB_STS_L1ENS_MASK)
+/*
+ * Internal VBUS connection status (used both in HS/FS  and SS mode).
+ * 0 - internal VBUS is not detected.
+ * 1 - internal VBUS is detected.
+ */
+#define USB_STS_VBUSS_MASK	BIT(17)
+#define USB_STS_VBUSS(p)	((p) & USB_STS_VBUSS_MASK)
+/*
+ * HS/FS LPM  state (used in FS/HS mode).
+ * 0 - L0 State
+ * 1 - L1 State
+ * 2 - L2 State
+ * 3 - L3 State
+ */
+#define USB_STS_LPMST_MASK	GENMASK(19, 18)
+#define DEV_L0_STATE(p)		(((p) & USB_STS_LPMST_MASK) == (0x0 << 18))
+#define DEV_L1_STATE(p)		(((p) & USB_STS_LPMST_MASK) == (0x1 << 18))
+#define DEV_L2_STATE(p)		(((p) & USB_STS_LPMST_MASK) == (0x2 << 18))
+#define DEV_L3_STATE(p)		(((p) & USB_STS_LPMST_MASK) == (0x3 << 18))
+/*
+ * Disable HS status (used in FS/HS mode).
+ * 0 - the disconnect bit for HS/FS mode is set .
+ * 1 - the disconnect bit for HS/FS mode is not set.
+ */
+#define USB_STS_USB2CONS_MASK	BIT(20)
+#define USB_STS_USB2CONS(p)	((p) & USB_STS_USB2CONS_MASK)
+/*
+ * HS/FS mode connection status (used in FS/HS mode).
+ * 0 - High Speed operations in USB2.0 (FS/HS) mode not disabled.
+ * 1 - High Speed operations in USB2.0 (FS/HS).
+ */
+#define USB_STS_DISABLE_HS_MASK	BIT(21)
+#define USB_STS_DISABLE_HS(p)	((p) & USB_STS_DISABLE_HS_MASK)
+/*
+ * U1 state enable status (used in SS mode).
+ * 0 - Entering to  U1 state disabled.
+ * 1 - Entering to  U1 state enabled.
+ */
+#define USB_STS_U1ENS_MASK	BIT(24)
+#define USB_STS_U1ENS(p)	((p) & USB_STS_U1ENS_MASK)
+/*
+ * U2 state enable status (used in SS mode).
+ * 0 - Entering to  U2 state disabled.
+ * 1 - Entering to  U2 state enabled.
+ */
+#define USB_STS_U2ENS_MASK	BIT(25)
+#define USB_STS_U2ENS(p)	((p) & USB_STS_U2ENS_MASK)
+/*
+ * SuperSpeed Link LTSSM state. This field reflects USBSS-DEV current
+ * SuperSpeed link state
+ */
+#define USB_STS_LST_MASK	GENMASK(29, 26)
+#define DEV_LST_U0		(((p) & USB_STS_LST_MASK) == (0x0 << 26))
+#define DEV_LST_U1		(((p) & USB_STS_LST_MASK) == (0x1 << 26))
+#define DEV_LST_U2		(((p) & USB_STS_LST_MASK) == (0x2 << 26))
+#define DEV_LST_U3		(((p) & USB_STS_LST_MASK) == (0x3 << 26))
+#define DEV_LST_DISABLED	(((p) & USB_STS_LST_MASK) == (0x4 << 26))
+#define DEV_LST_RXDETECT	(((p) & USB_STS_LST_MASK) == (0x5 << 26))
+#define DEV_LST_INACTIVE	(((p) & USB_STS_LST_MASK) == (0x6 << 26))
+#define DEV_LST_POLLING		(((p) & USB_STS_LST_MASK) == (0x7 << 26))
+#define DEV_LST_RECOVERY	(((p) & USB_STS_LST_MASK) == (0x8 << 26))
+#define DEV_LST_HOT_RESET	(((p) & USB_STS_LST_MASK) == (0x9 << 26))
+#define DEV_LST_COMP_MODE	(((p) & USB_STS_LST_MASK) == (0xa << 26))
+#define DEV_LST_LB_STATE	(((p) & USB_STS_LST_MASK) == (0xb << 26))
+/*
+ * DMA clock turn-off status.
+ * 0 - DMA clock is always on (default after hardware reset).
+ * 1 - DMA clock turn-off in U1, U2 and U3 (SS mode) is enabled.
+ */
+#define USB_STS_DMAOFF_MASK	BIT(30)
+#define USB_STS_DMAOFF(p)	((p) & USB_STS_DMAOFF_MASK)
+/*
+ * SFR Endian statuss.
+ * 0 - Little Endian order (default after hardware reset).
+ * 1 - Big Endian order.
+ */
+#define USB_STS_ENDIAN2_MASK	BIT(31)
+#define USB_STS_ENDIAN2(p)	((p) & USB_STS_ENDIAN2_MASK)
+
+/* USB_CMD -  bitmasks */
+/* Set Function Address */
+#define USB_CMD_SET_ADDR	BIT(0)
+/*
+ * Function Address This field is saved to the device only when the field
+ * SET_ADDR is set '1 ' during write to USB_CMD register.
+ * Software is responsible for entering the address of the device during
+ * SET_ADDRESS request service. This field should be set immediately after
+ * the SETUP packet is decoded, and prior to confirmation of the status phase
+ */
+#define USB_CMD_FADDR_MASK	GENMASK(7, 1)
+#define USB_CMD_FADDR(p)	(((p) << 1) & USB_CMD_FADDR_MASK)
+/* Send Function Wake Device Notification TP (used only in SS mode). */
+#define USB_CMD_SDNFW		BIT(8)
+/* Set Test Mode (used only in HS/FS mode). */
+#define USB_CMD_STMODE		BIT(9)
+/* Test mode selector (used only in HS/FS mode) */
+#define USB_STS_TMODE_SEL_MASK	GENMASK(11, 10)
+#define USB_STS_TMODE_SEL(p)	(((p) << 10) & USB_STS_TMODE_SEL_MASK)
+/*
+ *  Send Latency Tolerance Message Device Notification TP (used only
+ *  in SS mode).
+ */
+#define USB_CMD_SDNLTM		BIT(12)
+/* Send Custom Transaction Packet (used only in SS mode) */
+#define USB_CMD_SPKT		BIT(13)
+/*Device Notification 'Function Wake' - Interface value (only in SS mode. */
+#define USB_CMD_DNFW_INT_MASK	GENMASK(23, 16)
+#define USB_STS_DNFW_INT(p)	(((p) << 16) & USB_CMD_DNFW_INT_MASK)
+/*
+ * Device Notification 'Latency Tolerance Message' -373 BELT value [7:0]
+ * (used only in SS mode).
+ */
+#define USB_CMD_DNLTM_BELT_MASK	GENMASK(27, 16)
+#define USB_STS_DNLTM_BELT(p)	(((p) << 16) & USB_CMD_DNLTM_BELT_MASK)
+
+/* USB_ITPN - bitmasks */
+/*
+ * ITP(SS) / SOF (HS/FS) number
+ * In SS mode this field represent number of last ITP received from host.
+ * In HS/FS mode this field represent number of last SOF received from host.
+ */
+#define USB_ITPN_MASK		GENMASK(13, 0)
+#define USB_ITPN(p)		((p) & USB_ITPN_MASK)
+
+/* USB_LPM - bitmasks */
+/* Host Initiated Resume Duration. */
+#define USB_LPM_HIRD_MASK	GENMASK(3, 0)
+#define USB_LPM_HIRD(p)		((p) & USB_LPM_HIRD_MASK)
+/* Remote Wakeup Enable (bRemoteWake). */
+#define USB_LPM_BRW		BIT(4)
+
+/* USB_IEN - bitmasks */
+/* SS connection interrupt enable */
+#define USB_IEN_CONIEN		BIT(0)
+/* SS disconnection interrupt enable. */
+#define USB_IEN_DISIEN		BIT(1)
+/* USB SS warm reset interrupt enable. */
+#define USB_IEN_UWRESIEN	BIT(2)
+/* USB SS hot reset interrupt enable */
+#define USB_IEN_UHRESIEN	BIT(3)
+/* SS link U3 state enter interrupt enable (suspend).*/
+#define USB_IEN_U3ENTIEN	BIT(4)
+/* SS link U3 state exit interrupt enable (wakeup). */
+#define USB_IEN_U3EXTIEN	BIT(5)
+/* SS link U2 state enter interrupt enable.*/
+#define USB_IEN_U2ENTIEN	BIT(6)
+/* SS link U2 state exit interrupt enable.*/
+#define USB_IEN_U2EXTIEN	BIT(7)
+/* SS link U1 state enter interrupt enable.*/
+#define USB_IEN_U1ENTIEN	BIT(8)
+/* SS link U1 state exit interrupt enable.*/
+#define USB_IEN_U1EXTIEN	BIT(9)
+/* ITP/SOF packet detected interrupt enable.*/
+#define USB_IEN_ITPIEN		BIT(10)
+/* Wakeup interrupt enable.*/
+#define USB_IEN_WAKEIEN		BIT(11)
+/* Send Custom Packet interrupt enable.*/
+#define USB_IEN_SPKTIEN		BIT(12)
+/* HS/FS mode connection interrupt enable.*/
+#define USB_IEN_CON2IEN		BIT(16)
+/* HS/FS mode disconnection interrupt enable.*/
+#define USB_IEN_DIS2IEN		BIT(17)
+/* USB reset (HS/FS mode) interrupt enable.*/
+#define USB_IEN_U2RESIEN	BIT(18)
+/* LPM L2 state enter interrupt enable.*/
+#define USB_IEN_L2ENTIEN	BIT(20)
+/* LPM  L2 state exit interrupt enable.*/
+#define USB_IEN_L2EXTIEN	BIT(21)
+/* LPM L1 state enter interrupt enable.*/
+#define USB_IEN_L1ENTIEN	BIT(24)
+/* LPM  L1 state exit interrupt enable.*/
+#define USB_IEN_L1EXTIEN	BIT(25)
+/* Configuration reset interrupt enable.*/
+#define USB_IEN_CFGRESIEN	BIT(26)
+/* Start of the USB SS warm reset interrupt enable.*/
+#define USB_IEN_UWRESSIEN	BIT(28)
+/* End of the USB SS warm reset interrupt enable.*/
+#define USB_IEN_UWRESEIEN	BIT(29)
+
+#define USB_IEN_INIT  (USB_IEN_U2RESIEN | USB_ISTS_DIS2I | USB_IEN_CON2IEN \
+		       | USB_IEN_UHRESIEN | USB_IEN_UWRESIEN | USB_IEN_DISIEN \
+		       | USB_IEN_CONIEN | USB_IEN_U3EXTIEN | USB_IEN_L2ENTIEN \
+		       | USB_IEN_L2EXTIEN)
+
+/* USB_ISTS - bitmasks */
+/* SS Connection detected. */
+#define USB_ISTS_CONI		BIT(0)
+/* SS Disconnection detected. */
+#define USB_ISTS_DISI		BIT(1)
+/* UUSB warm reset detectede. */
+#define USB_ISTS_UWRESI		BIT(2)
+/* USB hot reset detected. */
+#define USB_ISTS_UHRESI		BIT(3)
+/* U3 link state enter detected (suspend).*/
+#define USB_ISTS_U3ENTI		BIT(4)
+/* U3 link state exit detected (wakeup). */
+#define USB_ISTS_U3EXTI		BIT(5)
+/* U2 link state enter detected.*/
+#define USB_ISTS_U2ENTI		BIT(6)
+/* U2 link state exit detected.*/
+#define USB_ISTS_U2EXTI		BIT(7)
+/* U1 link state enter detected.*/
+#define USB_ISTS_U1ENTI		BIT(8)
+/* U1 link state exit detected.*/
+#define USB_ISTS_U1EXTI		BIT(9)
+/* ITP/SOF packet detected.*/
+#define USB_ISTS_ITPI		BIT(10)
+/* Wakeup detected.*/
+#define USB_ISTS_WAKEI		BIT(11)
+/* Send Custom Packet detected.*/
+#define USB_ISTS_SPKTI		BIT(12)
+/* HS/FS mode connection detected.*/
+#define USB_ISTS_CON2I		BIT(16)
+/* HS/FS mode disconnection detected.*/
+#define USB_ISTS_DIS2I		BIT(17)
+/* USB reset (HS/FS mode) detected.*/
+#define USB_ISTS_U2RESI		BIT(18)
+/* LPM L2 state enter detected.*/
+#define USB_ISTS_L2ENTI		BIT(20)
+/* LPM  L2 state exit detected.*/
+#define USB_ISTS_L2EXTI		BIT(21)
+/* LPM L1 state enter detected.*/
+#define USB_ISTS_L1ENTI		BIT(24)
+/* LPM L1 state exit detected.*/
+#define USB_ISTS_L1EXTI		BIT(25)
+/* USB configuration reset detected.*/
+#define USB_ISTS_CFGRESI	BIT(26)
+/* Start of the USB warm reset detected.*/
+#define USB_ISTS_UWRESSI	BIT(28)
+/* End of the USB warm reset detected.*/
+#define USB_ISTS_UWRESEI	BIT(29)
+
+/* USB_SEL - bitmasks */
+#define EP_SEL_EPNO_MASK	GENMASK(3, 0)
+/* Endpoint number. */
+#define EP_SEL_EPNO(p)		((p) & EP_SEL_EPNO_MASK)
+/* Endpoint direction bit - 0 - OUT, 1 - IN. */
+#define EP_SEL_DIR		BIT(7)
+
+#define select_ep_in(nr)	(EP_SEL_EPNO(p) | EP_SEL_DIR)
+#define select_ep_out		(EP_SEL_EPNO(p))
+
+/* EP_TRADDR - bitmasks */
+/* Transfer Ring address. */
+#define EP_TRADDR_TRADDR(p)	((p))
+
+/* EP_CFG - bitmasks */
+/* Endpoint enable */
+#define EP_CFG_ENABLE		BIT(0)
+/*
+ *  Endpoint type.
+ * 1 - isochronous
+ * 2 - bulk
+ * 3 - interrupt
+ */
+#define EP_CFG_EPTYPE_MASK	GENMASK(2, 1)
+#define EP_CFG_EPTYPE(p)	(((p) << 1)  & EP_CFG_EPTYPE_MASK)
+/* Stream support enable (only in SS mode). */
+#define EP_CFG_STREAM_EN	BIT(3)
+/* TDL check (only in SS mode for BULK EP). */
+#define EP_CFG_TDL_CHK		BIT(4)
+/* SID check (only in SS mode for BULK OUT EP). */
+#define EP_CFG_SID_CHK		BIT(5)
+/* DMA transfer endianness. */
+#define EP_CFG_EPENDIAN		BIT(7)
+/* Max burst size (used only in SS mode). */
+#define EP_CFG_MAXBURST_MASK	GENMASK(11, 8)
+#define EP_CFG_MAXBURST(p)	(((p) << 8) & EP_CFG_MAXBURST_MASK)
+/* ISO max burst. */
+#define EP_CFG_MULT_MASK	GENMASK(15, 14)
+#define EP_CFG_MULT(p)		(((p) << 14) & EP_CFG_MULT)
+/* ISO max burst. */
+#define EP_CFG_MAXPKTSIZE_MASK	GENMASK(26, 16)
+#define EP_CFG_MAXPKTSIZE(p)	(((p) << 16) & EP_CFG_MAXPKTSIZE_MASK)
+/* Max number of buffered packets. */
+#define EP_CFG_BUFFERING_MASK	GENMASK(31, 27)
+#define EP_CFG_BUFFERING(p)	(((p) << 27) & EP_CFG_BUFFERING_MASK)
+
+/* EP_CMD - bitmasks */
+/* Endpoint reset. */
+#define EP_CMD_EPRST		BIT(0)
+/* Endpoint STALL set. */
+#define EP_CMD_SSTALL		BIT(1)
+/* Endpoint STALL clear. */
+#define EP_CMD_CSTALL		BIT(2)
+/* Send ERDY TP. */
+#define EP_CMD_ERDY		BIT(3)
+/* Request complete. */
+#define EP_CMD_REQ_CMPL		BIT(5)
+/* Transfer descriptor ready. */
+#define EP_CMD_DRDY		BIT(6)
+/* Data flush. */
+#define EP_CMD_DFLUSH		BIT(7)
+/*
+ * Transfer Descriptor Length write  (used only for Bulk Stream capable
+ * endpoints in SS mode).
+ */
+#define EP_CMD_STDL		BIT(8)
+/* Transfer Descriptor Length (used only in SS mode for bulk endpoints). */
+#define EP_CMD_TDL_MASK		GENMASK(15, 9)
+#define EP_CMD_TDL(p)		(((p) << 9) & EP_CMD_TDL_MASK)
+/* ERDY Stream ID value (used in SS mode). */
+#define EP_CMD_ERDY_SID_MASK	GENMASK(31, 16)
+#define EP_CMD_ERDY_SID(p)	(((p) << 16) & EP_CMD_SID_MASK)
+
+/* EP_STS - bitmasks */
+/* Setup transfer complete. */
+#define EP_STS_SETUP		BIT(0)
+/* Endpoint STALL status. */
+#define EP_STS_STALL(p)		((p) & BIT(1))
+/* Interrupt On Complete. */
+#define EP_STS_IOC		BIT(2)
+/* Interrupt on Short Packet. */
+#define EP_STS_ISP		BIT(3)
+/* Transfer descriptor missing. */
+#define EP_STS_DESCMIS		BIT(4)
+/* Stream Rejected (used only in SS mode) */
+#define EP_STS_STREAMR		BIT(5)
+/* EXIT from MOVE DATA State (used only for stream transfers in SS mode). */
+#define EP_STS_MD_EXIT		BIT(6)
+/* TRB error. */
+#define EP_STS_TRBERR		BIT(7)
+/* Not ready (used only in SS mode). */
+#define EP_STS_NRDY		BIT(8)
+/* DMA busy. */
+#define EP_STS_DBUSY(p)		((p) & BIT(9))
+/* Endpoint Buffer Empty */
+#define EP_STS_BUFFEMPTY(p)	((p) & BIT(10))
+/* Current Cycle Status */
+#define EP_STS_CCS(p)		((p) & BIT(11))
+/* Prime (used only in SS mode. */
+#define EP_STS_PRIME		BIT(12)
+/* Stream error (used only in SS mode). */
+#define EP_STS_SIDERR		BIT(13)
+/* OUT size mismatch. */
+#define EP_STS_OUTSMM		BIT(14)
+/* ISO transmission error. */
+#define EP_STS_ISOERR		BIT(15)
+/* Host Packet Pending (only for SS mode). */
+#define EP_STS_HOSTPP(p)	((p) & BIT(16))
+/* Stream Protocol State Machine State (only for Bulk stream endpoints). */
+#define EP_STS_SPSMST_MASK		GENMASK(18, 17)
+#define EP_STS_SPSMST_DISABLED(p)	(((p) & EP_STS_SPSMST_MASK) >> 17)
+#define EP_STS_SPSMST_IDLE(p)		(((p) & EP_STS_SPSMST_MASK) >> 17)
+#define EP_STS_SPSMST_START_STREAM(p)	(((p) & EP_STS_SPSMST_MASK) >> 17)
+#define EP_STS_SPSMST_MOVE_DATA(p)	(((p) & EP_STS_SPSMST_MASK) >> 17)
+/* Interrupt On Transfer complete. */
+#define EP_STS_IOT		BIT(19)
+/* OUT queue endpoint number. */
+#define EP_STS_OUTQ_NO_MASK	GENMASK(27, 24)
+#define EP_STS_OUTQ_NO(p)	(((p) & EP_STS_OUTQ_NO_MASK) >> 24)
+/* OUT queue valid flag. */
+#define EP_STS_OUTQ_VAL_MASK	BIT(28)
+#define EP_STS_OUTQ_VAL(p)	((p) & EP_STS_OUTQ_VAL_MASK)
+/* SETUP WAIT. */
+#define EP_STS_STPWAIT		BIT(31)
+
+/* EP_STS_SID - bitmasks */
+/* Stream ID (used only in SS mode). */
+#define EP_STS_SID_MASK		GENMASK(15, 0)
+#define EP_STS_SID(p)		((p) & EP_STS_SID_MASK)
+
+/* EP_STS_EN - bitmasks */
+/* SETUP interrupt enable. */
+#define EP_STS_EN_SETUPEN	BIT(0)
+/* OUT transfer missing descriptor enable. */
+#define EP_STS_EN_DESCMISEN	BIT(4)
+/* Stream Rejected enable. */
+#define EP_STS_EN_STREAMREN	BIT(5)
+/* Move Data Exit enable.*/
+#define EP_STS_EN_MD_EXITEN	BIT(6)
+/* TRB enable. */
+#define EP_STS_EN_TRBERREN	BIT(7)
+/* NRDY enable. */
+#define EP_STS_EN_NRDYEN	BIT(8)
+/* Prime enable. */
+#define EP_STS_EN_PRIMEEEN	BIT(12)
+/* Stream error enable. */
+#define EP_STS_EN_SIDERREN	BIT(13)
+/* OUT size mismatch enable. */
+#define EP_STS_EN_OUTSMMEN	BIT(14)
+/* ISO transmission error enable. */
+#define EP_STS_EN_ISOERREN	BIT(15)
+/* Interrupt on Transmission complete enable. */
+#define EP_STS_EN_IOTEN		BIT(19)
+/* Setup Wait interrupt enable. */
+#define EP_STS_EN_STPWAITEN	BIT(31)
+
+/* DRBL- bitmasks */
+#define DB_VALUE_BY_INDEX(index) (1 << (index))
+#define DB_VALUE_EP0_OUT	BIT(0)
+#define DB_VALUE_EP0_IN		BIT(16)
+
+/* EP_IEN - bitmasks */
+#define EP_IEN(index)		(1 << (index))
+#define EP_IEN_EP_OUT0		BIT(0)
+#define EP_IEN_EP_IN0		BIT(16)
+
+/* EP_ISTS - bitmasks */
+#define EP_ISTS(index)		(1 << (index))
+#define EP_ISTS_EP_OUT0		BIT(0)
+#define EP_ISTS_EP_IN0		BIT(16)
+
+/* EP_PWR- bitmasks */
+/*Power Shut Off capability enable*/
+#define PUSB_PWR_PSO_EN		BIT(0)
+/*Power Shut Off capability disable*/
+#define PUSB_PWR_PSO_DS		BIT(1)
+/*
+ * Enables turning-off Reference Clock.
+ * This bit is optional and implemented only when support for OTG is
+ * implemented (indicated by OTG_READY bit set to '1').
+ */
+#define PUSB_PWR_STB_CLK_SWITCH_EN	BIT(8)
+/*
+ * Status bit indicating that operation required by STB_CLK_SWITCH_EN write
+ * is completed
+ */
+#define PUSB_PWR_STB_CLK_SWITCH_DONE	BIT(9)
+/* This bit informs if Fast Registers Access is enabled. */
+#define PUSB_PWR_FST_REG_ACCESS_STAT	BIT(30)
+/* Fast Registers Access Enable. */
+#define PUSB_PWR_FST_REG_ACCESS	BIT(31)
+
+/* USB_CAP1- bitmasks */
+/*
+ * SFR Interface type
+ * These field reflects type of SFR interface implemented:
+ * 0x0 - OCP
+ * 0x1 - AHB,
+ * 0x2 - PLB
+ * 0x3 - AXI
+ * 0x4-0xF - reserved
+ */
+#define USB_CAP1_SFR_TYPE_MASK	GENMASK(3, 0)
+#define DEV_SFR_TYPE_OCP(p)	(((p) & USB_CAP1_SFR_TYPE_MASK) == 0x0)
+#define DEV_SFR_TYPE_AHB(p)	(((p) & USB_CAP1_SFR_TYPE_MASK) == 0x1)
+#define DEV_SFR_TYPE_PLB(p)	(((p) & USB_CAP1_SFR_TYPE_MASK) == 0x2)
+#define DEV_SFR_TYPE_AXI(p)	(((p) & USB_CAP1_SFR_TYPE_MASK) == 0x3)
+/*
+ * SFR Interface width
+ * These field reflects width of SFR interface implemented:
+ * 0x0 - 8 bit interface,
+ * 0x1 - 16 bit interface,
+ * 0x2 - 32 bit interface
+ * 0x3 - 64 bit interface
+ * 0x4-0xF - reserved
+ */
+#define USB_CAP1_SFR_WIDTH_MASK	GENMASK(7, 4)
+#define DEV_SFR_WIDTH_8(p)	(((p) & USB_CAP1_SFR_WIDTH_MASK) == (0x0 << 4))
+#define DEV_SFR_WIDTH_16(p)	(((p) & USB_CAP1_SFR_WIDTH_MASK) == (0x1 << 4))
+#define DEV_SFR_WIDTH_32(p)	(((p) & USB_CAP1_SFR_WIDTH_MASK) == (0x2 << 4))
+#define DEV_SFR_WIDTH_64(p)	(((p) & USB_CAP1_SFR_WIDTH_MASK) == (0x3 << 4))
+/*
+ * DMA Interface type
+ * These field reflects type of DMA interface implemented:
+ * 0x0 - OCP
+ * 0x1 - AHB,
+ * 0x2 - PLB
+ * 0x3 - AXI
+ * 0x4-0xF - reserved
+ */
+#define USB_CAP1_DMA_TYPE_MASK	GENMASK(11, 8)
+#define DEV_DMA_TYPE_OCP(p)	(((p) & USB_CAP1_DMA_TYPE_MASK) == (0x0 << 8))
+#define DEV_DMA_TYPE_AHB(p)	(((p) & USB_CAP1_DMA_TYPE_MASK) == (0x1 << 8))
+#define DEV_DMA_TYPE_PLB(p)	(((p) & USB_CAP1_DMA_TYPE_MASK) == (0x2 << 8))
+#define DEV_DMA_TYPE_AXI(p)	(((p) & USB_CAP1_DMA_TYPE_MASK) == (0x3 << 8))
+/*
+ * DMA Interface width
+ * These field reflects width of DMA interface implemented:
+ * 0x0 - reserved,
+ * 0x1 - reserved,
+ * 0x2 - 32 bit interface
+ * 0x3 - 64 bit interface
+ * 0x4-0xF - reserved
+ */
+#define USB_CAP1_DMA_WIDTH_MASK	GENMASK(15, 12)
+#define DEV_DMA_WIDTH_32(p)	(((p) & USB_CAP1_DMA_WIDTH_MASK) == (0x2 << 12))
+#define DEV_DMA_WIDTH_64(p)	(((p) & USB_CAP1_DMA_WIDTH_MASK) == (0x3 << 12))
+/*
+ * USB3 PHY Interface type
+ * These field reflects type of USB3 PHY interface implemented:
+ * 0x0 - USB PIPE,
+ * 0x1 - RMMI,
+ * 0x2-0xF - reserved
+ */
+#define USB_CAP1_U3PHY_TYPE_MASK GENMASK(19, 16)
+#define DEV_U3PHY_PIPE(p) (((p) & USB_CAP1_U3PHY_TYPE_MASK) == (0x0 << 16))
+#define DEV_U3PHY_RMMI(p) (((p) & USB_CAP1_U3PHY_TYPE_MASK) == (0x1 << 16))
+/*
+ * USB3 PHY Interface width
+ * These field reflects width of USB3 PHY interface implemented:
+ * 0x0 - 8 bit PIPE interface,
+ * 0x1 - 16 bit PIPE interface,
+ * 0x2 - 32 bit PIPE interface,
+ * 0x3 - 64 bit PIPE interface
+ * 0x4-0xF - reserved
+ * Note: When SSIC interface is implemented this field shows the width of
+ * internal PIPE interface. The RMMI interface is always 20bit wide.
+ */
+#define USB_CAP1_U3PHY_WIDTH_MASK GENMASK(23, 20)
+#define DEV_U3PHY_WIDTH_8(p) \
+	(((p) & USB_CAP1_U3PHY_WIDTH_MASK) == (0x0 << 20))
+#define DEV_U3PHY_WIDTH_16(p) \
+	(((p) & USB_CAP1_U3PHY_WIDTH_MASK) == (0x1 << 16))
+#define DEV_U3PHY_WIDTH_32(p) \
+	(((p) & USB_CAP1_U3PHY_WIDTH_MASK) == (0x2 << 20))
+#define DEV_U3PHY_WIDTH_64(p) \
+	(((p) & USB_CAP1_U3PHY_WIDTH_MASK) == (0x3 << 16))
+
+/*
+ * USB2 PHY Interface enable
+ * These field informs if USB2 PHY interface is implemented:
+ * 0x0 - interface NOT implemented,
+ * 0x1 - interface implemented
+ */
+#define USB_CAP1_U2PHY_EN(p)	((p) & BIT(24))
+/*
+ * USB2 PHY Interface type
+ * These field reflects type of USB2 PHY interface implemented:
+ * 0x0 - UTMI,
+ * 0x1 - ULPI
+ */
+#define DEV_U2PHY_ULPI(p)	((p) & BIT(25))
+/*
+ * USB2 PHY Interface width
+ * These field reflects width of USB2 PHY interface implemented:
+ * 0x0 - 8 bit interface,
+ * 0x1 - 16 bit interface,
+ * Note: The ULPI interface is always 8bit wide.
+ */
+#define DEV_U2PHY_WIDTH_16(p)	((p) & BIT(26))
+/*
+ * OTG Ready
+ * 0x0 - pure device mode
+ * 0x1 - some features and ports for CDNS USB OTG controller are implemented.
+ */
+#define USB_CAP1_OTG_READY(p)	((p) & BIT(27))
+
+/* USB_CAP2- bitmasks */
+/*
+ * The actual size of the connected On-chip RAM memory in kB:
+ * - 0 means 256 kB (max supported mem size)
+ * - value other than 0 reflects the mem size in kB
+ */
+#define USB_CAP2_ACTUAL_MEM_SIZE(p) ((p) & GENMASK(7, 0))
+/*
+ * Max supported mem size
+ * These field reflects width of on-chip RAM address bus width,
+ * which determines max supported mem size:
+ * 0x0-0x7 - reserved,
+ * 0x8 - support for 4kB mem,
+ * 0x9 - support for 8kB mem,
+ * 0xA - support for 16kB mem,
+ * 0xB - support for 32kB mem,
+ * 0xC - support for 64kB mem,
+ * 0xD - support for 128kB mem,
+ * 0xE - support for 256kB mem,
+ * 0xF - reserved
+ */
+#define USB_CAP2_MAX_MEM_SIZE(p) ((p) & GENMASK(11, 8))
+
+/* USB_CAP3- bitmasks */
+#define EP_IS_IMPLEMENTED(reg, index) ((reg) & (1 << (index)))
+
+/* USB_CAP4- bitmasks */
+#define EP_SUPPORT_ISO(reg, index) ((reg) & (1 << (index)))
+
+/* USB_CAP5- bitmasks */
+#define EP_SUPPORT_STREAM(reg, index) ((reg) & (1 << (index)))
+
+/* USB_CAP6- bitmasks */
+/* The USBSS-DEV Controller  Internal build number. */
+#define GET_DEV_VERSION__INTERNAL_NUMBER(p) ((p) & GENMASK(7, 0))
+/* The USBSS-DEV Controller  version number. */
+#define GET_DEV_VERSION(p) ((p) & GENMASK(31, 8))
+
+/* DBG_LINK1- bitmasks */
+/*
+ * LFPS_MIN_DET_U1_EXIT value This parameter configures the minimum
+ * time required for decoding the received LFPS as an LFPS.U1_Exit.
+ */
+#define DBG_LINK1_LFPS_MIN_DET_U1_EXIT(p)	((p) & GENMASK(7, 0))
+/*
+ * LFPS_MIN_GEN_U1_EXIT value This parameter configures the minimum time for
+ * phytxelecidle deassertion when LFPS.U1_Exit
+ */
+#define DBG_LINK1_LFPS_MIN_GEN_U1_EXIT(p)	(((p) << 8) & GENMASK(15, 8))
+/*
+ * RXDET_BREAK_DIS value This parameter configures terminating the Far-end
+ * Receiver termination detection sequence:
+ * 0: it is possible that USBSS_DEV will terminate Farend receiver
+ *    termination detection sequence
+ * 1: USBSS_DEV will not terminate Far-end receiver termination
+ *    detection sequence
+ */
+#define DBG_LINK1_RXDET_BREAK_DIS		BIT(16)
+/* LFPS_GEN_PING value This parameter configures the LFPS.Ping generation */
+#define DBG_LINK1_LFPS_GEN_PING(p)		(((p) << 17) & GENMASK(21, 17))
+/*
+ * Set the LFPS_MIN_DET_U1_EXIT value Writing '1' to this bit writes the
+ * LFPS_MIN_DET_U1_EXIT field value to the device. This bit is automatically
+ * cleared. Writing '0' has no effect
+ */
+#define DBG_LINK1_LFPS_MIN_DET_U1_EXIT_SET	BIT(24)
+/*
+ * Set the LFPS_MIN_GEN_U1_EXIT value. Writing '1' to this bit writes the
+ * LFPS_MIN_GEN_U1_EXIT field value to the device. This bit is automatically
+ * cleared. Writing '0' has no effect
+ */
+#define DBG_LINK1_LFPS_MIN_GEN_U1_EXIT_SET	BIT(25)
+/*
+ * Set the RXDET_BREAK_DIS value Writing '1' to this bit writes
+ * the RXDET_BREAK_DIS field value to the device. This bit is automatically
+ * cleared. Writing '0' has no effect
+ */
+#define DBG_LINK1_RXDET_BREAK_DIS_SET		BIT(26)
+/*
+ * Set the LFPS_GEN_PING_SET value Writing '1' to this bit writes
+ * the LFPS_GEN_PING field value to the device. This bit is automatically
+ * cleared. Writing '0' has no effect."
+ */
+#define DBG_LINK1_LFPS_GEN_PING_SET		BIT(27)
+
+#define gadget_to_cdns3_device(g) (container_of(g, struct cdns3_device, gadget))
+
+#define ep_to_cdns3_ep(ep) (container_of(ep, struct cdns3_endpoint, endpoint))
+
+/*-------------------------------------------------------------------------*/
+/*
+ * USBSS-DEV DMA interface.
+ */
+#define TRBS_PER_SEGMENT	40
+
+#define ISO_MAX_INTERVAL	10
+
+/*
+ *Only for ISOC endpoints - maximum number of TRBs is calculated as
+ * pow(2, bInterval-1) * number of usb requests. It is limitation made by
+ * driver to save memory. Controller must prepare TRB for each ITP even
+ * if bInterval > 1. It's the reason why driver needs so many TRBs for
+ * isochronous endpoints.
+ */
+#define TRBS_PER_ISOC_SEGMENT	(ISO_MAX_INTERVAL * 8)
+
+#define GET_TRBS_PER_SEGMENT(ep_type) ((ep_type) == USB_ENDPOINT_XFER_ISOC ? \
+				      TRBS_PER_ISOC_SEGMENT : TRBS_PER_SEGMENT)
+/**
+ * struct cdns3_trb - represent Transfer Descriptor block.
+ * @buffer:	pointer to buffer data
+ * @length:	length of data
+ * @control:	control flags.
+ *
+ * This structure describes transfer block serviced by DMA module.
+ */
+struct cdns3_trb {
+	__le32 buffer;
+	__le32 length;
+	__le32 control;
+};
+
+#define TRB_SIZE		(sizeof(struct cdns3_trb))
+#define TRB_RING_SIZE		(TRB_SIZE * TRBS_PER_SEGMENT)
+#define TRB_ISO_RING_SIZE	(TRB_SIZE * TRBS_PER_ISOC_SEGMENT)
+#define TRB_CTRL_RING_SIZE	(TRB_SIZE * 2)
+
+/* TRB bit mask */
+#define TRB_TYPE_BITMASK	GENMASK(15, 10)
+#define TRB_TYPE(p)		((p) << 10)
+#define TRB_FIELD_TO_TYPE(p)	(((p) & TRB_TYPE_BITMASK) >> 10)
+
+/* TRB type IDs */
+/* bulk, interrupt, isoc , and control data stage */
+#define TRB_NORMAL		1
+/* TRB for linking ring segments */
+#define TRB_LINK		6
+
+/* Cycle bit - indicates TRB ownership by driver or hw*/
+#define TRB_CYCLE		BIT(0)
+/*
+ * When set to '1', the device will toggle its interpretation of the Cycle bit
+ */
+#define TRB_TOGGLE		BIT(1)
+
+/* Interrupt on short packet*/
+#define TRB_ISP			BIT(2)
+/*Setting this bit enables FIFO DMA operation mode*/
+#define TRB_FIFO_MODE		BIT(3)
+/* Set PCIe no snoop attribute */
+#define TRB_CHAIN		BIT(4)
+/* Interrupt on completion */
+#define TRB_IOC			BIT(5)
+
+/* stream ID bitmasks. */
+#define TRB_STREAM_ID(p)	((p) & GENMASK(31, 16))
+
+/* transfer_len bitmasks. */
+#define TRB_LEN(p)		((p) & GENMASK(16, 0))
+
+/* transfer_len bitmasks - bits 31:24 */
+#define TRB_BURST_LEN(p)	((p) & GENMASK(31, 24))
+
+/* Data buffer pointer bitmasks*/
+#define TRB_BUFFER(p)		((p) & GENMASK(31, 0))
+
+/*-------------------------------------------------------------------------*/
+/* Driver numeric constants */
+
+/* Such declaration should be added to ch9.h */
+#define USB_DEVICE_MAX_ADDRESS		127
+
+/* Endpoint init values */
+#define CDNS3_EP_MAX_PACKET_LIMIT	1024
+#define CDNS3_EP_MAX_STREAMS		15
+#define CDNS3_EP0_MAX_PACKET_LIMIT	512
+
+/* All endpoints including EP0 */
+#define CDNS3_ENDPOINTS_MAX_COUNT	32
+#define CDNS3_EP_ZLP_BUF_SIZE		1024
+
+#define CDNS3_EP_BUF_SIZE		2	/* KB */
+#define CDNS3_ALIGNED_BUF_SIZE		16384	/* Bytes */
+#define CDNS3_MAX_NUM_DESCMISS_BUF	32
+#define CDNS3_DESCMIS_BUF_SIZE		2048	/* Bytes */
+/*-------------------------------------------------------------------------*/
+/* Used structs */
+
+struct cdns3_device;
+
+/**
+ * struct cdns3_endpoint - extended device side representation of USB endpoint.
+ * @endpoint: usb endpoint
+ * @pending_req_list: list of requests queuing on transfer ring.
+ * @deferred_req_list: list of requests waiting for queuing on transfer ring.
+ * @descmiss_req_list: list of requests internally allocated by driver (WA1).
+ * @trb_pool: transfer ring - array of transaction buffers
+ * @trb_pool_dma: dma address of transfer ring
+ * @cdns3_dev: device associated with this endpoint
+ * @name: a human readable name e.g. ep1out
+ * @flags: specify the current state of endpoint
+ * @descmis_req: internal transfer object used for getting data from on-chip
+ *     buffer. It can happen only if function driver doesn't send usb_request
+ *     object on time.
+ * @aligned_buff: aligned to 8 bytes data buffer. Buffer address used in
+ *     TRB shall be aligned to 8.
+ * @aligned_dma_addr: dma address of aligned_buff
+ * @dir: endpoint direction
+ * @num: endpoint number (1 - 15)
+ * @type: set to bmAttributes & USB_ENDPOINT_XFERTYPE_MASK
+ * @interval: interval between packets used for ISOC endpoint.
+ * @free_trbs: number of free TRBs in transfer ring
+ * @num_trbs: number of all TRBs in transfer ring
+ * @pcs: producer cycle state
+ * @ccs: consumer cycle state
+ * @enqueue: enqueue index in transfer ring
+ * @dequeue: dequeue index in transfer ring
+ */
+struct cdns3_endpoint {
+	struct usb_ep		endpoint;
+	struct list_head	pending_req_list;
+	struct list_head	deferred_req_list;
+	struct list_head	descmiss_req_list;
+
+	struct cdns3_trb	*trb_pool;
+	dma_addr_t		trb_pool_dma;
+
+	struct cdns3_device	*cdns3_dev;
+	char			name[20];
+
+#define EP_ENABLED		BIT(0)
+#define EP_STALL		BIT(1)
+#define EP_WEDGE		BIT(2)
+#define EP_TRANSFER_STARTED	BIT(3)
+#define EP_UPDATE_EP_TRBADDR	BIT(4)
+#define EP_PENDING_REQUEST	BIT(5)
+#define EP_RING_FULL		BIT(6)
+#define EP_CLAIMED		BIT(7)
+#define EP_QUIRK_EXTRA_BUF_DET	BIT(8)
+#define EP_QUIRK_EXTRA_BUF_EN	BIT(9)
+#define EP_QUIRK_END_TRANSFER	BIT(10)
+
+	u32			flags;
+
+	struct cdns3_request	*descmis_req;
+
+	void			*aligned_buff;
+	dma_addr_t		aligned_dma_addr;
+	u8			dir;
+	u8			num;
+	u8			type;
+	int			interval;
+
+	int			free_trbs;
+	int			num_trbs;
+	u8			pcs;
+	u8			ccs;
+	int			enqueue;
+	int			dequeue;
+};
+
+/**
+ * struct cdns3_request - extended device side representation of usb_request
+ *                        object .
+ * @request: generic usb_request object describing single I/O request.
+ * @priv_ep: extended representation of usb_ep object
+ * @trb: the first TRB association with this request
+ * @start_trb: number of the first TRB in transfer ring
+ * @end_trb: number of the last TRB in transfer ring
+ * @flags: flag specifying special usage of request
+ * @list: used by internally allocated request to add to descmiss_req_list.
+ */
+struct cdns3_request {
+	struct usb_request	request;
+	struct cdns3_endpoint	*priv_ep;
+	struct cdns3_trb	*trb;
+	int			start_trb;
+	int			end_trb;
+#define REQUEST_PENDING		BIT(0)
+#define REQUEST_INTERNAL	BIT(1)
+#define REQUEST_INTERNAL_CH	BIT(2)
+#define REQUEST_ZLP		BIT(3)
+	u32			flags;
+	struct list_head	list;
+};
+
+#define to_cdns3_request(r) (container_of(r, struct cdns3_request, request))
+
+/*Stages used during enumeration process.*/
+#define CDNS3_SETUP_STAGE		0x0
+#define CDNS3_DATA_STAGE		0x1
+#define CDNS3_STATUS_STAGE		0x2
+
+/**
+ * struct cdns3_device - represent USB device.
+ * @dev: pointer to device structure associated whit this controller
+ * @sysdev: pointer to the DMA capable device
+ * @gadget: device side representation of the peripheral controller
+ * @gadget_driver: pointer to the gadget driver
+ * @dev_ver: device controller version.
+ * @lock: for synchronizing
+ * @regs: base address for device side registers
+ * @setup_buf: used while processing usb control requests
+ * @setup_dma: dma address for setup_buf
+ * @zlp_buf - zlp buffer
+ * @ep0_stage: ep0 stage during enumeration process.
+ * @ep0_data_dir: direction for control transfer
+ * @eps: array of pointers to all endpoints with exclusion ep0
+ * @selected_ep: actually selected endpoint. It's used only to improve
+ *               performance.
+ * @isoch_delay: value from Set Isoch Delay request. Only valid on SS/SSP.
+ * @u1_allowed: allow device transition to u1 state
+ * @u2_allowed: allow device transition to u2 state
+ * @is_selfpowered: device is self powered
+ * @setup_pending: setup packet is processing by gadget driver
+ * @hw_configured_flag: hardware endpoint configuration was set.
+ * @wake_up_flag: allow device to remote up the host
+ * @status_completion_no_call: indicate that driver is waiting for status s
+ *     stage completion. It's used in deferred SET_CONFIGURATION request.
+ * @onchip_mem_allocated_size: actual size of on-chip memory assigned
+ *     to endpoints
+ * @pending_status_wq: workqueue handling status stage for deferred requests.
+ * @pending_status_request: request for which status stage was deferred
+ */
+struct cdns3_device {
+	struct device			*dev;
+	struct device			*sysdev;
+
+	struct usb_gadget		gadget;
+	struct usb_gadget_driver	*gadget_driver;
+
+#define CDNS_REVISION_V0		0x00024501
+#define CDNS_REVISION_V1		0x00024509
+	u32				dev_ver;
+
+	/* generic spin-lock for drivers */
+	spinlock_t			lock;
+
+	struct cdns3_usb_regs		__iomem *regs;
+
+	struct usb_ctrlrequest		*setup_buf;
+	dma_addr_t			setup_dma;
+	void				*zlp_buf;
+
+	u8				ep0_stage;
+	int				ep0_data_dir;
+
+	struct cdns3_endpoint		*eps[CDNS3_ENDPOINTS_MAX_COUNT];
+
+	u32				selected_ep;
+	u16				isoch_delay;
+
+	unsigned			u1_allowed:1;
+	unsigned			u2_allowed:1;
+	unsigned			is_selfpowered:1;
+	unsigned			setup_pending:1;
+	int				hw_configured_flag:1;
+	int				wake_up_flag:1;
+	unsigned			status_completion_no_call:1;
+
+	struct work_struct		pending_status_wq;
+	struct usb_request		*pending_status_request;
+
+	/*in KB */
+	int				onchip_mem_allocated_size;
+};
+
+int cdns3_handshake(void __iomem *ptr, u32 mask, u32 done, int usec);
+void cdns3_set_register_bit(void __iomem *ptr, u32 mask);
+dma_addr_t cdns3_trb_virt_to_dma(struct cdns3_endpoint *priv_ep,
+				 struct cdns3_trb *trb);
+enum usb_device_speed cdns3_get_speed(struct cdns3_device *priv_dev);
+void cdns3_pending_setup_status_handler(struct work_struct *work);
+void cdns3_hw_reset_eps_config(struct cdns3_device *priv_dev);
+void cdns3_set_hw_configuration(struct cdns3_device *priv_dev);
+void cdns3_select_ep(struct cdns3_device *priv_dev, u32 ep);
+void cdns3_allow_enable_l1(struct cdns3_device *priv_dev, int enable);
+struct usb_request *cdns3_next_request(struct list_head *list);
+int cdns3_ep_run_transfer(struct cdns3_endpoint *priv_ep,
+			  struct usb_request *request);
+int cdns3_allocate_trb_pool(struct cdns3_endpoint *priv_ep);
+u8 cdns3_ep_addr_to_index(u8 ep_addr);
+int cdns3_gadget_ep_set_wedge(struct usb_ep *ep);
+int cdns3_gadget_ep_set_halt(struct usb_ep *ep, int value);
+struct usb_request *cdns3_gadget_ep_alloc_request(struct usb_ep *ep,
+						  gfp_t gfp_flags);
+void cdns3_gadget_ep_free_request(struct usb_ep *ep,
+				  struct usb_request *request);
+int cdns3_gadget_ep_dequeue(struct usb_ep *ep, struct usb_request *request);
+void cdns3_gadget_giveback(struct cdns3_endpoint *priv_ep,
+			   struct cdns3_request *priv_req,
+			   int status);
+
+int cdns3_init_ep0(struct cdns3_device *priv_dev,
+		   struct cdns3_endpoint *priv_ep);
+void cdns3_ep0_config(struct cdns3_device *priv_dev);
+void cdns3_ep_config(struct cdns3_endpoint *priv_ep);
+void cdns3_check_ep0_interrupt_proceed(struct cdns3_device *priv_dev, int dir);
+
+#endif /* __LINUX_CDNS3_GADGET */
diff --git a/drivers/usb/cdns3/host-export.h b/drivers/usb/cdns3/host-export.h
new file mode 100644
index 000000000000..b498a170b7e8
--- /dev/null
+++ b/drivers/usb/cdns3/host-export.h
@@ -0,0 +1,28 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Cadence USBSS DRD Driver - Host Export APIs
+ *
+ * Copyright (C) 2017-2018 NXP
+ *
+ * Authors: Peter Chen <peter.chen@nxp.com>
+ */
+#ifndef __LINUX_CDNS3_HOST_EXPORT
+#define __LINUX_CDNS3_HOST_EXPORT
+
+#ifdef CONFIG_USB_CDNS3_HOST
+
+int cdns3_host_init(struct cdns3 *cdns);
+void cdns3_host_exit(struct cdns3 *cdns);
+
+#else
+
+static inline int cdns3_host_init(struct cdns3 *cdns)
+{
+	return -ENXIO;
+}
+
+static inline void cdns3_host_exit(struct cdns3 *cdns) { }
+
+#endif /* CONFIG_USB_CDNS3_HOST */
+
+#endif /* __LINUX_CDNS3_HOST_EXPORT */
diff --git a/drivers/usb/cdns3/host.c b/drivers/usb/cdns3/host.c
new file mode 100644
index 000000000000..b43b0236a885
--- /dev/null
+++ b/drivers/usb/cdns3/host.c
@@ -0,0 +1,72 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Cadence USBSS DRD Driver - host side
+ *
+ * Copyright (C) 2018 Cadence Design Systems.
+ * Copyright (C) 2017-2018 NXP
+ *
+ * Authors: Peter Chen <peter.chen@nxp.com>
+ *	    Pawel Laszczak <pawell@cadence.com>
+ */
+
+#include <linux/platform_device.h>
+#include "core.h"
+
+static int __cdns3_host_init(struct cdns3 *cdns)
+{
+	struct platform_device *xhci;
+	int ret;
+
+	xhci = platform_device_alloc("xhci-hcd", PLATFORM_DEVID_AUTO);
+	if (!xhci) {
+		dev_err(cdns->dev, "couldn't allocate xHCI device\n");
+		return -ENOMEM;
+	}
+
+	xhci->dev.parent = cdns->dev;
+	cdns->host_dev = xhci;
+
+	ret = platform_device_add_resources(xhci, cdns->xhci_res,
+					    CDNS3_XHCI_RESOURCES_NUM);
+	if (ret) {
+		dev_err(cdns->dev, "couldn't add resources to xHCI device\n");
+		goto err1;
+	}
+
+	ret = platform_device_add(xhci);
+	if (ret) {
+		dev_err(cdns->dev, "failed to register xHCI device\n");
+		goto err1;
+	}
+
+	return 0;
+err1:
+	platform_device_put(xhci);
+	return ret;
+}
+
+static void cdns3_host_exit(struct cdns3 *cdns)
+{
+	platform_device_unregister(cdns->host_dev);
+	cdns->host_dev = NULL;
+}
+
+int cdns3_host_init(struct cdns3 *cdns)
+{
+	struct cdns3_role_driver *rdrv;
+
+	rdrv = devm_kzalloc(cdns->dev, sizeof(*rdrv), GFP_KERNEL);
+	if (!rdrv)
+		return -ENOMEM;
+
+	rdrv->start	= __cdns3_host_init;
+	rdrv->stop	= cdns3_host_exit;
+	rdrv->state	= CDNS3_ROLE_STATE_INACTIVE;
+	rdrv->suspend	= NULL;
+	rdrv->resume	= NULL;
+	rdrv->name	= "host";
+
+	cdns->roles[CDNS3_ROLE_HOST] = rdrv;
+
+	return 0;
+}
diff --git a/drivers/usb/cdns3/trace.c b/drivers/usb/cdns3/trace.c
new file mode 100644
index 000000000000..587ae08e019d
--- /dev/null
+++ b/drivers/usb/cdns3/trace.c
@@ -0,0 +1,11 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * USBSS device controller driver Trace Support
+ *
+ * Copyright (C) 2018 Cadence.
+ *
+ * Author: Pawel Laszczak <pawell@cadence.com>
+ */
+
+#define CREATE_TRACE_POINTS
+#include "trace.h"
diff --git a/drivers/usb/cdns3/trace.h b/drivers/usb/cdns3/trace.h
new file mode 100644
index 000000000000..f3d7a91fae86
--- /dev/null
+++ b/drivers/usb/cdns3/trace.h
@@ -0,0 +1,389 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * USBSS device controller driver.
+ * Trace support header file.
+ *
+ * Copyright (C) 2018 Cadence.
+ *
+ * Author: Pawel Laszczak <pawell@cadence.com>
+ */
+
+#undef TRACE_SYSTEM
+#define TRACE_SYSTEM cdns3
+
+#if !defined(__LINUX_CDNS3_TRACE) || defined(TRACE_HEADER_MULTI_READ)
+#define __LINUX_CDNS3_TRACE
+
+#include <linux/types.h>
+#include <linux/tracepoint.h>
+#include <asm/byteorder.h>
+#include <linux/usb/ch9.h>
+#include "core.h"
+#include "gadget.h"
+#include "debug.h"
+
+#define CDNS3_MSG_MAX	500
+
+DECLARE_EVENT_CLASS(cdns3_log_doorbell,
+	TP_PROTO(const char *ep_name, u32 ep_trbaddr),
+	TP_ARGS(ep_name, ep_trbaddr),
+	TP_STRUCT__entry(
+		__string(name, ep_name)
+		__field(u32, ep_trbaddr)
+	),
+	TP_fast_assign(
+		__assign_str(name, ep_name);
+		__entry->ep_trbaddr = ep_trbaddr;
+	),
+	TP_printk("//Ding Dong %s, ep_trbaddr %08x", __get_str(name),
+		  __entry->ep_trbaddr)
+);
+
+DEFINE_EVENT(cdns3_log_doorbell, cdns3_doorbell_ep0,
+	TP_PROTO(const char *ep_name, u32 ep_trbaddr),
+	TP_ARGS(ep_name, ep_trbaddr)
+);
+
+DEFINE_EVENT(cdns3_log_doorbell, cdns3_doorbell_epx,
+	TP_PROTO(const char *ep_name, u32 ep_trbaddr),
+	TP_ARGS(ep_name, ep_trbaddr)
+);
+
+DECLARE_EVENT_CLASS(cdns3_log_usb_irq,
+	TP_PROTO(struct cdns3_device *priv_dev, u32 usb_ists),
+	TP_ARGS(priv_dev, usb_ists),
+	TP_STRUCT__entry(
+		__field(enum usb_device_speed, speed)
+		__field(u32, usb_ists)
+		__dynamic_array(char, str, CDNS3_MSG_MAX)
+	),
+	TP_fast_assign(
+		__entry->speed = cdns3_get_speed(priv_dev);
+		__entry->usb_ists = usb_ists;
+	),
+	TP_printk("%s", cdns3_decode_usb_irq(__get_str(str), __entry->speed,
+					     __entry->usb_ists))
+);
+
+DEFINE_EVENT(cdns3_log_usb_irq, cdns3_usb_irq,
+	TP_PROTO(struct cdns3_device *priv_dev, u32 usb_ists),
+	TP_ARGS(priv_dev, usb_ists)
+);
+
+DECLARE_EVENT_CLASS(cdns3_log_epx_irq,
+	TP_PROTO(struct cdns3_device *priv_dev, struct cdns3_endpoint *priv_ep),
+	TP_ARGS(priv_dev, priv_ep),
+	TP_STRUCT__entry(
+		__string(ep_name, priv_ep->name)
+		__field(u32, ep_sts)
+		__field(u32, ep_traddr)
+		__dynamic_array(char, str, CDNS3_MSG_MAX)
+	),
+	TP_fast_assign(
+		__assign_str(ep_name, priv_ep->name);
+		__entry->ep_sts = readl(&priv_dev->regs->ep_sts);
+		__entry->ep_traddr = readl(&priv_dev->regs->ep_traddr);
+	),
+	TP_printk("%s, ep_traddr: %08x",
+		  cdns3_decode_epx_irq(__get_str(str),
+				       __get_str(ep_name),
+				       __entry->ep_sts),
+		  __entry->ep_traddr)
+);
+
+DEFINE_EVENT(cdns3_log_epx_irq, cdns3_epx_irq,
+	TP_PROTO(struct cdns3_device *priv_dev, struct cdns3_endpoint *priv_ep),
+	TP_ARGS(priv_dev, priv_ep)
+);
+
+DECLARE_EVENT_CLASS(cdns3_log_ep0_irq,
+	TP_PROTO(struct cdns3_device *priv_dev,  u32 ep_sts),
+	TP_ARGS(priv_dev, ep_sts),
+	TP_STRUCT__entry(
+		__field(int, ep_dir)
+		__field(u32, ep_sts)
+		__dynamic_array(char, str, CDNS3_MSG_MAX)
+	),
+	TP_fast_assign(
+		__entry->ep_dir = priv_dev->ep0_data_dir;
+		__entry->ep_sts = ep_sts;
+	),
+	TP_printk("%s", cdns3_decode_ep0_irq(__get_str(str),
+					     __entry->ep_dir,
+					     __entry->ep_sts))
+);
+
+DEFINE_EVENT(cdns3_log_ep0_irq, cdns3_ep0_irq,
+	TP_PROTO(struct cdns3_device *priv_dev, u32 ep_sts),
+	TP_ARGS(priv_dev, ep_sts)
+);
+
+DECLARE_EVENT_CLASS(cdns3_log_ctrl,
+	TP_PROTO(struct usb_ctrlrequest *ctrl),
+	TP_ARGS(ctrl),
+	TP_STRUCT__entry(
+		__field(u8, bRequestType)
+		__field(u8, bRequest)
+		__field(u16, wValue)
+		__field(u16, wIndex)
+		__field(u16, wLength)
+		__dynamic_array(char, str, CDNS3_MSG_MAX)
+	),
+	TP_fast_assign(
+		__entry->bRequestType = ctrl->bRequestType;
+		__entry->bRequest = ctrl->bRequest;
+		__entry->wValue = le16_to_cpu(ctrl->wValue);
+		__entry->wIndex = le16_to_cpu(ctrl->wIndex);
+		__entry->wLength = le16_to_cpu(ctrl->wLength);
+	),
+	TP_printk("%s", usb_decode_ctrl(__get_str(str), __entry->bRequestType,
+					__entry->bRequest, __entry->wValue,
+					__entry->wIndex, __entry->wLength)
+	)
+);
+
+DEFINE_EVENT(cdns3_log_ctrl, cdns3_ctrl_req,
+	TP_PROTO(struct usb_ctrlrequest *ctrl),
+	TP_ARGS(ctrl)
+);
+
+DECLARE_EVENT_CLASS(cdns3_log_request,
+	TP_PROTO(struct cdns3_request *req),
+	TP_ARGS(req),
+	TP_STRUCT__entry(
+		__string(name, req->priv_ep->name)
+		__field(struct cdns3_request *, req)
+		__field(unsigned int, actual)
+		__field(unsigned int, length)
+		__field(int, status)
+		__field(int, zero)
+		__field(int, short_not_ok)
+		__field(int, no_interrupt)
+		__field(int, start_trb)
+		__field(int, end_trb)
+		__field(struct cdns3_trb *, start_trb_addr)
+		__field(int, flags)
+	),
+	TP_fast_assign(
+		__assign_str(name, req->priv_ep->name);
+		__entry->req = req;
+		__entry->actual = req->request.actual;
+		__entry->length = req->request.length;
+		__entry->status = req->request.status;
+		__entry->zero = req->request.zero;
+		__entry->short_not_ok = req->request.short_not_ok;
+		__entry->no_interrupt = req->request.no_interrupt;
+		__entry->start_trb = req->start_trb;
+		__entry->end_trb = req->end_trb;
+		__entry->start_trb_addr = req->trb;
+		__entry->flags = req->flags;
+	),
+	TP_printk("%s: req: %p, length: %u/%u %s%s%s, status: %d,"
+		  " trb: [start:%d, end:%d: virt addr %pa], flags:%x ",
+		__get_str(name), __entry->req, __entry->actual, __entry->length,
+		__entry->zero ? "zero | " : "",
+		__entry->short_not_ok ? "short | " : "",
+		__entry->no_interrupt ? "no int" : "",
+		__entry->status,
+		__entry->start_trb,
+		__entry->end_trb,
+		__entry->start_trb_addr,
+		__entry->flags
+	)
+);
+
+DEFINE_EVENT(cdns3_log_request, cdns3_alloc_request,
+	TP_PROTO(struct cdns3_request *req),
+	TP_ARGS(req)
+);
+
+DEFINE_EVENT(cdns3_log_request, cdns3_free_request,
+	TP_PROTO(struct cdns3_request *req),
+	TP_ARGS(req)
+);
+
+DEFINE_EVENT(cdns3_log_request, cdns3_ep_queue,
+	TP_PROTO(struct cdns3_request *req),
+	TP_ARGS(req)
+);
+
+DEFINE_EVENT(cdns3_log_request, cdns3_ep_dequeue,
+	TP_PROTO(struct cdns3_request *req),
+	TP_ARGS(req)
+);
+
+DEFINE_EVENT(cdns3_log_request, cdns3_gadget_giveback,
+	TP_PROTO(struct cdns3_request *req),
+	TP_ARGS(req)
+);
+
+DECLARE_EVENT_CLASS(cdns3_log_trb,
+	TP_PROTO(struct cdns3_endpoint *priv_ep, struct cdns3_trb *trb),
+	TP_ARGS(priv_ep, trb),
+	TP_STRUCT__entry(
+		__string(name, priv_ep->name)
+		__field(struct cdns3_trb *, trb)
+		__field(u32, buffer)
+		__field(u32, length)
+		__field(u32, control)
+		__field(u32, type)
+	),
+	TP_fast_assign(
+		__assign_str(name, priv_ep->name);
+		__entry->trb = trb;
+		__entry->buffer = trb->buffer;
+		__entry->length = trb->length;
+		__entry->control = trb->control;
+		__entry->type = usb_endpoint_type(priv_ep->endpoint.desc);
+	),
+	TP_printk("%s: trb 0x%pa, dma buf: 0x%08x, size: %ld, ctrl: 0x%08x (%s%s%s%s%s%s%s)",
+		__get_str(name), __entry->trb, __entry->buffer,
+		TRB_LEN(__entry->length), __entry->control,
+		__entry->control & TRB_CYCLE ? "C=1, " : "C=0, ",
+		__entry->control & TRB_TOGGLE ? "T=1, " : "T=0, ",
+		__entry->control & TRB_ISP ? "ISP, " : "",
+		__entry->control & TRB_FIFO_MODE ? "FIFO, " : "",
+		__entry->control & TRB_CHAIN ? "CHAIN, " : "",
+		__entry->control & TRB_IOC ? "IOC, " : "",
+		TRB_FIELD_TO_TYPE(__entry->control) == TRB_NORMAL ? "Normal" : "LINK"
+	)
+);
+
+DEFINE_EVENT(cdns3_log_trb, cdns3_prepare_trb,
+	TP_PROTO(struct cdns3_endpoint *priv_ep, struct cdns3_trb *trb),
+	TP_ARGS(priv_ep, trb)
+);
+
+DEFINE_EVENT(cdns3_log_trb, cdns3_complete_trb,
+	TP_PROTO(struct cdns3_endpoint *priv_ep, struct cdns3_trb *trb),
+	TP_ARGS(priv_ep, trb)
+);
+
+DECLARE_EVENT_CLASS(cdns3_log_ring,
+	TP_PROTO(struct cdns3_endpoint *priv_ep),
+	TP_ARGS(priv_ep),
+	TP_STRUCT__entry(
+		__dynamic_array(u8, ring, TRB_RING_SIZE)
+		__dynamic_array(u8, priv_ep, sizeof(struct cdns3_endpoint))
+		__dynamic_array(char, buffer,
+				(TRBS_PER_SEGMENT * 65) + CDNS3_MSG_MAX)
+	),
+	TP_fast_assign(
+		memcpy(__get_dynamic_array(priv_ep), priv_ep,
+		       sizeof(struct cdns3_endpoint));
+		memcpy(__get_dynamic_array(ring), priv_ep->trb_pool,
+		       TRB_RING_SIZE);
+	),
+
+	TP_printk("%s",
+		  cdns3_dbg_ring((struct cdns3_endpoint *)__get_str(priv_ep),
+				 (struct cdns3_trb *)__get_str(ring),
+				 __get_str(buffer)))
+);
+
+DEFINE_EVENT(cdns3_log_ring, cdns3_ring,
+	TP_PROTO(struct cdns3_endpoint *priv_ep),
+	TP_ARGS(priv_ep)
+);
+
+DECLARE_EVENT_CLASS(cdns3_log_ep,
+	TP_PROTO(struct cdns3_endpoint *priv_ep),
+	TP_ARGS(priv_ep),
+	TP_STRUCT__entry(
+		__string(name, priv_ep->name)
+		__field(unsigned int, maxpacket)
+		__field(unsigned int, maxpacket_limit)
+		__field(unsigned int, max_streams)
+		__field(unsigned int, maxburst)
+		__field(unsigned int, flags)
+		__field(unsigned int, dir)
+		__field(u8, enqueue)
+		__field(u8, dequeue)
+	),
+	TP_fast_assign(
+		__assign_str(name, priv_ep->name);
+		__entry->maxpacket = priv_ep->endpoint.maxpacket;
+		__entry->maxpacket_limit = priv_ep->endpoint.maxpacket_limit;
+		__entry->max_streams = priv_ep->endpoint.max_streams;
+		__entry->maxburst = priv_ep->endpoint.maxburst;
+		__entry->flags = priv_ep->flags;
+		__entry->dir = priv_ep->dir;
+		__entry->enqueue = priv_ep->enqueue;
+		__entry->dequeue = priv_ep->dequeue;
+	),
+	TP_printk("%s: mps: %d/%d. streams: %d, burst: %d, enq idx: %d, "
+		  "deq idx: %d, flags %s%s%s%s%s%s%s%s, dir: %s",
+		__get_str(name), __entry->maxpacket,
+		__entry->maxpacket_limit, __entry->max_streams,
+		__entry->maxburst, __entry->enqueue,
+		__entry->dequeue,
+		__entry->flags & EP_ENABLED ? "EN | " : "",
+		__entry->flags & EP_STALL ? "STALL | " : "",
+		__entry->flags & EP_WEDGE ? "WEDGE | " : "",
+		__entry->flags & EP_TRANSFER_STARTED ? "STARTED | " : "",
+		__entry->flags & EP_UPDATE_EP_TRBADDR ? "UPD TRB | " : "",
+		__entry->flags & EP_PENDING_REQUEST ? "REQ PEN | " : "",
+		__entry->flags & EP_RING_FULL ? "RING FULL |" : "",
+		__entry->flags & EP_CLAIMED ?  "CLAIMED " : "",
+		__entry->dir ? "IN" : "OUT"
+	)
+);
+
+DEFINE_EVENT(cdns3_log_ep, cdns3_gadget_ep_enable,
+	TP_PROTO(struct cdns3_endpoint *priv_ep),
+	TP_ARGS(priv_ep)
+);
+
+DEFINE_EVENT(cdns3_log_ep, cdns3_gadget_ep_disable,
+	TP_PROTO(struct cdns3_endpoint *priv_ep),
+	TP_ARGS(priv_ep)
+);
+
+DECLARE_EVENT_CLASS(cdns3_log_request_handled,
+	TP_PROTO(struct cdns3_request *priv_req, int current_index,
+		 int handled),
+	TP_ARGS(priv_req, current_index, handled),
+	TP_STRUCT__entry(
+		__field(struct cdns3_request *, priv_req)
+		__field(unsigned int, dma_position)
+		__field(unsigned int, handled)
+		__field(unsigned int, dequeue_idx)
+		__field(unsigned int, enqueue_idx)
+		__field(unsigned int, start_trb)
+		__field(unsigned int, end_trb)
+	),
+	TP_fast_assign(
+		__entry->priv_req = priv_req;
+		__entry->dma_position = current_index;
+		__entry->handled = handled;
+		__entry->dequeue_idx = priv_req->priv_ep->dequeue;
+		__entry->enqueue_idx = priv_req->priv_ep->enqueue;
+		__entry->start_trb = priv_req->start_trb;
+		__entry->end_trb = priv_req->end_trb;
+	),
+	TP_printk("Req: %p %s, DMA pos: %d, ep deq: %d, ep enq: %d,"
+		  " start trb: %d, end trb: %d",
+		__entry->priv_req,
+		__entry->handled ? "handled" : "not handled",
+		__entry->dma_position, __entry->dequeue_idx,
+		__entry->enqueue_idx, __entry->start_trb,
+		__entry->end_trb
+	)
+);
+
+DEFINE_EVENT(cdns3_log_request_handled, cdns3_request_handled,
+	TP_PROTO(struct cdns3_request *priv_req, int current_index,
+		 int handled),
+	TP_ARGS(priv_req, current_index, handled)
+);
+#endif /* __LINUX_CDNS3_TRACE */
+
+/* this part must be outside header guard */
+
+#undef TRACE_INCLUDE_PATH
+#define TRACE_INCLUDE_PATH .
+
+#undef TRACE_INCLUDE_FILE
+#define TRACE_INCLUDE_FILE trace
+
+#include <trace/define_trace.h>

^ permalink raw reply related	[flat|nested] 49+ messages in thread

* Re: [PATCH v2 5/5] usb:cdns3 Add Cadence USB3 DRD Driver
@ 2018-12-23 17:19     ` Randy Dunlap
  0 siblings, 0 replies; 49+ messages in thread
From: Randy Dunlap @ 2018-12-23 17:19 UTC (permalink / raw)
  To: Pawel Laszczak, devicetree
  Cc: gregkh, mark.rutland, linux-usb, hdegoede, heikki.krogerus,
	andy.shevchenko, robh+dt, rogerq, linux-kernel, adouglas,
	jbergsagel, nsekhar, nm, sureshp, peter.chen, pjez, kurahul

Hi,

Here are a few Kconfig text corrections.


On 12/23/18 7:13 AM, Pawel Laszczak wrote:
> diff --git a/drivers/usb/cdns3/Kconfig b/drivers/usb/cdns3/Kconfig
> new file mode 100644
> index 000000000000..4adfd87811e8
> --- /dev/null
> +++ b/drivers/usb/cdns3/Kconfig
> @@ -0,0 +1,44 @@
> +config USB_CDNS3
> +	tristate "Cadence USB3 Dual-Role Controller"
> +	depends on USB_SUPPORT && (USB || USB_GADGET) && HAS_DMA
> +	help
> +	  Say Y here if your system has a cadence USB3 dual-role controller.
> +	  It supports: dual-role switch, Host-only, and Peripheral-only.
> +
> +	  If you choose to build this driver is a dynamically linked

	                                     as

> +	  module, the module will be called cdns3.ko.
> +
> +if USB_CDNS3
> +
> +config USB_CDNS3_GADGET
> +        bool "Cadence USB3 device controller"
> +        depends on USB_GADGET
> +        help
> +          Say Y here to enable device controller functionality of the
> +          cadence USBSS-DEV driver.
> +
> +          This controller supports FF, HS and SS mode. It doesn't support
> +          LS and SSP mode

	                mode.

> +
> +config USB_CDNS3_HOST
> +        bool "Cadence USB3 host controller"
> +        depends on USB_XHCI_HCD
> +        help
> +          Say Y here to enable host controller functionality of the
> +          cadence driver.
> +
> +          Host controller is compliance with XHCI so it will use

	                        compliant

> +          standard XHCI driver.
> +
> +config USB_CDNS3_PCI_WRAP
> +	tristate "Cadence USB3 support on PCIe-based platforms"
> +	depends on USB_PCI && ACPI
> +	default USB_CDNS3
> +	help
> +	  If you're using the USBSS Core IP with a PCIe, please say
> +	  'Y' or 'M' here.
> +
> +	  If you choose to build this driver as module it will
> +	  be dynamically linked and module will be called cdns3-pci.ko
> +
> +endif


g'day.
-- 
~Randy

^ permalink raw reply	[flat|nested] 49+ messages in thread

* [v2,5/5] usb:cdns3 Add Cadence USB3 DRD Driver
@ 2018-12-23 17:19     ` Randy Dunlap
  0 siblings, 0 replies; 49+ messages in thread
From: Randy Dunlap @ 2018-12-23 17:19 UTC (permalink / raw)
  To: Pawel Laszczak, devicetree
  Cc: gregkh, mark.rutland, linux-usb, hdegoede, heikki.krogerus,
	andy.shevchenko, robh+dt, rogerq, linux-kernel, adouglas,
	jbergsagel, nsekhar, nm, sureshp, peter.chen, pjez, kurahul

Hi,

Here are a few Kconfig text corrections.


On 12/23/18 7:13 AM, Pawel Laszczak wrote:
> diff --git a/drivers/usb/cdns3/Kconfig b/drivers/usb/cdns3/Kconfig
> new file mode 100644
> index 000000000000..4adfd87811e8
> --- /dev/null
> +++ b/drivers/usb/cdns3/Kconfig
> @@ -0,0 +1,44 @@
> +config USB_CDNS3
> +	tristate "Cadence USB3 Dual-Role Controller"
> +	depends on USB_SUPPORT && (USB || USB_GADGET) && HAS_DMA
> +	help
> +	  Say Y here if your system has a cadence USB3 dual-role controller.
> +	  It supports: dual-role switch, Host-only, and Peripheral-only.
> +
> +	  If you choose to build this driver is a dynamically linked

	                                     as

> +	  module, the module will be called cdns3.ko.
> +
> +if USB_CDNS3
> +
> +config USB_CDNS3_GADGET
> +        bool "Cadence USB3 device controller"
> +        depends on USB_GADGET
> +        help
> +          Say Y here to enable device controller functionality of the
> +          cadence USBSS-DEV driver.
> +
> +          This controller supports FF, HS and SS mode. It doesn't support
> +          LS and SSP mode

	                mode.

> +
> +config USB_CDNS3_HOST
> +        bool "Cadence USB3 host controller"
> +        depends on USB_XHCI_HCD
> +        help
> +          Say Y here to enable host controller functionality of the
> +          cadence driver.
> +
> +          Host controller is compliance with XHCI so it will use

	                        compliant

> +          standard XHCI driver.
> +
> +config USB_CDNS3_PCI_WRAP
> +	tristate "Cadence USB3 support on PCIe-based platforms"
> +	depends on USB_PCI && ACPI
> +	default USB_CDNS3
> +	help
> +	  If you're using the USBSS Core IP with a PCIe, please say
> +	  'Y' or 'M' here.
> +
> +	  If you choose to build this driver as module it will
> +	  be dynamically linked and module will be called cdns3-pci.ko
> +
> +endif


g'day.

^ permalink raw reply	[flat|nested] 49+ messages in thread

* Re: [PATCH v2 5/5] usb:cdns3 Add Cadence USB3 DRD Driver
  2018-12-23 15:13   ` [PATCH v2 5/5] " Pawel Laszczak
  (?)
@ 2018-12-23 19:20     ` kbuild test robot
  -1 siblings, 0 replies; 49+ messages in thread
From: kbuild test robot @ 2018-12-23 19:20 UTC (permalink / raw)
  To: Pawel Laszczak
  Cc: kbuild-all, devicetree, gregkh, mark.rutland, linux-usb,
	hdegoede, heikki.krogerus, andy.shevchenko, robh+dt, rogerq,
	linux-kernel, adouglas, jbergsagel, nsekhar, nm, sureshp,
	peter.chen, pjez, kurahul, Pawel Laszczak

[-- Attachment #1: Type: text/plain, Size: 5737 bytes --]

Hi Pawel,

Thank you for the patch! Perhaps something to improve:

[auto build test WARNING on usb/usb-testing]
[also build test WARNING on v4.20-rc7 next-20181221]
[if your patch is applied to the wrong git tree, please drop us a note to help improve the system]

url:    https://github.com/0day-ci/linux/commits/Pawel-Laszczak/Introduced-new-Cadence-USBSS-DRD-Driver/20181223-231813
base:   https://git.kernel.org/pub/scm/linux/kernel/git/gregkh/usb.git usb-testing
config: x86_64-allmodconfig (attached as .config)
compiler: gcc-7 (Debian 7.3.0-1) 7.3.0
reproduce:
        # save the attached .config to linux build tree
        make ARCH=x86_64 

All warnings (new ones prefixed by >>):

>> drivers/usb/common/debug.c:253:25: warning: restricted __le16 degrades to integer
   drivers/usb/common/debug.c:254:25: warning: restricted __le16 degrades to integer
   drivers/usb/common/debug.c:255:25: warning: restricted __le16 degrades to integer
   drivers/usb/common/debug.c:256:25: warning: restricted __le16 degrades to integer
   drivers/usb/common/debug.c:257:25: warning: restricted __le16 degrades to integer
   drivers/usb/common/debug.c:258:25: warning: restricted __le16 degrades to integer

vim +253 drivers/usb/common/debug.c

cefb8b21 Pawel Laszczak 2018-12-23  205  
cefb8b21 Pawel Laszczak 2018-12-23  206  /**
cefb8b21 Pawel Laszczak 2018-12-23  207   * usb_decode_ctrl - returns a string representation of ctrl request
cefb8b21 Pawel Laszczak 2018-12-23  208   */
cefb8b21 Pawel Laszczak 2018-12-23  209  const char *usb_decode_ctrl(char *str, __u8 bRequestType, __u8 bRequest,
cefb8b21 Pawel Laszczak 2018-12-23  210  			    __u16 wValue,  __u16 wIndex, __u16 wLength)
cefb8b21 Pawel Laszczak 2018-12-23  211  {
cefb8b21 Pawel Laszczak 2018-12-23  212  	switch (bRequest) {
cefb8b21 Pawel Laszczak 2018-12-23  213  	case USB_REQ_GET_STATUS:
cefb8b21 Pawel Laszczak 2018-12-23  214  		usb_decode_get_status(bRequestType, wIndex, wLength, str);
cefb8b21 Pawel Laszczak 2018-12-23  215  		break;
cefb8b21 Pawel Laszczak 2018-12-23  216  	case USB_REQ_CLEAR_FEATURE:
cefb8b21 Pawel Laszczak 2018-12-23  217  	case USB_REQ_SET_FEATURE:
cefb8b21 Pawel Laszczak 2018-12-23  218  		usb_decode_set_clear_feature(bRequestType, bRequest, wValue,
cefb8b21 Pawel Laszczak 2018-12-23  219  					     wIndex, str);
cefb8b21 Pawel Laszczak 2018-12-23  220  		break;
cefb8b21 Pawel Laszczak 2018-12-23  221  	case USB_REQ_SET_ADDRESS:
cefb8b21 Pawel Laszczak 2018-12-23  222  		usb_decode_set_address(wValue, str);
cefb8b21 Pawel Laszczak 2018-12-23  223  		break;
cefb8b21 Pawel Laszczak 2018-12-23  224  	case USB_REQ_GET_DESCRIPTOR:
cefb8b21 Pawel Laszczak 2018-12-23  225  	case USB_REQ_SET_DESCRIPTOR:
cefb8b21 Pawel Laszczak 2018-12-23  226  		usb_decode_get_set_descriptor(bRequestType, bRequest, wValue,
cefb8b21 Pawel Laszczak 2018-12-23  227  					      wIndex, wLength, str);
cefb8b21 Pawel Laszczak 2018-12-23  228  		break;
cefb8b21 Pawel Laszczak 2018-12-23  229  	case USB_REQ_GET_CONFIGURATION:
cefb8b21 Pawel Laszczak 2018-12-23  230  		usb_decode_get_configuration(wLength, str);
cefb8b21 Pawel Laszczak 2018-12-23  231  		break;
cefb8b21 Pawel Laszczak 2018-12-23  232  	case USB_REQ_SET_CONFIGURATION:
cefb8b21 Pawel Laszczak 2018-12-23  233  		usb_decode_set_configuration(wValue, str);
cefb8b21 Pawel Laszczak 2018-12-23  234  		break;
cefb8b21 Pawel Laszczak 2018-12-23  235  	case USB_REQ_GET_INTERFACE:
cefb8b21 Pawel Laszczak 2018-12-23  236  		usb_decode_get_intf(wIndex, wLength, str);
cefb8b21 Pawel Laszczak 2018-12-23  237  		break;
cefb8b21 Pawel Laszczak 2018-12-23  238  	case USB_REQ_SET_INTERFACE:
cefb8b21 Pawel Laszczak 2018-12-23  239  		usb_decode_set_intf(wValue, wIndex, str);
cefb8b21 Pawel Laszczak 2018-12-23  240  		break;
cefb8b21 Pawel Laszczak 2018-12-23  241  	case USB_REQ_SYNCH_FRAME:
cefb8b21 Pawel Laszczak 2018-12-23  242  		usb_decode_synch_frame(wIndex, wLength, str);
cefb8b21 Pawel Laszczak 2018-12-23  243  		break;
cefb8b21 Pawel Laszczak 2018-12-23  244  	case USB_REQ_SET_SEL:
cefb8b21 Pawel Laszczak 2018-12-23  245  		usb_decode_set_sel(wLength, str);
cefb8b21 Pawel Laszczak 2018-12-23  246  		break;
cefb8b21 Pawel Laszczak 2018-12-23  247  	case USB_REQ_SET_ISOCH_DELAY:
cefb8b21 Pawel Laszczak 2018-12-23  248  		usb_decode_set_isoch_delay(wValue, str);
cefb8b21 Pawel Laszczak 2018-12-23  249  		break;
cefb8b21 Pawel Laszczak 2018-12-23  250  	default:
cefb8b21 Pawel Laszczak 2018-12-23  251  		sprintf(str, "%02x %02x %02x %02x %02x %02x %02x %02x",
cefb8b21 Pawel Laszczak 2018-12-23  252  			bRequestType, bRequest,
cefb8b21 Pawel Laszczak 2018-12-23 @253  			cpu_to_le16(wValue) & 0xff,
cefb8b21 Pawel Laszczak 2018-12-23  254  			cpu_to_le16(wValue) >> 8,
cefb8b21 Pawel Laszczak 2018-12-23  255  			cpu_to_le16(wIndex) & 0xff,
cefb8b21 Pawel Laszczak 2018-12-23  256  			cpu_to_le16(wIndex) >> 8,
cefb8b21 Pawel Laszczak 2018-12-23  257  			cpu_to_le16(wLength) & 0xff,
cefb8b21 Pawel Laszczak 2018-12-23  258  			cpu_to_le16(wLength) >> 8);
cefb8b21 Pawel Laszczak 2018-12-23  259  	}
cefb8b21 Pawel Laszczak 2018-12-23  260  
cefb8b21 Pawel Laszczak 2018-12-23  261  	return str;
cefb8b21 Pawel Laszczak 2018-12-23  262  }
cefb8b21 Pawel Laszczak 2018-12-23  263  EXPORT_SYMBOL_GPL(usb_decode_ctrl);
cefb8b21 Pawel Laszczak 2018-12-23  264  

:::::: The code at line 253 was first introduced by commit
:::::: cefb8b2176410ef0aacc22411a64f8a157612f14 usb:common Separated decoding functions from dwc3 driver.

:::::: TO: Pawel Laszczak <pawell@cadence.com>
:::::: CC: 0day robot <lkp@intel.com>

---
0-DAY kernel test infrastructure                Open Source Technology Center
https://lists.01.org/pipermail/kbuild-all                   Intel Corporation

[-- Attachment #2: .config.gz --]
[-- Type: application/gzip, Size: 66693 bytes --]

^ permalink raw reply	[flat|nested] 49+ messages in thread

* Re: [PATCH v2 5/5] usb:cdns3 Add Cadence USB3 DRD Driver
@ 2018-12-23 19:20     ` kbuild test robot
  0 siblings, 0 replies; 49+ messages in thread
From: kbuild test robot @ 2018-12-23 19:20 UTC (permalink / raw)
  Cc: kbuild-all, devicetree, gregkh, mark.rutland, linux-usb,
	hdegoede, heikki.krogerus, andy.shevchenko, robh+dt, rogerq,
	linux-kernel, adouglas, jbergsagel, nsekhar, nm, sureshp,
	peter.chen, pjez, kurahul, Pawel Laszczak

[-- Attachment #1: Type: text/plain, Size: 5737 bytes --]

Hi Pawel,

Thank you for the patch! Perhaps something to improve:

[auto build test WARNING on usb/usb-testing]
[also build test WARNING on v4.20-rc7 next-20181221]
[if your patch is applied to the wrong git tree, please drop us a note to help improve the system]

url:    https://github.com/0day-ci/linux/commits/Pawel-Laszczak/Introduced-new-Cadence-USBSS-DRD-Driver/20181223-231813
base:   https://git.kernel.org/pub/scm/linux/kernel/git/gregkh/usb.git usb-testing
config: x86_64-allmodconfig (attached as .config)
compiler: gcc-7 (Debian 7.3.0-1) 7.3.0
reproduce:
        # save the attached .config to linux build tree
        make ARCH=x86_64 

All warnings (new ones prefixed by >>):

>> drivers/usb/common/debug.c:253:25: warning: restricted __le16 degrades to integer
   drivers/usb/common/debug.c:254:25: warning: restricted __le16 degrades to integer
   drivers/usb/common/debug.c:255:25: warning: restricted __le16 degrades to integer
   drivers/usb/common/debug.c:256:25: warning: restricted __le16 degrades to integer
   drivers/usb/common/debug.c:257:25: warning: restricted __le16 degrades to integer
   drivers/usb/common/debug.c:258:25: warning: restricted __le16 degrades to integer

vim +253 drivers/usb/common/debug.c

cefb8b21 Pawel Laszczak 2018-12-23  205  
cefb8b21 Pawel Laszczak 2018-12-23  206  /**
cefb8b21 Pawel Laszczak 2018-12-23  207   * usb_decode_ctrl - returns a string representation of ctrl request
cefb8b21 Pawel Laszczak 2018-12-23  208   */
cefb8b21 Pawel Laszczak 2018-12-23  209  const char *usb_decode_ctrl(char *str, __u8 bRequestType, __u8 bRequest,
cefb8b21 Pawel Laszczak 2018-12-23  210  			    __u16 wValue,  __u16 wIndex, __u16 wLength)
cefb8b21 Pawel Laszczak 2018-12-23  211  {
cefb8b21 Pawel Laszczak 2018-12-23  212  	switch (bRequest) {
cefb8b21 Pawel Laszczak 2018-12-23  213  	case USB_REQ_GET_STATUS:
cefb8b21 Pawel Laszczak 2018-12-23  214  		usb_decode_get_status(bRequestType, wIndex, wLength, str);
cefb8b21 Pawel Laszczak 2018-12-23  215  		break;
cefb8b21 Pawel Laszczak 2018-12-23  216  	case USB_REQ_CLEAR_FEATURE:
cefb8b21 Pawel Laszczak 2018-12-23  217  	case USB_REQ_SET_FEATURE:
cefb8b21 Pawel Laszczak 2018-12-23  218  		usb_decode_set_clear_feature(bRequestType, bRequest, wValue,
cefb8b21 Pawel Laszczak 2018-12-23  219  					     wIndex, str);
cefb8b21 Pawel Laszczak 2018-12-23  220  		break;
cefb8b21 Pawel Laszczak 2018-12-23  221  	case USB_REQ_SET_ADDRESS:
cefb8b21 Pawel Laszczak 2018-12-23  222  		usb_decode_set_address(wValue, str);
cefb8b21 Pawel Laszczak 2018-12-23  223  		break;
cefb8b21 Pawel Laszczak 2018-12-23  224  	case USB_REQ_GET_DESCRIPTOR:
cefb8b21 Pawel Laszczak 2018-12-23  225  	case USB_REQ_SET_DESCRIPTOR:
cefb8b21 Pawel Laszczak 2018-12-23  226  		usb_decode_get_set_descriptor(bRequestType, bRequest, wValue,
cefb8b21 Pawel Laszczak 2018-12-23  227  					      wIndex, wLength, str);
cefb8b21 Pawel Laszczak 2018-12-23  228  		break;
cefb8b21 Pawel Laszczak 2018-12-23  229  	case USB_REQ_GET_CONFIGURATION:
cefb8b21 Pawel Laszczak 2018-12-23  230  		usb_decode_get_configuration(wLength, str);
cefb8b21 Pawel Laszczak 2018-12-23  231  		break;
cefb8b21 Pawel Laszczak 2018-12-23  232  	case USB_REQ_SET_CONFIGURATION:
cefb8b21 Pawel Laszczak 2018-12-23  233  		usb_decode_set_configuration(wValue, str);
cefb8b21 Pawel Laszczak 2018-12-23  234  		break;
cefb8b21 Pawel Laszczak 2018-12-23  235  	case USB_REQ_GET_INTERFACE:
cefb8b21 Pawel Laszczak 2018-12-23  236  		usb_decode_get_intf(wIndex, wLength, str);
cefb8b21 Pawel Laszczak 2018-12-23  237  		break;
cefb8b21 Pawel Laszczak 2018-12-23  238  	case USB_REQ_SET_INTERFACE:
cefb8b21 Pawel Laszczak 2018-12-23  239  		usb_decode_set_intf(wValue, wIndex, str);
cefb8b21 Pawel Laszczak 2018-12-23  240  		break;
cefb8b21 Pawel Laszczak 2018-12-23  241  	case USB_REQ_SYNCH_FRAME:
cefb8b21 Pawel Laszczak 2018-12-23  242  		usb_decode_synch_frame(wIndex, wLength, str);
cefb8b21 Pawel Laszczak 2018-12-23  243  		break;
cefb8b21 Pawel Laszczak 2018-12-23  244  	case USB_REQ_SET_SEL:
cefb8b21 Pawel Laszczak 2018-12-23  245  		usb_decode_set_sel(wLength, str);
cefb8b21 Pawel Laszczak 2018-12-23  246  		break;
cefb8b21 Pawel Laszczak 2018-12-23  247  	case USB_REQ_SET_ISOCH_DELAY:
cefb8b21 Pawel Laszczak 2018-12-23  248  		usb_decode_set_isoch_delay(wValue, str);
cefb8b21 Pawel Laszczak 2018-12-23  249  		break;
cefb8b21 Pawel Laszczak 2018-12-23  250  	default:
cefb8b21 Pawel Laszczak 2018-12-23  251  		sprintf(str, "%02x %02x %02x %02x %02x %02x %02x %02x",
cefb8b21 Pawel Laszczak 2018-12-23  252  			bRequestType, bRequest,
cefb8b21 Pawel Laszczak 2018-12-23 @253  			cpu_to_le16(wValue) & 0xff,
cefb8b21 Pawel Laszczak 2018-12-23  254  			cpu_to_le16(wValue) >> 8,
cefb8b21 Pawel Laszczak 2018-12-23  255  			cpu_to_le16(wIndex) & 0xff,
cefb8b21 Pawel Laszczak 2018-12-23  256  			cpu_to_le16(wIndex) >> 8,
cefb8b21 Pawel Laszczak 2018-12-23  257  			cpu_to_le16(wLength) & 0xff,
cefb8b21 Pawel Laszczak 2018-12-23  258  			cpu_to_le16(wLength) >> 8);
cefb8b21 Pawel Laszczak 2018-12-23  259  	}
cefb8b21 Pawel Laszczak 2018-12-23  260  
cefb8b21 Pawel Laszczak 2018-12-23  261  	return str;
cefb8b21 Pawel Laszczak 2018-12-23  262  }
cefb8b21 Pawel Laszczak 2018-12-23  263  EXPORT_SYMBOL_GPL(usb_decode_ctrl);
cefb8b21 Pawel Laszczak 2018-12-23  264  

:::::: The code at line 253 was first introduced by commit
:::::: cefb8b2176410ef0aacc22411a64f8a157612f14 usb:common Separated decoding functions from dwc3 driver.

:::::: TO: Pawel Laszczak <pawell@cadence.com>
:::::: CC: 0day robot <lkp@intel.com>

---
0-DAY kernel test infrastructure                Open Source Technology Center
https://lists.01.org/pipermail/kbuild-all                   Intel Corporation

[-- Attachment #2: .config.gz --]
[-- Type: application/gzip, Size: 66693 bytes --]

^ permalink raw reply	[flat|nested] 49+ messages in thread

* [v2,5/5] usb:cdns3 Add Cadence USB3 DRD Driver
@ 2018-12-23 19:20     ` kbuild test robot
  0 siblings, 0 replies; 49+ messages in thread
From: kbuild test robot @ 2018-12-23 19:20 UTC (permalink / raw)
  To: Pawel Laszczak
  Cc: kbuild-all, devicetree, gregkh, mark.rutland, linux-usb,
	hdegoede, heikki.krogerus, andy.shevchenko, robh+dt, rogerq,
	linux-kernel, adouglas, jbergsagel, nsekhar, nm, sureshp,
	peter.chen, pjez, kurahul

Hi Pawel,

Thank you for the patch! Perhaps something to improve:

[auto build test WARNING on usb/usb-testing]
[also build test WARNING on v4.20-rc7 next-20181221]
[if your patch is applied to the wrong git tree, please drop us a note to help improve the system]

url:    https://github.com/0day-ci/linux/commits/Pawel-Laszczak/Introduced-new-Cadence-USBSS-DRD-Driver/20181223-231813
base:   https://git.kernel.org/pub/scm/linux/kernel/git/gregkh/usb.git usb-testing
config: x86_64-allmodconfig (attached as .config)
compiler: gcc-7 (Debian 7.3.0-1) 7.3.0
reproduce:
        # save the attached .config to linux build tree
        make ARCH=x86_64 

All warnings (new ones prefixed by >>):

>> drivers/usb/common/debug.c:253:25: warning: restricted __le16 degrades to integer
   drivers/usb/common/debug.c:254:25: warning: restricted __le16 degrades to integer
   drivers/usb/common/debug.c:255:25: warning: restricted __le16 degrades to integer
   drivers/usb/common/debug.c:256:25: warning: restricted __le16 degrades to integer
   drivers/usb/common/debug.c:257:25: warning: restricted __le16 degrades to integer
   drivers/usb/common/debug.c:258:25: warning: restricted __le16 degrades to integer

vim +253 drivers/usb/common/debug.c

cefb8b21 Pawel Laszczak 2018-12-23  205  
cefb8b21 Pawel Laszczak 2018-12-23  206  /**
cefb8b21 Pawel Laszczak 2018-12-23  207   * usb_decode_ctrl - returns a string representation of ctrl request
cefb8b21 Pawel Laszczak 2018-12-23  208   */
cefb8b21 Pawel Laszczak 2018-12-23  209  const char *usb_decode_ctrl(char *str, __u8 bRequestType, __u8 bRequest,
cefb8b21 Pawel Laszczak 2018-12-23  210  			    __u16 wValue,  __u16 wIndex, __u16 wLength)
cefb8b21 Pawel Laszczak 2018-12-23  211  {
cefb8b21 Pawel Laszczak 2018-12-23  212  	switch (bRequest) {
cefb8b21 Pawel Laszczak 2018-12-23  213  	case USB_REQ_GET_STATUS:
cefb8b21 Pawel Laszczak 2018-12-23  214  		usb_decode_get_status(bRequestType, wIndex, wLength, str);
cefb8b21 Pawel Laszczak 2018-12-23  215  		break;
cefb8b21 Pawel Laszczak 2018-12-23  216  	case USB_REQ_CLEAR_FEATURE:
cefb8b21 Pawel Laszczak 2018-12-23  217  	case USB_REQ_SET_FEATURE:
cefb8b21 Pawel Laszczak 2018-12-23  218  		usb_decode_set_clear_feature(bRequestType, bRequest, wValue,
cefb8b21 Pawel Laszczak 2018-12-23  219  					     wIndex, str);
cefb8b21 Pawel Laszczak 2018-12-23  220  		break;
cefb8b21 Pawel Laszczak 2018-12-23  221  	case USB_REQ_SET_ADDRESS:
cefb8b21 Pawel Laszczak 2018-12-23  222  		usb_decode_set_address(wValue, str);
cefb8b21 Pawel Laszczak 2018-12-23  223  		break;
cefb8b21 Pawel Laszczak 2018-12-23  224  	case USB_REQ_GET_DESCRIPTOR:
cefb8b21 Pawel Laszczak 2018-12-23  225  	case USB_REQ_SET_DESCRIPTOR:
cefb8b21 Pawel Laszczak 2018-12-23  226  		usb_decode_get_set_descriptor(bRequestType, bRequest, wValue,
cefb8b21 Pawel Laszczak 2018-12-23  227  					      wIndex, wLength, str);
cefb8b21 Pawel Laszczak 2018-12-23  228  		break;
cefb8b21 Pawel Laszczak 2018-12-23  229  	case USB_REQ_GET_CONFIGURATION:
cefb8b21 Pawel Laszczak 2018-12-23  230  		usb_decode_get_configuration(wLength, str);
cefb8b21 Pawel Laszczak 2018-12-23  231  		break;
cefb8b21 Pawel Laszczak 2018-12-23  232  	case USB_REQ_SET_CONFIGURATION:
cefb8b21 Pawel Laszczak 2018-12-23  233  		usb_decode_set_configuration(wValue, str);
cefb8b21 Pawel Laszczak 2018-12-23  234  		break;
cefb8b21 Pawel Laszczak 2018-12-23  235  	case USB_REQ_GET_INTERFACE:
cefb8b21 Pawel Laszczak 2018-12-23  236  		usb_decode_get_intf(wIndex, wLength, str);
cefb8b21 Pawel Laszczak 2018-12-23  237  		break;
cefb8b21 Pawel Laszczak 2018-12-23  238  	case USB_REQ_SET_INTERFACE:
cefb8b21 Pawel Laszczak 2018-12-23  239  		usb_decode_set_intf(wValue, wIndex, str);
cefb8b21 Pawel Laszczak 2018-12-23  240  		break;
cefb8b21 Pawel Laszczak 2018-12-23  241  	case USB_REQ_SYNCH_FRAME:
cefb8b21 Pawel Laszczak 2018-12-23  242  		usb_decode_synch_frame(wIndex, wLength, str);
cefb8b21 Pawel Laszczak 2018-12-23  243  		break;
cefb8b21 Pawel Laszczak 2018-12-23  244  	case USB_REQ_SET_SEL:
cefb8b21 Pawel Laszczak 2018-12-23  245  		usb_decode_set_sel(wLength, str);
cefb8b21 Pawel Laszczak 2018-12-23  246  		break;
cefb8b21 Pawel Laszczak 2018-12-23  247  	case USB_REQ_SET_ISOCH_DELAY:
cefb8b21 Pawel Laszczak 2018-12-23  248  		usb_decode_set_isoch_delay(wValue, str);
cefb8b21 Pawel Laszczak 2018-12-23  249  		break;
cefb8b21 Pawel Laszczak 2018-12-23  250  	default:
cefb8b21 Pawel Laszczak 2018-12-23  251  		sprintf(str, "%02x %02x %02x %02x %02x %02x %02x %02x",
cefb8b21 Pawel Laszczak 2018-12-23  252  			bRequestType, bRequest,
cefb8b21 Pawel Laszczak 2018-12-23 @253  			cpu_to_le16(wValue) & 0xff,
cefb8b21 Pawel Laszczak 2018-12-23  254  			cpu_to_le16(wValue) >> 8,
cefb8b21 Pawel Laszczak 2018-12-23  255  			cpu_to_le16(wIndex) & 0xff,
cefb8b21 Pawel Laszczak 2018-12-23  256  			cpu_to_le16(wIndex) >> 8,
cefb8b21 Pawel Laszczak 2018-12-23  257  			cpu_to_le16(wLength) & 0xff,
cefb8b21 Pawel Laszczak 2018-12-23  258  			cpu_to_le16(wLength) >> 8);
cefb8b21 Pawel Laszczak 2018-12-23  259  	}
cefb8b21 Pawel Laszczak 2018-12-23  260  
cefb8b21 Pawel Laszczak 2018-12-23  261  	return str;
cefb8b21 Pawel Laszczak 2018-12-23  262  }
cefb8b21 Pawel Laszczak 2018-12-23  263  EXPORT_SYMBOL_GPL(usb_decode_ctrl);
cefb8b21 Pawel Laszczak 2018-12-23  264  

:::::: The code at line 253 was first introduced by commit
:::::: cefb8b2176410ef0aacc22411a64f8a157612f14 usb:common Separated decoding functions from dwc3 driver.

:::::: TO: Pawel Laszczak <pawell@cadence.com>
:::::: CC: 0day robot <lkp@intel.com>
---
0-DAY kernel test infrastructure                Open Source Technology Center
https://lists.01.org/pipermail/kbuild-all                   Intel Corporation

^ permalink raw reply	[flat|nested] 49+ messages in thread

* Re: [PATCH v2 5/5] usb:cdns3 Add Cadence USB3 DRD Driver
@ 2018-12-24  9:38     ` Peter Chen
  0 siblings, 0 replies; 49+ messages in thread
From: Peter Chen @ 2018-12-24  9:38 UTC (permalink / raw)
  To: pawell
  Cc: devicetree, Greg Kroah-Hartman, mark.rutland, linux-usb,
	hdegoede, Heikki Krogerus, andy.shevchenko, robh+dt, rogerq,
	lkml, adouglas, jbergsagel, nsekhar, nm, sureshp, peter.chen,
	pjez, kurahul

>
> The host side of USBSS-DRD controller is compliance
> with XHCI specification, so it works with
> standard XHCI linux driver.
>

After adding my glue layer change (with my phy part) and make one
change for this code,
the xHCI can work at my platform. I list the comments from today's
review and debug.
The comments are at  Makefile and drd.c.

> +         If you choose to build this driver as module it will
> +         be dynamically linked and module will be called cdns3-pci.ko
> +
> +endif
> diff --git a/drivers/usb/cdns3/Makefile b/drivers/usb/cdns3/Makefile
> new file mode 100644
> index 000000000000..3f63baa24294
> --- /dev/null
> +++ b/drivers/usb/cdns3/Makefile
> @@ -0,0 +1,16 @@
> +# SPDX-License-Identifier: GPL-2.0
> +# define_trace.h needs to know how to find our header
> +CFLAGS_trace.o                         := -I$(src)
> +
> +obj-$(CONFIG_USB_CDNS3)                        += cdns3.o
> +obj-$(CONFIG_USB_CDNS3_PCI_WRAP)       += cdns3-pci.o
> +
> +cdns3-y                                        := core.o drd.o trace.o
> +
> +ifneq ($(CONFIG_DEBUG_FS),)
> +       cdns3-y                         += debugfs.o
> +endif
> +
> +cdns3-$(CONFIG_USB_CDNS3_GADGET)       += gadget.o ep0.o
> +cdns3-$(CONFIG_USB_CDNS3_HOST)         += host.o
> +cdns3-pci-y                            := cdns3-pci-wrap.o

Why do you want add two lines for pci_wrap? I change this Makefile like below:

# SPDX-License-Identifier: GPL-2.0
# define_trace.h needs to know how to find our header
CFLAGS_trace.o                          := -I$(src)

cdns3-y                                 := core.o drd.o trace.o
obj-$(CONFIG_USB_CDNS3)                 += cdns3.o
ifneq ($(CONFIG_DEBUG_FS),)
        cdns3-y                         += debugfs.o
endif

cdns3-$(CONFIG_USB_CDNS3_GADGET)        += gadget.o ep0.o
cdns3-$(CONFIG_USB_CDNS3_HOST)          += host.o
obj-$(CONFIG_USB_CDNS3_PCI_WRAP)        += cdns3-pci-wrap.o
obj-$(CONFIG_USB_CDNS3_IMX_WRAP)        += cdns3-imx.o

and below is the diff:

diff --git a/drivers/usb/cdns3/Makefile b/drivers/usb/cdns3/Makefile
index 3f63baa24294..d1bca2829f57 100644
--- a/drivers/usb/cdns3/Makefile
+++ b/drivers/usb/cdns3/Makefile
@@ -2,15 +2,13 @@
 # define_trace.h needs to know how to find our header
 CFLAGS_trace.o                         := -I$(src)

-obj-$(CONFIG_USB_CDNS3)                        += cdns3.o
-obj-$(CONFIG_USB_CDNS3_PCI_WRAP)       += cdns3-pci.o
-
 cdns3-y                                        := core.o drd.o trace.o
-
+obj-$(CONFIG_USB_CDNS3)                        += cdns3.o
 ifneq ($(CONFIG_DEBUG_FS),)
        cdns3-y                         += debugfs.o
 endif

 cdns3-$(CONFIG_USB_CDNS3_GADGET)       += gadget.o ep0.o
 cdns3-$(CONFIG_USB_CDNS3_HOST)         += host.o
-cdns3-pci-y                            := cdns3-pci-wrap.o
+obj-$(CONFIG_USB_CDNS3_PCI_WRAP)       += cdns3-pci-wrap.o
+obj-$(CONFIG_USB_CDNS3_IMX_WRAP)       += cdns3-imx.o


> diff --git a/drivers/usb/cdns3/drd.c b/drivers/usb/cdns3/drd.c
> new file mode 100644
> index 000000000000..b0c32302eb0b
> --- /dev/null
> +++ b/drivers/usb/cdns3/drd.c
> @@ -0,0 +1,350 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Cadence USBSS DRD Driver.
> + *
> + * Copyright (C) 2018 Cadence.
> + *
> + * Author: Pawel Laszczak <pawell@cadence.com
> + *
> + */
> +#include <linux/kernel.h>
> +#include <linux/interrupt.h>
> +#include <linux/delay.h>
> +#include <linux/usb/otg.h>
> +
> +#include "gadget.h"
> +#include "drd.h"
> +#include "core.h"
> +
> +static int cdns3_drd_switch_gadget(struct cdns3 *cdns, int on);
> +static int cdns3_drd_switch_host(struct cdns3 *cdns, int on);
> +
> +/**
> + * cdns3_set_mode - change mode of OTG Core
> + * @cdns: pointer to context structure
> + * @mode: selected mode from cdns_role
> + */
> +void cdns3_set_mode(struct cdns3 *cdns, enum usb_dr_mode mode)
> +{
> +       u32 reg;
> +
> +       cdns->current_dr_mode = mode;
> +
> +       switch (mode) {
> +       case USB_DR_MODE_PERIPHERAL:
> +               dev_info(cdns->dev, "Set controller to Gadget mode\n");
> +               cdns3_drd_switch_gadget(cdns, 1);
> +               break;
> +       case USB_DR_MODE_HOST:
> +               dev_info(cdns->dev, "Set controller to Host mode\n");
> +               cdns3_drd_switch_host(cdns, 1);
> +               break;
> +       case USB_DR_MODE_OTG:
> +               dev_info(cdns->dev, "Set controller to OTG mode\n");
> +               if (cdns->version == CDNS3_CONTROLLER_V1) {
> +                       reg = readl(&cdns->otg_v1_regs->override);
> +                       reg |= OVERRIDE_IDPULLUP;
> +                       writel(reg, &cdns->otg_v1_regs->override);
> +               } else {
> +                       reg = readl(&cdns->otg_v0_regs->ctrl1);
> +                       reg |= OVERRIDE_IDPULLUP_V0;
> +                       writel(reg, &cdns->otg_v0_regs->ctrl1);
> +               }
> +
> +               /*
> +                * Hardware specification says: "ID_VALUE must be valid within
> +                * 50ms after idpullup is set to '1" so driver must wait
> +                * 50ms before reading this pin.
> +                */
> +               usleep_range(50000, 60000);
> +               break;
> +       default:
> +               cdns->current_dr_mode = USB_DR_MODE_UNKNOWN;
> +               dev_err(cdns->dev, "Unsupported mode of operation %d\n", mode);
> +               return;
> +       }
> +}

I suggest adding return value since switch may fail.


> +/**
> + * cdns3_init_otg_mode - initialize drd controller
> + * @cdns: Pointer to controller context structure
> + *
> + * Returns 0 on success otherwise negative errno
> + */
> +static void cdns3_init_otg_mode(struct cdns3 *cdns)

You may add return value for this function too like your comment.

> +{
> +       cdns3_otg_disable_irq(cdns);
> +       /* clear all interrupts */
> +       writel(~0, &cdns->otg_regs->ivect);
> +
> +       cdns3_set_mode(cdns, USB_DR_MODE_OTG);

return value
> +
> +       if (cdns3_is_host(cdns))
> +               cdns3_drd_switch_host(cdns, 1);

ditto

> +       else
> +               cdns3_drd_switch_gadget(cdns, 1);

ditto
> +
> +       cdns3_otg_enable_irq(cdns);
> +}
> +
> +/**
> + * cdns3_drd_update_mode - initialize mode of operation
> + * @cdns: Pointer to controller context structure
> + *
> + * Returns 0 on success otherwise negative errno
> + */
> +int cdns3_drd_update_mode(struct cdns3 *cdns)
> +{
> +       int ret = 0;
> +
> +       if (cdns->desired_dr_mode == cdns->current_dr_mode)
> +               return ret;
> +
> +       cdns3_drd_switch_gadget(cdns, 0);

return value

> +       cdns3_drd_switch_host(cdns, 0);

ditto
> +
> +       switch (cdns->desired_dr_mode) {
> +       case USB_DR_MODE_PERIPHERAL:
> +               cdns3_set_mode(cdns, USB_DR_MODE_PERIPHERAL);

ditto

> +               break;
> +       case USB_DR_MODE_HOST:
> +               cdns3_set_mode(cdns, USB_DR_MODE_HOST);

ditto
> +               break;
> +       case USB_DR_MODE_OTG:
> +               cdns3_init_otg_mode(cdns);

ditto
> +               break;
> +       default:
> +               dev_err(cdns->dev, "Unsupported mode of operation %d\n",
> +                       cdns->dr_mode);
> +               return -EINVAL;
> +       }
> +
> +       return ret;
> +}
> +
> +/**
> + * cdns3_drd_irq - interrupt handler for OTG events
> + *
> + * @irq: irq number for cdns3 core device
> + * @data: structure of cdns3
> + *
> + * Returns IRQ_HANDLED or IRQ_NONE
> + */
> +static irqreturn_t cdns3_drd_irq(int irq, void *data)
> +{
> +       irqreturn_t ret = IRQ_NONE;
> +       struct cdns3 *cdns = data;
> +       u32 reg;
> +
> +       if (cdns->dr_mode != USB_DR_MODE_OTG)
> +               return ret;
> +
> +       reg = readl(&cdns->otg_regs->ivect);
> +
> +       if (!reg)
> +               return ret;
> +
> +       if (reg & OTGIEN_ID_CHANGE_INT) {
> +               dev_dbg(cdns->dev, "OTG IRQ: new ID: %d\n",
> +                       cdns3_get_id(cdns));
> +
> +               queue_work(system_freezable_wq, &cdns->role_switch_wq);
> +
> +               ret = IRQ_HANDLED;
> +       }
> +
> +       writel(~0, &cdns->otg_regs->ivect);
> +       return ret;
> +}
> +
> +int cdns3_drd_init(struct cdns3 *cdns)
> +{
> +       void __iomem *regs;
> +       int ret = 0;
> +       u32 state;
> +
> +       regs = devm_ioremap_resource(cdns->dev, &cdns->otg_res);
> +       if (IS_ERR(regs))
> +               return PTR_ERR(regs);
> +
> +       /* Detection of DRD version. Controller has been released
> +        * in two versions. Both are similar, but they have same changes
> +        * in register maps.
> +        * The first register in old version is command register and it's read
> +        * only, so driver should read 0 from it. On the other hand, in v1
> +        * the first register contains device ID number which is not set to 0.
> +        * Driver uses this fact to detect the proper version of
> +        * controller.
> +        */
> +       cdns->otg_v0_regs = regs;
> +       if (!readl(&cdns->otg_v0_regs->cmd)) {
> +               cdns->version  = CDNS3_CONTROLLER_V0;
> +               cdns->otg_v1_regs = NULL;
> +               cdns->otg_regs = regs;
> +               dev_info(cdns->dev, "DRD version v0 (%08x)\n",
> +                        readl(&cdns->otg_v0_regs->version));
> +       } else {
> +               cdns->otg_v0_regs = NULL;
> +               cdns->otg_v1_regs = regs;
> +               cdns->otg_regs = (void *)&cdns->otg_v1_regs->cmd;
> +               cdns->version  = CDNS3_CONTROLLER_V1;
> +               dev_info(cdns->dev, "DRD version v1 (ID: %08x, rev: %08x)\n",
> +                        readl(&cdns->otg_v1_regs->did),
> +                        readl(&cdns->otg_v1_regs->rid));
> +       }
> +
> +       state = OTGSTS_STRAP(readl(&cdns->otg_regs->sts));
> +
> +       /* Update dr_mode according to STRAP configuration. */
> +       cdns->dr_mode = USB_DR_MODE_OTG;
> +       if (state == OTGSTS_STRAP_HOST) {
> +               dev_info(cdns->dev, "Controller strapped to HOST\n");
> +               cdns->dr_mode = USB_DR_MODE_HOST;
> +       } else if (state == OTGSTS_STRAP_GADGET) {
> +               dev_info(cdns->dev, "Controller strapped to PERIPHERAL\n");
> +               cdns->dr_mode = USB_DR_MODE_PERIPHERAL;
> +       }
> +
> +       cdns->desired_dr_mode = cdns->dr_mode;
> +       cdns->current_dr_mode = USB_DR_MODE_UNKNOWN;
> +
> +       ret = devm_request_threaded_irq(cdns->dev, cdns->irq, cdns3_drd_irq,
> +                                       NULL, IRQF_SHARED,
> +                                       dev_name(cdns->dev), cdns);
> +
> +       if (ret)
> +               return ret;
> +
> +       state = readl(&cdns->otg_regs->sts);
> +       if (OTGSTS_OTG_NRDY(state) != 0) {
> +               dev_err(cdns->dev, "Cadence USB3 OTG device not ready\n");
> +               return -ENODEV;
> +       }
> +
> +       ret = cdns3_drd_update_mode(cdns);
> +

Calling this function, it is timeout for waiting OTGSTS_XHCI_READY at otgsts,
do you know possible reasons?  After commenting out this function, my
xHCI function
works.

Peter

> +       return ret;
> +}
> +
> +int cdns3_drd_exit(struct cdns3 *cdns)
> +{
> +       return cdns3_drd_switch_host(cdns, 0);
> +}
> diff --git a/drivers/usb/cdns3/drd.h b/drivers/usb/cdns3/drd.h
> new file mode 100644
> index 000000000000..6a29cdcb492d
> --- /dev/null
> +++ b/drivers/usb/cdns3/drd.h
> @@ -0,0 +1,162 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +/*
> + * Cadence USB3 DRD header file.
> + *
> + * Copyright (C) 2018 Cadence.
> + *
> + * Author: Pawel Laszczak <pawell@cadence.com>
> + */
> +#ifndef __LINUX_CDNS3_DRD
> +#define __LINUX_CDNS3_DRD
> +
> +#include <linux/usb/otg.h>
> +#include <linux/phy/phy.h>
> +#include "core.h"
> +
> +/*  DRD register interface for version v1. */
> +struct cdns3_otg_regs {
> +       __le32 did;
> +       __le32 rid;
> +       __le32 capabilities;
> +       __le32 reserved1;
> +       __le32 cmd;
> +       __le32 sts;
> +       __le32 state;
> +       __le32 reserved2;
> +       __le32 ien;
> +       __le32 ivect;
> +       __le32 refclk;
> +       __le32 tmr;
> +       __le32 reserved3[4];
> +       __le32 simulate;
> +       __le32 override;
> +       __le32 susp_ctrl;
> +       __le32 reserved4;
> +       __le32 anasts;
> +       __le32 adp_ramp_time;
> +       __le32 ctrl1;
> +       __le32 ctrl2;
> +};
> +
> +/*  DRD register interface for version v0. */
> +struct cdns3_otg_legacy_regs {
> +       __le32 cmd;
> +       __le32 sts;
> +       __le32 state;
> +       __le32 refclk;
> +       __le32 ien;
> +       __le32 ivect;
> +       __le32 reserved1[3];
> +       __le32 tmr;
> +       __le32 reserved2[2];
> +       __le32 version;
> +       __le32 capabilities;
> +       __le32 reserved3[2];
> +       __le32 simulate;
> +       __le32 reserved4[5];
> +       __le32 ctrl1;
> +};
> +
> +/*
> + * Common registers interface for both version of DRD.
> + */
> +struct cdns3_otg_common_regs {
> +       __le32 cmd;
> +       __le32 sts;
> +       __le32 state;
> +       __le32 different1;
> +       __le32 ien;
> +       __le32 ivect;
> +};
> +
> +/* CDNS_RID - bitmasks */
> +#define CDNS_RID(p)                    ((p) & GENMASK(15, 0))
> +
> +/* CDNS_VID - bitmasks */
> +#define CDNS_DID(p)                    ((p) & GENMASK(31, 0))
> +
> +/* OTGCMD - bitmasks */
> +/* "Request the bus for Device mode. */
> +#define OTGCMD_DEV_BUS_REQ             BIT(0)
> +/* Request the bus for Host mode */
> +#define OTGCMD_HOST_BUS_REQ            BIT(1)
> +/* Enable OTG mode. */
> +#define OTGCMD_OTG_EN                  BIT(2)
> +/* Disable OTG mode */
> +#define OTGCMD_OTG_DIS                 BIT(3)
> +/*"Configure OTG as A-Device. */
> +#define OTGCMD_A_DEV_EN                        BIT(4)
> +/*"Configure OTG as A-Device. */
> +#define OTGCMD_A_DEV_DIS               BIT(5)
> +/* Drop the bus for Device mod e. */
> +#define OTGCMD_DEV_BUS_DROP            BIT(8)
> +/* Drop the bus for Host mode*/
> +#define OTGCMD_HOST_BUS_DROP           BIT(9)
> +/* Power Down USBSS-DEV. */
> +#define OTGCMD_DEV_POWER_OFF           BIT(11)
> +/* Power Down CDNSXHCI. */
> +#define OTGCMD_HOST_POWER_OFF          BIT(12)
> +
> +/* OTGIEN - bitmasks */
> +/* ID change interrupt enable */
> +#define OTGIEN_ID_CHANGE_INT           BIT(0)
> +/* Vbusvalid fall detected interrupt enable.*/
> +#define OTGIEN_VBUSVALID_RISE_INT      BIT(4)
> +/* Vbusvalid fall detected interrupt enable */
> +#define OTGIEN_VBUSVALID_FALL_INT      BIT(5)
> +
> +/* OTGSTS - bitmasks */
> +/*
> + * Current value of the ID pin. It is only valid when idpullup in
> + *  OTGCTRL1_TYPE register is set to '1'.
> + */
> +#define OTGSTS_ID_VALUE                        BIT(0)
> +/* Current value of the vbus_valid */
> +#define OTGSTS_VBUS_VALID              BIT(1)
> +/* Current value of the b_sess_vld */
> +#define OTGSTS_SESSION_VALID           BIT(2)
> +/*Device mode is active*/
> +#define OTGSTS_DEV_ACTIVE              BIT(3)
> +/* Host mode is active. */
> +#define OTGSTS_HOST_ACTIVE             BIT(4)
> +/* OTG Controller not ready. */
> +#define OTGSTS_OTG_NRDY_MASK           BIT(11)
> +#define OTGSTS_OTG_NRDY(p)             ((p) & OTGSTS_OTG_NRDY_MASK)
> +/*
> + * Value of the strap pins.
> + * 000 - no default configuration
> + * 010 - Controller initiall configured as Host
> + * 100 - Controller initially configured as Device
> + */
> +#define OTGSTS_STRAP(p)                        (((p) & GENMASK(14, 12)) >> 12)
> +#define OTGSTS_STRAP_NO_DEFAULT_CFG    0x00
> +#define OTGSTS_STRAP_HOST_OTG          0x01
> +#define OTGSTS_STRAP_HOST              0x02
> +#define OTGSTS_STRAP_GADGET            0x04
> +/* Host mode is turned on. */
> +#define OTGSTS_XHCI_READY              BIT(26)
> +/* "Device mode is turned on .*/
> +#define OTGSTS_DEV_READY               BIT(27)
> +
> +/* OTGSTATE- bitmasks */
> +#define OTGSTATE_HOST_STATE_MASK       GENMASK(5, 3)
> +#define OTGSTATE_HOST_STATE_IDLE       0x0
> +#define OTGSTATE_HOST_STATE_VBUS_FALL   0x7
> +#define OTGSTATE_HOST_STATE(p)         (((p) & OTGSTATE_HOST_STATE_MASK) >> 3)
> +
> +/* OTGREFCLK - bitmasks */
> +#define OTGREFCLK_STB_CLK_SWITCH_EN    BIT(31)
> +
> +/* OVERRIDE - bitmasks */
> +#define OVERRIDE_IDPULLUP              BIT(0)
> +/* Only for CDNS3_CONTROLLER_V0 version */
> +#define OVERRIDE_IDPULLUP_V0           BIT(24)
> +
> +int cdns3_is_host(struct cdns3 *cdns);
> +int cdns3_is_device(struct cdns3 *cdns);
> +int cdns3_get_id(struct cdns3 *cdns);
> +int cdns3_drd_init(struct cdns3 *cdns);
> +int cdns3_drd_exit(struct cdns3 *cdns);
> +int cdns3_drd_update_mode(struct cdns3 *cdns);
> +
> +#endif /* __LINUX_CDNS3_DRD */
> diff --git a/drivers/usb/cdns3/ep0.c b/drivers/usb/cdns3/ep0.c
> new file mode 100644
> index 000000000000..89cf1cde1555
> --- /dev/null
> +++ b/drivers/usb/cdns3/ep0.c
> @@ -0,0 +1,896 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Cadence USBSS DRD Driver - gadget side.
> + *
> + * Copyright (C) 2018 Cadence Design Systems.
> + * Copyright (C) 2017-2018 NXP
> + *
> + * Authors: Pawel Jez <pjez@cadence.com>,
> + *          Pawel Laszczak <pawell@cadence.com>
> + *         Peter Chen <peter.chen@nxp.com>
> + */
> +
> +#include <linux/usb/composite.h>
> +
> +#include "gadget.h"
> +#include "trace.h"
> +
> +static struct usb_endpoint_descriptor cdns3_gadget_ep0_desc = {
> +       .bLength = USB_DT_ENDPOINT_SIZE,
> +       .bDescriptorType = USB_DT_ENDPOINT,
> +       .bmAttributes = USB_ENDPOINT_XFER_CONTROL,
> +};
> +
> +/**
> + * cdns3_ep0_run_transfer - Do transfer on default endpoint hardware
> + * @priv_dev: extended gadget object
> + * @dma_addr: physical address where data is/will be stored
> + * @length: data length
> + * @erdy: set it to 1 when ERDY packet should be sent -
> + *        exit from flow control state
> + */
> +static void cdns3_ep0_run_transfer(struct cdns3_device *priv_dev,
> +                                  dma_addr_t dma_addr,
> +                                  unsigned int length, int erdy)
> +{
> +       struct cdns3_usb_regs __iomem *regs = priv_dev->regs;
> +       struct cdns3_endpoint *priv_ep = priv_dev->eps[0];
> +
> +       priv_ep->trb_pool->buffer = TRB_BUFFER(dma_addr);
> +       priv_ep->trb_pool->length = TRB_LEN(length);
> +       priv_ep->trb_pool->control = TRB_CYCLE | TRB_IOC | TRB_TYPE(TRB_NORMAL);
> +
> +       trace_cdns3_prepare_trb(priv_ep, priv_ep->trb_pool);
> +
> +       cdns3_select_ep(priv_dev, priv_dev->ep0_data_dir);
> +
> +       writel(EP_STS_TRBERR, &regs->ep_sts);
> +       writel(EP_TRADDR_TRADDR(priv_ep->trb_pool_dma), &regs->ep_traddr);
> +       trace_cdns3_doorbell_ep0(priv_dev->ep0_data_dir ? "ep0in" : "ep0out",
> +                                readl(&regs->ep_traddr));
> +
> +       /* TRB should be prepared before starting transfer */
> +       writel(EP_CMD_DRDY, &regs->ep_cmd);
> +
> +       if (erdy)
> +               writel(EP_CMD_ERDY, &priv_dev->regs->ep_cmd);
> +}
> +
> +/**
> + * cdns3_ep0_delegate_req - Returns status of handling setup packet
> + * Setup is handled by gadget driver
> + * @priv_dev: extended gadget object
> + * @ctrl_req: pointer to received setup packet
> + *
> + * Returns zero on success or negative value on failure
> + */
> +static int cdns3_ep0_delegate_req(struct cdns3_device *priv_dev,
> +                                 struct usb_ctrlrequest *ctrl_req)
> +{
> +       int ret;
> +
> +       spin_unlock(&priv_dev->lock);
> +       priv_dev->setup_pending = 1;
> +       ret = priv_dev->gadget_driver->setup(&priv_dev->gadget, ctrl_req);
> +       priv_dev->setup_pending = 0;
> +       spin_lock(&priv_dev->lock);
> +       return ret;
> +}
> +
> +static void cdns3_prepare_setup_packet(struct cdns3_device *priv_dev)
> +{
> +       priv_dev->ep0_data_dir = 0;
> +       priv_dev->ep0_stage = CDNS3_SETUP_STAGE;
> +       cdns3_ep0_run_transfer(priv_dev, priv_dev->setup_dma,
> +                              sizeof(struct usb_ctrlrequest), 0);
> +}
> +
> +static void cdns3_ep0_complete_setup(struct cdns3_device *priv_dev,
> +                                    u8 send_stall, u8 send_erdy)
> +{
> +       struct cdns3_endpoint *priv_ep = priv_dev->eps[0];
> +       struct usb_request *request;
> +
> +       request = cdns3_next_request(&priv_ep->pending_req_list);
> +       if (request)
> +               list_del_init(&request->list);
> +
> +       if (send_stall) {
> +               dev_dbg(priv_dev->dev, "STALL for ep0\n");
> +               /* set_stall on ep0 */
> +               cdns3_select_ep(priv_dev, 0x00);
> +               writel(EP_CMD_SSTALL, &priv_dev->regs->ep_cmd);
> +       } else {
> +               cdns3_prepare_setup_packet(priv_dev);
> +       }
> +
> +       priv_dev->ep0_stage = CDNS3_SETUP_STAGE;
> +       writel((send_erdy ? EP_CMD_ERDY : 0) | EP_CMD_REQ_CMPL,
> +              &priv_dev->regs->ep_cmd);
> +}
> +
> +/**
> + * cdns3_req_ep0_set_configuration - Handling of SET_CONFIG standard USB request
> + * @priv_dev: extended gadget object
> + * @ctrl_req: pointer to received setup packet
> + *
> + * Returns 0 if success, USB_GADGET_DELAYED_STATUS on deferred status stage,
> + * error code on error
> + */
> +static int cdns3_req_ep0_set_configuration(struct cdns3_device *priv_dev,
> +                                          struct usb_ctrlrequest *ctrl_req)
> +{
> +       enum usb_device_state device_state = priv_dev->gadget.state;
> +       struct cdns3_endpoint *priv_ep;
> +       u32 config = le16_to_cpu(ctrl_req->wValue);
> +       int result = 0;
> +       int i;
> +
> +       switch (device_state) {
> +       case USB_STATE_ADDRESS:
> +               /* Configure non-control EPs */
> +               for (i = 0; i < CDNS3_ENDPOINTS_MAX_COUNT; i++) {
> +                       priv_ep = priv_dev->eps[i];
> +                       if (!priv_ep)
> +                               continue;
> +
> +                       if (priv_ep->flags & EP_CLAIMED)
> +                               cdns3_ep_config(priv_ep);
> +               }
> +
> +               result = cdns3_ep0_delegate_req(priv_dev, ctrl_req);
> +
> +               if (result)
> +                       return result;
> +
> +               if (config) {
> +                       cdns3_set_hw_configuration(priv_dev);
> +               } else {
> +                       cdns3_hw_reset_eps_config(priv_dev);
> +                       usb_gadget_set_state(&priv_dev->gadget,
> +                                            USB_STATE_ADDRESS);
> +               }
> +               break;
> +       case USB_STATE_CONFIGURED:
> +               result = cdns3_ep0_delegate_req(priv_dev, ctrl_req);
> +
> +               if (!config && !result) {
> +                       cdns3_hw_reset_eps_config(priv_dev);
> +                       usb_gadget_set_state(&priv_dev->gadget,
> +                                            USB_STATE_ADDRESS);
> +               }
> +               break;
> +       default:
> +               result = -EINVAL;
> +       }
> +
> +       return result;
> +}
> +
> +/**
> + * cdns3_req_ep0_set_address - Handling of SET_ADDRESS standard USB request
> + * @priv_dev: extended gadget object
> + * @ctrl_req: pointer to received setup packet
> + *
> + * Returns 0 if success, error code on error
> + */
> +static int cdns3_req_ep0_set_address(struct cdns3_device *priv_dev,
> +                                    struct usb_ctrlrequest *ctrl_req)
> +{
> +       enum usb_device_state device_state = priv_dev->gadget.state;
> +       u32 reg;
> +       u32 addr;
> +
> +       addr = le16_to_cpu(ctrl_req->wValue);
> +
> +       if (addr > USB_DEVICE_MAX_ADDRESS) {
> +               dev_err(priv_dev->dev,
> +                       "Device address (%d) cannot be greater than %d\n",
> +                       addr, USB_DEVICE_MAX_ADDRESS);
> +               return -EINVAL;
> +       }
> +
> +       if (device_state == USB_STATE_CONFIGURED) {
> +               dev_err(priv_dev->dev,
> +                       "can't set_address from configured state\n");
> +               return -EINVAL;
> +       }
> +
> +       reg = readl(&priv_dev->regs->usb_cmd);
> +
> +       writel(reg | USB_CMD_FADDR(addr) | USB_CMD_SET_ADDR,
> +              &priv_dev->regs->usb_cmd);
> +
> +       usb_gadget_set_state(&priv_dev->gadget,
> +                            (addr ? USB_STATE_ADDRESS : USB_STATE_DEFAULT));
> +
> +       return 0;
> +}
> +
> +/**
> + * cdns3_req_ep0_get_status - Handling of GET_STATUS standard USB request
> + * @priv_dev: extended gadget object
> + * @ctrl_req: pointer to received setup packet
> + *
> + * Returns 0 if success, error code on error
> + */
> +static int cdns3_req_ep0_get_status(struct cdns3_device *priv_dev,
> +                                   struct usb_ctrlrequest *ctrl)
> +{
> +       __le16 *response_pkt;
> +       u16 usb_status = 0;
> +       u32 recip;
> +       u32 reg;
> +
> +       recip = ctrl->bRequestType & USB_RECIP_MASK;
> +
> +       switch (recip) {
> +       case USB_RECIP_DEVICE:
> +               /* self powered */
> +               if (priv_dev->is_selfpowered)
> +                       usb_status = BIT(USB_DEVICE_SELF_POWERED);
> +
> +               if (priv_dev->wake_up_flag)
> +                       usb_status |= BIT(USB_DEVICE_REMOTE_WAKEUP);
> +
> +               if (priv_dev->gadget.speed != USB_SPEED_SUPER)
> +                       break;
> +
> +               reg = readl(&priv_dev->regs->usb_sts);
> +
> +               if (priv_dev->u1_allowed)
> +                       usb_status |= BIT(USB_DEV_STAT_U1_ENABLED);
> +
> +               if (priv_dev->u2_allowed)
> +                       usb_status |= BIT(USB_DEV_STAT_U2_ENABLED);
> +
> +               break;
> +       case USB_RECIP_INTERFACE:
> +               return cdns3_ep0_delegate_req(priv_dev, ctrl);
> +       case USB_RECIP_ENDPOINT:
> +               /* check if endpoint is stalled */
> +               cdns3_select_ep(priv_dev, ctrl->wIndex);
> +               if (EP_STS_STALL(readl(&priv_dev->regs->ep_sts)))
> +                       usb_status =  BIT(USB_ENDPOINT_HALT);
> +               break;
> +       default:
> +               return -EINVAL;
> +       }
> +
> +       response_pkt = (__le16 *)priv_dev->setup_buf;
> +       *response_pkt = cpu_to_le16(usb_status);
> +
> +       cdns3_ep0_run_transfer(priv_dev, priv_dev->setup_dma,
> +                              sizeof(*response_pkt), 1);
> +       return 0;
> +}
> +
> +static int cdns3_ep0_feature_handle_device(struct cdns3_device *priv_dev,
> +                                          struct usb_ctrlrequest *ctrl,
> +                                          int set)
> +{
> +       enum usb_device_state state;
> +       enum usb_device_speed speed;
> +       int ret = 0;
> +       u32 wValue;
> +       u32 wIndex;
> +       u16 tmode;
> +
> +       wValue = le16_to_cpu(ctrl->wValue);
> +       wIndex = le16_to_cpu(ctrl->wIndex);
> +       state = priv_dev->gadget.state;
> +       speed = priv_dev->gadget.speed;
> +
> +       switch (ctrl->wValue) {
> +       case USB_DEVICE_REMOTE_WAKEUP:
> +               priv_dev->wake_up_flag = !!set;
> +               break;
> +       case USB_DEVICE_U1_ENABLE:
> +               if (state != USB_STATE_CONFIGURED || speed != USB_SPEED_SUPER)
> +                       return -EINVAL;
> +
> +               priv_dev->u1_allowed = !!set;
> +               break;
> +       case USB_DEVICE_U2_ENABLE:
> +               if (state != USB_STATE_CONFIGURED || speed != USB_SPEED_SUPER)
> +                       return -EINVAL;
> +
> +               priv_dev->u2_allowed = !!set;
> +               break;
> +       case USB_DEVICE_LTM_ENABLE:
> +               ret = -EINVAL;
> +               break;
> +       case USB_DEVICE_TEST_MODE:
> +               if (state != USB_STATE_CONFIGURED || speed > USB_SPEED_HIGH)
> +                       return -EINVAL;
> +
> +               tmode = le16_to_cpu(ctrl->wIndex);
> +
> +               if (!set || (tmode & 0xff) != 0)
> +                       return -EINVAL;
> +
> +               switch (tmode >> 8) {
> +               case TEST_J:
> +               case TEST_K:
> +               case TEST_SE0_NAK:
> +               case TEST_PACKET:
> +                       cdns3_ep0_complete_setup(priv_dev, 0, 1);
> +                       /**
> +                        *  Little delay to give the controller some time
> +                        * for sending status stage.
> +                        * This time should be less then 3ms.
> +                        */
> +                       usleep_range(1000, 2000);
> +                       cdns3_set_register_bit(&priv_dev->regs->usb_cmd,
> +                                              USB_CMD_STMODE |
> +                                              USB_STS_TMODE_SEL(tmode - 1));
> +                       break;
> +               default:
> +                       ret = -EINVAL;
> +               }
> +               break;
> +       default:
> +               ret = -EINVAL;
> +       }
> +
> +       return ret;
> +}
> +
> +static int cdns3_ep0_feature_handle_intf(struct cdns3_device *priv_dev,
> +                                        struct usb_ctrlrequest *ctrl,
> +                                        int set)
> +{
> +       u32 wValue;
> +       int ret = 0;
> +
> +       wValue = le16_to_cpu(ctrl->wValue);
> +
> +       switch (wValue) {
> +       case USB_INTRF_FUNC_SUSPEND:
> +               break;
> +       default:
> +               ret = -EINVAL;
> +       }
> +
> +       return ret;
> +}
> +
> +static int cdns3_ep0_feature_handle_endpoint(struct cdns3_device *priv_dev,
> +                                            struct usb_ctrlrequest *ctrl,
> +                                            int set)
> +{
> +       struct cdns3_endpoint *priv_ep;
> +       int ret = 0;
> +       u8 index;
> +
> +       if (le16_to_cpu(ctrl->wValue) != USB_ENDPOINT_HALT)
> +               return -EINVAL;
> +
> +       if (!(ctrl->wIndex & ~USB_DIR_IN))
> +               return 0;
> +
> +       index = cdns3_ep_addr_to_index(ctrl->wIndex);
> +       priv_ep = priv_dev->eps[index];
> +
> +       cdns3_select_ep(priv_dev, ctrl->wIndex);
> +
> +       if (set) {
> +               writel(EP_CMD_SSTALL, &priv_dev->regs->ep_cmd);
> +               priv_ep->flags |= EP_STALL;
> +       } else {
> +               struct usb_request *request;
> +
> +               if (priv_dev->eps[index]->flags & EP_WEDGE) {
> +                       cdns3_select_ep(priv_dev, 0x00);
> +                       return 0;
> +               }
> +
> +               writel(EP_CMD_CSTALL | EP_CMD_EPRST, &priv_dev->regs->ep_cmd);
> +
> +               /* wait for EPRST cleared */
> +               ret = cdns3_handshake(&priv_dev->regs->ep_cmd,
> +                                     EP_CMD_EPRST, 0, 100);
> +               if (ret)
> +                       return -EINVAL;
> +
> +               priv_ep->flags &= ~EP_STALL;
> +
> +               request = cdns3_next_request(&priv_ep->pending_req_list);
> +               if (request)
> +                       cdns3_ep_run_transfer(priv_ep, request);
> +       }
> +       return ret;
> +}
> +
> +/**
> + * cdns3_req_ep0_handle_feature -
> + * Handling of GET/SET_FEATURE standard USB request
> + *
> + * @priv_dev: extended gadget object
> + * @ctrl_req: pointer to received setup packet
> + * @set: must be set to 1 for SET_FEATURE request
> + *
> + * Returns 0 if success, error code on error
> + */
> +static int cdns3_req_ep0_handle_feature(struct cdns3_device *priv_dev,
> +                                       struct usb_ctrlrequest *ctrl,
> +                                       int set)
> +{
> +       int ret = 0;
> +       u32 recip;
> +
> +       recip = ctrl->bRequestType & USB_RECIP_MASK;
> +
> +       switch (recip) {
> +       case USB_RECIP_DEVICE:
> +               ret = cdns3_ep0_feature_handle_device(priv_dev, ctrl, set);
> +               break;
> +       case USB_RECIP_INTERFACE:
> +               ret = cdns3_ep0_feature_handle_intf(priv_dev, ctrl, set);
> +               break;
> +       case USB_RECIP_ENDPOINT:
> +               ret = cdns3_ep0_feature_handle_endpoint(priv_dev, ctrl, set);
> +               break;
> +       default:
> +               return -EINVAL;
> +       }
> +
> +       return ret;
> +}
> +
> +/**
> + * cdns3_req_ep0_set_sel - Handling of SET_SEL standard USB request
> + * @priv_dev: extended gadget object
> + * @ctrl_req: pointer to received setup packet
> + *
> + * Returns 0 if success, error code on error
> + */
> +static int cdns3_req_ep0_set_sel(struct cdns3_device *priv_dev,
> +                                struct usb_ctrlrequest *ctrl_req)
> +{
> +       if (priv_dev->gadget.state < USB_STATE_ADDRESS)
> +               return -EINVAL;
> +
> +       if (ctrl_req->wLength != 6) {
> +               dev_err(priv_dev->dev, "Set SEL should be 6 bytes, got %d\n",
> +                       ctrl_req->wLength);
> +               return -EINVAL;
> +       }
> +
> +       cdns3_ep0_run_transfer(priv_dev, priv_dev->setup_dma, 6, 1);
> +       return 0;
> +}
> +
> +/**
> + * cdns3_req_ep0_set_isoch_delay -
> + * Handling of GET_ISOCH_DELAY standard USB request
> + * @priv_dev: extended gadget object
> + * @ctrl_req: pointer to received setup packet
> + *
> + * Returns 0 if success, error code on error
> + */
> +static int cdns3_req_ep0_set_isoch_delay(struct cdns3_device *priv_dev,
> +                                        struct usb_ctrlrequest *ctrl_req)
> +{
> +       if (ctrl_req->wIndex || ctrl_req->wLength)
> +               return -EINVAL;
> +
> +       priv_dev->isoch_delay = ctrl_req->wValue;
> +
> +       return 0;
> +}
> +
> +/**
> + * cdns3_ep0_standard_request - Handling standard USB requests
> + * @priv_dev: extended gadget object
> + * @ctrl_req: pointer to received setup packet
> + *
> + * Returns 0 if success, error code on error
> + */
> +static int cdns3_ep0_standard_request(struct cdns3_device *priv_dev,
> +                                     struct usb_ctrlrequest *ctrl_req)
> +{
> +       int ret;
> +
> +       switch (ctrl_req->bRequest) {
> +       case USB_REQ_SET_ADDRESS:
> +               ret = cdns3_req_ep0_set_address(priv_dev, ctrl_req);
> +               break;
> +       case USB_REQ_SET_CONFIGURATION:
> +               ret = cdns3_req_ep0_set_configuration(priv_dev, ctrl_req);
> +               break;
> +       case USB_REQ_GET_STATUS:
> +               ret = cdns3_req_ep0_get_status(priv_dev, ctrl_req);
> +               break;
> +       case USB_REQ_CLEAR_FEATURE:
> +               ret = cdns3_req_ep0_handle_feature(priv_dev, ctrl_req, 0);
> +               break;
> +       case USB_REQ_SET_FEATURE:
> +               ret = cdns3_req_ep0_handle_feature(priv_dev, ctrl_req, 1);
> +               break;
> +       case USB_REQ_SET_SEL:
> +               ret = cdns3_req_ep0_set_sel(priv_dev, ctrl_req);
> +               break;
> +       case USB_REQ_SET_ISOCH_DELAY:
> +               ret = cdns3_req_ep0_set_isoch_delay(priv_dev, ctrl_req);
> +               break;
> +       default:
> +               ret = cdns3_ep0_delegate_req(priv_dev, ctrl_req);
> +               break;
> +       }
> +
> +       return ret;
> +}
> +
> +static void __pending_setup_status_handler(struct cdns3_device *priv_dev)
> +{
> +       struct usb_request *request = priv_dev->pending_status_request;
> +
> +       if (priv_dev->status_completion_no_call && request &&
> +           request->complete) {
> +               request->complete(&priv_dev->eps[0]->endpoint, request);
> +               priv_dev->status_completion_no_call = 0;
> +       }
> +}
> +
> +void cdns3_pending_setup_status_handler(struct work_struct *work)
> +{
> +       struct cdns3_device *priv_dev = container_of(work, struct cdns3_device,
> +                       pending_status_wq);
> +       unsigned long flags;
> +
> +       spin_lock_irqsave(&priv_dev->lock, flags);
> +       __pending_setup_status_handler(priv_dev);
> +       spin_unlock_irqrestore(&priv_dev->lock, flags);
> +}
> +
> +/**
> + * cdns3_gadget_ep_giveback - call struct usb_request's ->complete callback
> + * @priv_ep: The endpoint to whom the request belongs to
> + * @priv_req: The request we're giving back
> + * @status: completion code for the request
> + *
> + * Must be called with controller's lock held and interrupts disabled. This
> + * function will unmap @req and call its ->complete() callback to notify upper
> + * layers that it has completed.
> + */
> +
> +void cdns3_gadget_ep0_giveback(struct cdns3_device *priv_dev,
> +                              int status)
> +{
> +       struct cdns3_endpoint *priv_ep;
> +       struct usb_request *request;
> +
> +       priv_ep = priv_dev->eps[0];
> +       request = cdns3_next_request(&priv_ep->pending_req_list);
> +
> +       priv_ep->dir = priv_dev->ep0_data_dir;
> +       cdns3_gadget_giveback(priv_ep, to_cdns3_request(request), status);
> +}
> +
> +/**
> + * cdns3_ep0_setup_phase - Handling setup USB requests
> + * @priv_dev: extended gadget object
> + */
> +static void cdns3_ep0_setup_phase(struct cdns3_device *priv_dev)
> +{
> +       struct usb_ctrlrequest *ctrl = priv_dev->setup_buf;
> +       struct cdns3_endpoint *priv_ep = priv_dev->eps[0];
> +       int result;
> +
> +       priv_dev->ep0_data_dir = ctrl->bRequestType & USB_DIR_IN;
> +
> +       trace_cdns3_ctrl_req(ctrl);
> +
> +       if (!list_empty(&priv_ep->pending_req_list)) {
> +               struct usb_request *request;
> +
> +               request = cdns3_next_request(&priv_ep->pending_req_list);
> +               priv_ep->dir = priv_dev->ep0_data_dir;
> +               cdns3_gadget_giveback(priv_ep, to_cdns3_request(request),
> +                                     -ECONNRESET);
> +       }
> +
> +       if (le16_to_cpu(ctrl->wLength))
> +               priv_dev->ep0_stage = CDNS3_DATA_STAGE;
> +       else
> +               priv_dev->ep0_stage = CDNS3_STATUS_STAGE;
> +
> +       if ((ctrl->bRequestType & USB_TYPE_MASK) == USB_TYPE_STANDARD)
> +               result = cdns3_ep0_standard_request(priv_dev, ctrl);
> +       else
> +               result = cdns3_ep0_delegate_req(priv_dev, ctrl);
> +
> +       if (priv_dev->ep0_stage == CDNS3_STATUS_STAGE &&
> +           result != USB_GADGET_DELAYED_STATUS) {
> +               cdns3_ep0_complete_setup(priv_dev, 0, 1);
> +       } else if (result < 0) {
> +               cdns3_ep0_complete_setup(priv_dev, 1, 1);
> +       }
> +}
> +
> +static void cdns3_transfer_completed(struct cdns3_device *priv_dev)
> +{
> +       struct cdns3_endpoint *priv_ep = priv_dev->eps[0];
> +
> +       if (!list_empty(&priv_ep->pending_req_list)) {
> +               struct usb_request *request;
> +
> +               trace_cdns3_complete_trb(priv_ep, priv_ep->trb_pool);
> +               request = cdns3_next_request(&priv_ep->pending_req_list);
> +
> +               request->actual =
> +                       TRB_LEN(le32_to_cpu(priv_ep->trb_pool->length));
> +
> +               priv_ep->dir = priv_dev->ep0_data_dir;
> +               cdns3_gadget_giveback(priv_ep, to_cdns3_request(request), 0);
> +       }
> +
> +       cdns3_ep0_complete_setup(priv_dev, 0, 0);
> +}
> +
> +/**
> + * cdns3_check_new_setup - Check if controller receive new SETUP packet.
> + * @priv_dev: extended gadget object
> + *
> + * The SETUP packet can be kept in on-chip memory or in system memory.
> + */
> +static bool cdns3_check_new_setup(struct cdns3_device *priv_dev)
> +{
> +       u32 ep_sts_reg;
> +
> +       cdns3_select_ep(priv_dev, 0 | USB_DIR_OUT);
> +       ep_sts_reg = readl(&priv_dev->regs->ep_sts);
> +
> +       return !!(ep_sts_reg & (EP_STS_SETUP | EP_STS_STPWAIT));
> +}
> +
> +/**
> + * cdns3_check_ep0_interrupt_proceed - Processes interrupt related to endpoint 0
> + * @priv_dev: extended gadget object
> + * @dir: USB_DIR_IN for IN direction, USB_DIR_OUT for OUT direction
> + */
> +void cdns3_check_ep0_interrupt_proceed(struct cdns3_device *priv_dev, int dir)
> +{
> +       u32 ep_sts_reg;
> +
> +       cdns3_select_ep(priv_dev, dir);
> +
> +       ep_sts_reg = readl(&priv_dev->regs->ep_sts);
> +       writel(ep_sts_reg, &priv_dev->regs->ep_sts);
> +
> +       trace_cdns3_ep0_irq(priv_dev, ep_sts_reg);
> +
> +       __pending_setup_status_handler(priv_dev);
> +
> +       if ((ep_sts_reg & EP_STS_SETUP)) {
> +               cdns3_ep0_setup_phase(priv_dev);
> +       } else if ((ep_sts_reg & EP_STS_IOC) || (ep_sts_reg & EP_STS_ISP)) {
> +               priv_dev->ep0_data_dir = dir;
> +               cdns3_transfer_completed(priv_dev);
> +       }
> +
> +       if (ep_sts_reg & EP_STS_DESCMIS) {
> +               if (dir == 0 && !priv_dev->setup_pending)
> +                       cdns3_prepare_setup_packet(priv_dev);
> +       }
> +}
> +
> +/**
> + * cdns3_gadget_ep0_enable
> + * Function shouldn't be called by gadget driver,
> + * endpoint 0 is allways active
> + */
> +static int cdns3_gadget_ep0_enable(struct usb_ep *ep,
> +                                  const struct usb_endpoint_descriptor *desc)
> +{
> +       return -EINVAL;
> +}
> +
> +/**
> + * cdns3_gadget_ep0_disable
> + * Function shouldn't be called by gadget driver,
> + * endpoint 0 is allways active
> + */
> +static int cdns3_gadget_ep0_disable(struct usb_ep *ep)
> +{
> +       return -EINVAL;
> +}
> +
> +/**
> + * cdns3_gadget_ep0_set_halt
> + * @ep: pointer to endpoint zero object
> + * @value: 1 for set stall, 0 for clear stall
> + *
> + * Returns 0
> + */
> +static int cdns3_gadget_ep0_set_halt(struct usb_ep *ep, int value)
> +{
> +       /* TODO */
> +       return 0;
> +}
> +
> +/**
> + * cdns3_gadget_ep0_queue Transfer data on endpoint zero
> + * @ep: pointer to endpoint zero object
> + * @request: pointer to request object
> + * @gfp_flags: gfp flags
> + *
> + * Returns 0 on success, error code elsewhere
> + */
> +static int cdns3_gadget_ep0_queue(struct usb_ep *ep,
> +                                 struct usb_request *request,
> +                                 gfp_t gfp_flags)
> +{
> +       struct cdns3_endpoint *priv_ep = ep_to_cdns3_ep(ep);
> +       struct cdns3_device *priv_dev = priv_ep->cdns3_dev;
> +       unsigned long flags;
> +       int erdy_sent = 0;
> +       int ret = 0;
> +
> +       dev_dbg(priv_dev->dev, "Queue to Ep0%s L: %d\n",
> +               priv_dev->ep0_data_dir ? "IN" : "OUT",
> +               request->length);
> +
> +       /* cancel the request if controller receive new SETUP packet. */
> +       if (cdns3_check_new_setup(priv_dev))
> +               return -ECONNRESET;
> +
> +       /* send STATUS stage. Should be called only for SET_CONFIGURATION */
> +       if (priv_dev->ep0_stage == CDNS3_STATUS_STAGE) {
> +               spin_lock_irqsave(&priv_dev->lock, flags);
> +               cdns3_select_ep(priv_dev, 0x00);
> +
> +               erdy_sent = !priv_dev->hw_configured_flag;
> +               cdns3_set_hw_configuration(priv_dev);
> +
> +               if (!erdy_sent)
> +                       cdns3_ep0_complete_setup(priv_dev, 0, 1);
> +
> +               request->actual = 0;
> +               priv_dev->status_completion_no_call = true;
> +               priv_dev->pending_status_request = request;
> +               spin_unlock_irqrestore(&priv_dev->lock, flags);
> +
> +               /*
> +                * Since there is no completion interrupt for status stage,
> +                * it needs to call ->completion in software after
> +                * ep0_queue is back.
> +                */
> +               queue_work(system_freezable_wq, &priv_dev->pending_status_wq);
> +               return 0;
> +       }
> +
> +       spin_lock_irqsave(&priv_dev->lock, flags);
> +       if (!list_empty(&priv_ep->pending_req_list)) {
> +               dev_err(priv_dev->dev,
> +                       "can't handle multiple requests for ep0\n");
> +               spin_unlock_irqrestore(&priv_dev->lock, flags);
> +               return -EBUSY;
> +       }
> +
> +       ret = usb_gadget_map_request_by_dev(priv_dev->sysdev, request,
> +                                           priv_dev->ep0_data_dir);
> +       if (ret) {
> +               spin_unlock_irqrestore(&priv_dev->lock, flags);
> +               dev_err(priv_dev->dev, "failed to map request\n");
> +               return -EINVAL;
> +       }
> +
> +       request->status = -EINPROGRESS;
> +       list_add_tail(&request->list, &priv_ep->pending_req_list);
> +       cdns3_ep0_run_transfer(priv_dev, request->dma, request->length, 1);
> +       spin_unlock_irqrestore(&priv_dev->lock, flags);
> +
> +       return ret;
> +}
> +
> +/**
> + * cdns3_gadget_ep_set_wedge Set wedge on selected endpoint
> + * @ep: endpoint object
> + *
> + * Returns 0
> + */
> +int cdns3_gadget_ep_set_wedge(struct usb_ep *ep)
> +{
> +       struct cdns3_endpoint *priv_ep = ep_to_cdns3_ep(ep);
> +       struct cdns3_device *priv_dev = priv_ep->cdns3_dev;
> +
> +       dev_dbg(priv_dev->dev, "Wedge for %s\n", ep->name);
> +       cdns3_gadget_ep_set_halt(ep, 1);
> +       priv_ep->flags |= EP_WEDGE;
> +
> +       return 0;
> +}
> +
> +const struct usb_ep_ops cdns3_gadget_ep0_ops = {
> +       .enable = cdns3_gadget_ep0_enable,
> +       .disable = cdns3_gadget_ep0_disable,
> +       .alloc_request = cdns3_gadget_ep_alloc_request,
> +       .free_request = cdns3_gadget_ep_free_request,
> +       .queue = cdns3_gadget_ep0_queue,
> +       .dequeue = cdns3_gadget_ep_dequeue,
> +       .set_halt = cdns3_gadget_ep0_set_halt,
> +       .set_wedge = cdns3_gadget_ep_set_wedge,
> +};
> +
> +/**
> + * cdns3_ep0_config - Configures default endpoint
> + * @priv_dev: extended gadget object
> + *
> + * Functions sets parameters: maximal packet size and enables interrupts
> + */
> +void cdns3_ep0_config(struct cdns3_device *priv_dev)
> +{
> +       struct cdns3_usb_regs __iomem *regs;
> +       struct cdns3_endpoint *priv_ep;
> +       u32 max_packet_size = 64;
> +
> +       regs = priv_dev->regs;
> +
> +       if (priv_dev->gadget.speed == USB_SPEED_SUPER)
> +               max_packet_size = 512;
> +
> +       priv_ep = priv_dev->eps[0];
> +
> +       if (!list_empty(&priv_ep->pending_req_list)) {
> +               struct usb_request *request;
> +
> +               request = cdns3_next_request(&priv_ep->pending_req_list);
> +               list_del_init(&request->list);
> +       }
> +
> +       priv_dev->u1_allowed = 0;
> +       priv_dev->u2_allowed = 0;
> +
> +       priv_dev->gadget.ep0->maxpacket = max_packet_size;
> +       cdns3_gadget_ep0_desc.wMaxPacketSize = cpu_to_le16(max_packet_size);
> +
> +       /* init ep out */
> +       cdns3_select_ep(priv_dev, USB_DIR_OUT);
> +
> +       writel(EP_CFG_ENABLE | EP_CFG_MAXPKTSIZE(max_packet_size),
> +              &regs->ep_cfg);
> +
> +       writel(EP_STS_EN_SETUPEN | EP_STS_EN_DESCMISEN | EP_STS_EN_TRBERREN,
> +              &regs->ep_sts_en);
> +
> +       /* init ep in */
> +       cdns3_select_ep(priv_dev, USB_DIR_IN);
> +
> +       writel(EP_CFG_ENABLE | EP_CFG_MAXPKTSIZE(max_packet_size),
> +              &regs->ep_cfg);
> +
> +       writel(EP_STS_EN_SETUPEN | EP_STS_EN_TRBERREN, &regs->ep_sts_en);
> +
> +       cdns3_set_register_bit(&regs->usb_conf, USB_CONF_U1DS | USB_CONF_U2DS);
> +}
> +
> +/**
> + * cdns3_init_ep0 Initializes software endpoint 0 of gadget
> + * @priv_dev: extended gadget object
> + * @ep_priv: extended endpoint object
> + *
> + * Returns 0 on success else error code.
> + */
> +int cdns3_init_ep0(struct cdns3_device *priv_dev,
> +                  struct cdns3_endpoint *priv_ep)
> +{
> +       sprintf(priv_ep->name, "ep0");
> +
> +       /* fill linux fields */
> +       priv_ep->endpoint.ops = &cdns3_gadget_ep0_ops;
> +       priv_ep->endpoint.maxburst = 1;
> +       usb_ep_set_maxpacket_limit(&priv_ep->endpoint,
> +                                  CDNS3_EP0_MAX_PACKET_LIMIT);
> +       priv_ep->endpoint.address = 0;
> +       priv_ep->endpoint.caps.type_control = 1;
> +       priv_ep->endpoint.caps.dir_in = 1;
> +       priv_ep->endpoint.caps.dir_out = 1;
> +       priv_ep->endpoint.name = priv_ep->name;
> +       priv_ep->endpoint.desc = &cdns3_gadget_ep0_desc;
> +       priv_dev->gadget.ep0 = &priv_ep->endpoint;
> +       priv_ep->type = USB_ENDPOINT_XFER_CONTROL;
> +
> +       return cdns3_allocate_trb_pool(priv_ep);
> +}
> diff --git a/drivers/usb/cdns3/gadget-export.h b/drivers/usb/cdns3/gadget-export.h
> new file mode 100644
> index 000000000000..577469eee961
> --- /dev/null
> +++ b/drivers/usb/cdns3/gadget-export.h
> @@ -0,0 +1,28 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +/*
> + * Cadence USBSS DRD Driver - Gadget Export APIs.
> + *
> + * Copyright (C) 2017 NXP
> + * Copyright (C) 2017-2018 NXP
> + *
> + * Authors: Peter Chen <peter.chen@nxp.com>
> + */
> +#ifndef __LINUX_CDNS3_GADGET_EXPORT
> +#define __LINUX_CDNS3_GADGET_EXPORT
> +
> +#ifdef CONFIG_USB_CDNS3_GADGET
> +
> +int cdns3_gadget_init(struct cdns3 *cdns);
> +void cdns3_gadget_exit(struct cdns3 *cdns);
> +#else
> +
> +static inline int cdns3_gadget_init(struct cdns3 *cdns)
> +{
> +       return -ENXIO;
> +}
> +
> +static inline void cdns3_gadget_exit(struct cdns3 *cdns) { }
> +
> +#endif
> +
> +#endif /* __LINUX_CDNS3_GADGET_EXPORT */
> diff --git a/drivers/usb/cdns3/gadget.c b/drivers/usb/cdns3/gadget.c
> new file mode 100644
> index 000000000000..0d95eb00be37
> --- /dev/null
> +++ b/drivers/usb/cdns3/gadget.c
> @@ -0,0 +1,2102 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Cadence USBSS DRD Driver - gadget side.
> + *
> + * Copyright (C) 2018 Cadence Design Systems.
> + * Copyright (C) 2017-2018 NXP
> + *
> + * Authors: Pawel Jez <pjez@cadence.com>,
> + *          Pawel Laszczak <pawell@cadence.com>
> + *         Peter Chen <peter.chen@nxp.com>
> + */
> +
> +#include <linux/dma-mapping.h>
> +#include <linux/usb/gadget.h>
> +
> +#include "core.h"
> +#include "gadget-export.h"
> +#include "gadget.h"
> +
> +#include "trace.h"
> +
> +static int __cdns3_gadget_ep_queue(struct usb_ep *ep,
> +                                  struct usb_request *request,
> +                                  gfp_t gfp_flags);
> +
> +/**
> + * cdns3_handshake - spin reading  until handshake completes or fails
> + * @ptr: address of device controller register to be read
> + * @mask: bits to look at in result of read
> + * @done: value of those bits when handshake succeeds
> + * @usec: timeout in microseconds
> + *
> + * Returns negative errno, or zero on success
> + *
> + * Success happens when the "mask" bits have the specified value (hardware
> + * handshake done). There are two failure modes: "usec" have passed (major
> + * hardware flakeout), or the register reads as all-ones (hardware removed).
> + */
> +int cdns3_handshake(void __iomem *ptr, u32 mask, u32 done, int usec)
> +{
> +       u32     result;
> +
> +       do {
> +               result = readl(ptr);
> +               if (result == ~(u32)0)  /* card removed */
> +                       return -ENODEV;
> +               result &= mask;
> +               if (result == done)
> +                       return 0;
> +               udelay(1);
> +               usec--;
> +       } while (usec > 0);
> +       return -ETIMEDOUT;
> +}
> +
> +/**
> + * cdns3_set_register_bit - set bit in given register.
> + * @ptr: address of device controller register to be read and changed
> + * @mask: bits requested to set
> + */
> +void cdns3_set_register_bit(void __iomem *ptr, u32 mask)
> +{
> +       mask = readl(ptr) | mask;
> +       writel(mask, ptr);
> +}
> +
> +/**
> + * cdns3_ep_addr_to_index - Macro converts endpoint address to
> + * index of endpoint object in cdns3_device.eps[] container
> + * @ep_addr: endpoint address for which endpoint object is required
> + *
> + */
> +u8 cdns3_ep_addr_to_index(u8 ep_addr)
> +{
> +       return (((ep_addr & 0x7F)) + ((ep_addr & USB_DIR_IN) ? 16 : 0));
> +}
> +
> +/**
> + * cdns3_next_request - returns next request from list
> + * @list: list containing requests
> + *
> + * Returns request or NULL if no requests in list
> + */
> +struct usb_request *cdns3_next_request(struct list_head *list)
> +{
> +       if (list_empty(list))
> +               return NULL;
> +       return list_first_entry_or_null(list, struct usb_request, list);
> +}
> +
> +/**
> + * cdns3_next_priv_request - returns next request from list
> + * @list: list containing requests
> + *
> + * Returns request or NULL if no requests in list
> + */
> +struct cdns3_request *cdns3_next_priv_request(struct list_head *list)
> +{
> +       if (list_empty(list))
> +               return NULL;
> +       return list_first_entry_or_null(list, struct cdns3_request, list);
> +}
> +
> +/**
> + * select_ep - selects endpoint
> + * @priv_dev:  extended gadget object
> + * @ep: endpoint address
> + */
> +void cdns3_select_ep(struct cdns3_device *priv_dev, u32 ep)
> +{
> +       if (priv_dev->selected_ep == ep)
> +               return;
> +
> +       priv_dev->selected_ep = ep;
> +       writel(ep, &priv_dev->regs->ep_sel);
> +}
> +
> +dma_addr_t cdns3_trb_virt_to_dma(struct cdns3_endpoint *priv_ep,
> +                                struct cdns3_trb *trb)
> +{
> +       u32 offset = (char *)trb - (char *)priv_ep->trb_pool;
> +
> +       return priv_ep->trb_pool_dma + offset;
> +}
> +
> +int cdns3_ring_size(struct cdns3_endpoint *priv_ep)
> +{
> +       switch (priv_ep->type) {
> +       case USB_ENDPOINT_XFER_ISOC:
> +               return TRB_ISO_RING_SIZE;
> +       case USB_ENDPOINT_XFER_CONTROL:
> +               return TRB_CTRL_RING_SIZE;
> +       default:
> +               return TRB_RING_SIZE;
> +       }
> +}
> +
> +/**
> + * cdns3_allocate_trb_pool - Allocates TRB's pool for selected endpoint
> + * @priv_ep:  endpoint object
> + *
> + * Function will return 0 on success or -ENOMEM on allocation error
> + */
> +int cdns3_allocate_trb_pool(struct cdns3_endpoint *priv_ep)
> +{
> +       struct cdns3_device *priv_dev = priv_ep->cdns3_dev;
> +       int ring_size = cdns3_ring_size(priv_ep);
> +       struct cdns3_trb *link_trb;
> +
> +       if (!priv_ep->trb_pool) {
> +               priv_ep->trb_pool = dma_zalloc_coherent(priv_dev->sysdev,
> +                                                       ring_size,
> +                                                       &priv_ep->trb_pool_dma,
> +                                                       GFP_DMA);
> +               if (!priv_ep->trb_pool)
> +                       return -ENOMEM;
> +       } else {
> +               memset(priv_ep->trb_pool, 0, ring_size);
> +       }
> +
> +       if (!priv_ep->num)
> +               return 0;
> +
> +       if (!priv_ep->aligned_buff) {
> +               void *buff = dma_alloc_coherent(priv_dev->sysdev,
> +                                               CDNS3_ALIGNED_BUF_SIZE,
> +                                               &priv_ep->aligned_dma_addr,
> +                                               GFP_DMA);
> +
> +               priv_ep->aligned_buff  = buff;
> +               if (!priv_ep->aligned_buff) {
> +                       dma_free_coherent(priv_dev->sysdev,
> +                                         ring_size,
> +                                         priv_ep->trb_pool,
> +                                         priv_ep->trb_pool_dma);
> +                       priv_ep->trb_pool = NULL;
> +
> +                       return -ENOMEM;
> +               }
> +       }
> +
> +       priv_ep->num_trbs = ring_size / TRB_SIZE;
> +       /* Initialize the last TRB as Link TRB */
> +       link_trb = (priv_ep->trb_pool + (priv_ep->num_trbs - 1));
> +       link_trb->buffer = TRB_BUFFER(priv_ep->trb_pool_dma);
> +       link_trb->control = TRB_CYCLE | TRB_TYPE(TRB_LINK) |
> +                           TRB_CHAIN | TRB_TOGGLE;
> +
> +       return 0;
> +}
> +
> +static void cdns3_free_trb_pool(struct cdns3_endpoint *priv_ep)
> +{
> +       struct cdns3_device *priv_dev = priv_ep->cdns3_dev;
> +
> +       if (priv_ep->trb_pool) {
> +               dma_free_coherent(priv_dev->sysdev,
> +                                 cdns3_ring_size(priv_ep),
> +                                 priv_ep->trb_pool, priv_ep->trb_pool_dma);
> +               priv_ep->trb_pool = NULL;
> +       }
> +
> +       if (priv_ep->aligned_buff) {
> +               dma_free_coherent(priv_dev->sysdev, CDNS3_ALIGNED_BUF_SIZE,
> +                                 priv_ep->aligned_buff,
> +                                 priv_ep->aligned_dma_addr);
> +               priv_ep->aligned_buff = NULL;
> +       }
> +}
> +
> +/**
> + * cdns3_data_flush - flush data at onchip buffer
> + * @priv_ep: endpoint object
> + *
> + * Endpoint must be selected before call to this function
> + *
> + * Returns zero on success or negative value on failure
> + */
> +static int cdns3_data_flush(struct cdns3_endpoint *priv_ep)
> +{
> +       struct cdns3_device *priv_dev = priv_ep->cdns3_dev;
> +
> +       writel(EP_CMD_DFLUSH, &priv_dev->regs->ep_cmd);
> +
> +       /* wait for DFLUSH cleared */
> +       return cdns3_handshake(&priv_dev->regs->ep_cmd, EP_CMD_DFLUSH, 0, 100);
> +}
> +
> +/**
> + * cdns3_ep_stall_flush - Stalls and flushes selected endpoint
> + * @priv_ep: endpoint object
> + *
> + * Endpoint must be selected before call to this function
> + */
> +static void cdns3_ep_stall_flush(struct cdns3_endpoint *priv_ep)
> +{
> +       struct cdns3_device *priv_dev = priv_ep->cdns3_dev;
> +
> +       writel(EP_CMD_DFLUSH | EP_CMD_ERDY | EP_CMD_SSTALL,
> +              &priv_dev->regs->ep_cmd);
> +
> +       /* wait for DFLUSH cleared */
> +       cdns3_handshake(&priv_dev->regs->ep_cmd, EP_CMD_DFLUSH, 0, 100);
> +       priv_ep->flags |= EP_STALL;
> +}
> +
> +/**
> + * cdns3_hw_reset_eps_config - reset endpoints configuration kept by controller.
> + * @priv_dev: extended gadget object
> + */
> +void cdns3_hw_reset_eps_config(struct cdns3_device *priv_dev)
> +{
> +       writel(USB_CONF_CFGRST, &priv_dev->regs->usb_conf);
> +
> +       cdns3_allow_enable_l1(priv_dev, 0);
> +       priv_dev->hw_configured_flag = 0;
> +       priv_dev->onchip_mem_allocated_size = 0;
> +}
> +
> +/**
> + * cdns3_ep_inc_trb - increment a trb index.
> + * @index: Pointer to the TRB index to increment.
> + * @cs: Cycle state
> + * @trb_in_seg: number of TRBs in segment
> + *
> + * The index should never point to the link TRB. After incrementing,
> + * if it is point to the link TRB, wrap around to the beginning and revert
> + * cycle state bit The
> + * link TRB is always at the last TRB entry.
> + */
> +static void cdns3_ep_inc_trb(int *index, u8 *cs, int trb_in_seg)
> +{
> +       (*index)++;
> +       if (*index == (trb_in_seg - 1)) {
> +               *index = 0;
> +               *cs ^=  1;
> +       }
> +}
> +
> +/**
> + * cdns3_ep_inc_enq - increment endpoint's enqueue pointer
> + * @priv_ep: The endpoint whose enqueue pointer we're incrementing
> + */
> +static void cdns3_ep_inc_enq(struct cdns3_endpoint *priv_ep)
> +{
> +       priv_ep->free_trbs--;
> +       cdns3_ep_inc_trb(&priv_ep->enqueue, &priv_ep->pcs, priv_ep->num_trbs);
> +}
> +
> +/**
> + * cdns3_ep_inc_deq - increment endpoint's dequeue pointer
> + * @priv_ep: The endpoint whose dequeue pointer we're incrementing
> + */
> +static void cdns3_ep_inc_deq(struct cdns3_endpoint *priv_ep)
> +{
> +       priv_ep->free_trbs++;
> +       cdns3_ep_inc_trb(&priv_ep->dequeue, &priv_ep->ccs, priv_ep->num_trbs);
> +}
> +
> +/**
> + * cdns3_allow_enable_l1 - enable/disable permits to transition to L1.
> + * @priv_dev: Extended gadget object
> + * @enable: Enable/disable permit to transition to L1.
> + *
> + * If bit USB_CONF_L1EN is set and device receive Extended Token packet,
> + * then controller answer with ACK handshake.
> + * If bit USB_CONF_L1DS is set and device receive Extended Token packet,
> + * then controller answer with NYET handshake.
> + */
> +void cdns3_allow_enable_l1(struct cdns3_device *priv_dev, int enable)
> +{
> +       if (enable)
> +               writel(USB_CONF_L1EN, &priv_dev->regs->usb_conf);
> +       else
> +               writel(USB_CONF_L1DS, &priv_dev->regs->usb_conf);
> +}
> +
> +enum usb_device_speed cdns3_get_speed(struct cdns3_device *priv_dev)
> +{
> +       u32 reg;
> +
> +       reg = readl(&priv_dev->regs->usb_sts);
> +
> +       if (DEV_SUPERSPEED(reg))
> +               return USB_SPEED_SUPER;
> +       else if (DEV_HIGHSPEED(reg))
> +               return USB_SPEED_HIGH;
> +       else if (DEV_FULLSPEED(reg))
> +               return USB_SPEED_FULL;
> +       else if (DEV_LOWSPEED(reg))
> +               return USB_SPEED_LOW;
> +       return USB_SPEED_UNKNOWN;
> +}
> +
> +/**
> + * cdns3_start_all_request - add to ring all request not started
> + * @priv_dev: Extended gadget object
> + * @priv_ep: The endpoint for whom request will be started.
> + *
> + * Returns return ENOMEM if transfer ring i not enough TRBs to start
> + *         all requests.
> + */
> +static int cdns3_start_all_request(struct cdns3_device *priv_dev,
> +                                  struct cdns3_endpoint *priv_ep)
> +{
> +       struct cdns3_request *priv_req;
> +       struct usb_request *request;
> +       int ret = 0;
> +
> +       while (!list_empty(&priv_ep->deferred_req_list)) {
> +               request = cdns3_next_request(&priv_ep->deferred_req_list);
> +               priv_req = to_cdns3_request(request);
> +
> +               ret = cdns3_ep_run_transfer(priv_ep, request);
> +               if (ret)
> +                       return ret;
> +
> +               list_del(&request->list);
> +               list_add_tail(&request->list,
> +                             &priv_ep->pending_req_list);
> +       }
> +
> +       priv_ep->flags &= ~EP_RING_FULL;
> +       return ret;
> +}
> +
> +/**
> + * cdns3_descmiss_copy_data copy data from internal requests to request queued
> + * by class driver.
> + * @priv_ep: extended endpoint object
> + * @request: request object
> + */
> +static void cdns3_descmiss_copy_data(struct cdns3_endpoint *priv_ep,
> +                                    struct usb_request *request)
> +{
> +       struct usb_request *descmiss_req;
> +       struct cdns3_request *descmiss_priv_req;
> +
> +       while (!list_empty(&priv_ep->descmiss_req_list)) {
> +               int chunk_end;
> +               int length;
> +
> +               descmiss_priv_req =
> +                       cdns3_next_priv_request(&priv_ep->descmiss_req_list);
> +               descmiss_req = &descmiss_priv_req->request;
> +
> +               /* driver can't touch pending request */
> +               if (descmiss_priv_req->flags & REQUEST_PENDING)
> +                       break;
> +
> +               chunk_end = descmiss_priv_req->flags & REQUEST_INTERNAL_CH;
> +               length = request->actual + descmiss_req->actual;
> +
> +               if (length <= request->length) {
> +                       memcpy(&((u8 *)request->buf)[request->actual],
> +                              descmiss_req->buf,
> +                              descmiss_req->actual);
> +                       request->actual = length;
> +               } else {
> +                       /* It should never occures */
> +                       request->status = -ENOMEM;
> +               }
> +
> +               list_del_init(&descmiss_priv_req->list);
> +
> +               kfree(descmiss_req->buf);
> +               cdns3_gadget_ep_free_request(&priv_ep->endpoint, descmiss_req);
> +
> +               if (!chunk_end)
> +                       break;
> +       }
> +}
> +
> +/**
> + * cdns3_gadget_giveback - call struct usb_request's ->complete callback
> + * @priv_ep: The endpoint to whom the request belongs to
> + * @priv_req: The request we're giving back
> + * @status: completion code for the request
> + *
> + * Must be called with controller's lock held and interrupts disabled. This
> + * function will unmap @req and call its ->complete() callback to notify upper
> + * layers that it has completed.
> + */
> +void cdns3_gadget_giveback(struct cdns3_endpoint *priv_ep,
> +                          struct cdns3_request *priv_req,
> +                          int status)
> +{
> +       struct cdns3_device *priv_dev = priv_ep->cdns3_dev;
> +       struct usb_request *request = &priv_req->request;
> +
> +       list_del_init(&request->list);
> +
> +       if (request->status == -EINPROGRESS)
> +               request->status = status;
> +
> +       usb_gadget_unmap_request_by_dev(priv_dev->sysdev, request,
> +                                       priv_ep->dir);
> +
> +       priv_req->flags &= ~REQUEST_PENDING;
> +       trace_cdns3_gadget_giveback(priv_req);
> +
> +       /* WA1: */
> +       if (priv_ep->flags & EP_QUIRK_EXTRA_BUF_EN &&
> +           priv_req->flags & REQUEST_INTERNAL) {
> +               struct usb_request *req;
> +
> +               req = cdns3_next_request(&priv_ep->deferred_req_list);
> +               request = req;
> +               priv_ep->descmis_req = NULL;
> +
> +               if (!req)
> +                       return;
> +
> +               cdns3_descmiss_copy_data(priv_ep, req);
> +               if (!(priv_ep->flags & EP_QUIRK_END_TRANSFER) &&
> +                   req->length != req->actual) {
> +                       /* wait for next part of transfer */
> +                       return;
> +               }
> +
> +               if (req->status == -EINPROGRESS)
> +                       req->status = 0;
> +
> +               list_del_init(&req->list);
> +               cdns3_start_all_request(priv_dev, priv_ep);
> +       }
> +
> +       /* Start all not pending request */
> +       if (priv_ep->flags & EP_RING_FULL)
> +               cdns3_start_all_request(priv_dev, priv_ep);
> +
> +       if (request->complete) {
> +               spin_unlock(&priv_dev->lock);
> +               usb_gadget_giveback_request(&priv_ep->endpoint,
> +                                           request);
> +               spin_lock(&priv_dev->lock);
> +       }
> +
> +       if (request->buf == priv_dev->zlp_buf)
> +               cdns3_gadget_ep_free_request(&priv_ep->endpoint, request);
> +}
> +
> +/**
> + * cdns3_ep_run_transfer - start transfer on no-default endpoint hardware
> + * @priv_ep: endpoint object
> + *
> + * Returns zero on success or negative value on failure
> + */
> +int cdns3_ep_run_transfer(struct cdns3_endpoint *priv_ep,
> +                         struct usb_request *request)
> +{
> +       struct cdns3_device *priv_dev = priv_ep->cdns3_dev;
> +       struct cdns3_request *priv_req;
> +       struct cdns3_trb *trb;
> +       dma_addr_t trb_dma;
> +       int sg_iter = 0;
> +       u32 first_pcs;
> +       int  num_trb;
> +       int address;
> +       int pcs;
> +
> +       if (priv_ep->type == USB_ENDPOINT_XFER_ISOC)
> +               num_trb = priv_ep->interval;
> +       else
> +               num_trb = request->num_sgs ? request->num_sgs : 1;
> +
> +       if (num_trb > priv_ep->free_trbs) {
> +               priv_ep->flags |= EP_RING_FULL;
> +               return -ENOBUFS;
> +       }
> +
> +       priv_req = to_cdns3_request(request);
> +       address = priv_ep->endpoint.desc->bEndpointAddress;
> +
> +       priv_ep->flags |= EP_PENDING_REQUEST;
> +       trb_dma = request->dma;
> +
> +       /* must allocate buffer aligned to 8 */
> +       if ((request->dma % 8)) {
> +               if (request->length <= CDNS3_ALIGNED_BUF_SIZE) {
> +                       memcpy(priv_ep->aligned_buff, request->buf,
> +                              request->length);
> +                       trb_dma = priv_ep->aligned_dma_addr;
> +               } else {
> +                       return -ENOMEM;
> +               }
> +       }
> +
> +       trb = priv_ep->trb_pool + priv_ep->enqueue;
> +       priv_req->start_trb = priv_ep->enqueue;
> +       priv_req->trb = trb;
> +
> +       /* prepare ring */
> +       if ((priv_ep->enqueue + num_trb)  >= (priv_ep->num_trbs - 1)) {
> +               /*updating C bt in  Link TRB before starting DMA*/
> +               struct cdns3_trb *link_trb = priv_ep->trb_pool +
> +                                            (priv_ep->num_trbs - 1);
> +               link_trb->control = ((priv_ep->pcs) ? TRB_CYCLE : 0) |
> +                                   TRB_TYPE(TRB_LINK) | TRB_CHAIN |
> +                                   TRB_TOGGLE;
> +       }
> +
> +       first_pcs = priv_ep->pcs ? TRB_CYCLE : 0;
> +
> +       do {
> +       /* fill TRB */
> +               trb->buffer = TRB_BUFFER(request->num_sgs == 0
> +                               ? trb_dma : request->sg[sg_iter].dma_address);
> +
> +               trb->length = TRB_BURST_LEN(16) |
> +                   TRB_LEN(request->num_sgs == 0 ?
> +                               request->length : request->sg[sg_iter].length);
> +
> +               trb->control = TRB_TYPE(TRB_NORMAL);
> +               pcs = priv_ep->pcs ? TRB_CYCLE : 0;
> +
> +               /*
> +                * first trb should be prepared as last to avoid processing
> +                *  transfer to early
> +                */
> +               if (sg_iter != 0)
> +                       trb->control |= pcs;
> +
> +               if (priv_ep->type == USB_ENDPOINT_XFER_ISOC  && !priv_ep->dir) {
> +                       trb->control |= TRB_IOC | TRB_ISP;
> +               } else {
> +                       /* for last element in TD or in SG list */
> +                       if (sg_iter == (num_trb - 1) && sg_iter != 0)
> +                               trb->control |= pcs | TRB_IOC | TRB_ISP;
> +               }
> +               ++sg_iter;
> +               priv_req->end_trb = priv_ep->enqueue;
> +               cdns3_ep_inc_enq(priv_ep);
> +               trb = priv_ep->trb_pool + priv_ep->enqueue;
> +       } while (sg_iter < num_trb);
> +
> +       trb = priv_req->trb;
> +
> +       /*
> +        * Memory barrier = Cycle Bit must be set before trb->length  and
> +        * trb->buffer fields.
> +        */
> +       wmb();
> +
> +       priv_req->flags |= REQUEST_PENDING;
> +
> +       /* give the TD to the consumer*/
> +       if (sg_iter == 1)
> +               trb->control |= first_pcs | TRB_IOC | TRB_ISP;
> +       else
> +               trb->control |= first_pcs;
> +
> +       trace_cdns3_prepare_trb(priv_ep, priv_req->trb);
> +       trace_cdns3_ring(priv_ep);
> +
> +       /* arm transfer on selected endpoint */
> +       cdns3_select_ep(priv_ep->cdns3_dev, address);
> +
> +       /*
> +        * For DMULT mode we can set address to transfer ring only once after
> +        * enabling endpoint.
> +        */
> +       if (priv_ep->flags & EP_UPDATE_EP_TRBADDR) {
> +               writel(EP_TRADDR_TRADDR(priv_ep->trb_pool_dma),
> +                      &priv_dev->regs->ep_traddr);
> +               priv_ep->flags &= ~EP_UPDATE_EP_TRBADDR;
> +       }
> +
> +       /*clearing TRBERR and EP_STS_DESCMIS before seting DRDY*/
> +       writel(EP_STS_TRBERR | EP_STS_DESCMIS, &priv_dev->regs->ep_sts);
> +       trace_cdns3_doorbell_epx(priv_ep->name,
> +                                readl(&priv_dev->regs->ep_traddr));
> +       writel(EP_CMD_DRDY, &priv_dev->regs->ep_cmd);
> +
> +       return 0;
> +}
> +
> +void cdns3_set_hw_configuration(struct cdns3_device *priv_dev)
> +{
> +       struct cdns3_endpoint *priv_ep;
> +       struct usb_ep *ep;
> +       int result = 0;
> +
> +       if (priv_dev->hw_configured_flag)
> +               return;
> +
> +       writel(USB_CONF_CFGSET, &priv_dev->regs->usb_conf);
> +       writel(EP_CMD_ERDY | EP_CMD_REQ_CMPL, &priv_dev->regs->ep_cmd);
> +
> +       cdns3_set_register_bit(&priv_dev->regs->usb_conf,
> +                              USB_CONF_U1EN | USB_CONF_U2EN);
> +
> +       /* wait until configuration set */
> +       result = cdns3_handshake(&priv_dev->regs->usb_sts,
> +                                USB_STS_CFGSTS_MASK, 1, 100);
> +
> +       priv_dev->hw_configured_flag = 1;
> +       cdns3_allow_enable_l1(priv_dev, 1);
> +
> +       list_for_each_entry(ep, &priv_dev->gadget.ep_list, ep_list) {
> +               if (ep->enabled) {
> +                       priv_ep = ep_to_cdns3_ep(ep);
> +                       cdns3_start_all_request(priv_dev, priv_ep);
> +               }
> +       }
> +}
> +
> +/**
> + * cdns3_request_handled - check whether request has been handled by DMA
> + *
> + * @priv_ep: extended endpoint object.
> + * @priv_req: request object for checking
> + *
> + * Endpoint must be selected before invoking this function.
> + *
> + * Returns false if request has not been handled by DMA, else returns true.
> + *
> + * SR - start ring
> + * ER -  end ring
> + * DQ = priv_ep->dequeue - dequeue position
> + * EQ = priv_ep->enqueue -  enqueue position
> + * ST = priv_req->start_trb - index of first TRB in transfer ring
> + * ET = priv_req->end_trb - index of last TRB in transfer ring
> + * CI = current_index - index of processed TRB by DMA.
> + *
> + * As first step, function checks if cycle bit for priv_req->start_trb is
> + * correct.
> + *
> + * some rules:
> + * 1. priv_ep->dequeue never exceed current_index.
> + * 2  priv_ep->enqueue never exceed priv_ep->dequeue
> + *
> + * Then We can split recognition into two parts:
> + * Case 1 - priv_ep->dequeue < current_index
> + *      SR ... EQ ... DQ ... CI ... ER
> + *      SR ... DQ ... CI ... EQ ... ER
> + *
> + *      Request has been handled by DMA if ST and ET is between DQ and CI.
> + *
> + * Case 2 - priv_ep->dequeue > current_index
> + * This situation take place when CI go through the LINK TRB at the end of
> + * transfer ring.
> + *      SR ... CI ... EQ ... DQ ... ER
> + *
> + *      Request has been handled by DMA if ET is less then CI or
> + *      ET is greater or equal DQ.
> + */
> +static bool cdns3_request_handled(struct cdns3_endpoint *priv_ep,
> +                                 struct cdns3_request *priv_req)
> +{
> +       struct cdns3_device *priv_dev = priv_ep->cdns3_dev;
> +       struct cdns3_trb *trb = priv_req->trb;
> +       int current_index = 0;
> +       int handled = 0;
> +
> +       current_index = (readl(&priv_dev->regs->ep_traddr) -
> +                        priv_ep->trb_pool_dma) / TRB_SIZE;
> +
> +       trb = &priv_ep->trb_pool[priv_req->start_trb];
> +
> +       if ((trb->control  & TRB_CYCLE) != priv_ep->ccs)
> +               goto finish;
> +
> +       if (priv_ep->dequeue < current_index) {
> +               if ((current_index == (priv_ep->num_trbs - 1)) &&
> +                   !priv_ep->dequeue)
> +                       goto finish;
> +
> +               if (priv_req->end_trb >= priv_ep->dequeue &&
> +                   priv_req->end_trb < current_index)
> +                       handled = 1;
> +       } else if (priv_ep->dequeue  > current_index) {
> +               if (priv_req->end_trb  < current_index ||
> +                   priv_req->end_trb >= priv_ep->dequeue)
> +                       handled = 1;
> +       }
> +
> +finish:
> +       trace_cdns3_request_handled(priv_req, current_index, handled);
> +
> +       return handled;
> +}
> +
> +static void cdns3_transfer_completed(struct cdns3_device *priv_dev,
> +                                    struct cdns3_endpoint *priv_ep)
> +{
> +       struct cdns3_request *priv_req;
> +       struct usb_request *request;
> +       struct cdns3_trb *trb;
> +       int current_trb;
> +
> +       while (!list_empty(&priv_ep->pending_req_list)) {
> +               request = cdns3_next_request(&priv_ep->pending_req_list);
> +               priv_req = to_cdns3_request(request);
> +
> +               if (!cdns3_request_handled(priv_ep, priv_req))
> +                       return;
> +
> +               if (request->dma % 8 && priv_ep->dir == USB_DIR_OUT)
> +                       memcpy(request->buf, priv_ep->aligned_buff,
> +                              request->length);
> +
> +               trb = priv_ep->trb_pool + priv_ep->dequeue;
> +               trace_cdns3_complete_trb(priv_ep, trb);
> +
> +               if (trb != priv_req->trb)
> +                       dev_warn(priv_dev->dev,
> +                                "request_trb=0x%p, queue_trb=0x%p\n",
> +                                priv_req->trb, trb);
> +
> +               request->actual = TRB_LEN(le32_to_cpu(trb->length));
> +               current_trb = priv_req->start_trb;
> +
> +               while (current_trb != priv_req->end_trb) {
> +                       cdns3_ep_inc_deq(priv_ep);
> +                       current_trb = priv_ep->dequeue;
> +               }
> +
> +               cdns3_ep_inc_deq(priv_ep);
> +               cdns3_gadget_giveback(priv_ep, priv_req, 0);
> +       }
> +       priv_ep->flags &= ~EP_PENDING_REQUEST;
> +}
> +
> +/**
> + * cdns3_descmissing_packet - handles descriptor missing event.
> + * @priv_dev: extended gadget object
> + *
> + * WA1: Controller for OUT endpoints has shared on-chip buffers for all incoming
> + * packets, including ep0out. It's FIFO buffer, so packets must be handle by DMA
> + * in correct order. If the first packet in the buffer will not be handled,
> + * then the following packets directed for other endpoints and  functions
> + * will be blocked.
> + * Additionally the packets directed to one endpoint can block entire on-chip
> + * buffers. In this case transfer to other endpoints also will blocked.
> + *
> + * To resolve this issue after raising the descriptor missing interrupt
> + * driver prepares internal usb_request object and use it to arm DMA transfer.
> + *
> + * The problematic situation was observed in case when endpoint has been enabled
> + * but no usb_request were queued. Driver try detects such endpoints and will
> + * use this workaround only for these endpoint.
> + *
> + * Driver use limited number of buffer. This number can be set by macro
> + * CDNS_WA1_NUM_BUFFERS.
> + *
> + * Such blocking situation was observed on ACM gadget. For this function
> + * host send OUT data packet but ACM function is not prepared for this packet.
> + *
> + * It's limitation of controller but maybe this issues should be fixed in
> + * function driver.
> + */
> +static int cdns3_descmissing_packet(struct cdns3_endpoint *priv_ep)
> +{
> +       struct cdns3_request *priv_req;
> +       struct usb_request *request;
> +
> +       if (priv_ep->flags & EP_QUIRK_EXTRA_BUF_DET) {
> +               priv_ep->flags &= ~EP_QUIRK_EXTRA_BUF_DET;
> +               priv_ep->flags |= EP_QUIRK_EXTRA_BUF_EN;
> +       }
> +
> +       request = cdns3_gadget_ep_alloc_request(&priv_ep->endpoint,
> +                                               GFP_ATOMIC);
> +       if (!request)
> +               return -ENOMEM;
> +
> +       priv_req = to_cdns3_request(request);
> +       priv_req->flags |= REQUEST_INTERNAL;
> +
> +       /* if this field is still assigned it indicate that transfer related
> +        * with this request has not been finished yet. Driver in this
> +        * case simply allocate next request and assign flag REQUEST_INTERNAL_CH
> +        * flag to previous one. It will indicate that current request is
> +        * part of the previous one.
> +        */
> +       if (priv_ep->descmis_req)
> +               priv_ep->descmis_req->flags |= REQUEST_INTERNAL_CH;
> +
> +       priv_req->request.buf = kzalloc(CDNS3_DESCMIS_BUF_SIZE,
> +                                       GFP_ATOMIC);
> +       if (!priv_req) {
> +               cdns3_gadget_ep_free_request(&priv_ep->endpoint, request);
> +               return -ENOMEM;
> +       }
> +
> +       priv_req->request.length = CDNS3_DESCMIS_BUF_SIZE;
> +       priv_ep->descmis_req = priv_req;
> +
> +       __cdns3_gadget_ep_queue(&priv_ep->endpoint,
> +                               &priv_ep->descmis_req->request,
> +                               GFP_ATOMIC);
> +
> +       return 0;
> +}
> +
> +/**
> + * cdns3_check_ep_interrupt_proceed - Processes interrupt related to endpoint
> + * @priv_ep: endpoint object
> + *
> + * Returns 0
> + */
> +static int cdns3_check_ep_interrupt_proceed(struct cdns3_endpoint *priv_ep)
> +{
> +       struct cdns3_device *priv_dev = priv_ep->cdns3_dev;
> +       u32 ep_sts_reg;
> +
> +       cdns3_select_ep(priv_dev, priv_ep->endpoint.address);
> +
> +       trace_cdns3_epx_irq(priv_dev, priv_ep);
> +
> +       ep_sts_reg = readl(&priv_dev->regs->ep_sts);
> +       writel(ep_sts_reg, &priv_dev->regs->ep_sts);
> +
> +       if ((ep_sts_reg & EP_STS_IOC) || (ep_sts_reg & EP_STS_ISP)) {
> +               if (priv_ep->flags & EP_QUIRK_EXTRA_BUF_EN) {
> +                       if (ep_sts_reg & EP_STS_ISP)
> +                               priv_ep->flags |= EP_QUIRK_END_TRANSFER;
> +                       else
> +                               priv_ep->flags &= ~EP_QUIRK_END_TRANSFER;
> +               }
> +               cdns3_transfer_completed(priv_dev, priv_ep);
> +       }
> +
> +       /*
> +        * For isochronous transfer driver completes request on IOC or on
> +        * TRBERR. IOC appears only when device receive OUT data packet.
> +        * If host disable stream or lost some packet then the only way to
> +        * finish all queued transfer is to do it on TRBERR event.
> +        */
> +       if ((ep_sts_reg & EP_STS_TRBERR) &&
> +           priv_ep->type == USB_ENDPOINT_XFER_ISOC)
> +               cdns3_transfer_completed(priv_dev, priv_ep);
> +
> +       /*
> +        * WA1: this condition should only be meet when
> +        * priv_ep->flags & EP_QUIRK_EXTRA_BUF_DET or
> +        * priv_ep->flags & EP_QUIRK_EXTRA_BUF_EN.
> +        * In other cases this interrupt will be disabled/
> +        */
> +       if (ep_sts_reg & EP_STS_DESCMIS) {
> +               int err;
> +
> +               err = cdns3_descmissing_packet(priv_ep);
> +               if (err)
> +                       dev_err(priv_dev->dev,
> +                               "Failed: No sufficient memory for DESCMIS\n");
> +       }
> +
> +       return 0;
> +}
> +
> +/**
> + * cdns3_check_usb_interrupt_proceed - Processes interrupt related to device
> + * @priv_dev: extended gadget object
> + * @usb_ists: bitmap representation of device's reported interrupts
> + * (usb_ists register value)
> + */
> +static void cdns3_check_usb_interrupt_proceed(struct cdns3_device *priv_dev,
> +                                             u32 usb_ists)
> +{
> +       int speed = 0;
> +
> +       trace_cdns3_usb_irq(priv_dev, usb_ists);
> +       /* Connection detected */
> +       if (usb_ists & (USB_ISTS_CON2I | USB_ISTS_CONI)) {
> +               speed = cdns3_get_speed(priv_dev);
> +               priv_dev->gadget.speed = speed;
> +               usb_gadget_set_state(&priv_dev->gadget, USB_STATE_POWERED);
> +               cdns3_ep0_config(priv_dev);
> +       }
> +
> +       /* Disconnection detected */
> +       if (usb_ists & (USB_ISTS_DIS2I | USB_ISTS_DISI)) {
> +               if (priv_dev->gadget_driver &&
> +                   priv_dev->gadget_driver->disconnect) {
> +                       spin_unlock(&priv_dev->lock);
> +                       priv_dev->gadget_driver->disconnect(&priv_dev->gadget);
> +                       spin_lock(&priv_dev->lock);
> +               }
> +
> +               priv_dev->gadget.speed = USB_SPEED_UNKNOWN;
> +               usb_gadget_set_state(&priv_dev->gadget, USB_STATE_NOTATTACHED);
> +               cdns3_hw_reset_eps_config(priv_dev);
> +       }
> +
> +       /* reset*/
> +       if (usb_ists & (USB_ISTS_UWRESI | USB_ISTS_UHRESI | USB_ISTS_U2RESI)) {
> +               /*read again to check the actuall speed*/
> +               speed = cdns3_get_speed(priv_dev);
> +               usb_gadget_set_state(&priv_dev->gadget, USB_STATE_DEFAULT);
> +               priv_dev->gadget.speed = speed;
> +               cdns3_hw_reset_eps_config(priv_dev);
> +               cdns3_ep0_config(priv_dev);
> +       }
> +}
> +
> +/**
> + * cdns3_device_irq_handler- interrupt handler for device part of controller
> + *
> + * @irq: irq number for cdns3 core device
> + * @data: structure of cdns3
> + *
> + * Returns IRQ_HANDLED or IRQ_NONE
> + */
> +static irqreturn_t cdns3_device_irq_handler(int irq, void *data)
> +{
> +       struct cdns3_device *priv_dev;
> +       struct cdns3 *cdns = data;
> +       irqreturn_t ret = IRQ_NONE;
> +       unsigned long flags;
> +       u32 reg;
> +
> +       priv_dev = cdns->gadget_dev;
> +       spin_lock_irqsave(&priv_dev->lock, flags);
> +
> +       /* check USB device interrupt */
> +       reg = readl(&priv_dev->regs->usb_ists);
> +       writel(reg, &priv_dev->regs->usb_ists);
> +
> +       if (reg) {
> +               cdns3_check_usb_interrupt_proceed(priv_dev, reg);
> +               ret = IRQ_HANDLED;
> +       }
> +
> +       /* check endpoint interrupt */
> +       reg = readl(&priv_dev->regs->ep_ists);
> +
> +       if (reg) {
> +               reg = ~reg & readl(&priv_dev->regs->ep_ien);
> +               /* mask deferred interrupt. */
> +               writel(reg, &priv_dev->regs->ep_ien);
> +               ret = IRQ_WAKE_THREAD;
> +       }
> +
> +       spin_unlock_irqrestore(&priv_dev->lock, flags);
> +       return ret;
> +}
> +
> +/**
> + * cdns3_device_thread_irq_handler- interrupt handler for device part
> + * of controller
> + *
> + * @irq: irq number for cdns3 core device
> + * @data: structure of cdns3
> + *
> + * Returns IRQ_HANDLED or IRQ_NONE
> + */
> +static irqreturn_t cdns3_device_thread_irq_handler(int irq, void *data)
> +{
> +       struct cdns3_device *priv_dev;
> +       struct cdns3 *cdns = data;
> +       irqreturn_t ret = IRQ_NONE;
> +       unsigned long flags;
> +       u32 ep_ien;
> +       int bit;
> +       u32 reg;
> +
> +       priv_dev = cdns->gadget_dev;
> +       spin_lock_irqsave(&priv_dev->lock, flags);
> +
> +       reg = readl(&priv_dev->regs->ep_ists);
> +       ep_ien = reg;
> +
> +       /* handle default endpoint OUT */
> +       if (reg & EP_ISTS_EP_OUT0) {
> +               cdns3_check_ep0_interrupt_proceed(priv_dev, USB_DIR_OUT);
> +               ret = IRQ_HANDLED;
> +       }
> +
> +       /* handle default endpoint IN */
> +       if (reg & EP_ISTS_EP_IN0) {
> +               cdns3_check_ep0_interrupt_proceed(priv_dev, USB_DIR_IN);
> +               ret = IRQ_HANDLED;
> +       }
> +
> +       /* check if interrupt from non default endpoint, if no exit */
> +       reg &= ~(EP_ISTS_EP_OUT0 | EP_ISTS_EP_IN0);
> +       if (!reg)
> +               goto irqend;
> +
> +       for_each_set_bit(bit, (unsigned long *)&reg,
> +                        sizeof(u32) * BITS_PER_BYTE) {
> +               cdns3_check_ep_interrupt_proceed(priv_dev->eps[bit]);
> +               ret = IRQ_HANDLED;
> +       }
> +
> +irqend:
> +       ep_ien |= readl(&priv_dev->regs->ep_ien);
> +       /* Unmask all handled EP interrupts */
> +       writel(ep_ien, &priv_dev->regs->ep_ien);
> +       spin_unlock_irqrestore(&priv_dev->lock, flags);
> +       return ret;
> +}
> +
> +/**
> + * cdns3_ep_onchip_buffer_reserve - Try to reserve onchip buf for EP
> + *
> + * The real reservation will occur during write to EP_CFG register,
> + * this function is used to check if the 'size' reservation is allowed.
> + *
> + * @priv_dev: extended gadget object
> + * @size: the size (KB) for EP would like to allocate
> + *
> + * Return 0 if the required size can met or negative value on failure
> + */
> +static int cdns3_ep_onchip_buffer_reserve(struct cdns3_device *priv_dev,
> +                                         int size)
> +{
> +       u32 onchip_mem;
> +
> +       priv_dev->onchip_mem_allocated_size += size;
> +
> +       onchip_mem = USB_CAP2_ACTUAL_MEM_SIZE(readl(&priv_dev->regs->usb_cap2));
> +       if (!onchip_mem)
> +               onchip_mem = 256;
> +
> +       /* 2KB is reserved for EP0*/
> +       onchip_mem -= 2;
> +       if (priv_dev->onchip_mem_allocated_size > onchip_mem) {
> +               priv_dev->onchip_mem_allocated_size -= size;
> +               return -EPERM;
> +       }
> +
> +       return 0;
> +}
> +
> +/**
> + * cdns3_ep_config Configure hardware endpoint
> + * @priv_ep: extended endpoint object
> + */
> +void cdns3_ep_config(struct cdns3_endpoint *priv_ep)
> +{
> +       bool is_iso_ep = (priv_ep->type == USB_ENDPOINT_XFER_ISOC);
> +       struct cdns3_device *priv_dev = priv_ep->cdns3_dev;
> +       u32 bEndpointAddress = priv_ep->num | priv_ep->dir;
> +       u32 max_packet_size = 0;
> +       u32 ep_cfg = 0;
> +       int ret;
> +
> +       switch (priv_ep->type) {
> +       case USB_ENDPOINT_XFER_INT:
> +               ep_cfg = EP_CFG_EPTYPE(USB_ENDPOINT_XFER_INT);
> +               break;
> +       case USB_ENDPOINT_XFER_BULK:
> +               ep_cfg = EP_CFG_EPTYPE(USB_ENDPOINT_XFER_BULK);
> +               break;
> +       default:
> +               ep_cfg = EP_CFG_EPTYPE(USB_ENDPOINT_XFER_ISOC);
> +       }
> +
> +       switch (priv_dev->gadget.speed) {
> +       case USB_SPEED_FULL:
> +               max_packet_size = is_iso_ep ? 1023 : 64;
> +               break;
> +       case USB_SPEED_HIGH:
> +               max_packet_size = is_iso_ep ? 1024 : 512;
> +               break;
> +       case USB_SPEED_SUPER:
> +               max_packet_size = 1024;
> +               break;
> +       default:
> +               /* all other speed are not supported */
> +               return;
> +       }
> +
> +       ret = cdns3_ep_onchip_buffer_reserve(priv_dev, CDNS3_EP_BUF_SIZE);
> +       if (ret) {
> +               dev_err(priv_dev->dev, "onchip mem is full, ep is invalid\n");
> +               return;
> +       }
> +
> +       ep_cfg |= EP_CFG_MAXPKTSIZE(max_packet_size) |
> +                 EP_CFG_BUFFERING(CDNS3_EP_BUF_SIZE - 1) |
> +                 EP_CFG_MAXBURST(priv_ep->endpoint.maxburst);
> +
> +       cdns3_select_ep(priv_dev, bEndpointAddress);
> +       writel(ep_cfg, &priv_dev->regs->ep_cfg);
> +
> +       dev_dbg(priv_dev->dev, "Configure %s: with val %08x\n",
> +               priv_ep->name, ep_cfg);
> +}
> +
> +/* Find correct direction for HW endpoint according to description */
> +static int cdns3_ep_dir_is_correct(struct usb_endpoint_descriptor *desc,
> +                                  struct cdns3_endpoint *priv_ep)
> +{
> +       return (priv_ep->endpoint.caps.dir_in && usb_endpoint_dir_in(desc)) ||
> +              (priv_ep->endpoint.caps.dir_out && usb_endpoint_dir_out(desc));
> +}
> +
> +static struct
> +cdns3_endpoint *cdns3_find_available_ep(struct cdns3_device *priv_dev,
> +                                       struct usb_endpoint_descriptor *desc)
> +{
> +       struct usb_ep *ep;
> +       struct cdns3_endpoint *priv_ep;
> +
> +       list_for_each_entry(ep, &priv_dev->gadget.ep_list, ep_list) {
> +               unsigned long num;
> +               int ret;
> +               /* ep name pattern likes epXin or epXout */
> +               char c[2] = {ep->name[2], '\0'};
> +
> +               ret = kstrtoul(c, 10, &num);
> +               if (ret)
> +                       return ERR_PTR(ret);
> +
> +               priv_ep = ep_to_cdns3_ep(ep);
> +               if (cdns3_ep_dir_is_correct(desc, priv_ep)) {
> +                       if (!(priv_ep->flags & EP_CLAIMED)) {
> +                               priv_ep->num  = num;
> +                               return priv_ep;
> +                       }
> +               }
> +       }
> +
> +       return ERR_PTR(-ENOENT);
> +}
> +
> +/*
> + *  Cadence IP has one limitation that all endpoints must be configured
> + * (Type & MaxPacketSize) before setting configuration through hardware
> + * register, it means we can't change endpoints configuration after
> + * set_configuration.
> + *
> + * This function set EP_CLAIMED flag which is added when the gadget driver
> + * uses usb_ep_autoconfig to configure specific endpoint;
> + * When the udc driver receives set_configurion request,
> + * it goes through all claimed endpoints, and configure all endpoints
> + * accordingly.
> + *
> + * At usb_ep_ops.enable/disable, we only enable and disable endpoint through
> + * ep_cfg register which can be changed after set_configuration, and do
> + * some software operation accordingly.
> + */
> +static struct
> +usb_ep *cdns3_gadget_match_ep(struct usb_gadget *gadget,
> +                             struct usb_endpoint_descriptor *desc,
> +                             struct usb_ss_ep_comp_descriptor *comp_desc)
> +{
> +       struct cdns3_device *priv_dev = gadget_to_cdns3_device(gadget);
> +       struct cdns3_endpoint *priv_ep;
> +       unsigned long flags;
> +
> +       priv_ep = cdns3_find_available_ep(priv_dev, desc);
> +       if (IS_ERR(priv_ep)) {
> +               dev_err(priv_dev->dev, "no available ep\n");
> +               return NULL;
> +       }
> +
> +       dev_dbg(priv_dev->dev, "match endpoint: %s\n", priv_ep->name);
> +
> +       spin_lock_irqsave(&priv_dev->lock, flags);
> +       priv_ep->endpoint.desc = desc;
> +       priv_ep->dir  = usb_endpoint_dir_in(desc) ? USB_DIR_IN : USB_DIR_OUT;
> +       priv_ep->type = usb_endpoint_type(desc);
> +       priv_ep->flags |= EP_CLAIMED;
> +       spin_unlock_irqrestore(&priv_dev->lock, flags);
> +       return &priv_ep->endpoint;
> +}
> +
> +/**
> + * cdns3_gadget_ep_alloc_request Allocates request
> + * @ep: endpoint object associated with request
> + * @gfp_flags: gfp flags
> + *
> + * Returns allocated request address, NULL on allocation error
> + */
> +struct usb_request *cdns3_gadget_ep_alloc_request(struct usb_ep *ep,
> +                                                 gfp_t gfp_flags)
> +{
> +       struct cdns3_endpoint *priv_ep = ep_to_cdns3_ep(ep);
> +       struct cdns3_request *priv_req;
> +
> +       priv_req = kzalloc(sizeof(*priv_req), gfp_flags);
> +       if (!priv_req)
> +               return NULL;
> +
> +       priv_req->priv_ep = priv_ep;
> +
> +       trace_cdns3_alloc_request(priv_req);
> +       return &priv_req->request;
> +}
> +
> +/**
> + * cdns3_gadget_ep_free_request Free memory occupied by request
> + * @ep: endpoint object associated with request
> + * @request: request to free memory
> + */
> +void cdns3_gadget_ep_free_request(struct usb_ep *ep,
> +                                 struct usb_request *request)
> +{
> +       struct cdns3_request *priv_req = to_cdns3_request(request);
> +
> +       trace_cdns3_free_request(priv_req);
> +       kfree(priv_req);
> +}
> +
> +/**
> + * cdns3_gadget_ep_enable Enable endpoint
> + * @ep: endpoint object
> + * @desc: endpoint descriptor
> + *
> + * Returns 0 on success, error code elsewhere
> + */
> +static int cdns3_gadget_ep_enable(struct usb_ep *ep,
> +                                 const struct usb_endpoint_descriptor *desc)
> +{
> +       struct cdns3_endpoint *priv_ep;
> +       struct cdns3_device *priv_dev;
> +       u32 reg = EP_STS_EN_TRBERREN;
> +       u32 bEndpointAddress;
> +       unsigned long flags;
> +       int ret;
> +
> +       priv_ep = ep_to_cdns3_ep(ep);
> +       priv_dev = priv_ep->cdns3_dev;
> +
> +       if (!ep || !desc || desc->bDescriptorType != USB_DT_ENDPOINT) {
> +               dev_dbg(priv_dev->dev, "usbss: invalid parameters\n");
> +               return -EINVAL;
> +       }
> +
> +       if (!desc->wMaxPacketSize) {
> +               dev_err(priv_dev->dev, "usbss: missing wMaxPacketSize\n");
> +               return -EINVAL;
> +       }
> +
> +       if (dev_WARN_ONCE(priv_dev->dev, priv_ep->flags & EP_ENABLED,
> +                         "%s is already enabled\n", priv_ep->name))
> +               return 0;
> +
> +       spin_lock_irqsave(&priv_dev->lock, flags);
> +
> +       priv_ep->endpoint.desc = desc;
> +       priv_ep->type = usb_endpoint_type(desc);
> +       priv_ep->interval = desc->bInterval ? BIT(desc->bInterval - 1) : 0;
> +
> +       if (priv_ep->interval > ISO_MAX_INTERVAL &&
> +           priv_ep->type == USB_ENDPOINT_XFER_ISOC) {
> +               dev_err(priv_dev->dev, "Driver is limited to %d period\n",
> +                       ISO_MAX_INTERVAL);
> +
> +               ret =  -EINVAL;
> +               goto exit;
> +       }
> +
> +       ret = cdns3_allocate_trb_pool(priv_ep);
> +
> +       if (ret)
> +               goto exit;
> +
> +       bEndpointAddress = priv_ep->num | priv_ep->dir;
> +       cdns3_select_ep(priv_dev, bEndpointAddress);
> +
> +       trace_cdns3_gadget_ep_enable(priv_ep);
> +
> +       writel(EP_CMD_EPRST, &priv_dev->regs->ep_cmd);
> +
> +       ret = cdns3_handshake(&priv_dev->regs->ep_cmd,
> +                             EP_CMD_CSTALL | EP_CMD_EPRST, 0, 100);
> +
> +       /* enable interrupt for selected endpoint */
> +       cdns3_set_register_bit(&priv_dev->regs->ep_ien,
> +                              BIT(cdns3_ep_addr_to_index(bEndpointAddress)));
> +       /*
> +        * WA1: Set flag for all not ISOC OUT endpoints. If this flag is set
> +        * driver try to detect whether endpoint need additional internal
> +        * buffer for unblocking on-chip FIFO buffer. This flag will be cleared
> +        * if before first DESCMISS interrupt the DMA will be armed.
> +        */
> +       if (!priv_ep->dir && priv_ep->type != USB_ENDPOINT_XFER_ISOC) {
> +               priv_ep->flags |= EP_QUIRK_EXTRA_BUF_DET;
> +               reg |= EP_STS_EN_DESCMISEN;
> +       }
> +
> +       writel(reg, &priv_dev->regs->ep_sts_en);
> +
> +       cdns3_set_register_bit(&priv_dev->regs->ep_cfg, EP_CFG_ENABLE);
> +
> +       ep->desc = desc;
> +       priv_ep->flags &= ~(EP_PENDING_REQUEST | EP_STALL |
> +                           EP_QUIRK_EXTRA_BUF_EN);
> +       priv_ep->flags |= EP_ENABLED | EP_UPDATE_EP_TRBADDR;
> +       priv_ep->enqueue = 0;
> +       priv_ep->dequeue = 0;
> +       reg = readl(&priv_dev->regs->ep_sts);
> +       priv_ep->pcs = !!EP_STS_CCS(reg);
> +       priv_ep->ccs = !!EP_STS_CCS(reg);
> +       /* one TRB is reserved for link TRB used in DMULT mode*/
> +       priv_ep->free_trbs = priv_ep->num_trbs - 1;
> +exit:
> +       spin_unlock_irqrestore(&priv_dev->lock, flags);
> +
> +       return ret;
> +}
> +
> +/**
> + * cdns3_gadget_ep_disable Disable endpoint
> + * @ep: endpoint object
> + *
> + * Returns 0 on success, error code elsewhere
> + */
> +static int cdns3_gadget_ep_disable(struct usb_ep *ep)
> +{
> +       struct cdns3_endpoint *priv_ep;
> +       struct cdns3_request *priv_req;
> +       struct cdns3_device *priv_dev;
> +       struct usb_request *request;
> +       unsigned long flags;
> +       int ret = 0;
> +       u32 ep_cfg;
> +
> +       if (!ep) {
> +               pr_err("usbss: invalid parameters\n");
> +               return -EINVAL;
> +       }
> +
> +       priv_ep = ep_to_cdns3_ep(ep);
> +       priv_dev = priv_ep->cdns3_dev;
> +
> +       if (dev_WARN_ONCE(priv_dev->dev, !(priv_ep->flags & EP_ENABLED),
> +                         "%s is already disabled\n", priv_ep->name))
> +               return 0;
> +
> +       spin_lock_irqsave(&priv_dev->lock, flags);
> +
> +       trace_cdns3_gadget_ep_disable(priv_ep);
> +
> +       cdns3_select_ep(priv_dev, ep->desc->bEndpointAddress);
> +       ret = cdns3_data_flush(priv_ep);
> +
> +       ep_cfg = readl(&priv_dev->regs->ep_cfg);
> +       ep_cfg &= ~EP_CFG_ENABLE;
> +       writel(ep_cfg, &priv_dev->regs->ep_cfg);
> +
> +       while (!list_empty(&priv_ep->pending_req_list)) {
> +               request = cdns3_next_request(&priv_ep->pending_req_list);
> +
> +               cdns3_gadget_giveback(priv_ep, to_cdns3_request(request),
> +                                     -ESHUTDOWN);
> +       }
> +
> +       while (!list_empty(&priv_ep->descmiss_req_list)) {
> +               priv_req = cdns3_next_priv_request(&priv_ep->descmiss_req_list);
> +
> +               kfree(priv_req->request.buf);
> +               cdns3_gadget_ep_free_request(&priv_ep->endpoint,
> +                                            &priv_req->request);
> +       }
> +
> +       while (!list_empty(&priv_ep->deferred_req_list)) {
> +               request = cdns3_next_request(&priv_ep->deferred_req_list);
> +
> +               cdns3_gadget_giveback(priv_ep, to_cdns3_request(request),
> +                                     -ESHUTDOWN);
> +       }
> +
> +       priv_ep->descmis_req = NULL;
> +
> +       ep->desc = NULL;
> +       priv_ep->flags &= ~EP_ENABLED;
> +
> +       spin_unlock_irqrestore(&priv_dev->lock, flags);
> +
> +       return ret;
> +}
> +
> +/**
> + * cdns3_gadget_ep_queue Transfer data on endpoint
> + * @ep: endpoint object
> + * @request: request object
> + * @gfp_flags: gfp flags
> + *
> + * Returns 0 on success, error code elsewhere
> + */
> +static int __cdns3_gadget_ep_queue(struct usb_ep *ep,
> +                                  struct usb_request *request,
> +                                  gfp_t gfp_flags)
> +{
> +       struct cdns3_endpoint *priv_ep = ep_to_cdns3_ep(ep);
> +       struct cdns3_device *priv_dev = priv_ep->cdns3_dev;
> +       struct cdns3_request *priv_req;
> +       int deferred = 0;
> +       int ret = 0;
> +
> +       request->actual = 0;
> +       request->status = -EINPROGRESS;
> +       priv_req = to_cdns3_request(request);
> +       trace_cdns3_ep_queue(priv_req);
> +
> +       /*
> +        * WA1: if transfer was queued before DESCMISS appear than we
> +        * can disable handling of DESCMISS interrupt. Driver assumes that it
> +        * can disable special treatment for this endpoint.
> +        */
> +       if (priv_ep->flags & EP_QUIRK_EXTRA_BUF_DET) {
> +               u32 reg;
> +
> +               cdns3_select_ep(priv_dev, priv_ep->num | priv_ep->dir);
> +               reg = readl(&priv_dev->regs->ep_sts_en);
> +               priv_ep->flags &= ~EP_QUIRK_EXTRA_BUF_DET;
> +               reg &= EP_STS_EN_DESCMISEN;
> +               writel(reg, &priv_dev->regs->ep_sts_en);
> +       }
> +
> +       /* WA1 */
> +       if (priv_ep->flags & EP_QUIRK_EXTRA_BUF_EN) {
> +               u8 pending_empty = list_empty(&priv_ep->pending_req_list);
> +               u8 descmiss_empty = list_empty(&priv_ep->descmiss_req_list);
> +
> +               /*
> +                *  DESCMISS transfer has been finished, so data will be
> +                *  directly copied from internal allocated usb_request
> +                *  objects.
> +                */
> +               if (pending_empty && !descmiss_empty &&
> +                   !(priv_req->flags & REQUEST_INTERNAL)) {
> +                       cdns3_descmiss_copy_data(priv_ep, request);
> +                       list_add_tail(&request->list,
> +                                     &priv_ep->pending_req_list);
> +                       cdns3_gadget_giveback(priv_ep, priv_req,
> +                                             request->status);
> +                       return ret;
> +               }
> +
> +               /*
> +                * WA1 driver will wait for completion DESCMISS transfer,
> +                * before starts new, not DESCMISS transfer.
> +                */
> +               if (!pending_empty && !descmiss_empty)
> +                       deferred = 1;
> +
> +               if (priv_req->flags & REQUEST_INTERNAL)
> +                       list_add_tail(&priv_req->list,
> +                                     &priv_ep->descmiss_req_list);
> +       }
> +
> +       ret = usb_gadget_map_request_by_dev(priv_dev->sysdev, request,
> +                                           usb_endpoint_dir_in(ep->desc));
> +       if (ret)
> +               return ret;
> +
> +       /*
> +        * If hardware endpoint configuration has not been set yet then
> +        * just queue request in deferred list. Transfer will be started in
> +        * cdns3_set_hw_configuration.
> +        */
> +       if (!priv_dev->hw_configured_flag)
> +               deferred = 1;
> +       else
> +               ret = cdns3_ep_run_transfer(priv_ep, request);
> +
> +       if (ret || deferred)
> +               list_add_tail(&request->list, &priv_ep->deferred_req_list);
> +       else
> +               list_add_tail(&request->list, &priv_ep->pending_req_list);
> +
> +       return ret;
> +}
> +
> +static int cdns3_gadget_ep_queue(struct usb_ep *ep, struct usb_request *request,
> +                                gfp_t gfp_flags)
> +{
> +       struct usb_request *zlp_request;
> +       struct cdns3_endpoint *priv_ep;
> +       struct cdns3_device *priv_dev;
> +       unsigned long flags;
> +       int ret;
> +
> +       if (!request || !ep)
> +               return -EINVAL;
> +
> +       priv_ep = ep_to_cdns3_ep(ep);
> +       priv_dev = priv_ep->cdns3_dev;
> +
> +       spin_lock_irqsave(&priv_dev->lock, flags);
> +
> +       ret = __cdns3_gadget_ep_queue(ep, request, gfp_flags);
> +
> +       if (ret == 0 && request->zero && request->length &&
> +           (request->length % ep->maxpacket == 0)) {
> +               struct cdns3_request *priv_req;
> +
> +               zlp_request = cdns3_gadget_ep_alloc_request(ep, GFP_ATOMIC);
> +               zlp_request->buf = priv_dev->zlp_buf;
> +               zlp_request->length = 0;
> +
> +               priv_req = to_cdns3_request(zlp_request);
> +               priv_req->flags |= REQUEST_ZLP;
> +
> +               dev_dbg(priv_dev->dev, "Queuing ZLP for endpoint: %s\n",
> +                       priv_ep->name);
> +               ret = __cdns3_gadget_ep_queue(ep, zlp_request, gfp_flags);
> +       }
> +
> +       spin_unlock_irqrestore(&priv_dev->lock, flags);
> +       return ret;
> +}
> +
> +/**
> + * cdns3_gadget_ep_dequeue Remove request from transfer queue
> + * @ep: endpoint object associated with request
> + * @request: request object
> + *
> + * Returns 0 on success, error code elsewhere
> + */
> +int cdns3_gadget_ep_dequeue(struct usb_ep *ep,
> +                           struct usb_request *request)
> +{
> +       struct cdns3_endpoint *priv_ep = ep_to_cdns3_ep(ep);
> +       struct cdns3_device *priv_dev = priv_ep->cdns3_dev;
> +       struct usb_request *req, *req_temp;
> +       struct cdns3_request *priv_req;
> +       unsigned long flags;
> +       int ret = 0;
> +
> +       if (!ep || !request || !ep->desc)
> +               return -EINVAL;
> +
> +       spin_lock_irqsave(&priv_dev->lock, flags);
> +
> +       priv_req = to_cdns3_request(request);
> +
> +       trace_cdns3_ep_dequeue(priv_req);
> +
> +       cdns3_select_ep(priv_dev, ep->desc->bEndpointAddress);
> +
> +       list_for_each_entry_safe(req, req_temp, &priv_ep->pending_req_list,
> +                                list) {
> +               if (request == req)
> +                       goto found;
> +       }
> +
> +       list_for_each_entry_safe(req, req_temp, &priv_ep->deferred_req_list,
> +                                list) {
> +               if (request == req)
> +                       goto found;
> +       }
> +
> +found:
> +       if (request == req)
> +               cdns3_gadget_giveback(priv_ep, priv_req, -ECONNRESET);
> +
> +       spin_unlock_irqrestore(&priv_dev->lock, flags);
> +       return ret;
> +}
> +
> +/**
> + * cdns3_gadget_ep_set_halt Sets/clears stall on selected endpoint
> + * @ep: endpoint object to set/clear stall on
> + * @value: 1 for set stall, 0 for clear stall
> + *
> + * Returns 0 on success, error code elsewhere
> + */
> +int cdns3_gadget_ep_set_halt(struct usb_ep *ep, int value)
> +{
> +       struct cdns3_endpoint *priv_ep = ep_to_cdns3_ep(ep);
> +       struct cdns3_device *priv_dev = priv_ep->cdns3_dev;
> +       unsigned long flags;
> +       int ret = 0;
> +
> +       if (!(priv_ep->flags & EP_ENABLED))
> +               return -EPERM;
> +
> +       spin_lock_irqsave(&priv_dev->lock, flags);
> +
> +       /* if actual transfer is pending defer setting stall on this endpoint */
> +       if ((priv_ep->flags & EP_PENDING_REQUEST) && value) {
> +               priv_ep->flags |= EP_STALL;
> +               goto finish;
> +       }
> +
> +       dev_dbg(priv_dev->dev, "Halt endpoint %s\n", priv_ep->name);
> +
> +       cdns3_select_ep(priv_dev, ep->desc->bEndpointAddress);
> +       if (value) {
> +               cdns3_ep_stall_flush(priv_ep);
> +       } else {
> +               priv_ep->flags &= ~EP_WEDGE;
> +               writel(EP_CMD_CSTALL | EP_CMD_EPRST, &priv_dev->regs->ep_cmd);
> +
> +               /* wait for EPRST cleared */
> +               ret = cdns3_handshake(&priv_dev->regs->ep_cmd,
> +                                     EP_CMD_EPRST, 0, 100);
> +               if (unlikely(ret)) {
> +                       dev_err(priv_dev->dev,
> +                               "Clearing halt condition failed for %s\n",
> +                               priv_ep->name);
> +                       goto finish;
> +
> +               } else {
> +                       priv_ep->flags &= ~EP_STALL;
> +               }
> +       }
> +
> +       priv_ep->flags &= ~EP_PENDING_REQUEST;
> +finish:
> +       spin_unlock_irqrestore(&priv_dev->lock, flags);
> +
> +       return ret;
> +}
> +
> +extern const struct usb_ep_ops cdns3_gadget_ep0_ops;
> +
> +static const struct usb_ep_ops cdns3_gadget_ep_ops = {
> +       .enable = cdns3_gadget_ep_enable,
> +       .disable = cdns3_gadget_ep_disable,
> +       .alloc_request = cdns3_gadget_ep_alloc_request,
> +       .free_request = cdns3_gadget_ep_free_request,
> +       .queue = cdns3_gadget_ep_queue,
> +       .dequeue = cdns3_gadget_ep_dequeue,
> +       .set_halt = cdns3_gadget_ep_set_halt,
> +       .set_wedge = cdns3_gadget_ep_set_wedge,
> +};
> +
> +/**
> + * cdns3_gadget_get_frame Returns number of actual ITP frame
> + * @gadget: gadget object
> + *
> + * Returns number of actual ITP frame
> + */
> +static int cdns3_gadget_get_frame(struct usb_gadget *gadget)
> +{
> +       struct cdns3_device *priv_dev = gadget_to_cdns3_device(gadget);
> +
> +       return readl(&priv_dev->regs->usb_iptn);
> +}
> +
> +static int cdns3_gadget_wakeup(struct usb_gadget *gadget)
> +{
> +       return 0;
> +}
> +
> +static int cdns3_gadget_set_selfpowered(struct usb_gadget *gadget,
> +                                       int is_selfpowered)
> +{
> +       struct cdns3_device *priv_dev = gadget_to_cdns3_device(gadget);
> +       unsigned long flags;
> +
> +       spin_lock_irqsave(&priv_dev->lock, flags);
> +       priv_dev->is_selfpowered = !!is_selfpowered;
> +       spin_unlock_irqrestore(&priv_dev->lock, flags);
> +       return 0;
> +}
> +
> +static int cdns3_gadget_pullup(struct usb_gadget *gadget, int is_on)
> +{
> +       struct cdns3_device *priv_dev = gadget_to_cdns3_device(gadget);
> +
> +       if (is_on)
> +               writel(USB_CONF_DEVEN, &priv_dev->regs->usb_conf);
> +       else
> +               writel(USB_CONF_DEVDS, &priv_dev->regs->usb_conf);
> +
> +       return 0;
> +}
> +
> +static void cdns3_gadget_config(struct cdns3_device *priv_dev)
> +{
> +       struct cdns3_usb_regs __iomem *regs = priv_dev->regs;
> +
> +       cdns3_ep0_config(priv_dev);
> +
> +       /* enable interrupts for endpoint 0 (in and out) */
> +       writel(EP_IEN_EP_OUT0 | EP_IEN_EP_IN0, &regs->ep_ien);
> +
> +       /* enable generic interrupt*/
> +       writel(USB_IEN_INIT, &regs->usb_ien);
> +       writel(USB_CONF_CLK2OFFDS | USB_CONF_L1DS, &regs->usb_conf);
> +       writel(USB_CONF_DMULT, &regs->usb_conf);
> +       cdns3_gadget_pullup(&priv_dev->gadget, 1);
> +}
> +
> +/**
> + * cdns3_gadget_udc_start Gadget start
> + * @gadget: gadget object
> + * @driver: driver which operates on this gadget
> + *
> + * Returns 0 on success, error code elsewhere
> + */
> +static int cdns3_gadget_udc_start(struct usb_gadget *gadget,
> +                                 struct usb_gadget_driver *driver)
> +{
> +       struct cdns3_device *priv_dev = gadget_to_cdns3_device(gadget);
> +       unsigned long flags;
> +
> +       spin_lock_irqsave(&priv_dev->lock, flags);
> +       priv_dev->gadget_driver = driver;
> +       cdns3_gadget_config(priv_dev);
> +       spin_unlock_irqrestore(&priv_dev->lock, flags);
> +       return 0;
> +}
> +
> +/**
> + * cdns3_gadget_udc_stop Stops gadget
> + * @gadget: gadget object
> + *
> + * Returns 0
> + */
> +static int cdns3_gadget_udc_stop(struct usb_gadget *gadget)
> +{
> +       struct cdns3_device *priv_dev = gadget_to_cdns3_device(gadget);
> +       struct cdns3_endpoint *priv_ep;
> +       u32 bEndpointAddress;
> +       struct usb_ep *ep;
> +       int ret = 0;
> +
> +       priv_dev->gadget_driver = NULL;
> +
> +       priv_dev->onchip_mem_allocated_size = 0;
> +       priv_dev->gadget.speed = USB_SPEED_UNKNOWN;
> +
> +       list_for_each_entry(ep, &priv_dev->gadget.ep_list, ep_list) {
> +               priv_ep = ep_to_cdns3_ep(ep);
> +               bEndpointAddress = priv_ep->num | priv_ep->dir;
> +               cdns3_select_ep(priv_dev, bEndpointAddress);
> +               writel(EP_CMD_EPRST, &priv_dev->regs->ep_cmd);
> +               ret = cdns3_handshake(&priv_dev->regs->ep_cmd,
> +                                     EP_CMD_EPRST, 0, 100);
> +               cdns3_free_trb_pool(priv_ep);
> +       }
> +
> +       /* disable interrupt for device */
> +       writel(0, &priv_dev->regs->usb_ien);
> +       writel(USB_CONF_DEVDS, &priv_dev->regs->usb_conf);
> +
> +       return ret;
> +}
> +
> +static const struct usb_gadget_ops cdns3_gadget_ops = {
> +       .get_frame = cdns3_gadget_get_frame,
> +       .wakeup = cdns3_gadget_wakeup,
> +       .set_selfpowered = cdns3_gadget_set_selfpowered,
> +       .pullup = cdns3_gadget_pullup,
> +       .udc_start = cdns3_gadget_udc_start,
> +       .udc_stop = cdns3_gadget_udc_stop,
> +       .match_ep = cdns3_gadget_match_ep,
> +};
> +
> +static void cdns3_free_all_eps(struct cdns3_device *priv_dev)
> +{
> +       int i;
> +
> +       /*ep0 OUT point to ep0 IN*/
> +       priv_dev->eps[16] = NULL;
> +
> +       cdns3_free_trb_pool(priv_dev->eps[0]);
> +
> +       for (i = 0; i < CDNS3_ENDPOINTS_MAX_COUNT; i++)
> +               if (priv_dev->eps[i])
> +                       devm_kfree(priv_dev->dev, priv_dev->eps[i]);
> +}
> +
> +/**
> + * cdns3_init_eps Initializes software endpoints of gadget
> + * @cdns3: extended gadget object
> + *
> + * Returns 0 on success, error code elsewhere
> + */
> +static int cdns3_init_eps(struct cdns3_device *priv_dev)
> +{
> +       u32 ep_enabled_reg, iso_ep_reg;
> +       struct cdns3_endpoint *priv_ep;
> +       int ep_dir, ep_number;
> +       u32 ep_mask;
> +       int ret = 0;
> +       int i;
> +
> +       /* Read it from USB_CAP3 to USB_CAP5 */
> +       ep_enabled_reg = readl(&priv_dev->regs->usb_cap3);
> +       iso_ep_reg = readl(&priv_dev->regs->usb_cap4);
> +
> +       dev_dbg(priv_dev->dev, "Initializing non-zero endpoints\n");
> +
> +       for (i = 0; i < CDNS3_ENDPOINTS_MAX_COUNT; i++) {
> +               ep_dir = i >> 4;        /* i div 16 */
> +               ep_number = i & 0xF;    /* i % 16 */
> +               ep_mask = BIT(i);
> +
> +               if (!(ep_enabled_reg & ep_mask))
> +                       continue;
> +
> +               if (ep_dir && !ep_number) {
> +                       priv_dev->eps[i] = priv_dev->eps[0];
> +                       continue;
> +               }
> +
> +               priv_ep = devm_kzalloc(priv_dev->dev, sizeof(*priv_ep),
> +                                      GFP_KERNEL);
> +               if (!priv_ep) {
> +                       ret = -ENOMEM;
> +                       goto err;
> +               }
> +
> +               /* set parent of endpoint object */
> +               priv_ep->cdns3_dev = priv_dev;
> +               priv_dev->eps[i] = priv_ep;
> +               priv_ep->num = ep_number;
> +               priv_ep->dir = ep_dir ? USB_DIR_IN : USB_DIR_OUT;
> +
> +               if (!ep_number) {
> +                       ret = cdns3_init_ep0(priv_dev, priv_ep);
> +                       if (ret) {
> +                               dev_err(priv_dev->dev, "Failed to init ep0\n");
> +                               goto err;
> +                       }
> +               } else {
> +                       snprintf(priv_ep->name, sizeof(priv_ep->name), "ep%d%s",
> +                                ep_number, !!ep_dir ? "in" : "out");
> +                       priv_ep->endpoint.name = priv_ep->name;
> +
> +                       usb_ep_set_maxpacket_limit(&priv_ep->endpoint,
> +                                                  CDNS3_EP_MAX_PACKET_LIMIT);
> +                       priv_ep->endpoint.max_streams = CDNS3_EP_MAX_STREAMS;
> +                       priv_ep->endpoint.ops = &cdns3_gadget_ep_ops;
> +                       if (ep_dir)
> +                               priv_ep->endpoint.caps.dir_in = 1;
> +                       else
> +                               priv_ep->endpoint.caps.dir_out = 1;
> +
> +                       if (iso_ep_reg & ep_mask)
> +                               priv_ep->endpoint.caps.type_iso = 1;
> +
> +                       priv_ep->endpoint.caps.type_bulk = 1;
> +                       priv_ep->endpoint.caps.type_int = 1;
> +                       priv_ep->endpoint.maxburst = CDNS3_EP_BUF_SIZE - 1;
> +
> +                       list_add_tail(&priv_ep->endpoint.ep_list,
> +                                     &priv_dev->gadget.ep_list);
> +               }
> +
> +               priv_ep->flags = 0;
> +
> +               dev_info(priv_dev->dev, "Initialized  %s support: %s %s\n",
> +                        priv_ep->name,
> +                        priv_ep->endpoint.caps.type_bulk ? "BULK, INT" : "",
> +                        priv_ep->endpoint.caps.type_iso ? "ISO" : "");
> +
> +               INIT_LIST_HEAD(&priv_ep->pending_req_list);
> +               INIT_LIST_HEAD(&priv_ep->deferred_req_list);
> +               INIT_LIST_HEAD(&priv_ep->descmiss_req_list);
> +       }
> +
> +       return 0;
> +err:
> +       cdns3_free_all_eps(priv_dev);
> +       return -ENOMEM;
> +}
> +
> +static void cdns3_gadget_disable(struct cdns3 *cdns)
> +{
> +       struct cdns3_device *priv_dev;
> +
> +       priv_dev = cdns->gadget_dev;
> +
> +       if (priv_dev->gadget_driver)
> +               priv_dev->gadget_driver->disconnect(&priv_dev->gadget);
> +
> +       usb_gadget_disconnect(&priv_dev->gadget);
> +       priv_dev->gadget.speed = USB_SPEED_UNKNOWN;
> +}
> +
> +void cdns3_gadget_exit(struct cdns3 *cdns)
> +{
> +       struct cdns3_device *priv_dev;
> +
> +       priv_dev = cdns->gadget_dev;
> +
> +       cdns3_gadget_disable(cdns);
> +
> +       devm_free_irq(cdns->dev, cdns->irq, cdns);
> +
> +       pm_runtime_mark_last_busy(cdns->dev);
> +       pm_runtime_put_autosuspend(cdns->dev);
> +
> +       usb_del_gadget_udc(&priv_dev->gadget);
> +
> +       cdns3_free_all_eps(priv_dev);
> +
> +       dma_free_coherent(priv_dev->sysdev, 8, priv_dev->setup_buf,
> +                         priv_dev->setup_dma);
> +
> +       kfree(priv_dev->zlp_buf);
> +       kfree(priv_dev);
> +       cdns->gadget_dev = NULL;
> +}
> +
> +static int cdns3_gadget_start(struct cdns3 *cdns)
> +{
> +       struct cdns3_device *priv_dev;
> +       u32 max_speed;
> +       int ret;
> +
> +       priv_dev = kzalloc(sizeof(*priv_dev), GFP_KERNEL);
> +       if (!priv_dev)
> +               return -ENOMEM;
> +
> +       cdns->gadget_dev = priv_dev;
> +       priv_dev->sysdev = cdns->dev;
> +       priv_dev->dev = cdns->dev;
> +       priv_dev->regs = cdns->dev_regs;
> +
> +       max_speed = usb_get_maximum_speed(cdns->dev);
> +
> +       /* Check the maximum_speed parameter */
> +       switch (max_speed) {
> +       case USB_SPEED_FULL:
> +       case USB_SPEED_HIGH:
> +       case USB_SPEED_SUPER:
> +               break;
> +       default:
> +               dev_err(cdns->dev, "invalid maximum_speed parameter %d\n",
> +                       max_speed);
> +               /* fall through */
> +       case USB_SPEED_UNKNOWN:
> +               /* default to superspeed */
> +               max_speed = USB_SPEED_SUPER;
> +               break;
> +       }
> +
> +       /* fill gadget fields */
> +       priv_dev->gadget.max_speed = max_speed;
> +       priv_dev->gadget.speed = USB_SPEED_UNKNOWN;
> +       priv_dev->gadget.ops = &cdns3_gadget_ops;
> +       priv_dev->gadget.name = "usb-ss-gadget";
> +       priv_dev->gadget.sg_supported = 1;
> +
> +       spin_lock_init(&priv_dev->lock);
> +       INIT_WORK(&priv_dev->pending_status_wq,
> +                 cdns3_pending_setup_status_handler);
> +
> +       /* initialize endpoint container */
> +       INIT_LIST_HEAD(&priv_dev->gadget.ep_list);
> +
> +       ret = cdns3_init_eps(priv_dev);
> +       if (ret) {
> +               dev_err(priv_dev->dev, "Failed to create endpoints\n");
> +               goto err1;
> +       }
> +
> +       /* allocate memory for setup packet buffer */
> +       priv_dev->setup_buf = dma_alloc_coherent(priv_dev->sysdev, 8,
> +                                                &priv_dev->setup_dma, GFP_DMA);
> +       if (!priv_dev->setup_buf) {
> +               dev_err(priv_dev->dev, "Failed to allocate memory for SETUP buffer\n");
> +               ret = -ENOMEM;
> +               goto err2;
> +       }
> +
> +       priv_dev->dev_ver = readl(&priv_dev->regs->usb_cap6);
> +       dev_dbg(priv_dev->dev, "Device Controller version: %08x\n",
> +               readl(&priv_dev->regs->usb_cap6));
> +       dev_dbg(priv_dev->dev, "USB Capabilities:: %08x\n",
> +               readl(&priv_dev->regs->usb_cap1));
> +       dev_dbg(priv_dev->dev, "On-Chip memory cnfiguration: %08x\n",
> +               readl(&priv_dev->regs->usb_cap2));
> +
> +       priv_dev->zlp_buf = kzalloc(CDNS3_EP_ZLP_BUF_SIZE, GFP_KERNEL);
> +       if (!priv_dev->zlp_buf) {
> +               ret = -ENOMEM;
> +               goto err3;
> +       }
> +
> +       /* add USB gadget device */
> +       ret = usb_add_gadget_udc(priv_dev->dev, &priv_dev->gadget);
> +       if (ret < 0) {
> +               dev_err(priv_dev->dev,
> +                       "Failed to register USB device controller\n");
> +               goto err4;
> +       }
> +
> +       return 0;
> +err4:
> +       kfree(priv_dev->zlp_buf);
> +err3:
> +       dma_free_coherent(priv_dev->sysdev, 8, priv_dev->setup_buf,
> +                         priv_dev->setup_dma);
> +err2:
> +       cdns3_free_all_eps(priv_dev);
> +err1:
> +       cdns->gadget_dev = NULL;
> +       return ret;
> +}
> +
> +static int __cdns3_gadget_init(struct cdns3 *cdns)
> +{
> +       struct cdns3_device *priv_dev;
> +       unsigned long flags;
> +       int ret = 0;
> +
> +       ret = cdns3_gadget_start(cdns);
> +       if (ret)
> +               return ret;
> +
> +       priv_dev = cdns->gadget_dev;
> +       ret = devm_request_threaded_irq(cdns->dev, cdns->irq,
> +                                       cdns3_device_irq_handler,
> +                                       cdns3_device_thread_irq_handler,
> +                                       IRQF_SHARED, dev_name(cdns->dev), cdns);
> +       if (ret)
> +               goto err0;
> +
> +       pm_runtime_get_sync(cdns->dev);
> +       spin_lock_irqsave(&priv_dev->lock, flags);
> +       spin_unlock_irqrestore(&priv_dev->lock, flags);
> +       return 0;
> +err0:
> +       cdns3_gadget_exit(cdns);
> +       return ret;
> +}
> +
> +static int cdns3_gadget_suspend(struct cdns3 *cdns, bool do_wakeup)
> +{
> +       cdns3_gadget_disable(cdns);
> +       return 0;
> +}
> +
> +static int cdns3_gadget_resume(struct cdns3 *cdns, bool hibernated)
> +{
> +       struct cdns3_device *priv_dev;
> +       unsigned long flags;
> +
> +       priv_dev = cdns->gadget_dev;
> +       spin_lock_irqsave(&priv_dev->lock, flags);
> +
> +       if (!priv_dev->gadget_driver) {
> +               spin_unlock_irqrestore(&priv_dev->lock, flags);
> +               return 0;
> +       }
> +
> +       cdns3_gadget_config(priv_dev);
> +       spin_unlock_irqrestore(&priv_dev->lock, flags);
> +       return 0;
> +}
> +
> +/**
> + * cdns3_gadget_init - initialize device structure
> + *
> + * cdns: cdns3 instance
> + *
> + * This function initializes the gadget.
> + */
> +int cdns3_gadget_init(struct cdns3 *cdns)
> +{
> +       struct cdns3_role_driver *rdrv;
> +
> +       rdrv = devm_kzalloc(cdns->dev, sizeof(*rdrv), GFP_KERNEL);
> +       if (!rdrv)
> +               return -ENOMEM;
> +
> +       rdrv->start     = __cdns3_gadget_init;
> +       rdrv->stop      = cdns3_gadget_exit;
> +       rdrv->suspend   = cdns3_gadget_suspend;
> +       rdrv->resume    = cdns3_gadget_resume;
> +       rdrv->state     = CDNS3_ROLE_STATE_INACTIVE;
> +       rdrv->name      = "gadget";
> +       cdns->roles[CDNS3_ROLE_GADGET] = rdrv;
> +
> +       return 0;
> +}
> diff --git a/drivers/usb/cdns3/gadget.h b/drivers/usb/cdns3/gadget.h
> new file mode 100644
> index 000000000000..41cec7f085ad
> --- /dev/null
> +++ b/drivers/usb/cdns3/gadget.h
> @@ -0,0 +1,1206 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +/*
> + * USBSS device controller driver header file
> + *
> + * Copyright (C) 2018 Cadence.
> + * Copyright (C) 2017-2018 NXP
> + *
> + * Author: Pawel Laszczak <pawell@cadence.com>
> + *         Pawel Jez <pjez@cadence.com>
> + *         Peter Chen <peter.chen@nxp.com>
> + */
> +#ifndef __LINUX_CDNS3_GADGET
> +#define __LINUX_CDNS3_GADGET
> +#include <linux/usb/gadget.h>
> +
> +/*
> + * USBSS-DEV register interface.
> + * This corresponds to the USBSS Device Controller Interface
> + */
> +
> +/**
> + * struct cdns3_usb_regs - device controller registers.
> + * @usb_conf:      Global Configuration Register.
> + * @usb_sts:       Global Status Register.
> + * @usb_cmd:       Global Command Register.
> + * @usb_iptn:      ITP/SOF number Register.
> + * @usb_lpm:       Global Command Register.
> + * @usb_ien:       USB Interrupt Enable Register.
> + * @usb_ists:      USB Interrupt Status Register.
> + * @ep_sel:        Endpoint Select Register.
> + * @ep_traddr:     Endpoint Transfer Ring Address Register.
> + * @ep_cfg:        Endpoint Configuration Register.
> + * @ep_cmd:        Endpoint Command Register.
> + * @ep_sts:        Endpoint Status Register.
> + * @ep_sts_sid:    Endpoint Status Register.
> + * @ep_sts_en:     Endpoint Status Register Enable.
> + * @drbl:          Doorbell Register.
> + * @ep_ien:        EP Interrupt Enable Register.
> + * @ep_ists:       EP Interrupt Status Register.
> + * @usb_pwr:       Global Power Configuration Register.
> + * @usb_conf2:     Global Configuration Register 2.
> + * @usb_cap1:      Capability Register 1.
> + * @usb_cap2:      Capability Register 2.
> + * @usb_cap3:      Capability Register 3.
> + * @usb_cap4:      Capability Register 4.
> + * @usb_cap5:      Capability Register 5.
> + * @usb_cap6:      Capability Register 6.
> + * @usb_cpkt1:     Custom Packet Register 1.
> + * @usb_cpkt2:     Custom Packet Register 2.
> + * @usb_cpkt3:     Custom Packet Register 3.
> + * @reserved1:     Reserved.
> + * @cfg_regs:      Configuration registers.
> + * @reserved2:     Reserved.
> + * @dma_axi_ctrl:  AXI Control register.
> + * @dma_axi_id:    AXI ID register.
> + * @dma_axi_cap:   AXI Capability register.
> + * @dma_axi_ctrl0: AXI Control 0 register.
> + * @dma_axi_ctrl1: AXI Control 1 register.
> + */
> +struct cdns3_usb_regs {
> +       __le32 usb_conf;
> +       __le32 usb_sts;
> +       __le32 usb_cmd;
> +       __le32 usb_iptn;
> +       __le32 usb_lpm;
> +       __le32 usb_ien;
> +       __le32 usb_ists;
> +       __le32 ep_sel;
> +       __le32 ep_traddr;
> +       __le32 ep_cfg;
> +       __le32 ep_cmd;
> +       __le32 ep_sts;
> +       __le32 ep_sts_sid;
> +       __le32 ep_sts_en;
> +       __le32 drbl;
> +       __le32 ep_ien;
> +       __le32 ep_ists;
> +       __le32 usb_pwr;
> +       __le32 usb_conf2;
> +       __le32 usb_cap1;
> +       __le32 usb_cap2;
> +       __le32 usb_cap3;
> +       __le32 usb_cap4;
> +       __le32 usb_cap5;
> +       __le32 usb_cap6;
> +       __le32 usb_cpkt1;
> +       __le32 usb_cpkt2;
> +       __le32 usb_cpkt3;
> +       __le32 reserved1[36];
> +       __le32 cfg_reg1;
> +       __le32 dbg_link1;
> +       __le32 dbg_link2;
> +       __le32 cfg_regs[74];
> +       __le32 reserved2[34];
> +       __le32 dma_axi_ctrl;
> +       __le32 dma_axi_id;
> +       __le32 dma_axi_cap;
> +       __le32 dma_axi_ctrl0;
> +       __le32 dma_axi_ctrl1;
> +};
> +
> +/* USB_CONF - bitmasks */
> +/* Reset USB device configuration. */
> +#define USB_CONF_CFGRST                BIT(0)
> +/* Set Configuration. */
> +#define USB_CONF_CFGSET                BIT(1)
> +/* Disconnect USB device in SuperSpeed. */
> +#define USB_CONF_USB3DIS       BIT(3)
> +/* Disconnect USB device in HS/FS */
> +#define USB_CONF_USB2DIS       BIT(4)
> +/* Little Endian access - default */
> +#define USB_CONF_LENDIAN       BIT(5)
> +/*
> + * Big Endian access. Driver assume that byte order for
> + * SFRs access always is as Little Endian so this bit
> + * is not used.
> + */
> +#define USB_CONF_BENDIAN       BIT(6)
> +/* Device software reset. */
> +#define USB_CONF_SWRST         BIT(7)
> +/* Singular DMA transfer mode. */
> +#define USB_CONF_DSING         BIT(8)
> +/* Multiple DMA transfers mode. */
> +#define USB_CONF_DMULT         BIT(9)
> +/* DMA clock turn-off enable. */
> +#define USB_CONF_DMAOFFEN      BIT(10)
> +/* DMA clock turn-off disable. */
> +#define USB_CONF_DMAOFFDS      BIT(11)
> +/* Clear Force Full Speed. */
> +#define USB_CONF_CFORCE_FS     BIT(12)
> +/* Set Force Full Speed. */
> +#define USB_CONF_SFORCE_FS     BIT(13)
> +/* Device enable. */
> +#define USB_CONF_DEVEN         BIT(14)
> +/* Device disable. */
> +#define USB_CONF_DEVDS         BIT(15)
> +/* L1 LPM state entry enable (used in HS/FS mode). */
> +#define USB_CONF_L1EN          BIT(16)
> +/* L1 LPM state entry disable (used in HS/FS mode). */
> +#define USB_CONF_L1DS          BIT(17)
> +/* USB 2.0 clock gate disable. */
> +#define USB_CONF_CLK2OFFEN     BIT(18)
> +/* USB 2.0 clock gate enable. */
> +#define USB_CONF_CLK2OFFDS     BIT(19)
> +/* L0 LPM state entry request (used in HS/FS mode). */
> +#define USB_CONF_LGO_L0                BIT(20)
> +/* USB 3.0 clock gate disable. */
> +#define USB_CONF_CLK3OFFEN     BIT(21)
> +/* USB 3.0 clock gate enable. */
> +#define USB_CONF_CLK3OFFDS     BIT(22)
> +/* Bit 23 is reserved*/
> +/* U1 state entry enable (used in SS mode). */
> +#define USB_CONF_U1EN          BIT(24)
> +/* U1 state entry disable (used in SS mode). */
> +#define USB_CONF_U1DS          BIT(25)
> +/* U2 state entry enable (used in SS mode). */
> +#define USB_CONF_U2EN          BIT(26)
> +/* U2 state entry disable (used in SS mode). */
> +#define USB_CONF_U2DS          BIT(27)
> +/* U0 state entry request (used in SS mode). */
> +#define USB_CONF_LGO_U0                BIT(28)
> +/* U1 state entry request (used in SS mode). */
> +#define USB_CONF_LGO_U1                BIT(29)
> +/* U2 state entry request (used in SS mode). */
> +#define USB_CONF_LGO_U2                BIT(30)
> +/* SS.Inactive state entry request (used in SS mode) */
> +#define USB_CONF_LGO_SSINACT   BIT(31)
> +
> +/* USB_STS - bitmasks */
> +/*
> + * Configuration status.
> + * 1 - device is in the configured state.
> + * 0 - device is not configured.
> + */
> +#define USB_STS_CFGSTS_MASK    BIT(0)
> +#define USB_STS_CFGSTS(p)      ((p) & USB_STS_CFGSTS_MASK)
> +/*
> + * On-chip memory overflow.
> + * 0 - On-chip memory status OK.
> + * 1 - On-chip memory overflow.
> + */
> +#define USB_STS_OV_MASK                BIT(1)
> +#define USB_STS_OV(p)          ((p) & USB_STS_OV_MASK)
> +/*
> + * SuperSpeed connection status.
> + * 0 - USB in SuperSpeed mode disconnected.
> + * 1 - USB in SuperSpeed mode connected.
> + */
> +#define USB_STS_USB3CONS_MASK  BIT(2)
> +#define USB_STS_USB3CONS(p)    ((p) & USB_STS_USB3CONS_MASK)
> +/*
> + * DMA transfer configuration status.
> + * 0 - single request.
> + * 1 - multiple TRB chain
> + */
> +#define USB_STS_DTRANS_MASK    BIT(3)
> +#define USB_STS_DTRANS(p)      ((p) & USB_STS_DTRANS_MASK)
> +/*
> + * Device speed.
> + * 0 - Undefined (value after reset).
> + * 1 - Low speed
> + * 2 - Full speed
> + * 3 - High speed
> + * 4 - Super speed
> + */
> +#define USB_STS_USBSPEED_MASK  GENMASK(6, 4)
> +#define USB_STS_USBSPEED(p)    (((p) & USB_STS_USBSPEED_MASK) >> 4)
> +#define USB_STS_LS             (0x1 << 4)
> +#define USB_STS_FS             (0x2 << 4)
> +#define USB_STS_HS             (0x3 << 4)
> +#define USB_STS_SS             (0x4 << 4)
> +#define DEV_UNDEFSPEED(p)      (((p) & USB_STS_USBSPEED_MASK) == (0x0 << 4))
> +#define DEV_LOWSPEED(p)                (((p) & USB_STS_USBSPEED_MASK) == USB_STS_LS)
> +#define DEV_FULLSPEED(p)       (((p) & USB_STS_USBSPEED_MASK) == USB_STS_FS)
> +#define DEV_HIGHSPEED(p)       (((p) & USB_STS_USBSPEED_MASK) == USB_STS_HS)
> +#define DEV_SUPERSPEED(p)      (((p) & USB_STS_USBSPEED_MASK) == USB_STS_SS)
> +/*
> + * Endianness for SFR access.
> + * 0 - Little Endian order (default after hardware reset).
> + * 1 - Big Endian order
> + */
> +#define USB_STS_ENDIAN_MASK    BIT(7)
> +#define USB_STS_ENDIAN(p)      ((p) & USB_STS_ENDIAN_MASK)
> +/*
> + * HS/FS clock turn-off status.
> + * 0 - hsfs clock is always on.
> + * 1 - hsfs clock turn-off in L2 (HS/FS mode) is enabled
> + *          (default after hardware reset).
> + */
> +#define USB_STS_CLK2OFF_MASK   BIT(8)
> +#define USB_STS_CLK2OFF(p)     ((p) & USB_STS_CLK2OFF_MASK)
> +/*
> + * PCLK clock turn-off status.
> + * 0 - pclk clock is always on.
> + * 1 - pclk clock turn-off in U3 (SS mode) is enabled
> + *          (default after hardware reset).
> + */
> +#define USB_STS_CLK3OFF_MASK   BIT(9)
> +#define USB_STS_CLK3OFF(p)     ((p) & USB_STS_CLK3OFF_MASK)
> +/*
> + * Controller in reset state.
> + * 0 - Internal reset is active.
> + * 1 - Internal reset is not active and controller is fully operational.
> + */
> +#define USB_STS_IN_RST_MASK    BIT(10)
> +#define USB_STS_IN_RST(p)      ((p) & USB_STS_IN_RST_MASK)
> +/*
> + * Device enable Status.
> + * 0 - USB device is disabled (VBUS input is disconnected from internal logic).
> + * 1 - USB device is enabled (VBUS input is connected to the internal logic).
> + */
> +#define USB_STS_DEVS_MASK      BIT(14)
> +#define USB_STS_DEVS(p)                ((p) & USB_STS_DEVS_MASK)
> +/*
> + * DAddress statuss.
> + * 0 - USB device is default state.
> + * 1 - USB device is at least in address state.
> + */
> +#define USB_STS_ADDRESSED_MASK BIT(15)
> +#define USB_STS_ADDRESSED(p)   ((p) & USB_STS_ADDRESSED_MASK)
> +/*
> + * L1 LPM state enable status (used in HS/FS mode).
> + * 0 - Entering to L1 LPM state disabled.
> + * 1 - Entering to L1 LPM state enabled.
> + */
> +#define USB_STS_L1ENS_MASK     BIT(16)
> +#define USB_STS_L1ENS(p)       ((p) & USB_STS_L1ENS_MASK)
> +/*
> + * Internal VBUS connection status (used both in HS/FS  and SS mode).
> + * 0 - internal VBUS is not detected.
> + * 1 - internal VBUS is detected.
> + */
> +#define USB_STS_VBUSS_MASK     BIT(17)
> +#define USB_STS_VBUSS(p)       ((p) & USB_STS_VBUSS_MASK)
> +/*
> + * HS/FS LPM  state (used in FS/HS mode).
> + * 0 - L0 State
> + * 1 - L1 State
> + * 2 - L2 State
> + * 3 - L3 State
> + */
> +#define USB_STS_LPMST_MASK     GENMASK(19, 18)
> +#define DEV_L0_STATE(p)                (((p) & USB_STS_LPMST_MASK) == (0x0 << 18))
> +#define DEV_L1_STATE(p)                (((p) & USB_STS_LPMST_MASK) == (0x1 << 18))
> +#define DEV_L2_STATE(p)                (((p) & USB_STS_LPMST_MASK) == (0x2 << 18))
> +#define DEV_L3_STATE(p)                (((p) & USB_STS_LPMST_MASK) == (0x3 << 18))
> +/*
> + * Disable HS status (used in FS/HS mode).
> + * 0 - the disconnect bit for HS/FS mode is set .
> + * 1 - the disconnect bit for HS/FS mode is not set.
> + */
> +#define USB_STS_USB2CONS_MASK  BIT(20)
> +#define USB_STS_USB2CONS(p)    ((p) & USB_STS_USB2CONS_MASK)
> +/*
> + * HS/FS mode connection status (used in FS/HS mode).
> + * 0 - High Speed operations in USB2.0 (FS/HS) mode not disabled.
> + * 1 - High Speed operations in USB2.0 (FS/HS).
> + */
> +#define USB_STS_DISABLE_HS_MASK        BIT(21)
> +#define USB_STS_DISABLE_HS(p)  ((p) & USB_STS_DISABLE_HS_MASK)
> +/*
> + * U1 state enable status (used in SS mode).
> + * 0 - Entering to  U1 state disabled.
> + * 1 - Entering to  U1 state enabled.
> + */
> +#define USB_STS_U1ENS_MASK     BIT(24)
> +#define USB_STS_U1ENS(p)       ((p) & USB_STS_U1ENS_MASK)
> +/*
> + * U2 state enable status (used in SS mode).
> + * 0 - Entering to  U2 state disabled.
> + * 1 - Entering to  U2 state enabled.
> + */
> +#define USB_STS_U2ENS_MASK     BIT(25)
> +#define USB_STS_U2ENS(p)       ((p) & USB_STS_U2ENS_MASK)
> +/*
> + * SuperSpeed Link LTSSM state. This field reflects USBSS-DEV current
> + * SuperSpeed link state
> + */
> +#define USB_STS_LST_MASK       GENMASK(29, 26)
> +#define DEV_LST_U0             (((p) & USB_STS_LST_MASK) == (0x0 << 26))
> +#define DEV_LST_U1             (((p) & USB_STS_LST_MASK) == (0x1 << 26))
> +#define DEV_LST_U2             (((p) & USB_STS_LST_MASK) == (0x2 << 26))
> +#define DEV_LST_U3             (((p) & USB_STS_LST_MASK) == (0x3 << 26))
> +#define DEV_LST_DISABLED       (((p) & USB_STS_LST_MASK) == (0x4 << 26))
> +#define DEV_LST_RXDETECT       (((p) & USB_STS_LST_MASK) == (0x5 << 26))
> +#define DEV_LST_INACTIVE       (((p) & USB_STS_LST_MASK) == (0x6 << 26))
> +#define DEV_LST_POLLING                (((p) & USB_STS_LST_MASK) == (0x7 << 26))
> +#define DEV_LST_RECOVERY       (((p) & USB_STS_LST_MASK) == (0x8 << 26))
> +#define DEV_LST_HOT_RESET      (((p) & USB_STS_LST_MASK) == (0x9 << 26))
> +#define DEV_LST_COMP_MODE      (((p) & USB_STS_LST_MASK) == (0xa << 26))
> +#define DEV_LST_LB_STATE       (((p) & USB_STS_LST_MASK) == (0xb << 26))
> +/*
> + * DMA clock turn-off status.
> + * 0 - DMA clock is always on (default after hardware reset).
> + * 1 - DMA clock turn-off in U1, U2 and U3 (SS mode) is enabled.
> + */
> +#define USB_STS_DMAOFF_MASK    BIT(30)
> +#define USB_STS_DMAOFF(p)      ((p) & USB_STS_DMAOFF_MASK)
> +/*
> + * SFR Endian statuss.
> + * 0 - Little Endian order (default after hardware reset).
> + * 1 - Big Endian order.
> + */
> +#define USB_STS_ENDIAN2_MASK   BIT(31)
> +#define USB_STS_ENDIAN2(p)     ((p) & USB_STS_ENDIAN2_MASK)
> +
> +/* USB_CMD -  bitmasks */
> +/* Set Function Address */
> +#define USB_CMD_SET_ADDR       BIT(0)
> +/*
> + * Function Address This field is saved to the device only when the field
> + * SET_ADDR is set '1 ' during write to USB_CMD register.
> + * Software is responsible for entering the address of the device during
> + * SET_ADDRESS request service. This field should be set immediately after
> + * the SETUP packet is decoded, and prior to confirmation of the status phase
> + */
> +#define USB_CMD_FADDR_MASK     GENMASK(7, 1)
> +#define USB_CMD_FADDR(p)       (((p) << 1) & USB_CMD_FADDR_MASK)
> +/* Send Function Wake Device Notification TP (used only in SS mode). */
> +#define USB_CMD_SDNFW          BIT(8)
> +/* Set Test Mode (used only in HS/FS mode). */
> +#define USB_CMD_STMODE         BIT(9)
> +/* Test mode selector (used only in HS/FS mode) */
> +#define USB_STS_TMODE_SEL_MASK GENMASK(11, 10)
> +#define USB_STS_TMODE_SEL(p)   (((p) << 10) & USB_STS_TMODE_SEL_MASK)
> +/*
> + *  Send Latency Tolerance Message Device Notification TP (used only
> + *  in SS mode).
> + */
> +#define USB_CMD_SDNLTM         BIT(12)
> +/* Send Custom Transaction Packet (used only in SS mode) */
> +#define USB_CMD_SPKT           BIT(13)
> +/*Device Notification 'Function Wake' - Interface value (only in SS mode. */
> +#define USB_CMD_DNFW_INT_MASK  GENMASK(23, 16)
> +#define USB_STS_DNFW_INT(p)    (((p) << 16) & USB_CMD_DNFW_INT_MASK)
> +/*
> + * Device Notification 'Latency Tolerance Message' -373 BELT value [7:0]
> + * (used only in SS mode).
> + */
> +#define USB_CMD_DNLTM_BELT_MASK        GENMASK(27, 16)
> +#define USB_STS_DNLTM_BELT(p)  (((p) << 16) & USB_CMD_DNLTM_BELT_MASK)
> +
> +/* USB_ITPN - bitmasks */
> +/*
> + * ITP(SS) / SOF (HS/FS) number
> + * In SS mode this field represent number of last ITP received from host.
> + * In HS/FS mode this field represent number of last SOF received from host.
> + */
> +#define USB_ITPN_MASK          GENMASK(13, 0)
> +#define USB_ITPN(p)            ((p) & USB_ITPN_MASK)
> +
> +/* USB_LPM - bitmasks */
> +/* Host Initiated Resume Duration. */
> +#define USB_LPM_HIRD_MASK      GENMASK(3, 0)
> +#define USB_LPM_HIRD(p)                ((p) & USB_LPM_HIRD_MASK)
> +/* Remote Wakeup Enable (bRemoteWake). */
> +#define USB_LPM_BRW            BIT(4)
> +
> +/* USB_IEN - bitmasks */
> +/* SS connection interrupt enable */
> +#define USB_IEN_CONIEN         BIT(0)
> +/* SS disconnection interrupt enable. */
> +#define USB_IEN_DISIEN         BIT(1)
> +/* USB SS warm reset interrupt enable. */
> +#define USB_IEN_UWRESIEN       BIT(2)
> +/* USB SS hot reset interrupt enable */
> +#define USB_IEN_UHRESIEN       BIT(3)
> +/* SS link U3 state enter interrupt enable (suspend).*/
> +#define USB_IEN_U3ENTIEN       BIT(4)
> +/* SS link U3 state exit interrupt enable (wakeup). */
> +#define USB_IEN_U3EXTIEN       BIT(5)
> +/* SS link U2 state enter interrupt enable.*/
> +#define USB_IEN_U2ENTIEN       BIT(6)
> +/* SS link U2 state exit interrupt enable.*/
> +#define USB_IEN_U2EXTIEN       BIT(7)
> +/* SS link U1 state enter interrupt enable.*/
> +#define USB_IEN_U1ENTIEN       BIT(8)
> +/* SS link U1 state exit interrupt enable.*/
> +#define USB_IEN_U1EXTIEN       BIT(9)
> +/* ITP/SOF packet detected interrupt enable.*/
> +#define USB_IEN_ITPIEN         BIT(10)
> +/* Wakeup interrupt enable.*/
> +#define USB_IEN_WAKEIEN                BIT(11)
> +/* Send Custom Packet interrupt enable.*/
> +#define USB_IEN_SPKTIEN                BIT(12)
> +/* HS/FS mode connection interrupt enable.*/
> +#define USB_IEN_CON2IEN                BIT(16)
> +/* HS/FS mode disconnection interrupt enable.*/
> +#define USB_IEN_DIS2IEN                BIT(17)
> +/* USB reset (HS/FS mode) interrupt enable.*/
> +#define USB_IEN_U2RESIEN       BIT(18)
> +/* LPM L2 state enter interrupt enable.*/
> +#define USB_IEN_L2ENTIEN       BIT(20)
> +/* LPM  L2 state exit interrupt enable.*/
> +#define USB_IEN_L2EXTIEN       BIT(21)
> +/* LPM L1 state enter interrupt enable.*/
> +#define USB_IEN_L1ENTIEN       BIT(24)
> +/* LPM  L1 state exit interrupt enable.*/
> +#define USB_IEN_L1EXTIEN       BIT(25)
> +/* Configuration reset interrupt enable.*/
> +#define USB_IEN_CFGRESIEN      BIT(26)
> +/* Start of the USB SS warm reset interrupt enable.*/
> +#define USB_IEN_UWRESSIEN      BIT(28)
> +/* End of the USB SS warm reset interrupt enable.*/
> +#define USB_IEN_UWRESEIEN      BIT(29)
> +
> +#define USB_IEN_INIT  (USB_IEN_U2RESIEN | USB_ISTS_DIS2I | USB_IEN_CON2IEN \
> +                      | USB_IEN_UHRESIEN | USB_IEN_UWRESIEN | USB_IEN_DISIEN \
> +                      | USB_IEN_CONIEN | USB_IEN_U3EXTIEN | USB_IEN_L2ENTIEN \
> +                      | USB_IEN_L2EXTIEN)
> +
> +/* USB_ISTS - bitmasks */
> +/* SS Connection detected. */
> +#define USB_ISTS_CONI          BIT(0)
> +/* SS Disconnection detected. */
> +#define USB_ISTS_DISI          BIT(1)
> +/* UUSB warm reset detectede. */
> +#define USB_ISTS_UWRESI                BIT(2)
> +/* USB hot reset detected. */
> +#define USB_ISTS_UHRESI                BIT(3)
> +/* U3 link state enter detected (suspend).*/
> +#define USB_ISTS_U3ENTI                BIT(4)
> +/* U3 link state exit detected (wakeup). */
> +#define USB_ISTS_U3EXTI                BIT(5)
> +/* U2 link state enter detected.*/
> +#define USB_ISTS_U2ENTI                BIT(6)
> +/* U2 link state exit detected.*/
> +#define USB_ISTS_U2EXTI                BIT(7)
> +/* U1 link state enter detected.*/
> +#define USB_ISTS_U1ENTI                BIT(8)
> +/* U1 link state exit detected.*/
> +#define USB_ISTS_U1EXTI                BIT(9)
> +/* ITP/SOF packet detected.*/
> +#define USB_ISTS_ITPI          BIT(10)
> +/* Wakeup detected.*/
> +#define USB_ISTS_WAKEI         BIT(11)
> +/* Send Custom Packet detected.*/
> +#define USB_ISTS_SPKTI         BIT(12)
> +/* HS/FS mode connection detected.*/
> +#define USB_ISTS_CON2I         BIT(16)
> +/* HS/FS mode disconnection detected.*/
> +#define USB_ISTS_DIS2I         BIT(17)
> +/* USB reset (HS/FS mode) detected.*/
> +#define USB_ISTS_U2RESI                BIT(18)
> +/* LPM L2 state enter detected.*/
> +#define USB_ISTS_L2ENTI                BIT(20)
> +/* LPM  L2 state exit detected.*/
> +#define USB_ISTS_L2EXTI                BIT(21)
> +/* LPM L1 state enter detected.*/
> +#define USB_ISTS_L1ENTI                BIT(24)
> +/* LPM L1 state exit detected.*/
> +#define USB_ISTS_L1EXTI                BIT(25)
> +/* USB configuration reset detected.*/
> +#define USB_ISTS_CFGRESI       BIT(26)
> +/* Start of the USB warm reset detected.*/
> +#define USB_ISTS_UWRESSI       BIT(28)
> +/* End of the USB warm reset detected.*/
> +#define USB_ISTS_UWRESEI       BIT(29)
> +
> +/* USB_SEL - bitmasks */
> +#define EP_SEL_EPNO_MASK       GENMASK(3, 0)
> +/* Endpoint number. */
> +#define EP_SEL_EPNO(p)         ((p) & EP_SEL_EPNO_MASK)
> +/* Endpoint direction bit - 0 - OUT, 1 - IN. */
> +#define EP_SEL_DIR             BIT(7)
> +
> +#define select_ep_in(nr)       (EP_SEL_EPNO(p) | EP_SEL_DIR)
> +#define select_ep_out          (EP_SEL_EPNO(p))
> +
> +/* EP_TRADDR - bitmasks */
> +/* Transfer Ring address. */
> +#define EP_TRADDR_TRADDR(p)    ((p))
> +
> +/* EP_CFG - bitmasks */
> +/* Endpoint enable */
> +#define EP_CFG_ENABLE          BIT(0)
> +/*
> + *  Endpoint type.
> + * 1 - isochronous
> + * 2 - bulk
> + * 3 - interrupt
> + */
> +#define EP_CFG_EPTYPE_MASK     GENMASK(2, 1)
> +#define EP_CFG_EPTYPE(p)       (((p) << 1)  & EP_CFG_EPTYPE_MASK)
> +/* Stream support enable (only in SS mode). */
> +#define EP_CFG_STREAM_EN       BIT(3)
> +/* TDL check (only in SS mode for BULK EP). */
> +#define EP_CFG_TDL_CHK         BIT(4)
> +/* SID check (only in SS mode for BULK OUT EP). */
> +#define EP_CFG_SID_CHK         BIT(5)
> +/* DMA transfer endianness. */
> +#define EP_CFG_EPENDIAN                BIT(7)
> +/* Max burst size (used only in SS mode). */
> +#define EP_CFG_MAXBURST_MASK   GENMASK(11, 8)
> +#define EP_CFG_MAXBURST(p)     (((p) << 8) & EP_CFG_MAXBURST_MASK)
> +/* ISO max burst. */
> +#define EP_CFG_MULT_MASK       GENMASK(15, 14)
> +#define EP_CFG_MULT(p)         (((p) << 14) & EP_CFG_MULT)
> +/* ISO max burst. */
> +#define EP_CFG_MAXPKTSIZE_MASK GENMASK(26, 16)
> +#define EP_CFG_MAXPKTSIZE(p)   (((p) << 16) & EP_CFG_MAXPKTSIZE_MASK)
> +/* Max number of buffered packets. */
> +#define EP_CFG_BUFFERING_MASK  GENMASK(31, 27)
> +#define EP_CFG_BUFFERING(p)    (((p) << 27) & EP_CFG_BUFFERING_MASK)
> +
> +/* EP_CMD - bitmasks */
> +/* Endpoint reset. */
> +#define EP_CMD_EPRST           BIT(0)
> +/* Endpoint STALL set. */
> +#define EP_CMD_SSTALL          BIT(1)
> +/* Endpoint STALL clear. */
> +#define EP_CMD_CSTALL          BIT(2)
> +/* Send ERDY TP. */
> +#define EP_CMD_ERDY            BIT(3)
> +/* Request complete. */
> +#define EP_CMD_REQ_CMPL                BIT(5)
> +/* Transfer descriptor ready. */
> +#define EP_CMD_DRDY            BIT(6)
> +/* Data flush. */
> +#define EP_CMD_DFLUSH          BIT(7)
> +/*
> + * Transfer Descriptor Length write  (used only for Bulk Stream capable
> + * endpoints in SS mode).
> + */
> +#define EP_CMD_STDL            BIT(8)
> +/* Transfer Descriptor Length (used only in SS mode for bulk endpoints). */
> +#define EP_CMD_TDL_MASK                GENMASK(15, 9)
> +#define EP_CMD_TDL(p)          (((p) << 9) & EP_CMD_TDL_MASK)
> +/* ERDY Stream ID value (used in SS mode). */
> +#define EP_CMD_ERDY_SID_MASK   GENMASK(31, 16)
> +#define EP_CMD_ERDY_SID(p)     (((p) << 16) & EP_CMD_SID_MASK)
> +
> +/* EP_STS - bitmasks */
> +/* Setup transfer complete. */
> +#define EP_STS_SETUP           BIT(0)
> +/* Endpoint STALL status. */
> +#define EP_STS_STALL(p)                ((p) & BIT(1))
> +/* Interrupt On Complete. */
> +#define EP_STS_IOC             BIT(2)
> +/* Interrupt on Short Packet. */
> +#define EP_STS_ISP             BIT(3)
> +/* Transfer descriptor missing. */
> +#define EP_STS_DESCMIS         BIT(4)
> +/* Stream Rejected (used only in SS mode) */
> +#define EP_STS_STREAMR         BIT(5)
> +/* EXIT from MOVE DATA State (used only for stream transfers in SS mode). */
> +#define EP_STS_MD_EXIT         BIT(6)
> +/* TRB error. */
> +#define EP_STS_TRBERR          BIT(7)
> +/* Not ready (used only in SS mode). */
> +#define EP_STS_NRDY            BIT(8)
> +/* DMA busy. */
> +#define EP_STS_DBUSY(p)                ((p) & BIT(9))
> +/* Endpoint Buffer Empty */
> +#define EP_STS_BUFFEMPTY(p)    ((p) & BIT(10))
> +/* Current Cycle Status */
> +#define EP_STS_CCS(p)          ((p) & BIT(11))
> +/* Prime (used only in SS mode. */
> +#define EP_STS_PRIME           BIT(12)
> +/* Stream error (used only in SS mode). */
> +#define EP_STS_SIDERR          BIT(13)
> +/* OUT size mismatch. */
> +#define EP_STS_OUTSMM          BIT(14)
> +/* ISO transmission error. */
> +#define EP_STS_ISOERR          BIT(15)
> +/* Host Packet Pending (only for SS mode). */
> +#define EP_STS_HOSTPP(p)       ((p) & BIT(16))
> +/* Stream Protocol State Machine State (only for Bulk stream endpoints). */
> +#define EP_STS_SPSMST_MASK             GENMASK(18, 17)
> +#define EP_STS_SPSMST_DISABLED(p)      (((p) & EP_STS_SPSMST_MASK) >> 17)
> +#define EP_STS_SPSMST_IDLE(p)          (((p) & EP_STS_SPSMST_MASK) >> 17)
> +#define EP_STS_SPSMST_START_STREAM(p)  (((p) & EP_STS_SPSMST_MASK) >> 17)
> +#define EP_STS_SPSMST_MOVE_DATA(p)     (((p) & EP_STS_SPSMST_MASK) >> 17)
> +/* Interrupt On Transfer complete. */
> +#define EP_STS_IOT             BIT(19)
> +/* OUT queue endpoint number. */
> +#define EP_STS_OUTQ_NO_MASK    GENMASK(27, 24)
> +#define EP_STS_OUTQ_NO(p)      (((p) & EP_STS_OUTQ_NO_MASK) >> 24)
> +/* OUT queue valid flag. */
> +#define EP_STS_OUTQ_VAL_MASK   BIT(28)
> +#define EP_STS_OUTQ_VAL(p)     ((p) & EP_STS_OUTQ_VAL_MASK)
> +/* SETUP WAIT. */
> +#define EP_STS_STPWAIT         BIT(31)
> +
> +/* EP_STS_SID - bitmasks */
> +/* Stream ID (used only in SS mode). */
> +#define EP_STS_SID_MASK                GENMASK(15, 0)
> +#define EP_STS_SID(p)          ((p) & EP_STS_SID_MASK)
> +
> +/* EP_STS_EN - bitmasks */
> +/* SETUP interrupt enable. */
> +#define EP_STS_EN_SETUPEN      BIT(0)
> +/* OUT transfer missing descriptor enable. */
> +#define EP_STS_EN_DESCMISEN    BIT(4)
> +/* Stream Rejected enable. */
> +#define EP_STS_EN_STREAMREN    BIT(5)
> +/* Move Data Exit enable.*/
> +#define EP_STS_EN_MD_EXITEN    BIT(6)
> +/* TRB enable. */
> +#define EP_STS_EN_TRBERREN     BIT(7)
> +/* NRDY enable. */
> +#define EP_STS_EN_NRDYEN       BIT(8)
> +/* Prime enable. */
> +#define EP_STS_EN_PRIMEEEN     BIT(12)
> +/* Stream error enable. */
> +#define EP_STS_EN_SIDERREN     BIT(13)
> +/* OUT size mismatch enable. */
> +#define EP_STS_EN_OUTSMMEN     BIT(14)
> +/* ISO transmission error enable. */
> +#define EP_STS_EN_ISOERREN     BIT(15)
> +/* Interrupt on Transmission complete enable. */
> +#define EP_STS_EN_IOTEN                BIT(19)
> +/* Setup Wait interrupt enable. */
> +#define EP_STS_EN_STPWAITEN    BIT(31)
> +
> +/* DRBL- bitmasks */
> +#define DB_VALUE_BY_INDEX(index) (1 << (index))
> +#define DB_VALUE_EP0_OUT       BIT(0)
> +#define DB_VALUE_EP0_IN                BIT(16)
> +
> +/* EP_IEN - bitmasks */
> +#define EP_IEN(index)          (1 << (index))
> +#define EP_IEN_EP_OUT0         BIT(0)
> +#define EP_IEN_EP_IN0          BIT(16)
> +
> +/* EP_ISTS - bitmasks */
> +#define EP_ISTS(index)         (1 << (index))
> +#define EP_ISTS_EP_OUT0                BIT(0)
> +#define EP_ISTS_EP_IN0         BIT(16)
> +
> +/* EP_PWR- bitmasks */
> +/*Power Shut Off capability enable*/
> +#define PUSB_PWR_PSO_EN                BIT(0)
> +/*Power Shut Off capability disable*/
> +#define PUSB_PWR_PSO_DS                BIT(1)
> +/*
> + * Enables turning-off Reference Clock.
> + * This bit is optional and implemented only when support for OTG is
> + * implemented (indicated by OTG_READY bit set to '1').
> + */
> +#define PUSB_PWR_STB_CLK_SWITCH_EN     BIT(8)
> +/*
> + * Status bit indicating that operation required by STB_CLK_SWITCH_EN write
> + * is completed
> + */
> +#define PUSB_PWR_STB_CLK_SWITCH_DONE   BIT(9)
> +/* This bit informs if Fast Registers Access is enabled. */
> +#define PUSB_PWR_FST_REG_ACCESS_STAT   BIT(30)
> +/* Fast Registers Access Enable. */
> +#define PUSB_PWR_FST_REG_ACCESS        BIT(31)
> +
> +/* USB_CAP1- bitmasks */
> +/*
> + * SFR Interface type
> + * These field reflects type of SFR interface implemented:
> + * 0x0 - OCP
> + * 0x1 - AHB,
> + * 0x2 - PLB
> + * 0x3 - AXI
> + * 0x4-0xF - reserved
> + */
> +#define USB_CAP1_SFR_TYPE_MASK GENMASK(3, 0)
> +#define DEV_SFR_TYPE_OCP(p)    (((p) & USB_CAP1_SFR_TYPE_MASK) == 0x0)
> +#define DEV_SFR_TYPE_AHB(p)    (((p) & USB_CAP1_SFR_TYPE_MASK) == 0x1)
> +#define DEV_SFR_TYPE_PLB(p)    (((p) & USB_CAP1_SFR_TYPE_MASK) == 0x2)
> +#define DEV_SFR_TYPE_AXI(p)    (((p) & USB_CAP1_SFR_TYPE_MASK) == 0x3)
> +/*
> + * SFR Interface width
> + * These field reflects width of SFR interface implemented:
> + * 0x0 - 8 bit interface,
> + * 0x1 - 16 bit interface,
> + * 0x2 - 32 bit interface
> + * 0x3 - 64 bit interface
> + * 0x4-0xF - reserved
> + */
> +#define USB_CAP1_SFR_WIDTH_MASK        GENMASK(7, 4)
> +#define DEV_SFR_WIDTH_8(p)     (((p) & USB_CAP1_SFR_WIDTH_MASK) == (0x0 << 4))
> +#define DEV_SFR_WIDTH_16(p)    (((p) & USB_CAP1_SFR_WIDTH_MASK) == (0x1 << 4))
> +#define DEV_SFR_WIDTH_32(p)    (((p) & USB_CAP1_SFR_WIDTH_MASK) == (0x2 << 4))
> +#define DEV_SFR_WIDTH_64(p)    (((p) & USB_CAP1_SFR_WIDTH_MASK) == (0x3 << 4))
> +/*
> + * DMA Interface type
> + * These field reflects type of DMA interface implemented:
> + * 0x0 - OCP
> + * 0x1 - AHB,
> + * 0x2 - PLB
> + * 0x3 - AXI
> + * 0x4-0xF - reserved
> + */
> +#define USB_CAP1_DMA_TYPE_MASK GENMASK(11, 8)
> +#define DEV_DMA_TYPE_OCP(p)    (((p) & USB_CAP1_DMA_TYPE_MASK) == (0x0 << 8))
> +#define DEV_DMA_TYPE_AHB(p)    (((p) & USB_CAP1_DMA_TYPE_MASK) == (0x1 << 8))
> +#define DEV_DMA_TYPE_PLB(p)    (((p) & USB_CAP1_DMA_TYPE_MASK) == (0x2 << 8))
> +#define DEV_DMA_TYPE_AXI(p)    (((p) & USB_CAP1_DMA_TYPE_MASK) == (0x3 << 8))
> +/*
> + * DMA Interface width
> + * These field reflects width of DMA interface implemented:
> + * 0x0 - reserved,
> + * 0x1 - reserved,
> + * 0x2 - 32 bit interface
> + * 0x3 - 64 bit interface
> + * 0x4-0xF - reserved
> + */
> +#define USB_CAP1_DMA_WIDTH_MASK        GENMASK(15, 12)
> +#define DEV_DMA_WIDTH_32(p)    (((p) & USB_CAP1_DMA_WIDTH_MASK) == (0x2 << 12))
> +#define DEV_DMA_WIDTH_64(p)    (((p) & USB_CAP1_DMA_WIDTH_MASK) == (0x3 << 12))
> +/*
> + * USB3 PHY Interface type
> + * These field reflects type of USB3 PHY interface implemented:
> + * 0x0 - USB PIPE,
> + * 0x1 - RMMI,
> + * 0x2-0xF - reserved
> + */
> +#define USB_CAP1_U3PHY_TYPE_MASK GENMASK(19, 16)
> +#define DEV_U3PHY_PIPE(p) (((p) & USB_CAP1_U3PHY_TYPE_MASK) == (0x0 << 16))
> +#define DEV_U3PHY_RMMI(p) (((p) & USB_CAP1_U3PHY_TYPE_MASK) == (0x1 << 16))
> +/*
> + * USB3 PHY Interface width
> + * These field reflects width of USB3 PHY interface implemented:
> + * 0x0 - 8 bit PIPE interface,
> + * 0x1 - 16 bit PIPE interface,
> + * 0x2 - 32 bit PIPE interface,
> + * 0x3 - 64 bit PIPE interface
> + * 0x4-0xF - reserved
> + * Note: When SSIC interface is implemented this field shows the width of
> + * internal PIPE interface. The RMMI interface is always 20bit wide.
> + */
> +#define USB_CAP1_U3PHY_WIDTH_MASK GENMASK(23, 20)
> +#define DEV_U3PHY_WIDTH_8(p) \
> +       (((p) & USB_CAP1_U3PHY_WIDTH_MASK) == (0x0 << 20))
> +#define DEV_U3PHY_WIDTH_16(p) \
> +       (((p) & USB_CAP1_U3PHY_WIDTH_MASK) == (0x1 << 16))
> +#define DEV_U3PHY_WIDTH_32(p) \
> +       (((p) & USB_CAP1_U3PHY_WIDTH_MASK) == (0x2 << 20))
> +#define DEV_U3PHY_WIDTH_64(p) \
> +       (((p) & USB_CAP1_U3PHY_WIDTH_MASK) == (0x3 << 16))
> +
> +/*
> + * USB2 PHY Interface enable
> + * These field informs if USB2 PHY interface is implemented:
> + * 0x0 - interface NOT implemented,
> + * 0x1 - interface implemented
> + */
> +#define USB_CAP1_U2PHY_EN(p)   ((p) & BIT(24))
> +/*
> + * USB2 PHY Interface type
> + * These field reflects type of USB2 PHY interface implemented:
> + * 0x0 - UTMI,
> + * 0x1 - ULPI
> + */
> +#define DEV_U2PHY_ULPI(p)      ((p) & BIT(25))
> +/*
> + * USB2 PHY Interface width
> + * These field reflects width of USB2 PHY interface implemented:
> + * 0x0 - 8 bit interface,
> + * 0x1 - 16 bit interface,
> + * Note: The ULPI interface is always 8bit wide.
> + */
> +#define DEV_U2PHY_WIDTH_16(p)  ((p) & BIT(26))
> +/*
> + * OTG Ready
> + * 0x0 - pure device mode
> + * 0x1 - some features and ports for CDNS USB OTG controller are implemented.
> + */
> +#define USB_CAP1_OTG_READY(p)  ((p) & BIT(27))
> +
> +/* USB_CAP2- bitmasks */
> +/*
> + * The actual size of the connected On-chip RAM memory in kB:
> + * - 0 means 256 kB (max supported mem size)
> + * - value other than 0 reflects the mem size in kB
> + */
> +#define USB_CAP2_ACTUAL_MEM_SIZE(p) ((p) & GENMASK(7, 0))
> +/*
> + * Max supported mem size
> + * These field reflects width of on-chip RAM address bus width,
> + * which determines max supported mem size:
> + * 0x0-0x7 - reserved,
> + * 0x8 - support for 4kB mem,
> + * 0x9 - support for 8kB mem,
> + * 0xA - support for 16kB mem,
> + * 0xB - support for 32kB mem,
> + * 0xC - support for 64kB mem,
> + * 0xD - support for 128kB mem,
> + * 0xE - support for 256kB mem,
> + * 0xF - reserved
> + */
> +#define USB_CAP2_MAX_MEM_SIZE(p) ((p) & GENMASK(11, 8))
> +
> +/* USB_CAP3- bitmasks */
> +#define EP_IS_IMPLEMENTED(reg, index) ((reg) & (1 << (index)))
> +
> +/* USB_CAP4- bitmasks */
> +#define EP_SUPPORT_ISO(reg, index) ((reg) & (1 << (index)))
> +
> +/* USB_CAP5- bitmasks */
> +#define EP_SUPPORT_STREAM(reg, index) ((reg) & (1 << (index)))
> +
> +/* USB_CAP6- bitmasks */
> +/* The USBSS-DEV Controller  Internal build number. */
> +#define GET_DEV_VERSION__INTERNAL_NUMBER(p) ((p) & GENMASK(7, 0))
> +/* The USBSS-DEV Controller  version number. */
> +#define GET_DEV_VERSION(p) ((p) & GENMASK(31, 8))
> +
> +/* DBG_LINK1- bitmasks */
> +/*
> + * LFPS_MIN_DET_U1_EXIT value This parameter configures the minimum
> + * time required for decoding the received LFPS as an LFPS.U1_Exit.
> + */
> +#define DBG_LINK1_LFPS_MIN_DET_U1_EXIT(p)      ((p) & GENMASK(7, 0))
> +/*
> + * LFPS_MIN_GEN_U1_EXIT value This parameter configures the minimum time for
> + * phytxelecidle deassertion when LFPS.U1_Exit
> + */
> +#define DBG_LINK1_LFPS_MIN_GEN_U1_EXIT(p)      (((p) << 8) & GENMASK(15, 8))
> +/*
> + * RXDET_BREAK_DIS value This parameter configures terminating the Far-end
> + * Receiver termination detection sequence:
> + * 0: it is possible that USBSS_DEV will terminate Farend receiver
> + *    termination detection sequence
> + * 1: USBSS_DEV will not terminate Far-end receiver termination
> + *    detection sequence
> + */
> +#define DBG_LINK1_RXDET_BREAK_DIS              BIT(16)
> +/* LFPS_GEN_PING value This parameter configures the LFPS.Ping generation */
> +#define DBG_LINK1_LFPS_GEN_PING(p)             (((p) << 17) & GENMASK(21, 17))
> +/*
> + * Set the LFPS_MIN_DET_U1_EXIT value Writing '1' to this bit writes the
> + * LFPS_MIN_DET_U1_EXIT field value to the device. This bit is automatically
> + * cleared. Writing '0' has no effect
> + */
> +#define DBG_LINK1_LFPS_MIN_DET_U1_EXIT_SET     BIT(24)
> +/*
> + * Set the LFPS_MIN_GEN_U1_EXIT value. Writing '1' to this bit writes the
> + * LFPS_MIN_GEN_U1_EXIT field value to the device. This bit is automatically
> + * cleared. Writing '0' has no effect
> + */
> +#define DBG_LINK1_LFPS_MIN_GEN_U1_EXIT_SET     BIT(25)
> +/*
> + * Set the RXDET_BREAK_DIS value Writing '1' to this bit writes
> + * the RXDET_BREAK_DIS field value to the device. This bit is automatically
> + * cleared. Writing '0' has no effect
> + */
> +#define DBG_LINK1_RXDET_BREAK_DIS_SET          BIT(26)
> +/*
> + * Set the LFPS_GEN_PING_SET value Writing '1' to this bit writes
> + * the LFPS_GEN_PING field value to the device. This bit is automatically
> + * cleared. Writing '0' has no effect."
> + */
> +#define DBG_LINK1_LFPS_GEN_PING_SET            BIT(27)
> +
> +#define gadget_to_cdns3_device(g) (container_of(g, struct cdns3_device, gadget))
> +
> +#define ep_to_cdns3_ep(ep) (container_of(ep, struct cdns3_endpoint, endpoint))
> +
> +/*-------------------------------------------------------------------------*/
> +/*
> + * USBSS-DEV DMA interface.
> + */
> +#define TRBS_PER_SEGMENT       40
> +
> +#define ISO_MAX_INTERVAL       10
> +
> +/*
> + *Only for ISOC endpoints - maximum number of TRBs is calculated as
> + * pow(2, bInterval-1) * number of usb requests. It is limitation made by
> + * driver to save memory. Controller must prepare TRB for each ITP even
> + * if bInterval > 1. It's the reason why driver needs so many TRBs for
> + * isochronous endpoints.
> + */
> +#define TRBS_PER_ISOC_SEGMENT  (ISO_MAX_INTERVAL * 8)
> +
> +#define GET_TRBS_PER_SEGMENT(ep_type) ((ep_type) == USB_ENDPOINT_XFER_ISOC ? \
> +                                     TRBS_PER_ISOC_SEGMENT : TRBS_PER_SEGMENT)
> +/**
> + * struct cdns3_trb - represent Transfer Descriptor block.
> + * @buffer:    pointer to buffer data
> + * @length:    length of data
> + * @control:   control flags.
> + *
> + * This structure describes transfer block serviced by DMA module.
> + */
> +struct cdns3_trb {
> +       __le32 buffer;
> +       __le32 length;
> +       __le32 control;
> +};
> +
> +#define TRB_SIZE               (sizeof(struct cdns3_trb))
> +#define TRB_RING_SIZE          (TRB_SIZE * TRBS_PER_SEGMENT)
> +#define TRB_ISO_RING_SIZE      (TRB_SIZE * TRBS_PER_ISOC_SEGMENT)
> +#define TRB_CTRL_RING_SIZE     (TRB_SIZE * 2)
> +
> +/* TRB bit mask */
> +#define TRB_TYPE_BITMASK       GENMASK(15, 10)
> +#define TRB_TYPE(p)            ((p) << 10)
> +#define TRB_FIELD_TO_TYPE(p)   (((p) & TRB_TYPE_BITMASK) >> 10)
> +
> +/* TRB type IDs */
> +/* bulk, interrupt, isoc , and control data stage */
> +#define TRB_NORMAL             1
> +/* TRB for linking ring segments */
> +#define TRB_LINK               6
> +
> +/* Cycle bit - indicates TRB ownership by driver or hw*/
> +#define TRB_CYCLE              BIT(0)
> +/*
> + * When set to '1', the device will toggle its interpretation of the Cycle bit
> + */
> +#define TRB_TOGGLE             BIT(1)
> +
> +/* Interrupt on short packet*/
> +#define TRB_ISP                        BIT(2)
> +/*Setting this bit enables FIFO DMA operation mode*/
> +#define TRB_FIFO_MODE          BIT(3)
> +/* Set PCIe no snoop attribute */
> +#define TRB_CHAIN              BIT(4)
> +/* Interrupt on completion */
> +#define TRB_IOC                        BIT(5)
> +
> +/* stream ID bitmasks. */
> +#define TRB_STREAM_ID(p)       ((p) & GENMASK(31, 16))
> +
> +/* transfer_len bitmasks. */
> +#define TRB_LEN(p)             ((p) & GENMASK(16, 0))
> +
> +/* transfer_len bitmasks - bits 31:24 */
> +#define TRB_BURST_LEN(p)       ((p) & GENMASK(31, 24))
> +
> +/* Data buffer pointer bitmasks*/
> +#define TRB_BUFFER(p)          ((p) & GENMASK(31, 0))
> +
> +/*-------------------------------------------------------------------------*/
> +/* Driver numeric constants */
> +
> +/* Such declaration should be added to ch9.h */
> +#define USB_DEVICE_MAX_ADDRESS         127
> +
> +/* Endpoint init values */
> +#define CDNS3_EP_MAX_PACKET_LIMIT      1024
> +#define CDNS3_EP_MAX_STREAMS           15
> +#define CDNS3_EP0_MAX_PACKET_LIMIT     512
> +
> +/* All endpoints including EP0 */
> +#define CDNS3_ENDPOINTS_MAX_COUNT      32
> +#define CDNS3_EP_ZLP_BUF_SIZE          1024
> +
> +#define CDNS3_EP_BUF_SIZE              2       /* KB */
> +#define CDNS3_ALIGNED_BUF_SIZE         16384   /* Bytes */
> +#define CDNS3_MAX_NUM_DESCMISS_BUF     32
> +#define CDNS3_DESCMIS_BUF_SIZE         2048    /* Bytes */
> +/*-------------------------------------------------------------------------*/
> +/* Used structs */
> +
> +struct cdns3_device;
> +
> +/**
> + * struct cdns3_endpoint - extended device side representation of USB endpoint.
> + * @endpoint: usb endpoint
> + * @pending_req_list: list of requests queuing on transfer ring.
> + * @deferred_req_list: list of requests waiting for queuing on transfer ring.
> + * @descmiss_req_list: list of requests internally allocated by driver (WA1).
> + * @trb_pool: transfer ring - array of transaction buffers
> + * @trb_pool_dma: dma address of transfer ring
> + * @cdns3_dev: device associated with this endpoint
> + * @name: a human readable name e.g. ep1out
> + * @flags: specify the current state of endpoint
> + * @descmis_req: internal transfer object used for getting data from on-chip
> + *     buffer. It can happen only if function driver doesn't send usb_request
> + *     object on time.
> + * @aligned_buff: aligned to 8 bytes data buffer. Buffer address used in
> + *     TRB shall be aligned to 8.
> + * @aligned_dma_addr: dma address of aligned_buff
> + * @dir: endpoint direction
> + * @num: endpoint number (1 - 15)
> + * @type: set to bmAttributes & USB_ENDPOINT_XFERTYPE_MASK
> + * @interval: interval between packets used for ISOC endpoint.
> + * @free_trbs: number of free TRBs in transfer ring
> + * @num_trbs: number of all TRBs in transfer ring
> + * @pcs: producer cycle state
> + * @ccs: consumer cycle state
> + * @enqueue: enqueue index in transfer ring
> + * @dequeue: dequeue index in transfer ring
> + */
> +struct cdns3_endpoint {
> +       struct usb_ep           endpoint;
> +       struct list_head        pending_req_list;
> +       struct list_head        deferred_req_list;
> +       struct list_head        descmiss_req_list;
> +
> +       struct cdns3_trb        *trb_pool;
> +       dma_addr_t              trb_pool_dma;
> +
> +       struct cdns3_device     *cdns3_dev;
> +       char                    name[20];
> +
> +#define EP_ENABLED             BIT(0)
> +#define EP_STALL               BIT(1)
> +#define EP_WEDGE               BIT(2)
> +#define EP_TRANSFER_STARTED    BIT(3)
> +#define EP_UPDATE_EP_TRBADDR   BIT(4)
> +#define EP_PENDING_REQUEST     BIT(5)
> +#define EP_RING_FULL           BIT(6)
> +#define EP_CLAIMED             BIT(7)
> +#define EP_QUIRK_EXTRA_BUF_DET BIT(8)
> +#define EP_QUIRK_EXTRA_BUF_EN  BIT(9)
> +#define EP_QUIRK_END_TRANSFER  BIT(10)
> +
> +       u32                     flags;
> +
> +       struct cdns3_request    *descmis_req;
> +
> +       void                    *aligned_buff;
> +       dma_addr_t              aligned_dma_addr;
> +       u8                      dir;
> +       u8                      num;
> +       u8                      type;
> +       int                     interval;
> +
> +       int                     free_trbs;
> +       int                     num_trbs;
> +       u8                      pcs;
> +       u8                      ccs;
> +       int                     enqueue;
> +       int                     dequeue;
> +};
> +
> +/**
> + * struct cdns3_request - extended device side representation of usb_request
> + *                        object .
> + * @request: generic usb_request object describing single I/O request.
> + * @priv_ep: extended representation of usb_ep object
> + * @trb: the first TRB association with this request
> + * @start_trb: number of the first TRB in transfer ring
> + * @end_trb: number of the last TRB in transfer ring
> + * @flags: flag specifying special usage of request
> + * @list: used by internally allocated request to add to descmiss_req_list.
> + */
> +struct cdns3_request {
> +       struct usb_request      request;
> +       struct cdns3_endpoint   *priv_ep;
> +       struct cdns3_trb        *trb;
> +       int                     start_trb;
> +       int                     end_trb;
> +#define REQUEST_PENDING                BIT(0)
> +#define REQUEST_INTERNAL       BIT(1)
> +#define REQUEST_INTERNAL_CH    BIT(2)
> +#define REQUEST_ZLP            BIT(3)
> +       u32                     flags;
> +       struct list_head        list;
> +};
> +
> +#define to_cdns3_request(r) (container_of(r, struct cdns3_request, request))
> +
> +/*Stages used during enumeration process.*/
> +#define CDNS3_SETUP_STAGE              0x0
> +#define CDNS3_DATA_STAGE               0x1
> +#define CDNS3_STATUS_STAGE             0x2
> +
> +/**
> + * struct cdns3_device - represent USB device.
> + * @dev: pointer to device structure associated whit this controller
> + * @sysdev: pointer to the DMA capable device
> + * @gadget: device side representation of the peripheral controller
> + * @gadget_driver: pointer to the gadget driver
> + * @dev_ver: device controller version.
> + * @lock: for synchronizing
> + * @regs: base address for device side registers
> + * @setup_buf: used while processing usb control requests
> + * @setup_dma: dma address for setup_buf
> + * @zlp_buf - zlp buffer
> + * @ep0_stage: ep0 stage during enumeration process.
> + * @ep0_data_dir: direction for control transfer
> + * @eps: array of pointers to all endpoints with exclusion ep0
> + * @selected_ep: actually selected endpoint. It's used only to improve
> + *               performance.
> + * @isoch_delay: value from Set Isoch Delay request. Only valid on SS/SSP.
> + * @u1_allowed: allow device transition to u1 state
> + * @u2_allowed: allow device transition to u2 state
> + * @is_selfpowered: device is self powered
> + * @setup_pending: setup packet is processing by gadget driver
> + * @hw_configured_flag: hardware endpoint configuration was set.
> + * @wake_up_flag: allow device to remote up the host
> + * @status_completion_no_call: indicate that driver is waiting for status s
> + *     stage completion. It's used in deferred SET_CONFIGURATION request.
> + * @onchip_mem_allocated_size: actual size of on-chip memory assigned
> + *     to endpoints
> + * @pending_status_wq: workqueue handling status stage for deferred requests.
> + * @pending_status_request: request for which status stage was deferred
> + */
> +struct cdns3_device {
> +       struct device                   *dev;
> +       struct device                   *sysdev;
> +
> +       struct usb_gadget               gadget;
> +       struct usb_gadget_driver        *gadget_driver;
> +
> +#define CDNS_REVISION_V0               0x00024501
> +#define CDNS_REVISION_V1               0x00024509
> +       u32                             dev_ver;
> +
> +       /* generic spin-lock for drivers */
> +       spinlock_t                      lock;
> +
> +       struct cdns3_usb_regs           __iomem *regs;
> +
> +       struct usb_ctrlrequest          *setup_buf;
> +       dma_addr_t                      setup_dma;
> +       void                            *zlp_buf;
> +
> +       u8                              ep0_stage;
> +       int                             ep0_data_dir;
> +
> +       struct cdns3_endpoint           *eps[CDNS3_ENDPOINTS_MAX_COUNT];
> +
> +       u32                             selected_ep;
> +       u16                             isoch_delay;
> +
> +       unsigned                        u1_allowed:1;
> +       unsigned                        u2_allowed:1;
> +       unsigned                        is_selfpowered:1;
> +       unsigned                        setup_pending:1;
> +       int                             hw_configured_flag:1;
> +       int                             wake_up_flag:1;
> +       unsigned                        status_completion_no_call:1;
> +
> +       struct work_struct              pending_status_wq;
> +       struct usb_request              *pending_status_request;
> +
> +       /*in KB */
> +       int                             onchip_mem_allocated_size;
> +};
> +
> +int cdns3_handshake(void __iomem *ptr, u32 mask, u32 done, int usec);
> +void cdns3_set_register_bit(void __iomem *ptr, u32 mask);
> +dma_addr_t cdns3_trb_virt_to_dma(struct cdns3_endpoint *priv_ep,
> +                                struct cdns3_trb *trb);
> +enum usb_device_speed cdns3_get_speed(struct cdns3_device *priv_dev);
> +void cdns3_pending_setup_status_handler(struct work_struct *work);
> +void cdns3_hw_reset_eps_config(struct cdns3_device *priv_dev);
> +void cdns3_set_hw_configuration(struct cdns3_device *priv_dev);
> +void cdns3_select_ep(struct cdns3_device *priv_dev, u32 ep);
> +void cdns3_allow_enable_l1(struct cdns3_device *priv_dev, int enable);
> +struct usb_request *cdns3_next_request(struct list_head *list);
> +int cdns3_ep_run_transfer(struct cdns3_endpoint *priv_ep,
> +                         struct usb_request *request);
> +int cdns3_allocate_trb_pool(struct cdns3_endpoint *priv_ep);
> +u8 cdns3_ep_addr_to_index(u8 ep_addr);
> +int cdns3_gadget_ep_set_wedge(struct usb_ep *ep);
> +int cdns3_gadget_ep_set_halt(struct usb_ep *ep, int value);
> +struct usb_request *cdns3_gadget_ep_alloc_request(struct usb_ep *ep,
> +                                                 gfp_t gfp_flags);
> +void cdns3_gadget_ep_free_request(struct usb_ep *ep,
> +                                 struct usb_request *request);
> +int cdns3_gadget_ep_dequeue(struct usb_ep *ep, struct usb_request *request);
> +void cdns3_gadget_giveback(struct cdns3_endpoint *priv_ep,
> +                          struct cdns3_request *priv_req,
> +                          int status);
> +
> +int cdns3_init_ep0(struct cdns3_device *priv_dev,
> +                  struct cdns3_endpoint *priv_ep);
> +void cdns3_ep0_config(struct cdns3_device *priv_dev);
> +void cdns3_ep_config(struct cdns3_endpoint *priv_ep);
> +void cdns3_check_ep0_interrupt_proceed(struct cdns3_device *priv_dev, int dir);
> +
> +#endif /* __LINUX_CDNS3_GADGET */
> diff --git a/drivers/usb/cdns3/host-export.h b/drivers/usb/cdns3/host-export.h
> new file mode 100644
> index 000000000000..b498a170b7e8
> --- /dev/null
> +++ b/drivers/usb/cdns3/host-export.h
> @@ -0,0 +1,28 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +/*
> + * Cadence USBSS DRD Driver - Host Export APIs
> + *
> + * Copyright (C) 2017-2018 NXP
> + *
> + * Authors: Peter Chen <peter.chen@nxp.com>
> + */
> +#ifndef __LINUX_CDNS3_HOST_EXPORT
> +#define __LINUX_CDNS3_HOST_EXPORT
> +
> +#ifdef CONFIG_USB_CDNS3_HOST
> +
> +int cdns3_host_init(struct cdns3 *cdns);
> +void cdns3_host_exit(struct cdns3 *cdns);
> +
> +#else
> +
> +static inline int cdns3_host_init(struct cdns3 *cdns)
> +{
> +       return -ENXIO;
> +}
> +
> +static inline void cdns3_host_exit(struct cdns3 *cdns) { }
> +
> +#endif /* CONFIG_USB_CDNS3_HOST */
> +
> +#endif /* __LINUX_CDNS3_HOST_EXPORT */
> diff --git a/drivers/usb/cdns3/host.c b/drivers/usb/cdns3/host.c
> new file mode 100644
> index 000000000000..b43b0236a885
> --- /dev/null
> +++ b/drivers/usb/cdns3/host.c
> @@ -0,0 +1,72 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Cadence USBSS DRD Driver - host side
> + *
> + * Copyright (C) 2018 Cadence Design Systems.
> + * Copyright (C) 2017-2018 NXP
> + *
> + * Authors: Peter Chen <peter.chen@nxp.com>
> + *         Pawel Laszczak <pawell@cadence.com>
> + */
> +
> +#include <linux/platform_device.h>
> +#include "core.h"
> +
> +static int __cdns3_host_init(struct cdns3 *cdns)
> +{
> +       struct platform_device *xhci;
> +       int ret;
> +
> +       xhci = platform_device_alloc("xhci-hcd", PLATFORM_DEVID_AUTO);
> +       if (!xhci) {
> +               dev_err(cdns->dev, "couldn't allocate xHCI device\n");
> +               return -ENOMEM;
> +       }
> +
> +       xhci->dev.parent = cdns->dev;
> +       cdns->host_dev = xhci;
> +
> +       ret = platform_device_add_resources(xhci, cdns->xhci_res,
> +                                           CDNS3_XHCI_RESOURCES_NUM);
> +       if (ret) {
> +               dev_err(cdns->dev, "couldn't add resources to xHCI device\n");
> +               goto err1;
> +       }
> +
> +       ret = platform_device_add(xhci);
> +       if (ret) {
> +               dev_err(cdns->dev, "failed to register xHCI device\n");
> +               goto err1;
> +       }
> +
> +       return 0;
> +err1:
> +       platform_device_put(xhci);
> +       return ret;
> +}
> +
> +static void cdns3_host_exit(struct cdns3 *cdns)
> +{
> +       platform_device_unregister(cdns->host_dev);
> +       cdns->host_dev = NULL;
> +}
> +
> +int cdns3_host_init(struct cdns3 *cdns)
> +{
> +       struct cdns3_role_driver *rdrv;
> +
> +       rdrv = devm_kzalloc(cdns->dev, sizeof(*rdrv), GFP_KERNEL);
> +       if (!rdrv)
> +               return -ENOMEM;
> +
> +       rdrv->start     = __cdns3_host_init;
> +       rdrv->stop      = cdns3_host_exit;
> +       rdrv->state     = CDNS3_ROLE_STATE_INACTIVE;
> +       rdrv->suspend   = NULL;
> +       rdrv->resume    = NULL;
> +       rdrv->name      = "host";
> +
> +       cdns->roles[CDNS3_ROLE_HOST] = rdrv;
> +
> +       return 0;
> +}
> diff --git a/drivers/usb/cdns3/trace.c b/drivers/usb/cdns3/trace.c
> new file mode 100644
> index 000000000000..587ae08e019d
> --- /dev/null
> +++ b/drivers/usb/cdns3/trace.c
> @@ -0,0 +1,11 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * USBSS device controller driver Trace Support
> + *
> + * Copyright (C) 2018 Cadence.
> + *
> + * Author: Pawel Laszczak <pawell@cadence.com>
> + */
> +
> +#define CREATE_TRACE_POINTS
> +#include "trace.h"
> diff --git a/drivers/usb/cdns3/trace.h b/drivers/usb/cdns3/trace.h
> new file mode 100644
> index 000000000000..f3d7a91fae86
> --- /dev/null
> +++ b/drivers/usb/cdns3/trace.h
> @@ -0,0 +1,389 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +/*
> + * USBSS device controller driver.
> + * Trace support header file.
> + *
> + * Copyright (C) 2018 Cadence.
> + *
> + * Author: Pawel Laszczak <pawell@cadence.com>
> + */
> +
> +#undef TRACE_SYSTEM
> +#define TRACE_SYSTEM cdns3
> +
> +#if !defined(__LINUX_CDNS3_TRACE) || defined(TRACE_HEADER_MULTI_READ)
> +#define __LINUX_CDNS3_TRACE
> +
> +#include <linux/types.h>
> +#include <linux/tracepoint.h>
> +#include <asm/byteorder.h>
> +#include <linux/usb/ch9.h>
> +#include "core.h"
> +#include "gadget.h"
> +#include "debug.h"
> +
> +#define CDNS3_MSG_MAX  500
> +
> +DECLARE_EVENT_CLASS(cdns3_log_doorbell,
> +       TP_PROTO(const char *ep_name, u32 ep_trbaddr),
> +       TP_ARGS(ep_name, ep_trbaddr),
> +       TP_STRUCT__entry(
> +               __string(name, ep_name)
> +               __field(u32, ep_trbaddr)
> +       ),
> +       TP_fast_assign(
> +               __assign_str(name, ep_name);
> +               __entry->ep_trbaddr = ep_trbaddr;
> +       ),
> +       TP_printk("//Ding Dong %s, ep_trbaddr %08x", __get_str(name),
> +                 __entry->ep_trbaddr)
> +);
> +
> +DEFINE_EVENT(cdns3_log_doorbell, cdns3_doorbell_ep0,
> +       TP_PROTO(const char *ep_name, u32 ep_trbaddr),
> +       TP_ARGS(ep_name, ep_trbaddr)
> +);
> +
> +DEFINE_EVENT(cdns3_log_doorbell, cdns3_doorbell_epx,
> +       TP_PROTO(const char *ep_name, u32 ep_trbaddr),
> +       TP_ARGS(ep_name, ep_trbaddr)
> +);
> +
> +DECLARE_EVENT_CLASS(cdns3_log_usb_irq,
> +       TP_PROTO(struct cdns3_device *priv_dev, u32 usb_ists),
> +       TP_ARGS(priv_dev, usb_ists),
> +       TP_STRUCT__entry(
> +               __field(enum usb_device_speed, speed)
> +               __field(u32, usb_ists)
> +               __dynamic_array(char, str, CDNS3_MSG_MAX)
> +       ),
> +       TP_fast_assign(
> +               __entry->speed = cdns3_get_speed(priv_dev);
> +               __entry->usb_ists = usb_ists;
> +       ),
> +       TP_printk("%s", cdns3_decode_usb_irq(__get_str(str), __entry->speed,
> +                                            __entry->usb_ists))
> +);
> +
> +DEFINE_EVENT(cdns3_log_usb_irq, cdns3_usb_irq,
> +       TP_PROTO(struct cdns3_device *priv_dev, u32 usb_ists),
> +       TP_ARGS(priv_dev, usb_ists)
> +);
> +
> +DECLARE_EVENT_CLASS(cdns3_log_epx_irq,
> +       TP_PROTO(struct cdns3_device *priv_dev, struct cdns3_endpoint *priv_ep),
> +       TP_ARGS(priv_dev, priv_ep),
> +       TP_STRUCT__entry(
> +               __string(ep_name, priv_ep->name)
> +               __field(u32, ep_sts)
> +               __field(u32, ep_traddr)
> +               __dynamic_array(char, str, CDNS3_MSG_MAX)
> +       ),
> +       TP_fast_assign(
> +               __assign_str(ep_name, priv_ep->name);
> +               __entry->ep_sts = readl(&priv_dev->regs->ep_sts);
> +               __entry->ep_traddr = readl(&priv_dev->regs->ep_traddr);
> +       ),
> +       TP_printk("%s, ep_traddr: %08x",
> +                 cdns3_decode_epx_irq(__get_str(str),
> +                                      __get_str(ep_name),
> +                                      __entry->ep_sts),
> +                 __entry->ep_traddr)
> +);
> +
> +DEFINE_EVENT(cdns3_log_epx_irq, cdns3_epx_irq,
> +       TP_PROTO(struct cdns3_device *priv_dev, struct cdns3_endpoint *priv_ep),
> +       TP_ARGS(priv_dev, priv_ep)
> +);
> +
> +DECLARE_EVENT_CLASS(cdns3_log_ep0_irq,
> +       TP_PROTO(struct cdns3_device *priv_dev,  u32 ep_sts),
> +       TP_ARGS(priv_dev, ep_sts),
> +       TP_STRUCT__entry(
> +               __field(int, ep_dir)
> +               __field(u32, ep_sts)
> +               __dynamic_array(char, str, CDNS3_MSG_MAX)
> +       ),
> +       TP_fast_assign(
> +               __entry->ep_dir = priv_dev->ep0_data_dir;
> +               __entry->ep_sts = ep_sts;
> +       ),
> +       TP_printk("%s", cdns3_decode_ep0_irq(__get_str(str),
> +                                            __entry->ep_dir,
> +                                            __entry->ep_sts))
> +);
> +
> +DEFINE_EVENT(cdns3_log_ep0_irq, cdns3_ep0_irq,
> +       TP_PROTO(struct cdns3_device *priv_dev, u32 ep_sts),
> +       TP_ARGS(priv_dev, ep_sts)
> +);
> +
> +DECLARE_EVENT_CLASS(cdns3_log_ctrl,
> +       TP_PROTO(struct usb_ctrlrequest *ctrl),
> +       TP_ARGS(ctrl),
> +       TP_STRUCT__entry(
> +               __field(u8, bRequestType)
> +               __field(u8, bRequest)
> +               __field(u16, wValue)
> +               __field(u16, wIndex)
> +               __field(u16, wLength)
> +               __dynamic_array(char, str, CDNS3_MSG_MAX)
> +       ),
> +       TP_fast_assign(
> +               __entry->bRequestType = ctrl->bRequestType;
> +               __entry->bRequest = ctrl->bRequest;
> +               __entry->wValue = le16_to_cpu(ctrl->wValue);
> +               __entry->wIndex = le16_to_cpu(ctrl->wIndex);
> +               __entry->wLength = le16_to_cpu(ctrl->wLength);
> +       ),
> +       TP_printk("%s", usb_decode_ctrl(__get_str(str), __entry->bRequestType,
> +                                       __entry->bRequest, __entry->wValue,
> +                                       __entry->wIndex, __entry->wLength)
> +       )
> +);
> +
> +DEFINE_EVENT(cdns3_log_ctrl, cdns3_ctrl_req,
> +       TP_PROTO(struct usb_ctrlrequest *ctrl),
> +       TP_ARGS(ctrl)
> +);
> +
> +DECLARE_EVENT_CLASS(cdns3_log_request,
> +       TP_PROTO(struct cdns3_request *req),
> +       TP_ARGS(req),
> +       TP_STRUCT__entry(
> +               __string(name, req->priv_ep->name)
> +               __field(struct cdns3_request *, req)
> +               __field(unsigned int, actual)
> +               __field(unsigned int, length)
> +               __field(int, status)
> +               __field(int, zero)
> +               __field(int, short_not_ok)
> +               __field(int, no_interrupt)
> +               __field(int, start_trb)
> +               __field(int, end_trb)
> +               __field(struct cdns3_trb *, start_trb_addr)
> +               __field(int, flags)
> +       ),
> +       TP_fast_assign(
> +               __assign_str(name, req->priv_ep->name);
> +               __entry->req = req;
> +               __entry->actual = req->request.actual;
> +               __entry->length = req->request.length;
> +               __entry->status = req->request.status;
> +               __entry->zero = req->request.zero;
> +               __entry->short_not_ok = req->request.short_not_ok;
> +               __entry->no_interrupt = req->request.no_interrupt;
> +               __entry->start_trb = req->start_trb;
> +               __entry->end_trb = req->end_trb;
> +               __entry->start_trb_addr = req->trb;
> +               __entry->flags = req->flags;
> +       ),
> +       TP_printk("%s: req: %p, length: %u/%u %s%s%s, status: %d,"
> +                 " trb: [start:%d, end:%d: virt addr %pa], flags:%x ",
> +               __get_str(name), __entry->req, __entry->actual, __entry->length,
> +               __entry->zero ? "zero | " : "",
> +               __entry->short_not_ok ? "short | " : "",
> +               __entry->no_interrupt ? "no int" : "",
> +               __entry->status,
> +               __entry->start_trb,
> +               __entry->end_trb,
> +               __entry->start_trb_addr,
> +               __entry->flags
> +       )
> +);
> +
> +DEFINE_EVENT(cdns3_log_request, cdns3_alloc_request,
> +       TP_PROTO(struct cdns3_request *req),
> +       TP_ARGS(req)
> +);
> +
> +DEFINE_EVENT(cdns3_log_request, cdns3_free_request,
> +       TP_PROTO(struct cdns3_request *req),
> +       TP_ARGS(req)
> +);
> +
> +DEFINE_EVENT(cdns3_log_request, cdns3_ep_queue,
> +       TP_PROTO(struct cdns3_request *req),
> +       TP_ARGS(req)
> +);
> +
> +DEFINE_EVENT(cdns3_log_request, cdns3_ep_dequeue,
> +       TP_PROTO(struct cdns3_request *req),
> +       TP_ARGS(req)
> +);
> +
> +DEFINE_EVENT(cdns3_log_request, cdns3_gadget_giveback,
> +       TP_PROTO(struct cdns3_request *req),
> +       TP_ARGS(req)
> +);
> +
> +DECLARE_EVENT_CLASS(cdns3_log_trb,
> +       TP_PROTO(struct cdns3_endpoint *priv_ep, struct cdns3_trb *trb),
> +       TP_ARGS(priv_ep, trb),
> +       TP_STRUCT__entry(
> +               __string(name, priv_ep->name)
> +               __field(struct cdns3_trb *, trb)
> +               __field(u32, buffer)
> +               __field(u32, length)
> +               __field(u32, control)
> +               __field(u32, type)
> +       ),
> +       TP_fast_assign(
> +               __assign_str(name, priv_ep->name);
> +               __entry->trb = trb;
> +               __entry->buffer = trb->buffer;
> +               __entry->length = trb->length;
> +               __entry->control = trb->control;
> +               __entry->type = usb_endpoint_type(priv_ep->endpoint.desc);
> +       ),
> +       TP_printk("%s: trb 0x%pa, dma buf: 0x%08x, size: %ld, ctrl: 0x%08x (%s%s%s%s%s%s%s)",
> +               __get_str(name), __entry->trb, __entry->buffer,
> +               TRB_LEN(__entry->length), __entry->control,
> +               __entry->control & TRB_CYCLE ? "C=1, " : "C=0, ",
> +               __entry->control & TRB_TOGGLE ? "T=1, " : "T=0, ",
> +               __entry->control & TRB_ISP ? "ISP, " : "",
> +               __entry->control & TRB_FIFO_MODE ? "FIFO, " : "",
> +               __entry->control & TRB_CHAIN ? "CHAIN, " : "",
> +               __entry->control & TRB_IOC ? "IOC, " : "",
> +               TRB_FIELD_TO_TYPE(__entry->control) == TRB_NORMAL ? "Normal" : "LINK"
> +       )
> +);
> +
> +DEFINE_EVENT(cdns3_log_trb, cdns3_prepare_trb,
> +       TP_PROTO(struct cdns3_endpoint *priv_ep, struct cdns3_trb *trb),
> +       TP_ARGS(priv_ep, trb)
> +);
> +
> +DEFINE_EVENT(cdns3_log_trb, cdns3_complete_trb,
> +       TP_PROTO(struct cdns3_endpoint *priv_ep, struct cdns3_trb *trb),
> +       TP_ARGS(priv_ep, trb)
> +);
> +
> +DECLARE_EVENT_CLASS(cdns3_log_ring,
> +       TP_PROTO(struct cdns3_endpoint *priv_ep),
> +       TP_ARGS(priv_ep),
> +       TP_STRUCT__entry(
> +               __dynamic_array(u8, ring, TRB_RING_SIZE)
> +               __dynamic_array(u8, priv_ep, sizeof(struct cdns3_endpoint))
> +               __dynamic_array(char, buffer,
> +                               (TRBS_PER_SEGMENT * 65) + CDNS3_MSG_MAX)
> +       ),
> +       TP_fast_assign(
> +               memcpy(__get_dynamic_array(priv_ep), priv_ep,
> +                      sizeof(struct cdns3_endpoint));
> +               memcpy(__get_dynamic_array(ring), priv_ep->trb_pool,
> +                      TRB_RING_SIZE);
> +       ),
> +
> +       TP_printk("%s",
> +                 cdns3_dbg_ring((struct cdns3_endpoint *)__get_str(priv_ep),
> +                                (struct cdns3_trb *)__get_str(ring),
> +                                __get_str(buffer)))
> +);
> +
> +DEFINE_EVENT(cdns3_log_ring, cdns3_ring,
> +       TP_PROTO(struct cdns3_endpoint *priv_ep),
> +       TP_ARGS(priv_ep)
> +);
> +
> +DECLARE_EVENT_CLASS(cdns3_log_ep,
> +       TP_PROTO(struct cdns3_endpoint *priv_ep),
> +       TP_ARGS(priv_ep),
> +       TP_STRUCT__entry(
> +               __string(name, priv_ep->name)
> +               __field(unsigned int, maxpacket)
> +               __field(unsigned int, maxpacket_limit)
> +               __field(unsigned int, max_streams)
> +               __field(unsigned int, maxburst)
> +               __field(unsigned int, flags)
> +               __field(unsigned int, dir)
> +               __field(u8, enqueue)
> +               __field(u8, dequeue)
> +       ),
> +       TP_fast_assign(
> +               __assign_str(name, priv_ep->name);
> +               __entry->maxpacket = priv_ep->endpoint.maxpacket;
> +               __entry->maxpacket_limit = priv_ep->endpoint.maxpacket_limit;
> +               __entry->max_streams = priv_ep->endpoint.max_streams;
> +               __entry->maxburst = priv_ep->endpoint.maxburst;
> +               __entry->flags = priv_ep->flags;
> +               __entry->dir = priv_ep->dir;
> +               __entry->enqueue = priv_ep->enqueue;
> +               __entry->dequeue = priv_ep->dequeue;
> +       ),
> +       TP_printk("%s: mps: %d/%d. streams: %d, burst: %d, enq idx: %d, "
> +                 "deq idx: %d, flags %s%s%s%s%s%s%s%s, dir: %s",
> +               __get_str(name), __entry->maxpacket,
> +               __entry->maxpacket_limit, __entry->max_streams,
> +               __entry->maxburst, __entry->enqueue,
> +               __entry->dequeue,
> +               __entry->flags & EP_ENABLED ? "EN | " : "",
> +               __entry->flags & EP_STALL ? "STALL | " : "",
> +               __entry->flags & EP_WEDGE ? "WEDGE | " : "",
> +               __entry->flags & EP_TRANSFER_STARTED ? "STARTED | " : "",
> +               __entry->flags & EP_UPDATE_EP_TRBADDR ? "UPD TRB | " : "",
> +               __entry->flags & EP_PENDING_REQUEST ? "REQ PEN | " : "",
> +               __entry->flags & EP_RING_FULL ? "RING FULL |" : "",
> +               __entry->flags & EP_CLAIMED ?  "CLAIMED " : "",
> +               __entry->dir ? "IN" : "OUT"
> +       )
> +);
> +
> +DEFINE_EVENT(cdns3_log_ep, cdns3_gadget_ep_enable,
> +       TP_PROTO(struct cdns3_endpoint *priv_ep),
> +       TP_ARGS(priv_ep)
> +);
> +
> +DEFINE_EVENT(cdns3_log_ep, cdns3_gadget_ep_disable,
> +       TP_PROTO(struct cdns3_endpoint *priv_ep),
> +       TP_ARGS(priv_ep)
> +);
> +
> +DECLARE_EVENT_CLASS(cdns3_log_request_handled,
> +       TP_PROTO(struct cdns3_request *priv_req, int current_index,
> +                int handled),
> +       TP_ARGS(priv_req, current_index, handled),
> +       TP_STRUCT__entry(
> +               __field(struct cdns3_request *, priv_req)
> +               __field(unsigned int, dma_position)
> +               __field(unsigned int, handled)
> +               __field(unsigned int, dequeue_idx)
> +               __field(unsigned int, enqueue_idx)
> +               __field(unsigned int, start_trb)
> +               __field(unsigned int, end_trb)
> +       ),
> +       TP_fast_assign(
> +               __entry->priv_req = priv_req;
> +               __entry->dma_position = current_index;
> +               __entry->handled = handled;
> +               __entry->dequeue_idx = priv_req->priv_ep->dequeue;
> +               __entry->enqueue_idx = priv_req->priv_ep->enqueue;
> +               __entry->start_trb = priv_req->start_trb;
> +               __entry->end_trb = priv_req->end_trb;
> +       ),
> +       TP_printk("Req: %p %s, DMA pos: %d, ep deq: %d, ep enq: %d,"
> +                 " start trb: %d, end trb: %d",
> +               __entry->priv_req,
> +               __entry->handled ? "handled" : "not handled",
> +               __entry->dma_position, __entry->dequeue_idx,
> +               __entry->enqueue_idx, __entry->start_trb,
> +               __entry->end_trb
> +       )
> +);
> +
> +DEFINE_EVENT(cdns3_log_request_handled, cdns3_request_handled,
> +       TP_PROTO(struct cdns3_request *priv_req, int current_index,
> +                int handled),
> +       TP_ARGS(priv_req, current_index, handled)
> +);
> +#endif /* __LINUX_CDNS3_TRACE */
> +
> +/* this part must be outside header guard */
> +
> +#undef TRACE_INCLUDE_PATH
> +#define TRACE_INCLUDE_PATH .
> +
> +#undef TRACE_INCLUDE_FILE
> +#define TRACE_INCLUDE_FILE trace
> +
> +#include <trace/define_trace.h>
> --
> 2.17.1
>

^ permalink raw reply related	[flat|nested] 49+ messages in thread

* [v2,5/5] usb:cdns3 Add Cadence USB3 DRD Driver
@ 2018-12-24  9:38     ` Peter Chen
  0 siblings, 0 replies; 49+ messages in thread
From: Peter Chen @ 2018-12-24  9:38 UTC (permalink / raw)
  To: pawell
  Cc: devicetree, Greg Kroah-Hartman, mark.rutland, linux-usb,
	hdegoede, Heikki Krogerus, andy.shevchenko, robh+dt, rogerq,
	lkml, adouglas, jbergsagel, nsekhar, nm, sureshp, peter.chen,
	pjez, kurahul

>
> The host side of USBSS-DRD controller is compliance
> with XHCI specification, so it works with
> standard XHCI linux driver.
>

After adding my glue layer change (with my phy part) and make one
change for this code,
the xHCI can work at my platform. I list the comments from today's
review and debug.
The comments are at  Makefile and drd.c.

> +         If you choose to build this driver as module it will
> +         be dynamically linked and module will be called cdns3-pci.ko
> +
> +endif
> diff --git a/drivers/usb/cdns3/Makefile b/drivers/usb/cdns3/Makefile
> new file mode 100644
> index 000000000000..3f63baa24294
> --- /dev/null
> +++ b/drivers/usb/cdns3/Makefile
> @@ -0,0 +1,16 @@
> +# SPDX-License-Identifier: GPL-2.0
> +# define_trace.h needs to know how to find our header
> +CFLAGS_trace.o                         := -I$(src)
> +
> +obj-$(CONFIG_USB_CDNS3)                        += cdns3.o
> +obj-$(CONFIG_USB_CDNS3_PCI_WRAP)       += cdns3-pci.o
> +
> +cdns3-y                                        := core.o drd.o trace.o
> +
> +ifneq ($(CONFIG_DEBUG_FS),)
> +       cdns3-y                         += debugfs.o
> +endif
> +
> +cdns3-$(CONFIG_USB_CDNS3_GADGET)       += gadget.o ep0.o
> +cdns3-$(CONFIG_USB_CDNS3_HOST)         += host.o
> +cdns3-pci-y                            := cdns3-pci-wrap.o

Why do you want add two lines for pci_wrap? I change this Makefile like below:

# SPDX-License-Identifier: GPL-2.0
# define_trace.h needs to know how to find our header
CFLAGS_trace.o                          := -I$(src)

cdns3-y                                 := core.o drd.o trace.o
obj-$(CONFIG_USB_CDNS3)                 += cdns3.o
ifneq ($(CONFIG_DEBUG_FS),)
        cdns3-y                         += debugfs.o
endif

cdns3-$(CONFIG_USB_CDNS3_GADGET)        += gadget.o ep0.o
cdns3-$(CONFIG_USB_CDNS3_HOST)          += host.o
obj-$(CONFIG_USB_CDNS3_PCI_WRAP)        += cdns3-pci-wrap.o
obj-$(CONFIG_USB_CDNS3_IMX_WRAP)        += cdns3-imx.o

and below is the diff:



> diff --git a/drivers/usb/cdns3/drd.c b/drivers/usb/cdns3/drd.c
> new file mode 100644
> index 000000000000..b0c32302eb0b
> --- /dev/null
> +++ b/drivers/usb/cdns3/drd.c
> @@ -0,0 +1,350 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Cadence USBSS DRD Driver.
> + *
> + * Copyright (C) 2018 Cadence.
> + *
> + * Author: Pawel Laszczak <pawell@cadence.com
> + *
> + */
> +#include <linux/kernel.h>
> +#include <linux/interrupt.h>
> +#include <linux/delay.h>
> +#include <linux/usb/otg.h>
> +
> +#include "gadget.h"
> +#include "drd.h"
> +#include "core.h"
> +
> +static int cdns3_drd_switch_gadget(struct cdns3 *cdns, int on);
> +static int cdns3_drd_switch_host(struct cdns3 *cdns, int on);
> +
> +/**
> + * cdns3_set_mode - change mode of OTG Core
> + * @cdns: pointer to context structure
> + * @mode: selected mode from cdns_role
> + */
> +void cdns3_set_mode(struct cdns3 *cdns, enum usb_dr_mode mode)
> +{
> +       u32 reg;
> +
> +       cdns->current_dr_mode = mode;
> +
> +       switch (mode) {
> +       case USB_DR_MODE_PERIPHERAL:
> +               dev_info(cdns->dev, "Set controller to Gadget mode\n");
> +               cdns3_drd_switch_gadget(cdns, 1);
> +               break;
> +       case USB_DR_MODE_HOST:
> +               dev_info(cdns->dev, "Set controller to Host mode\n");
> +               cdns3_drd_switch_host(cdns, 1);
> +               break;
> +       case USB_DR_MODE_OTG:
> +               dev_info(cdns->dev, "Set controller to OTG mode\n");
> +               if (cdns->version == CDNS3_CONTROLLER_V1) {
> +                       reg = readl(&cdns->otg_v1_regs->override);
> +                       reg |= OVERRIDE_IDPULLUP;
> +                       writel(reg, &cdns->otg_v1_regs->override);
> +               } else {
> +                       reg = readl(&cdns->otg_v0_regs->ctrl1);
> +                       reg |= OVERRIDE_IDPULLUP_V0;
> +                       writel(reg, &cdns->otg_v0_regs->ctrl1);
> +               }
> +
> +               /*
> +                * Hardware specification says: "ID_VALUE must be valid within
> +                * 50ms after idpullup is set to '1" so driver must wait
> +                * 50ms before reading this pin.
> +                */
> +               usleep_range(50000, 60000);
> +               break;
> +       default:
> +               cdns->current_dr_mode = USB_DR_MODE_UNKNOWN;
> +               dev_err(cdns->dev, "Unsupported mode of operation %d\n", mode);
> +               return;
> +       }
> +}

I suggest adding return value since switch may fail.


> +/**
> + * cdns3_init_otg_mode - initialize drd controller
> + * @cdns: Pointer to controller context structure
> + *
> + * Returns 0 on success otherwise negative errno
> + */
> +static void cdns3_init_otg_mode(struct cdns3 *cdns)

You may add return value for this function too like your comment.

> +{
> +       cdns3_otg_disable_irq(cdns);
> +       /* clear all interrupts */
> +       writel(~0, &cdns->otg_regs->ivect);
> +
> +       cdns3_set_mode(cdns, USB_DR_MODE_OTG);

return value
> +
> +       if (cdns3_is_host(cdns))
> +               cdns3_drd_switch_host(cdns, 1);

ditto

> +       else
> +               cdns3_drd_switch_gadget(cdns, 1);

ditto
> +
> +       cdns3_otg_enable_irq(cdns);
> +}
> +
> +/**
> + * cdns3_drd_update_mode - initialize mode of operation
> + * @cdns: Pointer to controller context structure
> + *
> + * Returns 0 on success otherwise negative errno
> + */
> +int cdns3_drd_update_mode(struct cdns3 *cdns)
> +{
> +       int ret = 0;
> +
> +       if (cdns->desired_dr_mode == cdns->current_dr_mode)
> +               return ret;
> +
> +       cdns3_drd_switch_gadget(cdns, 0);

return value

> +       cdns3_drd_switch_host(cdns, 0);

ditto
> +
> +       switch (cdns->desired_dr_mode) {
> +       case USB_DR_MODE_PERIPHERAL:
> +               cdns3_set_mode(cdns, USB_DR_MODE_PERIPHERAL);

ditto

> +               break;
> +       case USB_DR_MODE_HOST:
> +               cdns3_set_mode(cdns, USB_DR_MODE_HOST);

ditto
> +               break;
> +       case USB_DR_MODE_OTG:
> +               cdns3_init_otg_mode(cdns);

ditto
> +               break;
> +       default:
> +               dev_err(cdns->dev, "Unsupported mode of operation %d\n",
> +                       cdns->dr_mode);
> +               return -EINVAL;
> +       }
> +
> +       return ret;
> +}
> +
> +/**
> + * cdns3_drd_irq - interrupt handler for OTG events
> + *
> + * @irq: irq number for cdns3 core device
> + * @data: structure of cdns3
> + *
> + * Returns IRQ_HANDLED or IRQ_NONE
> + */
> +static irqreturn_t cdns3_drd_irq(int irq, void *data)
> +{
> +       irqreturn_t ret = IRQ_NONE;
> +       struct cdns3 *cdns = data;
> +       u32 reg;
> +
> +       if (cdns->dr_mode != USB_DR_MODE_OTG)
> +               return ret;
> +
> +       reg = readl(&cdns->otg_regs->ivect);
> +
> +       if (!reg)
> +               return ret;
> +
> +       if (reg & OTGIEN_ID_CHANGE_INT) {
> +               dev_dbg(cdns->dev, "OTG IRQ: new ID: %d\n",
> +                       cdns3_get_id(cdns));
> +
> +               queue_work(system_freezable_wq, &cdns->role_switch_wq);
> +
> +               ret = IRQ_HANDLED;
> +       }
> +
> +       writel(~0, &cdns->otg_regs->ivect);
> +       return ret;
> +}
> +
> +int cdns3_drd_init(struct cdns3 *cdns)
> +{
> +       void __iomem *regs;
> +       int ret = 0;
> +       u32 state;
> +
> +       regs = devm_ioremap_resource(cdns->dev, &cdns->otg_res);
> +       if (IS_ERR(regs))
> +               return PTR_ERR(regs);
> +
> +       /* Detection of DRD version. Controller has been released
> +        * in two versions. Both are similar, but they have same changes
> +        * in register maps.
> +        * The first register in old version is command register and it's read
> +        * only, so driver should read 0 from it. On the other hand, in v1
> +        * the first register contains device ID number which is not set to 0.
> +        * Driver uses this fact to detect the proper version of
> +        * controller.
> +        */
> +       cdns->otg_v0_regs = regs;
> +       if (!readl(&cdns->otg_v0_regs->cmd)) {
> +               cdns->version  = CDNS3_CONTROLLER_V0;
> +               cdns->otg_v1_regs = NULL;
> +               cdns->otg_regs = regs;
> +               dev_info(cdns->dev, "DRD version v0 (%08x)\n",
> +                        readl(&cdns->otg_v0_regs->version));
> +       } else {
> +               cdns->otg_v0_regs = NULL;
> +               cdns->otg_v1_regs = regs;
> +               cdns->otg_regs = (void *)&cdns->otg_v1_regs->cmd;
> +               cdns->version  = CDNS3_CONTROLLER_V1;
> +               dev_info(cdns->dev, "DRD version v1 (ID: %08x, rev: %08x)\n",
> +                        readl(&cdns->otg_v1_regs->did),
> +                        readl(&cdns->otg_v1_regs->rid));
> +       }
> +
> +       state = OTGSTS_STRAP(readl(&cdns->otg_regs->sts));
> +
> +       /* Update dr_mode according to STRAP configuration. */
> +       cdns->dr_mode = USB_DR_MODE_OTG;
> +       if (state == OTGSTS_STRAP_HOST) {
> +               dev_info(cdns->dev, "Controller strapped to HOST\n");
> +               cdns->dr_mode = USB_DR_MODE_HOST;
> +       } else if (state == OTGSTS_STRAP_GADGET) {
> +               dev_info(cdns->dev, "Controller strapped to PERIPHERAL\n");
> +               cdns->dr_mode = USB_DR_MODE_PERIPHERAL;
> +       }
> +
> +       cdns->desired_dr_mode = cdns->dr_mode;
> +       cdns->current_dr_mode = USB_DR_MODE_UNKNOWN;
> +
> +       ret = devm_request_threaded_irq(cdns->dev, cdns->irq, cdns3_drd_irq,
> +                                       NULL, IRQF_SHARED,
> +                                       dev_name(cdns->dev), cdns);
> +
> +       if (ret)
> +               return ret;
> +
> +       state = readl(&cdns->otg_regs->sts);
> +       if (OTGSTS_OTG_NRDY(state) != 0) {
> +               dev_err(cdns->dev, "Cadence USB3 OTG device not ready\n");
> +               return -ENODEV;
> +       }
> +
> +       ret = cdns3_drd_update_mode(cdns);
> +

Calling this function, it is timeout for waiting OTGSTS_XHCI_READY at otgsts,
do you know possible reasons?  After commenting out this function, my
xHCI function
works.

Peter

> +       return ret;
> +}
> +
> +int cdns3_drd_exit(struct cdns3 *cdns)
> +{
> +       return cdns3_drd_switch_host(cdns, 0);
> +}
> diff --git a/drivers/usb/cdns3/drd.h b/drivers/usb/cdns3/drd.h
> new file mode 100644
> index 000000000000..6a29cdcb492d
> --- /dev/null
> +++ b/drivers/usb/cdns3/drd.h
> @@ -0,0 +1,162 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +/*
> + * Cadence USB3 DRD header file.
> + *
> + * Copyright (C) 2018 Cadence.
> + *
> + * Author: Pawel Laszczak <pawell@cadence.com>
> + */
> +#ifndef __LINUX_CDNS3_DRD
> +#define __LINUX_CDNS3_DRD
> +
> +#include <linux/usb/otg.h>
> +#include <linux/phy/phy.h>
> +#include "core.h"
> +
> +/*  DRD register interface for version v1. */
> +struct cdns3_otg_regs {
> +       __le32 did;
> +       __le32 rid;
> +       __le32 capabilities;
> +       __le32 reserved1;
> +       __le32 cmd;
> +       __le32 sts;
> +       __le32 state;
> +       __le32 reserved2;
> +       __le32 ien;
> +       __le32 ivect;
> +       __le32 refclk;
> +       __le32 tmr;
> +       __le32 reserved3[4];
> +       __le32 simulate;
> +       __le32 override;
> +       __le32 susp_ctrl;
> +       __le32 reserved4;
> +       __le32 anasts;
> +       __le32 adp_ramp_time;
> +       __le32 ctrl1;
> +       __le32 ctrl2;
> +};
> +
> +/*  DRD register interface for version v0. */
> +struct cdns3_otg_legacy_regs {
> +       __le32 cmd;
> +       __le32 sts;
> +       __le32 state;
> +       __le32 refclk;
> +       __le32 ien;
> +       __le32 ivect;
> +       __le32 reserved1[3];
> +       __le32 tmr;
> +       __le32 reserved2[2];
> +       __le32 version;
> +       __le32 capabilities;
> +       __le32 reserved3[2];
> +       __le32 simulate;
> +       __le32 reserved4[5];
> +       __le32 ctrl1;
> +};
> +
> +/*
> + * Common registers interface for both version of DRD.
> + */
> +struct cdns3_otg_common_regs {
> +       __le32 cmd;
> +       __le32 sts;
> +       __le32 state;
> +       __le32 different1;
> +       __le32 ien;
> +       __le32 ivect;
> +};
> +
> +/* CDNS_RID - bitmasks */
> +#define CDNS_RID(p)                    ((p) & GENMASK(15, 0))
> +
> +/* CDNS_VID - bitmasks */
> +#define CDNS_DID(p)                    ((p) & GENMASK(31, 0))
> +
> +/* OTGCMD - bitmasks */
> +/* "Request the bus for Device mode. */
> +#define OTGCMD_DEV_BUS_REQ             BIT(0)
> +/* Request the bus for Host mode */
> +#define OTGCMD_HOST_BUS_REQ            BIT(1)
> +/* Enable OTG mode. */
> +#define OTGCMD_OTG_EN                  BIT(2)
> +/* Disable OTG mode */
> +#define OTGCMD_OTG_DIS                 BIT(3)
> +/*"Configure OTG as A-Device. */
> +#define OTGCMD_A_DEV_EN                        BIT(4)
> +/*"Configure OTG as A-Device. */
> +#define OTGCMD_A_DEV_DIS               BIT(5)
> +/* Drop the bus for Device mod e. */
> +#define OTGCMD_DEV_BUS_DROP            BIT(8)
> +/* Drop the bus for Host mode*/
> +#define OTGCMD_HOST_BUS_DROP           BIT(9)
> +/* Power Down USBSS-DEV. */
> +#define OTGCMD_DEV_POWER_OFF           BIT(11)
> +/* Power Down CDNSXHCI. */
> +#define OTGCMD_HOST_POWER_OFF          BIT(12)
> +
> +/* OTGIEN - bitmasks */
> +/* ID change interrupt enable */
> +#define OTGIEN_ID_CHANGE_INT           BIT(0)
> +/* Vbusvalid fall detected interrupt enable.*/
> +#define OTGIEN_VBUSVALID_RISE_INT      BIT(4)
> +/* Vbusvalid fall detected interrupt enable */
> +#define OTGIEN_VBUSVALID_FALL_INT      BIT(5)
> +
> +/* OTGSTS - bitmasks */
> +/*
> + * Current value of the ID pin. It is only valid when idpullup in
> + *  OTGCTRL1_TYPE register is set to '1'.
> + */
> +#define OTGSTS_ID_VALUE                        BIT(0)
> +/* Current value of the vbus_valid */
> +#define OTGSTS_VBUS_VALID              BIT(1)
> +/* Current value of the b_sess_vld */
> +#define OTGSTS_SESSION_VALID           BIT(2)
> +/*Device mode is active*/
> +#define OTGSTS_DEV_ACTIVE              BIT(3)
> +/* Host mode is active. */
> +#define OTGSTS_HOST_ACTIVE             BIT(4)
> +/* OTG Controller not ready. */
> +#define OTGSTS_OTG_NRDY_MASK           BIT(11)
> +#define OTGSTS_OTG_NRDY(p)             ((p) & OTGSTS_OTG_NRDY_MASK)
> +/*
> + * Value of the strap pins.
> + * 000 - no default configuration
> + * 010 - Controller initiall configured as Host
> + * 100 - Controller initially configured as Device
> + */
> +#define OTGSTS_STRAP(p)                        (((p) & GENMASK(14, 12)) >> 12)
> +#define OTGSTS_STRAP_NO_DEFAULT_CFG    0x00
> +#define OTGSTS_STRAP_HOST_OTG          0x01
> +#define OTGSTS_STRAP_HOST              0x02
> +#define OTGSTS_STRAP_GADGET            0x04
> +/* Host mode is turned on. */
> +#define OTGSTS_XHCI_READY              BIT(26)
> +/* "Device mode is turned on .*/
> +#define OTGSTS_DEV_READY               BIT(27)
> +
> +/* OTGSTATE- bitmasks */
> +#define OTGSTATE_HOST_STATE_MASK       GENMASK(5, 3)
> +#define OTGSTATE_HOST_STATE_IDLE       0x0
> +#define OTGSTATE_HOST_STATE_VBUS_FALL   0x7
> +#define OTGSTATE_HOST_STATE(p)         (((p) & OTGSTATE_HOST_STATE_MASK) >> 3)
> +
> +/* OTGREFCLK - bitmasks */
> +#define OTGREFCLK_STB_CLK_SWITCH_EN    BIT(31)
> +
> +/* OVERRIDE - bitmasks */
> +#define OVERRIDE_IDPULLUP              BIT(0)
> +/* Only for CDNS3_CONTROLLER_V0 version */
> +#define OVERRIDE_IDPULLUP_V0           BIT(24)
> +
> +int cdns3_is_host(struct cdns3 *cdns);
> +int cdns3_is_device(struct cdns3 *cdns);
> +int cdns3_get_id(struct cdns3 *cdns);
> +int cdns3_drd_init(struct cdns3 *cdns);
> +int cdns3_drd_exit(struct cdns3 *cdns);
> +int cdns3_drd_update_mode(struct cdns3 *cdns);
> +
> +#endif /* __LINUX_CDNS3_DRD */
> diff --git a/drivers/usb/cdns3/ep0.c b/drivers/usb/cdns3/ep0.c
> new file mode 100644
> index 000000000000..89cf1cde1555
> --- /dev/null
> +++ b/drivers/usb/cdns3/ep0.c
> @@ -0,0 +1,896 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Cadence USBSS DRD Driver - gadget side.
> + *
> + * Copyright (C) 2018 Cadence Design Systems.
> + * Copyright (C) 2017-2018 NXP
> + *
> + * Authors: Pawel Jez <pjez@cadence.com>,
> + *          Pawel Laszczak <pawell@cadence.com>
> + *         Peter Chen <peter.chen@nxp.com>
> + */
> +
> +#include <linux/usb/composite.h>
> +
> +#include "gadget.h"
> +#include "trace.h"
> +
> +static struct usb_endpoint_descriptor cdns3_gadget_ep0_desc = {
> +       .bLength = USB_DT_ENDPOINT_SIZE,
> +       .bDescriptorType = USB_DT_ENDPOINT,
> +       .bmAttributes = USB_ENDPOINT_XFER_CONTROL,
> +};
> +
> +/**
> + * cdns3_ep0_run_transfer - Do transfer on default endpoint hardware
> + * @priv_dev: extended gadget object
> + * @dma_addr: physical address where data is/will be stored
> + * @length: data length
> + * @erdy: set it to 1 when ERDY packet should be sent -
> + *        exit from flow control state
> + */
> +static void cdns3_ep0_run_transfer(struct cdns3_device *priv_dev,
> +                                  dma_addr_t dma_addr,
> +                                  unsigned int length, int erdy)
> +{
> +       struct cdns3_usb_regs __iomem *regs = priv_dev->regs;
> +       struct cdns3_endpoint *priv_ep = priv_dev->eps[0];
> +
> +       priv_ep->trb_pool->buffer = TRB_BUFFER(dma_addr);
> +       priv_ep->trb_pool->length = TRB_LEN(length);
> +       priv_ep->trb_pool->control = TRB_CYCLE | TRB_IOC | TRB_TYPE(TRB_NORMAL);
> +
> +       trace_cdns3_prepare_trb(priv_ep, priv_ep->trb_pool);
> +
> +       cdns3_select_ep(priv_dev, priv_dev->ep0_data_dir);
> +
> +       writel(EP_STS_TRBERR, &regs->ep_sts);
> +       writel(EP_TRADDR_TRADDR(priv_ep->trb_pool_dma), &regs->ep_traddr);
> +       trace_cdns3_doorbell_ep0(priv_dev->ep0_data_dir ? "ep0in" : "ep0out",
> +                                readl(&regs->ep_traddr));
> +
> +       /* TRB should be prepared before starting transfer */
> +       writel(EP_CMD_DRDY, &regs->ep_cmd);
> +
> +       if (erdy)
> +               writel(EP_CMD_ERDY, &priv_dev->regs->ep_cmd);
> +}
> +
> +/**
> + * cdns3_ep0_delegate_req - Returns status of handling setup packet
> + * Setup is handled by gadget driver
> + * @priv_dev: extended gadget object
> + * @ctrl_req: pointer to received setup packet
> + *
> + * Returns zero on success or negative value on failure
> + */
> +static int cdns3_ep0_delegate_req(struct cdns3_device *priv_dev,
> +                                 struct usb_ctrlrequest *ctrl_req)
> +{
> +       int ret;
> +
> +       spin_unlock(&priv_dev->lock);
> +       priv_dev->setup_pending = 1;
> +       ret = priv_dev->gadget_driver->setup(&priv_dev->gadget, ctrl_req);
> +       priv_dev->setup_pending = 0;
> +       spin_lock(&priv_dev->lock);
> +       return ret;
> +}
> +
> +static void cdns3_prepare_setup_packet(struct cdns3_device *priv_dev)
> +{
> +       priv_dev->ep0_data_dir = 0;
> +       priv_dev->ep0_stage = CDNS3_SETUP_STAGE;
> +       cdns3_ep0_run_transfer(priv_dev, priv_dev->setup_dma,
> +                              sizeof(struct usb_ctrlrequest), 0);
> +}
> +
> +static void cdns3_ep0_complete_setup(struct cdns3_device *priv_dev,
> +                                    u8 send_stall, u8 send_erdy)
> +{
> +       struct cdns3_endpoint *priv_ep = priv_dev->eps[0];
> +       struct usb_request *request;
> +
> +       request = cdns3_next_request(&priv_ep->pending_req_list);
> +       if (request)
> +               list_del_init(&request->list);
> +
> +       if (send_stall) {
> +               dev_dbg(priv_dev->dev, "STALL for ep0\n");
> +               /* set_stall on ep0 */
> +               cdns3_select_ep(priv_dev, 0x00);
> +               writel(EP_CMD_SSTALL, &priv_dev->regs->ep_cmd);
> +       } else {
> +               cdns3_prepare_setup_packet(priv_dev);
> +       }
> +
> +       priv_dev->ep0_stage = CDNS3_SETUP_STAGE;
> +       writel((send_erdy ? EP_CMD_ERDY : 0) | EP_CMD_REQ_CMPL,
> +              &priv_dev->regs->ep_cmd);
> +}
> +
> +/**
> + * cdns3_req_ep0_set_configuration - Handling of SET_CONFIG standard USB request
> + * @priv_dev: extended gadget object
> + * @ctrl_req: pointer to received setup packet
> + *
> + * Returns 0 if success, USB_GADGET_DELAYED_STATUS on deferred status stage,
> + * error code on error
> + */
> +static int cdns3_req_ep0_set_configuration(struct cdns3_device *priv_dev,
> +                                          struct usb_ctrlrequest *ctrl_req)
> +{
> +       enum usb_device_state device_state = priv_dev->gadget.state;
> +       struct cdns3_endpoint *priv_ep;
> +       u32 config = le16_to_cpu(ctrl_req->wValue);
> +       int result = 0;
> +       int i;
> +
> +       switch (device_state) {
> +       case USB_STATE_ADDRESS:
> +               /* Configure non-control EPs */
> +               for (i = 0; i < CDNS3_ENDPOINTS_MAX_COUNT; i++) {
> +                       priv_ep = priv_dev->eps[i];
> +                       if (!priv_ep)
> +                               continue;
> +
> +                       if (priv_ep->flags & EP_CLAIMED)
> +                               cdns3_ep_config(priv_ep);
> +               }
> +
> +               result = cdns3_ep0_delegate_req(priv_dev, ctrl_req);
> +
> +               if (result)
> +                       return result;
> +
> +               if (config) {
> +                       cdns3_set_hw_configuration(priv_dev);
> +               } else {
> +                       cdns3_hw_reset_eps_config(priv_dev);
> +                       usb_gadget_set_state(&priv_dev->gadget,
> +                                            USB_STATE_ADDRESS);
> +               }
> +               break;
> +       case USB_STATE_CONFIGURED:
> +               result = cdns3_ep0_delegate_req(priv_dev, ctrl_req);
> +
> +               if (!config && !result) {
> +                       cdns3_hw_reset_eps_config(priv_dev);
> +                       usb_gadget_set_state(&priv_dev->gadget,
> +                                            USB_STATE_ADDRESS);
> +               }
> +               break;
> +       default:
> +               result = -EINVAL;
> +       }
> +
> +       return result;
> +}
> +
> +/**
> + * cdns3_req_ep0_set_address - Handling of SET_ADDRESS standard USB request
> + * @priv_dev: extended gadget object
> + * @ctrl_req: pointer to received setup packet
> + *
> + * Returns 0 if success, error code on error
> + */
> +static int cdns3_req_ep0_set_address(struct cdns3_device *priv_dev,
> +                                    struct usb_ctrlrequest *ctrl_req)
> +{
> +       enum usb_device_state device_state = priv_dev->gadget.state;
> +       u32 reg;
> +       u32 addr;
> +
> +       addr = le16_to_cpu(ctrl_req->wValue);
> +
> +       if (addr > USB_DEVICE_MAX_ADDRESS) {
> +               dev_err(priv_dev->dev,
> +                       "Device address (%d) cannot be greater than %d\n",
> +                       addr, USB_DEVICE_MAX_ADDRESS);
> +               return -EINVAL;
> +       }
> +
> +       if (device_state == USB_STATE_CONFIGURED) {
> +               dev_err(priv_dev->dev,
> +                       "can't set_address from configured state\n");
> +               return -EINVAL;
> +       }
> +
> +       reg = readl(&priv_dev->regs->usb_cmd);
> +
> +       writel(reg | USB_CMD_FADDR(addr) | USB_CMD_SET_ADDR,
> +              &priv_dev->regs->usb_cmd);
> +
> +       usb_gadget_set_state(&priv_dev->gadget,
> +                            (addr ? USB_STATE_ADDRESS : USB_STATE_DEFAULT));
> +
> +       return 0;
> +}
> +
> +/**
> + * cdns3_req_ep0_get_status - Handling of GET_STATUS standard USB request
> + * @priv_dev: extended gadget object
> + * @ctrl_req: pointer to received setup packet
> + *
> + * Returns 0 if success, error code on error
> + */
> +static int cdns3_req_ep0_get_status(struct cdns3_device *priv_dev,
> +                                   struct usb_ctrlrequest *ctrl)
> +{
> +       __le16 *response_pkt;
> +       u16 usb_status = 0;
> +       u32 recip;
> +       u32 reg;
> +
> +       recip = ctrl->bRequestType & USB_RECIP_MASK;
> +
> +       switch (recip) {
> +       case USB_RECIP_DEVICE:
> +               /* self powered */
> +               if (priv_dev->is_selfpowered)
> +                       usb_status = BIT(USB_DEVICE_SELF_POWERED);
> +
> +               if (priv_dev->wake_up_flag)
> +                       usb_status |= BIT(USB_DEVICE_REMOTE_WAKEUP);
> +
> +               if (priv_dev->gadget.speed != USB_SPEED_SUPER)
> +                       break;
> +
> +               reg = readl(&priv_dev->regs->usb_sts);
> +
> +               if (priv_dev->u1_allowed)
> +                       usb_status |= BIT(USB_DEV_STAT_U1_ENABLED);
> +
> +               if (priv_dev->u2_allowed)
> +                       usb_status |= BIT(USB_DEV_STAT_U2_ENABLED);
> +
> +               break;
> +       case USB_RECIP_INTERFACE:
> +               return cdns3_ep0_delegate_req(priv_dev, ctrl);
> +       case USB_RECIP_ENDPOINT:
> +               /* check if endpoint is stalled */
> +               cdns3_select_ep(priv_dev, ctrl->wIndex);
> +               if (EP_STS_STALL(readl(&priv_dev->regs->ep_sts)))
> +                       usb_status =  BIT(USB_ENDPOINT_HALT);
> +               break;
> +       default:
> +               return -EINVAL;
> +       }
> +
> +       response_pkt = (__le16 *)priv_dev->setup_buf;
> +       *response_pkt = cpu_to_le16(usb_status);
> +
> +       cdns3_ep0_run_transfer(priv_dev, priv_dev->setup_dma,
> +                              sizeof(*response_pkt), 1);
> +       return 0;
> +}
> +
> +static int cdns3_ep0_feature_handle_device(struct cdns3_device *priv_dev,
> +                                          struct usb_ctrlrequest *ctrl,
> +                                          int set)
> +{
> +       enum usb_device_state state;
> +       enum usb_device_speed speed;
> +       int ret = 0;
> +       u32 wValue;
> +       u32 wIndex;
> +       u16 tmode;
> +
> +       wValue = le16_to_cpu(ctrl->wValue);
> +       wIndex = le16_to_cpu(ctrl->wIndex);
> +       state = priv_dev->gadget.state;
> +       speed = priv_dev->gadget.speed;
> +
> +       switch (ctrl->wValue) {
> +       case USB_DEVICE_REMOTE_WAKEUP:
> +               priv_dev->wake_up_flag = !!set;
> +               break;
> +       case USB_DEVICE_U1_ENABLE:
> +               if (state != USB_STATE_CONFIGURED || speed != USB_SPEED_SUPER)
> +                       return -EINVAL;
> +
> +               priv_dev->u1_allowed = !!set;
> +               break;
> +       case USB_DEVICE_U2_ENABLE:
> +               if (state != USB_STATE_CONFIGURED || speed != USB_SPEED_SUPER)
> +                       return -EINVAL;
> +
> +               priv_dev->u2_allowed = !!set;
> +               break;
> +       case USB_DEVICE_LTM_ENABLE:
> +               ret = -EINVAL;
> +               break;
> +       case USB_DEVICE_TEST_MODE:
> +               if (state != USB_STATE_CONFIGURED || speed > USB_SPEED_HIGH)
> +                       return -EINVAL;
> +
> +               tmode = le16_to_cpu(ctrl->wIndex);
> +
> +               if (!set || (tmode & 0xff) != 0)
> +                       return -EINVAL;
> +
> +               switch (tmode >> 8) {
> +               case TEST_J:
> +               case TEST_K:
> +               case TEST_SE0_NAK:
> +               case TEST_PACKET:
> +                       cdns3_ep0_complete_setup(priv_dev, 0, 1);
> +                       /**
> +                        *  Little delay to give the controller some time
> +                        * for sending status stage.
> +                        * This time should be less then 3ms.
> +                        */
> +                       usleep_range(1000, 2000);
> +                       cdns3_set_register_bit(&priv_dev->regs->usb_cmd,
> +                                              USB_CMD_STMODE |
> +                                              USB_STS_TMODE_SEL(tmode - 1));
> +                       break;
> +               default:
> +                       ret = -EINVAL;
> +               }
> +               break;
> +       default:
> +               ret = -EINVAL;
> +       }
> +
> +       return ret;
> +}
> +
> +static int cdns3_ep0_feature_handle_intf(struct cdns3_device *priv_dev,
> +                                        struct usb_ctrlrequest *ctrl,
> +                                        int set)
> +{
> +       u32 wValue;
> +       int ret = 0;
> +
> +       wValue = le16_to_cpu(ctrl->wValue);
> +
> +       switch (wValue) {
> +       case USB_INTRF_FUNC_SUSPEND:
> +               break;
> +       default:
> +               ret = -EINVAL;
> +       }
> +
> +       return ret;
> +}
> +
> +static int cdns3_ep0_feature_handle_endpoint(struct cdns3_device *priv_dev,
> +                                            struct usb_ctrlrequest *ctrl,
> +                                            int set)
> +{
> +       struct cdns3_endpoint *priv_ep;
> +       int ret = 0;
> +       u8 index;
> +
> +       if (le16_to_cpu(ctrl->wValue) != USB_ENDPOINT_HALT)
> +               return -EINVAL;
> +
> +       if (!(ctrl->wIndex & ~USB_DIR_IN))
> +               return 0;
> +
> +       index = cdns3_ep_addr_to_index(ctrl->wIndex);
> +       priv_ep = priv_dev->eps[index];
> +
> +       cdns3_select_ep(priv_dev, ctrl->wIndex);
> +
> +       if (set) {
> +               writel(EP_CMD_SSTALL, &priv_dev->regs->ep_cmd);
> +               priv_ep->flags |= EP_STALL;
> +       } else {
> +               struct usb_request *request;
> +
> +               if (priv_dev->eps[index]->flags & EP_WEDGE) {
> +                       cdns3_select_ep(priv_dev, 0x00);
> +                       return 0;
> +               }
> +
> +               writel(EP_CMD_CSTALL | EP_CMD_EPRST, &priv_dev->regs->ep_cmd);
> +
> +               /* wait for EPRST cleared */
> +               ret = cdns3_handshake(&priv_dev->regs->ep_cmd,
> +                                     EP_CMD_EPRST, 0, 100);
> +               if (ret)
> +                       return -EINVAL;
> +
> +               priv_ep->flags &= ~EP_STALL;
> +
> +               request = cdns3_next_request(&priv_ep->pending_req_list);
> +               if (request)
> +                       cdns3_ep_run_transfer(priv_ep, request);
> +       }
> +       return ret;
> +}
> +
> +/**
> + * cdns3_req_ep0_handle_feature -
> + * Handling of GET/SET_FEATURE standard USB request
> + *
> + * @priv_dev: extended gadget object
> + * @ctrl_req: pointer to received setup packet
> + * @set: must be set to 1 for SET_FEATURE request
> + *
> + * Returns 0 if success, error code on error
> + */
> +static int cdns3_req_ep0_handle_feature(struct cdns3_device *priv_dev,
> +                                       struct usb_ctrlrequest *ctrl,
> +                                       int set)
> +{
> +       int ret = 0;
> +       u32 recip;
> +
> +       recip = ctrl->bRequestType & USB_RECIP_MASK;
> +
> +       switch (recip) {
> +       case USB_RECIP_DEVICE:
> +               ret = cdns3_ep0_feature_handle_device(priv_dev, ctrl, set);
> +               break;
> +       case USB_RECIP_INTERFACE:
> +               ret = cdns3_ep0_feature_handle_intf(priv_dev, ctrl, set);
> +               break;
> +       case USB_RECIP_ENDPOINT:
> +               ret = cdns3_ep0_feature_handle_endpoint(priv_dev, ctrl, set);
> +               break;
> +       default:
> +               return -EINVAL;
> +       }
> +
> +       return ret;
> +}
> +
> +/**
> + * cdns3_req_ep0_set_sel - Handling of SET_SEL standard USB request
> + * @priv_dev: extended gadget object
> + * @ctrl_req: pointer to received setup packet
> + *
> + * Returns 0 if success, error code on error
> + */
> +static int cdns3_req_ep0_set_sel(struct cdns3_device *priv_dev,
> +                                struct usb_ctrlrequest *ctrl_req)
> +{
> +       if (priv_dev->gadget.state < USB_STATE_ADDRESS)
> +               return -EINVAL;
> +
> +       if (ctrl_req->wLength != 6) {
> +               dev_err(priv_dev->dev, "Set SEL should be 6 bytes, got %d\n",
> +                       ctrl_req->wLength);
> +               return -EINVAL;
> +       }
> +
> +       cdns3_ep0_run_transfer(priv_dev, priv_dev->setup_dma, 6, 1);
> +       return 0;
> +}
> +
> +/**
> + * cdns3_req_ep0_set_isoch_delay -
> + * Handling of GET_ISOCH_DELAY standard USB request
> + * @priv_dev: extended gadget object
> + * @ctrl_req: pointer to received setup packet
> + *
> + * Returns 0 if success, error code on error
> + */
> +static int cdns3_req_ep0_set_isoch_delay(struct cdns3_device *priv_dev,
> +                                        struct usb_ctrlrequest *ctrl_req)
> +{
> +       if (ctrl_req->wIndex || ctrl_req->wLength)
> +               return -EINVAL;
> +
> +       priv_dev->isoch_delay = ctrl_req->wValue;
> +
> +       return 0;
> +}
> +
> +/**
> + * cdns3_ep0_standard_request - Handling standard USB requests
> + * @priv_dev: extended gadget object
> + * @ctrl_req: pointer to received setup packet
> + *
> + * Returns 0 if success, error code on error
> + */
> +static int cdns3_ep0_standard_request(struct cdns3_device *priv_dev,
> +                                     struct usb_ctrlrequest *ctrl_req)
> +{
> +       int ret;
> +
> +       switch (ctrl_req->bRequest) {
> +       case USB_REQ_SET_ADDRESS:
> +               ret = cdns3_req_ep0_set_address(priv_dev, ctrl_req);
> +               break;
> +       case USB_REQ_SET_CONFIGURATION:
> +               ret = cdns3_req_ep0_set_configuration(priv_dev, ctrl_req);
> +               break;
> +       case USB_REQ_GET_STATUS:
> +               ret = cdns3_req_ep0_get_status(priv_dev, ctrl_req);
> +               break;
> +       case USB_REQ_CLEAR_FEATURE:
> +               ret = cdns3_req_ep0_handle_feature(priv_dev, ctrl_req, 0);
> +               break;
> +       case USB_REQ_SET_FEATURE:
> +               ret = cdns3_req_ep0_handle_feature(priv_dev, ctrl_req, 1);
> +               break;
> +       case USB_REQ_SET_SEL:
> +               ret = cdns3_req_ep0_set_sel(priv_dev, ctrl_req);
> +               break;
> +       case USB_REQ_SET_ISOCH_DELAY:
> +               ret = cdns3_req_ep0_set_isoch_delay(priv_dev, ctrl_req);
> +               break;
> +       default:
> +               ret = cdns3_ep0_delegate_req(priv_dev, ctrl_req);
> +               break;
> +       }
> +
> +       return ret;
> +}
> +
> +static void __pending_setup_status_handler(struct cdns3_device *priv_dev)
> +{
> +       struct usb_request *request = priv_dev->pending_status_request;
> +
> +       if (priv_dev->status_completion_no_call && request &&
> +           request->complete) {
> +               request->complete(&priv_dev->eps[0]->endpoint, request);
> +               priv_dev->status_completion_no_call = 0;
> +       }
> +}
> +
> +void cdns3_pending_setup_status_handler(struct work_struct *work)
> +{
> +       struct cdns3_device *priv_dev = container_of(work, struct cdns3_device,
> +                       pending_status_wq);
> +       unsigned long flags;
> +
> +       spin_lock_irqsave(&priv_dev->lock, flags);
> +       __pending_setup_status_handler(priv_dev);
> +       spin_unlock_irqrestore(&priv_dev->lock, flags);
> +}
> +
> +/**
> + * cdns3_gadget_ep_giveback - call struct usb_request's ->complete callback
> + * @priv_ep: The endpoint to whom the request belongs to
> + * @priv_req: The request we're giving back
> + * @status: completion code for the request
> + *
> + * Must be called with controller's lock held and interrupts disabled. This
> + * function will unmap @req and call its ->complete() callback to notify upper
> + * layers that it has completed.
> + */
> +
> +void cdns3_gadget_ep0_giveback(struct cdns3_device *priv_dev,
> +                              int status)
> +{
> +       struct cdns3_endpoint *priv_ep;
> +       struct usb_request *request;
> +
> +       priv_ep = priv_dev->eps[0];
> +       request = cdns3_next_request(&priv_ep->pending_req_list);
> +
> +       priv_ep->dir = priv_dev->ep0_data_dir;
> +       cdns3_gadget_giveback(priv_ep, to_cdns3_request(request), status);
> +}
> +
> +/**
> + * cdns3_ep0_setup_phase - Handling setup USB requests
> + * @priv_dev: extended gadget object
> + */
> +static void cdns3_ep0_setup_phase(struct cdns3_device *priv_dev)
> +{
> +       struct usb_ctrlrequest *ctrl = priv_dev->setup_buf;
> +       struct cdns3_endpoint *priv_ep = priv_dev->eps[0];
> +       int result;
> +
> +       priv_dev->ep0_data_dir = ctrl->bRequestType & USB_DIR_IN;
> +
> +       trace_cdns3_ctrl_req(ctrl);
> +
> +       if (!list_empty(&priv_ep->pending_req_list)) {
> +               struct usb_request *request;
> +
> +               request = cdns3_next_request(&priv_ep->pending_req_list);
> +               priv_ep->dir = priv_dev->ep0_data_dir;
> +               cdns3_gadget_giveback(priv_ep, to_cdns3_request(request),
> +                                     -ECONNRESET);
> +       }
> +
> +       if (le16_to_cpu(ctrl->wLength))
> +               priv_dev->ep0_stage = CDNS3_DATA_STAGE;
> +       else
> +               priv_dev->ep0_stage = CDNS3_STATUS_STAGE;
> +
> +       if ((ctrl->bRequestType & USB_TYPE_MASK) == USB_TYPE_STANDARD)
> +               result = cdns3_ep0_standard_request(priv_dev, ctrl);
> +       else
> +               result = cdns3_ep0_delegate_req(priv_dev, ctrl);
> +
> +       if (priv_dev->ep0_stage == CDNS3_STATUS_STAGE &&
> +           result != USB_GADGET_DELAYED_STATUS) {
> +               cdns3_ep0_complete_setup(priv_dev, 0, 1);
> +       } else if (result < 0) {
> +               cdns3_ep0_complete_setup(priv_dev, 1, 1);
> +       }
> +}
> +
> +static void cdns3_transfer_completed(struct cdns3_device *priv_dev)
> +{
> +       struct cdns3_endpoint *priv_ep = priv_dev->eps[0];
> +
> +       if (!list_empty(&priv_ep->pending_req_list)) {
> +               struct usb_request *request;
> +
> +               trace_cdns3_complete_trb(priv_ep, priv_ep->trb_pool);
> +               request = cdns3_next_request(&priv_ep->pending_req_list);
> +
> +               request->actual =
> +                       TRB_LEN(le32_to_cpu(priv_ep->trb_pool->length));
> +
> +               priv_ep->dir = priv_dev->ep0_data_dir;
> +               cdns3_gadget_giveback(priv_ep, to_cdns3_request(request), 0);
> +       }
> +
> +       cdns3_ep0_complete_setup(priv_dev, 0, 0);
> +}
> +
> +/**
> + * cdns3_check_new_setup - Check if controller receive new SETUP packet.
> + * @priv_dev: extended gadget object
> + *
> + * The SETUP packet can be kept in on-chip memory or in system memory.
> + */
> +static bool cdns3_check_new_setup(struct cdns3_device *priv_dev)
> +{
> +       u32 ep_sts_reg;
> +
> +       cdns3_select_ep(priv_dev, 0 | USB_DIR_OUT);
> +       ep_sts_reg = readl(&priv_dev->regs->ep_sts);
> +
> +       return !!(ep_sts_reg & (EP_STS_SETUP | EP_STS_STPWAIT));
> +}
> +
> +/**
> + * cdns3_check_ep0_interrupt_proceed - Processes interrupt related to endpoint 0
> + * @priv_dev: extended gadget object
> + * @dir: USB_DIR_IN for IN direction, USB_DIR_OUT for OUT direction
> + */
> +void cdns3_check_ep0_interrupt_proceed(struct cdns3_device *priv_dev, int dir)
> +{
> +       u32 ep_sts_reg;
> +
> +       cdns3_select_ep(priv_dev, dir);
> +
> +       ep_sts_reg = readl(&priv_dev->regs->ep_sts);
> +       writel(ep_sts_reg, &priv_dev->regs->ep_sts);
> +
> +       trace_cdns3_ep0_irq(priv_dev, ep_sts_reg);
> +
> +       __pending_setup_status_handler(priv_dev);
> +
> +       if ((ep_sts_reg & EP_STS_SETUP)) {
> +               cdns3_ep0_setup_phase(priv_dev);
> +       } else if ((ep_sts_reg & EP_STS_IOC) || (ep_sts_reg & EP_STS_ISP)) {
> +               priv_dev->ep0_data_dir = dir;
> +               cdns3_transfer_completed(priv_dev);
> +       }
> +
> +       if (ep_sts_reg & EP_STS_DESCMIS) {
> +               if (dir == 0 && !priv_dev->setup_pending)
> +                       cdns3_prepare_setup_packet(priv_dev);
> +       }
> +}
> +
> +/**
> + * cdns3_gadget_ep0_enable
> + * Function shouldn't be called by gadget driver,
> + * endpoint 0 is allways active
> + */
> +static int cdns3_gadget_ep0_enable(struct usb_ep *ep,
> +                                  const struct usb_endpoint_descriptor *desc)
> +{
> +       return -EINVAL;
> +}
> +
> +/**
> + * cdns3_gadget_ep0_disable
> + * Function shouldn't be called by gadget driver,
> + * endpoint 0 is allways active
> + */
> +static int cdns3_gadget_ep0_disable(struct usb_ep *ep)
> +{
> +       return -EINVAL;
> +}
> +
> +/**
> + * cdns3_gadget_ep0_set_halt
> + * @ep: pointer to endpoint zero object
> + * @value: 1 for set stall, 0 for clear stall
> + *
> + * Returns 0
> + */
> +static int cdns3_gadget_ep0_set_halt(struct usb_ep *ep, int value)
> +{
> +       /* TODO */
> +       return 0;
> +}
> +
> +/**
> + * cdns3_gadget_ep0_queue Transfer data on endpoint zero
> + * @ep: pointer to endpoint zero object
> + * @request: pointer to request object
> + * @gfp_flags: gfp flags
> + *
> + * Returns 0 on success, error code elsewhere
> + */
> +static int cdns3_gadget_ep0_queue(struct usb_ep *ep,
> +                                 struct usb_request *request,
> +                                 gfp_t gfp_flags)
> +{
> +       struct cdns3_endpoint *priv_ep = ep_to_cdns3_ep(ep);
> +       struct cdns3_device *priv_dev = priv_ep->cdns3_dev;
> +       unsigned long flags;
> +       int erdy_sent = 0;
> +       int ret = 0;
> +
> +       dev_dbg(priv_dev->dev, "Queue to Ep0%s L: %d\n",
> +               priv_dev->ep0_data_dir ? "IN" : "OUT",
> +               request->length);
> +
> +       /* cancel the request if controller receive new SETUP packet. */
> +       if (cdns3_check_new_setup(priv_dev))
> +               return -ECONNRESET;
> +
> +       /* send STATUS stage. Should be called only for SET_CONFIGURATION */
> +       if (priv_dev->ep0_stage == CDNS3_STATUS_STAGE) {
> +               spin_lock_irqsave(&priv_dev->lock, flags);
> +               cdns3_select_ep(priv_dev, 0x00);
> +
> +               erdy_sent = !priv_dev->hw_configured_flag;
> +               cdns3_set_hw_configuration(priv_dev);
> +
> +               if (!erdy_sent)
> +                       cdns3_ep0_complete_setup(priv_dev, 0, 1);
> +
> +               request->actual = 0;
> +               priv_dev->status_completion_no_call = true;
> +               priv_dev->pending_status_request = request;
> +               spin_unlock_irqrestore(&priv_dev->lock, flags);
> +
> +               /*
> +                * Since there is no completion interrupt for status stage,
> +                * it needs to call ->completion in software after
> +                * ep0_queue is back.
> +                */
> +               queue_work(system_freezable_wq, &priv_dev->pending_status_wq);
> +               return 0;
> +       }
> +
> +       spin_lock_irqsave(&priv_dev->lock, flags);
> +       if (!list_empty(&priv_ep->pending_req_list)) {
> +               dev_err(priv_dev->dev,
> +                       "can't handle multiple requests for ep0\n");
> +               spin_unlock_irqrestore(&priv_dev->lock, flags);
> +               return -EBUSY;
> +       }
> +
> +       ret = usb_gadget_map_request_by_dev(priv_dev->sysdev, request,
> +                                           priv_dev->ep0_data_dir);
> +       if (ret) {
> +               spin_unlock_irqrestore(&priv_dev->lock, flags);
> +               dev_err(priv_dev->dev, "failed to map request\n");
> +               return -EINVAL;
> +       }
> +
> +       request->status = -EINPROGRESS;
> +       list_add_tail(&request->list, &priv_ep->pending_req_list);
> +       cdns3_ep0_run_transfer(priv_dev, request->dma, request->length, 1);
> +       spin_unlock_irqrestore(&priv_dev->lock, flags);
> +
> +       return ret;
> +}
> +
> +/**
> + * cdns3_gadget_ep_set_wedge Set wedge on selected endpoint
> + * @ep: endpoint object
> + *
> + * Returns 0
> + */
> +int cdns3_gadget_ep_set_wedge(struct usb_ep *ep)
> +{
> +       struct cdns3_endpoint *priv_ep = ep_to_cdns3_ep(ep);
> +       struct cdns3_device *priv_dev = priv_ep->cdns3_dev;
> +
> +       dev_dbg(priv_dev->dev, "Wedge for %s\n", ep->name);
> +       cdns3_gadget_ep_set_halt(ep, 1);
> +       priv_ep->flags |= EP_WEDGE;
> +
> +       return 0;
> +}
> +
> +const struct usb_ep_ops cdns3_gadget_ep0_ops = {
> +       .enable = cdns3_gadget_ep0_enable,
> +       .disable = cdns3_gadget_ep0_disable,
> +       .alloc_request = cdns3_gadget_ep_alloc_request,
> +       .free_request = cdns3_gadget_ep_free_request,
> +       .queue = cdns3_gadget_ep0_queue,
> +       .dequeue = cdns3_gadget_ep_dequeue,
> +       .set_halt = cdns3_gadget_ep0_set_halt,
> +       .set_wedge = cdns3_gadget_ep_set_wedge,
> +};
> +
> +/**
> + * cdns3_ep0_config - Configures default endpoint
> + * @priv_dev: extended gadget object
> + *
> + * Functions sets parameters: maximal packet size and enables interrupts
> + */
> +void cdns3_ep0_config(struct cdns3_device *priv_dev)
> +{
> +       struct cdns3_usb_regs __iomem *regs;
> +       struct cdns3_endpoint *priv_ep;
> +       u32 max_packet_size = 64;
> +
> +       regs = priv_dev->regs;
> +
> +       if (priv_dev->gadget.speed == USB_SPEED_SUPER)
> +               max_packet_size = 512;
> +
> +       priv_ep = priv_dev->eps[0];
> +
> +       if (!list_empty(&priv_ep->pending_req_list)) {
> +               struct usb_request *request;
> +
> +               request = cdns3_next_request(&priv_ep->pending_req_list);
> +               list_del_init(&request->list);
> +       }
> +
> +       priv_dev->u1_allowed = 0;
> +       priv_dev->u2_allowed = 0;
> +
> +       priv_dev->gadget.ep0->maxpacket = max_packet_size;
> +       cdns3_gadget_ep0_desc.wMaxPacketSize = cpu_to_le16(max_packet_size);
> +
> +       /* init ep out */
> +       cdns3_select_ep(priv_dev, USB_DIR_OUT);
> +
> +       writel(EP_CFG_ENABLE | EP_CFG_MAXPKTSIZE(max_packet_size),
> +              &regs->ep_cfg);
> +
> +       writel(EP_STS_EN_SETUPEN | EP_STS_EN_DESCMISEN | EP_STS_EN_TRBERREN,
> +              &regs->ep_sts_en);
> +
> +       /* init ep in */
> +       cdns3_select_ep(priv_dev, USB_DIR_IN);
> +
> +       writel(EP_CFG_ENABLE | EP_CFG_MAXPKTSIZE(max_packet_size),
> +              &regs->ep_cfg);
> +
> +       writel(EP_STS_EN_SETUPEN | EP_STS_EN_TRBERREN, &regs->ep_sts_en);
> +
> +       cdns3_set_register_bit(&regs->usb_conf, USB_CONF_U1DS | USB_CONF_U2DS);
> +}
> +
> +/**
> + * cdns3_init_ep0 Initializes software endpoint 0 of gadget
> + * @priv_dev: extended gadget object
> + * @ep_priv: extended endpoint object
> + *
> + * Returns 0 on success else error code.
> + */
> +int cdns3_init_ep0(struct cdns3_device *priv_dev,
> +                  struct cdns3_endpoint *priv_ep)
> +{
> +       sprintf(priv_ep->name, "ep0");
> +
> +       /* fill linux fields */
> +       priv_ep->endpoint.ops = &cdns3_gadget_ep0_ops;
> +       priv_ep->endpoint.maxburst = 1;
> +       usb_ep_set_maxpacket_limit(&priv_ep->endpoint,
> +                                  CDNS3_EP0_MAX_PACKET_LIMIT);
> +       priv_ep->endpoint.address = 0;
> +       priv_ep->endpoint.caps.type_control = 1;
> +       priv_ep->endpoint.caps.dir_in = 1;
> +       priv_ep->endpoint.caps.dir_out = 1;
> +       priv_ep->endpoint.name = priv_ep->name;
> +       priv_ep->endpoint.desc = &cdns3_gadget_ep0_desc;
> +       priv_dev->gadget.ep0 = &priv_ep->endpoint;
> +       priv_ep->type = USB_ENDPOINT_XFER_CONTROL;
> +
> +       return cdns3_allocate_trb_pool(priv_ep);
> +}
> diff --git a/drivers/usb/cdns3/gadget-export.h b/drivers/usb/cdns3/gadget-export.h
> new file mode 100644
> index 000000000000..577469eee961
> --- /dev/null
> +++ b/drivers/usb/cdns3/gadget-export.h
> @@ -0,0 +1,28 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +/*
> + * Cadence USBSS DRD Driver - Gadget Export APIs.
> + *
> + * Copyright (C) 2017 NXP
> + * Copyright (C) 2017-2018 NXP
> + *
> + * Authors: Peter Chen <peter.chen@nxp.com>
> + */
> +#ifndef __LINUX_CDNS3_GADGET_EXPORT
> +#define __LINUX_CDNS3_GADGET_EXPORT
> +
> +#ifdef CONFIG_USB_CDNS3_GADGET
> +
> +int cdns3_gadget_init(struct cdns3 *cdns);
> +void cdns3_gadget_exit(struct cdns3 *cdns);
> +#else
> +
> +static inline int cdns3_gadget_init(struct cdns3 *cdns)
> +{
> +       return -ENXIO;
> +}
> +
> +static inline void cdns3_gadget_exit(struct cdns3 *cdns) { }
> +
> +#endif
> +
> +#endif /* __LINUX_CDNS3_GADGET_EXPORT */
> diff --git a/drivers/usb/cdns3/gadget.c b/drivers/usb/cdns3/gadget.c
> new file mode 100644
> index 000000000000..0d95eb00be37
> --- /dev/null
> +++ b/drivers/usb/cdns3/gadget.c
> @@ -0,0 +1,2102 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Cadence USBSS DRD Driver - gadget side.
> + *
> + * Copyright (C) 2018 Cadence Design Systems.
> + * Copyright (C) 2017-2018 NXP
> + *
> + * Authors: Pawel Jez <pjez@cadence.com>,
> + *          Pawel Laszczak <pawell@cadence.com>
> + *         Peter Chen <peter.chen@nxp.com>
> + */
> +
> +#include <linux/dma-mapping.h>
> +#include <linux/usb/gadget.h>
> +
> +#include "core.h"
> +#include "gadget-export.h"
> +#include "gadget.h"
> +
> +#include "trace.h"
> +
> +static int __cdns3_gadget_ep_queue(struct usb_ep *ep,
> +                                  struct usb_request *request,
> +                                  gfp_t gfp_flags);
> +
> +/**
> + * cdns3_handshake - spin reading  until handshake completes or fails
> + * @ptr: address of device controller register to be read
> + * @mask: bits to look at in result of read
> + * @done: value of those bits when handshake succeeds
> + * @usec: timeout in microseconds
> + *
> + * Returns negative errno, or zero on success
> + *
> + * Success happens when the "mask" bits have the specified value (hardware
> + * handshake done). There are two failure modes: "usec" have passed (major
> + * hardware flakeout), or the register reads as all-ones (hardware removed).
> + */
> +int cdns3_handshake(void __iomem *ptr, u32 mask, u32 done, int usec)
> +{
> +       u32     result;
> +
> +       do {
> +               result = readl(ptr);
> +               if (result == ~(u32)0)  /* card removed */
> +                       return -ENODEV;
> +               result &= mask;
> +               if (result == done)
> +                       return 0;
> +               udelay(1);
> +               usec--;
> +       } while (usec > 0);
> +       return -ETIMEDOUT;
> +}
> +
> +/**
> + * cdns3_set_register_bit - set bit in given register.
> + * @ptr: address of device controller register to be read and changed
> + * @mask: bits requested to set
> + */
> +void cdns3_set_register_bit(void __iomem *ptr, u32 mask)
> +{
> +       mask = readl(ptr) | mask;
> +       writel(mask, ptr);
> +}
> +
> +/**
> + * cdns3_ep_addr_to_index - Macro converts endpoint address to
> + * index of endpoint object in cdns3_device.eps[] container
> + * @ep_addr: endpoint address for which endpoint object is required
> + *
> + */
> +u8 cdns3_ep_addr_to_index(u8 ep_addr)
> +{
> +       return (((ep_addr & 0x7F)) + ((ep_addr & USB_DIR_IN) ? 16 : 0));
> +}
> +
> +/**
> + * cdns3_next_request - returns next request from list
> + * @list: list containing requests
> + *
> + * Returns request or NULL if no requests in list
> + */
> +struct usb_request *cdns3_next_request(struct list_head *list)
> +{
> +       if (list_empty(list))
> +               return NULL;
> +       return list_first_entry_or_null(list, struct usb_request, list);
> +}
> +
> +/**
> + * cdns3_next_priv_request - returns next request from list
> + * @list: list containing requests
> + *
> + * Returns request or NULL if no requests in list
> + */
> +struct cdns3_request *cdns3_next_priv_request(struct list_head *list)
> +{
> +       if (list_empty(list))
> +               return NULL;
> +       return list_first_entry_or_null(list, struct cdns3_request, list);
> +}
> +
> +/**
> + * select_ep - selects endpoint
> + * @priv_dev:  extended gadget object
> + * @ep: endpoint address
> + */
> +void cdns3_select_ep(struct cdns3_device *priv_dev, u32 ep)
> +{
> +       if (priv_dev->selected_ep == ep)
> +               return;
> +
> +       priv_dev->selected_ep = ep;
> +       writel(ep, &priv_dev->regs->ep_sel);
> +}
> +
> +dma_addr_t cdns3_trb_virt_to_dma(struct cdns3_endpoint *priv_ep,
> +                                struct cdns3_trb *trb)
> +{
> +       u32 offset = (char *)trb - (char *)priv_ep->trb_pool;
> +
> +       return priv_ep->trb_pool_dma + offset;
> +}
> +
> +int cdns3_ring_size(struct cdns3_endpoint *priv_ep)
> +{
> +       switch (priv_ep->type) {
> +       case USB_ENDPOINT_XFER_ISOC:
> +               return TRB_ISO_RING_SIZE;
> +       case USB_ENDPOINT_XFER_CONTROL:
> +               return TRB_CTRL_RING_SIZE;
> +       default:
> +               return TRB_RING_SIZE;
> +       }
> +}
> +
> +/**
> + * cdns3_allocate_trb_pool - Allocates TRB's pool for selected endpoint
> + * @priv_ep:  endpoint object
> + *
> + * Function will return 0 on success or -ENOMEM on allocation error
> + */
> +int cdns3_allocate_trb_pool(struct cdns3_endpoint *priv_ep)
> +{
> +       struct cdns3_device *priv_dev = priv_ep->cdns3_dev;
> +       int ring_size = cdns3_ring_size(priv_ep);
> +       struct cdns3_trb *link_trb;
> +
> +       if (!priv_ep->trb_pool) {
> +               priv_ep->trb_pool = dma_zalloc_coherent(priv_dev->sysdev,
> +                                                       ring_size,
> +                                                       &priv_ep->trb_pool_dma,
> +                                                       GFP_DMA);
> +               if (!priv_ep->trb_pool)
> +                       return -ENOMEM;
> +       } else {
> +               memset(priv_ep->trb_pool, 0, ring_size);
> +       }
> +
> +       if (!priv_ep->num)
> +               return 0;
> +
> +       if (!priv_ep->aligned_buff) {
> +               void *buff = dma_alloc_coherent(priv_dev->sysdev,
> +                                               CDNS3_ALIGNED_BUF_SIZE,
> +                                               &priv_ep->aligned_dma_addr,
> +                                               GFP_DMA);
> +
> +               priv_ep->aligned_buff  = buff;
> +               if (!priv_ep->aligned_buff) {
> +                       dma_free_coherent(priv_dev->sysdev,
> +                                         ring_size,
> +                                         priv_ep->trb_pool,
> +                                         priv_ep->trb_pool_dma);
> +                       priv_ep->trb_pool = NULL;
> +
> +                       return -ENOMEM;
> +               }
> +       }
> +
> +       priv_ep->num_trbs = ring_size / TRB_SIZE;
> +       /* Initialize the last TRB as Link TRB */
> +       link_trb = (priv_ep->trb_pool + (priv_ep->num_trbs - 1));
> +       link_trb->buffer = TRB_BUFFER(priv_ep->trb_pool_dma);
> +       link_trb->control = TRB_CYCLE | TRB_TYPE(TRB_LINK) |
> +                           TRB_CHAIN | TRB_TOGGLE;
> +
> +       return 0;
> +}
> +
> +static void cdns3_free_trb_pool(struct cdns3_endpoint *priv_ep)
> +{
> +       struct cdns3_device *priv_dev = priv_ep->cdns3_dev;
> +
> +       if (priv_ep->trb_pool) {
> +               dma_free_coherent(priv_dev->sysdev,
> +                                 cdns3_ring_size(priv_ep),
> +                                 priv_ep->trb_pool, priv_ep->trb_pool_dma);
> +               priv_ep->trb_pool = NULL;
> +       }
> +
> +       if (priv_ep->aligned_buff) {
> +               dma_free_coherent(priv_dev->sysdev, CDNS3_ALIGNED_BUF_SIZE,
> +                                 priv_ep->aligned_buff,
> +                                 priv_ep->aligned_dma_addr);
> +               priv_ep->aligned_buff = NULL;
> +       }
> +}
> +
> +/**
> + * cdns3_data_flush - flush data at onchip buffer
> + * @priv_ep: endpoint object
> + *
> + * Endpoint must be selected before call to this function
> + *
> + * Returns zero on success or negative value on failure
> + */
> +static int cdns3_data_flush(struct cdns3_endpoint *priv_ep)
> +{
> +       struct cdns3_device *priv_dev = priv_ep->cdns3_dev;
> +
> +       writel(EP_CMD_DFLUSH, &priv_dev->regs->ep_cmd);
> +
> +       /* wait for DFLUSH cleared */
> +       return cdns3_handshake(&priv_dev->regs->ep_cmd, EP_CMD_DFLUSH, 0, 100);
> +}
> +
> +/**
> + * cdns3_ep_stall_flush - Stalls and flushes selected endpoint
> + * @priv_ep: endpoint object
> + *
> + * Endpoint must be selected before call to this function
> + */
> +static void cdns3_ep_stall_flush(struct cdns3_endpoint *priv_ep)
> +{
> +       struct cdns3_device *priv_dev = priv_ep->cdns3_dev;
> +
> +       writel(EP_CMD_DFLUSH | EP_CMD_ERDY | EP_CMD_SSTALL,
> +              &priv_dev->regs->ep_cmd);
> +
> +       /* wait for DFLUSH cleared */
> +       cdns3_handshake(&priv_dev->regs->ep_cmd, EP_CMD_DFLUSH, 0, 100);
> +       priv_ep->flags |= EP_STALL;
> +}
> +
> +/**
> + * cdns3_hw_reset_eps_config - reset endpoints configuration kept by controller.
> + * @priv_dev: extended gadget object
> + */
> +void cdns3_hw_reset_eps_config(struct cdns3_device *priv_dev)
> +{
> +       writel(USB_CONF_CFGRST, &priv_dev->regs->usb_conf);
> +
> +       cdns3_allow_enable_l1(priv_dev, 0);
> +       priv_dev->hw_configured_flag = 0;
> +       priv_dev->onchip_mem_allocated_size = 0;
> +}
> +
> +/**
> + * cdns3_ep_inc_trb - increment a trb index.
> + * @index: Pointer to the TRB index to increment.
> + * @cs: Cycle state
> + * @trb_in_seg: number of TRBs in segment
> + *
> + * The index should never point to the link TRB. After incrementing,
> + * if it is point to the link TRB, wrap around to the beginning and revert
> + * cycle state bit The
> + * link TRB is always at the last TRB entry.
> + */
> +static void cdns3_ep_inc_trb(int *index, u8 *cs, int trb_in_seg)
> +{
> +       (*index)++;
> +       if (*index == (trb_in_seg - 1)) {
> +               *index = 0;
> +               *cs ^=  1;
> +       }
> +}
> +
> +/**
> + * cdns3_ep_inc_enq - increment endpoint's enqueue pointer
> + * @priv_ep: The endpoint whose enqueue pointer we're incrementing
> + */
> +static void cdns3_ep_inc_enq(struct cdns3_endpoint *priv_ep)
> +{
> +       priv_ep->free_trbs--;
> +       cdns3_ep_inc_trb(&priv_ep->enqueue, &priv_ep->pcs, priv_ep->num_trbs);
> +}
> +
> +/**
> + * cdns3_ep_inc_deq - increment endpoint's dequeue pointer
> + * @priv_ep: The endpoint whose dequeue pointer we're incrementing
> + */
> +static void cdns3_ep_inc_deq(struct cdns3_endpoint *priv_ep)
> +{
> +       priv_ep->free_trbs++;
> +       cdns3_ep_inc_trb(&priv_ep->dequeue, &priv_ep->ccs, priv_ep->num_trbs);
> +}
> +
> +/**
> + * cdns3_allow_enable_l1 - enable/disable permits to transition to L1.
> + * @priv_dev: Extended gadget object
> + * @enable: Enable/disable permit to transition to L1.
> + *
> + * If bit USB_CONF_L1EN is set and device receive Extended Token packet,
> + * then controller answer with ACK handshake.
> + * If bit USB_CONF_L1DS is set and device receive Extended Token packet,
> + * then controller answer with NYET handshake.
> + */
> +void cdns3_allow_enable_l1(struct cdns3_device *priv_dev, int enable)
> +{
> +       if (enable)
> +               writel(USB_CONF_L1EN, &priv_dev->regs->usb_conf);
> +       else
> +               writel(USB_CONF_L1DS, &priv_dev->regs->usb_conf);
> +}
> +
> +enum usb_device_speed cdns3_get_speed(struct cdns3_device *priv_dev)
> +{
> +       u32 reg;
> +
> +       reg = readl(&priv_dev->regs->usb_sts);
> +
> +       if (DEV_SUPERSPEED(reg))
> +               return USB_SPEED_SUPER;
> +       else if (DEV_HIGHSPEED(reg))
> +               return USB_SPEED_HIGH;
> +       else if (DEV_FULLSPEED(reg))
> +               return USB_SPEED_FULL;
> +       else if (DEV_LOWSPEED(reg))
> +               return USB_SPEED_LOW;
> +       return USB_SPEED_UNKNOWN;
> +}
> +
> +/**
> + * cdns3_start_all_request - add to ring all request not started
> + * @priv_dev: Extended gadget object
> + * @priv_ep: The endpoint for whom request will be started.
> + *
> + * Returns return ENOMEM if transfer ring i not enough TRBs to start
> + *         all requests.
> + */
> +static int cdns3_start_all_request(struct cdns3_device *priv_dev,
> +                                  struct cdns3_endpoint *priv_ep)
> +{
> +       struct cdns3_request *priv_req;
> +       struct usb_request *request;
> +       int ret = 0;
> +
> +       while (!list_empty(&priv_ep->deferred_req_list)) {
> +               request = cdns3_next_request(&priv_ep->deferred_req_list);
> +               priv_req = to_cdns3_request(request);
> +
> +               ret = cdns3_ep_run_transfer(priv_ep, request);
> +               if (ret)
> +                       return ret;
> +
> +               list_del(&request->list);
> +               list_add_tail(&request->list,
> +                             &priv_ep->pending_req_list);
> +       }
> +
> +       priv_ep->flags &= ~EP_RING_FULL;
> +       return ret;
> +}
> +
> +/**
> + * cdns3_descmiss_copy_data copy data from internal requests to request queued
> + * by class driver.
> + * @priv_ep: extended endpoint object
> + * @request: request object
> + */
> +static void cdns3_descmiss_copy_data(struct cdns3_endpoint *priv_ep,
> +                                    struct usb_request *request)
> +{
> +       struct usb_request *descmiss_req;
> +       struct cdns3_request *descmiss_priv_req;
> +
> +       while (!list_empty(&priv_ep->descmiss_req_list)) {
> +               int chunk_end;
> +               int length;
> +
> +               descmiss_priv_req =
> +                       cdns3_next_priv_request(&priv_ep->descmiss_req_list);
> +               descmiss_req = &descmiss_priv_req->request;
> +
> +               /* driver can't touch pending request */
> +               if (descmiss_priv_req->flags & REQUEST_PENDING)
> +                       break;
> +
> +               chunk_end = descmiss_priv_req->flags & REQUEST_INTERNAL_CH;
> +               length = request->actual + descmiss_req->actual;
> +
> +               if (length <= request->length) {
> +                       memcpy(&((u8 *)request->buf)[request->actual],
> +                              descmiss_req->buf,
> +                              descmiss_req->actual);
> +                       request->actual = length;
> +               } else {
> +                       /* It should never occures */
> +                       request->status = -ENOMEM;
> +               }
> +
> +               list_del_init(&descmiss_priv_req->list);
> +
> +               kfree(descmiss_req->buf);
> +               cdns3_gadget_ep_free_request(&priv_ep->endpoint, descmiss_req);
> +
> +               if (!chunk_end)
> +                       break;
> +       }
> +}
> +
> +/**
> + * cdns3_gadget_giveback - call struct usb_request's ->complete callback
> + * @priv_ep: The endpoint to whom the request belongs to
> + * @priv_req: The request we're giving back
> + * @status: completion code for the request
> + *
> + * Must be called with controller's lock held and interrupts disabled. This
> + * function will unmap @req and call its ->complete() callback to notify upper
> + * layers that it has completed.
> + */
> +void cdns3_gadget_giveback(struct cdns3_endpoint *priv_ep,
> +                          struct cdns3_request *priv_req,
> +                          int status)
> +{
> +       struct cdns3_device *priv_dev = priv_ep->cdns3_dev;
> +       struct usb_request *request = &priv_req->request;
> +
> +       list_del_init(&request->list);
> +
> +       if (request->status == -EINPROGRESS)
> +               request->status = status;
> +
> +       usb_gadget_unmap_request_by_dev(priv_dev->sysdev, request,
> +                                       priv_ep->dir);
> +
> +       priv_req->flags &= ~REQUEST_PENDING;
> +       trace_cdns3_gadget_giveback(priv_req);
> +
> +       /* WA1: */
> +       if (priv_ep->flags & EP_QUIRK_EXTRA_BUF_EN &&
> +           priv_req->flags & REQUEST_INTERNAL) {
> +               struct usb_request *req;
> +
> +               req = cdns3_next_request(&priv_ep->deferred_req_list);
> +               request = req;
> +               priv_ep->descmis_req = NULL;
> +
> +               if (!req)
> +                       return;
> +
> +               cdns3_descmiss_copy_data(priv_ep, req);
> +               if (!(priv_ep->flags & EP_QUIRK_END_TRANSFER) &&
> +                   req->length != req->actual) {
> +                       /* wait for next part of transfer */
> +                       return;
> +               }
> +
> +               if (req->status == -EINPROGRESS)
> +                       req->status = 0;
> +
> +               list_del_init(&req->list);
> +               cdns3_start_all_request(priv_dev, priv_ep);
> +       }
> +
> +       /* Start all not pending request */
> +       if (priv_ep->flags & EP_RING_FULL)
> +               cdns3_start_all_request(priv_dev, priv_ep);
> +
> +       if (request->complete) {
> +               spin_unlock(&priv_dev->lock);
> +               usb_gadget_giveback_request(&priv_ep->endpoint,
> +                                           request);
> +               spin_lock(&priv_dev->lock);
> +       }
> +
> +       if (request->buf == priv_dev->zlp_buf)
> +               cdns3_gadget_ep_free_request(&priv_ep->endpoint, request);
> +}
> +
> +/**
> + * cdns3_ep_run_transfer - start transfer on no-default endpoint hardware
> + * @priv_ep: endpoint object
> + *
> + * Returns zero on success or negative value on failure
> + */
> +int cdns3_ep_run_transfer(struct cdns3_endpoint *priv_ep,
> +                         struct usb_request *request)
> +{
> +       struct cdns3_device *priv_dev = priv_ep->cdns3_dev;
> +       struct cdns3_request *priv_req;
> +       struct cdns3_trb *trb;
> +       dma_addr_t trb_dma;
> +       int sg_iter = 0;
> +       u32 first_pcs;
> +       int  num_trb;
> +       int address;
> +       int pcs;
> +
> +       if (priv_ep->type == USB_ENDPOINT_XFER_ISOC)
> +               num_trb = priv_ep->interval;
> +       else
> +               num_trb = request->num_sgs ? request->num_sgs : 1;
> +
> +       if (num_trb > priv_ep->free_trbs) {
> +               priv_ep->flags |= EP_RING_FULL;
> +               return -ENOBUFS;
> +       }
> +
> +       priv_req = to_cdns3_request(request);
> +       address = priv_ep->endpoint.desc->bEndpointAddress;
> +
> +       priv_ep->flags |= EP_PENDING_REQUEST;
> +       trb_dma = request->dma;
> +
> +       /* must allocate buffer aligned to 8 */
> +       if ((request->dma % 8)) {
> +               if (request->length <= CDNS3_ALIGNED_BUF_SIZE) {
> +                       memcpy(priv_ep->aligned_buff, request->buf,
> +                              request->length);
> +                       trb_dma = priv_ep->aligned_dma_addr;
> +               } else {
> +                       return -ENOMEM;
> +               }
> +       }
> +
> +       trb = priv_ep->trb_pool + priv_ep->enqueue;
> +       priv_req->start_trb = priv_ep->enqueue;
> +       priv_req->trb = trb;
> +
> +       /* prepare ring */
> +       if ((priv_ep->enqueue + num_trb)  >= (priv_ep->num_trbs - 1)) {
> +               /*updating C bt in  Link TRB before starting DMA*/
> +               struct cdns3_trb *link_trb = priv_ep->trb_pool +
> +                                            (priv_ep->num_trbs - 1);
> +               link_trb->control = ((priv_ep->pcs) ? TRB_CYCLE : 0) |
> +                                   TRB_TYPE(TRB_LINK) | TRB_CHAIN |
> +                                   TRB_TOGGLE;
> +       }
> +
> +       first_pcs = priv_ep->pcs ? TRB_CYCLE : 0;
> +
> +       do {
> +       /* fill TRB */
> +               trb->buffer = TRB_BUFFER(request->num_sgs == 0
> +                               ? trb_dma : request->sg[sg_iter].dma_address);
> +
> +               trb->length = TRB_BURST_LEN(16) |
> +                   TRB_LEN(request->num_sgs == 0 ?
> +                               request->length : request->sg[sg_iter].length);
> +
> +               trb->control = TRB_TYPE(TRB_NORMAL);
> +               pcs = priv_ep->pcs ? TRB_CYCLE : 0;
> +
> +               /*
> +                * first trb should be prepared as last to avoid processing
> +                *  transfer to early
> +                */
> +               if (sg_iter != 0)
> +                       trb->control |= pcs;
> +
> +               if (priv_ep->type == USB_ENDPOINT_XFER_ISOC  && !priv_ep->dir) {
> +                       trb->control |= TRB_IOC | TRB_ISP;
> +               } else {
> +                       /* for last element in TD or in SG list */
> +                       if (sg_iter == (num_trb - 1) && sg_iter != 0)
> +                               trb->control |= pcs | TRB_IOC | TRB_ISP;
> +               }
> +               ++sg_iter;
> +               priv_req->end_trb = priv_ep->enqueue;
> +               cdns3_ep_inc_enq(priv_ep);
> +               trb = priv_ep->trb_pool + priv_ep->enqueue;
> +       } while (sg_iter < num_trb);
> +
> +       trb = priv_req->trb;
> +
> +       /*
> +        * Memory barrier = Cycle Bit must be set before trb->length  and
> +        * trb->buffer fields.
> +        */
> +       wmb();
> +
> +       priv_req->flags |= REQUEST_PENDING;
> +
> +       /* give the TD to the consumer*/
> +       if (sg_iter == 1)
> +               trb->control |= first_pcs | TRB_IOC | TRB_ISP;
> +       else
> +               trb->control |= first_pcs;
> +
> +       trace_cdns3_prepare_trb(priv_ep, priv_req->trb);
> +       trace_cdns3_ring(priv_ep);
> +
> +       /* arm transfer on selected endpoint */
> +       cdns3_select_ep(priv_ep->cdns3_dev, address);
> +
> +       /*
> +        * For DMULT mode we can set address to transfer ring only once after
> +        * enabling endpoint.
> +        */
> +       if (priv_ep->flags & EP_UPDATE_EP_TRBADDR) {
> +               writel(EP_TRADDR_TRADDR(priv_ep->trb_pool_dma),
> +                      &priv_dev->regs->ep_traddr);
> +               priv_ep->flags &= ~EP_UPDATE_EP_TRBADDR;
> +       }
> +
> +       /*clearing TRBERR and EP_STS_DESCMIS before seting DRDY*/
> +       writel(EP_STS_TRBERR | EP_STS_DESCMIS, &priv_dev->regs->ep_sts);
> +       trace_cdns3_doorbell_epx(priv_ep->name,
> +                                readl(&priv_dev->regs->ep_traddr));
> +       writel(EP_CMD_DRDY, &priv_dev->regs->ep_cmd);
> +
> +       return 0;
> +}
> +
> +void cdns3_set_hw_configuration(struct cdns3_device *priv_dev)
> +{
> +       struct cdns3_endpoint *priv_ep;
> +       struct usb_ep *ep;
> +       int result = 0;
> +
> +       if (priv_dev->hw_configured_flag)
> +               return;
> +
> +       writel(USB_CONF_CFGSET, &priv_dev->regs->usb_conf);
> +       writel(EP_CMD_ERDY | EP_CMD_REQ_CMPL, &priv_dev->regs->ep_cmd);
> +
> +       cdns3_set_register_bit(&priv_dev->regs->usb_conf,
> +                              USB_CONF_U1EN | USB_CONF_U2EN);
> +
> +       /* wait until configuration set */
> +       result = cdns3_handshake(&priv_dev->regs->usb_sts,
> +                                USB_STS_CFGSTS_MASK, 1, 100);
> +
> +       priv_dev->hw_configured_flag = 1;
> +       cdns3_allow_enable_l1(priv_dev, 1);
> +
> +       list_for_each_entry(ep, &priv_dev->gadget.ep_list, ep_list) {
> +               if (ep->enabled) {
> +                       priv_ep = ep_to_cdns3_ep(ep);
> +                       cdns3_start_all_request(priv_dev, priv_ep);
> +               }
> +       }
> +}
> +
> +/**
> + * cdns3_request_handled - check whether request has been handled by DMA
> + *
> + * @priv_ep: extended endpoint object.
> + * @priv_req: request object for checking
> + *
> + * Endpoint must be selected before invoking this function.
> + *
> + * Returns false if request has not been handled by DMA, else returns true.
> + *
> + * SR - start ring
> + * ER -  end ring
> + * DQ = priv_ep->dequeue - dequeue position
> + * EQ = priv_ep->enqueue -  enqueue position
> + * ST = priv_req->start_trb - index of first TRB in transfer ring
> + * ET = priv_req->end_trb - index of last TRB in transfer ring
> + * CI = current_index - index of processed TRB by DMA.
> + *
> + * As first step, function checks if cycle bit for priv_req->start_trb is
> + * correct.
> + *
> + * some rules:
> + * 1. priv_ep->dequeue never exceed current_index.
> + * 2  priv_ep->enqueue never exceed priv_ep->dequeue
> + *
> + * Then We can split recognition into two parts:
> + * Case 1 - priv_ep->dequeue < current_index
> + *      SR ... EQ ... DQ ... CI ... ER
> + *      SR ... DQ ... CI ... EQ ... ER
> + *
> + *      Request has been handled by DMA if ST and ET is between DQ and CI.
> + *
> + * Case 2 - priv_ep->dequeue > current_index
> + * This situation take place when CI go through the LINK TRB at the end of
> + * transfer ring.
> + *      SR ... CI ... EQ ... DQ ... ER
> + *
> + *      Request has been handled by DMA if ET is less then CI or
> + *      ET is greater or equal DQ.
> + */
> +static bool cdns3_request_handled(struct cdns3_endpoint *priv_ep,
> +                                 struct cdns3_request *priv_req)
> +{
> +       struct cdns3_device *priv_dev = priv_ep->cdns3_dev;
> +       struct cdns3_trb *trb = priv_req->trb;
> +       int current_index = 0;
> +       int handled = 0;
> +
> +       current_index = (readl(&priv_dev->regs->ep_traddr) -
> +                        priv_ep->trb_pool_dma) / TRB_SIZE;
> +
> +       trb = &priv_ep->trb_pool[priv_req->start_trb];
> +
> +       if ((trb->control  & TRB_CYCLE) != priv_ep->ccs)
> +               goto finish;
> +
> +       if (priv_ep->dequeue < current_index) {
> +               if ((current_index == (priv_ep->num_trbs - 1)) &&
> +                   !priv_ep->dequeue)
> +                       goto finish;
> +
> +               if (priv_req->end_trb >= priv_ep->dequeue &&
> +                   priv_req->end_trb < current_index)
> +                       handled = 1;
> +       } else if (priv_ep->dequeue  > current_index) {
> +               if (priv_req->end_trb  < current_index ||
> +                   priv_req->end_trb >= priv_ep->dequeue)
> +                       handled = 1;
> +       }
> +
> +finish:
> +       trace_cdns3_request_handled(priv_req, current_index, handled);
> +
> +       return handled;
> +}
> +
> +static void cdns3_transfer_completed(struct cdns3_device *priv_dev,
> +                                    struct cdns3_endpoint *priv_ep)
> +{
> +       struct cdns3_request *priv_req;
> +       struct usb_request *request;
> +       struct cdns3_trb *trb;
> +       int current_trb;
> +
> +       while (!list_empty(&priv_ep->pending_req_list)) {
> +               request = cdns3_next_request(&priv_ep->pending_req_list);
> +               priv_req = to_cdns3_request(request);
> +
> +               if (!cdns3_request_handled(priv_ep, priv_req))
> +                       return;
> +
> +               if (request->dma % 8 && priv_ep->dir == USB_DIR_OUT)
> +                       memcpy(request->buf, priv_ep->aligned_buff,
> +                              request->length);
> +
> +               trb = priv_ep->trb_pool + priv_ep->dequeue;
> +               trace_cdns3_complete_trb(priv_ep, trb);
> +
> +               if (trb != priv_req->trb)
> +                       dev_warn(priv_dev->dev,
> +                                "request_trb=0x%p, queue_trb=0x%p\n",
> +                                priv_req->trb, trb);
> +
> +               request->actual = TRB_LEN(le32_to_cpu(trb->length));
> +               current_trb = priv_req->start_trb;
> +
> +               while (current_trb != priv_req->end_trb) {
> +                       cdns3_ep_inc_deq(priv_ep);
> +                       current_trb = priv_ep->dequeue;
> +               }
> +
> +               cdns3_ep_inc_deq(priv_ep);
> +               cdns3_gadget_giveback(priv_ep, priv_req, 0);
> +       }
> +       priv_ep->flags &= ~EP_PENDING_REQUEST;
> +}
> +
> +/**
> + * cdns3_descmissing_packet - handles descriptor missing event.
> + * @priv_dev: extended gadget object
> + *
> + * WA1: Controller for OUT endpoints has shared on-chip buffers for all incoming
> + * packets, including ep0out. It's FIFO buffer, so packets must be handle by DMA
> + * in correct order. If the first packet in the buffer will not be handled,
> + * then the following packets directed for other endpoints and  functions
> + * will be blocked.
> + * Additionally the packets directed to one endpoint can block entire on-chip
> + * buffers. In this case transfer to other endpoints also will blocked.
> + *
> + * To resolve this issue after raising the descriptor missing interrupt
> + * driver prepares internal usb_request object and use it to arm DMA transfer.
> + *
> + * The problematic situation was observed in case when endpoint has been enabled
> + * but no usb_request were queued. Driver try detects such endpoints and will
> + * use this workaround only for these endpoint.
> + *
> + * Driver use limited number of buffer. This number can be set by macro
> + * CDNS_WA1_NUM_BUFFERS.
> + *
> + * Such blocking situation was observed on ACM gadget. For this function
> + * host send OUT data packet but ACM function is not prepared for this packet.
> + *
> + * It's limitation of controller but maybe this issues should be fixed in
> + * function driver.
> + */
> +static int cdns3_descmissing_packet(struct cdns3_endpoint *priv_ep)
> +{
> +       struct cdns3_request *priv_req;
> +       struct usb_request *request;
> +
> +       if (priv_ep->flags & EP_QUIRK_EXTRA_BUF_DET) {
> +               priv_ep->flags &= ~EP_QUIRK_EXTRA_BUF_DET;
> +               priv_ep->flags |= EP_QUIRK_EXTRA_BUF_EN;
> +       }
> +
> +       request = cdns3_gadget_ep_alloc_request(&priv_ep->endpoint,
> +                                               GFP_ATOMIC);
> +       if (!request)
> +               return -ENOMEM;
> +
> +       priv_req = to_cdns3_request(request);
> +       priv_req->flags |= REQUEST_INTERNAL;
> +
> +       /* if this field is still assigned it indicate that transfer related
> +        * with this request has not been finished yet. Driver in this
> +        * case simply allocate next request and assign flag REQUEST_INTERNAL_CH
> +        * flag to previous one. It will indicate that current request is
> +        * part of the previous one.
> +        */
> +       if (priv_ep->descmis_req)
> +               priv_ep->descmis_req->flags |= REQUEST_INTERNAL_CH;
> +
> +       priv_req->request.buf = kzalloc(CDNS3_DESCMIS_BUF_SIZE,
> +                                       GFP_ATOMIC);
> +       if (!priv_req) {
> +               cdns3_gadget_ep_free_request(&priv_ep->endpoint, request);
> +               return -ENOMEM;
> +       }
> +
> +       priv_req->request.length = CDNS3_DESCMIS_BUF_SIZE;
> +       priv_ep->descmis_req = priv_req;
> +
> +       __cdns3_gadget_ep_queue(&priv_ep->endpoint,
> +                               &priv_ep->descmis_req->request,
> +                               GFP_ATOMIC);
> +
> +       return 0;
> +}
> +
> +/**
> + * cdns3_check_ep_interrupt_proceed - Processes interrupt related to endpoint
> + * @priv_ep: endpoint object
> + *
> + * Returns 0
> + */
> +static int cdns3_check_ep_interrupt_proceed(struct cdns3_endpoint *priv_ep)
> +{
> +       struct cdns3_device *priv_dev = priv_ep->cdns3_dev;
> +       u32 ep_sts_reg;
> +
> +       cdns3_select_ep(priv_dev, priv_ep->endpoint.address);
> +
> +       trace_cdns3_epx_irq(priv_dev, priv_ep);
> +
> +       ep_sts_reg = readl(&priv_dev->regs->ep_sts);
> +       writel(ep_sts_reg, &priv_dev->regs->ep_sts);
> +
> +       if ((ep_sts_reg & EP_STS_IOC) || (ep_sts_reg & EP_STS_ISP)) {
> +               if (priv_ep->flags & EP_QUIRK_EXTRA_BUF_EN) {
> +                       if (ep_sts_reg & EP_STS_ISP)
> +                               priv_ep->flags |= EP_QUIRK_END_TRANSFER;
> +                       else
> +                               priv_ep->flags &= ~EP_QUIRK_END_TRANSFER;
> +               }
> +               cdns3_transfer_completed(priv_dev, priv_ep);
> +       }
> +
> +       /*
> +        * For isochronous transfer driver completes request on IOC or on
> +        * TRBERR. IOC appears only when device receive OUT data packet.
> +        * If host disable stream or lost some packet then the only way to
> +        * finish all queued transfer is to do it on TRBERR event.
> +        */
> +       if ((ep_sts_reg & EP_STS_TRBERR) &&
> +           priv_ep->type == USB_ENDPOINT_XFER_ISOC)
> +               cdns3_transfer_completed(priv_dev, priv_ep);
> +
> +       /*
> +        * WA1: this condition should only be meet when
> +        * priv_ep->flags & EP_QUIRK_EXTRA_BUF_DET or
> +        * priv_ep->flags & EP_QUIRK_EXTRA_BUF_EN.
> +        * In other cases this interrupt will be disabled/
> +        */
> +       if (ep_sts_reg & EP_STS_DESCMIS) {
> +               int err;
> +
> +               err = cdns3_descmissing_packet(priv_ep);
> +               if (err)
> +                       dev_err(priv_dev->dev,
> +                               "Failed: No sufficient memory for DESCMIS\n");
> +       }
> +
> +       return 0;
> +}
> +
> +/**
> + * cdns3_check_usb_interrupt_proceed - Processes interrupt related to device
> + * @priv_dev: extended gadget object
> + * @usb_ists: bitmap representation of device's reported interrupts
> + * (usb_ists register value)
> + */
> +static void cdns3_check_usb_interrupt_proceed(struct cdns3_device *priv_dev,
> +                                             u32 usb_ists)
> +{
> +       int speed = 0;
> +
> +       trace_cdns3_usb_irq(priv_dev, usb_ists);
> +       /* Connection detected */
> +       if (usb_ists & (USB_ISTS_CON2I | USB_ISTS_CONI)) {
> +               speed = cdns3_get_speed(priv_dev);
> +               priv_dev->gadget.speed = speed;
> +               usb_gadget_set_state(&priv_dev->gadget, USB_STATE_POWERED);
> +               cdns3_ep0_config(priv_dev);
> +       }
> +
> +       /* Disconnection detected */
> +       if (usb_ists & (USB_ISTS_DIS2I | USB_ISTS_DISI)) {
> +               if (priv_dev->gadget_driver &&
> +                   priv_dev->gadget_driver->disconnect) {
> +                       spin_unlock(&priv_dev->lock);
> +                       priv_dev->gadget_driver->disconnect(&priv_dev->gadget);
> +                       spin_lock(&priv_dev->lock);
> +               }
> +
> +               priv_dev->gadget.speed = USB_SPEED_UNKNOWN;
> +               usb_gadget_set_state(&priv_dev->gadget, USB_STATE_NOTATTACHED);
> +               cdns3_hw_reset_eps_config(priv_dev);
> +       }
> +
> +       /* reset*/
> +       if (usb_ists & (USB_ISTS_UWRESI | USB_ISTS_UHRESI | USB_ISTS_U2RESI)) {
> +               /*read again to check the actuall speed*/
> +               speed = cdns3_get_speed(priv_dev);
> +               usb_gadget_set_state(&priv_dev->gadget, USB_STATE_DEFAULT);
> +               priv_dev->gadget.speed = speed;
> +               cdns3_hw_reset_eps_config(priv_dev);
> +               cdns3_ep0_config(priv_dev);
> +       }
> +}
> +
> +/**
> + * cdns3_device_irq_handler- interrupt handler for device part of controller
> + *
> + * @irq: irq number for cdns3 core device
> + * @data: structure of cdns3
> + *
> + * Returns IRQ_HANDLED or IRQ_NONE
> + */
> +static irqreturn_t cdns3_device_irq_handler(int irq, void *data)
> +{
> +       struct cdns3_device *priv_dev;
> +       struct cdns3 *cdns = data;
> +       irqreturn_t ret = IRQ_NONE;
> +       unsigned long flags;
> +       u32 reg;
> +
> +       priv_dev = cdns->gadget_dev;
> +       spin_lock_irqsave(&priv_dev->lock, flags);
> +
> +       /* check USB device interrupt */
> +       reg = readl(&priv_dev->regs->usb_ists);
> +       writel(reg, &priv_dev->regs->usb_ists);
> +
> +       if (reg) {
> +               cdns3_check_usb_interrupt_proceed(priv_dev, reg);
> +               ret = IRQ_HANDLED;
> +       }
> +
> +       /* check endpoint interrupt */
> +       reg = readl(&priv_dev->regs->ep_ists);
> +
> +       if (reg) {
> +               reg = ~reg & readl(&priv_dev->regs->ep_ien);
> +               /* mask deferred interrupt. */
> +               writel(reg, &priv_dev->regs->ep_ien);
> +               ret = IRQ_WAKE_THREAD;
> +       }
> +
> +       spin_unlock_irqrestore(&priv_dev->lock, flags);
> +       return ret;
> +}
> +
> +/**
> + * cdns3_device_thread_irq_handler- interrupt handler for device part
> + * of controller
> + *
> + * @irq: irq number for cdns3 core device
> + * @data: structure of cdns3
> + *
> + * Returns IRQ_HANDLED or IRQ_NONE
> + */
> +static irqreturn_t cdns3_device_thread_irq_handler(int irq, void *data)
> +{
> +       struct cdns3_device *priv_dev;
> +       struct cdns3 *cdns = data;
> +       irqreturn_t ret = IRQ_NONE;
> +       unsigned long flags;
> +       u32 ep_ien;
> +       int bit;
> +       u32 reg;
> +
> +       priv_dev = cdns->gadget_dev;
> +       spin_lock_irqsave(&priv_dev->lock, flags);
> +
> +       reg = readl(&priv_dev->regs->ep_ists);
> +       ep_ien = reg;
> +
> +       /* handle default endpoint OUT */
> +       if (reg & EP_ISTS_EP_OUT0) {
> +               cdns3_check_ep0_interrupt_proceed(priv_dev, USB_DIR_OUT);
> +               ret = IRQ_HANDLED;
> +       }
> +
> +       /* handle default endpoint IN */
> +       if (reg & EP_ISTS_EP_IN0) {
> +               cdns3_check_ep0_interrupt_proceed(priv_dev, USB_DIR_IN);
> +               ret = IRQ_HANDLED;
> +       }
> +
> +       /* check if interrupt from non default endpoint, if no exit */
> +       reg &= ~(EP_ISTS_EP_OUT0 | EP_ISTS_EP_IN0);
> +       if (!reg)
> +               goto irqend;
> +
> +       for_each_set_bit(bit, (unsigned long *)&reg,
> +                        sizeof(u32) * BITS_PER_BYTE) {
> +               cdns3_check_ep_interrupt_proceed(priv_dev->eps[bit]);
> +               ret = IRQ_HANDLED;
> +       }
> +
> +irqend:
> +       ep_ien |= readl(&priv_dev->regs->ep_ien);
> +       /* Unmask all handled EP interrupts */
> +       writel(ep_ien, &priv_dev->regs->ep_ien);
> +       spin_unlock_irqrestore(&priv_dev->lock, flags);
> +       return ret;
> +}
> +
> +/**
> + * cdns3_ep_onchip_buffer_reserve - Try to reserve onchip buf for EP
> + *
> + * The real reservation will occur during write to EP_CFG register,
> + * this function is used to check if the 'size' reservation is allowed.
> + *
> + * @priv_dev: extended gadget object
> + * @size: the size (KB) for EP would like to allocate
> + *
> + * Return 0 if the required size can met or negative value on failure
> + */
> +static int cdns3_ep_onchip_buffer_reserve(struct cdns3_device *priv_dev,
> +                                         int size)
> +{
> +       u32 onchip_mem;
> +
> +       priv_dev->onchip_mem_allocated_size += size;
> +
> +       onchip_mem = USB_CAP2_ACTUAL_MEM_SIZE(readl(&priv_dev->regs->usb_cap2));
> +       if (!onchip_mem)
> +               onchip_mem = 256;
> +
> +       /* 2KB is reserved for EP0*/
> +       onchip_mem -= 2;
> +       if (priv_dev->onchip_mem_allocated_size > onchip_mem) {
> +               priv_dev->onchip_mem_allocated_size -= size;
> +               return -EPERM;
> +       }
> +
> +       return 0;
> +}
> +
> +/**
> + * cdns3_ep_config Configure hardware endpoint
> + * @priv_ep: extended endpoint object
> + */
> +void cdns3_ep_config(struct cdns3_endpoint *priv_ep)
> +{
> +       bool is_iso_ep = (priv_ep->type == USB_ENDPOINT_XFER_ISOC);
> +       struct cdns3_device *priv_dev = priv_ep->cdns3_dev;
> +       u32 bEndpointAddress = priv_ep->num | priv_ep->dir;
> +       u32 max_packet_size = 0;
> +       u32 ep_cfg = 0;
> +       int ret;
> +
> +       switch (priv_ep->type) {
> +       case USB_ENDPOINT_XFER_INT:
> +               ep_cfg = EP_CFG_EPTYPE(USB_ENDPOINT_XFER_INT);
> +               break;
> +       case USB_ENDPOINT_XFER_BULK:
> +               ep_cfg = EP_CFG_EPTYPE(USB_ENDPOINT_XFER_BULK);
> +               break;
> +       default:
> +               ep_cfg = EP_CFG_EPTYPE(USB_ENDPOINT_XFER_ISOC);
> +       }
> +
> +       switch (priv_dev->gadget.speed) {
> +       case USB_SPEED_FULL:
> +               max_packet_size = is_iso_ep ? 1023 : 64;
> +               break;
> +       case USB_SPEED_HIGH:
> +               max_packet_size = is_iso_ep ? 1024 : 512;
> +               break;
> +       case USB_SPEED_SUPER:
> +               max_packet_size = 1024;
> +               break;
> +       default:
> +               /* all other speed are not supported */
> +               return;
> +       }
> +
> +       ret = cdns3_ep_onchip_buffer_reserve(priv_dev, CDNS3_EP_BUF_SIZE);
> +       if (ret) {
> +               dev_err(priv_dev->dev, "onchip mem is full, ep is invalid\n");
> +               return;
> +       }
> +
> +       ep_cfg |= EP_CFG_MAXPKTSIZE(max_packet_size) |
> +                 EP_CFG_BUFFERING(CDNS3_EP_BUF_SIZE - 1) |
> +                 EP_CFG_MAXBURST(priv_ep->endpoint.maxburst);
> +
> +       cdns3_select_ep(priv_dev, bEndpointAddress);
> +       writel(ep_cfg, &priv_dev->regs->ep_cfg);
> +
> +       dev_dbg(priv_dev->dev, "Configure %s: with val %08x\n",
> +               priv_ep->name, ep_cfg);
> +}
> +
> +/* Find correct direction for HW endpoint according to description */
> +static int cdns3_ep_dir_is_correct(struct usb_endpoint_descriptor *desc,
> +                                  struct cdns3_endpoint *priv_ep)
> +{
> +       return (priv_ep->endpoint.caps.dir_in && usb_endpoint_dir_in(desc)) ||
> +              (priv_ep->endpoint.caps.dir_out && usb_endpoint_dir_out(desc));
> +}
> +
> +static struct
> +cdns3_endpoint *cdns3_find_available_ep(struct cdns3_device *priv_dev,
> +                                       struct usb_endpoint_descriptor *desc)
> +{
> +       struct usb_ep *ep;
> +       struct cdns3_endpoint *priv_ep;
> +
> +       list_for_each_entry(ep, &priv_dev->gadget.ep_list, ep_list) {
> +               unsigned long num;
> +               int ret;
> +               /* ep name pattern likes epXin or epXout */
> +               char c[2] = {ep->name[2], '\0'};
> +
> +               ret = kstrtoul(c, 10, &num);
> +               if (ret)
> +                       return ERR_PTR(ret);
> +
> +               priv_ep = ep_to_cdns3_ep(ep);
> +               if (cdns3_ep_dir_is_correct(desc, priv_ep)) {
> +                       if (!(priv_ep->flags & EP_CLAIMED)) {
> +                               priv_ep->num  = num;
> +                               return priv_ep;
> +                       }
> +               }
> +       }
> +
> +       return ERR_PTR(-ENOENT);
> +}
> +
> +/*
> + *  Cadence IP has one limitation that all endpoints must be configured
> + * (Type & MaxPacketSize) before setting configuration through hardware
> + * register, it means we can't change endpoints configuration after
> + * set_configuration.
> + *
> + * This function set EP_CLAIMED flag which is added when the gadget driver
> + * uses usb_ep_autoconfig to configure specific endpoint;
> + * When the udc driver receives set_configurion request,
> + * it goes through all claimed endpoints, and configure all endpoints
> + * accordingly.
> + *
> + * At usb_ep_ops.enable/disable, we only enable and disable endpoint through
> + * ep_cfg register which can be changed after set_configuration, and do
> + * some software operation accordingly.
> + */
> +static struct
> +usb_ep *cdns3_gadget_match_ep(struct usb_gadget *gadget,
> +                             struct usb_endpoint_descriptor *desc,
> +                             struct usb_ss_ep_comp_descriptor *comp_desc)
> +{
> +       struct cdns3_device *priv_dev = gadget_to_cdns3_device(gadget);
> +       struct cdns3_endpoint *priv_ep;
> +       unsigned long flags;
> +
> +       priv_ep = cdns3_find_available_ep(priv_dev, desc);
> +       if (IS_ERR(priv_ep)) {
> +               dev_err(priv_dev->dev, "no available ep\n");
> +               return NULL;
> +       }
> +
> +       dev_dbg(priv_dev->dev, "match endpoint: %s\n", priv_ep->name);
> +
> +       spin_lock_irqsave(&priv_dev->lock, flags);
> +       priv_ep->endpoint.desc = desc;
> +       priv_ep->dir  = usb_endpoint_dir_in(desc) ? USB_DIR_IN : USB_DIR_OUT;
> +       priv_ep->type = usb_endpoint_type(desc);
> +       priv_ep->flags |= EP_CLAIMED;
> +       spin_unlock_irqrestore(&priv_dev->lock, flags);
> +       return &priv_ep->endpoint;
> +}
> +
> +/**
> + * cdns3_gadget_ep_alloc_request Allocates request
> + * @ep: endpoint object associated with request
> + * @gfp_flags: gfp flags
> + *
> + * Returns allocated request address, NULL on allocation error
> + */
> +struct usb_request *cdns3_gadget_ep_alloc_request(struct usb_ep *ep,
> +                                                 gfp_t gfp_flags)
> +{
> +       struct cdns3_endpoint *priv_ep = ep_to_cdns3_ep(ep);
> +       struct cdns3_request *priv_req;
> +
> +       priv_req = kzalloc(sizeof(*priv_req), gfp_flags);
> +       if (!priv_req)
> +               return NULL;
> +
> +       priv_req->priv_ep = priv_ep;
> +
> +       trace_cdns3_alloc_request(priv_req);
> +       return &priv_req->request;
> +}
> +
> +/**
> + * cdns3_gadget_ep_free_request Free memory occupied by request
> + * @ep: endpoint object associated with request
> + * @request: request to free memory
> + */
> +void cdns3_gadget_ep_free_request(struct usb_ep *ep,
> +                                 struct usb_request *request)
> +{
> +       struct cdns3_request *priv_req = to_cdns3_request(request);
> +
> +       trace_cdns3_free_request(priv_req);
> +       kfree(priv_req);
> +}
> +
> +/**
> + * cdns3_gadget_ep_enable Enable endpoint
> + * @ep: endpoint object
> + * @desc: endpoint descriptor
> + *
> + * Returns 0 on success, error code elsewhere
> + */
> +static int cdns3_gadget_ep_enable(struct usb_ep *ep,
> +                                 const struct usb_endpoint_descriptor *desc)
> +{
> +       struct cdns3_endpoint *priv_ep;
> +       struct cdns3_device *priv_dev;
> +       u32 reg = EP_STS_EN_TRBERREN;
> +       u32 bEndpointAddress;
> +       unsigned long flags;
> +       int ret;
> +
> +       priv_ep = ep_to_cdns3_ep(ep);
> +       priv_dev = priv_ep->cdns3_dev;
> +
> +       if (!ep || !desc || desc->bDescriptorType != USB_DT_ENDPOINT) {
> +               dev_dbg(priv_dev->dev, "usbss: invalid parameters\n");
> +               return -EINVAL;
> +       }
> +
> +       if (!desc->wMaxPacketSize) {
> +               dev_err(priv_dev->dev, "usbss: missing wMaxPacketSize\n");
> +               return -EINVAL;
> +       }
> +
> +       if (dev_WARN_ONCE(priv_dev->dev, priv_ep->flags & EP_ENABLED,
> +                         "%s is already enabled\n", priv_ep->name))
> +               return 0;
> +
> +       spin_lock_irqsave(&priv_dev->lock, flags);
> +
> +       priv_ep->endpoint.desc = desc;
> +       priv_ep->type = usb_endpoint_type(desc);
> +       priv_ep->interval = desc->bInterval ? BIT(desc->bInterval - 1) : 0;
> +
> +       if (priv_ep->interval > ISO_MAX_INTERVAL &&
> +           priv_ep->type == USB_ENDPOINT_XFER_ISOC) {
> +               dev_err(priv_dev->dev, "Driver is limited to %d period\n",
> +                       ISO_MAX_INTERVAL);
> +
> +               ret =  -EINVAL;
> +               goto exit;
> +       }
> +
> +       ret = cdns3_allocate_trb_pool(priv_ep);
> +
> +       if (ret)
> +               goto exit;
> +
> +       bEndpointAddress = priv_ep->num | priv_ep->dir;
> +       cdns3_select_ep(priv_dev, bEndpointAddress);
> +
> +       trace_cdns3_gadget_ep_enable(priv_ep);
> +
> +       writel(EP_CMD_EPRST, &priv_dev->regs->ep_cmd);
> +
> +       ret = cdns3_handshake(&priv_dev->regs->ep_cmd,
> +                             EP_CMD_CSTALL | EP_CMD_EPRST, 0, 100);
> +
> +       /* enable interrupt for selected endpoint */
> +       cdns3_set_register_bit(&priv_dev->regs->ep_ien,
> +                              BIT(cdns3_ep_addr_to_index(bEndpointAddress)));
> +       /*
> +        * WA1: Set flag for all not ISOC OUT endpoints. If this flag is set
> +        * driver try to detect whether endpoint need additional internal
> +        * buffer for unblocking on-chip FIFO buffer. This flag will be cleared
> +        * if before first DESCMISS interrupt the DMA will be armed.
> +        */
> +       if (!priv_ep->dir && priv_ep->type != USB_ENDPOINT_XFER_ISOC) {
> +               priv_ep->flags |= EP_QUIRK_EXTRA_BUF_DET;
> +               reg |= EP_STS_EN_DESCMISEN;
> +       }
> +
> +       writel(reg, &priv_dev->regs->ep_sts_en);
> +
> +       cdns3_set_register_bit(&priv_dev->regs->ep_cfg, EP_CFG_ENABLE);
> +
> +       ep->desc = desc;
> +       priv_ep->flags &= ~(EP_PENDING_REQUEST | EP_STALL |
> +                           EP_QUIRK_EXTRA_BUF_EN);
> +       priv_ep->flags |= EP_ENABLED | EP_UPDATE_EP_TRBADDR;
> +       priv_ep->enqueue = 0;
> +       priv_ep->dequeue = 0;
> +       reg = readl(&priv_dev->regs->ep_sts);
> +       priv_ep->pcs = !!EP_STS_CCS(reg);
> +       priv_ep->ccs = !!EP_STS_CCS(reg);
> +       /* one TRB is reserved for link TRB used in DMULT mode*/
> +       priv_ep->free_trbs = priv_ep->num_trbs - 1;
> +exit:
> +       spin_unlock_irqrestore(&priv_dev->lock, flags);
> +
> +       return ret;
> +}
> +
> +/**
> + * cdns3_gadget_ep_disable Disable endpoint
> + * @ep: endpoint object
> + *
> + * Returns 0 on success, error code elsewhere
> + */
> +static int cdns3_gadget_ep_disable(struct usb_ep *ep)
> +{
> +       struct cdns3_endpoint *priv_ep;
> +       struct cdns3_request *priv_req;
> +       struct cdns3_device *priv_dev;
> +       struct usb_request *request;
> +       unsigned long flags;
> +       int ret = 0;
> +       u32 ep_cfg;
> +
> +       if (!ep) {
> +               pr_err("usbss: invalid parameters\n");
> +               return -EINVAL;
> +       }
> +
> +       priv_ep = ep_to_cdns3_ep(ep);
> +       priv_dev = priv_ep->cdns3_dev;
> +
> +       if (dev_WARN_ONCE(priv_dev->dev, !(priv_ep->flags & EP_ENABLED),
> +                         "%s is already disabled\n", priv_ep->name))
> +               return 0;
> +
> +       spin_lock_irqsave(&priv_dev->lock, flags);
> +
> +       trace_cdns3_gadget_ep_disable(priv_ep);
> +
> +       cdns3_select_ep(priv_dev, ep->desc->bEndpointAddress);
> +       ret = cdns3_data_flush(priv_ep);
> +
> +       ep_cfg = readl(&priv_dev->regs->ep_cfg);
> +       ep_cfg &= ~EP_CFG_ENABLE;
> +       writel(ep_cfg, &priv_dev->regs->ep_cfg);
> +
> +       while (!list_empty(&priv_ep->pending_req_list)) {
> +               request = cdns3_next_request(&priv_ep->pending_req_list);
> +
> +               cdns3_gadget_giveback(priv_ep, to_cdns3_request(request),
> +                                     -ESHUTDOWN);
> +       }
> +
> +       while (!list_empty(&priv_ep->descmiss_req_list)) {
> +               priv_req = cdns3_next_priv_request(&priv_ep->descmiss_req_list);
> +
> +               kfree(priv_req->request.buf);
> +               cdns3_gadget_ep_free_request(&priv_ep->endpoint,
> +                                            &priv_req->request);
> +       }
> +
> +       while (!list_empty(&priv_ep->deferred_req_list)) {
> +               request = cdns3_next_request(&priv_ep->deferred_req_list);
> +
> +               cdns3_gadget_giveback(priv_ep, to_cdns3_request(request),
> +                                     -ESHUTDOWN);
> +       }
> +
> +       priv_ep->descmis_req = NULL;
> +
> +       ep->desc = NULL;
> +       priv_ep->flags &= ~EP_ENABLED;
> +
> +       spin_unlock_irqrestore(&priv_dev->lock, flags);
> +
> +       return ret;
> +}
> +
> +/**
> + * cdns3_gadget_ep_queue Transfer data on endpoint
> + * @ep: endpoint object
> + * @request: request object
> + * @gfp_flags: gfp flags
> + *
> + * Returns 0 on success, error code elsewhere
> + */
> +static int __cdns3_gadget_ep_queue(struct usb_ep *ep,
> +                                  struct usb_request *request,
> +                                  gfp_t gfp_flags)
> +{
> +       struct cdns3_endpoint *priv_ep = ep_to_cdns3_ep(ep);
> +       struct cdns3_device *priv_dev = priv_ep->cdns3_dev;
> +       struct cdns3_request *priv_req;
> +       int deferred = 0;
> +       int ret = 0;
> +
> +       request->actual = 0;
> +       request->status = -EINPROGRESS;
> +       priv_req = to_cdns3_request(request);
> +       trace_cdns3_ep_queue(priv_req);
> +
> +       /*
> +        * WA1: if transfer was queued before DESCMISS appear than we
> +        * can disable handling of DESCMISS interrupt. Driver assumes that it
> +        * can disable special treatment for this endpoint.
> +        */
> +       if (priv_ep->flags & EP_QUIRK_EXTRA_BUF_DET) {
> +               u32 reg;
> +
> +               cdns3_select_ep(priv_dev, priv_ep->num | priv_ep->dir);
> +               reg = readl(&priv_dev->regs->ep_sts_en);
> +               priv_ep->flags &= ~EP_QUIRK_EXTRA_BUF_DET;
> +               reg &= EP_STS_EN_DESCMISEN;
> +               writel(reg, &priv_dev->regs->ep_sts_en);
> +       }
> +
> +       /* WA1 */
> +       if (priv_ep->flags & EP_QUIRK_EXTRA_BUF_EN) {
> +               u8 pending_empty = list_empty(&priv_ep->pending_req_list);
> +               u8 descmiss_empty = list_empty(&priv_ep->descmiss_req_list);
> +
> +               /*
> +                *  DESCMISS transfer has been finished, so data will be
> +                *  directly copied from internal allocated usb_request
> +                *  objects.
> +                */
> +               if (pending_empty && !descmiss_empty &&
> +                   !(priv_req->flags & REQUEST_INTERNAL)) {
> +                       cdns3_descmiss_copy_data(priv_ep, request);
> +                       list_add_tail(&request->list,
> +                                     &priv_ep->pending_req_list);
> +                       cdns3_gadget_giveback(priv_ep, priv_req,
> +                                             request->status);
> +                       return ret;
> +               }
> +
> +               /*
> +                * WA1 driver will wait for completion DESCMISS transfer,
> +                * before starts new, not DESCMISS transfer.
> +                */
> +               if (!pending_empty && !descmiss_empty)
> +                       deferred = 1;
> +
> +               if (priv_req->flags & REQUEST_INTERNAL)
> +                       list_add_tail(&priv_req->list,
> +                                     &priv_ep->descmiss_req_list);
> +       }
> +
> +       ret = usb_gadget_map_request_by_dev(priv_dev->sysdev, request,
> +                                           usb_endpoint_dir_in(ep->desc));
> +       if (ret)
> +               return ret;
> +
> +       /*
> +        * If hardware endpoint configuration has not been set yet then
> +        * just queue request in deferred list. Transfer will be started in
> +        * cdns3_set_hw_configuration.
> +        */
> +       if (!priv_dev->hw_configured_flag)
> +               deferred = 1;
> +       else
> +               ret = cdns3_ep_run_transfer(priv_ep, request);
> +
> +       if (ret || deferred)
> +               list_add_tail(&request->list, &priv_ep->deferred_req_list);
> +       else
> +               list_add_tail(&request->list, &priv_ep->pending_req_list);
> +
> +       return ret;
> +}
> +
> +static int cdns3_gadget_ep_queue(struct usb_ep *ep, struct usb_request *request,
> +                                gfp_t gfp_flags)
> +{
> +       struct usb_request *zlp_request;
> +       struct cdns3_endpoint *priv_ep;
> +       struct cdns3_device *priv_dev;
> +       unsigned long flags;
> +       int ret;
> +
> +       if (!request || !ep)
> +               return -EINVAL;
> +
> +       priv_ep = ep_to_cdns3_ep(ep);
> +       priv_dev = priv_ep->cdns3_dev;
> +
> +       spin_lock_irqsave(&priv_dev->lock, flags);
> +
> +       ret = __cdns3_gadget_ep_queue(ep, request, gfp_flags);
> +
> +       if (ret == 0 && request->zero && request->length &&
> +           (request->length % ep->maxpacket == 0)) {
> +               struct cdns3_request *priv_req;
> +
> +               zlp_request = cdns3_gadget_ep_alloc_request(ep, GFP_ATOMIC);
> +               zlp_request->buf = priv_dev->zlp_buf;
> +               zlp_request->length = 0;
> +
> +               priv_req = to_cdns3_request(zlp_request);
> +               priv_req->flags |= REQUEST_ZLP;
> +
> +               dev_dbg(priv_dev->dev, "Queuing ZLP for endpoint: %s\n",
> +                       priv_ep->name);
> +               ret = __cdns3_gadget_ep_queue(ep, zlp_request, gfp_flags);
> +       }
> +
> +       spin_unlock_irqrestore(&priv_dev->lock, flags);
> +       return ret;
> +}
> +
> +/**
> + * cdns3_gadget_ep_dequeue Remove request from transfer queue
> + * @ep: endpoint object associated with request
> + * @request: request object
> + *
> + * Returns 0 on success, error code elsewhere
> + */
> +int cdns3_gadget_ep_dequeue(struct usb_ep *ep,
> +                           struct usb_request *request)
> +{
> +       struct cdns3_endpoint *priv_ep = ep_to_cdns3_ep(ep);
> +       struct cdns3_device *priv_dev = priv_ep->cdns3_dev;
> +       struct usb_request *req, *req_temp;
> +       struct cdns3_request *priv_req;
> +       unsigned long flags;
> +       int ret = 0;
> +
> +       if (!ep || !request || !ep->desc)
> +               return -EINVAL;
> +
> +       spin_lock_irqsave(&priv_dev->lock, flags);
> +
> +       priv_req = to_cdns3_request(request);
> +
> +       trace_cdns3_ep_dequeue(priv_req);
> +
> +       cdns3_select_ep(priv_dev, ep->desc->bEndpointAddress);
> +
> +       list_for_each_entry_safe(req, req_temp, &priv_ep->pending_req_list,
> +                                list) {
> +               if (request == req)
> +                       goto found;
> +       }
> +
> +       list_for_each_entry_safe(req, req_temp, &priv_ep->deferred_req_list,
> +                                list) {
> +               if (request == req)
> +                       goto found;
> +       }
> +
> +found:
> +       if (request == req)
> +               cdns3_gadget_giveback(priv_ep, priv_req, -ECONNRESET);
> +
> +       spin_unlock_irqrestore(&priv_dev->lock, flags);
> +       return ret;
> +}
> +
> +/**
> + * cdns3_gadget_ep_set_halt Sets/clears stall on selected endpoint
> + * @ep: endpoint object to set/clear stall on
> + * @value: 1 for set stall, 0 for clear stall
> + *
> + * Returns 0 on success, error code elsewhere
> + */
> +int cdns3_gadget_ep_set_halt(struct usb_ep *ep, int value)
> +{
> +       struct cdns3_endpoint *priv_ep = ep_to_cdns3_ep(ep);
> +       struct cdns3_device *priv_dev = priv_ep->cdns3_dev;
> +       unsigned long flags;
> +       int ret = 0;
> +
> +       if (!(priv_ep->flags & EP_ENABLED))
> +               return -EPERM;
> +
> +       spin_lock_irqsave(&priv_dev->lock, flags);
> +
> +       /* if actual transfer is pending defer setting stall on this endpoint */
> +       if ((priv_ep->flags & EP_PENDING_REQUEST) && value) {
> +               priv_ep->flags |= EP_STALL;
> +               goto finish;
> +       }
> +
> +       dev_dbg(priv_dev->dev, "Halt endpoint %s\n", priv_ep->name);
> +
> +       cdns3_select_ep(priv_dev, ep->desc->bEndpointAddress);
> +       if (value) {
> +               cdns3_ep_stall_flush(priv_ep);
> +       } else {
> +               priv_ep->flags &= ~EP_WEDGE;
> +               writel(EP_CMD_CSTALL | EP_CMD_EPRST, &priv_dev->regs->ep_cmd);
> +
> +               /* wait for EPRST cleared */
> +               ret = cdns3_handshake(&priv_dev->regs->ep_cmd,
> +                                     EP_CMD_EPRST, 0, 100);
> +               if (unlikely(ret)) {
> +                       dev_err(priv_dev->dev,
> +                               "Clearing halt condition failed for %s\n",
> +                               priv_ep->name);
> +                       goto finish;
> +
> +               } else {
> +                       priv_ep->flags &= ~EP_STALL;
> +               }
> +       }
> +
> +       priv_ep->flags &= ~EP_PENDING_REQUEST;
> +finish:
> +       spin_unlock_irqrestore(&priv_dev->lock, flags);
> +
> +       return ret;
> +}
> +
> +extern const struct usb_ep_ops cdns3_gadget_ep0_ops;
> +
> +static const struct usb_ep_ops cdns3_gadget_ep_ops = {
> +       .enable = cdns3_gadget_ep_enable,
> +       .disable = cdns3_gadget_ep_disable,
> +       .alloc_request = cdns3_gadget_ep_alloc_request,
> +       .free_request = cdns3_gadget_ep_free_request,
> +       .queue = cdns3_gadget_ep_queue,
> +       .dequeue = cdns3_gadget_ep_dequeue,
> +       .set_halt = cdns3_gadget_ep_set_halt,
> +       .set_wedge = cdns3_gadget_ep_set_wedge,
> +};
> +
> +/**
> + * cdns3_gadget_get_frame Returns number of actual ITP frame
> + * @gadget: gadget object
> + *
> + * Returns number of actual ITP frame
> + */
> +static int cdns3_gadget_get_frame(struct usb_gadget *gadget)
> +{
> +       struct cdns3_device *priv_dev = gadget_to_cdns3_device(gadget);
> +
> +       return readl(&priv_dev->regs->usb_iptn);
> +}
> +
> +static int cdns3_gadget_wakeup(struct usb_gadget *gadget)
> +{
> +       return 0;
> +}
> +
> +static int cdns3_gadget_set_selfpowered(struct usb_gadget *gadget,
> +                                       int is_selfpowered)
> +{
> +       struct cdns3_device *priv_dev = gadget_to_cdns3_device(gadget);
> +       unsigned long flags;
> +
> +       spin_lock_irqsave(&priv_dev->lock, flags);
> +       priv_dev->is_selfpowered = !!is_selfpowered;
> +       spin_unlock_irqrestore(&priv_dev->lock, flags);
> +       return 0;
> +}
> +
> +static int cdns3_gadget_pullup(struct usb_gadget *gadget, int is_on)
> +{
> +       struct cdns3_device *priv_dev = gadget_to_cdns3_device(gadget);
> +
> +       if (is_on)
> +               writel(USB_CONF_DEVEN, &priv_dev->regs->usb_conf);
> +       else
> +               writel(USB_CONF_DEVDS, &priv_dev->regs->usb_conf);
> +
> +       return 0;
> +}
> +
> +static void cdns3_gadget_config(struct cdns3_device *priv_dev)
> +{
> +       struct cdns3_usb_regs __iomem *regs = priv_dev->regs;
> +
> +       cdns3_ep0_config(priv_dev);
> +
> +       /* enable interrupts for endpoint 0 (in and out) */
> +       writel(EP_IEN_EP_OUT0 | EP_IEN_EP_IN0, &regs->ep_ien);
> +
> +       /* enable generic interrupt*/
> +       writel(USB_IEN_INIT, &regs->usb_ien);
> +       writel(USB_CONF_CLK2OFFDS | USB_CONF_L1DS, &regs->usb_conf);
> +       writel(USB_CONF_DMULT, &regs->usb_conf);
> +       cdns3_gadget_pullup(&priv_dev->gadget, 1);
> +}
> +
> +/**
> + * cdns3_gadget_udc_start Gadget start
> + * @gadget: gadget object
> + * @driver: driver which operates on this gadget
> + *
> + * Returns 0 on success, error code elsewhere
> + */
> +static int cdns3_gadget_udc_start(struct usb_gadget *gadget,
> +                                 struct usb_gadget_driver *driver)
> +{
> +       struct cdns3_device *priv_dev = gadget_to_cdns3_device(gadget);
> +       unsigned long flags;
> +
> +       spin_lock_irqsave(&priv_dev->lock, flags);
> +       priv_dev->gadget_driver = driver;
> +       cdns3_gadget_config(priv_dev);
> +       spin_unlock_irqrestore(&priv_dev->lock, flags);
> +       return 0;
> +}
> +
> +/**
> + * cdns3_gadget_udc_stop Stops gadget
> + * @gadget: gadget object
> + *
> + * Returns 0
> + */
> +static int cdns3_gadget_udc_stop(struct usb_gadget *gadget)
> +{
> +       struct cdns3_device *priv_dev = gadget_to_cdns3_device(gadget);
> +       struct cdns3_endpoint *priv_ep;
> +       u32 bEndpointAddress;
> +       struct usb_ep *ep;
> +       int ret = 0;
> +
> +       priv_dev->gadget_driver = NULL;
> +
> +       priv_dev->onchip_mem_allocated_size = 0;
> +       priv_dev->gadget.speed = USB_SPEED_UNKNOWN;
> +
> +       list_for_each_entry(ep, &priv_dev->gadget.ep_list, ep_list) {
> +               priv_ep = ep_to_cdns3_ep(ep);
> +               bEndpointAddress = priv_ep->num | priv_ep->dir;
> +               cdns3_select_ep(priv_dev, bEndpointAddress);
> +               writel(EP_CMD_EPRST, &priv_dev->regs->ep_cmd);
> +               ret = cdns3_handshake(&priv_dev->regs->ep_cmd,
> +                                     EP_CMD_EPRST, 0, 100);
> +               cdns3_free_trb_pool(priv_ep);
> +       }
> +
> +       /* disable interrupt for device */
> +       writel(0, &priv_dev->regs->usb_ien);
> +       writel(USB_CONF_DEVDS, &priv_dev->regs->usb_conf);
> +
> +       return ret;
> +}
> +
> +static const struct usb_gadget_ops cdns3_gadget_ops = {
> +       .get_frame = cdns3_gadget_get_frame,
> +       .wakeup = cdns3_gadget_wakeup,
> +       .set_selfpowered = cdns3_gadget_set_selfpowered,
> +       .pullup = cdns3_gadget_pullup,
> +       .udc_start = cdns3_gadget_udc_start,
> +       .udc_stop = cdns3_gadget_udc_stop,
> +       .match_ep = cdns3_gadget_match_ep,
> +};
> +
> +static void cdns3_free_all_eps(struct cdns3_device *priv_dev)
> +{
> +       int i;
> +
> +       /*ep0 OUT point to ep0 IN*/
> +       priv_dev->eps[16] = NULL;
> +
> +       cdns3_free_trb_pool(priv_dev->eps[0]);
> +
> +       for (i = 0; i < CDNS3_ENDPOINTS_MAX_COUNT; i++)
> +               if (priv_dev->eps[i])
> +                       devm_kfree(priv_dev->dev, priv_dev->eps[i]);
> +}
> +
> +/**
> + * cdns3_init_eps Initializes software endpoints of gadget
> + * @cdns3: extended gadget object
> + *
> + * Returns 0 on success, error code elsewhere
> + */
> +static int cdns3_init_eps(struct cdns3_device *priv_dev)
> +{
> +       u32 ep_enabled_reg, iso_ep_reg;
> +       struct cdns3_endpoint *priv_ep;
> +       int ep_dir, ep_number;
> +       u32 ep_mask;
> +       int ret = 0;
> +       int i;
> +
> +       /* Read it from USB_CAP3 to USB_CAP5 */
> +       ep_enabled_reg = readl(&priv_dev->regs->usb_cap3);
> +       iso_ep_reg = readl(&priv_dev->regs->usb_cap4);
> +
> +       dev_dbg(priv_dev->dev, "Initializing non-zero endpoints\n");
> +
> +       for (i = 0; i < CDNS3_ENDPOINTS_MAX_COUNT; i++) {
> +               ep_dir = i >> 4;        /* i div 16 */
> +               ep_number = i & 0xF;    /* i % 16 */
> +               ep_mask = BIT(i);
> +
> +               if (!(ep_enabled_reg & ep_mask))
> +                       continue;
> +
> +               if (ep_dir && !ep_number) {
> +                       priv_dev->eps[i] = priv_dev->eps[0];
> +                       continue;
> +               }
> +
> +               priv_ep = devm_kzalloc(priv_dev->dev, sizeof(*priv_ep),
> +                                      GFP_KERNEL);
> +               if (!priv_ep) {
> +                       ret = -ENOMEM;
> +                       goto err;
> +               }
> +
> +               /* set parent of endpoint object */
> +               priv_ep->cdns3_dev = priv_dev;
> +               priv_dev->eps[i] = priv_ep;
> +               priv_ep->num = ep_number;
> +               priv_ep->dir = ep_dir ? USB_DIR_IN : USB_DIR_OUT;
> +
> +               if (!ep_number) {
> +                       ret = cdns3_init_ep0(priv_dev, priv_ep);
> +                       if (ret) {
> +                               dev_err(priv_dev->dev, "Failed to init ep0\n");
> +                               goto err;
> +                       }
> +               } else {
> +                       snprintf(priv_ep->name, sizeof(priv_ep->name), "ep%d%s",
> +                                ep_number, !!ep_dir ? "in" : "out");
> +                       priv_ep->endpoint.name = priv_ep->name;
> +
> +                       usb_ep_set_maxpacket_limit(&priv_ep->endpoint,
> +                                                  CDNS3_EP_MAX_PACKET_LIMIT);
> +                       priv_ep->endpoint.max_streams = CDNS3_EP_MAX_STREAMS;
> +                       priv_ep->endpoint.ops = &cdns3_gadget_ep_ops;
> +                       if (ep_dir)
> +                               priv_ep->endpoint.caps.dir_in = 1;
> +                       else
> +                               priv_ep->endpoint.caps.dir_out = 1;
> +
> +                       if (iso_ep_reg & ep_mask)
> +                               priv_ep->endpoint.caps.type_iso = 1;
> +
> +                       priv_ep->endpoint.caps.type_bulk = 1;
> +                       priv_ep->endpoint.caps.type_int = 1;
> +                       priv_ep->endpoint.maxburst = CDNS3_EP_BUF_SIZE - 1;
> +
> +                       list_add_tail(&priv_ep->endpoint.ep_list,
> +                                     &priv_dev->gadget.ep_list);
> +               }
> +
> +               priv_ep->flags = 0;
> +
> +               dev_info(priv_dev->dev, "Initialized  %s support: %s %s\n",
> +                        priv_ep->name,
> +                        priv_ep->endpoint.caps.type_bulk ? "BULK, INT" : "",
> +                        priv_ep->endpoint.caps.type_iso ? "ISO" : "");
> +
> +               INIT_LIST_HEAD(&priv_ep->pending_req_list);
> +               INIT_LIST_HEAD(&priv_ep->deferred_req_list);
> +               INIT_LIST_HEAD(&priv_ep->descmiss_req_list);
> +       }
> +
> +       return 0;
> +err:
> +       cdns3_free_all_eps(priv_dev);
> +       return -ENOMEM;
> +}
> +
> +static void cdns3_gadget_disable(struct cdns3 *cdns)
> +{
> +       struct cdns3_device *priv_dev;
> +
> +       priv_dev = cdns->gadget_dev;
> +
> +       if (priv_dev->gadget_driver)
> +               priv_dev->gadget_driver->disconnect(&priv_dev->gadget);
> +
> +       usb_gadget_disconnect(&priv_dev->gadget);
> +       priv_dev->gadget.speed = USB_SPEED_UNKNOWN;
> +}
> +
> +void cdns3_gadget_exit(struct cdns3 *cdns)
> +{
> +       struct cdns3_device *priv_dev;
> +
> +       priv_dev = cdns->gadget_dev;
> +
> +       cdns3_gadget_disable(cdns);
> +
> +       devm_free_irq(cdns->dev, cdns->irq, cdns);
> +
> +       pm_runtime_mark_last_busy(cdns->dev);
> +       pm_runtime_put_autosuspend(cdns->dev);
> +
> +       usb_del_gadget_udc(&priv_dev->gadget);
> +
> +       cdns3_free_all_eps(priv_dev);
> +
> +       dma_free_coherent(priv_dev->sysdev, 8, priv_dev->setup_buf,
> +                         priv_dev->setup_dma);
> +
> +       kfree(priv_dev->zlp_buf);
> +       kfree(priv_dev);
> +       cdns->gadget_dev = NULL;
> +}
> +
> +static int cdns3_gadget_start(struct cdns3 *cdns)
> +{
> +       struct cdns3_device *priv_dev;
> +       u32 max_speed;
> +       int ret;
> +
> +       priv_dev = kzalloc(sizeof(*priv_dev), GFP_KERNEL);
> +       if (!priv_dev)
> +               return -ENOMEM;
> +
> +       cdns->gadget_dev = priv_dev;
> +       priv_dev->sysdev = cdns->dev;
> +       priv_dev->dev = cdns->dev;
> +       priv_dev->regs = cdns->dev_regs;
> +
> +       max_speed = usb_get_maximum_speed(cdns->dev);
> +
> +       /* Check the maximum_speed parameter */
> +       switch (max_speed) {
> +       case USB_SPEED_FULL:
> +       case USB_SPEED_HIGH:
> +       case USB_SPEED_SUPER:
> +               break;
> +       default:
> +               dev_err(cdns->dev, "invalid maximum_speed parameter %d\n",
> +                       max_speed);
> +               /* fall through */
> +       case USB_SPEED_UNKNOWN:
> +               /* default to superspeed */
> +               max_speed = USB_SPEED_SUPER;
> +               break;
> +       }
> +
> +       /* fill gadget fields */
> +       priv_dev->gadget.max_speed = max_speed;
> +       priv_dev->gadget.speed = USB_SPEED_UNKNOWN;
> +       priv_dev->gadget.ops = &cdns3_gadget_ops;
> +       priv_dev->gadget.name = "usb-ss-gadget";
> +       priv_dev->gadget.sg_supported = 1;
> +
> +       spin_lock_init(&priv_dev->lock);
> +       INIT_WORK(&priv_dev->pending_status_wq,
> +                 cdns3_pending_setup_status_handler);
> +
> +       /* initialize endpoint container */
> +       INIT_LIST_HEAD(&priv_dev->gadget.ep_list);
> +
> +       ret = cdns3_init_eps(priv_dev);
> +       if (ret) {
> +               dev_err(priv_dev->dev, "Failed to create endpoints\n");
> +               goto err1;
> +       }
> +
> +       /* allocate memory for setup packet buffer */
> +       priv_dev->setup_buf = dma_alloc_coherent(priv_dev->sysdev, 8,
> +                                                &priv_dev->setup_dma, GFP_DMA);
> +       if (!priv_dev->setup_buf) {
> +               dev_err(priv_dev->dev, "Failed to allocate memory for SETUP buffer\n");
> +               ret = -ENOMEM;
> +               goto err2;
> +       }
> +
> +       priv_dev->dev_ver = readl(&priv_dev->regs->usb_cap6);
> +       dev_dbg(priv_dev->dev, "Device Controller version: %08x\n",
> +               readl(&priv_dev->regs->usb_cap6));
> +       dev_dbg(priv_dev->dev, "USB Capabilities:: %08x\n",
> +               readl(&priv_dev->regs->usb_cap1));
> +       dev_dbg(priv_dev->dev, "On-Chip memory cnfiguration: %08x\n",
> +               readl(&priv_dev->regs->usb_cap2));
> +
> +       priv_dev->zlp_buf = kzalloc(CDNS3_EP_ZLP_BUF_SIZE, GFP_KERNEL);
> +       if (!priv_dev->zlp_buf) {
> +               ret = -ENOMEM;
> +               goto err3;
> +       }
> +
> +       /* add USB gadget device */
> +       ret = usb_add_gadget_udc(priv_dev->dev, &priv_dev->gadget);
> +       if (ret < 0) {
> +               dev_err(priv_dev->dev,
> +                       "Failed to register USB device controller\n");
> +               goto err4;
> +       }
> +
> +       return 0;
> +err4:
> +       kfree(priv_dev->zlp_buf);
> +err3:
> +       dma_free_coherent(priv_dev->sysdev, 8, priv_dev->setup_buf,
> +                         priv_dev->setup_dma);
> +err2:
> +       cdns3_free_all_eps(priv_dev);
> +err1:
> +       cdns->gadget_dev = NULL;
> +       return ret;
> +}
> +
> +static int __cdns3_gadget_init(struct cdns3 *cdns)
> +{
> +       struct cdns3_device *priv_dev;
> +       unsigned long flags;
> +       int ret = 0;
> +
> +       ret = cdns3_gadget_start(cdns);
> +       if (ret)
> +               return ret;
> +
> +       priv_dev = cdns->gadget_dev;
> +       ret = devm_request_threaded_irq(cdns->dev, cdns->irq,
> +                                       cdns3_device_irq_handler,
> +                                       cdns3_device_thread_irq_handler,
> +                                       IRQF_SHARED, dev_name(cdns->dev), cdns);
> +       if (ret)
> +               goto err0;
> +
> +       pm_runtime_get_sync(cdns->dev);
> +       spin_lock_irqsave(&priv_dev->lock, flags);
> +       spin_unlock_irqrestore(&priv_dev->lock, flags);
> +       return 0;
> +err0:
> +       cdns3_gadget_exit(cdns);
> +       return ret;
> +}
> +
> +static int cdns3_gadget_suspend(struct cdns3 *cdns, bool do_wakeup)
> +{
> +       cdns3_gadget_disable(cdns);
> +       return 0;
> +}
> +
> +static int cdns3_gadget_resume(struct cdns3 *cdns, bool hibernated)
> +{
> +       struct cdns3_device *priv_dev;
> +       unsigned long flags;
> +
> +       priv_dev = cdns->gadget_dev;
> +       spin_lock_irqsave(&priv_dev->lock, flags);
> +
> +       if (!priv_dev->gadget_driver) {
> +               spin_unlock_irqrestore(&priv_dev->lock, flags);
> +               return 0;
> +       }
> +
> +       cdns3_gadget_config(priv_dev);
> +       spin_unlock_irqrestore(&priv_dev->lock, flags);
> +       return 0;
> +}
> +
> +/**
> + * cdns3_gadget_init - initialize device structure
> + *
> + * cdns: cdns3 instance
> + *
> + * This function initializes the gadget.
> + */
> +int cdns3_gadget_init(struct cdns3 *cdns)
> +{
> +       struct cdns3_role_driver *rdrv;
> +
> +       rdrv = devm_kzalloc(cdns->dev, sizeof(*rdrv), GFP_KERNEL);
> +       if (!rdrv)
> +               return -ENOMEM;
> +
> +       rdrv->start     = __cdns3_gadget_init;
> +       rdrv->stop      = cdns3_gadget_exit;
> +       rdrv->suspend   = cdns3_gadget_suspend;
> +       rdrv->resume    = cdns3_gadget_resume;
> +       rdrv->state     = CDNS3_ROLE_STATE_INACTIVE;
> +       rdrv->name      = "gadget";
> +       cdns->roles[CDNS3_ROLE_GADGET] = rdrv;
> +
> +       return 0;
> +}
> diff --git a/drivers/usb/cdns3/gadget.h b/drivers/usb/cdns3/gadget.h
> new file mode 100644
> index 000000000000..41cec7f085ad
> --- /dev/null
> +++ b/drivers/usb/cdns3/gadget.h
> @@ -0,0 +1,1206 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +/*
> + * USBSS device controller driver header file
> + *
> + * Copyright (C) 2018 Cadence.
> + * Copyright (C) 2017-2018 NXP
> + *
> + * Author: Pawel Laszczak <pawell@cadence.com>
> + *         Pawel Jez <pjez@cadence.com>
> + *         Peter Chen <peter.chen@nxp.com>
> + */
> +#ifndef __LINUX_CDNS3_GADGET
> +#define __LINUX_CDNS3_GADGET
> +#include <linux/usb/gadget.h>
> +
> +/*
> + * USBSS-DEV register interface.
> + * This corresponds to the USBSS Device Controller Interface
> + */
> +
> +/**
> + * struct cdns3_usb_regs - device controller registers.
> + * @usb_conf:      Global Configuration Register.
> + * @usb_sts:       Global Status Register.
> + * @usb_cmd:       Global Command Register.
> + * @usb_iptn:      ITP/SOF number Register.
> + * @usb_lpm:       Global Command Register.
> + * @usb_ien:       USB Interrupt Enable Register.
> + * @usb_ists:      USB Interrupt Status Register.
> + * @ep_sel:        Endpoint Select Register.
> + * @ep_traddr:     Endpoint Transfer Ring Address Register.
> + * @ep_cfg:        Endpoint Configuration Register.
> + * @ep_cmd:        Endpoint Command Register.
> + * @ep_sts:        Endpoint Status Register.
> + * @ep_sts_sid:    Endpoint Status Register.
> + * @ep_sts_en:     Endpoint Status Register Enable.
> + * @drbl:          Doorbell Register.
> + * @ep_ien:        EP Interrupt Enable Register.
> + * @ep_ists:       EP Interrupt Status Register.
> + * @usb_pwr:       Global Power Configuration Register.
> + * @usb_conf2:     Global Configuration Register 2.
> + * @usb_cap1:      Capability Register 1.
> + * @usb_cap2:      Capability Register 2.
> + * @usb_cap3:      Capability Register 3.
> + * @usb_cap4:      Capability Register 4.
> + * @usb_cap5:      Capability Register 5.
> + * @usb_cap6:      Capability Register 6.
> + * @usb_cpkt1:     Custom Packet Register 1.
> + * @usb_cpkt2:     Custom Packet Register 2.
> + * @usb_cpkt3:     Custom Packet Register 3.
> + * @reserved1:     Reserved.
> + * @cfg_regs:      Configuration registers.
> + * @reserved2:     Reserved.
> + * @dma_axi_ctrl:  AXI Control register.
> + * @dma_axi_id:    AXI ID register.
> + * @dma_axi_cap:   AXI Capability register.
> + * @dma_axi_ctrl0: AXI Control 0 register.
> + * @dma_axi_ctrl1: AXI Control 1 register.
> + */
> +struct cdns3_usb_regs {
> +       __le32 usb_conf;
> +       __le32 usb_sts;
> +       __le32 usb_cmd;
> +       __le32 usb_iptn;
> +       __le32 usb_lpm;
> +       __le32 usb_ien;
> +       __le32 usb_ists;
> +       __le32 ep_sel;
> +       __le32 ep_traddr;
> +       __le32 ep_cfg;
> +       __le32 ep_cmd;
> +       __le32 ep_sts;
> +       __le32 ep_sts_sid;
> +       __le32 ep_sts_en;
> +       __le32 drbl;
> +       __le32 ep_ien;
> +       __le32 ep_ists;
> +       __le32 usb_pwr;
> +       __le32 usb_conf2;
> +       __le32 usb_cap1;
> +       __le32 usb_cap2;
> +       __le32 usb_cap3;
> +       __le32 usb_cap4;
> +       __le32 usb_cap5;
> +       __le32 usb_cap6;
> +       __le32 usb_cpkt1;
> +       __le32 usb_cpkt2;
> +       __le32 usb_cpkt3;
> +       __le32 reserved1[36];
> +       __le32 cfg_reg1;
> +       __le32 dbg_link1;
> +       __le32 dbg_link2;
> +       __le32 cfg_regs[74];
> +       __le32 reserved2[34];
> +       __le32 dma_axi_ctrl;
> +       __le32 dma_axi_id;
> +       __le32 dma_axi_cap;
> +       __le32 dma_axi_ctrl0;
> +       __le32 dma_axi_ctrl1;
> +};
> +
> +/* USB_CONF - bitmasks */
> +/* Reset USB device configuration. */
> +#define USB_CONF_CFGRST                BIT(0)
> +/* Set Configuration. */
> +#define USB_CONF_CFGSET                BIT(1)
> +/* Disconnect USB device in SuperSpeed. */
> +#define USB_CONF_USB3DIS       BIT(3)
> +/* Disconnect USB device in HS/FS */
> +#define USB_CONF_USB2DIS       BIT(4)
> +/* Little Endian access - default */
> +#define USB_CONF_LENDIAN       BIT(5)
> +/*
> + * Big Endian access. Driver assume that byte order for
> + * SFRs access always is as Little Endian so this bit
> + * is not used.
> + */
> +#define USB_CONF_BENDIAN       BIT(6)
> +/* Device software reset. */
> +#define USB_CONF_SWRST         BIT(7)
> +/* Singular DMA transfer mode. */
> +#define USB_CONF_DSING         BIT(8)
> +/* Multiple DMA transfers mode. */
> +#define USB_CONF_DMULT         BIT(9)
> +/* DMA clock turn-off enable. */
> +#define USB_CONF_DMAOFFEN      BIT(10)
> +/* DMA clock turn-off disable. */
> +#define USB_CONF_DMAOFFDS      BIT(11)
> +/* Clear Force Full Speed. */
> +#define USB_CONF_CFORCE_FS     BIT(12)
> +/* Set Force Full Speed. */
> +#define USB_CONF_SFORCE_FS     BIT(13)
> +/* Device enable. */
> +#define USB_CONF_DEVEN         BIT(14)
> +/* Device disable. */
> +#define USB_CONF_DEVDS         BIT(15)
> +/* L1 LPM state entry enable (used in HS/FS mode). */
> +#define USB_CONF_L1EN          BIT(16)
> +/* L1 LPM state entry disable (used in HS/FS mode). */
> +#define USB_CONF_L1DS          BIT(17)
> +/* USB 2.0 clock gate disable. */
> +#define USB_CONF_CLK2OFFEN     BIT(18)
> +/* USB 2.0 clock gate enable. */
> +#define USB_CONF_CLK2OFFDS     BIT(19)
> +/* L0 LPM state entry request (used in HS/FS mode). */
> +#define USB_CONF_LGO_L0                BIT(20)
> +/* USB 3.0 clock gate disable. */
> +#define USB_CONF_CLK3OFFEN     BIT(21)
> +/* USB 3.0 clock gate enable. */
> +#define USB_CONF_CLK3OFFDS     BIT(22)
> +/* Bit 23 is reserved*/
> +/* U1 state entry enable (used in SS mode). */
> +#define USB_CONF_U1EN          BIT(24)
> +/* U1 state entry disable (used in SS mode). */
> +#define USB_CONF_U1DS          BIT(25)
> +/* U2 state entry enable (used in SS mode). */
> +#define USB_CONF_U2EN          BIT(26)
> +/* U2 state entry disable (used in SS mode). */
> +#define USB_CONF_U2DS          BIT(27)
> +/* U0 state entry request (used in SS mode). */
> +#define USB_CONF_LGO_U0                BIT(28)
> +/* U1 state entry request (used in SS mode). */
> +#define USB_CONF_LGO_U1                BIT(29)
> +/* U2 state entry request (used in SS mode). */
> +#define USB_CONF_LGO_U2                BIT(30)
> +/* SS.Inactive state entry request (used in SS mode) */
> +#define USB_CONF_LGO_SSINACT   BIT(31)
> +
> +/* USB_STS - bitmasks */
> +/*
> + * Configuration status.
> + * 1 - device is in the configured state.
> + * 0 - device is not configured.
> + */
> +#define USB_STS_CFGSTS_MASK    BIT(0)
> +#define USB_STS_CFGSTS(p)      ((p) & USB_STS_CFGSTS_MASK)
> +/*
> + * On-chip memory overflow.
> + * 0 - On-chip memory status OK.
> + * 1 - On-chip memory overflow.
> + */
> +#define USB_STS_OV_MASK                BIT(1)
> +#define USB_STS_OV(p)          ((p) & USB_STS_OV_MASK)
> +/*
> + * SuperSpeed connection status.
> + * 0 - USB in SuperSpeed mode disconnected.
> + * 1 - USB in SuperSpeed mode connected.
> + */
> +#define USB_STS_USB3CONS_MASK  BIT(2)
> +#define USB_STS_USB3CONS(p)    ((p) & USB_STS_USB3CONS_MASK)
> +/*
> + * DMA transfer configuration status.
> + * 0 - single request.
> + * 1 - multiple TRB chain
> + */
> +#define USB_STS_DTRANS_MASK    BIT(3)
> +#define USB_STS_DTRANS(p)      ((p) & USB_STS_DTRANS_MASK)
> +/*
> + * Device speed.
> + * 0 - Undefined (value after reset).
> + * 1 - Low speed
> + * 2 - Full speed
> + * 3 - High speed
> + * 4 - Super speed
> + */
> +#define USB_STS_USBSPEED_MASK  GENMASK(6, 4)
> +#define USB_STS_USBSPEED(p)    (((p) & USB_STS_USBSPEED_MASK) >> 4)
> +#define USB_STS_LS             (0x1 << 4)
> +#define USB_STS_FS             (0x2 << 4)
> +#define USB_STS_HS             (0x3 << 4)
> +#define USB_STS_SS             (0x4 << 4)
> +#define DEV_UNDEFSPEED(p)      (((p) & USB_STS_USBSPEED_MASK) == (0x0 << 4))
> +#define DEV_LOWSPEED(p)                (((p) & USB_STS_USBSPEED_MASK) == USB_STS_LS)
> +#define DEV_FULLSPEED(p)       (((p) & USB_STS_USBSPEED_MASK) == USB_STS_FS)
> +#define DEV_HIGHSPEED(p)       (((p) & USB_STS_USBSPEED_MASK) == USB_STS_HS)
> +#define DEV_SUPERSPEED(p)      (((p) & USB_STS_USBSPEED_MASK) == USB_STS_SS)
> +/*
> + * Endianness for SFR access.
> + * 0 - Little Endian order (default after hardware reset).
> + * 1 - Big Endian order
> + */
> +#define USB_STS_ENDIAN_MASK    BIT(7)
> +#define USB_STS_ENDIAN(p)      ((p) & USB_STS_ENDIAN_MASK)
> +/*
> + * HS/FS clock turn-off status.
> + * 0 - hsfs clock is always on.
> + * 1 - hsfs clock turn-off in L2 (HS/FS mode) is enabled
> + *          (default after hardware reset).
> + */
> +#define USB_STS_CLK2OFF_MASK   BIT(8)
> +#define USB_STS_CLK2OFF(p)     ((p) & USB_STS_CLK2OFF_MASK)
> +/*
> + * PCLK clock turn-off status.
> + * 0 - pclk clock is always on.
> + * 1 - pclk clock turn-off in U3 (SS mode) is enabled
> + *          (default after hardware reset).
> + */
> +#define USB_STS_CLK3OFF_MASK   BIT(9)
> +#define USB_STS_CLK3OFF(p)     ((p) & USB_STS_CLK3OFF_MASK)
> +/*
> + * Controller in reset state.
> + * 0 - Internal reset is active.
> + * 1 - Internal reset is not active and controller is fully operational.
> + */
> +#define USB_STS_IN_RST_MASK    BIT(10)
> +#define USB_STS_IN_RST(p)      ((p) & USB_STS_IN_RST_MASK)
> +/*
> + * Device enable Status.
> + * 0 - USB device is disabled (VBUS input is disconnected from internal logic).
> + * 1 - USB device is enabled (VBUS input is connected to the internal logic).
> + */
> +#define USB_STS_DEVS_MASK      BIT(14)
> +#define USB_STS_DEVS(p)                ((p) & USB_STS_DEVS_MASK)
> +/*
> + * DAddress statuss.
> + * 0 - USB device is default state.
> + * 1 - USB device is at least in address state.
> + */
> +#define USB_STS_ADDRESSED_MASK BIT(15)
> +#define USB_STS_ADDRESSED(p)   ((p) & USB_STS_ADDRESSED_MASK)
> +/*
> + * L1 LPM state enable status (used in HS/FS mode).
> + * 0 - Entering to L1 LPM state disabled.
> + * 1 - Entering to L1 LPM state enabled.
> + */
> +#define USB_STS_L1ENS_MASK     BIT(16)
> +#define USB_STS_L1ENS(p)       ((p) & USB_STS_L1ENS_MASK)
> +/*
> + * Internal VBUS connection status (used both in HS/FS  and SS mode).
> + * 0 - internal VBUS is not detected.
> + * 1 - internal VBUS is detected.
> + */
> +#define USB_STS_VBUSS_MASK     BIT(17)
> +#define USB_STS_VBUSS(p)       ((p) & USB_STS_VBUSS_MASK)
> +/*
> + * HS/FS LPM  state (used in FS/HS mode).
> + * 0 - L0 State
> + * 1 - L1 State
> + * 2 - L2 State
> + * 3 - L3 State
> + */
> +#define USB_STS_LPMST_MASK     GENMASK(19, 18)
> +#define DEV_L0_STATE(p)                (((p) & USB_STS_LPMST_MASK) == (0x0 << 18))
> +#define DEV_L1_STATE(p)                (((p) & USB_STS_LPMST_MASK) == (0x1 << 18))
> +#define DEV_L2_STATE(p)                (((p) & USB_STS_LPMST_MASK) == (0x2 << 18))
> +#define DEV_L3_STATE(p)                (((p) & USB_STS_LPMST_MASK) == (0x3 << 18))
> +/*
> + * Disable HS status (used in FS/HS mode).
> + * 0 - the disconnect bit for HS/FS mode is set .
> + * 1 - the disconnect bit for HS/FS mode is not set.
> + */
> +#define USB_STS_USB2CONS_MASK  BIT(20)
> +#define USB_STS_USB2CONS(p)    ((p) & USB_STS_USB2CONS_MASK)
> +/*
> + * HS/FS mode connection status (used in FS/HS mode).
> + * 0 - High Speed operations in USB2.0 (FS/HS) mode not disabled.
> + * 1 - High Speed operations in USB2.0 (FS/HS).
> + */
> +#define USB_STS_DISABLE_HS_MASK        BIT(21)
> +#define USB_STS_DISABLE_HS(p)  ((p) & USB_STS_DISABLE_HS_MASK)
> +/*
> + * U1 state enable status (used in SS mode).
> + * 0 - Entering to  U1 state disabled.
> + * 1 - Entering to  U1 state enabled.
> + */
> +#define USB_STS_U1ENS_MASK     BIT(24)
> +#define USB_STS_U1ENS(p)       ((p) & USB_STS_U1ENS_MASK)
> +/*
> + * U2 state enable status (used in SS mode).
> + * 0 - Entering to  U2 state disabled.
> + * 1 - Entering to  U2 state enabled.
> + */
> +#define USB_STS_U2ENS_MASK     BIT(25)
> +#define USB_STS_U2ENS(p)       ((p) & USB_STS_U2ENS_MASK)
> +/*
> + * SuperSpeed Link LTSSM state. This field reflects USBSS-DEV current
> + * SuperSpeed link state
> + */
> +#define USB_STS_LST_MASK       GENMASK(29, 26)
> +#define DEV_LST_U0             (((p) & USB_STS_LST_MASK) == (0x0 << 26))
> +#define DEV_LST_U1             (((p) & USB_STS_LST_MASK) == (0x1 << 26))
> +#define DEV_LST_U2             (((p) & USB_STS_LST_MASK) == (0x2 << 26))
> +#define DEV_LST_U3             (((p) & USB_STS_LST_MASK) == (0x3 << 26))
> +#define DEV_LST_DISABLED       (((p) & USB_STS_LST_MASK) == (0x4 << 26))
> +#define DEV_LST_RXDETECT       (((p) & USB_STS_LST_MASK) == (0x5 << 26))
> +#define DEV_LST_INACTIVE       (((p) & USB_STS_LST_MASK) == (0x6 << 26))
> +#define DEV_LST_POLLING                (((p) & USB_STS_LST_MASK) == (0x7 << 26))
> +#define DEV_LST_RECOVERY       (((p) & USB_STS_LST_MASK) == (0x8 << 26))
> +#define DEV_LST_HOT_RESET      (((p) & USB_STS_LST_MASK) == (0x9 << 26))
> +#define DEV_LST_COMP_MODE      (((p) & USB_STS_LST_MASK) == (0xa << 26))
> +#define DEV_LST_LB_STATE       (((p) & USB_STS_LST_MASK) == (0xb << 26))
> +/*
> + * DMA clock turn-off status.
> + * 0 - DMA clock is always on (default after hardware reset).
> + * 1 - DMA clock turn-off in U1, U2 and U3 (SS mode) is enabled.
> + */
> +#define USB_STS_DMAOFF_MASK    BIT(30)
> +#define USB_STS_DMAOFF(p)      ((p) & USB_STS_DMAOFF_MASK)
> +/*
> + * SFR Endian statuss.
> + * 0 - Little Endian order (default after hardware reset).
> + * 1 - Big Endian order.
> + */
> +#define USB_STS_ENDIAN2_MASK   BIT(31)
> +#define USB_STS_ENDIAN2(p)     ((p) & USB_STS_ENDIAN2_MASK)
> +
> +/* USB_CMD -  bitmasks */
> +/* Set Function Address */
> +#define USB_CMD_SET_ADDR       BIT(0)
> +/*
> + * Function Address This field is saved to the device only when the field
> + * SET_ADDR is set '1 ' during write to USB_CMD register.
> + * Software is responsible for entering the address of the device during
> + * SET_ADDRESS request service. This field should be set immediately after
> + * the SETUP packet is decoded, and prior to confirmation of the status phase
> + */
> +#define USB_CMD_FADDR_MASK     GENMASK(7, 1)
> +#define USB_CMD_FADDR(p)       (((p) << 1) & USB_CMD_FADDR_MASK)
> +/* Send Function Wake Device Notification TP (used only in SS mode). */
> +#define USB_CMD_SDNFW          BIT(8)
> +/* Set Test Mode (used only in HS/FS mode). */
> +#define USB_CMD_STMODE         BIT(9)
> +/* Test mode selector (used only in HS/FS mode) */
> +#define USB_STS_TMODE_SEL_MASK GENMASK(11, 10)
> +#define USB_STS_TMODE_SEL(p)   (((p) << 10) & USB_STS_TMODE_SEL_MASK)
> +/*
> + *  Send Latency Tolerance Message Device Notification TP (used only
> + *  in SS mode).
> + */
> +#define USB_CMD_SDNLTM         BIT(12)
> +/* Send Custom Transaction Packet (used only in SS mode) */
> +#define USB_CMD_SPKT           BIT(13)
> +/*Device Notification 'Function Wake' - Interface value (only in SS mode. */
> +#define USB_CMD_DNFW_INT_MASK  GENMASK(23, 16)
> +#define USB_STS_DNFW_INT(p)    (((p) << 16) & USB_CMD_DNFW_INT_MASK)
> +/*
> + * Device Notification 'Latency Tolerance Message' -373 BELT value [7:0]
> + * (used only in SS mode).
> + */
> +#define USB_CMD_DNLTM_BELT_MASK        GENMASK(27, 16)
> +#define USB_STS_DNLTM_BELT(p)  (((p) << 16) & USB_CMD_DNLTM_BELT_MASK)
> +
> +/* USB_ITPN - bitmasks */
> +/*
> + * ITP(SS) / SOF (HS/FS) number
> + * In SS mode this field represent number of last ITP received from host.
> + * In HS/FS mode this field represent number of last SOF received from host.
> + */
> +#define USB_ITPN_MASK          GENMASK(13, 0)
> +#define USB_ITPN(p)            ((p) & USB_ITPN_MASK)
> +
> +/* USB_LPM - bitmasks */
> +/* Host Initiated Resume Duration. */
> +#define USB_LPM_HIRD_MASK      GENMASK(3, 0)
> +#define USB_LPM_HIRD(p)                ((p) & USB_LPM_HIRD_MASK)
> +/* Remote Wakeup Enable (bRemoteWake). */
> +#define USB_LPM_BRW            BIT(4)
> +
> +/* USB_IEN - bitmasks */
> +/* SS connection interrupt enable */
> +#define USB_IEN_CONIEN         BIT(0)
> +/* SS disconnection interrupt enable. */
> +#define USB_IEN_DISIEN         BIT(1)
> +/* USB SS warm reset interrupt enable. */
> +#define USB_IEN_UWRESIEN       BIT(2)
> +/* USB SS hot reset interrupt enable */
> +#define USB_IEN_UHRESIEN       BIT(3)
> +/* SS link U3 state enter interrupt enable (suspend).*/
> +#define USB_IEN_U3ENTIEN       BIT(4)
> +/* SS link U3 state exit interrupt enable (wakeup). */
> +#define USB_IEN_U3EXTIEN       BIT(5)
> +/* SS link U2 state enter interrupt enable.*/
> +#define USB_IEN_U2ENTIEN       BIT(6)
> +/* SS link U2 state exit interrupt enable.*/
> +#define USB_IEN_U2EXTIEN       BIT(7)
> +/* SS link U1 state enter interrupt enable.*/
> +#define USB_IEN_U1ENTIEN       BIT(8)
> +/* SS link U1 state exit interrupt enable.*/
> +#define USB_IEN_U1EXTIEN       BIT(9)
> +/* ITP/SOF packet detected interrupt enable.*/
> +#define USB_IEN_ITPIEN         BIT(10)
> +/* Wakeup interrupt enable.*/
> +#define USB_IEN_WAKEIEN                BIT(11)
> +/* Send Custom Packet interrupt enable.*/
> +#define USB_IEN_SPKTIEN                BIT(12)
> +/* HS/FS mode connection interrupt enable.*/
> +#define USB_IEN_CON2IEN                BIT(16)
> +/* HS/FS mode disconnection interrupt enable.*/
> +#define USB_IEN_DIS2IEN                BIT(17)
> +/* USB reset (HS/FS mode) interrupt enable.*/
> +#define USB_IEN_U2RESIEN       BIT(18)
> +/* LPM L2 state enter interrupt enable.*/
> +#define USB_IEN_L2ENTIEN       BIT(20)
> +/* LPM  L2 state exit interrupt enable.*/
> +#define USB_IEN_L2EXTIEN       BIT(21)
> +/* LPM L1 state enter interrupt enable.*/
> +#define USB_IEN_L1ENTIEN       BIT(24)
> +/* LPM  L1 state exit interrupt enable.*/
> +#define USB_IEN_L1EXTIEN       BIT(25)
> +/* Configuration reset interrupt enable.*/
> +#define USB_IEN_CFGRESIEN      BIT(26)
> +/* Start of the USB SS warm reset interrupt enable.*/
> +#define USB_IEN_UWRESSIEN      BIT(28)
> +/* End of the USB SS warm reset interrupt enable.*/
> +#define USB_IEN_UWRESEIEN      BIT(29)
> +
> +#define USB_IEN_INIT  (USB_IEN_U2RESIEN | USB_ISTS_DIS2I | USB_IEN_CON2IEN \
> +                      | USB_IEN_UHRESIEN | USB_IEN_UWRESIEN | USB_IEN_DISIEN \
> +                      | USB_IEN_CONIEN | USB_IEN_U3EXTIEN | USB_IEN_L2ENTIEN \
> +                      | USB_IEN_L2EXTIEN)
> +
> +/* USB_ISTS - bitmasks */
> +/* SS Connection detected. */
> +#define USB_ISTS_CONI          BIT(0)
> +/* SS Disconnection detected. */
> +#define USB_ISTS_DISI          BIT(1)
> +/* UUSB warm reset detectede. */
> +#define USB_ISTS_UWRESI                BIT(2)
> +/* USB hot reset detected. */
> +#define USB_ISTS_UHRESI                BIT(3)
> +/* U3 link state enter detected (suspend).*/
> +#define USB_ISTS_U3ENTI                BIT(4)
> +/* U3 link state exit detected (wakeup). */
> +#define USB_ISTS_U3EXTI                BIT(5)
> +/* U2 link state enter detected.*/
> +#define USB_ISTS_U2ENTI                BIT(6)
> +/* U2 link state exit detected.*/
> +#define USB_ISTS_U2EXTI                BIT(7)
> +/* U1 link state enter detected.*/
> +#define USB_ISTS_U1ENTI                BIT(8)
> +/* U1 link state exit detected.*/
> +#define USB_ISTS_U1EXTI                BIT(9)
> +/* ITP/SOF packet detected.*/
> +#define USB_ISTS_ITPI          BIT(10)
> +/* Wakeup detected.*/
> +#define USB_ISTS_WAKEI         BIT(11)
> +/* Send Custom Packet detected.*/
> +#define USB_ISTS_SPKTI         BIT(12)
> +/* HS/FS mode connection detected.*/
> +#define USB_ISTS_CON2I         BIT(16)
> +/* HS/FS mode disconnection detected.*/
> +#define USB_ISTS_DIS2I         BIT(17)
> +/* USB reset (HS/FS mode) detected.*/
> +#define USB_ISTS_U2RESI                BIT(18)
> +/* LPM L2 state enter detected.*/
> +#define USB_ISTS_L2ENTI                BIT(20)
> +/* LPM  L2 state exit detected.*/
> +#define USB_ISTS_L2EXTI                BIT(21)
> +/* LPM L1 state enter detected.*/
> +#define USB_ISTS_L1ENTI                BIT(24)
> +/* LPM L1 state exit detected.*/
> +#define USB_ISTS_L1EXTI                BIT(25)
> +/* USB configuration reset detected.*/
> +#define USB_ISTS_CFGRESI       BIT(26)
> +/* Start of the USB warm reset detected.*/
> +#define USB_ISTS_UWRESSI       BIT(28)
> +/* End of the USB warm reset detected.*/
> +#define USB_ISTS_UWRESEI       BIT(29)
> +
> +/* USB_SEL - bitmasks */
> +#define EP_SEL_EPNO_MASK       GENMASK(3, 0)
> +/* Endpoint number. */
> +#define EP_SEL_EPNO(p)         ((p) & EP_SEL_EPNO_MASK)
> +/* Endpoint direction bit - 0 - OUT, 1 - IN. */
> +#define EP_SEL_DIR             BIT(7)
> +
> +#define select_ep_in(nr)       (EP_SEL_EPNO(p) | EP_SEL_DIR)
> +#define select_ep_out          (EP_SEL_EPNO(p))
> +
> +/* EP_TRADDR - bitmasks */
> +/* Transfer Ring address. */
> +#define EP_TRADDR_TRADDR(p)    ((p))
> +
> +/* EP_CFG - bitmasks */
> +/* Endpoint enable */
> +#define EP_CFG_ENABLE          BIT(0)
> +/*
> + *  Endpoint type.
> + * 1 - isochronous
> + * 2 - bulk
> + * 3 - interrupt
> + */
> +#define EP_CFG_EPTYPE_MASK     GENMASK(2, 1)
> +#define EP_CFG_EPTYPE(p)       (((p) << 1)  & EP_CFG_EPTYPE_MASK)
> +/* Stream support enable (only in SS mode). */
> +#define EP_CFG_STREAM_EN       BIT(3)
> +/* TDL check (only in SS mode for BULK EP). */
> +#define EP_CFG_TDL_CHK         BIT(4)
> +/* SID check (only in SS mode for BULK OUT EP). */
> +#define EP_CFG_SID_CHK         BIT(5)
> +/* DMA transfer endianness. */
> +#define EP_CFG_EPENDIAN                BIT(7)
> +/* Max burst size (used only in SS mode). */
> +#define EP_CFG_MAXBURST_MASK   GENMASK(11, 8)
> +#define EP_CFG_MAXBURST(p)     (((p) << 8) & EP_CFG_MAXBURST_MASK)
> +/* ISO max burst. */
> +#define EP_CFG_MULT_MASK       GENMASK(15, 14)
> +#define EP_CFG_MULT(p)         (((p) << 14) & EP_CFG_MULT)
> +/* ISO max burst. */
> +#define EP_CFG_MAXPKTSIZE_MASK GENMASK(26, 16)
> +#define EP_CFG_MAXPKTSIZE(p)   (((p) << 16) & EP_CFG_MAXPKTSIZE_MASK)
> +/* Max number of buffered packets. */
> +#define EP_CFG_BUFFERING_MASK  GENMASK(31, 27)
> +#define EP_CFG_BUFFERING(p)    (((p) << 27) & EP_CFG_BUFFERING_MASK)
> +
> +/* EP_CMD - bitmasks */
> +/* Endpoint reset. */
> +#define EP_CMD_EPRST           BIT(0)
> +/* Endpoint STALL set. */
> +#define EP_CMD_SSTALL          BIT(1)
> +/* Endpoint STALL clear. */
> +#define EP_CMD_CSTALL          BIT(2)
> +/* Send ERDY TP. */
> +#define EP_CMD_ERDY            BIT(3)
> +/* Request complete. */
> +#define EP_CMD_REQ_CMPL                BIT(5)
> +/* Transfer descriptor ready. */
> +#define EP_CMD_DRDY            BIT(6)
> +/* Data flush. */
> +#define EP_CMD_DFLUSH          BIT(7)
> +/*
> + * Transfer Descriptor Length write  (used only for Bulk Stream capable
> + * endpoints in SS mode).
> + */
> +#define EP_CMD_STDL            BIT(8)
> +/* Transfer Descriptor Length (used only in SS mode for bulk endpoints). */
> +#define EP_CMD_TDL_MASK                GENMASK(15, 9)
> +#define EP_CMD_TDL(p)          (((p) << 9) & EP_CMD_TDL_MASK)
> +/* ERDY Stream ID value (used in SS mode). */
> +#define EP_CMD_ERDY_SID_MASK   GENMASK(31, 16)
> +#define EP_CMD_ERDY_SID(p)     (((p) << 16) & EP_CMD_SID_MASK)
> +
> +/* EP_STS - bitmasks */
> +/* Setup transfer complete. */
> +#define EP_STS_SETUP           BIT(0)
> +/* Endpoint STALL status. */
> +#define EP_STS_STALL(p)                ((p) & BIT(1))
> +/* Interrupt On Complete. */
> +#define EP_STS_IOC             BIT(2)
> +/* Interrupt on Short Packet. */
> +#define EP_STS_ISP             BIT(3)
> +/* Transfer descriptor missing. */
> +#define EP_STS_DESCMIS         BIT(4)
> +/* Stream Rejected (used only in SS mode) */
> +#define EP_STS_STREAMR         BIT(5)
> +/* EXIT from MOVE DATA State (used only for stream transfers in SS mode). */
> +#define EP_STS_MD_EXIT         BIT(6)
> +/* TRB error. */
> +#define EP_STS_TRBERR          BIT(7)
> +/* Not ready (used only in SS mode). */
> +#define EP_STS_NRDY            BIT(8)
> +/* DMA busy. */
> +#define EP_STS_DBUSY(p)                ((p) & BIT(9))
> +/* Endpoint Buffer Empty */
> +#define EP_STS_BUFFEMPTY(p)    ((p) & BIT(10))
> +/* Current Cycle Status */
> +#define EP_STS_CCS(p)          ((p) & BIT(11))
> +/* Prime (used only in SS mode. */
> +#define EP_STS_PRIME           BIT(12)
> +/* Stream error (used only in SS mode). */
> +#define EP_STS_SIDERR          BIT(13)
> +/* OUT size mismatch. */
> +#define EP_STS_OUTSMM          BIT(14)
> +/* ISO transmission error. */
> +#define EP_STS_ISOERR          BIT(15)
> +/* Host Packet Pending (only for SS mode). */
> +#define EP_STS_HOSTPP(p)       ((p) & BIT(16))
> +/* Stream Protocol State Machine State (only for Bulk stream endpoints). */
> +#define EP_STS_SPSMST_MASK             GENMASK(18, 17)
> +#define EP_STS_SPSMST_DISABLED(p)      (((p) & EP_STS_SPSMST_MASK) >> 17)
> +#define EP_STS_SPSMST_IDLE(p)          (((p) & EP_STS_SPSMST_MASK) >> 17)
> +#define EP_STS_SPSMST_START_STREAM(p)  (((p) & EP_STS_SPSMST_MASK) >> 17)
> +#define EP_STS_SPSMST_MOVE_DATA(p)     (((p) & EP_STS_SPSMST_MASK) >> 17)
> +/* Interrupt On Transfer complete. */
> +#define EP_STS_IOT             BIT(19)
> +/* OUT queue endpoint number. */
> +#define EP_STS_OUTQ_NO_MASK    GENMASK(27, 24)
> +#define EP_STS_OUTQ_NO(p)      (((p) & EP_STS_OUTQ_NO_MASK) >> 24)
> +/* OUT queue valid flag. */
> +#define EP_STS_OUTQ_VAL_MASK   BIT(28)
> +#define EP_STS_OUTQ_VAL(p)     ((p) & EP_STS_OUTQ_VAL_MASK)
> +/* SETUP WAIT. */
> +#define EP_STS_STPWAIT         BIT(31)
> +
> +/* EP_STS_SID - bitmasks */
> +/* Stream ID (used only in SS mode). */
> +#define EP_STS_SID_MASK                GENMASK(15, 0)
> +#define EP_STS_SID(p)          ((p) & EP_STS_SID_MASK)
> +
> +/* EP_STS_EN - bitmasks */
> +/* SETUP interrupt enable. */
> +#define EP_STS_EN_SETUPEN      BIT(0)
> +/* OUT transfer missing descriptor enable. */
> +#define EP_STS_EN_DESCMISEN    BIT(4)
> +/* Stream Rejected enable. */
> +#define EP_STS_EN_STREAMREN    BIT(5)
> +/* Move Data Exit enable.*/
> +#define EP_STS_EN_MD_EXITEN    BIT(6)
> +/* TRB enable. */
> +#define EP_STS_EN_TRBERREN     BIT(7)
> +/* NRDY enable. */
> +#define EP_STS_EN_NRDYEN       BIT(8)
> +/* Prime enable. */
> +#define EP_STS_EN_PRIMEEEN     BIT(12)
> +/* Stream error enable. */
> +#define EP_STS_EN_SIDERREN     BIT(13)
> +/* OUT size mismatch enable. */
> +#define EP_STS_EN_OUTSMMEN     BIT(14)
> +/* ISO transmission error enable. */
> +#define EP_STS_EN_ISOERREN     BIT(15)
> +/* Interrupt on Transmission complete enable. */
> +#define EP_STS_EN_IOTEN                BIT(19)
> +/* Setup Wait interrupt enable. */
> +#define EP_STS_EN_STPWAITEN    BIT(31)
> +
> +/* DRBL- bitmasks */
> +#define DB_VALUE_BY_INDEX(index) (1 << (index))
> +#define DB_VALUE_EP0_OUT       BIT(0)
> +#define DB_VALUE_EP0_IN                BIT(16)
> +
> +/* EP_IEN - bitmasks */
> +#define EP_IEN(index)          (1 << (index))
> +#define EP_IEN_EP_OUT0         BIT(0)
> +#define EP_IEN_EP_IN0          BIT(16)
> +
> +/* EP_ISTS - bitmasks */
> +#define EP_ISTS(index)         (1 << (index))
> +#define EP_ISTS_EP_OUT0                BIT(0)
> +#define EP_ISTS_EP_IN0         BIT(16)
> +
> +/* EP_PWR- bitmasks */
> +/*Power Shut Off capability enable*/
> +#define PUSB_PWR_PSO_EN                BIT(0)
> +/*Power Shut Off capability disable*/
> +#define PUSB_PWR_PSO_DS                BIT(1)
> +/*
> + * Enables turning-off Reference Clock.
> + * This bit is optional and implemented only when support for OTG is
> + * implemented (indicated by OTG_READY bit set to '1').
> + */
> +#define PUSB_PWR_STB_CLK_SWITCH_EN     BIT(8)
> +/*
> + * Status bit indicating that operation required by STB_CLK_SWITCH_EN write
> + * is completed
> + */
> +#define PUSB_PWR_STB_CLK_SWITCH_DONE   BIT(9)
> +/* This bit informs if Fast Registers Access is enabled. */
> +#define PUSB_PWR_FST_REG_ACCESS_STAT   BIT(30)
> +/* Fast Registers Access Enable. */
> +#define PUSB_PWR_FST_REG_ACCESS        BIT(31)
> +
> +/* USB_CAP1- bitmasks */
> +/*
> + * SFR Interface type
> + * These field reflects type of SFR interface implemented:
> + * 0x0 - OCP
> + * 0x1 - AHB,
> + * 0x2 - PLB
> + * 0x3 - AXI
> + * 0x4-0xF - reserved
> + */
> +#define USB_CAP1_SFR_TYPE_MASK GENMASK(3, 0)
> +#define DEV_SFR_TYPE_OCP(p)    (((p) & USB_CAP1_SFR_TYPE_MASK) == 0x0)
> +#define DEV_SFR_TYPE_AHB(p)    (((p) & USB_CAP1_SFR_TYPE_MASK) == 0x1)
> +#define DEV_SFR_TYPE_PLB(p)    (((p) & USB_CAP1_SFR_TYPE_MASK) == 0x2)
> +#define DEV_SFR_TYPE_AXI(p)    (((p) & USB_CAP1_SFR_TYPE_MASK) == 0x3)
> +/*
> + * SFR Interface width
> + * These field reflects width of SFR interface implemented:
> + * 0x0 - 8 bit interface,
> + * 0x1 - 16 bit interface,
> + * 0x2 - 32 bit interface
> + * 0x3 - 64 bit interface
> + * 0x4-0xF - reserved
> + */
> +#define USB_CAP1_SFR_WIDTH_MASK        GENMASK(7, 4)
> +#define DEV_SFR_WIDTH_8(p)     (((p) & USB_CAP1_SFR_WIDTH_MASK) == (0x0 << 4))
> +#define DEV_SFR_WIDTH_16(p)    (((p) & USB_CAP1_SFR_WIDTH_MASK) == (0x1 << 4))
> +#define DEV_SFR_WIDTH_32(p)    (((p) & USB_CAP1_SFR_WIDTH_MASK) == (0x2 << 4))
> +#define DEV_SFR_WIDTH_64(p)    (((p) & USB_CAP1_SFR_WIDTH_MASK) == (0x3 << 4))
> +/*
> + * DMA Interface type
> + * These field reflects type of DMA interface implemented:
> + * 0x0 - OCP
> + * 0x1 - AHB,
> + * 0x2 - PLB
> + * 0x3 - AXI
> + * 0x4-0xF - reserved
> + */
> +#define USB_CAP1_DMA_TYPE_MASK GENMASK(11, 8)
> +#define DEV_DMA_TYPE_OCP(p)    (((p) & USB_CAP1_DMA_TYPE_MASK) == (0x0 << 8))
> +#define DEV_DMA_TYPE_AHB(p)    (((p) & USB_CAP1_DMA_TYPE_MASK) == (0x1 << 8))
> +#define DEV_DMA_TYPE_PLB(p)    (((p) & USB_CAP1_DMA_TYPE_MASK) == (0x2 << 8))
> +#define DEV_DMA_TYPE_AXI(p)    (((p) & USB_CAP1_DMA_TYPE_MASK) == (0x3 << 8))
> +/*
> + * DMA Interface width
> + * These field reflects width of DMA interface implemented:
> + * 0x0 - reserved,
> + * 0x1 - reserved,
> + * 0x2 - 32 bit interface
> + * 0x3 - 64 bit interface
> + * 0x4-0xF - reserved
> + */
> +#define USB_CAP1_DMA_WIDTH_MASK        GENMASK(15, 12)
> +#define DEV_DMA_WIDTH_32(p)    (((p) & USB_CAP1_DMA_WIDTH_MASK) == (0x2 << 12))
> +#define DEV_DMA_WIDTH_64(p)    (((p) & USB_CAP1_DMA_WIDTH_MASK) == (0x3 << 12))
> +/*
> + * USB3 PHY Interface type
> + * These field reflects type of USB3 PHY interface implemented:
> + * 0x0 - USB PIPE,
> + * 0x1 - RMMI,
> + * 0x2-0xF - reserved
> + */
> +#define USB_CAP1_U3PHY_TYPE_MASK GENMASK(19, 16)
> +#define DEV_U3PHY_PIPE(p) (((p) & USB_CAP1_U3PHY_TYPE_MASK) == (0x0 << 16))
> +#define DEV_U3PHY_RMMI(p) (((p) & USB_CAP1_U3PHY_TYPE_MASK) == (0x1 << 16))
> +/*
> + * USB3 PHY Interface width
> + * These field reflects width of USB3 PHY interface implemented:
> + * 0x0 - 8 bit PIPE interface,
> + * 0x1 - 16 bit PIPE interface,
> + * 0x2 - 32 bit PIPE interface,
> + * 0x3 - 64 bit PIPE interface
> + * 0x4-0xF - reserved
> + * Note: When SSIC interface is implemented this field shows the width of
> + * internal PIPE interface. The RMMI interface is always 20bit wide.
> + */
> +#define USB_CAP1_U3PHY_WIDTH_MASK GENMASK(23, 20)
> +#define DEV_U3PHY_WIDTH_8(p) \
> +       (((p) & USB_CAP1_U3PHY_WIDTH_MASK) == (0x0 << 20))
> +#define DEV_U3PHY_WIDTH_16(p) \
> +       (((p) & USB_CAP1_U3PHY_WIDTH_MASK) == (0x1 << 16))
> +#define DEV_U3PHY_WIDTH_32(p) \
> +       (((p) & USB_CAP1_U3PHY_WIDTH_MASK) == (0x2 << 20))
> +#define DEV_U3PHY_WIDTH_64(p) \
> +       (((p) & USB_CAP1_U3PHY_WIDTH_MASK) == (0x3 << 16))
> +
> +/*
> + * USB2 PHY Interface enable
> + * These field informs if USB2 PHY interface is implemented:
> + * 0x0 - interface NOT implemented,
> + * 0x1 - interface implemented
> + */
> +#define USB_CAP1_U2PHY_EN(p)   ((p) & BIT(24))
> +/*
> + * USB2 PHY Interface type
> + * These field reflects type of USB2 PHY interface implemented:
> + * 0x0 - UTMI,
> + * 0x1 - ULPI
> + */
> +#define DEV_U2PHY_ULPI(p)      ((p) & BIT(25))
> +/*
> + * USB2 PHY Interface width
> + * These field reflects width of USB2 PHY interface implemented:
> + * 0x0 - 8 bit interface,
> + * 0x1 - 16 bit interface,
> + * Note: The ULPI interface is always 8bit wide.
> + */
> +#define DEV_U2PHY_WIDTH_16(p)  ((p) & BIT(26))
> +/*
> + * OTG Ready
> + * 0x0 - pure device mode
> + * 0x1 - some features and ports for CDNS USB OTG controller are implemented.
> + */
> +#define USB_CAP1_OTG_READY(p)  ((p) & BIT(27))
> +
> +/* USB_CAP2- bitmasks */
> +/*
> + * The actual size of the connected On-chip RAM memory in kB:
> + * - 0 means 256 kB (max supported mem size)
> + * - value other than 0 reflects the mem size in kB
> + */
> +#define USB_CAP2_ACTUAL_MEM_SIZE(p) ((p) & GENMASK(7, 0))
> +/*
> + * Max supported mem size
> + * These field reflects width of on-chip RAM address bus width,
> + * which determines max supported mem size:
> + * 0x0-0x7 - reserved,
> + * 0x8 - support for 4kB mem,
> + * 0x9 - support for 8kB mem,
> + * 0xA - support for 16kB mem,
> + * 0xB - support for 32kB mem,
> + * 0xC - support for 64kB mem,
> + * 0xD - support for 128kB mem,
> + * 0xE - support for 256kB mem,
> + * 0xF - reserved
> + */
> +#define USB_CAP2_MAX_MEM_SIZE(p) ((p) & GENMASK(11, 8))
> +
> +/* USB_CAP3- bitmasks */
> +#define EP_IS_IMPLEMENTED(reg, index) ((reg) & (1 << (index)))
> +
> +/* USB_CAP4- bitmasks */
> +#define EP_SUPPORT_ISO(reg, index) ((reg) & (1 << (index)))
> +
> +/* USB_CAP5- bitmasks */
> +#define EP_SUPPORT_STREAM(reg, index) ((reg) & (1 << (index)))
> +
> +/* USB_CAP6- bitmasks */
> +/* The USBSS-DEV Controller  Internal build number. */
> +#define GET_DEV_VERSION__INTERNAL_NUMBER(p) ((p) & GENMASK(7, 0))
> +/* The USBSS-DEV Controller  version number. */
> +#define GET_DEV_VERSION(p) ((p) & GENMASK(31, 8))
> +
> +/* DBG_LINK1- bitmasks */
> +/*
> + * LFPS_MIN_DET_U1_EXIT value This parameter configures the minimum
> + * time required for decoding the received LFPS as an LFPS.U1_Exit.
> + */
> +#define DBG_LINK1_LFPS_MIN_DET_U1_EXIT(p)      ((p) & GENMASK(7, 0))
> +/*
> + * LFPS_MIN_GEN_U1_EXIT value This parameter configures the minimum time for
> + * phytxelecidle deassertion when LFPS.U1_Exit
> + */
> +#define DBG_LINK1_LFPS_MIN_GEN_U1_EXIT(p)      (((p) << 8) & GENMASK(15, 8))
> +/*
> + * RXDET_BREAK_DIS value This parameter configures terminating the Far-end
> + * Receiver termination detection sequence:
> + * 0: it is possible that USBSS_DEV will terminate Farend receiver
> + *    termination detection sequence
> + * 1: USBSS_DEV will not terminate Far-end receiver termination
> + *    detection sequence
> + */
> +#define DBG_LINK1_RXDET_BREAK_DIS              BIT(16)
> +/* LFPS_GEN_PING value This parameter configures the LFPS.Ping generation */
> +#define DBG_LINK1_LFPS_GEN_PING(p)             (((p) << 17) & GENMASK(21, 17))
> +/*
> + * Set the LFPS_MIN_DET_U1_EXIT value Writing '1' to this bit writes the
> + * LFPS_MIN_DET_U1_EXIT field value to the device. This bit is automatically
> + * cleared. Writing '0' has no effect
> + */
> +#define DBG_LINK1_LFPS_MIN_DET_U1_EXIT_SET     BIT(24)
> +/*
> + * Set the LFPS_MIN_GEN_U1_EXIT value. Writing '1' to this bit writes the
> + * LFPS_MIN_GEN_U1_EXIT field value to the device. This bit is automatically
> + * cleared. Writing '0' has no effect
> + */
> +#define DBG_LINK1_LFPS_MIN_GEN_U1_EXIT_SET     BIT(25)
> +/*
> + * Set the RXDET_BREAK_DIS value Writing '1' to this bit writes
> + * the RXDET_BREAK_DIS field value to the device. This bit is automatically
> + * cleared. Writing '0' has no effect
> + */
> +#define DBG_LINK1_RXDET_BREAK_DIS_SET          BIT(26)
> +/*
> + * Set the LFPS_GEN_PING_SET value Writing '1' to this bit writes
> + * the LFPS_GEN_PING field value to the device. This bit is automatically
> + * cleared. Writing '0' has no effect."
> + */
> +#define DBG_LINK1_LFPS_GEN_PING_SET            BIT(27)
> +
> +#define gadget_to_cdns3_device(g) (container_of(g, struct cdns3_device, gadget))
> +
> +#define ep_to_cdns3_ep(ep) (container_of(ep, struct cdns3_endpoint, endpoint))
> +
> +/*-------------------------------------------------------------------------*/
> +/*
> + * USBSS-DEV DMA interface.
> + */
> +#define TRBS_PER_SEGMENT       40
> +
> +#define ISO_MAX_INTERVAL       10
> +
> +/*
> + *Only for ISOC endpoints - maximum number of TRBs is calculated as
> + * pow(2, bInterval-1) * number of usb requests. It is limitation made by
> + * driver to save memory. Controller must prepare TRB for each ITP even
> + * if bInterval > 1. It's the reason why driver needs so many TRBs for
> + * isochronous endpoints.
> + */
> +#define TRBS_PER_ISOC_SEGMENT  (ISO_MAX_INTERVAL * 8)
> +
> +#define GET_TRBS_PER_SEGMENT(ep_type) ((ep_type) == USB_ENDPOINT_XFER_ISOC ? \
> +                                     TRBS_PER_ISOC_SEGMENT : TRBS_PER_SEGMENT)
> +/**
> + * struct cdns3_trb - represent Transfer Descriptor block.
> + * @buffer:    pointer to buffer data
> + * @length:    length of data
> + * @control:   control flags.
> + *
> + * This structure describes transfer block serviced by DMA module.
> + */
> +struct cdns3_trb {
> +       __le32 buffer;
> +       __le32 length;
> +       __le32 control;
> +};
> +
> +#define TRB_SIZE               (sizeof(struct cdns3_trb))
> +#define TRB_RING_SIZE          (TRB_SIZE * TRBS_PER_SEGMENT)
> +#define TRB_ISO_RING_SIZE      (TRB_SIZE * TRBS_PER_ISOC_SEGMENT)
> +#define TRB_CTRL_RING_SIZE     (TRB_SIZE * 2)
> +
> +/* TRB bit mask */
> +#define TRB_TYPE_BITMASK       GENMASK(15, 10)
> +#define TRB_TYPE(p)            ((p) << 10)
> +#define TRB_FIELD_TO_TYPE(p)   (((p) & TRB_TYPE_BITMASK) >> 10)
> +
> +/* TRB type IDs */
> +/* bulk, interrupt, isoc , and control data stage */
> +#define TRB_NORMAL             1
> +/* TRB for linking ring segments */
> +#define TRB_LINK               6
> +
> +/* Cycle bit - indicates TRB ownership by driver or hw*/
> +#define TRB_CYCLE              BIT(0)
> +/*
> + * When set to '1', the device will toggle its interpretation of the Cycle bit
> + */
> +#define TRB_TOGGLE             BIT(1)
> +
> +/* Interrupt on short packet*/
> +#define TRB_ISP                        BIT(2)
> +/*Setting this bit enables FIFO DMA operation mode*/
> +#define TRB_FIFO_MODE          BIT(3)
> +/* Set PCIe no snoop attribute */
> +#define TRB_CHAIN              BIT(4)
> +/* Interrupt on completion */
> +#define TRB_IOC                        BIT(5)
> +
> +/* stream ID bitmasks. */
> +#define TRB_STREAM_ID(p)       ((p) & GENMASK(31, 16))
> +
> +/* transfer_len bitmasks. */
> +#define TRB_LEN(p)             ((p) & GENMASK(16, 0))
> +
> +/* transfer_len bitmasks - bits 31:24 */
> +#define TRB_BURST_LEN(p)       ((p) & GENMASK(31, 24))
> +
> +/* Data buffer pointer bitmasks*/
> +#define TRB_BUFFER(p)          ((p) & GENMASK(31, 0))
> +
> +/*-------------------------------------------------------------------------*/
> +/* Driver numeric constants */
> +
> +/* Such declaration should be added to ch9.h */
> +#define USB_DEVICE_MAX_ADDRESS         127
> +
> +/* Endpoint init values */
> +#define CDNS3_EP_MAX_PACKET_LIMIT      1024
> +#define CDNS3_EP_MAX_STREAMS           15
> +#define CDNS3_EP0_MAX_PACKET_LIMIT     512
> +
> +/* All endpoints including EP0 */
> +#define CDNS3_ENDPOINTS_MAX_COUNT      32
> +#define CDNS3_EP_ZLP_BUF_SIZE          1024
> +
> +#define CDNS3_EP_BUF_SIZE              2       /* KB */
> +#define CDNS3_ALIGNED_BUF_SIZE         16384   /* Bytes */
> +#define CDNS3_MAX_NUM_DESCMISS_BUF     32
> +#define CDNS3_DESCMIS_BUF_SIZE         2048    /* Bytes */
> +/*-------------------------------------------------------------------------*/
> +/* Used structs */
> +
> +struct cdns3_device;
> +
> +/**
> + * struct cdns3_endpoint - extended device side representation of USB endpoint.
> + * @endpoint: usb endpoint
> + * @pending_req_list: list of requests queuing on transfer ring.
> + * @deferred_req_list: list of requests waiting for queuing on transfer ring.
> + * @descmiss_req_list: list of requests internally allocated by driver (WA1).
> + * @trb_pool: transfer ring - array of transaction buffers
> + * @trb_pool_dma: dma address of transfer ring
> + * @cdns3_dev: device associated with this endpoint
> + * @name: a human readable name e.g. ep1out
> + * @flags: specify the current state of endpoint
> + * @descmis_req: internal transfer object used for getting data from on-chip
> + *     buffer. It can happen only if function driver doesn't send usb_request
> + *     object on time.
> + * @aligned_buff: aligned to 8 bytes data buffer. Buffer address used in
> + *     TRB shall be aligned to 8.
> + * @aligned_dma_addr: dma address of aligned_buff
> + * @dir: endpoint direction
> + * @num: endpoint number (1 - 15)
> + * @type: set to bmAttributes & USB_ENDPOINT_XFERTYPE_MASK
> + * @interval: interval between packets used for ISOC endpoint.
> + * @free_trbs: number of free TRBs in transfer ring
> + * @num_trbs: number of all TRBs in transfer ring
> + * @pcs: producer cycle state
> + * @ccs: consumer cycle state
> + * @enqueue: enqueue index in transfer ring
> + * @dequeue: dequeue index in transfer ring
> + */
> +struct cdns3_endpoint {
> +       struct usb_ep           endpoint;
> +       struct list_head        pending_req_list;
> +       struct list_head        deferred_req_list;
> +       struct list_head        descmiss_req_list;
> +
> +       struct cdns3_trb        *trb_pool;
> +       dma_addr_t              trb_pool_dma;
> +
> +       struct cdns3_device     *cdns3_dev;
> +       char                    name[20];
> +
> +#define EP_ENABLED             BIT(0)
> +#define EP_STALL               BIT(1)
> +#define EP_WEDGE               BIT(2)
> +#define EP_TRANSFER_STARTED    BIT(3)
> +#define EP_UPDATE_EP_TRBADDR   BIT(4)
> +#define EP_PENDING_REQUEST     BIT(5)
> +#define EP_RING_FULL           BIT(6)
> +#define EP_CLAIMED             BIT(7)
> +#define EP_QUIRK_EXTRA_BUF_DET BIT(8)
> +#define EP_QUIRK_EXTRA_BUF_EN  BIT(9)
> +#define EP_QUIRK_END_TRANSFER  BIT(10)
> +
> +       u32                     flags;
> +
> +       struct cdns3_request    *descmis_req;
> +
> +       void                    *aligned_buff;
> +       dma_addr_t              aligned_dma_addr;
> +       u8                      dir;
> +       u8                      num;
> +       u8                      type;
> +       int                     interval;
> +
> +       int                     free_trbs;
> +       int                     num_trbs;
> +       u8                      pcs;
> +       u8                      ccs;
> +       int                     enqueue;
> +       int                     dequeue;
> +};
> +
> +/**
> + * struct cdns3_request - extended device side representation of usb_request
> + *                        object .
> + * @request: generic usb_request object describing single I/O request.
> + * @priv_ep: extended representation of usb_ep object
> + * @trb: the first TRB association with this request
> + * @start_trb: number of the first TRB in transfer ring
> + * @end_trb: number of the last TRB in transfer ring
> + * @flags: flag specifying special usage of request
> + * @list: used by internally allocated request to add to descmiss_req_list.
> + */
> +struct cdns3_request {
> +       struct usb_request      request;
> +       struct cdns3_endpoint   *priv_ep;
> +       struct cdns3_trb        *trb;
> +       int                     start_trb;
> +       int                     end_trb;
> +#define REQUEST_PENDING                BIT(0)
> +#define REQUEST_INTERNAL       BIT(1)
> +#define REQUEST_INTERNAL_CH    BIT(2)
> +#define REQUEST_ZLP            BIT(3)
> +       u32                     flags;
> +       struct list_head        list;
> +};
> +
> +#define to_cdns3_request(r) (container_of(r, struct cdns3_request, request))
> +
> +/*Stages used during enumeration process.*/
> +#define CDNS3_SETUP_STAGE              0x0
> +#define CDNS3_DATA_STAGE               0x1
> +#define CDNS3_STATUS_STAGE             0x2
> +
> +/**
> + * struct cdns3_device - represent USB device.
> + * @dev: pointer to device structure associated whit this controller
> + * @sysdev: pointer to the DMA capable device
> + * @gadget: device side representation of the peripheral controller
> + * @gadget_driver: pointer to the gadget driver
> + * @dev_ver: device controller version.
> + * @lock: for synchronizing
> + * @regs: base address for device side registers
> + * @setup_buf: used while processing usb control requests
> + * @setup_dma: dma address for setup_buf
> + * @zlp_buf - zlp buffer
> + * @ep0_stage: ep0 stage during enumeration process.
> + * @ep0_data_dir: direction for control transfer
> + * @eps: array of pointers to all endpoints with exclusion ep0
> + * @selected_ep: actually selected endpoint. It's used only to improve
> + *               performance.
> + * @isoch_delay: value from Set Isoch Delay request. Only valid on SS/SSP.
> + * @u1_allowed: allow device transition to u1 state
> + * @u2_allowed: allow device transition to u2 state
> + * @is_selfpowered: device is self powered
> + * @setup_pending: setup packet is processing by gadget driver
> + * @hw_configured_flag: hardware endpoint configuration was set.
> + * @wake_up_flag: allow device to remote up the host
> + * @status_completion_no_call: indicate that driver is waiting for status s
> + *     stage completion. It's used in deferred SET_CONFIGURATION request.
> + * @onchip_mem_allocated_size: actual size of on-chip memory assigned
> + *     to endpoints
> + * @pending_status_wq: workqueue handling status stage for deferred requests.
> + * @pending_status_request: request for which status stage was deferred
> + */
> +struct cdns3_device {
> +       struct device                   *dev;
> +       struct device                   *sysdev;
> +
> +       struct usb_gadget               gadget;
> +       struct usb_gadget_driver        *gadget_driver;
> +
> +#define CDNS_REVISION_V0               0x00024501
> +#define CDNS_REVISION_V1               0x00024509
> +       u32                             dev_ver;
> +
> +       /* generic spin-lock for drivers */
> +       spinlock_t                      lock;
> +
> +       struct cdns3_usb_regs           __iomem *regs;
> +
> +       struct usb_ctrlrequest          *setup_buf;
> +       dma_addr_t                      setup_dma;
> +       void                            *zlp_buf;
> +
> +       u8                              ep0_stage;
> +       int                             ep0_data_dir;
> +
> +       struct cdns3_endpoint           *eps[CDNS3_ENDPOINTS_MAX_COUNT];
> +
> +       u32                             selected_ep;
> +       u16                             isoch_delay;
> +
> +       unsigned                        u1_allowed:1;
> +       unsigned                        u2_allowed:1;
> +       unsigned                        is_selfpowered:1;
> +       unsigned                        setup_pending:1;
> +       int                             hw_configured_flag:1;
> +       int                             wake_up_flag:1;
> +       unsigned                        status_completion_no_call:1;
> +
> +       struct work_struct              pending_status_wq;
> +       struct usb_request              *pending_status_request;
> +
> +       /*in KB */
> +       int                             onchip_mem_allocated_size;
> +};
> +
> +int cdns3_handshake(void __iomem *ptr, u32 mask, u32 done, int usec);
> +void cdns3_set_register_bit(void __iomem *ptr, u32 mask);
> +dma_addr_t cdns3_trb_virt_to_dma(struct cdns3_endpoint *priv_ep,
> +                                struct cdns3_trb *trb);
> +enum usb_device_speed cdns3_get_speed(struct cdns3_device *priv_dev);
> +void cdns3_pending_setup_status_handler(struct work_struct *work);
> +void cdns3_hw_reset_eps_config(struct cdns3_device *priv_dev);
> +void cdns3_set_hw_configuration(struct cdns3_device *priv_dev);
> +void cdns3_select_ep(struct cdns3_device *priv_dev, u32 ep);
> +void cdns3_allow_enable_l1(struct cdns3_device *priv_dev, int enable);
> +struct usb_request *cdns3_next_request(struct list_head *list);
> +int cdns3_ep_run_transfer(struct cdns3_endpoint *priv_ep,
> +                         struct usb_request *request);
> +int cdns3_allocate_trb_pool(struct cdns3_endpoint *priv_ep);
> +u8 cdns3_ep_addr_to_index(u8 ep_addr);
> +int cdns3_gadget_ep_set_wedge(struct usb_ep *ep);
> +int cdns3_gadget_ep_set_halt(struct usb_ep *ep, int value);
> +struct usb_request *cdns3_gadget_ep_alloc_request(struct usb_ep *ep,
> +                                                 gfp_t gfp_flags);
> +void cdns3_gadget_ep_free_request(struct usb_ep *ep,
> +                                 struct usb_request *request);
> +int cdns3_gadget_ep_dequeue(struct usb_ep *ep, struct usb_request *request);
> +void cdns3_gadget_giveback(struct cdns3_endpoint *priv_ep,
> +                          struct cdns3_request *priv_req,
> +                          int status);
> +
> +int cdns3_init_ep0(struct cdns3_device *priv_dev,
> +                  struct cdns3_endpoint *priv_ep);
> +void cdns3_ep0_config(struct cdns3_device *priv_dev);
> +void cdns3_ep_config(struct cdns3_endpoint *priv_ep);
> +void cdns3_check_ep0_interrupt_proceed(struct cdns3_device *priv_dev, int dir);
> +
> +#endif /* __LINUX_CDNS3_GADGET */
> diff --git a/drivers/usb/cdns3/host-export.h b/drivers/usb/cdns3/host-export.h
> new file mode 100644
> index 000000000000..b498a170b7e8
> --- /dev/null
> +++ b/drivers/usb/cdns3/host-export.h
> @@ -0,0 +1,28 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +/*
> + * Cadence USBSS DRD Driver - Host Export APIs
> + *
> + * Copyright (C) 2017-2018 NXP
> + *
> + * Authors: Peter Chen <peter.chen@nxp.com>
> + */
> +#ifndef __LINUX_CDNS3_HOST_EXPORT
> +#define __LINUX_CDNS3_HOST_EXPORT
> +
> +#ifdef CONFIG_USB_CDNS3_HOST
> +
> +int cdns3_host_init(struct cdns3 *cdns);
> +void cdns3_host_exit(struct cdns3 *cdns);
> +
> +#else
> +
> +static inline int cdns3_host_init(struct cdns3 *cdns)
> +{
> +       return -ENXIO;
> +}
> +
> +static inline void cdns3_host_exit(struct cdns3 *cdns) { }
> +
> +#endif /* CONFIG_USB_CDNS3_HOST */
> +
> +#endif /* __LINUX_CDNS3_HOST_EXPORT */
> diff --git a/drivers/usb/cdns3/host.c b/drivers/usb/cdns3/host.c
> new file mode 100644
> index 000000000000..b43b0236a885
> --- /dev/null
> +++ b/drivers/usb/cdns3/host.c
> @@ -0,0 +1,72 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Cadence USBSS DRD Driver - host side
> + *
> + * Copyright (C) 2018 Cadence Design Systems.
> + * Copyright (C) 2017-2018 NXP
> + *
> + * Authors: Peter Chen <peter.chen@nxp.com>
> + *         Pawel Laszczak <pawell@cadence.com>
> + */
> +
> +#include <linux/platform_device.h>
> +#include "core.h"
> +
> +static int __cdns3_host_init(struct cdns3 *cdns)
> +{
> +       struct platform_device *xhci;
> +       int ret;
> +
> +       xhci = platform_device_alloc("xhci-hcd", PLATFORM_DEVID_AUTO);
> +       if (!xhci) {
> +               dev_err(cdns->dev, "couldn't allocate xHCI device\n");
> +               return -ENOMEM;
> +       }
> +
> +       xhci->dev.parent = cdns->dev;
> +       cdns->host_dev = xhci;
> +
> +       ret = platform_device_add_resources(xhci, cdns->xhci_res,
> +                                           CDNS3_XHCI_RESOURCES_NUM);
> +       if (ret) {
> +               dev_err(cdns->dev, "couldn't add resources to xHCI device\n");
> +               goto err1;
> +       }
> +
> +       ret = platform_device_add(xhci);
> +       if (ret) {
> +               dev_err(cdns->dev, "failed to register xHCI device\n");
> +               goto err1;
> +       }
> +
> +       return 0;
> +err1:
> +       platform_device_put(xhci);
> +       return ret;
> +}
> +
> +static void cdns3_host_exit(struct cdns3 *cdns)
> +{
> +       platform_device_unregister(cdns->host_dev);
> +       cdns->host_dev = NULL;
> +}
> +
> +int cdns3_host_init(struct cdns3 *cdns)
> +{
> +       struct cdns3_role_driver *rdrv;
> +
> +       rdrv = devm_kzalloc(cdns->dev, sizeof(*rdrv), GFP_KERNEL);
> +       if (!rdrv)
> +               return -ENOMEM;
> +
> +       rdrv->start     = __cdns3_host_init;
> +       rdrv->stop      = cdns3_host_exit;
> +       rdrv->state     = CDNS3_ROLE_STATE_INACTIVE;
> +       rdrv->suspend   = NULL;
> +       rdrv->resume    = NULL;
> +       rdrv->name      = "host";
> +
> +       cdns->roles[CDNS3_ROLE_HOST] = rdrv;
> +
> +       return 0;
> +}
> diff --git a/drivers/usb/cdns3/trace.c b/drivers/usb/cdns3/trace.c
> new file mode 100644
> index 000000000000..587ae08e019d
> --- /dev/null
> +++ b/drivers/usb/cdns3/trace.c
> @@ -0,0 +1,11 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * USBSS device controller driver Trace Support
> + *
> + * Copyright (C) 2018 Cadence.
> + *
> + * Author: Pawel Laszczak <pawell@cadence.com>
> + */
> +
> +#define CREATE_TRACE_POINTS
> +#include "trace.h"
> diff --git a/drivers/usb/cdns3/trace.h b/drivers/usb/cdns3/trace.h
> new file mode 100644
> index 000000000000..f3d7a91fae86
> --- /dev/null
> +++ b/drivers/usb/cdns3/trace.h
> @@ -0,0 +1,389 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +/*
> + * USBSS device controller driver.
> + * Trace support header file.
> + *
> + * Copyright (C) 2018 Cadence.
> + *
> + * Author: Pawel Laszczak <pawell@cadence.com>
> + */
> +
> +#undef TRACE_SYSTEM
> +#define TRACE_SYSTEM cdns3
> +
> +#if !defined(__LINUX_CDNS3_TRACE) || defined(TRACE_HEADER_MULTI_READ)
> +#define __LINUX_CDNS3_TRACE
> +
> +#include <linux/types.h>
> +#include <linux/tracepoint.h>
> +#include <asm/byteorder.h>
> +#include <linux/usb/ch9.h>
> +#include "core.h"
> +#include "gadget.h"
> +#include "debug.h"
> +
> +#define CDNS3_MSG_MAX  500
> +
> +DECLARE_EVENT_CLASS(cdns3_log_doorbell,
> +       TP_PROTO(const char *ep_name, u32 ep_trbaddr),
> +       TP_ARGS(ep_name, ep_trbaddr),
> +       TP_STRUCT__entry(
> +               __string(name, ep_name)
> +               __field(u32, ep_trbaddr)
> +       ),
> +       TP_fast_assign(
> +               __assign_str(name, ep_name);
> +               __entry->ep_trbaddr = ep_trbaddr;
> +       ),
> +       TP_printk("//Ding Dong %s, ep_trbaddr %08x", __get_str(name),
> +                 __entry->ep_trbaddr)
> +);
> +
> +DEFINE_EVENT(cdns3_log_doorbell, cdns3_doorbell_ep0,
> +       TP_PROTO(const char *ep_name, u32 ep_trbaddr),
> +       TP_ARGS(ep_name, ep_trbaddr)
> +);
> +
> +DEFINE_EVENT(cdns3_log_doorbell, cdns3_doorbell_epx,
> +       TP_PROTO(const char *ep_name, u32 ep_trbaddr),
> +       TP_ARGS(ep_name, ep_trbaddr)
> +);
> +
> +DECLARE_EVENT_CLASS(cdns3_log_usb_irq,
> +       TP_PROTO(struct cdns3_device *priv_dev, u32 usb_ists),
> +       TP_ARGS(priv_dev, usb_ists),
> +       TP_STRUCT__entry(
> +               __field(enum usb_device_speed, speed)
> +               __field(u32, usb_ists)
> +               __dynamic_array(char, str, CDNS3_MSG_MAX)
> +       ),
> +       TP_fast_assign(
> +               __entry->speed = cdns3_get_speed(priv_dev);
> +               __entry->usb_ists = usb_ists;
> +       ),
> +       TP_printk("%s", cdns3_decode_usb_irq(__get_str(str), __entry->speed,
> +                                            __entry->usb_ists))
> +);
> +
> +DEFINE_EVENT(cdns3_log_usb_irq, cdns3_usb_irq,
> +       TP_PROTO(struct cdns3_device *priv_dev, u32 usb_ists),
> +       TP_ARGS(priv_dev, usb_ists)
> +);
> +
> +DECLARE_EVENT_CLASS(cdns3_log_epx_irq,
> +       TP_PROTO(struct cdns3_device *priv_dev, struct cdns3_endpoint *priv_ep),
> +       TP_ARGS(priv_dev, priv_ep),
> +       TP_STRUCT__entry(
> +               __string(ep_name, priv_ep->name)
> +               __field(u32, ep_sts)
> +               __field(u32, ep_traddr)
> +               __dynamic_array(char, str, CDNS3_MSG_MAX)
> +       ),
> +       TP_fast_assign(
> +               __assign_str(ep_name, priv_ep->name);
> +               __entry->ep_sts = readl(&priv_dev->regs->ep_sts);
> +               __entry->ep_traddr = readl(&priv_dev->regs->ep_traddr);
> +       ),
> +       TP_printk("%s, ep_traddr: %08x",
> +                 cdns3_decode_epx_irq(__get_str(str),
> +                                      __get_str(ep_name),
> +                                      __entry->ep_sts),
> +                 __entry->ep_traddr)
> +);
> +
> +DEFINE_EVENT(cdns3_log_epx_irq, cdns3_epx_irq,
> +       TP_PROTO(struct cdns3_device *priv_dev, struct cdns3_endpoint *priv_ep),
> +       TP_ARGS(priv_dev, priv_ep)
> +);
> +
> +DECLARE_EVENT_CLASS(cdns3_log_ep0_irq,
> +       TP_PROTO(struct cdns3_device *priv_dev,  u32 ep_sts),
> +       TP_ARGS(priv_dev, ep_sts),
> +       TP_STRUCT__entry(
> +               __field(int, ep_dir)
> +               __field(u32, ep_sts)
> +               __dynamic_array(char, str, CDNS3_MSG_MAX)
> +       ),
> +       TP_fast_assign(
> +               __entry->ep_dir = priv_dev->ep0_data_dir;
> +               __entry->ep_sts = ep_sts;
> +       ),
> +       TP_printk("%s", cdns3_decode_ep0_irq(__get_str(str),
> +                                            __entry->ep_dir,
> +                                            __entry->ep_sts))
> +);
> +
> +DEFINE_EVENT(cdns3_log_ep0_irq, cdns3_ep0_irq,
> +       TP_PROTO(struct cdns3_device *priv_dev, u32 ep_sts),
> +       TP_ARGS(priv_dev, ep_sts)
> +);
> +
> +DECLARE_EVENT_CLASS(cdns3_log_ctrl,
> +       TP_PROTO(struct usb_ctrlrequest *ctrl),
> +       TP_ARGS(ctrl),
> +       TP_STRUCT__entry(
> +               __field(u8, bRequestType)
> +               __field(u8, bRequest)
> +               __field(u16, wValue)
> +               __field(u16, wIndex)
> +               __field(u16, wLength)
> +               __dynamic_array(char, str, CDNS3_MSG_MAX)
> +       ),
> +       TP_fast_assign(
> +               __entry->bRequestType = ctrl->bRequestType;
> +               __entry->bRequest = ctrl->bRequest;
> +               __entry->wValue = le16_to_cpu(ctrl->wValue);
> +               __entry->wIndex = le16_to_cpu(ctrl->wIndex);
> +               __entry->wLength = le16_to_cpu(ctrl->wLength);
> +       ),
> +       TP_printk("%s", usb_decode_ctrl(__get_str(str), __entry->bRequestType,
> +                                       __entry->bRequest, __entry->wValue,
> +                                       __entry->wIndex, __entry->wLength)
> +       )
> +);
> +
> +DEFINE_EVENT(cdns3_log_ctrl, cdns3_ctrl_req,
> +       TP_PROTO(struct usb_ctrlrequest *ctrl),
> +       TP_ARGS(ctrl)
> +);
> +
> +DECLARE_EVENT_CLASS(cdns3_log_request,
> +       TP_PROTO(struct cdns3_request *req),
> +       TP_ARGS(req),
> +       TP_STRUCT__entry(
> +               __string(name, req->priv_ep->name)
> +               __field(struct cdns3_request *, req)
> +               __field(unsigned int, actual)
> +               __field(unsigned int, length)
> +               __field(int, status)
> +               __field(int, zero)
> +               __field(int, short_not_ok)
> +               __field(int, no_interrupt)
> +               __field(int, start_trb)
> +               __field(int, end_trb)
> +               __field(struct cdns3_trb *, start_trb_addr)
> +               __field(int, flags)
> +       ),
> +       TP_fast_assign(
> +               __assign_str(name, req->priv_ep->name);
> +               __entry->req = req;
> +               __entry->actual = req->request.actual;
> +               __entry->length = req->request.length;
> +               __entry->status = req->request.status;
> +               __entry->zero = req->request.zero;
> +               __entry->short_not_ok = req->request.short_not_ok;
> +               __entry->no_interrupt = req->request.no_interrupt;
> +               __entry->start_trb = req->start_trb;
> +               __entry->end_trb = req->end_trb;
> +               __entry->start_trb_addr = req->trb;
> +               __entry->flags = req->flags;
> +       ),
> +       TP_printk("%s: req: %p, length: %u/%u %s%s%s, status: %d,"
> +                 " trb: [start:%d, end:%d: virt addr %pa], flags:%x ",
> +               __get_str(name), __entry->req, __entry->actual, __entry->length,
> +               __entry->zero ? "zero | " : "",
> +               __entry->short_not_ok ? "short | " : "",
> +               __entry->no_interrupt ? "no int" : "",
> +               __entry->status,
> +               __entry->start_trb,
> +               __entry->end_trb,
> +               __entry->start_trb_addr,
> +               __entry->flags
> +       )
> +);
> +
> +DEFINE_EVENT(cdns3_log_request, cdns3_alloc_request,
> +       TP_PROTO(struct cdns3_request *req),
> +       TP_ARGS(req)
> +);
> +
> +DEFINE_EVENT(cdns3_log_request, cdns3_free_request,
> +       TP_PROTO(struct cdns3_request *req),
> +       TP_ARGS(req)
> +);
> +
> +DEFINE_EVENT(cdns3_log_request, cdns3_ep_queue,
> +       TP_PROTO(struct cdns3_request *req),
> +       TP_ARGS(req)
> +);
> +
> +DEFINE_EVENT(cdns3_log_request, cdns3_ep_dequeue,
> +       TP_PROTO(struct cdns3_request *req),
> +       TP_ARGS(req)
> +);
> +
> +DEFINE_EVENT(cdns3_log_request, cdns3_gadget_giveback,
> +       TP_PROTO(struct cdns3_request *req),
> +       TP_ARGS(req)
> +);
> +
> +DECLARE_EVENT_CLASS(cdns3_log_trb,
> +       TP_PROTO(struct cdns3_endpoint *priv_ep, struct cdns3_trb *trb),
> +       TP_ARGS(priv_ep, trb),
> +       TP_STRUCT__entry(
> +               __string(name, priv_ep->name)
> +               __field(struct cdns3_trb *, trb)
> +               __field(u32, buffer)
> +               __field(u32, length)
> +               __field(u32, control)
> +               __field(u32, type)
> +       ),
> +       TP_fast_assign(
> +               __assign_str(name, priv_ep->name);
> +               __entry->trb = trb;
> +               __entry->buffer = trb->buffer;
> +               __entry->length = trb->length;
> +               __entry->control = trb->control;
> +               __entry->type = usb_endpoint_type(priv_ep->endpoint.desc);
> +       ),
> +       TP_printk("%s: trb 0x%pa, dma buf: 0x%08x, size: %ld, ctrl: 0x%08x (%s%s%s%s%s%s%s)",
> +               __get_str(name), __entry->trb, __entry->buffer,
> +               TRB_LEN(__entry->length), __entry->control,
> +               __entry->control & TRB_CYCLE ? "C=1, " : "C=0, ",
> +               __entry->control & TRB_TOGGLE ? "T=1, " : "T=0, ",
> +               __entry->control & TRB_ISP ? "ISP, " : "",
> +               __entry->control & TRB_FIFO_MODE ? "FIFO, " : "",
> +               __entry->control & TRB_CHAIN ? "CHAIN, " : "",
> +               __entry->control & TRB_IOC ? "IOC, " : "",
> +               TRB_FIELD_TO_TYPE(__entry->control) == TRB_NORMAL ? "Normal" : "LINK"
> +       )
> +);
> +
> +DEFINE_EVENT(cdns3_log_trb, cdns3_prepare_trb,
> +       TP_PROTO(struct cdns3_endpoint *priv_ep, struct cdns3_trb *trb),
> +       TP_ARGS(priv_ep, trb)
> +);
> +
> +DEFINE_EVENT(cdns3_log_trb, cdns3_complete_trb,
> +       TP_PROTO(struct cdns3_endpoint *priv_ep, struct cdns3_trb *trb),
> +       TP_ARGS(priv_ep, trb)
> +);
> +
> +DECLARE_EVENT_CLASS(cdns3_log_ring,
> +       TP_PROTO(struct cdns3_endpoint *priv_ep),
> +       TP_ARGS(priv_ep),
> +       TP_STRUCT__entry(
> +               __dynamic_array(u8, ring, TRB_RING_SIZE)
> +               __dynamic_array(u8, priv_ep, sizeof(struct cdns3_endpoint))
> +               __dynamic_array(char, buffer,
> +                               (TRBS_PER_SEGMENT * 65) + CDNS3_MSG_MAX)
> +       ),
> +       TP_fast_assign(
> +               memcpy(__get_dynamic_array(priv_ep), priv_ep,
> +                      sizeof(struct cdns3_endpoint));
> +               memcpy(__get_dynamic_array(ring), priv_ep->trb_pool,
> +                      TRB_RING_SIZE);
> +       ),
> +
> +       TP_printk("%s",
> +                 cdns3_dbg_ring((struct cdns3_endpoint *)__get_str(priv_ep),
> +                                (struct cdns3_trb *)__get_str(ring),
> +                                __get_str(buffer)))
> +);
> +
> +DEFINE_EVENT(cdns3_log_ring, cdns3_ring,
> +       TP_PROTO(struct cdns3_endpoint *priv_ep),
> +       TP_ARGS(priv_ep)
> +);
> +
> +DECLARE_EVENT_CLASS(cdns3_log_ep,
> +       TP_PROTO(struct cdns3_endpoint *priv_ep),
> +       TP_ARGS(priv_ep),
> +       TP_STRUCT__entry(
> +               __string(name, priv_ep->name)
> +               __field(unsigned int, maxpacket)
> +               __field(unsigned int, maxpacket_limit)
> +               __field(unsigned int, max_streams)
> +               __field(unsigned int, maxburst)
> +               __field(unsigned int, flags)
> +               __field(unsigned int, dir)
> +               __field(u8, enqueue)
> +               __field(u8, dequeue)
> +       ),
> +       TP_fast_assign(
> +               __assign_str(name, priv_ep->name);
> +               __entry->maxpacket = priv_ep->endpoint.maxpacket;
> +               __entry->maxpacket_limit = priv_ep->endpoint.maxpacket_limit;
> +               __entry->max_streams = priv_ep->endpoint.max_streams;
> +               __entry->maxburst = priv_ep->endpoint.maxburst;
> +               __entry->flags = priv_ep->flags;
> +               __entry->dir = priv_ep->dir;
> +               __entry->enqueue = priv_ep->enqueue;
> +               __entry->dequeue = priv_ep->dequeue;
> +       ),
> +       TP_printk("%s: mps: %d/%d. streams: %d, burst: %d, enq idx: %d, "
> +                 "deq idx: %d, flags %s%s%s%s%s%s%s%s, dir: %s",
> +               __get_str(name), __entry->maxpacket,
> +               __entry->maxpacket_limit, __entry->max_streams,
> +               __entry->maxburst, __entry->enqueue,
> +               __entry->dequeue,
> +               __entry->flags & EP_ENABLED ? "EN | " : "",
> +               __entry->flags & EP_STALL ? "STALL | " : "",
> +               __entry->flags & EP_WEDGE ? "WEDGE | " : "",
> +               __entry->flags & EP_TRANSFER_STARTED ? "STARTED | " : "",
> +               __entry->flags & EP_UPDATE_EP_TRBADDR ? "UPD TRB | " : "",
> +               __entry->flags & EP_PENDING_REQUEST ? "REQ PEN | " : "",
> +               __entry->flags & EP_RING_FULL ? "RING FULL |" : "",
> +               __entry->flags & EP_CLAIMED ?  "CLAIMED " : "",
> +               __entry->dir ? "IN" : "OUT"
> +       )
> +);
> +
> +DEFINE_EVENT(cdns3_log_ep, cdns3_gadget_ep_enable,
> +       TP_PROTO(struct cdns3_endpoint *priv_ep),
> +       TP_ARGS(priv_ep)
> +);
> +
> +DEFINE_EVENT(cdns3_log_ep, cdns3_gadget_ep_disable,
> +       TP_PROTO(struct cdns3_endpoint *priv_ep),
> +       TP_ARGS(priv_ep)
> +);
> +
> +DECLARE_EVENT_CLASS(cdns3_log_request_handled,
> +       TP_PROTO(struct cdns3_request *priv_req, int current_index,
> +                int handled),
> +       TP_ARGS(priv_req, current_index, handled),
> +       TP_STRUCT__entry(
> +               __field(struct cdns3_request *, priv_req)
> +               __field(unsigned int, dma_position)
> +               __field(unsigned int, handled)
> +               __field(unsigned int, dequeue_idx)
> +               __field(unsigned int, enqueue_idx)
> +               __field(unsigned int, start_trb)
> +               __field(unsigned int, end_trb)
> +       ),
> +       TP_fast_assign(
> +               __entry->priv_req = priv_req;
> +               __entry->dma_position = current_index;
> +               __entry->handled = handled;
> +               __entry->dequeue_idx = priv_req->priv_ep->dequeue;
> +               __entry->enqueue_idx = priv_req->priv_ep->enqueue;
> +               __entry->start_trb = priv_req->start_trb;
> +               __entry->end_trb = priv_req->end_trb;
> +       ),
> +       TP_printk("Req: %p %s, DMA pos: %d, ep deq: %d, ep enq: %d,"
> +                 " start trb: %d, end trb: %d",
> +               __entry->priv_req,
> +               __entry->handled ? "handled" : "not handled",
> +               __entry->dma_position, __entry->dequeue_idx,
> +               __entry->enqueue_idx, __entry->start_trb,
> +               __entry->end_trb
> +       )
> +);
> +
> +DEFINE_EVENT(cdns3_log_request_handled, cdns3_request_handled,
> +       TP_PROTO(struct cdns3_request *priv_req, int current_index,
> +                int handled),
> +       TP_ARGS(priv_req, current_index, handled)
> +);
> +#endif /* __LINUX_CDNS3_TRACE */
> +
> +/* this part must be outside header guard */
> +
> +#undef TRACE_INCLUDE_PATH
> +#define TRACE_INCLUDE_PATH .
> +
> +#undef TRACE_INCLUDE_FILE
> +#define TRACE_INCLUDE_FILE trace
> +
> +#include <trace/define_trace.h>
> --
> 2.17.1
>

diff --git a/drivers/usb/cdns3/Makefile b/drivers/usb/cdns3/Makefile
index 3f63baa24294..d1bca2829f57 100644
--- a/drivers/usb/cdns3/Makefile
+++ b/drivers/usb/cdns3/Makefile
@@ -2,15 +2,13 @@
 # define_trace.h needs to know how to find our header
 CFLAGS_trace.o                         := -I$(src)

-obj-$(CONFIG_USB_CDNS3)                        += cdns3.o
-obj-$(CONFIG_USB_CDNS3_PCI_WRAP)       += cdns3-pci.o
-
 cdns3-y                                        := core.o drd.o trace.o
-
+obj-$(CONFIG_USB_CDNS3)                        += cdns3.o
 ifneq ($(CONFIG_DEBUG_FS),)
        cdns3-y                         += debugfs.o
 endif

 cdns3-$(CONFIG_USB_CDNS3_GADGET)       += gadget.o ep0.o
 cdns3-$(CONFIG_USB_CDNS3_HOST)         += host.o
-cdns3-pci-y                            := cdns3-pci-wrap.o
+obj-$(CONFIG_USB_CDNS3_PCI_WRAP)       += cdns3-pci-wrap.o
+obj-$(CONFIG_USB_CDNS3_IMX_WRAP)       += cdns3-imx.o

^ permalink raw reply related	[flat|nested] 49+ messages in thread

* Re: [PATCH v2 5/5] usb:cdns3 Add Cadence USB3 DRD Driver
@ 2018-12-25  9:38       ` Peter Chen
  0 siblings, 0 replies; 49+ messages in thread
From: Peter Chen @ 2018-12-25  9:38 UTC (permalink / raw)
  To: pawell
  Cc: devicetree, Greg Kroah-Hartman, mark.rutland, linux-usb,
	hdegoede, Heikki Krogerus, andy.shevchenko, robh+dt, rogerq,
	lkml, adouglas, jbergsagel, nsekhar, nm, sureshp, peter.chen,
	pjez, kurahul

> > +
> > +       if (ret)
> > +               return ret;
> > +
> > +       state = readl(&cdns->otg_regs->sts);
> > +       if (OTGSTS_OTG_NRDY(state) != 0) {
> > +               dev_err(cdns->dev, "Cadence USB3 OTG device not ready\n");
> > +               return -ENODEV;
> > +       }
> > +
> > +       ret = cdns3_drd_update_mode(cdns);
> > +
>
> Calling this function, it is timeout for waiting OTGSTS_XHCI_READY at otgsts,
> do you know possible reasons?  After commenting out this function, my
> xHCI function
> works.
>

Pawel, since OTG compliance (Known as HNP/SRP)  has not been used
widely, Linux kernel does not
maintain it from some time ago (maybe 1-2 years). In software design,
we do not need to consider it from
hardware point, eg, kinds of OTG timer. For dual-role switch on the
fly,  through /sys is enough.

Through the debug, we find it needs to wait 1s after setting de-select
the host or gadget before request
XHCI at otg_regs->cmd, and enable fast simulate can reduce delay to
2-3ms. Would you please help
to check with your hardware team this behavior. With below changes, I
can get OTGSTS_XHCI_READY at otgsts.

@@ -141,6 +143,7 @@ static int cdns3_drd_switch_host(struct cdns3 *cdns, int on)
                writel(OTGCMD_HOST_BUS_DROP | OTGCMD_DEV_BUS_DROP |
                       OTGCMD_DEV_POWER_OFF | OTGCMD_HOST_POWER_OFF,
                       &cdns->otg_regs->cmd);
+               usleep_range(3000, 4000);
        }

        return 0;
@@ -178,6 +181,7 @@ static int cdns3_drd_switch_gadget(struct cdns3
*cdns, int on)
                writel(OTGCMD_HOST_BUS_DROP | OTGCMD_DEV_BUS_DROP |
                       OTGCMD_DEV_POWER_OFF | OTGCMD_HOST_POWER_OFF,
                       &cdns->otg_regs->cmd);
+               usleep_range(3000, 4000);
        }

@@ -299,6 +306,7 @@ int cdns3_drd_init(struct cdns3 *cdns)
                cdns->version  = CDNS3_CONTROLLER_V0;
                cdns->otg_v1_regs = NULL;
                cdns->otg_regs = regs;
+               writel(0x1, &cdns->otg_v0_regs->simulate);
                dev_info(cdns->dev, "DRD version v0 (%08x)\n",
                         readl(&cdns->otg_v0_regs->version));
        } else {

Peter

> Peter
>
> > +       return ret;
> > +}
> > +
> > +int cdns3_drd_exit(struct cdns3 *cdns)
> > +{
> > +       return cdns3_drd_switch_host(cdns, 0);
> > +}
> > diff --git a/drivers/usb/cdns3/drd.h b/drivers/usb/cdns3/drd.h
> > new file mode 100644
> > index 000000000000..6a29cdcb492d
> > --- /dev/null
> > +++ b/drivers/usb/cdns3/drd.h
> > @@ -0,0 +1,162 @@
> > +/* SPDX-License-Identifier: GPL-2.0 */
> > +/*
> > + * Cadence USB3 DRD header file.
> > + *
> > + * Copyright (C) 2018 Cadence.
> > + *
> > + * Author: Pawel Laszczak <pawell@cadence.com>
> > + */
> > +#ifndef __LINUX_CDNS3_DRD
> > +#define __LINUX_CDNS3_DRD
> > +
> > +#include <linux/usb/otg.h>
> > +#include <linux/phy/phy.h>
> > +#include "core.h"
> > +
> > +/*  DRD register interface for version v1. */
> > +struct cdns3_otg_regs {
> > +       __le32 did;
> > +       __le32 rid;
> > +       __le32 capabilities;
> > +       __le32 reserved1;
> > +       __le32 cmd;
> > +       __le32 sts;
> > +       __le32 state;
> > +       __le32 reserved2;
> > +       __le32 ien;
> > +       __le32 ivect;
> > +       __le32 refclk;
> > +       __le32 tmr;
> > +       __le32 reserved3[4];
> > +       __le32 simulate;
> > +       __le32 override;
> > +       __le32 susp_ctrl;
> > +       __le32 reserved4;
> > +       __le32 anasts;
> > +       __le32 adp_ramp_time;
> > +       __le32 ctrl1;
> > +       __le32 ctrl2;
> > +};
> > +
> > +/*  DRD register interface for version v0. */
> > +struct cdns3_otg_legacy_regs {
> > +       __le32 cmd;
> > +       __le32 sts;
> > +       __le32 state;
> > +       __le32 refclk;
> > +       __le32 ien;
> > +       __le32 ivect;
> > +       __le32 reserved1[3];
> > +       __le32 tmr;
> > +       __le32 reserved2[2];
> > +       __le32 version;
> > +       __le32 capabilities;
> > +       __le32 reserved3[2];
> > +       __le32 simulate;
> > +       __le32 reserved4[5];
> > +       __le32 ctrl1;
> > +};
> > +
> > +/*
> > + * Common registers interface for both version of DRD.
> > + */
> > +struct cdns3_otg_common_regs {
> > +       __le32 cmd;
> > +       __le32 sts;
> > +       __le32 state;
> > +       __le32 different1;
> > +       __le32 ien;
> > +       __le32 ivect;
> > +};
> > +
> > +/* CDNS_RID - bitmasks */
> > +#define CDNS_RID(p)                    ((p) & GENMASK(15, 0))
> > +
> > +/* CDNS_VID - bitmasks */
> > +#define CDNS_DID(p)                    ((p) & GENMASK(31, 0))
> > +
> > +/* OTGCMD - bitmasks */
> > +/* "Request the bus for Device mode. */
> > +#define OTGCMD_DEV_BUS_REQ             BIT(0)
> > +/* Request the bus for Host mode */
> > +#define OTGCMD_HOST_BUS_REQ            BIT(1)
> > +/* Enable OTG mode. */
> > +#define OTGCMD_OTG_EN                  BIT(2)
> > +/* Disable OTG mode */
> > +#define OTGCMD_OTG_DIS                 BIT(3)
> > +/*"Configure OTG as A-Device. */
> > +#define OTGCMD_A_DEV_EN                        BIT(4)
> > +/*"Configure OTG as A-Device. */
> > +#define OTGCMD_A_DEV_DIS               BIT(5)
> > +/* Drop the bus for Device mod e. */
> > +#define OTGCMD_DEV_BUS_DROP            BIT(8)
> > +/* Drop the bus for Host mode*/
> > +#define OTGCMD_HOST_BUS_DROP           BIT(9)
> > +/* Power Down USBSS-DEV. */
> > +#define OTGCMD_DEV_POWER_OFF           BIT(11)
> > +/* Power Down CDNSXHCI. */
> > +#define OTGCMD_HOST_POWER_OFF          BIT(12)
> > +
> > +/* OTGIEN - bitmasks */
> > +/* ID change interrupt enable */
> > +#define OTGIEN_ID_CHANGE_INT           BIT(0)
> > +/* Vbusvalid fall detected interrupt enable.*/
> > +#define OTGIEN_VBUSVALID_RISE_INT      BIT(4)
> > +/* Vbusvalid fall detected interrupt enable */
> > +#define OTGIEN_VBUSVALID_FALL_INT      BIT(5)
> > +
> > +/* OTGSTS - bitmasks */
> > +/*
> > + * Current value of the ID pin. It is only valid when idpullup in
> > + *  OTGCTRL1_TYPE register is set to '1'.
> > + */
> > +#define OTGSTS_ID_VALUE                        BIT(0)
> > +/* Current value of the vbus_valid */
> > +#define OTGSTS_VBUS_VALID              BIT(1)
> > +/* Current value of the b_sess_vld */
> > +#define OTGSTS_SESSION_VALID           BIT(2)
> > +/*Device mode is active*/
> > +#define OTGSTS_DEV_ACTIVE              BIT(3)
> > +/* Host mode is active. */
> > +#define OTGSTS_HOST_ACTIVE             BIT(4)
> > +/* OTG Controller not ready. */
> > +#define OTGSTS_OTG_NRDY_MASK           BIT(11)
> > +#define OTGSTS_OTG_NRDY(p)             ((p) & OTGSTS_OTG_NRDY_MASK)
> > +/*
> > + * Value of the strap pins.
> > + * 000 - no default configuration
> > + * 010 - Controller initiall configured as Host
> > + * 100 - Controller initially configured as Device
> > + */
> > +#define OTGSTS_STRAP(p)                        (((p) & GENMASK(14, 12)) >> 12)
> > +#define OTGSTS_STRAP_NO_DEFAULT_CFG    0x00
> > +#define OTGSTS_STRAP_HOST_OTG          0x01
> > +#define OTGSTS_STRAP_HOST              0x02
> > +#define OTGSTS_STRAP_GADGET            0x04
> > +/* Host mode is turned on. */
> > +#define OTGSTS_XHCI_READY              BIT(26)
> > +/* "Device mode is turned on .*/
> > +#define OTGSTS_DEV_READY               BIT(27)
> > +
> > +/* OTGSTATE- bitmasks */
> > +#define OTGSTATE_HOST_STATE_MASK       GENMASK(5, 3)
> > +#define OTGSTATE_HOST_STATE_IDLE       0x0
> > +#define OTGSTATE_HOST_STATE_VBUS_FALL   0x7
> > +#define OTGSTATE_HOST_STATE(p)         (((p) & OTGSTATE_HOST_STATE_MASK) >> 3)
> > +
> > +/* OTGREFCLK - bitmasks */
> > +#define OTGREFCLK_STB_CLK_SWITCH_EN    BIT(31)
> > +
> > +/* OVERRIDE - bitmasks */
> > +#define OVERRIDE_IDPULLUP              BIT(0)
> > +/* Only for CDNS3_CONTROLLER_V0 version */
> > +#define OVERRIDE_IDPULLUP_V0           BIT(24)
> > +
> > +int cdns3_is_host(struct cdns3 *cdns);
> > +int cdns3_is_device(struct cdns3 *cdns);
> > +int cdns3_get_id(struct cdns3 *cdns);
> > +int cdns3_drd_init(struct cdns3 *cdns);
> > +int cdns3_drd_exit(struct cdns3 *cdns);
> > +int cdns3_drd_update_mode(struct cdns3 *cdns);
> > +
> > +#endif /* __LINUX_CDNS3_DRD */
> > diff --git a/drivers/usb/cdns3/ep0.c b/drivers/usb/cdns3/ep0.c
> > new file mode 100644
> > index 000000000000..89cf1cde1555
> > --- /dev/null
> > +++ b/drivers/usb/cdns3/ep0.c
> > @@ -0,0 +1,896 @@
> > +// SPDX-License-Identifier: GPL-2.0
> > +/*
> > + * Cadence USBSS DRD Driver - gadget side.
> > + *
> > + * Copyright (C) 2018 Cadence Design Systems.
> > + * Copyright (C) 2017-2018 NXP
> > + *
> > + * Authors: Pawel Jez <pjez@cadence.com>,
> > + *          Pawel Laszczak <pawell@cadence.com>
> > + *         Peter Chen <peter.chen@nxp.com>
> > + */
> > +
> > +#include <linux/usb/composite.h>
> > +
> > +#include "gadget.h"
> > +#include "trace.h"
> > +
> > +static struct usb_endpoint_descriptor cdns3_gadget_ep0_desc = {
> > +       .bLength = USB_DT_ENDPOINT_SIZE,
> > +       .bDescriptorType = USB_DT_ENDPOINT,
> > +       .bmAttributes = USB_ENDPOINT_XFER_CONTROL,
> > +};
> > +
> > +/**
> > + * cdns3_ep0_run_transfer - Do transfer on default endpoint hardware
> > + * @priv_dev: extended gadget object
> > + * @dma_addr: physical address where data is/will be stored
> > + * @length: data length
> > + * @erdy: set it to 1 when ERDY packet should be sent -
> > + *        exit from flow control state
> > + */
> > +static void cdns3_ep0_run_transfer(struct cdns3_device *priv_dev,
> > +                                  dma_addr_t dma_addr,
> > +                                  unsigned int length, int erdy)
> > +{
> > +       struct cdns3_usb_regs __iomem *regs = priv_dev->regs;
> > +       struct cdns3_endpoint *priv_ep = priv_dev->eps[0];
> > +
> > +       priv_ep->trb_pool->buffer = TRB_BUFFER(dma_addr);
> > +       priv_ep->trb_pool->length = TRB_LEN(length);
> > +       priv_ep->trb_pool->control = TRB_CYCLE | TRB_IOC | TRB_TYPE(TRB_NORMAL);
> > +
> > +       trace_cdns3_prepare_trb(priv_ep, priv_ep->trb_pool);
> > +
> > +       cdns3_select_ep(priv_dev, priv_dev->ep0_data_dir);
> > +
> > +       writel(EP_STS_TRBERR, &regs->ep_sts);
> > +       writel(EP_TRADDR_TRADDR(priv_ep->trb_pool_dma), &regs->ep_traddr);
> > +       trace_cdns3_doorbell_ep0(priv_dev->ep0_data_dir ? "ep0in" : "ep0out",
> > +                                readl(&regs->ep_traddr));
> > +
> > +       /* TRB should be prepared before starting transfer */
> > +       writel(EP_CMD_DRDY, &regs->ep_cmd);
> > +
> > +       if (erdy)
> > +               writel(EP_CMD_ERDY, &priv_dev->regs->ep_cmd);
> > +}
> > +
> > +/**
> > + * cdns3_ep0_delegate_req - Returns status of handling setup packet
> > + * Setup is handled by gadget driver
> > + * @priv_dev: extended gadget object
> > + * @ctrl_req: pointer to received setup packet
> > + *
> > + * Returns zero on success or negative value on failure
> > + */
> > +static int cdns3_ep0_delegate_req(struct cdns3_device *priv_dev,
> > +                                 struct usb_ctrlrequest *ctrl_req)
> > +{
> > +       int ret;
> > +
> > +       spin_unlock(&priv_dev->lock);
> > +       priv_dev->setup_pending = 1;
> > +       ret = priv_dev->gadget_driver->setup(&priv_dev->gadget, ctrl_req);
> > +       priv_dev->setup_pending = 0;
> > +       spin_lock(&priv_dev->lock);
> > +       return ret;
> > +}
> > +
> > +static void cdns3_prepare_setup_packet(struct cdns3_device *priv_dev)
> > +{
> > +       priv_dev->ep0_data_dir = 0;
> > +       priv_dev->ep0_stage = CDNS3_SETUP_STAGE;
> > +       cdns3_ep0_run_transfer(priv_dev, priv_dev->setup_dma,
> > +                              sizeof(struct usb_ctrlrequest), 0);
> > +}
> > +
> > +static void cdns3_ep0_complete_setup(struct cdns3_device *priv_dev,
> > +                                    u8 send_stall, u8 send_erdy)
> > +{
> > +       struct cdns3_endpoint *priv_ep = priv_dev->eps[0];
> > +       struct usb_request *request;
> > +
> > +       request = cdns3_next_request(&priv_ep->pending_req_list);
> > +       if (request)
> > +               list_del_init(&request->list);
> > +
> > +       if (send_stall) {
> > +               dev_dbg(priv_dev->dev, "STALL for ep0\n");
> > +               /* set_stall on ep0 */
> > +               cdns3_select_ep(priv_dev, 0x00);
> > +               writel(EP_CMD_SSTALL, &priv_dev->regs->ep_cmd);
> > +       } else {
> > +               cdns3_prepare_setup_packet(priv_dev);
> > +       }
> > +
> > +       priv_dev->ep0_stage = CDNS3_SETUP_STAGE;
> > +       writel((send_erdy ? EP_CMD_ERDY : 0) | EP_CMD_REQ_CMPL,
> > +              &priv_dev->regs->ep_cmd);
> > +}
> > +
> > +/**
> > + * cdns3_req_ep0_set_configuration - Handling of SET_CONFIG standard USB request
> > + * @priv_dev: extended gadget object
> > + * @ctrl_req: pointer to received setup packet
> > + *
> > + * Returns 0 if success, USB_GADGET_DELAYED_STATUS on deferred status stage,
> > + * error code on error
> > + */
> > +static int cdns3_req_ep0_set_configuration(struct cdns3_device *priv_dev,
> > +                                          struct usb_ctrlrequest *ctrl_req)
> > +{
> > +       enum usb_device_state device_state = priv_dev->gadget.state;
> > +       struct cdns3_endpoint *priv_ep;
> > +       u32 config = le16_to_cpu(ctrl_req->wValue);
> > +       int result = 0;
> > +       int i;
> > +
> > +       switch (device_state) {
> > +       case USB_STATE_ADDRESS:
> > +               /* Configure non-control EPs */
> > +               for (i = 0; i < CDNS3_ENDPOINTS_MAX_COUNT; i++) {
> > +                       priv_ep = priv_dev->eps[i];
> > +                       if (!priv_ep)
> > +                               continue;
> > +
> > +                       if (priv_ep->flags & EP_CLAIMED)
> > +                               cdns3_ep_config(priv_ep);
> > +               }
> > +
> > +               result = cdns3_ep0_delegate_req(priv_dev, ctrl_req);
> > +
> > +               if (result)
> > +                       return result;
> > +
> > +               if (config) {
> > +                       cdns3_set_hw_configuration(priv_dev);
> > +               } else {
> > +                       cdns3_hw_reset_eps_config(priv_dev);
> > +                       usb_gadget_set_state(&priv_dev->gadget,
> > +                                            USB_STATE_ADDRESS);
> > +               }
> > +               break;
> > +       case USB_STATE_CONFIGURED:
> > +               result = cdns3_ep0_delegate_req(priv_dev, ctrl_req);
> > +
> > +               if (!config && !result) {
> > +                       cdns3_hw_reset_eps_config(priv_dev);
> > +                       usb_gadget_set_state(&priv_dev->gadget,
> > +                                            USB_STATE_ADDRESS);
> > +               }
> > +               break;
> > +       default:
> > +               result = -EINVAL;
> > +       }
> > +
> > +       return result;
> > +}
> > +
> > +/**
> > + * cdns3_req_ep0_set_address - Handling of SET_ADDRESS standard USB request
> > + * @priv_dev: extended gadget object
> > + * @ctrl_req: pointer to received setup packet
> > + *
> > + * Returns 0 if success, error code on error
> > + */
> > +static int cdns3_req_ep0_set_address(struct cdns3_device *priv_dev,
> > +                                    struct usb_ctrlrequest *ctrl_req)
> > +{
> > +       enum usb_device_state device_state = priv_dev->gadget.state;
> > +       u32 reg;
> > +       u32 addr;
> > +
> > +       addr = le16_to_cpu(ctrl_req->wValue);
> > +
> > +       if (addr > USB_DEVICE_MAX_ADDRESS) {
> > +               dev_err(priv_dev->dev,
> > +                       "Device address (%d) cannot be greater than %d\n",
> > +                       addr, USB_DEVICE_MAX_ADDRESS);
> > +               return -EINVAL;
> > +       }
> > +
> > +       if (device_state == USB_STATE_CONFIGURED) {
> > +               dev_err(priv_dev->dev,
> > +                       "can't set_address from configured state\n");
> > +               return -EINVAL;
> > +       }
> > +
> > +       reg = readl(&priv_dev->regs->usb_cmd);
> > +
> > +       writel(reg | USB_CMD_FADDR(addr) | USB_CMD_SET_ADDR,
> > +              &priv_dev->regs->usb_cmd);
> > +
> > +       usb_gadget_set_state(&priv_dev->gadget,
> > +                            (addr ? USB_STATE_ADDRESS : USB_STATE_DEFAULT));
> > +
> > +       return 0;
> > +}
> > +
> > +/**
> > + * cdns3_req_ep0_get_status - Handling of GET_STATUS standard USB request
> > + * @priv_dev: extended gadget object
> > + * @ctrl_req: pointer to received setup packet
> > + *
> > + * Returns 0 if success, error code on error
> > + */
> > +static int cdns3_req_ep0_get_status(struct cdns3_device *priv_dev,
> > +                                   struct usb_ctrlrequest *ctrl)
> > +{
> > +       __le16 *response_pkt;
> > +       u16 usb_status = 0;
> > +       u32 recip;
> > +       u32 reg;
> > +
> > +       recip = ctrl->bRequestType & USB_RECIP_MASK;
> > +
> > +       switch (recip) {
> > +       case USB_RECIP_DEVICE:
> > +               /* self powered */
> > +               if (priv_dev->is_selfpowered)
> > +                       usb_status = BIT(USB_DEVICE_SELF_POWERED);
> > +
> > +               if (priv_dev->wake_up_flag)
> > +                       usb_status |= BIT(USB_DEVICE_REMOTE_WAKEUP);
> > +
> > +               if (priv_dev->gadget.speed != USB_SPEED_SUPER)
> > +                       break;
> > +
> > +               reg = readl(&priv_dev->regs->usb_sts);
> > +
> > +               if (priv_dev->u1_allowed)
> > +                       usb_status |= BIT(USB_DEV_STAT_U1_ENABLED);
> > +
> > +               if (priv_dev->u2_allowed)
> > +                       usb_status |= BIT(USB_DEV_STAT_U2_ENABLED);
> > +
> > +               break;
> > +       case USB_RECIP_INTERFACE:
> > +               return cdns3_ep0_delegate_req(priv_dev, ctrl);
> > +       case USB_RECIP_ENDPOINT:
> > +               /* check if endpoint is stalled */
> > +               cdns3_select_ep(priv_dev, ctrl->wIndex);
> > +               if (EP_STS_STALL(readl(&priv_dev->regs->ep_sts)))
> > +                       usb_status =  BIT(USB_ENDPOINT_HALT);
> > +               break;
> > +       default:
> > +               return -EINVAL;
> > +       }
> > +
> > +       response_pkt = (__le16 *)priv_dev->setup_buf;
> > +       *response_pkt = cpu_to_le16(usb_status);
> > +
> > +       cdns3_ep0_run_transfer(priv_dev, priv_dev->setup_dma,
> > +                              sizeof(*response_pkt), 1);
> > +       return 0;
> > +}
> > +
> > +static int cdns3_ep0_feature_handle_device(struct cdns3_device *priv_dev,
> > +                                          struct usb_ctrlrequest *ctrl,
> > +                                          int set)
> > +{
> > +       enum usb_device_state state;
> > +       enum usb_device_speed speed;
> > +       int ret = 0;
> > +       u32 wValue;
> > +       u32 wIndex;
> > +       u16 tmode;
> > +
> > +       wValue = le16_to_cpu(ctrl->wValue);
> > +       wIndex = le16_to_cpu(ctrl->wIndex);
> > +       state = priv_dev->gadget.state;
> > +       speed = priv_dev->gadget.speed;
> > +
> > +       switch (ctrl->wValue) {
> > +       case USB_DEVICE_REMOTE_WAKEUP:
> > +               priv_dev->wake_up_flag = !!set;
> > +               break;
> > +       case USB_DEVICE_U1_ENABLE:
> > +               if (state != USB_STATE_CONFIGURED || speed != USB_SPEED_SUPER)
> > +                       return -EINVAL;
> > +
> > +               priv_dev->u1_allowed = !!set;
> > +               break;
> > +       case USB_DEVICE_U2_ENABLE:
> > +               if (state != USB_STATE_CONFIGURED || speed != USB_SPEED_SUPER)
> > +                       return -EINVAL;
> > +
> > +               priv_dev->u2_allowed = !!set;
> > +               break;
> > +       case USB_DEVICE_LTM_ENABLE:
> > +               ret = -EINVAL;
> > +               break;
> > +       case USB_DEVICE_TEST_MODE:
> > +               if (state != USB_STATE_CONFIGURED || speed > USB_SPEED_HIGH)
> > +                       return -EINVAL;
> > +
> > +               tmode = le16_to_cpu(ctrl->wIndex);
> > +
> > +               if (!set || (tmode & 0xff) != 0)
> > +                       return -EINVAL;
> > +
> > +               switch (tmode >> 8) {
> > +               case TEST_J:
> > +               case TEST_K:
> > +               case TEST_SE0_NAK:
> > +               case TEST_PACKET:
> > +                       cdns3_ep0_complete_setup(priv_dev, 0, 1);
> > +                       /**
> > +                        *  Little delay to give the controller some time
> > +                        * for sending status stage.
> > +                        * This time should be less then 3ms.
> > +                        */
> > +                       usleep_range(1000, 2000);
> > +                       cdns3_set_register_bit(&priv_dev->regs->usb_cmd,
> > +                                              USB_CMD_STMODE |
> > +                                              USB_STS_TMODE_SEL(tmode - 1));
> > +                       break;
> > +               default:
> > +                       ret = -EINVAL;
> > +               }
> > +               break;
> > +       default:
> > +               ret = -EINVAL;
> > +       }
> > +
> > +       return ret;
> > +}
> > +
> > +static int cdns3_ep0_feature_handle_intf(struct cdns3_device *priv_dev,
> > +                                        struct usb_ctrlrequest *ctrl,
> > +                                        int set)
> > +{
> > +       u32 wValue;
> > +       int ret = 0;
> > +
> > +       wValue = le16_to_cpu(ctrl->wValue);
> > +
> > +       switch (wValue) {
> > +       case USB_INTRF_FUNC_SUSPEND:
> > +               break;
> > +       default:
> > +               ret = -EINVAL;
> > +       }
> > +
> > +       return ret;
> > +}
> > +
> > +static int cdns3_ep0_feature_handle_endpoint(struct cdns3_device *priv_dev,
> > +                                            struct usb_ctrlrequest *ctrl,
> > +                                            int set)
> > +{
> > +       struct cdns3_endpoint *priv_ep;
> > +       int ret = 0;
> > +       u8 index;
> > +
> > +       if (le16_to_cpu(ctrl->wValue) != USB_ENDPOINT_HALT)
> > +               return -EINVAL;
> > +
> > +       if (!(ctrl->wIndex & ~USB_DIR_IN))
> > +               return 0;
> > +
> > +       index = cdns3_ep_addr_to_index(ctrl->wIndex);
> > +       priv_ep = priv_dev->eps[index];
> > +
> > +       cdns3_select_ep(priv_dev, ctrl->wIndex);
> > +
> > +       if (set) {
> > +               writel(EP_CMD_SSTALL, &priv_dev->regs->ep_cmd);
> > +               priv_ep->flags |= EP_STALL;
> > +       } else {
> > +               struct usb_request *request;
> > +
> > +               if (priv_dev->eps[index]->flags & EP_WEDGE) {
> > +                       cdns3_select_ep(priv_dev, 0x00);
> > +                       return 0;
> > +               }
> > +
> > +               writel(EP_CMD_CSTALL | EP_CMD_EPRST, &priv_dev->regs->ep_cmd);
> > +
> > +               /* wait for EPRST cleared */
> > +               ret = cdns3_handshake(&priv_dev->regs->ep_cmd,
> > +                                     EP_CMD_EPRST, 0, 100);
> > +               if (ret)
> > +                       return -EINVAL;
> > +
> > +               priv_ep->flags &= ~EP_STALL;
> > +
> > +               request = cdns3_next_request(&priv_ep->pending_req_list);
> > +               if (request)
> > +                       cdns3_ep_run_transfer(priv_ep, request);
> > +       }
> > +       return ret;
> > +}
> > +
> > +/**
> > + * cdns3_req_ep0_handle_feature -
> > + * Handling of GET/SET_FEATURE standard USB request
> > + *
> > + * @priv_dev: extended gadget object
> > + * @ctrl_req: pointer to received setup packet
> > + * @set: must be set to 1 for SET_FEATURE request
> > + *
> > + * Returns 0 if success, error code on error
> > + */
> > +static int cdns3_req_ep0_handle_feature(struct cdns3_device *priv_dev,
> > +                                       struct usb_ctrlrequest *ctrl,
> > +                                       int set)
> > +{
> > +       int ret = 0;
> > +       u32 recip;
> > +
> > +       recip = ctrl->bRequestType & USB_RECIP_MASK;
> > +
> > +       switch (recip) {
> > +       case USB_RECIP_DEVICE:
> > +               ret = cdns3_ep0_feature_handle_device(priv_dev, ctrl, set);
> > +               break;
> > +       case USB_RECIP_INTERFACE:
> > +               ret = cdns3_ep0_feature_handle_intf(priv_dev, ctrl, set);
> > +               break;
> > +       case USB_RECIP_ENDPOINT:
> > +               ret = cdns3_ep0_feature_handle_endpoint(priv_dev, ctrl, set);
> > +               break;
> > +       default:
> > +               return -EINVAL;
> > +       }
> > +
> > +       return ret;
> > +}
> > +
> > +/**
> > + * cdns3_req_ep0_set_sel - Handling of SET_SEL standard USB request
> > + * @priv_dev: extended gadget object
> > + * @ctrl_req: pointer to received setup packet
> > + *
> > + * Returns 0 if success, error code on error
> > + */
> > +static int cdns3_req_ep0_set_sel(struct cdns3_device *priv_dev,
> > +                                struct usb_ctrlrequest *ctrl_req)
> > +{
> > +       if (priv_dev->gadget.state < USB_STATE_ADDRESS)
> > +               return -EINVAL;
> > +
> > +       if (ctrl_req->wLength != 6) {
> > +               dev_err(priv_dev->dev, "Set SEL should be 6 bytes, got %d\n",
> > +                       ctrl_req->wLength);
> > +               return -EINVAL;
> > +       }
> > +
> > +       cdns3_ep0_run_transfer(priv_dev, priv_dev->setup_dma, 6, 1);
> > +       return 0;
> > +}
> > +
> > +/**
> > + * cdns3_req_ep0_set_isoch_delay -
> > + * Handling of GET_ISOCH_DELAY standard USB request
> > + * @priv_dev: extended gadget object
> > + * @ctrl_req: pointer to received setup packet
> > + *
> > + * Returns 0 if success, error code on error
> > + */
> > +static int cdns3_req_ep0_set_isoch_delay(struct cdns3_device *priv_dev,
> > +                                        struct usb_ctrlrequest *ctrl_req)
> > +{
> > +       if (ctrl_req->wIndex || ctrl_req->wLength)
> > +               return -EINVAL;
> > +
> > +       priv_dev->isoch_delay = ctrl_req->wValue;
> > +
> > +       return 0;
> > +}
> > +
> > +/**
> > + * cdns3_ep0_standard_request - Handling standard USB requests
> > + * @priv_dev: extended gadget object
> > + * @ctrl_req: pointer to received setup packet
> > + *
> > + * Returns 0 if success, error code on error
> > + */
> > +static int cdns3_ep0_standard_request(struct cdns3_device *priv_dev,
> > +                                     struct usb_ctrlrequest *ctrl_req)
> > +{
> > +       int ret;
> > +
> > +       switch (ctrl_req->bRequest) {
> > +       case USB_REQ_SET_ADDRESS:
> > +               ret = cdns3_req_ep0_set_address(priv_dev, ctrl_req);
> > +               break;
> > +       case USB_REQ_SET_CONFIGURATION:
> > +               ret = cdns3_req_ep0_set_configuration(priv_dev, ctrl_req);
> > +               break;
> > +       case USB_REQ_GET_STATUS:
> > +               ret = cdns3_req_ep0_get_status(priv_dev, ctrl_req);
> > +               break;
> > +       case USB_REQ_CLEAR_FEATURE:
> > +               ret = cdns3_req_ep0_handle_feature(priv_dev, ctrl_req, 0);
> > +               break;
> > +       case USB_REQ_SET_FEATURE:
> > +               ret = cdns3_req_ep0_handle_feature(priv_dev, ctrl_req, 1);
> > +               break;
> > +       case USB_REQ_SET_SEL:
> > +               ret = cdns3_req_ep0_set_sel(priv_dev, ctrl_req);
> > +               break;
> > +       case USB_REQ_SET_ISOCH_DELAY:
> > +               ret = cdns3_req_ep0_set_isoch_delay(priv_dev, ctrl_req);
> > +               break;
> > +       default:
> > +               ret = cdns3_ep0_delegate_req(priv_dev, ctrl_req);
> > +               break;
> > +       }
> > +
> > +       return ret;
> > +}
> > +
> > +static void __pending_setup_status_handler(struct cdns3_device *priv_dev)
> > +{
> > +       struct usb_request *request = priv_dev->pending_status_request;
> > +
> > +       if (priv_dev->status_completion_no_call && request &&
> > +           request->complete) {
> > +               request->complete(&priv_dev->eps[0]->endpoint, request);
> > +               priv_dev->status_completion_no_call = 0;
> > +       }
> > +}
> > +
> > +void cdns3_pending_setup_status_handler(struct work_struct *work)
> > +{
> > +       struct cdns3_device *priv_dev = container_of(work, struct cdns3_device,
> > +                       pending_status_wq);
> > +       unsigned long flags;
> > +
> > +       spin_lock_irqsave(&priv_dev->lock, flags);
> > +       __pending_setup_status_handler(priv_dev);
> > +       spin_unlock_irqrestore(&priv_dev->lock, flags);
> > +}
> > +
> > +/**
> > + * cdns3_gadget_ep_giveback - call struct usb_request's ->complete callback
> > + * @priv_ep: The endpoint to whom the request belongs to
> > + * @priv_req: The request we're giving back
> > + * @status: completion code for the request
> > + *
> > + * Must be called with controller's lock held and interrupts disabled. This
> > + * function will unmap @req and call its ->complete() callback to notify upper
> > + * layers that it has completed.
> > + */
> > +
> > +void cdns3_gadget_ep0_giveback(struct cdns3_device *priv_dev,
> > +                              int status)
> > +{
> > +       struct cdns3_endpoint *priv_ep;
> > +       struct usb_request *request;
> > +
> > +       priv_ep = priv_dev->eps[0];
> > +       request = cdns3_next_request(&priv_ep->pending_req_list);
> > +
> > +       priv_ep->dir = priv_dev->ep0_data_dir;
> > +       cdns3_gadget_giveback(priv_ep, to_cdns3_request(request), status);
> > +}
> > +
> > +/**
> > + * cdns3_ep0_setup_phase - Handling setup USB requests
> > + * @priv_dev: extended gadget object
> > + */
> > +static void cdns3_ep0_setup_phase(struct cdns3_device *priv_dev)
> > +{
> > +       struct usb_ctrlrequest *ctrl = priv_dev->setup_buf;
> > +       struct cdns3_endpoint *priv_ep = priv_dev->eps[0];
> > +       int result;
> > +
> > +       priv_dev->ep0_data_dir = ctrl->bRequestType & USB_DIR_IN;
> > +
> > +       trace_cdns3_ctrl_req(ctrl);
> > +
> > +       if (!list_empty(&priv_ep->pending_req_list)) {
> > +               struct usb_request *request;
> > +
> > +               request = cdns3_next_request(&priv_ep->pending_req_list);
> > +               priv_ep->dir = priv_dev->ep0_data_dir;
> > +               cdns3_gadget_giveback(priv_ep, to_cdns3_request(request),
> > +                                     -ECONNRESET);
> > +       }
> > +
> > +       if (le16_to_cpu(ctrl->wLength))
> > +               priv_dev->ep0_stage = CDNS3_DATA_STAGE;
> > +       else
> > +               priv_dev->ep0_stage = CDNS3_STATUS_STAGE;
> > +
> > +       if ((ctrl->bRequestType & USB_TYPE_MASK) == USB_TYPE_STANDARD)
> > +               result = cdns3_ep0_standard_request(priv_dev, ctrl);
> > +       else
> > +               result = cdns3_ep0_delegate_req(priv_dev, ctrl);
> > +
> > +       if (priv_dev->ep0_stage == CDNS3_STATUS_STAGE &&
> > +           result != USB_GADGET_DELAYED_STATUS) {
> > +               cdns3_ep0_complete_setup(priv_dev, 0, 1);
> > +       } else if (result < 0) {
> > +               cdns3_ep0_complete_setup(priv_dev, 1, 1);
> > +       }
> > +}
> > +
> > +static void cdns3_transfer_completed(struct cdns3_device *priv_dev)
> > +{
> > +       struct cdns3_endpoint *priv_ep = priv_dev->eps[0];
> > +
> > +       if (!list_empty(&priv_ep->pending_req_list)) {
> > +               struct usb_request *request;
> > +
> > +               trace_cdns3_complete_trb(priv_ep, priv_ep->trb_pool);
> > +               request = cdns3_next_request(&priv_ep->pending_req_list);
> > +
> > +               request->actual =
> > +                       TRB_LEN(le32_to_cpu(priv_ep->trb_pool->length));
> > +
> > +               priv_ep->dir = priv_dev->ep0_data_dir;
> > +               cdns3_gadget_giveback(priv_ep, to_cdns3_request(request), 0);
> > +       }
> > +
> > +       cdns3_ep0_complete_setup(priv_dev, 0, 0);
> > +}
> > +
> > +/**
> > + * cdns3_check_new_setup - Check if controller receive new SETUP packet.
> > + * @priv_dev: extended gadget object
> > + *
> > + * The SETUP packet can be kept in on-chip memory or in system memory.
> > + */
> > +static bool cdns3_check_new_setup(struct cdns3_device *priv_dev)
> > +{
> > +       u32 ep_sts_reg;
> > +
> > +       cdns3_select_ep(priv_dev, 0 | USB_DIR_OUT);
> > +       ep_sts_reg = readl(&priv_dev->regs->ep_sts);
> > +
> > +       return !!(ep_sts_reg & (EP_STS_SETUP | EP_STS_STPWAIT));
> > +}
> > +
> > +/**
> > + * cdns3_check_ep0_interrupt_proceed - Processes interrupt related to endpoint 0
> > + * @priv_dev: extended gadget object
> > + * @dir: USB_DIR_IN for IN direction, USB_DIR_OUT for OUT direction
> > + */
> > +void cdns3_check_ep0_interrupt_proceed(struct cdns3_device *priv_dev, int dir)
> > +{
> > +       u32 ep_sts_reg;
> > +
> > +       cdns3_select_ep(priv_dev, dir);
> > +
> > +       ep_sts_reg = readl(&priv_dev->regs->ep_sts);
> > +       writel(ep_sts_reg, &priv_dev->regs->ep_sts);
> > +
> > +       trace_cdns3_ep0_irq(priv_dev, ep_sts_reg);
> > +
> > +       __pending_setup_status_handler(priv_dev);
> > +
> > +       if ((ep_sts_reg & EP_STS_SETUP)) {
> > +               cdns3_ep0_setup_phase(priv_dev);
> > +       } else if ((ep_sts_reg & EP_STS_IOC) || (ep_sts_reg & EP_STS_ISP)) {
> > +               priv_dev->ep0_data_dir = dir;
> > +               cdns3_transfer_completed(priv_dev);
> > +       }
> > +
> > +       if (ep_sts_reg & EP_STS_DESCMIS) {
> > +               if (dir == 0 && !priv_dev->setup_pending)
> > +                       cdns3_prepare_setup_packet(priv_dev);
> > +       }
> > +}
> > +
> > +/**
> > + * cdns3_gadget_ep0_enable
> > + * Function shouldn't be called by gadget driver,
> > + * endpoint 0 is allways active
> > + */
> > +static int cdns3_gadget_ep0_enable(struct usb_ep *ep,
> > +                                  const struct usb_endpoint_descriptor *desc)
> > +{
> > +       return -EINVAL;
> > +}
> > +
> > +/**
> > + * cdns3_gadget_ep0_disable
> > + * Function shouldn't be called by gadget driver,
> > + * endpoint 0 is allways active
> > + */
> > +static int cdns3_gadget_ep0_disable(struct usb_ep *ep)
> > +{
> > +       return -EINVAL;
> > +}
> > +
> > +/**
> > + * cdns3_gadget_ep0_set_halt
> > + * @ep: pointer to endpoint zero object
> > + * @value: 1 for set stall, 0 for clear stall
> > + *
> > + * Returns 0
> > + */
> > +static int cdns3_gadget_ep0_set_halt(struct usb_ep *ep, int value)
> > +{
> > +       /* TODO */
> > +       return 0;
> > +}
> > +
> > +/**
> > + * cdns3_gadget_ep0_queue Transfer data on endpoint zero
> > + * @ep: pointer to endpoint zero object
> > + * @request: pointer to request object
> > + * @gfp_flags: gfp flags
> > + *
> > + * Returns 0 on success, error code elsewhere
> > + */
> > +static int cdns3_gadget_ep0_queue(struct usb_ep *ep,
> > +                                 struct usb_request *request,
> > +                                 gfp_t gfp_flags)
> > +{
> > +       struct cdns3_endpoint *priv_ep = ep_to_cdns3_ep(ep);
> > +       struct cdns3_device *priv_dev = priv_ep->cdns3_dev;
> > +       unsigned long flags;
> > +       int erdy_sent = 0;
> > +       int ret = 0;
> > +
> > +       dev_dbg(priv_dev->dev, "Queue to Ep0%s L: %d\n",
> > +               priv_dev->ep0_data_dir ? "IN" : "OUT",
> > +               request->length);
> > +
> > +       /* cancel the request if controller receive new SETUP packet. */
> > +       if (cdns3_check_new_setup(priv_dev))
> > +               return -ECONNRESET;
> > +
> > +       /* send STATUS stage. Should be called only for SET_CONFIGURATION */
> > +       if (priv_dev->ep0_stage == CDNS3_STATUS_STAGE) {
> > +               spin_lock_irqsave(&priv_dev->lock, flags);
> > +               cdns3_select_ep(priv_dev, 0x00);
> > +
> > +               erdy_sent = !priv_dev->hw_configured_flag;
> > +               cdns3_set_hw_configuration(priv_dev);
> > +
> > +               if (!erdy_sent)
> > +                       cdns3_ep0_complete_setup(priv_dev, 0, 1);
> > +
> > +               request->actual = 0;
> > +               priv_dev->status_completion_no_call = true;
> > +               priv_dev->pending_status_request = request;
> > +               spin_unlock_irqrestore(&priv_dev->lock, flags);
> > +
> > +               /*
> > +                * Since there is no completion interrupt for status stage,
> > +                * it needs to call ->completion in software after
> > +                * ep0_queue is back.
> > +                */
> > +               queue_work(system_freezable_wq, &priv_dev->pending_status_wq);
> > +               return 0;
> > +       }
> > +
> > +       spin_lock_irqsave(&priv_dev->lock, flags);
> > +       if (!list_empty(&priv_ep->pending_req_list)) {
> > +               dev_err(priv_dev->dev,
> > +                       "can't handle multiple requests for ep0\n");
> > +               spin_unlock_irqrestore(&priv_dev->lock, flags);
> > +               return -EBUSY;
> > +       }
> > +
> > +       ret = usb_gadget_map_request_by_dev(priv_dev->sysdev, request,
> > +                                           priv_dev->ep0_data_dir);
> > +       if (ret) {
> > +               spin_unlock_irqrestore(&priv_dev->lock, flags);
> > +               dev_err(priv_dev->dev, "failed to map request\n");
> > +               return -EINVAL;
> > +       }
> > +
> > +       request->status = -EINPROGRESS;
> > +       list_add_tail(&request->list, &priv_ep->pending_req_list);
> > +       cdns3_ep0_run_transfer(priv_dev, request->dma, request->length, 1);
> > +       spin_unlock_irqrestore(&priv_dev->lock, flags);
> > +
> > +       return ret;
> > +}
> > +
> > +/**
> > + * cdns3_gadget_ep_set_wedge Set wedge on selected endpoint
> > + * @ep: endpoint object
> > + *
> > + * Returns 0
> > + */
> > +int cdns3_gadget_ep_set_wedge(struct usb_ep *ep)
> > +{
> > +       struct cdns3_endpoint *priv_ep = ep_to_cdns3_ep(ep);
> > +       struct cdns3_device *priv_dev = priv_ep->cdns3_dev;
> > +
> > +       dev_dbg(priv_dev->dev, "Wedge for %s\n", ep->name);
> > +       cdns3_gadget_ep_set_halt(ep, 1);
> > +       priv_ep->flags |= EP_WEDGE;
> > +
> > +       return 0;
> > +}
> > +
> > +const struct usb_ep_ops cdns3_gadget_ep0_ops = {
> > +       .enable = cdns3_gadget_ep0_enable,
> > +       .disable = cdns3_gadget_ep0_disable,
> > +       .alloc_request = cdns3_gadget_ep_alloc_request,
> > +       .free_request = cdns3_gadget_ep_free_request,
> > +       .queue = cdns3_gadget_ep0_queue,
> > +       .dequeue = cdns3_gadget_ep_dequeue,
> > +       .set_halt = cdns3_gadget_ep0_set_halt,
> > +       .set_wedge = cdns3_gadget_ep_set_wedge,
> > +};
> > +
> > +/**
> > + * cdns3_ep0_config - Configures default endpoint
> > + * @priv_dev: extended gadget object
> > + *
> > + * Functions sets parameters: maximal packet size and enables interrupts
> > + */
> > +void cdns3_ep0_config(struct cdns3_device *priv_dev)
> > +{
> > +       struct cdns3_usb_regs __iomem *regs;
> > +       struct cdns3_endpoint *priv_ep;
> > +       u32 max_packet_size = 64;
> > +
> > +       regs = priv_dev->regs;
> > +
> > +       if (priv_dev->gadget.speed == USB_SPEED_SUPER)
> > +               max_packet_size = 512;
> > +
> > +       priv_ep = priv_dev->eps[0];
> > +
> > +       if (!list_empty(&priv_ep->pending_req_list)) {
> > +               struct usb_request *request;
> > +
> > +               request = cdns3_next_request(&priv_ep->pending_req_list);
> > +               list_del_init(&request->list);
> > +       }
> > +
> > +       priv_dev->u1_allowed = 0;
> > +       priv_dev->u2_allowed = 0;
> > +
> > +       priv_dev->gadget.ep0->maxpacket = max_packet_size;
> > +       cdns3_gadget_ep0_desc.wMaxPacketSize = cpu_to_le16(max_packet_size);
> > +
> > +       /* init ep out */
> > +       cdns3_select_ep(priv_dev, USB_DIR_OUT);
> > +
> > +       writel(EP_CFG_ENABLE | EP_CFG_MAXPKTSIZE(max_packet_size),
> > +              &regs->ep_cfg);
> > +
> > +       writel(EP_STS_EN_SETUPEN | EP_STS_EN_DESCMISEN | EP_STS_EN_TRBERREN,
> > +              &regs->ep_sts_en);
> > +
> > +       /* init ep in */
> > +       cdns3_select_ep(priv_dev, USB_DIR_IN);
> > +
> > +       writel(EP_CFG_ENABLE | EP_CFG_MAXPKTSIZE(max_packet_size),
> > +              &regs->ep_cfg);
> > +
> > +       writel(EP_STS_EN_SETUPEN | EP_STS_EN_TRBERREN, &regs->ep_sts_en);
> > +
> > +       cdns3_set_register_bit(&regs->usb_conf, USB_CONF_U1DS | USB_CONF_U2DS);
> > +}
> > +
> > +/**
> > + * cdns3_init_ep0 Initializes software endpoint 0 of gadget
> > + * @priv_dev: extended gadget object
> > + * @ep_priv: extended endpoint object
> > + *
> > + * Returns 0 on success else error code.
> > + */
> > +int cdns3_init_ep0(struct cdns3_device *priv_dev,
> > +                  struct cdns3_endpoint *priv_ep)
> > +{
> > +       sprintf(priv_ep->name, "ep0");
> > +
> > +       /* fill linux fields */
> > +       priv_ep->endpoint.ops = &cdns3_gadget_ep0_ops;
> > +       priv_ep->endpoint.maxburst = 1;
> > +       usb_ep_set_maxpacket_limit(&priv_ep->endpoint,
> > +                                  CDNS3_EP0_MAX_PACKET_LIMIT);
> > +       priv_ep->endpoint.address = 0;
> > +       priv_ep->endpoint.caps.type_control = 1;
> > +       priv_ep->endpoint.caps.dir_in = 1;
> > +       priv_ep->endpoint.caps.dir_out = 1;
> > +       priv_ep->endpoint.name = priv_ep->name;
> > +       priv_ep->endpoint.desc = &cdns3_gadget_ep0_desc;
> > +       priv_dev->gadget.ep0 = &priv_ep->endpoint;
> > +       priv_ep->type = USB_ENDPOINT_XFER_CONTROL;
> > +
> > +       return cdns3_allocate_trb_pool(priv_ep);
> > +}
> > diff --git a/drivers/usb/cdns3/gadget-export.h b/drivers/usb/cdns3/gadget-export.h
> > new file mode 100644
> > index 000000000000..577469eee961
> > --- /dev/null
> > +++ b/drivers/usb/cdns3/gadget-export.h
> > @@ -0,0 +1,28 @@
> > +/* SPDX-License-Identifier: GPL-2.0 */
> > +/*
> > + * Cadence USBSS DRD Driver - Gadget Export APIs.
> > + *
> > + * Copyright (C) 2017 NXP
> > + * Copyright (C) 2017-2018 NXP
> > + *
> > + * Authors: Peter Chen <peter.chen@nxp.com>
> > + */
> > +#ifndef __LINUX_CDNS3_GADGET_EXPORT
> > +#define __LINUX_CDNS3_GADGET_EXPORT
> > +
> > +#ifdef CONFIG_USB_CDNS3_GADGET
> > +
> > +int cdns3_gadget_init(struct cdns3 *cdns);
> > +void cdns3_gadget_exit(struct cdns3 *cdns);
> > +#else
> > +
> > +static inline int cdns3_gadget_init(struct cdns3 *cdns)
> > +{
> > +       return -ENXIO;
> > +}
> > +
> > +static inline void cdns3_gadget_exit(struct cdns3 *cdns) { }
> > +
> > +#endif
> > +
> > +#endif /* __LINUX_CDNS3_GADGET_EXPORT */
> > diff --git a/drivers/usb/cdns3/gadget.c b/drivers/usb/cdns3/gadget.c
> > new file mode 100644
> > index 000000000000..0d95eb00be37
> > --- /dev/null
> > +++ b/drivers/usb/cdns3/gadget.c
> > @@ -0,0 +1,2102 @@
> > +// SPDX-License-Identifier: GPL-2.0
> > +/*
> > + * Cadence USBSS DRD Driver - gadget side.
> > + *
> > + * Copyright (C) 2018 Cadence Design Systems.
> > + * Copyright (C) 2017-2018 NXP
> > + *
> > + * Authors: Pawel Jez <pjez@cadence.com>,
> > + *          Pawel Laszczak <pawell@cadence.com>
> > + *         Peter Chen <peter.chen@nxp.com>
> > + */
> > +
> > +#include <linux/dma-mapping.h>
> > +#include <linux/usb/gadget.h>
> > +
> > +#include "core.h"
> > +#include "gadget-export.h"
> > +#include "gadget.h"
> > +
> > +#include "trace.h"
> > +
> > +static int __cdns3_gadget_ep_queue(struct usb_ep *ep,
> > +                                  struct usb_request *request,
> > +                                  gfp_t gfp_flags);
> > +
> > +/**
> > + * cdns3_handshake - spin reading  until handshake completes or fails
> > + * @ptr: address of device controller register to be read
> > + * @mask: bits to look at in result of read
> > + * @done: value of those bits when handshake succeeds
> > + * @usec: timeout in microseconds
> > + *
> > + * Returns negative errno, or zero on success
> > + *
> > + * Success happens when the "mask" bits have the specified value (hardware
> > + * handshake done). There are two failure modes: "usec" have passed (major
> > + * hardware flakeout), or the register reads as all-ones (hardware removed).
> > + */
> > +int cdns3_handshake(void __iomem *ptr, u32 mask, u32 done, int usec)
> > +{
> > +       u32     result;
> > +
> > +       do {
> > +               result = readl(ptr);
> > +               if (result == ~(u32)0)  /* card removed */
> > +                       return -ENODEV;
> > +               result &= mask;
> > +               if (result == done)
> > +                       return 0;
> > +               udelay(1);
> > +               usec--;
> > +       } while (usec > 0);
> > +       return -ETIMEDOUT;
> > +}
> > +
> > +/**
> > + * cdns3_set_register_bit - set bit in given register.
> > + * @ptr: address of device controller register to be read and changed
> > + * @mask: bits requested to set
> > + */
> > +void cdns3_set_register_bit(void __iomem *ptr, u32 mask)
> > +{
> > +       mask = readl(ptr) | mask;
> > +       writel(mask, ptr);
> > +}
> > +
> > +/**
> > + * cdns3_ep_addr_to_index - Macro converts endpoint address to
> > + * index of endpoint object in cdns3_device.eps[] container
> > + * @ep_addr: endpoint address for which endpoint object is required
> > + *
> > + */
> > +u8 cdns3_ep_addr_to_index(u8 ep_addr)
> > +{
> > +       return (((ep_addr & 0x7F)) + ((ep_addr & USB_DIR_IN) ? 16 : 0));
> > +}
> > +
> > +/**
> > + * cdns3_next_request - returns next request from list
> > + * @list: list containing requests
> > + *
> > + * Returns request or NULL if no requests in list
> > + */
> > +struct usb_request *cdns3_next_request(struct list_head *list)
> > +{
> > +       if (list_empty(list))
> > +               return NULL;
> > +       return list_first_entry_or_null(list, struct usb_request, list);
> > +}
> > +
> > +/**
> > + * cdns3_next_priv_request - returns next request from list
> > + * @list: list containing requests
> > + *
> > + * Returns request or NULL if no requests in list
> > + */
> > +struct cdns3_request *cdns3_next_priv_request(struct list_head *list)
> > +{
> > +       if (list_empty(list))
> > +               return NULL;
> > +       return list_first_entry_or_null(list, struct cdns3_request, list);
> > +}
> > +
> > +/**
> > + * select_ep - selects endpoint
> > + * @priv_dev:  extended gadget object
> > + * @ep: endpoint address
> > + */
> > +void cdns3_select_ep(struct cdns3_device *priv_dev, u32 ep)
> > +{
> > +       if (priv_dev->selected_ep == ep)
> > +               return;
> > +
> > +       priv_dev->selected_ep = ep;
> > +       writel(ep, &priv_dev->regs->ep_sel);
> > +}
> > +
> > +dma_addr_t cdns3_trb_virt_to_dma(struct cdns3_endpoint *priv_ep,
> > +                                struct cdns3_trb *trb)
> > +{
> > +       u32 offset = (char *)trb - (char *)priv_ep->trb_pool;
> > +
> > +       return priv_ep->trb_pool_dma + offset;
> > +}
> > +
> > +int cdns3_ring_size(struct cdns3_endpoint *priv_ep)
> > +{
> > +       switch (priv_ep->type) {
> > +       case USB_ENDPOINT_XFER_ISOC:
> > +               return TRB_ISO_RING_SIZE;
> > +       case USB_ENDPOINT_XFER_CONTROL:
> > +               return TRB_CTRL_RING_SIZE;
> > +       default:
> > +               return TRB_RING_SIZE;
> > +       }
> > +}
> > +
> > +/**
> > + * cdns3_allocate_trb_pool - Allocates TRB's pool for selected endpoint
> > + * @priv_ep:  endpoint object
> > + *
> > + * Function will return 0 on success or -ENOMEM on allocation error
> > + */
> > +int cdns3_allocate_trb_pool(struct cdns3_endpoint *priv_ep)
> > +{
> > +       struct cdns3_device *priv_dev = priv_ep->cdns3_dev;
> > +       int ring_size = cdns3_ring_size(priv_ep);
> > +       struct cdns3_trb *link_trb;
> > +
> > +       if (!priv_ep->trb_pool) {
> > +               priv_ep->trb_pool = dma_zalloc_coherent(priv_dev->sysdev,
> > +                                                       ring_size,
> > +                                                       &priv_ep->trb_pool_dma,
> > +                                                       GFP_DMA);
> > +               if (!priv_ep->trb_pool)
> > +                       return -ENOMEM;
> > +       } else {
> > +               memset(priv_ep->trb_pool, 0, ring_size);
> > +       }
> > +
> > +       if (!priv_ep->num)
> > +               return 0;
> > +
> > +       if (!priv_ep->aligned_buff) {
> > +               void *buff = dma_alloc_coherent(priv_dev->sysdev,
> > +                                               CDNS3_ALIGNED_BUF_SIZE,
> > +                                               &priv_ep->aligned_dma_addr,
> > +                                               GFP_DMA);
> > +
> > +               priv_ep->aligned_buff  = buff;
> > +               if (!priv_ep->aligned_buff) {
> > +                       dma_free_coherent(priv_dev->sysdev,
> > +                                         ring_size,
> > +                                         priv_ep->trb_pool,
> > +                                         priv_ep->trb_pool_dma);
> > +                       priv_ep->trb_pool = NULL;
> > +
> > +                       return -ENOMEM;
> > +               }
> > +       }
> > +
> > +       priv_ep->num_trbs = ring_size / TRB_SIZE;
> > +       /* Initialize the last TRB as Link TRB */
> > +       link_trb = (priv_ep->trb_pool + (priv_ep->num_trbs - 1));
> > +       link_trb->buffer = TRB_BUFFER(priv_ep->trb_pool_dma);
> > +       link_trb->control = TRB_CYCLE | TRB_TYPE(TRB_LINK) |
> > +                           TRB_CHAIN | TRB_TOGGLE;
> > +
> > +       return 0;
> > +}
> > +
> > +static void cdns3_free_trb_pool(struct cdns3_endpoint *priv_ep)
> > +{
> > +       struct cdns3_device *priv_dev = priv_ep->cdns3_dev;
> > +
> > +       if (priv_ep->trb_pool) {
> > +               dma_free_coherent(priv_dev->sysdev,
> > +                                 cdns3_ring_size(priv_ep),
> > +                                 priv_ep->trb_pool, priv_ep->trb_pool_dma);
> > +               priv_ep->trb_pool = NULL;
> > +       }
> > +
> > +       if (priv_ep->aligned_buff) {
> > +               dma_free_coherent(priv_dev->sysdev, CDNS3_ALIGNED_BUF_SIZE,
> > +                                 priv_ep->aligned_buff,
> > +                                 priv_ep->aligned_dma_addr);
> > +               priv_ep->aligned_buff = NULL;
> > +       }
> > +}
> > +
> > +/**
> > + * cdns3_data_flush - flush data at onchip buffer
> > + * @priv_ep: endpoint object
> > + *
> > + * Endpoint must be selected before call to this function
> > + *
> > + * Returns zero on success or negative value on failure
> > + */
> > +static int cdns3_data_flush(struct cdns3_endpoint *priv_ep)
> > +{
> > +       struct cdns3_device *priv_dev = priv_ep->cdns3_dev;
> > +
> > +       writel(EP_CMD_DFLUSH, &priv_dev->regs->ep_cmd);
> > +
> > +       /* wait for DFLUSH cleared */
> > +       return cdns3_handshake(&priv_dev->regs->ep_cmd, EP_CMD_DFLUSH, 0, 100);
> > +}
> > +
> > +/**
> > + * cdns3_ep_stall_flush - Stalls and flushes selected endpoint
> > + * @priv_ep: endpoint object
> > + *
> > + * Endpoint must be selected before call to this function
> > + */
> > +static void cdns3_ep_stall_flush(struct cdns3_endpoint *priv_ep)
> > +{
> > +       struct cdns3_device *priv_dev = priv_ep->cdns3_dev;
> > +
> > +       writel(EP_CMD_DFLUSH | EP_CMD_ERDY | EP_CMD_SSTALL,
> > +              &priv_dev->regs->ep_cmd);
> > +
> > +       /* wait for DFLUSH cleared */
> > +       cdns3_handshake(&priv_dev->regs->ep_cmd, EP_CMD_DFLUSH, 0, 100);
> > +       priv_ep->flags |= EP_STALL;
> > +}
> > +
> > +/**
> > + * cdns3_hw_reset_eps_config - reset endpoints configuration kept by controller.
> > + * @priv_dev: extended gadget object
> > + */
> > +void cdns3_hw_reset_eps_config(struct cdns3_device *priv_dev)
> > +{
> > +       writel(USB_CONF_CFGRST, &priv_dev->regs->usb_conf);
> > +
> > +       cdns3_allow_enable_l1(priv_dev, 0);
> > +       priv_dev->hw_configured_flag = 0;
> > +       priv_dev->onchip_mem_allocated_size = 0;
> > +}
> > +
> > +/**
> > + * cdns3_ep_inc_trb - increment a trb index.
> > + * @index: Pointer to the TRB index to increment.
> > + * @cs: Cycle state
> > + * @trb_in_seg: number of TRBs in segment
> > + *
> > + * The index should never point to the link TRB. After incrementing,
> > + * if it is point to the link TRB, wrap around to the beginning and revert
> > + * cycle state bit The
> > + * link TRB is always at the last TRB entry.
> > + */
> > +static void cdns3_ep_inc_trb(int *index, u8 *cs, int trb_in_seg)
> > +{
> > +       (*index)++;
> > +       if (*index == (trb_in_seg - 1)) {
> > +               *index = 0;
> > +               *cs ^=  1;
> > +       }
> > +}
> > +
> > +/**
> > + * cdns3_ep_inc_enq - increment endpoint's enqueue pointer
> > + * @priv_ep: The endpoint whose enqueue pointer we're incrementing
> > + */
> > +static void cdns3_ep_inc_enq(struct cdns3_endpoint *priv_ep)
> > +{
> > +       priv_ep->free_trbs--;
> > +       cdns3_ep_inc_trb(&priv_ep->enqueue, &priv_ep->pcs, priv_ep->num_trbs);
> > +}
> > +
> > +/**
> > + * cdns3_ep_inc_deq - increment endpoint's dequeue pointer
> > + * @priv_ep: The endpoint whose dequeue pointer we're incrementing
> > + */
> > +static void cdns3_ep_inc_deq(struct cdns3_endpoint *priv_ep)
> > +{
> > +       priv_ep->free_trbs++;
> > +       cdns3_ep_inc_trb(&priv_ep->dequeue, &priv_ep->ccs, priv_ep->num_trbs);
> > +}
> > +
> > +/**
> > + * cdns3_allow_enable_l1 - enable/disable permits to transition to L1.
> > + * @priv_dev: Extended gadget object
> > + * @enable: Enable/disable permit to transition to L1.
> > + *
> > + * If bit USB_CONF_L1EN is set and device receive Extended Token packet,
> > + * then controller answer with ACK handshake.
> > + * If bit USB_CONF_L1DS is set and device receive Extended Token packet,
> > + * then controller answer with NYET handshake.
> > + */
> > +void cdns3_allow_enable_l1(struct cdns3_device *priv_dev, int enable)
> > +{
> > +       if (enable)
> > +               writel(USB_CONF_L1EN, &priv_dev->regs->usb_conf);
> > +       else
> > +               writel(USB_CONF_L1DS, &priv_dev->regs->usb_conf);
> > +}
> > +
> > +enum usb_device_speed cdns3_get_speed(struct cdns3_device *priv_dev)
> > +{
> > +       u32 reg;
> > +
> > +       reg = readl(&priv_dev->regs->usb_sts);
> > +
> > +       if (DEV_SUPERSPEED(reg))
> > +               return USB_SPEED_SUPER;
> > +       else if (DEV_HIGHSPEED(reg))
> > +               return USB_SPEED_HIGH;
> > +       else if (DEV_FULLSPEED(reg))
> > +               return USB_SPEED_FULL;
> > +       else if (DEV_LOWSPEED(reg))
> > +               return USB_SPEED_LOW;
> > +       return USB_SPEED_UNKNOWN;
> > +}
> > +
> > +/**
> > + * cdns3_start_all_request - add to ring all request not started
> > + * @priv_dev: Extended gadget object
> > + * @priv_ep: The endpoint for whom request will be started.
> > + *
> > + * Returns return ENOMEM if transfer ring i not enough TRBs to start
> > + *         all requests.
> > + */
> > +static int cdns3_start_all_request(struct cdns3_device *priv_dev,
> > +                                  struct cdns3_endpoint *priv_ep)
> > +{
> > +       struct cdns3_request *priv_req;
> > +       struct usb_request *request;
> > +       int ret = 0;
> > +
> > +       while (!list_empty(&priv_ep->deferred_req_list)) {
> > +               request = cdns3_next_request(&priv_ep->deferred_req_list);
> > +               priv_req = to_cdns3_request(request);
> > +
> > +               ret = cdns3_ep_run_transfer(priv_ep, request);
> > +               if (ret)
> > +                       return ret;
> > +
> > +               list_del(&request->list);
> > +               list_add_tail(&request->list,
> > +                             &priv_ep->pending_req_list);
> > +       }
> > +
> > +       priv_ep->flags &= ~EP_RING_FULL;
> > +       return ret;
> > +}
> > +
> > +/**
> > + * cdns3_descmiss_copy_data copy data from internal requests to request queued
> > + * by class driver.
> > + * @priv_ep: extended endpoint object
> > + * @request: request object
> > + */
> > +static void cdns3_descmiss_copy_data(struct cdns3_endpoint *priv_ep,
> > +                                    struct usb_request *request)
> > +{
> > +       struct usb_request *descmiss_req;
> > +       struct cdns3_request *descmiss_priv_req;
> > +
> > +       while (!list_empty(&priv_ep->descmiss_req_list)) {
> > +               int chunk_end;
> > +               int length;
> > +
> > +               descmiss_priv_req =
> > +                       cdns3_next_priv_request(&priv_ep->descmiss_req_list);
> > +               descmiss_req = &descmiss_priv_req->request;
> > +
> > +               /* driver can't touch pending request */
> > +               if (descmiss_priv_req->flags & REQUEST_PENDING)
> > +                       break;
> > +
> > +               chunk_end = descmiss_priv_req->flags & REQUEST_INTERNAL_CH;
> > +               length = request->actual + descmiss_req->actual;
> > +
> > +               if (length <= request->length) {
> > +                       memcpy(&((u8 *)request->buf)[request->actual],
> > +                              descmiss_req->buf,
> > +                              descmiss_req->actual);
> > +                       request->actual = length;
> > +               } else {
> > +                       /* It should never occures */
> > +                       request->status = -ENOMEM;
> > +               }
> > +
> > +               list_del_init(&descmiss_priv_req->list);
> > +
> > +               kfree(descmiss_req->buf);
> > +               cdns3_gadget_ep_free_request(&priv_ep->endpoint, descmiss_req);
> > +
> > +               if (!chunk_end)
> > +                       break;
> > +       }
> > +}
> > +
> > +/**
> > + * cdns3_gadget_giveback - call struct usb_request's ->complete callback
> > + * @priv_ep: The endpoint to whom the request belongs to
> > + * @priv_req: The request we're giving back
> > + * @status: completion code for the request
> > + *
> > + * Must be called with controller's lock held and interrupts disabled. This
> > + * function will unmap @req and call its ->complete() callback to notify upper
> > + * layers that it has completed.
> > + */
> > +void cdns3_gadget_giveback(struct cdns3_endpoint *priv_ep,
> > +                          struct cdns3_request *priv_req,
> > +                          int status)
> > +{
> > +       struct cdns3_device *priv_dev = priv_ep->cdns3_dev;
> > +       struct usb_request *request = &priv_req->request;
> > +
> > +       list_del_init(&request->list);
> > +
> > +       if (request->status == -EINPROGRESS)
> > +               request->status = status;
> > +
> > +       usb_gadget_unmap_request_by_dev(priv_dev->sysdev, request,
> > +                                       priv_ep->dir);
> > +
> > +       priv_req->flags &= ~REQUEST_PENDING;
> > +       trace_cdns3_gadget_giveback(priv_req);
> > +
> > +       /* WA1: */
> > +       if (priv_ep->flags & EP_QUIRK_EXTRA_BUF_EN &&
> > +           priv_req->flags & REQUEST_INTERNAL) {
> > +               struct usb_request *req;
> > +
> > +               req = cdns3_next_request(&priv_ep->deferred_req_list);
> > +               request = req;
> > +               priv_ep->descmis_req = NULL;
> > +
> > +               if (!req)
> > +                       return;
> > +
> > +               cdns3_descmiss_copy_data(priv_ep, req);
> > +               if (!(priv_ep->flags & EP_QUIRK_END_TRANSFER) &&
> > +                   req->length != req->actual) {
> > +                       /* wait for next part of transfer */
> > +                       return;
> > +               }
> > +
> > +               if (req->status == -EINPROGRESS)
> > +                       req->status = 0;
> > +
> > +               list_del_init(&req->list);
> > +               cdns3_start_all_request(priv_dev, priv_ep);
> > +       }
> > +
> > +       /* Start all not pending request */
> > +       if (priv_ep->flags & EP_RING_FULL)
> > +               cdns3_start_all_request(priv_dev, priv_ep);
> > +
> > +       if (request->complete) {
> > +               spin_unlock(&priv_dev->lock);
> > +               usb_gadget_giveback_request(&priv_ep->endpoint,
> > +                                           request);
> > +               spin_lock(&priv_dev->lock);
> > +       }
> > +
> > +       if (request->buf == priv_dev->zlp_buf)
> > +               cdns3_gadget_ep_free_request(&priv_ep->endpoint, request);
> > +}
> > +
> > +/**
> > + * cdns3_ep_run_transfer - start transfer on no-default endpoint hardware
> > + * @priv_ep: endpoint object
> > + *
> > + * Returns zero on success or negative value on failure
> > + */
> > +int cdns3_ep_run_transfer(struct cdns3_endpoint *priv_ep,
> > +                         struct usb_request *request)
> > +{
> > +       struct cdns3_device *priv_dev = priv_ep->cdns3_dev;
> > +       struct cdns3_request *priv_req;
> > +       struct cdns3_trb *trb;
> > +       dma_addr_t trb_dma;
> > +       int sg_iter = 0;
> > +       u32 first_pcs;
> > +       int  num_trb;
> > +       int address;
> > +       int pcs;
> > +
> > +       if (priv_ep->type == USB_ENDPOINT_XFER_ISOC)
> > +               num_trb = priv_ep->interval;
> > +       else
> > +               num_trb = request->num_sgs ? request->num_sgs : 1;
> > +
> > +       if (num_trb > priv_ep->free_trbs) {
> > +               priv_ep->flags |= EP_RING_FULL;
> > +               return -ENOBUFS;
> > +       }
> > +
> > +       priv_req = to_cdns3_request(request);
> > +       address = priv_ep->endpoint.desc->bEndpointAddress;
> > +
> > +       priv_ep->flags |= EP_PENDING_REQUEST;
> > +       trb_dma = request->dma;
> > +
> > +       /* must allocate buffer aligned to 8 */
> > +       if ((request->dma % 8)) {
> > +               if (request->length <= CDNS3_ALIGNED_BUF_SIZE) {
> > +                       memcpy(priv_ep->aligned_buff, request->buf,
> > +                              request->length);
> > +                       trb_dma = priv_ep->aligned_dma_addr;
> > +               } else {
> > +                       return -ENOMEM;
> > +               }
> > +       }
> > +
> > +       trb = priv_ep->trb_pool + priv_ep->enqueue;
> > +       priv_req->start_trb = priv_ep->enqueue;
> > +       priv_req->trb = trb;
> > +
> > +       /* prepare ring */
> > +       if ((priv_ep->enqueue + num_trb)  >= (priv_ep->num_trbs - 1)) {
> > +               /*updating C bt in  Link TRB before starting DMA*/
> > +               struct cdns3_trb *link_trb = priv_ep->trb_pool +
> > +                                            (priv_ep->num_trbs - 1);
> > +               link_trb->control = ((priv_ep->pcs) ? TRB_CYCLE : 0) |
> > +                                   TRB_TYPE(TRB_LINK) | TRB_CHAIN |
> > +                                   TRB_TOGGLE;
> > +       }
> > +
> > +       first_pcs = priv_ep->pcs ? TRB_CYCLE : 0;
> > +
> > +       do {
> > +       /* fill TRB */
> > +               trb->buffer = TRB_BUFFER(request->num_sgs == 0
> > +                               ? trb_dma : request->sg[sg_iter].dma_address);
> > +
> > +               trb->length = TRB_BURST_LEN(16) |
> > +                   TRB_LEN(request->num_sgs == 0 ?
> > +                               request->length : request->sg[sg_iter].length);
> > +
> > +               trb->control = TRB_TYPE(TRB_NORMAL);
> > +               pcs = priv_ep->pcs ? TRB_CYCLE : 0;
> > +
> > +               /*
> > +                * first trb should be prepared as last to avoid processing
> > +                *  transfer to early
> > +                */
> > +               if (sg_iter != 0)
> > +                       trb->control |= pcs;
> > +
> > +               if (priv_ep->type == USB_ENDPOINT_XFER_ISOC  && !priv_ep->dir) {
> > +                       trb->control |= TRB_IOC | TRB_ISP;
> > +               } else {
> > +                       /* for last element in TD or in SG list */
> > +                       if (sg_iter == (num_trb - 1) && sg_iter != 0)
> > +                               trb->control |= pcs | TRB_IOC | TRB_ISP;
> > +               }
> > +               ++sg_iter;
> > +               priv_req->end_trb = priv_ep->enqueue;
> > +               cdns3_ep_inc_enq(priv_ep);
> > +               trb = priv_ep->trb_pool + priv_ep->enqueue;
> > +       } while (sg_iter < num_trb);
> > +
> > +       trb = priv_req->trb;
> > +
> > +       /*
> > +        * Memory barrier = Cycle Bit must be set before trb->length  and
> > +        * trb->buffer fields.
> > +        */
> > +       wmb();
> > +
> > +       priv_req->flags |= REQUEST_PENDING;
> > +
> > +       /* give the TD to the consumer*/
> > +       if (sg_iter == 1)
> > +               trb->control |= first_pcs | TRB_IOC | TRB_ISP;
> > +       else
> > +               trb->control |= first_pcs;
> > +
> > +       trace_cdns3_prepare_trb(priv_ep, priv_req->trb);
> > +       trace_cdns3_ring(priv_ep);
> > +
> > +       /* arm transfer on selected endpoint */
> > +       cdns3_select_ep(priv_ep->cdns3_dev, address);
> > +
> > +       /*
> > +        * For DMULT mode we can set address to transfer ring only once after
> > +        * enabling endpoint.
> > +        */
> > +       if (priv_ep->flags & EP_UPDATE_EP_TRBADDR) {
> > +               writel(EP_TRADDR_TRADDR(priv_ep->trb_pool_dma),
> > +                      &priv_dev->regs->ep_traddr);
> > +               priv_ep->flags &= ~EP_UPDATE_EP_TRBADDR;
> > +       }
> > +
> > +       /*clearing TRBERR and EP_STS_DESCMIS before seting DRDY*/
> > +       writel(EP_STS_TRBERR | EP_STS_DESCMIS, &priv_dev->regs->ep_sts);
> > +       trace_cdns3_doorbell_epx(priv_ep->name,
> > +                                readl(&priv_dev->regs->ep_traddr));
> > +       writel(EP_CMD_DRDY, &priv_dev->regs->ep_cmd);
> > +
> > +       return 0;
> > +}
> > +
> > +void cdns3_set_hw_configuration(struct cdns3_device *priv_dev)
> > +{
> > +       struct cdns3_endpoint *priv_ep;
> > +       struct usb_ep *ep;
> > +       int result = 0;
> > +
> > +       if (priv_dev->hw_configured_flag)
> > +               return;
> > +
> > +       writel(USB_CONF_CFGSET, &priv_dev->regs->usb_conf);
> > +       writel(EP_CMD_ERDY | EP_CMD_REQ_CMPL, &priv_dev->regs->ep_cmd);
> > +
> > +       cdns3_set_register_bit(&priv_dev->regs->usb_conf,
> > +                              USB_CONF_U1EN | USB_CONF_U2EN);
> > +
> > +       /* wait until configuration set */
> > +       result = cdns3_handshake(&priv_dev->regs->usb_sts,
> > +                                USB_STS_CFGSTS_MASK, 1, 100);
> > +
> > +       priv_dev->hw_configured_flag = 1;
> > +       cdns3_allow_enable_l1(priv_dev, 1);
> > +
> > +       list_for_each_entry(ep, &priv_dev->gadget.ep_list, ep_list) {
> > +               if (ep->enabled) {
> > +                       priv_ep = ep_to_cdns3_ep(ep);
> > +                       cdns3_start_all_request(priv_dev, priv_ep);
> > +               }
> > +       }
> > +}
> > +
> > +/**
> > + * cdns3_request_handled - check whether request has been handled by DMA
> > + *
> > + * @priv_ep: extended endpoint object.
> > + * @priv_req: request object for checking
> > + *
> > + * Endpoint must be selected before invoking this function.
> > + *
> > + * Returns false if request has not been handled by DMA, else returns true.
> > + *
> > + * SR - start ring
> > + * ER -  end ring
> > + * DQ = priv_ep->dequeue - dequeue position
> > + * EQ = priv_ep->enqueue -  enqueue position
> > + * ST = priv_req->start_trb - index of first TRB in transfer ring
> > + * ET = priv_req->end_trb - index of last TRB in transfer ring
> > + * CI = current_index - index of processed TRB by DMA.
> > + *
> > + * As first step, function checks if cycle bit for priv_req->start_trb is
> > + * correct.
> > + *
> > + * some rules:
> > + * 1. priv_ep->dequeue never exceed current_index.
> > + * 2  priv_ep->enqueue never exceed priv_ep->dequeue
> > + *
> > + * Then We can split recognition into two parts:
> > + * Case 1 - priv_ep->dequeue < current_index
> > + *      SR ... EQ ... DQ ... CI ... ER
> > + *      SR ... DQ ... CI ... EQ ... ER
> > + *
> > + *      Request has been handled by DMA if ST and ET is between DQ and CI.
> > + *
> > + * Case 2 - priv_ep->dequeue > current_index
> > + * This situation take place when CI go through the LINK TRB at the end of
> > + * transfer ring.
> > + *      SR ... CI ... EQ ... DQ ... ER
> > + *
> > + *      Request has been handled by DMA if ET is less then CI or
> > + *      ET is greater or equal DQ.
> > + */
> > +static bool cdns3_request_handled(struct cdns3_endpoint *priv_ep,
> > +                                 struct cdns3_request *priv_req)
> > +{
> > +       struct cdns3_device *priv_dev = priv_ep->cdns3_dev;
> > +       struct cdns3_trb *trb = priv_req->trb;
> > +       int current_index = 0;
> > +       int handled = 0;
> > +
> > +       current_index = (readl(&priv_dev->regs->ep_traddr) -
> > +                        priv_ep->trb_pool_dma) / TRB_SIZE;
> > +
> > +       trb = &priv_ep->trb_pool[priv_req->start_trb];
> > +
> > +       if ((trb->control  & TRB_CYCLE) != priv_ep->ccs)
> > +               goto finish;
> > +
> > +       if (priv_ep->dequeue < current_index) {
> > +               if ((current_index == (priv_ep->num_trbs - 1)) &&
> > +                   !priv_ep->dequeue)
> > +                       goto finish;
> > +
> > +               if (priv_req->end_trb >= priv_ep->dequeue &&
> > +                   priv_req->end_trb < current_index)
> > +                       handled = 1;
> > +       } else if (priv_ep->dequeue  > current_index) {
> > +               if (priv_req->end_trb  < current_index ||
> > +                   priv_req->end_trb >= priv_ep->dequeue)
> > +                       handled = 1;
> > +       }
> > +
> > +finish:
> > +       trace_cdns3_request_handled(priv_req, current_index, handled);
> > +
> > +       return handled;
> > +}
> > +
> > +static void cdns3_transfer_completed(struct cdns3_device *priv_dev,
> > +                                    struct cdns3_endpoint *priv_ep)
> > +{
> > +       struct cdns3_request *priv_req;
> > +       struct usb_request *request;
> > +       struct cdns3_trb *trb;
> > +       int current_trb;
> > +
> > +       while (!list_empty(&priv_ep->pending_req_list)) {
> > +               request = cdns3_next_request(&priv_ep->pending_req_list);
> > +               priv_req = to_cdns3_request(request);
> > +
> > +               if (!cdns3_request_handled(priv_ep, priv_req))
> > +                       return;
> > +
> > +               if (request->dma % 8 && priv_ep->dir == USB_DIR_OUT)
> > +                       memcpy(request->buf, priv_ep->aligned_buff,
> > +                              request->length);
> > +
> > +               trb = priv_ep->trb_pool + priv_ep->dequeue;
> > +               trace_cdns3_complete_trb(priv_ep, trb);
> > +
> > +               if (trb != priv_req->trb)
> > +                       dev_warn(priv_dev->dev,
> > +                                "request_trb=0x%p, queue_trb=0x%p\n",
> > +                                priv_req->trb, trb);
> > +
> > +               request->actual = TRB_LEN(le32_to_cpu(trb->length));
> > +               current_trb = priv_req->start_trb;
> > +
> > +               while (current_trb != priv_req->end_trb) {
> > +                       cdns3_ep_inc_deq(priv_ep);
> > +                       current_trb = priv_ep->dequeue;
> > +               }
> > +
> > +               cdns3_ep_inc_deq(priv_ep);
> > +               cdns3_gadget_giveback(priv_ep, priv_req, 0);
> > +       }
> > +       priv_ep->flags &= ~EP_PENDING_REQUEST;
> > +}
> > +
> > +/**
> > + * cdns3_descmissing_packet - handles descriptor missing event.
> > + * @priv_dev: extended gadget object
> > + *
> > + * WA1: Controller for OUT endpoints has shared on-chip buffers for all incoming
> > + * packets, including ep0out. It's FIFO buffer, so packets must be handle by DMA
> > + * in correct order. If the first packet in the buffer will not be handled,
> > + * then the following packets directed for other endpoints and  functions
> > + * will be blocked.
> > + * Additionally the packets directed to one endpoint can block entire on-chip
> > + * buffers. In this case transfer to other endpoints also will blocked.
> > + *
> > + * To resolve this issue after raising the descriptor missing interrupt
> > + * driver prepares internal usb_request object and use it to arm DMA transfer.
> > + *
> > + * The problematic situation was observed in case when endpoint has been enabled
> > + * but no usb_request were queued. Driver try detects such endpoints and will
> > + * use this workaround only for these endpoint.
> > + *
> > + * Driver use limited number of buffer. This number can be set by macro
> > + * CDNS_WA1_NUM_BUFFERS.
> > + *
> > + * Such blocking situation was observed on ACM gadget. For this function
> > + * host send OUT data packet but ACM function is not prepared for this packet.
> > + *
> > + * It's limitation of controller but maybe this issues should be fixed in
> > + * function driver.
> > + */
> > +static int cdns3_descmissing_packet(struct cdns3_endpoint *priv_ep)
> > +{
> > +       struct cdns3_request *priv_req;
> > +       struct usb_request *request;
> > +
> > +       if (priv_ep->flags & EP_QUIRK_EXTRA_BUF_DET) {
> > +               priv_ep->flags &= ~EP_QUIRK_EXTRA_BUF_DET;
> > +               priv_ep->flags |= EP_QUIRK_EXTRA_BUF_EN;
> > +       }
> > +
> > +       request = cdns3_gadget_ep_alloc_request(&priv_ep->endpoint,
> > +                                               GFP_ATOMIC);
> > +       if (!request)
> > +               return -ENOMEM;
> > +
> > +       priv_req = to_cdns3_request(request);
> > +       priv_req->flags |= REQUEST_INTERNAL;
> > +
> > +       /* if this field is still assigned it indicate that transfer related
> > +        * with this request has not been finished yet. Driver in this
> > +        * case simply allocate next request and assign flag REQUEST_INTERNAL_CH
> > +        * flag to previous one. It will indicate that current request is
> > +        * part of the previous one.
> > +        */
> > +       if (priv_ep->descmis_req)
> > +               priv_ep->descmis_req->flags |= REQUEST_INTERNAL_CH;
> > +
> > +       priv_req->request.buf = kzalloc(CDNS3_DESCMIS_BUF_SIZE,
> > +                                       GFP_ATOMIC);
> > +       if (!priv_req) {
> > +               cdns3_gadget_ep_free_request(&priv_ep->endpoint, request);
> > +               return -ENOMEM;
> > +       }
> > +
> > +       priv_req->request.length = CDNS3_DESCMIS_BUF_SIZE;
> > +       priv_ep->descmis_req = priv_req;
> > +
> > +       __cdns3_gadget_ep_queue(&priv_ep->endpoint,
> > +                               &priv_ep->descmis_req->request,
> > +                               GFP_ATOMIC);
> > +
> > +       return 0;
> > +}
> > +
> > +/**
> > + * cdns3_check_ep_interrupt_proceed - Processes interrupt related to endpoint
> > + * @priv_ep: endpoint object
> > + *
> > + * Returns 0
> > + */
> > +static int cdns3_check_ep_interrupt_proceed(struct cdns3_endpoint *priv_ep)
> > +{
> > +       struct cdns3_device *priv_dev = priv_ep->cdns3_dev;
> > +       u32 ep_sts_reg;
> > +
> > +       cdns3_select_ep(priv_dev, priv_ep->endpoint.address);
> > +
> > +       trace_cdns3_epx_irq(priv_dev, priv_ep);
> > +
> > +       ep_sts_reg = readl(&priv_dev->regs->ep_sts);
> > +       writel(ep_sts_reg, &priv_dev->regs->ep_sts);
> > +
> > +       if ((ep_sts_reg & EP_STS_IOC) || (ep_sts_reg & EP_STS_ISP)) {
> > +               if (priv_ep->flags & EP_QUIRK_EXTRA_BUF_EN) {
> > +                       if (ep_sts_reg & EP_STS_ISP)
> > +                               priv_ep->flags |= EP_QUIRK_END_TRANSFER;
> > +                       else
> > +                               priv_ep->flags &= ~EP_QUIRK_END_TRANSFER;
> > +               }
> > +               cdns3_transfer_completed(priv_dev, priv_ep);
> > +       }
> > +
> > +       /*
> > +        * For isochronous transfer driver completes request on IOC or on
> > +        * TRBERR. IOC appears only when device receive OUT data packet.
> > +        * If host disable stream or lost some packet then the only way to
> > +        * finish all queued transfer is to do it on TRBERR event.
> > +        */
> > +       if ((ep_sts_reg & EP_STS_TRBERR) &&
> > +           priv_ep->type == USB_ENDPOINT_XFER_ISOC)
> > +               cdns3_transfer_completed(priv_dev, priv_ep);
> > +
> > +       /*
> > +        * WA1: this condition should only be meet when
> > +        * priv_ep->flags & EP_QUIRK_EXTRA_BUF_DET or
> > +        * priv_ep->flags & EP_QUIRK_EXTRA_BUF_EN.
> > +        * In other cases this interrupt will be disabled/
> > +        */
> > +       if (ep_sts_reg & EP_STS_DESCMIS) {
> > +               int err;
> > +
> > +               err = cdns3_descmissing_packet(priv_ep);
> > +               if (err)
> > +                       dev_err(priv_dev->dev,
> > +                               "Failed: No sufficient memory for DESCMIS\n");
> > +       }
> > +
> > +       return 0;
> > +}
> > +
> > +/**
> > + * cdns3_check_usb_interrupt_proceed - Processes interrupt related to device
> > + * @priv_dev: extended gadget object
> > + * @usb_ists: bitmap representation of device's reported interrupts
> > + * (usb_ists register value)
> > + */
> > +static void cdns3_check_usb_interrupt_proceed(struct cdns3_device *priv_dev,
> > +                                             u32 usb_ists)
> > +{
> > +       int speed = 0;
> > +
> > +       trace_cdns3_usb_irq(priv_dev, usb_ists);
> > +       /* Connection detected */
> > +       if (usb_ists & (USB_ISTS_CON2I | USB_ISTS_CONI)) {
> > +               speed = cdns3_get_speed(priv_dev);
> > +               priv_dev->gadget.speed = speed;
> > +               usb_gadget_set_state(&priv_dev->gadget, USB_STATE_POWERED);
> > +               cdns3_ep0_config(priv_dev);
> > +       }
> > +
> > +       /* Disconnection detected */
> > +       if (usb_ists & (USB_ISTS_DIS2I | USB_ISTS_DISI)) {
> > +               if (priv_dev->gadget_driver &&
> > +                   priv_dev->gadget_driver->disconnect) {
> > +                       spin_unlock(&priv_dev->lock);
> > +                       priv_dev->gadget_driver->disconnect(&priv_dev->gadget);
> > +                       spin_lock(&priv_dev->lock);
> > +               }
> > +
> > +               priv_dev->gadget.speed = USB_SPEED_UNKNOWN;
> > +               usb_gadget_set_state(&priv_dev->gadget, USB_STATE_NOTATTACHED);
> > +               cdns3_hw_reset_eps_config(priv_dev);
> > +       }
> > +
> > +       /* reset*/
> > +       if (usb_ists & (USB_ISTS_UWRESI | USB_ISTS_UHRESI | USB_ISTS_U2RESI)) {
> > +               /*read again to check the actuall speed*/
> > +               speed = cdns3_get_speed(priv_dev);
> > +               usb_gadget_set_state(&priv_dev->gadget, USB_STATE_DEFAULT);
> > +               priv_dev->gadget.speed = speed;
> > +               cdns3_hw_reset_eps_config(priv_dev);
> > +               cdns3_ep0_config(priv_dev);
> > +       }
> > +}
> > +
> > +/**
> > + * cdns3_device_irq_handler- interrupt handler for device part of controller
> > + *
> > + * @irq: irq number for cdns3 core device
> > + * @data: structure of cdns3
> > + *
> > + * Returns IRQ_HANDLED or IRQ_NONE
> > + */
> > +static irqreturn_t cdns3_device_irq_handler(int irq, void *data)
> > +{
> > +       struct cdns3_device *priv_dev;
> > +       struct cdns3 *cdns = data;
> > +       irqreturn_t ret = IRQ_NONE;
> > +       unsigned long flags;
> > +       u32 reg;
> > +
> > +       priv_dev = cdns->gadget_dev;
> > +       spin_lock_irqsave(&priv_dev->lock, flags);
> > +
> > +       /* check USB device interrupt */
> > +       reg = readl(&priv_dev->regs->usb_ists);
> > +       writel(reg, &priv_dev->regs->usb_ists);
> > +
> > +       if (reg) {
> > +               cdns3_check_usb_interrupt_proceed(priv_dev, reg);
> > +               ret = IRQ_HANDLED;
> > +       }
> > +
> > +       /* check endpoint interrupt */
> > +       reg = readl(&priv_dev->regs->ep_ists);
> > +
> > +       if (reg) {
> > +               reg = ~reg & readl(&priv_dev->regs->ep_ien);
> > +               /* mask deferred interrupt. */
> > +               writel(reg, &priv_dev->regs->ep_ien);
> > +               ret = IRQ_WAKE_THREAD;
> > +       }
> > +
> > +       spin_unlock_irqrestore(&priv_dev->lock, flags);
> > +       return ret;
> > +}
> > +
> > +/**
> > + * cdns3_device_thread_irq_handler- interrupt handler for device part
> > + * of controller
> > + *
> > + * @irq: irq number for cdns3 core device
> > + * @data: structure of cdns3
> > + *
> > + * Returns IRQ_HANDLED or IRQ_NONE
> > + */
> > +static irqreturn_t cdns3_device_thread_irq_handler(int irq, void *data)
> > +{
> > +       struct cdns3_device *priv_dev;
> > +       struct cdns3 *cdns = data;
> > +       irqreturn_t ret = IRQ_NONE;
> > +       unsigned long flags;
> > +       u32 ep_ien;
> > +       int bit;
> > +       u32 reg;
> > +
> > +       priv_dev = cdns->gadget_dev;
> > +       spin_lock_irqsave(&priv_dev->lock, flags);
> > +
> > +       reg = readl(&priv_dev->regs->ep_ists);
> > +       ep_ien = reg;
> > +
> > +       /* handle default endpoint OUT */
> > +       if (reg & EP_ISTS_EP_OUT0) {
> > +               cdns3_check_ep0_interrupt_proceed(priv_dev, USB_DIR_OUT);
> > +               ret = IRQ_HANDLED;
> > +       }
> > +
> > +       /* handle default endpoint IN */
> > +       if (reg & EP_ISTS_EP_IN0) {
> > +               cdns3_check_ep0_interrupt_proceed(priv_dev, USB_DIR_IN);
> > +               ret = IRQ_HANDLED;
> > +       }
> > +
> > +       /* check if interrupt from non default endpoint, if no exit */
> > +       reg &= ~(EP_ISTS_EP_OUT0 | EP_ISTS_EP_IN0);
> > +       if (!reg)
> > +               goto irqend;
> > +
> > +       for_each_set_bit(bit, (unsigned long *)&reg,
> > +                        sizeof(u32) * BITS_PER_BYTE) {
> > +               cdns3_check_ep_interrupt_proceed(priv_dev->eps[bit]);
> > +               ret = IRQ_HANDLED;
> > +       }
> > +
> > +irqend:
> > +       ep_ien |= readl(&priv_dev->regs->ep_ien);
> > +       /* Unmask all handled EP interrupts */
> > +       writel(ep_ien, &priv_dev->regs->ep_ien);
> > +       spin_unlock_irqrestore(&priv_dev->lock, flags);
> > +       return ret;
> > +}
> > +
> > +/**
> > + * cdns3_ep_onchip_buffer_reserve - Try to reserve onchip buf for EP
> > + *
> > + * The real reservation will occur during write to EP_CFG register,
> > + * this function is used to check if the 'size' reservation is allowed.
> > + *
> > + * @priv_dev: extended gadget object
> > + * @size: the size (KB) for EP would like to allocate
> > + *
> > + * Return 0 if the required size can met or negative value on failure
> > + */
> > +static int cdns3_ep_onchip_buffer_reserve(struct cdns3_device *priv_dev,
> > +                                         int size)
> > +{
> > +       u32 onchip_mem;
> > +
> > +       priv_dev->onchip_mem_allocated_size += size;
> > +
> > +       onchip_mem = USB_CAP2_ACTUAL_MEM_SIZE(readl(&priv_dev->regs->usb_cap2));
> > +       if (!onchip_mem)
> > +               onchip_mem = 256;
> > +
> > +       /* 2KB is reserved for EP0*/
> > +       onchip_mem -= 2;
> > +       if (priv_dev->onchip_mem_allocated_size > onchip_mem) {
> > +               priv_dev->onchip_mem_allocated_size -= size;
> > +               return -EPERM;
> > +       }
> > +
> > +       return 0;
> > +}
> > +
> > +/**
> > + * cdns3_ep_config Configure hardware endpoint
> > + * @priv_ep: extended endpoint object
> > + */
> > +void cdns3_ep_config(struct cdns3_endpoint *priv_ep)
> > +{
> > +       bool is_iso_ep = (priv_ep->type == USB_ENDPOINT_XFER_ISOC);
> > +       struct cdns3_device *priv_dev = priv_ep->cdns3_dev;
> > +       u32 bEndpointAddress = priv_ep->num | priv_ep->dir;
> > +       u32 max_packet_size = 0;
> > +       u32 ep_cfg = 0;
> > +       int ret;
> > +
> > +       switch (priv_ep->type) {
> > +       case USB_ENDPOINT_XFER_INT:
> > +               ep_cfg = EP_CFG_EPTYPE(USB_ENDPOINT_XFER_INT);
> > +               break;
> > +       case USB_ENDPOINT_XFER_BULK:
> > +               ep_cfg = EP_CFG_EPTYPE(USB_ENDPOINT_XFER_BULK);
> > +               break;
> > +       default:
> > +               ep_cfg = EP_CFG_EPTYPE(USB_ENDPOINT_XFER_ISOC);
> > +       }
> > +
> > +       switch (priv_dev->gadget.speed) {
> > +       case USB_SPEED_FULL:
> > +               max_packet_size = is_iso_ep ? 1023 : 64;
> > +               break;
> > +       case USB_SPEED_HIGH:
> > +               max_packet_size = is_iso_ep ? 1024 : 512;
> > +               break;
> > +       case USB_SPEED_SUPER:
> > +               max_packet_size = 1024;
> > +               break;
> > +       default:
> > +               /* all other speed are not supported */
> > +               return;
> > +       }
> > +
> > +       ret = cdns3_ep_onchip_buffer_reserve(priv_dev, CDNS3_EP_BUF_SIZE);
> > +       if (ret) {
> > +               dev_err(priv_dev->dev, "onchip mem is full, ep is invalid\n");
> > +               return;
> > +       }
> > +
> > +       ep_cfg |= EP_CFG_MAXPKTSIZE(max_packet_size) |
> > +                 EP_CFG_BUFFERING(CDNS3_EP_BUF_SIZE - 1) |
> > +                 EP_CFG_MAXBURST(priv_ep->endpoint.maxburst);
> > +
> > +       cdns3_select_ep(priv_dev, bEndpointAddress);
> > +       writel(ep_cfg, &priv_dev->regs->ep_cfg);
> > +
> > +       dev_dbg(priv_dev->dev, "Configure %s: with val %08x\n",
> > +               priv_ep->name, ep_cfg);
> > +}
> > +
> > +/* Find correct direction for HW endpoint according to description */
> > +static int cdns3_ep_dir_is_correct(struct usb_endpoint_descriptor *desc,
> > +                                  struct cdns3_endpoint *priv_ep)
> > +{
> > +       return (priv_ep->endpoint.caps.dir_in && usb_endpoint_dir_in(desc)) ||
> > +              (priv_ep->endpoint.caps.dir_out && usb_endpoint_dir_out(desc));
> > +}
> > +
> > +static struct
> > +cdns3_endpoint *cdns3_find_available_ep(struct cdns3_device *priv_dev,
> > +                                       struct usb_endpoint_descriptor *desc)
> > +{
> > +       struct usb_ep *ep;
> > +       struct cdns3_endpoint *priv_ep;
> > +
> > +       list_for_each_entry(ep, &priv_dev->gadget.ep_list, ep_list) {
> > +               unsigned long num;
> > +               int ret;
> > +               /* ep name pattern likes epXin or epXout */
> > +               char c[2] = {ep->name[2], '\0'};
> > +
> > +               ret = kstrtoul(c, 10, &num);
> > +               if (ret)
> > +                       return ERR_PTR(ret);
> > +
> > +               priv_ep = ep_to_cdns3_ep(ep);
> > +               if (cdns3_ep_dir_is_correct(desc, priv_ep)) {
> > +                       if (!(priv_ep->flags & EP_CLAIMED)) {
> > +                               priv_ep->num  = num;
> > +                               return priv_ep;
> > +                       }
> > +               }
> > +       }
> > +
> > +       return ERR_PTR(-ENOENT);
> > +}
> > +
> > +/*
> > + *  Cadence IP has one limitation that all endpoints must be configured
> > + * (Type & MaxPacketSize) before setting configuration through hardware
> > + * register, it means we can't change endpoints configuration after
> > + * set_configuration.
> > + *
> > + * This function set EP_CLAIMED flag which is added when the gadget driver
> > + * uses usb_ep_autoconfig to configure specific endpoint;
> > + * When the udc driver receives set_configurion request,
> > + * it goes through all claimed endpoints, and configure all endpoints
> > + * accordingly.
> > + *
> > + * At usb_ep_ops.enable/disable, we only enable and disable endpoint through
> > + * ep_cfg register which can be changed after set_configuration, and do
> > + * some software operation accordingly.
> > + */
> > +static struct
> > +usb_ep *cdns3_gadget_match_ep(struct usb_gadget *gadget,
> > +                             struct usb_endpoint_descriptor *desc,
> > +                             struct usb_ss_ep_comp_descriptor *comp_desc)
> > +{
> > +       struct cdns3_device *priv_dev = gadget_to_cdns3_device(gadget);
> > +       struct cdns3_endpoint *priv_ep;
> > +       unsigned long flags;
> > +
> > +       priv_ep = cdns3_find_available_ep(priv_dev, desc);
> > +       if (IS_ERR(priv_ep)) {
> > +               dev_err(priv_dev->dev, "no available ep\n");
> > +               return NULL;
> > +       }
> > +
> > +       dev_dbg(priv_dev->dev, "match endpoint: %s\n", priv_ep->name);
> > +
> > +       spin_lock_irqsave(&priv_dev->lock, flags);
> > +       priv_ep->endpoint.desc = desc;
> > +       priv_ep->dir  = usb_endpoint_dir_in(desc) ? USB_DIR_IN : USB_DIR_OUT;
> > +       priv_ep->type = usb_endpoint_type(desc);
> > +       priv_ep->flags |= EP_CLAIMED;
> > +       spin_unlock_irqrestore(&priv_dev->lock, flags);
> > +       return &priv_ep->endpoint;
> > +}
> > +
> > +/**
> > + * cdns3_gadget_ep_alloc_request Allocates request
> > + * @ep: endpoint object associated with request
> > + * @gfp_flags: gfp flags
> > + *
> > + * Returns allocated request address, NULL on allocation error
> > + */
> > +struct usb_request *cdns3_gadget_ep_alloc_request(struct usb_ep *ep,
> > +                                                 gfp_t gfp_flags)
> > +{
> > +       struct cdns3_endpoint *priv_ep = ep_to_cdns3_ep(ep);
> > +       struct cdns3_request *priv_req;
> > +
> > +       priv_req = kzalloc(sizeof(*priv_req), gfp_flags);
> > +       if (!priv_req)
> > +               return NULL;
> > +
> > +       priv_req->priv_ep = priv_ep;
> > +
> > +       trace_cdns3_alloc_request(priv_req);
> > +       return &priv_req->request;
> > +}
> > +
> > +/**
> > + * cdns3_gadget_ep_free_request Free memory occupied by request
> > + * @ep: endpoint object associated with request
> > + * @request: request to free memory
> > + */
> > +void cdns3_gadget_ep_free_request(struct usb_ep *ep,
> > +                                 struct usb_request *request)
> > +{
> > +       struct cdns3_request *priv_req = to_cdns3_request(request);
> > +
> > +       trace_cdns3_free_request(priv_req);
> > +       kfree(priv_req);
> > +}
> > +
> > +/**
> > + * cdns3_gadget_ep_enable Enable endpoint
> > + * @ep: endpoint object
> > + * @desc: endpoint descriptor
> > + *
> > + * Returns 0 on success, error code elsewhere
> > + */
> > +static int cdns3_gadget_ep_enable(struct usb_ep *ep,
> > +                                 const struct usb_endpoint_descriptor *desc)
> > +{
> > +       struct cdns3_endpoint *priv_ep;
> > +       struct cdns3_device *priv_dev;
> > +       u32 reg = EP_STS_EN_TRBERREN;
> > +       u32 bEndpointAddress;
> > +       unsigned long flags;
> > +       int ret;
> > +
> > +       priv_ep = ep_to_cdns3_ep(ep);
> > +       priv_dev = priv_ep->cdns3_dev;
> > +
> > +       if (!ep || !desc || desc->bDescriptorType != USB_DT_ENDPOINT) {
> > +               dev_dbg(priv_dev->dev, "usbss: invalid parameters\n");
> > +               return -EINVAL;
> > +       }
> > +
> > +       if (!desc->wMaxPacketSize) {
> > +               dev_err(priv_dev->dev, "usbss: missing wMaxPacketSize\n");
> > +               return -EINVAL;
> > +       }
> > +
> > +       if (dev_WARN_ONCE(priv_dev->dev, priv_ep->flags & EP_ENABLED,
> > +                         "%s is already enabled\n", priv_ep->name))
> > +               return 0;
> > +
> > +       spin_lock_irqsave(&priv_dev->lock, flags);
> > +
> > +       priv_ep->endpoint.desc = desc;
> > +       priv_ep->type = usb_endpoint_type(desc);
> > +       priv_ep->interval = desc->bInterval ? BIT(desc->bInterval - 1) : 0;
> > +
> > +       if (priv_ep->interval > ISO_MAX_INTERVAL &&
> > +           priv_ep->type == USB_ENDPOINT_XFER_ISOC) {
> > +               dev_err(priv_dev->dev, "Driver is limited to %d period\n",
> > +                       ISO_MAX_INTERVAL);
> > +
> > +               ret =  -EINVAL;
> > +               goto exit;
> > +       }
> > +
> > +       ret = cdns3_allocate_trb_pool(priv_ep);
> > +
> > +       if (ret)
> > +               goto exit;
> > +
> > +       bEndpointAddress = priv_ep->num | priv_ep->dir;
> > +       cdns3_select_ep(priv_dev, bEndpointAddress);
> > +
> > +       trace_cdns3_gadget_ep_enable(priv_ep);
> > +
> > +       writel(EP_CMD_EPRST, &priv_dev->regs->ep_cmd);
> > +
> > +       ret = cdns3_handshake(&priv_dev->regs->ep_cmd,
> > +                             EP_CMD_CSTALL | EP_CMD_EPRST, 0, 100);
> > +
> > +       /* enable interrupt for selected endpoint */
> > +       cdns3_set_register_bit(&priv_dev->regs->ep_ien,
> > +                              BIT(cdns3_ep_addr_to_index(bEndpointAddress)));
> > +       /*
> > +        * WA1: Set flag for all not ISOC OUT endpoints. If this flag is set
> > +        * driver try to detect whether endpoint need additional internal
> > +        * buffer for unblocking on-chip FIFO buffer. This flag will be cleared
> > +        * if before first DESCMISS interrupt the DMA will be armed.
> > +        */
> > +       if (!priv_ep->dir && priv_ep->type != USB_ENDPOINT_XFER_ISOC) {
> > +               priv_ep->flags |= EP_QUIRK_EXTRA_BUF_DET;
> > +               reg |= EP_STS_EN_DESCMISEN;
> > +       }
> > +
> > +       writel(reg, &priv_dev->regs->ep_sts_en);
> > +
> > +       cdns3_set_register_bit(&priv_dev->regs->ep_cfg, EP_CFG_ENABLE);
> > +
> > +       ep->desc = desc;
> > +       priv_ep->flags &= ~(EP_PENDING_REQUEST | EP_STALL |
> > +                           EP_QUIRK_EXTRA_BUF_EN);
> > +       priv_ep->flags |= EP_ENABLED | EP_UPDATE_EP_TRBADDR;
> > +       priv_ep->enqueue = 0;
> > +       priv_ep->dequeue = 0;
> > +       reg = readl(&priv_dev->regs->ep_sts);
> > +       priv_ep->pcs = !!EP_STS_CCS(reg);
> > +       priv_ep->ccs = !!EP_STS_CCS(reg);
> > +       /* one TRB is reserved for link TRB used in DMULT mode*/
> > +       priv_ep->free_trbs = priv_ep->num_trbs - 1;
> > +exit:
> > +       spin_unlock_irqrestore(&priv_dev->lock, flags);
> > +
> > +       return ret;
> > +}
> > +
> > +/**
> > + * cdns3_gadget_ep_disable Disable endpoint
> > + * @ep: endpoint object
> > + *
> > + * Returns 0 on success, error code elsewhere
> > + */
> > +static int cdns3_gadget_ep_disable(struct usb_ep *ep)
> > +{
> > +       struct cdns3_endpoint *priv_ep;
> > +       struct cdns3_request *priv_req;
> > +       struct cdns3_device *priv_dev;
> > +       struct usb_request *request;
> > +       unsigned long flags;
> > +       int ret = 0;
> > +       u32 ep_cfg;
> > +
> > +       if (!ep) {
> > +               pr_err("usbss: invalid parameters\n");
> > +               return -EINVAL;
> > +       }
> > +
> > +       priv_ep = ep_to_cdns3_ep(ep);
> > +       priv_dev = priv_ep->cdns3_dev;
> > +
> > +       if (dev_WARN_ONCE(priv_dev->dev, !(priv_ep->flags & EP_ENABLED),
> > +                         "%s is already disabled\n", priv_ep->name))
> > +               return 0;
> > +
> > +       spin_lock_irqsave(&priv_dev->lock, flags);
> > +
> > +       trace_cdns3_gadget_ep_disable(priv_ep);
> > +
> > +       cdns3_select_ep(priv_dev, ep->desc->bEndpointAddress);
> > +       ret = cdns3_data_flush(priv_ep);
> > +
> > +       ep_cfg = readl(&priv_dev->regs->ep_cfg);
> > +       ep_cfg &= ~EP_CFG_ENABLE;
> > +       writel(ep_cfg, &priv_dev->regs->ep_cfg);
> > +
> > +       while (!list_empty(&priv_ep->pending_req_list)) {
> > +               request = cdns3_next_request(&priv_ep->pending_req_list);
> > +
> > +               cdns3_gadget_giveback(priv_ep, to_cdns3_request(request),
> > +                                     -ESHUTDOWN);
> > +       }
> > +
> > +       while (!list_empty(&priv_ep->descmiss_req_list)) {
> > +               priv_req = cdns3_next_priv_request(&priv_ep->descmiss_req_list);
> > +
> > +               kfree(priv_req->request.buf);
> > +               cdns3_gadget_ep_free_request(&priv_ep->endpoint,
> > +                                            &priv_req->request);
> > +       }
> > +
> > +       while (!list_empty(&priv_ep->deferred_req_list)) {
> > +               request = cdns3_next_request(&priv_ep->deferred_req_list);
> > +
> > +               cdns3_gadget_giveback(priv_ep, to_cdns3_request(request),
> > +                                     -ESHUTDOWN);
> > +       }
> > +
> > +       priv_ep->descmis_req = NULL;
> > +
> > +       ep->desc = NULL;
> > +       priv_ep->flags &= ~EP_ENABLED;
> > +
> > +       spin_unlock_irqrestore(&priv_dev->lock, flags);
> > +
> > +       return ret;
> > +}
> > +
> > +/**
> > + * cdns3_gadget_ep_queue Transfer data on endpoint
> > + * @ep: endpoint object
> > + * @request: request object
> > + * @gfp_flags: gfp flags
> > + *
> > + * Returns 0 on success, error code elsewhere
> > + */
> > +static int __cdns3_gadget_ep_queue(struct usb_ep *ep,
> > +                                  struct usb_request *request,
> > +                                  gfp_t gfp_flags)
> > +{
> > +       struct cdns3_endpoint *priv_ep = ep_to_cdns3_ep(ep);
> > +       struct cdns3_device *priv_dev = priv_ep->cdns3_dev;
> > +       struct cdns3_request *priv_req;
> > +       int deferred = 0;
> > +       int ret = 0;
> > +
> > +       request->actual = 0;
> > +       request->status = -EINPROGRESS;
> > +       priv_req = to_cdns3_request(request);
> > +       trace_cdns3_ep_queue(priv_req);
> > +
> > +       /*
> > +        * WA1: if transfer was queued before DESCMISS appear than we
> > +        * can disable handling of DESCMISS interrupt. Driver assumes that it
> > +        * can disable special treatment for this endpoint.
> > +        */
> > +       if (priv_ep->flags & EP_QUIRK_EXTRA_BUF_DET) {
> > +               u32 reg;
> > +
> > +               cdns3_select_ep(priv_dev, priv_ep->num | priv_ep->dir);
> > +               reg = readl(&priv_dev->regs->ep_sts_en);
> > +               priv_ep->flags &= ~EP_QUIRK_EXTRA_BUF_DET;
> > +               reg &= EP_STS_EN_DESCMISEN;
> > +               writel(reg, &priv_dev->regs->ep_sts_en);
> > +       }
> > +
> > +       /* WA1 */
> > +       if (priv_ep->flags & EP_QUIRK_EXTRA_BUF_EN) {
> > +               u8 pending_empty = list_empty(&priv_ep->pending_req_list);
> > +               u8 descmiss_empty = list_empty(&priv_ep->descmiss_req_list);
> > +
> > +               /*
> > +                *  DESCMISS transfer has been finished, so data will be
> > +                *  directly copied from internal allocated usb_request
> > +                *  objects.
> > +                */
> > +               if (pending_empty && !descmiss_empty &&
> > +                   !(priv_req->flags & REQUEST_INTERNAL)) {
> > +                       cdns3_descmiss_copy_data(priv_ep, request);
> > +                       list_add_tail(&request->list,
> > +                                     &priv_ep->pending_req_list);
> > +                       cdns3_gadget_giveback(priv_ep, priv_req,
> > +                                             request->status);
> > +                       return ret;
> > +               }
> > +
> > +               /*
> > +                * WA1 driver will wait for completion DESCMISS transfer,
> > +                * before starts new, not DESCMISS transfer.
> > +                */
> > +               if (!pending_empty && !descmiss_empty)
> > +                       deferred = 1;
> > +
> > +               if (priv_req->flags & REQUEST_INTERNAL)
> > +                       list_add_tail(&priv_req->list,
> > +                                     &priv_ep->descmiss_req_list);
> > +       }
> > +
> > +       ret = usb_gadget_map_request_by_dev(priv_dev->sysdev, request,
> > +                                           usb_endpoint_dir_in(ep->desc));
> > +       if (ret)
> > +               return ret;
> > +
> > +       /*
> > +        * If hardware endpoint configuration has not been set yet then
> > +        * just queue request in deferred list. Transfer will be started in
> > +        * cdns3_set_hw_configuration.
> > +        */
> > +       if (!priv_dev->hw_configured_flag)
> > +               deferred = 1;
> > +       else
> > +               ret = cdns3_ep_run_transfer(priv_ep, request);
> > +
> > +       if (ret || deferred)
> > +               list_add_tail(&request->list, &priv_ep->deferred_req_list);
> > +       else
> > +               list_add_tail(&request->list, &priv_ep->pending_req_list);
> > +
> > +       return ret;
> > +}
> > +
> > +static int cdns3_gadget_ep_queue(struct usb_ep *ep, struct usb_request *request,
> > +                                gfp_t gfp_flags)
> > +{
> > +       struct usb_request *zlp_request;
> > +       struct cdns3_endpoint *priv_ep;
> > +       struct cdns3_device *priv_dev;
> > +       unsigned long flags;
> > +       int ret;
> > +
> > +       if (!request || !ep)
> > +               return -EINVAL;
> > +
> > +       priv_ep = ep_to_cdns3_ep(ep);
> > +       priv_dev = priv_ep->cdns3_dev;
> > +
> > +       spin_lock_irqsave(&priv_dev->lock, flags);
> > +
> > +       ret = __cdns3_gadget_ep_queue(ep, request, gfp_flags);
> > +
> > +       if (ret == 0 && request->zero && request->length &&
> > +           (request->length % ep->maxpacket == 0)) {
> > +               struct cdns3_request *priv_req;
> > +
> > +               zlp_request = cdns3_gadget_ep_alloc_request(ep, GFP_ATOMIC);
> > +               zlp_request->buf = priv_dev->zlp_buf;
> > +               zlp_request->length = 0;
> > +
> > +               priv_req = to_cdns3_request(zlp_request);
> > +               priv_req->flags |= REQUEST_ZLP;
> > +
> > +               dev_dbg(priv_dev->dev, "Queuing ZLP for endpoint: %s\n",
> > +                       priv_ep->name);
> > +               ret = __cdns3_gadget_ep_queue(ep, zlp_request, gfp_flags);
> > +       }
> > +
> > +       spin_unlock_irqrestore(&priv_dev->lock, flags);
> > +       return ret;
> > +}
> > +
> > +/**
> > + * cdns3_gadget_ep_dequeue Remove request from transfer queue
> > + * @ep: endpoint object associated with request
> > + * @request: request object
> > + *
> > + * Returns 0 on success, error code elsewhere
> > + */
> > +int cdns3_gadget_ep_dequeue(struct usb_ep *ep,
> > +                           struct usb_request *request)
> > +{
> > +       struct cdns3_endpoint *priv_ep = ep_to_cdns3_ep(ep);
> > +       struct cdns3_device *priv_dev = priv_ep->cdns3_dev;
> > +       struct usb_request *req, *req_temp;
> > +       struct cdns3_request *priv_req;
> > +       unsigned long flags;
> > +       int ret = 0;
> > +
> > +       if (!ep || !request || !ep->desc)
> > +               return -EINVAL;
> > +
> > +       spin_lock_irqsave(&priv_dev->lock, flags);
> > +
> > +       priv_req = to_cdns3_request(request);
> > +
> > +       trace_cdns3_ep_dequeue(priv_req);
> > +
> > +       cdns3_select_ep(priv_dev, ep->desc->bEndpointAddress);
> > +
> > +       list_for_each_entry_safe(req, req_temp, &priv_ep->pending_req_list,
> > +                                list) {
> > +               if (request == req)
> > +                       goto found;
> > +       }
> > +
> > +       list_for_each_entry_safe(req, req_temp, &priv_ep->deferred_req_list,
> > +                                list) {
> > +               if (request == req)
> > +                       goto found;
> > +       }
> > +
> > +found:
> > +       if (request == req)
> > +               cdns3_gadget_giveback(priv_ep, priv_req, -ECONNRESET);
> > +
> > +       spin_unlock_irqrestore(&priv_dev->lock, flags);
> > +       return ret;
> > +}
> > +
> > +/**
> > + * cdns3_gadget_ep_set_halt Sets/clears stall on selected endpoint
> > + * @ep: endpoint object to set/clear stall on
> > + * @value: 1 for set stall, 0 for clear stall
> > + *
> > + * Returns 0 on success, error code elsewhere
> > + */
> > +int cdns3_gadget_ep_set_halt(struct usb_ep *ep, int value)
> > +{
> > +       struct cdns3_endpoint *priv_ep = ep_to_cdns3_ep(ep);
> > +       struct cdns3_device *priv_dev = priv_ep->cdns3_dev;
> > +       unsigned long flags;
> > +       int ret = 0;
> > +
> > +       if (!(priv_ep->flags & EP_ENABLED))
> > +               return -EPERM;
> > +
> > +       spin_lock_irqsave(&priv_dev->lock, flags);
> > +
> > +       /* if actual transfer is pending defer setting stall on this endpoint */
> > +       if ((priv_ep->flags & EP_PENDING_REQUEST) && value) {
> > +               priv_ep->flags |= EP_STALL;
> > +               goto finish;
> > +       }
> > +
> > +       dev_dbg(priv_dev->dev, "Halt endpoint %s\n", priv_ep->name);
> > +
> > +       cdns3_select_ep(priv_dev, ep->desc->bEndpointAddress);
> > +       if (value) {
> > +               cdns3_ep_stall_flush(priv_ep);
> > +       } else {
> > +               priv_ep->flags &= ~EP_WEDGE;
> > +               writel(EP_CMD_CSTALL | EP_CMD_EPRST, &priv_dev->regs->ep_cmd);
> > +
> > +               /* wait for EPRST cleared */
> > +               ret = cdns3_handshake(&priv_dev->regs->ep_cmd,
> > +                                     EP_CMD_EPRST, 0, 100);
> > +               if (unlikely(ret)) {
> > +                       dev_err(priv_dev->dev,
> > +                               "Clearing halt condition failed for %s\n",
> > +                               priv_ep->name);
> > +                       goto finish;
> > +
> > +               } else {
> > +                       priv_ep->flags &= ~EP_STALL;
> > +               }
> > +       }
> > +
> > +       priv_ep->flags &= ~EP_PENDING_REQUEST;
> > +finish:
> > +       spin_unlock_irqrestore(&priv_dev->lock, flags);
> > +
> > +       return ret;
> > +}
> > +
> > +extern const struct usb_ep_ops cdns3_gadget_ep0_ops;
> > +
> > +static const struct usb_ep_ops cdns3_gadget_ep_ops = {
> > +       .enable = cdns3_gadget_ep_enable,
> > +       .disable = cdns3_gadget_ep_disable,
> > +       .alloc_request = cdns3_gadget_ep_alloc_request,
> > +       .free_request = cdns3_gadget_ep_free_request,
> > +       .queue = cdns3_gadget_ep_queue,
> > +       .dequeue = cdns3_gadget_ep_dequeue,
> > +       .set_halt = cdns3_gadget_ep_set_halt,
> > +       .set_wedge = cdns3_gadget_ep_set_wedge,
> > +};
> > +
> > +/**
> > + * cdns3_gadget_get_frame Returns number of actual ITP frame
> > + * @gadget: gadget object
> > + *
> > + * Returns number of actual ITP frame
> > + */
> > +static int cdns3_gadget_get_frame(struct usb_gadget *gadget)
> > +{
> > +       struct cdns3_device *priv_dev = gadget_to_cdns3_device(gadget);
> > +
> > +       return readl(&priv_dev->regs->usb_iptn);
> > +}
> > +
> > +static int cdns3_gadget_wakeup(struct usb_gadget *gadget)
> > +{
> > +       return 0;
> > +}
> > +
> > +static int cdns3_gadget_set_selfpowered(struct usb_gadget *gadget,
> > +                                       int is_selfpowered)
> > +{
> > +       struct cdns3_device *priv_dev = gadget_to_cdns3_device(gadget);
> > +       unsigned long flags;
> > +
> > +       spin_lock_irqsave(&priv_dev->lock, flags);
> > +       priv_dev->is_selfpowered = !!is_selfpowered;
> > +       spin_unlock_irqrestore(&priv_dev->lock, flags);
> > +       return 0;
> > +}
> > +
> > +static int cdns3_gadget_pullup(struct usb_gadget *gadget, int is_on)
> > +{
> > +       struct cdns3_device *priv_dev = gadget_to_cdns3_device(gadget);
> > +
> > +       if (is_on)
> > +               writel(USB_CONF_DEVEN, &priv_dev->regs->usb_conf);
> > +       else
> > +               writel(USB_CONF_DEVDS, &priv_dev->regs->usb_conf);
> > +
> > +       return 0;
> > +}
> > +
> > +static void cdns3_gadget_config(struct cdns3_device *priv_dev)
> > +{
> > +       struct cdns3_usb_regs __iomem *regs = priv_dev->regs;
> > +
> > +       cdns3_ep0_config(priv_dev);
> > +
> > +       /* enable interrupts for endpoint 0 (in and out) */
> > +       writel(EP_IEN_EP_OUT0 | EP_IEN_EP_IN0, &regs->ep_ien);
> > +
> > +       /* enable generic interrupt*/
> > +       writel(USB_IEN_INIT, &regs->usb_ien);
> > +       writel(USB_CONF_CLK2OFFDS | USB_CONF_L1DS, &regs->usb_conf);
> > +       writel(USB_CONF_DMULT, &regs->usb_conf);
> > +       cdns3_gadget_pullup(&priv_dev->gadget, 1);
> > +}
> > +
> > +/**
> > + * cdns3_gadget_udc_start Gadget start
> > + * @gadget: gadget object
> > + * @driver: driver which operates on this gadget
> > + *
> > + * Returns 0 on success, error code elsewhere
> > + */
> > +static int cdns3_gadget_udc_start(struct usb_gadget *gadget,
> > +                                 struct usb_gadget_driver *driver)
> > +{
> > +       struct cdns3_device *priv_dev = gadget_to_cdns3_device(gadget);
> > +       unsigned long flags;
> > +
> > +       spin_lock_irqsave(&priv_dev->lock, flags);
> > +       priv_dev->gadget_driver = driver;
> > +       cdns3_gadget_config(priv_dev);
> > +       spin_unlock_irqrestore(&priv_dev->lock, flags);
> > +       return 0;
> > +}
> > +
> > +/**
> > + * cdns3_gadget_udc_stop Stops gadget
> > + * @gadget: gadget object
> > + *
> > + * Returns 0
> > + */
> > +static int cdns3_gadget_udc_stop(struct usb_gadget *gadget)
> > +{
> > +       struct cdns3_device *priv_dev = gadget_to_cdns3_device(gadget);
> > +       struct cdns3_endpoint *priv_ep;
> > +       u32 bEndpointAddress;
> > +       struct usb_ep *ep;
> > +       int ret = 0;
> > +
> > +       priv_dev->gadget_driver = NULL;
> > +
> > +       priv_dev->onchip_mem_allocated_size = 0;
> > +       priv_dev->gadget.speed = USB_SPEED_UNKNOWN;
> > +
> > +       list_for_each_entry(ep, &priv_dev->gadget.ep_list, ep_list) {
> > +               priv_ep = ep_to_cdns3_ep(ep);
> > +               bEndpointAddress = priv_ep->num | priv_ep->dir;
> > +               cdns3_select_ep(priv_dev, bEndpointAddress);
> > +               writel(EP_CMD_EPRST, &priv_dev->regs->ep_cmd);
> > +               ret = cdns3_handshake(&priv_dev->regs->ep_cmd,
> > +                                     EP_CMD_EPRST, 0, 100);
> > +               cdns3_free_trb_pool(priv_ep);
> > +       }
> > +
> > +       /* disable interrupt for device */
> > +       writel(0, &priv_dev->regs->usb_ien);
> > +       writel(USB_CONF_DEVDS, &priv_dev->regs->usb_conf);
> > +
> > +       return ret;
> > +}
> > +
> > +static const struct usb_gadget_ops cdns3_gadget_ops = {
> > +       .get_frame = cdns3_gadget_get_frame,
> > +       .wakeup = cdns3_gadget_wakeup,
> > +       .set_selfpowered = cdns3_gadget_set_selfpowered,
> > +       .pullup = cdns3_gadget_pullup,
> > +       .udc_start = cdns3_gadget_udc_start,
> > +       .udc_stop = cdns3_gadget_udc_stop,
> > +       .match_ep = cdns3_gadget_match_ep,
> > +};
> > +
> > +static void cdns3_free_all_eps(struct cdns3_device *priv_dev)
> > +{
> > +       int i;
> > +
> > +       /*ep0 OUT point to ep0 IN*/
> > +       priv_dev->eps[16] = NULL;
> > +
> > +       cdns3_free_trb_pool(priv_dev->eps[0]);
> > +
> > +       for (i = 0; i < CDNS3_ENDPOINTS_MAX_COUNT; i++)
> > +               if (priv_dev->eps[i])
> > +                       devm_kfree(priv_dev->dev, priv_dev->eps[i]);
> > +}
> > +
> > +/**
> > + * cdns3_init_eps Initializes software endpoints of gadget
> > + * @cdns3: extended gadget object
> > + *
> > + * Returns 0 on success, error code elsewhere
> > + */
> > +static int cdns3_init_eps(struct cdns3_device *priv_dev)
> > +{
> > +       u32 ep_enabled_reg, iso_ep_reg;
> > +       struct cdns3_endpoint *priv_ep;
> > +       int ep_dir, ep_number;
> > +       u32 ep_mask;
> > +       int ret = 0;
> > +       int i;
> > +
> > +       /* Read it from USB_CAP3 to USB_CAP5 */
> > +       ep_enabled_reg = readl(&priv_dev->regs->usb_cap3);
> > +       iso_ep_reg = readl(&priv_dev->regs->usb_cap4);
> > +
> > +       dev_dbg(priv_dev->dev, "Initializing non-zero endpoints\n");
> > +
> > +       for (i = 0; i < CDNS3_ENDPOINTS_MAX_COUNT; i++) {
> > +               ep_dir = i >> 4;        /* i div 16 */
> > +               ep_number = i & 0xF;    /* i % 16 */
> > +               ep_mask = BIT(i);
> > +
> > +               if (!(ep_enabled_reg & ep_mask))
> > +                       continue;
> > +
> > +               if (ep_dir && !ep_number) {
> > +                       priv_dev->eps[i] = priv_dev->eps[0];
> > +                       continue;
> > +               }
> > +
> > +               priv_ep = devm_kzalloc(priv_dev->dev, sizeof(*priv_ep),
> > +                                      GFP_KERNEL);
> > +               if (!priv_ep) {
> > +                       ret = -ENOMEM;
> > +                       goto err;
> > +               }
> > +
> > +               /* set parent of endpoint object */
> > +               priv_ep->cdns3_dev = priv_dev;
> > +               priv_dev->eps[i] = priv_ep;
> > +               priv_ep->num = ep_number;
> > +               priv_ep->dir = ep_dir ? USB_DIR_IN : USB_DIR_OUT;
> > +
> > +               if (!ep_number) {
> > +                       ret = cdns3_init_ep0(priv_dev, priv_ep);
> > +                       if (ret) {
> > +                               dev_err(priv_dev->dev, "Failed to init ep0\n");
> > +                               goto err;
> > +                       }
> > +               } else {
> > +                       snprintf(priv_ep->name, sizeof(priv_ep->name), "ep%d%s",
> > +                                ep_number, !!ep_dir ? "in" : "out");
> > +                       priv_ep->endpoint.name = priv_ep->name;
> > +
> > +                       usb_ep_set_maxpacket_limit(&priv_ep->endpoint,
> > +                                                  CDNS3_EP_MAX_PACKET_LIMIT);
> > +                       priv_ep->endpoint.max_streams = CDNS3_EP_MAX_STREAMS;
> > +                       priv_ep->endpoint.ops = &cdns3_gadget_ep_ops;
> > +                       if (ep_dir)
> > +                               priv_ep->endpoint.caps.dir_in = 1;
> > +                       else
> > +                               priv_ep->endpoint.caps.dir_out = 1;
> > +
> > +                       if (iso_ep_reg & ep_mask)
> > +                               priv_ep->endpoint.caps.type_iso = 1;
> > +
> > +                       priv_ep->endpoint.caps.type_bulk = 1;
> > +                       priv_ep->endpoint.caps.type_int = 1;
> > +                       priv_ep->endpoint.maxburst = CDNS3_EP_BUF_SIZE - 1;
> > +
> > +                       list_add_tail(&priv_ep->endpoint.ep_list,
> > +                                     &priv_dev->gadget.ep_list);
> > +               }
> > +
> > +               priv_ep->flags = 0;
> > +
> > +               dev_info(priv_dev->dev, "Initialized  %s support: %s %s\n",
> > +                        priv_ep->name,
> > +                        priv_ep->endpoint.caps.type_bulk ? "BULK, INT" : "",
> > +                        priv_ep->endpoint.caps.type_iso ? "ISO" : "");
> > +
> > +               INIT_LIST_HEAD(&priv_ep->pending_req_list);
> > +               INIT_LIST_HEAD(&priv_ep->deferred_req_list);
> > +               INIT_LIST_HEAD(&priv_ep->descmiss_req_list);
> > +       }
> > +
> > +       return 0;
> > +err:
> > +       cdns3_free_all_eps(priv_dev);
> > +       return -ENOMEM;
> > +}
> > +
> > +static void cdns3_gadget_disable(struct cdns3 *cdns)
> > +{
> > +       struct cdns3_device *priv_dev;
> > +
> > +       priv_dev = cdns->gadget_dev;
> > +
> > +       if (priv_dev->gadget_driver)
> > +               priv_dev->gadget_driver->disconnect(&priv_dev->gadget);
> > +
> > +       usb_gadget_disconnect(&priv_dev->gadget);
> > +       priv_dev->gadget.speed = USB_SPEED_UNKNOWN;
> > +}
> > +
> > +void cdns3_gadget_exit(struct cdns3 *cdns)
> > +{
> > +       struct cdns3_device *priv_dev;
> > +
> > +       priv_dev = cdns->gadget_dev;
> > +
> > +       cdns3_gadget_disable(cdns);
> > +
> > +       devm_free_irq(cdns->dev, cdns->irq, cdns);
> > +
> > +       pm_runtime_mark_last_busy(cdns->dev);
> > +       pm_runtime_put_autosuspend(cdns->dev);
> > +
> > +       usb_del_gadget_udc(&priv_dev->gadget);
> > +
> > +       cdns3_free_all_eps(priv_dev);
> > +
> > +       dma_free_coherent(priv_dev->sysdev, 8, priv_dev->setup_buf,
> > +                         priv_dev->setup_dma);
> > +
> > +       kfree(priv_dev->zlp_buf);
> > +       kfree(priv_dev);
> > +       cdns->gadget_dev = NULL;
> > +}
> > +
> > +static int cdns3_gadget_start(struct cdns3 *cdns)
> > +{
> > +       struct cdns3_device *priv_dev;
> > +       u32 max_speed;
> > +       int ret;
> > +
> > +       priv_dev = kzalloc(sizeof(*priv_dev), GFP_KERNEL);
> > +       if (!priv_dev)
> > +               return -ENOMEM;
> > +
> > +       cdns->gadget_dev = priv_dev;
> > +       priv_dev->sysdev = cdns->dev;
> > +       priv_dev->dev = cdns->dev;
> > +       priv_dev->regs = cdns->dev_regs;
> > +
> > +       max_speed = usb_get_maximum_speed(cdns->dev);
> > +
> > +       /* Check the maximum_speed parameter */
> > +       switch (max_speed) {
> > +       case USB_SPEED_FULL:
> > +       case USB_SPEED_HIGH:
> > +       case USB_SPEED_SUPER:
> > +               break;
> > +       default:
> > +               dev_err(cdns->dev, "invalid maximum_speed parameter %d\n",
> > +                       max_speed);
> > +               /* fall through */
> > +       case USB_SPEED_UNKNOWN:
> > +               /* default to superspeed */
> > +               max_speed = USB_SPEED_SUPER;
> > +               break;
> > +       }
> > +
> > +       /* fill gadget fields */
> > +       priv_dev->gadget.max_speed = max_speed;
> > +       priv_dev->gadget.speed = USB_SPEED_UNKNOWN;
> > +       priv_dev->gadget.ops = &cdns3_gadget_ops;
> > +       priv_dev->gadget.name = "usb-ss-gadget";
> > +       priv_dev->gadget.sg_supported = 1;
> > +
> > +       spin_lock_init(&priv_dev->lock);
> > +       INIT_WORK(&priv_dev->pending_status_wq,
> > +                 cdns3_pending_setup_status_handler);
> > +
> > +       /* initialize endpoint container */
> > +       INIT_LIST_HEAD(&priv_dev->gadget.ep_list);
> > +
> > +       ret = cdns3_init_eps(priv_dev);
> > +       if (ret) {
> > +               dev_err(priv_dev->dev, "Failed to create endpoints\n");
> > +               goto err1;
> > +       }
> > +
> > +       /* allocate memory for setup packet buffer */
> > +       priv_dev->setup_buf = dma_alloc_coherent(priv_dev->sysdev, 8,
> > +                                                &priv_dev->setup_dma, GFP_DMA);
> > +       if (!priv_dev->setup_buf) {
> > +               dev_err(priv_dev->dev, "Failed to allocate memory for SETUP buffer\n");
> > +               ret = -ENOMEM;
> > +               goto err2;
> > +       }
> > +
> > +       priv_dev->dev_ver = readl(&priv_dev->regs->usb_cap6);
> > +       dev_dbg(priv_dev->dev, "Device Controller version: %08x\n",
> > +               readl(&priv_dev->regs->usb_cap6));
> > +       dev_dbg(priv_dev->dev, "USB Capabilities:: %08x\n",
> > +               readl(&priv_dev->regs->usb_cap1));
> > +       dev_dbg(priv_dev->dev, "On-Chip memory cnfiguration: %08x\n",
> > +               readl(&priv_dev->regs->usb_cap2));
> > +
> > +       priv_dev->zlp_buf = kzalloc(CDNS3_EP_ZLP_BUF_SIZE, GFP_KERNEL);
> > +       if (!priv_dev->zlp_buf) {
> > +               ret = -ENOMEM;
> > +               goto err3;
> > +       }
> > +
> > +       /* add USB gadget device */
> > +       ret = usb_add_gadget_udc(priv_dev->dev, &priv_dev->gadget);
> > +       if (ret < 0) {
> > +               dev_err(priv_dev->dev,
> > +                       "Failed to register USB device controller\n");
> > +               goto err4;
> > +       }
> > +
> > +       return 0;
> > +err4:
> > +       kfree(priv_dev->zlp_buf);
> > +err3:
> > +       dma_free_coherent(priv_dev->sysdev, 8, priv_dev->setup_buf,
> > +                         priv_dev->setup_dma);
> > +err2:
> > +       cdns3_free_all_eps(priv_dev);
> > +err1:
> > +       cdns->gadget_dev = NULL;
> > +       return ret;
> > +}
> > +
> > +static int __cdns3_gadget_init(struct cdns3 *cdns)
> > +{
> > +       struct cdns3_device *priv_dev;
> > +       unsigned long flags;
> > +       int ret = 0;
> > +
> > +       ret = cdns3_gadget_start(cdns);
> > +       if (ret)
> > +               return ret;
> > +
> > +       priv_dev = cdns->gadget_dev;
> > +       ret = devm_request_threaded_irq(cdns->dev, cdns->irq,
> > +                                       cdns3_device_irq_handler,
> > +                                       cdns3_device_thread_irq_handler,
> > +                                       IRQF_SHARED, dev_name(cdns->dev), cdns);
> > +       if (ret)
> > +               goto err0;
> > +
> > +       pm_runtime_get_sync(cdns->dev);
> > +       spin_lock_irqsave(&priv_dev->lock, flags);
> > +       spin_unlock_irqrestore(&priv_dev->lock, flags);
> > +       return 0;
> > +err0:
> > +       cdns3_gadget_exit(cdns);
> > +       return ret;
> > +}
> > +
> > +static int cdns3_gadget_suspend(struct cdns3 *cdns, bool do_wakeup)
> > +{
> > +       cdns3_gadget_disable(cdns);
> > +       return 0;
> > +}
> > +
> > +static int cdns3_gadget_resume(struct cdns3 *cdns, bool hibernated)
> > +{
> > +       struct cdns3_device *priv_dev;
> > +       unsigned long flags;
> > +
> > +       priv_dev = cdns->gadget_dev;
> > +       spin_lock_irqsave(&priv_dev->lock, flags);
> > +
> > +       if (!priv_dev->gadget_driver) {
> > +               spin_unlock_irqrestore(&priv_dev->lock, flags);
> > +               return 0;
> > +       }
> > +
> > +       cdns3_gadget_config(priv_dev);
> > +       spin_unlock_irqrestore(&priv_dev->lock, flags);
> > +       return 0;
> > +}
> > +
> > +/**
> > + * cdns3_gadget_init - initialize device structure
> > + *
> > + * cdns: cdns3 instance
> > + *
> > + * This function initializes the gadget.
> > + */
> > +int cdns3_gadget_init(struct cdns3 *cdns)
> > +{
> > +       struct cdns3_role_driver *rdrv;
> > +
> > +       rdrv = devm_kzalloc(cdns->dev, sizeof(*rdrv), GFP_KERNEL);
> > +       if (!rdrv)
> > +               return -ENOMEM;
> > +
> > +       rdrv->start     = __cdns3_gadget_init;
> > +       rdrv->stop      = cdns3_gadget_exit;
> > +       rdrv->suspend   = cdns3_gadget_suspend;
> > +       rdrv->resume    = cdns3_gadget_resume;
> > +       rdrv->state     = CDNS3_ROLE_STATE_INACTIVE;
> > +       rdrv->name      = "gadget";
> > +       cdns->roles[CDNS3_ROLE_GADGET] = rdrv;
> > +
> > +       return 0;
> > +}
> > diff --git a/drivers/usb/cdns3/gadget.h b/drivers/usb/cdns3/gadget.h
> > new file mode 100644
> > index 000000000000..41cec7f085ad
> > --- /dev/null
> > +++ b/drivers/usb/cdns3/gadget.h
> > @@ -0,0 +1,1206 @@
> > +/* SPDX-License-Identifier: GPL-2.0 */
> > +/*
> > + * USBSS device controller driver header file
> > + *
> > + * Copyright (C) 2018 Cadence.
> > + * Copyright (C) 2017-2018 NXP
> > + *
> > + * Author: Pawel Laszczak <pawell@cadence.com>
> > + *         Pawel Jez <pjez@cadence.com>
> > + *         Peter Chen <peter.chen@nxp.com>
> > + */
> > +#ifndef __LINUX_CDNS3_GADGET
> > +#define __LINUX_CDNS3_GADGET
> > +#include <linux/usb/gadget.h>
> > +
> > +/*
> > + * USBSS-DEV register interface.
> > + * This corresponds to the USBSS Device Controller Interface
> > + */
> > +
> > +/**
> > + * struct cdns3_usb_regs - device controller registers.
> > + * @usb_conf:      Global Configuration Register.
> > + * @usb_sts:       Global Status Register.
> > + * @usb_cmd:       Global Command Register.
> > + * @usb_iptn:      ITP/SOF number Register.
> > + * @usb_lpm:       Global Command Register.
> > + * @usb_ien:       USB Interrupt Enable Register.
> > + * @usb_ists:      USB Interrupt Status Register.
> > + * @ep_sel:        Endpoint Select Register.
> > + * @ep_traddr:     Endpoint Transfer Ring Address Register.
> > + * @ep_cfg:        Endpoint Configuration Register.
> > + * @ep_cmd:        Endpoint Command Register.
> > + * @ep_sts:        Endpoint Status Register.
> > + * @ep_sts_sid:    Endpoint Status Register.
> > + * @ep_sts_en:     Endpoint Status Register Enable.
> > + * @drbl:          Doorbell Register.
> > + * @ep_ien:        EP Interrupt Enable Register.
> > + * @ep_ists:       EP Interrupt Status Register.
> > + * @usb_pwr:       Global Power Configuration Register.
> > + * @usb_conf2:     Global Configuration Register 2.
> > + * @usb_cap1:      Capability Register 1.
> > + * @usb_cap2:      Capability Register 2.
> > + * @usb_cap3:      Capability Register 3.
> > + * @usb_cap4:      Capability Register 4.
> > + * @usb_cap5:      Capability Register 5.
> > + * @usb_cap6:      Capability Register 6.
> > + * @usb_cpkt1:     Custom Packet Register 1.
> > + * @usb_cpkt2:     Custom Packet Register 2.
> > + * @usb_cpkt3:     Custom Packet Register 3.
> > + * @reserved1:     Reserved.
> > + * @cfg_regs:      Configuration registers.
> > + * @reserved2:     Reserved.
> > + * @dma_axi_ctrl:  AXI Control register.
> > + * @dma_axi_id:    AXI ID register.
> > + * @dma_axi_cap:   AXI Capability register.
> > + * @dma_axi_ctrl0: AXI Control 0 register.
> > + * @dma_axi_ctrl1: AXI Control 1 register.
> > + */
> > +struct cdns3_usb_regs {
> > +       __le32 usb_conf;
> > +       __le32 usb_sts;
> > +       __le32 usb_cmd;
> > +       __le32 usb_iptn;
> > +       __le32 usb_lpm;
> > +       __le32 usb_ien;
> > +       __le32 usb_ists;
> > +       __le32 ep_sel;
> > +       __le32 ep_traddr;
> > +       __le32 ep_cfg;
> > +       __le32 ep_cmd;
> > +       __le32 ep_sts;
> > +       __le32 ep_sts_sid;
> > +       __le32 ep_sts_en;
> > +       __le32 drbl;
> > +       __le32 ep_ien;
> > +       __le32 ep_ists;
> > +       __le32 usb_pwr;
> > +       __le32 usb_conf2;
> > +       __le32 usb_cap1;
> > +       __le32 usb_cap2;
> > +       __le32 usb_cap3;
> > +       __le32 usb_cap4;
> > +       __le32 usb_cap5;
> > +       __le32 usb_cap6;
> > +       __le32 usb_cpkt1;
> > +       __le32 usb_cpkt2;
> > +       __le32 usb_cpkt3;
> > +       __le32 reserved1[36];
> > +       __le32 cfg_reg1;
> > +       __le32 dbg_link1;
> > +       __le32 dbg_link2;
> > +       __le32 cfg_regs[74];
> > +       __le32 reserved2[34];
> > +       __le32 dma_axi_ctrl;
> > +       __le32 dma_axi_id;
> > +       __le32 dma_axi_cap;
> > +       __le32 dma_axi_ctrl0;
> > +       __le32 dma_axi_ctrl1;
> > +};
> > +
> > +/* USB_CONF - bitmasks */
> > +/* Reset USB device configuration. */
> > +#define USB_CONF_CFGRST                BIT(0)
> > +/* Set Configuration. */
> > +#define USB_CONF_CFGSET                BIT(1)
> > +/* Disconnect USB device in SuperSpeed. */
> > +#define USB_CONF_USB3DIS       BIT(3)
> > +/* Disconnect USB device in HS/FS */
> > +#define USB_CONF_USB2DIS       BIT(4)
> > +/* Little Endian access - default */
> > +#define USB_CONF_LENDIAN       BIT(5)
> > +/*
> > + * Big Endian access. Driver assume that byte order for
> > + * SFRs access always is as Little Endian so this bit
> > + * is not used.
> > + */
> > +#define USB_CONF_BENDIAN       BIT(6)
> > +/* Device software reset. */
> > +#define USB_CONF_SWRST         BIT(7)
> > +/* Singular DMA transfer mode. */
> > +#define USB_CONF_DSING         BIT(8)
> > +/* Multiple DMA transfers mode. */
> > +#define USB_CONF_DMULT         BIT(9)
> > +/* DMA clock turn-off enable. */
> > +#define USB_CONF_DMAOFFEN      BIT(10)
> > +/* DMA clock turn-off disable. */
> > +#define USB_CONF_DMAOFFDS      BIT(11)
> > +/* Clear Force Full Speed. */
> > +#define USB_CONF_CFORCE_FS     BIT(12)
> > +/* Set Force Full Speed. */
> > +#define USB_CONF_SFORCE_FS     BIT(13)
> > +/* Device enable. */
> > +#define USB_CONF_DEVEN         BIT(14)
> > +/* Device disable. */
> > +#define USB_CONF_DEVDS         BIT(15)
> > +/* L1 LPM state entry enable (used in HS/FS mode). */
> > +#define USB_CONF_L1EN          BIT(16)
> > +/* L1 LPM state entry disable (used in HS/FS mode). */
> > +#define USB_CONF_L1DS          BIT(17)
> > +/* USB 2.0 clock gate disable. */
> > +#define USB_CONF_CLK2OFFEN     BIT(18)
> > +/* USB 2.0 clock gate enable. */
> > +#define USB_CONF_CLK2OFFDS     BIT(19)
> > +/* L0 LPM state entry request (used in HS/FS mode). */
> > +#define USB_CONF_LGO_L0                BIT(20)
> > +/* USB 3.0 clock gate disable. */
> > +#define USB_CONF_CLK3OFFEN     BIT(21)
> > +/* USB 3.0 clock gate enable. */
> > +#define USB_CONF_CLK3OFFDS     BIT(22)
> > +/* Bit 23 is reserved*/
> > +/* U1 state entry enable (used in SS mode). */
> > +#define USB_CONF_U1EN          BIT(24)
> > +/* U1 state entry disable (used in SS mode). */
> > +#define USB_CONF_U1DS          BIT(25)
> > +/* U2 state entry enable (used in SS mode). */
> > +#define USB_CONF_U2EN          BIT(26)
> > +/* U2 state entry disable (used in SS mode). */
> > +#define USB_CONF_U2DS          BIT(27)
> > +/* U0 state entry request (used in SS mode). */
> > +#define USB_CONF_LGO_U0                BIT(28)
> > +/* U1 state entry request (used in SS mode). */
> > +#define USB_CONF_LGO_U1                BIT(29)
> > +/* U2 state entry request (used in SS mode). */
> > +#define USB_CONF_LGO_U2                BIT(30)
> > +/* SS.Inactive state entry request (used in SS mode) */
> > +#define USB_CONF_LGO_SSINACT   BIT(31)
> > +
> > +/* USB_STS - bitmasks */
> > +/*
> > + * Configuration status.
> > + * 1 - device is in the configured state.
> > + * 0 - device is not configured.
> > + */
> > +#define USB_STS_CFGSTS_MASK    BIT(0)
> > +#define USB_STS_CFGSTS(p)      ((p) & USB_STS_CFGSTS_MASK)
> > +/*
> > + * On-chip memory overflow.
> > + * 0 - On-chip memory status OK.
> > + * 1 - On-chip memory overflow.
> > + */
> > +#define USB_STS_OV_MASK                BIT(1)
> > +#define USB_STS_OV(p)          ((p) & USB_STS_OV_MASK)
> > +/*
> > + * SuperSpeed connection status.
> > + * 0 - USB in SuperSpeed mode disconnected.
> > + * 1 - USB in SuperSpeed mode connected.
> > + */
> > +#define USB_STS_USB3CONS_MASK  BIT(2)
> > +#define USB_STS_USB3CONS(p)    ((p) & USB_STS_USB3CONS_MASK)
> > +/*
> > + * DMA transfer configuration status.
> > + * 0 - single request.
> > + * 1 - multiple TRB chain
> > + */
> > +#define USB_STS_DTRANS_MASK    BIT(3)
> > +#define USB_STS_DTRANS(p)      ((p) & USB_STS_DTRANS_MASK)
> > +/*
> > + * Device speed.
> > + * 0 - Undefined (value after reset).
> > + * 1 - Low speed
> > + * 2 - Full speed
> > + * 3 - High speed
> > + * 4 - Super speed
> > + */
> > +#define USB_STS_USBSPEED_MASK  GENMASK(6, 4)
> > +#define USB_STS_USBSPEED(p)    (((p) & USB_STS_USBSPEED_MASK) >> 4)
> > +#define USB_STS_LS             (0x1 << 4)
> > +#define USB_STS_FS             (0x2 << 4)
> > +#define USB_STS_HS             (0x3 << 4)
> > +#define USB_STS_SS             (0x4 << 4)
> > +#define DEV_UNDEFSPEED(p)      (((p) & USB_STS_USBSPEED_MASK) == (0x0 << 4))
> > +#define DEV_LOWSPEED(p)                (((p) & USB_STS_USBSPEED_MASK) == USB_STS_LS)
> > +#define DEV_FULLSPEED(p)       (((p) & USB_STS_USBSPEED_MASK) == USB_STS_FS)
> > +#define DEV_HIGHSPEED(p)       (((p) & USB_STS_USBSPEED_MASK) == USB_STS_HS)
> > +#define DEV_SUPERSPEED(p)      (((p) & USB_STS_USBSPEED_MASK) == USB_STS_SS)
> > +/*
> > + * Endianness for SFR access.
> > + * 0 - Little Endian order (default after hardware reset).
> > + * 1 - Big Endian order
> > + */
> > +#define USB_STS_ENDIAN_MASK    BIT(7)
> > +#define USB_STS_ENDIAN(p)      ((p) & USB_STS_ENDIAN_MASK)
> > +/*
> > + * HS/FS clock turn-off status.
> > + * 0 - hsfs clock is always on.
> > + * 1 - hsfs clock turn-off in L2 (HS/FS mode) is enabled
> > + *          (default after hardware reset).
> > + */
> > +#define USB_STS_CLK2OFF_MASK   BIT(8)
> > +#define USB_STS_CLK2OFF(p)     ((p) & USB_STS_CLK2OFF_MASK)
> > +/*
> > + * PCLK clock turn-off status.
> > + * 0 - pclk clock is always on.
> > + * 1 - pclk clock turn-off in U3 (SS mode) is enabled
> > + *          (default after hardware reset).
> > + */
> > +#define USB_STS_CLK3OFF_MASK   BIT(9)
> > +#define USB_STS_CLK3OFF(p)     ((p) & USB_STS_CLK3OFF_MASK)
> > +/*
> > + * Controller in reset state.
> > + * 0 - Internal reset is active.
> > + * 1 - Internal reset is not active and controller is fully operational.
> > + */
> > +#define USB_STS_IN_RST_MASK    BIT(10)
> > +#define USB_STS_IN_RST(p)      ((p) & USB_STS_IN_RST_MASK)
> > +/*
> > + * Device enable Status.
> > + * 0 - USB device is disabled (VBUS input is disconnected from internal logic).
> > + * 1 - USB device is enabled (VBUS input is connected to the internal logic).
> > + */
> > +#define USB_STS_DEVS_MASK      BIT(14)
> > +#define USB_STS_DEVS(p)                ((p) & USB_STS_DEVS_MASK)
> > +/*
> > + * DAddress statuss.
> > + * 0 - USB device is default state.
> > + * 1 - USB device is at least in address state.
> > + */
> > +#define USB_STS_ADDRESSED_MASK BIT(15)
> > +#define USB_STS_ADDRESSED(p)   ((p) & USB_STS_ADDRESSED_MASK)
> > +/*
> > + * L1 LPM state enable status (used in HS/FS mode).
> > + * 0 - Entering to L1 LPM state disabled.
> > + * 1 - Entering to L1 LPM state enabled.
> > + */
> > +#define USB_STS_L1ENS_MASK     BIT(16)
> > +#define USB_STS_L1ENS(p)       ((p) & USB_STS_L1ENS_MASK)
> > +/*
> > + * Internal VBUS connection status (used both in HS/FS  and SS mode).
> > + * 0 - internal VBUS is not detected.
> > + * 1 - internal VBUS is detected.
> > + */
> > +#define USB_STS_VBUSS_MASK     BIT(17)
> > +#define USB_STS_VBUSS(p)       ((p) & USB_STS_VBUSS_MASK)
> > +/*
> > + * HS/FS LPM  state (used in FS/HS mode).
> > + * 0 - L0 State
> > + * 1 - L1 State
> > + * 2 - L2 State
> > + * 3 - L3 State
> > + */
> > +#define USB_STS_LPMST_MASK     GENMASK(19, 18)
> > +#define DEV_L0_STATE(p)                (((p) & USB_STS_LPMST_MASK) == (0x0 << 18))
> > +#define DEV_L1_STATE(p)                (((p) & USB_STS_LPMST_MASK) == (0x1 << 18))
> > +#define DEV_L2_STATE(p)                (((p) & USB_STS_LPMST_MASK) == (0x2 << 18))
> > +#define DEV_L3_STATE(p)                (((p) & USB_STS_LPMST_MASK) == (0x3 << 18))
> > +/*
> > + * Disable HS status (used in FS/HS mode).
> > + * 0 - the disconnect bit for HS/FS mode is set .
> > + * 1 - the disconnect bit for HS/FS mode is not set.
> > + */
> > +#define USB_STS_USB2CONS_MASK  BIT(20)
> > +#define USB_STS_USB2CONS(p)    ((p) & USB_STS_USB2CONS_MASK)
> > +/*
> > + * HS/FS mode connection status (used in FS/HS mode).
> > + * 0 - High Speed operations in USB2.0 (FS/HS) mode not disabled.
> > + * 1 - High Speed operations in USB2.0 (FS/HS).
> > + */
> > +#define USB_STS_DISABLE_HS_MASK        BIT(21)
> > +#define USB_STS_DISABLE_HS(p)  ((p) & USB_STS_DISABLE_HS_MASK)
> > +/*
> > + * U1 state enable status (used in SS mode).
> > + * 0 - Entering to  U1 state disabled.
> > + * 1 - Entering to  U1 state enabled.
> > + */
> > +#define USB_STS_U1ENS_MASK     BIT(24)
> > +#define USB_STS_U1ENS(p)       ((p) & USB_STS_U1ENS_MASK)
> > +/*
> > + * U2 state enable status (used in SS mode).
> > + * 0 - Entering to  U2 state disabled.
> > + * 1 - Entering to  U2 state enabled.
> > + */
> > +#define USB_STS_U2ENS_MASK     BIT(25)
> > +#define USB_STS_U2ENS(p)       ((p) & USB_STS_U2ENS_MASK)
> > +/*
> > + * SuperSpeed Link LTSSM state. This field reflects USBSS-DEV current
> > + * SuperSpeed link state
> > + */
> > +#define USB_STS_LST_MASK       GENMASK(29, 26)
> > +#define DEV_LST_U0             (((p) & USB_STS_LST_MASK) == (0x0 << 26))
> > +#define DEV_LST_U1             (((p) & USB_STS_LST_MASK) == (0x1 << 26))
> > +#define DEV_LST_U2             (((p) & USB_STS_LST_MASK) == (0x2 << 26))
> > +#define DEV_LST_U3             (((p) & USB_STS_LST_MASK) == (0x3 << 26))
> > +#define DEV_LST_DISABLED       (((p) & USB_STS_LST_MASK) == (0x4 << 26))
> > +#define DEV_LST_RXDETECT       (((p) & USB_STS_LST_MASK) == (0x5 << 26))
> > +#define DEV_LST_INACTIVE       (((p) & USB_STS_LST_MASK) == (0x6 << 26))
> > +#define DEV_LST_POLLING                (((p) & USB_STS_LST_MASK) == (0x7 << 26))
> > +#define DEV_LST_RECOVERY       (((p) & USB_STS_LST_MASK) == (0x8 << 26))
> > +#define DEV_LST_HOT_RESET      (((p) & USB_STS_LST_MASK) == (0x9 << 26))
> > +#define DEV_LST_COMP_MODE      (((p) & USB_STS_LST_MASK) == (0xa << 26))
> > +#define DEV_LST_LB_STATE       (((p) & USB_STS_LST_MASK) == (0xb << 26))
> > +/*
> > + * DMA clock turn-off status.
> > + * 0 - DMA clock is always on (default after hardware reset).
> > + * 1 - DMA clock turn-off in U1, U2 and U3 (SS mode) is enabled.
> > + */
> > +#define USB_STS_DMAOFF_MASK    BIT(30)
> > +#define USB_STS_DMAOFF(p)      ((p) & USB_STS_DMAOFF_MASK)
> > +/*
> > + * SFR Endian statuss.
> > + * 0 - Little Endian order (default after hardware reset).
> > + * 1 - Big Endian order.
> > + */
> > +#define USB_STS_ENDIAN2_MASK   BIT(31)
> > +#define USB_STS_ENDIAN2(p)     ((p) & USB_STS_ENDIAN2_MASK)
> > +
> > +/* USB_CMD -  bitmasks */
> > +/* Set Function Address */
> > +#define USB_CMD_SET_ADDR       BIT(0)
> > +/*
> > + * Function Address This field is saved to the device only when the field
> > + * SET_ADDR is set '1 ' during write to USB_CMD register.
> > + * Software is responsible for entering the address of the device during
> > + * SET_ADDRESS request service. This field should be set immediately after
> > + * the SETUP packet is decoded, and prior to confirmation of the status phase
> > + */
> > +#define USB_CMD_FADDR_MASK     GENMASK(7, 1)
> > +#define USB_CMD_FADDR(p)       (((p) << 1) & USB_CMD_FADDR_MASK)
> > +/* Send Function Wake Device Notification TP (used only in SS mode). */
> > +#define USB_CMD_SDNFW          BIT(8)
> > +/* Set Test Mode (used only in HS/FS mode). */
> > +#define USB_CMD_STMODE         BIT(9)
> > +/* Test mode selector (used only in HS/FS mode) */
> > +#define USB_STS_TMODE_SEL_MASK GENMASK(11, 10)
> > +#define USB_STS_TMODE_SEL(p)   (((p) << 10) & USB_STS_TMODE_SEL_MASK)
> > +/*
> > + *  Send Latency Tolerance Message Device Notification TP (used only
> > + *  in SS mode).
> > + */
> > +#define USB_CMD_SDNLTM         BIT(12)
> > +/* Send Custom Transaction Packet (used only in SS mode) */
> > +#define USB_CMD_SPKT           BIT(13)
> > +/*Device Notification 'Function Wake' - Interface value (only in SS mode. */
> > +#define USB_CMD_DNFW_INT_MASK  GENMASK(23, 16)
> > +#define USB_STS_DNFW_INT(p)    (((p) << 16) & USB_CMD_DNFW_INT_MASK)
> > +/*
> > + * Device Notification 'Latency Tolerance Message' -373 BELT value [7:0]
> > + * (used only in SS mode).
> > + */
> > +#define USB_CMD_DNLTM_BELT_MASK        GENMASK(27, 16)
> > +#define USB_STS_DNLTM_BELT(p)  (((p) << 16) & USB_CMD_DNLTM_BELT_MASK)
> > +
> > +/* USB_ITPN - bitmasks */
> > +/*
> > + * ITP(SS) / SOF (HS/FS) number
> > + * In SS mode this field represent number of last ITP received from host.
> > + * In HS/FS mode this field represent number of last SOF received from host.
> > + */
> > +#define USB_ITPN_MASK          GENMASK(13, 0)
> > +#define USB_ITPN(p)            ((p) & USB_ITPN_MASK)
> > +
> > +/* USB_LPM - bitmasks */
> > +/* Host Initiated Resume Duration. */
> > +#define USB_LPM_HIRD_MASK      GENMASK(3, 0)
> > +#define USB_LPM_HIRD(p)                ((p) & USB_LPM_HIRD_MASK)
> > +/* Remote Wakeup Enable (bRemoteWake). */
> > +#define USB_LPM_BRW            BIT(4)
> > +
> > +/* USB_IEN - bitmasks */
> > +/* SS connection interrupt enable */
> > +#define USB_IEN_CONIEN         BIT(0)
> > +/* SS disconnection interrupt enable. */
> > +#define USB_IEN_DISIEN         BIT(1)
> > +/* USB SS warm reset interrupt enable. */
> > +#define USB_IEN_UWRESIEN       BIT(2)
> > +/* USB SS hot reset interrupt enable */
> > +#define USB_IEN_UHRESIEN       BIT(3)
> > +/* SS link U3 state enter interrupt enable (suspend).*/
> > +#define USB_IEN_U3ENTIEN       BIT(4)
> > +/* SS link U3 state exit interrupt enable (wakeup). */
> > +#define USB_IEN_U3EXTIEN       BIT(5)
> > +/* SS link U2 state enter interrupt enable.*/
> > +#define USB_IEN_U2ENTIEN       BIT(6)
> > +/* SS link U2 state exit interrupt enable.*/
> > +#define USB_IEN_U2EXTIEN       BIT(7)
> > +/* SS link U1 state enter interrupt enable.*/
> > +#define USB_IEN_U1ENTIEN       BIT(8)
> > +/* SS link U1 state exit interrupt enable.*/
> > +#define USB_IEN_U1EXTIEN       BIT(9)
> > +/* ITP/SOF packet detected interrupt enable.*/
> > +#define USB_IEN_ITPIEN         BIT(10)
> > +/* Wakeup interrupt enable.*/
> > +#define USB_IEN_WAKEIEN                BIT(11)
> > +/* Send Custom Packet interrupt enable.*/
> > +#define USB_IEN_SPKTIEN                BIT(12)
> > +/* HS/FS mode connection interrupt enable.*/
> > +#define USB_IEN_CON2IEN                BIT(16)
> > +/* HS/FS mode disconnection interrupt enable.*/
> > +#define USB_IEN_DIS2IEN                BIT(17)
> > +/* USB reset (HS/FS mode) interrupt enable.*/
> > +#define USB_IEN_U2RESIEN       BIT(18)
> > +/* LPM L2 state enter interrupt enable.*/
> > +#define USB_IEN_L2ENTIEN       BIT(20)
> > +/* LPM  L2 state exit interrupt enable.*/
> > +#define USB_IEN_L2EXTIEN       BIT(21)
> > +/* LPM L1 state enter interrupt enable.*/
> > +#define USB_IEN_L1ENTIEN       BIT(24)
> > +/* LPM  L1 state exit interrupt enable.*/
> > +#define USB_IEN_L1EXTIEN       BIT(25)
> > +/* Configuration reset interrupt enable.*/
> > +#define USB_IEN_CFGRESIEN      BIT(26)
> > +/* Start of the USB SS warm reset interrupt enable.*/
> > +#define USB_IEN_UWRESSIEN      BIT(28)
> > +/* End of the USB SS warm reset interrupt enable.*/
> > +#define USB_IEN_UWRESEIEN      BIT(29)
> > +
> > +#define USB_IEN_INIT  (USB_IEN_U2RESIEN | USB_ISTS_DIS2I | USB_IEN_CON2IEN \
> > +                      | USB_IEN_UHRESIEN | USB_IEN_UWRESIEN | USB_IEN_DISIEN \
> > +                      | USB_IEN_CONIEN | USB_IEN_U3EXTIEN | USB_IEN_L2ENTIEN \
> > +                      | USB_IEN_L2EXTIEN)
> > +
> > +/* USB_ISTS - bitmasks */
> > +/* SS Connection detected. */
> > +#define USB_ISTS_CONI          BIT(0)
> > +/* SS Disconnection detected. */
> > +#define USB_ISTS_DISI          BIT(1)
> > +/* UUSB warm reset detectede. */
> > +#define USB_ISTS_UWRESI                BIT(2)
> > +/* USB hot reset detected. */
> > +#define USB_ISTS_UHRESI                BIT(3)
> > +/* U3 link state enter detected (suspend).*/
> > +#define USB_ISTS_U3ENTI                BIT(4)
> > +/* U3 link state exit detected (wakeup). */
> > +#define USB_ISTS_U3EXTI                BIT(5)
> > +/* U2 link state enter detected.*/
> > +#define USB_ISTS_U2ENTI                BIT(6)
> > +/* U2 link state exit detected.*/
> > +#define USB_ISTS_U2EXTI                BIT(7)
> > +/* U1 link state enter detected.*/
> > +#define USB_ISTS_U1ENTI                BIT(8)
> > +/* U1 link state exit detected.*/
> > +#define USB_ISTS_U1EXTI                BIT(9)
> > +/* ITP/SOF packet detected.*/
> > +#define USB_ISTS_ITPI          BIT(10)
> > +/* Wakeup detected.*/
> > +#define USB_ISTS_WAKEI         BIT(11)
> > +/* Send Custom Packet detected.*/
> > +#define USB_ISTS_SPKTI         BIT(12)
> > +/* HS/FS mode connection detected.*/
> > +#define USB_ISTS_CON2I         BIT(16)
> > +/* HS/FS mode disconnection detected.*/
> > +#define USB_ISTS_DIS2I         BIT(17)
> > +/* USB reset (HS/FS mode) detected.*/
> > +#define USB_ISTS_U2RESI                BIT(18)
> > +/* LPM L2 state enter detected.*/
> > +#define USB_ISTS_L2ENTI                BIT(20)
> > +/* LPM  L2 state exit detected.*/
> > +#define USB_ISTS_L2EXTI                BIT(21)
> > +/* LPM L1 state enter detected.*/
> > +#define USB_ISTS_L1ENTI                BIT(24)
> > +/* LPM L1 state exit detected.*/
> > +#define USB_ISTS_L1EXTI                BIT(25)
> > +/* USB configuration reset detected.*/
> > +#define USB_ISTS_CFGRESI       BIT(26)
> > +/* Start of the USB warm reset detected.*/
> > +#define USB_ISTS_UWRESSI       BIT(28)
> > +/* End of the USB warm reset detected.*/
> > +#define USB_ISTS_UWRESEI       BIT(29)
> > +
> > +/* USB_SEL - bitmasks */
> > +#define EP_SEL_EPNO_MASK       GENMASK(3, 0)
> > +/* Endpoint number. */
> > +#define EP_SEL_EPNO(p)         ((p) & EP_SEL_EPNO_MASK)
> > +/* Endpoint direction bit - 0 - OUT, 1 - IN. */
> > +#define EP_SEL_DIR             BIT(7)
> > +
> > +#define select_ep_in(nr)       (EP_SEL_EPNO(p) | EP_SEL_DIR)
> > +#define select_ep_out          (EP_SEL_EPNO(p))
> > +
> > +/* EP_TRADDR - bitmasks */
> > +/* Transfer Ring address. */
> > +#define EP_TRADDR_TRADDR(p)    ((p))
> > +
> > +/* EP_CFG - bitmasks */
> > +/* Endpoint enable */
> > +#define EP_CFG_ENABLE          BIT(0)
> > +/*
> > + *  Endpoint type.
> > + * 1 - isochronous
> > + * 2 - bulk
> > + * 3 - interrupt
> > + */
> > +#define EP_CFG_EPTYPE_MASK     GENMASK(2, 1)
> > +#define EP_CFG_EPTYPE(p)       (((p) << 1)  & EP_CFG_EPTYPE_MASK)
> > +/* Stream support enable (only in SS mode). */
> > +#define EP_CFG_STREAM_EN       BIT(3)
> > +/* TDL check (only in SS mode for BULK EP). */
> > +#define EP_CFG_TDL_CHK         BIT(4)
> > +/* SID check (only in SS mode for BULK OUT EP). */
> > +#define EP_CFG_SID_CHK         BIT(5)
> > +/* DMA transfer endianness. */
> > +#define EP_CFG_EPENDIAN                BIT(7)
> > +/* Max burst size (used only in SS mode). */
> > +#define EP_CFG_MAXBURST_MASK   GENMASK(11, 8)
> > +#define EP_CFG_MAXBURST(p)     (((p) << 8) & EP_CFG_MAXBURST_MASK)
> > +/* ISO max burst. */
> > +#define EP_CFG_MULT_MASK       GENMASK(15, 14)
> > +#define EP_CFG_MULT(p)         (((p) << 14) & EP_CFG_MULT)
> > +/* ISO max burst. */
> > +#define EP_CFG_MAXPKTSIZE_MASK GENMASK(26, 16)
> > +#define EP_CFG_MAXPKTSIZE(p)   (((p) << 16) & EP_CFG_MAXPKTSIZE_MASK)
> > +/* Max number of buffered packets. */
> > +#define EP_CFG_BUFFERING_MASK  GENMASK(31, 27)
> > +#define EP_CFG_BUFFERING(p)    (((p) << 27) & EP_CFG_BUFFERING_MASK)
> > +
> > +/* EP_CMD - bitmasks */
> > +/* Endpoint reset. */
> > +#define EP_CMD_EPRST           BIT(0)
> > +/* Endpoint STALL set. */
> > +#define EP_CMD_SSTALL          BIT(1)
> > +/* Endpoint STALL clear. */
> > +#define EP_CMD_CSTALL          BIT(2)
> > +/* Send ERDY TP. */
> > +#define EP_CMD_ERDY            BIT(3)
> > +/* Request complete. */
> > +#define EP_CMD_REQ_CMPL                BIT(5)
> > +/* Transfer descriptor ready. */
> > +#define EP_CMD_DRDY            BIT(6)
> > +/* Data flush. */
> > +#define EP_CMD_DFLUSH          BIT(7)
> > +/*
> > + * Transfer Descriptor Length write  (used only for Bulk Stream capable
> > + * endpoints in SS mode).
> > + */
> > +#define EP_CMD_STDL            BIT(8)
> > +/* Transfer Descriptor Length (used only in SS mode for bulk endpoints). */
> > +#define EP_CMD_TDL_MASK                GENMASK(15, 9)
> > +#define EP_CMD_TDL(p)          (((p) << 9) & EP_CMD_TDL_MASK)
> > +/* ERDY Stream ID value (used in SS mode). */
> > +#define EP_CMD_ERDY_SID_MASK   GENMASK(31, 16)
> > +#define EP_CMD_ERDY_SID(p)     (((p) << 16) & EP_CMD_SID_MASK)
> > +
> > +/* EP_STS - bitmasks */
> > +/* Setup transfer complete. */
> > +#define EP_STS_SETUP           BIT(0)
> > +/* Endpoint STALL status. */
> > +#define EP_STS_STALL(p)                ((p) & BIT(1))
> > +/* Interrupt On Complete. */
> > +#define EP_STS_IOC             BIT(2)
> > +/* Interrupt on Short Packet. */
> > +#define EP_STS_ISP             BIT(3)
> > +/* Transfer descriptor missing. */
> > +#define EP_STS_DESCMIS         BIT(4)
> > +/* Stream Rejected (used only in SS mode) */
> > +#define EP_STS_STREAMR         BIT(5)
> > +/* EXIT from MOVE DATA State (used only for stream transfers in SS mode). */
> > +#define EP_STS_MD_EXIT         BIT(6)
> > +/* TRB error. */
> > +#define EP_STS_TRBERR          BIT(7)
> > +/* Not ready (used only in SS mode). */
> > +#define EP_STS_NRDY            BIT(8)
> > +/* DMA busy. */
> > +#define EP_STS_DBUSY(p)                ((p) & BIT(9))
> > +/* Endpoint Buffer Empty */
> > +#define EP_STS_BUFFEMPTY(p)    ((p) & BIT(10))
> > +/* Current Cycle Status */
> > +#define EP_STS_CCS(p)          ((p) & BIT(11))
> > +/* Prime (used only in SS mode. */
> > +#define EP_STS_PRIME           BIT(12)
> > +/* Stream error (used only in SS mode). */
> > +#define EP_STS_SIDERR          BIT(13)
> > +/* OUT size mismatch. */
> > +#define EP_STS_OUTSMM          BIT(14)
> > +/* ISO transmission error. */
> > +#define EP_STS_ISOERR          BIT(15)
> > +/* Host Packet Pending (only for SS mode). */
> > +#define EP_STS_HOSTPP(p)       ((p) & BIT(16))
> > +/* Stream Protocol State Machine State (only for Bulk stream endpoints). */
> > +#define EP_STS_SPSMST_MASK             GENMASK(18, 17)
> > +#define EP_STS_SPSMST_DISABLED(p)      (((p) & EP_STS_SPSMST_MASK) >> 17)
> > +#define EP_STS_SPSMST_IDLE(p)          (((p) & EP_STS_SPSMST_MASK) >> 17)
> > +#define EP_STS_SPSMST_START_STREAM(p)  (((p) & EP_STS_SPSMST_MASK) >> 17)
> > +#define EP_STS_SPSMST_MOVE_DATA(p)     (((p) & EP_STS_SPSMST_MASK) >> 17)
> > +/* Interrupt On Transfer complete. */
> > +#define EP_STS_IOT             BIT(19)
> > +/* OUT queue endpoint number. */
> > +#define EP_STS_OUTQ_NO_MASK    GENMASK(27, 24)
> > +#define EP_STS_OUTQ_NO(p)      (((p) & EP_STS_OUTQ_NO_MASK) >> 24)
> > +/* OUT queue valid flag. */
> > +#define EP_STS_OUTQ_VAL_MASK   BIT(28)
> > +#define EP_STS_OUTQ_VAL(p)     ((p) & EP_STS_OUTQ_VAL_MASK)
> > +/* SETUP WAIT. */
> > +#define EP_STS_STPWAIT         BIT(31)
> > +
> > +/* EP_STS_SID - bitmasks */
> > +/* Stream ID (used only in SS mode). */
> > +#define EP_STS_SID_MASK                GENMASK(15, 0)
> > +#define EP_STS_SID(p)          ((p) & EP_STS_SID_MASK)
> > +
> > +/* EP_STS_EN - bitmasks */
> > +/* SETUP interrupt enable. */
> > +#define EP_STS_EN_SETUPEN      BIT(0)
> > +/* OUT transfer missing descriptor enable. */
> > +#define EP_STS_EN_DESCMISEN    BIT(4)
> > +/* Stream Rejected enable. */
> > +#define EP_STS_EN_STREAMREN    BIT(5)
> > +/* Move Data Exit enable.*/
> > +#define EP_STS_EN_MD_EXITEN    BIT(6)
> > +/* TRB enable. */
> > +#define EP_STS_EN_TRBERREN     BIT(7)
> > +/* NRDY enable. */
> > +#define EP_STS_EN_NRDYEN       BIT(8)
> > +/* Prime enable. */
> > +#define EP_STS_EN_PRIMEEEN     BIT(12)
> > +/* Stream error enable. */
> > +#define EP_STS_EN_SIDERREN     BIT(13)
> > +/* OUT size mismatch enable. */
> > +#define EP_STS_EN_OUTSMMEN     BIT(14)
> > +/* ISO transmission error enable. */
> > +#define EP_STS_EN_ISOERREN     BIT(15)
> > +/* Interrupt on Transmission complete enable. */
> > +#define EP_STS_EN_IOTEN                BIT(19)
> > +/* Setup Wait interrupt enable. */
> > +#define EP_STS_EN_STPWAITEN    BIT(31)
> > +
> > +/* DRBL- bitmasks */
> > +#define DB_VALUE_BY_INDEX(index) (1 << (index))
> > +#define DB_VALUE_EP0_OUT       BIT(0)
> > +#define DB_VALUE_EP0_IN                BIT(16)
> > +
> > +/* EP_IEN - bitmasks */
> > +#define EP_IEN(index)          (1 << (index))
> > +#define EP_IEN_EP_OUT0         BIT(0)
> > +#define EP_IEN_EP_IN0          BIT(16)
> > +
> > +/* EP_ISTS - bitmasks */
> > +#define EP_ISTS(index)         (1 << (index))
> > +#define EP_ISTS_EP_OUT0                BIT(0)
> > +#define EP_ISTS_EP_IN0         BIT(16)
> > +
> > +/* EP_PWR- bitmasks */
> > +/*Power Shut Off capability enable*/
> > +#define PUSB_PWR_PSO_EN                BIT(0)
> > +/*Power Shut Off capability disable*/
> > +#define PUSB_PWR_PSO_DS                BIT(1)
> > +/*
> > + * Enables turning-off Reference Clock.
> > + * This bit is optional and implemented only when support for OTG is
> > + * implemented (indicated by OTG_READY bit set to '1').
> > + */
> > +#define PUSB_PWR_STB_CLK_SWITCH_EN     BIT(8)
> > +/*
> > + * Status bit indicating that operation required by STB_CLK_SWITCH_EN write
> > + * is completed
> > + */
> > +#define PUSB_PWR_STB_CLK_SWITCH_DONE   BIT(9)
> > +/* This bit informs if Fast Registers Access is enabled. */
> > +#define PUSB_PWR_FST_REG_ACCESS_STAT   BIT(30)
> > +/* Fast Registers Access Enable. */
> > +#define PUSB_PWR_FST_REG_ACCESS        BIT(31)
> > +
> > +/* USB_CAP1- bitmasks */
> > +/*
> > + * SFR Interface type
> > + * These field reflects type of SFR interface implemented:
> > + * 0x0 - OCP
> > + * 0x1 - AHB,
> > + * 0x2 - PLB
> > + * 0x3 - AXI
> > + * 0x4-0xF - reserved
> > + */
> > +#define USB_CAP1_SFR_TYPE_MASK GENMASK(3, 0)
> > +#define DEV_SFR_TYPE_OCP(p)    (((p) & USB_CAP1_SFR_TYPE_MASK) == 0x0)
> > +#define DEV_SFR_TYPE_AHB(p)    (((p) & USB_CAP1_SFR_TYPE_MASK) == 0x1)
> > +#define DEV_SFR_TYPE_PLB(p)    (((p) & USB_CAP1_SFR_TYPE_MASK) == 0x2)
> > +#define DEV_SFR_TYPE_AXI(p)    (((p) & USB_CAP1_SFR_TYPE_MASK) == 0x3)
> > +/*
> > + * SFR Interface width
> > + * These field reflects width of SFR interface implemented:
> > + * 0x0 - 8 bit interface,
> > + * 0x1 - 16 bit interface,
> > + * 0x2 - 32 bit interface
> > + * 0x3 - 64 bit interface
> > + * 0x4-0xF - reserved
> > + */
> > +#define USB_CAP1_SFR_WIDTH_MASK        GENMASK(7, 4)
> > +#define DEV_SFR_WIDTH_8(p)     (((p) & USB_CAP1_SFR_WIDTH_MASK) == (0x0 << 4))
> > +#define DEV_SFR_WIDTH_16(p)    (((p) & USB_CAP1_SFR_WIDTH_MASK) == (0x1 << 4))
> > +#define DEV_SFR_WIDTH_32(p)    (((p) & USB_CAP1_SFR_WIDTH_MASK) == (0x2 << 4))
> > +#define DEV_SFR_WIDTH_64(p)    (((p) & USB_CAP1_SFR_WIDTH_MASK) == (0x3 << 4))
> > +/*
> > + * DMA Interface type
> > + * These field reflects type of DMA interface implemented:
> > + * 0x0 - OCP
> > + * 0x1 - AHB,
> > + * 0x2 - PLB
> > + * 0x3 - AXI
> > + * 0x4-0xF - reserved
> > + */
> > +#define USB_CAP1_DMA_TYPE_MASK GENMASK(11, 8)
> > +#define DEV_DMA_TYPE_OCP(p)    (((p) & USB_CAP1_DMA_TYPE_MASK) == (0x0 << 8))
> > +#define DEV_DMA_TYPE_AHB(p)    (((p) & USB_CAP1_DMA_TYPE_MASK) == (0x1 << 8))
> > +#define DEV_DMA_TYPE_PLB(p)    (((p) & USB_CAP1_DMA_TYPE_MASK) == (0x2 << 8))
> > +#define DEV_DMA_TYPE_AXI(p)    (((p) & USB_CAP1_DMA_TYPE_MASK) == (0x3 << 8))
> > +/*
> > + * DMA Interface width
> > + * These field reflects width of DMA interface implemented:
> > + * 0x0 - reserved,
> > + * 0x1 - reserved,
> > + * 0x2 - 32 bit interface
> > + * 0x3 - 64 bit interface
> > + * 0x4-0xF - reserved
> > + */
> > +#define USB_CAP1_DMA_WIDTH_MASK        GENMASK(15, 12)
> > +#define DEV_DMA_WIDTH_32(p)    (((p) & USB_CAP1_DMA_WIDTH_MASK) == (0x2 << 12))
> > +#define DEV_DMA_WIDTH_64(p)    (((p) & USB_CAP1_DMA_WIDTH_MASK) == (0x3 << 12))
> > +/*
> > + * USB3 PHY Interface type
> > + * These field reflects type of USB3 PHY interface implemented:
> > + * 0x0 - USB PIPE,
> > + * 0x1 - RMMI,
> > + * 0x2-0xF - reserved
> > + */
> > +#define USB_CAP1_U3PHY_TYPE_MASK GENMASK(19, 16)
> > +#define DEV_U3PHY_PIPE(p) (((p) & USB_CAP1_U3PHY_TYPE_MASK) == (0x0 << 16))
> > +#define DEV_U3PHY_RMMI(p) (((p) & USB_CAP1_U3PHY_TYPE_MASK) == (0x1 << 16))
> > +/*
> > + * USB3 PHY Interface width
> > + * These field reflects width of USB3 PHY interface implemented:
> > + * 0x0 - 8 bit PIPE interface,
> > + * 0x1 - 16 bit PIPE interface,
> > + * 0x2 - 32 bit PIPE interface,
> > + * 0x3 - 64 bit PIPE interface
> > + * 0x4-0xF - reserved
> > + * Note: When SSIC interface is implemented this field shows the width of
> > + * internal PIPE interface. The RMMI interface is always 20bit wide.
> > + */
> > +#define USB_CAP1_U3PHY_WIDTH_MASK GENMASK(23, 20)
> > +#define DEV_U3PHY_WIDTH_8(p) \
> > +       (((p) & USB_CAP1_U3PHY_WIDTH_MASK) == (0x0 << 20))
> > +#define DEV_U3PHY_WIDTH_16(p) \
> > +       (((p) & USB_CAP1_U3PHY_WIDTH_MASK) == (0x1 << 16))
> > +#define DEV_U3PHY_WIDTH_32(p) \
> > +       (((p) & USB_CAP1_U3PHY_WIDTH_MASK) == (0x2 << 20))
> > +#define DEV_U3PHY_WIDTH_64(p) \
> > +       (((p) & USB_CAP1_U3PHY_WIDTH_MASK) == (0x3 << 16))
> > +
> > +/*
> > + * USB2 PHY Interface enable
> > + * These field informs if USB2 PHY interface is implemented:
> > + * 0x0 - interface NOT implemented,
> > + * 0x1 - interface implemented
> > + */
> > +#define USB_CAP1_U2PHY_EN(p)   ((p) & BIT(24))
> > +/*
> > + * USB2 PHY Interface type
> > + * These field reflects type of USB2 PHY interface implemented:
> > + * 0x0 - UTMI,
> > + * 0x1 - ULPI
> > + */
> > +#define DEV_U2PHY_ULPI(p)      ((p) & BIT(25))
> > +/*
> > + * USB2 PHY Interface width
> > + * These field reflects width of USB2 PHY interface implemented:
> > + * 0x0 - 8 bit interface,
> > + * 0x1 - 16 bit interface,
> > + * Note: The ULPI interface is always 8bit wide.
> > + */
> > +#define DEV_U2PHY_WIDTH_16(p)  ((p) & BIT(26))
> > +/*
> > + * OTG Ready
> > + * 0x0 - pure device mode
> > + * 0x1 - some features and ports for CDNS USB OTG controller are implemented.
> > + */
> > +#define USB_CAP1_OTG_READY(p)  ((p) & BIT(27))
> > +
> > +/* USB_CAP2- bitmasks */
> > +/*
> > + * The actual size of the connected On-chip RAM memory in kB:
> > + * - 0 means 256 kB (max supported mem size)
> > + * - value other than 0 reflects the mem size in kB
> > + */
> > +#define USB_CAP2_ACTUAL_MEM_SIZE(p) ((p) & GENMASK(7, 0))
> > +/*
> > + * Max supported mem size
> > + * These field reflects width of on-chip RAM address bus width,
> > + * which determines max supported mem size:
> > + * 0x0-0x7 - reserved,
> > + * 0x8 - support for 4kB mem,
> > + * 0x9 - support for 8kB mem,
> > + * 0xA - support for 16kB mem,
> > + * 0xB - support for 32kB mem,
> > + * 0xC - support for 64kB mem,
> > + * 0xD - support for 128kB mem,
> > + * 0xE - support for 256kB mem,
> > + * 0xF - reserved
> > + */
> > +#define USB_CAP2_MAX_MEM_SIZE(p) ((p) & GENMASK(11, 8))
> > +
> > +/* USB_CAP3- bitmasks */
> > +#define EP_IS_IMPLEMENTED(reg, index) ((reg) & (1 << (index)))
> > +
> > +/* USB_CAP4- bitmasks */
> > +#define EP_SUPPORT_ISO(reg, index) ((reg) & (1 << (index)))
> > +
> > +/* USB_CAP5- bitmasks */
> > +#define EP_SUPPORT_STREAM(reg, index) ((reg) & (1 << (index)))
> > +
> > +/* USB_CAP6- bitmasks */
> > +/* The USBSS-DEV Controller  Internal build number. */
> > +#define GET_DEV_VERSION__INTERNAL_NUMBER(p) ((p) & GENMASK(7, 0))
> > +/* The USBSS-DEV Controller  version number. */
> > +#define GET_DEV_VERSION(p) ((p) & GENMASK(31, 8))
> > +
> > +/* DBG_LINK1- bitmasks */
> > +/*
> > + * LFPS_MIN_DET_U1_EXIT value This parameter configures the minimum
> > + * time required for decoding the received LFPS as an LFPS.U1_Exit.
> > + */
> > +#define DBG_LINK1_LFPS_MIN_DET_U1_EXIT(p)      ((p) & GENMASK(7, 0))
> > +/*
> > + * LFPS_MIN_GEN_U1_EXIT value This parameter configures the minimum time for
> > + * phytxelecidle deassertion when LFPS.U1_Exit
> > + */
> > +#define DBG_LINK1_LFPS_MIN_GEN_U1_EXIT(p)      (((p) << 8) & GENMASK(15, 8))
> > +/*
> > + * RXDET_BREAK_DIS value This parameter configures terminating the Far-end
> > + * Receiver termination detection sequence:
> > + * 0: it is possible that USBSS_DEV will terminate Farend receiver
> > + *    termination detection sequence
> > + * 1: USBSS_DEV will not terminate Far-end receiver termination
> > + *    detection sequence
> > + */
> > +#define DBG_LINK1_RXDET_BREAK_DIS              BIT(16)
> > +/* LFPS_GEN_PING value This parameter configures the LFPS.Ping generation */
> > +#define DBG_LINK1_LFPS_GEN_PING(p)             (((p) << 17) & GENMASK(21, 17))
> > +/*
> > + * Set the LFPS_MIN_DET_U1_EXIT value Writing '1' to this bit writes the
> > + * LFPS_MIN_DET_U1_EXIT field value to the device. This bit is automatically
> > + * cleared. Writing '0' has no effect
> > + */
> > +#define DBG_LINK1_LFPS_MIN_DET_U1_EXIT_SET     BIT(24)
> > +/*
> > + * Set the LFPS_MIN_GEN_U1_EXIT value. Writing '1' to this bit writes the
> > + * LFPS_MIN_GEN_U1_EXIT field value to the device. This bit is automatically
> > + * cleared. Writing '0' has no effect
> > + */
> > +#define DBG_LINK1_LFPS_MIN_GEN_U1_EXIT_SET     BIT(25)
> > +/*
> > + * Set the RXDET_BREAK_DIS value Writing '1' to this bit writes
> > + * the RXDET_BREAK_DIS field value to the device. This bit is automatically
> > + * cleared. Writing '0' has no effect
> > + */
> > +#define DBG_LINK1_RXDET_BREAK_DIS_SET          BIT(26)
> > +/*
> > + * Set the LFPS_GEN_PING_SET value Writing '1' to this bit writes
> > + * the LFPS_GEN_PING field value to the device. This bit is automatically
> > + * cleared. Writing '0' has no effect."
> > + */
> > +#define DBG_LINK1_LFPS_GEN_PING_SET            BIT(27)
> > +
> > +#define gadget_to_cdns3_device(g) (container_of(g, struct cdns3_device, gadget))
> > +
> > +#define ep_to_cdns3_ep(ep) (container_of(ep, struct cdns3_endpoint, endpoint))
> > +
> > +/*-------------------------------------------------------------------------*/
> > +/*
> > + * USBSS-DEV DMA interface.
> > + */
> > +#define TRBS_PER_SEGMENT       40
> > +
> > +#define ISO_MAX_INTERVAL       10
> > +
> > +/*
> > + *Only for ISOC endpoints - maximum number of TRBs is calculated as
> > + * pow(2, bInterval-1) * number of usb requests. It is limitation made by
> > + * driver to save memory. Controller must prepare TRB for each ITP even
> > + * if bInterval > 1. It's the reason why driver needs so many TRBs for
> > + * isochronous endpoints.
> > + */
> > +#define TRBS_PER_ISOC_SEGMENT  (ISO_MAX_INTERVAL * 8)
> > +
> > +#define GET_TRBS_PER_SEGMENT(ep_type) ((ep_type) == USB_ENDPOINT_XFER_ISOC ? \
> > +                                     TRBS_PER_ISOC_SEGMENT : TRBS_PER_SEGMENT)
> > +/**
> > + * struct cdns3_trb - represent Transfer Descriptor block.
> > + * @buffer:    pointer to buffer data
> > + * @length:    length of data
> > + * @control:   control flags.
> > + *
> > + * This structure describes transfer block serviced by DMA module.
> > + */
> > +struct cdns3_trb {
> > +       __le32 buffer;
> > +       __le32 length;
> > +       __le32 control;
> > +};
> > +
> > +#define TRB_SIZE               (sizeof(struct cdns3_trb))
> > +#define TRB_RING_SIZE          (TRB_SIZE * TRBS_PER_SEGMENT)
> > +#define TRB_ISO_RING_SIZE      (TRB_SIZE * TRBS_PER_ISOC_SEGMENT)
> > +#define TRB_CTRL_RING_SIZE     (TRB_SIZE * 2)
> > +
> > +/* TRB bit mask */
> > +#define TRB_TYPE_BITMASK       GENMASK(15, 10)
> > +#define TRB_TYPE(p)            ((p) << 10)
> > +#define TRB_FIELD_TO_TYPE(p)   (((p) & TRB_TYPE_BITMASK) >> 10)
> > +
> > +/* TRB type IDs */
> > +/* bulk, interrupt, isoc , and control data stage */
> > +#define TRB_NORMAL             1
> > +/* TRB for linking ring segments */
> > +#define TRB_LINK               6
> > +
> > +/* Cycle bit - indicates TRB ownership by driver or hw*/
> > +#define TRB_CYCLE              BIT(0)
> > +/*
> > + * When set to '1', the device will toggle its interpretation of the Cycle bit
> > + */
> > +#define TRB_TOGGLE             BIT(1)
> > +
> > +/* Interrupt on short packet*/
> > +#define TRB_ISP                        BIT(2)
> > +/*Setting this bit enables FIFO DMA operation mode*/
> > +#define TRB_FIFO_MODE          BIT(3)
> > +/* Set PCIe no snoop attribute */
> > +#define TRB_CHAIN              BIT(4)
> > +/* Interrupt on completion */
> > +#define TRB_IOC                        BIT(5)
> > +
> > +/* stream ID bitmasks. */
> > +#define TRB_STREAM_ID(p)       ((p) & GENMASK(31, 16))
> > +
> > +/* transfer_len bitmasks. */
> > +#define TRB_LEN(p)             ((p) & GENMASK(16, 0))
> > +
> > +/* transfer_len bitmasks - bits 31:24 */
> > +#define TRB_BURST_LEN(p)       ((p) & GENMASK(31, 24))
> > +
> > +/* Data buffer pointer bitmasks*/
> > +#define TRB_BUFFER(p)          ((p) & GENMASK(31, 0))
> > +
> > +/*-------------------------------------------------------------------------*/
> > +/* Driver numeric constants */
> > +
> > +/* Such declaration should be added to ch9.h */
> > +#define USB_DEVICE_MAX_ADDRESS         127
> > +
> > +/* Endpoint init values */
> > +#define CDNS3_EP_MAX_PACKET_LIMIT      1024
> > +#define CDNS3_EP_MAX_STREAMS           15
> > +#define CDNS3_EP0_MAX_PACKET_LIMIT     512
> > +
> > +/* All endpoints including EP0 */
> > +#define CDNS3_ENDPOINTS_MAX_COUNT      32
> > +#define CDNS3_EP_ZLP_BUF_SIZE          1024
> > +
> > +#define CDNS3_EP_BUF_SIZE              2       /* KB */
> > +#define CDNS3_ALIGNED_BUF_SIZE         16384   /* Bytes */
> > +#define CDNS3_MAX_NUM_DESCMISS_BUF     32
> > +#define CDNS3_DESCMIS_BUF_SIZE         2048    /* Bytes */
> > +/*-------------------------------------------------------------------------*/
> > +/* Used structs */
> > +
> > +struct cdns3_device;
> > +
> > +/**
> > + * struct cdns3_endpoint - extended device side representation of USB endpoint.
> > + * @endpoint: usb endpoint
> > + * @pending_req_list: list of requests queuing on transfer ring.
> > + * @deferred_req_list: list of requests waiting for queuing on transfer ring.
> > + * @descmiss_req_list: list of requests internally allocated by driver (WA1).
> > + * @trb_pool: transfer ring - array of transaction buffers
> > + * @trb_pool_dma: dma address of transfer ring
> > + * @cdns3_dev: device associated with this endpoint
> > + * @name: a human readable name e.g. ep1out
> > + * @flags: specify the current state of endpoint
> > + * @descmis_req: internal transfer object used for getting data from on-chip
> > + *     buffer. It can happen only if function driver doesn't send usb_request
> > + *     object on time.
> > + * @aligned_buff: aligned to 8 bytes data buffer. Buffer address used in
> > + *     TRB shall be aligned to 8.
> > + * @aligned_dma_addr: dma address of aligned_buff
> > + * @dir: endpoint direction
> > + * @num: endpoint number (1 - 15)
> > + * @type: set to bmAttributes & USB_ENDPOINT_XFERTYPE_MASK
> > + * @interval: interval between packets used for ISOC endpoint.
> > + * @free_trbs: number of free TRBs in transfer ring
> > + * @num_trbs: number of all TRBs in transfer ring
> > + * @pcs: producer cycle state
> > + * @ccs: consumer cycle state
> > + * @enqueue: enqueue index in transfer ring
> > + * @dequeue: dequeue index in transfer ring
> > + */
> > +struct cdns3_endpoint {
> > +       struct usb_ep           endpoint;
> > +       struct list_head        pending_req_list;
> > +       struct list_head        deferred_req_list;
> > +       struct list_head        descmiss_req_list;
> > +
> > +       struct cdns3_trb        *trb_pool;
> > +       dma_addr_t              trb_pool_dma;
> > +
> > +       struct cdns3_device     *cdns3_dev;
> > +       char                    name[20];
> > +
> > +#define EP_ENABLED             BIT(0)
> > +#define EP_STALL               BIT(1)
> > +#define EP_WEDGE               BIT(2)
> > +#define EP_TRANSFER_STARTED    BIT(3)
> > +#define EP_UPDATE_EP_TRBADDR   BIT(4)
> > +#define EP_PENDING_REQUEST     BIT(5)
> > +#define EP_RING_FULL           BIT(6)
> > +#define EP_CLAIMED             BIT(7)
> > +#define EP_QUIRK_EXTRA_BUF_DET BIT(8)
> > +#define EP_QUIRK_EXTRA_BUF_EN  BIT(9)
> > +#define EP_QUIRK_END_TRANSFER  BIT(10)
> > +
> > +       u32                     flags;
> > +
> > +       struct cdns3_request    *descmis_req;
> > +
> > +       void                    *aligned_buff;
> > +       dma_addr_t              aligned_dma_addr;
> > +       u8                      dir;
> > +       u8                      num;
> > +       u8                      type;
> > +       int                     interval;
> > +
> > +       int                     free_trbs;
> > +       int                     num_trbs;
> > +       u8                      pcs;
> > +       u8                      ccs;
> > +       int                     enqueue;
> > +       int                     dequeue;
> > +};
> > +
> > +/**
> > + * struct cdns3_request - extended device side representation of usb_request
> > + *                        object .
> > + * @request: generic usb_request object describing single I/O request.
> > + * @priv_ep: extended representation of usb_ep object
> > + * @trb: the first TRB association with this request
> > + * @start_trb: number of the first TRB in transfer ring
> > + * @end_trb: number of the last TRB in transfer ring
> > + * @flags: flag specifying special usage of request
> > + * @list: used by internally allocated request to add to descmiss_req_list.
> > + */
> > +struct cdns3_request {
> > +       struct usb_request      request;
> > +       struct cdns3_endpoint   *priv_ep;
> > +       struct cdns3_trb        *trb;
> > +       int                     start_trb;
> > +       int                     end_trb;
> > +#define REQUEST_PENDING                BIT(0)
> > +#define REQUEST_INTERNAL       BIT(1)
> > +#define REQUEST_INTERNAL_CH    BIT(2)
> > +#define REQUEST_ZLP            BIT(3)
> > +       u32                     flags;
> > +       struct list_head        list;
> > +};
> > +
> > +#define to_cdns3_request(r) (container_of(r, struct cdns3_request, request))
> > +
> > +/*Stages used during enumeration process.*/
> > +#define CDNS3_SETUP_STAGE              0x0
> > +#define CDNS3_DATA_STAGE               0x1
> > +#define CDNS3_STATUS_STAGE             0x2
> > +
> > +/**
> > + * struct cdns3_device - represent USB device.
> > + * @dev: pointer to device structure associated whit this controller
> > + * @sysdev: pointer to the DMA capable device
> > + * @gadget: device side representation of the peripheral controller
> > + * @gadget_driver: pointer to the gadget driver
> > + * @dev_ver: device controller version.
> > + * @lock: for synchronizing
> > + * @regs: base address for device side registers
> > + * @setup_buf: used while processing usb control requests
> > + * @setup_dma: dma address for setup_buf
> > + * @zlp_buf - zlp buffer
> > + * @ep0_stage: ep0 stage during enumeration process.
> > + * @ep0_data_dir: direction for control transfer
> > + * @eps: array of pointers to all endpoints with exclusion ep0
> > + * @selected_ep: actually selected endpoint. It's used only to improve
> > + *               performance.
> > + * @isoch_delay: value from Set Isoch Delay request. Only valid on SS/SSP.
> > + * @u1_allowed: allow device transition to u1 state
> > + * @u2_allowed: allow device transition to u2 state
> > + * @is_selfpowered: device is self powered
> > + * @setup_pending: setup packet is processing by gadget driver
> > + * @hw_configured_flag: hardware endpoint configuration was set.
> > + * @wake_up_flag: allow device to remote up the host
> > + * @status_completion_no_call: indicate that driver is waiting for status s
> > + *     stage completion. It's used in deferred SET_CONFIGURATION request.
> > + * @onchip_mem_allocated_size: actual size of on-chip memory assigned
> > + *     to endpoints
> > + * @pending_status_wq: workqueue handling status stage for deferred requests.
> > + * @pending_status_request: request for which status stage was deferred
> > + */
> > +struct cdns3_device {
> > +       struct device                   *dev;
> > +       struct device                   *sysdev;
> > +
> > +       struct usb_gadget               gadget;
> > +       struct usb_gadget_driver        *gadget_driver;
> > +
> > +#define CDNS_REVISION_V0               0x00024501
> > +#define CDNS_REVISION_V1               0x00024509
> > +       u32                             dev_ver;
> > +
> > +       /* generic spin-lock for drivers */
> > +       spinlock_t                      lock;
> > +
> > +       struct cdns3_usb_regs           __iomem *regs;
> > +
> > +       struct usb_ctrlrequest          *setup_buf;
> > +       dma_addr_t                      setup_dma;
> > +       void                            *zlp_buf;
> > +
> > +       u8                              ep0_stage;
> > +       int                             ep0_data_dir;
> > +
> > +       struct cdns3_endpoint           *eps[CDNS3_ENDPOINTS_MAX_COUNT];
> > +
> > +       u32                             selected_ep;
> > +       u16                             isoch_delay;
> > +
> > +       unsigned                        u1_allowed:1;
> > +       unsigned                        u2_allowed:1;
> > +       unsigned                        is_selfpowered:1;
> > +       unsigned                        setup_pending:1;
> > +       int                             hw_configured_flag:1;
> > +       int                             wake_up_flag:1;
> > +       unsigned                        status_completion_no_call:1;
> > +
> > +       struct work_struct              pending_status_wq;
> > +       struct usb_request              *pending_status_request;
> > +
> > +       /*in KB */
> > +       int                             onchip_mem_allocated_size;
> > +};
> > +
> > +int cdns3_handshake(void __iomem *ptr, u32 mask, u32 done, int usec);
> > +void cdns3_set_register_bit(void __iomem *ptr, u32 mask);
> > +dma_addr_t cdns3_trb_virt_to_dma(struct cdns3_endpoint *priv_ep,
> > +                                struct cdns3_trb *trb);
> > +enum usb_device_speed cdns3_get_speed(struct cdns3_device *priv_dev);
> > +void cdns3_pending_setup_status_handler(struct work_struct *work);
> > +void cdns3_hw_reset_eps_config(struct cdns3_device *priv_dev);
> > +void cdns3_set_hw_configuration(struct cdns3_device *priv_dev);
> > +void cdns3_select_ep(struct cdns3_device *priv_dev, u32 ep);
> > +void cdns3_allow_enable_l1(struct cdns3_device *priv_dev, int enable);
> > +struct usb_request *cdns3_next_request(struct list_head *list);
> > +int cdns3_ep_run_transfer(struct cdns3_endpoint *priv_ep,
> > +                         struct usb_request *request);
> > +int cdns3_allocate_trb_pool(struct cdns3_endpoint *priv_ep);
> > +u8 cdns3_ep_addr_to_index(u8 ep_addr);
> > +int cdns3_gadget_ep_set_wedge(struct usb_ep *ep);
> > +int cdns3_gadget_ep_set_halt(struct usb_ep *ep, int value);
> > +struct usb_request *cdns3_gadget_ep_alloc_request(struct usb_ep *ep,
> > +                                                 gfp_t gfp_flags);
> > +void cdns3_gadget_ep_free_request(struct usb_ep *ep,
> > +                                 struct usb_request *request);
> > +int cdns3_gadget_ep_dequeue(struct usb_ep *ep, struct usb_request *request);
> > +void cdns3_gadget_giveback(struct cdns3_endpoint *priv_ep,
> > +                          struct cdns3_request *priv_req,
> > +                          int status);
> > +
> > +int cdns3_init_ep0(struct cdns3_device *priv_dev,
> > +                  struct cdns3_endpoint *priv_ep);
> > +void cdns3_ep0_config(struct cdns3_device *priv_dev);
> > +void cdns3_ep_config(struct cdns3_endpoint *priv_ep);
> > +void cdns3_check_ep0_interrupt_proceed(struct cdns3_device *priv_dev, int dir);
> > +
> > +#endif /* __LINUX_CDNS3_GADGET */
> > diff --git a/drivers/usb/cdns3/host-export.h b/drivers/usb/cdns3/host-export.h
> > new file mode 100644
> > index 000000000000..b498a170b7e8
> > --- /dev/null
> > +++ b/drivers/usb/cdns3/host-export.h
> > @@ -0,0 +1,28 @@
> > +/* SPDX-License-Identifier: GPL-2.0 */
> > +/*
> > + * Cadence USBSS DRD Driver - Host Export APIs
> > + *
> > + * Copyright (C) 2017-2018 NXP
> > + *
> > + * Authors: Peter Chen <peter.chen@nxp.com>
> > + */
> > +#ifndef __LINUX_CDNS3_HOST_EXPORT
> > +#define __LINUX_CDNS3_HOST_EXPORT
> > +
> > +#ifdef CONFIG_USB_CDNS3_HOST
> > +
> > +int cdns3_host_init(struct cdns3 *cdns);
> > +void cdns3_host_exit(struct cdns3 *cdns);
> > +
> > +#else
> > +
> > +static inline int cdns3_host_init(struct cdns3 *cdns)
> > +{
> > +       return -ENXIO;
> > +}
> > +
> > +static inline void cdns3_host_exit(struct cdns3 *cdns) { }
> > +
> > +#endif /* CONFIG_USB_CDNS3_HOST */
> > +
> > +#endif /* __LINUX_CDNS3_HOST_EXPORT */
> > diff --git a/drivers/usb/cdns3/host.c b/drivers/usb/cdns3/host.c
> > new file mode 100644
> > index 000000000000..b43b0236a885
> > --- /dev/null
> > +++ b/drivers/usb/cdns3/host.c
> > @@ -0,0 +1,72 @@
> > +// SPDX-License-Identifier: GPL-2.0
> > +/*
> > + * Cadence USBSS DRD Driver - host side
> > + *
> > + * Copyright (C) 2018 Cadence Design Systems.
> > + * Copyright (C) 2017-2018 NXP
> > + *
> > + * Authors: Peter Chen <peter.chen@nxp.com>
> > + *         Pawel Laszczak <pawell@cadence.com>
> > + */
> > +
> > +#include <linux/platform_device.h>
> > +#include "core.h"
> > +
> > +static int __cdns3_host_init(struct cdns3 *cdns)
> > +{
> > +       struct platform_device *xhci;
> > +       int ret;
> > +
> > +       xhci = platform_device_alloc("xhci-hcd", PLATFORM_DEVID_AUTO);
> > +       if (!xhci) {
> > +               dev_err(cdns->dev, "couldn't allocate xHCI device\n");
> > +               return -ENOMEM;
> > +       }
> > +
> > +       xhci->dev.parent = cdns->dev;
> > +       cdns->host_dev = xhci;
> > +
> > +       ret = platform_device_add_resources(xhci, cdns->xhci_res,
> > +                                           CDNS3_XHCI_RESOURCES_NUM);
> > +       if (ret) {
> > +               dev_err(cdns->dev, "couldn't add resources to xHCI device\n");
> > +               goto err1;
> > +       }
> > +
> > +       ret = platform_device_add(xhci);
> > +       if (ret) {
> > +               dev_err(cdns->dev, "failed to register xHCI device\n");
> > +               goto err1;
> > +       }
> > +
> > +       return 0;
> > +err1:
> > +       platform_device_put(xhci);
> > +       return ret;
> > +}
> > +
> > +static void cdns3_host_exit(struct cdns3 *cdns)
> > +{
> > +       platform_device_unregister(cdns->host_dev);
> > +       cdns->host_dev = NULL;
> > +}
> > +
> > +int cdns3_host_init(struct cdns3 *cdns)
> > +{
> > +       struct cdns3_role_driver *rdrv;
> > +
> > +       rdrv = devm_kzalloc(cdns->dev, sizeof(*rdrv), GFP_KERNEL);
> > +       if (!rdrv)
> > +               return -ENOMEM;
> > +
> > +       rdrv->start     = __cdns3_host_init;
> > +       rdrv->stop      = cdns3_host_exit;
> > +       rdrv->state     = CDNS3_ROLE_STATE_INACTIVE;
> > +       rdrv->suspend   = NULL;
> > +       rdrv->resume    = NULL;
> > +       rdrv->name      = "host";
> > +
> > +       cdns->roles[CDNS3_ROLE_HOST] = rdrv;
> > +
> > +       return 0;
> > +}
> > diff --git a/drivers/usb/cdns3/trace.c b/drivers/usb/cdns3/trace.c
> > new file mode 100644
> > index 000000000000..587ae08e019d
> > --- /dev/null
> > +++ b/drivers/usb/cdns3/trace.c
> > @@ -0,0 +1,11 @@
> > +// SPDX-License-Identifier: GPL-2.0
> > +/*
> > + * USBSS device controller driver Trace Support
> > + *
> > + * Copyright (C) 2018 Cadence.
> > + *
> > + * Author: Pawel Laszczak <pawell@cadence.com>
> > + */
> > +
> > +#define CREATE_TRACE_POINTS
> > +#include "trace.h"
> > diff --git a/drivers/usb/cdns3/trace.h b/drivers/usb/cdns3/trace.h
> > new file mode 100644
> > index 000000000000..f3d7a91fae86
> > --- /dev/null
> > +++ b/drivers/usb/cdns3/trace.h
> > @@ -0,0 +1,389 @@
> > +/* SPDX-License-Identifier: GPL-2.0 */
> > +/*
> > + * USBSS device controller driver.
> > + * Trace support header file.
> > + *
> > + * Copyright (C) 2018 Cadence.
> > + *
> > + * Author: Pawel Laszczak <pawell@cadence.com>
> > + */
> > +
> > +#undef TRACE_SYSTEM
> > +#define TRACE_SYSTEM cdns3
> > +
> > +#if !defined(__LINUX_CDNS3_TRACE) || defined(TRACE_HEADER_MULTI_READ)
> > +#define __LINUX_CDNS3_TRACE
> > +
> > +#include <linux/types.h>
> > +#include <linux/tracepoint.h>
> > +#include <asm/byteorder.h>
> > +#include <linux/usb/ch9.h>
> > +#include "core.h"
> > +#include "gadget.h"
> > +#include "debug.h"
> > +
> > +#define CDNS3_MSG_MAX  500
> > +
> > +DECLARE_EVENT_CLASS(cdns3_log_doorbell,
> > +       TP_PROTO(const char *ep_name, u32 ep_trbaddr),
> > +       TP_ARGS(ep_name, ep_trbaddr),
> > +       TP_STRUCT__entry(
> > +               __string(name, ep_name)
> > +               __field(u32, ep_trbaddr)
> > +       ),
> > +       TP_fast_assign(
> > +               __assign_str(name, ep_name);
> > +               __entry->ep_trbaddr = ep_trbaddr;
> > +       ),
> > +       TP_printk("//Ding Dong %s, ep_trbaddr %08x", __get_str(name),
> > +                 __entry->ep_trbaddr)
> > +);
> > +
> > +DEFINE_EVENT(cdns3_log_doorbell, cdns3_doorbell_ep0,
> > +       TP_PROTO(const char *ep_name, u32 ep_trbaddr),
> > +       TP_ARGS(ep_name, ep_trbaddr)
> > +);
> > +
> > +DEFINE_EVENT(cdns3_log_doorbell, cdns3_doorbell_epx,
> > +       TP_PROTO(const char *ep_name, u32 ep_trbaddr),
> > +       TP_ARGS(ep_name, ep_trbaddr)
> > +);
> > +
> > +DECLARE_EVENT_CLASS(cdns3_log_usb_irq,
> > +       TP_PROTO(struct cdns3_device *priv_dev, u32 usb_ists),
> > +       TP_ARGS(priv_dev, usb_ists),
> > +       TP_STRUCT__entry(
> > +               __field(enum usb_device_speed, speed)
> > +               __field(u32, usb_ists)
> > +               __dynamic_array(char, str, CDNS3_MSG_MAX)
> > +       ),
> > +       TP_fast_assign(
> > +               __entry->speed = cdns3_get_speed(priv_dev);
> > +               __entry->usb_ists = usb_ists;
> > +       ),
> > +       TP_printk("%s", cdns3_decode_usb_irq(__get_str(str), __entry->speed,
> > +                                            __entry->usb_ists))
> > +);
> > +
> > +DEFINE_EVENT(cdns3_log_usb_irq, cdns3_usb_irq,
> > +       TP_PROTO(struct cdns3_device *priv_dev, u32 usb_ists),
> > +       TP_ARGS(priv_dev, usb_ists)
> > +);
> > +
> > +DECLARE_EVENT_CLASS(cdns3_log_epx_irq,
> > +       TP_PROTO(struct cdns3_device *priv_dev, struct cdns3_endpoint *priv_ep),
> > +       TP_ARGS(priv_dev, priv_ep),
> > +       TP_STRUCT__entry(
> > +               __string(ep_name, priv_ep->name)
> > +               __field(u32, ep_sts)
> > +               __field(u32, ep_traddr)
> > +               __dynamic_array(char, str, CDNS3_MSG_MAX)
> > +       ),
> > +       TP_fast_assign(
> > +               __assign_str(ep_name, priv_ep->name);
> > +               __entry->ep_sts = readl(&priv_dev->regs->ep_sts);
> > +               __entry->ep_traddr = readl(&priv_dev->regs->ep_traddr);
> > +       ),
> > +       TP_printk("%s, ep_traddr: %08x",
> > +                 cdns3_decode_epx_irq(__get_str(str),
> > +                                      __get_str(ep_name),
> > +                                      __entry->ep_sts),
> > +                 __entry->ep_traddr)
> > +);
> > +
> > +DEFINE_EVENT(cdns3_log_epx_irq, cdns3_epx_irq,
> > +       TP_PROTO(struct cdns3_device *priv_dev, struct cdns3_endpoint *priv_ep),
> > +       TP_ARGS(priv_dev, priv_ep)
> > +);
> > +
> > +DECLARE_EVENT_CLASS(cdns3_log_ep0_irq,
> > +       TP_PROTO(struct cdns3_device *priv_dev,  u32 ep_sts),
> > +       TP_ARGS(priv_dev, ep_sts),
> > +       TP_STRUCT__entry(
> > +               __field(int, ep_dir)
> > +               __field(u32, ep_sts)
> > +               __dynamic_array(char, str, CDNS3_MSG_MAX)
> > +       ),
> > +       TP_fast_assign(
> > +               __entry->ep_dir = priv_dev->ep0_data_dir;
> > +               __entry->ep_sts = ep_sts;
> > +       ),
> > +       TP_printk("%s", cdns3_decode_ep0_irq(__get_str(str),
> > +                                            __entry->ep_dir,
> > +                                            __entry->ep_sts))
> > +);
> > +
> > +DEFINE_EVENT(cdns3_log_ep0_irq, cdns3_ep0_irq,
> > +       TP_PROTO(struct cdns3_device *priv_dev, u32 ep_sts),
> > +       TP_ARGS(priv_dev, ep_sts)
> > +);
> > +
> > +DECLARE_EVENT_CLASS(cdns3_log_ctrl,
> > +       TP_PROTO(struct usb_ctrlrequest *ctrl),
> > +       TP_ARGS(ctrl),
> > +       TP_STRUCT__entry(
> > +               __field(u8, bRequestType)
> > +               __field(u8, bRequest)
> > +               __field(u16, wValue)
> > +               __field(u16, wIndex)
> > +               __field(u16, wLength)
> > +               __dynamic_array(char, str, CDNS3_MSG_MAX)
> > +       ),
> > +       TP_fast_assign(
> > +               __entry->bRequestType = ctrl->bRequestType;
> > +               __entry->bRequest = ctrl->bRequest;
> > +               __entry->wValue = le16_to_cpu(ctrl->wValue);
> > +               __entry->wIndex = le16_to_cpu(ctrl->wIndex);
> > +               __entry->wLength = le16_to_cpu(ctrl->wLength);
> > +       ),
> > +       TP_printk("%s", usb_decode_ctrl(__get_str(str), __entry->bRequestType,
> > +                                       __entry->bRequest, __entry->wValue,
> > +                                       __entry->wIndex, __entry->wLength)
> > +       )
> > +);
> > +
> > +DEFINE_EVENT(cdns3_log_ctrl, cdns3_ctrl_req,
> > +       TP_PROTO(struct usb_ctrlrequest *ctrl),
> > +       TP_ARGS(ctrl)
> > +);
> > +
> > +DECLARE_EVENT_CLASS(cdns3_log_request,
> > +       TP_PROTO(struct cdns3_request *req),
> > +       TP_ARGS(req),
> > +       TP_STRUCT__entry(
> > +               __string(name, req->priv_ep->name)
> > +               __field(struct cdns3_request *, req)
> > +               __field(unsigned int, actual)
> > +               __field(unsigned int, length)
> > +               __field(int, status)
> > +               __field(int, zero)
> > +               __field(int, short_not_ok)
> > +               __field(int, no_interrupt)
> > +               __field(int, start_trb)
> > +               __field(int, end_trb)
> > +               __field(struct cdns3_trb *, start_trb_addr)
> > +               __field(int, flags)
> > +       ),
> > +       TP_fast_assign(
> > +               __assign_str(name, req->priv_ep->name);
> > +               __entry->req = req;
> > +               __entry->actual = req->request.actual;
> > +               __entry->length = req->request.length;
> > +               __entry->status = req->request.status;
> > +               __entry->zero = req->request.zero;
> > +               __entry->short_not_ok = req->request.short_not_ok;
> > +               __entry->no_interrupt = req->request.no_interrupt;
> > +               __entry->start_trb = req->start_trb;
> > +               __entry->end_trb = req->end_trb;
> > +               __entry->start_trb_addr = req->trb;
> > +               __entry->flags = req->flags;
> > +       ),
> > +       TP_printk("%s: req: %p, length: %u/%u %s%s%s, status: %d,"
> > +                 " trb: [start:%d, end:%d: virt addr %pa], flags:%x ",
> > +               __get_str(name), __entry->req, __entry->actual, __entry->length,
> > +               __entry->zero ? "zero | " : "",
> > +               __entry->short_not_ok ? "short | " : "",
> > +               __entry->no_interrupt ? "no int" : "",
> > +               __entry->status,
> > +               __entry->start_trb,
> > +               __entry->end_trb,
> > +               __entry->start_trb_addr,
> > +               __entry->flags
> > +       )
> > +);
> > +
> > +DEFINE_EVENT(cdns3_log_request, cdns3_alloc_request,
> > +       TP_PROTO(struct cdns3_request *req),
> > +       TP_ARGS(req)
> > +);
> > +
> > +DEFINE_EVENT(cdns3_log_request, cdns3_free_request,
> > +       TP_PROTO(struct cdns3_request *req),
> > +       TP_ARGS(req)
> > +);
> > +
> > +DEFINE_EVENT(cdns3_log_request, cdns3_ep_queue,
> > +       TP_PROTO(struct cdns3_request *req),
> > +       TP_ARGS(req)
> > +);
> > +
> > +DEFINE_EVENT(cdns3_log_request, cdns3_ep_dequeue,
> > +       TP_PROTO(struct cdns3_request *req),
> > +       TP_ARGS(req)
> > +);
> > +
> > +DEFINE_EVENT(cdns3_log_request, cdns3_gadget_giveback,
> > +       TP_PROTO(struct cdns3_request *req),
> > +       TP_ARGS(req)
> > +);
> > +
> > +DECLARE_EVENT_CLASS(cdns3_log_trb,
> > +       TP_PROTO(struct cdns3_endpoint *priv_ep, struct cdns3_trb *trb),
> > +       TP_ARGS(priv_ep, trb),
> > +       TP_STRUCT__entry(
> > +               __string(name, priv_ep->name)
> > +               __field(struct cdns3_trb *, trb)
> > +               __field(u32, buffer)
> > +               __field(u32, length)
> > +               __field(u32, control)
> > +               __field(u32, type)
> > +       ),
> > +       TP_fast_assign(
> > +               __assign_str(name, priv_ep->name);
> > +               __entry->trb = trb;
> > +               __entry->buffer = trb->buffer;
> > +               __entry->length = trb->length;
> > +               __entry->control = trb->control;
> > +               __entry->type = usb_endpoint_type(priv_ep->endpoint.desc);
> > +       ),
> > +       TP_printk("%s: trb 0x%pa, dma buf: 0x%08x, size: %ld, ctrl: 0x%08x (%s%s%s%s%s%s%s)",
> > +               __get_str(name), __entry->trb, __entry->buffer,
> > +               TRB_LEN(__entry->length), __entry->control,
> > +               __entry->control & TRB_CYCLE ? "C=1, " : "C=0, ",
> > +               __entry->control & TRB_TOGGLE ? "T=1, " : "T=0, ",
> > +               __entry->control & TRB_ISP ? "ISP, " : "",
> > +               __entry->control & TRB_FIFO_MODE ? "FIFO, " : "",
> > +               __entry->control & TRB_CHAIN ? "CHAIN, " : "",
> > +               __entry->control & TRB_IOC ? "IOC, " : "",
> > +               TRB_FIELD_TO_TYPE(__entry->control) == TRB_NORMAL ? "Normal" : "LINK"
> > +       )
> > +);
> > +
> > +DEFINE_EVENT(cdns3_log_trb, cdns3_prepare_trb,
> > +       TP_PROTO(struct cdns3_endpoint *priv_ep, struct cdns3_trb *trb),
> > +       TP_ARGS(priv_ep, trb)
> > +);
> > +
> > +DEFINE_EVENT(cdns3_log_trb, cdns3_complete_trb,
> > +       TP_PROTO(struct cdns3_endpoint *priv_ep, struct cdns3_trb *trb),
> > +       TP_ARGS(priv_ep, trb)
> > +);
> > +
> > +DECLARE_EVENT_CLASS(cdns3_log_ring,
> > +       TP_PROTO(struct cdns3_endpoint *priv_ep),
> > +       TP_ARGS(priv_ep),
> > +       TP_STRUCT__entry(
> > +               __dynamic_array(u8, ring, TRB_RING_SIZE)
> > +               __dynamic_array(u8, priv_ep, sizeof(struct cdns3_endpoint))
> > +               __dynamic_array(char, buffer,
> > +                               (TRBS_PER_SEGMENT * 65) + CDNS3_MSG_MAX)
> > +       ),
> > +       TP_fast_assign(
> > +               memcpy(__get_dynamic_array(priv_ep), priv_ep,
> > +                      sizeof(struct cdns3_endpoint));
> > +               memcpy(__get_dynamic_array(ring), priv_ep->trb_pool,
> > +                      TRB_RING_SIZE);
> > +       ),
> > +
> > +       TP_printk("%s",
> > +                 cdns3_dbg_ring((struct cdns3_endpoint *)__get_str(priv_ep),
> > +                                (struct cdns3_trb *)__get_str(ring),
> > +                                __get_str(buffer)))
> > +);
> > +
> > +DEFINE_EVENT(cdns3_log_ring, cdns3_ring,
> > +       TP_PROTO(struct cdns3_endpoint *priv_ep),
> > +       TP_ARGS(priv_ep)
> > +);
> > +
> > +DECLARE_EVENT_CLASS(cdns3_log_ep,
> > +       TP_PROTO(struct cdns3_endpoint *priv_ep),
> > +       TP_ARGS(priv_ep),
> > +       TP_STRUCT__entry(
> > +               __string(name, priv_ep->name)
> > +               __field(unsigned int, maxpacket)
> > +               __field(unsigned int, maxpacket_limit)
> > +               __field(unsigned int, max_streams)
> > +               __field(unsigned int, maxburst)
> > +               __field(unsigned int, flags)
> > +               __field(unsigned int, dir)
> > +               __field(u8, enqueue)
> > +               __field(u8, dequeue)
> > +       ),
> > +       TP_fast_assign(
> > +               __assign_str(name, priv_ep->name);
> > +               __entry->maxpacket = priv_ep->endpoint.maxpacket;
> > +               __entry->maxpacket_limit = priv_ep->endpoint.maxpacket_limit;
> > +               __entry->max_streams = priv_ep->endpoint.max_streams;
> > +               __entry->maxburst = priv_ep->endpoint.maxburst;
> > +               __entry->flags = priv_ep->flags;
> > +               __entry->dir = priv_ep->dir;
> > +               __entry->enqueue = priv_ep->enqueue;
> > +               __entry->dequeue = priv_ep->dequeue;
> > +       ),
> > +       TP_printk("%s: mps: %d/%d. streams: %d, burst: %d, enq idx: %d, "
> > +                 "deq idx: %d, flags %s%s%s%s%s%s%s%s, dir: %s",
> > +               __get_str(name), __entry->maxpacket,
> > +               __entry->maxpacket_limit, __entry->max_streams,
> > +               __entry->maxburst, __entry->enqueue,
> > +               __entry->dequeue,
> > +               __entry->flags & EP_ENABLED ? "EN | " : "",
> > +               __entry->flags & EP_STALL ? "STALL | " : "",
> > +               __entry->flags & EP_WEDGE ? "WEDGE | " : "",
> > +               __entry->flags & EP_TRANSFER_STARTED ? "STARTED | " : "",
> > +               __entry->flags & EP_UPDATE_EP_TRBADDR ? "UPD TRB | " : "",
> > +               __entry->flags & EP_PENDING_REQUEST ? "REQ PEN | " : "",
> > +               __entry->flags & EP_RING_FULL ? "RING FULL |" : "",
> > +               __entry->flags & EP_CLAIMED ?  "CLAIMED " : "",
> > +               __entry->dir ? "IN" : "OUT"
> > +       )
> > +);
> > +
> > +DEFINE_EVENT(cdns3_log_ep, cdns3_gadget_ep_enable,
> > +       TP_PROTO(struct cdns3_endpoint *priv_ep),
> > +       TP_ARGS(priv_ep)
> > +);
> > +
> > +DEFINE_EVENT(cdns3_log_ep, cdns3_gadget_ep_disable,
> > +       TP_PROTO(struct cdns3_endpoint *priv_ep),
> > +       TP_ARGS(priv_ep)
> > +);
> > +
> > +DECLARE_EVENT_CLASS(cdns3_log_request_handled,
> > +       TP_PROTO(struct cdns3_request *priv_req, int current_index,
> > +                int handled),
> > +       TP_ARGS(priv_req, current_index, handled),
> > +       TP_STRUCT__entry(
> > +               __field(struct cdns3_request *, priv_req)
> > +               __field(unsigned int, dma_position)
> > +               __field(unsigned int, handled)
> > +               __field(unsigned int, dequeue_idx)
> > +               __field(unsigned int, enqueue_idx)
> > +               __field(unsigned int, start_trb)
> > +               __field(unsigned int, end_trb)
> > +       ),
> > +       TP_fast_assign(
> > +               __entry->priv_req = priv_req;
> > +               __entry->dma_position = current_index;
> > +               __entry->handled = handled;
> > +               __entry->dequeue_idx = priv_req->priv_ep->dequeue;
> > +               __entry->enqueue_idx = priv_req->priv_ep->enqueue;
> > +               __entry->start_trb = priv_req->start_trb;
> > +               __entry->end_trb = priv_req->end_trb;
> > +       ),
> > +       TP_printk("Req: %p %s, DMA pos: %d, ep deq: %d, ep enq: %d,"
> > +                 " start trb: %d, end trb: %d",
> > +               __entry->priv_req,
> > +               __entry->handled ? "handled" : "not handled",
> > +               __entry->dma_position, __entry->dequeue_idx,
> > +               __entry->enqueue_idx, __entry->start_trb,
> > +               __entry->end_trb
> > +       )
> > +);
> > +
> > +DEFINE_EVENT(cdns3_log_request_handled, cdns3_request_handled,
> > +       TP_PROTO(struct cdns3_request *priv_req, int current_index,
> > +                int handled),
> > +       TP_ARGS(priv_req, current_index, handled)
> > +);
> > +#endif /* __LINUX_CDNS3_TRACE */
> > +
> > +/* this part must be outside header guard */
> > +
> > +#undef TRACE_INCLUDE_PATH
> > +#define TRACE_INCLUDE_PATH .
> > +
> > +#undef TRACE_INCLUDE_FILE
> > +#define TRACE_INCLUDE_FILE trace
> > +
> > +#include <trace/define_trace.h>
> > --
> > 2.17.1
> >

^ permalink raw reply	[flat|nested] 49+ messages in thread

* [v2,5/5] usb:cdns3 Add Cadence USB3 DRD Driver
@ 2018-12-25  9:38       ` Peter Chen
  0 siblings, 0 replies; 49+ messages in thread
From: Peter Chen @ 2018-12-25  9:38 UTC (permalink / raw)
  To: pawell
  Cc: devicetree, Greg Kroah-Hartman, mark.rutland, linux-usb,
	hdegoede, Heikki Krogerus, andy.shevchenko, robh+dt, rogerq,
	lkml, adouglas, jbergsagel, nsekhar, nm, sureshp, peter.chen,
	pjez, kurahul

> > +
> > +       if (ret)
> > +               return ret;
> > +
> > +       state = readl(&cdns->otg_regs->sts);
> > +       if (OTGSTS_OTG_NRDY(state) != 0) {
> > +               dev_err(cdns->dev, "Cadence USB3 OTG device not ready\n");
> > +               return -ENODEV;
> > +       }
> > +
> > +       ret = cdns3_drd_update_mode(cdns);
> > +
>
> Calling this function, it is timeout for waiting OTGSTS_XHCI_READY at otgsts,
> do you know possible reasons?  After commenting out this function, my
> xHCI function
> works.
>

Pawel, since OTG compliance (Known as HNP/SRP)  has not been used
widely, Linux kernel does not
maintain it from some time ago (maybe 1-2 years). In software design,
we do not need to consider it from
hardware point, eg, kinds of OTG timer. For dual-role switch on the
fly,  through /sys is enough.

Through the debug, we find it needs to wait 1s after setting de-select
the host or gadget before request
XHCI at otg_regs->cmd, and enable fast simulate can reduce delay to
2-3ms. Would you please help
to check with your hardware team this behavior. With below changes, I
can get OTGSTS_XHCI_READY at otgsts.

@@ -141,6 +143,7 @@ static int cdns3_drd_switch_host(struct cdns3 *cdns, int on)
                writel(OTGCMD_HOST_BUS_DROP | OTGCMD_DEV_BUS_DROP |
                       OTGCMD_DEV_POWER_OFF | OTGCMD_HOST_POWER_OFF,
                       &cdns->otg_regs->cmd);
+               usleep_range(3000, 4000);
        }

        return 0;
@@ -178,6 +181,7 @@ static int cdns3_drd_switch_gadget(struct cdns3
*cdns, int on)
                writel(OTGCMD_HOST_BUS_DROP | OTGCMD_DEV_BUS_DROP |
                       OTGCMD_DEV_POWER_OFF | OTGCMD_HOST_POWER_OFF,
                       &cdns->otg_regs->cmd);
+               usleep_range(3000, 4000);
        }

@@ -299,6 +306,7 @@ int cdns3_drd_init(struct cdns3 *cdns)
                cdns->version  = CDNS3_CONTROLLER_V0;
                cdns->otg_v1_regs = NULL;
                cdns->otg_regs = regs;
+               writel(0x1, &cdns->otg_v0_regs->simulate);
                dev_info(cdns->dev, "DRD version v0 (%08x)\n",
                         readl(&cdns->otg_v0_regs->version));
        } else {

Peter

> Peter
>
> > +       return ret;
> > +}
> > +
> > +int cdns3_drd_exit(struct cdns3 *cdns)
> > +{
> > +       return cdns3_drd_switch_host(cdns, 0);
> > +}
> > diff --git a/drivers/usb/cdns3/drd.h b/drivers/usb/cdns3/drd.h
> > new file mode 100644
> > index 000000000000..6a29cdcb492d
> > --- /dev/null
> > +++ b/drivers/usb/cdns3/drd.h
> > @@ -0,0 +1,162 @@
> > +/* SPDX-License-Identifier: GPL-2.0 */
> > +/*
> > + * Cadence USB3 DRD header file.
> > + *
> > + * Copyright (C) 2018 Cadence.
> > + *
> > + * Author: Pawel Laszczak <pawell@cadence.com>
> > + */
> > +#ifndef __LINUX_CDNS3_DRD
> > +#define __LINUX_CDNS3_DRD
> > +
> > +#include <linux/usb/otg.h>
> > +#include <linux/phy/phy.h>
> > +#include "core.h"
> > +
> > +/*  DRD register interface for version v1. */
> > +struct cdns3_otg_regs {
> > +       __le32 did;
> > +       __le32 rid;
> > +       __le32 capabilities;
> > +       __le32 reserved1;
> > +       __le32 cmd;
> > +       __le32 sts;
> > +       __le32 state;
> > +       __le32 reserved2;
> > +       __le32 ien;
> > +       __le32 ivect;
> > +       __le32 refclk;
> > +       __le32 tmr;
> > +       __le32 reserved3[4];
> > +       __le32 simulate;
> > +       __le32 override;
> > +       __le32 susp_ctrl;
> > +       __le32 reserved4;
> > +       __le32 anasts;
> > +       __le32 adp_ramp_time;
> > +       __le32 ctrl1;
> > +       __le32 ctrl2;
> > +};
> > +
> > +/*  DRD register interface for version v0. */
> > +struct cdns3_otg_legacy_regs {
> > +       __le32 cmd;
> > +       __le32 sts;
> > +       __le32 state;
> > +       __le32 refclk;
> > +       __le32 ien;
> > +       __le32 ivect;
> > +       __le32 reserved1[3];
> > +       __le32 tmr;
> > +       __le32 reserved2[2];
> > +       __le32 version;
> > +       __le32 capabilities;
> > +       __le32 reserved3[2];
> > +       __le32 simulate;
> > +       __le32 reserved4[5];
> > +       __le32 ctrl1;
> > +};
> > +
> > +/*
> > + * Common registers interface for both version of DRD.
> > + */
> > +struct cdns3_otg_common_regs {
> > +       __le32 cmd;
> > +       __le32 sts;
> > +       __le32 state;
> > +       __le32 different1;
> > +       __le32 ien;
> > +       __le32 ivect;
> > +};
> > +
> > +/* CDNS_RID - bitmasks */
> > +#define CDNS_RID(p)                    ((p) & GENMASK(15, 0))
> > +
> > +/* CDNS_VID - bitmasks */
> > +#define CDNS_DID(p)                    ((p) & GENMASK(31, 0))
> > +
> > +/* OTGCMD - bitmasks */
> > +/* "Request the bus for Device mode. */
> > +#define OTGCMD_DEV_BUS_REQ             BIT(0)
> > +/* Request the bus for Host mode */
> > +#define OTGCMD_HOST_BUS_REQ            BIT(1)
> > +/* Enable OTG mode. */
> > +#define OTGCMD_OTG_EN                  BIT(2)
> > +/* Disable OTG mode */
> > +#define OTGCMD_OTG_DIS                 BIT(3)
> > +/*"Configure OTG as A-Device. */
> > +#define OTGCMD_A_DEV_EN                        BIT(4)
> > +/*"Configure OTG as A-Device. */
> > +#define OTGCMD_A_DEV_DIS               BIT(5)
> > +/* Drop the bus for Device mod e. */
> > +#define OTGCMD_DEV_BUS_DROP            BIT(8)
> > +/* Drop the bus for Host mode*/
> > +#define OTGCMD_HOST_BUS_DROP           BIT(9)
> > +/* Power Down USBSS-DEV. */
> > +#define OTGCMD_DEV_POWER_OFF           BIT(11)
> > +/* Power Down CDNSXHCI. */
> > +#define OTGCMD_HOST_POWER_OFF          BIT(12)
> > +
> > +/* OTGIEN - bitmasks */
> > +/* ID change interrupt enable */
> > +#define OTGIEN_ID_CHANGE_INT           BIT(0)
> > +/* Vbusvalid fall detected interrupt enable.*/
> > +#define OTGIEN_VBUSVALID_RISE_INT      BIT(4)
> > +/* Vbusvalid fall detected interrupt enable */
> > +#define OTGIEN_VBUSVALID_FALL_INT      BIT(5)
> > +
> > +/* OTGSTS - bitmasks */
> > +/*
> > + * Current value of the ID pin. It is only valid when idpullup in
> > + *  OTGCTRL1_TYPE register is set to '1'.
> > + */
> > +#define OTGSTS_ID_VALUE                        BIT(0)
> > +/* Current value of the vbus_valid */
> > +#define OTGSTS_VBUS_VALID              BIT(1)
> > +/* Current value of the b_sess_vld */
> > +#define OTGSTS_SESSION_VALID           BIT(2)
> > +/*Device mode is active*/
> > +#define OTGSTS_DEV_ACTIVE              BIT(3)
> > +/* Host mode is active. */
> > +#define OTGSTS_HOST_ACTIVE             BIT(4)
> > +/* OTG Controller not ready. */
> > +#define OTGSTS_OTG_NRDY_MASK           BIT(11)
> > +#define OTGSTS_OTG_NRDY(p)             ((p) & OTGSTS_OTG_NRDY_MASK)
> > +/*
> > + * Value of the strap pins.
> > + * 000 - no default configuration
> > + * 010 - Controller initiall configured as Host
> > + * 100 - Controller initially configured as Device
> > + */
> > +#define OTGSTS_STRAP(p)                        (((p) & GENMASK(14, 12)) >> 12)
> > +#define OTGSTS_STRAP_NO_DEFAULT_CFG    0x00
> > +#define OTGSTS_STRAP_HOST_OTG          0x01
> > +#define OTGSTS_STRAP_HOST              0x02
> > +#define OTGSTS_STRAP_GADGET            0x04
> > +/* Host mode is turned on. */
> > +#define OTGSTS_XHCI_READY              BIT(26)
> > +/* "Device mode is turned on .*/
> > +#define OTGSTS_DEV_READY               BIT(27)
> > +
> > +/* OTGSTATE- bitmasks */
> > +#define OTGSTATE_HOST_STATE_MASK       GENMASK(5, 3)
> > +#define OTGSTATE_HOST_STATE_IDLE       0x0
> > +#define OTGSTATE_HOST_STATE_VBUS_FALL   0x7
> > +#define OTGSTATE_HOST_STATE(p)         (((p) & OTGSTATE_HOST_STATE_MASK) >> 3)
> > +
> > +/* OTGREFCLK - bitmasks */
> > +#define OTGREFCLK_STB_CLK_SWITCH_EN    BIT(31)
> > +
> > +/* OVERRIDE - bitmasks */
> > +#define OVERRIDE_IDPULLUP              BIT(0)
> > +/* Only for CDNS3_CONTROLLER_V0 version */
> > +#define OVERRIDE_IDPULLUP_V0           BIT(24)
> > +
> > +int cdns3_is_host(struct cdns3 *cdns);
> > +int cdns3_is_device(struct cdns3 *cdns);
> > +int cdns3_get_id(struct cdns3 *cdns);
> > +int cdns3_drd_init(struct cdns3 *cdns);
> > +int cdns3_drd_exit(struct cdns3 *cdns);
> > +int cdns3_drd_update_mode(struct cdns3 *cdns);
> > +
> > +#endif /* __LINUX_CDNS3_DRD */
> > diff --git a/drivers/usb/cdns3/ep0.c b/drivers/usb/cdns3/ep0.c
> > new file mode 100644
> > index 000000000000..89cf1cde1555
> > --- /dev/null
> > +++ b/drivers/usb/cdns3/ep0.c
> > @@ -0,0 +1,896 @@
> > +// SPDX-License-Identifier: GPL-2.0
> > +/*
> > + * Cadence USBSS DRD Driver - gadget side.
> > + *
> > + * Copyright (C) 2018 Cadence Design Systems.
> > + * Copyright (C) 2017-2018 NXP
> > + *
> > + * Authors: Pawel Jez <pjez@cadence.com>,
> > + *          Pawel Laszczak <pawell@cadence.com>
> > + *         Peter Chen <peter.chen@nxp.com>
> > + */
> > +
> > +#include <linux/usb/composite.h>
> > +
> > +#include "gadget.h"
> > +#include "trace.h"
> > +
> > +static struct usb_endpoint_descriptor cdns3_gadget_ep0_desc = {
> > +       .bLength = USB_DT_ENDPOINT_SIZE,
> > +       .bDescriptorType = USB_DT_ENDPOINT,
> > +       .bmAttributes = USB_ENDPOINT_XFER_CONTROL,
> > +};
> > +
> > +/**
> > + * cdns3_ep0_run_transfer - Do transfer on default endpoint hardware
> > + * @priv_dev: extended gadget object
> > + * @dma_addr: physical address where data is/will be stored
> > + * @length: data length
> > + * @erdy: set it to 1 when ERDY packet should be sent -
> > + *        exit from flow control state
> > + */
> > +static void cdns3_ep0_run_transfer(struct cdns3_device *priv_dev,
> > +                                  dma_addr_t dma_addr,
> > +                                  unsigned int length, int erdy)
> > +{
> > +       struct cdns3_usb_regs __iomem *regs = priv_dev->regs;
> > +       struct cdns3_endpoint *priv_ep = priv_dev->eps[0];
> > +
> > +       priv_ep->trb_pool->buffer = TRB_BUFFER(dma_addr);
> > +       priv_ep->trb_pool->length = TRB_LEN(length);
> > +       priv_ep->trb_pool->control = TRB_CYCLE | TRB_IOC | TRB_TYPE(TRB_NORMAL);
> > +
> > +       trace_cdns3_prepare_trb(priv_ep, priv_ep->trb_pool);
> > +
> > +       cdns3_select_ep(priv_dev, priv_dev->ep0_data_dir);
> > +
> > +       writel(EP_STS_TRBERR, &regs->ep_sts);
> > +       writel(EP_TRADDR_TRADDR(priv_ep->trb_pool_dma), &regs->ep_traddr);
> > +       trace_cdns3_doorbell_ep0(priv_dev->ep0_data_dir ? "ep0in" : "ep0out",
> > +                                readl(&regs->ep_traddr));
> > +
> > +       /* TRB should be prepared before starting transfer */
> > +       writel(EP_CMD_DRDY, &regs->ep_cmd);
> > +
> > +       if (erdy)
> > +               writel(EP_CMD_ERDY, &priv_dev->regs->ep_cmd);
> > +}
> > +
> > +/**
> > + * cdns3_ep0_delegate_req - Returns status of handling setup packet
> > + * Setup is handled by gadget driver
> > + * @priv_dev: extended gadget object
> > + * @ctrl_req: pointer to received setup packet
> > + *
> > + * Returns zero on success or negative value on failure
> > + */
> > +static int cdns3_ep0_delegate_req(struct cdns3_device *priv_dev,
> > +                                 struct usb_ctrlrequest *ctrl_req)
> > +{
> > +       int ret;
> > +
> > +       spin_unlock(&priv_dev->lock);
> > +       priv_dev->setup_pending = 1;
> > +       ret = priv_dev->gadget_driver->setup(&priv_dev->gadget, ctrl_req);
> > +       priv_dev->setup_pending = 0;
> > +       spin_lock(&priv_dev->lock);
> > +       return ret;
> > +}
> > +
> > +static void cdns3_prepare_setup_packet(struct cdns3_device *priv_dev)
> > +{
> > +       priv_dev->ep0_data_dir = 0;
> > +       priv_dev->ep0_stage = CDNS3_SETUP_STAGE;
> > +       cdns3_ep0_run_transfer(priv_dev, priv_dev->setup_dma,
> > +                              sizeof(struct usb_ctrlrequest), 0);
> > +}
> > +
> > +static void cdns3_ep0_complete_setup(struct cdns3_device *priv_dev,
> > +                                    u8 send_stall, u8 send_erdy)
> > +{
> > +       struct cdns3_endpoint *priv_ep = priv_dev->eps[0];
> > +       struct usb_request *request;
> > +
> > +       request = cdns3_next_request(&priv_ep->pending_req_list);
> > +       if (request)
> > +               list_del_init(&request->list);
> > +
> > +       if (send_stall) {
> > +               dev_dbg(priv_dev->dev, "STALL for ep0\n");
> > +               /* set_stall on ep0 */
> > +               cdns3_select_ep(priv_dev, 0x00);
> > +               writel(EP_CMD_SSTALL, &priv_dev->regs->ep_cmd);
> > +       } else {
> > +               cdns3_prepare_setup_packet(priv_dev);
> > +       }
> > +
> > +       priv_dev->ep0_stage = CDNS3_SETUP_STAGE;
> > +       writel((send_erdy ? EP_CMD_ERDY : 0) | EP_CMD_REQ_CMPL,
> > +              &priv_dev->regs->ep_cmd);
> > +}
> > +
> > +/**
> > + * cdns3_req_ep0_set_configuration - Handling of SET_CONFIG standard USB request
> > + * @priv_dev: extended gadget object
> > + * @ctrl_req: pointer to received setup packet
> > + *
> > + * Returns 0 if success, USB_GADGET_DELAYED_STATUS on deferred status stage,
> > + * error code on error
> > + */
> > +static int cdns3_req_ep0_set_configuration(struct cdns3_device *priv_dev,
> > +                                          struct usb_ctrlrequest *ctrl_req)
> > +{
> > +       enum usb_device_state device_state = priv_dev->gadget.state;
> > +       struct cdns3_endpoint *priv_ep;
> > +       u32 config = le16_to_cpu(ctrl_req->wValue);
> > +       int result = 0;
> > +       int i;
> > +
> > +       switch (device_state) {
> > +       case USB_STATE_ADDRESS:
> > +               /* Configure non-control EPs */
> > +               for (i = 0; i < CDNS3_ENDPOINTS_MAX_COUNT; i++) {
> > +                       priv_ep = priv_dev->eps[i];
> > +                       if (!priv_ep)
> > +                               continue;
> > +
> > +                       if (priv_ep->flags & EP_CLAIMED)
> > +                               cdns3_ep_config(priv_ep);
> > +               }
> > +
> > +               result = cdns3_ep0_delegate_req(priv_dev, ctrl_req);
> > +
> > +               if (result)
> > +                       return result;
> > +
> > +               if (config) {
> > +                       cdns3_set_hw_configuration(priv_dev);
> > +               } else {
> > +                       cdns3_hw_reset_eps_config(priv_dev);
> > +                       usb_gadget_set_state(&priv_dev->gadget,
> > +                                            USB_STATE_ADDRESS);
> > +               }
> > +               break;
> > +       case USB_STATE_CONFIGURED:
> > +               result = cdns3_ep0_delegate_req(priv_dev, ctrl_req);
> > +
> > +               if (!config && !result) {
> > +                       cdns3_hw_reset_eps_config(priv_dev);
> > +                       usb_gadget_set_state(&priv_dev->gadget,
> > +                                            USB_STATE_ADDRESS);
> > +               }
> > +               break;
> > +       default:
> > +               result = -EINVAL;
> > +       }
> > +
> > +       return result;
> > +}
> > +
> > +/**
> > + * cdns3_req_ep0_set_address - Handling of SET_ADDRESS standard USB request
> > + * @priv_dev: extended gadget object
> > + * @ctrl_req: pointer to received setup packet
> > + *
> > + * Returns 0 if success, error code on error
> > + */
> > +static int cdns3_req_ep0_set_address(struct cdns3_device *priv_dev,
> > +                                    struct usb_ctrlrequest *ctrl_req)
> > +{
> > +       enum usb_device_state device_state = priv_dev->gadget.state;
> > +       u32 reg;
> > +       u32 addr;
> > +
> > +       addr = le16_to_cpu(ctrl_req->wValue);
> > +
> > +       if (addr > USB_DEVICE_MAX_ADDRESS) {
> > +               dev_err(priv_dev->dev,
> > +                       "Device address (%d) cannot be greater than %d\n",
> > +                       addr, USB_DEVICE_MAX_ADDRESS);
> > +               return -EINVAL;
> > +       }
> > +
> > +       if (device_state == USB_STATE_CONFIGURED) {
> > +               dev_err(priv_dev->dev,
> > +                       "can't set_address from configured state\n");
> > +               return -EINVAL;
> > +       }
> > +
> > +       reg = readl(&priv_dev->regs->usb_cmd);
> > +
> > +       writel(reg | USB_CMD_FADDR(addr) | USB_CMD_SET_ADDR,
> > +              &priv_dev->regs->usb_cmd);
> > +
> > +       usb_gadget_set_state(&priv_dev->gadget,
> > +                            (addr ? USB_STATE_ADDRESS : USB_STATE_DEFAULT));
> > +
> > +       return 0;
> > +}
> > +
> > +/**
> > + * cdns3_req_ep0_get_status - Handling of GET_STATUS standard USB request
> > + * @priv_dev: extended gadget object
> > + * @ctrl_req: pointer to received setup packet
> > + *
> > + * Returns 0 if success, error code on error
> > + */
> > +static int cdns3_req_ep0_get_status(struct cdns3_device *priv_dev,
> > +                                   struct usb_ctrlrequest *ctrl)
> > +{
> > +       __le16 *response_pkt;
> > +       u16 usb_status = 0;
> > +       u32 recip;
> > +       u32 reg;
> > +
> > +       recip = ctrl->bRequestType & USB_RECIP_MASK;
> > +
> > +       switch (recip) {
> > +       case USB_RECIP_DEVICE:
> > +               /* self powered */
> > +               if (priv_dev->is_selfpowered)
> > +                       usb_status = BIT(USB_DEVICE_SELF_POWERED);
> > +
> > +               if (priv_dev->wake_up_flag)
> > +                       usb_status |= BIT(USB_DEVICE_REMOTE_WAKEUP);
> > +
> > +               if (priv_dev->gadget.speed != USB_SPEED_SUPER)
> > +                       break;
> > +
> > +               reg = readl(&priv_dev->regs->usb_sts);
> > +
> > +               if (priv_dev->u1_allowed)
> > +                       usb_status |= BIT(USB_DEV_STAT_U1_ENABLED);
> > +
> > +               if (priv_dev->u2_allowed)
> > +                       usb_status |= BIT(USB_DEV_STAT_U2_ENABLED);
> > +
> > +               break;
> > +       case USB_RECIP_INTERFACE:
> > +               return cdns3_ep0_delegate_req(priv_dev, ctrl);
> > +       case USB_RECIP_ENDPOINT:
> > +               /* check if endpoint is stalled */
> > +               cdns3_select_ep(priv_dev, ctrl->wIndex);
> > +               if (EP_STS_STALL(readl(&priv_dev->regs->ep_sts)))
> > +                       usb_status =  BIT(USB_ENDPOINT_HALT);
> > +               break;
> > +       default:
> > +               return -EINVAL;
> > +       }
> > +
> > +       response_pkt = (__le16 *)priv_dev->setup_buf;
> > +       *response_pkt = cpu_to_le16(usb_status);
> > +
> > +       cdns3_ep0_run_transfer(priv_dev, priv_dev->setup_dma,
> > +                              sizeof(*response_pkt), 1);
> > +       return 0;
> > +}
> > +
> > +static int cdns3_ep0_feature_handle_device(struct cdns3_device *priv_dev,
> > +                                          struct usb_ctrlrequest *ctrl,
> > +                                          int set)
> > +{
> > +       enum usb_device_state state;
> > +       enum usb_device_speed speed;
> > +       int ret = 0;
> > +       u32 wValue;
> > +       u32 wIndex;
> > +       u16 tmode;
> > +
> > +       wValue = le16_to_cpu(ctrl->wValue);
> > +       wIndex = le16_to_cpu(ctrl->wIndex);
> > +       state = priv_dev->gadget.state;
> > +       speed = priv_dev->gadget.speed;
> > +
> > +       switch (ctrl->wValue) {
> > +       case USB_DEVICE_REMOTE_WAKEUP:
> > +               priv_dev->wake_up_flag = !!set;
> > +               break;
> > +       case USB_DEVICE_U1_ENABLE:
> > +               if (state != USB_STATE_CONFIGURED || speed != USB_SPEED_SUPER)
> > +                       return -EINVAL;
> > +
> > +               priv_dev->u1_allowed = !!set;
> > +               break;
> > +       case USB_DEVICE_U2_ENABLE:
> > +               if (state != USB_STATE_CONFIGURED || speed != USB_SPEED_SUPER)
> > +                       return -EINVAL;
> > +
> > +               priv_dev->u2_allowed = !!set;
> > +               break;
> > +       case USB_DEVICE_LTM_ENABLE:
> > +               ret = -EINVAL;
> > +               break;
> > +       case USB_DEVICE_TEST_MODE:
> > +               if (state != USB_STATE_CONFIGURED || speed > USB_SPEED_HIGH)
> > +                       return -EINVAL;
> > +
> > +               tmode = le16_to_cpu(ctrl->wIndex);
> > +
> > +               if (!set || (tmode & 0xff) != 0)
> > +                       return -EINVAL;
> > +
> > +               switch (tmode >> 8) {
> > +               case TEST_J:
> > +               case TEST_K:
> > +               case TEST_SE0_NAK:
> > +               case TEST_PACKET:
> > +                       cdns3_ep0_complete_setup(priv_dev, 0, 1);
> > +                       /**
> > +                        *  Little delay to give the controller some time
> > +                        * for sending status stage.
> > +                        * This time should be less then 3ms.
> > +                        */
> > +                       usleep_range(1000, 2000);
> > +                       cdns3_set_register_bit(&priv_dev->regs->usb_cmd,
> > +                                              USB_CMD_STMODE |
> > +                                              USB_STS_TMODE_SEL(tmode - 1));
> > +                       break;
> > +               default:
> > +                       ret = -EINVAL;
> > +               }
> > +               break;
> > +       default:
> > +               ret = -EINVAL;
> > +       }
> > +
> > +       return ret;
> > +}
> > +
> > +static int cdns3_ep0_feature_handle_intf(struct cdns3_device *priv_dev,
> > +                                        struct usb_ctrlrequest *ctrl,
> > +                                        int set)
> > +{
> > +       u32 wValue;
> > +       int ret = 0;
> > +
> > +       wValue = le16_to_cpu(ctrl->wValue);
> > +
> > +       switch (wValue) {
> > +       case USB_INTRF_FUNC_SUSPEND:
> > +               break;
> > +       default:
> > +               ret = -EINVAL;
> > +       }
> > +
> > +       return ret;
> > +}
> > +
> > +static int cdns3_ep0_feature_handle_endpoint(struct cdns3_device *priv_dev,
> > +                                            struct usb_ctrlrequest *ctrl,
> > +                                            int set)
> > +{
> > +       struct cdns3_endpoint *priv_ep;
> > +       int ret = 0;
> > +       u8 index;
> > +
> > +       if (le16_to_cpu(ctrl->wValue) != USB_ENDPOINT_HALT)
> > +               return -EINVAL;
> > +
> > +       if (!(ctrl->wIndex & ~USB_DIR_IN))
> > +               return 0;
> > +
> > +       index = cdns3_ep_addr_to_index(ctrl->wIndex);
> > +       priv_ep = priv_dev->eps[index];
> > +
> > +       cdns3_select_ep(priv_dev, ctrl->wIndex);
> > +
> > +       if (set) {
> > +               writel(EP_CMD_SSTALL, &priv_dev->regs->ep_cmd);
> > +               priv_ep->flags |= EP_STALL;
> > +       } else {
> > +               struct usb_request *request;
> > +
> > +               if (priv_dev->eps[index]->flags & EP_WEDGE) {
> > +                       cdns3_select_ep(priv_dev, 0x00);
> > +                       return 0;
> > +               }
> > +
> > +               writel(EP_CMD_CSTALL | EP_CMD_EPRST, &priv_dev->regs->ep_cmd);
> > +
> > +               /* wait for EPRST cleared */
> > +               ret = cdns3_handshake(&priv_dev->regs->ep_cmd,
> > +                                     EP_CMD_EPRST, 0, 100);
> > +               if (ret)
> > +                       return -EINVAL;
> > +
> > +               priv_ep->flags &= ~EP_STALL;
> > +
> > +               request = cdns3_next_request(&priv_ep->pending_req_list);
> > +               if (request)
> > +                       cdns3_ep_run_transfer(priv_ep, request);
> > +       }
> > +       return ret;
> > +}
> > +
> > +/**
> > + * cdns3_req_ep0_handle_feature -
> > + * Handling of GET/SET_FEATURE standard USB request
> > + *
> > + * @priv_dev: extended gadget object
> > + * @ctrl_req: pointer to received setup packet
> > + * @set: must be set to 1 for SET_FEATURE request
> > + *
> > + * Returns 0 if success, error code on error
> > + */
> > +static int cdns3_req_ep0_handle_feature(struct cdns3_device *priv_dev,
> > +                                       struct usb_ctrlrequest *ctrl,
> > +                                       int set)
> > +{
> > +       int ret = 0;
> > +       u32 recip;
> > +
> > +       recip = ctrl->bRequestType & USB_RECIP_MASK;
> > +
> > +       switch (recip) {
> > +       case USB_RECIP_DEVICE:
> > +               ret = cdns3_ep0_feature_handle_device(priv_dev, ctrl, set);
> > +               break;
> > +       case USB_RECIP_INTERFACE:
> > +               ret = cdns3_ep0_feature_handle_intf(priv_dev, ctrl, set);
> > +               break;
> > +       case USB_RECIP_ENDPOINT:
> > +               ret = cdns3_ep0_feature_handle_endpoint(priv_dev, ctrl, set);
> > +               break;
> > +       default:
> > +               return -EINVAL;
> > +       }
> > +
> > +       return ret;
> > +}
> > +
> > +/**
> > + * cdns3_req_ep0_set_sel - Handling of SET_SEL standard USB request
> > + * @priv_dev: extended gadget object
> > + * @ctrl_req: pointer to received setup packet
> > + *
> > + * Returns 0 if success, error code on error
> > + */
> > +static int cdns3_req_ep0_set_sel(struct cdns3_device *priv_dev,
> > +                                struct usb_ctrlrequest *ctrl_req)
> > +{
> > +       if (priv_dev->gadget.state < USB_STATE_ADDRESS)
> > +               return -EINVAL;
> > +
> > +       if (ctrl_req->wLength != 6) {
> > +               dev_err(priv_dev->dev, "Set SEL should be 6 bytes, got %d\n",
> > +                       ctrl_req->wLength);
> > +               return -EINVAL;
> > +       }
> > +
> > +       cdns3_ep0_run_transfer(priv_dev, priv_dev->setup_dma, 6, 1);
> > +       return 0;
> > +}
> > +
> > +/**
> > + * cdns3_req_ep0_set_isoch_delay -
> > + * Handling of GET_ISOCH_DELAY standard USB request
> > + * @priv_dev: extended gadget object
> > + * @ctrl_req: pointer to received setup packet
> > + *
> > + * Returns 0 if success, error code on error
> > + */
> > +static int cdns3_req_ep0_set_isoch_delay(struct cdns3_device *priv_dev,
> > +                                        struct usb_ctrlrequest *ctrl_req)
> > +{
> > +       if (ctrl_req->wIndex || ctrl_req->wLength)
> > +               return -EINVAL;
> > +
> > +       priv_dev->isoch_delay = ctrl_req->wValue;
> > +
> > +       return 0;
> > +}
> > +
> > +/**
> > + * cdns3_ep0_standard_request - Handling standard USB requests
> > + * @priv_dev: extended gadget object
> > + * @ctrl_req: pointer to received setup packet
> > + *
> > + * Returns 0 if success, error code on error
> > + */
> > +static int cdns3_ep0_standard_request(struct cdns3_device *priv_dev,
> > +                                     struct usb_ctrlrequest *ctrl_req)
> > +{
> > +       int ret;
> > +
> > +       switch (ctrl_req->bRequest) {
> > +       case USB_REQ_SET_ADDRESS:
> > +               ret = cdns3_req_ep0_set_address(priv_dev, ctrl_req);
> > +               break;
> > +       case USB_REQ_SET_CONFIGURATION:
> > +               ret = cdns3_req_ep0_set_configuration(priv_dev, ctrl_req);
> > +               break;
> > +       case USB_REQ_GET_STATUS:
> > +               ret = cdns3_req_ep0_get_status(priv_dev, ctrl_req);
> > +               break;
> > +       case USB_REQ_CLEAR_FEATURE:
> > +               ret = cdns3_req_ep0_handle_feature(priv_dev, ctrl_req, 0);
> > +               break;
> > +       case USB_REQ_SET_FEATURE:
> > +               ret = cdns3_req_ep0_handle_feature(priv_dev, ctrl_req, 1);
> > +               break;
> > +       case USB_REQ_SET_SEL:
> > +               ret = cdns3_req_ep0_set_sel(priv_dev, ctrl_req);
> > +               break;
> > +       case USB_REQ_SET_ISOCH_DELAY:
> > +               ret = cdns3_req_ep0_set_isoch_delay(priv_dev, ctrl_req);
> > +               break;
> > +       default:
> > +               ret = cdns3_ep0_delegate_req(priv_dev, ctrl_req);
> > +               break;
> > +       }
> > +
> > +       return ret;
> > +}
> > +
> > +static void __pending_setup_status_handler(struct cdns3_device *priv_dev)
> > +{
> > +       struct usb_request *request = priv_dev->pending_status_request;
> > +
> > +       if (priv_dev->status_completion_no_call && request &&
> > +           request->complete) {
> > +               request->complete(&priv_dev->eps[0]->endpoint, request);
> > +               priv_dev->status_completion_no_call = 0;
> > +       }
> > +}
> > +
> > +void cdns3_pending_setup_status_handler(struct work_struct *work)
> > +{
> > +       struct cdns3_device *priv_dev = container_of(work, struct cdns3_device,
> > +                       pending_status_wq);
> > +       unsigned long flags;
> > +
> > +       spin_lock_irqsave(&priv_dev->lock, flags);
> > +       __pending_setup_status_handler(priv_dev);
> > +       spin_unlock_irqrestore(&priv_dev->lock, flags);
> > +}
> > +
> > +/**
> > + * cdns3_gadget_ep_giveback - call struct usb_request's ->complete callback
> > + * @priv_ep: The endpoint to whom the request belongs to
> > + * @priv_req: The request we're giving back
> > + * @status: completion code for the request
> > + *
> > + * Must be called with controller's lock held and interrupts disabled. This
> > + * function will unmap @req and call its ->complete() callback to notify upper
> > + * layers that it has completed.
> > + */
> > +
> > +void cdns3_gadget_ep0_giveback(struct cdns3_device *priv_dev,
> > +                              int status)
> > +{
> > +       struct cdns3_endpoint *priv_ep;
> > +       struct usb_request *request;
> > +
> > +       priv_ep = priv_dev->eps[0];
> > +       request = cdns3_next_request(&priv_ep->pending_req_list);
> > +
> > +       priv_ep->dir = priv_dev->ep0_data_dir;
> > +       cdns3_gadget_giveback(priv_ep, to_cdns3_request(request), status);
> > +}
> > +
> > +/**
> > + * cdns3_ep0_setup_phase - Handling setup USB requests
> > + * @priv_dev: extended gadget object
> > + */
> > +static void cdns3_ep0_setup_phase(struct cdns3_device *priv_dev)
> > +{
> > +       struct usb_ctrlrequest *ctrl = priv_dev->setup_buf;
> > +       struct cdns3_endpoint *priv_ep = priv_dev->eps[0];
> > +       int result;
> > +
> > +       priv_dev->ep0_data_dir = ctrl->bRequestType & USB_DIR_IN;
> > +
> > +       trace_cdns3_ctrl_req(ctrl);
> > +
> > +       if (!list_empty(&priv_ep->pending_req_list)) {
> > +               struct usb_request *request;
> > +
> > +               request = cdns3_next_request(&priv_ep->pending_req_list);
> > +               priv_ep->dir = priv_dev->ep0_data_dir;
> > +               cdns3_gadget_giveback(priv_ep, to_cdns3_request(request),
> > +                                     -ECONNRESET);
> > +       }
> > +
> > +       if (le16_to_cpu(ctrl->wLength))
> > +               priv_dev->ep0_stage = CDNS3_DATA_STAGE;
> > +       else
> > +               priv_dev->ep0_stage = CDNS3_STATUS_STAGE;
> > +
> > +       if ((ctrl->bRequestType & USB_TYPE_MASK) == USB_TYPE_STANDARD)
> > +               result = cdns3_ep0_standard_request(priv_dev, ctrl);
> > +       else
> > +               result = cdns3_ep0_delegate_req(priv_dev, ctrl);
> > +
> > +       if (priv_dev->ep0_stage == CDNS3_STATUS_STAGE &&
> > +           result != USB_GADGET_DELAYED_STATUS) {
> > +               cdns3_ep0_complete_setup(priv_dev, 0, 1);
> > +       } else if (result < 0) {
> > +               cdns3_ep0_complete_setup(priv_dev, 1, 1);
> > +       }
> > +}
> > +
> > +static void cdns3_transfer_completed(struct cdns3_device *priv_dev)
> > +{
> > +       struct cdns3_endpoint *priv_ep = priv_dev->eps[0];
> > +
> > +       if (!list_empty(&priv_ep->pending_req_list)) {
> > +               struct usb_request *request;
> > +
> > +               trace_cdns3_complete_trb(priv_ep, priv_ep->trb_pool);
> > +               request = cdns3_next_request(&priv_ep->pending_req_list);
> > +
> > +               request->actual =
> > +                       TRB_LEN(le32_to_cpu(priv_ep->trb_pool->length));
> > +
> > +               priv_ep->dir = priv_dev->ep0_data_dir;
> > +               cdns3_gadget_giveback(priv_ep, to_cdns3_request(request), 0);
> > +       }
> > +
> > +       cdns3_ep0_complete_setup(priv_dev, 0, 0);
> > +}
> > +
> > +/**
> > + * cdns3_check_new_setup - Check if controller receive new SETUP packet.
> > + * @priv_dev: extended gadget object
> > + *
> > + * The SETUP packet can be kept in on-chip memory or in system memory.
> > + */
> > +static bool cdns3_check_new_setup(struct cdns3_device *priv_dev)
> > +{
> > +       u32 ep_sts_reg;
> > +
> > +       cdns3_select_ep(priv_dev, 0 | USB_DIR_OUT);
> > +       ep_sts_reg = readl(&priv_dev->regs->ep_sts);
> > +
> > +       return !!(ep_sts_reg & (EP_STS_SETUP | EP_STS_STPWAIT));
> > +}
> > +
> > +/**
> > + * cdns3_check_ep0_interrupt_proceed - Processes interrupt related to endpoint 0
> > + * @priv_dev: extended gadget object
> > + * @dir: USB_DIR_IN for IN direction, USB_DIR_OUT for OUT direction
> > + */
> > +void cdns3_check_ep0_interrupt_proceed(struct cdns3_device *priv_dev, int dir)
> > +{
> > +       u32 ep_sts_reg;
> > +
> > +       cdns3_select_ep(priv_dev, dir);
> > +
> > +       ep_sts_reg = readl(&priv_dev->regs->ep_sts);
> > +       writel(ep_sts_reg, &priv_dev->regs->ep_sts);
> > +
> > +       trace_cdns3_ep0_irq(priv_dev, ep_sts_reg);
> > +
> > +       __pending_setup_status_handler(priv_dev);
> > +
> > +       if ((ep_sts_reg & EP_STS_SETUP)) {
> > +               cdns3_ep0_setup_phase(priv_dev);
> > +       } else if ((ep_sts_reg & EP_STS_IOC) || (ep_sts_reg & EP_STS_ISP)) {
> > +               priv_dev->ep0_data_dir = dir;
> > +               cdns3_transfer_completed(priv_dev);
> > +       }
> > +
> > +       if (ep_sts_reg & EP_STS_DESCMIS) {
> > +               if (dir == 0 && !priv_dev->setup_pending)
> > +                       cdns3_prepare_setup_packet(priv_dev);
> > +       }
> > +}
> > +
> > +/**
> > + * cdns3_gadget_ep0_enable
> > + * Function shouldn't be called by gadget driver,
> > + * endpoint 0 is allways active
> > + */
> > +static int cdns3_gadget_ep0_enable(struct usb_ep *ep,
> > +                                  const struct usb_endpoint_descriptor *desc)
> > +{
> > +       return -EINVAL;
> > +}
> > +
> > +/**
> > + * cdns3_gadget_ep0_disable
> > + * Function shouldn't be called by gadget driver,
> > + * endpoint 0 is allways active
> > + */
> > +static int cdns3_gadget_ep0_disable(struct usb_ep *ep)
> > +{
> > +       return -EINVAL;
> > +}
> > +
> > +/**
> > + * cdns3_gadget_ep0_set_halt
> > + * @ep: pointer to endpoint zero object
> > + * @value: 1 for set stall, 0 for clear stall
> > + *
> > + * Returns 0
> > + */
> > +static int cdns3_gadget_ep0_set_halt(struct usb_ep *ep, int value)
> > +{
> > +       /* TODO */
> > +       return 0;
> > +}
> > +
> > +/**
> > + * cdns3_gadget_ep0_queue Transfer data on endpoint zero
> > + * @ep: pointer to endpoint zero object
> > + * @request: pointer to request object
> > + * @gfp_flags: gfp flags
> > + *
> > + * Returns 0 on success, error code elsewhere
> > + */
> > +static int cdns3_gadget_ep0_queue(struct usb_ep *ep,
> > +                                 struct usb_request *request,
> > +                                 gfp_t gfp_flags)
> > +{
> > +       struct cdns3_endpoint *priv_ep = ep_to_cdns3_ep(ep);
> > +       struct cdns3_device *priv_dev = priv_ep->cdns3_dev;
> > +       unsigned long flags;
> > +       int erdy_sent = 0;
> > +       int ret = 0;
> > +
> > +       dev_dbg(priv_dev->dev, "Queue to Ep0%s L: %d\n",
> > +               priv_dev->ep0_data_dir ? "IN" : "OUT",
> > +               request->length);
> > +
> > +       /* cancel the request if controller receive new SETUP packet. */
> > +       if (cdns3_check_new_setup(priv_dev))
> > +               return -ECONNRESET;
> > +
> > +       /* send STATUS stage. Should be called only for SET_CONFIGURATION */
> > +       if (priv_dev->ep0_stage == CDNS3_STATUS_STAGE) {
> > +               spin_lock_irqsave(&priv_dev->lock, flags);
> > +               cdns3_select_ep(priv_dev, 0x00);
> > +
> > +               erdy_sent = !priv_dev->hw_configured_flag;
> > +               cdns3_set_hw_configuration(priv_dev);
> > +
> > +               if (!erdy_sent)
> > +                       cdns3_ep0_complete_setup(priv_dev, 0, 1);
> > +
> > +               request->actual = 0;
> > +               priv_dev->status_completion_no_call = true;
> > +               priv_dev->pending_status_request = request;
> > +               spin_unlock_irqrestore(&priv_dev->lock, flags);
> > +
> > +               /*
> > +                * Since there is no completion interrupt for status stage,
> > +                * it needs to call ->completion in software after
> > +                * ep0_queue is back.
> > +                */
> > +               queue_work(system_freezable_wq, &priv_dev->pending_status_wq);
> > +               return 0;
> > +       }
> > +
> > +       spin_lock_irqsave(&priv_dev->lock, flags);
> > +       if (!list_empty(&priv_ep->pending_req_list)) {
> > +               dev_err(priv_dev->dev,
> > +                       "can't handle multiple requests for ep0\n");
> > +               spin_unlock_irqrestore(&priv_dev->lock, flags);
> > +               return -EBUSY;
> > +       }
> > +
> > +       ret = usb_gadget_map_request_by_dev(priv_dev->sysdev, request,
> > +                                           priv_dev->ep0_data_dir);
> > +       if (ret) {
> > +               spin_unlock_irqrestore(&priv_dev->lock, flags);
> > +               dev_err(priv_dev->dev, "failed to map request\n");
> > +               return -EINVAL;
> > +       }
> > +
> > +       request->status = -EINPROGRESS;
> > +       list_add_tail(&request->list, &priv_ep->pending_req_list);
> > +       cdns3_ep0_run_transfer(priv_dev, request->dma, request->length, 1);
> > +       spin_unlock_irqrestore(&priv_dev->lock, flags);
> > +
> > +       return ret;
> > +}
> > +
> > +/**
> > + * cdns3_gadget_ep_set_wedge Set wedge on selected endpoint
> > + * @ep: endpoint object
> > + *
> > + * Returns 0
> > + */
> > +int cdns3_gadget_ep_set_wedge(struct usb_ep *ep)
> > +{
> > +       struct cdns3_endpoint *priv_ep = ep_to_cdns3_ep(ep);
> > +       struct cdns3_device *priv_dev = priv_ep->cdns3_dev;
> > +
> > +       dev_dbg(priv_dev->dev, "Wedge for %s\n", ep->name);
> > +       cdns3_gadget_ep_set_halt(ep, 1);
> > +       priv_ep->flags |= EP_WEDGE;
> > +
> > +       return 0;
> > +}
> > +
> > +const struct usb_ep_ops cdns3_gadget_ep0_ops = {
> > +       .enable = cdns3_gadget_ep0_enable,
> > +       .disable = cdns3_gadget_ep0_disable,
> > +       .alloc_request = cdns3_gadget_ep_alloc_request,
> > +       .free_request = cdns3_gadget_ep_free_request,
> > +       .queue = cdns3_gadget_ep0_queue,
> > +       .dequeue = cdns3_gadget_ep_dequeue,
> > +       .set_halt = cdns3_gadget_ep0_set_halt,
> > +       .set_wedge = cdns3_gadget_ep_set_wedge,
> > +};
> > +
> > +/**
> > + * cdns3_ep0_config - Configures default endpoint
> > + * @priv_dev: extended gadget object
> > + *
> > + * Functions sets parameters: maximal packet size and enables interrupts
> > + */
> > +void cdns3_ep0_config(struct cdns3_device *priv_dev)
> > +{
> > +       struct cdns3_usb_regs __iomem *regs;
> > +       struct cdns3_endpoint *priv_ep;
> > +       u32 max_packet_size = 64;
> > +
> > +       regs = priv_dev->regs;
> > +
> > +       if (priv_dev->gadget.speed == USB_SPEED_SUPER)
> > +               max_packet_size = 512;
> > +
> > +       priv_ep = priv_dev->eps[0];
> > +
> > +       if (!list_empty(&priv_ep->pending_req_list)) {
> > +               struct usb_request *request;
> > +
> > +               request = cdns3_next_request(&priv_ep->pending_req_list);
> > +               list_del_init(&request->list);
> > +       }
> > +
> > +       priv_dev->u1_allowed = 0;
> > +       priv_dev->u2_allowed = 0;
> > +
> > +       priv_dev->gadget.ep0->maxpacket = max_packet_size;
> > +       cdns3_gadget_ep0_desc.wMaxPacketSize = cpu_to_le16(max_packet_size);
> > +
> > +       /* init ep out */
> > +       cdns3_select_ep(priv_dev, USB_DIR_OUT);
> > +
> > +       writel(EP_CFG_ENABLE | EP_CFG_MAXPKTSIZE(max_packet_size),
> > +              &regs->ep_cfg);
> > +
> > +       writel(EP_STS_EN_SETUPEN | EP_STS_EN_DESCMISEN | EP_STS_EN_TRBERREN,
> > +              &regs->ep_sts_en);
> > +
> > +       /* init ep in */
> > +       cdns3_select_ep(priv_dev, USB_DIR_IN);
> > +
> > +       writel(EP_CFG_ENABLE | EP_CFG_MAXPKTSIZE(max_packet_size),
> > +              &regs->ep_cfg);
> > +
> > +       writel(EP_STS_EN_SETUPEN | EP_STS_EN_TRBERREN, &regs->ep_sts_en);
> > +
> > +       cdns3_set_register_bit(&regs->usb_conf, USB_CONF_U1DS | USB_CONF_U2DS);
> > +}
> > +
> > +/**
> > + * cdns3_init_ep0 Initializes software endpoint 0 of gadget
> > + * @priv_dev: extended gadget object
> > + * @ep_priv: extended endpoint object
> > + *
> > + * Returns 0 on success else error code.
> > + */
> > +int cdns3_init_ep0(struct cdns3_device *priv_dev,
> > +                  struct cdns3_endpoint *priv_ep)
> > +{
> > +       sprintf(priv_ep->name, "ep0");
> > +
> > +       /* fill linux fields */
> > +       priv_ep->endpoint.ops = &cdns3_gadget_ep0_ops;
> > +       priv_ep->endpoint.maxburst = 1;
> > +       usb_ep_set_maxpacket_limit(&priv_ep->endpoint,
> > +                                  CDNS3_EP0_MAX_PACKET_LIMIT);
> > +       priv_ep->endpoint.address = 0;
> > +       priv_ep->endpoint.caps.type_control = 1;
> > +       priv_ep->endpoint.caps.dir_in = 1;
> > +       priv_ep->endpoint.caps.dir_out = 1;
> > +       priv_ep->endpoint.name = priv_ep->name;
> > +       priv_ep->endpoint.desc = &cdns3_gadget_ep0_desc;
> > +       priv_dev->gadget.ep0 = &priv_ep->endpoint;
> > +       priv_ep->type = USB_ENDPOINT_XFER_CONTROL;
> > +
> > +       return cdns3_allocate_trb_pool(priv_ep);
> > +}
> > diff --git a/drivers/usb/cdns3/gadget-export.h b/drivers/usb/cdns3/gadget-export.h
> > new file mode 100644
> > index 000000000000..577469eee961
> > --- /dev/null
> > +++ b/drivers/usb/cdns3/gadget-export.h
> > @@ -0,0 +1,28 @@
> > +/* SPDX-License-Identifier: GPL-2.0 */
> > +/*
> > + * Cadence USBSS DRD Driver - Gadget Export APIs.
> > + *
> > + * Copyright (C) 2017 NXP
> > + * Copyright (C) 2017-2018 NXP
> > + *
> > + * Authors: Peter Chen <peter.chen@nxp.com>
> > + */
> > +#ifndef __LINUX_CDNS3_GADGET_EXPORT
> > +#define __LINUX_CDNS3_GADGET_EXPORT
> > +
> > +#ifdef CONFIG_USB_CDNS3_GADGET
> > +
> > +int cdns3_gadget_init(struct cdns3 *cdns);
> > +void cdns3_gadget_exit(struct cdns3 *cdns);
> > +#else
> > +
> > +static inline int cdns3_gadget_init(struct cdns3 *cdns)
> > +{
> > +       return -ENXIO;
> > +}
> > +
> > +static inline void cdns3_gadget_exit(struct cdns3 *cdns) { }
> > +
> > +#endif
> > +
> > +#endif /* __LINUX_CDNS3_GADGET_EXPORT */
> > diff --git a/drivers/usb/cdns3/gadget.c b/drivers/usb/cdns3/gadget.c
> > new file mode 100644
> > index 000000000000..0d95eb00be37
> > --- /dev/null
> > +++ b/drivers/usb/cdns3/gadget.c
> > @@ -0,0 +1,2102 @@
> > +// SPDX-License-Identifier: GPL-2.0
> > +/*
> > + * Cadence USBSS DRD Driver - gadget side.
> > + *
> > + * Copyright (C) 2018 Cadence Design Systems.
> > + * Copyright (C) 2017-2018 NXP
> > + *
> > + * Authors: Pawel Jez <pjez@cadence.com>,
> > + *          Pawel Laszczak <pawell@cadence.com>
> > + *         Peter Chen <peter.chen@nxp.com>
> > + */
> > +
> > +#include <linux/dma-mapping.h>
> > +#include <linux/usb/gadget.h>
> > +
> > +#include "core.h"
> > +#include "gadget-export.h"
> > +#include "gadget.h"
> > +
> > +#include "trace.h"
> > +
> > +static int __cdns3_gadget_ep_queue(struct usb_ep *ep,
> > +                                  struct usb_request *request,
> > +                                  gfp_t gfp_flags);
> > +
> > +/**
> > + * cdns3_handshake - spin reading  until handshake completes or fails
> > + * @ptr: address of device controller register to be read
> > + * @mask: bits to look at in result of read
> > + * @done: value of those bits when handshake succeeds
> > + * @usec: timeout in microseconds
> > + *
> > + * Returns negative errno, or zero on success
> > + *
> > + * Success happens when the "mask" bits have the specified value (hardware
> > + * handshake done). There are two failure modes: "usec" have passed (major
> > + * hardware flakeout), or the register reads as all-ones (hardware removed).
> > + */
> > +int cdns3_handshake(void __iomem *ptr, u32 mask, u32 done, int usec)
> > +{
> > +       u32     result;
> > +
> > +       do {
> > +               result = readl(ptr);
> > +               if (result == ~(u32)0)  /* card removed */
> > +                       return -ENODEV;
> > +               result &= mask;
> > +               if (result == done)
> > +                       return 0;
> > +               udelay(1);
> > +               usec--;
> > +       } while (usec > 0);
> > +       return -ETIMEDOUT;
> > +}
> > +
> > +/**
> > + * cdns3_set_register_bit - set bit in given register.
> > + * @ptr: address of device controller register to be read and changed
> > + * @mask: bits requested to set
> > + */
> > +void cdns3_set_register_bit(void __iomem *ptr, u32 mask)
> > +{
> > +       mask = readl(ptr) | mask;
> > +       writel(mask, ptr);
> > +}
> > +
> > +/**
> > + * cdns3_ep_addr_to_index - Macro converts endpoint address to
> > + * index of endpoint object in cdns3_device.eps[] container
> > + * @ep_addr: endpoint address for which endpoint object is required
> > + *
> > + */
> > +u8 cdns3_ep_addr_to_index(u8 ep_addr)
> > +{
> > +       return (((ep_addr & 0x7F)) + ((ep_addr & USB_DIR_IN) ? 16 : 0));
> > +}
> > +
> > +/**
> > + * cdns3_next_request - returns next request from list
> > + * @list: list containing requests
> > + *
> > + * Returns request or NULL if no requests in list
> > + */
> > +struct usb_request *cdns3_next_request(struct list_head *list)
> > +{
> > +       if (list_empty(list))
> > +               return NULL;
> > +       return list_first_entry_or_null(list, struct usb_request, list);
> > +}
> > +
> > +/**
> > + * cdns3_next_priv_request - returns next request from list
> > + * @list: list containing requests
> > + *
> > + * Returns request or NULL if no requests in list
> > + */
> > +struct cdns3_request *cdns3_next_priv_request(struct list_head *list)
> > +{
> > +       if (list_empty(list))
> > +               return NULL;
> > +       return list_first_entry_or_null(list, struct cdns3_request, list);
> > +}
> > +
> > +/**
> > + * select_ep - selects endpoint
> > + * @priv_dev:  extended gadget object
> > + * @ep: endpoint address
> > + */
> > +void cdns3_select_ep(struct cdns3_device *priv_dev, u32 ep)
> > +{
> > +       if (priv_dev->selected_ep == ep)
> > +               return;
> > +
> > +       priv_dev->selected_ep = ep;
> > +       writel(ep, &priv_dev->regs->ep_sel);
> > +}
> > +
> > +dma_addr_t cdns3_trb_virt_to_dma(struct cdns3_endpoint *priv_ep,
> > +                                struct cdns3_trb *trb)
> > +{
> > +       u32 offset = (char *)trb - (char *)priv_ep->trb_pool;
> > +
> > +       return priv_ep->trb_pool_dma + offset;
> > +}
> > +
> > +int cdns3_ring_size(struct cdns3_endpoint *priv_ep)
> > +{
> > +       switch (priv_ep->type) {
> > +       case USB_ENDPOINT_XFER_ISOC:
> > +               return TRB_ISO_RING_SIZE;
> > +       case USB_ENDPOINT_XFER_CONTROL:
> > +               return TRB_CTRL_RING_SIZE;
> > +       default:
> > +               return TRB_RING_SIZE;
> > +       }
> > +}
> > +
> > +/**
> > + * cdns3_allocate_trb_pool - Allocates TRB's pool for selected endpoint
> > + * @priv_ep:  endpoint object
> > + *
> > + * Function will return 0 on success or -ENOMEM on allocation error
> > + */
> > +int cdns3_allocate_trb_pool(struct cdns3_endpoint *priv_ep)
> > +{
> > +       struct cdns3_device *priv_dev = priv_ep->cdns3_dev;
> > +       int ring_size = cdns3_ring_size(priv_ep);
> > +       struct cdns3_trb *link_trb;
> > +
> > +       if (!priv_ep->trb_pool) {
> > +               priv_ep->trb_pool = dma_zalloc_coherent(priv_dev->sysdev,
> > +                                                       ring_size,
> > +                                                       &priv_ep->trb_pool_dma,
> > +                                                       GFP_DMA);
> > +               if (!priv_ep->trb_pool)
> > +                       return -ENOMEM;
> > +       } else {
> > +               memset(priv_ep->trb_pool, 0, ring_size);
> > +       }
> > +
> > +       if (!priv_ep->num)
> > +               return 0;
> > +
> > +       if (!priv_ep->aligned_buff) {
> > +               void *buff = dma_alloc_coherent(priv_dev->sysdev,
> > +                                               CDNS3_ALIGNED_BUF_SIZE,
> > +                                               &priv_ep->aligned_dma_addr,
> > +                                               GFP_DMA);
> > +
> > +               priv_ep->aligned_buff  = buff;
> > +               if (!priv_ep->aligned_buff) {
> > +                       dma_free_coherent(priv_dev->sysdev,
> > +                                         ring_size,
> > +                                         priv_ep->trb_pool,
> > +                                         priv_ep->trb_pool_dma);
> > +                       priv_ep->trb_pool = NULL;
> > +
> > +                       return -ENOMEM;
> > +               }
> > +       }
> > +
> > +       priv_ep->num_trbs = ring_size / TRB_SIZE;
> > +       /* Initialize the last TRB as Link TRB */
> > +       link_trb = (priv_ep->trb_pool + (priv_ep->num_trbs - 1));
> > +       link_trb->buffer = TRB_BUFFER(priv_ep->trb_pool_dma);
> > +       link_trb->control = TRB_CYCLE | TRB_TYPE(TRB_LINK) |
> > +                           TRB_CHAIN | TRB_TOGGLE;
> > +
> > +       return 0;
> > +}
> > +
> > +static void cdns3_free_trb_pool(struct cdns3_endpoint *priv_ep)
> > +{
> > +       struct cdns3_device *priv_dev = priv_ep->cdns3_dev;
> > +
> > +       if (priv_ep->trb_pool) {
> > +               dma_free_coherent(priv_dev->sysdev,
> > +                                 cdns3_ring_size(priv_ep),
> > +                                 priv_ep->trb_pool, priv_ep->trb_pool_dma);
> > +               priv_ep->trb_pool = NULL;
> > +       }
> > +
> > +       if (priv_ep->aligned_buff) {
> > +               dma_free_coherent(priv_dev->sysdev, CDNS3_ALIGNED_BUF_SIZE,
> > +                                 priv_ep->aligned_buff,
> > +                                 priv_ep->aligned_dma_addr);
> > +               priv_ep->aligned_buff = NULL;
> > +       }
> > +}
> > +
> > +/**
> > + * cdns3_data_flush - flush data at onchip buffer
> > + * @priv_ep: endpoint object
> > + *
> > + * Endpoint must be selected before call to this function
> > + *
> > + * Returns zero on success or negative value on failure
> > + */
> > +static int cdns3_data_flush(struct cdns3_endpoint *priv_ep)
> > +{
> > +       struct cdns3_device *priv_dev = priv_ep->cdns3_dev;
> > +
> > +       writel(EP_CMD_DFLUSH, &priv_dev->regs->ep_cmd);
> > +
> > +       /* wait for DFLUSH cleared */
> > +       return cdns3_handshake(&priv_dev->regs->ep_cmd, EP_CMD_DFLUSH, 0, 100);
> > +}
> > +
> > +/**
> > + * cdns3_ep_stall_flush - Stalls and flushes selected endpoint
> > + * @priv_ep: endpoint object
> > + *
> > + * Endpoint must be selected before call to this function
> > + */
> > +static void cdns3_ep_stall_flush(struct cdns3_endpoint *priv_ep)
> > +{
> > +       struct cdns3_device *priv_dev = priv_ep->cdns3_dev;
> > +
> > +       writel(EP_CMD_DFLUSH | EP_CMD_ERDY | EP_CMD_SSTALL,
> > +              &priv_dev->regs->ep_cmd);
> > +
> > +       /* wait for DFLUSH cleared */
> > +       cdns3_handshake(&priv_dev->regs->ep_cmd, EP_CMD_DFLUSH, 0, 100);
> > +       priv_ep->flags |= EP_STALL;
> > +}
> > +
> > +/**
> > + * cdns3_hw_reset_eps_config - reset endpoints configuration kept by controller.
> > + * @priv_dev: extended gadget object
> > + */
> > +void cdns3_hw_reset_eps_config(struct cdns3_device *priv_dev)
> > +{
> > +       writel(USB_CONF_CFGRST, &priv_dev->regs->usb_conf);
> > +
> > +       cdns3_allow_enable_l1(priv_dev, 0);
> > +       priv_dev->hw_configured_flag = 0;
> > +       priv_dev->onchip_mem_allocated_size = 0;
> > +}
> > +
> > +/**
> > + * cdns3_ep_inc_trb - increment a trb index.
> > + * @index: Pointer to the TRB index to increment.
> > + * @cs: Cycle state
> > + * @trb_in_seg: number of TRBs in segment
> > + *
> > + * The index should never point to the link TRB. After incrementing,
> > + * if it is point to the link TRB, wrap around to the beginning and revert
> > + * cycle state bit The
> > + * link TRB is always at the last TRB entry.
> > + */
> > +static void cdns3_ep_inc_trb(int *index, u8 *cs, int trb_in_seg)
> > +{
> > +       (*index)++;
> > +       if (*index == (trb_in_seg - 1)) {
> > +               *index = 0;
> > +               *cs ^=  1;
> > +       }
> > +}
> > +
> > +/**
> > + * cdns3_ep_inc_enq - increment endpoint's enqueue pointer
> > + * @priv_ep: The endpoint whose enqueue pointer we're incrementing
> > + */
> > +static void cdns3_ep_inc_enq(struct cdns3_endpoint *priv_ep)
> > +{
> > +       priv_ep->free_trbs--;
> > +       cdns3_ep_inc_trb(&priv_ep->enqueue, &priv_ep->pcs, priv_ep->num_trbs);
> > +}
> > +
> > +/**
> > + * cdns3_ep_inc_deq - increment endpoint's dequeue pointer
> > + * @priv_ep: The endpoint whose dequeue pointer we're incrementing
> > + */
> > +static void cdns3_ep_inc_deq(struct cdns3_endpoint *priv_ep)
> > +{
> > +       priv_ep->free_trbs++;
> > +       cdns3_ep_inc_trb(&priv_ep->dequeue, &priv_ep->ccs, priv_ep->num_trbs);
> > +}
> > +
> > +/**
> > + * cdns3_allow_enable_l1 - enable/disable permits to transition to L1.
> > + * @priv_dev: Extended gadget object
> > + * @enable: Enable/disable permit to transition to L1.
> > + *
> > + * If bit USB_CONF_L1EN is set and device receive Extended Token packet,
> > + * then controller answer with ACK handshake.
> > + * If bit USB_CONF_L1DS is set and device receive Extended Token packet,
> > + * then controller answer with NYET handshake.
> > + */
> > +void cdns3_allow_enable_l1(struct cdns3_device *priv_dev, int enable)
> > +{
> > +       if (enable)
> > +               writel(USB_CONF_L1EN, &priv_dev->regs->usb_conf);
> > +       else
> > +               writel(USB_CONF_L1DS, &priv_dev->regs->usb_conf);
> > +}
> > +
> > +enum usb_device_speed cdns3_get_speed(struct cdns3_device *priv_dev)
> > +{
> > +       u32 reg;
> > +
> > +       reg = readl(&priv_dev->regs->usb_sts);
> > +
> > +       if (DEV_SUPERSPEED(reg))
> > +               return USB_SPEED_SUPER;
> > +       else if (DEV_HIGHSPEED(reg))
> > +               return USB_SPEED_HIGH;
> > +       else if (DEV_FULLSPEED(reg))
> > +               return USB_SPEED_FULL;
> > +       else if (DEV_LOWSPEED(reg))
> > +               return USB_SPEED_LOW;
> > +       return USB_SPEED_UNKNOWN;
> > +}
> > +
> > +/**
> > + * cdns3_start_all_request - add to ring all request not started
> > + * @priv_dev: Extended gadget object
> > + * @priv_ep: The endpoint for whom request will be started.
> > + *
> > + * Returns return ENOMEM if transfer ring i not enough TRBs to start
> > + *         all requests.
> > + */
> > +static int cdns3_start_all_request(struct cdns3_device *priv_dev,
> > +                                  struct cdns3_endpoint *priv_ep)
> > +{
> > +       struct cdns3_request *priv_req;
> > +       struct usb_request *request;
> > +       int ret = 0;
> > +
> > +       while (!list_empty(&priv_ep->deferred_req_list)) {
> > +               request = cdns3_next_request(&priv_ep->deferred_req_list);
> > +               priv_req = to_cdns3_request(request);
> > +
> > +               ret = cdns3_ep_run_transfer(priv_ep, request);
> > +               if (ret)
> > +                       return ret;
> > +
> > +               list_del(&request->list);
> > +               list_add_tail(&request->list,
> > +                             &priv_ep->pending_req_list);
> > +       }
> > +
> > +       priv_ep->flags &= ~EP_RING_FULL;
> > +       return ret;
> > +}
> > +
> > +/**
> > + * cdns3_descmiss_copy_data copy data from internal requests to request queued
> > + * by class driver.
> > + * @priv_ep: extended endpoint object
> > + * @request: request object
> > + */
> > +static void cdns3_descmiss_copy_data(struct cdns3_endpoint *priv_ep,
> > +                                    struct usb_request *request)
> > +{
> > +       struct usb_request *descmiss_req;
> > +       struct cdns3_request *descmiss_priv_req;
> > +
> > +       while (!list_empty(&priv_ep->descmiss_req_list)) {
> > +               int chunk_end;
> > +               int length;
> > +
> > +               descmiss_priv_req =
> > +                       cdns3_next_priv_request(&priv_ep->descmiss_req_list);
> > +               descmiss_req = &descmiss_priv_req->request;
> > +
> > +               /* driver can't touch pending request */
> > +               if (descmiss_priv_req->flags & REQUEST_PENDING)
> > +                       break;
> > +
> > +               chunk_end = descmiss_priv_req->flags & REQUEST_INTERNAL_CH;
> > +               length = request->actual + descmiss_req->actual;
> > +
> > +               if (length <= request->length) {
> > +                       memcpy(&((u8 *)request->buf)[request->actual],
> > +                              descmiss_req->buf,
> > +                              descmiss_req->actual);
> > +                       request->actual = length;
> > +               } else {
> > +                       /* It should never occures */
> > +                       request->status = -ENOMEM;
> > +               }
> > +
> > +               list_del_init(&descmiss_priv_req->list);
> > +
> > +               kfree(descmiss_req->buf);
> > +               cdns3_gadget_ep_free_request(&priv_ep->endpoint, descmiss_req);
> > +
> > +               if (!chunk_end)
> > +                       break;
> > +       }
> > +}
> > +
> > +/**
> > + * cdns3_gadget_giveback - call struct usb_request's ->complete callback
> > + * @priv_ep: The endpoint to whom the request belongs to
> > + * @priv_req: The request we're giving back
> > + * @status: completion code for the request
> > + *
> > + * Must be called with controller's lock held and interrupts disabled. This
> > + * function will unmap @req and call its ->complete() callback to notify upper
> > + * layers that it has completed.
> > + */
> > +void cdns3_gadget_giveback(struct cdns3_endpoint *priv_ep,
> > +                          struct cdns3_request *priv_req,
> > +                          int status)
> > +{
> > +       struct cdns3_device *priv_dev = priv_ep->cdns3_dev;
> > +       struct usb_request *request = &priv_req->request;
> > +
> > +       list_del_init(&request->list);
> > +
> > +       if (request->status == -EINPROGRESS)
> > +               request->status = status;
> > +
> > +       usb_gadget_unmap_request_by_dev(priv_dev->sysdev, request,
> > +                                       priv_ep->dir);
> > +
> > +       priv_req->flags &= ~REQUEST_PENDING;
> > +       trace_cdns3_gadget_giveback(priv_req);
> > +
> > +       /* WA1: */
> > +       if (priv_ep->flags & EP_QUIRK_EXTRA_BUF_EN &&
> > +           priv_req->flags & REQUEST_INTERNAL) {
> > +               struct usb_request *req;
> > +
> > +               req = cdns3_next_request(&priv_ep->deferred_req_list);
> > +               request = req;
> > +               priv_ep->descmis_req = NULL;
> > +
> > +               if (!req)
> > +                       return;
> > +
> > +               cdns3_descmiss_copy_data(priv_ep, req);
> > +               if (!(priv_ep->flags & EP_QUIRK_END_TRANSFER) &&
> > +                   req->length != req->actual) {
> > +                       /* wait for next part of transfer */
> > +                       return;
> > +               }
> > +
> > +               if (req->status == -EINPROGRESS)
> > +                       req->status = 0;
> > +
> > +               list_del_init(&req->list);
> > +               cdns3_start_all_request(priv_dev, priv_ep);
> > +       }
> > +
> > +       /* Start all not pending request */
> > +       if (priv_ep->flags & EP_RING_FULL)
> > +               cdns3_start_all_request(priv_dev, priv_ep);
> > +
> > +       if (request->complete) {
> > +               spin_unlock(&priv_dev->lock);
> > +               usb_gadget_giveback_request(&priv_ep->endpoint,
> > +                                           request);
> > +               spin_lock(&priv_dev->lock);
> > +       }
> > +
> > +       if (request->buf == priv_dev->zlp_buf)
> > +               cdns3_gadget_ep_free_request(&priv_ep->endpoint, request);
> > +}
> > +
> > +/**
> > + * cdns3_ep_run_transfer - start transfer on no-default endpoint hardware
> > + * @priv_ep: endpoint object
> > + *
> > + * Returns zero on success or negative value on failure
> > + */
> > +int cdns3_ep_run_transfer(struct cdns3_endpoint *priv_ep,
> > +                         struct usb_request *request)
> > +{
> > +       struct cdns3_device *priv_dev = priv_ep->cdns3_dev;
> > +       struct cdns3_request *priv_req;
> > +       struct cdns3_trb *trb;
> > +       dma_addr_t trb_dma;
> > +       int sg_iter = 0;
> > +       u32 first_pcs;
> > +       int  num_trb;
> > +       int address;
> > +       int pcs;
> > +
> > +       if (priv_ep->type == USB_ENDPOINT_XFER_ISOC)
> > +               num_trb = priv_ep->interval;
> > +       else
> > +               num_trb = request->num_sgs ? request->num_sgs : 1;
> > +
> > +       if (num_trb > priv_ep->free_trbs) {
> > +               priv_ep->flags |= EP_RING_FULL;
> > +               return -ENOBUFS;
> > +       }
> > +
> > +       priv_req = to_cdns3_request(request);
> > +       address = priv_ep->endpoint.desc->bEndpointAddress;
> > +
> > +       priv_ep->flags |= EP_PENDING_REQUEST;
> > +       trb_dma = request->dma;
> > +
> > +       /* must allocate buffer aligned to 8 */
> > +       if ((request->dma % 8)) {
> > +               if (request->length <= CDNS3_ALIGNED_BUF_SIZE) {
> > +                       memcpy(priv_ep->aligned_buff, request->buf,
> > +                              request->length);
> > +                       trb_dma = priv_ep->aligned_dma_addr;
> > +               } else {
> > +                       return -ENOMEM;
> > +               }
> > +       }
> > +
> > +       trb = priv_ep->trb_pool + priv_ep->enqueue;
> > +       priv_req->start_trb = priv_ep->enqueue;
> > +       priv_req->trb = trb;
> > +
> > +       /* prepare ring */
> > +       if ((priv_ep->enqueue + num_trb)  >= (priv_ep->num_trbs - 1)) {
> > +               /*updating C bt in  Link TRB before starting DMA*/
> > +               struct cdns3_trb *link_trb = priv_ep->trb_pool +
> > +                                            (priv_ep->num_trbs - 1);
> > +               link_trb->control = ((priv_ep->pcs) ? TRB_CYCLE : 0) |
> > +                                   TRB_TYPE(TRB_LINK) | TRB_CHAIN |
> > +                                   TRB_TOGGLE;
> > +       }
> > +
> > +       first_pcs = priv_ep->pcs ? TRB_CYCLE : 0;
> > +
> > +       do {
> > +       /* fill TRB */
> > +               trb->buffer = TRB_BUFFER(request->num_sgs == 0
> > +                               ? trb_dma : request->sg[sg_iter].dma_address);
> > +
> > +               trb->length = TRB_BURST_LEN(16) |
> > +                   TRB_LEN(request->num_sgs == 0 ?
> > +                               request->length : request->sg[sg_iter].length);
> > +
> > +               trb->control = TRB_TYPE(TRB_NORMAL);
> > +               pcs = priv_ep->pcs ? TRB_CYCLE : 0;
> > +
> > +               /*
> > +                * first trb should be prepared as last to avoid processing
> > +                *  transfer to early
> > +                */
> > +               if (sg_iter != 0)
> > +                       trb->control |= pcs;
> > +
> > +               if (priv_ep->type == USB_ENDPOINT_XFER_ISOC  && !priv_ep->dir) {
> > +                       trb->control |= TRB_IOC | TRB_ISP;
> > +               } else {
> > +                       /* for last element in TD or in SG list */
> > +                       if (sg_iter == (num_trb - 1) && sg_iter != 0)
> > +                               trb->control |= pcs | TRB_IOC | TRB_ISP;
> > +               }
> > +               ++sg_iter;
> > +               priv_req->end_trb = priv_ep->enqueue;
> > +               cdns3_ep_inc_enq(priv_ep);
> > +               trb = priv_ep->trb_pool + priv_ep->enqueue;
> > +       } while (sg_iter < num_trb);
> > +
> > +       trb = priv_req->trb;
> > +
> > +       /*
> > +        * Memory barrier = Cycle Bit must be set before trb->length  and
> > +        * trb->buffer fields.
> > +        */
> > +       wmb();
> > +
> > +       priv_req->flags |= REQUEST_PENDING;
> > +
> > +       /* give the TD to the consumer*/
> > +       if (sg_iter == 1)
> > +               trb->control |= first_pcs | TRB_IOC | TRB_ISP;
> > +       else
> > +               trb->control |= first_pcs;
> > +
> > +       trace_cdns3_prepare_trb(priv_ep, priv_req->trb);
> > +       trace_cdns3_ring(priv_ep);
> > +
> > +       /* arm transfer on selected endpoint */
> > +       cdns3_select_ep(priv_ep->cdns3_dev, address);
> > +
> > +       /*
> > +        * For DMULT mode we can set address to transfer ring only once after
> > +        * enabling endpoint.
> > +        */
> > +       if (priv_ep->flags & EP_UPDATE_EP_TRBADDR) {
> > +               writel(EP_TRADDR_TRADDR(priv_ep->trb_pool_dma),
> > +                      &priv_dev->regs->ep_traddr);
> > +               priv_ep->flags &= ~EP_UPDATE_EP_TRBADDR;
> > +       }
> > +
> > +       /*clearing TRBERR and EP_STS_DESCMIS before seting DRDY*/
> > +       writel(EP_STS_TRBERR | EP_STS_DESCMIS, &priv_dev->regs->ep_sts);
> > +       trace_cdns3_doorbell_epx(priv_ep->name,
> > +                                readl(&priv_dev->regs->ep_traddr));
> > +       writel(EP_CMD_DRDY, &priv_dev->regs->ep_cmd);
> > +
> > +       return 0;
> > +}
> > +
> > +void cdns3_set_hw_configuration(struct cdns3_device *priv_dev)
> > +{
> > +       struct cdns3_endpoint *priv_ep;
> > +       struct usb_ep *ep;
> > +       int result = 0;
> > +
> > +       if (priv_dev->hw_configured_flag)
> > +               return;
> > +
> > +       writel(USB_CONF_CFGSET, &priv_dev->regs->usb_conf);
> > +       writel(EP_CMD_ERDY | EP_CMD_REQ_CMPL, &priv_dev->regs->ep_cmd);
> > +
> > +       cdns3_set_register_bit(&priv_dev->regs->usb_conf,
> > +                              USB_CONF_U1EN | USB_CONF_U2EN);
> > +
> > +       /* wait until configuration set */
> > +       result = cdns3_handshake(&priv_dev->regs->usb_sts,
> > +                                USB_STS_CFGSTS_MASK, 1, 100);
> > +
> > +       priv_dev->hw_configured_flag = 1;
> > +       cdns3_allow_enable_l1(priv_dev, 1);
> > +
> > +       list_for_each_entry(ep, &priv_dev->gadget.ep_list, ep_list) {
> > +               if (ep->enabled) {
> > +                       priv_ep = ep_to_cdns3_ep(ep);
> > +                       cdns3_start_all_request(priv_dev, priv_ep);
> > +               }
> > +       }
> > +}
> > +
> > +/**
> > + * cdns3_request_handled - check whether request has been handled by DMA
> > + *
> > + * @priv_ep: extended endpoint object.
> > + * @priv_req: request object for checking
> > + *
> > + * Endpoint must be selected before invoking this function.
> > + *
> > + * Returns false if request has not been handled by DMA, else returns true.
> > + *
> > + * SR - start ring
> > + * ER -  end ring
> > + * DQ = priv_ep->dequeue - dequeue position
> > + * EQ = priv_ep->enqueue -  enqueue position
> > + * ST = priv_req->start_trb - index of first TRB in transfer ring
> > + * ET = priv_req->end_trb - index of last TRB in transfer ring
> > + * CI = current_index - index of processed TRB by DMA.
> > + *
> > + * As first step, function checks if cycle bit for priv_req->start_trb is
> > + * correct.
> > + *
> > + * some rules:
> > + * 1. priv_ep->dequeue never exceed current_index.
> > + * 2  priv_ep->enqueue never exceed priv_ep->dequeue
> > + *
> > + * Then We can split recognition into two parts:
> > + * Case 1 - priv_ep->dequeue < current_index
> > + *      SR ... EQ ... DQ ... CI ... ER
> > + *      SR ... DQ ... CI ... EQ ... ER
> > + *
> > + *      Request has been handled by DMA if ST and ET is between DQ and CI.
> > + *
> > + * Case 2 - priv_ep->dequeue > current_index
> > + * This situation take place when CI go through the LINK TRB at the end of
> > + * transfer ring.
> > + *      SR ... CI ... EQ ... DQ ... ER
> > + *
> > + *      Request has been handled by DMA if ET is less then CI or
> > + *      ET is greater or equal DQ.
> > + */
> > +static bool cdns3_request_handled(struct cdns3_endpoint *priv_ep,
> > +                                 struct cdns3_request *priv_req)
> > +{
> > +       struct cdns3_device *priv_dev = priv_ep->cdns3_dev;
> > +       struct cdns3_trb *trb = priv_req->trb;
> > +       int current_index = 0;
> > +       int handled = 0;
> > +
> > +       current_index = (readl(&priv_dev->regs->ep_traddr) -
> > +                        priv_ep->trb_pool_dma) / TRB_SIZE;
> > +
> > +       trb = &priv_ep->trb_pool[priv_req->start_trb];
> > +
> > +       if ((trb->control  & TRB_CYCLE) != priv_ep->ccs)
> > +               goto finish;
> > +
> > +       if (priv_ep->dequeue < current_index) {
> > +               if ((current_index == (priv_ep->num_trbs - 1)) &&
> > +                   !priv_ep->dequeue)
> > +                       goto finish;
> > +
> > +               if (priv_req->end_trb >= priv_ep->dequeue &&
> > +                   priv_req->end_trb < current_index)
> > +                       handled = 1;
> > +       } else if (priv_ep->dequeue  > current_index) {
> > +               if (priv_req->end_trb  < current_index ||
> > +                   priv_req->end_trb >= priv_ep->dequeue)
> > +                       handled = 1;
> > +       }
> > +
> > +finish:
> > +       trace_cdns3_request_handled(priv_req, current_index, handled);
> > +
> > +       return handled;
> > +}
> > +
> > +static void cdns3_transfer_completed(struct cdns3_device *priv_dev,
> > +                                    struct cdns3_endpoint *priv_ep)
> > +{
> > +       struct cdns3_request *priv_req;
> > +       struct usb_request *request;
> > +       struct cdns3_trb *trb;
> > +       int current_trb;
> > +
> > +       while (!list_empty(&priv_ep->pending_req_list)) {
> > +               request = cdns3_next_request(&priv_ep->pending_req_list);
> > +               priv_req = to_cdns3_request(request);
> > +
> > +               if (!cdns3_request_handled(priv_ep, priv_req))
> > +                       return;
> > +
> > +               if (request->dma % 8 && priv_ep->dir == USB_DIR_OUT)
> > +                       memcpy(request->buf, priv_ep->aligned_buff,
> > +                              request->length);
> > +
> > +               trb = priv_ep->trb_pool + priv_ep->dequeue;
> > +               trace_cdns3_complete_trb(priv_ep, trb);
> > +
> > +               if (trb != priv_req->trb)
> > +                       dev_warn(priv_dev->dev,
> > +                                "request_trb=0x%p, queue_trb=0x%p\n",
> > +                                priv_req->trb, trb);
> > +
> > +               request->actual = TRB_LEN(le32_to_cpu(trb->length));
> > +               current_trb = priv_req->start_trb;
> > +
> > +               while (current_trb != priv_req->end_trb) {
> > +                       cdns3_ep_inc_deq(priv_ep);
> > +                       current_trb = priv_ep->dequeue;
> > +               }
> > +
> > +               cdns3_ep_inc_deq(priv_ep);
> > +               cdns3_gadget_giveback(priv_ep, priv_req, 0);
> > +       }
> > +       priv_ep->flags &= ~EP_PENDING_REQUEST;
> > +}
> > +
> > +/**
> > + * cdns3_descmissing_packet - handles descriptor missing event.
> > + * @priv_dev: extended gadget object
> > + *
> > + * WA1: Controller for OUT endpoints has shared on-chip buffers for all incoming
> > + * packets, including ep0out. It's FIFO buffer, so packets must be handle by DMA
> > + * in correct order. If the first packet in the buffer will not be handled,
> > + * then the following packets directed for other endpoints and  functions
> > + * will be blocked.
> > + * Additionally the packets directed to one endpoint can block entire on-chip
> > + * buffers. In this case transfer to other endpoints also will blocked.
> > + *
> > + * To resolve this issue after raising the descriptor missing interrupt
> > + * driver prepares internal usb_request object and use it to arm DMA transfer.
> > + *
> > + * The problematic situation was observed in case when endpoint has been enabled
> > + * but no usb_request were queued. Driver try detects such endpoints and will
> > + * use this workaround only for these endpoint.
> > + *
> > + * Driver use limited number of buffer. This number can be set by macro
> > + * CDNS_WA1_NUM_BUFFERS.
> > + *
> > + * Such blocking situation was observed on ACM gadget. For this function
> > + * host send OUT data packet but ACM function is not prepared for this packet.
> > + *
> > + * It's limitation of controller but maybe this issues should be fixed in
> > + * function driver.
> > + */
> > +static int cdns3_descmissing_packet(struct cdns3_endpoint *priv_ep)
> > +{
> > +       struct cdns3_request *priv_req;
> > +       struct usb_request *request;
> > +
> > +       if (priv_ep->flags & EP_QUIRK_EXTRA_BUF_DET) {
> > +               priv_ep->flags &= ~EP_QUIRK_EXTRA_BUF_DET;
> > +               priv_ep->flags |= EP_QUIRK_EXTRA_BUF_EN;
> > +       }
> > +
> > +       request = cdns3_gadget_ep_alloc_request(&priv_ep->endpoint,
> > +                                               GFP_ATOMIC);
> > +       if (!request)
> > +               return -ENOMEM;
> > +
> > +       priv_req = to_cdns3_request(request);
> > +       priv_req->flags |= REQUEST_INTERNAL;
> > +
> > +       /* if this field is still assigned it indicate that transfer related
> > +        * with this request has not been finished yet. Driver in this
> > +        * case simply allocate next request and assign flag REQUEST_INTERNAL_CH
> > +        * flag to previous one. It will indicate that current request is
> > +        * part of the previous one.
> > +        */
> > +       if (priv_ep->descmis_req)
> > +               priv_ep->descmis_req->flags |= REQUEST_INTERNAL_CH;
> > +
> > +       priv_req->request.buf = kzalloc(CDNS3_DESCMIS_BUF_SIZE,
> > +                                       GFP_ATOMIC);
> > +       if (!priv_req) {
> > +               cdns3_gadget_ep_free_request(&priv_ep->endpoint, request);
> > +               return -ENOMEM;
> > +       }
> > +
> > +       priv_req->request.length = CDNS3_DESCMIS_BUF_SIZE;
> > +       priv_ep->descmis_req = priv_req;
> > +
> > +       __cdns3_gadget_ep_queue(&priv_ep->endpoint,
> > +                               &priv_ep->descmis_req->request,
> > +                               GFP_ATOMIC);
> > +
> > +       return 0;
> > +}
> > +
> > +/**
> > + * cdns3_check_ep_interrupt_proceed - Processes interrupt related to endpoint
> > + * @priv_ep: endpoint object
> > + *
> > + * Returns 0
> > + */
> > +static int cdns3_check_ep_interrupt_proceed(struct cdns3_endpoint *priv_ep)
> > +{
> > +       struct cdns3_device *priv_dev = priv_ep->cdns3_dev;
> > +       u32 ep_sts_reg;
> > +
> > +       cdns3_select_ep(priv_dev, priv_ep->endpoint.address);
> > +
> > +       trace_cdns3_epx_irq(priv_dev, priv_ep);
> > +
> > +       ep_sts_reg = readl(&priv_dev->regs->ep_sts);
> > +       writel(ep_sts_reg, &priv_dev->regs->ep_sts);
> > +
> > +       if ((ep_sts_reg & EP_STS_IOC) || (ep_sts_reg & EP_STS_ISP)) {
> > +               if (priv_ep->flags & EP_QUIRK_EXTRA_BUF_EN) {
> > +                       if (ep_sts_reg & EP_STS_ISP)
> > +                               priv_ep->flags |= EP_QUIRK_END_TRANSFER;
> > +                       else
> > +                               priv_ep->flags &= ~EP_QUIRK_END_TRANSFER;
> > +               }
> > +               cdns3_transfer_completed(priv_dev, priv_ep);
> > +       }
> > +
> > +       /*
> > +        * For isochronous transfer driver completes request on IOC or on
> > +        * TRBERR. IOC appears only when device receive OUT data packet.
> > +        * If host disable stream or lost some packet then the only way to
> > +        * finish all queued transfer is to do it on TRBERR event.
> > +        */
> > +       if ((ep_sts_reg & EP_STS_TRBERR) &&
> > +           priv_ep->type == USB_ENDPOINT_XFER_ISOC)
> > +               cdns3_transfer_completed(priv_dev, priv_ep);
> > +
> > +       /*
> > +        * WA1: this condition should only be meet when
> > +        * priv_ep->flags & EP_QUIRK_EXTRA_BUF_DET or
> > +        * priv_ep->flags & EP_QUIRK_EXTRA_BUF_EN.
> > +        * In other cases this interrupt will be disabled/
> > +        */
> > +       if (ep_sts_reg & EP_STS_DESCMIS) {
> > +               int err;
> > +
> > +               err = cdns3_descmissing_packet(priv_ep);
> > +               if (err)
> > +                       dev_err(priv_dev->dev,
> > +                               "Failed: No sufficient memory for DESCMIS\n");
> > +       }
> > +
> > +       return 0;
> > +}
> > +
> > +/**
> > + * cdns3_check_usb_interrupt_proceed - Processes interrupt related to device
> > + * @priv_dev: extended gadget object
> > + * @usb_ists: bitmap representation of device's reported interrupts
> > + * (usb_ists register value)
> > + */
> > +static void cdns3_check_usb_interrupt_proceed(struct cdns3_device *priv_dev,
> > +                                             u32 usb_ists)
> > +{
> > +       int speed = 0;
> > +
> > +       trace_cdns3_usb_irq(priv_dev, usb_ists);
> > +       /* Connection detected */
> > +       if (usb_ists & (USB_ISTS_CON2I | USB_ISTS_CONI)) {
> > +               speed = cdns3_get_speed(priv_dev);
> > +               priv_dev->gadget.speed = speed;
> > +               usb_gadget_set_state(&priv_dev->gadget, USB_STATE_POWERED);
> > +               cdns3_ep0_config(priv_dev);
> > +       }
> > +
> > +       /* Disconnection detected */
> > +       if (usb_ists & (USB_ISTS_DIS2I | USB_ISTS_DISI)) {
> > +               if (priv_dev->gadget_driver &&
> > +                   priv_dev->gadget_driver->disconnect) {
> > +                       spin_unlock(&priv_dev->lock);
> > +                       priv_dev->gadget_driver->disconnect(&priv_dev->gadget);
> > +                       spin_lock(&priv_dev->lock);
> > +               }
> > +
> > +               priv_dev->gadget.speed = USB_SPEED_UNKNOWN;
> > +               usb_gadget_set_state(&priv_dev->gadget, USB_STATE_NOTATTACHED);
> > +               cdns3_hw_reset_eps_config(priv_dev);
> > +       }
> > +
> > +       /* reset*/
> > +       if (usb_ists & (USB_ISTS_UWRESI | USB_ISTS_UHRESI | USB_ISTS_U2RESI)) {
> > +               /*read again to check the actuall speed*/
> > +               speed = cdns3_get_speed(priv_dev);
> > +               usb_gadget_set_state(&priv_dev->gadget, USB_STATE_DEFAULT);
> > +               priv_dev->gadget.speed = speed;
> > +               cdns3_hw_reset_eps_config(priv_dev);
> > +               cdns3_ep0_config(priv_dev);
> > +       }
> > +}
> > +
> > +/**
> > + * cdns3_device_irq_handler- interrupt handler for device part of controller
> > + *
> > + * @irq: irq number for cdns3 core device
> > + * @data: structure of cdns3
> > + *
> > + * Returns IRQ_HANDLED or IRQ_NONE
> > + */
> > +static irqreturn_t cdns3_device_irq_handler(int irq, void *data)
> > +{
> > +       struct cdns3_device *priv_dev;
> > +       struct cdns3 *cdns = data;
> > +       irqreturn_t ret = IRQ_NONE;
> > +       unsigned long flags;
> > +       u32 reg;
> > +
> > +       priv_dev = cdns->gadget_dev;
> > +       spin_lock_irqsave(&priv_dev->lock, flags);
> > +
> > +       /* check USB device interrupt */
> > +       reg = readl(&priv_dev->regs->usb_ists);
> > +       writel(reg, &priv_dev->regs->usb_ists);
> > +
> > +       if (reg) {
> > +               cdns3_check_usb_interrupt_proceed(priv_dev, reg);
> > +               ret = IRQ_HANDLED;
> > +       }
> > +
> > +       /* check endpoint interrupt */
> > +       reg = readl(&priv_dev->regs->ep_ists);
> > +
> > +       if (reg) {
> > +               reg = ~reg & readl(&priv_dev->regs->ep_ien);
> > +               /* mask deferred interrupt. */
> > +               writel(reg, &priv_dev->regs->ep_ien);
> > +               ret = IRQ_WAKE_THREAD;
> > +       }
> > +
> > +       spin_unlock_irqrestore(&priv_dev->lock, flags);
> > +       return ret;
> > +}
> > +
> > +/**
> > + * cdns3_device_thread_irq_handler- interrupt handler for device part
> > + * of controller
> > + *
> > + * @irq: irq number for cdns3 core device
> > + * @data: structure of cdns3
> > + *
> > + * Returns IRQ_HANDLED or IRQ_NONE
> > + */
> > +static irqreturn_t cdns3_device_thread_irq_handler(int irq, void *data)
> > +{
> > +       struct cdns3_device *priv_dev;
> > +       struct cdns3 *cdns = data;
> > +       irqreturn_t ret = IRQ_NONE;
> > +       unsigned long flags;
> > +       u32 ep_ien;
> > +       int bit;
> > +       u32 reg;
> > +
> > +       priv_dev = cdns->gadget_dev;
> > +       spin_lock_irqsave(&priv_dev->lock, flags);
> > +
> > +       reg = readl(&priv_dev->regs->ep_ists);
> > +       ep_ien = reg;
> > +
> > +       /* handle default endpoint OUT */
> > +       if (reg & EP_ISTS_EP_OUT0) {
> > +               cdns3_check_ep0_interrupt_proceed(priv_dev, USB_DIR_OUT);
> > +               ret = IRQ_HANDLED;
> > +       }
> > +
> > +       /* handle default endpoint IN */
> > +       if (reg & EP_ISTS_EP_IN0) {
> > +               cdns3_check_ep0_interrupt_proceed(priv_dev, USB_DIR_IN);
> > +               ret = IRQ_HANDLED;
> > +       }
> > +
> > +       /* check if interrupt from non default endpoint, if no exit */
> > +       reg &= ~(EP_ISTS_EP_OUT0 | EP_ISTS_EP_IN0);
> > +       if (!reg)
> > +               goto irqend;
> > +
> > +       for_each_set_bit(bit, (unsigned long *)&reg,
> > +                        sizeof(u32) * BITS_PER_BYTE) {
> > +               cdns3_check_ep_interrupt_proceed(priv_dev->eps[bit]);
> > +               ret = IRQ_HANDLED;
> > +       }
> > +
> > +irqend:
> > +       ep_ien |= readl(&priv_dev->regs->ep_ien);
> > +       /* Unmask all handled EP interrupts */
> > +       writel(ep_ien, &priv_dev->regs->ep_ien);
> > +       spin_unlock_irqrestore(&priv_dev->lock, flags);
> > +       return ret;
> > +}
> > +
> > +/**
> > + * cdns3_ep_onchip_buffer_reserve - Try to reserve onchip buf for EP
> > + *
> > + * The real reservation will occur during write to EP_CFG register,
> > + * this function is used to check if the 'size' reservation is allowed.
> > + *
> > + * @priv_dev: extended gadget object
> > + * @size: the size (KB) for EP would like to allocate
> > + *
> > + * Return 0 if the required size can met or negative value on failure
> > + */
> > +static int cdns3_ep_onchip_buffer_reserve(struct cdns3_device *priv_dev,
> > +                                         int size)
> > +{
> > +       u32 onchip_mem;
> > +
> > +       priv_dev->onchip_mem_allocated_size += size;
> > +
> > +       onchip_mem = USB_CAP2_ACTUAL_MEM_SIZE(readl(&priv_dev->regs->usb_cap2));
> > +       if (!onchip_mem)
> > +               onchip_mem = 256;
> > +
> > +       /* 2KB is reserved for EP0*/
> > +       onchip_mem -= 2;
> > +       if (priv_dev->onchip_mem_allocated_size > onchip_mem) {
> > +               priv_dev->onchip_mem_allocated_size -= size;
> > +               return -EPERM;
> > +       }
> > +
> > +       return 0;
> > +}
> > +
> > +/**
> > + * cdns3_ep_config Configure hardware endpoint
> > + * @priv_ep: extended endpoint object
> > + */
> > +void cdns3_ep_config(struct cdns3_endpoint *priv_ep)
> > +{
> > +       bool is_iso_ep = (priv_ep->type == USB_ENDPOINT_XFER_ISOC);
> > +       struct cdns3_device *priv_dev = priv_ep->cdns3_dev;
> > +       u32 bEndpointAddress = priv_ep->num | priv_ep->dir;
> > +       u32 max_packet_size = 0;
> > +       u32 ep_cfg = 0;
> > +       int ret;
> > +
> > +       switch (priv_ep->type) {
> > +       case USB_ENDPOINT_XFER_INT:
> > +               ep_cfg = EP_CFG_EPTYPE(USB_ENDPOINT_XFER_INT);
> > +               break;
> > +       case USB_ENDPOINT_XFER_BULK:
> > +               ep_cfg = EP_CFG_EPTYPE(USB_ENDPOINT_XFER_BULK);
> > +               break;
> > +       default:
> > +               ep_cfg = EP_CFG_EPTYPE(USB_ENDPOINT_XFER_ISOC);
> > +       }
> > +
> > +       switch (priv_dev->gadget.speed) {
> > +       case USB_SPEED_FULL:
> > +               max_packet_size = is_iso_ep ? 1023 : 64;
> > +               break;
> > +       case USB_SPEED_HIGH:
> > +               max_packet_size = is_iso_ep ? 1024 : 512;
> > +               break;
> > +       case USB_SPEED_SUPER:
> > +               max_packet_size = 1024;
> > +               break;
> > +       default:
> > +               /* all other speed are not supported */
> > +               return;
> > +       }
> > +
> > +       ret = cdns3_ep_onchip_buffer_reserve(priv_dev, CDNS3_EP_BUF_SIZE);
> > +       if (ret) {
> > +               dev_err(priv_dev->dev, "onchip mem is full, ep is invalid\n");
> > +               return;
> > +       }
> > +
> > +       ep_cfg |= EP_CFG_MAXPKTSIZE(max_packet_size) |
> > +                 EP_CFG_BUFFERING(CDNS3_EP_BUF_SIZE - 1) |
> > +                 EP_CFG_MAXBURST(priv_ep->endpoint.maxburst);
> > +
> > +       cdns3_select_ep(priv_dev, bEndpointAddress);
> > +       writel(ep_cfg, &priv_dev->regs->ep_cfg);
> > +
> > +       dev_dbg(priv_dev->dev, "Configure %s: with val %08x\n",
> > +               priv_ep->name, ep_cfg);
> > +}
> > +
> > +/* Find correct direction for HW endpoint according to description */
> > +static int cdns3_ep_dir_is_correct(struct usb_endpoint_descriptor *desc,
> > +                                  struct cdns3_endpoint *priv_ep)
> > +{
> > +       return (priv_ep->endpoint.caps.dir_in && usb_endpoint_dir_in(desc)) ||
> > +              (priv_ep->endpoint.caps.dir_out && usb_endpoint_dir_out(desc));
> > +}
> > +
> > +static struct
> > +cdns3_endpoint *cdns3_find_available_ep(struct cdns3_device *priv_dev,
> > +                                       struct usb_endpoint_descriptor *desc)
> > +{
> > +       struct usb_ep *ep;
> > +       struct cdns3_endpoint *priv_ep;
> > +
> > +       list_for_each_entry(ep, &priv_dev->gadget.ep_list, ep_list) {
> > +               unsigned long num;
> > +               int ret;
> > +               /* ep name pattern likes epXin or epXout */
> > +               char c[2] = {ep->name[2], '\0'};
> > +
> > +               ret = kstrtoul(c, 10, &num);
> > +               if (ret)
> > +                       return ERR_PTR(ret);
> > +
> > +               priv_ep = ep_to_cdns3_ep(ep);
> > +               if (cdns3_ep_dir_is_correct(desc, priv_ep)) {
> > +                       if (!(priv_ep->flags & EP_CLAIMED)) {
> > +                               priv_ep->num  = num;
> > +                               return priv_ep;
> > +                       }
> > +               }
> > +       }
> > +
> > +       return ERR_PTR(-ENOENT);
> > +}
> > +
> > +/*
> > + *  Cadence IP has one limitation that all endpoints must be configured
> > + * (Type & MaxPacketSize) before setting configuration through hardware
> > + * register, it means we can't change endpoints configuration after
> > + * set_configuration.
> > + *
> > + * This function set EP_CLAIMED flag which is added when the gadget driver
> > + * uses usb_ep_autoconfig to configure specific endpoint;
> > + * When the udc driver receives set_configurion request,
> > + * it goes through all claimed endpoints, and configure all endpoints
> > + * accordingly.
> > + *
> > + * At usb_ep_ops.enable/disable, we only enable and disable endpoint through
> > + * ep_cfg register which can be changed after set_configuration, and do
> > + * some software operation accordingly.
> > + */
> > +static struct
> > +usb_ep *cdns3_gadget_match_ep(struct usb_gadget *gadget,
> > +                             struct usb_endpoint_descriptor *desc,
> > +                             struct usb_ss_ep_comp_descriptor *comp_desc)
> > +{
> > +       struct cdns3_device *priv_dev = gadget_to_cdns3_device(gadget);
> > +       struct cdns3_endpoint *priv_ep;
> > +       unsigned long flags;
> > +
> > +       priv_ep = cdns3_find_available_ep(priv_dev, desc);
> > +       if (IS_ERR(priv_ep)) {
> > +               dev_err(priv_dev->dev, "no available ep\n");
> > +               return NULL;
> > +       }
> > +
> > +       dev_dbg(priv_dev->dev, "match endpoint: %s\n", priv_ep->name);
> > +
> > +       spin_lock_irqsave(&priv_dev->lock, flags);
> > +       priv_ep->endpoint.desc = desc;
> > +       priv_ep->dir  = usb_endpoint_dir_in(desc) ? USB_DIR_IN : USB_DIR_OUT;
> > +       priv_ep->type = usb_endpoint_type(desc);
> > +       priv_ep->flags |= EP_CLAIMED;
> > +       spin_unlock_irqrestore(&priv_dev->lock, flags);
> > +       return &priv_ep->endpoint;
> > +}
> > +
> > +/**
> > + * cdns3_gadget_ep_alloc_request Allocates request
> > + * @ep: endpoint object associated with request
> > + * @gfp_flags: gfp flags
> > + *
> > + * Returns allocated request address, NULL on allocation error
> > + */
> > +struct usb_request *cdns3_gadget_ep_alloc_request(struct usb_ep *ep,
> > +                                                 gfp_t gfp_flags)
> > +{
> > +       struct cdns3_endpoint *priv_ep = ep_to_cdns3_ep(ep);
> > +       struct cdns3_request *priv_req;
> > +
> > +       priv_req = kzalloc(sizeof(*priv_req), gfp_flags);
> > +       if (!priv_req)
> > +               return NULL;
> > +
> > +       priv_req->priv_ep = priv_ep;
> > +
> > +       trace_cdns3_alloc_request(priv_req);
> > +       return &priv_req->request;
> > +}
> > +
> > +/**
> > + * cdns3_gadget_ep_free_request Free memory occupied by request
> > + * @ep: endpoint object associated with request
> > + * @request: request to free memory
> > + */
> > +void cdns3_gadget_ep_free_request(struct usb_ep *ep,
> > +                                 struct usb_request *request)
> > +{
> > +       struct cdns3_request *priv_req = to_cdns3_request(request);
> > +
> > +       trace_cdns3_free_request(priv_req);
> > +       kfree(priv_req);
> > +}
> > +
> > +/**
> > + * cdns3_gadget_ep_enable Enable endpoint
> > + * @ep: endpoint object
> > + * @desc: endpoint descriptor
> > + *
> > + * Returns 0 on success, error code elsewhere
> > + */
> > +static int cdns3_gadget_ep_enable(struct usb_ep *ep,
> > +                                 const struct usb_endpoint_descriptor *desc)
> > +{
> > +       struct cdns3_endpoint *priv_ep;
> > +       struct cdns3_device *priv_dev;
> > +       u32 reg = EP_STS_EN_TRBERREN;
> > +       u32 bEndpointAddress;
> > +       unsigned long flags;
> > +       int ret;
> > +
> > +       priv_ep = ep_to_cdns3_ep(ep);
> > +       priv_dev = priv_ep->cdns3_dev;
> > +
> > +       if (!ep || !desc || desc->bDescriptorType != USB_DT_ENDPOINT) {
> > +               dev_dbg(priv_dev->dev, "usbss: invalid parameters\n");
> > +               return -EINVAL;
> > +       }
> > +
> > +       if (!desc->wMaxPacketSize) {
> > +               dev_err(priv_dev->dev, "usbss: missing wMaxPacketSize\n");
> > +               return -EINVAL;
> > +       }
> > +
> > +       if (dev_WARN_ONCE(priv_dev->dev, priv_ep->flags & EP_ENABLED,
> > +                         "%s is already enabled\n", priv_ep->name))
> > +               return 0;
> > +
> > +       spin_lock_irqsave(&priv_dev->lock, flags);
> > +
> > +       priv_ep->endpoint.desc = desc;
> > +       priv_ep->type = usb_endpoint_type(desc);
> > +       priv_ep->interval = desc->bInterval ? BIT(desc->bInterval - 1) : 0;
> > +
> > +       if (priv_ep->interval > ISO_MAX_INTERVAL &&
> > +           priv_ep->type == USB_ENDPOINT_XFER_ISOC) {
> > +               dev_err(priv_dev->dev, "Driver is limited to %d period\n",
> > +                       ISO_MAX_INTERVAL);
> > +
> > +               ret =  -EINVAL;
> > +               goto exit;
> > +       }
> > +
> > +       ret = cdns3_allocate_trb_pool(priv_ep);
> > +
> > +       if (ret)
> > +               goto exit;
> > +
> > +       bEndpointAddress = priv_ep->num | priv_ep->dir;
> > +       cdns3_select_ep(priv_dev, bEndpointAddress);
> > +
> > +       trace_cdns3_gadget_ep_enable(priv_ep);
> > +
> > +       writel(EP_CMD_EPRST, &priv_dev->regs->ep_cmd);
> > +
> > +       ret = cdns3_handshake(&priv_dev->regs->ep_cmd,
> > +                             EP_CMD_CSTALL | EP_CMD_EPRST, 0, 100);
> > +
> > +       /* enable interrupt for selected endpoint */
> > +       cdns3_set_register_bit(&priv_dev->regs->ep_ien,
> > +                              BIT(cdns3_ep_addr_to_index(bEndpointAddress)));
> > +       /*
> > +        * WA1: Set flag for all not ISOC OUT endpoints. If this flag is set
> > +        * driver try to detect whether endpoint need additional internal
> > +        * buffer for unblocking on-chip FIFO buffer. This flag will be cleared
> > +        * if before first DESCMISS interrupt the DMA will be armed.
> > +        */
> > +       if (!priv_ep->dir && priv_ep->type != USB_ENDPOINT_XFER_ISOC) {
> > +               priv_ep->flags |= EP_QUIRK_EXTRA_BUF_DET;
> > +               reg |= EP_STS_EN_DESCMISEN;
> > +       }
> > +
> > +       writel(reg, &priv_dev->regs->ep_sts_en);
> > +
> > +       cdns3_set_register_bit(&priv_dev->regs->ep_cfg, EP_CFG_ENABLE);
> > +
> > +       ep->desc = desc;
> > +       priv_ep->flags &= ~(EP_PENDING_REQUEST | EP_STALL |
> > +                           EP_QUIRK_EXTRA_BUF_EN);
> > +       priv_ep->flags |= EP_ENABLED | EP_UPDATE_EP_TRBADDR;
> > +       priv_ep->enqueue = 0;
> > +       priv_ep->dequeue = 0;
> > +       reg = readl(&priv_dev->regs->ep_sts);
> > +       priv_ep->pcs = !!EP_STS_CCS(reg);
> > +       priv_ep->ccs = !!EP_STS_CCS(reg);
> > +       /* one TRB is reserved for link TRB used in DMULT mode*/
> > +       priv_ep->free_trbs = priv_ep->num_trbs - 1;
> > +exit:
> > +       spin_unlock_irqrestore(&priv_dev->lock, flags);
> > +
> > +       return ret;
> > +}
> > +
> > +/**
> > + * cdns3_gadget_ep_disable Disable endpoint
> > + * @ep: endpoint object
> > + *
> > + * Returns 0 on success, error code elsewhere
> > + */
> > +static int cdns3_gadget_ep_disable(struct usb_ep *ep)
> > +{
> > +       struct cdns3_endpoint *priv_ep;
> > +       struct cdns3_request *priv_req;
> > +       struct cdns3_device *priv_dev;
> > +       struct usb_request *request;
> > +       unsigned long flags;
> > +       int ret = 0;
> > +       u32 ep_cfg;
> > +
> > +       if (!ep) {
> > +               pr_err("usbss: invalid parameters\n");
> > +               return -EINVAL;
> > +       }
> > +
> > +       priv_ep = ep_to_cdns3_ep(ep);
> > +       priv_dev = priv_ep->cdns3_dev;
> > +
> > +       if (dev_WARN_ONCE(priv_dev->dev, !(priv_ep->flags & EP_ENABLED),
> > +                         "%s is already disabled\n", priv_ep->name))
> > +               return 0;
> > +
> > +       spin_lock_irqsave(&priv_dev->lock, flags);
> > +
> > +       trace_cdns3_gadget_ep_disable(priv_ep);
> > +
> > +       cdns3_select_ep(priv_dev, ep->desc->bEndpointAddress);
> > +       ret = cdns3_data_flush(priv_ep);
> > +
> > +       ep_cfg = readl(&priv_dev->regs->ep_cfg);
> > +       ep_cfg &= ~EP_CFG_ENABLE;
> > +       writel(ep_cfg, &priv_dev->regs->ep_cfg);
> > +
> > +       while (!list_empty(&priv_ep->pending_req_list)) {
> > +               request = cdns3_next_request(&priv_ep->pending_req_list);
> > +
> > +               cdns3_gadget_giveback(priv_ep, to_cdns3_request(request),
> > +                                     -ESHUTDOWN);
> > +       }
> > +
> > +       while (!list_empty(&priv_ep->descmiss_req_list)) {
> > +               priv_req = cdns3_next_priv_request(&priv_ep->descmiss_req_list);
> > +
> > +               kfree(priv_req->request.buf);
> > +               cdns3_gadget_ep_free_request(&priv_ep->endpoint,
> > +                                            &priv_req->request);
> > +       }
> > +
> > +       while (!list_empty(&priv_ep->deferred_req_list)) {
> > +               request = cdns3_next_request(&priv_ep->deferred_req_list);
> > +
> > +               cdns3_gadget_giveback(priv_ep, to_cdns3_request(request),
> > +                                     -ESHUTDOWN);
> > +       }
> > +
> > +       priv_ep->descmis_req = NULL;
> > +
> > +       ep->desc = NULL;
> > +       priv_ep->flags &= ~EP_ENABLED;
> > +
> > +       spin_unlock_irqrestore(&priv_dev->lock, flags);
> > +
> > +       return ret;
> > +}
> > +
> > +/**
> > + * cdns3_gadget_ep_queue Transfer data on endpoint
> > + * @ep: endpoint object
> > + * @request: request object
> > + * @gfp_flags: gfp flags
> > + *
> > + * Returns 0 on success, error code elsewhere
> > + */
> > +static int __cdns3_gadget_ep_queue(struct usb_ep *ep,
> > +                                  struct usb_request *request,
> > +                                  gfp_t gfp_flags)
> > +{
> > +       struct cdns3_endpoint *priv_ep = ep_to_cdns3_ep(ep);
> > +       struct cdns3_device *priv_dev = priv_ep->cdns3_dev;
> > +       struct cdns3_request *priv_req;
> > +       int deferred = 0;
> > +       int ret = 0;
> > +
> > +       request->actual = 0;
> > +       request->status = -EINPROGRESS;
> > +       priv_req = to_cdns3_request(request);
> > +       trace_cdns3_ep_queue(priv_req);
> > +
> > +       /*
> > +        * WA1: if transfer was queued before DESCMISS appear than we
> > +        * can disable handling of DESCMISS interrupt. Driver assumes that it
> > +        * can disable special treatment for this endpoint.
> > +        */
> > +       if (priv_ep->flags & EP_QUIRK_EXTRA_BUF_DET) {
> > +               u32 reg;
> > +
> > +               cdns3_select_ep(priv_dev, priv_ep->num | priv_ep->dir);
> > +               reg = readl(&priv_dev->regs->ep_sts_en);
> > +               priv_ep->flags &= ~EP_QUIRK_EXTRA_BUF_DET;
> > +               reg &= EP_STS_EN_DESCMISEN;
> > +               writel(reg, &priv_dev->regs->ep_sts_en);
> > +       }
> > +
> > +       /* WA1 */
> > +       if (priv_ep->flags & EP_QUIRK_EXTRA_BUF_EN) {
> > +               u8 pending_empty = list_empty(&priv_ep->pending_req_list);
> > +               u8 descmiss_empty = list_empty(&priv_ep->descmiss_req_list);
> > +
> > +               /*
> > +                *  DESCMISS transfer has been finished, so data will be
> > +                *  directly copied from internal allocated usb_request
> > +                *  objects.
> > +                */
> > +               if (pending_empty && !descmiss_empty &&
> > +                   !(priv_req->flags & REQUEST_INTERNAL)) {
> > +                       cdns3_descmiss_copy_data(priv_ep, request);
> > +                       list_add_tail(&request->list,
> > +                                     &priv_ep->pending_req_list);
> > +                       cdns3_gadget_giveback(priv_ep, priv_req,
> > +                                             request->status);
> > +                       return ret;
> > +               }
> > +
> > +               /*
> > +                * WA1 driver will wait for completion DESCMISS transfer,
> > +                * before starts new, not DESCMISS transfer.
> > +                */
> > +               if (!pending_empty && !descmiss_empty)
> > +                       deferred = 1;
> > +
> > +               if (priv_req->flags & REQUEST_INTERNAL)
> > +                       list_add_tail(&priv_req->list,
> > +                                     &priv_ep->descmiss_req_list);
> > +       }
> > +
> > +       ret = usb_gadget_map_request_by_dev(priv_dev->sysdev, request,
> > +                                           usb_endpoint_dir_in(ep->desc));
> > +       if (ret)
> > +               return ret;
> > +
> > +       /*
> > +        * If hardware endpoint configuration has not been set yet then
> > +        * just queue request in deferred list. Transfer will be started in
> > +        * cdns3_set_hw_configuration.
> > +        */
> > +       if (!priv_dev->hw_configured_flag)
> > +               deferred = 1;
> > +       else
> > +               ret = cdns3_ep_run_transfer(priv_ep, request);
> > +
> > +       if (ret || deferred)
> > +               list_add_tail(&request->list, &priv_ep->deferred_req_list);
> > +       else
> > +               list_add_tail(&request->list, &priv_ep->pending_req_list);
> > +
> > +       return ret;
> > +}
> > +
> > +static int cdns3_gadget_ep_queue(struct usb_ep *ep, struct usb_request *request,
> > +                                gfp_t gfp_flags)
> > +{
> > +       struct usb_request *zlp_request;
> > +       struct cdns3_endpoint *priv_ep;
> > +       struct cdns3_device *priv_dev;
> > +       unsigned long flags;
> > +       int ret;
> > +
> > +       if (!request || !ep)
> > +               return -EINVAL;
> > +
> > +       priv_ep = ep_to_cdns3_ep(ep);
> > +       priv_dev = priv_ep->cdns3_dev;
> > +
> > +       spin_lock_irqsave(&priv_dev->lock, flags);
> > +
> > +       ret = __cdns3_gadget_ep_queue(ep, request, gfp_flags);
> > +
> > +       if (ret == 0 && request->zero && request->length &&
> > +           (request->length % ep->maxpacket == 0)) {
> > +               struct cdns3_request *priv_req;
> > +
> > +               zlp_request = cdns3_gadget_ep_alloc_request(ep, GFP_ATOMIC);
> > +               zlp_request->buf = priv_dev->zlp_buf;
> > +               zlp_request->length = 0;
> > +
> > +               priv_req = to_cdns3_request(zlp_request);
> > +               priv_req->flags |= REQUEST_ZLP;
> > +
> > +               dev_dbg(priv_dev->dev, "Queuing ZLP for endpoint: %s\n",
> > +                       priv_ep->name);
> > +               ret = __cdns3_gadget_ep_queue(ep, zlp_request, gfp_flags);
> > +       }
> > +
> > +       spin_unlock_irqrestore(&priv_dev->lock, flags);
> > +       return ret;
> > +}
> > +
> > +/**
> > + * cdns3_gadget_ep_dequeue Remove request from transfer queue
> > + * @ep: endpoint object associated with request
> > + * @request: request object
> > + *
> > + * Returns 0 on success, error code elsewhere
> > + */
> > +int cdns3_gadget_ep_dequeue(struct usb_ep *ep,
> > +                           struct usb_request *request)
> > +{
> > +       struct cdns3_endpoint *priv_ep = ep_to_cdns3_ep(ep);
> > +       struct cdns3_device *priv_dev = priv_ep->cdns3_dev;
> > +       struct usb_request *req, *req_temp;
> > +       struct cdns3_request *priv_req;
> > +       unsigned long flags;
> > +       int ret = 0;
> > +
> > +       if (!ep || !request || !ep->desc)
> > +               return -EINVAL;
> > +
> > +       spin_lock_irqsave(&priv_dev->lock, flags);
> > +
> > +       priv_req = to_cdns3_request(request);
> > +
> > +       trace_cdns3_ep_dequeue(priv_req);
> > +
> > +       cdns3_select_ep(priv_dev, ep->desc->bEndpointAddress);
> > +
> > +       list_for_each_entry_safe(req, req_temp, &priv_ep->pending_req_list,
> > +                                list) {
> > +               if (request == req)
> > +                       goto found;
> > +       }
> > +
> > +       list_for_each_entry_safe(req, req_temp, &priv_ep->deferred_req_list,
> > +                                list) {
> > +               if (request == req)
> > +                       goto found;
> > +       }
> > +
> > +found:
> > +       if (request == req)
> > +               cdns3_gadget_giveback(priv_ep, priv_req, -ECONNRESET);
> > +
> > +       spin_unlock_irqrestore(&priv_dev->lock, flags);
> > +       return ret;
> > +}
> > +
> > +/**
> > + * cdns3_gadget_ep_set_halt Sets/clears stall on selected endpoint
> > + * @ep: endpoint object to set/clear stall on
> > + * @value: 1 for set stall, 0 for clear stall
> > + *
> > + * Returns 0 on success, error code elsewhere
> > + */
> > +int cdns3_gadget_ep_set_halt(struct usb_ep *ep, int value)
> > +{
> > +       struct cdns3_endpoint *priv_ep = ep_to_cdns3_ep(ep);
> > +       struct cdns3_device *priv_dev = priv_ep->cdns3_dev;
> > +       unsigned long flags;
> > +       int ret = 0;
> > +
> > +       if (!(priv_ep->flags & EP_ENABLED))
> > +               return -EPERM;
> > +
> > +       spin_lock_irqsave(&priv_dev->lock, flags);
> > +
> > +       /* if actual transfer is pending defer setting stall on this endpoint */
> > +       if ((priv_ep->flags & EP_PENDING_REQUEST) && value) {
> > +               priv_ep->flags |= EP_STALL;
> > +               goto finish;
> > +       }
> > +
> > +       dev_dbg(priv_dev->dev, "Halt endpoint %s\n", priv_ep->name);
> > +
> > +       cdns3_select_ep(priv_dev, ep->desc->bEndpointAddress);
> > +       if (value) {
> > +               cdns3_ep_stall_flush(priv_ep);
> > +       } else {
> > +               priv_ep->flags &= ~EP_WEDGE;
> > +               writel(EP_CMD_CSTALL | EP_CMD_EPRST, &priv_dev->regs->ep_cmd);
> > +
> > +               /* wait for EPRST cleared */
> > +               ret = cdns3_handshake(&priv_dev->regs->ep_cmd,
> > +                                     EP_CMD_EPRST, 0, 100);
> > +               if (unlikely(ret)) {
> > +                       dev_err(priv_dev->dev,
> > +                               "Clearing halt condition failed for %s\n",
> > +                               priv_ep->name);
> > +                       goto finish;
> > +
> > +               } else {
> > +                       priv_ep->flags &= ~EP_STALL;
> > +               }
> > +       }
> > +
> > +       priv_ep->flags &= ~EP_PENDING_REQUEST;
> > +finish:
> > +       spin_unlock_irqrestore(&priv_dev->lock, flags);
> > +
> > +       return ret;
> > +}
> > +
> > +extern const struct usb_ep_ops cdns3_gadget_ep0_ops;
> > +
> > +static const struct usb_ep_ops cdns3_gadget_ep_ops = {
> > +       .enable = cdns3_gadget_ep_enable,
> > +       .disable = cdns3_gadget_ep_disable,
> > +       .alloc_request = cdns3_gadget_ep_alloc_request,
> > +       .free_request = cdns3_gadget_ep_free_request,
> > +       .queue = cdns3_gadget_ep_queue,
> > +       .dequeue = cdns3_gadget_ep_dequeue,
> > +       .set_halt = cdns3_gadget_ep_set_halt,
> > +       .set_wedge = cdns3_gadget_ep_set_wedge,
> > +};
> > +
> > +/**
> > + * cdns3_gadget_get_frame Returns number of actual ITP frame
> > + * @gadget: gadget object
> > + *
> > + * Returns number of actual ITP frame
> > + */
> > +static int cdns3_gadget_get_frame(struct usb_gadget *gadget)
> > +{
> > +       struct cdns3_device *priv_dev = gadget_to_cdns3_device(gadget);
> > +
> > +       return readl(&priv_dev->regs->usb_iptn);
> > +}
> > +
> > +static int cdns3_gadget_wakeup(struct usb_gadget *gadget)
> > +{
> > +       return 0;
> > +}
> > +
> > +static int cdns3_gadget_set_selfpowered(struct usb_gadget *gadget,
> > +                                       int is_selfpowered)
> > +{
> > +       struct cdns3_device *priv_dev = gadget_to_cdns3_device(gadget);
> > +       unsigned long flags;
> > +
> > +       spin_lock_irqsave(&priv_dev->lock, flags);
> > +       priv_dev->is_selfpowered = !!is_selfpowered;
> > +       spin_unlock_irqrestore(&priv_dev->lock, flags);
> > +       return 0;
> > +}
> > +
> > +static int cdns3_gadget_pullup(struct usb_gadget *gadget, int is_on)
> > +{
> > +       struct cdns3_device *priv_dev = gadget_to_cdns3_device(gadget);
> > +
> > +       if (is_on)
> > +               writel(USB_CONF_DEVEN, &priv_dev->regs->usb_conf);
> > +       else
> > +               writel(USB_CONF_DEVDS, &priv_dev->regs->usb_conf);
> > +
> > +       return 0;
> > +}
> > +
> > +static void cdns3_gadget_config(struct cdns3_device *priv_dev)
> > +{
> > +       struct cdns3_usb_regs __iomem *regs = priv_dev->regs;
> > +
> > +       cdns3_ep0_config(priv_dev);
> > +
> > +       /* enable interrupts for endpoint 0 (in and out) */
> > +       writel(EP_IEN_EP_OUT0 | EP_IEN_EP_IN0, &regs->ep_ien);
> > +
> > +       /* enable generic interrupt*/
> > +       writel(USB_IEN_INIT, &regs->usb_ien);
> > +       writel(USB_CONF_CLK2OFFDS | USB_CONF_L1DS, &regs->usb_conf);
> > +       writel(USB_CONF_DMULT, &regs->usb_conf);
> > +       cdns3_gadget_pullup(&priv_dev->gadget, 1);
> > +}
> > +
> > +/**
> > + * cdns3_gadget_udc_start Gadget start
> > + * @gadget: gadget object
> > + * @driver: driver which operates on this gadget
> > + *
> > + * Returns 0 on success, error code elsewhere
> > + */
> > +static int cdns3_gadget_udc_start(struct usb_gadget *gadget,
> > +                                 struct usb_gadget_driver *driver)
> > +{
> > +       struct cdns3_device *priv_dev = gadget_to_cdns3_device(gadget);
> > +       unsigned long flags;
> > +
> > +       spin_lock_irqsave(&priv_dev->lock, flags);
> > +       priv_dev->gadget_driver = driver;
> > +       cdns3_gadget_config(priv_dev);
> > +       spin_unlock_irqrestore(&priv_dev->lock, flags);
> > +       return 0;
> > +}
> > +
> > +/**
> > + * cdns3_gadget_udc_stop Stops gadget
> > + * @gadget: gadget object
> > + *
> > + * Returns 0
> > + */
> > +static int cdns3_gadget_udc_stop(struct usb_gadget *gadget)
> > +{
> > +       struct cdns3_device *priv_dev = gadget_to_cdns3_device(gadget);
> > +       struct cdns3_endpoint *priv_ep;
> > +       u32 bEndpointAddress;
> > +       struct usb_ep *ep;
> > +       int ret = 0;
> > +
> > +       priv_dev->gadget_driver = NULL;
> > +
> > +       priv_dev->onchip_mem_allocated_size = 0;
> > +       priv_dev->gadget.speed = USB_SPEED_UNKNOWN;
> > +
> > +       list_for_each_entry(ep, &priv_dev->gadget.ep_list, ep_list) {
> > +               priv_ep = ep_to_cdns3_ep(ep);
> > +               bEndpointAddress = priv_ep->num | priv_ep->dir;
> > +               cdns3_select_ep(priv_dev, bEndpointAddress);
> > +               writel(EP_CMD_EPRST, &priv_dev->regs->ep_cmd);
> > +               ret = cdns3_handshake(&priv_dev->regs->ep_cmd,
> > +                                     EP_CMD_EPRST, 0, 100);
> > +               cdns3_free_trb_pool(priv_ep);
> > +       }
> > +
> > +       /* disable interrupt for device */
> > +       writel(0, &priv_dev->regs->usb_ien);
> > +       writel(USB_CONF_DEVDS, &priv_dev->regs->usb_conf);
> > +
> > +       return ret;
> > +}
> > +
> > +static const struct usb_gadget_ops cdns3_gadget_ops = {
> > +       .get_frame = cdns3_gadget_get_frame,
> > +       .wakeup = cdns3_gadget_wakeup,
> > +       .set_selfpowered = cdns3_gadget_set_selfpowered,
> > +       .pullup = cdns3_gadget_pullup,
> > +       .udc_start = cdns3_gadget_udc_start,
> > +       .udc_stop = cdns3_gadget_udc_stop,
> > +       .match_ep = cdns3_gadget_match_ep,
> > +};
> > +
> > +static void cdns3_free_all_eps(struct cdns3_device *priv_dev)
> > +{
> > +       int i;
> > +
> > +       /*ep0 OUT point to ep0 IN*/
> > +       priv_dev->eps[16] = NULL;
> > +
> > +       cdns3_free_trb_pool(priv_dev->eps[0]);
> > +
> > +       for (i = 0; i < CDNS3_ENDPOINTS_MAX_COUNT; i++)
> > +               if (priv_dev->eps[i])
> > +                       devm_kfree(priv_dev->dev, priv_dev->eps[i]);
> > +}
> > +
> > +/**
> > + * cdns3_init_eps Initializes software endpoints of gadget
> > + * @cdns3: extended gadget object
> > + *
> > + * Returns 0 on success, error code elsewhere
> > + */
> > +static int cdns3_init_eps(struct cdns3_device *priv_dev)
> > +{
> > +       u32 ep_enabled_reg, iso_ep_reg;
> > +       struct cdns3_endpoint *priv_ep;
> > +       int ep_dir, ep_number;
> > +       u32 ep_mask;
> > +       int ret = 0;
> > +       int i;
> > +
> > +       /* Read it from USB_CAP3 to USB_CAP5 */
> > +       ep_enabled_reg = readl(&priv_dev->regs->usb_cap3);
> > +       iso_ep_reg = readl(&priv_dev->regs->usb_cap4);
> > +
> > +       dev_dbg(priv_dev->dev, "Initializing non-zero endpoints\n");
> > +
> > +       for (i = 0; i < CDNS3_ENDPOINTS_MAX_COUNT; i++) {
> > +               ep_dir = i >> 4;        /* i div 16 */
> > +               ep_number = i & 0xF;    /* i % 16 */
> > +               ep_mask = BIT(i);
> > +
> > +               if (!(ep_enabled_reg & ep_mask))
> > +                       continue;
> > +
> > +               if (ep_dir && !ep_number) {
> > +                       priv_dev->eps[i] = priv_dev->eps[0];
> > +                       continue;
> > +               }
> > +
> > +               priv_ep = devm_kzalloc(priv_dev->dev, sizeof(*priv_ep),
> > +                                      GFP_KERNEL);
> > +               if (!priv_ep) {
> > +                       ret = -ENOMEM;
> > +                       goto err;
> > +               }
> > +
> > +               /* set parent of endpoint object */
> > +               priv_ep->cdns3_dev = priv_dev;
> > +               priv_dev->eps[i] = priv_ep;
> > +               priv_ep->num = ep_number;
> > +               priv_ep->dir = ep_dir ? USB_DIR_IN : USB_DIR_OUT;
> > +
> > +               if (!ep_number) {
> > +                       ret = cdns3_init_ep0(priv_dev, priv_ep);
> > +                       if (ret) {
> > +                               dev_err(priv_dev->dev, "Failed to init ep0\n");
> > +                               goto err;
> > +                       }
> > +               } else {
> > +                       snprintf(priv_ep->name, sizeof(priv_ep->name), "ep%d%s",
> > +                                ep_number, !!ep_dir ? "in" : "out");
> > +                       priv_ep->endpoint.name = priv_ep->name;
> > +
> > +                       usb_ep_set_maxpacket_limit(&priv_ep->endpoint,
> > +                                                  CDNS3_EP_MAX_PACKET_LIMIT);
> > +                       priv_ep->endpoint.max_streams = CDNS3_EP_MAX_STREAMS;
> > +                       priv_ep->endpoint.ops = &cdns3_gadget_ep_ops;
> > +                       if (ep_dir)
> > +                               priv_ep->endpoint.caps.dir_in = 1;
> > +                       else
> > +                               priv_ep->endpoint.caps.dir_out = 1;
> > +
> > +                       if (iso_ep_reg & ep_mask)
> > +                               priv_ep->endpoint.caps.type_iso = 1;
> > +
> > +                       priv_ep->endpoint.caps.type_bulk = 1;
> > +                       priv_ep->endpoint.caps.type_int = 1;
> > +                       priv_ep->endpoint.maxburst = CDNS3_EP_BUF_SIZE - 1;
> > +
> > +                       list_add_tail(&priv_ep->endpoint.ep_list,
> > +                                     &priv_dev->gadget.ep_list);
> > +               }
> > +
> > +               priv_ep->flags = 0;
> > +
> > +               dev_info(priv_dev->dev, "Initialized  %s support: %s %s\n",
> > +                        priv_ep->name,
> > +                        priv_ep->endpoint.caps.type_bulk ? "BULK, INT" : "",
> > +                        priv_ep->endpoint.caps.type_iso ? "ISO" : "");
> > +
> > +               INIT_LIST_HEAD(&priv_ep->pending_req_list);
> > +               INIT_LIST_HEAD(&priv_ep->deferred_req_list);
> > +               INIT_LIST_HEAD(&priv_ep->descmiss_req_list);
> > +       }
> > +
> > +       return 0;
> > +err:
> > +       cdns3_free_all_eps(priv_dev);
> > +       return -ENOMEM;
> > +}
> > +
> > +static void cdns3_gadget_disable(struct cdns3 *cdns)
> > +{
> > +       struct cdns3_device *priv_dev;
> > +
> > +       priv_dev = cdns->gadget_dev;
> > +
> > +       if (priv_dev->gadget_driver)
> > +               priv_dev->gadget_driver->disconnect(&priv_dev->gadget);
> > +
> > +       usb_gadget_disconnect(&priv_dev->gadget);
> > +       priv_dev->gadget.speed = USB_SPEED_UNKNOWN;
> > +}
> > +
> > +void cdns3_gadget_exit(struct cdns3 *cdns)
> > +{
> > +       struct cdns3_device *priv_dev;
> > +
> > +       priv_dev = cdns->gadget_dev;
> > +
> > +       cdns3_gadget_disable(cdns);
> > +
> > +       devm_free_irq(cdns->dev, cdns->irq, cdns);
> > +
> > +       pm_runtime_mark_last_busy(cdns->dev);
> > +       pm_runtime_put_autosuspend(cdns->dev);
> > +
> > +       usb_del_gadget_udc(&priv_dev->gadget);
> > +
> > +       cdns3_free_all_eps(priv_dev);
> > +
> > +       dma_free_coherent(priv_dev->sysdev, 8, priv_dev->setup_buf,
> > +                         priv_dev->setup_dma);
> > +
> > +       kfree(priv_dev->zlp_buf);
> > +       kfree(priv_dev);
> > +       cdns->gadget_dev = NULL;
> > +}
> > +
> > +static int cdns3_gadget_start(struct cdns3 *cdns)
> > +{
> > +       struct cdns3_device *priv_dev;
> > +       u32 max_speed;
> > +       int ret;
> > +
> > +       priv_dev = kzalloc(sizeof(*priv_dev), GFP_KERNEL);
> > +       if (!priv_dev)
> > +               return -ENOMEM;
> > +
> > +       cdns->gadget_dev = priv_dev;
> > +       priv_dev->sysdev = cdns->dev;
> > +       priv_dev->dev = cdns->dev;
> > +       priv_dev->regs = cdns->dev_regs;
> > +
> > +       max_speed = usb_get_maximum_speed(cdns->dev);
> > +
> > +       /* Check the maximum_speed parameter */
> > +       switch (max_speed) {
> > +       case USB_SPEED_FULL:
> > +       case USB_SPEED_HIGH:
> > +       case USB_SPEED_SUPER:
> > +               break;
> > +       default:
> > +               dev_err(cdns->dev, "invalid maximum_speed parameter %d\n",
> > +                       max_speed);
> > +               /* fall through */
> > +       case USB_SPEED_UNKNOWN:
> > +               /* default to superspeed */
> > +               max_speed = USB_SPEED_SUPER;
> > +               break;
> > +       }
> > +
> > +       /* fill gadget fields */
> > +       priv_dev->gadget.max_speed = max_speed;
> > +       priv_dev->gadget.speed = USB_SPEED_UNKNOWN;
> > +       priv_dev->gadget.ops = &cdns3_gadget_ops;
> > +       priv_dev->gadget.name = "usb-ss-gadget";
> > +       priv_dev->gadget.sg_supported = 1;
> > +
> > +       spin_lock_init(&priv_dev->lock);
> > +       INIT_WORK(&priv_dev->pending_status_wq,
> > +                 cdns3_pending_setup_status_handler);
> > +
> > +       /* initialize endpoint container */
> > +       INIT_LIST_HEAD(&priv_dev->gadget.ep_list);
> > +
> > +       ret = cdns3_init_eps(priv_dev);
> > +       if (ret) {
> > +               dev_err(priv_dev->dev, "Failed to create endpoints\n");
> > +               goto err1;
> > +       }
> > +
> > +       /* allocate memory for setup packet buffer */
> > +       priv_dev->setup_buf = dma_alloc_coherent(priv_dev->sysdev, 8,
> > +                                                &priv_dev->setup_dma, GFP_DMA);
> > +       if (!priv_dev->setup_buf) {
> > +               dev_err(priv_dev->dev, "Failed to allocate memory for SETUP buffer\n");
> > +               ret = -ENOMEM;
> > +               goto err2;
> > +       }
> > +
> > +       priv_dev->dev_ver = readl(&priv_dev->regs->usb_cap6);
> > +       dev_dbg(priv_dev->dev, "Device Controller version: %08x\n",
> > +               readl(&priv_dev->regs->usb_cap6));
> > +       dev_dbg(priv_dev->dev, "USB Capabilities:: %08x\n",
> > +               readl(&priv_dev->regs->usb_cap1));
> > +       dev_dbg(priv_dev->dev, "On-Chip memory cnfiguration: %08x\n",
> > +               readl(&priv_dev->regs->usb_cap2));
> > +
> > +       priv_dev->zlp_buf = kzalloc(CDNS3_EP_ZLP_BUF_SIZE, GFP_KERNEL);
> > +       if (!priv_dev->zlp_buf) {
> > +               ret = -ENOMEM;
> > +               goto err3;
> > +       }
> > +
> > +       /* add USB gadget device */
> > +       ret = usb_add_gadget_udc(priv_dev->dev, &priv_dev->gadget);
> > +       if (ret < 0) {
> > +               dev_err(priv_dev->dev,
> > +                       "Failed to register USB device controller\n");
> > +               goto err4;
> > +       }
> > +
> > +       return 0;
> > +err4:
> > +       kfree(priv_dev->zlp_buf);
> > +err3:
> > +       dma_free_coherent(priv_dev->sysdev, 8, priv_dev->setup_buf,
> > +                         priv_dev->setup_dma);
> > +err2:
> > +       cdns3_free_all_eps(priv_dev);
> > +err1:
> > +       cdns->gadget_dev = NULL;
> > +       return ret;
> > +}
> > +
> > +static int __cdns3_gadget_init(struct cdns3 *cdns)
> > +{
> > +       struct cdns3_device *priv_dev;
> > +       unsigned long flags;
> > +       int ret = 0;
> > +
> > +       ret = cdns3_gadget_start(cdns);
> > +       if (ret)
> > +               return ret;
> > +
> > +       priv_dev = cdns->gadget_dev;
> > +       ret = devm_request_threaded_irq(cdns->dev, cdns->irq,
> > +                                       cdns3_device_irq_handler,
> > +                                       cdns3_device_thread_irq_handler,
> > +                                       IRQF_SHARED, dev_name(cdns->dev), cdns);
> > +       if (ret)
> > +               goto err0;
> > +
> > +       pm_runtime_get_sync(cdns->dev);
> > +       spin_lock_irqsave(&priv_dev->lock, flags);
> > +       spin_unlock_irqrestore(&priv_dev->lock, flags);
> > +       return 0;
> > +err0:
> > +       cdns3_gadget_exit(cdns);
> > +       return ret;
> > +}
> > +
> > +static int cdns3_gadget_suspend(struct cdns3 *cdns, bool do_wakeup)
> > +{
> > +       cdns3_gadget_disable(cdns);
> > +       return 0;
> > +}
> > +
> > +static int cdns3_gadget_resume(struct cdns3 *cdns, bool hibernated)
> > +{
> > +       struct cdns3_device *priv_dev;
> > +       unsigned long flags;
> > +
> > +       priv_dev = cdns->gadget_dev;
> > +       spin_lock_irqsave(&priv_dev->lock, flags);
> > +
> > +       if (!priv_dev->gadget_driver) {
> > +               spin_unlock_irqrestore(&priv_dev->lock, flags);
> > +               return 0;
> > +       }
> > +
> > +       cdns3_gadget_config(priv_dev);
> > +       spin_unlock_irqrestore(&priv_dev->lock, flags);
> > +       return 0;
> > +}
> > +
> > +/**
> > + * cdns3_gadget_init - initialize device structure
> > + *
> > + * cdns: cdns3 instance
> > + *
> > + * This function initializes the gadget.
> > + */
> > +int cdns3_gadget_init(struct cdns3 *cdns)
> > +{
> > +       struct cdns3_role_driver *rdrv;
> > +
> > +       rdrv = devm_kzalloc(cdns->dev, sizeof(*rdrv), GFP_KERNEL);
> > +       if (!rdrv)
> > +               return -ENOMEM;
> > +
> > +       rdrv->start     = __cdns3_gadget_init;
> > +       rdrv->stop      = cdns3_gadget_exit;
> > +       rdrv->suspend   = cdns3_gadget_suspend;
> > +       rdrv->resume    = cdns3_gadget_resume;
> > +       rdrv->state     = CDNS3_ROLE_STATE_INACTIVE;
> > +       rdrv->name      = "gadget";
> > +       cdns->roles[CDNS3_ROLE_GADGET] = rdrv;
> > +
> > +       return 0;
> > +}
> > diff --git a/drivers/usb/cdns3/gadget.h b/drivers/usb/cdns3/gadget.h
> > new file mode 100644
> > index 000000000000..41cec7f085ad
> > --- /dev/null
> > +++ b/drivers/usb/cdns3/gadget.h
> > @@ -0,0 +1,1206 @@
> > +/* SPDX-License-Identifier: GPL-2.0 */
> > +/*
> > + * USBSS device controller driver header file
> > + *
> > + * Copyright (C) 2018 Cadence.
> > + * Copyright (C) 2017-2018 NXP
> > + *
> > + * Author: Pawel Laszczak <pawell@cadence.com>
> > + *         Pawel Jez <pjez@cadence.com>
> > + *         Peter Chen <peter.chen@nxp.com>
> > + */
> > +#ifndef __LINUX_CDNS3_GADGET
> > +#define __LINUX_CDNS3_GADGET
> > +#include <linux/usb/gadget.h>
> > +
> > +/*
> > + * USBSS-DEV register interface.
> > + * This corresponds to the USBSS Device Controller Interface
> > + */
> > +
> > +/**
> > + * struct cdns3_usb_regs - device controller registers.
> > + * @usb_conf:      Global Configuration Register.
> > + * @usb_sts:       Global Status Register.
> > + * @usb_cmd:       Global Command Register.
> > + * @usb_iptn:      ITP/SOF number Register.
> > + * @usb_lpm:       Global Command Register.
> > + * @usb_ien:       USB Interrupt Enable Register.
> > + * @usb_ists:      USB Interrupt Status Register.
> > + * @ep_sel:        Endpoint Select Register.
> > + * @ep_traddr:     Endpoint Transfer Ring Address Register.
> > + * @ep_cfg:        Endpoint Configuration Register.
> > + * @ep_cmd:        Endpoint Command Register.
> > + * @ep_sts:        Endpoint Status Register.
> > + * @ep_sts_sid:    Endpoint Status Register.
> > + * @ep_sts_en:     Endpoint Status Register Enable.
> > + * @drbl:          Doorbell Register.
> > + * @ep_ien:        EP Interrupt Enable Register.
> > + * @ep_ists:       EP Interrupt Status Register.
> > + * @usb_pwr:       Global Power Configuration Register.
> > + * @usb_conf2:     Global Configuration Register 2.
> > + * @usb_cap1:      Capability Register 1.
> > + * @usb_cap2:      Capability Register 2.
> > + * @usb_cap3:      Capability Register 3.
> > + * @usb_cap4:      Capability Register 4.
> > + * @usb_cap5:      Capability Register 5.
> > + * @usb_cap6:      Capability Register 6.
> > + * @usb_cpkt1:     Custom Packet Register 1.
> > + * @usb_cpkt2:     Custom Packet Register 2.
> > + * @usb_cpkt3:     Custom Packet Register 3.
> > + * @reserved1:     Reserved.
> > + * @cfg_regs:      Configuration registers.
> > + * @reserved2:     Reserved.
> > + * @dma_axi_ctrl:  AXI Control register.
> > + * @dma_axi_id:    AXI ID register.
> > + * @dma_axi_cap:   AXI Capability register.
> > + * @dma_axi_ctrl0: AXI Control 0 register.
> > + * @dma_axi_ctrl1: AXI Control 1 register.
> > + */
> > +struct cdns3_usb_regs {
> > +       __le32 usb_conf;
> > +       __le32 usb_sts;
> > +       __le32 usb_cmd;
> > +       __le32 usb_iptn;
> > +       __le32 usb_lpm;
> > +       __le32 usb_ien;
> > +       __le32 usb_ists;
> > +       __le32 ep_sel;
> > +       __le32 ep_traddr;
> > +       __le32 ep_cfg;
> > +       __le32 ep_cmd;
> > +       __le32 ep_sts;
> > +       __le32 ep_sts_sid;
> > +       __le32 ep_sts_en;
> > +       __le32 drbl;
> > +       __le32 ep_ien;
> > +       __le32 ep_ists;
> > +       __le32 usb_pwr;
> > +       __le32 usb_conf2;
> > +       __le32 usb_cap1;
> > +       __le32 usb_cap2;
> > +       __le32 usb_cap3;
> > +       __le32 usb_cap4;
> > +       __le32 usb_cap5;
> > +       __le32 usb_cap6;
> > +       __le32 usb_cpkt1;
> > +       __le32 usb_cpkt2;
> > +       __le32 usb_cpkt3;
> > +       __le32 reserved1[36];
> > +       __le32 cfg_reg1;
> > +       __le32 dbg_link1;
> > +       __le32 dbg_link2;
> > +       __le32 cfg_regs[74];
> > +       __le32 reserved2[34];
> > +       __le32 dma_axi_ctrl;
> > +       __le32 dma_axi_id;
> > +       __le32 dma_axi_cap;
> > +       __le32 dma_axi_ctrl0;
> > +       __le32 dma_axi_ctrl1;
> > +};
> > +
> > +/* USB_CONF - bitmasks */
> > +/* Reset USB device configuration. */
> > +#define USB_CONF_CFGRST                BIT(0)
> > +/* Set Configuration. */
> > +#define USB_CONF_CFGSET                BIT(1)
> > +/* Disconnect USB device in SuperSpeed. */
> > +#define USB_CONF_USB3DIS       BIT(3)
> > +/* Disconnect USB device in HS/FS */
> > +#define USB_CONF_USB2DIS       BIT(4)
> > +/* Little Endian access - default */
> > +#define USB_CONF_LENDIAN       BIT(5)
> > +/*
> > + * Big Endian access. Driver assume that byte order for
> > + * SFRs access always is as Little Endian so this bit
> > + * is not used.
> > + */
> > +#define USB_CONF_BENDIAN       BIT(6)
> > +/* Device software reset. */
> > +#define USB_CONF_SWRST         BIT(7)
> > +/* Singular DMA transfer mode. */
> > +#define USB_CONF_DSING         BIT(8)
> > +/* Multiple DMA transfers mode. */
> > +#define USB_CONF_DMULT         BIT(9)
> > +/* DMA clock turn-off enable. */
> > +#define USB_CONF_DMAOFFEN      BIT(10)
> > +/* DMA clock turn-off disable. */
> > +#define USB_CONF_DMAOFFDS      BIT(11)
> > +/* Clear Force Full Speed. */
> > +#define USB_CONF_CFORCE_FS     BIT(12)
> > +/* Set Force Full Speed. */
> > +#define USB_CONF_SFORCE_FS     BIT(13)
> > +/* Device enable. */
> > +#define USB_CONF_DEVEN         BIT(14)
> > +/* Device disable. */
> > +#define USB_CONF_DEVDS         BIT(15)
> > +/* L1 LPM state entry enable (used in HS/FS mode). */
> > +#define USB_CONF_L1EN          BIT(16)
> > +/* L1 LPM state entry disable (used in HS/FS mode). */
> > +#define USB_CONF_L1DS          BIT(17)
> > +/* USB 2.0 clock gate disable. */
> > +#define USB_CONF_CLK2OFFEN     BIT(18)
> > +/* USB 2.0 clock gate enable. */
> > +#define USB_CONF_CLK2OFFDS     BIT(19)
> > +/* L0 LPM state entry request (used in HS/FS mode). */
> > +#define USB_CONF_LGO_L0                BIT(20)
> > +/* USB 3.0 clock gate disable. */
> > +#define USB_CONF_CLK3OFFEN     BIT(21)
> > +/* USB 3.0 clock gate enable. */
> > +#define USB_CONF_CLK3OFFDS     BIT(22)
> > +/* Bit 23 is reserved*/
> > +/* U1 state entry enable (used in SS mode). */
> > +#define USB_CONF_U1EN          BIT(24)
> > +/* U1 state entry disable (used in SS mode). */
> > +#define USB_CONF_U1DS          BIT(25)
> > +/* U2 state entry enable (used in SS mode). */
> > +#define USB_CONF_U2EN          BIT(26)
> > +/* U2 state entry disable (used in SS mode). */
> > +#define USB_CONF_U2DS          BIT(27)
> > +/* U0 state entry request (used in SS mode). */
> > +#define USB_CONF_LGO_U0                BIT(28)
> > +/* U1 state entry request (used in SS mode). */
> > +#define USB_CONF_LGO_U1                BIT(29)
> > +/* U2 state entry request (used in SS mode). */
> > +#define USB_CONF_LGO_U2                BIT(30)
> > +/* SS.Inactive state entry request (used in SS mode) */
> > +#define USB_CONF_LGO_SSINACT   BIT(31)
> > +
> > +/* USB_STS - bitmasks */
> > +/*
> > + * Configuration status.
> > + * 1 - device is in the configured state.
> > + * 0 - device is not configured.
> > + */
> > +#define USB_STS_CFGSTS_MASK    BIT(0)
> > +#define USB_STS_CFGSTS(p)      ((p) & USB_STS_CFGSTS_MASK)
> > +/*
> > + * On-chip memory overflow.
> > + * 0 - On-chip memory status OK.
> > + * 1 - On-chip memory overflow.
> > + */
> > +#define USB_STS_OV_MASK                BIT(1)
> > +#define USB_STS_OV(p)          ((p) & USB_STS_OV_MASK)
> > +/*
> > + * SuperSpeed connection status.
> > + * 0 - USB in SuperSpeed mode disconnected.
> > + * 1 - USB in SuperSpeed mode connected.
> > + */
> > +#define USB_STS_USB3CONS_MASK  BIT(2)
> > +#define USB_STS_USB3CONS(p)    ((p) & USB_STS_USB3CONS_MASK)
> > +/*
> > + * DMA transfer configuration status.
> > + * 0 - single request.
> > + * 1 - multiple TRB chain
> > + */
> > +#define USB_STS_DTRANS_MASK    BIT(3)
> > +#define USB_STS_DTRANS(p)      ((p) & USB_STS_DTRANS_MASK)
> > +/*
> > + * Device speed.
> > + * 0 - Undefined (value after reset).
> > + * 1 - Low speed
> > + * 2 - Full speed
> > + * 3 - High speed
> > + * 4 - Super speed
> > + */
> > +#define USB_STS_USBSPEED_MASK  GENMASK(6, 4)
> > +#define USB_STS_USBSPEED(p)    (((p) & USB_STS_USBSPEED_MASK) >> 4)
> > +#define USB_STS_LS             (0x1 << 4)
> > +#define USB_STS_FS             (0x2 << 4)
> > +#define USB_STS_HS             (0x3 << 4)
> > +#define USB_STS_SS             (0x4 << 4)
> > +#define DEV_UNDEFSPEED(p)      (((p) & USB_STS_USBSPEED_MASK) == (0x0 << 4))
> > +#define DEV_LOWSPEED(p)                (((p) & USB_STS_USBSPEED_MASK) == USB_STS_LS)
> > +#define DEV_FULLSPEED(p)       (((p) & USB_STS_USBSPEED_MASK) == USB_STS_FS)
> > +#define DEV_HIGHSPEED(p)       (((p) & USB_STS_USBSPEED_MASK) == USB_STS_HS)
> > +#define DEV_SUPERSPEED(p)      (((p) & USB_STS_USBSPEED_MASK) == USB_STS_SS)
> > +/*
> > + * Endianness for SFR access.
> > + * 0 - Little Endian order (default after hardware reset).
> > + * 1 - Big Endian order
> > + */
> > +#define USB_STS_ENDIAN_MASK    BIT(7)
> > +#define USB_STS_ENDIAN(p)      ((p) & USB_STS_ENDIAN_MASK)
> > +/*
> > + * HS/FS clock turn-off status.
> > + * 0 - hsfs clock is always on.
> > + * 1 - hsfs clock turn-off in L2 (HS/FS mode) is enabled
> > + *          (default after hardware reset).
> > + */
> > +#define USB_STS_CLK2OFF_MASK   BIT(8)
> > +#define USB_STS_CLK2OFF(p)     ((p) & USB_STS_CLK2OFF_MASK)
> > +/*
> > + * PCLK clock turn-off status.
> > + * 0 - pclk clock is always on.
> > + * 1 - pclk clock turn-off in U3 (SS mode) is enabled
> > + *          (default after hardware reset).
> > + */
> > +#define USB_STS_CLK3OFF_MASK   BIT(9)
> > +#define USB_STS_CLK3OFF(p)     ((p) & USB_STS_CLK3OFF_MASK)
> > +/*
> > + * Controller in reset state.
> > + * 0 - Internal reset is active.
> > + * 1 - Internal reset is not active and controller is fully operational.
> > + */
> > +#define USB_STS_IN_RST_MASK    BIT(10)
> > +#define USB_STS_IN_RST(p)      ((p) & USB_STS_IN_RST_MASK)
> > +/*
> > + * Device enable Status.
> > + * 0 - USB device is disabled (VBUS input is disconnected from internal logic).
> > + * 1 - USB device is enabled (VBUS input is connected to the internal logic).
> > + */
> > +#define USB_STS_DEVS_MASK      BIT(14)
> > +#define USB_STS_DEVS(p)                ((p) & USB_STS_DEVS_MASK)
> > +/*
> > + * DAddress statuss.
> > + * 0 - USB device is default state.
> > + * 1 - USB device is at least in address state.
> > + */
> > +#define USB_STS_ADDRESSED_MASK BIT(15)
> > +#define USB_STS_ADDRESSED(p)   ((p) & USB_STS_ADDRESSED_MASK)
> > +/*
> > + * L1 LPM state enable status (used in HS/FS mode).
> > + * 0 - Entering to L1 LPM state disabled.
> > + * 1 - Entering to L1 LPM state enabled.
> > + */
> > +#define USB_STS_L1ENS_MASK     BIT(16)
> > +#define USB_STS_L1ENS(p)       ((p) & USB_STS_L1ENS_MASK)
> > +/*
> > + * Internal VBUS connection status (used both in HS/FS  and SS mode).
> > + * 0 - internal VBUS is not detected.
> > + * 1 - internal VBUS is detected.
> > + */
> > +#define USB_STS_VBUSS_MASK     BIT(17)
> > +#define USB_STS_VBUSS(p)       ((p) & USB_STS_VBUSS_MASK)
> > +/*
> > + * HS/FS LPM  state (used in FS/HS mode).
> > + * 0 - L0 State
> > + * 1 - L1 State
> > + * 2 - L2 State
> > + * 3 - L3 State
> > + */
> > +#define USB_STS_LPMST_MASK     GENMASK(19, 18)
> > +#define DEV_L0_STATE(p)                (((p) & USB_STS_LPMST_MASK) == (0x0 << 18))
> > +#define DEV_L1_STATE(p)                (((p) & USB_STS_LPMST_MASK) == (0x1 << 18))
> > +#define DEV_L2_STATE(p)                (((p) & USB_STS_LPMST_MASK) == (0x2 << 18))
> > +#define DEV_L3_STATE(p)                (((p) & USB_STS_LPMST_MASK) == (0x3 << 18))
> > +/*
> > + * Disable HS status (used in FS/HS mode).
> > + * 0 - the disconnect bit for HS/FS mode is set .
> > + * 1 - the disconnect bit for HS/FS mode is not set.
> > + */
> > +#define USB_STS_USB2CONS_MASK  BIT(20)
> > +#define USB_STS_USB2CONS(p)    ((p) & USB_STS_USB2CONS_MASK)
> > +/*
> > + * HS/FS mode connection status (used in FS/HS mode).
> > + * 0 - High Speed operations in USB2.0 (FS/HS) mode not disabled.
> > + * 1 - High Speed operations in USB2.0 (FS/HS).
> > + */
> > +#define USB_STS_DISABLE_HS_MASK        BIT(21)
> > +#define USB_STS_DISABLE_HS(p)  ((p) & USB_STS_DISABLE_HS_MASK)
> > +/*
> > + * U1 state enable status (used in SS mode).
> > + * 0 - Entering to  U1 state disabled.
> > + * 1 - Entering to  U1 state enabled.
> > + */
> > +#define USB_STS_U1ENS_MASK     BIT(24)
> > +#define USB_STS_U1ENS(p)       ((p) & USB_STS_U1ENS_MASK)
> > +/*
> > + * U2 state enable status (used in SS mode).
> > + * 0 - Entering to  U2 state disabled.
> > + * 1 - Entering to  U2 state enabled.
> > + */
> > +#define USB_STS_U2ENS_MASK     BIT(25)
> > +#define USB_STS_U2ENS(p)       ((p) & USB_STS_U2ENS_MASK)
> > +/*
> > + * SuperSpeed Link LTSSM state. This field reflects USBSS-DEV current
> > + * SuperSpeed link state
> > + */
> > +#define USB_STS_LST_MASK       GENMASK(29, 26)
> > +#define DEV_LST_U0             (((p) & USB_STS_LST_MASK) == (0x0 << 26))
> > +#define DEV_LST_U1             (((p) & USB_STS_LST_MASK) == (0x1 << 26))
> > +#define DEV_LST_U2             (((p) & USB_STS_LST_MASK) == (0x2 << 26))
> > +#define DEV_LST_U3             (((p) & USB_STS_LST_MASK) == (0x3 << 26))
> > +#define DEV_LST_DISABLED       (((p) & USB_STS_LST_MASK) == (0x4 << 26))
> > +#define DEV_LST_RXDETECT       (((p) & USB_STS_LST_MASK) == (0x5 << 26))
> > +#define DEV_LST_INACTIVE       (((p) & USB_STS_LST_MASK) == (0x6 << 26))
> > +#define DEV_LST_POLLING                (((p) & USB_STS_LST_MASK) == (0x7 << 26))
> > +#define DEV_LST_RECOVERY       (((p) & USB_STS_LST_MASK) == (0x8 << 26))
> > +#define DEV_LST_HOT_RESET      (((p) & USB_STS_LST_MASK) == (0x9 << 26))
> > +#define DEV_LST_COMP_MODE      (((p) & USB_STS_LST_MASK) == (0xa << 26))
> > +#define DEV_LST_LB_STATE       (((p) & USB_STS_LST_MASK) == (0xb << 26))
> > +/*
> > + * DMA clock turn-off status.
> > + * 0 - DMA clock is always on (default after hardware reset).
> > + * 1 - DMA clock turn-off in U1, U2 and U3 (SS mode) is enabled.
> > + */
> > +#define USB_STS_DMAOFF_MASK    BIT(30)
> > +#define USB_STS_DMAOFF(p)      ((p) & USB_STS_DMAOFF_MASK)
> > +/*
> > + * SFR Endian statuss.
> > + * 0 - Little Endian order (default after hardware reset).
> > + * 1 - Big Endian order.
> > + */
> > +#define USB_STS_ENDIAN2_MASK   BIT(31)
> > +#define USB_STS_ENDIAN2(p)     ((p) & USB_STS_ENDIAN2_MASK)
> > +
> > +/* USB_CMD -  bitmasks */
> > +/* Set Function Address */
> > +#define USB_CMD_SET_ADDR       BIT(0)
> > +/*
> > + * Function Address This field is saved to the device only when the field
> > + * SET_ADDR is set '1 ' during write to USB_CMD register.
> > + * Software is responsible for entering the address of the device during
> > + * SET_ADDRESS request service. This field should be set immediately after
> > + * the SETUP packet is decoded, and prior to confirmation of the status phase
> > + */
> > +#define USB_CMD_FADDR_MASK     GENMASK(7, 1)
> > +#define USB_CMD_FADDR(p)       (((p) << 1) & USB_CMD_FADDR_MASK)
> > +/* Send Function Wake Device Notification TP (used only in SS mode). */
> > +#define USB_CMD_SDNFW          BIT(8)
> > +/* Set Test Mode (used only in HS/FS mode). */
> > +#define USB_CMD_STMODE         BIT(9)
> > +/* Test mode selector (used only in HS/FS mode) */
> > +#define USB_STS_TMODE_SEL_MASK GENMASK(11, 10)
> > +#define USB_STS_TMODE_SEL(p)   (((p) << 10) & USB_STS_TMODE_SEL_MASK)
> > +/*
> > + *  Send Latency Tolerance Message Device Notification TP (used only
> > + *  in SS mode).
> > + */
> > +#define USB_CMD_SDNLTM         BIT(12)
> > +/* Send Custom Transaction Packet (used only in SS mode) */
> > +#define USB_CMD_SPKT           BIT(13)
> > +/*Device Notification 'Function Wake' - Interface value (only in SS mode. */
> > +#define USB_CMD_DNFW_INT_MASK  GENMASK(23, 16)
> > +#define USB_STS_DNFW_INT(p)    (((p) << 16) & USB_CMD_DNFW_INT_MASK)
> > +/*
> > + * Device Notification 'Latency Tolerance Message' -373 BELT value [7:0]
> > + * (used only in SS mode).
> > + */
> > +#define USB_CMD_DNLTM_BELT_MASK        GENMASK(27, 16)
> > +#define USB_STS_DNLTM_BELT(p)  (((p) << 16) & USB_CMD_DNLTM_BELT_MASK)
> > +
> > +/* USB_ITPN - bitmasks */
> > +/*
> > + * ITP(SS) / SOF (HS/FS) number
> > + * In SS mode this field represent number of last ITP received from host.
> > + * In HS/FS mode this field represent number of last SOF received from host.
> > + */
> > +#define USB_ITPN_MASK          GENMASK(13, 0)
> > +#define USB_ITPN(p)            ((p) & USB_ITPN_MASK)
> > +
> > +/* USB_LPM - bitmasks */
> > +/* Host Initiated Resume Duration. */
> > +#define USB_LPM_HIRD_MASK      GENMASK(3, 0)
> > +#define USB_LPM_HIRD(p)                ((p) & USB_LPM_HIRD_MASK)
> > +/* Remote Wakeup Enable (bRemoteWake). */
> > +#define USB_LPM_BRW            BIT(4)
> > +
> > +/* USB_IEN - bitmasks */
> > +/* SS connection interrupt enable */
> > +#define USB_IEN_CONIEN         BIT(0)
> > +/* SS disconnection interrupt enable. */
> > +#define USB_IEN_DISIEN         BIT(1)
> > +/* USB SS warm reset interrupt enable. */
> > +#define USB_IEN_UWRESIEN       BIT(2)
> > +/* USB SS hot reset interrupt enable */
> > +#define USB_IEN_UHRESIEN       BIT(3)
> > +/* SS link U3 state enter interrupt enable (suspend).*/
> > +#define USB_IEN_U3ENTIEN       BIT(4)
> > +/* SS link U3 state exit interrupt enable (wakeup). */
> > +#define USB_IEN_U3EXTIEN       BIT(5)
> > +/* SS link U2 state enter interrupt enable.*/
> > +#define USB_IEN_U2ENTIEN       BIT(6)
> > +/* SS link U2 state exit interrupt enable.*/
> > +#define USB_IEN_U2EXTIEN       BIT(7)
> > +/* SS link U1 state enter interrupt enable.*/
> > +#define USB_IEN_U1ENTIEN       BIT(8)
> > +/* SS link U1 state exit interrupt enable.*/
> > +#define USB_IEN_U1EXTIEN       BIT(9)
> > +/* ITP/SOF packet detected interrupt enable.*/
> > +#define USB_IEN_ITPIEN         BIT(10)
> > +/* Wakeup interrupt enable.*/
> > +#define USB_IEN_WAKEIEN                BIT(11)
> > +/* Send Custom Packet interrupt enable.*/
> > +#define USB_IEN_SPKTIEN                BIT(12)
> > +/* HS/FS mode connection interrupt enable.*/
> > +#define USB_IEN_CON2IEN                BIT(16)
> > +/* HS/FS mode disconnection interrupt enable.*/
> > +#define USB_IEN_DIS2IEN                BIT(17)
> > +/* USB reset (HS/FS mode) interrupt enable.*/
> > +#define USB_IEN_U2RESIEN       BIT(18)
> > +/* LPM L2 state enter interrupt enable.*/
> > +#define USB_IEN_L2ENTIEN       BIT(20)
> > +/* LPM  L2 state exit interrupt enable.*/
> > +#define USB_IEN_L2EXTIEN       BIT(21)
> > +/* LPM L1 state enter interrupt enable.*/
> > +#define USB_IEN_L1ENTIEN       BIT(24)
> > +/* LPM  L1 state exit interrupt enable.*/
> > +#define USB_IEN_L1EXTIEN       BIT(25)
> > +/* Configuration reset interrupt enable.*/
> > +#define USB_IEN_CFGRESIEN      BIT(26)
> > +/* Start of the USB SS warm reset interrupt enable.*/
> > +#define USB_IEN_UWRESSIEN      BIT(28)
> > +/* End of the USB SS warm reset interrupt enable.*/
> > +#define USB_IEN_UWRESEIEN      BIT(29)
> > +
> > +#define USB_IEN_INIT  (USB_IEN_U2RESIEN | USB_ISTS_DIS2I | USB_IEN_CON2IEN \
> > +                      | USB_IEN_UHRESIEN | USB_IEN_UWRESIEN | USB_IEN_DISIEN \
> > +                      | USB_IEN_CONIEN | USB_IEN_U3EXTIEN | USB_IEN_L2ENTIEN \
> > +                      | USB_IEN_L2EXTIEN)
> > +
> > +/* USB_ISTS - bitmasks */
> > +/* SS Connection detected. */
> > +#define USB_ISTS_CONI          BIT(0)
> > +/* SS Disconnection detected. */
> > +#define USB_ISTS_DISI          BIT(1)
> > +/* UUSB warm reset detectede. */
> > +#define USB_ISTS_UWRESI                BIT(2)
> > +/* USB hot reset detected. */
> > +#define USB_ISTS_UHRESI                BIT(3)
> > +/* U3 link state enter detected (suspend).*/
> > +#define USB_ISTS_U3ENTI                BIT(4)
> > +/* U3 link state exit detected (wakeup). */
> > +#define USB_ISTS_U3EXTI                BIT(5)
> > +/* U2 link state enter detected.*/
> > +#define USB_ISTS_U2ENTI                BIT(6)
> > +/* U2 link state exit detected.*/
> > +#define USB_ISTS_U2EXTI                BIT(7)
> > +/* U1 link state enter detected.*/
> > +#define USB_ISTS_U1ENTI                BIT(8)
> > +/* U1 link state exit detected.*/
> > +#define USB_ISTS_U1EXTI                BIT(9)
> > +/* ITP/SOF packet detected.*/
> > +#define USB_ISTS_ITPI          BIT(10)
> > +/* Wakeup detected.*/
> > +#define USB_ISTS_WAKEI         BIT(11)
> > +/* Send Custom Packet detected.*/
> > +#define USB_ISTS_SPKTI         BIT(12)
> > +/* HS/FS mode connection detected.*/
> > +#define USB_ISTS_CON2I         BIT(16)
> > +/* HS/FS mode disconnection detected.*/
> > +#define USB_ISTS_DIS2I         BIT(17)
> > +/* USB reset (HS/FS mode) detected.*/
> > +#define USB_ISTS_U2RESI                BIT(18)
> > +/* LPM L2 state enter detected.*/
> > +#define USB_ISTS_L2ENTI                BIT(20)
> > +/* LPM  L2 state exit detected.*/
> > +#define USB_ISTS_L2EXTI                BIT(21)
> > +/* LPM L1 state enter detected.*/
> > +#define USB_ISTS_L1ENTI                BIT(24)
> > +/* LPM L1 state exit detected.*/
> > +#define USB_ISTS_L1EXTI                BIT(25)
> > +/* USB configuration reset detected.*/
> > +#define USB_ISTS_CFGRESI       BIT(26)
> > +/* Start of the USB warm reset detected.*/
> > +#define USB_ISTS_UWRESSI       BIT(28)
> > +/* End of the USB warm reset detected.*/
> > +#define USB_ISTS_UWRESEI       BIT(29)
> > +
> > +/* USB_SEL - bitmasks */
> > +#define EP_SEL_EPNO_MASK       GENMASK(3, 0)
> > +/* Endpoint number. */
> > +#define EP_SEL_EPNO(p)         ((p) & EP_SEL_EPNO_MASK)
> > +/* Endpoint direction bit - 0 - OUT, 1 - IN. */
> > +#define EP_SEL_DIR             BIT(7)
> > +
> > +#define select_ep_in(nr)       (EP_SEL_EPNO(p) | EP_SEL_DIR)
> > +#define select_ep_out          (EP_SEL_EPNO(p))
> > +
> > +/* EP_TRADDR - bitmasks */
> > +/* Transfer Ring address. */
> > +#define EP_TRADDR_TRADDR(p)    ((p))
> > +
> > +/* EP_CFG - bitmasks */
> > +/* Endpoint enable */
> > +#define EP_CFG_ENABLE          BIT(0)
> > +/*
> > + *  Endpoint type.
> > + * 1 - isochronous
> > + * 2 - bulk
> > + * 3 - interrupt
> > + */
> > +#define EP_CFG_EPTYPE_MASK     GENMASK(2, 1)
> > +#define EP_CFG_EPTYPE(p)       (((p) << 1)  & EP_CFG_EPTYPE_MASK)
> > +/* Stream support enable (only in SS mode). */
> > +#define EP_CFG_STREAM_EN       BIT(3)
> > +/* TDL check (only in SS mode for BULK EP). */
> > +#define EP_CFG_TDL_CHK         BIT(4)
> > +/* SID check (only in SS mode for BULK OUT EP). */
> > +#define EP_CFG_SID_CHK         BIT(5)
> > +/* DMA transfer endianness. */
> > +#define EP_CFG_EPENDIAN                BIT(7)
> > +/* Max burst size (used only in SS mode). */
> > +#define EP_CFG_MAXBURST_MASK   GENMASK(11, 8)
> > +#define EP_CFG_MAXBURST(p)     (((p) << 8) & EP_CFG_MAXBURST_MASK)
> > +/* ISO max burst. */
> > +#define EP_CFG_MULT_MASK       GENMASK(15, 14)
> > +#define EP_CFG_MULT(p)         (((p) << 14) & EP_CFG_MULT)
> > +/* ISO max burst. */
> > +#define EP_CFG_MAXPKTSIZE_MASK GENMASK(26, 16)
> > +#define EP_CFG_MAXPKTSIZE(p)   (((p) << 16) & EP_CFG_MAXPKTSIZE_MASK)
> > +/* Max number of buffered packets. */
> > +#define EP_CFG_BUFFERING_MASK  GENMASK(31, 27)
> > +#define EP_CFG_BUFFERING(p)    (((p) << 27) & EP_CFG_BUFFERING_MASK)
> > +
> > +/* EP_CMD - bitmasks */
> > +/* Endpoint reset. */
> > +#define EP_CMD_EPRST           BIT(0)
> > +/* Endpoint STALL set. */
> > +#define EP_CMD_SSTALL          BIT(1)
> > +/* Endpoint STALL clear. */
> > +#define EP_CMD_CSTALL          BIT(2)
> > +/* Send ERDY TP. */
> > +#define EP_CMD_ERDY            BIT(3)
> > +/* Request complete. */
> > +#define EP_CMD_REQ_CMPL                BIT(5)
> > +/* Transfer descriptor ready. */
> > +#define EP_CMD_DRDY            BIT(6)
> > +/* Data flush. */
> > +#define EP_CMD_DFLUSH          BIT(7)
> > +/*
> > + * Transfer Descriptor Length write  (used only for Bulk Stream capable
> > + * endpoints in SS mode).
> > + */
> > +#define EP_CMD_STDL            BIT(8)
> > +/* Transfer Descriptor Length (used only in SS mode for bulk endpoints). */
> > +#define EP_CMD_TDL_MASK                GENMASK(15, 9)
> > +#define EP_CMD_TDL(p)          (((p) << 9) & EP_CMD_TDL_MASK)
> > +/* ERDY Stream ID value (used in SS mode). */
> > +#define EP_CMD_ERDY_SID_MASK   GENMASK(31, 16)
> > +#define EP_CMD_ERDY_SID(p)     (((p) << 16) & EP_CMD_SID_MASK)
> > +
> > +/* EP_STS - bitmasks */
> > +/* Setup transfer complete. */
> > +#define EP_STS_SETUP           BIT(0)
> > +/* Endpoint STALL status. */
> > +#define EP_STS_STALL(p)                ((p) & BIT(1))
> > +/* Interrupt On Complete. */
> > +#define EP_STS_IOC             BIT(2)
> > +/* Interrupt on Short Packet. */
> > +#define EP_STS_ISP             BIT(3)
> > +/* Transfer descriptor missing. */
> > +#define EP_STS_DESCMIS         BIT(4)
> > +/* Stream Rejected (used only in SS mode) */
> > +#define EP_STS_STREAMR         BIT(5)
> > +/* EXIT from MOVE DATA State (used only for stream transfers in SS mode). */
> > +#define EP_STS_MD_EXIT         BIT(6)
> > +/* TRB error. */
> > +#define EP_STS_TRBERR          BIT(7)
> > +/* Not ready (used only in SS mode). */
> > +#define EP_STS_NRDY            BIT(8)
> > +/* DMA busy. */
> > +#define EP_STS_DBUSY(p)                ((p) & BIT(9))
> > +/* Endpoint Buffer Empty */
> > +#define EP_STS_BUFFEMPTY(p)    ((p) & BIT(10))
> > +/* Current Cycle Status */
> > +#define EP_STS_CCS(p)          ((p) & BIT(11))
> > +/* Prime (used only in SS mode. */
> > +#define EP_STS_PRIME           BIT(12)
> > +/* Stream error (used only in SS mode). */
> > +#define EP_STS_SIDERR          BIT(13)
> > +/* OUT size mismatch. */
> > +#define EP_STS_OUTSMM          BIT(14)
> > +/* ISO transmission error. */
> > +#define EP_STS_ISOERR          BIT(15)
> > +/* Host Packet Pending (only for SS mode). */
> > +#define EP_STS_HOSTPP(p)       ((p) & BIT(16))
> > +/* Stream Protocol State Machine State (only for Bulk stream endpoints). */
> > +#define EP_STS_SPSMST_MASK             GENMASK(18, 17)
> > +#define EP_STS_SPSMST_DISABLED(p)      (((p) & EP_STS_SPSMST_MASK) >> 17)
> > +#define EP_STS_SPSMST_IDLE(p)          (((p) & EP_STS_SPSMST_MASK) >> 17)
> > +#define EP_STS_SPSMST_START_STREAM(p)  (((p) & EP_STS_SPSMST_MASK) >> 17)
> > +#define EP_STS_SPSMST_MOVE_DATA(p)     (((p) & EP_STS_SPSMST_MASK) >> 17)
> > +/* Interrupt On Transfer complete. */
> > +#define EP_STS_IOT             BIT(19)
> > +/* OUT queue endpoint number. */
> > +#define EP_STS_OUTQ_NO_MASK    GENMASK(27, 24)
> > +#define EP_STS_OUTQ_NO(p)      (((p) & EP_STS_OUTQ_NO_MASK) >> 24)
> > +/* OUT queue valid flag. */
> > +#define EP_STS_OUTQ_VAL_MASK   BIT(28)
> > +#define EP_STS_OUTQ_VAL(p)     ((p) & EP_STS_OUTQ_VAL_MASK)
> > +/* SETUP WAIT. */
> > +#define EP_STS_STPWAIT         BIT(31)
> > +
> > +/* EP_STS_SID - bitmasks */
> > +/* Stream ID (used only in SS mode). */
> > +#define EP_STS_SID_MASK                GENMASK(15, 0)
> > +#define EP_STS_SID(p)          ((p) & EP_STS_SID_MASK)
> > +
> > +/* EP_STS_EN - bitmasks */
> > +/* SETUP interrupt enable. */
> > +#define EP_STS_EN_SETUPEN      BIT(0)
> > +/* OUT transfer missing descriptor enable. */
> > +#define EP_STS_EN_DESCMISEN    BIT(4)
> > +/* Stream Rejected enable. */
> > +#define EP_STS_EN_STREAMREN    BIT(5)
> > +/* Move Data Exit enable.*/
> > +#define EP_STS_EN_MD_EXITEN    BIT(6)
> > +/* TRB enable. */
> > +#define EP_STS_EN_TRBERREN     BIT(7)
> > +/* NRDY enable. */
> > +#define EP_STS_EN_NRDYEN       BIT(8)
> > +/* Prime enable. */
> > +#define EP_STS_EN_PRIMEEEN     BIT(12)
> > +/* Stream error enable. */
> > +#define EP_STS_EN_SIDERREN     BIT(13)
> > +/* OUT size mismatch enable. */
> > +#define EP_STS_EN_OUTSMMEN     BIT(14)
> > +/* ISO transmission error enable. */
> > +#define EP_STS_EN_ISOERREN     BIT(15)
> > +/* Interrupt on Transmission complete enable. */
> > +#define EP_STS_EN_IOTEN                BIT(19)
> > +/* Setup Wait interrupt enable. */
> > +#define EP_STS_EN_STPWAITEN    BIT(31)
> > +
> > +/* DRBL- bitmasks */
> > +#define DB_VALUE_BY_INDEX(index) (1 << (index))
> > +#define DB_VALUE_EP0_OUT       BIT(0)
> > +#define DB_VALUE_EP0_IN                BIT(16)
> > +
> > +/* EP_IEN - bitmasks */
> > +#define EP_IEN(index)          (1 << (index))
> > +#define EP_IEN_EP_OUT0         BIT(0)
> > +#define EP_IEN_EP_IN0          BIT(16)
> > +
> > +/* EP_ISTS - bitmasks */
> > +#define EP_ISTS(index)         (1 << (index))
> > +#define EP_ISTS_EP_OUT0                BIT(0)
> > +#define EP_ISTS_EP_IN0         BIT(16)
> > +
> > +/* EP_PWR- bitmasks */
> > +/*Power Shut Off capability enable*/
> > +#define PUSB_PWR_PSO_EN                BIT(0)
> > +/*Power Shut Off capability disable*/
> > +#define PUSB_PWR_PSO_DS                BIT(1)
> > +/*
> > + * Enables turning-off Reference Clock.
> > + * This bit is optional and implemented only when support for OTG is
> > + * implemented (indicated by OTG_READY bit set to '1').
> > + */
> > +#define PUSB_PWR_STB_CLK_SWITCH_EN     BIT(8)
> > +/*
> > + * Status bit indicating that operation required by STB_CLK_SWITCH_EN write
> > + * is completed
> > + */
> > +#define PUSB_PWR_STB_CLK_SWITCH_DONE   BIT(9)
> > +/* This bit informs if Fast Registers Access is enabled. */
> > +#define PUSB_PWR_FST_REG_ACCESS_STAT   BIT(30)
> > +/* Fast Registers Access Enable. */
> > +#define PUSB_PWR_FST_REG_ACCESS        BIT(31)
> > +
> > +/* USB_CAP1- bitmasks */
> > +/*
> > + * SFR Interface type
> > + * These field reflects type of SFR interface implemented:
> > + * 0x0 - OCP
> > + * 0x1 - AHB,
> > + * 0x2 - PLB
> > + * 0x3 - AXI
> > + * 0x4-0xF - reserved
> > + */
> > +#define USB_CAP1_SFR_TYPE_MASK GENMASK(3, 0)
> > +#define DEV_SFR_TYPE_OCP(p)    (((p) & USB_CAP1_SFR_TYPE_MASK) == 0x0)
> > +#define DEV_SFR_TYPE_AHB(p)    (((p) & USB_CAP1_SFR_TYPE_MASK) == 0x1)
> > +#define DEV_SFR_TYPE_PLB(p)    (((p) & USB_CAP1_SFR_TYPE_MASK) == 0x2)
> > +#define DEV_SFR_TYPE_AXI(p)    (((p) & USB_CAP1_SFR_TYPE_MASK) == 0x3)
> > +/*
> > + * SFR Interface width
> > + * These field reflects width of SFR interface implemented:
> > + * 0x0 - 8 bit interface,
> > + * 0x1 - 16 bit interface,
> > + * 0x2 - 32 bit interface
> > + * 0x3 - 64 bit interface
> > + * 0x4-0xF - reserved
> > + */
> > +#define USB_CAP1_SFR_WIDTH_MASK        GENMASK(7, 4)
> > +#define DEV_SFR_WIDTH_8(p)     (((p) & USB_CAP1_SFR_WIDTH_MASK) == (0x0 << 4))
> > +#define DEV_SFR_WIDTH_16(p)    (((p) & USB_CAP1_SFR_WIDTH_MASK) == (0x1 << 4))
> > +#define DEV_SFR_WIDTH_32(p)    (((p) & USB_CAP1_SFR_WIDTH_MASK) == (0x2 << 4))
> > +#define DEV_SFR_WIDTH_64(p)    (((p) & USB_CAP1_SFR_WIDTH_MASK) == (0x3 << 4))
> > +/*
> > + * DMA Interface type
> > + * These field reflects type of DMA interface implemented:
> > + * 0x0 - OCP
> > + * 0x1 - AHB,
> > + * 0x2 - PLB
> > + * 0x3 - AXI
> > + * 0x4-0xF - reserved
> > + */
> > +#define USB_CAP1_DMA_TYPE_MASK GENMASK(11, 8)
> > +#define DEV_DMA_TYPE_OCP(p)    (((p) & USB_CAP1_DMA_TYPE_MASK) == (0x0 << 8))
> > +#define DEV_DMA_TYPE_AHB(p)    (((p) & USB_CAP1_DMA_TYPE_MASK) == (0x1 << 8))
> > +#define DEV_DMA_TYPE_PLB(p)    (((p) & USB_CAP1_DMA_TYPE_MASK) == (0x2 << 8))
> > +#define DEV_DMA_TYPE_AXI(p)    (((p) & USB_CAP1_DMA_TYPE_MASK) == (0x3 << 8))
> > +/*
> > + * DMA Interface width
> > + * These field reflects width of DMA interface implemented:
> > + * 0x0 - reserved,
> > + * 0x1 - reserved,
> > + * 0x2 - 32 bit interface
> > + * 0x3 - 64 bit interface
> > + * 0x4-0xF - reserved
> > + */
> > +#define USB_CAP1_DMA_WIDTH_MASK        GENMASK(15, 12)
> > +#define DEV_DMA_WIDTH_32(p)    (((p) & USB_CAP1_DMA_WIDTH_MASK) == (0x2 << 12))
> > +#define DEV_DMA_WIDTH_64(p)    (((p) & USB_CAP1_DMA_WIDTH_MASK) == (0x3 << 12))
> > +/*
> > + * USB3 PHY Interface type
> > + * These field reflects type of USB3 PHY interface implemented:
> > + * 0x0 - USB PIPE,
> > + * 0x1 - RMMI,
> > + * 0x2-0xF - reserved
> > + */
> > +#define USB_CAP1_U3PHY_TYPE_MASK GENMASK(19, 16)
> > +#define DEV_U3PHY_PIPE(p) (((p) & USB_CAP1_U3PHY_TYPE_MASK) == (0x0 << 16))
> > +#define DEV_U3PHY_RMMI(p) (((p) & USB_CAP1_U3PHY_TYPE_MASK) == (0x1 << 16))
> > +/*
> > + * USB3 PHY Interface width
> > + * These field reflects width of USB3 PHY interface implemented:
> > + * 0x0 - 8 bit PIPE interface,
> > + * 0x1 - 16 bit PIPE interface,
> > + * 0x2 - 32 bit PIPE interface,
> > + * 0x3 - 64 bit PIPE interface
> > + * 0x4-0xF - reserved
> > + * Note: When SSIC interface is implemented this field shows the width of
> > + * internal PIPE interface. The RMMI interface is always 20bit wide.
> > + */
> > +#define USB_CAP1_U3PHY_WIDTH_MASK GENMASK(23, 20)
> > +#define DEV_U3PHY_WIDTH_8(p) \
> > +       (((p) & USB_CAP1_U3PHY_WIDTH_MASK) == (0x0 << 20))
> > +#define DEV_U3PHY_WIDTH_16(p) \
> > +       (((p) & USB_CAP1_U3PHY_WIDTH_MASK) == (0x1 << 16))
> > +#define DEV_U3PHY_WIDTH_32(p) \
> > +       (((p) & USB_CAP1_U3PHY_WIDTH_MASK) == (0x2 << 20))
> > +#define DEV_U3PHY_WIDTH_64(p) \
> > +       (((p) & USB_CAP1_U3PHY_WIDTH_MASK) == (0x3 << 16))
> > +
> > +/*
> > + * USB2 PHY Interface enable
> > + * These field informs if USB2 PHY interface is implemented:
> > + * 0x0 - interface NOT implemented,
> > + * 0x1 - interface implemented
> > + */
> > +#define USB_CAP1_U2PHY_EN(p)   ((p) & BIT(24))
> > +/*
> > + * USB2 PHY Interface type
> > + * These field reflects type of USB2 PHY interface implemented:
> > + * 0x0 - UTMI,
> > + * 0x1 - ULPI
> > + */
> > +#define DEV_U2PHY_ULPI(p)      ((p) & BIT(25))
> > +/*
> > + * USB2 PHY Interface width
> > + * These field reflects width of USB2 PHY interface implemented:
> > + * 0x0 - 8 bit interface,
> > + * 0x1 - 16 bit interface,
> > + * Note: The ULPI interface is always 8bit wide.
> > + */
> > +#define DEV_U2PHY_WIDTH_16(p)  ((p) & BIT(26))
> > +/*
> > + * OTG Ready
> > + * 0x0 - pure device mode
> > + * 0x1 - some features and ports for CDNS USB OTG controller are implemented.
> > + */
> > +#define USB_CAP1_OTG_READY(p)  ((p) & BIT(27))
> > +
> > +/* USB_CAP2- bitmasks */
> > +/*
> > + * The actual size of the connected On-chip RAM memory in kB:
> > + * - 0 means 256 kB (max supported mem size)
> > + * - value other than 0 reflects the mem size in kB
> > + */
> > +#define USB_CAP2_ACTUAL_MEM_SIZE(p) ((p) & GENMASK(7, 0))
> > +/*
> > + * Max supported mem size
> > + * These field reflects width of on-chip RAM address bus width,
> > + * which determines max supported mem size:
> > + * 0x0-0x7 - reserved,
> > + * 0x8 - support for 4kB mem,
> > + * 0x9 - support for 8kB mem,
> > + * 0xA - support for 16kB mem,
> > + * 0xB - support for 32kB mem,
> > + * 0xC - support for 64kB mem,
> > + * 0xD - support for 128kB mem,
> > + * 0xE - support for 256kB mem,
> > + * 0xF - reserved
> > + */
> > +#define USB_CAP2_MAX_MEM_SIZE(p) ((p) & GENMASK(11, 8))
> > +
> > +/* USB_CAP3- bitmasks */
> > +#define EP_IS_IMPLEMENTED(reg, index) ((reg) & (1 << (index)))
> > +
> > +/* USB_CAP4- bitmasks */
> > +#define EP_SUPPORT_ISO(reg, index) ((reg) & (1 << (index)))
> > +
> > +/* USB_CAP5- bitmasks */
> > +#define EP_SUPPORT_STREAM(reg, index) ((reg) & (1 << (index)))
> > +
> > +/* USB_CAP6- bitmasks */
> > +/* The USBSS-DEV Controller  Internal build number. */
> > +#define GET_DEV_VERSION__INTERNAL_NUMBER(p) ((p) & GENMASK(7, 0))
> > +/* The USBSS-DEV Controller  version number. */
> > +#define GET_DEV_VERSION(p) ((p) & GENMASK(31, 8))
> > +
> > +/* DBG_LINK1- bitmasks */
> > +/*
> > + * LFPS_MIN_DET_U1_EXIT value This parameter configures the minimum
> > + * time required for decoding the received LFPS as an LFPS.U1_Exit.
> > + */
> > +#define DBG_LINK1_LFPS_MIN_DET_U1_EXIT(p)      ((p) & GENMASK(7, 0))
> > +/*
> > + * LFPS_MIN_GEN_U1_EXIT value This parameter configures the minimum time for
> > + * phytxelecidle deassertion when LFPS.U1_Exit
> > + */
> > +#define DBG_LINK1_LFPS_MIN_GEN_U1_EXIT(p)      (((p) << 8) & GENMASK(15, 8))
> > +/*
> > + * RXDET_BREAK_DIS value This parameter configures terminating the Far-end
> > + * Receiver termination detection sequence:
> > + * 0: it is possible that USBSS_DEV will terminate Farend receiver
> > + *    termination detection sequence
> > + * 1: USBSS_DEV will not terminate Far-end receiver termination
> > + *    detection sequence
> > + */
> > +#define DBG_LINK1_RXDET_BREAK_DIS              BIT(16)
> > +/* LFPS_GEN_PING value This parameter configures the LFPS.Ping generation */
> > +#define DBG_LINK1_LFPS_GEN_PING(p)             (((p) << 17) & GENMASK(21, 17))
> > +/*
> > + * Set the LFPS_MIN_DET_U1_EXIT value Writing '1' to this bit writes the
> > + * LFPS_MIN_DET_U1_EXIT field value to the device. This bit is automatically
> > + * cleared. Writing '0' has no effect
> > + */
> > +#define DBG_LINK1_LFPS_MIN_DET_U1_EXIT_SET     BIT(24)
> > +/*
> > + * Set the LFPS_MIN_GEN_U1_EXIT value. Writing '1' to this bit writes the
> > + * LFPS_MIN_GEN_U1_EXIT field value to the device. This bit is automatically
> > + * cleared. Writing '0' has no effect
> > + */
> > +#define DBG_LINK1_LFPS_MIN_GEN_U1_EXIT_SET     BIT(25)
> > +/*
> > + * Set the RXDET_BREAK_DIS value Writing '1' to this bit writes
> > + * the RXDET_BREAK_DIS field value to the device. This bit is automatically
> > + * cleared. Writing '0' has no effect
> > + */
> > +#define DBG_LINK1_RXDET_BREAK_DIS_SET          BIT(26)
> > +/*
> > + * Set the LFPS_GEN_PING_SET value Writing '1' to this bit writes
> > + * the LFPS_GEN_PING field value to the device. This bit is automatically
> > + * cleared. Writing '0' has no effect."
> > + */
> > +#define DBG_LINK1_LFPS_GEN_PING_SET            BIT(27)
> > +
> > +#define gadget_to_cdns3_device(g) (container_of(g, struct cdns3_device, gadget))
> > +
> > +#define ep_to_cdns3_ep(ep) (container_of(ep, struct cdns3_endpoint, endpoint))
> > +
> > +/*-------------------------------------------------------------------------*/
> > +/*
> > + * USBSS-DEV DMA interface.
> > + */
> > +#define TRBS_PER_SEGMENT       40
> > +
> > +#define ISO_MAX_INTERVAL       10
> > +
> > +/*
> > + *Only for ISOC endpoints - maximum number of TRBs is calculated as
> > + * pow(2, bInterval-1) * number of usb requests. It is limitation made by
> > + * driver to save memory. Controller must prepare TRB for each ITP even
> > + * if bInterval > 1. It's the reason why driver needs so many TRBs for
> > + * isochronous endpoints.
> > + */
> > +#define TRBS_PER_ISOC_SEGMENT  (ISO_MAX_INTERVAL * 8)
> > +
> > +#define GET_TRBS_PER_SEGMENT(ep_type) ((ep_type) == USB_ENDPOINT_XFER_ISOC ? \
> > +                                     TRBS_PER_ISOC_SEGMENT : TRBS_PER_SEGMENT)
> > +/**
> > + * struct cdns3_trb - represent Transfer Descriptor block.
> > + * @buffer:    pointer to buffer data
> > + * @length:    length of data
> > + * @control:   control flags.
> > + *
> > + * This structure describes transfer block serviced by DMA module.
> > + */
> > +struct cdns3_trb {
> > +       __le32 buffer;
> > +       __le32 length;
> > +       __le32 control;
> > +};
> > +
> > +#define TRB_SIZE               (sizeof(struct cdns3_trb))
> > +#define TRB_RING_SIZE          (TRB_SIZE * TRBS_PER_SEGMENT)
> > +#define TRB_ISO_RING_SIZE      (TRB_SIZE * TRBS_PER_ISOC_SEGMENT)
> > +#define TRB_CTRL_RING_SIZE     (TRB_SIZE * 2)
> > +
> > +/* TRB bit mask */
> > +#define TRB_TYPE_BITMASK       GENMASK(15, 10)
> > +#define TRB_TYPE(p)            ((p) << 10)
> > +#define TRB_FIELD_TO_TYPE(p)   (((p) & TRB_TYPE_BITMASK) >> 10)
> > +
> > +/* TRB type IDs */
> > +/* bulk, interrupt, isoc , and control data stage */
> > +#define TRB_NORMAL             1
> > +/* TRB for linking ring segments */
> > +#define TRB_LINK               6
> > +
> > +/* Cycle bit - indicates TRB ownership by driver or hw*/
> > +#define TRB_CYCLE              BIT(0)
> > +/*
> > + * When set to '1', the device will toggle its interpretation of the Cycle bit
> > + */
> > +#define TRB_TOGGLE             BIT(1)
> > +
> > +/* Interrupt on short packet*/
> > +#define TRB_ISP                        BIT(2)
> > +/*Setting this bit enables FIFO DMA operation mode*/
> > +#define TRB_FIFO_MODE          BIT(3)
> > +/* Set PCIe no snoop attribute */
> > +#define TRB_CHAIN              BIT(4)
> > +/* Interrupt on completion */
> > +#define TRB_IOC                        BIT(5)
> > +
> > +/* stream ID bitmasks. */
> > +#define TRB_STREAM_ID(p)       ((p) & GENMASK(31, 16))
> > +
> > +/* transfer_len bitmasks. */
> > +#define TRB_LEN(p)             ((p) & GENMASK(16, 0))
> > +
> > +/* transfer_len bitmasks - bits 31:24 */
> > +#define TRB_BURST_LEN(p)       ((p) & GENMASK(31, 24))
> > +
> > +/* Data buffer pointer bitmasks*/
> > +#define TRB_BUFFER(p)          ((p) & GENMASK(31, 0))
> > +
> > +/*-------------------------------------------------------------------------*/
> > +/* Driver numeric constants */
> > +
> > +/* Such declaration should be added to ch9.h */
> > +#define USB_DEVICE_MAX_ADDRESS         127
> > +
> > +/* Endpoint init values */
> > +#define CDNS3_EP_MAX_PACKET_LIMIT      1024
> > +#define CDNS3_EP_MAX_STREAMS           15
> > +#define CDNS3_EP0_MAX_PACKET_LIMIT     512
> > +
> > +/* All endpoints including EP0 */
> > +#define CDNS3_ENDPOINTS_MAX_COUNT      32
> > +#define CDNS3_EP_ZLP_BUF_SIZE          1024
> > +
> > +#define CDNS3_EP_BUF_SIZE              2       /* KB */
> > +#define CDNS3_ALIGNED_BUF_SIZE         16384   /* Bytes */
> > +#define CDNS3_MAX_NUM_DESCMISS_BUF     32
> > +#define CDNS3_DESCMIS_BUF_SIZE         2048    /* Bytes */
> > +/*-------------------------------------------------------------------------*/
> > +/* Used structs */
> > +
> > +struct cdns3_device;
> > +
> > +/**
> > + * struct cdns3_endpoint - extended device side representation of USB endpoint.
> > + * @endpoint: usb endpoint
> > + * @pending_req_list: list of requests queuing on transfer ring.
> > + * @deferred_req_list: list of requests waiting for queuing on transfer ring.
> > + * @descmiss_req_list: list of requests internally allocated by driver (WA1).
> > + * @trb_pool: transfer ring - array of transaction buffers
> > + * @trb_pool_dma: dma address of transfer ring
> > + * @cdns3_dev: device associated with this endpoint
> > + * @name: a human readable name e.g. ep1out
> > + * @flags: specify the current state of endpoint
> > + * @descmis_req: internal transfer object used for getting data from on-chip
> > + *     buffer. It can happen only if function driver doesn't send usb_request
> > + *     object on time.
> > + * @aligned_buff: aligned to 8 bytes data buffer. Buffer address used in
> > + *     TRB shall be aligned to 8.
> > + * @aligned_dma_addr: dma address of aligned_buff
> > + * @dir: endpoint direction
> > + * @num: endpoint number (1 - 15)
> > + * @type: set to bmAttributes & USB_ENDPOINT_XFERTYPE_MASK
> > + * @interval: interval between packets used for ISOC endpoint.
> > + * @free_trbs: number of free TRBs in transfer ring
> > + * @num_trbs: number of all TRBs in transfer ring
> > + * @pcs: producer cycle state
> > + * @ccs: consumer cycle state
> > + * @enqueue: enqueue index in transfer ring
> > + * @dequeue: dequeue index in transfer ring
> > + */
> > +struct cdns3_endpoint {
> > +       struct usb_ep           endpoint;
> > +       struct list_head        pending_req_list;
> > +       struct list_head        deferred_req_list;
> > +       struct list_head        descmiss_req_list;
> > +
> > +       struct cdns3_trb        *trb_pool;
> > +       dma_addr_t              trb_pool_dma;
> > +
> > +       struct cdns3_device     *cdns3_dev;
> > +       char                    name[20];
> > +
> > +#define EP_ENABLED             BIT(0)
> > +#define EP_STALL               BIT(1)
> > +#define EP_WEDGE               BIT(2)
> > +#define EP_TRANSFER_STARTED    BIT(3)
> > +#define EP_UPDATE_EP_TRBADDR   BIT(4)
> > +#define EP_PENDING_REQUEST     BIT(5)
> > +#define EP_RING_FULL           BIT(6)
> > +#define EP_CLAIMED             BIT(7)
> > +#define EP_QUIRK_EXTRA_BUF_DET BIT(8)
> > +#define EP_QUIRK_EXTRA_BUF_EN  BIT(9)
> > +#define EP_QUIRK_END_TRANSFER  BIT(10)
> > +
> > +       u32                     flags;
> > +
> > +       struct cdns3_request    *descmis_req;
> > +
> > +       void                    *aligned_buff;
> > +       dma_addr_t              aligned_dma_addr;
> > +       u8                      dir;
> > +       u8                      num;
> > +       u8                      type;
> > +       int                     interval;
> > +
> > +       int                     free_trbs;
> > +       int                     num_trbs;
> > +       u8                      pcs;
> > +       u8                      ccs;
> > +       int                     enqueue;
> > +       int                     dequeue;
> > +};
> > +
> > +/**
> > + * struct cdns3_request - extended device side representation of usb_request
> > + *                        object .
> > + * @request: generic usb_request object describing single I/O request.
> > + * @priv_ep: extended representation of usb_ep object
> > + * @trb: the first TRB association with this request
> > + * @start_trb: number of the first TRB in transfer ring
> > + * @end_trb: number of the last TRB in transfer ring
> > + * @flags: flag specifying special usage of request
> > + * @list: used by internally allocated request to add to descmiss_req_list.
> > + */
> > +struct cdns3_request {
> > +       struct usb_request      request;
> > +       struct cdns3_endpoint   *priv_ep;
> > +       struct cdns3_trb        *trb;
> > +       int                     start_trb;
> > +       int                     end_trb;
> > +#define REQUEST_PENDING                BIT(0)
> > +#define REQUEST_INTERNAL       BIT(1)
> > +#define REQUEST_INTERNAL_CH    BIT(2)
> > +#define REQUEST_ZLP            BIT(3)
> > +       u32                     flags;
> > +       struct list_head        list;
> > +};
> > +
> > +#define to_cdns3_request(r) (container_of(r, struct cdns3_request, request))
> > +
> > +/*Stages used during enumeration process.*/
> > +#define CDNS3_SETUP_STAGE              0x0
> > +#define CDNS3_DATA_STAGE               0x1
> > +#define CDNS3_STATUS_STAGE             0x2
> > +
> > +/**
> > + * struct cdns3_device - represent USB device.
> > + * @dev: pointer to device structure associated whit this controller
> > + * @sysdev: pointer to the DMA capable device
> > + * @gadget: device side representation of the peripheral controller
> > + * @gadget_driver: pointer to the gadget driver
> > + * @dev_ver: device controller version.
> > + * @lock: for synchronizing
> > + * @regs: base address for device side registers
> > + * @setup_buf: used while processing usb control requests
> > + * @setup_dma: dma address for setup_buf
> > + * @zlp_buf - zlp buffer
> > + * @ep0_stage: ep0 stage during enumeration process.
> > + * @ep0_data_dir: direction for control transfer
> > + * @eps: array of pointers to all endpoints with exclusion ep0
> > + * @selected_ep: actually selected endpoint. It's used only to improve
> > + *               performance.
> > + * @isoch_delay: value from Set Isoch Delay request. Only valid on SS/SSP.
> > + * @u1_allowed: allow device transition to u1 state
> > + * @u2_allowed: allow device transition to u2 state
> > + * @is_selfpowered: device is self powered
> > + * @setup_pending: setup packet is processing by gadget driver
> > + * @hw_configured_flag: hardware endpoint configuration was set.
> > + * @wake_up_flag: allow device to remote up the host
> > + * @status_completion_no_call: indicate that driver is waiting for status s
> > + *     stage completion. It's used in deferred SET_CONFIGURATION request.
> > + * @onchip_mem_allocated_size: actual size of on-chip memory assigned
> > + *     to endpoints
> > + * @pending_status_wq: workqueue handling status stage for deferred requests.
> > + * @pending_status_request: request for which status stage was deferred
> > + */
> > +struct cdns3_device {
> > +       struct device                   *dev;
> > +       struct device                   *sysdev;
> > +
> > +       struct usb_gadget               gadget;
> > +       struct usb_gadget_driver        *gadget_driver;
> > +
> > +#define CDNS_REVISION_V0               0x00024501
> > +#define CDNS_REVISION_V1               0x00024509
> > +       u32                             dev_ver;
> > +
> > +       /* generic spin-lock for drivers */
> > +       spinlock_t                      lock;
> > +
> > +       struct cdns3_usb_regs           __iomem *regs;
> > +
> > +       struct usb_ctrlrequest          *setup_buf;
> > +       dma_addr_t                      setup_dma;
> > +       void                            *zlp_buf;
> > +
> > +       u8                              ep0_stage;
> > +       int                             ep0_data_dir;
> > +
> > +       struct cdns3_endpoint           *eps[CDNS3_ENDPOINTS_MAX_COUNT];
> > +
> > +       u32                             selected_ep;
> > +       u16                             isoch_delay;
> > +
> > +       unsigned                        u1_allowed:1;
> > +       unsigned                        u2_allowed:1;
> > +       unsigned                        is_selfpowered:1;
> > +       unsigned                        setup_pending:1;
> > +       int                             hw_configured_flag:1;
> > +       int                             wake_up_flag:1;
> > +       unsigned                        status_completion_no_call:1;
> > +
> > +       struct work_struct              pending_status_wq;
> > +       struct usb_request              *pending_status_request;
> > +
> > +       /*in KB */
> > +       int                             onchip_mem_allocated_size;
> > +};
> > +
> > +int cdns3_handshake(void __iomem *ptr, u32 mask, u32 done, int usec);
> > +void cdns3_set_register_bit(void __iomem *ptr, u32 mask);
> > +dma_addr_t cdns3_trb_virt_to_dma(struct cdns3_endpoint *priv_ep,
> > +                                struct cdns3_trb *trb);
> > +enum usb_device_speed cdns3_get_speed(struct cdns3_device *priv_dev);
> > +void cdns3_pending_setup_status_handler(struct work_struct *work);
> > +void cdns3_hw_reset_eps_config(struct cdns3_device *priv_dev);
> > +void cdns3_set_hw_configuration(struct cdns3_device *priv_dev);
> > +void cdns3_select_ep(struct cdns3_device *priv_dev, u32 ep);
> > +void cdns3_allow_enable_l1(struct cdns3_device *priv_dev, int enable);
> > +struct usb_request *cdns3_next_request(struct list_head *list);
> > +int cdns3_ep_run_transfer(struct cdns3_endpoint *priv_ep,
> > +                         struct usb_request *request);
> > +int cdns3_allocate_trb_pool(struct cdns3_endpoint *priv_ep);
> > +u8 cdns3_ep_addr_to_index(u8 ep_addr);
> > +int cdns3_gadget_ep_set_wedge(struct usb_ep *ep);
> > +int cdns3_gadget_ep_set_halt(struct usb_ep *ep, int value);
> > +struct usb_request *cdns3_gadget_ep_alloc_request(struct usb_ep *ep,
> > +                                                 gfp_t gfp_flags);
> > +void cdns3_gadget_ep_free_request(struct usb_ep *ep,
> > +                                 struct usb_request *request);
> > +int cdns3_gadget_ep_dequeue(struct usb_ep *ep, struct usb_request *request);
> > +void cdns3_gadget_giveback(struct cdns3_endpoint *priv_ep,
> > +                          struct cdns3_request *priv_req,
> > +                          int status);
> > +
> > +int cdns3_init_ep0(struct cdns3_device *priv_dev,
> > +                  struct cdns3_endpoint *priv_ep);
> > +void cdns3_ep0_config(struct cdns3_device *priv_dev);
> > +void cdns3_ep_config(struct cdns3_endpoint *priv_ep);
> > +void cdns3_check_ep0_interrupt_proceed(struct cdns3_device *priv_dev, int dir);
> > +
> > +#endif /* __LINUX_CDNS3_GADGET */
> > diff --git a/drivers/usb/cdns3/host-export.h b/drivers/usb/cdns3/host-export.h
> > new file mode 100644
> > index 000000000000..b498a170b7e8
> > --- /dev/null
> > +++ b/drivers/usb/cdns3/host-export.h
> > @@ -0,0 +1,28 @@
> > +/* SPDX-License-Identifier: GPL-2.0 */
> > +/*
> > + * Cadence USBSS DRD Driver - Host Export APIs
> > + *
> > + * Copyright (C) 2017-2018 NXP
> > + *
> > + * Authors: Peter Chen <peter.chen@nxp.com>
> > + */
> > +#ifndef __LINUX_CDNS3_HOST_EXPORT
> > +#define __LINUX_CDNS3_HOST_EXPORT
> > +
> > +#ifdef CONFIG_USB_CDNS3_HOST
> > +
> > +int cdns3_host_init(struct cdns3 *cdns);
> > +void cdns3_host_exit(struct cdns3 *cdns);
> > +
> > +#else
> > +
> > +static inline int cdns3_host_init(struct cdns3 *cdns)
> > +{
> > +       return -ENXIO;
> > +}
> > +
> > +static inline void cdns3_host_exit(struct cdns3 *cdns) { }
> > +
> > +#endif /* CONFIG_USB_CDNS3_HOST */
> > +
> > +#endif /* __LINUX_CDNS3_HOST_EXPORT */
> > diff --git a/drivers/usb/cdns3/host.c b/drivers/usb/cdns3/host.c
> > new file mode 100644
> > index 000000000000..b43b0236a885
> > --- /dev/null
> > +++ b/drivers/usb/cdns3/host.c
> > @@ -0,0 +1,72 @@
> > +// SPDX-License-Identifier: GPL-2.0
> > +/*
> > + * Cadence USBSS DRD Driver - host side
> > + *
> > + * Copyright (C) 2018 Cadence Design Systems.
> > + * Copyright (C) 2017-2018 NXP
> > + *
> > + * Authors: Peter Chen <peter.chen@nxp.com>
> > + *         Pawel Laszczak <pawell@cadence.com>
> > + */
> > +
> > +#include <linux/platform_device.h>
> > +#include "core.h"
> > +
> > +static int __cdns3_host_init(struct cdns3 *cdns)
> > +{
> > +       struct platform_device *xhci;
> > +       int ret;
> > +
> > +       xhci = platform_device_alloc("xhci-hcd", PLATFORM_DEVID_AUTO);
> > +       if (!xhci) {
> > +               dev_err(cdns->dev, "couldn't allocate xHCI device\n");
> > +               return -ENOMEM;
> > +       }
> > +
> > +       xhci->dev.parent = cdns->dev;
> > +       cdns->host_dev = xhci;
> > +
> > +       ret = platform_device_add_resources(xhci, cdns->xhci_res,
> > +                                           CDNS3_XHCI_RESOURCES_NUM);
> > +       if (ret) {
> > +               dev_err(cdns->dev, "couldn't add resources to xHCI device\n");
> > +               goto err1;
> > +       }
> > +
> > +       ret = platform_device_add(xhci);
> > +       if (ret) {
> > +               dev_err(cdns->dev, "failed to register xHCI device\n");
> > +               goto err1;
> > +       }
> > +
> > +       return 0;
> > +err1:
> > +       platform_device_put(xhci);
> > +       return ret;
> > +}
> > +
> > +static void cdns3_host_exit(struct cdns3 *cdns)
> > +{
> > +       platform_device_unregister(cdns->host_dev);
> > +       cdns->host_dev = NULL;
> > +}
> > +
> > +int cdns3_host_init(struct cdns3 *cdns)
> > +{
> > +       struct cdns3_role_driver *rdrv;
> > +
> > +       rdrv = devm_kzalloc(cdns->dev, sizeof(*rdrv), GFP_KERNEL);
> > +       if (!rdrv)
> > +               return -ENOMEM;
> > +
> > +       rdrv->start     = __cdns3_host_init;
> > +       rdrv->stop      = cdns3_host_exit;
> > +       rdrv->state     = CDNS3_ROLE_STATE_INACTIVE;
> > +       rdrv->suspend   = NULL;
> > +       rdrv->resume    = NULL;
> > +       rdrv->name      = "host";
> > +
> > +       cdns->roles[CDNS3_ROLE_HOST] = rdrv;
> > +
> > +       return 0;
> > +}
> > diff --git a/drivers/usb/cdns3/trace.c b/drivers/usb/cdns3/trace.c
> > new file mode 100644
> > index 000000000000..587ae08e019d
> > --- /dev/null
> > +++ b/drivers/usb/cdns3/trace.c
> > @@ -0,0 +1,11 @@
> > +// SPDX-License-Identifier: GPL-2.0
> > +/*
> > + * USBSS device controller driver Trace Support
> > + *
> > + * Copyright (C) 2018 Cadence.
> > + *
> > + * Author: Pawel Laszczak <pawell@cadence.com>
> > + */
> > +
> > +#define CREATE_TRACE_POINTS
> > +#include "trace.h"
> > diff --git a/drivers/usb/cdns3/trace.h b/drivers/usb/cdns3/trace.h
> > new file mode 100644
> > index 000000000000..f3d7a91fae86
> > --- /dev/null
> > +++ b/drivers/usb/cdns3/trace.h
> > @@ -0,0 +1,389 @@
> > +/* SPDX-License-Identifier: GPL-2.0 */
> > +/*
> > + * USBSS device controller driver.
> > + * Trace support header file.
> > + *
> > + * Copyright (C) 2018 Cadence.
> > + *
> > + * Author: Pawel Laszczak <pawell@cadence.com>
> > + */
> > +
> > +#undef TRACE_SYSTEM
> > +#define TRACE_SYSTEM cdns3
> > +
> > +#if !defined(__LINUX_CDNS3_TRACE) || defined(TRACE_HEADER_MULTI_READ)
> > +#define __LINUX_CDNS3_TRACE
> > +
> > +#include <linux/types.h>
> > +#include <linux/tracepoint.h>
> > +#include <asm/byteorder.h>
> > +#include <linux/usb/ch9.h>
> > +#include "core.h"
> > +#include "gadget.h"
> > +#include "debug.h"
> > +
> > +#define CDNS3_MSG_MAX  500
> > +
> > +DECLARE_EVENT_CLASS(cdns3_log_doorbell,
> > +       TP_PROTO(const char *ep_name, u32 ep_trbaddr),
> > +       TP_ARGS(ep_name, ep_trbaddr),
> > +       TP_STRUCT__entry(
> > +               __string(name, ep_name)
> > +               __field(u32, ep_trbaddr)
> > +       ),
> > +       TP_fast_assign(
> > +               __assign_str(name, ep_name);
> > +               __entry->ep_trbaddr = ep_trbaddr;
> > +       ),
> > +       TP_printk("//Ding Dong %s, ep_trbaddr %08x", __get_str(name),
> > +                 __entry->ep_trbaddr)
> > +);
> > +
> > +DEFINE_EVENT(cdns3_log_doorbell, cdns3_doorbell_ep0,
> > +       TP_PROTO(const char *ep_name, u32 ep_trbaddr),
> > +       TP_ARGS(ep_name, ep_trbaddr)
> > +);
> > +
> > +DEFINE_EVENT(cdns3_log_doorbell, cdns3_doorbell_epx,
> > +       TP_PROTO(const char *ep_name, u32 ep_trbaddr),
> > +       TP_ARGS(ep_name, ep_trbaddr)
> > +);
> > +
> > +DECLARE_EVENT_CLASS(cdns3_log_usb_irq,
> > +       TP_PROTO(struct cdns3_device *priv_dev, u32 usb_ists),
> > +       TP_ARGS(priv_dev, usb_ists),
> > +       TP_STRUCT__entry(
> > +               __field(enum usb_device_speed, speed)
> > +               __field(u32, usb_ists)
> > +               __dynamic_array(char, str, CDNS3_MSG_MAX)
> > +       ),
> > +       TP_fast_assign(
> > +               __entry->speed = cdns3_get_speed(priv_dev);
> > +               __entry->usb_ists = usb_ists;
> > +       ),
> > +       TP_printk("%s", cdns3_decode_usb_irq(__get_str(str), __entry->speed,
> > +                                            __entry->usb_ists))
> > +);
> > +
> > +DEFINE_EVENT(cdns3_log_usb_irq, cdns3_usb_irq,
> > +       TP_PROTO(struct cdns3_device *priv_dev, u32 usb_ists),
> > +       TP_ARGS(priv_dev, usb_ists)
> > +);
> > +
> > +DECLARE_EVENT_CLASS(cdns3_log_epx_irq,
> > +       TP_PROTO(struct cdns3_device *priv_dev, struct cdns3_endpoint *priv_ep),
> > +       TP_ARGS(priv_dev, priv_ep),
> > +       TP_STRUCT__entry(
> > +               __string(ep_name, priv_ep->name)
> > +               __field(u32, ep_sts)
> > +               __field(u32, ep_traddr)
> > +               __dynamic_array(char, str, CDNS3_MSG_MAX)
> > +       ),
> > +       TP_fast_assign(
> > +               __assign_str(ep_name, priv_ep->name);
> > +               __entry->ep_sts = readl(&priv_dev->regs->ep_sts);
> > +               __entry->ep_traddr = readl(&priv_dev->regs->ep_traddr);
> > +       ),
> > +       TP_printk("%s, ep_traddr: %08x",
> > +                 cdns3_decode_epx_irq(__get_str(str),
> > +                                      __get_str(ep_name),
> > +                                      __entry->ep_sts),
> > +                 __entry->ep_traddr)
> > +);
> > +
> > +DEFINE_EVENT(cdns3_log_epx_irq, cdns3_epx_irq,
> > +       TP_PROTO(struct cdns3_device *priv_dev, struct cdns3_endpoint *priv_ep),
> > +       TP_ARGS(priv_dev, priv_ep)
> > +);
> > +
> > +DECLARE_EVENT_CLASS(cdns3_log_ep0_irq,
> > +       TP_PROTO(struct cdns3_device *priv_dev,  u32 ep_sts),
> > +       TP_ARGS(priv_dev, ep_sts),
> > +       TP_STRUCT__entry(
> > +               __field(int, ep_dir)
> > +               __field(u32, ep_sts)
> > +               __dynamic_array(char, str, CDNS3_MSG_MAX)
> > +       ),
> > +       TP_fast_assign(
> > +               __entry->ep_dir = priv_dev->ep0_data_dir;
> > +               __entry->ep_sts = ep_sts;
> > +       ),
> > +       TP_printk("%s", cdns3_decode_ep0_irq(__get_str(str),
> > +                                            __entry->ep_dir,
> > +                                            __entry->ep_sts))
> > +);
> > +
> > +DEFINE_EVENT(cdns3_log_ep0_irq, cdns3_ep0_irq,
> > +       TP_PROTO(struct cdns3_device *priv_dev, u32 ep_sts),
> > +       TP_ARGS(priv_dev, ep_sts)
> > +);
> > +
> > +DECLARE_EVENT_CLASS(cdns3_log_ctrl,
> > +       TP_PROTO(struct usb_ctrlrequest *ctrl),
> > +       TP_ARGS(ctrl),
> > +       TP_STRUCT__entry(
> > +               __field(u8, bRequestType)
> > +               __field(u8, bRequest)
> > +               __field(u16, wValue)
> > +               __field(u16, wIndex)
> > +               __field(u16, wLength)
> > +               __dynamic_array(char, str, CDNS3_MSG_MAX)
> > +       ),
> > +       TP_fast_assign(
> > +               __entry->bRequestType = ctrl->bRequestType;
> > +               __entry->bRequest = ctrl->bRequest;
> > +               __entry->wValue = le16_to_cpu(ctrl->wValue);
> > +               __entry->wIndex = le16_to_cpu(ctrl->wIndex);
> > +               __entry->wLength = le16_to_cpu(ctrl->wLength);
> > +       ),
> > +       TP_printk("%s", usb_decode_ctrl(__get_str(str), __entry->bRequestType,
> > +                                       __entry->bRequest, __entry->wValue,
> > +                                       __entry->wIndex, __entry->wLength)
> > +       )
> > +);
> > +
> > +DEFINE_EVENT(cdns3_log_ctrl, cdns3_ctrl_req,
> > +       TP_PROTO(struct usb_ctrlrequest *ctrl),
> > +       TP_ARGS(ctrl)
> > +);
> > +
> > +DECLARE_EVENT_CLASS(cdns3_log_request,
> > +       TP_PROTO(struct cdns3_request *req),
> > +       TP_ARGS(req),
> > +       TP_STRUCT__entry(
> > +               __string(name, req->priv_ep->name)
> > +               __field(struct cdns3_request *, req)
> > +               __field(unsigned int, actual)
> > +               __field(unsigned int, length)
> > +               __field(int, status)
> > +               __field(int, zero)
> > +               __field(int, short_not_ok)
> > +               __field(int, no_interrupt)
> > +               __field(int, start_trb)
> > +               __field(int, end_trb)
> > +               __field(struct cdns3_trb *, start_trb_addr)
> > +               __field(int, flags)
> > +       ),
> > +       TP_fast_assign(
> > +               __assign_str(name, req->priv_ep->name);
> > +               __entry->req = req;
> > +               __entry->actual = req->request.actual;
> > +               __entry->length = req->request.length;
> > +               __entry->status = req->request.status;
> > +               __entry->zero = req->request.zero;
> > +               __entry->short_not_ok = req->request.short_not_ok;
> > +               __entry->no_interrupt = req->request.no_interrupt;
> > +               __entry->start_trb = req->start_trb;
> > +               __entry->end_trb = req->end_trb;
> > +               __entry->start_trb_addr = req->trb;
> > +               __entry->flags = req->flags;
> > +       ),
> > +       TP_printk("%s: req: %p, length: %u/%u %s%s%s, status: %d,"
> > +                 " trb: [start:%d, end:%d: virt addr %pa], flags:%x ",
> > +               __get_str(name), __entry->req, __entry->actual, __entry->length,
> > +               __entry->zero ? "zero | " : "",
> > +               __entry->short_not_ok ? "short | " : "",
> > +               __entry->no_interrupt ? "no int" : "",
> > +               __entry->status,
> > +               __entry->start_trb,
> > +               __entry->end_trb,
> > +               __entry->start_trb_addr,
> > +               __entry->flags
> > +       )
> > +);
> > +
> > +DEFINE_EVENT(cdns3_log_request, cdns3_alloc_request,
> > +       TP_PROTO(struct cdns3_request *req),
> > +       TP_ARGS(req)
> > +);
> > +
> > +DEFINE_EVENT(cdns3_log_request, cdns3_free_request,
> > +       TP_PROTO(struct cdns3_request *req),
> > +       TP_ARGS(req)
> > +);
> > +
> > +DEFINE_EVENT(cdns3_log_request, cdns3_ep_queue,
> > +       TP_PROTO(struct cdns3_request *req),
> > +       TP_ARGS(req)
> > +);
> > +
> > +DEFINE_EVENT(cdns3_log_request, cdns3_ep_dequeue,
> > +       TP_PROTO(struct cdns3_request *req),
> > +       TP_ARGS(req)
> > +);
> > +
> > +DEFINE_EVENT(cdns3_log_request, cdns3_gadget_giveback,
> > +       TP_PROTO(struct cdns3_request *req),
> > +       TP_ARGS(req)
> > +);
> > +
> > +DECLARE_EVENT_CLASS(cdns3_log_trb,
> > +       TP_PROTO(struct cdns3_endpoint *priv_ep, struct cdns3_trb *trb),
> > +       TP_ARGS(priv_ep, trb),
> > +       TP_STRUCT__entry(
> > +               __string(name, priv_ep->name)
> > +               __field(struct cdns3_trb *, trb)
> > +               __field(u32, buffer)
> > +               __field(u32, length)
> > +               __field(u32, control)
> > +               __field(u32, type)
> > +       ),
> > +       TP_fast_assign(
> > +               __assign_str(name, priv_ep->name);
> > +               __entry->trb = trb;
> > +               __entry->buffer = trb->buffer;
> > +               __entry->length = trb->length;
> > +               __entry->control = trb->control;
> > +               __entry->type = usb_endpoint_type(priv_ep->endpoint.desc);
> > +       ),
> > +       TP_printk("%s: trb 0x%pa, dma buf: 0x%08x, size: %ld, ctrl: 0x%08x (%s%s%s%s%s%s%s)",
> > +               __get_str(name), __entry->trb, __entry->buffer,
> > +               TRB_LEN(__entry->length), __entry->control,
> > +               __entry->control & TRB_CYCLE ? "C=1, " : "C=0, ",
> > +               __entry->control & TRB_TOGGLE ? "T=1, " : "T=0, ",
> > +               __entry->control & TRB_ISP ? "ISP, " : "",
> > +               __entry->control & TRB_FIFO_MODE ? "FIFO, " : "",
> > +               __entry->control & TRB_CHAIN ? "CHAIN, " : "",
> > +               __entry->control & TRB_IOC ? "IOC, " : "",
> > +               TRB_FIELD_TO_TYPE(__entry->control) == TRB_NORMAL ? "Normal" : "LINK"
> > +       )
> > +);
> > +
> > +DEFINE_EVENT(cdns3_log_trb, cdns3_prepare_trb,
> > +       TP_PROTO(struct cdns3_endpoint *priv_ep, struct cdns3_trb *trb),
> > +       TP_ARGS(priv_ep, trb)
> > +);
> > +
> > +DEFINE_EVENT(cdns3_log_trb, cdns3_complete_trb,
> > +       TP_PROTO(struct cdns3_endpoint *priv_ep, struct cdns3_trb *trb),
> > +       TP_ARGS(priv_ep, trb)
> > +);
> > +
> > +DECLARE_EVENT_CLASS(cdns3_log_ring,
> > +       TP_PROTO(struct cdns3_endpoint *priv_ep),
> > +       TP_ARGS(priv_ep),
> > +       TP_STRUCT__entry(
> > +               __dynamic_array(u8, ring, TRB_RING_SIZE)
> > +               __dynamic_array(u8, priv_ep, sizeof(struct cdns3_endpoint))
> > +               __dynamic_array(char, buffer,
> > +                               (TRBS_PER_SEGMENT * 65) + CDNS3_MSG_MAX)
> > +       ),
> > +       TP_fast_assign(
> > +               memcpy(__get_dynamic_array(priv_ep), priv_ep,
> > +                      sizeof(struct cdns3_endpoint));
> > +               memcpy(__get_dynamic_array(ring), priv_ep->trb_pool,
> > +                      TRB_RING_SIZE);
> > +       ),
> > +
> > +       TP_printk("%s",
> > +                 cdns3_dbg_ring((struct cdns3_endpoint *)__get_str(priv_ep),
> > +                                (struct cdns3_trb *)__get_str(ring),
> > +                                __get_str(buffer)))
> > +);
> > +
> > +DEFINE_EVENT(cdns3_log_ring, cdns3_ring,
> > +       TP_PROTO(struct cdns3_endpoint *priv_ep),
> > +       TP_ARGS(priv_ep)
> > +);
> > +
> > +DECLARE_EVENT_CLASS(cdns3_log_ep,
> > +       TP_PROTO(struct cdns3_endpoint *priv_ep),
> > +       TP_ARGS(priv_ep),
> > +       TP_STRUCT__entry(
> > +               __string(name, priv_ep->name)
> > +               __field(unsigned int, maxpacket)
> > +               __field(unsigned int, maxpacket_limit)
> > +               __field(unsigned int, max_streams)
> > +               __field(unsigned int, maxburst)
> > +               __field(unsigned int, flags)
> > +               __field(unsigned int, dir)
> > +               __field(u8, enqueue)
> > +               __field(u8, dequeue)
> > +       ),
> > +       TP_fast_assign(
> > +               __assign_str(name, priv_ep->name);
> > +               __entry->maxpacket = priv_ep->endpoint.maxpacket;
> > +               __entry->maxpacket_limit = priv_ep->endpoint.maxpacket_limit;
> > +               __entry->max_streams = priv_ep->endpoint.max_streams;
> > +               __entry->maxburst = priv_ep->endpoint.maxburst;
> > +               __entry->flags = priv_ep->flags;
> > +               __entry->dir = priv_ep->dir;
> > +               __entry->enqueue = priv_ep->enqueue;
> > +               __entry->dequeue = priv_ep->dequeue;
> > +       ),
> > +       TP_printk("%s: mps: %d/%d. streams: %d, burst: %d, enq idx: %d, "
> > +                 "deq idx: %d, flags %s%s%s%s%s%s%s%s, dir: %s",
> > +               __get_str(name), __entry->maxpacket,
> > +               __entry->maxpacket_limit, __entry->max_streams,
> > +               __entry->maxburst, __entry->enqueue,
> > +               __entry->dequeue,
> > +               __entry->flags & EP_ENABLED ? "EN | " : "",
> > +               __entry->flags & EP_STALL ? "STALL | " : "",
> > +               __entry->flags & EP_WEDGE ? "WEDGE | " : "",
> > +               __entry->flags & EP_TRANSFER_STARTED ? "STARTED | " : "",
> > +               __entry->flags & EP_UPDATE_EP_TRBADDR ? "UPD TRB | " : "",
> > +               __entry->flags & EP_PENDING_REQUEST ? "REQ PEN | " : "",
> > +               __entry->flags & EP_RING_FULL ? "RING FULL |" : "",
> > +               __entry->flags & EP_CLAIMED ?  "CLAIMED " : "",
> > +               __entry->dir ? "IN" : "OUT"
> > +       )
> > +);
> > +
> > +DEFINE_EVENT(cdns3_log_ep, cdns3_gadget_ep_enable,
> > +       TP_PROTO(struct cdns3_endpoint *priv_ep),
> > +       TP_ARGS(priv_ep)
> > +);
> > +
> > +DEFINE_EVENT(cdns3_log_ep, cdns3_gadget_ep_disable,
> > +       TP_PROTO(struct cdns3_endpoint *priv_ep),
> > +       TP_ARGS(priv_ep)
> > +);
> > +
> > +DECLARE_EVENT_CLASS(cdns3_log_request_handled,
> > +       TP_PROTO(struct cdns3_request *priv_req, int current_index,
> > +                int handled),
> > +       TP_ARGS(priv_req, current_index, handled),
> > +       TP_STRUCT__entry(
> > +               __field(struct cdns3_request *, priv_req)
> > +               __field(unsigned int, dma_position)
> > +               __field(unsigned int, handled)
> > +               __field(unsigned int, dequeue_idx)
> > +               __field(unsigned int, enqueue_idx)
> > +               __field(unsigned int, start_trb)
> > +               __field(unsigned int, end_trb)
> > +       ),
> > +       TP_fast_assign(
> > +               __entry->priv_req = priv_req;
> > +               __entry->dma_position = current_index;
> > +               __entry->handled = handled;
> > +               __entry->dequeue_idx = priv_req->priv_ep->dequeue;
> > +               __entry->enqueue_idx = priv_req->priv_ep->enqueue;
> > +               __entry->start_trb = priv_req->start_trb;
> > +               __entry->end_trb = priv_req->end_trb;
> > +       ),
> > +       TP_printk("Req: %p %s, DMA pos: %d, ep deq: %d, ep enq: %d,"
> > +                 " start trb: %d, end trb: %d",
> > +               __entry->priv_req,
> > +               __entry->handled ? "handled" : "not handled",
> > +               __entry->dma_position, __entry->dequeue_idx,
> > +               __entry->enqueue_idx, __entry->start_trb,
> > +               __entry->end_trb
> > +       )
> > +);
> > +
> > +DEFINE_EVENT(cdns3_log_request_handled, cdns3_request_handled,
> > +       TP_PROTO(struct cdns3_request *priv_req, int current_index,
> > +                int handled),
> > +       TP_ARGS(priv_req, current_index, handled)
> > +);
> > +#endif /* __LINUX_CDNS3_TRACE */
> > +
> > +/* this part must be outside header guard */
> > +
> > +#undef TRACE_INCLUDE_PATH
> > +#define TRACE_INCLUDE_PATH .
> > +
> > +#undef TRACE_INCLUDE_FILE
> > +#define TRACE_INCLUDE_FILE trace
> > +
> > +#include <trace/define_trace.h>
> > --
> > 2.17.1
> >

^ permalink raw reply	[flat|nested] 49+ messages in thread

* RE: [PATCH v2 5/5] usb:cdns3 Add Cadence USB3 DRD Driver
@ 2018-12-27  9:36         ` Pawel Laszczak
  0 siblings, 0 replies; 49+ messages in thread
From: Pawel Laszczak @ 2018-12-27  9:36 UTC (permalink / raw)
  To: Peter Chen
  Cc: devicetree, Greg Kroah-Hartman, mark.rutland, linux-usb,
	hdegoede, Heikki Krogerus, andy.shevchenko, robh+dt, rogerq,
	lkml, Alan Douglas, jbergsagel, nsekhar, nm, Suresh Punnoose,
	peter.chen, Pawel Jez, Rahul Kumar

Hi,

>> > +
>> > +       if (ret)
>> > +               return ret;
>> > +
>> > +       state = readl(&cdns->otg_regs->sts);
>> > +       if (OTGSTS_OTG_NRDY(state) != 0) {
>> > +               dev_err(cdns->dev, "Cadence USB3 OTG device not ready\n");
>> > +               return -ENODEV;
>> > +       }
>> > +
>> > +       ret = cdns3_drd_update_mode(cdns);
>> > +
>>
>> Calling this function, it is timeout for waiting OTGSTS_XHCI_READY at otgsts,
>> do you know possible reasons?  After commenting out this function, my
>> xHCI function
>> works.
>>
>
>Pawel, since OTG compliance (Known as HNP/SRP)  has not been used
>widely, Linux kernel does not
>maintain it from some time ago (maybe 1-2 years). In software design,
>we do not need to consider it from
>hardware point, eg, kinds of OTG timer. For dual-role switch on the
>fly,  through /sys is enough.
>
>Through the debug, we find it needs to wait 1s after setting de-select
>the host or gadget before request
>XHCI at otg_regs->cmd, and enable fast simulate can reduce delay to
>2-3ms. Would you please help
>to check with your hardware team this behavior. With below changes, I
>can get OTGSTS_XHCI_READY at otgsts.
>
>@@ -141,6 +143,7 @@ static int cdns3_drd_switch_host(struct cdns3 *cdns, int on)
>                writel(OTGCMD_HOST_BUS_DROP | OTGCMD_DEV_BUS_DROP |
>                       OTGCMD_DEV_POWER_OFF | OTGCMD_HOST_POWER_OFF,
>                       &cdns->otg_regs->cmd);
>+               usleep_range(3000, 4000);
>        }
>
>        return 0;
>@@ -178,6 +181,7 @@ static int cdns3_drd_switch_gadget(struct cdns3
>*cdns, int on)
>                writel(OTGCMD_HOST_BUS_DROP | OTGCMD_DEV_BUS_DROP |
>                       OTGCMD_DEV_POWER_OFF | OTGCMD_HOST_POWER_OFF,
>                       &cdns->otg_regs->cmd);
>+               usleep_range(3000, 4000);
>        }
>
>@@ -299,6 +306,7 @@ int cdns3_drd_init(struct cdns3 *cdns)
>                cdns->version  = CDNS3_CONTROLLER_V0;
>                cdns->otg_v1_regs = NULL;
>                cdns->otg_regs = regs;
>+               writel(0x1, &cdns->otg_v0_regs->simulate);
>                dev_info(cdns->dev, "DRD version v0 (%08x)\n",
>                         readl(&cdns->otg_v0_regs->version));
>        } else {

I have confirmation from HW team that time that driver should wait after de-selecting mode 
is 2-3ms for simulate mode. It's time when FSM is in DRD_H_WAIT_VBUS_FAIL. 
Driver cannot re-enable the host/device mode before this time has elapsed. 

3 ms is the maximum time. Additionally, you can confirm the current FSM state by reading the
host_otg_state (bit 5:3) or dev_otg_state (2:0)  from OTGSTATE register. 

If bit 0 in simulate register is cleared the time is exactly 1s.

Cheers,
Pawel


^ permalink raw reply	[flat|nested] 49+ messages in thread

* [v2,5/5] usb:cdns3 Add Cadence USB3 DRD Driver
@ 2018-12-27  9:36         ` Pawel Laszczak
  0 siblings, 0 replies; 49+ messages in thread
From: Pawel Laszczak @ 2018-12-27  9:36 UTC (permalink / raw)
  To: Peter Chen
  Cc: devicetree, Greg Kroah-Hartman, mark.rutland, linux-usb,
	hdegoede, Heikki Krogerus, andy.shevchenko, robh+dt, rogerq,
	lkml, Alan Douglas, jbergsagel, nsekhar, nm, Suresh Punnoose,
	peter.chen, Pawel Jez, Rahul Kumar

Hi,

>> > +
>> > +       if (ret)
>> > +               return ret;
>> > +
>> > +       state = readl(&cdns->otg_regs->sts);
>> > +       if (OTGSTS_OTG_NRDY(state) != 0) {
>> > +               dev_err(cdns->dev, "Cadence USB3 OTG device not ready\n");
>> > +               return -ENODEV;
>> > +       }
>> > +
>> > +       ret = cdns3_drd_update_mode(cdns);
>> > +
>>
>> Calling this function, it is timeout for waiting OTGSTS_XHCI_READY at otgsts,
>> do you know possible reasons?  After commenting out this function, my
>> xHCI function
>> works.
>>
>
>Pawel, since OTG compliance (Known as HNP/SRP)  has not been used
>widely, Linux kernel does not
>maintain it from some time ago (maybe 1-2 years). In software design,
>we do not need to consider it from
>hardware point, eg, kinds of OTG timer. For dual-role switch on the
>fly,  through /sys is enough.
>
>Through the debug, we find it needs to wait 1s after setting de-select
>the host or gadget before request
>XHCI at otg_regs->cmd, and enable fast simulate can reduce delay to
>2-3ms. Would you please help
>to check with your hardware team this behavior. With below changes, I
>can get OTGSTS_XHCI_READY at otgsts.
>
>@@ -141,6 +143,7 @@ static int cdns3_drd_switch_host(struct cdns3 *cdns, int on)
>                writel(OTGCMD_HOST_BUS_DROP | OTGCMD_DEV_BUS_DROP |
>                       OTGCMD_DEV_POWER_OFF | OTGCMD_HOST_POWER_OFF,
>                       &cdns->otg_regs->cmd);
>+               usleep_range(3000, 4000);
>        }
>
>        return 0;
>@@ -178,6 +181,7 @@ static int cdns3_drd_switch_gadget(struct cdns3
>*cdns, int on)
>                writel(OTGCMD_HOST_BUS_DROP | OTGCMD_DEV_BUS_DROP |
>                       OTGCMD_DEV_POWER_OFF | OTGCMD_HOST_POWER_OFF,
>                       &cdns->otg_regs->cmd);
>+               usleep_range(3000, 4000);
>        }
>
>@@ -299,6 +306,7 @@ int cdns3_drd_init(struct cdns3 *cdns)
>                cdns->version  = CDNS3_CONTROLLER_V0;
>                cdns->otg_v1_regs = NULL;
>                cdns->otg_regs = regs;
>+               writel(0x1, &cdns->otg_v0_regs->simulate);
>                dev_info(cdns->dev, "DRD version v0 (%08x)\n",
>                         readl(&cdns->otg_v0_regs->version));
>        } else {

I have confirmation from HW team that time that driver should wait after de-selecting mode 
is 2-3ms for simulate mode. It's time when FSM is in DRD_H_WAIT_VBUS_FAIL. 
Driver cannot re-enable the host/device mode before this time has elapsed. 

3 ms is the maximum time. Additionally, you can confirm the current FSM state by reading the
host_otg_state (bit 5:3) or dev_otg_state (2:0)  from OTGSTATE register. 

If bit 0 in simulate register is cleared the time is exactly 1s.

Cheers,
Pawel

^ permalink raw reply	[flat|nested] 49+ messages in thread

* RE: [PATCH v2 5/5] usb:cdns3 Add Cadence USB3 DRD Driver
@ 2018-12-27 13:11       ` Pawel Laszczak
  0 siblings, 0 replies; 49+ messages in thread
From: Pawel Laszczak @ 2018-12-27 13:11 UTC (permalink / raw)
  To: Peter Chen
  Cc: devicetree, Greg Kroah-Hartman, mark.rutland, linux-usb,
	hdegoede, Heikki Krogerus, andy.shevchenko, robh+dt, rogerq,
	lkml, Alan Douglas, jbergsagel, nsekhar, nm, Suresh Punnoose,
	peter.chen, Pawel Jez, Rahul Kumar

HI,

>>
>> The host side of USBSS-DRD controller is compliance
>> with XHCI specification, so it works with
>> standard XHCI linux driver.
>>
>
>After adding my glue layer change (with my phy part) and make one
>change for this code,
>the xHCI can work at my platform. I list the comments from today's
>review and debug.
>The comments are at  Makefile and drd.c.
>
>> +         If you choose to build this driver as module it will
>> +         be dynamically linked and module will be called cdns3-pci.ko
>> +
>> +endif
>> diff --git a/drivers/usb/cdns3/Makefile b/drivers/usb/cdns3/Makefile
>> new file mode 100644
>> index 000000000000..3f63baa24294
>> --- /dev/null
>> +++ b/drivers/usb/cdns3/Makefile
>> @@ -0,0 +1,16 @@
>> +# SPDX-License-Identifier: GPL-2.0
>> +# define_trace.h needs to know how to find our header
>> +CFLAGS_trace.o                         := -I$(src)
>> +
>> +obj-$(CONFIG_USB_CDNS3)                        += cdns3.o
>> +obj-$(CONFIG_USB_CDNS3_PCI_WRAP)       += cdns3-pci.o
>> +
>> +cdns3-y                                        := core.o drd.o trace.o
>> +
>> +ifneq ($(CONFIG_DEBUG_FS),)
>> +       cdns3-y                         += debugfs.o
>> +endif
>> +
>> +cdns3-$(CONFIG_USB_CDNS3_GADGET)       += gadget.o ep0.o
>> +cdns3-$(CONFIG_USB_CDNS3_HOST)         += host.o
>> +cdns3-pci-y                            := cdns3-pci-wrap.o
>
>Why do you want add two lines for pci_wrap? I change this Makefile like below:

I don't need these two lines. I will change it as you suggested. 

>
># SPDX-License-Identifier: GPL-2.0
># define_trace.h needs to know how to find our header
>CFLAGS_trace.o                          := -I$(src)
>
>cdns3-y                                 := core.o drd.o trace.o
>obj-$(CONFIG_USB_CDNS3)                 += cdns3.o
>ifneq ($(CONFIG_DEBUG_FS),)
>        cdns3-y                         += debugfs.o
>endif
>
>cdns3-$(CONFIG_USB_CDNS3_GADGET)        += gadget.o ep0.o
>cdns3-$(CONFIG_USB_CDNS3_HOST)          += host.o
>obj-$(CONFIG_USB_CDNS3_PCI_WRAP)        += cdns3-pci-wrap.o
>obj-$(CONFIG_USB_CDNS3_IMX_WRAP)        += cdns3-imx.o
>
>and below is the diff:
>
>diff --git a/drivers/usb/cdns3/Makefile b/drivers/usb/cdns3/Makefile
>index 3f63baa24294..d1bca2829f57 100644
>--- a/drivers/usb/cdns3/Makefile
>+++ b/drivers/usb/cdns3/Makefile
>@@ -2,15 +2,13 @@
> # define_trace.h needs to know how to find our header
> CFLAGS_trace.o                         := -I$(src)
>
>-obj-$(CONFIG_USB_CDNS3)                        += cdns3.o
>-obj-$(CONFIG_USB_CDNS3_PCI_WRAP)       += cdns3-pci.o
>-
> cdns3-y                                        := core.o drd.o trace.o
>-
>+obj-$(CONFIG_USB_CDNS3)                        += cdns3.o
> ifneq ($(CONFIG_DEBUG_FS),)
>        cdns3-y                         += debugfs.o
> endif
>
> cdns3-$(CONFIG_USB_CDNS3_GADGET)       += gadget.o ep0.o
> cdns3-$(CONFIG_USB_CDNS3_HOST)         += host.o
>-cdns3-pci-y                            := cdns3-pci-wrap.o
>+obj-$(CONFIG_USB_CDNS3_PCI_WRAP)       += cdns3-pci-wrap.o
>+obj-$(CONFIG_USB_CDNS3_IMX_WRAP)       += cdns3-imx.o
>
>
>> diff --git a/drivers/usb/cdns3/drd.c b/drivers/usb/cdns3/drd.c
>> new file mode 100644
>> index 000000000000..b0c32302eb0b
>> --- /dev/null
>> +++ b/drivers/usb/cdns3/drd.c
>> @@ -0,0 +1,350 @@
>> +// SPDX-License-Identifier: GPL-2.0
>> +/*
>> + * Cadence USBSS DRD Driver.
>> + *
>> + * Copyright (C) 2018 Cadence.
>> + *
>> + * Author: Pawel Laszczak <pawell@cadence.com
>> + *
>> + */
>> +#include <linux/kernel.h>
>> +#include <linux/interrupt.h>
>> +#include <linux/delay.h>
>> +#include <linux/usb/otg.h>
>> +
>> +#include "gadget.h"
>> +#include "drd.h"
>> +#include "core.h"
>> +
>> +static int cdns3_drd_switch_gadget(struct cdns3 *cdns, int on);
>> +static int cdns3_drd_switch_host(struct cdns3 *cdns, int on);
>> +
>> +/**
>> + * cdns3_set_mode - change mode of OTG Core
>> + * @cdns: pointer to context structure
>> + * @mode: selected mode from cdns_role
>> + */
>> +void cdns3_set_mode(struct cdns3 *cdns, enum usb_dr_mode mode)
>> +{
>> +       u32 reg;
>> +
>> +       cdns->current_dr_mode = mode;
>> +
>> +       switch (mode) {
>> +       case USB_DR_MODE_PERIPHERAL:
>> +               dev_info(cdns->dev, "Set controller to Gadget mode\n");
>> +               cdns3_drd_switch_gadget(cdns, 1);
>> +               break;
>> +       case USB_DR_MODE_HOST:
>> +               dev_info(cdns->dev, "Set controller to Host mode\n");
>> +               cdns3_drd_switch_host(cdns, 1);
>> +               break;
>> +       case USB_DR_MODE_OTG:
>> +               dev_info(cdns->dev, "Set controller to OTG mode\n");
>> +               if (cdns->version == CDNS3_CONTROLLER_V1) {
>> +                       reg = readl(&cdns->otg_v1_regs->override);
>> +                       reg |= OVERRIDE_IDPULLUP;
>> +                       writel(reg, &cdns->otg_v1_regs->override);
>> +               } else {
>> +                       reg = readl(&cdns->otg_v0_regs->ctrl1);
>> +                       reg |= OVERRIDE_IDPULLUP_V0;
>> +                       writel(reg, &cdns->otg_v0_regs->ctrl1);
>> +               }
>> +
>> +               /*
>> +                * Hardware specification says: "ID_VALUE must be valid within
>> +                * 50ms after idpullup is set to '1" so driver must wait
>> +                * 50ms before reading this pin.
>> +                */
>> +               usleep_range(50000, 60000);
>> +               break;
>> +       default:
>> +               cdns->current_dr_mode = USB_DR_MODE_UNKNOWN;
>> +               dev_err(cdns->dev, "Unsupported mode of operation %d\n", mode);
>> +               return;
>> +       }
>> +}
>
>I suggest adding return value since switch may fail.
>
>
>> +/**
>> + * cdns3_init_otg_mode - initialize drd controller
>> + * @cdns: Pointer to controller context structure
>> + *
>> + * Returns 0 on success otherwise negative errno
>> + */
>> +static void cdns3_init_otg_mode(struct cdns3 *cdns)
>
>You may add return value for this function too like your comment.
Ok, I will do it.
>
>> +{
>> +       cdns3_otg_disable_irq(cdns);
>> +       /* clear all interrupts */
>> +       writel(~0, &cdns->otg_regs->ivect);
>> +
>> +       cdns3_set_mode(cdns, USB_DR_MODE_OTG);
>
>return value
>> +
>> +       if (cdns3_is_host(cdns))
>> +               cdns3_drd_switch_host(cdns, 1);
>
>ditto
>
>> +       else
>> +               cdns3_drd_switch_gadget(cdns, 1);
>
>ditto
>> +
>> +       cdns3_otg_enable_irq(cdns);
>> +}
>> +
>> +/**
>> + * cdns3_drd_update_mode - initialize mode of operation
>> + * @cdns: Pointer to controller context structure
>> + *
>> + * Returns 0 on success otherwise negative errno
>> + */
>> +int cdns3_drd_update_mode(struct cdns3 *cdns)
>> +{
>> +       int ret = 0;
>> +
>> +       if (cdns->desired_dr_mode == cdns->current_dr_mode)
>> +               return ret;
>> +
>> +       cdns3_drd_switch_gadget(cdns, 0);
>
>return value
>
>> +       cdns3_drd_switch_host(cdns, 0);
	In these two line we don't need check return value.
	For disabling mode these functions always return 0. 

>ditto
>> +
>> +       switch (cdns->desired_dr_mode) {
>> +       case USB_DR_MODE_PERIPHERAL:
>> +               cdns3_set_mode(cdns, USB_DR_MODE_PERIPHERAL);
>
>ditto
>
>> +               break;
>> +       case USB_DR_MODE_HOST:
>> +               cdns3_set_mode(cdns, USB_DR_MODE_HOST);
>
>ditto
>> +               break;
>> +       case USB_DR_MODE_OTG:
>> +               cdns3_init_otg_mode(cdns);
>
>ditto
>> +               break;
>> +       default:
>> +               dev_err(cdns->dev, "Unsupported mode of operation %d\n",
>> +                       cdns->dr_mode);
>> +               return -EINVAL;
>> +       }
>> +
>> +       return ret;
>> +}
>> +
>> +/**
>> + * cdns3_drd_irq - interrupt handler for OTG events
>> + *
>> + * @irq: irq number for cdns3 core device
>> + * @data: structure of cdns3
>> + *
>> + * Returns IRQ_HANDLED or IRQ_NONE
>> + */
>> +static irqreturn_t cdns3_drd_irq(int irq, void *data)
>> +{
>> +       irqreturn_t ret = IRQ_NONE;
>> +       struct cdns3 *cdns = data;
>> +       u32 reg;
>> +
>> +       if (cdns->dr_mode != USB_DR_MODE_OTG)
>> +               return ret;
>> +
>> +       reg = readl(&cdns->otg_regs->ivect);
>> +
>> +       if (!reg)
>> +               return ret;
>> +
>> +       if (reg & OTGIEN_ID_CHANGE_INT) {
>> +               dev_dbg(cdns->dev, "OTG IRQ: new ID: %d\n",
>> +                       cdns3_get_id(cdns));
>> +
>> +               queue_work(system_freezable_wq, &cdns->role_switch_wq);
>> +
>> +               ret = IRQ_HANDLED;
>> +       }
>> +
>> +       writel(~0, &cdns->otg_regs->ivect);
>> +       return ret;
>> +}
>> +
>> +int cdns3_drd_init(struct cdns3 *cdns)
>> +{
>> +       void __iomem *regs;
>> +       int ret = 0;
>> +       u32 state;
>> +
>> +       regs = devm_ioremap_resource(cdns->dev, &cdns->otg_res);
>> +       if (IS_ERR(regs))
>> +               return PTR_ERR(regs);
>> +
>> +       /* Detection of DRD version. Controller has been released
>> +        * in two versions. Both are similar, but they have same changes
>> +        * in register maps.
>> +        * The first register in old version is command register and it's read
>> +        * only, so driver should read 0 from it. On the other hand, in v1
>> +        * the first register contains device ID number which is not set to 0.
>> +        * Driver uses this fact to detect the proper version of
>> +        * controller.
>> +        */
>> +       cdns->otg_v0_regs = regs;
>> +       if (!readl(&cdns->otg_v0_regs->cmd)) {
>> +               cdns->version  = CDNS3_CONTROLLER_V0;
>> +               cdns->otg_v1_regs = NULL;
>> +               cdns->otg_regs = regs;
>> +               dev_info(cdns->dev, "DRD version v0 (%08x)\n",
>> +                        readl(&cdns->otg_v0_regs->version));
>> +       } else {
>> +               cdns->otg_v0_regs = NULL;
>> +               cdns->otg_v1_regs = regs;
>> +               cdns->otg_regs = (void *)&cdns->otg_v1_regs->cmd;
>> +               cdns->version  = CDNS3_CONTROLLER_V1;
>> +               dev_info(cdns->dev, "DRD version v1 (ID: %08x, rev: %08x)\n",
>> +                        readl(&cdns->otg_v1_regs->did),
>> +                        readl(&cdns->otg_v1_regs->rid));
>> +       }
>> +
>> +       state = OTGSTS_STRAP(readl(&cdns->otg_regs->sts));
>> +
>> +       /* Update dr_mode according to STRAP configuration. */
>> +       cdns->dr_mode = USB_DR_MODE_OTG;
>> +       if (state == OTGSTS_STRAP_HOST) {
>> +               dev_info(cdns->dev, "Controller strapped to HOST\n");
>> +               cdns->dr_mode = USB_DR_MODE_HOST;
>> +       } else if (state == OTGSTS_STRAP_GADGET) {
>> +               dev_info(cdns->dev, "Controller strapped to PERIPHERAL\n");
>> +               cdns->dr_mode = USB_DR_MODE_PERIPHERAL;
>> +       }
>> +
>> +       cdns->desired_dr_mode = cdns->dr_mode;
>> +       cdns->current_dr_mode = USB_DR_MODE_UNKNOWN;
>> +
>> +       ret = devm_request_threaded_irq(cdns->dev, cdns->irq, cdns3_drd_irq,
>> +                                       NULL, IRQF_SHARED,
>> +                                       dev_name(cdns->dev), cdns);
>> +
>> +       if (ret)
>> +               return ret;
>> +
>> +       state = readl(&cdns->otg_regs->sts);
>> +       if (OTGSTS_OTG_NRDY(state) != 0) {
>> +               dev_err(cdns->dev, "Cadence USB3 OTG device not ready\n");
>> +               return -ENODEV;
>> +       }
>> +
>> +       ret = cdns3_drd_update_mode(cdns);
>> +
>
>Calling this function, it is timeout for waiting OTGSTS_XHCI_READY at otgsts,
>do you know possible reasons?  After commenting out this function, my
>xHCI function
>works.
>
The prorblem is in:
		writel(OTGCMD_HOST_BUS_DROP | OTGCMD_DEV_BUS_DROP |
		       OTGCMD_DEV_POWER_OFF | OTGCMD_HOST_POWER_OFF,
		       &cdns->otg_regs->cmd);

After disabling current mode driver must  wait 1s before turning on  new mode. 

For simulation this time can be shortened to 2-3 ms by mans of OTGSIMULATE register. 

Cheers, 
Pawel
 


^ permalink raw reply	[flat|nested] 49+ messages in thread

* [v2,5/5] usb:cdns3 Add Cadence USB3 DRD Driver
@ 2018-12-27 13:11       ` Pawel Laszczak
  0 siblings, 0 replies; 49+ messages in thread
From: Pawel Laszczak @ 2018-12-27 13:11 UTC (permalink / raw)
  To: Peter Chen
  Cc: devicetree, Greg Kroah-Hartman, mark.rutland, linux-usb,
	hdegoede, Heikki Krogerus, andy.shevchenko, robh+dt, rogerq,
	lkml, Alan Douglas, jbergsagel, nsekhar, nm, Suresh Punnoose,
	peter.chen, Pawel Jez, Rahul Kumar

HI,

>>
>> The host side of USBSS-DRD controller is compliance
>> with XHCI specification, so it works with
>> standard XHCI linux driver.
>>
>
>After adding my glue layer change (with my phy part) and make one
>change for this code,
>the xHCI can work at my platform. I list the comments from today's
>review and debug.
>The comments are at  Makefile and drd.c.
>
>> +         If you choose to build this driver as module it will
>> +         be dynamically linked and module will be called cdns3-pci.ko
>> +
>> +endif
>> diff --git a/drivers/usb/cdns3/Makefile b/drivers/usb/cdns3/Makefile
>> new file mode 100644
>> index 000000000000..3f63baa24294
>> --- /dev/null
>> +++ b/drivers/usb/cdns3/Makefile
>> @@ -0,0 +1,16 @@
>> +# SPDX-License-Identifier: GPL-2.0
>> +# define_trace.h needs to know how to find our header
>> +CFLAGS_trace.o                         := -I$(src)
>> +
>> +obj-$(CONFIG_USB_CDNS3)                        += cdns3.o
>> +obj-$(CONFIG_USB_CDNS3_PCI_WRAP)       += cdns3-pci.o
>> +
>> +cdns3-y                                        := core.o drd.o trace.o
>> +
>> +ifneq ($(CONFIG_DEBUG_FS),)
>> +       cdns3-y                         += debugfs.o
>> +endif
>> +
>> +cdns3-$(CONFIG_USB_CDNS3_GADGET)       += gadget.o ep0.o
>> +cdns3-$(CONFIG_USB_CDNS3_HOST)         += host.o
>> +cdns3-pci-y                            := cdns3-pci-wrap.o
>
>Why do you want add two lines for pci_wrap? I change this Makefile like below:

I don't need these two lines. I will change it as you suggested. 

>
># SPDX-License-Identifier: GPL-2.0
># define_trace.h needs to know how to find our header
>CFLAGS_trace.o                          := -I$(src)
>
>cdns3-y                                 := core.o drd.o trace.o
>obj-$(CONFIG_USB_CDNS3)                 += cdns3.o
>ifneq ($(CONFIG_DEBUG_FS),)
>        cdns3-y                         += debugfs.o
>endif
>
>cdns3-$(CONFIG_USB_CDNS3_GADGET)        += gadget.o ep0.o
>cdns3-$(CONFIG_USB_CDNS3_HOST)          += host.o
>obj-$(CONFIG_USB_CDNS3_PCI_WRAP)        += cdns3-pci-wrap.o
>obj-$(CONFIG_USB_CDNS3_IMX_WRAP)        += cdns3-imx.o
>
>and below is the diff:
>
>diff --git a/drivers/usb/cdns3/Makefile b/drivers/usb/cdns3/Makefile
>index 3f63baa24294..d1bca2829f57 100644
>--- a/drivers/usb/cdns3/Makefile
>+++ b/drivers/usb/cdns3/Makefile
>@@ -2,15 +2,13 @@
> # define_trace.h needs to know how to find our header
> CFLAGS_trace.o                         := -I$(src)
>
>-obj-$(CONFIG_USB_CDNS3)                        += cdns3.o
>-obj-$(CONFIG_USB_CDNS3_PCI_WRAP)       += cdns3-pci.o
>-
> cdns3-y                                        := core.o drd.o trace.o
>-
>+obj-$(CONFIG_USB_CDNS3)                        += cdns3.o
> ifneq ($(CONFIG_DEBUG_FS),)
>        cdns3-y                         += debugfs.o
> endif
>
> cdns3-$(CONFIG_USB_CDNS3_GADGET)       += gadget.o ep0.o
> cdns3-$(CONFIG_USB_CDNS3_HOST)         += host.o
>-cdns3-pci-y                            := cdns3-pci-wrap.o
>+obj-$(CONFIG_USB_CDNS3_PCI_WRAP)       += cdns3-pci-wrap.o
>+obj-$(CONFIG_USB_CDNS3_IMX_WRAP)       += cdns3-imx.o
>
>
>> diff --git a/drivers/usb/cdns3/drd.c b/drivers/usb/cdns3/drd.c
>> new file mode 100644
>> index 000000000000..b0c32302eb0b
>> --- /dev/null
>> +++ b/drivers/usb/cdns3/drd.c
>> @@ -0,0 +1,350 @@
>> +// SPDX-License-Identifier: GPL-2.0
>> +/*
>> + * Cadence USBSS DRD Driver.
>> + *
>> + * Copyright (C) 2018 Cadence.
>> + *
>> + * Author: Pawel Laszczak <pawell@cadence.com
>> + *
>> + */
>> +#include <linux/kernel.h>
>> +#include <linux/interrupt.h>
>> +#include <linux/delay.h>
>> +#include <linux/usb/otg.h>
>> +
>> +#include "gadget.h"
>> +#include "drd.h"
>> +#include "core.h"
>> +
>> +static int cdns3_drd_switch_gadget(struct cdns3 *cdns, int on);
>> +static int cdns3_drd_switch_host(struct cdns3 *cdns, int on);
>> +
>> +/**
>> + * cdns3_set_mode - change mode of OTG Core
>> + * @cdns: pointer to context structure
>> + * @mode: selected mode from cdns_role
>> + */
>> +void cdns3_set_mode(struct cdns3 *cdns, enum usb_dr_mode mode)
>> +{
>> +       u32 reg;
>> +
>> +       cdns->current_dr_mode = mode;
>> +
>> +       switch (mode) {
>> +       case USB_DR_MODE_PERIPHERAL:
>> +               dev_info(cdns->dev, "Set controller to Gadget mode\n");
>> +               cdns3_drd_switch_gadget(cdns, 1);
>> +               break;
>> +       case USB_DR_MODE_HOST:
>> +               dev_info(cdns->dev, "Set controller to Host mode\n");
>> +               cdns3_drd_switch_host(cdns, 1);
>> +               break;
>> +       case USB_DR_MODE_OTG:
>> +               dev_info(cdns->dev, "Set controller to OTG mode\n");
>> +               if (cdns->version == CDNS3_CONTROLLER_V1) {
>> +                       reg = readl(&cdns->otg_v1_regs->override);
>> +                       reg |= OVERRIDE_IDPULLUP;
>> +                       writel(reg, &cdns->otg_v1_regs->override);
>> +               } else {
>> +                       reg = readl(&cdns->otg_v0_regs->ctrl1);
>> +                       reg |= OVERRIDE_IDPULLUP_V0;
>> +                       writel(reg, &cdns->otg_v0_regs->ctrl1);
>> +               }
>> +
>> +               /*
>> +                * Hardware specification says: "ID_VALUE must be valid within
>> +                * 50ms after idpullup is set to '1" so driver must wait
>> +                * 50ms before reading this pin.
>> +                */
>> +               usleep_range(50000, 60000);
>> +               break;
>> +       default:
>> +               cdns->current_dr_mode = USB_DR_MODE_UNKNOWN;
>> +               dev_err(cdns->dev, "Unsupported mode of operation %d\n", mode);
>> +               return;
>> +       }
>> +}
>
>I suggest adding return value since switch may fail.
>
>
>> +/**
>> + * cdns3_init_otg_mode - initialize drd controller
>> + * @cdns: Pointer to controller context structure
>> + *
>> + * Returns 0 on success otherwise negative errno
>> + */
>> +static void cdns3_init_otg_mode(struct cdns3 *cdns)
>
>You may add return value for this function too like your comment.
Ok, I will do it.
>
>> +{
>> +       cdns3_otg_disable_irq(cdns);
>> +       /* clear all interrupts */
>> +       writel(~0, &cdns->otg_regs->ivect);
>> +
>> +       cdns3_set_mode(cdns, USB_DR_MODE_OTG);
>
>return value
>> +
>> +       if (cdns3_is_host(cdns))
>> +               cdns3_drd_switch_host(cdns, 1);
>
>ditto
>
>> +       else
>> +               cdns3_drd_switch_gadget(cdns, 1);
>
>ditto
>> +
>> +       cdns3_otg_enable_irq(cdns);
>> +}
>> +
>> +/**
>> + * cdns3_drd_update_mode - initialize mode of operation
>> + * @cdns: Pointer to controller context structure
>> + *
>> + * Returns 0 on success otherwise negative errno
>> + */
>> +int cdns3_drd_update_mode(struct cdns3 *cdns)
>> +{
>> +       int ret = 0;
>> +
>> +       if (cdns->desired_dr_mode == cdns->current_dr_mode)
>> +               return ret;
>> +
>> +       cdns3_drd_switch_gadget(cdns, 0);
>
>return value
>
>> +       cdns3_drd_switch_host(cdns, 0);
	In these two line we don't need check return value.
	For disabling mode these functions always return 0. 

>ditto
>> +
>> +       switch (cdns->desired_dr_mode) {
>> +       case USB_DR_MODE_PERIPHERAL:
>> +               cdns3_set_mode(cdns, USB_DR_MODE_PERIPHERAL);
>
>ditto
>
>> +               break;
>> +       case USB_DR_MODE_HOST:
>> +               cdns3_set_mode(cdns, USB_DR_MODE_HOST);
>
>ditto
>> +               break;
>> +       case USB_DR_MODE_OTG:
>> +               cdns3_init_otg_mode(cdns);
>
>ditto
>> +               break;
>> +       default:
>> +               dev_err(cdns->dev, "Unsupported mode of operation %d\n",
>> +                       cdns->dr_mode);
>> +               return -EINVAL;
>> +       }
>> +
>> +       return ret;
>> +}
>> +
>> +/**
>> + * cdns3_drd_irq - interrupt handler for OTG events
>> + *
>> + * @irq: irq number for cdns3 core device
>> + * @data: structure of cdns3
>> + *
>> + * Returns IRQ_HANDLED or IRQ_NONE
>> + */
>> +static irqreturn_t cdns3_drd_irq(int irq, void *data)
>> +{
>> +       irqreturn_t ret = IRQ_NONE;
>> +       struct cdns3 *cdns = data;
>> +       u32 reg;
>> +
>> +       if (cdns->dr_mode != USB_DR_MODE_OTG)
>> +               return ret;
>> +
>> +       reg = readl(&cdns->otg_regs->ivect);
>> +
>> +       if (!reg)
>> +               return ret;
>> +
>> +       if (reg & OTGIEN_ID_CHANGE_INT) {
>> +               dev_dbg(cdns->dev, "OTG IRQ: new ID: %d\n",
>> +                       cdns3_get_id(cdns));
>> +
>> +               queue_work(system_freezable_wq, &cdns->role_switch_wq);
>> +
>> +               ret = IRQ_HANDLED;
>> +       }
>> +
>> +       writel(~0, &cdns->otg_regs->ivect);
>> +       return ret;
>> +}
>> +
>> +int cdns3_drd_init(struct cdns3 *cdns)
>> +{
>> +       void __iomem *regs;
>> +       int ret = 0;
>> +       u32 state;
>> +
>> +       regs = devm_ioremap_resource(cdns->dev, &cdns->otg_res);
>> +       if (IS_ERR(regs))
>> +               return PTR_ERR(regs);
>> +
>> +       /* Detection of DRD version. Controller has been released
>> +        * in two versions. Both are similar, but they have same changes
>> +        * in register maps.
>> +        * The first register in old version is command register and it's read
>> +        * only, so driver should read 0 from it. On the other hand, in v1
>> +        * the first register contains device ID number which is not set to 0.
>> +        * Driver uses this fact to detect the proper version of
>> +        * controller.
>> +        */
>> +       cdns->otg_v0_regs = regs;
>> +       if (!readl(&cdns->otg_v0_regs->cmd)) {
>> +               cdns->version  = CDNS3_CONTROLLER_V0;
>> +               cdns->otg_v1_regs = NULL;
>> +               cdns->otg_regs = regs;
>> +               dev_info(cdns->dev, "DRD version v0 (%08x)\n",
>> +                        readl(&cdns->otg_v0_regs->version));
>> +       } else {
>> +               cdns->otg_v0_regs = NULL;
>> +               cdns->otg_v1_regs = regs;
>> +               cdns->otg_regs = (void *)&cdns->otg_v1_regs->cmd;
>> +               cdns->version  = CDNS3_CONTROLLER_V1;
>> +               dev_info(cdns->dev, "DRD version v1 (ID: %08x, rev: %08x)\n",
>> +                        readl(&cdns->otg_v1_regs->did),
>> +                        readl(&cdns->otg_v1_regs->rid));
>> +       }
>> +
>> +       state = OTGSTS_STRAP(readl(&cdns->otg_regs->sts));
>> +
>> +       /* Update dr_mode according to STRAP configuration. */
>> +       cdns->dr_mode = USB_DR_MODE_OTG;
>> +       if (state == OTGSTS_STRAP_HOST) {
>> +               dev_info(cdns->dev, "Controller strapped to HOST\n");
>> +               cdns->dr_mode = USB_DR_MODE_HOST;
>> +       } else if (state == OTGSTS_STRAP_GADGET) {
>> +               dev_info(cdns->dev, "Controller strapped to PERIPHERAL\n");
>> +               cdns->dr_mode = USB_DR_MODE_PERIPHERAL;
>> +       }
>> +
>> +       cdns->desired_dr_mode = cdns->dr_mode;
>> +       cdns->current_dr_mode = USB_DR_MODE_UNKNOWN;
>> +
>> +       ret = devm_request_threaded_irq(cdns->dev, cdns->irq, cdns3_drd_irq,
>> +                                       NULL, IRQF_SHARED,
>> +                                       dev_name(cdns->dev), cdns);
>> +
>> +       if (ret)
>> +               return ret;
>> +
>> +       state = readl(&cdns->otg_regs->sts);
>> +       if (OTGSTS_OTG_NRDY(state) != 0) {
>> +               dev_err(cdns->dev, "Cadence USB3 OTG device not ready\n");
>> +               return -ENODEV;
>> +       }
>> +
>> +       ret = cdns3_drd_update_mode(cdns);
>> +
>
>Calling this function, it is timeout for waiting OTGSTS_XHCI_READY at otgsts,
>do you know possible reasons?  After commenting out this function, my
>xHCI function
>works.
>
The prorblem is in:
		writel(OTGCMD_HOST_BUS_DROP | OTGCMD_DEV_BUS_DROP |
		       OTGCMD_DEV_POWER_OFF | OTGCMD_HOST_POWER_OFF,
		       &cdns->otg_regs->cmd);

After disabling current mode driver must  wait 1s before turning on  new mode. 

For simulation this time can be shortened to 2-3 ms by mans of OTGSIMULATE register. 

Cheers, 
Pawel

^ permalink raw reply	[flat|nested] 49+ messages in thread

* RE: [PATCH v2 5/5] usb:cdns3 Add Cadence USB3 DRD Driver
@ 2018-12-28  1:31           ` Peter Chen
  0 siblings, 0 replies; 49+ messages in thread
From: Peter Chen @ 2018-12-28  1:31 UTC (permalink / raw)
  To: Pawel Laszczak, Peter Chen
  Cc: devicetree, Greg Kroah-Hartman, mark.rutland, linux-usb,
	hdegoede, Heikki Krogerus, andy.shevchenko, robh+dt, rogerq,
	lkml, Alan Douglas, jbergsagel, nsekhar, nm, Suresh Punnoose,
	Pawel Jez, Rahul Kumar

 
> >
> >@@ -299,6 +306,7 @@ int cdns3_drd_init(struct cdns3 *cdns)
> >                cdns->version  = CDNS3_CONTROLLER_V0;
> >                cdns->otg_v1_regs = NULL;
> >                cdns->otg_regs = regs;
> >+               writel(0x1, &cdns->otg_v0_regs->simulate);
> >                dev_info(cdns->dev, "DRD version v0 (%08x)\n",
> >                         readl(&cdns->otg_v0_regs->version));
> >        } else {
> 
> I have confirmation from HW team that time that driver should wait after de-
> selecting mode is 2-3ms for simulate mode. It's time when FSM is in
> DRD_H_WAIT_VBUS_FAIL.
> Driver cannot re-enable the host/device mode before this time has elapsed.
> 
> 3 ms is the maximum time. Additionally, you can confirm the current FSM state by
> reading the host_otg_state (bit 5:3) or dev_otg_state (2:0)  from OTGSTATE
> register.
> 
> If bit 0 in simulate register is cleared the time is exactly 1s.
> 

Thanks, Pawel.

Would you please add below changes in your next revision?
- Set bit 0 in simulate register
- timeout logic for waiting host_otg_state or dev_otg_state at OTGSTATE
when switch to host or device.

Peter


^ permalink raw reply	[flat|nested] 49+ messages in thread

* [v2,5/5] usb:cdns3 Add Cadence USB3 DRD Driver
@ 2018-12-28  1:31           ` Peter Chen
  0 siblings, 0 replies; 49+ messages in thread
From: Peter Chen @ 2018-12-28  1:31 UTC (permalink / raw)
  To: Pawel Laszczak, Peter Chen
  Cc: devicetree, Greg Kroah-Hartman, mark.rutland, linux-usb,
	hdegoede, Heikki Krogerus, andy.shevchenko, robh+dt, rogerq,
	lkml, Alan Douglas, jbergsagel, nsekhar, nm, Suresh Punnoose,
	Pawel Jez, Rahul Kumar

> >
> >@@ -299,6 +306,7 @@ int cdns3_drd_init(struct cdns3 *cdns)
> >                cdns->version  = CDNS3_CONTROLLER_V0;
> >                cdns->otg_v1_regs = NULL;
> >                cdns->otg_regs = regs;
> >+               writel(0x1, &cdns->otg_v0_regs->simulate);
> >                dev_info(cdns->dev, "DRD version v0 (%08x)\n",
> >                         readl(&cdns->otg_v0_regs->version));
> >        } else {
> 
> I have confirmation from HW team that time that driver should wait after de-
> selecting mode is 2-3ms for simulate mode. It's time when FSM is in
> DRD_H_WAIT_VBUS_FAIL.
> Driver cannot re-enable the host/device mode before this time has elapsed.
> 
> 3 ms is the maximum time. Additionally, you can confirm the current FSM state by
> reading the host_otg_state (bit 5:3) or dev_otg_state (2:0)  from OTGSTATE
> register.
> 
> If bit 0 in simulate register is cleared the time is exactly 1s.
> 

Thanks, Pawel.

Would you please add below changes in your next revision?
- Set bit 0 in simulate register
- timeout logic for waiting host_otg_state or dev_otg_state at OTGSTATE
when switch to host or device.

Peter

^ permalink raw reply	[flat|nested] 49+ messages in thread

* Re: [PATCH v2 5/5] usb:cdns3 Add Cadence USB3 DRD Driver
  2018-12-23 15:13   ` [PATCH v2 5/5] " Pawel Laszczak
  (?)
@ 2018-12-29  7:41     ` Chunfeng Yun
  -1 siblings, 0 replies; 49+ messages in thread
From: Chunfeng Yun @ 2018-12-29  7:41 UTC (permalink / raw)
  To: Pawel Laszczak
  Cc: devicetree, gregkh, mark.rutland, linux-usb, hdegoede,
	heikki.krogerus, andy.shevchenko, robh+dt, rogerq, linux-kernel,
	adouglas, jbergsagel, nsekhar, nm, sureshp, peter.chen, pjez,
	kurahul

hi,
On Sun, 2018-12-23 at 15:13 +0000, Pawel Laszczak wrote:
> This patch introduce new Cadence USBSS DRD driver
> to linux kernel.

<...>

> diff --git a/drivers/usb/cdns3/cdns3-pci-wrap.c b/drivers/usb/cdns3/cdns3-pci-wrap.c
> new file mode 100644
> index 000000000000..e93179c45ece
> --- /dev/null
> +++ b/drivers/usb/cdns3/cdns3-pci-wrap.c
> @@ -0,0 +1,157 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Cadence USBSS PCI Glue driver
> + *
> + * Copyright (C) 2018 Cadence.
> + *
> + * Author: Pawel Laszczak <pawell@cadence.com>
> + */
> +
> +#include <linux/kernel.h>
> +#include <linux/module.h>
> +#include <linux/pci.h>
> +#include <linux/platform_device.h>
> +#include <linux/dma-mapping.h>
> +#include <linux/slab.h>
> +
> +struct cdns3_wrap {
> +	struct platform_device *plat_dev;
> +	struct pci_dev *hg_dev;
> +	struct resource dev_res[4];
> +};
> +
> +struct cdns3_wrap wrap;
> +
> +#define RES_IRQ_ID		0
> +#define RES_HOST_ID		1
> +#define RES_DEV_ID		2
> +#define RES_DRD_ID		3
> +
> +#define PCI_BAR_HOST		0
> +#define PCI_BAR_DEV		2
> +#define PCI_BAR_OTG		4
> +
> +#define PCI_DEV_FN_HOST_DEVICE	0
> +#define PCI_DEV_FN_OTG		1
> +
> +#define PCI_DRIVER_NAME		"cdns3-pci-usbss"
> +#define PLAT_DRIVER_NAME	"cdns-usb3"
> +
> +#define CDNS_VENDOR_ID 0x17cd
> +#define CDNS_DEVICE_ID 0x0100
> +
> +/**
> + * cdns3_pci_probe - Probe function for Cadence USB wrapper driver
> + * @pdev: platform device object
> + * @id: pci device id
> + *
> + * Returns 0 on success otherwise negative errno
> + */
> +static int cdns3_pci_probe(struct pci_dev *pdev,
> +			   const struct pci_device_id *id)
> +{
> +	struct platform_device_info plat_info;
> +	struct cdns3_wrap *wrap;
> +	struct resource *res;
> +	int err;
> +
> +	/*
> +	 * for GADGET/HOST PCI (devfn) function number is 0,
> +	 * for OTG PCI (devfn) function number is 1
> +	 */
> +	if (!id || pdev->devfn != PCI_DEV_FN_HOST_DEVICE)
> +		return -EINVAL;
> +
> +	err = pcim_enable_device(pdev);
> +	if (err) {
> +		dev_err(&pdev->dev, "Enabling PCI device has failed %d\n", err);
> +		return err;
> +	}
> +
> +	pci_set_master(pdev);
> +	wrap = devm_kzalloc(&pdev->dev, sizeof(*wrap), GFP_KERNEL);
> +	if (!wrap) {
> +		dev_err(&pdev->dev, "Failed to allocate memory\n");
> +		return -ENOMEM;
> +	}
> +
> +	/* function 0: host(BAR_0) + device(BAR_1) + otg(BAR_2)). */
> +	memset(wrap->dev_res, 0x00,
> +	       sizeof(struct resource) * ARRAY_SIZE(wrap->dev_res));
> +	dev_dbg(&pdev->dev, "Initialize Device resources\n");
> +	res = wrap->dev_res;
> +
> +	res[RES_DEV_ID].start = pci_resource_start(pdev, PCI_BAR_DEV);
> +	res[RES_DEV_ID].end =   pci_resource_end(pdev, PCI_BAR_DEV);
> +	res[RES_DEV_ID].name = "cdns3-dev-regs";
> +	res[RES_DEV_ID].flags = IORESOURCE_MEM;
> +	dev_dbg(&pdev->dev, "USBSS-DEV physical base addr: %pa\n",
> +		&res[RES_DEV_ID].start);
> +
> +	res[RES_HOST_ID].start = pci_resource_start(pdev, PCI_BAR_HOST);
> +	res[RES_HOST_ID].end = pci_resource_end(pdev, PCI_BAR_HOST);
> +	res[RES_HOST_ID].name = "cdns3-xhci-regs";
> +	res[RES_HOST_ID].flags = IORESOURCE_MEM;
> +	dev_dbg(&pdev->dev, "USBSS-XHCI physical base addr: %pa\n",
> +		&res[RES_HOST_ID].start);
> +
> +	res[RES_DRD_ID].start = pci_resource_start(pdev, PCI_BAR_OTG);
> +	res[RES_DRD_ID].end =   pci_resource_end(pdev, PCI_BAR_OTG);
> +	res[RES_DRD_ID].name = "cdns3-otg";
> +	res[RES_DRD_ID].flags = IORESOURCE_MEM;
> +	dev_dbg(&pdev->dev, "USBSS-DRD physical base addr: %pa\n",
> +		&res[RES_DRD_ID].start);
> +
> +	/* Interrupt common for both device and XHCI */
> +	wrap->dev_res[RES_IRQ_ID].start = pdev->irq;
> +	wrap->dev_res[RES_IRQ_ID].name = "cdns3-irq";
> +	wrap->dev_res[RES_IRQ_ID].flags = IORESOURCE_IRQ;
> +
> +	/* set up platform device info */
> +	memset(&plat_info, 0, sizeof(plat_info));
> +	plat_info.parent = &pdev->dev;
> +	plat_info.fwnode = pdev->dev.fwnode;
> +	plat_info.name = PLAT_DRIVER_NAME;
> +	plat_info.id = pdev->devfn;
> +	plat_info.res = wrap->dev_res;
> +	plat_info.num_res = ARRAY_SIZE(wrap->dev_res);
> +	plat_info.dma_mask = pdev->dma_mask;
> +
> +	/* register platform device */
> +	wrap->plat_dev = platform_device_register_full(&plat_info);
> +	if (IS_ERR(wrap->plat_dev)) {
> +		err = PTR_ERR(wrap->plat_dev);
> +		return err;
> +	}
return PTR_ERR(wrap->plat_dev);

> +
> +	pci_set_drvdata(pdev, wrap);
> +
> +	return err;
> +}
> +
> +void cdns3_pci_remove(struct pci_dev *pdev)
> +{
> +	struct cdns3_wrap *wrap = (struct cdns3_wrap *)pci_get_drvdata(pdev);
> +
> +	platform_device_unregister(wrap->plat_dev);
> +}
> +
> +static const struct pci_device_id cdns3_pci_ids[] = {
> +	{ PCI_DEVICE(CDNS_VENDOR_ID, CDNS_DEVICE_ID), },
> +	{ 0, }
> +};
> +
> +static struct pci_driver cdns3_pci_driver = {
> +	.name = PCI_DRIVER_NAME,
> +	.id_table = cdns3_pci_ids,
> +	.probe = cdns3_pci_probe,
> +	.remove = cdns3_pci_remove,
> +};
> +
> +module_pci_driver(cdns3_pci_driver);
> +MODULE_DEVICE_TABLE(pci, cdns3_pci_ids);
> +
> +MODULE_AUTHOR("Pawel Laszczak <pawell@cadence.com>");
> +MODULE_LICENSE("GPL v2");
> +MODULE_DESCRIPTION("Cadence USBSS PCI wrapperr");
> +
> diff --git a/drivers/usb/cdns3/core.c b/drivers/usb/cdns3/core.c
> new file mode 100644
> index 000000000000..d274586aca36
> --- /dev/null
> +++ b/drivers/usb/cdns3/core.c
> @@ -0,0 +1,406 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Cadence USBSS DRD Driver.
> + *
> + * Copyright (C) 2018 Cadence.
> + * Copyright (C) 2017-2018 NXP
> + *
> + * Author: Peter Chen <peter.chen@nxp.com>
> + *         Pawel Laszczak <pawell@cadence.com>
> + */
> +
> +#include <linux/module.h>
> +#include <linux/kernel.h>
> +#include <linux/platform_device.h>
> +#include <linux/interrupt.h>
> +#include <linux/io.h>
> +#include <linux/pm_runtime.h>
> +
> +#include "gadget.h"
> +#include "core.h"
> +#include "host-export.h"
> +#include "gadget-export.h"
> +#include "drd.h"
> +#include "debug.h"
> +
> +static inline
> +struct cdns3_role_driver *cdns3_get_current_role_driver(struct cdns3 *cdns)
> +{
> +	WARN_ON(cdns->role >= CDNS3_ROLE_END || !cdns->roles[cdns->role]);
> +	return cdns->roles[cdns->role];
> +}
> +
> +static inline int cdns3_role_start(struct cdns3 *cdns, enum cdns3_roles role)
remove inline
> +{
> +	int ret;
> +
> +	if (WARN_ON(role >= CDNS3_ROLE_END))
> +		return 0;
> +
> +	if (!cdns->roles[role])
> +		return -ENXIO;
> +
> +	if (cdns->roles[role]->state == CDNS3_ROLE_STATE_ACTIVE)
> +		return 0;
> +
> +	mutex_lock(&cdns->mutex);
> +	cdns->role = role;
> +	ret = cdns->roles[role]->start(cdns);
> +	if (!ret)
> +		cdns->roles[role]->state = CDNS3_ROLE_STATE_ACTIVE;
> +	mutex_unlock(&cdns->mutex);
> +	return ret;
> +}
> +
> +void cdns3_role_stop(struct cdns3 *cdns)
> +{
> +	enum cdns3_roles role = cdns->role;
> +
> +	if (role >= CDNS3_ROLE_END) {
> +		WARN_ON(role > CDNS3_ROLE_END);
> +		return;
> +	}
> +
> +	if (cdns->roles[role]->state == CDNS3_ROLE_STATE_INACTIVE)
> +		return;
> +
> +	mutex_lock(&cdns->mutex);
> +	cdns->roles[role]->stop(cdns);
> +	cdns->roles[role]->state = CDNS3_ROLE_STATE_INACTIVE;
> +	mutex_unlock(&cdns->mutex);
> +}
> +
> +/*
> + * cdns->role gets from cdns3_get_initial_role, and this API tells role at the
> + * runtime.
> + * If both roles are supported, the role is selected based on vbus/id.
> + * It could be read from OTG register or external connector.
> + * If only single role is supported, only one role structure
> + * is allocated, cdns->roles[CDNS3_ROLE_HOST] or cdns->roles[CDNS3_ROLE_GADGET].
> + */
> +static enum cdns3_roles cdns3_get_initial_role(struct cdns3 *cdns)
> +{
> +	if (cdns->roles[CDNS3_ROLE_HOST] && cdns->roles[CDNS3_ROLE_GADGET]) {
> +		if (cdns3_is_host(cdns))
> +			return CDNS3_ROLE_HOST;
> +		if (cdns3_is_device(cdns))
> +			return CDNS3_ROLE_GADGET;
> +	}
> +	return cdns->roles[CDNS3_ROLE_HOST]
> +		? CDNS3_ROLE_HOST
> +		: CDNS3_ROLE_GADGET;
> +}
> +
> +static void cdns3_exit_roles(struct cdns3 *cdns)
> +{
> +	cdns3_role_stop(cdns);
> +	cdns3_drd_exit(cdns);
> +}
> +
> +/**
> + * cdns3_core_init_role - initialize role of operation
> + * @cdns: Pointer to cdns3 structure
> + *
> + * Returns 0 on success otherwise negative errno
> + */
> +static int cdns3_core_init_role(struct cdns3 *cdns)
> +{
> +	struct device *dev = cdns->dev;
> +	enum usb_dr_mode best_dr_mode;
> +	enum usb_dr_mode dr_mode;
> +	int ret = 0;
> +
> +	dr_mode = usb_get_dr_mode(dev);
> +	cdns->role = CDNS3_ROLE_END;
> +
> +	/*
> +	 * If driver can't read mode by means of usb_get_dr_mdoe function then
> +	 * chooses mode according with Kernel configuration. This setting
> +	 * can be restricted later depending on strap pin configuration.
> +	 */
> +	if (dr_mode == USB_DR_MODE_UNKNOWN) {
> +		if (IS_ENABLED(CONFIG_USB_CDNS3_HOST) &&
> +		    IS_ENABLED(CONFIG_USB_CDNS3_GADGET))
> +			dr_mode = USB_DR_MODE_OTG;
> +		else if (IS_ENABLED(CONFIG_USB_CDNS3_HOST))
> +			dr_mode = USB_DR_MODE_HOST;
> +		else if (IS_ENABLED(CONFIG_USB_CDNS3_GADGET))
> +			dr_mode = USB_DR_MODE_PERIPHERAL;
> +	}
> +
> +	best_dr_mode = USB_DR_MODE_OTG;
> +
> +	if (dr_mode == USB_DR_MODE_OTG) {
> +		best_dr_mode = cdns->dr_mode;
> +	} else if (cdns->dr_mode == USB_DR_MODE_OTG) {
> +		best_dr_mode = dr_mode;
> +	} else if (cdns->dr_mode != dr_mode) {
> +		dev_err(dev, "Incorrect DRD configuration\n");
> +		return -EINVAL;
> +	}
> +
> +	dr_mode = best_dr_mode;
> +
> +	if (dr_mode == USB_DR_MODE_OTG || dr_mode == USB_DR_MODE_HOST) {
> +		ret = cdns3_host_init(cdns);
> +		if (ret) {
> +			dev_err(dev, "Host initialization failed with %d\n",
> +				ret);
> +			goto err;
> +		}
> +	}
> +
> +	if (dr_mode == USB_DR_MODE_OTG || dr_mode == USB_DR_MODE_PERIPHERAL) {
> +		ret = cdns3_gadget_init(cdns);
> +		if (ret) {
> +			dev_err(dev, "Device initialization failed with %d\n",
> +				ret);
> +			goto err;
> +		}
> +	}
> +
> +	cdns->desired_dr_mode = dr_mode;
> +	cdns->dr_mode = dr_mode;
> +	/*
> +	 * dr_mode could be change so DRD must update controller
> +	 * configuration
> +	 */
> +	ret = cdns3_drd_update_mode(cdns);
Do you need check ret value? if not, no need assign it
> +
> +	cdns->role = cdns3_get_initial_role(cdns);
> +
> +	ret = cdns3_role_start(cdns, cdns->role);
> +	if (ret) {
> +		dev_err(dev, "can't start %s role\n",
> +			cdns3_get_current_role_driver(cdns)->name);
> +		goto err;
> +	}
> +
> +	return ret;
> +err:
> +	cdns3_exit_roles(cdns);
> +	return ret;
> +}
> +
> +/**
> + * cdsn3_get_real_role - get real role of controller based on hardware settings.
> + * @cdns: Pointer to cdns3 structure
> + *
> + * Returns role
> + */
> +enum cdns3_roles cdsn3_get_real_role(struct cdns3 *cdns)
> +{
> +	enum cdns3_roles role = CDNS3_ROLE_END;
> +
> +	if (cdns->current_dr_mode == USB_DR_MODE_OTG) {
> +		if (cdns3_get_id(cdns))
> +			role = CDNS3_ROLE_GADGET;
> +		else
> +			role = CDNS3_ROLE_HOST;
> +	} else {
> +		if (cdns3_is_host(cdns))
> +			role = CDNS3_ROLE_HOST;
> +		if (cdns3_is_device(cdns))
> +			role = CDNS3_ROLE_GADGET;
> +	}
> +
> +	return role;
> +}
> +
> +/**
> + * cdns3_role_switch - work queue handler for role switch
> + *
> + * @work: work queue item structure
> + *
> + * Handles below events:
> + * - Role switch for dual-role devices
> + * - CDNS3_ROLE_GADGET <--> CDNS3_ROLE_END for peripheral-only devices
> + */
> +static void cdns3_role_switch(struct work_struct *work)
> +{
> +	enum cdns3_roles role = CDNS3_ROLE_END;
> +	struct cdns3_role_driver *role_drv;
> +	enum cdns3_roles current_role;
> +	struct cdns3 *cdns;
> +	int ret = 0;
> +
> +	cdns = container_of(work, struct cdns3, role_switch_wq);
> +
> +	/* During switching cdns->role can be different then role */
> +	role = cdsn3_get_real_role(cdns);
> +
> +	role_drv = cdns3_get_current_role_driver(cdns);
> +
> +	pm_runtime_get_sync(cdns->dev);
> +
> +	/* Disable current role. This state can be forced from user space. */
> +	if (cdns->debug_disable && role_drv->state == CDNS3_ROLE_STATE_ACTIVE) {
> +		cdns3_role_stop(cdns);
> +		goto exit;
> +	}
> +
> +	/* Do nothing if nothing changed */
> +	if (cdns->role == role && role_drv->state == CDNS3_ROLE_STATE_ACTIVE)
> +		goto exit;
> +
> +	cdns3_role_stop(cdns);
> +
> +	role = cdsn3_get_real_role(cdns);
> +
> +	current_role = cdns->role;
> +	dev_dbg(cdns->dev, "Switching role");
> +
> +	ret = cdns3_role_start(cdns, role);
> +
remove blank line
> +	if (ret) {
> +		/* Back to current role */
> +		dev_err(cdns->dev, "set %d has failed, back to %d\n",
> +			role, current_role);
> +		cdns3_role_start(cdns, current_role);
> +	}
> +exit:
> +	pm_runtime_put_sync(cdns->dev);
> +}
> +
> +/**
> + * cdns3_probe - probe for cdns3 core device
> + * @pdev: Pointer to cdns3 core platform device
> + *
> + * Returns 0 on success otherwise negative errno
> + */
> +static int cdns3_probe(struct platform_device *pdev)
> +{
> +	struct device *dev = &pdev->dev;
> +	struct resource	*res;
> +	struct cdns3 *cdns;
> +	void __iomem *regs;
> +	int ret;
> +
> +	cdns = devm_kzalloc(dev, sizeof(*cdns), GFP_KERNEL);
> +	if (!cdns)
> +		return -ENOMEM;
> +
> +	cdns->dev = dev;
> +
> +	platform_set_drvdata(pdev, cdns);
> +
> +	res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
> +	if (!res) {
> +		dev_err(dev, "missing IRQ\n");
> +		return -ENODEV;
> +	}
> +	cdns->irq = res->start;
> +
> +	cdns->xhci_res[0] = *res;
> +
> +	/*
> +	 * Request memory region
> +	 * region-0: xHCI
> +	 * region-1: Peripheral
> +	 * region-2: OTG registers
> +	 */
> +	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +	cdns->xhci_res[1] = *res;
> +
> +	res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
> +	regs = devm_ioremap_resource(dev, res);
> +	if (IS_ERR(regs))
> +		return PTR_ERR(regs);
> +	cdns->dev_regs	= regs;
> +
> +	res = platform_get_resource(pdev, IORESOURCE_MEM, 2);
> +	cdns->otg_res = *res;
It'll make the DTS clearer if add reg-names
> +
> +	mutex_init(&cdns->mutex);
> +
> +	cdns->phy = devm_phy_get(dev, "cdns3,usbphy");
Try to use devm_phy_optional_get()?
> +	if (IS_ERR(cdns->phy)) {
> +		ret = PTR_ERR(cdns->phy);
> +		if (ret == -ENODEV) {
> +			cdns->phy = NULL;
> +		} else if (ret == -EPROBE_DEFER) {
> +			return ret;
> +		} else {
> +			dev_err(dev, "no phy found\n");
> +			goto err0;
> +		}
> +	}
> +
> +	phy_init(cdns->phy);
> +
> +	INIT_WORK(&cdns->role_switch_wq, cdns3_role_switch);
> +
> +	ret = cdns3_drd_init(cdns);
> +	if (ret)
> +		goto err1;
> +
> +	ret = cdns3_core_init_role(cdns);
> +	if (ret)
> +		goto err1;
> +
> +	cdns3_debugfs_init(cdns);
> +	device_set_wakeup_capable(dev, true);
> +	pm_runtime_set_active(dev);
> +	pm_runtime_enable(dev);
> +
> +	/*
> +	 * The controller needs less time between bus and controller suspend,
> +	 * and we also needs a small delay to avoid frequently entering low
> +	 * power mode.
> +	 */
> +	pm_runtime_set_autosuspend_delay(dev, 20);
> +	pm_runtime_mark_last_busy(dev);
> +	pm_runtime_use_autosuspend(dev);
> +	dev_dbg(dev, "Cadence USB3 core: probe succeed\n");
> +
> +	return 0;
> +
> +err1:
> +	phy_exit(cdns->phy);
> +err0:
> +	return ret;
> +}
> +
> +/**
> + * cdns3_remove - unbind drd driver and clean up
> + * @pdev: Pointer to Linux platform device
> + *
> + * Returns 0 on success otherwise negative errno
> + */
> +static int cdns3_remove(struct platform_device *pdev)
> +{
> +	struct cdns3 *cdns = platform_get_drvdata(pdev);
> +
> +	pm_runtime_get_sync(&pdev->dev);
> +	pm_runtime_disable(&pdev->dev);
> +	pm_runtime_put_noidle(&pdev->dev);
> +	cdns3_debugfs_exit(cdns);
> +	cdns3_exit_roles(cdns);
> +	phy_exit(cdns->phy);
> +	return 0;
> +}
> +
> +#ifdef CONFIG_OF
> +static const struct of_device_id of_cdns3_match[] = {
> +	{ .compatible = "cdns,usb3-1.0.0" },
> +	{ .compatible = "cdns,usb3-1.0.1" },
> +	{ },
> +};
> +MODULE_DEVICE_TABLE(of, of_cdns3_match);
> +#endif
> +
> +static struct platform_driver cdns3_driver = {
> +	.probe		= cdns3_probe,
> +	.remove		= cdns3_remove,
> +	.driver		= {
> +		.name	= "cdns-usb3",
> +		.of_match_table	= of_match_ptr(of_cdns3_match),
> +	},
> +};
> +
> +module_platform_driver(cdns3_driver);
> +
> +MODULE_ALIAS("platform:cdns3");
> +MODULE_AUTHOR("Pawel Laszczak <pawell@cadence.com>");
> +MODULE_LICENSE("GPL v2");
> +MODULE_DESCRIPTION("Cadence USB3 DRD Controller Driver");
> diff --git a/drivers/usb/cdns3/core.h b/drivers/usb/cdns3/core.h
> new file mode 100644
> index 000000000000..fb4b39206158
> --- /dev/null
> +++ b/drivers/usb/cdns3/core.h
> @@ -0,0 +1,116 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +/*
> + * Cadence USBSS DRD Header File.
> + *
> + * Copyright (C) 2017-2018 NXP
> + * Copyright (C) 2018 Cadence.
> + *
> + * Authors: Peter Chen <peter.chen@nxp.com>
> + *          Pawel Laszczak <pawell@cadence.com>
> + */
> +#include <linux/usb/otg.h>
> +
> +#ifndef __LINUX_CDNS3_CORE_H
> +#define __LINUX_CDNS3_CORE_H
> +
> +struct cdns3;
> +enum cdns3_roles {
> +	CDNS3_ROLE_HOST = 0,
> +	CDNS3_ROLE_GADGET,
> +	CDNS3_ROLE_END,
> +};
> +
> +/**
> + * struct cdns3_role_driver - host/gadget role driver
> + * @start: start this role
> + * @stop: stop this role
> + * @suspend: suspend callback for this role
> + * @resume: resume callback for this role
> + * @irq: irq handler for this role
> + * @name: role name string (host/gadget)
> + * @state: current state
> + */
> +struct cdns3_role_driver {
> +	int (*start)(struct cdns3 *cdns);
> +	void (*stop)(struct cdns3 *cdns);
> +	int (*suspend)(struct cdns3 *cdns, bool do_wakeup);
> +	int (*resume)(struct cdns3 *cdns, bool hibernated);
> +	const char *name;
> +#define CDNS3_ROLE_STATE_INACTIVE	0
> +#define CDNS3_ROLE_STATE_ACTIVE		1
> +	int state;
> +};
> +
> +#define CDNS3_XHCI_RESOURCES_NUM	2
> +/**
> + * struct cdns3 - Representation of Cadence USB3 DRD controller.
> + * @dev: pointer to Cadence device struct
> + * @xhci_regs: pointer to base of xhci registers
> + * @xhci_res: the resource for xhci
> + * @dev_regs: pointer to base of dev registers
> + * @otg_regs: pointer to base of otg registers
> + * @irq: irq number for controller
> + * @roles: array of supported roles for this controller
> + * @role: current role
> + * @host_dev: the child host device pointer for cdns3 core
> + * @gadget_dev: the child gadget device pointer for cdns3 core
> + * @usb: phy for this controller
> + * @role_switch_wq: work queue item for role switch
> + * @in_lpm: the controller in low power mode
> + * @wakeup_int: the wakeup interrupt
> + * @mutex: the mutex for concurrent code at driver
> + * @dr_mode: supported mode of operation it can be only Host, only Device
> + *           or OTG mode that allow to switch between Device and Host mode.
> + *           This field based on firmware setting, kernel configuration
> + *           and hardware configuration.
> + * @current_dr_mode: current mode of operation when in dual-role mode
> + * @desired_dr_mode: desired mode of operation when in dual-role mode.
> + *           This value can be changed during runtime.
> + *           Available options depends on  dr_mode:
> + *           dr_mode                 |  desired_dr_mode and current_dr_mode
> + *           ----------------------------------------------------------------
> + *           USB_DR_MODE_HOST        | only USB_DR_MODE_HOST
> + *           USB_DR_MODE_PERIPHERAL  | only USB_DR_MODE_PERIPHERAL
> + *           USB_DR_MODE_OTG         | only USB_DR_MODE_HOST
> + *           USB_DR_MODE_OTG         | only USB_DR_MODE_PERIPHERAL
> + *           USB_DR_MODE_OTG         | USB_DR_MODE_OTG
> + *
> + *           Desired_dr_role can be changed by means of debugfs.
> + * @root: debugfs root folder pointer
> + * @debug_disable:
> + */
> +struct cdns3 {
> +	struct device			*dev;
> +	void __iomem			*xhci_regs;
> +	struct resource			xhci_res[CDNS3_XHCI_RESOURCES_NUM];
> +	struct cdns3_usb_regs __iomem	*dev_regs;
> +
> +	struct resource			otg_res;
> +	struct cdns3_otg_legacy_regs	*otg_v0_regs;
> +	struct cdns3_otg_regs		*otg_v1_regs;
> +	struct cdns3_otg_common_regs	*otg_regs;
> +#define CDNS3_CONTROLLER_V0	0
> +#define CDNS3_CONTROLLER_V1	1
> +	u32				version;
> +
> +	int				irq;
> +	struct cdns3_role_driver	*roles[CDNS3_ROLE_END];
> +	enum cdns3_roles		role;
> +	struct platform_device		*host_dev;
> +	struct cdns3_device		*gadget_dev;
> +	struct phy			*phy;
> +	struct work_struct		role_switch_wq;
> +	int				in_lpm:1;
> +	int				wakeup_int:1;
> +	/* mutext used in workqueue*/
> +	struct mutex			mutex;
> +	enum usb_dr_mode		dr_mode;
> +	enum usb_dr_mode		current_dr_mode;
> +	enum usb_dr_mode		desired_dr_mode;
> +	struct dentry			*root;
> +	int				debug_disable:1;
> +};
> +
> +void cdns3_role_stop(struct cdns3 *cdns);
> +
> +#endif /* __LINUX_CDNS3_CORE_H */
> diff --git a/drivers/usb/cdns3/debug.h b/drivers/usb/cdns3/debug.h
> new file mode 100644
> index 000000000000..94f9ef15f899
> --- /dev/null
> +++ b/drivers/usb/cdns3/debug.h
> @@ -0,0 +1,166 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +/*
> + * Cadence USBSS DRD Driver.
> + * Debug header file.
> + *
> + * Copyright (C) 2018 Cadence.
> + *
> + * Author: Pawel Laszczak <pawell@cadence.com>
> + */
> +#ifndef __LINUX_CDNS3_DEBUG
> +#define __LINUX_CDNS3_DEBUG
> +
> +#include "core.h"
> +
> +static inline char *cdns3_decode_usb_irq(char *str,
> +					 enum usb_device_speed speed,
> +
too many sentences as inline function
> 					 u32 usb_ists)
> +{
> +	int ret;
> +
> +	ret = sprintf(str, "IRQ %08x = ", usb_ists);
> +
> +	if (usb_ists & (USB_ISTS_CON2I | USB_ISTS_CONI)) {
> +		ret += sprintf(str + ret, "Connection %s\n",
> +			       usb_speed_string(speed));
> +	}
> +	if (usb_ists & USB_ISTS_CON2I || usb_ists & USB_ISTS_CONI)
> +		ret += sprintf(str + ret, "Disconnection ");
> +	if (usb_ists & USB_ISTS_L2ENTI)
> +		ret += sprintf(str + ret, "suspended ");
> +
> +	if (usb_ists & USB_ISTS_L2EXTI)
> +		ret += sprintf(str + ret, "L2 exit ");
> +	if (usb_ists & USB_ISTS_U3EXTI)
> +		ret += sprintf(str + ret, "U3 exit ");
> +	if (usb_ists & USB_ISTS_UWRESI)
> +		ret += sprintf(str + ret, "Warm Reset ");
> +	if (usb_ists & USB_ISTS_UHRESI)
> +		ret += sprintf(str + ret, "Hot Reset ");
> +	if (usb_ists & USB_ISTS_U2RESI)
> +		ret += sprintf(str + ret, "Reset");
> +
> +	return str;
> +}
> +
> +static inline  char *cdns3_decode_ep_irq(char *str,
> +					 u32 ep_sts,
> +					 const char *ep_name)
ditto
> +{
> +	int ret;
> +
> +	ret = sprintf(str, "IRQ for %s: %08x ", ep_name, ep_sts);
> +
> +	if (ep_sts & EP_STS_SETUP)
> +		ret += sprintf(str + ret, "SETUP ");
> +	if (ep_sts & EP_STS_IOC)
> +		ret += sprintf(str + ret, "IOC ");
> +	if (ep_sts & EP_STS_ISP)
> +		ret += sprintf(str + ret, "ISP ");
> +	if (ep_sts & EP_STS_DESCMIS)
> +		ret += sprintf(str + ret, "DESCMIS ");
> +	if (ep_sts & EP_STS_STREAMR)
> +		ret += sprintf(str + ret, "STREAMR ");
> +	if (ep_sts & EP_STS_MD_EXIT)
> +		ret += sprintf(str + ret, "MD_EXIT ");
> +	if (ep_sts & EP_STS_TRBERR)
> +		ret += sprintf(str + ret, "TRBERR ");
> +	if (ep_sts & EP_STS_NRDY)
> +		ret += sprintf(str + ret, "NRDY ");
> +	if (ep_sts & EP_STS_PRIME)
> +		ret += sprintf(str + ret, "PRIME ");
> +	if (ep_sts & EP_STS_SIDERR)
> +		ret += sprintf(str + ret, "SIDERRT ");
> +	if (ep_sts & EP_STS_OUTSMM)
> +		ret += sprintf(str + ret, "OUTSMM ");
> +	if (ep_sts & EP_STS_ISOERR)
> +		ret += sprintf(str + ret, "ISOERR ");
> +	if (ep_sts & EP_STS_IOT)
> +		ret += sprintf(str + ret, "IOT ");
> +
> +	return str;
> +}
> +
> +static inline char *cdns3_decode_epx_irq(char *str,
> +					 char *ep_name,
> +					 u32 ep_sts)
> +{
> +	return cdns3_decode_ep_irq(str, ep_sts, ep_name);
> +}
> +
> +static inline char *cdns3_decode_ep0_irq(char *str,
> +					 int dir,
> +					 u32 ep_sts)
> +{
> +	return cdns3_decode_ep_irq(str, ep_sts,
> +				   dir ? "ep0IN" : "ep0OUT");
> +}
> +
> +/**
> + * Debug a transfer ring.
> + *
> + * Prints out all TRBs in the endpoint ring, even those after the Link TRB.
> + *.
> + */
> +static inline char *cdns3_dbg_ring(struct cdns3_endpoint *priv_ep,
> +				   struct cdns3_trb *ring, char *str)
ditto
> +{
> +	dma_addr_t addr = priv_ep->trb_pool_dma;
> +	struct cdns3_trb *trb;
> +	int trb_per_sector;
> +	int ret = 0;
> +	int i;
> +
> +	trb_per_sector = GET_TRBS_PER_SEGMENT(priv_ep->type);
> +
> +	trb = &priv_ep->trb_pool[priv_ep->dequeue];
> +	ret += sprintf(str + ret, "\n\t\tRing contents for %s:", priv_ep->name);
> +
> +	ret += sprintf(str + ret,
> +		       "\n\t\tRing deq index: %d, trb: %p (virt), 0x%llx (dma)\n",
> +		       priv_ep->dequeue, trb,
> +		       (unsigned long long)cdns3_trb_virt_to_dma(priv_ep, trb));
> +
> +	trb = &priv_ep->trb_pool[priv_ep->enqueue];
> +	ret += sprintf(str + ret,
> +		       "\t\tRing enq index: %d, trb: %p (virt), 0x%llx (dma)\n",
> +		       priv_ep->enqueue, trb,
> +		       (unsigned long long)cdns3_trb_virt_to_dma(priv_ep, trb));
> +
> +	ret += sprintf(str + ret,
> +		       "\t\tfree trbs: %d, CCS=%d, PCS=%d\n",
> +		       priv_ep->free_trbs, priv_ep->ccs, priv_ep->pcs);
> +
> +	if (trb_per_sector > TRBS_PER_SEGMENT)
> +		trb_per_sector = TRBS_PER_SEGMENT;
> +
> +	if (trb_per_sector > TRBS_PER_SEGMENT) {
> +		sprintf(str + ret, "\t\tTo big transfer ring %d\n",
> +			trb_per_sector);
> +		return str;
> +	}
> +
> +	for (i = 0; i < trb_per_sector; ++i) {
> +		trb = &ring[i];
> +		ret += sprintf(str + ret,
> +			"\t\t@%pad %08x %08x %08x\n", &addr,
> +			le32_to_cpu(trb->buffer),
> +			le32_to_cpu(trb->length),
> +			le32_to_cpu(trb->control));
> +		addr += sizeof(*trb);
> +	}
> +
> +	return str;
> +}
> +
> +#ifdef CONFIG_DEBUG_FS
> +void cdns3_debugfs_init(struct cdns3 *cdns);
> +void cdns3_debugfs_exit(struct cdns3 *cdns);
> +#else
> +void cdns3_debugfs_init(struct cdns3 *cdns);
> +{  }
> +void cdns3_debugfs_exit(struct cdns3 *cdns);
> +{  }
> +#endif
> +
> +#endif /*__LINUX_CDNS3_DEBUG*/
> diff --git a/drivers/usb/cdns3/debugfs.c b/drivers/usb/cdns3/debugfs.c
> new file mode 100644
> index 000000000000..d7919f5c1d90
> --- /dev/null
> +++ b/drivers/usb/cdns3/debugfs.c
> @@ -0,0 +1,168 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Cadence USBSS DRD Controller DebugFS filer.
> + *
> + * Copyright (C) 2018 Cadence.
> + *
> + * Author: Pawel Laszczak <pawell@cadence.com>
> + */
> +
> +#include <linux/types.h>
> +#include <linux/debugfs.h>
> +#include <linux/seq_file.h>
> +#include <linux/uaccess.h>
> +
> +#include "core.h"
> +#include "gadget.h"
> +#include "drd.h"
> +
> +static int cdns3_mode_show(struct seq_file *s, void *unused)
> +{
> +	struct cdns3 *cdns = s->private;
> +
> +	switch (cdns->current_dr_mode) {
> +	case USB_DR_MODE_HOST:
> +		seq_puts(s, "host\n");
> +		break;
> +	case USB_DR_MODE_PERIPHERAL:
> +		seq_puts(s, "device\n");
> +		break;
> +	case USB_DR_MODE_OTG:
> +		seq_puts(s, "otg\n");
> +		break;
> +	default:
> +		seq_puts(s, "UNKNOWN mode\n");
> +	}
> +
> +	return 0;
> +}
> +
> +static int cdns3_mode_open(struct inode *inode, struct file *file)
> +{
> +	return single_open(file, cdns3_mode_show, inode->i_private);
> +}
> +
> +static ssize_t cdns3_mode_write(struct file *file,
> +				const char __user *ubuf,
> +				size_t count, loff_t *ppos)
> +{
> +	struct seq_file	 *s = file->private_data;
> +	struct cdns3 *cdns = s->private;
> +	u32 mode = USB_DR_MODE_UNKNOWN;
> +	char buf[32];
> +
> +	if (copy_from_user(&buf, ubuf, min_t(size_t, sizeof(buf) - 1, count)))
> +		return -EFAULT;
> +
> +	if (!strncmp(buf, "host", 4)) {
> +		if (cdns->dr_mode == USB_DR_MODE_HOST ||
> +		    cdns->dr_mode == USB_DR_MODE_OTG) {
> +			mode = USB_DR_MODE_HOST;
> +		}
> +	}
> +
> +	if (!strncmp(buf, "device", 6))
> +		if (cdns->dr_mode == USB_DR_MODE_PERIPHERAL ||
> +		    cdns->dr_mode == USB_DR_MODE_OTG)
> +			mode = USB_DR_MODE_PERIPHERAL;
> +
> +	if (!strncmp(buf, "otg", 3) && cdns->dr_mode == USB_DR_MODE_OTG)
> +		mode = USB_DR_MODE_OTG;
> +
> +	if (mode == USB_DR_MODE_UNKNOWN) {
> +		dev_err(cdns->dev, "Failed: incorrect mode setting\n");
> +		return -EFAULT;
> +	}
> +
> +	if (cdns->current_dr_mode != mode) {
> +		cdns->desired_dr_mode = mode;
> +		cdns->debug_disable = 0;
> +		cdns3_role_stop(cdns);
> +		cdns3_drd_update_mode(cdns);
> +		queue_work(system_freezable_wq, &cdns->role_switch_wq);
> +	}
> +
> +	return count;
> +}
> +
> +static const struct file_operations cdns3_mode_fops = {
> +	.open			= cdns3_mode_open,
> +	.write			= cdns3_mode_write,
> +	.read			= seq_read,
> +	.llseek			= seq_lseek,
> +	.release		= single_release,
> +};
> +
> +static int cdns3_disable_show(struct seq_file *s, void *unused)
> +{
> +	struct cdns3 *cdns = s->private;
> +
> +	if (!cdns->debug_disable)
> +		seq_puts(s, "0\n");
> +	else
> +		seq_puts(s, "1\n");
> +
> +	return 0;
> +}
> +
> +static ssize_t cdns3_disable_write(struct file *file,
> +				   const char __user *ubuf,
> +				   size_t count, loff_t *ppos)
> +{
> +	struct seq_file	 *s = file->private_data;
> +	struct cdns3 *cdns = s->private;
> +	int disable;
> +	char buf[32];
> +
> +	if (copy_from_user(&buf, ubuf, min_t(size_t, sizeof(buf) - 1, count)))
> +		return -EFAULT;
> +
> +	if (!strncmp(buf, "1", 1) || !strncmp(buf, "yes", 3)) {
> +		disable = 1;
> +	} else if (!strncmp(buf, "0", 1) || !strncmp(buf, "no", 2)) {
> +		disable = 0;
> +	} else {
> +		dev_err(cdns->dev, "Failed: incorrect disable setting\n");
> +		return -EFAULT;
> +	}
> +
There is common API kstrtobool()?

> +	if (disable != cdns->debug_disable) {
> +		cdns->debug_disable = disable;
> +		queue_work(system_freezable_wq, &cdns->role_switch_wq);
> +	}
> +
> +	return count;
> +}
> +
> +static int cdns3_disable_open(struct inode *inode, struct file *file)
> +{
> +	return single_open(file, cdns3_disable_show, inode->i_private);
> +}
> +
> +static const struct file_operations cdns3_disable_fops = {
> +	.open			= cdns3_disable_open,
> +	.write			= cdns3_disable_write,
> +	.read			= seq_read,
> +	.llseek			= seq_lseek,
> +	.release		= single_release,
> +};
> +
> +void cdns3_debugfs_init(struct cdns3 *cdns)
> +{
> +	struct dentry *root;
> +
> +	root = debugfs_create_dir(dev_name(cdns->dev), NULL);
> +	cdns->root = root;
> +	if (IS_ENABLED(CONFIG_USB_CDNS3_GADGET) &&
> +	    IS_ENABLED(CONFIG_USB_CDNS3_HOST))
> +		debugfs_create_file("mode", 0644, root, cdns,
> +				    &cdns3_mode_fops);
> +
> +	debugfs_create_file("disable", 0644, root, cdns,
> +			    &cdns3_disable_fops);
> +}
> +
> +void cdns3_debugfs_exit(struct cdns3 *cdns)
> +{
> +	debugfs_remove_recursive(cdns->root);
> +}
> diff --git a/drivers/usb/cdns3/drd.c b/drivers/usb/cdns3/drd.c
> new file mode 100644
> index 000000000000..b0c32302eb0b
> --- /dev/null
> +++ b/drivers/usb/cdns3/drd.c
> @@ -0,0 +1,350 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Cadence USBSS DRD Driver.
> + *
> + * Copyright (C) 2018 Cadence.
> + *
> + * Author: Pawel Laszczak <pawell@cadence.com
> + *
> + */
> +#include <linux/kernel.h>
> +#include <linux/interrupt.h>
> +#include <linux/delay.h>
> +#include <linux/usb/otg.h>
> +
> +#include "gadget.h"
> +#include "drd.h"
> +#include "core.h"
> +
> +static int cdns3_drd_switch_gadget(struct cdns3 *cdns, int on);
> +static int cdns3_drd_switch_host(struct cdns3 *cdns, int on);
> +
> +/**
> + * cdns3_set_mode - change mode of OTG Core
> + * @cdns: pointer to context structure
> + * @mode: selected mode from cdns_role
> + */
> +void cdns3_set_mode(struct cdns3 *cdns, enum usb_dr_mode mode)
> +{
> +	u32 reg;
> +
> +	cdns->current_dr_mode = mode;
> +
> +	switch (mode) {
> +	case USB_DR_MODE_PERIPHERAL:
> +		dev_info(cdns->dev, "Set controller to Gadget mode\n");
> +		cdns3_drd_switch_gadget(cdns, 1);
> +		break;
> +	case USB_DR_MODE_HOST:
> +		dev_info(cdns->dev, "Set controller to Host mode\n");
> +		cdns3_drd_switch_host(cdns, 1);
> +		break;
> +	case USB_DR_MODE_OTG:
> +		dev_info(cdns->dev, "Set controller to OTG mode\n");
> +		if (cdns->version == CDNS3_CONTROLLER_V1) {
> +			reg = readl(&cdns->otg_v1_regs->override);
> +			reg |= OVERRIDE_IDPULLUP;
> +			writel(reg, &cdns->otg_v1_regs->override);
> +		} else {
> +			reg = readl(&cdns->otg_v0_regs->ctrl1);
> +			reg |= OVERRIDE_IDPULLUP_V0;
> +			writel(reg, &cdns->otg_v0_regs->ctrl1);
> +		}
> +
> +		/*
> +		 * Hardware specification says: "ID_VALUE must be valid within
> +		 * 50ms after idpullup is set to '1" so driver must wait
> +		 * 50ms before reading this pin.
> +		 */
> +		usleep_range(50000, 60000);
> +		break;
> +	default:
> +		cdns->current_dr_mode = USB_DR_MODE_UNKNOWN;
> +		dev_err(cdns->dev, "Unsupported mode of operation %d\n", mode);
> +		return;
> +	}
> +}
> +
> +int cdns3_get_id(struct cdns3 *cdns)
> +{
> +	int id;
> +
> +	id = readl(&cdns->otg_regs->sts) & OTGSTS_ID_VALUE;
> +	dev_dbg(cdns->dev, "OTG ID: %d", id);
> +	return  1 ; //id;
return fixed value ?
> +}
> +
> +int cdns3_is_host(struct cdns3 *cdns)
> +{
> +	if (cdns->current_dr_mode == USB_DR_MODE_HOST)
> +		return 1;
> +	else if (!cdns3_get_id(cdns))
> +		return 1;
> +
> +	return 0;
> +}
> +
> +

> +
> +/**
> + * cdns3_init_otg_mode - initialize drd controller
> + * @cdns: Pointer to controller context structure
> + *
> + * Returns 0 on success otherwise negative errno
void function
> + */
> +static void cdns3_init_otg_mode(struct cdns3 *cdns)
> +{
> +	cdns3_otg_disable_irq(cdns);
> +	/* clear all interrupts */
> +	writel(~0, &cdns->otg_regs->ivect);
> +
> +	cdns3_set_mode(cdns, USB_DR_MODE_OTG);
> +
> +	if (cdns3_is_host(cdns))
> +		cdns3_drd_switch_host(cdns, 1);
> +	else
> +		cdns3_drd_switch_gadget(cdns, 1);
> +
> +	cdns3_otg_enable_irq(cdns);
> +}
> +
> +/**
> + * cdns3_drd_update_mode - initialize mode of operation
> + * @cdns: Pointer to controller context structure
> + *
> + * Returns 0 on success otherwise negative errno
> + */
> +int cdns3_drd_update_mode(struct cdns3 *cdns)
> +{
> +	int ret = 0;
> +
> +	if (cdns->desired_dr_mode == cdns->current_dr_mode)
> +		return ret;
> +
> +	cdns3_drd_switch_gadget(cdns, 0);
> +	cdns3_drd_switch_host(cdns, 0);
> +
> +	switch (cdns->desired_dr_mode) {
> +	case USB_DR_MODE_PERIPHERAL:
> +		cdns3_set_mode(cdns, USB_DR_MODE_PERIPHERAL);
> +		break;
> +	case USB_DR_MODE_HOST:
> +		cdns3_set_mode(cdns, USB_DR_MODE_HOST);
> +		break;
> +	case USB_DR_MODE_OTG:
> +		cdns3_init_otg_mode(cdns);
> +		break;
> +	default:
> +		dev_err(cdns->dev, "Unsupported mode of operation %d\n",
> +			cdns->dr_mode);
> +		return -EINVAL;
> +	}
> +
> +	return ret;
no need define local variable @ret
> +}
> +
> +/**
> + * cdns3_drd_irq - interrupt handler for OTG events
> + *
> + * @irq: irq number for cdns3 core device
> + * @data: structure of cdns3
> + *
> + * Returns IRQ_HANDLED or IRQ_NONE
> + */
> +static irqreturn_t cdns3_drd_irq(int irq, void *data)
> +{
> +	irqreturn_t ret = IRQ_NONE;
> +	struct cdns3 *cdns = data;
> +	u32 reg;
> +
> +	if (cdns->dr_mode != USB_DR_MODE_OTG)
> +		return ret;
> +
> +	reg = readl(&cdns->otg_regs->ivect);
> +
> +	if (!reg)
> +		return ret;
> +
> +	if (reg & OTGIEN_ID_CHANGE_INT) {
> +		dev_dbg(cdns->dev, "OTG IRQ: new ID: %d\n",
> +			cdns3_get_id(cdns));
> +
> +		queue_work(system_freezable_wq, &cdns->role_switch_wq);
> +
do you need to clear interrupts status here?
> +		ret = IRQ_HANDLED;
> +	}
> +
> +	writel(~0, &cdns->otg_regs->ivect);
> +	return ret;
> +}
> +
> +int cdns3_drd_init(struct cdns3 *cdns)
> +{
> +	void __iomem *regs;
> +	int ret = 0;
> +	u32 state;
> +
> +	regs = devm_ioremap_resource(cdns->dev, &cdns->otg_res);
> +	if (IS_ERR(regs))
> +		return PTR_ERR(regs);
> +
> +	/* Detection of DRD version. Controller has been released
> +	 * in two versions. Both are similar, but they have same changes
> +	 * in register maps.
> +	 * The first register in old version is command register and it's read
> +	 * only, so driver should read 0 from it. On the other hand, in v1
> +	 * the first register contains device ID number which is not set to 0.
> +	 * Driver uses this fact to detect the proper version of
> +	 * controller.
> +	 */
> +	cdns->otg_v0_regs = regs;
> +	if (!readl(&cdns->otg_v0_regs->cmd)) {
> +		cdns->version  = CDNS3_CONTROLLER_V0;
> +		cdns->otg_v1_regs = NULL;
> +		cdns->otg_regs = regs;
> +		dev_info(cdns->dev, "DRD version v0 (%08x)\n",
> +			 readl(&cdns->otg_v0_regs->version));
> +	} else {
> +		cdns->otg_v0_regs = NULL;
> +		cdns->otg_v1_regs = regs;
> +		cdns->otg_regs = (void *)&cdns->otg_v1_regs->cmd;
> +		cdns->version  = CDNS3_CONTROLLER_V1;
> +		dev_info(cdns->dev, "DRD version v1 (ID: %08x, rev: %08x)\n",
> +			 readl(&cdns->otg_v1_regs->did),
> +			 readl(&cdns->otg_v1_regs->rid));
> +	}
> +
> +	state = OTGSTS_STRAP(readl(&cdns->otg_regs->sts));
> +
> +	/* Update dr_mode according to STRAP configuration. */
> +	cdns->dr_mode = USB_DR_MODE_OTG;
> +	if (state == OTGSTS_STRAP_HOST) {
> +		dev_info(cdns->dev, "Controller strapped to HOST\n");
> +		cdns->dr_mode = USB_DR_MODE_HOST;
> +	} else if (state == OTGSTS_STRAP_GADGET) {
> +		dev_info(cdns->dev, "Controller strapped to PERIPHERAL\n");
> +		cdns->dr_mode = USB_DR_MODE_PERIPHERAL;
> +	}
> +
> +	cdns->desired_dr_mode = cdns->dr_mode;
> +	cdns->current_dr_mode = USB_DR_MODE_UNKNOWN;
> +
> +	ret = devm_request_threaded_irq(cdns->dev, cdns->irq, cdns3_drd_irq,
> +					NULL, IRQF_SHARED,
> +					dev_name(cdns->dev), cdns);
> +
> +	if (ret)
> +		return ret;
> +
> +	state = readl(&cdns->otg_regs->sts);
> +	if (OTGSTS_OTG_NRDY(state) != 0) {
> +		dev_err(cdns->dev, "Cadence USB3 OTG device not ready\n");
> +		return -ENODEV;
> +	}
> +
> +	ret = cdns3_drd_update_mode(cdns);
> +
> +	return ret;
return cdns3_drd_update_mode();
> +}
> +
> +int cdns3_drd_exit(struct cdns3 *cdns)
> +{
> +	return cdns3_drd_switch_host(cdns, 0);
> +}


<...>

> diff --git a/drivers/usb/cdns3/ep0.c b/drivers/usb/cdns3/ep0.c
> new file mode 100644
> index 000000000000..89cf1cde1555
> --- /dev/null
> +++ b/drivers/usb/cdns3/ep0.c
> @@ -0,0 +1,896 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Cadence USBSS DRD Driver - gadget side.
> + *
> + * Copyright (C) 2018 Cadence Design Systems.
> + * Copyright (C) 2017-2018 NXP
> + *
> + * Authors: Pawel Jez <pjez@cadence.com>,
> + *          Pawel Laszczak <pawell@cadence.com>
> + *	    Peter Chen <peter.chen@nxp.com>
> + */
> +
> +#include <linux/usb/composite.h>
> +
> +#include "gadget.h"
> +#include "trace.h"
> +
> +static struct usb_endpoint_descriptor cdns3_gadget_ep0_desc = {
> +	.bLength = USB_DT_ENDPOINT_SIZE,
> +	.bDescriptorType = USB_DT_ENDPOINT,
> +	.bmAttributes =	USB_ENDPOINT_XFER_CONTROL,
> +};
> +
<...>
> +
> +
> +#endif /* __LINUX_CDNS3_GADGET_EXPORT */
> diff --git a/drivers/usb/cdns3/gadget.c b/drivers/usb/cdns3/gadget.c
> new file mode 100644
> index 000000000000..0d95eb00be37
> --- /dev/null
> +++ b/drivers/usb/cdns3/gadget.c
> @@ -0,0 +1,2102 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Cadence USBSS DRD Driver - gadget side.
> + *
> + * Copyright (C) 2018 Cadence Design Systems.
> + * Copyright (C) 2017-2018 NXP
> + *
> + * Authors: Pawel Jez <pjez@cadence.com>,
> + *          Pawel Laszczak <pawell@cadence.com>
> + *	    Peter Chen <peter.chen@nxp.com>
> + */
> +
> +#include <linux/dma-mapping.h>
> +#include <linux/usb/gadget.h>
> +
<...>

> +/**
> + * cdns3_next_request - returns next request from list
> + * @list: list containing requests
> + *
> + * Returns request or NULL if no requests in list
> + */
> +struct usb_request *cdns3_next_request(struct list_head *list)
> +{
> +	if (list_empty(list))
> +		return NULL;
No need check it, if list is empty, list_first_entry_or_null() will
return NULL
> +	return list_first_entry_or_null(list, struct usb_request, list);
> +}
> +
> +/**
> + * cdns3_next_priv_request - returns next request from list
> + * @list: list containing requests
> + *
> + * Returns request or NULL if no requests in list
> + */
> +struct cdns3_request *cdns3_next_priv_request(struct list_head *list)
> +{
> +	if (list_empty(list))
> +		return NULL;
ditto
> +	return list_first_entry_or_null(list, struct cdns3_request, list);
> +}
> +

<...>




^ permalink raw reply	[flat|nested] 49+ messages in thread

* Re: [PATCH v2 5/5] usb:cdns3 Add Cadence USB3 DRD Driver
@ 2018-12-29  7:41     ` Chunfeng Yun
  0 siblings, 0 replies; 49+ messages in thread
From: Chunfeng Yun @ 2018-12-29  7:41 UTC (permalink / raw)
  To: Pawel Laszczak
  Cc: devicetree, gregkh, mark.rutland, linux-usb, hdegoede,
	heikki.krogerus, andy.shevchenko, robh+dt, rogerq, linux-kernel,
	adouglas, jbergsagel, nsekhar, nm, sureshp, peter.chen, pjez,
	kurahul

hi,
On Sun, 2018-12-23 at 15:13 +0000, Pawel Laszczak wrote:
> This patch introduce new Cadence USBSS DRD driver
> to linux kernel.

<...>

> diff --git a/drivers/usb/cdns3/cdns3-pci-wrap.c b/drivers/usb/cdns3/cdns3-pci-wrap.c
> new file mode 100644
> index 000000000000..e93179c45ece
> --- /dev/null
> +++ b/drivers/usb/cdns3/cdns3-pci-wrap.c
> @@ -0,0 +1,157 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Cadence USBSS PCI Glue driver
> + *
> + * Copyright (C) 2018 Cadence.
> + *
> + * Author: Pawel Laszczak <pawell@cadence.com>
> + */
> +
> +#include <linux/kernel.h>
> +#include <linux/module.h>
> +#include <linux/pci.h>
> +#include <linux/platform_device.h>
> +#include <linux/dma-mapping.h>
> +#include <linux/slab.h>
> +
> +struct cdns3_wrap {
> +	struct platform_device *plat_dev;
> +	struct pci_dev *hg_dev;
> +	struct resource dev_res[4];
> +};
> +
> +struct cdns3_wrap wrap;
> +
> +#define RES_IRQ_ID		0
> +#define RES_HOST_ID		1
> +#define RES_DEV_ID		2
> +#define RES_DRD_ID		3
> +
> +#define PCI_BAR_HOST		0
> +#define PCI_BAR_DEV		2
> +#define PCI_BAR_OTG		4
> +
> +#define PCI_DEV_FN_HOST_DEVICE	0
> +#define PCI_DEV_FN_OTG		1
> +
> +#define PCI_DRIVER_NAME		"cdns3-pci-usbss"
> +#define PLAT_DRIVER_NAME	"cdns-usb3"
> +
> +#define CDNS_VENDOR_ID 0x17cd
> +#define CDNS_DEVICE_ID 0x0100
> +
> +/**
> + * cdns3_pci_probe - Probe function for Cadence USB wrapper driver
> + * @pdev: platform device object
> + * @id: pci device id
> + *
> + * Returns 0 on success otherwise negative errno
> + */
> +static int cdns3_pci_probe(struct pci_dev *pdev,
> +			   const struct pci_device_id *id)
> +{
> +	struct platform_device_info plat_info;
> +	struct cdns3_wrap *wrap;
> +	struct resource *res;
> +	int err;
> +
> +	/*
> +	 * for GADGET/HOST PCI (devfn) function number is 0,
> +	 * for OTG PCI (devfn) function number is 1
> +	 */
> +	if (!id || pdev->devfn != PCI_DEV_FN_HOST_DEVICE)
> +		return -EINVAL;
> +
> +	err = pcim_enable_device(pdev);
> +	if (err) {
> +		dev_err(&pdev->dev, "Enabling PCI device has failed %d\n", err);
> +		return err;
> +	}
> +
> +	pci_set_master(pdev);
> +	wrap = devm_kzalloc(&pdev->dev, sizeof(*wrap), GFP_KERNEL);
> +	if (!wrap) {
> +		dev_err(&pdev->dev, "Failed to allocate memory\n");
> +		return -ENOMEM;
> +	}
> +
> +	/* function 0: host(BAR_0) + device(BAR_1) + otg(BAR_2)). */
> +	memset(wrap->dev_res, 0x00,
> +	       sizeof(struct resource) * ARRAY_SIZE(wrap->dev_res));
> +	dev_dbg(&pdev->dev, "Initialize Device resources\n");
> +	res = wrap->dev_res;
> +
> +	res[RES_DEV_ID].start = pci_resource_start(pdev, PCI_BAR_DEV);
> +	res[RES_DEV_ID].end =   pci_resource_end(pdev, PCI_BAR_DEV);
> +	res[RES_DEV_ID].name = "cdns3-dev-regs";
> +	res[RES_DEV_ID].flags = IORESOURCE_MEM;
> +	dev_dbg(&pdev->dev, "USBSS-DEV physical base addr: %pa\n",
> +		&res[RES_DEV_ID].start);
> +
> +	res[RES_HOST_ID].start = pci_resource_start(pdev, PCI_BAR_HOST);
> +	res[RES_HOST_ID].end = pci_resource_end(pdev, PCI_BAR_HOST);
> +	res[RES_HOST_ID].name = "cdns3-xhci-regs";
> +	res[RES_HOST_ID].flags = IORESOURCE_MEM;
> +	dev_dbg(&pdev->dev, "USBSS-XHCI physical base addr: %pa\n",
> +		&res[RES_HOST_ID].start);
> +
> +	res[RES_DRD_ID].start = pci_resource_start(pdev, PCI_BAR_OTG);
> +	res[RES_DRD_ID].end =   pci_resource_end(pdev, PCI_BAR_OTG);
> +	res[RES_DRD_ID].name = "cdns3-otg";
> +	res[RES_DRD_ID].flags = IORESOURCE_MEM;
> +	dev_dbg(&pdev->dev, "USBSS-DRD physical base addr: %pa\n",
> +		&res[RES_DRD_ID].start);
> +
> +	/* Interrupt common for both device and XHCI */
> +	wrap->dev_res[RES_IRQ_ID].start = pdev->irq;
> +	wrap->dev_res[RES_IRQ_ID].name = "cdns3-irq";
> +	wrap->dev_res[RES_IRQ_ID].flags = IORESOURCE_IRQ;
> +
> +	/* set up platform device info */
> +	memset(&plat_info, 0, sizeof(plat_info));
> +	plat_info.parent = &pdev->dev;
> +	plat_info.fwnode = pdev->dev.fwnode;
> +	plat_info.name = PLAT_DRIVER_NAME;
> +	plat_info.id = pdev->devfn;
> +	plat_info.res = wrap->dev_res;
> +	plat_info.num_res = ARRAY_SIZE(wrap->dev_res);
> +	plat_info.dma_mask = pdev->dma_mask;
> +
> +	/* register platform device */
> +	wrap->plat_dev = platform_device_register_full(&plat_info);
> +	if (IS_ERR(wrap->plat_dev)) {
> +		err = PTR_ERR(wrap->plat_dev);
> +		return err;
> +	}
return PTR_ERR(wrap->plat_dev);

> +
> +	pci_set_drvdata(pdev, wrap);
> +
> +	return err;
> +}
> +
> +void cdns3_pci_remove(struct pci_dev *pdev)
> +{
> +	struct cdns3_wrap *wrap = (struct cdns3_wrap *)pci_get_drvdata(pdev);
> +
> +	platform_device_unregister(wrap->plat_dev);
> +}
> +
> +static const struct pci_device_id cdns3_pci_ids[] = {
> +	{ PCI_DEVICE(CDNS_VENDOR_ID, CDNS_DEVICE_ID), },
> +	{ 0, }
> +};
> +
> +static struct pci_driver cdns3_pci_driver = {
> +	.name = PCI_DRIVER_NAME,
> +	.id_table = cdns3_pci_ids,
> +	.probe = cdns3_pci_probe,
> +	.remove = cdns3_pci_remove,
> +};
> +
> +module_pci_driver(cdns3_pci_driver);
> +MODULE_DEVICE_TABLE(pci, cdns3_pci_ids);
> +
> +MODULE_AUTHOR("Pawel Laszczak <pawell@cadence.com>");
> +MODULE_LICENSE("GPL v2");
> +MODULE_DESCRIPTION("Cadence USBSS PCI wrapperr");
> +
> diff --git a/drivers/usb/cdns3/core.c b/drivers/usb/cdns3/core.c
> new file mode 100644
> index 000000000000..d274586aca36
> --- /dev/null
> +++ b/drivers/usb/cdns3/core.c
> @@ -0,0 +1,406 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Cadence USBSS DRD Driver.
> + *
> + * Copyright (C) 2018 Cadence.
> + * Copyright (C) 2017-2018 NXP
> + *
> + * Author: Peter Chen <peter.chen@nxp.com>
> + *         Pawel Laszczak <pawell@cadence.com>
> + */
> +
> +#include <linux/module.h>
> +#include <linux/kernel.h>
> +#include <linux/platform_device.h>
> +#include <linux/interrupt.h>
> +#include <linux/io.h>
> +#include <linux/pm_runtime.h>
> +
> +#include "gadget.h"
> +#include "core.h"
> +#include "host-export.h"
> +#include "gadget-export.h"
> +#include "drd.h"
> +#include "debug.h"
> +
> +static inline
> +struct cdns3_role_driver *cdns3_get_current_role_driver(struct cdns3 *cdns)
> +{
> +	WARN_ON(cdns->role >= CDNS3_ROLE_END || !cdns->roles[cdns->role]);
> +	return cdns->roles[cdns->role];
> +}
> +
> +static inline int cdns3_role_start(struct cdns3 *cdns, enum cdns3_roles role)
remove inline
> +{
> +	int ret;
> +
> +	if (WARN_ON(role >= CDNS3_ROLE_END))
> +		return 0;
> +
> +	if (!cdns->roles[role])
> +		return -ENXIO;
> +
> +	if (cdns->roles[role]->state == CDNS3_ROLE_STATE_ACTIVE)
> +		return 0;
> +
> +	mutex_lock(&cdns->mutex);
> +	cdns->role = role;
> +	ret = cdns->roles[role]->start(cdns);
> +	if (!ret)
> +		cdns->roles[role]->state = CDNS3_ROLE_STATE_ACTIVE;
> +	mutex_unlock(&cdns->mutex);
> +	return ret;
> +}
> +
> +void cdns3_role_stop(struct cdns3 *cdns)
> +{
> +	enum cdns3_roles role = cdns->role;
> +
> +	if (role >= CDNS3_ROLE_END) {
> +		WARN_ON(role > CDNS3_ROLE_END);
> +		return;
> +	}
> +
> +	if (cdns->roles[role]->state == CDNS3_ROLE_STATE_INACTIVE)
> +		return;
> +
> +	mutex_lock(&cdns->mutex);
> +	cdns->roles[role]->stop(cdns);
> +	cdns->roles[role]->state = CDNS3_ROLE_STATE_INACTIVE;
> +	mutex_unlock(&cdns->mutex);
> +}
> +
> +/*
> + * cdns->role gets from cdns3_get_initial_role, and this API tells role at the
> + * runtime.
> + * If both roles are supported, the role is selected based on vbus/id.
> + * It could be read from OTG register or external connector.
> + * If only single role is supported, only one role structure
> + * is allocated, cdns->roles[CDNS3_ROLE_HOST] or cdns->roles[CDNS3_ROLE_GADGET].
> + */
> +static enum cdns3_roles cdns3_get_initial_role(struct cdns3 *cdns)
> +{
> +	if (cdns->roles[CDNS3_ROLE_HOST] && cdns->roles[CDNS3_ROLE_GADGET]) {
> +		if (cdns3_is_host(cdns))
> +			return CDNS3_ROLE_HOST;
> +		if (cdns3_is_device(cdns))
> +			return CDNS3_ROLE_GADGET;
> +	}
> +	return cdns->roles[CDNS3_ROLE_HOST]
> +		? CDNS3_ROLE_HOST
> +		: CDNS3_ROLE_GADGET;
> +}
> +
> +static void cdns3_exit_roles(struct cdns3 *cdns)
> +{
> +	cdns3_role_stop(cdns);
> +	cdns3_drd_exit(cdns);
> +}
> +
> +/**
> + * cdns3_core_init_role - initialize role of operation
> + * @cdns: Pointer to cdns3 structure
> + *
> + * Returns 0 on success otherwise negative errno
> + */
> +static int cdns3_core_init_role(struct cdns3 *cdns)
> +{
> +	struct device *dev = cdns->dev;
> +	enum usb_dr_mode best_dr_mode;
> +	enum usb_dr_mode dr_mode;
> +	int ret = 0;
> +
> +	dr_mode = usb_get_dr_mode(dev);
> +	cdns->role = CDNS3_ROLE_END;
> +
> +	/*
> +	 * If driver can't read mode by means of usb_get_dr_mdoe function then
> +	 * chooses mode according with Kernel configuration. This setting
> +	 * can be restricted later depending on strap pin configuration.
> +	 */
> +	if (dr_mode == USB_DR_MODE_UNKNOWN) {
> +		if (IS_ENABLED(CONFIG_USB_CDNS3_HOST) &&
> +		    IS_ENABLED(CONFIG_USB_CDNS3_GADGET))
> +			dr_mode = USB_DR_MODE_OTG;
> +		else if (IS_ENABLED(CONFIG_USB_CDNS3_HOST))
> +			dr_mode = USB_DR_MODE_HOST;
> +		else if (IS_ENABLED(CONFIG_USB_CDNS3_GADGET))
> +			dr_mode = USB_DR_MODE_PERIPHERAL;
> +	}
> +
> +	best_dr_mode = USB_DR_MODE_OTG;
> +
> +	if (dr_mode == USB_DR_MODE_OTG) {
> +		best_dr_mode = cdns->dr_mode;
> +	} else if (cdns->dr_mode == USB_DR_MODE_OTG) {
> +		best_dr_mode = dr_mode;
> +	} else if (cdns->dr_mode != dr_mode) {
> +		dev_err(dev, "Incorrect DRD configuration\n");
> +		return -EINVAL;
> +	}
> +
> +	dr_mode = best_dr_mode;
> +
> +	if (dr_mode == USB_DR_MODE_OTG || dr_mode == USB_DR_MODE_HOST) {
> +		ret = cdns3_host_init(cdns);
> +		if (ret) {
> +			dev_err(dev, "Host initialization failed with %d\n",
> +				ret);
> +			goto err;
> +		}
> +	}
> +
> +	if (dr_mode == USB_DR_MODE_OTG || dr_mode == USB_DR_MODE_PERIPHERAL) {
> +		ret = cdns3_gadget_init(cdns);
> +		if (ret) {
> +			dev_err(dev, "Device initialization failed with %d\n",
> +				ret);
> +			goto err;
> +		}
> +	}
> +
> +	cdns->desired_dr_mode = dr_mode;
> +	cdns->dr_mode = dr_mode;
> +	/*
> +	 * dr_mode could be change so DRD must update controller
> +	 * configuration
> +	 */
> +	ret = cdns3_drd_update_mode(cdns);
Do you need check ret value? if not, no need assign it
> +
> +	cdns->role = cdns3_get_initial_role(cdns);
> +
> +	ret = cdns3_role_start(cdns, cdns->role);
> +	if (ret) {
> +		dev_err(dev, "can't start %s role\n",
> +			cdns3_get_current_role_driver(cdns)->name);
> +		goto err;
> +	}
> +
> +	return ret;
> +err:
> +	cdns3_exit_roles(cdns);
> +	return ret;
> +}
> +
> +/**
> + * cdsn3_get_real_role - get real role of controller based on hardware settings.
> + * @cdns: Pointer to cdns3 structure
> + *
> + * Returns role
> + */
> +enum cdns3_roles cdsn3_get_real_role(struct cdns3 *cdns)
> +{
> +	enum cdns3_roles role = CDNS3_ROLE_END;
> +
> +	if (cdns->current_dr_mode == USB_DR_MODE_OTG) {
> +		if (cdns3_get_id(cdns))
> +			role = CDNS3_ROLE_GADGET;
> +		else
> +			role = CDNS3_ROLE_HOST;
> +	} else {
> +		if (cdns3_is_host(cdns))
> +			role = CDNS3_ROLE_HOST;
> +		if (cdns3_is_device(cdns))
> +			role = CDNS3_ROLE_GADGET;
> +	}
> +
> +	return role;
> +}
> +
> +/**
> + * cdns3_role_switch - work queue handler for role switch
> + *
> + * @work: work queue item structure
> + *
> + * Handles below events:
> + * - Role switch for dual-role devices
> + * - CDNS3_ROLE_GADGET <--> CDNS3_ROLE_END for peripheral-only devices
> + */
> +static void cdns3_role_switch(struct work_struct *work)
> +{
> +	enum cdns3_roles role = CDNS3_ROLE_END;
> +	struct cdns3_role_driver *role_drv;
> +	enum cdns3_roles current_role;
> +	struct cdns3 *cdns;
> +	int ret = 0;
> +
> +	cdns = container_of(work, struct cdns3, role_switch_wq);
> +
> +	/* During switching cdns->role can be different then role */
> +	role = cdsn3_get_real_role(cdns);
> +
> +	role_drv = cdns3_get_current_role_driver(cdns);
> +
> +	pm_runtime_get_sync(cdns->dev);
> +
> +	/* Disable current role. This state can be forced from user space. */
> +	if (cdns->debug_disable && role_drv->state == CDNS3_ROLE_STATE_ACTIVE) {
> +		cdns3_role_stop(cdns);
> +		goto exit;
> +	}
> +
> +	/* Do nothing if nothing changed */
> +	if (cdns->role == role && role_drv->state == CDNS3_ROLE_STATE_ACTIVE)
> +		goto exit;
> +
> +	cdns3_role_stop(cdns);
> +
> +	role = cdsn3_get_real_role(cdns);
> +
> +	current_role = cdns->role;
> +	dev_dbg(cdns->dev, "Switching role");
> +
> +	ret = cdns3_role_start(cdns, role);
> +
remove blank line
> +	if (ret) {
> +		/* Back to current role */
> +		dev_err(cdns->dev, "set %d has failed, back to %d\n",
> +			role, current_role);
> +		cdns3_role_start(cdns, current_role);
> +	}
> +exit:
> +	pm_runtime_put_sync(cdns->dev);
> +}
> +
> +/**
> + * cdns3_probe - probe for cdns3 core device
> + * @pdev: Pointer to cdns3 core platform device
> + *
> + * Returns 0 on success otherwise negative errno
> + */
> +static int cdns3_probe(struct platform_device *pdev)
> +{
> +	struct device *dev = &pdev->dev;
> +	struct resource	*res;
> +	struct cdns3 *cdns;
> +	void __iomem *regs;
> +	int ret;
> +
> +	cdns = devm_kzalloc(dev, sizeof(*cdns), GFP_KERNEL);
> +	if (!cdns)
> +		return -ENOMEM;
> +
> +	cdns->dev = dev;
> +
> +	platform_set_drvdata(pdev, cdns);
> +
> +	res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
> +	if (!res) {
> +		dev_err(dev, "missing IRQ\n");
> +		return -ENODEV;
> +	}
> +	cdns->irq = res->start;
> +
> +	cdns->xhci_res[0] = *res;
> +
> +	/*
> +	 * Request memory region
> +	 * region-0: xHCI
> +	 * region-1: Peripheral
> +	 * region-2: OTG registers
> +	 */
> +	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +	cdns->xhci_res[1] = *res;
> +
> +	res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
> +	regs = devm_ioremap_resource(dev, res);
> +	if (IS_ERR(regs))
> +		return PTR_ERR(regs);
> +	cdns->dev_regs	= regs;
> +
> +	res = platform_get_resource(pdev, IORESOURCE_MEM, 2);
> +	cdns->otg_res = *res;
It'll make the DTS clearer if add reg-names
> +
> +	mutex_init(&cdns->mutex);
> +
> +	cdns->phy = devm_phy_get(dev, "cdns3,usbphy");
Try to use devm_phy_optional_get()?
> +	if (IS_ERR(cdns->phy)) {
> +		ret = PTR_ERR(cdns->phy);
> +		if (ret == -ENODEV) {
> +			cdns->phy = NULL;
> +		} else if (ret == -EPROBE_DEFER) {
> +			return ret;
> +		} else {
> +			dev_err(dev, "no phy found\n");
> +			goto err0;
> +		}
> +	}
> +
> +	phy_init(cdns->phy);
> +
> +	INIT_WORK(&cdns->role_switch_wq, cdns3_role_switch);
> +
> +	ret = cdns3_drd_init(cdns);
> +	if (ret)
> +		goto err1;
> +
> +	ret = cdns3_core_init_role(cdns);
> +	if (ret)
> +		goto err1;
> +
> +	cdns3_debugfs_init(cdns);
> +	device_set_wakeup_capable(dev, true);
> +	pm_runtime_set_active(dev);
> +	pm_runtime_enable(dev);
> +
> +	/*
> +	 * The controller needs less time between bus and controller suspend,
> +	 * and we also needs a small delay to avoid frequently entering low
> +	 * power mode.
> +	 */
> +	pm_runtime_set_autosuspend_delay(dev, 20);
> +	pm_runtime_mark_last_busy(dev);
> +	pm_runtime_use_autosuspend(dev);
> +	dev_dbg(dev, "Cadence USB3 core: probe succeed\n");
> +
> +	return 0;
> +
> +err1:
> +	phy_exit(cdns->phy);
> +err0:
> +	return ret;
> +}
> +
> +/**
> + * cdns3_remove - unbind drd driver and clean up
> + * @pdev: Pointer to Linux platform device
> + *
> + * Returns 0 on success otherwise negative errno
> + */
> +static int cdns3_remove(struct platform_device *pdev)
> +{
> +	struct cdns3 *cdns = platform_get_drvdata(pdev);
> +
> +	pm_runtime_get_sync(&pdev->dev);
> +	pm_runtime_disable(&pdev->dev);
> +	pm_runtime_put_noidle(&pdev->dev);
> +	cdns3_debugfs_exit(cdns);
> +	cdns3_exit_roles(cdns);
> +	phy_exit(cdns->phy);
> +	return 0;
> +}
> +
> +#ifdef CONFIG_OF
> +static const struct of_device_id of_cdns3_match[] = {
> +	{ .compatible = "cdns,usb3-1.0.0" },
> +	{ .compatible = "cdns,usb3-1.0.1" },
> +	{ },
> +};
> +MODULE_DEVICE_TABLE(of, of_cdns3_match);
> +#endif
> +
> +static struct platform_driver cdns3_driver = {
> +	.probe		= cdns3_probe,
> +	.remove		= cdns3_remove,
> +	.driver		= {
> +		.name	= "cdns-usb3",
> +		.of_match_table	= of_match_ptr(of_cdns3_match),
> +	},
> +};
> +
> +module_platform_driver(cdns3_driver);
> +
> +MODULE_ALIAS("platform:cdns3");
> +MODULE_AUTHOR("Pawel Laszczak <pawell@cadence.com>");
> +MODULE_LICENSE("GPL v2");
> +MODULE_DESCRIPTION("Cadence USB3 DRD Controller Driver");
> diff --git a/drivers/usb/cdns3/core.h b/drivers/usb/cdns3/core.h
> new file mode 100644
> index 000000000000..fb4b39206158
> --- /dev/null
> +++ b/drivers/usb/cdns3/core.h
> @@ -0,0 +1,116 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +/*
> + * Cadence USBSS DRD Header File.
> + *
> + * Copyright (C) 2017-2018 NXP
> + * Copyright (C) 2018 Cadence.
> + *
> + * Authors: Peter Chen <peter.chen@nxp.com>
> + *          Pawel Laszczak <pawell@cadence.com>
> + */
> +#include <linux/usb/otg.h>
> +
> +#ifndef __LINUX_CDNS3_CORE_H
> +#define __LINUX_CDNS3_CORE_H
> +
> +struct cdns3;
> +enum cdns3_roles {
> +	CDNS3_ROLE_HOST = 0,
> +	CDNS3_ROLE_GADGET,
> +	CDNS3_ROLE_END,
> +};
> +
> +/**
> + * struct cdns3_role_driver - host/gadget role driver
> + * @start: start this role
> + * @stop: stop this role
> + * @suspend: suspend callback for this role
> + * @resume: resume callback for this role
> + * @irq: irq handler for this role
> + * @name: role name string (host/gadget)
> + * @state: current state
> + */
> +struct cdns3_role_driver {
> +	int (*start)(struct cdns3 *cdns);
> +	void (*stop)(struct cdns3 *cdns);
> +	int (*suspend)(struct cdns3 *cdns, bool do_wakeup);
> +	int (*resume)(struct cdns3 *cdns, bool hibernated);
> +	const char *name;
> +#define CDNS3_ROLE_STATE_INACTIVE	0
> +#define CDNS3_ROLE_STATE_ACTIVE		1
> +	int state;
> +};
> +
> +#define CDNS3_XHCI_RESOURCES_NUM	2
> +/**
> + * struct cdns3 - Representation of Cadence USB3 DRD controller.
> + * @dev: pointer to Cadence device struct
> + * @xhci_regs: pointer to base of xhci registers
> + * @xhci_res: the resource for xhci
> + * @dev_regs: pointer to base of dev registers
> + * @otg_regs: pointer to base of otg registers
> + * @irq: irq number for controller
> + * @roles: array of supported roles for this controller
> + * @role: current role
> + * @host_dev: the child host device pointer for cdns3 core
> + * @gadget_dev: the child gadget device pointer for cdns3 core
> + * @usb: phy for this controller
> + * @role_switch_wq: work queue item for role switch
> + * @in_lpm: the controller in low power mode
> + * @wakeup_int: the wakeup interrupt
> + * @mutex: the mutex for concurrent code at driver
> + * @dr_mode: supported mode of operation it can be only Host, only Device
> + *           or OTG mode that allow to switch between Device and Host mode.
> + *           This field based on firmware setting, kernel configuration
> + *           and hardware configuration.
> + * @current_dr_mode: current mode of operation when in dual-role mode
> + * @desired_dr_mode: desired mode of operation when in dual-role mode.
> + *           This value can be changed during runtime.
> + *           Available options depends on  dr_mode:
> + *           dr_mode                 |  desired_dr_mode and current_dr_mode
> + *           ----------------------------------------------------------------
> + *           USB_DR_MODE_HOST        | only USB_DR_MODE_HOST
> + *           USB_DR_MODE_PERIPHERAL  | only USB_DR_MODE_PERIPHERAL
> + *           USB_DR_MODE_OTG         | only USB_DR_MODE_HOST
> + *           USB_DR_MODE_OTG         | only USB_DR_MODE_PERIPHERAL
> + *           USB_DR_MODE_OTG         | USB_DR_MODE_OTG
> + *
> + *           Desired_dr_role can be changed by means of debugfs.
> + * @root: debugfs root folder pointer
> + * @debug_disable:
> + */
> +struct cdns3 {
> +	struct device			*dev;
> +	void __iomem			*xhci_regs;
> +	struct resource			xhci_res[CDNS3_XHCI_RESOURCES_NUM];
> +	struct cdns3_usb_regs __iomem	*dev_regs;
> +
> +	struct resource			otg_res;
> +	struct cdns3_otg_legacy_regs	*otg_v0_regs;
> +	struct cdns3_otg_regs		*otg_v1_regs;
> +	struct cdns3_otg_common_regs	*otg_regs;
> +#define CDNS3_CONTROLLER_V0	0
> +#define CDNS3_CONTROLLER_V1	1
> +	u32				version;
> +
> +	int				irq;
> +	struct cdns3_role_driver	*roles[CDNS3_ROLE_END];
> +	enum cdns3_roles		role;
> +	struct platform_device		*host_dev;
> +	struct cdns3_device		*gadget_dev;
> +	struct phy			*phy;
> +	struct work_struct		role_switch_wq;
> +	int				in_lpm:1;
> +	int				wakeup_int:1;
> +	/* mutext used in workqueue*/
> +	struct mutex			mutex;
> +	enum usb_dr_mode		dr_mode;
> +	enum usb_dr_mode		current_dr_mode;
> +	enum usb_dr_mode		desired_dr_mode;
> +	struct dentry			*root;
> +	int				debug_disable:1;
> +};
> +
> +void cdns3_role_stop(struct cdns3 *cdns);
> +
> +#endif /* __LINUX_CDNS3_CORE_H */
> diff --git a/drivers/usb/cdns3/debug.h b/drivers/usb/cdns3/debug.h
> new file mode 100644
> index 000000000000..94f9ef15f899
> --- /dev/null
> +++ b/drivers/usb/cdns3/debug.h
> @@ -0,0 +1,166 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +/*
> + * Cadence USBSS DRD Driver.
> + * Debug header file.
> + *
> + * Copyright (C) 2018 Cadence.
> + *
> + * Author: Pawel Laszczak <pawell@cadence.com>
> + */
> +#ifndef __LINUX_CDNS3_DEBUG
> +#define __LINUX_CDNS3_DEBUG
> +
> +#include "core.h"
> +
> +static inline char *cdns3_decode_usb_irq(char *str,
> +					 enum usb_device_speed speed,
> +
too many sentences as inline function
> 					 u32 usb_ists)
> +{
> +	int ret;
> +
> +	ret = sprintf(str, "IRQ %08x = ", usb_ists);
> +
> +	if (usb_ists & (USB_ISTS_CON2I | USB_ISTS_CONI)) {
> +		ret += sprintf(str + ret, "Connection %s\n",
> +			       usb_speed_string(speed));
> +	}
> +	if (usb_ists & USB_ISTS_CON2I || usb_ists & USB_ISTS_CONI)
> +		ret += sprintf(str + ret, "Disconnection ");
> +	if (usb_ists & USB_ISTS_L2ENTI)
> +		ret += sprintf(str + ret, "suspended ");
> +
> +	if (usb_ists & USB_ISTS_L2EXTI)
> +		ret += sprintf(str + ret, "L2 exit ");
> +	if (usb_ists & USB_ISTS_U3EXTI)
> +		ret += sprintf(str + ret, "U3 exit ");
> +	if (usb_ists & USB_ISTS_UWRESI)
> +		ret += sprintf(str + ret, "Warm Reset ");
> +	if (usb_ists & USB_ISTS_UHRESI)
> +		ret += sprintf(str + ret, "Hot Reset ");
> +	if (usb_ists & USB_ISTS_U2RESI)
> +		ret += sprintf(str + ret, "Reset");
> +
> +	return str;
> +}
> +
> +static inline  char *cdns3_decode_ep_irq(char *str,
> +					 u32 ep_sts,
> +					 const char *ep_name)
ditto
> +{
> +	int ret;
> +
> +	ret = sprintf(str, "IRQ for %s: %08x ", ep_name, ep_sts);
> +
> +	if (ep_sts & EP_STS_SETUP)
> +		ret += sprintf(str + ret, "SETUP ");
> +	if (ep_sts & EP_STS_IOC)
> +		ret += sprintf(str + ret, "IOC ");
> +	if (ep_sts & EP_STS_ISP)
> +		ret += sprintf(str + ret, "ISP ");
> +	if (ep_sts & EP_STS_DESCMIS)
> +		ret += sprintf(str + ret, "DESCMIS ");
> +	if (ep_sts & EP_STS_STREAMR)
> +		ret += sprintf(str + ret, "STREAMR ");
> +	if (ep_sts & EP_STS_MD_EXIT)
> +		ret += sprintf(str + ret, "MD_EXIT ");
> +	if (ep_sts & EP_STS_TRBERR)
> +		ret += sprintf(str + ret, "TRBERR ");
> +	if (ep_sts & EP_STS_NRDY)
> +		ret += sprintf(str + ret, "NRDY ");
> +	if (ep_sts & EP_STS_PRIME)
> +		ret += sprintf(str + ret, "PRIME ");
> +	if (ep_sts & EP_STS_SIDERR)
> +		ret += sprintf(str + ret, "SIDERRT ");
> +	if (ep_sts & EP_STS_OUTSMM)
> +		ret += sprintf(str + ret, "OUTSMM ");
> +	if (ep_sts & EP_STS_ISOERR)
> +		ret += sprintf(str + ret, "ISOERR ");
> +	if (ep_sts & EP_STS_IOT)
> +		ret += sprintf(str + ret, "IOT ");
> +
> +	return str;
> +}
> +
> +static inline char *cdns3_decode_epx_irq(char *str,
> +					 char *ep_name,
> +					 u32 ep_sts)
> +{
> +	return cdns3_decode_ep_irq(str, ep_sts, ep_name);
> +}
> +
> +static inline char *cdns3_decode_ep0_irq(char *str,
> +					 int dir,
> +					 u32 ep_sts)
> +{
> +	return cdns3_decode_ep_irq(str, ep_sts,
> +				   dir ? "ep0IN" : "ep0OUT");
> +}
> +
> +/**
> + * Debug a transfer ring.
> + *
> + * Prints out all TRBs in the endpoint ring, even those after the Link TRB.
> + *.
> + */
> +static inline char *cdns3_dbg_ring(struct cdns3_endpoint *priv_ep,
> +				   struct cdns3_trb *ring, char *str)
ditto
> +{
> +	dma_addr_t addr = priv_ep->trb_pool_dma;
> +	struct cdns3_trb *trb;
> +	int trb_per_sector;
> +	int ret = 0;
> +	int i;
> +
> +	trb_per_sector = GET_TRBS_PER_SEGMENT(priv_ep->type);
> +
> +	trb = &priv_ep->trb_pool[priv_ep->dequeue];
> +	ret += sprintf(str + ret, "\n\t\tRing contents for %s:", priv_ep->name);
> +
> +	ret += sprintf(str + ret,
> +		       "\n\t\tRing deq index: %d, trb: %p (virt), 0x%llx (dma)\n",
> +		       priv_ep->dequeue, trb,
> +		       (unsigned long long)cdns3_trb_virt_to_dma(priv_ep, trb));
> +
> +	trb = &priv_ep->trb_pool[priv_ep->enqueue];
> +	ret += sprintf(str + ret,
> +		       "\t\tRing enq index: %d, trb: %p (virt), 0x%llx (dma)\n",
> +		       priv_ep->enqueue, trb,
> +		       (unsigned long long)cdns3_trb_virt_to_dma(priv_ep, trb));
> +
> +	ret += sprintf(str + ret,
> +		       "\t\tfree trbs: %d, CCS=%d, PCS=%d\n",
> +		       priv_ep->free_trbs, priv_ep->ccs, priv_ep->pcs);
> +
> +	if (trb_per_sector > TRBS_PER_SEGMENT)
> +		trb_per_sector = TRBS_PER_SEGMENT;
> +
> +	if (trb_per_sector > TRBS_PER_SEGMENT) {
> +		sprintf(str + ret, "\t\tTo big transfer ring %d\n",
> +			trb_per_sector);
> +		return str;
> +	}
> +
> +	for (i = 0; i < trb_per_sector; ++i) {
> +		trb = &ring[i];
> +		ret += sprintf(str + ret,
> +			"\t\t@%pad %08x %08x %08x\n", &addr,
> +			le32_to_cpu(trb->buffer),
> +			le32_to_cpu(trb->length),
> +			le32_to_cpu(trb->control));
> +		addr += sizeof(*trb);
> +	}
> +
> +	return str;
> +}
> +
> +#ifdef CONFIG_DEBUG_FS
> +void cdns3_debugfs_init(struct cdns3 *cdns);
> +void cdns3_debugfs_exit(struct cdns3 *cdns);
> +#else
> +void cdns3_debugfs_init(struct cdns3 *cdns);
> +{  }
> +void cdns3_debugfs_exit(struct cdns3 *cdns);
> +{  }
> +#endif
> +
> +#endif /*__LINUX_CDNS3_DEBUG*/
> diff --git a/drivers/usb/cdns3/debugfs.c b/drivers/usb/cdns3/debugfs.c
> new file mode 100644
> index 000000000000..d7919f5c1d90
> --- /dev/null
> +++ b/drivers/usb/cdns3/debugfs.c
> @@ -0,0 +1,168 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Cadence USBSS DRD Controller DebugFS filer.
> + *
> + * Copyright (C) 2018 Cadence.
> + *
> + * Author: Pawel Laszczak <pawell@cadence.com>
> + */
> +
> +#include <linux/types.h>
> +#include <linux/debugfs.h>
> +#include <linux/seq_file.h>
> +#include <linux/uaccess.h>
> +
> +#include "core.h"
> +#include "gadget.h"
> +#include "drd.h"
> +
> +static int cdns3_mode_show(struct seq_file *s, void *unused)
> +{
> +	struct cdns3 *cdns = s->private;
> +
> +	switch (cdns->current_dr_mode) {
> +	case USB_DR_MODE_HOST:
> +		seq_puts(s, "host\n");
> +		break;
> +	case USB_DR_MODE_PERIPHERAL:
> +		seq_puts(s, "device\n");
> +		break;
> +	case USB_DR_MODE_OTG:
> +		seq_puts(s, "otg\n");
> +		break;
> +	default:
> +		seq_puts(s, "UNKNOWN mode\n");
> +	}
> +
> +	return 0;
> +}
> +
> +static int cdns3_mode_open(struct inode *inode, struct file *file)
> +{
> +	return single_open(file, cdns3_mode_show, inode->i_private);
> +}
> +
> +static ssize_t cdns3_mode_write(struct file *file,
> +				const char __user *ubuf,
> +				size_t count, loff_t *ppos)
> +{
> +	struct seq_file	 *s = file->private_data;
> +	struct cdns3 *cdns = s->private;
> +	u32 mode = USB_DR_MODE_UNKNOWN;
> +	char buf[32];
> +
> +	if (copy_from_user(&buf, ubuf, min_t(size_t, sizeof(buf) - 1, count)))
> +		return -EFAULT;
> +
> +	if (!strncmp(buf, "host", 4)) {
> +		if (cdns->dr_mode == USB_DR_MODE_HOST ||
> +		    cdns->dr_mode == USB_DR_MODE_OTG) {
> +			mode = USB_DR_MODE_HOST;
> +		}
> +	}
> +
> +	if (!strncmp(buf, "device", 6))
> +		if (cdns->dr_mode == USB_DR_MODE_PERIPHERAL ||
> +		    cdns->dr_mode == USB_DR_MODE_OTG)
> +			mode = USB_DR_MODE_PERIPHERAL;
> +
> +	if (!strncmp(buf, "otg", 3) && cdns->dr_mode == USB_DR_MODE_OTG)
> +		mode = USB_DR_MODE_OTG;
> +
> +	if (mode == USB_DR_MODE_UNKNOWN) {
> +		dev_err(cdns->dev, "Failed: incorrect mode setting\n");
> +		return -EFAULT;
> +	}
> +
> +	if (cdns->current_dr_mode != mode) {
> +		cdns->desired_dr_mode = mode;
> +		cdns->debug_disable = 0;
> +		cdns3_role_stop(cdns);
> +		cdns3_drd_update_mode(cdns);
> +		queue_work(system_freezable_wq, &cdns->role_switch_wq);
> +	}
> +
> +	return count;
> +}
> +
> +static const struct file_operations cdns3_mode_fops = {
> +	.open			= cdns3_mode_open,
> +	.write			= cdns3_mode_write,
> +	.read			= seq_read,
> +	.llseek			= seq_lseek,
> +	.release		= single_release,
> +};
> +
> +static int cdns3_disable_show(struct seq_file *s, void *unused)
> +{
> +	struct cdns3 *cdns = s->private;
> +
> +	if (!cdns->debug_disable)
> +		seq_puts(s, "0\n");
> +	else
> +		seq_puts(s, "1\n");
> +
> +	return 0;
> +}
> +
> +static ssize_t cdns3_disable_write(struct file *file,
> +				   const char __user *ubuf,
> +				   size_t count, loff_t *ppos)
> +{
> +	struct seq_file	 *s = file->private_data;
> +	struct cdns3 *cdns = s->private;
> +	int disable;
> +	char buf[32];
> +
> +	if (copy_from_user(&buf, ubuf, min_t(size_t, sizeof(buf) - 1, count)))
> +		return -EFAULT;
> +
> +	if (!strncmp(buf, "1", 1) || !strncmp(buf, "yes", 3)) {
> +		disable = 1;
> +	} else if (!strncmp(buf, "0", 1) || !strncmp(buf, "no", 2)) {
> +		disable = 0;
> +	} else {
> +		dev_err(cdns->dev, "Failed: incorrect disable setting\n");
> +		return -EFAULT;
> +	}
> +
There is common API kstrtobool()?

> +	if (disable != cdns->debug_disable) {
> +		cdns->debug_disable = disable;
> +		queue_work(system_freezable_wq, &cdns->role_switch_wq);
> +	}
> +
> +	return count;
> +}
> +
> +static int cdns3_disable_open(struct inode *inode, struct file *file)
> +{
> +	return single_open(file, cdns3_disable_show, inode->i_private);
> +}
> +
> +static const struct file_operations cdns3_disable_fops = {
> +	.open			= cdns3_disable_open,
> +	.write			= cdns3_disable_write,
> +	.read			= seq_read,
> +	.llseek			= seq_lseek,
> +	.release		= single_release,
> +};
> +
> +void cdns3_debugfs_init(struct cdns3 *cdns)
> +{
> +	struct dentry *root;
> +
> +	root = debugfs_create_dir(dev_name(cdns->dev), NULL);
> +	cdns->root = root;
> +	if (IS_ENABLED(CONFIG_USB_CDNS3_GADGET) &&
> +	    IS_ENABLED(CONFIG_USB_CDNS3_HOST))
> +		debugfs_create_file("mode", 0644, root, cdns,
> +				    &cdns3_mode_fops);
> +
> +	debugfs_create_file("disable", 0644, root, cdns,
> +			    &cdns3_disable_fops);
> +}
> +
> +void cdns3_debugfs_exit(struct cdns3 *cdns)
> +{
> +	debugfs_remove_recursive(cdns->root);
> +}
> diff --git a/drivers/usb/cdns3/drd.c b/drivers/usb/cdns3/drd.c
> new file mode 100644
> index 000000000000..b0c32302eb0b
> --- /dev/null
> +++ b/drivers/usb/cdns3/drd.c
> @@ -0,0 +1,350 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Cadence USBSS DRD Driver.
> + *
> + * Copyright (C) 2018 Cadence.
> + *
> + * Author: Pawel Laszczak <pawell@cadence.com
> + *
> + */
> +#include <linux/kernel.h>
> +#include <linux/interrupt.h>
> +#include <linux/delay.h>
> +#include <linux/usb/otg.h>
> +
> +#include "gadget.h"
> +#include "drd.h"
> +#include "core.h"
> +
> +static int cdns3_drd_switch_gadget(struct cdns3 *cdns, int on);
> +static int cdns3_drd_switch_host(struct cdns3 *cdns, int on);
> +
> +/**
> + * cdns3_set_mode - change mode of OTG Core
> + * @cdns: pointer to context structure
> + * @mode: selected mode from cdns_role
> + */
> +void cdns3_set_mode(struct cdns3 *cdns, enum usb_dr_mode mode)
> +{
> +	u32 reg;
> +
> +	cdns->current_dr_mode = mode;
> +
> +	switch (mode) {
> +	case USB_DR_MODE_PERIPHERAL:
> +		dev_info(cdns->dev, "Set controller to Gadget mode\n");
> +		cdns3_drd_switch_gadget(cdns, 1);
> +		break;
> +	case USB_DR_MODE_HOST:
> +		dev_info(cdns->dev, "Set controller to Host mode\n");
> +		cdns3_drd_switch_host(cdns, 1);
> +		break;
> +	case USB_DR_MODE_OTG:
> +		dev_info(cdns->dev, "Set controller to OTG mode\n");
> +		if (cdns->version == CDNS3_CONTROLLER_V1) {
> +			reg = readl(&cdns->otg_v1_regs->override);
> +			reg |= OVERRIDE_IDPULLUP;
> +			writel(reg, &cdns->otg_v1_regs->override);
> +		} else {
> +			reg = readl(&cdns->otg_v0_regs->ctrl1);
> +			reg |= OVERRIDE_IDPULLUP_V0;
> +			writel(reg, &cdns->otg_v0_regs->ctrl1);
> +		}
> +
> +		/*
> +		 * Hardware specification says: "ID_VALUE must be valid within
> +		 * 50ms after idpullup is set to '1" so driver must wait
> +		 * 50ms before reading this pin.
> +		 */
> +		usleep_range(50000, 60000);
> +		break;
> +	default:
> +		cdns->current_dr_mode = USB_DR_MODE_UNKNOWN;
> +		dev_err(cdns->dev, "Unsupported mode of operation %d\n", mode);
> +		return;
> +	}
> +}
> +
> +int cdns3_get_id(struct cdns3 *cdns)
> +{
> +	int id;
> +
> +	id = readl(&cdns->otg_regs->sts) & OTGSTS_ID_VALUE;
> +	dev_dbg(cdns->dev, "OTG ID: %d", id);
> +	return  1 ; //id;
return fixed value ?
> +}
> +
> +int cdns3_is_host(struct cdns3 *cdns)
> +{
> +	if (cdns->current_dr_mode == USB_DR_MODE_HOST)
> +		return 1;
> +	else if (!cdns3_get_id(cdns))
> +		return 1;
> +
> +	return 0;
> +}
> +
> +

> +
> +/**
> + * cdns3_init_otg_mode - initialize drd controller
> + * @cdns: Pointer to controller context structure
> + *
> + * Returns 0 on success otherwise negative errno
void function
> + */
> +static void cdns3_init_otg_mode(struct cdns3 *cdns)
> +{
> +	cdns3_otg_disable_irq(cdns);
> +	/* clear all interrupts */
> +	writel(~0, &cdns->otg_regs->ivect);
> +
> +	cdns3_set_mode(cdns, USB_DR_MODE_OTG);
> +
> +	if (cdns3_is_host(cdns))
> +		cdns3_drd_switch_host(cdns, 1);
> +	else
> +		cdns3_drd_switch_gadget(cdns, 1);
> +
> +	cdns3_otg_enable_irq(cdns);
> +}
> +
> +/**
> + * cdns3_drd_update_mode - initialize mode of operation
> + * @cdns: Pointer to controller context structure
> + *
> + * Returns 0 on success otherwise negative errno
> + */
> +int cdns3_drd_update_mode(struct cdns3 *cdns)
> +{
> +	int ret = 0;
> +
> +	if (cdns->desired_dr_mode == cdns->current_dr_mode)
> +		return ret;
> +
> +	cdns3_drd_switch_gadget(cdns, 0);
> +	cdns3_drd_switch_host(cdns, 0);
> +
> +	switch (cdns->desired_dr_mode) {
> +	case USB_DR_MODE_PERIPHERAL:
> +		cdns3_set_mode(cdns, USB_DR_MODE_PERIPHERAL);
> +		break;
> +	case USB_DR_MODE_HOST:
> +		cdns3_set_mode(cdns, USB_DR_MODE_HOST);
> +		break;
> +	case USB_DR_MODE_OTG:
> +		cdns3_init_otg_mode(cdns);
> +		break;
> +	default:
> +		dev_err(cdns->dev, "Unsupported mode of operation %d\n",
> +			cdns->dr_mode);
> +		return -EINVAL;
> +	}
> +
> +	return ret;
no need define local variable @ret
> +}
> +
> +/**
> + * cdns3_drd_irq - interrupt handler for OTG events
> + *
> + * @irq: irq number for cdns3 core device
> + * @data: structure of cdns3
> + *
> + * Returns IRQ_HANDLED or IRQ_NONE
> + */
> +static irqreturn_t cdns3_drd_irq(int irq, void *data)
> +{
> +	irqreturn_t ret = IRQ_NONE;
> +	struct cdns3 *cdns = data;
> +	u32 reg;
> +
> +	if (cdns->dr_mode != USB_DR_MODE_OTG)
> +		return ret;
> +
> +	reg = readl(&cdns->otg_regs->ivect);
> +
> +	if (!reg)
> +		return ret;
> +
> +	if (reg & OTGIEN_ID_CHANGE_INT) {
> +		dev_dbg(cdns->dev, "OTG IRQ: new ID: %d\n",
> +			cdns3_get_id(cdns));
> +
> +		queue_work(system_freezable_wq, &cdns->role_switch_wq);
> +
do you need to clear interrupts status here?
> +		ret = IRQ_HANDLED;
> +	}
> +
> +	writel(~0, &cdns->otg_regs->ivect);
> +	return ret;
> +}
> +
> +int cdns3_drd_init(struct cdns3 *cdns)
> +{
> +	void __iomem *regs;
> +	int ret = 0;
> +	u32 state;
> +
> +	regs = devm_ioremap_resource(cdns->dev, &cdns->otg_res);
> +	if (IS_ERR(regs))
> +		return PTR_ERR(regs);
> +
> +	/* Detection of DRD version. Controller has been released
> +	 * in two versions. Both are similar, but they have same changes
> +	 * in register maps.
> +	 * The first register in old version is command register and it's read
> +	 * only, so driver should read 0 from it. On the other hand, in v1
> +	 * the first register contains device ID number which is not set to 0.
> +	 * Driver uses this fact to detect the proper version of
> +	 * controller.
> +	 */
> +	cdns->otg_v0_regs = regs;
> +	if (!readl(&cdns->otg_v0_regs->cmd)) {
> +		cdns->version  = CDNS3_CONTROLLER_V0;
> +		cdns->otg_v1_regs = NULL;
> +		cdns->otg_regs = regs;
> +		dev_info(cdns->dev, "DRD version v0 (%08x)\n",
> +			 readl(&cdns->otg_v0_regs->version));
> +	} else {
> +		cdns->otg_v0_regs = NULL;
> +		cdns->otg_v1_regs = regs;
> +		cdns->otg_regs = (void *)&cdns->otg_v1_regs->cmd;
> +		cdns->version  = CDNS3_CONTROLLER_V1;
> +		dev_info(cdns->dev, "DRD version v1 (ID: %08x, rev: %08x)\n",
> +			 readl(&cdns->otg_v1_regs->did),
> +			 readl(&cdns->otg_v1_regs->rid));
> +	}
> +
> +	state = OTGSTS_STRAP(readl(&cdns->otg_regs->sts));
> +
> +	/* Update dr_mode according to STRAP configuration. */
> +	cdns->dr_mode = USB_DR_MODE_OTG;
> +	if (state == OTGSTS_STRAP_HOST) {
> +		dev_info(cdns->dev, "Controller strapped to HOST\n");
> +		cdns->dr_mode = USB_DR_MODE_HOST;
> +	} else if (state == OTGSTS_STRAP_GADGET) {
> +		dev_info(cdns->dev, "Controller strapped to PERIPHERAL\n");
> +		cdns->dr_mode = USB_DR_MODE_PERIPHERAL;
> +	}
> +
> +	cdns->desired_dr_mode = cdns->dr_mode;
> +	cdns->current_dr_mode = USB_DR_MODE_UNKNOWN;
> +
> +	ret = devm_request_threaded_irq(cdns->dev, cdns->irq, cdns3_drd_irq,
> +					NULL, IRQF_SHARED,
> +					dev_name(cdns->dev), cdns);
> +
> +	if (ret)
> +		return ret;
> +
> +	state = readl(&cdns->otg_regs->sts);
> +	if (OTGSTS_OTG_NRDY(state) != 0) {
> +		dev_err(cdns->dev, "Cadence USB3 OTG device not ready\n");
> +		return -ENODEV;
> +	}
> +
> +	ret = cdns3_drd_update_mode(cdns);
> +
> +	return ret;
return cdns3_drd_update_mode();
> +}
> +
> +int cdns3_drd_exit(struct cdns3 *cdns)
> +{
> +	return cdns3_drd_switch_host(cdns, 0);
> +}


<...>

> diff --git a/drivers/usb/cdns3/ep0.c b/drivers/usb/cdns3/ep0.c
> new file mode 100644
> index 000000000000..89cf1cde1555
> --- /dev/null
> +++ b/drivers/usb/cdns3/ep0.c
> @@ -0,0 +1,896 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Cadence USBSS DRD Driver - gadget side.
> + *
> + * Copyright (C) 2018 Cadence Design Systems.
> + * Copyright (C) 2017-2018 NXP
> + *
> + * Authors: Pawel Jez <pjez@cadence.com>,
> + *          Pawel Laszczak <pawell@cadence.com>
> + *	    Peter Chen <peter.chen@nxp.com>
> + */
> +
> +#include <linux/usb/composite.h>
> +
> +#include "gadget.h"
> +#include "trace.h"
> +
> +static struct usb_endpoint_descriptor cdns3_gadget_ep0_desc = {
> +	.bLength = USB_DT_ENDPOINT_SIZE,
> +	.bDescriptorType = USB_DT_ENDPOINT,
> +	.bmAttributes =	USB_ENDPOINT_XFER_CONTROL,
> +};
> +
<...>
> +
> +
> +#endif /* __LINUX_CDNS3_GADGET_EXPORT */
> diff --git a/drivers/usb/cdns3/gadget.c b/drivers/usb/cdns3/gadget.c
> new file mode 100644
> index 000000000000..0d95eb00be37
> --- /dev/null
> +++ b/drivers/usb/cdns3/gadget.c
> @@ -0,0 +1,2102 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Cadence USBSS DRD Driver - gadget side.
> + *
> + * Copyright (C) 2018 Cadence Design Systems.
> + * Copyright (C) 2017-2018 NXP
> + *
> + * Authors: Pawel Jez <pjez@cadence.com>,
> + *          Pawel Laszczak <pawell@cadence.com>
> + *	    Peter Chen <peter.chen@nxp.com>
> + */
> +
> +#include <linux/dma-mapping.h>
> +#include <linux/usb/gadget.h>
> +
<...>

> +/**
> + * cdns3_next_request - returns next request from list
> + * @list: list containing requests
> + *
> + * Returns request or NULL if no requests in list
> + */
> +struct usb_request *cdns3_next_request(struct list_head *list)
> +{
> +	if (list_empty(list))
> +		return NULL;
No need check it, if list is empty, list_first_entry_or_null() will
return NULL
> +	return list_first_entry_or_null(list, struct usb_request, list);
> +}
> +
> +/**
> + * cdns3_next_priv_request - returns next request from list
> + * @list: list containing requests
> + *
> + * Returns request or NULL if no requests in list
> + */
> +struct cdns3_request *cdns3_next_priv_request(struct list_head *list)
> +{
> +	if (list_empty(list))
> +		return NULL;
ditto
> +	return list_first_entry_or_null(list, struct cdns3_request, list);
> +}
> +

<...>

^ permalink raw reply	[flat|nested] 49+ messages in thread

* [v2,5/5] usb:cdns3 Add Cadence USB3 DRD Driver
@ 2018-12-29  7:41     ` Chunfeng Yun
  0 siblings, 0 replies; 49+ messages in thread
From: Chunfeng Yun @ 2018-12-29  7:41 UTC (permalink / raw)
  To: Pawel Laszczak
  Cc: devicetree, gregkh, mark.rutland, linux-usb, hdegoede,
	heikki.krogerus, andy.shevchenko, robh+dt, rogerq, linux-kernel,
	adouglas, jbergsagel, nsekhar, nm, sureshp, peter.chen, pjez,
	kurahul

hi,
On Sun, 2018-12-23 at 15:13 +0000, Pawel Laszczak wrote:
> This patch introduce new Cadence USBSS DRD driver
> to linux kernel.

<...>

> diff --git a/drivers/usb/cdns3/cdns3-pci-wrap.c b/drivers/usb/cdns3/cdns3-pci-wrap.c
> new file mode 100644
> index 000000000000..e93179c45ece
> --- /dev/null
> +++ b/drivers/usb/cdns3/cdns3-pci-wrap.c
> @@ -0,0 +1,157 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Cadence USBSS PCI Glue driver
> + *
> + * Copyright (C) 2018 Cadence.
> + *
> + * Author: Pawel Laszczak <pawell@cadence.com>
> + */
> +
> +#include <linux/kernel.h>
> +#include <linux/module.h>
> +#include <linux/pci.h>
> +#include <linux/platform_device.h>
> +#include <linux/dma-mapping.h>
> +#include <linux/slab.h>
> +
> +struct cdns3_wrap {
> +	struct platform_device *plat_dev;
> +	struct pci_dev *hg_dev;
> +	struct resource dev_res[4];
> +};
> +
> +struct cdns3_wrap wrap;
> +
> +#define RES_IRQ_ID		0
> +#define RES_HOST_ID		1
> +#define RES_DEV_ID		2
> +#define RES_DRD_ID		3
> +
> +#define PCI_BAR_HOST		0
> +#define PCI_BAR_DEV		2
> +#define PCI_BAR_OTG		4
> +
> +#define PCI_DEV_FN_HOST_DEVICE	0
> +#define PCI_DEV_FN_OTG		1
> +
> +#define PCI_DRIVER_NAME		"cdns3-pci-usbss"
> +#define PLAT_DRIVER_NAME	"cdns-usb3"
> +
> +#define CDNS_VENDOR_ID 0x17cd
> +#define CDNS_DEVICE_ID 0x0100
> +
> +/**
> + * cdns3_pci_probe - Probe function for Cadence USB wrapper driver
> + * @pdev: platform device object
> + * @id: pci device id
> + *
> + * Returns 0 on success otherwise negative errno
> + */
> +static int cdns3_pci_probe(struct pci_dev *pdev,
> +			   const struct pci_device_id *id)
> +{
> +	struct platform_device_info plat_info;
> +	struct cdns3_wrap *wrap;
> +	struct resource *res;
> +	int err;
> +
> +	/*
> +	 * for GADGET/HOST PCI (devfn) function number is 0,
> +	 * for OTG PCI (devfn) function number is 1
> +	 */
> +	if (!id || pdev->devfn != PCI_DEV_FN_HOST_DEVICE)
> +		return -EINVAL;
> +
> +	err = pcim_enable_device(pdev);
> +	if (err) {
> +		dev_err(&pdev->dev, "Enabling PCI device has failed %d\n", err);
> +		return err;
> +	}
> +
> +	pci_set_master(pdev);
> +	wrap = devm_kzalloc(&pdev->dev, sizeof(*wrap), GFP_KERNEL);
> +	if (!wrap) {
> +		dev_err(&pdev->dev, "Failed to allocate memory\n");
> +		return -ENOMEM;
> +	}
> +
> +	/* function 0: host(BAR_0) + device(BAR_1) + otg(BAR_2)). */
> +	memset(wrap->dev_res, 0x00,
> +	       sizeof(struct resource) * ARRAY_SIZE(wrap->dev_res));
> +	dev_dbg(&pdev->dev, "Initialize Device resources\n");
> +	res = wrap->dev_res;
> +
> +	res[RES_DEV_ID].start = pci_resource_start(pdev, PCI_BAR_DEV);
> +	res[RES_DEV_ID].end =   pci_resource_end(pdev, PCI_BAR_DEV);
> +	res[RES_DEV_ID].name = "cdns3-dev-regs";
> +	res[RES_DEV_ID].flags = IORESOURCE_MEM;
> +	dev_dbg(&pdev->dev, "USBSS-DEV physical base addr: %pa\n",
> +		&res[RES_DEV_ID].start);
> +
> +	res[RES_HOST_ID].start = pci_resource_start(pdev, PCI_BAR_HOST);
> +	res[RES_HOST_ID].end = pci_resource_end(pdev, PCI_BAR_HOST);
> +	res[RES_HOST_ID].name = "cdns3-xhci-regs";
> +	res[RES_HOST_ID].flags = IORESOURCE_MEM;
> +	dev_dbg(&pdev->dev, "USBSS-XHCI physical base addr: %pa\n",
> +		&res[RES_HOST_ID].start);
> +
> +	res[RES_DRD_ID].start = pci_resource_start(pdev, PCI_BAR_OTG);
> +	res[RES_DRD_ID].end =   pci_resource_end(pdev, PCI_BAR_OTG);
> +	res[RES_DRD_ID].name = "cdns3-otg";
> +	res[RES_DRD_ID].flags = IORESOURCE_MEM;
> +	dev_dbg(&pdev->dev, "USBSS-DRD physical base addr: %pa\n",
> +		&res[RES_DRD_ID].start);
> +
> +	/* Interrupt common for both device and XHCI */
> +	wrap->dev_res[RES_IRQ_ID].start = pdev->irq;
> +	wrap->dev_res[RES_IRQ_ID].name = "cdns3-irq";
> +	wrap->dev_res[RES_IRQ_ID].flags = IORESOURCE_IRQ;
> +
> +	/* set up platform device info */
> +	memset(&plat_info, 0, sizeof(plat_info));
> +	plat_info.parent = &pdev->dev;
> +	plat_info.fwnode = pdev->dev.fwnode;
> +	plat_info.name = PLAT_DRIVER_NAME;
> +	plat_info.id = pdev->devfn;
> +	plat_info.res = wrap->dev_res;
> +	plat_info.num_res = ARRAY_SIZE(wrap->dev_res);
> +	plat_info.dma_mask = pdev->dma_mask;
> +
> +	/* register platform device */
> +	wrap->plat_dev = platform_device_register_full(&plat_info);
> +	if (IS_ERR(wrap->plat_dev)) {
> +		err = PTR_ERR(wrap->plat_dev);
> +		return err;
> +	}
return PTR_ERR(wrap->plat_dev);

> +
> +	pci_set_drvdata(pdev, wrap);
> +
> +	return err;
> +}
> +
> +void cdns3_pci_remove(struct pci_dev *pdev)
> +{
> +	struct cdns3_wrap *wrap = (struct cdns3_wrap *)pci_get_drvdata(pdev);
> +
> +	platform_device_unregister(wrap->plat_dev);
> +}
> +
> +static const struct pci_device_id cdns3_pci_ids[] = {
> +	{ PCI_DEVICE(CDNS_VENDOR_ID, CDNS_DEVICE_ID), },
> +	{ 0, }
> +};
> +
> +static struct pci_driver cdns3_pci_driver = {
> +	.name = PCI_DRIVER_NAME,
> +	.id_table = cdns3_pci_ids,
> +	.probe = cdns3_pci_probe,
> +	.remove = cdns3_pci_remove,
> +};
> +
> +module_pci_driver(cdns3_pci_driver);
> +MODULE_DEVICE_TABLE(pci, cdns3_pci_ids);
> +
> +MODULE_AUTHOR("Pawel Laszczak <pawell@cadence.com>");
> +MODULE_LICENSE("GPL v2");
> +MODULE_DESCRIPTION("Cadence USBSS PCI wrapperr");
> +
> diff --git a/drivers/usb/cdns3/core.c b/drivers/usb/cdns3/core.c
> new file mode 100644
> index 000000000000..d274586aca36
> --- /dev/null
> +++ b/drivers/usb/cdns3/core.c
> @@ -0,0 +1,406 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Cadence USBSS DRD Driver.
> + *
> + * Copyright (C) 2018 Cadence.
> + * Copyright (C) 2017-2018 NXP
> + *
> + * Author: Peter Chen <peter.chen@nxp.com>
> + *         Pawel Laszczak <pawell@cadence.com>
> + */
> +
> +#include <linux/module.h>
> +#include <linux/kernel.h>
> +#include <linux/platform_device.h>
> +#include <linux/interrupt.h>
> +#include <linux/io.h>
> +#include <linux/pm_runtime.h>
> +
> +#include "gadget.h"
> +#include "core.h"
> +#include "host-export.h"
> +#include "gadget-export.h"
> +#include "drd.h"
> +#include "debug.h"
> +
> +static inline
> +struct cdns3_role_driver *cdns3_get_current_role_driver(struct cdns3 *cdns)
> +{
> +	WARN_ON(cdns->role >= CDNS3_ROLE_END || !cdns->roles[cdns->role]);
> +	return cdns->roles[cdns->role];
> +}
> +
> +static inline int cdns3_role_start(struct cdns3 *cdns, enum cdns3_roles role)
remove inline
> +{
> +	int ret;
> +
> +	if (WARN_ON(role >= CDNS3_ROLE_END))
> +		return 0;
> +
> +	if (!cdns->roles[role])
> +		return -ENXIO;
> +
> +	if (cdns->roles[role]->state == CDNS3_ROLE_STATE_ACTIVE)
> +		return 0;
> +
> +	mutex_lock(&cdns->mutex);
> +	cdns->role = role;
> +	ret = cdns->roles[role]->start(cdns);
> +	if (!ret)
> +		cdns->roles[role]->state = CDNS3_ROLE_STATE_ACTIVE;
> +	mutex_unlock(&cdns->mutex);
> +	return ret;
> +}
> +
> +void cdns3_role_stop(struct cdns3 *cdns)
> +{
> +	enum cdns3_roles role = cdns->role;
> +
> +	if (role >= CDNS3_ROLE_END) {
> +		WARN_ON(role > CDNS3_ROLE_END);
> +		return;
> +	}
> +
> +	if (cdns->roles[role]->state == CDNS3_ROLE_STATE_INACTIVE)
> +		return;
> +
> +	mutex_lock(&cdns->mutex);
> +	cdns->roles[role]->stop(cdns);
> +	cdns->roles[role]->state = CDNS3_ROLE_STATE_INACTIVE;
> +	mutex_unlock(&cdns->mutex);
> +}
> +
> +/*
> + * cdns->role gets from cdns3_get_initial_role, and this API tells role at the
> + * runtime.
> + * If both roles are supported, the role is selected based on vbus/id.
> + * It could be read from OTG register or external connector.
> + * If only single role is supported, only one role structure
> + * is allocated, cdns->roles[CDNS3_ROLE_HOST] or cdns->roles[CDNS3_ROLE_GADGET].
> + */
> +static enum cdns3_roles cdns3_get_initial_role(struct cdns3 *cdns)
> +{
> +	if (cdns->roles[CDNS3_ROLE_HOST] && cdns->roles[CDNS3_ROLE_GADGET]) {
> +		if (cdns3_is_host(cdns))
> +			return CDNS3_ROLE_HOST;
> +		if (cdns3_is_device(cdns))
> +			return CDNS3_ROLE_GADGET;
> +	}
> +	return cdns->roles[CDNS3_ROLE_HOST]
> +		? CDNS3_ROLE_HOST
> +		: CDNS3_ROLE_GADGET;
> +}
> +
> +static void cdns3_exit_roles(struct cdns3 *cdns)
> +{
> +	cdns3_role_stop(cdns);
> +	cdns3_drd_exit(cdns);
> +}
> +
> +/**
> + * cdns3_core_init_role - initialize role of operation
> + * @cdns: Pointer to cdns3 structure
> + *
> + * Returns 0 on success otherwise negative errno
> + */
> +static int cdns3_core_init_role(struct cdns3 *cdns)
> +{
> +	struct device *dev = cdns->dev;
> +	enum usb_dr_mode best_dr_mode;
> +	enum usb_dr_mode dr_mode;
> +	int ret = 0;
> +
> +	dr_mode = usb_get_dr_mode(dev);
> +	cdns->role = CDNS3_ROLE_END;
> +
> +	/*
> +	 * If driver can't read mode by means of usb_get_dr_mdoe function then
> +	 * chooses mode according with Kernel configuration. This setting
> +	 * can be restricted later depending on strap pin configuration.
> +	 */
> +	if (dr_mode == USB_DR_MODE_UNKNOWN) {
> +		if (IS_ENABLED(CONFIG_USB_CDNS3_HOST) &&
> +		    IS_ENABLED(CONFIG_USB_CDNS3_GADGET))
> +			dr_mode = USB_DR_MODE_OTG;
> +		else if (IS_ENABLED(CONFIG_USB_CDNS3_HOST))
> +			dr_mode = USB_DR_MODE_HOST;
> +		else if (IS_ENABLED(CONFIG_USB_CDNS3_GADGET))
> +			dr_mode = USB_DR_MODE_PERIPHERAL;
> +	}
> +
> +	best_dr_mode = USB_DR_MODE_OTG;
> +
> +	if (dr_mode == USB_DR_MODE_OTG) {
> +		best_dr_mode = cdns->dr_mode;
> +	} else if (cdns->dr_mode == USB_DR_MODE_OTG) {
> +		best_dr_mode = dr_mode;
> +	} else if (cdns->dr_mode != dr_mode) {
> +		dev_err(dev, "Incorrect DRD configuration\n");
> +		return -EINVAL;
> +	}
> +
> +	dr_mode = best_dr_mode;
> +
> +	if (dr_mode == USB_DR_MODE_OTG || dr_mode == USB_DR_MODE_HOST) {
> +		ret = cdns3_host_init(cdns);
> +		if (ret) {
> +			dev_err(dev, "Host initialization failed with %d\n",
> +				ret);
> +			goto err;
> +		}
> +	}
> +
> +	if (dr_mode == USB_DR_MODE_OTG || dr_mode == USB_DR_MODE_PERIPHERAL) {
> +		ret = cdns3_gadget_init(cdns);
> +		if (ret) {
> +			dev_err(dev, "Device initialization failed with %d\n",
> +				ret);
> +			goto err;
> +		}
> +	}
> +
> +	cdns->desired_dr_mode = dr_mode;
> +	cdns->dr_mode = dr_mode;
> +	/*
> +	 * dr_mode could be change so DRD must update controller
> +	 * configuration
> +	 */
> +	ret = cdns3_drd_update_mode(cdns);
Do you need check ret value? if not, no need assign it
> +
> +	cdns->role = cdns3_get_initial_role(cdns);
> +
> +	ret = cdns3_role_start(cdns, cdns->role);
> +	if (ret) {
> +		dev_err(dev, "can't start %s role\n",
> +			cdns3_get_current_role_driver(cdns)->name);
> +		goto err;
> +	}
> +
> +	return ret;
> +err:
> +	cdns3_exit_roles(cdns);
> +	return ret;
> +}
> +
> +/**
> + * cdsn3_get_real_role - get real role of controller based on hardware settings.
> + * @cdns: Pointer to cdns3 structure
> + *
> + * Returns role
> + */
> +enum cdns3_roles cdsn3_get_real_role(struct cdns3 *cdns)
> +{
> +	enum cdns3_roles role = CDNS3_ROLE_END;
> +
> +	if (cdns->current_dr_mode == USB_DR_MODE_OTG) {
> +		if (cdns3_get_id(cdns))
> +			role = CDNS3_ROLE_GADGET;
> +		else
> +			role = CDNS3_ROLE_HOST;
> +	} else {
> +		if (cdns3_is_host(cdns))
> +			role = CDNS3_ROLE_HOST;
> +		if (cdns3_is_device(cdns))
> +			role = CDNS3_ROLE_GADGET;
> +	}
> +
> +	return role;
> +}
> +
> +/**
> + * cdns3_role_switch - work queue handler for role switch
> + *
> + * @work: work queue item structure
> + *
> + * Handles below events:
> + * - Role switch for dual-role devices
> + * - CDNS3_ROLE_GADGET <--> CDNS3_ROLE_END for peripheral-only devices
> + */
> +static void cdns3_role_switch(struct work_struct *work)
> +{
> +	enum cdns3_roles role = CDNS3_ROLE_END;
> +	struct cdns3_role_driver *role_drv;
> +	enum cdns3_roles current_role;
> +	struct cdns3 *cdns;
> +	int ret = 0;
> +
> +	cdns = container_of(work, struct cdns3, role_switch_wq);
> +
> +	/* During switching cdns->role can be different then role */
> +	role = cdsn3_get_real_role(cdns);
> +
> +	role_drv = cdns3_get_current_role_driver(cdns);
> +
> +	pm_runtime_get_sync(cdns->dev);
> +
> +	/* Disable current role. This state can be forced from user space. */
> +	if (cdns->debug_disable && role_drv->state == CDNS3_ROLE_STATE_ACTIVE) {
> +		cdns3_role_stop(cdns);
> +		goto exit;
> +	}
> +
> +	/* Do nothing if nothing changed */
> +	if (cdns->role == role && role_drv->state == CDNS3_ROLE_STATE_ACTIVE)
> +		goto exit;
> +
> +	cdns3_role_stop(cdns);
> +
> +	role = cdsn3_get_real_role(cdns);
> +
> +	current_role = cdns->role;
> +	dev_dbg(cdns->dev, "Switching role");
> +
> +	ret = cdns3_role_start(cdns, role);
> +
remove blank line
> +	if (ret) {
> +		/* Back to current role */
> +		dev_err(cdns->dev, "set %d has failed, back to %d\n",
> +			role, current_role);
> +		cdns3_role_start(cdns, current_role);
> +	}
> +exit:
> +	pm_runtime_put_sync(cdns->dev);
> +}
> +
> +/**
> + * cdns3_probe - probe for cdns3 core device
> + * @pdev: Pointer to cdns3 core platform device
> + *
> + * Returns 0 on success otherwise negative errno
> + */
> +static int cdns3_probe(struct platform_device *pdev)
> +{
> +	struct device *dev = &pdev->dev;
> +	struct resource	*res;
> +	struct cdns3 *cdns;
> +	void __iomem *regs;
> +	int ret;
> +
> +	cdns = devm_kzalloc(dev, sizeof(*cdns), GFP_KERNEL);
> +	if (!cdns)
> +		return -ENOMEM;
> +
> +	cdns->dev = dev;
> +
> +	platform_set_drvdata(pdev, cdns);
> +
> +	res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
> +	if (!res) {
> +		dev_err(dev, "missing IRQ\n");
> +		return -ENODEV;
> +	}
> +	cdns->irq = res->start;
> +
> +	cdns->xhci_res[0] = *res;
> +
> +	/*
> +	 * Request memory region
> +	 * region-0: xHCI
> +	 * region-1: Peripheral
> +	 * region-2: OTG registers
> +	 */
> +	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +	cdns->xhci_res[1] = *res;
> +
> +	res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
> +	regs = devm_ioremap_resource(dev, res);
> +	if (IS_ERR(regs))
> +		return PTR_ERR(regs);
> +	cdns->dev_regs	= regs;
> +
> +	res = platform_get_resource(pdev, IORESOURCE_MEM, 2);
> +	cdns->otg_res = *res;
It'll make the DTS clearer if add reg-names
> +
> +	mutex_init(&cdns->mutex);
> +
> +	cdns->phy = devm_phy_get(dev, "cdns3,usbphy");
Try to use devm_phy_optional_get()?
> +	if (IS_ERR(cdns->phy)) {
> +		ret = PTR_ERR(cdns->phy);
> +		if (ret == -ENODEV) {
> +			cdns->phy = NULL;
> +		} else if (ret == -EPROBE_DEFER) {
> +			return ret;
> +		} else {
> +			dev_err(dev, "no phy found\n");
> +			goto err0;
> +		}
> +	}
> +
> +	phy_init(cdns->phy);
> +
> +	INIT_WORK(&cdns->role_switch_wq, cdns3_role_switch);
> +
> +	ret = cdns3_drd_init(cdns);
> +	if (ret)
> +		goto err1;
> +
> +	ret = cdns3_core_init_role(cdns);
> +	if (ret)
> +		goto err1;
> +
> +	cdns3_debugfs_init(cdns);
> +	device_set_wakeup_capable(dev, true);
> +	pm_runtime_set_active(dev);
> +	pm_runtime_enable(dev);
> +
> +	/*
> +	 * The controller needs less time between bus and controller suspend,
> +	 * and we also needs a small delay to avoid frequently entering low
> +	 * power mode.
> +	 */
> +	pm_runtime_set_autosuspend_delay(dev, 20);
> +	pm_runtime_mark_last_busy(dev);
> +	pm_runtime_use_autosuspend(dev);
> +	dev_dbg(dev, "Cadence USB3 core: probe succeed\n");
> +
> +	return 0;
> +
> +err1:
> +	phy_exit(cdns->phy);
> +err0:
> +	return ret;
> +}
> +
> +/**
> + * cdns3_remove - unbind drd driver and clean up
> + * @pdev: Pointer to Linux platform device
> + *
> + * Returns 0 on success otherwise negative errno
> + */
> +static int cdns3_remove(struct platform_device *pdev)
> +{
> +	struct cdns3 *cdns = platform_get_drvdata(pdev);
> +
> +	pm_runtime_get_sync(&pdev->dev);
> +	pm_runtime_disable(&pdev->dev);
> +	pm_runtime_put_noidle(&pdev->dev);
> +	cdns3_debugfs_exit(cdns);
> +	cdns3_exit_roles(cdns);
> +	phy_exit(cdns->phy);
> +	return 0;
> +}
> +
> +#ifdef CONFIG_OF
> +static const struct of_device_id of_cdns3_match[] = {
> +	{ .compatible = "cdns,usb3-1.0.0" },
> +	{ .compatible = "cdns,usb3-1.0.1" },
> +	{ },
> +};
> +MODULE_DEVICE_TABLE(of, of_cdns3_match);
> +#endif
> +
> +static struct platform_driver cdns3_driver = {
> +	.probe		= cdns3_probe,
> +	.remove		= cdns3_remove,
> +	.driver		= {
> +		.name	= "cdns-usb3",
> +		.of_match_table	= of_match_ptr(of_cdns3_match),
> +	},
> +};
> +
> +module_platform_driver(cdns3_driver);
> +
> +MODULE_ALIAS("platform:cdns3");
> +MODULE_AUTHOR("Pawel Laszczak <pawell@cadence.com>");
> +MODULE_LICENSE("GPL v2");
> +MODULE_DESCRIPTION("Cadence USB3 DRD Controller Driver");
> diff --git a/drivers/usb/cdns3/core.h b/drivers/usb/cdns3/core.h
> new file mode 100644
> index 000000000000..fb4b39206158
> --- /dev/null
> +++ b/drivers/usb/cdns3/core.h
> @@ -0,0 +1,116 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +/*
> + * Cadence USBSS DRD Header File.
> + *
> + * Copyright (C) 2017-2018 NXP
> + * Copyright (C) 2018 Cadence.
> + *
> + * Authors: Peter Chen <peter.chen@nxp.com>
> + *          Pawel Laszczak <pawell@cadence.com>
> + */
> +#include <linux/usb/otg.h>
> +
> +#ifndef __LINUX_CDNS3_CORE_H
> +#define __LINUX_CDNS3_CORE_H
> +
> +struct cdns3;
> +enum cdns3_roles {
> +	CDNS3_ROLE_HOST = 0,
> +	CDNS3_ROLE_GADGET,
> +	CDNS3_ROLE_END,
> +};
> +
> +/**
> + * struct cdns3_role_driver - host/gadget role driver
> + * @start: start this role
> + * @stop: stop this role
> + * @suspend: suspend callback for this role
> + * @resume: resume callback for this role
> + * @irq: irq handler for this role
> + * @name: role name string (host/gadget)
> + * @state: current state
> + */
> +struct cdns3_role_driver {
> +	int (*start)(struct cdns3 *cdns);
> +	void (*stop)(struct cdns3 *cdns);
> +	int (*suspend)(struct cdns3 *cdns, bool do_wakeup);
> +	int (*resume)(struct cdns3 *cdns, bool hibernated);
> +	const char *name;
> +#define CDNS3_ROLE_STATE_INACTIVE	0
> +#define CDNS3_ROLE_STATE_ACTIVE		1
> +	int state;
> +};
> +
> +#define CDNS3_XHCI_RESOURCES_NUM	2
> +/**
> + * struct cdns3 - Representation of Cadence USB3 DRD controller.
> + * @dev: pointer to Cadence device struct
> + * @xhci_regs: pointer to base of xhci registers
> + * @xhci_res: the resource for xhci
> + * @dev_regs: pointer to base of dev registers
> + * @otg_regs: pointer to base of otg registers
> + * @irq: irq number for controller
> + * @roles: array of supported roles for this controller
> + * @role: current role
> + * @host_dev: the child host device pointer for cdns3 core
> + * @gadget_dev: the child gadget device pointer for cdns3 core
> + * @usb: phy for this controller
> + * @role_switch_wq: work queue item for role switch
> + * @in_lpm: the controller in low power mode
> + * @wakeup_int: the wakeup interrupt
> + * @mutex: the mutex for concurrent code at driver
> + * @dr_mode: supported mode of operation it can be only Host, only Device
> + *           or OTG mode that allow to switch between Device and Host mode.
> + *           This field based on firmware setting, kernel configuration
> + *           and hardware configuration.
> + * @current_dr_mode: current mode of operation when in dual-role mode
> + * @desired_dr_mode: desired mode of operation when in dual-role mode.
> + *           This value can be changed during runtime.
> + *           Available options depends on  dr_mode:
> + *           dr_mode                 |  desired_dr_mode and current_dr_mode
> + *           ----------------------------------------------------------------
> + *           USB_DR_MODE_HOST        | only USB_DR_MODE_HOST
> + *           USB_DR_MODE_PERIPHERAL  | only USB_DR_MODE_PERIPHERAL
> + *           USB_DR_MODE_OTG         | only USB_DR_MODE_HOST
> + *           USB_DR_MODE_OTG         | only USB_DR_MODE_PERIPHERAL
> + *           USB_DR_MODE_OTG         | USB_DR_MODE_OTG
> + *
> + *           Desired_dr_role can be changed by means of debugfs.
> + * @root: debugfs root folder pointer
> + * @debug_disable:
> + */
> +struct cdns3 {
> +	struct device			*dev;
> +	void __iomem			*xhci_regs;
> +	struct resource			xhci_res[CDNS3_XHCI_RESOURCES_NUM];
> +	struct cdns3_usb_regs __iomem	*dev_regs;
> +
> +	struct resource			otg_res;
> +	struct cdns3_otg_legacy_regs	*otg_v0_regs;
> +	struct cdns3_otg_regs		*otg_v1_regs;
> +	struct cdns3_otg_common_regs	*otg_regs;
> +#define CDNS3_CONTROLLER_V0	0
> +#define CDNS3_CONTROLLER_V1	1
> +	u32				version;
> +
> +	int				irq;
> +	struct cdns3_role_driver	*roles[CDNS3_ROLE_END];
> +	enum cdns3_roles		role;
> +	struct platform_device		*host_dev;
> +	struct cdns3_device		*gadget_dev;
> +	struct phy			*phy;
> +	struct work_struct		role_switch_wq;
> +	int				in_lpm:1;
> +	int				wakeup_int:1;
> +	/* mutext used in workqueue*/
> +	struct mutex			mutex;
> +	enum usb_dr_mode		dr_mode;
> +	enum usb_dr_mode		current_dr_mode;
> +	enum usb_dr_mode		desired_dr_mode;
> +	struct dentry			*root;
> +	int				debug_disable:1;
> +};
> +
> +void cdns3_role_stop(struct cdns3 *cdns);
> +
> +#endif /* __LINUX_CDNS3_CORE_H */
> diff --git a/drivers/usb/cdns3/debug.h b/drivers/usb/cdns3/debug.h
> new file mode 100644
> index 000000000000..94f9ef15f899
> --- /dev/null
> +++ b/drivers/usb/cdns3/debug.h
> @@ -0,0 +1,166 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +/*
> + * Cadence USBSS DRD Driver.
> + * Debug header file.
> + *
> + * Copyright (C) 2018 Cadence.
> + *
> + * Author: Pawel Laszczak <pawell@cadence.com>
> + */
> +#ifndef __LINUX_CDNS3_DEBUG
> +#define __LINUX_CDNS3_DEBUG
> +
> +#include "core.h"
> +
> +static inline char *cdns3_decode_usb_irq(char *str,
> +					 enum usb_device_speed speed,
> +
too many sentences as inline function
> 					 u32 usb_ists)
> +{
> +	int ret;
> +
> +	ret = sprintf(str, "IRQ %08x = ", usb_ists);
> +
> +	if (usb_ists & (USB_ISTS_CON2I | USB_ISTS_CONI)) {
> +		ret += sprintf(str + ret, "Connection %s\n",
> +			       usb_speed_string(speed));
> +	}
> +	if (usb_ists & USB_ISTS_CON2I || usb_ists & USB_ISTS_CONI)
> +		ret += sprintf(str + ret, "Disconnection ");
> +	if (usb_ists & USB_ISTS_L2ENTI)
> +		ret += sprintf(str + ret, "suspended ");
> +
> +	if (usb_ists & USB_ISTS_L2EXTI)
> +		ret += sprintf(str + ret, "L2 exit ");
> +	if (usb_ists & USB_ISTS_U3EXTI)
> +		ret += sprintf(str + ret, "U3 exit ");
> +	if (usb_ists & USB_ISTS_UWRESI)
> +		ret += sprintf(str + ret, "Warm Reset ");
> +	if (usb_ists & USB_ISTS_UHRESI)
> +		ret += sprintf(str + ret, "Hot Reset ");
> +	if (usb_ists & USB_ISTS_U2RESI)
> +		ret += sprintf(str + ret, "Reset");
> +
> +	return str;
> +}
> +
> +static inline  char *cdns3_decode_ep_irq(char *str,
> +					 u32 ep_sts,
> +					 const char *ep_name)
ditto
> +{
> +	int ret;
> +
> +	ret = sprintf(str, "IRQ for %s: %08x ", ep_name, ep_sts);
> +
> +	if (ep_sts & EP_STS_SETUP)
> +		ret += sprintf(str + ret, "SETUP ");
> +	if (ep_sts & EP_STS_IOC)
> +		ret += sprintf(str + ret, "IOC ");
> +	if (ep_sts & EP_STS_ISP)
> +		ret += sprintf(str + ret, "ISP ");
> +	if (ep_sts & EP_STS_DESCMIS)
> +		ret += sprintf(str + ret, "DESCMIS ");
> +	if (ep_sts & EP_STS_STREAMR)
> +		ret += sprintf(str + ret, "STREAMR ");
> +	if (ep_sts & EP_STS_MD_EXIT)
> +		ret += sprintf(str + ret, "MD_EXIT ");
> +	if (ep_sts & EP_STS_TRBERR)
> +		ret += sprintf(str + ret, "TRBERR ");
> +	if (ep_sts & EP_STS_NRDY)
> +		ret += sprintf(str + ret, "NRDY ");
> +	if (ep_sts & EP_STS_PRIME)
> +		ret += sprintf(str + ret, "PRIME ");
> +	if (ep_sts & EP_STS_SIDERR)
> +		ret += sprintf(str + ret, "SIDERRT ");
> +	if (ep_sts & EP_STS_OUTSMM)
> +		ret += sprintf(str + ret, "OUTSMM ");
> +	if (ep_sts & EP_STS_ISOERR)
> +		ret += sprintf(str + ret, "ISOERR ");
> +	if (ep_sts & EP_STS_IOT)
> +		ret += sprintf(str + ret, "IOT ");
> +
> +	return str;
> +}
> +
> +static inline char *cdns3_decode_epx_irq(char *str,
> +					 char *ep_name,
> +					 u32 ep_sts)
> +{
> +	return cdns3_decode_ep_irq(str, ep_sts, ep_name);
> +}
> +
> +static inline char *cdns3_decode_ep0_irq(char *str,
> +					 int dir,
> +					 u32 ep_sts)
> +{
> +	return cdns3_decode_ep_irq(str, ep_sts,
> +				   dir ? "ep0IN" : "ep0OUT");
> +}
> +
> +/**
> + * Debug a transfer ring.
> + *
> + * Prints out all TRBs in the endpoint ring, even those after the Link TRB.
> + *.
> + */
> +static inline char *cdns3_dbg_ring(struct cdns3_endpoint *priv_ep,
> +				   struct cdns3_trb *ring, char *str)
ditto
> +{
> +	dma_addr_t addr = priv_ep->trb_pool_dma;
> +	struct cdns3_trb *trb;
> +	int trb_per_sector;
> +	int ret = 0;
> +	int i;
> +
> +	trb_per_sector = GET_TRBS_PER_SEGMENT(priv_ep->type);
> +
> +	trb = &priv_ep->trb_pool[priv_ep->dequeue];
> +	ret += sprintf(str + ret, "\n\t\tRing contents for %s:", priv_ep->name);
> +
> +	ret += sprintf(str + ret,
> +		       "\n\t\tRing deq index: %d, trb: %p (virt), 0x%llx (dma)\n",
> +		       priv_ep->dequeue, trb,
> +		       (unsigned long long)cdns3_trb_virt_to_dma(priv_ep, trb));
> +
> +	trb = &priv_ep->trb_pool[priv_ep->enqueue];
> +	ret += sprintf(str + ret,
> +		       "\t\tRing enq index: %d, trb: %p (virt), 0x%llx (dma)\n",
> +		       priv_ep->enqueue, trb,
> +		       (unsigned long long)cdns3_trb_virt_to_dma(priv_ep, trb));
> +
> +	ret += sprintf(str + ret,
> +		       "\t\tfree trbs: %d, CCS=%d, PCS=%d\n",
> +		       priv_ep->free_trbs, priv_ep->ccs, priv_ep->pcs);
> +
> +	if (trb_per_sector > TRBS_PER_SEGMENT)
> +		trb_per_sector = TRBS_PER_SEGMENT;
> +
> +	if (trb_per_sector > TRBS_PER_SEGMENT) {
> +		sprintf(str + ret, "\t\tTo big transfer ring %d\n",
> +			trb_per_sector);
> +		return str;
> +	}
> +
> +	for (i = 0; i < trb_per_sector; ++i) {
> +		trb = &ring[i];
> +		ret += sprintf(str + ret,
> +			"\t\t@%pad %08x %08x %08x\n", &addr,
> +			le32_to_cpu(trb->buffer),
> +			le32_to_cpu(trb->length),
> +			le32_to_cpu(trb->control));
> +		addr += sizeof(*trb);
> +	}
> +
> +	return str;
> +}
> +
> +#ifdef CONFIG_DEBUG_FS
> +void cdns3_debugfs_init(struct cdns3 *cdns);
> +void cdns3_debugfs_exit(struct cdns3 *cdns);
> +#else
> +void cdns3_debugfs_init(struct cdns3 *cdns);
> +{  }
> +void cdns3_debugfs_exit(struct cdns3 *cdns);
> +{  }
> +#endif
> +
> +#endif /*__LINUX_CDNS3_DEBUG*/
> diff --git a/drivers/usb/cdns3/debugfs.c b/drivers/usb/cdns3/debugfs.c
> new file mode 100644
> index 000000000000..d7919f5c1d90
> --- /dev/null
> +++ b/drivers/usb/cdns3/debugfs.c
> @@ -0,0 +1,168 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Cadence USBSS DRD Controller DebugFS filer.
> + *
> + * Copyright (C) 2018 Cadence.
> + *
> + * Author: Pawel Laszczak <pawell@cadence.com>
> + */
> +
> +#include <linux/types.h>
> +#include <linux/debugfs.h>
> +#include <linux/seq_file.h>
> +#include <linux/uaccess.h>
> +
> +#include "core.h"
> +#include "gadget.h"
> +#include "drd.h"
> +
> +static int cdns3_mode_show(struct seq_file *s, void *unused)
> +{
> +	struct cdns3 *cdns = s->private;
> +
> +	switch (cdns->current_dr_mode) {
> +	case USB_DR_MODE_HOST:
> +		seq_puts(s, "host\n");
> +		break;
> +	case USB_DR_MODE_PERIPHERAL:
> +		seq_puts(s, "device\n");
> +		break;
> +	case USB_DR_MODE_OTG:
> +		seq_puts(s, "otg\n");
> +		break;
> +	default:
> +		seq_puts(s, "UNKNOWN mode\n");
> +	}
> +
> +	return 0;
> +}
> +
> +static int cdns3_mode_open(struct inode *inode, struct file *file)
> +{
> +	return single_open(file, cdns3_mode_show, inode->i_private);
> +}
> +
> +static ssize_t cdns3_mode_write(struct file *file,
> +				const char __user *ubuf,
> +				size_t count, loff_t *ppos)
> +{
> +	struct seq_file	 *s = file->private_data;
> +	struct cdns3 *cdns = s->private;
> +	u32 mode = USB_DR_MODE_UNKNOWN;
> +	char buf[32];
> +
> +	if (copy_from_user(&buf, ubuf, min_t(size_t, sizeof(buf) - 1, count)))
> +		return -EFAULT;
> +
> +	if (!strncmp(buf, "host", 4)) {
> +		if (cdns->dr_mode == USB_DR_MODE_HOST ||
> +		    cdns->dr_mode == USB_DR_MODE_OTG) {
> +			mode = USB_DR_MODE_HOST;
> +		}
> +	}
> +
> +	if (!strncmp(buf, "device", 6))
> +		if (cdns->dr_mode == USB_DR_MODE_PERIPHERAL ||
> +		    cdns->dr_mode == USB_DR_MODE_OTG)
> +			mode = USB_DR_MODE_PERIPHERAL;
> +
> +	if (!strncmp(buf, "otg", 3) && cdns->dr_mode == USB_DR_MODE_OTG)
> +		mode = USB_DR_MODE_OTG;
> +
> +	if (mode == USB_DR_MODE_UNKNOWN) {
> +		dev_err(cdns->dev, "Failed: incorrect mode setting\n");
> +		return -EFAULT;
> +	}
> +
> +	if (cdns->current_dr_mode != mode) {
> +		cdns->desired_dr_mode = mode;
> +		cdns->debug_disable = 0;
> +		cdns3_role_stop(cdns);
> +		cdns3_drd_update_mode(cdns);
> +		queue_work(system_freezable_wq, &cdns->role_switch_wq);
> +	}
> +
> +	return count;
> +}
> +
> +static const struct file_operations cdns3_mode_fops = {
> +	.open			= cdns3_mode_open,
> +	.write			= cdns3_mode_write,
> +	.read			= seq_read,
> +	.llseek			= seq_lseek,
> +	.release		= single_release,
> +};
> +
> +static int cdns3_disable_show(struct seq_file *s, void *unused)
> +{
> +	struct cdns3 *cdns = s->private;
> +
> +	if (!cdns->debug_disable)
> +		seq_puts(s, "0\n");
> +	else
> +		seq_puts(s, "1\n");
> +
> +	return 0;
> +}
> +
> +static ssize_t cdns3_disable_write(struct file *file,
> +				   const char __user *ubuf,
> +				   size_t count, loff_t *ppos)
> +{
> +	struct seq_file	 *s = file->private_data;
> +	struct cdns3 *cdns = s->private;
> +	int disable;
> +	char buf[32];
> +
> +	if (copy_from_user(&buf, ubuf, min_t(size_t, sizeof(buf) - 1, count)))
> +		return -EFAULT;
> +
> +	if (!strncmp(buf, "1", 1) || !strncmp(buf, "yes", 3)) {
> +		disable = 1;
> +	} else if (!strncmp(buf, "0", 1) || !strncmp(buf, "no", 2)) {
> +		disable = 0;
> +	} else {
> +		dev_err(cdns->dev, "Failed: incorrect disable setting\n");
> +		return -EFAULT;
> +	}
> +
There is common API kstrtobool()?

> +	if (disable != cdns->debug_disable) {
> +		cdns->debug_disable = disable;
> +		queue_work(system_freezable_wq, &cdns->role_switch_wq);
> +	}
> +
> +	return count;
> +}
> +
> +static int cdns3_disable_open(struct inode *inode, struct file *file)
> +{
> +	return single_open(file, cdns3_disable_show, inode->i_private);
> +}
> +
> +static const struct file_operations cdns3_disable_fops = {
> +	.open			= cdns3_disable_open,
> +	.write			= cdns3_disable_write,
> +	.read			= seq_read,
> +	.llseek			= seq_lseek,
> +	.release		= single_release,
> +};
> +
> +void cdns3_debugfs_init(struct cdns3 *cdns)
> +{
> +	struct dentry *root;
> +
> +	root = debugfs_create_dir(dev_name(cdns->dev), NULL);
> +	cdns->root = root;
> +	if (IS_ENABLED(CONFIG_USB_CDNS3_GADGET) &&
> +	    IS_ENABLED(CONFIG_USB_CDNS3_HOST))
> +		debugfs_create_file("mode", 0644, root, cdns,
> +				    &cdns3_mode_fops);
> +
> +	debugfs_create_file("disable", 0644, root, cdns,
> +			    &cdns3_disable_fops);
> +}
> +
> +void cdns3_debugfs_exit(struct cdns3 *cdns)
> +{
> +	debugfs_remove_recursive(cdns->root);
> +}
> diff --git a/drivers/usb/cdns3/drd.c b/drivers/usb/cdns3/drd.c
> new file mode 100644
> index 000000000000..b0c32302eb0b
> --- /dev/null
> +++ b/drivers/usb/cdns3/drd.c
> @@ -0,0 +1,350 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Cadence USBSS DRD Driver.
> + *
> + * Copyright (C) 2018 Cadence.
> + *
> + * Author: Pawel Laszczak <pawell@cadence.com
> + *
> + */
> +#include <linux/kernel.h>
> +#include <linux/interrupt.h>
> +#include <linux/delay.h>
> +#include <linux/usb/otg.h>
> +
> +#include "gadget.h"
> +#include "drd.h"
> +#include "core.h"
> +
> +static int cdns3_drd_switch_gadget(struct cdns3 *cdns, int on);
> +static int cdns3_drd_switch_host(struct cdns3 *cdns, int on);
> +
> +/**
> + * cdns3_set_mode - change mode of OTG Core
> + * @cdns: pointer to context structure
> + * @mode: selected mode from cdns_role
> + */
> +void cdns3_set_mode(struct cdns3 *cdns, enum usb_dr_mode mode)
> +{
> +	u32 reg;
> +
> +	cdns->current_dr_mode = mode;
> +
> +	switch (mode) {
> +	case USB_DR_MODE_PERIPHERAL:
> +		dev_info(cdns->dev, "Set controller to Gadget mode\n");
> +		cdns3_drd_switch_gadget(cdns, 1);
> +		break;
> +	case USB_DR_MODE_HOST:
> +		dev_info(cdns->dev, "Set controller to Host mode\n");
> +		cdns3_drd_switch_host(cdns, 1);
> +		break;
> +	case USB_DR_MODE_OTG:
> +		dev_info(cdns->dev, "Set controller to OTG mode\n");
> +		if (cdns->version == CDNS3_CONTROLLER_V1) {
> +			reg = readl(&cdns->otg_v1_regs->override);
> +			reg |= OVERRIDE_IDPULLUP;
> +			writel(reg, &cdns->otg_v1_regs->override);
> +		} else {
> +			reg = readl(&cdns->otg_v0_regs->ctrl1);
> +			reg |= OVERRIDE_IDPULLUP_V0;
> +			writel(reg, &cdns->otg_v0_regs->ctrl1);
> +		}
> +
> +		/*
> +		 * Hardware specification says: "ID_VALUE must be valid within
> +		 * 50ms after idpullup is set to '1" so driver must wait
> +		 * 50ms before reading this pin.
> +		 */
> +		usleep_range(50000, 60000);
> +		break;
> +	default:
> +		cdns->current_dr_mode = USB_DR_MODE_UNKNOWN;
> +		dev_err(cdns->dev, "Unsupported mode of operation %d\n", mode);
> +		return;
> +	}
> +}
> +
> +int cdns3_get_id(struct cdns3 *cdns)
> +{
> +	int id;
> +
> +	id = readl(&cdns->otg_regs->sts) & OTGSTS_ID_VALUE;
> +	dev_dbg(cdns->dev, "OTG ID: %d", id);
> +	return  1 ; //id;
return fixed value ?
> +}
> +
> +int cdns3_is_host(struct cdns3 *cdns)
> +{
> +	if (cdns->current_dr_mode == USB_DR_MODE_HOST)
> +		return 1;
> +	else if (!cdns3_get_id(cdns))
> +		return 1;
> +
> +	return 0;
> +}
> +
> +

> +
> +/**
> + * cdns3_init_otg_mode - initialize drd controller
> + * @cdns: Pointer to controller context structure
> + *
> + * Returns 0 on success otherwise negative errno
void function
> + */
> +static void cdns3_init_otg_mode(struct cdns3 *cdns)
> +{
> +	cdns3_otg_disable_irq(cdns);
> +	/* clear all interrupts */
> +	writel(~0, &cdns->otg_regs->ivect);
> +
> +	cdns3_set_mode(cdns, USB_DR_MODE_OTG);
> +
> +	if (cdns3_is_host(cdns))
> +		cdns3_drd_switch_host(cdns, 1);
> +	else
> +		cdns3_drd_switch_gadget(cdns, 1);
> +
> +	cdns3_otg_enable_irq(cdns);
> +}
> +
> +/**
> + * cdns3_drd_update_mode - initialize mode of operation
> + * @cdns: Pointer to controller context structure
> + *
> + * Returns 0 on success otherwise negative errno
> + */
> +int cdns3_drd_update_mode(struct cdns3 *cdns)
> +{
> +	int ret = 0;
> +
> +	if (cdns->desired_dr_mode == cdns->current_dr_mode)
> +		return ret;
> +
> +	cdns3_drd_switch_gadget(cdns, 0);
> +	cdns3_drd_switch_host(cdns, 0);
> +
> +	switch (cdns->desired_dr_mode) {
> +	case USB_DR_MODE_PERIPHERAL:
> +		cdns3_set_mode(cdns, USB_DR_MODE_PERIPHERAL);
> +		break;
> +	case USB_DR_MODE_HOST:
> +		cdns3_set_mode(cdns, USB_DR_MODE_HOST);
> +		break;
> +	case USB_DR_MODE_OTG:
> +		cdns3_init_otg_mode(cdns);
> +		break;
> +	default:
> +		dev_err(cdns->dev, "Unsupported mode of operation %d\n",
> +			cdns->dr_mode);
> +		return -EINVAL;
> +	}
> +
> +	return ret;
no need define local variable @ret
> +}
> +
> +/**
> + * cdns3_drd_irq - interrupt handler for OTG events
> + *
> + * @irq: irq number for cdns3 core device
> + * @data: structure of cdns3
> + *
> + * Returns IRQ_HANDLED or IRQ_NONE
> + */
> +static irqreturn_t cdns3_drd_irq(int irq, void *data)
> +{
> +	irqreturn_t ret = IRQ_NONE;
> +	struct cdns3 *cdns = data;
> +	u32 reg;
> +
> +	if (cdns->dr_mode != USB_DR_MODE_OTG)
> +		return ret;
> +
> +	reg = readl(&cdns->otg_regs->ivect);
> +
> +	if (!reg)
> +		return ret;
> +
> +	if (reg & OTGIEN_ID_CHANGE_INT) {
> +		dev_dbg(cdns->dev, "OTG IRQ: new ID: %d\n",
> +			cdns3_get_id(cdns));
> +
> +		queue_work(system_freezable_wq, &cdns->role_switch_wq);
> +
do you need to clear interrupts status here?
> +		ret = IRQ_HANDLED;
> +	}
> +
> +	writel(~0, &cdns->otg_regs->ivect);
> +	return ret;
> +}
> +
> +int cdns3_drd_init(struct cdns3 *cdns)
> +{
> +	void __iomem *regs;
> +	int ret = 0;
> +	u32 state;
> +
> +	regs = devm_ioremap_resource(cdns->dev, &cdns->otg_res);
> +	if (IS_ERR(regs))
> +		return PTR_ERR(regs);
> +
> +	/* Detection of DRD version. Controller has been released
> +	 * in two versions. Both are similar, but they have same changes
> +	 * in register maps.
> +	 * The first register in old version is command register and it's read
> +	 * only, so driver should read 0 from it. On the other hand, in v1
> +	 * the first register contains device ID number which is not set to 0.
> +	 * Driver uses this fact to detect the proper version of
> +	 * controller.
> +	 */
> +	cdns->otg_v0_regs = regs;
> +	if (!readl(&cdns->otg_v0_regs->cmd)) {
> +		cdns->version  = CDNS3_CONTROLLER_V0;
> +		cdns->otg_v1_regs = NULL;
> +		cdns->otg_regs = regs;
> +		dev_info(cdns->dev, "DRD version v0 (%08x)\n",
> +			 readl(&cdns->otg_v0_regs->version));
> +	} else {
> +		cdns->otg_v0_regs = NULL;
> +		cdns->otg_v1_regs = regs;
> +		cdns->otg_regs = (void *)&cdns->otg_v1_regs->cmd;
> +		cdns->version  = CDNS3_CONTROLLER_V1;
> +		dev_info(cdns->dev, "DRD version v1 (ID: %08x, rev: %08x)\n",
> +			 readl(&cdns->otg_v1_regs->did),
> +			 readl(&cdns->otg_v1_regs->rid));
> +	}
> +
> +	state = OTGSTS_STRAP(readl(&cdns->otg_regs->sts));
> +
> +	/* Update dr_mode according to STRAP configuration. */
> +	cdns->dr_mode = USB_DR_MODE_OTG;
> +	if (state == OTGSTS_STRAP_HOST) {
> +		dev_info(cdns->dev, "Controller strapped to HOST\n");
> +		cdns->dr_mode = USB_DR_MODE_HOST;
> +	} else if (state == OTGSTS_STRAP_GADGET) {
> +		dev_info(cdns->dev, "Controller strapped to PERIPHERAL\n");
> +		cdns->dr_mode = USB_DR_MODE_PERIPHERAL;
> +	}
> +
> +	cdns->desired_dr_mode = cdns->dr_mode;
> +	cdns->current_dr_mode = USB_DR_MODE_UNKNOWN;
> +
> +	ret = devm_request_threaded_irq(cdns->dev, cdns->irq, cdns3_drd_irq,
> +					NULL, IRQF_SHARED,
> +					dev_name(cdns->dev), cdns);
> +
> +	if (ret)
> +		return ret;
> +
> +	state = readl(&cdns->otg_regs->sts);
> +	if (OTGSTS_OTG_NRDY(state) != 0) {
> +		dev_err(cdns->dev, "Cadence USB3 OTG device not ready\n");
> +		return -ENODEV;
> +	}
> +
> +	ret = cdns3_drd_update_mode(cdns);
> +
> +	return ret;
return cdns3_drd_update_mode();
> +}
> +
> +int cdns3_drd_exit(struct cdns3 *cdns)
> +{
> +	return cdns3_drd_switch_host(cdns, 0);
> +}


<...>

> diff --git a/drivers/usb/cdns3/ep0.c b/drivers/usb/cdns3/ep0.c
> new file mode 100644
> index 000000000000..89cf1cde1555
> --- /dev/null
> +++ b/drivers/usb/cdns3/ep0.c
> @@ -0,0 +1,896 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Cadence USBSS DRD Driver - gadget side.
> + *
> + * Copyright (C) 2018 Cadence Design Systems.
> + * Copyright (C) 2017-2018 NXP
> + *
> + * Authors: Pawel Jez <pjez@cadence.com>,
> + *          Pawel Laszczak <pawell@cadence.com>
> + *	    Peter Chen <peter.chen@nxp.com>
> + */
> +
> +#include <linux/usb/composite.h>
> +
> +#include "gadget.h"
> +#include "trace.h"
> +
> +static struct usb_endpoint_descriptor cdns3_gadget_ep0_desc = {
> +	.bLength = USB_DT_ENDPOINT_SIZE,
> +	.bDescriptorType = USB_DT_ENDPOINT,
> +	.bmAttributes =	USB_ENDPOINT_XFER_CONTROL,
> +};
> +
<...>
> +
> +
> +#endif /* __LINUX_CDNS3_GADGET_EXPORT */
> diff --git a/drivers/usb/cdns3/gadget.c b/drivers/usb/cdns3/gadget.c
> new file mode 100644
> index 000000000000..0d95eb00be37
> --- /dev/null
> +++ b/drivers/usb/cdns3/gadget.c
> @@ -0,0 +1,2102 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Cadence USBSS DRD Driver - gadget side.
> + *
> + * Copyright (C) 2018 Cadence Design Systems.
> + * Copyright (C) 2017-2018 NXP
> + *
> + * Authors: Pawel Jez <pjez@cadence.com>,
> + *          Pawel Laszczak <pawell@cadence.com>
> + *	    Peter Chen <peter.chen@nxp.com>
> + */
> +
> +#include <linux/dma-mapping.h>
> +#include <linux/usb/gadget.h>
> +
<...>

> +/**
> + * cdns3_next_request - returns next request from list
> + * @list: list containing requests
> + *
> + * Returns request or NULL if no requests in list
> + */
> +struct usb_request *cdns3_next_request(struct list_head *list)
> +{
> +	if (list_empty(list))
> +		return NULL;
No need check it, if list is empty, list_first_entry_or_null() will
return NULL
> +	return list_first_entry_or_null(list, struct usb_request, list);
> +}
> +
> +/**
> + * cdns3_next_priv_request - returns next request from list
> + * @list: list containing requests
> + *
> + * Returns request or NULL if no requests in list
> + */
> +struct cdns3_request *cdns3_next_priv_request(struct list_head *list)
> +{
> +	if (list_empty(list))
> +		return NULL;
ditto
> +	return list_first_entry_or_null(list, struct cdns3_request, list);
> +}
> +

<...>

^ permalink raw reply	[flat|nested] 49+ messages in thread

* RE: [PATCH v2 5/5] usb:cdns3 Add Cadence USB3 DRD Driver
@ 2018-12-31  5:32             ` Pawel Laszczak
  0 siblings, 0 replies; 49+ messages in thread
From: Pawel Laszczak @ 2018-12-31  5:32 UTC (permalink / raw)
  To: Peter Chen, Peter Chen
  Cc: devicetree, Greg Kroah-Hartman, mark.rutland, linux-usb,
	hdegoede, Heikki Krogerus, andy.shevchenko, robh+dt, rogerq,
	lkml, Alan Douglas, jbergsagel, nsekhar, nm, Suresh Punnoose,
	Pawel Jez, Rahul Kumar

Hi Peter,
>
>> >
>> >@@ -299,6 +306,7 @@ int cdns3_drd_init(struct cdns3 *cdns)
>> >                cdns->version  = CDNS3_CONTROLLER_V0;
>> >                cdns->otg_v1_regs = NULL;
>> >                cdns->otg_regs = regs;
>> >+               writel(0x1, &cdns->otg_v0_regs->simulate);
>> >                dev_info(cdns->dev, "DRD version v0 (%08x)\n",
>> >                         readl(&cdns->otg_v0_regs->version));
>> >        } else {
>>
>> I have confirmation from HW team that time that driver should wait after de-
>> selecting mode is 2-3ms for simulate mode. It's time when FSM is in
>> DRD_H_WAIT_VBUS_FAIL.
>> Driver cannot re-enable the host/device mode before this time has elapsed.
>>
>> 3 ms is the maximum time. Additionally, you can confirm the current FSM state by
>> reading the host_otg_state (bit 5:3) or dev_otg_state (2:0)  from OTGSTATE
>> register.
>>
>> If bit 0 in simulate register is cleared the time is exactly 1s.
>>
>
>Thanks, Pawel.
>
>Would you please add below changes in your next revision?
>- Set bit 0 in simulate register
But it's used only for simulation environments to speed up simulation. 
On real platforms this bit should be cleared. I'm not sure if I can 
add some code related to simulation environment to driver. 
If yes then I must introduce the way, that allow to recognize this two modes. 
I could add module parameter or add additional config in Kconfig file.

>- timeout logic for waiting host_otg_state or dev_otg_state at OTGSTATE
>when switch to host or device.

I will add such code. 

Pawel


^ permalink raw reply	[flat|nested] 49+ messages in thread

* [v2,5/5] usb:cdns3 Add Cadence USB3 DRD Driver
@ 2018-12-31  5:32             ` Pawel Laszczak
  0 siblings, 0 replies; 49+ messages in thread
From: Pawel Laszczak @ 2018-12-31  5:32 UTC (permalink / raw)
  To: Peter Chen, Peter Chen
  Cc: devicetree, Greg Kroah-Hartman, mark.rutland, linux-usb,
	hdegoede, Heikki Krogerus, andy.shevchenko, robh+dt, rogerq,
	lkml, Alan Douglas, jbergsagel, nsekhar, nm, Suresh Punnoose,
	Pawel Jez, Rahul Kumar

Hi Peter,
>
>> >
>> >@@ -299,6 +306,7 @@ int cdns3_drd_init(struct cdns3 *cdns)
>> >                cdns->version  = CDNS3_CONTROLLER_V0;
>> >                cdns->otg_v1_regs = NULL;
>> >                cdns->otg_regs = regs;
>> >+               writel(0x1, &cdns->otg_v0_regs->simulate);
>> >                dev_info(cdns->dev, "DRD version v0 (%08x)\n",
>> >                         readl(&cdns->otg_v0_regs->version));
>> >        } else {
>>
>> I have confirmation from HW team that time that driver should wait after de-
>> selecting mode is 2-3ms for simulate mode. It's time when FSM is in
>> DRD_H_WAIT_VBUS_FAIL.
>> Driver cannot re-enable the host/device mode before this time has elapsed.
>>
>> 3 ms is the maximum time. Additionally, you can confirm the current FSM state by
>> reading the host_otg_state (bit 5:3) or dev_otg_state (2:0)  from OTGSTATE
>> register.
>>
>> If bit 0 in simulate register is cleared the time is exactly 1s.
>>
>
>Thanks, Pawel.
>
>Would you please add below changes in your next revision?
>- Set bit 0 in simulate register
But it's used only for simulation environments to speed up simulation. 
On real platforms this bit should be cleared. I'm not sure if I can 
add some code related to simulation environment to driver. 
If yes then I must introduce the way, that allow to recognize this two modes. 
I could add module parameter or add additional config in Kconfig file.

>- timeout logic for waiting host_otg_state or dev_otg_state at OTGSTATE
>when switch to host or device.

I will add such code. 

Pawel

^ permalink raw reply	[flat|nested] 49+ messages in thread

* RE: [PATCH v2 5/5] usb:cdns3 Add Cadence USB3 DRD Driver
  2018-12-23 19:20     ` [PATCH v2 5/5] " kbuild test robot
  (?)
@ 2018-12-31  9:42       ` Pawel Laszczak
  -1 siblings, 0 replies; 49+ messages in thread
From: Pawel Laszczak @ 2018-12-31  9:42 UTC (permalink / raw)
  To: kbuild test robot
  Cc: kbuild-all, devicetree, gregkh, mark.rutland, linux-usb,
	hdegoede, heikki.krogerus, andy.shevchenko, robh+dt, rogerq,
	linux-kernel, Alan Douglas, jbergsagel, nsekhar, nm,
	Suresh Punnoose, peter.chen, Pawel Jez, Rahul Kumar

Hi

I can't recreate these issue on my environment. I use  gcc (Ubuntu 7.3.0-27ubuntu1~18.04) 7.3.0.  Maybe there are some differenct between these two compilers. 

Do you have any idea how I should change this fragment of code:
		sprintf(str, "%02x %02x %02x %02x %02x %02x %02x %02x",
			bRequestType, bRequest,
			cpu_to_le16(wValue) & 0xff,
			cpu_to_le16(wValue) >> 8,
			cpu_to_le16(wIndex) & 0xff,
			cpu_to_le16(wIndex) >> 8,
			cpu_to_le16(wLength) & 0xff,
			cpu_to_le16(wLength) >> 8);

to remove "restricted __le16 degrades to integer" warnings ?

Maybe I should cast all to u8. Then this code will look like:
		sprintf(str, "%02x %02x %02x %02x %02x %02x %02x %02x",
			bRequestType, bRequest,
			(u8)(cpu_to_le16(wValue) & 0xff),
			(u8)(cpu_to_le16(wValue) >> 8),
			(u8)(cpu_to_le16(wIndex) & 0xff),
			(u8)(cpu_to_le16(wIndex) >> 8),
			(u8)(cpu_to_le16(wLength) & 0xff),
			(u8)(cpu_to_le16(wLength) >> 8));

Should it Fix these warnings ?

Cheers,
Pawel

>Thank you for the patch! Perhaps something to improve:
>
>
>
>[auto build test WARNING on usb/usb-testing]
>
>[also build test WARNING on v4.20-rc7 next-20181221]
>
>[if your patch is applied to the wrong git tree, please drop us a note to help improve the system]
>
>
>
>url:    https://urldefense.proofpoint.com/v2/url?u=https-3A__github.com_0day-2Dci_linux_commits_Pawel-2DLaszczak_Introduced-
>2Dnew-2DCadence-2DUSBSS-2DDRD-2DDriver_20181223-2D231813&d=DwIBAg&c=aUq983L2pue2FqKFoP6PGHMJQyoJ7kl3s3GZ-
>_haXqY&r=e1OgxfvkL0qo9XO6fX1gscva-w03uSYC1nIyxl89-rI&m=JS8MUBEUPN46-me57xUY-
>7hoBbSrlgd2SCB9ahNjK4s&s=bhpHqRyEtMdMbWdGoBqQ9Pz9wq7pRA7-OohrGik3BpM&e=
>
>base:   https://urldefense.proofpoint.com/v2/url?u=https-
>3A__git.kernel.org_pub_scm_linux_kernel_git_gregkh_usb.git&d=DwIBAg&c=aUq983L2pue2FqKFoP6PGHMJQyoJ7kl3s3GZ-
>_haXqY&r=e1OgxfvkL0qo9XO6fX1gscva-w03uSYC1nIyxl89-rI&m=JS8MUBEUPN46-me57xUY-
>7hoBbSrlgd2SCB9ahNjK4s&s=Vf__lGpV27zdf1egowm9p8YBJjz9aMmgbi8nW_Z_DLk&e= usb-testing
>
>config: x86_64-allmodconfig (attached as .config)
>
>compiler: gcc-7 (Debian 7.3.0-1) 7.3.0
>
>reproduce:
>
>        # save the attached .config to linux build tree
>
>        make ARCH=x86_64
>
>
>
>All warnings (new ones prefixed by >>):
>
>
>
>>> drivers/usb/common/debug.c:253:25: warning: restricted __le16 degrades to integer
>
>   drivers/usb/common/debug.c:254:25: warning: restricted __le16 degrades to integer
>
>   drivers/usb/common/debug.c:255:25: warning: restricted __le16 degrades to integer
>
>   drivers/usb/common/debug.c:256:25: warning: restricted __le16 degrades to integer
>
>   drivers/usb/common/debug.c:257:25: warning: restricted __le16 degrades to integer
>
>   drivers/usb/common/debug.c:258:25: warning: restricted __le16 degrades to integer
>
>
>
>vim +253 drivers/usb/common/debug.c
>
>
>
>cefb8b21 Pawel Laszczak 2018-12-23  205
>
>cefb8b21 Pawel Laszczak 2018-12-23  206  /**
>
>cefb8b21 Pawel Laszczak 2018-12-23  207   * usb_decode_ctrl - returns a string representation of ctrl request
>
>cefb8b21 Pawel Laszczak 2018-12-23  208   */
>
>cefb8b21 Pawel Laszczak 2018-12-23  209  const char *usb_decode_ctrl(char *str, __u8 bRequestType, __u8 bRequest,
>
>cefb8b21 Pawel Laszczak 2018-12-23  210  			    __u16 wValue,  __u16 wIndex, __u16 wLength)
>
>cefb8b21 Pawel Laszczak 2018-12-23  211  {
>
>cefb8b21 Pawel Laszczak 2018-12-23  212  	switch (bRequest) {
>
>cefb8b21 Pawel Laszczak 2018-12-23  213  	case USB_REQ_GET_STATUS:
>
>cefb8b21 Pawel Laszczak 2018-12-23  214  		usb_decode_get_status(bRequestType, wIndex, wLength, str);
>
>cefb8b21 Pawel Laszczak 2018-12-23  215  		break;
>
>cefb8b21 Pawel Laszczak 2018-12-23  216  	case USB_REQ_CLEAR_FEATURE:
>
>cefb8b21 Pawel Laszczak 2018-12-23  217  	case USB_REQ_SET_FEATURE:
>
>cefb8b21 Pawel Laszczak 2018-12-23  218  		usb_decode_set_clear_feature(bRequestType, bRequest, wValue,
>
>cefb8b21 Pawel Laszczak 2018-12-23  219  					     wIndex, str);
>
>cefb8b21 Pawel Laszczak 2018-12-23  220  		break;
>
>cefb8b21 Pawel Laszczak 2018-12-23  221  	case USB_REQ_SET_ADDRESS:
>
>cefb8b21 Pawel Laszczak 2018-12-23  222  		usb_decode_set_address(wValue, str);
>
>cefb8b21 Pawel Laszczak 2018-12-23  223  		break;
>
>cefb8b21 Pawel Laszczak 2018-12-23  224  	case USB_REQ_GET_DESCRIPTOR:
>
>cefb8b21 Pawel Laszczak 2018-12-23  225  	case USB_REQ_SET_DESCRIPTOR:
>
>cefb8b21 Pawel Laszczak 2018-12-23  226  		usb_decode_get_set_descriptor(bRequestType, bRequest, wValue,
>
>cefb8b21 Pawel Laszczak 2018-12-23  227  					      wIndex, wLength, str);
>
>cefb8b21 Pawel Laszczak 2018-12-23  228  		break;
>
>cefb8b21 Pawel Laszczak 2018-12-23  229  	case USB_REQ_GET_CONFIGURATION:
>
>cefb8b21 Pawel Laszczak 2018-12-23  230  		usb_decode_get_configuration(wLength, str);
>
>cefb8b21 Pawel Laszczak 2018-12-23  231  		break;
>
>cefb8b21 Pawel Laszczak 2018-12-23  232  	case USB_REQ_SET_CONFIGURATION:
>
>cefb8b21 Pawel Laszczak 2018-12-23  233  		usb_decode_set_configuration(wValue, str);
>
>cefb8b21 Pawel Laszczak 2018-12-23  234  		break;
>
>cefb8b21 Pawel Laszczak 2018-12-23  235  	case USB_REQ_GET_INTERFACE:
>
>cefb8b21 Pawel Laszczak 2018-12-23  236  		usb_decode_get_intf(wIndex, wLength, str);
>
>cefb8b21 Pawel Laszczak 2018-12-23  237  		break;
>
>cefb8b21 Pawel Laszczak 2018-12-23  238  	case USB_REQ_SET_INTERFACE:
>
>cefb8b21 Pawel Laszczak 2018-12-23  239  		usb_decode_set_intf(wValue, wIndex, str);
>
>cefb8b21 Pawel Laszczak 2018-12-23  240  		break;
>
>cefb8b21 Pawel Laszczak 2018-12-23  241  	case USB_REQ_SYNCH_FRAME:
>
>cefb8b21 Pawel Laszczak 2018-12-23  242  		usb_decode_synch_frame(wIndex, wLength, str);
>
>cefb8b21 Pawel Laszczak 2018-12-23  243  		break;
>
>cefb8b21 Pawel Laszczak 2018-12-23  244  	case USB_REQ_SET_SEL:
>
>cefb8b21 Pawel Laszczak 2018-12-23  245  		usb_decode_set_sel(wLength, str);
>
>cefb8b21 Pawel Laszczak 2018-12-23  246  		break;
>
>cefb8b21 Pawel Laszczak 2018-12-23  247  	case USB_REQ_SET_ISOCH_DELAY:
>
>cefb8b21 Pawel Laszczak 2018-12-23  248  		usb_decode_set_isoch_delay(wValue, str);
>
>cefb8b21 Pawel Laszczak 2018-12-23  249  		break;
>
>cefb8b21 Pawel Laszczak 2018-12-23  250  	default:
>
>cefb8b21 Pawel Laszczak 2018-12-23  251  		sprintf(str, "%02x %02x %02x %02x %02x %02x %02x %02x",
>
>cefb8b21 Pawel Laszczak 2018-12-23  252  			bRequestType, bRequest,
>
>cefb8b21 Pawel Laszczak 2018-12-23 @253  			cpu_to_le16(wValue) & 0xff,
>
>cefb8b21 Pawel Laszczak 2018-12-23  254  			cpu_to_le16(wValue) >> 8,
>
>cefb8b21 Pawel Laszczak 2018-12-23  255  			cpu_to_le16(wIndex) & 0xff,
>
>cefb8b21 Pawel Laszczak 2018-12-23  256  			cpu_to_le16(wIndex) >> 8,
>
>cefb8b21 Pawel Laszczak 2018-12-23  257  			cpu_to_le16(wLength) & 0xff,
>
>cefb8b21 Pawel Laszczak 2018-12-23  258  			cpu_to_le16(wLength) >> 8);
>
>cefb8b21 Pawel Laszczak 2018-12-23  259  	}
>
>cefb8b21 Pawel Laszczak 2018-12-23  260
>
>cefb8b21 Pawel Laszczak 2018-12-23  261  	return str;
>
>cefb8b21 Pawel Laszczak 2018-12-23  262  }
>
>cefb8b21 Pawel Laszczak 2018-12-23  263  EXPORT_SYMBOL_GPL(usb_decode_ctrl);
>
>cefb8b21 Pawel Laszczak 2018-12-23  264
>
>
>
>:::::: The code at line 253 was first introduced by commit
>
>:::::: cefb8b2176410ef0aacc22411a64f8a157612f14 usb:common Separated decoding functions from dwc3 driver.
>
>
>
>:::::: TO: Pawel Laszczak <pawell@cadence.com>
>
>:::::: CC: 0day robot <lkp@intel.com>
>
>
>
>---
>
>0-DAY kernel test infrastructure                Open Source Technology Center
>
>https://urldefense.proofpoint.com/v2/url?u=https-3A__lists.01.org_pipermail_kbuild-
>2Dall&d=DwIBAg&c=aUq983L2pue2FqKFoP6PGHMJQyoJ7kl3s3GZ-_haXqY&r=e1OgxfvkL0qo9XO6fX1gscva-w03uSYC1nIyxl89-
>rI&m=JS8MUBEUPN46-me57xUY-7hoBbSrlgd2SCB9ahNjK4s&s=7vN3rhLkFNcUQ6jriRP_jESizA-Wm6VxtiGb8qhTKqU&e=                   Intel
>Corporation


^ permalink raw reply	[flat|nested] 49+ messages in thread

* RE: [PATCH v2 5/5] usb:cdns3 Add Cadence USB3 DRD Driver
@ 2018-12-31  9:42       ` Pawel Laszczak
  0 siblings, 0 replies; 49+ messages in thread
From: Pawel Laszczak @ 2018-12-31  9:42 UTC (permalink / raw)
  To: kbuild test robot
  Cc: kbuild-all, devicetree, gregkh, mark.rutland, linux-usb,
	hdegoede, heikki.krogerus, andy.shevchenko, robh+dt, rogerq,
	linux-kernel, Alan Douglas, jbergsagel, nsekhar, nm,
	Suresh Punnoose, peter.chen@nxp.com

Hi

I can't recreate these issue on my environment. I use  gcc (Ubuntu 7.3.0-27ubuntu1~18.04) 7.3.0.  Maybe there are some differenct between these two compilers. 

Do you have any idea how I should change this fragment of code:
		sprintf(str, "%02x %02x %02x %02x %02x %02x %02x %02x",
			bRequestType, bRequest,
			cpu_to_le16(wValue) & 0xff,
			cpu_to_le16(wValue) >> 8,
			cpu_to_le16(wIndex) & 0xff,
			cpu_to_le16(wIndex) >> 8,
			cpu_to_le16(wLength) & 0xff,
			cpu_to_le16(wLength) >> 8);

to remove "restricted __le16 degrades to integer" warnings ?

Maybe I should cast all to u8. Then this code will look like:
		sprintf(str, "%02x %02x %02x %02x %02x %02x %02x %02x",
			bRequestType, bRequest,
			(u8)(cpu_to_le16(wValue) & 0xff),
			(u8)(cpu_to_le16(wValue) >> 8),
			(u8)(cpu_to_le16(wIndex) & 0xff),
			(u8)(cpu_to_le16(wIndex) >> 8),
			(u8)(cpu_to_le16(wLength) & 0xff),
			(u8)(cpu_to_le16(wLength) >> 8));

Should it Fix these warnings ?

Cheers,
Pawel

>Thank you for the patch! Perhaps something to improve:
>
>
>
>[auto build test WARNING on usb/usb-testing]
>
>[also build test WARNING on v4.20-rc7 next-20181221]
>
>[if your patch is applied to the wrong git tree, please drop us a note to help improve the system]
>
>
>
>url:    https://urldefense.proofpoint.com/v2/url?u=https-3A__github.com_0day-2Dci_linux_commits_Pawel-2DLaszczak_Introduced-
>2Dnew-2DCadence-2DUSBSS-2DDRD-2DDriver_20181223-2D231813&d=DwIBAg&c=aUq983L2pue2FqKFoP6PGHMJQyoJ7kl3s3GZ-
>_haXqY&r=e1OgxfvkL0qo9XO6fX1gscva-w03uSYC1nIyxl89-rI&m=JS8MUBEUPN46-me57xUY-
>7hoBbSrlgd2SCB9ahNjK4s&s=bhpHqRyEtMdMbWdGoBqQ9Pz9wq7pRA7-OohrGik3BpM&e=
>
>base:   https://urldefense.proofpoint.com/v2/url?u=https-
>3A__git.kernel.org_pub_scm_linux_kernel_git_gregkh_usb.git&d=DwIBAg&c=aUq983L2pue2FqKFoP6PGHMJQyoJ7kl3s3GZ-
>_haXqY&r=e1OgxfvkL0qo9XO6fX1gscva-w03uSYC1nIyxl89-rI&m=JS8MUBEUPN46-me57xUY-
>7hoBbSrlgd2SCB9ahNjK4s&s=Vf__lGpV27zdf1egowm9p8YBJjz9aMmgbi8nW_Z_DLk&e= usb-testing
>
>config: x86_64-allmodconfig (attached as .config)
>
>compiler: gcc-7 (Debian 7.3.0-1) 7.3.0
>
>reproduce:
>
>        # save the attached .config to linux build tree
>
>        make ARCH=x86_64
>
>
>
>All warnings (new ones prefixed by >>):
>
>
>
>>> drivers/usb/common/debug.c:253:25: warning: restricted __le16 degrades to integer
>
>   drivers/usb/common/debug.c:254:25: warning: restricted __le16 degrades to integer
>
>   drivers/usb/common/debug.c:255:25: warning: restricted __le16 degrades to integer
>
>   drivers/usb/common/debug.c:256:25: warning: restricted __le16 degrades to integer
>
>   drivers/usb/common/debug.c:257:25: warning: restricted __le16 degrades to integer
>
>   drivers/usb/common/debug.c:258:25: warning: restricted __le16 degrades to integer
>
>
>
>vim +253 drivers/usb/common/debug.c
>
>
>
>cefb8b21 Pawel Laszczak 2018-12-23  205
>
>cefb8b21 Pawel Laszczak 2018-12-23  206  /**
>
>cefb8b21 Pawel Laszczak 2018-12-23  207   * usb_decode_ctrl - returns a string representation of ctrl request
>
>cefb8b21 Pawel Laszczak 2018-12-23  208   */
>
>cefb8b21 Pawel Laszczak 2018-12-23  209  const char *usb_decode_ctrl(char *str, __u8 bRequestType, __u8 bRequest,
>
>cefb8b21 Pawel Laszczak 2018-12-23  210  			    __u16 wValue,  __u16 wIndex, __u16 wLength)
>
>cefb8b21 Pawel Laszczak 2018-12-23  211  {
>
>cefb8b21 Pawel Laszczak 2018-12-23  212  	switch (bRequest) {
>
>cefb8b21 Pawel Laszczak 2018-12-23  213  	case USB_REQ_GET_STATUS:
>
>cefb8b21 Pawel Laszczak 2018-12-23  214  		usb_decode_get_status(bRequestType, wIndex, wLength, str);
>
>cefb8b21 Pawel Laszczak 2018-12-23  215  		break;
>
>cefb8b21 Pawel Laszczak 2018-12-23  216  	case USB_REQ_CLEAR_FEATURE:
>
>cefb8b21 Pawel Laszczak 2018-12-23  217  	case USB_REQ_SET_FEATURE:
>
>cefb8b21 Pawel Laszczak 2018-12-23  218  		usb_decode_set_clear_feature(bRequestType, bRequest, wValue,
>
>cefb8b21 Pawel Laszczak 2018-12-23  219  					     wIndex, str);
>
>cefb8b21 Pawel Laszczak 2018-12-23  220  		break;
>
>cefb8b21 Pawel Laszczak 2018-12-23  221  	case USB_REQ_SET_ADDRESS:
>
>cefb8b21 Pawel Laszczak 2018-12-23  222  		usb_decode_set_address(wValue, str);
>
>cefb8b21 Pawel Laszczak 2018-12-23  223  		break;
>
>cefb8b21 Pawel Laszczak 2018-12-23  224  	case USB_REQ_GET_DESCRIPTOR:
>
>cefb8b21 Pawel Laszczak 2018-12-23  225  	case USB_REQ_SET_DESCRIPTOR:
>
>cefb8b21 Pawel Laszczak 2018-12-23  226  		usb_decode_get_set_descriptor(bRequestType, bRequest, wValue,
>
>cefb8b21 Pawel Laszczak 2018-12-23  227  					      wIndex, wLength, str);
>
>cefb8b21 Pawel Laszczak 2018-12-23  228  		break;
>
>cefb8b21 Pawel Laszczak 2018-12-23  229  	case USB_REQ_GET_CONFIGURATION:
>
>cefb8b21 Pawel Laszczak 2018-12-23  230  		usb_decode_get_configuration(wLength, str);
>
>cefb8b21 Pawel Laszczak 2018-12-23  231  		break;
>
>cefb8b21 Pawel Laszczak 2018-12-23  232  	case USB_REQ_SET_CONFIGURATION:
>
>cefb8b21 Pawel Laszczak 2018-12-23  233  		usb_decode_set_configuration(wValue, str);
>
>cefb8b21 Pawel Laszczak 2018-12-23  234  		break;
>
>cefb8b21 Pawel Laszczak 2018-12-23  235  	case USB_REQ_GET_INTERFACE:
>
>cefb8b21 Pawel Laszczak 2018-12-23  236  		usb_decode_get_intf(wIndex, wLength, str);
>
>cefb8b21 Pawel Laszczak 2018-12-23  237  		break;
>
>cefb8b21 Pawel Laszczak 2018-12-23  238  	case USB_REQ_SET_INTERFACE:
>
>cefb8b21 Pawel Laszczak 2018-12-23  239  		usb_decode_set_intf(wValue, wIndex, str);
>
>cefb8b21 Pawel Laszczak 2018-12-23  240  		break;
>
>cefb8b21 Pawel Laszczak 2018-12-23  241  	case USB_REQ_SYNCH_FRAME:
>
>cefb8b21 Pawel Laszczak 2018-12-23  242  		usb_decode_synch_frame(wIndex, wLength, str);
>
>cefb8b21 Pawel Laszczak 2018-12-23  243  		break;
>
>cefb8b21 Pawel Laszczak 2018-12-23  244  	case USB_REQ_SET_SEL:
>
>cefb8b21 Pawel Laszczak 2018-12-23  245  		usb_decode_set_sel(wLength, str);
>
>cefb8b21 Pawel Laszczak 2018-12-23  246  		break;
>
>cefb8b21 Pawel Laszczak 2018-12-23  247  	case USB_REQ_SET_ISOCH_DELAY:
>
>cefb8b21 Pawel Laszczak 2018-12-23  248  		usb_decode_set_isoch_delay(wValue, str);
>
>cefb8b21 Pawel Laszczak 2018-12-23  249  		break;
>
>cefb8b21 Pawel Laszczak 2018-12-23  250  	default:
>
>cefb8b21 Pawel Laszczak 2018-12-23  251  		sprintf(str, "%02x %02x %02x %02x %02x %02x %02x %02x",
>
>cefb8b21 Pawel Laszczak 2018-12-23  252  			bRequestType, bRequest,
>
>cefb8b21 Pawel Laszczak 2018-12-23 @253  			cpu_to_le16(wValue) & 0xff,
>
>cefb8b21 Pawel Laszczak 2018-12-23  254  			cpu_to_le16(wValue) >> 8,
>
>cefb8b21 Pawel Laszczak 2018-12-23  255  			cpu_to_le16(wIndex) & 0xff,
>
>cefb8b21 Pawel Laszczak 2018-12-23  256  			cpu_to_le16(wIndex) >> 8,
>
>cefb8b21 Pawel Laszczak 2018-12-23  257  			cpu_to_le16(wLength) & 0xff,
>
>cefb8b21 Pawel Laszczak 2018-12-23  258  			cpu_to_le16(wLength) >> 8);
>
>cefb8b21 Pawel Laszczak 2018-12-23  259  	}
>
>cefb8b21 Pawel Laszczak 2018-12-23  260
>
>cefb8b21 Pawel Laszczak 2018-12-23  261  	return str;
>
>cefb8b21 Pawel Laszczak 2018-12-23  262  }
>
>cefb8b21 Pawel Laszczak 2018-12-23  263  EXPORT_SYMBOL_GPL(usb_decode_ctrl);
>
>cefb8b21 Pawel Laszczak 2018-12-23  264
>
>
>
>:::::: The code at line 253 was first introduced by commit
>
>:::::: cefb8b2176410ef0aacc22411a64f8a157612f14 usb:common Separated decoding functions from dwc3 driver.
>
>
>
>:::::: TO: Pawel Laszczak <pawell@cadence.com>
>
>:::::: CC: 0day robot <lkp@intel.com>
>
>
>
>---
>
>0-DAY kernel test infrastructure                Open Source Technology Center
>
>https://urldefense.proofpoint.com/v2/url?u=https-3A__lists.01.org_pipermail_kbuild-
>2Dall&d=DwIBAg&c=aUq983L2pue2FqKFoP6PGHMJQyoJ7kl3s3GZ-_haXqY&r=e1OgxfvkL0qo9XO6fX1gscva-w03uSYC1nIyxl89-
>rI&m=JS8MUBEUPN46-me57xUY-7hoBbSrlgd2SCB9ahNjK4s&s=7vN3rhLkFNcUQ6jriRP_jESizA-Wm6VxtiGb8qhTKqU&e=                   Intel
>Corporation

^ permalink raw reply	[flat|nested] 49+ messages in thread

* [v2,5/5] usb:cdns3 Add Cadence USB3 DRD Driver
@ 2018-12-31  9:42       ` Pawel Laszczak
  0 siblings, 0 replies; 49+ messages in thread
From: Pawel Laszczak @ 2018-12-31  9:42 UTC (permalink / raw)
  To: kbuild test robot
  Cc: kbuild-all, devicetree, gregkh, mark.rutland, linux-usb,
	hdegoede, heikki.krogerus, andy.shevchenko, robh+dt, rogerq,
	linux-kernel, Alan Douglas, jbergsagel, nsekhar, nm,
	Suresh Punnoose, peter.chen, Pawel Jez, Rahul Kumar

Hi

I can't recreate these issue on my environment. I use  gcc (Ubuntu 7.3.0-27ubuntu1~18.04) 7.3.0.  Maybe there are some differenct between these two compilers. 

Do you have any idea how I should change this fragment of code:
		sprintf(str, "%02x %02x %02x %02x %02x %02x %02x %02x",
			bRequestType, bRequest,
			cpu_to_le16(wValue) & 0xff,
			cpu_to_le16(wValue) >> 8,
			cpu_to_le16(wIndex) & 0xff,
			cpu_to_le16(wIndex) >> 8,
			cpu_to_le16(wLength) & 0xff,
			cpu_to_le16(wLength) >> 8);

to remove "restricted __le16 degrades to integer" warnings ?

Maybe I should cast all to u8. Then this code will look like:
		sprintf(str, "%02x %02x %02x %02x %02x %02x %02x %02x",
			bRequestType, bRequest,
			(u8)(cpu_to_le16(wValue) & 0xff),
			(u8)(cpu_to_le16(wValue) >> 8),
			(u8)(cpu_to_le16(wIndex) & 0xff),
			(u8)(cpu_to_le16(wIndex) >> 8),
			(u8)(cpu_to_le16(wLength) & 0xff),
			(u8)(cpu_to_le16(wLength) >> 8));

Should it Fix these warnings ?

Cheers,
Pawel

>Thank you for the patch! Perhaps something to improve:
>
>
>
>[auto build test WARNING on usb/usb-testing]
>
>[also build test WARNING on v4.20-rc7 next-20181221]
>
>[if your patch is applied to the wrong git tree, please drop us a note to help improve the system]
>
>
>
>url:    https://urldefense.proofpoint.com/v2/url?u=https-3A__github.com_0day-2Dci_linux_commits_Pawel-2DLaszczak_Introduced-
>2Dnew-2DCadence-2DUSBSS-2DDRD-2DDriver_20181223-2D231813&d=DwIBAg&c=aUq983L2pue2FqKFoP6PGHMJQyoJ7kl3s3GZ-
>_haXqY&r=e1OgxfvkL0qo9XO6fX1gscva-w03uSYC1nIyxl89-rI&m=JS8MUBEUPN46-me57xUY-
>7hoBbSrlgd2SCB9ahNjK4s&s=bhpHqRyEtMdMbWdGoBqQ9Pz9wq7pRA7-OohrGik3BpM&e=
>
>base:   https://urldefense.proofpoint.com/v2/url?u=https-
>3A__git.kernel.org_pub_scm_linux_kernel_git_gregkh_usb.git&d=DwIBAg&c=aUq983L2pue2FqKFoP6PGHMJQyoJ7kl3s3GZ-
>_haXqY&r=e1OgxfvkL0qo9XO6fX1gscva-w03uSYC1nIyxl89-rI&m=JS8MUBEUPN46-me57xUY-
>7hoBbSrlgd2SCB9ahNjK4s&s=Vf__lGpV27zdf1egowm9p8YBJjz9aMmgbi8nW_Z_DLk&e= usb-testing
>
>config: x86_64-allmodconfig (attached as .config)
>
>compiler: gcc-7 (Debian 7.3.0-1) 7.3.0
>
>reproduce:
>
>        # save the attached .config to linux build tree
>
>        make ARCH=x86_64
>
>
>
>All warnings (new ones prefixed by >>):
>
>
>
>>> drivers/usb/common/debug.c:253:25: warning: restricted __le16 degrades to integer
>
>   drivers/usb/common/debug.c:254:25: warning: restricted __le16 degrades to integer
>
>   drivers/usb/common/debug.c:255:25: warning: restricted __le16 degrades to integer
>
>   drivers/usb/common/debug.c:256:25: warning: restricted __le16 degrades to integer
>
>   drivers/usb/common/debug.c:257:25: warning: restricted __le16 degrades to integer
>
>   drivers/usb/common/debug.c:258:25: warning: restricted __le16 degrades to integer
>
>
>
>vim +253 drivers/usb/common/debug.c
>
>
>
>cefb8b21 Pawel Laszczak 2018-12-23  205
>
>cefb8b21 Pawel Laszczak 2018-12-23  206  /**
>
>cefb8b21 Pawel Laszczak 2018-12-23  207   * usb_decode_ctrl - returns a string representation of ctrl request
>
>cefb8b21 Pawel Laszczak 2018-12-23  208   */
>
>cefb8b21 Pawel Laszczak 2018-12-23  209  const char *usb_decode_ctrl(char *str, __u8 bRequestType, __u8 bRequest,
>
>cefb8b21 Pawel Laszczak 2018-12-23  210  			    __u16 wValue,  __u16 wIndex, __u16 wLength)
>
>cefb8b21 Pawel Laszczak 2018-12-23  211  {
>
>cefb8b21 Pawel Laszczak 2018-12-23  212  	switch (bRequest) {
>
>cefb8b21 Pawel Laszczak 2018-12-23  213  	case USB_REQ_GET_STATUS:
>
>cefb8b21 Pawel Laszczak 2018-12-23  214  		usb_decode_get_status(bRequestType, wIndex, wLength, str);
>
>cefb8b21 Pawel Laszczak 2018-12-23  215  		break;
>
>cefb8b21 Pawel Laszczak 2018-12-23  216  	case USB_REQ_CLEAR_FEATURE:
>
>cefb8b21 Pawel Laszczak 2018-12-23  217  	case USB_REQ_SET_FEATURE:
>
>cefb8b21 Pawel Laszczak 2018-12-23  218  		usb_decode_set_clear_feature(bRequestType, bRequest, wValue,
>
>cefb8b21 Pawel Laszczak 2018-12-23  219  					     wIndex, str);
>
>cefb8b21 Pawel Laszczak 2018-12-23  220  		break;
>
>cefb8b21 Pawel Laszczak 2018-12-23  221  	case USB_REQ_SET_ADDRESS:
>
>cefb8b21 Pawel Laszczak 2018-12-23  222  		usb_decode_set_address(wValue, str);
>
>cefb8b21 Pawel Laszczak 2018-12-23  223  		break;
>
>cefb8b21 Pawel Laszczak 2018-12-23  224  	case USB_REQ_GET_DESCRIPTOR:
>
>cefb8b21 Pawel Laszczak 2018-12-23  225  	case USB_REQ_SET_DESCRIPTOR:
>
>cefb8b21 Pawel Laszczak 2018-12-23  226  		usb_decode_get_set_descriptor(bRequestType, bRequest, wValue,
>
>cefb8b21 Pawel Laszczak 2018-12-23  227  					      wIndex, wLength, str);
>
>cefb8b21 Pawel Laszczak 2018-12-23  228  		break;
>
>cefb8b21 Pawel Laszczak 2018-12-23  229  	case USB_REQ_GET_CONFIGURATION:
>
>cefb8b21 Pawel Laszczak 2018-12-23  230  		usb_decode_get_configuration(wLength, str);
>
>cefb8b21 Pawel Laszczak 2018-12-23  231  		break;
>
>cefb8b21 Pawel Laszczak 2018-12-23  232  	case USB_REQ_SET_CONFIGURATION:
>
>cefb8b21 Pawel Laszczak 2018-12-23  233  		usb_decode_set_configuration(wValue, str);
>
>cefb8b21 Pawel Laszczak 2018-12-23  234  		break;
>
>cefb8b21 Pawel Laszczak 2018-12-23  235  	case USB_REQ_GET_INTERFACE:
>
>cefb8b21 Pawel Laszczak 2018-12-23  236  		usb_decode_get_intf(wIndex, wLength, str);
>
>cefb8b21 Pawel Laszczak 2018-12-23  237  		break;
>
>cefb8b21 Pawel Laszczak 2018-12-23  238  	case USB_REQ_SET_INTERFACE:
>
>cefb8b21 Pawel Laszczak 2018-12-23  239  		usb_decode_set_intf(wValue, wIndex, str);
>
>cefb8b21 Pawel Laszczak 2018-12-23  240  		break;
>
>cefb8b21 Pawel Laszczak 2018-12-23  241  	case USB_REQ_SYNCH_FRAME:
>
>cefb8b21 Pawel Laszczak 2018-12-23  242  		usb_decode_synch_frame(wIndex, wLength, str);
>
>cefb8b21 Pawel Laszczak 2018-12-23  243  		break;
>
>cefb8b21 Pawel Laszczak 2018-12-23  244  	case USB_REQ_SET_SEL:
>
>cefb8b21 Pawel Laszczak 2018-12-23  245  		usb_decode_set_sel(wLength, str);
>
>cefb8b21 Pawel Laszczak 2018-12-23  246  		break;
>
>cefb8b21 Pawel Laszczak 2018-12-23  247  	case USB_REQ_SET_ISOCH_DELAY:
>
>cefb8b21 Pawel Laszczak 2018-12-23  248  		usb_decode_set_isoch_delay(wValue, str);
>
>cefb8b21 Pawel Laszczak 2018-12-23  249  		break;
>
>cefb8b21 Pawel Laszczak 2018-12-23  250  	default:
>
>cefb8b21 Pawel Laszczak 2018-12-23  251  		sprintf(str, "%02x %02x %02x %02x %02x %02x %02x %02x",
>
>cefb8b21 Pawel Laszczak 2018-12-23  252  			bRequestType, bRequest,
>
>cefb8b21 Pawel Laszczak 2018-12-23 @253  			cpu_to_le16(wValue) & 0xff,
>
>cefb8b21 Pawel Laszczak 2018-12-23  254  			cpu_to_le16(wValue) >> 8,
>
>cefb8b21 Pawel Laszczak 2018-12-23  255  			cpu_to_le16(wIndex) & 0xff,
>
>cefb8b21 Pawel Laszczak 2018-12-23  256  			cpu_to_le16(wIndex) >> 8,
>
>cefb8b21 Pawel Laszczak 2018-12-23  257  			cpu_to_le16(wLength) & 0xff,
>
>cefb8b21 Pawel Laszczak 2018-12-23  258  			cpu_to_le16(wLength) >> 8);
>
>cefb8b21 Pawel Laszczak 2018-12-23  259  	}
>
>cefb8b21 Pawel Laszczak 2018-12-23  260
>
>cefb8b21 Pawel Laszczak 2018-12-23  261  	return str;
>
>cefb8b21 Pawel Laszczak 2018-12-23  262  }
>
>cefb8b21 Pawel Laszczak 2018-12-23  263  EXPORT_SYMBOL_GPL(usb_decode_ctrl);
>
>cefb8b21 Pawel Laszczak 2018-12-23  264
>
>
>
>:::::: The code at line 253 was first introduced by commit
>
>:::::: cefb8b2176410ef0aacc22411a64f8a157612f14 usb:common Separated decoding functions from dwc3 driver.
>
>
>
>:::::: TO: Pawel Laszczak <pawell@cadence.com>
>
>:::::: CC: 0day robot <lkp@intel.com>
>
>
>
>---
>
>0-DAY kernel test infrastructure                Open Source Technology Center
>
>https://urldefense.proofpoint.com/v2/url?u=https-3A__lists.01.org_pipermail_kbuild-
>2Dall&d=DwIBAg&c=aUq983L2pue2FqKFoP6PGHMJQyoJ7kl3s3GZ-_haXqY&r=e1OgxfvkL0qo9XO6fX1gscva-w03uSYC1nIyxl89-
>rI&m=JS8MUBEUPN46-me57xUY-7hoBbSrlgd2SCB9ahNjK4s&s=7vN3rhLkFNcUQ6jriRP_jESizA-Wm6VxtiGb8qhTKqU&e=                   Intel
>Corporation

^ permalink raw reply	[flat|nested] 49+ messages in thread

* RE: [PATCH v2 5/5] usb:cdns3 Add Cadence USB3 DRD Driver
  2018-12-29  7:41     ` [PATCH v2 5/5] " Chunfeng Yun
  (?)
@ 2018-12-31 12:31       ` Pawel Laszczak
  -1 siblings, 0 replies; 49+ messages in thread
From: Pawel Laszczak @ 2018-12-31 12:31 UTC (permalink / raw)
  To: Chunfeng Yun
  Cc: devicetree, gregkh, mark.rutland, linux-usb, hdegoede,
	heikki.krogerus, andy.shevchenko, robh+dt, rogerq, linux-kernel,
	Alan Douglas, jbergsagel, nsekhar, nm, Suresh Punnoose,
	peter.chen, Pawel Jez, Rahul Kumar

Hi

>On Sun, 2018-12-23 at 15:13 +0000, Pawel Laszczak wrote:
>> This patch introduce new Cadence USBSS DRD driver
>> to linux kernel.
>
><...>
>
>> diff --git a/drivers/usb/cdns3/cdns3-pci-wrap.c b/drivers/usb/cdns3/cdns3-pci-wrap.c
>> new file mode 100644
>> index 000000000000..e93179c45ece
>> --- /dev/null
>> +++ b/drivers/usb/cdns3/cdns3-pci-wrap.c
>> @@ -0,0 +1,157 @@
>> +// SPDX-License-Identifier: GPL-2.0
>> +/*
>> + * Cadence USBSS PCI Glue driver
>> + *
>> + * Copyright (C) 2018 Cadence.
>> + *
>> + * Author: Pawel Laszczak <pawell@cadence.com>
>> + */
>> +
>> +#include <linux/kernel.h>
>> +#include <linux/module.h>
>> +#include <linux/pci.h>
>> +#include <linux/platform_device.h>
>> +#include <linux/dma-mapping.h>
>> +#include <linux/slab.h>
>> +
>> +struct cdns3_wrap {
>> +	struct platform_device *plat_dev;
>> +	struct pci_dev *hg_dev;
>> +	struct resource dev_res[4];
>> +};
>> +
>> +struct cdns3_wrap wrap;
>> +
>> +#define RES_IRQ_ID		0
>> +#define RES_HOST_ID		1
>> +#define RES_DEV_ID		2
>> +#define RES_DRD_ID		3
>> +
>> +#define PCI_BAR_HOST		0
>> +#define PCI_BAR_DEV		2
>> +#define PCI_BAR_OTG		4
>> +
>> +#define PCI_DEV_FN_HOST_DEVICE	0
>> +#define PCI_DEV_FN_OTG		1
>> +
>> +#define PCI_DRIVER_NAME		"cdns3-pci-usbss"
>> +#define PLAT_DRIVER_NAME	"cdns-usb3"
>> +
>> +#define CDNS_VENDOR_ID 0x17cd
>> +#define CDNS_DEVICE_ID 0x0100
>> +
>> +/**
>> + * cdns3_pci_probe - Probe function for Cadence USB wrapper driver
>> + * @pdev: platform device object
>> + * @id: pci device id
>> + *
>> + * Returns 0 on success otherwise negative errno
>> + */
>> +static int cdns3_pci_probe(struct pci_dev *pdev,
>> +			   const struct pci_device_id *id)
>> +{
>> +	struct platform_device_info plat_info;
>> +	struct cdns3_wrap *wrap;
>> +	struct resource *res;
>> +	int err;
>> +
>> +	/*
>> +	 * for GADGET/HOST PCI (devfn) function number is 0,
>> +	 * for OTG PCI (devfn) function number is 1
>> +	 */
>> +	if (!id || pdev->devfn != PCI_DEV_FN_HOST_DEVICE)
>> +		return -EINVAL;
>> +
>> +	err = pcim_enable_device(pdev);
>> +	if (err) {
>> +		dev_err(&pdev->dev, "Enabling PCI device has failed %d\n", err);
>> +		return err;
>> +	}
>> +
>> +	pci_set_master(pdev);
>> +	wrap = devm_kzalloc(&pdev->dev, sizeof(*wrap), GFP_KERNEL);
>> +	if (!wrap) {
>> +		dev_err(&pdev->dev, "Failed to allocate memory\n");
>> +		return -ENOMEM;
>> +	}
>> +
>> +	/* function 0: host(BAR_0) + device(BAR_1) + otg(BAR_2)). */
>> +	memset(wrap->dev_res, 0x00,
>> +	       sizeof(struct resource) * ARRAY_SIZE(wrap->dev_res));
>> +	dev_dbg(&pdev->dev, "Initialize Device resources\n");
>> +	res = wrap->dev_res;
>> +
>> +	res[RES_DEV_ID].start = pci_resource_start(pdev, PCI_BAR_DEV);
>> +	res[RES_DEV_ID].end =   pci_resource_end(pdev, PCI_BAR_DEV);
>> +	res[RES_DEV_ID].name = "cdns3-dev-regs";
>> +	res[RES_DEV_ID].flags = IORESOURCE_MEM;
>> +	dev_dbg(&pdev->dev, "USBSS-DEV physical base addr: %pa\n",
>> +		&res[RES_DEV_ID].start);
>> +
>> +	res[RES_HOST_ID].start = pci_resource_start(pdev, PCI_BAR_HOST);
>> +	res[RES_HOST_ID].end = pci_resource_end(pdev, PCI_BAR_HOST);
>> +	res[RES_HOST_ID].name = "cdns3-xhci-regs";
>> +	res[RES_HOST_ID].flags = IORESOURCE_MEM;
>> +	dev_dbg(&pdev->dev, "USBSS-XHCI physical base addr: %pa\n",
>> +		&res[RES_HOST_ID].start);
>> +
>> +	res[RES_DRD_ID].start = pci_resource_start(pdev, PCI_BAR_OTG);
>> +	res[RES_DRD_ID].end =   pci_resource_end(pdev, PCI_BAR_OTG);
>> +	res[RES_DRD_ID].name = "cdns3-otg";
>> +	res[RES_DRD_ID].flags = IORESOURCE_MEM;
>> +	dev_dbg(&pdev->dev, "USBSS-DRD physical base addr: %pa\n",
>> +		&res[RES_DRD_ID].start);
>> +
>> +	/* Interrupt common for both device and XHCI */
>> +	wrap->dev_res[RES_IRQ_ID].start = pdev->irq;
>> +	wrap->dev_res[RES_IRQ_ID].name = "cdns3-irq";
>> +	wrap->dev_res[RES_IRQ_ID].flags = IORESOURCE_IRQ;
>> +
>> +	/* set up platform device info */
>> +	memset(&plat_info, 0, sizeof(plat_info));
>> +	plat_info.parent = &pdev->dev;
>> +	plat_info.fwnode = pdev->dev.fwnode;
>> +	plat_info.name = PLAT_DRIVER_NAME;
>> +	plat_info.id = pdev->devfn;
>> +	plat_info.res = wrap->dev_res;
>> +	plat_info.num_res = ARRAY_SIZE(wrap->dev_res);
>> +	plat_info.dma_mask = pdev->dma_mask;
>> +
>> +	/* register platform device */
>> +	wrap->plat_dev = platform_device_register_full(&plat_info);
>> +	if (IS_ERR(wrap->plat_dev)) {
>> +		err = PTR_ERR(wrap->plat_dev);
>> +		return err;
>> +	}
>return PTR_ERR(wrap->plat_dev);
Ok. 
>
>> +
>> +	pci_set_drvdata(pdev, wrap);
>> +
>> +	return err;
>> +}
>> +
>> +void cdns3_pci_remove(struct pci_dev *pdev)
>> +{
>> +	struct cdns3_wrap *wrap = (struct cdns3_wrap *)pci_get_drvdata(pdev);
>> +
>> +	platform_device_unregister(wrap->plat_dev);
>> +}
>> +
>> +static const struct pci_device_id cdns3_pci_ids[] = {
>> +	{ PCI_DEVICE(CDNS_VENDOR_ID, CDNS_DEVICE_ID), },
>> +	{ 0, }
>> +};
>> +
>> +static struct pci_driver cdns3_pci_driver = {
>> +	.name = PCI_DRIVER_NAME,
>> +	.id_table = cdns3_pci_ids,
>> +	.probe = cdns3_pci_probe,
>> +	.remove = cdns3_pci_remove,
>> +};
>> +
>> +module_pci_driver(cdns3_pci_driver);
>> +MODULE_DEVICE_TABLE(pci, cdns3_pci_ids);
>> +
>> +MODULE_AUTHOR("Pawel Laszczak <pawell@cadence.com>");
>> +MODULE_LICENSE("GPL v2");
>> +MODULE_DESCRIPTION("Cadence USBSS PCI wrapperr");
>> +
>> diff --git a/drivers/usb/cdns3/core.c b/drivers/usb/cdns3/core.c
>> new file mode 100644
>> index 000000000000..d274586aca36
>> --- /dev/null
>> +++ b/drivers/usb/cdns3/core.c
>> @@ -0,0 +1,406 @@
>> +// SPDX-License-Identifier: GPL-2.0
>> +/*
>> + * Cadence USBSS DRD Driver.
>> + *
>> + * Copyright (C) 2018 Cadence.
>> + * Copyright (C) 2017-2018 NXP
>> + *
>> + * Author: Peter Chen <peter.chen@nxp.com>
>> + *         Pawel Laszczak <pawell@cadence.com>
>> + */
>> +
>> +#include <linux/module.h>
>> +#include <linux/kernel.h>
>> +#include <linux/platform_device.h>
>> +#include <linux/interrupt.h>
>> +#include <linux/io.h>
>> +#include <linux/pm_runtime.h>
>> +
>> +#include "gadget.h"
>> +#include "core.h"
>> +#include "host-export.h"
>> +#include "gadget-export.h"
>> +#include "drd.h"
>> +#include "debug.h"
>> +
>> +static inline
>> +struct cdns3_role_driver *cdns3_get_current_role_driver(struct cdns3 *cdns)
>> +{
>> +	WARN_ON(cdns->role >= CDNS3_ROLE_END || !cdns->roles[cdns->role]);
>> +	return cdns->roles[cdns->role];
>> +}
>> +
>> +static inline int cdns3_role_start(struct cdns3 *cdns, enum cdns3_roles role)
>remove inline
Ok.

>> +{
>> +	int ret;
>> +
>> +	if (WARN_ON(role >= CDNS3_ROLE_END))
>> +		return 0;
>> +
>> +	if (!cdns->roles[role])
>> +		return -ENXIO;
>> +
>> +	if (cdns->roles[role]->state == CDNS3_ROLE_STATE_ACTIVE)
>> +		return 0;
>> +
>> +	mutex_lock(&cdns->mutex);
>> +	cdns->role = role;
>> +	ret = cdns->roles[role]->start(cdns);
>> +	if (!ret)
>> +		cdns->roles[role]->state = CDNS3_ROLE_STATE_ACTIVE;
>> +	mutex_unlock(&cdns->mutex);
>> +	return ret;
>> +}
>> +
>> +void cdns3_role_stop(struct cdns3 *cdns)
>> +{
>> +	enum cdns3_roles role = cdns->role;
>> +
>> +	if (role >= CDNS3_ROLE_END) {
>> +		WARN_ON(role > CDNS3_ROLE_END);
>> +		return;
>> +	}
>> +
>> +	if (cdns->roles[role]->state == CDNS3_ROLE_STATE_INACTIVE)
>> +		return;
>> +
>> +	mutex_lock(&cdns->mutex);
>> +	cdns->roles[role]->stop(cdns);
>> +	cdns->roles[role]->state = CDNS3_ROLE_STATE_INACTIVE;
>> +	mutex_unlock(&cdns->mutex);
>> +}
>> +
>> +/*
>> + * cdns->role gets from cdns3_get_initial_role, and this API tells role at the
>> + * runtime.
>> + * If both roles are supported, the role is selected based on vbus/id.
>> + * It could be read from OTG register or external connector.
>> + * If only single role is supported, only one role structure
>> + * is allocated, cdns->roles[CDNS3_ROLE_HOST] or cdns->roles[CDNS3_ROLE_GADGET].
>> + */
>> +static enum cdns3_roles cdns3_get_initial_role(struct cdns3 *cdns)
>> +{
>> +	if (cdns->roles[CDNS3_ROLE_HOST] && cdns->roles[CDNS3_ROLE_GADGET]) {
>> +		if (cdns3_is_host(cdns))
>> +			return CDNS3_ROLE_HOST;
>> +		if (cdns3_is_device(cdns))
>> +			return CDNS3_ROLE_GADGET;
>> +	}
>> +	return cdns->roles[CDNS3_ROLE_HOST]
>> +		? CDNS3_ROLE_HOST
>> +		: CDNS3_ROLE_GADGET;
>> +}
>> +
>> +static void cdns3_exit_roles(struct cdns3 *cdns)
>> +{
>> +	cdns3_role_stop(cdns);
>> +	cdns3_drd_exit(cdns);
>> +}
>> +
>> +/**
>> + * cdns3_core_init_role - initialize role of operation
>> + * @cdns: Pointer to cdns3 structure
>> + *
>> + * Returns 0 on success otherwise negative errno
>> + */
>> +static int cdns3_core_init_role(struct cdns3 *cdns)
>> +{
>> +	struct device *dev = cdns->dev;
>> +	enum usb_dr_mode best_dr_mode;
>> +	enum usb_dr_mode dr_mode;
>> +	int ret = 0;
>> +
>> +	dr_mode = usb_get_dr_mode(dev);
>> +	cdns->role = CDNS3_ROLE_END;
>> +
>> +	/*
>> +	 * If driver can't read mode by means of usb_get_dr_mdoe function then
>> +	 * chooses mode according with Kernel configuration. This setting
>> +	 * can be restricted later depending on strap pin configuration.
>> +	 */
>> +	if (dr_mode == USB_DR_MODE_UNKNOWN) {
>> +		if (IS_ENABLED(CONFIG_USB_CDNS3_HOST) &&
>> +		    IS_ENABLED(CONFIG_USB_CDNS3_GADGET))
>> +			dr_mode = USB_DR_MODE_OTG;
>> +		else if (IS_ENABLED(CONFIG_USB_CDNS3_HOST))
>> +			dr_mode = USB_DR_MODE_HOST;
>> +		else if (IS_ENABLED(CONFIG_USB_CDNS3_GADGET))
>> +			dr_mode = USB_DR_MODE_PERIPHERAL;
>> +	}
>> +
>> +	best_dr_mode = USB_DR_MODE_OTG;
>> +
>> +	if (dr_mode == USB_DR_MODE_OTG) {
>> +		best_dr_mode = cdns->dr_mode;
>> +	} else if (cdns->dr_mode == USB_DR_MODE_OTG) {
>> +		best_dr_mode = dr_mode;
>> +	} else if (cdns->dr_mode != dr_mode) {
>> +		dev_err(dev, "Incorrect DRD configuration\n");
>> +		return -EINVAL;
>> +	}
>> +
>> +	dr_mode = best_dr_mode;
>> +
>> +	if (dr_mode == USB_DR_MODE_OTG || dr_mode == USB_DR_MODE_HOST) {
>> +		ret = cdns3_host_init(cdns);
>> +		if (ret) {
>> +			dev_err(dev, "Host initialization failed with %d\n",
>> +				ret);
>> +			goto err;
>> +		}
>> +	}
>> +
>> +	if (dr_mode == USB_DR_MODE_OTG || dr_mode == USB_DR_MODE_PERIPHERAL) {
>> +		ret = cdns3_gadget_init(cdns);
>> +		if (ret) {
>> +			dev_err(dev, "Device initialization failed with %d\n",
>> +				ret);
>> +			goto err;
>> +		}
>> +	}
>> +
>> +	cdns->desired_dr_mode = dr_mode;
>> +	cdns->dr_mode = dr_mode;
>> +	/*
>> +	 * dr_mode could be change so DRD must update controller
>> +	 * configuration
>> +	 */
>> +	ret = cdns3_drd_update_mode(cdns);
>Do you need check ret value? if not, no need assign it

I will add checking. 
>> +
>> +	cdns->role = cdns3_get_initial_role(cdns);
>> +
>> +	ret = cdns3_role_start(cdns, cdns->role);
>> +	if (ret) {
>> +		dev_err(dev, "can't start %s role\n",
>> +			cdns3_get_current_role_driver(cdns)->name);
>> +		goto err;
>> +	}
>> +
>> +	return ret;
>> +err:
>> +	cdns3_exit_roles(cdns);
>> +	return ret;
>> +}
>> +
>> +/**
>> + * cdsn3_get_real_role - get real role of controller based on hardware settings.
>> + * @cdns: Pointer to cdns3 structure
>> + *
>> + * Returns role
>> + */
>> +enum cdns3_roles cdsn3_get_real_role(struct cdns3 *cdns)
>> +{
>> +	enum cdns3_roles role = CDNS3_ROLE_END;
>> +
>> +	if (cdns->current_dr_mode == USB_DR_MODE_OTG) {
>> +		if (cdns3_get_id(cdns))
>> +			role = CDNS3_ROLE_GADGET;
>> +		else
>> +			role = CDNS3_ROLE_HOST;
>> +	} else {
>> +		if (cdns3_is_host(cdns))
>> +			role = CDNS3_ROLE_HOST;
>> +		if (cdns3_is_device(cdns))
>> +			role = CDNS3_ROLE_GADGET;
>> +	}
>> +
>> +	return role;
>> +}
>> +
>> +/**
>> + * cdns3_role_switch - work queue handler for role switch
>> + *
>> + * @work: work queue item structure
>> + *
>> + * Handles below events:
>> + * - Role switch for dual-role devices
>> + * - CDNS3_ROLE_GADGET <--> CDNS3_ROLE_END for peripheral-only devices
>> + */
>> +static void cdns3_role_switch(struct work_struct *work)
>> +{
>> +	enum cdns3_roles role = CDNS3_ROLE_END;
>> +	struct cdns3_role_driver *role_drv;
>> +	enum cdns3_roles current_role;
>> +	struct cdns3 *cdns;
>> +	int ret = 0;
>> +
>> +	cdns = container_of(work, struct cdns3, role_switch_wq);
>> +
>> +	/* During switching cdns->role can be different then role */
>> +	role = cdsn3_get_real_role(cdns);
>> +
>> +	role_drv = cdns3_get_current_role_driver(cdns);
>> +
>> +	pm_runtime_get_sync(cdns->dev);
>> +
>> +	/* Disable current role. This state can be forced from user space. */
>> +	if (cdns->debug_disable && role_drv->state == CDNS3_ROLE_STATE_ACTIVE) {
>> +		cdns3_role_stop(cdns);
>> +		goto exit;
>> +	}
>> +
>> +	/* Do nothing if nothing changed */
>> +	if (cdns->role == role && role_drv->state == CDNS3_ROLE_STATE_ACTIVE)
>> +		goto exit;
>> +
>> +	cdns3_role_stop(cdns);
>> +
>> +	role = cdsn3_get_real_role(cdns);
>> +
>> +	current_role = cdns->role;
>> +	dev_dbg(cdns->dev, "Switching role");
>> +
>> +	ret = cdns3_role_start(cdns, role);
>> +
>remove blank line
>> +	if (ret) {
>> +		/* Back to current role */
>> +		dev_err(cdns->dev, "set %d has failed, back to %d\n",
>> +			role, current_role);
>> +		cdns3_role_start(cdns, current_role);
>> +	}
>> +exit:
>> +	pm_runtime_put_sync(cdns->dev);
>> +}
>> +
>> +/**
>> + * cdns3_probe - probe for cdns3 core device
>> + * @pdev: Pointer to cdns3 core platform device
>> + *
>> + * Returns 0 on success otherwise negative errno
>> + */
>> +static int cdns3_probe(struct platform_device *pdev)
>> +{
>> +	struct device *dev = &pdev->dev;
>> +	struct resource	*res;
>> +	struct cdns3 *cdns;
>> +	void __iomem *regs;
>> +	int ret;
>> +
>> +	cdns = devm_kzalloc(dev, sizeof(*cdns), GFP_KERNEL);
>> +	if (!cdns)
>> +		return -ENOMEM;
>> +
>> +	cdns->dev = dev;
>> +
>> +	platform_set_drvdata(pdev, cdns);
>> +
>> +	res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
>> +	if (!res) {
>> +		dev_err(dev, "missing IRQ\n");
>> +		return -ENODEV;
>> +	}
>> +	cdns->irq = res->start;
>> +
>> +	cdns->xhci_res[0] = *res;
>> +
>> +	/*
>> +	 * Request memory region
>> +	 * region-0: xHCI
>> +	 * region-1: Peripheral
>> +	 * region-2: OTG registers
>> +	 */
>> +	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
>> +	cdns->xhci_res[1] = *res;
>> +
>> +	res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
>> +	regs = devm_ioremap_resource(dev, res);
>> +	if (IS_ERR(regs))
>> +		return PTR_ERR(regs);
>> +	cdns->dev_regs	= regs;
>> +
>> +	res = platform_get_resource(pdev, IORESOURCE_MEM, 2);
>> +	cdns->otg_res = *res;
>It'll make the DTS clearer if add reg-names

Good Idea. I assume that in this case regs-names will be mandatory property  and
I have to replace platform_get_resource with platform_get_resource_byname. 
Also I have to made some changes in cdns3-pci-wrap.c file.

>> +
>> +	mutex_init(&cdns->mutex);
>> +
>> +	cdns->phy = devm_phy_get(dev, "cdns3,usbphy");
>Try to use devm_phy_optional_get()?

Ok, so the code will look like:
	cdns->phy = devm_phy_optional_get(dev, "cdns3,usbphy");
	if (IS_ERR(cdns->phy) )
		return PTR_ERR(cdns->phy);

>> +	if (IS_ERR(cdns->phy)) {
>> +		ret = PTR_ERR(cdns->phy);
>> +		if (ret == -ENODEV) {
>> +			cdns->phy = NULL;
>> +		} else if (ret == -EPROBE_DEFER) {
>> +			return ret;
>> +		} else {
>> +			dev_err(dev, "no phy found\n");
>> +			goto err0;
>> +		}
>> +	}
>> +
>> +	phy_init(cdns->phy);
>> +
>> +	INIT_WORK(&cdns->role_switch_wq, cdns3_role_switch);
>> +
>> +	ret = cdns3_drd_init(cdns);
>> +	if (ret)
>> +		goto err1;
>> +
>> +	ret = cdns3_core_init_role(cdns);
>> +	if (ret)
>> +		goto err1;
>> +
>> +	cdns3_debugfs_init(cdns);
>> +	device_set_wakeup_capable(dev, true);
>> +	pm_runtime_set_active(dev);
>> +	pm_runtime_enable(dev);
>> +
>> +	/*
>> +	 * The controller needs less time between bus and controller suspend,
>> +	 * and we also needs a small delay to avoid frequently entering low
>> +	 * power mode.
>> +	 */
>> +	pm_runtime_set_autosuspend_delay(dev, 20);
>> +	pm_runtime_mark_last_busy(dev);
>> +	pm_runtime_use_autosuspend(dev);
>> +	dev_dbg(dev, "Cadence USB3 core: probe succeed\n");
>> +
>> +	return 0;
>> +
>> +err1:
>> +	phy_exit(cdns->phy);
>> +err0:
>> +	return ret;
>> +}
>> +
>> +/**
>> + * cdns3_remove - unbind drd driver and clean up
>> + * @pdev: Pointer to Linux platform device
>> + *
>> + * Returns 0 on success otherwise negative errno
>> + */
>> +static int cdns3_remove(struct platform_device *pdev)
>> +{
>> +	struct cdns3 *cdns = platform_get_drvdata(pdev);
>> +
>> +	pm_runtime_get_sync(&pdev->dev);
>> +	pm_runtime_disable(&pdev->dev);
>> +	pm_runtime_put_noidle(&pdev->dev);
>> +	cdns3_debugfs_exit(cdns);
>> +	cdns3_exit_roles(cdns);
>> +	phy_exit(cdns->phy);
>> +	return 0;
>> +}
>> +
>> +#ifdef CONFIG_OF
>> +static const struct of_device_id of_cdns3_match[] = {
>> +	{ .compatible = "cdns,usb3-1.0.0" },
>> +	{ .compatible = "cdns,usb3-1.0.1" },
>> +	{ },
>> +};
>> +MODULE_DEVICE_TABLE(of, of_cdns3_match);
>> +#endif
>> +
>> +static struct platform_driver cdns3_driver = {
>> +	.probe		= cdns3_probe,
>> +	.remove		= cdns3_remove,
>> +	.driver		= {
>> +		.name	= "cdns-usb3",
>> +		.of_match_table	= of_match_ptr(of_cdns3_match),
>> +	},
>> +};
>> +
>> +module_platform_driver(cdns3_driver);
>> +
>> +MODULE_ALIAS("platform:cdns3");
>> +MODULE_AUTHOR("Pawel Laszczak <pawell@cadence.com>");
>> +MODULE_LICENSE("GPL v2");
>> +MODULE_DESCRIPTION("Cadence USB3 DRD Controller Driver");
>> diff --git a/drivers/usb/cdns3/core.h b/drivers/usb/cdns3/core.h
>> new file mode 100644
>> index 000000000000..fb4b39206158
>> --- /dev/null
>> +++ b/drivers/usb/cdns3/core.h
>> @@ -0,0 +1,116 @@
>> +/* SPDX-License-Identifier: GPL-2.0 */
>> +/*
>> + * Cadence USBSS DRD Header File.
>> + *
>> + * Copyright (C) 2017-2018 NXP
>> + * Copyright (C) 2018 Cadence.
>> + *
>> + * Authors: Peter Chen <peter.chen@nxp.com>
>> + *          Pawel Laszczak <pawell@cadence.com>
>> + */
>> +#include <linux/usb/otg.h>
>> +
>> +#ifndef __LINUX_CDNS3_CORE_H
>> +#define __LINUX_CDNS3_CORE_H
>> +
>> +struct cdns3;
>> +enum cdns3_roles {
>> +	CDNS3_ROLE_HOST = 0,
>> +	CDNS3_ROLE_GADGET,
>> +	CDNS3_ROLE_END,
>> +};
>> +
>> +/**
>> + * struct cdns3_role_driver - host/gadget role driver
>> + * @start: start this role
>> + * @stop: stop this role
>> + * @suspend: suspend callback for this role
>> + * @resume: resume callback for this role
>> + * @irq: irq handler for this role
>> + * @name: role name string (host/gadget)
>> + * @state: current state
>> + */
>> +struct cdns3_role_driver {
>> +	int (*start)(struct cdns3 *cdns);
>> +	void (*stop)(struct cdns3 *cdns);
>> +	int (*suspend)(struct cdns3 *cdns, bool do_wakeup);
>> +	int (*resume)(struct cdns3 *cdns, bool hibernated);
>> +	const char *name;
>> +#define CDNS3_ROLE_STATE_INACTIVE	0
>> +#define CDNS3_ROLE_STATE_ACTIVE		1
>> +	int state;
>> +};
>> +
>> +#define CDNS3_XHCI_RESOURCES_NUM	2
>> +/**
>> + * struct cdns3 - Representation of Cadence USB3 DRD controller.
>> + * @dev: pointer to Cadence device struct
>> + * @xhci_regs: pointer to base of xhci registers
>> + * @xhci_res: the resource for xhci
>> + * @dev_regs: pointer to base of dev registers
>> + * @otg_regs: pointer to base of otg registers
>> + * @irq: irq number for controller
>> + * @roles: array of supported roles for this controller
>> + * @role: current role
>> + * @host_dev: the child host device pointer for cdns3 core
>> + * @gadget_dev: the child gadget device pointer for cdns3 core
>> + * @usb: phy for this controller
>> + * @role_switch_wq: work queue item for role switch
>> + * @in_lpm: the controller in low power mode
>> + * @wakeup_int: the wakeup interrupt
>> + * @mutex: the mutex for concurrent code at driver
>> + * @dr_mode: supported mode of operation it can be only Host, only Device
>> + *           or OTG mode that allow to switch between Device and Host mode.
>> + *           This field based on firmware setting, kernel configuration
>> + *           and hardware configuration.
>> + * @current_dr_mode: current mode of operation when in dual-role mode
>> + * @desired_dr_mode: desired mode of operation when in dual-role mode.
>> + *           This value can be changed during runtime.
>> + *           Available options depends on  dr_mode:
>> + *           dr_mode                 |  desired_dr_mode and current_dr_mode
>> + *           ----------------------------------------------------------------
>> + *           USB_DR_MODE_HOST        | only USB_DR_MODE_HOST
>> + *           USB_DR_MODE_PERIPHERAL  | only USB_DR_MODE_PERIPHERAL
>> + *           USB_DR_MODE_OTG         | only USB_DR_MODE_HOST
>> + *           USB_DR_MODE_OTG         | only USB_DR_MODE_PERIPHERAL
>> + *           USB_DR_MODE_OTG         | USB_DR_MODE_OTG
>> + *
>> + *           Desired_dr_role can be changed by means of debugfs.
>> + * @root: debugfs root folder pointer
>> + * @debug_disable:
>> + */
>> +struct cdns3 {
>> +	struct device			*dev;
>> +	void __iomem			*xhci_regs;
>> +	struct resource			xhci_res[CDNS3_XHCI_RESOURCES_NUM];
>> +	struct cdns3_usb_regs __iomem	*dev_regs;
>> +
>> +	struct resource			otg_res;
>> +	struct cdns3_otg_legacy_regs	*otg_v0_regs;
>> +	struct cdns3_otg_regs		*otg_v1_regs;
>> +	struct cdns3_otg_common_regs	*otg_regs;
>> +#define CDNS3_CONTROLLER_V0	0
>> +#define CDNS3_CONTROLLER_V1	1
>> +	u32				version;
>> +
>> +	int				irq;
>> +	struct cdns3_role_driver	*roles[CDNS3_ROLE_END];
>> +	enum cdns3_roles		role;
>> +	struct platform_device		*host_dev;
>> +	struct cdns3_device		*gadget_dev;
>> +	struct phy			*phy;
>> +	struct work_struct		role_switch_wq;
>> +	int				in_lpm:1;
>> +	int				wakeup_int:1;
>> +	/* mutext used in workqueue*/
>> +	struct mutex			mutex;
>> +	enum usb_dr_mode		dr_mode;
>> +	enum usb_dr_mode		current_dr_mode;
>> +	enum usb_dr_mode		desired_dr_mode;
>> +	struct dentry			*root;
>> +	int				debug_disable:1;
>> +};
>> +
>> +void cdns3_role_stop(struct cdns3 *cdns);
>> +
>> +#endif /* __LINUX_CDNS3_CORE_H */
>> diff --git a/drivers/usb/cdns3/debug.h b/drivers/usb/cdns3/debug.h
>> new file mode 100644
>> index 000000000000..94f9ef15f899
>> --- /dev/null
>> +++ b/drivers/usb/cdns3/debug.h
>> @@ -0,0 +1,166 @@
>> +/* SPDX-License-Identifier: GPL-2.0 */
>> +/*
>> + * Cadence USBSS DRD Driver.
>> + * Debug header file.
>> + *
>> + * Copyright (C) 2018 Cadence.
>> + *
>> + * Author: Pawel Laszczak <pawell@cadence.com>
>> + */
>> +#ifndef __LINUX_CDNS3_DEBUG
>> +#define __LINUX_CDNS3_DEBUG
>> +
>> +#include "core.h"
>> +
>> +static inline char *cdns3_decode_usb_irq(char *str,
>> +					 enum usb_device_speed speed,
>> +
>too many sentences as inline function
>> 					 u32 usb_ists)
>> +{
>> +	int ret;
>> +
>> +	ret = sprintf(str, "IRQ %08x = ", usb_ists);
>> +
>> +	if (usb_ists & (USB_ISTS_CON2I | USB_ISTS_CONI)) {
>> +		ret += sprintf(str + ret, "Connection %s\n",
>> +			       usb_speed_string(speed));
>> +	}
>> +	if (usb_ists & USB_ISTS_CON2I || usb_ists & USB_ISTS_CONI)
>> +		ret += sprintf(str + ret, "Disconnection ");
>> +	if (usb_ists & USB_ISTS_L2ENTI)
>> +		ret += sprintf(str + ret, "suspended ");
>> +
>> +	if (usb_ists & USB_ISTS_L2EXTI)
>> +		ret += sprintf(str + ret, "L2 exit ");
>> +	if (usb_ists & USB_ISTS_U3EXTI)
>> +		ret += sprintf(str + ret, "U3 exit ");
>> +	if (usb_ists & USB_ISTS_UWRESI)
>> +		ret += sprintf(str + ret, "Warm Reset ");
>> +	if (usb_ists & USB_ISTS_UHRESI)
>> +		ret += sprintf(str + ret, "Hot Reset ");
>> +	if (usb_ists & USB_ISTS_U2RESI)
>> +		ret += sprintf(str + ret, "Reset");
>> +
>> +	return str;
>> +}
>> +
>> +static inline  char *cdns3_decode_ep_irq(char *str,
>> +					 u32 ep_sts,
>> +					 const char *ep_name)
>ditto
>> +{
>> +	int ret;
>> +
>> +	ret = sprintf(str, "IRQ for %s: %08x ", ep_name, ep_sts);
>> +
>> +	if (ep_sts & EP_STS_SETUP)
>> +		ret += sprintf(str + ret, "SETUP ");
>> +	if (ep_sts & EP_STS_IOC)
>> +		ret += sprintf(str + ret, "IOC ");
>> +	if (ep_sts & EP_STS_ISP)
>> +		ret += sprintf(str + ret, "ISP ");
>> +	if (ep_sts & EP_STS_DESCMIS)
>> +		ret += sprintf(str + ret, "DESCMIS ");
>> +	if (ep_sts & EP_STS_STREAMR)
>> +		ret += sprintf(str + ret, "STREAMR ");
>> +	if (ep_sts & EP_STS_MD_EXIT)
>> +		ret += sprintf(str + ret, "MD_EXIT ");
>> +	if (ep_sts & EP_STS_TRBERR)
>> +		ret += sprintf(str + ret, "TRBERR ");
>> +	if (ep_sts & EP_STS_NRDY)
>> +		ret += sprintf(str + ret, "NRDY ");
>> +	if (ep_sts & EP_STS_PRIME)
>> +		ret += sprintf(str + ret, "PRIME ");
>> +	if (ep_sts & EP_STS_SIDERR)
>> +		ret += sprintf(str + ret, "SIDERRT ");
>> +	if (ep_sts & EP_STS_OUTSMM)
>> +		ret += sprintf(str + ret, "OUTSMM ");
>> +	if (ep_sts & EP_STS_ISOERR)
>> +		ret += sprintf(str + ret, "ISOERR ");
>> +	if (ep_sts & EP_STS_IOT)
>> +		ret += sprintf(str + ret, "IOT ");
>> +
>> +	return str;
>> +}
>> +
>> +static inline char *cdns3_decode_epx_irq(char *str,
>> +					 char *ep_name,
>> +					 u32 ep_sts)
>> +{
>> +	return cdns3_decode_ep_irq(str, ep_sts, ep_name);
>> +}
>> +
>> +static inline char *cdns3_decode_ep0_irq(char *str,
>> +					 int dir,
>> +					 u32 ep_sts)
>> +{
>> +	return cdns3_decode_ep_irq(str, ep_sts,
>> +				   dir ? "ep0IN" : "ep0OUT");
>> +}
>> +
>> +/**
>> + * Debug a transfer ring.
>> + *
>> + * Prints out all TRBs in the endpoint ring, even those after the Link TRB.
>> + *.
>> + */
>> +static inline char *cdns3_dbg_ring(struct cdns3_endpoint *priv_ep,
>> +				   struct cdns3_trb *ring, char *str)
>ditto
>> +{
>> +	dma_addr_t addr = priv_ep->trb_pool_dma;
>> +	struct cdns3_trb *trb;
>> +	int trb_per_sector;
>> +	int ret = 0;
>> +	int i;
>> +
>> +	trb_per_sector = GET_TRBS_PER_SEGMENT(priv_ep->type);
>> +
>> +	trb = &priv_ep->trb_pool[priv_ep->dequeue];
>> +	ret += sprintf(str + ret, "\n\t\tRing contents for %s:", priv_ep->name);
>> +
>> +	ret += sprintf(str + ret,
>> +		       "\n\t\tRing deq index: %d, trb: %p (virt), 0x%llx (dma)\n",
>> +		       priv_ep->dequeue, trb,
>> +		       (unsigned long long)cdns3_trb_virt_to_dma(priv_ep, trb));
>> +
>> +	trb = &priv_ep->trb_pool[priv_ep->enqueue];
>> +	ret += sprintf(str + ret,
>> +		       "\t\tRing enq index: %d, trb: %p (virt), 0x%llx (dma)\n",
>> +		       priv_ep->enqueue, trb,
>> +		       (unsigned long long)cdns3_trb_virt_to_dma(priv_ep, trb));
>> +
>> +	ret += sprintf(str + ret,
>> +		       "\t\tfree trbs: %d, CCS=%d, PCS=%d\n",
>> +		       priv_ep->free_trbs, priv_ep->ccs, priv_ep->pcs);
>> +
>> +	if (trb_per_sector > TRBS_PER_SEGMENT)
>> +		trb_per_sector = TRBS_PER_SEGMENT;
>> +
>> +	if (trb_per_sector > TRBS_PER_SEGMENT) {
>> +		sprintf(str + ret, "\t\tTo big transfer ring %d\n",
>> +			trb_per_sector);
>> +		return str;
>> +	}
>> +
>> +	for (i = 0; i < trb_per_sector; ++i) {
>> +		trb = &ring[i];
>> +		ret += sprintf(str + ret,
>> +			"\t\t@%pad %08x %08x %08x\n", &addr,
>> +			le32_to_cpu(trb->buffer),
>> +			le32_to_cpu(trb->length),
>> +			le32_to_cpu(trb->control));
>> +		addr += sizeof(*trb);
>> +	}
>> +
>> +	return str;
>> +}
>> +
>> +#ifdef CONFIG_DEBUG_FS
>> +void cdns3_debugfs_init(struct cdns3 *cdns);
>> +void cdns3_debugfs_exit(struct cdns3 *cdns);
>> +#else
>> +void cdns3_debugfs_init(struct cdns3 *cdns);
>> +{  }
>> +void cdns3_debugfs_exit(struct cdns3 *cdns);
>> +{  }
>> +#endif
>> +
>> +#endif /*__LINUX_CDNS3_DEBUG*/
>> diff --git a/drivers/usb/cdns3/debugfs.c b/drivers/usb/cdns3/debugfs.c
>> new file mode 100644
>> index 000000000000..d7919f5c1d90
>> --- /dev/null
>> +++ b/drivers/usb/cdns3/debugfs.c
>> @@ -0,0 +1,168 @@
>> +// SPDX-License-Identifier: GPL-2.0
>> +/*
>> + * Cadence USBSS DRD Controller DebugFS filer.
>> + *
>> + * Copyright (C) 2018 Cadence.
>> + *
>> + * Author: Pawel Laszczak <pawell@cadence.com>
>> + */
>> +
>> +#include <linux/types.h>
>> +#include <linux/debugfs.h>
>> +#include <linux/seq_file.h>
>> +#include <linux/uaccess.h>
>> +
>> +#include "core.h"
>> +#include "gadget.h"
>> +#include "drd.h"
>> +
>> +static int cdns3_mode_show(struct seq_file *s, void *unused)
>> +{
>> +	struct cdns3 *cdns = s->private;
>> +
>> +	switch (cdns->current_dr_mode) {
>> +	case USB_DR_MODE_HOST:
>> +		seq_puts(s, "host\n");
>> +		break;
>> +	case USB_DR_MODE_PERIPHERAL:
>> +		seq_puts(s, "device\n");
>> +		break;
>> +	case USB_DR_MODE_OTG:
>> +		seq_puts(s, "otg\n");
>> +		break;
>> +	default:
>> +		seq_puts(s, "UNKNOWN mode\n");
>> +	}
>> +
>> +	return 0;
>> +}
>> +
>> +static int cdns3_mode_open(struct inode *inode, struct file *file)
>> +{
>> +	return single_open(file, cdns3_mode_show, inode->i_private);
>> +}
>> +
>> +static ssize_t cdns3_mode_write(struct file *file,
>> +				const char __user *ubuf,
>> +				size_t count, loff_t *ppos)
>> +{
>> +	struct seq_file	 *s = file->private_data;
>> +	struct cdns3 *cdns = s->private;
>> +	u32 mode = USB_DR_MODE_UNKNOWN;
>> +	char buf[32];
>> +
>> +	if (copy_from_user(&buf, ubuf, min_t(size_t, sizeof(buf) - 1, count)))
>> +		return -EFAULT;
>> +
>> +	if (!strncmp(buf, "host", 4)) {
>> +		if (cdns->dr_mode == USB_DR_MODE_HOST ||
>> +		    cdns->dr_mode == USB_DR_MODE_OTG) {
>> +			mode = USB_DR_MODE_HOST;
>> +		}
>> +	}
>> +
>> +	if (!strncmp(buf, "device", 6))
>> +		if (cdns->dr_mode == USB_DR_MODE_PERIPHERAL ||
>> +		    cdns->dr_mode == USB_DR_MODE_OTG)
>> +			mode = USB_DR_MODE_PERIPHERAL;
>> +
>> +	if (!strncmp(buf, "otg", 3) && cdns->dr_mode == USB_DR_MODE_OTG)
>> +		mode = USB_DR_MODE_OTG;
>> +
>> +	if (mode == USB_DR_MODE_UNKNOWN) {
>> +		dev_err(cdns->dev, "Failed: incorrect mode setting\n");
>> +		return -EFAULT;
>> +	}
>> +
>> +	if (cdns->current_dr_mode != mode) {
>> +		cdns->desired_dr_mode = mode;
>> +		cdns->debug_disable = 0;
>> +		cdns3_role_stop(cdns);
>> +		cdns3_drd_update_mode(cdns);
>> +		queue_work(system_freezable_wq, &cdns->role_switch_wq);
>> +	}
>> +
>> +	return count;
>> +}
>> +
>> +static const struct file_operations cdns3_mode_fops = {
>> +	.open			= cdns3_mode_open,
>> +	.write			= cdns3_mode_write,
>> +	.read			= seq_read,
>> +	.llseek			= seq_lseek,
>> +	.release		= single_release,
>> +};
>> +
>> +static int cdns3_disable_show(struct seq_file *s, void *unused)
>> +{
>> +	struct cdns3 *cdns = s->private;
>> +
>> +	if (!cdns->debug_disable)
>> +		seq_puts(s, "0\n");
>> +	else
>> +		seq_puts(s, "1\n");
>> +
>> +	return 0;
>> +}
>> +
>> +static ssize_t cdns3_disable_write(struct file *file,
>> +				   const char __user *ubuf,
>> +				   size_t count, loff_t *ppos)
>> +{
>> +	struct seq_file	 *s = file->private_data;
>> +	struct cdns3 *cdns = s->private;
>> +	int disable;
>> +	char buf[32];
>> +
>> +	if (copy_from_user(&buf, ubuf, min_t(size_t, sizeof(buf) - 1, count)))
>> +		return -EFAULT;
>> +
>> +	if (!strncmp(buf, "1", 1) || !strncmp(buf, "yes", 3)) {
>> +		disable = 1;
>> +	} else if (!strncmp(buf, "0", 1) || !strncmp(buf, "no", 2)) {
>> +		disable = 0;
>> +	} else {
>> +		dev_err(cdns->dev, "Failed: incorrect disable setting\n");
>> +		return -EFAULT;
>> +	}
>> +
>There is common API kstrtobool()?
Ok, much simpler.
>
>> +	if (disable != cdns->debug_disable) {
>> +		cdns->debug_disable = disable;
>> +		queue_work(system_freezable_wq, &cdns->role_switch_wq);
>> +	}
>> +
>> +	return count;
>> +}
>> +
>> +static int cdns3_disable_open(struct inode *inode, struct file *file)
>> +{
>> +	return single_open(file, cdns3_disable_show, inode->i_private);
>> +}
>> +
>> +static const struct file_operations cdns3_disable_fops = {
>> +	.open			= cdns3_disable_open,
>> +	.write			= cdns3_disable_write,
>> +	.read			= seq_read,
>> +	.llseek			= seq_lseek,
>> +	.release		= single_release,
>> +};
>> +
>> +void cdns3_debugfs_init(struct cdns3 *cdns)
>> +{
>> +	struct dentry *root;
>> +
>> +	root = debugfs_create_dir(dev_name(cdns->dev), NULL);
>> +	cdns->root = root;
>> +	if (IS_ENABLED(CONFIG_USB_CDNS3_GADGET) &&
>> +	    IS_ENABLED(CONFIG_USB_CDNS3_HOST))
>> +		debugfs_create_file("mode", 0644, root, cdns,
>> +				    &cdns3_mode_fops);
>> +
>> +	debugfs_create_file("disable", 0644, root, cdns,
>> +			    &cdns3_disable_fops);
>> +}
>> +
>> +void cdns3_debugfs_exit(struct cdns3 *cdns)
>> +{
>> +	debugfs_remove_recursive(cdns->root);
>> +}
>> diff --git a/drivers/usb/cdns3/drd.c b/drivers/usb/cdns3/drd.c
>> new file mode 100644
>> index 000000000000..b0c32302eb0b
>> --- /dev/null
>> +++ b/drivers/usb/cdns3/drd.c
>> @@ -0,0 +1,350 @@
>> +// SPDX-License-Identifier: GPL-2.0
>> +/*
>> + * Cadence USBSS DRD Driver.
>> + *
>> + * Copyright (C) 2018 Cadence.
>> + *
>> + * Author: Pawel Laszczak <pawell@cadence.com
>> + *
>> + */
>> +#include <linux/kernel.h>
>> +#include <linux/interrupt.h>
>> +#include <linux/delay.h>
>> +#include <linux/usb/otg.h>
>> +
>> +#include "gadget.h"
>> +#include "drd.h"
>> +#include "core.h"
>> +
>> +static int cdns3_drd_switch_gadget(struct cdns3 *cdns, int on);
>> +static int cdns3_drd_switch_host(struct cdns3 *cdns, int on);
>> +
>> +/**
>> + * cdns3_set_mode - change mode of OTG Core
>> + * @cdns: pointer to context structure
>> + * @mode: selected mode from cdns_role
>> + */
>> +void cdns3_set_mode(struct cdns3 *cdns, enum usb_dr_mode mode)
>> +{
>> +	u32 reg;
>> +
>> +	cdns->current_dr_mode = mode;
>> +
>> +	switch (mode) {
>> +	case USB_DR_MODE_PERIPHERAL:
>> +		dev_info(cdns->dev, "Set controller to Gadget mode\n");
>> +		cdns3_drd_switch_gadget(cdns, 1);
>> +		break;
>> +	case USB_DR_MODE_HOST:
>> +		dev_info(cdns->dev, "Set controller to Host mode\n");
>> +		cdns3_drd_switch_host(cdns, 1);
>> +		break;
>> +	case USB_DR_MODE_OTG:
>> +		dev_info(cdns->dev, "Set controller to OTG mode\n");
>> +		if (cdns->version == CDNS3_CONTROLLER_V1) {
>> +			reg = readl(&cdns->otg_v1_regs->override);
>> +			reg |= OVERRIDE_IDPULLUP;
>> +			writel(reg, &cdns->otg_v1_regs->override);
>> +		} else {
>> +			reg = readl(&cdns->otg_v0_regs->ctrl1);
>> +			reg |= OVERRIDE_IDPULLUP_V0;
>> +			writel(reg, &cdns->otg_v0_regs->ctrl1);
>> +		}
>> +
>> +		/*
>> +		 * Hardware specification says: "ID_VALUE must be valid within
>> +		 * 50ms after idpullup is set to '1" so driver must wait
>> +		 * 50ms before reading this pin.
>> +		 */
>> +		usleep_range(50000, 60000);
>> +		break;
>> +	default:
>> +		cdns->current_dr_mode = USB_DR_MODE_UNKNOWN;
>> +		dev_err(cdns->dev, "Unsupported mode of operation %d\n", mode);
>> +		return;
>> +	}
>> +}
>> +
>> +int cdns3_get_id(struct cdns3 *cdns)
>> +{
>> +	int id;
>> +
>> +	id = readl(&cdns->otg_regs->sts) & OTGSTS_ID_VALUE;
>> +	dev_dbg(cdns->dev, "OTG ID: %d", id);
>> +	return  1 ; //id;
>return fixed value ?

My mistake. I forgot to restore this.  
It should be "return id" .
I thought that checkpatch remained me about this :). 
Thanks. 

>> +}
>> +
>> +int cdns3_is_host(struct cdns3 *cdns)
>> +{
>> +	if (cdns->current_dr_mode == USB_DR_MODE_HOST)
>> +		return 1;
>> +	else if (!cdns3_get_id(cdns))
>> +		return 1;
>> +
>> +	return 0;
>> +}
>> +
>> +
>
>> +
>> +/**
>> + * cdns3_init_otg_mode - initialize drd controller
>> + * @cdns: Pointer to controller context structure
>> + *
>> + * Returns 0 on success otherwise negative errno
>void function
It should be int. 
>> + */
>> +static void cdns3_init_otg_mode(struct cdns3 *cdns)
>> +{
>> +	cdns3_otg_disable_irq(cdns);
>> +	/* clear all interrupts */
>> +	writel(~0, &cdns->otg_regs->ivect);
>> +
>> +	cdns3_set_mode(cdns, USB_DR_MODE_OTG);
>> +
>> +	if (cdns3_is_host(cdns))
>> +		cdns3_drd_switch_host(cdns, 1);
>> +	else
>> +		cdns3_drd_switch_gadget(cdns, 1);
>> +
>> +	cdns3_otg_enable_irq(cdns);
>> +}
>> +
>> +/**
>> + * cdns3_drd_update_mode - initialize mode of operation
>> + * @cdns: Pointer to controller context structure
>> + *
>> + * Returns 0 on success otherwise negative errno
>> + */
>> +int cdns3_drd_update_mode(struct cdns3 *cdns)
>> +{
>> +	int ret = 0;
>> +
>> +	if (cdns->desired_dr_mode == cdns->current_dr_mode)
>> +		return ret;
>> +
>> +	cdns3_drd_switch_gadget(cdns, 0);
>> +	cdns3_drd_switch_host(cdns, 0);
>> +
>> +	switch (cdns->desired_dr_mode) {
>> +	case USB_DR_MODE_PERIPHERAL:
>> +		cdns3_set_mode(cdns, USB_DR_MODE_PERIPHERAL);
>> +		break;
>> +	case USB_DR_MODE_HOST:
>> +		cdns3_set_mode(cdns, USB_DR_MODE_HOST);
>> +		break;
>> +	case USB_DR_MODE_OTG:
>> +		cdns3_init_otg_mode(cdns);
>> +		break;
>> +	default:
>> +		dev_err(cdns->dev, "Unsupported mode of operation %d\n",
>> +			cdns->dr_mode);
>> +		return -EINVAL;
>> +	}
>> +
>> +	return ret;
>no need define local variable @ret

It will keep return value from cdns3_set_mode and cdns3_init_otg_mode functions.
>> +}
>> +
>> +/**
>> + * cdns3_drd_irq - interrupt handler for OTG events
>> + *
>> + * @irq: irq number for cdns3 core device
>> + * @data: structure of cdns3
>> + *
>> + * Returns IRQ_HANDLED or IRQ_NONE
>> + */
>> +static irqreturn_t cdns3_drd_irq(int irq, void *data)
>> +{
>> +	irqreturn_t ret = IRQ_NONE;
>> +	struct cdns3 *cdns = data;
>> +	u32 reg;
>> +
>> +	if (cdns->dr_mode != USB_DR_MODE_OTG)
>> +		return ret;
>> +
>> +	reg = readl(&cdns->otg_regs->ivect);
>> +
>> +	if (!reg)
>> +		return ret;
>> +
>> +	if (reg & OTGIEN_ID_CHANGE_INT) {
>> +		dev_dbg(cdns->dev, "OTG IRQ: new ID: %d\n",
>> +			cdns3_get_id(cdns));
>> +
>> +		queue_work(system_freezable_wq, &cdns->role_switch_wq);
>> +
>do you need to clear interrupts status here?
It's the best place for clearing interrupt. Driver enables only detection id changes. 
We can read ID status also from OTG status register. Even if driver lost some events it will 
not be a problem.  

>> +		ret = IRQ_HANDLED;
>> +	}
>> +
>> +	writel(~0, &cdns->otg_regs->ivect);
>> +	return ret;
>> +}
>> +
>> +int cdns3_drd_init(struct cdns3 *cdns)
>> +{
>> +	void __iomem *regs;
>> +	int ret = 0;
>> +	u32 state;
>> +
>> +	regs = devm_ioremap_resource(cdns->dev, &cdns->otg_res);
>> +	if (IS_ERR(regs))
>> +		return PTR_ERR(regs);
>> +
>> +	/* Detection of DRD version. Controller has been released
>> +	 * in two versions. Both are similar, but they have same changes
>> +	 * in register maps.
>> +	 * The first register in old version is command register and it's read
>> +	 * only, so driver should read 0 from it. On the other hand, in v1
>> +	 * the first register contains device ID number which is not set to 0.
>> +	 * Driver uses this fact to detect the proper version of
>> +	 * controller.
>> +	 */
>> +	cdns->otg_v0_regs = regs;
>> +	if (!readl(&cdns->otg_v0_regs->cmd)) {
>> +		cdns->version  = CDNS3_CONTROLLER_V0;
>> +		cdns->otg_v1_regs = NULL;
>> +		cdns->otg_regs = regs;
>> +		dev_info(cdns->dev, "DRD version v0 (%08x)\n",
>> +			 readl(&cdns->otg_v0_regs->version));
>> +	} else {
>> +		cdns->otg_v0_regs = NULL;
>> +		cdns->otg_v1_regs = regs;
>> +		cdns->otg_regs = (void *)&cdns->otg_v1_regs->cmd;
>> +		cdns->version  = CDNS3_CONTROLLER_V1;
>> +		dev_info(cdns->dev, "DRD version v1 (ID: %08x, rev: %08x)\n",
>> +			 readl(&cdns->otg_v1_regs->did),
>> +			 readl(&cdns->otg_v1_regs->rid));
>> +	}
>> +
>> +	state = OTGSTS_STRAP(readl(&cdns->otg_regs->sts));
>> +
>> +	/* Update dr_mode according to STRAP configuration. */
>> +	cdns->dr_mode = USB_DR_MODE_OTG;
>> +	if (state == OTGSTS_STRAP_HOST) {
>> +		dev_info(cdns->dev, "Controller strapped to HOST\n");
>> +		cdns->dr_mode = USB_DR_MODE_HOST;
>> +	} else if (state == OTGSTS_STRAP_GADGET) {
>> +		dev_info(cdns->dev, "Controller strapped to PERIPHERAL\n");
>> +		cdns->dr_mode = USB_DR_MODE_PERIPHERAL;
>> +	}
>> +
>> +	cdns->desired_dr_mode = cdns->dr_mode;
>> +	cdns->current_dr_mode = USB_DR_MODE_UNKNOWN;
>> +
>> +	ret = devm_request_threaded_irq(cdns->dev, cdns->irq, cdns3_drd_irq,
>> +					NULL, IRQF_SHARED,
>> +					dev_name(cdns->dev), cdns);
>> +
>> +	if (ret)
>> +		return ret;
>> +
>> +	state = readl(&cdns->otg_regs->sts);
>> +	if (OTGSTS_OTG_NRDY(state) != 0) {
>> +		dev_err(cdns->dev, "Cadence USB3 OTG device not ready\n");
>> +		return -ENODEV;
>> +	}
>> +
>> +	ret = cdns3_drd_update_mode(cdns);
>> +
>> +	return ret;
>return cdns3_drd_update_mode();
>> +}
>> +
>> +int cdns3_drd_exit(struct cdns3 *cdns)
>> +{
>> +	return cdns3_drd_switch_host(cdns, 0);
>> +}
>
>
><...>
>
>> diff --git a/drivers/usb/cdns3/ep0.c b/drivers/usb/cdns3/ep0.c
>> new file mode 100644
>> index 000000000000..89cf1cde1555
>> --- /dev/null
>> +++ b/drivers/usb/cdns3/ep0.c
>> @@ -0,0 +1,896 @@
>> +// SPDX-License-Identifier: GPL-2.0
>> +/*
>> + * Cadence USBSS DRD Driver - gadget side.
>> + *
>> + * Copyright (C) 2018 Cadence Design Systems.
>> + * Copyright (C) 2017-2018 NXP
>> + *
>> + * Authors: Pawel Jez <pjez@cadence.com>,
>> + *          Pawel Laszczak <pawell@cadence.com>
>> + *	    Peter Chen <peter.chen@nxp.com>
>> + */
>> +
>> +#include <linux/usb/composite.h>
>> +
>> +#include "gadget.h"
>> +#include "trace.h"
>> +
>> +static struct usb_endpoint_descriptor cdns3_gadget_ep0_desc = {
>> +	.bLength = USB_DT_ENDPOINT_SIZE,
>> +	.bDescriptorType = USB_DT_ENDPOINT,
>> +	.bmAttributes =	USB_ENDPOINT_XFER_CONTROL,
>> +};
>> +
><...>
>> +
>> +
>> +#endif /* __LINUX_CDNS3_GADGET_EXPORT */
>> diff --git a/drivers/usb/cdns3/gadget.c b/drivers/usb/cdns3/gadget.c
>> new file mode 100644
>> index 000000000000..0d95eb00be37
>> --- /dev/null
>> +++ b/drivers/usb/cdns3/gadget.c
>> @@ -0,0 +1,2102 @@
>> +// SPDX-License-Identifier: GPL-2.0
>> +/*
>> + * Cadence USBSS DRD Driver - gadget side.
>> + *
>> + * Copyright (C) 2018 Cadence Design Systems.
>> + * Copyright (C) 2017-2018 NXP
>> + *
>> + * Authors: Pawel Jez <pjez@cadence.com>,
>> + *          Pawel Laszczak <pawell@cadence.com>
>> + *	    Peter Chen <peter.chen@nxp.com>
>> + */
>> +
>> +#include <linux/dma-mapping.h>
>> +#include <linux/usb/gadget.h>
>> +
><...>
>
>> +/**
>> + * cdns3_next_request - returns next request from list
>> + * @list: list containing requests
>> + *
>> + * Returns request or NULL if no requests in list
>> + */
>> +struct usb_request *cdns3_next_request(struct list_head *list)
>> +{
>> +	if (list_empty(list))
>> +		return NULL;
>No need check it, if list is empty, list_first_entry_or_null() will
>return NULL
Ok, 

>> +	return list_first_entry_or_null(list, struct usb_request, list);
>> +}
>> +
>> +/**
>> + * cdns3_next_priv_request - returns next request from list
>> + * @list: list containing requests
>> + *
>> + * Returns request or NULL if no requests in list
>> + */
>> +struct cdns3_request *cdns3_next_priv_request(struct list_head *list)
>> +{
>> +	if (list_empty(list))
>> +		return NULL;
>ditto
>> +	return list_first_entry_or_null(list, struct cdns3_request, list);
>> +}
>> +
>
><...>
>
Thanks for comments.
Cheers,
Pawel


^ permalink raw reply	[flat|nested] 49+ messages in thread

* RE: [PATCH v2 5/5] usb:cdns3 Add Cadence USB3 DRD Driver
@ 2018-12-31 12:31       ` Pawel Laszczak
  0 siblings, 0 replies; 49+ messages in thread
From: Pawel Laszczak @ 2018-12-31 12:31 UTC (permalink / raw)
  To: Chunfeng Yun
  Cc: devicetree, gregkh, mark.rutland, linux-usb, hdegoede,
	heikki.krogerus, andy.shevchenko, robh+dt, rogerq, linux-kernel,
	Alan Douglas, jbergsagel, nsekhar, nm, Suresh Punnoose,
	peter.chen, Pawel Jez

Hi

>On Sun, 2018-12-23 at 15:13 +0000, Pawel Laszczak wrote:
>> This patch introduce new Cadence USBSS DRD driver
>> to linux kernel.
>
><...>
>
>> diff --git a/drivers/usb/cdns3/cdns3-pci-wrap.c b/drivers/usb/cdns3/cdns3-pci-wrap.c
>> new file mode 100644
>> index 000000000000..e93179c45ece
>> --- /dev/null
>> +++ b/drivers/usb/cdns3/cdns3-pci-wrap.c
>> @@ -0,0 +1,157 @@
>> +// SPDX-License-Identifier: GPL-2.0
>> +/*
>> + * Cadence USBSS PCI Glue driver
>> + *
>> + * Copyright (C) 2018 Cadence.
>> + *
>> + * Author: Pawel Laszczak <pawell@cadence.com>
>> + */
>> +
>> +#include <linux/kernel.h>
>> +#include <linux/module.h>
>> +#include <linux/pci.h>
>> +#include <linux/platform_device.h>
>> +#include <linux/dma-mapping.h>
>> +#include <linux/slab.h>
>> +
>> +struct cdns3_wrap {
>> +	struct platform_device *plat_dev;
>> +	struct pci_dev *hg_dev;
>> +	struct resource dev_res[4];
>> +};
>> +
>> +struct cdns3_wrap wrap;
>> +
>> +#define RES_IRQ_ID		0
>> +#define RES_HOST_ID		1
>> +#define RES_DEV_ID		2
>> +#define RES_DRD_ID		3
>> +
>> +#define PCI_BAR_HOST		0
>> +#define PCI_BAR_DEV		2
>> +#define PCI_BAR_OTG		4
>> +
>> +#define PCI_DEV_FN_HOST_DEVICE	0
>> +#define PCI_DEV_FN_OTG		1
>> +
>> +#define PCI_DRIVER_NAME		"cdns3-pci-usbss"
>> +#define PLAT_DRIVER_NAME	"cdns-usb3"
>> +
>> +#define CDNS_VENDOR_ID 0x17cd
>> +#define CDNS_DEVICE_ID 0x0100
>> +
>> +/**
>> + * cdns3_pci_probe - Probe function for Cadence USB wrapper driver
>> + * @pdev: platform device object
>> + * @id: pci device id
>> + *
>> + * Returns 0 on success otherwise negative errno
>> + */
>> +static int cdns3_pci_probe(struct pci_dev *pdev,
>> +			   const struct pci_device_id *id)
>> +{
>> +	struct platform_device_info plat_info;
>> +	struct cdns3_wrap *wrap;
>> +	struct resource *res;
>> +	int err;
>> +
>> +	/*
>> +	 * for GADGET/HOST PCI (devfn) function number is 0,
>> +	 * for OTG PCI (devfn) function number is 1
>> +	 */
>> +	if (!id || pdev->devfn != PCI_DEV_FN_HOST_DEVICE)
>> +		return -EINVAL;
>> +
>> +	err = pcim_enable_device(pdev);
>> +	if (err) {
>> +		dev_err(&pdev->dev, "Enabling PCI device has failed %d\n", err);
>> +		return err;
>> +	}
>> +
>> +	pci_set_master(pdev);
>> +	wrap = devm_kzalloc(&pdev->dev, sizeof(*wrap), GFP_KERNEL);
>> +	if (!wrap) {
>> +		dev_err(&pdev->dev, "Failed to allocate memory\n");
>> +		return -ENOMEM;
>> +	}
>> +
>> +	/* function 0: host(BAR_0) + device(BAR_1) + otg(BAR_2)). */
>> +	memset(wrap->dev_res, 0x00,
>> +	       sizeof(struct resource) * ARRAY_SIZE(wrap->dev_res));
>> +	dev_dbg(&pdev->dev, "Initialize Device resources\n");
>> +	res = wrap->dev_res;
>> +
>> +	res[RES_DEV_ID].start = pci_resource_start(pdev, PCI_BAR_DEV);
>> +	res[RES_DEV_ID].end =   pci_resource_end(pdev, PCI_BAR_DEV);
>> +	res[RES_DEV_ID].name = "cdns3-dev-regs";
>> +	res[RES_DEV_ID].flags = IORESOURCE_MEM;
>> +	dev_dbg(&pdev->dev, "USBSS-DEV physical base addr: %pa\n",
>> +		&res[RES_DEV_ID].start);
>> +
>> +	res[RES_HOST_ID].start = pci_resource_start(pdev, PCI_BAR_HOST);
>> +	res[RES_HOST_ID].end = pci_resource_end(pdev, PCI_BAR_HOST);
>> +	res[RES_HOST_ID].name = "cdns3-xhci-regs";
>> +	res[RES_HOST_ID].flags = IORESOURCE_MEM;
>> +	dev_dbg(&pdev->dev, "USBSS-XHCI physical base addr: %pa\n",
>> +		&res[RES_HOST_ID].start);
>> +
>> +	res[RES_DRD_ID].start = pci_resource_start(pdev, PCI_BAR_OTG);
>> +	res[RES_DRD_ID].end =   pci_resource_end(pdev, PCI_BAR_OTG);
>> +	res[RES_DRD_ID].name = "cdns3-otg";
>> +	res[RES_DRD_ID].flags = IORESOURCE_MEM;
>> +	dev_dbg(&pdev->dev, "USBSS-DRD physical base addr: %pa\n",
>> +		&res[RES_DRD_ID].start);
>> +
>> +	/* Interrupt common for both device and XHCI */
>> +	wrap->dev_res[RES_IRQ_ID].start = pdev->irq;
>> +	wrap->dev_res[RES_IRQ_ID].name = "cdns3-irq";
>> +	wrap->dev_res[RES_IRQ_ID].flags = IORESOURCE_IRQ;
>> +
>> +	/* set up platform device info */
>> +	memset(&plat_info, 0, sizeof(plat_info));
>> +	plat_info.parent = &pdev->dev;
>> +	plat_info.fwnode = pdev->dev.fwnode;
>> +	plat_info.name = PLAT_DRIVER_NAME;
>> +	plat_info.id = pdev->devfn;
>> +	plat_info.res = wrap->dev_res;
>> +	plat_info.num_res = ARRAY_SIZE(wrap->dev_res);
>> +	plat_info.dma_mask = pdev->dma_mask;
>> +
>> +	/* register platform device */
>> +	wrap->plat_dev = platform_device_register_full(&plat_info);
>> +	if (IS_ERR(wrap->plat_dev)) {
>> +		err = PTR_ERR(wrap->plat_dev);
>> +		return err;
>> +	}
>return PTR_ERR(wrap->plat_dev);
Ok. 
>
>> +
>> +	pci_set_drvdata(pdev, wrap);
>> +
>> +	return err;
>> +}
>> +
>> +void cdns3_pci_remove(struct pci_dev *pdev)
>> +{
>> +	struct cdns3_wrap *wrap = (struct cdns3_wrap *)pci_get_drvdata(pdev);
>> +
>> +	platform_device_unregister(wrap->plat_dev);
>> +}
>> +
>> +static const struct pci_device_id cdns3_pci_ids[] = {
>> +	{ PCI_DEVICE(CDNS_VENDOR_ID, CDNS_DEVICE_ID), },
>> +	{ 0, }
>> +};
>> +
>> +static struct pci_driver cdns3_pci_driver = {
>> +	.name = PCI_DRIVER_NAME,
>> +	.id_table = cdns3_pci_ids,
>> +	.probe = cdns3_pci_probe,
>> +	.remove = cdns3_pci_remove,
>> +};
>> +
>> +module_pci_driver(cdns3_pci_driver);
>> +MODULE_DEVICE_TABLE(pci, cdns3_pci_ids);
>> +
>> +MODULE_AUTHOR("Pawel Laszczak <pawell@cadence.com>");
>> +MODULE_LICENSE("GPL v2");
>> +MODULE_DESCRIPTION("Cadence USBSS PCI wrapperr");
>> +
>> diff --git a/drivers/usb/cdns3/core.c b/drivers/usb/cdns3/core.c
>> new file mode 100644
>> index 000000000000..d274586aca36
>> --- /dev/null
>> +++ b/drivers/usb/cdns3/core.c
>> @@ -0,0 +1,406 @@
>> +// SPDX-License-Identifier: GPL-2.0
>> +/*
>> + * Cadence USBSS DRD Driver.
>> + *
>> + * Copyright (C) 2018 Cadence.
>> + * Copyright (C) 2017-2018 NXP
>> + *
>> + * Author: Peter Chen <peter.chen@nxp.com>
>> + *         Pawel Laszczak <pawell@cadence.com>
>> + */
>> +
>> +#include <linux/module.h>
>> +#include <linux/kernel.h>
>> +#include <linux/platform_device.h>
>> +#include <linux/interrupt.h>
>> +#include <linux/io.h>
>> +#include <linux/pm_runtime.h>
>> +
>> +#include "gadget.h"
>> +#include "core.h"
>> +#include "host-export.h"
>> +#include "gadget-export.h"
>> +#include "drd.h"
>> +#include "debug.h"
>> +
>> +static inline
>> +struct cdns3_role_driver *cdns3_get_current_role_driver(struct cdns3 *cdns)
>> +{
>> +	WARN_ON(cdns->role >= CDNS3_ROLE_END || !cdns->roles[cdns->role]);
>> +	return cdns->roles[cdns->role];
>> +}
>> +
>> +static inline int cdns3_role_start(struct cdns3 *cdns, enum cdns3_roles role)
>remove inline
Ok.

>> +{
>> +	int ret;
>> +
>> +	if (WARN_ON(role >= CDNS3_ROLE_END))
>> +		return 0;
>> +
>> +	if (!cdns->roles[role])
>> +		return -ENXIO;
>> +
>> +	if (cdns->roles[role]->state == CDNS3_ROLE_STATE_ACTIVE)
>> +		return 0;
>> +
>> +	mutex_lock(&cdns->mutex);
>> +	cdns->role = role;
>> +	ret = cdns->roles[role]->start(cdns);
>> +	if (!ret)
>> +		cdns->roles[role]->state = CDNS3_ROLE_STATE_ACTIVE;
>> +	mutex_unlock(&cdns->mutex);
>> +	return ret;
>> +}
>> +
>> +void cdns3_role_stop(struct cdns3 *cdns)
>> +{
>> +	enum cdns3_roles role = cdns->role;
>> +
>> +	if (role >= CDNS3_ROLE_END) {
>> +		WARN_ON(role > CDNS3_ROLE_END);
>> +		return;
>> +	}
>> +
>> +	if (cdns->roles[role]->state == CDNS3_ROLE_STATE_INACTIVE)
>> +		return;
>> +
>> +	mutex_lock(&cdns->mutex);
>> +	cdns->roles[role]->stop(cdns);
>> +	cdns->roles[role]->state = CDNS3_ROLE_STATE_INACTIVE;
>> +	mutex_unlock(&cdns->mutex);
>> +}
>> +
>> +/*
>> + * cdns->role gets from cdns3_get_initial_role, and this API tells role at the
>> + * runtime.
>> + * If both roles are supported, the role is selected based on vbus/id.
>> + * It could be read from OTG register or external connector.
>> + * If only single role is supported, only one role structure
>> + * is allocated, cdns->roles[CDNS3_ROLE_HOST] or cdns->roles[CDNS3_ROLE_GADGET].
>> + */
>> +static enum cdns3_roles cdns3_get_initial_role(struct cdns3 *cdns)
>> +{
>> +	if (cdns->roles[CDNS3_ROLE_HOST] && cdns->roles[CDNS3_ROLE_GADGET]) {
>> +		if (cdns3_is_host(cdns))
>> +			return CDNS3_ROLE_HOST;
>> +		if (cdns3_is_device(cdns))
>> +			return CDNS3_ROLE_GADGET;
>> +	}
>> +	return cdns->roles[CDNS3_ROLE_HOST]
>> +		? CDNS3_ROLE_HOST
>> +		: CDNS3_ROLE_GADGET;
>> +}
>> +
>> +static void cdns3_exit_roles(struct cdns3 *cdns)
>> +{
>> +	cdns3_role_stop(cdns);
>> +	cdns3_drd_exit(cdns);
>> +}
>> +
>> +/**
>> + * cdns3_core_init_role - initialize role of operation
>> + * @cdns: Pointer to cdns3 structure
>> + *
>> + * Returns 0 on success otherwise negative errno
>> + */
>> +static int cdns3_core_init_role(struct cdns3 *cdns)
>> +{
>> +	struct device *dev = cdns->dev;
>> +	enum usb_dr_mode best_dr_mode;
>> +	enum usb_dr_mode dr_mode;
>> +	int ret = 0;
>> +
>> +	dr_mode = usb_get_dr_mode(dev);
>> +	cdns->role = CDNS3_ROLE_END;
>> +
>> +	/*
>> +	 * If driver can't read mode by means of usb_get_dr_mdoe function then
>> +	 * chooses mode according with Kernel configuration. This setting
>> +	 * can be restricted later depending on strap pin configuration.
>> +	 */
>> +	if (dr_mode == USB_DR_MODE_UNKNOWN) {
>> +		if (IS_ENABLED(CONFIG_USB_CDNS3_HOST) &&
>> +		    IS_ENABLED(CONFIG_USB_CDNS3_GADGET))
>> +			dr_mode = USB_DR_MODE_OTG;
>> +		else if (IS_ENABLED(CONFIG_USB_CDNS3_HOST))
>> +			dr_mode = USB_DR_MODE_HOST;
>> +		else if (IS_ENABLED(CONFIG_USB_CDNS3_GADGET))
>> +			dr_mode = USB_DR_MODE_PERIPHERAL;
>> +	}
>> +
>> +	best_dr_mode = USB_DR_MODE_OTG;
>> +
>> +	if (dr_mode == USB_DR_MODE_OTG) {
>> +		best_dr_mode = cdns->dr_mode;
>> +	} else if (cdns->dr_mode == USB_DR_MODE_OTG) {
>> +		best_dr_mode = dr_mode;
>> +	} else if (cdns->dr_mode != dr_mode) {
>> +		dev_err(dev, "Incorrect DRD configuration\n");
>> +		return -EINVAL;
>> +	}
>> +
>> +	dr_mode = best_dr_mode;
>> +
>> +	if (dr_mode == USB_DR_MODE_OTG || dr_mode == USB_DR_MODE_HOST) {
>> +		ret = cdns3_host_init(cdns);
>> +		if (ret) {
>> +			dev_err(dev, "Host initialization failed with %d\n",
>> +				ret);
>> +			goto err;
>> +		}
>> +	}
>> +
>> +	if (dr_mode == USB_DR_MODE_OTG || dr_mode == USB_DR_MODE_PERIPHERAL) {
>> +		ret = cdns3_gadget_init(cdns);
>> +		if (ret) {
>> +			dev_err(dev, "Device initialization failed with %d\n",
>> +				ret);
>> +			goto err;
>> +		}
>> +	}
>> +
>> +	cdns->desired_dr_mode = dr_mode;
>> +	cdns->dr_mode = dr_mode;
>> +	/*
>> +	 * dr_mode could be change so DRD must update controller
>> +	 * configuration
>> +	 */
>> +	ret = cdns3_drd_update_mode(cdns);
>Do you need check ret value? if not, no need assign it

I will add checking. 
>> +
>> +	cdns->role = cdns3_get_initial_role(cdns);
>> +
>> +	ret = cdns3_role_start(cdns, cdns->role);
>> +	if (ret) {
>> +		dev_err(dev, "can't start %s role\n",
>> +			cdns3_get_current_role_driver(cdns)->name);
>> +		goto err;
>> +	}
>> +
>> +	return ret;
>> +err:
>> +	cdns3_exit_roles(cdns);
>> +	return ret;
>> +}
>> +
>> +/**
>> + * cdsn3_get_real_role - get real role of controller based on hardware settings.
>> + * @cdns: Pointer to cdns3 structure
>> + *
>> + * Returns role
>> + */
>> +enum cdns3_roles cdsn3_get_real_role(struct cdns3 *cdns)
>> +{
>> +	enum cdns3_roles role = CDNS3_ROLE_END;
>> +
>> +	if (cdns->current_dr_mode == USB_DR_MODE_OTG) {
>> +		if (cdns3_get_id(cdns))
>> +			role = CDNS3_ROLE_GADGET;
>> +		else
>> +			role = CDNS3_ROLE_HOST;
>> +	} else {
>> +		if (cdns3_is_host(cdns))
>> +			role = CDNS3_ROLE_HOST;
>> +		if (cdns3_is_device(cdns))
>> +			role = CDNS3_ROLE_GADGET;
>> +	}
>> +
>> +	return role;
>> +}
>> +
>> +/**
>> + * cdns3_role_switch - work queue handler for role switch
>> + *
>> + * @work: work queue item structure
>> + *
>> + * Handles below events:
>> + * - Role switch for dual-role devices
>> + * - CDNS3_ROLE_GADGET <--> CDNS3_ROLE_END for peripheral-only devices
>> + */
>> +static void cdns3_role_switch(struct work_struct *work)
>> +{
>> +	enum cdns3_roles role = CDNS3_ROLE_END;
>> +	struct cdns3_role_driver *role_drv;
>> +	enum cdns3_roles current_role;
>> +	struct cdns3 *cdns;
>> +	int ret = 0;
>> +
>> +	cdns = container_of(work, struct cdns3, role_switch_wq);
>> +
>> +	/* During switching cdns->role can be different then role */
>> +	role = cdsn3_get_real_role(cdns);
>> +
>> +	role_drv = cdns3_get_current_role_driver(cdns);
>> +
>> +	pm_runtime_get_sync(cdns->dev);
>> +
>> +	/* Disable current role. This state can be forced from user space. */
>> +	if (cdns->debug_disable && role_drv->state == CDNS3_ROLE_STATE_ACTIVE) {
>> +		cdns3_role_stop(cdns);
>> +		goto exit;
>> +	}
>> +
>> +	/* Do nothing if nothing changed */
>> +	if (cdns->role == role && role_drv->state == CDNS3_ROLE_STATE_ACTIVE)
>> +		goto exit;
>> +
>> +	cdns3_role_stop(cdns);
>> +
>> +	role = cdsn3_get_real_role(cdns);
>> +
>> +	current_role = cdns->role;
>> +	dev_dbg(cdns->dev, "Switching role");
>> +
>> +	ret = cdns3_role_start(cdns, role);
>> +
>remove blank line
>> +	if (ret) {
>> +		/* Back to current role */
>> +		dev_err(cdns->dev, "set %d has failed, back to %d\n",
>> +			role, current_role);
>> +		cdns3_role_start(cdns, current_role);
>> +	}
>> +exit:
>> +	pm_runtime_put_sync(cdns->dev);
>> +}
>> +
>> +/**
>> + * cdns3_probe - probe for cdns3 core device
>> + * @pdev: Pointer to cdns3 core platform device
>> + *
>> + * Returns 0 on success otherwise negative errno
>> + */
>> +static int cdns3_probe(struct platform_device *pdev)
>> +{
>> +	struct device *dev = &pdev->dev;
>> +	struct resource	*res;
>> +	struct cdns3 *cdns;
>> +	void __iomem *regs;
>> +	int ret;
>> +
>> +	cdns = devm_kzalloc(dev, sizeof(*cdns), GFP_KERNEL);
>> +	if (!cdns)
>> +		return -ENOMEM;
>> +
>> +	cdns->dev = dev;
>> +
>> +	platform_set_drvdata(pdev, cdns);
>> +
>> +	res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
>> +	if (!res) {
>> +		dev_err(dev, "missing IRQ\n");
>> +		return -ENODEV;
>> +	}
>> +	cdns->irq = res->start;
>> +
>> +	cdns->xhci_res[0] = *res;
>> +
>> +	/*
>> +	 * Request memory region
>> +	 * region-0: xHCI
>> +	 * region-1: Peripheral
>> +	 * region-2: OTG registers
>> +	 */
>> +	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
>> +	cdns->xhci_res[1] = *res;
>> +
>> +	res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
>> +	regs = devm_ioremap_resource(dev, res);
>> +	if (IS_ERR(regs))
>> +		return PTR_ERR(regs);
>> +	cdns->dev_regs	= regs;
>> +
>> +	res = platform_get_resource(pdev, IORESOURCE_MEM, 2);
>> +	cdns->otg_res = *res;
>It'll make the DTS clearer if add reg-names

Good Idea. I assume that in this case regs-names will be mandatory property  and
I have to replace platform_get_resource with platform_get_resource_byname. 
Also I have to made some changes in cdns3-pci-wrap.c file.

>> +
>> +	mutex_init(&cdns->mutex);
>> +
>> +	cdns->phy = devm_phy_get(dev, "cdns3,usbphy");
>Try to use devm_phy_optional_get()?

Ok, so the code will look like:
	cdns->phy = devm_phy_optional_get(dev, "cdns3,usbphy");
	if (IS_ERR(cdns->phy) )
		return PTR_ERR(cdns->phy);

>> +	if (IS_ERR(cdns->phy)) {
>> +		ret = PTR_ERR(cdns->phy);
>> +		if (ret == -ENODEV) {
>> +			cdns->phy = NULL;
>> +		} else if (ret == -EPROBE_DEFER) {
>> +			return ret;
>> +		} else {
>> +			dev_err(dev, "no phy found\n");
>> +			goto err0;
>> +		}
>> +	}
>> +
>> +	phy_init(cdns->phy);
>> +
>> +	INIT_WORK(&cdns->role_switch_wq, cdns3_role_switch);
>> +
>> +	ret = cdns3_drd_init(cdns);
>> +	if (ret)
>> +		goto err1;
>> +
>> +	ret = cdns3_core_init_role(cdns);
>> +	if (ret)
>> +		goto err1;
>> +
>> +	cdns3_debugfs_init(cdns);
>> +	device_set_wakeup_capable(dev, true);
>> +	pm_runtime_set_active(dev);
>> +	pm_runtime_enable(dev);
>> +
>> +	/*
>> +	 * The controller needs less time between bus and controller suspend,
>> +	 * and we also needs a small delay to avoid frequently entering low
>> +	 * power mode.
>> +	 */
>> +	pm_runtime_set_autosuspend_delay(dev, 20);
>> +	pm_runtime_mark_last_busy(dev);
>> +	pm_runtime_use_autosuspend(dev);
>> +	dev_dbg(dev, "Cadence USB3 core: probe succeed\n");
>> +
>> +	return 0;
>> +
>> +err1:
>> +	phy_exit(cdns->phy);
>> +err0:
>> +	return ret;
>> +}
>> +
>> +/**
>> + * cdns3_remove - unbind drd driver and clean up
>> + * @pdev: Pointer to Linux platform device
>> + *
>> + * Returns 0 on success otherwise negative errno
>> + */
>> +static int cdns3_remove(struct platform_device *pdev)
>> +{
>> +	struct cdns3 *cdns = platform_get_drvdata(pdev);
>> +
>> +	pm_runtime_get_sync(&pdev->dev);
>> +	pm_runtime_disable(&pdev->dev);
>> +	pm_runtime_put_noidle(&pdev->dev);
>> +	cdns3_debugfs_exit(cdns);
>> +	cdns3_exit_roles(cdns);
>> +	phy_exit(cdns->phy);
>> +	return 0;
>> +}
>> +
>> +#ifdef CONFIG_OF
>> +static const struct of_device_id of_cdns3_match[] = {
>> +	{ .compatible = "cdns,usb3-1.0.0" },
>> +	{ .compatible = "cdns,usb3-1.0.1" },
>> +	{ },
>> +};
>> +MODULE_DEVICE_TABLE(of, of_cdns3_match);
>> +#endif
>> +
>> +static struct platform_driver cdns3_driver = {
>> +	.probe		= cdns3_probe,
>> +	.remove		= cdns3_remove,
>> +	.driver		= {
>> +		.name	= "cdns-usb3",
>> +		.of_match_table	= of_match_ptr(of_cdns3_match),
>> +	},
>> +};
>> +
>> +module_platform_driver(cdns3_driver);
>> +
>> +MODULE_ALIAS("platform:cdns3");
>> +MODULE_AUTHOR("Pawel Laszczak <pawell@cadence.com>");
>> +MODULE_LICENSE("GPL v2");
>> +MODULE_DESCRIPTION("Cadence USB3 DRD Controller Driver");
>> diff --git a/drivers/usb/cdns3/core.h b/drivers/usb/cdns3/core.h
>> new file mode 100644
>> index 000000000000..fb4b39206158
>> --- /dev/null
>> +++ b/drivers/usb/cdns3/core.h
>> @@ -0,0 +1,116 @@
>> +/* SPDX-License-Identifier: GPL-2.0 */
>> +/*
>> + * Cadence USBSS DRD Header File.
>> + *
>> + * Copyright (C) 2017-2018 NXP
>> + * Copyright (C) 2018 Cadence.
>> + *
>> + * Authors: Peter Chen <peter.chen@nxp.com>
>> + *          Pawel Laszczak <pawell@cadence.com>
>> + */
>> +#include <linux/usb/otg.h>
>> +
>> +#ifndef __LINUX_CDNS3_CORE_H
>> +#define __LINUX_CDNS3_CORE_H
>> +
>> +struct cdns3;
>> +enum cdns3_roles {
>> +	CDNS3_ROLE_HOST = 0,
>> +	CDNS3_ROLE_GADGET,
>> +	CDNS3_ROLE_END,
>> +};
>> +
>> +/**
>> + * struct cdns3_role_driver - host/gadget role driver
>> + * @start: start this role
>> + * @stop: stop this role
>> + * @suspend: suspend callback for this role
>> + * @resume: resume callback for this role
>> + * @irq: irq handler for this role
>> + * @name: role name string (host/gadget)
>> + * @state: current state
>> + */
>> +struct cdns3_role_driver {
>> +	int (*start)(struct cdns3 *cdns);
>> +	void (*stop)(struct cdns3 *cdns);
>> +	int (*suspend)(struct cdns3 *cdns, bool do_wakeup);
>> +	int (*resume)(struct cdns3 *cdns, bool hibernated);
>> +	const char *name;
>> +#define CDNS3_ROLE_STATE_INACTIVE	0
>> +#define CDNS3_ROLE_STATE_ACTIVE		1
>> +	int state;
>> +};
>> +
>> +#define CDNS3_XHCI_RESOURCES_NUM	2
>> +/**
>> + * struct cdns3 - Representation of Cadence USB3 DRD controller.
>> + * @dev: pointer to Cadence device struct
>> + * @xhci_regs: pointer to base of xhci registers
>> + * @xhci_res: the resource for xhci
>> + * @dev_regs: pointer to base of dev registers
>> + * @otg_regs: pointer to base of otg registers
>> + * @irq: irq number for controller
>> + * @roles: array of supported roles for this controller
>> + * @role: current role
>> + * @host_dev: the child host device pointer for cdns3 core
>> + * @gadget_dev: the child gadget device pointer for cdns3 core
>> + * @usb: phy for this controller
>> + * @role_switch_wq: work queue item for role switch
>> + * @in_lpm: the controller in low power mode
>> + * @wakeup_int: the wakeup interrupt
>> + * @mutex: the mutex for concurrent code at driver
>> + * @dr_mode: supported mode of operation it can be only Host, only Device
>> + *           or OTG mode that allow to switch between Device and Host mode.
>> + *           This field based on firmware setting, kernel configuration
>> + *           and hardware configuration.
>> + * @current_dr_mode: current mode of operation when in dual-role mode
>> + * @desired_dr_mode: desired mode of operation when in dual-role mode.
>> + *           This value can be changed during runtime.
>> + *           Available options depends on  dr_mode:
>> + *           dr_mode                 |  desired_dr_mode and current_dr_mode
>> + *           ----------------------------------------------------------------
>> + *           USB_DR_MODE_HOST        | only USB_DR_MODE_HOST
>> + *           USB_DR_MODE_PERIPHERAL  | only USB_DR_MODE_PERIPHERAL
>> + *           USB_DR_MODE_OTG         | only USB_DR_MODE_HOST
>> + *           USB_DR_MODE_OTG         | only USB_DR_MODE_PERIPHERAL
>> + *           USB_DR_MODE_OTG         | USB_DR_MODE_OTG
>> + *
>> + *           Desired_dr_role can be changed by means of debugfs.
>> + * @root: debugfs root folder pointer
>> + * @debug_disable:
>> + */
>> +struct cdns3 {
>> +	struct device			*dev;
>> +	void __iomem			*xhci_regs;
>> +	struct resource			xhci_res[CDNS3_XHCI_RESOURCES_NUM];
>> +	struct cdns3_usb_regs __iomem	*dev_regs;
>> +
>> +	struct resource			otg_res;
>> +	struct cdns3_otg_legacy_regs	*otg_v0_regs;
>> +	struct cdns3_otg_regs		*otg_v1_regs;
>> +	struct cdns3_otg_common_regs	*otg_regs;
>> +#define CDNS3_CONTROLLER_V0	0
>> +#define CDNS3_CONTROLLER_V1	1
>> +	u32				version;
>> +
>> +	int				irq;
>> +	struct cdns3_role_driver	*roles[CDNS3_ROLE_END];
>> +	enum cdns3_roles		role;
>> +	struct platform_device		*host_dev;
>> +	struct cdns3_device		*gadget_dev;
>> +	struct phy			*phy;
>> +	struct work_struct		role_switch_wq;
>> +	int				in_lpm:1;
>> +	int				wakeup_int:1;
>> +	/* mutext used in workqueue*/
>> +	struct mutex			mutex;
>> +	enum usb_dr_mode		dr_mode;
>> +	enum usb_dr_mode		current_dr_mode;
>> +	enum usb_dr_mode		desired_dr_mode;
>> +	struct dentry			*root;
>> +	int				debug_disable:1;
>> +};
>> +
>> +void cdns3_role_stop(struct cdns3 *cdns);
>> +
>> +#endif /* __LINUX_CDNS3_CORE_H */
>> diff --git a/drivers/usb/cdns3/debug.h b/drivers/usb/cdns3/debug.h
>> new file mode 100644
>> index 000000000000..94f9ef15f899
>> --- /dev/null
>> +++ b/drivers/usb/cdns3/debug.h
>> @@ -0,0 +1,166 @@
>> +/* SPDX-License-Identifier: GPL-2.0 */
>> +/*
>> + * Cadence USBSS DRD Driver.
>> + * Debug header file.
>> + *
>> + * Copyright (C) 2018 Cadence.
>> + *
>> + * Author: Pawel Laszczak <pawell@cadence.com>
>> + */
>> +#ifndef __LINUX_CDNS3_DEBUG
>> +#define __LINUX_CDNS3_DEBUG
>> +
>> +#include "core.h"
>> +
>> +static inline char *cdns3_decode_usb_irq(char *str,
>> +					 enum usb_device_speed speed,
>> +
>too many sentences as inline function
>> 					 u32 usb_ists)
>> +{
>> +	int ret;
>> +
>> +	ret = sprintf(str, "IRQ %08x = ", usb_ists);
>> +
>> +	if (usb_ists & (USB_ISTS_CON2I | USB_ISTS_CONI)) {
>> +		ret += sprintf(str + ret, "Connection %s\n",
>> +			       usb_speed_string(speed));
>> +	}
>> +	if (usb_ists & USB_ISTS_CON2I || usb_ists & USB_ISTS_CONI)
>> +		ret += sprintf(str + ret, "Disconnection ");
>> +	if (usb_ists & USB_ISTS_L2ENTI)
>> +		ret += sprintf(str + ret, "suspended ");
>> +
>> +	if (usb_ists & USB_ISTS_L2EXTI)
>> +		ret += sprintf(str + ret, "L2 exit ");
>> +	if (usb_ists & USB_ISTS_U3EXTI)
>> +		ret += sprintf(str + ret, "U3 exit ");
>> +	if (usb_ists & USB_ISTS_UWRESI)
>> +		ret += sprintf(str + ret, "Warm Reset ");
>> +	if (usb_ists & USB_ISTS_UHRESI)
>> +		ret += sprintf(str + ret, "Hot Reset ");
>> +	if (usb_ists & USB_ISTS_U2RESI)
>> +		ret += sprintf(str + ret, "Reset");
>> +
>> +	return str;
>> +}
>> +
>> +static inline  char *cdns3_decode_ep_irq(char *str,
>> +					 u32 ep_sts,
>> +					 const char *ep_name)
>ditto
>> +{
>> +	int ret;
>> +
>> +	ret = sprintf(str, "IRQ for %s: %08x ", ep_name, ep_sts);
>> +
>> +	if (ep_sts & EP_STS_SETUP)
>> +		ret += sprintf(str + ret, "SETUP ");
>> +	if (ep_sts & EP_STS_IOC)
>> +		ret += sprintf(str + ret, "IOC ");
>> +	if (ep_sts & EP_STS_ISP)
>> +		ret += sprintf(str + ret, "ISP ");
>> +	if (ep_sts & EP_STS_DESCMIS)
>> +		ret += sprintf(str + ret, "DESCMIS ");
>> +	if (ep_sts & EP_STS_STREAMR)
>> +		ret += sprintf(str + ret, "STREAMR ");
>> +	if (ep_sts & EP_STS_MD_EXIT)
>> +		ret += sprintf(str + ret, "MD_EXIT ");
>> +	if (ep_sts & EP_STS_TRBERR)
>> +		ret += sprintf(str + ret, "TRBERR ");
>> +	if (ep_sts & EP_STS_NRDY)
>> +		ret += sprintf(str + ret, "NRDY ");
>> +	if (ep_sts & EP_STS_PRIME)
>> +		ret += sprintf(str + ret, "PRIME ");
>> +	if (ep_sts & EP_STS_SIDERR)
>> +		ret += sprintf(str + ret, "SIDERRT ");
>> +	if (ep_sts & EP_STS_OUTSMM)
>> +		ret += sprintf(str + ret, "OUTSMM ");
>> +	if (ep_sts & EP_STS_ISOERR)
>> +		ret += sprintf(str + ret, "ISOERR ");
>> +	if (ep_sts & EP_STS_IOT)
>> +		ret += sprintf(str + ret, "IOT ");
>> +
>> +	return str;
>> +}
>> +
>> +static inline char *cdns3_decode_epx_irq(char *str,
>> +					 char *ep_name,
>> +					 u32 ep_sts)
>> +{
>> +	return cdns3_decode_ep_irq(str, ep_sts, ep_name);
>> +}
>> +
>> +static inline char *cdns3_decode_ep0_irq(char *str,
>> +					 int dir,
>> +					 u32 ep_sts)
>> +{
>> +	return cdns3_decode_ep_irq(str, ep_sts,
>> +				   dir ? "ep0IN" : "ep0OUT");
>> +}
>> +
>> +/**
>> + * Debug a transfer ring.
>> + *
>> + * Prints out all TRBs in the endpoint ring, even those after the Link TRB.
>> + *.
>> + */
>> +static inline char *cdns3_dbg_ring(struct cdns3_endpoint *priv_ep,
>> +				   struct cdns3_trb *ring, char *str)
>ditto
>> +{
>> +	dma_addr_t addr = priv_ep->trb_pool_dma;
>> +	struct cdns3_trb *trb;
>> +	int trb_per_sector;
>> +	int ret = 0;
>> +	int i;
>> +
>> +	trb_per_sector = GET_TRBS_PER_SEGMENT(priv_ep->type);
>> +
>> +	trb = &priv_ep->trb_pool[priv_ep->dequeue];
>> +	ret += sprintf(str + ret, "\n\t\tRing contents for %s:", priv_ep->name);
>> +
>> +	ret += sprintf(str + ret,
>> +		       "\n\t\tRing deq index: %d, trb: %p (virt), 0x%llx (dma)\n",
>> +		       priv_ep->dequeue, trb,
>> +		       (unsigned long long)cdns3_trb_virt_to_dma(priv_ep, trb));
>> +
>> +	trb = &priv_ep->trb_pool[priv_ep->enqueue];
>> +	ret += sprintf(str + ret,
>> +		       "\t\tRing enq index: %d, trb: %p (virt), 0x%llx (dma)\n",
>> +		       priv_ep->enqueue, trb,
>> +		       (unsigned long long)cdns3_trb_virt_to_dma(priv_ep, trb));
>> +
>> +	ret += sprintf(str + ret,
>> +		       "\t\tfree trbs: %d, CCS=%d, PCS=%d\n",
>> +		       priv_ep->free_trbs, priv_ep->ccs, priv_ep->pcs);
>> +
>> +	if (trb_per_sector > TRBS_PER_SEGMENT)
>> +		trb_per_sector = TRBS_PER_SEGMENT;
>> +
>> +	if (trb_per_sector > TRBS_PER_SEGMENT) {
>> +		sprintf(str + ret, "\t\tTo big transfer ring %d\n",
>> +			trb_per_sector);
>> +		return str;
>> +	}
>> +
>> +	for (i = 0; i < trb_per_sector; ++i) {
>> +		trb = &ring[i];
>> +		ret += sprintf(str + ret,
>> +			"\t\t@%pad %08x %08x %08x\n", &addr,
>> +			le32_to_cpu(trb->buffer),
>> +			le32_to_cpu(trb->length),
>> +			le32_to_cpu(trb->control));
>> +		addr += sizeof(*trb);
>> +	}
>> +
>> +	return str;
>> +}
>> +
>> +#ifdef CONFIG_DEBUG_FS
>> +void cdns3_debugfs_init(struct cdns3 *cdns);
>> +void cdns3_debugfs_exit(struct cdns3 *cdns);
>> +#else
>> +void cdns3_debugfs_init(struct cdns3 *cdns);
>> +{  }
>> +void cdns3_debugfs_exit(struct cdns3 *cdns);
>> +{  }
>> +#endif
>> +
>> +#endif /*__LINUX_CDNS3_DEBUG*/
>> diff --git a/drivers/usb/cdns3/debugfs.c b/drivers/usb/cdns3/debugfs.c
>> new file mode 100644
>> index 000000000000..d7919f5c1d90
>> --- /dev/null
>> +++ b/drivers/usb/cdns3/debugfs.c
>> @@ -0,0 +1,168 @@
>> +// SPDX-License-Identifier: GPL-2.0
>> +/*
>> + * Cadence USBSS DRD Controller DebugFS filer.
>> + *
>> + * Copyright (C) 2018 Cadence.
>> + *
>> + * Author: Pawel Laszczak <pawell@cadence.com>
>> + */
>> +
>> +#include <linux/types.h>
>> +#include <linux/debugfs.h>
>> +#include <linux/seq_file.h>
>> +#include <linux/uaccess.h>
>> +
>> +#include "core.h"
>> +#include "gadget.h"
>> +#include "drd.h"
>> +
>> +static int cdns3_mode_show(struct seq_file *s, void *unused)
>> +{
>> +	struct cdns3 *cdns = s->private;
>> +
>> +	switch (cdns->current_dr_mode) {
>> +	case USB_DR_MODE_HOST:
>> +		seq_puts(s, "host\n");
>> +		break;
>> +	case USB_DR_MODE_PERIPHERAL:
>> +		seq_puts(s, "device\n");
>> +		break;
>> +	case USB_DR_MODE_OTG:
>> +		seq_puts(s, "otg\n");
>> +		break;
>> +	default:
>> +		seq_puts(s, "UNKNOWN mode\n");
>> +	}
>> +
>> +	return 0;
>> +}
>> +
>> +static int cdns3_mode_open(struct inode *inode, struct file *file)
>> +{
>> +	return single_open(file, cdns3_mode_show, inode->i_private);
>> +}
>> +
>> +static ssize_t cdns3_mode_write(struct file *file,
>> +				const char __user *ubuf,
>> +				size_t count, loff_t *ppos)
>> +{
>> +	struct seq_file	 *s = file->private_data;
>> +	struct cdns3 *cdns = s->private;
>> +	u32 mode = USB_DR_MODE_UNKNOWN;
>> +	char buf[32];
>> +
>> +	if (copy_from_user(&buf, ubuf, min_t(size_t, sizeof(buf) - 1, count)))
>> +		return -EFAULT;
>> +
>> +	if (!strncmp(buf, "host", 4)) {
>> +		if (cdns->dr_mode == USB_DR_MODE_HOST ||
>> +		    cdns->dr_mode == USB_DR_MODE_OTG) {
>> +			mode = USB_DR_MODE_HOST;
>> +		}
>> +	}
>> +
>> +	if (!strncmp(buf, "device", 6))
>> +		if (cdns->dr_mode == USB_DR_MODE_PERIPHERAL ||
>> +		    cdns->dr_mode == USB_DR_MODE_OTG)
>> +			mode = USB_DR_MODE_PERIPHERAL;
>> +
>> +	if (!strncmp(buf, "otg", 3) && cdns->dr_mode == USB_DR_MODE_OTG)
>> +		mode = USB_DR_MODE_OTG;
>> +
>> +	if (mode == USB_DR_MODE_UNKNOWN) {
>> +		dev_err(cdns->dev, "Failed: incorrect mode setting\n");
>> +		return -EFAULT;
>> +	}
>> +
>> +	if (cdns->current_dr_mode != mode) {
>> +		cdns->desired_dr_mode = mode;
>> +		cdns->debug_disable = 0;
>> +		cdns3_role_stop(cdns);
>> +		cdns3_drd_update_mode(cdns);
>> +		queue_work(system_freezable_wq, &cdns->role_switch_wq);
>> +	}
>> +
>> +	return count;
>> +}
>> +
>> +static const struct file_operations cdns3_mode_fops = {
>> +	.open			= cdns3_mode_open,
>> +	.write			= cdns3_mode_write,
>> +	.read			= seq_read,
>> +	.llseek			= seq_lseek,
>> +	.release		= single_release,
>> +};
>> +
>> +static int cdns3_disable_show(struct seq_file *s, void *unused)
>> +{
>> +	struct cdns3 *cdns = s->private;
>> +
>> +	if (!cdns->debug_disable)
>> +		seq_puts(s, "0\n");
>> +	else
>> +		seq_puts(s, "1\n");
>> +
>> +	return 0;
>> +}
>> +
>> +static ssize_t cdns3_disable_write(struct file *file,
>> +				   const char __user *ubuf,
>> +				   size_t count, loff_t *ppos)
>> +{
>> +	struct seq_file	 *s = file->private_data;
>> +	struct cdns3 *cdns = s->private;
>> +	int disable;
>> +	char buf[32];
>> +
>> +	if (copy_from_user(&buf, ubuf, min_t(size_t, sizeof(buf) - 1, count)))
>> +		return -EFAULT;
>> +
>> +	if (!strncmp(buf, "1", 1) || !strncmp(buf, "yes", 3)) {
>> +		disable = 1;
>> +	} else if (!strncmp(buf, "0", 1) || !strncmp(buf, "no", 2)) {
>> +		disable = 0;
>> +	} else {
>> +		dev_err(cdns->dev, "Failed: incorrect disable setting\n");
>> +		return -EFAULT;
>> +	}
>> +
>There is common API kstrtobool()?
Ok, much simpler.
>
>> +	if (disable != cdns->debug_disable) {
>> +		cdns->debug_disable = disable;
>> +		queue_work(system_freezable_wq, &cdns->role_switch_wq);
>> +	}
>> +
>> +	return count;
>> +}
>> +
>> +static int cdns3_disable_open(struct inode *inode, struct file *file)
>> +{
>> +	return single_open(file, cdns3_disable_show, inode->i_private);
>> +}
>> +
>> +static const struct file_operations cdns3_disable_fops = {
>> +	.open			= cdns3_disable_open,
>> +	.write			= cdns3_disable_write,
>> +	.read			= seq_read,
>> +	.llseek			= seq_lseek,
>> +	.release		= single_release,
>> +};
>> +
>> +void cdns3_debugfs_init(struct cdns3 *cdns)
>> +{
>> +	struct dentry *root;
>> +
>> +	root = debugfs_create_dir(dev_name(cdns->dev), NULL);
>> +	cdns->root = root;
>> +	if (IS_ENABLED(CONFIG_USB_CDNS3_GADGET) &&
>> +	    IS_ENABLED(CONFIG_USB_CDNS3_HOST))
>> +		debugfs_create_file("mode", 0644, root, cdns,
>> +				    &cdns3_mode_fops);
>> +
>> +	debugfs_create_file("disable", 0644, root, cdns,
>> +			    &cdns3_disable_fops);
>> +}
>> +
>> +void cdns3_debugfs_exit(struct cdns3 *cdns)
>> +{
>> +	debugfs_remove_recursive(cdns->root);
>> +}
>> diff --git a/drivers/usb/cdns3/drd.c b/drivers/usb/cdns3/drd.c
>> new file mode 100644
>> index 000000000000..b0c32302eb0b
>> --- /dev/null
>> +++ b/drivers/usb/cdns3/drd.c
>> @@ -0,0 +1,350 @@
>> +// SPDX-License-Identifier: GPL-2.0
>> +/*
>> + * Cadence USBSS DRD Driver.
>> + *
>> + * Copyright (C) 2018 Cadence.
>> + *
>> + * Author: Pawel Laszczak <pawell@cadence.com
>> + *
>> + */
>> +#include <linux/kernel.h>
>> +#include <linux/interrupt.h>
>> +#include <linux/delay.h>
>> +#include <linux/usb/otg.h>
>> +
>> +#include "gadget.h"
>> +#include "drd.h"
>> +#include "core.h"
>> +
>> +static int cdns3_drd_switch_gadget(struct cdns3 *cdns, int on);
>> +static int cdns3_drd_switch_host(struct cdns3 *cdns, int on);
>> +
>> +/**
>> + * cdns3_set_mode - change mode of OTG Core
>> + * @cdns: pointer to context structure
>> + * @mode: selected mode from cdns_role
>> + */
>> +void cdns3_set_mode(struct cdns3 *cdns, enum usb_dr_mode mode)
>> +{
>> +	u32 reg;
>> +
>> +	cdns->current_dr_mode = mode;
>> +
>> +	switch (mode) {
>> +	case USB_DR_MODE_PERIPHERAL:
>> +		dev_info(cdns->dev, "Set controller to Gadget mode\n");
>> +		cdns3_drd_switch_gadget(cdns, 1);
>> +		break;
>> +	case USB_DR_MODE_HOST:
>> +		dev_info(cdns->dev, "Set controller to Host mode\n");
>> +		cdns3_drd_switch_host(cdns, 1);
>> +		break;
>> +	case USB_DR_MODE_OTG:
>> +		dev_info(cdns->dev, "Set controller to OTG mode\n");
>> +		if (cdns->version == CDNS3_CONTROLLER_V1) {
>> +			reg = readl(&cdns->otg_v1_regs->override);
>> +			reg |= OVERRIDE_IDPULLUP;
>> +			writel(reg, &cdns->otg_v1_regs->override);
>> +		} else {
>> +			reg = readl(&cdns->otg_v0_regs->ctrl1);
>> +			reg |= OVERRIDE_IDPULLUP_V0;
>> +			writel(reg, &cdns->otg_v0_regs->ctrl1);
>> +		}
>> +
>> +		/*
>> +		 * Hardware specification says: "ID_VALUE must be valid within
>> +		 * 50ms after idpullup is set to '1" so driver must wait
>> +		 * 50ms before reading this pin.
>> +		 */
>> +		usleep_range(50000, 60000);
>> +		break;
>> +	default:
>> +		cdns->current_dr_mode = USB_DR_MODE_UNKNOWN;
>> +		dev_err(cdns->dev, "Unsupported mode of operation %d\n", mode);
>> +		return;
>> +	}
>> +}
>> +
>> +int cdns3_get_id(struct cdns3 *cdns)
>> +{
>> +	int id;
>> +
>> +	id = readl(&cdns->otg_regs->sts) & OTGSTS_ID_VALUE;
>> +	dev_dbg(cdns->dev, "OTG ID: %d", id);
>> +	return  1 ; //id;
>return fixed value ?

My mistake. I forgot to restore this.  
It should be "return id" .
I thought that checkpatch remained me about this :). 
Thanks. 

>> +}
>> +
>> +int cdns3_is_host(struct cdns3 *cdns)
>> +{
>> +	if (cdns->current_dr_mode == USB_DR_MODE_HOST)
>> +		return 1;
>> +	else if (!cdns3_get_id(cdns))
>> +		return 1;
>> +
>> +	return 0;
>> +}
>> +
>> +
>
>> +
>> +/**
>> + * cdns3_init_otg_mode - initialize drd controller
>> + * @cdns: Pointer to controller context structure
>> + *
>> + * Returns 0 on success otherwise negative errno
>void function
It should be int. 
>> + */
>> +static void cdns3_init_otg_mode(struct cdns3 *cdns)
>> +{
>> +	cdns3_otg_disable_irq(cdns);
>> +	/* clear all interrupts */
>> +	writel(~0, &cdns->otg_regs->ivect);
>> +
>> +	cdns3_set_mode(cdns, USB_DR_MODE_OTG);
>> +
>> +	if (cdns3_is_host(cdns))
>> +		cdns3_drd_switch_host(cdns, 1);
>> +	else
>> +		cdns3_drd_switch_gadget(cdns, 1);
>> +
>> +	cdns3_otg_enable_irq(cdns);
>> +}
>> +
>> +/**
>> + * cdns3_drd_update_mode - initialize mode of operation
>> + * @cdns: Pointer to controller context structure
>> + *
>> + * Returns 0 on success otherwise negative errno
>> + */
>> +int cdns3_drd_update_mode(struct cdns3 *cdns)
>> +{
>> +	int ret = 0;
>> +
>> +	if (cdns->desired_dr_mode == cdns->current_dr_mode)
>> +		return ret;
>> +
>> +	cdns3_drd_switch_gadget(cdns, 0);
>> +	cdns3_drd_switch_host(cdns, 0);
>> +
>> +	switch (cdns->desired_dr_mode) {
>> +	case USB_DR_MODE_PERIPHERAL:
>> +		cdns3_set_mode(cdns, USB_DR_MODE_PERIPHERAL);
>> +		break;
>> +	case USB_DR_MODE_HOST:
>> +		cdns3_set_mode(cdns, USB_DR_MODE_HOST);
>> +		break;
>> +	case USB_DR_MODE_OTG:
>> +		cdns3_init_otg_mode(cdns);
>> +		break;
>> +	default:
>> +		dev_err(cdns->dev, "Unsupported mode of operation %d\n",
>> +			cdns->dr_mode);
>> +		return -EINVAL;
>> +	}
>> +
>> +	return ret;
>no need define local variable @ret

It will keep return value from cdns3_set_mode and cdns3_init_otg_mode functions.
>> +}
>> +
>> +/**
>> + * cdns3_drd_irq - interrupt handler for OTG events
>> + *
>> + * @irq: irq number for cdns3 core device
>> + * @data: structure of cdns3
>> + *
>> + * Returns IRQ_HANDLED or IRQ_NONE
>> + */
>> +static irqreturn_t cdns3_drd_irq(int irq, void *data)
>> +{
>> +	irqreturn_t ret = IRQ_NONE;
>> +	struct cdns3 *cdns = data;
>> +	u32 reg;
>> +
>> +	if (cdns->dr_mode != USB_DR_MODE_OTG)
>> +		return ret;
>> +
>> +	reg = readl(&cdns->otg_regs->ivect);
>> +
>> +	if (!reg)
>> +		return ret;
>> +
>> +	if (reg & OTGIEN_ID_CHANGE_INT) {
>> +		dev_dbg(cdns->dev, "OTG IRQ: new ID: %d\n",
>> +			cdns3_get_id(cdns));
>> +
>> +		queue_work(system_freezable_wq, &cdns->role_switch_wq);
>> +
>do you need to clear interrupts status here?
It's the best place for clearing interrupt. Driver enables only detection id changes. 
We can read ID status also from OTG status register. Even if driver lost some events it will 
not be a problem.  

>> +		ret = IRQ_HANDLED;
>> +	}
>> +
>> +	writel(~0, &cdns->otg_regs->ivect);
>> +	return ret;
>> +}
>> +
>> +int cdns3_drd_init(struct cdns3 *cdns)
>> +{
>> +	void __iomem *regs;
>> +	int ret = 0;
>> +	u32 state;
>> +
>> +	regs = devm_ioremap_resource(cdns->dev, &cdns->otg_res);
>> +	if (IS_ERR(regs))
>> +		return PTR_ERR(regs);
>> +
>> +	/* Detection of DRD version. Controller has been released
>> +	 * in two versions. Both are similar, but they have same changes
>> +	 * in register maps.
>> +	 * The first register in old version is command register and it's read
>> +	 * only, so driver should read 0 from it. On the other hand, in v1
>> +	 * the first register contains device ID number which is not set to 0.
>> +	 * Driver uses this fact to detect the proper version of
>> +	 * controller.
>> +	 */
>> +	cdns->otg_v0_regs = regs;
>> +	if (!readl(&cdns->otg_v0_regs->cmd)) {
>> +		cdns->version  = CDNS3_CONTROLLER_V0;
>> +		cdns->otg_v1_regs = NULL;
>> +		cdns->otg_regs = regs;
>> +		dev_info(cdns->dev, "DRD version v0 (%08x)\n",
>> +			 readl(&cdns->otg_v0_regs->version));
>> +	} else {
>> +		cdns->otg_v0_regs = NULL;
>> +		cdns->otg_v1_regs = regs;
>> +		cdns->otg_regs = (void *)&cdns->otg_v1_regs->cmd;
>> +		cdns->version  = CDNS3_CONTROLLER_V1;
>> +		dev_info(cdns->dev, "DRD version v1 (ID: %08x, rev: %08x)\n",
>> +			 readl(&cdns->otg_v1_regs->did),
>> +			 readl(&cdns->otg_v1_regs->rid));
>> +	}
>> +
>> +	state = OTGSTS_STRAP(readl(&cdns->otg_regs->sts));
>> +
>> +	/* Update dr_mode according to STRAP configuration. */
>> +	cdns->dr_mode = USB_DR_MODE_OTG;
>> +	if (state == OTGSTS_STRAP_HOST) {
>> +		dev_info(cdns->dev, "Controller strapped to HOST\n");
>> +		cdns->dr_mode = USB_DR_MODE_HOST;
>> +	} else if (state == OTGSTS_STRAP_GADGET) {
>> +		dev_info(cdns->dev, "Controller strapped to PERIPHERAL\n");
>> +		cdns->dr_mode = USB_DR_MODE_PERIPHERAL;
>> +	}
>> +
>> +	cdns->desired_dr_mode = cdns->dr_mode;
>> +	cdns->current_dr_mode = USB_DR_MODE_UNKNOWN;
>> +
>> +	ret = devm_request_threaded_irq(cdns->dev, cdns->irq, cdns3_drd_irq,
>> +					NULL, IRQF_SHARED,
>> +					dev_name(cdns->dev), cdns);
>> +
>> +	if (ret)
>> +		return ret;
>> +
>> +	state = readl(&cdns->otg_regs->sts);
>> +	if (OTGSTS_OTG_NRDY(state) != 0) {
>> +		dev_err(cdns->dev, "Cadence USB3 OTG device not ready\n");
>> +		return -ENODEV;
>> +	}
>> +
>> +	ret = cdns3_drd_update_mode(cdns);
>> +
>> +	return ret;
>return cdns3_drd_update_mode();
>> +}
>> +
>> +int cdns3_drd_exit(struct cdns3 *cdns)
>> +{
>> +	return cdns3_drd_switch_host(cdns, 0);
>> +}
>
>
><...>
>
>> diff --git a/drivers/usb/cdns3/ep0.c b/drivers/usb/cdns3/ep0.c
>> new file mode 100644
>> index 000000000000..89cf1cde1555
>> --- /dev/null
>> +++ b/drivers/usb/cdns3/ep0.c
>> @@ -0,0 +1,896 @@
>> +// SPDX-License-Identifier: GPL-2.0
>> +/*
>> + * Cadence USBSS DRD Driver - gadget side.
>> + *
>> + * Copyright (C) 2018 Cadence Design Systems.
>> + * Copyright (C) 2017-2018 NXP
>> + *
>> + * Authors: Pawel Jez <pjez@cadence.com>,
>> + *          Pawel Laszczak <pawell@cadence.com>
>> + *	    Peter Chen <peter.chen@nxp.com>
>> + */
>> +
>> +#include <linux/usb/composite.h>
>> +
>> +#include "gadget.h"
>> +#include "trace.h"
>> +
>> +static struct usb_endpoint_descriptor cdns3_gadget_ep0_desc = {
>> +	.bLength = USB_DT_ENDPOINT_SIZE,
>> +	.bDescriptorType = USB_DT_ENDPOINT,
>> +	.bmAttributes =	USB_ENDPOINT_XFER_CONTROL,
>> +};
>> +
><...>
>> +
>> +
>> +#endif /* __LINUX_CDNS3_GADGET_EXPORT */
>> diff --git a/drivers/usb/cdns3/gadget.c b/drivers/usb/cdns3/gadget.c
>> new file mode 100644
>> index 000000000000..0d95eb00be37
>> --- /dev/null
>> +++ b/drivers/usb/cdns3/gadget.c
>> @@ -0,0 +1,2102 @@
>> +// SPDX-License-Identifier: GPL-2.0
>> +/*
>> + * Cadence USBSS DRD Driver - gadget side.
>> + *
>> + * Copyright (C) 2018 Cadence Design Systems.
>> + * Copyright (C) 2017-2018 NXP
>> + *
>> + * Authors: Pawel Jez <pjez@cadence.com>,
>> + *          Pawel Laszczak <pawell@cadence.com>
>> + *	    Peter Chen <peter.chen@nxp.com>
>> + */
>> +
>> +#include <linux/dma-mapping.h>
>> +#include <linux/usb/gadget.h>
>> +
><...>
>
>> +/**
>> + * cdns3_next_request - returns next request from list
>> + * @list: list containing requests
>> + *
>> + * Returns request or NULL if no requests in list
>> + */
>> +struct usb_request *cdns3_next_request(struct list_head *list)
>> +{
>> +	if (list_empty(list))
>> +		return NULL;
>No need check it, if list is empty, list_first_entry_or_null() will
>return NULL
Ok, 

>> +	return list_first_entry_or_null(list, struct usb_request, list);
>> +}
>> +
>> +/**
>> + * cdns3_next_priv_request - returns next request from list
>> + * @list: list containing requests
>> + *
>> + * Returns request or NULL if no requests in list
>> + */
>> +struct cdns3_request *cdns3_next_priv_request(struct list_head *list)
>> +{
>> +	if (list_empty(list))
>> +		return NULL;
>ditto
>> +	return list_first_entry_or_null(list, struct cdns3_request, list);
>> +}
>> +
>
><...>
>
Thanks for comments.
Cheers,
Pawel


^ permalink raw reply	[flat|nested] 49+ messages in thread

* [v2,5/5] usb:cdns3 Add Cadence USB3 DRD Driver
@ 2018-12-31 12:31       ` Pawel Laszczak
  0 siblings, 0 replies; 49+ messages in thread
From: Pawel Laszczak @ 2018-12-31 12:31 UTC (permalink / raw)
  To: Chunfeng Yun
  Cc: devicetree, gregkh, mark.rutland, linux-usb, hdegoede,
	heikki.krogerus, andy.shevchenko, robh+dt, rogerq, linux-kernel,
	Alan Douglas, jbergsagel, nsekhar, nm, Suresh Punnoose,
	peter.chen, Pawel Jez, Rahul Kumar

Hi

>On Sun, 2018-12-23 at 15:13 +0000, Pawel Laszczak wrote:
>> This patch introduce new Cadence USBSS DRD driver
>> to linux kernel.
>
><...>
>
>> diff --git a/drivers/usb/cdns3/cdns3-pci-wrap.c b/drivers/usb/cdns3/cdns3-pci-wrap.c
>> new file mode 100644
>> index 000000000000..e93179c45ece
>> --- /dev/null
>> +++ b/drivers/usb/cdns3/cdns3-pci-wrap.c
>> @@ -0,0 +1,157 @@
>> +// SPDX-License-Identifier: GPL-2.0
>> +/*
>> + * Cadence USBSS PCI Glue driver
>> + *
>> + * Copyright (C) 2018 Cadence.
>> + *
>> + * Author: Pawel Laszczak <pawell@cadence.com>
>> + */
>> +
>> +#include <linux/kernel.h>
>> +#include <linux/module.h>
>> +#include <linux/pci.h>
>> +#include <linux/platform_device.h>
>> +#include <linux/dma-mapping.h>
>> +#include <linux/slab.h>
>> +
>> +struct cdns3_wrap {
>> +	struct platform_device *plat_dev;
>> +	struct pci_dev *hg_dev;
>> +	struct resource dev_res[4];
>> +};
>> +
>> +struct cdns3_wrap wrap;
>> +
>> +#define RES_IRQ_ID		0
>> +#define RES_HOST_ID		1
>> +#define RES_DEV_ID		2
>> +#define RES_DRD_ID		3
>> +
>> +#define PCI_BAR_HOST		0
>> +#define PCI_BAR_DEV		2
>> +#define PCI_BAR_OTG		4
>> +
>> +#define PCI_DEV_FN_HOST_DEVICE	0
>> +#define PCI_DEV_FN_OTG		1
>> +
>> +#define PCI_DRIVER_NAME		"cdns3-pci-usbss"
>> +#define PLAT_DRIVER_NAME	"cdns-usb3"
>> +
>> +#define CDNS_VENDOR_ID 0x17cd
>> +#define CDNS_DEVICE_ID 0x0100
>> +
>> +/**
>> + * cdns3_pci_probe - Probe function for Cadence USB wrapper driver
>> + * @pdev: platform device object
>> + * @id: pci device id
>> + *
>> + * Returns 0 on success otherwise negative errno
>> + */
>> +static int cdns3_pci_probe(struct pci_dev *pdev,
>> +			   const struct pci_device_id *id)
>> +{
>> +	struct platform_device_info plat_info;
>> +	struct cdns3_wrap *wrap;
>> +	struct resource *res;
>> +	int err;
>> +
>> +	/*
>> +	 * for GADGET/HOST PCI (devfn) function number is 0,
>> +	 * for OTG PCI (devfn) function number is 1
>> +	 */
>> +	if (!id || pdev->devfn != PCI_DEV_FN_HOST_DEVICE)
>> +		return -EINVAL;
>> +
>> +	err = pcim_enable_device(pdev);
>> +	if (err) {
>> +		dev_err(&pdev->dev, "Enabling PCI device has failed %d\n", err);
>> +		return err;
>> +	}
>> +
>> +	pci_set_master(pdev);
>> +	wrap = devm_kzalloc(&pdev->dev, sizeof(*wrap), GFP_KERNEL);
>> +	if (!wrap) {
>> +		dev_err(&pdev->dev, "Failed to allocate memory\n");
>> +		return -ENOMEM;
>> +	}
>> +
>> +	/* function 0: host(BAR_0) + device(BAR_1) + otg(BAR_2)). */
>> +	memset(wrap->dev_res, 0x00,
>> +	       sizeof(struct resource) * ARRAY_SIZE(wrap->dev_res));
>> +	dev_dbg(&pdev->dev, "Initialize Device resources\n");
>> +	res = wrap->dev_res;
>> +
>> +	res[RES_DEV_ID].start = pci_resource_start(pdev, PCI_BAR_DEV);
>> +	res[RES_DEV_ID].end =   pci_resource_end(pdev, PCI_BAR_DEV);
>> +	res[RES_DEV_ID].name = "cdns3-dev-regs";
>> +	res[RES_DEV_ID].flags = IORESOURCE_MEM;
>> +	dev_dbg(&pdev->dev, "USBSS-DEV physical base addr: %pa\n",
>> +		&res[RES_DEV_ID].start);
>> +
>> +	res[RES_HOST_ID].start = pci_resource_start(pdev, PCI_BAR_HOST);
>> +	res[RES_HOST_ID].end = pci_resource_end(pdev, PCI_BAR_HOST);
>> +	res[RES_HOST_ID].name = "cdns3-xhci-regs";
>> +	res[RES_HOST_ID].flags = IORESOURCE_MEM;
>> +	dev_dbg(&pdev->dev, "USBSS-XHCI physical base addr: %pa\n",
>> +		&res[RES_HOST_ID].start);
>> +
>> +	res[RES_DRD_ID].start = pci_resource_start(pdev, PCI_BAR_OTG);
>> +	res[RES_DRD_ID].end =   pci_resource_end(pdev, PCI_BAR_OTG);
>> +	res[RES_DRD_ID].name = "cdns3-otg";
>> +	res[RES_DRD_ID].flags = IORESOURCE_MEM;
>> +	dev_dbg(&pdev->dev, "USBSS-DRD physical base addr: %pa\n",
>> +		&res[RES_DRD_ID].start);
>> +
>> +	/* Interrupt common for both device and XHCI */
>> +	wrap->dev_res[RES_IRQ_ID].start = pdev->irq;
>> +	wrap->dev_res[RES_IRQ_ID].name = "cdns3-irq";
>> +	wrap->dev_res[RES_IRQ_ID].flags = IORESOURCE_IRQ;
>> +
>> +	/* set up platform device info */
>> +	memset(&plat_info, 0, sizeof(plat_info));
>> +	plat_info.parent = &pdev->dev;
>> +	plat_info.fwnode = pdev->dev.fwnode;
>> +	plat_info.name = PLAT_DRIVER_NAME;
>> +	plat_info.id = pdev->devfn;
>> +	plat_info.res = wrap->dev_res;
>> +	plat_info.num_res = ARRAY_SIZE(wrap->dev_res);
>> +	plat_info.dma_mask = pdev->dma_mask;
>> +
>> +	/* register platform device */
>> +	wrap->plat_dev = platform_device_register_full(&plat_info);
>> +	if (IS_ERR(wrap->plat_dev)) {
>> +		err = PTR_ERR(wrap->plat_dev);
>> +		return err;
>> +	}
>return PTR_ERR(wrap->plat_dev);
Ok. 
>
>> +
>> +	pci_set_drvdata(pdev, wrap);
>> +
>> +	return err;
>> +}
>> +
>> +void cdns3_pci_remove(struct pci_dev *pdev)
>> +{
>> +	struct cdns3_wrap *wrap = (struct cdns3_wrap *)pci_get_drvdata(pdev);
>> +
>> +	platform_device_unregister(wrap->plat_dev);
>> +}
>> +
>> +static const struct pci_device_id cdns3_pci_ids[] = {
>> +	{ PCI_DEVICE(CDNS_VENDOR_ID, CDNS_DEVICE_ID), },
>> +	{ 0, }
>> +};
>> +
>> +static struct pci_driver cdns3_pci_driver = {
>> +	.name = PCI_DRIVER_NAME,
>> +	.id_table = cdns3_pci_ids,
>> +	.probe = cdns3_pci_probe,
>> +	.remove = cdns3_pci_remove,
>> +};
>> +
>> +module_pci_driver(cdns3_pci_driver);
>> +MODULE_DEVICE_TABLE(pci, cdns3_pci_ids);
>> +
>> +MODULE_AUTHOR("Pawel Laszczak <pawell@cadence.com>");
>> +MODULE_LICENSE("GPL v2");
>> +MODULE_DESCRIPTION("Cadence USBSS PCI wrapperr");
>> +
>> diff --git a/drivers/usb/cdns3/core.c b/drivers/usb/cdns3/core.c
>> new file mode 100644
>> index 000000000000..d274586aca36
>> --- /dev/null
>> +++ b/drivers/usb/cdns3/core.c
>> @@ -0,0 +1,406 @@
>> +// SPDX-License-Identifier: GPL-2.0
>> +/*
>> + * Cadence USBSS DRD Driver.
>> + *
>> + * Copyright (C) 2018 Cadence.
>> + * Copyright (C) 2017-2018 NXP
>> + *
>> + * Author: Peter Chen <peter.chen@nxp.com>
>> + *         Pawel Laszczak <pawell@cadence.com>
>> + */
>> +
>> +#include <linux/module.h>
>> +#include <linux/kernel.h>
>> +#include <linux/platform_device.h>
>> +#include <linux/interrupt.h>
>> +#include <linux/io.h>
>> +#include <linux/pm_runtime.h>
>> +
>> +#include "gadget.h"
>> +#include "core.h"
>> +#include "host-export.h"
>> +#include "gadget-export.h"
>> +#include "drd.h"
>> +#include "debug.h"
>> +
>> +static inline
>> +struct cdns3_role_driver *cdns3_get_current_role_driver(struct cdns3 *cdns)
>> +{
>> +	WARN_ON(cdns->role >= CDNS3_ROLE_END || !cdns->roles[cdns->role]);
>> +	return cdns->roles[cdns->role];
>> +}
>> +
>> +static inline int cdns3_role_start(struct cdns3 *cdns, enum cdns3_roles role)
>remove inline
Ok.

>> +{
>> +	int ret;
>> +
>> +	if (WARN_ON(role >= CDNS3_ROLE_END))
>> +		return 0;
>> +
>> +	if (!cdns->roles[role])
>> +		return -ENXIO;
>> +
>> +	if (cdns->roles[role]->state == CDNS3_ROLE_STATE_ACTIVE)
>> +		return 0;
>> +
>> +	mutex_lock(&cdns->mutex);
>> +	cdns->role = role;
>> +	ret = cdns->roles[role]->start(cdns);
>> +	if (!ret)
>> +		cdns->roles[role]->state = CDNS3_ROLE_STATE_ACTIVE;
>> +	mutex_unlock(&cdns->mutex);
>> +	return ret;
>> +}
>> +
>> +void cdns3_role_stop(struct cdns3 *cdns)
>> +{
>> +	enum cdns3_roles role = cdns->role;
>> +
>> +	if (role >= CDNS3_ROLE_END) {
>> +		WARN_ON(role > CDNS3_ROLE_END);
>> +		return;
>> +	}
>> +
>> +	if (cdns->roles[role]->state == CDNS3_ROLE_STATE_INACTIVE)
>> +		return;
>> +
>> +	mutex_lock(&cdns->mutex);
>> +	cdns->roles[role]->stop(cdns);
>> +	cdns->roles[role]->state = CDNS3_ROLE_STATE_INACTIVE;
>> +	mutex_unlock(&cdns->mutex);
>> +}
>> +
>> +/*
>> + * cdns->role gets from cdns3_get_initial_role, and this API tells role at the
>> + * runtime.
>> + * If both roles are supported, the role is selected based on vbus/id.
>> + * It could be read from OTG register or external connector.
>> + * If only single role is supported, only one role structure
>> + * is allocated, cdns->roles[CDNS3_ROLE_HOST] or cdns->roles[CDNS3_ROLE_GADGET].
>> + */
>> +static enum cdns3_roles cdns3_get_initial_role(struct cdns3 *cdns)
>> +{
>> +	if (cdns->roles[CDNS3_ROLE_HOST] && cdns->roles[CDNS3_ROLE_GADGET]) {
>> +		if (cdns3_is_host(cdns))
>> +			return CDNS3_ROLE_HOST;
>> +		if (cdns3_is_device(cdns))
>> +			return CDNS3_ROLE_GADGET;
>> +	}
>> +	return cdns->roles[CDNS3_ROLE_HOST]
>> +		? CDNS3_ROLE_HOST
>> +		: CDNS3_ROLE_GADGET;
>> +}
>> +
>> +static void cdns3_exit_roles(struct cdns3 *cdns)
>> +{
>> +	cdns3_role_stop(cdns);
>> +	cdns3_drd_exit(cdns);
>> +}
>> +
>> +/**
>> + * cdns3_core_init_role - initialize role of operation
>> + * @cdns: Pointer to cdns3 structure
>> + *
>> + * Returns 0 on success otherwise negative errno
>> + */
>> +static int cdns3_core_init_role(struct cdns3 *cdns)
>> +{
>> +	struct device *dev = cdns->dev;
>> +	enum usb_dr_mode best_dr_mode;
>> +	enum usb_dr_mode dr_mode;
>> +	int ret = 0;
>> +
>> +	dr_mode = usb_get_dr_mode(dev);
>> +	cdns->role = CDNS3_ROLE_END;
>> +
>> +	/*
>> +	 * If driver can't read mode by means of usb_get_dr_mdoe function then
>> +	 * chooses mode according with Kernel configuration. This setting
>> +	 * can be restricted later depending on strap pin configuration.
>> +	 */
>> +	if (dr_mode == USB_DR_MODE_UNKNOWN) {
>> +		if (IS_ENABLED(CONFIG_USB_CDNS3_HOST) &&
>> +		    IS_ENABLED(CONFIG_USB_CDNS3_GADGET))
>> +			dr_mode = USB_DR_MODE_OTG;
>> +		else if (IS_ENABLED(CONFIG_USB_CDNS3_HOST))
>> +			dr_mode = USB_DR_MODE_HOST;
>> +		else if (IS_ENABLED(CONFIG_USB_CDNS3_GADGET))
>> +			dr_mode = USB_DR_MODE_PERIPHERAL;
>> +	}
>> +
>> +	best_dr_mode = USB_DR_MODE_OTG;
>> +
>> +	if (dr_mode == USB_DR_MODE_OTG) {
>> +		best_dr_mode = cdns->dr_mode;
>> +	} else if (cdns->dr_mode == USB_DR_MODE_OTG) {
>> +		best_dr_mode = dr_mode;
>> +	} else if (cdns->dr_mode != dr_mode) {
>> +		dev_err(dev, "Incorrect DRD configuration\n");
>> +		return -EINVAL;
>> +	}
>> +
>> +	dr_mode = best_dr_mode;
>> +
>> +	if (dr_mode == USB_DR_MODE_OTG || dr_mode == USB_DR_MODE_HOST) {
>> +		ret = cdns3_host_init(cdns);
>> +		if (ret) {
>> +			dev_err(dev, "Host initialization failed with %d\n",
>> +				ret);
>> +			goto err;
>> +		}
>> +	}
>> +
>> +	if (dr_mode == USB_DR_MODE_OTG || dr_mode == USB_DR_MODE_PERIPHERAL) {
>> +		ret = cdns3_gadget_init(cdns);
>> +		if (ret) {
>> +			dev_err(dev, "Device initialization failed with %d\n",
>> +				ret);
>> +			goto err;
>> +		}
>> +	}
>> +
>> +	cdns->desired_dr_mode = dr_mode;
>> +	cdns->dr_mode = dr_mode;
>> +	/*
>> +	 * dr_mode could be change so DRD must update controller
>> +	 * configuration
>> +	 */
>> +	ret = cdns3_drd_update_mode(cdns);
>Do you need check ret value? if not, no need assign it

I will add checking. 
>> +
>> +	cdns->role = cdns3_get_initial_role(cdns);
>> +
>> +	ret = cdns3_role_start(cdns, cdns->role);
>> +	if (ret) {
>> +		dev_err(dev, "can't start %s role\n",
>> +			cdns3_get_current_role_driver(cdns)->name);
>> +		goto err;
>> +	}
>> +
>> +	return ret;
>> +err:
>> +	cdns3_exit_roles(cdns);
>> +	return ret;
>> +}
>> +
>> +/**
>> + * cdsn3_get_real_role - get real role of controller based on hardware settings.
>> + * @cdns: Pointer to cdns3 structure
>> + *
>> + * Returns role
>> + */
>> +enum cdns3_roles cdsn3_get_real_role(struct cdns3 *cdns)
>> +{
>> +	enum cdns3_roles role = CDNS3_ROLE_END;
>> +
>> +	if (cdns->current_dr_mode == USB_DR_MODE_OTG) {
>> +		if (cdns3_get_id(cdns))
>> +			role = CDNS3_ROLE_GADGET;
>> +		else
>> +			role = CDNS3_ROLE_HOST;
>> +	} else {
>> +		if (cdns3_is_host(cdns))
>> +			role = CDNS3_ROLE_HOST;
>> +		if (cdns3_is_device(cdns))
>> +			role = CDNS3_ROLE_GADGET;
>> +	}
>> +
>> +	return role;
>> +}
>> +
>> +/**
>> + * cdns3_role_switch - work queue handler for role switch
>> + *
>> + * @work: work queue item structure
>> + *
>> + * Handles below events:
>> + * - Role switch for dual-role devices
>> + * - CDNS3_ROLE_GADGET <--> CDNS3_ROLE_END for peripheral-only devices
>> + */
>> +static void cdns3_role_switch(struct work_struct *work)
>> +{
>> +	enum cdns3_roles role = CDNS3_ROLE_END;
>> +	struct cdns3_role_driver *role_drv;
>> +	enum cdns3_roles current_role;
>> +	struct cdns3 *cdns;
>> +	int ret = 0;
>> +
>> +	cdns = container_of(work, struct cdns3, role_switch_wq);
>> +
>> +	/* During switching cdns->role can be different then role */
>> +	role = cdsn3_get_real_role(cdns);
>> +
>> +	role_drv = cdns3_get_current_role_driver(cdns);
>> +
>> +	pm_runtime_get_sync(cdns->dev);
>> +
>> +	/* Disable current role. This state can be forced from user space. */
>> +	if (cdns->debug_disable && role_drv->state == CDNS3_ROLE_STATE_ACTIVE) {
>> +		cdns3_role_stop(cdns);
>> +		goto exit;
>> +	}
>> +
>> +	/* Do nothing if nothing changed */
>> +	if (cdns->role == role && role_drv->state == CDNS3_ROLE_STATE_ACTIVE)
>> +		goto exit;
>> +
>> +	cdns3_role_stop(cdns);
>> +
>> +	role = cdsn3_get_real_role(cdns);
>> +
>> +	current_role = cdns->role;
>> +	dev_dbg(cdns->dev, "Switching role");
>> +
>> +	ret = cdns3_role_start(cdns, role);
>> +
>remove blank line
>> +	if (ret) {
>> +		/* Back to current role */
>> +		dev_err(cdns->dev, "set %d has failed, back to %d\n",
>> +			role, current_role);
>> +		cdns3_role_start(cdns, current_role);
>> +	}
>> +exit:
>> +	pm_runtime_put_sync(cdns->dev);
>> +}
>> +
>> +/**
>> + * cdns3_probe - probe for cdns3 core device
>> + * @pdev: Pointer to cdns3 core platform device
>> + *
>> + * Returns 0 on success otherwise negative errno
>> + */
>> +static int cdns3_probe(struct platform_device *pdev)
>> +{
>> +	struct device *dev = &pdev->dev;
>> +	struct resource	*res;
>> +	struct cdns3 *cdns;
>> +	void __iomem *regs;
>> +	int ret;
>> +
>> +	cdns = devm_kzalloc(dev, sizeof(*cdns), GFP_KERNEL);
>> +	if (!cdns)
>> +		return -ENOMEM;
>> +
>> +	cdns->dev = dev;
>> +
>> +	platform_set_drvdata(pdev, cdns);
>> +
>> +	res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
>> +	if (!res) {
>> +		dev_err(dev, "missing IRQ\n");
>> +		return -ENODEV;
>> +	}
>> +	cdns->irq = res->start;
>> +
>> +	cdns->xhci_res[0] = *res;
>> +
>> +	/*
>> +	 * Request memory region
>> +	 * region-0: xHCI
>> +	 * region-1: Peripheral
>> +	 * region-2: OTG registers
>> +	 */
>> +	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
>> +	cdns->xhci_res[1] = *res;
>> +
>> +	res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
>> +	regs = devm_ioremap_resource(dev, res);
>> +	if (IS_ERR(regs))
>> +		return PTR_ERR(regs);
>> +	cdns->dev_regs	= regs;
>> +
>> +	res = platform_get_resource(pdev, IORESOURCE_MEM, 2);
>> +	cdns->otg_res = *res;
>It'll make the DTS clearer if add reg-names

Good Idea. I assume that in this case regs-names will be mandatory property  and
I have to replace platform_get_resource with platform_get_resource_byname. 
Also I have to made some changes in cdns3-pci-wrap.c file.

>> +
>> +	mutex_init(&cdns->mutex);
>> +
>> +	cdns->phy = devm_phy_get(dev, "cdns3,usbphy");
>Try to use devm_phy_optional_get()?

Ok, so the code will look like:
	cdns->phy = devm_phy_optional_get(dev, "cdns3,usbphy");
	if (IS_ERR(cdns->phy) )
		return PTR_ERR(cdns->phy);

>> +	if (IS_ERR(cdns->phy)) {
>> +		ret = PTR_ERR(cdns->phy);
>> +		if (ret == -ENODEV) {
>> +			cdns->phy = NULL;
>> +		} else if (ret == -EPROBE_DEFER) {
>> +			return ret;
>> +		} else {
>> +			dev_err(dev, "no phy found\n");
>> +			goto err0;
>> +		}
>> +	}
>> +
>> +	phy_init(cdns->phy);
>> +
>> +	INIT_WORK(&cdns->role_switch_wq, cdns3_role_switch);
>> +
>> +	ret = cdns3_drd_init(cdns);
>> +	if (ret)
>> +		goto err1;
>> +
>> +	ret = cdns3_core_init_role(cdns);
>> +	if (ret)
>> +		goto err1;
>> +
>> +	cdns3_debugfs_init(cdns);
>> +	device_set_wakeup_capable(dev, true);
>> +	pm_runtime_set_active(dev);
>> +	pm_runtime_enable(dev);
>> +
>> +	/*
>> +	 * The controller needs less time between bus and controller suspend,
>> +	 * and we also needs a small delay to avoid frequently entering low
>> +	 * power mode.
>> +	 */
>> +	pm_runtime_set_autosuspend_delay(dev, 20);
>> +	pm_runtime_mark_last_busy(dev);
>> +	pm_runtime_use_autosuspend(dev);
>> +	dev_dbg(dev, "Cadence USB3 core: probe succeed\n");
>> +
>> +	return 0;
>> +
>> +err1:
>> +	phy_exit(cdns->phy);
>> +err0:
>> +	return ret;
>> +}
>> +
>> +/**
>> + * cdns3_remove - unbind drd driver and clean up
>> + * @pdev: Pointer to Linux platform device
>> + *
>> + * Returns 0 on success otherwise negative errno
>> + */
>> +static int cdns3_remove(struct platform_device *pdev)
>> +{
>> +	struct cdns3 *cdns = platform_get_drvdata(pdev);
>> +
>> +	pm_runtime_get_sync(&pdev->dev);
>> +	pm_runtime_disable(&pdev->dev);
>> +	pm_runtime_put_noidle(&pdev->dev);
>> +	cdns3_debugfs_exit(cdns);
>> +	cdns3_exit_roles(cdns);
>> +	phy_exit(cdns->phy);
>> +	return 0;
>> +}
>> +
>> +#ifdef CONFIG_OF
>> +static const struct of_device_id of_cdns3_match[] = {
>> +	{ .compatible = "cdns,usb3-1.0.0" },
>> +	{ .compatible = "cdns,usb3-1.0.1" },
>> +	{ },
>> +};
>> +MODULE_DEVICE_TABLE(of, of_cdns3_match);
>> +#endif
>> +
>> +static struct platform_driver cdns3_driver = {
>> +	.probe		= cdns3_probe,
>> +	.remove		= cdns3_remove,
>> +	.driver		= {
>> +		.name	= "cdns-usb3",
>> +		.of_match_table	= of_match_ptr(of_cdns3_match),
>> +	},
>> +};
>> +
>> +module_platform_driver(cdns3_driver);
>> +
>> +MODULE_ALIAS("platform:cdns3");
>> +MODULE_AUTHOR("Pawel Laszczak <pawell@cadence.com>");
>> +MODULE_LICENSE("GPL v2");
>> +MODULE_DESCRIPTION("Cadence USB3 DRD Controller Driver");
>> diff --git a/drivers/usb/cdns3/core.h b/drivers/usb/cdns3/core.h
>> new file mode 100644
>> index 000000000000..fb4b39206158
>> --- /dev/null
>> +++ b/drivers/usb/cdns3/core.h
>> @@ -0,0 +1,116 @@
>> +/* SPDX-License-Identifier: GPL-2.0 */
>> +/*
>> + * Cadence USBSS DRD Header File.
>> + *
>> + * Copyright (C) 2017-2018 NXP
>> + * Copyright (C) 2018 Cadence.
>> + *
>> + * Authors: Peter Chen <peter.chen@nxp.com>
>> + *          Pawel Laszczak <pawell@cadence.com>
>> + */
>> +#include <linux/usb/otg.h>
>> +
>> +#ifndef __LINUX_CDNS3_CORE_H
>> +#define __LINUX_CDNS3_CORE_H
>> +
>> +struct cdns3;
>> +enum cdns3_roles {
>> +	CDNS3_ROLE_HOST = 0,
>> +	CDNS3_ROLE_GADGET,
>> +	CDNS3_ROLE_END,
>> +};
>> +
>> +/**
>> + * struct cdns3_role_driver - host/gadget role driver
>> + * @start: start this role
>> + * @stop: stop this role
>> + * @suspend: suspend callback for this role
>> + * @resume: resume callback for this role
>> + * @irq: irq handler for this role
>> + * @name: role name string (host/gadget)
>> + * @state: current state
>> + */
>> +struct cdns3_role_driver {
>> +	int (*start)(struct cdns3 *cdns);
>> +	void (*stop)(struct cdns3 *cdns);
>> +	int (*suspend)(struct cdns3 *cdns, bool do_wakeup);
>> +	int (*resume)(struct cdns3 *cdns, bool hibernated);
>> +	const char *name;
>> +#define CDNS3_ROLE_STATE_INACTIVE	0
>> +#define CDNS3_ROLE_STATE_ACTIVE		1
>> +	int state;
>> +};
>> +
>> +#define CDNS3_XHCI_RESOURCES_NUM	2
>> +/**
>> + * struct cdns3 - Representation of Cadence USB3 DRD controller.
>> + * @dev: pointer to Cadence device struct
>> + * @xhci_regs: pointer to base of xhci registers
>> + * @xhci_res: the resource for xhci
>> + * @dev_regs: pointer to base of dev registers
>> + * @otg_regs: pointer to base of otg registers
>> + * @irq: irq number for controller
>> + * @roles: array of supported roles for this controller
>> + * @role: current role
>> + * @host_dev: the child host device pointer for cdns3 core
>> + * @gadget_dev: the child gadget device pointer for cdns3 core
>> + * @usb: phy for this controller
>> + * @role_switch_wq: work queue item for role switch
>> + * @in_lpm: the controller in low power mode
>> + * @wakeup_int: the wakeup interrupt
>> + * @mutex: the mutex for concurrent code at driver
>> + * @dr_mode: supported mode of operation it can be only Host, only Device
>> + *           or OTG mode that allow to switch between Device and Host mode.
>> + *           This field based on firmware setting, kernel configuration
>> + *           and hardware configuration.
>> + * @current_dr_mode: current mode of operation when in dual-role mode
>> + * @desired_dr_mode: desired mode of operation when in dual-role mode.
>> + *           This value can be changed during runtime.
>> + *           Available options depends on  dr_mode:
>> + *           dr_mode                 |  desired_dr_mode and current_dr_mode
>> + *           ----------------------------------------------------------------
>> + *           USB_DR_MODE_HOST        | only USB_DR_MODE_HOST
>> + *           USB_DR_MODE_PERIPHERAL  | only USB_DR_MODE_PERIPHERAL
>> + *           USB_DR_MODE_OTG         | only USB_DR_MODE_HOST
>> + *           USB_DR_MODE_OTG         | only USB_DR_MODE_PERIPHERAL
>> + *           USB_DR_MODE_OTG         | USB_DR_MODE_OTG
>> + *
>> + *           Desired_dr_role can be changed by means of debugfs.
>> + * @root: debugfs root folder pointer
>> + * @debug_disable:
>> + */
>> +struct cdns3 {
>> +	struct device			*dev;
>> +	void __iomem			*xhci_regs;
>> +	struct resource			xhci_res[CDNS3_XHCI_RESOURCES_NUM];
>> +	struct cdns3_usb_regs __iomem	*dev_regs;
>> +
>> +	struct resource			otg_res;
>> +	struct cdns3_otg_legacy_regs	*otg_v0_regs;
>> +	struct cdns3_otg_regs		*otg_v1_regs;
>> +	struct cdns3_otg_common_regs	*otg_regs;
>> +#define CDNS3_CONTROLLER_V0	0
>> +#define CDNS3_CONTROLLER_V1	1
>> +	u32				version;
>> +
>> +	int				irq;
>> +	struct cdns3_role_driver	*roles[CDNS3_ROLE_END];
>> +	enum cdns3_roles		role;
>> +	struct platform_device		*host_dev;
>> +	struct cdns3_device		*gadget_dev;
>> +	struct phy			*phy;
>> +	struct work_struct		role_switch_wq;
>> +	int				in_lpm:1;
>> +	int				wakeup_int:1;
>> +	/* mutext used in workqueue*/
>> +	struct mutex			mutex;
>> +	enum usb_dr_mode		dr_mode;
>> +	enum usb_dr_mode		current_dr_mode;
>> +	enum usb_dr_mode		desired_dr_mode;
>> +	struct dentry			*root;
>> +	int				debug_disable:1;
>> +};
>> +
>> +void cdns3_role_stop(struct cdns3 *cdns);
>> +
>> +#endif /* __LINUX_CDNS3_CORE_H */
>> diff --git a/drivers/usb/cdns3/debug.h b/drivers/usb/cdns3/debug.h
>> new file mode 100644
>> index 000000000000..94f9ef15f899
>> --- /dev/null
>> +++ b/drivers/usb/cdns3/debug.h
>> @@ -0,0 +1,166 @@
>> +/* SPDX-License-Identifier: GPL-2.0 */
>> +/*
>> + * Cadence USBSS DRD Driver.
>> + * Debug header file.
>> + *
>> + * Copyright (C) 2018 Cadence.
>> + *
>> + * Author: Pawel Laszczak <pawell@cadence.com>
>> + */
>> +#ifndef __LINUX_CDNS3_DEBUG
>> +#define __LINUX_CDNS3_DEBUG
>> +
>> +#include "core.h"
>> +
>> +static inline char *cdns3_decode_usb_irq(char *str,
>> +					 enum usb_device_speed speed,
>> +
>too many sentences as inline function
>> 					 u32 usb_ists)
>> +{
>> +	int ret;
>> +
>> +	ret = sprintf(str, "IRQ %08x = ", usb_ists);
>> +
>> +	if (usb_ists & (USB_ISTS_CON2I | USB_ISTS_CONI)) {
>> +		ret += sprintf(str + ret, "Connection %s\n",
>> +			       usb_speed_string(speed));
>> +	}
>> +	if (usb_ists & USB_ISTS_CON2I || usb_ists & USB_ISTS_CONI)
>> +		ret += sprintf(str + ret, "Disconnection ");
>> +	if (usb_ists & USB_ISTS_L2ENTI)
>> +		ret += sprintf(str + ret, "suspended ");
>> +
>> +	if (usb_ists & USB_ISTS_L2EXTI)
>> +		ret += sprintf(str + ret, "L2 exit ");
>> +	if (usb_ists & USB_ISTS_U3EXTI)
>> +		ret += sprintf(str + ret, "U3 exit ");
>> +	if (usb_ists & USB_ISTS_UWRESI)
>> +		ret += sprintf(str + ret, "Warm Reset ");
>> +	if (usb_ists & USB_ISTS_UHRESI)
>> +		ret += sprintf(str + ret, "Hot Reset ");
>> +	if (usb_ists & USB_ISTS_U2RESI)
>> +		ret += sprintf(str + ret, "Reset");
>> +
>> +	return str;
>> +}
>> +
>> +static inline  char *cdns3_decode_ep_irq(char *str,
>> +					 u32 ep_sts,
>> +					 const char *ep_name)
>ditto
>> +{
>> +	int ret;
>> +
>> +	ret = sprintf(str, "IRQ for %s: %08x ", ep_name, ep_sts);
>> +
>> +	if (ep_sts & EP_STS_SETUP)
>> +		ret += sprintf(str + ret, "SETUP ");
>> +	if (ep_sts & EP_STS_IOC)
>> +		ret += sprintf(str + ret, "IOC ");
>> +	if (ep_sts & EP_STS_ISP)
>> +		ret += sprintf(str + ret, "ISP ");
>> +	if (ep_sts & EP_STS_DESCMIS)
>> +		ret += sprintf(str + ret, "DESCMIS ");
>> +	if (ep_sts & EP_STS_STREAMR)
>> +		ret += sprintf(str + ret, "STREAMR ");
>> +	if (ep_sts & EP_STS_MD_EXIT)
>> +		ret += sprintf(str + ret, "MD_EXIT ");
>> +	if (ep_sts & EP_STS_TRBERR)
>> +		ret += sprintf(str + ret, "TRBERR ");
>> +	if (ep_sts & EP_STS_NRDY)
>> +		ret += sprintf(str + ret, "NRDY ");
>> +	if (ep_sts & EP_STS_PRIME)
>> +		ret += sprintf(str + ret, "PRIME ");
>> +	if (ep_sts & EP_STS_SIDERR)
>> +		ret += sprintf(str + ret, "SIDERRT ");
>> +	if (ep_sts & EP_STS_OUTSMM)
>> +		ret += sprintf(str + ret, "OUTSMM ");
>> +	if (ep_sts & EP_STS_ISOERR)
>> +		ret += sprintf(str + ret, "ISOERR ");
>> +	if (ep_sts & EP_STS_IOT)
>> +		ret += sprintf(str + ret, "IOT ");
>> +
>> +	return str;
>> +}
>> +
>> +static inline char *cdns3_decode_epx_irq(char *str,
>> +					 char *ep_name,
>> +					 u32 ep_sts)
>> +{
>> +	return cdns3_decode_ep_irq(str, ep_sts, ep_name);
>> +}
>> +
>> +static inline char *cdns3_decode_ep0_irq(char *str,
>> +					 int dir,
>> +					 u32 ep_sts)
>> +{
>> +	return cdns3_decode_ep_irq(str, ep_sts,
>> +				   dir ? "ep0IN" : "ep0OUT");
>> +}
>> +
>> +/**
>> + * Debug a transfer ring.
>> + *
>> + * Prints out all TRBs in the endpoint ring, even those after the Link TRB.
>> + *.
>> + */
>> +static inline char *cdns3_dbg_ring(struct cdns3_endpoint *priv_ep,
>> +				   struct cdns3_trb *ring, char *str)
>ditto
>> +{
>> +	dma_addr_t addr = priv_ep->trb_pool_dma;
>> +	struct cdns3_trb *trb;
>> +	int trb_per_sector;
>> +	int ret = 0;
>> +	int i;
>> +
>> +	trb_per_sector = GET_TRBS_PER_SEGMENT(priv_ep->type);
>> +
>> +	trb = &priv_ep->trb_pool[priv_ep->dequeue];
>> +	ret += sprintf(str + ret, "\n\t\tRing contents for %s:", priv_ep->name);
>> +
>> +	ret += sprintf(str + ret,
>> +		       "\n\t\tRing deq index: %d, trb: %p (virt), 0x%llx (dma)\n",
>> +		       priv_ep->dequeue, trb,
>> +		       (unsigned long long)cdns3_trb_virt_to_dma(priv_ep, trb));
>> +
>> +	trb = &priv_ep->trb_pool[priv_ep->enqueue];
>> +	ret += sprintf(str + ret,
>> +		       "\t\tRing enq index: %d, trb: %p (virt), 0x%llx (dma)\n",
>> +		       priv_ep->enqueue, trb,
>> +		       (unsigned long long)cdns3_trb_virt_to_dma(priv_ep, trb));
>> +
>> +	ret += sprintf(str + ret,
>> +		       "\t\tfree trbs: %d, CCS=%d, PCS=%d\n",
>> +		       priv_ep->free_trbs, priv_ep->ccs, priv_ep->pcs);
>> +
>> +	if (trb_per_sector > TRBS_PER_SEGMENT)
>> +		trb_per_sector = TRBS_PER_SEGMENT;
>> +
>> +	if (trb_per_sector > TRBS_PER_SEGMENT) {
>> +		sprintf(str + ret, "\t\tTo big transfer ring %d\n",
>> +			trb_per_sector);
>> +		return str;
>> +	}
>> +
>> +	for (i = 0; i < trb_per_sector; ++i) {
>> +		trb = &ring[i];
>> +		ret += sprintf(str + ret,
>> +			"\t\t@%pad %08x %08x %08x\n", &addr,
>> +			le32_to_cpu(trb->buffer),
>> +			le32_to_cpu(trb->length),
>> +			le32_to_cpu(trb->control));
>> +		addr += sizeof(*trb);
>> +	}
>> +
>> +	return str;
>> +}
>> +
>> +#ifdef CONFIG_DEBUG_FS
>> +void cdns3_debugfs_init(struct cdns3 *cdns);
>> +void cdns3_debugfs_exit(struct cdns3 *cdns);
>> +#else
>> +void cdns3_debugfs_init(struct cdns3 *cdns);
>> +{  }
>> +void cdns3_debugfs_exit(struct cdns3 *cdns);
>> +{  }
>> +#endif
>> +
>> +#endif /*__LINUX_CDNS3_DEBUG*/
>> diff --git a/drivers/usb/cdns3/debugfs.c b/drivers/usb/cdns3/debugfs.c
>> new file mode 100644
>> index 000000000000..d7919f5c1d90
>> --- /dev/null
>> +++ b/drivers/usb/cdns3/debugfs.c
>> @@ -0,0 +1,168 @@
>> +// SPDX-License-Identifier: GPL-2.0
>> +/*
>> + * Cadence USBSS DRD Controller DebugFS filer.
>> + *
>> + * Copyright (C) 2018 Cadence.
>> + *
>> + * Author: Pawel Laszczak <pawell@cadence.com>
>> + */
>> +
>> +#include <linux/types.h>
>> +#include <linux/debugfs.h>
>> +#include <linux/seq_file.h>
>> +#include <linux/uaccess.h>
>> +
>> +#include "core.h"
>> +#include "gadget.h"
>> +#include "drd.h"
>> +
>> +static int cdns3_mode_show(struct seq_file *s, void *unused)
>> +{
>> +	struct cdns3 *cdns = s->private;
>> +
>> +	switch (cdns->current_dr_mode) {
>> +	case USB_DR_MODE_HOST:
>> +		seq_puts(s, "host\n");
>> +		break;
>> +	case USB_DR_MODE_PERIPHERAL:
>> +		seq_puts(s, "device\n");
>> +		break;
>> +	case USB_DR_MODE_OTG:
>> +		seq_puts(s, "otg\n");
>> +		break;
>> +	default:
>> +		seq_puts(s, "UNKNOWN mode\n");
>> +	}
>> +
>> +	return 0;
>> +}
>> +
>> +static int cdns3_mode_open(struct inode *inode, struct file *file)
>> +{
>> +	return single_open(file, cdns3_mode_show, inode->i_private);
>> +}
>> +
>> +static ssize_t cdns3_mode_write(struct file *file,
>> +				const char __user *ubuf,
>> +				size_t count, loff_t *ppos)
>> +{
>> +	struct seq_file	 *s = file->private_data;
>> +	struct cdns3 *cdns = s->private;
>> +	u32 mode = USB_DR_MODE_UNKNOWN;
>> +	char buf[32];
>> +
>> +	if (copy_from_user(&buf, ubuf, min_t(size_t, sizeof(buf) - 1, count)))
>> +		return -EFAULT;
>> +
>> +	if (!strncmp(buf, "host", 4)) {
>> +		if (cdns->dr_mode == USB_DR_MODE_HOST ||
>> +		    cdns->dr_mode == USB_DR_MODE_OTG) {
>> +			mode = USB_DR_MODE_HOST;
>> +		}
>> +	}
>> +
>> +	if (!strncmp(buf, "device", 6))
>> +		if (cdns->dr_mode == USB_DR_MODE_PERIPHERAL ||
>> +		    cdns->dr_mode == USB_DR_MODE_OTG)
>> +			mode = USB_DR_MODE_PERIPHERAL;
>> +
>> +	if (!strncmp(buf, "otg", 3) && cdns->dr_mode == USB_DR_MODE_OTG)
>> +		mode = USB_DR_MODE_OTG;
>> +
>> +	if (mode == USB_DR_MODE_UNKNOWN) {
>> +		dev_err(cdns->dev, "Failed: incorrect mode setting\n");
>> +		return -EFAULT;
>> +	}
>> +
>> +	if (cdns->current_dr_mode != mode) {
>> +		cdns->desired_dr_mode = mode;
>> +		cdns->debug_disable = 0;
>> +		cdns3_role_stop(cdns);
>> +		cdns3_drd_update_mode(cdns);
>> +		queue_work(system_freezable_wq, &cdns->role_switch_wq);
>> +	}
>> +
>> +	return count;
>> +}
>> +
>> +static const struct file_operations cdns3_mode_fops = {
>> +	.open			= cdns3_mode_open,
>> +	.write			= cdns3_mode_write,
>> +	.read			= seq_read,
>> +	.llseek			= seq_lseek,
>> +	.release		= single_release,
>> +};
>> +
>> +static int cdns3_disable_show(struct seq_file *s, void *unused)
>> +{
>> +	struct cdns3 *cdns = s->private;
>> +
>> +	if (!cdns->debug_disable)
>> +		seq_puts(s, "0\n");
>> +	else
>> +		seq_puts(s, "1\n");
>> +
>> +	return 0;
>> +}
>> +
>> +static ssize_t cdns3_disable_write(struct file *file,
>> +				   const char __user *ubuf,
>> +				   size_t count, loff_t *ppos)
>> +{
>> +	struct seq_file	 *s = file->private_data;
>> +	struct cdns3 *cdns = s->private;
>> +	int disable;
>> +	char buf[32];
>> +
>> +	if (copy_from_user(&buf, ubuf, min_t(size_t, sizeof(buf) - 1, count)))
>> +		return -EFAULT;
>> +
>> +	if (!strncmp(buf, "1", 1) || !strncmp(buf, "yes", 3)) {
>> +		disable = 1;
>> +	} else if (!strncmp(buf, "0", 1) || !strncmp(buf, "no", 2)) {
>> +		disable = 0;
>> +	} else {
>> +		dev_err(cdns->dev, "Failed: incorrect disable setting\n");
>> +		return -EFAULT;
>> +	}
>> +
>There is common API kstrtobool()?
Ok, much simpler.
>
>> +	if (disable != cdns->debug_disable) {
>> +		cdns->debug_disable = disable;
>> +		queue_work(system_freezable_wq, &cdns->role_switch_wq);
>> +	}
>> +
>> +	return count;
>> +}
>> +
>> +static int cdns3_disable_open(struct inode *inode, struct file *file)
>> +{
>> +	return single_open(file, cdns3_disable_show, inode->i_private);
>> +}
>> +
>> +static const struct file_operations cdns3_disable_fops = {
>> +	.open			= cdns3_disable_open,
>> +	.write			= cdns3_disable_write,
>> +	.read			= seq_read,
>> +	.llseek			= seq_lseek,
>> +	.release		= single_release,
>> +};
>> +
>> +void cdns3_debugfs_init(struct cdns3 *cdns)
>> +{
>> +	struct dentry *root;
>> +
>> +	root = debugfs_create_dir(dev_name(cdns->dev), NULL);
>> +	cdns->root = root;
>> +	if (IS_ENABLED(CONFIG_USB_CDNS3_GADGET) &&
>> +	    IS_ENABLED(CONFIG_USB_CDNS3_HOST))
>> +		debugfs_create_file("mode", 0644, root, cdns,
>> +				    &cdns3_mode_fops);
>> +
>> +	debugfs_create_file("disable", 0644, root, cdns,
>> +			    &cdns3_disable_fops);
>> +}
>> +
>> +void cdns3_debugfs_exit(struct cdns3 *cdns)
>> +{
>> +	debugfs_remove_recursive(cdns->root);
>> +}
>> diff --git a/drivers/usb/cdns3/drd.c b/drivers/usb/cdns3/drd.c
>> new file mode 100644
>> index 000000000000..b0c32302eb0b
>> --- /dev/null
>> +++ b/drivers/usb/cdns3/drd.c
>> @@ -0,0 +1,350 @@
>> +// SPDX-License-Identifier: GPL-2.0
>> +/*
>> + * Cadence USBSS DRD Driver.
>> + *
>> + * Copyright (C) 2018 Cadence.
>> + *
>> + * Author: Pawel Laszczak <pawell@cadence.com
>> + *
>> + */
>> +#include <linux/kernel.h>
>> +#include <linux/interrupt.h>
>> +#include <linux/delay.h>
>> +#include <linux/usb/otg.h>
>> +
>> +#include "gadget.h"
>> +#include "drd.h"
>> +#include "core.h"
>> +
>> +static int cdns3_drd_switch_gadget(struct cdns3 *cdns, int on);
>> +static int cdns3_drd_switch_host(struct cdns3 *cdns, int on);
>> +
>> +/**
>> + * cdns3_set_mode - change mode of OTG Core
>> + * @cdns: pointer to context structure
>> + * @mode: selected mode from cdns_role
>> + */
>> +void cdns3_set_mode(struct cdns3 *cdns, enum usb_dr_mode mode)
>> +{
>> +	u32 reg;
>> +
>> +	cdns->current_dr_mode = mode;
>> +
>> +	switch (mode) {
>> +	case USB_DR_MODE_PERIPHERAL:
>> +		dev_info(cdns->dev, "Set controller to Gadget mode\n");
>> +		cdns3_drd_switch_gadget(cdns, 1);
>> +		break;
>> +	case USB_DR_MODE_HOST:
>> +		dev_info(cdns->dev, "Set controller to Host mode\n");
>> +		cdns3_drd_switch_host(cdns, 1);
>> +		break;
>> +	case USB_DR_MODE_OTG:
>> +		dev_info(cdns->dev, "Set controller to OTG mode\n");
>> +		if (cdns->version == CDNS3_CONTROLLER_V1) {
>> +			reg = readl(&cdns->otg_v1_regs->override);
>> +			reg |= OVERRIDE_IDPULLUP;
>> +			writel(reg, &cdns->otg_v1_regs->override);
>> +		} else {
>> +			reg = readl(&cdns->otg_v0_regs->ctrl1);
>> +			reg |= OVERRIDE_IDPULLUP_V0;
>> +			writel(reg, &cdns->otg_v0_regs->ctrl1);
>> +		}
>> +
>> +		/*
>> +		 * Hardware specification says: "ID_VALUE must be valid within
>> +		 * 50ms after idpullup is set to '1" so driver must wait
>> +		 * 50ms before reading this pin.
>> +		 */
>> +		usleep_range(50000, 60000);
>> +		break;
>> +	default:
>> +		cdns->current_dr_mode = USB_DR_MODE_UNKNOWN;
>> +		dev_err(cdns->dev, "Unsupported mode of operation %d\n", mode);
>> +		return;
>> +	}
>> +}
>> +
>> +int cdns3_get_id(struct cdns3 *cdns)
>> +{
>> +	int id;
>> +
>> +	id = readl(&cdns->otg_regs->sts) & OTGSTS_ID_VALUE;
>> +	dev_dbg(cdns->dev, "OTG ID: %d", id);
>> +	return  1 ; //id;
>return fixed value ?

My mistake. I forgot to restore this.  
It should be "return id" .
I thought that checkpatch remained me about this :). 
Thanks. 

>> +}
>> +
>> +int cdns3_is_host(struct cdns3 *cdns)
>> +{
>> +	if (cdns->current_dr_mode == USB_DR_MODE_HOST)
>> +		return 1;
>> +	else if (!cdns3_get_id(cdns))
>> +		return 1;
>> +
>> +	return 0;
>> +}
>> +
>> +
>
>> +
>> +/**
>> + * cdns3_init_otg_mode - initialize drd controller
>> + * @cdns: Pointer to controller context structure
>> + *
>> + * Returns 0 on success otherwise negative errno
>void function
It should be int. 
>> + */
>> +static void cdns3_init_otg_mode(struct cdns3 *cdns)
>> +{
>> +	cdns3_otg_disable_irq(cdns);
>> +	/* clear all interrupts */
>> +	writel(~0, &cdns->otg_regs->ivect);
>> +
>> +	cdns3_set_mode(cdns, USB_DR_MODE_OTG);
>> +
>> +	if (cdns3_is_host(cdns))
>> +		cdns3_drd_switch_host(cdns, 1);
>> +	else
>> +		cdns3_drd_switch_gadget(cdns, 1);
>> +
>> +	cdns3_otg_enable_irq(cdns);
>> +}
>> +
>> +/**
>> + * cdns3_drd_update_mode - initialize mode of operation
>> + * @cdns: Pointer to controller context structure
>> + *
>> + * Returns 0 on success otherwise negative errno
>> + */
>> +int cdns3_drd_update_mode(struct cdns3 *cdns)
>> +{
>> +	int ret = 0;
>> +
>> +	if (cdns->desired_dr_mode == cdns->current_dr_mode)
>> +		return ret;
>> +
>> +	cdns3_drd_switch_gadget(cdns, 0);
>> +	cdns3_drd_switch_host(cdns, 0);
>> +
>> +	switch (cdns->desired_dr_mode) {
>> +	case USB_DR_MODE_PERIPHERAL:
>> +		cdns3_set_mode(cdns, USB_DR_MODE_PERIPHERAL);
>> +		break;
>> +	case USB_DR_MODE_HOST:
>> +		cdns3_set_mode(cdns, USB_DR_MODE_HOST);
>> +		break;
>> +	case USB_DR_MODE_OTG:
>> +		cdns3_init_otg_mode(cdns);
>> +		break;
>> +	default:
>> +		dev_err(cdns->dev, "Unsupported mode of operation %d\n",
>> +			cdns->dr_mode);
>> +		return -EINVAL;
>> +	}
>> +
>> +	return ret;
>no need define local variable @ret

It will keep return value from cdns3_set_mode and cdns3_init_otg_mode functions.
>> +}
>> +
>> +/**
>> + * cdns3_drd_irq - interrupt handler for OTG events
>> + *
>> + * @irq: irq number for cdns3 core device
>> + * @data: structure of cdns3
>> + *
>> + * Returns IRQ_HANDLED or IRQ_NONE
>> + */
>> +static irqreturn_t cdns3_drd_irq(int irq, void *data)
>> +{
>> +	irqreturn_t ret = IRQ_NONE;
>> +	struct cdns3 *cdns = data;
>> +	u32 reg;
>> +
>> +	if (cdns->dr_mode != USB_DR_MODE_OTG)
>> +		return ret;
>> +
>> +	reg = readl(&cdns->otg_regs->ivect);
>> +
>> +	if (!reg)
>> +		return ret;
>> +
>> +	if (reg & OTGIEN_ID_CHANGE_INT) {
>> +		dev_dbg(cdns->dev, "OTG IRQ: new ID: %d\n",
>> +			cdns3_get_id(cdns));
>> +
>> +		queue_work(system_freezable_wq, &cdns->role_switch_wq);
>> +
>do you need to clear interrupts status here?
It's the best place for clearing interrupt. Driver enables only detection id changes. 
We can read ID status also from OTG status register. Even if driver lost some events it will 
not be a problem.  

>> +		ret = IRQ_HANDLED;
>> +	}
>> +
>> +	writel(~0, &cdns->otg_regs->ivect);
>> +	return ret;
>> +}
>> +
>> +int cdns3_drd_init(struct cdns3 *cdns)
>> +{
>> +	void __iomem *regs;
>> +	int ret = 0;
>> +	u32 state;
>> +
>> +	regs = devm_ioremap_resource(cdns->dev, &cdns->otg_res);
>> +	if (IS_ERR(regs))
>> +		return PTR_ERR(regs);
>> +
>> +	/* Detection of DRD version. Controller has been released
>> +	 * in two versions. Both are similar, but they have same changes
>> +	 * in register maps.
>> +	 * The first register in old version is command register and it's read
>> +	 * only, so driver should read 0 from it. On the other hand, in v1
>> +	 * the first register contains device ID number which is not set to 0.
>> +	 * Driver uses this fact to detect the proper version of
>> +	 * controller.
>> +	 */
>> +	cdns->otg_v0_regs = regs;
>> +	if (!readl(&cdns->otg_v0_regs->cmd)) {
>> +		cdns->version  = CDNS3_CONTROLLER_V0;
>> +		cdns->otg_v1_regs = NULL;
>> +		cdns->otg_regs = regs;
>> +		dev_info(cdns->dev, "DRD version v0 (%08x)\n",
>> +			 readl(&cdns->otg_v0_regs->version));
>> +	} else {
>> +		cdns->otg_v0_regs = NULL;
>> +		cdns->otg_v1_regs = regs;
>> +		cdns->otg_regs = (void *)&cdns->otg_v1_regs->cmd;
>> +		cdns->version  = CDNS3_CONTROLLER_V1;
>> +		dev_info(cdns->dev, "DRD version v1 (ID: %08x, rev: %08x)\n",
>> +			 readl(&cdns->otg_v1_regs->did),
>> +			 readl(&cdns->otg_v1_regs->rid));
>> +	}
>> +
>> +	state = OTGSTS_STRAP(readl(&cdns->otg_regs->sts));
>> +
>> +	/* Update dr_mode according to STRAP configuration. */
>> +	cdns->dr_mode = USB_DR_MODE_OTG;
>> +	if (state == OTGSTS_STRAP_HOST) {
>> +		dev_info(cdns->dev, "Controller strapped to HOST\n");
>> +		cdns->dr_mode = USB_DR_MODE_HOST;
>> +	} else if (state == OTGSTS_STRAP_GADGET) {
>> +		dev_info(cdns->dev, "Controller strapped to PERIPHERAL\n");
>> +		cdns->dr_mode = USB_DR_MODE_PERIPHERAL;
>> +	}
>> +
>> +	cdns->desired_dr_mode = cdns->dr_mode;
>> +	cdns->current_dr_mode = USB_DR_MODE_UNKNOWN;
>> +
>> +	ret = devm_request_threaded_irq(cdns->dev, cdns->irq, cdns3_drd_irq,
>> +					NULL, IRQF_SHARED,
>> +					dev_name(cdns->dev), cdns);
>> +
>> +	if (ret)
>> +		return ret;
>> +
>> +	state = readl(&cdns->otg_regs->sts);
>> +	if (OTGSTS_OTG_NRDY(state) != 0) {
>> +		dev_err(cdns->dev, "Cadence USB3 OTG device not ready\n");
>> +		return -ENODEV;
>> +	}
>> +
>> +	ret = cdns3_drd_update_mode(cdns);
>> +
>> +	return ret;
>return cdns3_drd_update_mode();
>> +}
>> +
>> +int cdns3_drd_exit(struct cdns3 *cdns)
>> +{
>> +	return cdns3_drd_switch_host(cdns, 0);
>> +}
>
>
><...>
>
>> diff --git a/drivers/usb/cdns3/ep0.c b/drivers/usb/cdns3/ep0.c
>> new file mode 100644
>> index 000000000000..89cf1cde1555
>> --- /dev/null
>> +++ b/drivers/usb/cdns3/ep0.c
>> @@ -0,0 +1,896 @@
>> +// SPDX-License-Identifier: GPL-2.0
>> +/*
>> + * Cadence USBSS DRD Driver - gadget side.
>> + *
>> + * Copyright (C) 2018 Cadence Design Systems.
>> + * Copyright (C) 2017-2018 NXP
>> + *
>> + * Authors: Pawel Jez <pjez@cadence.com>,
>> + *          Pawel Laszczak <pawell@cadence.com>
>> + *	    Peter Chen <peter.chen@nxp.com>
>> + */
>> +
>> +#include <linux/usb/composite.h>
>> +
>> +#include "gadget.h"
>> +#include "trace.h"
>> +
>> +static struct usb_endpoint_descriptor cdns3_gadget_ep0_desc = {
>> +	.bLength = USB_DT_ENDPOINT_SIZE,
>> +	.bDescriptorType = USB_DT_ENDPOINT,
>> +	.bmAttributes =	USB_ENDPOINT_XFER_CONTROL,
>> +};
>> +
><...>
>> +
>> +
>> +#endif /* __LINUX_CDNS3_GADGET_EXPORT */
>> diff --git a/drivers/usb/cdns3/gadget.c b/drivers/usb/cdns3/gadget.c
>> new file mode 100644
>> index 000000000000..0d95eb00be37
>> --- /dev/null
>> +++ b/drivers/usb/cdns3/gadget.c
>> @@ -0,0 +1,2102 @@
>> +// SPDX-License-Identifier: GPL-2.0
>> +/*
>> + * Cadence USBSS DRD Driver - gadget side.
>> + *
>> + * Copyright (C) 2018 Cadence Design Systems.
>> + * Copyright (C) 2017-2018 NXP
>> + *
>> + * Authors: Pawel Jez <pjez@cadence.com>,
>> + *          Pawel Laszczak <pawell@cadence.com>
>> + *	    Peter Chen <peter.chen@nxp.com>
>> + */
>> +
>> +#include <linux/dma-mapping.h>
>> +#include <linux/usb/gadget.h>
>> +
><...>
>
>> +/**
>> + * cdns3_next_request - returns next request from list
>> + * @list: list containing requests
>> + *
>> + * Returns request or NULL if no requests in list
>> + */
>> +struct usb_request *cdns3_next_request(struct list_head *list)
>> +{
>> +	if (list_empty(list))
>> +		return NULL;
>No need check it, if list is empty, list_first_entry_or_null() will
>return NULL
Ok, 

>> +	return list_first_entry_or_null(list, struct usb_request, list);
>> +}
>> +
>> +/**
>> + * cdns3_next_priv_request - returns next request from list
>> + * @list: list containing requests
>> + *
>> + * Returns request or NULL if no requests in list
>> + */
>> +struct cdns3_request *cdns3_next_priv_request(struct list_head *list)
>> +{
>> +	if (list_empty(list))
>> +		return NULL;
>ditto
>> +	return list_first_entry_or_null(list, struct cdns3_request, list);
>> +}
>> +
>
><...>
>
Thanks for comments.
Cheers,
Pawel

^ permalink raw reply	[flat|nested] 49+ messages in thread

* Re: [PATCH v2 0/5] Introduced new Cadence USBSS DRD Driver.
  2018-12-23 15:13 ` Pawel Laszczak
                   ` (5 preceding siblings ...)
  (?)
@ 2019-01-02  6:44 ` Peter Chen
  -1 siblings, 0 replies; 49+ messages in thread
From: Peter Chen @ 2019-01-02  6:44 UTC (permalink / raw)
  To: Pawel Laszczak
  Cc: devicetree, Greg Kroah-Hartman, mark.rutland, linux-usb,
	hdegoede, Heikki Krogerus, andy.shevchenko, robh+dt, rogerq,
	lkml, adouglas, jbergsagel, nsekhar, nm, sureshp, peter.chen,
	pjez, kurahul

[-- Attachment #1: Type: text/plain, Size: 4629 bytes --]

>  - Improved algorithm fixing hardware issue related to blocking endpoints.
>    This issue is related to on-chip shared FIFO buffers for OUT packets.
>    Problem was reported by Peter Chan.

Pawel, I suggest not adding this workaround in this initial version
for Cadence USB3 driver,
we can add it later as patch, it can keep driver more simple and
clean, this WA only affects
a few of use cases. and I suggest adding flag for platform driver to
enable or disable this WA.

Besides, I begin to debug gadget mode based on your version, and find
some issues.
I use mass storage gadget to debug, and at host side, I use Bonnie++
for mounted folder.
Eg, bonnie\+\+ -d /mnt/disk -u 0:0 -s 256 -r 128

One is related to this workaround, you may improve it later. At
failure case, the class driver
has two OUT requests, one is 16384 bytes, and another is 4096 bytes.
The host sends the
data before class driver adding request, and the controller driver
only giveback 15872 bytes
for two requests. It seems the internal two requests (2 * 2048 bytes)
for DESCMISS doesn't
give the data to class driver. I attached the trace for you reference,
and dequeue occurs at
line 600.

Another issue is the interrupt (IOC, TRBERR, etc) can't occur during
the transfer, it can be improved
after introduce another WA (issue caused by simultaneous access to TRB
both by SW and HW).

Would you please have a look?

Peter

>  - Changed organization of endpoint array in cdns3_device object.
>       - added ep0 to common eps array
>       - removed cdns3_free_trb_pool and cdns3_ep_addr_to_bit_pos macros.
>       - removed ep0_trb_dma, ep0_trb fields from cdns3_device.
>  - Removed ep0_request and ep_nums fields from cdns3_device.
>  - Other minor changes according with Felipe suggestion.
>
> ---
>
> Pawel Laszczak (5):
>   dt-bindings: add binding for USBSS-DRD controller.
>   usb:common Separated decoding functions from dwc3 driver.
>   usb:common Patch simplify usb_decode_set_clear_feature function.
>   usb:common Simplify usb_decode_get_set_descriptor function.
>   usb:cdns3 Add Cadence USB3 DRD Driver
>
>  .../devicetree/bindings/usb/cdns3-usb.txt     |   30 +
>  drivers/usb/Kconfig                           |    2 +
>  drivers/usb/Makefile                          |    2 +
>  drivers/usb/cdns3/Kconfig                     |   44 +
>  drivers/usb/cdns3/Makefile                    |   16 +
>  drivers/usb/cdns3/cdns3-pci-wrap.c            |  157 ++
>  drivers/usb/cdns3/core.c                      |  406 ++++
>  drivers/usb/cdns3/core.h                      |  116 +
>  drivers/usb/cdns3/debug.h                     |  166 ++
>  drivers/usb/cdns3/debugfs.c                   |  168 ++
>  drivers/usb/cdns3/drd.c                       |  350 +++
>  drivers/usb/cdns3/drd.h                       |  162 ++
>  drivers/usb/cdns3/ep0.c                       |  896 +++++++
>  drivers/usb/cdns3/gadget-export.h             |   28 +
>  drivers/usb/cdns3/gadget.c                    | 2102 +++++++++++++++++
>  drivers/usb/cdns3/gadget.h                    | 1206 ++++++++++
>  drivers/usb/cdns3/host-export.h               |   28 +
>  drivers/usb/cdns3/host.c                      |   72 +
>  drivers/usb/cdns3/trace.c                     |   11 +
>  drivers/usb/cdns3/trace.h                     |  389 +++
>  drivers/usb/common/Makefile                   |    2 +-
>  drivers/usb/common/debug.c                    |  265 +++
>  drivers/usb/dwc3/debug.h                      |  243 --
>  drivers/usb/dwc3/trace.h                      |    2 +-
>  include/linux/usb/ch9.h                       |   19 +
>  25 files changed, 6637 insertions(+), 245 deletions(-)
>  create mode 100644 Documentation/devicetree/bindings/usb/cdns3-usb.txt
>  create mode 100644 drivers/usb/cdns3/Kconfig
>  create mode 100644 drivers/usb/cdns3/Makefile
>  create mode 100644 drivers/usb/cdns3/cdns3-pci-wrap.c
>  create mode 100644 drivers/usb/cdns3/core.c
>  create mode 100644 drivers/usb/cdns3/core.h
>  create mode 100644 drivers/usb/cdns3/debug.h
>  create mode 100644 drivers/usb/cdns3/debugfs.c
>  create mode 100644 drivers/usb/cdns3/drd.c
>  create mode 100644 drivers/usb/cdns3/drd.h
>  create mode 100644 drivers/usb/cdns3/ep0.c
>  create mode 100644 drivers/usb/cdns3/gadget-export.h
>  create mode 100644 drivers/usb/cdns3/gadget.c
>  create mode 100644 drivers/usb/cdns3/gadget.h
>  create mode 100644 drivers/usb/cdns3/host-export.h
>  create mode 100644 drivers/usb/cdns3/host.c
>  create mode 100644 drivers/usb/cdns3/trace.c
>  create mode 100644 drivers/usb/cdns3/trace.h
>  create mode 100644 drivers/usb/common/debug.c
>
> --
> 2.17.1
>

[-- Attachment #2: descmiss_error.log --]
[-- Type: application/octet-stream, Size: 149655 bytes --]

 irq/408-5b13000-1696  [002] d..1   325.740865: cdns3_epx_irq: IRQ for ep1in: 0100088c IOC ISP TRBERR , ep_traddr: 960
02198
 irq/408-5b13000-1696  [002] d..1   325.740869: cdns3_request_handled: Req: ffff800836bfc900 handled, DMA pos: 34, ep 
deq: 32, ep enq: 34, start trb: 32, end trb: 32
 irq/408-5b13000-1696  [002] d..1   325.740870: cdns3_complete_trb: ep1in: trb ffff00000a52f180, dma buf: 0xfcac2800, 
size: 512, ctrl: 0x00000425 (C=1, T=0, ISP, IOC, Normal)
 irq/408-5b13000-1696  [002] d..1   325.740873: cdns3_gadget_giveback: ep1in: req: ffff800836bfc900, length: 512/512 ,
 status: 0, trb: [start:32, end:32: virt addr ffff00000a52f180], flags:0 
 irq/408-5b13000-1696  [002] d..1   325.740883: cdns3_request_handled: Req: ffff800836bfc800 handled, DMA pos: 34, ep 
deq: 33, ep enq: 34, start trb: 33, end trb: 33
 irq/408-5b13000-1696  [002] d..1   325.740884: cdns3_complete_trb: ep1in: trb ffff00000a52f18c, dma buf: 0xfcac3000, 
size: 13, ctrl: 0x00000425 (C=1, T=0, ISP, IOC, Normal)
 irq/408-5b13000-1696  [002] d..1   325.740888: cdns3_gadget_giveback: ep1in: req: ffff800836bfc800, length: 13/13 , s
tatus: 0, trb: [start:33, end:33: virt addr ffff00000a52f18c], flags:0 
    file-storage-3834  [003] d..1   325.740889: cdns3_ep_queue: ep1out: req: ffff800836bfcd00, length: 0/1024 , status
: -115, trb: [start:37, end:37: virt addr ffff00000a5301bc], flags:0 
    file-storage-3834  [003] d..1   325.740893: cdns3_prepare_trb: ep1out: trb ffff00000a5301c8, dma buf: 0xfcac3800, 
size: 1024, ctrl: 0x00000425 (C=1, T=0, ISP, IOC, Normal)
    file-storage-3834  [003] d..1   325.740896: cdns3_ring: 
                Ring contents for ep1out:
                Ring deq index: 38, trb: ffff00000a5301c8 (virt), 0x960031c8 (dma)
                Ring enq index: 0, trb: ffff00000a530000 (virt), 0x96003000 (dma)
                free trbs: 38, CCS=1, PCS=0
                @0x0000000096003000 fca8a800 0000001f 00000425
                @0x000000009600300c fca8c000 0000001f 00000425
                @0x0000000096003018 fca8d800 0000001f 00000425
                @0x0000000096003024 fca8f000 0000001f 00000425
                @0x0000000096003030 fca90800 0000001f 00000425
                @0x000000009600303c fca92000 0000001f 00000425
                @0x0000000096003048 fca93800 0000001f 00000425
                @0x0000000096003054 fca95000 0000001f 00000425
                @0x0000000096003060 fca96800 0000001f 00000425
                @0x000000009600306c fca98000 0000001f 00000425
                @0x0000000096003078 fca99800 0000001f 00000425
                @0x0000000096003084 fca9b000 0000001f 00000425
                @0x0000000096003090 fca9c800 0000001f 00000425
                @0x000000009600309c fca9e000 0000001f 00000425
                @0x00000000960030a8 fca9f800 0000001f 00000425
                @0x00000000960030b4 fcaa1000 0000001f 00000425
                @0x00000000960030c0 fcaa2800 0000001f 00000425
                @0x00000000960030cc fcaa4000 0000001f 00000425
                @0x00000000960030d8 fcaa5800 0000001f 00000425
                @0x00000000960030e4 fcaa7000 0000001f 00000425
                @0x00000000960030f0 fcaa8800 0000001f 00000425
                @0x00000000960030fc fcaaa000 0000001f 00000425
                @0x0000000096003108 fcaab800 0000001f 00000425
                @0x0000000096003114 fcaad000 0000001f 00000425
                @0x0000000096003120 fcaae800 0000001f 00000425
                @0x000000009600312c fcab0000 0000001f 00000425
                @0x0000000096003138 fcab1800 0000001f 00000425
                @0x0000000096003144 fcab3000 0000001f 00000425
                @0x0000000096003150 fcab4800 0000001f 00000425
                @0x000000009600315c fcab6000 0000001f 00000425
                @0x0000000096003168 fcab7800 0000001f 00000425
                @0x0000000096003174 fcab9000 0000001f 00000425
                @0x0000000096003180 fcaba800 0000001f 00000425
                @0x000000009600318c fcabc000 0000001f 00000425
                @0x0000000096003198 fcabd800 0000001f 00000425
                @0x00000000960031a4 fcabf000 0000001f 00000425
                @0x00000000960031b0 fcac1000 0000001f 00000425
                @0x00000000960031bc fcac2000 0000001f 00000425
                @0x00000000960031c8 fcac3800 00000400 00000425
                @0x00000000960031d4 96003000 00000000 00001813

    file-storage-3834  [003] d..1   325.740905: cdns3_doorbell_epx: //Ding Dong ep1out, ep_trbaddr 960031c8
 irq/408-5b13000-1696  [002] d..1   325.791428: cdns3_epx_irq: IRQ for ep1out: 01000c08 ISP , ep_traddr: 960031d4
 irq/408-5b13000-1696  [002] d..1   325.791433: cdns3_request_handled: Req: ffff800836bfcd00 handled, DMA pos: 39, ep 
deq: 38, ep enq: 0, start trb: 38, end trb: 38
 irq/408-5b13000-1696  [002] d..1   325.791435: cdns3_complete_trb: ep1out: trb ffff00000a5301c8, dma buf: 0xfcac3800,
 size: 31, ctrl: 0x00000425 (C=1, T=0, ISP, IOC, Normal)
 irq/408-5b13000-1696  [002] d..1   325.791439: cdns3_gadget_giveback: ep1out: req: ffff800836bfcd00, length: 31/1024 
, status: 0, trb: [start:38, end:38: virt addr ffff00000a5301c8], flags:0 
    file-storage-3834  [003] d..1   325.791464: cdns3_ep_queue: ep1in: req: ffff800836bfc900, length: 0/512 , status: 
-115, trb: [start:32, end:32: virt addr ffff00000a52f180], flags:0 
    file-storage-3834  [003] d..1   325.791468: cdns3_prepare_trb: ep1in: trb ffff00000a52f198, dma buf: 0xfcac4000, s
ize: 512, ctrl: 0x00000425 (C=1, T=0, ISP, IOC, Normal)
    file-storage-3834  [003] d..1   325.791472: cdns3_ring: 
                Ring contents for ep1in:
                Ring deq index: 34, trb: ffff00000a52f198 (virt), 0x96002198 (dma)
                Ring enq index: 35, trb: ffff00000a52f1a4 (virt), 0x960021a4 (dma)
                free trbs: 38, CCS=1, PCS=1
                @0x0000000096002000 fcaa9800 0000000d 00000425
                @0x000000009600200c fcaaa800 00000200 00000425
                @0x0000000096002018 fcaab000 0000000d 00000425
                @0x0000000096002024 fcaac000 00000200 00000425
                @0x0000000096002030 fcaac800 0000000d 00000425
                @0x000000009600203c fcaad800 00000200 00000425
                @0x0000000096002048 fcaae000 0000000d 00000425
                @0x0000000096002054 fcaaf000 00000200 00000425
                @0x0000000096002060 fcaaf800 0000000d 00000425
                @0x000000009600206c fcab0800 00000200 00000425
                @0x0000000096002078 fcab1000 0000000d 00000425
                @0x0000000096002084 fcab2000 00000200 00000425
                @0x0000000096002090 fcab2800 0000000d 00000425
                @0x000000009600209c fcab3800 00000200 00000425
                @0x00000000960020a8 fcab4000 0000000d 00000425
                @0x00000000960020b4 fcab5000 00000200 00000425
                @0x00000000960020c0 fcab5800 0000000d 00000425
                @0x00000000960020cc fcab6800 00000200 00000425
                @0x00000000960020d8 fcab7000 0000000d 00000425
                @0x00000000960020e4 fcab8000 00000200 00000425
                @0x00000000960020f0 fcab8800 0000000d 00000425
                @0x00000000960020fc fcab9800 00000200 00000425
                @0x0000000096002108 fcaba000 0000000d 00000425
                @0x0000000096002114 fcabb000 00000200 00000425
                @0x0000000096002120 fcabb800 0000000d 00000425
                @0x000000009600212c fcabc800 00000200 00000425
                @0x0000000096002138 fcabd000 0000000d 00000425
                @0x0000000096002144 fcabe000 00000200 00000425
                @0x0000000096002150 fcabe800 0000000d 00000425
                @0x000000009600215c fcac0000 00000200 00000425
                @0x0000000096002168 fcac0800 0000000d 00000425
                @0x0000000096002174 fcac1800 0000000d 00000425
                @0x0000000096002180 fcac2800 00000200 00000425
                @0x000000009600218c fcac3000 0000000d 00000425
                @0x0000000096002198 fcac4000 00000200 00000425
                @0x00000000960021a4 fcaa6800 0000000d 00000424
                @0x00000000960021b0 fcaa7800 00000200 00000424
                @0x00000000960021bc fcaa8000 0000000d 00000424
                @0x00000000960021c8 fcaa9000 00000200 00000424
                @0x00000000960021d4 96002000 00000000 00001812

    file-storage-3834  [003] d..1   325.791480: cdns3_doorbell_epx: //Ding Dong ep1in, ep_trbaddr 96002198
    file-storage-3834  [003] d..1   325.791483: cdns3_ep_queue: ep1in: req: ffff800836bfc800, length: 0/13 , status: -
115, trb: [start:33, end:33: virt addr ffff00000a52f18c], flags:0 
    file-storage-3834  [003] d..1   325.791486: cdns3_prepare_trb: ep1in: trb ffff00000a52f1a4, dma buf: 0xfcac4800, s
ize: 13, ctrl: 0x00000425 (C=1, T=0, ISP, IOC, Normal)
    file-storage-3834  [003] d..1   325.791489: cdns3_ring: 
                Ring contents for ep1in:
                Ring deq index: 34, trb: ffff00000a52f198 (virt), 0x96002198 (dma)
                Ring enq index: 36, trb: ffff00000a52f1b0 (virt), 0x960021b0 (dma)
                free trbs: 37, CCS=1, PCS=1
                @0x0000000096002000 fcaa9800 0000000d 00000425
                @0x000000009600200c fcaaa800 00000200 00000425
                @0x0000000096002018 fcaab000 0000000d 00000425
                @0x0000000096002024 fcaac000 00000200 00000425
                @0x0000000096002030 fcaac800 0000000d 00000425
                @0x000000009600203c fcaad800 00000200 00000425
                @0x0000000096002048 fcaae000 0000000d 00000425
                @0x0000000096002054 fcaaf000 00000200 00000425
                @0x0000000096002060 fcaaf800 0000000d 00000425
                @0x000000009600206c fcab0800 00000200 00000425
                @0x0000000096002078 fcab1000 0000000d 00000425
                @0x0000000096002084 fcab2000 00000200 00000425
                @0x0000000096002090 fcab2800 0000000d 00000425
                @0x000000009600209c fcab3800 00000200 00000425
                @0x00000000960020a8 fcab4000 0000000d 00000425
                @0x00000000960020b4 fcab5000 00000200 00000425
                @0x00000000960020c0 fcab5800 0000000d 00000425
                @0x00000000960020cc fcab6800 00000200 00000425
                @0x00000000960020d8 fcab7000 0000000d 00000425
                @0x00000000960020e4 fcab8000 00000200 00000425
                @0x00000000960020f0 fcab8800 0000000d 00000425
                @0x00000000960020fc fcab9800 00000200 00000425
                @0x0000000096002108 fcaba000 0000000d 00000425
                @0x0000000096002114 fcabb000 00000200 00000425
                @0x0000000096002120 fcabb800 0000000d 00000425
                @0x000000009600212c fcabc800 00000200 00000425
                @0x0000000096002138 fcabd000 0000000d 00000425
                @0x0000000096002144 fcabe000 00000200 00000425
                @0x0000000096002150 fcabe800 0000000d 00000425
                @0x000000009600215c fcac0000 00000200 00000425
                @0x0000000096002168 fcac0800 0000000d 00000425
                @0x0000000096002174 fcac1800 0000000d 00000425
                @0x0000000096002180 fcac2800 00000200 00000425
                @0x000000009600218c fcac3000 0000000d 00000425
                @0x0000000096002198 fcac4000 00000200 00000425
                @0x00000000960021a4 fcac4800 0000000d 00000425
                @0x00000000960021b0 fcaa7800 00000200 00000424
                @0x00000000960021bc fcaa8000 0000000d 00000424
                @0x00000000960021c8 fcaa9000 00000200 00000424
                @0x00000000960021d4 96002000 00000000 00001812

    file-storage-3834  [003] d..1   325.791496: cdns3_doorbell_epx: //Ding Dong ep1in, ep_trbaddr 960021a4
 irq/408-5b13000-1696  [002] d..1   325.791510: cdns3_epx_irq: IRQ for ep1in: 0100088c IOC ISP TRBERR , ep_traddr: 960
021b0
 irq/408-5b13000-1696  [002] d..1   325.791516: cdns3_request_handled: Req: ffff800836bfc900 handled, DMA pos: 36, ep 
deq: 34, ep enq: 36, start trb: 34, end trb: 34
 irq/408-5b13000-1696  [002] d..1   325.791518: cdns3_complete_trb: ep1in: trb ffff00000a52f198, dma buf: 0xfcac4000, 
size: 512, ctrl: 0x00000425 (C=1, T=0, ISP, IOC, Normal)
 irq/408-5b13000-1696  [002] d..1   325.791521: cdns3_gadget_giveback: ep1in: req: ffff800836bfc900, length: 512/512 ,
 status: 0, trb: [start:34, end:34: virt addr ffff00000a52f198], flags:0 
 irq/408-5b13000-1696  [002] d..1   325.791530: cdns3_request_handled: Req: ffff800836bfc800 handled, DMA pos: 36, ep 
deq: 35, ep enq: 36, start trb: 35, end trb: 35
 irq/408-5b13000-1696  [002] d..1   325.791531: cdns3_complete_trb: ep1in: trb ffff00000a52f1a4, dma buf: 0xfcac4800, 
size: 13, ctrl: 0x00000425 (C=1, T=0, ISP, IOC, Normal)
 irq/408-5b13000-1696  [002] d..1   325.791534: cdns3_gadget_giveback: ep1in: req: ffff800836bfc800, length: 13/13 , s
tatus: 0, trb: [start:35, end:35: virt addr ffff00000a52f1a4], flags:0 
    file-storage-3834  [003] d..1   325.791536: cdns3_ep_queue: ep1out: req: ffff800836bfcd00, length: 0/1024 , status
: -115, trb: [start:38, end:38: virt addr ffff00000a5301c8], flags:0 
    file-storage-3834  [003] d..1   325.791540: cdns3_prepare_trb: ep1out: trb ffff00000a530000, dma buf: 0xfcac5000, 
size: 1024, ctrl: 0x00000424 (C=0, T=0, ISP, IOC, Normal)
    file-storage-3834  [003] d..1   325.791543: cdns3_ring: 
                Ring contents for ep1out:
                Ring deq index: 0, trb: ffff00000a530000 (virt), 0x96003000 (dma)
                Ring enq index: 1, trb: ffff00000a53000c (virt), 0x9600300c (dma)
                free trbs: 38, CCS=0, PCS=0
                @0x0000000096003000 fcac5000 00000400 00000424
                @0x000000009600300c fca8c000 0000001f 00000425
                @0x0000000096003018 fca8d800 0000001f 00000425
                @0x0000000096003024 fca8f000 0000001f 00000425
                @0x0000000096003030 fca90800 0000001f 00000425
                @0x000000009600303c fca92000 0000001f 00000425
                @0x0000000096003048 fca93800 0000001f 00000425
                @0x0000000096003054 fca95000 0000001f 00000425
                @0x0000000096003060 fca96800 0000001f 00000425
                @0x000000009600306c fca98000 0000001f 00000425
                @0x0000000096003078 fca99800 0000001f 00000425
                @0x0000000096003084 fca9b000 0000001f 00000425
                @0x0000000096003090 fca9c800 0000001f 00000425
                @0x000000009600309c fca9e000 0000001f 00000425
                @0x00000000960030a8 fca9f800 0000001f 00000425
                @0x00000000960030b4 fcaa1000 0000001f 00000425
                @0x00000000960030c0 fcaa2800 0000001f 00000425
                @0x00000000960030cc fcaa4000 0000001f 00000425
                @0x00000000960030d8 fcaa5800 0000001f 00000425
                @0x00000000960030e4 fcaa7000 0000001f 00000425
                @0x00000000960030f0 fcaa8800 0000001f 00000425
                @0x00000000960030fc fcaaa000 0000001f 00000425
                @0x0000000096003108 fcaab800 0000001f 00000425
                @0x0000000096003114 fcaad000 0000001f 00000425
                @0x0000000096003120 fcaae800 0000001f 00000425
                @0x000000009600312c fcab0000 0000001f 00000425
                @0x0000000096003138 fcab1800 0000001f 00000425
                @0x0000000096003144 fcab3000 0000001f 00000425
                @0x0000000096003150 fcab4800 0000001f 00000425
                @0x000000009600315c fcab6000 0000001f 00000425
                @0x0000000096003168 fcab7800 0000001f 00000425
                @0x0000000096003174 fcab9000 0000001f 00000425
                @0x0000000096003180 fcaba800 0000001f 00000425
                @0x000000009600318c fcabc000 0000001f 00000425
                @0x0000000096003198 fcabd800 0000001f 00000425
                @0x00000000960031a4 fcabf000 0000001f 00000425
                @0x00000000960031b0 fcac1000 0000001f 00000425
                @0x00000000960031bc fcac2000 0000001f 00000425
                @0x00000000960031c8 fcac3800 0000001f 00000425
                @0x00000000960031d4 96003000 00000000 00001813

    file-storage-3834  [003] d..1   325.791552: cdns3_doorbell_epx: //Ding Dong ep1out, ep_trbaddr 960031d4
 irq/408-5b13000-1696  [002] d..1   325.833458: cdns3_epx_irq: IRQ for ep1out: 01000408 ISP , ep_traddr: 9600300c
 irq/408-5b13000-1696  [002] d..1   325.833464: cdns3_request_handled: Req: ffff800836bfcd00 handled, DMA pos: 1, ep d
eq: 0, ep enq: 1, start trb: 0, end trb: 0
 irq/408-5b13000-1696  [002] d..1   325.833466: cdns3_complete_trb: ep1out: trb ffff00000a530000, dma buf: 0xfcac5000,
 size: 31, ctrl: 0x00000424 (C=0, T=0, ISP, IOC, Normal)
 irq/408-5b13000-1696  [002] d..1   325.833470: cdns3_gadget_giveback: ep1out: req: ffff800836bfcd00, length: 31/1024 
, status: 0, trb: [start:0, end:0: virt addr ffff00000a530000], flags:0 
    file-storage-3834  [003] d..1   325.833491: cdns3_ep_queue: ep1out: req: ffff800836bfcd00, length: 0/16384 , statu
s: -115, trb: [start:0, end:0: virt addr ffff00000a530000], flags:0 
    file-storage-3834  [003] d..1   325.833496: cdns3_prepare_trb: ep1out: trb ffff00000a53000c, dma buf: 0xfcac6000, 
size: 16384, ctrl: 0x00000424 (C=0, T=0, ISP, IOC, Normal)
    file-storage-3834  [003] d..1   325.833499: cdns3_ring: 
                Ring contents for ep1out:
                Ring deq index: 1, trb: ffff00000a53000c (virt), 0x9600300c (dma)
                Ring enq index: 2, trb: ffff00000a530018 (virt), 0x96003018 (dma)
                free trbs: 38, CCS=0, PCS=0
                @0x0000000096003000 fcac5000 0000001f 00000424
                @0x000000009600300c fcac6000 00004000 00000424
                @0x0000000096003018 fca8d800 0000001f 00000425
                @0x0000000096003024 fca8f000 0000001f 00000425
                @0x0000000096003030 fca90800 0000001f 00000425
                @0x000000009600303c fca92000 0000001f 00000425
                @0x0000000096003048 fca93800 0000001f 00000425
                @0x0000000096003054 fca95000 0000001f 00000425
                @0x0000000096003060 fca96800 0000001f 00000425
                @0x000000009600306c fca98000 0000001f 00000425
                @0x0000000096003078 fca99800 0000001f 00000425
                @0x0000000096003084 fca9b000 0000001f 00000425
                @0x0000000096003090 fca9c800 0000001f 00000425
                @0x000000009600309c fca9e000 0000001f 00000425
                @0x00000000960030a8 fca9f800 0000001f 00000425
                @0x00000000960030b4 fcaa1000 0000001f 00000425
                @0x00000000960030c0 fcaa2800 0000001f 00000425
                @0x00000000960030cc fcaa4000 0000001f 00000425
                @0x00000000960030d8 fcaa5800 0000001f 00000425
                @0x00000000960030e4 fcaa7000 0000001f 00000425
                @0x00000000960030f0 fcaa8800 0000001f 00000425
                @0x00000000960030fc fcaaa000 0000001f 00000425
                @0x0000000096003108 fcaab800 0000001f 00000425
                @0x0000000096003114 fcaad000 0000001f 00000425
                @0x0000000096003120 fcaae800 0000001f 00000425
                @0x000000009600312c fcab0000 0000001f 00000425
                @0x0000000096003138 fcab1800 0000001f 00000425
                @0x0000000096003144 fcab3000 0000001f 00000425
                @0x0000000096003150 fcab4800 0000001f 00000425
                @0x000000009600315c fcab6000 0000001f 00000425
                @0x0000000096003168 fcab7800 0000001f 00000425
                @0x0000000096003174 fcab9000 0000001f 00000425
                @0x0000000096003180 fcaba800 0000001f 00000425
                @0x000000009600318c fcabc000 0000001f 00000425
                @0x0000000096003198 fcabd800 0000001f 00000425
                @0x00000000960031a4 fcabf000 0000001f 00000425
                @0x00000000960031b0 fcac1000 0000001f 00000425
                @0x00000000960031bc fcac2000 0000001f 00000425
                @0x00000000960031c8 fcac3800 0000001f 00000425
                @0x00000000960031d4 96003000 00000000 00001813

    file-storage-3834  [003] d..1   325.833507: cdns3_doorbell_epx: //Ding Dong ep1out, ep_trbaddr 9600300c
    file-storage-3834  [003] d..1   325.833510: cdns3_ep_queue: ep1out: req: ffff800836c59f00, length: 0/16384 , statu
s: -115, trb: [start:36, end:36: virt addr ffff00000a5301b0], flags:0 
    file-storage-3834  [003] d..1   325.833514: cdns3_prepare_trb: ep1out: trb ffff00000a530018, dma buf: 0xfcaca000, 
size: 16384, ctrl: 0x00000424 (C=0, T=0, ISP, IOC, Normal)
    file-storage-3834  [003] d..1   325.833517: cdns3_ring: 
                Ring contents for ep1out:
                Ring deq index: 1, trb: ffff00000a53000c (virt), 0x9600300c (dma)
                Ring enq index: 3, trb: ffff00000a530024 (virt), 0x96003024 (dma)
                free trbs: 37, CCS=0, PCS=0
                @0x0000000096003000 fcac5000 0000001f 00000424
                @0x000000009600300c fcac6000 00004000 00000424
                @0x0000000096003018 fcaca000 00004000 00000424
                @0x0000000096003024 fca8f000 0000001f 00000425
                @0x0000000096003030 fca90800 0000001f 00000425
                @0x000000009600303c fca92000 0000001f 00000425
                @0x0000000096003048 fca93800 0000001f 00000425
                @0x0000000096003054 fca95000 0000001f 00000425
                @0x0000000096003060 fca96800 0000001f 00000425
                @0x000000009600306c fca98000 0000001f 00000425
                @0x0000000096003078 fca99800 0000001f 00000425
                @0x0000000096003084 fca9b000 0000001f 00000425
                @0x0000000096003090 fca9c800 0000001f 00000425
                @0x000000009600309c fca9e000 0000001f 00000425
                @0x00000000960030a8 fca9f800 0000001f 00000425
                @0x00000000960030b4 fcaa1000 0000001f 00000425
                @0x00000000960030c0 fcaa2800 0000001f 00000425
                @0x00000000960030cc fcaa4000 0000001f 00000425
                @0x00000000960030d8 fcaa5800 0000001f 00000425
                @0x00000000960030e4 fcaa7000 0000001f 00000425
                @0x00000000960030f0 fcaa8800 0000001f 00000425
                @0x00000000960030fc fcaaa000 0000001f 00000425
                @0x0000000096003108 fcaab800 0000001f 00000425
                @0x0000000096003114 fcaad000 0000001f 00000425
                @0x0000000096003120 fcaae800 0000001f 00000425
                @0x000000009600312c fcab0000 0000001f 00000425
                @0x0000000096003138 fcab1800 0000001f 00000425
                @0x0000000096003144 fcab3000 0000001f 00000425
                @0x0000000096003150 fcab4800 0000001f 00000425
                @0x000000009600315c fcab6000 0000001f 00000425
                @0x0000000096003168 fcab7800 0000001f 00000425
                @0x0000000096003174 fcab9000 0000001f 00000425
                @0x0000000096003180 fcaba800 0000001f 00000425
                @0x000000009600318c fcabc000 0000001f 00000425
                @0x0000000096003198 fcabd800 0000001f 00000425
                @0x00000000960031a4 fcabf000 0000001f 00000425
                @0x00000000960031b0 fcac1000 0000001f 00000425
                @0x00000000960031bc fcac2000 0000001f 00000425
                @0x00000000960031c8 fcac3800 0000001f 00000425
                @0x00000000960031d4 96003000 00000000 00001813

    file-storage-3834  [003] d..1   325.833525: cdns3_doorbell_epx: //Ding Dong ep1out, ep_trbaddr 9600300c
 irq/408-5b13000-1696  [002] d..1   325.833567: cdns3_epx_irq: IRQ for ep1out: 01010204 IOC , ep_traddr: 96003018
 irq/408-5b13000-1696  [002] d..1   325.833571: cdns3_request_handled: Req: ffff800836bfcd00 handled, DMA pos: 2, ep d
eq: 1, ep enq: 3, start trb: 1, end trb: 1
 irq/408-5b13000-1696  [002] d..1   325.833572: cdns3_complete_trb: ep1out: trb ffff00000a53000c, dma buf: 0xfcac6000,
 size: 16384, ctrl: 0x00000424 (C=0, T=0, ISP, IOC, Normal)
 irq/408-5b13000-1696  [002] d..1   325.833590: cdns3_gadget_giveback: ep1out: req: ffff800836bfcd00, length: 16384/16
384 , status: 0, trb: [start:1, end:1: virt addr ffff00000a53000c], flags:0 
 irq/408-5b13000-1696  [002] d..1   325.833599: cdns3_request_handled: Req: ffff800836c59f00 not handled, DMA pos: 2, 
ep deq: 2, ep enq: 3, start trb: 2, end trb: 2
 irq/408-5b13000-1696  [002] d..1   325.833618: cdns3_epx_irq: IRQ for ep1out: 11010014 IOC DESCMIS , ep_traddr: 96003
024
 irq/408-5b13000-1696  [002] d..1   325.833621: cdns3_request_handled: Req: ffff800836c59f00 handled, DMA pos: 3, ep d
eq: 2, ep enq: 3, start trb: 2, end trb: 2
 irq/408-5b13000-1696  [002] d..1   325.833623: cdns3_complete_trb: ep1out: trb ffff00000a530018, dma buf: 0xfcaca000,
 size: 16384, ctrl: 0x00000424 (C=0, T=0, ISP, IOC, Normal)
 irq/408-5b13000-1696  [002] d..1   325.833641: cdns3_gadget_giveback: ep1out: req: ffff800836c59f00, length: 16384/16
384 , status: 0, trb: [start:2, end:2: virt addr ffff00000a530018], flags:0 
 irq/408-5b13000-1696  [002] d..1   325.833647: cdns3_alloc_request: ep1out: req: ffff800836c5ee00, length: 0/0 , stat
us: 0, trb: [start:0, end:0: virt addr           (null)], flags:0 
 irq/408-5b13000-1696  [002] d..1   325.833650: cdns3_ep_queue: ep1out: req: ffff800836c5ee00, length: 0/2048 , status
: -115, trb: [start:0, end:0: virt addr           (null)], flags:2 
 irq/408-5b13000-1696  [002] d..1   325.833655: cdns3_prepare_trb: ep1out: trb ffff00000a530024, dma buf: 0xfcace000, 
size: 2048, ctrl: 0x00000424 (C=0, T=0, ISP, IOC, Normal)
 irq/408-5b13000-1696  [002] d..1   325.833658: cdns3_ring: 
                Ring contents for ep1out:
                Ring deq index: 3, trb: ffff00000a530024 (virt), 0x96003024 (dma)
                Ring enq index: 4, trb: ffff00000a530030 (virt), 0x96003030 (dma)
                free trbs: 38, CCS=0, PCS=0
                @0x0000000096003000 fcac5000 0000001f 00000424
                @0x000000009600300c fcac6000 00004000 00000424
                @0x0000000096003018 fcaca000 00004000 00000424
                @0x0000000096003024 fcace000 00000800 00000424
                @0x0000000096003030 fca90800 0000001f 00000425
                @0x000000009600303c fca92000 0000001f 00000425
                @0x0000000096003048 fca93800 0000001f 00000425
                @0x0000000096003054 fca95000 0000001f 00000425
                @0x0000000096003060 fca96800 0000001f 00000425
                @0x000000009600306c fca98000 0000001f 00000425
                @0x0000000096003078 fca99800 0000001f 00000425
                @0x0000000096003084 fca9b000 0000001f 00000425
                @0x0000000096003090 fca9c800 0000001f 00000425
                @0x000000009600309c fca9e000 0000001f 00000425
                @0x00000000960030a8 fca9f800 0000001f 00000425
                @0x00000000960030b4 fcaa1000 0000001f 00000425
                @0x00000000960030c0 fcaa2800 0000001f 00000425
                @0x00000000960030cc fcaa4000 0000001f 00000425
                @0x00000000960030d8 fcaa5800 0000001f 00000425
                @0x00000000960030e4 fcaa7000 0000001f 00000425
                @0x00000000960030f0 fcaa8800 0000001f 00000425
                @0x00000000960030fc fcaaa000 0000001f 00000425
                @0x0000000096003108 fcaab800 0000001f 00000425
                @0x0000000096003114 fcaad000 0000001f 00000425
                @0x0000000096003120 fcaae800 0000001f 00000425
                @0x000000009600312c fcab0000 0000001f 00000425
                @0x0000000096003138 fcab1800 0000001f 00000425
                @0x0000000096003144 fcab3000 0000001f 00000425
                @0x0000000096003150 fcab4800 0000001f 00000425
                @0x000000009600315c fcab6000 0000001f 00000425
                @0x0000000096003168 fcab7800 0000001f 00000425
                @0x0000000096003174 fcab9000 0000001f 00000425
                @0x0000000096003180 fcaba800 0000001f 00000425
                @0x000000009600318c fcabc000 0000001f 00000425
                @0x0000000096003198 fcabd800 0000001f 00000425
                @0x00000000960031a4 fcabf000 0000001f 00000425
                @0x00000000960031b0 fcac1000 0000001f 00000425
                @0x00000000960031bc fcac2000 0000001f 00000425
                @0x00000000960031c8 fcac3800 0000001f 00000425
                @0x00000000960031d4 96003000 00000000 00001813

 irq/408-5b13000-1696  [002] d..1   325.833667: cdns3_doorbell_epx: //Ding Dong ep1out, ep_trbaddr 96003024
 irq/408-5b13000-1696  [002] d..1   325.833689: cdns3_epx_irq: IRQ for ep1out: 11010014 IOC DESCMIS , ep_traddr: 96003
030
 irq/408-5b13000-1696  [002] d..1   325.833692: cdns3_request_handled: Req: ffff800836c5ee00 handled, DMA pos: 4, ep d
eq: 3, ep enq: 4, start trb: 3, end trb: 3
 irq/408-5b13000-1696  [002] d..1   325.833694: cdns3_complete_trb: ep1out: trb ffff00000a530024, dma buf: 0xfcace000,
 size: 2048, ctrl: 0x00000424 (C=0, T=0, ISP, IOC, Normal)
 irq/408-5b13000-1696  [002] d..1   325.833700: cdns3_gadget_giveback: ep1out: req: ffff800836c5ee00, length: 2048/204
8 , status: 0, trb: [start:3, end:3: virt addr ffff00000a530024], flags:2 
 irq/408-5b13000-1696  [002] d..1   325.833702: cdns3_alloc_request: ep1out: req: ffff800836c5e900, length: 0/0 , stat
us: 0, trb: [start:0, end:0: virt addr           (null)], flags:0 
 irq/408-5b13000-1696  [002] d..1   325.833704: cdns3_ep_queue: ep1out: req: ffff800836c5e900, length: 0/2048 , status
: -115, trb: [start:0, end:0: virt addr           (null)], flags:2 
 irq/408-5b13000-1696  [002] d..1   325.833708: cdns3_prepare_trb: ep1out: trb ffff00000a530030, dma buf: 0xfcace800, 
size: 2048, ctrl: 0x00000424 (C=0, T=0, ISP, IOC, Normal)
 irq/408-5b13000-1696  [002] d..1   325.833711: cdns3_ring: 
                Ring contents for ep1out:
                Ring deq index: 4, trb: ffff00000a530030 (virt), 0x96003030 (dma)
                Ring enq index: 5, trb: ffff00000a53003c (virt), 0x9600303c (dma)
                free trbs: 38, CCS=0, PCS=0
                @0x0000000096003000 fcac5000 0000001f 00000424
                @0x000000009600300c fcac6000 00004000 00000424
                @0x0000000096003018 fcaca000 00004000 00000424
                @0x0000000096003024 fcace000 00000800 00000424
                @0x0000000096003030 fcace800 00000800 00000424
                @0x000000009600303c fca92000 0000001f 00000425
                @0x0000000096003048 fca93800 0000001f 00000425
                @0x0000000096003054 fca95000 0000001f 00000425
                @0x0000000096003060 fca96800 0000001f 00000425
                @0x000000009600306c fca98000 0000001f 00000425
                @0x0000000096003078 fca99800 0000001f 00000425
                @0x0000000096003084 fca9b000 0000001f 00000425
                @0x0000000096003090 fca9c800 0000001f 00000425
                @0x000000009600309c fca9e000 0000001f 00000425
                @0x00000000960030a8 fca9f800 0000001f 00000425
                @0x00000000960030b4 fcaa1000 0000001f 00000425
                @0x00000000960030c0 fcaa2800 0000001f 00000425
                @0x00000000960030cc fcaa4000 0000001f 00000425
                @0x00000000960030d8 fcaa5800 0000001f 00000425
                @0x00000000960030e4 fcaa7000 0000001f 00000425
                @0x00000000960030f0 fcaa8800 0000001f 00000425
                @0x00000000960030fc fcaaa000 0000001f 00000425
                @0x0000000096003108 fcaab800 0000001f 00000425
                @0x0000000096003114 fcaad000 0000001f 00000425
                @0x0000000096003120 fcaae800 0000001f 00000425
                @0x000000009600312c fcab0000 0000001f 00000425
                @0x0000000096003138 fcab1800 0000001f 00000425
                @0x0000000096003144 fcab3000 0000001f 00000425
                @0x0000000096003150 fcab4800 0000001f 00000425
                @0x000000009600315c fcab6000 0000001f 00000425
                @0x0000000096003168 fcab7800 0000001f 00000425
                @0x0000000096003174 fcab9000 0000001f 00000425
                @0x0000000096003180 fcaba800 0000001f 00000425
                @0x000000009600318c fcabc000 0000001f 00000425
                @0x0000000096003198 fcabd800 0000001f 00000425
                @0x00000000960031a4 fcabf000 0000001f 00000425
                @0x00000000960031b0 fcac1000 0000001f 00000425
                @0x00000000960031bc fcac2000 0000001f 00000425
                @0x00000000960031c8 fcac3800 0000001f 00000425
                @0x00000000960031d4 96003000 00000000 00001813

 irq/408-5b13000-1696  [002] d..1   325.833719: cdns3_doorbell_epx: //Ding Dong ep1out, ep_trbaddr 96003030
    file-storage-3834  [003] d..1   325.833722: cdns3_ep_queue: ep1out: req: ffff800836bfcd00, length: 0/16384 , statu
s: -115, trb: [start:1, end:1: virt addr ffff00000a53000c], flags:0 
    file-storage-3834  [003] d..1   325.833728: cdns3_prepare_trb: ep1out: trb ffff00000a53003c, dma buf: 0xfcacf000, 
size: 16384, ctrl: 0x00000424 (C=0, T=0, ISP, IOC, Normal)
    file-storage-3834  [003] d..1   325.833731: cdns3_ring: 
                Ring contents for ep1out:
                Ring deq index: 4, trb: ffff00000a530030 (virt), 0x96003030 (dma)
                Ring enq index: 6, trb: ffff00000a530048 (virt), 0x96003048 (dma)
                free trbs: 37, CCS=0, PCS=0
                @0x0000000096003000 fcac5000 0000001f 00000424
                @0x000000009600300c fcac6000 00004000 00000424
                @0x0000000096003018 fcaca000 00004000 00000424
                @0x0000000096003024 fcace000 00000800 00000424
                @0x0000000096003030 fcace800 00000800 00000424
                @0x000000009600303c fcacf000 00004000 00000424
                @0x0000000096003048 fca93800 0000001f 00000425
                @0x0000000096003054 fca95000 0000001f 00000425
                @0x0000000096003060 fca96800 0000001f 00000425
                @0x000000009600306c fca98000 0000001f 00000425
                @0x0000000096003078 fca99800 0000001f 00000425
                @0x0000000096003084 fca9b000 0000001f 00000425
                @0x0000000096003090 fca9c800 0000001f 00000425
                @0x000000009600309c fca9e000 0000001f 00000425
                @0x00000000960030a8 fca9f800 0000001f 00000425
                @0x00000000960030b4 fcaa1000 0000001f 00000425
                @0x00000000960030c0 fcaa2800 0000001f 00000425
                @0x00000000960030cc fcaa4000 0000001f 00000425
                @0x00000000960030d8 fcaa5800 0000001f 00000425
                @0x00000000960030e4 fcaa7000 0000001f 00000425
                @0x00000000960030f0 fcaa8800 0000001f 00000425
                @0x00000000960030fc fcaaa000 0000001f 00000425
                @0x0000000096003108 fcaab800 0000001f 00000425
                @0x0000000096003114 fcaad000 0000001f 00000425
                @0x0000000096003120 fcaae800 0000001f 00000425
                @0x000000009600312c fcab0000 0000001f 00000425
                @0x0000000096003138 fcab1800 0000001f 00000425
                @0x0000000096003144 fcab3000 0000001f 00000425
                @0x0000000096003150 fcab4800 0000001f 00000425
                @0x000000009600315c fcab6000 0000001f 00000425
                @0x0000000096003168 fcab7800 0000001f 00000425
                @0x0000000096003174 fcab9000 0000001f 00000425
                @0x0000000096003180 fcaba800 0000001f 00000425
                @0x000000009600318c fcabc000 0000001f 00000425
                @0x0000000096003198 fcabd800 0000001f 00000425
                @0x00000000960031a4 fcabf000 0000001f 00000425
                @0x00000000960031b0 fcac1000 0000001f 00000425
                @0x00000000960031bc fcac2000 0000001f 00000425
                @0x00000000960031c8 fcac3800 0000001f 00000425
                @0x00000000960031d4 96003000 00000000 00001813

    file-storage-3834  [003] d..1   325.833740: cdns3_doorbell_epx: //Ding Dong ep1out, ep_trbaddr 9600303c
 irq/408-5b13000-1696  [002] d..1   325.833754: cdns3_epx_irq: IRQ for ep1out: 01010604 IOC , ep_traddr: 9600303c
 irq/408-5b13000-1696  [002] d..1   325.833758: cdns3_request_handled: Req: ffff800836c5e900 handled, DMA pos: 5, ep d
eq: 4, ep enq: 6, start trb: 4, end trb: 4
 irq/408-5b13000-1696  [002] d..1   325.833759: cdns3_complete_trb: ep1out: trb ffff00000a530030, dma buf: 0xfcace800,
 size: 2048, ctrl: 0x00000424 (C=0, T=0, ISP, IOC, Normal)
 irq/408-5b13000-1696  [002] d..1   325.833765: cdns3_gadget_giveback: ep1out: req: ffff800836c5e900, length: 2048/204
8 , status: 0, trb: [start:4, end:4: virt addr ffff00000a530030], flags:2 
 irq/408-5b13000-1696  [002] d..1   325.833767: cdns3_request_handled: Req: ffff800836bfcd00 not handled, DMA pos: 5, 
ep deq: 5, ep enq: 6, start trb: 5, end trb: 5
 irq/408-5b13000-1696  [002] d..1   325.833785: cdns3_epx_irq: IRQ for ep1out: 01000408 ISP , ep_traddr: 96003048
 irq/408-5b13000-1696  [002] d..1   325.833788: cdns3_request_handled: Req: ffff800836bfcd00 handled, DMA pos: 6, ep d
eq: 5, ep enq: 6, start trb: 5, end trb: 5
 irq/408-5b13000-1696  [002] d..1   325.833790: cdns3_complete_trb: ep1out: trb ffff00000a53003c, dma buf: 0xfcacf000,
 size: 15872, ctrl: 0x00000424 (C=0, T=0, ISP, IOC, Normal)
 irq/408-5b13000-1696  [002] d..1   325.833805: cdns3_gadget_giveback: ep1out: req: ffff800836bfcd00, length: 15872/16
384 , status: 0, trb: [start:5, end:5: virt addr ffff00000a53003c], flags:0 
    file-storage-3834  [003] d..1   325.833807: cdns3_ep_queue: ep1out: req: ffff800836c59f00, length: 0/4096 , status
: -115, trb: [start:2, end:2: virt addr ffff00000a530018], flags:0 
    file-storage-3834  [003] d..1   325.833812: cdns3_prepare_trb: ep1out: trb ffff00000a530048, dma buf: 0xfcad3000, 
size: 4096, ctrl: 0x00000424 (C=0, T=0, ISP, IOC, Normal)
    file-storage-3834  [003] d..1   325.833815: cdns3_ring: 
                Ring contents for ep1out:
                Ring deq index: 6, trb: ffff00000a530048 (virt), 0x96003048 (dma)
                Ring enq index: 7, trb: ffff00000a530054 (virt), 0x96003054 (dma)
                free trbs: 38, CCS=0, PCS=0
                @0x0000000096003000 fcac5000 0000001f 00000424
                @0x000000009600300c fcac6000 00004000 00000424
                @0x0000000096003018 fcaca000 00004000 00000424
                @0x0000000096003024 fcace000 00000800 00000424
                @0x0000000096003030 fcace800 00000800 00000424
                @0x000000009600303c fcacf000 00003e00 00000424
                @0x0000000096003048 fcad3000 00001000 00000424
                @0x0000000096003054 fca95000 0000001f 00000425
                @0x0000000096003060 fca96800 0000001f 00000425
                @0x000000009600306c fca98000 0000001f 00000425
                @0x0000000096003078 fca99800 0000001f 00000425
                @0x0000000096003084 fca9b000 0000001f 00000425
                @0x0000000096003090 fca9c800 0000001f 00000425
                @0x000000009600309c fca9e000 0000001f 00000425
                @0x00000000960030a8 fca9f800 0000001f 00000425
                @0x00000000960030b4 fcaa1000 0000001f 00000425
                @0x00000000960030c0 fcaa2800 0000001f 00000425
                @0x00000000960030cc fcaa4000 0000001f 00000425
                @0x00000000960030d8 fcaa5800 0000001f 00000425
                @0x00000000960030e4 fcaa7000 0000001f 00000425
                @0x00000000960030f0 fcaa8800 0000001f 00000425
                @0x00000000960030fc fcaaa000 0000001f 00000425
                @0x0000000096003108 fcaab800 0000001f 00000425
                @0x0000000096003114 fcaad000 0000001f 00000425
                @0x0000000096003120 fcaae800 0000001f 00000425
                @0x000000009600312c fcab0000 0000001f 00000425
                @0x0000000096003138 fcab1800 0000001f 00000425
                @0x0000000096003144 fcab3000 0000001f 00000425
                @0x0000000096003150 fcab4800 0000001f 00000425
                @0x000000009600315c fcab6000 0000001f 00000425
                @0x0000000096003168 fcab7800 0000001f 00000425
                @0x0000000096003174 fcab9000 0000001f 00000425
                @0x0000000096003180 fcaba800 0000001f 00000425
                @0x000000009600318c fcabc000 0000001f 00000425
                @0x0000000096003198 fcabd800 0000001f 00000425
                @0x00000000960031a4 fcabf000 0000001f 00000425
                @0x00000000960031b0 fcac1000 0000001f 00000425
                @0x00000000960031bc fcac2000 0000001f 00000425
                @0x00000000960031c8 fcac3800 0000001f 00000425
                @0x00000000960031d4 96003000 00000000 00001813

    file-storage-3834  [003] d..1   325.833823: cdns3_doorbell_epx: //Ding Dong ep1out, ep_trbaddr 96003048
 irq/408-5b13000-1696  [002] d..1   325.833841: cdns3_request_handled: Req: ffff800836c59f00 not handled, DMA pos: 6, 
ep deq: 6, ep enq: 7, start trb: 6, end trb: 6
    file-storage-3834  [003] d..1   325.833901: cdns3_ep_dequeue: ep1out: req: ffff800836c59f00, length: 0/4096 , stat
us: -115, trb: [start:6, end:6: virt addr ffff00000a530048], flags:1 
    file-storage-3834  [003] d..1   325.833908: cdns3_gadget_giveback: ep1out: req: ffff800836c59f00, length: 0/4096 ,
 status: -104, trb: [start:6, end:6: virt addr ffff00000a530048], flags:0 
    file-storage-3834  [003] d..1   325.833925: cdns3_ep_queue: ep1in: req: ffff800836bfc900, length: 0/13 , status: -
115, trb: [start:34, end:34: virt addr ffff00000a52f198], flags:0 
    file-storage-3834  [003] d..1   325.833930: cdns3_prepare_trb: ep1in: trb ffff00000a52f1b0, dma buf: 0xfcad4000, s
ize: 13, ctrl: 0x00000425 (C=1, T=0, ISP, IOC, Normal)
    file-storage-3834  [003] d..1   325.833932: cdns3_ring: 
                Ring contents for ep1in:
                Ring deq index: 36, trb: ffff00000a52f1b0 (virt), 0x960021b0 (dma)
                Ring enq index: 37, trb: ffff00000a52f1bc (virt), 0x960021bc (dma)
                free trbs: 38, CCS=1, PCS=1
                @0x0000000096002000 fcaa9800 0000000d 00000425
                @0x000000009600200c fcaaa800 00000200 00000425
                @0x0000000096002018 fcaab000 0000000d 00000425
                @0x0000000096002024 fcaac000 00000200 00000425
                @0x0000000096002030 fcaac800 0000000d 00000425
                @0x000000009600203c fcaad800 00000200 00000425
                @0x0000000096002048 fcaae000 0000000d 00000425
                @0x0000000096002054 fcaaf000 00000200 00000425
                @0x0000000096002060 fcaaf800 0000000d 00000425
                @0x000000009600206c fcab0800 00000200 00000425
                @0x0000000096002078 fcab1000 0000000d 00000425
                @0x0000000096002084 fcab2000 00000200 00000425
                @0x0000000096002090 fcab2800 0000000d 00000425
                @0x000000009600209c fcab3800 00000200 00000425
                @0x00000000960020a8 fcab4000 0000000d 00000425
                @0x00000000960020b4 fcab5000 00000200 00000425
                @0x00000000960020c0 fcab5800 0000000d 00000425
                @0x00000000960020cc fcab6800 00000200 00000425
                @0x00000000960020d8 fcab7000 0000000d 00000425
                @0x00000000960020e4 fcab8000 00000200 00000425
                @0x00000000960020f0 fcab8800 0000000d 00000425
                @0x00000000960020fc fcab9800 00000200 00000425
                @0x0000000096002108 fcaba000 0000000d 00000425
                @0x0000000096002114 fcabb000 00000200 00000425
                @0x0000000096002120 fcabb800 0000000d 00000425
                @0x000000009600212c fcabc800 00000200 00000425
                @0x0000000096002138 fcabd000 0000000d 00000425
                @0x0000000096002144 fcabe000 00000200 00000425
                @0x0000000096002150 fcabe800 0000000d 00000425
                @0x000000009600215c fcac0000 00000200 00000425
                @0x0000000096002168 fcac0800 0000000d 00000425
                @0x0000000096002174 fcac1800 0000000d 00000425
                @0x0000000096002180 fcac2800 00000200 00000425
                @0x000000009600218c fcac3000 0000000d 00000425
                @0x0000000096002198 fcac4000 00000200 00000425
                @0x00000000960021a4 fcac4800 0000000d 00000425
                @0x00000000960021b0 fcad4000 0000000d 00000425
                @0x00000000960021bc fcaa8000 0000000d 00000424
                @0x00000000960021c8 fcaa9000 00000200 00000424
                @0x00000000960021d4 96002000 00000000 00001812

    file-storage-3834  [003] d..1   325.833940: cdns3_doorbell_epx: //Ding Dong ep1in, ep_trbaddr 960021b0
    file-storage-3834  [003] d..1   325.833943: cdns3_ep_queue: ep1out: req: ffff800836c59f00, length: 0/1024 , status
: -115, trb: [start:6, end:6: virt addr ffff00000a530048], flags:0 
    file-storage-3834  [003] d..1   325.833946: cdns3_prepare_trb: ep1out: trb ffff00000a530054, dma buf: 0xfcad4800, 
size: 1024, ctrl: 0x00000424 (C=0, T=0, ISP, IOC, Normal)
    file-storage-3834  [003] d..1   325.833948: cdns3_ring: 
                Ring contents for ep1out:
                Ring deq index: 6, trb: ffff00000a530048 (virt), 0x96003048 (dma)
                Ring enq index: 8, trb: ffff00000a530060 (virt), 0x96003060 (dma)
                free trbs: 37, CCS=0, PCS=0
                @0x0000000096003000 fcac5000 0000001f 00000424
                @0x000000009600300c fcac6000 00004000 00000424
                @0x0000000096003018 fcaca000 00004000 00000424
                @0x0000000096003024 fcace000 00000800 00000424
                @0x0000000096003030 fcace800 00000800 00000424
                @0x000000009600303c fcacf000 00003e00 00000424
                @0x0000000096003048 fcad3000 00001000 00000424
                @0x0000000096003054 fcad4800 00000400 00000424
                @0x0000000096003060 fca96800 0000001f 00000425
                @0x000000009600306c fca98000 0000001f 00000425
                @0x0000000096003078 fca99800 0000001f 00000425
                @0x0000000096003084 fca9b000 0000001f 00000425
                @0x0000000096003090 fca9c800 0000001f 00000425
                @0x000000009600309c fca9e000 0000001f 00000425
                @0x00000000960030a8 fca9f800 0000001f 00000425
                @0x00000000960030b4 fcaa1000 0000001f 00000425
                @0x00000000960030c0 fcaa2800 0000001f 00000425
                @0x00000000960030cc fcaa4000 0000001f 00000425
                @0x00000000960030d8 fcaa5800 0000001f 00000425
                @0x00000000960030e4 fcaa7000 0000001f 00000425
                @0x00000000960030f0 fcaa8800 0000001f 00000425
                @0x00000000960030fc fcaaa000 0000001f 00000425
                @0x0000000096003108 fcaab800 0000001f 00000425
                @0x0000000096003114 fcaad000 0000001f 00000425
                @0x0000000096003120 fcaae800 0000001f 00000425
                @0x000000009600312c fcab0000 0000001f 00000425
                @0x0000000096003138 fcab1800 0000001f 00000425
                @0x0000000096003144 fcab3000 0000001f 00000425
                @0x0000000096003150 fcab4800 0000001f 00000425
                @0x000000009600315c fcab6000 0000001f 00000425
                @0x0000000096003168 fcab7800 0000001f 00000425
                @0x0000000096003174 fcab9000 0000001f 00000425
                @0x0000000096003180 fcaba800 0000001f 00000425
                @0x000000009600318c fcabc000 0000001f 00000425
                @0x0000000096003198 fcabd800 0000001f 00000425
                @0x00000000960031a4 fcabf000 0000001f 00000425
                @0x00000000960031b0 fcac1000 0000001f 00000425
                @0x00000000960031bc fcac2000 0000001f 00000425
                @0x00000000960031c8 fcac3800 0000001f 00000425
                @0x00000000960031d4 96003000 00000000 00001813

    file-storage-3834  [003] d..1   325.833956: cdns3_doorbell_epx: //Ding Dong ep1out, ep_trbaddr 96003048
 irq/408-5b13000-1696  [002] d..1   325.833968: cdns3_epx_irq: IRQ for ep1in: 0100088c IOC ISP TRBERR , ep_traddr: 960
021bc
 irq/408-5b13000-1696  [002] d..1   325.833973: cdns3_request_handled: Req: ffff800836bfc900 handled, DMA pos: 37, ep 
deq: 36, ep enq: 37, start trb: 36, end trb: 36
 irq/408-5b13000-1696  [002] d..1   325.833975: cdns3_complete_trb: ep1in: trb ffff00000a52f1b0, dma buf: 0xfcad4000, 
size: 13, ctrl: 0x00000425 (C=1, T=0, ISP, IOC, Normal)
 irq/408-5b13000-1696  [002] d..1   325.833978: cdns3_gadget_giveback: ep1in: req: ffff800836bfc900, length: 13/13 , s
tatus: 0, trb: [start:36, end:36: virt addr ffff00000a52f1b0], flags:0 
 irq/408-5b13000-1696  [002] d..1   325.835951: cdns3_epx_irq: IRQ for ep1out: 01000408 ISP , ep_traddr: 96003054
 irq/408-5b13000-1696  [002] d..1   325.835955: cdns3_request_handled: Req: ffff800836c59f00 not handled, DMA pos: 7, 
ep deq: 6, ep enq: 8, start trb: 7, end trb: 7
 irq/408-5b13000-1696  [002] d..1   325.835983: cdns3_epx_irq: IRQ for ep1out: 11010014 IOC DESCMIS , ep_traddr: 96003
060
 irq/408-5b13000-1696  [002] d..1   325.835987: cdns3_request_handled: Req: ffff800836c59f00 handled, DMA pos: 8, ep d
eq: 6, ep enq: 8, start trb: 7, end trb: 7
 irq/408-5b13000-1696  [002] d..1   325.835989: cdns3_complete_trb: ep1out: trb ffff00000a530048, dma buf: 0xfcad3000,
 size: 31, ctrl: 0x00000424 (C=0, T=0, ISP, IOC, Normal)
 irq/408-5b13000-1696  [002] d..1   325.844973: cdns3_gadget_giveback: ep1out: req: ffff800836c59f00, length: 31/1024 
, status: 0, trb: [start:7, end:7: virt addr ffff00000a530054], flags:0 
 irq/408-5b13000-1696  [002] d..1   325.844988: cdns3_alloc_request: ep1out: req: ffff800836c5e600, length: 0/0 , stat
us: 0, trb: [start:0, end:0: virt addr           (null)], flags:0 
 irq/408-5b13000-1696  [002] d..1   325.844992: cdns3_ep_queue: ep1out: req: ffff800836c5e600, length: 0/2048 , status
: -115, trb: [start:0, end:0: virt addr           (null)], flags:2 
 irq/408-5b13000-1696  [002] d..1   325.844998: cdns3_prepare_trb: ep1out: trb ffff00000a530060, dma buf: 0xfcad5000, 
size: 2048, ctrl: 0x00000424 (C=0, T=0, ISP, IOC, Normal)
 irq/408-5b13000-1696  [002] d..1   325.845002: cdns3_ring: 
                Ring contents for ep1out:
                Ring deq index: 7, trb: ffff00000a530054 (virt), 0x96003054 (dma)
                Ring enq index: 9, trb: ffff00000a53006c (virt), 0x9600306c (dma)
                free trbs: 37, CCS=0, PCS=0
                @0x0000000096003000 fcac5000 0000001f 00000424
                @0x000000009600300c fcac6000 00004000 00000424
                @0x0000000096003018 fcaca000 00004000 00000424
                @0x0000000096003024 fcace000 00000800 00000424
                @0x0000000096003030 fcace800 00000800 00000424
                @0x000000009600303c fcacf000 00003e00 00000424
                @0x0000000096003048 fcad3000 0000001f 00000424
                @0x0000000096003054 fcad4800 00000400 00000424
                @0x0000000096003060 fcad5000 00000800 00000424
                @0x000000009600306c fca98000 0000001f 00000425
                @0x0000000096003078 fca99800 0000001f 00000425
                @0x0000000096003084 fca9b000 0000001f 00000425
                @0x0000000096003090 fca9c800 0000001f 00000425
                @0x000000009600309c fca9e000 0000001f 00000425
                @0x00000000960030a8 fca9f800 0000001f 00000425
                @0x00000000960030b4 fcaa1000 0000001f 00000425
                @0x00000000960030c0 fcaa2800 0000001f 00000425
                @0x00000000960030cc fcaa4000 0000001f 00000425
                @0x00000000960030d8 fcaa5800 0000001f 00000425
                @0x00000000960030e4 fcaa7000 0000001f 00000425
                @0x00000000960030f0 fcaa8800 0000001f 00000425
                @0x00000000960030fc fcaaa000 0000001f 00000425
                @0x0000000096003108 fcaab800 0000001f 00000425
                @0x0000000096003114 fcaad000 0000001f 00000425
                @0x0000000096003120 fcaae800 0000001f 00000425
                @0x000000009600312c fcab0000 0000001f 00000425
                @0x0000000096003138 fcab1800 0000001f 00000425
                @0x0000000096003144 fcab3000 0000001f 00000425
                @0x0000000096003150 fcab4800 0000001f 00000425
                @0x000000009600315c fcab6000 0000001f 00000425
                @0x0000000096003168 fcab7800 0000001f 00000425
                @0x0000000096003174 fcab9000 0000001f 00000425
                @0x0000000096003180 fcaba800 0000001f 00000425
                @0x000000009600318c fcabc000 0000001f 00000425
                @0x0000000096003198 fcabd800 0000001f 00000425
                @0x00000000960031a4 fcabf000 0000001f 00000425
                @0x00000000960031b0 fcac1000 0000001f 00000425
                @0x00000000960031bc fcac2000 0000001f 00000425
                @0x00000000960031c8 fcac3800 0000001f 00000425
                @0x00000000960031d4 96003000 00000000 00001813

 irq/408-5b13000-1696  [002] d..1   325.845011: cdns3_doorbell_epx: //Ding Dong ep1out, ep_trbaddr 96003060
    file-storage-3834  [003] d..1   325.845039: cdns3_ep_queue: ep1out: req: ffff800836c59f00, length: 0/1024 , status
: -115, trb: [start:7, end:7: virt addr ffff00000a530054], flags:0 
    file-storage-3834  [003] d..1   325.845045: cdns3_prepare_trb: ep1out: trb ffff00000a53006c, dma buf: 0xfcad5800, 
size: 1024, ctrl: 0x00000424 (C=0, T=0, ISP, IOC, Normal)
    file-storage-3834  [003] d..1   325.845049: cdns3_ring: 
                Ring contents for ep1out:
                Ring deq index: 7, trb: ffff00000a530054 (virt), 0x96003054 (dma)
                Ring enq index: 10, trb: ffff00000a530078 (virt), 0x96003078 (dma)
                free trbs: 36, CCS=0, PCS=0
                @0x0000000096003000 fcac5000 0000001f 00000424
                @0x000000009600300c fcac6000 00004000 00000424
                @0x0000000096003018 fcaca000 00004000 00000424
                @0x0000000096003024 fcace000 00000800 00000424
                @0x0000000096003030 fcace800 00000800 00000424
                @0x000000009600303c fcacf000 00003e00 00000424
                @0x0000000096003048 fcad3000 0000001f 00000424
                @0x0000000096003054 fcad4800 00000400 00000424
                @0x0000000096003060 fcad5000 00000800 00000424
                @0x000000009600306c fcad5800 00000400 00000424
                @0x0000000096003078 fca99800 0000001f 00000425
                @0x0000000096003084 fca9b000 0000001f 00000425
                @0x0000000096003090 fca9c800 0000001f 00000425
                @0x000000009600309c fca9e000 0000001f 00000425
                @0x00000000960030a8 fca9f800 0000001f 00000425
                @0x00000000960030b4 fcaa1000 0000001f 00000425
                @0x00000000960030c0 fcaa2800 0000001f 00000425
                @0x00000000960030cc fcaa4000 0000001f 00000425
                @0x00000000960030d8 fcaa5800 0000001f 00000425
                @0x00000000960030e4 fcaa7000 0000001f 00000425
                @0x00000000960030f0 fcaa8800 0000001f 00000425
                @0x00000000960030fc fcaaa000 0000001f 00000425
                @0x0000000096003108 fcaab800 0000001f 00000425
                @0x0000000096003114 fcaad000 0000001f 00000425
                @0x0000000096003120 fcaae800 0000001f 00000425
                @0x000000009600312c fcab0000 0000001f 00000425
                @0x0000000096003138 fcab1800 0000001f 00000425
                @0x0000000096003144 fcab3000 0000001f 00000425
                @0x0000000096003150 fcab4800 0000001f 00000425
                @0x000000009600315c fcab6000 0000001f 00000425
                @0x0000000096003168 fcab7800 0000001f 00000425
                @0x0000000096003174 fcab9000 0000001f 00000425
                @0x0000000096003180 fcaba800 0000001f 00000425
                @0x000000009600318c fcabc000 0000001f 00000425
                @0x0000000096003198 fcabd800 0000001f 00000425
                @0x00000000960031a4 fcabf000 0000001f 00000425
                @0x00000000960031b0 fcac1000 0000001f 00000425
                @0x00000000960031bc fcac2000 0000001f 00000425
                @0x00000000960031c8 fcac3800 0000001f 00000425
                @0x00000000960031d4 96003000 00000000 00001813

    file-storage-3834  [003] d..1   325.845058: cdns3_doorbell_epx: //Ding Dong ep1out, ep_trbaddr 9600306c
 irq/408-5b13000-1696  [002] d..1   325.845083: cdns3_epx_irq: IRQ for ep1out: 11010014 IOC DESCMIS , ep_traddr: 96003
078
 irq/408-5b13000-1696  [002] d..1   325.845087: cdns3_request_handled: Req: ffff800836c5e600 handled, DMA pos: 10, ep 
deq: 7, ep enq: 10, start trb: 8, end trb: 8
 irq/408-5b13000-1696  [002] d..1   325.845089: cdns3_complete_trb: ep1out: trb ffff00000a530054, dma buf: 0xfcad4800,
 size: 1024, ctrl: 0x00000424 (C=0, T=0, ISP, IOC, Normal)
 irq/408-5b13000-1696  [002] d..1   325.854081: cdns3_gadget_giveback: ep1out: req: ffff800836c5e600, length: 1024/204
8 , status: 0, trb: [start:8, end:8: virt addr ffff00000a530060], flags:2 
 irq/408-5b13000-1696  [002] d..1   325.854085: cdns3_request_handled: Req: ffff800836c59f00 handled, DMA pos: 10, ep 
deq: 8, ep enq: 10, start trb: 9, end trb: 9
 irq/408-5b13000-1696  [002] d..1   325.854086: cdns3_complete_trb: ep1out: trb ffff00000a530060, dma buf: 0xfcad5000,
 size: 2048, ctrl: 0x00000424 (C=0, T=0, ISP, IOC, Normal)
 irq/408-5b13000-1696  [002] d..1   325.863065: cdns3_gadget_giveback: ep1out: req: ffff800836c59f00, length: 2048/102
4 , status: 0, trb: [start:9, end:9: virt addr ffff00000a53006c], flags:0 
 irq/408-5b13000-1696  [002] d..1   325.863094: cdns3_alloc_request: ep1out: req: ffff800836c5e100, length: 0/0 , stat
us: 0, trb: [start:0, end:0: virt addr           (null)], flags:0 
 irq/408-5b13000-1696  [002] d..1   325.863098: cdns3_ep_queue: ep1out: req: ffff800836c5e100, length: 0/2048 , status
: -115, trb: [start:0, end:0: virt addr           (null)], flags:2 
 irq/408-5b13000-1696  [002] d..1   325.863106: cdns3_prepare_trb: ep1out: trb ffff00000a530078, dma buf: 0xfcad6000, 
size: 2048, ctrl: 0x00000424 (C=0, T=0, ISP, IOC, Normal)
 irq/408-5b13000-1696  [002] d..1   325.863111: cdns3_ring: 
                Ring contents for ep1out:
                Ring deq index: 9, trb: ffff00000a53006c (virt), 0x9600306c (dma)
                Ring enq index: 11, trb: ffff00000a530084 (virt), 0x96003084 (dma)
                free trbs: 37, CCS=0, PCS=0
                @0x0000000096003000 fcac5000 0000001f 00000424
                @0x000000009600300c fcac6000 00004000 00000424
                @0x0000000096003018 fcaca000 00004000 00000424
                @0x0000000096003024 fcace000 00000800 00000424
                @0x0000000096003030 fcace800 00000800 00000424
                @0x000000009600303c fcacf000 00003e00 00000424
                @0x0000000096003048 fcad3000 0000001f 00000424
                @0x0000000096003054 fcad4800 00000400 00000424
                @0x0000000096003060 fcad5000 00000800 00000424
                @0x000000009600306c fcad5800 00000400 00000424
                @0x0000000096003078 fcad6000 00000800 00000424
                @0x0000000096003084 fca9b000 0000001f 00000425
                @0x0000000096003090 fca9c800 0000001f 00000425
                @0x000000009600309c fca9e000 0000001f 00000425
                @0x00000000960030a8 fca9f800 0000001f 00000425
                @0x00000000960030b4 fcaa1000 0000001f 00000425
                @0x00000000960030c0 fcaa2800 0000001f 00000425
                @0x00000000960030cc fcaa4000 0000001f 00000425
                @0x00000000960030d8 fcaa5800 0000001f 00000425
                @0x00000000960030e4 fcaa7000 0000001f 00000425
                @0x00000000960030f0 fcaa8800 0000001f 00000425
                @0x00000000960030fc fcaaa000 0000001f 00000425
                @0x0000000096003108 fcaab800 0000001f 00000425
                @0x0000000096003114 fcaad000 0000001f 00000425
                @0x0000000096003120 fcaae800 0000001f 00000425
                @0x000000009600312c fcab0000 0000001f 00000425
                @0x0000000096003138 fcab1800 0000001f 00000425
                @0x0000000096003144 fcab3000 0000001f 00000425
                @0x0000000096003150 fcab4800 0000001f 00000425
                @0x000000009600315c fcab6000 0000001f 00000425
                @0x0000000096003168 fcab7800 0000001f 00000425
                @0x0000000096003174 fcab9000 0000001f 00000425
                @0x0000000096003180 fcaba800 0000001f 00000425
                @0x000000009600318c fcabc000 0000001f 00000425
                @0x0000000096003198 fcabd800 0000001f 00000425
                @0x00000000960031a4 fcabf000 0000001f 00000425
                @0x00000000960031b0 fcac1000 0000001f 00000425
                @0x00000000960031bc fcac2000 0000001f 00000425
                @0x00000000960031c8 fcac3800 0000001f 00000425
                @0x00000000960031d4 96003000 00000000 00001813

 irq/408-5b13000-1696  [002] d..1   325.863120: cdns3_doorbell_epx: //Ding Dong ep1out, ep_trbaddr 96003078
    file-storage-3834  [003] d..1   325.863125: cdns3_ep_queue: ep1out: req: ffff800836c59f00, length: 0/1024 , status
: -115, trb: [start:9, end:9: virt addr ffff00000a53006c], flags:0 
    file-storage-3834  [003] d..1   325.863133: cdns3_prepare_trb: ep1out: trb ffff00000a530084, dma buf: 0xfcad6800, 
size: 1024, ctrl: 0x00000424 (C=0, T=0, ISP, IOC, Normal)
    file-storage-3834  [003] d..1   325.863138: cdns3_ring: 
                Ring contents for ep1out:
                Ring deq index: 9, trb: ffff00000a53006c (virt), 0x9600306c (dma)
                Ring enq index: 12, trb: ffff00000a530090 (virt), 0x96003090 (dma)
                free trbs: 36, CCS=0, PCS=0
                @0x0000000096003000 fcac5000 0000001f 00000424
                @0x000000009600300c fcac6000 00004000 00000424
                @0x0000000096003018 fcaca000 00004000 00000424
                @0x0000000096003024 fcace000 00000800 00000424
                @0x0000000096003030 fcace800 00000800 00000424
                @0x000000009600303c fcacf000 00003e00 00000424
                @0x0000000096003048 fcad3000 0000001f 00000424
                @0x0000000096003054 fcad4800 00000400 00000424
                @0x0000000096003060 fcad5000 00000800 00000424
                @0x000000009600306c fcad5800 00000400 00000424
                @0x0000000096003078 fcad6000 00000800 00000424
                @0x0000000096003084 fcad6800 00000400 00000424
                @0x0000000096003090 fca9c800 0000001f 00000425
                @0x000000009600309c fca9e000 0000001f 00000425
                @0x00000000960030a8 fca9f800 0000001f 00000425
                @0x00000000960030b4 fcaa1000 0000001f 00000425
                @0x00000000960030c0 fcaa2800 0000001f 00000425
                @0x00000000960030cc fcaa4000 0000001f 00000425
                @0x00000000960030d8 fcaa5800 0000001f 00000425
                @0x00000000960030e4 fcaa7000 0000001f 00000425
                @0x00000000960030f0 fcaa8800 0000001f 00000425
                @0x00000000960030fc fcaaa000 0000001f 00000425
                @0x0000000096003108 fcaab800 0000001f 00000425
                @0x0000000096003114 fcaad000 0000001f 00000425
                @0x0000000096003120 fcaae800 0000001f 00000425
                @0x000000009600312c fcab0000 0000001f 00000425
                @0x0000000096003138 fcab1800 0000001f 00000425
                @0x0000000096003144 fcab3000 0000001f 00000425
                @0x0000000096003150 fcab4800 0000001f 00000425
                @0x000000009600315c fcab6000 0000001f 00000425
                @0x0000000096003168 fcab7800 0000001f 00000425
                @0x0000000096003174 fcab9000 0000001f 00000425
                @0x0000000096003180 fcaba800 0000001f 00000425
                @0x000000009600318c fcabc000 0000001f 00000425
                @0x0000000096003198 fcabd800 0000001f 00000425
                @0x00000000960031a4 fcabf000 0000001f 00000425
                @0x00000000960031b0 fcac1000 0000001f 00000425
                @0x00000000960031bc fcac2000 0000001f 00000425
                @0x00000000960031c8 fcac3800 0000001f 00000425
                @0x00000000960031d4 96003000 00000000 00001813

    file-storage-3834  [003] d..1   325.863146: cdns3_doorbell_epx: //Ding Dong ep1out, ep_trbaddr 96003084
 irq/408-5b13000-1696  [002] d..1   325.863174: cdns3_epx_irq: IRQ for ep1out: 11010014 IOC DESCMIS , ep_traddr: 96003
090
 irq/408-5b13000-1696  [002] d..1   325.863179: cdns3_request_handled: Req: ffff800836c5e100 handled, DMA pos: 12, ep 
deq: 9, ep enq: 12, start trb: 10, end trb: 10
 irq/408-5b13000-1696  [002] d..1   325.863180: cdns3_complete_trb: ep1out: trb ffff00000a53006c, dma buf: 0xfcad5800,
 size: 1024, ctrl: 0x00000424 (C=0, T=0, ISP, IOC, Normal)
 irq/408-5b13000-1696  [002] d..1   325.872168: cdns3_gadget_giveback: ep1out: req: ffff800836c5e100, length: 1024/204
8 , status: 0, trb: [start:10, end:10: virt addr ffff00000a530078], flags:2 
 irq/408-5b13000-1696  [002] d..1   325.872171: cdns3_request_handled: Req: ffff800836c59f00 handled, DMA pos: 12, ep 
deq: 10, ep enq: 12, start trb: 11, end trb: 11
 irq/408-5b13000-1696  [002] d..1   325.872173: cdns3_complete_trb: ep1out: trb ffff00000a530078, dma buf: 0xfcad6000,
 size: 2048, ctrl: 0x00000424 (C=0, T=0, ISP, IOC, Normal)
 irq/408-5b13000-1696  [002] d..1   325.881153: cdns3_gadget_giveback: ep1out: req: ffff800836c59f00, length: 2048/102
4 , status: 0, trb: [start:11, end:11: virt addr ffff00000a530084], flags:0 
 irq/408-5b13000-1696  [002] d..1   325.881179: cdns3_alloc_request: ep1out: req: ffff800836c5ef00, length: 0/0 , stat
us: 0, trb: [start:0, end:0: virt addr           (null)], flags:0 
 irq/408-5b13000-1696  [002] d..1   325.881183: cdns3_ep_queue: ep1out: req: ffff800836c5ef00, length: 0/2048 , status
: -115, trb: [start:0, end:0: virt addr           (null)], flags:2 
 irq/408-5b13000-1696  [002] d..1   325.881191: cdns3_prepare_trb: ep1out: trb ffff00000a530090, dma buf: 0xfcad7000, 
size: 2048, ctrl: 0x00000424 (C=0, T=0, ISP, IOC, Normal)
 irq/408-5b13000-1696  [002] d..1   325.881196: cdns3_ring: 
                Ring contents for ep1out:
                Ring deq index: 11, trb: ffff00000a530084 (virt), 0x96003084 (dma)
                Ring enq index: 13, trb: ffff00000a53009c (virt), 0x9600309c (dma)
                free trbs: 37, CCS=0, PCS=0
                @0x0000000096003000 fcac5000 0000001f 00000424
                @0x000000009600300c fcac6000 00004000 00000424
                @0x0000000096003018 fcaca000 00004000 00000424
                @0x0000000096003024 fcace000 00000800 00000424
                @0x0000000096003030 fcace800 00000800 00000424
                @0x000000009600303c fcacf000 00003e00 00000424
                @0x0000000096003048 fcad3000 0000001f 00000424
                @0x0000000096003054 fcad4800 00000400 00000424
                @0x0000000096003060 fcad5000 00000800 00000424
                @0x000000009600306c fcad5800 00000400 00000424
                @0x0000000096003078 fcad6000 00000800 00000424
                @0x0000000096003084 fcad6800 00000400 00000424
                @0x0000000096003090 fcad7000 00000800 00000424
                @0x000000009600309c fca9e000 0000001f 00000425
                @0x00000000960030a8 fca9f800 0000001f 00000425
                @0x00000000960030b4 fcaa1000 0000001f 00000425
                @0x00000000960030c0 fcaa2800 0000001f 00000425
                @0x00000000960030cc fcaa4000 0000001f 00000425
                @0x00000000960030d8 fcaa5800 0000001f 00000425
                @0x00000000960030e4 fcaa7000 0000001f 00000425
                @0x00000000960030f0 fcaa8800 0000001f 00000425
                @0x00000000960030fc fcaaa000 0000001f 00000425
                @0x0000000096003108 fcaab800 0000001f 00000425
                @0x0000000096003114 fcaad000 0000001f 00000425
                @0x0000000096003120 fcaae800 0000001f 00000425
                @0x000000009600312c fcab0000 0000001f 00000425
                @0x0000000096003138 fcab1800 0000001f 00000425
                @0x0000000096003144 fcab3000 0000001f 00000425
                @0x0000000096003150 fcab4800 0000001f 00000425
                @0x000000009600315c fcab6000 0000001f 00000425
                @0x0000000096003168 fcab7800 0000001f 00000425
                @0x0000000096003174 fcab9000 0000001f 00000425
                @0x0000000096003180 fcaba800 0000001f 00000425
                @0x000000009600318c fcabc000 0000001f 00000425
                @0x0000000096003198 fcabd800 0000001f 00000425
                @0x00000000960031a4 fcabf000 0000001f 00000425
                @0x00000000960031b0 fcac1000 0000001f 00000425
                @0x00000000960031bc fcac2000 0000001f 00000425
                @0x00000000960031c8 fcac3800 0000001f 00000425
                @0x00000000960031d4 96003000 00000000 00001813

 irq/408-5b13000-1696  [002] d..1   325.881205: cdns3_doorbell_epx: //Ding Dong ep1out, ep_trbaddr 96003090
    file-storage-3834  [003] d..1   325.881209: cdns3_ep_queue: ep1out: req: ffff800836c59f00, length: 0/1024 , status
: -115, trb: [start:11, end:11: virt addr ffff00000a530084], flags:0 
    file-storage-3834  [003] d..1   325.881217: cdns3_prepare_trb: ep1out: trb ffff00000a53009c, dma buf: 0xfcad7800, 
size: 1024, ctrl: 0x00000424 (C=0, T=0, ISP, IOC, Normal)
    file-storage-3834  [003] d..1   325.881221: cdns3_ring: 
                Ring contents for ep1out:
                Ring deq index: 11, trb: ffff00000a530084 (virt), 0x96003084 (dma)
                Ring enq index: 14, trb: ffff00000a5300a8 (virt), 0x960030a8 (dma)
                free trbs: 36, CCS=0, PCS=0
                @0x0000000096003000 fcac5000 0000001f 00000424
                @0x000000009600300c fcac6000 00004000 00000424
                @0x0000000096003018 fcaca000 00004000 00000424
                @0x0000000096003024 fcace000 00000800 00000424
                @0x0000000096003030 fcace800 00000800 00000424
                @0x000000009600303c fcacf000 00003e00 00000424
                @0x0000000096003048 fcad3000 0000001f 00000424
                @0x0000000096003054 fcad4800 00000400 00000424
                @0x0000000096003060 fcad5000 00000800 00000424
                @0x000000009600306c fcad5800 00000400 00000424
                @0x0000000096003078 fcad6000 00000800 00000424
                @0x0000000096003084 fcad6800 00000400 00000424
                @0x0000000096003090 fcad7000 00000800 00000424
                @0x000000009600309c fcad7800 00000400 00000424
                @0x00000000960030a8 fca9f800 0000001f 00000425
                @0x00000000960030b4 fcaa1000 0000001f 00000425
                @0x00000000960030c0 fcaa2800 0000001f 00000425
                @0x00000000960030cc fcaa4000 0000001f 00000425
                @0x00000000960030d8 fcaa5800 0000001f 00000425
                @0x00000000960030e4 fcaa7000 0000001f 00000425
                @0x00000000960030f0 fcaa8800 0000001f 00000425
                @0x00000000960030fc fcaaa000 0000001f 00000425
                @0x0000000096003108 fcaab800 0000001f 00000425
                @0x0000000096003114 fcaad000 0000001f 00000425
                @0x0000000096003120 fcaae800 0000001f 00000425
                @0x000000009600312c fcab0000 0000001f 00000425
                @0x0000000096003138 fcab1800 0000001f 00000425
                @0x0000000096003144 fcab3000 0000001f 00000425
                @0x0000000096003150 fcab4800 0000001f 00000425
                @0x000000009600315c fcab6000 0000001f 00000425
                @0x0000000096003168 fcab7800 0000001f 00000425
                @0x0000000096003174 fcab9000 0000001f 00000425
                @0x0000000096003180 fcaba800 0000001f 00000425
                @0x000000009600318c fcabc000 0000001f 00000425
                @0x0000000096003198 fcabd800 0000001f 00000425
                @0x00000000960031a4 fcabf000 0000001f 00000425
                @0x00000000960031b0 fcac1000 0000001f 00000425
                @0x00000000960031bc fcac2000 0000001f 00000425
                @0x00000000960031c8 fcac3800 0000001f 00000425
                @0x00000000960031d4 96003000 00000000 00001813

    file-storage-3834  [003] d..1   325.881229: cdns3_doorbell_epx: //Ding Dong ep1out, ep_trbaddr 9600309c
 irq/408-5b13000-1696  [002] d..1   325.881256: cdns3_epx_irq: IRQ for ep1out: 11010014 IOC DESCMIS , ep_traddr: 96003
0a8
 irq/408-5b13000-1696  [002] d..1   325.881261: cdns3_request_handled: Req: ffff800836c5ef00 handled, DMA pos: 14, ep 
deq: 11, ep enq: 14, start trb: 12, end trb: 12
 irq/408-5b13000-1696  [002] d..1   325.881263: cdns3_complete_trb: ep1out: trb ffff00000a530084, dma buf: 0xfcad6800,
 size: 1024, ctrl: 0x00000424 (C=0, T=0, ISP, IOC, Normal)
 irq/408-5b13000-1696  [002] d..1   325.890247: cdns3_gadget_giveback: ep1out: req: ffff800836c5ef00, length: 1024/204
8 , status: 0, trb: [start:12, end:12: virt addr ffff00000a530090], flags:2 
 irq/408-5b13000-1696  [002] d..1   325.890250: cdns3_request_handled: Req: ffff800836c59f00 handled, DMA pos: 14, ep 
deq: 12, ep enq: 14, start trb: 13, end trb: 13
 irq/408-5b13000-1696  [002] d..1   325.890252: cdns3_complete_trb: ep1out: trb ffff00000a530090, dma buf: 0xfcad7000,
 size: 2048, ctrl: 0x00000424 (C=0, T=0, ISP, IOC, Normal)
 irq/408-5b13000-1696  [002] d..1   325.899232: cdns3_gadget_giveback: ep1out: req: ffff800836c59f00, length: 2048/102
4 , status: 0, trb: [start:13, end:13: virt addr ffff00000a53009c], flags:0 
 irq/408-5b13000-1696  [002] d..1   325.899258: cdns3_alloc_request: ep1out: req: ffff80083aaaf100, length: 0/0 , stat
us: 0, trb: [start:0, end:0: virt addr           (null)], flags:0 
 irq/408-5b13000-1696  [002] d..1   325.899263: cdns3_ep_queue: ep1out: req: ffff80083aaaf100, length: 0/2048 , status
: -115, trb: [start:0, end:0: virt addr           (null)], flags:2 
 irq/408-5b13000-1696  [002] d..1   325.899270: cdns3_prepare_trb: ep1out: trb ffff00000a5300a8, dma buf: 0xfcad8000, 
size: 2048, ctrl: 0x00000424 (C=0, T=0, ISP, IOC, Normal)
 irq/408-5b13000-1696  [002] d..1   325.899274: cdns3_ring: 
                Ring contents for ep1out:
                Ring deq index: 13, trb: ffff00000a53009c (virt), 0x9600309c (dma)
                Ring enq index: 15, trb: ffff00000a5300b4 (virt), 0x960030b4 (dma)
                free trbs: 37, CCS=0, PCS=0
                @0x0000000096003000 fcac5000 0000001f 00000424
                @0x000000009600300c fcac6000 00004000 00000424
                @0x0000000096003018 fcaca000 00004000 00000424
                @0x0000000096003024 fcace000 00000800 00000424
                @0x0000000096003030 fcace800 00000800 00000424
                @0x000000009600303c fcacf000 00003e00 00000424
                @0x0000000096003048 fcad3000 0000001f 00000424
                @0x0000000096003054 fcad4800 00000400 00000424
                @0x0000000096003060 fcad5000 00000800 00000424
                @0x000000009600306c fcad5800 00000400 00000424
                @0x0000000096003078 fcad6000 00000800 00000424
                @0x0000000096003084 fcad6800 00000400 00000424
                @0x0000000096003090 fcad7000 00000800 00000424
                @0x000000009600309c fcad7800 00000400 00000424
                @0x00000000960030a8 fcad8000 00000800 00000424
                @0x00000000960030b4 fcaa1000 0000001f 00000425
                @0x00000000960030c0 fcaa2800 0000001f 00000425
                @0x00000000960030cc fcaa4000 0000001f 00000425
                @0x00000000960030d8 fcaa5800 0000001f 00000425
                @0x00000000960030e4 fcaa7000 0000001f 00000425
                @0x00000000960030f0 fcaa8800 0000001f 00000425
                @0x00000000960030fc fcaaa000 0000001f 00000425
                @0x0000000096003108 fcaab800 0000001f 00000425
                @0x0000000096003114 fcaad000 0000001f 00000425
                @0x0000000096003120 fcaae800 0000001f 00000425
                @0x000000009600312c fcab0000 0000001f 00000425
                @0x0000000096003138 fcab1800 0000001f 00000425
                @0x0000000096003144 fcab3000 0000001f 00000425
                @0x0000000096003150 fcab4800 0000001f 00000425
                @0x000000009600315c fcab6000 0000001f 00000425
                @0x0000000096003168 fcab7800 0000001f 00000425
                @0x0000000096003174 fcab9000 0000001f 00000425
                @0x0000000096003180 fcaba800 0000001f 00000425
                @0x000000009600318c fcabc000 0000001f 00000425
                @0x0000000096003198 fcabd800 0000001f 00000425
                @0x00000000960031a4 fcabf000 0000001f 00000425
                @0x00000000960031b0 fcac1000 0000001f 00000425
                @0x00000000960031bc fcac2000 0000001f 00000425
                @0x00000000960031c8 fcac3800 0000001f 00000425
                @0x00000000960031d4 96003000 00000000 00001813

 irq/408-5b13000-1696  [002] d..1   325.899283: cdns3_doorbell_epx: //Ding Dong ep1out, ep_trbaddr 960030a8
    file-storage-3834  [003] d..1   325.899287: cdns3_ep_queue: ep1out: req: ffff800836c59f00, length: 0/1024 , status
: -115, trb: [start:13, end:13: virt addr ffff00000a53009c], flags:0 
    file-storage-3834  [003] d..1   325.899294: cdns3_prepare_trb: ep1out: trb ffff00000a5300b4, dma buf: 0xfcad8800, 
size: 1024, ctrl: 0x00000424 (C=0, T=0, ISP, IOC, Normal)
    file-storage-3834  [003] d..1   325.899299: cdns3_ring: 
                Ring contents for ep1out:
                Ring deq index: 13, trb: ffff00000a53009c (virt), 0x9600309c (dma)
                Ring enq index: 16, trb: ffff00000a5300c0 (virt), 0x960030c0 (dma)
                free trbs: 36, CCS=0, PCS=0
                @0x0000000096003000 fcac5000 0000001f 00000424
                @0x000000009600300c fcac6000 00004000 00000424
                @0x0000000096003018 fcaca000 00004000 00000424
                @0x0000000096003024 fcace000 00000800 00000424
                @0x0000000096003030 fcace800 00000800 00000424
                @0x000000009600303c fcacf000 00003e00 00000424
                @0x0000000096003048 fcad3000 0000001f 00000424
                @0x0000000096003054 fcad4800 00000400 00000424
                @0x0000000096003060 fcad5000 00000800 00000424
                @0x000000009600306c fcad5800 00000400 00000424
                @0x0000000096003078 fcad6000 00000800 00000424
                @0x0000000096003084 fcad6800 00000400 00000424
                @0x0000000096003090 fcad7000 00000800 00000424
                @0x000000009600309c fcad7800 00000400 00000424
                @0x00000000960030a8 fcad8000 00000800 00000424
                @0x00000000960030b4 fcad8800 00000400 00000424
                @0x00000000960030c0 fcaa2800 0000001f 00000425
                @0x00000000960030cc fcaa4000 0000001f 00000425
                @0x00000000960030d8 fcaa5800 0000001f 00000425
                @0x00000000960030e4 fcaa7000 0000001f 00000425
                @0x00000000960030f0 fcaa8800 0000001f 00000425
                @0x00000000960030fc fcaaa000 0000001f 00000425
                @0x0000000096003108 fcaab800 0000001f 00000425
                @0x0000000096003114 fcaad000 0000001f 00000425
                @0x0000000096003120 fcaae800 0000001f 00000425
                @0x000000009600312c fcab0000 0000001f 00000425
                @0x0000000096003138 fcab1800 0000001f 00000425
                @0x0000000096003144 fcab3000 0000001f 00000425
                @0x0000000096003150 fcab4800 0000001f 00000425
                @0x000000009600315c fcab6000 0000001f 00000425
                @0x0000000096003168 fcab7800 0000001f 00000425
                @0x0000000096003174 fcab9000 0000001f 00000425
                @0x0000000096003180 fcaba800 0000001f 00000425
                @0x000000009600318c fcabc000 0000001f 00000425
                @0x0000000096003198 fcabd800 0000001f 00000425
                @0x00000000960031a4 fcabf000 0000001f 00000425
                @0x00000000960031b0 fcac1000 0000001f 00000425
                @0x00000000960031bc fcac2000 0000001f 00000425
                @0x00000000960031c8 fcac3800 0000001f 00000425
                @0x00000000960031d4 96003000 00000000 00001813

    file-storage-3834  [003] d..1   325.899307: cdns3_doorbell_epx: //Ding Dong ep1out, ep_trbaddr 960030b4
 irq/408-5b13000-1696  [002] d..1   325.899332: cdns3_epx_irq: IRQ for ep1out: 11010014 IOC DESCMIS , ep_traddr: 96003
0c0
 irq/408-5b13000-1696  [002] d..1   325.899336: cdns3_request_handled: Req: ffff80083aaaf100 handled, DMA pos: 16, ep 
deq: 13, ep enq: 16, start trb: 14, end trb: 14
 irq/408-5b13000-1696  [002] d..1   325.899338: cdns3_complete_trb: ep1out: trb ffff00000a53009c, dma buf: 0xfcad7800,
 size: 1024, ctrl: 0x00000424 (C=0, T=0, ISP, IOC, Normal)
 irq/408-5b13000-1696  [002] d..1   325.908326: cdns3_gadget_giveback: ep1out: req: ffff80083aaaf100, length: 1024/204
8 , status: 0, trb: [start:14, end:14: virt addr ffff00000a5300a8], flags:2 
 irq/408-5b13000-1696  [002] d..1   325.908329: cdns3_request_handled: Req: ffff800836c59f00 handled, DMA pos: 16, ep 
deq: 14, ep enq: 16, start trb: 15, end trb: 15
 irq/408-5b13000-1696  [002] d..1   325.908331: cdns3_complete_trb: ep1out: trb ffff00000a5300a8, dma buf: 0xfcad8000,
 size: 2048, ctrl: 0x00000424 (C=0, T=0, ISP, IOC, Normal)
 irq/408-5b13000-1696  [002] d..1   325.917311: cdns3_gadget_giveback: ep1out: req: ffff800836c59f00, length: 2048/102
4 , status: 0, trb: [start:15, end:15: virt addr ffff00000a5300b4], flags:0 
 irq/408-5b13000-1696  [002] d..1   325.917336: cdns3_alloc_request: ep1out: req: ffff800836ed4600, length: 0/0 , stat
us: 0, trb: [start:0, end:0: virt addr           (null)], flags:0 
 irq/408-5b13000-1696  [002] d..1   325.917339: cdns3_ep_queue: ep1out: req: ffff800836ed4600, length: 0/2048 , status
: -115, trb: [start:0, end:0: virt addr           (null)], flags:2 
 irq/408-5b13000-1696  [002] d..1   325.917347: cdns3_prepare_trb: ep1out: trb ffff00000a5300c0, dma buf: 0xfcad9000, 
size: 2048, ctrl: 0x00000424 (C=0, T=0, ISP, IOC, Normal)
 irq/408-5b13000-1696  [002] d..1   325.917351: cdns3_ring: 
                Ring contents for ep1out:
                Ring deq index: 15, trb: ffff00000a5300b4 (virt), 0x960030b4 (dma)
                Ring enq index: 17, trb: ffff00000a5300cc (virt), 0x960030cc (dma)
                free trbs: 37, CCS=0, PCS=0
                @0x0000000096003000 fcac5000 0000001f 00000424
                @0x000000009600300c fcac6000 00004000 00000424
                @0x0000000096003018 fcaca000 00004000 00000424
                @0x0000000096003024 fcace000 00000800 00000424
                @0x0000000096003030 fcace800 00000800 00000424
                @0x000000009600303c fcacf000 00003e00 00000424
                @0x0000000096003048 fcad3000 0000001f 00000424
                @0x0000000096003054 fcad4800 00000400 00000424
                @0x0000000096003060 fcad5000 00000800 00000424
                @0x000000009600306c fcad5800 00000400 00000424
                @0x0000000096003078 fcad6000 00000800 00000424
                @0x0000000096003084 fcad6800 00000400 00000424
                @0x0000000096003090 fcad7000 00000800 00000424
                @0x000000009600309c fcad7800 00000400 00000424
                @0x00000000960030a8 fcad8000 00000800 00000424
                @0x00000000960030b4 fcad8800 00000400 00000424
                @0x00000000960030c0 fcad9000 00000800 00000424
                @0x00000000960030cc fcaa4000 0000001f 00000425
                @0x00000000960030d8 fcaa5800 0000001f 00000425
                @0x00000000960030e4 fcaa7000 0000001f 00000425
                @0x00000000960030f0 fcaa8800 0000001f 00000425
                @0x00000000960030fc fcaaa000 0000001f 00000425
                @0x0000000096003108 fcaab800 0000001f 00000425
                @0x0000000096003114 fcaad000 0000001f 00000425
                @0x0000000096003120 fcaae800 0000001f 00000425
                @0x000000009600312c fcab0000 0000001f 00000425
                @0x0000000096003138 fcab1800 0000001f 00000425
                @0x0000000096003144 fcab3000 0000001f 00000425
                @0x0000000096003150 fcab4800 0000001f 00000425
                @0x000000009600315c fcab6000 0000001f 00000425
                @0x0000000096003168 fcab7800 0000001f 00000425
                @0x0000000096003174 fcab9000 0000001f 00000425
                @0x0000000096003180 fcaba800 0000001f 00000425
                @0x000000009600318c fcabc000 0000001f 00000425
                @0x0000000096003198 fcabd800 0000001f 00000425
                @0x00000000960031a4 fcabf000 0000001f 00000425
                @0x00000000960031b0 fcac1000 0000001f 00000425
                @0x00000000960031bc fcac2000 0000001f 00000425
                @0x00000000960031c8 fcac3800 0000001f 00000425
                @0x00000000960031d4 96003000 00000000 00001813

 irq/408-5b13000-1696  [002] d..1   325.917359: cdns3_doorbell_epx: //Ding Dong ep1out, ep_trbaddr 960030c0
    file-storage-3834  [003] d..1   325.917364: cdns3_ep_queue: ep1out: req: ffff800836c59f00, length: 0/1024 , status
: -115, trb: [start:15, end:15: virt addr ffff00000a5300b4], flags:0 
    file-storage-3834  [003] d..1   325.917371: cdns3_prepare_trb: ep1out: trb ffff00000a5300cc, dma buf: 0xfcad9800, 
size: 1024, ctrl: 0x00000424 (C=0, T=0, ISP, IOC, Normal)
    file-storage-3834  [003] d..1   325.917375: cdns3_ring: 
                Ring contents for ep1out:
                Ring deq index: 15, trb: ffff00000a5300b4 (virt), 0x960030b4 (dma)
                Ring enq index: 18, trb: ffff00000a5300d8 (virt), 0x960030d8 (dma)
                free trbs: 36, CCS=0, PCS=0
                @0x0000000096003000 fcac5000 0000001f 00000424
                @0x000000009600300c fcac6000 00004000 00000424
                @0x0000000096003018 fcaca000 00004000 00000424
                @0x0000000096003024 fcace000 00000800 00000424
                @0x0000000096003030 fcace800 00000800 00000424
                @0x000000009600303c fcacf000 00003e00 00000424
                @0x0000000096003048 fcad3000 0000001f 00000424
                @0x0000000096003054 fcad4800 00000400 00000424
                @0x0000000096003060 fcad5000 00000800 00000424
                @0x000000009600306c fcad5800 00000400 00000424
                @0x0000000096003078 fcad6000 00000800 00000424
                @0x0000000096003084 fcad6800 00000400 00000424
                @0x0000000096003090 fcad7000 00000800 00000424
                @0x000000009600309c fcad7800 00000400 00000424
                @0x00000000960030a8 fcad8000 00000800 00000424
                @0x00000000960030b4 fcad8800 00000400 00000424
                @0x00000000960030c0 fcad9000 00000800 00000424
                @0x00000000960030cc fcad9800 00000400 00000424
                @0x00000000960030d8 fcaa5800 0000001f 00000425
                @0x00000000960030e4 fcaa7000 0000001f 00000425
                @0x00000000960030f0 fcaa8800 0000001f 00000425
                @0x00000000960030fc fcaaa000 0000001f 00000425
                @0x0000000096003108 fcaab800 0000001f 00000425
                @0x0000000096003114 fcaad000 0000001f 00000425
                @0x0000000096003120 fcaae800 0000001f 00000425
                @0x000000009600312c fcab0000 0000001f 00000425
                @0x0000000096003138 fcab1800 0000001f 00000425
                @0x0000000096003144 fcab3000 0000001f 00000425
                @0x0000000096003150 fcab4800 0000001f 00000425
                @0x000000009600315c fcab6000 0000001f 00000425
                @0x0000000096003168 fcab7800 0000001f 00000425
                @0x0000000096003174 fcab9000 0000001f 00000425
                @0x0000000096003180 fcaba800 0000001f 00000425
                @0x000000009600318c fcabc000 0000001f 00000425
                @0x0000000096003198 fcabd800 0000001f 00000425
                @0x00000000960031a4 fcabf000 0000001f 00000425
                @0x00000000960031b0 fcac1000 0000001f 00000425
                @0x00000000960031bc fcac2000 0000001f 00000425
                @0x00000000960031c8 fcac3800 0000001f 00000425
                @0x00000000960031d4 96003000 00000000 00001813

    file-storage-3834  [003] d..1   325.917384: cdns3_doorbell_epx: //Ding Dong ep1out, ep_trbaddr 960030cc
 irq/408-5b13000-1696  [002] d..1   325.917409: cdns3_epx_irq: IRQ for ep1out: 11010014 IOC DESCMIS , ep_traddr: 96003
0d8
 irq/408-5b13000-1696  [002] d..1   325.917413: cdns3_request_handled: Req: ffff800836ed4600 handled, DMA pos: 18, ep 
deq: 15, ep enq: 18, start trb: 16, end trb: 16
 irq/408-5b13000-1696  [002] d..1   325.917415: cdns3_complete_trb: ep1out: trb ffff00000a5300b4, dma buf: 0xfcad8800,
 size: 1024, ctrl: 0x00000424 (C=0, T=0, ISP, IOC, Normal)
 irq/408-5b13000-1696  [002] d..1   325.926404: cdns3_gadget_giveback: ep1out: req: ffff800836ed4600, length: 1024/204
8 , status: 0, trb: [start:16, end:16: virt addr ffff00000a5300c0], flags:2 
 irq/408-5b13000-1696  [002] d..1   325.926407: cdns3_request_handled: Req: ffff800836c59f00 handled, DMA pos: 18, ep 
deq: 16, ep enq: 18, start trb: 17, end trb: 17
 irq/408-5b13000-1696  [002] d..1   325.926409: cdns3_complete_trb: ep1out: trb ffff00000a5300c0, dma buf: 0xfcad9000,
 size: 2048, ctrl: 0x00000424 (C=0, T=0, ISP, IOC, Normal)
 irq/408-5b13000-1696  [002] d..1   325.935389: cdns3_gadget_giveback: ep1out: req: ffff800836c59f00, length: 2048/102
4 , status: 0, trb: [start:17, end:17: virt addr ffff00000a5300cc], flags:0 
 irq/408-5b13000-1696  [002] d..1   325.935414: cdns3_alloc_request: ep1out: req: ffff800836ed4d00, length: 0/0 , stat
us: 0, trb: [start:0, end:0: virt addr           (null)], flags:0 
 irq/408-5b13000-1696  [002] d..1   325.935418: cdns3_ep_queue: ep1out: req: ffff800836ed4d00, length: 0/2048 , status
: -115, trb: [start:0, end:0: virt addr           (null)], flags:2 
 irq/408-5b13000-1696  [002] d..1   325.935425: cdns3_prepare_trb: ep1out: trb ffff00000a5300d8, dma buf: 0xfcada000, 
size: 2048, ctrl: 0x00000424 (C=0, T=0, ISP, IOC, Normal)
 irq/408-5b13000-1696  [002] d..1   325.935428: cdns3_ring: 
                Ring contents for ep1out:
                Ring deq index: 17, trb: ffff00000a5300cc (virt), 0x960030cc (dma)
                Ring enq index: 19, trb: ffff00000a5300e4 (virt), 0x960030e4 (dma)
                free trbs: 37, CCS=0, PCS=0
                @0x0000000096003000 fcac5000 0000001f 00000424
                @0x000000009600300c fcac6000 00004000 00000424
                @0x0000000096003018 fcaca000 00004000 00000424
                @0x0000000096003024 fcace000 00000800 00000424
                @0x0000000096003030 fcace800 00000800 00000424
                @0x000000009600303c fcacf000 00003e00 00000424
                @0x0000000096003048 fcad3000 0000001f 00000424
                @0x0000000096003054 fcad4800 00000400 00000424
                @0x0000000096003060 fcad5000 00000800 00000424
                @0x000000009600306c fcad5800 00000400 00000424
                @0x0000000096003078 fcad6000 00000800 00000424
                @0x0000000096003084 fcad6800 00000400 00000424
                @0x0000000096003090 fcad7000 00000800 00000424
                @0x000000009600309c fcad7800 00000400 00000424
                @0x00000000960030a8 fcad8000 00000800 00000424
                @0x00000000960030b4 fcad8800 00000400 00000424
                @0x00000000960030c0 fcad9000 00000800 00000424
                @0x00000000960030cc fcad9800 00000400 00000424
                @0x00000000960030d8 fcada000 00000800 00000424
                @0x00000000960030e4 fcaa7000 0000001f 00000425
                @0x00000000960030f0 fcaa8800 0000001f 00000425
                @0x00000000960030fc fcaaa000 0000001f 00000425
                @0x0000000096003108 fcaab800 0000001f 00000425
                @0x0000000096003114 fcaad000 0000001f 00000425
                @0x0000000096003120 fcaae800 0000001f 00000425
                @0x000000009600312c fcab0000 0000001f 00000425
                @0x0000000096003138 fcab1800 0000001f 00000425
                @0x0000000096003144 fcab3000 0000001f 00000425
                @0x0000000096003150 fcab4800 0000001f 00000425
                @0x000000009600315c fcab6000 0000001f 00000425
                @0x0000000096003168 fcab7800 0000001f 00000425
                @0x0000000096003174 fcab9000 0000001f 00000425
                @0x0000000096003180 fcaba800 0000001f 00000425
                @0x000000009600318c fcabc000 0000001f 00000425
                @0x0000000096003198 fcabd800 0000001f 00000425
                @0x00000000960031a4 fcabf000 0000001f 00000425
                @0x00000000960031b0 fcac1000 0000001f 00000425
                @0x00000000960031bc fcac2000 0000001f 00000425
                @0x00000000960031c8 fcac3800 0000001f 00000425
                @0x00000000960031d4 96003000 00000000 00001813

 irq/408-5b13000-1696  [002] d..1   325.935437: cdns3_doorbell_epx: //Ding Dong ep1out, ep_trbaddr 960030d8
    file-storage-3834  [003] d..1   325.935441: cdns3_ep_queue: ep1out: req: ffff800836c59f00, length: 0/1024 , status
: -115, trb: [start:17, end:17: virt addr ffff00000a5300cc], flags:0 
    file-storage-3834  [003] d..1   325.935448: cdns3_prepare_trb: ep1out: trb ffff00000a5300e4, dma buf: 0xfcada800, 
size: 1024, ctrl: 0x00000424 (C=0, T=0, ISP, IOC, Normal)
    file-storage-3834  [003] d..1   325.935453: cdns3_ring: 
                Ring contents for ep1out:
                Ring deq index: 17, trb: ffff00000a5300cc (virt), 0x960030cc (dma)
                Ring enq index: 20, trb: ffff00000a5300f0 (virt), 0x960030f0 (dma)
                free trbs: 36, CCS=0, PCS=0
                @0x0000000096003000 fcac5000 0000001f 00000424
                @0x000000009600300c fcac6000 00004000 00000424
                @0x0000000096003018 fcaca000 00004000 00000424
                @0x0000000096003024 fcace000 00000800 00000424
                @0x0000000096003030 fcace800 00000800 00000424
                @0x000000009600303c fcacf000 00003e00 00000424
                @0x0000000096003048 fcad3000 0000001f 00000424
                @0x0000000096003054 fcad4800 00000400 00000424
                @0x0000000096003060 fcad5000 00000800 00000424
                @0x000000009600306c fcad5800 00000400 00000424
                @0x0000000096003078 fcad6000 00000800 00000424
                @0x0000000096003084 fcad6800 00000400 00000424
                @0x0000000096003090 fcad7000 00000800 00000424
                @0x000000009600309c fcad7800 00000400 00000424
                @0x00000000960030a8 fcad8000 00000800 00000424
                @0x00000000960030b4 fcad8800 00000400 00000424
                @0x00000000960030c0 fcad9000 00000800 00000424
                @0x00000000960030cc fcad9800 00000400 00000424
                @0x00000000960030d8 fcada000 00000800 00000424
                @0x00000000960030e4 fcada800 00000400 00000424
                @0x00000000960030f0 fcaa8800 0000001f 00000425
                @0x00000000960030fc fcaaa000 0000001f 00000425
                @0x0000000096003108 fcaab800 0000001f 00000425
                @0x0000000096003114 fcaad000 0000001f 00000425
                @0x0000000096003120 fcaae800 0000001f 00000425
                @0x000000009600312c fcab0000 0000001f 00000425
                @0x0000000096003138 fcab1800 0000001f 00000425
                @0x0000000096003144 fcab3000 0000001f 00000425
                @0x0000000096003150 fcab4800 0000001f 00000425
                @0x000000009600315c fcab6000 0000001f 00000425
                @0x0000000096003168 fcab7800 0000001f 00000425
                @0x0000000096003174 fcab9000 0000001f 00000425
                @0x0000000096003180 fcaba800 0000001f 00000425
                @0x000000009600318c fcabc000 0000001f 00000425
                @0x0000000096003198 fcabd800 0000001f 00000425
                @0x00000000960031a4 fcabf000 0000001f 00000425
                @0x00000000960031b0 fcac1000 0000001f 00000425
                @0x00000000960031bc fcac2000 0000001f 00000425
                @0x00000000960031c8 fcac3800 0000001f 00000425
                @0x00000000960031d4 96003000 00000000 00001813

    file-storage-3834  [003] d..1   325.935461: cdns3_doorbell_epx: //Ding Dong ep1out, ep_trbaddr 960030e4
 irq/408-5b13000-1696  [002] d..1   325.935480: cdns3_epx_irq: IRQ for ep1out: 11010014 IOC DESCMIS , ep_traddr: 96003
0f0
 irq/408-5b13000-1696  [002] d..1   325.935484: cdns3_request_handled: Req: ffff800836ed4d00 handled, DMA pos: 20, ep 
deq: 17, ep enq: 20, start trb: 18, end trb: 18
 irq/408-5b13000-1696  [002] d..1   325.935486: cdns3_complete_trb: ep1out: trb ffff00000a5300cc, dma buf: 0xfcad9800,
 size: 1024, ctrl: 0x00000424 (C=0, T=0, ISP, IOC, Normal)
 irq/408-5b13000-1696  [002] d..1   325.944483: cdns3_gadget_giveback: ep1out: req: ffff800836ed4d00, length: 1024/204
8 , status: 0, trb: [start:18, end:18: virt addr ffff00000a5300d8], flags:2 
 irq/408-5b13000-1696  [002] d..1   325.944486: cdns3_request_handled: Req: ffff800836c59f00 handled, DMA pos: 20, ep 
deq: 18, ep enq: 20, start trb: 19, end trb: 19
 irq/408-5b13000-1696  [002] d..1   325.944488: cdns3_complete_trb: ep1out: trb ffff00000a5300d8, dma buf: 0xfcada000,
 size: 2048, ctrl: 0x00000424 (C=0, T=0, ISP, IOC, Normal)
 irq/408-5b13000-1696  [002] d..1   325.953468: cdns3_gadget_giveback: ep1out: req: ffff800836c59f00, length: 2048/102
4 , status: 0, trb: [start:19, end:19: virt addr ffff00000a5300e4], flags:0 
 irq/408-5b13000-1696  [002] d..1   325.953493: cdns3_alloc_request: ep1out: req: ffff800836ed4500, length: 0/0 , stat
us: 0, trb: [start:0, end:0: virt addr           (null)], flags:0 
 irq/408-5b13000-1696  [002] d..1   325.953500: cdns3_ep_queue: ep1out: req: ffff800836ed4500, length: 0/2048 , status
: -115, trb: [start:0, end:0: virt addr           (null)], flags:2 
 irq/408-5b13000-1696  [002] d..1   325.953508: cdns3_prepare_trb: ep1out: trb ffff00000a5300f0, dma buf: 0xfcadb000, 
size: 2048, ctrl: 0x00000424 (C=0, T=0, ISP, IOC, Normal)
 irq/408-5b13000-1696  [002] d..1   325.953513: cdns3_ring: 
                Ring contents for ep1out:
                Ring deq index: 19, trb: ffff00000a5300e4 (virt), 0x960030e4 (dma)
                Ring enq index: 21, trb: ffff00000a5300fc (virt), 0x960030fc (dma)
                free trbs: 37, CCS=0, PCS=0
                @0x0000000096003000 fcac5000 0000001f 00000424
                @0x000000009600300c fcac6000 00004000 00000424
                @0x0000000096003018 fcaca000 00004000 00000424
                @0x0000000096003024 fcace000 00000800 00000424
                @0x0000000096003030 fcace800 00000800 00000424
                @0x000000009600303c fcacf000 00003e00 00000424
                @0x0000000096003048 fcad3000 0000001f 00000424
                @0x0000000096003054 fcad4800 00000400 00000424
                @0x0000000096003060 fcad5000 00000800 00000424
                @0x000000009600306c fcad5800 00000400 00000424
                @0x0000000096003078 fcad6000 00000800 00000424
                @0x0000000096003084 fcad6800 00000400 00000424
                @0x0000000096003090 fcad7000 00000800 00000424
                @0x000000009600309c fcad7800 00000400 00000424
                @0x00000000960030a8 fcad8000 00000800 00000424
                @0x00000000960030b4 fcad8800 00000400 00000424
                @0x00000000960030c0 fcad9000 00000800 00000424
                @0x00000000960030cc fcad9800 00000400 00000424
                @0x00000000960030d8 fcada000 00000800 00000424
                @0x00000000960030e4 fcada800 00000400 00000424
                @0x00000000960030f0 fcadb000 00000800 00000424
                @0x00000000960030fc fcaaa000 0000001f 00000425
                @0x0000000096003108 fcaab800 0000001f 00000425
                @0x0000000096003114 fcaad000 0000001f 00000425
                @0x0000000096003120 fcaae800 0000001f 00000425
                @0x000000009600312c fcab0000 0000001f 00000425
                @0x0000000096003138 fcab1800 0000001f 00000425
                @0x0000000096003144 fcab3000 0000001f 00000425
                @0x0000000096003150 fcab4800 0000001f 00000425
                @0x000000009600315c fcab6000 0000001f 00000425
                @0x0000000096003168 fcab7800 0000001f 00000425
                @0x0000000096003174 fcab9000 0000001f 00000425
                @0x0000000096003180 fcaba800 0000001f 00000425
                @0x000000009600318c fcabc000 0000001f 00000425
                @0x0000000096003198 fcabd800 0000001f 00000425
                @0x00000000960031a4 fcabf000 0000001f 00000425
                @0x00000000960031b0 fcac1000 0000001f 00000425
                @0x00000000960031bc fcac2000 0000001f 00000425
                @0x00000000960031c8 fcac3800 0000001f 00000425
                @0x00000000960031d4 96003000 00000000 00001813

 irq/408-5b13000-1696  [002] d..1   325.953522: cdns3_doorbell_epx: //Ding Dong ep1out, ep_trbaddr 960030f0
    file-storage-3834  [003] d..1   325.953526: cdns3_ep_queue: ep1out: req: ffff800836c59f00, length: 0/1024 , status
: -115, trb: [start:19, end:19: virt addr ffff00000a5300e4], flags:0 
    file-storage-3834  [003] d..1   325.953533: cdns3_prepare_trb: ep1out: trb ffff00000a5300fc, dma buf: 0xfcadb800, 
size: 1024, ctrl: 0x00000424 (C=0, T=0, ISP, IOC, Normal)
    file-storage-3834  [003] d..1   325.953537: cdns3_ring: 
                Ring contents for ep1out:
                Ring deq index: 19, trb: ffff00000a5300e4 (virt), 0x960030e4 (dma)
                Ring enq index: 22, trb: ffff00000a530108 (virt), 0x96003108 (dma)
                free trbs: 36, CCS=0, PCS=0
                @0x0000000096003000 fcac5000 0000001f 00000424
                @0x000000009600300c fcac6000 00004000 00000424
                @0x0000000096003018 fcaca000 00004000 00000424
                @0x0000000096003024 fcace000 00000800 00000424
                @0x0000000096003030 fcace800 00000800 00000424
                @0x000000009600303c fcacf000 00003e00 00000424
                @0x0000000096003048 fcad3000 0000001f 00000424
                @0x0000000096003054 fcad4800 00000400 00000424
                @0x0000000096003060 fcad5000 00000800 00000424
                @0x000000009600306c fcad5800 00000400 00000424
                @0x0000000096003078 fcad6000 00000800 00000424
                @0x0000000096003084 fcad6800 00000400 00000424
                @0x0000000096003090 fcad7000 00000800 00000424
                @0x000000009600309c fcad7800 00000400 00000424
                @0x00000000960030a8 fcad8000 00000800 00000424
                @0x00000000960030b4 fcad8800 00000400 00000424
                @0x00000000960030c0 fcad9000 00000800 00000424
                @0x00000000960030cc fcad9800 00000400 00000424
                @0x00000000960030d8 fcada000 00000800 00000424
                @0x00000000960030e4 fcada800 00000400 00000424
                @0x00000000960030f0 fcadb000 00000800 00000424
                @0x00000000960030fc fcadb800 00000400 00000424
                @0x0000000096003108 fcaab800 0000001f 00000425
                @0x0000000096003114 fcaad000 0000001f 00000425
                @0x0000000096003120 fcaae800 0000001f 00000425
                @0x000000009600312c fcab0000 0000001f 00000425
                @0x0000000096003138 fcab1800 0000001f 00000425
                @0x0000000096003144 fcab3000 0000001f 00000425
                @0x0000000096003150 fcab4800 0000001f 00000425
                @0x000000009600315c fcab6000 0000001f 00000425
                @0x0000000096003168 fcab7800 0000001f 00000425
                @0x0000000096003174 fcab9000 0000001f 00000425
                @0x0000000096003180 fcaba800 0000001f 00000425
                @0x000000009600318c fcabc000 0000001f 00000425
                @0x0000000096003198 fcabd800 0000001f 00000425
                @0x00000000960031a4 fcabf000 0000001f 00000425
                @0x00000000960031b0 fcac1000 0000001f 00000425
                @0x00000000960031bc fcac2000 0000001f 00000425
                @0x00000000960031c8 fcac3800 0000001f 00000425
                @0x00000000960031d4 96003000 00000000 00001813

    file-storage-3834  [003] d..1   325.953545: cdns3_doorbell_epx: //Ding Dong ep1out, ep_trbaddr 96003108
 irq/408-5b13000-1696  [002] d..1   325.953572: cdns3_epx_irq: IRQ for ep1out: 11010014 IOC DESCMIS , ep_traddr: 96003
108
 irq/408-5b13000-1696  [002] d..1   325.953577: cdns3_request_handled: Req: ffff800836ed4500 handled, DMA pos: 22, ep 
deq: 19, ep enq: 22, start trb: 20, end trb: 20
 irq/408-5b13000-1696  [002] d..1   325.953579: cdns3_complete_trb: ep1out: trb ffff00000a5300e4, dma buf: 0xfcada800,
 size: 1024, ctrl: 0x00000424 (C=0, T=0, ISP, IOC, Normal)
 irq/408-5b13000-1696  [002] d..1   325.962562: cdns3_gadget_giveback: ep1out: req: ffff800836ed4500, length: 1024/204
8 , status: 0, trb: [start:20, end:20: virt addr ffff00000a5300f0], flags:2 
 irq/408-5b13000-1696  [002] d..1   325.962565: cdns3_request_handled: Req: ffff800836c59f00 handled, DMA pos: 22, ep 
deq: 20, ep enq: 22, start trb: 21, end trb: 21
 irq/408-5b13000-1696  [002] d..1   325.962567: cdns3_complete_trb: ep1out: trb ffff00000a5300f0, dma buf: 0xfcadb000,
 size: 2048, ctrl: 0x00000424 (C=0, T=0, ISP, IOC, Normal)
 irq/408-5b13000-1696  [002] d..1   325.971547: cdns3_gadget_giveback: ep1out: req: ffff800836c59f00, length: 2048/102
4 , status: 0, trb: [start:21, end:21: virt addr ffff00000a5300fc], flags:0 
 irq/408-5b13000-1696  [002] d..1   325.971573: cdns3_alloc_request: ep1out: req: ffff800836ed4900, length: 0/0 , stat
us: 0, trb: [start:0, end:0: virt addr           (null)], flags:0 
 irq/408-5b13000-1696  [002] d..1   325.971577: cdns3_ep_queue: ep1out: req: ffff800836ed4900, length: 0/2048 , status
: -115, trb: [start:0, end:0: virt addr           (null)], flags:2 
 irq/408-5b13000-1696  [002] d..1   325.971584: cdns3_prepare_trb: ep1out: trb ffff00000a530108, dma buf: 0xfcadc000, 
size: 2048, ctrl: 0x00000424 (C=0, T=0, ISP, IOC, Normal)
 irq/408-5b13000-1696  [002] d..1   325.971589: cdns3_ring: 
                Ring contents for ep1out:
                Ring deq index: 21, trb: ffff00000a5300fc (virt), 0x960030fc (dma)
                Ring enq index: 23, trb: ffff00000a530114 (virt), 0x96003114 (dma)
                free trbs: 37, CCS=0, PCS=0
                @0x0000000096003000 fcac5000 0000001f 00000424
                @0x000000009600300c fcac6000 00004000 00000424
                @0x0000000096003018 fcaca000 00004000 00000424
                @0x0000000096003024 fcace000 00000800 00000424
                @0x0000000096003030 fcace800 00000800 00000424
                @0x000000009600303c fcacf000 00003e00 00000424
                @0x0000000096003048 fcad3000 0000001f 00000424
                @0x0000000096003054 fcad4800 00000400 00000424
                @0x0000000096003060 fcad5000 00000800 00000424
                @0x000000009600306c fcad5800 00000400 00000424
                @0x0000000096003078 fcad6000 00000800 00000424
                @0x0000000096003084 fcad6800 00000400 00000424
                @0x0000000096003090 fcad7000 00000800 00000424
                @0x000000009600309c fcad7800 00000400 00000424
                @0x00000000960030a8 fcad8000 00000800 00000424
                @0x00000000960030b4 fcad8800 00000400 00000424
                @0x00000000960030c0 fcad9000 00000800 00000424
                @0x00000000960030cc fcad9800 00000400 00000424
                @0x00000000960030d8 fcada000 00000800 00000424
                @0x00000000960030e4 fcada800 00000400 00000424
                @0x00000000960030f0 fcadb000 00000800 00000424
                @0x00000000960030fc fcadb800 00000400 00000424
                @0x0000000096003108 fcadc000 00000800 00000424
                @0x0000000096003114 fcaad000 0000001f 00000425
                @0x0000000096003120 fcaae800 0000001f 00000425
                @0x000000009600312c fcab0000 0000001f 00000425
                @0x0000000096003138 fcab1800 0000001f 00000425
                @0x0000000096003144 fcab3000 0000001f 00000425
                @0x0000000096003150 fcab4800 0000001f 00000425
                @0x000000009600315c fcab6000 0000001f 00000425
                @0x0000000096003168 fcab7800 0000001f 00000425
                @0x0000000096003174 fcab9000 0000001f 00000425
                @0x0000000096003180 fcaba800 0000001f 00000425
                @0x000000009600318c fcabc000 0000001f 00000425
                @0x0000000096003198 fcabd800 0000001f 00000425
                @0x00000000960031a4 fcabf000 0000001f 00000425
                @0x00000000960031b0 fcac1000 0000001f 00000425
                @0x00000000960031bc fcac2000 0000001f 00000425
                @0x00000000960031c8 fcac3800 0000001f 00000425
                @0x00000000960031d4 96003000 00000000 00001813

 irq/408-5b13000-1696  [002] d..1   325.971598: cdns3_doorbell_epx: //Ding Dong ep1out, ep_trbaddr 96003108
    file-storage-3834  [003] d..1   325.971602: cdns3_ep_queue: ep1out: req: ffff800836c59f00, length: 0/1024 , status
: -115, trb: [start:21, end:21: virt addr ffff00000a5300fc], flags:0 
    file-storage-3834  [003] d..1   325.971610: cdns3_prepare_trb: ep1out: trb ffff00000a530114, dma buf: 0xfcadc800, 
size: 1024, ctrl: 0x00000424 (C=0, T=0, ISP, IOC, Normal)
    file-storage-3834  [003] d..1   325.971614: cdns3_ring: 
                Ring contents for ep1out:
                Ring deq index: 21, trb: ffff00000a5300fc (virt), 0x960030fc (dma)
                Ring enq index: 24, trb: ffff00000a530120 (virt), 0x96003120 (dma)
                free trbs: 36, CCS=0, PCS=0
                @0x0000000096003000 fcac5000 0000001f 00000424
                @0x000000009600300c fcac6000 00004000 00000424
                @0x0000000096003018 fcaca000 00004000 00000424
                @0x0000000096003024 fcace000 00000800 00000424
                @0x0000000096003030 fcace800 00000800 00000424
                @0x000000009600303c fcacf000 00003e00 00000424
                @0x0000000096003048 fcad3000 0000001f 00000424
                @0x0000000096003054 fcad4800 00000400 00000424
                @0x0000000096003060 fcad5000 00000800 00000424
                @0x000000009600306c fcad5800 00000400 00000424
                @0x0000000096003078 fcad6000 00000800 00000424
                @0x0000000096003084 fcad6800 00000400 00000424
                @0x0000000096003090 fcad7000 00000800 00000424
                @0x000000009600309c fcad7800 00000400 00000424
                @0x00000000960030a8 fcad8000 00000800 00000424
                @0x00000000960030b4 fcad8800 00000400 00000424
                @0x00000000960030c0 fcad9000 00000800 00000424
                @0x00000000960030cc fcad9800 00000400 00000424
                @0x00000000960030d8 fcada000 00000800 00000424
                @0x00000000960030e4 fcada800 00000400 00000424
                @0x00000000960030f0 fcadb000 00000800 00000424
                @0x00000000960030fc fcadb800 00000400 00000424
                @0x0000000096003108 fcadc000 00000800 00000424
                @0x0000000096003114 fcadc800 00000400 00000424
                @0x0000000096003120 fcaae800 0000001f 00000425
                @0x000000009600312c fcab0000 0000001f 00000425
                @0x0000000096003138 fcab1800 0000001f 00000425
                @0x0000000096003144 fcab3000 0000001f 00000425
                @0x0000000096003150 fcab4800 0000001f 00000425
                @0x000000009600315c fcab6000 0000001f 00000425
                @0x0000000096003168 fcab7800 0000001f 00000425
                @0x0000000096003174 fcab9000 0000001f 00000425
                @0x0000000096003180 fcaba800 0000001f 00000425
                @0x000000009600318c fcabc000 0000001f 00000425
                @0x0000000096003198 fcabd800 0000001f 00000425
                @0x00000000960031a4 fcabf000 0000001f 00000425
                @0x00000000960031b0 fcac1000 0000001f 00000425
                @0x00000000960031bc fcac2000 0000001f 00000425
                @0x00000000960031c8 fcac3800 0000001f 00000425
                @0x00000000960031d4 96003000 00000000 00001813

    file-storage-3834  [003] d..1   325.971623: cdns3_doorbell_epx: //Ding Dong ep1out, ep_trbaddr 96003114
 irq/408-5b13000-1696  [002] d..1   325.971642: cdns3_epx_irq: IRQ for ep1out: 11010014 IOC DESCMIS , ep_traddr: 96003
120
 irq/408-5b13000-1696  [002] d..1   325.971646: cdns3_request_handled: Req: ffff800836ed4900 handled, DMA pos: 24, ep 
deq: 21, ep enq: 24, start trb: 22, end trb: 22
 irq/408-5b13000-1696  [002] d..1   325.971648: cdns3_complete_trb: ep1out: trb ffff00000a5300fc, dma buf: 0xfcadb800,
 size: 1024, ctrl: 0x00000424 (C=0, T=0, ISP, IOC, Normal)
 irq/408-5b13000-1696  [002] d..1   325.980640: cdns3_gadget_giveback: ep1out: req: ffff800836ed4900, length: 1024/204
8 , status: 0, trb: [start:22, end:22: virt addr ffff00000a530108], flags:2 
 irq/408-5b13000-1696  [002] d..1   325.980643: cdns3_request_handled: Req: ffff800836c59f00 handled, DMA pos: 24, ep 
deq: 22, ep enq: 24, start trb: 23, end trb: 23
 irq/408-5b13000-1696  [002] d..1   325.980645: cdns3_complete_trb: ep1out: trb ffff00000a530108, dma buf: 0xfcadc000,
 size: 2048, ctrl: 0x00000424 (C=0, T=0, ISP, IOC, Normal)
 irq/408-5b13000-1696  [002] d..1   325.989626: cdns3_gadget_giveback: ep1out: req: ffff800836c59f00, length: 2048/102
4 , status: 0, trb: [start:23, end:23: virt addr ffff00000a530114], flags:0 
 irq/408-5b13000-1696  [002] d..1   325.989653: cdns3_alloc_request: ep1out: req: ffff800836ed4800, length: 0/0 , stat
us: 0, trb: [start:0, end:0: virt addr           (null)], flags:0 
 irq/408-5b13000-1696  [002] d..1   325.989657: cdns3_ep_queue: ep1out: req: ffff800836ed4800, length: 0/2048 , status
: -115, trb: [start:0, end:0: virt addr           (null)], flags:2 
 irq/408-5b13000-1696  [002] d..1   325.989665: cdns3_prepare_trb: ep1out: trb ffff00000a530120, dma buf: 0xfcadd000, 
size: 2048, ctrl: 0x00000424 (C=0, T=0, ISP, IOC, Normal)
 irq/408-5b13000-1696  [002] d..1   325.989668: cdns3_ring: 
                Ring contents for ep1out:
                Ring deq index: 23, trb: ffff00000a530114 (virt), 0x96003114 (dma)
                Ring enq index: 25, trb: ffff00000a53012c (virt), 0x9600312c (dma)
                free trbs: 37, CCS=0, PCS=0
                @0x0000000096003000 fcac5000 0000001f 00000424
                @0x000000009600300c fcac6000 00004000 00000424
                @0x0000000096003018 fcaca000 00004000 00000424
                @0x0000000096003024 fcace000 00000800 00000424
                @0x0000000096003030 fcace800 00000800 00000424
                @0x000000009600303c fcacf000 00003e00 00000424
                @0x0000000096003048 fcad3000 0000001f 00000424
                @0x0000000096003054 fcad4800 00000400 00000424
                @0x0000000096003060 fcad5000 00000800 00000424
                @0x000000009600306c fcad5800 00000400 00000424
                @0x0000000096003078 fcad6000 00000800 00000424
                @0x0000000096003084 fcad6800 00000400 00000424
                @0x0000000096003090 fcad7000 00000800 00000424
                @0x000000009600309c fcad7800 00000400 00000424
                @0x00000000960030a8 fcad8000 00000800 00000424
                @0x00000000960030b4 fcad8800 00000400 00000424
                @0x00000000960030c0 fcad9000 00000800 00000424
                @0x00000000960030cc fcad9800 00000400 00000424
                @0x00000000960030d8 fcada000 00000800 00000424
                @0x00000000960030e4 fcada800 00000400 00000424
                @0x00000000960030f0 fcadb000 00000800 00000424
                @0x00000000960030fc fcadb800 00000400 00000424
                @0x0000000096003108 fcadc000 00000800 00000424
                @0x0000000096003114 fcadc800 00000400 00000424
                @0x0000000096003120 fcadd000 00000800 00000424
                @0x000000009600312c fcab0000 0000001f 00000425
                @0x0000000096003138 fcab1800 0000001f 00000425
                @0x0000000096003144 fcab3000 0000001f 00000425
                @0x0000000096003150 fcab4800 0000001f 00000425
                @0x000000009600315c fcab6000 0000001f 00000425
                @0x0000000096003168 fcab7800 0000001f 00000425
                @0x0000000096003174 fcab9000 0000001f 00000425
                @0x0000000096003180 fcaba800 0000001f 00000425
                @0x000000009600318c fcabc000 0000001f 00000425
                @0x0000000096003198 fcabd800 0000001f 00000425
                @0x00000000960031a4 fcabf000 0000001f 00000425
                @0x00000000960031b0 fcac1000 0000001f 00000425
                @0x00000000960031bc fcac2000 0000001f 00000425
                @0x00000000960031c8 fcac3800 0000001f 00000425
                @0x00000000960031d4 96003000 00000000 00001813

 irq/408-5b13000-1696  [002] d..1   325.989677: cdns3_doorbell_epx: //Ding Dong ep1out, ep_trbaddr 96003120
    file-storage-3834  [003] d..1   325.989681: cdns3_ep_queue: ep1out: req: ffff800836c59f00, length: 0/1024 , status
: -115, trb: [start:23, end:23: virt addr ffff00000a530114], flags:0 
    file-storage-3834  [003] d..1   325.989689: cdns3_prepare_trb: ep1out: trb ffff00000a53012c, dma buf: 0xfcadd800, 
size: 1024, ctrl: 0x00000424 (C=0, T=0, ISP, IOC, Normal)
    file-storage-3834  [003] d..1   325.989693: cdns3_ring: 
                Ring contents for ep1out:
                Ring deq index: 23, trb: ffff00000a530114 (virt), 0x96003114 (dma)
                Ring enq index: 26, trb: ffff00000a530138 (virt), 0x96003138 (dma)
                free trbs: 36, CCS=0, PCS=0
                @0x0000000096003000 fcac5000 0000001f 00000424
                @0x000000009600300c fcac6000 00004000 00000424
                @0x0000000096003018 fcaca000 00004000 00000424
                @0x0000000096003024 fcace000 00000800 00000424
                @0x0000000096003030 fcace800 00000800 00000424
                @0x000000009600303c fcacf000 00003e00 00000424
                @0x0000000096003048 fcad3000 0000001f 00000424
                @0x0000000096003054 fcad4800 00000400 00000424
                @0x0000000096003060 fcad5000 00000800 00000424
                @0x000000009600306c fcad5800 00000400 00000424
                @0x0000000096003078 fcad6000 00000800 00000424
                @0x0000000096003084 fcad6800 00000400 00000424
                @0x0000000096003090 fcad7000 00000800 00000424
                @0x000000009600309c fcad7800 00000400 00000424
                @0x00000000960030a8 fcad8000 00000800 00000424
                @0x00000000960030b4 fcad8800 00000400 00000424
                @0x00000000960030c0 fcad9000 00000800 00000424
                @0x00000000960030cc fcad9800 00000400 00000424
                @0x00000000960030d8 fcada000 00000800 00000424
                @0x00000000960030e4 fcada800 00000400 00000424
                @0x00000000960030f0 fcadb000 00000800 00000424
                @0x00000000960030fc fcadb800 00000400 00000424
                @0x0000000096003108 fcadc000 00000800 00000424
                @0x0000000096003114 fcadc800 00000400 00000424
                @0x0000000096003120 fcadd000 00000800 00000424
                @0x000000009600312c fcadd800 00000400 00000424
                @0x0000000096003138 fcab1800 0000001f 00000425
                @0x0000000096003144 fcab3000 0000001f 00000425
                @0x0000000096003150 fcab4800 0000001f 00000425
                @0x000000009600315c fcab6000 0000001f 00000425
                @0x0000000096003168 fcab7800 0000001f 00000425
                @0x0000000096003174 fcab9000 0000001f 00000425
                @0x0000000096003180 fcaba800 0000001f 00000425
                @0x000000009600318c fcabc000 0000001f 00000425
                @0x0000000096003198 fcabd800 0000001f 00000425
                @0x00000000960031a4 fcabf000 0000001f 00000425
                @0x00000000960031b0 fcac1000 0000001f 00000425
                @0x00000000960031bc fcac2000 0000001f 00000425
                @0x00000000960031c8 fcac3800 0000001f 00000425
                @0x00000000960031d4 96003000 00000000 00001813

    file-storage-3834  [003] d..1   325.989701: cdns3_doorbell_epx: //Ding Dong ep1out, ep_trbaddr 96003138
 irq/408-5b13000-1696  [002] d..1   325.989733: cdns3_epx_irq: IRQ for ep1out: 11010014 IOC DESCMIS , ep_traddr: 96003
138
 irq/408-5b13000-1696  [002] d..1   325.989738: cdns3_request_handled: Req: ffff800836ed4800 handled, DMA pos: 26, ep 
deq: 23, ep enq: 26, start trb: 24, end trb: 24
 irq/408-5b13000-1696  [002] d..1   325.989739: cdns3_complete_trb: ep1out: trb ffff00000a530114, dma buf: 0xfcadc800,
 size: 1024, ctrl: 0x00000424 (C=0, T=0, ISP, IOC, Normal)
 irq/408-5b13000-1696  [002] d..1   325.998728: cdns3_gadget_giveback: ep1out: req: ffff800836ed4800, length: 1024/204
8 , status: 0, trb: [start:24, end:24: virt addr ffff00000a530120], flags:2 
 irq/408-5b13000-1696  [002] d..1   325.998731: cdns3_request_handled: Req: ffff800836c59f00 handled, DMA pos: 26, ep 
deq: 24, ep enq: 26, start trb: 25, end trb: 25
 irq/408-5b13000-1696  [002] d..1   325.998732: cdns3_complete_trb: ep1out: trb ffff00000a530120, dma buf: 0xfcadd000,
 size: 2048, ctrl: 0x00000424 (C=0, T=0, ISP, IOC, Normal)
 irq/408-5b13000-1696  [002] d..1   326.007713: cdns3_gadget_giveback: ep1out: req: ffff800836c59f00, length: 2048/102
4 , status: 0, trb: [start:25, end:25: virt addr ffff00000a53012c], flags:0 
 irq/408-5b13000-1696  [002] d..1   326.007737: cdns3_alloc_request: ep1out: req: ffff800836ed4e00, length: 0/0 , stat
us: 0, trb: [start:0, end:0: virt addr           (null)], flags:0 
 irq/408-5b13000-1696  [002] d..1   326.007741: cdns3_ep_queue: ep1out: req: ffff800836ed4e00, length: 0/2048 , status
: -115, trb: [start:0, end:0: virt addr           (null)], flags:2 
 irq/408-5b13000-1696  [002] d..1   326.007749: cdns3_prepare_trb: ep1out: trb ffff00000a530138, dma buf: 0xfcade000, 
size: 2048, ctrl: 0x00000424 (C=0, T=0, ISP, IOC, Normal)
 irq/408-5b13000-1696  [002] d..1   326.007753: cdns3_ring: 
                Ring contents for ep1out:
                Ring deq index: 25, trb: ffff00000a53012c (virt), 0x9600312c (dma)
                Ring enq index: 27, trb: ffff00000a530144 (virt), 0x96003144 (dma)
                free trbs: 37, CCS=0, PCS=0
                @0x0000000096003000 fcac5000 0000001f 00000424
                @0x000000009600300c fcac6000 00004000 00000424
                @0x0000000096003018 fcaca000 00004000 00000424
                @0x0000000096003024 fcace000 00000800 00000424
                @0x0000000096003030 fcace800 00000800 00000424
                @0x000000009600303c fcacf000 00003e00 00000424
                @0x0000000096003048 fcad3000 0000001f 00000424
                @0x0000000096003054 fcad4800 00000400 00000424
                @0x0000000096003060 fcad5000 00000800 00000424
                @0x000000009600306c fcad5800 00000400 00000424
                @0x0000000096003078 fcad6000 00000800 00000424
                @0x0000000096003084 fcad6800 00000400 00000424
                @0x0000000096003090 fcad7000 00000800 00000424
                @0x000000009600309c fcad7800 00000400 00000424
                @0x00000000960030a8 fcad8000 00000800 00000424
                @0x00000000960030b4 fcad8800 00000400 00000424
                @0x00000000960030c0 fcad9000 00000800 00000424
                @0x00000000960030cc fcad9800 00000400 00000424
                @0x00000000960030d8 fcada000 00000800 00000424
                @0x00000000960030e4 fcada800 00000400 00000424
                @0x00000000960030f0 fcadb000 00000800 00000424
                @0x00000000960030fc fcadb800 00000400 00000424
                @0x0000000096003108 fcadc000 00000800 00000424
                @0x0000000096003114 fcadc800 00000400 00000424
                @0x0000000096003120 fcadd000 00000800 00000424
                @0x000000009600312c fcadd800 00000400 00000424
                @0x0000000096003138 fcade000 00000800 00000424
                @0x0000000096003144 fcab3000 0000001f 00000425
                @0x0000000096003150 fcab4800 0000001f 00000425
                @0x000000009600315c fcab6000 0000001f 00000425
                @0x0000000096003168 fcab7800 0000001f 00000425
                @0x0000000096003174 fcab9000 0000001f 00000425
                @0x0000000096003180 fcaba800 0000001f 00000425
                @0x000000009600318c fcabc000 0000001f 00000425
                @0x0000000096003198 fcabd800 0000001f 00000425
                @0x00000000960031a4 fcabf000 0000001f 00000425
                @0x00000000960031b0 fcac1000 0000001f 00000425
                @0x00000000960031bc fcac2000 0000001f 00000425
                @0x00000000960031c8 fcac3800 0000001f 00000425
                @0x00000000960031d4 96003000 00000000 00001813

 irq/408-5b13000-1696  [002] d..1   326.007761: cdns3_doorbell_epx: //Ding Dong ep1out, ep_trbaddr 96003138
    file-storage-3834  [003] d..1   326.007765: cdns3_ep_queue: ep1out: req: ffff800836c59f00, length: 0/1024 , status
: -115, trb: [start:25, end:25: virt addr ffff00000a53012c], flags:0 
    file-storage-3834  [003] d..1   326.007773: cdns3_prepare_trb: ep1out: trb ffff00000a530144, dma buf: 0xfcade800, 
size: 1024, ctrl: 0x00000424 (C=0, T=0, ISP, IOC, Normal)
    file-storage-3834  [003] d..1   326.007777: cdns3_ring: 
                Ring contents for ep1out:
                Ring deq index: 25, trb: ffff00000a53012c (virt), 0x9600312c (dma)
                Ring enq index: 28, trb: ffff00000a530150 (virt), 0x96003150 (dma)
                free trbs: 36, CCS=0, PCS=0
                @0x0000000096003000 fcac5000 0000001f 00000424
                @0x000000009600300c fcac6000 00004000 00000424
                @0x0000000096003018 fcaca000 00004000 00000424
                @0x0000000096003024 fcace000 00000800 00000424
                @0x0000000096003030 fcace800 00000800 00000424
                @0x000000009600303c fcacf000 00003e00 00000424
                @0x0000000096003048 fcad3000 0000001f 00000424
                @0x0000000096003054 fcad4800 00000400 00000424
                @0x0000000096003060 fcad5000 00000800 00000424
                @0x000000009600306c fcad5800 00000400 00000424
                @0x0000000096003078 fcad6000 00000800 00000424
                @0x0000000096003084 fcad6800 00000400 00000424
                @0x0000000096003090 fcad7000 00000800 00000424
                @0x000000009600309c fcad7800 00000400 00000424
                @0x00000000960030a8 fcad8000 00000800 00000424
                @0x00000000960030b4 fcad8800 00000400 00000424
                @0x00000000960030c0 fcad9000 00000800 00000424
                @0x00000000960030cc fcad9800 00000400 00000424
                @0x00000000960030d8 fcada000 00000800 00000424
                @0x00000000960030e4 fcada800 00000400 00000424
                @0x00000000960030f0 fcadb000 00000800 00000424
                @0x00000000960030fc fcadb800 00000400 00000424
                @0x0000000096003108 fcadc000 00000800 00000424
                @0x0000000096003114 fcadc800 00000400 00000424
                @0x0000000096003120 fcadd000 00000800 00000424
                @0x000000009600312c fcadd800 00000400 00000424
                @0x0000000096003138 fcade000 00000800 00000424
                @0x0000000096003144 fcade800 00000400 00000424
                @0x0000000096003150 fcab4800 0000001f 00000425
                @0x000000009600315c fcab6000 0000001f 00000425
                @0x0000000096003168 fcab7800 0000001f 00000425
                @0x0000000096003174 fcab9000 0000001f 00000425
                @0x0000000096003180 fcaba800 0000001f 00000425
                @0x000000009600318c fcabc000 0000001f 00000425
                @0x0000000096003198 fcabd800 0000001f 00000425
                @0x00000000960031a4 fcabf000 0000001f 00000425
                @0x00000000960031b0 fcac1000 0000001f 00000425
                @0x00000000960031bc fcac2000 0000001f 00000425
                @0x00000000960031c8 fcac3800 0000001f 00000425
                @0x00000000960031d4 96003000 00000000 00001813

    file-storage-3834  [003] d..1   326.007785: cdns3_doorbell_epx: //Ding Dong ep1out, ep_trbaddr 96003150
 irq/408-5b13000-1696  [002] d..1   326.007805: cdns3_epx_irq: IRQ for ep1out: 11010014 IOC DESCMIS , ep_traddr: 96003
150
 irq/408-5b13000-1696  [002] d..1   326.007809: cdns3_request_handled: Req: ffff800836ed4e00 handled, DMA pos: 28, ep 
deq: 25, ep enq: 28, start trb: 26, end trb: 26
 irq/408-5b13000-1696  [002] d..1   326.007811: cdns3_complete_trb: ep1out: trb ffff00000a53012c, dma buf: 0xfcadd800,
 size: 1024, ctrl: 0x00000424 (C=0, T=0, ISP, IOC, Normal)
 irq/408-5b13000-1696  [002] d..1   326.016807: cdns3_gadget_giveback: ep1out: req: ffff800836ed4e00, length: 1024/204
8 , status: 0, trb: [start:26, end:26: virt addr ffff00000a530138], flags:2 
 irq/408-5b13000-1696  [002] d..1   326.016810: cdns3_request_handled: Req: ffff800836c59f00 handled, DMA pos: 28, ep 
deq: 26, ep enq: 28, start trb: 27, end trb: 27
 irq/408-5b13000-1696  [002] d..1   326.016812: cdns3_complete_trb: ep1out: trb ffff00000a530138, dma buf: 0xfcade000,
 size: 2048, ctrl: 0x00000424 (C=0, T=0, ISP, IOC, Normal)
 irq/408-5b13000-1696  [002] d..1   326.025792: cdns3_gadget_giveback: ep1out: req: ffff800836c59f00, length: 2048/102
4 , status: 0, trb: [start:27, end:27: virt addr ffff00000a530144], flags:0 
 irq/408-5b13000-1696  [002] d..1   326.025816: cdns3_alloc_request: ep1out: req: ffff800836ed4200, length: 0/0 , stat
us: 0, trb: [start:0, end:0: virt addr           (null)], flags:0 
 irq/408-5b13000-1696  [002] d..1   326.025820: cdns3_ep_queue: ep1out: req: ffff800836ed4200, length: 0/2048 , status
: -115, trb: [start:0, end:0: virt addr           (null)], flags:2 
 irq/408-5b13000-1696  [002] d..1   326.025827: cdns3_prepare_trb: ep1out: trb ffff00000a530150, dma buf: 0xfcadf000, 
size: 2048, ctrl: 0x00000424 (C=0, T=0, ISP, IOC, Normal)
 irq/408-5b13000-1696  [002] d..1   326.025832: cdns3_ring: 
                Ring contents for ep1out:
                Ring deq index: 27, trb: ffff00000a530144 (virt), 0x96003144 (dma)
                Ring enq index: 29, trb: ffff00000a53015c (virt), 0x9600315c (dma)
                free trbs: 37, CCS=0, PCS=0
                @0x0000000096003000 fcac5000 0000001f 00000424
                @0x000000009600300c fcac6000 00004000 00000424
                @0x0000000096003018 fcaca000 00004000 00000424
                @0x0000000096003024 fcace000 00000800 00000424
                @0x0000000096003030 fcace800 00000800 00000424
                @0x000000009600303c fcacf000 00003e00 00000424
                @0x0000000096003048 fcad3000 0000001f 00000424
                @0x0000000096003054 fcad4800 00000400 00000424
                @0x0000000096003060 fcad5000 00000800 00000424
                @0x000000009600306c fcad5800 00000400 00000424
                @0x0000000096003078 fcad6000 00000800 00000424
                @0x0000000096003084 fcad6800 00000400 00000424
                @0x0000000096003090 fcad7000 00000800 00000424
                @0x000000009600309c fcad7800 00000400 00000424
                @0x00000000960030a8 fcad8000 00000800 00000424
                @0x00000000960030b4 fcad8800 00000400 00000424
                @0x00000000960030c0 fcad9000 00000800 00000424
                @0x00000000960030cc fcad9800 00000400 00000424
                @0x00000000960030d8 fcada000 00000800 00000424
                @0x00000000960030e4 fcada800 00000400 00000424
                @0x00000000960030f0 fcadb000 00000800 00000424
                @0x00000000960030fc fcadb800 00000400 00000424
                @0x0000000096003108 fcadc000 00000800 00000424
                @0x0000000096003114 fcadc800 00000400 00000424
                @0x0000000096003120 fcadd000 00000800 00000424
                @0x000000009600312c fcadd800 00000400 00000424
                @0x0000000096003138 fcade000 00000800 00000424
                @0x0000000096003144 fcade800 00000400 00000424
                @0x0000000096003150 fcadf000 00000800 00000424
                @0x000000009600315c fcab6000 0000001f 00000425
                @0x0000000096003168 fcab7800 0000001f 00000425
                @0x0000000096003174 fcab9000 0000001f 00000425
                @0x0000000096003180 fcaba800 0000001f 00000425
                @0x000000009600318c fcabc000 0000001f 00000425
                @0x0000000096003198 fcabd800 0000001f 00000425
                @0x00000000960031a4 fcabf000 0000001f 00000425
                @0x00000000960031b0 fcac1000 0000001f 00000425
                @0x00000000960031bc fcac2000 0000001f 00000425
                @0x00000000960031c8 fcac3800 0000001f 00000425
                @0x00000000960031d4 96003000 00000000 00001813

 irq/408-5b13000-1696  [002] d..1   326.025841: cdns3_doorbell_epx: //Ding Dong ep1out, ep_trbaddr 96003150
    file-storage-3834  [003] d..1   326.025845: cdns3_ep_queue: ep1out: req: ffff800836c59f00, length: 0/1024 , status
: -115, trb: [start:27, end:27: virt addr ffff00000a530144], flags:0 
    file-storage-3834  [003] d..1   326.025852: cdns3_prepare_trb: ep1out: trb ffff00000a53015c, dma buf: 0xfcadf800, 
size: 1024, ctrl: 0x00000424 (C=0, T=0, ISP, IOC, Normal)
    file-storage-3834  [003] d..1   326.025857: cdns3_ring: 
                Ring contents for ep1out:
                Ring deq index: 27, trb: ffff00000a530144 (virt), 0x96003144 (dma)
                Ring enq index: 30, trb: ffff00000a530168 (virt), 0x96003168 (dma)
                free trbs: 36, CCS=0, PCS=0
                @0x0000000096003000 fcac5000 0000001f 00000424
                @0x000000009600300c fcac6000 00004000 00000424
                @0x0000000096003018 fcaca000 00004000 00000424
                @0x0000000096003024 fcace000 00000800 00000424
                @0x0000000096003030 fcace800 00000800 00000424
                @0x000000009600303c fcacf000 00003e00 00000424
                @0x0000000096003048 fcad3000 0000001f 00000424
                @0x0000000096003054 fcad4800 00000400 00000424
                @0x0000000096003060 fcad5000 00000800 00000424
                @0x000000009600306c fcad5800 00000400 00000424
                @0x0000000096003078 fcad6000 00000800 00000424
                @0x0000000096003084 fcad6800 00000400 00000424
                @0x0000000096003090 fcad7000 00000800 00000424
                @0x000000009600309c fcad7800 00000400 00000424
                @0x00000000960030a8 fcad8000 00000800 00000424
                @0x00000000960030b4 fcad8800 00000400 00000424
                @0x00000000960030c0 fcad9000 00000800 00000424
                @0x00000000960030cc fcad9800 00000400 00000424
                @0x00000000960030d8 fcada000 00000800 00000424
                @0x00000000960030e4 fcada800 00000400 00000424
                @0x00000000960030f0 fcadb000 00000800 00000424
                @0x00000000960030fc fcadb800 00000400 00000424
                @0x0000000096003108 fcadc000 00000800 00000424
                @0x0000000096003114 fcadc800 00000400 00000424
                @0x0000000096003120 fcadd000 00000800 00000424
                @0x000000009600312c fcadd800 00000400 00000424
                @0x0000000096003138 fcade000 00000800 00000424
                @0x0000000096003144 fcade800 00000400 00000424
                @0x0000000096003150 fcadf000 00000800 00000424
                @0x000000009600315c fcadf800 00000400 00000424
                @0x0000000096003168 fcab7800 0000001f 00000425
                @0x0000000096003174 fcab9000 0000001f 00000425
                @0x0000000096003180 fcaba800 0000001f 00000425
                @0x000000009600318c fcabc000 0000001f 00000425
                @0x0000000096003198 fcabd800 0000001f 00000425
                @0x00000000960031a4 fcabf000 0000001f 00000425
                @0x00000000960031b0 fcac1000 0000001f 00000425
                @0x00000000960031bc fcac2000 0000001f 00000425
                @0x00000000960031c8 fcac3800 0000001f 00000425
                @0x00000000960031d4 96003000 00000000 00001813

    file-storage-3834  [003] d..1   326.025865: cdns3_doorbell_epx: //Ding Dong ep1out, ep_trbaddr 96003168
 irq/408-5b13000-1696  [002] d..1   326.025890: cdns3_epx_irq: IRQ for ep1out: 11010014 IOC DESCMIS , ep_traddr: 96003
168
 irq/408-5b13000-1696  [002] d..1   326.025895: cdns3_request_handled: Req: ffff800836ed4200 handled, DMA pos: 30, ep 
deq: 27, ep enq: 30, start trb: 28, end trb: 28
 irq/408-5b13000-1696  [002] d..1   326.025896: cdns3_complete_trb: ep1out: trb ffff00000a530144, dma buf: 0xfcade800,
 size: 1024, ctrl: 0x00000424 (C=0, T=0, ISP, IOC, Normal)
 irq/408-5b13000-1696  [002] d..1   326.034885: cdns3_gadget_giveback: ep1out: req: ffff800836ed4200, length: 1024/204
8 , status: 0, trb: [start:28, end:28: virt addr ffff00000a530150], flags:2 
 irq/408-5b13000-1696  [002] d..1   326.034888: cdns3_request_handled: Req: ffff800836c59f00 handled, DMA pos: 30, ep 
deq: 28, ep enq: 30, start trb: 29, end trb: 29
 irq/408-5b13000-1696  [002] d..1   326.034890: cdns3_complete_trb: ep1out: trb ffff00000a530150, dma buf: 0xfcadf000,
 size: 2048, ctrl: 0x00000424 (C=0, T=0, ISP, IOC, Normal)
 irq/408-5b13000-1696  [002] d..1   326.043862: cdns3_gadget_giveback: ep1out: req: ffff800836c59f00, length: 2048/102
4 , status: 0, trb: [start:29, end:29: virt addr ffff00000a53015c], flags:0 
 irq/408-5b13000-1696  [002] d..1   326.043886: cdns3_alloc_request: ep1out: req: ffff800836ed4300, length: 0/0 , stat
us: 0, trb: [start:0, end:0: virt addr           (null)], flags:0 
 irq/408-5b13000-1696  [002] d..1   326.043891: cdns3_ep_queue: ep1out: req: ffff800836ed4300, length: 0/2048 , status
: -115, trb: [start:0, end:0: virt addr           (null)], flags:2 
 irq/408-5b13000-1696  [002] d..1   326.043898: cdns3_prepare_trb: ep1out: trb ffff00000a530168, dma buf: 0xfcae0000, 
size: 2048, ctrl: 0x00000424 (C=0, T=0, ISP, IOC, Normal)
 irq/408-5b13000-1696  [002] d..1   326.043903: cdns3_ring: 
                Ring contents for ep1out:
                Ring deq index: 29, trb: ffff00000a53015c (virt), 0x9600315c (dma)
                Ring enq index: 31, trb: ffff00000a530174 (virt), 0x96003174 (dma)
                free trbs: 37, CCS=0, PCS=0
                @0x0000000096003000 fcac5000 0000001f 00000424
                @0x000000009600300c fcac6000 00004000 00000424
                @0x0000000096003018 fcaca000 00004000 00000424
                @0x0000000096003024 fcace000 00000800 00000424
                @0x0000000096003030 fcace800 00000800 00000424
                @0x000000009600303c fcacf000 00003e00 00000424
                @0x0000000096003048 fcad3000 0000001f 00000424
                @0x0000000096003054 fcad4800 00000400 00000424
                @0x0000000096003060 fcad5000 00000800 00000424
                @0x000000009600306c fcad5800 00000400 00000424
                @0x0000000096003078 fcad6000 00000800 00000424
                @0x0000000096003084 fcad6800 00000400 00000424
                @0x0000000096003090 fcad7000 00000800 00000424
                @0x000000009600309c fcad7800 00000400 00000424
                @0x00000000960030a8 fcad8000 00000800 00000424
                @0x00000000960030b4 fcad8800 00000400 00000424
                @0x00000000960030c0 fcad9000 00000800 00000424
                @0x00000000960030cc fcad9800 00000400 00000424
                @0x00000000960030d8 fcada000 00000800 00000424
                @0x00000000960030e4 fcada800 00000400 00000424
                @0x00000000960030f0 fcadb000 00000800 00000424
                @0x00000000960030fc fcadb800 00000400 00000424
                @0x0000000096003108 fcadc000 00000800 00000424
                @0x0000000096003114 fcadc800 00000400 00000424
                @0x0000000096003120 fcadd000 00000800 00000424
                @0x000000009600312c fcadd800 00000400 00000424
                @0x0000000096003138 fcade000 00000800 00000424
                @0x0000000096003144 fcade800 00000400 00000424
                @0x0000000096003150 fcadf000 00000800 00000424
                @0x000000009600315c fcadf800 00000400 00000424
                @0x0000000096003168 fcae0000 00000800 00000424
                @0x0000000096003174 fcab9000 0000001f 00000425
                @0x0000000096003180 fcaba800 0000001f 00000425
                @0x000000009600318c fcabc000 0000001f 00000425
                @0x0000000096003198 fcabd800 0000001f 00000425
                @0x00000000960031a4 fcabf000 0000001f 00000425
                @0x00000000960031b0 fcac1000 0000001f 00000425
                @0x00000000960031bc fcac2000 0000001f 00000425
                @0x00000000960031c8 fcac3800 0000001f 00000425
                @0x00000000960031d4 96003000 00000000 00001813

 irq/408-5b13000-1696  [002] d..1   326.043911: cdns3_doorbell_epx: //Ding Dong ep1out, ep_trbaddr 96003168
    file-storage-3834  [003] d..1   326.043916: cdns3_ep_queue: ep1out: req: ffff800836c59f00, length: 0/1024 , status
: -115, trb: [start:29, end:29: virt addr ffff00000a53015c], flags:0 
    file-storage-3834  [003] d..1   326.043923: cdns3_prepare_trb: ep1out: trb ffff00000a530174, dma buf: 0xfcae0800, 
size: 1024, ctrl: 0x00000424 (C=0, T=0, ISP, IOC, Normal)
    file-storage-3834  [003] d..1   326.043928: cdns3_ring: 
                Ring contents for ep1out:
                Ring deq index: 29, trb: ffff00000a53015c (virt), 0x9600315c (dma)
                Ring enq index: 32, trb: ffff00000a530180 (virt), 0x96003180 (dma)
                free trbs: 36, CCS=0, PCS=0
                @0x0000000096003000 fcac5000 0000001f 00000424
                @0x000000009600300c fcac6000 00004000 00000424
                @0x0000000096003018 fcaca000 00004000 00000424
                @0x0000000096003024 fcace000 00000800 00000424
                @0x0000000096003030 fcace800 00000800 00000424
                @0x000000009600303c fcacf000 00003e00 00000424
                @0x0000000096003048 fcad3000 0000001f 00000424
                @0x0000000096003054 fcad4800 00000400 00000424
                @0x0000000096003060 fcad5000 00000800 00000424
                @0x000000009600306c fcad5800 00000400 00000424
                @0x0000000096003078 fcad6000 00000800 00000424
                @0x0000000096003084 fcad6800 00000400 00000424
                @0x0000000096003090 fcad7000 00000800 00000424
                @0x000000009600309c fcad7800 00000400 00000424
                @0x00000000960030a8 fcad8000 00000800 00000424
                @0x00000000960030b4 fcad8800 00000400 00000424
                @0x00000000960030c0 fcad9000 00000800 00000424
                @0x00000000960030cc fcad9800 00000400 00000424
                @0x00000000960030d8 fcada000 00000800 00000424
                @0x00000000960030e4 fcada800 00000400 00000424
                @0x00000000960030f0 fcadb000 00000800 00000424
                @0x00000000960030fc fcadb800 00000400 00000424
                @0x0000000096003108 fcadc000 00000800 00000424
                @0x0000000096003114 fcadc800 00000400 00000424
                @0x0000000096003120 fcadd000 00000800 00000424
                @0x000000009600312c fcadd800 00000400 00000424
                @0x0000000096003138 fcade000 00000800 00000424
                @0x0000000096003144 fcade800 00000400 00000424
                @0x0000000096003150 fcadf000 00000800 00000424
                @0x000000009600315c fcadf800 00000400 00000424
                @0x0000000096003168 fcae0000 00000800 00000424
                @0x0000000096003174 fcae0800 00000400 00000424
                @0x0000000096003180 fcaba800 0000001f 00000425
                @0x000000009600318c fcabc000 0000001f 00000425
                @0x0000000096003198 fcabd800 0000001f 00000425
                @0x00000000960031a4 fcabf000 0000001f 00000425
                @0x00000000960031b0 fcac1000 0000001f 00000425
                @0x00000000960031bc fcac2000 0000001f 00000425
                @0x00000000960031c8 fcac3800 0000001f 00000425
                @0x00000000960031d4 96003000 00000000 00001813

    file-storage-3834  [003] d..1   326.043936: cdns3_doorbell_epx: //Ding Dong ep1out, ep_trbaddr 96003174
 irq/408-5b13000-1696  [002] d..1   326.043951: cdns3_epx_irq: IRQ for ep1out: 11010014 IOC DESCMIS , ep_traddr: 96003
180
 irq/408-5b13000-1696  [002] d..1   326.043955: cdns3_request_handled: Req: ffff800836ed4300 handled, DMA pos: 32, ep 
deq: 29, ep enq: 32, start trb: 30, end trb: 30
 irq/408-5b13000-1696  [002] d..1   326.043957: cdns3_complete_trb: ep1out: trb ffff00000a53015c, dma buf: 0xfcadf800,
 size: 1024, ctrl: 0x00000424 (C=0, T=0, ISP, IOC, Normal)
 irq/408-5b13000-1696  [002] d..1   326.052955: cdns3_gadget_giveback: ep1out: req: ffff800836ed4300, length: 1024/204
8 , status: 0, trb: [start:30, end:30: virt addr ffff00000a530168], flags:2 
 irq/408-5b13000-1696  [002] d..1   326.052958: cdns3_request_handled: Req: ffff800836c59f00 handled, DMA pos: 32, ep 
deq: 30, ep enq: 32, start trb: 31, end trb: 31
 irq/408-5b13000-1696  [002] d..1   326.052959: cdns3_complete_trb: ep1out: trb ffff00000a530168, dma buf: 0xfcae0000,
 size: 2048, ctrl: 0x00000424 (C=0, T=0, ISP, IOC, Normal)
 irq/408-5b13000-1696  [002] d..1   326.061932: cdns3_gadget_giveback: ep1out: req: ffff800836c59f00, length: 2048/102
4 , status: 0, trb: [start:31, end:31: virt addr ffff00000a530174], flags:0 
 irq/408-5b13000-1696  [002] d..1   326.061957: cdns3_alloc_request: ep1out: req: ffff800836ed4b00, length: 0/0 , stat
us: 0, trb: [start:0, end:0: virt addr           (null)], flags:0 
 irq/408-5b13000-1696  [002] d..1   326.061961: cdns3_ep_queue: ep1out: req: ffff800836ed4b00, length: 0/2048 , status
: -115, trb: [start:0, end:0: virt addr           (null)], flags:2 
 irq/408-5b13000-1696  [002] d..1   326.061968: cdns3_prepare_trb: ep1out: trb ffff00000a530180, dma buf: 0xfcae1000, 
size: 2048, ctrl: 0x00000424 (C=0, T=0, ISP, IOC, Normal)
 irq/408-5b13000-1696  [002] d..1   326.061972: cdns3_ring: 
                Ring contents for ep1out:
                Ring deq index: 31, trb: ffff00000a530174 (virt), 0x96003174 (dma)
                Ring enq index: 33, trb: ffff00000a53018c (virt), 0x9600318c (dma)
                free trbs: 37, CCS=0, PCS=0
                @0x0000000096003000 fcac5000 0000001f 00000424
                @0x000000009600300c fcac6000 00004000 00000424
                @0x0000000096003018 fcaca000 00004000 00000424
                @0x0000000096003024 fcace000 00000800 00000424
                @0x0000000096003030 fcace800 00000800 00000424
                @0x000000009600303c fcacf000 00003e00 00000424
                @0x0000000096003048 fcad3000 0000001f 00000424
                @0x0000000096003054 fcad4800 00000400 00000424
                @0x0000000096003060 fcad5000 00000800 00000424
                @0x000000009600306c fcad5800 00000400 00000424
                @0x0000000096003078 fcad6000 00000800 00000424
                @0x0000000096003084 fcad6800 00000400 00000424
                @0x0000000096003090 fcad7000 00000800 00000424
                @0x000000009600309c fcad7800 00000400 00000424
                @0x00000000960030a8 fcad8000 00000800 00000424
                @0x00000000960030b4 fcad8800 00000400 00000424
                @0x00000000960030c0 fcad9000 00000800 00000424
                @0x00000000960030cc fcad9800 00000400 00000424
                @0x00000000960030d8 fcada000 00000800 00000424
                @0x00000000960030e4 fcada800 00000400 00000424
                @0x00000000960030f0 fcadb000 00000800 00000424
                @0x00000000960030fc fcadb800 00000400 00000424
                @0x0000000096003108 fcadc000 00000800 00000424
                @0x0000000096003114 fcadc800 00000400 00000424
                @0x0000000096003120 fcadd000 00000800 00000424
                @0x000000009600312c fcadd800 00000400 00000424
                @0x0000000096003138 fcade000 00000800 00000424
                @0x0000000096003144 fcade800 00000400 00000424
                @0x0000000096003150 fcadf000 00000800 00000424
                @0x000000009600315c fcadf800 00000400 00000424
                @0x0000000096003168 fcae0000 00000800 00000424
                @0x0000000096003174 fcae0800 00000400 00000424
                @0x0000000096003180 fcae1000 00000800 00000424
                @0x000000009600318c fcabc000 0000001f 00000425
                @0x0000000096003198 fcabd800 0000001f 00000425
                @0x00000000960031a4 fcabf000 0000001f 00000425
                @0x00000000960031b0 fcac1000 0000001f 00000425
                @0x00000000960031bc fcac2000 0000001f 00000425
                @0x00000000960031c8 fcac3800 0000001f 00000425
                @0x00000000960031d4 96003000 00000000 00001813

 irq/408-5b13000-1696  [002] d..1   326.061981: cdns3_doorbell_epx: //Ding Dong ep1out, ep_trbaddr 96003180
    file-storage-3834  [003] d..1   326.061985: cdns3_ep_queue: ep1out: req: ffff800836c59f00, length: 0/1024 , status
: -115, trb: [start:31, end:31: virt addr ffff00000a530174], flags:0 
    file-storage-3834  [003] d..1   326.061992: cdns3_prepare_trb: ep1out: trb ffff00000a53018c, dma buf: 0xfcae1800, 
size: 1024, ctrl: 0x00000424 (C=0, T=0, ISP, IOC, Normal)
    file-storage-3834  [003] d..1   326.061997: cdns3_ring: 
                Ring contents for ep1out:
                Ring deq index: 31, trb: ffff00000a530174 (virt), 0x96003174 (dma)
                Ring enq index: 34, trb: ffff00000a530198 (virt), 0x96003198 (dma)
                free trbs: 36, CCS=0, PCS=0

^ permalink raw reply	[flat|nested] 49+ messages in thread

* Re: [PATCH v2 5/5] usb:cdns3 Add Cadence USB3 DRD Driver
  2018-12-23 15:13   ` [PATCH v2 5/5] " Pawel Laszczak
                     ` (5 preceding siblings ...)
  (?)
@ 2019-01-10  1:30   ` Peter Chen
  2019-01-10  6:57       ` [v2,5/5] " Greg Kroah-Hartman
  -1 siblings, 1 reply; 49+ messages in thread
From: Peter Chen @ 2019-01-10  1:30 UTC (permalink / raw)
  To: Pawel Laszczak
  Cc: devicetree, Greg Kroah-Hartman, mark.rutland, linux-usb,
	hdegoede, Heikki Krogerus, andy.shevchenko, robh+dt, rogerq,
	lkml, adouglas, jbergsagel, nsekhar, nm, sureshp, peter.chen,
	pjez, kurahul

[-- Attachment #1: Type: text/plain, Size: 236856 bytes --]

On Mon, Dec 24, 2018 at 12:44 AM Pawel Laszczak <pawell@cadence.com> wrote:
>
> This patch introduce new Cadence USBSS DRD driver
> to linux kernel.
>
> The Cadence USBSS DRD Driver is a highly
> configurable IP Core which can be
> instantiated as Dual-Role Device (DRD),
> Peripheral Only and Host Only (XHCI)
> configurations.
>
> The current driver has been validated with
> FPGA burned. We have support for PCIe
> bus, which is used on FPGA prototyping.
>
> The host side of USBSS-DRD controller is compliance
> with XHCI specification, so it works with
> standard XHCI linux driver.
>
> Signed-off-by: Pawel Laszczak <pawell@cadence.com>
> ---
>  drivers/usb/Kconfig                |    2 +
>  drivers/usb/Makefile               |    2 +
>  drivers/usb/cdns3/Kconfig          |   44 +
>  drivers/usb/cdns3/Makefile         |   16 +
>  drivers/usb/cdns3/cdns3-pci-wrap.c |  157 +++
>  drivers/usb/cdns3/core.c           |  406 ++++++
>  drivers/usb/cdns3/core.h           |  116 ++
>  drivers/usb/cdns3/debug.h          |  166 +++
>  drivers/usb/cdns3/debugfs.c        |  168 +++
>  drivers/usb/cdns3/drd.c            |  350 +++++
>  drivers/usb/cdns3/drd.h            |  162 +++

Hi Pawel,

- I reviewed your dual-switch flow, and the handling for dr_mode, it
is a little complicated.
In fact, dr_mode is static, it is only used to decide which role
driver (host/gadget) needs
to be created, after that, we use variable cdns->role to track the current role.

- I find the strap value is always device mode at my platform, so I
delete its handling,
meanwhile, I keep id handling and add vbus handling for you use, and
add external connector
support for my platform.

- runtime power manage is not supported at this version, so I delete
related code at core.c
and drd.c.

- debugfs is nice to have feature, I suggest removing it at this
initial version. Besides, role switch
through /sys is normal feature, the end user may use it at real
product, so, it is better at device's
/sys entry instead of debugfs.

- I don't know why you add "disable" at debugfs, please comment.

My changes for drd flow is attached for your reference, It works well
at my platform,
and I attached my local code base too.

Other main changes:
diff --git a/drivers/usb/cdns3/trace.h b/drivers/usb/cdns3/trace.h
index f3d7a91fae86..8e97575a6b01 100644
--- a/drivers/usb/cdns3/trace.h
+++ b/drivers/usb/cdns3/trace.h
@@ -179,7 +179,7 @@ DECLARE_EVENT_CLASS(cdns3_log_request,
                __entry->flags = req->flags;
        ),
        TP_printk("%s: req: %p, length: %u/%u %s%s%s, status: %d,"
-                 " trb: [start:%d, end:%d: virt addr %pa], flags:%x ",
+                 " trb: [start:%d, end:%d: virt addr 0x%p], flags:%x ",
                __get_str(name), __entry->req, __entry->actual, __entry->length,
                __entry->zero ? "zero | " : "",
                __entry->short_not_ok ? "short | " : "",
@@ -236,7 +236,7 @@ DECLARE_EVENT_CLASS(cdns3_log_trb,
                __entry->control = trb->control;
                __entry->type = usb_endpoint_type(priv_ep->endpoint.desc);
        ),
-       TP_printk("%s: trb 0x%pa, dma buf: 0x%08x, size: %ld, ctrl:
0x%08x (%s%s%s%s%s%s%s)",
+       TP_printk("%s: trb 0x%p, dma buf: 0x%08x, size: %ld, ctrl:
0x%08x (%s%s%s%s%s%s%s)",
                __get_str(name), __entry->trb, __entry->buffer,
                TRB_LEN(__entry->length), __entry->control,
                __entry->control & TRB_CYCLE ? "C=1, " : "C=0, ",

%pa is used for physical address, the former code print zero value.

@@ -951,6 +996,9 @@ static irqreturn_t cdns3_device_irq_handler(int
irq, void *data)
        unsigned long flags;
        u32 reg;

+       if (cdns->role != CDNS3_ROLE_GADGET)
+               return ret;
+

Need to add judgement, otherwise, host interrupt will cause oops.

Peter

>  drivers/usb/cdns3/ep0.c            |  896 ++++++++++++
>  drivers/usb/cdns3/gadget-export.h  |   28 +
>  drivers/usb/cdns3/gadget.c         | 2102 ++++++++++++++++++++++++++++
>  drivers/usb/cdns3/gadget.h         | 1206 ++++++++++++++++
>  drivers/usb/cdns3/host-export.h    |   28 +
>  drivers/usb/cdns3/host.c           |   72 +
>  drivers/usb/cdns3/trace.c          |   11 +
>  drivers/usb/cdns3/trace.h          |  389 +++++
>  19 files changed, 6321 insertions(+)
>  create mode 100644 drivers/usb/cdns3/Kconfig
>  create mode 100644 drivers/usb/cdns3/Makefile
>  create mode 100644 drivers/usb/cdns3/cdns3-pci-wrap.c
>  create mode 100644 drivers/usb/cdns3/core.c
>  create mode 100644 drivers/usb/cdns3/core.h
>  create mode 100644 drivers/usb/cdns3/debug.h
>  create mode 100644 drivers/usb/cdns3/debugfs.c
>  create mode 100644 drivers/usb/cdns3/drd.c
>  create mode 100644 drivers/usb/cdns3/drd.h
>  create mode 100644 drivers/usb/cdns3/ep0.c
>  create mode 100644 drivers/usb/cdns3/gadget-export.h
>  create mode 100644 drivers/usb/cdns3/gadget.c
>  create mode 100644 drivers/usb/cdns3/gadget.h
>  create mode 100644 drivers/usb/cdns3/host-export.h
>  create mode 100644 drivers/usb/cdns3/host.c
>  create mode 100644 drivers/usb/cdns3/trace.c
>  create mode 100644 drivers/usb/cdns3/trace.h
>
> diff --git a/drivers/usb/Kconfig b/drivers/usb/Kconfig
> index 987fc5ba6321..5f9334019d04 100644
> --- a/drivers/usb/Kconfig
> +++ b/drivers/usb/Kconfig
> @@ -112,6 +112,8 @@ source "drivers/usb/usbip/Kconfig"
>
>  endif
>
> +source "drivers/usb/cdns3/Kconfig"
> +
>  source "drivers/usb/mtu3/Kconfig"
>
>  source "drivers/usb/musb/Kconfig"
> diff --git a/drivers/usb/Makefile b/drivers/usb/Makefile
> index 7d1b8c82b208..ab125b966cac 100644
> --- a/drivers/usb/Makefile
> +++ b/drivers/usb/Makefile
> @@ -12,6 +12,8 @@ obj-$(CONFIG_USB_DWC3)                += dwc3/
>  obj-$(CONFIG_USB_DWC2)         += dwc2/
>  obj-$(CONFIG_USB_ISP1760)      += isp1760/
>
> +obj-$(CONFIG_USB_CDNS3)                += cdns3/
> +
>  obj-$(CONFIG_USB_MON)          += mon/
>  obj-$(CONFIG_USB_MTU3)         += mtu3/
>
> diff --git a/drivers/usb/cdns3/Kconfig b/drivers/usb/cdns3/Kconfig
> new file mode 100644
> index 000000000000..4adfd87811e8
> --- /dev/null
> +++ b/drivers/usb/cdns3/Kconfig
> @@ -0,0 +1,44 @@
> +config USB_CDNS3
> +       tristate "Cadence USB3 Dual-Role Controller"
> +       depends on USB_SUPPORT && (USB || USB_GADGET) && HAS_DMA
> +       help
> +         Say Y here if your system has a cadence USB3 dual-role controller.
> +         It supports: dual-role switch, Host-only, and Peripheral-only.
> +
> +         If you choose to build this driver is a dynamically linked
> +         module, the module will be called cdns3.ko.
> +
> +if USB_CDNS3
> +
> +config USB_CDNS3_GADGET
> +        bool "Cadence USB3 device controller"
> +        depends on USB_GADGET
> +        help
> +          Say Y here to enable device controller functionality of the
> +          cadence USBSS-DEV driver.
> +
> +          This controller supports FF, HS and SS mode. It doesn't support
> +          LS and SSP mode
> +
> +config USB_CDNS3_HOST
> +        bool "Cadence USB3 host controller"
> +        depends on USB_XHCI_HCD
> +        help
> +          Say Y here to enable host controller functionality of the
> +          cadence driver.
> +
> +          Host controller is compliance with XHCI so it will use
> +          standard XHCI driver.
> +
> +config USB_CDNS3_PCI_WRAP
> +       tristate "Cadence USB3 support on PCIe-based platforms"
> +       depends on USB_PCI && ACPI
> +       default USB_CDNS3
> +       help
> +         If you're using the USBSS Core IP with a PCIe, please say
> +         'Y' or 'M' here.
> +
> +         If you choose to build this driver as module it will
> +         be dynamically linked and module will be called cdns3-pci.ko
> +
> +endif
> diff --git a/drivers/usb/cdns3/Makefile b/drivers/usb/cdns3/Makefile
> new file mode 100644
> index 000000000000..3f63baa24294
> --- /dev/null
> +++ b/drivers/usb/cdns3/Makefile
> @@ -0,0 +1,16 @@
> +# SPDX-License-Identifier: GPL-2.0
> +# define_trace.h needs to know how to find our header
> +CFLAGS_trace.o                         := -I$(src)
> +
> +obj-$(CONFIG_USB_CDNS3)                        += cdns3.o
> +obj-$(CONFIG_USB_CDNS3_PCI_WRAP)       += cdns3-pci.o
> +
> +cdns3-y                                        := core.o drd.o trace.o
> +
> +ifneq ($(CONFIG_DEBUG_FS),)
> +       cdns3-y                         += debugfs.o
> +endif
> +
> +cdns3-$(CONFIG_USB_CDNS3_GADGET)       += gadget.o ep0.o
> +cdns3-$(CONFIG_USB_CDNS3_HOST)         += host.o
> +cdns3-pci-y                            := cdns3-pci-wrap.o
> diff --git a/drivers/usb/cdns3/cdns3-pci-wrap.c b/drivers/usb/cdns3/cdns3-pci-wrap.c
> new file mode 100644
> index 000000000000..e93179c45ece
> --- /dev/null
> +++ b/drivers/usb/cdns3/cdns3-pci-wrap.c
> @@ -0,0 +1,157 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Cadence USBSS PCI Glue driver
> + *
> + * Copyright (C) 2018 Cadence.
> + *
> + * Author: Pawel Laszczak <pawell@cadence.com>
> + */
> +
> +#include <linux/kernel.h>
> +#include <linux/module.h>
> +#include <linux/pci.h>
> +#include <linux/platform_device.h>
> +#include <linux/dma-mapping.h>
> +#include <linux/slab.h>
> +
> +struct cdns3_wrap {
> +       struct platform_device *plat_dev;
> +       struct pci_dev *hg_dev;
> +       struct resource dev_res[4];
> +};
> +
> +struct cdns3_wrap wrap;
> +
> +#define RES_IRQ_ID             0
> +#define RES_HOST_ID            1
> +#define RES_DEV_ID             2
> +#define RES_DRD_ID             3
> +
> +#define PCI_BAR_HOST           0
> +#define PCI_BAR_DEV            2
> +#define PCI_BAR_OTG            4
> +
> +#define PCI_DEV_FN_HOST_DEVICE 0
> +#define PCI_DEV_FN_OTG         1
> +
> +#define PCI_DRIVER_NAME                "cdns3-pci-usbss"
> +#define PLAT_DRIVER_NAME       "cdns-usb3"
> +
> +#define CDNS_VENDOR_ID 0x17cd
> +#define CDNS_DEVICE_ID 0x0100
> +
> +/**
> + * cdns3_pci_probe - Probe function for Cadence USB wrapper driver
> + * @pdev: platform device object
> + * @id: pci device id
> + *
> + * Returns 0 on success otherwise negative errno
> + */
> +static int cdns3_pci_probe(struct pci_dev *pdev,
> +                          const struct pci_device_id *id)
> +{
> +       struct platform_device_info plat_info;
> +       struct cdns3_wrap *wrap;
> +       struct resource *res;
> +       int err;
> +
> +       /*
> +        * for GADGET/HOST PCI (devfn) function number is 0,
> +        * for OTG PCI (devfn) function number is 1
> +        */
> +       if (!id || pdev->devfn != PCI_DEV_FN_HOST_DEVICE)
> +               return -EINVAL;
> +
> +       err = pcim_enable_device(pdev);
> +       if (err) {
> +               dev_err(&pdev->dev, "Enabling PCI device has failed %d\n", err);
> +               return err;
> +       }
> +
> +       pci_set_master(pdev);
> +       wrap = devm_kzalloc(&pdev->dev, sizeof(*wrap), GFP_KERNEL);
> +       if (!wrap) {
> +               dev_err(&pdev->dev, "Failed to allocate memory\n");
> +               return -ENOMEM;
> +       }
> +
> +       /* function 0: host(BAR_0) + device(BAR_1) + otg(BAR_2)). */
> +       memset(wrap->dev_res, 0x00,
> +              sizeof(struct resource) * ARRAY_SIZE(wrap->dev_res));
> +       dev_dbg(&pdev->dev, "Initialize Device resources\n");
> +       res = wrap->dev_res;
> +
> +       res[RES_DEV_ID].start = pci_resource_start(pdev, PCI_BAR_DEV);
> +       res[RES_DEV_ID].end =   pci_resource_end(pdev, PCI_BAR_DEV);
> +       res[RES_DEV_ID].name = "cdns3-dev-regs";
> +       res[RES_DEV_ID].flags = IORESOURCE_MEM;
> +       dev_dbg(&pdev->dev, "USBSS-DEV physical base addr: %pa\n",
> +               &res[RES_DEV_ID].start);
> +
> +       res[RES_HOST_ID].start = pci_resource_start(pdev, PCI_BAR_HOST);
> +       res[RES_HOST_ID].end = pci_resource_end(pdev, PCI_BAR_HOST);
> +       res[RES_HOST_ID].name = "cdns3-xhci-regs";
> +       res[RES_HOST_ID].flags = IORESOURCE_MEM;
> +       dev_dbg(&pdev->dev, "USBSS-XHCI physical base addr: %pa\n",
> +               &res[RES_HOST_ID].start);
> +
> +       res[RES_DRD_ID].start = pci_resource_start(pdev, PCI_BAR_OTG);
> +       res[RES_DRD_ID].end =   pci_resource_end(pdev, PCI_BAR_OTG);
> +       res[RES_DRD_ID].name = "cdns3-otg";
> +       res[RES_DRD_ID].flags = IORESOURCE_MEM;
> +       dev_dbg(&pdev->dev, "USBSS-DRD physical base addr: %pa\n",
> +               &res[RES_DRD_ID].start);
> +
> +       /* Interrupt common for both device and XHCI */
> +       wrap->dev_res[RES_IRQ_ID].start = pdev->irq;
> +       wrap->dev_res[RES_IRQ_ID].name = "cdns3-irq";
> +       wrap->dev_res[RES_IRQ_ID].flags = IORESOURCE_IRQ;
> +
> +       /* set up platform device info */
> +       memset(&plat_info, 0, sizeof(plat_info));
> +       plat_info.parent = &pdev->dev;
> +       plat_info.fwnode = pdev->dev.fwnode;
> +       plat_info.name = PLAT_DRIVER_NAME;
> +       plat_info.id = pdev->devfn;
> +       plat_info.res = wrap->dev_res;
> +       plat_info.num_res = ARRAY_SIZE(wrap->dev_res);
> +       plat_info.dma_mask = pdev->dma_mask;
> +
> +       /* register platform device */
> +       wrap->plat_dev = platform_device_register_full(&plat_info);
> +       if (IS_ERR(wrap->plat_dev)) {
> +               err = PTR_ERR(wrap->plat_dev);
> +               return err;
> +       }
> +
> +       pci_set_drvdata(pdev, wrap);
> +
> +       return err;
> +}
> +
> +void cdns3_pci_remove(struct pci_dev *pdev)
> +{
> +       struct cdns3_wrap *wrap = (struct cdns3_wrap *)pci_get_drvdata(pdev);
> +
> +       platform_device_unregister(wrap->plat_dev);
> +}
> +
> +static const struct pci_device_id cdns3_pci_ids[] = {
> +       { PCI_DEVICE(CDNS_VENDOR_ID, CDNS_DEVICE_ID), },
> +       { 0, }
> +};
> +
> +static struct pci_driver cdns3_pci_driver = {
> +       .name = PCI_DRIVER_NAME,
> +       .id_table = cdns3_pci_ids,
> +       .probe = cdns3_pci_probe,
> +       .remove = cdns3_pci_remove,
> +};
> +
> +module_pci_driver(cdns3_pci_driver);
> +MODULE_DEVICE_TABLE(pci, cdns3_pci_ids);
> +
> +MODULE_AUTHOR("Pawel Laszczak <pawell@cadence.com>");
> +MODULE_LICENSE("GPL v2");
> +MODULE_DESCRIPTION("Cadence USBSS PCI wrapperr");
> +
> diff --git a/drivers/usb/cdns3/core.c b/drivers/usb/cdns3/core.c
> new file mode 100644
> index 000000000000..d274586aca36
> --- /dev/null
> +++ b/drivers/usb/cdns3/core.c
> @@ -0,0 +1,406 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Cadence USBSS DRD Driver.
> + *
> + * Copyright (C) 2018 Cadence.
> + * Copyright (C) 2017-2018 NXP
> + *
> + * Author: Peter Chen <peter.chen@nxp.com>
> + *         Pawel Laszczak <pawell@cadence.com>
> + */
> +
> +#include <linux/module.h>
> +#include <linux/kernel.h>
> +#include <linux/platform_device.h>
> +#include <linux/interrupt.h>
> +#include <linux/io.h>
> +#include <linux/pm_runtime.h>
> +
> +#include "gadget.h"
> +#include "core.h"
> +#include "host-export.h"
> +#include "gadget-export.h"
> +#include "drd.h"
> +#include "debug.h"
> +
> +static inline
> +struct cdns3_role_driver *cdns3_get_current_role_driver(struct cdns3 *cdns)
> +{
> +       WARN_ON(cdns->role >= CDNS3_ROLE_END || !cdns->roles[cdns->role]);
> +       return cdns->roles[cdns->role];
> +}
> +
> +static inline int cdns3_role_start(struct cdns3 *cdns, enum cdns3_roles role)
> +{
> +       int ret;
> +
> +       if (WARN_ON(role >= CDNS3_ROLE_END))
> +               return 0;
> +
> +       if (!cdns->roles[role])
> +               return -ENXIO;
> +
> +       if (cdns->roles[role]->state == CDNS3_ROLE_STATE_ACTIVE)
> +               return 0;
> +
> +       mutex_lock(&cdns->mutex);
> +       cdns->role = role;
> +       ret = cdns->roles[role]->start(cdns);
> +       if (!ret)
> +               cdns->roles[role]->state = CDNS3_ROLE_STATE_ACTIVE;
> +       mutex_unlock(&cdns->mutex);
> +       return ret;
> +}
> +
> +void cdns3_role_stop(struct cdns3 *cdns)
> +{
> +       enum cdns3_roles role = cdns->role;
> +
> +       if (role >= CDNS3_ROLE_END) {
> +               WARN_ON(role > CDNS3_ROLE_END);
> +               return;
> +       }
> +
> +       if (cdns->roles[role]->state == CDNS3_ROLE_STATE_INACTIVE)
> +               return;
> +
> +       mutex_lock(&cdns->mutex);
> +       cdns->roles[role]->stop(cdns);
> +       cdns->roles[role]->state = CDNS3_ROLE_STATE_INACTIVE;
> +       mutex_unlock(&cdns->mutex);
> +}
> +
> +/*
> + * cdns->role gets from cdns3_get_initial_role, and this API tells role at the
> + * runtime.
> + * If both roles are supported, the role is selected based on vbus/id.
> + * It could be read from OTG register or external connector.
> + * If only single role is supported, only one role structure
> + * is allocated, cdns->roles[CDNS3_ROLE_HOST] or cdns->roles[CDNS3_ROLE_GADGET].
> + */
> +static enum cdns3_roles cdns3_get_initial_role(struct cdns3 *cdns)
> +{
> +       if (cdns->roles[CDNS3_ROLE_HOST] && cdns->roles[CDNS3_ROLE_GADGET]) {
> +               if (cdns3_is_host(cdns))
> +                       return CDNS3_ROLE_HOST;
> +               if (cdns3_is_device(cdns))
> +                       return CDNS3_ROLE_GADGET;
> +       }
> +       return cdns->roles[CDNS3_ROLE_HOST]
> +               ? CDNS3_ROLE_HOST
> +               : CDNS3_ROLE_GADGET;
> +}
> +
> +static void cdns3_exit_roles(struct cdns3 *cdns)
> +{
> +       cdns3_role_stop(cdns);
> +       cdns3_drd_exit(cdns);
> +}
> +
> +/**
> + * cdns3_core_init_role - initialize role of operation
> + * @cdns: Pointer to cdns3 structure
> + *
> + * Returns 0 on success otherwise negative errno
> + */
> +static int cdns3_core_init_role(struct cdns3 *cdns)
> +{
> +       struct device *dev = cdns->dev;
> +       enum usb_dr_mode best_dr_mode;
> +       enum usb_dr_mode dr_mode;
> +       int ret = 0;
> +
> +       dr_mode = usb_get_dr_mode(dev);
> +       cdns->role = CDNS3_ROLE_END;
> +
> +       /*
> +        * If driver can't read mode by means of usb_get_dr_mdoe function then
> +        * chooses mode according with Kernel configuration. This setting
> +        * can be restricted later depending on strap pin configuration.
> +        */
> +       if (dr_mode == USB_DR_MODE_UNKNOWN) {
> +               if (IS_ENABLED(CONFIG_USB_CDNS3_HOST) &&
> +                   IS_ENABLED(CONFIG_USB_CDNS3_GADGET))
> +                       dr_mode = USB_DR_MODE_OTG;
> +               else if (IS_ENABLED(CONFIG_USB_CDNS3_HOST))
> +                       dr_mode = USB_DR_MODE_HOST;
> +               else if (IS_ENABLED(CONFIG_USB_CDNS3_GADGET))
> +                       dr_mode = USB_DR_MODE_PERIPHERAL;
> +       }
> +
> +       best_dr_mode = USB_DR_MODE_OTG;
> +
> +       if (dr_mode == USB_DR_MODE_OTG) {
> +               best_dr_mode = cdns->dr_mode;
> +       } else if (cdns->dr_mode == USB_DR_MODE_OTG) {
> +               best_dr_mode = dr_mode;
> +       } else if (cdns->dr_mode != dr_mode) {
> +               dev_err(dev, "Incorrect DRD configuration\n");
> +               return -EINVAL;
> +       }
> +
> +       dr_mode = best_dr_mode;
> +
> +       if (dr_mode == USB_DR_MODE_OTG || dr_mode == USB_DR_MODE_HOST) {
> +               ret = cdns3_host_init(cdns);
> +               if (ret) {
> +                       dev_err(dev, "Host initialization failed with %d\n",
> +                               ret);
> +                       goto err;
> +               }
> +       }
> +
> +       if (dr_mode == USB_DR_MODE_OTG || dr_mode == USB_DR_MODE_PERIPHERAL) {
> +               ret = cdns3_gadget_init(cdns);
> +               if (ret) {
> +                       dev_err(dev, "Device initialization failed with %d\n",
> +                               ret);
> +                       goto err;
> +               }
> +       }
> +
> +       cdns->desired_dr_mode = dr_mode;
> +       cdns->dr_mode = dr_mode;
> +       /*
> +        * dr_mode could be change so DRD must update controller
> +        * configuration
> +        */
> +       ret = cdns3_drd_update_mode(cdns);
> +
> +       cdns->role = cdns3_get_initial_role(cdns);
> +
> +       ret = cdns3_role_start(cdns, cdns->role);
> +       if (ret) {
> +               dev_err(dev, "can't start %s role\n",
> +                       cdns3_get_current_role_driver(cdns)->name);
> +               goto err;
> +       }
> +
> +       return ret;
> +err:
> +       cdns3_exit_roles(cdns);
> +       return ret;
> +}
> +
> +/**
> + * cdsn3_get_real_role - get real role of controller based on hardware settings.
> + * @cdns: Pointer to cdns3 structure
> + *
> + * Returns role
> + */
> +enum cdns3_roles cdsn3_get_real_role(struct cdns3 *cdns)
> +{
> +       enum cdns3_roles role = CDNS3_ROLE_END;
> +
> +       if (cdns->current_dr_mode == USB_DR_MODE_OTG) {
> +               if (cdns3_get_id(cdns))
> +                       role = CDNS3_ROLE_GADGET;
> +               else
> +                       role = CDNS3_ROLE_HOST;
> +       } else {
> +               if (cdns3_is_host(cdns))
> +                       role = CDNS3_ROLE_HOST;
> +               if (cdns3_is_device(cdns))
> +                       role = CDNS3_ROLE_GADGET;
> +       }
> +
> +       return role;
> +}
> +
> +/**
> + * cdns3_role_switch - work queue handler for role switch
> + *
> + * @work: work queue item structure
> + *
> + * Handles below events:
> + * - Role switch for dual-role devices
> + * - CDNS3_ROLE_GADGET <--> CDNS3_ROLE_END for peripheral-only devices
> + */
> +static void cdns3_role_switch(struct work_struct *work)
> +{
> +       enum cdns3_roles role = CDNS3_ROLE_END;
> +       struct cdns3_role_driver *role_drv;
> +       enum cdns3_roles current_role;
> +       struct cdns3 *cdns;
> +       int ret = 0;
> +
> +       cdns = container_of(work, struct cdns3, role_switch_wq);
> +
> +       /* During switching cdns->role can be different then role */
> +       role = cdsn3_get_real_role(cdns);
> +
> +       role_drv = cdns3_get_current_role_driver(cdns);
> +
> +       pm_runtime_get_sync(cdns->dev);
> +
> +       /* Disable current role. This state can be forced from user space. */
> +       if (cdns->debug_disable && role_drv->state == CDNS3_ROLE_STATE_ACTIVE) {
> +               cdns3_role_stop(cdns);
> +               goto exit;
> +       }
> +
> +       /* Do nothing if nothing changed */
> +       if (cdns->role == role && role_drv->state == CDNS3_ROLE_STATE_ACTIVE)
> +               goto exit;
> +
> +       cdns3_role_stop(cdns);
> +
> +       role = cdsn3_get_real_role(cdns);
> +
> +       current_role = cdns->role;
> +       dev_dbg(cdns->dev, "Switching role");
> +
> +       ret = cdns3_role_start(cdns, role);
> +
> +       if (ret) {
> +               /* Back to current role */
> +               dev_err(cdns->dev, "set %d has failed, back to %d\n",
> +                       role, current_role);
> +               cdns3_role_start(cdns, current_role);
> +       }
> +exit:
> +       pm_runtime_put_sync(cdns->dev);
> +}
> +
> +/**
> + * cdns3_probe - probe for cdns3 core device
> + * @pdev: Pointer to cdns3 core platform device
> + *
> + * Returns 0 on success otherwise negative errno
> + */
> +static int cdns3_probe(struct platform_device *pdev)
> +{
> +       struct device *dev = &pdev->dev;
> +       struct resource *res;
> +       struct cdns3 *cdns;
> +       void __iomem *regs;
> +       int ret;
> +
> +       cdns = devm_kzalloc(dev, sizeof(*cdns), GFP_KERNEL);
> +       if (!cdns)
> +               return -ENOMEM;
> +
> +       cdns->dev = dev;
> +
> +       platform_set_drvdata(pdev, cdns);
> +
> +       res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
> +       if (!res) {
> +               dev_err(dev, "missing IRQ\n");
> +               return -ENODEV;
> +       }
> +       cdns->irq = res->start;
> +
> +       cdns->xhci_res[0] = *res;
> +
> +       /*
> +        * Request memory region
> +        * region-0: xHCI
> +        * region-1: Peripheral
> +        * region-2: OTG registers
> +        */
> +       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +       cdns->xhci_res[1] = *res;
> +
> +       res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
> +       regs = devm_ioremap_resource(dev, res);
> +       if (IS_ERR(regs))
> +               return PTR_ERR(regs);
> +       cdns->dev_regs  = regs;
> +
> +       res = platform_get_resource(pdev, IORESOURCE_MEM, 2);
> +       cdns->otg_res = *res;
> +
> +       mutex_init(&cdns->mutex);
> +
> +       cdns->phy = devm_phy_get(dev, "cdns3,usbphy");
> +       if (IS_ERR(cdns->phy)) {
> +               ret = PTR_ERR(cdns->phy);
> +               if (ret == -ENODEV) {
> +                       cdns->phy = NULL;
> +               } else if (ret == -EPROBE_DEFER) {
> +                       return ret;
> +               } else {
> +                       dev_err(dev, "no phy found\n");
> +                       goto err0;
> +               }
> +       }
> +
> +       phy_init(cdns->phy);
> +
> +       INIT_WORK(&cdns->role_switch_wq, cdns3_role_switch);
> +
> +       ret = cdns3_drd_init(cdns);
> +       if (ret)
> +               goto err1;
> +
> +       ret = cdns3_core_init_role(cdns);
> +       if (ret)
> +               goto err1;
> +
> +       cdns3_debugfs_init(cdns);
> +       device_set_wakeup_capable(dev, true);
> +       pm_runtime_set_active(dev);
> +       pm_runtime_enable(dev);
> +
> +       /*
> +        * The controller needs less time between bus and controller suspend,
> +        * and we also needs a small delay to avoid frequently entering low
> +        * power mode.
> +        */
> +       pm_runtime_set_autosuspend_delay(dev, 20);
> +       pm_runtime_mark_last_busy(dev);
> +       pm_runtime_use_autosuspend(dev);
> +       dev_dbg(dev, "Cadence USB3 core: probe succeed\n");
> +
> +       return 0;
> +
> +err1:
> +       phy_exit(cdns->phy);
> +err0:
> +       return ret;
> +}
> +
> +/**
> + * cdns3_remove - unbind drd driver and clean up
> + * @pdev: Pointer to Linux platform device
> + *
> + * Returns 0 on success otherwise negative errno
> + */
> +static int cdns3_remove(struct platform_device *pdev)
> +{
> +       struct cdns3 *cdns = platform_get_drvdata(pdev);
> +
> +       pm_runtime_get_sync(&pdev->dev);
> +       pm_runtime_disable(&pdev->dev);
> +       pm_runtime_put_noidle(&pdev->dev);
> +       cdns3_debugfs_exit(cdns);
> +       cdns3_exit_roles(cdns);
> +       phy_exit(cdns->phy);
> +       return 0;
> +}
> +
> +#ifdef CONFIG_OF
> +static const struct of_device_id of_cdns3_match[] = {
> +       { .compatible = "cdns,usb3-1.0.0" },
> +       { .compatible = "cdns,usb3-1.0.1" },
> +       { },
> +};
> +MODULE_DEVICE_TABLE(of, of_cdns3_match);
> +#endif
> +
> +static struct platform_driver cdns3_driver = {
> +       .probe          = cdns3_probe,
> +       .remove         = cdns3_remove,
> +       .driver         = {
> +               .name   = "cdns-usb3",
> +               .of_match_table = of_match_ptr(of_cdns3_match),
> +       },
> +};
> +
> +module_platform_driver(cdns3_driver);
> +
> +MODULE_ALIAS("platform:cdns3");
> +MODULE_AUTHOR("Pawel Laszczak <pawell@cadence.com>");
> +MODULE_LICENSE("GPL v2");
> +MODULE_DESCRIPTION("Cadence USB3 DRD Controller Driver");
> diff --git a/drivers/usb/cdns3/core.h b/drivers/usb/cdns3/core.h
> new file mode 100644
> index 000000000000..fb4b39206158
> --- /dev/null
> +++ b/drivers/usb/cdns3/core.h
> @@ -0,0 +1,116 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +/*
> + * Cadence USBSS DRD Header File.
> + *
> + * Copyright (C) 2017-2018 NXP
> + * Copyright (C) 2018 Cadence.
> + *
> + * Authors: Peter Chen <peter.chen@nxp.com>
> + *          Pawel Laszczak <pawell@cadence.com>
> + */
> +#include <linux/usb/otg.h>
> +
> +#ifndef __LINUX_CDNS3_CORE_H
> +#define __LINUX_CDNS3_CORE_H
> +
> +struct cdns3;
> +enum cdns3_roles {
> +       CDNS3_ROLE_HOST = 0,
> +       CDNS3_ROLE_GADGET,
> +       CDNS3_ROLE_END,
> +};
> +
> +/**
> + * struct cdns3_role_driver - host/gadget role driver
> + * @start: start this role
> + * @stop: stop this role
> + * @suspend: suspend callback for this role
> + * @resume: resume callback for this role
> + * @irq: irq handler for this role
> + * @name: role name string (host/gadget)
> + * @state: current state
> + */
> +struct cdns3_role_driver {
> +       int (*start)(struct cdns3 *cdns);
> +       void (*stop)(struct cdns3 *cdns);
> +       int (*suspend)(struct cdns3 *cdns, bool do_wakeup);
> +       int (*resume)(struct cdns3 *cdns, bool hibernated);
> +       const char *name;
> +#define CDNS3_ROLE_STATE_INACTIVE      0
> +#define CDNS3_ROLE_STATE_ACTIVE                1
> +       int state;
> +};
> +
> +#define CDNS3_XHCI_RESOURCES_NUM       2
> +/**
> + * struct cdns3 - Representation of Cadence USB3 DRD controller.
> + * @dev: pointer to Cadence device struct
> + * @xhci_regs: pointer to base of xhci registers
> + * @xhci_res: the resource for xhci
> + * @dev_regs: pointer to base of dev registers
> + * @otg_regs: pointer to base of otg registers
> + * @irq: irq number for controller
> + * @roles: array of supported roles for this controller
> + * @role: current role
> + * @host_dev: the child host device pointer for cdns3 core
> + * @gadget_dev: the child gadget device pointer for cdns3 core
> + * @usb: phy for this controller
> + * @role_switch_wq: work queue item for role switch
> + * @in_lpm: the controller in low power mode
> + * @wakeup_int: the wakeup interrupt
> + * @mutex: the mutex for concurrent code at driver
> + * @dr_mode: supported mode of operation it can be only Host, only Device
> + *           or OTG mode that allow to switch between Device and Host mode.
> + *           This field based on firmware setting, kernel configuration
> + *           and hardware configuration.
> + * @current_dr_mode: current mode of operation when in dual-role mode
> + * @desired_dr_mode: desired mode of operation when in dual-role mode.
> + *           This value can be changed during runtime.
> + *           Available options depends on  dr_mode:
> + *           dr_mode                 |  desired_dr_mode and current_dr_mode
> + *           ----------------------------------------------------------------
> + *           USB_DR_MODE_HOST        | only USB_DR_MODE_HOST
> + *           USB_DR_MODE_PERIPHERAL  | only USB_DR_MODE_PERIPHERAL
> + *           USB_DR_MODE_OTG         | only USB_DR_MODE_HOST
> + *           USB_DR_MODE_OTG         | only USB_DR_MODE_PERIPHERAL
> + *           USB_DR_MODE_OTG         | USB_DR_MODE_OTG
> + *
> + *           Desired_dr_role can be changed by means of debugfs.
> + * @root: debugfs root folder pointer
> + * @debug_disable:
> + */
> +struct cdns3 {
> +       struct device                   *dev;
> +       void __iomem                    *xhci_regs;
> +       struct resource                 xhci_res[CDNS3_XHCI_RESOURCES_NUM];
> +       struct cdns3_usb_regs __iomem   *dev_regs;
> +
> +       struct resource                 otg_res;
> +       struct cdns3_otg_legacy_regs    *otg_v0_regs;
> +       struct cdns3_otg_regs           *otg_v1_regs;
> +       struct cdns3_otg_common_regs    *otg_regs;
> +#define CDNS3_CONTROLLER_V0    0
> +#define CDNS3_CONTROLLER_V1    1
> +       u32                             version;
> +
> +       int                             irq;
> +       struct cdns3_role_driver        *roles[CDNS3_ROLE_END];
> +       enum cdns3_roles                role;
> +       struct platform_device          *host_dev;
> +       struct cdns3_device             *gadget_dev;
> +       struct phy                      *phy;
> +       struct work_struct              role_switch_wq;
> +       int                             in_lpm:1;
> +       int                             wakeup_int:1;
> +       /* mutext used in workqueue*/
> +       struct mutex                    mutex;
> +       enum usb_dr_mode                dr_mode;
> +       enum usb_dr_mode                current_dr_mode;
> +       enum usb_dr_mode                desired_dr_mode;
> +       struct dentry                   *root;
> +       int                             debug_disable:1;
> +};
> +
> +void cdns3_role_stop(struct cdns3 *cdns);
> +
> +#endif /* __LINUX_CDNS3_CORE_H */
> diff --git a/drivers/usb/cdns3/debug.h b/drivers/usb/cdns3/debug.h
> new file mode 100644
> index 000000000000..94f9ef15f899
> --- /dev/null
> +++ b/drivers/usb/cdns3/debug.h
> @@ -0,0 +1,166 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +/*
> + * Cadence USBSS DRD Driver.
> + * Debug header file.
> + *
> + * Copyright (C) 2018 Cadence.
> + *
> + * Author: Pawel Laszczak <pawell@cadence.com>
> + */
> +#ifndef __LINUX_CDNS3_DEBUG
> +#define __LINUX_CDNS3_DEBUG
> +
> +#include "core.h"
> +
> +static inline char *cdns3_decode_usb_irq(char *str,
> +                                        enum usb_device_speed speed,
> +                                        u32 usb_ists)
> +{
> +       int ret;
> +
> +       ret = sprintf(str, "IRQ %08x = ", usb_ists);
> +
> +       if (usb_ists & (USB_ISTS_CON2I | USB_ISTS_CONI)) {
> +               ret += sprintf(str + ret, "Connection %s\n",
> +                              usb_speed_string(speed));
> +       }
> +       if (usb_ists & USB_ISTS_CON2I || usb_ists & USB_ISTS_CONI)
> +               ret += sprintf(str + ret, "Disconnection ");
> +       if (usb_ists & USB_ISTS_L2ENTI)
> +               ret += sprintf(str + ret, "suspended ");
> +
> +       if (usb_ists & USB_ISTS_L2EXTI)
> +               ret += sprintf(str + ret, "L2 exit ");
> +       if (usb_ists & USB_ISTS_U3EXTI)
> +               ret += sprintf(str + ret, "U3 exit ");
> +       if (usb_ists & USB_ISTS_UWRESI)
> +               ret += sprintf(str + ret, "Warm Reset ");
> +       if (usb_ists & USB_ISTS_UHRESI)
> +               ret += sprintf(str + ret, "Hot Reset ");
> +       if (usb_ists & USB_ISTS_U2RESI)
> +               ret += sprintf(str + ret, "Reset");
> +
> +       return str;
> +}
> +
> +static inline  char *cdns3_decode_ep_irq(char *str,
> +                                        u32 ep_sts,
> +                                        const char *ep_name)
> +{
> +       int ret;
> +
> +       ret = sprintf(str, "IRQ for %s: %08x ", ep_name, ep_sts);
> +
> +       if (ep_sts & EP_STS_SETUP)
> +               ret += sprintf(str + ret, "SETUP ");
> +       if (ep_sts & EP_STS_IOC)
> +               ret += sprintf(str + ret, "IOC ");
> +       if (ep_sts & EP_STS_ISP)
> +               ret += sprintf(str + ret, "ISP ");
> +       if (ep_sts & EP_STS_DESCMIS)
> +               ret += sprintf(str + ret, "DESCMIS ");
> +       if (ep_sts & EP_STS_STREAMR)
> +               ret += sprintf(str + ret, "STREAMR ");
> +       if (ep_sts & EP_STS_MD_EXIT)
> +               ret += sprintf(str + ret, "MD_EXIT ");
> +       if (ep_sts & EP_STS_TRBERR)
> +               ret += sprintf(str + ret, "TRBERR ");
> +       if (ep_sts & EP_STS_NRDY)
> +               ret += sprintf(str + ret, "NRDY ");
> +       if (ep_sts & EP_STS_PRIME)
> +               ret += sprintf(str + ret, "PRIME ");
> +       if (ep_sts & EP_STS_SIDERR)
> +               ret += sprintf(str + ret, "SIDERRT ");
> +       if (ep_sts & EP_STS_OUTSMM)
> +               ret += sprintf(str + ret, "OUTSMM ");
> +       if (ep_sts & EP_STS_ISOERR)
> +               ret += sprintf(str + ret, "ISOERR ");
> +       if (ep_sts & EP_STS_IOT)
> +               ret += sprintf(str + ret, "IOT ");
> +
> +       return str;
> +}
> +
> +static inline char *cdns3_decode_epx_irq(char *str,
> +                                        char *ep_name,
> +                                        u32 ep_sts)
> +{
> +       return cdns3_decode_ep_irq(str, ep_sts, ep_name);
> +}
> +
> +static inline char *cdns3_decode_ep0_irq(char *str,
> +                                        int dir,
> +                                        u32 ep_sts)
> +{
> +       return cdns3_decode_ep_irq(str, ep_sts,
> +                                  dir ? "ep0IN" : "ep0OUT");
> +}
> +
> +/**
> + * Debug a transfer ring.
> + *
> + * Prints out all TRBs in the endpoint ring, even those after the Link TRB.
> + *.
> + */
> +static inline char *cdns3_dbg_ring(struct cdns3_endpoint *priv_ep,
> +                                  struct cdns3_trb *ring, char *str)
> +{
> +       dma_addr_t addr = priv_ep->trb_pool_dma;
> +       struct cdns3_trb *trb;
> +       int trb_per_sector;
> +       int ret = 0;
> +       int i;
> +
> +       trb_per_sector = GET_TRBS_PER_SEGMENT(priv_ep->type);
> +
> +       trb = &priv_ep->trb_pool[priv_ep->dequeue];
> +       ret += sprintf(str + ret, "\n\t\tRing contents for %s:", priv_ep->name);
> +
> +       ret += sprintf(str + ret,
> +                      "\n\t\tRing deq index: %d, trb: %p (virt), 0x%llx (dma)\n",
> +                      priv_ep->dequeue, trb,
> +                      (unsigned long long)cdns3_trb_virt_to_dma(priv_ep, trb));
> +
> +       trb = &priv_ep->trb_pool[priv_ep->enqueue];
> +       ret += sprintf(str + ret,
> +                      "\t\tRing enq index: %d, trb: %p (virt), 0x%llx (dma)\n",
> +                      priv_ep->enqueue, trb,
> +                      (unsigned long long)cdns3_trb_virt_to_dma(priv_ep, trb));
> +
> +       ret += sprintf(str + ret,
> +                      "\t\tfree trbs: %d, CCS=%d, PCS=%d\n",
> +                      priv_ep->free_trbs, priv_ep->ccs, priv_ep->pcs);
> +
> +       if (trb_per_sector > TRBS_PER_SEGMENT)
> +               trb_per_sector = TRBS_PER_SEGMENT;
> +
> +       if (trb_per_sector > TRBS_PER_SEGMENT) {
> +               sprintf(str + ret, "\t\tTo big transfer ring %d\n",
> +                       trb_per_sector);
> +               return str;
> +       }
> +
> +       for (i = 0; i < trb_per_sector; ++i) {
> +               trb = &ring[i];
> +               ret += sprintf(str + ret,
> +                       "\t\t@%pad %08x %08x %08x\n", &addr,
> +                       le32_to_cpu(trb->buffer),
> +                       le32_to_cpu(trb->length),
> +                       le32_to_cpu(trb->control));
> +               addr += sizeof(*trb);
> +       }
> +
> +       return str;
> +}
> +
> +#ifdef CONFIG_DEBUG_FS
> +void cdns3_debugfs_init(struct cdns3 *cdns);
> +void cdns3_debugfs_exit(struct cdns3 *cdns);
> +#else
> +void cdns3_debugfs_init(struct cdns3 *cdns);
> +{  }
> +void cdns3_debugfs_exit(struct cdns3 *cdns);
> +{  }
> +#endif
> +
> +#endif /*__LINUX_CDNS3_DEBUG*/
> diff --git a/drivers/usb/cdns3/debugfs.c b/drivers/usb/cdns3/debugfs.c
> new file mode 100644
> index 000000000000..d7919f5c1d90
> --- /dev/null
> +++ b/drivers/usb/cdns3/debugfs.c
> @@ -0,0 +1,168 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Cadence USBSS DRD Controller DebugFS filer.
> + *
> + * Copyright (C) 2018 Cadence.
> + *
> + * Author: Pawel Laszczak <pawell@cadence.com>
> + */
> +
> +#include <linux/types.h>
> +#include <linux/debugfs.h>
> +#include <linux/seq_file.h>
> +#include <linux/uaccess.h>
> +
> +#include "core.h"
> +#include "gadget.h"
> +#include "drd.h"
> +
> +static int cdns3_mode_show(struct seq_file *s, void *unused)
> +{
> +       struct cdns3 *cdns = s->private;
> +
> +       switch (cdns->current_dr_mode) {
> +       case USB_DR_MODE_HOST:
> +               seq_puts(s, "host\n");
> +               break;
> +       case USB_DR_MODE_PERIPHERAL:
> +               seq_puts(s, "device\n");
> +               break;
> +       case USB_DR_MODE_OTG:
> +               seq_puts(s, "otg\n");
> +               break;
> +       default:
> +               seq_puts(s, "UNKNOWN mode\n");
> +       }
> +
> +       return 0;
> +}
> +
> +static int cdns3_mode_open(struct inode *inode, struct file *file)
> +{
> +       return single_open(file, cdns3_mode_show, inode->i_private);
> +}
> +
> +static ssize_t cdns3_mode_write(struct file *file,
> +                               const char __user *ubuf,
> +                               size_t count, loff_t *ppos)
> +{
> +       struct seq_file  *s = file->private_data;
> +       struct cdns3 *cdns = s->private;
> +       u32 mode = USB_DR_MODE_UNKNOWN;
> +       char buf[32];
> +
> +       if (copy_from_user(&buf, ubuf, min_t(size_t, sizeof(buf) - 1, count)))
> +               return -EFAULT;
> +
> +       if (!strncmp(buf, "host", 4)) {
> +               if (cdns->dr_mode == USB_DR_MODE_HOST ||
> +                   cdns->dr_mode == USB_DR_MODE_OTG) {
> +                       mode = USB_DR_MODE_HOST;
> +               }
> +       }
> +
> +       if (!strncmp(buf, "device", 6))
> +               if (cdns->dr_mode == USB_DR_MODE_PERIPHERAL ||
> +                   cdns->dr_mode == USB_DR_MODE_OTG)
> +                       mode = USB_DR_MODE_PERIPHERAL;
> +
> +       if (!strncmp(buf, "otg", 3) && cdns->dr_mode == USB_DR_MODE_OTG)
> +               mode = USB_DR_MODE_OTG;
> +
> +       if (mode == USB_DR_MODE_UNKNOWN) {
> +               dev_err(cdns->dev, "Failed: incorrect mode setting\n");
> +               return -EFAULT;
> +       }
> +
> +       if (cdns->current_dr_mode != mode) {
> +               cdns->desired_dr_mode = mode;
> +               cdns->debug_disable = 0;
> +               cdns3_role_stop(cdns);
> +               cdns3_drd_update_mode(cdns);
> +               queue_work(system_freezable_wq, &cdns->role_switch_wq);
> +       }
> +
> +       return count;
> +}
> +
> +static const struct file_operations cdns3_mode_fops = {
> +       .open                   = cdns3_mode_open,
> +       .write                  = cdns3_mode_write,
> +       .read                   = seq_read,
> +       .llseek                 = seq_lseek,
> +       .release                = single_release,
> +};
> +
> +static int cdns3_disable_show(struct seq_file *s, void *unused)
> +{
> +       struct cdns3 *cdns = s->private;
> +
> +       if (!cdns->debug_disable)
> +               seq_puts(s, "0\n");
> +       else
> +               seq_puts(s, "1\n");
> +
> +       return 0;
> +}
> +
> +static ssize_t cdns3_disable_write(struct file *file,
> +                                  const char __user *ubuf,
> +                                  size_t count, loff_t *ppos)
> +{
> +       struct seq_file  *s = file->private_data;
> +       struct cdns3 *cdns = s->private;
> +       int disable;
> +       char buf[32];
> +
> +       if (copy_from_user(&buf, ubuf, min_t(size_t, sizeof(buf) - 1, count)))
> +               return -EFAULT;
> +
> +       if (!strncmp(buf, "1", 1) || !strncmp(buf, "yes", 3)) {
> +               disable = 1;
> +       } else if (!strncmp(buf, "0", 1) || !strncmp(buf, "no", 2)) {
> +               disable = 0;
> +       } else {
> +               dev_err(cdns->dev, "Failed: incorrect disable setting\n");
> +               return -EFAULT;
> +       }
> +
> +       if (disable != cdns->debug_disable) {
> +               cdns->debug_disable = disable;
> +               queue_work(system_freezable_wq, &cdns->role_switch_wq);
> +       }
> +
> +       return count;
> +}
> +
> +static int cdns3_disable_open(struct inode *inode, struct file *file)
> +{
> +       return single_open(file, cdns3_disable_show, inode->i_private);
> +}
> +
> +static const struct file_operations cdns3_disable_fops = {
> +       .open                   = cdns3_disable_open,
> +       .write                  = cdns3_disable_write,
> +       .read                   = seq_read,
> +       .llseek                 = seq_lseek,
> +       .release                = single_release,
> +};
> +
> +void cdns3_debugfs_init(struct cdns3 *cdns)
> +{
> +       struct dentry *root;
> +
> +       root = debugfs_create_dir(dev_name(cdns->dev), NULL);
> +       cdns->root = root;
> +       if (IS_ENABLED(CONFIG_USB_CDNS3_GADGET) &&
> +           IS_ENABLED(CONFIG_USB_CDNS3_HOST))
> +               debugfs_create_file("mode", 0644, root, cdns,
> +                                   &cdns3_mode_fops);
> +
> +       debugfs_create_file("disable", 0644, root, cdns,
> +                           &cdns3_disable_fops);
> +}
> +
> +void cdns3_debugfs_exit(struct cdns3 *cdns)
> +{
> +       debugfs_remove_recursive(cdns->root);
> +}
> diff --git a/drivers/usb/cdns3/drd.c b/drivers/usb/cdns3/drd.c
> new file mode 100644
> index 000000000000..b0c32302eb0b
> --- /dev/null
> +++ b/drivers/usb/cdns3/drd.c
> @@ -0,0 +1,350 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Cadence USBSS DRD Driver.
> + *
> + * Copyright (C) 2018 Cadence.
> + *
> + * Author: Pawel Laszczak <pawell@cadence.com
> + *
> + */
> +#include <linux/kernel.h>
> +#include <linux/interrupt.h>
> +#include <linux/delay.h>
> +#include <linux/usb/otg.h>
> +
> +#include "gadget.h"
> +#include "drd.h"
> +#include "core.h"
> +
> +static int cdns3_drd_switch_gadget(struct cdns3 *cdns, int on);
> +static int cdns3_drd_switch_host(struct cdns3 *cdns, int on);
> +
> +/**
> + * cdns3_set_mode - change mode of OTG Core
> + * @cdns: pointer to context structure
> + * @mode: selected mode from cdns_role
> + */
> +void cdns3_set_mode(struct cdns3 *cdns, enum usb_dr_mode mode)
> +{
> +       u32 reg;
> +
> +       cdns->current_dr_mode = mode;
> +
> +       switch (mode) {
> +       case USB_DR_MODE_PERIPHERAL:
> +               dev_info(cdns->dev, "Set controller to Gadget mode\n");
> +               cdns3_drd_switch_gadget(cdns, 1);
> +               break;
> +       case USB_DR_MODE_HOST:
> +               dev_info(cdns->dev, "Set controller to Host mode\n");
> +               cdns3_drd_switch_host(cdns, 1);
> +               break;
> +       case USB_DR_MODE_OTG:
> +               dev_info(cdns->dev, "Set controller to OTG mode\n");
> +               if (cdns->version == CDNS3_CONTROLLER_V1) {
> +                       reg = readl(&cdns->otg_v1_regs->override);
> +                       reg |= OVERRIDE_IDPULLUP;
> +                       writel(reg, &cdns->otg_v1_regs->override);
> +               } else {
> +                       reg = readl(&cdns->otg_v0_regs->ctrl1);
> +                       reg |= OVERRIDE_IDPULLUP_V0;
> +                       writel(reg, &cdns->otg_v0_regs->ctrl1);
> +               }
> +
> +               /*
> +                * Hardware specification says: "ID_VALUE must be valid within
> +                * 50ms after idpullup is set to '1" so driver must wait
> +                * 50ms before reading this pin.
> +                */
> +               usleep_range(50000, 60000);
> +               break;
> +       default:
> +               cdns->current_dr_mode = USB_DR_MODE_UNKNOWN;
> +               dev_err(cdns->dev, "Unsupported mode of operation %d\n", mode);
> +               return;
> +       }
> +}
> +
> +int cdns3_get_id(struct cdns3 *cdns)
> +{
> +       int id;
> +
> +       id = readl(&cdns->otg_regs->sts) & OTGSTS_ID_VALUE;
> +       dev_dbg(cdns->dev, "OTG ID: %d", id);
> +       return  1 ; //id;
> +}
> +
> +int cdns3_is_host(struct cdns3 *cdns)
> +{
> +       if (cdns->current_dr_mode == USB_DR_MODE_HOST)
> +               return 1;
> +       else if (!cdns3_get_id(cdns))
> +               return 1;
> +
> +       return 0;
> +}
> +
> +int cdns3_is_device(struct cdns3 *cdns)
> +{
> +       if (cdns->current_dr_mode == USB_DR_MODE_PERIPHERAL)
> +               return 1;
> +       else if (cdns->current_dr_mode == USB_DR_MODE_OTG)
> +               if (cdns3_get_id(cdns))
> +                       return 1;
> +
> +       return 0;
> +}
> +
> +/**
> + * cdns3_otg_disable_irq - Disable all OTG interrupts
> + * @cdns: Pointer to controller context structure
> + */
> +static void cdns3_otg_disable_irq(struct cdns3 *cdns)
> +{
> +       writel(0, &cdns->otg_regs->ien);
> +}
> +
> +/**
> + * cdns3_otg_enable_irq - enable id and sess_valid interrupts
> + * @cdns: Pointer to controller context structure
> + */
> +static void cdns3_otg_enable_irq(struct cdns3 *cdns)
> +{
> +       writel(OTGIEN_ID_CHANGE_INT | OTGIEN_VBUSVALID_RISE_INT |
> +              OTGIEN_VBUSVALID_FALL_INT, &cdns->otg_regs->ien);
> +}
> +
> +/**
> + * cdns3_drd_switch_host - start/stop host
> + * @cdns: Pointer to controller context structure
> + * @on: 1 for start, 0 for stop
> + *
> + * Returns 0 on success otherwise negative errno
> + */
> +static int cdns3_drd_switch_host(struct cdns3 *cdns, int on)
> +{
> +       int ret;
> +       u32 reg = OTGCMD_OTG_DIS;
> +
> +       /* switch OTG core */
> +       if (on) {
> +               writel(OTGCMD_HOST_BUS_REQ | reg, &cdns->otg_regs->cmd);
> +
> +               dev_dbg(cdns->dev, "Waiting for Host mode is turned on\n");
> +               ret = cdns3_handshake(&cdns->otg_regs->sts, OTGSTS_XHCI_READY,
> +                                     OTGSTS_XHCI_READY, 100000);
> +
> +               if (ret)
> +                       return ret;
> +       } else {
> +               usleep_range(30, 40);
> +               writel(OTGCMD_HOST_BUS_DROP | OTGCMD_DEV_BUS_DROP |
> +                      OTGCMD_DEV_POWER_OFF | OTGCMD_HOST_POWER_OFF,
> +                      &cdns->otg_regs->cmd);
> +       }
> +
> +       return 0;
> +}
> +
> +/**
> + * cdns3_drd_switch_gadget - start/stop gadget
> + * @cdns: Pointer to controller context structure
> + * @on: 1 for start, 0 for stop
> + *
> + * Returns 0 on success otherwise negative errno
> + */
> +static int cdns3_drd_switch_gadget(struct cdns3 *cdns, int on)
> +{
> +       int ret;
> +       u32 reg = OTGCMD_OTG_DIS;
> +
> +       /* switch OTG core */
> +       if (on) {
> +               writel(OTGCMD_DEV_BUS_REQ | reg, &cdns->otg_regs->cmd);
> +
> +               dev_dbg(cdns->dev, "Waiting for Device mode is turned on\n");
> +
> +               ret = cdns3_handshake(&cdns->otg_regs->sts, OTGSTS_DEV_READY,
> +                                     OTGSTS_DEV_READY, 100000);
> +
> +               if (ret)
> +                       return ret;
> +       } else {
> +               /*
> +                * driver should wait at least 10us after disabling Device
> +                * before turning-off Device (DEV_BUS_DROP)
> +                */
> +               usleep_range(20, 30);
> +               writel(OTGCMD_HOST_BUS_DROP | OTGCMD_DEV_BUS_DROP |
> +                      OTGCMD_DEV_POWER_OFF | OTGCMD_HOST_POWER_OFF,
> +                      &cdns->otg_regs->cmd);
> +       }
> +
> +       return 0;
> +}
> +
> +/**
> + * cdns3_init_otg_mode - initialize drd controller
> + * @cdns: Pointer to controller context structure
> + *
> + * Returns 0 on success otherwise negative errno
> + */
> +static void cdns3_init_otg_mode(struct cdns3 *cdns)
> +{
> +       cdns3_otg_disable_irq(cdns);
> +       /* clear all interrupts */
> +       writel(~0, &cdns->otg_regs->ivect);
> +
> +       cdns3_set_mode(cdns, USB_DR_MODE_OTG);
> +
> +       if (cdns3_is_host(cdns))
> +               cdns3_drd_switch_host(cdns, 1);
> +       else
> +               cdns3_drd_switch_gadget(cdns, 1);
> +
> +       cdns3_otg_enable_irq(cdns);
> +}
> +
> +/**
> + * cdns3_drd_update_mode - initialize mode of operation
> + * @cdns: Pointer to controller context structure
> + *
> + * Returns 0 on success otherwise negative errno
> + */
> +int cdns3_drd_update_mode(struct cdns3 *cdns)
> +{
> +       int ret = 0;
> +
> +       if (cdns->desired_dr_mode == cdns->current_dr_mode)
> +               return ret;
> +
> +       cdns3_drd_switch_gadget(cdns, 0);
> +       cdns3_drd_switch_host(cdns, 0);
> +
> +       switch (cdns->desired_dr_mode) {
> +       case USB_DR_MODE_PERIPHERAL:
> +               cdns3_set_mode(cdns, USB_DR_MODE_PERIPHERAL);
> +               break;
> +       case USB_DR_MODE_HOST:
> +               cdns3_set_mode(cdns, USB_DR_MODE_HOST);
> +               break;
> +       case USB_DR_MODE_OTG:
> +               cdns3_init_otg_mode(cdns);
> +               break;
> +       default:
> +               dev_err(cdns->dev, "Unsupported mode of operation %d\n",
> +                       cdns->dr_mode);
> +               return -EINVAL;
> +       }
> +
> +       return ret;
> +}
> +
> +/**
> + * cdns3_drd_irq - interrupt handler for OTG events
> + *
> + * @irq: irq number for cdns3 core device
> + * @data: structure of cdns3
> + *
> + * Returns IRQ_HANDLED or IRQ_NONE
> + */
> +static irqreturn_t cdns3_drd_irq(int irq, void *data)
> +{
> +       irqreturn_t ret = IRQ_NONE;
> +       struct cdns3 *cdns = data;
> +       u32 reg;
> +
> +       if (cdns->dr_mode != USB_DR_MODE_OTG)
> +               return ret;
> +
> +       reg = readl(&cdns->otg_regs->ivect);
> +
> +       if (!reg)
> +               return ret;
> +
> +       if (reg & OTGIEN_ID_CHANGE_INT) {
> +               dev_dbg(cdns->dev, "OTG IRQ: new ID: %d\n",
> +                       cdns3_get_id(cdns));
> +
> +               queue_work(system_freezable_wq, &cdns->role_switch_wq);
> +
> +               ret = IRQ_HANDLED;
> +       }
> +
> +       writel(~0, &cdns->otg_regs->ivect);
> +       return ret;
> +}
> +
> +int cdns3_drd_init(struct cdns3 *cdns)
> +{
> +       void __iomem *regs;
> +       int ret = 0;
> +       u32 state;
> +
> +       regs = devm_ioremap_resource(cdns->dev, &cdns->otg_res);
> +       if (IS_ERR(regs))
> +               return PTR_ERR(regs);
> +
> +       /* Detection of DRD version. Controller has been released
> +        * in two versions. Both are similar, but they have same changes
> +        * in register maps.
> +        * The first register in old version is command register and it's read
> +        * only, so driver should read 0 from it. On the other hand, in v1
> +        * the first register contains device ID number which is not set to 0.
> +        * Driver uses this fact to detect the proper version of
> +        * controller.
> +        */
> +       cdns->otg_v0_regs = regs;
> +       if (!readl(&cdns->otg_v0_regs->cmd)) {
> +               cdns->version  = CDNS3_CONTROLLER_V0;
> +               cdns->otg_v1_regs = NULL;
> +               cdns->otg_regs = regs;
> +               dev_info(cdns->dev, "DRD version v0 (%08x)\n",
> +                        readl(&cdns->otg_v0_regs->version));
> +       } else {
> +               cdns->otg_v0_regs = NULL;
> +               cdns->otg_v1_regs = regs;
> +               cdns->otg_regs = (void *)&cdns->otg_v1_regs->cmd;
> +               cdns->version  = CDNS3_CONTROLLER_V1;
> +               dev_info(cdns->dev, "DRD version v1 (ID: %08x, rev: %08x)\n",
> +                        readl(&cdns->otg_v1_regs->did),
> +                        readl(&cdns->otg_v1_regs->rid));
> +       }
> +
> +       state = OTGSTS_STRAP(readl(&cdns->otg_regs->sts));
> +
> +       /* Update dr_mode according to STRAP configuration. */
> +       cdns->dr_mode = USB_DR_MODE_OTG;
> +       if (state == OTGSTS_STRAP_HOST) {
> +               dev_info(cdns->dev, "Controller strapped to HOST\n");
> +               cdns->dr_mode = USB_DR_MODE_HOST;
> +       } else if (state == OTGSTS_STRAP_GADGET) {
> +               dev_info(cdns->dev, "Controller strapped to PERIPHERAL\n");
> +               cdns->dr_mode = USB_DR_MODE_PERIPHERAL;
> +       }
> +
> +       cdns->desired_dr_mode = cdns->dr_mode;
> +       cdns->current_dr_mode = USB_DR_MODE_UNKNOWN;
> +
> +       ret = devm_request_threaded_irq(cdns->dev, cdns->irq, cdns3_drd_irq,
> +                                       NULL, IRQF_SHARED,
> +                                       dev_name(cdns->dev), cdns);
> +
> +       if (ret)
> +               return ret;
> +
> +       state = readl(&cdns->otg_regs->sts);
> +       if (OTGSTS_OTG_NRDY(state) != 0) {
> +               dev_err(cdns->dev, "Cadence USB3 OTG device not ready\n");
> +               return -ENODEV;
> +       }
> +
> +       ret = cdns3_drd_update_mode(cdns);
> +
> +       return ret;
> +}
> +
> +int cdns3_drd_exit(struct cdns3 *cdns)
> +{
> +       return cdns3_drd_switch_host(cdns, 0);
> +}
> diff --git a/drivers/usb/cdns3/drd.h b/drivers/usb/cdns3/drd.h
> new file mode 100644
> index 000000000000..6a29cdcb492d
> --- /dev/null
> +++ b/drivers/usb/cdns3/drd.h
> @@ -0,0 +1,162 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +/*
> + * Cadence USB3 DRD header file.
> + *
> + * Copyright (C) 2018 Cadence.
> + *
> + * Author: Pawel Laszczak <pawell@cadence.com>
> + */
> +#ifndef __LINUX_CDNS3_DRD
> +#define __LINUX_CDNS3_DRD
> +
> +#include <linux/usb/otg.h>
> +#include <linux/phy/phy.h>
> +#include "core.h"
> +
> +/*  DRD register interface for version v1. */
> +struct cdns3_otg_regs {
> +       __le32 did;
> +       __le32 rid;
> +       __le32 capabilities;
> +       __le32 reserved1;
> +       __le32 cmd;
> +       __le32 sts;
> +       __le32 state;
> +       __le32 reserved2;
> +       __le32 ien;
> +       __le32 ivect;
> +       __le32 refclk;
> +       __le32 tmr;
> +       __le32 reserved3[4];
> +       __le32 simulate;
> +       __le32 override;
> +       __le32 susp_ctrl;
> +       __le32 reserved4;
> +       __le32 anasts;
> +       __le32 adp_ramp_time;
> +       __le32 ctrl1;
> +       __le32 ctrl2;
> +};
> +
> +/*  DRD register interface for version v0. */
> +struct cdns3_otg_legacy_regs {
> +       __le32 cmd;
> +       __le32 sts;
> +       __le32 state;
> +       __le32 refclk;
> +       __le32 ien;
> +       __le32 ivect;
> +       __le32 reserved1[3];
> +       __le32 tmr;
> +       __le32 reserved2[2];
> +       __le32 version;
> +       __le32 capabilities;
> +       __le32 reserved3[2];
> +       __le32 simulate;
> +       __le32 reserved4[5];
> +       __le32 ctrl1;
> +};
> +
> +/*
> + * Common registers interface for both version of DRD.
> + */
> +struct cdns3_otg_common_regs {
> +       __le32 cmd;
> +       __le32 sts;
> +       __le32 state;
> +       __le32 different1;
> +       __le32 ien;
> +       __le32 ivect;
> +};
> +
> +/* CDNS_RID - bitmasks */
> +#define CDNS_RID(p)                    ((p) & GENMASK(15, 0))
> +
> +/* CDNS_VID - bitmasks */
> +#define CDNS_DID(p)                    ((p) & GENMASK(31, 0))
> +
> +/* OTGCMD - bitmasks */
> +/* "Request the bus for Device mode. */
> +#define OTGCMD_DEV_BUS_REQ             BIT(0)
> +/* Request the bus for Host mode */
> +#define OTGCMD_HOST_BUS_REQ            BIT(1)
> +/* Enable OTG mode. */
> +#define OTGCMD_OTG_EN                  BIT(2)
> +/* Disable OTG mode */
> +#define OTGCMD_OTG_DIS                 BIT(3)
> +/*"Configure OTG as A-Device. */
> +#define OTGCMD_A_DEV_EN                        BIT(4)
> +/*"Configure OTG as A-Device. */
> +#define OTGCMD_A_DEV_DIS               BIT(5)
> +/* Drop the bus for Device mod e. */
> +#define OTGCMD_DEV_BUS_DROP            BIT(8)
> +/* Drop the bus for Host mode*/
> +#define OTGCMD_HOST_BUS_DROP           BIT(9)
> +/* Power Down USBSS-DEV. */
> +#define OTGCMD_DEV_POWER_OFF           BIT(11)
> +/* Power Down CDNSXHCI. */
> +#define OTGCMD_HOST_POWER_OFF          BIT(12)
> +
> +/* OTGIEN - bitmasks */
> +/* ID change interrupt enable */
> +#define OTGIEN_ID_CHANGE_INT           BIT(0)
> +/* Vbusvalid fall detected interrupt enable.*/
> +#define OTGIEN_VBUSVALID_RISE_INT      BIT(4)
> +/* Vbusvalid fall detected interrupt enable */
> +#define OTGIEN_VBUSVALID_FALL_INT      BIT(5)
> +
> +/* OTGSTS - bitmasks */
> +/*
> + * Current value of the ID pin. It is only valid when idpullup in
> + *  OTGCTRL1_TYPE register is set to '1'.
> + */
> +#define OTGSTS_ID_VALUE                        BIT(0)
> +/* Current value of the vbus_valid */
> +#define OTGSTS_VBUS_VALID              BIT(1)
> +/* Current value of the b_sess_vld */
> +#define OTGSTS_SESSION_VALID           BIT(2)
> +/*Device mode is active*/
> +#define OTGSTS_DEV_ACTIVE              BIT(3)
> +/* Host mode is active. */
> +#define OTGSTS_HOST_ACTIVE             BIT(4)
> +/* OTG Controller not ready. */
> +#define OTGSTS_OTG_NRDY_MASK           BIT(11)
> +#define OTGSTS_OTG_NRDY(p)             ((p) & OTGSTS_OTG_NRDY_MASK)
> +/*
> + * Value of the strap pins.
> + * 000 - no default configuration
> + * 010 - Controller initiall configured as Host
> + * 100 - Controller initially configured as Device
> + */
> +#define OTGSTS_STRAP(p)                        (((p) & GENMASK(14, 12)) >> 12)
> +#define OTGSTS_STRAP_NO_DEFAULT_CFG    0x00
> +#define OTGSTS_STRAP_HOST_OTG          0x01
> +#define OTGSTS_STRAP_HOST              0x02
> +#define OTGSTS_STRAP_GADGET            0x04
> +/* Host mode is turned on. */
> +#define OTGSTS_XHCI_READY              BIT(26)
> +/* "Device mode is turned on .*/
> +#define OTGSTS_DEV_READY               BIT(27)
> +
> +/* OTGSTATE- bitmasks */
> +#define OTGSTATE_HOST_STATE_MASK       GENMASK(5, 3)
> +#define OTGSTATE_HOST_STATE_IDLE       0x0
> +#define OTGSTATE_HOST_STATE_VBUS_FALL   0x7
> +#define OTGSTATE_HOST_STATE(p)         (((p) & OTGSTATE_HOST_STATE_MASK) >> 3)
> +
> +/* OTGREFCLK - bitmasks */
> +#define OTGREFCLK_STB_CLK_SWITCH_EN    BIT(31)
> +
> +/* OVERRIDE - bitmasks */
> +#define OVERRIDE_IDPULLUP              BIT(0)
> +/* Only for CDNS3_CONTROLLER_V0 version */
> +#define OVERRIDE_IDPULLUP_V0           BIT(24)
> +
> +int cdns3_is_host(struct cdns3 *cdns);
> +int cdns3_is_device(struct cdns3 *cdns);
> +int cdns3_get_id(struct cdns3 *cdns);
> +int cdns3_drd_init(struct cdns3 *cdns);
> +int cdns3_drd_exit(struct cdns3 *cdns);
> +int cdns3_drd_update_mode(struct cdns3 *cdns);
> +
> +#endif /* __LINUX_CDNS3_DRD */
> diff --git a/drivers/usb/cdns3/ep0.c b/drivers/usb/cdns3/ep0.c
> new file mode 100644
> index 000000000000..89cf1cde1555
> --- /dev/null
> +++ b/drivers/usb/cdns3/ep0.c
> @@ -0,0 +1,896 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Cadence USBSS DRD Driver - gadget side.
> + *
> + * Copyright (C) 2018 Cadence Design Systems.
> + * Copyright (C) 2017-2018 NXP
> + *
> + * Authors: Pawel Jez <pjez@cadence.com>,
> + *          Pawel Laszczak <pawell@cadence.com>
> + *         Peter Chen <peter.chen@nxp.com>
> + */
> +
> +#include <linux/usb/composite.h>
> +
> +#include "gadget.h"
> +#include "trace.h"
> +
> +static struct usb_endpoint_descriptor cdns3_gadget_ep0_desc = {
> +       .bLength = USB_DT_ENDPOINT_SIZE,
> +       .bDescriptorType = USB_DT_ENDPOINT,
> +       .bmAttributes = USB_ENDPOINT_XFER_CONTROL,
> +};
> +
> +/**
> + * cdns3_ep0_run_transfer - Do transfer on default endpoint hardware
> + * @priv_dev: extended gadget object
> + * @dma_addr: physical address where data is/will be stored
> + * @length: data length
> + * @erdy: set it to 1 when ERDY packet should be sent -
> + *        exit from flow control state
> + */
> +static void cdns3_ep0_run_transfer(struct cdns3_device *priv_dev,
> +                                  dma_addr_t dma_addr,
> +                                  unsigned int length, int erdy)
> +{
> +       struct cdns3_usb_regs __iomem *regs = priv_dev->regs;
> +       struct cdns3_endpoint *priv_ep = priv_dev->eps[0];
> +
> +       priv_ep->trb_pool->buffer = TRB_BUFFER(dma_addr);
> +       priv_ep->trb_pool->length = TRB_LEN(length);
> +       priv_ep->trb_pool->control = TRB_CYCLE | TRB_IOC | TRB_TYPE(TRB_NORMAL);
> +
> +       trace_cdns3_prepare_trb(priv_ep, priv_ep->trb_pool);
> +
> +       cdns3_select_ep(priv_dev, priv_dev->ep0_data_dir);
> +
> +       writel(EP_STS_TRBERR, &regs->ep_sts);
> +       writel(EP_TRADDR_TRADDR(priv_ep->trb_pool_dma), &regs->ep_traddr);
> +       trace_cdns3_doorbell_ep0(priv_dev->ep0_data_dir ? "ep0in" : "ep0out",
> +                                readl(&regs->ep_traddr));
> +
> +       /* TRB should be prepared before starting transfer */
> +       writel(EP_CMD_DRDY, &regs->ep_cmd);
> +
> +       if (erdy)
> +               writel(EP_CMD_ERDY, &priv_dev->regs->ep_cmd);
> +}
> +
> +/**
> + * cdns3_ep0_delegate_req - Returns status of handling setup packet
> + * Setup is handled by gadget driver
> + * @priv_dev: extended gadget object
> + * @ctrl_req: pointer to received setup packet
> + *
> + * Returns zero on success or negative value on failure
> + */
> +static int cdns3_ep0_delegate_req(struct cdns3_device *priv_dev,
> +                                 struct usb_ctrlrequest *ctrl_req)
> +{
> +       int ret;
> +
> +       spin_unlock(&priv_dev->lock);
> +       priv_dev->setup_pending = 1;
> +       ret = priv_dev->gadget_driver->setup(&priv_dev->gadget, ctrl_req);
> +       priv_dev->setup_pending = 0;
> +       spin_lock(&priv_dev->lock);
> +       return ret;
> +}
> +
> +static void cdns3_prepare_setup_packet(struct cdns3_device *priv_dev)
> +{
> +       priv_dev->ep0_data_dir = 0;
> +       priv_dev->ep0_stage = CDNS3_SETUP_STAGE;
> +       cdns3_ep0_run_transfer(priv_dev, priv_dev->setup_dma,
> +                              sizeof(struct usb_ctrlrequest), 0);
> +}
> +
> +static void cdns3_ep0_complete_setup(struct cdns3_device *priv_dev,
> +                                    u8 send_stall, u8 send_erdy)
> +{
> +       struct cdns3_endpoint *priv_ep = priv_dev->eps[0];
> +       struct usb_request *request;
> +
> +       request = cdns3_next_request(&priv_ep->pending_req_list);
> +       if (request)
> +               list_del_init(&request->list);
> +
> +       if (send_stall) {
> +               dev_dbg(priv_dev->dev, "STALL for ep0\n");
> +               /* set_stall on ep0 */
> +               cdns3_select_ep(priv_dev, 0x00);
> +               writel(EP_CMD_SSTALL, &priv_dev->regs->ep_cmd);
> +       } else {
> +               cdns3_prepare_setup_packet(priv_dev);
> +       }
> +
> +       priv_dev->ep0_stage = CDNS3_SETUP_STAGE;
> +       writel((send_erdy ? EP_CMD_ERDY : 0) | EP_CMD_REQ_CMPL,
> +              &priv_dev->regs->ep_cmd);
> +}
> +
> +/**
> + * cdns3_req_ep0_set_configuration - Handling of SET_CONFIG standard USB request
> + * @priv_dev: extended gadget object
> + * @ctrl_req: pointer to received setup packet
> + *
> + * Returns 0 if success, USB_GADGET_DELAYED_STATUS on deferred status stage,
> + * error code on error
> + */
> +static int cdns3_req_ep0_set_configuration(struct cdns3_device *priv_dev,
> +                                          struct usb_ctrlrequest *ctrl_req)
> +{
> +       enum usb_device_state device_state = priv_dev->gadget.state;
> +       struct cdns3_endpoint *priv_ep;
> +       u32 config = le16_to_cpu(ctrl_req->wValue);
> +       int result = 0;
> +       int i;
> +
> +       switch (device_state) {
> +       case USB_STATE_ADDRESS:
> +               /* Configure non-control EPs */
> +               for (i = 0; i < CDNS3_ENDPOINTS_MAX_COUNT; i++) {
> +                       priv_ep = priv_dev->eps[i];
> +                       if (!priv_ep)
> +                               continue;
> +
> +                       if (priv_ep->flags & EP_CLAIMED)
> +                               cdns3_ep_config(priv_ep);
> +               }
> +
> +               result = cdns3_ep0_delegate_req(priv_dev, ctrl_req);
> +
> +               if (result)
> +                       return result;
> +
> +               if (config) {
> +                       cdns3_set_hw_configuration(priv_dev);
> +               } else {
> +                       cdns3_hw_reset_eps_config(priv_dev);
> +                       usb_gadget_set_state(&priv_dev->gadget,
> +                                            USB_STATE_ADDRESS);
> +               }
> +               break;
> +       case USB_STATE_CONFIGURED:
> +               result = cdns3_ep0_delegate_req(priv_dev, ctrl_req);
> +
> +               if (!config && !result) {
> +                       cdns3_hw_reset_eps_config(priv_dev);
> +                       usb_gadget_set_state(&priv_dev->gadget,
> +                                            USB_STATE_ADDRESS);
> +               }
> +               break;
> +       default:
> +               result = -EINVAL;
> +       }
> +
> +       return result;
> +}
> +
> +/**
> + * cdns3_req_ep0_set_address - Handling of SET_ADDRESS standard USB request
> + * @priv_dev: extended gadget object
> + * @ctrl_req: pointer to received setup packet
> + *
> + * Returns 0 if success, error code on error
> + */
> +static int cdns3_req_ep0_set_address(struct cdns3_device *priv_dev,
> +                                    struct usb_ctrlrequest *ctrl_req)
> +{
> +       enum usb_device_state device_state = priv_dev->gadget.state;
> +       u32 reg;
> +       u32 addr;
> +
> +       addr = le16_to_cpu(ctrl_req->wValue);
> +
> +       if (addr > USB_DEVICE_MAX_ADDRESS) {
> +               dev_err(priv_dev->dev,
> +                       "Device address (%d) cannot be greater than %d\n",
> +                       addr, USB_DEVICE_MAX_ADDRESS);
> +               return -EINVAL;
> +       }
> +
> +       if (device_state == USB_STATE_CONFIGURED) {
> +               dev_err(priv_dev->dev,
> +                       "can't set_address from configured state\n");
> +               return -EINVAL;
> +       }
> +
> +       reg = readl(&priv_dev->regs->usb_cmd);
> +
> +       writel(reg | USB_CMD_FADDR(addr) | USB_CMD_SET_ADDR,
> +              &priv_dev->regs->usb_cmd);
> +
> +       usb_gadget_set_state(&priv_dev->gadget,
> +                            (addr ? USB_STATE_ADDRESS : USB_STATE_DEFAULT));
> +
> +       return 0;
> +}
> +
> +/**
> + * cdns3_req_ep0_get_status - Handling of GET_STATUS standard USB request
> + * @priv_dev: extended gadget object
> + * @ctrl_req: pointer to received setup packet
> + *
> + * Returns 0 if success, error code on error
> + */
> +static int cdns3_req_ep0_get_status(struct cdns3_device *priv_dev,
> +                                   struct usb_ctrlrequest *ctrl)
> +{
> +       __le16 *response_pkt;
> +       u16 usb_status = 0;
> +       u32 recip;
> +       u32 reg;
> +
> +       recip = ctrl->bRequestType & USB_RECIP_MASK;
> +
> +       switch (recip) {
> +       case USB_RECIP_DEVICE:
> +               /* self powered */
> +               if (priv_dev->is_selfpowered)
> +                       usb_status = BIT(USB_DEVICE_SELF_POWERED);
> +
> +               if (priv_dev->wake_up_flag)
> +                       usb_status |= BIT(USB_DEVICE_REMOTE_WAKEUP);
> +
> +               if (priv_dev->gadget.speed != USB_SPEED_SUPER)
> +                       break;
> +
> +               reg = readl(&priv_dev->regs->usb_sts);
> +
> +               if (priv_dev->u1_allowed)
> +                       usb_status |= BIT(USB_DEV_STAT_U1_ENABLED);
> +
> +               if (priv_dev->u2_allowed)
> +                       usb_status |= BIT(USB_DEV_STAT_U2_ENABLED);
> +
> +               break;
> +       case USB_RECIP_INTERFACE:
> +               return cdns3_ep0_delegate_req(priv_dev, ctrl);
> +       case USB_RECIP_ENDPOINT:
> +               /* check if endpoint is stalled */
> +               cdns3_select_ep(priv_dev, ctrl->wIndex);
> +               if (EP_STS_STALL(readl(&priv_dev->regs->ep_sts)))
> +                       usb_status =  BIT(USB_ENDPOINT_HALT);
> +               break;
> +       default:
> +               return -EINVAL;
> +       }
> +
> +       response_pkt = (__le16 *)priv_dev->setup_buf;
> +       *response_pkt = cpu_to_le16(usb_status);
> +
> +       cdns3_ep0_run_transfer(priv_dev, priv_dev->setup_dma,
> +                              sizeof(*response_pkt), 1);
> +       return 0;
> +}
> +
> +static int cdns3_ep0_feature_handle_device(struct cdns3_device *priv_dev,
> +                                          struct usb_ctrlrequest *ctrl,
> +                                          int set)
> +{
> +       enum usb_device_state state;
> +       enum usb_device_speed speed;
> +       int ret = 0;
> +       u32 wValue;
> +       u32 wIndex;
> +       u16 tmode;
> +
> +       wValue = le16_to_cpu(ctrl->wValue);
> +       wIndex = le16_to_cpu(ctrl->wIndex);
> +       state = priv_dev->gadget.state;
> +       speed = priv_dev->gadget.speed;
> +
> +       switch (ctrl->wValue) {
> +       case USB_DEVICE_REMOTE_WAKEUP:
> +               priv_dev->wake_up_flag = !!set;
> +               break;
> +       case USB_DEVICE_U1_ENABLE:
> +               if (state != USB_STATE_CONFIGURED || speed != USB_SPEED_SUPER)
> +                       return -EINVAL;
> +
> +               priv_dev->u1_allowed = !!set;
> +               break;
> +       case USB_DEVICE_U2_ENABLE:
> +               if (state != USB_STATE_CONFIGURED || speed != USB_SPEED_SUPER)
> +                       return -EINVAL;
> +
> +               priv_dev->u2_allowed = !!set;
> +               break;
> +       case USB_DEVICE_LTM_ENABLE:
> +               ret = -EINVAL;
> +               break;
> +       case USB_DEVICE_TEST_MODE:
> +               if (state != USB_STATE_CONFIGURED || speed > USB_SPEED_HIGH)
> +                       return -EINVAL;
> +
> +               tmode = le16_to_cpu(ctrl->wIndex);
> +
> +               if (!set || (tmode & 0xff) != 0)
> +                       return -EINVAL;
> +
> +               switch (tmode >> 8) {
> +               case TEST_J:
> +               case TEST_K:
> +               case TEST_SE0_NAK:
> +               case TEST_PACKET:
> +                       cdns3_ep0_complete_setup(priv_dev, 0, 1);
> +                       /**
> +                        *  Little delay to give the controller some time
> +                        * for sending status stage.
> +                        * This time should be less then 3ms.
> +                        */
> +                       usleep_range(1000, 2000);
> +                       cdns3_set_register_bit(&priv_dev->regs->usb_cmd,
> +                                              USB_CMD_STMODE |
> +                                              USB_STS_TMODE_SEL(tmode - 1));
> +                       break;
> +               default:
> +                       ret = -EINVAL;
> +               }
> +               break;
> +       default:
> +               ret = -EINVAL;
> +       }
> +
> +       return ret;
> +}
> +
> +static int cdns3_ep0_feature_handle_intf(struct cdns3_device *priv_dev,
> +                                        struct usb_ctrlrequest *ctrl,
> +                                        int set)
> +{
> +       u32 wValue;
> +       int ret = 0;
> +
> +       wValue = le16_to_cpu(ctrl->wValue);
> +
> +       switch (wValue) {
> +       case USB_INTRF_FUNC_SUSPEND:
> +               break;
> +       default:
> +               ret = -EINVAL;
> +       }
> +
> +       return ret;
> +}
> +
> +static int cdns3_ep0_feature_handle_endpoint(struct cdns3_device *priv_dev,
> +                                            struct usb_ctrlrequest *ctrl,
> +                                            int set)
> +{
> +       struct cdns3_endpoint *priv_ep;
> +       int ret = 0;
> +       u8 index;
> +
> +       if (le16_to_cpu(ctrl->wValue) != USB_ENDPOINT_HALT)
> +               return -EINVAL;
> +
> +       if (!(ctrl->wIndex & ~USB_DIR_IN))
> +               return 0;
> +
> +       index = cdns3_ep_addr_to_index(ctrl->wIndex);
> +       priv_ep = priv_dev->eps[index];
> +
> +       cdns3_select_ep(priv_dev, ctrl->wIndex);
> +
> +       if (set) {
> +               writel(EP_CMD_SSTALL, &priv_dev->regs->ep_cmd);
> +               priv_ep->flags |= EP_STALL;
> +       } else {
> +               struct usb_request *request;
> +
> +               if (priv_dev->eps[index]->flags & EP_WEDGE) {
> +                       cdns3_select_ep(priv_dev, 0x00);
> +                       return 0;
> +               }
> +
> +               writel(EP_CMD_CSTALL | EP_CMD_EPRST, &priv_dev->regs->ep_cmd);
> +
> +               /* wait for EPRST cleared */
> +               ret = cdns3_handshake(&priv_dev->regs->ep_cmd,
> +                                     EP_CMD_EPRST, 0, 100);
> +               if (ret)
> +                       return -EINVAL;
> +
> +               priv_ep->flags &= ~EP_STALL;
> +
> +               request = cdns3_next_request(&priv_ep->pending_req_list);
> +               if (request)
> +                       cdns3_ep_run_transfer(priv_ep, request);
> +       }
> +       return ret;
> +}
> +
> +/**
> + * cdns3_req_ep0_handle_feature -
> + * Handling of GET/SET_FEATURE standard USB request
> + *
> + * @priv_dev: extended gadget object
> + * @ctrl_req: pointer to received setup packet
> + * @set: must be set to 1 for SET_FEATURE request
> + *
> + * Returns 0 if success, error code on error
> + */
> +static int cdns3_req_ep0_handle_feature(struct cdns3_device *priv_dev,
> +                                       struct usb_ctrlrequest *ctrl,
> +                                       int set)
> +{
> +       int ret = 0;
> +       u32 recip;
> +
> +       recip = ctrl->bRequestType & USB_RECIP_MASK;
> +
> +       switch (recip) {
> +       case USB_RECIP_DEVICE:
> +               ret = cdns3_ep0_feature_handle_device(priv_dev, ctrl, set);
> +               break;
> +       case USB_RECIP_INTERFACE:
> +               ret = cdns3_ep0_feature_handle_intf(priv_dev, ctrl, set);
> +               break;
> +       case USB_RECIP_ENDPOINT:
> +               ret = cdns3_ep0_feature_handle_endpoint(priv_dev, ctrl, set);
> +               break;
> +       default:
> +               return -EINVAL;
> +       }
> +
> +       return ret;
> +}
> +
> +/**
> + * cdns3_req_ep0_set_sel - Handling of SET_SEL standard USB request
> + * @priv_dev: extended gadget object
> + * @ctrl_req: pointer to received setup packet
> + *
> + * Returns 0 if success, error code on error
> + */
> +static int cdns3_req_ep0_set_sel(struct cdns3_device *priv_dev,
> +                                struct usb_ctrlrequest *ctrl_req)
> +{
> +       if (priv_dev->gadget.state < USB_STATE_ADDRESS)
> +               return -EINVAL;
> +
> +       if (ctrl_req->wLength != 6) {
> +               dev_err(priv_dev->dev, "Set SEL should be 6 bytes, got %d\n",
> +                       ctrl_req->wLength);
> +               return -EINVAL;
> +       }
> +
> +       cdns3_ep0_run_transfer(priv_dev, priv_dev->setup_dma, 6, 1);
> +       return 0;
> +}
> +
> +/**
> + * cdns3_req_ep0_set_isoch_delay -
> + * Handling of GET_ISOCH_DELAY standard USB request
> + * @priv_dev: extended gadget object
> + * @ctrl_req: pointer to received setup packet
> + *
> + * Returns 0 if success, error code on error
> + */
> +static int cdns3_req_ep0_set_isoch_delay(struct cdns3_device *priv_dev,
> +                                        struct usb_ctrlrequest *ctrl_req)
> +{
> +       if (ctrl_req->wIndex || ctrl_req->wLength)
> +               return -EINVAL;
> +
> +       priv_dev->isoch_delay = ctrl_req->wValue;
> +
> +       return 0;
> +}
> +
> +/**
> + * cdns3_ep0_standard_request - Handling standard USB requests
> + * @priv_dev: extended gadget object
> + * @ctrl_req: pointer to received setup packet
> + *
> + * Returns 0 if success, error code on error
> + */
> +static int cdns3_ep0_standard_request(struct cdns3_device *priv_dev,
> +                                     struct usb_ctrlrequest *ctrl_req)
> +{
> +       int ret;
> +
> +       switch (ctrl_req->bRequest) {
> +       case USB_REQ_SET_ADDRESS:
> +               ret = cdns3_req_ep0_set_address(priv_dev, ctrl_req);
> +               break;
> +       case USB_REQ_SET_CONFIGURATION:
> +               ret = cdns3_req_ep0_set_configuration(priv_dev, ctrl_req);
> +               break;
> +       case USB_REQ_GET_STATUS:
> +               ret = cdns3_req_ep0_get_status(priv_dev, ctrl_req);
> +               break;
> +       case USB_REQ_CLEAR_FEATURE:
> +               ret = cdns3_req_ep0_handle_feature(priv_dev, ctrl_req, 0);
> +               break;
> +       case USB_REQ_SET_FEATURE:
> +               ret = cdns3_req_ep0_handle_feature(priv_dev, ctrl_req, 1);
> +               break;
> +       case USB_REQ_SET_SEL:
> +               ret = cdns3_req_ep0_set_sel(priv_dev, ctrl_req);
> +               break;
> +       case USB_REQ_SET_ISOCH_DELAY:
> +               ret = cdns3_req_ep0_set_isoch_delay(priv_dev, ctrl_req);
> +               break;
> +       default:
> +               ret = cdns3_ep0_delegate_req(priv_dev, ctrl_req);
> +               break;
> +       }
> +
> +       return ret;
> +}
> +
> +static void __pending_setup_status_handler(struct cdns3_device *priv_dev)
> +{
> +       struct usb_request *request = priv_dev->pending_status_request;
> +
> +       if (priv_dev->status_completion_no_call && request &&
> +           request->complete) {
> +               request->complete(&priv_dev->eps[0]->endpoint, request);
> +               priv_dev->status_completion_no_call = 0;
> +       }
> +}
> +
> +void cdns3_pending_setup_status_handler(struct work_struct *work)
> +{
> +       struct cdns3_device *priv_dev = container_of(work, struct cdns3_device,
> +                       pending_status_wq);
> +       unsigned long flags;
> +
> +       spin_lock_irqsave(&priv_dev->lock, flags);
> +       __pending_setup_status_handler(priv_dev);
> +       spin_unlock_irqrestore(&priv_dev->lock, flags);
> +}
> +
> +/**
> + * cdns3_gadget_ep_giveback - call struct usb_request's ->complete callback
> + * @priv_ep: The endpoint to whom the request belongs to
> + * @priv_req: The request we're giving back
> + * @status: completion code for the request
> + *
> + * Must be called with controller's lock held and interrupts disabled. This
> + * function will unmap @req and call its ->complete() callback to notify upper
> + * layers that it has completed.
> + */
> +
> +void cdns3_gadget_ep0_giveback(struct cdns3_device *priv_dev,
> +                              int status)
> +{
> +       struct cdns3_endpoint *priv_ep;
> +       struct usb_request *request;
> +
> +       priv_ep = priv_dev->eps[0];
> +       request = cdns3_next_request(&priv_ep->pending_req_list);
> +
> +       priv_ep->dir = priv_dev->ep0_data_dir;
> +       cdns3_gadget_giveback(priv_ep, to_cdns3_request(request), status);
> +}
> +
> +/**
> + * cdns3_ep0_setup_phase - Handling setup USB requests
> + * @priv_dev: extended gadget object
> + */
> +static void cdns3_ep0_setup_phase(struct cdns3_device *priv_dev)
> +{
> +       struct usb_ctrlrequest *ctrl = priv_dev->setup_buf;
> +       struct cdns3_endpoint *priv_ep = priv_dev->eps[0];
> +       int result;
> +
> +       priv_dev->ep0_data_dir = ctrl->bRequestType & USB_DIR_IN;
> +
> +       trace_cdns3_ctrl_req(ctrl);
> +
> +       if (!list_empty(&priv_ep->pending_req_list)) {
> +               struct usb_request *request;
> +
> +               request = cdns3_next_request(&priv_ep->pending_req_list);
> +               priv_ep->dir = priv_dev->ep0_data_dir;
> +               cdns3_gadget_giveback(priv_ep, to_cdns3_request(request),
> +                                     -ECONNRESET);
> +       }
> +
> +       if (le16_to_cpu(ctrl->wLength))
> +               priv_dev->ep0_stage = CDNS3_DATA_STAGE;
> +       else
> +               priv_dev->ep0_stage = CDNS3_STATUS_STAGE;
> +
> +       if ((ctrl->bRequestType & USB_TYPE_MASK) == USB_TYPE_STANDARD)
> +               result = cdns3_ep0_standard_request(priv_dev, ctrl);
> +       else
> +               result = cdns3_ep0_delegate_req(priv_dev, ctrl);
> +
> +       if (priv_dev->ep0_stage == CDNS3_STATUS_STAGE &&
> +           result != USB_GADGET_DELAYED_STATUS) {
> +               cdns3_ep0_complete_setup(priv_dev, 0, 1);
> +       } else if (result < 0) {
> +               cdns3_ep0_complete_setup(priv_dev, 1, 1);
> +       }
> +}
> +
> +static void cdns3_transfer_completed(struct cdns3_device *priv_dev)
> +{
> +       struct cdns3_endpoint *priv_ep = priv_dev->eps[0];
> +
> +       if (!list_empty(&priv_ep->pending_req_list)) {
> +               struct usb_request *request;
> +
> +               trace_cdns3_complete_trb(priv_ep, priv_ep->trb_pool);
> +               request = cdns3_next_request(&priv_ep->pending_req_list);
> +
> +               request->actual =
> +                       TRB_LEN(le32_to_cpu(priv_ep->trb_pool->length));
> +
> +               priv_ep->dir = priv_dev->ep0_data_dir;
> +               cdns3_gadget_giveback(priv_ep, to_cdns3_request(request), 0);
> +       }
> +
> +       cdns3_ep0_complete_setup(priv_dev, 0, 0);
> +}
> +
> +/**
> + * cdns3_check_new_setup - Check if controller receive new SETUP packet.
> + * @priv_dev: extended gadget object
> + *
> + * The SETUP packet can be kept in on-chip memory or in system memory.
> + */
> +static bool cdns3_check_new_setup(struct cdns3_device *priv_dev)
> +{
> +       u32 ep_sts_reg;
> +
> +       cdns3_select_ep(priv_dev, 0 | USB_DIR_OUT);
> +       ep_sts_reg = readl(&priv_dev->regs->ep_sts);
> +
> +       return !!(ep_sts_reg & (EP_STS_SETUP | EP_STS_STPWAIT));
> +}
> +
> +/**
> + * cdns3_check_ep0_interrupt_proceed - Processes interrupt related to endpoint 0
> + * @priv_dev: extended gadget object
> + * @dir: USB_DIR_IN for IN direction, USB_DIR_OUT for OUT direction
> + */
> +void cdns3_check_ep0_interrupt_proceed(struct cdns3_device *priv_dev, int dir)
> +{
> +       u32 ep_sts_reg;
> +
> +       cdns3_select_ep(priv_dev, dir);
> +
> +       ep_sts_reg = readl(&priv_dev->regs->ep_sts);
> +       writel(ep_sts_reg, &priv_dev->regs->ep_sts);
> +
> +       trace_cdns3_ep0_irq(priv_dev, ep_sts_reg);
> +
> +       __pending_setup_status_handler(priv_dev);
> +
> +       if ((ep_sts_reg & EP_STS_SETUP)) {
> +               cdns3_ep0_setup_phase(priv_dev);
> +       } else if ((ep_sts_reg & EP_STS_IOC) || (ep_sts_reg & EP_STS_ISP)) {
> +               priv_dev->ep0_data_dir = dir;
> +               cdns3_transfer_completed(priv_dev);
> +       }
> +
> +       if (ep_sts_reg & EP_STS_DESCMIS) {
> +               if (dir == 0 && !priv_dev->setup_pending)
> +                       cdns3_prepare_setup_packet(priv_dev);
> +       }
> +}
> +
> +/**
> + * cdns3_gadget_ep0_enable
> + * Function shouldn't be called by gadget driver,
> + * endpoint 0 is allways active
> + */
> +static int cdns3_gadget_ep0_enable(struct usb_ep *ep,
> +                                  const struct usb_endpoint_descriptor *desc)
> +{
> +       return -EINVAL;
> +}
> +
> +/**
> + * cdns3_gadget_ep0_disable
> + * Function shouldn't be called by gadget driver,
> + * endpoint 0 is allways active
> + */
> +static int cdns3_gadget_ep0_disable(struct usb_ep *ep)
> +{
> +       return -EINVAL;
> +}
> +
> +/**
> + * cdns3_gadget_ep0_set_halt
> + * @ep: pointer to endpoint zero object
> + * @value: 1 for set stall, 0 for clear stall
> + *
> + * Returns 0
> + */
> +static int cdns3_gadget_ep0_set_halt(struct usb_ep *ep, int value)
> +{
> +       /* TODO */
> +       return 0;
> +}
> +
> +/**
> + * cdns3_gadget_ep0_queue Transfer data on endpoint zero
> + * @ep: pointer to endpoint zero object
> + * @request: pointer to request object
> + * @gfp_flags: gfp flags
> + *
> + * Returns 0 on success, error code elsewhere
> + */
> +static int cdns3_gadget_ep0_queue(struct usb_ep *ep,
> +                                 struct usb_request *request,
> +                                 gfp_t gfp_flags)
> +{
> +       struct cdns3_endpoint *priv_ep = ep_to_cdns3_ep(ep);
> +       struct cdns3_device *priv_dev = priv_ep->cdns3_dev;
> +       unsigned long flags;
> +       int erdy_sent = 0;
> +       int ret = 0;
> +
> +       dev_dbg(priv_dev->dev, "Queue to Ep0%s L: %d\n",
> +               priv_dev->ep0_data_dir ? "IN" : "OUT",
> +               request->length);
> +
> +       /* cancel the request if controller receive new SETUP packet. */
> +       if (cdns3_check_new_setup(priv_dev))
> +               return -ECONNRESET;
> +
> +       /* send STATUS stage. Should be called only for SET_CONFIGURATION */
> +       if (priv_dev->ep0_stage == CDNS3_STATUS_STAGE) {
> +               spin_lock_irqsave(&priv_dev->lock, flags);
> +               cdns3_select_ep(priv_dev, 0x00);
> +
> +               erdy_sent = !priv_dev->hw_configured_flag;
> +               cdns3_set_hw_configuration(priv_dev);
> +
> +               if (!erdy_sent)
> +                       cdns3_ep0_complete_setup(priv_dev, 0, 1);
> +
> +               request->actual = 0;
> +               priv_dev->status_completion_no_call = true;
> +               priv_dev->pending_status_request = request;
> +               spin_unlock_irqrestore(&priv_dev->lock, flags);
> +
> +               /*
> +                * Since there is no completion interrupt for status stage,
> +                * it needs to call ->completion in software after
> +                * ep0_queue is back.
> +                */
> +               queue_work(system_freezable_wq, &priv_dev->pending_status_wq);
> +               return 0;
> +       }
> +
> +       spin_lock_irqsave(&priv_dev->lock, flags);
> +       if (!list_empty(&priv_ep->pending_req_list)) {
> +               dev_err(priv_dev->dev,
> +                       "can't handle multiple requests for ep0\n");
> +               spin_unlock_irqrestore(&priv_dev->lock, flags);
> +               return -EBUSY;
> +       }
> +
> +       ret = usb_gadget_map_request_by_dev(priv_dev->sysdev, request,
> +                                           priv_dev->ep0_data_dir);
> +       if (ret) {
> +               spin_unlock_irqrestore(&priv_dev->lock, flags);
> +               dev_err(priv_dev->dev, "failed to map request\n");
> +               return -EINVAL;
> +       }
> +
> +       request->status = -EINPROGRESS;
> +       list_add_tail(&request->list, &priv_ep->pending_req_list);
> +       cdns3_ep0_run_transfer(priv_dev, request->dma, request->length, 1);
> +       spin_unlock_irqrestore(&priv_dev->lock, flags);
> +
> +       return ret;
> +}
> +
> +/**
> + * cdns3_gadget_ep_set_wedge Set wedge on selected endpoint
> + * @ep: endpoint object
> + *
> + * Returns 0
> + */
> +int cdns3_gadget_ep_set_wedge(struct usb_ep *ep)
> +{
> +       struct cdns3_endpoint *priv_ep = ep_to_cdns3_ep(ep);
> +       struct cdns3_device *priv_dev = priv_ep->cdns3_dev;
> +
> +       dev_dbg(priv_dev->dev, "Wedge for %s\n", ep->name);
> +       cdns3_gadget_ep_set_halt(ep, 1);
> +       priv_ep->flags |= EP_WEDGE;
> +
> +       return 0;
> +}
> +
> +const struct usb_ep_ops cdns3_gadget_ep0_ops = {
> +       .enable = cdns3_gadget_ep0_enable,
> +       .disable = cdns3_gadget_ep0_disable,
> +       .alloc_request = cdns3_gadget_ep_alloc_request,
> +       .free_request = cdns3_gadget_ep_free_request,
> +       .queue = cdns3_gadget_ep0_queue,
> +       .dequeue = cdns3_gadget_ep_dequeue,
> +       .set_halt = cdns3_gadget_ep0_set_halt,
> +       .set_wedge = cdns3_gadget_ep_set_wedge,
> +};
> +
> +/**
> + * cdns3_ep0_config - Configures default endpoint
> + * @priv_dev: extended gadget object
> + *
> + * Functions sets parameters: maximal packet size and enables interrupts
> + */
> +void cdns3_ep0_config(struct cdns3_device *priv_dev)
> +{
> +       struct cdns3_usb_regs __iomem *regs;
> +       struct cdns3_endpoint *priv_ep;
> +       u32 max_packet_size = 64;
> +
> +       regs = priv_dev->regs;
> +
> +       if (priv_dev->gadget.speed == USB_SPEED_SUPER)
> +               max_packet_size = 512;
> +
> +       priv_ep = priv_dev->eps[0];
> +
> +       if (!list_empty(&priv_ep->pending_req_list)) {
> +               struct usb_request *request;
> +
> +               request = cdns3_next_request(&priv_ep->pending_req_list);
> +               list_del_init(&request->list);
> +       }
> +
> +       priv_dev->u1_allowed = 0;
> +       priv_dev->u2_allowed = 0;
> +
> +       priv_dev->gadget.ep0->maxpacket = max_packet_size;
> +       cdns3_gadget_ep0_desc.wMaxPacketSize = cpu_to_le16(max_packet_size);
> +
> +       /* init ep out */
> +       cdns3_select_ep(priv_dev, USB_DIR_OUT);
> +
> +       writel(EP_CFG_ENABLE | EP_CFG_MAXPKTSIZE(max_packet_size),
> +              &regs->ep_cfg);
> +
> +       writel(EP_STS_EN_SETUPEN | EP_STS_EN_DESCMISEN | EP_STS_EN_TRBERREN,
> +              &regs->ep_sts_en);
> +
> +       /* init ep in */
> +       cdns3_select_ep(priv_dev, USB_DIR_IN);
> +
> +       writel(EP_CFG_ENABLE | EP_CFG_MAXPKTSIZE(max_packet_size),
> +              &regs->ep_cfg);
> +
> +       writel(EP_STS_EN_SETUPEN | EP_STS_EN_TRBERREN, &regs->ep_sts_en);
> +
> +       cdns3_set_register_bit(&regs->usb_conf, USB_CONF_U1DS | USB_CONF_U2DS);
> +}
> +
> +/**
> + * cdns3_init_ep0 Initializes software endpoint 0 of gadget
> + * @priv_dev: extended gadget object
> + * @ep_priv: extended endpoint object
> + *
> + * Returns 0 on success else error code.
> + */
> +int cdns3_init_ep0(struct cdns3_device *priv_dev,
> +                  struct cdns3_endpoint *priv_ep)
> +{
> +       sprintf(priv_ep->name, "ep0");
> +
> +       /* fill linux fields */
> +       priv_ep->endpoint.ops = &cdns3_gadget_ep0_ops;
> +       priv_ep->endpoint.maxburst = 1;
> +       usb_ep_set_maxpacket_limit(&priv_ep->endpoint,
> +                                  CDNS3_EP0_MAX_PACKET_LIMIT);
> +       priv_ep->endpoint.address = 0;
> +       priv_ep->endpoint.caps.type_control = 1;
> +       priv_ep->endpoint.caps.dir_in = 1;
> +       priv_ep->endpoint.caps.dir_out = 1;
> +       priv_ep->endpoint.name = priv_ep->name;
> +       priv_ep->endpoint.desc = &cdns3_gadget_ep0_desc;
> +       priv_dev->gadget.ep0 = &priv_ep->endpoint;
> +       priv_ep->type = USB_ENDPOINT_XFER_CONTROL;
> +
> +       return cdns3_allocate_trb_pool(priv_ep);
> +}
> diff --git a/drivers/usb/cdns3/gadget-export.h b/drivers/usb/cdns3/gadget-export.h
> new file mode 100644
> index 000000000000..577469eee961
> --- /dev/null
> +++ b/drivers/usb/cdns3/gadget-export.h
> @@ -0,0 +1,28 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +/*
> + * Cadence USBSS DRD Driver - Gadget Export APIs.
> + *
> + * Copyright (C) 2017 NXP
> + * Copyright (C) 2017-2018 NXP
> + *
> + * Authors: Peter Chen <peter.chen@nxp.com>
> + */
> +#ifndef __LINUX_CDNS3_GADGET_EXPORT
> +#define __LINUX_CDNS3_GADGET_EXPORT
> +
> +#ifdef CONFIG_USB_CDNS3_GADGET
> +
> +int cdns3_gadget_init(struct cdns3 *cdns);
> +void cdns3_gadget_exit(struct cdns3 *cdns);
> +#else
> +
> +static inline int cdns3_gadget_init(struct cdns3 *cdns)
> +{
> +       return -ENXIO;
> +}
> +
> +static inline void cdns3_gadget_exit(struct cdns3 *cdns) { }
> +
> +#endif
> +
> +#endif /* __LINUX_CDNS3_GADGET_EXPORT */
> diff --git a/drivers/usb/cdns3/gadget.c b/drivers/usb/cdns3/gadget.c
> new file mode 100644
> index 000000000000..0d95eb00be37
> --- /dev/null
> +++ b/drivers/usb/cdns3/gadget.c
> @@ -0,0 +1,2102 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Cadence USBSS DRD Driver - gadget side.
> + *
> + * Copyright (C) 2018 Cadence Design Systems.
> + * Copyright (C) 2017-2018 NXP
> + *
> + * Authors: Pawel Jez <pjez@cadence.com>,
> + *          Pawel Laszczak <pawell@cadence.com>
> + *         Peter Chen <peter.chen@nxp.com>
> + */
> +
> +#include <linux/dma-mapping.h>
> +#include <linux/usb/gadget.h>
> +
> +#include "core.h"
> +#include "gadget-export.h"
> +#include "gadget.h"
> +
> +#include "trace.h"
> +
> +static int __cdns3_gadget_ep_queue(struct usb_ep *ep,
> +                                  struct usb_request *request,
> +                                  gfp_t gfp_flags);
> +
> +/**
> + * cdns3_handshake - spin reading  until handshake completes or fails
> + * @ptr: address of device controller register to be read
> + * @mask: bits to look at in result of read
> + * @done: value of those bits when handshake succeeds
> + * @usec: timeout in microseconds
> + *
> + * Returns negative errno, or zero on success
> + *
> + * Success happens when the "mask" bits have the specified value (hardware
> + * handshake done). There are two failure modes: "usec" have passed (major
> + * hardware flakeout), or the register reads as all-ones (hardware removed).
> + */
> +int cdns3_handshake(void __iomem *ptr, u32 mask, u32 done, int usec)
> +{
> +       u32     result;
> +
> +       do {
> +               result = readl(ptr);
> +               if (result == ~(u32)0)  /* card removed */
> +                       return -ENODEV;
> +               result &= mask;
> +               if (result == done)
> +                       return 0;
> +               udelay(1);
> +               usec--;
> +       } while (usec > 0);
> +       return -ETIMEDOUT;
> +}
> +
> +/**
> + * cdns3_set_register_bit - set bit in given register.
> + * @ptr: address of device controller register to be read and changed
> + * @mask: bits requested to set
> + */
> +void cdns3_set_register_bit(void __iomem *ptr, u32 mask)
> +{
> +       mask = readl(ptr) | mask;
> +       writel(mask, ptr);
> +}
> +
> +/**
> + * cdns3_ep_addr_to_index - Macro converts endpoint address to
> + * index of endpoint object in cdns3_device.eps[] container
> + * @ep_addr: endpoint address for which endpoint object is required
> + *
> + */
> +u8 cdns3_ep_addr_to_index(u8 ep_addr)
> +{
> +       return (((ep_addr & 0x7F)) + ((ep_addr & USB_DIR_IN) ? 16 : 0));
> +}
> +
> +/**
> + * cdns3_next_request - returns next request from list
> + * @list: list containing requests
> + *
> + * Returns request or NULL if no requests in list
> + */
> +struct usb_request *cdns3_next_request(struct list_head *list)
> +{
> +       if (list_empty(list))
> +               return NULL;
> +       return list_first_entry_or_null(list, struct usb_request, list);
> +}
> +
> +/**
> + * cdns3_next_priv_request - returns next request from list
> + * @list: list containing requests
> + *
> + * Returns request or NULL if no requests in list
> + */
> +struct cdns3_request *cdns3_next_priv_request(struct list_head *list)
> +{
> +       if (list_empty(list))
> +               return NULL;
> +       return list_first_entry_or_null(list, struct cdns3_request, list);
> +}
> +
> +/**
> + * select_ep - selects endpoint
> + * @priv_dev:  extended gadget object
> + * @ep: endpoint address
> + */
> +void cdns3_select_ep(struct cdns3_device *priv_dev, u32 ep)
> +{
> +       if (priv_dev->selected_ep == ep)
> +               return;
> +
> +       priv_dev->selected_ep = ep;
> +       writel(ep, &priv_dev->regs->ep_sel);
> +}
> +
> +dma_addr_t cdns3_trb_virt_to_dma(struct cdns3_endpoint *priv_ep,
> +                                struct cdns3_trb *trb)
> +{
> +       u32 offset = (char *)trb - (char *)priv_ep->trb_pool;
> +
> +       return priv_ep->trb_pool_dma + offset;
> +}
> +
> +int cdns3_ring_size(struct cdns3_endpoint *priv_ep)
> +{
> +       switch (priv_ep->type) {
> +       case USB_ENDPOINT_XFER_ISOC:
> +               return TRB_ISO_RING_SIZE;
> +       case USB_ENDPOINT_XFER_CONTROL:
> +               return TRB_CTRL_RING_SIZE;
> +       default:
> +               return TRB_RING_SIZE;
> +       }
> +}
> +
> +/**
> + * cdns3_allocate_trb_pool - Allocates TRB's pool for selected endpoint
> + * @priv_ep:  endpoint object
> + *
> + * Function will return 0 on success or -ENOMEM on allocation error
> + */
> +int cdns3_allocate_trb_pool(struct cdns3_endpoint *priv_ep)
> +{
> +       struct cdns3_device *priv_dev = priv_ep->cdns3_dev;
> +       int ring_size = cdns3_ring_size(priv_ep);
> +       struct cdns3_trb *link_trb;
> +
> +       if (!priv_ep->trb_pool) {
> +               priv_ep->trb_pool = dma_zalloc_coherent(priv_dev->sysdev,
> +                                                       ring_size,
> +                                                       &priv_ep->trb_pool_dma,
> +                                                       GFP_DMA);
> +               if (!priv_ep->trb_pool)
> +                       return -ENOMEM;
> +       } else {
> +               memset(priv_ep->trb_pool, 0, ring_size);
> +       }
> +
> +       if (!priv_ep->num)
> +               return 0;
> +
> +       if (!priv_ep->aligned_buff) {
> +               void *buff = dma_alloc_coherent(priv_dev->sysdev,
> +                                               CDNS3_ALIGNED_BUF_SIZE,
> +                                               &priv_ep->aligned_dma_addr,
> +                                               GFP_DMA);
> +
> +               priv_ep->aligned_buff  = buff;
> +               if (!priv_ep->aligned_buff) {
> +                       dma_free_coherent(priv_dev->sysdev,
> +                                         ring_size,
> +                                         priv_ep->trb_pool,
> +                                         priv_ep->trb_pool_dma);
> +                       priv_ep->trb_pool = NULL;
> +
> +                       return -ENOMEM;
> +               }
> +       }
> +
> +       priv_ep->num_trbs = ring_size / TRB_SIZE;
> +       /* Initialize the last TRB as Link TRB */
> +       link_trb = (priv_ep->trb_pool + (priv_ep->num_trbs - 1));
> +       link_trb->buffer = TRB_BUFFER(priv_ep->trb_pool_dma);
> +       link_trb->control = TRB_CYCLE | TRB_TYPE(TRB_LINK) |
> +                           TRB_CHAIN | TRB_TOGGLE;
> +
> +       return 0;
> +}
> +
> +static void cdns3_free_trb_pool(struct cdns3_endpoint *priv_ep)
> +{
> +       struct cdns3_device *priv_dev = priv_ep->cdns3_dev;
> +
> +       if (priv_ep->trb_pool) {
> +               dma_free_coherent(priv_dev->sysdev,
> +                                 cdns3_ring_size(priv_ep),
> +                                 priv_ep->trb_pool, priv_ep->trb_pool_dma);
> +               priv_ep->trb_pool = NULL;
> +       }
> +
> +       if (priv_ep->aligned_buff) {
> +               dma_free_coherent(priv_dev->sysdev, CDNS3_ALIGNED_BUF_SIZE,
> +                                 priv_ep->aligned_buff,
> +                                 priv_ep->aligned_dma_addr);
> +               priv_ep->aligned_buff = NULL;
> +       }
> +}
> +
> +/**
> + * cdns3_data_flush - flush data at onchip buffer
> + * @priv_ep: endpoint object
> + *
> + * Endpoint must be selected before call to this function
> + *
> + * Returns zero on success or negative value on failure
> + */
> +static int cdns3_data_flush(struct cdns3_endpoint *priv_ep)
> +{
> +       struct cdns3_device *priv_dev = priv_ep->cdns3_dev;
> +
> +       writel(EP_CMD_DFLUSH, &priv_dev->regs->ep_cmd);
> +
> +       /* wait for DFLUSH cleared */
> +       return cdns3_handshake(&priv_dev->regs->ep_cmd, EP_CMD_DFLUSH, 0, 100);
> +}
> +
> +/**
> + * cdns3_ep_stall_flush - Stalls and flushes selected endpoint
> + * @priv_ep: endpoint object
> + *
> + * Endpoint must be selected before call to this function
> + */
> +static void cdns3_ep_stall_flush(struct cdns3_endpoint *priv_ep)
> +{
> +       struct cdns3_device *priv_dev = priv_ep->cdns3_dev;
> +
> +       writel(EP_CMD_DFLUSH | EP_CMD_ERDY | EP_CMD_SSTALL,
> +              &priv_dev->regs->ep_cmd);
> +
> +       /* wait for DFLUSH cleared */
> +       cdns3_handshake(&priv_dev->regs->ep_cmd, EP_CMD_DFLUSH, 0, 100);
> +       priv_ep->flags |= EP_STALL;
> +}
> +
> +/**
> + * cdns3_hw_reset_eps_config - reset endpoints configuration kept by controller.
> + * @priv_dev: extended gadget object
> + */
> +void cdns3_hw_reset_eps_config(struct cdns3_device *priv_dev)
> +{
> +       writel(USB_CONF_CFGRST, &priv_dev->regs->usb_conf);
> +
> +       cdns3_allow_enable_l1(priv_dev, 0);
> +       priv_dev->hw_configured_flag = 0;
> +       priv_dev->onchip_mem_allocated_size = 0;
> +}
> +
> +/**
> + * cdns3_ep_inc_trb - increment a trb index.
> + * @index: Pointer to the TRB index to increment.
> + * @cs: Cycle state
> + * @trb_in_seg: number of TRBs in segment
> + *
> + * The index should never point to the link TRB. After incrementing,
> + * if it is point to the link TRB, wrap around to the beginning and revert
> + * cycle state bit The
> + * link TRB is always at the last TRB entry.
> + */
> +static void cdns3_ep_inc_trb(int *index, u8 *cs, int trb_in_seg)
> +{
> +       (*index)++;
> +       if (*index == (trb_in_seg - 1)) {
> +               *index = 0;
> +               *cs ^=  1;
> +       }
> +}
> +
> +/**
> + * cdns3_ep_inc_enq - increment endpoint's enqueue pointer
> + * @priv_ep: The endpoint whose enqueue pointer we're incrementing
> + */
> +static void cdns3_ep_inc_enq(struct cdns3_endpoint *priv_ep)
> +{
> +       priv_ep->free_trbs--;
> +       cdns3_ep_inc_trb(&priv_ep->enqueue, &priv_ep->pcs, priv_ep->num_trbs);
> +}
> +
> +/**
> + * cdns3_ep_inc_deq - increment endpoint's dequeue pointer
> + * @priv_ep: The endpoint whose dequeue pointer we're incrementing
> + */
> +static void cdns3_ep_inc_deq(struct cdns3_endpoint *priv_ep)
> +{
> +       priv_ep->free_trbs++;
> +       cdns3_ep_inc_trb(&priv_ep->dequeue, &priv_ep->ccs, priv_ep->num_trbs);
> +}
> +
> +/**
> + * cdns3_allow_enable_l1 - enable/disable permits to transition to L1.
> + * @priv_dev: Extended gadget object
> + * @enable: Enable/disable permit to transition to L1.
> + *
> + * If bit USB_CONF_L1EN is set and device receive Extended Token packet,
> + * then controller answer with ACK handshake.
> + * If bit USB_CONF_L1DS is set and device receive Extended Token packet,
> + * then controller answer with NYET handshake.
> + */
> +void cdns3_allow_enable_l1(struct cdns3_device *priv_dev, int enable)
> +{
> +       if (enable)
> +               writel(USB_CONF_L1EN, &priv_dev->regs->usb_conf);
> +       else
> +               writel(USB_CONF_L1DS, &priv_dev->regs->usb_conf);
> +}
> +
> +enum usb_device_speed cdns3_get_speed(struct cdns3_device *priv_dev)
> +{
> +       u32 reg;
> +
> +       reg = readl(&priv_dev->regs->usb_sts);
> +
> +       if (DEV_SUPERSPEED(reg))
> +               return USB_SPEED_SUPER;
> +       else if (DEV_HIGHSPEED(reg))
> +               return USB_SPEED_HIGH;
> +       else if (DEV_FULLSPEED(reg))
> +               return USB_SPEED_FULL;
> +       else if (DEV_LOWSPEED(reg))
> +               return USB_SPEED_LOW;
> +       return USB_SPEED_UNKNOWN;
> +}
> +
> +/**
> + * cdns3_start_all_request - add to ring all request not started
> + * @priv_dev: Extended gadget object
> + * @priv_ep: The endpoint for whom request will be started.
> + *
> + * Returns return ENOMEM if transfer ring i not enough TRBs to start
> + *         all requests.
> + */
> +static int cdns3_start_all_request(struct cdns3_device *priv_dev,
> +                                  struct cdns3_endpoint *priv_ep)
> +{
> +       struct cdns3_request *priv_req;
> +       struct usb_request *request;
> +       int ret = 0;
> +
> +       while (!list_empty(&priv_ep->deferred_req_list)) {
> +               request = cdns3_next_request(&priv_ep->deferred_req_list);
> +               priv_req = to_cdns3_request(request);
> +
> +               ret = cdns3_ep_run_transfer(priv_ep, request);
> +               if (ret)
> +                       return ret;
> +
> +               list_del(&request->list);
> +               list_add_tail(&request->list,
> +                             &priv_ep->pending_req_list);
> +       }
> +
> +       priv_ep->flags &= ~EP_RING_FULL;
> +       return ret;
> +}
> +
> +/**
> + * cdns3_descmiss_copy_data copy data from internal requests to request queued
> + * by class driver.
> + * @priv_ep: extended endpoint object
> + * @request: request object
> + */
> +static void cdns3_descmiss_copy_data(struct cdns3_endpoint *priv_ep,
> +                                    struct usb_request *request)
> +{
> +       struct usb_request *descmiss_req;
> +       struct cdns3_request *descmiss_priv_req;
> +
> +       while (!list_empty(&priv_ep->descmiss_req_list)) {
> +               int chunk_end;
> +               int length;
> +
> +               descmiss_priv_req =
> +                       cdns3_next_priv_request(&priv_ep->descmiss_req_list);
> +               descmiss_req = &descmiss_priv_req->request;
> +
> +               /* driver can't touch pending request */
> +               if (descmiss_priv_req->flags & REQUEST_PENDING)
> +                       break;
> +
> +               chunk_end = descmiss_priv_req->flags & REQUEST_INTERNAL_CH;
> +               length = request->actual + descmiss_req->actual;
> +
> +               if (length <= request->length) {
> +                       memcpy(&((u8 *)request->buf)[request->actual],
> +                              descmiss_req->buf,
> +                              descmiss_req->actual);
> +                       request->actual = length;
> +               } else {
> +                       /* It should never occures */
> +                       request->status = -ENOMEM;
> +               }
> +
> +               list_del_init(&descmiss_priv_req->list);
> +
> +               kfree(descmiss_req->buf);
> +               cdns3_gadget_ep_free_request(&priv_ep->endpoint, descmiss_req);
> +
> +               if (!chunk_end)
> +                       break;
> +       }
> +}
> +
> +/**
> + * cdns3_gadget_giveback - call struct usb_request's ->complete callback
> + * @priv_ep: The endpoint to whom the request belongs to
> + * @priv_req: The request we're giving back
> + * @status: completion code for the request
> + *
> + * Must be called with controller's lock held and interrupts disabled. This
> + * function will unmap @req and call its ->complete() callback to notify upper
> + * layers that it has completed.
> + */
> +void cdns3_gadget_giveback(struct cdns3_endpoint *priv_ep,
> +                          struct cdns3_request *priv_req,
> +                          int status)
> +{
> +       struct cdns3_device *priv_dev = priv_ep->cdns3_dev;
> +       struct usb_request *request = &priv_req->request;
> +
> +       list_del_init(&request->list);
> +
> +       if (request->status == -EINPROGRESS)
> +               request->status = status;
> +
> +       usb_gadget_unmap_request_by_dev(priv_dev->sysdev, request,
> +                                       priv_ep->dir);
> +
> +       priv_req->flags &= ~REQUEST_PENDING;
> +       trace_cdns3_gadget_giveback(priv_req);
> +
> +       /* WA1: */
> +       if (priv_ep->flags & EP_QUIRK_EXTRA_BUF_EN &&
> +           priv_req->flags & REQUEST_INTERNAL) {
> +               struct usb_request *req;
> +
> +               req = cdns3_next_request(&priv_ep->deferred_req_list);
> +               request = req;
> +               priv_ep->descmis_req = NULL;
> +
> +               if (!req)
> +                       return;
> +
> +               cdns3_descmiss_copy_data(priv_ep, req);
> +               if (!(priv_ep->flags & EP_QUIRK_END_TRANSFER) &&
> +                   req->length != req->actual) {
> +                       /* wait for next part of transfer */
> +                       return;
> +               }
> +
> +               if (req->status == -EINPROGRESS)
> +                       req->status = 0;
> +
> +               list_del_init(&req->list);
> +               cdns3_start_all_request(priv_dev, priv_ep);
> +       }
> +
> +       /* Start all not pending request */
> +       if (priv_ep->flags & EP_RING_FULL)
> +               cdns3_start_all_request(priv_dev, priv_ep);
> +
> +       if (request->complete) {
> +               spin_unlock(&priv_dev->lock);
> +               usb_gadget_giveback_request(&priv_ep->endpoint,
> +                                           request);
> +               spin_lock(&priv_dev->lock);
> +       }
> +
> +       if (request->buf == priv_dev->zlp_buf)
> +               cdns3_gadget_ep_free_request(&priv_ep->endpoint, request);
> +}
> +
> +/**
> + * cdns3_ep_run_transfer - start transfer on no-default endpoint hardware
> + * @priv_ep: endpoint object
> + *
> + * Returns zero on success or negative value on failure
> + */
> +int cdns3_ep_run_transfer(struct cdns3_endpoint *priv_ep,
> +                         struct usb_request *request)
> +{
> +       struct cdns3_device *priv_dev = priv_ep->cdns3_dev;
> +       struct cdns3_request *priv_req;
> +       struct cdns3_trb *trb;
> +       dma_addr_t trb_dma;
> +       int sg_iter = 0;
> +       u32 first_pcs;
> +       int  num_trb;
> +       int address;
> +       int pcs;
> +
> +       if (priv_ep->type == USB_ENDPOINT_XFER_ISOC)
> +               num_trb = priv_ep->interval;
> +       else
> +               num_trb = request->num_sgs ? request->num_sgs : 1;
> +
> +       if (num_trb > priv_ep->free_trbs) {
> +               priv_ep->flags |= EP_RING_FULL;
> +               return -ENOBUFS;
> +       }
> +
> +       priv_req = to_cdns3_request(request);
> +       address = priv_ep->endpoint.desc->bEndpointAddress;
> +
> +       priv_ep->flags |= EP_PENDING_REQUEST;
> +       trb_dma = request->dma;
> +
> +       /* must allocate buffer aligned to 8 */
> +       if ((request->dma % 8)) {
> +               if (request->length <= CDNS3_ALIGNED_BUF_SIZE) {
> +                       memcpy(priv_ep->aligned_buff, request->buf,
> +                              request->length);
> +                       trb_dma = priv_ep->aligned_dma_addr;
> +               } else {
> +                       return -ENOMEM;
> +               }
> +       }
> +
> +       trb = priv_ep->trb_pool + priv_ep->enqueue;
> +       priv_req->start_trb = priv_ep->enqueue;
> +       priv_req->trb = trb;
> +
> +       /* prepare ring */
> +       if ((priv_ep->enqueue + num_trb)  >= (priv_ep->num_trbs - 1)) {
> +               /*updating C bt in  Link TRB before starting DMA*/
> +               struct cdns3_trb *link_trb = priv_ep->trb_pool +
> +                                            (priv_ep->num_trbs - 1);
> +               link_trb->control = ((priv_ep->pcs) ? TRB_CYCLE : 0) |
> +                                   TRB_TYPE(TRB_LINK) | TRB_CHAIN |
> +                                   TRB_TOGGLE;
> +       }
> +
> +       first_pcs = priv_ep->pcs ? TRB_CYCLE : 0;
> +
> +       do {
> +       /* fill TRB */
> +               trb->buffer = TRB_BUFFER(request->num_sgs == 0
> +                               ? trb_dma : request->sg[sg_iter].dma_address);
> +
> +               trb->length = TRB_BURST_LEN(16) |
> +                   TRB_LEN(request->num_sgs == 0 ?
> +                               request->length : request->sg[sg_iter].length);
> +
> +               trb->control = TRB_TYPE(TRB_NORMAL);
> +               pcs = priv_ep->pcs ? TRB_CYCLE : 0;
> +
> +               /*
> +                * first trb should be prepared as last to avoid processing
> +                *  transfer to early
> +                */
> +               if (sg_iter != 0)
> +                       trb->control |= pcs;
> +
> +               if (priv_ep->type == USB_ENDPOINT_XFER_ISOC  && !priv_ep->dir) {
> +                       trb->control |= TRB_IOC | TRB_ISP;
> +               } else {
> +                       /* for last element in TD or in SG list */
> +                       if (sg_iter == (num_trb - 1) && sg_iter != 0)
> +                               trb->control |= pcs | TRB_IOC | TRB_ISP;
> +               }
> +               ++sg_iter;
> +               priv_req->end_trb = priv_ep->enqueue;
> +               cdns3_ep_inc_enq(priv_ep);
> +               trb = priv_ep->trb_pool + priv_ep->enqueue;
> +       } while (sg_iter < num_trb);
> +
> +       trb = priv_req->trb;
> +
> +       /*
> +        * Memory barrier = Cycle Bit must be set before trb->length  and
> +        * trb->buffer fields.
> +        */
> +       wmb();
> +
> +       priv_req->flags |= REQUEST_PENDING;
> +
> +       /* give the TD to the consumer*/
> +       if (sg_iter == 1)
> +               trb->control |= first_pcs | TRB_IOC | TRB_ISP;
> +       else
> +               trb->control |= first_pcs;
> +
> +       trace_cdns3_prepare_trb(priv_ep, priv_req->trb);
> +       trace_cdns3_ring(priv_ep);
> +
> +       /* arm transfer on selected endpoint */
> +       cdns3_select_ep(priv_ep->cdns3_dev, address);
> +
> +       /*
> +        * For DMULT mode we can set address to transfer ring only once after
> +        * enabling endpoint.
> +        */
> +       if (priv_ep->flags & EP_UPDATE_EP_TRBADDR) {
> +               writel(EP_TRADDR_TRADDR(priv_ep->trb_pool_dma),
> +                      &priv_dev->regs->ep_traddr);
> +               priv_ep->flags &= ~EP_UPDATE_EP_TRBADDR;
> +       }
> +
> +       /*clearing TRBERR and EP_STS_DESCMIS before seting DRDY*/
> +       writel(EP_STS_TRBERR | EP_STS_DESCMIS, &priv_dev->regs->ep_sts);
> +       trace_cdns3_doorbell_epx(priv_ep->name,
> +                                readl(&priv_dev->regs->ep_traddr));
> +       writel(EP_CMD_DRDY, &priv_dev->regs->ep_cmd);
> +
> +       return 0;
> +}
> +
> +void cdns3_set_hw_configuration(struct cdns3_device *priv_dev)
> +{
> +       struct cdns3_endpoint *priv_ep;
> +       struct usb_ep *ep;
> +       int result = 0;
> +
> +       if (priv_dev->hw_configured_flag)
> +               return;
> +
> +       writel(USB_CONF_CFGSET, &priv_dev->regs->usb_conf);
> +       writel(EP_CMD_ERDY | EP_CMD_REQ_CMPL, &priv_dev->regs->ep_cmd);
> +
> +       cdns3_set_register_bit(&priv_dev->regs->usb_conf,
> +                              USB_CONF_U1EN | USB_CONF_U2EN);
> +
> +       /* wait until configuration set */
> +       result = cdns3_handshake(&priv_dev->regs->usb_sts,
> +                                USB_STS_CFGSTS_MASK, 1, 100);
> +
> +       priv_dev->hw_configured_flag = 1;
> +       cdns3_allow_enable_l1(priv_dev, 1);
> +
> +       list_for_each_entry(ep, &priv_dev->gadget.ep_list, ep_list) {
> +               if (ep->enabled) {
> +                       priv_ep = ep_to_cdns3_ep(ep);
> +                       cdns3_start_all_request(priv_dev, priv_ep);
> +               }
> +       }
> +}
> +
> +/**
> + * cdns3_request_handled - check whether request has been handled by DMA
> + *
> + * @priv_ep: extended endpoint object.
> + * @priv_req: request object for checking
> + *
> + * Endpoint must be selected before invoking this function.
> + *
> + * Returns false if request has not been handled by DMA, else returns true.
> + *
> + * SR - start ring
> + * ER -  end ring
> + * DQ = priv_ep->dequeue - dequeue position
> + * EQ = priv_ep->enqueue -  enqueue position
> + * ST = priv_req->start_trb - index of first TRB in transfer ring
> + * ET = priv_req->end_trb - index of last TRB in transfer ring
> + * CI = current_index - index of processed TRB by DMA.
> + *
> + * As first step, function checks if cycle bit for priv_req->start_trb is
> + * correct.
> + *
> + * some rules:
> + * 1. priv_ep->dequeue never exceed current_index.
> + * 2  priv_ep->enqueue never exceed priv_ep->dequeue
> + *
> + * Then We can split recognition into two parts:
> + * Case 1 - priv_ep->dequeue < current_index
> + *      SR ... EQ ... DQ ... CI ... ER
> + *      SR ... DQ ... CI ... EQ ... ER
> + *
> + *      Request has been handled by DMA if ST and ET is between DQ and CI.
> + *
> + * Case 2 - priv_ep->dequeue > current_index
> + * This situation take place when CI go through the LINK TRB at the end of
> + * transfer ring.
> + *      SR ... CI ... EQ ... DQ ... ER
> + *
> + *      Request has been handled by DMA if ET is less then CI or
> + *      ET is greater or equal DQ.
> + */
> +static bool cdns3_request_handled(struct cdns3_endpoint *priv_ep,
> +                                 struct cdns3_request *priv_req)
> +{
> +       struct cdns3_device *priv_dev = priv_ep->cdns3_dev;
> +       struct cdns3_trb *trb = priv_req->trb;
> +       int current_index = 0;
> +       int handled = 0;
> +
> +       current_index = (readl(&priv_dev->regs->ep_traddr) -
> +                        priv_ep->trb_pool_dma) / TRB_SIZE;
> +
> +       trb = &priv_ep->trb_pool[priv_req->start_trb];
> +
> +       if ((trb->control  & TRB_CYCLE) != priv_ep->ccs)
> +               goto finish;
> +
> +       if (priv_ep->dequeue < current_index) {
> +               if ((current_index == (priv_ep->num_trbs - 1)) &&
> +                   !priv_ep->dequeue)
> +                       goto finish;
> +
> +               if (priv_req->end_trb >= priv_ep->dequeue &&
> +                   priv_req->end_trb < current_index)
> +                       handled = 1;
> +       } else if (priv_ep->dequeue  > current_index) {
> +               if (priv_req->end_trb  < current_index ||
> +                   priv_req->end_trb >= priv_ep->dequeue)
> +                       handled = 1;
> +       }
> +
> +finish:
> +       trace_cdns3_request_handled(priv_req, current_index, handled);
> +
> +       return handled;
> +}
> +
> +static void cdns3_transfer_completed(struct cdns3_device *priv_dev,
> +                                    struct cdns3_endpoint *priv_ep)
> +{
> +       struct cdns3_request *priv_req;
> +       struct usb_request *request;
> +       struct cdns3_trb *trb;
> +       int current_trb;
> +
> +       while (!list_empty(&priv_ep->pending_req_list)) {
> +               request = cdns3_next_request(&priv_ep->pending_req_list);
> +               priv_req = to_cdns3_request(request);
> +
> +               if (!cdns3_request_handled(priv_ep, priv_req))
> +                       return;
> +
> +               if (request->dma % 8 && priv_ep->dir == USB_DIR_OUT)
> +                       memcpy(request->buf, priv_ep->aligned_buff,
> +                              request->length);
> +
> +               trb = priv_ep->trb_pool + priv_ep->dequeue;
> +               trace_cdns3_complete_trb(priv_ep, trb);
> +
> +               if (trb != priv_req->trb)
> +                       dev_warn(priv_dev->dev,
> +                                "request_trb=0x%p, queue_trb=0x%p\n",
> +                                priv_req->trb, trb);
> +
> +               request->actual = TRB_LEN(le32_to_cpu(trb->length));
> +               current_trb = priv_req->start_trb;
> +
> +               while (current_trb != priv_req->end_trb) {
> +                       cdns3_ep_inc_deq(priv_ep);
> +                       current_trb = priv_ep->dequeue;
> +               }
> +
> +               cdns3_ep_inc_deq(priv_ep);
> +               cdns3_gadget_giveback(priv_ep, priv_req, 0);
> +       }
> +       priv_ep->flags &= ~EP_PENDING_REQUEST;
> +}
> +
> +/**
> + * cdns3_descmissing_packet - handles descriptor missing event.
> + * @priv_dev: extended gadget object
> + *
> + * WA1: Controller for OUT endpoints has shared on-chip buffers for all incoming
> + * packets, including ep0out. It's FIFO buffer, so packets must be handle by DMA
> + * in correct order. If the first packet in the buffer will not be handled,
> + * then the following packets directed for other endpoints and  functions
> + * will be blocked.
> + * Additionally the packets directed to one endpoint can block entire on-chip
> + * buffers. In this case transfer to other endpoints also will blocked.
> + *
> + * To resolve this issue after raising the descriptor missing interrupt
> + * driver prepares internal usb_request object and use it to arm DMA transfer.
> + *
> + * The problematic situation was observed in case when endpoint has been enabled
> + * but no usb_request were queued. Driver try detects such endpoints and will
> + * use this workaround only for these endpoint.
> + *
> + * Driver use limited number of buffer. This number can be set by macro
> + * CDNS_WA1_NUM_BUFFERS.
> + *
> + * Such blocking situation was observed on ACM gadget. For this function
> + * host send OUT data packet but ACM function is not prepared for this packet.
> + *
> + * It's limitation of controller but maybe this issues should be fixed in
> + * function driver.
> + */
> +static int cdns3_descmissing_packet(struct cdns3_endpoint *priv_ep)
> +{
> +       struct cdns3_request *priv_req;
> +       struct usb_request *request;
> +
> +       if (priv_ep->flags & EP_QUIRK_EXTRA_BUF_DET) {
> +               priv_ep->flags &= ~EP_QUIRK_EXTRA_BUF_DET;
> +               priv_ep->flags |= EP_QUIRK_EXTRA_BUF_EN;
> +       }
> +
> +       request = cdns3_gadget_ep_alloc_request(&priv_ep->endpoint,
> +                                               GFP_ATOMIC);
> +       if (!request)
> +               return -ENOMEM;
> +
> +       priv_req = to_cdns3_request(request);
> +       priv_req->flags |= REQUEST_INTERNAL;
> +
> +       /* if this field is still assigned it indicate that transfer related
> +        * with this request has not been finished yet. Driver in this
> +        * case simply allocate next request and assign flag REQUEST_INTERNAL_CH
> +        * flag to previous one. It will indicate that current request is
> +        * part of the previous one.
> +        */
> +       if (priv_ep->descmis_req)
> +               priv_ep->descmis_req->flags |= REQUEST_INTERNAL_CH;
> +
> +       priv_req->request.buf = kzalloc(CDNS3_DESCMIS_BUF_SIZE,
> +                                       GFP_ATOMIC);
> +       if (!priv_req) {
> +               cdns3_gadget_ep_free_request(&priv_ep->endpoint, request);
> +               return -ENOMEM;
> +       }
> +
> +       priv_req->request.length = CDNS3_DESCMIS_BUF_SIZE;
> +       priv_ep->descmis_req = priv_req;
> +
> +       __cdns3_gadget_ep_queue(&priv_ep->endpoint,
> +                               &priv_ep->descmis_req->request,
> +                               GFP_ATOMIC);
> +
> +       return 0;
> +}
> +
> +/**
> + * cdns3_check_ep_interrupt_proceed - Processes interrupt related to endpoint
> + * @priv_ep: endpoint object
> + *
> + * Returns 0
> + */
> +static int cdns3_check_ep_interrupt_proceed(struct cdns3_endpoint *priv_ep)
> +{
> +       struct cdns3_device *priv_dev = priv_ep->cdns3_dev;
> +       u32 ep_sts_reg;
> +
> +       cdns3_select_ep(priv_dev, priv_ep->endpoint.address);
> +
> +       trace_cdns3_epx_irq(priv_dev, priv_ep);
> +
> +       ep_sts_reg = readl(&priv_dev->regs->ep_sts);
> +       writel(ep_sts_reg, &priv_dev->regs->ep_sts);
> +
> +       if ((ep_sts_reg & EP_STS_IOC) || (ep_sts_reg & EP_STS_ISP)) {
> +               if (priv_ep->flags & EP_QUIRK_EXTRA_BUF_EN) {
> +                       if (ep_sts_reg & EP_STS_ISP)
> +                               priv_ep->flags |= EP_QUIRK_END_TRANSFER;
> +                       else
> +                               priv_ep->flags &= ~EP_QUIRK_END_TRANSFER;
> +               }
> +               cdns3_transfer_completed(priv_dev, priv_ep);
> +       }
> +
> +       /*
> +        * For isochronous transfer driver completes request on IOC or on
> +        * TRBERR. IOC appears only when device receive OUT data packet.
> +        * If host disable stream or lost some packet then the only way to
> +        * finish all queued transfer is to do it on TRBERR event.
> +        */
> +       if ((ep_sts_reg & EP_STS_TRBERR) &&
> +           priv_ep->type == USB_ENDPOINT_XFER_ISOC)
> +               cdns3_transfer_completed(priv_dev, priv_ep);
> +
> +       /*
> +        * WA1: this condition should only be meet when
> +        * priv_ep->flags & EP_QUIRK_EXTRA_BUF_DET or
> +        * priv_ep->flags & EP_QUIRK_EXTRA_BUF_EN.
> +        * In other cases this interrupt will be disabled/
> +        */
> +       if (ep_sts_reg & EP_STS_DESCMIS) {
> +               int err;
> +
> +               err = cdns3_descmissing_packet(priv_ep);
> +               if (err)
> +                       dev_err(priv_dev->dev,
> +                               "Failed: No sufficient memory for DESCMIS\n");
> +       }
> +
> +       return 0;
> +}
> +
> +/**
> + * cdns3_check_usb_interrupt_proceed - Processes interrupt related to device
> + * @priv_dev: extended gadget object
> + * @usb_ists: bitmap representation of device's reported interrupts
> + * (usb_ists register value)
> + */
> +static void cdns3_check_usb_interrupt_proceed(struct cdns3_device *priv_dev,
> +                                             u32 usb_ists)
> +{
> +       int speed = 0;
> +
> +       trace_cdns3_usb_irq(priv_dev, usb_ists);
> +       /* Connection detected */
> +       if (usb_ists & (USB_ISTS_CON2I | USB_ISTS_CONI)) {
> +               speed = cdns3_get_speed(priv_dev);
> +               priv_dev->gadget.speed = speed;
> +               usb_gadget_set_state(&priv_dev->gadget, USB_STATE_POWERED);
> +               cdns3_ep0_config(priv_dev);
> +       }
> +
> +       /* Disconnection detected */
> +       if (usb_ists & (USB_ISTS_DIS2I | USB_ISTS_DISI)) {
> +               if (priv_dev->gadget_driver &&
> +                   priv_dev->gadget_driver->disconnect) {
> +                       spin_unlock(&priv_dev->lock);
> +                       priv_dev->gadget_driver->disconnect(&priv_dev->gadget);
> +                       spin_lock(&priv_dev->lock);
> +               }
> +
> +               priv_dev->gadget.speed = USB_SPEED_UNKNOWN;
> +               usb_gadget_set_state(&priv_dev->gadget, USB_STATE_NOTATTACHED);
> +               cdns3_hw_reset_eps_config(priv_dev);
> +       }
> +
> +       /* reset*/
> +       if (usb_ists & (USB_ISTS_UWRESI | USB_ISTS_UHRESI | USB_ISTS_U2RESI)) {
> +               /*read again to check the actuall speed*/
> +               speed = cdns3_get_speed(priv_dev);
> +               usb_gadget_set_state(&priv_dev->gadget, USB_STATE_DEFAULT);
> +               priv_dev->gadget.speed = speed;
> +               cdns3_hw_reset_eps_config(priv_dev);
> +               cdns3_ep0_config(priv_dev);
> +       }
> +}
> +
> +/**
> + * cdns3_device_irq_handler- interrupt handler for device part of controller
> + *
> + * @irq: irq number for cdns3 core device
> + * @data: structure of cdns3
> + *
> + * Returns IRQ_HANDLED or IRQ_NONE
> + */
> +static irqreturn_t cdns3_device_irq_handler(int irq, void *data)
> +{
> +       struct cdns3_device *priv_dev;
> +       struct cdns3 *cdns = data;
> +       irqreturn_t ret = IRQ_NONE;
> +       unsigned long flags;
> +       u32 reg;
> +
> +       priv_dev = cdns->gadget_dev;
> +       spin_lock_irqsave(&priv_dev->lock, flags);
> +
> +       /* check USB device interrupt */
> +       reg = readl(&priv_dev->regs->usb_ists);
> +       writel(reg, &priv_dev->regs->usb_ists);
> +
> +       if (reg) {
> +               cdns3_check_usb_interrupt_proceed(priv_dev, reg);
> +               ret = IRQ_HANDLED;
> +       }
> +
> +       /* check endpoint interrupt */
> +       reg = readl(&priv_dev->regs->ep_ists);
> +
> +       if (reg) {
> +               reg = ~reg & readl(&priv_dev->regs->ep_ien);
> +               /* mask deferred interrupt. */
> +               writel(reg, &priv_dev->regs->ep_ien);
> +               ret = IRQ_WAKE_THREAD;
> +       }
> +
> +       spin_unlock_irqrestore(&priv_dev->lock, flags);
> +       return ret;
> +}
> +
> +/**
> + * cdns3_device_thread_irq_handler- interrupt handler for device part
> + * of controller
> + *
> + * @irq: irq number for cdns3 core device
> + * @data: structure of cdns3
> + *
> + * Returns IRQ_HANDLED or IRQ_NONE
> + */
> +static irqreturn_t cdns3_device_thread_irq_handler(int irq, void *data)
> +{
> +       struct cdns3_device *priv_dev;
> +       struct cdns3 *cdns = data;
> +       irqreturn_t ret = IRQ_NONE;
> +       unsigned long flags;
> +       u32 ep_ien;
> +       int bit;
> +       u32 reg;
> +
> +       priv_dev = cdns->gadget_dev;
> +       spin_lock_irqsave(&priv_dev->lock, flags);
> +
> +       reg = readl(&priv_dev->regs->ep_ists);
> +       ep_ien = reg;
> +
> +       /* handle default endpoint OUT */
> +       if (reg & EP_ISTS_EP_OUT0) {
> +               cdns3_check_ep0_interrupt_proceed(priv_dev, USB_DIR_OUT);
> +               ret = IRQ_HANDLED;
> +       }
> +
> +       /* handle default endpoint IN */
> +       if (reg & EP_ISTS_EP_IN0) {
> +               cdns3_check_ep0_interrupt_proceed(priv_dev, USB_DIR_IN);
> +               ret = IRQ_HANDLED;
> +       }
> +
> +       /* check if interrupt from non default endpoint, if no exit */
> +       reg &= ~(EP_ISTS_EP_OUT0 | EP_ISTS_EP_IN0);
> +       if (!reg)
> +               goto irqend;
> +
> +       for_each_set_bit(bit, (unsigned long *)&reg,
> +                        sizeof(u32) * BITS_PER_BYTE) {
> +               cdns3_check_ep_interrupt_proceed(priv_dev->eps[bit]);
> +               ret = IRQ_HANDLED;
> +       }
> +
> +irqend:
> +       ep_ien |= readl(&priv_dev->regs->ep_ien);
> +       /* Unmask all handled EP interrupts */
> +       writel(ep_ien, &priv_dev->regs->ep_ien);
> +       spin_unlock_irqrestore(&priv_dev->lock, flags);
> +       return ret;
> +}
> +
> +/**
> + * cdns3_ep_onchip_buffer_reserve - Try to reserve onchip buf for EP
> + *
> + * The real reservation will occur during write to EP_CFG register,
> + * this function is used to check if the 'size' reservation is allowed.
> + *
> + * @priv_dev: extended gadget object
> + * @size: the size (KB) for EP would like to allocate
> + *
> + * Return 0 if the required size can met or negative value on failure
> + */
> +static int cdns3_ep_onchip_buffer_reserve(struct cdns3_device *priv_dev,
> +                                         int size)
> +{
> +       u32 onchip_mem;
> +
> +       priv_dev->onchip_mem_allocated_size += size;
> +
> +       onchip_mem = USB_CAP2_ACTUAL_MEM_SIZE(readl(&priv_dev->regs->usb_cap2));
> +       if (!onchip_mem)
> +               onchip_mem = 256;
> +
> +       /* 2KB is reserved for EP0*/
> +       onchip_mem -= 2;
> +       if (priv_dev->onchip_mem_allocated_size > onchip_mem) {
> +               priv_dev->onchip_mem_allocated_size -= size;
> +               return -EPERM;
> +       }
> +
> +       return 0;
> +}
> +
> +/**
> + * cdns3_ep_config Configure hardware endpoint
> + * @priv_ep: extended endpoint object
> + */
> +void cdns3_ep_config(struct cdns3_endpoint *priv_ep)
> +{
> +       bool is_iso_ep = (priv_ep->type == USB_ENDPOINT_XFER_ISOC);
> +       struct cdns3_device *priv_dev = priv_ep->cdns3_dev;
> +       u32 bEndpointAddress = priv_ep->num | priv_ep->dir;
> +       u32 max_packet_size = 0;
> +       u32 ep_cfg = 0;
> +       int ret;
> +
> +       switch (priv_ep->type) {
> +       case USB_ENDPOINT_XFER_INT:
> +               ep_cfg = EP_CFG_EPTYPE(USB_ENDPOINT_XFER_INT);
> +               break;
> +       case USB_ENDPOINT_XFER_BULK:
> +               ep_cfg = EP_CFG_EPTYPE(USB_ENDPOINT_XFER_BULK);
> +               break;
> +       default:
> +               ep_cfg = EP_CFG_EPTYPE(USB_ENDPOINT_XFER_ISOC);
> +       }
> +
> +       switch (priv_dev->gadget.speed) {
> +       case USB_SPEED_FULL:
> +               max_packet_size = is_iso_ep ? 1023 : 64;
> +               break;
> +       case USB_SPEED_HIGH:
> +               max_packet_size = is_iso_ep ? 1024 : 512;
> +               break;
> +       case USB_SPEED_SUPER:
> +               max_packet_size = 1024;
> +               break;
> +       default:
> +               /* all other speed are not supported */
> +               return;
> +       }
> +
> +       ret = cdns3_ep_onchip_buffer_reserve(priv_dev, CDNS3_EP_BUF_SIZE);
> +       if (ret) {
> +               dev_err(priv_dev->dev, "onchip mem is full, ep is invalid\n");
> +               return;
> +       }
> +
> +       ep_cfg |= EP_CFG_MAXPKTSIZE(max_packet_size) |
> +                 EP_CFG_BUFFERING(CDNS3_EP_BUF_SIZE - 1) |
> +                 EP_CFG_MAXBURST(priv_ep->endpoint.maxburst);
> +
> +       cdns3_select_ep(priv_dev, bEndpointAddress);
> +       writel(ep_cfg, &priv_dev->regs->ep_cfg);
> +
> +       dev_dbg(priv_dev->dev, "Configure %s: with val %08x\n",
> +               priv_ep->name, ep_cfg);
> +}
> +
> +/* Find correct direction for HW endpoint according to description */
> +static int cdns3_ep_dir_is_correct(struct usb_endpoint_descriptor *desc,
> +                                  struct cdns3_endpoint *priv_ep)
> +{
> +       return (priv_ep->endpoint.caps.dir_in && usb_endpoint_dir_in(desc)) ||
> +              (priv_ep->endpoint.caps.dir_out && usb_endpoint_dir_out(desc));
> +}
> +
> +static struct
> +cdns3_endpoint *cdns3_find_available_ep(struct cdns3_device *priv_dev,
> +                                       struct usb_endpoint_descriptor *desc)
> +{
> +       struct usb_ep *ep;
> +       struct cdns3_endpoint *priv_ep;
> +
> +       list_for_each_entry(ep, &priv_dev->gadget.ep_list, ep_list) {
> +               unsigned long num;
> +               int ret;
> +               /* ep name pattern likes epXin or epXout */
> +               char c[2] = {ep->name[2], '\0'};
> +
> +               ret = kstrtoul(c, 10, &num);
> +               if (ret)
> +                       return ERR_PTR(ret);
> +
> +               priv_ep = ep_to_cdns3_ep(ep);
> +               if (cdns3_ep_dir_is_correct(desc, priv_ep)) {
> +                       if (!(priv_ep->flags & EP_CLAIMED)) {
> +                               priv_ep->num  = num;
> +                               return priv_ep;
> +                       }
> +               }
> +       }
> +
> +       return ERR_PTR(-ENOENT);
> +}
> +
> +/*
> + *  Cadence IP has one limitation that all endpoints must be configured
> + * (Type & MaxPacketSize) before setting configuration through hardware
> + * register, it means we can't change endpoints configuration after
> + * set_configuration.
> + *
> + * This function set EP_CLAIMED flag which is added when the gadget driver
> + * uses usb_ep_autoconfig to configure specific endpoint;
> + * When the udc driver receives set_configurion request,
> + * it goes through all claimed endpoints, and configure all endpoints
> + * accordingly.
> + *
> + * At usb_ep_ops.enable/disable, we only enable and disable endpoint through
> + * ep_cfg register which can be changed after set_configuration, and do
> + * some software operation accordingly.
> + */
> +static struct
> +usb_ep *cdns3_gadget_match_ep(struct usb_gadget *gadget,
> +                             struct usb_endpoint_descriptor *desc,
> +                             struct usb_ss_ep_comp_descriptor *comp_desc)
> +{
> +       struct cdns3_device *priv_dev = gadget_to_cdns3_device(gadget);
> +       struct cdns3_endpoint *priv_ep;
> +       unsigned long flags;
> +
> +       priv_ep = cdns3_find_available_ep(priv_dev, desc);
> +       if (IS_ERR(priv_ep)) {
> +               dev_err(priv_dev->dev, "no available ep\n");
> +               return NULL;
> +       }
> +
> +       dev_dbg(priv_dev->dev, "match endpoint: %s\n", priv_ep->name);
> +
> +       spin_lock_irqsave(&priv_dev->lock, flags);
> +       priv_ep->endpoint.desc = desc;
> +       priv_ep->dir  = usb_endpoint_dir_in(desc) ? USB_DIR_IN : USB_DIR_OUT;
> +       priv_ep->type = usb_endpoint_type(desc);
> +       priv_ep->flags |= EP_CLAIMED;
> +       spin_unlock_irqrestore(&priv_dev->lock, flags);
> +       return &priv_ep->endpoint;
> +}
> +
> +/**
> + * cdns3_gadget_ep_alloc_request Allocates request
> + * @ep: endpoint object associated with request
> + * @gfp_flags: gfp flags
> + *
> + * Returns allocated request address, NULL on allocation error
> + */
> +struct usb_request *cdns3_gadget_ep_alloc_request(struct usb_ep *ep,
> +                                                 gfp_t gfp_flags)
> +{
> +       struct cdns3_endpoint *priv_ep = ep_to_cdns3_ep(ep);
> +       struct cdns3_request *priv_req;
> +
> +       priv_req = kzalloc(sizeof(*priv_req), gfp_flags);
> +       if (!priv_req)
> +               return NULL;
> +
> +       priv_req->priv_ep = priv_ep;
> +
> +       trace_cdns3_alloc_request(priv_req);
> +       return &priv_req->request;
> +}
> +
> +/**
> + * cdns3_gadget_ep_free_request Free memory occupied by request
> + * @ep: endpoint object associated with request
> + * @request: request to free memory
> + */
> +void cdns3_gadget_ep_free_request(struct usb_ep *ep,
> +                                 struct usb_request *request)
> +{
> +       struct cdns3_request *priv_req = to_cdns3_request(request);
> +
> +       trace_cdns3_free_request(priv_req);
> +       kfree(priv_req);
> +}
> +
> +/**
> + * cdns3_gadget_ep_enable Enable endpoint
> + * @ep: endpoint object
> + * @desc: endpoint descriptor
> + *
> + * Returns 0 on success, error code elsewhere
> + */
> +static int cdns3_gadget_ep_enable(struct usb_ep *ep,
> +                                 const struct usb_endpoint_descriptor *desc)
> +{
> +       struct cdns3_endpoint *priv_ep;
> +       struct cdns3_device *priv_dev;
> +       u32 reg = EP_STS_EN_TRBERREN;
> +       u32 bEndpointAddress;
> +       unsigned long flags;
> +       int ret;
> +
> +       priv_ep = ep_to_cdns3_ep(ep);
> +       priv_dev = priv_ep->cdns3_dev;
> +
> +       if (!ep || !desc || desc->bDescriptorType != USB_DT_ENDPOINT) {
> +               dev_dbg(priv_dev->dev, "usbss: invalid parameters\n");
> +               return -EINVAL;
> +       }
> +
> +       if (!desc->wMaxPacketSize) {
> +               dev_err(priv_dev->dev, "usbss: missing wMaxPacketSize\n");
> +               return -EINVAL;
> +       }
> +
> +       if (dev_WARN_ONCE(priv_dev->dev, priv_ep->flags & EP_ENABLED,
> +                         "%s is already enabled\n", priv_ep->name))
> +               return 0;
> +
> +       spin_lock_irqsave(&priv_dev->lock, flags);
> +
> +       priv_ep->endpoint.desc = desc;
> +       priv_ep->type = usb_endpoint_type(desc);
> +       priv_ep->interval = desc->bInterval ? BIT(desc->bInterval - 1) : 0;
> +
> +       if (priv_ep->interval > ISO_MAX_INTERVAL &&
> +           priv_ep->type == USB_ENDPOINT_XFER_ISOC) {
> +               dev_err(priv_dev->dev, "Driver is limited to %d period\n",
> +                       ISO_MAX_INTERVAL);
> +
> +               ret =  -EINVAL;
> +               goto exit;
> +       }
> +
> +       ret = cdns3_allocate_trb_pool(priv_ep);
> +
> +       if (ret)
> +               goto exit;
> +
> +       bEndpointAddress = priv_ep->num | priv_ep->dir;
> +       cdns3_select_ep(priv_dev, bEndpointAddress);
> +
> +       trace_cdns3_gadget_ep_enable(priv_ep);
> +
> +       writel(EP_CMD_EPRST, &priv_dev->regs->ep_cmd);
> +
> +       ret = cdns3_handshake(&priv_dev->regs->ep_cmd,
> +                             EP_CMD_CSTALL | EP_CMD_EPRST, 0, 100);
> +
> +       /* enable interrupt for selected endpoint */
> +       cdns3_set_register_bit(&priv_dev->regs->ep_ien,
> +                              BIT(cdns3_ep_addr_to_index(bEndpointAddress)));
> +       /*
> +        * WA1: Set flag for all not ISOC OUT endpoints. If this flag is set
> +        * driver try to detect whether endpoint need additional internal
> +        * buffer for unblocking on-chip FIFO buffer. This flag will be cleared
> +        * if before first DESCMISS interrupt the DMA will be armed.
> +        */
> +       if (!priv_ep->dir && priv_ep->type != USB_ENDPOINT_XFER_ISOC) {
> +               priv_ep->flags |= EP_QUIRK_EXTRA_BUF_DET;
> +               reg |= EP_STS_EN_DESCMISEN;
> +       }
> +
> +       writel(reg, &priv_dev->regs->ep_sts_en);
> +
> +       cdns3_set_register_bit(&priv_dev->regs->ep_cfg, EP_CFG_ENABLE);
> +
> +       ep->desc = desc;
> +       priv_ep->flags &= ~(EP_PENDING_REQUEST | EP_STALL |
> +                           EP_QUIRK_EXTRA_BUF_EN);
> +       priv_ep->flags |= EP_ENABLED | EP_UPDATE_EP_TRBADDR;
> +       priv_ep->enqueue = 0;
> +       priv_ep->dequeue = 0;
> +       reg = readl(&priv_dev->regs->ep_sts);
> +       priv_ep->pcs = !!EP_STS_CCS(reg);
> +       priv_ep->ccs = !!EP_STS_CCS(reg);
> +       /* one TRB is reserved for link TRB used in DMULT mode*/
> +       priv_ep->free_trbs = priv_ep->num_trbs - 1;
> +exit:
> +       spin_unlock_irqrestore(&priv_dev->lock, flags);
> +
> +       return ret;
> +}
> +
> +/**
> + * cdns3_gadget_ep_disable Disable endpoint
> + * @ep: endpoint object
> + *
> + * Returns 0 on success, error code elsewhere
> + */
> +static int cdns3_gadget_ep_disable(struct usb_ep *ep)
> +{
> +       struct cdns3_endpoint *priv_ep;
> +       struct cdns3_request *priv_req;
> +       struct cdns3_device *priv_dev;
> +       struct usb_request *request;
> +       unsigned long flags;
> +       int ret = 0;
> +       u32 ep_cfg;
> +
> +       if (!ep) {
> +               pr_err("usbss: invalid parameters\n");
> +               return -EINVAL;
> +       }
> +
> +       priv_ep = ep_to_cdns3_ep(ep);
> +       priv_dev = priv_ep->cdns3_dev;
> +
> +       if (dev_WARN_ONCE(priv_dev->dev, !(priv_ep->flags & EP_ENABLED),
> +                         "%s is already disabled\n", priv_ep->name))
> +               return 0;
> +
> +       spin_lock_irqsave(&priv_dev->lock, flags);
> +
> +       trace_cdns3_gadget_ep_disable(priv_ep);
> +
> +       cdns3_select_ep(priv_dev, ep->desc->bEndpointAddress);
> +       ret = cdns3_data_flush(priv_ep);
> +
> +       ep_cfg = readl(&priv_dev->regs->ep_cfg);
> +       ep_cfg &= ~EP_CFG_ENABLE;
> +       writel(ep_cfg, &priv_dev->regs->ep_cfg);
> +
> +       while (!list_empty(&priv_ep->pending_req_list)) {
> +               request = cdns3_next_request(&priv_ep->pending_req_list);
> +
> +               cdns3_gadget_giveback(priv_ep, to_cdns3_request(request),
> +                                     -ESHUTDOWN);
> +       }
> +
> +       while (!list_empty(&priv_ep->descmiss_req_list)) {
> +               priv_req = cdns3_next_priv_request(&priv_ep->descmiss_req_list);
> +
> +               kfree(priv_req->request.buf);
> +               cdns3_gadget_ep_free_request(&priv_ep->endpoint,
> +                                            &priv_req->request);
> +       }
> +
> +       while (!list_empty(&priv_ep->deferred_req_list)) {
> +               request = cdns3_next_request(&priv_ep->deferred_req_list);
> +
> +               cdns3_gadget_giveback(priv_ep, to_cdns3_request(request),
> +                                     -ESHUTDOWN);
> +       }
> +
> +       priv_ep->descmis_req = NULL;
> +
> +       ep->desc = NULL;
> +       priv_ep->flags &= ~EP_ENABLED;
> +
> +       spin_unlock_irqrestore(&priv_dev->lock, flags);
> +
> +       return ret;
> +}
> +
> +/**
> + * cdns3_gadget_ep_queue Transfer data on endpoint
> + * @ep: endpoint object
> + * @request: request object
> + * @gfp_flags: gfp flags
> + *
> + * Returns 0 on success, error code elsewhere
> + */
> +static int __cdns3_gadget_ep_queue(struct usb_ep *ep,
> +                                  struct usb_request *request,
> +                                  gfp_t gfp_flags)
> +{
> +       struct cdns3_endpoint *priv_ep = ep_to_cdns3_ep(ep);
> +       struct cdns3_device *priv_dev = priv_ep->cdns3_dev;
> +       struct cdns3_request *priv_req;
> +       int deferred = 0;
> +       int ret = 0;
> +
> +       request->actual = 0;
> +       request->status = -EINPROGRESS;
> +       priv_req = to_cdns3_request(request);
> +       trace_cdns3_ep_queue(priv_req);
> +
> +       /*
> +        * WA1: if transfer was queued before DESCMISS appear than we
> +        * can disable handling of DESCMISS interrupt. Driver assumes that it
> +        * can disable special treatment for this endpoint.
> +        */
> +       if (priv_ep->flags & EP_QUIRK_EXTRA_BUF_DET) {
> +               u32 reg;
> +
> +               cdns3_select_ep(priv_dev, priv_ep->num | priv_ep->dir);
> +               reg = readl(&priv_dev->regs->ep_sts_en);
> +               priv_ep->flags &= ~EP_QUIRK_EXTRA_BUF_DET;
> +               reg &= EP_STS_EN_DESCMISEN;
> +               writel(reg, &priv_dev->regs->ep_sts_en);
> +       }
> +
> +       /* WA1 */
> +       if (priv_ep->flags & EP_QUIRK_EXTRA_BUF_EN) {
> +               u8 pending_empty = list_empty(&priv_ep->pending_req_list);
> +               u8 descmiss_empty = list_empty(&priv_ep->descmiss_req_list);
> +
> +               /*
> +                *  DESCMISS transfer has been finished, so data will be
> +                *  directly copied from internal allocated usb_request
> +                *  objects.
> +                */
> +               if (pending_empty && !descmiss_empty &&
> +                   !(priv_req->flags & REQUEST_INTERNAL)) {
> +                       cdns3_descmiss_copy_data(priv_ep, request);
> +                       list_add_tail(&request->list,
> +                                     &priv_ep->pending_req_list);
> +                       cdns3_gadget_giveback(priv_ep, priv_req,
> +                                             request->status);
> +                       return ret;
> +               }
> +
> +               /*
> +                * WA1 driver will wait for completion DESCMISS transfer,
> +                * before starts new, not DESCMISS transfer.
> +                */
> +               if (!pending_empty && !descmiss_empty)
> +                       deferred = 1;
> +
> +               if (priv_req->flags & REQUEST_INTERNAL)
> +                       list_add_tail(&priv_req->list,
> +                                     &priv_ep->descmiss_req_list);
> +       }
> +
> +       ret = usb_gadget_map_request_by_dev(priv_dev->sysdev, request,
> +                                           usb_endpoint_dir_in(ep->desc));
> +       if (ret)
> +               return ret;
> +
> +       /*
> +        * If hardware endpoint configuration has not been set yet then
> +        * just queue request in deferred list. Transfer will be started in
> +        * cdns3_set_hw_configuration.
> +        */
> +       if (!priv_dev->hw_configured_flag)
> +               deferred = 1;
> +       else
> +               ret = cdns3_ep_run_transfer(priv_ep, request);
> +
> +       if (ret || deferred)
> +               list_add_tail(&request->list, &priv_ep->deferred_req_list);
> +       else
> +               list_add_tail(&request->list, &priv_ep->pending_req_list);
> +
> +       return ret;
> +}
> +
> +static int cdns3_gadget_ep_queue(struct usb_ep *ep, struct usb_request *request,
> +                                gfp_t gfp_flags)
> +{
> +       struct usb_request *zlp_request;
> +       struct cdns3_endpoint *priv_ep;
> +       struct cdns3_device *priv_dev;
> +       unsigned long flags;
> +       int ret;
> +
> +       if (!request || !ep)
> +               return -EINVAL;
> +
> +       priv_ep = ep_to_cdns3_ep(ep);
> +       priv_dev = priv_ep->cdns3_dev;
> +
> +       spin_lock_irqsave(&priv_dev->lock, flags);
> +
> +       ret = __cdns3_gadget_ep_queue(ep, request, gfp_flags);
> +
> +       if (ret == 0 && request->zero && request->length &&
> +           (request->length % ep->maxpacket == 0)) {
> +               struct cdns3_request *priv_req;
> +
> +               zlp_request = cdns3_gadget_ep_alloc_request(ep, GFP_ATOMIC);
> +               zlp_request->buf = priv_dev->zlp_buf;
> +               zlp_request->length = 0;
> +
> +               priv_req = to_cdns3_request(zlp_request);
> +               priv_req->flags |= REQUEST_ZLP;
> +
> +               dev_dbg(priv_dev->dev, "Queuing ZLP for endpoint: %s\n",
> +                       priv_ep->name);
> +               ret = __cdns3_gadget_ep_queue(ep, zlp_request, gfp_flags);
> +       }
> +
> +       spin_unlock_irqrestore(&priv_dev->lock, flags);
> +       return ret;
> +}
> +
> +/**
> + * cdns3_gadget_ep_dequeue Remove request from transfer queue
> + * @ep: endpoint object associated with request
> + * @request: request object
> + *
> + * Returns 0 on success, error code elsewhere
> + */
> +int cdns3_gadget_ep_dequeue(struct usb_ep *ep,
> +                           struct usb_request *request)
> +{
> +       struct cdns3_endpoint *priv_ep = ep_to_cdns3_ep(ep);
> +       struct cdns3_device *priv_dev = priv_ep->cdns3_dev;
> +       struct usb_request *req, *req_temp;
> +       struct cdns3_request *priv_req;
> +       unsigned long flags;
> +       int ret = 0;
> +
> +       if (!ep || !request || !ep->desc)
> +               return -EINVAL;
> +
> +       spin_lock_irqsave(&priv_dev->lock, flags);
> +
> +       priv_req = to_cdns3_request(request);
> +
> +       trace_cdns3_ep_dequeue(priv_req);
> +
> +       cdns3_select_ep(priv_dev, ep->desc->bEndpointAddress);
> +
> +       list_for_each_entry_safe(req, req_temp, &priv_ep->pending_req_list,
> +                                list) {
> +               if (request == req)
> +                       goto found;
> +       }
> +
> +       list_for_each_entry_safe(req, req_temp, &priv_ep->deferred_req_list,
> +                                list) {
> +               if (request == req)
> +                       goto found;
> +       }
> +
> +found:
> +       if (request == req)
> +               cdns3_gadget_giveback(priv_ep, priv_req, -ECONNRESET);
> +
> +       spin_unlock_irqrestore(&priv_dev->lock, flags);
> +       return ret;
> +}
> +
> +/**
> + * cdns3_gadget_ep_set_halt Sets/clears stall on selected endpoint
> + * @ep: endpoint object to set/clear stall on
> + * @value: 1 for set stall, 0 for clear stall
> + *
> + * Returns 0 on success, error code elsewhere
> + */
> +int cdns3_gadget_ep_set_halt(struct usb_ep *ep, int value)
> +{
> +       struct cdns3_endpoint *priv_ep = ep_to_cdns3_ep(ep);
> +       struct cdns3_device *priv_dev = priv_ep->cdns3_dev;
> +       unsigned long flags;
> +       int ret = 0;
> +
> +       if (!(priv_ep->flags & EP_ENABLED))
> +               return -EPERM;
> +
> +       spin_lock_irqsave(&priv_dev->lock, flags);
> +
> +       /* if actual transfer is pending defer setting stall on this endpoint */
> +       if ((priv_ep->flags & EP_PENDING_REQUEST) && value) {
> +               priv_ep->flags |= EP_STALL;
> +               goto finish;
> +       }
> +
> +       dev_dbg(priv_dev->dev, "Halt endpoint %s\n", priv_ep->name);
> +
> +       cdns3_select_ep(priv_dev, ep->desc->bEndpointAddress);
> +       if (value) {
> +               cdns3_ep_stall_flush(priv_ep);
> +       } else {
> +               priv_ep->flags &= ~EP_WEDGE;
> +               writel(EP_CMD_CSTALL | EP_CMD_EPRST, &priv_dev->regs->ep_cmd);
> +
> +               /* wait for EPRST cleared */
> +               ret = cdns3_handshake(&priv_dev->regs->ep_cmd,
> +                                     EP_CMD_EPRST, 0, 100);
> +               if (unlikely(ret)) {
> +                       dev_err(priv_dev->dev,
> +                               "Clearing halt condition failed for %s\n",
> +                               priv_ep->name);
> +                       goto finish;
> +
> +               } else {
> +                       priv_ep->flags &= ~EP_STALL;
> +               }
> +       }
> +
> +       priv_ep->flags &= ~EP_PENDING_REQUEST;
> +finish:
> +       spin_unlock_irqrestore(&priv_dev->lock, flags);
> +
> +       return ret;
> +}
> +
> +extern const struct usb_ep_ops cdns3_gadget_ep0_ops;
> +
> +static const struct usb_ep_ops cdns3_gadget_ep_ops = {
> +       .enable = cdns3_gadget_ep_enable,
> +       .disable = cdns3_gadget_ep_disable,
> +       .alloc_request = cdns3_gadget_ep_alloc_request,
> +       .free_request = cdns3_gadget_ep_free_request,
> +       .queue = cdns3_gadget_ep_queue,
> +       .dequeue = cdns3_gadget_ep_dequeue,
> +       .set_halt = cdns3_gadget_ep_set_halt,
> +       .set_wedge = cdns3_gadget_ep_set_wedge,
> +};
> +
> +/**
> + * cdns3_gadget_get_frame Returns number of actual ITP frame
> + * @gadget: gadget object
> + *
> + * Returns number of actual ITP frame
> + */
> +static int cdns3_gadget_get_frame(struct usb_gadget *gadget)
> +{
> +       struct cdns3_device *priv_dev = gadget_to_cdns3_device(gadget);
> +
> +       return readl(&priv_dev->regs->usb_iptn);
> +}
> +
> +static int cdns3_gadget_wakeup(struct usb_gadget *gadget)
> +{
> +       return 0;
> +}
> +
> +static int cdns3_gadget_set_selfpowered(struct usb_gadget *gadget,
> +                                       int is_selfpowered)
> +{
> +       struct cdns3_device *priv_dev = gadget_to_cdns3_device(gadget);
> +       unsigned long flags;
> +
> +       spin_lock_irqsave(&priv_dev->lock, flags);
> +       priv_dev->is_selfpowered = !!is_selfpowered;
> +       spin_unlock_irqrestore(&priv_dev->lock, flags);
> +       return 0;
> +}
> +
> +static int cdns3_gadget_pullup(struct usb_gadget *gadget, int is_on)
> +{
> +       struct cdns3_device *priv_dev = gadget_to_cdns3_device(gadget);
> +
> +       if (is_on)
> +               writel(USB_CONF_DEVEN, &priv_dev->regs->usb_conf);
> +       else
> +               writel(USB_CONF_DEVDS, &priv_dev->regs->usb_conf);
> +
> +       return 0;
> +}
> +
> +static void cdns3_gadget_config(struct cdns3_device *priv_dev)
> +{
> +       struct cdns3_usb_regs __iomem *regs = priv_dev->regs;
> +
> +       cdns3_ep0_config(priv_dev);
> +
> +       /* enable interrupts for endpoint 0 (in and out) */
> +       writel(EP_IEN_EP_OUT0 | EP_IEN_EP_IN0, &regs->ep_ien);
> +
> +       /* enable generic interrupt*/
> +       writel(USB_IEN_INIT, &regs->usb_ien);
> +       writel(USB_CONF_CLK2OFFDS | USB_CONF_L1DS, &regs->usb_conf);
> +       writel(USB_CONF_DMULT, &regs->usb_conf);
> +       cdns3_gadget_pullup(&priv_dev->gadget, 1);
> +}
> +
> +/**
> + * cdns3_gadget_udc_start Gadget start
> + * @gadget: gadget object
> + * @driver: driver which operates on this gadget
> + *
> + * Returns 0 on success, error code elsewhere
> + */
> +static int cdns3_gadget_udc_start(struct usb_gadget *gadget,
> +                                 struct usb_gadget_driver *driver)
> +{
> +       struct cdns3_device *priv_dev = gadget_to_cdns3_device(gadget);
> +       unsigned long flags;
> +
> +       spin_lock_irqsave(&priv_dev->lock, flags);
> +       priv_dev->gadget_driver = driver;
> +       cdns3_gadget_config(priv_dev);
> +       spin_unlock_irqrestore(&priv_dev->lock, flags);
> +       return 0;
> +}
> +
> +/**
> + * cdns3_gadget_udc_stop Stops gadget
> + * @gadget: gadget object
> + *
> + * Returns 0
> + */
> +static int cdns3_gadget_udc_stop(struct usb_gadget *gadget)
> +{
> +       struct cdns3_device *priv_dev = gadget_to_cdns3_device(gadget);
> +       struct cdns3_endpoint *priv_ep;
> +       u32 bEndpointAddress;
> +       struct usb_ep *ep;
> +       int ret = 0;
> +
> +       priv_dev->gadget_driver = NULL;
> +
> +       priv_dev->onchip_mem_allocated_size = 0;
> +       priv_dev->gadget.speed = USB_SPEED_UNKNOWN;
> +
> +       list_for_each_entry(ep, &priv_dev->gadget.ep_list, ep_list) {
> +               priv_ep = ep_to_cdns3_ep(ep);
> +               bEndpointAddress = priv_ep->num | priv_ep->dir;
> +               cdns3_select_ep(priv_dev, bEndpointAddress);
> +               writel(EP_CMD_EPRST, &priv_dev->regs->ep_cmd);
> +               ret = cdns3_handshake(&priv_dev->regs->ep_cmd,
> +                                     EP_CMD_EPRST, 0, 100);
> +               cdns3_free_trb_pool(priv_ep);
> +       }
> +
> +       /* disable interrupt for device */
> +       writel(0, &priv_dev->regs->usb_ien);
> +       writel(USB_CONF_DEVDS, &priv_dev->regs->usb_conf);
> +
> +       return ret;
> +}
> +
> +static const struct usb_gadget_ops cdns3_gadget_ops = {
> +       .get_frame = cdns3_gadget_get_frame,
> +       .wakeup = cdns3_gadget_wakeup,
> +       .set_selfpowered = cdns3_gadget_set_selfpowered,
> +       .pullup = cdns3_gadget_pullup,
> +       .udc_start = cdns3_gadget_udc_start,
> +       .udc_stop = cdns3_gadget_udc_stop,
> +       .match_ep = cdns3_gadget_match_ep,
> +};
> +
> +static void cdns3_free_all_eps(struct cdns3_device *priv_dev)
> +{
> +       int i;
> +
> +       /*ep0 OUT point to ep0 IN*/
> +       priv_dev->eps[16] = NULL;
> +
> +       cdns3_free_trb_pool(priv_dev->eps[0]);
> +
> +       for (i = 0; i < CDNS3_ENDPOINTS_MAX_COUNT; i++)
> +               if (priv_dev->eps[i])
> +                       devm_kfree(priv_dev->dev, priv_dev->eps[i]);
> +}
> +
> +/**
> + * cdns3_init_eps Initializes software endpoints of gadget
> + * @cdns3: extended gadget object
> + *
> + * Returns 0 on success, error code elsewhere
> + */
> +static int cdns3_init_eps(struct cdns3_device *priv_dev)
> +{
> +       u32 ep_enabled_reg, iso_ep_reg;
> +       struct cdns3_endpoint *priv_ep;
> +       int ep_dir, ep_number;
> +       u32 ep_mask;
> +       int ret = 0;
> +       int i;
> +
> +       /* Read it from USB_CAP3 to USB_CAP5 */
> +       ep_enabled_reg = readl(&priv_dev->regs->usb_cap3);
> +       iso_ep_reg = readl(&priv_dev->regs->usb_cap4);
> +
> +       dev_dbg(priv_dev->dev, "Initializing non-zero endpoints\n");
> +
> +       for (i = 0; i < CDNS3_ENDPOINTS_MAX_COUNT; i++) {
> +               ep_dir = i >> 4;        /* i div 16 */
> +               ep_number = i & 0xF;    /* i % 16 */
> +               ep_mask = BIT(i);
> +
> +               if (!(ep_enabled_reg & ep_mask))
> +                       continue;
> +
> +               if (ep_dir && !ep_number) {
> +                       priv_dev->eps[i] = priv_dev->eps[0];
> +                       continue;
> +               }
> +
> +               priv_ep = devm_kzalloc(priv_dev->dev, sizeof(*priv_ep),
> +                                      GFP_KERNEL);
> +               if (!priv_ep) {
> +                       ret = -ENOMEM;
> +                       goto err;
> +               }
> +
> +               /* set parent of endpoint object */
> +               priv_ep->cdns3_dev = priv_dev;
> +               priv_dev->eps[i] = priv_ep;
> +               priv_ep->num = ep_number;
> +               priv_ep->dir = ep_dir ? USB_DIR_IN : USB_DIR_OUT;
> +
> +               if (!ep_number) {
> +                       ret = cdns3_init_ep0(priv_dev, priv_ep);
> +                       if (ret) {
> +                               dev_err(priv_dev->dev, "Failed to init ep0\n");
> +                               goto err;
> +                       }
> +               } else {
> +                       snprintf(priv_ep->name, sizeof(priv_ep->name), "ep%d%s",
> +                                ep_number, !!ep_dir ? "in" : "out");
> +                       priv_ep->endpoint.name = priv_ep->name;
> +
> +                       usb_ep_set_maxpacket_limit(&priv_ep->endpoint,
> +                                                  CDNS3_EP_MAX_PACKET_LIMIT);
> +                       priv_ep->endpoint.max_streams = CDNS3_EP_MAX_STREAMS;
> +                       priv_ep->endpoint.ops = &cdns3_gadget_ep_ops;
> +                       if (ep_dir)
> +                               priv_ep->endpoint.caps.dir_in = 1;
> +                       else
> +                               priv_ep->endpoint.caps.dir_out = 1;
> +
> +                       if (iso_ep_reg & ep_mask)
> +                               priv_ep->endpoint.caps.type_iso = 1;
> +
> +                       priv_ep->endpoint.caps.type_bulk = 1;
> +                       priv_ep->endpoint.caps.type_int = 1;
> +                       priv_ep->endpoint.maxburst = CDNS3_EP_BUF_SIZE - 1;
> +
> +                       list_add_tail(&priv_ep->endpoint.ep_list,
> +                                     &priv_dev->gadget.ep_list);
> +               }
> +
> +               priv_ep->flags = 0;
> +
> +               dev_info(priv_dev->dev, "Initialized  %s support: %s %s\n",
> +                        priv_ep->name,
> +                        priv_ep->endpoint.caps.type_bulk ? "BULK, INT" : "",
> +                        priv_ep->endpoint.caps.type_iso ? "ISO" : "");
> +
> +               INIT_LIST_HEAD(&priv_ep->pending_req_list);
> +               INIT_LIST_HEAD(&priv_ep->deferred_req_list);
> +               INIT_LIST_HEAD(&priv_ep->descmiss_req_list);
> +       }
> +
> +       return 0;
> +err:
> +       cdns3_free_all_eps(priv_dev);
> +       return -ENOMEM;
> +}
> +
> +static void cdns3_gadget_disable(struct cdns3 *cdns)
> +{
> +       struct cdns3_device *priv_dev;
> +
> +       priv_dev = cdns->gadget_dev;
> +
> +       if (priv_dev->gadget_driver)
> +               priv_dev->gadget_driver->disconnect(&priv_dev->gadget);
> +
> +       usb_gadget_disconnect(&priv_dev->gadget);
> +       priv_dev->gadget.speed = USB_SPEED_UNKNOWN;
> +}
> +
> +void cdns3_gadget_exit(struct cdns3 *cdns)
> +{
> +       struct cdns3_device *priv_dev;
> +
> +       priv_dev = cdns->gadget_dev;
> +
> +       cdns3_gadget_disable(cdns);
> +
> +       devm_free_irq(cdns->dev, cdns->irq, cdns);
> +
> +       pm_runtime_mark_last_busy(cdns->dev);
> +       pm_runtime_put_autosuspend(cdns->dev);
> +
> +       usb_del_gadget_udc(&priv_dev->gadget);
> +
> +       cdns3_free_all_eps(priv_dev);
> +
> +       dma_free_coherent(priv_dev->sysdev, 8, priv_dev->setup_buf,
> +                         priv_dev->setup_dma);
> +
> +       kfree(priv_dev->zlp_buf);
> +       kfree(priv_dev);
> +       cdns->gadget_dev = NULL;
> +}
> +
> +static int cdns3_gadget_start(struct cdns3 *cdns)
> +{
> +       struct cdns3_device *priv_dev;
> +       u32 max_speed;
> +       int ret;
> +
> +       priv_dev = kzalloc(sizeof(*priv_dev), GFP_KERNEL);
> +       if (!priv_dev)
> +               return -ENOMEM;
> +
> +       cdns->gadget_dev = priv_dev;
> +       priv_dev->sysdev = cdns->dev;
> +       priv_dev->dev = cdns->dev;
> +       priv_dev->regs = cdns->dev_regs;
> +
> +       max_speed = usb_get_maximum_speed(cdns->dev);
> +
> +       /* Check the maximum_speed parameter */
> +       switch (max_speed) {
> +       case USB_SPEED_FULL:
> +       case USB_SPEED_HIGH:
> +       case USB_SPEED_SUPER:
> +               break;
> +       default:
> +               dev_err(cdns->dev, "invalid maximum_speed parameter %d\n",
> +                       max_speed);
> +               /* fall through */
> +       case USB_SPEED_UNKNOWN:
> +               /* default to superspeed */
> +               max_speed = USB_SPEED_SUPER;
> +               break;
> +       }
> +
> +       /* fill gadget fields */
> +       priv_dev->gadget.max_speed = max_speed;
> +       priv_dev->gadget.speed = USB_SPEED_UNKNOWN;
> +       priv_dev->gadget.ops = &cdns3_gadget_ops;
> +       priv_dev->gadget.name = "usb-ss-gadget";
> +       priv_dev->gadget.sg_supported = 1;
> +
> +       spin_lock_init(&priv_dev->lock);
> +       INIT_WORK(&priv_dev->pending_status_wq,
> +                 cdns3_pending_setup_status_handler);
> +
> +       /* initialize endpoint container */
> +       INIT_LIST_HEAD(&priv_dev->gadget.ep_list);
> +
> +       ret = cdns3_init_eps(priv_dev);
> +       if (ret) {
> +               dev_err(priv_dev->dev, "Failed to create endpoints\n");
> +               goto err1;
> +       }
> +
> +       /* allocate memory for setup packet buffer */
> +       priv_dev->setup_buf = dma_alloc_coherent(priv_dev->sysdev, 8,
> +                                                &priv_dev->setup_dma, GFP_DMA);
> +       if (!priv_dev->setup_buf) {
> +               dev_err(priv_dev->dev, "Failed to allocate memory for SETUP buffer\n");
> +               ret = -ENOMEM;
> +               goto err2;
> +       }
> +
> +       priv_dev->dev_ver = readl(&priv_dev->regs->usb_cap6);
> +       dev_dbg(priv_dev->dev, "Device Controller version: %08x\n",
> +               readl(&priv_dev->regs->usb_cap6));
> +       dev_dbg(priv_dev->dev, "USB Capabilities:: %08x\n",
> +               readl(&priv_dev->regs->usb_cap1));
> +       dev_dbg(priv_dev->dev, "On-Chip memory cnfiguration: %08x\n",
> +               readl(&priv_dev->regs->usb_cap2));
> +
> +       priv_dev->zlp_buf = kzalloc(CDNS3_EP_ZLP_BUF_SIZE, GFP_KERNEL);
> +       if (!priv_dev->zlp_buf) {
> +               ret = -ENOMEM;
> +               goto err3;
> +       }
> +
> +       /* add USB gadget device */
> +       ret = usb_add_gadget_udc(priv_dev->dev, &priv_dev->gadget);
> +       if (ret < 0) {
> +               dev_err(priv_dev->dev,
> +                       "Failed to register USB device controller\n");
> +               goto err4;
> +       }
> +
> +       return 0;
> +err4:
> +       kfree(priv_dev->zlp_buf);
> +err3:
> +       dma_free_coherent(priv_dev->sysdev, 8, priv_dev->setup_buf,
> +                         priv_dev->setup_dma);
> +err2:
> +       cdns3_free_all_eps(priv_dev);
> +err1:
> +       cdns->gadget_dev = NULL;
> +       return ret;
> +}
> +
> +static int __cdns3_gadget_init(struct cdns3 *cdns)
> +{
> +       struct cdns3_device *priv_dev;
> +       unsigned long flags;
> +       int ret = 0;
> +
> +       ret = cdns3_gadget_start(cdns);
> +       if (ret)
> +               return ret;
> +
> +       priv_dev = cdns->gadget_dev;
> +       ret = devm_request_threaded_irq(cdns->dev, cdns->irq,
> +                                       cdns3_device_irq_handler,
> +                                       cdns3_device_thread_irq_handler,
> +                                       IRQF_SHARED, dev_name(cdns->dev), cdns);
> +       if (ret)
> +               goto err0;
> +
> +       pm_runtime_get_sync(cdns->dev);
> +       spin_lock_irqsave(&priv_dev->lock, flags);
> +       spin_unlock_irqrestore(&priv_dev->lock, flags);
> +       return 0;
> +err0:
> +       cdns3_gadget_exit(cdns);
> +       return ret;
> +}
> +
> +static int cdns3_gadget_suspend(struct cdns3 *cdns, bool do_wakeup)
> +{
> +       cdns3_gadget_disable(cdns);
> +       return 0;
> +}
> +
> +static int cdns3_gadget_resume(struct cdns3 *cdns, bool hibernated)
> +{
> +       struct cdns3_device *priv_dev;
> +       unsigned long flags;
> +
> +       priv_dev = cdns->gadget_dev;
> +       spin_lock_irqsave(&priv_dev->lock, flags);
> +
> +       if (!priv_dev->gadget_driver) {
> +               spin_unlock_irqrestore(&priv_dev->lock, flags);
> +               return 0;
> +       }
> +
> +       cdns3_gadget_config(priv_dev);
> +       spin_unlock_irqrestore(&priv_dev->lock, flags);
> +       return 0;
> +}
> +
> +/**
> + * cdns3_gadget_init - initialize device structure
> + *
> + * cdns: cdns3 instance
> + *
> + * This function initializes the gadget.
> + */
> +int cdns3_gadget_init(struct cdns3 *cdns)
> +{
> +       struct cdns3_role_driver *rdrv;
> +
> +       rdrv = devm_kzalloc(cdns->dev, sizeof(*rdrv), GFP_KERNEL);
> +       if (!rdrv)
> +               return -ENOMEM;
> +
> +       rdrv->start     = __cdns3_gadget_init;
> +       rdrv->stop      = cdns3_gadget_exit;
> +       rdrv->suspend   = cdns3_gadget_suspend;
> +       rdrv->resume    = cdns3_gadget_resume;
> +       rdrv->state     = CDNS3_ROLE_STATE_INACTIVE;
> +       rdrv->name      = "gadget";
> +       cdns->roles[CDNS3_ROLE_GADGET] = rdrv;
> +
> +       return 0;
> +}
> diff --git a/drivers/usb/cdns3/gadget.h b/drivers/usb/cdns3/gadget.h
> new file mode 100644
> index 000000000000..41cec7f085ad
> --- /dev/null
> +++ b/drivers/usb/cdns3/gadget.h
> @@ -0,0 +1,1206 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +/*
> + * USBSS device controller driver header file
> + *
> + * Copyright (C) 2018 Cadence.
> + * Copyright (C) 2017-2018 NXP
> + *
> + * Author: Pawel Laszczak <pawell@cadence.com>
> + *         Pawel Jez <pjez@cadence.com>
> + *         Peter Chen <peter.chen@nxp.com>
> + */
> +#ifndef __LINUX_CDNS3_GADGET
> +#define __LINUX_CDNS3_GADGET
> +#include <linux/usb/gadget.h>
> +
> +/*
> + * USBSS-DEV register interface.
> + * This corresponds to the USBSS Device Controller Interface
> + */
> +
> +/**
> + * struct cdns3_usb_regs - device controller registers.
> + * @usb_conf:      Global Configuration Register.
> + * @usb_sts:       Global Status Register.
> + * @usb_cmd:       Global Command Register.
> + * @usb_iptn:      ITP/SOF number Register.
> + * @usb_lpm:       Global Command Register.
> + * @usb_ien:       USB Interrupt Enable Register.
> + * @usb_ists:      USB Interrupt Status Register.
> + * @ep_sel:        Endpoint Select Register.
> + * @ep_traddr:     Endpoint Transfer Ring Address Register.
> + * @ep_cfg:        Endpoint Configuration Register.
> + * @ep_cmd:        Endpoint Command Register.
> + * @ep_sts:        Endpoint Status Register.
> + * @ep_sts_sid:    Endpoint Status Register.
> + * @ep_sts_en:     Endpoint Status Register Enable.
> + * @drbl:          Doorbell Register.
> + * @ep_ien:        EP Interrupt Enable Register.
> + * @ep_ists:       EP Interrupt Status Register.
> + * @usb_pwr:       Global Power Configuration Register.
> + * @usb_conf2:     Global Configuration Register 2.
> + * @usb_cap1:      Capability Register 1.
> + * @usb_cap2:      Capability Register 2.
> + * @usb_cap3:      Capability Register 3.
> + * @usb_cap4:      Capability Register 4.
> + * @usb_cap5:      Capability Register 5.
> + * @usb_cap6:      Capability Register 6.
> + * @usb_cpkt1:     Custom Packet Register 1.
> + * @usb_cpkt2:     Custom Packet Register 2.
> + * @usb_cpkt3:     Custom Packet Register 3.
> + * @reserved1:     Reserved.
> + * @cfg_regs:      Configuration registers.
> + * @reserved2:     Reserved.
> + * @dma_axi_ctrl:  AXI Control register.
> + * @dma_axi_id:    AXI ID register.
> + * @dma_axi_cap:   AXI Capability register.
> + * @dma_axi_ctrl0: AXI Control 0 register.
> + * @dma_axi_ctrl1: AXI Control 1 register.
> + */
> +struct cdns3_usb_regs {
> +       __le32 usb_conf;
> +       __le32 usb_sts;
> +       __le32 usb_cmd;
> +       __le32 usb_iptn;
> +       __le32 usb_lpm;
> +       __le32 usb_ien;
> +       __le32 usb_ists;
> +       __le32 ep_sel;
> +       __le32 ep_traddr;
> +       __le32 ep_cfg;
> +       __le32 ep_cmd;
> +       __le32 ep_sts;
> +       __le32 ep_sts_sid;
> +       __le32 ep_sts_en;
> +       __le32 drbl;
> +       __le32 ep_ien;
> +       __le32 ep_ists;
> +       __le32 usb_pwr;
> +       __le32 usb_conf2;
> +       __le32 usb_cap1;
> +       __le32 usb_cap2;
> +       __le32 usb_cap3;
> +       __le32 usb_cap4;
> +       __le32 usb_cap5;
> +       __le32 usb_cap6;
> +       __le32 usb_cpkt1;
> +       __le32 usb_cpkt2;
> +       __le32 usb_cpkt3;
> +       __le32 reserved1[36];
> +       __le32 cfg_reg1;
> +       __le32 dbg_link1;
> +       __le32 dbg_link2;
> +       __le32 cfg_regs[74];
> +       __le32 reserved2[34];
> +       __le32 dma_axi_ctrl;
> +       __le32 dma_axi_id;
> +       __le32 dma_axi_cap;
> +       __le32 dma_axi_ctrl0;
> +       __le32 dma_axi_ctrl1;
> +};
> +
> +/* USB_CONF - bitmasks */
> +/* Reset USB device configuration. */
> +#define USB_CONF_CFGRST                BIT(0)
> +/* Set Configuration. */
> +#define USB_CONF_CFGSET                BIT(1)
> +/* Disconnect USB device in SuperSpeed. */
> +#define USB_CONF_USB3DIS       BIT(3)
> +/* Disconnect USB device in HS/FS */
> +#define USB_CONF_USB2DIS       BIT(4)
> +/* Little Endian access - default */
> +#define USB_CONF_LENDIAN       BIT(5)
> +/*
> + * Big Endian access. Driver assume that byte order for
> + * SFRs access always is as Little Endian so this bit
> + * is not used.
> + */
> +#define USB_CONF_BENDIAN       BIT(6)
> +/* Device software reset. */
> +#define USB_CONF_SWRST         BIT(7)
> +/* Singular DMA transfer mode. */
> +#define USB_CONF_DSING         BIT(8)
> +/* Multiple DMA transfers mode. */
> +#define USB_CONF_DMULT         BIT(9)
> +/* DMA clock turn-off enable. */
> +#define USB_CONF_DMAOFFEN      BIT(10)
> +/* DMA clock turn-off disable. */
> +#define USB_CONF_DMAOFFDS      BIT(11)
> +/* Clear Force Full Speed. */
> +#define USB_CONF_CFORCE_FS     BIT(12)
> +/* Set Force Full Speed. */
> +#define USB_CONF_SFORCE_FS     BIT(13)
> +/* Device enable. */
> +#define USB_CONF_DEVEN         BIT(14)
> +/* Device disable. */
> +#define USB_CONF_DEVDS         BIT(15)
> +/* L1 LPM state entry enable (used in HS/FS mode). */
> +#define USB_CONF_L1EN          BIT(16)
> +/* L1 LPM state entry disable (used in HS/FS mode). */
> +#define USB_CONF_L1DS          BIT(17)
> +/* USB 2.0 clock gate disable. */
> +#define USB_CONF_CLK2OFFEN     BIT(18)
> +/* USB 2.0 clock gate enable. */
> +#define USB_CONF_CLK2OFFDS     BIT(19)
> +/* L0 LPM state entry request (used in HS/FS mode). */
> +#define USB_CONF_LGO_L0                BIT(20)
> +/* USB 3.0 clock gate disable. */
> +#define USB_CONF_CLK3OFFEN     BIT(21)
> +/* USB 3.0 clock gate enable. */
> +#define USB_CONF_CLK3OFFDS     BIT(22)
> +/* Bit 23 is reserved*/
> +/* U1 state entry enable (used in SS mode). */
> +#define USB_CONF_U1EN          BIT(24)
> +/* U1 state entry disable (used in SS mode). */
> +#define USB_CONF_U1DS          BIT(25)
> +/* U2 state entry enable (used in SS mode). */
> +#define USB_CONF_U2EN          BIT(26)
> +/* U2 state entry disable (used in SS mode). */
> +#define USB_CONF_U2DS          BIT(27)
> +/* U0 state entry request (used in SS mode). */
> +#define USB_CONF_LGO_U0                BIT(28)
> +/* U1 state entry request (used in SS mode). */
> +#define USB_CONF_LGO_U1                BIT(29)
> +/* U2 state entry request (used in SS mode). */
> +#define USB_CONF_LGO_U2                BIT(30)
> +/* SS.Inactive state entry request (used in SS mode) */
> +#define USB_CONF_LGO_SSINACT   BIT(31)
> +
> +/* USB_STS - bitmasks */
> +/*
> + * Configuration status.
> + * 1 - device is in the configured state.
> + * 0 - device is not configured.
> + */
> +#define USB_STS_CFGSTS_MASK    BIT(0)
> +#define USB_STS_CFGSTS(p)      ((p) & USB_STS_CFGSTS_MASK)
> +/*
> + * On-chip memory overflow.
> + * 0 - On-chip memory status OK.
> + * 1 - On-chip memory overflow.
> + */
> +#define USB_STS_OV_MASK                BIT(1)
> +#define USB_STS_OV(p)          ((p) & USB_STS_OV_MASK)
> +/*
> + * SuperSpeed connection status.
> + * 0 - USB in SuperSpeed mode disconnected.
> + * 1 - USB in SuperSpeed mode connected.
> + */
> +#define USB_STS_USB3CONS_MASK  BIT(2)
> +#define USB_STS_USB3CONS(p)    ((p) & USB_STS_USB3CONS_MASK)
> +/*
> + * DMA transfer configuration status.
> + * 0 - single request.
> + * 1 - multiple TRB chain
> + */
> +#define USB_STS_DTRANS_MASK    BIT(3)
> +#define USB_STS_DTRANS(p)      ((p) & USB_STS_DTRANS_MASK)
> +/*
> + * Device speed.
> + * 0 - Undefined (value after reset).
> + * 1 - Low speed
> + * 2 - Full speed
> + * 3 - High speed
> + * 4 - Super speed
> + */
> +#define USB_STS_USBSPEED_MASK  GENMASK(6, 4)
> +#define USB_STS_USBSPEED(p)    (((p) & USB_STS_USBSPEED_MASK) >> 4)
> +#define USB_STS_LS             (0x1 << 4)
> +#define USB_STS_FS             (0x2 << 4)
> +#define USB_STS_HS             (0x3 << 4)
> +#define USB_STS_SS             (0x4 << 4)
> +#define DEV_UNDEFSPEED(p)      (((p) & USB_STS_USBSPEED_MASK) == (0x0 << 4))
> +#define DEV_LOWSPEED(p)                (((p) & USB_STS_USBSPEED_MASK) == USB_STS_LS)
> +#define DEV_FULLSPEED(p)       (((p) & USB_STS_USBSPEED_MASK) == USB_STS_FS)
> +#define DEV_HIGHSPEED(p)       (((p) & USB_STS_USBSPEED_MASK) == USB_STS_HS)
> +#define DEV_SUPERSPEED(p)      (((p) & USB_STS_USBSPEED_MASK) == USB_STS_SS)
> +/*
> + * Endianness for SFR access.
> + * 0 - Little Endian order (default after hardware reset).
> + * 1 - Big Endian order
> + */
> +#define USB_STS_ENDIAN_MASK    BIT(7)
> +#define USB_STS_ENDIAN(p)      ((p) & USB_STS_ENDIAN_MASK)
> +/*
> + * HS/FS clock turn-off status.
> + * 0 - hsfs clock is always on.
> + * 1 - hsfs clock turn-off in L2 (HS/FS mode) is enabled
> + *          (default after hardware reset).
> + */
> +#define USB_STS_CLK2OFF_MASK   BIT(8)
> +#define USB_STS_CLK2OFF(p)     ((p) & USB_STS_CLK2OFF_MASK)
> +/*
> + * PCLK clock turn-off status.
> + * 0 - pclk clock is always on.
> + * 1 - pclk clock turn-off in U3 (SS mode) is enabled
> + *          (default after hardware reset).
> + */
> +#define USB_STS_CLK3OFF_MASK   BIT(9)
> +#define USB_STS_CLK3OFF(p)     ((p) & USB_STS_CLK3OFF_MASK)
> +/*
> + * Controller in reset state.
> + * 0 - Internal reset is active.
> + * 1 - Internal reset is not active and controller is fully operational.
> + */
> +#define USB_STS_IN_RST_MASK    BIT(10)
> +#define USB_STS_IN_RST(p)      ((p) & USB_STS_IN_RST_MASK)
> +/*
> + * Device enable Status.
> + * 0 - USB device is disabled (VBUS input is disconnected from internal logic).
> + * 1 - USB device is enabled (VBUS input is connected to the internal logic).
> + */
> +#define USB_STS_DEVS_MASK      BIT(14)
> +#define USB_STS_DEVS(p)                ((p) & USB_STS_DEVS_MASK)
> +/*
> + * DAddress statuss.
> + * 0 - USB device is default state.
> + * 1 - USB device is at least in address state.
> + */
> +#define USB_STS_ADDRESSED_MASK BIT(15)
> +#define USB_STS_ADDRESSED(p)   ((p) & USB_STS_ADDRESSED_MASK)
> +/*
> + * L1 LPM state enable status (used in HS/FS mode).
> + * 0 - Entering to L1 LPM state disabled.
> + * 1 - Entering to L1 LPM state enabled.
> + */
> +#define USB_STS_L1ENS_MASK     BIT(16)
> +#define USB_STS_L1ENS(p)       ((p) & USB_STS_L1ENS_MASK)
> +/*
> + * Internal VBUS connection status (used both in HS/FS  and SS mode).
> + * 0 - internal VBUS is not detected.
> + * 1 - internal VBUS is detected.
> + */
> +#define USB_STS_VBUSS_MASK     BIT(17)
> +#define USB_STS_VBUSS(p)       ((p) & USB_STS_VBUSS_MASK)
> +/*
> + * HS/FS LPM  state (used in FS/HS mode).
> + * 0 - L0 State
> + * 1 - L1 State
> + * 2 - L2 State
> + * 3 - L3 State
> + */
> +#define USB_STS_LPMST_MASK     GENMASK(19, 18)
> +#define DEV_L0_STATE(p)                (((p) & USB_STS_LPMST_MASK) == (0x0 << 18))
> +#define DEV_L1_STATE(p)                (((p) & USB_STS_LPMST_MASK) == (0x1 << 18))
> +#define DEV_L2_STATE(p)                (((p) & USB_STS_LPMST_MASK) == (0x2 << 18))
> +#define DEV_L3_STATE(p)                (((p) & USB_STS_LPMST_MASK) == (0x3 << 18))
> +/*
> + * Disable HS status (used in FS/HS mode).
> + * 0 - the disconnect bit for HS/FS mode is set .
> + * 1 - the disconnect bit for HS/FS mode is not set.
> + */
> +#define USB_STS_USB2CONS_MASK  BIT(20)
> +#define USB_STS_USB2CONS(p)    ((p) & USB_STS_USB2CONS_MASK)
> +/*
> + * HS/FS mode connection status (used in FS/HS mode).
> + * 0 - High Speed operations in USB2.0 (FS/HS) mode not disabled.
> + * 1 - High Speed operations in USB2.0 (FS/HS).
> + */
> +#define USB_STS_DISABLE_HS_MASK        BIT(21)
> +#define USB_STS_DISABLE_HS(p)  ((p) & USB_STS_DISABLE_HS_MASK)
> +/*
> + * U1 state enable status (used in SS mode).
> + * 0 - Entering to  U1 state disabled.
> + * 1 - Entering to  U1 state enabled.
> + */
> +#define USB_STS_U1ENS_MASK     BIT(24)
> +#define USB_STS_U1ENS(p)       ((p) & USB_STS_U1ENS_MASK)
> +/*
> + * U2 state enable status (used in SS mode).
> + * 0 - Entering to  U2 state disabled.
> + * 1 - Entering to  U2 state enabled.
> + */
> +#define USB_STS_U2ENS_MASK     BIT(25)
> +#define USB_STS_U2ENS(p)       ((p) & USB_STS_U2ENS_MASK)
> +/*
> + * SuperSpeed Link LTSSM state. This field reflects USBSS-DEV current
> + * SuperSpeed link state
> + */
> +#define USB_STS_LST_MASK       GENMASK(29, 26)
> +#define DEV_LST_U0             (((p) & USB_STS_LST_MASK) == (0x0 << 26))
> +#define DEV_LST_U1             (((p) & USB_STS_LST_MASK) == (0x1 << 26))
> +#define DEV_LST_U2             (((p) & USB_STS_LST_MASK) == (0x2 << 26))
> +#define DEV_LST_U3             (((p) & USB_STS_LST_MASK) == (0x3 << 26))
> +#define DEV_LST_DISABLED       (((p) & USB_STS_LST_MASK) == (0x4 << 26))
> +#define DEV_LST_RXDETECT       (((p) & USB_STS_LST_MASK) == (0x5 << 26))
> +#define DEV_LST_INACTIVE       (((p) & USB_STS_LST_MASK) == (0x6 << 26))
> +#define DEV_LST_POLLING                (((p) & USB_STS_LST_MASK) == (0x7 << 26))
> +#define DEV_LST_RECOVERY       (((p) & USB_STS_LST_MASK) == (0x8 << 26))
> +#define DEV_LST_HOT_RESET      (((p) & USB_STS_LST_MASK) == (0x9 << 26))
> +#define DEV_LST_COMP_MODE      (((p) & USB_STS_LST_MASK) == (0xa << 26))
> +#define DEV_LST_LB_STATE       (((p) & USB_STS_LST_MASK) == (0xb << 26))
> +/*
> + * DMA clock turn-off status.
> + * 0 - DMA clock is always on (default after hardware reset).
> + * 1 - DMA clock turn-off in U1, U2 and U3 (SS mode) is enabled.
> + */
> +#define USB_STS_DMAOFF_MASK    BIT(30)
> +#define USB_STS_DMAOFF(p)      ((p) & USB_STS_DMAOFF_MASK)
> +/*
> + * SFR Endian statuss.
> + * 0 - Little Endian order (default after hardware reset).
> + * 1 - Big Endian order.
> + */
> +#define USB_STS_ENDIAN2_MASK   BIT(31)
> +#define USB_STS_ENDIAN2(p)     ((p) & USB_STS_ENDIAN2_MASK)
> +
> +/* USB_CMD -  bitmasks */
> +/* Set Function Address */
> +#define USB_CMD_SET_ADDR       BIT(0)
> +/*
> + * Function Address This field is saved to the device only when the field
> + * SET_ADDR is set '1 ' during write to USB_CMD register.
> + * Software is responsible for entering the address of the device during
> + * SET_ADDRESS request service. This field should be set immediately after
> + * the SETUP packet is decoded, and prior to confirmation of the status phase
> + */
> +#define USB_CMD_FADDR_MASK     GENMASK(7, 1)
> +#define USB_CMD_FADDR(p)       (((p) << 1) & USB_CMD_FADDR_MASK)
> +/* Send Function Wake Device Notification TP (used only in SS mode). */
> +#define USB_CMD_SDNFW          BIT(8)
> +/* Set Test Mode (used only in HS/FS mode). */
> +#define USB_CMD_STMODE         BIT(9)
> +/* Test mode selector (used only in HS/FS mode) */
> +#define USB_STS_TMODE_SEL_MASK GENMASK(11, 10)
> +#define USB_STS_TMODE_SEL(p)   (((p) << 10) & USB_STS_TMODE_SEL_MASK)
> +/*
> + *  Send Latency Tolerance Message Device Notification TP (used only
> + *  in SS mode).
> + */
> +#define USB_CMD_SDNLTM         BIT(12)
> +/* Send Custom Transaction Packet (used only in SS mode) */
> +#define USB_CMD_SPKT           BIT(13)
> +/*Device Notification 'Function Wake' - Interface value (only in SS mode. */
> +#define USB_CMD_DNFW_INT_MASK  GENMASK(23, 16)
> +#define USB_STS_DNFW_INT(p)    (((p) << 16) & USB_CMD_DNFW_INT_MASK)
> +/*
> + * Device Notification 'Latency Tolerance Message' -373 BELT value [7:0]
> + * (used only in SS mode).
> + */
> +#define USB_CMD_DNLTM_BELT_MASK        GENMASK(27, 16)
> +#define USB_STS_DNLTM_BELT(p)  (((p) << 16) & USB_CMD_DNLTM_BELT_MASK)
> +
> +/* USB_ITPN - bitmasks */
> +/*
> + * ITP(SS) / SOF (HS/FS) number
> + * In SS mode this field represent number of last ITP received from host.
> + * In HS/FS mode this field represent number of last SOF received from host.
> + */
> +#define USB_ITPN_MASK          GENMASK(13, 0)
> +#define USB_ITPN(p)            ((p) & USB_ITPN_MASK)
> +
> +/* USB_LPM - bitmasks */
> +/* Host Initiated Resume Duration. */
> +#define USB_LPM_HIRD_MASK      GENMASK(3, 0)
> +#define USB_LPM_HIRD(p)                ((p) & USB_LPM_HIRD_MASK)
> +/* Remote Wakeup Enable (bRemoteWake). */
> +#define USB_LPM_BRW            BIT(4)
> +
> +/* USB_IEN - bitmasks */
> +/* SS connection interrupt enable */
> +#define USB_IEN_CONIEN         BIT(0)
> +/* SS disconnection interrupt enable. */
> +#define USB_IEN_DISIEN         BIT(1)
> +/* USB SS warm reset interrupt enable. */
> +#define USB_IEN_UWRESIEN       BIT(2)
> +/* USB SS hot reset interrupt enable */
> +#define USB_IEN_UHRESIEN       BIT(3)
> +/* SS link U3 state enter interrupt enable (suspend).*/
> +#define USB_IEN_U3ENTIEN       BIT(4)
> +/* SS link U3 state exit interrupt enable (wakeup). */
> +#define USB_IEN_U3EXTIEN       BIT(5)
> +/* SS link U2 state enter interrupt enable.*/
> +#define USB_IEN_U2ENTIEN       BIT(6)
> +/* SS link U2 state exit interrupt enable.*/
> +#define USB_IEN_U2EXTIEN       BIT(7)
> +/* SS link U1 state enter interrupt enable.*/
> +#define USB_IEN_U1ENTIEN       BIT(8)
> +/* SS link U1 state exit interrupt enable.*/
> +#define USB_IEN_U1EXTIEN       BIT(9)
> +/* ITP/SOF packet detected interrupt enable.*/
> +#define USB_IEN_ITPIEN         BIT(10)
> +/* Wakeup interrupt enable.*/
> +#define USB_IEN_WAKEIEN                BIT(11)
> +/* Send Custom Packet interrupt enable.*/
> +#define USB_IEN_SPKTIEN                BIT(12)
> +/* HS/FS mode connection interrupt enable.*/
> +#define USB_IEN_CON2IEN                BIT(16)
> +/* HS/FS mode disconnection interrupt enable.*/
> +#define USB_IEN_DIS2IEN                BIT(17)
> +/* USB reset (HS/FS mode) interrupt enable.*/
> +#define USB_IEN_U2RESIEN       BIT(18)
> +/* LPM L2 state enter interrupt enable.*/
> +#define USB_IEN_L2ENTIEN       BIT(20)
> +/* LPM  L2 state exit interrupt enable.*/
> +#define USB_IEN_L2EXTIEN       BIT(21)
> +/* LPM L1 state enter interrupt enable.*/
> +#define USB_IEN_L1ENTIEN       BIT(24)
> +/* LPM  L1 state exit interrupt enable.*/
> +#define USB_IEN_L1EXTIEN       BIT(25)
> +/* Configuration reset interrupt enable.*/
> +#define USB_IEN_CFGRESIEN      BIT(26)
> +/* Start of the USB SS warm reset interrupt enable.*/
> +#define USB_IEN_UWRESSIEN      BIT(28)
> +/* End of the USB SS warm reset interrupt enable.*/
> +#define USB_IEN_UWRESEIEN      BIT(29)
> +
> +#define USB_IEN_INIT  (USB_IEN_U2RESIEN | USB_ISTS_DIS2I | USB_IEN_CON2IEN \
> +                      | USB_IEN_UHRESIEN | USB_IEN_UWRESIEN | USB_IEN_DISIEN \
> +                      | USB_IEN_CONIEN | USB_IEN_U3EXTIEN | USB_IEN_L2ENTIEN \
> +                      | USB_IEN_L2EXTIEN)
> +
> +/* USB_ISTS - bitmasks */
> +/* SS Connection detected. */
> +#define USB_ISTS_CONI          BIT(0)
> +/* SS Disconnection detected. */
> +#define USB_ISTS_DISI          BIT(1)
> +/* UUSB warm reset detectede. */
> +#define USB_ISTS_UWRESI                BIT(2)
> +/* USB hot reset detected. */
> +#define USB_ISTS_UHRESI                BIT(3)
> +/* U3 link state enter detected (suspend).*/
> +#define USB_ISTS_U3ENTI                BIT(4)
> +/* U3 link state exit detected (wakeup). */
> +#define USB_ISTS_U3EXTI                BIT(5)
> +/* U2 link state enter detected.*/
> +#define USB_ISTS_U2ENTI                BIT(6)
> +/* U2 link state exit detected.*/
> +#define USB_ISTS_U2EXTI                BIT(7)
> +/* U1 link state enter detected.*/
> +#define USB_ISTS_U1ENTI                BIT(8)
> +/* U1 link state exit detected.*/
> +#define USB_ISTS_U1EXTI                BIT(9)
> +/* ITP/SOF packet detected.*/
> +#define USB_ISTS_ITPI          BIT(10)
> +/* Wakeup detected.*/
> +#define USB_ISTS_WAKEI         BIT(11)
> +/* Send Custom Packet detected.*/
> +#define USB_ISTS_SPKTI         BIT(12)
> +/* HS/FS mode connection detected.*/
> +#define USB_ISTS_CON2I         BIT(16)
> +/* HS/FS mode disconnection detected.*/
> +#define USB_ISTS_DIS2I         BIT(17)
> +/* USB reset (HS/FS mode) detected.*/
> +#define USB_ISTS_U2RESI                BIT(18)
> +/* LPM L2 state enter detected.*/
> +#define USB_ISTS_L2ENTI                BIT(20)
> +/* LPM  L2 state exit detected.*/
> +#define USB_ISTS_L2EXTI                BIT(21)
> +/* LPM L1 state enter detected.*/
> +#define USB_ISTS_L1ENTI                BIT(24)
> +/* LPM L1 state exit detected.*/
> +#define USB_ISTS_L1EXTI                BIT(25)
> +/* USB configuration reset detected.*/
> +#define USB_ISTS_CFGRESI       BIT(26)
> +/* Start of the USB warm reset detected.*/
> +#define USB_ISTS_UWRESSI       BIT(28)
> +/* End of the USB warm reset detected.*/
> +#define USB_ISTS_UWRESEI       BIT(29)
> +
> +/* USB_SEL - bitmasks */
> +#define EP_SEL_EPNO_MASK       GENMASK(3, 0)
> +/* Endpoint number. */
> +#define EP_SEL_EPNO(p)         ((p) & EP_SEL_EPNO_MASK)
> +/* Endpoint direction bit - 0 - OUT, 1 - IN. */
> +#define EP_SEL_DIR             BIT(7)
> +
> +#define select_ep_in(nr)       (EP_SEL_EPNO(p) | EP_SEL_DIR)
> +#define select_ep_out          (EP_SEL_EPNO(p))
> +
> +/* EP_TRADDR - bitmasks */
> +/* Transfer Ring address. */
> +#define EP_TRADDR_TRADDR(p)    ((p))
> +
> +/* EP_CFG - bitmasks */
> +/* Endpoint enable */
> +#define EP_CFG_ENABLE          BIT(0)
> +/*
> + *  Endpoint type.
> + * 1 - isochronous
> + * 2 - bulk
> + * 3 - interrupt
> + */
> +#define EP_CFG_EPTYPE_MASK     GENMASK(2, 1)
> +#define EP_CFG_EPTYPE(p)       (((p) << 1)  & EP_CFG_EPTYPE_MASK)
> +/* Stream support enable (only in SS mode). */
> +#define EP_CFG_STREAM_EN       BIT(3)
> +/* TDL check (only in SS mode for BULK EP). */
> +#define EP_CFG_TDL_CHK         BIT(4)
> +/* SID check (only in SS mode for BULK OUT EP). */
> +#define EP_CFG_SID_CHK         BIT(5)
> +/* DMA transfer endianness. */
> +#define EP_CFG_EPENDIAN                BIT(7)
> +/* Max burst size (used only in SS mode). */
> +#define EP_CFG_MAXBURST_MASK   GENMASK(11, 8)
> +#define EP_CFG_MAXBURST(p)     (((p) << 8) & EP_CFG_MAXBURST_MASK)
> +/* ISO max burst. */
> +#define EP_CFG_MULT_MASK       GENMASK(15, 14)
> +#define EP_CFG_MULT(p)         (((p) << 14) & EP_CFG_MULT)
> +/* ISO max burst. */
> +#define EP_CFG_MAXPKTSIZE_MASK GENMASK(26, 16)
> +#define EP_CFG_MAXPKTSIZE(p)   (((p) << 16) & EP_CFG_MAXPKTSIZE_MASK)
> +/* Max number of buffered packets. */
> +#define EP_CFG_BUFFERING_MASK  GENMASK(31, 27)
> +#define EP_CFG_BUFFERING(p)    (((p) << 27) & EP_CFG_BUFFERING_MASK)
> +
> +/* EP_CMD - bitmasks */
> +/* Endpoint reset. */
> +#define EP_CMD_EPRST           BIT(0)
> +/* Endpoint STALL set. */
> +#define EP_CMD_SSTALL          BIT(1)
> +/* Endpoint STALL clear. */
> +#define EP_CMD_CSTALL          BIT(2)
> +/* Send ERDY TP. */
> +#define EP_CMD_ERDY            BIT(3)
> +/* Request complete. */
> +#define EP_CMD_REQ_CMPL                BIT(5)
> +/* Transfer descriptor ready. */
> +#define EP_CMD_DRDY            BIT(6)
> +/* Data flush. */
> +#define EP_CMD_DFLUSH          BIT(7)
> +/*
> + * Transfer Descriptor Length write  (used only for Bulk Stream capable
> + * endpoints in SS mode).
> + */
> +#define EP_CMD_STDL            BIT(8)
> +/* Transfer Descriptor Length (used only in SS mode for bulk endpoints). */
> +#define EP_CMD_TDL_MASK                GENMASK(15, 9)
> +#define EP_CMD_TDL(p)          (((p) << 9) & EP_CMD_TDL_MASK)
> +/* ERDY Stream ID value (used in SS mode). */
> +#define EP_CMD_ERDY_SID_MASK   GENMASK(31, 16)
> +#define EP_CMD_ERDY_SID(p)     (((p) << 16) & EP_CMD_SID_MASK)
> +
> +/* EP_STS - bitmasks */
> +/* Setup transfer complete. */
> +#define EP_STS_SETUP           BIT(0)
> +/* Endpoint STALL status. */
> +#define EP_STS_STALL(p)                ((p) & BIT(1))
> +/* Interrupt On Complete. */
> +#define EP_STS_IOC             BIT(2)
> +/* Interrupt on Short Packet. */
> +#define EP_STS_ISP             BIT(3)
> +/* Transfer descriptor missing. */
> +#define EP_STS_DESCMIS         BIT(4)
> +/* Stream Rejected (used only in SS mode) */
> +#define EP_STS_STREAMR         BIT(5)
> +/* EXIT from MOVE DATA State (used only for stream transfers in SS mode). */
> +#define EP_STS_MD_EXIT         BIT(6)
> +/* TRB error. */
> +#define EP_STS_TRBERR          BIT(7)
> +/* Not ready (used only in SS mode). */
> +#define EP_STS_NRDY            BIT(8)
> +/* DMA busy. */
> +#define EP_STS_DBUSY(p)                ((p) & BIT(9))
> +/* Endpoint Buffer Empty */
> +#define EP_STS_BUFFEMPTY(p)    ((p) & BIT(10))
> +/* Current Cycle Status */
> +#define EP_STS_CCS(p)          ((p) & BIT(11))
> +/* Prime (used only in SS mode. */
> +#define EP_STS_PRIME           BIT(12)
> +/* Stream error (used only in SS mode). */
> +#define EP_STS_SIDERR          BIT(13)
> +/* OUT size mismatch. */
> +#define EP_STS_OUTSMM          BIT(14)
> +/* ISO transmission error. */
> +#define EP_STS_ISOERR          BIT(15)
> +/* Host Packet Pending (only for SS mode). */
> +#define EP_STS_HOSTPP(p)       ((p) & BIT(16))
> +/* Stream Protocol State Machine State (only for Bulk stream endpoints). */
> +#define EP_STS_SPSMST_MASK             GENMASK(18, 17)
> +#define EP_STS_SPSMST_DISABLED(p)      (((p) & EP_STS_SPSMST_MASK) >> 17)
> +#define EP_STS_SPSMST_IDLE(p)          (((p) & EP_STS_SPSMST_MASK) >> 17)
> +#define EP_STS_SPSMST_START_STREAM(p)  (((p) & EP_STS_SPSMST_MASK) >> 17)
> +#define EP_STS_SPSMST_MOVE_DATA(p)     (((p) & EP_STS_SPSMST_MASK) >> 17)
> +/* Interrupt On Transfer complete. */
> +#define EP_STS_IOT             BIT(19)
> +/* OUT queue endpoint number. */
> +#define EP_STS_OUTQ_NO_MASK    GENMASK(27, 24)
> +#define EP_STS_OUTQ_NO(p)      (((p) & EP_STS_OUTQ_NO_MASK) >> 24)
> +/* OUT queue valid flag. */
> +#define EP_STS_OUTQ_VAL_MASK   BIT(28)
> +#define EP_STS_OUTQ_VAL(p)     ((p) & EP_STS_OUTQ_VAL_MASK)
> +/* SETUP WAIT. */
> +#define EP_STS_STPWAIT         BIT(31)
> +
> +/* EP_STS_SID - bitmasks */
> +/* Stream ID (used only in SS mode). */
> +#define EP_STS_SID_MASK                GENMASK(15, 0)
> +#define EP_STS_SID(p)          ((p) & EP_STS_SID_MASK)
> +
> +/* EP_STS_EN - bitmasks */
> +/* SETUP interrupt enable. */
> +#define EP_STS_EN_SETUPEN      BIT(0)
> +/* OUT transfer missing descriptor enable. */
> +#define EP_STS_EN_DESCMISEN    BIT(4)
> +/* Stream Rejected enable. */
> +#define EP_STS_EN_STREAMREN    BIT(5)
> +/* Move Data Exit enable.*/
> +#define EP_STS_EN_MD_EXITEN    BIT(6)
> +/* TRB enable. */
> +#define EP_STS_EN_TRBERREN     BIT(7)
> +/* NRDY enable. */
> +#define EP_STS_EN_NRDYEN       BIT(8)
> +/* Prime enable. */
> +#define EP_STS_EN_PRIMEEEN     BIT(12)
> +/* Stream error enable. */
> +#define EP_STS_EN_SIDERREN     BIT(13)
> +/* OUT size mismatch enable. */
> +#define EP_STS_EN_OUTSMMEN     BIT(14)
> +/* ISO transmission error enable. */
> +#define EP_STS_EN_ISOERREN     BIT(15)
> +/* Interrupt on Transmission complete enable. */
> +#define EP_STS_EN_IOTEN                BIT(19)
> +/* Setup Wait interrupt enable. */
> +#define EP_STS_EN_STPWAITEN    BIT(31)
> +
> +/* DRBL- bitmasks */
> +#define DB_VALUE_BY_INDEX(index) (1 << (index))
> +#define DB_VALUE_EP0_OUT       BIT(0)
> +#define DB_VALUE_EP0_IN                BIT(16)
> +
> +/* EP_IEN - bitmasks */
> +#define EP_IEN(index)          (1 << (index))
> +#define EP_IEN_EP_OUT0         BIT(0)
> +#define EP_IEN_EP_IN0          BIT(16)
> +
> +/* EP_ISTS - bitmasks */
> +#define EP_ISTS(index)         (1 << (index))
> +#define EP_ISTS_EP_OUT0                BIT(0)
> +#define EP_ISTS_EP_IN0         BIT(16)
> +
> +/* EP_PWR- bitmasks */
> +/*Power Shut Off capability enable*/
> +#define PUSB_PWR_PSO_EN                BIT(0)
> +/*Power Shut Off capability disable*/
> +#define PUSB_PWR_PSO_DS                BIT(1)
> +/*
> + * Enables turning-off Reference Clock.
> + * This bit is optional and implemented only when support for OTG is
> + * implemented (indicated by OTG_READY bit set to '1').
> + */
> +#define PUSB_PWR_STB_CLK_SWITCH_EN     BIT(8)
> +/*
> + * Status bit indicating that operation required by STB_CLK_SWITCH_EN write
> + * is completed
> + */
> +#define PUSB_PWR_STB_CLK_SWITCH_DONE   BIT(9)
> +/* This bit informs if Fast Registers Access is enabled. */
> +#define PUSB_PWR_FST_REG_ACCESS_STAT   BIT(30)
> +/* Fast Registers Access Enable. */
> +#define PUSB_PWR_FST_REG_ACCESS        BIT(31)
> +
> +/* USB_CAP1- bitmasks */
> +/*
> + * SFR Interface type
> + * These field reflects type of SFR interface implemented:
> + * 0x0 - OCP
> + * 0x1 - AHB,
> + * 0x2 - PLB
> + * 0x3 - AXI
> + * 0x4-0xF - reserved
> + */
> +#define USB_CAP1_SFR_TYPE_MASK GENMASK(3, 0)
> +#define DEV_SFR_TYPE_OCP(p)    (((p) & USB_CAP1_SFR_TYPE_MASK) == 0x0)
> +#define DEV_SFR_TYPE_AHB(p)    (((p) & USB_CAP1_SFR_TYPE_MASK) == 0x1)
> +#define DEV_SFR_TYPE_PLB(p)    (((p) & USB_CAP1_SFR_TYPE_MASK) == 0x2)
> +#define DEV_SFR_TYPE_AXI(p)    (((p) & USB_CAP1_SFR_TYPE_MASK) == 0x3)
> +/*
> + * SFR Interface width
> + * These field reflects width of SFR interface implemented:
> + * 0x0 - 8 bit interface,
> + * 0x1 - 16 bit interface,
> + * 0x2 - 32 bit interface
> + * 0x3 - 64 bit interface
> + * 0x4-0xF - reserved
> + */
> +#define USB_CAP1_SFR_WIDTH_MASK        GENMASK(7, 4)
> +#define DEV_SFR_WIDTH_8(p)     (((p) & USB_CAP1_SFR_WIDTH_MASK) == (0x0 << 4))
> +#define DEV_SFR_WIDTH_16(p)    (((p) & USB_CAP1_SFR_WIDTH_MASK) == (0x1 << 4))
> +#define DEV_SFR_WIDTH_32(p)    (((p) & USB_CAP1_SFR_WIDTH_MASK) == (0x2 << 4))
> +#define DEV_SFR_WIDTH_64(p)    (((p) & USB_CAP1_SFR_WIDTH_MASK) == (0x3 << 4))
> +/*
> + * DMA Interface type
> + * These field reflects type of DMA interface implemented:
> + * 0x0 - OCP
> + * 0x1 - AHB,
> + * 0x2 - PLB
> + * 0x3 - AXI
> + * 0x4-0xF - reserved
> + */
> +#define USB_CAP1_DMA_TYPE_MASK GENMASK(11, 8)
> +#define DEV_DMA_TYPE_OCP(p)    (((p) & USB_CAP1_DMA_TYPE_MASK) == (0x0 << 8))
> +#define DEV_DMA_TYPE_AHB(p)    (((p) & USB_CAP1_DMA_TYPE_MASK) == (0x1 << 8))
> +#define DEV_DMA_TYPE_PLB(p)    (((p) & USB_CAP1_DMA_TYPE_MASK) == (0x2 << 8))
> +#define DEV_DMA_TYPE_AXI(p)    (((p) & USB_CAP1_DMA_TYPE_MASK) == (0x3 << 8))
> +/*
> + * DMA Interface width
> + * These field reflects width of DMA interface implemented:
> + * 0x0 - reserved,
> + * 0x1 - reserved,
> + * 0x2 - 32 bit interface
> + * 0x3 - 64 bit interface
> + * 0x4-0xF - reserved
> + */
> +#define USB_CAP1_DMA_WIDTH_MASK        GENMASK(15, 12)
> +#define DEV_DMA_WIDTH_32(p)    (((p) & USB_CAP1_DMA_WIDTH_MASK) == (0x2 << 12))
> +#define DEV_DMA_WIDTH_64(p)    (((p) & USB_CAP1_DMA_WIDTH_MASK) == (0x3 << 12))
> +/*
> + * USB3 PHY Interface type
> + * These field reflects type of USB3 PHY interface implemented:
> + * 0x0 - USB PIPE,
> + * 0x1 - RMMI,
> + * 0x2-0xF - reserved
> + */
> +#define USB_CAP1_U3PHY_TYPE_MASK GENMASK(19, 16)
> +#define DEV_U3PHY_PIPE(p) (((p) & USB_CAP1_U3PHY_TYPE_MASK) == (0x0 << 16))
> +#define DEV_U3PHY_RMMI(p) (((p) & USB_CAP1_U3PHY_TYPE_MASK) == (0x1 << 16))
> +/*
> + * USB3 PHY Interface width
> + * These field reflects width of USB3 PHY interface implemented:
> + * 0x0 - 8 bit PIPE interface,
> + * 0x1 - 16 bit PIPE interface,
> + * 0x2 - 32 bit PIPE interface,
> + * 0x3 - 64 bit PIPE interface
> + * 0x4-0xF - reserved
> + * Note: When SSIC interface is implemented this field shows the width of
> + * internal PIPE interface. The RMMI interface is always 20bit wide.
> + */
> +#define USB_CAP1_U3PHY_WIDTH_MASK GENMASK(23, 20)
> +#define DEV_U3PHY_WIDTH_8(p) \
> +       (((p) & USB_CAP1_U3PHY_WIDTH_MASK) == (0x0 << 20))
> +#define DEV_U3PHY_WIDTH_16(p) \
> +       (((p) & USB_CAP1_U3PHY_WIDTH_MASK) == (0x1 << 16))
> +#define DEV_U3PHY_WIDTH_32(p) \
> +       (((p) & USB_CAP1_U3PHY_WIDTH_MASK) == (0x2 << 20))
> +#define DEV_U3PHY_WIDTH_64(p) \
> +       (((p) & USB_CAP1_U3PHY_WIDTH_MASK) == (0x3 << 16))
> +
> +/*
> + * USB2 PHY Interface enable
> + * These field informs if USB2 PHY interface is implemented:
> + * 0x0 - interface NOT implemented,
> + * 0x1 - interface implemented
> + */
> +#define USB_CAP1_U2PHY_EN(p)   ((p) & BIT(24))
> +/*
> + * USB2 PHY Interface type
> + * These field reflects type of USB2 PHY interface implemented:
> + * 0x0 - UTMI,
> + * 0x1 - ULPI
> + */
> +#define DEV_U2PHY_ULPI(p)      ((p) & BIT(25))
> +/*
> + * USB2 PHY Interface width
> + * These field reflects width of USB2 PHY interface implemented:
> + * 0x0 - 8 bit interface,
> + * 0x1 - 16 bit interface,
> + * Note: The ULPI interface is always 8bit wide.
> + */
> +#define DEV_U2PHY_WIDTH_16(p)  ((p) & BIT(26))
> +/*
> + * OTG Ready
> + * 0x0 - pure device mode
> + * 0x1 - some features and ports for CDNS USB OTG controller are implemented.
> + */
> +#define USB_CAP1_OTG_READY(p)  ((p) & BIT(27))
> +
> +/* USB_CAP2- bitmasks */
> +/*
> + * The actual size of the connected On-chip RAM memory in kB:
> + * - 0 means 256 kB (max supported mem size)
> + * - value other than 0 reflects the mem size in kB
> + */
> +#define USB_CAP2_ACTUAL_MEM_SIZE(p) ((p) & GENMASK(7, 0))
> +/*
> + * Max supported mem size
> + * These field reflects width of on-chip RAM address bus width,
> + * which determines max supported mem size:
> + * 0x0-0x7 - reserved,
> + * 0x8 - support for 4kB mem,
> + * 0x9 - support for 8kB mem,
> + * 0xA - support for 16kB mem,
> + * 0xB - support for 32kB mem,
> + * 0xC - support for 64kB mem,
> + * 0xD - support for 128kB mem,
> + * 0xE - support for 256kB mem,
> + * 0xF - reserved
> + */
> +#define USB_CAP2_MAX_MEM_SIZE(p) ((p) & GENMASK(11, 8))
> +
> +/* USB_CAP3- bitmasks */
> +#define EP_IS_IMPLEMENTED(reg, index) ((reg) & (1 << (index)))
> +
> +/* USB_CAP4- bitmasks */
> +#define EP_SUPPORT_ISO(reg, index) ((reg) & (1 << (index)))
> +
> +/* USB_CAP5- bitmasks */
> +#define EP_SUPPORT_STREAM(reg, index) ((reg) & (1 << (index)))
> +
> +/* USB_CAP6- bitmasks */
> +/* The USBSS-DEV Controller  Internal build number. */
> +#define GET_DEV_VERSION__INTERNAL_NUMBER(p) ((p) & GENMASK(7, 0))
> +/* The USBSS-DEV Controller  version number. */
> +#define GET_DEV_VERSION(p) ((p) & GENMASK(31, 8))
> +
> +/* DBG_LINK1- bitmasks */
> +/*
> + * LFPS_MIN_DET_U1_EXIT value This parameter configures the minimum
> + * time required for decoding the received LFPS as an LFPS.U1_Exit.
> + */
> +#define DBG_LINK1_LFPS_MIN_DET_U1_EXIT(p)      ((p) & GENMASK(7, 0))
> +/*
> + * LFPS_MIN_GEN_U1_EXIT value This parameter configures the minimum time for
> + * phytxelecidle deassertion when LFPS.U1_Exit
> + */
> +#define DBG_LINK1_LFPS_MIN_GEN_U1_EXIT(p)      (((p) << 8) & GENMASK(15, 8))
> +/*
> + * RXDET_BREAK_DIS value This parameter configures terminating the Far-end
> + * Receiver termination detection sequence:
> + * 0: it is possible that USBSS_DEV will terminate Farend receiver
> + *    termination detection sequence
> + * 1: USBSS_DEV will not terminate Far-end receiver termination
> + *    detection sequence
> + */
> +#define DBG_LINK1_RXDET_BREAK_DIS              BIT(16)
> +/* LFPS_GEN_PING value This parameter configures the LFPS.Ping generation */
> +#define DBG_LINK1_LFPS_GEN_PING(p)             (((p) << 17) & GENMASK(21, 17))
> +/*
> + * Set the LFPS_MIN_DET_U1_EXIT value Writing '1' to this bit writes the
> + * LFPS_MIN_DET_U1_EXIT field value to the device. This bit is automatically
> + * cleared. Writing '0' has no effect
> + */
> +#define DBG_LINK1_LFPS_MIN_DET_U1_EXIT_SET     BIT(24)
> +/*
> + * Set the LFPS_MIN_GEN_U1_EXIT value. Writing '1' to this bit writes the
> + * LFPS_MIN_GEN_U1_EXIT field value to the device. This bit is automatically
> + * cleared. Writing '0' has no effect
> + */
> +#define DBG_LINK1_LFPS_MIN_GEN_U1_EXIT_SET     BIT(25)
> +/*
> + * Set the RXDET_BREAK_DIS value Writing '1' to this bit writes
> + * the RXDET_BREAK_DIS field value to the device. This bit is automatically
> + * cleared. Writing '0' has no effect
> + */
> +#define DBG_LINK1_RXDET_BREAK_DIS_SET          BIT(26)
> +/*
> + * Set the LFPS_GEN_PING_SET value Writing '1' to this bit writes
> + * the LFPS_GEN_PING field value to the device. This bit is automatically
> + * cleared. Writing '0' has no effect."
> + */
> +#define DBG_LINK1_LFPS_GEN_PING_SET            BIT(27)
> +
> +#define gadget_to_cdns3_device(g) (container_of(g, struct cdns3_device, gadget))
> +
> +#define ep_to_cdns3_ep(ep) (container_of(ep, struct cdns3_endpoint, endpoint))
> +
> +/*-------------------------------------------------------------------------*/
> +/*
> + * USBSS-DEV DMA interface.
> + */
> +#define TRBS_PER_SEGMENT       40
> +
> +#define ISO_MAX_INTERVAL       10
> +
> +/*
> + *Only for ISOC endpoints - maximum number of TRBs is calculated as
> + * pow(2, bInterval-1) * number of usb requests. It is limitation made by
> + * driver to save memory. Controller must prepare TRB for each ITP even
> + * if bInterval > 1. It's the reason why driver needs so many TRBs for
> + * isochronous endpoints.
> + */
> +#define TRBS_PER_ISOC_SEGMENT  (ISO_MAX_INTERVAL * 8)
> +
> +#define GET_TRBS_PER_SEGMENT(ep_type) ((ep_type) == USB_ENDPOINT_XFER_ISOC ? \
> +                                     TRBS_PER_ISOC_SEGMENT : TRBS_PER_SEGMENT)
> +/**
> + * struct cdns3_trb - represent Transfer Descriptor block.
> + * @buffer:    pointer to buffer data
> + * @length:    length of data
> + * @control:   control flags.
> + *
> + * This structure describes transfer block serviced by DMA module.
> + */
> +struct cdns3_trb {
> +       __le32 buffer;
> +       __le32 length;
> +       __le32 control;
> +};
> +
> +#define TRB_SIZE               (sizeof(struct cdns3_trb))
> +#define TRB_RING_SIZE          (TRB_SIZE * TRBS_PER_SEGMENT)
> +#define TRB_ISO_RING_SIZE      (TRB_SIZE * TRBS_PER_ISOC_SEGMENT)
> +#define TRB_CTRL_RING_SIZE     (TRB_SIZE * 2)
> +
> +/* TRB bit mask */
> +#define TRB_TYPE_BITMASK       GENMASK(15, 10)
> +#define TRB_TYPE(p)            ((p) << 10)
> +#define TRB_FIELD_TO_TYPE(p)   (((p) & TRB_TYPE_BITMASK) >> 10)
> +
> +/* TRB type IDs */
> +/* bulk, interrupt, isoc , and control data stage */
> +#define TRB_NORMAL             1
> +/* TRB for linking ring segments */
> +#define TRB_LINK               6
> +
> +/* Cycle bit - indicates TRB ownership by driver or hw*/
> +#define TRB_CYCLE              BIT(0)
> +/*
> + * When set to '1', the device will toggle its interpretation of the Cycle bit
> + */
> +#define TRB_TOGGLE             BIT(1)
> +
> +/* Interrupt on short packet*/
> +#define TRB_ISP                        BIT(2)
> +/*Setting this bit enables FIFO DMA operation mode*/
> +#define TRB_FIFO_MODE          BIT(3)
> +/* Set PCIe no snoop attribute */
> +#define TRB_CHAIN              BIT(4)
> +/* Interrupt on completion */
> +#define TRB_IOC                        BIT(5)
> +
> +/* stream ID bitmasks. */
> +#define TRB_STREAM_ID(p)       ((p) & GENMASK(31, 16))
> +
> +/* transfer_len bitmasks. */
> +#define TRB_LEN(p)             ((p) & GENMASK(16, 0))
> +
> +/* transfer_len bitmasks - bits 31:24 */
> +#define TRB_BURST_LEN(p)       ((p) & GENMASK(31, 24))
> +
> +/* Data buffer pointer bitmasks*/
> +#define TRB_BUFFER(p)          ((p) & GENMASK(31, 0))
> +
> +/*-------------------------------------------------------------------------*/
> +/* Driver numeric constants */
> +
> +/* Such declaration should be added to ch9.h */
> +#define USB_DEVICE_MAX_ADDRESS         127
> +
> +/* Endpoint init values */
> +#define CDNS3_EP_MAX_PACKET_LIMIT      1024
> +#define CDNS3_EP_MAX_STREAMS           15
> +#define CDNS3_EP0_MAX_PACKET_LIMIT     512
> +
> +/* All endpoints including EP0 */
> +#define CDNS3_ENDPOINTS_MAX_COUNT      32
> +#define CDNS3_EP_ZLP_BUF_SIZE          1024
> +
> +#define CDNS3_EP_BUF_SIZE              2       /* KB */
> +#define CDNS3_ALIGNED_BUF_SIZE         16384   /* Bytes */
> +#define CDNS3_MAX_NUM_DESCMISS_BUF     32
> +#define CDNS3_DESCMIS_BUF_SIZE         2048    /* Bytes */
> +/*-------------------------------------------------------------------------*/
> +/* Used structs */
> +
> +struct cdns3_device;
> +
> +/**
> + * struct cdns3_endpoint - extended device side representation of USB endpoint.
> + * @endpoint: usb endpoint
> + * @pending_req_list: list of requests queuing on transfer ring.
> + * @deferred_req_list: list of requests waiting for queuing on transfer ring.
> + * @descmiss_req_list: list of requests internally allocated by driver (WA1).
> + * @trb_pool: transfer ring - array of transaction buffers
> + * @trb_pool_dma: dma address of transfer ring
> + * @cdns3_dev: device associated with this endpoint
> + * @name: a human readable name e.g. ep1out
> + * @flags: specify the current state of endpoint
> + * @descmis_req: internal transfer object used for getting data from on-chip
> + *     buffer. It can happen only if function driver doesn't send usb_request
> + *     object on time.
> + * @aligned_buff: aligned to 8 bytes data buffer. Buffer address used in
> + *     TRB shall be aligned to 8.
> + * @aligned_dma_addr: dma address of aligned_buff
> + * @dir: endpoint direction
> + * @num: endpoint number (1 - 15)
> + * @type: set to bmAttributes & USB_ENDPOINT_XFERTYPE_MASK
> + * @interval: interval between packets used for ISOC endpoint.
> + * @free_trbs: number of free TRBs in transfer ring
> + * @num_trbs: number of all TRBs in transfer ring
> + * @pcs: producer cycle state
> + * @ccs: consumer cycle state
> + * @enqueue: enqueue index in transfer ring
> + * @dequeue: dequeue index in transfer ring
> + */
> +struct cdns3_endpoint {
> +       struct usb_ep           endpoint;
> +       struct list_head        pending_req_list;
> +       struct list_head        deferred_req_list;
> +       struct list_head        descmiss_req_list;
> +
> +       struct cdns3_trb        *trb_pool;
> +       dma_addr_t              trb_pool_dma;
> +
> +       struct cdns3_device     *cdns3_dev;
> +       char                    name[20];
> +
> +#define EP_ENABLED             BIT(0)
> +#define EP_STALL               BIT(1)
> +#define EP_WEDGE               BIT(2)
> +#define EP_TRANSFER_STARTED    BIT(3)
> +#define EP_UPDATE_EP_TRBADDR   BIT(4)
> +#define EP_PENDING_REQUEST     BIT(5)
> +#define EP_RING_FULL           BIT(6)
> +#define EP_CLAIMED             BIT(7)
> +#define EP_QUIRK_EXTRA_BUF_DET BIT(8)
> +#define EP_QUIRK_EXTRA_BUF_EN  BIT(9)
> +#define EP_QUIRK_END_TRANSFER  BIT(10)
> +
> +       u32                     flags;
> +
> +       struct cdns3_request    *descmis_req;
> +
> +       void                    *aligned_buff;
> +       dma_addr_t              aligned_dma_addr;
> +       u8                      dir;
> +       u8                      num;
> +       u8                      type;
> +       int                     interval;
> +
> +       int                     free_trbs;
> +       int                     num_trbs;
> +       u8                      pcs;
> +       u8                      ccs;
> +       int                     enqueue;
> +       int                     dequeue;
> +};
> +
> +/**
> + * struct cdns3_request - extended device side representation of usb_request
> + *                        object .
> + * @request: generic usb_request object describing single I/O request.
> + * @priv_ep: extended representation of usb_ep object
> + * @trb: the first TRB association with this request
> + * @start_trb: number of the first TRB in transfer ring
> + * @end_trb: number of the last TRB in transfer ring
> + * @flags: flag specifying special usage of request
> + * @list: used by internally allocated request to add to descmiss_req_list.
> + */
> +struct cdns3_request {
> +       struct usb_request      request;
> +       struct cdns3_endpoint   *priv_ep;
> +       struct cdns3_trb        *trb;
> +       int                     start_trb;
> +       int                     end_trb;
> +#define REQUEST_PENDING                BIT(0)
> +#define REQUEST_INTERNAL       BIT(1)
> +#define REQUEST_INTERNAL_CH    BIT(2)
> +#define REQUEST_ZLP            BIT(3)
> +       u32                     flags;
> +       struct list_head        list;
> +};
> +
> +#define to_cdns3_request(r) (container_of(r, struct cdns3_request, request))
> +
> +/*Stages used during enumeration process.*/
> +#define CDNS3_SETUP_STAGE              0x0
> +#define CDNS3_DATA_STAGE               0x1
> +#define CDNS3_STATUS_STAGE             0x2
> +
> +/**
> + * struct cdns3_device - represent USB device.
> + * @dev: pointer to device structure associated whit this controller
> + * @sysdev: pointer to the DMA capable device
> + * @gadget: device side representation of the peripheral controller
> + * @gadget_driver: pointer to the gadget driver
> + * @dev_ver: device controller version.
> + * @lock: for synchronizing
> + * @regs: base address for device side registers
> + * @setup_buf: used while processing usb control requests
> + * @setup_dma: dma address for setup_buf
> + * @zlp_buf - zlp buffer
> + * @ep0_stage: ep0 stage during enumeration process.
> + * @ep0_data_dir: direction for control transfer
> + * @eps: array of pointers to all endpoints with exclusion ep0
> + * @selected_ep: actually selected endpoint. It's used only to improve
> + *               performance.
> + * @isoch_delay: value from Set Isoch Delay request. Only valid on SS/SSP.
> + * @u1_allowed: allow device transition to u1 state
> + * @u2_allowed: allow device transition to u2 state
> + * @is_selfpowered: device is self powered
> + * @setup_pending: setup packet is processing by gadget driver
> + * @hw_configured_flag: hardware endpoint configuration was set.
> + * @wake_up_flag: allow device to remote up the host
> + * @status_completion_no_call: indicate that driver is waiting for status s
> + *     stage completion. It's used in deferred SET_CONFIGURATION request.
> + * @onchip_mem_allocated_size: actual size of on-chip memory assigned
> + *     to endpoints
> + * @pending_status_wq: workqueue handling status stage for deferred requests.
> + * @pending_status_request: request for which status stage was deferred
> + */
> +struct cdns3_device {
> +       struct device                   *dev;
> +       struct device                   *sysdev;
> +
> +       struct usb_gadget               gadget;
> +       struct usb_gadget_driver        *gadget_driver;
> +
> +#define CDNS_REVISION_V0               0x00024501
> +#define CDNS_REVISION_V1               0x00024509
> +       u32                             dev_ver;
> +
> +       /* generic spin-lock for drivers */
> +       spinlock_t                      lock;
> +
> +       struct cdns3_usb_regs           __iomem *regs;
> +
> +       struct usb_ctrlrequest          *setup_buf;
> +       dma_addr_t                      setup_dma;
> +       void                            *zlp_buf;
> +
> +       u8                              ep0_stage;
> +       int                             ep0_data_dir;
> +
> +       struct cdns3_endpoint           *eps[CDNS3_ENDPOINTS_MAX_COUNT];
> +
> +       u32                             selected_ep;
> +       u16                             isoch_delay;
> +
> +       unsigned                        u1_allowed:1;
> +       unsigned                        u2_allowed:1;
> +       unsigned                        is_selfpowered:1;
> +       unsigned                        setup_pending:1;
> +       int                             hw_configured_flag:1;
> +       int                             wake_up_flag:1;
> +       unsigned                        status_completion_no_call:1;
> +
> +       struct work_struct              pending_status_wq;
> +       struct usb_request              *pending_status_request;
> +
> +       /*in KB */
> +       int                             onchip_mem_allocated_size;
> +};
> +
> +int cdns3_handshake(void __iomem *ptr, u32 mask, u32 done, int usec);
> +void cdns3_set_register_bit(void __iomem *ptr, u32 mask);
> +dma_addr_t cdns3_trb_virt_to_dma(struct cdns3_endpoint *priv_ep,
> +                                struct cdns3_trb *trb);
> +enum usb_device_speed cdns3_get_speed(struct cdns3_device *priv_dev);
> +void cdns3_pending_setup_status_handler(struct work_struct *work);
> +void cdns3_hw_reset_eps_config(struct cdns3_device *priv_dev);
> +void cdns3_set_hw_configuration(struct cdns3_device *priv_dev);
> +void cdns3_select_ep(struct cdns3_device *priv_dev, u32 ep);
> +void cdns3_allow_enable_l1(struct cdns3_device *priv_dev, int enable);
> +struct usb_request *cdns3_next_request(struct list_head *list);
> +int cdns3_ep_run_transfer(struct cdns3_endpoint *priv_ep,
> +                         struct usb_request *request);
> +int cdns3_allocate_trb_pool(struct cdns3_endpoint *priv_ep);
> +u8 cdns3_ep_addr_to_index(u8 ep_addr);
> +int cdns3_gadget_ep_set_wedge(struct usb_ep *ep);
> +int cdns3_gadget_ep_set_halt(struct usb_ep *ep, int value);
> +struct usb_request *cdns3_gadget_ep_alloc_request(struct usb_ep *ep,
> +                                                 gfp_t gfp_flags);
> +void cdns3_gadget_ep_free_request(struct usb_ep *ep,
> +                                 struct usb_request *request);
> +int cdns3_gadget_ep_dequeue(struct usb_ep *ep, struct usb_request *request);
> +void cdns3_gadget_giveback(struct cdns3_endpoint *priv_ep,
> +                          struct cdns3_request *priv_req,
> +                          int status);
> +
> +int cdns3_init_ep0(struct cdns3_device *priv_dev,
> +                  struct cdns3_endpoint *priv_ep);
> +void cdns3_ep0_config(struct cdns3_device *priv_dev);
> +void cdns3_ep_config(struct cdns3_endpoint *priv_ep);
> +void cdns3_check_ep0_interrupt_proceed(struct cdns3_device *priv_dev, int dir);
> +
> +#endif /* __LINUX_CDNS3_GADGET */
> diff --git a/drivers/usb/cdns3/host-export.h b/drivers/usb/cdns3/host-export.h
> new file mode 100644
> index 000000000000..b498a170b7e8
> --- /dev/null
> +++ b/drivers/usb/cdns3/host-export.h
> @@ -0,0 +1,28 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +/*
> + * Cadence USBSS DRD Driver - Host Export APIs
> + *
> + * Copyright (C) 2017-2018 NXP
> + *
> + * Authors: Peter Chen <peter.chen@nxp.com>
> + */
> +#ifndef __LINUX_CDNS3_HOST_EXPORT
> +#define __LINUX_CDNS3_HOST_EXPORT
> +
> +#ifdef CONFIG_USB_CDNS3_HOST
> +
> +int cdns3_host_init(struct cdns3 *cdns);
> +void cdns3_host_exit(struct cdns3 *cdns);
> +
> +#else
> +
> +static inline int cdns3_host_init(struct cdns3 *cdns)
> +{
> +       return -ENXIO;
> +}
> +
> +static inline void cdns3_host_exit(struct cdns3 *cdns) { }
> +
> +#endif /* CONFIG_USB_CDNS3_HOST */
> +
> +#endif /* __LINUX_CDNS3_HOST_EXPORT */
> diff --git a/drivers/usb/cdns3/host.c b/drivers/usb/cdns3/host.c
> new file mode 100644
> index 000000000000..b43b0236a885
> --- /dev/null
> +++ b/drivers/usb/cdns3/host.c
> @@ -0,0 +1,72 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Cadence USBSS DRD Driver - host side
> + *
> + * Copyright (C) 2018 Cadence Design Systems.
> + * Copyright (C) 2017-2018 NXP
> + *
> + * Authors: Peter Chen <peter.chen@nxp.com>
> + *         Pawel Laszczak <pawell@cadence.com>
> + */
> +
> +#include <linux/platform_device.h>
> +#include "core.h"
> +
> +static int __cdns3_host_init(struct cdns3 *cdns)
> +{
> +       struct platform_device *xhci;
> +       int ret;
> +
> +       xhci = platform_device_alloc("xhci-hcd", PLATFORM_DEVID_AUTO);
> +       if (!xhci) {
> +               dev_err(cdns->dev, "couldn't allocate xHCI device\n");
> +               return -ENOMEM;
> +       }
> +
> +       xhci->dev.parent = cdns->dev;
> +       cdns->host_dev = xhci;
> +
> +       ret = platform_device_add_resources(xhci, cdns->xhci_res,
> +                                           CDNS3_XHCI_RESOURCES_NUM);
> +       if (ret) {
> +               dev_err(cdns->dev, "couldn't add resources to xHCI device\n");
> +               goto err1;
> +       }
> +
> +       ret = platform_device_add(xhci);
> +       if (ret) {
> +               dev_err(cdns->dev, "failed to register xHCI device\n");
> +               goto err1;
> +       }
> +
> +       return 0;
> +err1:
> +       platform_device_put(xhci);
> +       return ret;
> +}
> +
> +static void cdns3_host_exit(struct cdns3 *cdns)
> +{
> +       platform_device_unregister(cdns->host_dev);
> +       cdns->host_dev = NULL;
> +}
> +
> +int cdns3_host_init(struct cdns3 *cdns)
> +{
> +       struct cdns3_role_driver *rdrv;
> +
> +       rdrv = devm_kzalloc(cdns->dev, sizeof(*rdrv), GFP_KERNEL);
> +       if (!rdrv)
> +               return -ENOMEM;
> +
> +       rdrv->start     = __cdns3_host_init;
> +       rdrv->stop      = cdns3_host_exit;
> +       rdrv->state     = CDNS3_ROLE_STATE_INACTIVE;
> +       rdrv->suspend   = NULL;
> +       rdrv->resume    = NULL;
> +       rdrv->name      = "host";
> +
> +       cdns->roles[CDNS3_ROLE_HOST] = rdrv;
> +
> +       return 0;
> +}
> diff --git a/drivers/usb/cdns3/trace.c b/drivers/usb/cdns3/trace.c
> new file mode 100644
> index 000000000000..587ae08e019d
> --- /dev/null
> +++ b/drivers/usb/cdns3/trace.c
> @@ -0,0 +1,11 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * USBSS device controller driver Trace Support
> + *
> + * Copyright (C) 2018 Cadence.
> + *
> + * Author: Pawel Laszczak <pawell@cadence.com>
> + */
> +
> +#define CREATE_TRACE_POINTS
> +#include "trace.h"
> diff --git a/drivers/usb/cdns3/trace.h b/drivers/usb/cdns3/trace.h
> new file mode 100644
> index 000000000000..f3d7a91fae86
> --- /dev/null
> +++ b/drivers/usb/cdns3/trace.h
> @@ -0,0 +1,389 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +/*
> + * USBSS device controller driver.
> + * Trace support header file.
> + *
> + * Copyright (C) 2018 Cadence.
> + *
> + * Author: Pawel Laszczak <pawell@cadence.com>
> + */
> +
> +#undef TRACE_SYSTEM
> +#define TRACE_SYSTEM cdns3
> +
> +#if !defined(__LINUX_CDNS3_TRACE) || defined(TRACE_HEADER_MULTI_READ)
> +#define __LINUX_CDNS3_TRACE
> +
> +#include <linux/types.h>
> +#include <linux/tracepoint.h>
> +#include <asm/byteorder.h>
> +#include <linux/usb/ch9.h>
> +#include "core.h"
> +#include "gadget.h"
> +#include "debug.h"
> +
> +#define CDNS3_MSG_MAX  500
> +
> +DECLARE_EVENT_CLASS(cdns3_log_doorbell,
> +       TP_PROTO(const char *ep_name, u32 ep_trbaddr),
> +       TP_ARGS(ep_name, ep_trbaddr),
> +       TP_STRUCT__entry(
> +               __string(name, ep_name)
> +               __field(u32, ep_trbaddr)
> +       ),
> +       TP_fast_assign(
> +               __assign_str(name, ep_name);
> +               __entry->ep_trbaddr = ep_trbaddr;
> +       ),
> +       TP_printk("//Ding Dong %s, ep_trbaddr %08x", __get_str(name),
> +                 __entry->ep_trbaddr)
> +);
> +
> +DEFINE_EVENT(cdns3_log_doorbell, cdns3_doorbell_ep0,
> +       TP_PROTO(const char *ep_name, u32 ep_trbaddr),
> +       TP_ARGS(ep_name, ep_trbaddr)
> +);
> +
> +DEFINE_EVENT(cdns3_log_doorbell, cdns3_doorbell_epx,
> +       TP_PROTO(const char *ep_name, u32 ep_trbaddr),
> +       TP_ARGS(ep_name, ep_trbaddr)
> +);
> +
> +DECLARE_EVENT_CLASS(cdns3_log_usb_irq,
> +       TP_PROTO(struct cdns3_device *priv_dev, u32 usb_ists),
> +       TP_ARGS(priv_dev, usb_ists),
> +       TP_STRUCT__entry(
> +               __field(enum usb_device_speed, speed)
> +               __field(u32, usb_ists)
> +               __dynamic_array(char, str, CDNS3_MSG_MAX)
> +       ),
> +       TP_fast_assign(
> +               __entry->speed = cdns3_get_speed(priv_dev);
> +               __entry->usb_ists = usb_ists;
> +       ),
> +       TP_printk("%s", cdns3_decode_usb_irq(__get_str(str), __entry->speed,
> +                                            __entry->usb_ists))
> +);
> +
> +DEFINE_EVENT(cdns3_log_usb_irq, cdns3_usb_irq,
> +       TP_PROTO(struct cdns3_device *priv_dev, u32 usb_ists),
> +       TP_ARGS(priv_dev, usb_ists)
> +);
> +
> +DECLARE_EVENT_CLASS(cdns3_log_epx_irq,
> +       TP_PROTO(struct cdns3_device *priv_dev, struct cdns3_endpoint *priv_ep),
> +       TP_ARGS(priv_dev, priv_ep),
> +       TP_STRUCT__entry(
> +               __string(ep_name, priv_ep->name)
> +               __field(u32, ep_sts)
> +               __field(u32, ep_traddr)
> +               __dynamic_array(char, str, CDNS3_MSG_MAX)
> +       ),
> +       TP_fast_assign(
> +               __assign_str(ep_name, priv_ep->name);
> +               __entry->ep_sts = readl(&priv_dev->regs->ep_sts);
> +               __entry->ep_traddr = readl(&priv_dev->regs->ep_traddr);
> +       ),
> +       TP_printk("%s, ep_traddr: %08x",
> +                 cdns3_decode_epx_irq(__get_str(str),
> +                                      __get_str(ep_name),
> +                                      __entry->ep_sts),
> +                 __entry->ep_traddr)
> +);
> +
> +DEFINE_EVENT(cdns3_log_epx_irq, cdns3_epx_irq,
> +       TP_PROTO(struct cdns3_device *priv_dev, struct cdns3_endpoint *priv_ep),
> +       TP_ARGS(priv_dev, priv_ep)
> +);
> +
> +DECLARE_EVENT_CLASS(cdns3_log_ep0_irq,
> +       TP_PROTO(struct cdns3_device *priv_dev,  u32 ep_sts),
> +       TP_ARGS(priv_dev, ep_sts),
> +       TP_STRUCT__entry(
> +               __field(int, ep_dir)
> +               __field(u32, ep_sts)
> +               __dynamic_array(char, str, CDNS3_MSG_MAX)
> +       ),
> +       TP_fast_assign(
> +               __entry->ep_dir = priv_dev->ep0_data_dir;
> +               __entry->ep_sts = ep_sts;
> +       ),
> +       TP_printk("%s", cdns3_decode_ep0_irq(__get_str(str),
> +                                            __entry->ep_dir,
> +                                            __entry->ep_sts))
> +);
> +
> +DEFINE_EVENT(cdns3_log_ep0_irq, cdns3_ep0_irq,
> +       TP_PROTO(struct cdns3_device *priv_dev, u32 ep_sts),
> +       TP_ARGS(priv_dev, ep_sts)
> +);
> +
> +DECLARE_EVENT_CLASS(cdns3_log_ctrl,
> +       TP_PROTO(struct usb_ctrlrequest *ctrl),
> +       TP_ARGS(ctrl),
> +       TP_STRUCT__entry(
> +               __field(u8, bRequestType)
> +               __field(u8, bRequest)
> +               __field(u16, wValue)
> +               __field(u16, wIndex)
> +               __field(u16, wLength)
> +               __dynamic_array(char, str, CDNS3_MSG_MAX)
> +       ),
> +       TP_fast_assign(
> +               __entry->bRequestType = ctrl->bRequestType;
> +               __entry->bRequest = ctrl->bRequest;
> +               __entry->wValue = le16_to_cpu(ctrl->wValue);
> +               __entry->wIndex = le16_to_cpu(ctrl->wIndex);
> +               __entry->wLength = le16_to_cpu(ctrl->wLength);
> +       ),
> +       TP_printk("%s", usb_decode_ctrl(__get_str(str), __entry->bRequestType,
> +                                       __entry->bRequest, __entry->wValue,
> +                                       __entry->wIndex, __entry->wLength)
> +       )
> +);
> +
> +DEFINE_EVENT(cdns3_log_ctrl, cdns3_ctrl_req,
> +       TP_PROTO(struct usb_ctrlrequest *ctrl),
> +       TP_ARGS(ctrl)
> +);
> +
> +DECLARE_EVENT_CLASS(cdns3_log_request,
> +       TP_PROTO(struct cdns3_request *req),
> +       TP_ARGS(req),
> +       TP_STRUCT__entry(
> +               __string(name, req->priv_ep->name)
> +               __field(struct cdns3_request *, req)
> +               __field(unsigned int, actual)
> +               __field(unsigned int, length)
> +               __field(int, status)
> +               __field(int, zero)
> +               __field(int, short_not_ok)
> +               __field(int, no_interrupt)
> +               __field(int, start_trb)
> +               __field(int, end_trb)
> +               __field(struct cdns3_trb *, start_trb_addr)
> +               __field(int, flags)
> +       ),
> +       TP_fast_assign(
> +               __assign_str(name, req->priv_ep->name);
> +               __entry->req = req;
> +               __entry->actual = req->request.actual;
> +               __entry->length = req->request.length;
> +               __entry->status = req->request.status;
> +               __entry->zero = req->request.zero;
> +               __entry->short_not_ok = req->request.short_not_ok;
> +               __entry->no_interrupt = req->request.no_interrupt;
> +               __entry->start_trb = req->start_trb;
> +               __entry->end_trb = req->end_trb;
> +               __entry->start_trb_addr = req->trb;
> +               __entry->flags = req->flags;
> +       ),
> +       TP_printk("%s: req: %p, length: %u/%u %s%s%s, status: %d,"
> +                 " trb: [start:%d, end:%d: virt addr %pa], flags:%x ",
> +               __get_str(name), __entry->req, __entry->actual, __entry->length,
> +               __entry->zero ? "zero | " : "",
> +               __entry->short_not_ok ? "short | " : "",
> +               __entry->no_interrupt ? "no int" : "",
> +               __entry->status,
> +               __entry->start_trb,
> +               __entry->end_trb,
> +               __entry->start_trb_addr,
> +               __entry->flags
> +       )
> +);
> +
> +DEFINE_EVENT(cdns3_log_request, cdns3_alloc_request,
> +       TP_PROTO(struct cdns3_request *req),
> +       TP_ARGS(req)
> +);
> +
> +DEFINE_EVENT(cdns3_log_request, cdns3_free_request,
> +       TP_PROTO(struct cdns3_request *req),
> +       TP_ARGS(req)
> +);
> +
> +DEFINE_EVENT(cdns3_log_request, cdns3_ep_queue,
> +       TP_PROTO(struct cdns3_request *req),
> +       TP_ARGS(req)
> +);
> +
> +DEFINE_EVENT(cdns3_log_request, cdns3_ep_dequeue,
> +       TP_PROTO(struct cdns3_request *req),
> +       TP_ARGS(req)
> +);
> +
> +DEFINE_EVENT(cdns3_log_request, cdns3_gadget_giveback,
> +       TP_PROTO(struct cdns3_request *req),
> +       TP_ARGS(req)
> +);
> +
> +DECLARE_EVENT_CLASS(cdns3_log_trb,
> +       TP_PROTO(struct cdns3_endpoint *priv_ep, struct cdns3_trb *trb),
> +       TP_ARGS(priv_ep, trb),
> +       TP_STRUCT__entry(
> +               __string(name, priv_ep->name)
> +               __field(struct cdns3_trb *, trb)
> +               __field(u32, buffer)
> +               __field(u32, length)
> +               __field(u32, control)
> +               __field(u32, type)
> +       ),
> +       TP_fast_assign(
> +               __assign_str(name, priv_ep->name);
> +               __entry->trb = trb;
> +               __entry->buffer = trb->buffer;
> +               __entry->length = trb->length;
> +               __entry->control = trb->control;
> +               __entry->type = usb_endpoint_type(priv_ep->endpoint.desc);
> +       ),
> +       TP_printk("%s: trb 0x%pa, dma buf: 0x%08x, size: %ld, ctrl: 0x%08x (%s%s%s%s%s%s%s)",
> +               __get_str(name), __entry->trb, __entry->buffer,
> +               TRB_LEN(__entry->length), __entry->control,
> +               __entry->control & TRB_CYCLE ? "C=1, " : "C=0, ",
> +               __entry->control & TRB_TOGGLE ? "T=1, " : "T=0, ",
> +               __entry->control & TRB_ISP ? "ISP, " : "",
> +               __entry->control & TRB_FIFO_MODE ? "FIFO, " : "",
> +               __entry->control & TRB_CHAIN ? "CHAIN, " : "",
> +               __entry->control & TRB_IOC ? "IOC, " : "",
> +               TRB_FIELD_TO_TYPE(__entry->control) == TRB_NORMAL ? "Normal" : "LINK"
> +       )
> +);
> +
> +DEFINE_EVENT(cdns3_log_trb, cdns3_prepare_trb,
> +       TP_PROTO(struct cdns3_endpoint *priv_ep, struct cdns3_trb *trb),
> +       TP_ARGS(priv_ep, trb)
> +);
> +
> +DEFINE_EVENT(cdns3_log_trb, cdns3_complete_trb,
> +       TP_PROTO(struct cdns3_endpoint *priv_ep, struct cdns3_trb *trb),
> +       TP_ARGS(priv_ep, trb)
> +);
> +
> +DECLARE_EVENT_CLASS(cdns3_log_ring,
> +       TP_PROTO(struct cdns3_endpoint *priv_ep),
> +       TP_ARGS(priv_ep),
> +       TP_STRUCT__entry(
> +               __dynamic_array(u8, ring, TRB_RING_SIZE)
> +               __dynamic_array(u8, priv_ep, sizeof(struct cdns3_endpoint))
> +               __dynamic_array(char, buffer,
> +                               (TRBS_PER_SEGMENT * 65) + CDNS3_MSG_MAX)
> +       ),
> +       TP_fast_assign(
> +               memcpy(__get_dynamic_array(priv_ep), priv_ep,
> +                      sizeof(struct cdns3_endpoint));
> +               memcpy(__get_dynamic_array(ring), priv_ep->trb_pool,
> +                      TRB_RING_SIZE);
> +       ),
> +
> +       TP_printk("%s",
> +                 cdns3_dbg_ring((struct cdns3_endpoint *)__get_str(priv_ep),
> +                                (struct cdns3_trb *)__get_str(ring),
> +                                __get_str(buffer)))
> +);
> +
> +DEFINE_EVENT(cdns3_log_ring, cdns3_ring,
> +       TP_PROTO(struct cdns3_endpoint *priv_ep),
> +       TP_ARGS(priv_ep)
> +);
> +
> +DECLARE_EVENT_CLASS(cdns3_log_ep,
> +       TP_PROTO(struct cdns3_endpoint *priv_ep),
> +       TP_ARGS(priv_ep),
> +       TP_STRUCT__entry(
> +               __string(name, priv_ep->name)
> +               __field(unsigned int, maxpacket)
> +               __field(unsigned int, maxpacket_limit)
> +               __field(unsigned int, max_streams)
> +               __field(unsigned int, maxburst)
> +               __field(unsigned int, flags)
> +               __field(unsigned int, dir)
> +               __field(u8, enqueue)
> +               __field(u8, dequeue)
> +       ),
> +       TP_fast_assign(
> +               __assign_str(name, priv_ep->name);
> +               __entry->maxpacket = priv_ep->endpoint.maxpacket;
> +               __entry->maxpacket_limit = priv_ep->endpoint.maxpacket_limit;
> +               __entry->max_streams = priv_ep->endpoint.max_streams;
> +               __entry->maxburst = priv_ep->endpoint.maxburst;
> +               __entry->flags = priv_ep->flags;
> +               __entry->dir = priv_ep->dir;
> +               __entry->enqueue = priv_ep->enqueue;
> +               __entry->dequeue = priv_ep->dequeue;
> +       ),
> +       TP_printk("%s: mps: %d/%d. streams: %d, burst: %d, enq idx: %d, "
> +                 "deq idx: %d, flags %s%s%s%s%s%s%s%s, dir: %s",
> +               __get_str(name), __entry->maxpacket,
> +               __entry->maxpacket_limit, __entry->max_streams,
> +               __entry->maxburst, __entry->enqueue,
> +               __entry->dequeue,
> +               __entry->flags & EP_ENABLED ? "EN | " : "",
> +               __entry->flags & EP_STALL ? "STALL | " : "",
> +               __entry->flags & EP_WEDGE ? "WEDGE | " : "",
> +               __entry->flags & EP_TRANSFER_STARTED ? "STARTED | " : "",
> +               __entry->flags & EP_UPDATE_EP_TRBADDR ? "UPD TRB | " : "",
> +               __entry->flags & EP_PENDING_REQUEST ? "REQ PEN | " : "",
> +               __entry->flags & EP_RING_FULL ? "RING FULL |" : "",
> +               __entry->flags & EP_CLAIMED ?  "CLAIMED " : "",
> +               __entry->dir ? "IN" : "OUT"
> +       )
> +);
> +
> +DEFINE_EVENT(cdns3_log_ep, cdns3_gadget_ep_enable,
> +       TP_PROTO(struct cdns3_endpoint *priv_ep),
> +       TP_ARGS(priv_ep)
> +);
> +
> +DEFINE_EVENT(cdns3_log_ep, cdns3_gadget_ep_disable,
> +       TP_PROTO(struct cdns3_endpoint *priv_ep),
> +       TP_ARGS(priv_ep)
> +);
> +
> +DECLARE_EVENT_CLASS(cdns3_log_request_handled,
> +       TP_PROTO(struct cdns3_request *priv_req, int current_index,
> +                int handled),
> +       TP_ARGS(priv_req, current_index, handled),
> +       TP_STRUCT__entry(
> +               __field(struct cdns3_request *, priv_req)
> +               __field(unsigned int, dma_position)
> +               __field(unsigned int, handled)
> +               __field(unsigned int, dequeue_idx)
> +               __field(unsigned int, enqueue_idx)
> +               __field(unsigned int, start_trb)
> +               __field(unsigned int, end_trb)
> +       ),
> +       TP_fast_assign(
> +               __entry->priv_req = priv_req;
> +               __entry->dma_position = current_index;
> +               __entry->handled = handled;
> +               __entry->dequeue_idx = priv_req->priv_ep->dequeue;
> +               __entry->enqueue_idx = priv_req->priv_ep->enqueue;
> +               __entry->start_trb = priv_req->start_trb;
> +               __entry->end_trb = priv_req->end_trb;
> +       ),
> +       TP_printk("Req: %p %s, DMA pos: %d, ep deq: %d, ep enq: %d,"
> +                 " start trb: %d, end trb: %d",
> +               __entry->priv_req,
> +               __entry->handled ? "handled" : "not handled",
> +               __entry->dma_position, __entry->dequeue_idx,
> +               __entry->enqueue_idx, __entry->start_trb,
> +               __entry->end_trb
> +       )
> +);
> +
> +DEFINE_EVENT(cdns3_log_request_handled, cdns3_request_handled,
> +       TP_PROTO(struct cdns3_request *priv_req, int current_index,
> +                int handled),
> +       TP_ARGS(priv_req, current_index, handled)
> +);
> +#endif /* __LINUX_CDNS3_TRACE */
> +
> +/* this part must be outside header guard */
> +
> +#undef TRACE_INCLUDE_PATH
> +#define TRACE_INCLUDE_PATH .
> +
> +#undef TRACE_INCLUDE_FILE
> +#define TRACE_INCLUDE_FILE trace
> +
> +#include <trace/define_trace.h>
> --
> 2.17.1
>

[-- Attachment #2: 0001-usb-cdns3-improve-drd-flow.patch --]
[-- Type: application/octet-stream, Size: 62524 bytes --]

From dd43a038b9eb553ffb864ddef703d41355272bae Mon Sep 17 00:00:00 2001
From: Peter Chen <peter.chen@nxp.com>
Date: Wed, 9 Jan 2019 17:50:10 +0800
Subject: [PATCH 1/1] usb: cdns3: improve drd flow

Signed-off-by: Peter Chen <peter.chen@nxp.com>
---
 drivers/usb/cdns3/Kconfig             |  10 +
 drivers/usb/cdns3/Makefile            |  14 +-
 drivers/usb/cdns3/cdns3-imx.c         | 600 ++++++++++++++++++++++++++++++++++
 drivers/usb/cdns3/cdns3-nxp-reg-def.h | 174 ++++++++++
 drivers/usb/cdns3/core.c              | 272 ++-------------
 drivers/usb/cdns3/core.h              |  35 +-
 drivers/usb/cdns3/drd.c               | 480 ++++++++++++++++-----------
 drivers/usb/cdns3/drd.h               |  14 +-
 drivers/usb/cdns3/gadget.c            |  70 +++-
 drivers/usb/cdns3/gadget.h            |   5 +-
 drivers/usb/cdns3/trace.h             |   4 +-
 11 files changed, 1210 insertions(+), 468 deletions(-)
 create mode 100644 drivers/usb/cdns3/cdns3-imx.c
 create mode 100644 drivers/usb/cdns3/cdns3-nxp-reg-def.h

diff --git a/drivers/usb/cdns3/Kconfig b/drivers/usb/cdns3/Kconfig
index 4adfd87811e8..0e89df18f180 100644
--- a/drivers/usb/cdns3/Kconfig
+++ b/drivers/usb/cdns3/Kconfig
@@ -41,4 +41,14 @@ config USB_CDNS3_PCI_WRAP
 	  If you choose to build this driver as module it will
 	  be dynamically linked and module will be called cdns3-pci.ko
 
+config USB_CDNS3_IMX_WRAP
+	tristate "Cadence USB3 support on NXP i.MX platforms"
+	depends on OF
+	default USB_CDNS3
+	help
+	  If you're using the USBSS Core IP with i.MX platform, please say
+	  'Y' or 'M' here.
+
+	  If you choose to build this driver as module it will
+	  be dynamically linked and module will be called cdns3-imx.ko
 endif
diff --git a/drivers/usb/cdns3/Makefile b/drivers/usb/cdns3/Makefile
index 3f63baa24294..7c2118b6b692 100644
--- a/drivers/usb/cdns3/Makefile
+++ b/drivers/usb/cdns3/Makefile
@@ -2,15 +2,13 @@
 # define_trace.h needs to know how to find our header
 CFLAGS_trace.o				:= -I$(src)
 
-obj-$(CONFIG_USB_CDNS3)			+= cdns3.o
-obj-$(CONFIG_USB_CDNS3_PCI_WRAP)	+= cdns3-pci.o
-
 cdns3-y					:= core.o drd.o trace.o
-
-ifneq ($(CONFIG_DEBUG_FS),)
-	cdns3-y				+= debugfs.o
-endif
+obj-$(CONFIG_USB_CDNS3)			+= cdns3.o
+#ifneq ($(CONFIG_DEBUG_FS),)
+#	cdns3-y				+= debugfs.o
+#endif
 
 cdns3-$(CONFIG_USB_CDNS3_GADGET)	+= gadget.o ep0.o
 cdns3-$(CONFIG_USB_CDNS3_HOST)		+= host.o
-cdns3-pci-y		 		:= cdns3-pci-wrap.o
+obj-$(CONFIG_USB_CDNS3_PCI_WRAP)	+= cdns3-pci-wrap.o
+obj-$(CONFIG_USB_CDNS3_IMX_WRAP)	+= cdns3-imx.o
diff --git a/drivers/usb/cdns3/cdns3-imx.c b/drivers/usb/cdns3/cdns3-imx.c
new file mode 100644
index 000000000000..600fab75b63e
--- /dev/null
+++ b/drivers/usb/cdns3/cdns3-imx.c
@@ -0,0 +1,600 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * cdns3-imx.c - imx Glue layer
+ *
+ * Copyright (C) 2017-2018 NXP
+ *
+ * Authors: Peter Chen <peter.chen@nxp.com>
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/platform_device.h>
+#include <linux/interrupt.h>
+#include <linux/of.h>
+#include <linux/of_platform.h>
+#include <linux/io.h>
+#include <linux/clk.h>
+#include <linux/usb/of.h>
+#include <linux/usb/phy.h>
+#include <linux/extcon.h>
+#include <linux/pm_runtime.h>
+#include <linux/reset-controller.h>
+
+#include "cdns3-nxp-reg-def.h"
+#include "core.h"
+
+#define USB3_CORE_CTRL1    0x00
+#define USB3_CORE_CTRL2    0x04
+#define USB3_INT_REG       0x08
+#define USB3_CORE_STATUS   0x0c
+#define XHCI_DEBUG_LINK_ST 0x10
+#define XHCI_DEBUG_BUS     0x14
+#define USB3_SSPHY_CTRL1   0x40
+#define USB3_SSPHY_CTRL2   0x44
+#define USB3_SSPHY_STATUS  0x4c
+#define USB2_PHY_CTRL1     0x50
+#define USB2_PHY_CTRL2     0x54
+#define USB2_PHY_STATUS    0x5c
+
+/* Register bits definition */
+
+/* USB3_CORE_CTRL1 */
+#define SW_RESET_MASK	(0x3f << 26)
+#define PWR_SW_RESET	(1 << 31)
+#define APB_SW_RESET	(1 << 30)
+#define AXI_SW_RESET	(1 << 29)
+#define RW_SW_RESET	(1 << 28)
+#define PHY_SW_RESET	(1 << 27)
+#define PHYAHB_SW_RESET	(1 << 26)
+#define ALL_SW_RESET	(PWR_SW_RESET | APB_SW_RESET | AXI_SW_RESET | \
+		RW_SW_RESET | PHY_SW_RESET | PHYAHB_SW_RESET)
+#define OC_DISABLE	(1 << 9)
+#define MDCTRL_CLK_SEL	(1 << 7)
+#define MODE_STRAP_MASK	(0x7)
+#define DEV_MODE	(1 << 2)
+#define HOST_MODE	(1 << 1)
+#define OTG_MODE	(1 << 0)
+
+/* USB3_INT_REG */
+#define CLK_125_REQ	(1 << 29)
+#define LPM_CLK_REQ	(1 << 28)
+#define DEVU3_WAEKUP_EN	(1 << 14)
+#define OTG_WAKEUP_EN	(1 << 12)
+#define DEV_INT_EN (3 << 8) /* DEV INT b9:8 */
+#define HOST_INT1_EN (1 << 0) /* HOST INT b7:0 */
+
+/* USB3_CORE_STATUS */
+#define MDCTRL_CLK_STATUS	(1 << 15)
+#define DEV_POWER_ON_READY	(1 << 13)
+#define HOST_POWER_ON_READY	(1 << 12)
+
+/* USB3_SSPHY_STATUS */
+#define PHY_REFCLK_REQ		(1 << 0)
+
+#define CDNS3_IMX_CLKS_NUM 5
+static int cdns3_imx_set_mode(struct cdns3 *cdns);
+
+struct cdns3_imx {
+	void __iomem *regs;
+	void __iomem *phy_regs;
+	enum usb_dr_mode role;
+	struct device *dev;
+	struct clk *imx_clks[CDNS3_IMX_CLKS_NUM];
+	struct reset_controller_dev rcdev;
+};
+
+struct cdns3_platform_data cdns3_imx_pdata = {
+	.set_mode = cdns3_imx_set_mode,
+};
+
+#if 1
+static void cdns3_usb_phy_init(void __iomem *regs)
+{
+	u32 value;
+
+	pr_debug("begin of %s\n", __func__);
+
+	writel(0x0830, regs + PHY_PMA_CMN_CTRL1);
+	writel(0x10, regs + TB_ADDR_CMN_DIAG_HSCLK_SEL);
+	writel(0x00F0, regs + TB_ADDR_CMN_PLL0_VCOCAL_INIT_TMR);
+	writel(0x0018, regs + TB_ADDR_CMN_PLL0_VCOCAL_ITER_TMR);
+	writel(0x00D0, regs + TB_ADDR_CMN_PLL0_INTDIV);
+	writel(0x4aaa, regs + TB_ADDR_CMN_PLL0_FRACDIV);
+	writel(0x0034, regs + TB_ADDR_CMN_PLL0_HIGH_THR);
+	writel(0x1ee, regs + TB_ADDR_CMN_PLL0_SS_CTRL1);
+	writel(0x7F03, regs + TB_ADDR_CMN_PLL0_SS_CTRL2);
+	writel(0x0020, regs + TB_ADDR_CMN_PLL0_DSM_DIAG);
+	writel(0x0000, regs + TB_ADDR_CMN_DIAG_PLL0_OVRD);
+	writel(0x0000, regs + TB_ADDR_CMN_DIAG_PLL0_FBH_OVRD);
+	writel(0x0000, regs + TB_ADDR_CMN_DIAG_PLL0_FBL_OVRD);
+	writel(0x0007, regs + TB_ADDR_CMN_DIAG_PLL0_V2I_TUNE);
+	writel(0x0027, regs + TB_ADDR_CMN_DIAG_PLL0_CP_TUNE);
+	writel(0x0008, regs + TB_ADDR_CMN_DIAG_PLL0_LF_PROG);
+	writel(0x0022, regs + TB_ADDR_CMN_DIAG_PLL0_TEST_MODE);
+	writel(0x000a, regs + TB_ADDR_CMN_PSM_CLK_CTRL);
+	writel(0x139, regs + TB_ADDR_XCVR_DIAG_RX_LANE_CAL_RST_TMR);
+	writel(0xbefc, regs + TB_ADDR_XCVR_PSM_RCTRL);
+
+	writel(0x7799, regs + TB_ADDR_TX_PSC_A0);
+	writel(0x7798, regs + TB_ADDR_TX_PSC_A1);
+	writel(0x509b, regs + TB_ADDR_TX_PSC_A2);
+	writel(0x3, regs + TB_ADDR_TX_DIAG_ECTRL_OVRD);
+	writel(0x509b, regs + TB_ADDR_TX_PSC_A3);
+	writel(0x2090, regs + TB_ADDR_TX_PSC_CAL);
+	writel(0x2090, regs + TB_ADDR_TX_PSC_RDY);
+
+	writel(0xA6FD, regs + TB_ADDR_RX_PSC_A0);
+	writel(0xA6FD, regs + TB_ADDR_RX_PSC_A1);
+	writel(0xA410, regs + TB_ADDR_RX_PSC_A2);
+	writel(0x2410, regs + TB_ADDR_RX_PSC_A3);
+
+	writel(0x23FF, regs + TB_ADDR_RX_PSC_CAL);
+	writel(0x2010, regs + TB_ADDR_RX_PSC_RDY);
+
+	writel(0x0020, regs + TB_ADDR_TX_TXCC_MGNLS_MULT_000);
+	writel(0x00ff, regs + TB_ADDR_TX_DIAG_BGREF_PREDRV_DELAY);
+	writel(0x0002, regs + TB_ADDR_RX_SLC_CU_ITER_TMR);
+	writel(0x0013, regs + TB_ADDR_RX_SIGDET_HL_FILT_TMR);
+	writel(0x0000, regs + TB_ADDR_RX_SAMP_DAC_CTRL);
+	writel(0x1004, regs + TB_ADDR_RX_DIAG_SIGDET_TUNE);
+	writel(0x4041, regs + TB_ADDR_RX_DIAG_LFPSDET_TUNE2);
+	writel(0x0480, regs + TB_ADDR_RX_DIAG_BS_TM);
+	writel(0x8006, regs + TB_ADDR_RX_DIAG_DFE_CTRL1);
+	writel(0x003f, regs + TB_ADDR_RX_DIAG_ILL_IQE_TRIM4);
+	writel(0x543f, regs + TB_ADDR_RX_DIAG_ILL_E_TRIM0);
+	writel(0x543f, regs + TB_ADDR_RX_DIAG_ILL_IQ_TRIM0);
+	writel(0x0000, regs + TB_ADDR_RX_DIAG_ILL_IQE_TRIM6);
+	writel(0x8000, regs + TB_ADDR_RX_DIAG_RXFE_TM3);
+	writel(0x0003, regs + TB_ADDR_RX_DIAG_RXFE_TM4);
+	writel(0x2408, regs + TB_ADDR_RX_DIAG_LFPSDET_TUNE);
+	writel(0x05ca, regs + TB_ADDR_RX_DIAG_DFE_CTRL3);
+	writel(0x0258, regs + TB_ADDR_RX_DIAG_SC2C_DELAY);
+	writel(0x1fff, regs + TB_ADDR_RX_REE_VGA_GAIN_NODFE);
+
+	writel(0x02c6, regs + TB_ADDR_XCVR_PSM_CAL_TMR);
+	writel(0x0002, regs + TB_ADDR_XCVR_PSM_A0BYP_TMR);
+	writel(0x02c6, regs + TB_ADDR_XCVR_PSM_A0IN_TMR);
+	writel(0x0010, regs + TB_ADDR_XCVR_PSM_A1IN_TMR);
+	writel(0x0010, regs + TB_ADDR_XCVR_PSM_A2IN_TMR);
+	writel(0x0010, regs + TB_ADDR_XCVR_PSM_A3IN_TMR);
+	writel(0x0010, regs + TB_ADDR_XCVR_PSM_A4IN_TMR);
+	writel(0x0010, regs + TB_ADDR_XCVR_PSM_A5IN_TMR);
+
+	writel(0x0002, regs + TB_ADDR_XCVR_PSM_A0OUT_TMR);
+	writel(0x0002, regs + TB_ADDR_XCVR_PSM_A1OUT_TMR);
+	writel(0x0002, regs + TB_ADDR_XCVR_PSM_A2OUT_TMR);
+	writel(0x0002, regs + TB_ADDR_XCVR_PSM_A3OUT_TMR);
+	writel(0x0002, regs + TB_ADDR_XCVR_PSM_A4OUT_TMR);
+	writel(0x0002, regs + TB_ADDR_XCVR_PSM_A5OUT_TMR);
+
+	/* Change rx detect parameter */
+	writel(0x960, regs + TB_ADDR_TX_RCVDET_EN_TMR);
+	writel(0x01e0, regs + TB_ADDR_TX_RCVDET_ST_TMR);
+	writel(0x0090, regs + TB_ADDR_XCVR_DIAG_LANE_FCM_EN_MGN_TMR);
+
+	/* RXDET_IN_P3_32KHZ, Receiver detect slow clock enable */
+	value = readl(regs + TB_ADDR_TX_RCVDETSC_CTRL);
+	value |= RXDET_IN_P3_32KHZ;
+	writel(value, regs + TB_ADDR_TX_RCVDETSC_CTRL);
+
+	udelay(10);
+
+	pr_debug("end of %s\n", __func__);
+}
+#endif
+static void cdns_imx_plat_init(struct cdns3_imx *imx)
+{
+	u32 value;
+	int timeout_us = 100000;
+
+	/* Wait clk value */
+	value = readl(imx->regs + USB3_SSPHY_STATUS);
+	writel(value, imx->regs + USB3_SSPHY_STATUS);
+	udelay(1);
+	value = readl(imx->regs + USB3_SSPHY_STATUS);
+	while ((value & 0xf0000000) != 0xf0000000 && timeout_us-- > 0) {
+		value = readl(imx->regs + USB3_SSPHY_STATUS);
+		dev_dbg(imx->dev, "clkvld:0x%x\n", value);
+		udelay(1);
+	}
+
+	if (timeout_us <= 0)
+		dev_err(imx->dev, "wait clkvld timeout\n");
+
+	/* Set all Reset bits */
+	value = readl(imx->regs + USB3_CORE_CTRL1);
+	value |= ALL_SW_RESET;
+	writel(value, imx->regs + USB3_CORE_CTRL1);
+	udelay(1);
+
+	value = readl(imx->regs + USB3_CORE_CTRL1);
+	value &= ~PHYAHB_SW_RESET;
+	writel(value, imx->regs + USB3_CORE_CTRL1);
+
+	cdns3_usb_phy_init(imx->phy_regs);
+
+	value = readl(imx->regs + USB3_CORE_CTRL1);
+	value &= ~ALL_SW_RESET;
+	writel(value, imx->regs + USB3_CORE_CTRL1);
+
+#if 0
+	if (role == USB_DR_MODE_HOST) {
+		value = readl(imx->regs + USB3_CORE_CTRL1);
+		value = (value & ~MODE_STRAP_MASK) | HOST_MODE | OC_DISABLE;
+		writel(value, imx->regs + USB3_CORE_CTRL1);
+		value &= ~PHYAHB_SW_RESET;
+		writel(value, imx->regs + USB3_CORE_CTRL1);
+		mdelay(1);
+		/* TODO check the sequence */
+		cdns3_usb_phy_init(imx->phy_regs);
+		/* Force B Session Valid as 1 */
+		writel(0x0060, imx->phy_regs + 0x380a4);
+		mdelay(1);
+
+		value = readl(imx->regs + USB3_INT_REG);
+		value |= HOST_INT1_EN;
+		writel(value, imx->regs + USB3_INT_REG);
+
+		value = readl(imx->regs + USB3_CORE_CTRL1);
+		value &= ~ALL_SW_RESET;
+		writel(value, imx->regs + USB3_CORE_CTRL1);
+
+		dev_dbg(imx->dev, "wait xhci_power_on_ready\n");
+
+		value = readl(imx->regs + USB3_CORE_STATUS);
+		timeout_us = 100000;
+		while (!(value & HOST_POWER_ON_READY) && timeout_us-- > 0) {
+			value = readl(imx->regs + USB3_CORE_STATUS);
+			udelay(1);
+		}
+
+		if (timeout_us <= 0)
+			dev_err(imx->dev, "wait xhci_power_on_ready timeout\n");
+
+		/*
+		value = readl(xhci_regs + XECP_PORT_CAP_REG);
+		value |= LPM_2_STB_SWITCH_EN;
+		writel(value, xhci_regs + XECP_PORT_CAP_REG);
+		*/
+
+		mdelay(1);
+
+		dev_dbg(imx->dev, "switch to host role successfully\n");
+	} else if (role == USB_DR_MODE_PERIPHERAL) {
+		value = readl(imx->regs + USB3_CORE_CTRL1);
+	//	value = (value & ~MODE_STRAP_MASK) | DEV_MODE;
+	//	writel(value, imx->regs + USB3_CORE_CTRL1);
+		value &= ~PHYAHB_SW_RESET;
+		writel(value, imx->regs + USB3_CORE_CTRL1);
+
+		cdns3_usb_phy_init(imx->phy_regs);
+		/* Force B Session Valid as 1 */
+//		writel(0x0060, imx->phy_regs + 0x380a4);
+		value = readl(imx->regs + USB3_INT_REG);
+		value |= DEV_INT_EN;
+		writel(value, imx->regs + USB3_INT_REG);
+
+		value = readl(imx->regs + USB3_CORE_CTRL1);
+		value &= ~ALL_SW_RESET;
+		writel(value, imx->regs + USB3_CORE_CTRL1);
+
+		dev_dbg(imx->dev, "wait gadget_power_on_ready\n");
+
+		value = readl(imx->regs + USB3_CORE_STATUS);
+		timeout_us = 100000;
+		while (!(value & DEV_POWER_ON_READY) && timeout_us-- > 0) {
+			value = readl(imx->regs + USB3_CORE_STATUS);
+			udelay(1);
+		}
+
+		if (timeout_us <= 0)
+			dev_err(imx->dev,
+				"wait gadget_power_on_ready timeout\n");
+
+		mdelay(1);
+
+		dev_dbg(imx->dev, "switch to gadget role successfully\n");
+	}
+#endif
+}
+
+static int cdns3_imx_set_mode(struct cdns3 *cdns)
+{
+	struct device *dev = cdns->dev->parent;
+	struct cdns3_imx *imx = dev_get_drvdata(dev);
+	enum cdns3_roles role = cdns->role;
+	u32 value;
+	int timeout_us = 100000;
+
+	switch (role) {
+	case CDNS3_ROLE_GADGET:
+		dev_info(dev, "Set controller to Gadget mode\n");
+		value = readl(imx->regs + USB3_CORE_CTRL1);
+		value = (value & ~MODE_STRAP_MASK) | DEV_MODE;
+		writel(value, imx->regs + USB3_CORE_CTRL1);
+
+		value = readl(imx->regs + USB3_INT_REG);
+		value |= DEV_INT_EN;
+		writel(value, imx->regs + USB3_INT_REG);
+
+		dev_dbg(dev, "wait gadget_power_on_ready\n");
+
+		value = readl(imx->regs + USB3_CORE_STATUS);
+		timeout_us = 100000;
+		while (!(value & DEV_POWER_ON_READY) && timeout_us-- > 0) {
+			value = readl(imx->regs + USB3_CORE_STATUS);
+			udelay(1);
+		}
+
+		if (timeout_us <= 0)
+			dev_err(dev, "wait gadget_power_on_ready timeout\n");
+		break;
+	case CDNS3_ROLE_HOST:
+		dev_info(dev, "Set controller to Host mode\n");
+		value = readl(imx->regs + USB3_CORE_CTRL1);
+		value = (value & ~MODE_STRAP_MASK) | HOST_MODE | OC_DISABLE;
+		writel(value, imx->regs + USB3_CORE_CTRL1);
+
+		value = readl(imx->regs + USB3_INT_REG);
+		value |= HOST_INT1_EN;
+		writel(value, imx->regs + USB3_INT_REG);
+
+		dev_dbg(dev, "wait xhci_power_on_ready\n");
+
+		value = readl(imx->regs + USB3_CORE_STATUS);
+		timeout_us = 100000;
+		while (!(value & HOST_POWER_ON_READY) && timeout_us-- > 0) {
+			value = readl(imx->regs + USB3_CORE_STATUS);
+			udelay(1);
+		}
+
+		if (timeout_us <= 0)
+			dev_err(dev, "wait xhci_power_on_ready timeout\n");
+		break;
+	case CDNS3_ROLE_END:
+		dev_info(dev, "reset controller\n");
+		cdns_imx_plat_init(imx);
+		break;
+	}
+
+	return 0;
+}
+
+static int imx_get_clks(struct device *dev)
+{
+	struct cdns3_imx *imx = dev_get_drvdata(dev);
+	int ret = 0;
+
+	imx->imx_clks[0] = devm_clk_get(dev, "usb3_lpm_clk");
+	if (IS_ERR(imx->imx_clks[0])) {
+		ret = PTR_ERR(imx->imx_clks[0]);
+		dev_err(dev, "Failed to get usb3_lpm_clk, err=%d\n", ret);
+		return ret;
+	}
+
+	imx->imx_clks[1] = devm_clk_get(dev, "usb3_bus_clk");
+	if (IS_ERR(imx->imx_clks[1])) {
+		ret = PTR_ERR(imx->imx_clks[1]);
+		dev_err(dev, "Failed to get usb3_bus_clk, err=%d\n", ret);
+		return ret;
+	}
+
+	imx->imx_clks[2] = devm_clk_get(dev, "usb3_aclk");
+	if (IS_ERR(imx->imx_clks[2])) {
+		ret = PTR_ERR(imx->imx_clks[2]);
+		dev_err(dev, "Failed to get usb3_aclk, err=%d\n", ret);
+		return ret;
+	}
+
+	imx->imx_clks[3] = devm_clk_get(dev, "usb3_ipg_clk");
+	if (IS_ERR(imx->imx_clks[3])) {
+		ret = PTR_ERR(imx->imx_clks[3]);
+		dev_err(dev, "Failed to get usb3_ipg_clk, err=%d\n", ret);
+		return ret;
+	}
+
+	imx->imx_clks[4] = devm_clk_get(dev, "usb3_core_pclk");
+	if (IS_ERR(imx->imx_clks[4])) {
+		ret = PTR_ERR(imx->imx_clks[4]);
+		dev_err(dev, "Failed to get usb3_core_pclk, err=%d\n", ret);
+		return ret;
+	}
+
+	return 0;
+}
+
+static int imx_prepare_enable_clks(struct device *dev)
+{
+	struct cdns3_imx *imx = dev_get_drvdata(dev);
+	int i, j, ret = 0;
+
+	for (i = 0; i < CDNS3_IMX_CLKS_NUM; i++) {
+		ret = clk_prepare_enable(imx->imx_clks[i]);
+		if (ret) {
+			dev_err(dev,
+				"Failed to prepare/enable cdns3 clk, err=%d\n",
+				ret);
+			goto err;
+		}
+	}
+
+	return ret;
+err:
+	for (j = i; j > 0; j--)
+		clk_disable_unprepare(imx->imx_clks[j - 1]);
+
+	return ret;
+}
+
+static void imx_disable_unprepare_clks(struct device *dev)
+{
+	struct cdns3_imx *imx = dev_get_drvdata(dev);
+	int i;
+
+	for (i = CDNS3_IMX_CLKS_NUM - 1; i >= 0; i--)
+		clk_disable_unprepare(imx->imx_clks[i]);
+}
+
+static int cdns3_imx_assert(struct reset_controller_dev *r, unsigned long id)
+{
+	struct cdns3_imx *imx = container_of(r, struct cdns3_imx, rcdev);
+	u32 value;
+
+	value = readl(imx->regs + USB3_CORE_CTRL1);
+	if (id == 0)
+		value &= ~PHY_SW_RESET;
+	else if (id == 1)
+		value &= ~PHYAHB_SW_RESET;
+
+	writel(value, imx->regs + USB3_CORE_CTRL1);
+	return 0;
+}
+
+static int cdns3_imx_deassert(struct reset_controller_dev *r, unsigned long id)
+{
+	struct cdns3_imx *imx = container_of(r, struct cdns3_imx, rcdev);
+	u32 value;
+
+	value = readl(imx->regs + USB3_CORE_CTRL1);
+	if (id == 0)
+		value &= ~PHY_SW_RESET;
+	else if (id == 1)
+		value &= ~PHYAHB_SW_RESET;
+
+	writel(value, imx->regs + USB3_CORE_CTRL1);
+	return 0;
+}
+
+static const struct reset_control_ops cdns3_imx_reset_ops = {
+	.assert = cdns3_imx_assert,
+	.deassert = cdns3_imx_deassert,
+};
+
+/**
+ * cdns3_nxp_probe - probe for imx glue driver
+ * @pdev: Pointer to imx glue platform device
+ *
+ * Returns 0 on success otherwise negative errno
+ */
+static int cdns3_imx_probe(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	struct device_node *node = dev->of_node;
+	struct resource	*res;
+	struct cdns3_imx *imx;
+	void __iomem *regs;
+	int ret;
+	struct of_dev_auxdata imx_auxdata = {
+		.compatible = "cdns,usb3-1.0.0",
+		.platform_data = &cdns3_imx_pdata,
+	};
+
+	dev_dbg(dev, "%s start, pdata=0x%p\n", __func__, &cdns3_imx_pdata);
+	if (!node) {
+		dev_err(dev, "device node not found\n");
+		return -EINVAL;
+	}
+
+	imx = devm_kzalloc(dev, sizeof(*imx), GFP_KERNEL);
+	if (!imx)
+		return -ENOMEM;
+
+	imx->dev = dev;
+	platform_set_drvdata(pdev, imx);
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	regs = devm_ioremap_resource(dev, res);
+	if (IS_ERR(regs))
+		return PTR_ERR(regs);
+
+	imx->regs = regs;
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+	regs = devm_ioremap_resource(dev, res);
+	if (IS_ERR(regs))
+		return PTR_ERR(regs);
+
+	imx->phy_regs = regs;
+
+	ret = imx_get_clks(dev);
+	if (ret)
+		return ret;
+
+	ret = imx_prepare_enable_clks(dev);
+	if (ret)
+		return ret;
+
+//	imx->role = usb_get_dr_mode(dev);
+//	dev_dbg(dev, "the init role is %d\n", imx->role);
+	cdns_imx_plat_init(imx);
+
+	imx->rcdev.owner = THIS_MODULE;
+	imx->rcdev.ops = &cdns3_imx_reset_ops;
+	imx->rcdev.of_node = dev->of_node;
+	imx->rcdev.nr_resets = 2;
+	ret = reset_controller_register(&imx->rcdev);
+	if (ret)
+		goto err1;
+
+	ret = of_platform_populate(node, NULL, &imx_auxdata, dev);
+	if (ret) {
+		dev_err(dev, "failed to create cnds3 core\n");
+		goto err2;
+	}
+
+	dev_dbg(dev, "Cadence USB3 core: probe succeed\n");
+
+	return 0;
+err2:
+	reset_controller_unregister(&imx->rcdev);
+err1:
+	imx_disable_unprepare_clks(dev);
+	return ret;
+}
+
+/**
+ * cdns3_imx_remove - remove glue imx driver
+ * @pdev: Pointer to Linux platform device
+ *
+ * Returns 0 on success otherwise negative errno
+ */
+static int cdns3_imx_remove(struct platform_device *pdev)
+{
+	imx_disable_unprepare_clks(&pdev->dev);
+	return 0;
+}
+
+#ifdef CONFIG_OF
+static const struct of_device_id of_cdns3_imx_match[] = {
+	{ .compatible = "nxp,cdns3" },
+	{ },
+};
+MODULE_DEVICE_TABLE(of, of_cdns3_match);
+#endif
+
+static struct platform_driver cdns3_imx_driver = {
+	.probe		= cdns3_imx_probe,
+	.remove		= cdns3_imx_remove,
+	.driver		= {
+		.name	= "nxp-cdns3",
+		.of_match_table	= of_match_ptr(of_cdns3_imx_match),
+	},
+};
+
+static int __init cdns3_imx_register(void)
+{
+	return platform_driver_register(&cdns3_imx_driver);
+}
+module_init(cdns3_imx_register);
+
+static void __exit cdns3_imx_unregister(void)
+{
+	platform_driver_unregister(&cdns3_imx_driver);
+}
+module_exit(cdns3_imx_unregister);
+
+MODULE_ALIAS("platform:cdns3-nxp");
+MODULE_AUTHOR("Peter Chen <peter.chen@nxp.com>");
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("Cadence USB3 imx glue driver");
diff --git a/drivers/usb/cdns3/cdns3-nxp-reg-def.h b/drivers/usb/cdns3/cdns3-nxp-reg-def.h
new file mode 100644
index 000000000000..4ac73005c023
--- /dev/null
+++ b/drivers/usb/cdns3/cdns3-nxp-reg-def.h
@@ -0,0 +1,174 @@
+/**
+ * cdns3-nxp-reg-def.h - nxp wrap layer register definition
+ *
+ * Copyright 2017 NXP
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2  of
+ * the License as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __DRIVERS_USB_CDNS3_NXP_H
+#define __DRIVERS_USB_CDNS3_NXP_H
+
+#define USB3_CORE_CTRL1    0x00
+#define USB3_CORE_CTRL2    0x04
+#define USB3_INT_REG       0x08
+#define USB3_CORE_STATUS   0x0c
+#define XHCI_DEBUG_LINK_ST 0x10
+#define XHCI_DEBUG_BUS     0x14
+#define USB3_SSPHY_CTRL1   0x40
+#define USB3_SSPHY_CTRL2   0x44
+#define USB3_SSPHY_STATUS  0x4c
+#define USB2_PHY_CTRL1     0x50
+#define USB2_PHY_CTRL2     0x54
+#define USB2_PHY_STATUS    0x5c
+
+/* Register bits definition */
+
+/* USB3_CORE_CTRL1 */
+#define SW_RESET_MASK	(0x3f << 26)
+#define PWR_SW_RESET	(1 << 31)
+#define APB_SW_RESET	(1 << 30)
+#define AXI_SW_RESET	(1 << 29)
+#define RW_SW_RESET	(1 << 28)
+#define PHY_SW_RESET	(1 << 27)
+#define PHYAHB_SW_RESET	(1 << 26)
+#define ALL_SW_RESET	(PWR_SW_RESET | APB_SW_RESET | AXI_SW_RESET | \
+		RW_SW_RESET | PHY_SW_RESET | PHYAHB_SW_RESET)
+#define OC_DISABLE	(1 << 9)
+#define MDCTRL_CLK_SEL	(1 << 7)
+#define MODE_STRAP_MASK	(0x7)
+#define DEV_MODE	(1 << 2)
+#define HOST_MODE	(1 << 1)
+#define OTG_MODE	(1 << 0)
+
+/* USB3_INT_REG */
+#define CLK_125_REQ	(1 << 29)
+#define LPM_CLK_REQ	(1 << 28)
+#define DEVU3_WAEKUP_EN	(1 << 14)
+#define OTG_WAKEUP_EN	(1 << 12)
+#define DEV_INT_EN (3 << 8) /* DEV INT b9:8 */
+#define HOST_INT1_EN (1 << 0) /* HOST INT b7:0 */
+
+/* USB3_CORE_STATUS */
+#define MDCTRL_CLK_STATUS	(1 << 15)
+#define DEV_POWER_ON_READY	(1 << 13)
+#define HOST_POWER_ON_READY	(1 << 12)
+
+/* USB3_SSPHY_STATUS */
+#define PHY_REFCLK_REQ		(1 << 0)
+
+
+/* PHY register definition */
+#define PHY_PMA_CMN_CTRL1			(0xC800 * 4)
+#define TB_ADDR_CMN_DIAG_HSCLK_SEL		(0x01e0 * 4)
+#define TB_ADDR_CMN_PLL0_VCOCAL_INIT_TMR	(0x0084 * 4)
+#define TB_ADDR_CMN_PLL0_VCOCAL_ITER_TMR	(0x0085 * 4)
+#define TB_ADDR_CMN_PLL0_INTDIV	                (0x0094 * 4)
+#define TB_ADDR_CMN_PLL0_FRACDIV		(0x0095 * 4)
+#define TB_ADDR_CMN_PLL0_HIGH_THR		(0x0096 * 4)
+#define TB_ADDR_CMN_PLL0_SS_CTRL1		(0x0098 * 4)
+#define TB_ADDR_CMN_PLL0_SS_CTRL2		(0x0099 * 4)
+#define TB_ADDR_CMN_PLL0_DSM_DIAG		(0x0097 * 4)
+#define TB_ADDR_CMN_DIAG_PLL0_OVRD		(0x01c2 * 4)
+#define TB_ADDR_CMN_DIAG_PLL0_FBH_OVRD		(0x01c0 * 4)
+#define TB_ADDR_CMN_DIAG_PLL0_FBL_OVRD		(0x01c1 * 4)
+#define TB_ADDR_CMN_DIAG_PLL0_V2I_TUNE          (0x01C5 * 4)
+#define TB_ADDR_CMN_DIAG_PLL0_CP_TUNE           (0x01C6 * 4)
+#define TB_ADDR_CMN_DIAG_PLL0_LF_PROG           (0x01C7 * 4)
+#define TB_ADDR_CMN_DIAG_PLL0_TEST_MODE		(0x01c4 * 4)
+#define TB_ADDR_CMN_PSM_CLK_CTRL		(0x0061 * 4)
+#define TB_ADDR_XCVR_DIAG_RX_LANE_CAL_RST_TMR	(0x40ea * 4)
+#define TB_ADDR_XCVR_PSM_RCTRL	                (0x4001 * 4)
+#define TB_ADDR_TX_PSC_A0		        (0x4100 * 4)
+#define TB_ADDR_TX_PSC_A1		        (0x4101 * 4)
+#define TB_ADDR_TX_PSC_A2		        (0x4102 * 4)
+#define TB_ADDR_TX_PSC_A3		        (0x4103 * 4)
+#define TB_ADDR_TX_DIAG_ECTRL_OVRD		(0x41f5 * 4)
+#define TB_ADDR_TX_PSC_CAL		        (0x4106 * 4)
+#define TB_ADDR_TX_PSC_RDY		        (0x4107 * 4)
+#define TB_ADDR_RX_PSC_A0	                (0x8000 * 4)
+#define TB_ADDR_RX_PSC_A1	                (0x8001 * 4)
+#define TB_ADDR_RX_PSC_A2	                (0x8002 * 4)
+#define TB_ADDR_RX_PSC_A3	                (0x8003 * 4)
+#define TB_ADDR_RX_PSC_CAL	                (0x8006 * 4)
+#define TB_ADDR_RX_PSC_RDY	                (0x8007 * 4)
+#define TB_ADDR_TX_TXCC_MGNLS_MULT_000		(0x4058 * 4)
+#define TB_ADDR_TX_DIAG_BGREF_PREDRV_DELAY	(0x41e7 * 4)
+#define TB_ADDR_RX_SLC_CU_ITER_TMR		(0x80e3 * 4)
+#define TB_ADDR_RX_SIGDET_HL_FILT_TMR		(0x8090 * 4)
+#define TB_ADDR_RX_SAMP_DAC_CTRL		(0x8058 * 4)
+#define TB_ADDR_RX_DIAG_SIGDET_TUNE		(0x81dc * 4)
+#define TB_ADDR_RX_DIAG_LFPSDET_TUNE2		(0x81df * 4)
+#define TB_ADDR_RX_DIAG_BS_TM	                (0x81f5 * 4)
+#define TB_ADDR_RX_DIAG_DFE_CTRL1		(0x81d3 * 4)
+#define TB_ADDR_RX_DIAG_ILL_IQE_TRIM4		(0x81c7 * 4)
+#define TB_ADDR_RX_DIAG_ILL_E_TRIM0		(0x81c2 * 4)
+#define TB_ADDR_RX_DIAG_ILL_IQ_TRIM0		(0x81c1 * 4)
+#define TB_ADDR_RX_DIAG_ILL_IQE_TRIM6		(0x81c9 * 4)
+#define TB_ADDR_RX_DIAG_RXFE_TM3		(0x81f8 * 4)
+#define TB_ADDR_RX_DIAG_RXFE_TM4		(0x81f9 * 4)
+#define TB_ADDR_RX_DIAG_LFPSDET_TUNE		(0x81dd * 4)
+#define TB_ADDR_RX_DIAG_DFE_CTRL3		(0x81d5 * 4)
+#define TB_ADDR_RX_DIAG_SC2C_DELAY		(0x81e1 * 4)
+#define TB_ADDR_RX_REE_VGA_GAIN_NODFE		(0x81bf * 4)
+#define TB_ADDR_XCVR_PSM_CAL_TMR		(0x4002 * 4)
+#define TB_ADDR_XCVR_PSM_A0BYP_TMR		(0x4004 * 4)
+#define TB_ADDR_XCVR_PSM_A0IN_TMR		(0x4003 * 4)
+#define TB_ADDR_XCVR_PSM_A1IN_TMR		(0x4005 * 4)
+#define TB_ADDR_XCVR_PSM_A2IN_TMR		(0x4006 * 4)
+#define TB_ADDR_XCVR_PSM_A3IN_TMR		(0x4007 * 4)
+#define TB_ADDR_XCVR_PSM_A4IN_TMR		(0x4008 * 4)
+#define TB_ADDR_XCVR_PSM_A5IN_TMR		(0x4009 * 4)
+#define TB_ADDR_XCVR_PSM_A0OUT_TMR		(0x400a * 4)
+#define TB_ADDR_XCVR_PSM_A1OUT_TMR		(0x400b * 4)
+#define TB_ADDR_XCVR_PSM_A2OUT_TMR		(0x400c * 4)
+#define TB_ADDR_XCVR_PSM_A3OUT_TMR		(0x400d * 4)
+#define TB_ADDR_XCVR_PSM_A4OUT_TMR		(0x400e * 4)
+#define TB_ADDR_XCVR_PSM_A5OUT_TMR		(0x400f * 4)
+#define TB_ADDR_TX_RCVDET_EN_TMR	        (0x4122 * 4)
+#define TB_ADDR_TX_RCVDET_ST_TMR	        (0x4123 * 4)
+#define TB_ADDR_XCVR_DIAG_LANE_FCM_EN_MGN_TMR	(0x40f2 * 4)
+#define TB_ADDR_TX_RCVDETSC_CTRL	        (0x4124 * 4)
+
+/* Register bits definition */
+
+/* TB_ADDR_TX_RCVDETSC_CTRL */
+#define RXDET_IN_P3_32KHZ			(1 << 0)
+
+/* OTG registers definition */
+#define OTGSTS		0x4
+#define OTGREFCLK	0xc
+
+/* Register bits definition */
+/* OTGSTS */
+#define OTG_NRDY	(1 << 11)
+/* OTGREFCLK */
+#define OTG_STB_CLK_SWITCH_EN	(1 << 31)
+
+/* xHCI registers definition  */
+#define XECP_PORT_CAP_REG	0x8000
+#define XECP_PM_PMCSR		0x8018
+#define XECP_AUX_CTRL_REG1	0x8120
+
+/* Register bits definition */
+/* XECP_PORT_CAP_REG */
+#define LPM_2_STB_SWITCH_EN	(1 << 25)
+
+/* XECP_AUX_CTRL_REG1 */
+#define CFG_RXDET_P3_EN		(1 << 15)
+
+/* XECP_PM_PMCSR */
+#define PS_MASK			(3 << 0)
+#define PS_D0			0
+#define PS_D1			(1 << 0)
+#endif /* __DRIVERS_USB_CDNS3_NXP_H */
diff --git a/drivers/usb/cdns3/core.c b/drivers/usb/cdns3/core.c
index d274586aca36..cadd249fbdaf 100644
--- a/drivers/usb/cdns3/core.c
+++ b/drivers/usb/cdns3/core.c
@@ -14,88 +14,13 @@
 #include <linux/platform_device.h>
 #include <linux/interrupt.h>
 #include <linux/io.h>
-#include <linux/pm_runtime.h>
 
 #include "gadget.h"
 #include "core.h"
+#include "drd.h"
 #include "host-export.h"
 #include "gadget-export.h"
-#include "drd.h"
-#include "debug.h"
-
-static inline
-struct cdns3_role_driver *cdns3_get_current_role_driver(struct cdns3 *cdns)
-{
-	WARN_ON(cdns->role >= CDNS3_ROLE_END || !cdns->roles[cdns->role]);
-	return cdns->roles[cdns->role];
-}
-
-static inline int cdns3_role_start(struct cdns3 *cdns, enum cdns3_roles role)
-{
-	int ret;
-
-	if (WARN_ON(role >= CDNS3_ROLE_END))
-		return 0;
-
-	if (!cdns->roles[role])
-		return -ENXIO;
-
-	if (cdns->roles[role]->state == CDNS3_ROLE_STATE_ACTIVE)
-		return 0;
-
-	mutex_lock(&cdns->mutex);
-	cdns->role = role;
-	ret = cdns->roles[role]->start(cdns);
-	if (!ret)
-		cdns->roles[role]->state = CDNS3_ROLE_STATE_ACTIVE;
-	mutex_unlock(&cdns->mutex);
-	return ret;
-}
-
-void cdns3_role_stop(struct cdns3 *cdns)
-{
-	enum cdns3_roles role = cdns->role;
-
-	if (role >= CDNS3_ROLE_END) {
-		WARN_ON(role > CDNS3_ROLE_END);
-		return;
-	}
-
-	if (cdns->roles[role]->state == CDNS3_ROLE_STATE_INACTIVE)
-		return;
-
-	mutex_lock(&cdns->mutex);
-	cdns->roles[role]->stop(cdns);
-	cdns->roles[role]->state = CDNS3_ROLE_STATE_INACTIVE;
-	mutex_unlock(&cdns->mutex);
-}
-
-/*
- * cdns->role gets from cdns3_get_initial_role, and this API tells role at the
- * runtime.
- * If both roles are supported, the role is selected based on vbus/id.
- * It could be read from OTG register or external connector.
- * If only single role is supported, only one role structure
- * is allocated, cdns->roles[CDNS3_ROLE_HOST] or cdns->roles[CDNS3_ROLE_GADGET].
- */
-static enum cdns3_roles cdns3_get_initial_role(struct cdns3 *cdns)
-{
-	if (cdns->roles[CDNS3_ROLE_HOST] && cdns->roles[CDNS3_ROLE_GADGET]) {
-		if (cdns3_is_host(cdns))
-			return CDNS3_ROLE_HOST;
-		if (cdns3_is_device(cdns))
-			return CDNS3_ROLE_GADGET;
-	}
-	return cdns->roles[CDNS3_ROLE_HOST]
-		? CDNS3_ROLE_HOST
-		: CDNS3_ROLE_GADGET;
-}
-
-static void cdns3_exit_roles(struct cdns3 *cdns)
-{
-	cdns3_role_stop(cdns);
-	cdns3_drd_exit(cdns);
-}
+//#include "debug.h"
 
 /**
  * cdns3_core_init_role - initialize role of operation
@@ -106,162 +31,37 @@ static void cdns3_exit_roles(struct cdns3 *cdns)
 static int cdns3_core_init_role(struct cdns3 *cdns)
 {
 	struct device *dev = cdns->dev;
-	enum usb_dr_mode best_dr_mode;
-	enum usb_dr_mode dr_mode;
+	enum usb_dr_mode dr_mode = cdns->dr_mode;
 	int ret = 0;
 
-	dr_mode = usb_get_dr_mode(dev);
-	cdns->role = CDNS3_ROLE_END;
-
-	/*
-	 * If driver can't read mode by means of usb_get_dr_mdoe function then
-	 * chooses mode according with Kernel configuration. This setting
-	 * can be restricted later depending on strap pin configuration.
-	 */
-	if (dr_mode == USB_DR_MODE_UNKNOWN) {
-		if (IS_ENABLED(CONFIG_USB_CDNS3_HOST) &&
-		    IS_ENABLED(CONFIG_USB_CDNS3_GADGET))
-			dr_mode = USB_DR_MODE_OTG;
-		else if (IS_ENABLED(CONFIG_USB_CDNS3_HOST))
-			dr_mode = USB_DR_MODE_HOST;
-		else if (IS_ENABLED(CONFIG_USB_CDNS3_GADGET))
-			dr_mode = USB_DR_MODE_PERIPHERAL;
-	}
-
-	best_dr_mode = USB_DR_MODE_OTG;
-
-	if (dr_mode == USB_DR_MODE_OTG) {
-		best_dr_mode = cdns->dr_mode;
-	} else if (cdns->dr_mode == USB_DR_MODE_OTG) {
-		best_dr_mode = dr_mode;
-	} else if (cdns->dr_mode != dr_mode) {
-		dev_err(dev, "Incorrect DRD configuration\n");
-		return -EINVAL;
-	}
-
-	dr_mode = best_dr_mode;
-
 	if (dr_mode == USB_DR_MODE_OTG || dr_mode == USB_DR_MODE_HOST) {
 		ret = cdns3_host_init(cdns);
 		if (ret) {
-			dev_err(dev, "Host initialization failed with %d\n",
-				ret);
-			goto err;
+			if (ret == -ENXIO)
+				dev_dbg(dev, "doesn't support host\n");
+			else
+				return ret;
 		}
 	}
 
 	if (dr_mode == USB_DR_MODE_OTG || dr_mode == USB_DR_MODE_PERIPHERAL) {
 		ret = cdns3_gadget_init(cdns);
 		if (ret) {
-			dev_err(dev, "Device initialization failed with %d\n",
-				ret);
-			goto err;
+			if (ret == -ENXIO)
+				dev_dbg(dev, "doesn't support gadget\n");
+			else
+				return ret;
 		}
 	}
 
-	cdns->desired_dr_mode = dr_mode;
-	cdns->dr_mode = dr_mode;
-	/*
-	 * dr_mode could be change so DRD must update controller
-	 * configuration
-	 */
-	ret = cdns3_drd_update_mode(cdns);
-
-	cdns->role = cdns3_get_initial_role(cdns);
-
-	ret = cdns3_role_start(cdns, cdns->role);
-	if (ret) {
-		dev_err(dev, "can't start %s role\n",
-			cdns3_get_current_role_driver(cdns)->name);
-		goto err;
+	if (!cdns->roles[CDNS3_ROLE_HOST] && !cdns->roles[CDNS3_ROLE_GADGET]) {
+		dev_err(dev, "no supported roles\n");
+		return -ENODEV;
 	}
 
-	return ret;
-err:
-	cdns3_exit_roles(cdns);
 	return ret;
 }
 
-/**
- * cdsn3_get_real_role - get real role of controller based on hardware settings.
- * @cdns: Pointer to cdns3 structure
- *
- * Returns role
- */
-enum cdns3_roles cdsn3_get_real_role(struct cdns3 *cdns)
-{
-	enum cdns3_roles role = CDNS3_ROLE_END;
-
-	if (cdns->current_dr_mode == USB_DR_MODE_OTG) {
-		if (cdns3_get_id(cdns))
-			role = CDNS3_ROLE_GADGET;
-		else
-			role = CDNS3_ROLE_HOST;
-	} else {
-		if (cdns3_is_host(cdns))
-			role = CDNS3_ROLE_HOST;
-		if (cdns3_is_device(cdns))
-			role = CDNS3_ROLE_GADGET;
-	}
-
-	return role;
-}
-
-/**
- * cdns3_role_switch - work queue handler for role switch
- *
- * @work: work queue item structure
- *
- * Handles below events:
- * - Role switch for dual-role devices
- * - CDNS3_ROLE_GADGET <--> CDNS3_ROLE_END for peripheral-only devices
- */
-static void cdns3_role_switch(struct work_struct *work)
-{
-	enum cdns3_roles role = CDNS3_ROLE_END;
-	struct cdns3_role_driver *role_drv;
-	enum cdns3_roles current_role;
-	struct cdns3 *cdns;
-	int ret = 0;
-
-	cdns = container_of(work, struct cdns3, role_switch_wq);
-
-	/* During switching cdns->role can be different then role */
-	role = cdsn3_get_real_role(cdns);
-
-	role_drv = cdns3_get_current_role_driver(cdns);
-
-	pm_runtime_get_sync(cdns->dev);
-
-	/* Disable current role. This state can be forced from user space. */
-	if (cdns->debug_disable && role_drv->state == CDNS3_ROLE_STATE_ACTIVE) {
-		cdns3_role_stop(cdns);
-		goto exit;
-	}
-
-	/* Do nothing if nothing changed */
-	if (cdns->role == role && role_drv->state == CDNS3_ROLE_STATE_ACTIVE)
-		goto exit;
-
-	cdns3_role_stop(cdns);
-
-	role = cdsn3_get_real_role(cdns);
-
-	current_role = cdns->role;
-	dev_dbg(cdns->dev, "Switching role");
-
-	ret = cdns3_role_start(cdns, role);
-
-	if (ret) {
-		/* Back to current role */
-		dev_err(cdns->dev, "set %d has failed, back to %d\n",
-			role, current_role);
-		cdns3_role_start(cdns, current_role);
-	}
-exit:
-	pm_runtime_put_sync(cdns->dev);
-}
-
 /**
  * cdns3_probe - probe for cdns3 core device
  * @pdev: Pointer to cdns3 core platform device
@@ -276,12 +76,13 @@ static int cdns3_probe(struct platform_device *pdev)
 	void __iomem *regs;
 	int ret;
 
+	dev_dbg(dev, "%s, start\n", __func__);
 	cdns = devm_kzalloc(dev, sizeof(*cdns), GFP_KERNEL);
 	if (!cdns)
 		return -ENOMEM;
 
 	cdns->dev = dev;
-
+	cdns->pdata = dev_get_platdata(dev);
 	platform_set_drvdata(pdev, cdns);
 
 	res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
@@ -326,11 +127,11 @@ static int cdns3_probe(struct platform_device *pdev)
 		}
 	}
 
-	phy_init(cdns->phy);
-
-	INIT_WORK(&cdns->role_switch_wq, cdns3_role_switch);
+	ret = phy_init(cdns->phy);
+	if (ret)
+		goto err0;
 
-	ret = cdns3_drd_init(cdns);
+	ret = cdns3_finalize_dr_mode(cdns);
 	if (ret)
 		goto err1;
 
@@ -338,20 +139,21 @@ static int cdns3_probe(struct platform_device *pdev)
 	if (ret)
 		goto err1;
 
-	cdns3_debugfs_init(cdns);
-	device_set_wakeup_capable(dev, true);
-	pm_runtime_set_active(dev);
-	pm_runtime_enable(dev);
+	if (cdns->roles[CDNS3_ROLE_GADGET]) {
+		ret = cdns3_drd_init(cdns);
+		if (ret) {
+			dev_err(dev, "init otg fails, ret = %d\n", ret);
+			goto err1;
+		}
+	}
 
-	/*
-	 * The controller needs less time between bus and controller suspend,
-	 * and we also needs a small delay to avoid frequently entering low
-	 * power mode.
-	 */
-	pm_runtime_set_autosuspend_delay(dev, 20);
-	pm_runtime_mark_last_busy(dev);
-	pm_runtime_use_autosuspend(dev);
-	dev_dbg(dev, "Cadence USB3 core: probe succeed\n");
+	cdns->role = cdns3_get_role(cdns);
+	ret = cdns3_role_start(cdns, cdns->role);
+	if (ret) {
+		dev_err(dev, "can't start %s role\n",
+			cdns3_get_current_role_driver(cdns)->name);
+		goto err1;
+	}
 
 	return 0;
 
@@ -371,11 +173,7 @@ static int cdns3_remove(struct platform_device *pdev)
 {
 	struct cdns3 *cdns = platform_get_drvdata(pdev);
 
-	pm_runtime_get_sync(&pdev->dev);
-	pm_runtime_disable(&pdev->dev);
-	pm_runtime_put_noidle(&pdev->dev);
-	cdns3_debugfs_exit(cdns);
-	cdns3_exit_roles(cdns);
+	cdns3_role_stop(cdns);
 	phy_exit(cdns->phy);
 	return 0;
 }
diff --git a/drivers/usb/cdns3/core.h b/drivers/usb/cdns3/core.h
index fb4b39206158..a91342b1a868 100644
--- a/drivers/usb/cdns3/core.h
+++ b/drivers/usb/cdns3/core.h
@@ -41,6 +41,10 @@ struct cdns3_role_driver {
 	int state;
 };
 
+struct cdns3_platform_data {
+	int (*set_mode) (struct cdns3 *cdns);
+};
+
 #define CDNS3_XHCI_RESOURCES_NUM	2
 /**
  * struct cdns3 - Representation of Cadence USB3 DRD controller.
@@ -56,28 +60,15 @@ struct cdns3_role_driver {
  * @gadget_dev: the child gadget device pointer for cdns3 core
  * @usb: phy for this controller
  * @role_switch_wq: work queue item for role switch
- * @in_lpm: the controller in low power mode
- * @wakeup_int: the wakeup interrupt
  * @mutex: the mutex for concurrent code at driver
  * @dr_mode: supported mode of operation it can be only Host, only Device
  *           or OTG mode that allow to switch between Device and Host mode.
  *           This field based on firmware setting, kernel configuration
  *           and hardware configuration.
- * @current_dr_mode: current mode of operation when in dual-role mode
- * @desired_dr_mode: desired mode of operation when in dual-role mode.
- *           This value can be changed during runtime.
- *           Available options depends on  dr_mode:
- *           dr_mode                 |  desired_dr_mode and current_dr_mode
- *           ----------------------------------------------------------------
- *           USB_DR_MODE_HOST        | only USB_DR_MODE_HOST
- *           USB_DR_MODE_PERIPHERAL  | only USB_DR_MODE_PERIPHERAL
- *           USB_DR_MODE_OTG         | only USB_DR_MODE_HOST
- *           USB_DR_MODE_OTG         | only USB_DR_MODE_PERIPHERAL
- *           USB_DR_MODE_OTG         | USB_DR_MODE_OTG
- *
- *           Desired_dr_role can be changed by means of debugfs.
  * @root: debugfs root folder pointer
  * @debug_disable:
+ * @extcon: Extern connector
+ * @extcon_nb: notifier block for Type-C extern connector
  */
 struct cdns3 {
 	struct device			*dev;
@@ -100,17 +91,21 @@ struct cdns3 {
 	struct cdns3_device		*gadget_dev;
 	struct phy			*phy;
 	struct work_struct		role_switch_wq;
-	int				in_lpm:1;
-	int				wakeup_int:1;
 	/* mutext used in workqueue*/
 	struct mutex			mutex;
 	enum usb_dr_mode		dr_mode;
-	enum usb_dr_mode		current_dr_mode;
-	enum usb_dr_mode		desired_dr_mode;
 	struct dentry			*root;
 	int				debug_disable:1;
+	struct extcon_dev 		*extcon;
+	struct notifier_block 		extcon_nb;
+	struct cdns3_platform_data 	*pdata;
 };
 
-void cdns3_role_stop(struct cdns3 *cdns);
+static inline struct cdns3_role_driver *cdns3_get_current_role_driver(
+	struct cdns3 *cdns)
+{
+	WARN_ON(cdns->role >= CDNS3_ROLE_END || !cdns->roles[cdns->role]);
+	return cdns->roles[cdns->role];
+}
 
 #endif /* __LINUX_CDNS3_CORE_H */
diff --git a/drivers/usb/cdns3/drd.c b/drivers/usb/cdns3/drd.c
index b0c32302eb0b..56d2b4ca15f8 100644
--- a/drivers/usb/cdns3/drd.c
+++ b/drivers/usb/cdns3/drd.c
@@ -4,95 +4,55 @@
  *
  * Copyright (C) 2018 Cadence.
  *
- * Author: Pawel Laszczak <pawell@cadence.com
+ * Author: Pawel Laszczak <pawell@cadence.com>
  *
  */
 #include <linux/kernel.h>
 #include <linux/interrupt.h>
 #include <linux/delay.h>
 #include <linux/usb/otg.h>
+#include <linux/extcon.h>
 
 #include "gadget.h"
-#include "drd.h"
 #include "core.h"
+#include "drd.h"
 
-static int cdns3_drd_switch_gadget(struct cdns3 *cdns, int on);
-static int cdns3_drd_switch_host(struct cdns3 *cdns, int on);
-
-/**
- * cdns3_set_mode - change mode of OTG Core
- * @cdns: pointer to context structure
- * @mode: selected mode from cdns_role
- */
-void cdns3_set_mode(struct cdns3 *cdns, enum usb_dr_mode mode)
+static int cdns3_get_id(struct cdns3 *cdns)
 {
-	u32 reg;
-
-	cdns->current_dr_mode = mode;
-
-	switch (mode) {
-	case USB_DR_MODE_PERIPHERAL:
-		dev_info(cdns->dev, "Set controller to Gadget mode\n");
-		cdns3_drd_switch_gadget(cdns, 1);
-		break;
-	case USB_DR_MODE_HOST:
-		dev_info(cdns->dev, "Set controller to Host mode\n");
-		cdns3_drd_switch_host(cdns, 1);
-		break;
-	case USB_DR_MODE_OTG:
-		dev_info(cdns->dev, "Set controller to OTG mode\n");
-		if (cdns->version == CDNS3_CONTROLLER_V1) {
-			reg = readl(&cdns->otg_v1_regs->override);
-			reg |= OVERRIDE_IDPULLUP;
-			writel(reg, &cdns->otg_v1_regs->override);
-		} else {
-			reg = readl(&cdns->otg_v0_regs->ctrl1);
-			reg |= OVERRIDE_IDPULLUP_V0;
-			writel(reg, &cdns->otg_v0_regs->ctrl1);
-		}
-
-		/*
-		 * Hardware specification says: "ID_VALUE must be valid within
-		 * 50ms after idpullup is set to '1" so driver must wait
-		 * 50ms before reading this pin.
-		 */
-		usleep_range(50000, 60000);
-		break;
-	default:
-		cdns->current_dr_mode = USB_DR_MODE_UNKNOWN;
-		dev_err(cdns->dev, "Unsupported mode of operation %d\n", mode);
-		return;
-	}
+	return !!(readl(&cdns->otg_regs->sts) & OTGSTS_ID_VALUE);
 }
 
-int cdns3_get_id(struct cdns3 *cdns)
+static int cdns3_get_vbus(struct cdns3 *cdns)
 {
-	int id;
-
-	id = readl(&cdns->otg_regs->sts) & OTGSTS_ID_VALUE;
-	dev_dbg(cdns->dev, "OTG ID: %d", id);
-	return  1 ; //id;
+	return !!(readl(&cdns->otg_regs->sts) & OTGSTS_SESSION_VALID);
 }
 
-int cdns3_is_host(struct cdns3 *cdns)
+static int cdns3_is_host(struct cdns3 *cdns)
 {
-	if (cdns->current_dr_mode == USB_DR_MODE_HOST)
-		return 1;
-	else if (!cdns3_get_id(cdns))
+	if (cdns->extcon) {
+		if (extcon_get_state(cdns->extcon, EXTCON_USB_HOST))
+			return 1;
+		else
+			return 0;
+	} else if (!cdns3_get_id(cdns)) {
 		return 1;
-
-	return 0;
+	} else {
+		return 0;
+	}
 }
 
-int cdns3_is_device(struct cdns3 *cdns)
+static int cdns3_is_device(struct cdns3 *cdns)
 {
-	if (cdns->current_dr_mode == USB_DR_MODE_PERIPHERAL)
-		return 1;
-	else if (cdns->current_dr_mode == USB_DR_MODE_OTG)
-		if (cdns3_get_id(cdns))
+	if (cdns->extcon) {
+		if (extcon_get_state(cdns->extcon, EXTCON_USB))
 			return 1;
-
-	return 0;
+		else
+			return 0;
+	} else if (cdns3_get_id(cdns) && cdns3_get_vbus(cdns)) {
+		return 1;
+	} else {
+		return 0;
+	}
 }
 
 /**
@@ -108,136 +68,104 @@ static void cdns3_otg_disable_irq(struct cdns3 *cdns)
  * cdns3_otg_enable_irq - enable id and sess_valid interrupts
  * @cdns: Pointer to controller context structure
  */
-static void cdns3_otg_enable_irq(struct cdns3 *cdns)
+static void cdns3_enable_id_irq(struct cdns3 *cdns)
 {
-	writel(OTGIEN_ID_CHANGE_INT | OTGIEN_VBUSVALID_RISE_INT |
-	       OTGIEN_VBUSVALID_FALL_INT, &cdns->otg_regs->ien);
+	writel(OTGIEN_ID_CHANGE_INT, &cdns->otg_regs->ien);
 }
 
-/**
- * cdns3_drd_switch_host - start/stop host
- * @cdns: Pointer to controller context structure
- * @on: 1 for start, 0 for stop
- *
- * Returns 0 on success otherwise negative errno
- */
-static int cdns3_drd_switch_host(struct cdns3 *cdns, int on)
+static void cdns3_enable_vbus_irq(struct cdns3 *cdns, bool on)
 {
-	int ret;
-	u32 reg = OTGCMD_OTG_DIS;
-
-	/* switch OTG core */
-	if (on) {
-		writel(OTGCMD_HOST_BUS_REQ | reg, &cdns->otg_regs->cmd);
-
-		dev_dbg(cdns->dev, "Waiting for Host mode is turned on\n");
-		ret = cdns3_handshake(&cdns->otg_regs->sts, OTGSTS_XHCI_READY,
-				      OTGSTS_XHCI_READY, 100000);
+	u32 value = readl(&cdns->otg_regs->ien);
+	u32 vbus = (OTGIEN_VBUSSESS_RISE_INT | OTGIEN_VBUSSESS_FALL_INT);
 
-		if (ret)
-			return ret;
-	} else {
-		usleep_range(30, 40);
-		writel(OTGCMD_HOST_BUS_DROP | OTGCMD_DEV_BUS_DROP |
-		       OTGCMD_DEV_POWER_OFF | OTGCMD_HOST_POWER_OFF,
-		       &cdns->otg_regs->cmd);
-	}
+	if (on)
+		writel(value | vbus, &cdns->otg_regs->ien);
+	else
+		writel(vbus & ~vbus, &cdns->otg_regs->ien);
+}
 
-	return 0;
+static void cdns3_drd_switch_off(struct cdns3 *cdns)
+{
+	/*
+	 * driver should wait at least 10us after disabling Device
+	 * before turning-off Device (DEV_BUS_DROP)
+	 */
+	usleep_range(30, 40);
+	writel(OTGCMD_HOST_BUS_DROP | OTGCMD_DEV_BUS_DROP |
+	       OTGCMD_DEV_POWER_OFF | OTGCMD_HOST_POWER_OFF,
+	       &cdns->otg_regs->cmd);
+	usleep_range(3000, 4000);
 }
 
-/**
- * cdns3_drd_switch_gadget - start/stop gadget
- * @cdns: Pointer to controller context structure
- * @on: 1 for start, 0 for stop
- *
- * Returns 0 on success otherwise negative errno
- */
-static int cdns3_drd_switch_gadget(struct cdns3 *cdns, int on)
+static int cdns3_drd_switch_on(struct cdns3 *cdns)
 {
-	int ret;
-	u32 reg = OTGCMD_OTG_DIS;
+	int ret = 0;
+
+	if (cdns->role == CDNS3_ROLE_GADGET) {
+		if (cdns->pdata && cdns->pdata->set_mode)
+			cdns->pdata->set_mode(cdns);
 
-	/* switch OTG core */
-	if (on) {
-		writel(OTGCMD_DEV_BUS_REQ | reg, &cdns->otg_regs->cmd);
+		writel(OTGCMD_DEV_BUS_REQ | OTGCMD_OTG_DIS,
+			&cdns->otg_regs->cmd);
 
 		dev_dbg(cdns->dev, "Waiting for Device mode is turned on\n");
 
 		ret = cdns3_handshake(&cdns->otg_regs->sts, OTGSTS_DEV_READY,
 				      OTGSTS_DEV_READY, 100000);
+		if (ret)
+			return ret;
+	} else if (cdns->role == CDNS3_ROLE_HOST) {
+		if (cdns->pdata && cdns->pdata->set_mode)
+			cdns->pdata->set_mode(cdns);
+
+		writel(OTGCMD_HOST_BUS_REQ | OTGCMD_OTG_DIS, &cdns->otg_regs->cmd);
 
+		ret = cdns3_handshake(&cdns->otg_regs->sts, OTGSTS_XHCI_READY,
+				      OTGSTS_XHCI_READY, 100000);
+		dev_dbg(cdns->dev, "Waiting for Host mode is turned on, ret=%d\n", ret);
 		if (ret)
 			return ret;
 	} else {
-		/*
-		 * driver should wait at least 10us after disabling Device
-		 * before turning-off Device (DEV_BUS_DROP)
-		 */
-		usleep_range(20, 30);
-		writel(OTGCMD_HOST_BUS_DROP | OTGCMD_DEV_BUS_DROP |
-		       OTGCMD_DEV_POWER_OFF | OTGCMD_HOST_POWER_OFF,
-		       &cdns->otg_regs->cmd);
+		if (cdns->pdata && cdns->pdata->set_mode)
+			cdns->pdata->set_mode(cdns);
 	}
 
-	return 0;
+	return ret;
 }
 
 /**
  * cdns3_init_otg_mode - initialize drd controller
  * @cdns: Pointer to controller context structure
  *
- * Returns 0 on success otherwise negative errno
  */
 static void cdns3_init_otg_mode(struct cdns3 *cdns)
 {
+	u32 reg;
+
 	cdns3_otg_disable_irq(cdns);
 	/* clear all interrupts */
 	writel(~0, &cdns->otg_regs->ivect);
 
-	cdns3_set_mode(cdns, USB_DR_MODE_OTG);
-
-	if (cdns3_is_host(cdns))
-		cdns3_drd_switch_host(cdns, 1);
-	else
-		cdns3_drd_switch_gadget(cdns, 1);
-
-	cdns3_otg_enable_irq(cdns);
-}
+	cdns3_enable_id_irq(cdns);
+	if (cdns3_get_id(cdns) == 0)
+		cdns3_enable_vbus_irq(cdns, true);
 
-/**
- * cdns3_drd_update_mode - initialize mode of operation
- * @cdns: Pointer to controller context structure
- *
- * Returns 0 on success otherwise negative errno
- */
-int cdns3_drd_update_mode(struct cdns3 *cdns)
-{
-	int ret = 0;
-
-	if (cdns->desired_dr_mode == cdns->current_dr_mode)
-		return ret;
-
-	cdns3_drd_switch_gadget(cdns, 0);
-	cdns3_drd_switch_host(cdns, 0);
-
-	switch (cdns->desired_dr_mode) {
-	case USB_DR_MODE_PERIPHERAL:
-		cdns3_set_mode(cdns, USB_DR_MODE_PERIPHERAL);
-		break;
-	case USB_DR_MODE_HOST:
-		cdns3_set_mode(cdns, USB_DR_MODE_HOST);
-		break;
-	case USB_DR_MODE_OTG:
-		cdns3_init_otg_mode(cdns);
-		break;
-	default:
-		dev_err(cdns->dev, "Unsupported mode of operation %d\n",
-			cdns->dr_mode);
-		return -EINVAL;
+	if (cdns->version == CDNS3_CONTROLLER_V1) {
+		reg = readl(&cdns->otg_v1_regs->override);
+		reg |= OVERRIDE_IDPULLUP;
+		writel(reg, &cdns->otg_v1_regs->override);
+	} else {
+		reg = readl(&cdns->otg_v0_regs->ctrl1);
+		reg |= OVERRIDE_IDPULLUP_V0;
+		writel(reg, &cdns->otg_v0_regs->ctrl1);
 	}
 
-	return ret;
+	/*
+	 * Hardware specification says: "ID_VALUE must be valid within
+	 * 50ms after idpullup is set to '1" so driver must wait
+	 * 50ms before reading this pin.
+	 */
+	usleep_range(50000, 60000);
 }
 
 /**
@@ -253,8 +181,9 @@ static irqreturn_t cdns3_drd_irq(int irq, void *data)
 	irqreturn_t ret = IRQ_NONE;
 	struct cdns3 *cdns = data;
 	u32 reg;
+	bool vbus;
 
-	if (cdns->dr_mode != USB_DR_MODE_OTG)
+	if (cdns->dr_mode == USB_DR_MODE_HOST)
 		return ret;
 
 	reg = readl(&cdns->otg_regs->ivect);
@@ -266,20 +195,72 @@ static irqreturn_t cdns3_drd_irq(int irq, void *data)
 		dev_dbg(cdns->dev, "OTG IRQ: new ID: %d\n",
 			cdns3_get_id(cdns));
 
+		writel(OTGIEN_ID_CHANGE_INT, &cdns->otg_regs->ivect);
 		queue_work(system_freezable_wq, &cdns->role_switch_wq);
 
 		ret = IRQ_HANDLED;
 	}
 
-	writel(~0, &cdns->otg_regs->ivect);
+	if ((reg & OTGIEN_VBUSSESS_RISE_INT)
+		|| (reg & OTGIEN_VBUSSESS_FALL_INT)) {
+		u32 vbus_status;
+		vbus = cdns3_get_vbus(cdns);
+		dev_dbg(cdns->dev, "OTG IRQ: VBUS : %d\n", vbus);
+		vbus_status = vbus ? OTGIEN_VBUSSESS_RISE_INT
+			: OTGIEN_VBUSSESS_FALL_INT;
+		writel(vbus_status, &cdns->otg_regs->ivect);
+		queue_work(system_freezable_wq, &cdns->role_switch_wq);
+		ret = IRQ_HANDLED;
+	}
+
 	return ret;
 }
 
-int cdns3_drd_init(struct cdns3 *cdns)
+static int cdns3_extcon_notifier(struct notifier_block *nb, unsigned long event,
+			     void *ptr)
+{
+	struct cdns3 *cdns = container_of(nb, struct cdns3, extcon_nb);
+
+	queue_work(system_freezable_wq, &cdns->role_switch_wq);
+
+	return NOTIFY_DONE;
+}
+
+static int cdns3_register_extcon(struct cdns3 *cdns)
+{
+	struct extcon_dev *extcon;
+	struct device *dev = cdns->dev;
+	int ret;
+
+	if (of_property_read_bool(dev->of_node, "extcon")) {
+		extcon = extcon_get_edev_by_phandle(dev, 0);
+		if (IS_ERR(extcon))
+			return PTR_ERR(extcon);
+
+		ret = devm_extcon_register_notifier(dev, extcon,
+			EXTCON_USB_HOST, &cdns->extcon_nb);
+		if (ret < 0) {
+			dev_err(dev, "register Host Connector failed\n");
+			return ret;
+		}
+
+		ret = devm_extcon_register_notifier(dev, extcon,
+			EXTCON_USB, &cdns->extcon_nb);
+		if (ret < 0) {
+			dev_err(dev, "register Device Connector failed\n");
+			return ret;
+		}
+
+		cdns->extcon = extcon;
+		cdns->extcon_nb.notifier_call = cdns3_extcon_notifier;
+	}
+
+	return 0;
+}
+
+int cdns3_finalize_dr_mode(struct cdns3 *cdns)
 {
 	void __iomem *regs;
-	int ret = 0;
-	u32 state;
 
 	regs = devm_ioremap_resource(cdns->dev, &cdns->otg_res);
 	if (IS_ERR(regs))
@@ -299,6 +280,7 @@ int cdns3_drd_init(struct cdns3 *cdns)
 		cdns->version  = CDNS3_CONTROLLER_V0;
 		cdns->otg_v1_regs = NULL;
 		cdns->otg_regs = regs;
+		writel(0x1, &cdns->otg_v0_regs->simulate);
 		dev_info(cdns->dev, "DRD version v0 (%08x)\n",
 			 readl(&cdns->otg_v0_regs->version));
 	} else {
@@ -311,27 +293,87 @@ int cdns3_drd_init(struct cdns3 *cdns)
 			 readl(&cdns->otg_v1_regs->rid));
 	}
 
-	state = OTGSTS_STRAP(readl(&cdns->otg_regs->sts));
+	cdns->dr_mode = usb_get_dr_mode(cdns->dev);
+	if (cdns->dr_mode == USB_DR_MODE_UNKNOWN)
+		cdns->dr_mode = USB_DR_MODE_OTG;
 
-	/* Update dr_mode according to STRAP configuration. */
-	cdns->dr_mode = USB_DR_MODE_OTG;
-	if (state == OTGSTS_STRAP_HOST) {
-		dev_info(cdns->dev, "Controller strapped to HOST\n");
-		cdns->dr_mode = USB_DR_MODE_HOST;
-	} else if (state == OTGSTS_STRAP_GADGET) {
-		dev_info(cdns->dev, "Controller strapped to PERIPHERAL\n");
-		cdns->dr_mode = USB_DR_MODE_PERIPHERAL;
+	return 0;
+}
+
+static int cdns3_do_role_switch(struct cdns3 *cdns, enum cdns3_roles role)
+{
+	int ret = 0;
+	enum cdns3_roles current_role;
+
+	dev_dbg(cdns->dev, "current role is %d, switch to %d\n",
+			cdns->role, role);
+
+	if (cdns->role == role)
+		return 0;
+
+	current_role = cdns->role;
+	cdns3_role_stop(cdns);
+	/*
+	 * - Enable vbus interrupt after leaving host
+	 * - Disable vbus interrupt before entering host
+	 */
+
+	if (current_role == CDNS3_ROLE_HOST)
+		cdns3_enable_vbus_irq(cdns, true);
+	else if (role == CDNS3_ROLE_HOST)
+		cdns3_enable_vbus_irq(cdns, false);
+	if (role == CDNS3_ROLE_END) {
+		/* TODO: Force B Session Valid as 0 */
+		return 0;
 	}
 
-	cdns->desired_dr_mode = cdns->dr_mode;
-	cdns->current_dr_mode = USB_DR_MODE_UNKNOWN;
+	ret = cdns3_role_start(cdns, role);
+	if (ret) {
+		/* Back to current role */
+		dev_err(cdns->dev, "set %d has failed, back to %d\n",
+					role, current_role);
+		if (role == CDNS3_ROLE_HOST)
+			cdns3_enable_vbus_irq(cdns, true);
+		ret = cdns3_role_start(cdns, current_role);
+	}
 
-	ret = devm_request_threaded_irq(cdns->dev, cdns->irq, cdns3_drd_irq,
-					NULL, IRQF_SHARED,
-					dev_name(cdns->dev), cdns);
+	return ret;
+}
 
-	if (ret)
-		return ret;
+/**
+ * cdns3_role_switch - work queue handler for role switch
+ *
+ * @work: work queue item structure
+ *
+ * Handles below events:
+ * - Role switch for dual-role devices
+ * - CDNS3_ROLE_GADGET <--> CDNS3_ROLE_END for peripheral-only devices
+ */
+static void cdns3_role_switch(struct work_struct *work)
+{
+	struct cdns3 *cdns = container_of(work, struct cdns3,
+			role_switch_wq);
+	bool device, host;
+
+	host = cdns3_is_host(cdns);
+	device = cdns3_is_device(cdns);
+
+	if (host) {
+		if (cdns->roles[CDNS3_ROLE_HOST])
+			cdns3_do_role_switch(cdns, CDNS3_ROLE_HOST);
+		return;
+	}
+
+	if (device)
+		cdns3_do_role_switch(cdns, CDNS3_ROLE_GADGET);
+	else
+		cdns3_do_role_switch(cdns, CDNS3_ROLE_END);
+}
+
+int cdns3_drd_init(struct cdns3 *cdns)
+{
+	int ret;
+	u32 state;
 
 	state = readl(&cdns->otg_regs->sts);
 	if (OTGSTS_OTG_NRDY(state) != 0) {
@@ -339,12 +381,82 @@ int cdns3_drd_init(struct cdns3 *cdns)
 		return -ENODEV;
 	}
 
-	ret = cdns3_drd_update_mode(cdns);
+	INIT_WORK(&cdns->role_switch_wq, cdns3_role_switch);
+	ret = cdns3_register_extcon(cdns);
+	if (ret)
+		return ret;
+
+	cdns3_init_otg_mode(cdns);
+
+	return devm_request_threaded_irq(cdns->dev, cdns->irq, cdns3_drd_irq,
+					NULL, IRQF_SHARED,
+					dev_name(cdns->dev), cdns);
+}
+
+int cdns3_role_start(struct cdns3 *cdns, enum cdns3_roles role)
+{
+	int ret;
+
+	if (role >= CDNS3_ROLE_END) {
+		WARN_ON(role > CDNS3_ROLE_END);
+		return 0;
+	}
+
+	if (!cdns->roles[role])
+		return -ENXIO;
+
+	if (cdns->roles[role]->state == CDNS3_ROLE_STATE_ACTIVE)
+		return 0;
 
+	mutex_lock(&cdns->mutex);
+	cdns->role = role;
+	cdns3_drd_switch_on(cdns);
+	ret = cdns->roles[role]->start(cdns);
+	if (!ret)
+		cdns->roles[role]->state = CDNS3_ROLE_STATE_ACTIVE;
+	mutex_unlock(&cdns->mutex);
 	return ret;
 }
 
-int cdns3_drd_exit(struct cdns3 *cdns)
+void cdns3_role_stop(struct cdns3 *cdns)
+{
+	enum cdns3_roles role = cdns->role;
+
+	if (role >= CDNS3_ROLE_END) {
+		WARN_ON(role > CDNS3_ROLE_END);
+		return;
+	}
+
+	if (cdns->roles[role]->state == CDNS3_ROLE_STATE_INACTIVE)
+		return;
+
+	mutex_lock(&cdns->mutex);
+	cdns->roles[role]->stop(cdns);
+	cdns->roles[role]->state = CDNS3_ROLE_STATE_INACTIVE;
+	cdns3_drd_switch_off(cdns);
+	mutex_unlock(&cdns->mutex);
+}
+
+/*
+ * cdns->role gets from cdns3_get_role, and this API tells role at the
+ * runtime.
+ * If both roles are supported, the role is selected based on vbus/id.
+ * It could be read from OTG register or external connector.
+ * If only single role is supported, only one role structure
+ * is allocated, cdns->roles[CDNS3_ROLE_HOST] or cdns->roles[CDNS3_ROLE_GADGET].
+ */
+enum cdns3_roles cdns3_get_role(struct cdns3 *cdns)
 {
-	return cdns3_drd_switch_host(cdns, 0);
+	if (cdns->roles[CDNS3_ROLE_HOST] && cdns->roles[CDNS3_ROLE_GADGET]) {
+		if (cdns3_is_host(cdns))
+			return CDNS3_ROLE_HOST;
+		else if (cdns3_is_device(cdns))
+			return CDNS3_ROLE_GADGET;
+		else
+			return CDNS3_ROLE_END;
+	} else {
+		return cdns->roles[CDNS3_ROLE_HOST]
+			? CDNS3_ROLE_HOST
+			: CDNS3_ROLE_GADGET;
+	}
 }
diff --git a/drivers/usb/cdns3/drd.h b/drivers/usb/cdns3/drd.h
index 6a29cdcb492d..42f151d2f653 100644
--- a/drivers/usb/cdns3/drd.h
+++ b/drivers/usb/cdns3/drd.h
@@ -11,7 +11,6 @@
 
 #include <linux/usb/otg.h>
 #include <linux/phy/phy.h>
-#include "core.h"
 
 /*  DRD register interface for version v1. */
 struct cdns3_otg_regs {
@@ -100,6 +99,10 @@ struct cdns3_otg_common_regs {
 /* OTGIEN - bitmasks */
 /* ID change interrupt enable */
 #define OTGIEN_ID_CHANGE_INT		BIT(0)
+/* session valid rise detected interrupt enable.*/
+#define OTGIEN_VBUSSESS_RISE_INT	BIT(2)
+/* session valid fall detected interrupt enable */
+#define OTGIEN_VBUSSESS_FALL_INT	BIT(3)
 /* Vbusvalid fall detected interrupt enable.*/
 #define OTGIEN_VBUSVALID_RISE_INT	BIT(4)
 /* Vbusvalid fall detected interrupt enable */
@@ -152,11 +155,10 @@ struct cdns3_otg_common_regs {
 /* Only for CDNS3_CONTROLLER_V0 version */
 #define OVERRIDE_IDPULLUP_V0		BIT(24)
 
-int cdns3_is_host(struct cdns3 *cdns);
-int cdns3_is_device(struct cdns3 *cdns);
-int cdns3_get_id(struct cdns3 *cdns);
 int cdns3_drd_init(struct cdns3 *cdns);
-int cdns3_drd_exit(struct cdns3 *cdns);
-int cdns3_drd_update_mode(struct cdns3 *cdns);
+int cdns3_finalize_dr_mode(struct cdns3 *cdns);
+enum cdns3_roles cdns3_get_role(struct cdns3 *cdns);
+int cdns3_role_start(struct cdns3 *cdns, enum cdns3_roles role);
+void cdns3_role_stop(struct cdns3 *cdns);
 
 #endif /* __LINUX_CDNS3_DRD */
diff --git a/drivers/usb/cdns3/gadget.c b/drivers/usb/cdns3/gadget.c
index 0d95eb00be37..5e04d7740087 100644
--- a/drivers/usb/cdns3/gadget.c
+++ b/drivers/usb/cdns3/gadget.c
@@ -498,6 +498,9 @@ int cdns3_ep_run_transfer(struct cdns3_endpoint *priv_ep,
 	int  num_trb;
 	int address;
 	int pcs;
+	bool doorbell;
+	int dma_index;
+	u32 reg;
 
 	if (priv_ep->type == USB_ENDPOINT_XFER_ISOC)
 		num_trb = priv_ep->interval;
@@ -541,6 +544,28 @@ int cdns3_ep_run_transfer(struct cdns3_endpoint *priv_ep,
 	}
 
 	first_pcs = priv_ep->pcs ? TRB_CYCLE : 0;
+	/* arm transfer on selected endpoint */
+	cdns3_select_ep(priv_ep->cdns3_dev, address);
+
+	doorbell = !!(readl(&priv_dev->regs->ep_cmd) & EP_CMD_DRDY);
+	dma_index = (readl(&priv_dev->regs->ep_traddr) -
+			 priv_ep->trb_pool_dma) / TRB_SIZE;
+
+	if (!priv_ep->wa_set) {
+		if (doorbell & ((priv_ep->enqueue == 0
+				     && dma_index >= TRBS_PER_SEGMENT - 2) ||
+			(dma_index == priv_ep->enqueue) ||
+			(dma_index == priv_ep->enqueue - 1))) {
+			priv_ep->wa_cycle_bit = first_pcs;
+			priv_ep->wa_set = 1;
+			priv_ep->wa_trb = trb;
+			first_pcs ^= 0x1;
+			reg = readl(&priv_dev->regs->ep_sts_en);
+			reg |= EP_STS_TRBERR;
+			writel(reg, &priv_dev->regs->ep_sts_en);
+		}
+	}
+
 
 	do {
 	/* fill TRB */
@@ -583,7 +608,6 @@ int cdns3_ep_run_transfer(struct cdns3_endpoint *priv_ep,
 	wmb();
 
 	priv_req->flags |= REQUEST_PENDING;
-
 	/* give the TD to the consumer*/
 	if (sg_iter == 1)
 		trb->control |= first_pcs | TRB_IOC | TRB_ISP;
@@ -593,9 +617,6 @@ int cdns3_ep_run_transfer(struct cdns3_endpoint *priv_ep,
 	trace_cdns3_prepare_trb(priv_ep, priv_req->trb);
 	trace_cdns3_ring(priv_ep);
 
-	/* arm transfer on selected endpoint */
-	cdns3_select_ep(priv_ep->cdns3_dev, address);
-
 	/*
 	 * For DMULT mode we can set address to transfer ring only once after
 	 * enabling endpoint.
@@ -607,10 +628,12 @@ int cdns3_ep_run_transfer(struct cdns3_endpoint *priv_ep,
 	}
 
 	/*clearing TRBERR and EP_STS_DESCMIS before seting DRDY*/
-	writel(EP_STS_TRBERR | EP_STS_DESCMIS, &priv_dev->regs->ep_sts);
-	trace_cdns3_doorbell_epx(priv_ep->name,
-				 readl(&priv_dev->regs->ep_traddr));
-	writel(EP_CMD_DRDY, &priv_dev->regs->ep_cmd);
+	if (!priv_ep->wa_set) {
+		writel(EP_STS_TRBERR | EP_STS_DESCMIS, &priv_dev->regs->ep_sts);
+		writel(EP_CMD_DRDY, &priv_dev->regs->ep_cmd);
+		trace_cdns3_doorbell_epx(priv_ep->name,
+					 readl(&priv_dev->regs->ep_traddr));
+	}
 
 	return 0;
 }
@@ -852,6 +875,28 @@ static int cdns3_check_ep_interrupt_proceed(struct cdns3_endpoint *priv_ep)
 	ep_sts_reg = readl(&priv_dev->regs->ep_sts);
 	writel(ep_sts_reg, &priv_dev->regs->ep_sts);
 
+	if (ep_sts_reg & EP_STS_TRBERR) {
+		u32 reg;
+		if(!priv_ep->wa_set) {
+			printk("not wa_set at TRBERR\n");
+		} else {
+			printk(KERN_DEBUG "wa_set at TRBERR\n");
+			priv_ep->wa_set = 0;
+			if(priv_ep->wa_cycle_bit) {
+				priv_ep->wa_trb->control =
+					priv_ep->wa_trb->control | 0x1;
+			} else {
+				priv_ep->wa_trb->control =
+					priv_ep->wa_trb->control & ~0x1;
+			}
+
+			reg = readl(&priv_dev->regs->ep_sts_en);
+			reg &= ~EP_STS_TRBERR;
+			writel(reg, &priv_dev->regs->ep_sts_en);
+			writel(EP_CMD_DRDY, &priv_dev->regs->ep_cmd);
+		}
+	}
+
 	if ((ep_sts_reg & EP_STS_IOC) || (ep_sts_reg & EP_STS_ISP)) {
 		if (priv_ep->flags & EP_QUIRK_EXTRA_BUF_EN) {
 			if (ep_sts_reg & EP_STS_ISP)
@@ -951,6 +996,9 @@ static irqreturn_t cdns3_device_irq_handler(int irq, void *data)
 	unsigned long flags;
 	u32 reg;
 
+	if (cdns->role != CDNS3_ROLE_GADGET)
+		return ret;
+
 	priv_dev = cdns->gadget_dev;
 	spin_lock_irqsave(&priv_dev->lock, flags);
 
@@ -1249,7 +1297,7 @@ static int cdns3_gadget_ep_enable(struct usb_ep *ep,
 {
 	struct cdns3_endpoint *priv_ep;
 	struct cdns3_device *priv_dev;
-	u32 reg = EP_STS_EN_TRBERREN;
+	u32 reg = 0/*EP_STS_EN_TRBERREN*/;
 	u32 bEndpointAddress;
 	unsigned long flags;
 	int ret;
@@ -1311,8 +1359,9 @@ static int cdns3_gadget_ep_enable(struct usb_ep *ep,
 	 * if before first DESCMISS interrupt the DMA will be armed.
 	 */
 	if (!priv_ep->dir && priv_ep->type != USB_ENDPOINT_XFER_ISOC) {
-		priv_ep->flags |= EP_QUIRK_EXTRA_BUF_DET;
+	/*	priv_ep->flags |= EP_QUIRK_EXTRA_BUF_DET;
 		reg |= EP_STS_EN_DESCMISEN;
+		*/
 	}
 
 	writel(reg, &priv_dev->regs->ep_sts_en);
@@ -1325,6 +1374,7 @@ static int cdns3_gadget_ep_enable(struct usb_ep *ep,
 	priv_ep->flags |= EP_ENABLED | EP_UPDATE_EP_TRBADDR;
 	priv_ep->enqueue = 0;
 	priv_ep->dequeue = 0;
+	priv_ep->wa_set = 0;
 	reg = readl(&priv_dev->regs->ep_sts);
 	priv_ep->pcs = !!EP_STS_CCS(reg);
 	priv_ep->ccs = !!EP_STS_CCS(reg);
diff --git a/drivers/usb/cdns3/gadget.h b/drivers/usb/cdns3/gadget.h
index 41cec7f085ad..a0bce149726e 100644
--- a/drivers/usb/cdns3/gadget.h
+++ b/drivers/usb/cdns3/gadget.h
@@ -897,7 +897,7 @@ struct cdns3_usb_regs {
 /*
  * USBSS-DEV DMA interface.
  */
-#define TRBS_PER_SEGMENT	40
+#define TRBS_PER_SEGMENT       40
 
 #define ISO_MAX_INTERVAL	10
 
@@ -1063,6 +1063,9 @@ struct cdns3_endpoint {
 	u8			ccs;
 	int			enqueue;
 	int			dequeue;
+	int 			wa_cycle_bit;
+	bool			wa_set;
+	struct cdns3_trb 	*wa_trb;
 };
 
 /**
diff --git a/drivers/usb/cdns3/trace.h b/drivers/usb/cdns3/trace.h
index f3d7a91fae86..8e97575a6b01 100644
--- a/drivers/usb/cdns3/trace.h
+++ b/drivers/usb/cdns3/trace.h
@@ -179,7 +179,7 @@ DECLARE_EVENT_CLASS(cdns3_log_request,
 		__entry->flags = req->flags;
 	),
 	TP_printk("%s: req: %p, length: %u/%u %s%s%s, status: %d,"
-		  " trb: [start:%d, end:%d: virt addr %pa], flags:%x ",
+		  " trb: [start:%d, end:%d: virt addr 0x%p], flags:%x ",
 		__get_str(name), __entry->req, __entry->actual, __entry->length,
 		__entry->zero ? "zero | " : "",
 		__entry->short_not_ok ? "short | " : "",
@@ -236,7 +236,7 @@ DECLARE_EVENT_CLASS(cdns3_log_trb,
 		__entry->control = trb->control;
 		__entry->type = usb_endpoint_type(priv_ep->endpoint.desc);
 	),
-	TP_printk("%s: trb 0x%pa, dma buf: 0x%08x, size: %ld, ctrl: 0x%08x (%s%s%s%s%s%s%s)",
+	TP_printk("%s: trb 0x%p, dma buf: 0x%08x, size: %ld, ctrl: 0x%08x (%s%s%s%s%s%s%s)",
 		__get_str(name), __entry->trb, __entry->buffer,
 		TRB_LEN(__entry->length), __entry->control,
 		__entry->control & TRB_CYCLE ? "C=1, " : "C=0, ",
-- 
2.14.1


[-- Attachment #3: cdns3.tar.bz2 --]
[-- Type: application/octet-stream, Size: 38408 bytes --]

^ permalink raw reply related	[flat|nested] 49+ messages in thread

* Re: [PATCH v2 5/5] usb:cdns3 Add Cadence USB3 DRD Driver
@ 2019-01-10  6:57       ` Greg Kroah-Hartman
  0 siblings, 0 replies; 49+ messages in thread
From: Greg Kroah-Hartman @ 2019-01-10  6:57 UTC (permalink / raw)
  To: Peter Chen
  Cc: Pawel Laszczak, devicetree, mark.rutland, linux-usb, hdegoede,
	Heikki Krogerus, andy.shevchenko, robh+dt, rogerq, lkml,
	adouglas, jbergsagel, nsekhar, nm, sureshp, peter.chen, pjez,
	kurahul

On Thu, Jan 10, 2019 at 09:30:41AM +0800, Peter Chen wrote:
> On Mon, Dec 24, 2018 at 12:44 AM Pawel Laszczak <pawell@cadence.com> wrote:
> - debugfs is nice to have feature, I suggest removing it at this
> initial version. Besides, role switch
> through /sys is normal feature, the end user may use it at real
> product, so, it is better at device's
> /sys entry instead of debugfs.
> 
> - I don't know why you add "disable" at debugfs, please comment.

As you imply here, no real-world functionality should ever be in
debugfs as it is an optional system component and kernel code should
work just fine without it being enabled (as more and more systems are
disabling it due to the obvious security problems it has.)

thanks,

greg k-h

^ permalink raw reply	[flat|nested] 49+ messages in thread

* [v2,5/5] usb:cdns3 Add Cadence USB3 DRD Driver
@ 2019-01-10  6:57       ` Greg Kroah-Hartman
  0 siblings, 0 replies; 49+ messages in thread
From: Greg Kroah-Hartman @ 2019-01-10  6:57 UTC (permalink / raw)
  To: Peter Chen
  Cc: Pawel Laszczak, devicetree, mark.rutland, linux-usb, hdegoede,
	Heikki Krogerus, andy.shevchenko, robh+dt, rogerq, lkml,
	adouglas, jbergsagel, nsekhar, nm, sureshp, peter.chen, pjez,
	kurahul

On Thu, Jan 10, 2019 at 09:30:41AM +0800, Peter Chen wrote:
> On Mon, Dec 24, 2018 at 12:44 AM Pawel Laszczak <pawell@cadence.com> wrote:
> - debugfs is nice to have feature, I suggest removing it at this
> initial version. Besides, role switch
> through /sys is normal feature, the end user may use it at real
> product, so, it is better at device's
> /sys entry instead of debugfs.
> 
> - I don't know why you add "disable" at debugfs, please comment.

As you imply here, no real-world functionality should ever be in
debugfs as it is an optional system component and kernel code should
work just fine without it being enabled (as more and more systems are
disabling it due to the obvious security problems it has.)

thanks,

greg k-h

^ permalink raw reply	[flat|nested] 49+ messages in thread

* RE: [PATCH v2 5/5] usb:cdns3 Add Cadence USB3 DRD Driver
@ 2019-01-10  7:18         ` Pawel Laszczak
  0 siblings, 0 replies; 49+ messages in thread
From: Pawel Laszczak @ 2019-01-10  7:18 UTC (permalink / raw)
  To: Greg Kroah-Hartman, Peter Chen
  Cc: devicetree, mark.rutland, linux-usb, hdegoede, Heikki Krogerus,
	andy.shevchenko, robh+dt, rogerq, lkml, adouglas, jbergsagel,
	nsekhar, nm, Suresh Punnoose, peter.chen, Pawel Jez, Rahul Kumar

Hi, 

>On Thu, Jan 10, 2019 at 09:30:41AM +0800, Peter Chen wrote:
>> On Mon, Dec 24, 2018 at 12:44 AM Pawel Laszczak <pawell@cadence.com> wrote:
>> - debugfs is nice to have feature, I suggest removing it at this
>> initial version. Besides, role switch
>> through /sys is normal feature, the end user may use it at real
>> product, so, it is better at device's
>> /sys entry instead of debugfs.
>>
>> - I don't know why you add "disable" at debugfs, please comment.
>
>As you imply here, no real-world functionality should ever be in
>debugfs as it is an optional system component and kernel code should
>work just fine without it being enabled (as more and more systems are
>disabling it due to the obvious security problems it has.)
>
"disable" it's not required. It's used for testing. It allows to enable/disable the current role.
It can be used for testing eg. connect/disconnect event. 

I mainly test the driver remotely and I can't do connect/disconnect by USB cable. 
"disable" allow me to get trace log from host/device without disconnecting cable:
I'm using the fallowing sequence:
 - load cdns3.ko modules 
 - echo 1 > disable - disable current role
 - echo cdns3:* tracing/set_event 
 - echo 0 > disable  - enable role again 
 - cat tracing/trace. 

Pawel

^ permalink raw reply	[flat|nested] 49+ messages in thread

* [v2,5/5] usb:cdns3 Add Cadence USB3 DRD Driver
@ 2019-01-10  7:18         ` Pawel Laszczak
  0 siblings, 0 replies; 49+ messages in thread
From: Pawel Laszczak @ 2019-01-10  7:18 UTC (permalink / raw)
  To: Greg Kroah-Hartman, Peter Chen
  Cc: devicetree, mark.rutland, linux-usb, hdegoede, Heikki Krogerus,
	andy.shevchenko, robh+dt, rogerq, lkml, adouglas, jbergsagel,
	nsekhar, nm, Suresh Punnoose, peter.chen, Pawel Jez, Rahul Kumar

Hi, 

>On Thu, Jan 10, 2019 at 09:30:41AM +0800, Peter Chen wrote:
>> On Mon, Dec 24, 2018 at 12:44 AM Pawel Laszczak <pawell@cadence.com> wrote:
>> - debugfs is nice to have feature, I suggest removing it at this
>> initial version. Besides, role switch
>> through /sys is normal feature, the end user may use it at real
>> product, so, it is better at device's
>> /sys entry instead of debugfs.
>>
>> - I don't know why you add "disable" at debugfs, please comment.
>
>As you imply here, no real-world functionality should ever be in
>debugfs as it is an optional system component and kernel code should
>work just fine without it being enabled (as more and more systems are
>disabling it due to the obvious security problems it has.)
>
"disable" it's not required. It's used for testing. It allows to enable/disable the current role.
It can be used for testing eg. connect/disconnect event. 

I mainly test the driver remotely and I can't do connect/disconnect by USB cable. 
"disable" allow me to get trace log from host/device without disconnecting cable:
I'm using the fallowing sequence:
 - load cdns3.ko modules 
 - echo 1 > disable - disable current role
 - echo cdns3:* tracing/set_event 
 - echo 0 > disable  - enable role again 
 - cat tracing/trace. 

Pawel

^ permalink raw reply	[flat|nested] 49+ messages in thread

end of thread, other threads:[~2019-01-10  7:19 UTC | newest]

Thread overview: 49+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2018-12-23 15:13 [PATCH v2 0/5] Introduced new Cadence USBSS DRD Driver Pawel Laszczak
2018-12-23 15:13 ` Pawel Laszczak
2018-12-23 15:13 ` [PATCH v2 1/5] dt-bindings: add binding for USBSS-DRD controller Pawel Laszczak
2018-12-23 15:13   ` [v2,1/5] " Pawel Laszczak
2018-12-23 15:13   ` [PATCH v2 1/5] " Pawel Laszczak
2018-12-23 15:13 ` [PATCH v2 2/5] usb:common Separated decoding functions from dwc3 driver Pawel Laszczak
2018-12-23 15:13   ` [v2,2/5] " Pawel Laszczak
2018-12-23 15:13   ` [PATCH v2 2/5] " Pawel Laszczak
2018-12-23 15:13 ` [PATCH v2 3/5] usb:common Patch simplify usb_decode_set_clear_feature function Pawel Laszczak
2018-12-23 15:13   ` [v2,3/5] " Pawel Laszczak
2018-12-23 15:13   ` [PATCH v2 3/5] " Pawel Laszczak
2018-12-23 15:13 ` [PATCH v2 4/5] usb:common Simplify usb_decode_get_set_descriptor function Pawel Laszczak
2018-12-23 15:13   ` [v2,4/5] " Pawel Laszczak
2018-12-23 15:13   ` [PATCH v2 4/5] " Pawel Laszczak
2018-12-23 15:13 ` [PATCH v2 5/5] usb:cdns3 Add Cadence USB3 DRD Driver Pawel Laszczak
2018-12-23 15:13   ` [v2,5/5] " Pawel Laszczak
2018-12-23 15:13   ` [PATCH v2 5/5] " Pawel Laszczak
2018-12-23 17:19   ` Randy Dunlap
2018-12-23 17:19     ` [v2,5/5] " Randy Dunlap
2018-12-23 19:20   ` [PATCH v2 5/5] " kbuild test robot
2018-12-23 19:20     ` [v2,5/5] " kbuild test robot
2018-12-23 19:20     ` [PATCH v2 5/5] " kbuild test robot
2018-12-31  9:42     ` Pawel Laszczak
2018-12-31  9:42       ` [v2,5/5] " Pawel Laszczak
2018-12-31  9:42       ` [PATCH v2 5/5] " Pawel Laszczak
2018-12-24  9:38   ` Peter Chen
2018-12-24  9:38     ` [v2,5/5] " Peter Chen
2018-12-25  9:38     ` [PATCH v2 5/5] " Peter Chen
2018-12-25  9:38       ` [v2,5/5] " Peter Chen
2018-12-27  9:36       ` [PATCH v2 5/5] " Pawel Laszczak
2018-12-27  9:36         ` [v2,5/5] " Pawel Laszczak
2018-12-28  1:31         ` [PATCH v2 5/5] " Peter Chen
2018-12-28  1:31           ` [v2,5/5] " Peter Chen
2018-12-31  5:32           ` [PATCH v2 5/5] " Pawel Laszczak
2018-12-31  5:32             ` [v2,5/5] " Pawel Laszczak
2018-12-27 13:11     ` [PATCH v2 5/5] " Pawel Laszczak
2018-12-27 13:11       ` [v2,5/5] " Pawel Laszczak
2018-12-29  7:41   ` [PATCH v2 5/5] " Chunfeng Yun
2018-12-29  7:41     ` [v2,5/5] " Chunfeng Yun
2018-12-29  7:41     ` [PATCH v2 5/5] " Chunfeng Yun
2018-12-31 12:31     ` Pawel Laszczak
2018-12-31 12:31       ` [v2,5/5] " Pawel Laszczak
2018-12-31 12:31       ` [PATCH v2 5/5] " Pawel Laszczak
2019-01-10  1:30   ` Peter Chen
2019-01-10  6:57     ` Greg Kroah-Hartman
2019-01-10  6:57       ` [v2,5/5] " Greg Kroah-Hartman
2019-01-10  7:18       ` [PATCH v2 5/5] " Pawel Laszczak
2019-01-10  7:18         ` [v2,5/5] " Pawel Laszczak
2019-01-02  6:44 ` [PATCH v2 0/5] Introduced new Cadence USBSS " Peter Chen

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.