[PATCH 006/149] USB: m66592-udc: peripheral controller driver for M66592

Greg Kroah-Hartman <[email protected]>
Newsgroups gmane.linux.usb.devel
Message-ID <[email protected]>
From: Yoshihiro Shimoda <[email protected]>

I would like to submit Renesas M66592 udc driver.

The M66592 is Renesas USB 2.0 peripheral controller.
This controller supports USB high-speed.

The driver has been tested Gadget Zero, Ethernet Gadget,
File-backed Storage Gadget, and passed usbtest script.

Signed-off-by : Yoshihiro Shimoda <[email protected]>
Signed-off-by: Greg Kroah-Hartman <[email protected]>
---
 drivers/usb/gadget/Kconfig        |   18 +
 drivers/usb/gadget/Makefile       |    1 +
 drivers/usb/gadget/ether.c        |    4 +
 drivers/usb/gadget/gadget_chips.h |    9 +
 drivers/usb/gadget/m66592-udc.c   | 1653 +++++++++++++++++++++++++++++++++++++
 drivers/usb/gadget/m66592-udc.h   |  577 +++++++++++++
 6 files changed, 2262 insertions(+), 0 deletions(-)
 create mode 100644 drivers/usb/gadget/m66592-udc.c
 create mode 100644 drivers/usb/gadget/m66592-udc.h

diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig
index f771a7c..0576888 100644
--- a/drivers/usb/gadget/Kconfig
+++ b/drivers/usb/gadget/Kconfig
@@ -226,6 +226,24 @@ config USB_AT91
 	depends on USB_GADGET_AT91
 	default USB_GADGET
 
+config USB_GADGET_M66592
+	boolean "M66592 driver"
+	select USB_GADGET_DUALSPEED
+	help
+	   M66592 is a USB 2.0 peripheral controller.
+
+	   It has seven configurable endpoints, and endpoint zero.
+
+	   Say "y" to link the driver statically, or "m" to build a
+	   dynamically linked module called "m66592_udc" and force all
+	   gadget drivers to also be dynamically linked.
+
+config USB_M66592
+	tristate
+	depends on USB_GADGET_M66592
+	default USB_GADGET
+	select USB_GADGET_SELECTED
+
 config USB_GADGET_DUMMY_HCD
 	boolean "Dummy HCD (DEVELOPMENT)"
 	depends on (USB=y || (USB=m && USB_GADGET=m)) && EXPERIMENTAL
diff --git a/drivers/usb/gadget/Makefile b/drivers/usb/gadget/Makefile
index 5db1939..2d41e84 100644
--- a/drivers/usb/gadget/Makefile
+++ b/drivers/usb/gadget/Makefile
@@ -9,6 +9,7 @@ obj-$(CONFIG_USB_OMAP)		+= omap_udc.o
 obj-$(CONFIG_USB_LH7A40X)	+= lh7a40x_udc.o
 obj-$(CONFIG_USB_AT91)		+= at91_udc.o
 obj-$(CONFIG_USB_FSL_USB2)	+= fsl_usb2_udc.o
+obj-$(CONFIG_USB_M66592)	+= m66592-udc.o
 
 #
 # USB gadget drivers
diff --git a/drivers/usb/gadget/ether.c b/drivers/usb/gadget/ether.c
index 325bf7c..49630ab 100644
--- a/drivers/usb/gadget/ether.c
+++ b/drivers/usb/gadget/ether.c
@@ -301,6 +301,10 @@ MODULE_PARM_DESC(host_addr, "Host Ethernet Address");
 #define	DEV_CONFIG_SUBSET
 #endif
 
+#ifdef CONFIG_USB_GADGET_M66592
+#define DEV_CONFIG_CDC
+#endif
+
 
 /*-------------------------------------------------------------------------*/
 
diff --git a/drivers/usb/gadget/gadget_chips.h b/drivers/usb/gadget/gadget_chips.h
index d041b91..96f50e3 100644
--- a/drivers/usb/gadget/gadget_chips.h
+++ b/drivers/usb/gadget/gadget_chips.h
@@ -125,6 +125,13 @@
 #define gadget_is_mpc8272(g)	0
 #endif
 
+#ifdef CONFIG_USB_GADGET_M66592
+#define	gadget_is_m66592(g)	!strcmp("m66592_udc", (g)->name)
+#else
+#define	gadget_is_m66592(g)	0
+#endif
+
+
 // CONFIG_USB_GADGET_SX2
 // CONFIG_USB_GADGET_AU1X00
 // ...
@@ -185,5 +192,7 @@ static inline int usb_gadget_controller_number(struct usb_gadget *gadget)
 		return 0x18;
 	else if (gadget_is_fsl_usb2(gadget))
 		return 0x19;
+	else if (gadget_is_m66592(gadget))
+		return 0x20;
 	return -ENOENT;
 }
