Преглед на файлове

Merged TI feature connectivity into ti-linux-4.19.y

* 'connectivity-ti-linux-4.19.y' of ssh://bitbucket.itg.ti.com/lcpdpublicdom/connectivity:
  net: ethernet: ti: am65-cpsw-nuss: enable 10Mbps link speed in rgmii mode
  ethernet: ti: am65-cpsw: allow hwts for ptp sync msgs and others
  Revert "HACK: arm64: dts: ti: k3-j721e-main: Drop dma-coherent for usb0"
  arm64: dts: ti: k3-j721e: usb: Sync with driver changes
  usb: cdns3: gadget: Fix full-speed mode
  usb: cdns3: fix cdns3_core_init_role()
  usb: cdns3: Add TI specific wrapper driver
  usb:cdns3 Fix for stuck packets in on-chip OUT buffer.
  usb:cdns3 Add Cadence USB3 DRD Driver
  usb:common Simplify usb_decode_get_set_descriptor function.
  usb:common Patch simplify usb_decode_set_clear_feature function.
  usb:common Separated decoding functions from dwc3 driver.
  dt-bindings: add binding for USBSS-DRD controller.
  usb: cdns3: Remove Cadence usb driver
  Revert "usb:common Separated decoding functions from dwc3 driver."
  Revert "usb:common Patch simplify usb_decode_set_clear_feature function."
  Revert "usb:common Simplify usb_decode_get_set_descriptor function."
  Revert "usb: cdns3: Add TI specific wrapper driver"

Signed-off-by: LCPD Auto Merger <lcpd_integration@list.ti.com>
LCPD Auto Merger преди 6 години
родител
ревизия
213171c3a4

+ 27 - 12
Documentation/devicetree/bindings/usb/cdns-usb3.txt

@@ -1,30 +1,45 @@
 Binding for the Cadence USBSS-DRD controller
 
 Required properties:
-  - reg: Physical base address and size of the controller's register areas.
+ - 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
-  - reg-names - register memory area names:
+	 - HOST registers area
+	 - DEVICE registers area
+	 - OTG/DRD registers area
+ - reg-names - register memory area names:
 	"xhci" - for HOST registers space
 	"dev" - for DEVICE registers space
 	"otg" - for OTG/DRD registers space
-  - compatible: Should contain: "cdns,usb3-1.0.0" or "cdns,usb3-1.0.1"
-  - interrupts: Interrupts used by cdns3 controller.
+ - compatible: Should contain: "cdns,usb3"
+ - interrupts: Interrupts used by cdns3 controller:
+	"host" - interrupt used by XHCI driver.
+	"peripheral" - interrupt used by device driver
+	"otg" - interrupt used by DRD/OTG  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: from the *Generic PHY* bindings;
+	Supported names are:
+	- cdns3,usb2-phy
+	- cdns3,usb3-phy
+
+ - cdns,on-chip-buff-size : size of memory intended as internal memory for endpoints
+	buffers expressed in KB
 
 Example:
 	usb@f3000000 {
-		compatible = "cdns,usb3-1.0.1";
-		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 */
+		compatible = "cdns,usb3";
+		interrupts = <GIC_USB_IRQ 7 IRQ_TYPE_LEVEL_HIGH>,
+				<GIC_USB_IRQ  7 IRQ_TYPE_LEVEL_HIGH>,
+				<GIC_USB_IRQ  8 IRQ_TYPE_LEVEL_HIGH>;
+		interrupt-names = "host", "peripheral", "otg";
+		reg = <0xf3000000 0x10000>,	/* memory area for HOST registers */
+			<0xf3010000 0x10000>,	/* memory area for DEVICE registers */
+			<0xf3020000 0x10000>;	/* memory area for OTG/DRD registers */
 		reg-names = "xhci", "dev", "otg";
+		phys = <&usb2_phy>, <&usb3_phy>;
+		phy-names = "cdns3,usb2-phy", "cnds3,usb3-phy";
 	};

+ 1 - 1
arch/arm64/boot/dts/ti/k3-j721e-common-proc-board.dts

@@ -567,7 +567,7 @@
 	dr_mode = "otg";
 	maximum-speed = "super-speed";
 	phys = <&serdes3_usb_link>;
-	phy-names = "cdns3,usbphy";
+	phy-names = "cdns3,usb3-phy";
 };
 
 &usbss1 {

+ 3 - 2
arch/arm64/boot/dts/ti/k3-j721e-main.dtsi

@@ -1877,6 +1877,7 @@
 	usbss0: cdns_usb@4104000 {
 		compatible = "ti,j721e-usb";
 		reg = <0x00 0x4104000 0x00 0x100>;
+		dma-coherent;
 		power-domains = <&k3_pds 288 TI_SCI_PD_EXCLUSIVE>;
 		clocks = <&k3_clks 288 15>, <&k3_clks 288 3>;
 		clock-names = "usb2_refclk", "lpm_clk";
@@ -1892,7 +1893,7 @@
 		};
 
 		usb0: usb@6000000 {
-			compatible = "cdns,usb3-1.0.1";
+			compatible = "cdns,usb3";
 			reg = <0x00 0x6000000 0x00 0x10000>,
 			      <0x00 0x6010000 0x00 0x10000>,
 			      <0x00 0x6020000 0x00 0x10000>;
@@ -1927,7 +1928,7 @@
 		};
 
 		usb1: usb@6400000 {
-			compatible = "cdns,usb3-1.0.1";
+			compatible = "cdns,usb3";
 			reg = <0x00 0x6400000 0x00 0x10000>,
 			      <0x00 0x6410000 0x00 0x10000>,
 			      <0x00 0x6420000 0x00 0x10000>;

+ 15 - 1
drivers/net/ethernet/ti/am65-cpsw-nuss.c

@@ -165,6 +165,9 @@ void am65_cpsw_nuss_adjust_link(struct net_device *ndev)
 
 		if (phy->speed == 1000)
 			mac_control |= CPSW_SL_CTL_GIG;
+		if (phy->speed == 10 && phy_interface_is_rgmii(phy))
+			/* Can be used with in band mode only */
+			mac_control |= CPSW_SL_CTL_EXT_EN;
 		if (phy->duplex)
 			mac_control |= CPSW_SL_CTL_FULLDUPLEX;
 
@@ -1242,10 +1245,21 @@ static int am65_cpsw_nuss_hwtstamp_set(struct net_device *ndev,
 	case HWTSTAMP_FILTER_NONE:
 		port->rx_ts_enabled = false;
 		break;
+	case HWTSTAMP_FILTER_ALL:
+	case HWTSTAMP_FILTER_SOME:
 	case HWTSTAMP_FILTER_PTP_V1_L4_EVENT:
+	case HWTSTAMP_FILTER_PTP_V1_L4_SYNC:
+	case HWTSTAMP_FILTER_PTP_V1_L4_DELAY_REQ:
 	case HWTSTAMP_FILTER_PTP_V2_L4_EVENT:
+	case HWTSTAMP_FILTER_PTP_V2_L4_SYNC:
+	case HWTSTAMP_FILTER_PTP_V2_L4_DELAY_REQ:
+	case HWTSTAMP_FILTER_PTP_V2_L2_EVENT:
+	case HWTSTAMP_FILTER_PTP_V2_L2_SYNC:
+	case HWTSTAMP_FILTER_PTP_V2_L2_DELAY_REQ:
 	case HWTSTAMP_FILTER_PTP_V2_EVENT:
-	case HWTSTAMP_FILTER_ALL:
+	case HWTSTAMP_FILTER_PTP_V2_SYNC:
+	case HWTSTAMP_FILTER_PTP_V2_DELAY_REQ:
+	case HWTSTAMP_FILTER_NTP_ALL:
 		port->rx_ts_enabled = true;
 		cfg.rx_filter = HWTSTAMP_FILTER_ALL;
 		break;

+ 4 - 2
drivers/usb/cdns3/Kconfig

@@ -1,6 +1,8 @@
 config USB_CDNS3
 	tristate "Cadence USB3 Dual-Role Controller"
 	depends on USB_SUPPORT && (USB || USB_GADGET) && HAS_DMA
+	select USB_XHCI_PLATFORM if USB_XHCI_HCD
+	select USB_ROLE_SWITCH
 	help
 	  Say Y here if your system has a Cadence USB3 dual-role controller.
 	  It supports: dual-role switch, Host-only, and Peripheral-only.
@@ -12,7 +14,7 @@ if USB_CDNS3
 
 config USB_CDNS3_GADGET
 	bool "Cadence USB3 device controller"
-	depends on USB_GADGET
+	depends on USB_GADGET=y || USB_GADGET=USB_CDNS3
 	help
 	  Say Y here to enable device controller functionality of the
 	  Cadence USBSS-DEV driver.
@@ -22,7 +24,7 @@ config USB_CDNS3_GADGET
 
 config USB_CDNS3_HOST
 	bool "Cadence USB3 host controller"
-	depends on USB_XHCI_HCD
+	depends on USB=y || USB=USB_CDNS3
 	help
 	  Say Y here to enable host controller functionality of the
 	  Cadence driver.

+ 5 - 3
drivers/usb/cdns3/Makefile

@@ -5,11 +5,13 @@ CFLAGS_trace.o				:= -I$(src)
 cdns3-y					:= core.o drd.o
 
 obj-$(CONFIG_USB_CDNS3)			+= cdns3.o
-ifneq ($(CONFIG_DEBUG_FS),)
-	cdns3-y				+= debugfs.o
+cdns3-$(CONFIG_USB_CDNS3_GADGET)	+= gadget.o ep0.o
+
+ifneq ($(CONFIG_USB_CDNS3_GADGET),)
+cdns3-$(CONFIG_TRACING)			+= trace.o
 endif
 
-cdns3-$(CONFIG_USB_CDNS3_GADGET)	+= gadget.o ep0.o trace.o
 cdns3-$(CONFIG_USB_CDNS3_HOST)		+= host.o
+
 obj-$(CONFIG_USB_CDNS3_PCI_WRAP)	+= cdns3-pci-wrap.o
 obj-$(CONFIG_USB_CDNS3_TI)		+= cdns3-ti.o

+ 116 - 58
drivers/usb/cdns3/cdns3-pci-wrap.c

@@ -16,18 +16,20 @@
 
 struct cdns3_wrap {
 	struct platform_device *plat_dev;
-	struct pci_dev *hg_dev;
-	struct resource dev_res[4];
+	struct resource dev_res[6];
+	int devfn;
 };
 
-#define RES_IRQ_ID		0
-#define RES_HOST_ID		1
-#define RES_DEV_ID		2
-#define RES_DRD_ID		3
+#define RES_IRQ_HOST_ID		0
+#define RES_IRQ_PERIPHERAL_ID	1
+#define RES_IRQ_OTG_ID		2
+#define RES_HOST_ID		3
+#define RES_DEV_ID		4
+#define RES_DRD_ID		5
 
 #define PCI_BAR_HOST		0
 #define PCI_BAR_DEV		2
-#define PCI_BAR_OTG		4
+#define PCI_BAR_OTG		0
 
 #define PCI_DEV_FN_HOST_DEVICE	0
 #define PCI_DEV_FN_OTG		1
@@ -38,19 +40,48 @@ struct cdns3_wrap {
 #define CDNS_VENDOR_ID		0x17cd
 #define CDNS_DEVICE_ID		0x0100
 
+static struct pci_dev *cdns3_get_second_fun(struct pci_dev *pdev)
+{
+	struct pci_dev *func;
+
+	/*
+	 * Gets the second function.
+	 * It's little tricky, but this platform has two function.
+	 * The fist keeps resources for Host/Device while the second
+	 * keeps resources for DRD/OTG.
+	 */
+	func = pci_get_device(pdev->vendor, pdev->device, NULL);
+	if (unlikely(!func))
+		return NULL;
+
+	if (func->devfn == pdev->devfn) {
+		func = pci_get_device(pdev->vendor, pdev->device, func);
+		if (unlikely(!func))
+			return NULL;
+	}
+
+	return func;
+}
+
 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;
+	struct pci_dev *func;
 	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)
+	if (!id || (pdev->devfn != PCI_DEV_FN_HOST_DEVICE &&
+		    pdev->devfn != PCI_DEV_FN_OTG))
+		return -EINVAL;
+
+	func = cdns3_get_second_fun(pdev);
+	if (unlikely(!func))
 		return -EINVAL;
 
 	err = pcim_enable_device(pdev);
@@ -60,69 +91,96 @@ static int cdns3_pci_probe(struct pci_dev *pdev,
 	}
 
 	pci_set_master(pdev);
-	wrap = devm_kzalloc(&pdev->dev, sizeof(*wrap), GFP_KERNEL);
-	if (!wrap) {
-		pci_disable_device(pdev);
-		return -ENOMEM;
+
+	if (pci_is_enabled(func)) {
+		wrap = pci_get_drvdata(func);
+	} else {
+		wrap = kzalloc(sizeof(*wrap), GFP_KERNEL);
+		if (!wrap) {
+			pci_disable_device(pdev);
+			return -ENOMEM;
+		}
 	}
 
-	/* function 0: host(BAR_0) + device(BAR_1) + otg(BAR_2)). */
-	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 = "dev";
-	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 = "xhci";
-	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 = "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)) {
-		pci_disable_device(pdev);
-		return PTR_ERR(wrap->plat_dev);
+	if (pdev->devfn == PCI_DEV_FN_HOST_DEVICE) {
+		/* function 0: host(BAR_0) + device(BAR_1).*/
+		dev_dbg(&pdev->dev, "Initialize Device resources\n");
+		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 = "dev";
+		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 = "xhci";
+		res[RES_HOST_ID].flags = IORESOURCE_MEM;
+		dev_dbg(&pdev->dev, "USBSS-XHCI physical base addr: %pa\n",
+			&res[RES_HOST_ID].start);
+
+		/* Interrupt for XHCI */
+		wrap->dev_res[RES_IRQ_HOST_ID].start = pdev->irq;
+		wrap->dev_res[RES_IRQ_HOST_ID].name = "host";
+		wrap->dev_res[RES_IRQ_HOST_ID].flags = IORESOURCE_IRQ;
+
+		/* Interrupt device. It's the same as for HOST. */
+		wrap->dev_res[RES_IRQ_PERIPHERAL_ID].start = pdev->irq;
+		wrap->dev_res[RES_IRQ_PERIPHERAL_ID].name = "peripheral";
+		wrap->dev_res[RES_IRQ_PERIPHERAL_ID].flags = IORESOURCE_IRQ;
+	} else {
+		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 = "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 for OTG/DRD. */
+		wrap->dev_res[RES_IRQ_OTG_ID].start = pdev->irq;
+		wrap->dev_res[RES_IRQ_OTG_ID].name = "otg";
+		wrap->dev_res[RES_IRQ_OTG_ID].flags = IORESOURCE_IRQ;
 	}
 
-	pci_set_drvdata(pdev, wrap);
+	if (pci_is_enabled(func)) {
+		/* 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;
+		wrap->devfn  = 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)) {
+			pci_disable_device(pdev);
+			kfree(wrap);
+			return PTR_ERR(wrap->plat_dev);
+		}
+	}
 
+	pci_set_drvdata(pdev, wrap);
 	return err;
 }
 
 static void cdns3_pci_remove(struct pci_dev *pdev)
 {
-	struct cdns3_wrap *wrap = (struct cdns3_wrap *)pci_get_drvdata(pdev);
+	struct cdns3_wrap *wrap;
+	struct pci_dev *func;
+
+	func = cdns3_get_second_fun(pdev);
+
+	wrap = (struct cdns3_wrap *)pci_get_drvdata(pdev);
+	if (wrap->devfn == pdev->devfn)
+		platform_device_unregister(wrap->plat_dev);
 
-	platform_device_unregister(wrap->plat_dev);
+	if (!pci_is_enabled(func))
+		kfree(wrap);
 }
 
 static const struct pci_device_id cdns3_pci_ids[] = {

+ 322 - 191
drivers/usb/cdns3/core.c

@@ -4,11 +4,14 @@
  *
  * Copyright (C) 2018-2019 Cadence.
  * Copyright (C) 2017-2018 NXP
+ * Copyright (C) 2019 Texas Instruments
  *
  * Author: Peter Chen <peter.chen@nxp.com>
  *         Pawel Laszczak <pawell@cadence.com>
+ *         Roger Quadros <rogerq@ti.com>
  */
 
+#include <linux/dma-mapping.h>
 #include <linux/module.h>
 #include <linux/kernel.h>
 #include <linux/platform_device.h>
@@ -21,55 +24,27 @@
 #include "host-export.h"
 #include "gadget-export.h"
 #include "drd.h"
-#include "debug.h"
 
-/**
- * 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;
-}
+static int cdns3_idle_init(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]);
+	WARN_ON(!cdns->roles[cdns->role]);
 	return cdns->roles[cdns->role];
 }
 
-static int cdns3_role_start(struct cdns3 *cdns, enum cdns3_roles role)
+static int cdns3_role_start(struct cdns3 *cdns, enum usb_role role)
 {
 	int ret;
 
-	if (WARN_ON(role >= CDNS3_ROLE_END))
+	if (WARN_ON(role > USB_ROLE_DEVICE))
 		return 0;
 
+	mutex_lock(&cdns->mutex);
+	cdns->role = role;
+	mutex_unlock(&cdns->mutex);
+
 	if (!cdns->roles[role])
 		return -ENXIO;
 
@@ -77,22 +52,20 @@ static int cdns3_role_start(struct cdns3 *cdns, enum cdns3_roles role)
 		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)
+static void cdns3_role_stop(struct cdns3 *cdns)
 {
-	enum cdns3_roles role = cdns->role;
+	enum usb_role role = cdns->role;
 
-	if (role >= CDNS3_ROLE_END) {
-		WARN_ON(role > CDNS3_ROLE_END);
+	if (WARN_ON(role > USB_ROLE_DEVICE))
 		return;
-	}
 
 	if (cdns->roles[role]->state == CDNS3_ROLE_STATE_INACTIVE)
 		return;
@@ -109,39 +82,7 @@ static void cdns3_exit_roles(struct cdns3 *cdns)
 	cdns3_drd_exit(cdns);
 }
 
-enum cdns3_roles cdsn3_get_real_role(struct cdns3 *cdns);
-
-static int cdns3_idle_role_start(struct cdns3 *cnds)
-{
-	/* Hold PHY in RESET */
-	return 0;
-}
-
-static void cdns3_idle_role_stop(struct cdns3 *cdns)
-{
-	/* Program Lane swap and bring PHY out of RESET */
-	phy_reset(cdns->phy);
-}
-
-static int cdns3_idle_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_idle_role_start;
-	rdrv->stop = cdns3_idle_role_stop;
-	rdrv->state = CDNS3_ROLE_STATE_INACTIVE;
-	rdrv->suspend = NULL;
-	rdrv->resume = NULL;
-	rdrv->name = "idle";
-
-	cdns->roles[CDNS3_ROLE_IDLE] = rdrv;
-
-	return 0;
-}
+static enum usb_role cdsn3_hw_role_state_machine(struct cdns3 *cdns);
 
 /**
  * cdns3_core_init_role - initialize role of operation
@@ -157,7 +98,7 @@ static int cdns3_core_init_role(struct cdns3 *cdns)
 	int ret = 0;
 
 	dr_mode = usb_get_dr_mode(dev);
-	cdns->role = CDNS3_ROLE_END;
+	cdns->role = USB_ROLE_NONE;
 
 	/*
 	 * If driver can't read mode by means of usb_get_dr_mode function then
@@ -180,6 +121,10 @@ static int cdns3_core_init_role(struct cdns3 *cdns)
 	 */
 	best_dr_mode = cdns->dr_mode;
 
+	ret = cdns3_idle_init(cdns);
+	if (ret)
+		return ret;
+
 	if (dr_mode == USB_DR_MODE_OTG) {
 		best_dr_mode = cdns->dr_mode;
 	} else if (cdns->dr_mode == USB_DR_MODE_OTG) {
@@ -191,10 +136,6 @@ static int cdns3_core_init_role(struct cdns3 *cdns)
 
 	dr_mode = best_dr_mode;
 
-	ret = cdns3_idle_init(cdns);
-	if (ret)
-		return ret;
-
 	if (dr_mode == USB_DR_MODE_OTG || dr_mode == USB_DR_MODE_HOST) {
 		ret = cdns3_host_init(cdns);
 		if (ret) {
@@ -213,29 +154,35 @@ static int cdns3_core_init_role(struct cdns3 *cdns)
 		}
 	}
 
-	cdns->desired_dr_mode = dr_mode;
 	cdns->dr_mode = dr_mode;
 
-	/*
-	 * desired_dr_mode might have changed so we need to update
-	 * the controller configuration"?
-	 */
 	ret = cdns3_drd_update_mode(cdns);
 	if (ret)
 		goto err;
 
-	cdns->role = cdsn3_get_real_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);
+	/* Initialize idle role to start with */
+	ret = cdns3_role_start(cdns, USB_ROLE_NONE);
+	if (ret)
 		goto err;
-	}
 
-	/* switch role if needed */
-	if (dr_mode == USB_DR_MODE_OTG)
-		queue_work(system_freezable_wq, &cdns->role_switch_wq);
+	switch (cdns->dr_mode) {
+	case USB_DR_MODE_UNKNOWN:
+	case USB_DR_MODE_OTG:
+		ret = cdns3_hw_role_switch(cdns);
+		if (ret)
+			goto err;
+		break;
+	case USB_DR_MODE_PERIPHERAL:
+		ret = cdns3_role_start(cdns, USB_ROLE_DEVICE);
+		if (ret)
+			goto err;
+		break;
+	case USB_DR_MODE_HOST:
+		ret = cdns3_role_start(cdns, USB_ROLE_HOST);
+		if (ret)
+			goto err;
+		break;
+	}
 
 	return ret;
 err:
@@ -244,49 +191,48 @@ err:
 }
 
 /**
- * cdsn3_get_real_role - get real role of controller based on hardware settings.
- * @cdns: Pointer to cdns3 structure
+ * cdsn3_hw_role_state_machine  - role switch state machine based on hw events.
+ * @cdns: Pointer to controller structure.
  *
- * Returns role
+ * Returns next role to be entered based on hw events.
  */
-enum cdns3_roles cdsn3_get_real_role(struct cdns3 *cdns)
+static enum usb_role cdsn3_hw_role_state_machine(struct cdns3 *cdns)
 {
-	enum cdns3_roles role;
+	enum usb_role role;
 	int id, vbus;
 
-	if (cdns->current_dr_mode != USB_DR_MODE_OTG)
+	if (cdns->dr_mode != USB_DR_MODE_OTG)
 		goto not_otg;
 
 	id = cdns3_get_id(cdns);
 	vbus = cdns3_get_vbus(cdns);
 
-	dev_dbg(cdns->dev, "id: %d, vbus: %d\n", id, vbus);
-	/* Role change state machine
+	/*
+	 * Role change state machine
 	 * Inputs: ID, VBUS
 	 * Previous state: cdns->role
 	 * Next state: role
 	 */
-
 	role = cdns->role;
+
 	switch (role) {
-	case CDNS3_ROLE_IDLE: /* from IDLE, we can change to HOST or GADGET */
+	case USB_ROLE_NONE:
+		/*
+		 * Driver treats USB_ROLE_NONE synonymous to IDLE state from
+		 * controller specification.
+		 */
 		if (!id)
-			role = CDNS3_ROLE_HOST;
+			role = USB_ROLE_HOST;
 		else if (vbus)
-			role = CDNS3_ROLE_GADGET;
+			role = USB_ROLE_DEVICE;
 		break;
-
-	case CDNS3_ROLE_HOST: /* from HOST, we can only change to IDLE */
+	case USB_ROLE_HOST: /* from HOST, we can only change to NONE */
 		if (id)
-			role = CDNS3_ROLE_IDLE;
+			role = USB_ROLE_NONE;
 		break;
-
-	case CDNS3_ROLE_GADGET: /* from GADGET, we can only change to IDLE */
+	case USB_ROLE_DEVICE: /* from GADGET, we can only change to NONE*/
 		if (!vbus)
-			role = CDNS3_ROLE_IDLE;
-		break;
-	case CDNS3_ROLE_END:	/* only at initialization, move to IDLE */
-		role = CDNS3_ROLE_IDLE;
+			role = USB_ROLE_NONE;
 		break;
 	}
 
@@ -296,60 +242,75 @@ enum cdns3_roles cdsn3_get_real_role(struct cdns3 *cdns)
 
 not_otg:
 	if (cdns3_is_host(cdns))
-		role = CDNS3_ROLE_HOST;
+		role = USB_ROLE_HOST;
 	if (cdns3_is_device(cdns))
-		role = CDNS3_ROLE_GADGET;
+		role = USB_ROLE_DEVICE;
 
 	return role;
 }
 
+static int cdns3_idle_role_start(struct cdns3 *cdns)
+{
+	return 0;
+}
+
+static void cdns3_idle_role_stop(struct cdns3 *cdns)
+{
+	/* Program Lane swap and bring PHY out of RESET */
+	phy_reset(cdns->usb3_phy);
+}
+
+static int cdns3_idle_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_idle_role_start;
+	rdrv->stop = cdns3_idle_role_stop;
+	rdrv->state = CDNS3_ROLE_STATE_INACTIVE;
+	rdrv->suspend = NULL;
+	rdrv->resume = NULL;
+	rdrv->name = "idle";
+
+	cdns->roles[USB_ROLE_NONE] = rdrv;
+
+	return 0;
+}
+
 /**
- * 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
+ * cdns3_hw_role_switch - switch roles based on HW state
+ * @cdns3: controller
  */
