summaryrefslogtreecommitdiffstats
path: root/drivers/usb/renesas_usbhs
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/usb/renesas_usbhs')
-rw-r--r--drivers/usb/renesas_usbhs/Kconfig16
-rw-r--r--drivers/usb/renesas_usbhs/Makefile9
-rw-r--r--drivers/usb/renesas_usbhs/common.c437
-rw-r--r--drivers/usb/renesas_usbhs/common.h230
-rw-r--r--drivers/usb/renesas_usbhs/mod.c328
-rw-r--r--drivers/usb/renesas_usbhs/mod.h137
-rw-r--r--drivers/usb/renesas_usbhs/mod_gadget.c1384
-rw-r--r--drivers/usb/renesas_usbhs/pipe.c874
-rw-r--r--drivers/usb/renesas_usbhs/pipe.h104
9 files changed, 3519 insertions, 0 deletions
diff --git a/drivers/usb/renesas_usbhs/Kconfig b/drivers/usb/renesas_usbhs/Kconfig
new file mode 100644
index 00000000000..b2e64918884
--- /dev/null
+++ b/drivers/usb/renesas_usbhs/Kconfig
@@ -0,0 +1,16 @@
+#
+# Renesas USB Controller Drivers
+#
+
+config USB_RENESAS_USBHS
+ tristate 'Renesas USBHS controller'
+ depends on SUPERH || ARCH_SHMOBILE
+ default n
+ help
+ Renesas USBHS is a discrete USB host and peripheral controller chip
+ that supports both full and high speed USB 2.0 data transfers.
+ It has nine or more configurable endpoints, and endpoint zero.
+
+ Say "y" to link the driver statically, or "m" to build a
+ dynamically linked module called "renesas_usbhs" and force all
+ gadget drivers to also be dynamically linked.
diff --git a/drivers/usb/renesas_usbhs/Makefile b/drivers/usb/renesas_usbhs/Makefile
new file mode 100644
index 00000000000..b8798ad1627
--- /dev/null
+++ b/drivers/usb/renesas_usbhs/Makefile
@@ -0,0 +1,9 @@
+#
+# for Renesas USB
+#
+
+obj-$(CONFIG_USB_RENESAS_USBHS) += renesas_usbhs.o
+
+renesas_usbhs-y := common.o mod.o pipe.o
+
+renesas_usbhs-$(CONFIG_USB_RENESAS_USBHS_UDC) += mod_gadget.o
diff --git a/drivers/usb/renesas_usbhs/common.c b/drivers/usb/renesas_usbhs/common.c
new file mode 100644
index 00000000000..f3664d6af66
--- /dev/null
+++ b/drivers/usb/renesas_usbhs/common.c
@@ -0,0 +1,437 @@
+/*
+ * Renesas USB driver
+ *
+ * Copyright (C) 2011 Renesas Solutions Corp.
+ * Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/pm_runtime.h>
+#include <linux/slab.h>
+#include <linux/sysfs.h>
+#include "./common.h"
+
+#define USBHSF_RUNTIME_PWCTRL (1 << 0)
+
+/* status */
+#define usbhsc_flags_init(p) do {(p)->flags = 0; } while (0)
+#define usbhsc_flags_set(p, b) ((p)->flags |= (b))
+#define usbhsc_flags_clr(p, b) ((p)->flags &= ~(b))
+#define usbhsc_flags_has(p, b) ((p)->flags & (b))
+
+/*
+ * platform call back
+ *
+ * renesas usb support platform callback function.
+ * Below macro call it.
+ * if platform doesn't have callback, it return 0 (no error)
+ */
+#define usbhs_platform_call(priv, func, args...)\
+ (!(priv) ? -ENODEV : \
+ !((priv)->pfunc->func) ? 0 : \
+ (priv)->pfunc->func(args))
+
+/*
+ * common functions
+ */
+u16 usbhs_read(struct usbhs_priv *priv, u32 reg)
+{
+ return ioread16(priv->base + reg);
+}
+
+void usbhs_write(struct usbhs_priv *priv, u32 reg, u16 data)
+{
+ iowrite16(data, priv->base + reg);
+}
+
+void usbhs_bset(struct usbhs_priv *priv, u32 reg, u16 mask, u16 data)
+{
+ u16 val = usbhs_read(priv, reg);
+
+ val &= ~mask;
+ val |= data & mask;
+
+ usbhs_write(priv, reg, val);
+}
+
+struct usbhs_priv *usbhs_pdev_to_priv(struct platform_device *pdev)
+{
+ return dev_get_drvdata(&pdev->dev);
+}
+
+/*
+ * syscfg functions
+ */
+void usbhs_sys_clock_ctrl(struct usbhs_priv *priv, int enable)
+{
+ usbhs_bset(priv, SYSCFG, SCKE, enable ? SCKE : 0);
+}
+
+void usbhs_sys_hispeed_ctrl(struct usbhs_priv *priv, int enable)
+{
+ usbhs_bset(priv, SYSCFG, HSE, enable ? HSE : 0);
+}
+
+void usbhs_sys_usb_ctrl(struct usbhs_priv *priv, int enable)
+{
+ usbhs_bset(priv, SYSCFG, USBE, enable ? USBE : 0);
+}
+
+void usbhs_sys_host_ctrl(struct usbhs_priv *priv, int enable)
+{
+ u16 mask = DCFM | DRPD | DPRPU;
+ u16 val = DCFM | DRPD;
+
+ /*
+ * 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;
+ u16 val = DPRPU;
+
+ /*
+ * if enable
+ *
+ * - select Function mode
+ * - D+ Line Pull-up
+ */
+ usbhs_bset(priv, SYSCFG, mask, enable ? val : 0);
+}
+
+/*
+ * frame functions
+ */
+int usbhs_frame_get_num(struct usbhs_priv *priv)
+{
+ return usbhs_read(priv, FRMNUM) & FRNM_MASK;
+}
+
+/*
+ * local functions
+ */
+static void usbhsc_bus_ctrl(struct usbhs_priv *priv, int enable)
+{
+ int wait = usbhs_get_dparam(priv, buswait_bwait);
+ u16 data = 0;
+
+ if (enable) {
+ /* set bus wait if platform have */
+ if (wait)
+ usbhs_bset(priv, BUSWAIT, 0x000F, wait);
+ }
+ usbhs_write(priv, DVSTCTR, data);
+}
+
+/*
+ * platform default param
+ */
+static u32 usbhsc_default_pipe_type[] = {
+ USB_ENDPOINT_XFER_CONTROL,
+ USB_ENDPOINT_XFER_ISOC,
+ USB_ENDPOINT_XFER_ISOC,
+ USB_ENDPOINT_XFER_BULK,
+ USB_ENDPOINT_XFER_BULK,
+ USB_ENDPOINT_XFER_BULK,
+ USB_ENDPOINT_XFER_INT,
+ USB_ENDPOINT_XFER_INT,
+ USB_ENDPOINT_XFER_INT,
+ USB_ENDPOINT_XFER_INT,
+};
+
+/*
+ * power control
+ */
+static void usbhsc_power_ctrl(struct usbhs_priv *priv, int enable)
+{
+ struct device *dev = usbhs_priv_to_dev(priv);
+
+ if (enable) {
+ /* enable PM */
+ pm_runtime_get_sync(dev);
+
+ /* USB on */
+ usbhs_sys_clock_ctrl(priv, enable);
+ usbhsc_bus_ctrl(priv, enable);
+ } else {
+ /* USB off */
+ usbhsc_bus_ctrl(priv, enable);
+ usbhs_sys_clock_ctrl(priv, enable);
+
+ /* disable PM */
+ pm_runtime_put_sync(dev);
+ }
+}
+
+/*
+ * notify hotplug
+ */
+static void usbhsc_notify_hotplug(struct work_struct *work)
+{
+ struct usbhs_priv *priv = container_of(work,
+ struct usbhs_priv,
+ notify_hotplug_work.work);
+ struct platform_device *pdev = usbhs_priv_to_pdev(priv);
+ struct usbhs_mod *mod = usbhs_mod_get_current(priv);
+ int id;
+ int enable;
+ int ret;
+
+ /*
+ * get vbus status from platform
+ */
+ enable = usbhs_platform_call(priv, get_vbus, pdev);
+
+ /*
+ * get id from platform
+ */
+ id = usbhs_platform_call(priv, get_id, pdev);
+
+ if (enable && !mod) {
+ ret = usbhs_mod_change(priv, id);
+ if (ret < 0)
+ return;
+
+ dev_dbg(&pdev->dev, "%s enable\n", __func__);
+
+ /* power on */
+ if (usbhsc_flags_has(priv, USBHSF_RUNTIME_PWCTRL))
+ usbhsc_power_ctrl(priv, enable);
+
+ /* module start */
+ usbhs_mod_call(priv, start, priv);
+
+ } else if (!enable && mod) {
+ dev_dbg(&pdev->dev, "%s disable\n", __func__);
+
+ /* module stop */
+ usbhs_mod_call(priv, stop, priv);
+
+ /* power off */
+ if (usbhsc_flags_has(priv, USBHSF_RUNTIME_PWCTRL))
+ usbhsc_power_ctrl(priv, enable);
+
+ usbhs_mod_change(priv, -1);
+
+ /* reset phy for next connection */
+ usbhs_platform_call(priv, phy_reset, pdev);
+ }
+}
+
+int usbhsc_drvcllbck_notify_hotplug(struct platform_device *pdev)
+{
+ struct usbhs_priv *priv = usbhs_pdev_to_priv(pdev);
+ int delay = usbhs_get_dparam(priv, detection_delay);
+
+ /*
+ * This functions will be called in interrupt.
+ * To make sure safety context,
+ * use workqueue for usbhs_notify_hotplug
+ */
+ schedule_delayed_work(&priv->notify_hotplug_work, delay);
+ return 0;
+}
+
+/*
+ * platform functions
+ */
+static int __devinit usbhs_probe(struct platform_device *pdev)
+{
+ struct renesas_usbhs_platform_info *info = pdev->dev.platform_data;
+ struct renesas_usbhs_driver_callback *dfunc;
+ struct usbhs_priv *priv;
+ struct resource *res;
+ unsigned int irq;
+ int ret;
+
+ /* check platform information */
+ if (!info ||
+ !info->platform_callback.get_id) {
+ dev_err(&pdev->dev, "no platform information\n");
+ return -EINVAL;
+ }
+
+ /* platform data */
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ irq = platform_get_irq(pdev, 0);
+ if (!res || (int)irq <= 0) {
+ dev_err(&pdev->dev, "Not enough Renesas USB platform resources.\n");
+ return -ENODEV;
+ }
+
+ /* usb private data */
+ priv = kzalloc(sizeof(*priv), GFP_KERNEL);
+ if (!priv) {
+ dev_err(&pdev->dev, "Could not allocate priv\n");
+ return -ENOMEM;
+ }
+
+ priv->base = ioremap_nocache(res->start, resource_size(res));
+ if (!priv->base) {
+ dev_err(&pdev->dev, "ioremap error.\n");
+ ret = -ENOMEM;
+ goto probe_end_kfree;
+ }
+
+ /*
+ * care platform info
+ */
+ priv->pfunc = &info->platform_callback;
+ priv->dparam = &info->driver_param;
+
+ /* set driver callback functions for platform */
+ dfunc = &info->driver_callback;
+ dfunc->notify_hotplug = usbhsc_drvcllbck_notify_hotplug;
+
+ /* set default param if platform doesn't have */
+ if (!priv->dparam->pipe_type) {
+ priv->dparam->pipe_type = usbhsc_default_pipe_type;
+ priv->dparam->pipe_size = ARRAY_SIZE(usbhsc_default_pipe_type);
+ }
+
+ /* FIXME */
+ /* runtime power control ? */
+ if (priv->pfunc->get_vbus)
+ usbhsc_flags_set(priv, USBHSF_RUNTIME_PWCTRL);
+
+ /*
+ * priv settings
+ */
+ priv->irq = irq;
+ priv->pdev = pdev;
+ INIT_DELAYED_WORK(&priv->notify_hotplug_work, usbhsc_notify_hotplug);
+ spin_lock_init(usbhs_priv_to_lock(priv));
+
+ /* call pipe and module init */
+ ret = usbhs_pipe_probe(priv);
+ if (ret < 0)
+ goto probe_end_iounmap;
+
+ ret = usbhs_mod_probe(priv);
+ if (ret < 0)
+ goto probe_end_pipe_exit;
+
+ /* dev_set_drvdata should be called after usbhs_mod_init */
+ dev_set_drvdata(&pdev->dev, priv);
+
+ /*
+ * deviece reset here because
+ * USB device might be used in boot loader.
+ */
+ usbhs_sys_clock_ctrl(priv, 0);
+
+ /*
+ * platform call
+ *
+ * USB phy setup might depend on CPU/Board.
+ * If platform has its callback functions,
+ * call it here.
+ */
+ ret = usbhs_platform_call(priv, hardware_init, pdev);
+ if (ret < 0) {
+ dev_err(&pdev->dev, "platform prove failed.\n");
+ goto probe_end_mod_exit;
+ }
+
+ /* reset phy for connection */
+ usbhs_platform_call(priv, phy_reset, pdev);
+
+ /* power control */
+ pm_runtime_enable(&pdev->dev);
+ if (!usbhsc_flags_has(priv, USBHSF_RUNTIME_PWCTRL)) {
+ usbhsc_power_ctrl(priv, 1);
+ usbhs_mod_autonomy_mode(priv);
+ }
+
+ /*
+ * manual call notify_hotplug for cold plug
+ */
+ ret = usbhsc_drvcllbck_notify_hotplug(pdev);
+ if (ret < 0)
+ goto probe_end_call_remove;
+
+ dev_info(&pdev->dev, "probed\n");
+
+ return ret;
+
+probe_end_call_remove:
+ usbhs_platform_call(priv, hardware_exit, pdev);
+probe_end_mod_exit:
+ usbhs_mod_remove(priv);
+probe_end_pipe_exit:
+ usbhs_pipe_remove(priv);
+probe_end_iounmap:
+ iounmap(priv->base);
+probe_end_kfree:
+ kfree(priv);
+
+ dev_info(&pdev->dev, "probe failed\n");
+
+ return ret;
+}
+
+static int __devexit usbhs_remove(struct platform_device *pdev)
+{
+ struct usbhs_priv *priv = usbhs_pdev_to_priv(pdev);
+ struct renesas_usbhs_platform_info *info = pdev->dev.platform_data;
+ struct renesas_usbhs_driver_callback *dfunc = &info->driver_callback;
+
+ dev_dbg(&pdev->dev, "usb remove\n");
+
+ dfunc->notify_hotplug = NULL;
+
+ /* power off */
+ if (!usbhsc_flags_has(priv, USBHSF_RUNTIME_PWCTRL))
+ usbhsc_power_ctrl(priv, 0);
+
+ pm_runtime_disable(&pdev->dev);
+
+ usbhs_platform_call(priv, hardware_exit, pdev);
+ usbhs_mod_remove(priv);
+ usbhs_pipe_remove(priv);
+ iounmap(priv->base);
+ kfree(priv);
+
+ return 0;
+}
+
+static struct platform_driver renesas_usbhs_driver = {
+ .driver = {
+ .name = "renesas_usbhs",
+ },
+ .probe = usbhs_probe,
+ .remove = __devexit_p(usbhs_remove),
+};
+
+static int __init usbhs_init(void)
+{
+ return platform_driver_register(&renesas_usbhs_driver);
+}
+
+static void __exit usbhs_exit(void)
+{
+ platform_driver_unregister(&renesas_usbhs_driver);
+}
+
+module_init(usbhs_init);
+module_exit(usbhs_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Renesas USB driver");
+MODULE_AUTHOR("Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>");
diff --git a/drivers/usb/renesas_usbhs/common.h b/drivers/usb/renesas_usbhs/common.h
new file mode 100644
index 00000000000..0aadcb40276
--- /dev/null
+++ b/drivers/usb/renesas_usbhs/common.h
@@ -0,0 +1,230 @@
+/*
+ * Renesas USB driver
+ *
+ * Copyright (C) 2011 Renesas Solutions Corp.
+ * Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+#ifndef RENESAS_USB_DRIVER_H
+#define RENESAS_USB_DRIVER_H
+
+#include <linux/platform_device.h>
+#include <linux/usb/renesas_usbhs.h>
+
+struct usbhs_priv;
+
+#include "./mod.h"
+#include "./pipe.h"
+
+/*
+ *
+ * register define
+ *
+ */
+#define SYSCFG 0x0000
+#define BUSWAIT 0x0002
+#define DVSTCTR 0x0008
+#define CFIFO 0x0014
+#define CFIFOSEL 0x0020
+#define CFIFOCTR 0x0022
+#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
+
+/* SYSCFG */
+#define SCKE (1 << 10) /* USB Module Clock Enable */
+#define HSE (1 << 7) /* High-Speed Operation Enable */
+#define DCFM (1 << 6) /* Controller Function Select */
+#define DRPD (1 << 5) /* D+ Line/D- Line Resistance Control */
+#define DPRPU (1 << 4) /* D+ Line Resistance Control */
+#define USBE (1 << 0) /* USB Module Operation Enable */
+
+/* DVSTCTR */
+#define EXTLP (1 << 10) /* Controls the EXTLP pin output state */
+#define PWEN (1 << 9) /* Controls the PWEN pin output state */
+#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 MBW_32 (0x2 << 10) /* CFIFO Port Access Bit Width */
+
+/* CFIFOCTR */
+#define BVAL (1 << 15) /* Buffer Memory Enable Flag */
+#define BCLR (1 << 14) /* CPU buffer clear */
+#define FRDY (1 << 13) /* FIFO Port Ready */
+#define DTLN_MASK (0x0FFF) /* Receive Data Length */
+
+/* INTENB0 */
+#define VBSE (1 << 15) /* Enable IRQ VBUS_0 and VBUSIN_0 */
+#define RSME (1 << 14) /* Enable IRQ Resume */
+#define SOFE (1 << 13) /* Enable IRQ Frame Number Update */
+#define DVSE (1 << 12) /* Enable IRQ Device State Transition */
+#define CTRE (1 << 11) /* Enable IRQ Control Stage Transition */
+#define BEMPE (1 << 10) /* Enable IRQ Buffer Empty */
+#define NRDYE (1 << 9) /* Enable IRQ Buffer Not Ready Response */
+#define BRDYE (1 << 8) /* Enable IRQ Buffer Ready */
+
+/* INTENB1 */
+#define BCHGE (1 << 14) /* USB Bus Change Interrupt Enable */
+#define DTCHE (1 << 12) /* Disconnection Detect Interrupt Enable */
+#define ATTCHE (1 << 11) /* Connection Detect Interrupt Enable */
+#define EOFERRE (1 << 6) /* EOF Error Detect Interrupt Enable */
+#define SIGNE (1 << 5) /* Setup Transaction Error Interrupt Enable */
+#define SACKE (1 << 4) /* Setup Transaction ACK Interrupt Enable */
+
+/* INTSTS0 */
+#define VBINT (1 << 15) /* VBUS0_0 and VBUS1_0 Interrupt Status */
+#define DVST (1 << 12) /* Device State Transition Interrupt Status */
+#define CTRT (1 << 11) /* Control Stage Interrupt Status */
+#define BEMP (1 << 10) /* Buffer Empty Interrupt Status */
+#define BRDY (1 << 8) /* Buffer Ready Interrupt Status */
+#define VBSTS (1 << 7) /* VBUS_0 and VBUSIN_0 Input Status */
+#define VALID (1 << 3) /* USB Request Receive */
+
+#define DVSQ_MASK (0x3 << 4) /* Device State */
+#define POWER_STATE (0 << 4)
+#define DEFAULT_STATE (1 << 4)
+#define ADDRESS_STATE (2 << 4)
+#define CONFIGURATION_STATE (3 << 4)
+
+#define CTSQ_MASK (0x7) /* Control Transfer Stage */
+#define IDLE_SETUP_STAGE 0 /* Idle stage or setup stage */
+#define READ_DATA_STAGE 1 /* Control read data stage */
+#define READ_STATUS_STAGE 2 /* Control read status stage */
+#define WRITE_DATA_STAGE 3 /* Control write data stage */
+#define WRITE_STATUS_STAGE 4 /* Control write status stage */
+#define NODATA_STATUS_STAGE 5 /* Control write NoData status stage */
+#define SEQUENCE_ERROR 6 /* Control transfer sequence error */
+
+/* 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 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 CSSTS (1 << 12) /* CSSTS Status */
+#define SQCLR (1 << 8) /* Toggle Bit Clear */
+#define ACLRM (1 << 9) /* Buffer Auto-Clear Mode */
+#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 */
+
+/* FRMNUM */
+#define FRNM_MASK (0x7FF)
+
+/*
+ * struct
+ */
+struct usbhs_priv {
+
+ void __iomem *base;
+ unsigned int irq;
+
+ struct renesas_usbhs_platform_callback *pfunc;
+ struct renesas_usbhs_driver_param *dparam;
+
+ struct delayed_work notify_hotplug_work;
+ struct platform_device *pdev;
+
+ spinlock_t lock;
+
+ u32 flags;
+
+ /*
+ * module control
+ */
+ struct usbhs_mod_info mod_info;
+
+ /*
+ * pipe control
+ */
+ struct usbhs_pipe_info pipe_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);
+
+int usbhsc_drvcllbck_notify_hotplug(struct platform_device *pdev);
+/*
+ * sysconfig
+ */
+void usbhs_sys_clock_ctrl(struct usbhs_priv *priv, int enable);
+void usbhs_sys_hispeed_ctrl(struct usbhs_priv *priv, int enable);
+void usbhs_sys_usb_ctrl(struct usbhs_priv *priv, int enable);
+void usbhs_sys_host_ctrl(struct usbhs_priv *priv, int enable);
+void usbhs_sys_function_ctrl(struct usbhs_priv *priv, int enable);
+
+/*
+ * frame
+ */
+int usbhs_frame_get_num(struct usbhs_priv *priv);
+
+/*
+ * data
+ */
+struct usbhs_priv *usbhs_pdev_to_priv(struct platform_device *pdev);
+#define usbhs_get_dparam(priv, param) (priv->dparam->param)
+#define usbhs_priv_to_pdev(priv) (priv->pdev)
+#define usbhs_priv_to_dev(priv) (&priv->pdev->dev)
+#define usbhs_priv_to_lock(priv) (&priv->lock)
+
+#endif /* RENESAS_USB_DRIVER_H */
diff --git a/drivers/usb/renesas_usbhs/mod.c b/drivers/usb/renesas_usbhs/mod.c
new file mode 100644
index 00000000000..a577f8f4064
--- /dev/null
+++ b/drivers/usb/renesas_usbhs/mod.c
@@ -0,0 +1,328 @@
+/*
+ * Renesas USB driver
+ *
+ * Copyright (C) 2011 Renesas Solutions Corp.
+ * Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+#include <linux/interrupt.h>
+
+#include "./common.h"
+#include "./mod.h"
+
+#define usbhs_priv_to_modinfo(priv) (&priv->mod_info)
+#define usbhs_mod_info_call(priv, func, param...) \
+({ \
+ struct usbhs_mod_info *info; \
+ info = usbhs_priv_to_modinfo(priv); \
+ !info->func ? 0 : \
+ info->func(param); \
+})
+
+/*
+ * autonomy
+ *
+ * these functions are used if platform doesn't have external phy.
+ * -> there is no "notify_hotplug" callback from platform
+ * -> call "notify_hotplug" by itself
+ * -> use own interrupt to connect/disconnect
+ * -> it mean module clock is always ON
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~
+ */
+static int usbhsm_autonomy_get_vbus(struct platform_device *pdev)
+{
+ struct usbhs_priv *priv = usbhs_pdev_to_priv(pdev);
+
+ return VBSTS & usbhs_read(priv, INTSTS0);
+}
+
+static int usbhsm_autonomy_irq_vbus(struct usbhs_priv *priv,
+ struct usbhs_irq_state *irq_state)
+{
+ struct platform_device *pdev = usbhs_priv_to_pdev(priv);
+
+ return usbhsc_drvcllbck_notify_hotplug(pdev);
+}
+
+void usbhs_mod_autonomy_mode(struct usbhs_priv *priv)
+{
+ struct usbhs_mod_info *info = usbhs_priv_to_modinfo(priv);
+
+ info->irq_vbus = usbhsm_autonomy_irq_vbus;
+ priv->pfunc->get_vbus = usbhsm_autonomy_get_vbus;
+
+ usbhs_irq_callback_update(priv, NULL);
+}
+
+/*
+ * host / gadget functions
+ *
+ * renesas_usbhs host/gadget can register itself by below functions.
+ * these functions are called when probe
+ *
+ */
+void usbhs_mod_register(struct usbhs_priv *priv, struct usbhs_mod *mod, int id)
+{
+ struct usbhs_mod_info *info = usbhs_priv_to_modinfo(priv);
+
+ info->mod[id] = mod;
+ mod->priv = priv;
+}
+
+struct usbhs_mod *usbhs_mod_get(struct usbhs_priv *priv, int id)
+{
+ struct usbhs_mod_info *info = usbhs_priv_to_modinfo(priv);
+ struct usbhs_mod *ret = NULL;
+
+ switch (id) {
+ case USBHS_HOST:
+ case USBHS_GADGET:
+ ret = info->mod[id];
+ break;
+ }
+
+ return ret;
+}
+
+int usbhs_mod_is_host(struct usbhs_priv *priv, struct usbhs_mod *mod)
+{
+ struct usbhs_mod_info *info = usbhs_priv_to_modinfo(priv);
+
+ if (!mod)
+ return -EINVAL;
+
+ return info->mod[USBHS_HOST] == mod;
+}
+
+struct usbhs_mod *usbhs_mod_get_current(struct usbhs_priv *priv)
+{
+ struct usbhs_mod_info *info = usbhs_priv_to_modinfo(priv);
+
+ return info->curt;
+}
+
+int usbhs_mod_change(struct usbhs_priv *priv, int id)
+{
+ struct usbhs_mod_info *info = usbhs_priv_to_modinfo(priv);
+ struct usbhs_mod *mod = NULL;
+ int ret = 0;
+
+ /* id < 0 mean no current */
+ switch (id) {
+ case USBHS_HOST:
+ case USBHS_GADGET:
+ mod = info->mod[id];
+ break;
+ default:
+ ret = -EINVAL;
+ }
+ info->curt = mod;
+
+ return ret;
+}
+
+static irqreturn_t usbhs_interrupt(int irq, void *data);
+int usbhs_mod_probe(struct usbhs_priv *priv)
+{
+ struct device *dev = usbhs_priv_to_dev(priv);
+ int ret;
+
+ /*
+ * install host/gadget driver
+ */
+ ret = usbhs_mod_gadget_probe(priv);
+ if (ret < 0)
+ return ret;
+
+ /* irq settings */
+ ret = request_irq(priv->irq, usbhs_interrupt,
+ IRQF_DISABLED, dev_name(dev), priv);
+ if (ret) {
+ dev_err(dev, "irq request err\n");
+ goto mod_init_gadget_err;
+ }
+
+ return ret;
+
+mod_init_gadget_err:
+ usbhs_mod_gadget_remove(priv);
+
+ return ret;
+}
+
+void usbhs_mod_remove(struct usbhs_priv *priv)
+{
+ usbhs_mod_gadget_remove(priv);
+ free_irq(priv->irq, priv);
+}
+
+/*
+ * status functions
+ */
+int usbhs_status_get_usb_speed(struct usbhs_irq_state *irq_state)
+{
+ switch (irq_state->dvstctr & RHST) {
+ case RHST_LOW_SPEED:
+ return USB_SPEED_LOW;
+ case RHST_FULL_SPEED:
+ return USB_SPEED_FULL;
+ case RHST_HIGH_SPEED:
+ return USB_SPEED_HIGH;
+ }
+
+ return USB_SPEED_UNKNOWN;
+}
+
+int usbhs_status_get_device_state(struct usbhs_irq_state *irq_state)
+{
+ int state = irq_state->intsts0 & DVSQ_MASK;
+
+ switch (state) {
+ case POWER_STATE:
+ case DEFAULT_STATE:
+ case ADDRESS_STATE:
+ case CONFIGURATION_STATE:
+ return state;
+ }
+
+ return -EIO;
+}
+
+int usbhs_status_get_ctrl_stage(struct usbhs_irq_state *irq_state)
+{
+ /*
+ * return value
+ *
+ * IDLE_SETUP_STAGE
+ * READ_DATA_STAGE
+ * READ_STATUS_STAGE
+ * WRITE_DATA_STAGE
+ * WRITE_STATUS_STAGE
+ * NODATA_STATUS_STAGE
+ * SEQUENCE_ERROR
+ */
+ return (int)irq_state->intsts0 & CTSQ_MASK;
+}
+
+static void usbhs_status_get_each_irq(struct usbhs_priv *priv,
+ struct usbhs_irq_state *state)
+{
+ struct usbhs_mod *mod = usbhs_mod_get_current(priv);
+
+ state->intsts0 = usbhs_read(priv, INTSTS0);
+ state->intsts1 = usbhs_read(priv, INTSTS1);
+
+ state->dvstctr = usbhs_read(priv, DVSTCTR);
+
+ /* 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;
+ }
+}
+
+/*
+ * interrupt
+ */
+#define INTSTS0_MAGIC 0xF800 /* acknowledge magical interrupt sources */
+#define INTSTS1_MAGIC 0xA870 /* acknowledge magical interrupt sources */
+static irqreturn_t usbhs_interrupt(int irq, void *data)
+{
+ struct usbhs_priv *priv = data;
+ struct usbhs_irq_state irq_state;
+
+ usbhs_status_get_each_irq(priv, &irq_state);
+
+ /*
+ * 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);
+ usbhs_write(priv, INTSTS1, ~irq_state.intsts1 & INTSTS1_MAGIC);
+
+ usbhs_write(priv, BRDYSTS, 0);
+ usbhs_write(priv, NRDYSTS, 0);
+ usbhs_write(priv, BEMPSTS, 0);
+
+ /*
+ * call irq callback functions
+ * see also
+ * usbhs_irq_setting_update
+ */
+ 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);
+
+ return IRQ_HANDLED;
+}
+
+void usbhs_irq_callback_update(struct usbhs_priv *priv, struct usbhs_mod *mod)
+{
+ u16 intenb0 = 0;
+ struct usbhs_mod_info *info = usbhs_priv_to_modinfo(priv);
+
+ usbhs_write(priv, INTENB0, 0);
+
+ usbhs_write(priv, BEMPENB, 0);
+ usbhs_write(priv, BRDYENB, 0);
+
+ /*
+ * see also
+ * usbhs_interrupt
+ */
+
+ /*
+ * it don't enable DVSE (intenb0) here
+ * but "mod->irq_dev_state" will be called.
+ */
+ if (info->irq_vbus)
+ intenb0 |= VBSE;
+
+ if (mod) {
+ if (mod->irq_ctrl_stage)
+ intenb0 |= CTRE;
+
+ if (mod->irq_empty && mod->irq_bempsts) {
+ usbhs_write(priv, BEMPENB, mod->irq_bempsts);
+ intenb0 |= BEMPE;
+ }
+
+ if (mod->irq_ready && mod->irq_brdysts) {
+ usbhs_write(priv, BRDYENB, mod->irq_brdysts);
+ intenb0 |= BRDYE;
+ }
+ }
+
+ usbhs_write(priv, INTENB0, intenb0);
+}
diff --git a/drivers/usb/renesas_usbhs/mod.h b/drivers/usb/renesas_usbhs/mod.h
new file mode 100644
index 00000000000..5c845a28a21
--- /dev/null
+++ b/drivers/usb/renesas_usbhs/mod.h
@@ -0,0 +1,137 @@
+/*
+ * Renesas USB driver
+ *
+ * Copyright (C) 2011 Renesas Solutions Corp.
+ * Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+#ifndef RENESAS_USB_MOD_H
+#define RENESAS_USB_MOD_H
+
+#include <linux/spinlock.h>
+#include <linux/usb/renesas_usbhs.h>
+#include "./common.h"
+
+/*
+ * struct
+ */
+struct usbhs_irq_state {
+ u16 intsts0;
+ u16 intsts1;
+ u16 brdysts;
+ u16 nrdysts;
+ u16 bempsts;
+ u16 dvstctr;
+};
+
+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);
+
+ /* INTSTS0 :: CTRT (CTSQ) */
+ int (*irq_ctrl_stage)(struct usbhs_priv *priv,
+ struct usbhs_irq_state *irq_state);
+
+ /* INTSTS0 :: BEMP */
+ /* BEMPSTS */
+ int (*irq_empty)(struct usbhs_priv *priv,
+ struct usbhs_irq_state *irq_state);
+ u16 irq_bempsts;
+
+ /* INTSTS0 :: BRDY */
+ /* BRDYSTS */
+ int (*irq_ready)(struct usbhs_priv *priv,
+ struct usbhs_irq_state *irq_state);
+ u16 irq_brdysts;
+
+ struct usbhs_priv *priv;
+};
+
+struct usbhs_mod_info {
+ struct usbhs_mod *mod[USBHS_MAX];
+ struct usbhs_mod *curt; /* current mod */
+
+ /*
+ * INTSTS0 :: VBINT
+ *
+ * This function will be used as autonomy mode
+ * when platform cannot call notify_hotplug.
+ *
+ * This callback cannot be member of "struct usbhs_mod"
+ * because it will be used even though
+ * host/gadget has not been selected.
+ */
+ int (*irq_vbus)(struct usbhs_priv *priv,
+ struct usbhs_irq_state *irq_state);
+};
+
+/*
+ * for host/gadget module
+ */
+struct usbhs_mod *usbhs_mod_get(struct usbhs_priv *priv, int id);
+struct usbhs_mod *usbhs_mod_get_current(struct usbhs_priv *priv);
+void usbhs_mod_register(struct usbhs_priv *priv, struct usbhs_mod *usb, int id);
+int usbhs_mod_is_host(struct usbhs_priv *priv, struct usbhs_mod *mod);
+int usbhs_mod_change(struct usbhs_priv *priv, int id);
+int usbhs_mod_probe(struct usbhs_priv *priv);
+void usbhs_mod_remove(struct usbhs_priv *priv);
+
+void usbhs_mod_autonomy_mode(struct usbhs_priv *priv);
+
+/*
+ * status functions
+ */
+int usbhs_status_get_usb_speed(struct usbhs_irq_state *irq_state);
+int usbhs_status_get_device_state(struct usbhs_irq_state *irq_state);
+int usbhs_status_get_ctrl_stage(struct usbhs_irq_state *irq_state);
+
+/*
+ * callback functions
+ */
+void usbhs_irq_callback_update(struct usbhs_priv *priv, struct usbhs_mod *mod);
+
+
+#define usbhs_mod_call(priv, func, param...) \
+ ({ \
+ struct usbhs_mod *mod; \
+ mod = usbhs_mod_get_current(priv); \
+ !mod ? -ENODEV : \
+ !mod->func ? 0 : \
+ mod->func(param); \
+ })
+
+/*
+ * gadget control
+ */
+#ifdef CONFIG_USB_RENESAS_USBHS_UDC
+extern int __devinit usbhs_mod_gadget_probe(struct usbhs_priv *priv);
+extern void __devexit usbhs_mod_gadget_remove(struct usbhs_priv *priv);
+#else
+static inline int usbhs_mod_gadget_probe(struct usbhs_priv *priv)
+{
+ return 0;
+}
+static inline void usbhs_mod_gadget_remove(struct usbhs_priv *priv)
+{
+}
+#endif
+
+#endif /* RENESAS_USB_MOD_H */
diff --git a/drivers/usb/renesas_usbhs/mod_gadget.c b/drivers/usb/renesas_usbhs/mod_gadget.c
new file mode 100644
index 00000000000..206cfabc928
--- /dev/null
+++ b/drivers/usb/renesas_usbhs/mod_gadget.c
@@ -0,0 +1,1384 @@
+/*
+ * Renesas USB driver
+ *
+ * Copyright (C) 2011 Renesas Solutions Corp.
+ * Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/usb/ch9.h>
+#include <linux/usb/gadget.h>
+#include "common.h"
+
+/*
+ * struct
+ */
+struct usbhsg_request {
+ struct usb_request req;
+ struct list_head node;
+};
+
+#define EP_NAME_SIZE 8
+struct usbhsg_gpriv;
+struct usbhsg_pipe_handle;
+struct usbhsg_uep {
+ struct usb_ep ep;
+ struct usbhs_pipe *pipe;
+ struct list_head list;
+
+ char ep_name[EP_NAME_SIZE];
+
+ struct usbhsg_gpriv *gpriv;
+ struct usbhsg_pipe_handle *handler;
+};
+
+struct usbhsg_gpriv {
+ struct usb_gadget gadget;
+ struct usbhs_mod mod;
+
+ struct usbhsg_uep *uep;
+ int uep_size;
+
+ struct usb_gadget_driver *driver;
+
+ u32 status;
+#define USBHSG_STATUS_STARTED (1 << 0)
+#define USBHSG_STATUS_REGISTERD (1 << 1)
+#define USBHSG_STATUS_WEDGE (1 << 2)
+};
+
+struct usbhsg_pipe_handle {
+ int (*prepare)(struct usbhsg_uep *uep, struct usbhsg_request *ureq);
+ int (*try_run)(struct usbhsg_uep *uep, struct usbhsg_request *ureq);
+ void (*irq_mask)(struct usbhsg_uep *uep, int enable);
+};
+
+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, pos = (g)->uep; \
+ i < (g)->uep_size; \
+ i++, pos = (g)->uep + 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_lock(gp) usbhs_priv_to_lock((gp)->mod.priv)
+#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_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)
+
+/*
+ * usbhsg_trylock
+ *
+ * This driver don't use spin_try_lock
+ * to avoid warning of CONFIG_DEBUG_SPINLOCK
+ */
+static spinlock_t *usbhsg_trylock(struct usbhsg_gpriv *gpriv,
+ unsigned long *flags)
+{
+ spinlock_t *lock = usbhsg_gpriv_to_lock(gpriv);
+
+ /* check spin lock status
+ * to avoid deadlock/nest */
+ if (spin_is_locked(lock))
+ return NULL;
+
+ spin_lock_irqsave(lock, *flags);
+
+ return lock;
+}
+
+static void usbhsg_unlock(spinlock_t *lock, unsigned long *flags)
+{
+ if (!lock)
+ return;
+
+ spin_unlock_irqrestore(lock, *flags);
+}
+
+/*
+ * list push/pop
+ */
+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);
+
+ /*
+ ********* assume under spin lock *********
+ */
+ list_del_init(&ureq->node);
+ list_add_tail(&ureq->node, &uep->list);
+ ureq->req.actual = 0;
+ ureq->req.status = -EINPROGRESS;
+
+ dev_dbg(dev, "pipe %d : queue push (%d)\n",
+ usbhs_pipe_number(pipe),
+ ureq->req.length);
+}
+
+static struct usbhsg_request *usbhsg_queue_get(struct usbhsg_uep *uep)
+{
+ /*
+ ********* assume under spin lock *********
+ */
+ if (list_empty(&uep->list))
+ return NULL;
+
+ return list_entry(uep->list.next, struct usbhsg_request, node);
+}
+
+#define usbhsg_queue_prepare(uep) __usbhsg_queue_handler(uep, 1);
+#define usbhsg_queue_handle(uep) __usbhsg_queue_handler(uep, 0);
+static int __usbhsg_queue_handler(struct usbhsg_uep *uep, int prepare)
+{
+ struct usbhsg_gpriv *gpriv = usbhsg_uep_to_gpriv(uep);
+ struct device *dev = usbhsg_gpriv_to_dev(gpriv);
+ struct usbhsg_request *ureq;
+ spinlock_t *lock;
+ unsigned long flags;
+ int ret = 0;
+
+ if (!uep->handler) {
+ dev_err(dev, "no handler function\n");
+ return -EIO;
+ }
+
+ /*
+ * CAUTION [*queue handler*]
+ *
+ * This function will be called for start/restart queue operation.
+ * OTOH the most much worry for USB driver is spinlock nest.
+ * Specially it are
+ * - usb_ep_ops :: queue
+ * - usb_request :: complete
+ *
+ * But the caller of this function need not care about spinlock.
+ * This function is using usbhsg_trylock for it.
+ * if "is_locked" is 1, this mean this function lock it.
+ * but if it is 0, this mean it is already under spin lock.
+ * see also
+ * CAUTION [*endpoint queue*]
+ * CAUTION [*request complete*]
+ */
+
+ /****************** spin try lock *******************/
+ lock = usbhsg_trylock(gpriv, &flags);
+
+ ureq = usbhsg_queue_get(uep);
+ if (ureq) {
+ if (prepare)
+ ret = uep->handler->prepare(uep, ureq);
+ else
+ ret = uep->handler->try_run(uep, ureq);
+ }
+ usbhsg_unlock(lock, &flags);
+ /******************** spin unlock ******************/
+
+ return ret;
+}
+
+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);
+
+ /*
+ ********* assume under spin lock *********
+ */
+
+ /*
+ * CAUTION [*request complete*]
+ *
+ * There is a possibility not to be called in correct order
+ * if "complete" is called without spinlock.
+ *
+ * So, this function assume it is under spinlock,
+ * and call usb_request :: complete.
+ *
+ * But this "complete" will push next usb_request.
+ * It mean "usb_ep_ops :: queue" which is using spinlock is called
+ * under spinlock.
+ *
+ * To avoid dead-lock, this driver is using usbhsg_trylock.
+ * CAUTION [*endpoint queue*]
+ * CAUTION [*queue handler*]
+ */
+
+ dev_dbg(dev, "pipe %d : queue pop\n", usbhs_pipe_number(pipe));
+
+ list_del_init(&ureq->node);
+
+ ureq->req.status = status;
+ ureq->req.complete(&uep->ep, &ureq->req);
+
+ /* more request ? */
+ if (0 == status)
+ usbhsg_queue_prepare(uep);
+}
+
+/*
+ * irq enable/disable function
+ */
+#define usbhsg_irq_callback_ctrl(uep, status, enable) \
+ ({ \
+ struct usbhsg_gpriv *gpriv = usbhsg_uep_to_gpriv(uep); \
+ struct usbhs_pipe *pipe = usbhsg_uep_to_pipe(uep); \
+ struct usbhs_priv *priv = usbhsg_gpriv_to_priv(gpriv); \
+ struct usbhs_mod *mod = usbhs_mod_get_current(priv); \
+ if (!mod) \
+ return; \
+ if (enable) \
+ mod->irq_##status |= (1 << usbhs_pipe_number(pipe)); \
+ else \
+ mod->irq_##status &= ~(1 << usbhs_pipe_number(pipe)); \
+ usbhs_irq_callback_update(priv, mod); \
+ })
+
+static void usbhsg_irq_empty_ctrl(struct usbhsg_uep *uep, int enable)
+{
+ usbhsg_irq_callback_ctrl(uep, bempsts, enable);
+}
+
+static void usbhsg_irq_ready_ctrl(struct usbhsg_uep *uep, int enable)
+{
+ usbhsg_irq_callback_ctrl(uep, brdysts, enable);
+}
+
+/*
+ * handler function
+ */
+static int usbhsg_try_run_ctrl_stage_end(struct usbhsg_uep *uep,
+ struct usbhsg_request *ureq)
+{
+ struct usbhs_pipe *pipe = usbhsg_uep_to_pipe(uep);
+
+ /*
+ ********* assume under spin lock *********
+ */
+
+ usbhs_dcp_control_transfer_done(pipe);
+ usbhsg_queue_pop(uep, ureq, 0);
+
+ return 0;
+}
+
+static int usbhsg_try_run_send_packet(struct usbhsg_uep *uep,
+ struct usbhsg_request *ureq)
+{
+ struct usbhs_pipe *pipe = usbhsg_uep_to_pipe(uep);
+ struct usb_request *req = &ureq->req;
+ struct usbhsg_gpriv *gpriv = usbhsg_uep_to_gpriv(uep);
+ struct device *dev = usbhsg_gpriv_to_dev(gpriv);
+ void *buf;
+ int remainder, send;
+ int is_done = 0;
+ int enable;
+ int maxp;
+
+ /*
+ ********* assume under spin lock *********
+ */
+
+ maxp = usbhs_pipe_get_maxpacket(pipe);
+ buf = req->buf + req->actual;
+ remainder = req->length - req->actual;
+
+ send = usbhs_fifo_write(pipe, buf, remainder);
+
+ /*
+ * send < 0 : pipe busy
+ * send = 0 : send zero packet
+ * send > 0 : send data
+ *
+ * send <= max_packet
+ */
+ if (send > 0)
+ req->actual += send;
+
+ /* send all packet ? */
+ if (send < remainder)
+ is_done = 0; /* there are remainder data */
+ else if (send < maxp)
+ is_done = 1; /* short packet */
+ else
+ is_done = !req->zero; /* send zero packet ? */
+
+ dev_dbg(dev, " send %d (%d/ %d/ %d/ %d)\n",
+ usbhs_pipe_number(pipe),
+ remainder, send, is_done, req->zero);
+
+ /*
+ * enable interrupt and send again in irq handler
+ * if it still have remainder data which should be sent.
+ */
+ enable = !is_done;
+ uep->handler->irq_mask(uep, enable);
+
+ /*
+ * usbhs_fifo_enable execute
+ * - after callback_update,
+ * - before queue_pop / stage_end
+ */
+ usbhs_fifo_enable(pipe);
+
+ /*
+ * all data were sent ?
+ */
+ if (is_done) {
+ /* it care below call in
+ "function mode" */
+ if (usbhsg_is_dcp(uep))
+ usbhs_dcp_control_transfer_done(pipe);
+
+ usbhsg_queue_pop(uep, ureq, 0);
+ }
+
+ return 0;
+}
+
+static int usbhsg_prepare_send_packet(struct usbhsg_uep *uep,
+ struct usbhsg_request *ureq)
+{
+ struct usbhs_pipe *pipe = usbhsg_uep_to_pipe(uep);
+
+ /*
+ ********* assume under spin lock *********
+ */
+
+ usbhs_fifo_prepare_write(pipe);
+ usbhsg_try_run_send_packet(uep, ureq);
+
+ return 0;
+}
+
+static int usbhsg_try_run_receive_packet(struct usbhsg_uep *uep,
+ struct usbhsg_request *ureq)
+{
+ struct usbhs_pipe *pipe = usbhsg_uep_to_pipe(uep);
+ struct usb_request *req = &ureq->req;
+ struct usbhsg_gpriv *gpriv = usbhsg_uep_to_gpriv(uep);
+ struct device *dev = usbhsg_gpriv_to_dev(gpriv);
+ void *buf;
+ int maxp;
+ int remainder, recv;
+ int is_done = 0;
+
+ /*
+ ********* assume under spin lock *********
+ */
+
+ maxp = usbhs_pipe_get_maxpacket(pipe);
+ buf = req->buf + req->actual;
+ remainder = req->length - req->actual;
+
+ recv = usbhs_fifo_read(pipe, buf, remainder);
+ /*
+ * recv < 0 : pipe busy
+ * recv >= 0 : receive data
+ *
+ * recv <= max_packet
+ */
+ if (recv < 0)
+ return -EBUSY;
+
+ /* update parameters */
+ req->actual += recv;
+
+ if ((recv == remainder) || /* receive all data */
+ (recv < maxp)) /* short packet */
+ is_done = 1;
+
+ dev_dbg(dev, " recv %d (%d/ %d/ %d/ %d)\n",
+ usbhs_pipe_number(pipe),
+ remainder, recv, is_done, req->zero);
+
+ /* read all data ? */
+ if (is_done) {
+ int disable = 0;
+
+ uep->handler->irq_mask(uep, disable);
+ usbhs_fifo_disable(pipe);
+ usbhsg_queue_pop(uep, ureq, 0);
+ }
+
+ return 0;
+}
+
+static int usbhsg_prepare_receive_packet(struct usbhsg_uep *uep,
+ struct usbhsg_request *ureq)
+{
+ struct usbhs_pipe *pipe = usbhsg_uep_to_pipe(uep);
+ int enable = 1;
+ int ret;
+
+ /*
+ ********* assume under spin lock *********
+ */
+
+ ret = usbhs_fifo_prepare_read(pipe);
+ if (ret < 0)
+ return ret;
+
+ /*
+ * data will be read in interrupt handler
+ */
+ uep->handler->irq_mask(uep, enable);
+
+ return ret;
+}
+
+static struct usbhsg_pipe_handle usbhsg_handler_send_by_empty = {
+ .prepare = usbhsg_prepare_send_packet,
+ .try_run = usbhsg_try_run_send_packet,
+ .irq_mask = usbhsg_irq_empty_ctrl,
+};
+
+static struct usbhsg_pipe_handle usbhsg_handler_send_by_ready = {
+ .prepare = usbhsg_prepare_send_packet,
+ .try_run = usbhsg_try_run_send_packet,
+ .irq_mask = usbhsg_irq_ready_ctrl,
+};
+
+static struct usbhsg_pipe_handle usbhsg_handler_recv_by_ready = {
+ .prepare = usbhsg_prepare_receive_packet,
+ .try_run = usbhsg_try_run_receive_packet,
+ .irq_mask = usbhsg_irq_ready_ctrl,
+};
+
+static struct usbhsg_pipe_handle usbhsg_handler_ctrl_stage_end = {
+ .prepare = usbhsg_try_run_ctrl_stage_end,
+ .try_run = usbhsg_try_run_ctrl_stage_end,
+};
+
+/*
+ * 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
+ */
+#define usbhsg_handler_send_ctrl usbhsg_handler_send_by_empty
+#define usbhsg_handler_recv_ctrl usbhsg_handler_recv_by_ready
+
+#define usbhsg_handler_send_packet usbhsg_handler_send_by_ready
+#define usbhsg_handler_recv_packet usbhsg_handler_recv_by_ready
+
+/*
+ * 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_fifo_disable(pipe);
+ usbhs_pipe_clear_sequence(pipe);
+ usbhs_fifo_enable(pipe);
+ }
+
+ usbhsg_recip_handler_std_control_done(priv, uep, ctrl);
+
+ usbhsg_queue_prepare(uep);
+
+ return 0;
+}
+
+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 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;
+ int recip = ctrl->bRequestType & USB_RECIP_MASK;
+ int nth = le16_to_cpu(ctrl->wIndex) & USB_ENDPOINT_NUMBER_MASK;
+ int ret;
+ int (*func)(struct usbhs_priv *priv, struct usbhsg_uep *uep,
+ struct usb_ctrlrequest *ctrl);
+ char *msg;
+
+ uep = usbhsg_gpriv_to_nth_uep(gpriv, nth);
+ if (!usbhsg_uep_to_pipe(uep)) {
+ dev_err(dev, "wrong recip request\n");
+ return -EINVAL;
+ }
+
+ switch (recip) {
+ case USB_RECIP_DEVICE:
+ msg = "DEVICE";
+ func = handler->device;
+ break;
+ case USB_RECIP_INTERFACE:
+ msg = "INTERFACE";
+ func = handler->interface;
+ break;
+ case USB_RECIP_ENDPOINT:
+ msg = "ENDPOINT";
+ func = handler->endpoint;
+ break;
+ default:
+ dev_warn(dev, "unsupported RECIP(%d)\n", recip);
+ func = NULL;
+ ret = -EINVAL;
+ }
+
+ if (func) {
+ dev_dbg(dev, "%s (pipe %d :%s)\n", handler->name, nth, msg);
+ ret = func(priv, uep, ctrl);
+ }
+
+ return ret;
+}
+
+/*
+ * irq functions
+ *
+ * it will be called from usbhs_interrupt
+ */
+static int usbhsg_irq_dev_state(struct usbhs_priv *priv,
+ struct usbhs_irq_state *irq_state)
+{
+ struct usbhsg_gpriv *gpriv = usbhsg_priv_to_gpriv(priv);
+ struct device *dev = usbhsg_gpriv_to_dev(gpriv);
+
+ gpriv->gadget.speed = usbhs_status_get_usb_speed(irq_state);
+
+ dev_dbg(dev, "state = %x : speed : %d\n",
+ usbhs_status_get_device_state(irq_state),
+ gpriv->gadget.speed);
+
+ return 0;
+}
+
+static int usbhsg_irq_ctrl_stage(struct usbhs_priv *priv,
+ struct usbhs_irq_state *irq_state)
+{
+ struct usbhsg_gpriv *gpriv = usbhsg_priv_to_gpriv(priv);
+ struct usbhsg_uep *dcp = usbhsg_gpriv_to_dcp(gpriv);
+ struct usbhs_pipe *pipe = usbhsg_uep_to_pipe(dcp);
+ struct device *dev = usbhsg_gpriv_to_dev(gpriv);
+ struct usb_ctrlrequest ctrl;
+ struct usbhsg_recip_handle *recip_handler = NULL;
+ int stage = usbhs_status_get_ctrl_stage(irq_state);
+ int ret = 0;
+
+ dev_dbg(dev, "stage = %d\n", stage);
+
+ /*
+ * see Manual
+ *
+ * "Operation"
+ * - "Interrupt Function"
+ * - "Control Transfer Stage Transition Interrupt"
+ * - Fig. "Control Transfer Stage Transitions"
+ */
+
+ switch (stage) {
+ case READ_DATA_STAGE:
+ dcp->handler = &usbhsg_handler_send_ctrl;
+ break;
+ case WRITE_DATA_STAGE:
+ dcp->handler = &usbhsg_handler_recv_ctrl;
+ break;
+ case NODATA_STATUS_STAGE:
+ dcp->handler = &usbhsg_handler_ctrl_stage_end;
+ break;
+ 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;
+ }
+ }
+
+ /*
+ * 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_fifo_stall(pipe);
+
+ return ret;
+}
+
+static int usbhsg_irq_empty(struct usbhs_priv *priv,
+ struct usbhs_irq_state *irq_state)
+{
+ struct usbhsg_gpriv *gpriv = usbhsg_priv_to_gpriv(priv);
+ struct usbhsg_uep *uep;
+ struct usbhs_pipe *pipe;
+ struct device *dev = usbhsg_gpriv_to_dev(gpriv);
+ 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;
+
+ uep = usbhsg_pipe_to_uep(pipe);
+ ret = usbhsg_queue_handle(uep);
+ if (ret < 0)
+ dev_err(dev, "send error %d : %d\n", i, ret);
+ }
+
+ return 0;
+}
+
+static int usbhsg_irq_ready(struct usbhs_priv *priv,
+ struct usbhs_irq_state *irq_state)
+{
+ struct usbhsg_gpriv *gpriv = usbhsg_priv_to_gpriv(priv);
+ struct usbhsg_uep *uep;
+ struct usbhs_pipe *pipe;
+ struct device *dev = usbhsg_gpriv_to_dev(gpriv);
+ 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;
+
+ uep = usbhsg_pipe_to_uep(pipe);
+ ret = usbhsg_queue_handle(uep);
+ if (ret < 0)
+ dev_err(dev, "receive error %d : %d\n", i, ret);
+ }
+
+ return 0;
+}
+
+/*
+ *
+ * usb_dcp_ops
+ *
+ */
+static int usbhsg_dcp_enable(struct usbhsg_uep *uep)
+{
+ struct usbhsg_gpriv *gpriv = usbhsg_uep_to_gpriv(uep);
+ struct usbhs_priv *priv = usbhsg_gpriv_to_priv(gpriv);
+ struct usbhs_pipe *pipe;
+
+ /*
+ ********* assume under spin lock *********
+ */
+
+ pipe = usbhs_dcp_malloc(priv);
+ if (!pipe)
+ return -EIO;
+
+ uep->pipe = pipe;
+ uep->pipe->mod_private = uep;
+ INIT_LIST_HEAD(&uep->list);
+
+ return 0;
+}
+
+#define usbhsg_dcp_disable usbhsg_pipe_disable
+static int usbhsg_pipe_disable(struct usbhsg_uep *uep)
+{
+ struct usbhs_pipe *pipe = usbhsg_uep_to_pipe(uep);
+ struct usbhsg_request *ureq;
+ int disable = 0;
+
+ /*
+ ********* assume under spin lock *********
+ */
+
+ usbhs_fifo_disable(pipe);
+
+ /*
+ * disable pipe irq
+ */
+ usbhsg_irq_empty_ctrl(uep, disable);
+ usbhsg_irq_ready_ctrl(uep, disable);
+
+ while (1) {
+ ureq = usbhsg_queue_get(uep);
+ if (!ureq)
+ break;
+
+ usbhsg_queue_pop(uep, ureq, -ECONNRESET);
+ }
+
+ return 0;
+}
+
+static void usbhsg_uep_init(struct usbhsg_gpriv *gpriv)
+{
+ int i;
+ struct usbhsg_uep *uep;
+
+ usbhsg_for_each_uep_with_dcp(uep, gpriv, i)
+ uep->pipe = NULL;
+}
+
+/*
+ *
+ * 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;
+ spinlock_t *lock;
+ unsigned long flags;
+ int ret = -EIO;
+
+ /*
+ * if it already have pipe,
+ * nothing to do
+ */
+ if (uep->pipe)
+ return 0;
+
+ /******************** spin lock ********************/
+ lock = usbhsg_trylock(gpriv, &flags);
+
+ pipe = usbhs_pipe_malloc(priv, desc);
+ if (pipe) {
+ uep->pipe = pipe;
+ pipe->mod_private = uep;
+ INIT_LIST_HEAD(&uep->list);
+
+ if (usb_endpoint_dir_in(desc))
+ uep->handler = &usbhsg_handler_send_packet;
+ else
+ uep->handler = &usbhsg_handler_recv_packet;
+
+ ret = 0;
+ }
+
+ usbhsg_unlock(lock, &flags);
+ /******************** spin unlock ******************/
+
+ return ret;
+}
+
+static int usbhsg_ep_disable(struct usb_ep *ep)
+{
+ struct usbhsg_uep *uep = usbhsg_ep_to_uep(ep);
+ struct usbhsg_gpriv *gpriv = usbhsg_uep_to_gpriv(uep);
+ spinlock_t *lock;
+ unsigned long flags;
+ int ret;
+
+ /******************** spin lock ********************/
+ lock = usbhsg_trylock(gpriv, &flags);
+
+ ret = usbhsg_pipe_disable(uep);
+
+ usbhsg_unlock(lock, &flags);
+ /******************** spin unlock ******************/
+
+ return ret;
+}
+
+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;
+
+ INIT_LIST_HEAD(&ureq->node);
+ 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->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);
+ spinlock_t *lock;
+ unsigned long flags;
+ int ret = 0;
+
+ /*
+ * CAUTION [*endpoint queue*]
+ *
+ * This function will be called from usb_request :: complete
+ * or usb driver timing.
+ * If this function is called from usb_request :: complete,
+ * it is already under spinlock on this driver.
+ * but it is called frm usb driver, this function should call spinlock.
+ *
+ * This function is using usbshg_trylock to solve this issue.
+ * if "is_locked" is 1, this mean this function lock it.
+ * but if it is 0, this mean it is already under spin lock.
+ * see also
+ * CAUTION [*queue handler*]
+ * CAUTION [*request complete*]
+ */
+
+ /******************** spin lock ********************/
+ lock = usbhsg_trylock(gpriv, &flags);
+
+ /* param check */
+ if (usbhsg_is_not_connected(gpriv) ||
+ unlikely(!gpriv->driver) ||
+ unlikely(!pipe))
+ ret = -ESHUTDOWN;
+ else
+ usbhsg_queue_push(uep, ureq);
+
+ usbhsg_unlock(lock, &flags);
+ /******************** spin unlock ******************/
+
+ usbhsg_queue_prepare(uep);
+
+ return ret;
+}
+
+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 usbhsg_gpriv *gpriv = usbhsg_uep_to_gpriv(uep);
+ spinlock_t *lock;
+ unsigned long flags;
+
+ /*
+ * see
+ * CAUTION [*queue handler*]
+ * CAUTION [*endpoint queue*]
+ * CAUTION [*request complete*]
+ */
+
+ /******************** spin lock ********************/
+ lock = usbhsg_trylock(gpriv, &flags);
+
+ usbhsg_queue_pop(uep, ureq, -ECONNRESET);
+
+ usbhsg_unlock(lock, &flags);
+ /******************** spin unlock ******************/
+
+ return 0;
+}
+
+static int __usbhsg_ep_set_halt_wedge(struct usb_ep *ep, int halt, int wedge)
+{
+ struct usbhsg_uep *uep = usbhsg_ep_to_uep(ep);
+ struct usbhs_pipe *pipe = usbhsg_uep_to_pipe(uep);
+ struct usbhsg_gpriv *gpriv = usbhsg_uep_to_gpriv(uep);
+ struct device *dev = usbhsg_gpriv_to_dev(gpriv);
+ spinlock_t *lock;
+ unsigned long flags;
+ int ret = -EAGAIN;
+
+ /*
+ * see
+ * CAUTION [*queue handler*]
+ * CAUTION [*endpoint queue*]
+ * CAUTION [*request complete*]
+ */
+
+ /******************** spin lock ********************/
+ lock = usbhsg_trylock(gpriv, &flags);
+ if (!usbhsg_queue_get(uep)) {
+
+ dev_dbg(dev, "set halt %d (pipe %d)\n",
+ halt, usbhs_pipe_number(pipe));
+
+ if (halt)
+ usbhs_fifo_stall(pipe);
+ else
+ usbhs_fifo_disable(pipe);
+
+ if (halt && wedge)
+ usbhsg_status_set(gpriv, USBHSG_STATUS_WEDGE);
+ else
+ usbhsg_status_clr(gpriv, USBHSG_STATUS_WEDGE);
+
+ ret = 0;
+ }
+
+ usbhsg_unlock(lock, &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 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,
+};
+
+/*
+ * 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);
+ spinlock_t *lock;
+ unsigned long flags;
+
+ /******************** spin lock ********************/
+ lock = usbhsg_trylock(gpriv, &flags);
+
+ /*
+ * enable interrupt and systems if ready
+ */
+ usbhsg_status_set(gpriv, status);
+ if (!(usbhsg_status_has(gpriv, USBHSG_STATUS_STARTED) &&
+ usbhsg_status_has(gpriv, USBHSG_STATUS_REGISTERD)))
+ goto usbhsg_try_start_unlock;
+
+ dev_dbg(dev, "start gadget\n");
+
+ /*
+ * pipe initialize and enable DCP
+ */
+ usbhs_pipe_init(priv);
+ usbhsg_uep_init(gpriv);
+ usbhsg_dcp_enable(dcp);
+
+ /*
+ * system config enble
+ * - HI speed
+ * - function
+ * - usb module
+ */
+ usbhs_sys_hispeed_ctrl(priv, 1);
+ usbhs_sys_function_ctrl(priv, 1);
+ usbhs_sys_usb_ctrl(priv, 1);
+
+ /*
+ * enable irq callback
+ */
+ mod->irq_dev_state = usbhsg_irq_dev_state;
+ mod->irq_ctrl_stage = usbhsg_irq_ctrl_stage;
+ mod->irq_empty = usbhsg_irq_empty;
+ mod->irq_ready = usbhsg_irq_ready;
+ mod->irq_bempsts = 0;
+ mod->irq_brdysts = 0;
+ usbhs_irq_callback_update(priv, mod);
+
+usbhsg_try_start_unlock:
+ usbhsg_unlock(lock, &flags);
+ /******************** spin unlock ********************/
+
+ 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 *dcp = usbhsg_gpriv_to_dcp(gpriv);
+ struct device *dev = usbhs_priv_to_dev(priv);
+ spinlock_t *lock;
+ unsigned long flags;
+
+ /******************** spin lock ********************/
+ lock = usbhsg_trylock(gpriv, &flags);
+
+ /*
+ * disable interrupt and systems if 1st try
+ */
+ usbhsg_status_clr(gpriv, status);
+ if (!usbhsg_status_has(gpriv, USBHSG_STATUS_STARTED) &&
+ !usbhsg_status_has(gpriv, USBHSG_STATUS_REGISTERD))
+ goto usbhsg_try_stop_unlock;
+
+ /* disable all irq */
+ mod->irq_dev_state = NULL;
+ mod->irq_ctrl_stage = NULL;
+ mod->irq_empty = NULL;
+ mod->irq_ready = NULL;
+ mod->irq_bempsts = 0;
+ mod->irq_brdysts = 0;
+ usbhs_irq_callback_update(priv, mod);
+
+ usbhsg_dcp_disable(dcp);
+
+ gpriv->gadget.speed = USB_SPEED_UNKNOWN;
+
+ /* disable sys */
+ usbhs_sys_hispeed_ctrl(priv, 0);
+ usbhs_sys_function_ctrl(priv, 0);
+ usbhs_sys_usb_ctrl(priv, 0);
+
+ usbhsg_unlock(lock, &flags);
+ /******************** spin unlock ********************/
+
+ if (gpriv->driver &&
+ gpriv->driver->disconnect)
+ gpriv->driver->disconnect(&gpriv->gadget);
+
+ dev_dbg(dev, "stop gadget\n");
+
+ return 0;
+
+usbhsg_try_stop_unlock:
+ usbhsg_unlock(lock, &flags);
+
+ return 0;
+}
+
+/*
+ *
+ * linux usb function
+ *
+ */
+struct usbhsg_gpriv *the_controller;
+int usb_gadget_probe_driver(struct usb_gadget_driver *driver,
+ int (*bind)(struct usb_gadget *))
+{
+ struct usbhsg_gpriv *gpriv = the_controller;
+ struct usbhs_priv *priv;
+ struct device *dev;
+ int ret;
+
+ if (!bind ||
+ !driver ||
+ !driver->setup ||
+ driver->speed != USB_SPEED_HIGH)
+ return -EINVAL;
+ if (!gpriv)
+ return -ENODEV;
+ if (gpriv->driver)
+ return -EBUSY;
+
+ dev = usbhsg_gpriv_to_dev(gpriv);
+ priv = usbhsg_gpriv_to_priv(gpriv);
+
+ /* first hook up the driver ... */
+ gpriv->driver = driver;
+ gpriv->gadget.dev.driver = &driver->driver;
+
+ ret = device_add(&gpriv->gadget.dev);
+ if (ret) {
+ dev_err(dev, "device_add error %d\n", ret);
+ goto add_fail;
+ }
+
+ ret = bind(&gpriv->gadget);
+ if (ret) {
+ dev_err(dev, "bind to driver %s error %d\n",
+ driver->driver.name, ret);
+ goto bind_fail;
+ }
+
+ dev_dbg(dev, "bind %s\n", driver->driver.name);
+
+ return usbhsg_try_start(priv, USBHSG_STATUS_REGISTERD);
+
+bind_fail:
+ device_del(&gpriv->gadget.dev);
+add_fail:
+ gpriv->driver = NULL;
+ gpriv->gadget.dev.driver = NULL;
+
+ return ret;
+}
+EXPORT_SYMBOL(usb_gadget_probe_driver);
+
+int usb_gadget_unregister_driver(struct usb_gadget_driver *driver)
+{
+ struct usbhsg_gpriv *gpriv = the_controller;
+ struct usbhs_priv *priv;
+ struct device *dev = usbhsg_gpriv_to_dev(gpriv);
+
+ if (!gpriv)
+ return -ENODEV;
+
+ if (!driver ||
+ !driver->unbind ||
+ driver != gpriv->driver)
+ return -EINVAL;
+
+ dev = usbhsg_gpriv_to_dev(gpriv);
+ priv = usbhsg_gpriv_to_priv(gpriv);
+
+ usbhsg_try_stop(priv, USBHSG_STATUS_REGISTERD);
+ device_del(&gpriv->gadget.dev);
+ gpriv->driver = NULL;
+
+ if (driver->disconnect)
+ driver->disconnect(&gpriv->gadget);
+
+ driver->unbind(&gpriv->gadget);
+ dev_dbg(dev, "unbind %s\n", driver->driver.name);
+
+ return 0;
+}
+EXPORT_SYMBOL(usb_gadget_unregister_driver);
+
+/*
+ * 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 struct usb_gadget_ops usbhsg_gadget_ops = {
+ .get_frame = usbhsg_get_frame,
+};
+
+static int usbhsg_start(struct usbhs_priv *priv)
+{
+ return usbhsg_try_start(priv, USBHSG_STATUS_STARTED);
+}
+
+static int usbhsg_stop(struct usbhs_priv *priv)
+{
+ return usbhsg_try_stop(priv, USBHSG_STATUS_STARTED);
+}
+
+int __devinit usbhs_mod_gadget_probe(struct usbhs_priv *priv)
+{
+ struct usbhsg_gpriv *gpriv;
+ struct usbhsg_uep *uep;
+ struct device *dev = usbhs_priv_to_dev(priv);
+ int pipe_size = usbhs_get_dparam(priv, pipe_size);
+ int i;
+
+ gpriv = kzalloc(sizeof(struct usbhsg_gpriv), GFP_KERNEL);
+ if (!gpriv) {
+ dev_err(dev, "Could not allocate gadget priv\n");
+ return -ENOMEM;
+ }
+
+ uep = kzalloc(sizeof(struct usbhsg_uep) * pipe_size, GFP_KERNEL);
+ if (!uep) {
+ dev_err(dev, "Could not allocate ep\n");
+ 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
+ */
+ device_initialize(&gpriv->gadget.dev);
+ dev_set_name(&gpriv->gadget.dev, "gadget");
+ gpriv->gadget.dev.parent = dev;
+ gpriv->gadget.name = "renesas_usbhs_udc";
+ gpriv->gadget.ops = &usbhsg_gadget_ops;
+ gpriv->gadget.is_dualspeed = 1;
+
+ INIT_LIST_HEAD(&gpriv->gadget.ep_list);
+
+ /*
+ * init usb_ep
+ */
+ usbhsg_for_each_uep_with_dcp(uep, gpriv, i) {
+ uep->gpriv = gpriv;
+ 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);
+ INIT_LIST_HEAD(&uep->list);
+
+ /* init DCP */
+ if (usbhsg_is_dcp(uep)) {
+ gpriv->gadget.ep0 = &uep->ep;
+ uep->ep.maxpacket = 64;
+ }
+ /* init normal pipe */
+ else {
+ uep->ep.maxpacket = 512;
+ list_add_tail(&uep->ep.ep_list, &gpriv->gadget.ep_list);
+ }
+ }
+
+ the_controller = gpriv;
+
+ dev_info(dev, "gadget probed\n");
+
+ return 0;
+
+usbhs_mod_gadget_probe_err_gpriv:
+ kfree(gpriv);
+
+ return -ENOMEM;
+}
+
+void __devexit usbhs_mod_gadget_remove(struct usbhs_priv *priv)
+{
+ struct usbhsg_gpriv *gpriv = usbhsg_priv_to_gpriv(priv);
+
+ kfree(gpriv);
+}
diff --git a/drivers/usb/renesas_usbhs/pipe.c b/drivers/usb/renesas_usbhs/pipe.c
new file mode 100644
index 00000000000..bc4521c5426
--- /dev/null
+++ b/drivers/usb/renesas_usbhs/pipe.c
@@ -0,0 +1,874 @@
+/*
+ * Renesas USB driver
+ *
+ * Copyright (C) 2011 Renesas Solutions Corp.
+ * Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+#include <linux/delay.h>
+#include <linux/io.h>
+#include <linux/slab.h>
+#include "./common.h"
+#include "./pipe.h"
+
+/*
+ * macros
+ */
+#define usbhsp_priv_to_pipeinfo(pr) (&(pr)->pipe_info)
+#define usbhsp_pipe_to_priv(p) ((p)->priv)
+
+#define usbhsp_addr_offset(p) ((usbhs_pipe_number(p) - 1) * 2)
+
+#define usbhsp_is_dcp(p) ((p)->priv->pipe_info.pipe == (p))
+
+#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)
+
+#define usbhsp_type(p) ((p)->pipe_type)
+#define usbhsp_type_is(p, t) ((p)->pipe_type == t)
+
+/*
+ * 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",
+};
+
+/*
+ * usb request functions
+ */
+void usbhs_usbreq_get_val(struct usbhs_priv *priv, struct usb_ctrlrequest *req)
+{
+ u16 val;
+
+ val = usbhs_read(priv, USBREQ);
+ req->bRequest = (val >> 8) & 0xFF;
+ req->bRequestType = (val >> 0) & 0xFF;
+
+ req->wValue = usbhs_read(priv, USBVAL);
+ req->wIndex = usbhs_read(priv, USBINDX);
+ req->wLength = usbhs_read(priv, USBLENG);
+}
+
+void usbhs_usbreq_set_val(struct usbhs_priv *priv, struct usb_ctrlrequest *req)
+{
+ usbhs_write(priv, USBREQ, (req->bRequest << 8) | req->bRequestType);
+ usbhs_write(priv, USBVAL, req->wValue);
+ usbhs_write(priv, USBINDX, req->wIndex);
+ usbhs_write(priv, USBLENG, req->wLength);
+}
+
+/*
+ * DCPCTR/PIPEnCTR functions
+ */
+static void usbhsp_pipectrl_set(struct usbhs_pipe *pipe, u16 mask, u16 val)
+{
+ struct usbhs_priv *priv = usbhsp_pipe_to_priv(pipe);
+ int offset = usbhsp_addr_offset(pipe);
+
+ if (usbhsp_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 = usbhsp_pipe_to_priv(pipe);
+ int offset = usbhsp_addr_offset(pipe);
+
+ if (usbhsp_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 = usbhsp_pipe_to_priv(pipe);
+
+ if (usbhsp_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 = usbhsp_pipe_to_priv(pipe);
+
+ if (usbhsp_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);
+}
+
+/*
+ * PIPEBUF
+ */
+static void usbhsp_pipe_buf_set(struct usbhs_pipe *pipe, u16 mask, u16 val)
+{
+ if (usbhsp_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);
+}
+
+static u16 usbhsp_pipe_maxp_get(struct usbhs_pipe *pipe)
+{
+ return __usbhsp_pipe_xxx_get(pipe, DCPMAXP, PIPEMAXP);
+}
+
+/*
+ * pipe control functions
+ */
+static void usbhsp_pipe_select(struct usbhs_pipe *pipe)
+{
+ struct usbhs_priv *priv = usbhsp_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 = usbhsp_pipe_to_priv(pipe);
+ int timeout = 1024;
+ u16 val;
+
+ /*
+ * 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_fifo_disable(pipe);
+
+ do {
+ val = usbhsp_pipectrl_get(pipe);
+ val &= CSSTS | PID_MASK;
+ if (!val)
+ return 0;
+
+ udelay(10);
+
+ } while (timeout--);
+
+ return -EBUSY;
+}
+
+static int usbhsp_pipe_is_accessible(struct usbhs_pipe *pipe)
+{
+ u16 val;
+
+ val = usbhsp_pipectrl_get(pipe);
+ if (val & BSTS)
+ return 0;
+
+ return -EBUSY;
+}
+
+/*
+ * PID ctrl
+ */
+static void __usbhsp_pid_try_nak_if_stall(struct usbhs_pipe *pipe)
+{
+ u16 pid = usbhsp_pipectrl_get(pipe);
+
+ pid &= PID_MASK;
+
+ /*
+ * see
+ * "Pipe n Control Register" - "PID"
+ */
+ switch (pid) {
+ case PID_STALL11:
+ usbhsp_pipectrl_set(pipe, PID_MASK, PID_STALL10);
+ /* fall-through */
+ case PID_STALL10:
+ usbhsp_pipectrl_set(pipe, PID_MASK, PID_NAK);
+ }
+}
+
+void usbhs_fifo_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_fifo_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_fifo_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;
+ }
+}
+
+/*
+ * CFIFO ctrl
+ */
+void usbhs_fifo_send_terminator(struct usbhs_pipe *pipe)
+{
+ struct usbhs_priv *priv = usbhsp_pipe_to_priv(pipe);
+
+ usbhs_bset(priv, CFIFOCTR, BVAL, BVAL);
+}
+
+static void usbhsp_fifo_clear(struct usbhs_pipe *pipe)
+{
+ struct usbhs_priv *priv = usbhsp_pipe_to_priv(pipe);
+
+ usbhs_write(priv, CFIFOCTR, BCLR);
+}
+
+static int usbhsp_fifo_barrier(struct usbhs_priv *priv)
+{
+ int timeout = 1024;
+
+ do {
+ /* The FIFO port is accessible */
+ if (usbhs_read(priv, CFIFOCTR) & FRDY)
+ return 0;
+
+ udelay(10);
+ } while (timeout--);
+
+ return -EBUSY;
+}
+
+static int usbhsp_fifo_rcv_len(struct usbhs_priv *priv)
+{
+ return usbhs_read(priv, CFIFOCTR) & DTLN_MASK;
+}
+
+static int usbhsp_fifo_select(struct usbhs_pipe *pipe, int write)
+{
+ struct usbhs_priv *priv = usbhsp_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 (usbhsp_is_dcp(pipe))
+ base |= (1 == write) << 5; /* ISEL */
+
+ /* "base" will be used below */
+ usbhs_write(priv, CFIFOSEL, base | MBW_32);
+
+ /* check ISEL and CURPIPE value */
+ while (timeout--) {
+ if (base == (mask & usbhs_read(priv, CFIFOSEL)))
+ return 0;
+ udelay(10);
+ }
+
+ dev_err(dev, "fifo select error\n");
+
+ return -EIO;
+}
+
+int usbhs_fifo_prepare_write(struct usbhs_pipe *pipe)
+{
+ return usbhsp_fifo_select(pipe, 1);
+}
+
+int usbhs_fifo_write(struct usbhs_pipe *pipe, u8 *buf, int len)
+{
+ struct usbhs_priv *priv = usbhsp_pipe_to_priv(pipe);
+ void __iomem *addr = priv->base + CFIFO;
+ int maxp = usbhs_pipe_get_maxpacket(pipe);
+ int total_len;
+ int i, ret;
+
+ ret = usbhsp_pipe_is_accessible(pipe);
+ if (ret < 0)
+ return ret;
+
+ ret = usbhsp_fifo_select(pipe, 1);
+ if (ret < 0)
+ return ret;
+
+ ret = usbhsp_fifo_barrier(priv);
+ if (ret < 0)
+ return ret;
+
+ len = min(len, maxp);
+ total_len = len;
+
+ /*
+ * FIXME
+ *
+ * 32-bit access only
+ */
+ if (len >= 4 &&
+ !((unsigned long)buf & 0x03)) {
+ iowrite32_rep(addr, buf, len / 4);
+ len %= 4;
+ buf += total_len - len;
+ }
+
+ /* the rest operation */
+ for (i = 0; i < len; i++)
+ iowrite8(buf[i], addr + (0x03 - (i & 0x03)));
+
+ if (total_len < maxp)
+ usbhs_fifo_send_terminator(pipe);
+
+ return total_len;
+}
+
+int usbhs_fifo_prepare_read(struct usbhs_pipe *pipe)
+{
+ int ret;
+
+ /*
+ * select pipe and enable it to prepare packet receive
+ */
+ ret = usbhsp_fifo_select(pipe, 0);
+ if (ret < 0)
+ return ret;
+
+ usbhs_fifo_enable(pipe);
+
+ return ret;
+}
+
+int usbhs_fifo_read(struct usbhs_pipe *pipe, u8 *buf, int len)
+{
+ struct usbhs_priv *priv = usbhsp_pipe_to_priv(pipe);
+ void __iomem *addr = priv->base + CFIFO;
+ int rcv_len;
+ int i, ret;
+ int total_len;
+ u32 data = 0;
+
+ ret = usbhsp_fifo_select(pipe, 0);
+ if (ret < 0)
+ return ret;
+
+ ret = usbhsp_fifo_barrier(priv);
+ if (ret < 0)
+ return ret;
+
+ rcv_len = usbhsp_fifo_rcv_len(priv);
+
+ /*
+ * Buffer clear if Zero-Length packet
+ *
+ * see
+ * "Operation" - "FIFO Buffer Memory" - "FIFO Port Function"
+ */
+ if (0 == rcv_len) {
+ usbhsp_fifo_clear(pipe);
+ return 0;
+ }
+
+ len = min(rcv_len, len);
+ total_len = len;
+
+ /*
+ * FIXME
+ *
+ * 32-bit access only
+ */
+ if (len >= 4 &&
+ !((unsigned long)buf & 0x03)) {
+ ioread32_rep(addr, buf, len / 4);
+ len %= 4;
+ buf += rcv_len - len;
+ }
+
+ /* the rest operation */
+ for (i = 0; i < len; i++) {
+ if (!(i & 0x03))
+ data = ioread32(addr);
+
+ buf[i] = (data >> ((i & 0x03) * 8)) & 0xff;
+ }
+
+ return total_len;
+}
+
+/*
+ * pipe setup
+ */
+static int usbhsp_possible_double_buffer(struct usbhs_pipe *pipe)
+{
+ /*
+ * only ISO / BULK pipe can use double buffer
+ */
+ if (usbhsp_type_is(pipe, USB_ENDPOINT_XFER_BULK) ||
+ usbhsp_type_is(pipe, USB_ENDPOINT_XFER_ISOC))
+ return 1;
+
+ return 0;
+}
+
+static u16 usbhsp_setup_pipecfg(struct usbhs_pipe *pipe,
+ const struct usb_endpoint_descriptor *desc,
+ int is_host)
+{
+ u16 type = 0;
+ u16 bfre = 0;
+ u16 dblb = 0;
+ u16 cntmd = 0;
+ u16 dir = 0;
+ u16 epnum = 0;
+ u16 shtnak = 0;
+ u16 type_array[] = {
+ [USB_ENDPOINT_XFER_BULK] = TYPE_BULK,
+ [USB_ENDPOINT_XFER_INT] = TYPE_INT,
+ [USB_ENDPOINT_XFER_ISOC] = TYPE_ISO,
+ };
+ int is_double = usbhsp_possible_double_buffer(pipe);
+
+ if (usbhsp_is_dcp(pipe))
+ return -EINVAL;
+
+ /*
+ * PIPECFG
+ *
+ * see
+ * - "Register Descriptions" - "PIPECFG" register
+ * - "Features" - "Pipe configuration"
+ * - "Operation" - "Pipe Control"
+ */
+
+ /* TYPE */
+ type = type_array[usbhsp_type(pipe)];
+
+ /* BFRE */
+ if (usbhsp_type_is(pipe, USB_ENDPOINT_XFER_ISOC) ||
+ usbhsp_type_is(pipe, USB_ENDPOINT_XFER_BULK))
+ bfre = 0; /* FIXME */
+
+ /* DBLB */
+ if (usbhsp_type_is(pipe, USB_ENDPOINT_XFER_ISOC) ||
+ usbhsp_type_is(pipe, USB_ENDPOINT_XFER_BULK))
+ dblb = (is_double) ? DBLB : 0;
+
+ /* CNTMD */
+ if (usbhsp_type_is(pipe, USB_ENDPOINT_XFER_BULK))
+ cntmd = 0; /* FIXME */
+
+ /* DIR */
+ if (usb_endpoint_dir_in(desc))
+ usbhsp_flags_set(pipe, IS_DIR_IN);
+
+ if ((is_host && usb_endpoint_dir_out(desc)) ||
+ (!is_host && usb_endpoint_dir_in(desc)))
+ dir |= DIR_OUT;
+
+ /* SHTNAK */
+ if (usbhsp_type_is(pipe, USB_ENDPOINT_XFER_BULK) &&
+ !dir)
+ shtnak = SHTNAK;
+
+ /* EPNUM */
+ epnum = 0xF & usb_endpoint_num(desc);
+
+ return type |
+ bfre |
+ dblb |
+ cntmd |
+ dir |
+ shtnak |
+ epnum;
+}
+
+static u16 usbhsp_setup_pipemaxp(struct usbhs_pipe *pipe,
+ const struct usb_endpoint_descriptor *desc,
+ int is_host)
+{
+ /* host should set DEVSEL */
+
+ /* reutn MXPS */
+ return PIPE_MAXP_MASK & le16_to_cpu(desc->wMaxPacketSize);
+}
+
+static u16 usbhsp_setup_pipebuff(struct usbhs_pipe *pipe,
+ const struct usb_endpoint_descriptor *desc,
+ int is_host)
+{
+ struct usbhs_priv *priv = usbhsp_pipe_to_priv(pipe);
+ struct usbhs_pipe_info *info = usbhsp_priv_to_pipeinfo(priv);
+ struct device *dev = usbhs_priv_to_dev(priv);
+ int pipe_num = usbhs_pipe_number(pipe);
+ int is_double = usbhsp_possible_double_buffer(pipe);
+ u16 buff_size;
+ u16 bufnmb;
+ u16 bufnmb_cnt;
+
+ /*
+ * PIPEBUF
+ *
+ * see
+ * - "Register Descriptions" - "PIPEBUF" register
+ * - "Features" - "Pipe configuration"
+ * - "Operation" - "FIFO Buffer Memory"
+ * - "Operation" - "Pipe Control"
+ *
+ * ex) if pipe6 - pipe9 are USB_ENDPOINT_XFER_INT (SH7724)
+ *
+ * BUFNMB: PIPE
+ * 0: pipe0 (DCP 256byte)
+ * 1: -
+ * 2: -
+ * 3: -
+ * 4: pipe6 (INT 64byte)
+ * 5: pipe7 (INT 64byte)
+ * 6: pipe8 (INT 64byte)
+ * 7: pipe9 (INT 64byte)
+ * 8 - xx: free (for BULK, ISOC)
+ */
+
+ /*
+ * FIXME
+ *
+ * it doesn't have good buffer allocator
+ *
+ * DCP : 256 byte
+ * BULK: 512 byte
+ * INT : 64 byte
+ * ISOC: 512 byte
+ */
+ if (usbhsp_type_is(pipe, USB_ENDPOINT_XFER_CONTROL))
+ buff_size = 256;
+ else if (usbhsp_type_is(pipe, USB_ENDPOINT_XFER_INT))
+ buff_size = 64;
+ else
+ buff_size = 512;
+
+ /* change buff_size to register value */
+ bufnmb_cnt = (buff_size / 64) - 1;
+
+ /* BUFNMB has been reserved for INT pipe
+ * see above */
+ if (usbhsp_type_is(pipe, USB_ENDPOINT_XFER_INT)) {
+ bufnmb = pipe_num - 2;
+ } else {
+ bufnmb = info->bufnmb_last;
+ info->bufnmb_last += bufnmb_cnt + 1;
+
+ /*
+ * double buffer
+ */
+ if (is_double)
+ info->bufnmb_last += bufnmb_cnt + 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;
+}
+
+/*
+ * pipe control
+ */
+int usbhs_pipe_get_maxpacket(struct usbhs_pipe *pipe)
+{
+ u16 mask = usbhsp_is_dcp(pipe) ? DCP_MAXP_MASK : PIPE_MAXP_MASK;
+
+ usbhsp_pipe_select(pipe);
+
+ return (int)(usbhsp_pipe_maxp_get(pipe) & mask);
+}
+
+int usbhs_pipe_is_dir_in(struct usbhs_pipe *pipe)
+{
+ return usbhsp_flags_has(pipe, IS_DIR_IN);
+}
+
+void usbhs_pipe_clear_sequence(struct usbhs_pipe *pipe)
+{
+ usbhsp_pipectrl_set(pipe, SQCLR, SQCLR);
+}
+
+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 (!usbhsp_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;
+}
+
+void usbhs_pipe_init(struct usbhs_priv *priv)
+{
+ struct usbhs_pipe_info *info = usbhsp_priv_to_pipeinfo(priv);
+ struct usbhs_pipe *pipe;
+ int i;
+
+ /*
+ * FIXME
+ *
+ * driver needs good allocator.
+ *
+ * find first free buffer area (BULK, ISOC)
+ * (DCP, INT area is fixed)
+ *
+ * buffer number 0 - 3 have been reserved for DCP
+ * see
+ * usbhsp_to_bufnmb
+ */
+ info->bufnmb_last = 4;
+ usbhs_for_each_pipe_with_dcp(pipe, priv, i) {
+ if (usbhsp_type_is(pipe, USB_ENDPOINT_XFER_INT))
+ info->bufnmb_last++;
+
+ usbhsp_flags_init(pipe);
+ pipe->mod_private = NULL;
+
+ usbhsp_fifo_clear(pipe);
+ }
+}
+
+struct usbhs_pipe *usbhs_pipe_malloc(struct usbhs_priv *priv,
+ const struct usb_endpoint_descriptor *desc)
+{
+ struct device *dev = usbhs_priv_to_dev(priv);
+ struct usbhs_mod *mod = usbhs_mod_get_current(priv);
+ struct usbhs_pipe *pipe;
+ int is_host = usbhs_mod_is_host(priv, mod);
+ int ret;
+ u16 pipecfg, pipebuf, pipemaxp;
+
+ pipe = usbhsp_get_pipe(priv, usb_endpoint_type(desc));
+ if (!pipe) {
+ dev_err(dev, "can't get pipe (%s)\n",
+ usbhsp_pipe_name[usb_endpoint_type(desc)]);
+ return NULL;
+ }
+
+ usbhs_fifo_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;
+ }
+
+ pipecfg = usbhsp_setup_pipecfg(pipe, desc, is_host);
+ pipebuf = usbhsp_setup_pipebuff(pipe, desc, is_host);
+ pipemaxp = usbhsp_setup_pipemaxp(pipe, desc, is_host);
+
+ /* buffer clear
+ * see PIPECFG :: BFRE */
+ usbhsp_pipectrl_set(pipe, ACLRM, ACLRM);
+ usbhsp_pipectrl_set(pipe, ACLRM, 0);
+
+ usbhsp_pipe_select(pipe);
+ usbhsp_pipe_cfg_set(pipe, 0xFFFF, pipecfg);
+ usbhsp_pipe_buf_set(pipe, 0xFFFF, pipebuf);
+ usbhsp_pipe_maxp_set(pipe, 0xFFFF, pipemaxp);
+
+ usbhs_pipe_clear_sequence(pipe);
+
+ dev_dbg(dev, "enable pipe %d : %s (%s)\n",
+ usbhs_pipe_number(pipe),
+ usbhsp_pipe_name[usb_endpoint_type(desc)],
+ usbhs_pipe_is_dir_in(pipe) ? "in" : "out");
+
+ return 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;
+
+ /*
+ * dcpcfg : default
+ * dcpmaxp : default
+ * pipebuf : nothing to do
+ */
+
+ usbhsp_pipe_select(pipe);
+ usbhs_pipe_clear_sequence(pipe);
+
+ return pipe;
+}
+
+void usbhs_dcp_control_transfer_done(struct usbhs_pipe *pipe)
+{
+ WARN_ON(!usbhsp_is_dcp(pipe));
+
+ usbhs_fifo_enable(pipe);
+ usbhsp_pipectrl_set(pipe, CCPL, CCPL);
+}
+
+
+/*
+ * pipe module function
+ */
+int usbhs_pipe_probe(struct usbhs_priv *priv)
+{
+ struct usbhs_pipe_info *info = usbhsp_priv_to_pipeinfo(priv);
+ struct usbhs_pipe *pipe;
+ struct device *dev = usbhs_priv_to_dev(priv);
+ u32 *pipe_type = usbhs_get_dparam(priv, pipe_type);
+ int pipe_size = usbhs_get_dparam(priv, pipe_size);
+ int i;
+
+ /* This driver expects 1st pipe is DCP */
+ if (pipe_type[0] != USB_ENDPOINT_XFER_CONTROL) {
+ dev_err(dev, "1st PIPE is not DCP\n");
+ return -EINVAL;
+ }
+
+ info->pipe = kzalloc(sizeof(struct usbhs_pipe) * pipe_size, GFP_KERNEL);
+ if (!info->pipe) {
+ dev_err(dev, "Could not allocate pipe\n");
+ return -ENOMEM;
+ }
+
+ info->size = pipe_size;
+
+ /*
+ * init pipe
+ */
+ usbhs_for_each_pipe_with_dcp(pipe, priv, i) {
+ pipe->priv = priv;
+ usbhsp_type(pipe) = pipe_type[i] & USB_ENDPOINT_XFERTYPE_MASK;
+
+ dev_dbg(dev, "pipe %x\t: %s\n",
+ i, usbhsp_pipe_name[pipe_type[i]]);
+ }
+
+ return 0;
+}
+
+void usbhs_pipe_remove(struct usbhs_priv *priv)
+{
+ struct usbhs_pipe_info *info = usbhsp_priv_to_pipeinfo(priv);
+
+ kfree(info->pipe);
+}
diff --git a/drivers/usb/renesas_usbhs/pipe.h b/drivers/usb/renesas_usbhs/pipe.h
new file mode 100644
index 00000000000..1cca9b7fb26
--- /dev/null
+++ b/drivers/usb/renesas_usbhs/pipe.h
@@ -0,0 +1,104 @@
+/*
+ * Renesas USB driver
+ *
+ * Copyright (C) 2011 Renesas Solutions Corp.
+ * Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+#ifndef RENESAS_USB_PIPE_H
+#define RENESAS_USB_PIPE_H
+
+#include "./common.h"
+
+/*
+ * struct
+ */
+struct usbhs_pipe {
+ u32 pipe_type; /* USB_ENDPOINT_XFER_xxx */
+
+ struct usbhs_priv *priv;
+
+ u32 flags;
+#define USBHS_PIPE_FLAGS_IS_USED (1 << 0)
+#define USBHS_PIPE_FLAGS_IS_DIR_IN (1 << 1)
+
+ void *mod_private;
+};
+
+struct usbhs_pipe_info {
+ struct usbhs_pipe *pipe;
+ int size; /* array size of "pipe" */
+ int bufnmb_last; /* FIXME : driver needs good allocator */
+};
+
+/*
+ * pipe list
+ */
+#define __usbhs_for_each_pipe(start, pos, info, i) \
+ for (i = start, pos = (info)->pipe; \
+ i < (info)->size; \
+ i++, pos = (info)->pipe + 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)
+
+/*
+ * pipe module probe / remove
+ */
+int usbhs_pipe_probe(struct usbhs_priv *priv);
+void usbhs_pipe_remove(struct usbhs_priv *priv);
+
+/*
+ * cfifo
+ */
+int usbhs_fifo_write(struct usbhs_pipe *pipe, u8 *buf, int len);
+int usbhs_fifo_read(struct usbhs_pipe *pipe, u8 *buf, int len);
+int usbhs_fifo_prepare_write(struct usbhs_pipe *pipe);
+int usbhs_fifo_prepare_read(struct usbhs_pipe *pipe);
+
+void usbhs_fifo_enable(struct usbhs_pipe *pipe);
+void usbhs_fifo_disable(struct usbhs_pipe *pipe);
+void usbhs_fifo_stall(struct usbhs_pipe *pipe);
+
+void usbhs_fifo_send_terminator(struct usbhs_pipe *pipe);
+
+
+/*
+ * 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);
+
+/*
+ * pipe control
+ */
+struct usbhs_pipe
+*usbhs_pipe_malloc(struct usbhs_priv *priv,
+ const struct usb_endpoint_descriptor *desc);
+
+int usbhs_pipe_is_dir_in(struct usbhs_pipe *pipe);
+void usbhs_pipe_init(struct usbhs_priv *priv);
+int usbhs_pipe_get_maxpacket(struct usbhs_pipe *pipe);
+void usbhs_pipe_clear_sequence(struct usbhs_pipe *pipe);
+
+#define usbhs_pipe_number(p) (int)((p) - (p)->priv->pipe_info.pipe)
+
+/*
+ * dcp control
+ */
+struct usbhs_pipe *usbhs_dcp_malloc(struct usbhs_priv *priv);
+void usbhs_dcp_control_transfer_done(struct usbhs_pipe *pipe);
+
+#endif /* RENESAS_USB_PIPE_H */