From 38ee0fb9589efd802dc9ff787f9bd2537324b78e Mon Sep 17 00:00:00 2001
From: Chen Zhenhua <chenzhenhua@phytium.com.cn>
Date: Fri, 14 Jul 2023 08:34:17 +0800
Subject: [PATCH] usb: phytium: Add support for Phytium USB controller

This patch adds the Phytium USBHS DRD controller support.

Signed-off-by: Wang Zhimin <wangzhimin1179@phytium.com.cn>
Signed-off-by: Li Mingzhe <limingzhe1839@phytium.com.cn>
Signed-off-by: Zuo Qian <zuoqian2032@phytium.com.cn>
Signed-off-by: Chen Zhenhua <chenzhenhua@phytium.com.cn>
Signed-off-by: Chen Baozi <chenbaozi@phytium.com.cn>
Signed-off-by: Wang Yinfeng <wangyinfeng@phytium.com.cn>
---
 drivers/usb/Kconfig            |    2 +
 drivers/usb/Makefile           |    1 +
 drivers/usb/phytium/Kconfig    |   22 +
 drivers/usb/phytium/Makefile   |    7 +
 drivers/usb/phytium/core.c     |   44 +
 drivers/usb/phytium/core.h     |   98 ++
 drivers/usb/phytium/dma.c      |  783 +++++++++
 drivers/usb/phytium/dma.h      |  197 +++
 drivers/usb/phytium/gadget.c   | 2538 +++++++++++++++++++++++++++++
 drivers/usb/phytium/gadget.h   |  253 +++
 drivers/usb/phytium/host.c     | 2761 ++++++++++++++++++++++++++++++++
 drivers/usb/phytium/host_api.h |  250 +++
 drivers/usb/phytium/hw-regs.h  |  297 ++++
 drivers/usb/phytium/pci.c      |  208 +++
 drivers/usb/phytium/platform.c |  216 +++
 15 files changed, 7677 insertions(+)
 create mode 100644 drivers/usb/phytium/Kconfig
 create mode 100644 drivers/usb/phytium/Makefile
 create mode 100644 drivers/usb/phytium/core.c
 create mode 100644 drivers/usb/phytium/core.h
 create mode 100644 drivers/usb/phytium/dma.c
 create mode 100644 drivers/usb/phytium/dma.h
 create mode 100644 drivers/usb/phytium/gadget.c
 create mode 100644 drivers/usb/phytium/gadget.h
 create mode 100644 drivers/usb/phytium/host.c
 create mode 100644 drivers/usb/phytium/host_api.h
 create mode 100644 drivers/usb/phytium/hw-regs.h
 create mode 100644 drivers/usb/phytium/pci.c
 create mode 100644 drivers/usb/phytium/platform.c

diff --git a/drivers/usb/Kconfig b/drivers/usb/Kconfig
index 7f33bcc315f27..7eaccbc6a5b13 100644
--- a/drivers/usb/Kconfig
+++ b/drivers/usb/Kconfig
@@ -129,6 +129,8 @@ source "drivers/usb/chipidea/Kconfig"
 
 source "drivers/usb/isp1760/Kconfig"
 
+source "drivers/usb/phytium/Kconfig"
+
 comment "USB port drivers"
 
 if USB
diff --git a/drivers/usb/Makefile b/drivers/usb/Makefile
index 3a9a0dd4be706..407a639164703 100644
--- a/drivers/usb/Makefile
+++ b/drivers/usb/Makefile
@@ -18,6 +18,7 @@ obj-$(CONFIG_USB_CDNS3)		+= cdns3/
 obj-$(CONFIG_USB_CDNSP_PCI)	+= cdns3/
 
 obj-$(CONFIG_USB_FOTG210)	+= fotg210/
+obj-$(CONFIG_USB_PHYTIUM)	+= phytium/
 
 obj-$(CONFIG_USB_MON)		+= mon/
 obj-$(CONFIG_USB_MTU3)		+= mtu3/
