]> www.pilppa.org Git - linux-2.6-omap-h63xx.git/commitdiff
USB: m66592-udc: peripheral controller driver for M66592
authorYoshihiro Shimoda <shimoda.yoshihiro@renesas.com>
Thu, 10 May 2007 04:18:23 +0000 (13:18 +0900)
committerGreg Kroah-Hartman <gregkh@suse.de>
Thu, 12 Jul 2007 23:29:45 +0000 (16:29 -0700)
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 <shimoda.yoshihiro@renesas.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
drivers/usb/gadget/Kconfig
drivers/usb/gadget/Makefile
drivers/usb/gadget/ether.c
drivers/usb/gadget/gadget_chips.h
drivers/usb/gadget/m66592-udc.c [new file with mode: 0644]
drivers/usb/gadget/m66592-udc.h [new file with mode: 0644]

index f771a7cae9ece36ce0085f871beac7c38199ce13..05768889497b3efdba4788a048316ac2f22e9d59 100644 (file)
@@ -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
index 5db19396631c49732ea4c0f0de94683020d78bdc..2d41e849c9eea6b566c0545df4d188057c2b6585 100644 (file)
@@ -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
index 325bf7cfb83ffd04d76ab3cccf2d984f2618db8c..49630ab569bf5d207314d89231518ec58a917aeb 100644 (file)
@@ -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
+
 
 /*-------------------------------------------------------------------------*/
 
index d041b919e7b88ffd7f75f58e8ce7ee6040e2dd5f..96f50e35819f08f046c3fc98f3016df040489c27 100644 (file)
 #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 (file)
index 0000000..611130c
--- /dev/null
@@ -0,0 +1,1653 @@
+/*
+ * M66592 UDC (USB gadget)
+ *
+ * Copyright (C) 2006-2007 Renesas Solutions Corp.
+ *
+ * Author : Yoshihiro Shimoda <shimoda.yoshihiro@renesas.com>
+ *
+ * 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 (file)
index 0000000..26b54f8
--- /dev/null
@@ -0,0 +1,577 @@
+/*
+ * M66592 UDC (USB gadget)
+ *
+ * Copyright (C) 2006-2007 Renesas Solutions Corp.
+ *
+ * Author : Yoshihiro Shimoda <shimoda.yoshihiro@renesas.com>
+ *
+ * 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__ */
+
+