S3C HS/OtG USB UDC driver status

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Hello,

I would like to ask if anyone has tested the S3C HS/OtG USB UDC driver on S3C6410 based NCP board. I have been working on a driver for the same chip for the last month. My drive is based on the (broken) driver from Samsung Linux BSP and previous work posted on USB mailing list by Minkyu Kang.

Now I gave the S3C HS/OtG USB UDC driver a try. I managed to get it running here, but the connection is very unstable. I tested it mainly with g_ether Ethernet gadget driver in CDC mode. It fails to work correctly in RNDIS mode (device is not recognized by Windows host). The problems I observed on our NCP board:

1. connection hangs (at usb protocol level) if I open/close ethernet link ('ifconfig usb0 up; ifconfig usb0 down')

2. trying to transfer some amount of data also causes the connection to hang ('ping -s 1000 192.168.129.1' is enough to kill it here)

I also noticed that the driver works a bit better if debug messages are enabled.

I would like to know if such problems are general for S3C HS/OTG chip or specific only for our board.



I used S3C HS/OTG chip in Slave mode (without DMA transfers, because I did not managed to get them working at all in BSP driver). During my development I noticed that the s3c hs/otg chip is acting really strange sometimes:

1. it reports spurious 'packet completed' interrupts (even if no packet has been written to fifo since the last acknowledged packet completed interrupt, this happens usually when there is high traffic on usb line)

2. it fails to report interrupts in some cases (this happened usually when I wrote a next packet in the packet completed interrupt, writing a next packet in TX fifo empty interrupt solved this issue)

3. the most serious issue: I observed that the packets sent from the periodic fifos are often corrupted, only the non-periodic fifo transmits packets correctly. Adding a workaround for this issue is really hard, because packets are processed in the order they were written to fifo. So I used the same non-periodic FIFO for packets from 2 different IN end points the lockups occured. If 2 packets are queued and the token for the first has not arrived yet, the whole fifo is blocked - packets for other endpoint would not be transferred. This also concerns a replies to control request what is deadly for the whole protocol. I've tried to work around this by flushing non periodic fifo before processing control request. I know that this is a HACK, but had no other idea how to solve this.

4. I was unable to properly detect cable attach/detach with s3c otg chip interrupts. I also noticed that OTG chip does not init properly if the usb cable is not attached (I noticed that interrupt mask is not set - whatever I wrote I always read 0). I have used an external interrupt GPIO line that is connected to VCC in usb connector to detect that USB cable has been attached or detached and reinit/suspend driver in such case.

I've included my driver in hope it will be helpful in solving bugs in S3C HS/OtG USB UDC driver. I'm aware that my driver is not yet ready and does not match kernel style. It will also conflict with S3C HS/OtG USB UDC driver (because it uses old-style hardcoded virtual address mappings for S3C OTG registers). However this driver works quite well for us. I've tested it witn g_serial and g_ether drivers and both worked. I managed to trasfer several GB from and to NCP board with g_ether driver in both modes: CDC Ethernet (with Linux host) and RNDIS (WindowsXP host). I know that is also fails with g_cdc composite driver (there are still some unresolved issues with non-periodic tx fifo blocking/flushing if there are too many packets waiting in a queue).

The patch:

commit b40a12d208f9fdb0418524699597c3dc125d06bc
Author: Marek Szyprowski <m.szyprowski@xxxxxxxxxxx>
Date:   Wed Jun 24 11:10:16 2009 +0200

    [drivers] [USB] add driver for USB HS OTG gadget driver

diff --git a/arch/arm/mach-s3c6400/include/mach/map.h b/arch/arm/mach-s3c6400/include/mach/map.h
index e2544ea..e4150fe 100644
--- a/arch/arm/mach-s3c6400/include/mach/map.h
+++ b/arch/arm/mach-s3c6400/include/mach/map.h
@@ -71,4 +71,10 @@
 #define S3C_PA_IIC1            S3C64XX_PA_IIC1
 #define S3C_PA_FB              S3C64XX_PA_FB

+#define S3C64XX_VA_OTG         S3C_VA_OTG
+#define S3C64XX_PA_OTG         (0x7C000000)
+
+#define S3C64XX_VA_OTGSFR      S3C_VA_OTGSFR
+#define S3C64XX_PA_OTGSFR      (0x7C100000)
+
 #endif /* __ASM_ARCH_6400_MAP_H */
diff --git a/arch/arm/plat-s3c/include/plat/map-base.h b/arch/arm/plat-s3c/include/plat/map-base.h
index b84289d..fc45a25 100644
--- a/arch/arm/plat-s3c/include/plat/map-base.h
+++ b/arch/arm/plat-s3c/include/plat/map-base.h
@@ -36,5 +36,6 @@
 #define S3C_VA_TIMER   S3C_ADDR(0x00300000)    /* timer block */
 #define S3C_VA_WATCHDOG        S3C_ADDR(0x00400000)    /* watchdog */
 #define S3C_VA_UART    S3C_ADDR(0x01000000)    /* UART */
-
+#define S3C_VA_OTG     S3C_ADDR(0x03900000)    /* OTG */
+#define S3C_VA_OTGSFR  S3C_ADDR(0x03a00000)    /* OTGSFR */
 #endif /* __ASM_PLAT_MAP_H */
