summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorKuninori Morimoto <kuninori.morimoto.gx@renesas.com>2011-04-04 13:44:59 +0900
committerGreg Kroah-Hartman <gregkh@suse.de>2011-04-13 16:07:07 -0700
commitf1407d5c66240b33d11a7f1a41d55ccf6a9d7647 (patch)
tree90d08090ac44e19e1e3fc0fe0073a34884b5f4c5
parenta6360dd37e1a144ed11e6548371bade559a1e4df (diff)
usb: renesas_usbhs: Add Renesas USBHS common code
Renesas SuperH has USBHS IP which can switch Host / Function. This driver is designed so that Host / Function may dynamically change. This patch add usb/renesas_usbhs and common code for SuperH USBHS. Signed-off-by: Kuninori Morimoto <kuninori.morimoto.gx@renesas.com> Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
-rw-r--r--drivers/Makefile1
-rw-r--r--drivers/usb/Kconfig2
-rw-r--r--drivers/usb/renesas_usbhs/Kconfig15
-rw-r--r--drivers/usb/renesas_usbhs/Makefile7
-rw-r--r--drivers/usb/renesas_usbhs/common.c394
-rw-r--r--drivers/usb/renesas_usbhs/common.h225
-rw-r--r--drivers/usb/renesas_usbhs/mod.c261
-rw-r--r--drivers/usb/renesas_usbhs/mod.h106
-rw-r--r--drivers/usb/renesas_usbhs/pipe.c880
-rw-r--r--drivers/usb/renesas_usbhs/pipe.h105
-rw-r--r--include/linux/usb/renesas_usbhs.h149
11 files changed, 2145 insertions, 0 deletions
diff --git a/drivers/Makefile b/drivers/Makefile
index 3f135b6fb014..ad67b7d4c271 100644
--- a/drivers/Makefile
+++ b/drivers/Makefile
@@ -67,6 +67,7 @@ obj-$(CONFIG_UWB) += uwb/
obj-$(CONFIG_USB_OTG_UTILS) += usb/otg/
obj-$(CONFIG_USB) += usb/
obj-$(CONFIG_USB_MUSB_HDRC) += usb/musb/
+obj-$(CONFIG_USB_RENESAS_USBHS) += usb/renesas_usbhs/
obj-$(CONFIG_PCI) += usb/
obj-$(CONFIG_USB_GADGET) += usb/gadget/
obj-$(CONFIG_SERIO) += input/serio/
diff --git a/drivers/usb/Kconfig b/drivers/usb/Kconfig
index 41b6e51188e4..d299906e4f00 100644
--- a/drivers/usb/Kconfig
+++ b/drivers/usb/Kconfig
@@ -115,6 +115,8 @@ source "drivers/usb/host/Kconfig"
source "drivers/usb/musb/Kconfig"
+source "drivers/usb/renesas_usbhs/Kconfig"
+
source "drivers/usb/class/Kconfig"
source "drivers/usb/storage/Kconfig"
diff --git a/drivers/usb/renesas_usbhs/Kconfig b/drivers/usb/renesas_usbhs/Kconfig
new file mode 100644
index 000000000000..481490e5500a
--- /dev/null
+++ b/drivers/usb/renesas_usbhs/Kconfig
@@ -0,0 +1,15 @@
+#
+# Renesas USB Controller Drivers
+#
+
+config USB_RENESAS_USBHS
+ tristate 'Renesas USBHS controller'
+ 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 000000000000..d76f3dd3b9d1
--- /dev/null
+++ b/drivers/usb/renesas_usbhs/Makefile
@@ -0,0 +1,7 @@
+#
+# for Renesas USB
+#
+
+obj-$(CONFIG_USB_RENESAS_USBHS) += renesas_usbhs.o
+
+renesas_usbhs-y := common.o mod.o pipe.o
diff --git a/drivers/usb/renesas_usbhs/common.c b/drivers/usb/renesas_usbhs/common.c
new file mode 100644
index 000000000000..d9ad60d1c156
--- /dev/null
+++ b/drivers/usb/renesas_usbhs/common.c
@@ -0,0 +1,394 @@
+/*
+ * 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"
+
+/*
+ * 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);
+}
+
+/*
+ * 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 struct usbhs_priv *usbhsc_pdev_to_priv(struct platform_device *pdev)
+{
+ return dev_get_drvdata(&pdev->dev);
+}
+
+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,
+};
+
+/*
+ * driver callback functions
+ */
+static void usbhsc_notify_hotplug(struct work_struct *work)
+{
+ struct usbhs_priv *priv = container_of(work,
+ struct usbhs_priv,
+ notify_hotplug_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__);
+
+ /* enable PM */
+ pm_runtime_get_sync(&pdev->dev);
+
+ /* USB on */
+ usbhs_sys_clock_ctrl(priv, enable);
+ usbhsc_bus_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);
+
+ /* USB off */
+ usbhsc_bus_ctrl(priv, enable);
+ usbhs_sys_clock_ctrl(priv, enable);
+
+ /* disable PM */
+ pm_runtime_put_sync(&pdev->dev);
+
+ usbhs_mod_change(priv, -1);
+
+ /* reset phy for next connection */
+ usbhs_platform_call(priv, phy_reset, pdev);
+ }
+}
+
+static int usbhsc_drvcllbck_notify_hotplug(struct platform_device *pdev)
+{
+ struct usbhs_priv *priv = usbhsc_pdev_to_priv(pdev);
+
+ /*
+ * This functions will be called in interrupt.
+ * To make sure safety context,
+ * use workqueue for usbhs_notify_hotplug
+ */
+ schedule_work(&priv->notify_hotplug_work);
+ 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 ||
+ !info->platform_callback.get_vbus) {
+ 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);
+ }
+
+ /*
+ * priv settings
+ */
+ priv->irq = irq;
+ priv->pdev = pdev;
+ INIT_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_mod_exit;
+
+ ret = usbhs_mod_probe(priv);
+ if (ret < 0)
+ goto probe_end_iounmap;
+
+ /* 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_pipe_exit;
+ }
+
+ /* reset phy for connection */
+ usbhs_platform_call(priv, phy_reset, pdev);
+
+ /*
+ * manual call notify_hotplug for cold plug
+ */
+ pm_runtime_enable(&pdev->dev);
+ 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_pipe_exit:
+ usbhs_pipe_remove(priv);
+probe_end_mod_exit:
+ usbhs_mod_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 = usbhsc_pdev_to_priv(pdev);
+
+ dev_dbg(&pdev->dev, "usb remove\n");
+
+ pm_runtime_disable(&pdev->dev);
+
+ usbhsc_bus_ctrl(priv, 0);
+
+ usbhs_platform_call(priv, hardware_exit, pdev);
+ usbhs_pipe_remove(priv);
+ usbhs_mod_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 000000000000..f1a2b62f93f9
--- /dev/null
+++ b/drivers/usb/renesas_usbhs/common.h
@@ -0,0 +1,225 @@
+/*
+ * 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 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 work_struct notify_hotplug_work;
+ struct platform_device *pdev;
+
+ spinlock_t lock;
+
+ /*
+ * 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);
+
+/*
+ * 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
+ */
+#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 000000000000..4a3398484cd7
--- /dev/null
+++ b/drivers/usb/renesas_usbhs/mod.c
@@ -0,0 +1,261 @@
+/*
+ * 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)
+
+/*
+ * 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;
+
+ /* irq settings */
+ ret = request_irq(priv->irq, usbhs_interrupt,
+ IRQF_DISABLED, dev_name(dev), priv);
+ if (ret)
+ dev_err(dev, "irq request err\n");
+
+ return ret;
+}
+
+void usbhs_mod_remove(struct usbhs_priv *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->brdysts = usbhs_read(priv, BRDYSTS);
+ state->nrdysts = usbhs_read(priv, NRDYSTS);
+ state->bempsts = usbhs_read(priv, BEMPSTS);
+
+ state->dvstctr = usbhs_read(priv, DVSTCTR);
+
+ /* mask */
+ 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 & 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;
+
+ 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 (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 000000000000..bd873d5b432a
--- /dev/null
+++ b/drivers/usb/renesas_usbhs/mod.h
@@ -0,0 +1,106 @@
+/*
+ * 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 */
+};
+
+/*
+ * 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);
+
+/*
+ * 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); \
+ })
+
+#endif /* RENESAS_USB_MOD_H */
diff --git a/drivers/usb/renesas_usbhs/pipe.c b/drivers/usb/renesas_usbhs/pipe.c
new file mode 100644
index 000000000000..b7a9137f599b
--- /dev/null
+++ b/drivers/usb/renesas_usbhs/pipe.c
@@ -0,0 +1,880 @@
+/*
+ * 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)