v4.19.13 snapshot.
diff --git a/drivers/usb/chipidea/Kconfig b/drivers/usb/chipidea/Kconfig
new file mode 100644
index 0000000..ee34e90
--- /dev/null
+++ b/drivers/usb/chipidea/Kconfig
@@ -0,0 +1,42 @@
+config USB_CHIPIDEA
+	tristate "ChipIdea Highspeed Dual Role Controller"
+	depends on ((USB_EHCI_HCD && USB_GADGET) || (USB_EHCI_HCD && !USB_GADGET) || (!USB_EHCI_HCD && USB_GADGET)) && HAS_DMA
+	select EXTCON
+	select RESET_CONTROLLER
+	select USB_ULPI_BUS
+	help
+	  Say Y here if your system has a dual role high speed USB
+	  controller based on ChipIdea silicon IP. It supports:
+	  Dual-role switch (ID, OTG FSM, sysfs), Host-only, and
+	  Peripheral-only.
+
+	  When compiled dynamically, the module will be called ci_hdrc.ko.
+
+if USB_CHIPIDEA
+
+config USB_CHIPIDEA_OF
+	tristate
+	depends on OF
+	default USB_CHIPIDEA
+
+config USB_CHIPIDEA_PCI
+	tristate
+	depends on USB_PCI
+	depends on NOP_USB_XCEIV
+	default USB_CHIPIDEA
+
+config USB_CHIPIDEA_UDC
+	bool "ChipIdea device controller"
+	depends on USB_GADGET
+	help
+	  Say Y here to enable device controller functionality of the
+	  ChipIdea driver.
+
+config USB_CHIPIDEA_HOST
+	bool "ChipIdea host controller"
+	depends on USB_EHCI_HCD
+	select USB_EHCI_ROOT_HUB_TT
+	help
+	  Say Y here to enable host controller functionality of the
+	  ChipIdea driver.
+endif
diff --git a/drivers/usb/chipidea/Makefile b/drivers/usb/chipidea/Makefile
new file mode 100644
index 0000000..12df94f
--- /dev/null
+++ b/drivers/usb/chipidea/Makefile
@@ -0,0 +1,18 @@
+# SPDX-License-Identifier: GPL-2.0
+obj-$(CONFIG_USB_CHIPIDEA)		+= ci_hdrc.o
+
+ci_hdrc-y				:= core.o otg.o debug.o ulpi.o
+ci_hdrc-$(CONFIG_USB_CHIPIDEA_UDC)	+= udc.o
+ci_hdrc-$(CONFIG_USB_CHIPIDEA_HOST)	+= host.o
+ci_hdrc-$(CONFIG_USB_OTG_FSM)		+= otg_fsm.o
+
+# Glue/Bridge layers go here
+
+obj-$(CONFIG_USB_CHIPIDEA)	+= ci_hdrc_usb2.o
+obj-$(CONFIG_USB_CHIPIDEA)	+= ci_hdrc_msm.o
+obj-$(CONFIG_USB_CHIPIDEA)	+= ci_hdrc_zevio.o
+
+obj-$(CONFIG_USB_CHIPIDEA_PCI)	+= ci_hdrc_pci.o
+
+obj-$(CONFIG_USB_CHIPIDEA_OF)	+= usbmisc_imx.o ci_hdrc_imx.o
+obj-$(CONFIG_USB_CHIPIDEA_OF)	+= ci_hdrc_tegra.o
diff --git a/drivers/usb/chipidea/bits.h b/drivers/usb/chipidea/bits.h
new file mode 100644
index 0000000..98da995
--- /dev/null
+++ b/drivers/usb/chipidea/bits.h
@@ -0,0 +1,147 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * bits.h - register bits of the ChipIdea USB IP core
+ *
+ * Copyright (C) 2008 Chipidea - MIPS Technologies, Inc. All rights reserved.
+ *
+ * Author: David Lopo
+ */
+
+#ifndef __DRIVERS_USB_CHIPIDEA_BITS_H
+#define __DRIVERS_USB_CHIPIDEA_BITS_H
+
+#include <linux/usb/ehci_def.h>
+
+/*
+ * ID
+ * For 1.x revision, bit24 - bit31 are reserved
+ * For 2.x revision, bit25 - bit28 are 0x2
+ */
+#define TAG		      (0x1F << 16)
+#define REVISION	      (0xF << 21)
+#define VERSION		      (0xF << 25)
+#define CIVERSION	      (0x7 << 29)
+
+/* SBUSCFG */
+#define AHBBRST_MASK		0x7
+
+/* HCCPARAMS */
+#define HCCPARAMS_LEN         BIT(17)
+
+/* DCCPARAMS */
+#define DCCPARAMS_DEN         (0x1F << 0)
+#define DCCPARAMS_DC          BIT(7)
+#define DCCPARAMS_HC          BIT(8)
+
+/* TESTMODE */
+#define TESTMODE_FORCE        BIT(0)
+
+/* USBCMD */
+#define USBCMD_RS             BIT(0)
+#define USBCMD_RST            BIT(1)
+#define USBCMD_SUTW           BIT(13)
+#define USBCMD_ATDTW          BIT(14)
+
+/* USBSTS & USBINTR */
+#define USBi_UI               BIT(0)
+#define USBi_UEI              BIT(1)
+#define USBi_PCI              BIT(2)
+#define USBi_URI              BIT(6)
+#define USBi_SLI              BIT(8)
+
+/* DEVICEADDR */
+#define DEVICEADDR_USBADRA    BIT(24)
+#define DEVICEADDR_USBADR     (0x7FUL << 25)
+
+/* TTCTRL */
+#define TTCTRL_TTHA_MASK	(0x7fUL << 24)
+/* Set non-zero value for internal TT Hub address representation */
+#define TTCTRL_TTHA		(0x7fUL << 24)
+
+/* BURSTSIZE */
+#define RX_BURST_MASK		0xff
+#define TX_BURST_MASK		0xff00
+
+/* PORTSC */
+#define PORTSC_CCS            BIT(0)
+#define PORTSC_CSC            BIT(1)
+#define PORTSC_PEC            BIT(3)
+#define PORTSC_OCC            BIT(5)
+#define PORTSC_FPR            BIT(6)
+#define PORTSC_SUSP           BIT(7)
+#define PORTSC_HSP            BIT(9)
+#define PORTSC_PP             BIT(12)
+#define PORTSC_PTC            (0x0FUL << 16)
+#define PORTSC_WKCN           BIT(20)
+#define PORTSC_PHCD(d)	      ((d) ? BIT(22) : BIT(23))
+/* PTS and PTW for non lpm version only */
+#define PORTSC_PFSC           BIT(24)
+#define PORTSC_PTS(d)						\
+	(u32)((((d) & 0x3) << 30) | (((d) & 0x4) ? BIT(25) : 0))
+#define PORTSC_PTW            BIT(28)
+#define PORTSC_STS            BIT(29)
+
+#define PORTSC_W1C_BITS						\
+	(PORTSC_CSC | PORTSC_PEC | PORTSC_OCC)
+
+/* DEVLC */
+#define DEVLC_PFSC            BIT(23)
+#define DEVLC_PSPD            (0x03UL << 25)
+#define DEVLC_PSPD_HS         (0x02UL << 25)
+#define DEVLC_PTW             BIT(27)
+#define DEVLC_STS             BIT(28)
+#define DEVLC_PTS(d)          (u32)(((d) & 0x7) << 29)
+
+/* Encoding for DEVLC_PTS and PORTSC_PTS */
+#define PTS_UTMI              0
+#define PTS_ULPI              2
+#define PTS_SERIAL            3
+#define PTS_HSIC              4
+
+/* OTGSC */
+#define OTGSC_IDPU	      BIT(5)
+#define OTGSC_HADP	      BIT(6)
+#define OTGSC_HABA	      BIT(7)
+#define OTGSC_ID	      BIT(8)
+#define OTGSC_AVV	      BIT(9)
+#define OTGSC_ASV	      BIT(10)
+#define OTGSC_BSV	      BIT(11)
+#define OTGSC_BSE	      BIT(12)
+#define OTGSC_IDIS	      BIT(16)
+#define OTGSC_AVVIS	      BIT(17)
+#define OTGSC_ASVIS	      BIT(18)
+#define OTGSC_BSVIS	      BIT(19)
+#define OTGSC_BSEIS	      BIT(20)
+#define OTGSC_1MSIS	      BIT(21)
+#define OTGSC_DPIS	      BIT(22)
+#define OTGSC_IDIE	      BIT(24)
+#define OTGSC_AVVIE	      BIT(25)
+#define OTGSC_ASVIE	      BIT(26)
+#define OTGSC_BSVIE	      BIT(27)
+#define OTGSC_BSEIE	      BIT(28)
+#define OTGSC_1MSIE	      BIT(29)
+#define OTGSC_DPIE	      BIT(30)
+#define OTGSC_INT_EN_BITS	(OTGSC_IDIE | OTGSC_AVVIE | OTGSC_ASVIE \
+				| OTGSC_BSVIE | OTGSC_BSEIE | OTGSC_1MSIE \
+				| OTGSC_DPIE)
+#define OTGSC_INT_STATUS_BITS	(OTGSC_IDIS | OTGSC_AVVIS | OTGSC_ASVIS	\
+				| OTGSC_BSVIS | OTGSC_BSEIS | OTGSC_1MSIS \
+				| OTGSC_DPIS)
+
+/* USBMODE */
+#define USBMODE_CM            (0x03UL <<  0)
+#define USBMODE_CM_DC         (0x02UL <<  0)
+#define USBMODE_SLOM          BIT(3)
+#define USBMODE_CI_SDIS       BIT(4)
+
+/* ENDPTCTRL */
+#define ENDPTCTRL_RXS         BIT(0)
+#define ENDPTCTRL_RXT         (0x03UL <<  2)
+#define ENDPTCTRL_RXR         BIT(6)         /* reserved for port 0 */
+#define ENDPTCTRL_RXE         BIT(7)
+#define ENDPTCTRL_TXS         BIT(16)
+#define ENDPTCTRL_TXT         (0x03UL << 18)
+#define ENDPTCTRL_TXR         BIT(22)        /* reserved for port 0 */
+#define ENDPTCTRL_TXE         BIT(23)
+
+#endif /* __DRIVERS_USB_CHIPIDEA_BITS_H */
diff --git a/drivers/usb/chipidea/ci.h b/drivers/usb/chipidea/ci.h
new file mode 100644
index 0000000..6a2cc5c
--- /dev/null
+++ b/drivers/usb/chipidea/ci.h
@@ -0,0 +1,448 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * ci.h - common structures, functions, and macros of the ChipIdea driver
+ *
+ * Copyright (C) 2008 Chipidea - MIPS Technologies, Inc. All rights reserved.
+ *
+ * Author: David Lopo
+ */
+
+#ifndef __DRIVERS_USB_CHIPIDEA_CI_H
+#define __DRIVERS_USB_CHIPIDEA_CI_H
+
+#include <linux/list.h>
+#include <linux/irqreturn.h>
+#include <linux/usb.h>
+#include <linux/usb/gadget.h>
+#include <linux/usb/otg-fsm.h>
+#include <linux/usb/otg.h>
+#include <linux/ulpi/interface.h>
+
+/******************************************************************************
+ * DEFINE
+ *****************************************************************************/
+#define TD_PAGE_COUNT      5
+#define CI_HDRC_PAGE_SIZE  4096ul /* page size for TD's */
+#define ENDPT_MAX          32
+
+/******************************************************************************
+ * REGISTERS
+ *****************************************************************************/
+/* Identification Registers */
+#define ID_ID				0x0
+#define ID_HWGENERAL			0x4
+#define ID_HWHOST			0x8
+#define ID_HWDEVICE			0xc
+#define ID_HWTXBUF			0x10
+#define ID_HWRXBUF			0x14
+#define ID_SBUSCFG			0x90
+
+/* register indices */
+enum ci_hw_regs {
+	CAP_CAPLENGTH,
+	CAP_HCCPARAMS,
+	CAP_DCCPARAMS,
+	CAP_TESTMODE,
+	CAP_LAST = CAP_TESTMODE,
+	OP_USBCMD,
+	OP_USBSTS,
+	OP_USBINTR,
+	OP_DEVICEADDR,
+	OP_ENDPTLISTADDR,
+	OP_TTCTRL,
+	OP_BURSTSIZE,
+	OP_ULPI_VIEWPORT,
+	OP_PORTSC,
+	OP_DEVLC,
+	OP_OTGSC,
+	OP_USBMODE,
+	OP_ENDPTSETUPSTAT,
+	OP_ENDPTPRIME,
+	OP_ENDPTFLUSH,
+	OP_ENDPTSTAT,
+	OP_ENDPTCOMPLETE,
+	OP_ENDPTCTRL,
+	/* endptctrl1..15 follow */
+	OP_LAST = OP_ENDPTCTRL + ENDPT_MAX / 2,
+};
+
+/******************************************************************************
+ * STRUCTURES
+ *****************************************************************************/
+/**
+ * struct ci_hw_ep - endpoint representation
+ * @ep: endpoint structure for gadget drivers
+ * @dir: endpoint direction (TX/RX)
+ * @num: endpoint number
+ * @type: endpoint type
+ * @name: string description of the endpoint
+ * @qh: queue head for this endpoint
+ * @wedge: is the endpoint wedged
+ * @ci: pointer to the controller
+ * @lock: pointer to controller's spinlock
+ * @td_pool: pointer to controller's TD pool
+ */
+struct ci_hw_ep {
+	struct usb_ep				ep;
+	u8					dir;
+	u8					num;
+	u8					type;
+	char					name[16];
+	struct {
+		struct list_head	queue;
+		struct ci_hw_qh		*ptr;
+		dma_addr_t		dma;
+	}					qh;
+	int					wedge;
+
+	/* global resources */
+	struct ci_hdrc				*ci;
+	spinlock_t				*lock;
+	struct dma_pool				*td_pool;
+	struct td_node				*pending_td;
+};
+
+enum ci_role {
+	CI_ROLE_HOST = 0,
+	CI_ROLE_GADGET,
+	CI_ROLE_END,
+};
+
+enum ci_revision {
+	CI_REVISION_1X = 10,	/* Revision 1.x */
+	CI_REVISION_20 = 20, /* Revision 2.0 */
+	CI_REVISION_21, /* Revision 2.1 */
+	CI_REVISION_22, /* Revision 2.2 */
+	CI_REVISION_23, /* Revision 2.3 */
+	CI_REVISION_24, /* Revision 2.4 */
+	CI_REVISION_25, /* Revision 2.5 */
+	CI_REVISION_25_PLUS, /* Revision above than 2.5 */
+	CI_REVISION_UNKNOWN = 99, /* Unknown Revision */
+};
+
+/**
+ * struct ci_role_driver - host/gadget role driver
+ * @start: start this role
+ * @stop: stop this role
+ * @irq: irq handler for this role
+ * @name: role name string (host/gadget)
+ */
+struct ci_role_driver {
+	int		(*start)(struct ci_hdrc *);
+	void		(*stop)(struct ci_hdrc *);
+	irqreturn_t	(*irq)(struct ci_hdrc *);
+	const char	*name;
+};
+
+/**
+ * struct hw_bank - hardware register mapping representation
+ * @lpm: set if the device is LPM capable
+ * @phys: physical address of the controller's registers
+ * @abs: absolute address of the beginning of register window
+ * @cap: capability registers
+ * @op: operational registers
+ * @size: size of the register window
+ * @regmap: register lookup table
+ */
+struct hw_bank {
+	unsigned	lpm;
+	resource_size_t	phys;
+	void __iomem	*abs;
+	void __iomem	*cap;
+	void __iomem	*op;
+	size_t		size;
+	void __iomem	*regmap[OP_LAST + 1];
+};
+
+/**
+ * struct ci_hdrc - chipidea device representation
+ * @dev: pointer to parent device
+ * @lock: access synchronization
+ * @hw_bank: hardware register mapping
+ * @irq: IRQ number
+ * @roles: array of supported roles for this controller
+ * @role: current role
+ * @is_otg: if the device is otg-capable
+ * @fsm: otg finite state machine
+ * @otg_fsm_hrtimer: hrtimer for otg fsm timers
+ * @hr_timeouts: time out list for active otg fsm timers
+ * @enabled_otg_timer_bits: bits of enabled otg timers
+ * @next_otg_timer: next nearest enabled timer to be expired
+ * @work: work for role changing
+ * @wq: workqueue thread
+ * @qh_pool: allocation pool for queue heads
+ * @td_pool: allocation pool for transfer descriptors
+ * @gadget: device side representation for peripheral controller
+ * @driver: gadget driver
+ * @resume_state: save the state of gadget suspend from
+ * @hw_ep_max: total number of endpoints supported by hardware
+ * @ci_hw_ep: array of endpoints
+ * @ep0_dir: ep0 direction
+ * @ep0out: pointer to ep0 OUT endpoint
+ * @ep0in: pointer to ep0 IN endpoint
+ * @status: ep0 status request
+ * @setaddr: if we should set the address on status completion
+ * @address: usb address received from the host
+ * @remote_wakeup: host-enabled remote wakeup
+ * @suspended: suspended by host
+ * @test_mode: the selected test mode
+ * @platdata: platform specific information supplied by parent device
+ * @vbus_active: is VBUS active
+ * @ulpi: pointer to ULPI device, if any
+ * @ulpi_ops: ULPI read/write ops for this device
+ * @phy: pointer to PHY, if any
+ * @usb_phy: pointer to USB PHY, if any and if using the USB PHY framework
+ * @hcd: pointer to usb_hcd for ehci host driver
+ * @debugfs: root dentry for this controller in debugfs
+ * @id_event: indicates there is an id event, and handled at ci_otg_work
+ * @b_sess_valid_event: indicates there is a vbus event, and handled
+ * at ci_otg_work
+ * @imx28_write_fix: Freescale imx28 needs swp instruction for writing
+ * @supports_runtime_pm: if runtime pm is supported
+ * @in_lpm: if the core in low power mode
+ * @wakeup_int: if wakeup interrupt occur
+ * @rev: The revision number for controller
+ */
+struct ci_hdrc {
+	struct device			*dev;
+	spinlock_t			lock;
+	struct hw_bank			hw_bank;
+	int				irq;
+	struct ci_role_driver		*roles[CI_ROLE_END];
+	enum ci_role			role;
+	bool				is_otg;
+	struct usb_otg			otg;
+	struct otg_fsm			fsm;
+	struct hrtimer			otg_fsm_hrtimer;
+	ktime_t				hr_timeouts[NUM_OTG_FSM_TIMERS];
+	unsigned			enabled_otg_timer_bits;
+	enum otg_fsm_timer		next_otg_timer;
+	struct work_struct		work;
+	struct workqueue_struct		*wq;
+
+	struct dma_pool			*qh_pool;
+	struct dma_pool			*td_pool;
+
+	struct usb_gadget		gadget;
+	struct usb_gadget_driver	*driver;
+	enum usb_device_state		resume_state;
+	unsigned			hw_ep_max;
+	struct ci_hw_ep			ci_hw_ep[ENDPT_MAX];
+	u32				ep0_dir;
+	struct ci_hw_ep			*ep0out, *ep0in;
+
+	struct usb_request		*status;
+	bool				setaddr;
+	u8				address;
+	u8				remote_wakeup;
+	u8				suspended;
+	u8				test_mode;
+
+	struct ci_hdrc_platform_data	*platdata;
+	int				vbus_active;
+	struct ulpi			*ulpi;
+	struct ulpi_ops 		ulpi_ops;
+	struct phy			*phy;
+	/* old usb_phy interface */
+	struct usb_phy			*usb_phy;
+	struct usb_hcd			*hcd;
+	struct dentry			*debugfs;
+	bool				id_event;
+	bool				b_sess_valid_event;
+	bool				imx28_write_fix;
+	bool				supports_runtime_pm;
+	bool				in_lpm;
+	bool				wakeup_int;
+	enum ci_revision		rev;
+};
+
+static inline struct ci_role_driver *ci_role(struct ci_hdrc *ci)
+{
+	BUG_ON(ci->role >= CI_ROLE_END || !ci->roles[ci->role]);
+	return ci->roles[ci->role];
+}
+
+static inline int ci_role_start(struct ci_hdrc *ci, enum ci_role role)
+{
+	int ret;
+
+	if (role >= CI_ROLE_END)
+		return -EINVAL;
+
+	if (!ci->roles[role])
+		return -ENXIO;
+
+	ret = ci->roles[role]->start(ci);
+	if (!ret)
+		ci->role = role;
+	return ret;
+}
+
+static inline void ci_role_stop(struct ci_hdrc *ci)
+{
+	enum ci_role role = ci->role;
+
+	if (role == CI_ROLE_END)
+		return;
+
+	ci->role = CI_ROLE_END;
+
+	ci->roles[role]->stop(ci);
+}
+
+/**
+ * hw_read_id_reg: reads from a identification register
+ * @ci: the controller
+ * @offset: offset from the beginning of identification registers region
+ * @mask: bitfield mask
+ *
+ * This function returns register contents
+ */
+static inline u32 hw_read_id_reg(struct ci_hdrc *ci, u32 offset, u32 mask)
+{
+	return ioread32(ci->hw_bank.abs + offset) & mask;
+}
+
+/**
+ * hw_write_id_reg: writes to a identification register
+ * @ci: the controller
+ * @offset: offset from the beginning of identification registers region
+ * @mask: bitfield mask
+ * @data: new value
+ */
+static inline void hw_write_id_reg(struct ci_hdrc *ci, u32 offset,
+			    u32 mask, u32 data)
+{
+	if (~mask)
+		data = (ioread32(ci->hw_bank.abs + offset) & ~mask)
+			| (data & mask);
+
+	iowrite32(data, ci->hw_bank.abs + offset);
+}
+
+/**
+ * hw_read: reads from a hw register
+ * @ci: the controller
+ * @reg:  register index
+ * @mask: bitfield mask
+ *
+ * This function returns register contents
+ */
+static inline u32 hw_read(struct ci_hdrc *ci, enum ci_hw_regs reg, u32 mask)
+{
+	return ioread32(ci->hw_bank.regmap[reg]) & mask;
+}
+
+#ifdef CONFIG_SOC_IMX28
+static inline void imx28_ci_writel(u32 val, volatile void __iomem *addr)
+{
+	__asm__ ("swp %0, %0, [%1]" : : "r"(val), "r"(addr));
+}
+#else
+static inline void imx28_ci_writel(u32 val, volatile void __iomem *addr)
+{
+}
+#endif
+
+static inline void __hw_write(struct ci_hdrc *ci, u32 val,
+		void __iomem *addr)
+{
+	if (ci->imx28_write_fix)
+		imx28_ci_writel(val, addr);
+	else
+		iowrite32(val, addr);
+}
+
+/**
+ * hw_write: writes to a hw register
+ * @ci: the controller
+ * @reg:  register index
+ * @mask: bitfield mask
+ * @data: new value
+ */
+static inline void hw_write(struct ci_hdrc *ci, enum ci_hw_regs reg,
+			    u32 mask, u32 data)
+{
+	if (~mask)
+		data = (ioread32(ci->hw_bank.regmap[reg]) & ~mask)
+			| (data & mask);
+
+	__hw_write(ci, data, ci->hw_bank.regmap[reg]);
+}
+
+/**
+ * hw_test_and_clear: tests & clears a hw register
+ * @ci: the controller
+ * @reg:  register index
+ * @mask: bitfield mask
+ *
+ * This function returns register contents
+ */
+static inline u32 hw_test_and_clear(struct ci_hdrc *ci, enum ci_hw_regs reg,
+				    u32 mask)
+{
+	u32 val = ioread32(ci->hw_bank.regmap[reg]) & mask;
+
+	__hw_write(ci, val, ci->hw_bank.regmap[reg]);
+	return val;
+}
+
+/**
+ * hw_test_and_write: tests & writes a hw register
+ * @ci: the controller
+ * @reg:  register index
+ * @mask: bitfield mask
+ * @data: new value
+ *
+ * This function returns register contents
+ */
+static inline u32 hw_test_and_write(struct ci_hdrc *ci, enum ci_hw_regs reg,
+				    u32 mask, u32 data)
+{
+	u32 val = hw_read(ci, reg, ~0);
+
+	hw_write(ci, reg, mask, data);
+	return (val & mask) >> __ffs(mask);
+}
+
+/**
+ * ci_otg_is_fsm_mode: runtime check if otg controller
+ * is in otg fsm mode.
+ *
+ * @ci: chipidea device
+ */
+static inline bool ci_otg_is_fsm_mode(struct ci_hdrc *ci)
+{
+#ifdef CONFIG_USB_OTG_FSM
+	struct usb_otg_caps *otg_caps = &ci->platdata->ci_otg_caps;
+
+	return ci->is_otg && ci->roles[CI_ROLE_HOST] &&
+		ci->roles[CI_ROLE_GADGET] && (otg_caps->srp_support ||
+		otg_caps->hnp_support || otg_caps->adp_support);
+#else
+	return false;
+#endif
+}
+
+int ci_ulpi_init(struct ci_hdrc *ci);
+void ci_ulpi_exit(struct ci_hdrc *ci);
+int ci_ulpi_resume(struct ci_hdrc *ci);
+
+u32 hw_read_intr_enable(struct ci_hdrc *ci);
+
+u32 hw_read_intr_status(struct ci_hdrc *ci);
+
+int hw_device_reset(struct ci_hdrc *ci);
+
+int hw_port_test_set(struct ci_hdrc *ci, u8 mode);
+
+u8 hw_port_test_get(struct ci_hdrc *ci);
+
+void hw_phymode_configure(struct ci_hdrc *ci);
+
+void ci_platform_configure(struct ci_hdrc *ci);
+
+void dbg_create_files(struct ci_hdrc *ci);
+
+void dbg_remove_files(struct ci_hdrc *ci);
+#endif	/* __DRIVERS_USB_CHIPIDEA_CI_H */
diff --git a/drivers/usb/chipidea/ci_hdrc_imx.c b/drivers/usb/chipidea/ci_hdrc_imx.c
new file mode 100644
index 0000000..19f5f5f
--- /dev/null
+++ b/drivers/usb/chipidea/ci_hdrc_imx.c
@@ -0,0 +1,498 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright 2012 Freescale Semiconductor, Inc.
+ * Copyright (C) 2012 Marek Vasut <marex@denx.de>
+ * on behalf of DENX Software Engineering GmbH
+ */
+
+#include <linux/module.h>
+#include <linux/of_platform.h>
+#include <linux/of_gpio.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/dma-mapping.h>
+#include <linux/usb/chipidea.h>
+#include <linux/usb/of.h>
+#include <linux/clk.h>
+
+#include "ci.h"
+#include "ci_hdrc_imx.h"
+
+struct ci_hdrc_imx_platform_flag {
+	unsigned int flags;
+};
+
+static const struct ci_hdrc_imx_platform_flag imx23_usb_data = {
+	.flags = CI_HDRC_TURN_VBUS_EARLY_ON |
+		CI_HDRC_DISABLE_STREAMING,
+};
+
+static const struct ci_hdrc_imx_platform_flag imx27_usb_data = {
+	.flags = CI_HDRC_DISABLE_STREAMING,
+};
+
+static const struct ci_hdrc_imx_platform_flag imx28_usb_data = {
+	.flags = CI_HDRC_IMX28_WRITE_FIX |
+		CI_HDRC_TURN_VBUS_EARLY_ON |
+		CI_HDRC_DISABLE_STREAMING,
+};
+
+static const struct ci_hdrc_imx_platform_flag imx6q_usb_data = {
+	.flags = CI_HDRC_SUPPORTS_RUNTIME_PM |
+		CI_HDRC_TURN_VBUS_EARLY_ON |
+		CI_HDRC_DISABLE_STREAMING,
+};
+
+static const struct ci_hdrc_imx_platform_flag imx6sl_usb_data = {
+	.flags = CI_HDRC_SUPPORTS_RUNTIME_PM |
+		CI_HDRC_TURN_VBUS_EARLY_ON |
+		CI_HDRC_DISABLE_HOST_STREAMING,
+};
+
+static const struct ci_hdrc_imx_platform_flag imx6sx_usb_data = {
+	.flags = CI_HDRC_SUPPORTS_RUNTIME_PM |
+		CI_HDRC_TURN_VBUS_EARLY_ON |
+		CI_HDRC_DISABLE_HOST_STREAMING,
+};
+
+static const struct ci_hdrc_imx_platform_flag imx6ul_usb_data = {
+	.flags = CI_HDRC_SUPPORTS_RUNTIME_PM |
+		CI_HDRC_TURN_VBUS_EARLY_ON,
+};
+
+static const struct ci_hdrc_imx_platform_flag imx7d_usb_data = {
+	.flags = CI_HDRC_SUPPORTS_RUNTIME_PM,
+};
+
+static const struct of_device_id ci_hdrc_imx_dt_ids[] = {
+	{ .compatible = "fsl,imx23-usb", .data = &imx23_usb_data},
+	{ .compatible = "fsl,imx28-usb", .data = &imx28_usb_data},
+	{ .compatible = "fsl,imx27-usb", .data = &imx27_usb_data},
+	{ .compatible = "fsl,imx6q-usb", .data = &imx6q_usb_data},
+	{ .compatible = "fsl,imx6sl-usb", .data = &imx6sl_usb_data},
+	{ .compatible = "fsl,imx6sx-usb", .data = &imx6sx_usb_data},
+	{ .compatible = "fsl,imx6ul-usb", .data = &imx6ul_usb_data},
+	{ .compatible = "fsl,imx7d-usb", .data = &imx7d_usb_data},
+	{ /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, ci_hdrc_imx_dt_ids);
+
+struct ci_hdrc_imx_data {
+	struct usb_phy *phy;
+	struct platform_device *ci_pdev;
+	struct clk *clk;
+	struct imx_usbmisc_data *usbmisc_data;
+	bool supports_runtime_pm;
+	bool override_phy_control;
+	bool in_lpm;
+	/* SoC before i.mx6 (except imx23/imx28) needs three clks */
+	bool need_three_clks;
+	struct clk *clk_ipg;
+	struct clk *clk_ahb;
+	struct clk *clk_per;
+	/* --------------------------------- */
+};
+
+/* Common functions shared by usbmisc drivers */
+
+static struct imx_usbmisc_data *usbmisc_get_init_data(struct device *dev)
+{
+	struct platform_device *misc_pdev;
+	struct device_node *np = dev->of_node;
+	struct of_phandle_args args;
+	struct imx_usbmisc_data *data;
+	int ret;
+
+	/*
+	 * In case the fsl,usbmisc property is not present this device doesn't
+	 * need usbmisc. Return NULL (which is no error here)
+	 */
+	if (!of_get_property(np, "fsl,usbmisc", NULL))
+		return NULL;
+
+	data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
+	if (!data)
+		return ERR_PTR(-ENOMEM);
+
+	ret = of_parse_phandle_with_args(np, "fsl,usbmisc", "#index-cells",
+					0, &args);
+	if (ret) {
+		dev_err(dev, "Failed to parse property fsl,usbmisc, errno %d\n",
+			ret);
+		return ERR_PTR(ret);
+	}
+
+	data->index = args.args[0];
+
+	misc_pdev = of_find_device_by_node(args.np);
+	of_node_put(args.np);
+
+	if (!misc_pdev || !platform_get_drvdata(misc_pdev))
+		return ERR_PTR(-EPROBE_DEFER);
+
+	data->dev = &misc_pdev->dev;
+
+	if (of_find_property(np, "disable-over-current", NULL))
+		data->disable_oc = 1;
+
+	if (of_find_property(np, "over-current-active-high", NULL))
+		data->oc_polarity = 1;
+
+	if (of_find_property(np, "external-vbus-divider", NULL))
+		data->evdo = 1;
+
+	if (of_usb_get_phy_mode(np) == USBPHY_INTERFACE_MODE_ULPI)
+		data->ulpi = 1;
+
+	return data;
+}
+
+/* End of common functions shared by usbmisc drivers*/
+static int imx_get_clks(struct device *dev)
+{
+	struct ci_hdrc_imx_data *data = dev_get_drvdata(dev);
+	int ret = 0;
+
+	data->clk_ipg = devm_clk_get(dev, "ipg");
+	if (IS_ERR(data->clk_ipg)) {
+		/* If the platform only needs one clocks */
+		data->clk = devm_clk_get(dev, NULL);
+		if (IS_ERR(data->clk)) {
+			ret = PTR_ERR(data->clk);
+			dev_err(dev,
+				"Failed to get clks, err=%ld,%ld\n",
+				PTR_ERR(data->clk), PTR_ERR(data->clk_ipg));
+			return ret;
+		}
+		return ret;
+	}
+
+	data->clk_ahb = devm_clk_get(dev, "ahb");
+	if (IS_ERR(data->clk_ahb)) {
+		ret = PTR_ERR(data->clk_ahb);
+		dev_err(dev,
+			"Failed to get ahb clock, err=%d\n", ret);
+		return ret;
+	}
+
+	data->clk_per = devm_clk_get(dev, "per");
+	if (IS_ERR(data->clk_per)) {
+		ret = PTR_ERR(data->clk_per);
+		dev_err(dev,
+			"Failed to get per clock, err=%d\n", ret);
+		return ret;
+	}
+
+	data->need_three_clks = true;
+	return ret;
+}
+
+static int imx_prepare_enable_clks(struct device *dev)
+{
+	struct ci_hdrc_imx_data *data = dev_get_drvdata(dev);
+	int ret = 0;
+
+	if (data->need_three_clks) {
+		ret = clk_prepare_enable(data->clk_ipg);
+		if (ret) {
+			dev_err(dev,
+				"Failed to prepare/enable ipg clk, err=%d\n",
+				ret);
+			return ret;
+		}
+
+		ret = clk_prepare_enable(data->clk_ahb);
+		if (ret) {
+			dev_err(dev,
+				"Failed to prepare/enable ahb clk, err=%d\n",
+				ret);
+			clk_disable_unprepare(data->clk_ipg);
+			return ret;
+		}
+
+		ret = clk_prepare_enable(data->clk_per);
+		if (ret) {
+			dev_err(dev,
+				"Failed to prepare/enable per clk, err=%d\n",
+				ret);
+			clk_disable_unprepare(data->clk_ahb);
+			clk_disable_unprepare(data->clk_ipg);
+			return ret;
+		}
+	} else {
+		ret = clk_prepare_enable(data->clk);
+		if (ret) {
+			dev_err(dev,
+				"Failed to prepare/enable clk, err=%d\n",
+				ret);
+			return ret;
+		}
+	}
+
+	return ret;
+}
+
+static void imx_disable_unprepare_clks(struct device *dev)
+{
+	struct ci_hdrc_imx_data *data = dev_get_drvdata(dev);
+
+	if (data->need_three_clks) {
+		clk_disable_unprepare(data->clk_per);
+		clk_disable_unprepare(data->clk_ahb);
+		clk_disable_unprepare(data->clk_ipg);
+	} else {
+		clk_disable_unprepare(data->clk);
+	}
+}
+
+static int ci_hdrc_imx_probe(struct platform_device *pdev)
+{
+	struct ci_hdrc_imx_data *data;
+	struct ci_hdrc_platform_data pdata = {
+		.name		= dev_name(&pdev->dev),
+		.capoffset	= DEF_CAPOFFSET,
+	};
+	int ret;
+	const struct of_device_id *of_id;
+	const struct ci_hdrc_imx_platform_flag *imx_platform_flag;
+	struct device_node *np = pdev->dev.of_node;
+
+	of_id = of_match_device(ci_hdrc_imx_dt_ids, &pdev->dev);
+	if (!of_id)
+		return -ENODEV;
+
+	imx_platform_flag = of_id->data;
+
+	data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL);
+	if (!data)
+		return -ENOMEM;
+
+	platform_set_drvdata(pdev, data);
+	data->usbmisc_data = usbmisc_get_init_data(&pdev->dev);
+	if (IS_ERR(data->usbmisc_data))
+		return PTR_ERR(data->usbmisc_data);
+
+	ret = imx_get_clks(&pdev->dev);
+	if (ret)
+		return ret;
+
+	ret = imx_prepare_enable_clks(&pdev->dev);
+	if (ret)
+		return ret;
+
+	data->phy = devm_usb_get_phy_by_phandle(&pdev->dev, "fsl,usbphy", 0);
+	if (IS_ERR(data->phy)) {
+		ret = PTR_ERR(data->phy);
+		/* Return -EINVAL if no usbphy is available */
+		if (ret == -ENODEV)
+			ret = -EINVAL;
+		goto err_clk;
+	}
+
+	pdata.usb_phy = data->phy;
+
+	if ((of_device_is_compatible(np, "fsl,imx53-usb") ||
+	     of_device_is_compatible(np, "fsl,imx51-usb")) && pdata.usb_phy &&
+	    of_usb_get_phy_mode(np) == USBPHY_INTERFACE_MODE_ULPI) {
+		pdata.flags |= CI_HDRC_OVERRIDE_PHY_CONTROL;
+		data->override_phy_control = true;
+		usb_phy_init(pdata.usb_phy);
+	}
+
+	pdata.flags |= imx_platform_flag->flags;
+	if (pdata.flags & CI_HDRC_SUPPORTS_RUNTIME_PM)
+		data->supports_runtime_pm = true;
+
+	ret = imx_usbmisc_init(data->usbmisc_data);
+	if (ret) {
+		dev_err(&pdev->dev, "usbmisc init failed, ret=%d\n", ret);
+		goto err_clk;
+	}
+
+	data->ci_pdev = ci_hdrc_add_device(&pdev->dev,
+				pdev->resource, pdev->num_resources,
+				&pdata);
+	if (IS_ERR(data->ci_pdev)) {
+		ret = PTR_ERR(data->ci_pdev);
+		if (ret != -EPROBE_DEFER)
+			dev_err(&pdev->dev,
+				"ci_hdrc_add_device failed, err=%d\n", ret);
+		goto err_clk;
+	}
+
+	ret = imx_usbmisc_init_post(data->usbmisc_data);
+	if (ret) {
+		dev_err(&pdev->dev, "usbmisc post failed, ret=%d\n", ret);
+		goto disable_device;
+	}
+
+	if (data->supports_runtime_pm) {
+		pm_runtime_set_active(&pdev->dev);
+		pm_runtime_enable(&pdev->dev);
+	}
+
+	device_set_wakeup_capable(&pdev->dev, true);
+
+	return 0;
+
+disable_device:
+	ci_hdrc_remove_device(data->ci_pdev);
+err_clk:
+	imx_disable_unprepare_clks(&pdev->dev);
+	return ret;
+}
+
+static int ci_hdrc_imx_remove(struct platform_device *pdev)
+{
+	struct ci_hdrc_imx_data *data = platform_get_drvdata(pdev);
+
+	if (data->supports_runtime_pm) {
+		pm_runtime_get_sync(&pdev->dev);
+		pm_runtime_disable(&pdev->dev);
+		pm_runtime_put_noidle(&pdev->dev);
+	}
+	ci_hdrc_remove_device(data->ci_pdev);
+	if (data->override_phy_control)
+		usb_phy_shutdown(data->phy);
+	imx_disable_unprepare_clks(&pdev->dev);
+
+	return 0;
+}
+
+static void ci_hdrc_imx_shutdown(struct platform_device *pdev)
+{
+	ci_hdrc_imx_remove(pdev);
+}
+
+#ifdef CONFIG_PM
+static int imx_controller_suspend(struct device *dev)
+{
+	struct ci_hdrc_imx_data *data = dev_get_drvdata(dev);
+
+	dev_dbg(dev, "at %s\n", __func__);
+
+	imx_disable_unprepare_clks(dev);
+	data->in_lpm = true;
+
+	return 0;
+}
+
+static int imx_controller_resume(struct device *dev)
+{
+	struct ci_hdrc_imx_data *data = dev_get_drvdata(dev);
+	int ret = 0;
+
+	dev_dbg(dev, "at %s\n", __func__);
+
+	if (!data->in_lpm) {
+		WARN_ON(1);
+		return 0;
+	}
+
+	ret = imx_prepare_enable_clks(dev);
+	if (ret)
+		return ret;
+
+	data->in_lpm = false;
+
+	ret = imx_usbmisc_set_wakeup(data->usbmisc_data, false);
+	if (ret) {
+		dev_err(dev, "usbmisc set_wakeup failed, ret=%d\n", ret);
+		goto clk_disable;
+	}
+
+	return 0;
+
+clk_disable:
+	imx_disable_unprepare_clks(dev);
+	return ret;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int ci_hdrc_imx_suspend(struct device *dev)
+{
+	int ret;
+
+	struct ci_hdrc_imx_data *data = dev_get_drvdata(dev);
+
+	if (data->in_lpm)
+		/* The core's suspend doesn't run */
+		return 0;
+
+	if (device_may_wakeup(dev)) {
+		ret = imx_usbmisc_set_wakeup(data->usbmisc_data, true);
+		if (ret) {
+			dev_err(dev, "usbmisc set_wakeup failed, ret=%d\n",
+					ret);
+			return ret;
+		}
+	}
+
+	return imx_controller_suspend(dev);
+}
+
+static int ci_hdrc_imx_resume(struct device *dev)
+{
+	struct ci_hdrc_imx_data *data = dev_get_drvdata(dev);
+	int ret;
+
+	ret = imx_controller_resume(dev);
+	if (!ret && data->supports_runtime_pm) {
+		pm_runtime_disable(dev);
+		pm_runtime_set_active(dev);
+		pm_runtime_enable(dev);
+	}
+
+	return ret;
+}
+#endif /* CONFIG_PM_SLEEP */
+
+static int ci_hdrc_imx_runtime_suspend(struct device *dev)
+{
+	struct ci_hdrc_imx_data *data = dev_get_drvdata(dev);
+	int ret;
+
+	if (data->in_lpm) {
+		WARN_ON(1);
+		return 0;
+	}
+
+	ret = imx_usbmisc_set_wakeup(data->usbmisc_data, true);
+	if (ret) {
+		dev_err(dev, "usbmisc set_wakeup failed, ret=%d\n", ret);
+		return ret;
+	}
+
+	return imx_controller_suspend(dev);
+}
+
+static int ci_hdrc_imx_runtime_resume(struct device *dev)
+{
+	return imx_controller_resume(dev);
+}
+
+#endif /* CONFIG_PM */
+
+static const struct dev_pm_ops ci_hdrc_imx_pm_ops = {
+	SET_SYSTEM_SLEEP_PM_OPS(ci_hdrc_imx_suspend, ci_hdrc_imx_resume)
+	SET_RUNTIME_PM_OPS(ci_hdrc_imx_runtime_suspend,
+			ci_hdrc_imx_runtime_resume, NULL)
+};
+static struct platform_driver ci_hdrc_imx_driver = {
+	.probe = ci_hdrc_imx_probe,
+	.remove = ci_hdrc_imx_remove,
+	.shutdown = ci_hdrc_imx_shutdown,
+	.driver = {
+		.name = "imx_usb",
+		.of_match_table = ci_hdrc_imx_dt_ids,
+		.pm = &ci_hdrc_imx_pm_ops,
+	 },
+};
+
+module_platform_driver(ci_hdrc_imx_driver);
+
+MODULE_ALIAS("platform:imx-usb");
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("CI HDRC i.MX USB binding");
+MODULE_AUTHOR("Marek Vasut <marex@denx.de>");
+MODULE_AUTHOR("Richard Zhao <richard.zhao@freescale.com>");
diff --git a/drivers/usb/chipidea/ci_hdrc_imx.h b/drivers/usb/chipidea/ci_hdrc_imx.h
new file mode 100644
index 0000000..204275f
--- /dev/null
+++ b/drivers/usb/chipidea/ci_hdrc_imx.h
@@ -0,0 +1,23 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright 2012 Freescale Semiconductor, Inc.
+ */
+
+#ifndef __DRIVER_USB_CHIPIDEA_CI_HDRC_IMX_H
+#define __DRIVER_USB_CHIPIDEA_CI_HDRC_IMX_H
+
+struct imx_usbmisc_data {
+	struct device *dev;
+	int index;
+
+	unsigned int disable_oc:1; /* over current detect disabled */
+	unsigned int oc_polarity:1; /* over current polarity if oc enabled */
+	unsigned int evdo:1; /* set external vbus divider option */
+	unsigned int ulpi:1; /* connected to an ULPI phy */
+};
+
+int imx_usbmisc_init(struct imx_usbmisc_data *);
+int imx_usbmisc_init_post(struct imx_usbmisc_data *);
+int imx_usbmisc_set_wakeup(struct imx_usbmisc_data *, bool);
+
+#endif /* __DRIVER_USB_CHIPIDEA_CI_HDRC_IMX_H */
diff --git a/drivers/usb/chipidea/ci_hdrc_msm.c b/drivers/usb/chipidea/ci_hdrc_msm.c
new file mode 100644
index 0000000..8800099
--- /dev/null
+++ b/drivers/usb/chipidea/ci_hdrc_msm.c
@@ -0,0 +1,316 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Copyright (c) 2010, Code Aurora Forum. All rights reserved. */
+
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/usb/chipidea.h>
+#include <linux/clk.h>
+#include <linux/reset.h>
+#include <linux/mfd/syscon.h>
+#include <linux/regmap.h>
+#include <linux/io.h>
+#include <linux/reset-controller.h>
+#include <linux/extcon.h>
+#include <linux/of.h>
+
+#include "ci.h"
+
+#define HS_PHY_AHB_MODE			0x0098
+
+#define HS_PHY_GENCONFIG		0x009c
+#define HS_PHY_TXFIFO_IDLE_FORCE_DIS	BIT(4)
+
+#define HS_PHY_GENCONFIG_2		0x00a0
+#define HS_PHY_SESS_VLD_CTRL_EN		BIT(7)
+#define HS_PHY_ULPI_TX_PKT_EN_CLR_FIX	BIT(19)
+
+#define HSPHY_SESS_VLD_CTRL		BIT(25)
+
+/* Vendor base starts at 0x200 beyond CI base */
+#define HS_PHY_CTRL			0x0040
+#define HS_PHY_SEC_CTRL			0x0078
+#define HS_PHY_DIG_CLAMP_N		BIT(16)
+#define HS_PHY_POR_ASSERT		BIT(0)
+
+struct ci_hdrc_msm {
+	struct platform_device *ci;
+	struct clk *core_clk;
+	struct clk *iface_clk;
+	struct clk *fs_clk;
+	struct ci_hdrc_platform_data pdata;
+	struct reset_controller_dev rcdev;
+	bool secondary_phy;
+	bool hsic;
+	void __iomem *base;
+};
+
+static int
+ci_hdrc_msm_por_reset(struct reset_controller_dev *r, unsigned long id)
+{
+	struct ci_hdrc_msm *ci_msm = container_of(r, struct ci_hdrc_msm, rcdev);
+	void __iomem *addr = ci_msm->base;
+	u32 val;
+
+	if (id)
+		addr += HS_PHY_SEC_CTRL;
+	else
+		addr += HS_PHY_CTRL;
+
+	val = readl_relaxed(addr);
+	val |= HS_PHY_POR_ASSERT;
+	writel(val, addr);
+	/*
+	 * wait for minimum 10 microseconds as suggested by manual.
+	 * Use a slightly larger value since the exact value didn't
+	 * work 100% of the time.
+	 */
+	udelay(12);
+	val &= ~HS_PHY_POR_ASSERT;
+	writel(val, addr);
+
+	return 0;
+}
+
+static const struct reset_control_ops ci_hdrc_msm_reset_ops = {
+	.reset = ci_hdrc_msm_por_reset,
+};
+
+static int ci_hdrc_msm_notify_event(struct ci_hdrc *ci, unsigned event)
+{
+	struct device *dev = ci->dev->parent;
+	struct ci_hdrc_msm *msm_ci = dev_get_drvdata(dev);
+	int ret;
+
+	switch (event) {
+	case CI_HDRC_CONTROLLER_RESET_EVENT:
+		dev_dbg(dev, "CI_HDRC_CONTROLLER_RESET_EVENT received\n");
+
+		hw_phymode_configure(ci);
+		if (msm_ci->secondary_phy) {
+			u32 val = readl_relaxed(msm_ci->base + HS_PHY_SEC_CTRL);
+			val |= HS_PHY_DIG_CLAMP_N;
+			writel_relaxed(val, msm_ci->base + HS_PHY_SEC_CTRL);
+		}
+
+		ret = phy_init(ci->phy);
+		if (ret)
+			return ret;
+
+		ret = phy_power_on(ci->phy);
+		if (ret) {
+			phy_exit(ci->phy);
+			return ret;
+		}
+
+		/* use AHB transactor, allow posted data writes */
+		hw_write_id_reg(ci, HS_PHY_AHB_MODE, 0xffffffff, 0x8);
+
+		/* workaround for rx buffer collision issue */
+		hw_write_id_reg(ci, HS_PHY_GENCONFIG,
+				HS_PHY_TXFIFO_IDLE_FORCE_DIS, 0);
+
+		if (!msm_ci->hsic)
+			hw_write_id_reg(ci, HS_PHY_GENCONFIG_2,
+					HS_PHY_ULPI_TX_PKT_EN_CLR_FIX, 0);
+
+		if (!IS_ERR(ci->platdata->vbus_extcon.edev)) {
+			hw_write_id_reg(ci, HS_PHY_GENCONFIG_2,
+					HS_PHY_SESS_VLD_CTRL_EN,
+					HS_PHY_SESS_VLD_CTRL_EN);
+			hw_write(ci, OP_USBCMD, HSPHY_SESS_VLD_CTRL,
+				 HSPHY_SESS_VLD_CTRL);
+
+		}
+		break;
+	case CI_HDRC_CONTROLLER_STOPPED_EVENT:
+		dev_dbg(dev, "CI_HDRC_CONTROLLER_STOPPED_EVENT received\n");
+		phy_power_off(ci->phy);
+		phy_exit(ci->phy);
+		break;
+	default:
+		dev_dbg(dev, "unknown ci_hdrc event\n");
+		break;
+	}
+
+	return 0;
+}
+
+static int ci_hdrc_msm_mux_phy(struct ci_hdrc_msm *ci,
+			       struct platform_device *pdev)
+{
+	struct regmap *regmap;
+	struct device *dev = &pdev->dev;
+	struct of_phandle_args args;
+	u32 val;
+	int ret;
+
+	ret = of_parse_phandle_with_fixed_args(dev->of_node, "phy-select", 2, 0,
+					       &args);
+	if (ret)
+		return 0;
+
+	regmap = syscon_node_to_regmap(args.np);
+	of_node_put(args.np);
+	if (IS_ERR(regmap))
+		return PTR_ERR(regmap);
+
+	ret = regmap_write(regmap, args.args[0], args.args[1]);
+	if (ret)
+		return ret;
+
+	ci->secondary_phy = !!args.args[1];
+	if (ci->secondary_phy) {
+		val = readl_relaxed(ci->base + HS_PHY_SEC_CTRL);
+		val |= HS_PHY_DIG_CLAMP_N;
+		writel_relaxed(val, ci->base + HS_PHY_SEC_CTRL);
+	}
+
+	return 0;
+}
+
+static int ci_hdrc_msm_probe(struct platform_device *pdev)
+{
+	struct ci_hdrc_msm *ci;
+	struct platform_device *plat_ci;
+	struct clk *clk;
+	struct reset_control *reset;
+	struct resource *res;
+	int ret;
+	struct device_node *ulpi_node, *phy_node;
+
+	dev_dbg(&pdev->dev, "ci_hdrc_msm_probe\n");
+
+	ci = devm_kzalloc(&pdev->dev, sizeof(*ci), GFP_KERNEL);
+	if (!ci)
+		return -ENOMEM;
+	platform_set_drvdata(pdev, ci);
+
+	ci->pdata.name = "ci_hdrc_msm";
+	ci->pdata.capoffset = DEF_CAPOFFSET;
+	ci->pdata.flags	= CI_HDRC_REGS_SHARED | CI_HDRC_DISABLE_STREAMING |
+			  CI_HDRC_OVERRIDE_AHB_BURST |
+			  CI_HDRC_OVERRIDE_PHY_CONTROL;
+	ci->pdata.notify_event = ci_hdrc_msm_notify_event;
+
+	reset = devm_reset_control_get(&pdev->dev, "core");
+	if (IS_ERR(reset))
+		return PTR_ERR(reset);
+
+	ci->core_clk = clk = devm_clk_get(&pdev->dev, "core");
+	if (IS_ERR(clk))
+		return PTR_ERR(clk);
+
+	ci->iface_clk = clk = devm_clk_get(&pdev->dev, "iface");
+	if (IS_ERR(clk))
+		return PTR_ERR(clk);
+
+	ci->fs_clk = clk = devm_clk_get(&pdev->dev, "fs");
+	if (IS_ERR(clk)) {
+		if (PTR_ERR(clk) == -EPROBE_DEFER)
+			return -EPROBE_DEFER;
+		ci->fs_clk = NULL;
+	}
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+	ci->base = devm_ioremap_resource(&pdev->dev, res);
+	if (IS_ERR(ci->base))
+		return PTR_ERR(ci->base);
+
+	ci->rcdev.owner = THIS_MODULE;
+	ci->rcdev.ops = &ci_hdrc_msm_reset_ops;
+	ci->rcdev.of_node = pdev->dev.of_node;
+	ci->rcdev.nr_resets = 2;
+	ret = reset_controller_register(&ci->rcdev);
+	if (ret)
+		return ret;
+
+	ret = clk_prepare_enable(ci->fs_clk);
+	if (ret)
+		goto err_fs;
+
+	reset_control_assert(reset);
+	usleep_range(10000, 12000);
+	reset_control_deassert(reset);
+
+	clk_disable_unprepare(ci->fs_clk);
+
+	ret = clk_prepare_enable(ci->core_clk);
+	if (ret)
+		goto err_fs;
+
+	ret = clk_prepare_enable(ci->iface_clk);
+	if (ret)
+		goto err_iface;
+
+	ret = ci_hdrc_msm_mux_phy(ci, pdev);
+	if (ret)
+		goto err_mux;
+
+	ulpi_node = of_get_child_by_name(pdev->dev.of_node, "ulpi");
+	if (ulpi_node) {
+		phy_node = of_get_next_available_child(ulpi_node, NULL);
+		ci->hsic = of_device_is_compatible(phy_node, "qcom,usb-hsic-phy");
+		of_node_put(phy_node);
+	}
+	of_node_put(ulpi_node);
+
+	plat_ci = ci_hdrc_add_device(&pdev->dev, pdev->resource,
+				     pdev->num_resources, &ci->pdata);
+	if (IS_ERR(plat_ci)) {
+		ret = PTR_ERR(plat_ci);
+		if (ret != -EPROBE_DEFER)
+			dev_err(&pdev->dev, "ci_hdrc_add_device failed!\n");
+		goto err_mux;
+	}
+
+	ci->ci = plat_ci;
+
+	pm_runtime_set_active(&pdev->dev);
+	pm_runtime_no_callbacks(&pdev->dev);
+	pm_runtime_enable(&pdev->dev);
+
+	return 0;
+
+err_mux:
+	clk_disable_unprepare(ci->iface_clk);
+err_iface:
+	clk_disable_unprepare(ci->core_clk);
+err_fs:
+	reset_controller_unregister(&ci->rcdev);
+	return ret;
+}
+
+static int ci_hdrc_msm_remove(struct platform_device *pdev)
+{
+	struct ci_hdrc_msm *ci = platform_get_drvdata(pdev);
+
+	pm_runtime_disable(&pdev->dev);
+	ci_hdrc_remove_device(ci->ci);
+	clk_disable_unprepare(ci->iface_clk);
+	clk_disable_unprepare(ci->core_clk);
+	reset_controller_unregister(&ci->rcdev);
+
+	return 0;
+}
+
+static const struct of_device_id msm_ci_dt_match[] = {
+	{ .compatible = "qcom,ci-hdrc", },
+	{ }
+};
+MODULE_DEVICE_TABLE(of, msm_ci_dt_match);
+
+static struct platform_driver ci_hdrc_msm_driver = {
+	.probe = ci_hdrc_msm_probe,
+	.remove = ci_hdrc_msm_remove,
+	.driver = {
+		.name = "msm_hsusb",
+		.of_match_table = msm_ci_dt_match,
+	},
+};
+
+module_platform_driver(ci_hdrc_msm_driver);
+
+MODULE_ALIAS("platform:msm_hsusb");
+MODULE_ALIAS("platform:ci13xxx_msm");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/usb/chipidea/ci_hdrc_pci.c b/drivers/usb/chipidea/ci_hdrc_pci.c
new file mode 100644
index 0000000..49a6154
--- /dev/null
+++ b/drivers/usb/chipidea/ci_hdrc_pci.c
@@ -0,0 +1,170 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * ci_hdrc_pci.c - MIPS USB IP core family device controller
+ *
+ * Copyright (C) 2008 Chipidea - MIPS Technologies, Inc. All rights reserved.
+ *
+ * Author: David Lopo
+ */
+
+#include <linux/platform_device.h>
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/interrupt.h>
+#include <linux/usb/gadget.h>
+#include <linux/usb/chipidea.h>
+#include <linux/usb/usb_phy_generic.h>
+
+/* driver name */
+#define UDC_DRIVER_NAME   "ci_hdrc_pci"
+
+struct ci_hdrc_pci {
+	struct platform_device	*ci;
+	struct platform_device	*phy;
+};
+
+/******************************************************************************
+ * PCI block
+ *****************************************************************************/
+static struct ci_hdrc_platform_data pci_platdata = {
+	.name		= UDC_DRIVER_NAME,
+	.capoffset	= DEF_CAPOFFSET,
+};
+
+static struct ci_hdrc_platform_data langwell_pci_platdata = {
+	.name		= UDC_DRIVER_NAME,
+	.capoffset	= 0,
+};
+
+static struct ci_hdrc_platform_data penwell_pci_platdata = {
+	.name		= UDC_DRIVER_NAME,
+	.capoffset	= 0,
+	.power_budget	= 200,
+};
+
+/**
+ * ci_hdrc_pci_probe: PCI probe
+ * @pdev: USB device controller being probed
+ * @id:   PCI hotplug ID connecting controller to UDC framework
+ *
+ * This function returns an error code
+ * Allocates basic PCI resources for this USB device controller, and then
+ * invokes the udc_probe() method to start the UDC associated with it
+ */
+static int ci_hdrc_pci_probe(struct pci_dev *pdev,
+				       const struct pci_device_id *id)
+{
+	struct ci_hdrc_platform_data *platdata = (void *)id->driver_data;
+	struct ci_hdrc_pci *ci;
+	struct resource res[3];
+	int retval = 0, nres = 2;
+
+	if (!platdata) {
+		dev_err(&pdev->dev, "device doesn't provide driver data\n");
+		return -ENODEV;
+	}
+
+	ci = devm_kzalloc(&pdev->dev, sizeof(*ci), GFP_KERNEL);
+	if (!ci)
+		return -ENOMEM;
+
+	retval = pcim_enable_device(pdev);
+	if (retval)
+		return retval;
+
+	if (!pdev->irq) {
+		dev_err(&pdev->dev, "No IRQ, check BIOS/PCI setup!");
+		return -ENODEV;
+	}
+
+	pci_set_master(pdev);
+	pci_try_set_mwi(pdev);
+
+	/* register a nop PHY */
+	ci->phy = usb_phy_generic_register();
+	if (IS_ERR(ci->phy))
+		return PTR_ERR(ci->phy);
+
+	memset(res, 0, sizeof(res));
+	res[0].start	= pci_resource_start(pdev, 0);
+	res[0].end	= pci_resource_end(pdev, 0);
+	res[0].flags	= IORESOURCE_MEM;
+	res[1].start	= pdev->irq;
+	res[1].flags	= IORESOURCE_IRQ;
+
+	ci->ci = ci_hdrc_add_device(&pdev->dev, res, nres, platdata);
+	if (IS_ERR(ci->ci)) {
+		dev_err(&pdev->dev, "ci_hdrc_add_device failed!\n");
+		usb_phy_generic_unregister(ci->phy);
+		return PTR_ERR(ci->ci);
+	}
+
+	pci_set_drvdata(pdev, ci);
+
+	return 0;
+}
+
+/**
+ * ci_hdrc_pci_remove: PCI remove
+ * @pdev: USB Device Controller being removed
+ *
+ * Reverses the effect of ci_hdrc_pci_probe(),
+ * first invoking the udc_remove() and then releases
+ * all PCI resources allocated for this USB device controller
+ */
+static void ci_hdrc_pci_remove(struct pci_dev *pdev)
+{
+	struct ci_hdrc_pci *ci = pci_get_drvdata(pdev);
+
+	ci_hdrc_remove_device(ci->ci);
+	usb_phy_generic_unregister(ci->phy);
+}
+
+/**
+ * PCI device table
+ * PCI device structure
+ *
+ * Check "pci.h" for details
+ *
+ * Note: ehci-pci driver may try to probe the device first. You have to add an
+ * ID to the bypass_pci_id_table in ehci-pci driver to prevent this.
+ */
+static const struct pci_device_id ci_hdrc_pci_id_table[] = {
+	{
+		PCI_DEVICE(0x153F, 0x1004),
+		.driver_data = (kernel_ulong_t)&pci_platdata,
+	},
+	{
+		PCI_DEVICE(0x153F, 0x1006),
+		.driver_data = (kernel_ulong_t)&pci_platdata,
+	},
+	{
+		PCI_VDEVICE(INTEL, 0x0811),
+		.driver_data = (kernel_ulong_t)&langwell_pci_platdata,
+	},
+	{
+		PCI_VDEVICE(INTEL, 0x0829),
+		.driver_data = (kernel_ulong_t)&penwell_pci_platdata,
+	},
+	{
+		/* Intel Clovertrail */
+		PCI_VDEVICE(INTEL, 0xe006),
+		.driver_data = (kernel_ulong_t)&penwell_pci_platdata,
+	},
+	{ 0 } /* end: all zeroes */
+};
+MODULE_DEVICE_TABLE(pci, ci_hdrc_pci_id_table);
+
+static struct pci_driver ci_hdrc_pci_driver = {
+	.name         =	UDC_DRIVER_NAME,
+	.id_table     =	ci_hdrc_pci_id_table,
+	.probe        =	ci_hdrc_pci_probe,
+	.remove       =	ci_hdrc_pci_remove,
+};
+
+module_pci_driver(ci_hdrc_pci_driver);
+
+MODULE_AUTHOR("MIPS - David Lopo <dlopo@chipidea.mips.com>");
+MODULE_DESCRIPTION("MIPS CI13XXX USB Peripheral Controller");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:ci13xxx_pci");
diff --git a/drivers/usb/chipidea/ci_hdrc_tegra.c b/drivers/usb/chipidea/ci_hdrc_tegra.c
new file mode 100644
index 0000000..772851b
--- /dev/null
+++ b/drivers/usb/chipidea/ci_hdrc_tegra.c
@@ -0,0 +1,152 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2016, NVIDIA Corporation
+ */
+
+#include <linux/clk.h>
+#include <linux/module.h>
+#include <linux/of_device.h>
+#include <linux/reset.h>
+
+#include <linux/usb/chipidea.h>
+
+#include "ci.h"
+
+struct tegra_udc {
+	struct ci_hdrc_platform_data data;
+	struct platform_device *dev;
+
+	struct usb_phy *phy;
+	struct clk *clk;
+};
+
+struct tegra_udc_soc_info {
+	unsigned long flags;
+};
+
+static const struct tegra_udc_soc_info tegra20_udc_soc_info = {
+	.flags = CI_HDRC_REQUIRES_ALIGNED_DMA,
+};
+
+static const struct tegra_udc_soc_info tegra30_udc_soc_info = {
+	.flags = CI_HDRC_REQUIRES_ALIGNED_DMA,
+};
+
+static const struct tegra_udc_soc_info tegra114_udc_soc_info = {
+	.flags = CI_HDRC_REQUIRES_ALIGNED_DMA,
+};
+
+static const struct tegra_udc_soc_info tegra124_udc_soc_info = {
+	.flags = CI_HDRC_REQUIRES_ALIGNED_DMA,
+};
+
+static const struct of_device_id tegra_udc_of_match[] = {
+	{
+		.compatible = "nvidia,tegra20-udc",
+		.data = &tegra20_udc_soc_info,
+	}, {
+		.compatible = "nvidia,tegra30-udc",
+		.data = &tegra30_udc_soc_info,
+	}, {
+		.compatible = "nvidia,tegra114-udc",
+		.data = &tegra114_udc_soc_info,
+	}, {
+		.compatible = "nvidia,tegra124-udc",
+		.data = &tegra124_udc_soc_info,
+	}, {
+		/* sentinel */
+	}
+};
+MODULE_DEVICE_TABLE(of, tegra_udc_of_match);
+
+static int tegra_udc_probe(struct platform_device *pdev)
+{
+	const struct tegra_udc_soc_info *soc;
+	struct tegra_udc *udc;
+	int err;
+
+	udc = devm_kzalloc(&pdev->dev, sizeof(*udc), GFP_KERNEL);
+	if (!udc)
+		return -ENOMEM;
+
+	soc = of_device_get_match_data(&pdev->dev);
+	if (!soc) {
+		dev_err(&pdev->dev, "failed to match OF data\n");
+		return -EINVAL;
+	}
+
+	udc->phy = devm_usb_get_phy_by_phandle(&pdev->dev, "nvidia,phy", 0);
+	if (IS_ERR(udc->phy)) {
+		err = PTR_ERR(udc->phy);
+		dev_err(&pdev->dev, "failed to get PHY: %d\n", err);
+		return err;
+	}
+
+	udc->clk = devm_clk_get(&pdev->dev, NULL);
+	if (IS_ERR(udc->clk)) {
+		err = PTR_ERR(udc->clk);
+		dev_err(&pdev->dev, "failed to get clock: %d\n", err);
+		return err;
+	}
+
+	err = clk_prepare_enable(udc->clk);
+	if (err < 0) {
+		dev_err(&pdev->dev, "failed to enable clock: %d\n", err);
+		return err;
+	}
+
+	/*
+	 * Tegra's USB PHY driver doesn't implement optional phy_init()
+	 * hook, so we have to power on UDC controller before ChipIdea
+	 * driver initialization kicks in.
+	 */
+	usb_phy_set_suspend(udc->phy, 0);
+
+	/* setup and register ChipIdea HDRC device */
+	udc->data.name = "tegra-udc";
+	udc->data.flags = soc->flags;
+	udc->data.usb_phy = udc->phy;
+	udc->data.capoffset = DEF_CAPOFFSET;
+
+	udc->dev = ci_hdrc_add_device(&pdev->dev, pdev->resource,
+				      pdev->num_resources, &udc->data);
+	if (IS_ERR(udc->dev)) {
+		err = PTR_ERR(udc->dev);
+		dev_err(&pdev->dev, "failed to add HDRC device: %d\n", err);
+		goto fail_power_off;
+	}
+
+	platform_set_drvdata(pdev, udc);
+
+	return 0;
+
+fail_power_off:
+	usb_phy_set_suspend(udc->phy, 1);
+	clk_disable_unprepare(udc->clk);
+	return err;
+}
+
+static int tegra_udc_remove(struct platform_device *pdev)
+{
+	struct tegra_udc *udc = platform_get_drvdata(pdev);
+
+	usb_phy_set_suspend(udc->phy, 1);
+	clk_disable_unprepare(udc->clk);
+
+	return 0;
+}
+
+static struct platform_driver tegra_udc_driver = {
+	.driver = {
+		.name = "tegra-udc",
+		.of_match_table = tegra_udc_of_match,
+	},
+	.probe = tegra_udc_probe,
+	.remove = tegra_udc_remove,
+};
+module_platform_driver(tegra_udc_driver);
+
+MODULE_DESCRIPTION("NVIDIA Tegra USB device mode driver");
+MODULE_AUTHOR("Thierry Reding <treding@nvidia.com>");
+MODULE_ALIAS("platform:tegra-udc");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/usb/chipidea/ci_hdrc_usb2.c b/drivers/usb/chipidea/ci_hdrc_usb2.c
new file mode 100644
index 0000000..c044fba
--- /dev/null
+++ b/drivers/usb/chipidea/ci_hdrc_usb2.c
@@ -0,0 +1,125 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2014 Marvell Technology Group Ltd.
+ *
+ * Antoine Tenart <antoine.tenart@free-electrons.com>
+ */
+
+#include <linux/clk.h>
+#include <linux/dma-mapping.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_platform.h>
+#include <linux/phy/phy.h>
+#include <linux/platform_device.h>
+#include <linux/usb/chipidea.h>
+#include <linux/usb/hcd.h>
+#include <linux/usb/ulpi.h>
+
+#include "ci.h"
+
+struct ci_hdrc_usb2_priv {
+	struct platform_device	*ci_pdev;
+	struct clk		*clk;
+};
+
+static const struct ci_hdrc_platform_data ci_default_pdata = {
+	.capoffset	= DEF_CAPOFFSET,
+	.flags		= CI_HDRC_DISABLE_STREAMING,
+};
+
+static struct ci_hdrc_platform_data ci_zynq_pdata = {
+	.capoffset	= DEF_CAPOFFSET,
+};
+
+static const struct of_device_id ci_hdrc_usb2_of_match[] = {
+	{ .compatible = "chipidea,usb2"},
+	{ .compatible = "xlnx,zynq-usb-2.20a", .data = &ci_zynq_pdata},
+	{ }
+};
+MODULE_DEVICE_TABLE(of, ci_hdrc_usb2_of_match);
+
+static int ci_hdrc_usb2_probe(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	struct ci_hdrc_usb2_priv *priv;
+	struct ci_hdrc_platform_data *ci_pdata = dev_get_platdata(dev);
+	int ret;
+	const struct of_device_id *match;
+
+	if (!ci_pdata) {
+		ci_pdata = devm_kmalloc(dev, sizeof(*ci_pdata), GFP_KERNEL);
+		if (!ci_pdata)
+			return -ENOMEM;
+		*ci_pdata = ci_default_pdata;	/* struct copy */
+	}
+
+	match = of_match_device(ci_hdrc_usb2_of_match, &pdev->dev);
+	if (match && match->data) {
+		/* struct copy */
+		*ci_pdata = *(struct ci_hdrc_platform_data *)match->data;
+	}
+
+	priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+	if (!priv)
+		return -ENOMEM;
+
+	priv->clk = devm_clk_get(dev, NULL);
+	if (!IS_ERR(priv->clk)) {
+		ret = clk_prepare_enable(priv->clk);
+		if (ret) {
+			dev_err(dev, "failed to enable the clock: %d\n", ret);
+			return ret;
+		}
+	}
+
+	ci_pdata->name = dev_name(dev);
+
+	priv->ci_pdev = ci_hdrc_add_device(dev, pdev->resource,
+					   pdev->num_resources, ci_pdata);
+	if (IS_ERR(priv->ci_pdev)) {
+		ret = PTR_ERR(priv->ci_pdev);
+		if (ret != -EPROBE_DEFER)
+			dev_err(dev,
+				"failed to register ci_hdrc platform device: %d\n",
+				ret);
+		goto clk_err;
+	}
+
+	platform_set_drvdata(pdev, priv);
+
+	pm_runtime_no_callbacks(dev);
+	pm_runtime_enable(dev);
+
+	return 0;
+
+clk_err:
+	if (!IS_ERR(priv->clk))
+		clk_disable_unprepare(priv->clk);
+	return ret;
+}
+
+static int ci_hdrc_usb2_remove(struct platform_device *pdev)
+{
+	struct ci_hdrc_usb2_priv *priv = platform_get_drvdata(pdev);
+
+	pm_runtime_disable(&pdev->dev);
+	ci_hdrc_remove_device(priv->ci_pdev);
+	clk_disable_unprepare(priv->clk);
+
+	return 0;
+}
+
+static struct platform_driver ci_hdrc_usb2_driver = {
+	.probe	= ci_hdrc_usb2_probe,
+	.remove	= ci_hdrc_usb2_remove,
+	.driver	= {
+		.name		= "chipidea-usb2",
+		.of_match_table	= of_match_ptr(ci_hdrc_usb2_of_match),
+	},
+};
+module_platform_driver(ci_hdrc_usb2_driver);
+
+MODULE_DESCRIPTION("ChipIdea HDRC USB2 binding for ci13xxx");
+MODULE_AUTHOR("Antoine Tenart <antoine.tenart@free-electrons.com>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/usb/chipidea/ci_hdrc_zevio.c b/drivers/usb/chipidea/ci_hdrc_zevio.c
new file mode 100644
index 0000000..e1634da
--- /dev/null
+++ b/drivers/usb/chipidea/ci_hdrc_zevio.c
@@ -0,0 +1,67 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ *	Copyright (C) 2013 Daniel Tang <tangrs@tangrs.id.au>
+ *
+ * Based off drivers/usb/chipidea/ci_hdrc_msm.c
+ */
+
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/usb/gadget.h>
+#include <linux/usb/chipidea.h>
+
+#include "ci.h"
+
+static struct ci_hdrc_platform_data ci_hdrc_zevio_platdata = {
+	.name			= "ci_hdrc_zevio",
+	.flags			= CI_HDRC_REGS_SHARED | CI_HDRC_FORCE_FULLSPEED,
+	.capoffset		= DEF_CAPOFFSET,
+};
+
+static int ci_hdrc_zevio_probe(struct platform_device *pdev)
+{
+	struct platform_device *ci_pdev;
+
+	dev_dbg(&pdev->dev, "ci_hdrc_zevio_probe\n");
+
+	ci_pdev = ci_hdrc_add_device(&pdev->dev,
+				pdev->resource, pdev->num_resources,
+				&ci_hdrc_zevio_platdata);
+
+	if (IS_ERR(ci_pdev)) {
+		dev_err(&pdev->dev, "ci_hdrc_add_device failed!\n");
+		return PTR_ERR(ci_pdev);
+	}
+
+	platform_set_drvdata(pdev, ci_pdev);
+
+	return 0;
+}
+
+static int ci_hdrc_zevio_remove(struct platform_device *pdev)
+{
+	struct platform_device *ci_pdev = platform_get_drvdata(pdev);
+
+	ci_hdrc_remove_device(ci_pdev);
+
+	return 0;
+}
+
+static const struct of_device_id ci_hdrc_zevio_dt_ids[] = {
+	{ .compatible = "lsi,zevio-usb", },
+	{ /* sentinel */ }
+};
+
+static struct platform_driver ci_hdrc_zevio_driver = {
+	.probe = ci_hdrc_zevio_probe,
+	.remove = ci_hdrc_zevio_remove,
+	.driver = {
+		.name = "zevio_usb",
+		.of_match_table = ci_hdrc_zevio_dt_ids,
+	},
+};
+
+MODULE_DEVICE_TABLE(of, ci_hdrc_zevio_dt_ids);
+module_platform_driver(ci_hdrc_zevio_driver);
+
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/usb/chipidea/core.c b/drivers/usb/chipidea/core.c
new file mode 100644
index 0000000..85fc6db
--- /dev/null
+++ b/drivers/usb/chipidea/core.c
@@ -0,0 +1,1299 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * core.c - ChipIdea USB IP core family device controller
+ *
+ * Copyright (C) 2008 Chipidea - MIPS Technologies, Inc. All rights reserved.
+ *
+ * Author: David Lopo
+ */
+
+/*
+ * Description: ChipIdea USB IP core family device controller
+ *
+ * This driver is composed of several blocks:
+ * - HW:     hardware interface
+ * - DBG:    debug facilities (optional)
+ * - UTIL:   utilities
+ * - ISR:    interrupts handling
+ * - ENDPT:  endpoint operations (Gadget API)
+ * - GADGET: gadget operations (Gadget API)
+ * - BUS:    bus glue code, bus abstraction layer
+ *
+ * Compile Options
+ * - STALL_IN:  non-empty bulk-in pipes cannot be halted
+ *              if defined mass storage compliance succeeds but with warnings
+ *              => case 4: Hi >  Dn
+ *              => case 5: Hi >  Di
+ *              => case 8: Hi <> Do
+ *              if undefined usbtest 13 fails
+ * - TRACE:     enable function tracing (depends on DEBUG)
+ *
+ * Main Features
+ * - Chapter 9 & Mass Storage Compliance with Gadget File Storage
+ * - Chapter 9 Compliance with Gadget Zero (STALL_IN undefined)
+ * - Normal & LPM support
+ *
+ * USBTEST Report
+ * - OK: 0-12, 13 (STALL_IN defined) & 14
+ * - Not Supported: 15 & 16 (ISO)
+ *
+ * TODO List
+ * - Suspend & Remote Wakeup
+ */
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/dma-mapping.h>
+#include <linux/extcon.h>
+#include <linux/phy/phy.h>
+#include <linux/platform_device.h>
+#include <linux/module.h>
+#include <linux/idr.h>
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/pm_runtime.h>
+#include <linux/usb/ch9.h>
+#include <linux/usb/gadget.h>
+#include <linux/usb/otg.h>
+#include <linux/usb/chipidea.h>
+#include <linux/usb/of.h>
+#include <linux/of.h>
+#include <linux/regulator/consumer.h>
+#include <linux/usb/ehci_def.h>
+
+#include "ci.h"
+#include "udc.h"
+#include "bits.h"
+#include "host.h"
+#include "otg.h"
+#include "otg_fsm.h"
+
+/* Controller register map */
+static const u8 ci_regs_nolpm[] = {
+	[CAP_CAPLENGTH]		= 0x00U,
+	[CAP_HCCPARAMS]		= 0x08U,
+	[CAP_DCCPARAMS]		= 0x24U,
+	[CAP_TESTMODE]		= 0x38U,
+	[OP_USBCMD]		= 0x00U,
+	[OP_USBSTS]		= 0x04U,
+	[OP_USBINTR]		= 0x08U,
+	[OP_DEVICEADDR]		= 0x14U,
+	[OP_ENDPTLISTADDR]	= 0x18U,
+	[OP_TTCTRL]		= 0x1CU,
+	[OP_BURSTSIZE]		= 0x20U,
+	[OP_ULPI_VIEWPORT]	= 0x30U,
+	[OP_PORTSC]		= 0x44U,
+	[OP_DEVLC]		= 0x84U,
+	[OP_OTGSC]		= 0x64U,
+	[OP_USBMODE]		= 0x68U,
+	[OP_ENDPTSETUPSTAT]	= 0x6CU,
+	[OP_ENDPTPRIME]		= 0x70U,
+	[OP_ENDPTFLUSH]		= 0x74U,
+	[OP_ENDPTSTAT]		= 0x78U,
+	[OP_ENDPTCOMPLETE]	= 0x7CU,
+	[OP_ENDPTCTRL]		= 0x80U,
+};
+
+static const u8 ci_regs_lpm[] = {
+	[CAP_CAPLENGTH]		= 0x00U,
+	[CAP_HCCPARAMS]		= 0x08U,
+	[CAP_DCCPARAMS]		= 0x24U,
+	[CAP_TESTMODE]		= 0xFCU,
+	[OP_USBCMD]		= 0x00U,
+	[OP_USBSTS]		= 0x04U,
+	[OP_USBINTR]		= 0x08U,
+	[OP_DEVICEADDR]		= 0x14U,
+	[OP_ENDPTLISTADDR]	= 0x18U,
+	[OP_TTCTRL]		= 0x1CU,
+	[OP_BURSTSIZE]		= 0x20U,
+	[OP_ULPI_VIEWPORT]	= 0x30U,
+	[OP_PORTSC]		= 0x44U,
+	[OP_DEVLC]		= 0x84U,
+	[OP_OTGSC]		= 0xC4U,
+	[OP_USBMODE]		= 0xC8U,
+	[OP_ENDPTSETUPSTAT]	= 0xD8U,
+	[OP_ENDPTPRIME]		= 0xDCU,
+	[OP_ENDPTFLUSH]		= 0xE0U,
+	[OP_ENDPTSTAT]		= 0xE4U,
+	[OP_ENDPTCOMPLETE]	= 0xE8U,
+	[OP_ENDPTCTRL]		= 0xECU,
+};
+
+static void hw_alloc_regmap(struct ci_hdrc *ci, bool is_lpm)
+{
+	int i;
+
+	for (i = 0; i < OP_ENDPTCTRL; i++)
+		ci->hw_bank.regmap[i] =
+			(i <= CAP_LAST ? ci->hw_bank.cap : ci->hw_bank.op) +
+			(is_lpm ? ci_regs_lpm[i] : ci_regs_nolpm[i]);
+
+	for (; i <= OP_LAST; i++)
+		ci->hw_bank.regmap[i] = ci->hw_bank.op +
+			4 * (i - OP_ENDPTCTRL) +
+			(is_lpm
+			 ? ci_regs_lpm[OP_ENDPTCTRL]
+			 : ci_regs_nolpm[OP_ENDPTCTRL]);
+
+}
+
+static enum ci_revision ci_get_revision(struct ci_hdrc *ci)
+{
+	int ver = hw_read_id_reg(ci, ID_ID, VERSION) >> __ffs(VERSION);
+	enum ci_revision rev = CI_REVISION_UNKNOWN;
+
+	if (ver == 0x2) {
+		rev = hw_read_id_reg(ci, ID_ID, REVISION)
+			>> __ffs(REVISION);
+		rev += CI_REVISION_20;
+	} else if (ver == 0x0) {
+		rev = CI_REVISION_1X;
+	}
+
+	return rev;
+}
+
+/**
+ * hw_read_intr_enable: returns interrupt enable register
+ *
+ * @ci: the controller
+ *
+ * This function returns register data
+ */
+u32 hw_read_intr_enable(struct ci_hdrc *ci)
+{
+	return hw_read(ci, OP_USBINTR, ~0);
+}
+
+/**
+ * hw_read_intr_status: returns interrupt status register
+ *
+ * @ci: the controller
+ *
+ * This function returns register data
+ */
+u32 hw_read_intr_status(struct ci_hdrc *ci)
+{
+	return hw_read(ci, OP_USBSTS, ~0);
+}
+
+/**
+ * hw_port_test_set: writes port test mode (execute without interruption)
+ * @mode: new value
+ *
+ * This function returns an error code
+ */
+int hw_port_test_set(struct ci_hdrc *ci, u8 mode)
+{
+	const u8 TEST_MODE_MAX = 7;
+
+	if (mode > TEST_MODE_MAX)
+		return -EINVAL;
+
+	hw_write(ci, OP_PORTSC, PORTSC_PTC, mode << __ffs(PORTSC_PTC));
+	return 0;
+}
+
+/**
+ * hw_port_test_get: reads port test mode value
+ *
+ * @ci: the controller
+ *
+ * This function returns port test mode value
+ */
+u8 hw_port_test_get(struct ci_hdrc *ci)
+{
+	return hw_read(ci, OP_PORTSC, PORTSC_PTC) >> __ffs(PORTSC_PTC);
+}
+
+static void hw_wait_phy_stable(void)
+{
+	/*
+	 * The phy needs some delay to output the stable status from low
+	 * power mode. And for OTGSC, the status inputs are debounced
+	 * using a 1 ms time constant, so, delay 2ms for controller to get
+	 * the stable status, like vbus and id when the phy leaves low power.
+	 */
+	usleep_range(2000, 2500);
+}
+
+/* The PHY enters/leaves low power mode */
+static void ci_hdrc_enter_lpm(struct ci_hdrc *ci, bool enable)
+{
+	enum ci_hw_regs reg = ci->hw_bank.lpm ? OP_DEVLC : OP_PORTSC;
+	bool lpm = !!(hw_read(ci, reg, PORTSC_PHCD(ci->hw_bank.lpm)));
+
+	if (enable && !lpm)
+		hw_write(ci, reg, PORTSC_PHCD(ci->hw_bank.lpm),
+				PORTSC_PHCD(ci->hw_bank.lpm));
+	else if (!enable && lpm)
+		hw_write(ci, reg, PORTSC_PHCD(ci->hw_bank.lpm),
+				0);
+}
+
+static int hw_device_init(struct ci_hdrc *ci, void __iomem *base)
+{
+	u32 reg;
+
+	/* bank is a module variable */
+	ci->hw_bank.abs = base;
+
+	ci->hw_bank.cap = ci->hw_bank.abs;
+	ci->hw_bank.cap += ci->platdata->capoffset;
+	ci->hw_bank.op = ci->hw_bank.cap + (ioread32(ci->hw_bank.cap) & 0xff);
+
+	hw_alloc_regmap(ci, false);
+	reg = hw_read(ci, CAP_HCCPARAMS, HCCPARAMS_LEN) >>
+		__ffs(HCCPARAMS_LEN);
+	ci->hw_bank.lpm  = reg;
+	if (reg)
+		hw_alloc_regmap(ci, !!reg);
+	ci->hw_bank.size = ci->hw_bank.op - ci->hw_bank.abs;
+	ci->hw_bank.size += OP_LAST;
+	ci->hw_bank.size /= sizeof(u32);
+
+	reg = hw_read(ci, CAP_DCCPARAMS, DCCPARAMS_DEN) >>
+		__ffs(DCCPARAMS_DEN);
+	ci->hw_ep_max = reg * 2;   /* cache hw ENDPT_MAX */
+
+	if (ci->hw_ep_max > ENDPT_MAX)
+		return -ENODEV;
+
+	ci_hdrc_enter_lpm(ci, false);
+
+	/* Disable all interrupts bits */
+	hw_write(ci, OP_USBINTR, 0xffffffff, 0);
+
+	/* Clear all interrupts status bits*/
+	hw_write(ci, OP_USBSTS, 0xffffffff, 0xffffffff);
+
+	ci->rev = ci_get_revision(ci);
+
+	dev_dbg(ci->dev,
+		"ChipIdea HDRC found, revision: %d, lpm: %d; cap: %p op: %p\n",
+		ci->rev, ci->hw_bank.lpm, ci->hw_bank.cap, ci->hw_bank.op);
+
+	/* setup lock mode ? */
+
+	/* ENDPTSETUPSTAT is '0' by default */
+
+	/* HCSPARAMS.bf.ppc SHOULD BE zero for device */
+
+	return 0;
+}
+
+void hw_phymode_configure(struct ci_hdrc *ci)
+{
+	u32 portsc, lpm, sts = 0;
+
+	switch (ci->platdata->phy_mode) {
+	case USBPHY_INTERFACE_MODE_UTMI:
+		portsc = PORTSC_PTS(PTS_UTMI);
+		lpm = DEVLC_PTS(PTS_UTMI);
+		break;
+	case USBPHY_INTERFACE_MODE_UTMIW:
+		portsc = PORTSC_PTS(PTS_UTMI) | PORTSC_PTW;
+		lpm = DEVLC_PTS(PTS_UTMI) | DEVLC_PTW;
+		break;
+	case USBPHY_INTERFACE_MODE_ULPI:
+		portsc = PORTSC_PTS(PTS_ULPI);
+		lpm = DEVLC_PTS(PTS_ULPI);
+		break;
+	case USBPHY_INTERFACE_MODE_SERIAL:
+		portsc = PORTSC_PTS(PTS_SERIAL);
+		lpm = DEVLC_PTS(PTS_SERIAL);
+		sts = 1;
+		break;
+	case USBPHY_INTERFACE_MODE_HSIC:
+		portsc = PORTSC_PTS(PTS_HSIC);
+		lpm = DEVLC_PTS(PTS_HSIC);
+		break;
+	default:
+		return;
+	}
+
+	if (ci->hw_bank.lpm) {
+		hw_write(ci, OP_DEVLC, DEVLC_PTS(7) | DEVLC_PTW, lpm);
+		if (sts)
+			hw_write(ci, OP_DEVLC, DEVLC_STS, DEVLC_STS);
+	} else {
+		hw_write(ci, OP_PORTSC, PORTSC_PTS(7) | PORTSC_PTW, portsc);
+		if (sts)
+			hw_write(ci, OP_PORTSC, PORTSC_STS, PORTSC_STS);
+	}
+}
+EXPORT_SYMBOL_GPL(hw_phymode_configure);
+
+/**
+ * _ci_usb_phy_init: initialize phy taking in account both phy and usb_phy
+ * interfaces
+ * @ci: the controller
+ *
+ * This function returns an error code if the phy failed to init
+ */
+static int _ci_usb_phy_init(struct ci_hdrc *ci)
+{
+	int ret;
+
+	if (ci->phy) {
+		ret = phy_init(ci->phy);
+		if (ret)
+			return ret;
+
+		ret = phy_power_on(ci->phy);
+		if (ret) {
+			phy_exit(ci->phy);
+			return ret;
+		}
+	} else {
+		ret = usb_phy_init(ci->usb_phy);
+	}
+
+	return ret;
+}
+
+/**
+ * _ci_usb_phy_exit: deinitialize phy taking in account both phy and usb_phy
+ * interfaces
+ * @ci: the controller
+ */
+static void ci_usb_phy_exit(struct ci_hdrc *ci)
+{
+	if (ci->platdata->flags & CI_HDRC_OVERRIDE_PHY_CONTROL)
+		return;
+
+	if (ci->phy) {
+		phy_power_off(ci->phy);
+		phy_exit(ci->phy);
+	} else {
+		usb_phy_shutdown(ci->usb_phy);
+	}
+}
+
+/**
+ * ci_usb_phy_init: initialize phy according to different phy type
+ * @ci: the controller
+ *
+ * This function returns an error code if usb_phy_init has failed
+ */
+static int ci_usb_phy_init(struct ci_hdrc *ci)
+{
+	int ret;
+
+	if (ci->platdata->flags & CI_HDRC_OVERRIDE_PHY_CONTROL)
+		return 0;
+
+	switch (ci->platdata->phy_mode) {
+	case USBPHY_INTERFACE_MODE_UTMI:
+	case USBPHY_INTERFACE_MODE_UTMIW:
+	case USBPHY_INTERFACE_MODE_HSIC:
+		ret = _ci_usb_phy_init(ci);
+		if (!ret)
+			hw_wait_phy_stable();
+		else
+			return ret;
+		hw_phymode_configure(ci);
+		break;
+	case USBPHY_INTERFACE_MODE_ULPI:
+	case USBPHY_INTERFACE_MODE_SERIAL:
+		hw_phymode_configure(ci);
+		ret = _ci_usb_phy_init(ci);
+		if (ret)
+			return ret;
+		break;
+	default:
+		ret = _ci_usb_phy_init(ci);
+		if (!ret)
+			hw_wait_phy_stable();
+	}
+
+	return ret;
+}
+
+
+/**
+ * ci_platform_configure: do controller configure
+ * @ci: the controller
+ *
+ */
+void ci_platform_configure(struct ci_hdrc *ci)
+{
+	bool is_device_mode, is_host_mode;
+
+	is_device_mode = hw_read(ci, OP_USBMODE, USBMODE_CM) == USBMODE_CM_DC;
+	is_host_mode = hw_read(ci, OP_USBMODE, USBMODE_CM) == USBMODE_CM_HC;
+
+	if (is_device_mode) {
+		phy_set_mode(ci->phy, PHY_MODE_USB_DEVICE);
+
+		if (ci->platdata->flags & CI_HDRC_DISABLE_DEVICE_STREAMING)
+			hw_write(ci, OP_USBMODE, USBMODE_CI_SDIS,
+				 USBMODE_CI_SDIS);
+	}
+
+	if (is_host_mode) {
+		phy_set_mode(ci->phy, PHY_MODE_USB_HOST);
+
+		if (ci->platdata->flags & CI_HDRC_DISABLE_HOST_STREAMING)
+			hw_write(ci, OP_USBMODE, USBMODE_CI_SDIS,
+				 USBMODE_CI_SDIS);
+	}
+
+	if (ci->platdata->flags & CI_HDRC_FORCE_FULLSPEED) {
+		if (ci->hw_bank.lpm)
+			hw_write(ci, OP_DEVLC, DEVLC_PFSC, DEVLC_PFSC);
+		else
+			hw_write(ci, OP_PORTSC, PORTSC_PFSC, PORTSC_PFSC);
+	}
+
+	if (ci->platdata->flags & CI_HDRC_SET_NON_ZERO_TTHA)
+		hw_write(ci, OP_TTCTRL, TTCTRL_TTHA_MASK, TTCTRL_TTHA);
+
+	hw_write(ci, OP_USBCMD, 0xff0000, ci->platdata->itc_setting << 16);
+
+	if (ci->platdata->flags & CI_HDRC_OVERRIDE_AHB_BURST)
+		hw_write_id_reg(ci, ID_SBUSCFG, AHBBRST_MASK,
+			ci->platdata->ahb_burst_config);
+
+	/* override burst size, take effect only when ahb_burst_config is 0 */
+	if (!hw_read_id_reg(ci, ID_SBUSCFG, AHBBRST_MASK)) {
+		if (ci->platdata->flags & CI_HDRC_OVERRIDE_TX_BURST)
+			hw_write(ci, OP_BURSTSIZE, TX_BURST_MASK,
+			ci->platdata->tx_burst_size << __ffs(TX_BURST_MASK));
+
+		if (ci->platdata->flags & CI_HDRC_OVERRIDE_RX_BURST)
+			hw_write(ci, OP_BURSTSIZE, RX_BURST_MASK,
+				ci->platdata->rx_burst_size);
+	}
+}
+
+/**
+ * hw_controller_reset: do controller reset
+ * @ci: the controller
+  *
+ * This function returns an error code
+ */
+static int hw_controller_reset(struct ci_hdrc *ci)
+{
+	int count = 0;
+
+	hw_write(ci, OP_USBCMD, USBCMD_RST, USBCMD_RST);
+	while (hw_read(ci, OP_USBCMD, USBCMD_RST)) {
+		udelay(10);
+		if (count++ > 1000)
+			return -ETIMEDOUT;
+	}
+
+	return 0;
+}
+
+/**
+ * hw_device_reset: resets chip (execute without interruption)
+ * @ci: the controller
+ *
+ * This function returns an error code
+ */
+int hw_device_reset(struct ci_hdrc *ci)
+{
+	int ret;
+
+	/* should flush & stop before reset */
+	hw_write(ci, OP_ENDPTFLUSH, ~0, ~0);
+	hw_write(ci, OP_USBCMD, USBCMD_RS, 0);
+
+	ret = hw_controller_reset(ci);
+	if (ret) {
+		dev_err(ci->dev, "error resetting controller, ret=%d\n", ret);
+		return ret;
+	}
+
+	if (ci->platdata->notify_event) {
+		ret = ci->platdata->notify_event(ci,
+			CI_HDRC_CONTROLLER_RESET_EVENT);
+		if (ret)
+			return ret;
+	}
+
+	/* USBMODE should be configured step by step */
+	hw_write(ci, OP_USBMODE, USBMODE_CM, USBMODE_CM_IDLE);
+	hw_write(ci, OP_USBMODE, USBMODE_CM, USBMODE_CM_DC);
+	/* HW >= 2.3 */
+	hw_write(ci, OP_USBMODE, USBMODE_SLOM, USBMODE_SLOM);
+
+	if (hw_read(ci, OP_USBMODE, USBMODE_CM) != USBMODE_CM_DC) {
+		pr_err("cannot enter in %s device mode", ci_role(ci)->name);
+		pr_err("lpm = %i", ci->hw_bank.lpm);
+		return -ENODEV;
+	}
+
+	ci_platform_configure(ci);
+
+	return 0;
+}
+
+static irqreturn_t ci_irq(int irq, void *data)
+{
+	struct ci_hdrc *ci = data;
+	irqreturn_t ret = IRQ_NONE;
+	u32 otgsc = 0;
+
+	if (ci->in_lpm) {
+		disable_irq_nosync(irq);
+		ci->wakeup_int = true;
+		pm_runtime_get(ci->dev);
+		return IRQ_HANDLED;
+	}
+
+	if (ci->is_otg) {
+		otgsc = hw_read_otgsc(ci, ~0);
+		if (ci_otg_is_fsm_mode(ci)) {
+			ret = ci_otg_fsm_irq(ci);
+			if (ret == IRQ_HANDLED)
+				return ret;
+		}
+	}
+
+	/*
+	 * Handle id change interrupt, it indicates device/host function
+	 * switch.
+	 */
+	if (ci->is_otg && (otgsc & OTGSC_IDIE) && (otgsc & OTGSC_IDIS)) {
+		ci->id_event = true;
+		/* Clear ID change irq status */
+		hw_write_otgsc(ci, OTGSC_IDIS, OTGSC_IDIS);
+		ci_otg_queue_work(ci);
+		return IRQ_HANDLED;
+	}
+
+	/*
+	 * Handle vbus change interrupt, it indicates device connection
+	 * and disconnection events.
+	 */
+	if (ci->is_otg && (otgsc & OTGSC_BSVIE) && (otgsc & OTGSC_BSVIS)) {
+		ci->b_sess_valid_event = true;
+		/* Clear BSV irq */
+		hw_write_otgsc(ci, OTGSC_BSVIS, OTGSC_BSVIS);
+		ci_otg_queue_work(ci);
+		return IRQ_HANDLED;
+	}
+
+	/* Handle device/host interrupt */
+	if (ci->role != CI_ROLE_END)
+		ret = ci_role(ci)->irq(ci);
+
+	return ret;
+}
+
+static int ci_cable_notifier(struct notifier_block *nb, unsigned long event,
+			     void *ptr)
+{
+	struct ci_hdrc_cable *cbl = container_of(nb, struct ci_hdrc_cable, nb);
+	struct ci_hdrc *ci = cbl->ci;
+
+	cbl->connected = event;
+	cbl->changed = true;
+
+	ci_irq(ci->irq, ci);
+	return NOTIFY_DONE;
+}
+
+static int ci_get_platdata(struct device *dev,
+		struct ci_hdrc_platform_data *platdata)
+{
+	struct extcon_dev *ext_vbus, *ext_id;
+	struct ci_hdrc_cable *cable;
+	int ret;
+
+	if (!platdata->phy_mode)
+		platdata->phy_mode = of_usb_get_phy_mode(dev->of_node);
+
+	if (!platdata->dr_mode)
+		platdata->dr_mode = usb_get_dr_mode(dev);
+
+	if (platdata->dr_mode == USB_DR_MODE_UNKNOWN)
+		platdata->dr_mode = USB_DR_MODE_OTG;
+
+	if (platdata->dr_mode != USB_DR_MODE_PERIPHERAL) {
+		/* Get the vbus regulator */
+		platdata->reg_vbus = devm_regulator_get(dev, "vbus");
+		if (PTR_ERR(platdata->reg_vbus) == -EPROBE_DEFER) {
+			return -EPROBE_DEFER;
+		} else if (PTR_ERR(platdata->reg_vbus) == -ENODEV) {
+			/* no vbus regulator is needed */
+			platdata->reg_vbus = NULL;
+		} else if (IS_ERR(platdata->reg_vbus)) {
+			dev_err(dev, "Getting regulator error: %ld\n",
+				PTR_ERR(platdata->reg_vbus));
+			return PTR_ERR(platdata->reg_vbus);
+		}
+		/* Get TPL support */
+		if (!platdata->tpl_support)
+			platdata->tpl_support =
+				of_usb_host_tpl_support(dev->of_node);
+	}
+
+	if (platdata->dr_mode == USB_DR_MODE_OTG) {
+		/* We can support HNP and SRP of OTG 2.0 */
+		platdata->ci_otg_caps.otg_rev = 0x0200;
+		platdata->ci_otg_caps.hnp_support = true;
+		platdata->ci_otg_caps.srp_support = true;
+
+		/* Update otg capabilities by DT properties */
+		ret = of_usb_update_otg_caps(dev->of_node,
+					&platdata->ci_otg_caps);
+		if (ret)
+			return ret;
+	}
+
+	if (usb_get_maximum_speed(dev) == USB_SPEED_FULL)
+		platdata->flags |= CI_HDRC_FORCE_FULLSPEED;
+
+	of_property_read_u32(dev->of_node, "phy-clkgate-delay-us",
+				     &platdata->phy_clkgate_delay_us);
+
+	platdata->itc_setting = 1;
+
+	of_property_read_u32(dev->of_node, "itc-setting",
+					&platdata->itc_setting);
+
+	ret = of_property_read_u32(dev->of_node, "ahb-burst-config",
+				&platdata->ahb_burst_config);
+	if (!ret) {
+		platdata->flags |= CI_HDRC_OVERRIDE_AHB_BURST;
+	} else if (ret != -EINVAL) {
+		dev_err(dev, "failed to get ahb-burst-config\n");
+		return ret;
+	}
+
+	ret = of_property_read_u32(dev->of_node, "tx-burst-size-dword",
+				&platdata->tx_burst_size);
+	if (!ret) {
+		platdata->flags |= CI_HDRC_OVERRIDE_TX_BURST;
+	} else if (ret != -EINVAL) {
+		dev_err(dev, "failed to get tx-burst-size-dword\n");
+		return ret;
+	}
+
+	ret = of_property_read_u32(dev->of_node, "rx-burst-size-dword",
+				&platdata->rx_burst_size);
+	if (!ret) {
+		platdata->flags |= CI_HDRC_OVERRIDE_RX_BURST;
+	} else if (ret != -EINVAL) {
+		dev_err(dev, "failed to get rx-burst-size-dword\n");
+		return ret;
+	}
+
+	if (of_find_property(dev->of_node, "non-zero-ttctrl-ttha", NULL))
+		platdata->flags |= CI_HDRC_SET_NON_ZERO_TTHA;
+
+	ext_id = ERR_PTR(-ENODEV);
+	ext_vbus = ERR_PTR(-ENODEV);
+	if (of_property_read_bool(dev->of_node, "extcon")) {
+		/* Each one of them is not mandatory */
+		ext_vbus = extcon_get_edev_by_phandle(dev, 0);
+		if (IS_ERR(ext_vbus) && PTR_ERR(ext_vbus) != -ENODEV)
+			return PTR_ERR(ext_vbus);
+
+		ext_id = extcon_get_edev_by_phandle(dev, 1);
+		if (IS_ERR(ext_id) && PTR_ERR(ext_id) != -ENODEV)
+			return PTR_ERR(ext_id);
+	}
+
+	cable = &platdata->vbus_extcon;
+	cable->nb.notifier_call = ci_cable_notifier;
+	cable->edev = ext_vbus;
+
+	if (!IS_ERR(ext_vbus)) {
+		ret = extcon_get_state(cable->edev, EXTCON_USB);
+		if (ret)
+			cable->connected = true;
+		else
+			cable->connected = false;
+	}
+
+	cable = &platdata->id_extcon;
+	cable->nb.notifier_call = ci_cable_notifier;
+	cable->edev = ext_id;
+
+	if (!IS_ERR(ext_id)) {
+		ret = extcon_get_state(cable->edev, EXTCON_USB_HOST);
+		if (ret)
+			cable->connected = true;
+		else
+			cable->connected = false;
+	}
+	return 0;
+}
+
+static int ci_extcon_register(struct ci_hdrc *ci)
+{
+	struct ci_hdrc_cable *id, *vbus;
+	int ret;
+
+	id = &ci->platdata->id_extcon;
+	id->ci = ci;
+	if (!IS_ERR_OR_NULL(id->edev)) {
+		ret = devm_extcon_register_notifier(ci->dev, id->edev,
+						EXTCON_USB_HOST, &id->nb);
+		if (ret < 0) {
+			dev_err(ci->dev, "register ID failed\n");
+			return ret;
+		}
+	}
+
+	vbus = &ci->platdata->vbus_extcon;
+	vbus->ci = ci;
+	if (!IS_ERR_OR_NULL(vbus->edev)) {
+		ret = devm_extcon_register_notifier(ci->dev, vbus->edev,
+						EXTCON_USB, &vbus->nb);
+		if (ret < 0) {
+			dev_err(ci->dev, "register VBUS failed\n");
+			return ret;
+		}
+	}
+
+	return 0;
+}
+
+static DEFINE_IDA(ci_ida);
+
+struct platform_device *ci_hdrc_add_device(struct device *dev,
+			struct resource *res, int nres,
+			struct ci_hdrc_platform_data *platdata)
+{
+	struct platform_device *pdev;
+	int id, ret;
+
+	ret = ci_get_platdata(dev, platdata);
+	if (ret)
+		return ERR_PTR(ret);
+
+	id = ida_simple_get(&ci_ida, 0, 0, GFP_KERNEL);
+	if (id < 0)
+		return ERR_PTR(id);
+
+	pdev = platform_device_alloc("ci_hdrc", id);
+	if (!pdev) {
+		ret = -ENOMEM;
+		goto put_id;
+	}
+
+	pdev->dev.parent = dev;
+
+	ret = platform_device_add_resources(pdev, res, nres);
+	if (ret)
+		goto err;
+
+	ret = platform_device_add_data(pdev, platdata, sizeof(*platdata));
+	if (ret)
+		goto err;
+
+	ret = platform_device_add(pdev);
+	if (ret)
+		goto err;
+
+	return pdev;
+
+err:
+	platform_device_put(pdev);
+put_id:
+	ida_simple_remove(&ci_ida, id);
+	return ERR_PTR(ret);
+}
+EXPORT_SYMBOL_GPL(ci_hdrc_add_device);
+
+void ci_hdrc_remove_device(struct platform_device *pdev)
+{
+	int id = pdev->id;
+	platform_device_unregister(pdev);
+	ida_simple_remove(&ci_ida, id);
+}
+EXPORT_SYMBOL_GPL(ci_hdrc_remove_device);
+
+static inline void ci_role_destroy(struct ci_hdrc *ci)
+{
+	ci_hdrc_gadget_destroy(ci);
+	ci_hdrc_host_destroy(ci);
+	if (ci->is_otg && ci->roles[CI_ROLE_GADGET])
+		ci_hdrc_otg_destroy(ci);
+}
+
+static void ci_get_otg_capable(struct ci_hdrc *ci)
+{
+	if (ci->platdata->flags & CI_HDRC_DUAL_ROLE_NOT_OTG)
+		ci->is_otg = false;
+	else
+		ci->is_otg = (hw_read(ci, CAP_DCCPARAMS,
+				DCCPARAMS_DC | DCCPARAMS_HC)
+					== (DCCPARAMS_DC | DCCPARAMS_HC));
+	if (ci->is_otg) {
+		dev_dbg(ci->dev, "It is OTG capable controller\n");
+		/* Disable and clear all OTG irq */
+		hw_write_otgsc(ci, OTGSC_INT_EN_BITS | OTGSC_INT_STATUS_BITS,
+							OTGSC_INT_STATUS_BITS);
+	}
+}
+
+static ssize_t role_show(struct device *dev, struct device_attribute *attr,
+			  char *buf)
+{
+	struct ci_hdrc *ci = dev_get_drvdata(dev);
+
+	if (ci->role != CI_ROLE_END)
+		return sprintf(buf, "%s\n", ci_role(ci)->name);
+
+	return 0;
+}
+
+static ssize_t role_store(struct device *dev,
+		struct device_attribute *attr, const char *buf, size_t n)
+{
+	struct ci_hdrc *ci = dev_get_drvdata(dev);
+	enum ci_role role;
+	int ret;
+
+	if (!(ci->roles[CI_ROLE_HOST] && ci->roles[CI_ROLE_GADGET])) {
+		dev_warn(dev, "Current configuration is not dual-role, quit\n");
+		return -EPERM;
+	}
+
+	for (role = CI_ROLE_HOST; role < CI_ROLE_END; role++)
+		if (!strncmp(buf, ci->roles[role]->name,
+			     strlen(ci->roles[role]->name)))
+			break;
+
+	if (role == CI_ROLE_END || role == ci->role)
+		return -EINVAL;
+
+	pm_runtime_get_sync(dev);
+	disable_irq(ci->irq);
+	ci_role_stop(ci);
+	ret = ci_role_start(ci, role);
+	if (!ret && ci->role == CI_ROLE_GADGET)
+		ci_handle_vbus_change(ci);
+	enable_irq(ci->irq);
+	pm_runtime_put_sync(dev);
+
+	return (ret == 0) ? n : ret;
+}
+static DEVICE_ATTR_RW(role);
+
+static struct attribute *ci_attrs[] = {
+	&dev_attr_role.attr,
+	NULL,
+};
+
+static const struct attribute_group ci_attr_group = {
+	.attrs = ci_attrs,
+};
+
+static int ci_hdrc_probe(struct platform_device *pdev)
+{
+	struct device	*dev = &pdev->dev;
+	struct ci_hdrc	*ci;
+	struct resource	*res;
+	void __iomem	*base;
+	int		ret;
+	enum usb_dr_mode dr_mode;
+
+	if (!dev_get_platdata(dev)) {
+		dev_err(dev, "platform data missing\n");
+		return -ENODEV;
+	}
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	base = devm_ioremap_resource(dev, res);
+	if (IS_ERR(base))
+		return PTR_ERR(base);
+
+	ci = devm_kzalloc(dev, sizeof(*ci), GFP_KERNEL);
+	if (!ci)
+		return -ENOMEM;
+
+	spin_lock_init(&ci->lock);
+	ci->dev = dev;
+	ci->platdata = dev_get_platdata(dev);
+	ci->imx28_write_fix = !!(ci->platdata->flags &
+		CI_HDRC_IMX28_WRITE_FIX);
+	ci->supports_runtime_pm = !!(ci->platdata->flags &
+		CI_HDRC_SUPPORTS_RUNTIME_PM);
+	platform_set_drvdata(pdev, ci);
+
+	ret = hw_device_init(ci, base);
+	if (ret < 0) {
+		dev_err(dev, "can't initialize hardware\n");
+		return -ENODEV;
+	}
+
+	ret = ci_ulpi_init(ci);
+	if (ret)
+		return ret;
+
+	if (ci->platdata->phy) {
+		ci->phy = ci->platdata->phy;
+	} else if (ci->platdata->usb_phy) {
+		ci->usb_phy = ci->platdata->usb_phy;
+	} else {
+		ci->phy = devm_phy_get(dev->parent, "usb-phy");
+		ci->usb_phy = devm_usb_get_phy(dev->parent, USB_PHY_TYPE_USB2);
+
+		/* if both generic PHY and USB PHY layers aren't enabled */
+		if (PTR_ERR(ci->phy) == -ENOSYS &&
+				PTR_ERR(ci->usb_phy) == -ENXIO) {
+			ret = -ENXIO;
+			goto ulpi_exit;
+		}
+
+		if (IS_ERR(ci->phy) && IS_ERR(ci->usb_phy)) {
+			ret = -EPROBE_DEFER;
+			goto ulpi_exit;
+		}
+
+		if (IS_ERR(ci->phy))
+			ci->phy = NULL;
+		else if (IS_ERR(ci->usb_phy))
+			ci->usb_phy = NULL;
+	}
+
+	ret = ci_usb_phy_init(ci);
+	if (ret) {
+		dev_err(dev, "unable to init phy: %d\n", ret);
+		return ret;
+	}
+
+	ci->hw_bank.phys = res->start;
+
+	ci->irq = platform_get_irq(pdev, 0);
+	if (ci->irq < 0) {
+		dev_err(dev, "missing IRQ\n");
+		ret = ci->irq;
+		goto deinit_phy;
+	}
+
+	ci_get_otg_capable(ci);
+
+	dr_mode = ci->platdata->dr_mode;
+	/* initialize role(s) before the interrupt is requested */
+	if (dr_mode == USB_DR_MODE_OTG || dr_mode == USB_DR_MODE_HOST) {
+		ret = ci_hdrc_host_init(ci);
+		if (ret) {
+			if (ret == -ENXIO)
+				dev_info(dev, "doesn't support host\n");
+			else
+				goto deinit_phy;
+		}
+	}
+
+	if (dr_mode == USB_DR_MODE_OTG || dr_mode == USB_DR_MODE_PERIPHERAL) {
+		ret = ci_hdrc_gadget_init(ci);
+		if (ret) {
+			if (ret == -ENXIO)
+				dev_info(dev, "doesn't support gadget\n");
+			else
+				goto deinit_host;
+		}
+	}
+
+	if (!ci->roles[CI_ROLE_HOST] && !ci->roles[CI_ROLE_GADGET]) {
+		dev_err(dev, "no supported roles\n");
+		ret = -ENODEV;
+		goto deinit_gadget;
+	}
+
+	if (ci->is_otg && ci->roles[CI_ROLE_GADGET]) {
+		ret = ci_hdrc_otg_init(ci);
+		if (ret) {
+			dev_err(dev, "init otg fails, ret = %d\n", ret);
+			goto deinit_gadget;
+		}
+	}
+
+	if (ci->roles[CI_ROLE_HOST] && ci->roles[CI_ROLE_GADGET]) {
+		if (ci->is_otg) {
+			ci->role = ci_otg_role(ci);
+			/* Enable ID change irq */
+			hw_write_otgsc(ci, OTGSC_IDIE, OTGSC_IDIE);
+		} else {
+			/*
+			 * If the controller is not OTG capable, but support
+			 * role switch, the defalt role is gadget, and the
+			 * user can switch it through debugfs.
+			 */
+			ci->role = CI_ROLE_GADGET;
+		}
+	} else {
+		ci->role = ci->roles[CI_ROLE_HOST]
+			? CI_ROLE_HOST
+			: CI_ROLE_GADGET;
+	}
+
+	if (!ci_otg_is_fsm_mode(ci)) {
+		/* only update vbus status for peripheral */
+		if (ci->role == CI_ROLE_GADGET)
+			ci_handle_vbus_change(ci);
+
+		ret = ci_role_start(ci, ci->role);
+		if (ret) {
+			dev_err(dev, "can't start %s role\n",
+						ci_role(ci)->name);
+			goto stop;
+		}
+	}
+
+	ret = devm_request_irq(dev, ci->irq, ci_irq, IRQF_SHARED,
+			ci->platdata->name, ci);
+	if (ret)
+		goto stop;
+
+	ret = ci_extcon_register(ci);
+	if (ret)
+		goto stop;
+
+	if (ci->supports_runtime_pm) {
+		pm_runtime_set_active(&pdev->dev);
+		pm_runtime_enable(&pdev->dev);
+		pm_runtime_set_autosuspend_delay(&pdev->dev, 2000);
+		pm_runtime_mark_last_busy(ci->dev);
+		pm_runtime_use_autosuspend(&pdev->dev);
+	}
+
+	if (ci_otg_is_fsm_mode(ci))
+		ci_hdrc_otg_fsm_start(ci);
+
+	device_set_wakeup_capable(&pdev->dev, true);
+	dbg_create_files(ci);
+
+	ret = sysfs_create_group(&dev->kobj, &ci_attr_group);
+	if (ret)
+		goto remove_debug;
+
+	return 0;
+
+remove_debug:
+	dbg_remove_files(ci);
+stop:
+	if (ci->is_otg && ci->roles[CI_ROLE_GADGET])
+		ci_hdrc_otg_destroy(ci);
+deinit_gadget:
+	ci_hdrc_gadget_destroy(ci);
+deinit_host:
+	ci_hdrc_host_destroy(ci);
+deinit_phy:
+	ci_usb_phy_exit(ci);
+ulpi_exit:
+	ci_ulpi_exit(ci);
+
+	return ret;
+}
+
+static int ci_hdrc_remove(struct platform_device *pdev)
+{
+	struct ci_hdrc *ci = platform_get_drvdata(pdev);
+
+	if (ci->supports_runtime_pm) {
+		pm_runtime_get_sync(&pdev->dev);
+		pm_runtime_disable(&pdev->dev);
+		pm_runtime_put_noidle(&pdev->dev);
+	}
+
+	dbg_remove_files(ci);
+	sysfs_remove_group(&ci->dev->kobj, &ci_attr_group);
+	ci_role_destroy(ci);
+	ci_hdrc_enter_lpm(ci, true);
+	ci_usb_phy_exit(ci);
+	ci_ulpi_exit(ci);
+
+	return 0;
+}
+
+#ifdef CONFIG_PM
+/* Prepare wakeup by SRP before suspend */
+static void ci_otg_fsm_suspend_for_srp(struct ci_hdrc *ci)
+{
+	if ((ci->fsm.otg->state == OTG_STATE_A_IDLE) &&
+				!hw_read_otgsc(ci, OTGSC_ID)) {
+		hw_write(ci, OP_PORTSC, PORTSC_W1C_BITS | PORTSC_PP,
+								PORTSC_PP);
+		hw_write(ci, OP_PORTSC, PORTSC_W1C_BITS | PORTSC_WKCN,
+								PORTSC_WKCN);
+	}
+}
+
+/* Handle SRP when wakeup by data pulse */
+static void ci_otg_fsm_wakeup_by_srp(struct ci_hdrc *ci)
+{
+	if ((ci->fsm.otg->state == OTG_STATE_A_IDLE) &&
+		(ci->fsm.a_bus_drop == 1) && (ci->fsm.a_bus_req == 0)) {
+		if (!hw_read_otgsc(ci, OTGSC_ID)) {
+			ci->fsm.a_srp_det = 1;
+			ci->fsm.a_bus_drop = 0;
+		} else {
+			ci->fsm.id = 1;
+		}
+		ci_otg_queue_work(ci);
+	}
+}
+
+static void ci_controller_suspend(struct ci_hdrc *ci)
+{
+	disable_irq(ci->irq);
+	ci_hdrc_enter_lpm(ci, true);
+	if (ci->platdata->phy_clkgate_delay_us)
+		usleep_range(ci->platdata->phy_clkgate_delay_us,
+			     ci->platdata->phy_clkgate_delay_us + 50);
+	usb_phy_set_suspend(ci->usb_phy, 1);
+	ci->in_lpm = true;
+	enable_irq(ci->irq);
+}
+
+static int ci_controller_resume(struct device *dev)
+{
+	struct ci_hdrc *ci = dev_get_drvdata(dev);
+	int ret;
+
+	dev_dbg(dev, "at %s\n", __func__);
+
+	if (!ci->in_lpm) {
+		WARN_ON(1);
+		return 0;
+	}
+
+	ci_hdrc_enter_lpm(ci, false);
+
+	ret = ci_ulpi_resume(ci);
+	if (ret)
+		return ret;
+
+	if (ci->usb_phy) {
+		usb_phy_set_suspend(ci->usb_phy, 0);
+		usb_phy_set_wakeup(ci->usb_phy, false);
+		hw_wait_phy_stable();
+	}
+
+	ci->in_lpm = false;
+	if (ci->wakeup_int) {
+		ci->wakeup_int = false;
+		pm_runtime_mark_last_busy(ci->dev);
+		pm_runtime_put_autosuspend(ci->dev);
+		enable_irq(ci->irq);
+		if (ci_otg_is_fsm_mode(ci))
+			ci_otg_fsm_wakeup_by_srp(ci);
+	}
+
+	return 0;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int ci_suspend(struct device *dev)
+{
+	struct ci_hdrc *ci = dev_get_drvdata(dev);
+
+	if (ci->wq)
+		flush_workqueue(ci->wq);
+	/*
+	 * Controller needs to be active during suspend, otherwise the core
+	 * may run resume when the parent is at suspend if other driver's
+	 * suspend fails, it occurs before parent's suspend has not started,
+	 * but the core suspend has finished.
+	 */
+	if (ci->in_lpm)
+		pm_runtime_resume(dev);
+
+	if (ci->in_lpm) {
+		WARN_ON(1);
+		return 0;
+	}
+
+	if (device_may_wakeup(dev)) {
+		if (ci_otg_is_fsm_mode(ci))
+			ci_otg_fsm_suspend_for_srp(ci);
+
+		usb_phy_set_wakeup(ci->usb_phy, true);
+		enable_irq_wake(ci->irq);
+	}
+
+	ci_controller_suspend(ci);
+
+	return 0;
+}
+
+static int ci_resume(struct device *dev)
+{
+	struct ci_hdrc *ci = dev_get_drvdata(dev);
+	int ret;
+
+	if (device_may_wakeup(dev))
+		disable_irq_wake(ci->irq);
+
+	ret = ci_controller_resume(dev);
+	if (ret)
+		return ret;
+
+	if (ci->supports_runtime_pm) {
+		pm_runtime_disable(dev);
+		pm_runtime_set_active(dev);
+		pm_runtime_enable(dev);
+	}
+
+	return ret;
+}
+#endif /* CONFIG_PM_SLEEP */
+
+static int ci_runtime_suspend(struct device *dev)
+{
+	struct ci_hdrc *ci = dev_get_drvdata(dev);
+
+	dev_dbg(dev, "at %s\n", __func__);
+
+	if (ci->in_lpm) {
+		WARN_ON(1);
+		return 0;
+	}
+
+	if (ci_otg_is_fsm_mode(ci))
+		ci_otg_fsm_suspend_for_srp(ci);
+
+	usb_phy_set_wakeup(ci->usb_phy, true);
+	ci_controller_suspend(ci);
+
+	return 0;
+}
+
+static int ci_runtime_resume(struct device *dev)
+{
+	return ci_controller_resume(dev);
+}
+
+#endif /* CONFIG_PM */
+static const struct dev_pm_ops ci_pm_ops = {
+	SET_SYSTEM_SLEEP_PM_OPS(ci_suspend, ci_resume)
+	SET_RUNTIME_PM_OPS(ci_runtime_suspend, ci_runtime_resume, NULL)
+};
+
+static struct platform_driver ci_hdrc_driver = {
+	.probe	= ci_hdrc_probe,
+	.remove	= ci_hdrc_remove,
+	.driver	= {
+		.name	= "ci_hdrc",
+		.pm	= &ci_pm_ops,
+	},
+};
+
+static int __init ci_hdrc_platform_register(void)
+{
+	ci_hdrc_host_driver_init();
+	return platform_driver_register(&ci_hdrc_driver);
+}
+module_init(ci_hdrc_platform_register);
+
+static void __exit ci_hdrc_platform_unregister(void)
+{
+	platform_driver_unregister(&ci_hdrc_driver);
+}
+module_exit(ci_hdrc_platform_unregister);
+
+MODULE_ALIAS("platform:ci_hdrc");
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("David Lopo <dlopo@chipidea.mips.com>");
+MODULE_DESCRIPTION("ChipIdea HDRC Driver");
diff --git a/drivers/usb/chipidea/debug.c b/drivers/usb/chipidea/debug.c
new file mode 100644
index 0000000..fcc91a3
--- /dev/null
+++ b/drivers/usb/chipidea/debug.c
@@ -0,0 +1,374 @@
+// SPDX-License-Identifier: GPL-2.0
+#include <linux/kernel.h>
+#include <linux/device.h>
+#include <linux/types.h>
+#include <linux/spinlock.h>
+#include <linux/debugfs.h>
+#include <linux/seq_file.h>
+#include <linux/uaccess.h>
+#include <linux/usb/ch9.h>
+#include <linux/usb/gadget.h>
+#include <linux/usb/phy.h>
+#include <linux/usb/otg.h>
+#include <linux/usb/otg-fsm.h>
+#include <linux/usb/chipidea.h>
+
+#include "ci.h"
+#include "udc.h"
+#include "bits.h"
+#include "otg.h"
+
+/**
+ * ci_device_show: prints information about device capabilities and status
+ */
+static int ci_device_show(struct seq_file *s, void *data)
+{
+	struct ci_hdrc *ci = s->private;
+	struct usb_gadget *gadget = &ci->gadget;
+
+	seq_printf(s, "speed             = %d\n", gadget->speed);
+	seq_printf(s, "max_speed         = %d\n", gadget->max_speed);
+	seq_printf(s, "is_otg            = %d\n", gadget->is_otg);
+	seq_printf(s, "is_a_peripheral   = %d\n", gadget->is_a_peripheral);
+	seq_printf(s, "b_hnp_enable      = %d\n", gadget->b_hnp_enable);
+	seq_printf(s, "a_hnp_support     = %d\n", gadget->a_hnp_support);
+	seq_printf(s, "a_alt_hnp_support = %d\n", gadget->a_alt_hnp_support);
+	seq_printf(s, "name              = %s\n",
+		   (gadget->name ? gadget->name : ""));
+
+	if (!ci->driver)
+		return 0;
+
+	seq_printf(s, "gadget function   = %s\n",
+		       (ci->driver->function ? ci->driver->function : ""));
+	seq_printf(s, "gadget max speed  = %d\n", ci->driver->max_speed);
+
+	return 0;
+}
+DEFINE_SHOW_ATTRIBUTE(ci_device);
+
+/**
+ * ci_port_test_show: reads port test mode
+ */
+static int ci_port_test_show(struct seq_file *s, void *data)
+{
+	struct ci_hdrc *ci = s->private;
+	unsigned long flags;
+	unsigned mode;
+
+	pm_runtime_get_sync(ci->dev);
+	spin_lock_irqsave(&ci->lock, flags);
+	mode = hw_port_test_get(ci);
+	spin_unlock_irqrestore(&ci->lock, flags);
+	pm_runtime_put_sync(ci->dev);
+
+	seq_printf(s, "mode = %u\n", mode);
+
+	return 0;
+}
+
+/**
+ * ci_port_test_write: writes port test mode
+ */
+static ssize_t ci_port_test_write(struct file *file, const char __user *ubuf,
+				  size_t count, loff_t *ppos)
+{
+	struct seq_file *s = file->private_data;
+	struct ci_hdrc *ci = s->private;
+	unsigned long flags;
+	unsigned mode;
+	char buf[32];
+	int ret;
+
+	count = min_t(size_t, sizeof(buf) - 1, count);
+	if (copy_from_user(buf, ubuf, count))
+		return -EFAULT;
+
+	/* sscanf requires a zero terminated string */
+	buf[count] = '\0';
+
+	if (sscanf(buf, "%u", &mode) != 1)
+		return -EINVAL;
+
+	if (mode > 255)
+		return -EBADRQC;
+
+	pm_runtime_get_sync(ci->dev);
+	spin_lock_irqsave(&ci->lock, flags);
+	ret = hw_port_test_set(ci, mode);
+	spin_unlock_irqrestore(&ci->lock, flags);
+	pm_runtime_put_sync(ci->dev);
+
+	return ret ? ret : count;
+}
+
+static int ci_port_test_open(struct inode *inode, struct file *file)
+{
+	return single_open(file, ci_port_test_show, inode->i_private);
+}
+
+static const struct file_operations ci_port_test_fops = {
+	.open		= ci_port_test_open,
+	.write		= ci_port_test_write,
+	.read		= seq_read,
+	.llseek		= seq_lseek,
+	.release	= single_release,
+};
+
+/**
+ * ci_qheads_show: DMA contents of all queue heads
+ */
+static int ci_qheads_show(struct seq_file *s, void *data)
+{
+	struct ci_hdrc *ci = s->private;
+	unsigned long flags;
+	unsigned i, j;
+
+	if (ci->role != CI_ROLE_GADGET) {
+		seq_printf(s, "not in gadget mode\n");
+		return 0;
+	}
+
+	spin_lock_irqsave(&ci->lock, flags);
+	for (i = 0; i < ci->hw_ep_max/2; i++) {
+		struct ci_hw_ep *hweprx = &ci->ci_hw_ep[i];
+		struct ci_hw_ep *hweptx =
+			&ci->ci_hw_ep[i + ci->hw_ep_max/2];
+		seq_printf(s, "EP=%02i: RX=%08X TX=%08X\n",
+			   i, (u32)hweprx->qh.dma, (u32)hweptx->qh.dma);
+		for (j = 0; j < (sizeof(struct ci_hw_qh)/sizeof(u32)); j++)
+			seq_printf(s, " %04X:    %08X    %08X\n", j,
+				   *((u32 *)hweprx->qh.ptr + j),
+				   *((u32 *)hweptx->qh.ptr + j));
+	}
+	spin_unlock_irqrestore(&ci->lock, flags);
+
+	return 0;
+}
+DEFINE_SHOW_ATTRIBUTE(ci_qheads);
+
+/**
+ * ci_requests_show: DMA contents of all requests currently queued (all endpts)
+ */
+static int ci_requests_show(struct seq_file *s, void *data)
+{
+	struct ci_hdrc *ci = s->private;
+	unsigned long flags;
+	struct ci_hw_req *req = NULL;
+	struct td_node *node, *tmpnode;
+	unsigned i, j, qsize = sizeof(struct ci_hw_td)/sizeof(u32);
+
+	if (ci->role != CI_ROLE_GADGET) {
+		seq_printf(s, "not in gadget mode\n");
+		return 0;
+	}
+
+	spin_lock_irqsave(&ci->lock, flags);
+	for (i = 0; i < ci->hw_ep_max; i++)
+		list_for_each_entry(req, &ci->ci_hw_ep[i].qh.queue, queue) {
+			list_for_each_entry_safe(node, tmpnode, &req->tds, td) {
+				seq_printf(s, "EP=%02i: TD=%08X %s\n",
+					   i % (ci->hw_ep_max / 2),
+					   (u32)node->dma,
+					   ((i < ci->hw_ep_max/2) ?
+					   "RX" : "TX"));
+
+				for (j = 0; j < qsize; j++)
+					seq_printf(s, " %04X:    %08X\n", j,
+						   *((u32 *)node->ptr + j));
+			}
+		}
+	spin_unlock_irqrestore(&ci->lock, flags);
+
+	return 0;
+}
+DEFINE_SHOW_ATTRIBUTE(ci_requests);
+
+static int ci_otg_show(struct seq_file *s, void *unused)
+{
+	struct ci_hdrc *ci = s->private;
+	struct otg_fsm *fsm;
+
+	if (!ci || !ci_otg_is_fsm_mode(ci))
+		return 0;
+
+	fsm = &ci->fsm;
+
+	/* ------ State ----- */
+	seq_printf(s, "OTG state: %s\n\n",
+			usb_otg_state_string(ci->otg.state));
+
+	/* ------ State Machine Variables ----- */
+	seq_printf(s, "a_bus_drop: %d\n", fsm->a_bus_drop);
+
+	seq_printf(s, "a_bus_req: %d\n", fsm->a_bus_req);
+
+	seq_printf(s, "a_srp_det: %d\n", fsm->a_srp_det);
+
+	seq_printf(s, "a_vbus_vld: %d\n", fsm->a_vbus_vld);
+
+	seq_printf(s, "b_conn: %d\n", fsm->b_conn);
+
+	seq_printf(s, "adp_change: %d\n", fsm->adp_change);
+
+	seq_printf(s, "power_up: %d\n", fsm->power_up);
+
+	seq_printf(s, "a_bus_resume: %d\n", fsm->a_bus_resume);
+
+	seq_printf(s, "a_bus_suspend: %d\n", fsm->a_bus_suspend);
+
+	seq_printf(s, "a_conn: %d\n", fsm->a_conn);
+
+	seq_printf(s, "b_bus_req: %d\n", fsm->b_bus_req);
+
+	seq_printf(s, "b_bus_suspend: %d\n", fsm->b_bus_suspend);
+
+	seq_printf(s, "b_se0_srp: %d\n", fsm->b_se0_srp);
+
+	seq_printf(s, "b_ssend_srp: %d\n", fsm->b_ssend_srp);
+
+	seq_printf(s, "b_sess_vld: %d\n", fsm->b_sess_vld);
+
+	seq_printf(s, "b_srp_done: %d\n", fsm->b_srp_done);
+
+	seq_printf(s, "drv_vbus: %d\n", fsm->drv_vbus);
+
+	seq_printf(s, "loc_conn: %d\n", fsm->loc_conn);
+
+	seq_printf(s, "loc_sof: %d\n", fsm->loc_sof);
+
+	seq_printf(s, "adp_prb: %d\n", fsm->adp_prb);
+
+	seq_printf(s, "id: %d\n", fsm->id);
+
+	seq_printf(s, "protocol: %d\n", fsm->protocol);
+
+	return 0;
+}
+DEFINE_SHOW_ATTRIBUTE(ci_otg);
+
+static int ci_role_show(struct seq_file *s, void *data)
+{
+	struct ci_hdrc *ci = s->private;
+
+	if (ci->role != CI_ROLE_END)
+		seq_printf(s, "%s\n", ci_role(ci)->name);
+
+	return 0;
+}
+
+static ssize_t ci_role_write(struct file *file, const char __user *ubuf,
+			     size_t count, loff_t *ppos)
+{
+	struct seq_file *s = file->private_data;
+	struct ci_hdrc *ci = s->private;
+	enum ci_role role;
+	char buf[8];
+	int ret;
+
+	if (copy_from_user(buf, ubuf, min_t(size_t, sizeof(buf) - 1, count)))
+		return -EFAULT;
+
+	for (role = CI_ROLE_HOST; role < CI_ROLE_END; role++)
+		if (ci->roles[role] &&
+		    !strncmp(buf, ci->roles[role]->name,
+			     strlen(ci->roles[role]->name)))
+			break;
+
+	if (role == CI_ROLE_END || role == ci->role)
+		return -EINVAL;
+
+	pm_runtime_get_sync(ci->dev);
+	disable_irq(ci->irq);
+	ci_role_stop(ci);
+	ret = ci_role_start(ci, role);
+	enable_irq(ci->irq);
+	pm_runtime_put_sync(ci->dev);
+
+	return ret ? ret : count;
+}
+
+static int ci_role_open(struct inode *inode, struct file *file)
+{
+	return single_open(file, ci_role_show, inode->i_private);
+}
+
+static const struct file_operations ci_role_fops = {
+	.open		= ci_role_open,
+	.write		= ci_role_write,
+	.read		= seq_read,
+	.llseek		= seq_lseek,
+	.release	= single_release,
+};
+
+static int ci_registers_show(struct seq_file *s, void *unused)
+{
+	struct ci_hdrc *ci = s->private;
+	u32 tmp_reg;
+
+	if (!ci || ci->in_lpm)
+		return -EPERM;
+
+	/* ------ Registers ----- */
+	tmp_reg = hw_read_intr_enable(ci);
+	seq_printf(s, "USBINTR reg: %08x\n", tmp_reg);
+
+	tmp_reg = hw_read_intr_status(ci);
+	seq_printf(s, "USBSTS reg: %08x\n", tmp_reg);
+
+	tmp_reg = hw_read(ci, OP_USBMODE, ~0);
+	seq_printf(s, "USBMODE reg: %08x\n", tmp_reg);
+
+	tmp_reg = hw_read(ci, OP_USBCMD, ~0);
+	seq_printf(s, "USBCMD reg: %08x\n", tmp_reg);
+
+	tmp_reg = hw_read(ci, OP_PORTSC, ~0);
+	seq_printf(s, "PORTSC reg: %08x\n", tmp_reg);
+
+	if (ci->is_otg) {
+		tmp_reg = hw_read_otgsc(ci, ~0);
+		seq_printf(s, "OTGSC reg: %08x\n", tmp_reg);
+	}
+
+	return 0;
+}
+DEFINE_SHOW_ATTRIBUTE(ci_registers);
+
+/**
+ * dbg_create_files: initializes the attribute interface
+ * @ci: device
+ *
+ * This function returns an error code
+ */
+void dbg_create_files(struct ci_hdrc *ci)
+{
+	ci->debugfs = debugfs_create_dir(dev_name(ci->dev), NULL);
+
+	debugfs_create_file("device", S_IRUGO, ci->debugfs, ci,
+			    &ci_device_fops);
+	debugfs_create_file("port_test", S_IRUGO | S_IWUSR, ci->debugfs, ci,
+			    &ci_port_test_fops);
+	debugfs_create_file("qheads", S_IRUGO, ci->debugfs, ci,
+			    &ci_qheads_fops);
+	debugfs_create_file("requests", S_IRUGO, ci->debugfs, ci,
+			    &ci_requests_fops);
+
+	if (ci_otg_is_fsm_mode(ci)) {
+		debugfs_create_file("otg", S_IRUGO, ci->debugfs, ci,
+				    &ci_otg_fops);
+	}
+
+	debugfs_create_file("role", S_IRUGO | S_IWUSR, ci->debugfs, ci,
+			    &ci_role_fops);
+	debugfs_create_file("registers", S_IRUGO, ci->debugfs, ci,
+			    &ci_registers_fops);
+}
+
+/**
+ * dbg_remove_files: destroys the attribute interface
+ * @ci: device
+ */
+void dbg_remove_files(struct ci_hdrc *ci)
+{
+	debugfs_remove_recursive(ci->debugfs);
+}
diff --git a/drivers/usb/chipidea/host.c b/drivers/usb/chipidea/host.c
new file mode 100644
index 0000000..4638d9b
--- /dev/null
+++ b/drivers/usb/chipidea/host.c
@@ -0,0 +1,275 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * host.c - ChipIdea USB host controller driver
+ *
+ * Copyright (c) 2012 Intel Corporation
+ *
+ * Author: Alexander Shishkin
+ */
+
+#include <linux/kernel.h>
+#include <linux/io.h>
+#include <linux/usb.h>
+#include <linux/usb/hcd.h>
+#include <linux/usb/chipidea.h>
+#include <linux/regulator/consumer.h>
+
+#include "../host/ehci.h"
+
+#include "ci.h"
+#include "bits.h"
+#include "host.h"
+
+static struct hc_driver __read_mostly ci_ehci_hc_driver;
+static int (*orig_bus_suspend)(struct usb_hcd *hcd);
+
+struct ehci_ci_priv {
+	struct regulator *reg_vbus;
+};
+
+static int ehci_ci_portpower(struct usb_hcd *hcd, int portnum, bool enable)
+{
+	struct ehci_hcd *ehci = hcd_to_ehci(hcd);
+	struct ehci_ci_priv *priv = (struct ehci_ci_priv *)ehci->priv;
+	struct device *dev = hcd->self.controller;
+	struct ci_hdrc *ci = dev_get_drvdata(dev);
+	int ret = 0;
+	int port = HCS_N_PORTS(ehci->hcs_params);
+
+	if (priv->reg_vbus) {
+		if (port > 1) {
+			dev_warn(dev,
+				"Not support multi-port regulator control\n");
+			return 0;
+		}
+		if (enable)
+			ret = regulator_enable(priv->reg_vbus);
+		else
+			ret = regulator_disable(priv->reg_vbus);
+		if (ret) {
+			dev_err(dev,
+				"Failed to %s vbus regulator, ret=%d\n",
+				enable ? "enable" : "disable", ret);
+			return ret;
+		}
+	}
+
+	if (enable && (ci->platdata->phy_mode == USBPHY_INTERFACE_MODE_HSIC)) {
+		/*
+		 * Marvell 28nm HSIC PHY requires forcing the port to HS mode.
+		 * As HSIC is always HS, this should be safe for others.
+		 */
+		hw_port_test_set(ci, 5);
+		hw_port_test_set(ci, 0);
+	}
+	return 0;
+};
+
+static int ehci_ci_reset(struct usb_hcd *hcd)
+{
+	struct device *dev = hcd->self.controller;
+	struct ci_hdrc *ci = dev_get_drvdata(dev);
+	struct ehci_hcd *ehci = hcd_to_ehci(hcd);
+	int ret;
+
+	ret = ehci_setup(hcd);
+	if (ret)
+		return ret;
+
+	ehci->need_io_watchdog = 0;
+
+	if (ci->platdata->notify_event) {
+		ret = ci->platdata->notify_event(ci,
+				CI_HDRC_CONTROLLER_RESET_EVENT);
+		if (ret)
+			return ret;
+	}
+
+	ci_platform_configure(ci);
+
+	return ret;
+}
+
+static const struct ehci_driver_overrides ehci_ci_overrides = {
+	.extra_priv_size = sizeof(struct ehci_ci_priv),
+	.port_power	 = ehci_ci_portpower,
+	.reset		 = ehci_ci_reset,
+};
+
+static irqreturn_t host_irq(struct ci_hdrc *ci)
+{
+	return usb_hcd_irq(ci->irq, ci->hcd);
+}
+
+static int host_start(struct ci_hdrc *ci)
+{
+	struct usb_hcd *hcd;
+	struct ehci_hcd *ehci;
+	struct ehci_ci_priv *priv;
+	int ret;
+
+	if (usb_disabled())
+		return -ENODEV;
+
+	hcd = __usb_create_hcd(&ci_ehci_hc_driver, ci->dev->parent,
+			       ci->dev, dev_name(ci->dev), NULL);
+	if (!hcd)
+		return -ENOMEM;
+
+	dev_set_drvdata(ci->dev, ci);
+	hcd->rsrc_start = ci->hw_bank.phys;
+	hcd->rsrc_len = ci->hw_bank.size;
+	hcd->regs = ci->hw_bank.abs;
+	hcd->has_tt = 1;
+
+	hcd->power_budget = ci->platdata->power_budget;
+	hcd->tpl_support = ci->platdata->tpl_support;
+	if (ci->phy || ci->usb_phy) {
+		hcd->skip_phy_initialization = 1;
+		if (ci->usb_phy)
+			hcd->usb_phy = ci->usb_phy;
+	}
+
+	ehci = hcd_to_ehci(hcd);
+	ehci->caps = ci->hw_bank.cap;
+	ehci->has_hostpc = ci->hw_bank.lpm;
+	ehci->has_tdi_phy_lpm = ci->hw_bank.lpm;
+	ehci->imx28_write_fix = ci->imx28_write_fix;
+
+	priv = (struct ehci_ci_priv *)ehci->priv;
+	priv->reg_vbus = NULL;
+
+	if (ci->platdata->reg_vbus && !ci_otg_is_fsm_mode(ci)) {
+		if (ci->platdata->flags & CI_HDRC_TURN_VBUS_EARLY_ON) {
+			ret = regulator_enable(ci->platdata->reg_vbus);
+			if (ret) {
+				dev_err(ci->dev,
+				"Failed to enable vbus regulator, ret=%d\n",
+									ret);
+				goto put_hcd;
+			}
+		} else {
+			priv->reg_vbus = ci->platdata->reg_vbus;
+		}
+	}
+
+	ret = usb_add_hcd(hcd, 0, 0);
+	if (ret) {
+		goto disable_reg;
+	} else {
+		struct usb_otg *otg = &ci->otg;
+
+		ci->hcd = hcd;
+
+		if (ci_otg_is_fsm_mode(ci)) {
+			otg->host = &hcd->self;
+			hcd->self.otg_port = 1;
+		}
+	}
+
+	return ret;
+
+disable_reg:
+	if (ci->platdata->reg_vbus && !ci_otg_is_fsm_mode(ci) &&
+			(ci->platdata->flags & CI_HDRC_TURN_VBUS_EARLY_ON))
+		regulator_disable(ci->platdata->reg_vbus);
+put_hcd:
+	usb_put_hcd(hcd);
+
+	return ret;
+}
+
+static void host_stop(struct ci_hdrc *ci)
+{
+	struct usb_hcd *hcd = ci->hcd;
+
+	if (hcd) {
+		if (ci->platdata->notify_event)
+			ci->platdata->notify_event(ci,
+				CI_HDRC_CONTROLLER_STOPPED_EVENT);
+		usb_remove_hcd(hcd);
+		ci->role = CI_ROLE_END;
+		synchronize_irq(ci->irq);
+		usb_put_hcd(hcd);
+		if (ci->platdata->reg_vbus && !ci_otg_is_fsm_mode(ci) &&
+			(ci->platdata->flags & CI_HDRC_TURN_VBUS_EARLY_ON))
+				regulator_disable(ci->platdata->reg_vbus);
+	}
+	ci->hcd = NULL;
+	ci->otg.host = NULL;
+}
+
+
+void ci_hdrc_host_destroy(struct ci_hdrc *ci)
+{
+	if (ci->role == CI_ROLE_HOST && ci->hcd)
+		host_stop(ci);
+}
+
+static int ci_ehci_bus_suspend(struct usb_hcd *hcd)
+{
+	struct ehci_hcd *ehci = hcd_to_ehci(hcd);
+	int port;
+	u32 tmp;
+
+	int ret = orig_bus_suspend(hcd);
+
+	if (ret)
+		return ret;
+
+	port = HCS_N_PORTS(ehci->hcs_params);
+	while (port--) {
+		u32 __iomem *reg = &ehci->regs->port_status[port];
+		u32 portsc = ehci_readl(ehci, reg);
+
+		if (portsc & PORT_CONNECT) {
+			/*
+			 * For chipidea, the resume signal will be ended
+			 * automatically, so for remote wakeup case, the
+			 * usbcmd.rs may not be set before the resume has
+			 * ended if other resume paths consumes too much
+			 * time (~24ms), in that case, the SOF will not
+			 * send out within 3ms after resume ends, then the
+			 * high speed device will enter full speed mode.
+			 */
+
+			tmp = ehci_readl(ehci, &ehci->regs->command);
+			tmp |= CMD_RUN;
+			ehci_writel(ehci, tmp, &ehci->regs->command);
+			/*
+			 * It needs a short delay between set RS bit and PHCD.
+			 */
+			usleep_range(150, 200);
+			break;
+		}
+	}
+
+	return 0;
+}
+
+int ci_hdrc_host_init(struct ci_hdrc *ci)
+{
+	struct ci_role_driver *rdrv;
+
+	if (!hw_read(ci, CAP_DCCPARAMS, DCCPARAMS_HC))
+		return -ENXIO;
+
+	rdrv = devm_kzalloc(ci->dev, sizeof(struct ci_role_driver), GFP_KERNEL);
+	if (!rdrv)
+		return -ENOMEM;
+
+	rdrv->start	= host_start;
+	rdrv->stop	= host_stop;
+	rdrv->irq	= host_irq;
+	rdrv->name	= "host";
+	ci->roles[CI_ROLE_HOST] = rdrv;
+
+	return 0;
+}
+
+void ci_hdrc_host_driver_init(void)
+{
+	ehci_init_driver(&ci_ehci_hc_driver, &ehci_ci_overrides);
+	orig_bus_suspend = ci_ehci_hc_driver.bus_suspend;
+	ci_ehci_hc_driver.bus_suspend = ci_ehci_bus_suspend;
+}
diff --git a/drivers/usb/chipidea/host.h b/drivers/usb/chipidea/host.h
new file mode 100644
index 0000000..70112cf
--- /dev/null
+++ b/drivers/usb/chipidea/host.h
@@ -0,0 +1,30 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef __DRIVERS_USB_CHIPIDEA_HOST_H
+#define __DRIVERS_USB_CHIPIDEA_HOST_H
+
+#ifdef CONFIG_USB_CHIPIDEA_HOST
+
+int ci_hdrc_host_init(struct ci_hdrc *ci);
+void ci_hdrc_host_destroy(struct ci_hdrc *ci);
+void ci_hdrc_host_driver_init(void);
+
+#else
+
+static inline int ci_hdrc_host_init(struct ci_hdrc *ci)
+{
+	return -ENXIO;
+}
+
+static inline void ci_hdrc_host_destroy(struct ci_hdrc *ci)
+{
+
+}
+
+static void ci_hdrc_host_driver_init(void)
+{
+
+}
+
+#endif
+
+#endif /* __DRIVERS_USB_CHIPIDEA_HOST_H */
diff --git a/drivers/usb/chipidea/otg.c b/drivers/usb/chipidea/otg.c
new file mode 100644
index 0000000..db4ceff
--- /dev/null
+++ b/drivers/usb/chipidea/otg.c
@@ -0,0 +1,254 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * otg.c - ChipIdea USB IP core OTG driver
+ *
+ * Copyright (C) 2013 Freescale Semiconductor, Inc.
+ *
+ * Author: Peter Chen
+ */
+
+/*
+ * This file mainly handles otgsc register, OTG fsm operations for HNP and SRP
+ * are also included.
+ */
+
+#include <linux/usb/otg.h>
+#include <linux/usb/gadget.h>
+#include <linux/usb/chipidea.h>
+
+#include "ci.h"
+#include "bits.h"
+#include "otg.h"
+#include "otg_fsm.h"
+
+/**
+ * hw_read_otgsc returns otgsc register bits value.
+ * @mask: bitfield mask
+ */
+u32 hw_read_otgsc(struct ci_hdrc *ci, u32 mask)
+{
+	struct ci_hdrc_cable *cable;
+	u32 val = hw_read(ci, OP_OTGSC, mask);
+
+	/*
+	 * If using extcon framework for VBUS and/or ID signal
+	 * detection overwrite OTGSC register value
+	 */
+	cable = &ci->platdata->vbus_extcon;
+	if (!IS_ERR(cable->edev)) {
+		if (cable->changed)
+			val |= OTGSC_BSVIS;
+		else
+			val &= ~OTGSC_BSVIS;
+
+		if (cable->connected)
+			val |= OTGSC_BSV;
+		else
+			val &= ~OTGSC_BSV;
+
+		if (cable->enabled)
+			val |= OTGSC_BSVIE;
+		else
+			val &= ~OTGSC_BSVIE;
+	}
+
+	cable = &ci->platdata->id_extcon;
+	if (!IS_ERR(cable->edev)) {
+		if (cable->changed)
+			val |= OTGSC_IDIS;
+		else
+			val &= ~OTGSC_IDIS;
+
+		if (cable->connected)
+			val &= ~OTGSC_ID; /* host */
+		else
+			val |= OTGSC_ID; /* device */
+
+		if (cable->enabled)
+			val |= OTGSC_IDIE;
+		else
+			val &= ~OTGSC_IDIE;
+	}
+
+	return val & mask;
+}
+
+/**
+ * hw_write_otgsc updates target bits of OTGSC register.
+ * @mask: bitfield mask
+ * @data: to be written
+ */
+void hw_write_otgsc(struct ci_hdrc *ci, u32 mask, u32 data)
+{
+	struct ci_hdrc_cable *cable;
+
+	cable = &ci->platdata->vbus_extcon;
+	if (!IS_ERR(cable->edev)) {
+		if (data & mask & OTGSC_BSVIS)
+			cable->changed = false;
+
+		/* Don't enable vbus interrupt if using external notifier */
+		if (data & mask & OTGSC_BSVIE) {
+			cable->enabled = true;
+			data &= ~OTGSC_BSVIE;
+		} else if (mask & OTGSC_BSVIE) {
+			cable->enabled = false;
+		}
+	}
+
+	cable = &ci->platdata->id_extcon;
+	if (!IS_ERR(cable->edev)) {
+		if (data & mask & OTGSC_IDIS)
+			cable->changed = false;
+
+		/* Don't enable id interrupt if using external notifier */
+		if (data & mask & OTGSC_IDIE) {
+			cable->enabled = true;
+			data &= ~OTGSC_IDIE;
+		} else if (mask & OTGSC_IDIE) {
+			cable->enabled = false;
+		}
+	}
+
+	hw_write(ci, OP_OTGSC, mask | OTGSC_INT_STATUS_BITS, data);
+}
+
+/**
+ * ci_otg_role - pick role based on ID pin state
+ * @ci: the controller
+ */
+enum ci_role ci_otg_role(struct ci_hdrc *ci)
+{
+	enum ci_role role = hw_read_otgsc(ci, OTGSC_ID)
+		? CI_ROLE_GADGET
+		: CI_ROLE_HOST;
+
+	return role;
+}
+
+void ci_handle_vbus_change(struct ci_hdrc *ci)
+{
+	if (!ci->is_otg)
+		return;
+
+	if (hw_read_otgsc(ci, OTGSC_BSV) && !ci->vbus_active)
+		usb_gadget_vbus_connect(&ci->gadget);
+	else if (!hw_read_otgsc(ci, OTGSC_BSV) && ci->vbus_active)
+		usb_gadget_vbus_disconnect(&ci->gadget);
+}
+
+/**
+ * When we switch to device mode, the vbus value should be lower
+ * than OTGSC_BSV before connecting to host.
+ *
+ * @ci: the controller
+ *
+ * This function returns an error code if timeout
+ */
+static int hw_wait_vbus_lower_bsv(struct ci_hdrc *ci)
+{
+	unsigned long elapse = jiffies + msecs_to_jiffies(5000);
+	u32 mask = OTGSC_BSV;
+
+	while (hw_read_otgsc(ci, mask)) {
+		if (time_after(jiffies, elapse)) {
+			dev_err(ci->dev, "timeout waiting for %08x in OTGSC\n",
+					mask);
+			return -ETIMEDOUT;
+		}
+		msleep(20);
+	}
+
+	return 0;
+}
+
+static void ci_handle_id_switch(struct ci_hdrc *ci)
+{
+	enum ci_role role = ci_otg_role(ci);
+
+	if (role != ci->role) {
+		dev_dbg(ci->dev, "switching from %s to %s\n",
+			ci_role(ci)->name, ci->roles[role]->name);
+
+		ci_role_stop(ci);
+
+		if (role == CI_ROLE_GADGET &&
+				IS_ERR(ci->platdata->vbus_extcon.edev))
+			/*
+			 * Wait vbus lower than OTGSC_BSV before connecting
+			 * to host. If connecting status is from an external
+			 * connector instead of register, we don't need to
+			 * care vbus on the board, since it will not affect
+			 * external connector status.
+			 */
+			hw_wait_vbus_lower_bsv(ci);
+
+		ci_role_start(ci, role);
+		/* vbus change may have already occurred */
+		if (role == CI_ROLE_GADGET)
+			ci_handle_vbus_change(ci);
+	}
+}
+/**
+ * ci_otg_work - perform otg (vbus/id) event handle
+ * @work: work struct
+ */
+static void ci_otg_work(struct work_struct *work)
+{
+	struct ci_hdrc *ci = container_of(work, struct ci_hdrc, work);
+
+	if (ci_otg_is_fsm_mode(ci) && !ci_otg_fsm_work(ci)) {
+		enable_irq(ci->irq);
+		return;
+	}
+
+	pm_runtime_get_sync(ci->dev);
+	if (ci->id_event) {
+		ci->id_event = false;
+		ci_handle_id_switch(ci);
+	} else if (ci->b_sess_valid_event) {
+		ci->b_sess_valid_event = false;
+		ci_handle_vbus_change(ci);
+	} else
+		dev_err(ci->dev, "unexpected event occurs at %s\n", __func__);
+	pm_runtime_put_sync(ci->dev);
+
+	enable_irq(ci->irq);
+}
+
+
+/**
+ * ci_hdrc_otg_init - initialize otg struct
+ * ci: the controller
+ */
+int ci_hdrc_otg_init(struct ci_hdrc *ci)
+{
+	INIT_WORK(&ci->work, ci_otg_work);
+	ci->wq = create_freezable_workqueue("ci_otg");
+	if (!ci->wq) {
+		dev_err(ci->dev, "can't create workqueue\n");
+		return -ENODEV;
+	}
+
+	if (ci_otg_is_fsm_mode(ci))
+		return ci_hdrc_otg_fsm_init(ci);
+
+	return 0;
+}
+
+/**
+ * ci_hdrc_otg_destroy - destroy otg struct
+ * ci: the controller
+ */
+void ci_hdrc_otg_destroy(struct ci_hdrc *ci)
+{
+	if (ci->wq) {
+		flush_workqueue(ci->wq);
+		destroy_workqueue(ci->wq);
+	}
+	/* Disable all OTG irq and clear status */
+	hw_write_otgsc(ci, OTGSC_INT_EN_BITS | OTGSC_INT_STATUS_BITS,
+						OTGSC_INT_STATUS_BITS);
+	if (ci_otg_is_fsm_mode(ci))
+		ci_hdrc_otg_fsm_remove(ci);
+}
diff --git a/drivers/usb/chipidea/otg.h b/drivers/usb/chipidea/otg.h
new file mode 100644
index 0000000..4f8b817
--- /dev/null
+++ b/drivers/usb/chipidea/otg.h
@@ -0,0 +1,24 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2013-2014 Freescale Semiconductor, Inc.
+ *
+ * Author: Peter Chen
+ */
+
+#ifndef __DRIVERS_USB_CHIPIDEA_OTG_H
+#define __DRIVERS_USB_CHIPIDEA_OTG_H
+
+u32 hw_read_otgsc(struct ci_hdrc *ci, u32 mask);
+void hw_write_otgsc(struct ci_hdrc *ci, u32 mask, u32 data);
+int ci_hdrc_otg_init(struct ci_hdrc *ci);
+void ci_hdrc_otg_destroy(struct ci_hdrc *ci);
+enum ci_role ci_otg_role(struct ci_hdrc *ci);
+void ci_handle_vbus_change(struct ci_hdrc *ci);
+static inline void ci_otg_queue_work(struct ci_hdrc *ci)
+{
+	disable_irq_nosync(ci->irq);
+	if (queue_work(ci->wq, &ci->work) == false)
+		enable_irq(ci->irq);
+}
+
+#endif /* __DRIVERS_USB_CHIPIDEA_OTG_H */
diff --git a/drivers/usb/chipidea/otg_fsm.c b/drivers/usb/chipidea/otg_fsm.c
new file mode 100644
index 0000000..6ed4b00
--- /dev/null
+++ b/drivers/usb/chipidea/otg_fsm.c
@@ -0,0 +1,844 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * otg_fsm.c - ChipIdea USB IP core OTG FSM driver
+ *
+ * Copyright (C) 2014 Freescale Semiconductor, Inc.
+ *
+ * Author: Jun Li
+ */
+
+/*
+ * This file mainly handles OTG fsm, it includes OTG fsm operations
+ * for HNP and SRP.
+ *
+ * TODO List
+ * - ADP
+ * - OTG test device
+ */
+
+#include <linux/usb/otg.h>
+#include <linux/usb/gadget.h>
+#include <linux/usb/hcd.h>
+#include <linux/usb/chipidea.h>
+#include <linux/regulator/consumer.h>
+
+#include "ci.h"
+#include "bits.h"
+#include "otg.h"
+#include "otg_fsm.h"
+
+/* Add for otg: interact with user space app */
+static ssize_t
+a_bus_req_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+	char		*next;
+	unsigned	size, t;
+	struct ci_hdrc	*ci = dev_get_drvdata(dev);
+
+	next = buf;
+	size = PAGE_SIZE;
+	t = scnprintf(next, size, "%d\n", ci->fsm.a_bus_req);
+	size -= t;
+	next += t;
+
+	return PAGE_SIZE - size;
+}
+
+static ssize_t
+a_bus_req_store(struct device *dev, struct device_attribute *attr,
+					const char *buf, size_t count)
+{
+	struct ci_hdrc *ci = dev_get_drvdata(dev);
+
+	if (count > 2)
+		return -1;
+
+	mutex_lock(&ci->fsm.lock);
+	if (buf[0] == '0') {
+		ci->fsm.a_bus_req = 0;
+	} else if (buf[0] == '1') {
+		/* If a_bus_drop is TRUE, a_bus_req can't be set */
+		if (ci->fsm.a_bus_drop) {
+			mutex_unlock(&ci->fsm.lock);
+			return count;
+		}
+		ci->fsm.a_bus_req = 1;
+		if (ci->fsm.otg->state == OTG_STATE_A_PERIPHERAL) {
+			ci->gadget.host_request_flag = 1;
+			mutex_unlock(&ci->fsm.lock);
+			return count;
+		}
+	}
+
+	ci_otg_queue_work(ci);
+	mutex_unlock(&ci->fsm.lock);
+
+	return count;
+}
+static DEVICE_ATTR_RW(a_bus_req);
+
+static ssize_t
+a_bus_drop_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+	char		*next;
+	unsigned	size, t;
+	struct ci_hdrc	*ci = dev_get_drvdata(dev);
+
+	next = buf;
+	size = PAGE_SIZE;
+	t = scnprintf(next, size, "%d\n", ci->fsm.a_bus_drop);
+	size -= t;
+	next += t;
+
+	return PAGE_SIZE - size;
+}
+
+static ssize_t
+a_bus_drop_store(struct device *dev, struct device_attribute *attr,
+					const char *buf, size_t count)
+{
+	struct ci_hdrc	*ci = dev_get_drvdata(dev);
+
+	if (count > 2)
+		return -1;
+
+	mutex_lock(&ci->fsm.lock);
+	if (buf[0] == '0') {
+		ci->fsm.a_bus_drop = 0;
+	} else if (buf[0] == '1') {
+		ci->fsm.a_bus_drop = 1;
+		ci->fsm.a_bus_req = 0;
+	}
+
+	ci_otg_queue_work(ci);
+	mutex_unlock(&ci->fsm.lock);
+
+	return count;
+}
+static DEVICE_ATTR_RW(a_bus_drop);
+
+static ssize_t
+b_bus_req_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+	char		*next;
+	unsigned	size, t;
+	struct ci_hdrc	*ci = dev_get_drvdata(dev);
+
+	next = buf;
+	size = PAGE_SIZE;
+	t = scnprintf(next, size, "%d\n", ci->fsm.b_bus_req);
+	size -= t;
+	next += t;
+
+	return PAGE_SIZE - size;
+}
+
+static ssize_t
+b_bus_req_store(struct device *dev, struct device_attribute *attr,
+					const char *buf, size_t count)
+{
+	struct ci_hdrc	*ci = dev_get_drvdata(dev);
+
+	if (count > 2)
+		return -1;
+
+	mutex_lock(&ci->fsm.lock);
+	if (buf[0] == '0')
+		ci->fsm.b_bus_req = 0;
+	else if (buf[0] == '1') {
+		ci->fsm.b_bus_req = 1;
+		if (ci->fsm.otg->state == OTG_STATE_B_PERIPHERAL) {
+			ci->gadget.host_request_flag = 1;
+			mutex_unlock(&ci->fsm.lock);
+			return count;
+		}
+	}
+
+	ci_otg_queue_work(ci);
+	mutex_unlock(&ci->fsm.lock);
+
+	return count;
+}
+static DEVICE_ATTR_RW(b_bus_req);
+
+static ssize_t
+a_clr_err_store(struct device *dev, struct device_attribute *attr,
+					const char *buf, size_t count)
+{
+	struct ci_hdrc	*ci = dev_get_drvdata(dev);
+
+	if (count > 2)
+		return -1;
+
+	mutex_lock(&ci->fsm.lock);
+	if (buf[0] == '1')
+		ci->fsm.a_clr_err = 1;
+
+	ci_otg_queue_work(ci);
+	mutex_unlock(&ci->fsm.lock);
+
+	return count;
+}
+static DEVICE_ATTR_WO(a_clr_err);
+
+static struct attribute *inputs_attrs[] = {
+	&dev_attr_a_bus_req.attr,
+	&dev_attr_a_bus_drop.attr,
+	&dev_attr_b_bus_req.attr,
+	&dev_attr_a_clr_err.attr,
+	NULL,
+};
+
+static const struct attribute_group inputs_attr_group = {
+	.name = "inputs",
+	.attrs = inputs_attrs,
+};
+
+/*
+ * Keep this list in the same order as timers indexed
+ * by enum otg_fsm_timer in include/linux/usb/otg-fsm.h
+ */
+static unsigned otg_timer_ms[] = {
+	TA_WAIT_VRISE,
+	TA_WAIT_VFALL,
+	TA_WAIT_BCON,
+	TA_AIDL_BDIS,
+	TB_ASE0_BRST,
+	TA_BIDL_ADIS,
+	TB_AIDL_BDIS,
+	TB_SE0_SRP,
+	TB_SRP_FAIL,
+	0,
+	TB_DATA_PLS,
+	TB_SSEND_SRP,
+};
+
+/*
+ * Add timer to active timer list
+ */
+static void ci_otg_add_timer(struct ci_hdrc *ci, enum otg_fsm_timer t)
+{
+	unsigned long flags, timer_sec, timer_nsec;
+
+	if (t >= NUM_OTG_FSM_TIMERS)
+		return;
+
+	spin_lock_irqsave(&ci->lock, flags);
+	timer_sec = otg_timer_ms[t] / MSEC_PER_SEC;
+	timer_nsec = (otg_timer_ms[t] % MSEC_PER_SEC) * NSEC_PER_MSEC;
+	ci->hr_timeouts[t] = ktime_add(ktime_get(),
+				ktime_set(timer_sec, timer_nsec));
+	ci->enabled_otg_timer_bits |= (1 << t);
+	if ((ci->next_otg_timer == NUM_OTG_FSM_TIMERS) ||
+			ktime_after(ci->hr_timeouts[ci->next_otg_timer],
+						ci->hr_timeouts[t])) {
+			ci->next_otg_timer = t;
+			hrtimer_start_range_ns(&ci->otg_fsm_hrtimer,
+					ci->hr_timeouts[t], NSEC_PER_MSEC,
+							HRTIMER_MODE_ABS);
+	}
+	spin_unlock_irqrestore(&ci->lock, flags);
+}
+
+/*
+ * Remove timer from active timer list
+ */
+static void ci_otg_del_timer(struct ci_hdrc *ci, enum otg_fsm_timer t)
+{
+	unsigned long flags, enabled_timer_bits;
+	enum otg_fsm_timer cur_timer, next_timer = NUM_OTG_FSM_TIMERS;
+
+	if ((t >= NUM_OTG_FSM_TIMERS) ||
+			!(ci->enabled_otg_timer_bits & (1 << t)))
+		return;
+
+	spin_lock_irqsave(&ci->lock, flags);
+	ci->enabled_otg_timer_bits &= ~(1 << t);
+	if (ci->next_otg_timer == t) {
+		if (ci->enabled_otg_timer_bits == 0) {
+			/* No enabled timers after delete it */
+			hrtimer_cancel(&ci->otg_fsm_hrtimer);
+			ci->next_otg_timer = NUM_OTG_FSM_TIMERS;
+		} else {
+			/* Find the next timer */
+			enabled_timer_bits = ci->enabled_otg_timer_bits;
+			for_each_set_bit(cur_timer, &enabled_timer_bits,
+							NUM_OTG_FSM_TIMERS) {
+				if ((next_timer == NUM_OTG_FSM_TIMERS) ||
+					ktime_before(ci->hr_timeouts[next_timer],
+					 ci->hr_timeouts[cur_timer]))
+					next_timer = cur_timer;
+			}
+		}
+	}
+	if (next_timer != NUM_OTG_FSM_TIMERS) {
+		ci->next_otg_timer = next_timer;
+		hrtimer_start_range_ns(&ci->otg_fsm_hrtimer,
+			ci->hr_timeouts[next_timer], NSEC_PER_MSEC,
+							HRTIMER_MODE_ABS);
+	}
+	spin_unlock_irqrestore(&ci->lock, flags);
+}
+
+/* OTG FSM timer handlers */
+static int a_wait_vrise_tmout(struct ci_hdrc *ci)
+{
+	ci->fsm.a_wait_vrise_tmout = 1;
+	return 0;
+}
+
+static int a_wait_vfall_tmout(struct ci_hdrc *ci)
+{
+	ci->fsm.a_wait_vfall_tmout = 1;
+	return 0;
+}
+
+static int a_wait_bcon_tmout(struct ci_hdrc *ci)
+{
+	ci->fsm.a_wait_bcon_tmout = 1;
+	return 0;
+}
+
+static int a_aidl_bdis_tmout(struct ci_hdrc *ci)
+{
+	ci->fsm.a_aidl_bdis_tmout = 1;
+	return 0;
+}
+
+static int b_ase0_brst_tmout(struct ci_hdrc *ci)
+{
+	ci->fsm.b_ase0_brst_tmout = 1;
+	return 0;
+}
+
+static int a_bidl_adis_tmout(struct ci_hdrc *ci)
+{
+	ci->fsm.a_bidl_adis_tmout = 1;
+	return 0;
+}
+
+static int b_aidl_bdis_tmout(struct ci_hdrc *ci)
+{
+	ci->fsm.a_bus_suspend = 1;
+	return 0;
+}
+
+static int b_se0_srp_tmout(struct ci_hdrc *ci)
+{
+	ci->fsm.b_se0_srp = 1;
+	return 0;
+}
+
+static int b_srp_fail_tmout(struct ci_hdrc *ci)
+{
+	ci->fsm.b_srp_done = 1;
+	return 1;
+}
+
+static int b_data_pls_tmout(struct ci_hdrc *ci)
+{
+	ci->fsm.b_srp_done = 1;
+	ci->fsm.b_bus_req = 0;
+	if (ci->fsm.power_up)
+		ci->fsm.power_up = 0;
+	hw_write_otgsc(ci, OTGSC_HABA, 0);
+	pm_runtime_put(ci->dev);
+	return 0;
+}
+
+static int b_ssend_srp_tmout(struct ci_hdrc *ci)
+{
+	ci->fsm.b_ssend_srp = 1;
+	/* only vbus fall below B_sess_vld in b_idle state */
+	if (ci->fsm.otg->state == OTG_STATE_B_IDLE)
+		return 0;
+	else
+		return 1;
+}
+
+/*
+ * Keep this list in the same order as timers indexed
+ * by enum otg_fsm_timer in include/linux/usb/otg-fsm.h
+ */
+static int (*otg_timer_handlers[])(struct ci_hdrc *) = {
+	a_wait_vrise_tmout,	/* A_WAIT_VRISE */
+	a_wait_vfall_tmout,	/* A_WAIT_VFALL */
+	a_wait_bcon_tmout,	/* A_WAIT_BCON */
+	a_aidl_bdis_tmout,	/* A_AIDL_BDIS */
+	b_ase0_brst_tmout,	/* B_ASE0_BRST */
+	a_bidl_adis_tmout,	/* A_BIDL_ADIS */
+	b_aidl_bdis_tmout,	/* B_AIDL_BDIS */
+	b_se0_srp_tmout,	/* B_SE0_SRP */
+	b_srp_fail_tmout,	/* B_SRP_FAIL */
+	NULL,			/* A_WAIT_ENUM */
+	b_data_pls_tmout,	/* B_DATA_PLS */
+	b_ssend_srp_tmout,	/* B_SSEND_SRP */
+};
+
+/*
+ * Enable the next nearest enabled timer if have
+ */
+static enum hrtimer_restart ci_otg_hrtimer_func(struct hrtimer *t)
+{
+	struct ci_hdrc *ci = container_of(t, struct ci_hdrc, otg_fsm_hrtimer);
+	ktime_t	now, *timeout;
+	unsigned long   enabled_timer_bits;
+	unsigned long   flags;
+	enum otg_fsm_timer cur_timer, next_timer = NUM_OTG_FSM_TIMERS;
+	int ret = -EINVAL;
+
+	spin_lock_irqsave(&ci->lock, flags);
+	enabled_timer_bits = ci->enabled_otg_timer_bits;
+	ci->next_otg_timer = NUM_OTG_FSM_TIMERS;
+
+	now = ktime_get();
+	for_each_set_bit(cur_timer, &enabled_timer_bits, NUM_OTG_FSM_TIMERS) {
+		if (ktime_compare(now, ci->hr_timeouts[cur_timer]) >= 0) {
+			ci->enabled_otg_timer_bits &= ~(1 << cur_timer);
+			if (otg_timer_handlers[cur_timer])
+				ret = otg_timer_handlers[cur_timer](ci);
+		} else {
+			if ((next_timer == NUM_OTG_FSM_TIMERS) ||
+				ktime_before(ci->hr_timeouts[cur_timer],
+					ci->hr_timeouts[next_timer]))
+				next_timer = cur_timer;
+		}
+	}
+	/* Enable the next nearest timer */
+	if (next_timer < NUM_OTG_FSM_TIMERS) {
+		timeout = &ci->hr_timeouts[next_timer];
+		hrtimer_start_range_ns(&ci->otg_fsm_hrtimer, *timeout,
+					NSEC_PER_MSEC, HRTIMER_MODE_ABS);
+		ci->next_otg_timer = next_timer;
+	}
+	spin_unlock_irqrestore(&ci->lock, flags);
+
+	if (!ret)
+		ci_otg_queue_work(ci);
+
+	return HRTIMER_NORESTART;
+}
+
+/* Initialize timers */
+static int ci_otg_init_timers(struct ci_hdrc *ci)
+{
+	hrtimer_init(&ci->otg_fsm_hrtimer, CLOCK_MONOTONIC, HRTIMER_MODE_ABS);
+	ci->otg_fsm_hrtimer.function = ci_otg_hrtimer_func;
+
+	return 0;
+}
+
+/* -------------------------------------------------------------*/
+/* Operations that will be called from OTG Finite State Machine */
+/* -------------------------------------------------------------*/
+static void ci_otg_fsm_add_timer(struct otg_fsm *fsm, enum otg_fsm_timer t)
+{
+	struct ci_hdrc	*ci = container_of(fsm, struct ci_hdrc, fsm);
+
+	if (t < NUM_OTG_FSM_TIMERS)
+		ci_otg_add_timer(ci, t);
+	return;
+}
+
+static void ci_otg_fsm_del_timer(struct otg_fsm *fsm, enum otg_fsm_timer t)
+{
+	struct ci_hdrc	*ci = container_of(fsm, struct ci_hdrc, fsm);
+
+	if (t < NUM_OTG_FSM_TIMERS)
+		ci_otg_del_timer(ci, t);
+	return;
+}
+
+/*
+ * A-device drive vbus: turn on vbus regulator and enable port power
+ * Data pulse irq should be disabled while vbus is on.
+ */
+static void ci_otg_drv_vbus(struct otg_fsm *fsm, int on)
+{
+	int ret;
+	struct ci_hdrc	*ci = container_of(fsm, struct ci_hdrc, fsm);
+
+	if (on) {
+		/* Enable power power */
+		hw_write(ci, OP_PORTSC, PORTSC_W1C_BITS | PORTSC_PP,
+							PORTSC_PP);
+		if (ci->platdata->reg_vbus) {
+			ret = regulator_enable(ci->platdata->reg_vbus);
+			if (ret) {
+				dev_err(ci->dev,
+				"Failed to enable vbus regulator, ret=%d\n",
+				ret);
+				return;
+			}
+		}
+		/* Disable data pulse irq */
+		hw_write_otgsc(ci, OTGSC_DPIE, 0);
+
+		fsm->a_srp_det = 0;
+		fsm->power_up = 0;
+	} else {
+		if (ci->platdata->reg_vbus)
+			regulator_disable(ci->platdata->reg_vbus);
+
+		fsm->a_bus_drop = 1;
+		fsm->a_bus_req = 0;
+	}
+}
+
+/*
+ * Control data line by Run Stop bit.
+ */
+static void ci_otg_loc_conn(struct otg_fsm *fsm, int on)
+{
+	struct ci_hdrc	*ci = container_of(fsm, struct ci_hdrc, fsm);
+
+	if (on)
+		hw_write(ci, OP_USBCMD, USBCMD_RS, USBCMD_RS);
+	else
+		hw_write(ci, OP_USBCMD, USBCMD_RS, 0);
+}
+
+/*
+ * Generate SOF by host.
+ * In host mode, controller will automatically send SOF.
+ * Suspend will block the data on the port.
+ *
+ * This is controlled through usbcore by usb autosuspend,
+ * so the usb device class driver need support autosuspend,
+ * otherwise the bus suspend will not happen.
+ */
+static void ci_otg_loc_sof(struct otg_fsm *fsm, int on)
+{
+	struct usb_device *udev;
+
+	if (!fsm->otg->host)
+		return;
+
+	udev = usb_hub_find_child(fsm->otg->host->root_hub, 1);
+	if (!udev)
+		return;
+
+	if (on) {
+		usb_disable_autosuspend(udev);
+	} else {
+		pm_runtime_set_autosuspend_delay(&udev->dev, 0);
+		usb_enable_autosuspend(udev);
+	}
+}
+
+/*
+ * Start SRP pulsing by data-line pulsing,
+ * no v-bus pulsing followed
+ */
+static void ci_otg_start_pulse(struct otg_fsm *fsm)
+{
+	struct ci_hdrc	*ci = container_of(fsm, struct ci_hdrc, fsm);
+
+	/* Hardware Assistant Data pulse */
+	hw_write_otgsc(ci, OTGSC_HADP, OTGSC_HADP);
+
+	pm_runtime_get(ci->dev);
+	ci_otg_add_timer(ci, B_DATA_PLS);
+}
+
+static int ci_otg_start_host(struct otg_fsm *fsm, int on)
+{
+	struct ci_hdrc	*ci = container_of(fsm, struct ci_hdrc, fsm);
+
+	if (on) {
+		ci_role_stop(ci);
+		ci_role_start(ci, CI_ROLE_HOST);
+	} else {
+		ci_role_stop(ci);
+		ci_role_start(ci, CI_ROLE_GADGET);
+	}
+	return 0;
+}
+
+static int ci_otg_start_gadget(struct otg_fsm *fsm, int on)
+{
+	struct ci_hdrc	*ci = container_of(fsm, struct ci_hdrc, fsm);
+
+	if (on)
+		usb_gadget_vbus_connect(&ci->gadget);
+	else
+		usb_gadget_vbus_disconnect(&ci->gadget);
+
+	return 0;
+}
+
+static struct otg_fsm_ops ci_otg_ops = {
+	.drv_vbus = ci_otg_drv_vbus,
+	.loc_conn = ci_otg_loc_conn,
+	.loc_sof = ci_otg_loc_sof,
+	.start_pulse = ci_otg_start_pulse,
+	.add_timer = ci_otg_fsm_add_timer,
+	.del_timer = ci_otg_fsm_del_timer,
+	.start_host = ci_otg_start_host,
+	.start_gadget = ci_otg_start_gadget,
+};
+
+int ci_otg_fsm_work(struct ci_hdrc *ci)
+{
+	/*
+	 * Don't do fsm transition for B device
+	 * when there is no gadget class driver
+	 */
+	if (ci->fsm.id && !(ci->driver) &&
+		ci->fsm.otg->state < OTG_STATE_A_IDLE)
+		return 0;
+
+	pm_runtime_get_sync(ci->dev);
+	if (otg_statemachine(&ci->fsm)) {
+		if (ci->fsm.otg->state == OTG_STATE_A_IDLE) {
+			/*
+			 * Further state change for cases:
+			 * a_idle to b_idle; or
+			 * a_idle to a_wait_vrise due to ID change(1->0), so
+			 * B-dev becomes A-dev can try to start new session
+			 * consequently; or
+			 * a_idle to a_wait_vrise when power up
+			 */
+			if ((ci->fsm.id) || (ci->id_event) ||
+						(ci->fsm.power_up)) {
+				ci_otg_queue_work(ci);
+			} else {
+				/* Enable data pulse irq */
+				hw_write(ci, OP_PORTSC, PORTSC_W1C_BITS |
+								PORTSC_PP, 0);
+				hw_write_otgsc(ci, OTGSC_DPIS, OTGSC_DPIS);
+				hw_write_otgsc(ci, OTGSC_DPIE, OTGSC_DPIE);
+			}
+			if (ci->id_event)
+				ci->id_event = false;
+		} else if (ci->fsm.otg->state == OTG_STATE_B_IDLE) {
+			if (ci->fsm.b_sess_vld) {
+				ci->fsm.power_up = 0;
+				/*
+				 * Further transite to b_periphearl state
+				 * when register gadget driver with vbus on
+				 */
+				ci_otg_queue_work(ci);
+			}
+		} else if (ci->fsm.otg->state == OTG_STATE_A_HOST) {
+			pm_runtime_mark_last_busy(ci->dev);
+			pm_runtime_put_autosuspend(ci->dev);
+			return 0;
+		}
+	}
+	pm_runtime_put_sync(ci->dev);
+	return 0;
+}
+
+/*
+ * Update fsm variables in each state if catching expected interrupts,
+ * called by otg fsm isr.
+ */
+static void ci_otg_fsm_event(struct ci_hdrc *ci)
+{
+	u32 intr_sts, otg_bsess_vld, port_conn;
+	struct otg_fsm *fsm = &ci->fsm;
+
+	intr_sts = hw_read_intr_status(ci);
+	otg_bsess_vld = hw_read_otgsc(ci, OTGSC_BSV);
+	port_conn = hw_read(ci, OP_PORTSC, PORTSC_CCS);
+
+	switch (ci->fsm.otg->state) {
+	case OTG_STATE_A_WAIT_BCON:
+		if (port_conn) {
+			fsm->b_conn = 1;
+			fsm->a_bus_req = 1;
+			ci_otg_queue_work(ci);
+		}
+		break;
+	case OTG_STATE_B_IDLE:
+		if (otg_bsess_vld && (intr_sts & USBi_PCI) && port_conn) {
+			fsm->b_sess_vld = 1;
+			ci_otg_queue_work(ci);
+		}
+		break;
+	case OTG_STATE_B_PERIPHERAL:
+		if ((intr_sts & USBi_SLI) && port_conn && otg_bsess_vld) {
+			ci_otg_add_timer(ci, B_AIDL_BDIS);
+		} else if (intr_sts & USBi_PCI) {
+			ci_otg_del_timer(ci, B_AIDL_BDIS);
+			if (fsm->a_bus_suspend == 1)
+				fsm->a_bus_suspend = 0;
+		}
+		break;
+	case OTG_STATE_B_HOST:
+		if ((intr_sts & USBi_PCI) && !port_conn) {
+			fsm->a_conn = 0;
+			fsm->b_bus_req = 0;
+			ci_otg_queue_work(ci);
+		}
+		break;
+	case OTG_STATE_A_PERIPHERAL:
+		if (intr_sts & USBi_SLI) {
+			 fsm->b_bus_suspend = 1;
+			/*
+			 * Init a timer to know how long this suspend
+			 * will continue, if time out, indicates B no longer
+			 * wants to be host role
+			 */
+			 ci_otg_add_timer(ci, A_BIDL_ADIS);
+		}
+
+		if (intr_sts & USBi_URI)
+			ci_otg_del_timer(ci, A_BIDL_ADIS);
+
+		if (intr_sts & USBi_PCI) {
+			if (fsm->b_bus_suspend == 1) {
+				ci_otg_del_timer(ci, A_BIDL_ADIS);
+				fsm->b_bus_suspend = 0;
+			}
+		}
+		break;
+	case OTG_STATE_A_SUSPEND:
+		if ((intr_sts & USBi_PCI) && !port_conn) {
+			fsm->b_conn = 0;
+
+			/* if gadget driver is binded */
+			if (ci->driver) {
+				/* A device to be peripheral mode */
+				ci->gadget.is_a_peripheral = 1;
+			}
+			ci_otg_queue_work(ci);
+		}
+		break;
+	case OTG_STATE_A_HOST:
+		if ((intr_sts & USBi_PCI) && !port_conn) {
+			fsm->b_conn = 0;
+			ci_otg_queue_work(ci);
+		}
+		break;
+	case OTG_STATE_B_WAIT_ACON:
+		if ((intr_sts & USBi_PCI) && port_conn) {
+			fsm->a_conn = 1;
+			ci_otg_queue_work(ci);
+		}
+		break;
+	default:
+		break;
+	}
+}
+
+/*
+ * ci_otg_irq - otg fsm related irq handling
+ * and also update otg fsm variable by monitoring usb host and udc
+ * state change interrupts.
+ * @ci: ci_hdrc
+ */
+irqreturn_t ci_otg_fsm_irq(struct ci_hdrc *ci)
+{
+	irqreturn_t retval =  IRQ_NONE;
+	u32 otgsc, otg_int_src = 0;
+	struct otg_fsm *fsm = &ci->fsm;
+
+	otgsc = hw_read_otgsc(ci, ~0);
+	otg_int_src = otgsc & OTGSC_INT_STATUS_BITS & (otgsc >> 8);
+	fsm->id = (otgsc & OTGSC_ID) ? 1 : 0;
+
+	if (otg_int_src) {
+		if (otg_int_src & OTGSC_DPIS) {
+			hw_write_otgsc(ci, OTGSC_DPIS, OTGSC_DPIS);
+			fsm->a_srp_det = 1;
+			fsm->a_bus_drop = 0;
+		} else if (otg_int_src & OTGSC_IDIS) {
+			hw_write_otgsc(ci, OTGSC_IDIS, OTGSC_IDIS);
+			if (fsm->id == 0) {
+				fsm->a_bus_drop = 0;
+				fsm->a_bus_req = 1;
+				ci->id_event = true;
+			}
+		} else if (otg_int_src & OTGSC_BSVIS) {
+			hw_write_otgsc(ci, OTGSC_BSVIS, OTGSC_BSVIS);
+			if (otgsc & OTGSC_BSV) {
+				fsm->b_sess_vld = 1;
+				ci_otg_del_timer(ci, B_SSEND_SRP);
+				ci_otg_del_timer(ci, B_SRP_FAIL);
+				fsm->b_ssend_srp = 0;
+			} else {
+				fsm->b_sess_vld = 0;
+				if (fsm->id)
+					ci_otg_add_timer(ci, B_SSEND_SRP);
+			}
+		} else if (otg_int_src & OTGSC_AVVIS) {
+			hw_write_otgsc(ci, OTGSC_AVVIS, OTGSC_AVVIS);
+			if (otgsc & OTGSC_AVV) {
+				fsm->a_vbus_vld = 1;
+			} else {
+				fsm->a_vbus_vld = 0;
+				fsm->b_conn = 0;
+			}
+		}
+		ci_otg_queue_work(ci);
+		return IRQ_HANDLED;
+	}
+
+	ci_otg_fsm_event(ci);
+
+	return retval;
+}
+
+void ci_hdrc_otg_fsm_start(struct ci_hdrc *ci)
+{
+	ci_otg_queue_work(ci);
+}
+
+int ci_hdrc_otg_fsm_init(struct ci_hdrc *ci)
+{
+	int retval = 0;
+
+	if (ci->phy)
+		ci->otg.phy = ci->phy;
+	else
+		ci->otg.usb_phy = ci->usb_phy;
+
+	ci->otg.gadget = &ci->gadget;
+	ci->fsm.otg = &ci->otg;
+	ci->fsm.power_up = 1;
+	ci->fsm.id = hw_read_otgsc(ci, OTGSC_ID) ? 1 : 0;
+	ci->fsm.otg->state = OTG_STATE_UNDEFINED;
+	ci->fsm.ops = &ci_otg_ops;
+	ci->gadget.hnp_polling_support = 1;
+	ci->fsm.host_req_flag = devm_kzalloc(ci->dev, 1, GFP_KERNEL);
+	if (!ci->fsm.host_req_flag)
+		return -ENOMEM;
+
+	mutex_init(&ci->fsm.lock);
+
+	retval = ci_otg_init_timers(ci);
+	if (retval) {
+		dev_err(ci->dev, "Couldn't init OTG timers\n");
+		return retval;
+	}
+	ci->enabled_otg_timer_bits = 0;
+	ci->next_otg_timer = NUM_OTG_FSM_TIMERS;
+
+	retval = sysfs_create_group(&ci->dev->kobj, &inputs_attr_group);
+	if (retval < 0) {
+		dev_dbg(ci->dev,
+			"Can't register sysfs attr group: %d\n", retval);
+		return retval;
+	}
+
+	/* Enable A vbus valid irq */
+	hw_write_otgsc(ci, OTGSC_AVVIE, OTGSC_AVVIE);
+
+	if (ci->fsm.id) {
+		ci->fsm.b_ssend_srp =
+			hw_read_otgsc(ci, OTGSC_BSV) ? 0 : 1;
+		ci->fsm.b_sess_vld =
+			hw_read_otgsc(ci, OTGSC_BSV) ? 1 : 0;
+		/* Enable BSV irq */
+		hw_write_otgsc(ci, OTGSC_BSVIE, OTGSC_BSVIE);
+	}
+
+	return 0;
+}
+
+void ci_hdrc_otg_fsm_remove(struct ci_hdrc *ci)
+{
+	sysfs_remove_group(&ci->dev->kobj, &inputs_attr_group);
+}
diff --git a/drivers/usb/chipidea/otg_fsm.h b/drivers/usb/chipidea/otg_fsm.h
new file mode 100644
index 0000000..2b49d29
--- /dev/null
+++ b/drivers/usb/chipidea/otg_fsm.h
@@ -0,0 +1,101 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2014 Freescale Semiconductor, Inc.
+ *
+ * Author: Jun Li
+ */
+
+#ifndef __DRIVERS_USB_CHIPIDEA_OTG_FSM_H
+#define __DRIVERS_USB_CHIPIDEA_OTG_FSM_H
+
+#include <linux/usb/otg-fsm.h>
+
+/*
+ *  A-DEVICE timing  constants
+ */
+
+/* Wait for VBUS Rise  */
+#define TA_WAIT_VRISE        (100)	/* a_wait_vrise: section 7.1.2
+					 * a_wait_vrise_tmr: section 7.4.5.1
+					 * TA_VBUS_RISE <= 100ms, section 4.4
+					 * Table 4-1: Electrical Characteristics
+					 * ->DC Electrical Timing
+					 */
+/* Wait for VBUS Fall  */
+#define TA_WAIT_VFALL        (1000)	/* a_wait_vfall: section 7.1.7
+					 * a_wait_vfall_tmr: section: 7.4.5.2
+					 */
+/* Wait for B-Connect */
+#define TA_WAIT_BCON         (10000)	/* a_wait_bcon: section 7.1.3
+					 * TA_WAIT_BCON: should be between 1100
+					 * and 30000 ms, section 5.5, Table 5-1
+					 */
+/* A-Idle to B-Disconnect */
+#define TA_AIDL_BDIS         (5000)	/* a_suspend min 200 ms, section 5.2.1
+					 * TA_AIDL_BDIS: section 5.5, Table 5-1
+					 */
+/* B-Idle to A-Disconnect */
+#define TA_BIDL_ADIS         (500)	/* TA_BIDL_ADIS: section 5.2.1
+					 * 500ms is used for B switch to host
+					 * for safe
+					 */
+
+/*
+ * B-device timing constants
+ */
+
+/* Data-Line Pulse Time*/
+#define TB_DATA_PLS          (10)	/* b_srp_init,continue 5~10ms
+					 * section:5.1.3
+					 */
+/* SRP Fail Time  */
+#define TB_SRP_FAIL          (6000)	/* b_srp_init,fail time 5~6s
+					 * section:5.1.6
+					 */
+/* A-SE0 to B-Reset  */
+#define TB_ASE0_BRST         (155)	/* minimum 155 ms, section:5.3.1 */
+/* SE0 Time Before SRP */
+#define TB_SE0_SRP           (1000)	/* b_idle,minimum 1s, section:5.1.2 */
+/* SSEND time before SRP */
+#define TB_SSEND_SRP         (1500)	/* minimum 1.5 sec, section:5.1.2 */
+
+#define TB_AIDL_BDIS         (20)	/* 4ms ~ 150ms, section 5.2.1 */
+
+#if IS_ENABLED(CONFIG_USB_OTG_FSM)
+
+int ci_hdrc_otg_fsm_init(struct ci_hdrc *ci);
+int ci_otg_fsm_work(struct ci_hdrc *ci);
+irqreturn_t ci_otg_fsm_irq(struct ci_hdrc *ci);
+void ci_hdrc_otg_fsm_start(struct ci_hdrc *ci);
+void ci_hdrc_otg_fsm_remove(struct ci_hdrc *ci);
+
+#else
+
+static inline int ci_hdrc_otg_fsm_init(struct ci_hdrc *ci)
+{
+	return 0;
+}
+
+static inline int ci_otg_fsm_work(struct ci_hdrc *ci)
+{
+	return -ENXIO;
+}
+
+static inline irqreturn_t ci_otg_fsm_irq(struct ci_hdrc *ci)
+{
+	return IRQ_NONE;
+}
+
+static inline void ci_hdrc_otg_fsm_start(struct ci_hdrc *ci)
+{
+
+}
+
+static inline void ci_hdrc_otg_fsm_remove(struct ci_hdrc *ci)
+{
+
+}
+
+#endif
+
+#endif /* __DRIVERS_USB_CHIPIDEA_OTG_FSM_H */
diff --git a/drivers/usb/chipidea/udc.c b/drivers/usb/chipidea/udc.c
new file mode 100644
index 0000000..9852ec5
--- /dev/null
+++ b/drivers/usb/chipidea/udc.c
@@ -0,0 +1,2016 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * udc.c - ChipIdea UDC driver
+ *
+ * Copyright (C) 2008 Chipidea - MIPS Technologies, Inc. All rights reserved.
+ *
+ * Author: David Lopo
+ */
+
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/dmapool.h>
+#include <linux/err.h>
+#include <linux/irqreturn.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/pm_runtime.h>
+#include <linux/usb/ch9.h>
+#include <linux/usb/gadget.h>
+#include <linux/usb/otg-fsm.h>
+#include <linux/usb/chipidea.h>
+
+#include "ci.h"
+#include "udc.h"
+#include "bits.h"
+#include "otg.h"
+#include "otg_fsm.h"
+
+/* control endpoint description */
+static const struct usb_endpoint_descriptor
+ctrl_endpt_out_desc = {
+	.bLength         = USB_DT_ENDPOINT_SIZE,
+	.bDescriptorType = USB_DT_ENDPOINT,
+
+	.bEndpointAddress = USB_DIR_OUT,
+	.bmAttributes    = USB_ENDPOINT_XFER_CONTROL,
+	.wMaxPacketSize  = cpu_to_le16(CTRL_PAYLOAD_MAX),
+};
+
+static const struct usb_endpoint_descriptor
+ctrl_endpt_in_desc = {
+	.bLength         = USB_DT_ENDPOINT_SIZE,
+	.bDescriptorType = USB_DT_ENDPOINT,
+
+	.bEndpointAddress = USB_DIR_IN,
+	.bmAttributes    = USB_ENDPOINT_XFER_CONTROL,
+	.wMaxPacketSize  = cpu_to_le16(CTRL_PAYLOAD_MAX),
+};
+
+/**
+ * hw_ep_bit: calculates the bit number
+ * @num: endpoint number
+ * @dir: endpoint direction
+ *
+ * This function returns bit number
+ */
+static inline int hw_ep_bit(int num, int dir)
+{
+	return num + ((dir == TX) ? 16 : 0);
+}
+
+static inline int ep_to_bit(struct ci_hdrc *ci, int n)
+{
+	int fill = 16 - ci->hw_ep_max / 2;
+
+	if (n >= ci->hw_ep_max / 2)
+		n += fill;
+
+	return n;
+}
+
+/**
+ * hw_device_state: enables/disables interrupts (execute without interruption)
+ * @dma: 0 => disable, !0 => enable and set dma engine
+ *
+ * This function returns an error code
+ */
+static int hw_device_state(struct ci_hdrc *ci, u32 dma)
+{
+	if (dma) {
+		hw_write(ci, OP_ENDPTLISTADDR, ~0, dma);
+		/* interrupt, error, port change, reset, sleep/suspend */
+		hw_write(ci, OP_USBINTR, ~0,
+			     USBi_UI|USBi_UEI|USBi_PCI|USBi_URI|USBi_SLI);
+	} else {
+		hw_write(ci, OP_USBINTR, ~0, 0);
+	}
+	return 0;
+}
+
+/**
+ * hw_ep_flush: flush endpoint fifo (execute without interruption)
+ * @num: endpoint number
+ * @dir: endpoint direction
+ *
+ * This function returns an error code
+ */
+static int hw_ep_flush(struct ci_hdrc *ci, int num, int dir)
+{
+	int n = hw_ep_bit(num, dir);
+
+	do {
+		/* flush any pending transfer */
+		hw_write(ci, OP_ENDPTFLUSH, ~0, BIT(n));
+		while (hw_read(ci, OP_ENDPTFLUSH, BIT(n)))
+			cpu_relax();
+	} while (hw_read(ci, OP_ENDPTSTAT, BIT(n)));
+
+	return 0;
+}
+
+/**
+ * hw_ep_disable: disables endpoint (execute without interruption)
+ * @num: endpoint number
+ * @dir: endpoint direction
+ *
+ * This function returns an error code
+ */
+static int hw_ep_disable(struct ci_hdrc *ci, int num, int dir)
+{
+	hw_write(ci, OP_ENDPTCTRL + num,
+		 (dir == TX) ? ENDPTCTRL_TXE : ENDPTCTRL_RXE, 0);
+	return 0;
+}
+
+/**
+ * hw_ep_enable: enables endpoint (execute without interruption)
+ * @num:  endpoint number
+ * @dir:  endpoint direction
+ * @type: endpoint type
+ *
+ * This function returns an error code
+ */
+static int hw_ep_enable(struct ci_hdrc *ci, int num, int dir, int type)
+{
+	u32 mask, data;
+
+	if (dir == TX) {
+		mask  = ENDPTCTRL_TXT;  /* type    */
+		data  = type << __ffs(mask);
+
+		mask |= ENDPTCTRL_TXS;  /* unstall */
+		mask |= ENDPTCTRL_TXR;  /* reset data toggle */
+		data |= ENDPTCTRL_TXR;
+		mask |= ENDPTCTRL_TXE;  /* enable  */
+		data |= ENDPTCTRL_TXE;
+	} else {
+		mask  = ENDPTCTRL_RXT;  /* type    */
+		data  = type << __ffs(mask);
+
+		mask |= ENDPTCTRL_RXS;  /* unstall */
+		mask |= ENDPTCTRL_RXR;  /* reset data toggle */
+		data |= ENDPTCTRL_RXR;
+		mask |= ENDPTCTRL_RXE;  /* enable  */
+		data |= ENDPTCTRL_RXE;
+	}
+	hw_write(ci, OP_ENDPTCTRL + num, mask, data);
+	return 0;
+}
+
+/**
+ * hw_ep_get_halt: return endpoint halt status
+ * @num: endpoint number
+ * @dir: endpoint direction
+ *
+ * This function returns 1 if endpoint halted
+ */
+static int hw_ep_get_halt(struct ci_hdrc *ci, int num, int dir)
+{
+	u32 mask = (dir == TX) ? ENDPTCTRL_TXS : ENDPTCTRL_RXS;
+
+	return hw_read(ci, OP_ENDPTCTRL + num, mask) ? 1 : 0;
+}
+
+/**
+ * hw_ep_prime: primes endpoint (execute without interruption)
+ * @num:     endpoint number
+ * @dir:     endpoint direction
+ * @is_ctrl: true if control endpoint
+ *
+ * This function returns an error code
+ */
+static int hw_ep_prime(struct ci_hdrc *ci, int num, int dir, int is_ctrl)
+{
+	int n = hw_ep_bit(num, dir);
+
+	/* Synchronize before ep prime */
+	wmb();
+
+	if (is_ctrl && dir == RX && hw_read(ci, OP_ENDPTSETUPSTAT, BIT(num)))
+		return -EAGAIN;
+
+	hw_write(ci, OP_ENDPTPRIME, ~0, BIT(n));
+
+	while (hw_read(ci, OP_ENDPTPRIME, BIT(n)))
+		cpu_relax();
+	if (is_ctrl && dir == RX && hw_read(ci, OP_ENDPTSETUPSTAT, BIT(num)))
+		return -EAGAIN;
+
+	/* status shoult be tested according with manual but it doesn't work */
+	return 0;
+}
+
+/**
+ * hw_ep_set_halt: configures ep halt & resets data toggle after clear (execute
+ *                 without interruption)
+ * @num:   endpoint number
+ * @dir:   endpoint direction
+ * @value: true => stall, false => unstall
+ *
+ * This function returns an error code
+ */
+static int hw_ep_set_halt(struct ci_hdrc *ci, int num, int dir, int value)
+{
+	if (value != 0 && value != 1)
+		return -EINVAL;
+
+	do {
+		enum ci_hw_regs reg = OP_ENDPTCTRL + num;
+		u32 mask_xs = (dir == TX) ? ENDPTCTRL_TXS : ENDPTCTRL_RXS;
+		u32 mask_xr = (dir == TX) ? ENDPTCTRL_TXR : ENDPTCTRL_RXR;
+
+		/* data toggle - reserved for EP0 but it's in ESS */
+		hw_write(ci, reg, mask_xs|mask_xr,
+			  value ? mask_xs : mask_xr);
+	} while (value != hw_ep_get_halt(ci, num, dir));
+
+	return 0;
+}
+
+/**
+ * hw_is_port_high_speed: test if port is high speed
+ *
+ * This function returns true if high speed port
+ */
+static int hw_port_is_high_speed(struct ci_hdrc *ci)
+{
+	return ci->hw_bank.lpm ? hw_read(ci, OP_DEVLC, DEVLC_PSPD) :
+		hw_read(ci, OP_PORTSC, PORTSC_HSP);
+}
+
+/**
+ * hw_test_and_clear_complete: test & clear complete status (execute without
+ *                             interruption)
+ * @n: endpoint number
+ *
+ * This function returns complete status
+ */
+static int hw_test_and_clear_complete(struct ci_hdrc *ci, int n)
+{
+	n = ep_to_bit(ci, n);
+	return hw_test_and_clear(ci, OP_ENDPTCOMPLETE, BIT(n));
+}
+
+/**
+ * hw_test_and_clear_intr_active: test & clear active interrupts (execute
+ *                                without interruption)
+ *
+ * This function returns active interrutps
+ */
+static u32 hw_test_and_clear_intr_active(struct ci_hdrc *ci)
+{
+	u32 reg = hw_read_intr_status(ci) & hw_read_intr_enable(ci);
+
+	hw_write(ci, OP_USBSTS, ~0, reg);
+	return reg;
+}
+
+/**
+ * hw_test_and_clear_setup_guard: test & clear setup guard (execute without
+ *                                interruption)
+ *
+ * This function returns guard value
+ */
+static int hw_test_and_clear_setup_guard(struct ci_hdrc *ci)
+{
+	return hw_test_and_write(ci, OP_USBCMD, USBCMD_SUTW, 0);
+}
+
+/**
+ * hw_test_and_set_setup_guard: test & set setup guard (execute without
+ *                              interruption)
+ *
+ * This function returns guard value
+ */
+static int hw_test_and_set_setup_guard(struct ci_hdrc *ci)
+{
+	return hw_test_and_write(ci, OP_USBCMD, USBCMD_SUTW, USBCMD_SUTW);
+}
+
+/**
+ * hw_usb_set_address: configures USB address (execute without interruption)
+ * @value: new USB address
+ *
+ * This function explicitly sets the address, without the "USBADRA" (advance)
+ * feature, which is not supported by older versions of the controller.
+ */
+static void hw_usb_set_address(struct ci_hdrc *ci, u8 value)
+{
+	hw_write(ci, OP_DEVICEADDR, DEVICEADDR_USBADR,
+		 value << __ffs(DEVICEADDR_USBADR));
+}
+
+/**
+ * hw_usb_reset: restart device after a bus reset (execute without
+ *               interruption)
+ *
+ * This function returns an error code
+ */
+static int hw_usb_reset(struct ci_hdrc *ci)
+{
+	hw_usb_set_address(ci, 0);
+
+	/* ESS flushes only at end?!? */
+	hw_write(ci, OP_ENDPTFLUSH,    ~0, ~0);
+
+	/* clear setup token semaphores */
+	hw_write(ci, OP_ENDPTSETUPSTAT, 0,  0);
+
+	/* clear complete status */
+	hw_write(ci, OP_ENDPTCOMPLETE,  0,  0);
+
+	/* wait until all bits cleared */
+	while (hw_read(ci, OP_ENDPTPRIME, ~0))
+		udelay(10);             /* not RTOS friendly */
+
+	/* reset all endpoints ? */
+
+	/* reset internal status and wait for further instructions
+	   no need to verify the port reset status (ESS does it) */
+
+	return 0;
+}
+
+/******************************************************************************
+ * UTIL block
+ *****************************************************************************/
+
+static int add_td_to_list(struct ci_hw_ep *hwep, struct ci_hw_req *hwreq,
+			  unsigned length)
+{
+	int i;
+	u32 temp;
+	struct td_node *lastnode, *node = kzalloc(sizeof(struct td_node),
+						  GFP_ATOMIC);
+
+	if (node == NULL)
+		return -ENOMEM;
+
+	node->ptr = dma_pool_zalloc(hwep->td_pool, GFP_ATOMIC, &node->dma);
+	if (node->ptr == NULL) {
+		kfree(node);
+		return -ENOMEM;
+	}
+
+	node->ptr->token = cpu_to_le32(length << __ffs(TD_TOTAL_BYTES));
+	node->ptr->token &= cpu_to_le32(TD_TOTAL_BYTES);
+	node->ptr->token |= cpu_to_le32(TD_STATUS_ACTIVE);
+	if (hwep->type == USB_ENDPOINT_XFER_ISOC && hwep->dir == TX) {
+		u32 mul = hwreq->req.length / hwep->ep.maxpacket;
+
+		if (hwreq->req.length == 0
+				|| hwreq->req.length % hwep->ep.maxpacket)
+			mul++;
+		node->ptr->token |= cpu_to_le32(mul << __ffs(TD_MULTO));
+	}
+
+	temp = (u32) (hwreq->req.dma + hwreq->req.actual);
+	if (length) {
+		node->ptr->page[0] = cpu_to_le32(temp);
+		for (i = 1; i < TD_PAGE_COUNT; i++) {
+			u32 page = temp + i * CI_HDRC_PAGE_SIZE;
+			page &= ~TD_RESERVED_MASK;
+			node->ptr->page[i] = cpu_to_le32(page);
+		}
+	}
+
+	hwreq->req.actual += length;
+
+	if (!list_empty(&hwreq->tds)) {
+		/* get the last entry */
+		lastnode = list_entry(hwreq->tds.prev,
+				struct td_node, td);
+		lastnode->ptr->next = cpu_to_le32(node->dma);
+	}
+
+	INIT_LIST_HEAD(&node->td);
+	list_add_tail(&node->td, &hwreq->tds);
+
+	return 0;
+}
+
+/**
+ * _usb_addr: calculates endpoint address from direction & number
+ * @ep:  endpoint
+ */
+static inline u8 _usb_addr(struct ci_hw_ep *ep)
+{
+	return ((ep->dir == TX) ? USB_ENDPOINT_DIR_MASK : 0) | ep->num;
+}
+
+/**
+ * _hardware_enqueue: configures a request at hardware level
+ * @hwep:   endpoint
+ * @hwreq:  request
+ *
+ * This function returns an error code
+ */
+static int _hardware_enqueue(struct ci_hw_ep *hwep, struct ci_hw_req *hwreq)
+{
+	struct ci_hdrc *ci = hwep->ci;
+	int ret = 0;
+	unsigned rest = hwreq->req.length;
+	int pages = TD_PAGE_COUNT;
+	struct td_node *firstnode, *lastnode;
+
+	/* don't queue twice */
+	if (hwreq->req.status == -EALREADY)
+		return -EALREADY;
+
+	hwreq->req.status = -EALREADY;
+
+	ret = usb_gadget_map_request_by_dev(ci->dev->parent,
+					    &hwreq->req, hwep->dir);
+	if (ret)
+		return ret;
+
+	/*
+	 * The first buffer could be not page aligned.
+	 * In that case we have to span into one extra td.
+	 */
+	if (hwreq->req.dma % PAGE_SIZE)
+		pages--;
+
+	if (rest == 0) {
+		ret = add_td_to_list(hwep, hwreq, 0);
+		if (ret < 0)
+			goto done;
+	}
+
+	while (rest > 0) {
+		unsigned count = min(hwreq->req.length - hwreq->req.actual,
+					(unsigned)(pages * CI_HDRC_PAGE_SIZE));
+		ret = add_td_to_list(hwep, hwreq, count);
+		if (ret < 0)
+			goto done;
+
+		rest -= count;
+	}
+
+	if (hwreq->req.zero && hwreq->req.length && hwep->dir == TX
+	    && (hwreq->req.length % hwep->ep.maxpacket == 0)) {
+		ret = add_td_to_list(hwep, hwreq, 0);
+		if (ret < 0)
+			goto done;
+	}
+
+	firstnode = list_first_entry(&hwreq->tds, struct td_node, td);
+
+	lastnode = list_entry(hwreq->tds.prev,
+		struct td_node, td);
+
+	lastnode->ptr->next = cpu_to_le32(TD_TERMINATE);
+	if (!hwreq->req.no_interrupt)
+		lastnode->ptr->token |= cpu_to_le32(TD_IOC);
+	wmb();
+
+	hwreq->req.actual = 0;
+	if (!list_empty(&hwep->qh.queue)) {
+		struct ci_hw_req *hwreqprev;
+		int n = hw_ep_bit(hwep->num, hwep->dir);
+		int tmp_stat;
+		struct td_node *prevlastnode;
+		u32 next = firstnode->dma & TD_ADDR_MASK;
+
+		hwreqprev = list_entry(hwep->qh.queue.prev,
+				struct ci_hw_req, queue);
+		prevlastnode = list_entry(hwreqprev->tds.prev,
+				struct td_node, td);
+
+		prevlastnode->ptr->next = cpu_to_le32(next);
+		wmb();
+		if (hw_read(ci, OP_ENDPTPRIME, BIT(n)))
+			goto done;
+		do {
+			hw_write(ci, OP_USBCMD, USBCMD_ATDTW, USBCMD_ATDTW);
+			tmp_stat = hw_read(ci, OP_ENDPTSTAT, BIT(n));
+		} while (!hw_read(ci, OP_USBCMD, USBCMD_ATDTW));
+		hw_write(ci, OP_USBCMD, USBCMD_ATDTW, 0);
+		if (tmp_stat)
+			goto done;
+	}
+
+	/*  QH configuration */
+	hwep->qh.ptr->td.next = cpu_to_le32(firstnode->dma);
+	hwep->qh.ptr->td.token &=
+		cpu_to_le32(~(TD_STATUS_HALTED|TD_STATUS_ACTIVE));
+
+	if (hwep->type == USB_ENDPOINT_XFER_ISOC && hwep->dir == RX) {
+		u32 mul = hwreq->req.length / hwep->ep.maxpacket;
+
+		if (hwreq->req.length == 0
+				|| hwreq->req.length % hwep->ep.maxpacket)
+			mul++;
+		hwep->qh.ptr->cap |= cpu_to_le32(mul << __ffs(QH_MULT));
+	}
+
+	ret = hw_ep_prime(ci, hwep->num, hwep->dir,
+			   hwep->type == USB_ENDPOINT_XFER_CONTROL);
+done:
+	return ret;
+}
+
+/*
+ * free_pending_td: remove a pending request for the endpoint
+ * @hwep: endpoint
+ */
+static void free_pending_td(struct ci_hw_ep *hwep)
+{
+	struct td_node *pending = hwep->pending_td;
+
+	dma_pool_free(hwep->td_pool, pending->ptr, pending->dma);
+	hwep->pending_td = NULL;
+	kfree(pending);
+}
+
+static int reprime_dtd(struct ci_hdrc *ci, struct ci_hw_ep *hwep,
+					   struct td_node *node)
+{
+	hwep->qh.ptr->td.next = cpu_to_le32(node->dma);
+	hwep->qh.ptr->td.token &=
+		cpu_to_le32(~(TD_STATUS_HALTED | TD_STATUS_ACTIVE));
+
+	return hw_ep_prime(ci, hwep->num, hwep->dir,
+				hwep->type == USB_ENDPOINT_XFER_CONTROL);
+}
+
+/**
+ * _hardware_dequeue: handles a request at hardware level
+ * @gadget: gadget
+ * @hwep:   endpoint
+ *
+ * This function returns an error code
+ */
+static int _hardware_dequeue(struct ci_hw_ep *hwep, struct ci_hw_req *hwreq)
+{
+	u32 tmptoken;
+	struct td_node *node, *tmpnode;
+	unsigned remaining_length;
+	unsigned actual = hwreq->req.length;
+	struct ci_hdrc *ci = hwep->ci;
+
+	if (hwreq->req.status != -EALREADY)
+		return -EINVAL;
+
+	hwreq->req.status = 0;
+
+	list_for_each_entry_safe(node, tmpnode, &hwreq->tds, td) {
+		tmptoken = le32_to_cpu(node->ptr->token);
+		if ((TD_STATUS_ACTIVE & tmptoken) != 0) {
+			int n = hw_ep_bit(hwep->num, hwep->dir);
+
+			if (ci->rev == CI_REVISION_24)
+				if (!hw_read(ci, OP_ENDPTSTAT, BIT(n)))
+					reprime_dtd(ci, hwep, node);
+			hwreq->req.status = -EALREADY;
+			return -EBUSY;
+		}
+
+		remaining_length = (tmptoken & TD_TOTAL_BYTES);
+		remaining_length >>= __ffs(TD_TOTAL_BYTES);
+		actual -= remaining_length;
+
+		hwreq->req.status = tmptoken & TD_STATUS;
+		if ((TD_STATUS_HALTED & hwreq->req.status)) {
+			hwreq->req.status = -EPIPE;
+			break;
+		} else if ((TD_STATUS_DT_ERR & hwreq->req.status)) {
+			hwreq->req.status = -EPROTO;
+			break;
+		} else if ((TD_STATUS_TR_ERR & hwreq->req.status)) {
+			hwreq->req.status = -EILSEQ;
+			break;
+		}
+
+		if (remaining_length) {
+			if (hwep->dir == TX) {
+				hwreq->req.status = -EPROTO;
+				break;
+			}
+		}
+		/*
+		 * As the hardware could still address the freed td
+		 * which will run the udc unusable, the cleanup of the
+		 * td has to be delayed by one.
+		 */
+		if (hwep->pending_td)
+			free_pending_td(hwep);
+
+		hwep->pending_td = node;
+		list_del_init(&node->td);
+	}
+
+	usb_gadget_unmap_request_by_dev(hwep->ci->dev->parent,
+					&hwreq->req, hwep->dir);
+
+	hwreq->req.actual += actual;
+
+	if (hwreq->req.status)
+		return hwreq->req.status;
+
+	return hwreq->req.actual;
+}
+
+/**
+ * _ep_nuke: dequeues all endpoint requests
+ * @hwep: endpoint
+ *
+ * This function returns an error code
+ * Caller must hold lock
+ */
+static int _ep_nuke(struct ci_hw_ep *hwep)
+__releases(hwep->lock)
+__acquires(hwep->lock)
+{
+	struct td_node *node, *tmpnode;
+	if (hwep == NULL)
+		return -EINVAL;
+
+	hw_ep_flush(hwep->ci, hwep->num, hwep->dir);
+
+	while (!list_empty(&hwep->qh.queue)) {
+
+		/* pop oldest request */
+		struct ci_hw_req *hwreq = list_entry(hwep->qh.queue.next,
+						     struct ci_hw_req, queue);
+
+		list_for_each_entry_safe(node, tmpnode, &hwreq->tds, td) {
+			dma_pool_free(hwep->td_pool, node->ptr, node->dma);
+			list_del_init(&node->td);
+			node->ptr = NULL;
+			kfree(node);
+		}
+
+		list_del_init(&hwreq->queue);
+		hwreq->req.status = -ESHUTDOWN;
+
+		if (hwreq->req.complete != NULL) {
+			spin_unlock(hwep->lock);
+			usb_gadget_giveback_request(&hwep->ep, &hwreq->req);
+			spin_lock(hwep->lock);
+		}
+	}
+
+	if (hwep->pending_td)
+		free_pending_td(hwep);
+
+	return 0;
+}
+
+static int _ep_set_halt(struct usb_ep *ep, int value, bool check_transfer)
+{
+	struct ci_hw_ep *hwep = container_of(ep, struct ci_hw_ep, ep);
+	int direction, retval = 0;
+	unsigned long flags;
+
+	if (ep == NULL || hwep->ep.desc == NULL)
+		return -EINVAL;
+
+	if (usb_endpoint_xfer_isoc(hwep->ep.desc))
+		return -EOPNOTSUPP;
+
+	spin_lock_irqsave(hwep->lock, flags);
+
+	if (value && hwep->dir == TX && check_transfer &&
+		!list_empty(&hwep->qh.queue) &&
+			!usb_endpoint_xfer_control(hwep->ep.desc)) {
+		spin_unlock_irqrestore(hwep->lock, flags);
+		return -EAGAIN;
+	}
+
+	direction = hwep->dir;
+	do {
+		retval |= hw_ep_set_halt(hwep->ci, hwep->num, hwep->dir, value);
+
+		if (!value)
+			hwep->wedge = 0;
+
+		if (hwep->type == USB_ENDPOINT_XFER_CONTROL)
+			hwep->dir = (hwep->dir == TX) ? RX : TX;
+
+	} while (hwep->dir != direction);
+
+	spin_unlock_irqrestore(hwep->lock, flags);
+	return retval;
+}
+
+
+/**
+ * _gadget_stop_activity: stops all USB activity, flushes & disables all endpts
+ * @gadget: gadget
+ *
+ * This function returns an error code
+ */
+static int _gadget_stop_activity(struct usb_gadget *gadget)
+{
+	struct usb_ep *ep;
+	struct ci_hdrc    *ci = container_of(gadget, struct ci_hdrc, gadget);
+	unsigned long flags;
+
+	spin_lock_irqsave(&ci->lock, flags);
+	ci->gadget.speed = USB_SPEED_UNKNOWN;
+	ci->remote_wakeup = 0;
+	ci->suspended = 0;
+	spin_unlock_irqrestore(&ci->lock, flags);
+
+	/* flush all endpoints */
+	gadget_for_each_ep(ep, gadget) {
+		usb_ep_fifo_flush(ep);
+	}
+	usb_ep_fifo_flush(&ci->ep0out->ep);
+	usb_ep_fifo_flush(&ci->ep0in->ep);
+
+	/* make sure to disable all endpoints */
+	gadget_for_each_ep(ep, gadget) {
+		usb_ep_disable(ep);
+	}
+
+	if (ci->status != NULL) {
+		usb_ep_free_request(&ci->ep0in->ep, ci->status);
+		ci->status = NULL;
+	}
+
+	return 0;
+}
+
+/******************************************************************************
+ * ISR block
+ *****************************************************************************/
+/**
+ * isr_reset_handler: USB reset interrupt handler
+ * @ci: UDC device
+ *
+ * This function resets USB engine after a bus reset occurred
+ */
+static void isr_reset_handler(struct ci_hdrc *ci)
+__releases(ci->lock)
+__acquires(ci->lock)
+{
+	int retval;
+
+	spin_unlock(&ci->lock);
+	if (ci->gadget.speed != USB_SPEED_UNKNOWN)
+		usb_gadget_udc_reset(&ci->gadget, ci->driver);
+
+	retval = _gadget_stop_activity(&ci->gadget);
+	if (retval)
+		goto done;
+
+	retval = hw_usb_reset(ci);
+	if (retval)
+		goto done;
+
+	ci->status = usb_ep_alloc_request(&ci->ep0in->ep, GFP_ATOMIC);
+	if (ci->status == NULL)
+		retval = -ENOMEM;
+
+done:
+	spin_lock(&ci->lock);
+
+	if (retval)
+		dev_err(ci->dev, "error: %i\n", retval);
+}
+
+/**
+ * isr_get_status_complete: get_status request complete function
+ * @ep:  endpoint
+ * @req: request handled
+ *
+ * Caller must release lock
+ */
+static void isr_get_status_complete(struct usb_ep *ep, struct usb_request *req)
+{
+	if (ep == NULL || req == NULL)
+		return;
+
+	kfree(req->buf);
+	usb_ep_free_request(ep, req);
+}
+
+/**
+ * _ep_queue: queues (submits) an I/O request to an endpoint
+ * @ep:        endpoint
+ * @req:       request
+ * @gfp_flags: GFP flags (not used)
+ *
+ * Caller must hold lock
+ * This function returns an error code
+ */
+static int _ep_queue(struct usb_ep *ep, struct usb_request *req,
+		    gfp_t __maybe_unused gfp_flags)
+{
+	struct ci_hw_ep  *hwep  = container_of(ep,  struct ci_hw_ep, ep);
+	struct ci_hw_req *hwreq = container_of(req, struct ci_hw_req, req);
+	struct ci_hdrc *ci = hwep->ci;
+	int retval = 0;
+
+	if (ep == NULL || req == NULL || hwep->ep.desc == NULL)
+		return -EINVAL;
+
+	if (hwep->type == USB_ENDPOINT_XFER_CONTROL) {
+		if (req->length)
+			hwep = (ci->ep0_dir == RX) ?
+			       ci->ep0out : ci->ep0in;
+		if (!list_empty(&hwep->qh.queue)) {
+			_ep_nuke(hwep);
+			dev_warn(hwep->ci->dev, "endpoint ctrl %X nuked\n",
+				 _usb_addr(hwep));
+		}
+	}
+
+	if (usb_endpoint_xfer_isoc(hwep->ep.desc) &&
+	    hwreq->req.length > hwep->ep.mult * hwep->ep.maxpacket) {
+		dev_err(hwep->ci->dev, "request length too big for isochronous\n");
+		return -EMSGSIZE;
+	}
+
+	/* first nuke then test link, e.g. previous status has not sent */
+	if (!list_empty(&hwreq->queue)) {
+		dev_err(hwep->ci->dev, "request already in queue\n");
+		return -EBUSY;
+	}
+
+	/* push request */
+	hwreq->req.status = -EINPROGRESS;
+	hwreq->req.actual = 0;
+
+	retval = _hardware_enqueue(hwep, hwreq);
+
+	if (retval == -EALREADY)
+		retval = 0;
+	if (!retval)
+		list_add_tail(&hwreq->queue, &hwep->qh.queue);
+
+	return retval;
+}
+
+/**
+ * isr_get_status_response: get_status request response
+ * @ci: ci struct
+ * @setup: setup request packet
+ *
+ * This function returns an error code
+ */
+static int isr_get_status_response(struct ci_hdrc *ci,
+				   struct usb_ctrlrequest *setup)
+__releases(hwep->lock)
+__acquires(hwep->lock)
+{
+	struct ci_hw_ep *hwep = ci->ep0in;
+	struct usb_request *req = NULL;
+	gfp_t gfp_flags = GFP_ATOMIC;
+	int dir, num, retval;
+
+	if (hwep == NULL || setup == NULL)
+		return -EINVAL;
+
+	spin_unlock(hwep->lock);
+	req = usb_ep_alloc_request(&hwep->ep, gfp_flags);
+	spin_lock(hwep->lock);
+	if (req == NULL)
+		return -ENOMEM;
+
+	req->complete = isr_get_status_complete;
+	req->length   = 2;
+	req->buf      = kzalloc(req->length, gfp_flags);
+	if (req->buf == NULL) {
+		retval = -ENOMEM;
+		goto err_free_req;
+	}
+
+	if ((setup->bRequestType & USB_RECIP_MASK) == USB_RECIP_DEVICE) {
+		*(u16 *)req->buf = (ci->remote_wakeup << 1) |
+			ci->gadget.is_selfpowered;
+	} else if ((setup->bRequestType & USB_RECIP_MASK) \
+		   == USB_RECIP_ENDPOINT) {
+		dir = (le16_to_cpu(setup->wIndex) & USB_ENDPOINT_DIR_MASK) ?
+			TX : RX;
+		num =  le16_to_cpu(setup->wIndex) & USB_ENDPOINT_NUMBER_MASK;
+		*(u16 *)req->buf = hw_ep_get_halt(ci, num, dir);
+	}
+	/* else do nothing; reserved for future use */
+
+	retval = _ep_queue(&hwep->ep, req, gfp_flags);
+	if (retval)
+		goto err_free_buf;
+
+	return 0;
+
+ err_free_buf:
+	kfree(req->buf);
+ err_free_req:
+	spin_unlock(hwep->lock);
+	usb_ep_free_request(&hwep->ep, req);
+	spin_lock(hwep->lock);
+	return retval;
+}
+
+/**
+ * isr_setup_status_complete: setup_status request complete function
+ * @ep:  endpoint
+ * @req: request handled
+ *
+ * Caller must release lock. Put the port in test mode if test mode
+ * feature is selected.
+ */
+static void
+isr_setup_status_complete(struct usb_ep *ep, struct usb_request *req)
+{
+	struct ci_hdrc *ci = req->context;
+	unsigned long flags;
+
+	if (ci->setaddr) {
+		hw_usb_set_address(ci, ci->address);
+		ci->setaddr = false;
+		if (ci->address)
+			usb_gadget_set_state(&ci->gadget, USB_STATE_ADDRESS);
+	}
+
+	spin_lock_irqsave(&ci->lock, flags);
+	if (ci->test_mode)
+		hw_port_test_set(ci, ci->test_mode);
+	spin_unlock_irqrestore(&ci->lock, flags);
+}
+
+/**
+ * isr_setup_status_phase: queues the status phase of a setup transation
+ * @ci: ci struct
+ *
+ * This function returns an error code
+ */
+static int isr_setup_status_phase(struct ci_hdrc *ci)
+{
+	struct ci_hw_ep *hwep;
+
+	/*
+	 * Unexpected USB controller behavior, caused by bad signal integrity
+	 * or ground reference problems, can lead to isr_setup_status_phase
+	 * being called with ci->status equal to NULL.
+	 * If this situation occurs, you should review your USB hardware design.
+	 */
+	if (WARN_ON_ONCE(!ci->status))
+		return -EPIPE;
+
+	hwep = (ci->ep0_dir == TX) ? ci->ep0out : ci->ep0in;
+	ci->status->context = ci;
+	ci->status->complete = isr_setup_status_complete;
+
+	return _ep_queue(&hwep->ep, ci->status, GFP_ATOMIC);
+}
+
+/**
+ * isr_tr_complete_low: transaction complete low level handler
+ * @hwep: endpoint
+ *
+ * This function returns an error code
+ * Caller must hold lock
+ */
+static int isr_tr_complete_low(struct ci_hw_ep *hwep)
+__releases(hwep->lock)
+__acquires(hwep->lock)
+{
+	struct ci_hw_req *hwreq, *hwreqtemp;
+	struct ci_hw_ep *hweptemp = hwep;
+	int retval = 0;
+
+	list_for_each_entry_safe(hwreq, hwreqtemp, &hwep->qh.queue,
+			queue) {
+		retval = _hardware_dequeue(hwep, hwreq);
+		if (retval < 0)
+			break;
+		list_del_init(&hwreq->queue);
+		if (hwreq->req.complete != NULL) {
+			spin_unlock(hwep->lock);
+			if ((hwep->type == USB_ENDPOINT_XFER_CONTROL) &&
+					hwreq->req.length)
+				hweptemp = hwep->ci->ep0in;
+			usb_gadget_giveback_request(&hweptemp->ep, &hwreq->req);
+			spin_lock(hwep->lock);
+		}
+	}
+
+	if (retval == -EBUSY)
+		retval = 0;
+
+	return retval;
+}
+
+static int otg_a_alt_hnp_support(struct ci_hdrc *ci)
+{
+	dev_warn(&ci->gadget.dev,
+		"connect the device to an alternate port if you want HNP\n");
+	return isr_setup_status_phase(ci);
+}
+
+/**
+ * isr_setup_packet_handler: setup packet handler
+ * @ci: UDC descriptor
+ *
+ * This function handles setup packet 
+ */
+static void isr_setup_packet_handler(struct ci_hdrc *ci)
+__releases(ci->lock)
+__acquires(ci->lock)
+{
+	struct ci_hw_ep *hwep = &ci->ci_hw_ep[0];
+	struct usb_ctrlrequest req;
+	int type, num, dir, err = -EINVAL;
+	u8 tmode = 0;
+
+	/*
+	 * Flush data and handshake transactions of previous
+	 * setup packet.
+	 */
+	_ep_nuke(ci->ep0out);
+	_ep_nuke(ci->ep0in);
+
+	/* read_setup_packet */
+	do {
+		hw_test_and_set_setup_guard(ci);
+		memcpy(&req, &hwep->qh.ptr->setup, sizeof(req));
+	} while (!hw_test_and_clear_setup_guard(ci));
+
+	type = req.bRequestType;
+
+	ci->ep0_dir = (type & USB_DIR_IN) ? TX : RX;
+
+	switch (req.bRequest) {
+	case USB_REQ_CLEAR_FEATURE:
+		if (type == (USB_DIR_OUT|USB_RECIP_ENDPOINT) &&
+				le16_to_cpu(req.wValue) ==
+				USB_ENDPOINT_HALT) {
+			if (req.wLength != 0)
+				break;
+			num  = le16_to_cpu(req.wIndex);
+			dir = (num & USB_ENDPOINT_DIR_MASK) ? TX : RX;
+			num &= USB_ENDPOINT_NUMBER_MASK;
+			if (dir == TX)
+				num += ci->hw_ep_max / 2;
+			if (!ci->ci_hw_ep[num].wedge) {
+				spin_unlock(&ci->lock);
+				err = usb_ep_clear_halt(
+					&ci->ci_hw_ep[num].ep);
+				spin_lock(&ci->lock);
+				if (err)
+					break;
+			}
+			err = isr_setup_status_phase(ci);
+		} else if (type == (USB_DIR_OUT|USB_RECIP_DEVICE) &&
+				le16_to_cpu(req.wValue) ==
+				USB_DEVICE_REMOTE_WAKEUP) {
+			if (req.wLength != 0)
+				break;
+			ci->remote_wakeup = 0;
+			err = isr_setup_status_phase(ci);
+		} else {
+			goto delegate;
+		}
+		break;
+	case USB_REQ_GET_STATUS:
+		if ((type != (USB_DIR_IN|USB_RECIP_DEVICE) ||
+			le16_to_cpu(req.wIndex) == OTG_STS_SELECTOR) &&
+		    type != (USB_DIR_IN|USB_RECIP_ENDPOINT) &&
+		    type != (USB_DIR_IN|USB_RECIP_INTERFACE))
+			goto delegate;
+		if (le16_to_cpu(req.wLength) != 2 ||
+		    le16_to_cpu(req.wValue)  != 0)
+			break;
+		err = isr_get_status_response(ci, &req);
+		break;
+	case USB_REQ_SET_ADDRESS:
+		if (type != (USB_DIR_OUT|USB_RECIP_DEVICE))
+			goto delegate;
+		if (le16_to_cpu(req.wLength) != 0 ||
+		    le16_to_cpu(req.wIndex)  != 0)
+			break;
+		ci->address = (u8)le16_to_cpu(req.wValue);
+		ci->setaddr = true;
+		err = isr_setup_status_phase(ci);
+		break;
+	case USB_REQ_SET_FEATURE:
+		if (type == (USB_DIR_OUT|USB_RECIP_ENDPOINT) &&
+				le16_to_cpu(req.wValue) ==
+				USB_ENDPOINT_HALT) {
+			if (req.wLength != 0)
+				break;
+			num  = le16_to_cpu(req.wIndex);
+			dir = (num & USB_ENDPOINT_DIR_MASK) ? TX : RX;
+			num &= USB_ENDPOINT_NUMBER_MASK;
+			if (dir == TX)
+				num += ci->hw_ep_max / 2;
+
+			spin_unlock(&ci->lock);
+			err = _ep_set_halt(&ci->ci_hw_ep[num].ep, 1, false);
+			spin_lock(&ci->lock);
+			if (!err)
+				isr_setup_status_phase(ci);
+		} else if (type == (USB_DIR_OUT|USB_RECIP_DEVICE)) {
+			if (req.wLength != 0)
+				break;
+			switch (le16_to_cpu(req.wValue)) {
+			case USB_DEVICE_REMOTE_WAKEUP:
+				ci->remote_wakeup = 1;
+				err = isr_setup_status_phase(ci);
+				break;
+			case USB_DEVICE_TEST_MODE:
+				tmode = le16_to_cpu(req.wIndex) >> 8;
+				switch (tmode) {
+				case TEST_J:
+				case TEST_K:
+				case TEST_SE0_NAK:
+				case TEST_PACKET:
+				case TEST_FORCE_EN:
+					ci->test_mode = tmode;
+					err = isr_setup_status_phase(
+							ci);
+					break;
+				default:
+					break;
+				}
+				break;
+			case USB_DEVICE_B_HNP_ENABLE:
+				if (ci_otg_is_fsm_mode(ci)) {
+					ci->gadget.b_hnp_enable = 1;
+					err = isr_setup_status_phase(
+							ci);
+				}
+				break;
+			case USB_DEVICE_A_ALT_HNP_SUPPORT:
+				if (ci_otg_is_fsm_mode(ci))
+					err = otg_a_alt_hnp_support(ci);
+				break;
+			case USB_DEVICE_A_HNP_SUPPORT:
+				if (ci_otg_is_fsm_mode(ci)) {
+					ci->gadget.a_hnp_support = 1;
+					err = isr_setup_status_phase(
+							ci);
+				}
+				break;
+			default:
+				goto delegate;
+			}
+		} else {
+			goto delegate;
+		}
+		break;
+	default:
+delegate:
+		if (req.wLength == 0)   /* no data phase */
+			ci->ep0_dir = TX;
+
+		spin_unlock(&ci->lock);
+		err = ci->driver->setup(&ci->gadget, &req);
+		spin_lock(&ci->lock);
+		break;
+	}
+
+	if (err < 0) {
+		spin_unlock(&ci->lock);
+		if (_ep_set_halt(&hwep->ep, 1, false))
+			dev_err(ci->dev, "error: _ep_set_halt\n");
+		spin_lock(&ci->lock);
+	}
+}
+
+/**
+ * isr_tr_complete_handler: transaction complete interrupt handler
+ * @ci: UDC descriptor
+ *
+ * This function handles traffic events
+ */
+static void isr_tr_complete_handler(struct ci_hdrc *ci)
+__releases(ci->lock)
+__acquires(ci->lock)
+{
+	unsigned i;
+	int err;
+
+	for (i = 0; i < ci->hw_ep_max; i++) {
+		struct ci_hw_ep *hwep  = &ci->ci_hw_ep[i];
+
+		if (hwep->ep.desc == NULL)
+			continue;   /* not configured */
+
+		if (hw_test_and_clear_complete(ci, i)) {
+			err = isr_tr_complete_low(hwep);
+			if (hwep->type == USB_ENDPOINT_XFER_CONTROL) {
+				if (err > 0)   /* needs status phase */
+					err = isr_setup_status_phase(ci);
+				if (err < 0) {
+					spin_unlock(&ci->lock);
+					if (_ep_set_halt(&hwep->ep, 1, false))
+						dev_err(ci->dev,
+						"error: _ep_set_halt\n");
+					spin_lock(&ci->lock);
+				}
+			}
+		}
+
+		/* Only handle setup packet below */
+		if (i == 0 &&
+			hw_test_and_clear(ci, OP_ENDPTSETUPSTAT, BIT(0)))
+			isr_setup_packet_handler(ci);
+	}
+}
+
+/******************************************************************************
+ * ENDPT block
+ *****************************************************************************/
+/**
+ * ep_enable: configure endpoint, making it usable
+ *
+ * Check usb_ep_enable() at "usb_gadget.h" for details
+ */
+static int ep_enable(struct usb_ep *ep,
+		     const struct usb_endpoint_descriptor *desc)
+{
+	struct ci_hw_ep *hwep = container_of(ep, struct ci_hw_ep, ep);
+	int retval = 0;
+	unsigned long flags;
+	u32 cap = 0;
+
+	if (ep == NULL || desc == NULL)
+		return -EINVAL;
+
+	spin_lock_irqsave(hwep->lock, flags);
+
+	/* only internal SW should enable ctrl endpts */
+
+	if (!list_empty(&hwep->qh.queue)) {
+		dev_warn(hwep->ci->dev, "enabling a non-empty endpoint!\n");
+		spin_unlock_irqrestore(hwep->lock, flags);
+		return -EBUSY;
+	}
+
+	hwep->ep.desc = desc;
+
+	hwep->dir  = usb_endpoint_dir_in(desc) ? TX : RX;
+	hwep->num  = usb_endpoint_num(desc);
+	hwep->type = usb_endpoint_type(desc);
+
+	hwep->ep.maxpacket = usb_endpoint_maxp(desc);
+	hwep->ep.mult = usb_endpoint_maxp_mult(desc);
+
+	if (hwep->type == USB_ENDPOINT_XFER_CONTROL)
+		cap |= QH_IOS;
+
+	cap |= QH_ZLT;
+	cap |= (hwep->ep.maxpacket << __ffs(QH_MAX_PKT)) & QH_MAX_PKT;
+	/*
+	 * For ISO-TX, we set mult at QH as the largest value, and use
+	 * MultO at TD as real mult value.
+	 */
+	if (hwep->type == USB_ENDPOINT_XFER_ISOC && hwep->dir == TX)
+		cap |= 3 << __ffs(QH_MULT);
+
+	hwep->qh.ptr->cap = cpu_to_le32(cap);
+
+	hwep->qh.ptr->td.next |= cpu_to_le32(TD_TERMINATE);   /* needed? */
+
+	if (hwep->num != 0 && hwep->type == USB_ENDPOINT_XFER_CONTROL) {
+		dev_err(hwep->ci->dev, "Set control xfer at non-ep0\n");
+		retval = -EINVAL;
+	}
+
+	/*
+	 * Enable endpoints in the HW other than ep0 as ep0
+	 * is always enabled
+	 */
+	if (hwep->num)
+		retval |= hw_ep_enable(hwep->ci, hwep->num, hwep->dir,
+				       hwep->type);
+
+	spin_unlock_irqrestore(hwep->lock, flags);
+	return retval;
+}
+
+/**
+ * ep_disable: endpoint is no longer usable
+ *
+ * Check usb_ep_disable() at "usb_gadget.h" for details
+ */
+static int ep_disable(struct usb_ep *ep)
+{
+	struct ci_hw_ep *hwep = container_of(ep, struct ci_hw_ep, ep);
+	int direction, retval = 0;
+	unsigned long flags;
+
+	if (ep == NULL)
+		return -EINVAL;
+	else if (hwep->ep.desc == NULL)
+		return -EBUSY;
+
+	spin_lock_irqsave(hwep->lock, flags);
+
+	/* only internal SW should disable ctrl endpts */
+
+	direction = hwep->dir;
+	do {
+		retval |= _ep_nuke(hwep);
+		retval |= hw_ep_disable(hwep->ci, hwep->num, hwep->dir);
+
+		if (hwep->type == USB_ENDPOINT_XFER_CONTROL)
+			hwep->dir = (hwep->dir == TX) ? RX : TX;
+
+	} while (hwep->dir != direction);
+
+	hwep->ep.desc = NULL;
+
+	spin_unlock_irqrestore(hwep->lock, flags);
+	return retval;
+}
+
+/**
+ * ep_alloc_request: allocate a request object to use with this endpoint
+ *
+ * Check usb_ep_alloc_request() at "usb_gadget.h" for details
+ */
+static struct usb_request *ep_alloc_request(struct usb_ep *ep, gfp_t gfp_flags)
+{
+	struct ci_hw_req *hwreq = NULL;
+
+	if (ep == NULL)
+		return NULL;
+
+	hwreq = kzalloc(sizeof(struct ci_hw_req), gfp_flags);
+	if (hwreq != NULL) {
+		INIT_LIST_HEAD(&hwreq->queue);
+		INIT_LIST_HEAD(&hwreq->tds);
+	}
+
+	return (hwreq == NULL) ? NULL : &hwreq->req;
+}
+
+/**
+ * ep_free_request: frees a request object
+ *
+ * Check usb_ep_free_request() at "usb_gadget.h" for details
+ */
+static void ep_free_request(struct usb_ep *ep, struct usb_request *req)
+{
+	struct ci_hw_ep  *hwep  = container_of(ep,  struct ci_hw_ep, ep);
+	struct ci_hw_req *hwreq = container_of(req, struct ci_hw_req, req);
+	struct td_node *node, *tmpnode;
+	unsigned long flags;
+
+	if (ep == NULL || req == NULL) {
+		return;
+	} else if (!list_empty(&hwreq->queue)) {
+		dev_err(hwep->ci->dev, "freeing queued request\n");
+		return;
+	}
+
+	spin_lock_irqsave(hwep->lock, flags);
+
+	list_for_each_entry_safe(node, tmpnode, &hwreq->tds, td) {
+		dma_pool_free(hwep->td_pool, node->ptr, node->dma);
+		list_del_init(&node->td);
+		node->ptr = NULL;
+		kfree(node);
+	}
+
+	kfree(hwreq);
+
+	spin_unlock_irqrestore(hwep->lock, flags);
+}
+
+/**
+ * ep_queue: queues (submits) an I/O request to an endpoint
+ *
+ * Check usb_ep_queue()* at usb_gadget.h" for details
+ */
+static int ep_queue(struct usb_ep *ep, struct usb_request *req,
+		    gfp_t __maybe_unused gfp_flags)
+{
+	struct ci_hw_ep  *hwep  = container_of(ep,  struct ci_hw_ep, ep);
+	int retval = 0;
+	unsigned long flags;
+
+	if (ep == NULL || req == NULL || hwep->ep.desc == NULL)
+		return -EINVAL;
+
+	spin_lock_irqsave(hwep->lock, flags);
+	retval = _ep_queue(ep, req, gfp_flags);
+	spin_unlock_irqrestore(hwep->lock, flags);
+	return retval;
+}
+
+/**
+ * ep_dequeue: dequeues (cancels, unlinks) an I/O request from an endpoint
+ *
+ * Check usb_ep_dequeue() at "usb_gadget.h" for details
+ */
+static int ep_dequeue(struct usb_ep *ep, struct usb_request *req)
+{
+	struct ci_hw_ep  *hwep  = container_of(ep,  struct ci_hw_ep, ep);
+	struct ci_hw_req *hwreq = container_of(req, struct ci_hw_req, req);
+	unsigned long flags;
+	struct td_node *node, *tmpnode;
+
+	if (ep == NULL || req == NULL || hwreq->req.status != -EALREADY ||
+		hwep->ep.desc == NULL || list_empty(&hwreq->queue) ||
+		list_empty(&hwep->qh.queue))
+		return -EINVAL;
+
+	spin_lock_irqsave(hwep->lock, flags);
+
+	hw_ep_flush(hwep->ci, hwep->num, hwep->dir);
+
+	list_for_each_entry_safe(node, tmpnode, &hwreq->tds, td) {
+		dma_pool_free(hwep->td_pool, node->ptr, node->dma);
+		list_del(&node->td);
+		kfree(node);
+	}
+
+	/* pop request */
+	list_del_init(&hwreq->queue);
+
+	usb_gadget_unmap_request(&hwep->ci->gadget, req, hwep->dir);
+
+	req->status = -ECONNRESET;
+
+	if (hwreq->req.complete != NULL) {
+		spin_unlock(hwep->lock);
+		usb_gadget_giveback_request(&hwep->ep, &hwreq->req);
+		spin_lock(hwep->lock);
+	}
+
+	spin_unlock_irqrestore(hwep->lock, flags);
+	return 0;
+}
+
+/**
+ * ep_set_halt: sets the endpoint halt feature
+ *
+ * Check usb_ep_set_halt() at "usb_gadget.h" for details
+ */
+static int ep_set_halt(struct usb_ep *ep, int value)
+{
+	return _ep_set_halt(ep, value, true);
+}
+
+/**
+ * ep_set_wedge: sets the halt feature and ignores clear requests
+ *
+ * Check usb_ep_set_wedge() at "usb_gadget.h" for details
+ */
+static int ep_set_wedge(struct usb_ep *ep)
+{
+	struct ci_hw_ep *hwep = container_of(ep, struct ci_hw_ep, ep);
+	unsigned long flags;
+
+	if (ep == NULL || hwep->ep.desc == NULL)
+		return -EINVAL;
+
+	spin_lock_irqsave(hwep->lock, flags);
+	hwep->wedge = 1;
+	spin_unlock_irqrestore(hwep->lock, flags);
+
+	return usb_ep_set_halt(ep);
+}
+
+/**
+ * ep_fifo_flush: flushes contents of a fifo
+ *
+ * Check usb_ep_fifo_flush() at "usb_gadget.h" for details
+ */
+static void ep_fifo_flush(struct usb_ep *ep)
+{
+	struct ci_hw_ep *hwep = container_of(ep, struct ci_hw_ep, ep);
+	unsigned long flags;
+
+	if (ep == NULL) {
+		dev_err(hwep->ci->dev, "%02X: -EINVAL\n", _usb_addr(hwep));
+		return;
+	}
+
+	spin_lock_irqsave(hwep->lock, flags);
+
+	hw_ep_flush(hwep->ci, hwep->num, hwep->dir);
+
+	spin_unlock_irqrestore(hwep->lock, flags);
+}
+
+/**
+ * Endpoint-specific part of the API to the USB controller hardware
+ * Check "usb_gadget.h" for details
+ */
+static const struct usb_ep_ops usb_ep_ops = {
+	.enable	       = ep_enable,
+	.disable       = ep_disable,
+	.alloc_request = ep_alloc_request,
+	.free_request  = ep_free_request,
+	.queue	       = ep_queue,
+	.dequeue       = ep_dequeue,
+	.set_halt      = ep_set_halt,
+	.set_wedge     = ep_set_wedge,
+	.fifo_flush    = ep_fifo_flush,
+};
+
+/******************************************************************************
+ * GADGET block
+ *****************************************************************************/
+static int ci_udc_vbus_session(struct usb_gadget *_gadget, int is_active)
+{
+	struct ci_hdrc *ci = container_of(_gadget, struct ci_hdrc, gadget);
+	unsigned long flags;
+	int gadget_ready = 0;
+
+	spin_lock_irqsave(&ci->lock, flags);
+	ci->vbus_active = is_active;
+	if (ci->driver)
+		gadget_ready = 1;
+	spin_unlock_irqrestore(&ci->lock, flags);
+
+	if (ci->usb_phy)
+		usb_phy_set_charger_state(ci->usb_phy, is_active ?
+			USB_CHARGER_PRESENT : USB_CHARGER_ABSENT);
+
+	if (gadget_ready) {
+		if (is_active) {
+			pm_runtime_get_sync(&_gadget->dev);
+			hw_device_reset(ci);
+			hw_device_state(ci, ci->ep0out->qh.dma);
+			usb_gadget_set_state(_gadget, USB_STATE_POWERED);
+			usb_udc_vbus_handler(_gadget, true);
+		} else {
+			usb_udc_vbus_handler(_gadget, false);
+			if (ci->driver)
+				ci->driver->disconnect(&ci->gadget);
+			hw_device_state(ci, 0);
+			if (ci->platdata->notify_event)
+				ci->platdata->notify_event(ci,
+				CI_HDRC_CONTROLLER_STOPPED_EVENT);
+			_gadget_stop_activity(&ci->gadget);
+			pm_runtime_put_sync(&_gadget->dev);
+			usb_gadget_set_state(_gadget, USB_STATE_NOTATTACHED);
+		}
+	}
+
+	return 0;
+}
+
+static int ci_udc_wakeup(struct usb_gadget *_gadget)
+{
+	struct ci_hdrc *ci = container_of(_gadget, struct ci_hdrc, gadget);
+	unsigned long flags;
+	int ret = 0;
+
+	spin_lock_irqsave(&ci->lock, flags);
+	if (!ci->remote_wakeup) {
+		ret = -EOPNOTSUPP;
+		goto out;
+	}
+	if (!hw_read(ci, OP_PORTSC, PORTSC_SUSP)) {
+		ret = -EINVAL;
+		goto out;
+	}
+	hw_write(ci, OP_PORTSC, PORTSC_FPR, PORTSC_FPR);
+out:
+	spin_unlock_irqrestore(&ci->lock, flags);
+	return ret;
+}
+
+static int ci_udc_vbus_draw(struct usb_gadget *_gadget, unsigned ma)
+{
+	struct ci_hdrc *ci = container_of(_gadget, struct ci_hdrc, gadget);
+
+	if (ci->usb_phy)
+		return usb_phy_set_power(ci->usb_phy, ma);
+	return -ENOTSUPP;
+}
+
+static int ci_udc_selfpowered(struct usb_gadget *_gadget, int is_on)
+{
+	struct ci_hdrc *ci = container_of(_gadget, struct ci_hdrc, gadget);
+	struct ci_hw_ep *hwep = ci->ep0in;
+	unsigned long flags;
+
+	spin_lock_irqsave(hwep->lock, flags);
+	_gadget->is_selfpowered = (is_on != 0);
+	spin_unlock_irqrestore(hwep->lock, flags);
+
+	return 0;
+}
+
+/* Change Data+ pullup status
+ * this func is used by usb_gadget_connect/disconnet
+ */
+static int ci_udc_pullup(struct usb_gadget *_gadget, int is_on)
+{
+	struct ci_hdrc *ci = container_of(_gadget, struct ci_hdrc, gadget);
+
+	/*
+	 * Data+ pullup controlled by OTG state machine in OTG fsm mode;
+	 * and don't touch Data+ in host mode for dual role config.
+	 */
+	if (ci_otg_is_fsm_mode(ci) || ci->role == CI_ROLE_HOST)
+		return 0;
+
+	pm_runtime_get_sync(&ci->gadget.dev);
+	if (is_on)
+		hw_write(ci, OP_USBCMD, USBCMD_RS, USBCMD_RS);
+	else
+		hw_write(ci, OP_USBCMD, USBCMD_RS, 0);
+	pm_runtime_put_sync(&ci->gadget.dev);
+
+	return 0;
+}
+
+static int ci_udc_start(struct usb_gadget *gadget,
+			 struct usb_gadget_driver *driver);
+static int ci_udc_stop(struct usb_gadget *gadget);
+/**
+ * Device operations part of the API to the USB controller hardware,
+ * which don't involve endpoints (or i/o)
+ * Check  "usb_gadget.h" for details
+ */
+static const struct usb_gadget_ops usb_gadget_ops = {
+	.vbus_session	= ci_udc_vbus_session,
+	.wakeup		= ci_udc_wakeup,
+	.set_selfpowered	= ci_udc_selfpowered,
+	.pullup		= ci_udc_pullup,
+	.vbus_draw	= ci_udc_vbus_draw,
+	.udc_start	= ci_udc_start,
+	.udc_stop	= ci_udc_stop,
+};
+
+static int init_eps(struct ci_hdrc *ci)
+{
+	int retval = 0, i, j;
+
+	for (i = 0; i < ci->hw_ep_max/2; i++)
+		for (j = RX; j <= TX; j++) {
+			int k = i + j * ci->hw_ep_max/2;
+			struct ci_hw_ep *hwep = &ci->ci_hw_ep[k];
+
+			scnprintf(hwep->name, sizeof(hwep->name), "ep%i%s", i,
+					(j == TX)  ? "in" : "out");
+
+			hwep->ci          = ci;
+			hwep->lock         = &ci->lock;
+			hwep->td_pool      = ci->td_pool;
+
+			hwep->ep.name      = hwep->name;
+			hwep->ep.ops       = &usb_ep_ops;
+
+			if (i == 0) {
+				hwep->ep.caps.type_control = true;
+			} else {
+				hwep->ep.caps.type_iso = true;
+				hwep->ep.caps.type_bulk = true;
+				hwep->ep.caps.type_int = true;
+			}
+
+			if (j == TX)
+				hwep->ep.caps.dir_in = true;
+			else
+				hwep->ep.caps.dir_out = true;
+
+			/*
+			 * for ep0: maxP defined in desc, for other
+			 * eps, maxP is set by epautoconfig() called
+			 * by gadget layer
+			 */
+			usb_ep_set_maxpacket_limit(&hwep->ep, (unsigned short)~0);
+
+			INIT_LIST_HEAD(&hwep->qh.queue);
+			hwep->qh.ptr = dma_pool_zalloc(ci->qh_pool, GFP_KERNEL,
+						       &hwep->qh.dma);
+			if (hwep->qh.ptr == NULL)
+				retval = -ENOMEM;
+
+			/*
+			 * set up shorthands for ep0 out and in endpoints,
+			 * don't add to gadget's ep_list
+			 */
+			if (i == 0) {
+				if (j == RX)
+					ci->ep0out = hwep;
+				else
+					ci->ep0in = hwep;
+
+				usb_ep_set_maxpacket_limit(&hwep->ep, CTRL_PAYLOAD_MAX);
+				continue;
+			}
+
+			list_add_tail(&hwep->ep.ep_list, &ci->gadget.ep_list);
+		}
+
+	return retval;
+}
+
+static void destroy_eps(struct ci_hdrc *ci)
+{
+	int i;
+
+	for (i = 0; i < ci->hw_ep_max; i++) {
+		struct ci_hw_ep *hwep = &ci->ci_hw_ep[i];
+
+		if (hwep->pending_td)
+			free_pending_td(hwep);
+		dma_pool_free(ci->qh_pool, hwep->qh.ptr, hwep->qh.dma);
+	}
+}
+
+/**
+ * ci_udc_start: register a gadget driver
+ * @gadget: our gadget
+ * @driver: the driver being registered
+ *
+ * Interrupts are enabled here.
+ */
+static int ci_udc_start(struct usb_gadget *gadget,
+			 struct usb_gadget_driver *driver)
+{
+	struct ci_hdrc *ci = container_of(gadget, struct ci_hdrc, gadget);
+	int retval = -ENOMEM;
+
+	if (driver->disconnect == NULL)
+		return -EINVAL;
+
+
+	ci->ep0out->ep.desc = &ctrl_endpt_out_desc;
+	retval = usb_ep_enable(&ci->ep0out->ep);
+	if (retval)
+		return retval;
+
+	ci->ep0in->ep.desc = &ctrl_endpt_in_desc;
+	retval = usb_ep_enable(&ci->ep0in->ep);
+	if (retval)
+		return retval;
+
+	ci->driver = driver;
+
+	/* Start otg fsm for B-device */
+	if (ci_otg_is_fsm_mode(ci) && ci->fsm.id) {
+		ci_hdrc_otg_fsm_start(ci);
+		return retval;
+	}
+
+	pm_runtime_get_sync(&ci->gadget.dev);
+	if (ci->vbus_active) {
+		hw_device_reset(ci);
+	} else {
+		usb_udc_vbus_handler(&ci->gadget, false);
+		pm_runtime_put_sync(&ci->gadget.dev);
+		return retval;
+	}
+
+	retval = hw_device_state(ci, ci->ep0out->qh.dma);
+	if (retval)
+		pm_runtime_put_sync(&ci->gadget.dev);
+
+	return retval;
+}
+
+static void ci_udc_stop_for_otg_fsm(struct ci_hdrc *ci)
+{
+	if (!ci_otg_is_fsm_mode(ci))
+		return;
+
+	mutex_lock(&ci->fsm.lock);
+	if (ci->fsm.otg->state == OTG_STATE_A_PERIPHERAL) {
+		ci->fsm.a_bidl_adis_tmout = 1;
+		ci_hdrc_otg_fsm_start(ci);
+	} else if (ci->fsm.otg->state == OTG_STATE_B_PERIPHERAL) {
+		ci->fsm.protocol = PROTO_UNDEF;
+		ci->fsm.otg->state = OTG_STATE_UNDEFINED;
+	}
+	mutex_unlock(&ci->fsm.lock);
+}
+
+/**
+ * ci_udc_stop: unregister a gadget driver
+ */
+static int ci_udc_stop(struct usb_gadget *gadget)
+{
+	struct ci_hdrc *ci = container_of(gadget, struct ci_hdrc, gadget);
+	unsigned long flags;
+
+	spin_lock_irqsave(&ci->lock, flags);
+
+	if (ci->vbus_active) {
+		hw_device_state(ci, 0);
+		spin_unlock_irqrestore(&ci->lock, flags);
+		if (ci->platdata->notify_event)
+			ci->platdata->notify_event(ci,
+			CI_HDRC_CONTROLLER_STOPPED_EVENT);
+		_gadget_stop_activity(&ci->gadget);
+		spin_lock_irqsave(&ci->lock, flags);
+		pm_runtime_put(&ci->gadget.dev);
+	}
+
+	ci->driver = NULL;
+	spin_unlock_irqrestore(&ci->lock, flags);
+
+	ci_udc_stop_for_otg_fsm(ci);
+	return 0;
+}
+
+/******************************************************************************
+ * BUS block
+ *****************************************************************************/
+/**
+ * udc_irq: ci interrupt handler
+ *
+ * This function returns IRQ_HANDLED if the IRQ has been handled
+ * It locks access to registers
+ */
+static irqreturn_t udc_irq(struct ci_hdrc *ci)
+{
+	irqreturn_t retval;
+	u32 intr;
+
+	if (ci == NULL)
+		return IRQ_HANDLED;
+
+	spin_lock(&ci->lock);
+
+	if (ci->platdata->flags & CI_HDRC_REGS_SHARED) {
+		if (hw_read(ci, OP_USBMODE, USBMODE_CM) !=
+				USBMODE_CM_DC) {
+			spin_unlock(&ci->lock);
+			return IRQ_NONE;
+		}
+	}
+	intr = hw_test_and_clear_intr_active(ci);
+
+	if (intr) {
+		/* order defines priority - do NOT change it */
+		if (USBi_URI & intr)
+			isr_reset_handler(ci);
+
+		if (USBi_PCI & intr) {
+			ci->gadget.speed = hw_port_is_high_speed(ci) ?
+				USB_SPEED_HIGH : USB_SPEED_FULL;
+			if (ci->suspended) {
+				if (ci->driver->resume) {
+					spin_unlock(&ci->lock);
+					ci->driver->resume(&ci->gadget);
+					spin_lock(&ci->lock);
+				}
+				ci->suspended = 0;
+				usb_gadget_set_state(&ci->gadget,
+						ci->resume_state);
+			}
+		}
+
+		if (USBi_UI  & intr)
+			isr_tr_complete_handler(ci);
+
+		if ((USBi_SLI & intr) && !(ci->suspended)) {
+			ci->suspended = 1;
+			ci->resume_state = ci->gadget.state;
+			if (ci->gadget.speed != USB_SPEED_UNKNOWN &&
+			    ci->driver->suspend) {
+				spin_unlock(&ci->lock);
+				ci->driver->suspend(&ci->gadget);
+				spin_lock(&ci->lock);
+			}
+			usb_gadget_set_state(&ci->gadget,
+					USB_STATE_SUSPENDED);
+		}
+		retval = IRQ_HANDLED;
+	} else {
+		retval = IRQ_NONE;
+	}
+	spin_unlock(&ci->lock);
+
+	return retval;
+}
+
+/**
+ * udc_start: initialize gadget role
+ * @ci: chipidea controller
+ */
+static int udc_start(struct ci_hdrc *ci)
+{
+	struct device *dev = ci->dev;
+	struct usb_otg_caps *otg_caps = &ci->platdata->ci_otg_caps;
+	int retval = 0;
+
+	ci->gadget.ops          = &usb_gadget_ops;
+	ci->gadget.speed        = USB_SPEED_UNKNOWN;
+	ci->gadget.max_speed    = USB_SPEED_HIGH;
+	ci->gadget.name         = ci->platdata->name;
+	ci->gadget.otg_caps	= otg_caps;
+
+	if (ci->platdata->flags & CI_HDRC_REQUIRES_ALIGNED_DMA)
+		ci->gadget.quirk_avoids_skb_reserve = 1;
+
+	if (ci->is_otg && (otg_caps->hnp_support || otg_caps->srp_support ||
+						otg_caps->adp_support))
+		ci->gadget.is_otg = 1;
+
+	INIT_LIST_HEAD(&ci->gadget.ep_list);
+
+	/* alloc resources */
+	ci->qh_pool = dma_pool_create("ci_hw_qh", dev->parent,
+				       sizeof(struct ci_hw_qh),
+				       64, CI_HDRC_PAGE_SIZE);
+	if (ci->qh_pool == NULL)
+		return -ENOMEM;
+
+	ci->td_pool = dma_pool_create("ci_hw_td", dev->parent,
+				       sizeof(struct ci_hw_td),
+				       64, CI_HDRC_PAGE_SIZE);
+	if (ci->td_pool == NULL) {
+		retval = -ENOMEM;
+		goto free_qh_pool;
+	}
+
+	retval = init_eps(ci);
+	if (retval)
+		goto free_pools;
+
+	ci->gadget.ep0 = &ci->ep0in->ep;
+
+	retval = usb_add_gadget_udc(dev, &ci->gadget);
+	if (retval)
+		goto destroy_eps;
+
+	pm_runtime_no_callbacks(&ci->gadget.dev);
+	pm_runtime_enable(&ci->gadget.dev);
+
+	return retval;
+
+destroy_eps:
+	destroy_eps(ci);
+free_pools:
+	dma_pool_destroy(ci->td_pool);
+free_qh_pool:
+	dma_pool_destroy(ci->qh_pool);
+	return retval;
+}
+
+/**
+ * ci_hdrc_gadget_destroy: parent remove must call this to remove UDC
+ *
+ * No interrupts active, the IRQ has been released
+ */
+void ci_hdrc_gadget_destroy(struct ci_hdrc *ci)
+{
+	if (!ci->roles[CI_ROLE_GADGET])
+		return;
+
+	usb_del_gadget_udc(&ci->gadget);
+
+	destroy_eps(ci);
+
+	dma_pool_destroy(ci->td_pool);
+	dma_pool_destroy(ci->qh_pool);
+}
+
+static int udc_id_switch_for_device(struct ci_hdrc *ci)
+{
+	if (ci->is_otg)
+		/* Clear and enable BSV irq */
+		hw_write_otgsc(ci, OTGSC_BSVIS | OTGSC_BSVIE,
+					OTGSC_BSVIS | OTGSC_BSVIE);
+
+	return 0;
+}
+
+static void udc_id_switch_for_host(struct ci_hdrc *ci)
+{
+	/*
+	 * host doesn't care B_SESSION_VALID event
+	 * so clear and disbale BSV irq
+	 */
+	if (ci->is_otg)
+		hw_write_otgsc(ci, OTGSC_BSVIE | OTGSC_BSVIS, OTGSC_BSVIS);
+
+	ci->vbus_active = 0;
+}
+
+/**
+ * ci_hdrc_gadget_init - initialize device related bits
+ * ci: the controller
+ *
+ * This function initializes the gadget, if the device is "device capable".
+ */
+int ci_hdrc_gadget_init(struct ci_hdrc *ci)
+{
+	struct ci_role_driver *rdrv;
+	int ret;
+
+	if (!hw_read(ci, CAP_DCCPARAMS, DCCPARAMS_DC))
+		return -ENXIO;
+
+	rdrv = devm_kzalloc(ci->dev, sizeof(*rdrv), GFP_KERNEL);
+	if (!rdrv)
+		return -ENOMEM;
+
+	rdrv->start	= udc_id_switch_for_device;
+	rdrv->stop	= udc_id_switch_for_host;
+	rdrv->irq	= udc_irq;
+	rdrv->name	= "gadget";
+
+	ret = udc_start(ci);
+	if (!ret)
+		ci->roles[CI_ROLE_GADGET] = rdrv;
+
+	return ret;
+}
diff --git a/drivers/usb/chipidea/udc.h b/drivers/usb/chipidea/udc.h
new file mode 100644
index 0000000..e023735
--- /dev/null
+++ b/drivers/usb/chipidea/udc.h
@@ -0,0 +1,100 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * udc.h - ChipIdea UDC structures
+ *
+ * Copyright (C) 2008 Chipidea - MIPS Technologies, Inc. All rights reserved.
+ *
+ * Author: David Lopo
+ */
+
+#ifndef __DRIVERS_USB_CHIPIDEA_UDC_H
+#define __DRIVERS_USB_CHIPIDEA_UDC_H
+
+#include <linux/list.h>
+
+#define CTRL_PAYLOAD_MAX   64
+#define RX        0  /* similar to USB_DIR_OUT but can be used as an index */
+#define TX        1  /* similar to USB_DIR_IN  but can be used as an index */
+
+/* DMA layout of transfer descriptors */
+struct ci_hw_td {
+	/* 0 */
+	__le32 next;
+#define TD_TERMINATE          BIT(0)
+#define TD_ADDR_MASK          (0xFFFFFFEUL << 5)
+	/* 1 */
+	__le32 token;
+#define TD_STATUS             (0x00FFUL <<  0)
+#define TD_STATUS_TR_ERR      BIT(3)
+#define TD_STATUS_DT_ERR      BIT(5)
+#define TD_STATUS_HALTED      BIT(6)
+#define TD_STATUS_ACTIVE      BIT(7)
+#define TD_MULTO              (0x0003UL << 10)
+#define TD_IOC                BIT(15)
+#define TD_TOTAL_BYTES        (0x7FFFUL << 16)
+	/* 2 */
+	__le32 page[5];
+#define TD_CURR_OFFSET        (0x0FFFUL <<  0)
+#define TD_FRAME_NUM          (0x07FFUL <<  0)
+#define TD_RESERVED_MASK      (0x0FFFUL <<  0)
+} __attribute__ ((packed, aligned(4)));
+
+/* DMA layout of queue heads */
+struct ci_hw_qh {
+	/* 0 */
+	__le32 cap;
+#define QH_IOS                BIT(15)
+#define QH_MAX_PKT            (0x07FFUL << 16)
+#define QH_ZLT                BIT(29)
+#define QH_MULT               (0x0003UL << 30)
+#define QH_ISO_MULT(x)		((x >> 11) & 0x03)
+	/* 1 */
+	__le32 curr;
+	/* 2 - 8 */
+	struct ci_hw_td		td;
+	/* 9 */
+	__le32 RESERVED;
+	struct usb_ctrlrequest   setup;
+} __attribute__ ((packed, aligned(4)));
+
+struct td_node {
+	struct list_head	td;
+	dma_addr_t		dma;
+	struct ci_hw_td		*ptr;
+};
+
+/**
+ * struct ci_hw_req - usb request representation
+ * @req: request structure for gadget drivers
+ * @queue: link to QH list
+ * @ptr: transfer descriptor for this request
+ * @dma: dma address for the transfer descriptor
+ * @zptr: transfer descriptor for the zero packet
+ * @zdma: dma address of the zero packet's transfer descriptor
+ */
+struct ci_hw_req {
+	struct usb_request	req;
+	struct list_head	queue;
+	struct list_head	tds;
+};
+
+#ifdef CONFIG_USB_CHIPIDEA_UDC
+
+int ci_hdrc_gadget_init(struct ci_hdrc *ci);
+void ci_hdrc_gadget_destroy(struct ci_hdrc *ci);
+
+#else
+
+static inline int ci_hdrc_gadget_init(struct ci_hdrc *ci)
+{
+	return -ENXIO;
+}
+
+static inline void ci_hdrc_gadget_destroy(struct ci_hdrc *ci)
+{
+
+}
+
+#endif
+
+#endif /* __DRIVERS_USB_CHIPIDEA_UDC_H */
diff --git a/drivers/usb/chipidea/ulpi.c b/drivers/usb/chipidea/ulpi.c
new file mode 100644
index 0000000..dfec07e
--- /dev/null
+++ b/drivers/usb/chipidea/ulpi.c
@@ -0,0 +1,108 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (c) 2016 Linaro Ltd.
+ */
+
+#include <linux/device.h>
+#include <linux/usb/chipidea.h>
+#include <linux/ulpi/interface.h>
+
+#include "ci.h"
+
+#define ULPI_WAKEUP		BIT(31)
+#define ULPI_RUN		BIT(30)
+#define ULPI_WRITE		BIT(29)
+#define ULPI_SYNC_STATE		BIT(27)
+#define ULPI_ADDR(n)		((n) << 16)
+#define ULPI_DATA(n)		(n)
+
+static int ci_ulpi_wait(struct ci_hdrc *ci, u32 mask)
+{
+	unsigned long usec = 10000;
+
+	while (usec--) {
+		if (!hw_read(ci, OP_ULPI_VIEWPORT, mask))
+			return 0;
+
+		udelay(1);
+	}
+
+	return -ETIMEDOUT;
+}
+
+static int ci_ulpi_read(struct device *dev, u8 addr)
+{
+	struct ci_hdrc *ci = dev_get_drvdata(dev);
+	int ret;
+
+	hw_write(ci, OP_ULPI_VIEWPORT, 0xffffffff, ULPI_WRITE | ULPI_WAKEUP);
+	ret = ci_ulpi_wait(ci, ULPI_WAKEUP);
+	if (ret)
+		return ret;
+
+	hw_write(ci, OP_ULPI_VIEWPORT, 0xffffffff, ULPI_RUN | ULPI_ADDR(addr));
+	ret = ci_ulpi_wait(ci, ULPI_RUN);
+	if (ret)
+		return ret;
+
+	return hw_read(ci, OP_ULPI_VIEWPORT, GENMASK(15, 8)) >> 8;
+}
+
+static int ci_ulpi_write(struct device *dev, u8 addr, u8 val)
+{
+	struct ci_hdrc *ci = dev_get_drvdata(dev);
+	int ret;
+
+	hw_write(ci, OP_ULPI_VIEWPORT, 0xffffffff, ULPI_WRITE | ULPI_WAKEUP);
+	ret = ci_ulpi_wait(ci, ULPI_WAKEUP);
+	if (ret)
+		return ret;
+
+	hw_write(ci, OP_ULPI_VIEWPORT, 0xffffffff,
+		 ULPI_RUN | ULPI_WRITE | ULPI_ADDR(addr) | val);
+	return ci_ulpi_wait(ci, ULPI_RUN);
+}
+
+int ci_ulpi_init(struct ci_hdrc *ci)
+{
+	if (ci->platdata->phy_mode != USBPHY_INTERFACE_MODE_ULPI)
+		return 0;
+
+	/*
+	 * Set PORTSC correctly so we can read/write ULPI registers for
+	 * identification purposes
+	 */
+	hw_phymode_configure(ci);
+
+	ci->ulpi_ops.read = ci_ulpi_read;
+	ci->ulpi_ops.write = ci_ulpi_write;
+	ci->ulpi = ulpi_register_interface(ci->dev, &ci->ulpi_ops);
+	if (IS_ERR(ci->ulpi))
+		dev_err(ci->dev, "failed to register ULPI interface");
+
+	return PTR_ERR_OR_ZERO(ci->ulpi);
+}
+
+void ci_ulpi_exit(struct ci_hdrc *ci)
+{
+	if (ci->ulpi) {
+		ulpi_unregister_interface(ci->ulpi);
+		ci->ulpi = NULL;
+	}
+}
+
+int ci_ulpi_resume(struct ci_hdrc *ci)
+{
+	int cnt = 100000;
+
+	if (ci->platdata->phy_mode != USBPHY_INTERFACE_MODE_ULPI)
+		return 0;
+
+	while (cnt-- > 0) {
+		if (hw_read(ci, OP_ULPI_VIEWPORT, ULPI_SYNC_STATE))
+			return 0;
+		udelay(1);
+	}
+
+	return -ETIMEDOUT;
+}
diff --git a/drivers/usb/chipidea/usbmisc_imx.c b/drivers/usb/chipidea/usbmisc_imx.c
new file mode 100644
index 0000000..34ad5bf
--- /dev/null
+++ b/drivers/usb/chipidea/usbmisc_imx.c
@@ -0,0 +1,638 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright 2012 Freescale Semiconductor, Inc.
+ */
+
+#include <linux/module.h>
+#include <linux/of_platform.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/delay.h>
+
+#include "ci_hdrc_imx.h"
+
+#define MX25_USB_PHY_CTRL_OFFSET	0x08
+#define MX25_BM_EXTERNAL_VBUS_DIVIDER	BIT(23)
+
+#define MX25_EHCI_INTERFACE_SINGLE_UNI	(2 << 0)
+#define MX25_EHCI_INTERFACE_DIFF_UNI	(0 << 0)
+#define MX25_EHCI_INTERFACE_MASK	(0xf)
+
+#define MX25_OTG_SIC_SHIFT		29
+#define MX25_OTG_SIC_MASK		(0x3 << MX25_OTG_SIC_SHIFT)
+#define MX25_OTG_PM_BIT			BIT(24)
+#define MX25_OTG_PP_BIT			BIT(11)
+#define MX25_OTG_OCPOL_BIT		BIT(3)
+
+#define MX25_H1_SIC_SHIFT		21
+#define MX25_H1_SIC_MASK		(0x3 << MX25_H1_SIC_SHIFT)
+#define MX25_H1_PP_BIT			BIT(18)
+#define MX25_H1_PM_BIT			BIT(16)
+#define MX25_H1_IPPUE_UP_BIT		BIT(7)
+#define MX25_H1_IPPUE_DOWN_BIT		BIT(6)
+#define MX25_H1_TLL_BIT			BIT(5)
+#define MX25_H1_USBTE_BIT		BIT(4)
+#define MX25_H1_OCPOL_BIT		BIT(2)
+
+#define MX27_H1_PM_BIT			BIT(8)
+#define MX27_H2_PM_BIT			BIT(16)
+#define MX27_OTG_PM_BIT			BIT(24)
+
+#define MX53_USB_OTG_PHY_CTRL_0_OFFSET	0x08
+#define MX53_USB_OTG_PHY_CTRL_1_OFFSET	0x0c
+#define MX53_USB_CTRL_1_OFFSET	        0x10
+#define MX53_USB_CTRL_1_H2_XCVR_CLK_SEL_MASK (0x11 << 2)
+#define MX53_USB_CTRL_1_H2_XCVR_CLK_SEL_ULPI BIT(2)
+#define MX53_USB_CTRL_1_H3_XCVR_CLK_SEL_MASK (0x11 << 6)
+#define MX53_USB_CTRL_1_H3_XCVR_CLK_SEL_ULPI BIT(6)
+#define MX53_USB_UH2_CTRL_OFFSET	0x14
+#define MX53_USB_UH3_CTRL_OFFSET	0x18
+#define MX53_USB_CLKONOFF_CTRL_OFFSET	0x24
+#define MX53_USB_CLKONOFF_CTRL_H2_INT60CKOFF BIT(21)
+#define MX53_USB_CLKONOFF_CTRL_H3_INT60CKOFF BIT(22)
+#define MX53_BM_OVER_CUR_DIS_H1		BIT(5)
+#define MX53_BM_OVER_CUR_DIS_OTG	BIT(8)
+#define MX53_BM_OVER_CUR_DIS_UHx	BIT(30)
+#define MX53_USB_CTRL_1_UH2_ULPI_EN	BIT(26)
+#define MX53_USB_CTRL_1_UH3_ULPI_EN	BIT(27)
+#define MX53_USB_UHx_CTRL_WAKE_UP_EN	BIT(7)
+#define MX53_USB_UHx_CTRL_ULPI_INT_EN	BIT(8)
+#define MX53_USB_PHYCTRL1_PLLDIV_MASK	0x3
+#define MX53_USB_PLL_DIV_24_MHZ		0x01
+
+#define MX6_BM_NON_BURST_SETTING	BIT(1)
+#define MX6_BM_OVER_CUR_DIS		BIT(7)
+#define MX6_BM_OVER_CUR_POLARITY	BIT(8)
+#define MX6_BM_WAKEUP_ENABLE		BIT(10)
+#define MX6_BM_ID_WAKEUP		BIT(16)
+#define MX6_BM_VBUS_WAKEUP		BIT(17)
+#define MX6SX_BM_DPDM_WAKEUP_EN		BIT(29)
+#define MX6_BM_WAKEUP_INTR		BIT(31)
+#define MX6_USB_OTG1_PHY_CTRL		0x18
+/* For imx6dql, it is host-only controller, for later imx6, it is otg's */
+#define MX6_USB_OTG2_PHY_CTRL		0x1c
+#define MX6SX_USB_VBUS_WAKEUP_SOURCE(v)	(v << 8)
+#define MX6SX_USB_VBUS_WAKEUP_SOURCE_VBUS	MX6SX_USB_VBUS_WAKEUP_SOURCE(0)
+#define MX6SX_USB_VBUS_WAKEUP_SOURCE_AVALID	MX6SX_USB_VBUS_WAKEUP_SOURCE(1)
+#define MX6SX_USB_VBUS_WAKEUP_SOURCE_BVALID	MX6SX_USB_VBUS_WAKEUP_SOURCE(2)
+#define MX6SX_USB_VBUS_WAKEUP_SOURCE_SESS_END	MX6SX_USB_VBUS_WAKEUP_SOURCE(3)
+
+#define VF610_OVER_CUR_DIS		BIT(7)
+
+#define MX7D_USBNC_USB_CTRL2		0x4
+#define MX7D_USB_VBUS_WAKEUP_SOURCE_MASK	0x3
+#define MX7D_USB_VBUS_WAKEUP_SOURCE(v)		(v << 0)
+#define MX7D_USB_VBUS_WAKEUP_SOURCE_VBUS	MX7D_USB_VBUS_WAKEUP_SOURCE(0)
+#define MX7D_USB_VBUS_WAKEUP_SOURCE_AVALID	MX7D_USB_VBUS_WAKEUP_SOURCE(1)
+#define MX7D_USB_VBUS_WAKEUP_SOURCE_BVALID	MX7D_USB_VBUS_WAKEUP_SOURCE(2)
+#define MX7D_USB_VBUS_WAKEUP_SOURCE_SESS_END	MX7D_USB_VBUS_WAKEUP_SOURCE(3)
+
+struct usbmisc_ops {
+	/* It's called once when probe a usb device */
+	int (*init)(struct imx_usbmisc_data *data);
+	/* It's called once after adding a usb device */
+	int (*post)(struct imx_usbmisc_data *data);
+	/* It's called when we need to enable/disable usb wakeup */
+	int (*set_wakeup)(struct imx_usbmisc_data *data, bool enabled);
+};
+
+struct imx_usbmisc {
+	void __iomem *base;
+	spinlock_t lock;
+	const struct usbmisc_ops *ops;
+};
+
+static inline bool is_imx53_usbmisc(struct imx_usbmisc_data *data);
+
+static int usbmisc_imx25_init(struct imx_usbmisc_data *data)
+{
+	struct imx_usbmisc *usbmisc = dev_get_drvdata(data->dev);
+	unsigned long flags;
+	u32 val = 0;
+
+	if (data->index > 1)
+		return -EINVAL;
+
+	spin_lock_irqsave(&usbmisc->lock, flags);
+	switch (data->index) {
+	case 0:
+		val = readl(usbmisc->base);
+		val &= ~(MX25_OTG_SIC_MASK | MX25_OTG_PP_BIT);
+		val |= (MX25_EHCI_INTERFACE_DIFF_UNI & MX25_EHCI_INTERFACE_MASK) << MX25_OTG_SIC_SHIFT;
+		val |= (MX25_OTG_PM_BIT | MX25_OTG_OCPOL_BIT);
+		writel(val, usbmisc->base);
+		break;
+	case 1:
+		val = readl(usbmisc->base);
+		val &= ~(MX25_H1_SIC_MASK | MX25_H1_PP_BIT |  MX25_H1_IPPUE_UP_BIT);
+		val |= (MX25_EHCI_INTERFACE_SINGLE_UNI & MX25_EHCI_INTERFACE_MASK) << MX25_H1_SIC_SHIFT;
+		val |= (MX25_H1_PM_BIT | MX25_H1_OCPOL_BIT | MX25_H1_TLL_BIT |
+			MX25_H1_USBTE_BIT | MX25_H1_IPPUE_DOWN_BIT);
+
+		writel(val, usbmisc->base);
+
+		break;
+	}
+	spin_unlock_irqrestore(&usbmisc->lock, flags);
+
+	return 0;
+}
+
+static int usbmisc_imx25_post(struct imx_usbmisc_data *data)
+{
+	struct imx_usbmisc *usbmisc = dev_get_drvdata(data->dev);
+	void __iomem *reg;
+	unsigned long flags;
+	u32 val;
+
+	if (data->index > 2)
+		return -EINVAL;
+
+	if (data->index)
+		return 0;
+
+	spin_lock_irqsave(&usbmisc->lock, flags);
+	reg = usbmisc->base + MX25_USB_PHY_CTRL_OFFSET;
+	val = readl(reg);
+
+	if (data->evdo)
+		val |= MX25_BM_EXTERNAL_VBUS_DIVIDER;
+	else
+		val &= ~MX25_BM_EXTERNAL_VBUS_DIVIDER;
+
+	writel(val, reg);
+	spin_unlock_irqrestore(&usbmisc->lock, flags);
+	usleep_range(5000, 10000); /* needed to stabilize voltage */
+
+	return 0;
+}
+
+static int usbmisc_imx27_init(struct imx_usbmisc_data *data)
+{
+	struct imx_usbmisc *usbmisc = dev_get_drvdata(data->dev);
+	unsigned long flags;
+	u32 val;
+
+	switch (data->index) {
+	case 0:
+		val = MX27_OTG_PM_BIT;
+		break;
+	case 1:
+		val = MX27_H1_PM_BIT;
+		break;
+	case 2:
+		val = MX27_H2_PM_BIT;
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	spin_lock_irqsave(&usbmisc->lock, flags);
+	if (data->disable_oc)
+		val = readl(usbmisc->base) | val;
+	else
+		val = readl(usbmisc->base) & ~val;
+	writel(val, usbmisc->base);
+	spin_unlock_irqrestore(&usbmisc->lock, flags);
+
+	return 0;
+}
+
+static int usbmisc_imx53_init(struct imx_usbmisc_data *data)
+{
+	struct imx_usbmisc *usbmisc = dev_get_drvdata(data->dev);
+	void __iomem *reg = NULL;
+	unsigned long flags;
+	u32 val = 0;
+
+	if (data->index > 3)
+		return -EINVAL;
+
+	/* Select a 24 MHz reference clock for the PHY  */
+	val = readl(usbmisc->base + MX53_USB_OTG_PHY_CTRL_1_OFFSET);
+	val &= ~MX53_USB_PHYCTRL1_PLLDIV_MASK;
+	val |= MX53_USB_PLL_DIV_24_MHZ;
+	writel(val, usbmisc->base + MX53_USB_OTG_PHY_CTRL_1_OFFSET);
+
+	spin_lock_irqsave(&usbmisc->lock, flags);
+
+	switch (data->index) {
+	case 0:
+		if (data->disable_oc) {
+			reg = usbmisc->base + MX53_USB_OTG_PHY_CTRL_0_OFFSET;
+			val = readl(reg) | MX53_BM_OVER_CUR_DIS_OTG;
+			writel(val, reg);
+		}
+		break;
+	case 1:
+		if (data->disable_oc) {
+			reg = usbmisc->base + MX53_USB_OTG_PHY_CTRL_0_OFFSET;
+			val = readl(reg) | MX53_BM_OVER_CUR_DIS_H1;
+			writel(val, reg);
+		}
+		break;
+	case 2:
+		if (data->ulpi) {
+			/* set USBH2 into ULPI-mode. */
+			reg = usbmisc->base + MX53_USB_CTRL_1_OFFSET;
+			val = readl(reg) | MX53_USB_CTRL_1_UH2_ULPI_EN;
+			/* select ULPI clock */
+			val &= ~MX53_USB_CTRL_1_H2_XCVR_CLK_SEL_MASK;
+			val |= MX53_USB_CTRL_1_H2_XCVR_CLK_SEL_ULPI;
+			writel(val, reg);
+			/* Set interrupt wake up enable */
+			reg = usbmisc->base + MX53_USB_UH2_CTRL_OFFSET;
+			val = readl(reg) | MX53_USB_UHx_CTRL_WAKE_UP_EN
+				| MX53_USB_UHx_CTRL_ULPI_INT_EN;
+			writel(val, reg);
+			if (is_imx53_usbmisc(data)) {
+				/* Disable internal 60Mhz clock */
+				reg = usbmisc->base +
+					MX53_USB_CLKONOFF_CTRL_OFFSET;
+				val = readl(reg) |
+					MX53_USB_CLKONOFF_CTRL_H2_INT60CKOFF;
+				writel(val, reg);
+			}
+
+		}
+		if (data->disable_oc) {
+			reg = usbmisc->base + MX53_USB_UH2_CTRL_OFFSET;
+			val = readl(reg) | MX53_BM_OVER_CUR_DIS_UHx;
+			writel(val, reg);
+		}
+		break;
+	case 3:
+		if (data->ulpi) {
+			/* set USBH3 into ULPI-mode. */
+			reg = usbmisc->base + MX53_USB_CTRL_1_OFFSET;
+			val = readl(reg) | MX53_USB_CTRL_1_UH3_ULPI_EN;
+			/* select ULPI clock */
+			val &= ~MX53_USB_CTRL_1_H3_XCVR_CLK_SEL_MASK;
+			val |= MX53_USB_CTRL_1_H3_XCVR_CLK_SEL_ULPI;
+			writel(val, reg);
+			/* Set interrupt wake up enable */
+			reg = usbmisc->base + MX53_USB_UH3_CTRL_OFFSET;
+			val = readl(reg) | MX53_USB_UHx_CTRL_WAKE_UP_EN
+				| MX53_USB_UHx_CTRL_ULPI_INT_EN;
+			writel(val, reg);
+
+			if (is_imx53_usbmisc(data)) {
+				/* Disable internal 60Mhz clock */
+				reg = usbmisc->base +
+					MX53_USB_CLKONOFF_CTRL_OFFSET;
+				val = readl(reg) |
+					MX53_USB_CLKONOFF_CTRL_H3_INT60CKOFF;
+				writel(val, reg);
+			}
+		}
+		if (data->disable_oc) {
+			reg = usbmisc->base + MX53_USB_UH3_CTRL_OFFSET;
+			val = readl(reg) | MX53_BM_OVER_CUR_DIS_UHx;
+			writel(val, reg);
+		}
+		break;
+	}
+
+	spin_unlock_irqrestore(&usbmisc->lock, flags);
+
+	return 0;
+}
+
+static int usbmisc_imx6q_set_wakeup
+	(struct imx_usbmisc_data *data, bool enabled)
+{
+	struct imx_usbmisc *usbmisc = dev_get_drvdata(data->dev);
+	unsigned long flags;
+	u32 val;
+	u32 wakeup_setting = (MX6_BM_WAKEUP_ENABLE |
+		MX6_BM_VBUS_WAKEUP | MX6_BM_ID_WAKEUP);
+	int ret = 0;
+
+	if (data->index > 3)
+		return -EINVAL;
+
+	spin_lock_irqsave(&usbmisc->lock, flags);
+	val = readl(usbmisc->base + data->index * 4);
+	if (enabled) {
+		val |= wakeup_setting;
+	} else {
+		if (val & MX6_BM_WAKEUP_INTR)
+			pr_debug("wakeup int at ci_hdrc.%d\n", data->index);
+		val &= ~wakeup_setting;
+	}
+	writel(val, usbmisc->base + data->index * 4);
+	spin_unlock_irqrestore(&usbmisc->lock, flags);
+
+	return ret;
+}
+
+static int usbmisc_imx6q_init(struct imx_usbmisc_data *data)
+{
+	struct imx_usbmisc *usbmisc = dev_get_drvdata(data->dev);
+	unsigned long flags;
+	u32 reg;
+
+	if (data->index > 3)
+		return -EINVAL;
+
+	spin_lock_irqsave(&usbmisc->lock, flags);
+
+	reg = readl(usbmisc->base + data->index * 4);
+	if (data->disable_oc) {
+		reg |= MX6_BM_OVER_CUR_DIS;
+	} else if (data->oc_polarity == 1) {
+		/* High active */
+		reg &= ~(MX6_BM_OVER_CUR_DIS | MX6_BM_OVER_CUR_POLARITY);
+	}
+	writel(reg, usbmisc->base + data->index * 4);
+
+	/* SoC non-burst setting */
+	reg = readl(usbmisc->base + data->index * 4);
+	writel(reg | MX6_BM_NON_BURST_SETTING,
+			usbmisc->base + data->index * 4);
+
+	spin_unlock_irqrestore(&usbmisc->lock, flags);
+
+	usbmisc_imx6q_set_wakeup(data, false);
+
+	return 0;
+}
+
+static int usbmisc_imx6sx_init(struct imx_usbmisc_data *data)
+{
+	void __iomem *reg = NULL;
+	unsigned long flags;
+	struct imx_usbmisc *usbmisc = dev_get_drvdata(data->dev);
+	u32 val;
+
+	usbmisc_imx6q_init(data);
+
+	if (data->index == 0 || data->index == 1) {
+		reg = usbmisc->base + MX6_USB_OTG1_PHY_CTRL + data->index * 4;
+		spin_lock_irqsave(&usbmisc->lock, flags);
+		/* Set vbus wakeup source as bvalid */
+		val = readl(reg);
+		writel(val | MX6SX_USB_VBUS_WAKEUP_SOURCE_BVALID, reg);
+		/*
+		 * Disable dp/dm wakeup in device mode when vbus is
+		 * not there.
+		 */
+		val = readl(usbmisc->base + data->index * 4);
+		writel(val & ~MX6SX_BM_DPDM_WAKEUP_EN,
+			usbmisc->base + data->index * 4);
+		spin_unlock_irqrestore(&usbmisc->lock, flags);
+	}
+
+	return 0;
+}
+
+static int usbmisc_vf610_init(struct imx_usbmisc_data *data)
+{
+	struct imx_usbmisc *usbmisc = dev_get_drvdata(data->dev);
+	u32 reg;
+
+	/*
+	 * Vybrid only has one misc register set, but in two different
+	 * areas. These is reflected in two instances of this driver.
+	 */
+	if (data->index >= 1)
+		return -EINVAL;
+
+	if (data->disable_oc) {
+		reg = readl(usbmisc->base);
+		writel(reg | VF610_OVER_CUR_DIS, usbmisc->base);
+	}
+
+	return 0;
+}
+
+static int usbmisc_imx7d_set_wakeup
+	(struct imx_usbmisc_data *data, bool enabled)
+{
+	struct imx_usbmisc *usbmisc = dev_get_drvdata(data->dev);
+	unsigned long flags;
+	u32 val;
+	u32 wakeup_setting = (MX6_BM_WAKEUP_ENABLE |
+		MX6_BM_VBUS_WAKEUP | MX6_BM_ID_WAKEUP);
+
+	spin_lock_irqsave(&usbmisc->lock, flags);
+	val = readl(usbmisc->base);
+	if (enabled) {
+		writel(val | wakeup_setting, usbmisc->base);
+	} else {
+		if (val & MX6_BM_WAKEUP_INTR)
+			dev_dbg(data->dev, "wakeup int\n");
+		writel(val & ~wakeup_setting, usbmisc->base);
+	}
+	spin_unlock_irqrestore(&usbmisc->lock, flags);
+
+	return 0;
+}
+
+static int usbmisc_imx7d_init(struct imx_usbmisc_data *data)
+{
+	struct imx_usbmisc *usbmisc = dev_get_drvdata(data->dev);
+	unsigned long flags;
+	u32 reg;
+
+	if (data->index >= 1)
+		return -EINVAL;
+
+	spin_lock_irqsave(&usbmisc->lock, flags);
+	reg = readl(usbmisc->base);
+	if (data->disable_oc) {
+		reg |= MX6_BM_OVER_CUR_DIS;
+	} else if (data->oc_polarity == 1) {
+		/* High active */
+		reg &= ~(MX6_BM_OVER_CUR_DIS | MX6_BM_OVER_CUR_POLARITY);
+	}
+	writel(reg, usbmisc->base);
+
+	reg = readl(usbmisc->base + MX7D_USBNC_USB_CTRL2);
+	reg &= ~MX7D_USB_VBUS_WAKEUP_SOURCE_MASK;
+	writel(reg | MX7D_USB_VBUS_WAKEUP_SOURCE_BVALID,
+		 usbmisc->base + MX7D_USBNC_USB_CTRL2);
+	spin_unlock_irqrestore(&usbmisc->lock, flags);
+
+	usbmisc_imx7d_set_wakeup(data, false);
+
+	return 0;
+}
+
+static const struct usbmisc_ops imx25_usbmisc_ops = {
+	.init = usbmisc_imx25_init,
+	.post = usbmisc_imx25_post,
+};
+
+static const struct usbmisc_ops imx27_usbmisc_ops = {
+	.init = usbmisc_imx27_init,
+};
+
+static const struct usbmisc_ops imx51_usbmisc_ops = {
+	.init = usbmisc_imx53_init,
+};
+
+static const struct usbmisc_ops imx53_usbmisc_ops = {
+	.init = usbmisc_imx53_init,
+};
+
+static const struct usbmisc_ops imx6q_usbmisc_ops = {
+	.set_wakeup = usbmisc_imx6q_set_wakeup,
+	.init = usbmisc_imx6q_init,
+};
+
+static const struct usbmisc_ops vf610_usbmisc_ops = {
+	.init = usbmisc_vf610_init,
+};
+
+static const struct usbmisc_ops imx6sx_usbmisc_ops = {
+	.set_wakeup = usbmisc_imx6q_set_wakeup,
+	.init = usbmisc_imx6sx_init,
+};
+
+static const struct usbmisc_ops imx7d_usbmisc_ops = {
+	.init = usbmisc_imx7d_init,
+	.set_wakeup = usbmisc_imx7d_set_wakeup,
+};
+
+static inline bool is_imx53_usbmisc(struct imx_usbmisc_data *data)
+{
+	struct imx_usbmisc *usbmisc = dev_get_drvdata(data->dev);
+
+	return usbmisc->ops == &imx53_usbmisc_ops;
+}
+
+int imx_usbmisc_init(struct imx_usbmisc_data *data)
+{
+	struct imx_usbmisc *usbmisc;
+
+	if (!data)
+		return 0;
+
+	usbmisc = dev_get_drvdata(data->dev);
+	if (!usbmisc->ops->init)
+		return 0;
+	return usbmisc->ops->init(data);
+}
+EXPORT_SYMBOL_GPL(imx_usbmisc_init);
+
+int imx_usbmisc_init_post(struct imx_usbmisc_data *data)
+{
+	struct imx_usbmisc *usbmisc;
+
+	if (!data)
+		return 0;
+
+	usbmisc = dev_get_drvdata(data->dev);
+	if (!usbmisc->ops->post)
+		return 0;
+	return usbmisc->ops->post(data);
+}
+EXPORT_SYMBOL_GPL(imx_usbmisc_init_post);
+
+int imx_usbmisc_set_wakeup(struct imx_usbmisc_data *data, bool enabled)
+{
+	struct imx_usbmisc *usbmisc;
+
+	if (!data)
+		return 0;
+
+	usbmisc = dev_get_drvdata(data->dev);
+	if (!usbmisc->ops->set_wakeup)
+		return 0;
+	return usbmisc->ops->set_wakeup(data, enabled);
+}
+EXPORT_SYMBOL_GPL(imx_usbmisc_set_wakeup);
+
+static const struct of_device_id usbmisc_imx_dt_ids[] = {
+	{
+		.compatible = "fsl,imx25-usbmisc",
+		.data = &imx25_usbmisc_ops,
+	},
+	{
+		.compatible = "fsl,imx35-usbmisc",
+		.data = &imx25_usbmisc_ops,
+	},
+	{
+		.compatible = "fsl,imx27-usbmisc",
+		.data = &imx27_usbmisc_ops,
+	},
+	{
+		.compatible = "fsl,imx51-usbmisc",
+		.data = &imx51_usbmisc_ops,
+	},
+	{
+		.compatible = "fsl,imx53-usbmisc",
+		.data = &imx53_usbmisc_ops,
+	},
+	{
+		.compatible = "fsl,imx6q-usbmisc",
+		.data = &imx6q_usbmisc_ops,
+	},
+	{
+		.compatible = "fsl,vf610-usbmisc",
+		.data = &vf610_usbmisc_ops,
+	},
+	{
+		.compatible = "fsl,imx6sx-usbmisc",
+		.data = &imx6sx_usbmisc_ops,
+	},
+	{
+		.compatible = "fsl,imx6ul-usbmisc",
+		.data = &imx6sx_usbmisc_ops,
+	},
+	{
+		.compatible = "fsl,imx7d-usbmisc",
+		.data = &imx7d_usbmisc_ops,
+	},
+	{ /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, usbmisc_imx_dt_ids);
+
+static int usbmisc_imx_probe(struct platform_device *pdev)
+{
+	struct resource	*res;
+	struct imx_usbmisc *data;
+	const struct of_device_id *of_id;
+
+	of_id = of_match_device(usbmisc_imx_dt_ids, &pdev->dev);
+	if (!of_id)
+		return -ENODEV;
+
+	data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL);
+	if (!data)
+		return -ENOMEM;
+
+	spin_lock_init(&data->lock);
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	data->base = devm_ioremap_resource(&pdev->dev, res);
+	if (IS_ERR(data->base))
+		return PTR_ERR(data->base);
+
+	data->ops = (const struct usbmisc_ops *)of_id->data;
+	platform_set_drvdata(pdev, data);
+
+	return 0;
+}
+
+static int usbmisc_imx_remove(struct platform_device *pdev)
+{
+	return 0;
+}
+
+static struct platform_driver usbmisc_imx_driver = {
+	.probe = usbmisc_imx_probe,
+	.remove = usbmisc_imx_remove,
+	.driver = {
+		.name = "usbmisc_imx",
+		.of_match_table = usbmisc_imx_dt_ids,
+	 },
+};
+
+module_platform_driver(usbmisc_imx_driver);
+
+MODULE_ALIAS("platform:usbmisc-imx");
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("driver for imx usb non-core registers");
+MODULE_AUTHOR("Richard Zhao <richard.zhao@freescale.com>");