Hi Marek, Thank you for the patch.
On lun., sept. 09, 2024 at 01:06, Marek Vasut <marek.vasut+rene...@mailbox.org> wrote: > From: Vitaliy Vasylskyy <vitaliy.vasyls...@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.vasyls...@globallogic.com> > Signed-off-by: Marek Vasut <marek.vasut+rene...@mailbox.org> I already did the comparison with the linux driver in v1: https://lore.kernel.org/r/all/87jzhcpi9z....@baylibre.com/ The diff between v1 and v2 looks good to me as well, so Reviewed-by: Mattijs Korpershoek <mkorpersh...@baylibre.com> > --- > Note that the driver does have a few checkpatch warnings in it, those > also partly come from Linux. > --- > Cc: Jonas Karlman <jo...@kwiboo.se> > Cc: Lukasz Majewski <lu...@denx.de> > Cc: Mattijs Korpershoek <mkorpersh...@baylibre.com> > Cc: Miquel Raynal <miquel.ray...@bootlin.com> > Cc: Simon Glass <s...@chromium.org> > Cc: Tom Rini <tr...@konsulko.com> > Cc: Vitaliy Vasylskyy <vitaliy.vasyls...@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...@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...@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...@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...@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...@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...@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...@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...@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...@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...@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