v4.19.13 snapshot.
diff --git a/drivers/usb/renesas_usbhs/Kconfig b/drivers/usb/renesas_usbhs/Kconfig
new file mode 100644
index 0000000..7fdbff2
--- /dev/null
+++ b/drivers/usb/renesas_usbhs/Kconfig
@@ -0,0 +1,18 @@
+# SPDX-License-Identifier: GPL-2.0
+#
+# Renesas USBHS Controller Drivers
+#
+
+config USB_RENESAS_USBHS
+	tristate 'Renesas USBHS controller'
+	depends on USB_GADGET
+	depends on ARCH_RENESAS || SUPERH || COMPILE_TEST
+	depends on EXTCON || !EXTCON # if EXTCON=m, USBHS cannot be built-in
+	default n
+	help
+	  Renesas USBHS is a discrete USB host and peripheral controller chip
+	  that supports both full and high speed USB 2.0 data transfers.
+	  It has nine or more configurable endpoints, and endpoint zero.
+
+	  Say "y" to link the driver statically, or "m" to build a
+	  dynamically linked module called "renesas_usbhs"
diff --git a/drivers/usb/renesas_usbhs/Makefile b/drivers/usb/renesas_usbhs/Makefile
new file mode 100644
index 0000000..5c5b51b
--- /dev/null
+++ b/drivers/usb/renesas_usbhs/Makefile
@@ -0,0 +1,16 @@
+# SPDX-License-Identifier: GPL-2.0
+#
+# for Renesas USB
+#
+
+obj-$(CONFIG_USB_RENESAS_USBHS)	+= renesas_usbhs.o
+
+renesas_usbhs-y			:= common.o mod.o pipe.o fifo.o rcar2.o rcar3.o rza.o
+
+ifneq ($(CONFIG_USB_RENESAS_USBHS_HCD),)
+	renesas_usbhs-y		+= mod_host.o
+endif
+
+ifneq ($(CONFIG_USB_RENESAS_USBHS_UDC),)
+	renesas_usbhs-y		+= mod_gadget.o
+endif
diff --git a/drivers/usb/renesas_usbhs/common.c b/drivers/usb/renesas_usbhs/common.c
new file mode 100644
index 0000000..4310df4
--- /dev/null
+++ b/drivers/usb/renesas_usbhs/common.c
@@ -0,0 +1,815 @@
+// SPDX-License-Identifier: GPL-1.0+
+/*
+ * Renesas USB driver
+ *
+ * Copyright (C) 2011 Renesas Solutions Corp.
+ * Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
+ */
+#include <linux/err.h>
+#include <linux/gpio.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/of_device.h>
+#include <linux/of_gpio.h>
+#include <linux/pm_runtime.h>
+#include <linux/slab.h>
+#include <linux/sysfs.h>
+#include "common.h"
+#include "rcar2.h"
+#include "rcar3.h"
+#include "rza.h"
+
+/*
+ *		image of renesas_usbhs
+ *
+ * ex) gadget case
+
+ * mod.c
+ * mod_gadget.c
+ * mod_host.c		pipe.c		fifo.c
+ *
+ *			+-------+	+-----------+
+ *			| pipe0 |------>| fifo pio  |
+ * +------------+	+-------+	+-----------+
+ * | mod_gadget |=====> | pipe1 |--+
+ * +------------+	+-------+  |	+-----------+
+ *			| pipe2 |  |  +-| fifo dma0 |
+ * +------------+	+-------+  |  |	+-----------+
+ * | mod_host   |	| pipe3 |<-|--+
+ * +------------+	+-------+  |	+-----------+
+ *			| ....  |  +--->| fifo dma1 |
+ *			| ....  |	+-----------+
+ */
+
+
+#define USBHSF_RUNTIME_PWCTRL	(1 << 0)
+
+/* status */
+#define usbhsc_flags_init(p)   do {(p)->flags = 0; } while (0)
+#define usbhsc_flags_set(p, b) ((p)->flags |=  (b))
+#define usbhsc_flags_clr(p, b) ((p)->flags &= ~(b))
+#define usbhsc_flags_has(p, b) ((p)->flags &   (b))
+
+/*
+ * platform call back
+ *
+ * renesas usb support platform callback function.
+ * Below macro call it.
+ * if platform doesn't have callback, it return 0 (no error)
+ */
+#define usbhs_platform_call(priv, func, args...)\
+	(!(priv) ? -ENODEV :			\
+	 !((priv)->pfunc.func) ? 0 :		\
+	 (priv)->pfunc.func(args))
+
+/*
+ *		common functions
+ */
+u16 usbhs_read(struct usbhs_priv *priv, u32 reg)
+{
+	return ioread16(priv->base + reg);
+}
+
+void usbhs_write(struct usbhs_priv *priv, u32 reg, u16 data)
+{
+	iowrite16(data, priv->base + reg);
+}
+
+void usbhs_bset(struct usbhs_priv *priv, u32 reg, u16 mask, u16 data)
+{
+	u16 val = usbhs_read(priv, reg);
+
+	val &= ~mask;
+	val |= data & mask;
+
+	usbhs_write(priv, reg, val);
+}
+
+struct usbhs_priv *usbhs_pdev_to_priv(struct platform_device *pdev)
+{
+	return dev_get_drvdata(&pdev->dev);
+}
+
+/*
+ *		syscfg functions
+ */
+static void usbhs_sys_clock_ctrl(struct usbhs_priv *priv, int enable)
+{
+	usbhs_bset(priv, SYSCFG, SCKE, enable ? SCKE : 0);
+}
+
+void usbhs_sys_host_ctrl(struct usbhs_priv *priv, int enable)
+{
+	u16 mask = DCFM | DRPD | DPRPU | HSE | USBE;
+	u16 val  = DCFM | DRPD | HSE | USBE;
+	int has_otg = usbhs_get_dparam(priv, has_otg);
+
+	if (has_otg)
+		usbhs_bset(priv, DVSTCTR, (EXTLP | PWEN), (EXTLP | PWEN));
+
+	/*
+	 * if enable
+	 *
+	 * - select Host mode
+	 * - D+ Line/D- Line Pull-down
+	 */
+	usbhs_bset(priv, SYSCFG, mask, enable ? val : 0);
+}
+
+void usbhs_sys_function_ctrl(struct usbhs_priv *priv, int enable)
+{
+	u16 mask = DCFM | DRPD | DPRPU | HSE | USBE;
+	u16 val  = HSE | USBE;
+
+	/*
+	 * if enable
+	 *
+	 * - select Function mode
+	 * - D+ Line Pull-up is disabled
+	 *      When D+ Line Pull-up is enabled,
+	 *      calling usbhs_sys_function_pullup(,1)
+	 */
+	usbhs_bset(priv, SYSCFG, mask, enable ? val : 0);
+}
+
+void usbhs_sys_function_pullup(struct usbhs_priv *priv, int enable)
+{
+	usbhs_bset(priv, SYSCFG, DPRPU, enable ? DPRPU : 0);
+}
+
+void usbhs_sys_set_test_mode(struct usbhs_priv *priv, u16 mode)
+{
+	usbhs_write(priv, TESTMODE, mode);
+}
+
+/*
+ *		frame functions
+ */
+int usbhs_frame_get_num(struct usbhs_priv *priv)
+{
+	return usbhs_read(priv, FRMNUM) & FRNM_MASK;
+}
+
+/*
+ *		usb request functions
+ */
+void usbhs_usbreq_get_val(struct usbhs_priv *priv, struct usb_ctrlrequest *req)
+{
+	u16 val;
+
+	val = usbhs_read(priv, USBREQ);
+	req->bRequest		= (val >> 8) & 0xFF;
+	req->bRequestType	= (val >> 0) & 0xFF;
+
+	req->wValue	= usbhs_read(priv, USBVAL);
+	req->wIndex	= usbhs_read(priv, USBINDX);
+	req->wLength	= usbhs_read(priv, USBLENG);
+}
+
+void usbhs_usbreq_set_val(struct usbhs_priv *priv, struct usb_ctrlrequest *req)
+{
+	usbhs_write(priv, USBREQ,  (req->bRequest << 8) | req->bRequestType);
+	usbhs_write(priv, USBVAL,  req->wValue);
+	usbhs_write(priv, USBINDX, req->wIndex);
+	usbhs_write(priv, USBLENG, req->wLength);
+
+	usbhs_bset(priv, DCPCTR, SUREQ, SUREQ);
+}
+
+/*
+ *		bus/vbus functions
+ */
+void usbhs_bus_send_sof_enable(struct usbhs_priv *priv)
+{
+	u16 status = usbhs_read(priv, DVSTCTR) & (USBRST | UACT);
+
+	if (status != USBRST) {
+		struct device *dev = usbhs_priv_to_dev(priv);
+		dev_err(dev, "usbhs should be reset\n");
+	}
+
+	usbhs_bset(priv, DVSTCTR, (USBRST | UACT), UACT);
+}
+
+void usbhs_bus_send_reset(struct usbhs_priv *priv)
+{
+	usbhs_bset(priv, DVSTCTR, (USBRST | UACT), USBRST);
+}
+
+int usbhs_bus_get_speed(struct usbhs_priv *priv)
+{
+	u16 dvstctr = usbhs_read(priv, DVSTCTR);
+
+	switch (RHST & dvstctr) {
+	case RHST_LOW_SPEED:
+		return USB_SPEED_LOW;
+	case RHST_FULL_SPEED:
+		return USB_SPEED_FULL;
+	case RHST_HIGH_SPEED:
+		return USB_SPEED_HIGH;
+	}
+
+	return USB_SPEED_UNKNOWN;
+}
+
+int usbhs_vbus_ctrl(struct usbhs_priv *priv, int enable)
+{
+	struct platform_device *pdev = usbhs_priv_to_pdev(priv);
+
+	return usbhs_platform_call(priv, set_vbus, pdev, enable);
+}
+
+static void usbhsc_bus_init(struct usbhs_priv *priv)
+{
+	usbhs_write(priv, DVSTCTR, 0);
+
+	usbhs_vbus_ctrl(priv, 0);
+}
+
+/*
+ *		device configuration
+ */
+int usbhs_set_device_config(struct usbhs_priv *priv, int devnum,
+			   u16 upphub, u16 hubport, u16 speed)
+{
+	struct device *dev = usbhs_priv_to_dev(priv);
+	u16 usbspd = 0;
+	u32 reg = DEVADD0 + (2 * devnum);
+
+	if (devnum > 10) {
+		dev_err(dev, "cannot set speed to unknown device %d\n", devnum);
+		return -EIO;
+	}
+
+	if (upphub > 0xA) {
+		dev_err(dev, "unsupported hub number %d\n", upphub);
+		return -EIO;
+	}
+
+	switch (speed) {
+	case USB_SPEED_LOW:
+		usbspd = USBSPD_SPEED_LOW;
+		break;
+	case USB_SPEED_FULL:
+		usbspd = USBSPD_SPEED_FULL;
+		break;
+	case USB_SPEED_HIGH:
+		usbspd = USBSPD_SPEED_HIGH;
+		break;
+	default:
+		dev_err(dev, "unsupported speed %d\n", speed);
+		return -EIO;
+	}
+
+	usbhs_write(priv, reg,	UPPHUB(upphub)	|
+				HUBPORT(hubport)|
+				USBSPD(usbspd));
+
+	return 0;
+}
+
+/*
+ *		interrupt functions
+ */
+void usbhs_xxxsts_clear(struct usbhs_priv *priv, u16 sts_reg, u16 bit)
+{
+	u16 pipe_mask = (u16)GENMASK(usbhs_get_dparam(priv, pipe_size), 0);
+
+	usbhs_write(priv, sts_reg, ~(1 << bit) & pipe_mask);
+}
+
+/*
+ *		local functions
+ */
+static void usbhsc_set_buswait(struct usbhs_priv *priv)
+{
+	int wait = usbhs_get_dparam(priv, buswait_bwait);
+
+	/* set bus wait if platform have */
+	if (wait)
+		usbhs_bset(priv, BUSWAIT, 0x000F, wait);
+}
+
+/*
+ *		platform default param
+ */
+
+/* commonly used on old SH-Mobile SoCs */
+static struct renesas_usbhs_driver_pipe_config usbhsc_default_pipe[] = {
+	RENESAS_USBHS_PIPE(USB_ENDPOINT_XFER_CONTROL, 64, 0x00, false),
+	RENESAS_USBHS_PIPE(USB_ENDPOINT_XFER_ISOC, 1024, 0x08, false),
+	RENESAS_USBHS_PIPE(USB_ENDPOINT_XFER_ISOC, 1024, 0x18, false),
+	RENESAS_USBHS_PIPE(USB_ENDPOINT_XFER_BULK, 512, 0x28, true),
+	RENESAS_USBHS_PIPE(USB_ENDPOINT_XFER_BULK, 512, 0x38, true),
+	RENESAS_USBHS_PIPE(USB_ENDPOINT_XFER_BULK, 512, 0x48, true),
+	RENESAS_USBHS_PIPE(USB_ENDPOINT_XFER_INT, 64, 0x04, false),
+	RENESAS_USBHS_PIPE(USB_ENDPOINT_XFER_INT, 64, 0x05, false),
+	RENESAS_USBHS_PIPE(USB_ENDPOINT_XFER_INT, 64, 0x06, false),
+	RENESAS_USBHS_PIPE(USB_ENDPOINT_XFER_INT, 64, 0x07, false),
+};
+
+/* commonly used on newer SH-Mobile and R-Car SoCs */
+static struct renesas_usbhs_driver_pipe_config usbhsc_new_pipe[] = {
+	RENESAS_USBHS_PIPE(USB_ENDPOINT_XFER_CONTROL, 64, 0x00, false),
+	RENESAS_USBHS_PIPE(USB_ENDPOINT_XFER_ISOC, 1024, 0x08, true),
+	RENESAS_USBHS_PIPE(USB_ENDPOINT_XFER_ISOC, 1024, 0x28, true),
+	RENESAS_USBHS_PIPE(USB_ENDPOINT_XFER_BULK, 512, 0x48, true),
+	RENESAS_USBHS_PIPE(USB_ENDPOINT_XFER_BULK, 512, 0x58, true),
+	RENESAS_USBHS_PIPE(USB_ENDPOINT_XFER_BULK, 512, 0x68, true),
+	RENESAS_USBHS_PIPE(USB_ENDPOINT_XFER_INT, 64, 0x04, false),
+	RENESAS_USBHS_PIPE(USB_ENDPOINT_XFER_INT, 64, 0x05, false),
+	RENESAS_USBHS_PIPE(USB_ENDPOINT_XFER_INT, 64, 0x06, false),
+	RENESAS_USBHS_PIPE(USB_ENDPOINT_XFER_BULK, 512, 0x78, true),
+	RENESAS_USBHS_PIPE(USB_ENDPOINT_XFER_BULK, 512, 0x88, true),
+	RENESAS_USBHS_PIPE(USB_ENDPOINT_XFER_BULK, 512, 0x98, true),
+	RENESAS_USBHS_PIPE(USB_ENDPOINT_XFER_BULK, 512, 0xa8, true),
+	RENESAS_USBHS_PIPE(USB_ENDPOINT_XFER_BULK, 512, 0xb8, true),
+	RENESAS_USBHS_PIPE(USB_ENDPOINT_XFER_BULK, 512, 0xc8, true),
+	RENESAS_USBHS_PIPE(USB_ENDPOINT_XFER_BULK, 512, 0xd8, true),
+};
+
+/*
+ *		power control
+ */
+static void usbhsc_power_ctrl(struct usbhs_priv *priv, int enable)
+{
+	struct platform_device *pdev = usbhs_priv_to_pdev(priv);
+	struct device *dev = usbhs_priv_to_dev(priv);
+
+	if (enable) {
+		/* enable PM */
+		pm_runtime_get_sync(dev);
+
+		/* enable platform power */
+		usbhs_platform_call(priv, power_ctrl, pdev, priv->base, enable);
+
+		/* USB on */
+		usbhs_sys_clock_ctrl(priv, enable);
+	} else {
+		/* USB off */
+		usbhs_sys_clock_ctrl(priv, enable);
+
+		/* disable platform power */
+		usbhs_platform_call(priv, power_ctrl, pdev, priv->base, enable);
+
+		/* disable PM */
+		pm_runtime_put_sync(dev);
+	}
+}
+
+/*
+ *		hotplug
+ */
+static void usbhsc_hotplug(struct usbhs_priv *priv)
+{
+	struct platform_device *pdev = usbhs_priv_to_pdev(priv);
+	struct usbhs_mod *mod = usbhs_mod_get_current(priv);
+	int id;
+	int enable;
+	int cable;
+	int ret;
+
+	/*
+	 * get vbus status from platform
+	 */
+	enable = usbhs_platform_call(priv, get_vbus, pdev);
+
+	/*
+	 * get id from platform
+	 */
+	id = usbhs_platform_call(priv, get_id, pdev);
+
+	if (enable && !mod) {
+		if (priv->edev) {
+			cable = extcon_get_state(priv->edev, EXTCON_USB_HOST);
+			if ((cable > 0 && id != USBHS_HOST) ||
+			    (!cable && id != USBHS_GADGET)) {
+				dev_info(&pdev->dev,
+					 "USB cable plugged in doesn't match the selected role!\n");
+				return;
+			}
+		}
+
+		ret = usbhs_mod_change(priv, id);
+		if (ret < 0)
+			return;
+
+		dev_dbg(&pdev->dev, "%s enable\n", __func__);
+
+		/* power on */
+		if (usbhsc_flags_has(priv, USBHSF_RUNTIME_PWCTRL))
+			usbhsc_power_ctrl(priv, enable);
+
+		/* bus init */
+		usbhsc_set_buswait(priv);
+		usbhsc_bus_init(priv);
+
+		/* module start */
+		usbhs_mod_call(priv, start, priv);
+
+	} else if (!enable && mod) {
+		dev_dbg(&pdev->dev, "%s disable\n", __func__);
+
+		/* module stop */
+		usbhs_mod_call(priv, stop, priv);
+
+		/* bus init */
+		usbhsc_bus_init(priv);
+
+		/* power off */
+		if (usbhsc_flags_has(priv, USBHSF_RUNTIME_PWCTRL))
+			usbhsc_power_ctrl(priv, enable);
+
+		usbhs_mod_change(priv, -1);
+
+		/* reset phy for next connection */
+		usbhs_platform_call(priv, phy_reset, pdev);
+	}
+}
+
+/*
+ *		notify hotplug
+ */
+static void usbhsc_notify_hotplug(struct work_struct *work)
+{
+	struct usbhs_priv *priv = container_of(work,
+					       struct usbhs_priv,
+					       notify_hotplug_work.work);
+	usbhsc_hotplug(priv);
+}
+
+static int usbhsc_drvcllbck_notify_hotplug(struct platform_device *pdev)
+{
+	struct usbhs_priv *priv = usbhs_pdev_to_priv(pdev);
+	int delay = usbhs_get_dparam(priv, detection_delay);
+
+	/*
+	 * This functions will be called in interrupt.
+	 * To make sure safety context,
+	 * use workqueue for usbhs_notify_hotplug
+	 */
+	schedule_delayed_work(&priv->notify_hotplug_work,
+			      msecs_to_jiffies(delay));
+	return 0;
+}
+
+/*
+ *		platform functions
+ */
+static const struct of_device_id usbhs_of_match[] = {
+	{
+		.compatible = "renesas,usbhs-r8a7790",
+		.data = (void *)USBHS_TYPE_RCAR_GEN2,
+	},
+	{
+		.compatible = "renesas,usbhs-r8a7791",
+		.data = (void *)USBHS_TYPE_RCAR_GEN2,
+	},
+	{
+		.compatible = "renesas,usbhs-r8a7794",
+		.data = (void *)USBHS_TYPE_RCAR_GEN2,
+	},
+	{
+		.compatible = "renesas,usbhs-r8a7795",
+		.data = (void *)USBHS_TYPE_RCAR_GEN3,
+	},
+	{
+		.compatible = "renesas,usbhs-r8a7796",
+		.data = (void *)USBHS_TYPE_RCAR_GEN3,
+	},
+	{
+		.compatible = "renesas,usbhs-r8a77995",
+		.data = (void *)USBHS_TYPE_RCAR_GEN3_WITH_PLL,
+	},
+	{
+		.compatible = "renesas,rcar-gen2-usbhs",
+		.data = (void *)USBHS_TYPE_RCAR_GEN2,
+	},
+	{
+		.compatible = "renesas,rcar-gen3-usbhs",
+		.data = (void *)USBHS_TYPE_RCAR_GEN3,
+	},
+	{
+		.compatible = "renesas,rza1-usbhs",
+		.data = (void *)USBHS_TYPE_RZA1,
+	},
+	{ },
+};
+MODULE_DEVICE_TABLE(of, usbhs_of_match);
+
+static struct renesas_usbhs_platform_info *usbhs_parse_dt(struct device *dev)
+{
+	struct renesas_usbhs_platform_info *info;
+	struct renesas_usbhs_driver_param *dparam;
+	u32 tmp;
+	int gpio;
+
+	info = devm_kzalloc(dev, sizeof(*info), GFP_KERNEL);
+	if (!info)
+		return NULL;
+
+	dparam = &info->driver_param;
+	dparam->type = (uintptr_t)of_device_get_match_data(dev);
+	if (!of_property_read_u32(dev->of_node, "renesas,buswait", &tmp))
+		dparam->buswait_bwait = tmp;
+	gpio = of_get_named_gpio_flags(dev->of_node, "renesas,enable-gpio", 0,
+				       NULL);
+	if (gpio > 0)
+		dparam->enable_gpio = gpio;
+
+	if (dparam->type == USBHS_TYPE_RCAR_GEN2 ||
+	    dparam->type == USBHS_TYPE_RCAR_GEN3 ||
+	    dparam->type == USBHS_TYPE_RCAR_GEN3_WITH_PLL) {
+		dparam->has_usb_dmac = 1;
+		dparam->pipe_configs = usbhsc_new_pipe;
+		dparam->pipe_size = ARRAY_SIZE(usbhsc_new_pipe);
+	}
+
+	if (dparam->type == USBHS_TYPE_RZA1) {
+		dparam->pipe_configs = usbhsc_new_pipe;
+		dparam->pipe_size = ARRAY_SIZE(usbhsc_new_pipe);
+	}
+
+	return info;
+}
+
+static int usbhs_probe(struct platform_device *pdev)
+{
+	struct renesas_usbhs_platform_info *info = renesas_usbhs_get_info(pdev);
+	struct renesas_usbhs_driver_callback *dfunc;
+	struct usbhs_priv *priv;
+	struct resource *res, *irq_res;
+	int ret;
+
+	/* check device node */
+	if (pdev->dev.of_node)
+		info = pdev->dev.platform_data = usbhs_parse_dt(&pdev->dev);
+
+	/* check platform information */
+	if (!info) {
+		dev_err(&pdev->dev, "no platform information\n");
+		return -EINVAL;
+	}
+
+	/* platform data */
+	irq_res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
+	if (!irq_res) {
+		dev_err(&pdev->dev, "Not enough Renesas USB platform resources.\n");
+		return -ENODEV;
+	}
+
+	/* usb private data */
+	priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
+	if (!priv)
+		return -ENOMEM;
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	priv->base = devm_ioremap_resource(&pdev->dev, res);
+	if (IS_ERR(priv->base))
+		return PTR_ERR(priv->base);
+
+	if (of_property_read_bool(pdev->dev.of_node, "extcon")) {
+		priv->edev = extcon_get_edev_by_phandle(&pdev->dev, 0);
+		if (IS_ERR(priv->edev))
+			return PTR_ERR(priv->edev);
+	}
+
+	/*
+	 * care platform info
+	 */
+
+	memcpy(&priv->dparam,
+	       &info->driver_param,
+	       sizeof(struct renesas_usbhs_driver_param));
+
+	switch (priv->dparam.type) {
+	case USBHS_TYPE_RCAR_GEN2:
+		priv->pfunc = usbhs_rcar2_ops;
+		break;
+	case USBHS_TYPE_RCAR_GEN3:
+		priv->pfunc = usbhs_rcar3_ops;
+		break;
+	case USBHS_TYPE_RCAR_GEN3_WITH_PLL:
+		priv->pfunc = usbhs_rcar3_with_pll_ops;
+		if (!IS_ERR_OR_NULL(priv->edev)) {
+			priv->nb.notifier_call = priv->pfunc.notifier;
+			ret = devm_extcon_register_notifier(&pdev->dev,
+							    priv->edev,
+							    EXTCON_USB_HOST,
+							    &priv->nb);
+			if (ret < 0)
+				dev_err(&pdev->dev, "no notifier registered\n");
+		}
+		break;
+	case USBHS_TYPE_RZA1:
+		priv->pfunc = usbhs_rza1_ops;
+		break;
+	default:
+		if (!info->platform_callback.get_id) {
+			dev_err(&pdev->dev, "no platform callbacks");
+			return -EINVAL;
+		}
+		memcpy(&priv->pfunc,
+		       &info->platform_callback,
+		       sizeof(struct renesas_usbhs_platform_callback));
+		break;
+	}
+
+	/* set driver callback functions for platform */
+	dfunc			= &info->driver_callback;
+	dfunc->notify_hotplug	= usbhsc_drvcllbck_notify_hotplug;
+
+	/* set default param if platform doesn't have */
+	if (!priv->dparam.pipe_configs) {
+		priv->dparam.pipe_configs = usbhsc_default_pipe;
+		priv->dparam.pipe_size = ARRAY_SIZE(usbhsc_default_pipe);
+	}
+	if (!priv->dparam.pio_dma_border)
+		priv->dparam.pio_dma_border = 64; /* 64byte */
+
+	/* FIXME */
+	/* runtime power control ? */
+	if (priv->pfunc.get_vbus)
+		usbhsc_flags_set(priv, USBHSF_RUNTIME_PWCTRL);
+
+	/*
+	 * priv settings
+	 */
+	priv->irq	= irq_res->start;
+	if (irq_res->flags & IORESOURCE_IRQ_SHAREABLE)
+		priv->irqflags = IRQF_SHARED;
+	priv->pdev	= pdev;
+	INIT_DELAYED_WORK(&priv->notify_hotplug_work, usbhsc_notify_hotplug);
+	spin_lock_init(usbhs_priv_to_lock(priv));
+
+	/* call pipe and module init */
+	ret = usbhs_pipe_probe(priv);
+	if (ret < 0)
+		return ret;
+
+	ret = usbhs_fifo_probe(priv);
+	if (ret < 0)
+		goto probe_end_pipe_exit;
+
+	ret = usbhs_mod_probe(priv);
+	if (ret < 0)
+		goto probe_end_fifo_exit;
+
+	/* dev_set_drvdata should be called after usbhs_mod_init */
+	platform_set_drvdata(pdev, priv);
+
+	/*
+	 * deviece reset here because
+	 * USB device might be used in boot loader.
+	 */
+	usbhs_sys_clock_ctrl(priv, 0);
+
+	/* check GPIO determining if USB function should be enabled */
+	if (priv->dparam.enable_gpio) {
+		gpio_request_one(priv->dparam.enable_gpio, GPIOF_IN, NULL);
+		ret = !gpio_get_value(priv->dparam.enable_gpio);
+		gpio_free(priv->dparam.enable_gpio);
+		if (ret) {
+			dev_warn(&pdev->dev,
+				 "USB function not selected (GPIO %d)\n",
+				 priv->dparam.enable_gpio);
+			ret = -ENOTSUPP;
+			goto probe_end_mod_exit;
+		}
+	}
+
+	/*
+	 * platform call
+	 *
+	 * USB phy setup might depend on CPU/Board.
+	 * If platform has its callback functions,
+	 * call it here.
+	 */
+	ret = usbhs_platform_call(priv, hardware_init, pdev);
+	if (ret < 0) {
+		dev_err(&pdev->dev, "platform init failed.\n");
+		goto probe_end_mod_exit;
+	}
+
+	/* reset phy for connection */
+	usbhs_platform_call(priv, phy_reset, pdev);
+
+	/* power control */
+	pm_runtime_enable(&pdev->dev);
+	if (!usbhsc_flags_has(priv, USBHSF_RUNTIME_PWCTRL)) {
+		usbhsc_power_ctrl(priv, 1);
+		usbhs_mod_autonomy_mode(priv);
+	}
+
+	/*
+	 * manual call notify_hotplug for cold plug
+	 */
+	usbhsc_drvcllbck_notify_hotplug(pdev);
+
+	dev_info(&pdev->dev, "probed\n");
+
+	return ret;
+
+probe_end_mod_exit:
+	usbhs_mod_remove(priv);
+probe_end_fifo_exit:
+	usbhs_fifo_remove(priv);
+probe_end_pipe_exit:
+	usbhs_pipe_remove(priv);
+
+	dev_info(&pdev->dev, "probe failed (%d)\n", ret);
+
+	return ret;
+}
+
+static int usbhs_remove(struct platform_device *pdev)
+{
+	struct usbhs_priv *priv = usbhs_pdev_to_priv(pdev);
+	struct renesas_usbhs_platform_info *info = renesas_usbhs_get_info(pdev);
+	struct renesas_usbhs_driver_callback *dfunc = &info->driver_callback;
+
+	dev_dbg(&pdev->dev, "usb remove\n");
+
+	dfunc->notify_hotplug = NULL;
+
+	/* power off */
+	if (!usbhsc_flags_has(priv, USBHSF_RUNTIME_PWCTRL))
+		usbhsc_power_ctrl(priv, 0);
+
+	pm_runtime_disable(&pdev->dev);
+
+	usbhs_platform_call(priv, hardware_exit, pdev);
+	usbhs_mod_remove(priv);
+	usbhs_fifo_remove(priv);
+	usbhs_pipe_remove(priv);
+
+	return 0;
+}
+
+static int usbhsc_suspend(struct device *dev)
+{
+	struct usbhs_priv *priv = dev_get_drvdata(dev);
+	struct usbhs_mod *mod = usbhs_mod_get_current(priv);
+
+	if (mod) {
+		usbhs_mod_call(priv, stop, priv);
+		usbhs_mod_change(priv, -1);
+	}
+
+	if (mod || !usbhsc_flags_has(priv, USBHSF_RUNTIME_PWCTRL))
+		usbhsc_power_ctrl(priv, 0);
+
+	return 0;
+}
+
+static int usbhsc_resume(struct device *dev)
+{
+	struct usbhs_priv *priv = dev_get_drvdata(dev);
+	struct platform_device *pdev = usbhs_priv_to_pdev(priv);
+
+	if (!usbhsc_flags_has(priv, USBHSF_RUNTIME_PWCTRL)) {
+		usbhsc_power_ctrl(priv, 1);
+		usbhs_mod_autonomy_mode(priv);
+	}
+
+	usbhs_platform_call(priv, phy_reset, pdev);
+
+	usbhsc_drvcllbck_notify_hotplug(pdev);
+
+	return 0;
+}
+
+static int usbhsc_runtime_nop(struct device *dev)
+{
+	/* Runtime PM callback shared between ->runtime_suspend()
+	 * and ->runtime_resume(). Simply returns success.
+	 *
+	 * This driver re-initializes all registers after
+	 * pm_runtime_get_sync() anyway so there is no need
+	 * to save and restore registers here.
+	 */
+	return 0;
+}
+
+static const struct dev_pm_ops usbhsc_pm_ops = {
+	.suspend		= usbhsc_suspend,
+	.resume			= usbhsc_resume,
+	.runtime_suspend	= usbhsc_runtime_nop,
+	.runtime_resume		= usbhsc_runtime_nop,
+};
+
+static struct platform_driver renesas_usbhs_driver = {
+	.driver		= {
+		.name	= "renesas_usbhs",
+		.pm	= &usbhsc_pm_ops,
+		.of_match_table = of_match_ptr(usbhs_of_match),
+	},
+	.probe		= usbhs_probe,
+	.remove		= usbhs_remove,
+};
+
+module_platform_driver(renesas_usbhs_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Renesas USB driver");
+MODULE_AUTHOR("Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>");
diff --git a/drivers/usb/renesas_usbhs/common.h b/drivers/usb/renesas_usbhs/common.h
new file mode 100644
index 0000000..6137f79
--- /dev/null
+++ b/drivers/usb/renesas_usbhs/common.h
@@ -0,0 +1,339 @@
+// SPDX-License-Identifier: GPL-1.0+
+/*
+ * Renesas USB driver
+ *
+ * Copyright (C) 2011 Renesas Solutions Corp.
+ * Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
+ */
+#ifndef RENESAS_USB_DRIVER_H
+#define RENESAS_USB_DRIVER_H
+
+#include <linux/extcon.h>
+#include <linux/platform_device.h>
+#include <linux/usb/renesas_usbhs.h>
+
+struct usbhs_priv;
+
+#include "mod.h"
+#include "pipe.h"
+
+/*
+ *
+ *		register define
+ *
+ */
+#define SYSCFG		0x0000
+#define BUSWAIT		0x0002
+#define DVSTCTR		0x0008
+#define TESTMODE	0x000C
+#define CFIFO		0x0014
+#define CFIFOSEL	0x0020
+#define CFIFOCTR	0x0022
+#define D0FIFO		0x0100
+#define D0FIFOSEL	0x0028
+#define D0FIFOCTR	0x002A
+#define D1FIFO		0x0120
+#define D1FIFOSEL	0x002C
+#define D1FIFOCTR	0x002E
+#define INTENB0		0x0030
+#define INTENB1		0x0032
+#define BRDYENB		0x0036
+#define NRDYENB		0x0038
+#define BEMPENB		0x003A
+#define INTSTS0		0x0040
+#define INTSTS1		0x0042
+#define BRDYSTS		0x0046
+#define NRDYSTS		0x0048
+#define BEMPSTS		0x004A
+#define FRMNUM		0x004C
+#define USBREQ		0x0054	/* USB request type register */
+#define USBVAL		0x0056	/* USB request value register */
+#define USBINDX		0x0058	/* USB request index register */
+#define USBLENG		0x005A	/* USB request length register */
+#define DCPCFG		0x005C
+#define DCPMAXP		0x005E
+#define DCPCTR		0x0060
+#define PIPESEL		0x0064
+#define PIPECFG		0x0068
+#define PIPEBUF		0x006A
+#define PIPEMAXP	0x006C
+#define PIPEPERI	0x006E
+#define PIPEnCTR	0x0070
+#define PIPE1TRE	0x0090
+#define PIPE1TRN	0x0092
+#define PIPE2TRE	0x0094
+#define PIPE2TRN	0x0096
+#define PIPE3TRE	0x0098
+#define PIPE3TRN	0x009A
+#define PIPE4TRE	0x009C
+#define PIPE4TRN	0x009E
+#define PIPE5TRE	0x00A0
+#define PIPE5TRN	0x00A2
+#define PIPEBTRE	0x00A4
+#define PIPEBTRN	0x00A6
+#define PIPECTRE	0x00A8
+#define PIPECTRN	0x00AA
+#define PIPEDTRE	0x00AC
+#define PIPEDTRN	0x00AE
+#define PIPEETRE	0x00B0
+#define PIPEETRN	0x00B2
+#define PIPEFTRE	0x00B4
+#define PIPEFTRN	0x00B6
+#define PIPE9TRE	0x00B8
+#define PIPE9TRN	0x00BA
+#define PIPEATRE	0x00BC
+#define PIPEATRN	0x00BE
+#define DEVADD0		0x00D0 /* Device address n configuration */
+#define DEVADD1		0x00D2
+#define DEVADD2		0x00D4
+#define DEVADD3		0x00D6
+#define DEVADD4		0x00D8
+#define DEVADD5		0x00DA
+#define DEVADD6		0x00DC
+#define DEVADD7		0x00DE
+#define DEVADD8		0x00E0
+#define DEVADD9		0x00E2
+#define DEVADDA		0x00E4
+#define D2FIFOSEL	0x00F0	/* for R-Car Gen2 */
+#define D2FIFOCTR	0x00F2	/* for R-Car Gen2 */
+#define D3FIFOSEL	0x00F4	/* for R-Car Gen2 */
+#define D3FIFOCTR	0x00F6	/* for R-Car Gen2 */
+#define SUSPMODE	0x0102	/* for RZ/A */
+
+/* SYSCFG */
+#define SCKE	(1 << 10)	/* USB Module Clock Enable */
+#define HSE	(1 << 7)	/* High-Speed Operation Enable */
+#define DCFM	(1 << 6)	/* Controller Function Select */
+#define DRPD	(1 << 5)	/* D+ Line/D- Line Resistance Control */
+#define DPRPU	(1 << 4)	/* D+ Line Resistance Control */
+#define USBE	(1 << 0)	/* USB Module Operation Enable */
+#define UCKSEL	(1 << 2)	/* Clock Select for RZ/A1 */
+#define UPLLE	(1 << 1)	/* USB PLL Enable for RZ/A1 */
+
+/* DVSTCTR */
+#define EXTLP	(1 << 10)	/* Controls the EXTLP pin output state */
+#define PWEN	(1 << 9)	/* Controls the PWEN pin output state */
+#define USBRST	(1 << 6)	/* Bus Reset Output */
+#define UACT	(1 << 4)	/* USB Bus Enable */
+#define RHST	(0x7)		/* Reset Handshake */
+#define  RHST_LOW_SPEED  1	/* Low-speed connection */
+#define  RHST_FULL_SPEED 2	/* Full-speed connection */
+#define  RHST_HIGH_SPEED 3	/* High-speed connection */
+
+/* CFIFOSEL */
+#define DREQE	(1 << 12)	/* DMA Transfer Request Enable */
+#define MBW_32	(0x2 << 10)	/* CFIFO Port Access Bit Width */
+
+/* CFIFOCTR */
+#define BVAL	(1 << 15)	/* Buffer Memory Enable Flag */
+#define BCLR	(1 << 14)	/* CPU buffer clear */
+#define FRDY	(1 << 13)	/* FIFO Port Ready */
+#define DTLN_MASK (0x0FFF)	/* Receive Data Length */
+
+/* INTENB0 */
+#define VBSE	(1 << 15)	/* Enable IRQ VBUS_0 and VBUSIN_0 */
+#define RSME	(1 << 14)	/* Enable IRQ Resume */
+#define SOFE	(1 << 13)	/* Enable IRQ Frame Number Update */
+#define DVSE	(1 << 12)	/* Enable IRQ Device State Transition */
+#define CTRE	(1 << 11)	/* Enable IRQ Control Stage Transition */
+#define BEMPE	(1 << 10)	/* Enable IRQ Buffer Empty */
+#define NRDYE	(1 << 9)	/* Enable IRQ Buffer Not Ready Response */
+#define BRDYE	(1 << 8)	/* Enable IRQ Buffer Ready */
+
+/* INTENB1 */
+#define BCHGE	(1 << 14)	/* USB Bus Change Interrupt Enable */
+#define DTCHE	(1 << 12)	/* Disconnection Detect Interrupt Enable */
+#define ATTCHE	(1 << 11)	/* Connection Detect Interrupt Enable */
+#define EOFERRE	(1 << 6)	/* EOF Error Detect Interrupt Enable */
+#define SIGNE	(1 << 5)	/* Setup Transaction Error Interrupt Enable */
+#define SACKE	(1 << 4)	/* Setup Transaction ACK Interrupt Enable */
+
+/* INTSTS0 */
+#define VBINT	(1 << 15)	/* VBUS0_0 and VBUS1_0 Interrupt Status */
+#define DVST	(1 << 12)	/* Device State Transition Interrupt Status */
+#define CTRT	(1 << 11)	/* Control Stage Interrupt Status */
+#define BEMP	(1 << 10)	/* Buffer Empty Interrupt Status */
+#define BRDY	(1 << 8)	/* Buffer Ready Interrupt Status */
+#define VBSTS	(1 << 7)	/* VBUS_0 and VBUSIN_0 Input Status */
+#define VALID	(1 << 3)	/* USB Request Receive */
+
+#define DVSQ_MASK		(0x3 << 4)	/* Device State */
+#define  POWER_STATE		(0 << 4)
+#define  DEFAULT_STATE		(1 << 4)
+#define  ADDRESS_STATE		(2 << 4)
+#define  CONFIGURATION_STATE	(3 << 4)
+
+#define CTSQ_MASK		(0x7)	/* Control Transfer Stage */
+#define  IDLE_SETUP_STAGE	0	/* Idle stage or setup stage */
+#define  READ_DATA_STAGE	1	/* Control read data stage */
+#define  READ_STATUS_STAGE	2	/* Control read status stage */
+#define  WRITE_DATA_STAGE	3	/* Control write data stage */
+#define  WRITE_STATUS_STAGE	4	/* Control write status stage */
+#define  NODATA_STATUS_STAGE	5	/* Control write NoData status stage */
+#define  SEQUENCE_ERROR		6	/* Control transfer sequence error */
+
+/* INTSTS1 */
+#define OVRCR	(1 << 15) /* OVRCR Interrupt Status */
+#define BCHG	(1 << 14) /* USB Bus Change Interrupt Status */
+#define DTCH	(1 << 12) /* USB Disconnection Detect Interrupt Status */
+#define ATTCH	(1 << 11) /* ATTCH Interrupt Status */
+#define EOFERR	(1 << 6)  /* EOF Error Detect Interrupt Status */
+#define SIGN	(1 << 5)  /* Setup Transaction Error Interrupt Status */
+#define SACK	(1 << 4)  /* Setup Transaction ACK Response Interrupt Status */
+
+/* PIPECFG */
+/* DCPCFG */
+#define TYPE_NONE	(0 << 14)	/* Transfer Type */
+#define TYPE_BULK	(1 << 14)
+#define TYPE_INT	(2 << 14)
+#define TYPE_ISO	(3 << 14)
+#define BFRE		(1 << 10)	/* BRDY Interrupt Operation Spec. */
+#define DBLB		(1 << 9)	/* Double Buffer Mode */
+#define SHTNAK		(1 << 7)	/* Pipe Disable in Transfer End */
+#define DIR_OUT		(1 << 4)	/* Transfer Direction */
+
+/* PIPEMAXP */
+/* DCPMAXP */
+#define DEVSEL_MASK	(0xF << 12)	/* Device Select */
+#define DCP_MAXP_MASK	(0x7F)
+#define PIPE_MAXP_MASK	(0x7FF)
+
+/* PIPEBUF */
+#define BUFSIZE_SHIFT	10
+#define BUFSIZE_MASK	(0x1F << BUFSIZE_SHIFT)
+#define BUFNMB_MASK	(0xFF)
+
+/* PIPEnCTR */
+/* DCPCTR */
+#define BSTS		(1 << 15)	/* Buffer Status */
+#define SUREQ		(1 << 14)	/* Sending SETUP Token */
+#define CSSTS		(1 << 12)	/* CSSTS Status */
+#define	ACLRM		(1 << 9)	/* Buffer Auto-Clear Mode */
+#define SQCLR		(1 << 8)	/* Toggle Bit Clear */
+#define SQSET		(1 << 7)	/* Toggle Bit Set */
+#define SQMON		(1 << 6)	/* Toggle Bit Check */
+#define PBUSY		(1 << 5)	/* Pipe Busy */
+#define PID_MASK	(0x3)		/* Response PID */
+#define  PID_NAK	0
+#define  PID_BUF	1
+#define  PID_STALL10	2
+#define  PID_STALL11	3
+
+#define CCPL		(1 << 2)	/* Control Transfer End Enable */
+
+/* PIPEnTRE */
+#define TRENB		(1 << 9)	/* Transaction Counter Enable */
+#define TRCLR		(1 << 8)	/* Transaction Counter Clear */
+
+/* FRMNUM */
+#define FRNM_MASK	(0x7FF)
+
+/* DEVADDn */
+#define UPPHUB(x)	(((x) & 0xF) << 11)	/* HUB Register */
+#define HUBPORT(x)	(((x) & 0x7) << 8)	/* HUB Port for Target Device */
+#define USBSPD(x)	(((x) & 0x3) << 6)	/* Device Transfer Rate */
+#define USBSPD_SPEED_LOW	0x1
+#define USBSPD_SPEED_FULL	0x2
+#define USBSPD_SPEED_HIGH	0x3
+
+/* SUSPMODE */
+#define SUSPM		(1 << 14)	/* SuspendM Control */
+
+/*
+ *		struct
+ */
+struct usbhs_priv {
+
+	void __iomem *base;
+	unsigned int irq;
+	unsigned long irqflags;
+
+	struct renesas_usbhs_platform_callback	pfunc;
+	struct renesas_usbhs_driver_param	dparam;
+
+	struct delayed_work notify_hotplug_work;
+	struct platform_device *pdev;
+
+	struct extcon_dev *edev;
+	struct notifier_block nb;
+
+	spinlock_t		lock;
+
+	u32 flags;
+
+	/*
+	 * module control
+	 */
+	struct usbhs_mod_info mod_info;
+
+	/*
+	 * pipe control
+	 */
+	struct usbhs_pipe_info pipe_info;
+
+	/*
+	 * fifo control
+	 */
+	struct usbhs_fifo_info fifo_info;
+
+	struct phy *phy;
+};
+
+/*
+ * common
+ */
+u16 usbhs_read(struct usbhs_priv *priv, u32 reg);
+void usbhs_write(struct usbhs_priv *priv, u32 reg, u16 data);
+void usbhs_bset(struct usbhs_priv *priv, u32 reg, u16 mask, u16 data);
+
+#define usbhs_lock(p, f) spin_lock_irqsave(usbhs_priv_to_lock(p), f)
+#define usbhs_unlock(p, f) spin_unlock_irqrestore(usbhs_priv_to_lock(p), f)
+
+/*
+ * sysconfig
+ */
+void usbhs_sys_host_ctrl(struct usbhs_priv *priv, int enable);
+void usbhs_sys_function_ctrl(struct usbhs_priv *priv, int enable);
+void usbhs_sys_function_pullup(struct usbhs_priv *priv, int enable);
+void usbhs_sys_set_test_mode(struct usbhs_priv *priv, u16 mode);
+
+/*
+ * usb request
+ */
+void usbhs_usbreq_get_val(struct usbhs_priv *priv, struct usb_ctrlrequest *req);
+void usbhs_usbreq_set_val(struct usbhs_priv *priv, struct usb_ctrlrequest *req);
+
+/*
+ * bus
+ */
+void usbhs_bus_send_sof_enable(struct usbhs_priv *priv);
+void usbhs_bus_send_reset(struct usbhs_priv *priv);
+int usbhs_bus_get_speed(struct usbhs_priv *priv);
+int usbhs_vbus_ctrl(struct usbhs_priv *priv, int enable);
+
+/*
+ * frame
+ */
+int usbhs_frame_get_num(struct usbhs_priv *priv);
+
+/*
+ * device config
+ */
+int usbhs_set_device_config(struct usbhs_priv *priv, int devnum, u16 upphub,
+			   u16 hubport, u16 speed);
+
+/*
+ * interrupt functions
+ */
+void usbhs_xxxsts_clear(struct usbhs_priv *priv, u16 sts_reg, u16 bit);
+
+/*
+ * data
+ */
+struct usbhs_priv *usbhs_pdev_to_priv(struct platform_device *pdev);
+#define usbhs_get_dparam(priv, param)	(priv->dparam.param)
+#define usbhs_priv_to_pdev(priv)	(priv->pdev)
+#define usbhs_priv_to_dev(priv)		(&priv->pdev->dev)
+#define usbhs_priv_to_lock(priv)	(&priv->lock)
+
+#endif /* RENESAS_USB_DRIVER_H */
diff --git a/drivers/usb/renesas_usbhs/fifo.c b/drivers/usb/renesas_usbhs/fifo.c
new file mode 100644
index 0000000..39fa2fc
--- /dev/null
+++ b/drivers/usb/renesas_usbhs/fifo.c
@@ -0,0 +1,1461 @@
+// SPDX-License-Identifier: GPL-1.0+
+/*
+ * Renesas USB driver
+ *
+ * Copyright (C) 2011 Renesas Solutions Corp.
+ * Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
+ */
+#include <linux/delay.h>
+#include <linux/io.h>
+#include <linux/scatterlist.h>
+#include "common.h"
+#include "pipe.h"
+
+#define usbhsf_get_cfifo(p)	(&((p)->fifo_info.cfifo))
+#define usbhsf_is_cfifo(p, f)	(usbhsf_get_cfifo(p) == f)
+
+#define usbhsf_fifo_is_busy(f)	((f)->pipe) /* see usbhs_pipe_select_fifo */
+
+/*
+ *		packet initialize
+ */
+void usbhs_pkt_init(struct usbhs_pkt *pkt)
+{
+	INIT_LIST_HEAD(&pkt->node);
+}
+
+/*
+ *		packet control function
+ */
+static int usbhsf_null_handle(struct usbhs_pkt *pkt, int *is_done)
+{
+	struct usbhs_priv *priv = usbhs_pipe_to_priv(pkt->pipe);
+	struct device *dev = usbhs_priv_to_dev(priv);
+
+	dev_err(dev, "null handler\n");
+
+	return -EINVAL;
+}
+
+static const struct usbhs_pkt_handle usbhsf_null_handler = {
+	.prepare = usbhsf_null_handle,
+	.try_run = usbhsf_null_handle,
+};
+
+void usbhs_pkt_push(struct usbhs_pipe *pipe, struct usbhs_pkt *pkt,
+		    void (*done)(struct usbhs_priv *priv,
+				 struct usbhs_pkt *pkt),
+		    void *buf, int len, int zero, int sequence)
+{
+	struct usbhs_priv *priv = usbhs_pipe_to_priv(pipe);
+	struct device *dev = usbhs_priv_to_dev(priv);
+	unsigned long flags;
+
+	if (!done) {
+		dev_err(dev, "no done function\n");
+		return;
+	}
+
+	/********************  spin lock ********************/
+	usbhs_lock(priv, flags);
+
+	if (!pipe->handler) {
+		dev_err(dev, "no handler function\n");
+		pipe->handler = &usbhsf_null_handler;
+	}
+
+	list_move_tail(&pkt->node, &pipe->list);
+
+	/*
+	 * each pkt must hold own handler.
+	 * because handler might be changed by its situation.
+	 * dma handler -> pio handler.
+	 */
+	pkt->pipe	= pipe;
+	pkt->buf	= buf;
+	pkt->handler	= pipe->handler;
+	pkt->length	= len;
+	pkt->zero	= zero;
+	pkt->actual	= 0;
+	pkt->done	= done;
+	pkt->sequence	= sequence;
+
+	usbhs_unlock(priv, flags);
+	/********************  spin unlock ******************/
+}
+
+static void __usbhsf_pkt_del(struct usbhs_pkt *pkt)
+{
+	list_del_init(&pkt->node);
+}
+
+static struct usbhs_pkt *__usbhsf_pkt_get(struct usbhs_pipe *pipe)
+{
+	return list_first_entry_or_null(&pipe->list, struct usbhs_pkt, node);
+}
+
+static void usbhsf_fifo_unselect(struct usbhs_pipe *pipe,
+				 struct usbhs_fifo *fifo);
+static struct dma_chan *usbhsf_dma_chan_get(struct usbhs_fifo *fifo,
+					    struct usbhs_pkt *pkt);
+#define usbhsf_dma_map(p)	__usbhsf_dma_map_ctrl(p, 1)
+#define usbhsf_dma_unmap(p)	__usbhsf_dma_map_ctrl(p, 0)
+static int __usbhsf_dma_map_ctrl(struct usbhs_pkt *pkt, int map);
+struct usbhs_pkt *usbhs_pkt_pop(struct usbhs_pipe *pipe, struct usbhs_pkt *pkt)
+{
+	struct usbhs_priv *priv = usbhs_pipe_to_priv(pipe);
+	struct usbhs_fifo *fifo = usbhs_pipe_to_fifo(pipe);
+	unsigned long flags;
+
+	/********************  spin lock ********************/
+	usbhs_lock(priv, flags);
+
+	usbhs_pipe_disable(pipe);
+
+	if (!pkt)
+		pkt = __usbhsf_pkt_get(pipe);
+
+	if (pkt) {
+		struct dma_chan *chan = NULL;
+
+		if (fifo)
+			chan = usbhsf_dma_chan_get(fifo, pkt);
+		if (chan) {
+			dmaengine_terminate_all(chan);
+			usbhsf_dma_unmap(pkt);
+		}
+
+		usbhs_pipe_clear_without_sequence(pipe, 0, 0);
+
+		__usbhsf_pkt_del(pkt);
+	}
+
+	if (fifo)
+		usbhsf_fifo_unselect(pipe, fifo);
+
+	usbhs_unlock(priv, flags);
+	/********************  spin unlock ******************/
+
+	return pkt;
+}
+
+enum {
+	USBHSF_PKT_PREPARE,
+	USBHSF_PKT_TRY_RUN,
+	USBHSF_PKT_DMA_DONE,
+};
+
+static int usbhsf_pkt_handler(struct usbhs_pipe *pipe, int type)
+{
+	struct usbhs_priv *priv = usbhs_pipe_to_priv(pipe);
+	struct usbhs_pkt *pkt;
+	struct device *dev = usbhs_priv_to_dev(priv);
+	int (*func)(struct usbhs_pkt *pkt, int *is_done);
+	unsigned long flags;
+	int ret = 0;
+	int is_done = 0;
+
+	/********************  spin lock ********************/
+	usbhs_lock(priv, flags);
+
+	pkt = __usbhsf_pkt_get(pipe);
+	if (!pkt)
+		goto __usbhs_pkt_handler_end;
+
+	switch (type) {
+	case USBHSF_PKT_PREPARE:
+		func = pkt->handler->prepare;
+		break;
+	case USBHSF_PKT_TRY_RUN:
+		func = pkt->handler->try_run;
+		break;
+	case USBHSF_PKT_DMA_DONE:
+		func = pkt->handler->dma_done;
+		break;
+	default:
+		dev_err(dev, "unknown pkt handler\n");
+		goto __usbhs_pkt_handler_end;
+	}
+
+	if (likely(func))
+		ret = func(pkt, &is_done);
+
+	if (is_done)
+		__usbhsf_pkt_del(pkt);
+
+__usbhs_pkt_handler_end:
+	usbhs_unlock(priv, flags);
+	/********************  spin unlock ******************/
+
+	if (is_done) {
+		pkt->done(priv, pkt);
+		usbhs_pkt_start(pipe);
+	}
+
+	return ret;
+}
+
+void usbhs_pkt_start(struct usbhs_pipe *pipe)
+{
+	usbhsf_pkt_handler(pipe, USBHSF_PKT_PREPARE);
+}
+
+/*
+ *		irq enable/disable function
+ */
+#define usbhsf_irq_empty_ctrl(p, e) usbhsf_irq_callback_ctrl(p, irq_bempsts, e)
+#define usbhsf_irq_ready_ctrl(p, e) usbhsf_irq_callback_ctrl(p, irq_brdysts, e)
+#define usbhsf_irq_callback_ctrl(pipe, status, enable)			\
+	({								\
+		struct usbhs_priv *priv = usbhs_pipe_to_priv(pipe);	\
+		struct usbhs_mod *mod = usbhs_mod_get_current(priv);	\
+		u16 status = (1 << usbhs_pipe_number(pipe));		\
+		if (!mod)						\
+			return;						\
+		if (enable)						\
+			mod->status |= status;				\
+		else							\
+			mod->status &= ~status;				\
+		usbhs_irq_callback_update(priv, mod);			\
+	})
+
+static void usbhsf_tx_irq_ctrl(struct usbhs_pipe *pipe, int enable)
+{
+	/*
+	 * And DCP pipe can NOT use "ready interrupt" for "send"
+	 * it should use "empty" interrupt.
+	 * see
+	 *   "Operation" - "Interrupt Function" - "BRDY Interrupt"
+	 *
+	 * on the other hand, normal pipe can use "ready interrupt" for "send"
+	 * even though it is single/double buffer
+	 */
+	if (usbhs_pipe_is_dcp(pipe))
+		usbhsf_irq_empty_ctrl(pipe, enable);
+	else
+		usbhsf_irq_ready_ctrl(pipe, enable);
+}
+
+static void usbhsf_rx_irq_ctrl(struct usbhs_pipe *pipe, int enable)
+{
+	usbhsf_irq_ready_ctrl(pipe, enable);
+}
+
+/*
+ *		FIFO ctrl
+ */
+static void usbhsf_send_terminator(struct usbhs_pipe *pipe,
+				   struct usbhs_fifo *fifo)
+{
+	struct usbhs_priv *priv = usbhs_pipe_to_priv(pipe);
+
+	usbhs_bset(priv, fifo->ctr, BVAL, BVAL);
+}
+
+static int usbhsf_fifo_barrier(struct usbhs_priv *priv,
+			       struct usbhs_fifo *fifo)
+{
+	/* The FIFO port is accessible */
+	if (usbhs_read(priv, fifo->ctr) & FRDY)
+		return 0;
+
+	return -EBUSY;
+}
+
+static void usbhsf_fifo_clear(struct usbhs_pipe *pipe,
+			      struct usbhs_fifo *fifo)
+{
+	struct usbhs_priv *priv = usbhs_pipe_to_priv(pipe);
+	int ret = 0;
+
+	if (!usbhs_pipe_is_dcp(pipe)) {
+		/*
+		 * This driver checks the pipe condition first to avoid -EBUSY
+		 * from usbhsf_fifo_barrier() if the pipe is RX direction and
+		 * empty.
+		 */
+		if (usbhs_pipe_is_dir_in(pipe))
+			ret = usbhs_pipe_is_accessible(pipe);
+		if (!ret)
+			ret = usbhsf_fifo_barrier(priv, fifo);
+	}
+
+	/*
+	 * if non-DCP pipe, this driver should set BCLR when
+	 * usbhsf_fifo_barrier() returns 0.
+	 */
+	if (!ret)
+		usbhs_write(priv, fifo->ctr, BCLR);
+}
+
+static int usbhsf_fifo_rcv_len(struct usbhs_priv *priv,
+			       struct usbhs_fifo *fifo)
+{
+	return usbhs_read(priv, fifo->ctr) & DTLN_MASK;
+}
+
+static void usbhsf_fifo_unselect(struct usbhs_pipe *pipe,
+				 struct usbhs_fifo *fifo)
+{
+	struct usbhs_priv *priv = usbhs_pipe_to_priv(pipe);
+
+	usbhs_pipe_select_fifo(pipe, NULL);
+	usbhs_write(priv, fifo->sel, 0);
+}
+
+static int usbhsf_fifo_select(struct usbhs_pipe *pipe,
+			      struct usbhs_fifo *fifo,
+			      int write)
+{
+	struct usbhs_priv *priv = usbhs_pipe_to_priv(pipe);
+	struct device *dev = usbhs_priv_to_dev(priv);
+	int timeout = 1024;
+	u16 mask = ((1 << 5) | 0xF);		/* mask of ISEL | CURPIPE */
+	u16 base = usbhs_pipe_number(pipe);	/* CURPIPE */
+
+	if (usbhs_pipe_is_busy(pipe) ||
+	    usbhsf_fifo_is_busy(fifo))
+		return -EBUSY;
+
+	if (usbhs_pipe_is_dcp(pipe)) {
+		base |= (1 == write) << 5;	/* ISEL */
+
+		if (usbhs_mod_is_host(priv))
+			usbhs_dcp_dir_for_host(pipe, write);
+	}
+
+	/* "base" will be used below  */
+	if (usbhs_get_dparam(priv, has_sudmac) && !usbhsf_is_cfifo(priv, fifo))
+		usbhs_write(priv, fifo->sel, base);
+	else
+		usbhs_write(priv, fifo->sel, base | MBW_32);
+
+	/* check ISEL and CURPIPE value */
+	while (timeout--) {
+		if (base == (mask & usbhs_read(priv, fifo->sel))) {
+			usbhs_pipe_select_fifo(pipe, fifo);
+			return 0;
+		}
+		udelay(10);
+	}
+
+	dev_err(dev, "fifo select error\n");
+
+	return -EIO;
+}
+
+/*
+ *		DCP status stage
+ */
+static int usbhs_dcp_dir_switch_to_write(struct usbhs_pkt *pkt, int *is_done)
+{
+	struct usbhs_pipe *pipe = pkt->pipe;
+	struct usbhs_priv *priv = usbhs_pipe_to_priv(pipe);
+	struct usbhs_fifo *fifo = usbhsf_get_cfifo(priv); /* CFIFO */
+	struct device *dev = usbhs_priv_to_dev(priv);
+	int ret;
+
+	usbhs_pipe_disable(pipe);
+
+	ret = usbhsf_fifo_select(pipe, fifo, 1);
+	if (ret < 0) {
+		dev_err(dev, "%s() faile\n", __func__);
+		return ret;
+	}
+
+	usbhs_pipe_sequence_data1(pipe); /* DATA1 */
+
+	usbhsf_fifo_clear(pipe, fifo);
+	usbhsf_send_terminator(pipe, fifo);
+
+	usbhsf_fifo_unselect(pipe, fifo);
+
+	usbhsf_tx_irq_ctrl(pipe, 1);
+	usbhs_pipe_enable(pipe);
+
+	return ret;
+}
+
+static int usbhs_dcp_dir_switch_to_read(struct usbhs_pkt *pkt, int *is_done)
+{
+	struct usbhs_pipe *pipe = pkt->pipe;
+	struct usbhs_priv *priv = usbhs_pipe_to_priv(pipe);
+	struct usbhs_fifo *fifo = usbhsf_get_cfifo(priv); /* CFIFO */
+	struct device *dev = usbhs_priv_to_dev(priv);
+	int ret;
+
+	usbhs_pipe_disable(pipe);
+
+	ret = usbhsf_fifo_select(pipe, fifo, 0);
+	if (ret < 0) {
+		dev_err(dev, "%s() fail\n", __func__);
+		return ret;
+	}
+
+	usbhs_pipe_sequence_data1(pipe); /* DATA1 */
+	usbhsf_fifo_clear(pipe, fifo);
+
+	usbhsf_fifo_unselect(pipe, fifo);
+
+	usbhsf_rx_irq_ctrl(pipe, 1);
+	usbhs_pipe_enable(pipe);
+
+	return ret;
+
+}
+
+static int usbhs_dcp_dir_switch_done(struct usbhs_pkt *pkt, int *is_done)
+{
+	struct usbhs_pipe *pipe = pkt->pipe;
+
+	if (pkt->handler == &usbhs_dcp_status_stage_in_handler)
+		usbhsf_tx_irq_ctrl(pipe, 0);
+	else
+		usbhsf_rx_irq_ctrl(pipe, 0);
+
+	pkt->actual = pkt->length;
+	*is_done = 1;
+
+	return 0;
+}
+
+const struct usbhs_pkt_handle usbhs_dcp_status_stage_in_handler = {
+	.prepare = usbhs_dcp_dir_switch_to_write,
+	.try_run = usbhs_dcp_dir_switch_done,
+};
+
+const struct usbhs_pkt_handle usbhs_dcp_status_stage_out_handler = {
+	.prepare = usbhs_dcp_dir_switch_to_read,
+	.try_run = usbhs_dcp_dir_switch_done,
+};
+
+/*
+ *		DCP data stage (push)
+ */
+static int usbhsf_dcp_data_stage_try_push(struct usbhs_pkt *pkt, int *is_done)
+{
+	struct usbhs_pipe *pipe = pkt->pipe;
+
+	usbhs_pipe_sequence_data1(pipe); /* DATA1 */
+
+	/*
+	 * change handler to PIO push
+	 */
+	pkt->handler = &usbhs_fifo_pio_push_handler;
+
+	return pkt->handler->prepare(pkt, is_done);
+}
+
+const struct usbhs_pkt_handle usbhs_dcp_data_stage_out_handler = {
+	.prepare = usbhsf_dcp_data_stage_try_push,
+};
+
+/*
+ *		DCP data stage (pop)
+ */
+static int usbhsf_dcp_data_stage_prepare_pop(struct usbhs_pkt *pkt,
+					     int *is_done)
+{
+	struct usbhs_pipe *pipe = pkt->pipe;
+	struct usbhs_priv *priv = usbhs_pipe_to_priv(pipe);
+	struct usbhs_fifo *fifo = usbhsf_get_cfifo(priv);
+
+	if (usbhs_pipe_is_busy(pipe))
+		return 0;
+
+	/*
+	 * prepare pop for DCP should
+	 *  - change DCP direction,
+	 *  - clear fifo
+	 *  - DATA1
+	 */
+	usbhs_pipe_disable(pipe);
+
+	usbhs_pipe_sequence_data1(pipe); /* DATA1 */
+
+	usbhsf_fifo_select(pipe, fifo, 0);
+	usbhsf_fifo_clear(pipe, fifo);
+	usbhsf_fifo_unselect(pipe, fifo);
+
+	/*
+	 * change handler to PIO pop
+	 */
+	pkt->handler = &usbhs_fifo_pio_pop_handler;
+
+	return pkt->handler->prepare(pkt, is_done);
+}
+
+const struct usbhs_pkt_handle usbhs_dcp_data_stage_in_handler = {
+	.prepare = usbhsf_dcp_data_stage_prepare_pop,
+};
+
+/*
+ *		PIO push handler
+ */
+static int usbhsf_pio_try_push(struct usbhs_pkt *pkt, int *is_done)
+{
+	struct usbhs_pipe *pipe = pkt->pipe;
+	struct usbhs_priv *priv = usbhs_pipe_to_priv(pipe);
+	struct device *dev = usbhs_priv_to_dev(priv);
+	struct usbhs_fifo *fifo = usbhsf_get_cfifo(priv); /* CFIFO */
+	void __iomem *addr = priv->base + fifo->port;
+	u8 *buf;
+	int maxp = usbhs_pipe_get_maxpacket(pipe);
+	int total_len;
+	int i, ret, len;
+	int is_short;
+
+	usbhs_pipe_data_sequence(pipe, pkt->sequence);
+	pkt->sequence = -1; /* -1 sequence will be ignored */
+
+	usbhs_pipe_set_trans_count_if_bulk(pipe, pkt->length);
+
+	ret = usbhsf_fifo_select(pipe, fifo, 1);
+	if (ret < 0)
+		return 0;
+
+	ret = usbhs_pipe_is_accessible(pipe);
+	if (ret < 0) {
+		/* inaccessible pipe is not an error */
+		ret = 0;
+		goto usbhs_fifo_write_busy;
+	}
+
+	ret = usbhsf_fifo_barrier(priv, fifo);
+	if (ret < 0)
+		goto usbhs_fifo_write_busy;
+
+	buf		= pkt->buf    + pkt->actual;
+	len		= pkt->length - pkt->actual;
+	len		= min(len, maxp);
+	total_len	= len;
+	is_short	= total_len < maxp;
+
+	/*
+	 * FIXME
+	 *
+	 * 32-bit access only
+	 */
+	if (len >= 4 && !((unsigned long)buf & 0x03)) {
+		iowrite32_rep(addr, buf, len / 4);
+		len %= 4;
+		buf += total_len - len;
+	}
+
+	/* the rest operation */
+	for (i = 0; i < len; i++)
+		iowrite8(buf[i], addr + (0x03 - (i & 0x03)));
+
+	/*
+	 * variable update
+	 */
+	pkt->actual += total_len;
+
+	if (pkt->actual < pkt->length)
+		*is_done = 0;		/* there are remainder data */
+	else if (is_short)
+		*is_done = 1;		/* short packet */
+	else
+		*is_done = !pkt->zero;	/* send zero packet ? */
+
+	/*
+	 * pipe/irq handling
+	 */
+	if (is_short)
+		usbhsf_send_terminator(pipe, fifo);
+
+	usbhsf_tx_irq_ctrl(pipe, !*is_done);
+	usbhs_pipe_running(pipe, !*is_done);
+	usbhs_pipe_enable(pipe);
+
+	dev_dbg(dev, "  send %d (%d/ %d/ %d/ %d)\n",
+		usbhs_pipe_number(pipe),
+		pkt->length, pkt->actual, *is_done, pkt->zero);
+
+	usbhsf_fifo_unselect(pipe, fifo);
+
+	return 0;
+
+usbhs_fifo_write_busy:
+	usbhsf_fifo_unselect(pipe, fifo);
+
+	/*
+	 * pipe is busy.
+	 * retry in interrupt
+	 */
+	usbhsf_tx_irq_ctrl(pipe, 1);
+	usbhs_pipe_running(pipe, 1);
+
+	return ret;
+}
+
+static int usbhsf_pio_prepare_push(struct usbhs_pkt *pkt, int *is_done)
+{
+	if (usbhs_pipe_is_running(pkt->pipe))
+		return 0;
+
+	return usbhsf_pio_try_push(pkt, is_done);
+}
+
+const struct usbhs_pkt_handle usbhs_fifo_pio_push_handler = {
+	.prepare = usbhsf_pio_prepare_push,
+	.try_run = usbhsf_pio_try_push,
+};
+
+/*
+ *		PIO pop handler
+ */
+static int usbhsf_prepare_pop(struct usbhs_pkt *pkt, int *is_done)
+{
+	struct usbhs_pipe *pipe = pkt->pipe;
+	struct usbhs_priv *priv = usbhs_pipe_to_priv(pipe);
+	struct usbhs_fifo *fifo = usbhsf_get_cfifo(priv);
+
+	if (usbhs_pipe_is_busy(pipe))
+		return 0;
+
+	if (usbhs_pipe_is_running(pipe))
+		return 0;
+
+	/*
+	 * pipe enable to prepare packet receive
+	 */
+	usbhs_pipe_data_sequence(pipe, pkt->sequence);
+	pkt->sequence = -1; /* -1 sequence will be ignored */
+
+	if (usbhs_pipe_is_dcp(pipe))
+		usbhsf_fifo_clear(pipe, fifo);
+
+	usbhs_pipe_set_trans_count_if_bulk(pipe, pkt->length);
+	usbhs_pipe_enable(pipe);
+	usbhs_pipe_running(pipe, 1);
+	usbhsf_rx_irq_ctrl(pipe, 1);
+
+	return 0;
+}
+
+static int usbhsf_pio_try_pop(struct usbhs_pkt *pkt, int *is_done)
+{
+	struct usbhs_pipe *pipe = pkt->pipe;
+	struct usbhs_priv *priv = usbhs_pipe_to_priv(pipe);
+	struct device *dev = usbhs_priv_to_dev(priv);
+	struct usbhs_fifo *fifo = usbhsf_get_cfifo(priv); /* CFIFO */
+	void __iomem *addr = priv->base + fifo->port;
+	u8 *buf;
+	u32 data = 0;
+	int maxp = usbhs_pipe_get_maxpacket(pipe);
+	int rcv_len, len;
+	int i, ret;
+	int total_len = 0;
+
+	ret = usbhsf_fifo_select(pipe, fifo, 0);
+	if (ret < 0)
+		return 0;
+
+	ret = usbhsf_fifo_barrier(priv, fifo);
+	if (ret < 0)
+		goto usbhs_fifo_read_busy;
+
+	rcv_len = usbhsf_fifo_rcv_len(priv, fifo);
+
+	buf		= pkt->buf    + pkt->actual;
+	len		= pkt->length - pkt->actual;
+	len		= min(len, rcv_len);
+	total_len	= len;
+
+	/*
+	 * update actual length first here to decide disable pipe.
+	 * if this pipe keeps BUF status and all data were popped,
+	 * then, next interrupt/token will be issued again
+	 */
+	pkt->actual += total_len;
+
+	if ((pkt->actual == pkt->length) ||	/* receive all data */
+	    (total_len < maxp)) {		/* short packet */
+		*is_done = 1;
+		usbhsf_rx_irq_ctrl(pipe, 0);
+		usbhs_pipe_running(pipe, 0);
+		/*
+		 * If function mode, since this controller is possible to enter
+		 * Control Write status stage at this timing, this driver
+		 * should not disable the pipe. If such a case happens, this
+		 * controller is not able to complete the status stage.
+		 */
+		if (!usbhs_mod_is_host(priv) && !usbhs_pipe_is_dcp(pipe))
+			usbhs_pipe_disable(pipe);	/* disable pipe first */
+	}
+
+	/*
+	 * Buffer clear if Zero-Length packet
+	 *
+	 * see
+	 * "Operation" - "FIFO Buffer Memory" - "FIFO Port Function"
+	 */
+	if (0 == rcv_len) {
+		pkt->zero = 1;
+		usbhsf_fifo_clear(pipe, fifo);
+		goto usbhs_fifo_read_end;
+	}
+
+	/*
+	 * FIXME
+	 *
+	 * 32-bit access only
+	 */
+	if (len >= 4 && !((unsigned long)buf & 0x03)) {
+		ioread32_rep(addr, buf, len / 4);
+		len %= 4;
+		buf += total_len - len;
+	}
+
+	/* the rest operation */
+	for (i = 0; i < len; i++) {
+		if (!(i & 0x03))
+			data = ioread32(addr);
+
+		buf[i] = (data >> ((i & 0x03) * 8)) & 0xff;
+	}
+
+usbhs_fifo_read_end:
+	dev_dbg(dev, "  recv %d (%d/ %d/ %d/ %d)\n",
+		usbhs_pipe_number(pipe),
+		pkt->length, pkt->actual, *is_done, pkt->zero);
+
+usbhs_fifo_read_busy:
+	usbhsf_fifo_unselect(pipe, fifo);
+
+	return ret;
+}
+
+const struct usbhs_pkt_handle usbhs_fifo_pio_pop_handler = {
+	.prepare = usbhsf_prepare_pop,
+	.try_run = usbhsf_pio_try_pop,
+};
+
+/*
+ *		DCP ctrol statge handler
+ */
+static int usbhsf_ctrl_stage_end(struct usbhs_pkt *pkt, int *is_done)
+{
+	usbhs_dcp_control_transfer_done(pkt->pipe);
+
+	*is_done = 1;
+
+	return 0;
+}
+
+const struct usbhs_pkt_handle usbhs_ctrl_stage_end_handler = {
+	.prepare = usbhsf_ctrl_stage_end,
+	.try_run = usbhsf_ctrl_stage_end,
+};
+
+/*
+ *		DMA fifo functions
+ */
+static struct dma_chan *usbhsf_dma_chan_get(struct usbhs_fifo *fifo,
+					    struct usbhs_pkt *pkt)
+{
+	if (&usbhs_fifo_dma_push_handler == pkt->handler)
+		return fifo->tx_chan;
+
+	if (&usbhs_fifo_dma_pop_handler == pkt->handler)
+		return fifo->rx_chan;
+
+	return NULL;
+}
+
+static struct usbhs_fifo *usbhsf_get_dma_fifo(struct usbhs_priv *priv,
+					      struct usbhs_pkt *pkt)
+{
+	struct usbhs_fifo *fifo;
+	int i;
+
+	usbhs_for_each_dfifo(priv, fifo, i) {
+		if (usbhsf_dma_chan_get(fifo, pkt) &&
+		    !usbhsf_fifo_is_busy(fifo))
+			return fifo;
+	}
+
+	return NULL;
+}
+
+#define usbhsf_dma_start(p, f)	__usbhsf_dma_ctrl(p, f, DREQE)
+#define usbhsf_dma_stop(p, f)	__usbhsf_dma_ctrl(p, f, 0)
+static void __usbhsf_dma_ctrl(struct usbhs_pipe *pipe,
+			      struct usbhs_fifo *fifo,
+			      u16 dreqe)
+{
+	struct usbhs_priv *priv = usbhs_pipe_to_priv(pipe);
+
+	usbhs_bset(priv, fifo->sel, DREQE, dreqe);
+}
+
+static int __usbhsf_dma_map_ctrl(struct usbhs_pkt *pkt, int map)
+{
+	struct usbhs_pipe *pipe = pkt->pipe;
+	struct usbhs_priv *priv = usbhs_pipe_to_priv(pipe);
+	struct usbhs_pipe_info *info = usbhs_priv_to_pipeinfo(priv);
+	struct usbhs_fifo *fifo = usbhs_pipe_to_fifo(pipe);
+	struct dma_chan *chan = usbhsf_dma_chan_get(fifo, pkt);
+
+	return info->dma_map_ctrl(chan->device->dev, pkt, map);
+}
+
+static void usbhsf_dma_complete(void *arg);
+static void xfer_work(struct work_struct *work)
+{
+	struct usbhs_pkt *pkt = container_of(work, struct usbhs_pkt, work);
+	struct usbhs_pipe *pipe = pkt->pipe;
+	struct usbhs_fifo *fifo;
+	struct usbhs_priv *priv = usbhs_pipe_to_priv(pipe);
+	struct dma_async_tx_descriptor *desc;
+	struct dma_chan *chan;
+	struct device *dev = usbhs_priv_to_dev(priv);
+	enum dma_transfer_direction dir;
+	unsigned long flags;
+
+	usbhs_lock(priv, flags);
+	fifo = usbhs_pipe_to_fifo(pipe);
+	if (!fifo)
+		goto xfer_work_end;
+
+	chan = usbhsf_dma_chan_get(fifo, pkt);
+	dir = usbhs_pipe_is_dir_in(pipe) ? DMA_DEV_TO_MEM : DMA_MEM_TO_DEV;
+
+	desc = dmaengine_prep_slave_single(chan, pkt->dma + pkt->actual,
+					pkt->trans, dir,
+					DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
+	if (!desc)
+		goto xfer_work_end;
+
+	desc->callback		= usbhsf_dma_complete;
+	desc->callback_param	= pipe;
+
+	pkt->cookie = dmaengine_submit(desc);
+	if (pkt->cookie < 0) {
+		dev_err(dev, "Failed to submit dma descriptor\n");
+		goto xfer_work_end;
+	}
+
+	dev_dbg(dev, "  %s %d (%d/ %d)\n",
+		fifo->name, usbhs_pipe_number(pipe), pkt->length, pkt->zero);
+
+	usbhs_pipe_running(pipe, 1);
+	usbhs_pipe_set_trans_count_if_bulk(pipe, pkt->trans);
+	dma_async_issue_pending(chan);
+	usbhsf_dma_start(pipe, fifo);
+	usbhs_pipe_enable(pipe);
+
+xfer_work_end:
+	usbhs_unlock(priv, flags);
+}
+
+/*
+ *		DMA push handler
+ */
+static int usbhsf_dma_prepare_push(struct usbhs_pkt *pkt, int *is_done)
+{
+	struct usbhs_pipe *pipe = pkt->pipe;
+	struct usbhs_priv *priv = usbhs_pipe_to_priv(pipe);
+	struct usbhs_fifo *fifo;
+	int len = pkt->length - pkt->actual;
+	int ret;
+	uintptr_t align_mask;
+
+	if (usbhs_pipe_is_busy(pipe))
+		return 0;
+
+	/* use PIO if packet is less than pio_dma_border or pipe is DCP */
+	if ((len < usbhs_get_dparam(priv, pio_dma_border)) ||
+	    usbhs_pipe_type_is(pipe, USB_ENDPOINT_XFER_ISOC))
+		goto usbhsf_pio_prepare_push;
+
+	/* check data length if this driver don't use USB-DMAC */
+	if (!usbhs_get_dparam(priv, has_usb_dmac) && len & 0x7)
+		goto usbhsf_pio_prepare_push;
+
+	/* check buffer alignment */
+	align_mask = usbhs_get_dparam(priv, has_usb_dmac) ?
+					USBHS_USB_DMAC_XFER_SIZE - 1 : 0x7;
+	if ((uintptr_t)(pkt->buf + pkt->actual) & align_mask)
+		goto usbhsf_pio_prepare_push;
+
+	/* return at this time if the pipe is running */
+	if (usbhs_pipe_is_running(pipe))
+		return 0;
+
+	/* get enable DMA fifo */
+	fifo = usbhsf_get_dma_fifo(priv, pkt);
+	if (!fifo)
+		goto usbhsf_pio_prepare_push;
+
+	ret = usbhsf_fifo_select(pipe, fifo, 0);
+	if (ret < 0)
+		goto usbhsf_pio_prepare_push;
+
+	if (usbhsf_dma_map(pkt) < 0)
+		goto usbhsf_pio_prepare_push_unselect;
+
+	pkt->trans = len;
+
+	usbhsf_tx_irq_ctrl(pipe, 0);
+	INIT_WORK(&pkt->work, xfer_work);
+	schedule_work(&pkt->work);
+
+	return 0;
+
+usbhsf_pio_prepare_push_unselect:
+	usbhsf_fifo_unselect(pipe, fifo);
+usbhsf_pio_prepare_push:
+	/*
+	 * change handler to PIO
+	 */
+	pkt->handler = &usbhs_fifo_pio_push_handler;
+
+	return pkt->handler->prepare(pkt, is_done);
+}
+
+static int usbhsf_dma_push_done(struct usbhs_pkt *pkt, int *is_done)
+{
+	struct usbhs_pipe *pipe = pkt->pipe;
+	int is_short = pkt->trans % usbhs_pipe_get_maxpacket(pipe);
+
+	pkt->actual += pkt->trans;
+
+	if (pkt->actual < pkt->length)
+		*is_done = 0;		/* there are remainder data */
+	else if (is_short)
+		*is_done = 1;		/* short packet */
+	else
+		*is_done = !pkt->zero;	/* send zero packet? */
+
+	usbhs_pipe_running(pipe, !*is_done);
+
+	usbhsf_dma_stop(pipe, pipe->fifo);
+	usbhsf_dma_unmap(pkt);
+	usbhsf_fifo_unselect(pipe, pipe->fifo);
+
+	if (!*is_done) {
+		/* change handler to PIO */
+		pkt->handler = &usbhs_fifo_pio_push_handler;
+		return pkt->handler->try_run(pkt, is_done);
+	}
+
+	return 0;
+}
+
+const struct usbhs_pkt_handle usbhs_fifo_dma_push_handler = {
+	.prepare	= usbhsf_dma_prepare_push,
+	.dma_done	= usbhsf_dma_push_done,
+};
+
+/*
+ *		DMA pop handler
+ */
+
+static int usbhsf_dma_prepare_pop_with_rx_irq(struct usbhs_pkt *pkt,
+					      int *is_done)
+{
+	return usbhsf_prepare_pop(pkt, is_done);
+}
+
+static int usbhsf_dma_prepare_pop_with_usb_dmac(struct usbhs_pkt *pkt,
+						int *is_done)
+{
+	struct usbhs_pipe *pipe = pkt->pipe;
+	struct usbhs_priv *priv = usbhs_pipe_to_priv(pipe);
+	struct usbhs_fifo *fifo;
+	int ret;
+
+	if (usbhs_pipe_is_busy(pipe))
+		return 0;
+
+	/* use PIO if packet is less than pio_dma_border or pipe is DCP */
+	if ((pkt->length < usbhs_get_dparam(priv, pio_dma_border)) ||
+	    usbhs_pipe_type_is(pipe, USB_ENDPOINT_XFER_ISOC))
+		goto usbhsf_pio_prepare_pop;
+
+	fifo = usbhsf_get_dma_fifo(priv, pkt);
+	if (!fifo)
+		goto usbhsf_pio_prepare_pop;
+
+	if ((uintptr_t)pkt->buf & (USBHS_USB_DMAC_XFER_SIZE - 1))
+		goto usbhsf_pio_prepare_pop;
+
+	/* return at this time if the pipe is running */
+	if (usbhs_pipe_is_running(pipe))
+		return 0;
+
+	usbhs_pipe_config_change_bfre(pipe, 1);
+
+	ret = usbhsf_fifo_select(pipe, fifo, 0);
+	if (ret < 0)
+		goto usbhsf_pio_prepare_pop;
+
+	if (usbhsf_dma_map(pkt) < 0)
+		goto usbhsf_pio_prepare_pop_unselect;
+
+	/* DMA */
+
+	/*
+	 * usbhs_fifo_dma_pop_handler :: prepare
+	 * enabled irq to come here.
+	 * but it is no longer needed for DMA. disable it.
+	 */
+	usbhsf_rx_irq_ctrl(pipe, 0);
+
+	pkt->trans = pkt->length;
+
+	INIT_WORK(&pkt->work, xfer_work);
+	schedule_work(&pkt->work);
+
+	return 0;
+
+usbhsf_pio_prepare_pop_unselect:
+	usbhsf_fifo_unselect(pipe, fifo);
+usbhsf_pio_prepare_pop:
+
+	/*
+	 * change handler to PIO
+	 */
+	pkt->handler = &usbhs_fifo_pio_pop_handler;
+	usbhs_pipe_config_change_bfre(pipe, 0);
+
+	return pkt->handler->prepare(pkt, is_done);
+}
+
+static int usbhsf_dma_prepare_pop(struct usbhs_pkt *pkt, int *is_done)
+{
+	struct usbhs_priv *priv = usbhs_pipe_to_priv(pkt->pipe);
+
+	if (usbhs_get_dparam(priv, has_usb_dmac))
+		return usbhsf_dma_prepare_pop_with_usb_dmac(pkt, is_done);
+	else
+		return usbhsf_dma_prepare_pop_with_rx_irq(pkt, is_done);
+}
+
+static int usbhsf_dma_try_pop_with_rx_irq(struct usbhs_pkt *pkt, int *is_done)
+{
+	struct usbhs_pipe *pipe = pkt->pipe;
+	struct usbhs_priv *priv = usbhs_pipe_to_priv(pipe);
+	struct usbhs_fifo *fifo;
+	int len, ret;
+
+	if (usbhs_pipe_is_busy(pipe))
+		return 0;
+
+	if (usbhs_pipe_is_dcp(pipe))
+		goto usbhsf_pio_prepare_pop;
+
+	/* get enable DMA fifo */
+	fifo = usbhsf_get_dma_fifo(priv, pkt);
+	if (!fifo)
+		goto usbhsf_pio_prepare_pop;
+
+	if ((uintptr_t)(pkt->buf + pkt->actual) & 0x7) /* 8byte alignment */
+		goto usbhsf_pio_prepare_pop;
+
+	ret = usbhsf_fifo_select(pipe, fifo, 0);
+	if (ret < 0)
+		goto usbhsf_pio_prepare_pop;
+
+	/* use PIO if packet is less than pio_dma_border */
+	len = usbhsf_fifo_rcv_len(priv, fifo);
+	len = min(pkt->length - pkt->actual, len);
+	if (len & 0x7) /* 8byte alignment */
+		goto usbhsf_pio_prepare_pop_unselect;
+
+	if (len < usbhs_get_dparam(priv, pio_dma_border))
+		goto usbhsf_pio_prepare_pop_unselect;
+
+	ret = usbhsf_fifo_barrier(priv, fifo);
+	if (ret < 0)
+		goto usbhsf_pio_prepare_pop_unselect;
+
+	if (usbhsf_dma_map(pkt) < 0)
+		goto usbhsf_pio_prepare_pop_unselect;
+
+	/* DMA */
+
+	/*
+	 * usbhs_fifo_dma_pop_handler :: prepare
+	 * enabled irq to come here.
+	 * but it is no longer needed for DMA. disable it.
+	 */
+	usbhsf_rx_irq_ctrl(pipe, 0);
+
+	pkt->trans = len;
+
+	INIT_WORK(&pkt->work, xfer_work);
+	schedule_work(&pkt->work);
+
+	return 0;
+
+usbhsf_pio_prepare_pop_unselect:
+	usbhsf_fifo_unselect(pipe, fifo);
+usbhsf_pio_prepare_pop:
+
+	/*
+	 * change handler to PIO
+	 */
+	pkt->handler = &usbhs_fifo_pio_pop_handler;
+
+	return pkt->handler->try_run(pkt, is_done);
+}
+
+static int usbhsf_dma_try_pop(struct usbhs_pkt *pkt, int *is_done)
+{
+	struct usbhs_priv *priv = usbhs_pipe_to_priv(pkt->pipe);
+
+	BUG_ON(usbhs_get_dparam(priv, has_usb_dmac));
+
+	return usbhsf_dma_try_pop_with_rx_irq(pkt, is_done);
+}
+
+static int usbhsf_dma_pop_done_with_rx_irq(struct usbhs_pkt *pkt, int *is_done)
+{
+	struct usbhs_pipe *pipe = pkt->pipe;
+	int maxp = usbhs_pipe_get_maxpacket(pipe);
+
+	usbhsf_dma_stop(pipe, pipe->fifo);
+	usbhsf_dma_unmap(pkt);
+	usbhsf_fifo_unselect(pipe, pipe->fifo);
+
+	pkt->actual += pkt->trans;
+
+	if ((pkt->actual == pkt->length) ||	/* receive all data */
+	    (pkt->trans < maxp)) {		/* short packet */
+		*is_done = 1;
+		usbhs_pipe_running(pipe, 0);
+	} else {
+		/* re-enable */
+		usbhs_pipe_running(pipe, 0);
+		usbhsf_prepare_pop(pkt, is_done);
+	}
+
+	return 0;
+}
+
+static size_t usbhs_dma_calc_received_size(struct usbhs_pkt *pkt,
+					   struct dma_chan *chan, int dtln)
+{
+	struct usbhs_pipe *pipe = pkt->pipe;
+	struct dma_tx_state state;
+	size_t received_size;
+	int maxp = usbhs_pipe_get_maxpacket(pipe);
+
+	dmaengine_tx_status(chan, pkt->cookie, &state);
+	received_size = pkt->length - state.residue;
+
+	if (dtln) {
+		received_size -= USBHS_USB_DMAC_XFER_SIZE;
+		received_size &= ~(maxp - 1);
+		received_size += dtln;
+	}
+
+	return received_size;
+}
+
+static int usbhsf_dma_pop_done_with_usb_dmac(struct usbhs_pkt *pkt,
+					     int *is_done)
+{
+	struct usbhs_pipe *pipe = pkt->pipe;
+	struct usbhs_priv *priv = usbhs_pipe_to_priv(pipe);
+	struct usbhs_fifo *fifo = usbhs_pipe_to_fifo(pipe);
+	struct dma_chan *chan = usbhsf_dma_chan_get(fifo, pkt);
+	int rcv_len;
+
+	/*
+	 * Since the driver disables rx_irq in DMA mode, the interrupt handler
+	 * cannot the BRDYSTS. So, the function clears it here because the
+	 * driver may use PIO mode next time.
+	 */
+	usbhs_xxxsts_clear(priv, BRDYSTS, usbhs_pipe_number(pipe));
+
+	rcv_len = usbhsf_fifo_rcv_len(priv, fifo);
+	usbhsf_fifo_clear(pipe, fifo);
+	pkt->actual = usbhs_dma_calc_received_size(pkt, chan, rcv_len);
+
+	usbhs_pipe_running(pipe, 0);
+	usbhsf_dma_stop(pipe, fifo);
+	usbhsf_dma_unmap(pkt);
+	usbhsf_fifo_unselect(pipe, pipe->fifo);
+
+	/* The driver can assume the rx transaction is always "done" */
+	*is_done = 1;
+
+	return 0;
+}
+
+static int usbhsf_dma_pop_done(struct usbhs_pkt *pkt, int *is_done)
+{
+	struct usbhs_priv *priv = usbhs_pipe_to_priv(pkt->pipe);
+
+	if (usbhs_get_dparam(priv, has_usb_dmac))
+		return usbhsf_dma_pop_done_with_usb_dmac(pkt, is_done);
+	else
+		return usbhsf_dma_pop_done_with_rx_irq(pkt, is_done);
+}
+
+const struct usbhs_pkt_handle usbhs_fifo_dma_pop_handler = {
+	.prepare	= usbhsf_dma_prepare_pop,
+	.try_run	= usbhsf_dma_try_pop,
+	.dma_done	= usbhsf_dma_pop_done
+};
+
+/*
+ *		DMA setting
+ */
+static bool usbhsf_dma_filter(struct dma_chan *chan, void *param)
+{
+	struct sh_dmae_slave *slave = param;
+
+	/*
+	 * FIXME
+	 *
+	 * usbhs doesn't recognize id = 0 as valid DMA
+	 */
+	if (0 == slave->shdma_slave.slave_id)
+		return false;
+
+	chan->private = slave;
+
+	return true;
+}
+
+static void usbhsf_dma_quit(struct usbhs_priv *priv, struct usbhs_fifo *fifo)
+{
+	if (fifo->tx_chan)
+		dma_release_channel(fifo->tx_chan);
+	if (fifo->rx_chan)
+		dma_release_channel(fifo->rx_chan);
+
+	fifo->tx_chan = NULL;
+	fifo->rx_chan = NULL;
+}
+
+static void usbhsf_dma_init_pdev(struct usbhs_fifo *fifo)
+{
+	dma_cap_mask_t mask;
+
+	dma_cap_zero(mask);
+	dma_cap_set(DMA_SLAVE, mask);
+	fifo->tx_chan = dma_request_channel(mask, usbhsf_dma_filter,
+					    &fifo->tx_slave);
+
+	dma_cap_zero(mask);
+	dma_cap_set(DMA_SLAVE, mask);
+	fifo->rx_chan = dma_request_channel(mask, usbhsf_dma_filter,
+					    &fifo->rx_slave);
+}
+
+static void usbhsf_dma_init_dt(struct device *dev, struct usbhs_fifo *fifo,
+			       int channel)
+{
+	char name[16];
+
+	/*
+	 * To avoid complex handing for DnFIFOs, the driver uses each
+	 * DnFIFO as TX or RX direction (not bi-direction).
+	 * So, the driver uses odd channels for TX, even channels for RX.
+	 */
+	snprintf(name, sizeof(name), "ch%d", channel);
+	if (channel & 1) {
+		fifo->tx_chan = dma_request_slave_channel_reason(dev, name);
+		if (IS_ERR(fifo->tx_chan))
+			fifo->tx_chan = NULL;
+	} else {
+		fifo->rx_chan = dma_request_slave_channel_reason(dev, name);
+		if (IS_ERR(fifo->rx_chan))
+			fifo->rx_chan = NULL;
+	}
+}
+
+static void usbhsf_dma_init(struct usbhs_priv *priv, struct usbhs_fifo *fifo,
+			    int channel)
+{
+	struct device *dev = usbhs_priv_to_dev(priv);
+
+	if (dev->of_node)
+		usbhsf_dma_init_dt(dev, fifo, channel);
+	else
+		usbhsf_dma_init_pdev(fifo);
+
+	if (fifo->tx_chan || fifo->rx_chan)
+		dev_dbg(dev, "enable DMAEngine (%s%s%s)\n",
+			 fifo->name,
+			 fifo->tx_chan ? "[TX]" : "    ",
+			 fifo->rx_chan ? "[RX]" : "    ");
+}
+
+/*
+ *		irq functions
+ */
+static int usbhsf_irq_empty(struct usbhs_priv *priv,
+			    struct usbhs_irq_state *irq_state)
+{
+	struct usbhs_pipe *pipe;
+	struct device *dev = usbhs_priv_to_dev(priv);
+	int i, ret;
+
+	if (!irq_state->bempsts) {
+		dev_err(dev, "debug %s !!\n", __func__);
+		return -EIO;
+	}
+
+	dev_dbg(dev, "irq empty [0x%04x]\n", irq_state->bempsts);
+
+	/*
+	 * search interrupted "pipe"
+	 * not "uep".
+	 */
+	usbhs_for_each_pipe_with_dcp(pipe, priv, i) {
+		if (!(irq_state->bempsts & (1 << i)))
+			continue;
+
+		ret = usbhsf_pkt_handler(pipe, USBHSF_PKT_TRY_RUN);
+		if (ret < 0)
+			dev_err(dev, "irq_empty run_error %d : %d\n", i, ret);
+	}
+
+	return 0;
+}
+
+static int usbhsf_irq_ready(struct usbhs_priv *priv,
+			    struct usbhs_irq_state *irq_state)
+{
+	struct usbhs_pipe *pipe;
+	struct device *dev = usbhs_priv_to_dev(priv);
+	int i, ret;
+
+	if (!irq_state->brdysts) {
+		dev_err(dev, "debug %s !!\n", __func__);
+		return -EIO;
+	}
+
+	dev_dbg(dev, "irq ready [0x%04x]\n", irq_state->brdysts);
+
+	/*
+	 * search interrupted "pipe"
+	 * not "uep".
+	 */
+	usbhs_for_each_pipe_with_dcp(pipe, priv, i) {
+		if (!(irq_state->brdysts & (1 << i)))
+			continue;
+
+		ret = usbhsf_pkt_handler(pipe, USBHSF_PKT_TRY_RUN);
+		if (ret < 0)
+			dev_err(dev, "irq_ready run_error %d : %d\n", i, ret);
+	}
+
+	return 0;
+}
+
+static void usbhsf_dma_complete(void *arg)
+{
+	struct usbhs_pipe *pipe = arg;
+	struct usbhs_priv *priv = usbhs_pipe_to_priv(pipe);
+	struct device *dev = usbhs_priv_to_dev(priv);
+	int ret;
+
+	ret = usbhsf_pkt_handler(pipe, USBHSF_PKT_DMA_DONE);
+	if (ret < 0)
+		dev_err(dev, "dma_complete run_error %d : %d\n",
+			usbhs_pipe_number(pipe), ret);
+}
+
+void usbhs_fifo_clear_dcp(struct usbhs_pipe *pipe)
+{
+	struct usbhs_priv *priv = usbhs_pipe_to_priv(pipe);
+	struct usbhs_fifo *fifo = usbhsf_get_cfifo(priv); /* CFIFO */
+
+	/* clear DCP FIFO of transmission */
+	if (usbhsf_fifo_select(pipe, fifo, 1) < 0)
+		return;
+	usbhsf_fifo_clear(pipe, fifo);
+	usbhsf_fifo_unselect(pipe, fifo);
+
+	/* clear DCP FIFO of reception */
+	if (usbhsf_fifo_select(pipe, fifo, 0) < 0)
+		return;
+	usbhsf_fifo_clear(pipe, fifo);
+	usbhsf_fifo_unselect(pipe, fifo);
+}
+
+/*
+ *		fifo init
+ */
+void usbhs_fifo_init(struct usbhs_priv *priv)
+{
+	struct usbhs_mod *mod = usbhs_mod_get_current(priv);
+	struct usbhs_fifo *cfifo = usbhsf_get_cfifo(priv);
+	struct usbhs_fifo *dfifo;
+	int i;
+
+	mod->irq_empty		= usbhsf_irq_empty;
+	mod->irq_ready		= usbhsf_irq_ready;
+	mod->irq_bempsts	= 0;
+	mod->irq_brdysts	= 0;
+
+	cfifo->pipe	= NULL;
+	usbhs_for_each_dfifo(priv, dfifo, i)
+		dfifo->pipe	= NULL;
+}
+
+void usbhs_fifo_quit(struct usbhs_priv *priv)
+{
+	struct usbhs_mod *mod = usbhs_mod_get_current(priv);
+
+	mod->irq_empty		= NULL;
+	mod->irq_ready		= NULL;
+	mod->irq_bempsts	= 0;
+	mod->irq_brdysts	= 0;
+}
+
+#define __USBHS_DFIFO_INIT(priv, fifo, channel, fifo_port)		\
+do {									\
+	fifo = usbhsf_get_dnfifo(priv, channel);			\
+	fifo->name	= "D"#channel"FIFO";				\
+	fifo->port	= fifo_port;					\
+	fifo->sel	= D##channel##FIFOSEL;				\
+	fifo->ctr	= D##channel##FIFOCTR;				\
+	fifo->tx_slave.shdma_slave.slave_id =				\
+			usbhs_get_dparam(priv, d##channel##_tx_id);	\
+	fifo->rx_slave.shdma_slave.slave_id =				\
+			usbhs_get_dparam(priv, d##channel##_rx_id);	\
+	usbhsf_dma_init(priv, fifo, channel);				\
+} while (0)
+
+#define USBHS_DFIFO_INIT(priv, fifo, channel)				\
+		__USBHS_DFIFO_INIT(priv, fifo, channel, D##channel##FIFO)
+#define USBHS_DFIFO_INIT_NO_PORT(priv, fifo, channel)			\
+		__USBHS_DFIFO_INIT(priv, fifo, channel, 0)
+
+int usbhs_fifo_probe(struct usbhs_priv *priv)
+{
+	struct usbhs_fifo *fifo;
+
+	/* CFIFO */
+	fifo = usbhsf_get_cfifo(priv);
+	fifo->name	= "CFIFO";
+	fifo->port	= CFIFO;
+	fifo->sel	= CFIFOSEL;
+	fifo->ctr	= CFIFOCTR;
+
+	/* DFIFO */
+	USBHS_DFIFO_INIT(priv, fifo, 0);
+	USBHS_DFIFO_INIT(priv, fifo, 1);
+	USBHS_DFIFO_INIT_NO_PORT(priv, fifo, 2);
+	USBHS_DFIFO_INIT_NO_PORT(priv, fifo, 3);
+
+	return 0;
+}
+
+void usbhs_fifo_remove(struct usbhs_priv *priv)
+{
+	struct usbhs_fifo *fifo;
+	int i;
+
+	usbhs_for_each_dfifo(priv, fifo, i)
+		usbhsf_dma_quit(priv, fifo);
+}
diff --git a/drivers/usb/renesas_usbhs/fifo.h b/drivers/usb/renesas_usbhs/fifo.h
new file mode 100644
index 0000000..88d1816
--- /dev/null
+++ b/drivers/usb/renesas_usbhs/fifo.h
@@ -0,0 +1,101 @@
+// SPDX-License-Identifier: GPL-1.0+
+/*
+ * Renesas USB driver
+ *
+ * Copyright (C) 2011 Renesas Solutions Corp.
+ * Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
+ */
+#ifndef RENESAS_USB_FIFO_H
+#define RENESAS_USB_FIFO_H
+
+#include <linux/interrupt.h>
+#include <linux/sh_dma.h>
+#include <linux/workqueue.h>
+#include <asm/dma.h>
+#include "pipe.h"
+
+struct usbhs_fifo {
+	char *name;
+	u32 port;	/* xFIFO */
+	u32 sel;	/* xFIFOSEL */
+	u32 ctr;	/* xFIFOCTR */
+
+	struct usbhs_pipe	*pipe;
+
+	struct dma_chan		*tx_chan;
+	struct dma_chan		*rx_chan;
+
+	struct sh_dmae_slave	tx_slave;
+	struct sh_dmae_slave	rx_slave;
+};
+
+#define USBHS_MAX_NUM_DFIFO	4
+struct usbhs_fifo_info {
+	struct usbhs_fifo cfifo;
+	struct usbhs_fifo dfifo[USBHS_MAX_NUM_DFIFO];
+};
+#define usbhsf_get_dnfifo(p, n)	(&((p)->fifo_info.dfifo[n]))
+#define usbhs_for_each_dfifo(priv, dfifo, i)			\
+	for ((i) = 0;						\
+	     ((i) < USBHS_MAX_NUM_DFIFO) &&			\
+		     ((dfifo) = usbhsf_get_dnfifo(priv, (i)));	\
+	     (i)++)
+
+struct usbhs_pkt_handle;
+struct usbhs_pkt {
+	struct list_head node;
+	struct usbhs_pipe *pipe;
+	const struct usbhs_pkt_handle *handler;
+	void (*done)(struct usbhs_priv *priv,
+		     struct usbhs_pkt *pkt);
+	struct work_struct work;
+	dma_addr_t dma;
+	dma_cookie_t cookie;
+	void *buf;
+	int length;
+	int trans;
+	int actual;
+	int zero;
+	int sequence;
+};
+
+struct usbhs_pkt_handle {
+	int (*prepare)(struct usbhs_pkt *pkt, int *is_done);
+	int (*try_run)(struct usbhs_pkt *pkt, int *is_done);
+	int (*dma_done)(struct usbhs_pkt *pkt, int *is_done);
+};
+
+/*
+ * fifo
+ */
+int usbhs_fifo_probe(struct usbhs_priv *priv);
+void usbhs_fifo_remove(struct usbhs_priv *priv);
+void usbhs_fifo_init(struct usbhs_priv *priv);
+void usbhs_fifo_quit(struct usbhs_priv *priv);
+void usbhs_fifo_clear_dcp(struct usbhs_pipe *pipe);
+
+/*
+ * packet info
+ */
+extern const struct usbhs_pkt_handle usbhs_fifo_pio_push_handler;
+extern const struct usbhs_pkt_handle usbhs_fifo_pio_pop_handler;
+extern const struct usbhs_pkt_handle usbhs_ctrl_stage_end_handler;
+
+extern const struct usbhs_pkt_handle usbhs_fifo_dma_push_handler;
+extern const struct usbhs_pkt_handle usbhs_fifo_dma_pop_handler;
+
+extern const struct usbhs_pkt_handle usbhs_dcp_status_stage_in_handler;
+extern const struct usbhs_pkt_handle usbhs_dcp_status_stage_out_handler;
+
+extern const struct usbhs_pkt_handle usbhs_dcp_data_stage_in_handler;
+extern const struct usbhs_pkt_handle usbhs_dcp_data_stage_out_handler;
+
+void usbhs_pkt_init(struct usbhs_pkt *pkt);
+void usbhs_pkt_push(struct usbhs_pipe *pipe, struct usbhs_pkt *pkt,
+		    void (*done)(struct usbhs_priv *priv,
+				 struct usbhs_pkt *pkt),
+		    void *buf, int len, int zero, int sequence);
+struct usbhs_pkt *usbhs_pkt_pop(struct usbhs_pipe *pipe, struct usbhs_pkt *pkt);
+void usbhs_pkt_start(struct usbhs_pipe *pipe);
+
+#endif /* RENESAS_USB_FIFO_H */
diff --git a/drivers/usb/renesas_usbhs/mod.c b/drivers/usb/renesas_usbhs/mod.c
new file mode 100644
index 0000000..7475c4f
--- /dev/null
+++ b/drivers/usb/renesas_usbhs/mod.c
@@ -0,0 +1,399 @@
+// SPDX-License-Identifier: GPL-1.0+
+/*
+ * Renesas USB driver
+ *
+ * Copyright (C) 2011 Renesas Solutions Corp.
+ * Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
+ */
+#include <linux/interrupt.h>
+
+#include "common.h"
+#include "mod.h"
+
+#define usbhs_priv_to_modinfo(priv) (&priv->mod_info)
+#define usbhs_mod_info_call(priv, func, param...)	\
+({						\
+	struct usbhs_mod_info *info;		\
+	info = usbhs_priv_to_modinfo(priv);	\
+	!info->func ? 0 :			\
+	 info->func(param);			\
+})
+
+/*
+ *		autonomy
+ *
+ * these functions are used if platform doesn't have external phy.
+ *  -> there is no "notify_hotplug" callback from platform
+ *  -> call "notify_hotplug" by itself
+ *  -> use own interrupt to connect/disconnect
+ *  -> it mean module clock is always ON
+ *             ~~~~~~~~~~~~~~~~~~~~~~~~~
+ */
+static int usbhsm_autonomy_get_vbus(struct platform_device *pdev)
+{
+	struct usbhs_priv *priv = usbhs_pdev_to_priv(pdev);
+
+	return  VBSTS & usbhs_read(priv, INTSTS0);
+}
+
+static int usbhsm_autonomy_irq_vbus(struct usbhs_priv *priv,
+				    struct usbhs_irq_state *irq_state)
+{
+	struct platform_device *pdev = usbhs_priv_to_pdev(priv);
+
+	renesas_usbhs_call_notify_hotplug(pdev);
+
+	return 0;
+}
+
+void usbhs_mod_autonomy_mode(struct usbhs_priv *priv)
+{
+	struct usbhs_mod_info *info = usbhs_priv_to_modinfo(priv);
+
+	info->irq_vbus		= usbhsm_autonomy_irq_vbus;
+	priv->pfunc.get_vbus	= usbhsm_autonomy_get_vbus;
+
+	usbhs_irq_callback_update(priv, NULL);
+}
+
+/*
+ *		host / gadget functions
+ *
+ * renesas_usbhs host/gadget can register itself by below functions.
+ * these functions are called when probe
+ *
+ */
+void usbhs_mod_register(struct usbhs_priv *priv, struct usbhs_mod *mod, int id)
+{
+	struct usbhs_mod_info *info = usbhs_priv_to_modinfo(priv);
+
+	info->mod[id]	= mod;
+	mod->priv	= priv;
+}
+
+struct usbhs_mod *usbhs_mod_get(struct usbhs_priv *priv, int id)
+{
+	struct usbhs_mod_info *info = usbhs_priv_to_modinfo(priv);
+	struct usbhs_mod *ret = NULL;
+
+	switch (id) {
+	case USBHS_HOST:
+	case USBHS_GADGET:
+		ret = info->mod[id];
+		break;
+	}
+
+	return ret;
+}
+
+int usbhs_mod_is_host(struct usbhs_priv *priv)
+{
+	struct usbhs_mod *mod = usbhs_mod_get_current(priv);
+	struct usbhs_mod_info *info = usbhs_priv_to_modinfo(priv);
+
+	if (!mod)
+		return -EINVAL;
+
+	return info->mod[USBHS_HOST] == mod;
+}
+
+struct usbhs_mod *usbhs_mod_get_current(struct usbhs_priv *priv)
+{
+	struct usbhs_mod_info *info = usbhs_priv_to_modinfo(priv);
+
+	return info->curt;
+}
+
+int usbhs_mod_change(struct usbhs_priv *priv, int id)
+{
+	struct usbhs_mod_info *info = usbhs_priv_to_modinfo(priv);
+	struct usbhs_mod *mod = NULL;
+	int ret = 0;
+
+	/* id < 0 mean no current */
+	switch (id) {
+	case USBHS_HOST:
+	case USBHS_GADGET:
+		mod = info->mod[id];
+		break;
+	default:
+		ret = -EINVAL;
+	}
+	info->curt = mod;
+
+	return ret;
+}
+
+static irqreturn_t usbhs_interrupt(int irq, void *data);
+int usbhs_mod_probe(struct usbhs_priv *priv)
+{
+	struct device *dev = usbhs_priv_to_dev(priv);
+	int ret;
+
+	/*
+	 * install host/gadget driver
+	 */
+	ret = usbhs_mod_host_probe(priv);
+	if (ret < 0)
+		return ret;
+
+	ret = usbhs_mod_gadget_probe(priv);
+	if (ret < 0)
+		goto mod_init_host_err;
+
+	/* irq settings */
+	ret = devm_request_irq(dev, priv->irq, usbhs_interrupt,
+			  priv->irqflags, dev_name(dev), priv);
+	if (ret) {
+		dev_err(dev, "irq request err\n");
+		goto mod_init_gadget_err;
+	}
+
+	return ret;
+
+mod_init_gadget_err:
+	usbhs_mod_gadget_remove(priv);
+mod_init_host_err:
+	usbhs_mod_host_remove(priv);
+
+	return ret;
+}
+
+void usbhs_mod_remove(struct usbhs_priv *priv)
+{
+	usbhs_mod_host_remove(priv);
+	usbhs_mod_gadget_remove(priv);
+}
+
+/*
+ *		status functions
+ */
+int usbhs_status_get_device_state(struct usbhs_irq_state *irq_state)
+{
+	int state = irq_state->intsts0 & DVSQ_MASK;
+
+	switch (state) {
+	case POWER_STATE:
+	case DEFAULT_STATE:
+	case ADDRESS_STATE:
+	case CONFIGURATION_STATE:
+		return state;
+	}
+
+	return -EIO;
+}
+
+int usbhs_status_get_ctrl_stage(struct usbhs_irq_state *irq_state)
+{
+	/*
+	 * return value
+	 *
+	 * IDLE_SETUP_STAGE
+	 * READ_DATA_STAGE
+	 * READ_STATUS_STAGE
+	 * WRITE_DATA_STAGE
+	 * WRITE_STATUS_STAGE
+	 * NODATA_STATUS_STAGE
+	 * SEQUENCE_ERROR
+	 */
+	return (int)irq_state->intsts0 & CTSQ_MASK;
+}
+
+static int usbhs_status_get_each_irq(struct usbhs_priv *priv,
+				     struct usbhs_irq_state *state)
+{
+	struct usbhs_mod *mod = usbhs_mod_get_current(priv);
+	u16 intenb0, intenb1;
+	unsigned long flags;
+
+	/********************  spin lock ********************/
+	usbhs_lock(priv, flags);
+	state->intsts0 = usbhs_read(priv, INTSTS0);
+	intenb0 = usbhs_read(priv, INTENB0);
+
+	if (usbhs_mod_is_host(priv)) {
+		state->intsts1 = usbhs_read(priv, INTSTS1);
+		intenb1 = usbhs_read(priv, INTENB1);
+	} else {
+		state->intsts1 = intenb1 = 0;
+	}
+
+	/* mask */
+	if (mod) {
+		state->brdysts = usbhs_read(priv, BRDYSTS);
+		state->nrdysts = usbhs_read(priv, NRDYSTS);
+		state->bempsts = usbhs_read(priv, BEMPSTS);
+
+		state->bempsts &= mod->irq_bempsts;
+		state->brdysts &= mod->irq_brdysts;
+	}
+	usbhs_unlock(priv, flags);
+	/********************  spin unlock ******************/
+
+	/*
+	 * Check whether the irq enable registers and the irq status are set
+	 * when IRQF_SHARED is set.
+	 */
+	if (priv->irqflags & IRQF_SHARED) {
+		if (!(intenb0 & state->intsts0) &&
+		    !(intenb1 & state->intsts1) &&
+		    !(state->bempsts) &&
+		    !(state->brdysts))
+			return -EIO;
+	}
+
+	return 0;
+}
+
+/*
+ *		interrupt
+ */
+#define INTSTS0_MAGIC 0xF800 /* acknowledge magical interrupt sources */
+#define INTSTS1_MAGIC 0xA870 /* acknowledge magical interrupt sources */
+static irqreturn_t usbhs_interrupt(int irq, void *data)
+{
+	struct usbhs_priv *priv = data;
+	struct usbhs_irq_state irq_state;
+
+	if (usbhs_status_get_each_irq(priv, &irq_state) < 0)
+		return IRQ_NONE;
+
+	/*
+	 * clear interrupt
+	 *
+	 * The hardware is _very_ picky to clear interrupt bit.
+	 * Especially INTSTS0_MAGIC, INTSTS1_MAGIC value.
+	 *
+	 * see
+	 *	"Operation"
+	 *	 - "Control Transfer (DCP)"
+	 *	   - Function :: VALID bit should 0
+	 */
+	usbhs_write(priv, INTSTS0, ~irq_state.intsts0 & INTSTS0_MAGIC);
+	if (usbhs_mod_is_host(priv))
+		usbhs_write(priv, INTSTS1, ~irq_state.intsts1 & INTSTS1_MAGIC);
+
+	/*
+	 * The driver should not clear the xxxSTS after the line of
+	 * "call irq callback functions" because each "if" statement is
+	 * possible to call the callback function for avoiding any side effects.
+	 */
+	if (irq_state.intsts0 & BRDY)
+		usbhs_write(priv, BRDYSTS, ~irq_state.brdysts);
+	usbhs_write(priv, NRDYSTS, ~irq_state.nrdysts);
+	if (irq_state.intsts0 & BEMP)
+		usbhs_write(priv, BEMPSTS, ~irq_state.bempsts);
+
+	/*
+	 * call irq callback functions
+	 * see also
+	 *	usbhs_irq_setting_update
+	 */
+
+	/* INTSTS0 */
+	if (irq_state.intsts0 & VBINT)
+		usbhs_mod_info_call(priv, irq_vbus, priv, &irq_state);
+
+	if (irq_state.intsts0 & DVST)
+		usbhs_mod_call(priv, irq_dev_state, priv, &irq_state);
+
+	if (irq_state.intsts0 & CTRT)
+		usbhs_mod_call(priv, irq_ctrl_stage, priv, &irq_state);
+
+	if (irq_state.intsts0 & BEMP)
+		usbhs_mod_call(priv, irq_empty, priv, &irq_state);
+
+	if (irq_state.intsts0 & BRDY)
+		usbhs_mod_call(priv, irq_ready, priv, &irq_state);
+
+	if (usbhs_mod_is_host(priv)) {
+		/* INTSTS1 */
+		if (irq_state.intsts1 & ATTCH)
+			usbhs_mod_call(priv, irq_attch, priv, &irq_state);
+
+		if (irq_state.intsts1 & DTCH)
+			usbhs_mod_call(priv, irq_dtch, priv, &irq_state);
+
+		if (irq_state.intsts1 & SIGN)
+			usbhs_mod_call(priv, irq_sign, priv, &irq_state);
+
+		if (irq_state.intsts1 & SACK)
+			usbhs_mod_call(priv, irq_sack, priv, &irq_state);
+	}
+	return IRQ_HANDLED;
+}
+
+void usbhs_irq_callback_update(struct usbhs_priv *priv, struct usbhs_mod *mod)
+{
+	u16 intenb0 = 0;
+	u16 intenb1 = 0;
+	struct usbhs_mod_info *info = usbhs_priv_to_modinfo(priv);
+
+	/*
+	 * BEMPENB/BRDYENB are picky.
+	 * below method is required
+	 *
+	 *  - clear  INTSTS0
+	 *  - update BEMPENB/BRDYENB
+	 *  - update INTSTS0
+	 */
+	usbhs_write(priv, INTENB0, 0);
+	if (usbhs_mod_is_host(priv))
+		usbhs_write(priv, INTENB1, 0);
+
+	usbhs_write(priv, BEMPENB, 0);
+	usbhs_write(priv, BRDYENB, 0);
+
+	/*
+	 * see also
+	 *	usbhs_interrupt
+	 */
+
+	/*
+	 * it don't enable DVSE (intenb0) here
+	 * but "mod->irq_dev_state" will be called.
+	 */
+	if (info->irq_vbus)
+		intenb0 |= VBSE;
+
+	if (mod) {
+		/*
+		 * INTSTS0
+		 */
+		if (mod->irq_ctrl_stage)
+			intenb0 |= CTRE;
+
+		if (mod->irq_empty && mod->irq_bempsts) {
+			usbhs_write(priv, BEMPENB, mod->irq_bempsts);
+			intenb0 |= BEMPE;
+		}
+
+		if (mod->irq_ready && mod->irq_brdysts) {
+			usbhs_write(priv, BRDYENB, mod->irq_brdysts);
+			intenb0 |= BRDYE;
+		}
+
+		if (usbhs_mod_is_host(priv)) {
+			/*
+			 * INTSTS1
+			 */
+			if (mod->irq_attch)
+				intenb1 |= ATTCHE;
+
+			if (mod->irq_dtch)
+				intenb1 |= DTCHE;
+
+			if (mod->irq_sign)
+				intenb1 |= SIGNE;
+
+			if (mod->irq_sack)
+				intenb1 |= SACKE;
+		}
+	}
+
+	if (intenb0)
+		usbhs_write(priv, INTENB0, intenb0);
+
+	if (usbhs_mod_is_host(priv) && intenb1)
+		usbhs_write(priv, INTENB1, intenb1);
+}
diff --git a/drivers/usb/renesas_usbhs/mod.h b/drivers/usb/renesas_usbhs/mod.h
new file mode 100644
index 0000000..a4a61d6
--- /dev/null
+++ b/drivers/usb/renesas_usbhs/mod.h
@@ -0,0 +1,163 @@
+// SPDX-License-Identifier: GPL-1.0+
+/*
+ * Renesas USB driver
+ *
+ * Copyright (C) 2011 Renesas Solutions Corp.
+ * Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
+ */
+#ifndef RENESAS_USB_MOD_H
+#define RENESAS_USB_MOD_H
+
+#include <linux/spinlock.h>
+#include <linux/usb/renesas_usbhs.h>
+#include "common.h"
+
+/*
+ *	struct
+ */
+struct usbhs_irq_state {
+	u16 intsts0;
+	u16 intsts1;
+	u16 brdysts;
+	u16 nrdysts;
+	u16 bempsts;
+};
+
+struct usbhs_mod {
+	char *name;
+
+	/*
+	 * entry point from common.c
+	 */
+	int (*start)(struct usbhs_priv *priv);
+	int (*stop)(struct usbhs_priv *priv);
+
+	/*
+	 * INTSTS0
+	 */
+
+	/* DVST (DVSQ) */
+	int (*irq_dev_state)(struct usbhs_priv *priv,
+			     struct usbhs_irq_state *irq_state);
+
+	/* CTRT (CTSQ) */
+	int (*irq_ctrl_stage)(struct usbhs_priv *priv,
+			      struct usbhs_irq_state *irq_state);
+
+	/* BEMP / BEMPSTS */
+	int (*irq_empty)(struct usbhs_priv *priv,
+			 struct usbhs_irq_state *irq_state);
+	u16 irq_bempsts;
+
+	/* BRDY / BRDYSTS */
+	int (*irq_ready)(struct usbhs_priv *priv,
+			 struct usbhs_irq_state *irq_state);
+	u16 irq_brdysts;
+
+	/*
+	 * INTSTS1
+	 */
+
+	/* ATTCHE */
+	int (*irq_attch)(struct usbhs_priv *priv,
+			 struct usbhs_irq_state *irq_state);
+
+	/* DTCHE */
+	int (*irq_dtch)(struct usbhs_priv *priv,
+			struct usbhs_irq_state *irq_state);
+
+	/* SIGN */
+	int (*irq_sign)(struct usbhs_priv *priv,
+			struct usbhs_irq_state *irq_state);
+
+	/* SACK */
+	int (*irq_sack)(struct usbhs_priv *priv,
+			struct usbhs_irq_state *irq_state);
+
+	struct usbhs_priv *priv;
+};
+
+struct usbhs_mod_info {
+	struct usbhs_mod *mod[USBHS_MAX];
+	struct usbhs_mod *curt; /* current mod */
+
+	/*
+	 * INTSTS0 :: VBINT
+	 *
+	 * This function will be used as autonomy mode
+	 * when platform cannot call notify_hotplug.
+	 *
+	 * This callback cannot be member of "struct usbhs_mod"
+	 * because it will be used even though
+	 * host/gadget has not been selected.
+	 */
+	int (*irq_vbus)(struct usbhs_priv *priv,
+			struct usbhs_irq_state *irq_state);
+};
+
+/*
+ *		for host/gadget module
+ */
+struct usbhs_mod *usbhs_mod_get(struct usbhs_priv *priv, int id);
+struct usbhs_mod *usbhs_mod_get_current(struct usbhs_priv *priv);
+void usbhs_mod_register(struct usbhs_priv *priv, struct usbhs_mod *usb, int id);
+int usbhs_mod_is_host(struct usbhs_priv *priv);
+int usbhs_mod_change(struct usbhs_priv *priv, int id);
+int usbhs_mod_probe(struct usbhs_priv *priv);
+void usbhs_mod_remove(struct usbhs_priv *priv);
+
+void usbhs_mod_autonomy_mode(struct usbhs_priv *priv);
+
+/*
+ *		status functions
+ */
+int usbhs_status_get_device_state(struct usbhs_irq_state *irq_state);
+int usbhs_status_get_ctrl_stage(struct usbhs_irq_state *irq_state);
+
+/*
+ *		callback functions
+ */
+void usbhs_irq_callback_update(struct usbhs_priv *priv, struct usbhs_mod *mod);
+
+
+#define usbhs_mod_call(priv, func, param...)		\
+	({						\
+		struct usbhs_mod *mod;			\
+		mod = usbhs_mod_get_current(priv);	\
+		!mod		? -ENODEV :		\
+		!mod->func	? 0 :			\
+		 mod->func(param);			\
+	})
+
+/*
+ * host / gadget control
+ */
+#if	defined(CONFIG_USB_RENESAS_USBHS_HCD) || \
+	defined(CONFIG_USB_RENESAS_USBHS_HCD_MODULE)
+extern int usbhs_mod_host_probe(struct usbhs_priv *priv);
+extern int usbhs_mod_host_remove(struct usbhs_priv *priv);
+#else
+static inline int usbhs_mod_host_probe(struct usbhs_priv *priv)
+{
+	return 0;
+}
+static inline void usbhs_mod_host_remove(struct usbhs_priv *priv)
+{
+}
+#endif
+
+#if	defined(CONFIG_USB_RENESAS_USBHS_UDC) || \
+	defined(CONFIG_USB_RENESAS_USBHS_UDC_MODULE)
+extern int usbhs_mod_gadget_probe(struct usbhs_priv *priv);
+extern void usbhs_mod_gadget_remove(struct usbhs_priv *priv);
+#else
+static inline int usbhs_mod_gadget_probe(struct usbhs_priv *priv)
+{
+	return 0;
+}
+static inline void usbhs_mod_gadget_remove(struct usbhs_priv *priv)
+{
+}
+#endif
+
+#endif /* RENESAS_USB_MOD_H */
diff --git a/drivers/usb/renesas_usbhs/mod_gadget.c b/drivers/usb/renesas_usbhs/mod_gadget.c
new file mode 100644
index 0000000..59cac40
--- /dev/null
+++ b/drivers/usb/renesas_usbhs/mod_gadget.c
@@ -0,0 +1,1175 @@
+// SPDX-License-Identifier: GPL-1.0+
+/*
+ * Renesas USB driver
+ *
+ * Copyright (C) 2011 Renesas Solutions Corp.
+ * Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
+ */
+#include <linux/delay.h>
+#include <linux/dma-mapping.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/usb/ch9.h>
+#include <linux/usb/gadget.h>
+#include <linux/usb/otg.h>
+#include "common.h"
+
+/*
+ *		struct
+ */
+struct usbhsg_request {
+	struct usb_request	req;
+	struct usbhs_pkt	pkt;
+};
+
+#define EP_NAME_SIZE 8
+struct usbhsg_gpriv;
+struct usbhsg_uep {
+	struct usb_ep		 ep;
+	struct usbhs_pipe	*pipe;
+	spinlock_t		lock;	/* protect the pipe */
+
+	char ep_name[EP_NAME_SIZE];
+
+	struct usbhsg_gpriv *gpriv;
+};
+
+struct usbhsg_gpriv {
+	struct usb_gadget	 gadget;
+	struct usbhs_mod	 mod;
+
+	struct usbhsg_uep	*uep;
+	int			 uep_size;
+
+	struct usb_gadget_driver	*driver;
+	struct usb_phy		*transceiver;
+	bool			 vbus_active;
+
+	u32	status;
+#define USBHSG_STATUS_STARTED		(1 << 0)
+#define USBHSG_STATUS_REGISTERD		(1 << 1)
+#define USBHSG_STATUS_WEDGE		(1 << 2)
+#define USBHSG_STATUS_SELF_POWERED	(1 << 3)
+#define USBHSG_STATUS_SOFT_CONNECT	(1 << 4)
+};
+
+struct usbhsg_recip_handle {
+	char *name;
+	int (*device)(struct usbhs_priv *priv, struct usbhsg_uep *uep,
+		      struct usb_ctrlrequest *ctrl);
+	int (*interface)(struct usbhs_priv *priv, struct usbhsg_uep *uep,
+			 struct usb_ctrlrequest *ctrl);
+	int (*endpoint)(struct usbhs_priv *priv, struct usbhsg_uep *uep,
+			struct usb_ctrlrequest *ctrl);
+};
+
+/*
+ *		macro
+ */
+#define usbhsg_priv_to_gpriv(priv)			\
+	container_of(					\
+		usbhs_mod_get(priv, USBHS_GADGET),	\
+		struct usbhsg_gpriv, mod)
+
+#define __usbhsg_for_each_uep(start, pos, g, i)	\
+	for ((i) = start;					\
+	     ((i) < (g)->uep_size) && ((pos) = (g)->uep + (i));	\
+	     (i)++)
+
+#define usbhsg_for_each_uep(pos, gpriv, i)	\
+	__usbhsg_for_each_uep(1, pos, gpriv, i)
+
+#define usbhsg_for_each_uep_with_dcp(pos, gpriv, i)	\
+	__usbhsg_for_each_uep(0, pos, gpriv, i)
+
+#define usbhsg_gadget_to_gpriv(g)\
+	container_of(g, struct usbhsg_gpriv, gadget)
+
+#define usbhsg_req_to_ureq(r)\
+	container_of(r, struct usbhsg_request, req)
+
+#define usbhsg_ep_to_uep(e)		container_of(e, struct usbhsg_uep, ep)
+#define usbhsg_gpriv_to_dev(gp)		usbhs_priv_to_dev((gp)->mod.priv)
+#define usbhsg_gpriv_to_priv(gp)	((gp)->mod.priv)
+#define usbhsg_gpriv_to_dcp(gp)		((gp)->uep)
+#define usbhsg_gpriv_to_nth_uep(gp, i)	((gp)->uep + i)
+#define usbhsg_uep_to_gpriv(u)		((u)->gpriv)
+#define usbhsg_uep_to_pipe(u)		((u)->pipe)
+#define usbhsg_pipe_to_uep(p)		((p)->mod_private)
+#define usbhsg_is_dcp(u)		((u) == usbhsg_gpriv_to_dcp((u)->gpriv))
+
+#define usbhsg_ureq_to_pkt(u)		(&(u)->pkt)
+#define usbhsg_pkt_to_ureq(i)	\
+	container_of(i, struct usbhsg_request, pkt)
+
+#define usbhsg_is_not_connected(gp) ((gp)->gadget.speed == USB_SPEED_UNKNOWN)
+
+/* status */
+#define usbhsg_status_init(gp)   do {(gp)->status = 0; } while (0)
+#define usbhsg_status_set(gp, b) (gp->status |=  b)
+#define usbhsg_status_clr(gp, b) (gp->status &= ~b)
+#define usbhsg_status_has(gp, b) (gp->status &   b)
+
+/*
+ *		queue push/pop
+ */
+static void __usbhsg_queue_pop(struct usbhsg_uep *uep,
+			       struct usbhsg_request *ureq,
+			       int status)
+{
+	struct usbhsg_gpriv *gpriv = usbhsg_uep_to_gpriv(uep);
+	struct usbhs_pipe *pipe = usbhsg_uep_to_pipe(uep);
+	struct device *dev = usbhsg_gpriv_to_dev(gpriv);
+	struct usbhs_priv *priv = usbhsg_gpriv_to_priv(gpriv);
+
+	if (pipe)
+		dev_dbg(dev, "pipe %d : queue pop\n", usbhs_pipe_number(pipe));
+
+	ureq->req.status = status;
+	spin_unlock(usbhs_priv_to_lock(priv));
+	usb_gadget_giveback_request(&uep->ep, &ureq->req);
+	spin_lock(usbhs_priv_to_lock(priv));
+}
+
+static void usbhsg_queue_pop(struct usbhsg_uep *uep,
+			     struct usbhsg_request *ureq,
+			     int status)
+{
+	struct usbhsg_gpriv *gpriv = usbhsg_uep_to_gpriv(uep);
+	struct usbhs_priv *priv = usbhsg_gpriv_to_priv(gpriv);
+	unsigned long flags;
+
+	usbhs_lock(priv, flags);
+	__usbhsg_queue_pop(uep, ureq, status);
+	usbhs_unlock(priv, flags);
+}
+
+static void usbhsg_queue_done(struct usbhs_priv *priv, struct usbhs_pkt *pkt)
+{
+	struct usbhs_pipe *pipe = pkt->pipe;
+	struct usbhsg_uep *uep = usbhsg_pipe_to_uep(pipe);
+	struct usbhsg_request *ureq = usbhsg_pkt_to_ureq(pkt);
+	unsigned long flags;
+
+	ureq->req.actual = pkt->actual;
+
+	usbhs_lock(priv, flags);
+	if (uep)
+		__usbhsg_queue_pop(uep, ureq, 0);
+	usbhs_unlock(priv, flags);
+}
+
+static void usbhsg_queue_push(struct usbhsg_uep *uep,
+			      struct usbhsg_request *ureq)
+{
+	struct usbhsg_gpriv *gpriv = usbhsg_uep_to_gpriv(uep);
+	struct device *dev = usbhsg_gpriv_to_dev(gpriv);
+	struct usbhs_pipe *pipe = usbhsg_uep_to_pipe(uep);
+	struct usbhs_pkt *pkt = usbhsg_ureq_to_pkt(ureq);
+	struct usb_request *req = &ureq->req;
+
+	req->actual = 0;
+	req->status = -EINPROGRESS;
+	usbhs_pkt_push(pipe, pkt, usbhsg_queue_done,
+		       req->buf, req->length, req->zero, -1);
+	usbhs_pkt_start(pipe);
+
+	dev_dbg(dev, "pipe %d : queue push (%d)\n",
+		usbhs_pipe_number(pipe),
+		req->length);
+}
+
+/*
+ *		dma map/unmap
+ */
+static int usbhsg_dma_map_ctrl(struct device *dma_dev, struct usbhs_pkt *pkt,
+			       int map)
+{
+	struct usbhsg_request *ureq = usbhsg_pkt_to_ureq(pkt);
+	struct usb_request *req = &ureq->req;
+	struct usbhs_pipe *pipe = pkt->pipe;
+	enum dma_data_direction dir;
+	int ret = 0;
+
+	dir = usbhs_pipe_is_dir_host(pipe);
+
+	if (map) {
+		/* it can not use scatter/gather */
+		WARN_ON(req->num_sgs);
+
+		ret = usb_gadget_map_request_by_dev(dma_dev, req, dir);
+		if (ret < 0)
+			return ret;
+
+		pkt->dma = req->dma;
+	} else {
+		usb_gadget_unmap_request_by_dev(dma_dev, req, dir);
+	}
+
+	return ret;
+}
+
+/*
+ *		USB_TYPE_STANDARD / clear feature functions
+ */
+static int usbhsg_recip_handler_std_control_done(struct usbhs_priv *priv,
+						 struct usbhsg_uep *uep,
+						 struct usb_ctrlrequest *ctrl)
+{
+	struct usbhsg_gpriv *gpriv = usbhsg_priv_to_gpriv(priv);
+	struct usbhsg_uep *dcp = usbhsg_gpriv_to_dcp(gpriv);
+	struct usbhs_pipe *pipe = usbhsg_uep_to_pipe(dcp);
+
+	usbhs_dcp_control_transfer_done(pipe);
+
+	return 0;
+}
+
+static int usbhsg_recip_handler_std_clear_endpoint(struct usbhs_priv *priv,
+						   struct usbhsg_uep *uep,
+						   struct usb_ctrlrequest *ctrl)
+{
+	struct usbhsg_gpriv *gpriv = usbhsg_uep_to_gpriv(uep);
+	struct usbhs_pipe *pipe = usbhsg_uep_to_pipe(uep);
+
+	if (!usbhsg_status_has(gpriv, USBHSG_STATUS_WEDGE)) {
+		usbhs_pipe_disable(pipe);
+		usbhs_pipe_sequence_data0(pipe);
+		usbhs_pipe_enable(pipe);
+	}
+
+	usbhsg_recip_handler_std_control_done(priv, uep, ctrl);
+
+	usbhs_pkt_start(pipe);
+
+	return 0;
+}
+
+static struct usbhsg_recip_handle req_clear_feature = {
+	.name		= "clear feature",
+	.device		= usbhsg_recip_handler_std_control_done,
+	.interface	= usbhsg_recip_handler_std_control_done,
+	.endpoint	= usbhsg_recip_handler_std_clear_endpoint,
+};
+
+/*
+ *		USB_TYPE_STANDARD / set feature functions
+ */
+static int usbhsg_recip_handler_std_set_device(struct usbhs_priv *priv,
+						 struct usbhsg_uep *uep,
+						 struct usb_ctrlrequest *ctrl)
+{
+	switch (le16_to_cpu(ctrl->wValue)) {
+	case USB_DEVICE_TEST_MODE:
+		usbhsg_recip_handler_std_control_done(priv, uep, ctrl);
+		udelay(100);
+		usbhs_sys_set_test_mode(priv, le16_to_cpu(ctrl->wIndex >> 8));
+		break;
+	default:
+		usbhsg_recip_handler_std_control_done(priv, uep, ctrl);
+		break;
+	}
+
+	return 0;
+}
+
+static int usbhsg_recip_handler_std_set_endpoint(struct usbhs_priv *priv,
+						 struct usbhsg_uep *uep,
+						 struct usb_ctrlrequest *ctrl)
+{
+	struct usbhs_pipe *pipe = usbhsg_uep_to_pipe(uep);
+
+	usbhs_pipe_stall(pipe);
+
+	usbhsg_recip_handler_std_control_done(priv, uep, ctrl);
+
+	return 0;
+}
+
+static struct usbhsg_recip_handle req_set_feature = {
+	.name		= "set feature",
+	.device		= usbhsg_recip_handler_std_set_device,
+	.interface	= usbhsg_recip_handler_std_control_done,
+	.endpoint	= usbhsg_recip_handler_std_set_endpoint,
+};
+
+/*
+ *		USB_TYPE_STANDARD / get status functions
+ */
+static void __usbhsg_recip_send_complete(struct usb_ep *ep,
+					 struct usb_request *req)
+{
+	struct usbhsg_request *ureq = usbhsg_req_to_ureq(req);
+
+	/* free allocated recip-buffer/usb_request */
+	kfree(ureq->pkt.buf);
+	usb_ep_free_request(ep, req);
+}
+
+static void __usbhsg_recip_send_status(struct usbhsg_gpriv *gpriv,
+				       unsigned short status)
+{
+	struct usbhsg_uep *dcp = usbhsg_gpriv_to_dcp(gpriv);
+	struct usbhs_pipe *pipe = usbhsg_uep_to_pipe(dcp);
+	struct device *dev = usbhsg_gpriv_to_dev(gpriv);
+	struct usb_request *req;
+	unsigned short *buf;
+
+	/* alloc new usb_request for recip */
+	req = usb_ep_alloc_request(&dcp->ep, GFP_ATOMIC);
+	if (!req) {
+		dev_err(dev, "recip request allocation fail\n");
+		return;
+	}
+
+	/* alloc recip data buffer */
+	buf = kmalloc(sizeof(*buf), GFP_ATOMIC);
+	if (!buf) {
+		usb_ep_free_request(&dcp->ep, req);
+		return;
+	}
+
+	/* recip data is status */
+	*buf = cpu_to_le16(status);
+
+	/* allocated usb_request/buffer will be freed */
+	req->complete	= __usbhsg_recip_send_complete;
+	req->buf	= buf;
+	req->length	= sizeof(*buf);
+	req->zero	= 0;
+
+	/* push packet */
+	pipe->handler = &usbhs_fifo_pio_push_handler;
+	usbhsg_queue_push(dcp, usbhsg_req_to_ureq(req));
+}
+
+static int usbhsg_recip_handler_std_get_device(struct usbhs_priv *priv,
+					       struct usbhsg_uep *uep,
+					       struct usb_ctrlrequest *ctrl)
+{
+	struct usbhsg_gpriv *gpriv = usbhsg_uep_to_gpriv(uep);
+	unsigned short status = 0;
+
+	if (usbhsg_status_has(gpriv, USBHSG_STATUS_SELF_POWERED))
+		status = 1 << USB_DEVICE_SELF_POWERED;
+
+	__usbhsg_recip_send_status(gpriv, status);
+
+	return 0;
+}
+
+static int usbhsg_recip_handler_std_get_interface(struct usbhs_priv *priv,
+						  struct usbhsg_uep *uep,
+						  struct usb_ctrlrequest *ctrl)
+{
+	struct usbhsg_gpriv *gpriv = usbhsg_uep_to_gpriv(uep);
+	unsigned short status = 0;
+
+	__usbhsg_recip_send_status(gpriv, status);
+
+	return 0;
+}
+
+static int usbhsg_recip_handler_std_get_endpoint(struct usbhs_priv *priv,
+						 struct usbhsg_uep *uep,
+						 struct usb_ctrlrequest *ctrl)
+{
+	struct usbhsg_gpriv *gpriv = usbhsg_uep_to_gpriv(uep);
+	struct usbhs_pipe *pipe = usbhsg_uep_to_pipe(uep);
+	unsigned short status = 0;
+
+	if (usbhs_pipe_is_stall(pipe))
+		status = 1 << USB_ENDPOINT_HALT;
+
+	__usbhsg_recip_send_status(gpriv, status);
+
+	return 0;
+}
+
+static struct usbhsg_recip_handle req_get_status = {
+	.name		= "get status",
+	.device		= usbhsg_recip_handler_std_get_device,
+	.interface	= usbhsg_recip_handler_std_get_interface,
+	.endpoint	= usbhsg_recip_handler_std_get_endpoint,
+};
+
+/*
+ *		USB_TYPE handler
+ */
+static int usbhsg_recip_run_handle(struct usbhs_priv *priv,
+				   struct usbhsg_recip_handle *handler,
+				   struct usb_ctrlrequest *ctrl)
+{
+	struct usbhsg_gpriv *gpriv = usbhsg_priv_to_gpriv(priv);
+	struct device *dev = usbhsg_gpriv_to_dev(gpriv);
+	struct usbhsg_uep *uep;
+	struct usbhs_pipe *pipe;
+	int recip = ctrl->bRequestType & USB_RECIP_MASK;
+	int nth = le16_to_cpu(ctrl->wIndex) & USB_ENDPOINT_NUMBER_MASK;
+	int ret = 0;
+	int (*func)(struct usbhs_priv *priv, struct usbhsg_uep *uep,
+		    struct usb_ctrlrequest *ctrl);
+	char *msg;
+
+	uep = usbhsg_gpriv_to_nth_uep(gpriv, nth);
+	pipe = usbhsg_uep_to_pipe(uep);
+	if (!pipe) {
+		dev_err(dev, "wrong recip request\n");
+		return -EINVAL;
+	}
+
+	switch (recip) {
+	case USB_RECIP_DEVICE:
+		msg	= "DEVICE";
+		func	= handler->device;
+		break;
+	case USB_RECIP_INTERFACE:
+		msg	= "INTERFACE";
+		func	= handler->interface;
+		break;
+	case USB_RECIP_ENDPOINT:
+		msg	= "ENDPOINT";
+		func	= handler->endpoint;
+		break;
+	default:
+		dev_warn(dev, "unsupported RECIP(%d)\n", recip);
+		func = NULL;
+		ret = -EINVAL;
+	}
+
+	if (func) {
+		dev_dbg(dev, "%s (pipe %d :%s)\n", handler->name, nth, msg);
+		ret = func(priv, uep, ctrl);
+	}
+
+	return ret;
+}
+
+/*
+ *		irq functions
+ *
+ * it will be called from usbhs_interrupt
+ */
+static int usbhsg_irq_dev_state(struct usbhs_priv *priv,
+				struct usbhs_irq_state *irq_state)
+{
+	struct usbhsg_gpriv *gpriv = usbhsg_priv_to_gpriv(priv);
+	struct device *dev = usbhsg_gpriv_to_dev(gpriv);
+
+	gpriv->gadget.speed = usbhs_bus_get_speed(priv);
+
+	dev_dbg(dev, "state = %x : speed : %d\n",
+		usbhs_status_get_device_state(irq_state),
+		gpriv->gadget.speed);
+
+	return 0;
+}
+
+static int usbhsg_irq_ctrl_stage(struct usbhs_priv *priv,
+				 struct usbhs_irq_state *irq_state)
+{
+	struct usbhsg_gpriv *gpriv = usbhsg_priv_to_gpriv(priv);
+	struct usbhsg_uep *dcp = usbhsg_gpriv_to_dcp(gpriv);
+	struct usbhs_pipe *pipe = usbhsg_uep_to_pipe(dcp);
+	struct device *dev = usbhsg_gpriv_to_dev(gpriv);
+	struct usb_ctrlrequest ctrl;
+	struct usbhsg_recip_handle *recip_handler = NULL;
+	int stage = usbhs_status_get_ctrl_stage(irq_state);
+	int ret = 0;
+
+	dev_dbg(dev, "stage = %d\n", stage);
+
+	/*
+	 * see Manual
+	 *
+	 *  "Operation"
+	 *  - "Interrupt Function"
+	 *    - "Control Transfer Stage Transition Interrupt"
+	 *      - Fig. "Control Transfer Stage Transitions"
+	 */
+
+	switch (stage) {
+	case READ_DATA_STAGE:
+		pipe->handler = &usbhs_fifo_pio_push_handler;
+		break;
+	case WRITE_DATA_STAGE:
+		pipe->handler = &usbhs_fifo_pio_pop_handler;
+		break;
+	case NODATA_STATUS_STAGE:
+		pipe->handler = &usbhs_ctrl_stage_end_handler;
+		break;
+	case READ_STATUS_STAGE:
+	case WRITE_STATUS_STAGE:
+		usbhs_dcp_control_transfer_done(pipe);
+		/* fall through */
+	default:
+		return ret;
+	}
+
+	/*
+	 * get usb request
+	 */
+	usbhs_usbreq_get_val(priv, &ctrl);
+
+	switch (ctrl.bRequestType & USB_TYPE_MASK) {
+	case USB_TYPE_STANDARD:
+		switch (ctrl.bRequest) {
+		case USB_REQ_CLEAR_FEATURE:
+			recip_handler = &req_clear_feature;
+			break;
+		case USB_REQ_SET_FEATURE:
+			recip_handler = &req_set_feature;
+			break;
+		case USB_REQ_GET_STATUS:
+			recip_handler = &req_get_status;
+			break;
+		}
+	}
+
+	/*
+	 * setup stage / run recip
+	 */
+	if (recip_handler)
+		ret = usbhsg_recip_run_handle(priv, recip_handler, &ctrl);
+	else
+		ret = gpriv->driver->setup(&gpriv->gadget, &ctrl);
+
+	if (ret < 0)
+		usbhs_pipe_stall(pipe);
+
+	return ret;
+}
+
+/*
+ *
+ *		usb_dcp_ops
+ *
+ */
+static int usbhsg_pipe_disable(struct usbhsg_uep *uep)
+{
+	struct usbhs_pipe *pipe = usbhsg_uep_to_pipe(uep);
+	struct usbhs_pkt *pkt;
+
+	while (1) {
+		pkt = usbhs_pkt_pop(pipe, NULL);
+		if (!pkt)
+			break;
+
+		usbhsg_queue_pop(uep, usbhsg_pkt_to_ureq(pkt), -ESHUTDOWN);
+	}
+
+	usbhs_pipe_disable(pipe);
+
+	return 0;
+}
+
+/*
+ *
+ *		usb_ep_ops
+ *
+ */
+static int usbhsg_ep_enable(struct usb_ep *ep,
+			 const struct usb_endpoint_descriptor *desc)
+{
+	struct usbhsg_uep *uep   = usbhsg_ep_to_uep(ep);
+	struct usbhsg_gpriv *gpriv = usbhsg_uep_to_gpriv(uep);
+	struct usbhs_priv *priv = usbhsg_gpriv_to_priv(gpriv);
+	struct usbhs_pipe *pipe;
+	int ret = -EIO;
+	unsigned long flags;
+
+	usbhs_lock(priv, flags);
+
+	/*
+	 * if it already have pipe,
+	 * nothing to do
+	 */
+	if (uep->pipe) {
+		usbhs_pipe_clear(uep->pipe);
+		usbhs_pipe_sequence_data0(uep->pipe);
+		ret = 0;
+		goto usbhsg_ep_enable_end;
+	}
+
+	pipe = usbhs_pipe_malloc(priv,
+				 usb_endpoint_type(desc),
+				 usb_endpoint_dir_in(desc));
+	if (pipe) {
+		uep->pipe		= pipe;
+		pipe->mod_private	= uep;
+
+		/* set epnum / maxp */
+		usbhs_pipe_config_update(pipe, 0,
+					 usb_endpoint_num(desc),
+					 usb_endpoint_maxp(desc));
+
+		/*
+		 * usbhs_fifo_dma_push/pop_handler try to
+		 * use dmaengine if possible.
+		 * It will use pio handler if impossible.
+		 */
+		if (usb_endpoint_dir_in(desc)) {
+			pipe->handler = &usbhs_fifo_dma_push_handler;
+		} else {
+			pipe->handler = &usbhs_fifo_dma_pop_handler;
+			usbhs_xxxsts_clear(priv, BRDYSTS,
+					   usbhs_pipe_number(pipe));
+		}
+
+		ret = 0;
+	}
+
+usbhsg_ep_enable_end:
+	usbhs_unlock(priv, flags);
+
+	return ret;
+}
+
+static int usbhsg_ep_disable(struct usb_ep *ep)
+{
+	struct usbhsg_uep *uep = usbhsg_ep_to_uep(ep);
+	struct usbhs_pipe *pipe;
+	unsigned long flags;
+
+	spin_lock_irqsave(&uep->lock, flags);
+	pipe = usbhsg_uep_to_pipe(uep);
+	if (!pipe)
+		goto out;
+
+	usbhsg_pipe_disable(uep);
+	usbhs_pipe_free(pipe);
+
+	uep->pipe->mod_private	= NULL;
+	uep->pipe		= NULL;
+
+out:
+	spin_unlock_irqrestore(&uep->lock, flags);
+
+	return 0;
+}
+
+static struct usb_request *usbhsg_ep_alloc_request(struct usb_ep *ep,
+						   gfp_t gfp_flags)
+{
+	struct usbhsg_request *ureq;
+
+	ureq = kzalloc(sizeof *ureq, gfp_flags);
+	if (!ureq)
+		return NULL;
+
+	usbhs_pkt_init(usbhsg_ureq_to_pkt(ureq));
+
+	return &ureq->req;
+}
+
+static void usbhsg_ep_free_request(struct usb_ep *ep,
+				   struct usb_request *req)
+{
+	struct usbhsg_request *ureq = usbhsg_req_to_ureq(req);
+
+	WARN_ON(!list_empty(&ureq->pkt.node));
+	kfree(ureq);
+}
+
+static int usbhsg_ep_queue(struct usb_ep *ep, struct usb_request *req,
+			  gfp_t gfp_flags)
+{
+	struct usbhsg_uep *uep = usbhsg_ep_to_uep(ep);
+	struct usbhsg_gpriv *gpriv = usbhsg_uep_to_gpriv(uep);
+	struct usbhsg_request *ureq = usbhsg_req_to_ureq(req);
+	struct usbhs_pipe *pipe = usbhsg_uep_to_pipe(uep);
+
+	/* param check */
+	if (usbhsg_is_not_connected(gpriv)	||
+	    unlikely(!gpriv->driver)		||
+	    unlikely(!pipe))
+		return -ESHUTDOWN;
+
+	usbhsg_queue_push(uep, ureq);
+
+	return 0;
+}
+
+static int usbhsg_ep_dequeue(struct usb_ep *ep, struct usb_request *req)
+{
+	struct usbhsg_uep *uep = usbhsg_ep_to_uep(ep);
+	struct usbhsg_request *ureq = usbhsg_req_to_ureq(req);
+	struct usbhs_pipe *pipe;
+	unsigned long flags;
+
+	spin_lock_irqsave(&uep->lock, flags);
+	pipe = usbhsg_uep_to_pipe(uep);
+	if (pipe)
+		usbhs_pkt_pop(pipe, usbhsg_ureq_to_pkt(ureq));
+
+	/*
+	 * To dequeue a request, this driver should call the usbhsg_queue_pop()
+	 * even if the pipe is NULL.
+	 */
+	usbhsg_queue_pop(uep, ureq, -ECONNRESET);
+	spin_unlock_irqrestore(&uep->lock, flags);
+
+	return 0;
+}
+
+static int __usbhsg_ep_set_halt_wedge(struct usb_ep *ep, int halt, int wedge)
+{
+	struct usbhsg_uep *uep = usbhsg_ep_to_uep(ep);
+	struct usbhs_pipe *pipe = usbhsg_uep_to_pipe(uep);
+	struct usbhsg_gpriv *gpriv = usbhsg_uep_to_gpriv(uep);
+	struct usbhs_priv *priv = usbhsg_gpriv_to_priv(gpriv);
+	struct device *dev = usbhsg_gpriv_to_dev(gpriv);
+	unsigned long flags;
+
+	usbhsg_pipe_disable(uep);
+
+	dev_dbg(dev, "set halt %d (pipe %d)\n",
+		halt, usbhs_pipe_number(pipe));
+
+	/********************  spin lock ********************/
+	usbhs_lock(priv, flags);
+
+	if (halt)
+		usbhs_pipe_stall(pipe);
+	else
+		usbhs_pipe_disable(pipe);
+
+	if (halt && wedge)
+		usbhsg_status_set(gpriv, USBHSG_STATUS_WEDGE);
+	else
+		usbhsg_status_clr(gpriv, USBHSG_STATUS_WEDGE);
+
+	usbhs_unlock(priv, flags);
+	/********************  spin unlock ******************/
+
+	return 0;
+}
+
+static int usbhsg_ep_set_halt(struct usb_ep *ep, int value)
+{
+	return __usbhsg_ep_set_halt_wedge(ep, value, 0);
+}
+
+static int usbhsg_ep_set_wedge(struct usb_ep *ep)
+{
+	return __usbhsg_ep_set_halt_wedge(ep, 1, 1);
+}
+
+static const struct usb_ep_ops usbhsg_ep_ops = {
+	.enable		= usbhsg_ep_enable,
+	.disable	= usbhsg_ep_disable,
+
+	.alloc_request	= usbhsg_ep_alloc_request,
+	.free_request	= usbhsg_ep_free_request,
+
+	.queue		= usbhsg_ep_queue,
+	.dequeue	= usbhsg_ep_dequeue,
+
+	.set_halt	= usbhsg_ep_set_halt,
+	.set_wedge	= usbhsg_ep_set_wedge,
+};
+
+/*
+ *		pullup control
+ */
+static int usbhsg_can_pullup(struct usbhs_priv *priv)
+{
+	struct usbhsg_gpriv *gpriv = usbhsg_priv_to_gpriv(priv);
+
+	return gpriv->driver &&
+	       usbhsg_status_has(gpriv, USBHSG_STATUS_SOFT_CONNECT);
+}
+
+static void usbhsg_update_pullup(struct usbhs_priv *priv)
+{
+	if (usbhsg_can_pullup(priv))
+		usbhs_sys_function_pullup(priv, 1);
+	else
+		usbhs_sys_function_pullup(priv, 0);
+}
+
+/*
+ *		usb module start/end
+ */
+static int usbhsg_try_start(struct usbhs_priv *priv, u32 status)
+{
+	struct usbhsg_gpriv *gpriv = usbhsg_priv_to_gpriv(priv);
+	struct usbhsg_uep *dcp = usbhsg_gpriv_to_dcp(gpriv);
+	struct usbhs_mod *mod = usbhs_mod_get_current(priv);
+	struct device *dev = usbhs_priv_to_dev(priv);
+	unsigned long flags;
+	int ret = 0;
+
+	/********************  spin lock ********************/
+	usbhs_lock(priv, flags);
+
+	usbhsg_status_set(gpriv, status);
+	if (!(usbhsg_status_has(gpriv, USBHSG_STATUS_STARTED) &&
+	      usbhsg_status_has(gpriv, USBHSG_STATUS_REGISTERD)))
+		ret = -1; /* not ready */
+
+	usbhs_unlock(priv, flags);
+	/********************  spin unlock ********************/
+
+	if (ret < 0)
+		return 0; /* not ready is not error */
+
+	/*
+	 * enable interrupt and systems if ready
+	 */
+	dev_dbg(dev, "start gadget\n");
+
+	/*
+	 * pipe initialize and enable DCP
+	 */
+	usbhs_fifo_init(priv);
+	usbhs_pipe_init(priv,
+			usbhsg_dma_map_ctrl);
+
+	/* dcp init instead of usbhsg_ep_enable() */
+	dcp->pipe		= usbhs_dcp_malloc(priv);
+	dcp->pipe->mod_private	= dcp;
+	usbhs_pipe_config_update(dcp->pipe, 0, 0, 64);
+
+	/*
+	 * system config enble
+	 * - HI speed
+	 * - function
+	 * - usb module
+	 */
+	usbhs_sys_function_ctrl(priv, 1);
+	usbhsg_update_pullup(priv);
+
+	/*
+	 * enable irq callback
+	 */
+	mod->irq_dev_state	= usbhsg_irq_dev_state;
+	mod->irq_ctrl_stage	= usbhsg_irq_ctrl_stage;
+	usbhs_irq_callback_update(priv, mod);
+
+	return 0;
+}
+
+static int usbhsg_try_stop(struct usbhs_priv *priv, u32 status)
+{
+	struct usbhsg_gpriv *gpriv = usbhsg_priv_to_gpriv(priv);
+	struct usbhs_mod *mod = usbhs_mod_get_current(priv);
+	struct usbhsg_uep *uep;
+	struct device *dev = usbhs_priv_to_dev(priv);
+	unsigned long flags;
+	int ret = 0, i;
+
+	/********************  spin lock ********************/
+	usbhs_lock(priv, flags);
+
+	usbhsg_status_clr(gpriv, status);
+	if (!usbhsg_status_has(gpriv, USBHSG_STATUS_STARTED) &&
+	    !usbhsg_status_has(gpriv, USBHSG_STATUS_REGISTERD))
+		ret = -1; /* already done */
+
+	usbhs_unlock(priv, flags);
+	/********************  spin unlock ********************/
+
+	if (ret < 0)
+		return 0; /* already done is not error */
+
+	/*
+	 * disable interrupt and systems if 1st try
+	 */
+	usbhs_fifo_quit(priv);
+
+	/* disable all irq */
+	mod->irq_dev_state	= NULL;
+	mod->irq_ctrl_stage	= NULL;
+	usbhs_irq_callback_update(priv, mod);
+
+	gpriv->gadget.speed = USB_SPEED_UNKNOWN;
+
+	/* disable sys */
+	usbhs_sys_set_test_mode(priv, 0);
+	usbhs_sys_function_ctrl(priv, 0);
+
+	/* disable all eps */
+	usbhsg_for_each_uep_with_dcp(uep, gpriv, i)
+		usbhsg_ep_disable(&uep->ep);
+
+	dev_dbg(dev, "stop gadget\n");
+
+	return 0;
+}
+
+/*
+ * VBUS provided by the PHY
+ */
+static int usbhsm_phy_get_vbus(struct platform_device *pdev)
+{
+	struct usbhs_priv *priv = usbhs_pdev_to_priv(pdev);
+	struct usbhsg_gpriv *gpriv = usbhsg_priv_to_gpriv(priv);
+
+	return  gpriv->vbus_active;
+}
+
+static void usbhs_mod_phy_mode(struct usbhs_priv *priv)
+{
+	struct usbhs_mod_info *info = &priv->mod_info;
+
+	info->irq_vbus		= NULL;
+	priv->pfunc.get_vbus	= usbhsm_phy_get_vbus;
+
+	usbhs_irq_callback_update(priv, NULL);
+}
+
+/*
+ *
+ *		linux usb function
+ *
+ */
+static int usbhsg_gadget_start(struct usb_gadget *gadget,
+		struct usb_gadget_driver *driver)
+{
+	struct usbhsg_gpriv *gpriv = usbhsg_gadget_to_gpriv(gadget);
+	struct usbhs_priv *priv = usbhsg_gpriv_to_priv(gpriv);
+	struct device *dev = usbhs_priv_to_dev(priv);
+	int ret;
+
+	if (!driver		||
+	    !driver->setup	||
+	    driver->max_speed < USB_SPEED_FULL)
+		return -EINVAL;
+
+	/* connect to bus through transceiver */
+	if (!IS_ERR_OR_NULL(gpriv->transceiver)) {
+		ret = otg_set_peripheral(gpriv->transceiver->otg,
+					&gpriv->gadget);
+		if (ret) {
+			dev_err(dev, "%s: can't bind to transceiver\n",
+				gpriv->gadget.name);
+			return ret;
+		}
+
+		/* get vbus using phy versions */
+		usbhs_mod_phy_mode(priv);
+	}
+
+	/* first hook up the driver ... */
+	gpriv->driver = driver;
+
+	return usbhsg_try_start(priv, USBHSG_STATUS_REGISTERD);
+}
+
+static int usbhsg_gadget_stop(struct usb_gadget *gadget)
+{
+	struct usbhsg_gpriv *gpriv = usbhsg_gadget_to_gpriv(gadget);
+	struct usbhs_priv *priv = usbhsg_gpriv_to_priv(gpriv);
+
+	usbhsg_try_stop(priv, USBHSG_STATUS_REGISTERD);
+
+	if (!IS_ERR_OR_NULL(gpriv->transceiver))
+		otg_set_peripheral(gpriv->transceiver->otg, NULL);
+
+	gpriv->driver = NULL;
+
+	return 0;
+}
+
+/*
+ *		usb gadget ops
+ */
+static int usbhsg_get_frame(struct usb_gadget *gadget)
+{
+	struct usbhsg_gpriv *gpriv = usbhsg_gadget_to_gpriv(gadget);
+	struct usbhs_priv *priv = usbhsg_gpriv_to_priv(gpriv);
+
+	return usbhs_frame_get_num(priv);
+}
+
+static int usbhsg_pullup(struct usb_gadget *gadget, int is_on)
+{
+	struct usbhsg_gpriv *gpriv = usbhsg_gadget_to_gpriv(gadget);
+	struct usbhs_priv *priv = usbhsg_gpriv_to_priv(gpriv);
+	unsigned long flags;
+
+	usbhs_lock(priv, flags);
+	if (is_on)
+		usbhsg_status_set(gpriv, USBHSG_STATUS_SOFT_CONNECT);
+	else
+		usbhsg_status_clr(gpriv, USBHSG_STATUS_SOFT_CONNECT);
+	usbhsg_update_pullup(priv);
+	usbhs_unlock(priv, flags);
+
+	return 0;
+}
+
+static int usbhsg_set_selfpowered(struct usb_gadget *gadget, int is_self)
+{
+	struct usbhsg_gpriv *gpriv = usbhsg_gadget_to_gpriv(gadget);
+
+	if (is_self)
+		usbhsg_status_set(gpriv, USBHSG_STATUS_SELF_POWERED);
+	else
+		usbhsg_status_clr(gpriv, USBHSG_STATUS_SELF_POWERED);
+
+	gadget->is_selfpowered = (is_self != 0);
+
+	return 0;
+}
+
+static int usbhsg_vbus_session(struct usb_gadget *gadget, int is_active)
+{
+	struct usbhsg_gpriv *gpriv = usbhsg_gadget_to_gpriv(gadget);
+	struct usbhs_priv *priv = usbhsg_gpriv_to_priv(gpriv);
+	struct platform_device *pdev = usbhs_priv_to_pdev(priv);
+
+	gpriv->vbus_active = !!is_active;
+
+	renesas_usbhs_call_notify_hotplug(pdev);
+
+	return 0;
+}
+
+static const struct usb_gadget_ops usbhsg_gadget_ops = {
+	.get_frame		= usbhsg_get_frame,
+	.set_selfpowered	= usbhsg_set_selfpowered,
+	.udc_start		= usbhsg_gadget_start,
+	.udc_stop		= usbhsg_gadget_stop,
+	.pullup			= usbhsg_pullup,
+	.vbus_session		= usbhsg_vbus_session,
+};
+
+static int usbhsg_start(struct usbhs_priv *priv)
+{
+	return usbhsg_try_start(priv, USBHSG_STATUS_STARTED);
+}
+
+static int usbhsg_stop(struct usbhs_priv *priv)
+{
+	struct usbhsg_gpriv *gpriv = usbhsg_priv_to_gpriv(priv);
+
+	/* cable disconnect */
+	if (gpriv->driver &&
+	    gpriv->driver->disconnect)
+		gpriv->driver->disconnect(&gpriv->gadget);
+
+	return usbhsg_try_stop(priv, USBHSG_STATUS_STARTED);
+}
+
+int usbhs_mod_gadget_probe(struct usbhs_priv *priv)
+{
+	struct usbhsg_gpriv *gpriv;
+	struct usbhsg_uep *uep;
+	struct device *dev = usbhs_priv_to_dev(priv);
+	struct renesas_usbhs_driver_pipe_config *pipe_configs =
+					usbhs_get_dparam(priv, pipe_configs);
+	int pipe_size = usbhs_get_dparam(priv, pipe_size);
+	int i;
+	int ret;
+
+	gpriv = kzalloc(sizeof(struct usbhsg_gpriv), GFP_KERNEL);
+	if (!gpriv)
+		return -ENOMEM;
+
+	uep = kcalloc(pipe_size, sizeof(struct usbhsg_uep), GFP_KERNEL);
+	if (!uep) {
+		ret = -ENOMEM;
+		goto usbhs_mod_gadget_probe_err_gpriv;
+	}
+
+	gpriv->transceiver = usb_get_phy(USB_PHY_TYPE_UNDEFINED);
+	dev_info(dev, "%stransceiver found\n",
+		 !IS_ERR(gpriv->transceiver) ? "" : "no ");
+
+	/*
+	 * CAUTION
+	 *
+	 * There is no guarantee that it is possible to access usb module here.
+	 * Don't accesses to it.
+	 * The accesse will be enable after "usbhsg_start"
+	 */
+
+	/*
+	 * register itself
+	 */
+	usbhs_mod_register(priv, &gpriv->mod, USBHS_GADGET);
+
+	/* init gpriv */
+	gpriv->mod.name		= "gadget";
+	gpriv->mod.start	= usbhsg_start;
+	gpriv->mod.stop		= usbhsg_stop;
+	gpriv->uep		= uep;
+	gpriv->uep_size		= pipe_size;
+	usbhsg_status_init(gpriv);
+
+	/*
+	 * init gadget
+	 */
+	gpriv->gadget.dev.parent	= dev;
+	gpriv->gadget.name		= "renesas_usbhs_udc";
+	gpriv->gadget.ops		= &usbhsg_gadget_ops;
+	gpriv->gadget.max_speed		= USB_SPEED_HIGH;
+	gpriv->gadget.quirk_avoids_skb_reserve = usbhs_get_dparam(priv,
+								has_usb_dmac);
+
+	INIT_LIST_HEAD(&gpriv->gadget.ep_list);
+
+	/*
+	 * init usb_ep
+	 */
+	usbhsg_for_each_uep_with_dcp(uep, gpriv, i) {
+		uep->gpriv	= gpriv;
+		uep->pipe	= NULL;
+		snprintf(uep->ep_name, EP_NAME_SIZE, "ep%d", i);
+
+		uep->ep.name		= uep->ep_name;
+		uep->ep.ops		= &usbhsg_ep_ops;
+		INIT_LIST_HEAD(&uep->ep.ep_list);
+		spin_lock_init(&uep->lock);
+
+		/* init DCP */
+		if (usbhsg_is_dcp(uep)) {
+			gpriv->gadget.ep0 = &uep->ep;
+			usb_ep_set_maxpacket_limit(&uep->ep, 64);
+			uep->ep.caps.type_control = true;
+		} else {
+			/* init normal pipe */
+			if (pipe_configs[i].type == USB_ENDPOINT_XFER_ISOC)
+				uep->ep.caps.type_iso = true;
+			if (pipe_configs[i].type == USB_ENDPOINT_XFER_BULK)
+				uep->ep.caps.type_bulk = true;
+			if (pipe_configs[i].type == USB_ENDPOINT_XFER_INT)
+				uep->ep.caps.type_int = true;
+			usb_ep_set_maxpacket_limit(&uep->ep,
+						   pipe_configs[i].bufsize);
+			list_add_tail(&uep->ep.ep_list, &gpriv->gadget.ep_list);
+		}
+		uep->ep.caps.dir_in = true;
+		uep->ep.caps.dir_out = true;
+	}
+
+	ret = usb_add_gadget_udc(dev, &gpriv->gadget);
+	if (ret)
+		goto err_add_udc;
+
+
+	dev_info(dev, "gadget probed\n");
+
+	return 0;
+
+err_add_udc:
+	kfree(gpriv->uep);
+
+usbhs_mod_gadget_probe_err_gpriv:
+	kfree(gpriv);
+
+	return ret;
+}
+
+void usbhs_mod_gadget_remove(struct usbhs_priv *priv)
+{
+	struct usbhsg_gpriv *gpriv = usbhsg_priv_to_gpriv(priv);
+
+	usb_del_gadget_udc(&gpriv->gadget);
+
+	kfree(gpriv->uep);
+	kfree(gpriv);
+}
diff --git a/drivers/usb/renesas_usbhs/mod_host.c b/drivers/usb/renesas_usbhs/mod_host.c
new file mode 100644
index 0000000..4e59c64
--- /dev/null
+++ b/drivers/usb/renesas_usbhs/mod_host.c
@@ -0,0 +1,1574 @@
+// SPDX-License-Identifier: GPL-1.0+
+/*
+ * Renesas USB driver
+ *
+ * Copyright (C) 2011 Renesas Solutions Corp.
+ * Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
+ */
+#include <linux/io.h>
+#include <linux/list.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/usb.h>
+#include <linux/usb/hcd.h>
+#include "common.h"
+
+/*
+ *** HARDWARE LIMITATION ***
+ *
+ * 1) renesas_usbhs has a limited number of controllable devices.
+ *    it can control only 9 devices in generally.
+ *	see DEVADDn / DCPMAXP / PIPEMAXP.
+ *
+ * 2) renesas_usbhs pipe number is limited.
+ *    the pipe will be re-used for each devices.
+ *    so, software should control DATA0/1 sequence of each devices.
+ */
+
+
+/*
+ *		image of mod_host
+ *
+ * +--------+
+ * | udev 0 | --> it is used when set address
+ * +--------+
+ *
+ * +--------+					pipes are reused for each uep.
+ * | udev 1 |-+- [uep 0 (dcp) ] --+		pipe will be switched when
+ * +--------+ |			  |		other device requested
+ *	      +- [uep 1 (bulk)]	--|---+		   +--------------+
+ *	      |			  +--------------> | pipe0 (dcp)  |
+ *	      +- [uep 2 (bulk)]	-@    |		   +--------------+
+ *				      |		   | pipe1 (isoc) |
+ * +--------+			      |		   +--------------+
+ * | udev 2 |-+- [uep 0 (dcp) ]	-@    +----------> | pipe2 (bulk) |
+ * +--------+ |					   +--------------+
+ *	      +- [uep 1 (int) ]	----+	  +------> | pipe3 (bulk) |
+ *				    |	  |	   +--------------+
+ * +--------+			    +-----|------> | pipe4 (int)  |
+ * | udev 3 |-+- [uep 0 (dcp) ]	-@	  |	   +--------------+
+ * +--------+ |				  |	   | ....	  |
+ *	      +- [uep 1 (bulk)]	-@	  |	   | ....	  |
+ *	      |				  |
+ *	      +- [uep 2 (bulk)]-----------+
+ *
+ * @ :	uep requested free pipe, but all have been used.
+ *	now it is waiting for free pipe
+ */
+
+
+/*
+ *		struct
+ */
+struct usbhsh_request {
+	struct urb		*urb;
+	struct usbhs_pkt	pkt;
+};
+
+struct usbhsh_device {
+	struct usb_device	*usbv;
+	struct list_head	ep_list_head; /* list of usbhsh_ep */
+};
+
+struct usbhsh_ep {
+	struct usbhs_pipe	*pipe;   /* attached pipe */
+	struct usbhsh_device	*udev;   /* attached udev */
+	struct usb_host_endpoint *ep;
+	struct list_head	ep_list; /* list to usbhsh_device */
+	unsigned int		counter; /* pipe attach counter */
+};
+
+#define USBHSH_DEVICE_MAX	10 /* see DEVADDn / DCPMAXP / PIPEMAXP */
+#define USBHSH_PORT_MAX		 7 /* see DEVADDn :: HUBPORT */
+struct usbhsh_hpriv {
+	struct usbhs_mod	mod;
+	struct usbhs_pipe	*dcp;
+
+	struct usbhsh_device	udev[USBHSH_DEVICE_MAX];
+
+	u32	port_stat;	/* USB_PORT_STAT_xxx */
+
+	struct completion	setup_ack_done;
+};
+
+
+static const char usbhsh_hcd_name[] = "renesas_usbhs host";
+
+/*
+ *		macro
+ */
+#define usbhsh_priv_to_hpriv(priv) \
+	container_of(usbhs_mod_get(priv, USBHS_HOST), struct usbhsh_hpriv, mod)
+
+#define __usbhsh_for_each_udev(start, pos, h, i)	\
+	for ((i) = start;						\
+	     ((i) < USBHSH_DEVICE_MAX) && ((pos) = (h)->udev + (i));	\
+	     (i)++)
+
+#define usbhsh_for_each_udev(pos, hpriv, i)	\
+	__usbhsh_for_each_udev(1, pos, hpriv, i)
+
+#define usbhsh_for_each_udev_with_dev0(pos, hpriv, i)	\
+	__usbhsh_for_each_udev(0, pos, hpriv, i)
+
+#define usbhsh_hcd_to_hpriv(h)	(struct usbhsh_hpriv *)((h)->hcd_priv)
+#define usbhsh_hcd_to_dev(h)	((h)->self.controller)
+
+#define usbhsh_hpriv_to_priv(h)	((h)->mod.priv)
+#define usbhsh_hpriv_to_dcp(h)	((h)->dcp)
+#define usbhsh_hpriv_to_hcd(h)	\
+	container_of((void *)h, struct usb_hcd, hcd_priv)
+
+#define usbhsh_ep_to_uep(u)	((u)->hcpriv)
+#define usbhsh_uep_to_pipe(u)	((u)->pipe)
+#define usbhsh_uep_to_udev(u)	((u)->udev)
+#define usbhsh_uep_to_ep(u)	((u)->ep)
+
+#define usbhsh_urb_to_ureq(u)	((u)->hcpriv)
+#define usbhsh_urb_to_usbv(u)	((u)->dev)
+
+#define usbhsh_usbv_to_udev(d)	dev_get_drvdata(&(d)->dev)
+
+#define usbhsh_udev_to_usbv(h)	((h)->usbv)
+#define usbhsh_udev_is_used(h)	usbhsh_udev_to_usbv(h)
+
+#define usbhsh_pipe_to_uep(p)	((p)->mod_private)
+
+#define usbhsh_device_parent(d)		(usbhsh_usbv_to_udev((d)->usbv->parent))
+#define usbhsh_device_hubport(d)	((d)->usbv->portnum)
+#define usbhsh_device_number(h, d)	((int)((d) - (h)->udev))
+#define usbhsh_device_nth(h, d)		((h)->udev + d)
+#define usbhsh_device0(h)		usbhsh_device_nth(h, 0)
+
+#define usbhsh_port_stat_init(h)	((h)->port_stat = 0)
+#define usbhsh_port_stat_set(h, s)	((h)->port_stat |= (s))
+#define usbhsh_port_stat_clear(h, s)	((h)->port_stat &= ~(s))
+#define usbhsh_port_stat_get(h)		((h)->port_stat)
+
+#define usbhsh_pkt_to_ureq(p)	\
+	container_of((void *)p, struct usbhsh_request, pkt)
+
+/*
+ *		req alloc/free
+ */
+static struct usbhsh_request *usbhsh_ureq_alloc(struct usbhsh_hpriv *hpriv,
+					       struct urb *urb,
+					       gfp_t mem_flags)
+{
+	struct usbhsh_request *ureq;
+
+	ureq = kzalloc(sizeof(struct usbhsh_request), mem_flags);
+	if (!ureq)
+		return NULL;
+
+	usbhs_pkt_init(&ureq->pkt);
+	ureq->urb = urb;
+	usbhsh_urb_to_ureq(urb) = ureq;
+
+	return ureq;
+}
+
+static void usbhsh_ureq_free(struct usbhsh_hpriv *hpriv,
+			    struct usbhsh_request *ureq)
+{
+	usbhsh_urb_to_ureq(ureq->urb) = NULL;
+	ureq->urb = NULL;
+
+	kfree(ureq);
+}
+
+/*
+ *		status
+ */
+static int usbhsh_is_running(struct usbhsh_hpriv *hpriv)
+{
+	/*
+	 * we can decide some device is attached or not
+	 * by checking mod.irq_attch
+	 * see
+	 *	usbhsh_irq_attch()
+	 *	usbhsh_irq_dtch()
+	 */
+	return (hpriv->mod.irq_attch == NULL);
+}
+
+/*
+ *		pipe control
+ */
+static void usbhsh_endpoint_sequence_save(struct usbhsh_hpriv *hpriv,
+					  struct urb *urb,
+					  struct usbhs_pkt *pkt)
+{
+	int len = urb->actual_length;
+	int maxp = usb_endpoint_maxp(&urb->ep->desc);
+	int t = 0;
+
+	/* DCP is out of sequence control */
+	if (usb_pipecontrol(urb->pipe))
+		return;
+
+	/*
+	 * renesas_usbhs pipe has a limitation in a number.
+	 * So, driver should re-use the limited pipe for each device/endpoint.
+	 * DATA0/1 sequence should be saved for it.
+	 * see [image of mod_host]
+	 *     [HARDWARE LIMITATION]
+	 */
+
+	/*
+	 * next sequence depends on actual_length
+	 *
+	 * ex) actual_length = 1147, maxp = 512
+	 * data0 : 512
+	 * data1 : 512
+	 * data0 : 123
+	 * data1 is the next sequence
+	 */
+	t = len / maxp;
+	if (len % maxp)
+		t++;
+	if (pkt->zero)
+		t++;
+	t %= 2;
+
+	if (t)
+		usb_dotoggle(urb->dev,
+			     usb_pipeendpoint(urb->pipe),
+			     usb_pipeout(urb->pipe));
+}
+
+static struct usbhsh_device *usbhsh_device_get(struct usbhsh_hpriv *hpriv,
+					       struct urb *urb);
+
+static int usbhsh_pipe_attach(struct usbhsh_hpriv *hpriv,
+			      struct urb *urb)
+{
+	struct usbhs_priv *priv = usbhsh_hpriv_to_priv(hpriv);
+	struct usbhsh_ep *uep = usbhsh_ep_to_uep(urb->ep);
+	struct usbhsh_device *udev = usbhsh_device_get(hpriv, urb);
+	struct usbhs_pipe *pipe;
+	struct usb_endpoint_descriptor *desc = &urb->ep->desc;
+	struct device *dev = usbhs_priv_to_dev(priv);
+	unsigned long flags;
+	int dir_in_req = !!usb_pipein(urb->pipe);
+	int is_dcp = usb_endpoint_xfer_control(desc);
+	int i, dir_in;
+	int ret = -EBUSY;
+
+	/********************  spin lock ********************/
+	usbhs_lock(priv, flags);
+
+	/*
+	 * if uep has been attached to pipe,
+	 * reuse it
+	 */
+	if (usbhsh_uep_to_pipe(uep)) {
+		ret = 0;
+		goto usbhsh_pipe_attach_done;
+	}
+
+	usbhs_for_each_pipe_with_dcp(pipe, priv, i) {
+
+		/* check pipe type */
+		if (!usbhs_pipe_type_is(pipe, usb_endpoint_type(desc)))
+			continue;
+
+		/* check pipe direction if normal pipe */
+		if (!is_dcp) {
+			dir_in = !!usbhs_pipe_is_dir_in(pipe);
+			if (0 != (dir_in - dir_in_req))
+				continue;
+		}
+
+		/* check pipe is free */
+		if (usbhsh_pipe_to_uep(pipe))
+			continue;
+
+		/*
+		 * attach pipe to uep
+		 *
+		 * usbhs_pipe_config_update() should be called after
+		 * usbhs_set_device_config()
+		 * see
+		 *  DCPMAXP/PIPEMAXP
+		 */
+		usbhsh_uep_to_pipe(uep)		= pipe;
+		usbhsh_pipe_to_uep(pipe)	= uep;
+
+		usbhs_pipe_config_update(pipe,
+					 usbhsh_device_number(hpriv, udev),
+					 usb_endpoint_num(desc),
+					 usb_endpoint_maxp(desc));
+
+		dev_dbg(dev, "%s [%d-%d(%s:%s)]\n", __func__,
+			usbhsh_device_number(hpriv, udev),
+			usb_endpoint_num(desc),
+			usbhs_pipe_name(pipe),
+			dir_in_req ? "in" : "out");
+
+		ret = 0;
+		break;
+	}
+
+usbhsh_pipe_attach_done:
+	if (0 == ret)
+		uep->counter++;
+
+	usbhs_unlock(priv, flags);
+	/********************  spin unlock ******************/
+
+	return ret;
+}
+
+static void usbhsh_pipe_detach(struct usbhsh_hpriv *hpriv,
+			       struct usbhsh_ep *uep)
+{
+	struct usbhs_priv *priv = usbhsh_hpriv_to_priv(hpriv);
+	struct usbhs_pipe *pipe;
+	struct device *dev = usbhs_priv_to_dev(priv);
+	unsigned long flags;
+
+	if (unlikely(!uep)) {
+		dev_err(dev, "no uep\n");
+		return;
+	}
+
+	/********************  spin lock ********************/
+	usbhs_lock(priv, flags);
+
+	pipe = usbhsh_uep_to_pipe(uep);
+
+	if (unlikely(!pipe)) {
+		dev_err(dev, "uep doens't have pipe\n");
+	} else if (1 == uep->counter--) { /* last user */
+		struct usb_host_endpoint *ep = usbhsh_uep_to_ep(uep);
+		struct usbhsh_device *udev = usbhsh_uep_to_udev(uep);
+
+		/* detach pipe from uep */
+		usbhsh_uep_to_pipe(uep)		= NULL;
+		usbhsh_pipe_to_uep(pipe)	= NULL;
+
+		dev_dbg(dev, "%s [%d-%d(%s)]\n", __func__,
+			usbhsh_device_number(hpriv, udev),
+			usb_endpoint_num(&ep->desc),
+			usbhs_pipe_name(pipe));
+	}
+
+	usbhs_unlock(priv, flags);
+	/********************  spin unlock ******************/
+}
+
+/*
+ *		endpoint control
+ */
+static int usbhsh_endpoint_attach(struct usbhsh_hpriv *hpriv,
+				  struct urb *urb,
+				  gfp_t mem_flags)
+{
+	struct usbhs_priv *priv = usbhsh_hpriv_to_priv(hpriv);
+	struct usbhsh_device *udev = usbhsh_device_get(hpriv, urb);
+	struct usb_host_endpoint *ep = urb->ep;
+	struct usbhsh_ep *uep;
+	struct device *dev = usbhs_priv_to_dev(priv);
+	struct usb_endpoint_descriptor *desc = &ep->desc;
+	unsigned long flags;
+
+	uep = kzalloc(sizeof(struct usbhsh_ep), mem_flags);
+	if (!uep)
+		return -ENOMEM;
+
+	/********************  spin lock ********************/
+	usbhs_lock(priv, flags);
+
+	/*
+	 * init endpoint
+	 */
+	uep->counter = 0;
+	INIT_LIST_HEAD(&uep->ep_list);
+	list_add_tail(&uep->ep_list, &udev->ep_list_head);
+
+	usbhsh_uep_to_udev(uep)	= udev;
+	usbhsh_uep_to_ep(uep)	= ep;
+	usbhsh_ep_to_uep(ep)	= uep;
+
+	usbhs_unlock(priv, flags);
+	/********************  spin unlock ******************/
+
+	dev_dbg(dev, "%s [%d-%d]\n", __func__,
+		usbhsh_device_number(hpriv, udev),
+		usb_endpoint_num(desc));
+
+	return 0;
+}
+
+static void usbhsh_endpoint_detach(struct usbhsh_hpriv *hpriv,
+				   struct usb_host_endpoint *ep)
+{
+	struct usbhs_priv *priv = usbhsh_hpriv_to_priv(hpriv);
+	struct device *dev = usbhs_priv_to_dev(priv);
+	struct usbhsh_ep *uep = usbhsh_ep_to_uep(ep);
+	unsigned long flags;
+
+	if (!uep)
+		return;
+
+	dev_dbg(dev, "%s [%d-%d]\n", __func__,
+		usbhsh_device_number(hpriv, usbhsh_uep_to_udev(uep)),
+		usb_endpoint_num(&ep->desc));
+
+	if (usbhsh_uep_to_pipe(uep))
+		usbhsh_pipe_detach(hpriv, uep);
+
+	/********************  spin lock ********************/
+	usbhs_lock(priv, flags);
+
+	/* remove this endpoint from udev */
+	list_del_init(&uep->ep_list);
+
+	usbhsh_uep_to_udev(uep)	= NULL;
+	usbhsh_uep_to_ep(uep)	= NULL;
+	usbhsh_ep_to_uep(ep)	= NULL;
+
+	usbhs_unlock(priv, flags);
+	/********************  spin unlock ******************/
+
+	kfree(uep);
+}
+
+static void usbhsh_endpoint_detach_all(struct usbhsh_hpriv *hpriv,
+				       struct usbhsh_device *udev)
+{
+	struct usbhsh_ep *uep, *next;
+
+	list_for_each_entry_safe(uep, next, &udev->ep_list_head, ep_list)
+		usbhsh_endpoint_detach(hpriv, usbhsh_uep_to_ep(uep));
+}
+
+/*
+ *		device control
+ */
+static int usbhsh_connected_to_rhdev(struct usb_hcd *hcd,
+				     struct usbhsh_device *udev)
+{
+	struct usb_device *usbv = usbhsh_udev_to_usbv(udev);
+
+	return hcd->self.root_hub == usbv->parent;
+}
+
+static int usbhsh_device_has_endpoint(struct usbhsh_device *udev)
+{
+	return !list_empty(&udev->ep_list_head);
+}
+
+static struct usbhsh_device *usbhsh_device_get(struct usbhsh_hpriv *hpriv,
+					       struct urb *urb)
+{
+	struct usb_device *usbv = usbhsh_urb_to_usbv(urb);
+	struct usbhsh_device *udev = usbhsh_usbv_to_udev(usbv);
+
+	/* usbhsh_device_attach() is still not called */
+	if (!udev)
+		return NULL;
+
+	/* if it is device0, return it */
+	if (0 == usb_pipedevice(urb->pipe))
+		return usbhsh_device0(hpriv);
+
+	/* return attached device */
+	return udev;
+}
+
+static struct usbhsh_device *usbhsh_device_attach(struct usbhsh_hpriv *hpriv,
+						 struct urb *urb)
+{
+	struct usbhsh_device *udev = NULL;
+	struct usbhsh_device *udev0 = usbhsh_device0(hpriv);
+	struct usbhsh_device *pos;
+	struct usb_hcd *hcd = usbhsh_hpriv_to_hcd(hpriv);
+	struct device *dev = usbhsh_hcd_to_dev(hcd);
+	struct usb_device *usbv = usbhsh_urb_to_usbv(urb);
+	struct usbhs_priv *priv = usbhsh_hpriv_to_priv(hpriv);
+	unsigned long flags;
+	u16 upphub, hubport;
+	int i;
+
+	/*
+	 * This function should be called only while urb is pointing to device0.
+	 * It will attach unused usbhsh_device to urb (usbv),
+	 * and initialize device0.
+	 * You can use usbhsh_device_get() to get "current" udev,
+	 * and usbhsh_usbv_to_udev() is for "attached" udev.
+	 */
+	if (0 != usb_pipedevice(urb->pipe)) {
+		dev_err(dev, "%s fail: urb isn't pointing device0\n", __func__);
+		return NULL;
+	}
+
+	/********************  spin lock ********************/
+	usbhs_lock(priv, flags);
+
+	/*
+	 * find unused device
+	 */
+	usbhsh_for_each_udev(pos, hpriv, i) {
+		if (usbhsh_udev_is_used(pos))
+			continue;
+		udev = pos;
+		break;
+	}
+
+	if (udev) {
+		/*
+		 * usbhsh_usbv_to_udev()
+		 * usbhsh_udev_to_usbv()
+		 * will be enable
+		 */
+		dev_set_drvdata(&usbv->dev, udev);
+		udev->usbv = usbv;
+	}
+
+	usbhs_unlock(priv, flags);
+	/********************  spin unlock ******************/
+
+	if (!udev) {
+		dev_err(dev, "no free usbhsh_device\n");
+		return NULL;
+	}
+
+	if (usbhsh_device_has_endpoint(udev)) {
+		dev_warn(dev, "udev have old endpoint\n");
+		usbhsh_endpoint_detach_all(hpriv, udev);
+	}
+
+	if (usbhsh_device_has_endpoint(udev0)) {
+		dev_warn(dev, "udev0 have old endpoint\n");
+		usbhsh_endpoint_detach_all(hpriv, udev0);
+	}
+
+	/* uep will be attached */
+	INIT_LIST_HEAD(&udev0->ep_list_head);
+	INIT_LIST_HEAD(&udev->ep_list_head);
+
+	/*
+	 * set device0 config
+	 */
+	usbhs_set_device_config(priv,
+				0, 0, 0, usbv->speed);
+
+	/*
+	 * set new device config
+	 */
+	upphub	= 0;
+	hubport	= 0;
+	if (!usbhsh_connected_to_rhdev(hcd, udev)) {
+		/* if udev is not connected to rhdev, it means parent is Hub */
+		struct usbhsh_device *parent = usbhsh_device_parent(udev);
+
+		upphub	= usbhsh_device_number(hpriv, parent);
+		hubport	= usbhsh_device_hubport(udev);
+
+		dev_dbg(dev, "%s connected to Hub [%d:%d](%p)\n", __func__,
+			upphub, hubport, parent);
+	}
+
+	usbhs_set_device_config(priv,
+			       usbhsh_device_number(hpriv, udev),
+			       upphub, hubport, usbv->speed);
+
+	dev_dbg(dev, "%s [%d](%p)\n", __func__,
+		usbhsh_device_number(hpriv, udev), udev);
+
+	return udev;
+}
+
+static void usbhsh_device_detach(struct usbhsh_hpriv *hpriv,
+			       struct usbhsh_device *udev)
+{
+	struct usb_hcd *hcd = usbhsh_hpriv_to_hcd(hpriv);
+	struct usbhs_priv *priv = usbhsh_hpriv_to_priv(hpriv);
+	struct device *dev = usbhsh_hcd_to_dev(hcd);
+	struct usb_device *usbv = usbhsh_udev_to_usbv(udev);
+	unsigned long flags;
+
+	dev_dbg(dev, "%s [%d](%p)\n", __func__,
+		usbhsh_device_number(hpriv, udev), udev);
+
+	if (usbhsh_device_has_endpoint(udev)) {
+		dev_warn(dev, "udev still have endpoint\n");
+		usbhsh_endpoint_detach_all(hpriv, udev);
+	}
+
+	/*
+	 * There is nothing to do if it is device0.
+	 * see
+	 *  usbhsh_device_attach()
+	 *  usbhsh_device_get()
+	 */
+	if (0 == usbhsh_device_number(hpriv, udev))
+		return;
+
+	/********************  spin lock ********************/
+	usbhs_lock(priv, flags);
+
+	/*
+	 * usbhsh_usbv_to_udev()
+	 * usbhsh_udev_to_usbv()
+	 * will be disable
+	 */
+	dev_set_drvdata(&usbv->dev, NULL);
+	udev->usbv = NULL;
+
+	usbhs_unlock(priv, flags);
+	/********************  spin unlock ******************/
+}
+
+/*
+ *		queue push/pop
+ */
+static void usbhsh_queue_done(struct usbhs_priv *priv, struct usbhs_pkt *pkt)
+{
+	struct usbhsh_request *ureq = usbhsh_pkt_to_ureq(pkt);
+	struct usbhsh_hpriv *hpriv = usbhsh_priv_to_hpriv(priv);
+	struct usb_hcd *hcd = usbhsh_hpriv_to_hcd(hpriv);
+	struct urb *urb = ureq->urb;
+	struct device *dev = usbhs_priv_to_dev(priv);
+	int status = 0;
+
+	dev_dbg(dev, "%s\n", __func__);
+
+	if (!urb) {
+		dev_warn(dev, "pkt doesn't have urb\n");
+		return;
+	}
+
+	if (!usbhsh_is_running(hpriv))
+		status = -ESHUTDOWN;
+
+	urb->actual_length = pkt->actual;
+
+	usbhsh_endpoint_sequence_save(hpriv, urb, pkt);
+	usbhsh_ureq_free(hpriv, ureq);
+
+	usbhsh_pipe_detach(hpriv, usbhsh_ep_to_uep(urb->ep));
+
+	usb_hcd_unlink_urb_from_ep(hcd, urb);
+	usb_hcd_giveback_urb(hcd, urb, status);
+}
+
+static int usbhsh_queue_push(struct usb_hcd *hcd,
+			     struct urb *urb,
+			     gfp_t mem_flags)
+{
+	struct usbhsh_hpriv *hpriv = usbhsh_hcd_to_hpriv(hcd);
+	struct usbhsh_ep *uep = usbhsh_ep_to_uep(urb->ep);
+	struct usbhs_pipe *pipe = usbhsh_uep_to_pipe(uep);
+	struct device *dev = usbhsh_hcd_to_dev(hcd);
+	struct usbhsh_request *ureq;
+	void *buf;
+	int len, sequence;
+
+	if (usb_pipeisoc(urb->pipe)) {
+		dev_err(dev, "pipe iso is not supported now\n");
+		return -EIO;
+	}
+
+	/* this ureq will be freed on usbhsh_queue_done() */
+	ureq = usbhsh_ureq_alloc(hpriv, urb, mem_flags);
+	if (unlikely(!ureq)) {
+		dev_err(dev, "ureq alloc fail\n");
+		return -ENOMEM;
+	}
+
+	if (usb_pipein(urb->pipe))
+		pipe->handler = &usbhs_fifo_dma_pop_handler;
+	else
+		pipe->handler = &usbhs_fifo_dma_push_handler;
+
+	buf = (void *)(urb->transfer_buffer + urb->actual_length);
+	len = urb->transfer_buffer_length - urb->actual_length;
+
+	sequence = usb_gettoggle(urb->dev,
+				 usb_pipeendpoint(urb->pipe),
+				 usb_pipeout(urb->pipe));
+
+	dev_dbg(dev, "%s\n", __func__);
+	usbhs_pkt_push(pipe, &ureq->pkt, usbhsh_queue_done,
+		       buf, len, (urb->transfer_flags & URB_ZERO_PACKET),
+		       sequence);
+
+	usbhs_pkt_start(pipe);
+
+	return 0;
+}
+
+static void usbhsh_queue_force_pop(struct usbhs_priv *priv,
+				   struct usbhs_pipe *pipe)
+{
+	struct usbhs_pkt *pkt;
+
+	while (1) {
+		pkt = usbhs_pkt_pop(pipe, NULL);
+		if (!pkt)
+			break;
+
+		/*
+		 * if all packet are gone, usbhsh_endpoint_disable()
+		 * will be called.
+		 * then, attached device/endpoint/pipe will be detached
+		 */
+		usbhsh_queue_done(priv, pkt);
+	}
+}
+
+static void usbhsh_queue_force_pop_all(struct usbhs_priv *priv)
+{
+	struct usbhs_pipe *pos;
+	int i;
+
+	usbhs_for_each_pipe_with_dcp(pos, priv, i)
+		usbhsh_queue_force_pop(priv, pos);
+}
+
+/*
+ *		DCP setup stage
+ */
+static int usbhsh_is_request_address(struct urb *urb)
+{
+	struct usb_ctrlrequest *req;
+
+	req = (struct usb_ctrlrequest *)urb->setup_packet;
+
+	if ((DeviceOutRequest    == req->bRequestType << 8) &&
+	    (USB_REQ_SET_ADDRESS == req->bRequest))
+		return 1;
+	else
+		return 0;
+}
+
+static void usbhsh_setup_stage_packet_push(struct usbhsh_hpriv *hpriv,
+					   struct urb *urb,
+					   struct usbhs_pipe *pipe)
+{
+	struct usbhs_priv *priv = usbhsh_hpriv_to_priv(hpriv);
+	struct usb_ctrlrequest req;
+	struct device *dev = usbhs_priv_to_dev(priv);
+
+	/*
+	 * wait setup packet ACK
+	 * see
+	 *	usbhsh_irq_setup_ack()
+	 *	usbhsh_irq_setup_err()
+	 */
+	init_completion(&hpriv->setup_ack_done);
+
+	/* copy original request */
+	memcpy(&req, urb->setup_packet, sizeof(struct usb_ctrlrequest));
+
+	/*
+	 * renesas_usbhs can not use original usb address.
+	 * see HARDWARE LIMITATION.
+	 * modify usb address here to use attached device.
+	 * see usbhsh_device_attach()
+	 */
+	if (usbhsh_is_request_address(urb)) {
+		struct usb_device *usbv = usbhsh_urb_to_usbv(urb);
+		struct usbhsh_device *udev = usbhsh_usbv_to_udev(usbv);
+
+		/* udev is a attached device */
+		req.wValue = usbhsh_device_number(hpriv, udev);
+		dev_dbg(dev, "create new address - %d\n", req.wValue);
+	}
+
+	/* set request */
+	usbhs_usbreq_set_val(priv, &req);
+
+	/*
+	 * wait setup packet ACK
+	 */
+	wait_for_completion(&hpriv->setup_ack_done);
+
+	dev_dbg(dev, "%s done\n", __func__);
+}
+
+/*
+ *		DCP data stage
+ */
+static void usbhsh_data_stage_packet_done(struct usbhs_priv *priv,
+					  struct usbhs_pkt *pkt)
+{
+	struct usbhsh_request *ureq = usbhsh_pkt_to_ureq(pkt);
+	struct usbhsh_hpriv *hpriv = usbhsh_priv_to_hpriv(priv);
+
+	/* this ureq was connected to urb when usbhsh_urb_enqueue()  */
+
+	usbhsh_ureq_free(hpriv, ureq);
+}
+
+static int usbhsh_data_stage_packet_push(struct usbhsh_hpriv *hpriv,
+					 struct urb *urb,
+					 struct usbhs_pipe *pipe,
+					 gfp_t mem_flags)
+
+{
+	struct usbhsh_request *ureq;
+
+	/* this ureq will be freed on usbhsh_data_stage_packet_done() */
+	ureq = usbhsh_ureq_alloc(hpriv, urb, mem_flags);
+	if (unlikely(!ureq))
+		return -ENOMEM;
+
+	if (usb_pipein(urb->pipe))
+		pipe->handler = &usbhs_dcp_data_stage_in_handler;
+	else
+		pipe->handler = &usbhs_dcp_data_stage_out_handler;
+
+	usbhs_pkt_push(pipe, &ureq->pkt,
+		       usbhsh_data_stage_packet_done,
+		       urb->transfer_buffer,
+		       urb->transfer_buffer_length,
+		       (urb->transfer_flags & URB_ZERO_PACKET),
+		       -1);
+
+	return 0;
+}
+
+/*
+ *		DCP status stage
+ */
+static int usbhsh_status_stage_packet_push(struct usbhsh_hpriv *hpriv,
+					    struct urb *urb,
+					    struct usbhs_pipe *pipe,
+					    gfp_t mem_flags)
+{
+	struct usbhsh_request *ureq;
+
+	/* This ureq will be freed on usbhsh_queue_done() */
+	ureq = usbhsh_ureq_alloc(hpriv, urb, mem_flags);
+	if (unlikely(!ureq))
+		return -ENOMEM;
+
+	if (usb_pipein(urb->pipe))
+		pipe->handler = &usbhs_dcp_status_stage_in_handler;
+	else
+		pipe->handler = &usbhs_dcp_status_stage_out_handler;
+
+	usbhs_pkt_push(pipe, &ureq->pkt,
+		       usbhsh_queue_done,
+		       NULL,
+		       urb->transfer_buffer_length,
+		       0, -1);
+
+	return 0;
+}
+
+static int usbhsh_dcp_queue_push(struct usb_hcd *hcd,
+				 struct urb *urb,
+				 gfp_t mflags)
+{
+	struct usbhsh_hpriv *hpriv = usbhsh_hcd_to_hpriv(hcd);
+	struct usbhsh_ep *uep = usbhsh_ep_to_uep(urb->ep);
+	struct usbhs_pipe *pipe = usbhsh_uep_to_pipe(uep);
+	struct device *dev = usbhsh_hcd_to_dev(hcd);
+	int ret;
+
+	dev_dbg(dev, "%s\n", __func__);
+
+	/*
+	 * setup stage
+	 *
+	 * usbhsh_send_setup_stage_packet() wait SACK/SIGN
+	 */
+	usbhsh_setup_stage_packet_push(hpriv, urb, pipe);
+
+	/*
+	 * data stage
+	 *
+	 * It is pushed only when urb has buffer.
+	 */
+	if (urb->transfer_buffer_length) {
+		ret = usbhsh_data_stage_packet_push(hpriv, urb, pipe, mflags);
+		if (ret < 0) {
+			dev_err(dev, "data stage failed\n");
+			return ret;
+		}
+	}
+
+	/*
+	 * status stage
+	 */
+	ret = usbhsh_status_stage_packet_push(hpriv, urb, pipe, mflags);
+	if (ret < 0) {
+		dev_err(dev, "status stage failed\n");
+		return ret;
+	}
+
+	/*
+	 * start pushed packets
+	 */
+	usbhs_pkt_start(pipe);
+
+	return 0;
+}
+
+/*
+ *		dma map functions
+ */
+static int usbhsh_dma_map_ctrl(struct device *dma_dev, struct usbhs_pkt *pkt,
+			       int map)
+{
+	if (map) {
+		struct usbhsh_request *ureq = usbhsh_pkt_to_ureq(pkt);
+		struct urb *urb = ureq->urb;
+
+		/* it can not use scatter/gather */
+		if (urb->num_sgs)
+			return -EINVAL;
+
+		pkt->dma = urb->transfer_dma;
+		if (!pkt->dma)
+			return -EINVAL;
+	}
+
+	return 0;
+}
+
+/*
+ *		for hc_driver
+ */
+static int usbhsh_host_start(struct usb_hcd *hcd)
+{
+	return 0;
+}
+
+static void usbhsh_host_stop(struct usb_hcd *hcd)
+{
+}
+
+static int usbhsh_urb_enqueue(struct usb_hcd *hcd,
+			      struct urb *urb,
+			      gfp_t mem_flags)
+{
+	struct usbhsh_hpriv *hpriv = usbhsh_hcd_to_hpriv(hcd);
+	struct usbhs_priv *priv = usbhsh_hpriv_to_priv(hpriv);
+	struct device *dev = usbhs_priv_to_dev(priv);
+	struct usb_host_endpoint *ep = urb->ep;
+	struct usbhsh_device *new_udev = NULL;
+	int is_dir_in = usb_pipein(urb->pipe);
+	int ret;
+
+	dev_dbg(dev, "%s (%s)\n", __func__, is_dir_in ? "in" : "out");
+
+	if (!usbhsh_is_running(hpriv)) {
+		ret = -EIO;
+		dev_err(dev, "host is not running\n");
+		goto usbhsh_urb_enqueue_error_not_linked;
+	}
+
+	ret = usb_hcd_link_urb_to_ep(hcd, urb);
+	if (ret) {
+		dev_err(dev, "urb link failed\n");
+		goto usbhsh_urb_enqueue_error_not_linked;
+	}
+
+	/*
+	 * attach udev if needed
+	 * see [image of mod_host]
+	 */
+	if (!usbhsh_device_get(hpriv, urb)) {
+		new_udev = usbhsh_device_attach(hpriv, urb);
+		if (!new_udev) {
+			ret = -EIO;
+			dev_err(dev, "device attach failed\n");
+			goto usbhsh_urb_enqueue_error_not_linked;
+		}
+	}
+
+	/*
+	 * attach endpoint if needed
+	 * see [image of mod_host]
+	 */
+	if (!usbhsh_ep_to_uep(ep)) {
+		ret = usbhsh_endpoint_attach(hpriv, urb, mem_flags);
+		if (ret < 0) {
+			dev_err(dev, "endpoint attach failed\n");
+			goto usbhsh_urb_enqueue_error_free_device;
+		}
+	}
+
+	/*
+	 * attach pipe to endpoint
+	 * see [image of mod_host]
+	 */
+	ret = usbhsh_pipe_attach(hpriv, urb);
+	if (ret < 0) {
+		dev_err(dev, "pipe attach failed\n");
+		goto usbhsh_urb_enqueue_error_free_endpoint;
+	}
+
+	/*
+	 * push packet
+	 */
+	if (usb_pipecontrol(urb->pipe))
+		ret = usbhsh_dcp_queue_push(hcd, urb, mem_flags);
+	else
+		ret = usbhsh_queue_push(hcd, urb, mem_flags);
+
+	return ret;
+
+usbhsh_urb_enqueue_error_free_endpoint:
+	usbhsh_endpoint_detach(hpriv, ep);
+usbhsh_urb_enqueue_error_free_device:
+	if (new_udev)
+		usbhsh_device_detach(hpriv, new_udev);
+usbhsh_urb_enqueue_error_not_linked:
+
+	dev_dbg(dev, "%s error\n", __func__);
+
+	return ret;
+}
+
+static int usbhsh_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status)
+{
+	struct usbhsh_hpriv *hpriv = usbhsh_hcd_to_hpriv(hcd);
+	struct usbhsh_request *ureq = usbhsh_urb_to_ureq(urb);
+
+	if (ureq) {
+		struct usbhs_priv *priv = usbhsh_hpriv_to_priv(hpriv);
+		struct usbhs_pkt *pkt = &ureq->pkt;
+
+		usbhs_pkt_pop(pkt->pipe, pkt);
+		usbhsh_queue_done(priv, pkt);
+	}
+
+	return 0;
+}
+
+static void usbhsh_endpoint_disable(struct usb_hcd *hcd,
+				    struct usb_host_endpoint *ep)
+{
+	struct usbhsh_ep *uep = usbhsh_ep_to_uep(ep);
+	struct usbhsh_device *udev;
+	struct usbhsh_hpriv *hpriv;
+
+	/*
+	 * this function might be called manytimes by same hcd/ep
+	 * in-endpoint == out-endpoint if ep == dcp.
+	 */
+	if (!uep)
+		return;
+
+	udev	= usbhsh_uep_to_udev(uep);
+	hpriv	= usbhsh_hcd_to_hpriv(hcd);
+
+	usbhsh_endpoint_detach(hpriv, ep);
+
+	/*
+	 * if there is no endpoint,
+	 * free device
+	 */
+	if (!usbhsh_device_has_endpoint(udev))
+		usbhsh_device_detach(hpriv, udev);
+}
+
+static int usbhsh_hub_status_data(struct usb_hcd *hcd, char *buf)
+{
+	struct usbhsh_hpriv *hpriv = usbhsh_hcd_to_hpriv(hcd);
+	int roothub_id = 1; /* only 1 root hub */
+
+	/*
+	 * does port stat was changed ?
+	 * check USB_PORT_STAT_C_xxx << 16
+	 */
+	if (usbhsh_port_stat_get(hpriv) & 0xFFFF0000)
+		*buf = (1 << roothub_id);
+	else
+		*buf = 0;
+
+	return !!(*buf);
+}
+
+static int __usbhsh_hub_hub_feature(struct usbhsh_hpriv *hpriv,
+				    u16 typeReq, u16 wValue,
+				    u16 wIndex, char *buf, u16 wLength)
+{
+	struct usbhs_priv *priv = usbhsh_hpriv_to_priv(hpriv);
+	struct device *dev = usbhs_priv_to_dev(priv);
+
+	switch (wValue) {
+	case C_HUB_OVER_CURRENT:
+	case C_HUB_LOCAL_POWER:
+		dev_dbg(dev, "%s :: C_HUB_xx\n", __func__);
+		return 0;
+	}
+
+	return -EPIPE;
+}
+
+static int __usbhsh_hub_port_feature(struct usbhsh_hpriv *hpriv,
+				     u16 typeReq, u16 wValue,
+				     u16 wIndex, char *buf, u16 wLength)
+{
+	struct usbhs_priv *priv = usbhsh_hpriv_to_priv(hpriv);
+	struct device *dev = usbhs_priv_to_dev(priv);
+	int enable = (typeReq == SetPortFeature);
+	int speed, i, timeout = 128;
+	int roothub_id = 1; /* only 1 root hub */
+
+	/* common error */
+	if (wIndex > roothub_id || wLength != 0)
+		return -EPIPE;
+
+	/* check wValue */
+	switch (wValue) {
+	case USB_PORT_FEAT_POWER:
+		usbhs_vbus_ctrl(priv, enable);
+		dev_dbg(dev, "%s :: USB_PORT_FEAT_POWER\n", __func__);
+		break;
+
+	case USB_PORT_FEAT_ENABLE:
+	case USB_PORT_FEAT_SUSPEND:
+	case USB_PORT_FEAT_C_ENABLE:
+	case USB_PORT_FEAT_C_SUSPEND:
+	case USB_PORT_FEAT_C_CONNECTION:
+	case USB_PORT_FEAT_C_OVER_CURRENT:
+	case USB_PORT_FEAT_C_RESET:
+		dev_dbg(dev, "%s :: USB_PORT_FEAT_xxx\n", __func__);
+		break;
+
+	case USB_PORT_FEAT_RESET:
+		if (!enable)
+			break;
+
+		usbhsh_port_stat_clear(hpriv,
+				       USB_PORT_STAT_HIGH_SPEED |
+				       USB_PORT_STAT_LOW_SPEED);
+
+		usbhsh_queue_force_pop_all(priv);
+
+		usbhs_bus_send_reset(priv);
+		msleep(20);
+		usbhs_bus_send_sof_enable(priv);
+
+		for (i = 0; i < timeout ; i++) {
+			switch (usbhs_bus_get_speed(priv)) {
+			case USB_SPEED_LOW:
+				speed = USB_PORT_STAT_LOW_SPEED;
+				goto got_usb_bus_speed;
+			case USB_SPEED_HIGH:
+				speed = USB_PORT_STAT_HIGH_SPEED;
+				goto got_usb_bus_speed;
+			case USB_SPEED_FULL:
+				speed = 0;
+				goto got_usb_bus_speed;
+			}
+
+			msleep(20);
+		}
+		return -EPIPE;
+
+got_usb_bus_speed:
+		usbhsh_port_stat_set(hpriv, speed);
+		usbhsh_port_stat_set(hpriv, USB_PORT_STAT_ENABLE);
+
+		dev_dbg(dev, "%s :: USB_PORT_FEAT_RESET (speed = %d)\n",
+			__func__, speed);
+
+		/* status change is not needed */
+		return 0;
+
+	default:
+		return -EPIPE;
+	}
+
+	/* set/clear status */
+	if (enable)
+		usbhsh_port_stat_set(hpriv, (1 << wValue));
+	else
+		usbhsh_port_stat_clear(hpriv, (1 << wValue));
+
+	return 0;
+}
+
+static int __usbhsh_hub_get_status(struct usbhsh_hpriv *hpriv,
+				   u16 typeReq, u16 wValue,
+				   u16 wIndex, char *buf, u16 wLength)
+{
+	struct usbhs_priv *priv = usbhsh_hpriv_to_priv(hpriv);
+	struct usb_hub_descriptor *desc = (struct usb_hub_descriptor *)buf;
+	struct device *dev = usbhs_priv_to_dev(priv);
+	int roothub_id = 1; /* only 1 root hub */
+
+	switch (typeReq) {
+	case GetHubStatus:
+		dev_dbg(dev, "%s :: GetHubStatus\n", __func__);
+
+		*buf = 0x00;
+		break;
+
+	case GetPortStatus:
+		if (wIndex != roothub_id)
+			return -EPIPE;
+
+		dev_dbg(dev, "%s :: GetPortStatus\n", __func__);
+		*(__le32 *)buf = cpu_to_le32(usbhsh_port_stat_get(hpriv));
+		break;
+
+	case GetHubDescriptor:
+		desc->bDescriptorType		= USB_DT_HUB;
+		desc->bHubContrCurrent		= 0;
+		desc->bNbrPorts			= roothub_id;
+		desc->bDescLength		= 9;
+		desc->bPwrOn2PwrGood		= 0;
+		desc->wHubCharacteristics	=
+			cpu_to_le16(HUB_CHAR_INDV_PORT_LPSM | HUB_CHAR_NO_OCPM);
+		desc->u.hs.DeviceRemovable[0]	= (roothub_id << 1);
+		desc->u.hs.DeviceRemovable[1]	= ~0;
+		dev_dbg(dev, "%s :: GetHubDescriptor\n", __func__);
+		break;
+	}
+
+	return 0;
+}
+
+static int usbhsh_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
+			      u16 wIndex, char *buf, u16 wLength)
+{
+	struct usbhsh_hpriv *hpriv = usbhsh_hcd_to_hpriv(hcd);
+	struct usbhs_priv *priv = usbhsh_hpriv_to_priv(hpriv);
+	struct device *dev = usbhs_priv_to_dev(priv);
+	int ret = -EPIPE;
+
+	switch (typeReq) {
+
+	/* Hub Feature */
+	case ClearHubFeature:
+	case SetHubFeature:
+		ret = __usbhsh_hub_hub_feature(hpriv, typeReq,
+					       wValue, wIndex, buf, wLength);
+		break;
+
+	/* Port Feature */
+	case SetPortFeature:
+	case ClearPortFeature:
+		ret = __usbhsh_hub_port_feature(hpriv, typeReq,
+						wValue, wIndex, buf, wLength);
+		break;
+
+	/* Get status */
+	case GetHubStatus:
+	case GetPortStatus:
+	case GetHubDescriptor:
+		ret = __usbhsh_hub_get_status(hpriv, typeReq,
+					      wValue, wIndex, buf, wLength);
+		break;
+	}
+
+	dev_dbg(dev, "typeReq = %x, ret = %d, port_stat = %x\n",
+		typeReq, ret, usbhsh_port_stat_get(hpriv));
+
+	return ret;
+}
+
+static int usbhsh_bus_nop(struct usb_hcd *hcd)
+{
+	/* nothing to do */
+	return 0;
+}
+
+static const struct hc_driver usbhsh_driver = {
+	.description =		usbhsh_hcd_name,
+	.hcd_priv_size =	sizeof(struct usbhsh_hpriv),
+
+	/*
+	 * generic hardware linkage
+	 */
+	.flags =		HCD_USB2,
+
+	.start =		usbhsh_host_start,
+	.stop =			usbhsh_host_stop,
+
+	/*
+	 * managing i/o requests and associated device resources
+	 */
+	.urb_enqueue =		usbhsh_urb_enqueue,
+	.urb_dequeue =		usbhsh_urb_dequeue,
+	.endpoint_disable =	usbhsh_endpoint_disable,
+
+	/*
+	 * root hub
+	 */
+	.hub_status_data =	usbhsh_hub_status_data,
+	.hub_control =		usbhsh_hub_control,
+	.bus_suspend =		usbhsh_bus_nop,
+	.bus_resume =		usbhsh_bus_nop,
+};
+
+/*
+ *		interrupt functions
+ */
+static int usbhsh_irq_attch(struct usbhs_priv *priv,
+			    struct usbhs_irq_state *irq_state)
+{
+	struct usbhsh_hpriv *hpriv = usbhsh_priv_to_hpriv(priv);
+	struct device *dev = usbhs_priv_to_dev(priv);
+
+	dev_dbg(dev, "device attached\n");
+
+	usbhsh_port_stat_set(hpriv, USB_PORT_STAT_CONNECTION);
+	usbhsh_port_stat_set(hpriv, USB_PORT_STAT_C_CONNECTION << 16);
+
+	/*
+	 * attch interrupt might happen infinitely on some device
+	 * (on self power USB hub ?)
+	 * disable it here.
+	 *
+	 * usbhsh_is_running() becomes effective
+	 * according to this process.
+	 * see
+	 *	usbhsh_is_running()
+	 *	usbhsh_urb_enqueue()
+	 */
+	hpriv->mod.irq_attch = NULL;
+	usbhs_irq_callback_update(priv, &hpriv->mod);
+
+	return 0;
+}
+
+static int usbhsh_irq_dtch(struct usbhs_priv *priv,
+			   struct usbhs_irq_state *irq_state)
+{
+	struct usbhsh_hpriv *hpriv = usbhsh_priv_to_hpriv(priv);
+	struct device *dev = usbhs_priv_to_dev(priv);
+
+	dev_dbg(dev, "device detached\n");
+
+	usbhsh_port_stat_clear(hpriv, USB_PORT_STAT_CONNECTION);
+	usbhsh_port_stat_set(hpriv, USB_PORT_STAT_C_CONNECTION << 16);
+
+	/*
+	 * enable attch interrupt again
+	 *
+	 * usbhsh_is_running() becomes invalid
+	 * according to this process.
+	 * see
+	 *	usbhsh_is_running()
+	 *	usbhsh_urb_enqueue()
+	 */
+	hpriv->mod.irq_attch = usbhsh_irq_attch;
+	usbhs_irq_callback_update(priv, &hpriv->mod);
+
+	/*
+	 * usbhsh_queue_force_pop_all() should be called
+	 * after usbhsh_is_running() becomes invalid.
+	 */
+	usbhsh_queue_force_pop_all(priv);
+
+	return 0;
+}
+
+static int usbhsh_irq_setup_ack(struct usbhs_priv *priv,
+				struct usbhs_irq_state *irq_state)
+{
+	struct usbhsh_hpriv *hpriv = usbhsh_priv_to_hpriv(priv);
+	struct device *dev = usbhs_priv_to_dev(priv);
+
+	dev_dbg(dev, "setup packet OK\n");
+
+	complete(&hpriv->setup_ack_done); /* see usbhsh_urb_enqueue() */
+
+	return 0;
+}
+
+static int usbhsh_irq_setup_err(struct usbhs_priv *priv,
+				struct usbhs_irq_state *irq_state)
+{
+	struct usbhsh_hpriv *hpriv = usbhsh_priv_to_hpriv(priv);
+	struct device *dev = usbhs_priv_to_dev(priv);
+
+	dev_dbg(dev, "setup packet Err\n");
+
+	complete(&hpriv->setup_ack_done); /* see usbhsh_urb_enqueue() */
+
+	return 0;
+}
+
+/*
+ *		module start/stop
+ */
+static void usbhsh_pipe_init_for_host(struct usbhs_priv *priv)
+{
+	struct usbhsh_hpriv *hpriv = usbhsh_priv_to_hpriv(priv);
+	struct usbhs_pipe *pipe;
+	struct renesas_usbhs_driver_pipe_config *pipe_configs =
+					usbhs_get_dparam(priv, pipe_configs);
+	int pipe_size = usbhs_get_dparam(priv, pipe_size);
+	int old_type, dir_in, i;
+
+	/* init all pipe */
+	old_type = USB_ENDPOINT_XFER_CONTROL;
+	for (i = 0; i < pipe_size; i++) {
+
+		/*
+		 * data "output" will be finished as soon as possible,
+		 * but there is no guaranty at data "input" case.
+		 *
+		 * "input" needs "standby" pipe.
+		 * So, "input" direction pipe > "output" direction pipe
+		 * is good idea.
+		 *
+		 * 1st USB_ENDPOINT_XFER_xxx will be output direction,
+		 * and the other will be input direction here.
+		 *
+		 * ex)
+		 * ...
+		 * USB_ENDPOINT_XFER_ISOC -> dir out
+		 * USB_ENDPOINT_XFER_ISOC -> dir in
+		 * USB_ENDPOINT_XFER_BULK -> dir out
+		 * USB_ENDPOINT_XFER_BULK -> dir in
+		 * USB_ENDPOINT_XFER_BULK -> dir in
+		 * ...
+		 */
+		dir_in = (pipe_configs[i].type == old_type);
+		old_type = pipe_configs[i].type;
+
+		if (USB_ENDPOINT_XFER_CONTROL == pipe_configs[i].type) {
+			pipe = usbhs_dcp_malloc(priv);
+			usbhsh_hpriv_to_dcp(hpriv) = pipe;
+		} else {
+			pipe = usbhs_pipe_malloc(priv,
+						 pipe_configs[i].type,
+						 dir_in);
+		}
+
+		pipe->mod_private = NULL;
+	}
+}
+
+static int usbhsh_start(struct usbhs_priv *priv)
+{
+	struct usbhsh_hpriv *hpriv = usbhsh_priv_to_hpriv(priv);
+	struct usb_hcd *hcd = usbhsh_hpriv_to_hcd(hpriv);
+	struct usbhs_mod *mod = usbhs_mod_get_current(priv);
+	struct device *dev = usbhs_priv_to_dev(priv);
+	int ret;
+
+	/* add hcd */
+	ret = usb_add_hcd(hcd, 0, 0);
+	if (ret < 0)
+		return 0;
+	device_wakeup_enable(hcd->self.controller);
+
+	/*
+	 * pipe initialize and enable DCP
+	 */
+	usbhs_fifo_init(priv);
+	usbhs_pipe_init(priv,
+			usbhsh_dma_map_ctrl);
+	usbhsh_pipe_init_for_host(priv);
+
+	/*
+	 * system config enble
+	 * - HI speed
+	 * - host
+	 * - usb module
+	 */
+	usbhs_sys_host_ctrl(priv, 1);
+
+	/*
+	 * enable irq callback
+	 */
+	mod->irq_attch		= usbhsh_irq_attch;
+	mod->irq_dtch		= usbhsh_irq_dtch;
+	mod->irq_sack		= usbhsh_irq_setup_ack;
+	mod->irq_sign		= usbhsh_irq_setup_err;
+	usbhs_irq_callback_update(priv, mod);
+
+	dev_dbg(dev, "start host\n");
+
+	return ret;
+}
+
+static int usbhsh_stop(struct usbhs_priv *priv)
+{
+	struct usbhsh_hpriv *hpriv = usbhsh_priv_to_hpriv(priv);
+	struct usb_hcd *hcd = usbhsh_hpriv_to_hcd(hpriv);
+	struct usbhs_mod *mod = usbhs_mod_get_current(priv);
+	struct device *dev = usbhs_priv_to_dev(priv);
+
+	/*
+	 * disable irq callback
+	 */
+	mod->irq_attch	= NULL;
+	mod->irq_dtch	= NULL;
+	mod->irq_sack	= NULL;
+	mod->irq_sign	= NULL;
+	usbhs_irq_callback_update(priv, mod);
+
+	usb_remove_hcd(hcd);
+
+	/* disable sys */
+	usbhs_sys_host_ctrl(priv, 0);
+
+	dev_dbg(dev, "quit host\n");
+
+	return 0;
+}
+
+int usbhs_mod_host_probe(struct usbhs_priv *priv)
+{
+	struct usbhsh_hpriv *hpriv;
+	struct usb_hcd *hcd;
+	struct usbhsh_device *udev;
+	struct device *dev = usbhs_priv_to_dev(priv);
+	int i;
+
+	/* initialize hcd */
+	hcd = usb_create_hcd(&usbhsh_driver, dev, usbhsh_hcd_name);
+	if (!hcd) {
+		dev_err(dev, "Failed to create hcd\n");
+		return -ENOMEM;
+	}
+	hcd->has_tt = 1; /* for low/full speed */
+
+	/*
+	 * CAUTION
+	 *
+	 * There is no guarantee that it is possible to access usb module here.
+	 * Don't accesses to it.
+	 * The accesse will be enable after "usbhsh_start"
+	 */
+
+	hpriv = usbhsh_hcd_to_hpriv(hcd);
+
+	/*
+	 * register itself
+	 */
+	usbhs_mod_register(priv, &hpriv->mod, USBHS_HOST);
+
+	/* init hpriv */
+	hpriv->mod.name		= "host";
+	hpriv->mod.start	= usbhsh_start;
+	hpriv->mod.stop		= usbhsh_stop;
+	usbhsh_port_stat_init(hpriv);
+
+	/* init all device */
+	usbhsh_for_each_udev_with_dev0(udev, hpriv, i) {
+		udev->usbv	= NULL;
+		INIT_LIST_HEAD(&udev->ep_list_head);
+	}
+
+	dev_info(dev, "host probed\n");
+
+	return 0;
+}
+
+int usbhs_mod_host_remove(struct usbhs_priv *priv)
+{
+	struct usbhsh_hpriv *hpriv = usbhsh_priv_to_hpriv(priv);
+	struct usb_hcd *hcd = usbhsh_hpriv_to_hcd(hpriv);
+
+	usb_put_hcd(hcd);
+
+	return 0;
+}
diff --git a/drivers/usb/renesas_usbhs/pipe.c b/drivers/usb/renesas_usbhs/pipe.c
new file mode 100644
index 0000000..c4922b9
--- /dev/null
+++ b/drivers/usb/renesas_usbhs/pipe.c
@@ -0,0 +1,834 @@
+// SPDX-License-Identifier: GPL-1.0+
+/*
+ * Renesas USB driver
+ *
+ * Copyright (C) 2011 Renesas Solutions Corp.
+ * Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
+ */
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include "common.h"
+#include "pipe.h"
+
+/*
+ *		macros
+ */
+#define usbhsp_addr_offset(p)	((usbhs_pipe_number(p) - 1) * 2)
+
+#define usbhsp_flags_set(p, f)	((p)->flags |=  USBHS_PIPE_FLAGS_##f)
+#define usbhsp_flags_clr(p, f)	((p)->flags &= ~USBHS_PIPE_FLAGS_##f)
+#define usbhsp_flags_has(p, f)	((p)->flags &   USBHS_PIPE_FLAGS_##f)
+#define usbhsp_flags_init(p)	do {(p)->flags = 0; } while (0)
+
+/*
+ * for debug
+ */
+static char *usbhsp_pipe_name[] = {
+	[USB_ENDPOINT_XFER_CONTROL]	= "DCP",
+	[USB_ENDPOINT_XFER_BULK]	= "BULK",
+	[USB_ENDPOINT_XFER_INT]		= "INT",
+	[USB_ENDPOINT_XFER_ISOC]	= "ISO",
+};
+
+char *usbhs_pipe_name(struct usbhs_pipe *pipe)
+{
+	return usbhsp_pipe_name[usbhs_pipe_type(pipe)];
+}
+
+static struct renesas_usbhs_driver_pipe_config
+*usbhsp_get_pipe_config(struct usbhs_priv *priv, int pipe_num)
+{
+	struct renesas_usbhs_driver_pipe_config *pipe_configs =
+					usbhs_get_dparam(priv, pipe_configs);
+
+	return &pipe_configs[pipe_num];
+}
+
+/*
+ *		DCPCTR/PIPEnCTR functions
+ */
+static void usbhsp_pipectrl_set(struct usbhs_pipe *pipe, u16 mask, u16 val)
+{
+	struct usbhs_priv *priv = usbhs_pipe_to_priv(pipe);
+	int offset = usbhsp_addr_offset(pipe);
+
+	if (usbhs_pipe_is_dcp(pipe))
+		usbhs_bset(priv, DCPCTR, mask, val);
+	else
+		usbhs_bset(priv, PIPEnCTR + offset, mask, val);
+}
+
+static u16 usbhsp_pipectrl_get(struct usbhs_pipe *pipe)
+{
+	struct usbhs_priv *priv = usbhs_pipe_to_priv(pipe);
+	int offset = usbhsp_addr_offset(pipe);
+
+	if (usbhs_pipe_is_dcp(pipe))
+		return usbhs_read(priv, DCPCTR);
+	else
+		return usbhs_read(priv, PIPEnCTR + offset);
+}
+
+/*
+ *		DCP/PIPE functions
+ */
+static void __usbhsp_pipe_xxx_set(struct usbhs_pipe *pipe,
+				  u16 dcp_reg, u16 pipe_reg,
+				  u16 mask, u16 val)
+{
+	struct usbhs_priv *priv = usbhs_pipe_to_priv(pipe);
+
+	if (usbhs_pipe_is_dcp(pipe))
+		usbhs_bset(priv, dcp_reg, mask, val);
+	else
+		usbhs_bset(priv, pipe_reg, mask, val);
+}
+
+static u16 __usbhsp_pipe_xxx_get(struct usbhs_pipe *pipe,
+				 u16 dcp_reg, u16 pipe_reg)
+{
+	struct usbhs_priv *priv = usbhs_pipe_to_priv(pipe);
+
+	if (usbhs_pipe_is_dcp(pipe))
+		return usbhs_read(priv, dcp_reg);
+	else
+		return usbhs_read(priv, pipe_reg);
+}
+
+/*
+ *		DCPCFG/PIPECFG functions
+ */
+static void usbhsp_pipe_cfg_set(struct usbhs_pipe *pipe, u16 mask, u16 val)
+{
+	__usbhsp_pipe_xxx_set(pipe, DCPCFG, PIPECFG, mask, val);
+}
+
+static u16 usbhsp_pipe_cfg_get(struct usbhs_pipe *pipe)
+{
+	return __usbhsp_pipe_xxx_get(pipe, DCPCFG, PIPECFG);
+}
+
+/*
+ *		PIPEnTRN/PIPEnTRE functions
+ */
+static void usbhsp_pipe_trn_set(struct usbhs_pipe *pipe, u16 mask, u16 val)
+{
+	struct usbhs_priv *priv = usbhs_pipe_to_priv(pipe);
+	struct device *dev = usbhs_priv_to_dev(priv);
+	int num = usbhs_pipe_number(pipe);
+	u16 reg;
+
+	/*
+	 * It is impossible to calculate address,
+	 * since PIPEnTRN addresses were mapped randomly.
+	 */
+#define CASE_PIPExTRN(a)		\
+	case 0x ## a:			\
+		reg = PIPE ## a ## TRN;	\
+		break;
+
+	switch (num) {
+	CASE_PIPExTRN(1);
+	CASE_PIPExTRN(2);
+	CASE_PIPExTRN(3);
+	CASE_PIPExTRN(4);
+	CASE_PIPExTRN(5);
+	CASE_PIPExTRN(B);
+	CASE_PIPExTRN(C);
+	CASE_PIPExTRN(D);
+	CASE_PIPExTRN(E);
+	CASE_PIPExTRN(F);
+	CASE_PIPExTRN(9);
+	CASE_PIPExTRN(A);
+	default:
+		dev_err(dev, "unknown pipe (%d)\n", num);
+		return;
+	}
+	__usbhsp_pipe_xxx_set(pipe, 0, reg, mask, val);
+}
+
+static void usbhsp_pipe_tre_set(struct usbhs_pipe *pipe, u16 mask, u16 val)
+{
+	struct usbhs_priv *priv = usbhs_pipe_to_priv(pipe);
+	struct device *dev = usbhs_priv_to_dev(priv);
+	int num = usbhs_pipe_number(pipe);
+	u16 reg;
+
+	/*
+	 * It is impossible to calculate address,
+	 * since PIPEnTRE addresses were mapped randomly.
+	 */
+#define CASE_PIPExTRE(a)			\
+	case 0x ## a:				\
+		reg = PIPE ## a ## TRE;		\
+		break;
+
+	switch (num) {
+	CASE_PIPExTRE(1);
+	CASE_PIPExTRE(2);
+	CASE_PIPExTRE(3);
+	CASE_PIPExTRE(4);
+	CASE_PIPExTRE(5);
+	CASE_PIPExTRE(B);
+	CASE_PIPExTRE(C);
+	CASE_PIPExTRE(D);
+	CASE_PIPExTRE(E);
+	CASE_PIPExTRE(F);
+	CASE_PIPExTRE(9);
+	CASE_PIPExTRE(A);
+	default:
+		dev_err(dev, "unknown pipe (%d)\n", num);
+		return;
+	}
+
+	__usbhsp_pipe_xxx_set(pipe, 0, reg, mask, val);
+}
+
+/*
+ *		PIPEBUF
+ */
+static void usbhsp_pipe_buf_set(struct usbhs_pipe *pipe, u16 mask, u16 val)
+{
+	if (usbhs_pipe_is_dcp(pipe))
+		return;
+
+	__usbhsp_pipe_xxx_set(pipe, 0, PIPEBUF, mask, val);
+}
+
+/*
+ *		DCPMAXP/PIPEMAXP
+ */
+static void usbhsp_pipe_maxp_set(struct usbhs_pipe *pipe, u16 mask, u16 val)
+{
+	__usbhsp_pipe_xxx_set(pipe, DCPMAXP, PIPEMAXP, mask, val);
+}
+
+/*
+ *		pipe control functions
+ */
+static void usbhsp_pipe_select(struct usbhs_pipe *pipe)
+{
+	struct usbhs_priv *priv = usbhs_pipe_to_priv(pipe);
+
+	/*
+	 * On pipe, this is necessary before
+	 * accesses to below registers.
+	 *
+	 * PIPESEL	: usbhsp_pipe_select
+	 * PIPECFG	: usbhsp_pipe_cfg_xxx
+	 * PIPEBUF	: usbhsp_pipe_buf_xxx
+	 * PIPEMAXP	: usbhsp_pipe_maxp_xxx
+	 * PIPEPERI
+	 */
+
+	/*
+	 * if pipe is dcp, no pipe is selected.
+	 * it is no problem, because dcp have its register
+	 */
+	usbhs_write(priv, PIPESEL, 0xF & usbhs_pipe_number(pipe));
+}
+
+static int usbhsp_pipe_barrier(struct usbhs_pipe *pipe)
+{
+	struct usbhs_priv *priv = usbhs_pipe_to_priv(pipe);
+	int timeout = 1024;
+	u16 mask = usbhs_mod_is_host(priv) ? (CSSTS | PID_MASK) : PID_MASK;
+
+	/*
+	 * make sure....
+	 *
+	 * Modify these bits when CSSTS = 0, PID = NAK, and no pipe number is
+	 * specified by the CURPIPE bits.
+	 * When changing the setting of this bit after changing
+	 * the PID bits for the selected pipe from BUF to NAK,
+	 * check that CSSTS = 0 and PBUSY = 0.
+	 */
+
+	/*
+	 * CURPIPE bit = 0
+	 *
+	 * see also
+	 *  "Operation"
+	 *  - "Pipe Control"
+	 *   - "Pipe Control Registers Switching Procedure"
+	 */
+	usbhs_write(priv, CFIFOSEL, 0);
+	usbhs_pipe_disable(pipe);
+
+	do {
+		if (!(usbhsp_pipectrl_get(pipe) & mask))
+			return 0;
+
+		udelay(10);
+
+	} while (timeout--);
+
+	return -EBUSY;
+}
+
+int usbhs_pipe_is_accessible(struct usbhs_pipe *pipe)
+{
+	u16 val;
+
+	val = usbhsp_pipectrl_get(pipe);
+	if (val & BSTS)
+		return 0;
+
+	return -EBUSY;
+}
+
+/*
+ *		PID ctrl
+ */
+static void __usbhsp_pid_try_nak_if_stall(struct usbhs_pipe *pipe)
+{
+	u16 pid = usbhsp_pipectrl_get(pipe);
+
+	pid &= PID_MASK;
+
+	/*
+	 * see
+	 * "Pipe n Control Register" - "PID"
+	 */
+	switch (pid) {
+	case PID_STALL11:
+		usbhsp_pipectrl_set(pipe, PID_MASK, PID_STALL10);
+		/* fall-through */
+	case PID_STALL10:
+		usbhsp_pipectrl_set(pipe, PID_MASK, PID_NAK);
+	}
+}
+
+void usbhs_pipe_disable(struct usbhs_pipe *pipe)
+{
+	int timeout = 1024;
+	u16 val;
+
+	/* see "Pipe n Control Register" - "PID" */
+	__usbhsp_pid_try_nak_if_stall(pipe);
+
+	usbhsp_pipectrl_set(pipe, PID_MASK, PID_NAK);
+
+	do {
+		val  = usbhsp_pipectrl_get(pipe);
+		val &= PBUSY;
+		if (!val)
+			break;
+
+		udelay(10);
+	} while (timeout--);
+}
+
+void usbhs_pipe_enable(struct usbhs_pipe *pipe)
+{
+	/* see "Pipe n Control Register" - "PID" */
+	__usbhsp_pid_try_nak_if_stall(pipe);
+
+	usbhsp_pipectrl_set(pipe, PID_MASK, PID_BUF);
+}
+
+void usbhs_pipe_stall(struct usbhs_pipe *pipe)
+{
+	u16 pid = usbhsp_pipectrl_get(pipe);
+
+	pid &= PID_MASK;
+
+	/*
+	 * see
+	 * "Pipe n Control Register" - "PID"
+	 */
+	switch (pid) {
+	case PID_NAK:
+		usbhsp_pipectrl_set(pipe, PID_MASK, PID_STALL10);
+		break;
+	case PID_BUF:
+		usbhsp_pipectrl_set(pipe, PID_MASK, PID_STALL11);
+		break;
+	}
+}
+
+int usbhs_pipe_is_stall(struct usbhs_pipe *pipe)
+{
+	u16 pid = usbhsp_pipectrl_get(pipe) & PID_MASK;
+
+	return (int)(pid == PID_STALL10 || pid == PID_STALL11);
+}
+
+void usbhs_pipe_set_trans_count_if_bulk(struct usbhs_pipe *pipe, int len)
+{
+	if (!usbhs_pipe_type_is(pipe, USB_ENDPOINT_XFER_BULK))
+		return;
+
+	/*
+	 * clear and disable transfer counter for IN/OUT pipe
+	 */
+	usbhsp_pipe_tre_set(pipe, TRCLR | TRENB, TRCLR);
+
+	/*
+	 * Only IN direction bulk pipe can use transfer count.
+	 * Without using this function,
+	 * received data will break if it was large data size.
+	 * see PIPEnTRN/PIPEnTRE for detail
+	 */
+	if (usbhs_pipe_is_dir_in(pipe)) {
+		int maxp = usbhs_pipe_get_maxpacket(pipe);
+
+		usbhsp_pipe_trn_set(pipe, 0xffff, DIV_ROUND_UP(len, maxp));
+		usbhsp_pipe_tre_set(pipe, TRENB, TRENB); /* enable */
+	}
+}
+
+
+/*
+ *		pipe setup
+ */
+static int usbhsp_setup_pipecfg(struct usbhs_pipe *pipe, int is_host,
+				int dir_in, u16 *pipecfg)
+{
+	u16 type = 0;
+	u16 bfre = 0;
+	u16 dblb = 0;
+	u16 cntmd = 0;
+	u16 dir = 0;
+	u16 epnum = 0;
+	u16 shtnak = 0;
+	static const u16 type_array[] = {
+		[USB_ENDPOINT_XFER_BULK] = TYPE_BULK,
+		[USB_ENDPOINT_XFER_INT]  = TYPE_INT,
+		[USB_ENDPOINT_XFER_ISOC] = TYPE_ISO,
+	};
+
+	if (usbhs_pipe_is_dcp(pipe))
+		return -EINVAL;
+
+	/*
+	 * PIPECFG
+	 *
+	 * see
+	 *  - "Register Descriptions" - "PIPECFG" register
+	 *  - "Features"  - "Pipe configuration"
+	 *  - "Operation" - "Pipe Control"
+	 */
+
+	/* TYPE */
+	type = type_array[usbhs_pipe_type(pipe)];
+
+	/* BFRE */
+	if (usbhs_pipe_type_is(pipe, USB_ENDPOINT_XFER_ISOC) ||
+	    usbhs_pipe_type_is(pipe, USB_ENDPOINT_XFER_BULK))
+		bfre = 0; /* FIXME */
+
+	/* DBLB: see usbhs_pipe_config_update() */
+
+	/* CNTMD */
+	if (usbhs_pipe_type_is(pipe, USB_ENDPOINT_XFER_BULK))
+		cntmd = 0; /* FIXME */
+
+	/* DIR */
+	if (dir_in)
+		usbhsp_flags_set(pipe, IS_DIR_HOST);
+
+	if (!!is_host ^ !!dir_in)
+		dir |= DIR_OUT;
+
+	if (!dir)
+		usbhsp_flags_set(pipe, IS_DIR_IN);
+
+	/* SHTNAK */
+	if (usbhs_pipe_type_is(pipe, USB_ENDPOINT_XFER_BULK) &&
+	    !dir)
+		shtnak = SHTNAK;
+
+	/* EPNUM */
+	epnum = 0; /* see usbhs_pipe_config_update() */
+	*pipecfg = type		|
+		   bfre		|
+		   dblb		|
+		   cntmd	|
+		   dir		|
+		   shtnak	|
+		   epnum;
+	return 0;
+}
+
+static u16 usbhsp_setup_pipebuff(struct usbhs_pipe *pipe)
+{
+	struct usbhs_priv *priv = usbhs_pipe_to_priv(pipe);
+	struct device *dev = usbhs_priv_to_dev(priv);
+	int pipe_num = usbhs_pipe_number(pipe);
+	u16 buff_size;
+	u16 bufnmb;
+	u16 bufnmb_cnt;
+	struct renesas_usbhs_driver_pipe_config *pipe_config =
+					usbhsp_get_pipe_config(priv, pipe_num);
+
+	/*
+	 * PIPEBUF
+	 *
+	 * see
+	 *  - "Register Descriptions" - "PIPEBUF" register
+	 *  - "Features"  - "Pipe configuration"
+	 *  - "Operation" - "FIFO Buffer Memory"
+	 *  - "Operation" - "Pipe Control"
+	 */
+	buff_size = pipe_config->bufsize;
+	bufnmb = pipe_config->bufnum;
+
+	/* change buff_size to register value */
+	bufnmb_cnt = (buff_size / 64) - 1;
+
+	dev_dbg(dev, "pipe : %d : buff_size 0x%x: bufnmb 0x%x\n",
+		pipe_num, buff_size, bufnmb);
+
+	return	(0x1f & bufnmb_cnt)	<< 10 |
+		(0xff & bufnmb)		<<  0;
+}
+
+void usbhs_pipe_config_update(struct usbhs_pipe *pipe, u16 devsel,
+			      u16 epnum, u16 maxp)
+{
+	struct usbhs_priv *priv = usbhs_pipe_to_priv(pipe);
+	int pipe_num = usbhs_pipe_number(pipe);
+	struct renesas_usbhs_driver_pipe_config *pipe_config =
+					usbhsp_get_pipe_config(priv, pipe_num);
+	u16 dblb = pipe_config->double_buf ? DBLB : 0;
+
+	if (devsel > 0xA) {
+		struct device *dev = usbhs_priv_to_dev(priv);
+
+		dev_err(dev, "devsel error %d\n", devsel);
+
+		devsel = 0;
+	}
+
+	usbhsp_pipe_barrier(pipe);
+
+	pipe->maxp = maxp;
+
+	usbhsp_pipe_select(pipe);
+	usbhsp_pipe_maxp_set(pipe, 0xFFFF,
+			     (devsel << 12) |
+			     maxp);
+
+	if (!usbhs_pipe_is_dcp(pipe))
+		usbhsp_pipe_cfg_set(pipe,  0x000F | DBLB, epnum | dblb);
+}
+
+/*
+ *		pipe control
+ */
+int usbhs_pipe_get_maxpacket(struct usbhs_pipe *pipe)
+{
+	/*
+	 * see
+	 *	usbhs_pipe_config_update()
+	 *	usbhs_dcp_malloc()
+	 */
+	return pipe->maxp;
+}
+
+int usbhs_pipe_is_dir_in(struct usbhs_pipe *pipe)
+{
+	return usbhsp_flags_has(pipe, IS_DIR_IN);
+}
+
+int usbhs_pipe_is_dir_host(struct usbhs_pipe *pipe)
+{
+	return usbhsp_flags_has(pipe, IS_DIR_HOST);
+}
+
+int usbhs_pipe_is_running(struct usbhs_pipe *pipe)
+{
+	return usbhsp_flags_has(pipe, IS_RUNNING);
+}
+
+void usbhs_pipe_running(struct usbhs_pipe *pipe, int running)
+{
+	if (running)
+		usbhsp_flags_set(pipe, IS_RUNNING);
+	else
+		usbhsp_flags_clr(pipe, IS_RUNNING);
+}
+
+void usbhs_pipe_data_sequence(struct usbhs_pipe *pipe, int sequence)
+{
+	u16 mask = (SQCLR | SQSET);
+	u16 val;
+
+	/*
+	 * sequence
+	 *  0  : data0
+	 *  1  : data1
+	 *  -1 : no change
+	 */
+	switch (sequence) {
+	case 0:
+		val = SQCLR;
+		break;
+	case 1:
+		val = SQSET;
+		break;
+	default:
+		return;
+	}
+
+	usbhsp_pipectrl_set(pipe, mask, val);
+}
+
+static int usbhs_pipe_get_data_sequence(struct usbhs_pipe *pipe)
+{
+	return !!(usbhsp_pipectrl_get(pipe) & SQMON);
+}
+
+void usbhs_pipe_clear(struct usbhs_pipe *pipe)
+{
+	if (usbhs_pipe_is_dcp(pipe)) {
+		usbhs_fifo_clear_dcp(pipe);
+	} else {
+		usbhsp_pipectrl_set(pipe, ACLRM, ACLRM);
+		usbhsp_pipectrl_set(pipe, ACLRM, 0);
+	}
+}
+
+/* Should call usbhsp_pipe_select() before */
+void usbhs_pipe_clear_without_sequence(struct usbhs_pipe *pipe,
+				       int needs_bfre, int bfre_enable)
+{
+	int sequence;
+
+	usbhsp_pipe_select(pipe);
+	sequence = usbhs_pipe_get_data_sequence(pipe);
+	if (needs_bfre)
+		usbhsp_pipe_cfg_set(pipe, BFRE, bfre_enable ? BFRE : 0);
+	usbhs_pipe_clear(pipe);
+	usbhs_pipe_data_sequence(pipe, sequence);
+}
+
+void usbhs_pipe_config_change_bfre(struct usbhs_pipe *pipe, int enable)
+{
+	if (usbhs_pipe_is_dcp(pipe))
+		return;
+
+	usbhsp_pipe_select(pipe);
+	/* check if the driver needs to change the BFRE value */
+	if (!(enable ^ !!(usbhsp_pipe_cfg_get(pipe) & BFRE)))
+		return;
+
+	usbhs_pipe_clear_without_sequence(pipe, 1, enable);
+}
+
+static struct usbhs_pipe *usbhsp_get_pipe(struct usbhs_priv *priv, u32 type)
+{
+	struct usbhs_pipe *pos, *pipe;
+	int i;
+
+	/*
+	 * find target pipe
+	 */
+	pipe = NULL;
+	usbhs_for_each_pipe_with_dcp(pos, priv, i) {
+		if (!usbhs_pipe_type_is(pos, type))
+			continue;
+		if (usbhsp_flags_has(pos, IS_USED))
+			continue;
+
+		pipe = pos;
+		break;
+	}
+
+	if (!pipe)
+		return NULL;
+
+	/*
+	 * initialize pipe flags
+	 */
+	usbhsp_flags_init(pipe);
+	usbhsp_flags_set(pipe, IS_USED);
+
+	return pipe;
+}
+
+static void usbhsp_put_pipe(struct usbhs_pipe *pipe)
+{
+	usbhsp_flags_init(pipe);
+}
+
+void usbhs_pipe_init(struct usbhs_priv *priv,
+		     int (*dma_map_ctrl)(struct device *dma_dev,
+					 struct usbhs_pkt *pkt, int map))
+{
+	struct usbhs_pipe_info *info = usbhs_priv_to_pipeinfo(priv);
+	struct usbhs_pipe *pipe;
+	int i;
+
+	usbhs_for_each_pipe_with_dcp(pipe, priv, i) {
+		usbhsp_flags_init(pipe);
+		pipe->fifo = NULL;
+		pipe->mod_private = NULL;
+		INIT_LIST_HEAD(&pipe->list);
+
+		/* pipe force init */
+		usbhs_pipe_clear(pipe);
+	}
+
+	info->dma_map_ctrl = dma_map_ctrl;
+}
+
+struct usbhs_pipe *usbhs_pipe_malloc(struct usbhs_priv *priv,
+				     int endpoint_type,
+				     int dir_in)
+{
+	struct device *dev = usbhs_priv_to_dev(priv);
+	struct usbhs_pipe *pipe;
+	int is_host = usbhs_mod_is_host(priv);
+	int ret;
+	u16 pipecfg, pipebuf;
+
+	pipe = usbhsp_get_pipe(priv, endpoint_type);
+	if (!pipe) {
+		dev_err(dev, "can't get pipe (%s)\n",
+			usbhsp_pipe_name[endpoint_type]);
+		return NULL;
+	}
+
+	INIT_LIST_HEAD(&pipe->list);
+
+	usbhs_pipe_disable(pipe);
+
+	/* make sure pipe is not busy */
+	ret = usbhsp_pipe_barrier(pipe);
+	if (ret < 0) {
+		dev_err(dev, "pipe setup failed %d\n", usbhs_pipe_number(pipe));
+		return NULL;
+	}
+
+	if (usbhsp_setup_pipecfg(pipe, is_host, dir_in, &pipecfg)) {
+		dev_err(dev, "can't setup pipe\n");
+		return NULL;
+	}
+
+	pipebuf  = usbhsp_setup_pipebuff(pipe);
+
+	usbhsp_pipe_select(pipe);
+	usbhsp_pipe_cfg_set(pipe, 0xFFFF, pipecfg);
+	usbhsp_pipe_buf_set(pipe, 0xFFFF, pipebuf);
+	usbhs_pipe_clear(pipe);
+
+	usbhs_pipe_sequence_data0(pipe);
+
+	dev_dbg(dev, "enable pipe %d : %s (%s)\n",
+		usbhs_pipe_number(pipe),
+		usbhs_pipe_name(pipe),
+		usbhs_pipe_is_dir_in(pipe) ? "in" : "out");
+
+	/*
+	 * epnum / maxp are still not set to this pipe.
+	 * call usbhs_pipe_config_update() after this function !!
+	 */
+
+	return pipe;
+}
+
+void usbhs_pipe_free(struct usbhs_pipe *pipe)
+{
+	usbhsp_put_pipe(pipe);
+}
+
+void usbhs_pipe_select_fifo(struct usbhs_pipe *pipe, struct usbhs_fifo *fifo)
+{
+	if (pipe->fifo)
+		pipe->fifo->pipe = NULL;
+
+	pipe->fifo = fifo;
+
+	if (fifo)
+		fifo->pipe = pipe;
+}
+
+
+/*
+ *		dcp control
+ */
+struct usbhs_pipe *usbhs_dcp_malloc(struct usbhs_priv *priv)
+{
+	struct usbhs_pipe *pipe;
+
+	pipe = usbhsp_get_pipe(priv, USB_ENDPOINT_XFER_CONTROL);
+	if (!pipe)
+		return NULL;
+
+	INIT_LIST_HEAD(&pipe->list);
+
+	/*
+	 * call usbhs_pipe_config_update() after this function !!
+	 */
+
+	return pipe;
+}
+
+void usbhs_dcp_control_transfer_done(struct usbhs_pipe *pipe)
+{
+	struct usbhs_priv *priv = usbhs_pipe_to_priv(pipe);
+
+	WARN_ON(!usbhs_pipe_is_dcp(pipe));
+
+	usbhs_pipe_enable(pipe);
+
+	if (!usbhs_mod_is_host(priv)) /* funconly */
+		usbhsp_pipectrl_set(pipe, CCPL, CCPL);
+}
+
+void usbhs_dcp_dir_for_host(struct usbhs_pipe *pipe, int dir_out)
+{
+	usbhsp_pipe_cfg_set(pipe, DIR_OUT,
+			    dir_out ? DIR_OUT : 0);
+}
+
+/*
+ *		pipe module function
+ */
+int usbhs_pipe_probe(struct usbhs_priv *priv)
+{
+	struct usbhs_pipe_info *info = usbhs_priv_to_pipeinfo(priv);
+	struct usbhs_pipe *pipe;
+	struct device *dev = usbhs_priv_to_dev(priv);
+	struct renesas_usbhs_driver_pipe_config *pipe_configs =
+					usbhs_get_dparam(priv, pipe_configs);
+	int pipe_size = usbhs_get_dparam(priv, pipe_size);
+	int i;
+
+	/* This driver expects 1st pipe is DCP */
+	if (pipe_configs[0].type != USB_ENDPOINT_XFER_CONTROL) {
+		dev_err(dev, "1st PIPE is not DCP\n");
+		return -EINVAL;
+	}
+
+	info->pipe = kcalloc(pipe_size, sizeof(struct usbhs_pipe),
+			     GFP_KERNEL);
+	if (!info->pipe)
+		return -ENOMEM;
+
+	info->size = pipe_size;
+
+	/*
+	 * init pipe
+	 */
+	usbhs_for_each_pipe_with_dcp(pipe, priv, i) {
+		pipe->priv = priv;
+
+		usbhs_pipe_type(pipe) =
+			pipe_configs[i].type & USB_ENDPOINT_XFERTYPE_MASK;
+
+		dev_dbg(dev, "pipe %x\t: %s\n",
+			i, usbhsp_pipe_name[pipe_configs[i].type]);
+	}
+
+	return 0;
+}
+
+void usbhs_pipe_remove(struct usbhs_priv *priv)
+{
+	struct usbhs_pipe_info *info = usbhs_priv_to_pipeinfo(priv);
+
+	kfree(info->pipe);
+}
diff --git a/drivers/usb/renesas_usbhs/pipe.h b/drivers/usb/renesas_usbhs/pipe.h
new file mode 100644
index 0000000..3080423
--- /dev/null
+++ b/drivers/usb/renesas_usbhs/pipe.h
@@ -0,0 +1,116 @@
+// SPDX-License-Identifier: GPL-1.0+
+/*
+ * Renesas USB driver
+ *
+ * Copyright (C) 2011 Renesas Solutions Corp.
+ * Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
+ */
+#ifndef RENESAS_USB_PIPE_H
+#define RENESAS_USB_PIPE_H
+
+#include "common.h"
+#include "fifo.h"
+
+/*
+ *	struct
+ */
+struct usbhs_pipe {
+	u32 pipe_type;	/* USB_ENDPOINT_XFER_xxx */
+
+	struct usbhs_priv *priv;
+	struct usbhs_fifo *fifo;
+	struct list_head list;
+
+	int maxp;
+
+	u32 flags;
+#define USBHS_PIPE_FLAGS_IS_USED		(1 << 0)
+#define USBHS_PIPE_FLAGS_IS_DIR_IN		(1 << 1)
+#define USBHS_PIPE_FLAGS_IS_DIR_HOST		(1 << 2)
+#define USBHS_PIPE_FLAGS_IS_RUNNING		(1 << 3)
+
+	const struct usbhs_pkt_handle *handler;
+
+	void *mod_private;
+};
+
+struct usbhs_pipe_info {
+	struct usbhs_pipe *pipe;
+	int size;	/* array size of "pipe" */
+
+	int (*dma_map_ctrl)(struct device *dma_dev, struct usbhs_pkt *pkt,
+			    int map);
+};
+
+/*
+ * pipe list
+ */
+#define __usbhs_for_each_pipe(start, pos, info, i)	\
+	for ((i) = start;						\
+	     ((i) < (info)->size) && ((pos) = (info)->pipe + (i));	\
+	     (i)++)
+
+#define usbhs_for_each_pipe(pos, priv, i)			\
+	__usbhs_for_each_pipe(1, pos, &((priv)->pipe_info), i)
+
+#define usbhs_for_each_pipe_with_dcp(pos, priv, i)		\
+	__usbhs_for_each_pipe(0, pos, &((priv)->pipe_info), i)
+
+/*
+ * data
+ */
+#define usbhs_priv_to_pipeinfo(pr)	(&(pr)->pipe_info)
+
+/*
+ * pipe control
+ */
+char *usbhs_pipe_name(struct usbhs_pipe *pipe);
+struct usbhs_pipe
+*usbhs_pipe_malloc(struct usbhs_priv *priv, int endpoint_type, int dir_in);
+void usbhs_pipe_free(struct usbhs_pipe *pipe);
+int usbhs_pipe_probe(struct usbhs_priv *priv);
+void usbhs_pipe_remove(struct usbhs_priv *priv);
+int usbhs_pipe_is_dir_in(struct usbhs_pipe *pipe);
+int usbhs_pipe_is_dir_host(struct usbhs_pipe *pipe);
+int usbhs_pipe_is_running(struct usbhs_pipe *pipe);
+void usbhs_pipe_running(struct usbhs_pipe *pipe, int running);
+
+void usbhs_pipe_init(struct usbhs_priv *priv,
+		     int (*dma_map_ctrl)(struct device *dma_dev,
+					 struct usbhs_pkt *pkt, int map));
+int usbhs_pipe_get_maxpacket(struct usbhs_pipe *pipe);
+void usbhs_pipe_clear(struct usbhs_pipe *pipe);
+void usbhs_pipe_clear_without_sequence(struct usbhs_pipe *pipe,
+				       int needs_bfre, int bfre_enable);
+int usbhs_pipe_is_accessible(struct usbhs_pipe *pipe);
+void usbhs_pipe_enable(struct usbhs_pipe *pipe);
+void usbhs_pipe_disable(struct usbhs_pipe *pipe);
+void usbhs_pipe_stall(struct usbhs_pipe *pipe);
+int usbhs_pipe_is_stall(struct usbhs_pipe *pipe);
+void usbhs_pipe_set_trans_count_if_bulk(struct usbhs_pipe *pipe, int len);
+void usbhs_pipe_select_fifo(struct usbhs_pipe *pipe, struct usbhs_fifo *fifo);
+void usbhs_pipe_config_update(struct usbhs_pipe *pipe, u16 devsel,
+			      u16 epnum, u16 maxp);
+void usbhs_pipe_config_change_bfre(struct usbhs_pipe *pipe, int enable);
+
+#define usbhs_pipe_sequence_data0(pipe)	usbhs_pipe_data_sequence(pipe, 0)
+#define usbhs_pipe_sequence_data1(pipe)	usbhs_pipe_data_sequence(pipe, 1)
+void usbhs_pipe_data_sequence(struct usbhs_pipe *pipe, int data);
+
+#define usbhs_pipe_to_priv(p)	((p)->priv)
+#define usbhs_pipe_number(p)	(int)((p) - (p)->priv->pipe_info.pipe)
+#define usbhs_pipe_is_dcp(p)	((p)->priv->pipe_info.pipe == (p))
+#define usbhs_pipe_to_fifo(p)	((p)->fifo)
+#define usbhs_pipe_is_busy(p)	usbhs_pipe_to_fifo(p)
+
+#define usbhs_pipe_type(p)		((p)->pipe_type)
+#define usbhs_pipe_type_is(p, t)	((p)->pipe_type == t)
+
+/*
+ * dcp control
+ */
+struct usbhs_pipe *usbhs_dcp_malloc(struct usbhs_priv *priv);
+void usbhs_dcp_control_transfer_done(struct usbhs_pipe *pipe);
+void usbhs_dcp_dir_for_host(struct usbhs_pipe *pipe, int dir_out);
+
+#endif /* RENESAS_USB_PIPE_H */
diff --git a/drivers/usb/renesas_usbhs/rcar2.c b/drivers/usb/renesas_usbhs/rcar2.c
new file mode 100644
index 0000000..0027092
--- /dev/null
+++ b/drivers/usb/renesas_usbhs/rcar2.c
@@ -0,0 +1,75 @@
+// SPDX-License-Identifier: GPL-1.0+
+/*
+ * Renesas USB driver R-Car Gen. 2 initialization and power control
+ *
+ * Copyright (C) 2014 Ulrich Hecht
+ */
+
+#include <linux/gpio.h>
+#include <linux/of_gpio.h>
+#include <linux/phy/phy.h>
+#include "common.h"
+#include "rcar2.h"
+
+static int usbhs_rcar2_hardware_init(struct platform_device *pdev)
+{
+	struct usbhs_priv *priv = usbhs_pdev_to_priv(pdev);
+
+	if (IS_ENABLED(CONFIG_GENERIC_PHY)) {
+		struct phy *phy = phy_get(&pdev->dev, "usb");
+
+		if (IS_ERR(phy))
+			return PTR_ERR(phy);
+
+		priv->phy = phy;
+		return 0;
+	}
+
+	return -ENXIO;
+}
+
+static int usbhs_rcar2_hardware_exit(struct platform_device *pdev)
+{
+	struct usbhs_priv *priv = usbhs_pdev_to_priv(pdev);
+
+	if (priv->phy) {
+		phy_put(priv->phy);
+		priv->phy = NULL;
+	}
+
+	return 0;
+}
+
+static int usbhs_rcar2_power_ctrl(struct platform_device *pdev,
+				void __iomem *base, int enable)
+{
+	struct usbhs_priv *priv = usbhs_pdev_to_priv(pdev);
+	int retval = -ENODEV;
+
+	if (priv->phy) {
+		if (enable) {
+			retval = phy_init(priv->phy);
+
+			if (!retval)
+				retval = phy_power_on(priv->phy);
+		} else {
+			phy_power_off(priv->phy);
+			phy_exit(priv->phy);
+			retval = 0;
+		}
+	}
+
+	return retval;
+}
+
+static int usbhs_rcar2_get_id(struct platform_device *pdev)
+{
+	return USBHS_GADGET;
+}
+
+const struct renesas_usbhs_platform_callback usbhs_rcar2_ops = {
+	.hardware_init = usbhs_rcar2_hardware_init,
+	.hardware_exit = usbhs_rcar2_hardware_exit,
+	.power_ctrl = usbhs_rcar2_power_ctrl,
+	.get_id = usbhs_rcar2_get_id,
+};
diff --git a/drivers/usb/renesas_usbhs/rcar2.h b/drivers/usb/renesas_usbhs/rcar2.h
new file mode 100644
index 0000000..45e3526
--- /dev/null
+++ b/drivers/usb/renesas_usbhs/rcar2.h
@@ -0,0 +1,5 @@
+// SPDX-License-Identifier: GPL-2.0
+#include "common.h"
+
+extern const struct renesas_usbhs_platform_callback
+	usbhs_rcar2_ops;
diff --git a/drivers/usb/renesas_usbhs/rcar3.c b/drivers/usb/renesas_usbhs/rcar3.c
new file mode 100644
index 0000000..d0ea4ff
--- /dev/null
+++ b/drivers/usb/renesas_usbhs/rcar3.c
@@ -0,0 +1,134 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Renesas USB driver R-Car Gen. 3 initialization and power control
+ *
+ * Copyright (C) 2016 Renesas Electronics Corporation
+ */
+
+#include <linux/delay.h>
+#include <linux/io.h>
+#include "common.h"
+#include "rcar3.h"
+
+#define LPSTS		0x102
+#define UGCTRL		0x180	/* 32-bit register */
+#define UGCTRL2		0x184	/* 32-bit register */
+#define UGSTS		0x188	/* 32-bit register */
+
+/* Low Power Status register (LPSTS) */
+#define LPSTS_SUSPM	0x4000
+
+/* R-Car D3 only: USB General control register (UGCTRL) */
+#define UGCTRL_PLLRESET		0x00000001
+#define UGCTRL_CONNECT		0x00000004
+
+/*
+ * USB General control register 2 (UGCTRL2)
+ * Remarks: bit[31:11] and bit[9:6] should be 0
+ */
+#define UGCTRL2_RESERVED_3	0x00000001	/* bit[3:0] should be B'0001 */
+#define UGCTRL2_USB0SEL_EHCI	0x00000010
+#define UGCTRL2_USB0SEL_HSUSB	0x00000020
+#define UGCTRL2_USB0SEL_OTG	0x00000030
+#define UGCTRL2_VBUSSEL		0x00000400
+
+/* R-Car D3 only: USB General status register (UGSTS) */
+#define UGSTS_LOCK		0x00000100
+
+static void usbhs_write32(struct usbhs_priv *priv, u32 reg, u32 data)
+{
+	iowrite32(data, priv->base + reg);
+}
+
+static u32 usbhs_read32(struct usbhs_priv *priv, u32 reg)
+{
+	return ioread32(priv->base + reg);
+}
+
+static void usbhs_rcar3_set_ugctrl2(struct usbhs_priv *priv, u32 val)
+{
+	usbhs_write32(priv, UGCTRL2, val | UGCTRL2_RESERVED_3);
+}
+
+static void usbhs_rcar3_set_usbsel(struct usbhs_priv *priv, bool ehci)
+{
+	if (ehci)
+		usbhs_rcar3_set_ugctrl2(priv, UGCTRL2_USB0SEL_EHCI);
+	else
+		usbhs_rcar3_set_ugctrl2(priv, UGCTRL2_USB0SEL_HSUSB);
+}
+
+static int usbhs_rcar3_power_ctrl(struct platform_device *pdev,
+				void __iomem *base, int enable)
+{
+	struct usbhs_priv *priv = usbhs_pdev_to_priv(pdev);
+
+	usbhs_rcar3_set_ugctrl2(priv, UGCTRL2_USB0SEL_OTG | UGCTRL2_VBUSSEL);
+
+	if (enable) {
+		usbhs_bset(priv, LPSTS, LPSTS_SUSPM, LPSTS_SUSPM);
+		/* The controller on R-Car Gen3 needs to wait up to 45 usec */
+		udelay(45);
+	} else {
+		usbhs_bset(priv, LPSTS, LPSTS_SUSPM, 0);
+	}
+
+	return 0;
+}
+
+/* R-Car D3 needs to release UGCTRL.PLLRESET */
+static int usbhs_rcar3_power_and_pll_ctrl(struct platform_device *pdev,
+					  void __iomem *base, int enable)
+{
+	struct usbhs_priv *priv = usbhs_pdev_to_priv(pdev);
+	u32 val;
+	int timeout = 1000;
+	bool is_host = false;
+
+	if (enable) {
+		usbhs_write32(priv, UGCTRL, 0);	/* release PLLRESET */
+		if (priv->edev)
+			is_host = extcon_get_state(priv->edev, EXTCON_USB_HOST);
+
+		usbhs_rcar3_set_usbsel(priv, is_host);
+
+		usbhs_bset(priv, LPSTS, LPSTS_SUSPM, LPSTS_SUSPM);
+		do {
+			val = usbhs_read32(priv, UGSTS);
+			udelay(1);
+		} while (!(val & UGSTS_LOCK) && timeout--);
+		usbhs_write32(priv, UGCTRL, UGCTRL_CONNECT);
+	} else {
+		usbhs_write32(priv, UGCTRL, 0);
+		usbhs_bset(priv, LPSTS, LPSTS_SUSPM, 0);
+		usbhs_write32(priv, UGCTRL, UGCTRL_PLLRESET);
+	}
+
+	return 0;
+}
+
+static int usbhs_rcar3_get_id(struct platform_device *pdev)
+{
+	return USBHS_GADGET;
+}
+
+static int usbhs_rcar3_notifier(struct notifier_block *nb, unsigned long event,
+				void *data)
+{
+	struct usbhs_priv *priv = container_of(nb, struct usbhs_priv, nb);
+
+	usbhs_rcar3_set_usbsel(priv, !!event);
+
+	return NOTIFY_DONE;
+}
+
+const struct renesas_usbhs_platform_callback usbhs_rcar3_ops = {
+	.power_ctrl = usbhs_rcar3_power_ctrl,
+	.get_id = usbhs_rcar3_get_id,
+};
+
+const struct renesas_usbhs_platform_callback usbhs_rcar3_with_pll_ops = {
+	.power_ctrl = usbhs_rcar3_power_and_pll_ctrl,
+	.get_id = usbhs_rcar3_get_id,
+	.notifier = usbhs_rcar3_notifier,
+};
diff --git a/drivers/usb/renesas_usbhs/rcar3.h b/drivers/usb/renesas_usbhs/rcar3.h
new file mode 100644
index 0000000..49e535a
--- /dev/null
+++ b/drivers/usb/renesas_usbhs/rcar3.h
@@ -0,0 +1,5 @@
+// SPDX-License-Identifier: GPL-2.0
+#include "common.h"
+
+extern const struct renesas_usbhs_platform_callback usbhs_rcar3_ops;
+extern const struct renesas_usbhs_platform_callback usbhs_rcar3_with_pll_ops;
diff --git a/drivers/usb/renesas_usbhs/rza.c b/drivers/usb/renesas_usbhs/rza.c
new file mode 100644
index 0000000..5b28725
--- /dev/null
+++ b/drivers/usb/renesas_usbhs/rza.c
@@ -0,0 +1,52 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Renesas USB driver RZ/A initialization and power control
+ *
+ * Copyright (C) 2018 Chris Brandt
+ * Copyright (C) 2018 Renesas Electronics Corporation
+ */
+
+#include <linux/delay.h>
+#include <linux/io.h>
+#include <linux/of_device.h>
+#include "common.h"
+#include "rza.h"
+
+static int usbhs_rza1_hardware_init(struct platform_device *pdev)
+{
+	struct usbhs_priv *priv = usbhs_pdev_to_priv(pdev);
+	struct device_node *usb_x1_clk, *extal_clk;
+	u32 freq_usb = 0, freq_extal = 0;
+
+	/* Input Clock Selection (NOTE: ch0 controls both ch0 and ch1) */
+	usb_x1_clk = of_find_node_by_name(NULL, "usb_x1");
+	extal_clk = of_find_node_by_name(NULL, "extal");
+	of_property_read_u32(usb_x1_clk, "clock-frequency", &freq_usb);
+	of_property_read_u32(extal_clk, "clock-frequency", &freq_extal);
+	if (freq_usb == 0) {
+		if (freq_extal == 12000000) {
+			/* Select 12MHz XTAL */
+			usbhs_bset(priv, SYSCFG, UCKSEL, UCKSEL);
+		} else {
+			dev_err(usbhs_priv_to_dev(priv), "A 48MHz USB clock or 12MHz main clock is required.\n");
+			return -EIO;
+		}
+	}
+
+	/* Enable USB PLL (NOTE: ch0 controls both ch0 and ch1) */
+	usbhs_bset(priv, SYSCFG, UPLLE, UPLLE);
+	udelay(1000);
+	usbhs_bset(priv, SUSPMODE, SUSPM, SUSPM);
+
+	return 0;
+}
+
+static int usbhs_rza_get_id(struct platform_device *pdev)
+{
+	return USBHS_GADGET;
+}
+
+const struct renesas_usbhs_platform_callback usbhs_rza1_ops = {
+	.hardware_init = usbhs_rza1_hardware_init,
+	.get_id = usbhs_rza_get_id,
+};
diff --git a/drivers/usb/renesas_usbhs/rza.h b/drivers/usb/renesas_usbhs/rza.h
new file mode 100644
index 0000000..ca917ca
--- /dev/null
+++ b/drivers/usb/renesas_usbhs/rza.h
@@ -0,0 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0
+#include "common.h"
+
+extern const struct renesas_usbhs_platform_callback usbhs_rza1_ops;