-static void cdns3_role_switch(struct work_struct *work)
+int cdns3_hw_role_switch(struct cdns3 *cdns)
 {
-	enum cdns3_roles role = CDNS3_ROLE_END;
-	struct cdns3_role_driver *role_drv;
-	enum cdns3_roles current_role;
-	struct cdns3 *cdns;
+	enum usb_role real_role, current_role;
 	int ret = 0;
 
-	cdns = container_of(work, struct cdns3, role_switch_wq);
+	/* Do nothing if role based on syfs. */
+	if (cdns->role_override)
+		return 0;
 
 	pm_runtime_get_sync(cdns->dev);
 
-	role = cdsn3_get_real_role(cdns);
-
-	role_drv = cdns3_get_current_role_driver(cdns);
-
-	/* Disable current role if requested from debugfs */
-	if (cdns->debug_disable && role_drv->state == CDNS3_ROLE_STATE_ACTIVE) {
-		cdns3_role_stop(cdns);
-		goto exit;
-	}
+	current_role = cdns->role;
+	real_role = cdsn3_hw_role_state_machine(cdns);
 
 	/* Do nothing if nothing changed */
-	if (cdns->role == role && role_drv->state == CDNS3_ROLE_STATE_ACTIVE)
+	if (current_role == real_role)
 		goto exit;
 
 	cdns3_role_stop(cdns);
 
-	role = cdsn3_get_real_role(cdns);
-
-	current_role = cdns->role;
-	dev_dbg(cdns->dev, "Switching role");
+	dev_dbg(cdns->dev, "Switching role %d -> %d", current_role, real_role);
 
-	ret = cdns3_role_start(cdns, role);
+	ret = cdns3_role_start(cdns, real_role);
 	if (ret) {
 		/* Back to current role */
 		dev_err(cdns->dev, "set %d has failed, back to %d\n",
-			role, current_role);
+			real_role, current_role);
 		ret = cdns3_role_start(cdns, current_role);
 		if (ret)
 			dev_err(cdns->dev, "back to %d failed too\n",
@@ -357,8 +318,115 @@ static void cdns3_role_switch(struct work_struct *work)
 	}
 exit:
 	pm_runtime_put_sync(cdns->dev);
+	return ret;
 }
 
+/**
+ * cdsn3_role_get - get current role of controller.
+ *
+ * @dev: Pointer to device structure
+ *
+ * Returns role
+ */
+static enum usb_role cdns3_role_get(struct device *dev)
+{
+	struct cdns3 *cdns = dev_get_drvdata(dev);
+
+	return cdns->role;
+}
+
+/**
+ * cdns3_role_set - set current role of controller.
+ *
+ * @dev: pointer to device object
+ * @role - the previous role
+ * Handles below events:
+ * - Role switch for dual-role devices
+ * - USB_ROLE_GADGET <--> USB_ROLE_NONE for peripheral-only devices
+ */
+static int cdns3_role_set(struct device *dev, enum usb_role role)
+{
+	struct cdns3 *cdns = dev_get_drvdata(dev);
+	int ret = 0;
+
+	pm_runtime_get_sync(cdns->dev);
+
+	/*
+	 * FIXME: switch role framework should be extended to meet
+	 * requirements. Driver assumes that role can be controlled
+	 * by SW or HW. Temporary workaround is to use USB_ROLE_NONE to
+	 * switch from SW to HW control.
+	 *
+	 * For dr_mode == USB_DR_MODE_OTG:
+	 *	if user sets USB_ROLE_HOST or USB_ROLE_DEVICE then driver
+	 *	sets role_override flag and forces that role.
+	 *	if user sets USB_ROLE_NONE, driver clears role_override and lets
+	 *	HW state machine take over.
+	 *
+	 * For dr_mode != USB_DR_MODE_OTG:
+	 *	Assumptions:
+	 *	1. Restricted user control between NONE and dr_mode.
+	 *	2. Driver doesn't need to rely on role_override flag.
+	 *	3. Driver needs to ensure that HW state machine is never called
+	 *	   if dr_mode != USB_DR_MODE_OTG.
+	 */
+	if (role == USB_ROLE_NONE)
+		cdns->role_override = 0;
+	else
+		cdns->role_override = 1;
+
+	/*
+	 * HW state might have changed so driver need to trigger
+	 * HW state machine if dr_mode == USB_DR_MODE_OTG.
+	 */
+	if (!cdns->role_override && cdns->dr_mode == USB_DR_MODE_OTG) {
+		cdns3_hw_role_switch(cdns);
+		goto pm_put;
+	}
+
+	if (cdns->role == role)
+		goto pm_put;
+
+	if (cdns->dr_mode == USB_DR_MODE_HOST) {
+		switch (role) {
+		case USB_ROLE_NONE:
+		case USB_ROLE_HOST:
+			break;
+		default:
+			ret = -EPERM;
+			goto pm_put;
+		}
+	}
+
+	if (cdns->dr_mode == USB_DR_MODE_PERIPHERAL) {
+		switch (role) {
+		case USB_ROLE_NONE:
+		case USB_ROLE_DEVICE:
+			break;
+		default:
+			ret = -EPERM;
+			goto pm_put;
+		}
+	}
+
+	cdns3_role_stop(cdns);
+	ret = cdns3_role_start(cdns, role);
+	if (ret) {
+		dev_err(cdns->dev, "set role %d has failed\n", role);
+		ret = -EPERM;
+	}
+
+pm_put:
+	pm_runtime_put_sync(cdns->dev);
+	return ret;
+}
+
+static const struct usb_role_switch_desc cdns3_switch_desc = {
+	.set = cdns3_role_set,
+	.get = cdns3_role_get,
+	.allow_userspace_control = true,
+};
+
 /**
  * cdns3_probe - probe for cdns3 core device
  * @pdev: Pointer to cdns3 core platform device
@@ -373,6 +441,12 @@ static int cdns3_probe(struct platform_device *pdev)
 	void __iomem *regs;
 	int ret;
 
+	ret = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(32));
+	if (ret) {
+		dev_err(dev, "error setting dma mask: %d\n", ret);
+		return -ENODEV;
+	}
+
 	cdns = devm_kzalloc(dev, sizeof(*cdns), GFP_KERNEL);
 	if (!cdns)
 		return -ENOMEM;
@@ -383,11 +457,8 @@ static int cdns3_probe(struct platform_device *pdev)
 
 	res = platform_get_resource_byname(pdev, IORESOURCE_IRQ, "host");
 	if (!res) {
-		platform_get_resource_byname(pdev, IORESOURCE_IRQ, 0);
-		if (!res) {
-			dev_err(dev, "missing host IRQ\n");
-			return -ENODEV;
-		}
+		dev_err(dev, "missing host IRQ\n");
+		return -ENODEV;
 	}
 
 	cdns->xhci_res[0] = *res;
@@ -404,14 +475,8 @@ static int cdns3_probe(struct platform_device *pdev)
 	if (cdns->dev_irq == -EPROBE_DEFER)
 		return cdns->dev_irq;
 
-	if (cdns->dev_irq < 0) {
-		cdns->dev_irq = platform_get_irq(pdev, 0);
-		if (cdns->dev_irq < 0) {
-			if (cdns->dev_irq != -EPROBE_DEFER)
-				dev_err(dev, "couldn't get peripheral irq\n");
-			return cdns->dev_irq;
-		}
-	}
+	if (cdns->dev_irq < 0)
+		dev_err(dev, "couldn't get peripheral irq\n");
 
 	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "dev");
 	regs = devm_ioremap_resource(dev, res);
@@ -426,12 +491,8 @@ static int cdns3_probe(struct platform_device *pdev)
 		return cdns->otg_irq;
 
 	if (cdns->otg_irq < 0) {
-		cdns->otg_irq = platform_get_irq(pdev, 0);
-		if (cdns->otg_irq < 0) {
-			if (cdns->otg_irq != -EPROBE_DEFER)
-				dev_err(dev, "couldn't get otg irq\n");
-			return cdns->otg_irq;
-		}
+		dev_err(dev, "couldn't get otg irq\n");
+		return cdns->otg_irq;
 	}
 
 	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "otg");
@@ -444,34 +505,45 @@ static int cdns3_probe(struct platform_device *pdev)
 
 	mutex_init(&cdns->mutex);
 
-	cdns->phy = devm_phy_optional_get(dev, "cdns3,usbphy");
-	if (IS_ERR(cdns->phy))
-		return PTR_ERR(cdns->phy);
+	cdns->usb2_phy = devm_phy_optional_get(dev, "cdns3,usb2-phy");
+	if (IS_ERR(cdns->usb2_phy))
+		return PTR_ERR(cdns->usb2_phy);
 
-	ret = phy_init(cdns->phy);
-	if (ret) {
-		dev_err(dev, "phy_init error\n");
+	ret = phy_init(cdns->usb2_phy);
+	if (ret)
 		return ret;
-	}
 
-	ret = phy_power_on(cdns->phy);
-	if (ret) {
-		dev_err(dev, "phy_power_on error\n");
-		phy_exit(cdns->phy);
-		return ret;
-	}
+	cdns->usb3_phy = devm_phy_optional_get(dev, "cdns3,usb3-phy");
+	if (IS_ERR(cdns->usb3_phy))
+		return PTR_ERR(cdns->usb3_phy);
+
+	ret = phy_init(cdns->usb3_phy);
+	if (ret)
+		goto err1;
 
-	INIT_WORK(&cdns->role_switch_wq, cdns3_role_switch);
+	ret = phy_power_on(cdns->usb2_phy);
+	if (ret)
+		goto err2;
+
+	ret = phy_power_on(cdns->usb3_phy);
+	if (ret)
+		goto err3;
+
+	cdns->role_sw = usb_role_switch_register(dev, &cdns3_switch_desc);
+	if (IS_ERR(cdns->role_sw)) {
+		ret = PTR_ERR(cdns->role_sw);
+		dev_warn(dev, "Unable to register Role Switch\n");
+		goto err4;
+	}
 
 	ret = cdns3_drd_init(cdns);
 	if (ret)
-		goto err;
+		goto err5;
 
 	ret = cdns3_core_init_role(cdns);
 	if (ret)
-		goto err;
+		goto err5;
 
-	cdns3_debugfs_init(cdns);
 	device_set_wakeup_capable(dev, true);
 	pm_runtime_set_active(dev);
 	pm_runtime_enable(dev);
@@ -487,10 +559,19 @@ static int cdns3_probe(struct platform_device *pdev)
 	dev_dbg(dev, "Cadence USB3 core: probe succeed\n");
 
 	return 0;
+err5:
+	cdns3_drd_exit(cdns);
+	usb_role_switch_unregister(cdns->role_sw);
+err4:
+	phy_power_off(cdns->usb3_phy);
+
+err3:
+	phy_power_off(cdns->usb2_phy);
+err2:
+	phy_exit(cdns->usb3_phy);
+err1:
+	phy_exit(cdns->usb2_phy);
 
-err:
-	phy_power_off(cdns->phy);
-	phy_exit(cdns->phy);
 	return ret;
 }
 
@@ -507,17 +588,66 @@ static int cdns3_remove(struct platform_device *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_power_off(cdns->phy);
-	phy_exit(cdns->phy);
+	usb_role_switch_unregister(cdns->role_sw);
+	phy_power_off(cdns->usb2_phy);
+	phy_power_off(cdns->usb3_phy);
+	phy_exit(cdns->usb2_phy);
+	phy_exit(cdns->usb3_phy);
 	return 0;
 }
 
+#ifdef CONFIG_PM_SLEEP
+
+static int cdns3_suspend(struct device *dev)
+{
+	struct cdns3 *cdns = dev_get_drvdata(dev);
+	unsigned long flags;
+
+	if (cdns->role == USB_ROLE_HOST)
+		return 0;
+
+	if (pm_runtime_status_suspended(dev))
+		pm_runtime_resume(dev);
+
+	if (cdns->roles[cdns->role]->suspend) {
+		spin_lock_irqsave(&cdns->gadget_dev->lock, flags);
+		cdns->roles[cdns->role]->suspend(cdns, false);
+		spin_unlock_irqrestore(&cdns->gadget_dev->lock, flags);
+	}
+
+	return 0;
+}
+
+static int cdns3_resume(struct device *dev)
+{
+	struct cdns3 *cdns = dev_get_drvdata(dev);
+	unsigned long flags;
+
+	if (cdns->role == USB_ROLE_HOST)
+		return 0;
+
+	if (cdns->roles[cdns->role]->resume) {
+		spin_lock_irqsave(&cdns->gadget_dev->lock, flags);
+		cdns->roles[cdns->role]->resume(cdns, false);
+		spin_unlock_irqrestore(&cdns->gadget_dev->lock, flags);
+	}
+
+	pm_runtime_disable(dev);
+	pm_runtime_set_active(dev);
+	pm_runtime_enable(dev);
+
+	return 0;
+}
+#endif
+
+static const struct dev_pm_ops cdns3_pm_ops = {
+	SET_SYSTEM_SLEEP_PM_OPS(cdns3_suspend, cdns3_resume)
+};
+
 #ifdef CONFIG_OF
 static const struct of_device_id of_cdns3_match[] = {
-	{ .compatible = "cdns,usb3-1.0.0" },
-	{ .compatible = "cdns,usb3-1.0.1" },
+	{ .compatible = "cdns,usb3" },
 	{ },
 };
 MODULE_DEVICE_TABLE(of, of_cdns3_match);
@@ -529,6 +659,7 @@ static struct platform_driver cdns3_driver = {
 	.driver		= {
 		.name	= "cdns-usb3",
 		.of_match_table	= of_match_ptr(of_cdns3_match),
+		.pm	= &cdns3_pm_ops,
 	},
 };
 

+ 12 - 35
drivers/usb/cdns3/core.h

@@ -9,17 +9,12 @@
  *          Pawel Laszczak <pawell@cadence.com>
  */
 #include <linux/usb/otg.h>
