[PATCH v2] usb: gadget: usbhs: Add Renesas USBHS device driver

Mattijs Korpershoek mkorpershoek at baylibre.com
Mon Sep 30 09:33:44 CEST 2024


Hi Marek,

Thank you for the patch.

On lun., sept. 09, 2024 at 01:06, Marek Vasut <marek.vasut+renesas at mailbox.org> wrote:

> From: Vitaliy Vasylskyy <vitaliy.vasylskyy at globallogic.com>
>
> Add UDC driver for Renesas USBHS controller found in R-Car Gen3 SoCs.
> This is mostly ported from the Linux kernel, with additional porting
> glue. The code has been synchronized with 1b4861e32e46 ("Linux 6.9.3")
> and cleaned up and ported to DM since the original implementation by
> Vitaliy.
>
> Signed-off-by: Vitaliy Vasylskyy <vitaliy.vasylskyy at globallogic.com>
> Signed-off-by: Marek Vasut <marek.vasut+renesas at mailbox.org>

I already did the comparison with the linux driver in v1:
https://lore.kernel.org/r/all/87jzhcpi9z.fsf@baylibre.com/

The diff between v1 and v2 looks good to me as well, so

Reviewed-by: Mattijs Korpershoek <mkorpershoek at baylibre.com>

> ---
> Note that the driver does have a few checkpatch warnings in it, those
> also partly come from Linux.
> ---
> Cc: Jonas Karlman <jonas at kwiboo.se>
> Cc: Lukasz Majewski <lukma at denx.de>
> Cc: Mattijs Korpershoek <mkorpershoek at baylibre.com>
> Cc: Miquel Raynal <miquel.raynal at bootlin.com>
> Cc: Simon Glass <sjg at chromium.org>
> Cc: Tom Rini <trini at konsulko.com>
> Cc: Vitaliy Vasylskyy <vitaliy.vasylskyy at globallogic.com>
> ---
> V2: Drop CONFIG_USB_RENESAS_USBHS_UDC check
> ---
>  drivers/usb/gadget/Kconfig            |    9 +
>  drivers/usb/gadget/Makefile           |    1 +
>  drivers/usb/gadget/rcar/Makefile      |    8 +
>  drivers/usb/gadget/rcar/common.c      |  478 +++++++++++
>  drivers/usb/gadget/rcar/common.h      |  328 +++++++
>  drivers/usb/gadget/rcar/fifo.c        | 1067 +++++++++++++++++++++++
>  drivers/usb/gadget/rcar/fifo.h        |  114 +++
>  drivers/usb/gadget/rcar/mod.c         |  345 ++++++++
>  drivers/usb/gadget/rcar/mod.h         |  161 ++++
>  drivers/usb/gadget/rcar/mod_gadget.c  | 1136 +++++++++++++++++++++++++
>  drivers/usb/gadget/rcar/pipe.c        |  849 ++++++++++++++++++
>  drivers/usb/gadget/rcar/pipe.h        |  114 +++
>  drivers/usb/gadget/rcar/renesas_usb.h |  125 +++
>  13 files changed, 4735 insertions(+)
>  create mode 100644 drivers/usb/gadget/rcar/Makefile
>  create mode 100644 drivers/usb/gadget/rcar/common.c
>  create mode 100644 drivers/usb/gadget/rcar/common.h
>  create mode 100644 drivers/usb/gadget/rcar/fifo.c
>  create mode 100644 drivers/usb/gadget/rcar/fifo.h
>  create mode 100644 drivers/usb/gadget/rcar/mod.c
>  create mode 100644 drivers/usb/gadget/rcar/mod.h
>  create mode 100644 drivers/usb/gadget/rcar/mod_gadget.c
>  create mode 100644 drivers/usb/gadget/rcar/pipe.c
>  create mode 100644 drivers/usb/gadget/rcar/pipe.h
>  create mode 100644 drivers/usb/gadget/rcar/renesas_usb.h
>
> diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig
> index 4621a6fd5e6..5fb73890c78 100644
> --- a/drivers/usb/gadget/Kconfig
> +++ b/drivers/usb/gadget/Kconfig
> @@ -114,6 +114,15 @@ config USB_GADGET_DWC2_OTG
>  	  driver to operate in Peripheral mode. This option requires
>  	  USB_GADGET to be enabled.
>  
> +config USB_RENESAS_USBHS
> +	bool "Renesas RCar USB2.0 HS controller (gadget mode)"
> +	select USB_GADGET_DUALSPEED
> +	help
> +	  The Renesas Rcar USB 2.0 high-speed gadget controller
> +	  integrated into Salvator and Kingfisher boards. Select this
> +	  option if you want the driver to operate in Peripheral mode.
> +	  This option requires USB_GADGET to be enabled.
> +
>  if USB_GADGET_DWC2_OTG
>  
>  config USB_GADGET_DWC2_OTG_PHY
> diff --git a/drivers/usb/gadget/Makefile b/drivers/usb/gadget/Makefile
> index 6abcce0d9c7..da76b6524de 100644
> --- a/drivers/usb/gadget/Makefile
> +++ b/drivers/usb/gadget/Makefile
> @@ -21,6 +21,7 @@ obj-$(CONFIG_USB_GADGET_BCM_UDC_OTG_PHY) += bcm_udc_otg_phy.o
>  obj-$(CONFIG_USB_GADGET_DWC2_OTG) += dwc2_udc_otg.o
>  obj-$(CONFIG_USB_GADGET_DWC2_OTG_PHY) += dwc2_udc_otg_phy.o
>  obj-$(CONFIG_USB_GADGET_MAX3420) += max3420_udc.o
> +obj-$(CONFIG_USB_RENESAS_USBHS) += rcar/
>  ifndef CONFIG_SPL_BUILD
>  obj-$(CONFIG_USB_GADGET_DOWNLOAD) += g_dnl.o
>  obj-$(CONFIG_USB_FUNCTION_THOR) += f_thor.o
> diff --git a/drivers/usb/gadget/rcar/Makefile b/drivers/usb/gadget/rcar/Makefile
> new file mode 100644
> index 00000000000..676f39c8e24
> --- /dev/null
> +++ b/drivers/usb/gadget/rcar/Makefile
> @@ -0,0 +1,8 @@
> +# SPDX-License-Identifier: GPL-2.0
> +
> +obj-$(CONFIG_USB_RENESAS_USBHS) += \
> +	common.o \
> +	fifo.o \
> +	mod.o \
> +	mod_gadget.o \
> +	pipe.o
> diff --git a/drivers/usb/gadget/rcar/common.c b/drivers/usb/gadget/rcar/common.c
> new file mode 100644
> index 00000000000..2ba022a3f2c
> --- /dev/null
> +++ b/drivers/usb/gadget/rcar/common.c
> @@ -0,0 +1,478 @@
> +// SPDX-License-Identifier: GPL-1.0+
> +/*
> + * Renesas USB driver
> + *
> + * Copyright (C) 2011 Renesas Solutions Corp.
> + * Copyright (C) 2019 Renesas Electronics Corporation
> + * Kuninori Morimoto <kuninori.morimoto.gx at renesas.com>
> + */
> +
> +#include <asm/io.h>
> +#include <clk.h>
> +#include <dm.h>
> +#include <errno.h>
> +#include <generic-phy.h>
> +#include <linux/err.h>
> +#include <linux/usb/ch9.h>
> +#include <linux/usb/gadget.h>
> +#include <usb.h>
> +
> +#include "common.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 |
> + *			| ....  |	+-----------+
> + */
> +
> +/*
> + *		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);
> +}
> +
> +/*
> + *		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;
> +
> +	/*
> +	 * 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	= cpu_to_le16(usbhs_read(priv, USBVAL));
> +	req->wIndex	= cpu_to_le16(usbhs_read(priv, USBINDX));
> +	req->wLength	= cpu_to_le16(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,  le16_to_cpu(req->wValue));
> +	usbhs_write(priv, USBINDX, le16_to_cpu(req->wIndex));
> +	usbhs_write(priv, USBLENG, le16_to_cpu(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;
> +}
> +
> +static void usbhsc_bus_init(struct usbhs_priv *priv)
> +{
> +	usbhs_write(priv, DVSTCTR, 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 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),
> +};
> +
> +#define LPSTS			0x102
> +#define LPSTS_SUSPM		BIT(14)
> +
> +#define UGCTRL2			0x184
> +#define UGCTRL2_RESERVED_3	BIT(0)
> +#define UGCTRL2_USB0SEL_EHCI	0x10
> +#define UGCTRL2_USB0SEL_HSUSB	0x20
> +#define UGCTRL2_USB0SEL_OTG	0x30
> +#define UGCTRL2_USB0SEL_MASK	0x30
> +#define UGCTRL2_VBUSSEL		BIT(10)
> +
> +struct usbhs_priv_otg_data {
> +	void __iomem		*base;
> +	void __iomem		*phybase;
> +
> +	struct platform_device	usbhs_dev;
> +	struct usbhs_priv	usbhs_priv;
> +
> +	struct phy		phy;
> +};
> +
> +static int usbhs_rcar3_power_ctrl(struct usbhs_priv *priv, bool enable)
> +{
> +	if (enable) {
> +		writel(UGCTRL2_USB0SEL_OTG | UGCTRL2_VBUSSEL | UGCTRL2_RESERVED_3,
> +		       priv->base + UGCTRL2);
> +
> +		usbhs_bset(priv, LPSTS, LPSTS_SUSPM, LPSTS_SUSPM);
> +		/* The controller on R-Car Gen3 needs to wait up to 90 usec */
> +		udelay(90);
> +
> +		usbhs_sys_clock_ctrl(priv, enable);
> +	} else {
> +		usbhs_sys_clock_ctrl(priv, enable);
> +
> +		usbhs_bset(priv, LPSTS, LPSTS_SUSPM, 0);
> +	}
> +
> +	return 0;
> +}
> +
> +void usbhsc_hotplug(struct usbhs_priv *priv)
> +{
> +	int ret;
> +
> +	ret = usbhs_mod_change(priv, USBHS_GADGET);
> +	if (ret < 0)
> +		return;
> +
> +	usbhs_rcar3_power_ctrl(priv, true);
> +
> +	/* bus init */
> +	usbhsc_set_buswait(priv);
> +	usbhsc_bus_init(priv);
> +
> +	/* module start */
> +	usbhs_mod_call(priv, start, priv);
> +}
> +
> +#define USB2_OBINTSTA		0x604
> +#define USB2_OBINT_SESSVLDCHG		BIT(12)
> +#define USB2_OBINT_IDDIGCHG		BIT(11)
> +
> +static int usbhs_udc_otg_gadget_handle_interrupts(struct udevice *dev)
> +{
> +	struct usbhs_priv_otg_data *priv = dev_get_priv(dev);
> +	const u32 status = readl(priv->phybase + USB2_OBINTSTA);
> +
> +	/* We don't have a good way to forward IRQ to PHY yet */
> +	if (status & (USB2_OBINT_SESSVLDCHG | USB2_OBINT_IDDIGCHG)) {
> +		writel(USB2_OBINT_SESSVLDCHG | USB2_OBINT_IDDIGCHG,
> +		       priv->phybase + USB2_OBINTSTA);
> +		generic_phy_set_mode(&priv->phy, PHY_MODE_USB_OTG, 0);
> +	}
> +
> +	usbhs_interrupt(0, &priv->usbhs_priv);
> +
> +	return 0;
> +}
> +
> +static int usbhs_probe(struct usbhs_priv *priv)
> +{
> +	int ret;
> +
> +	priv->dparam.type = USBHS_TYPE_RCAR_GEN3;
> +	priv->dparam.pio_dma_border = 64;
> +	priv->dparam.pipe_configs = usbhsc_new_pipe;
> +	priv->dparam.pipe_size = ARRAY_SIZE(usbhsc_new_pipe);
> +
> +	/* 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;
> +
> +	usbhs_sys_clock_ctrl(priv, 0);
> +
> +	usbhs_rcar3_power_ctrl(priv, true);
> +	usbhs_mod_autonomy_mode(priv);
> +	usbhsc_hotplug(priv);
> +
> +	return ret;
> +
> +probe_end_fifo_exit:
> +	usbhs_fifo_remove(priv);
> +probe_end_pipe_exit:
> +	usbhs_pipe_remove(priv);
> +	return ret;
> +}
> +
> +static int usbhs_udc_otg_probe(struct udevice *dev)
> +{
> +	struct usbhs_priv_otg_data *priv = dev_get_priv(dev);
> +	struct usb_gadget *gadget;
> +	struct clk_bulk clk_bulk;
> +	int ret = -EINVAL;
> +
> +	priv->base = dev_read_addr_ptr(dev);
> +	if (!priv->base)
> +		return -EINVAL;
> +
> +	ret = clk_get_bulk(dev, &clk_bulk);
> +	if (ret)
> +		return ret;
> +
> +	ret = clk_enable_bulk(&clk_bulk);
> +	if (ret)
> +		return ret;
> +
> +	clrsetbits_le32(priv->base + UGCTRL2, UGCTRL2_USB0SEL_MASK, UGCTRL2_USB0SEL_EHCI);
> +	clrsetbits_le16(priv->base + LPSTS, LPSTS_SUSPM, LPSTS_SUSPM);
> +
> +	ret = generic_setup_phy(dev, &priv->phy, 0, PHY_MODE_USB_OTG, 1);
> +	if (ret)
> +		goto err_clk;
> +
> +	priv->phybase = dev_read_addr_ptr(priv->phy.dev);
> +
> +	priv->usbhs_priv.pdev = &priv->usbhs_dev;
> +	priv->usbhs_priv.base = priv->base;
> +	priv->usbhs_dev.dev.driver_data = &priv->usbhs_priv;
> +	ret = usbhs_probe(&priv->usbhs_priv);
> +	if (ret < 0)
> +		goto err_phy;
> +
> +	gadget = usbhsg_get_gadget(&priv->usbhs_priv);
> +	gadget->is_dualspeed = 1;
> +	gadget->is_otg = 0;
> +	gadget->is_a_peripheral = 0;
> +	gadget->b_hnp_enable = 0;
> +	gadget->a_hnp_support = 0;
> +	gadget->a_alt_hnp_support = 0;
> +
> +	return usb_add_gadget_udc((struct device *)dev, gadget);
> +
> +err_phy:
> +	generic_shutdown_phy(&priv->phy);
> +err_clk:
> +	clk_disable_bulk(&clk_bulk);
> +	return ret;
> +}
> +
> +static int usbhs_udc_otg_remove(struct udevice *dev)
> +{
> +	struct usbhs_priv_otg_data *priv = dev_get_priv(dev);
> +
> +	usbhs_rcar3_power_ctrl(&priv->usbhs_priv, false);
> +	usbhs_mod_remove(&priv->usbhs_priv);
> +	usbhs_fifo_remove(&priv->usbhs_priv);
> +	usbhs_pipe_remove(&priv->usbhs_priv);
> +
> +	generic_shutdown_phy(&priv->phy);
> +
> +	return dm_scan_fdt_dev(dev);
> +}
> +
> +static const struct udevice_id usbhs_udc_otg_ids[] = {
> +	{ .compatible = "renesas,rcar-gen3-usbhs" },
> +	{},
> +};
> +
> +static const struct usb_gadget_generic_ops usbhs_udc_otg_ops = {
> +	.handle_interrupts = usbhs_udc_otg_gadget_handle_interrupts,
> +};
> +
> +U_BOOT_DRIVER(usbhs_udc_otg) = {
> +	.name		= "usbhs-udc-otg",
> +	.id		= UCLASS_USB_GADGET_GENERIC,
> +	.ops		= &usbhs_udc_otg_ops,
> +	.of_match	= usbhs_udc_otg_ids,
> +	.probe		= usbhs_udc_otg_probe,
> +	.remove		= usbhs_udc_otg_remove,
> +	.priv_auto	= sizeof(struct usbhs_priv_otg_data),
> +};
> diff --git a/drivers/usb/gadget/rcar/common.h b/drivers/usb/gadget/rcar/common.h
> new file mode 100644
> index 00000000000..544cfd77cdf
> --- /dev/null
> +++ b/drivers/usb/gadget/rcar/common.h
> @@ -0,0 +1,328 @@
> +/* SPDX-License-Identifier: GPL-1.0+ */
> +/*
> + * Renesas USB driver
> + *
> + * Copyright (C) 2011 Renesas Solutions Corp.
> + * Copyright (C) 2019 Renesas Electronics Corporation
> + * Kuninori Morimoto <kuninori.morimoto.gx at renesas.com>
> + */
> +#ifndef RENESAS_USB_DRIVER_H
> +#define RENESAS_USB_DRIVER_H
> +
> +#include <dm/device.h>
> +#include <dm/device_compat.h>
> +#include <linux/bug.h>
> +#include <linux/delay.h>
> +#include <linux/io.h>
> +#include "renesas_usb.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 CNEN	(1 << 8)	/* Single-ended receiver operation 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		(0x7 << 4)	/* Device State */
> +#define  POWER_STATE		(0 << 4)
> +#define  DEFAULT_STATE		(1 << 4)
> +#define  ADDRESS_STATE		(2 << 4)
> +#define  CONFIGURATION_STATE	(3 << 4)
> +#define  SUSPENDED_STATE	(4 << 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 INBUFM		(1 << 14)	/* (PIPEnCTR) Transfer Buffer Monitor */
> +#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;
> +	struct renesas_usbhs_driver_param	dparam;
> +	struct platform_device			*pdev;
> +
> +	/*
> +	 * module control
> +	 */
> +	struct usbhs_mod_info mod_info;
> +
> +	/*
> +	 * pipe control
> +	 */
> +	struct usbhs_pipe_info pipe_info;
> +
> +	/*
> +	 * fifo control
> +	 */
> +	struct usbhs_fifo_info fifo_info;
> +};
> +
> +/*
> + * 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);
> +void usbhsc_hotplug(struct usbhs_priv *priv);
> +
> +/*
> + * 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
> + */
> +#define usbhs_get_dparam(priv, param)	(priv->dparam.param)
> +#define usbhs_priv_to_dev(priv)		(&priv->pdev->dev)
> +
> +#endif /* RENESAS_USB_DRIVER_H */
> diff --git a/drivers/usb/gadget/rcar/fifo.c b/drivers/usb/gadget/rcar/fifo.c
> new file mode 100644
> index 00000000000..6016b2987d5
> --- /dev/null
> +++ b/drivers/usb/gadget/rcar/fifo.c
> @@ -0,0 +1,1067 @@
> +// SPDX-License-Identifier: GPL-1.0+
> +/*
> + * Renesas USB driver
> + *
> + * Copyright (C) 2011 Renesas Solutions Corp.
> + * Copyright (C) 2019 Renesas Electronics Corporation
> + * Kuninori Morimoto <kuninori.morimoto.gx at renesas.com>
> + */
> +#include <linux/delay.h>
> +#include <linux/io.h>
> +#include "common.h"
> +#include "pipe.h"
> +
> +#define usbhsf_get_cfifo(p)	(&((p)->fifo_info.cfifo))
> +
> +#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);
> +}
> +
> +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);
> +static void usbhsf_tx_irq_ctrl(struct usbhs_pipe *pipe, int enable);
> +static void usbhsf_rx_irq_ctrl(struct usbhs_pipe *pipe, int enable);
> +struct usbhs_pkt *usbhs_pkt_pop(struct usbhs_pipe *pipe, struct usbhs_pkt *pkt)
> +{
> +	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)
> +			usbhsf_dma_unmap(pkt);
> +
> +		usbhs_pipe_clear_without_sequence(pipe, 0, 0);
> +		usbhs_pipe_running(pipe, 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) {
> +		ret = -EINVAL;
> +		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  */
> +	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() failed\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 */
> +	if (usbhs_get_dparam(priv, cfifo_byte_addr)) {
> +		for (i = 0; i < len; i++)
> +			iowrite8(buf[i], addr + (i & 0x03));
> +	} else {
> +		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;
> +}
> +
> +#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);
> +
> +	return info->dma_map_ctrl(pkt, map);
> +}
> +
> +/*
> + *		DMA push handler
> + */
> +static int usbhsf_dma_prepare_push(struct usbhs_pkt *pkt, int *is_done)
> +{
> +	struct usbhs_pipe *pipe = pkt->pipe;
> +
> +	if (usbhs_pipe_is_busy(pipe))
> +		return 0;
> +
> +	/*
> +	 * 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(struct usbhs_pkt *pkt, int *is_done)
> +{
> +	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;
> +
> +	if (usbhs_pipe_is_busy(pipe))
> +		return 0;
> +
> +	/*
> +	 * 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 int usbhsf_dma_pop_done(struct usbhs_pkt *pkt, int *is_done)
> +{
> +	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
> +};
> +
> +/*
> + *		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;
> +}
> +
> +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);	\
> +} 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)
> +{
> +}
> diff --git a/drivers/usb/gadget/rcar/fifo.h b/drivers/usb/gadget/rcar/fifo.h
> new file mode 100644
> index 00000000000..86746ca9bdd
> --- /dev/null
> +++ b/drivers/usb/gadget/rcar/fifo.h
> @@ -0,0 +1,114 @@
> +/* SPDX-License-Identifier: GPL-1.0+ */
> +/*
> + * Renesas USB driver
> + *
> + * Copyright (C) 2011 Renesas Solutions Corp.
> + * Kuninori Morimoto <kuninori.morimoto.gx at renesas.com>
> + */
> +#ifndef RENESAS_USB_FIFO_H
> +#define RENESAS_USB_FIFO_H
> +
> +#include <dma.h>
> +#include "pipe.h"
> +
> +/*
> + * Drivers, using this library are expected to embed struct shdma_dev,
> + * struct shdma_chan, struct shdma_desc, and struct shdma_slave
> + * in their respective device, channel, descriptor and slave objects.
> + */
> +
> +struct shdma_slave {
> +	int slave_id;
> +};
> +
> +/* Used by slave DMA clients to request DMA to/from a specific peripheral */
> +struct sh_dmae_slave {
> +	struct shdma_slave		shdma_slave;	/* Set by the platform */
> +};
> +
> +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;
> +	const struct dmaengine_result *dma_result;
> +	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);
> +struct usbhs_pkt *__usbhsf_pkt_get(struct usbhs_pipe *pipe);
> +
> +#endif /* RENESAS_USB_FIFO_H */
> diff --git a/drivers/usb/gadget/rcar/mod.c b/drivers/usb/gadget/rcar/mod.c
> new file mode 100644
> index 00000000000..f5f8d169e17
> --- /dev/null
> +++ b/drivers/usb/gadget/rcar/mod.c
> @@ -0,0 +1,345 @@
> +// SPDX-License-Identifier: GPL-1.0+
> +/*
> + * Renesas USB driver
> + *
> + * Copyright (C) 2011 Renesas Solutions Corp.
> + * Copyright (C) 2019 Renesas Electronics Corporation
> + * Kuninori Morimoto <kuninori.morimoto.gx at renesas.com>
> + */
> +#include "common.h"
> +#include "mod.h"
> +
> +/*
> + *		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_irq_vbus(struct usbhs_priv *priv,
> +				    struct usbhs_irq_state *irq_state)
> +{
> +	usbhsc_hotplug(priv);
> +
> +	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;
> +
> +	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;
> +}
> +
> +irqreturn_t usbhs_interrupt(int irq, void *data);
> +int usbhs_mod_probe(struct usbhs_priv *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;
> +
> +	return ret;
> +
> +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)
> +{
> +	return (int)irq_state->intsts0 & DVSQ_MASK;
> +}
> +
> +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 ******************/
> +
> +	return 0;
> +}
> +
> +/*
> + *		interrupt
> + */
> +#define INTSTS0_MAGIC 0xF800 /* acknowledge magical interrupt sources */
> +#define INTSTS1_MAGIC 0xA870 /* acknowledge magical interrupt sources */
> +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
> +	 */
> +
> +	if (info->irq_vbus)
> +		intenb0 |= VBSE;
> +
> +	if (mod) {
> +		/*
> +		 * INTSTS0
> +		 */
> +		if (mod->irq_ctrl_stage)
> +			intenb0 |= CTRE;
> +
> +		if (mod->irq_dev_state)
> +			intenb0 |= DVSE;
> +
> +		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/gadget/rcar/mod.h b/drivers/usb/gadget/rcar/mod.h
> new file mode 100644
> index 00000000000..b670e950a28
> --- /dev/null
> +++ b/drivers/usb/gadget/rcar/mod.h
> @@ -0,0 +1,161 @@
> +/* SPDX-License-Identifier: GPL-1.0+ */
> +/*
> + * Renesas USB driver
> + *
> + * Copyright (C) 2011 Renesas Solutions Corp.
> + * Copyright (C) 2019 Renesas Electronics Corporation
> + * Kuninori Morimoto <kuninori.morimoto.gx at renesas.com>
> + */
> +#ifndef RENESAS_USB_MOD_H
> +#define RENESAS_USB_MOD_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 (runtime_pwctrl == 0)
> +	 * when the platform doesn't have own get_vbus function.
> +	 *
> +	 * 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);
> +void usbhs_mod_non_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);
> +
> +irqreturn_t usbhs_interrupt(int irq, void *data);
> +
> +#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);			\
> +	})
> +
> +#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);				\
> +})
> +
> +/*
> + * 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
> +
> +extern int usbhs_mod_gadget_probe(struct usbhs_priv *priv);
> +extern void usbhs_mod_gadget_remove(struct usbhs_priv *priv);
> +
> +#endif /* RENESAS_USB_MOD_H */
> diff --git a/drivers/usb/gadget/rcar/mod_gadget.c b/drivers/usb/gadget/rcar/mod_gadget.c
> new file mode 100644
> index 00000000000..bd9855eb4fa
> --- /dev/null
> +++ b/drivers/usb/gadget/rcar/mod_gadget.c
> @@ -0,0 +1,1136 @@
> +// SPDX-License-Identifier: GPL-1.0+
> +/*
> + * Renesas USB driver
> + *
> + * Copyright (C) 2011 Renesas Solutions Corp.
> + * Copyright (C) 2019 Renesas Electronics Corporation
> + * Kuninori Morimoto <kuninori.morimoto.gx at renesas.com>
> + */
> +#include <linux/delay.h>
> +#include <linux/dma-mapping.h>
> +#include <linux/io.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;
> +	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);
> +
> +	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)
> +{
> +	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 usbhs_pkt *pkt, int map)
> +{
> +	return -1;
> +}
> +
> +/*
> + *		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;
> +	__le16 *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);
> +	int state = usbhs_status_get_device_state(irq_state);
> +
> +	gpriv->gadget.speed = usbhs_bus_get_speed(priv);
> +
> +	dev_dbg(dev, "state = %x : speed : %d\n", state, gpriv->gadget.speed);
> +
> +	if (gpriv->gadget.speed != USB_SPEED_UNKNOWN &&
> +	    (state & SUSPENDED_STATE)) {
> +		if (gpriv->driver && gpriv->driver->suspend)
> +			gpriv->driver->suspend(&gpriv->gadget);
> +		usb_gadget_set_state(&gpriv->gadget, USB_STATE_SUSPENDED);
> +	}
> +
> +	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);
> +		fallthrough;
> +	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;
> +}
> +
> +bool usbhs_pipe_contains_transmittable_data(struct usbhs_pipe *pipe);
> +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 device *dev = usbhsg_gpriv_to_dev(gpriv);
> +	unsigned long flags;
> +	int ret = 0;
> +
> +	dev_dbg(dev, "set halt %d (pipe %d)\n",
> +		halt, usbhs_pipe_number(pipe));
> +
> +	/********************  spin lock ********************/
> +	usbhs_lock(priv, flags);
> +
> +	/*
> +	 * According to usb_ep_set_halt()'s description, this function should
> +	 * return -EAGAIN if the IN endpoint has any queue or data. Note
> +	 * that the usbhs_pipe_is_dir_in() returns false if the pipe is an
> +	 * IN endpoint in the gadget mode.
> +	 */
> +	if (!usbhs_pipe_is_dir_in(pipe) && (__usbhsf_pkt_get(pipe) ||
> +	    usbhs_pipe_contains_transmittable_data(pipe))) {
> +		ret = -EAGAIN;
> +		goto out;
> +	}
> +
> +	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);
> +
> +out:
> +	usbhs_unlock(priv, flags);
> +	/********************  spin unlock ******************/
> +
> +	return ret;
> +}
> +
> +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 void usbhs_mod_phy_mode(struct usbhs_priv *priv)
> +{
> +	struct usbhs_mod_info *info = &priv->mod_info;
> +
> +	info->irq_vbus = NULL;
> +
> +	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);
> +
> +	if (!driver || !driver->setup)
> +		return -EINVAL;
> +
> +	/* 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);
> +
> +	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);
> +
> +	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);
> +
> +	gpriv->vbus_active = !!is_active;
> +
> +	usbhsc_hotplug(priv);
> +
> +	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;
> +	}
> +
> +	/*
> +	 * 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;
> +
> +	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);
> +}
> +
> +struct usb_gadget *usbhsg_get_gadget(struct usbhs_priv *priv)
> +{
> +	struct usbhsg_gpriv *gpriv = usbhsg_priv_to_gpriv(priv);
> +	return &gpriv->gadget;
> +}
> diff --git a/drivers/usb/gadget/rcar/pipe.c b/drivers/usb/gadget/rcar/pipe.c
> new file mode 100644
> index 00000000000..a2b24f38144
> --- /dev/null
> +++ b/drivers/usb/gadget/rcar/pipe.c
> @@ -0,0 +1,849 @@
> +// SPDX-License-Identifier: GPL-1.0+
> +/*
> + * Renesas USB driver
> + *
> + * Copyright (C) 2011 Renesas Solutions Corp.
> + * Kuninori Morimoto <kuninori.morimoto.gx at renesas.com>
> + */
> +#include <linux/delay.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;
> +}
> +
> +bool usbhs_pipe_contains_transmittable_data(struct usbhs_pipe *pipe)
> +{
> +	u16 val;
> +
> +	/* Do not support for DCP pipe */
> +	if (usbhs_pipe_is_dcp(pipe))
> +		return false;
> +
> +	val = usbhsp_pipectrl_get(pipe);
> +	if (val & INBUFM)
> +		return true;
> +
> +	return false;
> +}
> +
> +/*
> + *		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);
> +		fallthrough;
> +	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 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_pipe_select(pipe);
> +	usbhsp_pipe_cfg_set(pipe, 0xFFFF, 0);
> +	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/gadget/rcar/pipe.h b/drivers/usb/gadget/rcar/pipe.h
> new file mode 100644
> index 00000000000..01c15178a28
> --- /dev/null
> +++ b/drivers/usb/gadget/rcar/pipe.h
> @@ -0,0 +1,114 @@
> +// SPDX-License-Identifier: GPL-1.0+
> +/*
> + * Renesas USB driver
> + *
> + * Copyright (C) 2011 Renesas Solutions Corp.
> + * Kuninori Morimoto <kuninori.morimoto.gx at 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 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 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/gadget/rcar/renesas_usb.h b/drivers/usb/gadget/rcar/renesas_usb.h
> new file mode 100644
> index 00000000000..8155e3dcaf6
> --- /dev/null
> +++ b/drivers/usb/gadget/rcar/renesas_usb.h
> @@ -0,0 +1,125 @@
> +// SPDX-License-Identifier: GPL-1.0+
> +/*
> + * Renesas USB
> + *
> + * Copyright (C) 2011 Renesas Solutions Corp.
> + * Kuninori Morimoto <kuninori.morimoto.gx at renesas.com>
> + *
> + * Ported to u-boot
> + * Copyright (C) 2016 GlobalLogic
> + */
> +#ifndef RENESAS_USB_H
> +#define RENESAS_USB_H
> +
> +#include <linux/usb/ch9.h>
> +#include <linux/compat.h>
> +
> +struct platform_device {
> +	const char	*name;
> +	struct device	dev;
> +};
> +
> +/*
> + * module type
> + *
> + * it will be return value from get_id
> + */
> +enum {
> +	USBHS_HOST = 0,
> +	USBHS_GADGET,
> +	USBHS_MAX,
> +};
> +
> +/*
> + * parameters for renesas usbhs
> + *
> + * some register needs USB chip specific parameters.
> + * This struct show it to driver
> + */
> +
> +struct renesas_usbhs_driver_pipe_config {
> +	u8 type;	/* USB_ENDPOINT_XFER_xxx */
> +	u16 bufsize;
> +	u8 bufnum;
> +	bool double_buf;
> +};
> +#define RENESAS_USBHS_PIPE(_type, _size, _num, _double_buf)	{	\
> +			.type = (_type),		\
> +			.bufsize = (_size),		\
> +			.bufnum = (_num),		\
> +			.double_buf = (_double_buf),	\
> +	}
> +
> +struct renesas_usbhs_driver_param {
> +	/*
> +	 * pipe settings
> +	 */
> +	struct renesas_usbhs_driver_pipe_config *pipe_configs;
> +	int pipe_size; /* pipe_configs array size */
> +
> +	/*
> +	 * option:
> +	 *
> +	 * for BUSWAIT :: BWAIT
> +	 * see
> +	 *	renesas_usbhs/common.c :: usbhsc_set_buswait()
> +	 * */
> +	int buswait_bwait;
> +
> +	/*
> +	 * option:
> +	 *
> +	 * delay time from notify_hotplug callback
> +	 */
> +	int detection_delay; /* msec */
> +
> +	/*
> +	 * option:
> +	 *
> +	 * dma id for dmaengine
> +	 * The data transfer direction on D0FIFO/D1FIFO should be
> +	 * fixed for keeping consistency.
> +	 * So, the platform id settings will be..
> +	 *	.d0_tx_id = xx_TX,
> +	 *	.d1_rx_id = xx_RX,
> +	 * or
> +	 *	.d1_tx_id = xx_TX,
> +	 *	.d0_rx_id = xx_RX,
> +	 */
> +	int d0_tx_id;
> +	int d0_rx_id;
> +	int d1_tx_id;
> +	int d1_rx_id;
> +	int d2_tx_id;
> +	int d2_rx_id;
> +	int d3_tx_id;
> +	int d3_rx_id;
> +
> +	/*
> +	 * option:
> +	 *
> +	 * pio <--> dma border.
> +	 */
> +	int pio_dma_border; /* default is 64byte */
> +
> +	uintptr_t type;
> +	u32 enable_gpio;
> +
> +	/*
> +	 * option:
> +	 */
> +	u32 has_otg:1; /* for controlling PWEN/EXTLP */
> +	u32 has_sudmac:1; /* for SUDMAC */
> +	u32 has_usb_dmac:1; /* for USB-DMAC */
> +	u32 cfifo_byte_addr:1; /* CFIFO is byte addressable */
> +#define USBHS_USB_DMAC_XFER_SIZE	32	/* hardcode the xfer size */
> +	u32 multi_clks:1;
> +	u32 has_new_pipe_configs:1;
> +};
> +
> +#define USBHS_TYPE_RCAR_GEN3		2
> +
> +struct usbhs_priv;
> +struct usb_gadget *usbhsg_get_gadget(struct usbhs_priv *priv);
> +
> +#endif /* RENESAS_USB_H */
> -- 
> 2.45.2


More information about the U-Boot mailing list