diff --git a/arch/arm/plat-s3c/include/plat/regs-usb-hs-otg.h b/arch/arm/plat-s3c/include/plat/regs-usb-hs-otg.h
new file mode 100755
index 0000000..8097fe1
--- /dev/null
+++ b/arch/arm/plat-s3c/include/plat/regs-usb-hs-otg.h
@@ -0,0 +1,387 @@
+/* linux/include/asm-arm/arch-s3c2410/regs-udc.h
+ *
+ * Copyright (C) 2008 Samsung Electronics
+ * Copyright (C) 2004 Herbert Poetzl <herbert@xxxxxxxxxxxx>
+ *
+ * This include file 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; either version 2 of
+ * the License, or (at your option) any later version.
+*/
+
+#ifndef __ASM_ARCH_REGS_USB_HS_OTG_H
+#define __ASM_ARCH_REGS_USB_HS_OTG_H
+
+/* USB2.0 OTG Controller register */
+#define S3C_USBOTG_PHYREG(x)           ((x) + S3C64XX_VA_OTGSFR)
+#define S3C_USBOTG_PHYPWR              S3C_USBOTG_PHYREG(0x0)
+#define S3C_USBOTG_PHYCLK              S3C_USBOTG_PHYREG(0x4)
+#define S3C_USBOTG_RSTCON              S3C_USBOTG_PHYREG(0x8)
+
+/* USB2.0 OTG Controller register */
+/* Core Global Registers */
+#define S3C_USBOTGREG(x)               ((x) + S3C64XX_VA_OTG)
+/* OTG Control & Status */
+#define S3C_UDC_OTG_GOTGCTL            S3C_USBOTGREG(0x000)
+/* OTG Interrupt */
+#define S3C_UDC_OTG_GOTGINT            S3C_USBOTGREG(0x004)
+/* Core AHB Configuration */
+#define S3C_UDC_OTG_GAHBCFG            S3C_USBOTGREG(0x008)
+/* Core USB Configuration */
+#define S3C_UDC_OTG_GUSBCFG            S3C_USBOTGREG(0x00C)
+/* Core Reset */
+#define S3C_UDC_OTG_GRSTCTL            S3C_USBOTGREG(0x010)
+/* Core Interrupt */
+#define S3C_UDC_OTG_GINTSTS            S3C_USBOTGREG(0x014)
+/* Core Interrupt Mask */
+#define S3C_UDC_OTG_GINTMSK            S3C_USBOTGREG(0x018)
+/* Receive Status Debug Read/Status Read */
+#define S3C_UDC_OTG_GRXSTSR            S3C_USBOTGREG(0x01C)
+/* Receive Status Debug Pop/Status Pop */
+#define S3C_UDC_OTG_GRXSTSP            S3C_USBOTGREG(0x020)
+/* Receive FIFO Size */
+#define S3C_UDC_OTG_GRXFSIZ            S3C_USBOTGREG(0x024)
+/* Non-Periodic Transmit FIFO Size */
+#define S3C_UDC_OTG_GNPTXFSIZ          S3C_USBOTGREG(0x028)
+/* Non-Periodic Transmit FIFO/Queue Status */
+#define S3C_UDC_OTG_GNPTXSTS           S3C_USBOTGREG(0x02C)
+
+/* Host Periodic Transmit FIFO Size */
+#define S3C_UDC_OTG_HPTXFSIZ           S3C_USBOTGREG(0x100)
+/* Device Periodic Transmit FIFO-1 Size */
+#define S3C_UDC_OTG_DPTXFSIZ1          S3C_USBOTGREG(0x104)
+/* Device Periodic Transmit FIFO-2 Size */
+#define S3C_UDC_OTG_DPTXFSIZ2          S3C_USBOTGREG(0x108)
+/* Device Periodic Transmit FIFO-3 Size */
+#define S3C_UDC_OTG_DPTXFSIZ3          S3C_USBOTGREG(0x10C)
+/* Device Periodic Transmit FIFO-4 Size */
+#define S3C_UDC_OTG_DPTXFSIZ4          S3C_USBOTGREG(0x110)
+/* Device Periodic Transmit FIFO-5 Size */
+#define S3C_UDC_OTG_DPTXFSIZ5          S3C_USBOTGREG(0x114)
+/* Device Periodic Transmit FIFO-6 Size */
+#define S3C_UDC_OTG_DPTXFSIZ6          S3C_USBOTGREG(0x118)
+/* Device Periodic Transmit FIFO-7 Size */
+#define S3C_UDC_OTG_DPTXFSIZ7          S3C_USBOTGREG(0x11C)
+/* Device Periodic Transmit FIFO-8 Size */
+#define S3C_UDC_OTG_DPTXFSIZ8          S3C_USBOTGREG(0x120)
+/* Device Periodic Transmit FIFO-9 Size */
+#define S3C_UDC_OTG_DPTXFSIZ9          S3C_USBOTGREG(0x124)
+/* Device Periodic Transmit FIFO-10 Size */
+#define S3C_UDC_OTG_DPTXFSIZ10         S3C_USBOTGREG(0x128)
+/* Device Periodic Transmit FIFO-11 Size */
+#define S3C_UDC_OTG_DPTXFSIZ11         S3C_USBOTGREG(0x12C)
+/* Device Periodic Transmit FIFO-12 Size */
+#define S3C_UDC_OTG_DPTXFSIZ12         S3C_USBOTGREG(0x130)
+/* Device Periodic Transmit FIFO-13 Size */
+#define S3C_UDC_OTG_DPTXFSIZ13         S3C_USBOTGREG(0x134)
+/* Device Periodic Transmit FIFO-14 Size */
+#define S3C_UDC_OTG_DPTXFSIZ14         S3C_USBOTGREG(0x138)
+/* Device Periodic Transmit FIFO-15 Size */
+#define S3C_UDC_OTG_DPTXFSIZ15         S3C_USBOTGREG(0x13C)
+
+/* Host Mode Registers
+ * Host Global Registers */
+/* Host Configuration */
+#define S3C_UDC_OTG_HCFG               S3C_USBOTGREG(0x400)
+/* Host Frame Interval */
+#define S3C_UDC_OTG_HFIR               S3C_USBOTGREG(0x404)
+/* Host Frame Number/Frame Time Remaining */
+#define S3C_UDC_OTG_HFNUM              S3C_USBOTGREG(0x408)
+/* Host Periodic Transmit FIFO/Queue Status */
+#define S3C_UDC_OTG_HPTXSTS            S3C_USBOTGREG(0x410)
+/* Host All Channels Interrupt */
+#define S3C_UDC_OTG_HAINT              S3C_USBOTGREG(0x414)
+/* Host All Channels Interrupt Mask */
+#define S3C_UDC_OTG_HAINTMSK           S3C_USBOTGREG(0x418)
+
+/* Host Port Control & Status Registers */
+#define S3C_UDC_OTG_HPRT               S3C_USBOTGREG(0x440)
+
+/* Host Channel-Specific Registers */
+/* Host Channel-0 Characteristics */
+#define  S3C_UDC_OTG_HCCHAR0           S3C_USBOTGREG(0x500)
+/* Host Channel-0 Split Control */
+#define  S3C_UDC_OTG_HCSPLT0           S3C_USBOTGREG(0x504)
+/* Host Channel-0 Interrupt */
+#define  S3C_UDC_OTG_HCINT0            S3C_USBOTGREG(0x508)
+/* Host Channel-0 Interrupt Mask */
+#define  S3C_UDC_OTG_HCINTMSK0         S3C_USBOTGREG(0x50C)
+/* Host Channel-0 Transfer Size */
+#define  S3C_UDC_OTG_HCTSIZ0           S3C_USBOTGREG(0x510)
+/* Host Channel-0 DMA Address */
+#define  S3C_UDC_OTG_HCDMA0            S3C_USBOTGREG(0x514)
+
+/* Device Mode Registers
+ * Device Global Registers */
+/* Device Configuration */
+#define  S3C_UDC_OTG_DCFG              S3C_USBOTGREG(0x800)
+/* Device Control */
+#define  S3C_UDC_OTG_DCTL              S3C_USBOTGREG(0x804)
+/* Device Status */
+#define  S3C_UDC_OTG_DSTS              S3C_USBOTGREG(0x808)
+/* Device IN Endpoint Common Interrupt Mask */
+#define  S3C_UDC_OTG_DIEPMSK           S3C_USBOTGREG(0x810)
+/* Device OUT Endpoint Common Interrupt Mask */
+#define  S3C_UDC_OTG_DOEPMSK           S3C_USBOTGREG(0x814)
+/* Device All Endpoints Interrupt */
+#define  S3C_UDC_OTG_DAINT             S3C_USBOTGREG(0x818)
+/* Device All Endpoints Interrupt Mask */
+#define  S3C_UDC_OTG_DAINTMSK          S3C_USBOTGREG(0x81C)
+/* Device IN Token Sequence Learning Queue Read 1 */
+#define  S3C_UDC_OTG_DTKNQR1           S3C_USBOTGREG(0x820)
+/* Device IN Token Sequence Learning Queue Read 2 */
+#define  S3C_UDC_OTG_DTKNQR2           S3C_USBOTGREG(0x824)
+/* Device VBUS Discharge Time */
+#define  S3C_UDC_OTG_DVBUSDIS          S3C_USBOTGREG(0x828)
+/* Device VBUS Pulsing Time */
+#define  S3C_UDC_OTG_DVBUSPULSE                S3C_USBOTGREG(0x82C)
+/* Device IN Token Sequence Learning Queue Read 3 */
+#define  S3C_UDC_OTG_DTKNQR3           S3C_USBOTGREG(0x830)
+/* Device IN Token Sequence Learning Queue Read 4 */
+#define  S3C_UDC_OTG_DTKNQR4           S3C_USBOTGREG(0x834)
+
+/* Device Logical IN Endpoint-Specific Registers */
+
+/* Device IN Endpoint x Control */
+#define  S3C_UDC_OTG_DIEPCTL(x)                (S3C_USBOTGREG(0x900) + ((x)<<5))
+/* Device IN Endpoint x Interrupt */
+#define  S3C_UDC_OTG_DIEPINT(x)                (S3C_USBOTGREG(0x908) + ((x)<<5))
+/* Device IN Endpoint x Transfer Size */
+#define  S3C_UDC_OTG_DIEPTSIZ(x)       (S3C_USBOTGREG(0x910) + ((x)<<5))
+/* Device IN Endpoint x DMA Address */
+#define  S3C_UDC_OTG_DIEPDMA(x)                (S3C_USBOTGREG(0x914) + ((x)<<5))
+
+/* Device IN Endpoint 0 Control */
+#define  S3C_UDC_OTG_DIEPCTL0          S3C_USBOTGREG(0x900)
+/* Device IN Endpoint 0 Interrupt */
+#define  S3C_UDC_OTG_DIEPINT0          S3C_USBOTGREG(0x908)
+/* Device IN Endpoint 0 Transfer Size */
+#define  S3C_UDC_OTG_DIEPTSIZ0         S3C_USBOTGREG(0x910)
+/* Device IN Endpoint 0 DMA Address */
+#define  S3C_UDC_OTG_DIEPDMA0          S3C_USBOTGREG(0x914)
+
+/* Device IN Endpoint 2 Control */
+#define  S3C_UDC_OTG_DIEPCTL2          S3C_USBOTGREG(0x940)
+/* Device IN Endpoint 2 Interrupt */
+#define  S3C_UDC_OTG_DIEPINT2          S3C_USBOTGREG(0x948)
+/* Device IN Endpoint 2 Transfer Size */
+#define  S3C_UDC_OTG_DIEPTSIZ2         S3C_USBOTGREG(0x950)
+/* Device IN Endpoint 2 DMA Address */
+#define  S3C_UDC_OTG_DIEPDMA2          S3C_USBOTGREG(0x954)
+
+/* Device IN Endpoint 3 Control */
+#define  S3C_UDC_OTG_DIEPCTL3          S3C_USBOTGREG(0x960)
+/* Device IN Endpoint 3 Interrupt */
+#define  S3C_UDC_OTG_DIEPINT3          S3C_USBOTGREG(0x968)
+/* Device IN Endpoint 3 Transfer Size */
+#define  S3C_UDC_OTG_DIEPTSIZ3         S3C_USBOTGREG(0x970)
+/* Device IN Endpoint 3 DMA Address */
+#define  S3C_UDC_OTG_DIEPDMA3          S3C_USBOTGREG(0x974)
+
+/* Device Logical OUT Endpoint-Specific Registers */
+
+/* Device OUT Endpoint x Control */
+#define  S3C_UDC_OTG_DOEPCTL(x)                (S3C_USBOTGREG(0xB00) + ((x)<<5))
+/* Device OUT Endpoint x Interrupt */
+#define  S3C_UDC_OTG_DOEPINT(x)                (S3C_USBOTGREG(0xB08) + ((x)<<5))
+/* Device OUT Endpoint x Transfer Size */
+#define  S3C_UDC_OTG_DOEPTSIZ(x)       (S3C_USBOTGREG(0xB10) + ((x)<<5))
+/* Device OUT Endpoint x DMA Address */
+#define  S3C_UDC_OTG_DOEPDMA(x)                (S3C_USBOTGREG(0xB14) + ((x)<<5))
+
+/* Device OUT Endpoint 0 Control */
+#define  S3C_UDC_OTG_DOEPCTL0          S3C_USBOTGREG(0xB00)
+/* Device OUT Endpoint 0 Interrupt */
+#define  S3C_UDC_OTG_DOEPINT0          S3C_USBOTGREG(0xB08)
+/* Device OUT Endpoint 0 Transfer Size */
+#define  S3C_UDC_OTG_DOEPTSIZ0         S3C_USBOTGREG(0xB10)
+/* Device OUT Endpoint 0 DMA Address */
+#define  S3C_UDC_OTG_DOEPDMA0          S3C_USBOTGREG(0xB14)
+
+/* Device OUT Endpoint 1 Control */
+#define  S3C_UDC_OTG_DOEPCTL1          S3C_USBOTGREG(0xB20)
+/* Device OUT Endpoint 1 Interrupt */
+#define  S3C_UDC_OTG_DOEPINT1          S3C_USBOTGREG(0xB28)
+/* Device OUT Endpoint 1 Transfer Size */
+#define  S3C_UDC_OTG_DOEPTSIZ1         S3C_USBOTGREG(0xB30)
+/* Device OUT Endpoint 1 DMA Address */
+#define  S3C_UDC_OTG_DOEPDMA1          S3C_USBOTGREG(0xB34)
+
+/* Endpoint FIFO address */
+#define S3C_UDC_OTG_EP0_FIFO           S3C_USBOTGREG(0x1000)
+#define S3C_UDC_OTG_EP1_FIFO           S3C_USBOTGREG(0x2000)
+#define S3C_UDC_OTG_EP2_FIFO           S3C_USBOTGREG(0x3000)
+#define S3C_UDC_OTG_EP3_FIFO           S3C_USBOTGREG(0x4000)
+#define S3C_UDC_OTG_EP4_FIFO           S3C_USBOTGREG(0x5000)
+#define S3C_UDC_OTG_EP5_FIFO           S3C_USBOTGREG(0x6000)
+#define S3C_UDC_OTG_EP6_FIFO           S3C_USBOTGREG(0x7000)
+#define S3C_UDC_OTG_EP7_FIFO           S3C_USBOTGREG(0x8000)
+#define S3C_UDC_OTG_EP8_FIFO           S3C_USBOTGREG(0x9000)
+#define S3C_UDC_OTG_EP9_FIFO           S3C_USBOTGREG(0xA000)
+#define S3C_UDC_OTG_EP10_FIFO          S3C_USBOTGREG(0xB000)
+#define S3C_UDC_OTG_EP11_FIFO          S3C_USBOTGREG(0xC000)
+#define S3C_UDC_OTG_EP12_FIFO          S3C_USBOTGREG(0xD000)
+#define S3C_UDC_OTG_EP13_FIFO          S3C_USBOTGREG(0xE000)
+#define S3C_UDC_OTG_EP14_FIFO          S3C_USBOTGREG(0xF000)
+#define S3C_UDC_OTG_EP15_FIFO          S3C_USBOTGREG(0x10000)
+
+/* S3C_USBOTG_PHYPWR */
+#define OTG_ENABLE             (0x0<<4)
+#define OTG_DISABLE            (0x1<<4)
+#define ANALOG_PWR_UP          (0x0<<3)
+#define ANALOG_PWR_DOWN                (0x1<<3)
+#define SUSPEND_DISABLE                (0x0<<0)
+#define SUSPEND_ENABLE         (0x1<<0)
+
+/* S3C_USBOTG_PHYCLK */
+#define REF_CLK_CRYSTAL                (0x0<<5)
+#define REF_CLK_OSCC           (0x1<<5)
+
+/* S3C_USBOTG_RSTCON */
+#define SW_RST_OFF             (0x0<<0)
+#define SW_RST_ON              (0x1<<0)
+
+/* S3C_UDC_OTG_GOTGCTL */
+#define B_SESSION_VALID                (0x1<<19)
+#define A_SESSION_VALID                (0x1<<18)
+
+/* S3C_UDC_OTG_GAHBCFG */
+#define PTXFE_HALF             (0x0<<8)
+#define PTXFE_ZERO             (0x1<<8)
+#define NPTXFE_HALF            (0x0<<7)
+#define NPTXFE_ZERO            (0x1<<7)
+#define MODE_SLAVE             (0x0<<5)
+#define MODE_DMA               (0x1<<5)
+#define BURST_SINGLE           (0x0<<1)
+#define BURST_INCR             (0x1<<1)
+#define BURST_INCR4            (0x3<<1)
+#define BURST_INCR8            (0x5<<1)
+#define BURST_INCR16           (0x7<<1)
+#define GBL_INT_UNMASK         (0x1<<0)
+#define GBL_INT_MASK           (0x0<<0)
+
+/* S3C_UDC_OTG_GUSBCFG */
+#define PHY_CLK_480M           (0x0<<15)
+#define PHY_CLK_48M            (0x1<<15)
+#define TXFIFO_RE_DIS          (0x0<<14)
+#define TXFIFO_RE_EN           (0x1<<14)
+#define TURN_AROUND            (0x5<<10)
+#define HNP_DISABLE            (0x0<<9)
+#define HNP_ENABLE             (0x1<<9)
+#define SRP_DISABLE            (0x0<<8)
+#define SRP_ENABLE             (0x1<<8)
+#define ULPI_DDR               (0x0<<7)
+#define HS_UTMI                (0x0<<6)
+#define INTERF_UTMI            (0x0<<4)
+#define INTERF_ULPI            (0x1<<4)
+#define PHY_INTERF_8   (0x0<<3)
+#define PHY_INTERF_16  (0x1<<3)
+#define TIME_OUT_CAL           (0x7<<0)
+
+/* S3C_UDC_OTG_GRSTCTL */
+#define AHB_MASTER_IDLE                (1u<<31)
+#define CORE_SOFT_RESET                (0x1<<0)
+#define CORE_TX_FIFO_FLUSH     (0x1<<5)
+
+/* S3C_UDC_OTG_GINTSTS/S3C_UDC_OTG_GINTMSK core interrupt register */
+#define INT_RESUME             (0x1<<31)
+#define INT_DISCONN            (0x1<<29)
+#define INT_CONN_CNG           (0x1<<28)
+#define INT_PTX_FIFO_EMPTY             (0x1<<26)
+#define INT_OUT_EP             (0x1<<19)
+#define INT_IN_EP              (0x1<<18)
+#define INT_ENUMDONE           (0x1<<13)
+#define INT_RESET              (0x1<<12)
+#define INT_SUSPEND            (0x1<<11)
+#define INT_EARLY_SUSPEND              (0x1<<10)
+#define INT_TX_FIFO_EMPTY              (0x1<<5)
+#define INT_RX_FIFO_NOT_EMPTY          (0x1<<4)
+#define INT_SOF                (0x1<<3)
+#define INT_DEV_MODE           (0x0<<0)
+#define INT_HOST_MODE          (0x1<<1)
+
+#define FULL_SPEED_CONTROL_PKT_SIZE            8
+#define FULL_SPEED_BULK_PKT_SIZE               64
+
+#define HIGH_SPEED_CONTROL_PKT_SIZE            64
+#define HIGH_SPEED_BULK_PKT_SIZE               512
+
+/* S3C_UDC_OTG_DSTS */
+#define RX_FIFO_SIZE           (2048<<0)
+#define NPTX_FIFO_START_ADDR           (RX_FIFO_SIZE<<0)
+#define NPTX_FIFO_SIZE         (2048<<16)
+#define PTX_FIFO_SIZE          (2048<<16)
+#define USB_HIGH_30_60MHZ              (0x0<<1)
+#define USB_FULL_30_60MHZ              (0x1<<1)
+#define USB_LOW_6MHZ           (0x2<<1)
+#define USB_FULL_48MHZ         (0x3<<1)
+
+/* S3C_UDC_OTG_GRXSTSP */
+#define BYTE_COUNT(x)          ((x & (0x7FF<<4)) >> 4)
+#define PKT_STS(x)             (x & (0xF<<17))
+#define EP_NUM(x)              (x & 0xF)
+#define OUT_PKT_RECEIVED               (0x2<<17)
+#define OUT_COMPLELTED         (0x3<<17)
+#define SETUP_COMPLETED                (0x4<<17)
+#define SETUP_PKT_RECEIVED             (0x6<<17)
+
+/* S3C_UDC_OTG_DCFG */
+#define EP_MIS_CNT(x)          (x<<18)
+#define DEVICE_ADDR(x)         (x<<4)
+#define SPEED_2_HIGH           (0x0<<0)
+#define SPEED_2_FULL           (0x1<<0)
+#define SPEED_1_LOW            (0x2<<0)
+#define SPEED_1_FULL           (0x3<<0)
+
+/* S3C_UDC_OTG_DCTL device control register */
+#define NORMAL_OPERATION               (0x1<<0)
+#define SOFT_DISCONNECT                (0x1<<1)
+
+/* S3C_UDC_OTG_DSTS */
+#define ENUM_SPEED(x)          (x & (0x3<<1))
+#define FRAME_CNT(x)           (x & (0x3ff<<8))
+
+/* S3C_UDC_OTG_DAINT device all endpoint interrupt register */
+#define S3C_UDC_INT_IN_EP(x)   (0x1<<(x))
+#define S3C_UDC_INT_OUT_EP(x)  (0x1<<(16 + (x)))
+
+/* S3C_UDC_OTG_DIEPCTL0/DOEPCTL0 device control
+   IN/OUT endpoint 0 control register */
+#define DEPCTL_EPENA           (0x1<<31)
+#define DEPCTL_EPDIS           (0x1<<30)
+#define DEPCTL_SNAK            (0x1<<27)
+#define DEPCTL_CNAK            (0x1<<26)
+#define DEPCTL_CTRL_TYPE               (0x0<<18)
+#define DEPCTL_ISO_TYPE                (0x1<<18)
+#define DEPCTL_BULK_TYPE               (0x2<<18)
+#define DEPCTL_INTR_TYPE               (0x3<<18)
+#define DEPCTL_USBACTEP                (0x1<<15)
+#define DEPCTL0_MPS_64         (0x0<<0)
+#define DEPCTL0_MPS_32         (0x1<<0)
+#define DEPCTL0_MPS_16         (0x2<<0)
+#define DEPCTL0_MPS_8          (0x3<<0)
+
+/* S3C_UDC_OTG_DIEPINTn */
+#define IN_EP_NAK_EFF          (0x1<<6)
+#define IN_TK_EPMIS            (0x1<<5)
+#define IN_TK_TXFEMP           (0x1<<4)
+#define IN_EP_TIMEOUT          (0x1<<3)
+
+/* S3C_UDC_OTG_DOEPINTn */
+#define BACK2BACK_SETUP                (0x1<<6)
+#define OUT_TK_EP_DIS          (0x1<<4)
+#define SETUP_PHASE_DONE               (0x1<<3)
+
+/* S3C_UDC_OTG_DIEPINTn/DOEPINTn */
+#define AHB_ERROR              (0x1<<2)
+#define EPDISBLD               (0x1<<1)
+#define TRANSFER_DONE          (0x1<<0)
+
+/* S3C_UDC_OTG_DIEPTSIZn */
+#define PKT_CNT(x)             (x<<19)
+#define XFERSIZE(x)            (x<<0)
+
+/* S3C_UDC_OTG_GNPTXSTS */
+#define GNPTXSTS_NPTXQSPCAVAIL (0xFF<<16)
+
+#endif
diff --git a/arch/arm/plat-s3c/include/plat/usb-hs-otg.h b/arch/arm/plat-s3c/include/plat/usb-hs-otg.h
new file mode 100644
index 0000000..004a90c
--- /dev/null
+++ b/arch/arm/plat-s3c/include/plat/usb-hs-otg.h
@@ -0,0 +1,35 @@
+/* linux/arch/arm/plat-s3c/include/plat/usb-hs-otg.h
+ *
+ * S3C - USB 2.0 HS OTG data definitions
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+*/
+
+#ifndef __PLAT_S3C_USB_HS_OTG_H
+#define __PLAT_S3C_USB_HS_OTG_H __FILE__
+
+/**
+ * struct s3c_usbgadger_platdata - USB HS OTG driver platform specific information
+ * @pinsetup: Setup the external GPIO pins to the right state to transfer
+ *             the data from the display system to the connected display
+ *             device.
+ * @phyclk:    Clock used by the driver.
+ *
+ */
+struct s3c_plat_otg_data {
+       int             phyclk;
+       void            (*pinsetup)(void);
+       int             gpio_usb_detect;
+};
+
+/**
+ * s3c_usbgadget_set_platdata() - Setup the USB HS OTG device with platform data.
+ * @pd: The platform data to set. The data is copied from the passed structure
+ *      so the machine data can mark the data __initdata so that any unused
+ *      machines will end up dumping their data at runtime.
+ */
+extern void s3c_usbgadget_set_platdata(struct s3c_plat_otg_data *pd);
+
+#endif /* __PLAT_S3C_USB_HS_OTG_H */
diff --git a/arch/arm/plat-s3c64xx/Kconfig b/arch/arm/plat-s3c64xx/Kconfig
index 54375a0..9fc2b7c 100644
--- a/arch/arm/plat-s3c64xx/Kconfig
+++ b/arch/arm/plat-s3c64xx/Kconfig
@@ -59,4 +59,9 @@ config S3C64XX_SETUP_FB_24BPP
        help
          Common setup code for S3C64XX with an 24bpp RGB display helper.