diff --git a/drivers/usb/gadget/m66592-udc.c b/drivers/usb/gadget/m66592-udc.c
new file mode 100644
index 0000000..611130c
--- /dev/null
+++ b/drivers/usb/gadget/m66592-udc.c
@@ -0,0 +1,1653 @@
+/*
+ * M66592 UDC (USB gadget)
+ *
+ * Copyright (C) 2006-2007 Renesas Solutions Corp.
+ *
+ * Author : Yoshihiro Shimoda <[email protected]>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * 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/module.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/smp_lock.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/timer.h>
+#include <linux/delay.h>
+#include <linux/list.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <linux/usb/ch9.h>
+#include <linux/usb_gadget.h>
+
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/system.h>
+
+#include "m66592-udc.h"
+
+MODULE_DESCRIPTION("M66592 USB gadget driiver");
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Yoshihiro Shimoda");
+
+#define DRIVER_VERSION	"9 May 2007"
+
+/* module parameters */
+static unsigned short clock = M66592_XTAL24;
+module_param(clock, ushort, 0644);
+MODULE_PARM_DESC(clock, "input clock: 48MHz=32768, 24MHz=16384, 12MHz=0(default=16384)");
+static unsigned short vif = M66592_LDRV;
+module_param(vif, ushort, 0644);
+MODULE_PARM_DESC(vif, "input VIF: 3.3V=32768, 1.5V=0(default=32768)");
+static unsigned short endian = 0;
+module_param(endian, ushort, 0644);
+MODULE_PARM_DESC(endian, "data endian: big=256, little=0(default=0)");
+static unsigned short irq_sense = M66592_INTL;
+module_param(irq_sense, ushort, 0644);
+MODULE_PARM_DESC(irq_sense, "IRQ sense: low level=2, falling edge=0(default=2)");
+
+static const char udc_name[] = "m66592_udc";
+static const char *m66592_ep_name[] = {
+	"ep0", "ep1", "ep2", "ep3", "ep4", "ep5", "ep6", "ep7"
+};
+
+static void disable_controller(struct m66592 *m66592);
+static void irq_ep0_write(struct m66592_ep *ep, struct m66592_request *req);
+static void irq_packet_write(struct m66592_ep *ep, struct m66592_request *req);
+static int m66592_queue(struct usb_ep *_ep, struct usb_request *_req,
+			gfp_t gfp_flags);
+
+static void transfer_complete(struct m66592_ep *ep,
+			      struct m66592_request *req,
+			      int status);
+/*-------------------------------------------------------------------------*/
+static inline u16 get_usb_speed(struct m66592 *m66592)
+{
+	return (m66592_read(m66592, M66592_DVSTCTR) & M66592_RHST);
+}
+
+static void enable_pipe_irq(struct m66592 *m66592, u16 pipenum,
+			    unsigned long reg)
+{
+	u16 tmp;
+
+	tmp = m66592_read(m66592, M66592_INTENB0);
+	m66592_bclr(m66592, M66592_BEMPE | M66592_NRDYE | M66592_BRDYE,
+		    M66592_INTENB0);
+	m66592_bset(m66592, (1 << pipenum), reg);
+	m66592_write(m66592, tmp, M66592_INTENB0);
+}
+
+static void disable_pipe_irq(struct m66592 *m66592, u16 pipenum,
+			     unsigned long reg)
+{
+	u16 tmp;
+
+	tmp = m66592_read(m66592, M66592_INTENB0);
+	m66592_bclr(m66592, M66592_BEMPE | M66592_NRDYE | M66592_BRDYE,
+		    M66592_INTENB0);
+	m66592_bclr(m66592, (1 << pipenum), reg);
+	m66592_write(m66592, tmp, M66592_INTENB0);
+}
+
+static void m66592_usb_connect(struct m66592 *m66592)
+{
+	m66592_bset(m66592, M66592_CTRE, M66592_INTENB0);
+	m66592_bset(m66592, M66592_WDST | M66592_RDST | M66592_CMPL,
+		    M66592_INTENB0);
+	m66592_bset(m66592, M66592_BEMPE | M66592_BRDYE, M66592_INTENB0);
+
+	m66592_bset(m66592, M66592_DPRPU, M66592_SYSCFG);
+}
+
+static void m66592_usb_disconnect(struct m66592 *m66592)
+{
+	m66592_bclr(m66592, M66592_CTRE, M66592_INTENB0);
+	m66592_bclr(m66592, M66592_WDST | M66592_RDST | M66592_CMPL,
+		    M66592_INTENB0);
+	m66592_bclr(m66592, M66592_BEMPE | M66592_BRDYE, M66592_INTENB0);
+	m66592_bclr(m66592, M66592_DPRPU, M66592_SYSCFG);
+
+	m66592->gadget.speed = USB_SPEED_UNKNOWN;
+	spin_unlock(&m66592->lock);
+	m66592->driver->disconnect(&m66592->gadget);
+	spin_lock(&m66592->lock);
+
+	disable_controller(m66592);
+	INIT_LIST_HEAD(&m66592->ep[0].queue);
+}
+
+static inline u16 control_reg_get_pid(struct m66592 *m66592, u16 pipenum)
+{
+	u16 pid = 0;
+	unsigned long offset;
+
+	if (pipenum == 0)
+		pid = m66592_read(m66592, M66592_DCPCTR) & M66592_PID;
+	else if (pipenum < M66592_MAX_NUM_PIPE) {
+		offset = get_pipectr_addr(pipenum);
+		pid = m66592_read(m66592, offset) & M66592_PID;
+	} else
+		printk(KERN_ERR "unexpect pipe num (%d)\n", pipenum);
+
+	return pid;
+}
+
+static inline void control_reg_set_pid(struct m66592 *m66592, u16 pipenum,
+				       u16 pid)
+{
+	unsigned long offset;
+
+	if (pipenum == 0)
+		m66592_mdfy(m66592, pid, M66592_PID, M66592_DCPCTR);
+	else if (pipenum < M66592_MAX_NUM_PIPE) {
+		offset = get_pipectr_addr(pipenum);
+		m66592_mdfy(m66592, pid, M66592_PID, offset);
+	} else
+		printk(KERN_ERR "unexpect pipe num (%d)\n", pipenum);
+}
+
+static inline void pipe_start(struct m66592 *m66592, u16 pipenum)
+{
+	control_reg_set_pid(m66592, pipenum, M66592_PID_BUF);
+}
+
+static inline void pipe_stop(struct m66592 *m66592, u16 pipenum)
+{
+	control_reg_set_pid(m66592, pipenum, M66592_PID_NAK);
+}
+
+static inline void pipe_stall(struct m66592 *m66592, u16 pipenum)
+{
+	control_reg_set_pid(m66592, pipenum, M66592_PID_STALL);
+}
+
+static inline u16 control_reg_get(struct m66592 *m66592, u16 pipenum)
+{
+	u16 ret = 0;
+	unsigned long offset;
+
+	if (pipenum == 0)
+		ret = m66592_read(m66592, M66592_DCPCTR);
+	else if (pipenum < M66592_MAX_NUM_PIPE) {
+		offset = get_pipectr_addr(pipenum);
+		ret = m66592_read(m66592, offset);
+	} else
+		printk(KERN_ERR "unexpect pipe num (%d)\n", pipenum);
+
+	return ret;
+}
+
+static inline void control_reg_sqclr(struct m66592 *m66592, u16 pipenum)
+{
+	unsigned long offset;
+
+	pipe_stop(m66592, pipenum);
+
+	if (pipenum == 0)
+		m66592_bset(m66592, M66592_SQCLR, M66592_DCPCTR);
+	else if (pipenum < M66592_MAX_NUM_PIPE) {
+		offset = get_pipectr_addr(pipenum);
+		m66592_bset(m66592, M66592_SQCLR, offset);
+	} else
+		printk(KERN_ERR "unexpect pipe num(%d)\n", pipenum);
+}
+
+static inline int get_buffer_size(struct m66592 *m66592, u16 pipenum)
+{
+	u16 tmp;
+	int size;
+
+	if (pipenum == 0) {
+		tmp = m66592_read(m66592, M66592_DCPCFG);
+		if ((tmp & M66592_CNTMD) != 0)
+			size = 256;
+		else {
+			tmp = m66592_read(m66592, M66592_DCPMAXP);
+			size = tmp & M66592_MAXP;
+		}
+	} else {
+		m66592_write(m66592, pipenum, M66592_PIPESEL);
+		tmp = m66592_read(m66592, M66592_PIPECFG);
+		if ((tmp & M66592_CNTMD) != 0) {
+			tmp = m66592_read(m66592, M66592_PIPEBUF);
+			size = ((tmp >> 10) + 1) * 64;
+		} else {
+			tmp = m66592_read(m66592, M66592_PIPEMAXP);
+			size = tmp & M66592_MXPS;
+		}
+	}
+
+	return size;
+}
+
+static inline void pipe_change(struct m66592 *m66592, u16 pipenum)
+{
+	struct m66592_ep *ep = m66592->pipenum2ep[pipenum];
+
+	if (ep->use_dma)
+		return;
+
+	m66592_mdfy(m66592, pipenum, M66592_CURPIPE, ep->fifosel);
+
+	ndelay(450);
+
+	m66592_bset(m66592, M66592_MBW, ep->fifosel);
+}
+
+static int pipe_buffer_setting(struct m66592 *m66592,
+			       struct m66592_pipe_info *info)
+{
+	u16 bufnum = 0, buf_bsize = 0;
+	u16 pipecfg = 0;
+
+	if (info->pipe == 0)
+		return -EINVAL;
+
+	m66592_write(m66592, info->pipe, M66592_PIPESEL);
+
+	if (info->dir_in)
+		pipecfg |= M66592_DIR;
+	pipecfg |= info->type;
+	pipecfg |= info->epnum;
+	switch (info->type) {
+	case M66592_INT:
+		bufnum = 4 + (info->pipe - M66592_BASE_PIPENUM_INT);
+		buf_bsize = 0;
+		break;
+	case M66592_BULK:
+		bufnum = m66592->bi_bufnum +
+			 (info->pipe - M66592_BASE_PIPENUM_BULK) * 16;
+		m66592->bi_bufnum += 16;
+		buf_bsize = 7;
+		pipecfg |= M66592_DBLB;
+		if (!info->dir_in)
+			pipecfg |= M66592_SHTNAK;
+		break;
+	case M66592_ISO:
+		bufnum = m66592->bi_bufnum +
+			 (info->pipe - M66592_BASE_PIPENUM_ISOC) * 16;
+		m66592->bi_bufnum += 16;
+		buf_bsize = 7;
+		break;
+	}
+	if (m66592->bi_bufnum > M66592_MAX_BUFNUM) {
+		printk(KERN_ERR "m66592 pipe memory is insufficient(%d)\n",
+		       m66592->bi_bufnum);
+		return -ENOMEM;
+	}
+
+	m66592_write(m66592, pipecfg, M66592_PIPECFG);
+	m66592_write(m66592, (buf_bsize << 10) | (bufnum), M66592_PIPEBUF);
+	m66592_write(m66592, info->maxpacket, M66592_PIPEMAXP);
+	if (info->interval)
+		info->interval--;
+	m66592_write(m66592, info->interval, M66592_PIPEPERI);
+
+	return 0;
+}
+
+static void pipe_buffer_release(struct m66592 *m66592,
+				struct m66592_pipe_info *info)
+{
+	if (info->pipe == 0)
+		return;
+
+	switch (info->type) {
+	case M66592_BULK:
+		if (is_bulk_pipe(info->pipe))
+			m66592->bi_bufnum -= 16;
+		break;
+	case M66592_ISO:
+		if (is_isoc_pipe(info->pipe))
+			m66592->bi_bufnum -= 16;
+		break;
+	}
+
+	if (is_bulk_pipe(info->pipe)) {
+		m66592->bulk--;
+	} else if (is_interrupt_pipe(info->pipe))
+		m66592->interrupt--;
+	else if (is_isoc_pipe(info->pipe)) {
+		m66592->isochronous--;
+		if (info->type == M66592_BULK)
+			m66592->bulk--;
+	} else
+		printk(KERN_ERR "ep_release: unexpect pipenum (%d)\n",
+		       info->pipe);
+}
+
+static void pipe_initialize(struct m66592_ep *ep)
+{
+	struct m66592 *m66592 = ep->m66592;
+
+	m66592_mdfy(m66592, 0, M66592_CURPIPE, ep->fifosel);
+
+	m66592_write(m66592, M66592_ACLRM, ep->pipectr);
+	m66592_write(m66592, 0, ep->pipectr);
+	m66592_write(m66592, M66592_SQCLR, ep->pipectr);
+	if (ep->use_dma) {
+		m66592_mdfy(m66592, ep->pipenum, M66592_CURPIPE, ep->fifosel);
+
+		ndelay(450);
+
+		m66592_bset(m66592, M66592_MBW, ep->fifosel);
+	}
+}
+
+static void m66592_ep_setting(struct m66592 *m66592, struct m66592_ep *ep,
+			      const struct usb_endpoint_descriptor *desc,
+			      u16 pipenum, int dma)
+{
+	if ((pipenum != 0) && dma) {
+		if (m66592->num_dma == 0) {
+			m66592->num_dma++;
+			ep->use_dma = 1;
+			ep->fifoaddr = M66592_D0FIFO;
+			ep->fifosel = M66592_D0FIFOSEL;
+			ep->fifoctr = M66592_D0FIFOCTR;
+			ep->fifotrn = M66592_D0FIFOTRN;
+		} else if (m66592->num_dma == 1) {
+			m66592->num_dma++;
+			ep->use_dma = 1;
+			ep->fifoaddr = M66592_D1FIFO;
+			ep->fifosel = M66592_D1FIFOSEL;
+			ep->fifoctr = M66592_D1FIFOCTR;
+			ep->fifotrn = M66592_D1FIFOTRN;
+		} else {
+			ep->use_dma = 0;
+			ep->fifoaddr = M66592_CFIFO;
+			ep->fifosel = M66592_CFIFOSEL;
+			ep->fifoctr = M66592_CFIFOCTR;
+			ep->fifotrn = 0;
+		}
+	} else {
+		ep->use_dma = 0;
+		ep->fifoaddr = M66592_CFIFO;
+		ep->fifosel = M66592_CFIFOSEL;
+		ep->fifoctr = M66592_CFIFOCTR;
+		ep->fifotrn = 0;
+	}
+
+	ep->pipectr = get_pipectr_addr(pipenum);
+	ep->pipenum = pipenum;
+	ep->ep.maxpacket = desc->wMaxPacketSize;
+	m66592->pipenum2ep[pipenum] = ep;
+	m66592->epaddr2ep[desc->bEndpointAddress&USB_ENDPOINT_NUMBER_MASK] = ep;
+	INIT_LIST_HEAD(&ep->queue);
+}
+
+static void m66592_ep_release(struct m66592_ep *ep)
+{
+	struct m66592 *m66592 = ep->m66592;
+	u16 pipenum = ep->pipenum;
+
+	if (pipenum == 0)
+		return;
+
+	if (ep->use_dma)
+		m66592->num_dma--;
+	ep->pipenum = 0;
+	ep->busy = 0;
+	ep->use_dma = 0;
+}
+
+static int alloc_pipe_config(struct m66592_ep *ep,
+			     const struct usb_endpoint_descriptor *desc)
+{
+	struct m66592 *m66592 = ep->m66592;
+	struct m66592_pipe_info info;
+	int dma = 0;
+	int *counter;
+	int ret;
+
+	ep->desc = desc;
+
+	BUG_ON(ep->pipenum);
+
+	switch(desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) {
+	case USB_ENDPOINT_XFER_BULK:
+		if (m66592->bulk >= M66592_MAX_NUM_BULK) {
+			if (m66592->isochronous >= M66592_MAX_NUM_ISOC) {
+				printk(KERN_ERR "bulk pipe is insufficient\n");
+				return -ENODEV;
+			} else {
+				info.pipe = M66592_BASE_PIPENUM_ISOC +
+					    m66592->isochronous;
+				counter = &m66592->isochronous;
+			}
+		} else {
+			info.pipe = M66592_BASE_PIPENUM_BULK + m66592->bulk;
+			counter = &m66592->bulk;
+		}
+		info.type = M66592_BULK;
+		dma = 1;
+		break;
+	case USB_ENDPOINT_XFER_INT:
+		if (m66592->interrupt >= M66592_MAX_NUM_INT) {
+			printk(KERN_ERR "interrupt pipe is insufficient\n");
+			return -ENODEV;
+		}
+		info.pipe = M66592_BASE_PIPENUM_INT + m66592->interrupt;
+		info.type = M66592_INT;
+		counter = &m66592->interrupt;
+		break;
+	case USB_ENDPOINT_XFER_ISOC:
+		if (m66592->isochronous >= M66592_MAX_NUM_ISOC) {
+			printk(KERN_ERR "isochronous pipe is insufficient\n");
+			return -ENODEV;
+		}
+		info.pipe = M66592_BASE_PIPENUM_ISOC + m66592->isochronous;
+		info.type = M66592_ISO;
+		counter = &m66592->isochronous;
+		break;
+	default:
+		printk(KERN_ERR "unexpect xfer type\n");
+		return -EINVAL;
+	}
+	ep->type = info.type;
+
+	info.epnum = desc->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK;
+	info.maxpacket = desc->wMaxPacketSize;
+	info.interval = desc->bInterval;
+	if (desc->bEndpointAddress & USB_ENDPOINT_DIR_MASK)
+		info.dir_in = 1;
+	else
+		info.dir_in = 0;
+
+	ret = pipe_buffer_setting(m66592, &info);
+	if (ret < 0) {
+		printk(KERN_ERR "pipe_buffer_setting fail\n");
+		return ret;
+	}
+
+	(*counter)++;
+	if ((counter == &m66592->isochronous) && info.type == M66592_BULK)
+		m66592->bulk++;
+
+	m66592_ep_setting(m66592, ep, desc, info.pipe, dma);
+	pipe_initialize(ep);
+
+	return 0;
+}
+
+static int free_pipe_config(struct m66592_ep *ep)
+{
+	struct m66592 *m66592 = ep->m66592;
+	struct m66592_pipe_info info;
+
+	info.pipe = ep->pipenum;
+	info.type = ep->type;
+	pipe_buffer_release(m66592, &info);
+	m66592_ep_release(ep);
+
+	return 0;
+}
+
+/*-------------------------------------------------------------------------*/
+static void pipe_irq_enable(struct m66592 *m66592, u16 pipenum)
+{
+	enable_irq_ready(m66592, pipenum);
+	enable_irq_nrdy(m66592, pipenum);
+}
+
+static void pipe_irq_disable(struct m66592 *m66592, u16 pipenum)
+{
+	disable_irq_ready(m66592, pipenum);
+	disable_irq_nrdy(m66592, pipenum);
+}
+
+/* if complete is true, gadget driver complete function is not call */
+static void control_end(struct m66592 *m66592, unsigned ccpl)
+{
+	m66592->ep[0].internal_ccpl = ccpl;
+	pipe_start(m66592, 0);
+	m66592_bset(m66592, M66592_CCPL, M66592_DCPCTR);
+}
+
+static void start_ep0_write(struct m66592_ep *ep, struct m66592_request *req)
+{
+	struct m66592 *m66592 = ep->m66592;
+
+	pipe_change(m66592, ep->pipenum);
+	m66592_mdfy(m66592, M66592_ISEL | M66592_PIPE0,
+		    (M66592_ISEL | M66592_CURPIPE),
+		    M66592_CFIFOSEL);
+	m66592_write(m66592, M66592_BCLR, ep->fifoctr);
+	if (req->req.length == 0) {
+		m66592_bset(m66592, M66592_BVAL, ep->fifoctr);
+		pipe_start(m66592, 0);
+		transfer_complete(ep, req, 0);
+	} else {
+		m66592_write(m66592, ~M66592_BEMP0, M66592_BEMPSTS);
+		irq_ep0_write(ep, req);
+	}
+}
+
+static void start_packet_write(struct m66592_ep *ep, struct m66592_request *req)
+{
+	struct m66592 *m66592 = ep->m66592;
+	u16 tmp;
+
+	pipe_change(m66592, ep->pipenum);
+	disable_irq_empty(m66592, ep->pipenum);
+	pipe_start(m66592, ep->pipenum);
+
+	tmp = m66592_read(m66592, ep->fifoctr);
+	if (unlikely((tmp & M66592_FRDY) == 0))
+		pipe_irq_enable(m66592, ep->pipenum);
+	else
+		irq_packet_write(ep, req);
+}
+
+static void start_packet_read(struct m66592_ep *ep, struct m66592_request *req)
+{
+	struct m66592 *m66592 = ep->m66592;
+	u16 pipenum = ep->pipenum;
+
+	if (ep->pipenum == 0) {
+		m66592_mdfy(m66592, M66592_PIPE0,
+			    (M66592_ISEL | M66592_CURPIPE),
+			    M66592_CFIFOSEL);
+		m66592_write(m66592, M66592_BCLR, ep->fifoctr);
+		pipe_start(m66592, pipenum);
+		pipe_irq_enable(m66592, pipenum);
+	} else {
+		if (ep->use_dma) {
+			m66592_bset(m66592, M66592_TRCLR, ep->fifosel);
+			pipe_change(m66592, pipenum);
+			m66592_bset(m66592, M66592_TRENB, ep->fifosel);
+			m66592_write(m66592,
+				     (req->req.length + ep->ep.maxpacket - 1) /
+				     ep->ep.maxpacket, ep->fifotrn);
+		}
+		pipe_start(m66592, pipenum);	/* trigger once */
+		pipe_irq_enable(m66592, pipenum);
+	}
+}
+
+static void start_packet(struct m66592_ep *ep, struct m66592_request *req)
+{
+	if (ep->desc->bEndpointAddress & USB_DIR_IN)
+		start_packet_write(ep, req);
+	else
+		start_packet_read(ep, req);
+}
+
+static void start_ep0(struct m66592_ep *ep, struct m66592_request *req)
+{
+	u16 ctsq;
+
+	ctsq = m66592_read(ep->m66592, M66592_INTSTS0) & M66592_CTSQ;
+
+	switch (ctsq) {
+	case M66592_CS_RDDS:
+		start_ep0_write(ep, req);
+		break;
+	case M66592_CS_WRDS:
+		start_packet_read(ep, req);
+		break;
+
+	case M66592_CS_WRND:
+		control_end(ep->m66592, 0);
+		break;
+	default:
+		printk(KERN_ERR "start_ep0: unexpect ctsq(%x)\n", ctsq);
+		break;
+	}
+}
+
+static void init_controller(struct m66592 *m66592)
+{
+	m66592_bset(m66592, (vif & M66592_LDRV) | (endian & M66592_BIGEND),
+		    M66592_PINCFG);
+	m66592_bset(m66592, M66592_HSE, M66592_SYSCFG);		/* High spd */
+	m66592_mdfy(m66592, clock & M66592_XTAL, M66592_XTAL, M66592_SYSCFG);
+
+	m66592_bclr(m66592, M66592_USBE, M66592_SYSCFG);
+	m66592_bclr(m66592, M66592_DPRPU, M66592_SYSCFG);
+	m66592_bset(m66592, M66592_USBE, M66592_SYSCFG);
+
+	m66592_bset(m66592, M66592_XCKE, M66592_SYSCFG);
+
+	msleep(3);
+
+	m66592_bset(m66592, M66592_RCKE | M66592_PLLC, M66592_SYSCFG);
+
+	msleep(1);
+
+	m66592_bset(m66592, M66592_SCKE, M66592_SYSCFG);
+
+	m66592_bset(m66592, irq_sense & M66592_INTL, M66592_INTENB1);
+	m66592_write(m66592, M66592_BURST | M66592_CPU_ADR_RD_WR,
+		     M66592_DMA0CFG);
+}
+
+static void disable_controller(struct m66592 *m66592)
+{
+	m66592_bclr(m66592, M66592_SCKE, M66592_SYSCFG);
+	udelay(1);
+	m66592_bclr(m66592, M66592_PLLC, M66592_SYSCFG);
+	udelay(1);
+	m66592_bclr(m66592, M66592_RCKE, M66592_SYSCFG);
+	udelay(1);
+	m66592_bclr(m66592, M66592_XCKE, M66592_SYSCFG);
+}
+
+static void m66592_start_xclock(struct m66592 *m66592)
+{
+	u16 tmp;
+
+	tmp = m66592_read(m66592, M66592_SYSCFG);
+	if (!(tmp & M66592_XCKE))
+		m66592_bset(m66592, M66592_XCKE, M66592_SYSCFG);
+}
+
+/*-------------------------------------------------------------------------*/
+static void transfer_complete(struct m66592_ep *ep,
+			      struct m66592_request *req,
+			      int status)
+{
+	int restart = 0;
+
+	if (unlikely(ep->pipenum == 0)) {
+		if (ep->internal_ccpl) {
+			ep->internal_ccpl = 0;
+			return;
+		}
+	}
+
+	list_del_init(&req->queue);
+	if (ep->m66592->gadget.speed == USB_SPEED_UNKNOWN)
+		req->req.status = -ESHUTDOWN;
+	else
+		req->req.status = status;
+
+	if (!list_empty(&ep->queue))
+		restart = 1;
+
+	if (likely(req->req.complete))
+		req->req.complete(&ep->ep, &req->req);
+
+	if (restart) {
+		req = list_entry(ep->queue.next, struct m66592_request, queue);
+		if (ep->desc)
+			start_packet(ep, req);
+	}
+}
+
+static void irq_ep0_write(struct m66592_ep *ep, struct m66592_request *req)
+{
+	int i;
+	volatile u16 tmp;
+	unsigned bufsize;
+	size_t size;
+	void *buf;
+	u16 pipenum = ep->pipenum;
+	struct m66592 *m66592 = ep->m66592;
+
+	pipe_change(m66592, pipenum);
+	m66592_bset(m66592, M66592_ISEL, ep->fifosel);
+
+	i = 0;
+	do {
+		tmp = m66592_read(m66592, ep->fifoctr);
+		if (i++ > 100000) {
+			printk(KERN_ERR "pipe0 is busy. maybe cpu i/o bus"
+				"conflict. please power off this controller.");
+			return;
+		}
+		ndelay(1);
+	} while ((tmp & M66592_FRDY) == 0);
+
+	/* prepare parameters */
+	bufsize = get_buffer_size(m66592, pipenum);
+	buf = req->req.buf + req->req.actual;
+	size = min(bufsize, req->req.length - req->req.actual);
+
+	/* write fifo */
+	if (req->req.buf) {
+		if (size > 0)
+			m66592_write_fifo(m66592, ep->fifoaddr, buf, size);
+		if ((size == 0) || ((size % ep->ep.maxpacket) != 0))
+			m66592_bset(m66592, M66592_BVAL, ep->fifoctr);
+	}
+
+	/* update parameters */
+	req->req.actual += size;
+
+	/* check transfer finish */
+	if ((!req->req.zero && (req->req.actual == req->req.length)) ||
+	    (size % ep->ep.maxpacket) || (size == 0)) {
+		disable_irq_ready(m66592, pipenum);
+		disable_irq_empty(m66592, pipenum);
+	} else {
+		disable_irq_ready(m66592, pipenum);
+		enable_irq_empty(m66592, pipenum);
+	}
+	pipe_start(m66592, pipenum);
+}
+
+static void irq_packet_write(struct m66592_ep *ep, struct m66592_request *req)
+{
+	u16 tmp;
+	unsigned bufsize;
+	size_t size;
+	void *buf;
+	u16 pipenum = ep->pipenum;
+	struct m66592 *m66592 = ep->m66592;
+
+	pipe_change(m66592, pipenum);
+	tmp = m66592_read(m66592, ep->fifoctr);
+	if (unlikely((tmp & M66592_FRDY) == 0)) {
+		pipe_stop(m66592, pipenum);
+		pipe_irq_disable(m66592, pipenum);
+		printk(KERN_ERR "write fifo not ready. pipnum=%d\n", pipenum);
+		return;
+	}
+
+	/* prepare parameters */
+	bufsize = get_buffer_size(m66592, pipenum);
+	buf = req->req.buf + req->req.actual;
+	size = min(bufsize, req->req.length - req->req.actual);
+
+	/* write fifo */
+	if (req->req.buf) {
+		m66592_write_fifo(m66592, ep->fifoaddr, buf, size);
+		if ((size == 0) || ((size % ep->ep.maxpacket) != 0) ||
+		    ((bufsize != ep->ep.maxpacket) && (bufsize > size)))
+			m66592_bset(m66592, M66592_BVAL, ep->fifoctr);
+	}
+
+	/* update parameters */
+	req->req.actual += size;
+	/* check transfer finish */
+	if ((!req->req.zero && (req->req.actual == req->req.length)) ||
+	    (size % ep->ep.maxpacket) || (size == 0)) {
+		disable_irq_ready(m66592, pipenum);
+		enable_irq_empty(m66592, pipenum);
+	} else {
+		disable_irq_empty(m66592, pipenum);
+		pipe_irq_enable(m66592, pipenum);
+	}
+}
+
+static void irq_packet_read(struct m66592_ep *ep, struct m66592_request *req)
+{
+	u16 tmp;
+	int rcv_len, bufsize, req_len;
+	int size;
+	void *buf;
+	u16 pipenum = ep->pipenum;
+	struct m66592 *m66592 = ep->m66592;
+	int finish = 0;
+
+	pipe_change(m66592, pipenum);
+	tmp = m66592_read(m66592, ep->fifoctr);
+	if (unlikely((tmp & M66592_FRDY) == 0)) {
+		req->req.status = -EPIPE;
+		pipe_stop(m66592, pipenum);
+		pipe_irq_disable(m66592, pipenum);
+		printk(KERN_ERR "read fifo not ready");
+		return;
+	}
+
+	/* prepare parameters */
+	rcv_len = tmp & M66592_DTLN;
+	bufsize = get_buffer_size(m66592, pipenum);
+
+	buf = req->req.buf + req->req.actual;
+	req_len = req->req.length - req->req.actual;
+	if (rcv_len < bufsize)
+		size = min(rcv_len, req_len);
+	else
+		size = min(bufsize, req_len);
+
+	/* update parameters */
+	req->req.actual += size;
+
+	/* check transfer finish */
+	if ((!req->req.zero && (req->req.actual == req->req.length)) ||
+	    (size % ep->ep.maxpacket) || (size == 0)) {
+		pipe_stop(m66592, pipenum);
+		pipe_irq_disable(m66592, pipenum);
+		finish = 1;
+	}
+
+	/* read fifo */
+	if (req->req.buf) {
+		if (size == 0)
+			m66592_write(m66592, M66592_BCLR, ep->fifoctr);
+		else
+			m66592_read_fifo(m66592, ep->fifoaddr, buf, size);
+	}
+
+	if ((ep->pipenum != 0) && finish)
+		transfer_complete(ep, req, 0);
+}
+
+static void irq_pipe_ready(struct m66592 *m66592, u16 status, u16 enb)
+{
+	u16 check;
+	u16 pipenum;
+	struct m66592_ep *ep;
+	struct m66592_request *req;
+
+	if ((status & M66592_BRDY0) && (enb & M66592_BRDY0)) {
+		m66592_write(m66592, ~M66592_BRDY0, M66592_BRDYSTS);
+		m66592_mdfy(m66592, M66592_PIPE0, M66592_CURPIPE,
+			    M66592_CFIFOSEL);
+
+		ep = &m66592->ep[0];
+		req = list_entry(ep->queue.next, struct m66592_request, queue);
+		irq_packet_read(ep, req);
+	} else {
+		for (pipenum = 1; pipenum < M66592_MAX_NUM_PIPE; pipenum++) {
+			check = 1 << pipenum;
+			if ((status & check) && (enb & check)) {
+				m66592_write(m66592, ~check, M66592_BRDYSTS);
+				ep = m66592->pipenum2ep[pipenum];
+				req = list_entry(ep->queue.next,
+						 struct m66592_request, queue);
+				if (ep->desc->bEndpointAddress & USB_DIR_IN)
+					irq_packet_write(ep, req);
+				else
+					irq_packet_read(ep, req);
+			}
+		}
+	}
+}
+
+static void irq_pipe_empty(struct m66592 *m66592, u16 status, u16 enb)
+{
+	u16 tmp;
+	u16 check;
+	u16 pipenum;
+	struct m66592_ep *ep;
+	struct m66592_request *req;
+
+	if ((status & M66592_BEMP0) && (enb & M66592_BEMP0)) {
+		m66592_write(m66592, ~M66592_BEMP0, M66592_BEMPSTS);
+
+		ep = &m66592->ep[0];
+		req = list_entry(ep->queue.next, struct m66592_request, queue);
+		irq_ep0_write(ep, req);
+	} else {
+		for (pipenum = 1; pipenum < M66592_MAX_NUM_PIPE; pipenum++) {
+			check = 1 << pipenum;
+			if ((status & check) && (enb & check)) {
+				m66592_write(m66592, ~check, M66592_BEMPSTS);
+				tmp = control_reg_get(m66592, pipenum);
+				if ((tmp & M66592_INBUFM) == 0) {
+					disable_irq_empty(m66592, pipenum);
+					pipe_irq_disable(m66592, pipenum);
+					pipe_stop(m66592, pipenum);
+					ep = m66592->pipenum2ep[pipenum];
+					req = list_entry(ep->queue.next,
+							 struct m66592_request,
+							 queue);
+					if (!list_empty(&ep->queue))
+						transfer_complete(ep, req, 0);
+				}
+			}
+		}
+	}
+}
+
+static void get_status(struct m66592 *m66592, struct usb_ctrlrequest *ctrl)
+{
+	struct m66592_ep *ep;
+	u16 pid;
+	u16 status = 0;
+
+	switch (ctrl->bRequestType & USB_RECIP_MASK) {
+	case USB_RECIP_DEVICE:
+		status = 1;	/* selfpower */
+		break;
+	case USB_RECIP_INTERFACE:
+		status = 0;
+		break;
+	case USB_RECIP_ENDPOINT:
+		ep = m66592->epaddr2ep[ctrl->wIndex&USB_ENDPOINT_NUMBER_MASK];
+		pid = control_reg_get_pid(m66592, ep->pipenum);
+		if (pid == M66592_PID_STALL)
+			status = 1;
+		else
+			status = 0;
+		break;
+	default:
+		pipe_stall(m66592, 0);
+		return;		/* exit */
+	}
+
+	*m66592->ep0_buf = status;
+	m66592->ep0_req->buf = m66592->ep0_buf;
+	m66592->ep0_req->length = 2;
+	m66592_queue(m66592->gadget.ep0, m66592->ep0_req, GFP_KERNEL);
+}
+
+static void clear_feature(struct m66592 *m66592, struct usb_ctrlrequest *ctrl)
+{
+	switch (ctrl->bRequestType & USB_RECIP_MASK) {
+	case USB_RECIP_DEVICE:
+		control_end(m66592, 1);
+		break;
+	case USB_RECIP_INTERFACE:
+		control_end(m66592, 1);
+		break;
+	case USB_RECIP_ENDPOINT: {
+		struct m66592_ep *ep;
+		struct m66592_request *req;
+
+		ep = m66592->epaddr2ep[ctrl->wIndex&USB_ENDPOINT_NUMBER_MASK];
+		pipe_stop(m66592, ep->pipenum);
+		control_reg_sqclr(m66592, ep->pipenum);
+
+		control_end(m66592, 1);
+
+		req = list_entry(ep->queue.next,
+		struct m66592_request, queue);
+		if (ep->busy) {
+			ep->busy = 0;
+			if (list_empty(&ep->queue))
+				break;
+			start_packet(ep, req);
+		} else if (!list_empty(&ep->queue))
+			pipe_start(m66592, ep->pipenum);
+		}
+		break;
+	default:
+		pipe_stall(m66592, 0);
+		break;
+	}
+}
+
+static void set_feature(struct m66592 *m66592, struct usb_ctrlrequest *ctrl)
+{
+
+	switch (ctrl->bRequestType & USB_RECIP_MASK) {
+	case USB_RECIP_DEVICE:
+		control_end(m66592, 1);
+		break;
+	case USB_RECIP_INTERFACE:
+		control_end(m66592, 1);
+		break;
+	case USB_RECIP_ENDPOINT: {
+		struct m66592_ep *ep;
+
+		ep = m66592->epaddr2ep[ctrl->wIndex&USB_ENDPOINT_NUMBER_MASK];
+		pipe_stall(m66592, ep->pipenum);
+
+		control_end(m66592, 1);
+		}
+		break;
+	default:
+		pipe_stall(m66592, 0);
+		break;
+	}
+}
+
+/* if return value is true, call class driver's setup() */
+static int setup_packet(struct m66592 *m66592, struct usb_ctrlrequest *ctrl)
+{
+	u16 *p = (u16 *)ctrl;
+	unsigned long offset = M66592_USBREQ;
+	int i, ret = 0;
+
+	/* read fifo */
+	m66592_write(m66592, ~M66592_VALID, M66592_INTSTS0);
+
+	for (i = 0; i < 4; i++)
+		p[i] = m66592_read(m66592, offset + i*2);
+
+	/* check request */
+	if ((ctrl->bRequestType & USB_TYPE_MASK) == USB_TYPE_STANDARD) {
+		switch (ctrl->bRequest) {
+		case USB_REQ_GET_STATUS:
+			get_status(m66592, ctrl);
+			break;
+		case USB_REQ_CLEAR_FEATURE:
+			clear_feature(m66592, ctrl);
+			break;
+		case USB_REQ_SET_FEATURE:
+			set_feature(m66592, ctrl);
+			break;
+		default:
+			ret = 1;
+			break;
+		}
+	} else
+		ret = 1;
+	return ret;
+}
+
+static void m66592_update_usb_speed(struct m66592 *m66592)
+{
+	u16 speed = get_usb_speed(m66592);
+
+	switch (speed) {
+	case M66592_HSMODE:
+		m66592->gadget.speed = USB_SPEED_HIGH;
+		break;
+	case M66592_FSMODE:
+		m66592->gadget.speed = USB_SPEED_FULL;
+		break;
+	default:
+		m66592->gadget.speed = USB_SPEED_UNKNOWN;
+		printk(KERN_ERR "USB speed unknown\n");
+	}
+}
+
+static void irq_device_state(struct m66592 *m66592)
+{
+	u16 dvsq;
+
+	dvsq = m66592_read(m66592, M66592_INTSTS0) & M66592_DVSQ;
+	m66592_write(m66592, ~M66592_DVST, M66592_INTSTS0);
+
+	if (dvsq == M66592_DS_DFLT) {	/* bus reset */
+		m66592->driver->disconnect(&m66592->gadget);
+		m66592_update_usb_speed(m66592);
+	}
+	if (m66592->old_dvsq == M66592_DS_CNFG && dvsq != M66592_DS_CNFG)
+		m66592_update_usb_speed(m66592);
+	if ((dvsq == M66592_DS_CNFG || dvsq == M66592_DS_ADDS) &&
+	    m66592->gadget.speed == USB_SPEED_UNKNOWN)
+		m66592_update_usb_speed(m66592);
+
+	m66592->old_dvsq = dvsq;
+}
+
+static void irq_control_stage(struct m66592 *m66592)
+{
+	struct usb_ctrlrequest ctrl;
+	u16 ctsq;
+
+	ctsq = m66592_read(m66592, M66592_INTSTS0) & M66592_CTSQ;
+	m66592_write(m66592, ~M66592_CTRT, M66592_INTSTS0);
+
+	switch (ctsq) {
+	case M66592_CS_IDST: {
+		struct m66592_ep *ep;
+		struct m66592_request *req;
+		ep = &m66592->ep[0];
+		req = list_entry(ep->queue.next, struct m66592_request, queue);
+		transfer_complete(ep, req, 0);
+		}
+		break;
+
+	case M66592_CS_RDDS:
+	case M66592_CS_WRDS:
+	case M66592_CS_WRND:
+		if (setup_packet(m66592, &ctrl)) {
+			if (m66592->driver->setup(&m66592->gadget, &ctrl) < 0)
+				pipe_stall(m66592, 0);
+		}
+		break;
+	case M66592_CS_RDSS:
+	case M66592_CS_WRSS:
+		control_end(m66592, 0);
+		break;
+	default:
+		printk(KERN_ERR "ctrl_stage: unexpect ctsq(%x)\n", ctsq);
+		break;
+	}
+}
+
+static irqreturn_t m66592_irq(int irq, void *_m66592)
+{
+	struct m66592 *m66592 = _m66592;
+	u16 intsts0;
+	u16 intenb0;
+	u16 brdysts, nrdysts, bempsts;
+	u16 brdyenb, nrdyenb, bempenb;
+	u16 savepipe;
+	u16 mask0;
+
+	intsts0 = m66592_read(m66592, M66592_INTSTS0);
+	intenb0 = m66592_read(m66592, M66592_INTENB0);
+
+	savepipe = m66592_read(m66592, M66592_CFIFOSEL);
+
+	mask0 = intsts0 & intenb0;
+	if (mask0) {
+		brdysts = m66592_read(m66592, M66592_BRDYSTS);
+		nrdysts = m66592_read(m66592, M66592_NRDYSTS);
+		bempsts = m66592_read(m66592, M66592_BEMPSTS);
+		brdyenb = m66592_read(m66592, M66592_BRDYENB);
+		nrdyenb = m66592_read(m66592, M66592_NRDYENB);
+		bempenb = m66592_read(m66592, M66592_BEMPENB);
+
+		if (mask0 & M66592_VBINT) {
+			m66592_write(m66592, (u16)~M66592_VBINT,
+				     M66592_INTSTS0);
+			m66592_start_xclock(m66592);
+
+			/* start vbus sampling */
+			m66592->old_vbus = m66592_read(m66592, M66592_INTSTS0)
+					   & M66592_VBSTS;
+			m66592->scount = M66592_MAX_SAMPLING;
+
+			mod_timer(&m66592->timer,
+				  jiffies + msecs_to_jiffies(50));
+		}
+		if (intsts0 & M66592_DVSQ)
+			irq_device_state(m66592);
+
+		if ((intsts0 & M66592_BRDY) && (intenb0 & M66592_BRDYE) &&
+		    (brdysts & brdyenb)) {
+			irq_pipe_ready(m66592, brdysts, brdyenb);
+		}
+		if ((intsts0 & M66592_BEMP) && (intenb0 & M66592_BEMPE) &&
+		    (bempsts & bempenb)) {
+			irq_pipe_empty(m66592, bempsts, bempenb);
+		}
+
+		if (intsts0 & M66592_CTRT)
+			irq_control_stage(m66592);
+	}
+
+	m66592_write(m66592, savepipe, M66592_CFIFOSEL);
+
+	return IRQ_HANDLED;
+}
+
+static void m66592_timer(unsigned long _m66592)
+{
+	struct m66592 *m66592 = (struct m66592 *)_m66592;
+	unsigned long flags;
+	u16 tmp;
+
+	spin_lock_irqsave(&m66592->lock, flags);
+	tmp = m66592_read(m66592, M66592_SYSCFG);
+	if (!(tmp & M66592_RCKE)) {
+		m66592_bset(m66592, M66592_RCKE | M66592_PLLC, M66592_SYSCFG);
+		udelay(10);
+		m66592_bset(m66592, M66592_SCKE, M66592_SYSCFG);
+	}
+	if (m66592->scount > 0) {
+		tmp = m66592_read(m66592, M66592_INTSTS0) & M66592_VBSTS;
+		if (tmp == m66592->old_vbus) {
+			m66592->scount--;
+			if (m66592->scount == 0) {
+				if (tmp == M66592_VBSTS)
+					m66592_usb_connect(m66592);
+				else
+					m66592_usb_disconnect(m66592);
+			} else {
+				mod_timer(&m66592->timer,
+					  jiffies + msecs_to_jiffies(50));
+			}
+		} else {
+			m66592->scount = M66592_MAX_SAMPLING;
+			m66592->old_vbus = tmp;
+			mod_timer(&m66592->timer,
+				  jiffies + msecs_to_jiffies(50));
+		}
+	}
+	spin_unlock_irqrestore(&m66592->lock, flags);
+}
+
+/*-------------------------------------------------------------------------*/
+static int m66592_enable(struct usb_ep *_ep,
+			 const struct usb_endpoint_descriptor *desc)
+{
+	struct m66592_ep *ep;
+
+	ep = container_of(_ep, struct m66592_ep, ep);
+	return alloc_pipe_config(ep, desc);
+}
+
+static int m66592_disable(struct usb_ep *_ep)
+{
+	struct m66592_ep *ep;
+	struct m66592_request *req;
+	unsigned long flags;
+
+	ep = container_of(_ep, struct m66592_ep, ep);
+	BUG_ON(!ep);
+
+	while (!list_empty(&ep->queue)) {
+		req = list_entry(ep->queue.next, struct m66592_request, queue);
+		spin_lock_irqsave(&ep->m66592->lock, flags);
+		transfer_complete(ep, req, -ECONNRESET);
+		spin_unlock_irqrestore(&ep->m66592->lock, flags);
+	}
+
+	pipe_irq_disable(ep->m66592, ep->pipenum);
+	return free_pipe_config(ep);
+}
+
+static struct usb_request *m66592_alloc_request(struct usb_ep *_ep,
+						gfp_t gfp_flags)
+{
+	struct m66592_request *req;
+
+	req = kzalloc(sizeof(struct m66592_request), gfp_flags);
+	if (!req)
+		return NULL;
+
+	INIT_LIST_HEAD(&req->queue);
+
+	return &req->req;
+}
+
+static void m66592_free_request(struct usb_ep *_ep, struct usb_request *_req)
+{
+	struct m66592_request *req;
+
+	req = container_of(_req, struct m66592_request, req);
+	kfree(req);
+}
+
+static void *m66592_alloc_buffer(struct usb_ep *_ep, unsigned bytes,
+				 dma_addr_t *dma, gfp_t gfp_flags)
+{
+	void *buf;
+
+	buf = kzalloc(bytes, gfp_flags);
+	if (dma)
+		*dma = virt_to_bus(buf);
+
+	return buf;
+}
+
+static void m66592_free_buffer(struct usb_ep *_ep, void *buf,
+			       dma_addr_t dma, unsigned bytes)
+{
+	kfree(buf);
+}
+
+static int m66592_queue(struct usb_ep *_ep, struct usb_request *_req,
+			gfp_t gfp_flags)
+{
+	struct m66592_ep *ep;
+	struct m66592_request *req;
+	unsigned long flags;
+	int request = 0;
+
+	ep = container_of(_ep, struct m66592_ep, ep);
+	req = container_of(_req, struct m66592_request, req);
+
+	if (ep->m66592->gadget.speed == USB_SPEED_UNKNOWN)
+		return -ESHUTDOWN;
+
+	spin_lock_irqsave(&ep->m66592->lock, flags);
+
+	if (list_empty(&ep->queue))
+		request = 1;
+
+	list_add_tail(&req->queue, &ep->queue);
+	req->req.actual = 0;
+	req->req.status = -EINPROGRESS;
+
+	if (ep->desc == 0)	/* control */
+		start_ep0(ep, req);
+	else {
+		if (request && !ep->busy)
+			start_packet(ep, req);
+	}
+
+	spin_unlock_irqrestore(&ep->m66592->lock, flags);
+
+	return 0;
+}
+
+static int m66592_dequeue(struct usb_ep *_ep, struct usb_request *_req)
+{
+	struct m66592_ep *ep;
+	struct m66592_request *req;
+	unsigned long flags;
+
+	ep = container_of(_ep, struct m66592_ep, ep);
+	req = container_of(_req, struct m66592_request, req);
+
+	spin_lock_irqsave(&ep->m66592->lock, flags);
+	if (!list_empty(&ep->queue))
+		transfer_complete(ep, req, -ECONNRESET);
+	spin_unlock_irqrestore(&ep->m66592->lock, flags);
+
+	return 0;
+}
+
+static int m66592_set_halt(struct usb_ep *_ep, int value)
+{
+	struct m66592_ep *ep;
+	struct m66592_request *req;
+	unsigned long flags;
+	int ret = 0;
+
+	ep = container_of(_ep, struct m66592_ep, ep);
+	req = list_entry(ep->queue.next, struct m66592_request, queue);
+
+	spin_lock_irqsave(&ep->m66592->lock, flags);
+	if (!list_empty(&ep->queue)) {
+		ret = -EAGAIN;
+		goto out;
+	}
+	if (value) {
+		ep->busy = 1;
+		pipe_stall(ep->m66592, ep->pipenum);
+	} else {
+		ep->busy = 0;
+		pipe_stop(ep->m66592, ep->pipenum);
+	}
+
+out:
+	spin_unlock_irqrestore(&ep->m66592->lock, flags);
+	return ret;
+}
+
+static int m66592_fifo_status(struct usb_ep *_ep)
+{
+	return -EOPNOTSUPP;
+}
+
+static void m66592_fifo_flush(struct usb_ep *_ep)
+{
+	struct m66592_ep *ep;
+	unsigned long flags;
+
+	ep = container_of(_ep, struct m66592_ep, ep);
+	spin_lock_irqsave(&ep->m66592->lock, flags);
+	if (list_empty(&ep->queue) && !ep->busy) {
+		pipe_stop(ep->m66592, ep->pipenum);
+		m66592_bclr(ep->m66592, M66592_BCLR, ep->fifoctr);
+	}
+	spin_unlock_irqrestore(&ep->m66592->lock, flags);
+}
+
+static struct usb_ep_ops m66592_ep_ops = {
+	.enable		= m66592_enable,
+	.disable	= m66592_disable,
+
+	.alloc_request	= m66592_alloc_request,
+	.free_request	= m66592_free_request,
+
+	.alloc_buffer	= m66592_alloc_buffer,
+	.free_buffer	= m66592_free_buffer,
+
+	.queue		= m66592_queue,
+	.dequeue	= m66592_dequeue,
+
+	.set_halt	= m66592_set_halt,
+	.fifo_status	= m66592_fifo_status,
+	.fifo_flush	= m66592_fifo_flush,
+};
+
+/*-------------------------------------------------------------------------*/
+static struct m66592 *the_controller;
+
+int usb_gadget_register_driver(struct usb_gadget_driver *driver)
+{
+	struct m66592 *m66592 = the_controller;
+	int retval;
+
+	if (!driver ||
+	    driver->speed != USB_SPEED_HIGH ||
+	    !driver->bind ||
+	    !driver->unbind ||
+	    !driver->setup)
+		return -EINVAL;
+	if (!m66592)
+		return -ENODEV;
+	if (m66592->driver)
+		return -EBUSY;
+
+	/* hook up the driver */
+	driver->driver.bus = NULL;
+	m66592->driver = driver;
+	m66592->gadget.dev.driver = &driver->driver;
+
+	retval = device_add(&m66592->gadget.dev);
+	if (retval) {
+		printk(KERN_ERR "device_add error (%d)\n", retval);
+		goto error;
+	}
+
+	retval = driver->bind (&m66592->gadget);
+	if (retval) {
+		printk(KERN_ERR "bind to driver error (%d)\n", retval);
+		device_del(&m66592->gadget.dev);
+		goto error;
+	}
+
+	m66592_bset(m66592, M66592_VBSE | M66592_URST, M66592_INTENB0);
+	if (m66592_read(m66592, M66592_INTSTS0) & M66592_VBSTS) {
+		m66592_start_xclock(m66592);
+		/* start vbus sampling */
+		m66592->old_vbus = m66592_read(m66592,
+					 M66592_INTSTS0) & M66592_VBSTS;
+		m66592->scount = M66592_MAX_SAMPLING;
+		mod_timer(&m66592->timer,
+			  jiffies + msecs_to_jiffies(50));
+	}
+
+	return 0;
+
+error:
+	m66592->driver = NULL;
+	m66592->gadget.dev.driver = NULL;
+
+	return retval;
+}
+EXPORT_SYMBOL(usb_gadget_register_driver);
+
+int usb_gadget_unregister_driver(struct usb_gadget_driver *driver)
+{
+	struct m66592 *m66592 = the_controller;
+	unsigned long flags;
+
+	spin_lock_irqsave(&m66592->lock, flags);
+	if (m66592->gadget.speed != USB_SPEED_UNKNOWN)
+		m66592_usb_disconnect(m66592);
+	spin_unlock_irqrestore(&m66592->lock, flags);
+
+	m66592_bclr(m66592, M66592_VBSE | M66592_URST, M66592_INTENB0);
+
+	driver->unbind(&m66592->gadget);
+
+	init_controller(m66592);
+	disable_controller(m66592);
+
+	device_del(&m66592->gadget.dev);
+	m66592->driver = NULL;
+	return 0;
+}
+EXPORT_SYMBOL(usb_gadget_unregister_driver);
+
+/*-------------------------------------------------------------------------*/
+static int m66592_get_frame(struct usb_gadget *_gadget)
+{
+	struct m66592 *m66592 = gadget_to_m66592(_gadget);
+	return m66592_read(m66592, M66592_FRMNUM) & 0x03FF;
+}
+
+static struct usb_gadget_ops m66592_gadget_ops = {
+	.get_frame		= m66592_get_frame,
+};
+
+#if defined(CONFIG_PM)
+static int m66592_suspend(struct platform_device *pdev, pm_message_t state)
+{
+	pdev->dev.power.power_state = state;
+	return 0;
+}
+
+static int m66592_resume(struct platform_device *pdev)
+{
+	pdev->dev.power.power_state = PMSG_ON;
+	return 0;
+}
+#else	/* if defined(CONFIG_PM) */
+#define m66592_suspend		NULL
+#define m66592_resume		NULL
+#endif
+
+static int __init_or_module m66592_remove(struct platform_device *pdev)
+{
+	struct m66592		*m66592 = dev_get_drvdata(&pdev->dev);
+
+	del_timer_sync(&m66592->timer);
+	iounmap(m66592->reg);
+	free_irq(platform_get_irq(pdev, 0), m66592);
+	kfree(m66592);
+	return 0;
+}
+
+#define resource_len(r) (((r)->end - (r)->start) + 1)
+static int __init m66592_probe(struct platform_device *pdev)
+{
+	struct resource *res = NULL;
+	int irq = -1;
+	void __iomem *reg = NULL;
+	struct m66592 *m66592 = NULL;
+	int ret = 0;
+	int i;
+
+	res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
+					   (char *)udc_name);
+	if (!res) {
+		ret = -ENODEV;
+		printk(KERN_ERR "platform_get_resource_byname error.\n");
+		goto clean_up;
+	}
+
+	irq = platform_get_irq(pdev, 0);
+	if (irq < 0) {
+		ret = -ENODEV;
+		printk(KERN_ERR "platform_get_irq error.\n");
+		goto clean_up;
+	}
+
+	reg = ioremap(res->start, resource_len(res));
+	if (reg == NULL) {
+		ret = -ENOMEM;
+		printk(KERN_ERR "ioremap error.\n");
+		goto clean_up;
+	}
+
+	/* initialize ucd */
+	m66592 = kzalloc(sizeof(struct m66592), GFP_KERNEL);
+	if (m66592 == NULL) {
+		printk(KERN_ERR "kzalloc error\n");
+		goto clean_up;
+	}
+
+	spin_lock_init(&m66592->lock);
+	dev_set_drvdata(&pdev->dev, m66592);
+
+	m66592->gadget.ops = &m66592_gadget_ops;
+	device_initialize(&m66592->gadget.dev);
+	strcpy(m66592->gadget.dev.bus_id, "gadget");
+	m66592->gadget.is_dualspeed = 1;
+	m66592->gadget.dev.parent = &pdev->dev;
+	m66592->gadget.dev.dma_mask = pdev->dev.dma_mask;
+	m66592->gadget.dev.release = pdev->dev.release;
+	m66592->gadget.name = udc_name;
+
+	init_timer(&m66592->timer);
+	m66592->timer.function = m66592_timer;
+	m66592->timer.data = (unsigned long)m66592;
+	m66592->reg = reg;
+
+	m66592->bi_bufnum = M66592_BASE_BUFNUM;
+
+	ret = request_irq(irq, m66592_irq, SA_INTERRUPT | SA_SHIRQ,
+			  udc_name, m66592);
+	if (ret < 0) {
+		printk(KERN_ERR "request_irq error (%d)\n", ret);
+		goto clean_up;
+	}
+
+	INIT_LIST_HEAD(&m66592->gadget.ep_list);
+	m66592->gadget.ep0 = &m66592->ep[0].ep;
+	INIT_LIST_HEAD(&m66592->gadget.ep0->ep_list);
+	for (i = 0; i < M66592_MAX_NUM_PIPE; i++) {
+		struct m66592_ep *ep = &m66592->ep[i];
+
+		if (i != 0) {
+			INIT_LIST_HEAD(&m66592->ep[i].ep.ep_list);
+			list_add_tail(&m66592->ep[i].ep.ep_list,
+				      &m66592->gadget.ep_list);
+		}
+		ep->m66592 = m66592;
+		INIT_LIST_HEAD(&ep->queue);
+		ep->ep.name = m66592_ep_name[i];
+		ep->ep.ops = &m66592_ep_ops;
+		ep->ep.maxpacket = 512;
+	}
+	m66592->ep[0].ep.maxpacket = 64;
+	m66592->ep[0].pipenum = 0;
+	m66592->ep[0].fifoaddr = M66592_CFIFO;
+	m66592->ep[0].fifosel = M66592_CFIFOSEL;
+	m66592->ep[0].fifoctr = M66592_CFIFOCTR;
+	m66592->ep[0].fifotrn = 0;
+	m66592->ep[0].pipectr = get_pipectr_addr(0);
+	m66592->pipenum2ep[0] = &m66592->ep[0];
+	m66592->epaddr2ep[0] = &m66592->ep[0];
+
+	the_controller = m66592;
+
+	m66592->ep0_req = m66592_alloc_request(&m66592->ep[0].ep, GFP_KERNEL);
+	if (m66592->ep0_req == NULL)
+		goto clean_up;
+	m66592->ep0_buf = m66592_alloc_buffer(&m66592->ep[0].ep, 2, NULL,
+					      GFP_KERNEL);
+	if (m66592->ep0_buf == NULL)
+		goto clean_up;
+
+	init_controller(m66592);
+
+	printk("driver %s, %s\n", udc_name, DRIVER_VERSION);
+	return 0;
+
+clean_up:
+	if (m66592) {
+		if (m66592->ep0_req)
+			m66592_free_request(&m66592->ep[0].ep, m66592->ep0_req);
+		kfree(m66592);
+	}
+	if (reg)
+		iounmap(reg);
+
+	return ret;
+}
+
+/*-------------------------------------------------------------------------*/
+static struct platform_driver m66592_driver = {
+	.probe =	m66592_probe,
+	.remove =	m66592_remove,
+	.suspend =	m66592_suspend,
+	.resume =	m66592_resume,
+	.driver		= {
+		.name =	(char *) udc_name,
+	},
+};
+
+static int __init m66592_udc_init(void)
+{
+	return platform_driver_register(&m66592_driver);
+}
+module_init(m66592_udc_init);
+
+static void __exit m66592_udc_cleanup(void)
+{
+	platform_driver_unregister(&m66592_driver);
+}
+module_exit(m66592_udc_cleanup);
+
diff --git a/drivers/usb/gadget/m66592-udc.h b/drivers/usb/gadget/m66592-udc.h
new file mode 100644
index 0000000..26b54f8
--- /dev/null
+++ b/drivers/usb/gadget/m66592-udc.h
@@ -0,0 +1,577 @@
+/*
+ * M66592 UDC (USB gadget)
+ *
+ * Copyright (C) 2006-2007 Renesas Solutions Corp.
+ *
+ * Author : Yoshihiro Shimoda <[email protected]>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * 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 __M66592_UDC_H__
+#define __M66592_UDC_H__
+
+#define M66592_SYSCFG		0x00
+#define	M66592_XTAL		0xC000	/* b15-14: Crystal selection */
+#define	  M66592_XTAL48		 0x8000		  /* 48MHz */
+#define   M66592_XTAL24		 0x4000		  /* 24MHz */
+#define	  M66592_XTAL12		 0x0000		  /* 12MHz */
+#define	M66592_XCKE		0x2000	/* b13: External clock enable */
+#define	M66592_RCKE		0x1000	/* b12: Register clock enable */
+#define	M66592_PLLC		0x0800	/* b11: PLL control */
+#define	M66592_SCKE		0x0400	/* b10: USB clock enable */
+#define	M66592_ATCKM		0x0100	/* b8: Automatic supply functional enable */
+#define	M66592_HSE		0x0080	/* b7: Hi-speed enable */
+#define	M66592_DCFM		0x0040	/* b6: Controller function select  */
+#define	M66592_DMRPD		0x0020	/* b5: D- pull down control */
+#define	M66592_DPRPU		0x0010	/* b4: D+ pull up control */
+#define	M66592_FSRPC		0x0004	/* b2: Full-speed receiver enable */
+#define	M66592_PCUT		0x0002	/* b1: Low power sleep enable */
+#define	M66592_USBE		0x0001	/* b0: USB module operation enable */
+
+#define M66592_SYSSTS		0x02
+#define	M66592_LNST		0x0003	/* b1-0: D+, D- line status */
+#define	  M66592_SE1		 0x0003		  /* SE1 */
+#define	  M66592_KSTS		 0x0002		  /* K State */
+#define	  M66592_JSTS		 0x0001		  /* J State */
+#define	  M66592_SE0		 0x0000		  /* SE0 */
+
+#define M66592_DVSTCTR		0x04
+#define	M66592_WKUP		0x0100	/* b8: Remote wakeup */
+#define	M66592_RWUPE		0x0080	/* b7: Remote wakeup sense */
+#define	M66592_USBRST		0x0040	/* b6: USB reset enable */
+#define	M66592_RESUME		0x0020	/* b5: Resume enable */
+#define	M66592_UACT		0x0010	/* b4: USB bus enable */
+#define	M66592_RHST		0x0003	/* b1-0: Reset handshake status */
+#define	  M66592_HSMODE		 0x0003		  /* Hi-Speed mode */
+#define	  M66592_FSMODE		 0x0002		  /* Full-Speed mode */
+#define	  M66592_HSPROC		 0x0001		  /* HS handshake is processing */
+
+#define M66592_TESTMODE		0x06
+#define	M66592_UTST		0x000F	/* b4-0: Test select */
+#define	  M66592_H_TST_PACKET	 0x000C		  /* HOST TEST Packet */
+#define	  M66592_H_TST_SE0_NAK	 0x000B		  /* HOST TEST SE0 NAK */
+#define	  M66592_H_TST_K	 0x000A		  /* HOST TEST K */
+#define	  M66592_H_TST_J	 0x0009		  /* HOST TEST J */
+#define	  M66592_H_TST_NORMAL	 0x0000		  /* HOST Normal Mode */
+#define	  M66592_P_TST_PACKET	 0x0004		  /* PERI TEST Packet */
+#define	  M66592_P_TST_SE0_NAK	 0x0003		  /* PERI TEST SE0 NAK */
+#define	  M66592_P_TST_K	 0x0002		  /* PERI TEST K */
+#define	  M66592_P_TST_J	 0x0001		  /* PERI TEST J */
+#define	  M66592_P_TST_NORMAL	 0x0000		  /* PERI Normal Mode */
+
+#define M66592_PINCFG		0x0A
+#define	M66592_LDRV		0x8000	/* b15: Drive Current Adjust */
+#define	M66592_BIGEND		0x0100	/* b8: Big endian mode */
+
+#define M66592_DMA0CFG		0x0C
+#define M66592_DMA1CFG		0x0E
+#define	M66592_DREQA		0x4000	/* b14: Dreq active select */
+#define	M66592_BURST		0x2000	/* b13: Burst mode */
+#define	M66592_DACKA		0x0400	/* b10: Dack active select */
+#define	M66592_DFORM		0x0380	/* b9-7: DMA mode select */
+#define	  M66592_CPU_ADR_RD_WR	 0x0000		  /* Address + RD/WR mode (CPU bus) */
+#define	  M66592_CPU_DACK_RD_WR	 0x0100		  /* DACK + RD/WR mode (CPU bus) */
+#define	  M66592_CPU_DACK_ONLY	 0x0180		  /* DACK only mode (CPU bus) */
+#define	  M66592_SPLIT_DACK_ONLY	 0x0200		  /* DACK only mode (SPLIT bus) */
+#define	  M66592_SPLIT_DACK_DSTB	 0x0300		  /* DACK + DSTB0 mode (SPLIT bus) */
+#define	M66592_DENDA		0x0040	/* b6: Dend active select */
+#define	M66592_PKTM		0x0020	/* b5: Packet mode */
+#define	M66592_DENDE		0x0010	/* b4: Dend enable */
+#define	M66592_OBUS		0x0004	/* b2: OUTbus mode */
+
+#define M66592_CFIFO		0x10
+#define M66592_D0FIFO		0x14
+#define M66592_D1FIFO		0x18
+
+#define M66592_CFIFOSEL		0x1E
+#define M66592_D0FIFOSEL	0x24
+#define M66592_D1FIFOSEL	0x2A
+#define	M66592_RCNT		0x8000	/* b15: Read count mode */
+#define	M66592_REW		0x4000	/* b14: Buffer rewind */
+#define	M66592_DCLRM		0x2000	/* b13: DMA buffer clear mode */
+#define	M66592_DREQE		0x1000	/* b12: DREQ output enable */
+#define	M66592_MBW		0x0400	/* b10: Maximum bit width for FIFO access */
+#define	  M66592_MBW_8		 0x0000	  /*  8bit */
+#define	  M66592_MBW_16		 0x0400		  /* 16bit */
+#define	M66592_TRENB		0x0200	/* b9: Transaction counter enable */
+#define	M66592_TRCLR		0x0100	/* b8: Transaction counter clear */
+#define	M66592_DEZPM		0x0080	/* b7: Zero-length packet additional mode */
+#define	M66592_ISEL		0x0020	/* b5: DCP FIFO port direction select */
+#define	M66592_CURPIPE		0x0007	/* b2-0: PIPE select */
+
+#define M66592_CFIFOCTR		0x20
+#define M66592_D0FIFOCTR	0x26
+#define M66592_D1FIFOCTR	0x2c
+#define	M66592_BVAL		0x8000	/* b15: Buffer valid flag */
+#define	M66592_BCLR		0x4000	/* b14: Buffer clear */
+#define	M66592_FRDY		0x2000	/* b13: FIFO ready */
+#define	M66592_DTLN		0x0FFF	/* b11-0: FIFO received data length */
+
+#define M66592_CFIFOSIE		0x22
+#define	M66592_TGL		0x8000	/* b15: Buffer toggle */
+#define	M66592_SCLR		0x4000	/* b14: Buffer clear */
+#define	M66592_SBUSY		0x2000	/* b13: SIE_FIFO busy */
+
+#define M66592_D0FIFOTRN	0x28
+#define M66592_D1FIFOTRN	0x2E
+#define	M66592_TRNCNT		0xFFFF	/* b15-0: Transaction counter */
+
+#define M66592_INTENB0	0x30
+#define	M66592_VBSE	0x8000	/* b15: VBUS interrupt */
+#define	M66592_RSME	0x4000	/* b14: Resume interrupt */
+#define	M66592_SOFE	0x2000	/* b13: Frame update interrupt */
+#define	M66592_DVSE	0x1000	/* b12: Device state transition interrupt */
+#define	M66592_CTRE	0x0800	/* b11: Control transfer stage transition interrupt */
+#define	M66592_BEMPE	0x0400	/* b10: Buffer empty interrupt */
+#define	M66592_NRDYE	0x0200	/* b9: Buffer not ready interrupt */
+#define	M66592_BRDYE	0x0100	/* b8: Buffer ready interrupt */
+#define	M66592_URST	0x0080	/* b7: USB reset detected interrupt */
+#define	M66592_SADR	0x0040	/* b6: Set address executed interrupt */
+#define	M66592_SCFG	0x0020	/* b5: Set configuration executed interrupt */
+#define	M66592_SUSP	0x0010	/* b4: Suspend detected interrupt */
+#define	M66592_WDST	0x0008	/* b3: Control write data stage completed interrupt */
+#define	M66592_RDST	0x0004	/* b2: Control read data stage completed interrupt */
+#define	M66592_CMPL	0x0002	/* b1: Control transfer complete interrupt */
+#define	M66592_SERR	0x0001	/* b0: Sequence error interrupt */
+
+#define M66592_INTENB1	0x32
+#define	M66592_BCHGE	0x4000	/* b14: USB us chenge interrupt */
+#define	M66592_DTCHE	0x1000	/* b12: Detach sense interrupt */
+#define	M66592_SIGNE	0x0020	/* b5: SETUP IGNORE interrupt */
+#define	M66592_SACKE	0x0010	/* b4: SETUP ACK interrupt */
+#define	M66592_BRDYM	0x0004	/* b2: BRDY clear timing */
+#define	M66592_INTL	0x0002	/* b1: Interrupt sense select */
+#define	M66592_PCSE	0x0001	/* b0: PCUT enable by CS assert */
+
+#define M66592_BRDYENB		0x36
+#define M66592_BRDYSTS		0x46
+#define	M66592_BRDY7		0x0080	/* b7: PIPE7 */
+#define	M66592_BRDY6		0x0040	/* b6: PIPE6 */
+#define	M66592_BRDY5		0x0020	/* b5: PIPE5 */
+#define	M66592_BRDY4		0x0010	/* b4: PIPE4 */
+#define	M66592_BRDY3		0x0008	/* b3: PIPE3 */
+#define	M66592_BRDY2		0x0004	/* b2: PIPE2 */
+#define	M66592_BRDY1		0x0002	/* b1: PIPE1 */
+#define	M66592_BRDY0		0x0001	/* b1: PIPE0 */
+
+#define M66592_NRDYENB		0x38
+#define M66592_NRDYSTS		0x48
+#define	M66592_NRDY7		0x0080	/* b7: PIPE7 */
+#define	M66592_NRDY6		0x0040	/* b6: PIPE6 */
+#define	M66592_NRDY5		0x0020	/* b5: PIPE5 */
+#define	M66592_NRDY4		0x0010	/* b4: PIPE4 */
+#define	M66592_NRDY3		0x0008	/* b3: PIPE3 */
+#define	M66592_NRDY2		0x0004	/* b2: PIPE2 */
+#define	M66592_NRDY1		0x0002	/* b1: PIPE1 */
+#define	M66592_NRDY0		0x0001	/* b1: PIPE0 */
+
+#define M66592_BEMPENB		0x3A
+#define M66592_BEMPSTS		0x4A
+#define	M66592_BEMP7		0x0080	/* b7: PIPE7 */
+#define	M66592_BEMP6		0x0040	/* b6: PIPE6 */
+#define	M66592_BEMP5		0x0020	/* b5: PIPE5 */
+#define	M66592_BEMP4		0x0010	/* b4: PIPE4 */
+#define	M66592_BEMP3		0x0008	/* b3: PIPE3 */
+#define	M66592_BEMP2		0x0004	/* b2: PIPE2 */
+#define	M66592_BEMP1		0x0002	/* b1: PIPE1 */
+#define	M66592_BEMP0		0x0001	/* b0: PIPE0 */
+
+#define M66592_SOFCFG		0x3C
+#define	M66592_SOFM		0x000C	/* b3-2: SOF palse mode */
+#define	  M66592_SOF_125US	 0x0008		  /* SOF OUT 125us uFrame Signal */
+#define	  M66592_SOF_1MS	 0x0004		  /* SOF OUT 1ms Frame Signal */
+#define	  M66592_SOF_DISABLE	 0x0000		  /* SOF OUT Disable */
+
+#define M66592_INTSTS0		0x40
+#define	M66592_VBINT		0x8000	/* b15: VBUS interrupt */
+#define	M66592_RESM		0x4000	/* b14: Resume interrupt */
+#define	M66592_SOFR		0x2000	/* b13: SOF frame update interrupt */
+#define	M66592_DVST		0x1000	/* b12: Device state transition interrupt */
+#define	M66592_CTRT		0x0800	/* b11: Control transfer stage transition interrupt */
+#define	M66592_BEMP		0x0400	/* b10: Buffer empty interrupt */
+#define	M66592_NRDY		0x0200	/* b9: Buffer not ready interrupt */
+#define	M66592_BRDY		0x0100	/* b8: Buffer ready interrupt */
+#define	M66592_VBSTS		0x0080	/* b7: VBUS input port */
+#define	M66592_DVSQ		0x0070	/* b6-4: Device state */
+#define	  M66592_DS_SPD_CNFG	 0x0070		  /* Suspend Configured */
+#define	  M66592_DS_SPD_ADDR	 0x0060		  /* Suspend Address */
+#define	  M66592_DS_SPD_DFLT	 0x0050		  /* Suspend Default */
+#define	  M66592_DS_SPD_POWR	 0x0040		  /* Suspend Powered */
+#define	  M66592_DS_SUSP	 0x0040		  /* Suspend */
+#define	  M66592_DS_CNFG	 0x0030		  /* Configured */
+#define	  M66592_DS_ADDS	 0x0020		  /* Address */
+#define	  M66592_DS_DFLT	 0x0010		  /* Default */
+#define	  M66592_DS_POWR	 0x0000		  /* Powered */
+#define	M66592_DVSQS		0x0030	/* b5-4: Device state */
+#define	M66592_VALID		0x0008	/* b3: Setup packet detected flag */
+#define	M66592_CTSQ		0x0007	/* b2-0: Control transfer stage */
+#define	  M66592_CS_SQER	 0x0006		  /* Sequence error */
+#define	  M66592_CS_WRND	 0x0005		  /* Control write nodata status stage */
+#define	  M66592_CS_WRSS	 0x0004		  /* Control write status stage */
+#define	  M66592_CS_WRDS	 0x0003		  /* Control write data stage */
+#define	  M66592_CS_RDSS	 0x0002		  /* Control read status stage */
+#define	  M66592_CS_RDDS	 0x0001		  /* Control read data stage */
+#define	  M66592_CS_IDST	 0x0000		  /* Idle or setup stage */
+
+#define M66592_INTSTS1		0x42
+#define	M66592_BCHG		0x4000	/* b14: USB bus chenge interrupt */
+#define	M66592_DTCH		0x1000	/* b12: Detach sense interrupt */
+#define	M66592_SIGN		0x0020	/* b5: SETUP IGNORE interrupt */
+#define	M66592_SACK		0x0010	/* b4: SETUP ACK interrupt */
+
+#define M66592_FRMNUM		0x4C
+#define	M66592_OVRN		0x8000	/* b15: Overrun error */
+#define	M66592_CRCE		0x4000	/* b14: Received data error */
+#define	M66592_SOFRM		0x0800	/* b11: SOF output mode */
+#define	M66592_FRNM		0x07FF	/* b10-0: Frame number */
+
+#define M66592_UFRMNUM		0x4E
+#define	M66592_UFRNM		0x0007	/* b2-0: Micro frame number */
+
+#define M66592_RECOVER		0x50
+#define	M66592_STSRECOV		0x0700	/* Status recovery */
+#define	  M66592_STSR_HI	 0x0400		  /* FULL(0) or HI(1) Speed */
+#define	  M66592_STSR_DEFAULT	 0x0100		  /* Default state */
+#define	  M66592_STSR_ADDRESS	 0x0200		  /* Address state */
+#define	  M66592_STSR_CONFIG	 0x0300		  /* Configured state */
+#define	M66592_USBADDR		0x007F	/* b6-0: USB address */
+
+#define M66592_USBREQ			0x54
+#define	M66592_bRequest			0xFF00	/* b15-8: bRequest */
+#define	  M66592_GET_STATUS		 0x0000
+#define	  M66592_CLEAR_FEATURE		 0x0100
+#define	  M66592_ReqRESERVED		 0x0200
+#define	  M66592_SET_FEATURE		 0x0300
+#define	  M66592_ReqRESERVED1		 0x0400
+#define	  M66592_SET_ADDRESS		 0x0500
+#define	  M66592_GET_DESCRIPTOR		 0x0600
+#define	  M66592_SET_DESCRIPTOR		 0x0700
+#define	  M66592_GET_CONFIGURATION	 0x0800
+#define	  M66592_SET_CONFIGURATION	 0x0900
+#define	  M66592_GET_INTERFACE		 0x0A00
+#define	  M66592_SET_INTERFACE		 0x0B00
+#define	  M66592_SYNCH_FRAME		 0x0C00
+#define	M66592_bmRequestType		0x00FF	/* b7-0: bmRequestType */
+#define	M66592_bmRequestTypeDir		0x0080	/* b7  : Data transfer direction */
+#define	  M66592_HOST_TO_DEVICE		 0x0000
+#define	  M66592_DEVICE_TO_HOST		 0x0080
+#define	M66592_bmRequestTypeType	0x0060	/* b6-5: Type */
+#define	  M66592_STANDARD		 0x0000
+#define	  M66592_CLASS			 0x0020
+#define	  M66592_VENDOR			 0x0040
+#define	M66592_bmRequestTypeRecip	0x001F	/* b4-0: Recipient */
+#define	  M66592_DEVICE			 0x0000
+#define	  M66592_INTERFACE		 0x0001
+#define	  M66592_ENDPOINT		 0x0002
+
+#define M66592_USBVAL				0x56
+#define	M66592_wValue				0xFFFF	/* b15-0: wValue */
+/* Standard Feature Selector */
+#define	  M66592_ENDPOINT_HALT			0x0000
+#define	  M66592_DEVICE_REMOTE_WAKEUP		0x0001
+#define	  M66592_TEST_MODE			0x0002
+/* Descriptor Types */
+#define	M66592_DT_TYPE				0xFF00
+#define	M66592_GET_DT_TYPE(v)			(((v) & DT_TYPE) >> 8)
+#define	  M66592_DT_DEVICE			0x01
+#define	  M66592_DT_CONFIGURATION		0x02
+#define	  M66592_DT_STRING			0x03
+#define	  M66592_DT_INTERFACE			0x04
+#define	  M66592_DT_ENDPOINT			0x05
+#define	  M66592_DT_DEVICE_QUALIFIER		0x06
+#define	  M66592_DT_OTHER_SPEED_CONFIGURATION	0x07
+#define	  M66592_DT_INTERFACE_POWER		0x08
+#define	M66592_DT_INDEX				0x00FF
+#define	M66592_CONF_NUM				0x00FF
+#define	M66592_ALT_SET				0x00FF
+
+#define M66592_USBINDEX			0x58
+#define	M66592_wIndex			0xFFFF	/* b15-0: wIndex */
+#define	M66592_TEST_SELECT		0xFF00	/* b15-b8: Test Mode Selectors */
+#define	  M66592_TEST_J			 0x0100		  /* Test_J */
+#define	  M66592_TEST_K			 0x0200		  /* Test_K */
+#define	  M66592_TEST_SE0_NAK		 0x0300		  /* Test_SE0_NAK */
+#define	  M66592_TEST_PACKET		 0x0400		  /* Test_Packet */
+#define	  M66592_TEST_FORCE_ENABLE	 0x0500		  /* Test_Force_Enable */
+#define	  M66592_TEST_STSelectors	 0x0600		  /* Standard test selectors */
+#define	  M66592_TEST_Reserved		 0x4000		  /* Reserved */
+#define	  M66592_TEST_VSTModes		 0xC000		  /* Vendor-specific test modes */
+#define	M66592_EP_DIR			0x0080	/* b7: Endpoint Direction */
+#define	  M66592_EP_DIR_IN		 0x0080
+#define	  M66592_EP_DIR_OUT		 0x0000
+
+#define M66592_USBLENG		0x5A
+#define	M66592_wLength		0xFFFF	/* b15-0: wLength */
+
+#define M66592_DCPCFG		0x5C
+#define	M66592_CNTMD		0x0100	/* b8: Continuous transfer mode select */
+#define	M66592_DIR		0x0010	/* b4: Control transfer DIR select */
+
+#define M66592_DCPMAXP		0x5E
+#define	M66592_DEVSEL		0xC000	/* b15-14: Device address select */
+#define	  M66592_DEVICE_0	 0x0000		  /* Device address 0 */
+#define	  M66592_DEVICE_1	 0x4000		  /* Device address 1 */
+#define	  M66592_DEVICE_2	 0x8000		  /* Device address 2 */
+#define	  M66592_DEVICE_3	 0xC000		  /* Device address 3 */
+#define	M66592_MAXP		0x007F	/* b6-0: Maxpacket size of default control pipe */
+
+#define M66592_DCPCTR		0x60
+#define	M66592_BSTS		0x8000	/* b15: Buffer status */
+#define	M66592_SUREQ		0x4000	/* b14: Send USB request  */
+#define	M66592_SQCLR		0x0100	/* b8: Sequence toggle bit clear */
+#define	M66592_SQSET		0x0080	/* b7: Sequence toggle bit set */
+#define	M66592_SQMON		0x0040	/* b6: Sequence toggle bit monitor */
+#define	M66592_CCPL		0x0004	/* b2: Enable control transfer complete */
+#define	M66592_PID		0x0003	/* b1-0: Response PID */
+#define	  M66592_PID_STALL	 0x0002		  /* STALL */
+#define	  M66592_PID_BUF	 0x0001		  /* BUF */
+#define	  M66592_PID_NAK	 0x0000		  /* NAK */
+
+#define M66592_PIPESEL		0x64
+#define	M66592_PIPENM		0x0007	/* b2-0: Pipe select */
+#define	  M66592_PIPE0		 0x0000		  /* PIPE 0 */
+#define	  M66592_PIPE1		 0x0001		  /* PIPE 1 */
+#define	  M66592_PIPE2		 0x0002		  /* PIPE 2 */
+#define	  M66592_PIPE3		 0x0003		  /* PIPE 3 */
+#define	  M66592_PIPE4		 0x0004		  /* PIPE 4 */
+#define	  M66592_PIPE5		 0x0005		  /* PIPE 5 */
+#define	  M66592_PIPE6		 0x0006		  /* PIPE 6 */
+#define	  M66592_PIPE7		 0x0007		  /* PIPE 7 */
+
+#define M66592_PIPECFG		0x66
+#define	M66592_TYP		0xC000	/* b15-14: Transfer type */
+#define	  M66592_ISO		 0xC000		  /* Isochronous */
+#define	  M66592_INT		 0x8000		  /* Interrupt */
+#define	  M66592_BULK		 0x4000		  /* Bulk */
+#define	M66592_BFRE		0x0400	/* b10: Buffer ready interrupt mode select */
+#define	M66592_DBLB		0x0200	/* b9: Double buffer mode select */
+#define	M66592_CNTMD		0x0100	/* b8: Continuous transfer mode select */
+#define	M66592_SHTNAK		0x0080	/* b7: Transfer end NAK */
+#define	M66592_DIR		0x0010	/* b4: Transfer direction select */
+#define	  M66592_DIR_H_OUT	 0x0010		  /* HOST OUT */
+#define	  M66592_DIR_P_IN	 0x0010		  /* PERI IN */
+#define	  M66592_DIR_H_IN	 0x0000		  /* HOST IN */
+#define	  M66592_DIR_P_OUT	 0x0000		  /* PERI OUT */
+#define	M66592_EPNUM		0x000F	/* b3-0: Eendpoint number select */
+#define	  M66592_EP1		 0x0001
+#define	  M66592_EP2		 0x0002
+#define	  M66592_EP3		 0x0003
+#define	  M66592_EP4		 0x0004
+#define	  M66592_EP5		 0x0005
+#define	  M66592_EP6		 0x0006
+#define	  M66592_EP7		 0x0007
+#define	  M66592_EP8		 0x0008
+#define	  M66592_EP9		 0x0009
+#define	  M66592_EP10		 0x000A
+#define	  M66592_EP11		 0x000B
+#define	  M66592_EP12		 0x000C
+#define	  M66592_EP13		 0x000D
+#define	  M66592_EP14		 0x000E
+#define	  M66592_EP15		 0x000F
+
+#define M66592_PIPEBUF		0x68
+#define	M66592_BUFSIZE		0x7C00	/* b14-10: Pipe buffer size */
+#define	M66592_BUF_SIZE(x)	((((x) / 64) - 1) << 10)
+#define	M66592_BUFNMB		0x00FF	/* b7-0: Pipe buffer number */
+
+#define M66592_PIPEMAXP		0x6A
+#define	M66592_MXPS		0x07FF	/* b10-0: Maxpacket size */
+
+#define M66592_PIPEPERI		0x6C
+#define	M66592_IFIS		0x1000	/* b12: Isochronous in-buffer flush mode select */
+#define	M66592_IITV		0x0007	/* b2-0: Isochronous interval */
+
+#define M66592_PIPE1CTR		0x70
+#define M66592_PIPE2CTR		0x72
+#define M66592_PIPE3CTR		0x74
+#define M66592_PIPE4CTR		0x76
+#define M66592_PIPE5CTR		0x78
+#define M66592_PIPE6CTR		0x7A
+#define M66592_PIPE7CTR		0x7C
+#define	M66592_BSTS		0x8000	/* b15: Buffer status */
+#define	M66592_INBUFM		0x4000	/* b14: IN buffer monitor (Only for PIPE1 to 5) */
+#define	M66592_ACLRM		0x0200	/* b9: Out buffer auto clear mode */
+#define	M66592_SQCLR		0x0100	/* b8: Sequence toggle bit clear */
+#define	M66592_SQSET		0x0080	/* b7: Sequence toggle bit set */
+#define	M66592_SQMON		0x0040	/* b6: Sequence toggle bit monitor */
+#define	M66592_PID		0x0003	/* b1-0: Response PID */
+
+#define M66592_INVALID_REG	0x7E
+
+
+#define __iomem
+
+#define get_pipectr_addr(pipenum)	(M66592_PIPE1CTR + (pipenum - 1) * 2)
+
+#define M66592_MAX_SAMPLING	10
+
+#define M66592_MAX_NUM_PIPE	8
+#define M66592_MAX_NUM_BULK	3
+#define M66592_MAX_NUM_ISOC	2
+#define M66592_MAX_NUM_INT	2
+
+#define M66592_BASE_PIPENUM_BULK	3
+#define M66592_BASE_PIPENUM_ISOC	1
+#define M66592_BASE_PIPENUM_INT		6
+
+#define M66592_BASE_BUFNUM	6
+#define M66592_MAX_BUFNUM	0x4F
+
+struct m66592_pipe_info {
+	u16	pipe;
+	u16	epnum;
+	u16	maxpacket;
+	u16	type;
+	u16	interval;
+	u16	dir_in;
+};
+
+struct m66592_request {
+	struct usb_request	req;
+	struct list_head	queue;
+};
+
+struct m66592_ep {
+	struct usb_ep		ep;
+	struct m66592		*m66592;
+
+	struct list_head	queue;
+	unsigned 		busy:1;
+	unsigned		internal_ccpl:1;	/* use only control */
+
+	/* this member can able to after m66592_enable */
+	unsigned		use_dma:1;
+	u16			pipenum;
+	u16			type;
+	const struct usb_endpoint_descriptor	*desc;
+	/* register address */
+	unsigned long		fifoaddr;
+	unsigned long		fifosel;
+	unsigned long		fifoctr;
+	unsigned long		fifotrn;
+	unsigned long		pipectr;
+};
+
+struct m66592 {
+	spinlock_t		lock;
+	void __iomem		*reg;
+
+	struct usb_gadget		gadget;
+	struct usb_gadget_driver	*driver;
+
+	struct m66592_ep	ep[M66592_MAX_NUM_PIPE];
+	struct m66592_ep	*pipenum2ep[M66592_MAX_NUM_PIPE];
+	struct m66592_ep	*epaddr2ep[16];
+
+	struct usb_request	*ep0_req;	/* for internal request */
+	u16			*ep0_buf;	/* for internal request */
+
+	struct timer_list	timer;
+
+	u16			old_vbus;
+	int			scount;
+
+	int			old_dvsq;
+
+	/* pipe config */
+	int bulk;
+	int interrupt;
+	int isochronous;
+	int num_dma;
+	int bi_bufnum;	/* bulk and isochronous's bufnum */
+};
+
+#define gadget_to_m66592(_gadget) container_of(_gadget, struct m66592, gadget)
+#define m66592_to_gadget(m66592) (&m66592->gadget)
+
+#define is_bulk_pipe(pipenum)	\
+	((pipenum >= M66592_BASE_PIPENUM_BULK) && \
+	 (pipenum < (M66592_BASE_PIPENUM_BULK + M66592_MAX_NUM_BULK)))
+#define is_interrupt_pipe(pipenum)	\
+	((pipenum >= M66592_BASE_PIPENUM_INT) && \
+	 (pipenum < (M66592_BASE_PIPENUM_INT + M66592_MAX_NUM_INT)))
+#define is_isoc_pipe(pipenum)	\
+	((pipenum >= M66592_BASE_PIPENUM_ISOC) && \
+	 (pipenum < (M66592_BASE_PIPENUM_ISOC + M66592_MAX_NUM_ISOC)))
+
+#define enable_irq_ready(m66592, pipenum)	\
+	enable_pipe_irq(m66592, pipenum, M66592_BRDYENB)
+#define disable_irq_ready(m66592, pipenum)	\
+	disable_pipe_irq(m66592, pipenum, M66592_BRDYENB)
+#define enable_irq_empty(m66592, pipenum)	\
+	enable_pipe_irq(m66592, pipenum, M66592_BEMPENB)
+#define disable_irq_empty(m66592, pipenum)	\
+	disable_pipe_irq(m66592, pipenum, M66592_BEMPENB)
+#define enable_irq_nrdy(m66592, pipenum)	\
+	enable_pipe_irq(m66592, pipenum, M66592_NRDYENB)
+#define disable_irq_nrdy(m66592, pipenum)	\
+	disable_pipe_irq(m66592, pipenum, M66592_NRDYENB)
+
+/*-------------------------------------------------------------------------*/
+static inline u16 m66592_read(struct m66592 *m66592, unsigned long offset)
+{
+	return inw((unsigned long)m66592->reg + offset);
+}
+
+static inline void m66592_read_fifo(struct m66592 *m66592,
+				    unsigned long offset,
+				    void *buf, unsigned long len)
+{
+	unsigned long fifoaddr = (unsigned long)m66592->reg + offset;
+
+	len = (len + 1) / 2;
+	insw(fifoaddr, buf, len);
+}
+
+static inline void m66592_write(struct m66592 *m66592, u16 val,
+				unsigned long offset)
+{
+	outw(val, (unsigned long)m66592->reg + offset);
+}
+
+static inline void m66592_write_fifo(struct m66592 *m66592,
+				     unsigned long offset,
+				     void *buf, unsigned long len)
+{
+	unsigned long fifoaddr = (unsigned long)m66592->reg + offset;
+	unsigned long odd = len & 0x0001;
+
+	len = len / 2;
+	outsw(fifoaddr, buf, len);
+	if (odd) {
+		unsigned char *p = buf + len*2;
+		outb(*p, fifoaddr);
+	}
+}
+
+static inline void m66592_mdfy(struct m66592 *m66592, u16 val, u16 pat,
+			       unsigned long offset)
+{
+	u16 tmp;
+	tmp = m66592_read(m66592, offset);
+	tmp = tmp & (~pat);
+	tmp = tmp | val;
+	m66592_write(m66592, tmp, offset);
+}
+
+#define m66592_bclr(m66592, val, offset)	\
+			m66592_mdfy(m66592, 0, val, offset)
+#define m66592_bset(m66592, val, offset)	\
+			m66592_mdfy(m66592, val, 0, offset)
+
+#endif	/* ifndef __M66592_UDC_H__ */
+
+
-- 
1.5.2.2


-------------------------------------------------------------------------
This SF.net email is sponsored by DB2 Express
Download DB2 Express C - the FREE version of DB2 express and take
control of your XML. No limits. Just data. Click to get it now.
http://sourceforge.net/powerbar/db2/
_______________________________________________
[email protected]
To unsubscribe, use the last form field at:
https://lists.sourceforge.net/lists/listinfo/linux-usb-devel
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.