+#include <linux/usb/role.h>
 
 #ifndef __LINUX_CDNS3_CORE_H
 #define __LINUX_CDNS3_CORE_H
 
 struct cdns3;
-enum cdns3_roles {
-	CDNS3_ROLE_IDLE = 0,
-	CDNS3_ROLE_HOST,
-	CDNS3_ROLE_GADGET,
-	CDNS3_ROLE_END,
-};
 
 /**
  * struct cdns3_role_driver - host/gadget role driver
@@ -59,28 +54,15 @@ struct cdns3_role_driver {
  * @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
+ * @usb2_phy: pointer to USB2 PHY
+ * @usb3_phy: pointer to USB3 PHY
  * @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         | USB_DR_MODE_OTG or USB_DR_MODE_HOST or
- *                                   | USB_DR_MODE_PERIPHERAL
- *           Desired_dr_role can be changed by means of debugfs.
- * @root: debugfs root folder pointer
- * @debug_disable:
+ * @role_sw: pointer to role switch object.
+ * @role_override: set 1 if role rely on SW.
  */
 struct cdns3 {
 	struct device			*dev;
@@ -98,24 +80,19 @@ struct cdns3 {
 
 	int				otg_irq;
 	int				dev_irq;
-	struct cdns3_role_driver	*roles[CDNS3_ROLE_END];
-	enum cdns3_roles		role;
+	struct cdns3_role_driver	*roles[USB_ROLE_DEVICE + 1];
+	enum usb_role			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;
+	struct phy			*usb2_phy;
+	struct phy			*usb3_phy;
 	/* 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 usb_role_switch		*role_sw;
+	int				role_override;
 };
 
-void cdns3_role_stop(struct cdns3 *cdns);
-int cdns3_handshake(void __iomem *ptr, u32 mask, u32 done, int usec);
+int cdns3_hw_role_switch(struct cdns3 *cdns);
 
 #endif /* __LINUX_CDNS3_CORE_H */

+ 6 - 13
drivers/usb/cdns3/debug.h

@@ -28,7 +28,12 @@ static inline char *cdns3_decode_usb_irq(char *str,
 		ret += sprintf(str + ret, "Disconnection ");
 	if (usb_ists & USB_ISTS_L2ENTI)
 		ret += sprintf(str + ret, "suspended ");
-
+	if (usb_ists & USB_ISTS_L1ENTI)
+		ret += sprintf(str + ret, "L1 enter ");
+	if (usb_ists & USB_ISTS_L1EXTI)
+		ret += sprintf(str + ret, "L1 exit ");
+	if (usb_ists & USB_ISTS_L2ENTI)
+		ret += sprintf(str + ret, "L2 enter ");
 	if (usb_ists & USB_ISTS_L2EXTI)
 		ret += sprintf(str + ret, "L2 exit ");
 	if (usb_ists & USB_ISTS_U3EXTI)
@@ -153,16 +158,4 @@ static inline char *cdns3_dbg_ring(struct cdns3_endpoint *priv_ep,
 	return str;
 }
 
-void cdns3_dbg(struct cdns3_device *priv_dev, const char *fmt, ...);
-
-#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*/

+ 0 - 173
drivers/usb/cdns3/debugfs.c

@@ -1,173 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0
-/*
- * Cadence USBSS DRD Controller DebugFS filer.
- *
- * Copyright (C) 2018-2019 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];
-	int ret = 0;
-
-	if (copy_from_user(&buf, ubuf, min_t(size_t, sizeof(buf) - 1, count)))
-		return -EFAULT;
-
-	if (cdns->debug_disable) {
-		dev_err(cdns->dev,
-			"Mode can't be changed when disable is set\n");
-		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;
-		cdns3_role_stop(cdns);
-		ret = cdns3_drd_update_mode(cdns);
-		if (ret)
-			return ret;
-
-		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;
-	bool disable;
-	char buf[16];
-
-	if (copy_from_user(&buf, ubuf, min_t(size_t, sizeof(buf) - 1, count)))
-		return -EFAULT;
-
-	if (kstrtobool(buf, &disable)) {
-		dev_err(cdns->dev, "wrong setting\n");
-		return -EINVAL;
-	}
-
-	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);
-}

+ 41 - 36
drivers/usb/cdns3/drd.c

@@ -3,13 +3,17 @@
  * Cadence USBSS DRD Driver.
  *
  * Copyright (C) 2018-2019 Cadence.
+ * Copyright (C) 2019 Texas Instruments
  *
  * Author: Pawel Laszczak <pawell@cadence.com>
+ *         Roger Quadros <rogerq@ti.com>
+ *
  *
  */
 #include <linux/kernel.h>
 #include <linux/interrupt.h>
 #include <linux/delay.h>
+#include <linux/iopoll.h>
 #include <linux/usb/otg.h>
 
 #include "gadget.h"
@@ -28,8 +32,6 @@ int cdns3_set_mode(struct cdns3 *cdns, enum usb_dr_mode mode)
 	int ret = 0;
 	u32 reg;
 
-	cdns->current_dr_mode = mode;
-
 	switch (mode) {
 	case USB_DR_MODE_PERIPHERAL:
 		break;
@@ -55,7 +57,6 @@ int cdns3_set_mode(struct cdns3 *cdns, enum usb_dr_mode mode)
 		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 -EINVAL;
 	}
@@ -69,6 +70,7 @@ int cdns3_get_id(struct cdns3 *cdns)
 
 	id = readl(&cdns->otg_regs->sts) & OTGSTS_ID_VALUE;
 	dev_dbg(cdns->dev, "OTG ID: %d", id);
+
 	return id;
 }
 
@@ -78,12 +80,13 @@ int cdns3_get_vbus(struct cdns3 *cdns)
 
 	vbus = !!(readl(&cdns->otg_regs->sts) & OTGSTS_VBUS_VALID);
 	dev_dbg(cdns->dev, "OTG VBUS: %d", vbus);
+
 	return vbus;
 }
 
 int cdns3_is_host(struct cdns3 *cdns)
 {
-	if (cdns->current_dr_mode == USB_DR_MODE_HOST)
+	if (cdns->dr_mode == USB_DR_MODE_HOST)
 		return 1;
 	else if (!cdns3_get_id(cdns))
 		return 1;
@@ -93,9 +96,9 @@ int cdns3_is_host(struct cdns3 *cdns)
 
 int cdns3_is_device(struct cdns3 *cdns)
 {
-	if (cdns->current_dr_mode == USB_DR_MODE_PERIPHERAL)
+	if (cdns->dr_mode == USB_DR_MODE_PERIPHERAL)
 		return 1;
-	else if (cdns->current_dr_mode == USB_DR_MODE_OTG)
+	else if (cdns->dr_mode == USB_DR_MODE_OTG)
 		if (cdns3_get_id(cdns))
 			return 1;
 
@@ -130,7 +133,7 @@ static void cdns3_otg_enable_irq(struct cdns3 *cdns)
  */
 int cdns3_drd_switch_host(struct cdns3 *cdns, int on)
 {
-	int ret;
+	int ret, val;
 	u32 reg = OTGCMD_OTG_DIS;
 
 	/* switch OTG core */
@@ -138,9 +141,9 @@ int cdns3_drd_switch_host(struct cdns3 *cdns, int on)
 		writel(OTGCMD_HOST_BUS_REQ | reg, &cdns->otg_regs->cmd);
 
 		dev_dbg(cdns->dev, "Waiting till Host mode is turned on\n");
-		ret = cdns3_handshake(&cdns->otg_regs->sts, OTGSTS_XHCI_READY,
-				      OTGSTS_XHCI_READY, 100000);
-
+		ret = readl_poll_timeout_atomic(&cdns->otg_regs->sts, val,
+						val & OTGSTS_XHCI_READY,
+						1, 100000);
 		if (ret) {
 			dev_err(cdns->dev, "timeout waiting for xhci_ready\n");
 			return ret;
@@ -150,9 +153,9 @@ int cdns3_drd_switch_host(struct cdns3 *cdns, int on)
 		       OTGCMD_DEV_POWER_OFF | OTGCMD_HOST_POWER_OFF,
 		       &cdns->otg_regs->cmd);
 		/* Waiting till H_IDLE state.*/
-		cdns3_handshake(&cdns->otg_regs->state,
-				OTGSTATE_HOST_STATE_MASK,
-				0, 2000000);
+		readl_poll_timeout_atomic(&cdns->otg_regs->state, val,
+					  !(val & OTGSTATE_HOST_STATE_MASK),
+					  1, 2000000);
 	}
 
 	return 0;
@@ -167,7 +170,7 @@ int cdns3_drd_switch_host(struct cdns3 *cdns, int on)
  */
 int cdns3_drd_switch_gadget(struct cdns3 *cdns, int on)
 {
-	int ret;
+	int ret, val;
 	u32 reg = OTGCMD_OTG_DIS;
 
 	/* switch OTG core */
@@ -176,9 +179,9 @@ int cdns3_drd_switch_gadget(struct cdns3 *cdns, int on)
 
 		dev_dbg(cdns->dev, "Waiting till Device mode is turned on\n");
 
-		ret = cdns3_handshake(&cdns->otg_regs->sts, OTGSTS_DEV_READY,
-				      OTGSTS_DEV_READY, 100000);
-
+		ret = readl_poll_timeout_atomic(&cdns->otg_regs->sts, val,
+						val & OTGSTS_DEV_READY,
+						1, 100000);
 		if (ret) {
 			dev_err(cdns->dev, "timeout waiting for dev_ready\n");
 			return ret;
@@ -193,8 +196,9 @@ int cdns3_drd_switch_gadget(struct cdns3 *cdns, int on)
 		       OTGCMD_DEV_POWER_OFF | OTGCMD_HOST_POWER_OFF,
 		       &cdns->otg_regs->cmd);
 		/* Waiting till DEV_IDLE state.*/
-		cdns3_handshake(&cdns->otg_regs->state, OTGSTATE_DEV_STATE_MASK,
-				0, 2000000);
+		readl_poll_timeout_atomic(&cdns->otg_regs->state, val,
+					  !(val & OTGSTATE_DEV_STATE_MASK),
+					  1, 2000000);
 	}
 
 	return 0;
@@ -232,10 +236,7 @@ int cdns3_drd_update_mode(struct cdns3 *cdns)
 {
 	int ret = 0;
 
-	if (cdns->desired_dr_mode == cdns->current_dr_mode)
-		return ret;
-
-	switch (cdns->desired_dr_mode) {
+	switch (cdns->dr_mode) {
 	case USB_DR_MODE_PERIPHERAL:
 		ret = cdns3_set_mode(cdns, USB_DR_MODE_PERIPHERAL);
 		break;
@@ -254,6 +255,15 @@ int cdns3_drd_update_mode(struct cdns3 *cdns)
 	return ret;
 }
 
+static irqreturn_t cdns3_drd_thread_irq(int irq, void *data)
+{
+	struct cdns3 *cdns = data;
+
+	cdns3_hw_role_switch(cdns);
+
+	return IRQ_HANDLED;
+}
+
 /**
  * cdns3_drd_irq - interrupt handler for OTG events
  *
@@ -280,20 +290,16 @@ static irqreturn_t cdns3_drd_irq(int irq, void *data)
 		dev_dbg(cdns->dev, "OTG IRQ: new ID: %d\n",
 			cdns3_get_id(cdns));
 
-		ret = IRQ_HANDLED;
+		ret = IRQ_WAKE_THREAD;
 	}
 
-	if (reg & OTGIEN_VBUSVALID_RISE_INT ||
-	    reg & OTGIEN_VBUSVALID_FALL_INT) {
+	if (reg & (OTGIEN_VBUSVALID_RISE_INT | OTGIEN_VBUSVALID_FALL_INT)) {
 		dev_dbg(cdns->dev, "OTG IRQ: new VBUS: %d\n",
 			cdns3_get_vbus(cdns));
 
-		ret = IRQ_HANDLED;
+		ret = IRQ_WAKE_THREAD;
 	}
 
-	if (ret == IRQ_HANDLED)
-		queue_work(system_freezable_wq, &cdns->role_switch_wq);
-
 	writel(~0, &cdns->otg_regs->ivect);
 	return ret;
 }
@@ -341,19 +347,17 @@ int cdns3_drd_init(struct cdns3 *cdns)
 	/* 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");
+		dev_dbg(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");
+		dev_dbg(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->otg_irq,
 					cdns3_drd_irq,
-					NULL, IRQF_SHARED,
+					cdns3_drd_thread_irq,
+					IRQF_SHARED,
 					dev_name(cdns->dev), cdns);
 
 	if (ret) {
@@ -372,5 +376,6 @@ int cdns3_drd_init(struct cdns3 *cdns)
 
 int cdns3_drd_exit(struct cdns3 *cdns)
 {
+	cdns3_otg_disable_irq(cdns);
 	return 0;
 }

+ 1 - 1
drivers/usb/cdns3/drd.h

@@ -162,6 +162,6 @@ int cdns3_drd_exit(struct cdns3 *cdns);
 int cdns3_drd_update_mode(struct cdns3 *cdns);
 int cdns3_drd_switch_gadget(struct cdns3 *cdns, int on);
 int cdns3_drd_switch_host(struct cdns3 *cdns, int on);
-
+int cdns3_set_mode(struct cdns3 *cdns, enum usb_dr_mode mode);
 
 #endif /* __LINUX_CDNS3_DRD */

+ 56 - 75
drivers/usb/cdns3/ep0.c

@@ -7,10 +7,11 @@
  *
  * Authors: Pawel Jez <pjez@cadence.com>,
  *          Pawel Laszczak <pawell@cadence.com>
- *	    Peter Chen <peter.chen@nxp.com>
+ *          Peter Chen <peter.chen@nxp.com>
  */
 
 #include <linux/usb/composite.h>
+#include <linux/iopoll.h>
 
 #include "gadget.h"
 #include "trace.h"
@@ -31,14 +32,25 @@ static struct usb_endpoint_descriptor cdns3_gadget_ep0_desc = {
  */
 static void cdns3_ep0_run_transfer(struct cdns3_device *priv_dev,
 				   dma_addr_t dma_addr,
-				   unsigned int length, int erdy)
+				   unsigned int length, int erdy, int zlp)
 {
 	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);
+	priv_ep->trb_pool[0].buffer = TRB_BUFFER(dma_addr);
+	priv_ep->trb_pool[0].length = TRB_LEN(length);
+
+	if (zlp) {
+		priv_ep->trb_pool[0].control = TRB_CYCLE | TRB_TYPE(TRB_NORMAL);
+		priv_ep->trb_pool[1].buffer = TRB_BUFFER(dma_addr);
+		priv_ep->trb_pool[1].length = TRB_LEN(0);
+		priv_ep->trb_pool[1].control = TRB_CYCLE | TRB_IOC |
+		    TRB_TYPE(TRB_NORMAL);
+	} else {
+		priv_ep->trb_pool[0].control = TRB_CYCLE | TRB_IOC |
+		    TRB_TYPE(TRB_NORMAL);
+		priv_ep->trb_pool[1].control = 0;
+	}
 
 	trace_cdns3_prepare_trb(priv_ep, priv_ep->trb_pool);
 
@@ -49,9 +61,12 @@ static void cdns3_ep0_run_transfer(struct cdns3_device *priv_dev,
 	trace_cdns3_doorbell_ep0(priv_dev->ep0_data_dir ? "ep0in" : "ep0out",
 				 readl(&regs->ep_traddr));
 
-	/* TRB should be prepared before starting transfer */
+	/* TRB should be prepared before starting transfer. */
 	writel(EP_CMD_DRDY, &regs->ep_cmd);
 
+	/* Resume controller before arming transfer. */
+	__cdns3_gadget_wakeup(priv_dev);
+
 	if (erdy)
 		writel(EP_CMD_ERDY, &priv_dev->regs->ep_cmd);
 }
@@ -82,7 +97,7 @@ 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);
+			       sizeof(struct usb_ctrlrequest), 0, 0);
 }
 
 static void cdns3_ep0_complete_setup(struct cdns3_device *priv_dev,
@@ -96,7 +111,7 @@ static void cdns3_ep0_complete_setup(struct cdns3_device *priv_dev,
 		list_del_init(&request->list);
 
 	if (send_stall) {
-		cdns3_dbg(priv_ep->cdns3_dev, "STALL for ep0\n");
+		trace_cdns3_halt(priv_ep, send_stall, 0);
 		/* set_stall on ep0 */
 		cdns3_select_ep(priv_dev, 0x00);
 		writel(EP_CMD_SSTALL, &priv_dev->regs->ep_cmd);
@@ -107,6 +122,8 @@ static void cdns3_ep0_complete_setup(struct cdns3_device *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_allow_enable_l1(priv_dev, 1);
 }
 
 /**
@@ -220,7 +237,6 @@ static int cdns3_req_ep0_get_status(struct cdns3_device *priv_dev,
 	__le16 *response_pkt;
 	u16 usb_status = 0;
 	u32 recip;
-	u32 reg;
 
 	recip = ctrl->bRequestType & USB_RECIP_MASK;
 
@@ -236,8 +252,6 @@ static int cdns3_req_ep0_get_status(struct cdns3_device *priv_dev,
 		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);
 
@@ -261,7 +275,7 @@ static int cdns3_req_ep0_get_status(struct cdns3_device *priv_dev,
 	*response_pkt = cpu_to_le16(usb_status);
 
 	cdns3_ep0_run_transfer(priv_dev, priv_dev->setup_dma,
-			       sizeof(*response_pkt), 1);
+			       sizeof(*response_pkt), 1, 0);
 	return 0;
 }
 
@@ -374,40 +388,12 @@ static int cdns3_ep0_feature_handle_endpoint(struct cdns3_device *priv_dev,
 
 	cdns3_select_ep(priv_dev, ctrl->wIndex);
 
-	if (set) {
-		cdns3_dbg(priv_ep->cdns3_dev, "Stall endpoint %s\n",
-			  priv_ep->name);
-		writel(EP_CMD_SSTALL, &priv_dev->regs->ep_cmd);
-		priv_ep->flags |= EP_STALL;
-	} else {
-		struct usb_request *request;
+	if (set)
+		__cdns3_gadget_ep_set_halt(priv_ep);
+	else if (!(priv_ep->flags & EP_WEDGE))
+		ret = __cdns3_gadget_ep_clear_halt(priv_ep);
 
-		if (priv_dev->eps[index]->flags & EP_WEDGE) {
-			cdns3_select_ep(priv_dev, 0x00);
-			return 0;
-		}
-
-		cdns3_dbg(priv_ep->cdns3_dev, "Clear Stalled endpoint %s\n",
-			  priv_ep->name);
-
-		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_dbg(priv_ep->cdns3_dev, "Resume transfer for %s\n",
-				  priv_ep->name);
-
-			cdns3_rearm_transfer(priv_ep, 1);
-		}
-	}
+	cdns3_select_ep(priv_dev, 0x00);
 
 	return ret;
 }
@@ -467,7 +453,7 @@ static int cdns3_req_ep0_set_sel(struct cdns3_device *priv_dev,
 		return -EINVAL;
 	}
 
-	cdns3_ep0_run_transfer(priv_dev, priv_dev->setup_dma, 6, 1);
+	cdns3_ep0_run_transfer(priv_dev, priv_dev->setup_dma, 6, 1, 0);
 	return 0;
 }
 
@@ -554,30 +540,6 @@ void cdns3_pending_setup_status_handler(struct work_struct *work)
 	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
@@ -674,7 +636,12 @@ void cdns3_check_ep0_interrupt_proceed(struct cdns3_device *priv_dev, int dir)
 
 	__pending_setup_status_handler(priv_dev);
 
-	if ((ep_sts_reg & EP_STS_SETUP)) {
+	if (ep_sts_reg & EP_STS_SETUP)
+		priv_dev->wait_for_setup = 1;
+
+	if (priv_dev->wait_for_setup && ep_sts_reg & EP_STS_IOC) {
+		priv_dev->wait_for_setup = 0;
+		cdns3_allow_enable_l1(priv_dev, 0);
 		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;
@@ -738,10 +705,9 @@ static int cdns3_gadget_ep0_queue(struct usb_ep *ep,
 	unsigned long flags;
 	int erdy_sent = 0;
 	int ret = 0;
+	u8 zlp = 0;
 
-	cdns3_dbg(priv_ep->cdns3_dev, "Queue to Ep0%s L: %d\n",
-		  priv_dev->ep0_data_dir ? "IN" : "OUT",
-		  request->length);
+	trace_cdns3_ep0_queue(priv_dev, request);
 
 	/* cancel the request if controller receive new SETUP packet. */
 	if (cdns3_check_new_setup(priv_dev))
@@ -758,6 +724,8 @@ static int cdns3_gadget_ep0_queue(struct usb_ep *ep,
 		if (!erdy_sent)
 			cdns3_ep0_complete_setup(priv_dev, 0, 1);
 
+		cdns3_allow_enable_l1(priv_dev, 1);
+
 		request->actual = 0;
 		priv_dev->status_completion_no_call = true;
 		priv_dev->pending_status_request = request;
@@ -790,7 +758,13 @@ static int cdns3_gadget_ep0_queue(struct usb_ep *ep,
 
 	request->status = -EINPROGRESS;
 	list_add_tail(&request->list, &priv_ep->pending_req_list);
-	cdns3_ep0_run_transfer(priv_dev, request->dma, request->length, 1);
+
+	if (request->zero && request->length &&
+	    (request->length % ep->maxpacket == 0))
+		zlp = 1;
+
+	cdns3_ep0_run_transfer(priv_dev, request->dma, request->length, 1, zlp);
+
 	spin_unlock_irqrestore(&priv_dev->lock, flags);
 
 	return ret;
@@ -860,6 +834,13 @@ void cdns3_ep0_config(struct cdns3_device *priv_dev)
 	/* init ep out */
 	cdns3_select_ep(priv_dev, USB_DIR_OUT);
 
+	if (priv_dev->dev_ver >= DEV_VER_V3) {
+		cdns3_set_register_bit(&priv_dev->regs->dtrans,
+				       BIT(0) | BIT(16));
+		cdns3_set_register_bit(&priv_dev->regs->tdl_from_trb,
+				       BIT(0) | BIT(16));
+	}
+
 	writel(EP_CFG_ENABLE | EP_CFG_MAXPKTSIZE(max_packet_size),
 	       &regs->ep_cfg);
 

+ 537 - 231
drivers/usb/cdns3/gadget.c

@@ -7,7 +7,7 @@
  *
  * Authors: Pawel Jez <pjez@cadence.com>,
  *          Pawel Laszczak <pawell@cadence.com>
- *	    Peter Chen <peter.chen@nxp.com>
+ *          Peter Chen <peter.chen@nxp.com>
  */
 
 /*
@@ -26,7 +26,7 @@
  * as valid during adding next TRB only if DMA is stopped or at TRBERR
  * interrupt.
  *
- * Issue has been fixed for DEV_VER_V2 version of controller.
+ * Issue has been fixed in DEV_VER_V3 version of controller.
  *
  * Work around 2:
  * Controller for OUT endpoints has shared on-chip buffers for all incoming
@@ -52,19 +52,20 @@
  * It's cause that buffer placed in on chip memory block transfer to other
  * endpoints.
  *
- * Issue has been fixed for DEV_VER_V2 version of controller.
+ * Issue has been fixed in DEV_VER_V2 version of controller.
  *
  */
 
 #include <linux/dma-mapping.h>
 #include <linux/usb/gadget.h>
 #include <linux/module.h>
+#include <linux/iopoll.h>
 
 #include "core.h"
-#include "drd.h"
 #include "gadget-export.h"
 #include "gadget.h"
 #include "trace.h"
+#include "drd.h"
 
 static int __cdns3_gadget_ep_queue(struct usb_ep *ep,
 				   struct usb_request *request,
@@ -92,6 +93,16 @@ u8 cdns3_ep_addr_to_index(u8 ep_addr)
 	return (((ep_addr & 0x7F)) + ((ep_addr & USB_DIR_IN) ? 16 : 0));
 }
 
+static int cdns3_get_dma_pos(struct cdns3_device *priv_dev,
+			     struct cdns3_endpoint *priv_ep)
+{
+	int dma_index;
+
+	dma_index = readl(&priv_dev->regs->ep_traddr) - priv_ep->trb_pool_dma;
+
+	return dma_index / TRB_SIZE;
+}
+
 /**
  * cdns3_next_request - returns next request from list
  * @list: list containing requests
@@ -172,10 +183,10 @@ int cdns3_allocate_trb_pool(struct cdns3_endpoint *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);
+		priv_ep->trb_pool = dma_alloc_coherent(priv_dev->sysdev,
+						       ring_size,
+						       &priv_ep->trb_pool_dma,
+						       GFP_DMA32 | GFP_ATOMIC);
 		if (!priv_ep->trb_pool)
 			return -ENOMEM;
 	} else {
@@ -186,11 +197,10 @@ int cdns3_allocate_trb_pool(struct cdns3_endpoint *priv_ep)
 		return 0;
 
 	priv_ep->num_trbs = ring_size / TRB_SIZE;
-	/* Initialize the last TRB as Link TRB */
+	/* 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;
+	link_trb->control = TRB_CYCLE | TRB_TYPE(TRB_LINK) | TRB_TOGGLE;
 
 	return 0;
 }
@@ -216,16 +226,18 @@ static void cdns3_free_trb_pool(struct cdns3_endpoint *priv_ep)
 static void cdns3_ep_stall_flush(struct cdns3_endpoint *priv_ep)
 {
 	struct cdns3_device *priv_dev = priv_ep->cdns3_dev;
+	int val;
 
-	cdns3_dbg(priv_ep->cdns3_dev, "Stall & flush endpoint %s\n",
-		  priv_ep->name);
+	trace_cdns3_halt(priv_ep, 1, 1);
 
 	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, 1000);
-	priv_ep->flags |= EP_STALL;
+	readl_poll_timeout_atomic(&priv_dev->regs->ep_cmd, val,
+				  !(val & EP_CMD_DFLUSH), 1, 1000);
+	priv_ep->flags |= EP_STALLED;
+	priv_ep->flags &= ~EP_STALL_PENDING;
 }
 
 /**
@@ -238,8 +250,9 @@ void cdns3_hw_reset_eps_config(struct cdns3_device *priv_dev)
 
 	cdns3_allow_enable_l1(priv_dev, 0);
 	priv_dev->hw_configured_flag = 0;
-	priv_dev->onchip_mem_allocated_size = 0;
+	priv_dev->onchip_used_size = 0;
 	priv_dev->out_mem_is_allocated = 0;
+	priv_dev->wait_for_setup = 0;
 }
 
 /**
@@ -401,6 +414,8 @@ static void cdns3_wa2_descmiss_copy_data(struct cdns3_endpoint *priv_ep,
 		chunk_end = descmiss_priv_req->flags & REQUEST_INTERNAL_CH;
 		length = request->actual + descmiss_req->actual;
 
+		request->status = descmiss_req->status;
+
 		if (length <= request->length) {
 			memcpy(&((u8 *)request->buf)[request->actual],
 			       descmiss_req->buf,
@@ -473,6 +488,7 @@ int cdns3_wa2_gadget_ep_queue(struct cdns3_device *priv_dev,
 		priv_ep->flags &= ~EP_QUIRK_EXTRA_BUF_DET;
 		reg = readl(&priv_dev->regs->ep_sts_en);
 		reg &= ~EP_STS_EN_DESCMISEN;
+		trace_cdns3_wa2(priv_ep, "workaround disabled\n");
 		writel(reg, &priv_dev->regs->ep_sts_en);
 	}
 
@@ -489,19 +505,30 @@ int cdns3_wa2_gadget_ep_queue(struct cdns3_device *priv_dev,
 		    !(priv_req->flags & REQUEST_INTERNAL)) {
 			cdns3_wa2_descmiss_copy_data(priv_ep,
 						     &priv_req->request);
+
+			trace_cdns3_wa2(priv_ep, "get internal stored data");
+
 			list_add_tail(&priv_req->request.list,
 				      &priv_ep->pending_req_list);
 			cdns3_gadget_giveback(priv_ep, priv_req,
 					      priv_req->request.status);
-			return deferred;
+
+			/*
+			 * Intentionally driver returns positive value as
+			 * correct value. It informs that transfer has
+			 * been finished.
+			 */
+			return EINPROGRESS;
 		}
 
 		/*
 		 * Driver will wait for completion DESCMISS transfer,
 		 * before starts new, not DESCMISS transfer.
 		 */
-		if (!pending_empty && !descmiss_empty)
+		if (!pending_empty && !descmiss_empty) {
+			trace_cdns3_wa2(priv_ep, "wait for pending transfer\n");
 			deferred = 1;
+		}
 
 		if (priv_req->flags & REQUEST_INTERNAL)
 			list_add_tail(&priv_req->list,
@@ -511,7 +538,7 @@ int cdns3_wa2_gadget_ep_queue(struct cdns3_device *priv_dev,
 	return deferred;
 }
 
-static void cdsn3_wa2_remove_old_request(struct cdns3_endpoint *priv_ep)
+static void cdns3_wa2_remove_old_request(struct cdns3_endpoint *priv_ep)
 {
 	struct cdns3_request *priv_req;
 
@@ -521,6 +548,8 @@ static void cdsn3_wa2_remove_old_request(struct cdns3_endpoint *priv_ep)
 		priv_req = cdns3_next_priv_request(&priv_ep->wa2_descmiss_req_list);
 		chain = !!(priv_req->flags & REQUEST_INTERNAL_CH);
 
+		trace_cdns3_wa2(priv_ep, "removes eldest request");
+
 		kfree(priv_req->request.buf);
 		cdns3_gadget_ep_free_request(&priv_ep->endpoint,
 					     &priv_req->request);
@@ -549,10 +578,10 @@ static void cdns3_wa2_descmissing_packet(struct cdns3_endpoint *priv_ep)
 		priv_ep->flags |= EP_QUIRK_EXTRA_BUF_EN;
 	}
 
-	cdns3_dbg(priv_ep->cdns3_dev, "WA2: Description Missing detected\n");
+	trace_cdns3_wa2(priv_ep, "Description Missing detected\n");
 
 	if (priv_ep->wa2_counter >= CDNS3_WA2_NUM_BUFFERS)
-		cdsn3_wa2_remove_old_request(priv_ep);
+		cdns3_wa2_remove_old_request(priv_ep);
 
 	request = cdns3_gadget_ep_alloc_request(&priv_ep->endpoint,
 						GFP_ATOMIC);
@@ -634,10 +663,6 @@ void cdns3_gadget_giveback(struct cdns3_endpoint *priv_ep,
 			return;
 	}
 
-	/* 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,
@@ -651,11 +676,10 @@ void cdns3_gadget_giveback(struct cdns3_endpoint *priv_ep,
 
 void cdns3_wa1_restore_cycle_bit(struct cdns3_endpoint *priv_ep)
 {
-	struct cdns3_device *priv_dev = priv_ep->cdns3_dev;
-
 	/* Work around for stale data address in TRB*/
 	if (priv_ep->wa1_set) {
-		cdns3_dbg(priv_dev, "WA1: update cycle bit\n");
+		trace_cdns3_wa1(priv_ep, "restore cycle bit");
+
 		priv_ep->wa1_set = 0;
 		priv_ep->wa1_trb_index = 0xFFFF;
 		if (priv_ep->wa1_cycle_bit) {
@@ -668,6 +692,35 @@ void cdns3_wa1_restore_cycle_bit(struct cdns3_endpoint *priv_ep)
 	}
 }
 
+static void cdns3_free_aligned_request_buf(struct work_struct *work)
+{
+	struct cdns3_device *priv_dev = container_of(work, struct cdns3_device,
+					aligned_buf_wq);
+	struct cdns3_aligned_buf *buf, *tmp;
+	unsigned long flags;
+
+	spin_lock_irqsave(&priv_dev->lock, flags);
+
+	list_for_each_entry_safe(buf, tmp, &priv_dev->aligned_buf_list, list) {
+		if (!buf->in_use) {
+			list_del(&buf->list);
+
+			/*
+			 * Re-enable interrupts to free DMA capable memory.
+			 * Driver can't free this memory with disabled
+			 * interrupts.
+			 */
+			spin_unlock_irqrestore(&priv_dev->lock, flags);
+			dma_free_coherent(priv_dev->sysdev, buf->size,
+					  buf->buf, buf->dma);
+			kfree(buf);
+			spin_lock_irqsave(&priv_dev->lock, flags);
+		}
+	}
+
+	spin_unlock_irqrestore(&priv_dev->lock, flags);
+}
+
 static int cdns3_prepare_aligned_request_buf(struct cdns3_request *priv_req)
 {
 	struct cdns3_endpoint *priv_ep = priv_req->priv_ep;
@@ -699,7 +752,8 @@ static int cdns3_prepare_aligned_request_buf(struct cdns3_request *priv_req)
 		if (priv_req->aligned_buf) {
 			trace_cdns3_free_aligned_request(priv_req);
 			priv_req->aligned_buf->in_use = 0;
-			priv_dev->run_garbage_colector = 1;
+			queue_work(system_freezable_wq,
+				   &priv_dev->aligned_buf_wq);
 		}
 
 		buf->in_use = 1;
@@ -735,7 +789,7 @@ static int cdns3_wa1_update_guard(struct cdns3_endpoint *priv_ep,
 			priv_ep->wa1_set = 1;
 			priv_ep->wa1_trb = trb;
 			priv_ep->wa1_trb_index = priv_ep->enqueue;
-			cdns3_dbg(priv_dev, "WA1 set guard\n");
+			trace_cdns3_wa1(priv_ep, "set guard");
 			return 0;
 		}
 	}
@@ -749,8 +803,7 @@ static void cdns3_wa1_tray_restore_cycle_bit(struct cdns3_device *priv_dev,
 	u32 doorbell;
 
 	doorbell = !!(readl(&priv_dev->regs->ep_cmd) & EP_CMD_DRDY);
-	dma_index = readl(&priv_dev->regs->ep_traddr) - priv_ep->trb_pool_dma;
-	dma_index /= TRB_SIZE;
+	dma_index = cdns3_get_dma_pos(priv_dev, priv_ep);
 
 	if (!doorbell || dma_index != priv_ep->wa1_trb_index)
 		cdns3_wa1_restore_cycle_bit(priv_ep);
@@ -769,7 +822,6 @@ int cdns3_ep_run_transfer(struct cdns3_endpoint *priv_ep,
 	struct cdns3_request *priv_req;
 	struct cdns3_trb *trb;
 	dma_addr_t trb_dma;
-	int prev_enqueue;
 	u32 togle_pcs = 1;
 	int sg_iter = 0;
 	int num_trb;
@@ -801,29 +853,51 @@ int cdns3_ep_run_transfer(struct cdns3_endpoint *priv_ep,
 	trb = priv_ep->trb_pool + priv_ep->enqueue;
 	priv_req->start_trb = priv_ep->enqueue;
 	priv_req->trb = trb;
-	prev_enqueue = priv_ep->enqueue;
+
+	cdns3_select_ep(priv_ep->cdns3_dev, address);
 
 	/* prepare ring */
 	if ((priv_ep->enqueue + num_trb)  >= (priv_ep->num_trbs - 1)) {
+		struct cdns3_trb *link_trb;
+		int doorbell, dma_index;
+		u32 ch_bit = 0;
+
+		doorbell = !!(readl(&priv_dev->regs->ep_cmd) & EP_CMD_DRDY);
+		dma_index = cdns3_get_dma_pos(priv_dev, priv_ep);
+
+		/* Driver can't update LINK TRB if it is current processed. */
+		if (doorbell && dma_index == priv_ep->num_trbs - 1) {
+			priv_ep->flags |= EP_DEFERRED_DRDY;
+			return -ENOBUFS;
+		}
+
 		/*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 = priv_ep->trb_pool + (priv_ep->num_trbs - 1);
+		/*
+		 * For TRs size equal 2 enabling TRB_CHAIN for epXin causes
+		 * that DMA stuck at the LINK TRB.
+		 * On the other hand, removing TRB_CHAIN for longer TRs for
+		 * epXout cause that DMA stuck after handling LINK TRB.
+		 * To eliminate this strange behavioral driver set TRB_CHAIN
+		 * bit only for TR size > 2.
+		 */
+		if (priv_ep->type == USB_ENDPOINT_XFER_ISOC ||
+		    TRBS_PER_SEGMENT > 2)
+			ch_bit = TRB_CHAIN;
+
 		link_trb->control = ((priv_ep->pcs) ? TRB_CYCLE : 0) |
-				    TRB_TYPE(TRB_LINK) | TRB_CHAIN |
-				    TRB_TOGGLE;
+				    TRB_TYPE(TRB_LINK) | TRB_TOGGLE | ch_bit;
 	}
 
-	/* arm transfer on selected endpoint */
-	cdns3_select_ep(priv_ep->cdns3_dev, address);
-
-	if (priv_dev->dev_ver < DEV_VER_V2)
+	if (priv_dev->dev_ver <= DEV_VER_V2)
 		togle_pcs = cdns3_wa1_update_guard(priv_ep, trb);
 
 	/* set incorrect Cycle Bit for first trb*/
 	control = priv_ep->pcs ? 0 : TRB_CYCLE;
+
 	do {
 		u32 length;
-		u8 td_size = 0;
+		u16 td_size = 0;
 
 		/* fill TRB */
 		control |= TRB_TYPE(TRB_NORMAL);
@@ -863,11 +937,12 @@ int cdns3_ep_run_transfer(struct cdns3_endpoint *priv_ep,
 				control |= pcs | TRB_IOC | TRB_ISP;
 		}
 
-		if (sg_iter) {
+		if (sg_iter)
 			trb->control = control;
-			control = 0;
-		}
+		else
+			priv_req->trb->control = control;
 
+		control = 0;
 		++sg_iter;
 		priv_req->end_trb = priv_ep->enqueue;
 		cdns3_ep_inc_enq(priv_ep);
@@ -879,20 +954,18 @@ int cdns3_ep_run_transfer(struct cdns3_endpoint *priv_ep,
 	priv_req->flags |= REQUEST_PENDING;
 
 	if (sg_iter == 1)
-		control |= TRB_IOC | TRB_ISP;
+		trb->control |= TRB_IOC | TRB_ISP;
 
 	/*
 	 * Memory barrier - cycle bit must be set before other filds in trb.
 	 */
 	wmb();
 
+	/* give the TD to the consumer*/
 	if (togle_pcs)
-		/* give the TD to the consumer*/
-		trb->control = control ^ 1;
-	else
-		trb->control = control;
+		trb->control =  trb->control ^ 1;
 
-	if (priv_dev->dev_ver < DEV_VER_V2)
+	if (priv_dev->dev_ver <= DEV_VER_V2)
 		cdns3_wa1_tray_restore_cycle_bit(priv_dev, priv_ep);
 
 	trace_cdns3_prepare_trb(priv_ep, priv_req->trb);
@@ -908,17 +981,26 @@ int cdns3_ep_run_transfer(struct cdns3_endpoint *priv_ep,
 	 * enabling endpoint.
 	 */
 	if (priv_ep->flags & EP_UPDATE_EP_TRBADDR) {
+		/*
+		 * Until SW is not ready to handle the OUT transfer the ISO OUT
+		 * Endpoint should be disabled (EP_CFG.ENABLE = 0).
+		 * EP_CFG_ENABLE must be set before updating ep_traddr.
+		 */
+		if (priv_ep->type == USB_ENDPOINT_XFER_ISOC  && !priv_ep->dir &&
+		    !(priv_ep->flags & EP_QUIRK_ISO_OUT_EN)) {
+			priv_ep->flags |= EP_QUIRK_ISO_OUT_EN;
+			cdns3_set_register_bit(&priv_dev->regs->ep_cfg,
+					       EP_CFG_ENABLE);
+		}
+
 		writel(EP_TRADDR_TRADDR(priv_ep->trb_pool_dma +
 					priv_req->start_trb * TRB_SIZE),
-		       &priv_dev->regs->ep_traddr);
-
-		cdns3_dbg(priv_ep->cdns3_dev, "Update ep_trbaddr for %s to %08x\n",
-			  priv_ep->name, readl(&priv_dev->regs->ep_traddr));
+					&priv_dev->regs->ep_traddr);
 
 		priv_ep->flags &= ~EP_UPDATE_EP_TRBADDR;
 	}
 
-	if (!priv_ep->wa1_set && !(priv_ep->flags & EP_STALL)) {
+	if (!priv_ep->wa1_set && !(priv_ep->flags & EP_STALLED)) {
 		trace_cdns3_ring(priv_ep);
 		/*clearing TRBERR and EP_STS_DESCMIS before seting DRDY*/
 		writel(EP_STS_TRBERR | EP_STS_DESCMIS, &priv_dev->regs->ep_sts);
@@ -927,6 +1009,9 @@ int cdns3_ep_run_transfer(struct cdns3_endpoint *priv_ep,
 					 readl(&priv_dev->regs->ep_traddr));
 	}
 
+	/* WORKAROUND for transition to L0 */
+	__cdns3_gadget_wakeup(priv_dev);
+
 	return 0;
 }
 
@@ -934,7 +1019,7 @@ void cdns3_set_hw_configuration(struct cdns3_device *priv_dev)
 {
 	struct cdns3_endpoint *priv_ep;
 	struct usb_ep *ep;
-	int result = 0;
+	int val;
 
 	if (priv_dev->hw_configured_flag)
 		return;
@@ -946,11 +1031,10 @@ void cdns3_set_hw_configuration(struct cdns3_device *priv_dev)
 			       USB_CONF_U1EN | USB_CONF_U2EN);
 
 	/* wait until configuration set */
-	result = cdns3_handshake(&priv_dev->regs->usb_sts,
-				 USB_STS_CFGSTS_MASK, 1, 100);
+	readl_poll_timeout_atomic(&priv_dev->regs->usb_sts, val,
+				  val & 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) {
@@ -984,6 +1068,9 @@ void cdns3_set_hw_configuration(struct cdns3_device *priv_dev)
  * some rules:
  * 1. priv_ep->dequeue never exceed current_index.
  * 2  priv_ep->enqueue never exceed priv_ep->dequeue
+ * 3. exception: priv_ep->enqueue == priv_ep->dequeue
+ *    and priv_ep->free_trbs is zero.
+ *    This case indicate that TR is full.
  *
  * Then We can split recognition into two parts:
  * Case 1 - priv_ep->dequeue < current_index
@@ -1007,17 +1094,29 @@ static bool cdns3_request_handled(struct cdns3_endpoint *priv_ep,
 	struct cdns3_trb *trb = priv_req->trb;
 	int current_index = 0;
 	int handled = 0;
+	int doorbell;
 
-	current_index = readl(&priv_dev->regs->ep_traddr) -
-			priv_ep->trb_pool_dma;
-	current_index /= TRB_SIZE;
+	current_index = cdns3_get_dma_pos(priv_dev, priv_ep);
+	doorbell = !!(readl(&priv_dev->regs->ep_cmd) & EP_CMD_DRDY);
 
 	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 (doorbell == 1 && current_index == priv_ep->dequeue)
+		goto finish;
+
+	/* The corner case for TRBS_PER_SEGMENT equal 2). */
+	if (TRBS_PER_SEGMENT == 2 && priv_ep->type != USB_ENDPOINT_XFER_ISOC) {
+		handled = 1;
+		goto finish;
+	}
+
+	if (priv_ep->enqueue == priv_ep->dequeue &&
+	    priv_ep->free_trbs == 0) {
+		handled = 1;
+	} else if (priv_ep->dequeue < current_index) {
 		if ((current_index == (priv_ep->num_trbs - 1)) &&
 		    !priv_ep->dequeue)
 			goto finish;
@@ -1054,7 +1153,7 @@ static void cdns3_transfer_completed(struct cdns3_device *priv_dev,
 		cdns3_select_ep(priv_dev, priv_ep->endpoint.address);
 
 		if (!cdns3_request_handled(priv_ep, priv_req))
-			return;
+			goto prepare_next_td;
 
 		trb = priv_ep->trb_pool + priv_ep->dequeue;
 		trace_cdns3_complete_trb(priv_ep, trb);
@@ -1067,8 +1166,17 @@ static void cdns3_transfer_completed(struct cdns3_device *priv_dev,
 		request->actual = TRB_LEN(le32_to_cpu(trb->length));
 		cdns3_move_deq_to_next_trb(priv_req);
 		cdns3_gadget_giveback(priv_ep, priv_req, 0);
+
+		if (priv_ep->type != USB_ENDPOINT_XFER_ISOC &&
+		    TRBS_PER_SEGMENT == 2)
+			break;
 	}
 	priv_ep->flags &= ~EP_PENDING_REQUEST;
+
+prepare_next_td:
+	if (!(priv_ep->flags & EP_STALLED) &&
+	    !(priv_ep->flags & EP_STALL_PENDING))
+		cdns3_start_all_request(priv_dev, priv_ep);
 }
 
 void cdns3_rearm_transfer(struct cdns3_endpoint *priv_ep, u8 rearm)
@@ -1084,6 +1192,8 @@ void cdns3_rearm_transfer(struct cdns3_endpoint *priv_ep, u8 rearm)
 		wmb();
 		writel(EP_CMD_DRDY, &priv_dev->regs->ep_cmd);
 
+		__cdns3_gadget_wakeup(priv_dev);
+
 		trace_cdns3_doorbell_epx(priv_ep->name,
 					 readl(&priv_dev->regs->ep_traddr));
 	}
@@ -1108,6 +1218,12 @@ static int cdns3_check_ep_interrupt_proceed(struct cdns3_endpoint *priv_ep)
 	writel(ep_sts_reg, &priv_dev->regs->ep_sts);
 
 	if (ep_sts_reg & EP_STS_TRBERR) {
+		if (priv_ep->flags & EP_STALL_PENDING &&
+		    !(ep_sts_reg & EP_STS_DESCMIS &&
+		    priv_dev->dev_ver < DEV_VER_V2)) {
+			cdns3_ep_stall_flush(priv_ep);
+		}
+
 		/*
 		 * For isochronous transfer driver completes request on
 		 * IOC or on TRBERR. IOC appears only when device receive
@@ -1116,10 +1232,25 @@ static int cdns3_check_ep_interrupt_proceed(struct cdns3_endpoint *priv_ep)
 		 * on TRBERR event.
 		 */
 		if (priv_ep->type == USB_ENDPOINT_XFER_ISOC &&
-		    !priv_ep->wa1_set)
+		    !priv_ep->wa1_set) {
+			if (!priv_ep->dir) {
+				u32 ep_cfg = readl(&priv_dev->regs->ep_cfg);
+
+				ep_cfg &= ~EP_CFG_ENABLE;
+				writel(ep_cfg, &priv_dev->regs->ep_cfg);
+				priv_ep->flags &= ~EP_QUIRK_ISO_OUT_EN;
+			}
 			cdns3_transfer_completed(priv_dev, priv_ep);
-		else
-			cdns3_rearm_transfer(priv_ep, priv_ep->wa1_set);
+		} else if (!(priv_ep->flags & EP_STALLED) &&
+			  !(priv_ep->flags & EP_STALL_PENDING)) {
+			if (priv_ep->flags & EP_DEFERRED_DRDY) {
+				priv_ep->flags &= ~EP_DEFERRED_DRDY;
+				cdns3_start_all_request(priv_dev, priv_ep);
+			} else {
+				cdns3_rearm_transfer(priv_ep,
+						     priv_ep->wa1_set);
+			}
+		}
 	}
 
 	if ((ep_sts_reg & EP_STS_IOC) || (ep_sts_reg & EP_STS_ISP)) {
@@ -1139,12 +1270,22 @@ static int cdns3_check_ep_interrupt_proceed(struct cdns3_endpoint *priv_ep)
 	 * priv_ep->flags & EP_QUIRK_EXTRA_BUF_EN.
 	 * In other cases this interrupt will be disabled/
 	 */
-	if (ep_sts_reg & EP_STS_DESCMIS && priv_dev->dev_ver < DEV_VER_V2)
+	if (ep_sts_reg & EP_STS_DESCMIS && priv_dev->dev_ver < DEV_VER_V2 &&
+	    !(priv_ep->flags & EP_STALLED))
 		cdns3_wa2_descmissing_packet(priv_ep);
 
 	return 0;
 }
 
+static void cdns3_disconnect_gadget(struct cdns3_device *priv_dev)
+{
+	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);
+	}
+}
+
 /**
  * cdns3_check_usb_interrupt_proceed - Processes interrupt related to device
  * @priv_dev: extended gadget object
@@ -1157,6 +1298,16 @@ static void cdns3_check_usb_interrupt_proceed(struct cdns3_device *priv_dev,
 	int speed = 0;
 
 	trace_cdns3_usb_irq(priv_dev, usb_ists);
+	if (usb_ists & USB_ISTS_L1ENTI) {
+		/*
+		 * WORKAROUND: CDNS3 controller has issue with hardware resuming
+		 * from L1. To fix it, if any DMA transfer is pending driver
+		 * must starts driving resume signal immediately.
+		 */
+		if (readl(&priv_dev->regs->drbl))
+			__cdns3_gadget_wakeup(priv_dev);
+	}
+
 	/* Connection detected */
 	if (usb_ists & (USB_ISTS_CON2I | USB_ISTS_CONI)) {
 		speed = cdns3_get_speed(priv_dev);
@@ -1167,33 +1318,44 @@ static void cdns3_check_usb_interrupt_proceed(struct cdns3_device *priv_dev,
 
 	/* Disconnection detected */
 	if (usb_ists & (USB_ISTS_DIS2I | USB_ISTS_DISI)) {
+		cdns3_disconnect_gadget(priv_dev);
+		priv_dev->gadget.speed = USB_SPEED_UNKNOWN;
+		usb_gadget_set_state(&priv_dev->gadget, USB_STATE_NOTATTACHED);
+		cdns3_hw_reset_eps_config(priv_dev);
+	}
+
+	if (usb_ists & (USB_ISTS_L2ENTI | USB_ISTS_U3ENTI)) {
 		if (priv_dev->gadget_driver &&
-		    priv_dev->gadget_driver->disconnect) {
+		    priv_dev->gadget_driver->suspend) {
 			spin_unlock(&priv_dev->lock);
-			priv_dev->gadget_driver->disconnect(&priv_dev->gadget);
+			priv_dev->gadget_driver->suspend(&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);
+	if (usb_ists & (USB_ISTS_L2EXTI | USB_ISTS_U3EXTI)) {
+		if (priv_dev->gadget_driver &&
+		    priv_dev->gadget_driver->resume) {
+			spin_unlock(&priv_dev->lock);
+			priv_dev->gadget_driver->resume(&priv_dev->gadget);
+			spin_lock(&priv_dev->lock);
+		}
 	}
 
 	/* reset*/
 	if (usb_ists & (USB_ISTS_UWRESI | USB_ISTS_UHRESI | USB_ISTS_U2RESI)) {
-		if (priv_dev->gadget_driver &&
-		    priv_dev->gadget_driver->reset) {
+		if (priv_dev->gadget_driver) {
 			spin_unlock(&priv_dev->lock);
-			priv_dev->gadget_driver->reset(&priv_dev->gadget);
+			usb_gadget_udc_reset(&priv_dev->gadget,
+					     priv_dev->gadget_driver);
 			spin_lock(&priv_dev->lock);
-		}
 
-		/*read again to check the actual 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);
+			/*read again to check the actual speed*/
+			speed = cdns3_get_speed(priv_dev);
+			priv_dev->gadget.speed = speed;
+			cdns3_hw_reset_eps_config(priv_dev);
+			cdns3_ep0_config(priv_dev);
+		}
 	}
 }
 
@@ -1210,33 +1372,33 @@ 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;
+		/* After masking interrupts the new interrupts won't be
+		 * reported in usb_ists/ep_ists. In order to not lose some
+		 * of them driver disables only detected interrupts.
+		 * They will be enabled ASAP after clearing source of
+		 * interrupt. This an unusual behavior only applies to
+		 * usb_ists register.
+		 */
+		reg = ~reg & readl(&priv_dev->regs->usb_ien);
+		/* mask deferred interrupt. */
+		writel(reg, &priv_dev->regs->usb_ien);
+		ret = IRQ_WAKE_THREAD;
 	}
 
 	/* check endpoint interrupt */
 	reg = readl(&priv_dev->regs->ep_ists);
-
 	if (reg) {
-		priv_dev->shadow_ep_en |= reg;
-		reg = ~reg & readl(&priv_dev->regs->ep_ien);
-		/* mask deferred interrupt. */
-		writel(reg, &priv_dev->regs->ep_ien);
+		writel(0, &priv_dev->regs->ep_ien);
 		ret = IRQ_WAKE_THREAD;
 	}
 
-	spin_unlock_irqrestore(&priv_dev->lock, flags);
 	return ret;
 }
 
@@ -1255,13 +1417,20 @@ static irqreturn_t cdns3_device_thread_irq_handler(int irq, void *data)
 	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->usb_ists);
+	if (reg) {
+		writel(reg, &priv_dev->regs->usb_ists);
+		writel(USB_IEN_INIT, &priv_dev->regs->usb_ien);
+		cdns3_check_usb_interrupt_proceed(priv_dev, reg);
+		ret = IRQ_HANDLED;
+	}
+
 	reg = readl(&priv_dev->regs->ep_ists);
 
 	/* handle default endpoint OUT */
@@ -1283,38 +1452,14 @@ static irqreturn_t cdns3_device_thread_irq_handler(int irq, void *data)
 
 	for_each_set_bit(bit, (unsigned long *)&reg,
 			 sizeof(u32) * BITS_PER_BYTE) {
-		priv_dev->shadow_ep_en |= BIT(bit);
 		cdns3_check_ep_interrupt_proceed(priv_dev->eps[bit]);
 		ret = IRQ_HANDLED;
 	}
 
-	if (priv_dev->run_garbage_colector) {
-		struct cdns3_aligned_buf *buf, *tmp;
-
-		list_for_each_entry_safe(buf, tmp, &priv_dev->aligned_buf_list,
-					 list) {
-			if (!buf->in_use) {
-				list_del(&buf->list);
-
-				spin_unlock_irqrestore(&priv_dev->lock, flags);
-				dma_free_coherent(priv_dev->sysdev, buf->size,
-						  buf->buf,
-						  buf->dma);
-				spin_lock_irqsave(&priv_dev->lock, flags);
-
-				kfree(buf);
-			}
-		}
-
-		priv_dev->run_garbage_colector = 0;
-	}
-
 irqend:
-	ep_ien = readl(&priv_dev->regs->ep_ien) | priv_dev->shadow_ep_en;
-	priv_dev->shadow_ep_en = 0;
-	/* Unmask all handled EP interrupts */
-	writel(ep_ien, &priv_dev->regs->ep_ien);
+	writel(~0, &priv_dev->regs->ep_ien);
 	spin_unlock_irqrestore(&priv_dev->lock, flags);
+
 	return ret;
 }
 
@@ -1333,33 +1478,72 @@ irqend:
 static int cdns3_ep_onchip_buffer_reserve(struct cdns3_device *priv_dev,
 					  int size, int is_in)
 {
-	u32 onchip_mem;
+	int remained;
+
+	/* 2KB are reserved for EP0*/
+	remained = priv_dev->onchip_buffers - priv_dev->onchip_used_size - 2;
 
 	if (is_in) {
-		priv_dev->onchip_mem_allocated_size += size;
+		if (remained < size)
+			return -EPERM;
+
+		priv_dev->onchip_used_size += size;
 	} else {
-		 /* ALL OUT EPs are shared the same chunk onchip memory */
-		if (size > priv_dev->out_mem_is_allocated) {
-			priv_dev->onchip_mem_allocated_size -= priv_dev->out_mem_is_allocated;
-			priv_dev->onchip_mem_allocated_size += size;
-			priv_dev->out_mem_is_allocated += size;
-		}
-	}
+		int required;
 
-	onchip_mem = USB_CAP2_ACTUAL_MEM_SIZE(readl(&priv_dev->regs->usb_cap2));
-	if (!onchip_mem)
-		onchip_mem = 256;
+		/**
+		 *  ALL OUT EPs are shared the same chunk onchip memory, so
+		 * driver checks if it already has assigned enough buffers
+		 */
+		if (priv_dev->out_mem_is_allocated >= size)
+			return 0;
 
-	/* 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;
+		required = size - priv_dev->out_mem_is_allocated;
+
+		if (required > remained)
+			return -EPERM;
+
+		priv_dev->out_mem_is_allocated += required;
+		priv_dev->onchip_used_size += required;
 	}
 
 	return 0;
 }
 
+void cdns3_configure_dmult(struct cdns3_device *priv_dev,
+			   struct cdns3_endpoint *priv_ep)
+{
+	struct cdns3_usb_regs __iomem *regs = priv_dev->regs;
+
+	/* For dev_ver > DEV_VER_V2 DMULT is configured per endpoint */
+	if (priv_dev->dev_ver <= DEV_VER_V2)
+		writel(USB_CONF_DMULT, &regs->usb_conf);
+
+	if (priv_dev->dev_ver == DEV_VER_V2)
+		writel(USB_CONF2_EN_TDL_TRB, &regs->usb_conf2);
+
+	if (priv_dev->dev_ver >= DEV_VER_V3 && priv_ep) {
+		u32 mask;
+
+		if (priv_ep->dir)
+			mask = BIT(priv_ep->num + 16);
+		else
+			mask = BIT(priv_ep->num);
+
+		if (priv_ep->type != USB_ENDPOINT_XFER_ISOC) {
+			cdns3_set_register_bit(&regs->tdl_from_trb, mask);
+			cdns3_set_register_bit(&regs->tdl_beh, mask);
+			cdns3_set_register_bit(&regs->tdl_beh2, mask);
+			cdns3_set_register_bit(&regs->dma_adv_td, mask);
+		}
+
+		if (priv_ep->type == USB_ENDPOINT_XFER_ISOC && !priv_ep->dir)
+			cdns3_set_register_bit(&regs->tdl_from_trb, mask);
+
+		cdns3_set_register_bit(&regs->dtrans, mask);
+	}
+}
+
 /**
  * cdns3_ep_config Configure hardware endpoint
  * @priv_ep: extended endpoint object
@@ -1370,25 +1554,29 @@ void cdns3_ep_config(struct cdns3_endpoint *priv_ep)
 	struct cdns3_device *priv_dev = priv_ep->cdns3_dev;
 	u32 bEndpointAddress = priv_ep->num | priv_ep->dir;
 	u32 max_packet_size = 0;
-	u8 buffering;
 	u8 maxburst = 0;
 	u32 ep_cfg = 0;
+	u8 buffering;
 	u8 mult = 0;
 	int ret;
 
 	buffering = CDNS3_EP_BUF_SIZE - 1;
 
+	cdns3_configure_dmult(priv_dev, priv_ep);
+
 	switch (priv_ep->type) {
 	case USB_ENDPOINT_XFER_INT:
 		ep_cfg = EP_CFG_EPTYPE(USB_ENDPOINT_XFER_INT);
 
-		if (priv_dev->dev_ver >= DEV_VER_V2)
+		if ((priv_dev->dev_ver == DEV_VER_V2 && !priv_ep->dir) ||
+		    priv_dev->dev_ver > DEV_VER_V2)
 			ep_cfg |= EP_CFG_TDL_CHK;
 		break;
 	case USB_ENDPOINT_XFER_BULK:
 		ep_cfg = EP_CFG_EPTYPE(USB_ENDPOINT_XFER_BULK);
 
-		if (priv_dev->dev_ver >= DEV_VER_V2)
+		if ((priv_dev->dev_ver == DEV_VER_V2  && !priv_ep->dir) ||
+		    priv_dev->dev_ver > DEV_VER_V2)
 			ep_cfg |= EP_CFG_TDL_CHK;
 		break;
 	default:
@@ -1431,7 +1619,7 @@ void cdns3_ep_config(struct cdns3_endpoint *priv_ep)
 	else
 		priv_ep->trb_burst_size = 16;
 
-	ret = cdns3_ep_onchip_buffer_reserve(priv_dev, buffering,
+	ret = cdns3_ep_onchip_buffer_reserve(priv_dev, buffering + 1,
 					     !!priv_ep->dir);
 	if (ret) {
 		dev_err(priv_dev->dev, "onchip mem is full, ep is invalid\n");
@@ -1586,7 +1774,9 @@ static int cdns3_gadget_ep_enable(struct usb_ep *ep,
 	u32 reg = EP_STS_EN_TRBERREN;
 	u32 bEndpointAddress;
 	unsigned long flags;
+	int enable = 1;
 	int ret;
+	int val;
 
 	priv_ep = ep_to_cdns3_ep(ep);
 	priv_dev = priv_ep->cdns3_dev;
@@ -1632,8 +1822,15 @@ static int cdns3_gadget_ep_enable(struct usb_ep *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, 1000);
+	ret = readl_poll_timeout_atomic(&priv_dev->regs->ep_cmd, val,
+					!(val & (EP_CMD_CSTALL | EP_CMD_EPRST)),
+					1, 1000);
+
+	if (unlikely(ret)) {
+		cdns3_free_trb_pool(priv_ep);
+		ret =  -EINVAL;
+		goto exit;
+	}
 
 	/* enable interrupt for selected endpoint */
 	cdns3_set_register_bit(&priv_dev->regs->ep_ien,
@@ -1644,11 +1841,23 @@ static int cdns3_gadget_ep_enable(struct usb_ep *ep,
 
 	writel(reg, &priv_dev->regs->ep_sts_en);
 
-	cdns3_set_register_bit(&priv_dev->regs->ep_cfg, EP_CFG_ENABLE);
+	/*
+	 * For some versions of controller at some point during ISO OUT traffic
+	 * DMA reads Transfer Ring for the EP which has never got doorbell.
+	 * This issue was detected only on simulation, but to avoid this issue
+	 * driver add protection against it. To fix it driver enable ISO OUT
+	 * endpoint before setting DRBL. This special treatment of ISO OUT
+	 * endpoints are recommended by controller specification.
+	 */
+	if (priv_ep->type == USB_ENDPOINT_XFER_ISOC  && !priv_ep->dir)
+		enable = 0;
+
+	if (enable)
+		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_PENDING_REQUEST | EP_STALLED | EP_STALL_PENDING |
+			    EP_QUIRK_ISO_OUT_EN | EP_QUIRK_EXTRA_BUF_EN);
 	priv_ep->flags |= EP_ENABLED | EP_UPDATE_EP_TRBADDR;
 	priv_ep->wa1_set = 0;
 	priv_ep->enqueue = 0;
@@ -1679,6 +1888,7 @@ static int cdns3_gadget_ep_disable(struct usb_ep *ep)
 	unsigned long flags;
 	int ret = 0;
 	u32 ep_cfg;
+	int val;
 
 	if (!ep) {
 		pr_err("usbss: invalid parameters\n");
@@ -1702,10 +1912,22 @@ static int cdns3_gadget_ep_disable(struct usb_ep *ep)
 	ep_cfg &= ~EP_CFG_ENABLE;
 	writel(ep_cfg, &priv_dev->regs->ep_cfg);
 
+	/**
+	 * Driver needs some time before resetting endpoint.
+	 * It need waits for clearing DBUSY bit or for timeout expired.
+	 * 10us is enough time for controller to stop transfer.
+	 */
+	readl_poll_timeout_atomic(&priv_dev->regs->ep_sts, val,
+				  !(val & EP_STS_DBUSY), 1, 10);
 	writel(EP_CMD_EPRST, &priv_dev->regs->ep_cmd);
 
-	ret = cdns3_handshake(&priv_dev->regs->ep_cmd,
-			      EP_CMD_CSTALL | EP_CMD_EPRST, 0, 1000);
+	readl_poll_timeout_atomic(&priv_dev->regs->ep_cmd, val,
+				  !(val & (EP_CMD_CSTALL | EP_CMD_EPRST)),
+				  1, 1000);
+	if (unlikely(ret))
+		dev_err(priv_dev->dev, "Timeout: %s resetting failed.\n",
+			priv_ep->name);
+
 	while (!list_empty(&priv_ep->pending_req_list)) {
 		request = cdns3_next_request(&priv_ep->pending_req_list);
 
@@ -1755,7 +1977,6 @@ static int __cdns3_gadget_ep_queue(struct usb_ep *ep,
 	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;
@@ -1763,9 +1984,13 @@ static int __cdns3_gadget_ep_queue(struct usb_ep *ep,
 	priv_req = to_cdns3_request(request);
 	trace_cdns3_ep_queue(priv_req);
 
-	if (priv_dev->dev_ver < DEV_VER_V2)
-		deferred = cdns3_wa2_gadget_ep_queue(priv_dev, priv_ep,
-						     priv_req);
+	if (priv_dev->dev_ver < DEV_VER_V2) {
+		ret = cdns3_wa2_gadget_ep_queue(priv_dev, priv_ep,
+						priv_req);
+
+		if (ret == EINPROGRESS)
+			return 0;
+	}
 
 	ret = cdns3_prepare_aligned_request_buf(priv_req);
 	if (ret < 0)
@@ -1776,22 +2001,18 @@ static int __cdns3_gadget_ep_queue(struct usb_ep *ep,
 	if (ret)
 		return ret;
 
+	list_add_tail(&request->list, &priv_ep->deferred_req_list);
+
 	/*
 	 * 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);
+	if (priv_dev->hw_configured_flag && !(priv_ep->flags & EP_STALLED) &&
+	    !(priv_ep->flags & EP_STALL_PENDING))
+		cdns3_start_all_request(priv_dev, priv_ep);
 
-	return ret;
+	return 0;
 }
 
 static int cdns3_gadget_ep_queue(struct usb_ep *ep, struct usb_request *request,
@@ -1903,6 +2124,60 @@ not_found:
 	return ret;
 }
 
+/**
+ * __cdns3_gadget_ep_set_halt Sets stall on selected endpoint
+ * Should be called after acquiring spin_lock and selecting ep
+ * @ep: endpoint object to set stall on.
+ */
+void __cdns3_gadget_ep_set_halt(struct cdns3_endpoint *priv_ep)
+{
+	struct cdns3_device *priv_dev = priv_ep->cdns3_dev;
+
+	trace_cdns3_halt(priv_ep, 1, 0);
+
+	if (!(priv_ep->flags & EP_STALLED)) {
+		u32 ep_sts_reg = readl(&priv_dev->regs->ep_sts);
+
+		if (!(ep_sts_reg & EP_STS_DBUSY))
+			cdns3_ep_stall_flush(priv_ep);
+		else
+			priv_ep->flags |= EP_STALL_PENDING;
+	}
+}
+
+/**
+ * __cdns3_gadget_ep_clear_halt Clears stall on selected endpoint
+ * Should be called after acquiring spin_lock and selecting ep
+ * @ep: endpoint object to clear stall on
+ */
+int __cdns3_gadget_ep_clear_halt(struct cdns3_endpoint *priv_ep)
+{
+	struct cdns3_device *priv_dev = priv_ep->cdns3_dev;
+	struct usb_request *request;
+	int ret = 0;
+	int val;
+
+	trace_cdns3_halt(priv_ep, 0, 0);
+
+	writel(EP_CMD_CSTALL | EP_CMD_EPRST, &priv_dev->regs->ep_cmd);
+
+	/* wait for EPRST cleared */
+	readl_poll_timeout_atomic(&priv_dev->regs->ep_cmd, val,
+				  !(val & EP_CMD_EPRST), 1, 100);
+	if (ret)
+		return -EINVAL;
+
+	priv_ep->flags &= ~(EP_STALLED | EP_STALL_PENDING);
+
+	request = cdns3_next_request(&priv_ep->pending_req_list);
+
+	if (request)
+		cdns3_rearm_transfer(priv_ep, 1);
+
+	cdns3_start_all_request(priv_dev, priv_ep);
+	return ret;
+}
+
 /**
  * cdns3_gadget_ep_set_halt Sets/clears stall on selected endpoint
  * @ep: endpoint object to set/clear stall on
@@ -1923,32 +2198,14 @@ int cdns3_gadget_ep_set_halt(struct usb_ep *ep, int value)
 	spin_lock_irqsave(&priv_dev->lock, flags);
 
 	cdns3_select_ep(priv_dev, ep->desc->bEndpointAddress);
-	if (value) {
-		cdns3_ep_stall_flush(priv_ep);
-	} else {
-		priv_ep->flags &= ~EP_WEDGE;
-
-		cdns3_dbg(priv_ep->cdns3_dev, "Clear stalled endpoint %s\n",
-			  priv_ep->name);
-
-		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;
-		}
+	if (!value) {
+		priv_ep->flags &= ~EP_WEDGE;
+		ret = __cdns3_gadget_ep_clear_halt(priv_ep);
+	} else {
+		__cdns3_gadget_ep_set_halt(priv_ep);
 	}
 
-	priv_ep->flags &= ~EP_PENDING_REQUEST;
-finish:
 	spin_unlock_irqrestore(&priv_dev->lock, flags);
 
 	return ret;
@@ -1980,11 +2237,33 @@ static int cdns3_gadget_get_frame(struct usb_gadget *gadget)
 	return readl(&priv_dev->regs->usb_itpn);
 }
 
-static int cdns3_gadget_wakeup(struct usb_gadget *gadget)
+int __cdns3_gadget_wakeup(struct cdns3_device *priv_dev)
 {
+	enum usb_device_speed speed;
+
+	speed = cdns3_get_speed(priv_dev);
+
+	if (speed >= USB_SPEED_SUPER)
+		return 0;
+
+	/* Start driving resume signaling to indicate remote wakeup. */
+	writel(USB_CONF_LGO_L0, &priv_dev->regs->usb_conf);
+
 	return 0;
 }
 
+static int cdns3_gadget_wakeup(struct usb_gadget *gadget)
+{
+	struct cdns3_device *priv_dev = gadget_to_cdns3_device(gadget);
+	unsigned long flags;
+	int ret = 0;
+
+	spin_lock_irqsave(&priv_dev->lock, flags);
+	ret = __cdns3_gadget_wakeup(priv_dev);
+	spin_unlock_irqrestore(&priv_dev->lock, flags);
+	return ret;
+}
+
 static int cdns3_gadget_set_selfpowered(struct usb_gadget *gadget,
 					int is_selfpowered)
 {
@@ -2012,6 +2291,7 @@ static int cdns3_gadget_pullup(struct usb_gadget *gadget, int is_on)
 static void cdns3_gadget_config(struct cdns3_device *priv_dev)
 {
 	struct cdns3_usb_regs __iomem *regs = priv_dev->regs;
+	u32 reg;
 
 	cdns3_ep0_config(priv_dev);
 
@@ -2019,11 +2299,11 @@ static void cdns3_gadget_config(struct cdns3_device *priv_dev)
 	writel(EP_IEN_EP_OUT0 | EP_IEN_EP_IN0, &regs->ep_ien);
 
 	/*
-	 *Driver need modify LFPS minimal U1 Exit time for 0x00024505 revision
-	 * of controller
+	 * Driver needs to modify LFPS minimal U1 Exit time for DEV_VER_TI_V1
+	 * revision of controller.
 	 */
 	if (priv_dev->dev_ver == DEV_VER_TI_V1) {
-		u32 reg = readl(&regs->dbg_link1);
+		reg = readl(&regs->dbg_link1);
 
 		reg &= ~DBG_LINK1_LFPS_MIN_GEN_U1_EXIT_MASK;
 		reg |= DBG_LINK1_LFPS_MIN_GEN_U1_EXIT(0x55) |
@@ -2031,12 +2311,21 @@ static void cdns3_gadget_config(struct cdns3_device *priv_dev)
 		writel(reg, &regs->dbg_link1);
 	}
 
+	/*
+	 * By default some platforms has set protected access to memory.
+	 * This cause problem with cache, so driver restore non-secure
+	 * access to memory.
+	 */
+	reg = readl(&regs->dma_axi_ctrl);
+	reg |= DMA_AXI_CTRL_MARPROT(DMA_AXI_CTRL_NON_SECURE) |
+	       DMA_AXI_CTRL_MAWPROT(DMA_AXI_CTRL_NON_SECURE);
+	writel(reg, &regs->dma_axi_ctrl);
+
 	/* 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);
-	if (priv_dev->dev_ver >= DEV_VER_V2)
-		writel(USB_CONF2_EN_TDL_TRB, &regs->usb_conf2);
+
+	cdns3_configure_dmult(priv_dev, NULL);
 
 	cdns3_gadget_pullup(&priv_dev->gadget, 1);
 }
@@ -2074,10 +2363,11 @@ static int cdns3_gadget_udc_stop(struct usb_gadget *gadget)
 	u32 bEndpointAddress;
 	struct usb_ep *ep;
 	int ret = 0;
+	int val;
 
 	priv_dev->gadget_driver = NULL;
 
-	priv_dev->onchip_mem_allocated_size = 0;
+	priv_dev->onchip_used_size = 0;
 	priv_dev->out_mem_is_allocated = 0;
 	priv_dev->gadget.speed = USB_SPEED_UNKNOWN;
 
@@ -2086,9 +2376,8 @@ static int cdns3_gadget_udc_stop(struct usb_gadget *gadget)
 		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);
+		readl_poll_timeout_atomic(&priv_dev->regs->ep_cmd, val,
+					  !(val & EP_CMD_EPRST), 1, 100);
 	}
 
 	/* disable interrupt for device */
@@ -2112,14 +2401,14 @@ static void cdns3_free_all_eps(struct cdns3_device *priv_dev)
 {
 	int i;
 
-	/*ep0 OUT point to ep0 IN*/
+	/* 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])
+		if (priv_dev->eps[i]) {
+			cdns3_free_trb_pool(priv_dev->eps[i]);
 			devm_kfree(priv_dev->dev, priv_dev->eps[i]);
+		}
 }
 
 /**
@@ -2217,27 +2506,12 @@ err:
 	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->dev_irq, cdns);
 
 	pm_runtime_mark_last_busy(cdns->dev);
@@ -2283,12 +2557,29 @@ static int cdns3_gadget_start(struct cdns3 *cdns)
 	priv_dev->dev = cdns->dev;
 	priv_dev->regs = cdns->dev_regs;
 
+	device_property_read_u16(priv_dev->dev, "cdns,on-chip-buff-size",
+				 &priv_dev->onchip_buffers);
+
+	if (priv_dev->onchip_buffers <=  0) {
+		u32 reg = readl(&priv_dev->regs->usb_cap2);
+
+		priv_dev->onchip_buffers = USB_CAP2_ACTUAL_MEM_SIZE(reg);
+	}
+
+	if (!priv_dev->onchip_buffers)
+		priv_dev->onchip_buffers = 256;
+
 	max_speed = usb_get_maximum_speed(cdns->dev);
 
 	/* Check the maximum_speed parameter */
 	switch (max_speed) {
 	case USB_SPEED_FULL:
+		writel(USB_CONF_SFORCE_FS, &priv_dev->regs->usb_conf);
+		writel(USB_CONF_USB3DIS, &priv_dev->regs->usb_conf);
+		break;
 	case USB_SPEED_HIGH:
+		writel(USB_CONF_USB3DIS, &priv_dev->regs->usb_conf);
+		break;
 	case USB_SPEED_SUPER:
 		break;
 	default:
@@ -2307,11 +2598,15 @@ static int cdns3_gadget_start(struct cdns3 *cdns)
 	priv_dev->gadget.ops = &cdns3_gadget_ops;
 	priv_dev->gadget.name = "usb-ss-gadget";
 	priv_dev->gadget.sg_supported = 1;
+	priv_dev->gadget.quirk_avoids_skb_reserve = 1;
 
 	spin_lock_init(&priv_dev->lock);
 	INIT_WORK(&priv_dev->pending_status_wq,
 		  cdns3_pending_setup_status_handler);
 
+	INIT_WORK(&priv_dev->aligned_buf_wq,
+		  cdns3_free_aligned_request_buf);
+
 	/* initialize endpoint container */
 	INIT_LIST_HEAD(&priv_dev->gadget.ep_list);
 	INIT_LIST_HEAD(&priv_dev->aligned_buf_list);
@@ -2326,7 +2621,6 @@ static int cdns3_gadget_start(struct cdns3 *cdns)
 	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;
 	}
@@ -2382,10 +2676,16 @@ static int __cdns3_gadget_init(struct cdns3 *cdns)
 		return ret;
 
 	priv_dev = cdns->gadget_dev;
+
+	/*
+	 * Because interrupt line can be shared with other components in
+	 * driver it can't use IRQF_ONESHOT flag here.
+	 */
 	ret = devm_request_threaded_irq(cdns->dev, cdns->dev_irq,
 					cdns3_device_irq_handler,
 					cdns3_device_thread_irq_handler,
 					IRQF_SHARED, dev_name(cdns->dev), cdns);
+
 	if (ret)
 		goto err0;
 
@@ -2397,25 +2697,31 @@ err0:
 
 static int cdns3_gadget_suspend(struct cdns3 *cdns, bool do_wakeup)
 {
-	cdns3_gadget_disable(cdns);
+	struct cdns3_device *priv_dev = cdns->gadget_dev;
+
+	cdns3_disconnect_gadget(priv_dev);
+
+	priv_dev->gadget.speed = USB_SPEED_UNKNOWN;
+	usb_gadget_set_state(&priv_dev->gadget, USB_STATE_NOTATTACHED);
+	cdns3_hw_reset_eps_config(priv_dev);
+
+	/* disable interrupt for device */
+	writel(0, &priv_dev->regs->usb_ien);
+
+	cdns3_gadget_pullup(&priv_dev->gadget, 0);
+
 	return 0;
 }
 
 static int cdns3_gadget_resume(struct cdns3 *cdns, bool hibernated)
 {
-	struct cdns3_device *priv_dev;
-	unsigned long flags;
+	struct cdns3_device *priv_dev = cdns->gadget_dev;
 
-	priv_dev = cdns->gadget_dev;
-	spin_lock_irqsave(&priv_dev->lock, flags);
-
-	if (!priv_dev->gadget_driver) {
-		spin_unlock_irqrestore(&priv_dev->lock, flags);
+	if (!priv_dev->gadget_driver)
 		return 0;
-	}
 
 	cdns3_gadget_config(priv_dev);
-	spin_unlock_irqrestore(&priv_dev->lock, flags);
+
 	return 0;
 }
 
@@ -2440,7 +2746,7 @@ int cdns3_gadget_init(struct cdns3 *cdns)
 	rdrv->resume	= cdns3_gadget_resume;
 	rdrv->state	= CDNS3_ROLE_STATE_INACTIVE;
 	rdrv->name	= "gadget";
-	cdns->roles[CDNS3_ROLE_GADGET] = rdrv;
+	cdns->roles[USB_ROLE_DEVICE] = rdrv;
 
 	return 0;
 }

+ 121 - 74
drivers/usb/cdns3/gadget.h

@@ -20,42 +20,52 @@
 
 /**
  * struct cdns3_usb_regs - device controller registers.
- * @usb_conf:      Global Configuration Register.
- * @usb_sts:       Global Status Register.
- * @usb_cmd:       Global Command Register.
- * @usb_itpn:      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.
+ * @usb_conf:      Global Configuration.
+ * @usb_sts:       Global Status.
+ * @usb_cmd:       Global Command.
+ * @usb_itpn:      ITP/SOF number.
+ * @usb_lpm:       Global Command.
+ * @usb_ien:       USB Interrupt Enable.
+ * @usb_ists:      USB Interrupt Status.
+ * @ep_sel:        Endpoint Select.
+ * @ep_traddr:     Endpoint Transfer Ring Address.
+ * @ep_cfg:        Endpoint Configuration.
+ * @ep_cmd:        Endpoint Command.
+ * @ep_sts:        Endpoint Status.
+ * @ep_sts_sid:    Endpoint Status.
+ * @ep_sts_en:     Endpoint Status Enable.
+ * @drbl:          Doorbell.
+ * @ep_ien:        EP Interrupt Enable.
+ * @ep_ists:       EP Interrupt Status.
+ * @usb_pwr:       Global Power Configuration.
+ * @usb_conf2:     Global Configuration 2.
+ * @usb_cap1:      Capability 1.
+ * @usb_cap2:      Capability 2.
+ * @usb_cap3:      Capability 3.
+ * @usb_cap4:      Capability 4.
+ * @usb_cap5:      Capability 5.
+ * @usb_cap6:      Capability 6.
+ * @usb_cpkt1:     Custom Packet 1.
+ * @usb_cpkt2:     Custom Packet 2.
+ * @usb_cpkt3:     Custom Packet 3.
+ * @ep_dma_ext_addr: Upper address for DMA operations.
+ * @buf_addr:      Address for On-chip Buffer operations.
+ * @buf_data:      Data for On-chip Buffer operations.
+ * @buf_ctrl:      On-chip Buffer Access Control.
+ * @dtrans:        DMA Transfer Mode.
+ * @tdl_from_trb:  Source of TD Configuration.
+ * @tdl_beh:       TDL Behavior Configuration.
+ * @ep_tdl:        Endpoint TDL.
+ * @tdl_beh2:      TDL Behavior 2 Configuration.
+ * @dma_adv_td:    DMA Advance TD Configuration.
  * @reserved1:     Reserved.
- * @cfg_regs:      Configuration registers.
+ * @cfg_regs:      Configuration.
  * @reserved2:     Reserved.
- * @dma_axi_ctrl:  AXI Control register.
+ * @dma_axi_ctrl:  AXI Control.
  * @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.
+ * @dma_axi_cap:   AXI Capability.
+ * @dma_axi_ctrl0: AXI Control 0.
+ * @dma_axi_ctrl1: AXI Control 1.
  */
 struct cdns3_usb_regs {
 	__le32 usb_conf;
@@ -86,12 +96,22 @@ struct cdns3_usb_regs {
 	__le32 usb_cpkt1;
 	__le32 usb_cpkt2;
 	__le32 usb_cpkt3;
-	__le32 reserved1[36];
+	__le32 ep_dma_ext_addr;
+	__le32 buf_addr;
+	__le32 buf_data;
+	__le32 buf_ctrl;
+	__le32 dtrans;
+	__le32 tdl_from_trb;
+	__le32 tdl_beh;
+	__le32 ep_tdl;
+	__le32 tdl_beh2;
+	__le32 dma_adv_td;
+	__le32 reserved1[26];
 	__le32 cfg_reg1;
 	__le32 dbg_link1;
 	__le32 dbg_link2;
 	__le32 cfg_regs[74];
-	__le32 reserved2[34];
+	__le32 reserved2[51];
 	__le32 dma_axi_ctrl;
 	__le32 dma_axi_id;
 	__le32 dma_axi_cap;
@@ -118,9 +138,9 @@ struct cdns3_usb_regs {
 #define USB_CONF_BENDIAN	BIT(6)
 /* Device software reset. */
 #define USB_CONF_SWRST		BIT(7)
-/* Singular DMA transfer mode. */
+/* Singular DMA transfer mode. Only for VER < DEV_VER_V3*/
 #define USB_CONF_DSING		BIT(8)
-/* Multiple DMA transfers mode. */
+/* Multiple DMA transfers mode. Only for VER < DEV_VER_V3 */
 #define USB_CONF_DMULT		BIT(9)
 /* DMA clock turn-off enable. */
 #define USB_CONF_DMAOFFEN	BIT(10)
@@ -192,6 +212,7 @@ struct cdns3_usb_regs {
  * DMA transfer configuration status.
  * 0 - single request.
  * 1 - multiple TRB chain
+ * Supported only for controller version <  DEV_VER_V3
  */
 #define USB_STS_DTRANS_MASK	BIT(3)
 #define USB_STS_DTRANS(p)	((p) & USB_STS_DTRANS_MASK)
@@ -248,7 +269,7 @@ struct cdns3_usb_regs {
  * Status of the "TDL calculation basing on TRB" feature.
  * 0 - disabled
  * 1 - enabled
- * Supported from the 0x0002450B version.
+ * Supported only for DEV_VER_V2 controller version.
  */
 #define USB_STS_TDL_TRB_ENABLED	BIT(11)
 /*
@@ -259,7 +280,7 @@ struct cdns3_usb_regs {
 #define USB_STS_DEVS_MASK	BIT(14)
 #define USB_STS_DEVS(p)		((p) & USB_STS_DEVS_MASK)
 /*
- * DAddress statuss.
+ * Address status.
  * 0 - USB device is default state.
  * 1 - USB device is at least in address state.
  */
@@ -344,7 +365,7 @@ struct cdns3_usb_regs {
 #define USB_STS_DMAOFF_MASK	BIT(30)
 #define USB_STS_DMAOFF(p)	((p) & USB_STS_DMAOFF_MASK)
 /*
- * SFR Endian statuss.
+ * SFR Endian status.
  * 0 - Little Endian order (default after hardware reset).
  * 1 - Big Endian order.
  */
@@ -454,7 +475,7 @@ struct cdns3_usb_regs {
 #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_IEN_L2EXTIEN | USB_IEN_L1ENTIEN | USB_IEN_U3ENTIEN)
 
 /* USB_ISTS - bitmasks */
 /* SS Connection detected. */
@@ -568,14 +589,20 @@ struct cdns3_usb_regs {
 /*
  * Transfer Descriptor Length write  (used only for Bulk Stream capable
  * endpoints in SS mode).
+ * Bit Removed from DEV_VER_V3 controller version.
  */
 #define EP_CMD_STDL		BIT(8)
-/* Transfer Descriptor Length (used only in SS mode for bulk endpoints). */
+/*
+ * Transfer Descriptor Length (used only in SS mode for bulk endpoints).
+ * Bits Removed from DEV_VER_V3 controller version.
+ */
 #define EP_CMD_TDL_MASK		GENMASK(15, 9)
-#define EP_CMD_TDL(p)		(((p) << 9) & EP_CMD_TDL_MASK)
+#define EP_CMD_TDL_SET(p)	(((p) << 9) & EP_CMD_TDL_MASK)
+#define EP_CMD_TDL_GET(p)	(((p) & EP_CMD_TDL_MASK) >> 9)
+
 /* 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)
+#define EP_CMD_ERDY_SID(p)	(((p) << 16) & EP_CMD_ERDY_SID_MASK)
 
 /* EP_STS - bitmasks */
 /* Setup transfer complete. */
@@ -596,8 +623,8 @@ struct cdns3_usb_regs {
 #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))
+/* DMA busy bit. */
+#define EP_STS_DBUSY		BIT(9)
 /* Endpoint Buffer Empty */
 #define EP_STS_BUFFEMPTY(p)	((p) & BIT(10))
 /* Current Cycle Status */
@@ -675,7 +702,7 @@ struct cdns3_usb_regs {
 #define EP_ISTS_EP_OUT0		BIT(0)
 #define EP_ISTS_EP_IN0		BIT(16)
 
-/* EP_PWR- bitmasks */
+/* USB_PWR- bitmasks */
 /*Power Shut Off capability enable*/
 #define PUSB_PWR_PSO_EN		BIT(0)
 /*Power Shut Off capability disable*/
@@ -694,17 +721,19 @@ struct cdns3_usb_regs {
 /* 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)
+#define PUSB_PWR_FST_REG_ACCESS		BIT(31)
 
 /* USB_CONF2- bitmasks */
 /*
  * Writing 1 disables TDL calculation basing on TRB feature in controller
- * for DMULT mode. Supported from the 0x0002450B version.
+ * for DMULT mode.
+ * Bit supported only for DEV_VER_V2 version.
  */
 #define USB_CONF2_DIS_TDL_TRB		BIT(1)
 /*
  * Writing 1 enables TDL calculation basing on TRB feature in controller
- * for DMULT mode. Supported from the 0x0002450B version.
+ * for DMULT mode.
+ * Bit supported only for DEV_VER_V2 version.
  */
 #define USB_CONF2_EN_TDL_TRB		BIT(2)
 
@@ -826,7 +855,7 @@ struct cdns3_usb_regs {
 /*
  * When set, indicates that controller supports automatic internal TDL
  * calculation basing on the size provided in TRB (TRB[22:17]) for DMULT mode
- * Supported from the 0x0002450B version.
+ * Supported only for DEV_VER_V2 controller version.
  */
 #define USB_CAP1_TDL_FROM_TRB(p)	((p) & BIT(28))
 
@@ -865,12 +894,13 @@ struct cdns3_usb_regs {
 /* USB_CAP6- bitmasks */
 /* The USBSS-DEV Controller  Internal build number. */
 #define GET_DEV_BASE_VERSION(p) ((p) & GENMASK(23, 0))
-/* The USBSS-DEV Controller  version number. */
+/* The USBSS-DEV Controller version number. */
 #define GET_DEV_CUSTOM_VERSION(p) ((p) & GENMASK(31, 24))
 
 #define DEV_VER_NXP_V1		0x00024502
 #define DEV_VER_TI_V1		0x00024509
-#define DEV_VER_V2		0x0002450B
+#define DEV_VER_V2		0x0002450C
+#define DEV_VER_V3		0x0002450d
 
 /* DBG_LINK1- bitmasks */
 /*
@@ -920,6 +950,13 @@ struct cdns3_usb_regs {
  */
 #define DBG_LINK1_LFPS_GEN_PING_SET		BIT(27)
 
+/* DMA_AXI_CTRL- bitmasks */
+/* The mawprot pin configuration. */
+#define DMA_AXI_CTRL_MARPROT(p) ((p) & GENMASK(2, 0))
+/* The marprot pin configuration. */
+#define DMA_AXI_CTRL_MAWPROT(p) (((p) & GENMASK(2, 0)) << 16)
+#define DMA_AXI_CTRL_NON_SECURE 0x02
+
 #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))
@@ -932,6 +969,10 @@ struct cdns3_usb_regs {
 
 #define ISO_MAX_INTERVAL	10
 
+#if TRBS_PER_SEGMENT < 2
+#error "Incorrect TRBS_PER_SEGMENT. Minimal Transfer Ring size is 2."
+#endif
+
 /*
  *Only for ISOC endpoints - maximum number of TRBs is calculated as
  * pow(2, bInterval-1) * number of usb requests. It is limitation made by
@@ -1000,7 +1041,9 @@ struct cdns3_trb {
 #define TRB_IOC			BIT(5)
 
 /* stream ID bitmasks. */
-#define TRB_STREAM_ID(p)	((p) & GENMASK(31, 16))
+#define TRB_STREAM_ID_BITMASK		GENMASK(31, 16)
+#define TRB_STREAM_ID(p)		((p) << 16)
+#define TRB_FIELD_TO_STREAMID(p)	(((p) & TRB_STREAM_ID_BITMASK) >> 16)
 
 /* Size of TD expressed in USB packets for HS/FS mode. */
 #define TRB_TDL_HS_SIZE(p)	(((p) << 16) & GENMASK(31, 16))
@@ -1086,17 +1129,19 @@ struct cdns3_endpoint {
 	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)
-
+#define EP_STALLED		BIT(1)
+#define EP_STALL_PENDING	BIT(2)
+#define EP_WEDGE		BIT(3)
+#define EP_TRANSFER_STARTED	BIT(4)
+#define EP_UPDATE_EP_TRBADDR	BIT(5)
+#define EP_PENDING_REQUEST	BIT(6)
+#define EP_RING_FULL		BIT(7)
+#define EP_CLAIMED		BIT(8)
+#define EP_DEFERRED_DRDY	BIT(9)
+#define EP_QUIRK_ISO_OUT_EN	BIT(10)
+#define EP_QUIRK_END_TRANSFER	BIT(11)
+#define EP_QUIRK_EXTRA_BUF_DET	BIT(12)
+#define EP_QUIRK_EXTRA_BUF_EN	BIT(13)
 	u32			flags;
 
 	struct cdns3_request	*descmis_req;
@@ -1189,8 +1234,7 @@ struct cdns3_request {
  * @ep0_data_dir: direction for control transfer
  * @eps: array of pointers to all endpoints with exclusion ep0
  * @aligned_buf_list: list of aligned buffers internally allocated by driver
- * @run_garbage_colector: infroms that at least one element of aligned_buf_list
- *                        can be freed
+ * @aligned_buf_wq: workqueue freeing  no longer used aligned buf.
  * @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.
@@ -1202,11 +1246,9 @@ struct cdns3_request {
  * @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
+ * @onchip_buffers: number of available on-chip buffers.
+ * @onchip_used_size: actual size of on-chip memory assigned to endpoints.
  * @pending_status_wq: workqueue handling status stage for deferred requests.
- * @shadow_ep_en: hold information about endpoints that will be enabled
- *     in soft irq.
  * @pending_status_request: request for which status stage was deferred
  */
 struct cdns3_device {
@@ -1235,11 +1277,12 @@ struct cdns3_device {
 	struct cdns3_endpoint		*eps[CDNS3_ENDPOINTS_MAX_COUNT];
 
 	struct list_head		aligned_buf_list;
-	unsigned			run_garbage_colector:1;
+	struct work_struct		aligned_buf_wq;
 
 	u32				selected_ep;
 	u16				isoch_delay;
 
+	unsigned			wait_for_setup:1;
 	unsigned			u1_allowed:1;
 	unsigned			u2_allowed:1;
 	unsigned			is_selfpowered:1;
@@ -1251,9 +1294,10 @@ struct cdns3_device {
 
 	struct work_struct		pending_status_wq;
 	struct usb_request		*pending_status_request;
-	u32				shadow_ep_en;
+
 	/*in KB */
-	int				onchip_mem_allocated_size;
+	u16				onchip_buffers;
+	u16				onchip_used_size;
 };
 
 void cdns3_set_register_bit(void __iomem *ptr, u32 mask);
@@ -1273,6 +1317,8 @@ 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);
+void __cdns3_gadget_ep_set_halt(struct cdns3_endpoint *priv_ep);
+int __cdns3_gadget_ep_clear_halt(struct cdns3_endpoint *priv_ep);
 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,
@@ -1287,5 +1333,6 @@ int cdns3_init_ep0(struct cdns3_device *priv_dev,
 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);
+int __cdns3_gadget_wakeup(struct cdns3_device *priv_dev);
 
 #endif /* __LINUX_CDNS3_GADGET */

+ 3 - 5
drivers/usb/cdns3/host.c

@@ -2,11 +2,11 @@
 /*
  * Cadence USBSS DRD Driver - host side
  *
- * Copyright (C) 2018 Cadence Design Systems.
+ * Copyright (C) 2018-2019 Cadence Design Systems.
  * Copyright (C) 2017-2018 NXP
  *
  * Authors: Peter Chen <peter.chen@nxp.com>
- *	    Pawel Laszczak <pawell@cadence.com>
+ *          Pawel Laszczak <pawell@cadence.com>
  */
 
 #include <linux/platform_device.h>
@@ -66,11 +66,9 @@ int cdns3_host_init(struct cdns3 *cdns)
 	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;
+	cdns->roles[USB_ROLE_HOST] = rdrv;
 
 	return 0;
 }

+ 1 - 13
drivers/usb/cdns3/trace.c

@@ -2,22 +2,10 @@
 /*
  * USBSS device controller driver Trace Support
  *
- * Copyright (C) 2018 Cadence.
+ * Copyright (C) 2018-2019 Cadence.
  *
  * Author: Pawel Laszczak <pawell@cadence.com>
  */
 
 #define CREATE_TRACE_POINTS
 #include "trace.h"
-
-void cdns3_dbg(struct cdns3_device *priv_dev, const char *fmt, ...)
-{
-	struct va_format vaf;
-	va_list args;
-
-	va_start(args, fmt);
-	vaf.fmt = fmt;
-	vaf.va = &args;
-	trace_cdns3_log(priv_dev, &vaf);
-	va_end(args);
-}

+ 63 - 17
drivers/usb/cdns3/trace.h

@@ -3,7 +3,7 @@
  * USBSS device controller driver.
  * Trace support header file.
  *
- * Copyright (C) 2018 Cadence.
+ * Copyright (C) 2018-2019 Cadence.
  *
  * Author: Pawel Laszczak <pawell@cadence.com>
  */
@@ -24,18 +24,49 @@
 
 #define CDNS3_MSG_MAX	500
 
-TRACE_EVENT(cdns3_log,
-	TP_PROTO(struct cdns3_device *priv_dev, struct va_format *vaf),
-	TP_ARGS(priv_dev, vaf),
+TRACE_EVENT(cdns3_halt,
+	TP_PROTO(struct cdns3_endpoint *ep_priv, u8 halt, u8 flush),
+	TP_ARGS(ep_priv, halt, flush),
 	TP_STRUCT__entry(
-		__string(name, dev_name(priv_dev->dev))
-		__dynamic_array(char, msg, CDNS3_MSG_MAX)
+		__string(name, ep_priv->name)
+		__field(u8, halt)
+		__field(u8, flush)
 	),
 	TP_fast_assign(
-		__assign_str(name, dev_name(priv_dev->dev));
-		vsnprintf(__get_str(msg), CDNS3_MSG_MAX, vaf->fmt, *vaf->va);
+		__assign_str(name, ep_priv->name);
+		__entry->halt = halt;
+		__entry->flush = flush;
 	),
-	TP_printk("%s: %s", __get_str(name), __get_str(msg))
+	TP_printk("Halt %s for %s: %s", __entry->flush ? " and flush" : "",
+		  __get_str(name), __entry->halt ? "set" : "cleared")
+);
+
+TRACE_EVENT(cdns3_wa1,
+	TP_PROTO(struct cdns3_endpoint *ep_priv, char *msg),
+	TP_ARGS(ep_priv, msg),
+	TP_STRUCT__entry(
+		__string(ep_name, ep_priv->name)
+		__string(msg, msg)
+	),
+	TP_fast_assign(
+		__assign_str(ep_name, ep_priv->name);
+		__assign_str(msg, msg);
+	),
+	TP_printk("WA1: %s %s", __get_str(ep_name), __get_str(msg))
+);
+
+TRACE_EVENT(cdns3_wa2,
+	TP_PROTO(struct cdns3_endpoint *ep_priv, char *msg),
+	TP_ARGS(ep_priv, msg),
+	TP_STRUCT__entry(
+		__string(ep_name, ep_priv->name)
+		__string(msg, msg)
+	),
+	TP_fast_assign(
+		__assign_str(ep_name, ep_priv->name);
+		__assign_str(msg, msg);
+	),
+	TP_printk("WA2: %s %s", __get_str(ep_name), __get_str(msg))
 );
 
 DECLARE_EVENT_CLASS(cdns3_log_doorbell,
@@ -49,7 +80,7 @@ DECLARE_EVENT_CLASS(cdns3_log_doorbell,
 		__assign_str(name, ep_name);
 		__entry->ep_trbaddr = ep_trbaddr;
 	),
-	TP_printk("//Ding Dong %s, ep_trbaddr %08x", __get_str(name),
+	TP_printk("%s, ep_trbaddr %08x", __get_str(name),
 		  __entry->ep_trbaddr)
 );
 
@@ -199,9 +230,9 @@ DECLARE_EVENT_CLASS(cdns3_log_request,
 		  " trb: [start:%d, end:%d: virt addr %pa], flags:%x ",
 		__get_str(name), __entry->req, __entry->buf, __entry->actual,
 		__entry->length,
-		__entry->zero ? "zero | " : "",
-		__entry->short_not_ok ? "short | " : "",
-		__entry->no_interrupt ? "no int" : "",
+		__entry->zero ? "Z" : "z",
+		__entry->short_not_ok ? "S" : "s",
+		__entry->no_interrupt ? "I" : "i",
 		__entry->status,
 		__entry->start_trb,
 		__entry->end_trb,
@@ -235,6 +266,21 @@ DEFINE_EVENT(cdns3_log_request, cdns3_gadget_giveback,
 	TP_ARGS(req)
 );
 
+TRACE_EVENT(cdns3_ep0_queue,
+	TP_PROTO(struct cdns3_device *dev_priv, struct usb_request *request),
+	TP_ARGS(dev_priv, request),
+	TP_STRUCT__entry(
+		__field(int, dir)
+		__field(int, length)
+	),
+	TP_fast_assign(
+		__entry->dir = dev_priv->ep0_data_dir;
+		__entry->length = request->length;
+	),
+	TP_printk("Queue to ep0%s length: %u", __entry->dir ? "in" : "out",
+		  __entry->length)
+);
+
 DECLARE_EVENT_CLASS(cdns3_log_aligned_request,
 	TP_PROTO(struct cdns3_request *priv_req),
 	TP_ARGS(priv_req),
@@ -256,9 +302,9 @@ DECLARE_EVENT_CLASS(cdns3_log_aligned_request,
 		__entry->aligned_dma = priv_req->aligned_buf->dma;
 		__entry->aligned_buf_size = priv_req->aligned_buf->size;
 	),
-	TP_printk("%s: req: %p, req buf %p, dma %08llx a_buf %p a_dma %08llx, size %d",
-		__get_str(name), __entry->req, __entry->buf, __entry->dma,
-		__entry->aligned_buf, __entry->aligned_dma,
+	TP_printk("%s: req: %p, req buf %p, dma %pad a_buf %p a_dma %pad, size %d",
+		__get_str(name), __entry->req, __entry->buf, &__entry->dma,
+		__entry->aligned_buf, &__entry->aligned_dma,
 		__entry->aligned_buf_size
 	)
 );
@@ -376,7 +422,7 @@ DECLARE_EVENT_CLASS(cdns3_log_ep,
 		__entry->maxburst, __entry->enqueue,
 		__entry->dequeue,
 		__entry->flags & EP_ENABLED ? "EN | " : "",
-		__entry->flags & EP_STALL ? "STALL | " : "",
+		__entry->flags & EP_STALLED ? "STALLED | " : "",
 		__entry->flags & EP_WEDGE ? "WEDGE | " : "",
 		__entry->flags & EP_TRANSFER_STARTED ? "STARTED | " : "",
 		__entry->flags & EP_UPDATE_EP_TRBADDR ? "UPD TRB | " : "",

+ 2 - 1
drivers/usb/common/Makefile

@@ -4,7 +4,8 @@
 #
 
 obj-$(CONFIG_USB_COMMON)	  += usb-common.o
-usb-common-y			  += common.o debug.o
+usb-common-y			  += common.o
+usb-common-$(CONFIG_TRACING)	  += debug.o
 usb-common-$(CONFIG_USB_LED_TRIG) += led.o
 
 obj-$(CONFIG_USB_OTG_FSM) += usb-otg-fsm.o

+ 3 - 5
drivers/usb/common/debug.c

@@ -8,15 +8,15 @@
  *	    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, size_t size)
 {
 	switch (bRequestType & USB_RECIP_MASK) {
+	case USB_RECIP_DEVICE:
+		snprintf(str, size, "Get Device Status(Length = %d)", wLength);
+		break;
 	case USB_RECIP_INTERFACE:
 		snprintf(str, size,
 			 "Get Interface Status(Intf = %d, Length = %d)",
@@ -266,5 +266,3 @@ const char *usb_decode_ctrl(char *str, size_t size, __u8 bRequestType,
 	return str;
 }
 EXPORT_SYMBOL_GPL(usb_decode_ctrl);
-
-#endif /* __LINUX_USB_COMMON_DEBUG */

+ 5 - 3
include/linux/usb/ch9.h

@@ -62,6 +62,7 @@ extern enum usb_device_speed usb_get_maximum_speed(struct device *dev);
  */
 extern const char *usb_state_string(enum usb_device_state state);
 
+#ifdef CONFIG_TRACING
 /**
  * usb_decode_ctrl - Returns human readable representation of control request.
  * @str: buffer to return a human-readable representation of control request.
@@ -83,8 +84,9 @@ extern const char *usb_state_string(enum usb_device_state state);
  * Important: wValue, wIndex, wLength parameters before invoking this function
  * should be processed by le16_to_cpu macro.
  */
-const char *usb_decode_ctrl(char *str, size_t size, __u8 bRequestType,
-			    __u8 bRequest, __u16 wValue, __u16 wIndex,
-			    __u16 wLength);
+extern const char *usb_decode_ctrl(char *str, size_t size, __u8 bRequestType,
+				   __u8 bRequest, __u16 wValue, __u16 wIndex,
+				   __u16 wLength);
+#endif
 
 #endif /* __LINUX_USB_CH9_H */