diff --git a/drivers/usb/phytium/Kconfig b/drivers/usb/phytium/Kconfig
new file mode 100644
index 0000000000000..324c8eb8d188d
--- /dev/null
+++ b/drivers/usb/phytium/Kconfig
@@ -0,0 +1,22 @@
+config USB_PHYTIUM
+	tristate "Phytium USB Support"
+	depends on USB
+	depends on USB_GADGET
+	help
+	  Say Y or M here if your system has a OTG USB Controller based on PHYTIUM SOC.
+	  like Pe220x.
+
+	  If you choose to build this driver is a dynamically linked modules, the module will
+	  be called phytium-usb.ko
+
+config USB_PHYTIUM_PCI
+	tristate "Phytium PCI USB Support"
+	default n
+	depends on USB
+	depends on USB_GADGET
+	help
+	  Say Y or M here if your system has a OTG USB Controller based on PHYTIUM SOC.
+	  like Pe220x.
+
+	  If you choose to build this driver is a dynamically linked modules, the module will
+	  be called phytium-usb.ko
diff --git a/drivers/usb/phytium/Makefile b/drivers/usb/phytium/Makefile
new file mode 100644
index 0000000000000..e176c334cba56
--- /dev/null
+++ b/drivers/usb/phytium/Makefile
@@ -0,0 +1,7 @@
+# SPDX-License-Identifier: GPL-2.0
+
+obj-$(CONFIG_USB_PHYTIUM) += phytium-usb.o
+obj-$(CONFIG_USB_PHYTIUM_PCI) += phytium-usb-pci.o
+
+phytium-usb-y := core.o dma.o platform.o host.o gadget.o
+phytium-usb-pci-y := core.o dma.o pci.o host.o gadget.o
diff --git a/drivers/usb/phytium/core.c b/drivers/usb/phytium/core.c
new file mode 100644
index 0000000000000..c0182c0770e5e
--- /dev/null
+++ b/drivers/usb/phytium/core.c
@@ -0,0 +1,44 @@
+// SPDX-License-Identifier: GPL-2.0
+
+#include "core.h"
+
+int phytium_core_reset(struct phytium_cusb *config, bool skip_wait)
+{
+	if (!config)
+		return 0;
+
+	spin_lock_init(&config->lock);
+
+	return 0;
+}
+
+uint32_t phytium_read32(uint32_t *address)
+{
+	return readl(address);
+}
+
+void phytium_write32(uint32_t *address, uint32_t value)
+{
+	writel(value, address);
+}
+
+uint16_t phytium_read16(uint16_t *address)
+{
+	return readw(address);
+}
+
+void phytium_write16(uint16_t *address, uint16_t value)
+{
+	writew(value, address);
+}
+
+uint8_t phytium_read8(uint8_t *address)
+{
+	return readb(address);
+}
+
+void phytium_write8(uint8_t *address, uint8_t value)
+{
+	writeb(value, address);
+}
+
diff --git a/drivers/usb/phytium/core.h b/drivers/usb/phytium/core.h
new file mode 100644
index 0000000000000..f563672ccaa99
--- /dev/null
+++ b/drivers/usb/phytium/core.h
@@ -0,0 +1,98 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+
+#ifndef __PHYTIUM_CORE_H__
+#define __PHYTIUM_CORE_H__
+
+#include <linux/usb/gadget.h>
+#include <linux/usb/otg.h>
+#include "host_api.h"
+#include "gadget.h"
+
+#define MAX_EPS_CHANNELS	16
+
+struct phytium_ep {
+	struct phytium_cusb	*config;
+	u16			max_packet;
+	u8			ep_num;
+	struct GADGET_EP	*gadget_ep;
+	struct list_head	req_list;
+	struct usb_ep		end_point;
+	char			name[12];
+	u8 is_tx;
+	const struct usb_endpoint_descriptor	*desc;
+	u8			busy;
+};
+
+struct phytium_request {
+	struct usb_request request;
+	struct GADGET_REQ	*gadget_request;
+	struct list_head	list;
+	struct phytium_ep	*ep;
+	struct phytium_cusb	*config;
+	u8			is_tx;
+	u8			epnum;
+};
+
+struct phytium_cusb {
+	struct device		*dev;
+	void __iomem		*regs;
+	void __iomem		*phy_regs;
+	int			irq;
+	spinlock_t		lock;
+	enum usb_dr_mode	dr_mode;
+
+	struct GADGET_OBJ	*gadget_obj;
+	struct GADGET_CFG	gadget_cfg;
+	struct GADGET_CALLBACKS gadget_callbacks;
+	struct GADGET_SYSREQ	gadget_sysreq;
+	struct GADGET_DEV	*gadget_dev;
+	void			*gadget_priv;
+
+	struct usb_gadget	gadget;
+	struct usb_gadget_driver *gadget_driver;
+	struct phytium_ep	endpoints_tx[MAX_EPS_CHANNELS];
+	struct phytium_ep	endpoints_rx[MAX_EPS_CHANNELS];
+	u8			ep0_data_stage_is_tx;
+
+	struct HOST_OBJ		*host_obj;
+	struct HOST_CFG		host_cfg;
+	struct HOST_CALLBACKS	host_callbacks;
+	struct HOST_SYSREQ	host_sysreq;
+	void			*host_priv;
+	struct usb_hcd          *hcd;
+
+	struct DMA_OBJ		*dma_obj;
+	struct DMA_CFG		dma_cfg;
+	struct DMA_CALLBACKS	dma_callbacks;
+	struct DMA_SYSREQ	dma_sysreq;
+	bool	isVhubHost;
+};
+
+int phytium_core_reset(struct phytium_cusb *config, bool skip_wait);
+
+int phytium_host_init(struct phytium_cusb *config);
+int phytium_host_uninit(struct phytium_cusb *config);
+
+#ifdef CONFIG_PM
+int phytium_host_resume(void *priv);
+int phytium_host_suspend(void *priv);
+int phytium_gadget_resume(void *priv);
+int phytium_gadget_suspend(void *priv);
+#endif
+
+int phytium_gadget_init(struct phytium_cusb *config);
+int phytium_gadget_uninit(struct phytium_cusb *config);
+
+uint32_t phytium_read32(uint32_t *address);
+
+void phytium_write32(uint32_t *address, uint32_t value);
+
+uint16_t phytium_read16(uint16_t *address);
+
+void phytium_write16(uint16_t *address, uint16_t value);
+
+uint8_t phytium_read8(uint8_t *address);
+
+void phytium_write8(uint8_t *address, uint8_t value);
+
+#endif
diff --git a/drivers/usb/phytium/dma.c b/drivers/usb/phytium/dma.c
new file mode 100644
index 0000000000000..30eb8d51f89f1
--- /dev/null
+++ b/drivers/usb/phytium/dma.c
@@ -0,0 +1,783 @@
+// SPDX-License-Identifier: GPL-2.0
+
+#include <linux/errno.h>
+#include <linux/string.h>
+#include "core.h"
+#include "dma.h"
+#include "hw-regs.h"
+
+#define TRB_POOL_SIZE (sizeof(struct DMA_Trb) * (NUM_OF_TRB))
+#define TRB_BURST_LENGTH        (0x80 << 24)
+
+#define BUILD_NORMAL_TRB_NO_IOC(trb, data_ptr, data_size, stream_id) { \
+	trb.dmaAddr = data_ptr; \
+	trb.dmaSize = data_size; \
+	trb.ctrl = (stream_id << 16) | TD_TYPE_NORMAL | TDF_CYCLE_BIT; }
+
+#define BUILD_NORMAL_TRB_NO_IOC_CHAIN(trb, data_ptr, data_size, stream_id) { \
+	trb.dmaAddr = data_ptr; \
+	trb.dmaSize = data_size; \
+	trb.ctrl = (stream_id << 16) | TD_TYPE_NORMAL | TDF_CYCLE_BIT | TDF_CHAIN_BIT; }
+
+#define BUILD_NORMAL_TRB(trb, data_ptr, data_size, stream_id) { \
+	trb.dmaAddr = data_ptr; \
+	trb.dmaSize = (data_size) | TRB_BURST_LENGTH; \
+	trb.ctrl = (stream_id << 16) | TD_TYPE_NORMAL | TDF_CYCLE_BIT |\
+	TDF_INT_ON_COMPLECTION | TDF_INT_ON_SHORT_PACKET; }
+
+#define BUILD_LINK_TRB(trb, target_ptr) { \
+	trb.dmaAddr = target_ptr; \
+	trb.dmaSize = 0; \
+	trb.ctrl = TD_TYPE_LINK | TDF_CYCLE_BIT | TDF_CHAIN_BIT; }
+
+#define BUILD_EMPTY_TRB(trb) { \
+	trb.dmaAddr = 0; \
+	trb.dmaSize = 0; \
+	trb.ctrl = 0; }
+
+uint32_t divRoundUp(uint32_t divident, uint32_t divisor)
+{
+	return divisor ? ((divident + divisor - 1) / divisor) : 0;
+}
+
+static inline struct DMA_TrbChainDesc *GetTrbChainDescEntry(struct list_head *list)
+{
+	return (struct DMA_TrbChainDesc *)((uintptr_t)list -
+			(uintptr_t)&(((struct DMA_TrbChainDesc *)0)->chainNode));
+}
+
+static int32_t phytium_dma_probe(struct DMA_CFG *config, struct DMA_SYSREQ *sysReq)
+{
+	if (!sysReq)
+		return -EINVAL;
+
+	sysReq->trbMemSize = TRB_POOL_SIZE;
+	sysReq->privDataSize = sizeof(struct DMA_CONTROLLER);
+
+	return 0;
+}
+
+static int32_t phytium_dma_init(struct DMA_CONTROLLER *priv,
+		const struct DMA_CFG *config, struct DMA_CALLBACKS *callbacks)
+{
+	if (!priv || !config || !callbacks)
+		return -EINVAL;
+
+	if (!config->trbAddr || !config->trbDmaAddr)
+		return -EINVAL;
+
+	memset((void *)priv, 0, sizeof(struct DMA_CONTROLLER));
+	memset((void *)config->trbAddr, 0, TRB_POOL_SIZE);
+
+	priv->trbDMAPoolAddr = config->trbDmaAddr;
+	priv->trbPoolAddr = config->trbAddr;
+	priv->regs = (struct DMARegs *)config->regBase;
+	priv->dmaCfg = *config;
+	priv->dmaDrv = DMA_GetInstance();
+	priv->dmaCallbacks = *callbacks;
+	priv->isHostCtrlMode = 0;
+	return 0;
+}
+
+static void phytium_dma_destroy(struct DMA_CONTROLLER *priv)
+{
+
+}
+
+static int32_t phytium_dma_start(struct DMA_CONTROLLER *priv)
+{
+	int i;
+
+	if (!priv)
+		return -EINVAL;
+
+	priv->dmaMode = DMA_MODE_CHANNEL_INDIVIDUAL;
+	if ((priv->dmaCfg.dmaModeRx & priv->dmaCfg.dmaModeTx) == 0xFFFF) {
+		priv->dmaMode = DMA_MODE_GLOBAL_DMULT;
+		phytium_write32(&priv->regs->conf, DMARF_DMULT);
+	} else if ((priv->dmaCfg.dmaModeRx | priv->dmaCfg.dmaModeTx)) {
+		priv->dmaMode = DMA_MODE_GLOBAL_DSING;
+		phytium_write32(&priv->regs->conf, DMARF_DSING);
+	}
+
+	for (i = 0; i < MAX_DMA_CHANNELS; i++) {
+		if (priv->dmaCfg.dmaModeRx & (1 << i)) {
+			priv->rx[i].dmultEnabled = 1;
+			priv->rx[i].maxTrbLen = TD_DMULT_MAX_TRB_DATA_SIZE;
+			priv->rx[i].maxTdLen = TD_DMULT_MAX_TD_DATA_SIZE;
+		} else {
+			priv->rx[i].dmultEnabled = 0;
+			priv->rx[i].maxTrbLen = TD_SING_MAX_TRB_DATA_SIZE;
+			priv->rx[i].maxTdLen = TD_SING_MAX_TD_DATA_SIZE;
+		}
+		priv->rx[i].lastTransferLength = 0;
+
+		if (priv->dmaCfg.dmaModeTx & (1 << i)) {
+			priv->tx[i].dmultEnabled = 1;
+			priv->tx[i].maxTrbLen = TD_DMULT_MAX_TRB_DATA_SIZE;
+			priv->tx[i].maxTdLen = TD_DMULT_MAX_TD_DATA_SIZE;
+		} else {
+			priv->tx[i].dmultEnabled = 0;
+			priv->tx[i].maxTrbLen = TD_SING_MAX_TRB_DATA_SIZE;
+			priv->tx[i].maxTdLen = TD_SING_MAX_TD_DATA_SIZE;
+		}
+		priv->tx[i].lastTransferLength = 0;
+	}
+
+	return 0;
+}
+
+static uint32_t phytium_dma_stop(struct DMA_CONTROLLER *priv)
+{
+
+	return 0;
+}
+
+static int32_t updateDescBuffer(struct DMA_CONTROLLER *priv,
+		struct DMA_TrbChainDesc *trbChainDesc, uint16_t status)
+{
+	uint32_t ep_sel, i;
+	struct DMA_Channel *channel;
+
+	if (!priv || !trbChainDesc)
+		return -EINVAL;
+
+	channel = trbChainDesc->channel;
+	if (!channel)
+		return -EINVAL;
+
+	if (channel->isIsoc && trbChainDesc->lastTrbIsLink) {
+		if (channel->trbChainDescList.prev == channel->trbChainDescList.next)
+			return 0;
+	}
+
+	for (i = 0; i < trbChainDesc->numOfTrbs; i++) {
+		if (trbChainDesc->framesDesc) {
+			if (trbChainDesc->isoError)
+				trbChainDesc->framesDesc[i].length = 0;
+			else
+				trbChainDesc->framesDesc[i].length =
+					(uint32_t)trbChainDesc->trbPool[i].dmaSize & TD_SIZE_MASK;
+		}
+
+		trbChainDesc->actualLen +=
+			(uint32_t)trbChainDesc->trbPool[i].dmaSize & TD_SIZE_MASK;
+	}
+
+	if (trbChainDesc->isoError)
+		trbChainDesc->actualLen = 0;
+
+	ep_sel = phytium_read32(&priv->regs->ep_sel);
+	channel->lastTransferLength = trbChainDesc->actualLen;
+
+	if (!trbChainDesc->lastTrbIsLink) {
+		if (!list_empty(&trbChainDesc->chainNode)) {
+			for (i = 0; i < trbChainDesc->mapSize; i++)
+				priv->trbChainFreeMap[(trbChainDesc->start >> 3) + i] = 0;
+
+			trbChainDesc->channel->numOfTrbChain--;
+			trbChainDesc->reserved = 0;
+			list_del(&trbChainDesc->chainNode);
+			trbChainDesc = NULL;
+		}
+	}
+
+	if (channel->trbChainDescList.prev == &channel->trbChainDescList)
+		channel->status = DMA_STATUS_FREE;
+
+	if (priv->dmaCallbacks.complete)
+		priv->dmaCallbacks.complete(priv->parent, channel->hwUsbEppNum,
+				channel->isDirTx, false);
+
+	if (channel->trbChainDescList.next != &channel->trbChainDescList) {
+		trbChainDesc = GetTrbChainDescEntry(channel->trbChainDescList.next);
+		if (trbChainDesc && trbChainDesc->lastTrbIsLink) {
+			if (!list_empty(&trbChainDesc->chainNode)) {
+				for (i = 0; i < trbChainDesc->mapSize; i++)
+					priv->trbChainFreeMap[(trbChainDesc->start >> 3) + i] = 0;
+
+				trbChainDesc->channel->numOfTrbChain--;
+				trbChainDesc->reserved = 0;
+				list_del(&trbChainDesc->chainNode);
+			}
+		}
+	}
+	phytium_write32(&priv->regs->ep_sel, ep_sel);
+
+	return 0;
+}
+
+static void phytium_dma_isr(struct DMA_CONTROLLER *priv)
+{
+	uint32_t ep_sts, ep_ists, ep_cfg;
+	int i;
+	uint8_t isDirTx, epNum;
+	struct DMA_Channel *channel;
+	struct DMA_TrbChainDesc *trbChainDesc;
+
+	if (!priv)
+		return;
+
+	ep_ists = phytium_read32(&priv->regs->ep_ists);
+	if (!ep_ists) {
+		phytium_write32(&priv->regs->ep_sts, DMARF_EP_IOC |
+				DMARF_EP_ISP | DMARF_EP_TRBERR);
+		return;
+	}
+
+	for (i = 0; i < 32; i++) {
+		if (!(ep_ists & (1 << i)))
+			continue;
+
+		isDirTx = i > 15 ? DMARD_EP_TX : 0u;
+		epNum = isDirTx ? i - 16 : i;
+		phytium_write32(&priv->regs->ep_sel, epNum | isDirTx);
+
+		if (isDirTx)
+			channel = &priv->tx[epNum];
+		else
+			channel = &priv->rx[epNum];
+
+		ep_sts = phytium_read32(&priv->regs->ep_sts);
+
+		if (ep_sts & DMARF_EP_TRBERR)
+			phytium_write32(&priv->regs->ep_sts, DMARF_EP_TRBERR);
+
+		if ((ep_sts & DMARF_EP_IOC) || (ep_sts & DMARF_EP_ISP) || (channel->dmultGuard
+					& DMARF_EP_IOC) || (channel->dmultGuard & DMARF_EP_ISP)) {
+retransmit:
+			phytium_write32(&priv->regs->ep_sts, DMARF_EP_IOC | DMARF_EP_ISP);
+			trbChainDesc = GetTrbChainDescEntry(channel->trbChainDescList.next);
+			if (!(ep_sts & DMARF_EP_TRBERR) && channel->dmultEnabled
+					&& !trbChainDesc->lastTrbIsLink) {
+				if (!priv->isHostCtrlMode && !channel->isDirTx) {
+					channel->dmultGuard = 0;
+					phytium_write32(&priv->regs->ep_sts, DMARF_EP_TRBERR);
+					ep_cfg = phytium_read32(&priv->regs->ep_cfg);
+					ep_cfg &= ~DMARV_EP_ENABLED;
+					phytium_write32(&priv->regs->ep_cfg, (uint32_t)ep_cfg);
+					updateDescBuffer(priv, trbChainDesc, 0);
+					break;
+				}
+				channel->dmultGuard = ep_sts;
+				break;
+			}
+
+			channel->dmultGuard = 0;
+			updateDescBuffer(priv, trbChainDesc, 0);
+		}
+
+		if (ep_sts & DMARF_EP_OUTSMM)
+			phytium_write32(&priv->regs->ep_sts, DMARF_EP_OUTSMM);
+
+		if (ep_sts & DMARF_EP_DESCMIS)
+			phytium_write32(&priv->regs->ep_sts, DMARF_EP_DESCMIS);
+
+		if (ep_sts & DMARF_EP_ISOERR) {
+			phytium_write32(&priv->regs->ep_sts, DMARF_EP_ISOERR);
+			trbChainDesc = GetTrbChainDescEntry(channel->trbChainDescList.next);
+			trbChainDesc->isoError = 1;
+			goto retransmit;
+		}
+	}
+}
+
+static void phytium_dma_errIsr(struct DMA_CONTROLLER *priv, uint8_t irqNr, uint8_t isDirTx)
+{
+	struct DMA_Channel *channel;
+
+	if (!priv)
+		return;
+
+	if (isDirTx)
+		channel = &priv->tx[irqNr];
+	else
+		channel = &priv->rx[irqNr];
+
+	if (channel->status >= DMA_STATUS_BUSY)
+		channel->status = DMA_STATUS_ABORT;
+
+	if (priv->dmaCallbacks.complete) {
+		if (!irqNr && priv->resubmit) {
+			priv->dmaCallbacks.complete(priv->parent, channel->hwUsbEppNum,
+					channel->isDirTx, true);
+			priv->resubmit = false;
+		} else
+			priv->dmaCallbacks.complete(priv->parent, channel->hwUsbEppNum,
+					channel->isDirTx, false);
+	}
+}
+
+static void *phytium_dma_channelAlloc(struct DMA_CONTROLLER *priv,
+		uint8_t isDirTx, uint8_t hwEpNum, uint8_t isIsoc)
+{
+	struct DMA_Channel *channel;
+	uint32_t ep_ien, ep_cfg;
+	uint16_t dmaMode;
+
+	if (!priv || (hwEpNum >= MAX_DMA_CHANNELS))
+		return NULL;
+
+	if (!priv->regs) {
+		pr_err("dma regs is null\n");
+		return NULL;
+	}
+
+	ep_ien = phytium_read32(&priv->regs->ep_ien);
+
+	if (isDirTx) {
+		if (priv->tx[hwEpNum].status > DMA_STATUS_FREE)
+			return NULL;
+
+		channel = &priv->tx[hwEpNum];
+		channel->isDirTx = 0x80;
+		ep_ien |= (0x01 << (hwEpNum + 16));
+		dmaMode = priv->dmaCfg.dmaModeTx;
+	} else {
+		if (priv->rx[hwEpNum].status > DMA_STATUS_FREE)
+			return NULL;
+
+		channel = &priv->rx[hwEpNum];
+		channel->isDirTx = 0x00;
+		ep_ien |= (0x01 << hwEpNum);
+		dmaMode = priv->dmaCfg.dmaModeRx;
+	}
+
+	channel->isIsoc = 0;
+	if (isIsoc)
+		channel->isIsoc = 1;
+
+	INIT_LIST_HEAD(&channel->trbChainDescList);
+	channel->numOfTrbChain = 0;
+	channel->controller = priv;
+	channel->dmultGuard = 0;
+	channel->status = DMA_STATUS_FREE;
+	channel->hwUsbEppNum = hwEpNum;
+
+	phytium_write32(&priv->regs->ep_sel, (uint32_t)hwEpNum | channel->isDirTx);
+	ep_cfg = phytium_read32(&priv->regs->ep_cfg);
+
+	if (priv->dmaMode == DMA_MODE_CHANNEL_INDIVIDUAL) {
+		if (dmaMode & (1 << hwEpNum))
+			ep_cfg |= DMARV_EP_DMULT;
+		else
+			ep_cfg |= DMARV_EP_DSING;
+	}
+
+	phytium_write32(&priv->regs->ep_sts, DMARF_EP_IOC | DMARF_EP_ISP | DMARF_EP_TRBERR);
+	phytium_write32(&priv->regs->ep_cfg, (uint32_t)DMARV_EP_ENABLED | ep_cfg);
+	phytium_write32(&priv->regs->ep_ien, ep_ien);
+	phytium_write32(&priv->regs->ep_sts_en, DMARF_EP_TRBERR);
+
+	return channel;
+}
+
+static int32_t phytium_dma_channelRelease(struct DMA_CONTROLLER *priv, struct DMA_Channel *channel)
+{
+	uint32_t ep_ien, i;
+	struct DMA_TrbChainDesc *trbChainDesc;
+
+	if (!channel || !priv)
+		return -EINVAL;
+
+	ep_ien = phytium_read32(&priv->regs->ep_ien);
+	if (channel->isDirTx)
+		ep_ien &= ~(0x01 << (channel->hwUsbEppNum + 16));
+	else
+		ep_ien &= ~(0x01 << channel->hwUsbEppNum);
+
+	phytium_write32(&priv->regs->ep_sel, (uint32_t)channel->hwUsbEppNum | channel->isDirTx);
+	phytium_write32(&priv->regs->ep_cfg, (uint32_t)0);
+	phytium_write32(&priv->regs->ep_ien, ep_ien);
+	phytium_write32(&priv->regs->ep_sts_en, 0x0);
+	phytium_write32(&priv->regs->ep_cmd, DMARF_EP_EPRST);
+	phytium_write32(&priv->regs->ep_sts, DMARF_EP_IOC | DMARF_EP_ISP |
+			DMARF_EP_TRBERR | DMARF_EP_ISOERR);
+
+	if (channel->status >= DMA_STATUS_BUSY)
+		channel->status = DMA_STATUS_ABORT;
+
+	priv->dmaDrv->dma_channelAbort(priv, channel);
+
+	while (channel->trbChainDescList.next != &channel->trbChainDescList) {
+		trbChainDesc = GetTrbChainDescEntry(channel->trbChainDescList.next);
+		if (trbChainDesc) {
+			if (!list_empty(&trbChainDesc->chainNode)) {
+				for (i = 0; i < trbChainDesc->mapSize; i++)
+					priv->trbChainFreeMap[(trbChainDesc->start >> 3) + i] = 0;
+
+				trbChainDesc->channel->numOfTrbChain--;
+				trbChainDesc->reserved = 0;
+				list_del(&trbChainDesc->chainNode);
+			}
+		}
+	}
+
+	channel->status = DMA_STATUS_UNKNOWN;
+
+	return 0;
+}
+
+static void ShowTrbChain(struct DMA_TrbChainDesc *trbChainDesc)
+{
+	int i;
+
+	if (!trbChainDesc)
+		return;
+	pr_debug("Trb Chain %p for channel %p\n", trbChainDesc, trbChainDesc->channel);
+	pr_debug("idx	| trb size	| trb addr	| FLAG: NORMAL  LINK  CHAIN  ALL\n");
+	for (i = 0; i < trbChainDesc->numOfTrbs + 2; i++)
+		pr_debug("%02d	| %08x	| %08x	|	%d	%d	%d	%08x\n",
+			i,
+			trbChainDesc->trbPool[i].dmaSize & TD_SIZE_MASK,
+			trbChainDesc->trbPool[i].dmaAddr,
+			(trbChainDesc->trbPool[i].ctrl & TD_TYPE_NORMAL) ? 1 : 0,
+			(trbChainDesc->trbPool[i].ctrl & TD_TYPE_LINK) ? 1 : 0,
+			(trbChainDesc->trbPool[i].ctrl & TDF_CHAIN_BIT) ? 1 : 0,
+			trbChainDesc->trbPool[i].ctrl);
+}
+
+static uint32_t phytium_dma_NewTd(struct DMA_CONTROLLER *priv,
+		struct DMA_TrbChainDesc *trbChainDesc)
+{
+	uint32_t startAddress, dataSize;
+	struct DMA_Channel *channel;
+	int i = 0;
+	uint32_t tmp = 0;
+
+	if (!priv || !trbChainDesc)
+		return 0;
+
+	startAddress = trbChainDesc->dwStartAddress;
+	channel = trbChainDesc->channel;
+	dataSize = channel->maxTrbLen;
+
+	if (trbChainDesc->numOfTrbs > 1) {
+		for (i = 0; i < (trbChainDesc->numOfTrbs - 1); i++) {
+			if (channel->dmultEnabled) {
+				if (trbChainDesc->framesDesc && priv->isHostCtrlMode
+						&& channel->isIsoc) {
+					BUILD_NORMAL_TRB_NO_IOC(trbChainDesc->trbPool[i],
+							trbChainDesc->dwStartAddress +
+							trbChainDesc->framesDesc[i].offset,
+							trbChainDesc->framesDesc[i].length, 0);
+					continue;
+				} else
+					BUILD_NORMAL_TRB_NO_IOC(trbChainDesc->trbPool[i],
+							startAddress, dataSize, 0);
+			} else
+				BUILD_NORMAL_TRB_NO_IOC_CHAIN(trbChainDesc->trbPool[i],
+						startAddress, dataSize, 0);
+			startAddress += dataSize;
+			tmp += dataSize;
+		}
+	}
+
+	if (trbChainDesc->framesDesc && priv->isHostCtrlMode && channel->isIsoc) {
+		BUILD_NORMAL_TRB(trbChainDesc->trbPool[i],
+				trbChainDesc->dwStartAddress + trbChainDesc->framesDesc[i].offset,
+				trbChainDesc->framesDesc[i].length, 0);
+	} else
+		BUILD_NORMAL_TRB(trbChainDesc->trbPool[i], startAddress,
+				trbChainDesc->len - tmp, 0);
+
+	trbChainDesc->lastTrbIsLink = 0;
+	if (channel->dmultEnabled) {
+		if (channel->isIsoc) {
+			trbChainDesc->lastTrbIsLink = 1;
+			BUILD_LINK_TRB(trbChainDesc->trbPool[i + 1], trbChainDesc->trbDMAAddr);
+		} else
+			BUILD_EMPTY_TRB(trbChainDesc->trbPool[i + 1]);
+	}
+
+	ShowTrbChain(trbChainDesc);
+
+	return 0;
+}
+
+static void phytium_dma_ArmTd(struct DMA_CONTROLLER *priv, struct DMA_TrbChainDesc *trbChainDesc)
+{
+	uint32_t ep_sts, ep_cfg, ep_cmd;
+	struct DMA_TrbChainDesc *startedChain;
+	struct DMA_Trb *lastPrevTrb;
+	struct DMA_Channel *channel;
+
+	if (!priv || !trbChainDesc)
+		return;
+
+	channel = trbChainDesc->channel;
+	phytium_write32(&priv->regs->ep_sel, (channel->isDirTx | channel->hwUsbEppNum));
+
+	ep_sts = phytium_read32(&priv->regs->ep_sts);
+
+	if (channel->status == DMA_STATUS_FREE) {
+		phytium_write32(&priv->regs->ep_sts, DMARF_EP_TRBERR);
+		phytium_write32(&priv->regs->traddr, trbChainDesc->trbDMAAddr);
+		phytium_write32(&priv->regs->ep_sts, DMARF_EP_TRBERR);
+
+		ep_cfg = phytium_read32(&priv->regs->ep_cfg);
+		phytium_write32(&priv->regs->ep_cmd, DMARF_EP_DRDY);
+		if (!(ep_cfg & DMARV_EP_ENABLED)) {
+			ep_cfg |= DMARV_EP_ENABLED;
+			phytium_write32(&priv->regs->ep_cfg, ep_cfg);
+		}
+	} else {
+		if (channel->trbChainDescList.prev != channel->trbChainDescList.next) {
+			startedChain = GetTrbChainDescEntry(channel->trbChainDescList.prev->prev);
+			lastPrevTrb = &startedChain->trbPool[startedChain->numOfTrbs];
+			startedChain->lastTrbIsLink = 1;
+			BUILD_LINK_TRB((*lastPrevTrb), trbChainDesc->trbDMAAddr);
+			ep_cmd = phytium_read32(&priv->regs->ep_cmd);
+			if (!(ep_cmd & DMARF_EP_DRDY)) {
+				phytium_write32(&priv->regs->traddr, trbChainDesc->trbDMAAddr);
+				phytium_write32(&priv->regs->ep_cmd, DMARF_EP_DRDY);
+			}
+			ShowTrbChain(startedChain);
+		}
+	}
+}
+
+static int32_t phytium_dma_TrbChainAlloc(struct DMA_CONTROLLER *priv,
+		struct DMA_Channel *channel, uint32_t numOfTrbs, struct DMA_TrbChainDesc **chain)
+{
+	struct DMA_TrbChainDesc *trbChainDesc = NULL;
+	int i, j;
+	uint32_t mapcount, count = 0;
+
+	if (!priv || !channel)
+		return -ENOMEM;
+
+	for (i = 0; i < TAB_SIZE_OF_DMA_CHAIN; i++) {
+		if (!priv->trbChainDesc[i].reserved) {
+			trbChainDesc = &priv->trbChainDesc[i];
+			break;
+		}
+	}
+
+	if (!trbChainDesc) {
+		pr_err("No Free TRB Chain Descriptor\n");
+		return -ENOMEM;
+	}
+
+	INIT_LIST_HEAD(&trbChainDesc->chainNode);
+	*chain = NULL;
+	mapcount = (numOfTrbs + 2 + 7) >> 3;
+
+	for (i = 0; i < TRB_MAP_SIZE; i++) {
+		if (priv->trbChainFreeMap[i] == 0x0)
+			count++;
+		else
+			count = 0;
+
+		if (count == mapcount)
+			break;
+	}
+
+	if (count != mapcount) {
+		pr_err("No Free TRBs count:0x%x, mapcount:0x%x\n", count, mapcount);
+		return -ENOMEM;
+	}
+
+	trbChainDesc->reserved = 1;
+	trbChainDesc->channel = channel;
+	trbChainDesc->actualLen = 0;
+	trbChainDesc->mapSize = mapcount;
+	trbChainDesc->numOfTrbs = numOfTrbs;
+	trbChainDesc->start = (i + 1 - mapcount) << 3;
+	trbChainDesc->end = trbChainDesc->start;
+	trbChainDesc->trbPool = (struct DMA_Trb *)priv->trbPoolAddr + trbChainDesc->start;
+	trbChainDesc->trbDMAAddr = (uint32_t)(uintptr_t)((struct DMA_Trb *)priv->trbDMAPoolAddr
+			+ trbChainDesc->start);
+	trbChainDesc->isoError = 0;
+
+	list_add_tail(&trbChainDesc->chainNode, &channel->trbChainDescList);
+
+	channel->numOfTrbChain++;
+
+	for (j = 0; j < count; j++)
+		priv->trbChainFreeMap[i - j] = 0xFF;
+
+	*chain = trbChainDesc;
+
+	return 0;
+}
+
+static int32_t phytium_dma_channelProgram(struct DMA_CONTROLLER *priv,
+		struct DMA_Channel *channel, uint16_t packetSize, uintptr_t dmaAddr,
+		uint32_t len, void *framesDesc, uint32_t framesNumber)
+{
+	uint32_t numOfTrbs;
+	uint8_t retval;
+	struct DMA_TrbChainDesc *trbChainDesc;
+
+	if (!priv || !channel)
+		return -EINVAL;
+
+	//printk(KERN_ERR "%s %d\n",__func__,__LINE__);
+	if (framesDesc && priv->isHostCtrlMode && channel->isIsoc)
+		numOfTrbs = framesNumber;
+	else
+		numOfTrbs = divRoundUp(len, channel->maxTrbLen);
+
+	retval = phytium_dma_TrbChainAlloc(priv, channel, numOfTrbs, &trbChainDesc);
+	if (retval)
+		return retval;
+
+	trbChainDesc->dwStartAddress = dmaAddr;
+	trbChainDesc->len = len;
+	trbChainDesc->framesDesc = framesDesc;
+
+	channel->wMaxPacketSize = packetSize;
+	phytium_dma_NewTd(priv, trbChainDesc);
+	channel->lastTransferLength = 0;
+	phytium_dma_ArmTd(priv, trbChainDesc);
+
+	channel->status = DMA_STATUS_BUSY;
+
+	return 0;
+}
+
+static enum DMA_Status phytium_dma_getChannelStatus(struct DMA_CONTROLLER *priv,
+		struct DMA_Channel *channel)
+{
+	uint32_t ep_cmd, ep_sts;
+
+	if (!priv || !channel)
+		return DMA_STATUS_UNKNOWN;
+
+	if (channel->status >= DMA_STATUS_BUSY) {
+		phytium_write32(&priv->regs->ep_sel, channel->isDirTx | channel->hwUsbEppNum);
+		ep_cmd = phytium_read32(&priv->regs->ep_cmd);
+		ep_sts = phytium_read32(&priv->regs->ep_sts);
+
+		if ((ep_cmd & DMARF_EP_DRDY) || (ep_sts & DMARF_EP_DBUSY))
+			channel->status = DMA_STATUS_ARMED;
+		else
+			channel->status = DMA_STATUS_BUSY;
+	}
+
+	return channel->status;
+}
+
+static int32_t phytium_dma_channelAbort(struct DMA_CONTROLLER *priv, struct DMA_Channel *channel)
+{
+	struct DMA_TrbChainDesc *trbChainDesc;
+	uint32_t ep_cfg, i;
+
+	if (!priv || !channel)
+		return -EINVAL;
+
+	if (phytium_dma_getChannelStatus(priv, channel) >= DMA_STATUS_BUSY)
+		phytium_write32(&priv->regs->conf, DMARF_RESET);
+
+	phytium_write32(&priv->regs->ep_sel, (uint32_t)(channel->isDirTx | channel->hwUsbEppNum));
+	ep_cfg = phytium_read32(&priv->regs->ep_cfg);
+	ep_cfg &= ~DMARV_EP_ENABLED;
+	phytium_write32(&priv->regs->ep_cfg, ep_cfg);
+	phytium_write32(&priv->regs->ep_sts, 0xFFFFFFFF);
+
+	while (channel->trbChainDescList.next != &channel->trbChainDescList) {
+		trbChainDesc = GetTrbChainDescEntry(channel->trbChainDescList.next);
+		if (trbChainDesc) {
+			if (!list_empty(&trbChainDesc->chainNode)) {
+				for (i = 0; i < trbChainDesc->mapSize; i++)
+					priv->trbChainFreeMap[(trbChainDesc->start >> 3) + i] = 0;
+
+				trbChainDesc->channel->numOfTrbChain--;
+				trbChainDesc->reserved = 0;
+				list_del(&trbChainDesc->chainNode);
+			}
+		}
+	}
+	if (channel->status != DMA_STATUS_UNKNOWN)
+		channel->status = DMA_STATUS_FREE;
+
+	return 0;
+}
+
+static int32_t phytium_dma_getActualLength(struct DMA_CONTROLLER *priv, struct DMA_Channel *channel)
+{
+	if (!priv || !channel)
+		return -EINVAL;
+
+	return channel->lastTransferLength;
+}
+
+static int32_t phytium_dma_getMaxLength(struct DMA_CONTROLLER *priv, struct DMA_Channel *channel)
+{
+	if (!priv || !channel)
+		return -EINVAL;
+
+	return channel->maxTdLen;
+}
+
+static int32_t phytium_dma_setMaxLength(struct DMA_CONTROLLER *priv,
+		struct DMA_Channel *channel, uint32_t val)
+{
+	if (!priv || !channel)
+		return -EINVAL;
+
+	if (channel->dmultEnabled)
+		channel->maxTrbLen = val;
+	else
+		channel->maxTrbLen = (val > TD_SING_MAX_TRB_DATA_SIZE) ?
+			TD_SING_MAX_TRB_DATA_SIZE : val;
+
+	return 0;
+}
+
+static void phytium_dma_setParentPriv(struct DMA_CONTROLLER *priv, void *parent)
+{
+	if (!priv)
+		return;
+
+	priv->parent = parent;
+}
+
+void phytium_dma_controllerReset(struct DMA_CONTROLLER *priv)
+{
+	uint32_t conf;
+
+	if (!priv)
+		return;
+
+	conf = phytium_read32(&priv->regs->conf);
+	conf |= DMARF_RESET;
+	phytium_write32(&priv->regs->conf, conf);
+
+	priv->resubmit = true;
+}
+
+void phytium_dma_setHostMode(struct DMA_CONTROLLER *priv)
+{
+	if (!priv)
+		return;
+
+	priv->isHostCtrlMode = 1;
+}
+
+struct DMA_OBJ phytium_dma_Drv = {
+	.dma_probe = phytium_dma_probe,
+	.dma_init = phytium_dma_init,
+	.dma_destroy = phytium_dma_destroy,
+	.dma_start = phytium_dma_start,
+	.dma_stop = phytium_dma_stop,
+	.dma_isr = phytium_dma_isr,
+	.dma_errIsr = phytium_dma_errIsr,
+	.dma_channelAlloc = phytium_dma_channelAlloc,
+	.dma_channelRelease = phytium_dma_channelRelease,
+	.dma_channelProgram = phytium_dma_channelProgram,
+	.dma_channelAbort = phytium_dma_channelAbort,
+	.dma_getChannelStatus = phytium_dma_getChannelStatus,
+	.dma_getActualLength = phytium_dma_getActualLength,
+	.dma_getMaxLength = phytium_dma_getMaxLength,
+	.dma_setMaxLength = phytium_dma_setMaxLength,
+	.dma_setParentPriv = phytium_dma_setParentPriv,
+	.dma_controllerReset = phytium_dma_controllerReset,
+	.dma_setHostMode = phytium_dma_setHostMode,
+};
+
+struct DMA_OBJ *DMA_GetInstance(void)
+{
+	return &phytium_dma_Drv;
+}
diff --git a/drivers/usb/phytium/dma.h b/drivers/usb/phytium/dma.h
new file mode 100644
index 0000000000000..218c99d3d0155
--- /dev/null
+++ b/drivers/usb/phytium/dma.h
@@ -0,0 +1,197 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+
+#ifndef __PHYTIUM_DMA_H__
+#define __PHYTIUM_DMA_H__
+
+#include <linux/types.h>
+
+#define NUM_OF_TRB 1024
+#define TRB_MAP_SIZE ((NUM_OF_TRB + (sizeof(uint8_t) * 8) - 1) / (sizeof(uint8_t) * 8))
+#define MAX_DMA_CHANNELS 16
+#define TAB_SIZE_OF_DMA_CHAIN (MAX_DMA_CHANNELS * 2)
+
+#define DMARD_EP_TX 0x80ul
+#define DMARD_EP_RX 0x00ul
+
+#define DMARF_EP_EPRST	0x00000001ul
+#define DMARF_EP_DRDY	0x00000040ul
+#define DMARF_EP_DFLUSH	0x00000080ul
+
+#define DMARF_EP_IOC		0x4ul
+#define DMARF_EP_ISP		0x8ul
+#define DMARF_EP_DESCMIS	0x10ul
+#define DMARF_EP_TRBERR		0x80ul
+#define DMARF_EP_DBUSY		0x200ul
+#define DMARF_EP_CCS		0x800ul
+#define DMARF_EP_OUTSMM		0x4000ul
+#define DMARF_EP_ISOERR		0x8000ul
+#define DMARF_EP_DTRANS		0x80000000ul
+
+#define DMARV_EP_DISABLED	0ul
+#define DMARV_EP_ENABLED	1ul
+#define DMARV_EP_DSING		0x1000ul
+#define DMARV_EP_DMULT		0x2000ul
+
+#define TD_SIZE_MASK		0x00001FFFF
+
+#define DMARF_RESET		0x00000001ul
+#define DMARF_DSING		0x00000100ul
+#define DMARF_DMULT		0x00000200ul
+
+#define TD_DMULT_MAX_TRB_DATA_SIZE	65536u
+#define TD_DMULT_MAX_TD_DATA_SIZE	(~1u)
+#define TD_SING_MAX_TRB_DATA_SIZE	65536u
+#define TD_SING_MAX_TD_DATA_SIZE	65536u
+
+#define TD_TYPE_NORMAL	0x400L
+#define TD_TYPE_LINK	0x1800L
+#define TDF_CYCLE_BIT	0x1L
+#define TDF_TOGGLE_CYCLE_BIT	0x2L
+#define TDF_INT_ON_SHORT_PACKET	0x4L
+#define TDF_FIFO_MODE		0x8L
+#define TDF_CHAIN_BIT		0x10L
+#define TDF_INT_ON_COMPLECTION	0x20L
+#define TDF_STREAMID_VALID	0x200L
+
+struct DMA_Trb {
+	uint32_t dmaAddr;
+	uint32_t dmaSize;
+	uint32_t ctrl;
+};
+
+enum DMA_Status {
+	DMA_STATUS_UNKNOWN,
+	DMA_STATUS_FREE,
+	DMA_STATUS_ABORT,
+	DMA_STATUS_BUSY,
+	DMA_STATUS_ARMED
+};
+
+struct DMA_CFG {
+	uintptr_t regBase;
+	uint16_t dmaModeTx;
+	uint16_t dmaModeRx;
+	void *trbAddr;
+	uintptr_t trbDmaAddr;
+};
+
+struct DMA_SYSREQ {
+	uint32_t privDataSize;
+	uint32_t trbMemSize;
+};
+
+struct DMA_CALLBACKS {
+	void (*complete)(void *pD, uint8_t epNum, uint8_t dir, bool resubmit);
+};
+
+struct DMA_CONTROLLER;
+
+struct DMA_Channel {
+	struct DMA_CONTROLLER *controller;
+	uint16_t wMaxPacketSize;
+	uint8_t hwUsbEppNum;
+	uint8_t isDirTx;
+	uint32_t maxTdLen;
+	uint32_t maxTrbLen;
+	enum DMA_Status status;
+	void *priv;
+	uint32_t dmultGuard;
+	uint8_t dmultEnabled;
+	uint8_t numOfTrbChain;
+	struct list_head trbChainDescList;
+	uint32_t lastTransferLength;
+	uint8_t isIsoc;
+};
+
+struct DMA_OBJ {
+	int32_t (*dma_probe)(struct DMA_CFG *config, struct DMA_SYSREQ *sysReq);
+
+	int32_t (*dma_init)(struct DMA_CONTROLLER *priv, const struct DMA_CFG *config,
+			struct DMA_CALLBACKS *callbacks);
+
+	void (*dma_destroy)(struct DMA_CONTROLLER *priv);
+
+	int32_t (*dma_start)(struct DMA_CONTROLLER *priv);
+
+	uint32_t (*dma_stop)(struct DMA_CONTROLLER *priv);
+
+	void (*dma_isr)(struct DMA_CONTROLLER *priv);
+
+	void (*dma_errIsr)(struct DMA_CONTROLLER *priv, uint8_t irqNr, uint8_t isDirTx);
+
+	void * (*dma_channelAlloc)(struct DMA_CONTROLLER *priv,
+			uint8_t isDirTx, uint8_t hwEpNum, uint8_t isIso);
+
+	int32_t (*dma_channelRelease)(struct DMA_CONTROLLER *priv, struct DMA_Channel *channel);
+
+	int32_t (*dma_channelProgram)(struct DMA_CONTROLLER *priv, struct DMA_Channel *channel,
+			uint16_t packetSize, uintptr_t dmaAddr,
+			uint32_t len, void *framesDesc, uint32_t framesNumber);
+
+	int32_t (*dma_channelAbort)(struct DMA_CONTROLLER *priv, struct DMA_Channel *channel);
+
+	enum DMA_Status (*dma_getChannelStatus)(struct DMA_CONTROLLER *priv,
+			struct DMA_Channel *channel);
+
+	int32_t (*dma_getActualLength)(struct DMA_CONTROLLER *priv, struct DMA_Channel *channel);
+
+	int32_t (*dma_getMaxLength)(struct DMA_CONTROLLER *priv, struct DMA_Channel *channel);
+
+	int32_t (*dma_setMaxLength)(struct DMA_CONTROLLER *priv,
+			struct DMA_Channel *channel, uint32_t val);
+
+	void (*dma_setParentPriv)(struct DMA_CONTROLLER *priv, void *parent);
+
+	void (*dma_controllerReset)(struct DMA_CONTROLLER *priv);
+
+	void (*dma_setHostMode)(struct DMA_CONTROLLER *priv);
+};
+
+enum DMA_Mode {
+	DMA_MODE_GLOBAL_DMULT,
+	DMA_MODE_GLOBAL_DSING,
+	DMA_MODE_CHANNEL_INDIVIDUAL,
+};
+
+struct DMA_TrbFrameDesc {
+	uint32_t length;
+	uint32_t offset;
+};
+
+struct DMA_TrbChainDesc {
+	uint8_t reserved;
+	struct DMA_Channel *channel;
+	struct DMA_Trb	*trbPool;
+	uint32_t trbDMAAddr;
+	uint32_t len;
+	uint32_t dwStartAddress;
+	uint32_t actualLen;
+	uint8_t isoError;
+	uint8_t lastTrbIsLink;
+	uint32_t mapSize;
+	uint32_t numOfTrbs;
+	uint32_t start;
+	uint32_t end;
+	struct DMA_TrbFrameDesc *framesDesc;
+	struct list_head chainNode;
+};
+
+struct DMA_CONTROLLER {
+	struct DMARegs *regs;
+	struct DMA_OBJ *dmaDrv;
+	struct DMA_CFG dmaCfg;
+	struct DMA_CALLBACKS dmaCallbacks;
+	struct DMA_Channel rx[MAX_DMA_CHANNELS];
+	struct DMA_Channel tx[MAX_DMA_CHANNELS];
+	enum DMA_Mode dmaMode;
+	uint8_t isHostCtrlMode;
+	void *parent;
+	void *trbPoolAddr;
+	uintptr_t trbDMAPoolAddr;
+	uint8_t trbChainFreeMap[TRB_MAP_SIZE];
+	struct DMA_TrbChainDesc trbChainDesc[TAB_SIZE_OF_DMA_CHAIN];
+	bool resubmit;
+};
+
+struct DMA_OBJ *DMA_GetInstance(void);
+#endif
diff --git a/drivers/usb/phytium/gadget.c b/drivers/usb/phytium/gadget.c
new file mode 100644
index 0000000000000..e5e1625d785f8
--- /dev/null
+++ b/drivers/usb/phytium/gadget.c
@@ -0,0 +1,2538 @@
+// SPDX-License-Identifier: GPL-2.0
+
+#include <linux/dma-mapping.h>
+#include "gadget.h"
+#include "dma.h"
+#include "core.h"
+#include "hw-regs.h"
+
+#define DRV_NAME "phytium_gadget"
+
+#define GADGET_PRIV_BUFFER_SIZE 64
+#define GADGET_USB_EP_NUMBER_MASK 0xf
+#define DMA_ADDR_INVALID	(~(dma_addr_t)0)
+
+#define GADGET_ESTALL		1
+#define GADGET_EUNHANDLED	2
+#define GADGET_EAUTOACK		3
+#define GADGET_ESHUTDOWN	4
+#define GADGET_ECONNRESET	5
+#define GADGET_EAGAIN		6
+
+static inline struct GadgetEp *toGadgetEp(struct GADGET_EP *gadget_Ep)
+{
+	return (struct GadgetEp *)((uintptr_t)gadget_Ep -
+					((uintptr_t)&((struct GadgetEp *)0)->gadgetEp));
+}
+
+static inline struct GadgetRequest *requestToGadgetRequest(struct GADGET_REQ *req)
+{
+	return (struct GadgetRequest *)((uintptr_t)req -
+					((uintptr_t)&((struct GadgetRequest *)0)->request));
+}
+
+static inline struct GADGET_REQ *listToGadgetRequest(struct list_head *list)
+{
+	return (struct GADGET_REQ *)((uintptr_t)list -
+					((uintptr_t)&((struct GADGET_REQ *)0)->list));
+}
+
+#define listBrowsingRequest(iterator, head, memeber)	\
+	for (iterator = listToGadgetRequest((head)->next); \
+		&iterator->list != (head);	\
+		iterator = listToGadgetRequest(iterator->list.next))
+
+static inline struct GADGET_REQ *gadgetGetNextReq(struct GadgetEp *gadgetEp)
+{
+	struct list_head *list = &gadgetEp->request;
+
+	if (list_empty(list)) {
+		pr_debug("no request available for %s\n", gadgetEp->gadgetEp.name);
+		return NULL;
+	}
+
+	return listToGadgetRequest(list->next);
+}
+
+static inline struct GADGET_REQ *gadgetGetNextEp0Req(struct GADGET_CTRL *priv)
+{
+	struct list_head *queue;
+
+	if (!priv)
+		return NULL;
+
+	queue = &priv->in[0].request;
+
+	if (list_empty(queue))
+		return NULL;
+
+	return listToGadgetRequest(queue->next);
+}
+
+void gadget_giveback(struct phytium_ep *phy_ep, struct usb_request *usb_req, int status)
+{
+	struct phytium_request	*phy_req;
+	struct phytium_cusb	*config;
+	int busy;
+
+	if (!phy_ep || !usb_req)
+		return;
+
+	busy = phy_ep->busy;
+	phy_req = usb_req ? container_of(usb_req, struct phytium_request, request) : NULL;
+	config = phy_req->config;
+
+	list_del(&phy_req->list);
+
+	if (usb_req->status == -EINPROGRESS) {
+		if (status == GADGET_ESHUTDOWN)
+			usb_req->status = -ESHUTDOWN;
+		else if (status == GADGET_ECONNRESET)
+			usb_req->status = -ECONNRESET;
+		else
+			usb_req->status = -phy_req->gadget_request->status;
+	}
+
+	if (usb_req->status == 0)
+		pr_debug("%s done request %p, %d/%d\n", phy_ep->end_point.name,
+				usb_req, usb_req->actual, usb_req->length);
+	else
+		pr_debug("%s request %p, %d/%d fault %d\n", phy_ep->end_point.name,
+				usb_req, usb_req->actual, usb_req->length, usb_req->status);
+
+	usb_gadget_unmap_request(&config->gadget, &phy_req->request, phy_req->is_tx);
+
+	busy = phy_ep->busy;
+	phy_ep->busy = 1;
+
+	spin_unlock(&config->lock);
+
+	if (phy_req->request.complete)
+		phy_req->request.complete(&phy_req->ep->end_point, usb_req);
+
+	spin_lock(&config->lock);
+
+	phy_ep->busy = busy;
+}
+
+static void gadget_callback_complete(struct GADGET_EP *gadget_ep, struct GADGET_REQ *gadget_req)
+{
+	struct phytium_ep	*phy_ep;
+	struct phytium_request	*phy_req;
+	struct usb_request	*usb_req;
+
+	if (!gadget_ep || !gadget_req)
+		return;
+
+	phy_req = gadget_req->context;
+	usb_req = &phy_req->request;
+	phy_ep = phy_req->ep;
+	usb_req->actual = gadget_req->actual;
+	usb_req->length = gadget_req->length;
+
+	gadget_giveback(phy_ep, usb_req, gadget_req->status);
+}
+
+static void gadgetDisconnect(struct GADGET_CTRL *priv)
+{
+	pr_info("Disconnect USB Device Driver\n");
+
+	if (!priv)
+		return;
+
+	priv->gadgetDev.speed = USB_SPEED_UNKNOWN;
+	priv->gadgetDev.state = USB_STATE_NOTATTACHED;
+
+	if (priv->eventCallback.disconnect)
+		priv->eventCallback.disconnect(priv);
+}
+
+static int gadget_get_frame(struct usb_gadget *gadget)
+{
+	pr_info("%s %d\n", __func__, __LINE__);
+
+	return 0;
+}
+
+static int gadget_wakeup(struct usb_gadget *gadget)
+{
+	pr_info("%s %d\n", __func__, __LINE__);
+
+	return 0;
+}
+
+static int gadget_vbus_session(struct usb_gadget *gadget, int is_active)
+{
+	pr_info("%s %d\n", __func__, __LINE__);
+
+	return 0;
+}
+
+static int gadget_vbus_draw(struct usb_gadget *gadget, unsigned int mA)
+{
+	pr_info("%s %d\n", __func__, __LINE__);
+
+	return 0;
+}
+
+static int gadget_pullup(struct usb_gadget *gadget, int is_on)
+{
+	pr_info("%s %d\n", __func__, __LINE__);
+
+	return 0;
+}
+
+static int gadget_udc_start(struct usb_gadget *gadget, struct usb_gadget_driver *driver)
+{
+	unsigned long flags;
+	struct phytium_cusb *config;
+	struct GADGET_CTRL *priv;
+	uint32_t gen_cfg;
+
+	if (!gadget || !driver)
+		return -EINVAL;
+
+	if (driver->max_speed < USB_SPEED_HIGH)
+		return -EINVAL;
+
+	config = container_of(gadget, struct phytium_cusb, gadget);
+
+	pr_info("registering driver %s\n", driver->function);
+
+	spin_lock_irqsave(&config->lock, flags);
+	config->gadget_driver = driver;
+	spin_unlock_irqrestore(&config->lock, flags);
+
+	config->gadget_obj->gadget_start(config->gadget_priv);
+
+	priv = (struct GADGET_CTRL *)config->gadget_priv;
+	if (priv->phy_regs) {
+		gen_cfg = phytium_read32(&priv->phy_regs->gen_cfg);
+		gen_cfg = gen_cfg & (~BIT(7));
+		phytium_write32(&priv->phy_regs->gen_cfg, gen_cfg);
+	}
+
+	return 0;
+}
+
+static int gadget_udc_stop(struct usb_gadget *gadget)
+{
+	struct phytium_cusb *config;
+	unsigned long flags;
+	struct GADGET_CTRL *priv;
+	uint32_t gen_cfg;
+
+	if (!gadget)
+		return -EINVAL;
+
+	config = container_of(gadget, struct phytium_cusb, gadget);
+
+	priv = (struct GADGET_CTRL *)config->gadget_priv;
+	if (priv->phy_regs) {
+		gen_cfg = phytium_read32(&priv->phy_regs->gen_cfg);
+		gen_cfg = gen_cfg | BIT(7);
+		phytium_write32(&priv->phy_regs->gen_cfg, gen_cfg);
+	}
+	spin_lock_irqsave(&config->lock, flags);
+	config->gadget_driver = NULL;
+	spin_unlock_irqrestore(&config->lock, flags);
+
+	return 0;
+}
+
+static struct usb_gadget_ops phytium_gadget_ops = {
+	.get_frame	=	gadget_get_frame,
+	.wakeup		=	gadget_wakeup,
+	.vbus_session	=	gadget_vbus_session,
+	.vbus_draw	=	gadget_vbus_draw,
+	.pullup		=	gadget_pullup,
+	.udc_start	=	gadget_udc_start,
+	.udc_stop	=	gadget_udc_stop,
+};
+
+static int gadget_ep_enable(struct usb_ep *ls_ep, const struct usb_endpoint_descriptor *desc)
+{
+	struct phytium_ep *phy_ep = NULL;
+	struct phytium_cusb *config;
+	unsigned long flags;
+
+	if (!ls_ep || !desc)
+		return -EINVAL;
+
+	phy_ep = ls_ep ? container_of(ls_ep, struct phytium_ep, end_point) : NULL;
+	config = phy_ep->config;
+
+	if (phy_ep->desc)
+		return -EBUSY;
+
+	spin_lock_irqsave(&config->lock, flags);
+
+	phy_ep->desc = desc;
+	phy_ep->busy = 0;
+	config->gadget_obj->gadget_epEnable(config->gadget_priv, phy_ep->gadget_ep, desc);
+
+	spin_unlock_irqrestore(&config->lock, flags);
+
+	return 0;
+}
+
+static int gadget_ep_disable(struct usb_ep *ls_ep)
+{
+	struct phytium_ep *phy_ep = NULL;
+	struct phytium_cusb *config;
+	unsigned long flags;
+
+	if (!ls_ep)
+		return -EBUSY;
+
+	pr_info("%s %d\n", __func__, __LINE__);
+
+	phy_ep = ls_ep ? container_of(ls_ep, struct phytium_ep, end_point) : NULL;
+	config = phy_ep->config;
+
+	spin_lock_irqsave(&config->lock, flags);
+
+	phy_ep->desc = NULL;
+	phy_ep->busy = 0;
+	phy_ep->end_point.desc = NULL;
+	config->gadget_obj->gadget_epDisable(config->gadget_priv, phy_ep->gadget_ep);
+
+	spin_unlock_irqrestore(&config->lock, flags);
+
+	return 0;
+}
+
+static struct usb_request *gadget_ep_alloc_request(struct usb_ep *ls_ep, gfp_t gfp_flags)
+{
+	struct phytium_ep *phy_ep;
+	struct phytium_cusb *config;
+	struct GADGET_EP *gadget_ep;
+	struct phytium_request *phy_request;
+
+	if (!ls_ep)
+		return NULL;
+
+	pr_info("%s %d\n", __func__, __LINE__);
+	phy_request = kzalloc(sizeof(*phy_request), gfp_flags);
+	if (!phy_request) {
+		pr_err("not enough momory\n");
+		return NULL;
+	}
+
+	phy_ep = ls_ep ? container_of(ls_ep, struct phytium_ep, end_point) : NULL;
+	config = phy_ep->config;
+	gadget_ep = phy_ep->gadget_ep;
+
+	INIT_LIST_HEAD(&phy_request->list);
+	phy_request->request.dma = DMA_ADDR_INVALID;
+	phy_request->epnum = phy_ep->ep_num;
+	phy_request->ep = phy_ep;
+	phy_request->config = phy_ep->config;
+
+	config->gadget_obj->gadget_reqAlloc(config->gadget_priv, gadget_ep,
+						&phy_request->gadget_request);
+
+	return &phy_request->request;
+}
+
+static void gadget_ep_free_request(struct usb_ep *ls_ep, struct usb_request *ls_req)
+{
+	struct phytium_ep *phy_ep;
+	struct phytium_cusb *config;
+	struct phytium_request *phy_request;
+
+	if (!ls_ep || !ls_req)
+		return;
+
+	pr_info("%s %d\n", __func__, __LINE__);
+	phy_request = ls_req ? container_of(ls_req, struct phytium_request, request) : NULL;
+	config = phy_request->config;
+	phy_ep = ls_ep ? container_of(ls_ep, struct phytium_ep, end_point) : NULL;
+
+	config->gadget_obj->gadget_reqFree(config->gadget_priv, phy_ep->gadget_ep,
+						phy_request->gadget_request);
+	kfree(phy_request);
+}
+
+static int gadget_ep_enqueue(struct usb_ep *ls_ep, struct usb_request *ls_req, gfp_t gfp_flags)
+{
+	struct phytium_ep *phy_ep;
+	struct phytium_cusb *config;
+	struct phytium_request *phy_request;
+	unsigned long flags;
+	int status = 0;
+
+	if (!ls_ep || !ls_req)
+		return -EINVAL;
+
+	if (!ls_req->buf)
+		return -ENODATA;
+
+	phy_ep = ls_ep ? container_of(ls_ep, struct phytium_ep, end_point) : NULL;
+	config = phy_ep->config;
+	phy_request = ls_req ? container_of(ls_req, struct phytium_request, request) : NULL;
+	phy_request->config = config;
+
+	if (phy_request->ep != phy_ep)
+		return -EINVAL;
+
+	phy_request->request.actual = 0;
+	phy_request->request.status = -EINPROGRESS;
+	phy_request->epnum = phy_ep->ep_num;
+	phy_request->is_tx = phy_ep->is_tx;
+
+	phy_request->gadget_request->length = ls_req->length;
+	phy_request->gadget_request->status = 0;
+	phy_request->gadget_request->complete = gadget_callback_complete;
+	phy_request->gadget_request->buf = ls_req->buf;
+	phy_request->gadget_request->context = ls_req;
+
+	status = usb_gadget_map_request(&config->gadget, &phy_request->request, phy_request->is_tx);
+
+	if (!phy_ep->desc) {
+		pr_debug("req %p queued to %s while ep %s\n", phy_request, ls_ep->name, "disabled");
+		status = -ESHUTDOWN;
+		usb_gadget_unmap_request(&config->gadget, &phy_request->request,
+					phy_request->is_tx);
+		return status;
+	}
+
+	spin_lock_irqsave(&config->lock, flags);
+
+	phy_request->gadget_request->dma = phy_request->request.dma;
+	list_add_tail(&phy_request->list, &phy_ep->req_list);
+
+	pr_debug("queue to %s (%s), length = %d\n", phy_ep->name,
+			phy_ep->is_tx ? "IN/TX" : "OUT/RX", phy_request->request.length);
+
+	status = config->gadget_obj->gadget_reqQueue(config->gadget_priv, phy_ep->gadget_ep,
+							phy_request->gadget_request);
+
+	spin_unlock_irqrestore(&config->lock, flags);
+
+	return status;
+}
+
+static int gadget_ep_dequeue(struct usb_ep *ls_ep, struct usb_request *ls_req)
+{
+	struct phytium_ep *phy_ep;
+	struct phytium_cusb *config;
+	unsigned long flags;
+	int status = 0;
+	struct phytium_request *phy_request;
+	struct phytium_request *phy_next_request;
+
+	if (!ls_ep || !ls_req)
+		return -EINVAL;
+
+	phy_ep = ls_ep ? container_of(ls_ep, struct phytium_ep, end_point) : NULL;
+	config = phy_ep->config;
+	phy_request = ls_req ? container_of(ls_req, struct phytium_request, request) : NULL;
+
+	if (phy_request->ep != phy_ep)
+		return -EINVAL;
+
+	spin_lock_irqsave(&config->lock, flags);
+
+	list_for_each_entry(phy_next_request, &phy_ep->req_list, list) {
+		if (phy_next_request == phy_request)
+			break;
+	}
+
+	if (phy_next_request != phy_request) {
+		pr_info("request %p not queued to %s\n", phy_request, ls_ep->name);
+		status = -EINVAL;
+		goto done;
+	}
+
+	status = config->gadget_obj->gadget_reqDequeue(config->gadget_priv, phy_ep->gadget_ep,
+							phy_request->gadget_request);
+done:
+	spin_unlock_irqrestore(&config->lock, flags);
+	return status;
+}
+
+static int gadget_ep_set_halt(struct usb_ep *ls_ep, int value)
+{
+	struct phytium_ep *phy_ep;
+	struct phytium_cusb *config;
+	struct GADGET_EP *gadget_ep = NULL;
+	unsigned long flags;
+	int status = 0;
+
+	if (!ls_ep)
+		return -EINVAL;
+
+	phy_ep = ls_ep ? container_of(ls_ep, struct phytium_ep, end_point) : NULL;
+	config = phy_ep->config;
+	gadget_ep = phy_ep->gadget_ep;
+
+	spin_lock_irqsave(&config->lock, flags);
+
+	status = config->gadget_obj->gadget_epSetHalt(config->gadget_priv,
+							phy_ep->gadget_ep, value);
+	if (status > 0) {
+		spin_unlock_irqrestore(&config->lock, flags);
+		return -status;
+	}
+
+	spin_unlock_irqrestore(&config->lock, flags);
+
+	return 0;
+}
+
+static const struct usb_ep_ops  gadget_ep_ops = {
+	.enable		=	gadget_ep_enable,
+	.disable	=	gadget_ep_disable,
+	.alloc_request	=	gadget_ep_alloc_request,
+	.free_request	=	gadget_ep_free_request,
+	.queue		=	gadget_ep_enqueue,
+	.dequeue	=	gadget_ep_dequeue,
+	.set_halt	=	gadget_ep_set_halt,
+};
+
+static int gadget_ep0_enable(struct usb_ep *ep, const struct usb_endpoint_descriptor *desc)
+{
+	return -EINVAL;
+}
+
+static int gadget_ep0_disable(struct usb_ep *ep)
+{
+	return -EINVAL;
+}
+
+static int gadget_ep0_enqueue(struct usb_ep *ls_ep, struct usb_request *ls_req, gfp_t gfp_flags)
+{
+	struct phytium_ep *phy_ep;
+	struct phytium_request *phy_request;
+	struct phytium_cusb *config;
+	int status = 0;
+	unsigned long flags;
+
+	if (!ls_ep || !ls_req)
+		return -EINVAL;
+
+	phy_ep = ls_ep ? container_of(ls_ep, struct phytium_ep, end_point) : NULL;
+	config = phy_ep->config;
+	phy_request = ls_req ? container_of(ls_req, struct phytium_request, request) : NULL;
+
+	spin_lock_irqsave(&config->lock, flags);
+
+	if (!list_empty(&phy_ep->req_list)) {
+		status = -EBUSY;
+		goto cleanup;
+	}
+
+	phy_request->config = config;
+	phy_request->request.actual = 0;
+	phy_request->gadget_request->actual = 0;
+	phy_request->is_tx = config->ep0_data_stage_is_tx;
+	phy_request->gadget_request->length = phy_request->request.length;
+	phy_request->gadget_request->status = 0;
+	phy_request->gadget_request->complete = gadget_callback_complete;
+	phy_request->gadget_request->buf = phy_request->request.buf;
+	phy_request->gadget_request->context = phy_request;
+
+	status = usb_gadget_map_request(&config->gadget, &phy_request->request, phy_request->is_tx);
+	if (status) {
+		pr_info("failed to map request\n");
+		status = -EINVAL;
+		goto cleanup;
+	}
+
+	phy_request->gadget_request->dma = phy_request->request.dma;
+	list_add_tail(&phy_request->list, &phy_ep->req_list);
+
+	pr_debug("queue to %s (%s), length = %d\n", phy_ep->name,
+			phy_ep->is_tx ? "IN/TX" : "OUT/RX", phy_request->request.length);
+
+	status = config->gadget_obj->gadget_reqQueue(config->gadget_priv, phy_ep->gadget_ep,
+							phy_request->gadget_request);
+	if (status > 0) {
+		status = -status;
+		usb_gadget_unmap_request(&config->gadget, &phy_request->request,
+					phy_request->is_tx);
+		list_del(&phy_request->list);
+		goto cleanup;
+	}
+
+cleanup:
+	spin_unlock_irqrestore(&config->lock, flags);
+	return status;
+}
+
+static int gadget_ep0_dequeue(struct usb_ep *ep, struct usb_request *ls_request)
+{
+	return -EOPNOTSUPP;
+}
+
+static int gadget_ep0_set_halt(struct usb_ep *ep, int value)
+{
+	return -EINVAL;
+}
+
+
+static const struct usb_ep_ops  gadget_ep0_ops = {
+	.enable		=	gadget_ep0_enable,
+	.disable	=	gadget_ep0_disable,
+	.alloc_request	=	gadget_ep_alloc_request,
+	.free_request	=	gadget_ep_free_request,
+	.queue		=	gadget_ep0_enqueue,
+	.dequeue	=	gadget_ep0_dequeue,
+	.set_halt	=	gadget_ep0_set_halt,
+};
+
+static int32_t gadgetWaitForBusyBit(struct GADGET_CTRL *priv, struct GADGET_EP *gadget_Ep)
+{
+	struct GadgetEp *gadgetEp;
+	uint8_t epNum;
+	uint8_t txcs = CS_BUSY;
+	uint8_t buf = 0;
+	uint8_t bufflag = 0;
+
+	if (!priv || !gadget_Ep)
+		return -EINVAL;
+
+	gadgetEp = toGadgetEp(gadget_Ep);
+	epNum = gadgetEp->hwEpNum;
+
+	if (gadgetEp->isInEp || gadgetEp->hwEpNum == 0)
+		return 0;
+
+	buf = phytium_read8(&priv->regs->ep[epNum - 1].txcon) & CON_BUF;
+
+	while ((txcs & CS_BUSY) || (bufflag == 0)) {
+		txcs = phytium_read8(&priv->regs->ep[epNum - 1].txcs);
+
+		if (((txcs & CS_NPAK) >> CS_NPAK_OFFSET) == buf || buf == 0)
+			bufflag = 1;
+		else
+			bufflag = 0;
+	}
+
+	return 0;
+}
+
+static inline void gadgetEpXDataReceive(struct GADGET_CTRL *priv,
+					struct GadgetRequest *gadgetRequest)
+{
+	struct GadgetEp *gadgetEp;
+	struct GADGET_REQ *gadgetReq;
+	uint8_t epType;
+	uint32_t requestSize, channelStatus, chMaxLen;
+
+	if (!priv || !gadgetRequest)
+		return;
+
+	gadgetEp = gadgetRequest->ep;
+	chMaxLen = priv->dmaDrv->dma_getMaxLength(priv->dmaController, gadgetEp->channel);
+	epType = gadgetEp->gadgetEp.desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK;
+	channelStatus = priv->dmaDrv->dma_getChannelStatus(priv->dmaController, gadgetEp->channel);
+
+	gadgetReq = &gadgetRequest->request;
+	if (gadgetReq->actual < gadgetReq->length || gadgetRequest->zlp) {
+		gadgetRequest->zlp = 0;
+		if ((gadgetReq->length - gadgetReq->actual) < chMaxLen)
+			requestSize = gadgetReq->length - gadgetReq->actual;
+		else
+			requestSize = chMaxLen;
+
+		priv->dmaDrv->dma_channelProgram(priv->dmaController, gadgetEp->channel,
+				gadgetEp->gadgetEp.maxPacket,
+				gadgetReq->dma + gadgetReq->actual, requestSize, NULL, 0);
+	}
+}
+
+static inline void gadgetEpXDataSend(struct GADGET_CTRL *priv, struct GadgetRequest *gadgetRequest)
+{
+	struct GadgetEp *gadgetEp;
+	struct GADGET_REQ *gadgetReq;
+	uint8_t epType;
+	uint32_t requestSize, channelStatus, chMaxLen;
+
+	if (!priv || !gadgetRequest)
+		return;
+
+	gadgetEp = gadgetRequest->ep;
+	chMaxLen = priv->dmaDrv->dma_getMaxLength(priv->dmaController, gadgetEp->channel);
+	epType = gadgetEp->gadgetEp.desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK;
+	channelStatus = priv->dmaDrv->dma_getChannelStatus(priv->dmaController, gadgetEp->channel);
+
+	gadgetReq = &gadgetRequest->request;
+	if ((gadgetReq->length - gadgetReq->actual) < chMaxLen)
+		requestSize = gadgetReq->length - gadgetReq->actual;
+	else
+		requestSize = chMaxLen;
+	pr_debug("Transmit/IN %s gadgetReq %p gadgetRequest:%p requestSize:0x%x packetSize:0x%x\n",
+		       gadgetEp->gadgetEp.name, gadgetReq, gadgetRequest,
+		       requestSize, gadgetEp->gadgetEp.maxPacket);
+
+	gadgetRequest->zlp = 0;
+	priv->dmaDrv->dma_channelProgram(priv->dmaController, gadgetEp->channel,
+				gadgetEp->gadgetEp.maxPacket,
+				gadgetReq->dma + gadgetReq->actual, requestSize, NULL, 0);
+}
+
+static int32_t gadgetEpXSetHalt(struct GADGET_CTRL *priv, struct GADGET_EP *gadget_Ep,
+				uint8_t value)
+{
+	struct GadgetEp *gadgetEp;
+	uint8_t epType;
+	struct GADGET_REQ *req = NULL;
+	struct GadgetRequest *gadgetRequest = NULL;
+	uint8_t epNum, txcon, rxcon;
+	uint32_t status = DMA_STATUS_ARMED;
+
+	if (!priv || !gadget_Ep)
+		return -EINVAL;
+
+	gadgetEp = toGadgetEp(gadget_Ep);
+	epType = gadgetEp->gadgetEp.desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK;
+	if (epType == USB_ENDPOINT_XFER_ISOC)
+		return -EINVAL;
+
+	pr_debug("%s: %s stall\n", gadget_Ep->name, value ? "set" : "clear");
+	req = gadgetGetNextReq(gadgetEp);
+
+	if (!value)
+		gadgetEp->wedged = 0;
+
+	if (value && gadgetEp->isInEp && req && gadgetEp->state == GADGET_EP_BUSY) {
+		while (status == DMA_STATUS_ARMED)
+			status = priv->dmaDrv->dma_getChannelStatus(priv->dmaController,
+									gadgetEp->channel);
+
+		gadgetWaitForBusyBit(priv, gadget_Ep);
+	}
+
+	epNum = gadgetEp->hwEpNum;
+
+	if (gadgetEp->isInEp) {
+		txcon = phytium_read8(&priv->regs->ep[epNum - 1].txcon);
+		if (value) {
+			phytium_write8(&priv->regs->ep[epNum - 1].txcon, txcon | CON_STALL);
+			phytium_write8(&priv->regs->endprst, ENDPRST_IO_TX | epNum);
+			phytium_write8(&priv->regs->endprst,
+					ENDPRST_IO_TX | epNum | ENDPRST_FIFORST);
+
+		} else {
+			phytium_write8(&priv->regs->endprst, ENDPRST_IO_TX | epNum);
+			phytium_write8(&priv->regs->endprst,
+					ENDPRST_IO_TX | epNum | ENDPRST_TOGRST);
+			phytium_write8(&priv->regs->ep[epNum - 1].txcon, txcon & (~CON_STALL));
+		}
+	} else {
+		rxcon = phytium_read8(&priv->regs->ep[epNum - 1].rxcon);
+		if (value) {
+			phytium_write8(&priv->regs->ep[epNum - 1].rxcon, rxcon | CON_STALL);
+			phytium_write8(&priv->regs->endprst, ENDPRST_IO_TX | epNum);
+			phytium_write8(&priv->regs->endprst, epNum | ENDPRST_FIFORST);
+		} else {
+			phytium_write8(&priv->regs->endprst, ENDPRST_IO_TX | epNum);
+			phytium_write8(&priv->regs->endprst,
+					epNum | ENDPRST_TOGRST | ENDPRST_FIFORST);
+			phytium_write8(&priv->regs->ep[epNum - 1].rxcon, rxcon & (~CON_STALL));
+		}
+	}
+
+	if (gadgetEp->state != GADGET_EP_BUSY && !value && req) {
+		gadgetRequest = requestToGadgetRequest(req);
+		if (gadgetEp->isInEp)
+			gadgetEpXDataSend(priv, gadgetRequest);
+		else
+			gadgetEpXDataReceive(priv, gadgetRequest);
+	}
+
+	return 0;
+}
+
+static int32_t gadgetEp0SetHalt(struct GADGET_CTRL *priv, struct GADGET_EP *gadget_Ep,
+				uint8_t value)
+{
+	struct GadgetEp *gadgetEp;
+
+	if (!priv || !gadget_Ep)
+		return -EINVAL;
+
+	gadgetEp = toGadgetEp(gadget_Ep);
+	if (!list_empty(&gadgetEp->request))
+		return -EBUSY;
+
+	switch (priv->ep0State) {
+	case GADGET_EP0_STAGE_IN:
+	case GADGET_EP0_STAGE_OUT:
+	case GADGET_EP0_STAGE_ACK:
+	case GADGET_EP0_STAGE_STATUSIN:
+	case GADGET_EP0_STAGE_STATUSOUT:
+		priv->ep0State = GADGET_EP0_STAGE_SETUP;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+
+static void gadgetEp0Callback(struct GADGET_CTRL *priv, struct GadgetEp *gadgetEp,
+		struct GADGET_REQ *req, uint8_t status)
+{
+	if (!priv || !gadgetEp || !req)
+		return;
+
+	priv->ep0State = GADGET_EP0_STAGE_SETUP;
+	list_del(&req->list);
+	gadgetEp->requestsInList--;
+
+	if (req->status == EINPROGRESS)
+		req->status = status;
+
+	if (req->complete)
+		req->complete(&gadgetEp->gadgetEp, req);
+}
+
+static enum usb_device_speed gadgetGetActualSpeed(struct GADGET_CTRL *priv)
+{
+	uint8_t speedctrl;
+
+	if (!priv)
+		return USB_SPEED_UNKNOWN;
+
+	speedctrl = phytium_read8(&priv->regs->speedctrl) & (~SPEEDCTRL_HSDISABLE);
+	switch (speedctrl) {
+	case SPEEDCTRL_HS:
+		return USB_SPEED_HIGH;
+	case SPEEDCTRL_FS:
+		return USB_SPEED_FULL;
+	case SPEEDCTRL_LS:
+		return USB_SPEED_LOW;
+	default:
+		return USB_SPEED_UNKNOWN;
+	}
+}
+
+static int32_t gadgetServiceSetFeatureReq(struct GADGET_CTRL *priv, struct usb_ctrlrequest *setup)
+{
+	uint8_t epNum, isIn;
+	struct GadgetEp *gadgetEp;
+
+	if (!priv || !setup)
+		return 0;
+
+	switch (setup->bRequestType & USB_RECIP_MASK) {
+	case USB_RECIP_DEVICE:
+		switch (setup->wValue) {
+		case USB_DEVICE_REMOTE_WAKEUP:
+			pr_info("set feature - remote wakup\n");
+			priv->isRemoteWakeup = 1;
+			break;
+		case USB_DEVICE_B_HNP_ENABLE:
+			pr_info("set feature - B HNP Enable\n");
+			pr_info("otg not implement\n");
+			return -EINVAL;
+		case USB_DEVICE_A_HNP_SUPPORT:
+			pr_info("set feature - A HNP support\n");
+			pr_info("otg not implete\n");
+			return -EINVAL;
+		}
+		break;
+	case USB_RECIP_INTERFACE:
+		break;
+	case USB_RECIP_ENDPOINT:
+		epNum = setup->wIndex & 0x0f;
+		isIn = setup->wIndex & USB_DIR_IN;
+		if (epNum == 0 || epNum > 15 || setup->wValue != 0)
+			return -EINVAL;
+
+		gadgetEp = isIn ? &priv->in[epNum] : &priv->out[epNum];
+
+		gadgetEpXSetHalt(priv, &gadgetEp->gadgetEp, 1);
+		break;
+	default:
+		return -EINVAL;
+	}
+	return 0;
+}
+
+static int32_t gadgetServiceClearFeatureReq(struct GADGET_CTRL *priv, struct usb_ctrlrequest *setup)
+{
+	uint8_t epNum, isIn;
+	struct GadgetEp *gadgetEp;
+
+	if (!priv || !setup)
+		return -EINVAL;
+
+	switch (setup->bRequestType & USB_RECIP_MASK) {
+	case USB_RECIP_DEVICE:
+		if (setup->wValue == USB_DEVICE_B_HNP_ENABLE) {
+			pr_err("otg not implement\n");
+			return -EINVAL;
+		}
+
+		if (setup->wValue != USB_DEVICE_REMOTE_WAKEUP)
+			return GADGET_EUNHANDLED;
+
+		priv->isRemoteWakeup = 0;
+		return GADGET_EAUTOACK;
+	case USB_RECIP_INTERFACE:
+		break;
+	case USB_RECIP_ENDPOINT:
+		pr_info("clear feature wIndex:0x%x wValue:0x%x\n", setup->wIndex, setup->wValue);
+		epNum = setup->wIndex & 0x7f;
+		isIn = setup->wIndex & USB_DIR_IN;
+		if (epNum == 0 || epNum > 15 || setup->wValue != 0)
+			return GADGET_EUNHANDLED;
+
+		gadgetEp = isIn ? &priv->in[epNum] : &priv->out[epNum];
+		if (!gadgetEp->gadgetEp.desc)
+			return -EINVAL;
+
+		if (gadgetEp->wedged)
+			break;
+		gadgetEpXSetHalt(priv, &gadgetEp->gadgetEp, 0);
+		break;
+	default:
+		return GADGET_EUNHANDLED;
+	}
+
+	return 0;
+}
+
+static int32_t gadgetServiceSetupReq(struct GADGET_CTRL *priv, struct usb_ctrlrequest *setup)
+{
+	struct GadgetEp *gadgetEp;
+	uint8_t isIn, epNum;
+	uint8_t rxcon, txcon;
+	int len;
+
+	if (!priv || !setup)
+		return -EINVAL;
+
+	if ((setup->bRequestType & USB_TYPE_MASK) == USB_TYPE_STANDARD) {
+		if (setup->bRequest != USB_REQ_GET_STATUS)
+			return GADGET_EUNHANDLED;
+	} else
+		return GADGET_EUNHANDLED;
+
+	priv->privBuffAddr[1] = 0;
+	priv->privBuffAddr[0] = 0;
+
+	switch (setup->bRequest & USB_RECIP_MASK) {
+	case USB_RECIP_DEVICE:
+		pr_info("wIndex:0x%x isSelfPowered:%d isRomoteWakeup:%d\n",
+				setup->wIndex, priv->isSelfPowered, priv->isRemoteWakeup);
+
+		if (setup->wIndex == OTG_STS_SELECTOR)
+			priv->privBuffAddr[0] = priv->hostRequestFlag;
+		else {
+			priv->privBuffAddr[0] = priv->isSelfPowered ? USB_DEVICE_SELF_POWERED : 0;
+			priv->privBuffAddr[1] = priv->isRemoteWakeup ? USB_DEVICE_REMOTE_WAKEUP : 0;
+		}
+		break;
+	case USB_RECIP_INTERFACE:
+		break;
+	case USB_RECIP_ENDPOINT:
+		epNum = setup->wIndex & 0x0f;
+		if (!epNum)
+			break;
+
+		isIn = setup->wIndex & USB_DIR_IN;
+
+		gadgetEp = isIn ? &priv->in[epNum] : &priv->out[epNum];
+		if (!gadgetEp->gadgetEp.desc)
+			return -EINVAL;
+
+		if (isIn) {
+			txcon = phytium_read8(&priv->regs->ep[epNum - 1].txcon);
+			priv->privBuffAddr[0] = (txcon & CON_STALL) ? 1 : 0;
+		} else {
+			rxcon = phytium_read8(&priv->regs->ep[epNum - 1].rxcon);
+			priv->privBuffAddr[0] = (rxcon & CON_STALL) ? 1 : 0;
+		}
+		break;
+	default:
+		return GADGET_EUNHANDLED;
+	}
+
+	len = setup->wLength;
+	if (len > 2)
+		len = 2;
+
+	priv->dmaDrv->dma_channelProgram(priv->dmaController, priv->in[0].channel,
+			priv->in[0].gadgetEp.maxPacket, priv->privBuffDma, len, NULL, 0);
+
+	return 0;
+}
+
+static int32_t gadgetGetSetup(struct GADGET_CTRL *priv, struct usb_ctrlrequest *setup)
+{
+	int i;
+	uint8_t ep0cs;
+	struct GADGET_REQ *request = NULL;
+	struct GadgetRequest *gadgetRequest;
+
+	if (!priv || !setup)
+		return -EINVAL;
+
+	phytium_write8(&priv->regs->ep0cs, EP0CS_CHGSET);
+
+	for (i = 0; i < 8; i++)
+		((char *)setup)[i] = phytium_read8(&priv->regs->setupdat[i]);
+
+	ep0cs = phytium_read8(&priv->regs->ep0cs);
+	if (ep0cs & EP0CS_CHGSET) {
+		pr_info("setup flags change: not error\n");
+		return GADGET_EAUTOACK;
+	}
+
+	phytium_write8(&priv->regs->usbirq, USBIR_SUDAV);
+	pr_debug("setup packet: req%02x.%02x v:%04x i:%04x I%d\n", setup->bRequestType,
+			setup->bRequest, setup->wValue, setup->wIndex, setup->wLength);
+
+	request = gadgetGetNextEp0Req(priv);
+	if (request) {
+		gadgetRequest = requestToGadgetRequest(request);
+		pr_info("Previous request has not been finished but new was received\n");
+		gadgetEp0Callback(priv, gadgetRequest->ep, request, 0);
+	}
+
+	if (setup->wLength) {
+		if (setup->bRequestType & USB_DIR_IN)
+			priv->ep0State = GADGET_EP0_STAGE_IN;
+		else
+			priv->ep0State = GADGET_EP0_STAGE_OUT;
+	} else
+		priv->ep0State = GADGET_EP0_STAGE_ACK;
+
+	return 0;
+}
+
+static void gadgetEp0StageSetup(struct GADGET_CTRL *priv)
+{
+	struct usb_ctrlrequest setup;
+	int32_t retval;
+	uint8_t ep0cs;
+
+	if (!priv)
+		return;
+
+	retval = gadgetGetSetup(priv, &setup);
+
+	priv->gadgetDev.speed = gadgetGetActualSpeed(priv);
+
+	switch (priv->ep0State) {
+	case GADGET_EP0_STAGE_ACK:
+		if ((setup.bRequestType & USB_TYPE_MASK) != USB_TYPE_STANDARD)
+			retval = GADGET_EUNHANDLED;
+		else {
+			switch (setup.bRequest) {
+			case USB_REQ_SET_ADDRESS:
+				priv->deviceAddress = setup.wValue & 0x7F;
+				priv->gadgetDev.state = USB_STATE_ADDRESS;
+				pr_info("set address: %d\n", priv->deviceAddress);
+				retval = GADGET_EAUTOACK;
+				break;
+			case USB_REQ_SET_FEATURE:
+				retval = gadgetServiceSetFeatureReq(priv, &setup);
+				break;
+			case USB_REQ_CLEAR_FEATURE:
+				retval = gadgetServiceClearFeatureReq(priv, &setup);
+				break;
+			default:
+				retval = GADGET_EUNHANDLED;
+				break;
+			}
+		}
+
+		if (retval == GADGET_EUNHANDLED)
+			break;
+		else if (retval == 0)
+			phytium_write8(&priv->regs->ep0cs, EP0CS_HSNAK);
+
+		priv->ep0State = GADGET_EP0_STAGE_SETUP;
+		break;
+	case GADGET_EP0_STAGE_IN:
+		pr_debug("setup data stage in\n");
+		retval = gadgetServiceSetupReq(priv, &setup);
+
+		if (retval == 0)
+			priv->ep0State = GADGET_EP0_STAGE_STATUSOUT;
+		break;
+	case GADGET_EP0_STAGE_OUT:
+		pr_debug("setup data stage out\n");
+		phytium_write8(&priv->regs->ep0Rxbc, 0);
+		retval = GADGET_EUNHANDLED;
+		break;
+	default:
+		if (retval == GADGET_EAUTOACK)
+			return;
+
+		pr_debug("forward request\n");
+		retval = GADGET_EUNHANDLED;
+		break;
+	}
+
+	if (retval == GADGET_EUNHANDLED) {
+		if (priv->eventCallback.setup)
+			retval = priv->eventCallback.setup(priv, &setup);
+
+		if (retval == 0x7FFF) {
+			pr_debug("Respond Delayed not finished yet\n");
+			return;
+		}
+
+		if (retval)
+			retval = GADGET_ESTALL;
+	}
+
+	if (retval == GADGET_EUNHANDLED || retval == GADGET_ESTALL) {
+		pr_debug("request not handled - send stall\n");
+		ep0cs = phytium_read8(&priv->regs->ep0cs);
+		ep0cs |= EP0CS_STALL;
+		phytium_write8(&priv->regs->ep0cs, ep0cs);
+		priv->ep0State = GADGET_EP0_STAGE_SETUP;
+	} else if (priv->ep0State == GADGET_EP0_STAGE_ACK) {
+		priv->ep0State = GADGET_EP0_STAGE_SETUP;
+		phytium_write8(&priv->regs->ep0cs, EP0CS_HSNAK);
+		pr_debug("setup transfer completed\n");
+	}
+}
+static void gadgetEp0DataSend(struct GADGET_CTRL *priv)
+{
+	struct GADGET_REQ *request;
+	uint32_t chMaxLen, requestSize;
+
+	if (!priv)
+		return;
+
+	request = gadgetGetNextEp0Req(priv);
+	if (!request) {
+		pr_debug("Ep0 queue is empty\n");
+		return;
+	}
+
+	if (priv->dmaDrv->dma_getChannelStatus(priv->dmaController, priv->in[0].channel)
+						>= DMA_STATUS_BUSY) {
+		pr_err("transfer is pending now\n");
+		return;
+	}
+
+	chMaxLen = priv->dmaDrv->dma_getMaxLength(priv->dmaController, priv->in[0].channel);
+	requestSize = (request->length < chMaxLen) ? request->length : chMaxLen;
+	pr_debug("usbRequest;%p requestSize:%d packetSize:%d\n", request, requestSize,
+			priv->in[0].gadgetEp.maxPacket);
+	priv->ep0State = GADGET_EP0_STAGE_STATUSOUT;
+
+	priv->dmaDrv->dma_channelProgram(priv->dmaController, priv->in[0].channel,
+			priv->in[0].gadgetEp.maxPacket, request->dma, requestSize, NULL, 0);
+}
+
+static void gadgetEp0DataReceive(struct GADGET_CTRL *priv)
+{
+	uint32_t chMaxLen, requestSize;
+	struct GADGET_REQ *request;
+
+	if (!priv)
+		return;
+
+	request = gadgetGetNextEp0Req(priv);
+	if (!request) {
+		pr_debug("Ep0 queue is empty\n");
+		return;
+	}
+
+	chMaxLen = priv->dmaDrv->dma_getMaxLength(priv->dmaController, priv->out[0].channel);
+	requestSize = (request->length < chMaxLen) ? request->length : chMaxLen;
+	pr_debug("usbRequest;%p requestSize:%d packetSize:%d\n", request, requestSize,
+			priv->out[0].gadgetEp.maxPacket);
+	priv->ep0State = GADGET_EP0_STAGE_STATUSIN;
+
+	priv->dmaDrv->dma_channelProgram(priv->dmaController, priv->out[0].channel,
+			priv->out[0].gadgetEp.maxPacket, request->dma, requestSize, NULL, 0);
+}
+
+static uint32_t gadgetEp0Irq(struct GADGET_CTRL *priv)
+{
+	uint8_t usbcs;
+	struct GADGET_REQ *request;
+
+	if (!priv)
+		return 0;
+
+	switch (priv->ep0State) {
+	case GADGET_EP0_STAGE_IN://send data
+		pr_debug("DATA Stage IN\n");
+		gadgetEp0DataSend(priv);
+		break;
+	case GADGET_EP0_STAGE_OUT://receive data
+		pr_debug("DATA Stage OUT\n");
+		gadgetEp0DataReceive(priv);
+		break;
+	case GADGET_EP0_STAGE_STATUSIN:
+		pr_debug("DATA Stage STATUS IN\n");
+		request = gadgetGetNextEp0Req(priv);
+		if (request)
+			request->actual = priv->dmaDrv->dma_getActualLength(priv->dmaController,
+								priv->out[0].channel);
+
+		priv->ep0State = GADGET_EP0_STAGE_SETUP;
+		phytium_write8(&priv->regs->ep0cs, EP0CS_HSNAK);
+		gadgetEp0Callback(priv, &priv->out[0], request, 0);
+		break;
+	case GADGET_EP0_STAGE_STATUSOUT:
+		pr_debug("DATA Stage STATUS OUT\n");
+		request = gadgetGetNextEp0Req(priv);
+		if (request)
+			request->actual = priv->dmaDrv->dma_getActualLength(priv->dmaController,
+								priv->in[0].channel);
+
+		phytium_write8(&priv->regs->ep0cs, EP0CS_HSNAK);
+		if (request)
+			gadgetEp0Callback(priv, &priv->in[0], request, 0);
+		else
+			priv->ep0State = GADGET_EP0_STAGE_SETUP;
+		break;
+	case GADGET_EP0_STAGE_SETUP:
+		pr_debug("DATA Stage SETUP\n");
+		gadgetEp0StageSetup(priv);
+		break;
+	case GADGET_EP0_STAGE_ACK:
+		pr_debug("DATA Stage ACK\n");
+		break;
+	default:
+		pr_debug("DATA Stage UNKNOWN\n");
+		usbcs = phytium_read8(&priv->regs->usbcs);
+		usbcs |= EP0CS_STALL;
+		phytium_write8(&priv->regs->usbcs, usbcs);
+		break;
+	}
+
+	return 0;
+}
+
+static void gadgetEpXCallback(struct GADGET_CTRL *priv, struct GadgetEp *gadgetEp,
+		struct GADGET_REQ *req, uint32_t status)
+{
+	if (!gadgetEp || !req)
+		return;
+
+	list_del(&req->list);
+	gadgetEp->requestsInList--;
+	req->status = status;
+
+	if (req->complete)
+		req->complete(&gadgetEp->gadgetEp, req);
+}
+
+static void gadgetEpXDataCallback(struct GADGET_CTRL *priv, uint8_t epNum, uint8_t epDir)
+{
+	struct GadgetEp *gadgetEp;
+	struct GADGET_REQ *gadgetReq;
+	uint32_t actual_length = 0;
+	uint32_t chMaxLen = 0;
+	uint8_t epType;
+
+	if (!priv)
+		return;
+
+	pr_debug("%s %d epNum:%d epDir:%d\n", __func__, __LINE__, epNum, epDir);
+	gadgetEp = epDir ? &priv->in[epNum] : &priv->out[epNum];
+
+	gadgetReq = gadgetGetNextReq(gadgetEp);
+	if (!gadgetReq) {
+		pr_debug("%s queue is empty\n", gadgetEp->gadgetEp.name);
+		return;
+	}
+
+	if (gadgetEp->channel) {
+		actual_length = priv->dmaDrv->dma_getActualLength(priv->dmaController,
+				gadgetEp->channel);
+		chMaxLen = priv->dmaDrv->dma_getMaxLength(priv->dmaController, gadgetEp->channel);
+		gadgetReq->actual += actual_length;
+	}
+
+	if (gadgetReq->actual == gadgetReq->length || actual_length < chMaxLen) {
+		gadgetEpXCallback(priv, gadgetEp, gadgetReq, 0);
+
+		if (gadgetEp->gadgetEp.desc) {
+			epType = gadgetEp->gadgetEp.desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK;
+			if (epType == USB_ENDPOINT_XFER_ISOC)
+				return;
+
+			gadgetReq = gadgetGetNextReq(gadgetEp);
+			gadgetEp->state = GADGET_EP_ALLOCATED;
+			if (!gadgetReq) {
+				pr_debug("%s queue is empty\n", gadgetEp->gadgetEp.name);
+				return;
+			}
+			gadgetEp->state = GADGET_EP_BUSY;
+			if (epDir)
+				gadgetEpXDataSend(priv, requestToGadgetRequest(gadgetReq));
+			else
+				gadgetEpXDataReceive(priv, requestToGadgetRequest(gadgetReq));
+		}
+	} else {
+		if (epDir)
+			gadgetEpXDataSend(priv, requestToGadgetRequest(gadgetReq));
+		else
+			gadgetEpXDataReceive(priv, requestToGadgetRequest(gadgetReq));
+	}
+}
+
+void gadget_CallbackTransfer(void *priv, uint8_t epNum, uint8_t epDir, bool resubmit)
+{
+	if (!epNum)
+		gadgetEp0Irq(priv);
+	else
+		gadgetEpXDataCallback(priv, epNum, epDir);
+}
+
+static void gadgetAbortEndpoint(struct GADGET_CTRL *priv, struct GadgetEp *gadgetEp)
+{
+	struct GADGET_REQ *gadgetReq;
+
+	pr_debug("Abort Device endpoint: %s, dma channel: %p\n", gadgetEp->gadgetEp.name,
+			gadgetEp->channel);
+
+	if (gadgetEp->channel && gadgetEp->hwEpNum != 0) {
+		priv->dmaDrv->dma_channelRelease(priv->dmaController, gadgetEp->channel);
+		gadgetEp->channel = NULL;
+	}
+
+	if (gadgetEp->channel && gadgetEp->hwEpNum == 0) {
+		if (priv->releaseEp0Flag == 1) {
+			priv->dmaDrv->dma_channelAbort(priv->dmaController, gadgetEp->channel);
+			priv->dmaDrv->dma_channelAlloc(priv->dmaController, gadgetEp->isInEp,
+					gadgetEp->hwEpNum, 0);
+		}
+	}
+
+	while (gadgetEp->request.next != &gadgetEp->request) {
+		gadgetReq = listToGadgetRequest(gadgetEp->request.next);
+		pr_debug("shutdown request %p form epName:%s\n", gadgetReq,
+				gadgetEp->gadgetEp.name);
+		if (!gadgetEp->gadgetEp.address)
+			gadgetEp0Callback(priv, gadgetEp, gadgetReq, GADGET_ESHUTDOWN);
+		else
+			gadgetEpXCallback(priv, gadgetEp, gadgetReq, GADGET_ESHUTDOWN);
+	}
+
+	gadgetEp->state = GADGET_EP_FREE;
+}
+
+static void gadgetStopActivity(struct GADGET_CTRL *priv)
+{
+	int i = 0;
+	struct GadgetEp *gadgetEp;
+
+	pr_debug("USB Stop Activity\n");
+
+	if (!priv)
+		return;
+
+	for (i = 0; i < 16; i++) {
+		gadgetEp = &priv->in[i];
+		if (gadgetEp->state != GADGET_EP_NOT_IMPLEMENTED)
+			gadgetAbortEndpoint(priv, gadgetEp);
+
+		gadgetEp = &priv->out[i];
+		if (gadgetEp->state != GADGET_EP_NOT_IMPLEMENTED)
+			gadgetAbortEndpoint(priv, gadgetEp);
+	}
+}
+
+
+static int32_t gadgetEp0Enable(struct GADGET_CTRL *priv, struct GadgetEp *gadgetEp)
+{
+	uint8_t txien, rxien;
+
+	if (!priv || !gadgetEp)
+		return -EINVAL;
+
+	if (gadgetEp->state != GADGET_EP_FREE)
+		return -EBUSY;
+
+	if (!gadgetEp->isInEp) {
+		rxien = phytium_read16(&priv->regs->rxien);
+		rxien &= ~(1 << gadgetEp->hwEpNum);
+		phytium_write16(&priv->regs->rxien, rxien);
+		phytium_write8(&priv->regs->ep0fifoctrl, FIFOCTRL_IO_TX | FIFOCTRL_FIFOAUTO);
+	} else {
+		txien = phytium_read16(&priv->regs->txien);
+		txien &= ~(1 << gadgetEp->hwEpNum);
+		phytium_write16(&priv->regs->txien, txien);
+		phytium_write8(&priv->regs->ep0fifoctrl, FIFOCTRL_FIFOAUTO);
+	}
+
+	gadgetEp->gadgetEp.desc = NULL;
+	gadgetEp->state = GADGET_EP_ALLOCATED;
+	gadgetEp->channel = priv->dmaDrv->dma_channelAlloc(priv->dmaController,
+			gadgetEp->isInEp, gadgetEp->hwEpNum, 0);
+	phytium_write8(&priv->regs->ep0maxpack, 0x40);
+
+	return 0;
+}
+
+static int32_t gadgetEpXEnable(struct GADGET_CTRL *priv, struct GadgetEp *gadgetEp,
+		const struct usb_endpoint_descriptor *desc)
+{
+	uint32_t status = -EINVAL;
+	uint32_t payload;
+	uint16_t type, iso = 0;
+	uint8_t epNum = 0;
+
+	if (!priv || !gadgetEp || !desc)
+		return -EINVAL;
+
+	pr_debug("enable endpoint %s\n", gadgetEp->gadgetEp.name);
+	if (gadgetEp->state != GADGET_EP_FREE) {
+		status = -EBUSY;
+		goto fail;
+	}
+
+	payload = desc->wMaxPacketSize & 0x7ff;
+	if (!payload) {
+		status = -EINVAL;
+		goto fail;
+	}
+
+	epNum = gadgetEp->hwEpNum;
+
+	type = desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK;
+	switch (type) {
+	case USB_ENDPOINT_XFER_ISOC:
+		type = CON_TYPE_ISOC;
+		switch (payload >> 11) {
+		case 0:
+			iso = CON_TYPE_ISOC_1_ISOD;
+			break;
+		case 1:
+			payload *= 2;
+			iso = CON_TYPE_ISOC_2_ISOD;
+			break;
+		case 2:
+			payload *= 3;
+			iso = CON_TYPE_ISOC_3_ISOD;
+			break;
+		}
+		break;
+	case USB_ENDPOINT_XFER_INT:
+		type = CON_TYPE_INT;
+		break;
+	case USB_ENDPOINT_XFER_BULK:
+		type = CON_TYPE_BULK;
+		break;
+	}
+
+	if (desc->bEndpointAddress & USB_DIR_IN) {
+		if (!gadgetEp->isInEp) {
+			status = -ENODEV;
+			goto fail;
+		}
+
+		if (payload > priv->gadgetCfg.epIN[epNum].maxPacketSize) {
+			status = -EINVAL;
+			goto fail;
+		}
+
+		phytium_write16(&priv->regs->txmaxpack[epNum - 1], payload);
+		phytium_write8(&priv->regs->ep[epNum - 1].txcon, CON_VAL | type
+				| iso | (priv->gadgetCfg.epIN[epNum - 1].bufferingValue - 1));
+
+		phytium_write8(&priv->regs->fifoctrl, FIFOCTRL_FIFOAUTO | FIFOCTRL_IO_TX | epNum);
+		phytium_write8(&priv->regs->endprst, ENDPRST_IO_TX | epNum);
+		phytium_write8(&priv->regs->endprst, ENDPRST_IO_TX | epNum |
+				ENDPRST_FIFORST | ENDPRST_TOGRST);
+	} else {
+		if (gadgetEp->isInEp) {
+			status = -ENODEV;
+			goto fail;
+		}
+
+		if (payload > priv->gadgetCfg.epOUT[epNum].maxPacketSize) {
+			status = -EINVAL;
+			goto fail;
+		}
+
+		phytium_write16(&priv->regs->rxmaxpack[epNum - 1], payload);
+		phytium_write8(&priv->regs->ep[epNum - 1].rxcon, CON_VAL | type
+				| iso | (priv->gadgetCfg.epIN[epNum - 1].bufferingValue - 1));
+
+		phytium_write8(&priv->regs->fifoctrl, FIFOCTRL_FIFOAUTO | epNum);
+		phytium_write8(&priv->regs->endprst, epNum);
+		phytium_write8(&priv->regs->endprst, epNum | ENDPRST_FIFORST | ENDPRST_TOGRST);
+	}
+
+	if (priv->dmaController)
+		gadgetEp->channel = priv->dmaDrv->dma_channelAlloc(priv->dmaController,
+				gadgetEp->isInEp, epNum, (type == CON_TYPE_ISOC) ? 1 : 0);
+
+	if (type == CON_TYPE_ISOC) {
+		if (gadgetEp->isInEp) {
+			phytium_write16(&priv->regs->isoautodump, 1 << epNum);
+			phytium_write16(&priv->regs->isodctrl, 1 << epNum);
+		}
+
+		priv->dmaDrv->dma_setMaxLength(priv->dmaController, gadgetEp->channel, payload);
+	}
+
+	gadgetEp->state = GADGET_EP_ALLOCATED;
+	gadgetEp->gadgetEp.desc = desc;
+fail:
+	return status;
+}
+
+static int32_t gadgetEpEnable(struct GADGET_CTRL *priv, struct GADGET_EP *gadget_Ep,
+		const struct usb_endpoint_descriptor *desc)
+{
+	struct GadgetEp *gadgetEp = NULL;
+
+	if (!priv || !gadget_Ep || !desc)
+		return -EINVAL;
+
+	gadgetEp = toGadgetEp(gadget_Ep);
+	gadgetEp->wedged = 0;
+
+	if (gadgetEp->hwEpNum)
+		return gadgetEpXEnable(priv, gadgetEp, desc);
+	else
+		return gadgetEp0Enable(priv, gadgetEp);
+}
+
+static int32_t gadgetEpXDisable(struct GADGET_CTRL *priv, struct GadgetEp *gadgetEp)
+{
+	uint8_t txcon, rxcon;
+
+	if (!priv || !gadgetEp)
+		return -EINVAL;
+
+	pr_debug("disable endpoint %s\n", gadgetEp->gadgetEp.name);
+	if (gadgetEp->isInEp) {
+		txcon = phytium_read8(&priv->regs->ep[gadgetEp->hwEpNum - 1].txcon);
+		txcon &= ~CON_VAL;
+		phytium_write8(&priv->regs->ep[gadgetEp->hwEpNum - 1].txcon, txcon);
+	} else {
+		rxcon = phytium_read8(&priv->regs->ep[gadgetEp->hwEpNum - 1].rxcon);
+		rxcon &= ~CON_VAL;
+		phytium_write8(&priv->regs->ep[gadgetEp->hwEpNum - 1].rxcon, rxcon);
+	}
+	gadgetAbortEndpoint(priv, gadgetEp);
+	gadgetEp->gadgetEp.desc = 0;
+	gadgetEp->state = GADGET_EP_FREE;
+	return 0;
+}
+
+static int32_t gadgetEp0Disable(struct GADGET_CTRL *priv, struct GadgetEp *gadgetEp)
+{
+	if (!priv || !gadgetEp)
+		return -EINVAL;
+
+	pr_debug("disable endpoint %s\n", gadgetEp->gadgetEp.name);
+	gadgetAbortEndpoint(priv, gadgetEp);
+
+	return 0;
+}
+
+static int32_t gadgetEpDisable(struct GADGET_CTRL *priv, struct GADGET_EP *gadget_Ep)
+{
+	struct GadgetEp *gadgetEp = NULL;
+
+	if (!priv || !gadget_Ep)
+		return -EINVAL;
+
+	if (gadget_Ep->address == 0)
+		return -EINVAL;
+
+	gadgetEp = toGadgetEp(gadget_Ep);
+	gadgetEp->wedged = 0;
+	if (gadgetEp->hwEpNum)
+		return gadgetEpXDisable(priv, gadgetEp);
+	else
+		return gadgetEp0Disable(priv, gadgetEp);
+}
+
+static int32_t gadgetEpXQueue(struct GADGET_CTRL *priv, struct GadgetEp *gadgetEp,
+		struct GADGET_REQ *req)
+{
+	struct GadgetRequest *gadgetRequest;
+
+	if (!priv || !gadgetEp || !req)
+		return -EINVAL;
+
+	req->actual = 0;
+	req->status = EINPROGRESS;
+
+	gadgetRequest = requestToGadgetRequest(req);
+	gadgetRequest->ep = gadgetEp;
+
+	if (req->length == 0)
+		gadgetRequest->zlp = 1;
+
+	if (gadgetEp->gadgetEp.desc == NULL) {
+		pr_info("%s is disabled - can not queue request %p\n",
+				gadgetEp->gadgetEp.name, req);
+		return -EINVAL;
+	}
+
+	list_add_tail(&req->list, &gadgetEp->request);
+	pr_debug("queue to %s (%s), length:%d\n", gadgetEp->gadgetEp.name,
+			(gadgetEp->isInEp ? "IN/TX" : "OUT/RX"), req->length);
+
+	if ((gadgetEp->state == GADGET_EP_ALLOCATED) && (&req->list == gadgetEp->request.next)) {
+		if (gadgetEp->isInEp) {
+			if (!(phytium_read8(&priv->regs->ep[gadgetEp->hwEpNum - 1].txcon)
+						& CON_STALL)) {
+				gadgetEp->state = GADGET_EP_BUSY;
+				gadgetEpXDataSend(priv, gadgetRequest);
+			}
+		} else {
+			if (!(phytium_read8(&priv->regs->ep[gadgetEp->hwEpNum - 1].rxcon)
+						& CON_STALL)) {
+				gadgetEp->state = GADGET_EP_BUSY;
+				gadgetEpXDataReceive(priv, gadgetRequest);
+			}
+		}
+	} else if (gadgetEp->state == GADGET_EP_BUSY) {
+		if (usb_endpoint_xfer_isoc(gadgetEp->gadgetEp.desc)) {
+			if (gadgetEp->isInEp)
+				gadgetEpXDataSend(priv, gadgetRequest);
+			else
+				gadgetEpXDataReceive(priv, gadgetRequest);
+		}
+	}
+
+	pr_debug("endpoint %s (%s) now is busy - transfer will be waiting in Queue\n",
+			gadgetEp->gadgetEp.name, (gadgetEp->isInEp ? "IN/TX" : "OUT/RX"));
+
+	return 0;
+}
+
+static int32_t gadgetEp0Queue(struct GADGET_CTRL *priv, struct GadgetEp *gadgetEp,
+		struct GADGET_REQ *req)
+{
+	struct GadgetRequest *gadgetRequest;
+
+	if (!priv || !gadgetEp || !req)
+		return -EINVAL;
+
+	req->actual = 0;
+	req->status = EINPROGRESS;
+
+	if (!list_empty(&gadgetEp->request))
+		return -EBUSY;
+
+	gadgetRequest = requestToGadgetRequest(req);
+	gadgetRequest->ep = gadgetEp;
+
+	switch (priv->ep0State) {
+	case GADGET_EP0_STAGE_OUT:
+	case GADGET_EP0_STAGE_IN:
+	case GADGET_EP0_STAGE_ACK:
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	list_add_tail(&req->list, &gadgetEp->request);
+	gadgetEp->requestsInList++;
+
+	pr_debug("queue to %s (%s), length:%d stage:%d\n", gadgetEp->gadgetEp.name,
+			gadgetEp->isInEp ? "IN/TX" : "OUT/RX", req->length, priv->ep0State);
+
+	switch (priv->ep0State) {
+	case GADGET_EP0_STAGE_OUT:
+		gadgetEp0DataReceive(priv);
+		break;
+	case GADGET_EP0_STAGE_IN:
+		gadgetEp0DataSend(priv);
+		break;
+	case GADGET_EP0_STAGE_ACK:
+		if (req->length)
+			return -EINVAL;
+		phytium_write8(&priv->regs->ep0cs, EP0CS_HSNAK);
+		gadgetEp0Callback(priv, gadgetRequest->ep, req, 0);
+		pr_info("control transfer completed\n");
+		break;
+	default:
+		break;
+	}
+
+	return 0;
+}
+
+static int32_t gadgetEpQueue(struct GADGET_CTRL *priv, struct GADGET_EP *gadget_Ep,
+		struct GADGET_REQ *req)
+{
+	struct GadgetEp *gadgetEp = NULL;
+
+	if (!priv || !gadget_Ep || !req)
+		return -EINVAL;
+
+	gadgetEp = toGadgetEp(gadget_Ep);
+
+	if (gadget_Ep->address & GADGET_USB_EP_NUMBER_MASK)
+		return gadgetEpXQueue(priv, gadgetEp, req);
+	else
+		return gadgetEp0Queue(priv, gadgetEp, req);
+}
+
+static int32_t gadgetEpXDequeue(struct GADGET_CTRL *priv, struct GadgetEp *gadgetEp,
+		struct GADGET_REQ *req)
+{
+	struct GadgetRequest *gadgetRequest;
+	struct GADGET_REQ *iterator;
+
+	if (!priv || !gadgetEp || !req)
+		return -EINVAL;
+
+	gadgetRequest = requestToGadgetRequest(req);
+	if (gadgetRequest->ep != gadgetEp)
+		return -EINVAL;
+
+	pr_debug("Dequeue request %p form %s\n", req, gadgetEp->gadgetEp.name);
+
+	listBrowsingRequest(iterator, &gadgetEp->request, list) {
+		if (req == iterator)
+			break;
+	}
+
+	if (req != iterator) {
+		pr_info("request %p not queued to %s\n", req, gadgetEp->gadgetEp.name);
+		return -EINVAL;
+	}
+
+	if (gadgetEp->state == GADGET_EP_BUSY) {
+		priv->dmaDrv->dma_channelAbort(priv->dmaController, gadgetEp->channel);
+		gadgetEp->state = GADGET_EP_ALLOCATED;
+	}
+
+	gadgetEpXCallback(priv, gadgetEp, req, GADGET_ECONNRESET);
+
+	return 0;
+}
+
+static int32_t gadgetEp0Dequeue(struct GADGET_CTRL *priv, struct GadgetEp *gadgetEp,
+		struct GADGET_REQ *req)
+{
+	return 0;
+}
+
+static int32_t gadgetEpDequeue(struct GADGET_CTRL *priv, struct GADGET_EP *gadget_Ep,
+		struct GADGET_REQ *req)
+{
+	struct GadgetEp *gadgetEp = NULL;
+
+	if (!priv || !gadget_Ep || !req)
+		return -EINVAL;
+
+	gadgetEp = toGadgetEp(gadget_Ep);
+
+	if (gadget_Ep->address & GADGET_USB_EP_NUMBER_MASK)
+		return gadgetEpXDequeue(priv, gadgetEp, req);
+	else
+		return gadgetEp0Dequeue(priv, gadgetEp, req);
+}
+
+static int32_t gadgetEpSetHalt(struct GADGET_CTRL *priv, struct GADGET_EP *gadget_Ep, uint8_t value)
+{
+	if (!priv || !gadget_Ep)
+		return -EINVAL;
+
+	if (gadget_Ep->address & GADGET_USB_EP_NUMBER_MASK)
+		return gadgetEpXSetHalt(priv, gadget_Ep, value);
+	else
+		return gadgetEp0SetHalt(priv, gadget_Ep, value);
+}
+
+static int32_t gadgetEpSetWedge(struct GADGET_CTRL *priv, struct GADGET_EP *gadget_Ep)
+{
+	struct GadgetEp *gadgetEp = NULL;
+
+	if (!priv || !gadget_Ep)
+		return -EINVAL;
+
+	gadgetEp = toGadgetEp(gadget_Ep);
+	gadgetEp->wedged = 1;
+
+	return gadgetEpSetHalt(priv, gadget_Ep, 1);
+}
+
+static int32_t gadgetEpFifoStatus(struct GADGET_CTRL *priv, struct GADGET_EP *gadget_Ep)
+{
+	if (!priv || !gadget_Ep)
+		return -EINVAL;
+
+	return 0;
+}
+
+static void gadgetEpFifoFlush(struct GADGET_CTRL *priv, struct GADGET_EP *gadget_Ep)
+{
+	if (!priv || !gadget_Ep)
+		return;
+}
+
+static int32_t gadgetEpAllocRequest(struct GADGET_CTRL *priv, struct GADGET_EP *gadget_Ep,
+		struct GADGET_REQ **req)
+{
+	struct GadgetEp *gadgetEp = NULL;
+	struct GadgetRequest *gadgetRequest = NULL;
+
+	if (!priv || !gadget_Ep || !req)
+		return -EINVAL;
+
+	gadgetEp = toGadgetEp(gadget_Ep);
+	if (priv->eventCallback.usbRequestMemAlloc)
+		gadgetRequest = priv->eventCallback.usbRequestMemAlloc(priv,
+				sizeof(*gadgetRequest));
+	if (!gadgetRequest)
+		return -ENOMEM;
+
+	memset(gadgetRequest, 0, sizeof(*gadgetRequest));
+	*req = &gadgetRequest->request;
+	INIT_LIST_HEAD(&gadgetRequest->request.list);
+	gadgetRequest->ep = gadgetEp;
+
+	return 0;
+}
+
+static void gadgetEpFreeRequest(struct GADGET_CTRL *priv, struct GADGET_EP *gadget_Ep,
+		struct GADGET_REQ *req)
+{
+	struct GadgetRequest *gadgetRequest = NULL;
+
+	if (!priv || !gadget_Ep || !req)
+		return;
+
+	gadgetRequest = requestToGadgetRequest(req);
+
+	if (priv->eventCallback.usbRequestMemFree)
+		priv->eventCallback.usbRequestMemFree(priv, gadgetRequest);
+}
+
+static struct GADGET_EP_OPS gadgetEpOps = {
+	.epEnable	=	gadgetEpEnable,
+	.epDisable	=	gadgetEpDisable,
+	.reqQueue	=	gadgetEpQueue,
+	.reqDequeue	=	gadgetEpDequeue,
+	.epSetHalt	=	gadgetEpSetHalt,
+	.epSetWedge	=	gadgetEpSetWedge,
+	.epFifoStatus	=	gadgetEpFifoStatus,
+	.reqAlloc	=	gadgetEpAllocRequest,
+	.reqFree	=	gadgetEpFreeRequest
+};
+
+static void gadgetInitDeviceEp(struct GADGET_CTRL *priv, uint8_t isInEp)
+{
+	uint8_t num;
+	struct GadgetEp *gadgetEp;
+	struct GADGET_EP_CFG epCfg;
+
+	if (!priv)
+		return;
+
+	for (num = 0; num < 16; num++) {
+		gadgetEp = isInEp ? &priv->in[num] : &priv->out[num];
+		if (num) {
+			epCfg = isInEp ? priv->gadgetCfg.epIN[num] : priv->gadgetCfg.epOUT[num];
+			if (!epCfg.bufferingValue) {
+				gadgetEp->state = GADGET_EP_NOT_IMPLEMENTED;
+				gadgetEp->hwEpNum = num;
+				continue;
+			}
+		}
+		INIT_LIST_HEAD(&gadgetEp->gadgetEp.epList);
+		INIT_LIST_HEAD(&gadgetEp->request);
+		gadgetEp->state = GADGET_EP_FREE;
+		gadgetEp->hwEpNum = num;
+		gadgetEp->isInEp = isInEp;
+		gadgetEp->requestsInList = 0;
+		snprintf(gadgetEp->gadgetEp.name, sizeof(gadgetEp->gadgetEp.name),
+				"Ep%d%s", num, isInEp ? "in" : "out");
+		if (!num) {
+			gadgetEp->gadgetEp.maxPacket = priv->gadgetCfg.epIN[num].maxPacketSize;
+			if (isInEp)
+				priv->gadgetDev.ep0 = &gadgetEp->gadgetEp;
+			gadgetEp->gadgetEp.ops = &gadgetEpOps;
+			gadgetEp0Enable(priv, gadgetEp);
+			continue;
+		}
+
+		if (isInEp) {
+			if (epCfg.startBuf)
+				phytium_write16(&priv->regs->txstaddr[num - 1].addr,
+						epCfg.startBuf);
+			phytium_write8(&priv->regs->ep[num - 1].txcon, 0);
+			gadgetEp->gadgetEp.maxPacket = epCfg.maxPacketSize;
+		} else {
+			if (epCfg.startBuf)
+				phytium_write16(&priv->regs->rxstaddr[num - 1].addr,
+						epCfg.startBuf);
+			phytium_write8(&priv->regs->ep[num - 1].rxcon, 0);
+			gadgetEp->gadgetEp.maxPacket = epCfg.maxPacketSize;
+		}
+
+		gadgetEp->gadgetEp.ops = &gadgetEpOps;
+		gadgetEp->gadgetEp.address = isInEp ? (0x80 | num) : num;
+		gadgetEp->gadgetEp.maxburst = 0;
+		gadgetEp->gadgetEp.mult = 0;
+		gadgetEp->gadgetEp.maxStreams = 0;
+		priv->endpointInList++;
+		list_add_tail(&gadgetEp->gadgetEp.epList, &priv->gadgetDev.epList);
+	}
+}
+
+static void gadgetInitEndpoint(struct GADGET_CTRL *priv)
+{
+	if (!priv)
+		return;
+
+	gadgetInitDeviceEp(priv, 1);
+	gadgetInitDeviceEp(priv, 0);
+}
+
+static void gadgetSetup(struct GADGET_CTRL *priv)
+{
+	if (!priv)
+		return;
+
+	INIT_LIST_HEAD(&priv->gadgetDev.epList);
+	priv->gadgetDev.state = USB_STATE_NOTATTACHED;
+	priv->gadgetDev.maxSpeed = USB_SPEED_HIGH;
+	priv->gadgetDev.speed = USB_SPEED_FULL;
+	snprintf(priv->gadgetDev.name, sizeof(priv->gadgetDev.name), "Phytium USB SD Driver");
+
+	gadgetInitEndpoint(priv);
+
+	phytium_write8(&priv->regs->ep0maxpack, 0x40);
+	phytium_write16(&priv->regs->rxien, 0);
+	phytium_write16(&priv->regs->txien, 0);
+	phytium_write16(&priv->regs->rxirq, 0xFFFF);
+	phytium_write16(&priv->regs->txirq, 0xFFFF);
+	phytium_write8(&priv->regs->usbirq, 0xEF);
+	phytium_write8(&priv->regs->endprst, ENDPRST_IO_TX);
+	phytium_write8(&priv->regs->endprst, ENDPRST_FIFORST | ENDPRST_TOGRST | ENDPRST_IO_TX);
+	phytium_write8(&priv->regs->endprst, ENDPRST_FIFORST | ENDPRST_TOGRST);
+	priv->isReady = 1;
+}
+int32_t gadgetInit(struct GADGET_CTRL *priv, struct GADGET_CFG *config,
+		struct GADGET_CALLBACKS *callbacks, struct device *pdev)
+{
+	struct DMA_SYSREQ dmaSysReq;
+	uint8_t usbcs;
+
+	if (!priv || !config || !callbacks)
+		return -EINVAL;
+
+	priv->dev = pdev;
+	priv->eventCallback = *callbacks;
+	priv->gadgetCfg = *config;
+	priv->regs = (struct HW_REGS *)config->regBase;
+	priv->phy_regs = (struct VHUB_REGS *)config->phy_regBase;
+	priv->gadgetDrv = GADGET_GetInstance();
+	priv->dmaDrv = DMA_GetInstance();
+	priv->dmaController = (void *)(priv + 1);
+	priv->dmaCfg.dmaModeRx = 0xFFFF;
+	priv->dmaCfg.dmaModeTx = 0xFFFF;
+	priv->dmaCfg.regBase = config->regBase + 0x400;
+	priv->dmaCfg.trbAddr = config->trbAddr;
+	priv->dmaCfg.trbDmaAddr = config->trbDmaAddr;
+
+	priv->dmaDrv->dma_probe(NULL, &dmaSysReq);
+	priv->privBuffAddr = (uint8_t *)((uintptr_t)config->trbAddr + dmaSysReq.trbMemSize);
+	priv->privBuffDma = (uintptr_t)((uintptr_t)config->trbDmaAddr + dmaSysReq.trbMemSize);
+	priv->dmaCallback.complete = gadget_CallbackTransfer;
+	priv->dmaDrv->dma_init(priv->dmaController, &priv->dmaCfg, &priv->dmaCallback);
+	priv->dmaDrv->dma_setParentPriv(priv->dmaController, priv);
+
+	usbcs = phytium_read8(&priv->regs->usbcs);
+	usbcs |= USBCS_DISCON | USBCS_LPMNYET;
+	phytium_write8(&priv->regs->usbcs, usbcs);
+
+	gadgetSetup(priv);
+
+	usbcs = phytium_read8(&priv->regs->usbcs);
+	usbcs &= USBCS_DISCON;
+	phytium_write8(&priv->regs->usbcs, usbcs);
+
+	return 0;
+}
+
+static void gadgetDestroy(struct GADGET_CTRL *priv)
+{
+	pr_debug("Destroy Device Controller driver\n");
+
+	if (priv)
+		return;
+	gadgetDisconnect(priv);
+
+	gadgetStopActivity(priv);
+
+	phytium_write8(&priv->regs->usbcs, USBCS_DISCON);
+
+	priv->isReady = 0;
+}
+
+static void gadgetStart(struct GADGET_CTRL *priv)
+{
+	uint8_t usbien, usbcs;
+
+	pr_debug("Usb Device Controller start\n");
+	if (!priv)
+		return;
+
+	usbien = phytium_read8(&priv->regs->usbien);
+	usbien |= USBIR_URES | USBIR_SUDAV | USBIR_LPMIR;
+	phytium_write8(&priv->regs->usbien, usbien);
+
+	usbcs = phytium_read8(&priv->regs->usbcs);
+	usbcs &= ~USBCS_DISCON;
+	phytium_write8(&priv->regs->usbcs, usbcs);
+
+	priv->dmaDrv->dma_start(priv->dmaController);
+}
+
+static void gadgetReset(struct GADGET_CTRL *priv)
+{
+	int i = 0;
+
+	pr_debug("Usb Disable Device Activity\n");
+
+	if (!priv)
+		return;
+
+	if (priv->gadgetDev.speed != USB_SPEED_UNKNOWN)
+		gadgetDisconnect(priv);
+
+	priv->gadgetDev.aHnpSupport = 0;
+	priv->gadgetDev.bHnpEnable = 0;
+	priv->gadgetDev.state = USB_STATE_DEFAULT;
+	priv->deviceAddress = 0;
+	priv->ep0State = GADGET_EP0_STAGE_SETUP;
+
+	gadgetStopActivity(priv);
+
+	for (i = 0; i < 1000; i++) {
+		priv->gadgetDev.speed = gadgetGetActualSpeed(priv);
+		if (priv->gadgetDev.speed == USB_SPEED_HIGH)
+			return;
+	}
+}
+
+static void gadgetStop(struct GADGET_CTRL *priv)
+{
+	pr_debug("Usb Device Controller stop\n");
+
+	if (!priv)
+		return;
+
+	if (!priv->isReady)
+		return;
+
+	gadgetReset(priv);
+
+	phytium_write8(&priv->regs->usbien, 0);
+	priv->dmaDrv->dma_stop(priv->dmaController);
+
+	priv->isReady = 0;
+}
+
+static void gadgetIsr(struct GADGET_CTRL *priv)
+{
+	uint8_t usbirq, usbien, usbcs;
+
+	if (!priv)
+		return;
+
+	usbirq = phytium_read8(&priv->regs->usbirq);
+	usbien = phytium_read8(&priv->regs->usbien);
+
+	pr_debug("usbirq:0x%x usbien:0x%x\n", usbirq, usbien);
+
+	usbirq = usbirq & usbien;
+
+	if (!usbirq)
+		goto DMA_IRQ;
+
+	if (usbirq & USBIR_LPMIR) {
+		pr_debug("USBIRQ LPM\n");
+		usbcs = phytium_read8(&priv->regs->usbcs);
+		usbcs &= ~USBCS_LPMNYET;
+		phytium_write8(&priv->regs->usbcs, usbcs);
+		phytium_write8(&priv->regs->usbirq, USBIR_LPMIR);
+	}
+
+	if (usbirq & USBIR_URES) {
+		pr_debug("USBIRQ RESET\n");
+		phytium_write8(&priv->regs->usbirq, USBIR_URES);
+		priv->releaseEp0Flag = 1;
+		gadgetReset(priv);
+		priv->releaseEp0Flag = 0;
+		priv->gadgetDev.state = USB_STATE_DEFAULT;
+		if (priv->eventCallback.connect)
+			priv->eventCallback.connect(priv);
+	}
+
+	if (usbirq & USBIR_HSPEED) {
+		pr_debug("USBIRQ HighSpeed\n");
+		phytium_write8(&priv->regs->usbirq, USBIR_HSPEED);
+		priv->gadgetDev.speed = USB_SPEED_HIGH;
+	}
+
+	if (usbirq & USBIR_SUDAV) {
+		pr_debug("USBIRQ SUDAV\n");
+		priv->ep0State = GADGET_EP0_STAGE_SETUP;
+		gadgetEp0Irq(priv);
+	}
+
+	if (usbirq & USBIR_SOF) {
+		pr_debug("USBIRQ SOF\n");
+		phytium_write8(&priv->regs->usbirq, USBIR_SOF);
+	}
+
+	if (usbirq & USBIR_SUTOK) {
+		pr_debug("USBIRQ SUTOK\n");
+		phytium_write8(&priv->regs->usbirq, USBIR_SUTOK);
+	}
+
+	if (usbirq & USBIR_SUSP) {
+		pr_debug("USBIRQ SUSPEND\n");
+		phytium_write8(&priv->regs->usbirq, USBIR_SUSP);
+	}
+
+	return;
+DMA_IRQ:
+	priv->dmaDrv->dma_isr(priv->dmaController);
+}
+
+static void gadgetGetDevInstance(struct GADGET_CTRL *priv, struct GADGET_DEV **dev)
+{
+	if (!priv || !dev)
+		return;
+
+	*dev = &priv->gadgetDev;
+}
+
+static int32_t gadgetGetFrame(struct GADGET_CTRL *priv, uint32_t *numOfFrame)
+{
+	if (!priv || !numOfFrame)
+		return -EINVAL;
+
+	*numOfFrame = phytium_read16(&priv->regs->frmnr);
+
+	return 0;
+}
+
+static int32_t gadgetWakeUp(struct GADGET_CTRL *priv)
+{
+	if (!priv)
+		return -EINVAL;
+
+	return -EOPNOTSUPP;
+}
+
+static int32_t gadgetSetSelfPowered(struct GADGET_CTRL *priv)
+{
+	if (!priv)
+		return -EINVAL;
+
+	priv->isSelfPowered = 1;
+
+	return 0;
+}
+
+static int32_t gadgetClearSelfPowered(struct GADGET_CTRL *priv)
+{
+	if (!priv)
+		return -EINVAL;
+
+	priv->isSelfPowered = 0;
+
+	return 0;
+}
+
+static int32_t gadgetVbusSession(struct GADGET_CTRL *priv, uint8_t isActive)
+{
+	if (!priv)
+		return -EINVAL;
+
+	return -EOPNOTSUPP;
+}
+
+static int32_t gadgetVbusDraw(struct GADGET_CTRL *priv, uint8_t mA)
+{
+	if (!priv)
+		return -EINVAL;
+
+	return -EOPNOTSUPP;
+}
+
+static int32_t gadgetPullUp(struct GADGET_CTRL *priv, uint8_t isOn)
+{
+	if (!priv)
+		return -EINVAL;
+
+	return -EOPNOTSUPP;
+}
+
+struct GADGET_OBJ GadgetObj = {
+	.gadget_init	=	gadgetInit,
+	.gadget_destroy =	gadgetDestroy,
+	.gadget_start	=	gadgetStart,
+	.gadget_stop	=	gadgetStop,
+	.gadget_isr	=	gadgetIsr,
+	//endpoint operation
+	.gadget_epEnable	=	gadgetEpEnable,
+	.gadget_epDisable	=	gadgetEpDisable,
+	.gadget_epSetHalt	=	gadgetEpSetHalt,
+	.gadget_epSetWedge	=	gadgetEpSetWedge,
+	.gadget_epFifoStatus	=	gadgetEpFifoStatus,
+	.gadget_epFifoFlush	=	gadgetEpFifoFlush,
+	.gadget_reqQueue	=	gadgetEpQueue,
+	.gadget_reqDequeue	=	gadgetEpDequeue,
+	.gadget_reqAlloc	=	gadgetEpAllocRequest,
+	.gadget_reqFree		=	gadgetEpFreeRequest,
+
+	//Device operations
+	.gadget_getDevInstance =	gadgetGetDevInstance,
+	.gadget_dGetFrame	=	gadgetGetFrame,
+	.gadget_dWakeUp		=	gadgetWakeUp,
+	.gadget_dSetSelfpowered =	gadgetSetSelfPowered,
+	.gadget_dClearSelfpowered =	gadgetClearSelfPowered,
+	.gadget_dVbusSession	=	gadgetVbusSession,
+	.gadget_dVbusDraw	=	gadgetVbusDraw,
+	.gadget_dPullUp		=	gadgetPullUp,
+};
+
+struct GADGET_OBJ *GADGET_GetInstance(void)
+{
+	return &GadgetObj;
+}
+
+static int phytium_gadget_set_default_cfg(struct phytium_cusb *config)
+{
+	int index;
+
+	config->gadget_cfg.regBase = (uintptr_t)config->regs;
+	config->gadget_cfg.phy_regBase = (uintptr_t)config->phy_regs;
+	config->gadget_cfg.dmaInterfaceWidth = GADGET_DMA_32_WIDTH;
+
+	for (index = 0; index < 16; index++) {
+		if (index == 0) {
+			config->gadget_cfg.epIN[index].bufferingValue = 1;
+			config->gadget_cfg.epIN[index].maxPacketSize = 64;
+			config->gadget_cfg.epIN[index].startBuf = 0;
+
+			config->gadget_cfg.epOUT[index].bufferingValue = 1;
+			config->gadget_cfg.epOUT[index].maxPacketSize = 64;
+			config->gadget_cfg.epOUT[index].startBuf = 0;
+		} else {
+			config->gadget_cfg.epIN[index].bufferingValue = 4;
+			config->gadget_cfg.epIN[index].maxPacketSize = 1024;
+			config->gadget_cfg.epIN[index].startBuf = 64 + 4096 * (index - 1);
+
+			config->gadget_cfg.epOUT[index].bufferingValue = 4;
+			config->gadget_cfg.epOUT[index].maxPacketSize = 1024;
+			config->gadget_cfg.epOUT[index].startBuf = 64 + 4096 * (index - 1);
+		}
+	}
+
+	return 0;
+}
+
+void gadget_callback_connect(struct GADGET_CTRL *priv)
+{
+	if (!priv)
+		return;
+}
+
+void gadget_callback_disconnect(struct GADGET_CTRL *priv)
+{
+	if (!priv)
+		return;
+}
+
+int32_t gadget_callback_setup(struct GADGET_CTRL *priv, struct usb_ctrlrequest *ctrl)
+{
+	struct phytium_cusb *config;
+	int ret = 0;
+
+	if (!priv || !ctrl)
+		return -EINVAL;
+
+	config = dev_get_drvdata(priv->dev);
+	if (!config)
+		return -1;
+
+	if (!config->gadget_driver)
+		return -EOPNOTSUPP;
+
+	if (ctrl->bRequestType & USB_DIR_IN)
+		config->ep0_data_stage_is_tx = 1;
+	else
+		config->ep0_data_stage_is_tx = 0;
+
+	spin_unlock(&config->lock);
+	ret = config->gadget_driver->setup(&config->gadget, ctrl);
+	spin_lock(&config->lock);
+
+	if (ret == 0x7FFF)
+		return ret;
+
+	if (ret < 0)
+		return 1;
+
+	return 0;
+}
+
+void *gadget_callback_usbRequestMemAlloc(struct GADGET_CTRL *priv, u32 size)
+{
+	struct GADGET_REQ *gadget_req = NULL;
+
+	gadget_req = kzalloc(size, GFP_NOWAIT);
+	if (!gadget_req)
+		return NULL;
+
+	return gadget_req;
+}
+
+void gadget_callback_usbRequestMemFree(struct GADGET_CTRL *priv, void *usbReq)
+{
+	if (!usbReq)
+		return;
+
+	kfree(usbReq);
+}
+
+static void init_peripheral_ep(struct phytium_cusb *config,
+		struct phytium_ep *phy_ep, struct GADGET_EP *gadget_ep, int is_tx)
+{
+	if (!config || !phy_ep || !gadget_ep)
+		return;
+
+	memset(phy_ep, 0, sizeof(*phy_ep));
+	phy_ep->config = config;
+	phy_ep->is_tx = is_tx;
+	phy_ep->gadget_ep = gadget_ep;
+	phy_ep->ep_num = gadget_ep->address & 0xF;
+	phy_ep->end_point.maxpacket = gadget_ep->maxPacket;
+	phy_ep->end_point.maxpacket_limit = 1024;
+
+	INIT_LIST_HEAD(&phy_ep->req_list);
+	sprintf(phy_ep->name, "ep%d%s", phy_ep->ep_num, is_tx ? "in" : "out");
+
+	switch (phy_ep->ep_num) {
+	case 0:
+		phy_ep->end_point.caps.type_control = 1;
+		break;
+	case 1:
+		phy_ep->end_point.caps.type_bulk = 1;
+		break;
+	case 2:
+		phy_ep->end_point.caps.type_int = 1;
+		break;
+	case 3:
+		phy_ep->end_point.caps.type_iso = 1;
+		break;
+	default:
+		phy_ep->end_point.caps.type_int = 1;
+		phy_ep->end_point.caps.type_bulk = 1;
+		break;
+	}
+
+	if (is_tx) {
+		phy_ep->end_point.caps.dir_in = 1;
+		phy_ep->end_point.caps.dir_out = 0;
+	} else {
+		phy_ep->end_point.caps.dir_in = 0;
+		phy_ep->end_point.caps.dir_out = 1;
+	}
+
+	phy_ep->end_point.name = phy_ep->name;
+
+	INIT_LIST_HEAD(&phy_ep->end_point.ep_list);
+
+	if (!phy_ep->ep_num) {
+		phy_ep->end_point.ops = &gadget_ep0_ops;
+		config->gadget.ep0 = &phy_ep->end_point;
+
+		if (config->gadget_dev->maxSpeed > USB_SPEED_HIGH)
+			config->gadget.ep0->maxpacket = 9;
+	} else {
+		phy_ep->end_point.ops = &gadget_ep_ops;
+		list_add_tail(&phy_ep->end_point.ep_list, &config->gadget.ep_list);
+	}
+}
+
+static void gadget_init_endpoint(struct phytium_cusb *config)
+{
+	struct list_head *list;
+	struct GADGET_EP *gadget_ep;
+
+	if (!config)
+		return;
+
+	INIT_LIST_HEAD(&(config->gadget.ep_list));
+
+	init_peripheral_ep(config, &config->endpoints_tx[0], config->gadget_dev->ep0, 1);
+	init_peripheral_ep(config, &config->endpoints_rx[0], config->gadget_dev->ep0, 0);
+
+	list_for_each(list, &config->gadget_dev->epList) {
+		gadget_ep = (struct GADGET_EP *)list;
+		if (gadget_ep->address & USB_DIR_IN)
+			init_peripheral_ep(config, &config->endpoints_tx[gadget_ep->address & 0xf],
+					gadget_ep, 1);
+		else
+			init_peripheral_ep(config, &config->endpoints_rx[gadget_ep->address & 0xf],
+					gadget_ep, 0);
+	}
+}
+
+static int gadget_setup(struct phytium_cusb *config)
+{
+	int ret = -1;
+
+	config->gadget_obj->gadget_getDevInstance(config->gadget_priv, &config->gadget_dev);
+	config->gadget.ops = &phytium_gadget_ops;
+	config->gadget.max_speed = config->gadget_dev->maxSpeed;
+	config->gadget.speed = USB_SPEED_HIGH;
+	config->gadget.name = "phytium_gadget";
+	config->gadget.is_otg = 0;
+
+	gadget_init_endpoint(config);
+
+	ret = usb_add_gadget_udc(config->dev, &config->gadget);
+	if (ret)
+		goto err;
+
+	return 0;
+
+err:
+	config->gadget.dev.parent = NULL;
+	device_unregister(&config->gadget.dev);
+	return ret;
+}
+
+int phytium_gadget_reinit(struct phytium_cusb *config)
+{
+	struct GADGET_CTRL *ctrl;
+
+	if (!config)
+		return 0;
+
+	ctrl = (struct GADGET_CTRL *)config->gadget_priv;
+	if (!ctrl)
+		return 0;
+
+	gadgetStop(ctrl);
+
+	config->gadget_obj->gadget_init(config->gadget_priv, &config->gadget_cfg,
+			&config->gadget_callbacks, config->dev);
+
+	return 0;
+}
+
+int phytium_gadget_init(struct phytium_cusb *config)
+{
+	int ret;
+
+	if (!config)
+		return 0;
+
+	phytium_gadget_set_default_cfg(config);
+	config->gadget_obj = &GadgetObj;
+
+	config->dma_cfg.regBase = config->gadget_cfg.regBase + 0x400;
+	config->dma_obj = DMA_GetInstance();
+	config->dma_obj->dma_probe(&config->dma_cfg, &config->dma_sysreq);
+
+	config->gadget_sysreq.privDataSize = sizeof(struct GADGET_CTRL);
+	config->gadget_sysreq.trbMemSize = config->dma_sysreq.trbMemSize + GADGET_PRIV_BUFFER_SIZE;
+	config->gadget_sysreq.privDataSize += config->dma_sysreq.privDataSize;
+
+	config->gadget_priv = devm_kzalloc(config->dev,
+			config->gadget_sysreq.privDataSize, GFP_KERNEL);
+	if (!config->gadget_priv) {
+		ret = -ENOMEM;
+		goto err_probe;
+	}
+	config->gadget_cfg.trbAddr = dma_alloc_coherent(config->dev,
+			config->gadget_sysreq.trbMemSize,
+			(dma_addr_t *)&config->gadget_cfg.trbDmaAddr, GFP_KERNEL);
+	if (!config->gadget_cfg.trbAddr) {
+		ret = -ENOMEM;
+		goto err_dma_coherent;
+	}
+
+	config->gadget_callbacks.connect		= gadget_callback_connect;
+	config->gadget_callbacks.disconnect		= gadget_callback_disconnect;
+	config->gadget_callbacks.setup			= gadget_callback_setup;
+	config->gadget_callbacks.usbRequestMemAlloc	= gadget_callback_usbRequestMemAlloc;
+	config->gadget_callbacks.usbRequestMemFree	= gadget_callback_usbRequestMemFree;
+
+	ret = config->gadget_obj->gadget_init(config->gadget_priv, &config->gadget_cfg,
+			&config->gadget_callbacks, config->dev);
+	if (ret) {
+		ret = -ENODEV;
+		goto err_init;
+	}
+
+	//dev_set_drvdata(config->dev, config);
+
+	gadget_setup(config);
+
+	return 0;
+
+err_init:
+	dma_free_coherent(config->dev, config->gadget_sysreq.trbMemSize,
+			config->gadget_cfg.trbAddr, config->gadget_cfg.trbDmaAddr);
+err_dma_coherent:
+err_probe:
+	dev_set_drvdata(config->dev, NULL);
+
+	return ret;
+}
+
+int phytium_gadget_uninit(struct phytium_cusb *config)
+{
+	if (config)
+		usb_del_gadget_udc(&config->gadget);
+
+	return 0;
+}
+
+#ifdef CONFIG_PM
+int phytium_gadget_resume(void *priv)
+{
+	struct GADGET_CTRL *ctrl;
+	uint32_t gen_cfg;
+	unsigned long flags = 0;
+	struct phytium_cusb *config = (struct phytium_cusb *)priv;
+
+	if (!config)
+		return 0;
+
+	ctrl = (struct GADGET_CTRL *)config->gadget_priv;
+	if (!ctrl)
+		return 0;
+
+	spin_lock_irqsave(&config->lock, flags);
+	phytium_gadget_reinit(config);
+
+	if (config->gadget_driver) {
+		config->gadget_obj->gadget_start(config->gadget_priv);
+		if (ctrl->phy_regs) {
+			gen_cfg = phytium_read32(&ctrl->phy_regs->gen_cfg);
+			gen_cfg = gen_cfg & (~BIT(7));
+			phytium_write32(&ctrl->phy_regs->gen_cfg, gen_cfg);
+		}
+	}
+	spin_unlock_irqrestore(&config->lock, flags);
+
+	return 0;
+}
+
+int phytium_gadget_suspend(void *priv)
+{
+	return 0;
+}
+#endif
diff --git a/drivers/usb/phytium/gadget.h b/drivers/usb/phytium/gadget.h
new file mode 100644
index 0000000000000..d87b55ade7a70
--- /dev/null
+++ b/drivers/usb/phytium/gadget.h
@@ -0,0 +1,253 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+
+#ifndef __PHYTIUM_GADGET_H_
+#define __PHYTIUM_GADGET_H_
+
+#include <linux/usb/ch9.h>
+#include <linux/usb/gadget.h>
+#include "dma.h"
+
+struct GADGET_CTRL;
+struct GADGET_EP;
+struct GADGET_REQ;
+
+enum GADGET_EP_STATE {
+	GADGET_EP_FREE,
+	GADGET_EP_ALLOCATED,
+	GADGET_EP_BUSY,
+	GADGET_EP_NOT_IMPLEMENTED
+};
+
+enum GADGET_EP0_STAGE {
+	GADGET_EP0_STAGE_SETUP,
+	GADGET_EP0_STAGE_IN,
+	GADGET_EP0_STAGE_OUT,
+	GADGET_EP0_STAGE_STATUSIN,
+	GADGET_EP0_STAGE_STATUSOUT,
+	GADGET_EP0_STAGE_ACK
+};
+
+struct GADGET_EP_OPS {
+	int32_t (*epEnable)(struct GADGET_CTRL *priv, struct GADGET_EP *ep,
+			const struct usb_endpoint_descriptor *desc);
+
+	int32_t (*epDisable)(struct GADGET_CTRL *priv, struct GADGET_EP *ep);
+
+	int32_t (*epSetHalt)(struct GADGET_CTRL *priv, struct GADGET_EP *ep, uint8_t value);
+
+	int32_t (*epSetWedge)(struct GADGET_CTRL *priv, struct GADGET_EP *ep);
+
+	int32_t (*epFifoStatus)(struct GADGET_CTRL *priv, struct GADGET_EP *ep);
+
+	int32_t (*epFifoFlush)(struct GADGET_CTRL *priv, struct GADGET_EP *ep);
+
+	int32_t (*reqQueue)(struct GADGET_CTRL *priv, struct GADGET_EP *ep,
+			struct GADGET_REQ *req);
+
+	int32_t (*reqDequeue)(struct GADGET_CTRL *priv, struct GADGET_EP *ep,
+			struct GADGET_REQ *req);
+
+	int32_t (*reqAlloc)(struct GADGET_CTRL *priv, struct GADGET_EP *ep,
+			struct GADGET_REQ **req);
+
+	void (*reqFree)(struct GADGET_CTRL *priv, struct GADGET_EP *ep,
+			struct GADGET_REQ *req);
+};
+
+struct GADGET_EP {
+	struct list_head epList;
+	char name[255];
+	struct GADGET_EP_OPS *ops;
+	uint16_t maxPacket;
+	uint16_t maxStreams;
+	uint8_t mult;
+	uint8_t maxburst;
+	uint8_t address;
+	const struct usb_endpoint_descriptor *desc;
+	const struct usb_ss_ep_comp_descriptor *compDesc;
+};
+
+enum GADGET_DMAInterfaceWidth {
+	GADGET_DMA_32_WIDTH = 4,
+	GADGET_DMA_64_WIDTH = 8,
+};
+
+struct GADGET_EP_CFG {
+	uint8_t bufferingValue;
+	uint16_t startBuf;
+	uint16_t maxPacketSize;
+};
+
+struct GADGET_CFG {
+	uintptr_t regBase;
+	uintptr_t phy_regBase;
+	struct GADGET_EP_CFG epIN[16];
+	struct GADGET_EP_CFG epOUT[16];
+	enum GADGET_DMAInterfaceWidth dmaInterfaceWidth;
+	void *trbAddr;
+	uintptr_t trbDmaAddr;
+};
+
+struct GADGET_SYSREQ {
+	uint32_t privDataSize;
+	uint32_t trbMemSize;
+};
+
+struct GadgetEp {
+	struct GADGET_EP gadgetEp;
+	enum GADGET_EP_STATE state;
+	uint8_t hwEpNum;
+	uint8_t isInEp;
+	struct list_head request;
+	uint8_t iso_flag;
+	void *channel;
+	uint32_t requestsInList;
+	uint8_t wedged;
+};
+
+struct GADGET_DEV {
+	struct list_head epList;
+	struct GADGET_EP *ep0;
+	unsigned int speed;
+	unsigned int maxSpeed;
+	enum usb_device_state state;
+	uint8_t sgSupported;
+	uint8_t bHnpEnable;
+	uint8_t aHnpSupport;
+	char name[255];
+};
+
+struct GADGET_SgList {
+	uintptr_t link;
+	uint32_t offset;
+	uint32_t length;
+	uintptr_t dmaAddress;
+};
+
+struct GADGET_REQ {
+	struct list_head list;
+	void *buf;
+	uint32_t length;
+	uintptr_t dma;
+	uint32_t numOfSgs;
+	uint32_t numMappedSgs;
+	uint16_t streamId;
+	uint8_t oInterrupt;
+	uint8_t zero;
+	uint8_t shortNotOk;
+	void *context;
+	uint32_t status;
+	uint32_t actual;
+	struct GADGET_SgList *sg;
+	void (*complete)(struct GADGET_EP *ep, struct GADGET_REQ *req);
+};
+
+
+struct GADGET_CALLBACKS {
+	void (*disconnect)(struct GADGET_CTRL *priv);
+
+	void (*connect)(struct GADGET_CTRL *priv);
+
+	int32_t (*setup)(struct GADGET_CTRL *priv,
+			struct usb_ctrlrequest *ctrl);
+
+	void *(*usbRequestMemAlloc)(struct GADGET_CTRL *priv,
+			uint32_t requiredSize);
+
+	void (*usbRequestMemFree)(struct GADGET_CTRL *priv, void *usbRequest);
+};
+
+struct GADGET_OBJ {
+	int32_t (*gadget_init)(struct GADGET_CTRL *priv, struct GADGET_CFG *config,
+			struct GADGET_CALLBACKS *callbacks, struct device *pdev);
+
+	void (*gadget_destroy)(struct GADGET_CTRL *priv);
+
+	void (*gadget_start)(struct GADGET_CTRL *priv);
+
+	void (*gadget_stop)(struct GADGET_CTRL *priv);
+
+	void (*gadget_isr)(struct GADGET_CTRL *priv);
+
+	int32_t (*gadget_epEnable)(struct GADGET_CTRL *priv, struct GADGET_EP *ep,
+			const struct usb_endpoint_descriptor *desc);
+
+	int32_t (*gadget_epDisable)(struct GADGET_CTRL *priv, struct GADGET_EP *ep);
+
+	int32_t (*gadget_epSetHalt)(struct GADGET_CTRL *priv, struct GADGET_EP *ep, uint8_t value);
+
+	int32_t (*gadget_epSetWedge)(struct GADGET_CTRL *priv, struct GADGET_EP *ep);
+
+	int32_t (*gadget_epFifoStatus)(struct GADGET_CTRL *priv, struct GADGET_EP *ep);
+
+	void (*gadget_epFifoFlush)(struct GADGET_CTRL *priv, struct GADGET_EP *ep);
+
+	int32_t (*gadget_reqQueue)(struct GADGET_CTRL *priv, struct GADGET_EP *ep,
+			struct GADGET_REQ *req);
+
+	int32_t (*gadget_reqDequeue)(struct GADGET_CTRL *priv, struct GADGET_EP *ep,
+			struct GADGET_REQ *req);
+
+	int32_t (*gadget_reqAlloc)(struct GADGET_CTRL *priv, struct GADGET_EP *ep,
+			struct GADGET_REQ **req);
+
+	void (*gadget_reqFree)(struct GADGET_CTRL *priv, struct GADGET_EP *ep,
+			struct GADGET_REQ *req);
+
+	void (*gadget_getDevInstance)(struct GADGET_CTRL *priv, struct GADGET_DEV **dev);
+
+	int32_t (*gadget_dGetFrame)(struct GADGET_CTRL *priv, uint32_t *numOfFrame);
+
+	int32_t (*gadget_dWakeUp)(struct GADGET_CTRL *priv);
+
+	int32_t (*gadget_dSetSelfpowered)(struct GADGET_CTRL *priv);
+
+	int32_t (*gadget_dClearSelfpowered)(struct GADGET_CTRL *priv);
+
+	int32_t (*gadget_dVbusSession)(struct GADGET_CTRL *priv, uint8_t isActive);
+
+	int32_t (*gadget_dVbusDraw)(struct GADGET_CTRL *priv, uint8_t mA);
+
+	int32_t (*gadget_dPullUp)(struct GADGET_CTRL *priv, uint8_t isOn);
+
+	void (*gadget_dGetConfigParams)(struct GADGET_CTRL *priv,
+			struct usb_dcd_config_params *configParams);
+};
+
+struct GADGET_CTRL {
+	struct device *dev;
+	struct GADGET_DEV gadgetDev;
+	struct HW_REGS *regs;
+	struct GADGET_OBJ *gadgetDrv;
+	struct GADGET_CFG gadgetCfg;
+	struct GADGET_CALLBACKS eventCallback;
+	struct GadgetEp in[16];
+	struct GadgetEp out[16];
+	enum GADGET_EP0_STAGE ep0State;
+	uint8_t isRemoteWakeup;
+	uint8_t isSelfPowered;
+	uint8_t deviceAddress;
+	struct DMA_OBJ *dmaDrv;
+	void *dmaController;
+	struct DMA_CFG dmaCfg;
+	struct DMA_CALLBACKS dmaCallback;
+	uint8_t releaseEp0Flag;
+	uint8_t isReady;
+	uint8_t *privBuffAddr;
+	uintptr_t privBuffDma;
+	uint8_t endpointInList;
+	uint8_t hostRequestFlag;
+	struct VHUB_REGS *phy_regs;
+};
+
+struct GadgetRequest {
+	struct GADGET_REQ request;
+	struct GadgetEp *ep;
+	struct GADGET_DEV *dev;
+	uint8_t zlp;
+};
+
+struct GADGET_OBJ *GADGET_GetInstance(void);
+
+#endif /* __LINUX_PHYTIUM_GADGET */
+
diff --git a/drivers/usb/phytium/host.c b/drivers/usb/phytium/host.c
new file mode 100644
index 0000000000000..35a104d0c5739
--- /dev/null
+++ b/drivers/usb/phytium/host.c
@@ -0,0 +1,2761 @@
+// SPDX-License-Identifier: GPL-2.0
+
+#include <linux/dma-mapping.h>
+#include <linux/usb.h>
+#include<linux/usb/hcd.h>
+#include <linux/interrupt.h>
+//#include "list.h"
+#include "core.h"
+#include "dma.h"
+#include "hw-regs.h"
+
+#define DRV_NAME "phytium_usb"
+
+#define HOST_GENERIC_EP_CONTROL 0x00
+#define HOST_GENERIC_EP_ISOC 0x01
+#define HOST_GENERIC_EP_BULK 0x02
+#define HOST_GENERIC_EP_INT 0x03
+
+#define HOST_ESTALL	1
+#define HOST_EUNHANDLED	2
+#define HOST_EAUTOACK	3
+#define HOST_ESHUTDOWN	4
+
+#define HOST_EP_NUM	16
+
+static int get_epnum_from_pool(struct HOST_CTRL *priv, int real_epNum, bool dirIn)
+{
+	int index, dir = 0;
+	int ret = 0;
+
+	if (!priv)
+		return 0;
+
+	if (!dirIn)
+		dir = 1;
+
+	if (real_epNum <= MAX_INSTANCE_EP_NUM) {
+		if (!priv->ep_remap_pool[dir][real_epNum]) {
+			priv->ep_remap_pool[dir][real_epNum] = real_epNum;
+			ret = real_epNum;
+			goto out;
+		}
+
+		if (priv->ep_remap_pool[dir][real_epNum] == real_epNum) {
+			ret = real_epNum;
+			goto out;
+		}
+	} else {
+		for (index = 1; index <= MAX_INSTANCE_EP_NUM; index++) {
+			if (priv->ep_remap_pool[dir][index] == real_epNum) {
+				ret = index;
+				goto out;
+			}
+		}
+	}
+
+	for (index = 1; index <= MAX_INSTANCE_EP_NUM; index++) {
+		if (!priv->ep_remap_pool[dir][index]) {
+			priv->ep_remap_pool[dir][index] = real_epNum;
+			ret = index;
+			goto out;
+		}
+	}
+
+out:
+	return ret;
+}
+
+static int release_epnum_from_pool(struct HOST_CTRL *priv, int real_epNum, bool dirIn)
+{
+	int index = 0;
+	int dir = 0;
+
+	if (!priv)
+		return 0;
+
+	if (!dirIn)
+		dir = 1;
+
+	for (index = 1; index <= MAX_INSTANCE_EP_NUM; index++) {
+		if (priv->ep_remap_pool[dir][index] == real_epNum) {
+			priv->ep_remap_pool[dir][index] = 0;
+
+			return 0;
+		}
+	}
+
+	return 0;
+}
+
+static inline struct HOST_REQ *getUsbRequestEntry(struct list_head *list)
+{
+	return (struct HOST_REQ *)((uintptr_t)list - (uintptr_t)&(((struct HOST_REQ *)0)->list));
+}
+
+static inline struct HOST_EP_PRIV *getUsbHEpPrivEntry(struct list_head *list)
+{
+	struct HOST_EP_PRIV *hostEpPriv;
+
+	if (list_empty(list))
+		return NULL;
+
+	hostEpPriv = (struct HOST_EP_PRIV *)((uintptr_t)list -
+			(uintptr_t)&(((struct HOST_EP_PRIV *)0)->node));
+
+	return hostEpPriv;
+}
+
+static struct HOST_REQ *getNextReq(struct HOST_EP *usbEp)
+{
+	struct list_head *queue;
+
+	if (!usbEp)
+		return NULL;
+
+	queue = &usbEp->reqList;
+
+	if (list_empty(queue))
+		return NULL;
+
+	return getUsbRequestEntry(queue->next);
+}
+
+static void host_SetVbus(struct HOST_CTRL *priv, uint8_t isOn)
+{
+	uint8_t otgctrl = phytium_read8(&priv->regs->otgctrl);
+
+	if (isOn) {
+		if (!(otgctrl & OTGCTRL_BUSREQ) || (otgctrl & OTGCTRL_ABUSDROP)) {
+			otgctrl &= ~OTGCTRL_ABUSDROP;
+			otgctrl |= OTGCTRL_BUSREQ;
+			phytium_write8(&priv->regs->otgctrl, otgctrl);
+		}
+		priv->otgState = HOST_OTG_STATE_A_WAIT_BCON;
+	} else {
+		if ((otgctrl & OTGCTRL_BUSREQ) || (otgctrl & OTGCTRL_ABUSDROP)) {
+			otgctrl |= OTGCTRL_ABUSDROP;
+			otgctrl &= ~OTGCTRL_BUSREQ;
+			phytium_write8(&priv->regs->otgctrl, otgctrl);
+		}
+		priv->otgState = HOST_OTG_STATE_A_IDLE;
+	}
+}
+
+static inline void disconnectHostDetect(struct HOST_CTRL *priv)
+{
+	uint8_t otgctrl, otgstate;
+	uint32_t gen_cfg;
+
+	if (!priv)
+		return;
+
+	memset(priv->ep_remap_pool, 0, sizeof(priv->ep_remap_pool));
+	otgctrl = phytium_read8(&priv->regs->otgctrl);
+	if ((otgctrl & OTGCTRL_ASETBHNPEN) && priv->otgState == HOST_OTG_STATE_A_SUSPEND)
+		pr_info("Device no Response\n");
+
+	phytium_write8(&priv->regs->otgirq, OTGIRQ_CONIRQ);
+retry:
+	otgstate = phytium_read8(&priv->regs->otgstate);
+	if ((otgstate == HOST_OTG_STATE_A_HOST || otgstate == HOST_OTG_STATE_B_HOST)) {
+		pr_info("IRQ OTG: DisconnIrq Babble\n");
+		goto retry;
+	}
+
+	phytium_write8(&priv->regs->endprst, ENDPRST_IO_TX);
+	phytium_write8(&priv->regs->endprst, ENDPRST_FIFORST | ENDPRST_TOGRST | ENDPRST_IO_TX);
+	phytium_write8(&priv->regs->endprst, ENDPRST_FIFORST | ENDPRST_TOGRST);
+	phytium_write8(&priv->regs->ep0fifoctrl, FIFOCTRL_FIFOAUTO | 0 | 0x04);
+	phytium_write8(&priv->regs->ep0fifoctrl, FIFOCTRL_FIFOAUTO | FIFOCTRL_IO_TX | 0 | 0x04);
+
+	priv->portStatus = USB_PORT_STAT_POWER;
+	priv->portStatus |= USB_PORT_STAT_C_CONNECTION << 16;
+
+	if (priv->hostCallbacks.portStatusChange)
+		priv->hostCallbacks.portStatusChange(priv);
+
+	if (priv->otgState == HOST_OTG_STATE_A_SUSPEND)
+		host_SetVbus(priv, 1);
+
+	priv->otgState = HOST_OTG_STATE_A_IDLE;
+	if (priv->custom_regs) {
+		phytium_write32(&priv->custom_regs->wakeup, 1);
+	} else {
+		gen_cfg = phytium_read32(&priv->vhub_regs->gen_cfg);
+		gen_cfg |= BIT(1);
+		phytium_write32(&priv->vhub_regs->gen_cfg, gen_cfg);
+	}
+}
+
+static inline void A_IdleDetect(struct HOST_CTRL *priv, uint8_t otgstate)
+{
+	uint8_t otgctrl;
+
+	if (!priv)
+		return;
+
+	phytium_write8(&priv->regs->otgirq, OTGIRQ_IDLEIRQ);
+
+	if (otgstate != HOST_OTG_STATE_A_IDLE) {
+		pr_info("IRQ OTG: A_IDLE Babble\n");
+		return;
+	}
+
+	priv->portStatus = 0;
+	otgctrl = phytium_read8(&priv->regs->otgctrl);
+	otgctrl &= ~OTGCTRL_ASETBHNPEN;
+	phytium_write8(&priv->regs->otgctrl, otgctrl);
+
+	host_SetVbus(priv, 1);
+
+	priv->otgState = HOST_OTG_STATE_A_IDLE;
+}
+
+static inline void B_IdleDetect(struct HOST_CTRL *priv, uint8_t otgstate)
+{
+	uint8_t otgctrl, usbcs;
+
+	if (!priv)
+		return;
+
+	phytium_write8(&priv->regs->otgirq, OTGIRQ_IDLEIRQ);
+
+	if (otgstate != HOST_OTG_STATE_B_IDLE) {
+		pr_info("IRQ OTG: B_IDLE Babble\n");
+		return;
+	}
+
+	otgctrl = phytium_read8(&priv->regs->otgctrl);
+	otgctrl &= ~OTGCTRL_ASETBHNPEN;
+	phytium_write8(&priv->regs->otgctrl, otgctrl);
+
+	host_SetVbus(priv, 0);
+
+	priv->otgState = HOST_OTG_STATE_B_IDLE;
+
+	usbcs = phytium_read8(&priv->regs->usbcs);
+	usbcs &= ~USBCS_DISCON;
+	phytium_write8(&priv->regs->usbcs, usbcs);
+}
+
+static uint32_t waitForBusyBit(struct HOST_CTRL *priv, struct HostEp *hwEp)
+{
+	uint8_t *csReg;
+	uint8_t flag = CS_BUSY;
+	uint8_t buf = 0;
+	uint8_t val = CS_BUSY;
+	uint8_t otgstate;
+	uint8_t bufflag = 0;
+
+	if (!priv || !hwEp)
+		return 0;
+
+	if (hwEp->isInEp)
+		return 0;
+
+	if (hwEp->hwEpNum == 0) {
+		csReg = &priv->regs->ep0cs;
+		flag = EP0CS_TXBUSY_MASK;
+		buf = 0;
+	} else {
+		csReg = &priv->regs->ep[hwEp->hwEpNum - 1].txcs;
+		buf = phytium_read8(&priv->regs->ep[hwEp->hwEpNum - 1].txcon) & CON_BUF;
+	}
+
+	while ((val & flag) || bufflag == 0) {
+		otgstate = phytium_read8(&priv->regs->otgstate);
+		if (otgstate != HOST_OTG_STATE_B_HOST && otgstate != HOST_OTG_STATE_A_HOST) {
+			priv->ep0State = HOST_EP0_STAGE_IDLE;
+			return HOST_ESHUTDOWN;
+		}
+
+		val = phytium_read8(csReg);
+		if (((val & CS_NPAK) >> CS_NPAK_OFFSET) == buf || buf == 0)
+			bufflag = 1;
+		else
+			bufflag = 0;
+	}
+
+	return 0;
+}
+
+static inline void connectHostDetect(struct HOST_CTRL *priv, uint8_t otgState)
+{
+	uint32_t gen_cfg;
+
+	if (!priv)
+		return;
+	pr_debug("otgState:0x%x pirv->otgState:0x%x\n", otgState, priv->otgState);
+	if (priv->custom_regs) {
+		phytium_write32(&priv->custom_regs->wakeup, 0);
+	} else {
+		gen_cfg = phytium_read32(&priv->vhub_regs->gen_cfg);
+		gen_cfg &= ~BIT(1);
+		phytium_write32(&priv->vhub_regs->gen_cfg, gen_cfg);
+	}
+
+	phytium_write8(&priv->regs->otgirq, OTGIRQ_CONIRQ);
+
+	if ((otgState != HOST_OTG_STATE_A_HOST) && (otgState != HOST_OTG_STATE_B_HOST))
+		return;
+
+	if ((priv->otgState == HOST_OTG_STATE_A_PERIPHERAL)
+			|| (priv->otgState == HOST_OTG_STATE_B_PERIPHERAL))
+		priv->otgState = otgState;
+
+	priv->ep0State = HOST_EP0_STAGE_IDLE;
+
+	priv->portStatus &= ~(USB_PORT_STAT_LOW_SPEED | USB_PORT_STAT_HIGH_SPEED |
+			USB_PORT_STAT_ENABLE);
+
+	priv->portStatus |= USB_PORT_STAT_C_CONNECTION | (USB_PORT_STAT_C_CONNECTION << 16);
+	priv->dmaDrv->dma_controllerReset(priv->dmaController);
+	priv->port_resetting = 1;
+	host_SetVbus(priv, 1);
+
+	switch (phytium_read8(&priv->regs->speedctrl)) {
+	case SPEEDCTRL_HS:
+		priv->portStatus |= USB_PORT_STAT_HIGH_SPEED;
+		pr_debug("detect High speed device\n");
+		break;
+	case SPEEDCTRL_FS:
+		priv->portStatus &= ~(USB_PORT_STAT_HIGH_SPEED | USB_PORT_STAT_LOW_SPEED);
+		pr_debug("detect Full speed device\n");
+		break;
+	case SPEEDCTRL_LS:
+		priv->portStatus |= USB_PORT_STAT_LOW_SPEED;
+		pr_debug("detect Low speed device\n");
+		break;
+	}
+
+	priv->vBusErrCnt = 0;
+	priv->dmaDrv->dma_setHostMode(priv->dmaController);
+
+	if (priv->hostCallbacks.portStatusChange)
+		priv->hostCallbacks.portStatusChange(priv);
+
+	priv->otgState = otgState;
+}
+
+static void hostOtgIrq(struct HOST_CTRL *priv)
+{
+	uint8_t otgirq, otgien;
+	uint8_t otgstatus, otgstate;
+	uint8_t otgctrl;
+
+	if (!priv)
+		return;
+
+	otgirq = phytium_read8(&priv->regs->otgirq);
+	otgien = phytium_read8(&priv->regs->otgien);
+	otgstatus = phytium_read8(&priv->regs->otgstatus);
+	otgstate = phytium_read8(&priv->regs->otgstate);
+	otgirq &= otgien;
+
+	if (!otgirq)
+		return;
+
+	if (otgirq & OTGIRQ_BSE0SRPIRQ) {
+		otgirq &= ~OTGIRQ_BSE0SRPIRQ;
+		phytium_write8(&priv->regs->otgirq, OTGIRQ_BSE0SRPIRQ);
+
+		otgctrl = phytium_read8(&priv->regs->otgctrl);
+		otgctrl &= ~OTGIRQ_BSE0SRPIRQ;
+		phytium_write8(&priv->regs->otgctrl, otgctrl);
+	}
+
+	if (otgirq & OTGIRQ_SRPDETIRQ) {
+		otgirq &= ~OTGIRQ_SRPDETIRQ;
+		phytium_write8(&priv->regs->otgirq, OTGIRQ_SRPDETIRQ);
+
+		otgctrl = phytium_read8(&priv->regs->otgctrl);
+		otgctrl &= ~OTGIRQ_SRPDETIRQ;
+		phytium_write8(&priv->regs->otgctrl, otgctrl);
+	}
+
+	if (otgirq & OTGIRQ_VBUSERRIRQ) {
+		otgirq &= ~OTGIRQ_VBUSERRIRQ;
+		phytium_write8(&priv->regs->otgirq, OTGIRQ_VBUSERRIRQ);
+
+		if (otgstate != HOST_OTG_STATE_A_VBUS_ERR) {
+			pr_info("IRQ OTG: VBUS ERROR Babble\n");
+			return;
+		}
+
+		host_SetVbus(priv, 0);
+		priv->otgState = HOST_OTG_STATE_A_VBUS_ERR;
+		if (priv->portStatus & USB_PORT_STAT_CONNECTION) {
+			priv->portStatus = USB_PORT_STAT_POWER | (USB_PORT_STAT_C_CONNECTION << 16);
+			if (priv->hostCallbacks.portStatusChange)
+				priv->hostCallbacks.portStatusChange(priv);
+			return;
+		}
+
+		if (priv->vBusErrCnt >= 3) {
+			priv->vBusErrCnt = 0;
+			pr_info("%s %d VBUS OVER CURRENT\n", __func__, __LINE__);
+			priv->portStatus |= USB_PORT_STAT_OVERCURRENT |
+				(USB_PORT_STAT_C_OVERCURRENT << 16);
+
+			phytium_write8(&priv->regs->otgirq, OTGIRQ_IDLEIRQ);
+		} else {
+			priv->vBusErrCnt++;
+			host_SetVbus(priv, 1);
+			phytium_write8(&priv->regs->otgirq, OTGIRQ_IDLEIRQ);
+		}
+	}
+
+	if (otgirq & OTGIRQ_CONIRQ) {
+		if (priv->otgState == HOST_OTG_STATE_A_HOST ||
+				priv->otgState == HOST_OTG_STATE_B_HOST ||
+				priv->otgState == HOST_OTG_STATE_A_SUSPEND) {
+			if (otgstate == HOST_OTG_STATE_A_WAIT_VFALL ||
+					otgstate == HOST_OTG_STATE_A_WAIT_BCON ||
+					otgstate == HOST_OTG_STATE_A_SUSPEND)
+				disconnectHostDetect(priv);
+		} else if (priv->otgState != HOST_OTG_STATE_A_HOST &&
+				priv->otgState != HOST_OTG_STATE_B_HOST &&
+				priv->otgState != HOST_OTG_STATE_A_SUSPEND)
+			connectHostDetect(priv, otgstate);
+
+		phytium_write8(&priv->regs->otgirq, OTGIRQ_CONIRQ);
+	}
+
+	if (otgirq & OTGIRQ_IDLEIRQ) {
+		if (!(otgstatus & OTGSTATUS_ID))
+			A_IdleDetect(priv, otgstate);
+		else
+			B_IdleDetect(priv, otgstate);
+	}
+
+	phytium_write8(&priv->regs->otgirq, OTGIRQ_IDCHANGEIRQ |
+			OTGIRQ_SRPDETIRQ);
+}
+
+static void hostErrorIrq(struct HOST_CTRL *priv)
+{
+	uint16_t txerrirq, txerrien;
+	uint16_t rxerrirq, rxerrien;
+	uint16_t i, mask;
+
+	if (!priv)
+		return;
+
+	txerrirq = phytium_read16(&priv->regs->txerrirq);
+	txerrien = phytium_read16(&priv->regs->txerrien);
+	txerrirq &= txerrien;
+
+	rxerrirq = phytium_read16(&priv->regs->rxerrirq);
+	rxerrien = phytium_read16(&priv->regs->rxerrien);
+	rxerrirq &= rxerrirq;
+	if (!txerrirq && !rxerrirq)
+		return;
+
+	for (i = 0; i < HOST_EP_NUM; i++) {
+		mask = 1 << i;
+		if (rxerrirq & mask) {
+			phytium_write16(&priv->regs->rxerrirq, mask);
+			rxerrien &= ~mask;
+			phytium_write16(&priv->regs->rxerrien, rxerrien);
+			priv->dmaDrv->dma_errIsr(priv->dmaController, i, 0);
+		}
+
+		if (txerrirq & mask) {
+			phytium_write16(&priv->regs->txerrirq, mask);
+			txerrien &= ~mask;
+			phytium_write16(&priv->regs->txerrien, txerrien);
+			priv->dmaDrv->dma_errIsr(priv->dmaController, i, 1);
+		}
+	}
+}
+
+static uint32_t decodeErrorCode(uint8_t code)
+{
+	uint32_t status = 0;
+
+	switch (code) {
+	case ERR_NONE:
+		status = 0;
+		break;
+	case ERR_CRC:
+		pr_info("CRC Error\n");
+		status = HOST_ESHUTDOWN;
+		break;
+	case ERR_DATA_TOGGLE_MISMATCH:
+		pr_info("Toggle MisMatch Error\n");
+		status = HOST_ESHUTDOWN;
+		break;
+	case ERR_STALL:
+		pr_debug("Stall Error\n");
+		status = HOST_ESTALL;
+		break;
+	case ERR_TIMEOUT:
+		pr_debug("Timeout Error\n");
+		status = HOST_ESHUTDOWN;
+		break;
+	case ERR_PID:
+		pr_info("PID Error\n");
+		status = HOST_ESHUTDOWN;
+		break;
+	case ERR_TOO_LONG_PACKET:
+		pr_info("TOO_LONG_PACKET Error\n");
+		status = HOST_ESHUTDOWN;
+		break;
+	case ERR_DATA_UNDERRUN:
+		pr_info("UNDERRUN Error\n");
+		status = HOST_ESHUTDOWN;
+		break;
+	}
+
+	return status;
+}
+
+static struct HOST_EP_PRIV *getIntTransfer(struct list_head *head)
+{
+	struct list_head *listEntry = NULL;
+	struct HOST_EP_PRIV *usbHEpPriv = NULL;
+	struct HOST_EP_PRIV *usbHEpPrivActual = NULL;
+
+	list_for_each(listEntry, head) {
+		usbHEpPriv = getUsbHEpPrivEntry(listEntry);
+		if (!usbHEpPrivActual)
+			usbHEpPrivActual = usbHEpPriv;
+
+		if (usbHEpPriv->frame < usbHEpPrivActual->frame)
+			usbHEpPrivActual = usbHEpPriv;
+	}
+
+	return usbHEpPrivActual;
+}
+
+static void givebackRequest(struct HOST_CTRL *priv, struct HOST_REQ *usbReq, uint32_t status)
+{
+	if (!priv || !usbReq)
+		return;
+
+	list_del(&usbReq->list);
+
+	if (usbReq->status == EINPROGRESS)
+		usbReq->status = status;
+
+	if (priv->hostCallbacks.givebackRequest)
+		priv->hostCallbacks.givebackRequest(priv, usbReq, status);
+}
+
+static void hostEpProgram(struct HOST_CTRL *priv, struct HostEp *hwEp,
+		struct HOST_REQ *usbReq, uintptr_t dmaBuff, uint32_t length)
+{
+	struct HOST_EP *usbHEp;
+	struct HOST_EP_PRIV *usbEpPriv;
+	uint32_t chMaxLen;
+	uint8_t regCon = 0;
+	uint8_t ep0cs;
+	uint16_t txerrien = 0;
+	uint16_t rxerrien = 0;
+	uint32_t result;
+	uint8_t txsoftimer, rxsoftimer;
+	u8 retval = 0;
+
+	if (!priv || !hwEp || !usbReq)
+		return;
+
+	usbHEp = hwEp->scheduledUsbHEp;
+	usbEpPriv = (struct HOST_EP_PRIV *)usbHEp->hcPriv;
+
+	if (!hwEp->channel) {
+		if (usbEpPriv->type == USB_ENDPOINT_XFER_ISOC)
+			hwEp->channel = priv->dmaDrv->dma_channelAlloc(priv->dmaController,
+					!hwEp->isInEp, hwEp->hwEpNum, 1);
+		else
+			hwEp->channel = priv->dmaDrv->dma_channelAlloc(priv->dmaController,
+					!hwEp->isInEp, hwEp->hwEpNum, 0);
+	}
+
+	chMaxLen = priv->dmaDrv->dma_getMaxLength(priv->dmaController, hwEp->channel);
+
+	pr_debug("chMaxLen:0x%x buffLength:0x%x\n", chMaxLen, usbReq->buffLength);
+	if (usbReq->buffLength > chMaxLen)
+		length = chMaxLen;
+
+	switch (usbEpPriv->type) {
+	case USB_ENDPOINT_XFER_CONTROL:
+		regCon = CON_TYPE_CONTROL;
+		break;
+	case USB_ENDPOINT_XFER_BULK:
+		regCon = CON_TYPE_BULK;
+		break;
+	case USB_ENDPOINT_XFER_INT:
+		regCon = CON_TYPE_INT;
+		break;
+	case USB_ENDPOINT_XFER_ISOC:
+		if (usbEpPriv->isocEpConfigured)
+			goto dma_program;
+
+		usbEpPriv->isocEpConfigured = 1;
+		regCon = CON_TYPE_ISOC;
+		switch (usbHEp->desc.wMaxPacketSize >> 11) {
+		case 0:
+			regCon |= CON_TYPE_ISOC_1_ISOD;
+			priv->dmaDrv->dma_setMaxLength(priv->dmaController,
+					hwEp->channel, usbEpPriv->maxPacketSize);
+			break;
+		case 1:
+			regCon |= CON_TYPE_ISOC_2_ISOD;
+			priv->dmaDrv->dma_setMaxLength(priv->dmaController,
+					hwEp->channel, 2 * 1024);
+			break;
+		case 2:
+			priv->dmaDrv->dma_setMaxLength(priv->dmaController,
+					hwEp->channel, 3 * 1024);
+			regCon |= CON_TYPE_ISOC_3_ISOD;
+			break;
+		}
+		break;
+	}
+
+	if (usbEpPriv->type != USB_ENDPOINT_XFER_ISOC) {
+		if (!hwEp->hwEpNum) {
+			if (phytium_read8(&priv->regs->ep0cs) & 0x4) {
+				phytium_write8(&priv->regs->ep0fifoctrl, FIFOCTRL_FIFOAUTO |
+					0 | 0x4);
+				phytium_write8(&priv->regs->ep0fifoctrl, FIFOCTRL_FIFOAUTO |
+					FIFOCTRL_IO_TX | 0 | 0x4);
+			}
+		}
+		if (waitForBusyBit(priv, hwEp) > 0) {
+			usbReq->status = HOST_ESHUTDOWN;
+			givebackRequest(priv, usbReq, HOST_ESHUTDOWN);
+			pr_info("something error happen\n");
+			return;
+		}
+	}
+
+	if (!hwEp->isInEp) {
+		if (hwEp->hwEpNum) {
+			regCon |= hwEp->hwBuffers - 1;
+			phytium_write8(&priv->regs->ep[hwEp->hwEpNum - 1].txcon, regCon);
+			if (usbEpPriv->type != USB_ENDPOINT_XFER_ISOC) {
+				retval = priv->hostCallbacks.getEpToggle(priv,
+						usbReq->usbDev, usbHEp->device_epNum, 0);
+				if (retval) {
+					phytium_write8(&priv->regs->endprst, hwEp->hwEpNum |
+						ENDPRST_IO_TX);
+
+					phytium_write8(&priv->regs->endprst, hwEp->hwEpNum |
+						ENDPRST_TOGSETQ | ENDPRST_IO_TX | ENDPRST_FIFORST);
+				} else {
+					phytium_write8(&priv->regs->endprst, hwEp->hwEpNum |
+						ENDPRST_IO_TX);
+
+					phytium_write8(&priv->regs->endprst, hwEp->hwEpNum |
+						ENDPRST_TOGRST | ENDPRST_IO_TX | ENDPRST_FIFORST);
+				}
+			}
+
+			phytium_write8(&priv->regs->ep[hwEp->hwEpNum - 1].txcon, regCon | CON_VAL);
+			phytium_write16(&priv->regs->txmaxpack[hwEp->hwEpNum - 1],
+					usbEpPriv->maxPacketSize);
+
+			phytium_write8(&priv->regs->epExt[hwEp->hwEpNum - 1].txctrl,
+					usbHEp->device_epNum);
+
+			phytium_write8(&priv->regs->fnaddr, usbEpPriv->faddress);
+
+			txsoftimer = phytium_read8(&priv->regs->txsoftimer[hwEp->hwEpNum].ctrl);
+			txsoftimer = txsoftimer | BIT(1);
+			phytium_write8(&priv->regs->txsoftimer[hwEp->hwEpNum].ctrl, txsoftimer);
+
+			phytium_write16(&priv->regs->txsoftimer[hwEp->hwEpNum].timer,
+					usbEpPriv->frame);
+
+			phytium_write8(&priv->regs->txsoftimer[hwEp->hwEpNum].ctrl, 0x83);
+		} else {
+			phytium_write8(&priv->regs->fnaddr, usbEpPriv->faddress);
+			phytium_write8(&priv->regs->ep0maxpack, usbEpPriv->maxPacketSize);
+			phytium_write8(&priv->regs->ep0ctrl, usbEpPriv->epNum);
+
+			if (priv->ep0State == HOST_EP0_STAGE_SETUP) {
+				ep0cs = phytium_read8(&priv->regs->ep0cs);
+				ep0cs |= EP0CS_HCSET;
+				phytium_write8(&priv->regs->ep0cs, ep0cs);
+			}
+		}
+
+		phytium_write16(&priv->regs->txerrirq, 1 << hwEp->hwEpNum);
+		txerrien = phytium_read16(&priv->regs->txerrien);
+		txerrien |= 1 << hwEp->hwEpNum;
+		phytium_write16(&priv->regs->txerrien, txerrien);
+	} else {
+		if (hwEp->hwEpNum) {
+			regCon |= hwEp->hwBuffers - 1;
+			phytium_write8(&priv->regs->ep[hwEp->hwEpNum - 1].rxcon, regCon);
+
+			if (usbEpPriv->type != USB_ENDPOINT_XFER_ISOC) {
+				if (priv->hostCallbacks.getEpToggle) {
+					retval = priv->hostCallbacks.getEpToggle(priv,
+							usbReq->usbDev, usbHEp->device_epNum, 1);
+					if (retval) {
+						phytium_write8(&priv->regs->endprst, hwEp->hwEpNum);
+						phytium_write8(&priv->regs->endprst, hwEp->hwEpNum |
+								ENDPRST_TOGSETQ | ENDPRST_FIFORST);
+					} else {
+						phytium_write8(&priv->regs->endprst, hwEp->hwEpNum);
+						phytium_write8(&priv->regs->endprst, hwEp->hwEpNum |
+								ENDPRST_TOGRST | ENDPRST_FIFORST);
+					}
+				}
+			}
+
+			phytium_write16(&priv->regs->rxmaxpack[hwEp->hwEpNum - 1],
+					usbEpPriv->maxPacketSize);
+
+			phytium_write8(&priv->regs->epExt[hwEp->hwEpNum - 1].rxctrl,
+					usbHEp->device_epNum);
+
+			phytium_write8(&priv->regs->fnaddr, usbEpPriv->faddress);
+
+			phytium_write8(&priv->regs->ep[hwEp->hwEpNum - 1].rxcs, 1);
+			phytium_write8(&priv->regs->ep[hwEp->hwEpNum - 1].rxcs, 1);
+			phytium_write8(&priv->regs->ep[hwEp->hwEpNum - 1].rxcs, 1);
+			phytium_write8(&priv->regs->ep[hwEp->hwEpNum - 1].rxcs, 1);
+
+			phytium_write8(&priv->regs->ep[hwEp->hwEpNum - 1].rxcon, regCon | CON_VAL);
+			rxsoftimer = phytium_read8(&priv->regs->rxsoftimer[hwEp->hwEpNum].ctrl);
+			rxsoftimer = rxsoftimer | BIT(1);
+			phytium_write8(&priv->regs->rxsoftimer[hwEp->hwEpNum].ctrl, rxsoftimer);
+
+			phytium_write16(&priv->regs->rxsoftimer[hwEp->hwEpNum].timer,
+					usbEpPriv->frame);
+
+			phytium_write8(&priv->regs->rxsoftimer[hwEp->hwEpNum].ctrl, 0x83);
+		} else {
+			phytium_write8(&priv->regs->fnaddr, usbEpPriv->faddress);
+			phytium_write8(&priv->regs->ep0maxpack, usbEpPriv->maxPacketSize);
+			phytium_write8(&priv->regs->ep0ctrl, usbEpPriv->epNum);
+
+			if (priv->ep0State == HOST_EP0_STAGE_IN
+					|| priv->ep0State == HOST_EP0_STAGE_STATUSIN)
+				phytium_write8(&priv->regs->ep0cs, EP0CS_HCSETTOGGLE);
+		}
+
+		phytium_write16(&priv->regs->rxerrirq, 1 << hwEp->hwEpNum);
+		rxerrien = phytium_read16(&priv->regs->rxerrien);
+		rxerrien |= 1 << hwEp->hwEpNum;
+		phytium_write16(&priv->regs->rxerrien, rxerrien);
+	}
+dma_program:
+	result = priv->dmaDrv->dma_channelProgram(priv->dmaController, hwEp->channel,
+			usbEpPriv->maxPacketSize, dmaBuff, length,
+			(void *)usbReq->isoFramesDesc, usbReq->isoFramesNumber);
+	if (result) {
+		if (!hwEp->isInEp) {
+			txerrien &= ~(1 << hwEp->hwEpNum);
+			if (hwEp->hwEpNum)
+				phytium_write8(&priv->regs->ep[hwEp->hwEpNum - 1].txcon, 0x08);
+			phytium_write16(&priv->regs->txerrien, txerrien);
+		} else {
+			rxerrien &= ~(1 << hwEp->hwEpNum);
+			if (hwEp->hwEpNum)
+				phytium_write8(&priv->regs->ep[hwEp->hwEpNum - 1].rxcon, 0x08);
+			phytium_write16(&priv->regs->rxerrien, rxerrien);
+		}
+
+		priv->dmaDrv->dma_channelRelease(priv->dmaController, hwEp->channel);
+		hwEp->channel = NULL;
+	}
+}
+
+static void hostStartReq(struct HOST_CTRL *priv, struct HOST_REQ *req)
+{
+	uintptr_t dmaBuff;
+	uint32_t length;
+	struct HOST_EP *hostEp;
+	struct HOST_EP_PRIV *hostEpPriv;
+	struct HOST_EP_PRIV *hostNewEpPriv;
+	struct HOST_REQ *usbReq = NULL;
+	struct HostEp *hwEp = NULL;
+	int num;
+
+	if (!priv || !req)
+		return;
+
+	hostEp = req->hcPriv;
+	if (hostEp) {
+		hostEpPriv = (struct HOST_EP_PRIV *)hostEp->hcPriv;
+		if (hostEpPriv) {
+			hostEpPriv->genericHwEp->state = HOST_EP_BUSY;
+			switch (hostEpPriv->type) {
+			case USB_ENDPOINT_XFER_CONTROL:
+				usbReq = getNextReq(hostEp);
+
+				priv->in[HOST_GENERIC_EP_CONTROL].scheduledUsbHEp = hostEp;
+				priv->ep0State = HOST_EP0_STAGE_SETUP;
+				hostEpPriv->currentHwEp = hostEpPriv->genericHwEp;
+				hostEpPriv->genericHwEp->scheduledUsbHEp = hostEp;
+				dmaBuff = usbReq->setupDma;
+				length = 8;
+				pr_debug("packet info: %02x %02x %04x %04x %04x\n",
+					usbReq->setup->bRequestType,
+					usbReq->setup->bRequest,
+					usbReq->setup->wValue,
+					usbReq->setup->wIndex,
+					usbReq->setup->wLength);
+				hostEpProgram(priv, hostEpPriv->genericHwEp,
+						usbReq, dmaBuff, length);
+				break;
+			case USB_ENDPOINT_XFER_BULK:
+				hwEp = hostEpPriv->genericHwEp;
+				usbReq = getNextReq(hostEp);
+				hostEpPriv->currentHwEp = hwEp;
+				hwEp->scheduledUsbHEp = hostEp;
+				dmaBuff = usbReq->buffDma + usbReq->actualLength;
+				length = usbReq->buffLength - usbReq->actualLength;
+				hostEpProgram(priv, hwEp, usbReq, dmaBuff, length);
+				break;
+			case USB_ENDPOINT_XFER_INT:
+				if (hostEpPriv->genericHwEp->scheduledUsbHEp)
+					return;
+
+				num = req->epNum - 1;
+				if (hostEpPriv->isIn)
+					hostNewEpPriv = getIntTransfer(&priv->intInHEpQueue[num]);
+				else
+					hostNewEpPriv = getIntTransfer(&priv->intOutHEpQueue[num]);
+
+				hostNewEpPriv->currentHwEp = hostEpPriv->genericHwEp;
+				hostEpPriv->genericHwEp->scheduledUsbHEp = hostNewEpPriv->usbHEp;
+				usbReq = getNextReq(hostEp);
+				dmaBuff = usbReq->buffDma + usbReq->actualLength;
+				length = usbReq->buffLength - usbReq->actualLength;
+				hostEpProgram(priv, hostEpPriv->genericHwEp,
+						usbReq, dmaBuff, length);
+				break;
+			case USB_ENDPOINT_XFER_ISOC:
+				hostEpPriv->currentHwEp = hostEpPriv->genericHwEp;
+				hostEpPriv->genericHwEp->scheduledUsbHEp = hostEp;
+				dmaBuff = req->buffDma + req->actualLength;
+				length = req->buffLength - req->actualLength;
+				hostEpProgram(priv, hostEpPriv->genericHwEp, req, dmaBuff, length);
+				break;
+			}
+		}
+	}
+}
+
+static void abortTransfer(struct HOST_CTRL *priv,
+		struct HOST_REQ *usbReq, struct HostEp *hwEp)
+{
+	struct HOST_EP *usbEp;
+	struct HOST_EP_PRIV *usbHEpPriv;
+	uint32_t status;
+
+	if (!priv || !usbReq || !hwEp || !hwEp->scheduledUsbHEp)
+		return;
+
+	usbEp = hwEp->scheduledUsbHEp;
+	usbHEpPriv = (struct HOST_EP_PRIV *)usbEp->hcPriv;
+	if (!usbHEpPriv)
+		return;
+
+	status = (usbReq->status == EINPROGRESS) ? 0 : usbReq->status;
+	givebackRequest(priv, usbReq, status);
+
+	if (list_empty(&usbEp->reqList)) {
+		usbHEpPriv->epIsReady = 0;
+		usbHEpPriv->currentHwEp = NULL;
+		hwEp->scheduledUsbHEp = NULL;
+
+		if (hwEp->channel) {
+			priv->dmaDrv->dma_channelRelease(priv->dmaController, hwEp->channel);
+			hwEp->channel = NULL;
+		}
+
+		if (usb_endpoint_xfer_int(&usbEp->desc))
+			list_del(&usbHEpPriv->node);
+	}
+}
+
+static void scheduleNextTransfer(struct HOST_CTRL *priv,
+		struct HOST_REQ *usbReq, struct HostEp *hwEp)
+{
+	struct HOST_EP *usbEp;
+	struct HOST_EP_PRIV *usbHEpPriv;
+	uint8_t endprst;
+	uint32_t status;
+	struct HOST_REQ *usbNextReq = NULL;
+
+	if (!priv || !usbReq || !hwEp)
+		return;
+
+	usbEp = hwEp->scheduledUsbHEp;
+	usbHEpPriv = (struct HOST_EP_PRIV *)usbEp->hcPriv;
+	if (!usbHEpPriv)
+		return;
+
+	status = (usbReq->status == EINPROGRESS) ? 0 : usbReq->status;
+	switch (usbHEpPriv->type) {
+	case USB_ENDPOINT_XFER_BULK:
+	case USB_ENDPOINT_XFER_INT:
+		if (hwEp->isInEp) {
+			phytium_write8(&priv->regs->endprst, hwEp->hwEpNum);
+			endprst = (phytium_read8(&priv->regs->endprst) & ENDPRST_TOGSETQ) ? 1 : 0;
+			if (priv->hostCallbacks.setEpToggle)
+				priv->hostCallbacks.setEpToggle(priv, usbReq->usbDev,
+						usbEp->device_epNum, usbHEpPriv->isIn, endprst);
+		} else {
+			if (waitForBusyBit(priv, hwEp) > 0) {
+				usbReq->status = HOST_ESHUTDOWN;
+				givebackRequest(priv, usbReq, HOST_ESHUTDOWN);
+				return;
+			}
+
+			phytium_write8(&priv->regs->endprst, hwEp->hwEpNum | ENDPRST_IO_TX);
+			endprst = (phytium_read8(&priv->regs->endprst) & ENDPRST_TOGSETQ) ? 1 : 0;
+			if (priv->hostCallbacks.setEpToggle)
+				priv->hostCallbacks.setEpToggle(priv, usbReq->usbDev,
+						usbEp->device_epNum, usbHEpPriv->isIn, endprst);
+		}
+		break;
+	}
+
+	if (usbHEpPriv->transferFinished)
+		givebackRequest(priv, usbReq, status);
+
+	if (list_empty(&usbEp->reqList)) {
+		if (usbHEpPriv->type == USB_ENDPOINT_XFER_CONTROL)
+			hwEp->state = HOST_EP_ALLOCATED;
+		else {
+			if (usbHEpPriv->genericHwEp == hwEp)
+				hwEp->state = HOST_EP_ALLOCATED;
+			else
+				hwEp->state = HOST_EP_FREE;
+		}
+
+		usbHEpPriv->epIsReady = 0;
+		usbHEpPriv->currentHwEp = NULL;
+		hwEp->scheduledUsbHEp = NULL;
+
+		if (hwEp->channel) {
+			priv->dmaDrv->dma_channelRelease(priv->dmaController, hwEp->channel);
+			hwEp->channel = NULL;
+		}
+
+		if (usb_endpoint_xfer_int(&usbEp->desc))
+			list_del(&usbHEpPriv->node);
+		usbHEpPriv = NULL;
+	} else {
+		if (usbHEpPriv->type == USB_ENDPOINT_XFER_INT) {
+			usbHEpPriv->currentHwEp = NULL;
+			hwEp->scheduledUsbHEp = NULL;
+		}
+
+		if (usbHEpPriv->type == USB_ENDPOINT_XFER_ISOC)
+			return;
+	}
+
+	if (usbHEpPriv) {
+		usbNextReq = getNextReq(usbHEpPriv->usbHEp);
+		hostStartReq(priv, usbNextReq);
+	}
+}
+
+static int32_t hostEp0Irq(struct HOST_CTRL *priv, uint8_t isIn)
+{
+	struct HostEp *hwEp;
+	struct HOST_EP *hostEp;
+	struct HOST_REQ *usbReq = NULL;
+	struct HOST_EP_PRIV *usbHEpPriv;
+	uint32_t status, length;
+	uint8_t usbError;
+	uint8_t nextStage = 0;
+	int ret = 0;
+
+	if (!priv)
+		return 0;
+
+	hwEp = isIn ? &priv->in[HOST_GENERIC_EP_CONTROL] : &priv->out[HOST_GENERIC_EP_CONTROL];
+	hostEp = hwEp->scheduledUsbHEp;
+	usbHEpPriv = (struct HOST_EP_PRIV *)hostEp->hcPriv;
+
+	usbReq = getNextReq(hostEp);
+	if (!usbReq)
+		return 0;
+
+	usbError = isIn ? phytium_read8(&priv->regs->rx0err) : phytium_read8(&priv->regs->tx0err);
+	usbError = (usbError & ERR_TYPE) >> 2;
+	status = decodeErrorCode(usbError);
+	if (status) {
+		usbReq->status = status;
+
+		if (status == HOST_ESTALL) {
+			ret = 1;
+			priv->dmaDrv->dma_controllerReset(priv->dmaController);
+		}
+
+		phytium_write16(&priv->regs->rxerrirq, 1 << hwEp->hwEpNum);
+		phytium_write16(&priv->regs->txerrirq, 1 << hwEp->hwEpNum);
+
+		phytium_write8(&priv->regs->ep0fifoctrl, FIFOCTRL_FIFOAUTO | 0 | 0x4);
+		phytium_write8(&priv->regs->ep0fifoctrl, FIFOCTRL_FIFOAUTO |
+				FIFOCTRL_IO_TX | 0 | 0x4);
+	}
+
+	length = priv->dmaDrv->dma_getActualLength(priv->dmaController, hwEp->channel);
+	priv->dmaDrv->dma_channelRelease(priv->dmaController, hwEp->channel);
+	hwEp->channel = NULL;
+
+	if (usbReq->status == EINPROGRESS && priv->ep0State < HOST_EP0_STAGE_STATUSIN) {
+		nextStage = 0;
+		switch (priv->ep0State) {
+		case HOST_EP0_STAGE_IN:
+			pr_debug("Ep0 Data IN\n");
+			usbHEpPriv->currentHwEp = &priv->out[HOST_GENERIC_EP_CONTROL];
+			usbReq->actualLength = length;
+			priv->ep0State = HOST_EP0_STAGE_STATUSOUT;
+			break;
+		case HOST_EP0_STAGE_OUT:
+			pr_debug("Ep0 Data OUT\n");
+			usbHEpPriv->currentHwEp = &priv->in[HOST_GENERIC_EP_CONTROL];
+			usbReq->actualLength = length;
+			priv->ep0State = HOST_EP0_STAGE_STATUSIN;
+			break;
+		case HOST_EP0_STAGE_SETUP:
+			pr_debug("Ep0 Stage Setup\n");
+			if (!usbReq->setup->wLength) {
+				pr_debug("EP0_STAGE_STATUSIN\n");
+				priv->ep0State = HOST_EP0_STAGE_STATUSIN;
+				usbHEpPriv->currentHwEp = &priv->in[HOST_GENERIC_EP_CONTROL];
+				break;
+			} else if (usbReq->setup->bRequestType & USB_DIR_IN) {
+				pr_debug("EP0_STAGE_STAGE_IN\n");
+				priv->ep0State = HOST_EP0_STAGE_IN;
+				usbHEpPriv->currentHwEp = &priv->in[HOST_GENERIC_EP_CONTROL];
+				nextStage = 1;
+				break;
+			}
+			priv->ep0State = HOST_EP0_STAGE_OUT;
+			nextStage = 1;
+			break;
+		case HOST_EP0_STAGE_STATUSIN:
+		case HOST_EP0_STAGE_STATUSOUT:
+		case HOST_EP0_STAGE_ACK:
+		case HOST_EP0_STAGE_IDLE:
+		default:
+			pr_debug("EP0 STAGE is %d\n", priv->ep0State);
+			break;
+		}
+
+		if (nextStage) {
+			length = usbReq->buffLength;
+			hostEpProgram(priv, usbHEpPriv->currentHwEp, usbReq,
+					usbReq->buffDma, length);
+		} else
+			hostEpProgram(priv, usbHEpPriv->currentHwEp, usbReq, usbReq->setupDma, 0);
+	} else
+		priv->ep0State = HOST_EP0_STAGE_IDLE;
+
+	if (priv->ep0State == HOST_EP0_STAGE_IDLE || usbReq->status != EINPROGRESS) {
+		usbHEpPriv->transferFinished = 1;
+		scheduleNextTransfer(priv, usbReq, hwEp);
+	}
+
+	return ret;
+}
+
+static void updateTimeIntTransfer(struct list_head *head, struct HOST_EP_PRIV *lastFinished)
+{
+	struct list_head *listEntry = NULL;
+	struct HOST_EP_PRIV *usbHEpPriv = NULL;
+	uint16_t time = lastFinished->frame;
+
+	list_for_each(listEntry, head) {
+		usbHEpPriv = getUsbHEpPrivEntry(listEntry);
+		if (usbHEpPriv == lastFinished) {
+			lastFinished->frame = lastFinished->interval;
+			continue;
+		}
+
+		if (usbHEpPriv->frame < time)
+			usbHEpPriv->frame = 0;
+		else
+			lastFinished->interval = usbHEpPriv->frame;
+	}
+}
+
+static int32_t hostEpXIrq(struct HOST_CTRL *priv, uint8_t hwEpNum, uint8_t isIn, bool resubmit)
+{
+	struct HostEp *hwEp;
+	struct HOST_EP *hostEp;
+	struct HOST_EP_PRIV *usbHEpPriv;
+	struct HOST_REQ *usbReq;
+
+	uint8_t usbError, rxcon, txcon;
+	uint32_t status, length, chMaxLen;
+
+	if (!priv)
+		return -EINVAL;
+
+	hwEp = isIn ? &priv->in[hwEpNum] : &priv->out[hwEpNum];
+	hostEp = hwEp->scheduledUsbHEp;
+	if (!hostEp)
+		return -EINVAL;
+
+	usbHEpPriv = (struct HOST_EP_PRIV *)hostEp->hcPriv;
+
+	usbReq = getNextReq(hostEp);
+	if (!usbReq)
+		return 0;
+
+	if (isIn)
+		usbError = phytium_read8(&priv->regs->epExt[hwEpNum - 1].rxerr);
+	else
+		usbError = phytium_read8(&priv->regs->epExt[hwEpNum - 1].txerr);
+
+	usbError = (usbError & ERR_TYPE) >> 2;
+	status = decodeErrorCode(usbError);
+	if (status) {
+		pr_debug("%s %d Aborting\n", __func__, __LINE__);
+		if (isIn)
+			phytium_write16(&priv->regs->rxerrirq, 1 << hwEpNum);
+		else
+			phytium_write16(&priv->regs->txerrirq, 1 << hwEpNum);
+		priv->dmaDrv->dma_channelAbort(priv->dmaController, hwEp->channel);
+	}
+
+	length = priv->dmaDrv->dma_getActualLength(priv->dmaController, hwEp->channel);
+	chMaxLen = priv->dmaDrv->dma_getMaxLength(priv->dmaController, hwEp->channel);
+
+	if (status != 0)
+		usbReq->status = status;
+
+	usbReq->actualLength += length;
+
+	if (!resubmit)
+		usbHEpPriv->transferFinished = 1;
+
+	if (length == chMaxLen) {
+		if ((usbReq->buffLength - usbReq->actualLength) == 0)
+			usbHEpPriv->transferFinished = 1;
+		else
+			usbHEpPriv->transferFinished = 0;
+	}
+
+	if (usbHEpPriv->type != USB_ENDPOINT_XFER_ISOC) {
+		if (!hwEp->isInEp) {
+			txcon = phytium_read8(&priv->regs->ep[hwEp->hwEpNum - 1].txcon);
+			txcon &= ~CON_VAL;
+			phytium_write8(&priv->regs->ep[hwEp->hwEpNum - 1].txcon, txcon);
+		} else {
+			rxcon = phytium_read8(&priv->regs->ep[hwEp->hwEpNum - 1].rxcon);
+			rxcon &= ~CON_VAL;
+			phytium_write8(&priv->regs->ep[hwEp->hwEpNum - 1].rxcon, rxcon);
+		}
+		priv->dmaDrv->dma_channelRelease(priv->dmaController, hwEp->channel);
+		hwEp->channel = NULL;
+	}
+
+	if (usbHEpPriv->type == USB_ENDPOINT_XFER_INT) {
+		if (usbHEpPriv->isIn)
+			updateTimeIntTransfer(&priv->intInHEpQueue[hwEp->hwEpNum - 1], usbHEpPriv);
+		else
+			updateTimeIntTransfer(&priv->intOutHEpQueue[hwEp->hwEpNum - 1], usbHEpPriv);
+	}
+
+	scheduleNextTransfer(priv, usbReq, hwEp);
+
+	return 0;
+}
+
+static void host_CallbackTransfer(void *priv, uint8_t epNum, uint8_t isDirTx, bool resubmit)
+{
+	struct HOST_CTRL *host_priv = (struct HOST_CTRL *)priv;
+	int i;
+	int ret = 0;
+
+	if (!epNum) {
+		ret = hostEp0Irq(host_priv, !isDirTx);
+		if (resubmit | ret) {
+			for (i = 1; i < HOST_EP_NUM; i++) {
+				hostEpXIrq(host_priv, i, !isDirTx, true);
+				hostEpXIrq(host_priv, i, isDirTx, true);
+			}
+		}
+	} else
+		hostEpXIrq(host_priv, epNum, !isDirTx, false);
+}
+
+static int hc_reset(struct usb_hcd *hcd)
+{
+	hcd->speed = HCD_USB2;
+	hcd->self.root_hub->speed = USB_SPEED_HIGH;
+
+	return 0;
+}
+
+static int hc_start(struct usb_hcd *hcd)
+{
+	hcd->state = HC_STATE_RUNNING;
+	return 0;
+}
+
+static void hc_stop(struct usb_hcd *hcd)
+{
+	struct phytium_cusb *config = *(struct phytium_cusb **)hcd->hcd_priv;
+
+	if (config)
+		config->host_obj->host_stop(config->host_priv);
+
+	if (config->host_cfg.trbAddr) {
+		dma_free_coherent(config->dev, config->host_sysreq.trbMemSize,
+				config->host_cfg.trbAddr, config->host_cfg.trbDmaAddr);
+		config->host_cfg.trbAddr = NULL;
+	}
+
+	if (config->host_priv) {
+		devm_kfree(config->dev, config->host_priv);
+		config->host_priv = NULL;
+	}
+
+	hcd->state = HC_STATE_HALT;
+}
+
+static void hc_shutdown(struct usb_hcd *hcd)
+{
+}
+
+static void host_endpoint_update(struct phytium_cusb *config,
+		struct HOST_USB_DEVICE *udev, struct usb_host_endpoint *ep)
+{
+	int epnum;
+	struct HOST_EP *hostEp;
+
+	if (!config || !udev || !ep)
+		return;
+
+	epnum = get_epnum_from_pool(config->host_priv, usb_endpoint_num(&ep->desc),
+			usb_endpoint_dir_in(&ep->desc));
+
+	if (usb_endpoint_dir_out(&ep->desc)) {
+		if (udev->out_ep[epnum] == NULL) {
+			hostEp = kzalloc(sizeof(struct HOST_EP) +
+				config->host_obj->host_epGetPrivateDataSize(config->host_priv),
+				GFP_ATOMIC);
+			udev->out_ep[epnum] = hostEp;
+		} else
+			hostEp = udev->out_ep[epnum];
+	} else {
+		if (udev->in_ep[epnum] == NULL) {
+			hostEp = kzalloc(sizeof(struct HOST_EP) +
+				config->host_obj->host_epGetPrivateDataSize(config->host_priv),
+				GFP_ATOMIC);
+			udev->in_ep[epnum] = hostEp;
+		} else
+			hostEp = udev->in_ep[epnum];
+	}
+
+	hostEp->desc = *(struct usb_endpoint_descriptor *)(&ep->desc);
+	hostEp->userExt = (void *)ep;
+	INIT_LIST_HEAD(&hostEp->reqList);
+	hostEp->hcPriv = &((uint8_t *)hostEp)[sizeof(struct HOST_EP)];
+}
+
+static int hc_urb_enqueue(struct usb_hcd *hcd, struct urb *urb, gfp_t mem_flags)
+{
+	unsigned long flags;
+	u32 isoFrameSize;
+	int retval;
+	int index;
+	struct HOST_REQ *req;
+	struct HOST_CTRL *priv;
+	struct HOST_USB_DEVICE *usbDev;
+	struct usb_host_endpoint *host_ep;
+	struct usb_endpoint_descriptor *host_ep_desc;
+	struct phytium_cusb *config = *(struct phytium_cusb **)hcd->hcd_priv;
+
+	if (!config)
+		return -ENODEV;
+
+	priv = config->host_priv;
+	if (!priv)
+		return -ENODEV;
+
+	usbDev = priv->host_devices_table[urb->dev->slot_id];
+	if (!usbDev)
+		return -ENODEV;
+
+	host_ep = urb->ep;
+	host_ep_desc = &host_ep->desc;
+
+	spin_lock_irqsave(&config->lock, flags);
+	retval = usb_hcd_link_urb_to_ep(hcd, urb);
+	if (retval < 0) {
+		spin_unlock_irqrestore(&config->lock, flags);
+		return retval;
+	}
+	spin_unlock_irqrestore(&config->lock, flags);
+	isoFrameSize = urb->number_of_packets * sizeof(struct HOST_ISOFRAMESDESC);
+	req = kzalloc((sizeof(struct HOST_REQ) +
+				isoFrameSize), mem_flags);
+	if (!req) {
+		spin_lock_irqsave(&config->lock, flags);
+		usb_hcd_unlink_urb_from_ep(hcd, urb);
+		spin_unlock_irqrestore(&config->lock, flags);
+		return -ENOMEM;
+	}
+
+	req->isoFramesDesc = NULL;
+	req->isoFramesNumber = urb->number_of_packets;
+	req->epNum = get_epnum_from_pool(config->host_priv, usb_endpoint_num(host_ep_desc),
+			usb_endpoint_dir_in(host_ep_desc));
+
+	if (usb_endpoint_dir_in(host_ep_desc)) {
+		if (!usbDev->in_ep[req->epNum])
+			host_endpoint_update(config, usbDev, host_ep);
+	} else {
+		if (!usbDev->out_ep[req->epNum])
+			host_endpoint_update(config, usbDev, host_ep);
+	}
+
+	if (usb_endpoint_xfer_isoc(host_ep_desc)) {
+		req->isoFramesDesc = (struct HOST_ISOFRAMESDESC *)(&req[1]);
+		for (index = 0; index < urb->number_of_packets; index++) {
+			req->isoFramesDesc[index].length = urb->iso_frame_desc[index].length;
+			req->isoFramesDesc[index].offset = urb->iso_frame_desc[index].offset;
+		}
+	}
+
+	spin_lock_irqsave(&config->lock, flags);
+	INIT_LIST_HEAD(&req->list);
+	req->userExt = (void *)urb;
+	req->actualLength = urb->actual_length;
+	req->bufAddress = urb->transfer_buffer;
+	req->buffDma = urb->transfer_dma;
+	req->buffLength = urb->transfer_buffer_length;
+	req->epIsIn = usb_endpoint_dir_in(host_ep_desc);
+	req->eptype = usb_endpoint_type(host_ep_desc);
+	req->faddress = usb_pipedevice(urb->pipe);
+	req->interval = urb->interval;
+	req->reqUnlinked = 0;
+	req->setup = (struct usb_ctrlrequest *)urb->setup_packet;
+	req->setupDma = urb->setup_dma;
+	req->status = EINPROGRESS;
+	req->usbDev = &usbDev->udev;
+	req->usbEp = req->epIsIn ? usbDev->in_ep[req->epNum] : usbDev->out_ep[req->epNum];
+	req->usbEp->device_epNum = usb_endpoint_num(host_ep_desc);
+	if (!req->epNum)
+		usbDev->ep0_hep.desc.wMaxPacketSize = urb->dev->ep0.desc.wMaxPacketSize;
+
+	req->hcPriv = (void *)req->usbEp;
+	urb->hcpriv = req;
+	host_ep->hcpriv = (void *)usbDev;
+	retval = config->host_obj->host_reqQueue(config->host_priv, req);
+	if (retval) {
+		usb_hcd_unlink_urb_from_ep(hcd, urb);
+		urb->hcpriv = NULL;
+		spin_unlock_irqrestore(&config->lock, flags);
+		kfree(req);
+		return -retval;
+	}
+	spin_unlock_irqrestore(&config->lock, flags);
+
+	return 0;
+}
+
+static int hc_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status)
+{
+	unsigned long flags;
+	struct HOST_CTRL *priv;
+	int ret = 0;
+	struct phytium_cusb *config = *(struct phytium_cusb **)hcd->hcd_priv;
+
+	if (!config)
+		return -ENODEV;
+
+	priv = config->host_priv;
+	if (!priv->host_devices_table[urb->dev->slot_id])
+		return -ENODEV;
+
+	spin_lock_irqsave(&config->lock, flags);
+	if (usb_hcd_check_unlink_urb(hcd, urb, status))
+		goto done;
+
+	if (!urb->hcpriv)
+		goto err_giveback;
+
+	ret = config->host_obj->host_reqDequeue(priv, urb->hcpriv, status);
+
+	release_epnum_from_pool(config->host_priv, usb_pipeendpoint(urb->pipe),
+			usb_pipein(urb->pipe));
+
+	kfree(urb->hcpriv);
+	urb->hcpriv = NULL;
+done:
+	spin_unlock_irqrestore(&config->lock, flags);
+	return ret;
+
+err_giveback:
+	kfree(urb->hcpriv);
+	usb_hcd_unlink_urb_from_ep(hcd, urb);
+	spin_unlock_irqrestore(&config->lock, flags);
+	usb_hcd_giveback_urb(hcd, urb, -ESHUTDOWN);
+	return ret;
+}
+
+static void hc_endpoint_disable(struct usb_hcd *hcd, struct usb_host_endpoint *ld_ep)
+{
+	struct HOST_USB_DEVICE *usbDev;
+	int ep_num;
+	struct phytium_cusb *config;
+
+	config = *(struct phytium_cusb **)hcd->hcd_priv;
+	if (!config)
+		return;
+
+	ep_num = get_epnum_from_pool(config->host_priv, usb_endpoint_num(&ld_ep->desc),
+			usb_endpoint_dir_in(&ld_ep->desc));
+
+	usbDev = (struct HOST_USB_DEVICE *)ld_ep->hcpriv;
+	if (!usbDev)
+		return;
+
+	if (ld_ep->desc.bEndpointAddress) {
+		if (usb_endpoint_dir_in(&ld_ep->desc)) {
+			if (usbDev->in_ep[ep_num]) {
+				usbDev->in_ep[ep_num]->userExt = NULL;
+				INIT_LIST_HEAD(&usbDev->in_ep[ep_num]->reqList);
+				kfree(usbDev->in_ep[ep_num]);
+				usbDev->in_ep[ep_num] = NULL;
+			}
+		} else {
+			if (usbDev->out_ep[ep_num]) {
+				usbDev->out_ep[ep_num]->userExt = NULL;
+				INIT_LIST_HEAD(&usbDev->out_ep[ep_num]->reqList);
+				kfree(usbDev->out_ep[ep_num]);
+				usbDev->out_ep[ep_num] = NULL;
+			}
+		}
+	}
+}
+
+static int hc_get_frame(struct usb_hcd *hcd)
+{
+	return 0;
+}
+
+static int hc_alloc_dev(struct usb_hcd *hcd, struct usb_device *udev)
+{
+	struct HOST_USB_DEVICE *usbDev;
+	struct phytium_cusb *config;
+	struct HOST_CTRL *priv;
+	unsigned long flags = 0;
+	int index;
+	int slot = -EINVAL;
+
+	if (!hcd || !udev)
+		return -EINVAL;
+
+	config = *(struct phytium_cusb **)hcd->hcd_priv;
+	if (!config)
+		return 0;
+
+	spin_lock_irqsave(&config->lock, flags);
+	priv = config->host_priv;
+	for (index = 0; index < MAX_SUPPORTED_DEVICES; index++) {
+		if (priv->host_devices_table[index] == NULL) {
+			slot = index;
+			break;
+		}
+	}
+	spin_unlock_irqrestore(&config->lock, flags);
+
+	if (slot < 0)
+		return -ENOMEM;
+
+	usbDev = kzalloc((sizeof(struct HOST_USB_DEVICE) +
+				config->host_obj->host_epGetPrivateDataSize(priv)), GFP_KERNEL);
+	if (!usbDev)
+		return -ENOMEM;
+
+	usbDev->ep0_hep.hcPriv = &((uint8_t *)usbDev)[sizeof(struct HOST_USB_DEVICE)];
+	usbDev->udev.userExt = (void *)usbDev;
+	usbDev->ld_udev = udev;
+	usbDev->ld_udev->slot_id = slot;
+	usbDev->ep0_hep.desc.bLength = 7;
+	usbDev->ep0_hep.desc.bDescriptorType = USB_DT_ENDPOINT;
+	usbDev->in_ep[0] = &usbDev->ep0_hep;
+	usbDev->out_ep[0] = &usbDev->ep0_hep;
+	INIT_LIST_HEAD(&usbDev->ep0_hep.reqList);
+
+	priv->host_devices_table[slot] = usbDev;
+
+	return 1;
+}
+
+static void hc_free_dev(struct usb_hcd *hcd, struct usb_device *udev)
+{
+	struct HOST_CTRL *priv;
+	struct HOST_USB_DEVICE *usbDev;
+	struct phytium_cusb *config;
+	int i;
+
+	if (!hcd || !udev)
+		return;
+
+	config = *(struct phytium_cusb **)(hcd->hcd_priv);
+	if (!config)
+		return;
+
+	priv = (struct HOST_CTRL *)config->host_priv;
+	usbDev = priv->host_devices_table[udev->slot_id];
+	if (!usbDev)
+		return;
+
+	usbDev->in_ep[0] = NULL;
+	usbDev->out_ep[0] = NULL;
+	for (i = 1; i < HOST_EP_NUM; i++) {
+		if (usbDev->in_ep[i])
+			hc_endpoint_disable(hcd,
+					(struct usb_host_endpoint *)usbDev->in_ep[i]->userExt);
+		if (usbDev->out_ep[i])
+			hc_endpoint_disable(hcd,
+					(struct usb_host_endpoint *)usbDev->out_ep[i]->userExt);
+	}
+
+	priv->host_devices_table[udev->slot_id] = NULL;
+	usbDev->ld_udev->slot_id = 0;
+	usbDev->udev.userExt = (void *)NULL;
+	kfree(usbDev);
+}
+
+static int hc_reset_device(struct usb_hcd *hcd, struct usb_device *udev)
+{
+	struct HOST_CTRL *priv;
+	struct HOST_USB_DEVICE *usb_device;
+	struct phytium_cusb *config;
+
+	if (!hcd || !udev)
+		return -EINVAL;
+
+	config = *(struct phytium_cusb **)hcd->hcd_priv;
+	priv = (struct HOST_CTRL *)config->host_priv;
+	usb_device = priv->host_devices_table[udev->slot_id];
+	if (!usb_device)
+		return -ENODEV;
+
+	usb_device->udev.speed = usb_device->ld_udev->speed;
+	usb_device->udev.devnum = usb_device->ld_udev->devnum;
+
+	if (USB_SPEED_HIGH == usb_device->udev.speed || USB_SPEED_FULL == usb_device->udev.speed)
+		usb_device->ep0_hep.desc.wMaxPacketSize = 64;
+	else
+		usb_device->ep0_hep.desc.wMaxPacketSize = 8;
+
+	pr_debug("speed:%d ep0 wMaxPacketSize:%d\n", usb_device->udev.speed,
+			usb_device->ep0_hep.desc.wMaxPacketSize);
+
+	return 0;
+}
+
+static int hc_update_device(struct usb_hcd *hcd, struct usb_device *udev)
+{
+	struct HOST_CTRL *priv;
+	struct HOST_USB_DEVICE *usb_device;
+	struct phytium_cusb *config;
+
+	if (!hcd || !udev)
+		return -EINVAL;
+
+	config = *(struct phytium_cusb **)(hcd->hcd_priv);
+	priv = (struct HOST_CTRL *)config->host_priv;
+
+	usb_device = priv->host_devices_table[udev->slot_id];
+	if (!usb_device)
+		return -ENODEV;
+
+	usb_device->udev.devnum = udev->devnum;
+
+	return 0;
+}
+
+static int hc_add_endpoint(struct usb_hcd *hcd, struct usb_device *udev,
+		struct usb_host_endpoint *ep)
+{
+	return 0;
+}
+
+static int hc_drop_endpoint(struct usb_hcd *hcd, struct usb_device *udev,
+		struct usb_host_endpoint *ep)
+{
+	return 0;
+}
+
+static int hc_hub_status_data(struct usb_hcd *hcd, char *buf)
+{
+	struct HOST_CTRL *priv;
+	struct phytium_cusb *config = *(struct phytium_cusb **)hcd->hcd_priv;
+
+	if (!config)
+		return 0;
+
+	priv = (struct HOST_CTRL *)config->host_priv;
+	if (!priv)
+		return 0;
+
+	if (priv->portStatus & 0xffff0000) {
+		*buf = 0x02;
+		return 1;
+	}
+
+	return 0;
+}
+
+#ifdef CONFIG_PM
+static int hc_bus_suspend(struct usb_hcd *hcd)
+{
+	unsigned long flags;
+	struct phytium_cusb *config = *(struct phytium_cusb **)(hcd->hcd_priv);
+
+	if (!config)
+		return 0;
+
+	spin_lock_irqsave(&config->lock, flags);
+	hcd->state = HC_STATE_SUSPENDED;
+	spin_unlock_irqrestore(&config->lock, flags);
+
+	return 0;
+}
+
+static int hc_bus_resume(struct usb_hcd *hcd)
+{
+	unsigned long flags;
+	struct phytium_cusb *config = *(struct phytium_cusb **)(hcd->hcd_priv);
+
+	if (!config)
+		return 0;
+
+	spin_lock_irqsave(&config->lock, flags);
+	hcd->state = HC_STATE_RESUMING;
+	spin_unlock_irqrestore(&config->lock, flags);
+	return 0;
+}
+#endif
+
+static void host_giveback_request(struct HOST_CTRL *priv,
+		struct HOST_REQ *req, uint32_t status)
+{
+	struct urb *urb_req;
+	int urb_status = 0;
+	int i = 0;
+	struct phytium_cusb *config;
+
+	if (!priv || !req)
+		return;
+
+	urb_req = req->userExt;
+	urb_req->actual_length = req->actualLength;
+
+	switch (status) {
+	case HOST_ESTALL:
+		urb_status = -EPIPE;
+		break;
+	case HOST_EUNHANDLED:
+		urb_status = -EPROTO;
+		break;
+	case HOST_ESHUTDOWN:
+		urb_status = -ESHUTDOWN;
+		break;
+	default:
+		urb_status = status;
+	}
+	pr_debug("complete %p %pS (%d) dev%d ep%d%s %d/%d\n",
+			urb_req, urb_req->complete, urb_status,
+			usb_pipedevice(urb_req->pipe),
+			usb_pipeendpoint(urb_req->pipe),
+			usb_pipein(urb_req->pipe) ? "in" : "out",
+			urb_req->actual_length,
+			urb_req->transfer_buffer_length);
+
+	if (usb_endpoint_xfer_isoc(&urb_req->ep->desc)) {
+		for (i = 0; i < urb_req->number_of_packets; i++) {
+			urb_req->iso_frame_desc[i].status = 0;
+			urb_req->iso_frame_desc[i].actual_length = req->isoFramesDesc[i].length;
+		}
+	}
+
+	config = dev_get_drvdata(priv->dev);
+	usb_hcd_unlink_urb_from_ep(config->hcd, urb_req);
+	spin_unlock(&config->lock);
+	usb_hcd_giveback_urb(config->hcd, urb_req, urb_status);
+	kfree(req);
+	spin_lock(&config->lock);
+}
+
+static void host_rh_port_status_change(struct HOST_CTRL *priv)
+{
+	uint32_t statusHub;
+	char *status;
+	uint32_t retval;
+	struct usb_ctrlrequest setup;
+	struct phytium_cusb *config;
+
+	if (!priv)
+		return;
+
+	config = dev_get_drvdata(priv->dev);
+	if (!config)
+		return;
+
+	status = (char *)&statusHub;
+	hc_hub_status_data(config->hcd, status);
+
+	if (status) {
+		setup.bRequestType = USB_TYPE_CLASS | USB_RECIP_OTHER | USB_DIR_IN;//to host
+		setup.bRequest = USB_REQ_GET_STATUS;
+		setup.wValue = 0;
+		setup.wIndex = 1;
+		setup.wLength = 4;
+		retval = config->host_obj->host_vhubControl(priv, &setup, (uint8_t *)&statusHub);
+		if (retval)
+			return;
+
+		if (status[1] & USB_PORT_STAT_C_CONNECTION) {
+			if (status[0] & USB_PORT_STAT_CONNECTION) {
+				if (config->hcd->status_urb)
+					usb_hcd_poll_rh_status(config->hcd);
+				else
+					usb_hcd_resume_root_hub(config->hcd);
+			} else {
+				usb_hcd_resume_root_hub(config->hcd);
+				usb_hcd_poll_rh_status(config->hcd);
+			}
+		}
+	} else
+		usb_hcd_resume_root_hub(config->hcd);
+}
+
+static void host_set_ep_toggle(struct HOST_CTRL *priv,
+		struct HOST_DEVICE *udev, u8 ep_num, u8 is_in, u8 toggle)
+{
+	struct HOST_USB_DEVICE *device;
+
+	if (!priv || !udev)
+		return;
+
+	device = (struct HOST_USB_DEVICE *)udev->userExt;
+
+	usb_settoggle(device->ld_udev, ep_num, !is_in, toggle);
+}
+
+static u8 host_get_ep_toggle(struct HOST_CTRL *priv,
+		struct HOST_DEVICE *udev, u8 ep_num, u8 is_in)
+{
+	struct HOST_USB_DEVICE *device;
+
+	if (!priv || !udev)
+		return 0;
+
+	device = (struct HOST_USB_DEVICE *)udev->userExt;
+
+	return usb_gettoggle(device->ld_udev, ep_num, !is_in);
+}
+
+static uint32_t initEndpoints(struct HOST_CTRL *priv)
+{
+	int epNum;
+
+	if (!priv)
+		return 0;
+
+	priv->hwEpInCount = 0;
+	priv->hwEpOutCount = 0;
+	phytium_write8(&priv->regs->ep0fifoctrl, FIFOCTRL_FIFOAUTO | 0);
+	phytium_write8(&priv->regs->ep0fifoctrl, FIFOCTRL_FIFOAUTO | FIFOCTRL_IO_TX | 0);
+	memset(priv->ep_remap_pool, 0, sizeof(priv->ep_remap_pool));
+
+	for (epNum = 0; epNum < HOST_EP_NUM; epNum++) {
+		priv->in[epNum].isInEp = 1;
+		priv->in[epNum].hwEpNum = epNum;
+		if (priv->hostCfg.epIN[epNum].bufferingValue) {
+			priv->in[epNum].hwMaxPacketSize = priv->hostCfg.epIN[epNum].maxPacketSize;
+			priv->in[epNum].hwBuffers = priv->hostCfg.epIN[epNum].bufferingValue;
+			priv->in[epNum].state = HOST_EP_FREE;
+			priv->in[epNum].channel = NULL;
+			priv->hwEpInCount++;
+
+			if (epNum) {
+				phytium_write16(&priv->regs->rxstaddr[epNum - 1].addr,
+						priv->hostCfg.epIN[epNum].startBuf);
+				phytium_write8(&priv->regs->ep[epNum - 1].rxcon, 0x08);
+				phytium_write8(&priv->regs->fifoctrl, FIFOCTRL_FIFOAUTO | epNum);
+				phytium_write8(&priv->regs->irqmode[epNum - 1].inirqmode, 0x80);
+			}
+		} else
+			priv->in[epNum].state = HOST_EP_NOT_IMPLEMENTED;
+
+		priv->out[epNum].isInEp = 0;
+		priv->out[epNum].hwEpNum = epNum;
+		if (priv->hostCfg.epOUT[epNum].bufferingValue) {
+			priv->out[epNum].hwMaxPacketSize = priv->hostCfg.epOUT[epNum].maxPacketSize;
+			priv->out[epNum].hwBuffers = priv->hostCfg.epOUT[epNum].bufferingValue;
+			priv->out[epNum].state = HOST_EP_FREE;
+			priv->out[epNum].channel = NULL;
+			priv->hwEpInCount++;
+
+			if (epNum) {
+				phytium_write16(&priv->regs->txstaddr[epNum - 1].addr,
+						priv->hostCfg.epOUT[epNum].startBuf);
+				phytium_write8(&priv->regs->ep[epNum - 1].txcon, 0x08);
+				phytium_write8(&priv->regs->fifoctrl,
+						FIFOCTRL_FIFOAUTO | FIFOCTRL_IO_TX | epNum);
+				phytium_write8(&priv->regs->irqmode[epNum - 1].outirqmode, 0x80);
+			}
+		} else
+			priv->out[epNum].state = HOST_EP_NOT_IMPLEMENTED;
+	}
+
+	return 0;
+}
+
+int32_t hostInit(struct HOST_CTRL *priv, struct HOST_CFG *config,
+		struct HOST_CALLBACKS *callbacks, struct device *pdev, bool isVhubHost)
+{
+	int index;
+
+	if (!config || !priv || !callbacks || !pdev)
+		return -EINVAL;
+
+	priv->dev = pdev;
+	priv->hostCallbacks = *callbacks;
+	priv->hostCfg = *config;
+	priv->regs = (struct HW_REGS *)config->regBase;
+	priv->hostDrv = HOST_GetInstance();
+
+	priv->dmaDrv = DMA_GetInstance();
+	priv->dmaCfg.dmaModeRx = 0xFFFF;
+	priv->dmaCfg.dmaModeTx = 0xFFFF;
+	priv->dmaCfg.regBase = config->regBase + 0x400;
+	priv->dmaCfg.trbAddr = config->trbAddr;
+	priv->dmaCfg.trbDmaAddr = config->trbDmaAddr;
+	priv->dmaCallback.complete = host_CallbackTransfer;
+
+	priv->dmaController = (void *)(priv + 1);
+	priv->dmaDrv->dma_init(priv->dmaController, &priv->dmaCfg, &priv->dmaCallback);
+	priv->dmaDrv->dma_setParentPriv(priv->dmaController, priv);
+
+	INIT_LIST_HEAD(&priv->ctrlHEpQueue);
+
+	for (index = 0; index < MAX_INSTANCE_EP_NUM; index++) {
+		INIT_LIST_HEAD(&priv->isoInHEpQueue[index]);
+		INIT_LIST_HEAD(&priv->isoOutHEpQueue[index]);
+		INIT_LIST_HEAD(&priv->intInHEpQueue[index]);
+		INIT_LIST_HEAD(&priv->intOutHEpQueue[index]);
+		INIT_LIST_HEAD(&priv->bulkInHEpQueue[index]);
+		INIT_LIST_HEAD(&priv->bulkOutHEpQueue[index]);
+	}
+
+	phytium_write8(&priv->regs->cpuctrl, BIT(1));
+	phytium_write8(&priv->regs->otgctrl, OTGCTRL_ABUSDROP);
+	phytium_write8(&priv->regs->ep0maxpack, 0x40);
+
+	//disable interrupts
+	phytium_write8(&priv->regs->otgien, 0x0);
+	phytium_write8(&priv->regs->usbien, 0x0);
+	phytium_write16(&priv->regs->txerrien, 0x0);
+	phytium_write16(&priv->regs->rxerrien, 0x0);
+
+	//clear all interrupt except otg idle irq
+	phytium_write8(&priv->regs->otgirq, 0xFE);
+	phytium_write8(&priv->regs->usbirq, 0xFF);
+	phytium_write16(&priv->regs->txerrirq, 0xFF);
+	phytium_write16(&priv->regs->rxerrirq, 0xFF);
+
+	phytium_write8(&priv->regs->tawaitbcon, 0x80);
+
+	initEndpoints(priv);
+	priv->otgState = HOST_OTG_STATE_B_IDLE;
+
+	//reset all endpoint
+	phytium_write8(&priv->regs->endprst, ENDPRST_IO_TX);
+	phytium_write8(&priv->regs->endprst, ENDPRST_FIFORST | ENDPRST_TOGRST | ENDPRST_IO_TX);
+	phytium_write8(&priv->regs->endprst, ENDPRST_FIFORST | ENDPRST_TOGRST);
+
+	if (isVhubHost)
+		priv->vhub_regs = (struct VHUB_REGS *)(config->phy_regBase);
+	else
+		priv->custom_regs = (struct CUSTOM_REGS *)(config->phy_regBase);
+
+	return 0;
+}
+
+void hostDestroy(struct HOST_CTRL *priv)
+{
+}
+
+void hostStart(struct HOST_CTRL *priv)
+{
+	uint8_t otgstate, usbien;
+
+	if (!priv)
+		return;
+
+	priv->dmaDrv->dma_start(priv->dmaController);
+	usbien = phytium_read8(&priv->regs->usbien);
+	usbien = usbien | USBIR_URES | USBIR_SUSP;
+	phytium_write8(&priv->regs->usbien, usbien);
+retry:
+	otgstate = phytium_read8(&priv->regs->otgstate);
+	switch (otgstate) {
+	case HOST_OTG_STATE_A_IDLE:
+		priv->ep0State = HOST_EP0_STAGE_IDLE;
+		priv->otgState = HOST_OTG_STATE_A_IDLE;
+		phytium_write8(&priv->regs->otgirq, OTGIRQ_IDLEIRQ);
+		host_SetVbus(priv, 1);
+		break;
+	case HOST_OTG_STATE_B_IDLE:
+		host_SetVbus(priv, 1);
+		break;
+	case HOST_OTG_STATE_A_WAIT_VFALL:
+		goto retry;
+	}
+
+	phytium_write8(&priv->regs->otgien, OTGIRQ_CONIRQ |
+			OTGIRQ_VBUSERRIRQ | OTGIRQ_SRPDETIRQ);
+}
+
+void hostStop(struct HOST_CTRL *priv)
+{
+	if (!priv)
+		return;
+
+	phytium_write8(&priv->regs->otgien, 0x0);
+	phytium_write8(&priv->regs->usbien, 0x0);
+	phytium_write16(&priv->regs->txerrien, 0x0);
+	phytium_write16(&priv->regs->rxerrien, 0x0);
+
+	phytium_write8(&priv->regs->otgirq, 0xFE);
+	phytium_write8(&priv->regs->usbirq, 0xFF);
+	phytium_write16(&priv->regs->txerrirq, 0xFF);
+	phytium_write16(&priv->regs->rxerrirq, 0xFF);
+
+	host_SetVbus(priv, 0);
+	priv->dmaDrv->dma_stop(priv->dmaController);
+}
+
+static void handleReset(struct HOST_CTRL *priv)
+{
+	if (!priv)
+		return;
+
+	if (priv->otgState == HOST_OTG_STATE_A_WAIT_BCON
+			|| priv->otgState == HOST_OTG_STATE_B_WAIT_ACON) {
+		switch (phytium_read8(&priv->regs->speedctrl)) {
+		case SPEEDCTRL_HS:
+			priv->portStatus |= USB_PORT_STAT_HIGH_SPEED;
+			break;
+		case SPEEDCTRL_FS:
+			priv->portStatus &= ~(USB_PORT_STAT_HIGH_SPEED | USB_PORT_STAT_LOW_SPEED);
+			break;
+		case SPEEDCTRL_LS:
+			priv->portStatus |= USB_PORT_STAT_LOW_SPEED;
+			break;
+		}
+
+		priv->dmaDrv->dma_setHostMode(priv->dmaController);
+
+		if (priv->hostCallbacks.portStatusChange)
+			priv->hostCallbacks.portStatusChange(priv);
+
+		switch (phytium_read8(&priv->regs->otgstate)) {
+		case HOST_OTG_STATE_B_HOST:
+			priv->otgState = HOST_OTG_STATE_B_HOST;
+			break;
+		case HOST_OTG_STATE_A_HOST:
+			break;
+		default:
+			priv->otgState = HOST_OTG_STATE_A_HOST;
+			break;
+		}
+	}
+}
+
+void hostIsr(struct HOST_CTRL *priv)
+{
+	uint8_t usbirq, usbien;
+
+	if (!priv)
+		return;
+
+	usbirq = phytium_read8(&priv->regs->usbirq);
+	usbien = phytium_read8(&priv->regs->usbien);
+	pr_debug("raw usbirq:0x%x usbien:0x%x\n", usbirq, usbien);
+	usbirq = usbirq & usbien;
+
+	hostErrorIrq(priv);
+	hostOtgIrq(priv);
+
+	if (!usbirq)
+		goto DMA_IRQ;
+
+	if (usbirq & USBIR_URES) {
+		phytium_write8(&priv->regs->usbirq, USBIR_URES);
+		priv->port_resetting = 0;
+		handleReset(priv);
+	}
+
+	if (usbirq & USBIR_SOF)
+		phytium_write8(&priv->regs->usbirq, USBIR_SOF);
+
+	if (usbirq & USBIR_SUSP) {
+		pr_debug("clear suspend irq\n");
+		phytium_write8(&priv->regs->usbirq, USBIR_SUSP);
+		phytium_write8(&priv->regs->clkgate, 0x7);
+	}
+
+	return;
+DMA_IRQ:
+	priv->dmaDrv->dma_isr(priv->dmaController);
+}
+
+int32_t hostEpDisable(struct HOST_CTRL *priv, struct HOST_EP *ep)
+{
+	return 0;
+}
+
+unsigned int get_endpoint_interval(struct usb_endpoint_descriptor desc,
+		int speed)
+{
+	unsigned int interval = 0;
+
+	switch (speed) {
+	case USB_SPEED_HIGH:
+		if (usb_endpoint_xfer_control(&desc) || usb_endpoint_xfer_bulk(&desc)) {
+			if (desc.bInterval == 0)
+				return interval;
+			interval = fls(desc.bInterval) - 1;
+			interval = clamp_val(interval, 0, 15);
+			interval = 1 << interval;
+			if ((1 << interval) != desc.bInterval)
+				pr_debug("rounding to %d microframes, desc %d microframes\n",
+						1 << interval, desc.bInterval);
+			break;
+		}
+
+		if (usb_endpoint_xfer_isoc(&desc) || usb_endpoint_xfer_int(&desc)) {
+			interval = clamp_val(desc.bInterval, 1, 16) - 1;
+			interval = 1 << interval;
+			if (interval != desc.bInterval - 1)
+				pr_debug("rounding to %d %sframes\n", 1 << interval,
+						speed == USB_SPEED_FULL ? "" : "micro");
+		}
+		break;
+	case USB_SPEED_FULL:
+		if (usb_endpoint_xfer_isoc(&desc)) {
+			interval = clamp_val(desc.bInterval, 1, 16) - 1;
+			if (interval != desc.bInterval - 1)
+				pr_debug("rounding to %d %sframes\n", 1 << interval,
+						speed == USB_SPEED_FULL ? "" : "micro");
+			interval += 3;
+			break;
+		}
+		fallthrough;
+	case USB_SPEED_LOW:
+		if (usb_endpoint_xfer_int(&desc) || usb_endpoint_xfer_isoc(&desc)) {
+			interval = fls(desc.bInterval * 8) - 1;
+			interval = clamp_val(interval, 3, 10);
+			if ((1 << interval) != desc.bInterval * 8)
+				pr_debug("rounding to %d microframes, desc %d microframes\n",
+						1 << interval, desc.bInterval);
+		}
+	}
+
+	return interval;
+}
+
+int32_t hostReqQueue(struct HOST_CTRL *priv, struct HOST_REQ *req)
+{
+	struct HOST_EP_PRIV *hostEpPriv;
+	struct list_head *hEpQueue = NULL;
+	uint8_t idleQueue = 0;
+
+	if (!priv || !req)
+		return -EINVAL;
+
+	list_add_tail((struct list_head *)&req->list, (struct list_head *)&req->usbEp->reqList);
+	hostEpPriv = (struct HOST_EP_PRIV *)req->usbEp->hcPriv;
+
+	if (hostEpPriv->epIsReady) {
+		if (usb_endpoint_xfer_isoc(&req->usbEp->desc)) {
+			hostEpPriv->isocEpConfigured = 1;
+			hostStartReq(priv, req);
+		}
+		return 0;
+	}
+
+	hostEpPriv->epIsReady = 1;
+	hostEpPriv->maxPacketSize = req->usbEp->desc.wMaxPacketSize;
+	hostEpPriv->interval = req->interval;
+	hostEpPriv->epNum = req->usbEp->desc.bEndpointAddress & 0xf;
+	hostEpPriv->faddress = req->faddress;
+	hostEpPriv->usbHEp = req->usbEp;
+	hostEpPriv->isIn = req->epIsIn;
+
+	hostEpPriv->frame = get_endpoint_interval(req->usbEp->desc, req->usbDev->speed);
+	hostEpPriv->interval = hostEpPriv->frame;
+	switch (usb_endpoint_type(&req->usbEp->desc)) {
+	case USB_ENDPOINT_XFER_CONTROL:
+		hostEpPriv->isIn = 0;
+		hEpQueue = &priv->ctrlHEpQueue;
+		hostEpPriv->type = USB_ENDPOINT_XFER_CONTROL;
+		break;
+	case USB_ENDPOINT_XFER_BULK:
+		hEpQueue = hostEpPriv->isIn ? &priv->bulkInHEpQueue[req->epNum - 1] :
+			&priv->bulkOutHEpQueue[req->epNum - 1];
+		hostEpPriv->type = USB_ENDPOINT_XFER_BULK;
+		break;
+	case USB_ENDPOINT_XFER_INT:
+		hEpQueue = hostEpPriv->isIn ? &priv->intInHEpQueue[req->epNum - 1] :
+			&priv->intOutHEpQueue[req->epNum - 1];
+		hostEpPriv->type = USB_ENDPOINT_XFER_INT;
+		break;
+	case USB_ENDPOINT_XFER_ISOC:
+		hEpQueue = hostEpPriv->isIn ? &priv->isoInHEpQueue[req->epNum - 1] :
+			&priv->isoOutHEpQueue[req->epNum - 1];
+		hostEpPriv->type = USB_ENDPOINT_XFER_ISOC;
+		break;
+	default:
+		break;
+	}
+
+	if (hostEpPriv->isIn)
+		hostEpPriv->genericHwEp = &priv->in[req->epNum];
+	else
+		hostEpPriv->genericHwEp = &priv->out[req->epNum];
+
+	hostEpPriv->genericHwEp->state = HOST_EP_ALLOCATED;
+	hostEpPriv->genericHwEp->refCount++;
+
+	if (list_empty(hEpQueue)) {
+		if (hostEpPriv->genericHwEp->state == HOST_EP_BUSY) {
+			pr_err("Error:Hardware endpoint %d is busy\n",
+					hostEpPriv->genericHwEp->hwEpNum);
+			return -EINVAL;
+		}
+		idleQueue = 1;
+	}
+
+	if (usb_endpoint_xfer_int(&req->usbEp->desc))
+		list_add_tail(&hostEpPriv->node, hEpQueue);
+
+	if (idleQueue)
+		hostStartReq(priv, req);
+
+	return 0;
+}
+
+static int abortActuallyUsbRequest(struct HOST_CTRL *priv,
+		struct HOST_REQ *req, struct HOST_EP *usbEp)
+{
+	struct HOST_EP_PRIV *usbEpPriv;
+	struct HostEp *hostEp;
+	uint8_t rxcon, txcon;
+
+	if (!priv || !req || !usbEp)
+		return -EINVAL;
+
+	usbEpPriv = (struct HOST_EP_PRIV *)usbEp->hcPriv;
+	hostEp = usbEpPriv->currentHwEp;
+
+	usbEpPriv->transferFinished = 1;
+	if (hostEp->isInEp) {
+		if (hostEp->hwEpNum) {
+			rxcon = phytium_read8(&priv->regs->ep[hostEp->hwEpNum - 1].rxcon);
+			rxcon = rxcon & (~BIT(7));
+			phytium_write8(&priv->regs->ep[hostEp->hwEpNum - 1].rxcon, rxcon);
+		}
+	} else {
+		if (hostEp->hwEpNum) {
+			txcon = phytium_read8(&priv->regs->ep[hostEp->hwEpNum - 1].txcon);
+			txcon = txcon & (~BIT(7));
+			phytium_write8(&priv->regs->ep[hostEp->hwEpNum - 1].txcon, txcon);
+		}
+	}
+	abortTransfer(priv, req, hostEp);
+
+	return 0;
+}
+
+int32_t hostReqDequeue(struct HOST_CTRL *priv, struct HOST_REQ *req, uint32_t status)
+{
+	struct HOST_EP *usbEp;
+	struct HOST_EP_PRIV *usbEpPriv;
+	int ret = 0;
+
+	if (!priv || !req)
+		return -EINVAL;
+
+	usbEp = req->usbEp;
+	usbEpPriv = (struct HOST_EP_PRIV *)usbEp->hcPriv;
+
+	pr_debug("Dequeue usbReq:%p dev%d usbEp%d%s\n", req, req->faddress, req->epNum,
+			req->epIsIn ? "in" : "out");
+
+	if (!usbEpPriv->epIsReady)
+		return 0;
+
+	if (!usbEpPriv->currentHwEp || req->list.prev != &usbEp->reqList) {
+		givebackRequest(priv, req, status);
+		if (list_empty(&usbEp->reqList)) {
+			usbEpPriv->epIsReady = 0;
+			usbEpPriv->currentHwEp = NULL;
+			if (usb_endpoint_xfer_int(&usbEp->desc))
+				list_del(&usbEpPriv->node);
+		}
+	} else
+		ret = abortActuallyUsbRequest(priv, req, usbEp);
+
+	if (usbEpPriv->isocEpConfigured)
+		usbEpPriv->isocEpConfigured = 0;
+
+	return ret;
+}
+
+int32_t hostVHubStatusData(struct HOST_CTRL *priv, uint8_t *status)
+{
+	return 0;
+}
+
+int32_t hostGetDevicePD(struct HOST_CTRL *priv)
+{
+	return 0;
+}
+
+int32_t hostGetPrivateDataSize(struct HOST_CTRL *priv)
+{
+	if (!priv)
+		return 0;
+
+	return sizeof(struct HOST_EP_PRIV);
+}
+
+static int hub_descriptor(struct usb_hub_descriptor *buf)
+{
+	buf->bDescLength = 9;
+	buf->bDescriptorType = 0x29;
+	buf->bNbrPorts = 1;
+	buf->wHubCharacteristics = 0x11;
+	buf->bPwrOn2PwrGood = 5;
+	buf->bHubContrCurrent = 0;
+	buf->u.hs.DeviceRemovable[0] = 0x2;
+
+	return 0;
+}
+
+static int HubPortSuspend(struct HOST_CTRL *priv, u16 on)
+{
+	uint8_t otgctrl;
+
+	if (!priv)
+		return 0;
+
+	otgctrl = phytium_read8(&priv->regs->otgctrl);
+
+	if (on) {
+		otgctrl &= ~OTGCTRL_BUSREQ;
+		otgctrl &= ~OTGCTRL_BHNPEN;
+
+		priv->portStatus |= USB_PORT_STAT_SUSPEND;
+
+		switch (phytium_read8(&priv->regs->otgstate)) {
+		case HOST_OTG_STATE_A_HOST:
+			priv->otgState = HOST_OTG_STATE_A_SUSPEND;
+			otgctrl |= OTGCTRL_ASETBHNPEN;
+			break;
+		case HOST_OTG_STATE_B_HOST:
+			priv->otgState = HOST_OTG_STATE_B_HOST_2;
+			break;
+		default:
+			break;
+		}
+		phytium_write8(&priv->regs->otgctrl, otgctrl);
+	} else {
+		otgctrl |= OTGCTRL_BUSREQ;
+		otgctrl &= ~OTGCTRL_ASETBHNPEN;
+		phytium_write8(&priv->regs->otgctrl, otgctrl);
+		priv->portStatus |= USB_PORT_STAT_RESUME;
+	}
+	return 0;
+}
+
+static void HubPortReset(struct HOST_CTRL *priv, uint8_t on)
+{
+	uint8_t speed;
+
+	if (!priv)
+		return;
+
+	if (on) {
+		phytium_write16(&priv->regs->txerrirq, 0xFFFF);
+		phytium_write16(&priv->regs->txirq, 0xFFFF);
+		phytium_write16(&priv->regs->rxerrirq, 0xFFFF);
+		phytium_write16(&priv->regs->rxirq, 0xFFFF);
+
+		phytium_write8(&priv->regs->endprst, ENDPRST_IO_TX);
+		phytium_write8(&priv->regs->endprst, ENDPRST_FIFORST |
+				ENDPRST_TOGRST | ENDPRST_IO_TX);
+		phytium_write8(&priv->regs->endprst, ENDPRST_FIFORST | ENDPRST_TOGRST);
+		phytium_write8(&priv->regs->ep0fifoctrl, FIFOCTRL_FIFOAUTO | 0 | 0x04);
+		phytium_write8(&priv->regs->ep0fifoctrl, FIFOCTRL_FIFOAUTO |
+				FIFOCTRL_IO_TX | 0 | 0x04);
+
+		priv->portStatus |= USB_PORT_STAT_RESET;
+		priv->portStatus &= ~USB_PORT_STAT_ENABLE;
+		priv->port_resetting = 0;
+	} else {
+		speed = phytium_read8(&priv->regs->speedctrl);
+		if (speed == SPEEDCTRL_HS)
+			priv->portStatus |= USB_PORT_STAT_HIGH_SPEED;
+		else if (speed == SPEEDCTRL_FS)
+			priv->portStatus &= ~(USB_PORT_STAT_HIGH_SPEED | USB_PORT_STAT_LOW_SPEED);
+		else
+			priv->portStatus |= USB_PORT_STAT_LOW_SPEED;
+
+		priv->portStatus &= ~USB_PORT_STAT_RESET;
+		priv->portStatus |= USB_PORT_STAT_ENABLE | (USB_PORT_STAT_C_RESET << 16)
+			| (USB_PORT_STAT_C_ENABLE << 16);
+
+		if (priv->hostCallbacks.portStatusChange)
+			priv->hostCallbacks.portStatusChange(priv);
+	}
+}
+
+static int get_PortStatus(struct HOST_CTRL *priv, u16 wIndex, uint8_t *buff)
+{
+	uint32_t temp = 0;
+
+	if (!priv || !buff)
+		return 0;
+
+	if ((wIndex & 0xff) != 1)
+		return 1;
+
+	if (priv->portStatus & USB_PORT_STAT_RESET) {
+		if (!priv->port_resetting)
+			HubPortReset(priv, 0);
+	}
+
+	if (priv->portStatus & USB_PORT_STAT_RESUME) {
+		priv->portStatus &= ~(USB_PORT_STAT_SUSPEND | USB_PORT_STAT_RESUME);
+		priv->portStatus |= USB_PORT_STAT_C_SUSPEND << 16;
+		if (priv->hostCallbacks.portStatusChange)
+			priv->hostCallbacks.portStatusChange(priv);
+	}
+
+	temp = priv->portStatus & (~USB_PORT_STAT_RESUME);
+	buff[0] = temp;
+	buff[1] = temp >> 8;
+	buff[2] = temp >> 16;
+	buff[3] = temp >> 24;
+
+	return 0;
+}
+
+static int set_PortFeature(struct HOST_CTRL *priv, u16 wValue, u16 wIndex)
+{
+	if ((wIndex & 0xff) != 1)
+		return 1;
+
+	switch (wValue) {
+	case USB_PORT_FEAT_CONNECTION:
+		break;
+	case USB_PORT_FEAT_ENABLE:
+		break;
+	case USB_PORT_FEAT_SUSPEND:
+		HubPortSuspend(priv, 1);
+		break;
+	case USB_PORT_FEAT_OVER_CURRENT:
+		break;
+	case USB_PORT_FEAT_RESET:
+		HubPortReset(priv, 1);
+		break;
+	case USB_PORT_FEAT_L1:
+		break;
+	case USB_PORT_FEAT_POWER:
+		hostStart(priv);
+		break;
+	case USB_PORT_FEAT_LOWSPEED:
+		break;
+	case USB_PORT_FEAT_C_CONNECTION:
+		break;
+	case USB_PORT_FEAT_C_ENABLE:
+		break;
+	case USB_PORT_FEAT_C_SUSPEND:
+		break;
+	case USB_PORT_FEAT_C_OVER_CURRENT:
+		break;
+	case USB_PORT_FEAT_INDICATOR:
+		break;
+	case USB_PORT_FEAT_C_PORT_L1:
+		break;
+	default:
+		break;
+	}
+	priv->portStatus |= 1 << wValue;
+
+	return 0;
+}
+
+static int Clear_PortFeature(struct HOST_CTRL *priv, u16 wValue, u16 wIndex)
+{
+	if ((wIndex & 0xff) != 1)
+		return 1;
+
+	switch (wValue) {
+	case USB_PORT_FEAT_CONNECTION:
+		break;
+	case USB_PORT_FEAT_ENABLE:
+		break;
+	case USB_PORT_FEAT_SUSPEND:
+		HubPortSuspend(priv, 0);
+		break;
+	case USB_PORT_FEAT_OVER_CURRENT:
+		break;
+	case USB_PORT_FEAT_RESET:
+		break;
+	case USB_PORT_FEAT_L1:
+		break;
+	case USB_PORT_FEAT_POWER:
+		break;
+	case USB_PORT_FEAT_LOWSPEED:
+		break;
+	case USB_PORT_FEAT_C_CONNECTION:
+		break;
+	case USB_PORT_FEAT_C_ENABLE:
+		break;
+	case USB_PORT_FEAT_C_SUSPEND:
+		break;
+	case USB_PORT_FEAT_C_OVER_CURRENT:
+		break;
+	case USB_PORT_FEAT_INDICATOR:
+		break;
+	case USB_PORT_FEAT_C_PORT_L1:
+		break;
+	default:
+		break;
+	}
+	priv->portStatus &= ~(1 << wValue);
+
+	return 0;
+}
+
+static int hc_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
+		u16 wIndex, char *buf, u16 wLength)
+{
+	unsigned long flags = 0;
+	int retval = 0;
+	struct HOST_CTRL *priv;
+	struct phytium_cusb *config = *(struct phytium_cusb **)hcd->hcd_priv;
+
+	if (!config)
+		return -EINVAL;
+
+	if (!buf)
+		return -EINVAL;
+
+	if (unlikely(!HCD_HW_ACCESSIBLE(hcd))) {
+		spin_unlock_irqrestore(&config->lock, flags);
+		return -ESHUTDOWN;
+	}
+
+	priv = (struct HOST_CTRL *)config->host_priv;
+	if (!priv)
+		return -EINVAL;
+
+	spin_lock_irqsave(&config->lock, flags);
+	switch (typeReq) {
+	case GetHubStatus:
+		break;
+	case GetPortStatus:
+		get_PortStatus(priv, wIndex, (uint8_t *)buf);
+		break;
+	case GetHubDescriptor:
+		hub_descriptor((struct usb_hub_descriptor *)buf);
+		break;
+	case SetPortFeature:
+		set_PortFeature(priv, wValue, wIndex);
+		break;
+	case ClearPortFeature:
+		retval = Clear_PortFeature(priv, wValue, wIndex);
+		break;
+	case SetHubFeature:
+		break;
+	case ClearHubFeature:
+		break;
+	default:
+		break;
+	}
+
+	spin_unlock_irqrestore(&config->lock, flags);
+
+	return retval;
+}
+
+int32_t hostVHubControl(struct HOST_CTRL *priv, struct usb_ctrlrequest *setup, uint8_t *buff)
+{
+	uint16_t request;
+	uint32_t retval = 0;
+
+	if (!priv || !setup || !buff)
+		return -EINVAL;
+
+	request = setup->bRequestType << 0x08 | setup->bRequest;
+	switch (request) {
+	case ClearHubFeature:
+		break;
+	case SetHubFeature:
+		break;
+	case ClearPortFeature:
+		retval = Clear_PortFeature(priv, setup->wValue, setup->wIndex);
+		break;
+	case SetPortFeature:
+		retval = set_PortFeature(priv, setup->wValue, setup->wIndex);
+		break;
+	case GetHubDescriptor:
+		retval = hub_descriptor((struct usb_hub_descriptor *)buff);
+		break;
+	case GetHubStatus:
+		break;
+	case GetPortStatus:
+		retval = get_PortStatus(priv, setup->wIndex, (uint8_t *)buff);
+		break;
+	default:
+		retval = EOPNOTSUPP;
+		break;
+	}
+
+	return retval;
+}
+
+struct HOST_OBJ hostDrv = {
+	.host_init	=	hostInit,
+	.host_destroy	=	hostDestroy,
+	.host_start	=	hostStart,
+	.host_stop	=	hostStop,
+	.host_isr	=	hostIsr,
+
+	//endpoint operation
+	.host_epDisable =	hostEpDisable,
+	.host_reqQueue =	hostReqQueue,
+	.host_reqDequeue =	hostReqDequeue,
+	.host_vhubStatusData =	hostVHubStatusData,
+	.host_vhubControl =	hostVHubControl,
+	.host_getDevicePD =	hostGetDevicePD,
+	.host_epGetPrivateDataSize = hostGetPrivateDataSize,
+};
+
+static struct hc_driver host_driver = {
+	.description	=	"phytium-hcd",
+	.product_desc	=	"Phytium Host USB Driver",
+	.hcd_priv_size	=	sizeof(struct phytium_cusb *),
+	.flags		=	HCD_MEMORY | HCD_USB2 | HCD_DMA,
+	.reset		=	hc_reset,
+	.start		=	hc_start,
+	.stop		=	hc_stop,
+	.shutdown	=	hc_shutdown,
+	.urb_enqueue	=	hc_urb_enqueue,
+	.urb_dequeue	=	hc_urb_dequeue,
+	.endpoint_disable =	hc_endpoint_disable,
+	.get_frame_number =	hc_get_frame,
+	.alloc_dev	=	hc_alloc_dev,
+	.free_dev	=	hc_free_dev,
+	.reset_device	=	hc_reset_device,
+	.update_device	=	hc_update_device,
+	.add_endpoint	=	hc_add_endpoint,
+	.drop_endpoint	=	hc_drop_endpoint,
+	.hub_status_data =	hc_hub_status_data,
+	.hub_control	=	hc_hub_control,
+#ifdef	CONFIG_PM
+	.bus_suspend	=	hc_bus_suspend,
+	.bus_resume	=	hc_bus_resume,
+#endif
+};
+
+static int phytium_host_set_default_cfg(struct phytium_cusb *config)
+{
+	int index;
+
+	config->host_cfg.regBase = (uintptr_t)config->regs;
+	config->host_cfg.phy_regBase = (uintptr_t)config->phy_regs;
+	config->host_cfg.dmultEnabled = 1;
+	config->host_cfg.memoryAlignment = 0;
+	config->host_cfg.dmaSupport = 1;
+	config->host_cfg.isEmbeddedHost = 1;
+
+	for (index = 0; index < HOST_EP_NUM; index++) {
+		if (index == 0) {
+			config->host_cfg.epIN[index].bufferingValue = 1;
+			config->host_cfg.epIN[index].maxPacketSize = 64;
+			config->host_cfg.epIN[index].startBuf = 0;
+
+			config->host_cfg.epOUT[index].bufferingValue = 1;
+			config->host_cfg.epOUT[index].maxPacketSize = 64;
+			config->host_cfg.epOUT[index].startBuf = 0;
+		} else {
+			config->host_cfg.epIN[index].bufferingValue = 2;
+			config->host_cfg.epIN[index].maxPacketSize = 1024;
+			config->host_cfg.epIN[index].startBuf = 64 + 2 * 1024 * (index - 1);
+
+			config->host_cfg.epOUT[index].bufferingValue = 2;
+			config->host_cfg.epOUT[index].maxPacketSize = 1024;
+			config->host_cfg.epOUT[index].startBuf = 64 + 2 * 1024 * (index - 1);
+		}
+	}
+
+	return 0;
+}
+
+static int phytium_host_reinit(struct phytium_cusb *config)
+{
+	struct HOST_CTRL *ctrl;
+
+	if (!config || !config->host_priv)
+		return 0;
+
+	ctrl = (struct HOST_CTRL *)config->host_priv;
+
+	usb_root_hub_lost_power(config->hcd->self.root_hub);
+	hostStop(ctrl);
+
+	ctrl->portStatus = 0;
+
+	config->host_obj->host_init(config->host_priv, &config->host_cfg,
+			&config->host_callbacks, config->dev, config->isVhubHost);
+
+	return 0;
+}
+
+int phytium_host_init(struct phytium_cusb *config)
+{
+	int ret;
+
+	if (!config)
+		return 0;
+
+	phytium_host_set_default_cfg(config);
+	config->host_obj = HOST_GetInstance();
+	config->dma_cfg.regBase = config->host_cfg.regBase + 0x400;
+
+	config->dma_obj = DMA_GetInstance();
+	config->dma_obj->dma_probe(&config->dma_cfg, &config->dma_sysreq);
+
+	config->host_sysreq.privDataSize = sizeof(struct HOST_CTRL);
+	config->host_sysreq.trbMemSize = config->dma_sysreq.trbMemSize;
+	config->host_sysreq.privDataSize += config->dma_sysreq.privDataSize;
+
+	config->host_priv = devm_kzalloc(config->dev, config->host_sysreq.privDataSize, GFP_KERNEL);
+	if (!config->host_priv) {
+		ret = -ENOMEM;
+		goto err_probe;
+	}
+
+	config->host_cfg.trbAddr = dma_alloc_coherent(config->dev, config->host_sysreq.trbMemSize,
+			(dma_addr_t *)&config->host_cfg.trbDmaAddr, GFP_KERNEL);
+	if (!config->host_cfg.trbAddr) {
+		ret = -ENOMEM;
+		goto err_dma_coherent;
+	}
+
+	config->host_callbacks.portStatusChange = host_rh_port_status_change;
+	config->host_callbacks.getEpToggle = host_get_ep_toggle;
+	config->host_callbacks.setEpToggle = host_set_ep_toggle;
+	config->host_callbacks.givebackRequest = host_giveback_request;
+
+	config->host_obj->host_init(config->host_priv, &config->host_cfg,
+			&config->host_callbacks, config->dev, config->isVhubHost);
+
+	config->hcd = usb_create_hcd(&host_driver, config->dev, dev_name(config->dev));
+	if (!config->hcd) {
+		ret = -ENODEV;
+		goto err_host;
+	}
+
+	*config->hcd->hcd_priv = (unsigned long)config;
+	config->hcd->self.uses_pio_for_control = 0;
+	config->hcd->uses_new_polling = 1;
+	config->hcd->has_tt = 1;
+
+	dev_set_drvdata(config->dev, config);
+
+	config->hcd->self.otg_port = 1;
+	config->hcd->power_budget = 500;
+	ret = usb_add_hcd(config->hcd, 0, 0);
+	if (ret < 0)
+		goto err_setup;
+
+	return 0;
+err_setup:
+	usb_put_hcd(config->hcd);
+err_host:
+	config->host_obj->host_destroy(config->host_priv);
+	dma_free_coherent(config->dev, config->host_sysreq.trbMemSize,
+			config->host_cfg.trbAddr, config->host_cfg.trbDmaAddr);
+err_dma_coherent:
+err_probe:
+	dev_set_drvdata(config->dev, NULL);
+	return ret;
+}
+
+int phytium_host_uninit(struct phytium_cusb *config)
+{
+	if (!config)
+		return 0;
+
+	if (config->hcd) {
+		usb_remove_hcd(config->hcd);
+		usb_put_hcd(config->hcd);
+		config->hcd = NULL;
+	}
+
+	return 0;
+}
+
+#ifdef CONFIG_PM
+int phytium_host_resume(void *priv)
+{
+	int otgctrl;
+	struct phytium_cusb *config = (struct phytium_cusb *)priv;
+	struct HOST_CTRL *ctrl = (struct HOST_CTRL *)config->host_priv;
+
+	if (!ctrl)
+		return -EINVAL;
+
+	otgctrl = phytium_read8(&ctrl->regs->otgctrl);
+	otgctrl |= 1;
+	phytium_write8(&ctrl->regs->otgctrl, otgctrl);
+
+	phytium_host_reinit(config);
+
+	return 0;
+}
+
+int phytium_host_suspend(void *priv)
+{
+	int otgctrl;
+	struct phytium_cusb *config = (struct phytium_cusb *)priv;
+	struct HOST_CTRL *ctrl = (struct HOST_CTRL *)config->host_priv;
+
+	if (!ctrl)
+		return -EINVAL;
+
+	otgctrl = phytium_read8(&ctrl->regs->otgctrl);
+	otgctrl = otgctrl & (~1);
+	phytium_write8(&ctrl->regs->otgctrl, otgctrl);
+
+	return 0;
+}
+#endif
+
+struct HOST_OBJ *HOST_GetInstance(void)
+{
+	return &hostDrv;
+}
diff --git a/drivers/usb/phytium/host_api.h b/drivers/usb/phytium/host_api.h
new file mode 100644
index 0000000000000..c47cb04ede444
--- /dev/null
+++ b/drivers/usb/phytium/host_api.h
@@ -0,0 +1,250 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+
+#ifndef __PHYTIUM_HOST_API_H_
+#define __PHYTIUM_HOST_API_H_
+
+#include <linux/usb/ch9.h>
+#include "dma.h"
+
+#define MAX_SUPPORTED_DEVICES 16
+#define USB_PORT_STAT_RESUME (1 << 31)
+#define MAX_INSTANCE_EP_NUM 6
+#define ENDPOINT_DIR 2
+
+enum HOST_OtgState {
+	HOST_OTG_STATE_A_IDLE,
+	HOST_OTG_STATE_A_WAIT_VRISE,
+	HOST_OTG_STATE_A_WAIT_BCON,
+	HOST_OTG_STATE_A_HOST,
+	HOST_OTG_STATE_A_SUSPEND,
+	HOST_OTG_STATE_A_PERIPHERAL,
+	HOST_OTG_STATE_A_VBUS_ERR,
+	HOST_OTG_STATE_A_WAIT_VFALL,
+	HOST_OTG_STATE_B_IDLE = 0x10,
+	HOST_OTG_STATE_B_PERIPHERAL,
+	HOST_OTG_STATE_B_WAIT_ACON,
+	HOST_OTG_STATE_B_HOST,
+	HOST_OTG_STATE_B_HOST_2,
+	HOST_OTG_STATE_B_SRP_INT1,
+	HOST_OTG_STATE_B_SRP_INT2,
+	HOST_OTG_STATE_B_DISCHRG1,
+	HOST_OTG_STATE_B_DISCHRG2,
+	HOST_OTG_STATE_UNKNOWN,
+};
+
+enum HOST_EP0_STAGE {
+	HOST_EP0_STAGE_IDLE,
+	HOST_EP0_STAGE_SETUP,
+	HOST_EP0_STAGE_IN,
+	HOST_EP0_STAGE_OUT,
+	HOST_EP0_STAGE_STATUSIN,
+	HOST_EP0_STAGE_STATUSOUT,
+	HOST_EP0_STAGE_ACK,
+};
+
+enum HOST_EP_STATE {
+	HOST_EP_FREE,
+	HOST_EP_ALLOCATED,
+	HOST_EP_BUSY,
+	HOST_EP_NOT_IMPLEMENTED
+};
+
+struct HOST_DEVICE {
+	uint8_t devnum;
+	uint8_t hubPort;
+	unsigned int speed;
+	struct HOST_DEVICE *parent;
+	void *hcPriv;
+	void *userExt;
+};
+
+struct HOST_EP {
+	struct usb_endpoint_descriptor desc;
+	struct list_head reqList;
+	void *userExt;
+	uint8_t *hcPriv;
+	uint8_t device_epNum;
+};
+
+struct HOST_USB_DEVICE {
+	struct HOST_EP ep0_hep;
+	struct HOST_EP *in_ep[16];
+	struct HOST_EP *out_ep[16];
+	struct HOST_DEVICE udev;
+	struct usb_device *ld_udev;
+};
+
+struct HostEp {
+	uint8_t name[255];
+	uint8_t hwEpNum;
+	uint8_t hwBuffers;
+	uint16_t hwMaxPacketSize;
+	uint8_t isInEp;
+	void *channel;
+	uint8_t usbEpNum;
+	uint8_t type;
+	uint8_t usbPacketSize;
+	enum HOST_EP_STATE state;
+	struct HOST_EP *scheduledUsbHEp;
+	uint8_t refCount;
+};
+
+struct HOST_ISOFRAMESDESC {
+	uint32_t length;
+	uint32_t offset;
+};
+
+struct HOST_EP_PRIV {
+	struct list_head node;
+	struct HostEp *genericHwEp;
+	struct HostEp *currentHwEp;
+	uint8_t epIsReady;
+	uint8_t isIn;
+	uint8_t type;
+	uint8_t interval;
+	uint8_t epNum;
+	uint8_t faddress;
+	uint16_t maxPacketSize;
+	uint32_t frame;
+	uint8_t hubAddress;
+	uint8_t portAddress;
+	uint8_t split;
+	struct HOST_EP *usbHEp;
+	uint8_t isocEpConfigured;
+	uint8_t transferFinished;
+};
+
+struct HOST_REQ {
+	struct list_head list;
+	struct HOST_EP *usbEp;
+	void *userExt;
+	void *hcPriv;
+	struct HOST_DEVICE *usbDev;
+	struct usb_ctrlrequest *setup;
+	uintptr_t setupDma;
+	void *bufAddress;
+	uintptr_t buffDma;
+	uint32_t buffLength;
+	uint32_t actualLength;
+	uint8_t epIsIn;
+	uint8_t eptype;
+	uint8_t epNum;
+	uint8_t faddress;
+	uint8_t interval;
+	uint8_t status;
+	uint8_t reqUnlinked;
+	struct HOST_ISOFRAMESDESC *isoFramesDesc;
+	uint32_t isoFramesNumber;
+};
+
+struct HOST_SYSREQ {
+	uint32_t privDataSize;
+	uint32_t trbMemSize;
+};
+
+struct HOST_EP_CFG {
+	uint8_t bufferingValue;
+	uint16_t startBuf;
+	uint16_t maxPacketSize;
+};
+
+struct HOST_CFG {
+	uintptr_t regBase;
+	uintptr_t phy_regBase;
+	struct HOST_EP_CFG epIN[16];
+	struct HOST_EP_CFG epOUT[16];
+	uint8_t dmultEnabled;
+	uint8_t memoryAlignment;
+	uint8_t dmaSupport;
+	uint8_t isEmbeddedHost;
+	void *trbAddr;
+	uintptr_t trbDmaAddr;
+};
+
+struct HOST_CTRL;
+
+struct HOST_CALLBACKS {
+	void (*portStatusChange)(struct HOST_CTRL *priv);
+
+	uint8_t (*getEpToggle)(struct HOST_CTRL *priv,
+			struct HOST_DEVICE *usbDev, uint8_t epNum, uint8_t isIn);
+
+	void (*setEpToggle)(struct HOST_CTRL *priv, struct HOST_DEVICE *usbDev,
+			uint8_t epNum, uint8_t isIn, uint8_t toggle);
+
+	void (*givebackRequest)(struct HOST_CTRL *priv,
+			struct HOST_REQ *usbReq, uint32_t status);
+
+	void (*setTimer)(struct HOST_CTRL *priv, uint32_t time, uint8_t id);
+};
+
+struct HOST_OBJ {
+	int32_t (*host_probe)(struct HOST_CFG *config, struct HOST_SYSREQ *sysReq);
+
+	int32_t (*host_init)(struct HOST_CTRL *priv, struct HOST_CFG *config,
+			struct HOST_CALLBACKS *callbacks, struct device *pdev, bool isVhubHost);
+
+	void (*host_destroy)(struct HOST_CTRL *priv);
+
+	void (*host_start)(struct HOST_CTRL *priv);
+
+	void (*host_stop)(struct HOST_CTRL *priv);
+
+	void (*host_isr)(struct HOST_CTRL *priv);
+
+	int32_t (*host_epDisable)(struct HOST_CTRL *priv, struct HOST_EP *ep);
+
+	int32_t (*host_reqQueue)(struct HOST_CTRL *priv, struct HOST_REQ *req);
+
+	int32_t (*host_reqDequeue)(struct HOST_CTRL *priv,
+			struct HOST_REQ *req, uint32_t status);
+
+	int32_t (*host_vhubStatusData)(struct HOST_CTRL *priv, uint8_t *status);
+
+	int32_t (*host_vhubControl)(struct HOST_CTRL *priv,
+			struct usb_ctrlrequest *setup, uint8_t *buff);
+
+	int32_t (*host_getDevicePD)(struct HOST_CTRL *priv);
+
+	int32_t (*host_epGetPrivateDataSize)(struct HOST_CTRL *priv);
+};
+
+struct HOST_CTRL {
+	struct device *dev;
+	struct HW_REGS	*regs;
+	struct HOST_OBJ *hostDrv;
+	struct HOST_CFG hostCfg;
+	struct HOST_CALLBACKS hostCallbacks;
+	struct HostEp in[16];
+	struct HostEp out[16];
+	uint32_t portStatus;
+	struct list_head ctrlHEpQueue;
+	struct list_head isoInHEpQueue[MAX_INSTANCE_EP_NUM];
+	struct list_head isoOutHEpQueue[MAX_INSTANCE_EP_NUM];
+	struct list_head intInHEpQueue[MAX_INSTANCE_EP_NUM];
+	struct list_head intOutHEpQueue[MAX_INSTANCE_EP_NUM];
+	struct list_head bulkInHEpQueue[MAX_INSTANCE_EP_NUM];
+	struct list_head bulkOutHEpQueue[MAX_INSTANCE_EP_NUM];
+	uint8_t hwEpInCount;
+	uint8_t hwEpOutCount;
+	unsigned int speed;
+	enum HOST_OtgState otgState;
+	enum HOST_EP0_STAGE ep0State;
+	uint8_t vBusErrCnt;
+	uint8_t isRemoteWakeup;
+	uint8_t isSelfPowered;
+	uint8_t deviceAddress;
+	struct DMA_OBJ *dmaDrv;
+	void *dmaController;
+	struct DMA_CFG dmaCfg;
+	struct DMA_CALLBACKS dmaCallback;
+	uint8_t port_resetting;
+	struct HOST_USB_DEVICE *host_devices_table[MAX_SUPPORTED_DEVICES];
+	struct CUSTOM_REGS *custom_regs;
+	struct VHUB_REGS *vhub_regs;
+	int ep_remap_pool[ENDPOINT_DIR][MAX_INSTANCE_EP_NUM + 1];
+};
+
+struct HOST_OBJ *HOST_GetInstance(void);
+
+#endif
diff --git a/drivers/usb/phytium/hw-regs.h b/drivers/usb/phytium/hw-regs.h
new file mode 100644
index 0000000000000..8da9f8e9b9253
--- /dev/null
+++ b/drivers/usb/phytium/hw-regs.h
@@ -0,0 +1,297 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef __LINUX_PHYTIUM_HW_REGS
+#define __LINUX_PHYTIUM_HW_REGS
+
+#define USBIEN			0x198
+#define USBIEN_SUDAVIE		BIT(0)
+#define USBIEN_SOFIE		BIT(1)
+#define USBIEN_SUTOKIE		BIT(2)
+#define USBIEN_SUSPIE		BIT(3)
+#define USBIEN_URESIE		BIT(4)
+#define USBIEN_HSPEEDIE		BIT(5)
+#define USBIEN_LPMIE		BIT(7)
+
+#define USBCS			0x1a3
+#define USBCS_LSMODE		BIT(0)
+#define USBCS_LPMNYET		BIT(1)
+#define USBCS_SIGSUME		BIT(5)
+#define USBCS_DISCON		BIT(6)
+#define USBCS_WAKESRC		BIT(7)
+
+#define USBIR_SOF		BIT(1)
+#define USBIR_SUSP		BIT(3)
+#define USBIR_URES		BIT(4)
+#define USBIR_HSPEED		BIT(5)
+
+#define USBIR_SUDAV		BIT(0)
+#define USBIR_SUTOK		BIT(2)
+#define USBIR_LPMIR		BIT(7)
+
+#define OTGIRQ_IDLEIRQ		BIT(0)
+#define OTGIRQ_SRPDETIRQ	BIT(1)
+#define OTGIRQ_CONIRQ		BIT(2)
+#define OTGIRQ_LOCSOFIRQ	BIT(2)
+#define OTGIRQ_VBUSERRIRQ	BIT(3)
+#define OTGIRQ_PERIPHIRQ	BIT(4)
+#define OTGIRQ_IDCHANGEIRQ	BIT(5)
+#define OTGIRQ_HOSTDISCON	BIT(6)
+#define OTGIRQ_BSE0SRPIRQ	BIT(7)
+
+#define OTGCTRL_BUSREQ		BIT(0)
+#define OTGCTRL_ABUSDROP	BIT(1)
+#define OTGCTRL_ASETBHNPEN	BIT(2)
+#define OTGCTRL_BHNPEN		BIT(3)
+#define OTGCTRL_SRPVBUSDETEN	BIT(4)
+#define OTGCTRL_SRPDATDETEN	BIT(5)
+#define OTGCTRL_FORCEBCONN	BIT(7)
+
+#define OTGSTATUS_ID		BIT(6)
+
+#define ENDPRST_EP		0x0f
+#define ENDPRST_IO_TX		BIT(4)
+#define ENDPRST_TOGRST		BIT(5)
+#define ENDPRST_FIFORST		BIT(6)
+#define ENDPRST_TOGSETQ		BIT(7)
+
+#define FIFOCTRL_EP		0x0f
+#define FIFOCTRL_IO_TX		BIT(4)
+#define FIFOCTRL_FIFOAUTO	BIT(5)
+
+#define SPEEDCTRL_LS		BIT(0)
+#define SPEEDCTRL_FS		BIT(1)
+#define SPEEDCTRL_HS		BIT(2)
+#define SPEEDCTRL_HSDISABLE	BIT(7)
+
+#define CON_TYPE_CONTROL	0x00
+#define CON_TYPE_ISOC		0x04
+#define CON_TYPE_BULK		0x08
+#define CON_TYPE_INT		0x0C
+#define CON_TYPE_ISOC_1_ISOD	0x00
+#define CON_TYPE_ISOC_2_ISOD	0x10
+#define CON_TYPE_ISOC_3_ISOD	0x20
+#define CON_STALL		0x40
+#define CON_VAL			0x80
+
+#define ERR_TYPE		0x1c
+#define ERR_COUNT		0x03
+#define ERR_RESEND		BIT(6)
+#define ERR_UNDERRIEN		BIT(7)
+
+#define ERR_NONE			0
+#define ERR_CRC				1
+#define ERR_DATA_TOGGLE_MISMATCH	2
+#define ERR_STALL			3
+#define ERR_TIMEOUT			4
+#define ERR_PID				5
+#define ERR_TOO_LONG_PACKET		6
+#define ERR_DATA_UNDERRUN		7
+
+#define EP0CS_HCSETTOGGLE	BIT(6)
+#define EP0CS_HCSET		BIT(4)
+#define EP0CS_RXBUSY_MASK	BIT(3)
+#define EP0CS_TXBUSY_MASK	BIT(2)
+#define EP0CS_STALL		BIT(0)
+#define EP0CS_HSNAK		BIT(1)
+#define EP0CS_DSTALL		BIT(4)
+#define EP0CS_CHGSET		BIT(7)
+
+#define CS_ERR		0x01
+#define CS_BUSY		0x02
+#define CS_NPAK		0x0c
+#define CS_NPAK_OFFSET	0x02
+#define CS_AUTO		0x10
+
+#define CON_BUF_SINGLE	0x00
+#define CON_BUF_DOUBLE	0x01
+#define CON_BUF_TRIPLE	0x02
+#define CON_BUF_QUAD	0x03
+#define CON_BUF	0x03
+
+struct HW_REGS {
+	uint8_t ep0Rxbc;        /*address 0x00*/
+	uint8_t ep0Txbc;        /*address 0x01*/
+	uint8_t ep0cs;          /*address 0x02*/
+	int8_t reserved0;       /*address 0x03*/
+	uint8_t lpmctrll;       /*address 0x04*/
+	uint8_t lpmctrlh;       /*address 0x05*/
+	uint8_t lpmclock;
+	uint8_t ep0fifoctrl;
+	struct ep {             /*address 0x08*/
+		uint16_t rxbc;        //outbc (hcinbc)
+		uint8_t rxcon;
+		uint8_t rxcs;
+		uint16_t txbc;         //inbc  (hcoutbc
+		uint8_t txcon;
+		uint8_t txcs;
+	} ep[15];
+	uint8_t reserved1[4];
+	uint32_t fifodat[15];   /*address 0x84*/
+	uint8_t ep0ctrl;        /*address 0xC0*/
+	uint8_t tx0err;         /*address 0xC1*/
+	uint8_t reserved2;
+	uint8_t rx0err;         /*address 0xC3*/
+	struct epExt {
+		uint8_t txctrl;
+		uint8_t txerr;
+		uint8_t rxctrl;
+		uint8_t rxerr;
+	} epExt[15];
+	uint8_t ep0datatx[64]; /*address 0x100*/
+	uint8_t ep0datarx[64]; /*address 0x140*/
+	uint8_t setupdat[8];    /*address 0x180*/
+	uint16_t txirq;         /*address 0x188*/
+	uint16_t rxirq;         /*address 0x18A*/
+	uint8_t usbirq;         /*address 0x18C*/
+	uint8_t reserved4;
+	uint16_t rxpngirq;      /*address 0x18E*/
+	uint16_t txfullirq;     /*address 0x190*/
+	uint16_t rxemptirq;     /*address 0x192*/
+	uint16_t txien;         /*address 0x194*/
+	uint16_t rxien;         /*address 0x196*/
+	uint8_t usbien;         /*address 0x198*/
+	uint8_t reserved6;
+	uint16_t rxpngien;      /*address 0x19A*/
+	uint16_t txfullien;     /*address 0x19C*/
+	uint16_t rxemptien;     /*address 0x19E*/
+	uint8_t usbivect;       /*address 0x1A0*/
+	uint8_t fifoivect;      /*address 0x1A1*/
+	uint8_t endprst;        /*address 0x1A2*/
+	uint8_t usbcs;          /*address 0x1A3*/
+	uint16_t frmnr;         /*address 0x1A4*/
+	uint8_t fnaddr;         /*address 0x1A6*/
+	uint8_t clkgate;        /*address 0x1A7*/
+	uint8_t fifoctrl;       /*address 0x1A8*/
+	uint8_t speedctrl;      /*address 0x1A9*/
+	uint8_t reserved8[1];   /*address 0x1AA*/
+	uint8_t portctrl;       /*address 0x1AB*/
+	uint16_t hcfrmnr;       /*address 0x1AC*/
+	uint16_t hcfrmremain;   /*address 0x1AE*/
+	uint8_t reserved9[4];   /*address 0x1B0*/
+	uint16_t rxerrirq;      /*address 0x1B4*/
+	uint16_t txerrirq;      /*address 0x1B6*/
+	uint16_t rxerrien;      /*address 0x1B8*/
+	uint16_t txerrien;      /*address 0x1BA*/
+	/*OTG extension*/
+	uint8_t otgirq;         /*address 0x1BC*/
+	uint8_t otgstate;       /*address 0x1BD*/
+	uint8_t otgctrl;        /*address 0x1BE*/
+	uint8_t otgstatus;      /*address 0x1BF*/
+	uint8_t otgien;         /*address 0x1C0*/
+	uint8_t taaidlbdis;     /*address 0x1C1*/
+	uint8_t tawaitbcon;     /*address 0x1C2*/
+	uint8_t tbvbuspls;      /*address 0x1C3*/
+	uint8_t otg2ctrl;       /*address 0x1C4*/
+	uint8_t reserved10[2];  /*address 0x1C5*/
+	uint8_t tbvbusdispls;   /*address 0x1C7*/
+	uint8_t traddr;         /*address 0x1C8*/
+	uint8_t trwdata;        /*address 0x1C9*/
+	uint8_t trrdata;        /*address 0x1CA*/
+	uint8_t trctrl;         /*address 0x1CB*/
+	uint16_t isoautoarm;    /*address 0x1CC*/
+	uint8_t adpbc1ien;      /*address 0x1CE*/
+	uint8_t adpbc2ien;      /*address 0x1CF*/
+	uint8_t adpbcctr0;      /*address 0x1D0*/
+	uint8_t adpbcctr1;      /*address 0x1D1*/
+	uint8_t adpbcctr2;      /*address 0x1D2*/
+	uint8_t adpbc1irq;      /*address 0x1D3*/
+	uint8_t adpbc0status;   /*address 0x1D4*/
+	uint8_t adpbc1status;   /*address 0x1D5*/
+	uint8_t adpbc2status;   /*address 0x1D6*/
+	uint8_t adpbc2irq;      /*address 0x1D7*/
+	uint16_t isodctrl;      /*address 0x1D8*/
+	uint8_t reserved11[2];
+	uint16_t isoautodump;   /*address 0x1DC*/
+	uint8_t reserved12[2];
+	uint8_t ep0maxpack;     /*address 0x1E0*/
+	uint8_t reserved13;
+	uint16_t rxmaxpack[15]; /*address 0x1E2*/
+	struct rxsoftimer { /*address 0x200 to  0x23F*/
+		uint16_t timer;
+		uint8_t reserved;
+		uint8_t ctrl;
+	} rxsoftimer[16];
+
+	struct  txsoftimer { /*address 0x240 to  0x27F*/
+		uint16_t timer;
+		uint8_t reserved;
+		uint8_t ctrl;
+	} txsoftimer[16];
+	uint8_t reserved14[132];
+	struct rxstaddr {       /*address 0x304*/
+		uint16_t addr;
+		uint16_t reserved;
+	} rxstaddr[15];
+	uint8_t reserved15[4];
+	struct txstaddr {       /*address 0x344*/
+		uint16_t addr;
+		uint16_t reserved;
+	} txstaddr[15];
+	int8_t reserved16[4];	/*address 0x380*/
+	struct irqmode {	/*address 0x384*/
+		int8_t inirqmode;
+		int8_t reserved21;
+		int8_t outirqmode;
+		int8_t reserved22;
+	} irqmode[15];
+	/*The Microprocessor control*/
+	uint8_t cpuctrl;         /*address 0x3C0*/
+	int8_t reserved17[15];
+	/*The debug counters and workarounds*/
+	uint8_t debug_rx_bcl;    /*address 0x3D0*/
+	uint8_t debug_rx_bch;    /*address 0x3D1*/
+	uint8_t debug_rx_status; /*address 0x3D2*/
+	uint8_t debug_irq;       /*address 0x3D3*/
+	uint8_t debug_tx_bcl;    /*address 0x3D4*/
+	uint8_t debug_tx_bch;    /*address 0x3D5*/
+	uint8_t debug_tx_status; /*address 0x3D6*/
+	uint8_t debug_ien;       /*address 0x3D7*/
+	uint8_t phywa_en;        /*address 0x3D8*/
+	/*endian*/
+	uint8_t wa1_cnt;       /*address 0x3D9*/
+	int8_t reserved18[2];  /*address 0x3DA*/
+	uint8_t endian_sfr_cs; /*address 0x3DC*/
+	int8_t reserved19[2];    /*address 0x3DD*/
+	uint8_t endian_sfr_s;  /*address 0x3DF*/
+	int8_t reserved20[2];    /*address 0x3E0*/
+	uint16_t txmaxpack[15]; /*address 0x3E2*/
+};
+
+struct CUSTOM_REGS {
+	uint32_t secure_ctrl;	/*address 0x80000*/
+	uint32_t secsid_atst;	/*address 0x80004*/
+	uint32_t nsaid_smmuid;	/*address 0x80008*/
+	uint32_t ace;		/*address 0x8000c*/
+	uint32_t wakeup;	/*address 0x80010*/
+	uint32_t debug;		/*address 0x80014*/
+};
+
+struct VHUB_REGS {
+	uint32_t gen_cfg;	/*address 0x00*/
+	uint32_t gen_st;	/*address 0x04*/
+	uint32_t bc_cfg;	/*address 0x08*/
+	uint32_t bc_st;		/*address 0x0c*/
+	uint32_t adp_cfg;	/*address 0x10*/
+	uint32_t adp_st;	/*address 0x14*/
+	uint32_t dbg_cfg;	/*address 0x18*/
+	uint32_t dbg_st;	/*address 0x1c*/
+	uint32_t utmip_cfg;	/*address 0x20*/
+	uint32_t utmip_st;	/*address 0x24*/
+};
+
+struct DMARegs {
+	uint32_t conf;      /*address 0x400*/
+	uint32_t sts;       /*address 0x404*/
+	uint32_t reserved5[5];
+	uint32_t ep_sel;    /*address 0x41C*/
+	uint32_t traddr;    /*address 0x420*/
+	uint32_t ep_cfg;    /*address 0x424*/
+	uint32_t ep_cmd;    /*address 0x428*/
+	uint32_t ep_sts;    /*address 0x42c*/
+	uint32_t ep_sts_sid;/*address 0x430*/
+	uint32_t ep_sts_en; /*address 0x434*/
+	uint32_t drbl;      /*address 0x438*/
+	uint32_t ep_ien;    /*address 0x43C*/
+	uint32_t ep_ists;   /*address 0x440*/
+};
+
+#endif
diff --git a/drivers/usb/phytium/pci.c b/drivers/usb/phytium/pci.c
new file mode 100644
index 0000000000000..b4d675effb88b
--- /dev/null
+++ b/drivers/usb/phytium/pci.c
@@ -0,0 +1,208 @@
+// SPDX-License-Identifier: GPL-2.0
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/io.h>
+#include <linux/pci.h>
+#include <linux/of_platform.h>
+#include <linux/dma-mapping.h>
+#include <linux/usb/otg.h>
+#include "core.h"
+
+#define PHYTIUM_OTG_USB_LOADED  3
+
+static bool phytium_hw_is_device(struct phytium_cusb *config)
+{
+	pr_info("%s %d\n", __func__, __LINE__);
+
+	return false;
+}
+
+static bool phytium_hw_is_host(struct phytium_cusb *config)
+{
+	pr_info("%s %d\n", __func__, __LINE__);
+
+	return true;
+}
+
+static int phytium_get_dr_mode(struct phytium_cusb *config)
+{
+	enum usb_dr_mode mode;
+
+	config->dr_mode = usb_get_dr_mode(config->dev);
+	if (config->dr_mode == USB_DR_MODE_UNKNOWN)
+		config->dr_mode = USB_DR_MODE_OTG;
+
+	mode = config->dr_mode;
+	if (phytium_hw_is_device(config)) {
+		if (IS_ENABLED(CONFIG_USB_PHYTIUM_HOST)) {
+			dev_err(config->dev, "Controller does not support host mode.\n");
+			return -EINVAL;
+		}
+
+		mode = USB_DR_MODE_PERIPHERAL;
+	} else if (phytium_hw_is_host(config)) {
+		if (IS_ENABLED(CONFIG_USB_PHYTIUM_PERIPHERAL)) {
+			dev_err(config->dev, "Controller does not support device mode.\n");
+			return -EINVAL;
+		}
+		mode = USB_DR_MODE_HOST;
+	} else {
+		if (IS_ENABLED(CONFIG_USB_PHYTIUM_HOST))
+			mode = USB_DR_MODE_HOST;
+		else if (IS_ENABLED(CONFIG_USB_PHYTIUM_PERIPHERAL))
+			mode = USB_DR_MODE_PERIPHERAL;
+	}
+
+	if (mode != config->dr_mode) {
+		dev_warn(config->dev, "Configuration mismatch. dr_mode forced to %s\n",
+		mode == USB_DR_MODE_HOST ? "host" : "device");
+		config->dr_mode = mode;
+	}
+
+	return 0;
+}
+
+static int phytium_pci_probe(struct pci_dev *pdev, const struct pci_device_id *pid)
+{
+	struct phytium_cusb *config;
+	int retval = 0;
+
+	if (usb_disabled())
+		return -ENODEV;
+
+	retval = pcim_enable_device(pdev);
+	if (retval < 0) {
+		dev_err(&pdev->dev, "pcim_enable_device failed\n");
+		return -ENODEV;
+	}
+	pci_set_master(pdev);
+
+	config = devm_kzalloc(&pdev->dev, sizeof(*config), GFP_KERNEL);
+	if (!config)
+		return -ENOMEM;
+
+	spin_lock_init(&config->lock);
+	config->dev = &pdev->dev;
+
+	config->irq = pdev->irq;
+	if (config->irq <= 0) {
+		dev_err(config->dev, "getting usb irq failed\n");
+		return config->irq;
+	}
+
+	config->regs = pci_iomap(pdev, 0, 0);
+	if (IS_ERR(config->regs)) {
+		dev_err(config->dev, "map IOMEM resource failed\n");
+		return -1;
+	}
+
+	if (!pdev->dev.dma_mask)
+		pdev->dev.dma_mask = &pdev->dev.coherent_dma_mask;
+
+	if (dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(64)))
+		dev_err(&pdev->dev, "failed to set 64-bit dma\n");
+	else if (dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(32)))
+		dev_err(&pdev->dev, "failed to set 32-bit dma\n");
+
+	pci_enable_msi(pdev);
+
+	phytium_get_dr_mode(config);
+
+	phytium_core_reset(config, false);
+
+	if (config->dr_mode == USB_DR_MODE_HOST || config->dr_mode == USB_DR_MODE_OTG)
+		phytium_host_init(config);
+
+	if (config->dr_mode == USB_DR_MODE_PERIPHERAL || config->dr_mode == USB_DR_MODE_OTG)
+		phytium_gadget_init(config);
+
+	dev_set_drvdata(config->dev, config);
+
+	return 0;
+}
+
+static void phytium_pci_remove(struct pci_dev *pdev)
+{
+	struct phytium_cusb *config = dev_get_drvdata(&pdev->dev);
+
+	phytium_get_dr_mode(config);
+	if (config->dr_mode == USB_DR_MODE_HOST || config->dr_mode == USB_DR_MODE_OTG)
+		phytium_host_uninit(config);
+
+	if (config->dr_mode == USB_DR_MODE_PERIPHERAL || config->dr_mode == USB_DR_MODE_OTG)
+		phytium_gadget_uninit(config);
+
+	if (config->dr_mode == USB_DR_MODE_PERIPHERAL || config->dr_mode == USB_DR_MODE_OTG)
+		usb_del_gadget_udc(&config->gadget);
+
+	dev_set_drvdata(&pdev->dev, NULL);
+	pr_info("%s %d\n", __func__, __LINE__);
+}
+
+static void phytium_pci_shutdown(struct pci_dev *pdev)
+{
+	struct phytium_cusb *config;
+
+	config = dev_get_drvdata(&pdev->dev);
+
+	phytium_get_dr_mode(config);
+
+	if (config->dr_mode == USB_DR_MODE_PERIPHERAL || config->dr_mode == USB_DR_MODE_OTG)
+		usb_del_gadget_udc(&config->gadget);
+}
+
+#ifdef CONFIG_PM
+static int phytium_pci_resume(struct pci_dev *pdev)
+{
+	unsigned long flags = 0;
+	struct phytium_cusb *config;
+	int ret = 0;
+
+	config = dev_get_drvdata(&pdev->dev);
+
+	spin_lock_irqsave(&config->lock, flags);
+	ret = phytium_host_resume(config);
+	spin_unlock_irqrestore(&config->lock, flags);
+
+	return ret;
+}
+
+static int phytium_pci_suspend(struct pci_dev *pdev, pm_message_t state)
+{
+	unsigned long flags = 0;
+	struct phytium_cusb *config;
+	int ret;
+
+	config = dev_get_drvdata(&pdev->dev);
+
+	spin_lock_irqsave(&config->lock, flags);
+	ret = phytium_host_suspend(config);
+	spin_unlock_irqrestore(&config->lock, flags);
+
+	return 0;
+}
+#endif
+
+const struct pci_device_id phytium_pci_id_table[] = {
+	{0x10ee, 0x8012, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},
+	{}
+};
+
+static struct pci_driver phytium_otg_driver = {
+	.name = "phytium_usb",
+	.id_table = phytium_pci_id_table,
+	.probe = phytium_pci_probe,
+	.remove = phytium_pci_remove,
+	.shutdown = phytium_pci_shutdown,
+#ifdef CONFIG_PM
+	.resume	= phytium_pci_resume,
+	.suspend = phytium_pci_suspend,
+#endif
+};
+
+module_pci_driver(phytium_otg_driver);
+
+MODULE_AUTHOR("Chen Zhenhua <chenzhenhua@phytium.com.cn>");
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Phytium usb pci wrapper");
diff --git a/drivers/usb/phytium/platform.c b/drivers/usb/phytium/platform.c
new file mode 100644
index 0000000000000..e89e3b14e9e9c
--- /dev/null
+++ b/drivers/usb/phytium/platform.c
@@ -0,0 +1,216 @@
+// SPDX-License-Identifier: GPL-2.0
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/io.h>
+#include <linux/platform_device.h>
+#include <linux/of_platform.h>
+#include <linux/dma-mapping.h>
+#include <linux/usb/otg.h>
+#include <linux/acpi.h>
+#include "core.h"
+#include "hw-regs.h"
+
+#define PHYTIUM_OTG_USB_LOADED  3
+#define USB2_2_BASE_ADDRESS 0x31800000
+
+static const struct of_device_id phytium_otg_of_match[] = {
+	{
+		.compatible = "phytium,usb2",
+	},
+	{},
+};
+MODULE_DEVICE_TABLE(of, phytium_otg_of_match);
+
+#ifdef CONFIG_ACPI
+static const struct acpi_device_id phytium_otg_acpi_match[] = {
+	{ "PHYT0037", 0 },
+	{ }
+};
+MODULE_DEVICE_TABLE(acpi, phytium_otg_acpi_match);
+#endif
+
+static int phytium_get_dr_mode(struct phytium_cusb *config)
+{
+	config->dr_mode = usb_get_dr_mode(config->dev);
+	if (config->dr_mode == USB_DR_MODE_UNKNOWN)
+		config->dr_mode = USB_DR_MODE_PERIPHERAL;
+
+	return 0;
+}
+
+static irqreturn_t platform_usb_irq(int irq, void *dev_id)
+{
+	unsigned long flags;
+	uint8_t otgstate;
+
+	struct phytium_cusb *config = (struct phytium_cusb *)dev_id;
+	struct GADGET_CTRL *gadget_ctrl = config->gadget_priv;
+	struct HOST_CTRL *host_ctrl = config->host_priv;
+
+	if (gadget_ctrl || host_ctrl) {
+		if (host_ctrl)
+			otgstate = phytium_read8(&host_ctrl->regs->otgstate);
+		else
+			otgstate = phytium_read8(&gadget_ctrl->regs->otgstate);
+
+		spin_lock_irqsave(&config->lock, flags);
+		if (otgstate > HOST_OTG_STATE_A_WAIT_VFALL)
+			config->gadget_obj->gadget_isr(config->gadget_priv);
+		else
+			config->host_obj->host_isr(config->host_priv);
+		spin_unlock_irqrestore(&config->lock, flags);
+	}
+
+	return IRQ_HANDLED;
+}
+
+static int phytium_driver_probe(struct platform_device *pdev)
+{
+	struct phytium_cusb *config;
+	struct resource *res, *phy_res;
+	int retval = 0;
+
+	config = devm_kzalloc(&pdev->dev, sizeof(*config), GFP_KERNEL);
+	if (!config)
+		return -ENOMEM;
+
+	spin_lock_init(&config->lock);
+	config->dev = &pdev->dev;
+	config->isVhubHost = false;
+
+	config->irq = platform_get_irq(pdev, 0);
+	if (config->irq <= 0) {
+		dev_err(config->dev, "getting usb irq failed\n");
+		return config->irq;
+	}
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	config->regs = devm_ioremap(&pdev->dev, res->start, resource_size(res));
+	if (IS_ERR(config->regs)) {
+		dev_err(config->dev, "map IOMEM resource failed\n");
+		return -1;
+	}
+
+	phy_res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+	config->phy_regs = devm_ioremap(&pdev->dev, phy_res->start, resource_size(phy_res));
+	if (IS_ERR(config->phy_regs)) {
+		dev_err(config->dev, "map IOMEM phy resource failed\n");
+		return -1;
+	}
+
+	phytium_get_dr_mode(config);
+
+	phytium_core_reset(config, false);
+
+	if (config->dr_mode == USB_DR_MODE_HOST ||
+	    config->dr_mode == USB_DR_MODE_OTG) {
+		if (res->start == USB2_2_BASE_ADDRESS)
+			config->isVhubHost = true;
+
+		phytium_host_init(config);
+	}
+
+	if (config->dr_mode == USB_DR_MODE_PERIPHERAL ||
+			config->dr_mode == USB_DR_MODE_OTG)
+		phytium_gadget_init(config);
+
+	dev_set_drvdata(&pdev->dev, config);
+
+	if (config->irq > 0) {
+		retval = devm_request_irq(config->dev, config->irq, platform_usb_irq,
+				IRQF_SHARED, "phytium_otg", config);
+		if (retval != 0) {
+			dev_err(config->dev, "request irq %d err %d\n", config->irq, retval);
+			config->irq = 0;
+		}
+	}
+
+	return retval;
+}
+
+static int phytium_driver_remove(struct platform_device *dev)
+{
+	struct phytium_cusb *config = platform_get_drvdata(dev);
+
+	if (!config)
+		return 0;
+
+	phytium_get_dr_mode(config);
+
+	if (config->dr_mode == USB_DR_MODE_HOST ||
+			config->dr_mode == USB_DR_MODE_OTG)
+		phytium_host_uninit(config);
+
+	if (config->dr_mode == USB_DR_MODE_PERIPHERAL ||
+			config->dr_mode == USB_DR_MODE_OTG)
+		phytium_gadget_uninit(config);
+
+	dev_set_drvdata(&dev->dev, NULL);
+	return 0;
+}
+
+static void phytium_driver_shutdown(struct platform_device *dev)
+{
+	pr_info("%s %d\n", __func__, __LINE__);
+}
+
+#ifdef CONFIG_PM
+static int phytium_driver_suspend(struct device *dev)
+{
+	struct phytium_cusb *config;
+	int ret = 0;
+
+	config = dev_get_drvdata(dev);
+
+	if (config->dr_mode == USB_DR_MODE_HOST ||
+			config->dr_mode == USB_DR_MODE_OTG)
+		ret = phytium_host_suspend(config);
+
+	if (config->dr_mode == USB_DR_MODE_PERIPHERAL ||
+			config->dr_mode == USB_DR_MODE_OTG)
+		ret = phytium_gadget_suspend(config);
+
+	return ret;
+}
+
+static int phytium_driver_resume(struct device *dev)
+{
+	struct phytium_cusb *config;
+	int ret = 0;
+
+	config = dev_get_drvdata(dev);
+	if (config->dr_mode == USB_DR_MODE_HOST ||
+			config->dr_mode == USB_DR_MODE_OTG)
+		ret = phytium_host_resume(config);
+
+	if (config->dr_mode == USB_DR_MODE_PERIPHERAL ||
+			config->dr_mode == USB_DR_MODE_OTG)
+		ret = phytium_gadget_resume(config);
+
+	return ret;
+}
+
+static const struct dev_pm_ops phytium_usb_pm_ops = {
+	SET_SYSTEM_SLEEP_PM_OPS(phytium_driver_suspend, phytium_driver_resume)
+};
+#endif
+
+static struct platform_driver phytium_otg_driver = {
+	.driver	=	{
+		.name	= "phytium-otg",
+		.of_match_table	= of_match_ptr(phytium_otg_of_match),
+		.acpi_match_table = ACPI_PTR(phytium_otg_acpi_match),
+#ifdef CONFIG_PM
+		.pm	= &phytium_usb_pm_ops,
+#endif
+	},
+	.probe	= phytium_driver_probe,
+	.remove	= phytium_driver_remove,
+	.shutdown	= phytium_driver_shutdown,
+};
+
+module_platform_driver(phytium_otg_driver);
+
+MODULE_AUTHOR("Chen Zhenhua <chenzhenhua@phytium.com.cn>");
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Phytium usb platform wrapper");