+config S3C64XX_USB_GADGET
+       bool
+       help
+         Common setup code for S3C64XX with an HS USB 2.0 OTG helper.
+
 endif
diff --git a/arch/arm/plat-s3c64xx/Makefile b/arch/arm/plat-s3c64xx/Makefile
index 3b7c752..779c819 100644
--- a/arch/arm/plat-s3c64xx/Makefile
+++ b/arch/arm/plat-s3c64xx/Makefile
@@ -34,6 +34,7 @@ obj-$(CONFIG_CPU_FREQ_S3C64XX)        += cpufreq.o

 # Device setup

+obj-$(CONFIG_S3C64XX_USB_GADGET) += dev-usbgadget.o
 obj-$(CONFIG_S3C64XX_SETUP_I2C0) += setup-i2c0.o
 obj-$(CONFIG_S3C64XX_SETUP_I2C1) += setup-i2c1.o
 obj-$(CONFIG_S3C64XX_SETUP_FB_24BPP) += setup-fb-24bpp.o
diff --git a/arch/arm/plat-s3c64xx/clock.c b/arch/arm/plat-s3c64xx/clock.c
index ad1b968..99d8edd 100644
--- a/arch/arm/plat-s3c64xx/clock.c
+++ b/arch/arm/plat-s3c64xx/clock.c
@@ -152,6 +152,12 @@ static struct clk init_clocks_disable[] = {
                .parent         = &clk_48m,
                .enable         = s3c64xx_sclk_ctrl,
                .ctrlbit        = S3C_CLKCON_SCLK_MMC2_48,
+       }, {
+               .name           = "otg",
+               .id             = -1,
+               .parent         = &clk_h,
+               .enable         = s3c64xx_hclk_ctrl,
+               .ctrlbit        = S3C_CLKCON_HCLK_USB,
        },
 };

diff --git a/arch/arm/plat-s3c64xx/cpu.c b/arch/arm/plat-s3c64xx/cpu.c
index cdefba3..ad76700 100644
--- a/arch/arm/plat-s3c64xx/cpu.c
+++ b/arch/arm/plat-s3c64xx/cpu.c
@@ -107,6 +107,16 @@ static struct map_desc s3c_iodesc[] __initdata = {
                .pfn            = __phys_to_pfn(S3C64XX_PA_MODEM),
                .length         = SZ_4K,
                .type           = MT_DEVICE,
+       }, {
+               .virtual        = (unsigned long)S3C64XX_VA_OTG,
+               .pfn            = __phys_to_pfn(S3C64XX_PA_OTG),
+               .length         = SZ_1M,
+               .type           = MT_DEVICE,
+       }, {
+               .virtual        = (unsigned long)S3C64XX_VA_OTGSFR,
+               .pfn            = __phys_to_pfn(S3C64XX_PA_OTGSFR),
+               .length         = SZ_1M,
+               .type           = MT_DEVICE,
        },
 };

diff --git a/arch/arm/plat-s3c64xx/dev-usbgadget.c b/arch/arm/plat-s3c64xx/dev-usbgadget.c
new file mode 100644
index 0000000..5d41c28
--- /dev/null
+++ b/arch/arm/plat-s3c64xx/dev-usbgadget.c
@@ -0,0 +1,50 @@
+/* Base S3C64XX usbgadget resource and device definitions */
+
+#include <linux/kernel.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <linux/ioport.h>
+
+#include <mach/map.h>
+#include <plat/map-base.h>
+#include <plat/devs.h>
+#include <plat/irqs.h>
+#include <plat/usb-hs-otg.h>
+
+static struct resource s3c_usbgadget_resource[] = {
+       [0] = {
+               .start = S3C64XX_PA_OTG,
+               .end   = S3C64XX_PA_OTG + SZ_1M  - 1,
+               .flags = IORESOURCE_MEM,
+       },
+       [1] = {
+               .start = IRQ_OTG,
+               .end   = IRQ_OTG,
+               .flags = IORESOURCE_IRQ,
+       }
+};
+
+struct platform_device s3c_device_usbgadget = {
+       .name             = "s3c-hs-usbgadget",
+       .id               = -1,
+       .num_resources    = ARRAY_SIZE(s3c_usbgadget_resource),
+       .resource         = s3c_usbgadget_resource,
+};
+
+void __init s3c_usbgadget_set_platdata(struct s3c_plat_otg_data *pd)
+{
+       struct s3c_plat_otg_data *npd;
+
+       if (!pd) {
+               printk(KERN_ERR "%s: no platform data\n", __func__);
+               return;
+       }
+
+       npd = kmemdup(pd, sizeof(struct s3c_plat_otg_data), GFP_KERNEL);
+       if (!npd)
+               printk(KERN_ERR "%s: no memory for platform data\n", __func__);
+
+       s3c_device_usbgadget.dev.platform_data = npd;
+}
+
+EXPORT_SYMBOL(s3c_device_usbgadget);
diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig
index 080bb1e..cb0bb84 100644
--- a/drivers/usb/gadget/Kconfig
+++ b/drivers/usb/gadget/Kconfig
@@ -293,6 +293,22 @@ config USB_S3C2410_DEBUG
        boolean "S3C2410 udc debug messages"
        depends on USB_GADGET_S3C2410

+config USB_GADGET_S3C_OTGD_HS
+       boolean "S3C high speed(2.0, dual-speed) USB OTG device"
+       depends on CPU_S3C6400 || CPU_S3C6410
+       select USB_GADGET_SELECTED
+       select USB_GADGET_DUALSPEED
+       help
+         Say "y" to link the driver statically, or "m" to build a
+         dynamically linked module called "s3c_hs_udc" and force all
+         gadget drivers to also be dynamically linked.
+
+config USB_S3C
+       tristate
+       depends on USB_GADGET_S3C_OTGD_HS
+       default USB_GADGET
+       select USB_GADGET_SELECTED
+
 #
 # Controllers available in both integrated and discrete versions
 #
diff --git a/drivers/usb/gadget/Makefile b/drivers/usb/gadget/Makefile
index 39a51d7..b653183 100644
--- a/drivers/usb/gadget/Makefile
+++ b/drivers/usb/gadget/Makefile
@@ -21,6 +21,7 @@ obj-$(CONFIG_USB_FSL_USB2)    += fsl_usb2_udc.o
 obj-$(CONFIG_USB_M66592)       += m66592-udc.o
 obj-$(CONFIG_USB_FSL_QE)       += fsl_qe_udc.o
 obj-$(CONFIG_USB_CI13XXX)      += ci13xxx_udc.o
+obj-$(CONFIG_USB_S3C)          += s3c_hs_udc.o

 #
 # USB gadget drivers
diff --git a/drivers/usb/gadget/gadget_chips.h b/drivers/usb/gadget/gadget_chips.h
index ec6d439..0301176 100644
--- a/drivers/usb/gadget/gadget_chips.h
+++ b/drivers/usb/gadget/gadget_chips.h
@@ -104,6 +104,12 @@
 #define gadget_is_s3c2410(g)    0
 #endif

+#ifdef CONFIG_USB_GADGET_S3C_OTGD_HS
+#define gadget_is_s3c64xx(g)    !strcmp("s3c_hs_udc", (g)->name)
+#else
+#define gadget_is_s3c64xx(g)    0
+#endif
+
 #ifdef CONFIG_USB_GADGET_AT91
 #define gadget_is_at91(g)      !strcmp("at91_udc", (g)->name)
 #else
@@ -231,6 +237,8 @@ static inline int usb_gadget_controller_number(struct usb_gadget *gadget)
                return 0x22;
        else if (gadget_is_ci13xxx(gadget))
                return 0x23;
+       else if (gadget_is_s3c64xx(gadget))
+               return 0x24;
        return -ENOENT;
 }

diff --git a/drivers/usb/gadget/s3c_hs_udc.c b/drivers/usb/gadget/s3c_hs_udc.c
new file mode 100644
index 0000000..7637c69
--- /dev/null
+++ b/drivers/usb/gadget/s3c_hs_udc.c
@@ -0,0 +1,2173 @@
+/*
+ * drivers/usb/gadget/s3c_hs_udc.c
+ * Samsung S3C on-chip full/high speed USB OTG 2.0 device controllers
+ *
+ * Copyright (C) 2008-2009 Samsung Electronics
+ * Minkyu Kang <mk7.kang@xxxxxxxxxxx>
+ * Marek Szyprowski <m.szyprowski@xxxxxxxxxxx>
+ *
+ * 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; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+#include "s3c_hs_udc.h"
+#include <linux/platform_device.h>
+#include <linux/clk.h>
+#include <mach/map.h>
+#include <plat/regs-clock.h>
+#include <plat/regs-usb-hs-otg.h>
+#include <plat/regs-sys.h>
+#include <plat/usb-hs-otg.h>
+#include <linux/gpio.h>
+
+static char *state_names[] = {
+       "WAIT_FOR_SETUP",
+       "DATA_STATE_XMIT",
+       "DATA_STATE_NEED_ZLP",
+       "WAIT_FOR_OUT_STATUS",
+       "DATA_STATE_RECV"
+};
+
+#define S3C_USB_DBG_LEVEL 3
+
+#define DBG(level, fmt, args...) do { \
+       if (level >= S3C_USB_DBG_LEVEL) { \
+               printk(KERN_INFO "[%s] " fmt, \
+                               __func__, ##args); \
+       } } while (0)
+
+
+#define        DRIVER_DESC "Samsung Dual-speed USB 2.0 OTG Device Controller"
+#define DRIVER_AUTHOR "Samsung Electronics"
+#define        DRIVER_VERSION "04 Dec 2008"
+
+struct s3c_udc *the_controller;
+
+static const char driver_name[] = "s3c_hs_udc";
+static const char driver_desc[] = DRIVER_DESC;
+static const char ep0name[] = "ep0-control";
+
+static u32 ep0_fifo_size = EP0_FIFO_SIZE;
+static u32 ep_fifo_size =  EP_FIFO_SIZE;
+static u32 ep_fifo_size2 = EP_FIFO_SIZE2;
+
+struct usb_ctrlrequest ctrl;
+static int reset_available = 1;
+static int tx_stopped;
+
+static int fifo_active;
+static int current_ep = NO_EP_ACTIVE;
+
+#ifdef CONFIG_USB_GADGET_DEBUG_FILES
+
+static const char proc_node_name[] = "driver/otg";
+
+static int
+udc_proc_read(char *page, char **start, off_t off, int count,
+             int *eof, void *_dev)
+{
+       char *buf = page;
+       struct s3c_udc *dev = _dev;
+       char *next = buf;
+       unsigned size = count;
+       unsigned long flags;
+       int t;
+
+       if (off != 0)
+               return 0;
+
+       local_irq_save(flags);
+
+       /* basic device status */
+       t = scnprintf(next, size,
+                     DRIVER_DESC "\n"
+                     "%s version: %s\n"
+                     "Gadget driver: %s\n"
+                     "\n",
+                     driver_name, DRIVER_VERSION,
+                     dev->driver ? dev->driver->driver.name : "(none)");
+       size -= t;
+       next += t;
+
+       local_irq_restore(flags);
+       *eof = 1;
+       return count - size;
+}
+
+#define create_proc_files() \
+       create_proc_read_entry(proc_node_name, 0, NULL, udc_proc_read, dev)
+#define remove_proc_files() \
+       remove_proc_entry(proc_node_name, NULL)
+
+#else  /* !CONFIG_USB_GADGET_DEBUG_FILES */
+
+#define create_proc_files() do {} while (0)
+#define remove_proc_files() do {} while (0)
+
+#endif /* CONFIG_USB_GADGET_DEBUG_FILES */
+
+
+static inline u32 s3c_otg_readl(u32 reg)
+{
+       return readl(reg);
+}
+
+static inline void s3c_otg_writel(u32 val, u32 reg, int update)
+{
+       u32 temp = 0;
+
+       if (update)
+               temp = readl(reg);
+
+       writel(val|temp, reg);
+}
+
+/*
+ *     retire a request
+ */
+static void s3c_otg_done(struct s3c_ep *ep, struct s3c_request *req, int status)
+{
+       unsigned int stopped = ep->stopped;
+
+       DBG(1, "%s %p, stopped = %d\n", ep->ep.name, ep, stopped);
+       list_del_init(&req->queue);
+
+       if (req->req.status == -EINPROGRESS)
+               req->req.status = status;
+       else
+               status = req->req.status;
+
+       if (status && (status != -ESHUTDOWN))
+               DBG(2, "complete %s stat %d len %u/%u\n",
+                       ep->ep.name, status, req->req.actual, req->req.length);
+
+       /* don't modify queue heads during completion callback */
+       ep->stopped = 1;
+
+       req->req.complete(&ep->ep, &req->req);
+
+       ep->stopped = stopped;
+}
+
+/*
+ *     dequeue ALL requests
+ */
+void s3c_otg_nuke(struct s3c_ep *ep, int status)
+{
+       struct s3c_request *req;
+
+       DBG(1, "%s %p\n", ep->ep.name, ep);
+
+       /* called with irqs blocked */
+       while (!list_empty(&ep->queue)) {
+               req = list_entry(ep->queue.next, struct s3c_request, queue);
+               s3c_otg_done(ep, req, status);
+       }
+}
+
+static void s3c_otg_ep_control(int ep, int dir, u32 val, int update)
+{
+       u32 epctrl;
+
+       if(dir)
+               epctrl = (u32)(S3C_UDC_OTG_DIEPCTL(ep));
+       else
+               epctrl = (u32)(S3C_UDC_OTG_DOEPCTL(ep));
+
+       s3c_otg_writel(val, epctrl, update);
+}
+
+static int s3c_otg_write_packet(struct s3c_ep *ep,
+               struct s3c_request *req, int max)
+{
+       u32 *buf;
+       int length;
+       int count;
+       u32 fifo = ep->fifo;
+       u32 epsize;
+
+       buf = req->req.buf + req->req.actual;
+       prefetch(buf);
+
+       length = req->req.length - req->req.actual;
+       length = min(length, max);
+       req->req.actual += length;
+
+       DBG(1, "%s: %d/%d, fifo=0x%x\n", ep->ep.name, length, max, fifo);
+
+       epsize = (u32)(S3C_UDC_OTG_DIEPTSIZ(ep_index(ep)));
+
+       s3c_otg_writel(PKT_CNT(0x1)|XFERSIZE(length), epsize, 0);
+       s3c_otg_ep_control(ep_index(ep), USB_DIR_IN,
+                       DEPCTL_EPENA|DEPCTL_CNAK, 1);
+
+       /* lenght in bytes */
+       for (count = 0; count < length; count += 4)
+               s3c_otg_writel(*buf++, fifo, 0);
+
+       return length;
+}
+
+static int s3c_otg_write_fifo_ep0(struct s3c_ep *ep, struct s3c_request *req)
+{
+       u32 max;
+       unsigned count;
+       int is_last;
+
+       max = ep_maxpacket(ep);
+       count = s3c_otg_write_packet(ep, req, max);
+
+       /* last packet is usually short (or a zlp) */
+       if (count != max) {
+               is_last = 1;
+       } else {
+               if ((req->req.length != req->req.actual) || req->req.zero)
+                       is_last = 0;
+               else
+                       is_last = 1;
+       }
+
+       DBG(2, "wrote %s %d bytes%s %d left %p\n",
+                       ep->ep.name, count,     is_last ? "/L" : "",
+                       req->req.length - req->req.actual, req);
+
+       /* requests complete when all IN data is in the FIFO */
+       return is_last;
+}
+
+static int s3c_otg_read_fifo_ep0(struct s3c_ep *ep, struct s3c_request *req)
+{
+       u32 csr;
+       u32 *buf;
+       unsigned bufferspace;
+       unsigned count;
+       unsigned is_short;
+       unsigned bytes;
+       u32 fifo = ep->fifo;
+
+       csr = s3c_otg_readl((u32)S3C_UDC_OTG_GRXSTSP);
+       bytes = BYTE_COUNT(csr);
+
+       buf = req->req.buf + req->req.actual;
+       prefetchw(buf);
+       bufferspace = req->req.length - req->req.actual;
+
+       /* read all bytes from this packet */
+       if (EP_NUM(csr) == 0) {
+               count = bytes / 4 + (bytes % 4 ? 1 : 0);
+               req->req.actual += min(bytes, bufferspace);
+       } else {
+               count = 0;
+               bytes = 0;
+       }
+
+       is_short = (bytes < ep->ep.maxpacket);
+
+       DBG(2, "read %s %d bytes%s %d/%d\n",
+                 ep->ep.name, bytes, is_short ? "/S" : "",
+                 req->req.actual, req->req.length);
+
+       while (count--) {
+               u32 byte = s3c_otg_readl(fifo);
+
+               if (unlikely(bufferspace == 0)) {
+                       /* this happens when the driver's buffer
+                        * is smaller than what the host sent.
+                        * discard the extra data.
+                        */
+                       if (req->req.status != -EOVERFLOW)
+                               DBG(3, "%s overflow %d\n", ep->ep.name, count);
+                       req->req.status = -EOVERFLOW;
+               } else {
+                       *buf++ = byte;
+                       bufferspace -= 4;
+               }
+       }
+
+       /* completion */
+       if (is_short || req->req.actual == req->req.length)
+               return 1;
+
+       return 0;
+}
+
+static int s3c_otg_write_ep0(struct s3c_udc *dev)
+{
+       struct s3c_request *req;
+       struct s3c_ep *ep = &dev->ep[0];
+       int ret;
+       int need_zlp = 0;
+
+       if (list_empty(&ep->queue))
+               req = NULL;
+       else
+               req = list_entry(ep->queue.next, struct s3c_request, queue);
+
+       if (!req) {
+               DBG(2, "NULL REQ\n");
+               return 0;
+       }
+
+       DBG(2, "length = 0x%x, actual = 0x%x\n",
+                       req->req.length, req->req.actual);
+
+       if (req->req.length == 0) {
+               dev->ep0state = WAIT_FOR_SETUP;
+               s3c_otg_done(ep, req, 0);
+               return 1;
+       }
+
+       /* Next write will end with the packet size, */
+       /* so we need Zero-length-packet */
+       if (req->req.length - req->req.actual == ep0_fifo_size)
+               need_zlp = 1;
+
+       ret = s3c_otg_write_fifo_ep0(ep, req);
+
+       if ((ret == 1) && !need_zlp) {
+               /* Last packet */
+               DBG(1, "finished, waiting for status\n");
+               dev->ep0state = WAIT_FOR_SETUP;
+       }
+
+       if (need_zlp) {
+               DBG(1, "Need ZLP!\n");
+               dev->ep0state = DATA_STATE_NEED_ZLP;
+       }
+
+       if (ret)
+               s3c_otg_done(ep, req, 0);
+
+       return ret;
+}
+
+static int first_time = 1;
+
+static int s3c_otg_read_ep0(struct s3c_udc *dev)
+{
+       struct s3c_request *req;
+       struct s3c_ep *ep = &dev->ep[0];
+       int ret;
+
+       if (!list_empty(&ep->queue))
+               req = list_entry(ep->queue.next, struct s3c_request, queue);
+       else {
+               DBG(3, "---> BUG\n");
+               BUG();
+               return 0;
+       }
+
+       DBG(2, "length = 0x%x, actual = 0x%x\n",
+                       req->req.length, req->req.actual);
+
+       if (req->req.length == 0) {
+               dev->ep0state = WAIT_FOR_SETUP;
+               first_time = 1;
+               s3c_otg_done(ep, req, 0);
+               return 1;
+       }
+
+       if (!req->req.actual && first_time) {
+               first_time = 0;
+               return 1;
+       }
+
+       ret = s3c_otg_read_fifo_ep0(ep, req);
+
+       if (ret)
+               s3c_otg_done(ep, req, 0);
+
+       dev->ep0state = WAIT_FOR_SETUP;
+       first_time = 1;
+
+       return ret;
+}
+
+static void s3c_otg_kick_ep0(struct s3c_udc *dev, struct s3c_ep *ep)
+{
+       int res = 0;
+
+       DBG(1, "ep_is_in = %d\n", ep_is_in(ep));
+
+       if (ep_is_in(ep)) {
+               dev->ep0state = DATA_STATE_XMIT;
+               while (!res)
+                       res = s3c_otg_write_ep0(dev);
+       } else {
+               s3c_otg_ep_control(0, USB_DIR_OUT, DEPCTL_CNAK, 1);
+               dev->ep0state = DATA_STATE_RECV;
+               s3c_otg_read_ep0(dev);
+       }
+}
+
+/*
+ * Write request to FIFO
+ */
+static int s3c_otg_write_fifo(struct s3c_ep *ep, struct s3c_request *req)
+{
+       u32 max;
+       unsigned count;
+       int is_last = 0;
+       int is_short = 0;
+
+       max = le16_to_cpu(ep->desc->wMaxPacketSize);
+       count = s3c_otg_write_packet(ep, req, max);
+
+       /* last packet is usually short (or a zlp) */
+       if (count != max) {
+               is_last = 1;
+               is_short = 1;
+       } else {
+               if ((req->req.length != req->req.actual) || req->req.zero)
+                       is_last = 0;
+               else
+                       is_last = 1;
+
+               /* interrupt/iso maxpacket may not fill the fifo */
+               is_short = (max < ep_maxpacket(ep));
+       }
+
+       DBG(2, "wrote %s %d bytes%s%s %d/%d\n",
+                       ep->ep.name, count,
+                       is_last ? "/L" : "", is_short ? "/S" : "",
+                       req->req.actual, req->req.length);
+
+       s3c_otg_writel(INT_TX_FIFO_EMPTY, (u32)S3C_UDC_OTG_GINTMSK, 1);
+       DBG(2, "INT_TX_FIFO_EMPTY enabled\n");
+
+       if (is_last)
+               req->pending = 0;
+
+       fifo_active = 1;
+       return 1;
+}
+
+static int s3c_otg_handle_completed(struct s3c_udc *dev, int num)
+{
+       struct s3c_request *req;
+       struct s3c_ep *ep = &dev->ep[num];
+
+       if (!fifo_active)
+       {
+               DBG(2, "Warning: got completed request when fifo is not active!\n");
+               return 0;
+       }
+
+       if (!list_empty(&ep->queue))
+               req = list_entry(ep->queue.next, struct s3c_request, queue);
+       else {
+               DBG(2, "BUG: no request for completed action! ep num %d\n", num);
+               return 0;
+       }
+
+       if (req->pending)
+       {
+               DBG(2, "packet not completed yet!\n");
+               /* Do not start writing to txfifo here, hardware will loose
+                  packet completition irq in such case. Let it be started
+                  from INT_TX_FIFO_EMPTY irq */
+       }
+       else
+       {
+               DBG(2, "packet completed succesfully\n");
+               s3c_otg_done(ep, req, 0);
+               fifo_active = 0;
+               current_ep = NO_EP_ACTIVE;
+       }
+       /* enable INT_TX_FIFO_EMPTY irq to send next packet from IN queue(s) */
+       s3c_otg_writel(INT_TX_FIFO_EMPTY, (u32)S3C_UDC_OTG_GINTMSK, 1);
+
+       return 0;
+}
+
+/*
+ * Read to request from FIFO (max read == bytes in fifo)
+ */
+static int s3c_otg_read_fifo(struct s3c_ep *ep, struct s3c_request *req)
+{
+       u32 csr;
+       u32 *buf;
+       unsigned bufferspace;
+       unsigned count;
+       unsigned is_short = 0;
+       unsigned bytes;
+       u32 fifo = ep->fifo;
+
+       csr = s3c_otg_readl((u32)S3C_UDC_OTG_GRXSTSP);
+       bytes = BYTE_COUNT(csr);
+
+       if (!bytes) {
+               DBG(2, "%d bytes\n", bytes);
+               s3c_otg_writel(INT_RX_FIFO_NOT_EMPTY, (u32)S3C_UDC_OTG_GINTMSK, 1);
+               DBG(2, "INT_RX_FIFO_NOT_EMPTY enabled\n");
+               return 0;
+       }
+
+       buf = req->req.buf + req->req.actual;
+       prefetchw(buf);
+       bufferspace = req->req.length - req->req.actual;
+
+       count = bytes / 4 + (bytes % 4 ? 1 : 0);
+       req->req.actual += min(bytes, bufferspace);
+
+       is_short = (bytes < ep->ep.maxpacket);
+
+       DBG(2, "read %s %d bytes%s %d/%d\n",
+                       ep->ep.name, bytes, is_short ? "/S" : "",
+                       req->req.actual, req->req.length);
+
+       while (count--) {
+               u32 byte = s3c_otg_readl(fifo);
+
+               if (unlikely(bufferspace == 0)) {
+                       /* this happens when the driver's buffer
+                        * is smaller than what the host sent.
+                        * discard the extra data.
+                        */
+                       if (req->req.status != -EOVERFLOW)
+                               DBG(3, "%s overflow %d\n", ep->ep.name, count);
+                       req->req.status = -EOVERFLOW;
+               } else {
+                       *buf++ = byte;
+                       bufferspace -= 4;
+               }
+       }
+
+       s3c_otg_writel(INT_RX_FIFO_NOT_EMPTY, (u32)S3C_UDC_OTG_GINTMSK, 1);
+       DBG(2, "INT_RX_FIFO_NOT_EMPTY enabled\n");
+
+       /* completion */
+       if (is_short || req->req.actual == req->req.length) {
+               s3c_otg_done(ep, req, 0);
+               return 1;
+       }
+
+       /* finished that packet.  the next one may be waiting... */
+       return 0;
+}
+
+static struct usb_request *s3c_otg_alloc_request(
+               struct usb_ep *ep, gfp_t gfp_flags)
+{
+       struct s3c_request *req;
+
+       if (!ep)
+               return NULL;
+
+       DBG(1, "%s %p\n", ep->name, ep);
+
+       req = kzalloc(sizeof *req, gfp_flags);
+       if (!req)
+               return NULL;
+
+       INIT_LIST_HEAD(&req->queue);
+
+       return &req->req;
+}
+
+static void s3c_otg_free_request(struct usb_ep *ep, struct usb_request *_req)
+{
+       struct s3c_request *req;
+
+       if (!ep)
+               return;
+
+       DBG(1, "%s %p\n", ep->name, ep);
+
+       if (!_req)
+               return;
+
+       req = container_of(_req, struct s3c_request, req);
+
+       WARN_ON(!list_empty(&req->queue));
+       kfree(req);
+}
+
+/*
+ * Queue one request
+ *  Kickstart transfer if needed
+ */
+static int s3c_otg_queue(struct usb_ep *_ep,
+               struct usb_request *_req, gfp_t gfp_flags)
+{
+       struct s3c_request *req;
+       struct s3c_ep *ep;
+       struct s3c_udc *dev;
+       unsigned long flags;
+       u32 csr;
+
+       req = container_of(_req, struct s3c_request, req);
+       if (!_req || !_req->complete || !_req->buf
+                       || !list_empty(&req->queue)) {
+               DBG(3, "bad params\n");
+               return -EINVAL;
+       }
+
+       ep = container_of(_ep, struct s3c_ep, ep);
+       if (!_ep || (!ep->desc && ep->ep.name != ep0name)) {
+               DBG(3, "bad ep\n");
+               return -EINVAL;
+       }
+
+       dev = ep->dev;
+       if (!dev->driver || dev->gadget.speed == USB_SPEED_UNKNOWN) {
+               DBG(3, "bogus device state %p\n", dev->driver);
+               return -ESHUTDOWN;
+       }
+
+       DBG(2, "%s queue req %p, len %d buf %p\n",
+                       _ep->name, _req, _req->length, _req->buf);
+
+       local_irq_save(flags);
+
+       _req->status = -EINPROGRESS;
+       _req->actual = 0;
+       req->pending = 1;
+
+       DBG(2, "ep=%d, Q empty=%d, stopped=%d, tx_stopped=%d\n",
+                       ep_index(ep), list_empty(&ep->queue), ep->stopped, tx_stopped);
+
+       /* kickstart this i/o queue? */
+       if (list_empty(&ep->queue) && !ep->stopped) {
+               if (ep_index(ep) == 0) {
+                       list_add_tail(&req->queue, &ep->queue);
+                       s3c_otg_kick_ep0(dev, ep);
+                       req = NULL;
+               } else if (ep_is_in(ep)) {
+                       if (!tx_stopped && !fifo_active) {
+                               csr = s3c_otg_readl((u32)S3C_UDC_OTG_GINTSTS);
+                               if ((csr & INT_TX_FIFO_EMPTY)){
+                                       s3c_otg_write_fifo(ep, req);
+                                       current_ep = ep_index(ep);
+                               }
+                       }
+               } else {
+                       s3c_otg_ep_control(ep_index(ep), USB_DIR_OUT, DEPCTL_CNAK, 1);
+                       /* OUT packets are queued and read from fifo only
+                          in INT_RX_FIFO_NOT_EMPTY isr */
+                       s3c_otg_writel(INT_RX_FIFO_NOT_EMPTY, (u32)S3C_UDC_OTG_GINTMSK, 1);
+                       DBG(2, "INT_RX_FIFO_NOT_EMPTY enabled\n");
+               }
+       }
+
+       /* pio or dma irq handler advances the queue. */
+       if (req)
+               list_add_tail(&req->queue, &ep->queue);
+
+       local_irq_restore(flags);
+
+       return 0;
+}
+
+/*
+ * dequeue JUST ONE request
+ */
+static int s3c_otg_dequeue(struct usb_ep *_ep, struct usb_request *_req)
+{
+       struct s3c_ep *ep;
+       struct s3c_request *req;
+       unsigned long flags;
+
+       ep = container_of(_ep, struct s3c_ep, ep);
+       if (!_ep || ep->ep.name == ep0name)
+               return -EINVAL;
+
+       local_irq_save(flags);
+
+       /* make sure it's actually queued on this endpoint */
+       list_for_each_entry(req, &ep->queue, queue) {
+               if (&req->req == _req)
+                       break;
+       }
+
+       if (&req->req != _req) {
+               local_irq_restore(flags);
+               return -EINVAL;
+       }
+
+       s3c_otg_done(ep, req, -ECONNRESET);
+
+       local_irq_restore(flags);
+
+       return 0;
+}
+
+static int s3c_otg_set_halt(struct usb_ep *_ep, int value)
+{
+       return 0;
+}
+
+static int s3c_otg_fifo_status(struct usb_ep *_ep)
+{
+       int count = 0;
+       struct s3c_ep *ep;
+
+       ep = container_of(_ep, struct s3c_ep, ep);
+       if (!_ep) {
+               DBG(3, "bad ep\n");
+               return -ENODEV;
+       }
+
+       /* LPD can't report unclaimed bytes from IN fifos */
+       if (ep_is_in(ep))
+               return -EOPNOTSUPP;
+
+       return count;
+}
+
+static void s3c_otg_fifo_flush(struct usb_ep *_ep)
+{
+       struct s3c_ep *ep;
+
+       ep = container_of(_ep, struct s3c_ep, ep);
+       if (unlikely(!_ep || (!ep->desc && ep->ep.name != ep0name))) {
+               DBG(3, "bad ep\n");
+               return;
+       }
+}
+
+static int s3c_otg_ep_enable(struct usb_ep *_ep,
+               const struct usb_endpoint_descriptor *desc)
+{
+       struct s3c_ep *ep;
+       struct s3c_udc *dev;
+       unsigned long flags;
+
+       ep = container_of(_ep, struct s3c_ep, ep);
+
+       ep->bEndpointAddress = desc->bEndpointAddress;
+       ep->bmAttributes = desc->bmAttributes;
+       ep->enabled = 1;
+
+       if(ep_is_in(ep)){
+               s3c_otg_ep_control(ep_index(ep), USB_DIR_IN, ep_fifo_size, 1);
+               if(desc->bmAttributes == USB_ENDPOINT_XFER_BULK){
+                       ep->ep_type = ep_bulk_in;
+                       s3c_otg_ep_control(ep_index(ep), USB_DIR_IN,
+                                       DEPCTL_BULK_TYPE|DEPCTL_USBACTEP, 1);
+               }
+               else if(desc->bmAttributes == USB_ENDPOINT_XFER_INT){
+                       ep->ep_type = ep_interrupt;
+                       s3c_otg_ep_control(ep_index(ep), USB_DIR_IN,
+                                       DEPCTL_INTR_TYPE|DEPCTL_USBACTEP, 1);
+               }
+               /* Unmask EPx interrupt */
+               s3c_otg_writel(S3C_UDC_INT_IN_EP(ep_index(ep)),
+                       (u32)S3C_UDC_OTG_DAINTMSK, 1);
+       }
+       else{
+               s3c_otg_ep_control(ep_index(ep), USB_DIR_OUT, ep_fifo_size, 1);
+               if(desc->bmAttributes == USB_ENDPOINT_XFER_BULK){
+                       ep->ep_type = ep_bulk_out;
+                       s3c_otg_ep_control(ep_index(ep), USB_DIR_OUT,
+                               DEPCTL_EPDIS|
+                               DEPCTL_BULK_TYPE|DEPCTL_USBACTEP, 1);
+               }
+               else if(desc->bmAttributes == USB_ENDPOINT_XFER_INT){
+                       ep->ep_type = ep_interrupt;
+                       s3c_otg_ep_control(ep_index(ep), USB_DIR_OUT,
+                               DEPCTL_EPDIS|
+                               DEPCTL_INTR_TYPE|DEPCTL_USBACTEP, 1);
+               }
+               /* Unmask EPx interrupt */
+               s3c_otg_writel(S3C_UDC_INT_OUT_EP(ep_index(ep)),
+                       (u32)S3C_UDC_OTG_DAINTMSK, 1);
+       }
+
+       if (!_ep || !desc || ep->desc || _ep->name == ep0name
+           || desc->bDescriptorType != USB_DT_ENDPOINT
+           || ep->bEndpointAddress != desc->bEndpointAddress
+           || ep_maxpacket(ep) < le16_to_cpu(desc->wMaxPacketSize)) {
+               DBG(3, "bad ep or descriptor\n");
+               return -EINVAL;
+       }
+
+       /* xfer types must match, except that interrupt ~= bulk */
+       if (ep->bmAttributes != desc->bmAttributes
+                       && ep->bmAttributes != USB_ENDPOINT_XFER_BULK
+                       && desc->bmAttributes != USB_ENDPOINT_XFER_INT) {
+               DBG(3, "%s type mismatch\n", _ep->name);
+               return -EINVAL;
+       }
+
+       /* hardware _could_ do smaller, but driver doesn't */
+       if ((desc->bmAttributes == USB_ENDPOINT_XFER_BULK
+                       && le16_to_cpu(desc->wMaxPacketSize)
+                       != ep_maxpacket(ep))
+                       || !desc->wMaxPacketSize) {
+               DBG(3, "bad %s maxpacket\n", _ep->name);
+               return -ERANGE;
+       }
+
+       dev = ep->dev;
+       if (!dev->driver || dev->gadget.speed == USB_SPEED_UNKNOWN) {
+               DBG(3, "bogus device state\n");
+               return -ESHUTDOWN;
+       }
+
+       local_irq_save(flags);
+
+       ep->stopped = 0;
+       ep->desc = desc;
+       ep->pio_irqs = 0;
+       ep->ep.maxpacket = le16_to_cpu(desc->wMaxPacketSize);
+
+       /* Reset halt state */
+       s3c_otg_set_halt(_ep, 0);
+
+       local_irq_restore(flags);
+
+       DBG(2, "enabled %s, stopped = %d, maxpacket = %d\n",
+               _ep->name, ep->stopped, ep->ep.maxpacket);
+
+       return 0;
+}
+
+static int s3c_otg_ep_disable(struct usb_ep *_ep)
+{
+       struct s3c_ep *ep;
+       unsigned long flags;
+       u32 epctrl;
+       u32 epctrlValue;
+       u32 epintrValue;
+       u32 ep_num;
+
+       ep = container_of(_ep, struct s3c_ep, ep);
+       if (!_ep || !ep->desc) {
+               DBG(3, "%s not enabled\n", _ep ? ep->ep.name : NULL);
+               return -EINVAL;
+       }
+
+       local_irq_save(flags);
+
+       /* Nuke all pending requests */
+       s3c_otg_nuke(ep, -ESHUTDOWN);
+
+       /* Set end point as not active and clear EPx interrupt mask bits */
+       ep_num = ep_index(ep);
+       epintrValue = s3c_otg_readl((u32)S3C_UDC_OTG_DAINTMSK);
+       if(ep_is_in(ep)){
+               epctrl = (u32)(S3C_UDC_OTG_DIEPCTL(ep_num));
+               epintrValue &= ~S3C_UDC_INT_IN_EP(ep_num);
+       }
+       else{
+               epctrl = (u32)(S3C_UDC_OTG_DOEPCTL(ep_num));
+               epintrValue &= ~S3C_UDC_INT_OUT_EP(ep_num);
+       }
+       s3c_otg_writel(epintrValue, (u32)S3C_UDC_OTG_DAINTMSK, 0);
+
+       epctrlValue = s3c_otg_readl(epctrl);
+       epctrlValue &= ~DEPCTL_USBACTEP;
+       s3c_otg_writel(epctrlValue, epctrl, 0);
+
+       ep->desc = 0;
+       ep->stopped = 1;
+       ep->enabled = 0;
+       ep->ep.maxpacket = EP_FIFO_SIZE;
+
+       local_irq_restore(flags);
+
+       DBG(2, "disabled %s\n", _ep->name);
+       return 0;
+}
+
+static struct usb_ep_ops s3c_ep_ops = {
+       .enable = s3c_otg_ep_enable,
+       .disable = s3c_otg_ep_disable,
+
+       .alloc_request = s3c_otg_alloc_request,
+       .free_request = s3c_otg_free_request,
+
+       .queue = s3c_otg_queue,
+       .dequeue = s3c_otg_dequeue,
+
+       .set_halt = s3c_otg_set_halt,
+       .fifo_status = s3c_otg_fifo_status,
+       .fifo_flush = s3c_otg_fifo_flush,
+};
+
+void s3c_otg_set_speed(struct s3c_udc *dev, enum usb_device_speed speed)
+{
+       u32 ep0_mps;
+       u8 i;
+
+       /* do not change the EP0 max size on speed change */
+       ep0_fifo_size = EP0_FIFO_SIZE;
+       ep0_mps = DEPCTL0_MPS_64;
+
+       if (speed == USB_SPEED_HIGH) {
+               ep_fifo_size =  EP_FIFO_SIZE;
+               ep_fifo_size2 = EP_FIFO_SIZE2;
+       } else {
+               ep_fifo_size = 64;
+               ep_fifo_size2 = 64;
+       }
+
+       dev->gadget.speed = speed;
+
+       dev->ep[0].ep.maxpacket = ep0_fifo_size;
+
+       for(i = 1; i < S3C_MAX_ENDPOINTS; i++){
+               dev->ep[i].ep.maxpacket = ep_fifo_size;
+       }
+
+       /* EP0 - Control */
+       s3c_otg_ep_control(0, USB_DIR_OUT, ep0_mps, 1);
+       s3c_otg_ep_control(0, USB_DIR_IN, ep0_mps, 1);
+
+       DBG(2, "%s Speed Detection\n",
+                       speed == USB_SPEED_HIGH ? "High" : "Full");
+}
+
+/*
+ * set the USB address for this device
+ *
+ * Called from control endpoint function
+ * after it decodes a set address setup packet.
+ */
+static void s3c_otg_set_address(struct s3c_udc *dev, unsigned char addr)
+{
+       u32 dcfg = s3c_otg_readl((u32)S3C_UDC_OTG_DCFG);
+       dcfg &= ~DEVICE_ADDR(0x7F);
+       s3c_otg_writel(DEVICE_ADDR(addr) | dcfg, (u32)S3C_UDC_OTG_DCFG, 0);
+
+       s3c_otg_ep_control(0, USB_DIR_IN, DEPCTL_EPENA|DEPCTL_CNAK, 1);
+
+       DBG(2, "USB OTG 2.0 Device Address=%d\n", addr);
+
+       dev->usb_address = addr;
+}
+
+static inline int s3c_otg_read_setup(struct s3c_ep *ep, u32 *ctrl, int max)
+{
+       int bytes;
+       int count;
+       u32 csr = s3c_otg_readl((u32)S3C_UDC_OTG_GRXSTSP);
+
+       bytes = BYTE_COUNT(csr);
+
+       /* 32 bits interface */
+       count = bytes / 4;
+
+       while (count--)
+               *ctrl++ = s3c_otg_readl((u32)S3C_UDC_OTG_EP0_FIFO);
+
+       return bytes;
+}
+
+/* An answer to a setup packet */
+static void s3c_otg_setup(struct s3c_udc *dev)
+{
+       struct s3c_ep *ep = &dev->ep[0];
+       int bytes;
+       int is_in;
+       int ret;
+
+       /* Nuke all previous transfers */
+       s3c_otg_nuke(ep, -EPROTO);
+
+       /* read control req from fifo (8 bytes) */
+       bytes = s3c_otg_read_setup(ep, (u32 *)&ctrl, 8);
+
+       DBG(2, "SETUP REQ %02x %02x %04x %04x %d\n",
+                       ctrl.bRequestType, ctrl.bRequest,
+                       ctrl.wValue, ctrl.wIndex, ctrl.wLength);
+
+       /* Set direction of EP0 */
+       if (ctrl.bRequestType & USB_DIR_IN) {
+               ep->bEndpointAddress |= USB_DIR_IN;
+               is_in = 1;
+       } else {
+               ep->bEndpointAddress &= ~USB_DIR_IN;
+               is_in = 0;
+       }
+
+       dev->req_pending = 1;
+
+       /* Handle some SETUP packets ourselves */
+       switch (ctrl.bRequest) {
+       case USB_REQ_SET_ADDRESS:
+               if (ctrl.bRequestType != (USB_TYPE_STANDARD|USB_RECIP_DEVICE))
+                       break;
+
+               s3c_otg_set_address(dev, ctrl.wValue);
+               return;
+
+       case USB_REQ_SET_INTERFACE:
+               DBG(2, "USB_REQ_SET_INTERFACE (%d)\n", ctrl.wValue);
+               /* FALLTHROUGH */
+
+       case USB_REQ_SET_CONFIGURATION:
+               DBG(2, "USB_REQ_SET_CONFIGURATION (%d)\n", ctrl.wValue);
+
+               s3c_otg_ep_control(0, USB_DIR_IN,
+                               DEPCTL_EPENA|DEPCTL_CNAK, 1);
+
+               reset_available = 1;
+               dev->req_config = 1;
+               break;
+
+       case USB_REQ_GET_DESCRIPTOR:
+               DBG(2, "USB_REQ_GET_DESCRIPTOR\n");
+               break;
+
+       case USB_REQ_GET_CONFIGURATION:
+               DBG(2, "USB_REQ_GET_CONFIGURATION\n");
+               break;
+
+       case USB_REQ_GET_STATUS:
+               DBG(2, "USB_REQ_GET_STATUS\n");
+               s3c_otg_ep_control(0, USB_DIR_IN, DEPCTL_EPENA|DEPCTL_CNAK, 1);
+               break;
+
+       case USB_REQ_CLEAR_FEATURE:
+               DBG(2, "USB_REQ_CLEAR_FEATURE\n");
+               break;
+
+       case USB_REQ_SET_FEATURE:
+               DBG(2, "USB_REQ_SET_FEATURE\n");
+               break;
+
+       default:
+               DBG(3, "Default of ctrl.bRequest=0x%x\n", ctrl.bRequest);
+               break;
+       }
+
+       if (dev->driver) {
+               /* device-2-host (IN) or no data setup command,
+                * process immediately */
+
+               DBG(1, "usb_ctrlrequest will be passed to fsg_setup()\n");
+
+               ret = dev->driver->setup(&dev->gadget, &ctrl);
+
+               if (ret < 0) {
+                       /* setup processing failed, force stall */
+                       DBG(3, "gadget setup FAILED (stalling) - %d\n", ret);
+                       dev->ep0state = WAIT_FOR_SETUP;
+               }
+       }
+
+       if (is_in == 0 && (ctrl.bRequestType & USB_RECIP_INTERFACE)) {
+               u32 epsize;
+               /* HACK: send ZLP to acknowledle OUT interface control request */
+
+               DBG(2, "sending ZLP to acknowledle OUT control request: %d\n", is_in);
+               epsize = (u32)(S3C_UDC_OTG_DIEPTSIZ(ep_index(ep)));
+               s3c_otg_writel(PKT_CNT(0x1)|XFERSIZE(0), epsize, 0);
+               s3c_otg_ep_control(0, USB_DIR_IN, DEPCTL_EPENA|DEPCTL_CNAK, 1);
+       }
+}
+
+/*
+ * handle ep0 interrupt
+ */
+static void s3c_otg_handle_ep0(struct s3c_udc *dev)
+{
+       if (dev->ep0state == WAIT_FOR_SETUP)
+               s3c_otg_setup(dev);
+       else
+               DBG(3, "strange state!! - %s\n", state_names[dev->ep0state]);
+}
+
+static void s3c_otg_handle_ep_out(struct s3c_udc *dev, u32 ep_num)
+{
+       struct s3c_ep *ep = &dev->ep[ep_num];
+       struct s3c_request *req;
+
+       if (unlikely(!(ep->desc))) {
+               /* Throw packet away.. */
+               DBG(3, "No descriptor?!?\n");
+               return;
+       }
+
+       if (list_empty(&ep->queue))
+               req = 0;
+       else
+               req = list_entry(ep->queue.next, struct s3c_request, queue);
+
+       if (unlikely(!req))
+               DBG(2, "NULL REQ on OUT EP-%d\n", ep_num);
+       else
+               s3c_otg_read_fifo(ep, req);
+}
+
+static int s3c_otg_handle_ep_in(struct s3c_udc *dev, u32 ep_num)
+{
+       struct s3c_ep *ep = &dev->ep[ep_num];
+       struct s3c_request *req;
+
+       if (list_empty(&ep->queue))
+               req = 0;
+       else
+               req = list_entry(ep->queue.next, struct s3c_request, queue);
+
+       if (unlikely(!req)) {
+               DBG(2, "NULL REQ on IN EP-%d\n", ep_num);
+               return 0;
+       } else if (req->pending) {
+               current_ep = ep_num;
+               return s3c_otg_write_fifo(ep, req);
+       }
+       return 0;
+}
+
+static int s3c_otg_handle_in(struct s3c_udc *dev)
+{
+       int write_done = 0;
+       struct s3c_ep *ep;
+       u8 i;
+
+       if (likely(!tx_stopped))
+       {
+               if(current_ep == NO_EP_ACTIVE){
+                       for(i = 1; i < S3C_MAX_ENDPOINTS; i++){
+                               ep = &dev->ep[i];
+                               if(ep_is_in(ep)){
+                                       if (write_done == 0 && ! list_empty(&ep->queue))
+                                               write_done = s3c_otg_handle_ep_in(dev, i);
+                               }
+                       }
+               }
+               else{
+                       ep = &dev->ep[current_ep];
+                       if(ep_is_in(ep)){
+                               if (write_done == 0 && ! list_empty(&ep->queue))
+                                       write_done = s3c_otg_handle_ep_in(dev, current_ep);
+                       }
+               }
+       }
+
+       if (!write_done)
+       {
+               u32 gintmsk = s3c_otg_readl((u32)S3C_UDC_OTG_GINTMSK);
+               DBG(2, "no requests, -> disabling INT_TX_FIFO_EMPTY\n");
+               s3c_otg_writel(gintmsk & (~INT_TX_FIFO_EMPTY),
+                               (u32)S3C_UDC_OTG_GINTMSK, 0);
+       }
+       return write_done;
+}
+
+
+static void s3c_otg_reset_txreq(struct s3c_udc *dev, u32 ep_num)
+{
+       struct s3c_ep *ep = &dev->ep[ep_num];
+       struct s3c_request *req;
+
+       if (list_empty(&ep->queue))
+               return;
+       else
+               req = list_entry(ep->queue.next, struct s3c_request, queue);
+
+       req->req.actual = 0;
+       req->pending = 1;
+       DBG(2, "ep%d, request %p reseted\n", ep_num, req);
+}
+
+static void s3c_otg_flush_txfifo(struct s3c_udc *dev)
+{
+       struct s3c_ep *ep;
+
+       if (current_ep != NO_EP_ACTIVE) {
+               /* Yet another ugly HACK: make sure that tx fifo
+                  WILL be ready for flush */
+               mdelay(1);
+
+               DBG(2, "WARNING: TX fifo already in use!!!\n");
+               DBG(2, "WARNING: flushing TX fifo\n");
+               s3c_otg_writel(CORE_TX_FIFO_FLUSH, (u32)S3C_UDC_OTG_GRSTCTL, 1);
+               while (s3c_otg_readl((u32)S3C_UDC_OTG_GRSTCTL) & (CORE_TX_FIFO_FLUSH))
+                       DBG(2, "waiting for fifo to flush\n");
+               DBG(2, "S3C_UDC_OTG_GNPTXSTS %08x\n", s3c_otg_readl((u32)S3C_UDC_OTG_GNPTXSTS));
+
+               ep = &dev->ep[current_ep];
+               if (ep_is_in(ep)) {
+                       if (! list_empty(&ep->queue))
+                               s3c_otg_reset_txreq(dev, current_ep);
+               }
+       }
+}
+
+static void s3c_otg_handle_ep(struct s3c_udc *dev)
+{
+       u32 csr;
+       u32 packet_status;
+       u32 ep_num;
+       u32 bytes = 0;
+
+       {
+       u32 gintmsk;
+       gintmsk = s3c_otg_readl((u32)S3C_UDC_OTG_GINTMSK);
+       gintmsk &= ~INT_RX_FIFO_NOT_EMPTY;
+       s3c_otg_writel(gintmsk, (u32)S3C_UDC_OTG_GINTMSK, 0);
+       DBG(2, "INT_RX_FIFO_NOT_EMPTY disabled\n");
+       }
+
+       csr = s3c_otg_readl((u32)S3C_UDC_OTG_GRXSTSR);
+
+       packet_status = PKT_STS(csr);
+       bytes = BYTE_COUNT(csr);
+       ep_num = EP_NUM(csr);
+
+       switch (packet_status) {
+       case SETUP_PKT_RECEIVED:
+               DBG(2, "SETUP received : %d bytes\n", bytes);
+               if (!bytes)
+                       break;
+
+               /* make sure that tx fifo is empty to correctly send SETUP packet reply */
+               tx_stopped = 1;
+               s3c_otg_flush_txfifo(dev);
+
+               s3c_otg_handle_ep0(dev);
+               s3c_otg_writel(INT_RX_FIFO_NOT_EMPTY, (u32)S3C_UDC_OTG_GINTMSK, 1);
+               DBG(2, "INT_RX_FIFO_NOT_EMPTY enabled\n");
+               break;
+
+       case OUT_PKT_RECEIVED:
+               if (!bytes)
+               {
+                       DBG(2, "ZERO OUT packet received\n");
+                       break;
+               }
+
+               if (ep_num == 0) {
+                       DBG(2, "CONTROL OUT received : %d bytes\n", bytes);
+
+                       dev->ep0state = DATA_STATE_RECV;
+                       s3c_otg_read_ep0(dev);
+
+                       s3c_otg_writel(INT_RX_FIFO_NOT_EMPTY, (u32)S3C_UDC_OTG_GINTMSK, 1);
+                       DBG(2, "INT_RX_FIFO_NOT_EMPTY enabled\n");
+
+               } else {
+                       DBG(2, " Bulk OUT or INT OUT received : %d bytes\n", bytes);
+
+                       s3c_otg_handle_ep_out(dev, ep_num);
+
+                       if (! list_empty(&dev->ep[ep_num].queue))
+                               s3c_otg_ep_control(ep_num, USB_DIR_OUT, DEPCTL_CNAK, 1);
+               }
+               break;
+
+       case SETUP_COMPLETED:
+               DBG(2, "SETUP_COMPLETED\n");
+               s3c_otg_ep_control(0, USB_DIR_OUT, DEPCTL_CNAK, 1);
+
+               /* send waiting IN packets */
+               tx_stopped = 0;
+               s3c_otg_handle_in(dev);
+               break;
+
+       case OUT_COMPLELTED:
+               DBG(2, "OUT_COMPLELTED - ep%d\n", ep_num);
+               s3c_otg_ep_control(ep_num, USB_DIR_OUT, DEPCTL_CNAK, 1);
+               break;
+
+       default:
+               s3c_otg_writel(INT_RX_FIFO_NOT_EMPTY, (u32)S3C_UDC_OTG_GINTMSK, 1);
+               DBG(2, "INT_RX_FIFO_NOT_EMPTY enabled\n");
+
+               DBG(2, "reserved packet received : %d bytes\n", bytes);
+               break;
+       }
+
+       if (!bytes) {
+               /* pop fifo */
+               csr = s3c_otg_readl((u32)S3C_UDC_OTG_GRXSTSP);
+               DBG(2, "RX Fifo poped\n");
+               s3c_otg_writel(INT_RX_FIFO_NOT_EMPTY, (u32)S3C_UDC_OTG_GINTMSK, 1);
+               DBG(2, "INT_RX_FIFO_NOT_EMPTY enabled\n");
+       }
+}
+
+/*
+ * disable USB device controller
+ */
+static void s3c_otg_disable(struct s3c_udc *dev)
+{
+       s3c_otg_set_address(dev, 0);
+
+       dev->ep0state = WAIT_FOR_SETUP;
+       dev->gadget.speed = USB_SPEED_UNKNOWN;
+       dev->usb_address = 0;
+
+       s3c_otg_writel(ANALOG_PWR_DOWN, (u32)S3C_USBOTG_PHYPWR, 1);
+       dev->usb_enabled = 0;
+}
+
+/*
+ * initialize software state
+ */
+static void s3c_otg_reinit(struct s3c_udc *dev)
+{
+       u32 i;
+
+       /* device/ep0 records init */
+       INIT_LIST_HEAD(&dev->gadget.ep_list);
+       INIT_LIST_HEAD(&dev->gadget.ep0->ep_list);
+       dev->ep0state = WAIT_FOR_SETUP;
+
+       /* basic endpoint records init */
+       for (i = 0; i < S3C_MAX_ENDPOINTS; i++) {
+               struct s3c_ep *ep = &dev->ep[i];
+
+               if (i != 0)
+                       list_add_tail(&ep->ep.ep_list, &dev->gadget.ep_list);
+
+               ep->desc = 0;
+               ep->stopped = 0;
+               INIT_LIST_HEAD(&ep->queue);
+               ep->pio_irqs = 0;
+       }
+}
+
+#define GUSBCFG_INIT   (PHY_CLK_480M|TXFIFO_RE_EN| \
+               TURN_AROUND|HNP_DISABLE|SRP_DISABLE|ULPI_DDR| \
+               HS_UTMI|INTERF_UTMI|PHY_INTERF_16|TIME_OUT_CAL)
+
+#define GINTMSK_INIT   (INT_RESUME|INT_ENUMDONE| \
+               INT_RESET|INT_SUSPEND|INT_RX_FIFO_NOT_EMPTY | INT_OUT_EP | INT_IN_EP)
+
+#define DOEPMSK_INIT   (AHB_ERROR | TRANSFER_DONE)
+
+#define DIEPMSK_INIT   (IN_EP_TIMEOUT|AHB_ERROR | TRANSFER_DONE)
+
+#define GAHBCFG_INIT   (PTXFE_HALF|NPTXFE_HALF| \
+               MODE_SLAVE|BURST_INCR16|GBL_INT_UNMASK)
+
+static void s3c_otg_config(void)
+{
+       u32 reg;
+       u8 i;
+
+       /* OTG USB configuration */
+       s3c_otg_writel(GUSBCFG_INIT, (u32)S3C_UDC_OTG_GUSBCFG, 0);
+
+       /* Soft-reset OTG Core and then unreset again */
+       s3c_otg_writel(CORE_SOFT_RESET, (u32)S3C_UDC_OTG_GRSTCTL, 0);
+
+       /* Put the OTG device core in the disconnected state */
+       s3c_otg_writel(SOFT_DISCONNECT, (u32)S3C_UDC_OTG_DCTL, 1);
+
+       udelay(20);
+
+       /* Make the OTG device core exit from the disconnected state */
+       reg = s3c_otg_readl((u32)S3C_UDC_OTG_DCTL);
+       s3c_otg_writel(reg & ~SOFT_DISCONNECT, (u32)S3C_UDC_OTG_DCTL, 0);
+
+       /* Configure OTG Core to initial settings of device mode */
+       s3c_otg_writel(EP_MIS_CNT(0x1)|SPEED_2_HIGH, (u32)S3C_UDC_OTG_DCFG, 0);
+
+       udelay(1000);
+
+       /* Set Rx FIFO Size */
+       s3c_otg_writel(RX_FIFO_SIZE, (u32)S3C_UDC_OTG_GRXFSIZ, 0);
+
+       /* Set Non Periodic Tx FIFO Size */
+       s3c_otg_writel(NPTX_FIFO_SIZE|NPTX_FIFO_START_ADDR, (u32)S3C_UDC_OTG_GNPTXFSIZ, 0);
+
+       s3c_otg_writel((768 << 16) | (2048*2+768*0), (u32)S3C_UDC_OTG_DPTXFSIZ1, 0);
+       s3c_otg_writel((768 << 16) | (2048*2+768*1), (u32)S3C_UDC_OTG_DPTXFSIZ2, 0);
+       s3c_otg_writel((768 << 16) | (2048*2+768*2), (u32)S3C_UDC_OTG_DPTXFSIZ3, 0);
+       s3c_otg_writel((768 << 16) | (2048*2+768*3), (u32)S3C_UDC_OTG_DPTXFSIZ4, 0);
+
+
+       /* FIFO status */
+       DBG(2, "Fifo status: RXFSIZ: %08x\n", readl(S3C_UDC_OTG_GRXFSIZ));
+       DBG(2, "Fifo status: NPTXFSIZ: %08x\n", readl(S3C_UDC_OTG_GNPTXFSIZ));
+       DBG(2, "Fifo status: DPTXFSIZ1: %08x\n", readl(S3C_UDC_OTG_DPTXFSIZ1));
+       DBG(2, "Fifo status: DPTXFSIZ2: %08x\n", readl(S3C_UDC_OTG_DPTXFSIZ2));
+       DBG(2, "Fifo status: DPTXFSIZ3: %08x\n", readl(S3C_UDC_OTG_DPTXFSIZ3));
+       DBG(2, "Fifo status: DPTXFSIZ4: %08x\n", readl(S3C_UDC_OTG_DPTXFSIZ4));
+
+
+       /* Unmask the core interrupts */
+       s3c_otg_writel(GINTMSK_INIT, (u32)S3C_UDC_OTG_GINTMSK, 0);
+
+
+       /* Set NAK bit of EP0, EP1, EP2 */
+       s3c_otg_ep_control(0, USB_DIR_OUT,
+                       DEPCTL_EPDIS|DEPCTL_SNAK|DEPCTL0_MPS_64, 0);
+       s3c_otg_ep_control(0, USB_DIR_IN,
+                       DEPCTL_EPDIS|DEPCTL_SNAK|DEPCTL0_MPS_64, 0);
+
+       for(i = 1; i < S3C_MAX_ENDPOINTS; i++){
+               s3c_otg_ep_control(i, USB_DIR_OUT,
+                       DEPCTL_EPDIS|DEPCTL_SNAK|DEPCTL_BULK_TYPE, 0);
+               s3c_otg_ep_control(i, USB_DIR_IN,
+                       DEPCTL_EPDIS|DEPCTL_SNAK|DEPCTL_BULK_TYPE, 0);
+       }
+
+       /* Unmask EP0 interrupt */
+       s3c_otg_writel(S3C_UDC_INT_IN_EP(0)|S3C_UDC_INT_OUT_EP(0),
+                       (u32)S3C_UDC_OTG_DAINTMSK, 0);
+
+       /* Unmask device OUT EP common interrupts */
+       s3c_otg_writel(DOEPMSK_INIT, (u32)S3C_UDC_OTG_DOEPMSK, 0);
+
+       /* Unmask device IN EP common interrupts */
+       s3c_otg_writel(DIEPMSK_INIT, (u32)S3C_UDC_OTG_DIEPMSK, 0);
+
+       /* Clear NAK bit of EP0 For Slave mode */
+       s3c_otg_ep_control(0, USB_DIR_OUT, DEPCTL_EPDIS|DEPCTL_CNAK, 0);
+
+       /* Initialize OTG Link Core */
+       s3c_otg_writel(GAHBCFG_INIT, (u32)S3C_UDC_OTG_GAHBCFG, 0);
+}
+
+static int s3c_otg_enable(struct s3c_udc *dev)
+{
+       /* USB_SIG_MASK */
+       s3c_otg_writel(S3C64XX_OTHERS_USBMASK, (u32)S3C64XX_OTHERS, 1);
+
+       /* Initializes OTG Phy. */
+       s3c_otg_writel(SUSPEND_DISABLE, (u32)S3C_USBOTG_PHYPWR, 0);
+       s3c_otg_writel(dev->phyclk, (u32)S3C_USBOTG_PHYCLK, 0);
+
+       s3c_otg_writel(SW_RST_ON, (u32)S3C_USBOTG_RSTCON, 0);
+       udelay(50);
+
+       s3c_otg_writel(SW_RST_OFF, (u32)S3C_USBOTG_RSTCON, 0);
+       udelay(50);
+
+       s3c_otg_config();
+
+       DBG(2, "S3C USB 2.0 OTG Controller Core Initialized\n");
+
+       dev->gadget.speed = USB_SPEED_UNKNOWN;
+       dev->usb_enabled = 1;
+
+       return 0;
+}
+
+/*
+ *     usb client interrupt handler.
+ */
+static irqreturn_t s3c_otg_irq(int irq, void *_dev)
+{
+       struct s3c_udc *dev = _dev;
+       u32 intr_status;
+       u32 usb_status;
+       u32 gintmsk;
+       unsigned long flags;
+
+       local_irq_save(flags);
+
+       intr_status = s3c_otg_readl((u32)S3C_UDC_OTG_GINTSTS);
+       gintmsk = s3c_otg_readl((u32)S3C_UDC_OTG_GINTMSK);
+
+       DBG(1, "\n");
+       DBG(1, "GINTSTS=0x%08x(on state %s), GINTMSK : 0x%08x\n",
+                       intr_status, state_names[dev->ep0state], gintmsk);
+
+       if (intr_status & INT_IN_EP) {
+               int i;
+               u32 daint = s3c_otg_readl((u32)S3C_UDC_OTG_DAINT);
+               u32 mask = s3c_otg_readl((u32)S3C_UDC_OTG_DIEPMSK);
+               DBG(2, "IN Endpoint int: %08x, in ep mask: %08x\n", daint, mask);
+
+               for (i=0; i<S3C_MAX_ENDPOINTS; i++)
+                       if(daint & S3C_UDC_INT_IN_EP(i))
+                       {
+                               u32 sts = s3c_otg_readl(((u32)S3C_UDC_OTG_DIEPINT0)+i*0x20);
+                               DBG(2, "\tep %d, DIEPINT: %08x\n", i, sts & mask);
+
+                               if (sts & 1) /* pkt complete */
+                               {
+                                       DBG(2, "\tep %d, packet completed\n", i);
+                                       if (i > 0)
+                                               s3c_otg_handle_completed(dev, i);
+                               }
+                               s3c_otg_writel(0, (u32)S3C_UDC_OTG_DIEPINT(i), 1);
+                       }
+       }
+
+       if (intr_status & INT_OUT_EP) {
+               int i;
+               u32 daint = s3c_otg_readl((u32)S3C_UDC_OTG_DAINT);
+               u32 mask = s3c_otg_readl((u32)S3C_UDC_OTG_DOEPMSK);
+               DBG(2, "OUT Endpoint int: %08x, out ep mask: %08x\n", daint, mask);
+
+               for (i=0; i<S3C_MAX_ENDPOINTS; i++)
+                       if(daint & S3C_UDC_INT_OUT_EP(i))
+                       {
+                               u32 sts = s3c_otg_readl((u32)S3C_UDC_OTG_DOEPINT(i));
+                               DBG(2, "\tep %d, DOEPINT %08x\n", i, sts);
+
+                               if (sts & 1)
+                                       DBG(2, "\tep %d, packet completed\n", i);
+
+                               s3c_otg_writel(0, (u32)S3C_UDC_OTG_DOEPINT(i), 1);
+                       }
+       }
+
+       if (intr_status & INT_ENUMDONE) {
+               DBG(2, "Speed Detection interrupt\n");
+               s3c_otg_writel(INT_ENUMDONE, (u32)S3C_UDC_OTG_GINTSTS, 0);
+
+               usb_status = ENUM_SPEED(s3c_otg_readl((u32)S3C_UDC_OTG_DSTS));
+
+               if (usb_status & (USB_FULL_30_60MHZ | USB_FULL_48MHZ))
+                       s3c_otg_set_speed(dev, USB_SPEED_FULL);
+               else
+                       s3c_otg_set_speed(dev, USB_SPEED_HIGH);
+       }
+
+       if (intr_status & INT_EARLY_SUSPEND) {
+               DBG(2, "Early suspend interrupt\n");
+               s3c_otg_writel(INT_EARLY_SUSPEND, (u32)S3C_UDC_OTG_GINTSTS, 0);
+       }
+
+       if (intr_status & INT_SUSPEND) {
+               DBG(2, "Suspend interrupt\n");
+               s3c_otg_writel(INT_SUSPEND, (u32)S3C_UDC_OTG_GINTSTS, 0);
+
+               if (dev->gadget.speed != USB_SPEED_UNKNOWN
+                       && dev->driver
+                       && dev->driver->suspend) {
+                       dev->driver->suspend(&dev->gadget);
+               }
+       }
+
+       if (intr_status & INT_RESUME) {
+               DBG(2, "Resume interrupt\n");
+               s3c_otg_writel(INT_RESUME, (u32)S3C_UDC_OTG_GINTSTS, 0);
+
+               if (dev->gadget.speed != USB_SPEED_UNKNOWN
+                       && dev->driver
+                       && dev->driver->resume) {
+                       dev->driver->resume(&dev->gadget);
+               }
+       }
+
+       if (intr_status & INT_RESET) {
+               DBG(2, "Reset interrupt\n");
+               s3c_otg_writel(INT_RESET, (u32)S3C_UDC_OTG_GINTSTS, 0);
+
+               usb_status = s3c_otg_readl((u32)S3C_UDC_OTG_GOTGCTL);
+
+               if (usb_status | (A_SESSION_VALID|B_SESSION_VALID)) {
+                       if (reset_available) {
+                               s3c_otg_config();
+                               dev->ep0state = WAIT_FOR_SETUP;
+                               reset_available = 0;
+                       }
+               } else {
+                       reset_available = 1;
+                       DBG(2, "RESET handling skipped\n");
+               }
+       }
+
+       if (intr_status & INT_RX_FIFO_NOT_EMPTY) {
+               DBG(2, "  ** INT_RX_FIFO_NOT_EMPTY\n");
+               s3c_otg_handle_ep(dev);
+       }
+
+
+       if ((intr_status & INT_TX_FIFO_EMPTY) && (gintmsk & INT_TX_FIFO_EMPTY)) {
+               DBG(2, "  ** INT_TX_FIFO_EMPTY\n");
+               s3c_otg_handle_in(dev);
+       }
+
+       local_irq_restore(flags);
+       return IRQ_HANDLED;
+}
+
+static void s3c_otg_stop_activity(struct s3c_udc *dev,
+               struct usb_gadget_driver *driver)
+{
+       int i;
+
+       /* don't disconnect drivers more than once */
+       if (dev->gadget.speed == USB_SPEED_UNKNOWN)
+               driver = 0;
+       dev->gadget.speed = USB_SPEED_UNKNOWN;
+
+       /* prevent new request submissions, kill any outstanding requests  */
+       for (i = 0; i < S3C_MAX_ENDPOINTS; i++) {
+               struct s3c_ep *ep = &dev->ep[i];
+               ep->stopped = 1;
+               s3c_otg_nuke(ep, -ESHUTDOWN);
+       }
+
+       /* report disconnect; the driver is already quiesced */
+       if (driver) {
+               driver->disconnect(&dev->gadget);
+       }
+
+       /* re-init driver-visible data structures */
+       s3c_otg_reinit(dev);
+}
+
+/*
+ *     usb detect interrupt handler.
+ */
+static irqreturn_t s3c_otg_usb_detect_irq(int irq, void *_dev)
+{
+       struct s3c_udc *dev = _dev;
+       int val;
+
+       mdelay(1);
+       val = gpio_get_value(dev->usb_detect_gpio);
+
+       if (val != dev->usb_detected) {
+
+               unsigned long flags;
+               local_irq_save(flags);
+
+               dev->usb_detected = val;
+
+               if (val) {
+                       DBG(3, "USB cable attached\n");
+                       if (dev->usb_enabled)
+                               s3c_otg_enable(dev);
+               } else {
+                       DBG(3, "USB cable detached\n");
+                       if (dev->usb_enabled && dev->driver) {
+                               s3c_otg_stop_activity(dev, dev->driver);
+                       }
+               }
+               local_irq_restore(flags);
+       }
+
+       return IRQ_HANDLED;
+}
+
+
+/*
+ * Register the gadget driver. Used by gadget drivers when
+ * registering themselves with the controller.
+ */
+int usb_gadget_register_driver(struct usb_gadget_driver *driver)
+{
+       struct s3c_udc *dev = the_controller;
+       int retval;
+       unsigned long flags;
+
+       if (!driver
+           || driver->speed != USB_SPEED_HIGH
+           || !driver->bind
+           || !driver->setup)
+               return -EINVAL;
+
+       if (!dev) {
+               DBG(3, "No device\n");
+               return -ENODEV;
+       }
+
+       spin_lock_irqsave(&dev->lock, flags);
+
+       if (dev->driver) {
+               DBG(3, "Already bound to %s\n", driver->driver.name);
+               return -EBUSY;
+       }
+
+       /* first hook up the driver ... */
+       dev->driver = driver;
+       dev->gadget.dev.driver = &driver->driver;
+       spin_unlock_irqrestore(&dev->lock, flags);
+
+       retval = device_add(&dev->gadget.dev);
+
+       if (retval) { /* TODO */
+               DBG(3, "target device_add failed, error %d\n", retval);
+               return retval;
+       }
+
+       retval = driver->bind(&dev->gadget);
+       if (retval) {
+               DBG(3, "%s: bind to driver %s --> error %d\n",
+                               dev->gadget.name, driver->driver.name, retval);
+               device_del(&dev->gadget.dev);
+
+               dev->driver = 0;
+               dev->gadget.dev.driver = 0;
+               return retval;
+       }
+
+       printk("Registered gadget driver '%s'\n", driver->driver.name);
+       s3c_otg_enable(dev);
+
+       enable_irq(IRQ_OTG);
+
+       return 0;
+}
+EXPORT_SYMBOL(usb_gadget_register_driver);
+
+/*
+  Unregister entry point for the peripheral controller driver.
+*/
+int usb_gadget_unregister_driver(struct usb_gadget_driver *driver)
+{
+       struct s3c_udc *dev = the_controller;
+       unsigned long flags;
+
+       if (!dev)
+               return -ENODEV;
+
+       if (!driver || driver != dev->driver)
+               return -EINVAL;
+
+       disable_irq(IRQ_OTG);
+
+       spin_lock_irqsave(&dev->lock, flags);
+
+       dev->driver = 0;
+       s3c_otg_stop_activity(dev, driver);
+
+       spin_unlock_irqrestore(&dev->lock, flags);
+
+       if (driver->unbind)
+               driver->unbind(&dev->gadget);
+
+       device_del(&dev->gadget.dev);
+
+       printk(KERN_INFO "Unregistered gadget driver '%s'\n",
+                       driver->driver.name);
+
+       s3c_otg_disable(dev);
+
+       return 0;
+}
+EXPORT_SYMBOL(usb_gadget_unregister_driver);
+
+/*
+ *     device-scoped parts of the api to the usb controller hardware
+ */
+static int s3c_otg_get_frame(struct usb_gadget *gadget)
+{
+       u32 frame = s3c_otg_readl((u32)S3C_UDC_OTG_DSTS);
+       return FRAME_CNT(frame);
+}
+
+static int s3c_otg_wakeup(struct usb_gadget *gadget)
+{
+       return -EOPNOTSUPP;
+}
+
+static int s3c_otg_set_selfpowered(
+               struct usb_gadget *gadget, int is_selfpowered)
+{
+       return -EOPNOTSUPP;
+}
+
+static int s3c_otg_pullup(struct usb_gadget *gadget, int is_on)
+{
+       return -EOPNOTSUPP;
+}
+
+static int s3c_otg_vbus_session(struct usb_gadget *gadget, int is_active)
+{
+       return -EOPNOTSUPP;
+}
+
+static int s3c_otg_vbus_draw(struct usb_gadget *gadget, unsigned mA)
+{
+       return -EOPNOTSUPP;
+}
+
+static const struct usb_gadget_ops s3c_udc_ops = {
+       .get_frame = s3c_otg_get_frame,
+       .wakeup = s3c_otg_wakeup,
+       .set_selfpowered = s3c_otg_set_selfpowered,
+       .vbus_session = s3c_otg_vbus_session,
+       .vbus_draw = s3c_otg_vbus_draw,
+       .pullup = s3c_otg_pullup,
+};
+
+static void nop_release(struct device *dev)
+{
+       DBG(2, "%s\n", dev_name(dev));
+}
+
+static struct s3c_udc memory = {
+       .usb_address = 0,
+       .gadget = {
+                  .ops = &s3c_udc_ops,
+                  .ep0 = &memory.ep[0].ep,
+                  .name = driver_name,
+                  .dev = {
+                          .init_name = "gadget",
+                          .release = nop_release,
+                          },
+                  },
+       .ep[0] = {
+                 .ep = {
+                        .name = ep0name,
+                        .ops = &s3c_ep_ops,
+                        .maxpacket = EP0_FIFO_SIZE,
+                        },
+                 .dev = &memory,
+
+                 .bEndpointAddress = 0,
+                 .bmAttributes = 0,
+
+                 .ep_type = ep_control,
+                 .fifo = (u32) S3C_UDC_OTG_EP0_FIFO,
+                 },
+       .ep[1] = {
+                 .ep = {
+                        .name = "ep1",
+                        .ops = &s3c_ep_ops,
+                        .maxpacket = EP_FIFO_SIZE,
+                        },
+                 .dev = &memory,
+
+                 .fifo = (u32) S3C_UDC_OTG_EP1_FIFO,
+                 },
+       .ep[2] = {
+                 .ep = {
+                        .name = "ep2",
+                        .ops = &s3c_ep_ops,
+                        .maxpacket = EP_FIFO_SIZE,
+                        },
+                 .dev = &memory,
+
+                 .fifo = (u32) S3C_UDC_OTG_EP2_FIFO,
+                 },
+
+       .ep[3] = {
+                 .ep = {
+                        .name = "ep3",
+                        .ops = &s3c_ep_ops,
+                        .maxpacket = EP_FIFO_SIZE,
+                        },
+                 .dev = &memory,
+
+                 .fifo = (u32) S3C_UDC_OTG_EP3_FIFO,
+                 },
+       .ep[4] = {
+                 .ep = {
+                        .name = "ep4",
+                        .ops = &s3c_ep_ops,
+                        .maxpacket = EP_FIFO_SIZE,
+                        },
+                 .dev = &memory,
+
+                 .fifo = (u32) S3C_UDC_OTG_EP4_FIFO,
+                 },
+       .ep[5] = {
+                 .ep = {
+                        .name = "ep5",
+                        .ops = &s3c_ep_ops,
+                        .maxpacket = EP_FIFO_SIZE,
+                        },
+                 .dev = &memory,
+
+                 .fifo = (u32) S3C_UDC_OTG_EP5_FIFO,
+                 },
+       .ep[6] = {
+                 .ep = {
+                        .name = "ep6",
+                        .ops = &s3c_ep_ops,
+                        .maxpacket = EP_FIFO_SIZE,
+                        },
+                 .dev = &memory,
+
+                 .fifo = (u32) S3C_UDC_OTG_EP6_FIFO,
+                 },
+       .ep[7] = {
+                 .ep = {
+                        .name = "ep7",
+                        .ops = &s3c_ep_ops,
+                        .maxpacket = EP_FIFO_SIZE,
+                        },
+                 .dev = &memory,
+
+                 .fifo = (u32) S3C_UDC_OTG_EP7_FIFO,
+                 },
+       .ep[8] = {
+                 .ep = {
+                        .name = "ep8",
+                        .ops = &s3c_ep_ops,
+                        .maxpacket = EP_FIFO_SIZE,
+                        },
+                 .dev = &memory,
+
+                 .fifo = (u32) S3C_UDC_OTG_EP8_FIFO,
+                 },
+       .ep[9] = {
+                 .ep = {
+                        .name = "ep9",
+                        .ops = &s3c_ep_ops,
+                        .maxpacket = EP_FIFO_SIZE,
+                        },
+                 .dev = &memory,
+
+                 .fifo = (u32) S3C_UDC_OTG_EP9_FIFO,
+                 },
+       .ep[10] = {
+                 .ep = {
+                        .name = "ep10",
+                        .ops = &s3c_ep_ops,
+                        .maxpacket = EP_FIFO_SIZE,
+                        },
+                 .dev = &memory,
+
+                 .fifo = (u32) S3C_UDC_OTG_EP10_FIFO,
+                 },
+       .ep[11] = {
+                 .ep = {
+                        .name = "ep11",
+                        .ops = &s3c_ep_ops,
+                        .maxpacket = EP_FIFO_SIZE,
+                        },
+                 .dev = &memory,
+
+                 .fifo = (u32) S3C_UDC_OTG_EP11_FIFO,
+                 },
+       .ep[12] = {
+                 .ep = {
+                        .name = "ep12",
+                        .ops = &s3c_ep_ops,
+                        .maxpacket = EP_FIFO_SIZE,
+                        },
+                 .dev = &memory,
+
+                 .fifo = (u32) S3C_UDC_OTG_EP12_FIFO,
+                 },
+       .ep[13] = {
+                 .ep = {
+                        .name = "ep13",
+                        .ops = &s3c_ep_ops,
+                        .maxpacket = EP_FIFO_SIZE,
+                        },
+                 .dev = &memory,
+
+                 .fifo = (u32) S3C_UDC_OTG_EP13_FIFO,
+                 },
+       .ep[14] = {
+                 .ep = {
+                        .name = "ep14",
+                        .ops = &s3c_ep_ops,
+                        .maxpacket = EP_FIFO_SIZE,
+                        },
+                 .dev = &memory,
+
+                 .fifo = (u32) S3C_UDC_OTG_EP14_FIFO,
+                 },
+       .ep[15] = {
+                 .ep = {
+                        .name = "ep15",
+                        .ops = &s3c_ep_ops,
+                        .maxpacket = EP_FIFO_SIZE,
+                        },
+                 .dev = &memory,
+
+                 .fifo = (u32) S3C_UDC_OTG_EP15_FIFO,
+                 },
+};
+
+static struct clk *otg_clock;
+
+/*
+ * binds to the platform device
+ */
+static int s3c_otg_probe(struct platform_device *pdev)
+{
+       struct s3c_udc *dev = &memory;
+       struct s3c_plat_otg_data *pdata = pdev->dev.platform_data;
+       int retval;
+
+       DBG(2, "%p\n", pdev);
+
+       spin_lock_init(&dev->lock);
+       dev->dev = pdev;
+
+       device_initialize(&dev->gadget.dev);
+       dev->gadget.dev.parent = &pdev->dev;
+
+       dev->gadget.is_dualspeed = 1;
+       dev->gadget.is_otg = 0;
+       dev->gadget.is_a_peripheral = 0;
+       dev->gadget.b_hnp_enable = 0;
+       dev->gadget.a_hnp_support = 0;
+       dev->gadget.a_alt_hnp_support = 0;
+
+       dev->phyclk = pdata->phyclk;
+
+       the_controller = dev;
+       platform_set_drvdata(pdev, dev);
+
+       otg_clock = clk_get(&pdev->dev, "otg");
+       if (otg_clock == NULL) {
+               DBG(3, "failed to find otg clock source\n");
+               return -ENOENT;
+       }
+       clk_enable(otg_clock);
+
+       if (pdata->pinsetup)
+           (pdata->pinsetup)();
+
+       s3c_otg_reinit(dev);
+
+       /* irq setup after old hardware state is cleaned up */
+       retval = request_irq(IRQ_OTG, s3c_otg_irq,
+                       IRQF_DISABLED, driver_name, dev);
+
+       if (retval != 0) {
+               DBG(3, "%s: can't get irq %i - %d\n",
+                               driver_name, IRQ_OTG, retval);
+               return -EBUSY;
+       }
+
+       if (pdata->gpio_usb_detect) {
+               int irq;
+
+               retval = gpio_request(pdata->gpio_usb_detect, "usb_detect");
+               if (retval < 0) {
+                       DBG(3, "%s: can't request gpio %d, error %d !!!\n",
+                               driver_name, pdata->gpio_usb_detect, retval);
+                       return -EBUSY;
+               }
+               retval = gpio_direction_input(pdata->gpio_usb_detect);
+               if (retval < 0) {
+                       DBG(3, "%s: can't configure gpio %d as input, error %d !!!\n",
+                               driver_name, pdata->gpio_usb_detect, retval);
+                       gpio_free(dev->usb_detect_gpio);
+                       return -EBUSY;
+               }
+               irq = gpio_to_irq(pdata->gpio_usb_detect);
+               if (irq < 0) {
+                       DBG(3, "%s: can't configure gpio %d as interrupt, error %d !!!\n",
+                               driver_name, pdata->gpio_usb_detect, retval);
+                       gpio_free(dev->usb_detect_gpio);
+                       return -EBUSY;
+               }
+
+               dev->usb_detect_irq = irq;
+               dev->usb_detect_gpio = pdata->gpio_usb_detect;
+
+               retval = request_irq(irq, s3c_otg_usb_detect_irq,
+                       IRQF_SHARED | IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, driver_name, dev);
+
+               if (retval != 0) {
+                       DBG(3, "%s: can't get usb detect irq - %d\n",
+                                       driver_name, retval);
+                       return -EBUSY;
+               }
+               dev->has_usb_detect = 1;
+               dev->usb_detected = gpio_get_value(dev->usb_detect_gpio);
+       }
+
+       disable_irq(IRQ_OTG);
+
+       create_proc_files();
+
+       return retval;
+}
+
+static int s3c_otg_remove(struct platform_device *pdev)
+{
+       struct s3c_udc *dev = platform_get_drvdata(pdev);
+
+       if (otg_clock != NULL) {
+               clk_disable(otg_clock);
+               clk_put(otg_clock);
+               otg_clock = NULL;
+       }
+
+       remove_proc_files();
+       usb_gadget_unregister_driver(dev->driver);
+
+       free_irq(IRQ_OTG, dev);
+
+       if (dev->has_usb_detect) {
+               free_irq(dev->usb_detect_irq, dev);
+               gpio_free(dev->usb_detect_gpio);
+       }
+
+       platform_set_drvdata(pdev, 0);
+
+       the_controller = 0;
+
+       return 0;
+}
+
+#ifdef CONFIG_PM
+static int s3c_otg_suspend(struct platform_device *pdev, pm_message_t state)
+{
+       struct s3c_udc *dev = the_controller;
+
+       if (dev->driver) {
+               disable_irq(IRQ_OTG);
+               s3c_otg_stop_activity(dev, dev->driver);
+               s3c_otg_disable(dev);
+               clk_disable(otg_clock);
+       }
+
+       return 0;
+}
+
+static int s3c_otg_resume(struct platform_device *pdev)
+{
+       struct s3c_udc *dev = the_controller;
+
+       if (dev->has_usb_detect)
+               dev->usb_detected = gpio_get_value(dev->usb_detect_gpio);
+
+       if (dev->driver) {
+               clk_enable(otg_clock);
+               s3c_otg_enable(dev);
+               s3c_otg_reinit(dev);
+               enable_irq(IRQ_OTG);
+       }
+
+       return 0;
+}
+#else
+#define s3c_otg_suspend NULL
+#define s3c_otg_resume  NULL
+#endif
+
+/*-------------------------------------------------------------------------*/
+static struct platform_driver s3c_otg_driver = {
+       .probe          = s3c_otg_probe,
+       .remove         = s3c_otg_remove,
+       .suspend        = s3c_otg_suspend,
+       .resume         = s3c_otg_resume,
+       .driver         = {
+               .owner  = THIS_MODULE,
+               .name   = "s3c-hs-usbgadget",
+       },
+};
+
+static int __init otg_init(void)
+{
+       int ret;
+
+       ret = platform_driver_register(&s3c_otg_driver);
+       if (!ret)
+               printk(KERN_INFO "Loaded %s version %s %s\n",
+                               driver_name, DRIVER_VERSION, "(Slave Mode)");
+
+       return ret;
+}
+
+static void __exit otg_exit(void)
+{
+       platform_driver_unregister(&s3c_otg_driver);
+       printk(KERN_INFO "Unloaded %s version %s\n",
+                       driver_name, DRIVER_VERSION);
+}
+
+module_init(otg_init);
+module_exit(otg_exit);
+
+MODULE_AUTHOR(DRIVER_AUTHOR);
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_VERSION(DRIVER_VERSION);
+MODULE_LICENSE("GPL");
diff --git a/drivers/usb/gadget/s3c_hs_udc.h b/drivers/usb/gadget/s3c_hs_udc.h
new file mode 100644
index 0000000..16ed0d7
--- /dev/null
+++ b/drivers/usb/gadget/s3c_hs_udc.h
@@ -0,0 +1,144 @@
+/*
+ * drivers/usb/gadget/s3c_hs_udc.h
+ * Samsung S3C on-chip full/high speed USB device controllers
+ *
+ * Copyright (C) 2008-2009 Samsung Electronics
+ * Minkyu Kang <mk7.kang@xxxxxxxxxxx>
+ * Marek Szyprowski <m.szyprowski@xxxxxxxxxxx>
+ *
+ * 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; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ */
+
+#ifndef __S3C_UDC_H
+#define __S3C_UDC_H
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/ioport.h>
+#include <linux/types.h>
+#include <linux/version.h>
+#include <linux/errno.h>
+#include <linux/delay.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/init.h>
+#include <linux/timer.h>
+#include <linux/list.h>
+#include <linux/interrupt.h>
+#include <linux/proc_fs.h>
+#include <linux/mm.h>
+#include <linux/device.h>
+#include <linux/dma-mapping.h>
+#include <asm/byteorder.h>
+#include <linux/io.h>
+#include <asm/dma.h>
+#include <asm/irq.h>
+#include <asm/system.h>
+#include <asm/unaligned.h>
+#include <mach/hardware.h>
+
+#include <linux/usb/ch9.h>
+#include <linux/usb/gadget.h>
+
+/* Max packet size */
+#if defined(CONFIG_USB_GADGET_S3C_FS)
+#define EP0_FIFO_SIZE          8
+#define EP_FIFO_SIZE           64
+#define S3C_MAX_ENDPOINTS      5
+#elif defined(CONFIG_USB_GADGET_S3C_HS)
+#define EP0_FIFO_SIZE          64
+#define EP_FIFO_SIZE           512
+#define EP_FIFO_SIZE2          1024
+#define S3C_MAX_ENDPOINTS      9
+#else
+#define EP0_FIFO_SIZE          64
+#define EP_FIFO_SIZE           512
+#define EP_FIFO_SIZE2          1024
+#define S3C_MAX_ENDPOINTS      16
+#endif
+
+#define WAIT_FOR_SETUP          0
+#define DATA_STATE_XMIT         1
+#define DATA_STATE_NEED_ZLP     2
+#define WAIT_FOR_OUT_STATUS     3
+#define DATA_STATE_RECV         4
+
+#define NO_EP_ACTIVE           -1
+
+enum ep_type {
+       ep_control, ep_bulk_in, ep_bulk_out, ep_interrupt
+};
+
+struct s3c_ep {
+       struct usb_ep ep;
+       struct s3c_udc *dev;
+
+       const struct usb_endpoint_descriptor *desc;
+       struct list_head queue;
+       unsigned long pio_irqs;
+
+       u8 stopped;
+       u8 bEndpointAddress;
+       u8 bmAttributes;
+
+       unsigned enabled : 1;
+
+       u32 ep_type;
+       u32 fifo;
+#ifdef CONFIG_USB_GADGET_S3C_FS
+       u32 csr1;
+       u32 csr2;
+#endif
+};
+
+struct s3c_request {
+       struct usb_request req;
+       struct list_head queue;
+       int pending;
+};
+
+struct s3c_udc {
+       struct usb_gadget gadget;
+       struct usb_gadget_driver *driver;
+       struct platform_device *dev;
+       spinlock_t lock;
+
+       int phyclk;
+       int ep0state;
+       struct s3c_ep ep[S3C_MAX_ENDPOINTS];
+
+       unsigned char usb_address;
+
+       unsigned req_pending:1;
+       unsigned req_std:1;
+       unsigned req_config:1;
+
+       unsigned int usb_detect_gpio;
+       unsigned int usb_detect_irq;
+
+       unsigned has_usb_detect:1;
+       unsigned usb_enabled:1;
+       unsigned usb_detected:1;
+};
+
+extern struct s3c_udc *the_controller;
+
+#define ep_is_in(EP)           (((EP)->bEndpointAddress & USB_DIR_IN) \
+                                                               == USB_DIR_IN)
+#define ep_index(EP)           ((EP)->bEndpointAddress & 0xF)
+#define ep_maxpacket(EP)       ((EP)->ep.maxpacket)
+
+#endif


Best regards
--
Marek Szyprowski
Samsung Poland R&D Center


--
To unsubscribe from this list: send the line "unsubscribe linux-usb" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html

[Index of Archives]     [Linux Media]     [Linux Input]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]     [Old Linux USB Devel Archive]

  Powered by Linux