All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH RESEND 0/4] USB: gadget: Add Samsung S3C24xx USB HS controller driver
@ 2011-03-23 21:35 ` Heiko Stübner
  0 siblings, 0 replies; 52+ messages in thread
From: Heiko Stübner @ 2011-03-23 21:35 UTC (permalink / raw)
  To: Ben Dooks, Thomas Abraham, Sangbeom Kim, Kukjin Kim, linux-arm-kernel

On 2010-10-13 Sangbeom Kim sent 4 patches to add support the USB High-Speed
device controller module on Samsung's S3C2416, S3C2443 and S3C2450. [1]

bdooks worte on 2010-10-22:
> patch 3/4 needs a bit of work, otherwise looks ok.
> I'll look at applying the s3c24xx items in the next day or so.

On 2010-10-23 Kukjin Kim send a revised version of patch 3.
It seems the patches were forgotten after this.

We resurrected them while working on a s3c2416 based device. Patch 3 needed an 
update to work with current kernels. The other 3 patches were not modified.

Tests, comments, etc very welcome.

Thanks
Heiko


[1] http://comments.gmane.org/gmane.linux.kernel.samsung-soc/3315
[2] http://comments.gmane.org/gmane.linux.kernel.samsung-soc/3469

Sangbeom Kim wrote on 2010-10-13:
> The Samsung's S3C2416, S3C2443 and S3C2450 includes a USB High-Speed
> device controller module. This driver enables support for USB high-speed
> gadget functionality for the Samsung S3C24xx SoC's that include this
> controller.

The patch series contains:
[PATCH 1/4] ARM: S3C2416: Add USB Phy register definitions
[PATCH 2/4] ARM: S3C24XX: Add plaform device definition for USB High-Speed 
gadget controller.
[PATCH v2 3/4] USB: Gadget: Add Samsung S3C24XX USB High-Speed controller 
driver
[PATCH 4/4] ARM: S3C2416: Add support for USB 2.0 High-Speed gadget controller

^ permalink raw reply	[flat|nested] 52+ messages in thread

* [PATCH RESEND 0/4] USB: gadget: Add Samsung S3C24xx USB HS controller driver
@ 2011-03-23 21:35 ` Heiko Stübner
  0 siblings, 0 replies; 52+ messages in thread
From: Heiko Stübner @ 2011-03-23 21:35 UTC (permalink / raw)
  To: linux-arm-kernel

On 2010-10-13 Sangbeom Kim sent 4 patches to add support the USB High-Speed
device controller module on Samsung's S3C2416, S3C2443 and S3C2450. [1]

bdooks worte on 2010-10-22:
> patch 3/4 needs a bit of work, otherwise looks ok.
> I'll look at applying the s3c24xx items in the next day or so.

On 2010-10-23 Kukjin Kim send a revised version of patch 3.
It seems the patches were forgotten after this.

We resurrected them while working on a s3c2416 based device. Patch 3 needed an 
update to work with current kernels. The other 3 patches were not modified.

Tests, comments, etc very welcome.

Thanks
Heiko


[1] http://comments.gmane.org/gmane.linux.kernel.samsung-soc/3315
[2] http://comments.gmane.org/gmane.linux.kernel.samsung-soc/3469

Sangbeom Kim wrote on 2010-10-13:
> The Samsung's S3C2416, S3C2443 and S3C2450 includes a USB High-Speed
> device controller module. This driver enables support for USB high-speed
> gadget functionality for the Samsung S3C24xx SoC's that include this
> controller.

The patch series contains:
[PATCH 1/4] ARM: S3C2416: Add USB Phy register definitions
[PATCH 2/4] ARM: S3C24XX: Add plaform device definition for USB High-Speed 
gadget controller.
[PATCH v2 3/4] USB: Gadget: Add Samsung S3C24XX USB High-Speed controller 
driver
[PATCH 4/4] ARM: S3C2416: Add support for USB 2.0 High-Speed gadget controller

^ permalink raw reply	[flat|nested] 52+ messages in thread

* [PATCH RESEND 1/4] ARM: S3C2416: Add USB Phy register definitions
  2011-03-23 21:35 ` Heiko Stübner
@ 2011-03-23 21:37   ` Heiko Stübner
  -1 siblings, 0 replies; 52+ messages in thread
From: Heiko Stübner @ 2011-03-23 21:37 UTC (permalink / raw)
  To: Ben Dooks
  Cc: Thomas Abraham, Sangbeom Kim, Kukjin Kim, linux-arm-kernel,
	linux-samsung-soc, linux-usb, dbrownell, Alexander Neumann

From: Thomas Abraham <thomas.ab@samsung.com>

Add register definitions required to configure the USB Phy. The definitions
for PHYCTRL, PHYPWR, URSTCON and UCLKCON registers and corresponding bit
field definitions are added.

Signed-off-by: Thomas Abraham <thomas.ab@samsung.com>
Signed-off-by: Sangbeom Kim <sbkim73@samsung.com>
---
 .../mach-s3c2410/include/mach/regs-s3c2443-clock.h |   25 ++++++++++++++++++++
 1 files changed, 25 insertions(+), 0 deletions(-)

diff --git a/arch/arm/mach-s3c2410/include/mach/regs-s3c2443-clock.h b/arch/arm/mach-s3c2410/include/mach/regs-s3c2443-clock.h
index 44494a5..5e06c72 100644
--- a/arch/arm/mach-s3c2410/include/mach/regs-s3c2443-clock.h
+++ b/arch/arm/mach-s3c2410/include/mach/regs-s3c2443-clock.h
@@ -37,6 +37,10 @@
 #define S3C2443_SYSID			S3C2443_CLKREG(0x5C)
 #define S3C2443_PWRCFG			S3C2443_CLKREG(0x60)
 #define S3C2443_RSTCON			S3C2443_CLKREG(0x64)
+#define S3C2443_PHYCTRL			S3C2443_CLKREG(0x80)
+#define S3C2443_PHYPWR			S3C2443_CLKREG(0x84)
+#define S3C2443_URSTCON			S3C2443_CLKREG(0x88)
+#define S3C2443_UCLKCON			S3C2443_CLKREG(0x8C)
 
 #define S3C2443_SWRST_RESET		(0x533c2443)
 
@@ -121,6 +125,27 @@
 
 #define S3C2443_PWRCFG_SLEEP		(1<<15)
 
+#define S3C2443_PWRCFG_USBPHY		(1 << 4)
+
+#define S3C2443_URSTCON_FUNCRST		(1 << 2)
+#define S3C2443_URSTCON_PHYRST		(1 << 0)
+
+#define S3C2443_PHYCTRL_CLKSEL		(1 << 3)
+#define S3C2443_PHYCTRL_EXTCLK		(1 << 2)
+#define S3C2443_PHYCTRL_PLLSEL		(1 << 1)
+#define S3C2443_PHYCTRL_DSPORT		(1 << 0)
+
+#define S3C2443_PHYPWR_COMMON_ON	(1 << 31)
+#define S3C2443_PHYPWR_ANALOG_PD	(1 << 4)
+#define S3C2443_PHYPWR_PLL_REFCLK	(1 << 3)
+#define S3C2443_PHYPWR_XO_ON		(1 << 2)
+#define S3C2443_PHYPWR_PLL_PWRDN	(1 << 1)
+#define S3C2443_PHYPWR_FSUSPEND		(1 << 0)
+
+#define S3C2443_UCLKCON_DETECT_VBUS	(1 << 31)
+#define S3C2443_UCLKCON_FUNC_CLKEN	(1 << 2)
+#define S3C2443_UCLKCON_TCLKEN		(1 << 0)
+
 #include <asm/div64.h>
 
 static inline unsigned int
-- 
1.7.2.3

^ permalink raw reply related	[flat|nested] 52+ messages in thread

* [PATCH RESEND 1/4] ARM: S3C2416: Add USB Phy register definitions
@ 2011-03-23 21:37   ` Heiko Stübner
  0 siblings, 0 replies; 52+ messages in thread
From: Heiko Stübner @ 2011-03-23 21:37 UTC (permalink / raw)
  To: linux-arm-kernel

From: Thomas Abraham <thomas.ab@samsung.com>

Add register definitions required to configure the USB Phy. The definitions
for PHYCTRL, PHYPWR, URSTCON and UCLKCON registers and corresponding bit
field definitions are added.

Signed-off-by: Thomas Abraham <thomas.ab@samsung.com>
Signed-off-by: Sangbeom Kim <sbkim73@samsung.com>
---
 .../mach-s3c2410/include/mach/regs-s3c2443-clock.h |   25 ++++++++++++++++++++
 1 files changed, 25 insertions(+), 0 deletions(-)

diff --git a/arch/arm/mach-s3c2410/include/mach/regs-s3c2443-clock.h b/arch/arm/mach-s3c2410/include/mach/regs-s3c2443-clock.h
index 44494a5..5e06c72 100644
--- a/arch/arm/mach-s3c2410/include/mach/regs-s3c2443-clock.h
+++ b/arch/arm/mach-s3c2410/include/mach/regs-s3c2443-clock.h
@@ -37,6 +37,10 @@
 #define S3C2443_SYSID			S3C2443_CLKREG(0x5C)
 #define S3C2443_PWRCFG			S3C2443_CLKREG(0x60)
 #define S3C2443_RSTCON			S3C2443_CLKREG(0x64)
+#define S3C2443_PHYCTRL			S3C2443_CLKREG(0x80)
+#define S3C2443_PHYPWR			S3C2443_CLKREG(0x84)
+#define S3C2443_URSTCON			S3C2443_CLKREG(0x88)
+#define S3C2443_UCLKCON			S3C2443_CLKREG(0x8C)
 
 #define S3C2443_SWRST_RESET		(0x533c2443)
 
@@ -121,6 +125,27 @@
 
 #define S3C2443_PWRCFG_SLEEP		(1<<15)
 
+#define S3C2443_PWRCFG_USBPHY		(1 << 4)
+
+#define S3C2443_URSTCON_FUNCRST		(1 << 2)
+#define S3C2443_URSTCON_PHYRST		(1 << 0)
+
+#define S3C2443_PHYCTRL_CLKSEL		(1 << 3)
+#define S3C2443_PHYCTRL_EXTCLK		(1 << 2)
+#define S3C2443_PHYCTRL_PLLSEL		(1 << 1)
+#define S3C2443_PHYCTRL_DSPORT		(1 << 0)
+
+#define S3C2443_PHYPWR_COMMON_ON	(1 << 31)
+#define S3C2443_PHYPWR_ANALOG_PD	(1 << 4)
+#define S3C2443_PHYPWR_PLL_REFCLK	(1 << 3)
+#define S3C2443_PHYPWR_XO_ON		(1 << 2)
+#define S3C2443_PHYPWR_PLL_PWRDN	(1 << 1)
+#define S3C2443_PHYPWR_FSUSPEND		(1 << 0)
+
+#define S3C2443_UCLKCON_DETECT_VBUS	(1 << 31)
+#define S3C2443_UCLKCON_FUNC_CLKEN	(1 << 2)
+#define S3C2443_UCLKCON_TCLKEN		(1 << 0)
+
 #include <asm/div64.h>
 
 static inline unsigned int
-- 
1.7.2.3

^ permalink raw reply related	[flat|nested] 52+ messages in thread

* [PATCH RESEND 2/4] ARM: S3C24XX: Add plaform device definition for USB High-Speed gadget controller.
  2011-03-23 21:35 ` Heiko Stübner
@ 2011-03-23 21:38   ` Heiko Stübner
  -1 siblings, 0 replies; 52+ messages in thread
From: Heiko Stübner @ 2011-03-23 21:38 UTC (permalink / raw)
  To: Ben Dooks
  Cc: Thomas Abraham, Sangbeom Kim, Kukjin Kim, linux-arm-kernel,
	linux-samsung-soc, linux-usb, dbrownell, Alexander Neumann

From: Thomas Abraham <thomas.ab@samsung.com>

S3C2416, S3C2443 and S3C2450 includes a USB High-Speed Gadget controller module.
This patch adds the following for supporting this controller.

1. Definition for USB High-Speed controller base address.
2. Platform device instantiation.
3. Declaration for platform data structure.
4. Functionality to setup platform data for the controller.

Signed-off-by: Thomas Abraham <thomas.ab@samsung.com>
Signed-off-by: Sangbeom Kim <sbkim73@samsung.com>
---
 arch/arm/mach-s3c2410/include/mach/map.h  |    4 +++
 arch/arm/plat-s3c24xx/devs.c              |   41 +++++++++++++++++++++++++++++
 arch/arm/plat-s3c24xx/include/plat/udc.h  |   17 ++++++++++++
 arch/arm/plat-samsung/include/plat/devs.h |    1 +
 4 files changed, 63 insertions(+), 0 deletions(-)

diff --git a/arch/arm/mach-s3c2410/include/mach/map.h b/arch/arm/mach-s3c2410/include/mach/map.h
index 25bbf5a..425552d 100644
--- a/arch/arm/mach-s3c2410/include/mach/map.h
+++ b/arch/arm/mach-s3c2410/include/mach/map.h
@@ -21,6 +21,10 @@
 /* USB host controller */
 #define S3C2410_PA_USBHOST (0x49000000)
 
+/* S3C2416/S3C2443/S3C2450 High-Speed USB Gadget */
+#define S3C2416_PA_HSUDC	(0x49800000)
+#define S3C2416_SZ_HSUDC	(SZ_4K)
+
 /* DMA controller */
 #define S3C2410_PA_DMA	   (0x4B000000)
 #define S3C24XX_SZ_DMA	   SZ_1M
diff --git a/arch/arm/plat-s3c24xx/devs.c b/arch/arm/plat-s3c24xx/devs.c
index 268f3ed..7366799 100644
--- a/arch/arm/plat-s3c24xx/devs.c
+++ b/arch/arm/plat-s3c24xx/devs.c
@@ -22,6 +22,7 @@
 #include <linux/io.h>
 #include <linux/slab.h>
 #include <linux/string.h>
+#include <linux/dma-mapping.h>
 
 #include <asm/mach/arch.h>
 #include <asm/mach/map.h>
@@ -233,6 +234,46 @@ void __init s3c24xx_udc_set_platdata(struct s3c2410_udc_mach_info *pd)
 	}
 }
 
+/* USB High Speed 2.0 Device (Gadget) */
+static struct resource s3c_hsudc_resource[] = {
+	[0] = {
+		.start	= S3C2416_PA_HSUDC,
+		.end	= S3C2416_PA_HSUDC + S3C2416_SZ_HSUDC - 1,
+		.flags	= IORESOURCE_MEM,
+	},
+	[1] = {
+		.start	= IRQ_USBD,
+		.end	= IRQ_USBD,
+		.flags	= IORESOURCE_IRQ,
+	}
+};
+
+static u64 s3c_hsudc_dmamask = DMA_BIT_MASK(32);
+
+struct platform_device s3c_device_usb_hsudc = {
+	.name		= "s3c-hsudc",
+	.id		= -1,
+	.num_resources	= ARRAY_SIZE(s3c_hsudc_resource),
+	.resource	= s3c_hsudc_resource,
+	.dev		= {
+		.dma_mask		= &s3c_hsudc_dmamask,
+		.coherent_dma_mask	= DMA_BIT_MASK(32),
+	},
+};
+
+void __init s3c24xx_hsudc_set_platdata(struct s3c24xx_hsudc_platdata *pd)
+{
+	struct s3c24xx_hsudc_platdata *npd;
+
+	npd = kmalloc(sizeof(*npd), GFP_KERNEL);
+	if (npd) {
+		memcpy(npd, pd, sizeof(*npd));
+		s3c_device_usb_hsudc.dev.platform_data = npd;
+	} else {
+		printk(KERN_ERR "no memory for udc platform data\n");
+	}
+}
+
 /* IIS */
 
 static struct resource s3c_iis_resource[] = {
diff --git a/arch/arm/plat-s3c24xx/include/plat/udc.h b/arch/arm/plat-s3c24xx/include/plat/udc.h
index 546bb40..a3fc1be 100644
--- a/arch/arm/plat-s3c24xx/include/plat/udc.h
+++ b/arch/arm/plat-s3c24xx/include/plat/udc.h
@@ -33,4 +33,21 @@ struct s3c2410_udc_mach_info {
 
 extern void __init s3c24xx_udc_set_platdata(struct s3c2410_udc_mach_info *);
 
+/**
+ * s3c24xx_hsudc_platdata - Platform data for USB High-Speed gadget controller.
+ * @epnum: Number of endpoints to be instantiated by the controller driver.
+ * @gpio_init: Platform specific USB related GPIO initialization.
+ * @gpio_uninit: Platform specific USB releted GPIO uninitialzation.
+ *
+ * Representation of platform data for the S3C24XX USB 2.0 High Speed gadget
+ * controllers.
+ */
+struct s3c24xx_hsudc_platdata {
+	unsigned int	epnum;
+	void		(*gpio_init)(void);
+	void		(*gpio_uninit)(void);
+};
+
+extern void __init s3c24xx_hsudc_set_platdata(struct s3c24xx_hsudc_platdata *pd);
+
 #endif /* __ASM_ARM_ARCH_UDC_H */
diff --git a/arch/arm/plat-samsung/include/plat/devs.h b/arch/arm/plat-samsung/include/plat/devs.h
index b4d208b..fcb6e30 100644
--- a/arch/arm/plat-samsung/include/plat/devs.h
+++ b/arch/arm/plat-samsung/include/plat/devs.h
@@ -85,6 +85,7 @@ extern struct platform_device s3c64xx_device_onenand1;
 extern struct platform_device s5p_device_onenand;
 
 extern struct platform_device s3c_device_usbgadget;
+extern struct platform_device s3c_device_usb_hsudc;
 extern struct platform_device s3c_device_usb_hsotg;
 
 extern struct platform_device s5pv210_device_ac97;
-- 
1.7.2.3

^ permalink raw reply related	[flat|nested] 52+ messages in thread

* [PATCH RESEND 2/4] ARM: S3C24XX: Add plaform device definition for USB High-Speed gadget controller.
@ 2011-03-23 21:38   ` Heiko Stübner
  0 siblings, 0 replies; 52+ messages in thread
From: Heiko Stübner @ 2011-03-23 21:38 UTC (permalink / raw)
  To: linux-arm-kernel

From: Thomas Abraham <thomas.ab@samsung.com>

S3C2416, S3C2443 and S3C2450 includes a USB High-Speed Gadget controller module.
This patch adds the following for supporting this controller.

1. Definition for USB High-Speed controller base address.
2. Platform device instantiation.
3. Declaration for platform data structure.
4. Functionality to setup platform data for the controller.

Signed-off-by: Thomas Abraham <thomas.ab@samsung.com>
Signed-off-by: Sangbeom Kim <sbkim73@samsung.com>
---
 arch/arm/mach-s3c2410/include/mach/map.h  |    4 +++
 arch/arm/plat-s3c24xx/devs.c              |   41 +++++++++++++++++++++++++++++
 arch/arm/plat-s3c24xx/include/plat/udc.h  |   17 ++++++++++++
 arch/arm/plat-samsung/include/plat/devs.h |    1 +
 4 files changed, 63 insertions(+), 0 deletions(-)

diff --git a/arch/arm/mach-s3c2410/include/mach/map.h b/arch/arm/mach-s3c2410/include/mach/map.h
index 25bbf5a..425552d 100644
--- a/arch/arm/mach-s3c2410/include/mach/map.h
+++ b/arch/arm/mach-s3c2410/include/mach/map.h
@@ -21,6 +21,10 @@
 /* USB host controller */
 #define S3C2410_PA_USBHOST (0x49000000)
 
+/* S3C2416/S3C2443/S3C2450 High-Speed USB Gadget */
+#define S3C2416_PA_HSUDC	(0x49800000)
+#define S3C2416_SZ_HSUDC	(SZ_4K)
+
 /* DMA controller */
 #define S3C2410_PA_DMA	   (0x4B000000)
 #define S3C24XX_SZ_DMA	   SZ_1M
diff --git a/arch/arm/plat-s3c24xx/devs.c b/arch/arm/plat-s3c24xx/devs.c
index 268f3ed..7366799 100644
--- a/arch/arm/plat-s3c24xx/devs.c
+++ b/arch/arm/plat-s3c24xx/devs.c
@@ -22,6 +22,7 @@
 #include <linux/io.h>
 #include <linux/slab.h>
 #include <linux/string.h>
+#include <linux/dma-mapping.h>
 
 #include <asm/mach/arch.h>
 #include <asm/mach/map.h>
@@ -233,6 +234,46 @@ void __init s3c24xx_udc_set_platdata(struct s3c2410_udc_mach_info *pd)
 	}
 }
 
+/* USB High Speed 2.0 Device (Gadget) */
+static struct resource s3c_hsudc_resource[] = {
+	[0] = {
+		.start	= S3C2416_PA_HSUDC,
+		.end	= S3C2416_PA_HSUDC + S3C2416_SZ_HSUDC - 1,
+		.flags	= IORESOURCE_MEM,
+	},
+	[1] = {
+		.start	= IRQ_USBD,
+		.end	= IRQ_USBD,
+		.flags	= IORESOURCE_IRQ,
+	}
+};
+
+static u64 s3c_hsudc_dmamask = DMA_BIT_MASK(32);
+
+struct platform_device s3c_device_usb_hsudc = {
+	.name		= "s3c-hsudc",
+	.id		= -1,
+	.num_resources	= ARRAY_SIZE(s3c_hsudc_resource),
+	.resource	= s3c_hsudc_resource,
+	.dev		= {
+		.dma_mask		= &s3c_hsudc_dmamask,
+		.coherent_dma_mask	= DMA_BIT_MASK(32),
+	},
+};
+
+void __init s3c24xx_hsudc_set_platdata(struct s3c24xx_hsudc_platdata *pd)
+{
+	struct s3c24xx_hsudc_platdata *npd;
+
+	npd = kmalloc(sizeof(*npd), GFP_KERNEL);
+	if (npd) {
+		memcpy(npd, pd, sizeof(*npd));
+		s3c_device_usb_hsudc.dev.platform_data = npd;
+	} else {
+		printk(KERN_ERR "no memory for udc platform data\n");
+	}
+}
+
 /* IIS */
 
 static struct resource s3c_iis_resource[] = {
diff --git a/arch/arm/plat-s3c24xx/include/plat/udc.h b/arch/arm/plat-s3c24xx/include/plat/udc.h
index 546bb40..a3fc1be 100644
--- a/arch/arm/plat-s3c24xx/include/plat/udc.h
+++ b/arch/arm/plat-s3c24xx/include/plat/udc.h
@@ -33,4 +33,21 @@ struct s3c2410_udc_mach_info {
 
 extern void __init s3c24xx_udc_set_platdata(struct s3c2410_udc_mach_info *);
 
+/**
+ * s3c24xx_hsudc_platdata - Platform data for USB High-Speed gadget controller.
+ * @epnum: Number of endpoints to be instantiated by the controller driver.
+ * @gpio_init: Platform specific USB related GPIO initialization.
+ * @gpio_uninit: Platform specific USB releted GPIO uninitialzation.
+ *
+ * Representation of platform data for the S3C24XX USB 2.0 High Speed gadget
+ * controllers.
+ */
+struct s3c24xx_hsudc_platdata {
+	unsigned int	epnum;
+	void		(*gpio_init)(void);
+	void		(*gpio_uninit)(void);
+};
+
+extern void __init s3c24xx_hsudc_set_platdata(struct s3c24xx_hsudc_platdata *pd);
+
 #endif /* __ASM_ARM_ARCH_UDC_H */
diff --git a/arch/arm/plat-samsung/include/plat/devs.h b/arch/arm/plat-samsung/include/plat/devs.h
index b4d208b..fcb6e30 100644
--- a/arch/arm/plat-samsung/include/plat/devs.h
+++ b/arch/arm/plat-samsung/include/plat/devs.h
@@ -85,6 +85,7 @@ extern struct platform_device s3c64xx_device_onenand1;
 extern struct platform_device s5p_device_onenand;
 
 extern struct platform_device s3c_device_usbgadget;
+extern struct platform_device s3c_device_usb_hsudc;
 extern struct platform_device s3c_device_usb_hsotg;
 
 extern struct platform_device s5pv210_device_ac97;
-- 
1.7.2.3

^ permalink raw reply related	[flat|nested] 52+ messages in thread

* [PATCH RESEND v3 3/4] USB: Gadget: Add Samsung S3C24XX USB High-Speed controller driver
  2011-03-23 21:35 ` Heiko Stübner
@ 2011-03-23 21:39   ` Heiko Stübner
  -1 siblings, 0 replies; 52+ messages in thread
From: Heiko Stübner @ 2011-03-23 21:39 UTC (permalink / raw)
  To: Ben Dooks
  Cc: Thomas Abraham, Sangbeom Kim, Kukjin Kim, linux-arm-kernel,
	linux-samsung-soc, linux-usb, dbrownell, Alexander Neumann

From: Thomas Abraham <thomas.ab@samsung.com>

The Samsung's S3C2416, S3C2443 and S3C2450 includes a USB High-Speed
device controller module. This driver enables support for USB high-speed
gadget functionality for the Samsung S3C24xx SoC's that include this
controller.

Signed-off-by: Thomas Abraham <thomas.ab@samsung.com>
Signed-off-by: Sangbeom Kim <sbkim73@samsung.com>
Signed-off-by: Kukjin Kim <kgene.kim@samsung.com>
---
Changes since v1:
- Addressed comments from Ben Dooks

Changes since v2:
- Update the driver to match the data structure changes introduced by
  commit b0fca50f5a94a268ed02cfddf44448051ed9343f.
  Alexander Neumann <alexander@debian.org>
- Move s3c_hsudc to 0x29 in usb_gadget_controller_number in gadget_chips.h
  Heiko Stuebner <heiko@sntech.de>

 drivers/usb/gadget/Kconfig        |   17 +
 drivers/usb/gadget/Makefile       |    1 +
 drivers/usb/gadget/gadget_chips.h |    8 +
 drivers/usb/gadget/s3c-hsudc.c    | 1329 +++++++++++++++++++++++++++++++++++++
 4 files changed, 1355 insertions(+), 0 deletions(-)
 create mode 100644 drivers/usb/gadget/s3c-hsudc.c

diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig
index d500996..d836756 100644
--- a/drivers/usb/gadget/Kconfig
+++ b/drivers/usb/gadget/Kconfig
@@ -338,6 +338,23 @@ config USB_S3C2410_DEBUG
 	boolean "S3C2410 udc debug messages"
 	depends on USB_GADGET_S3C2410
 
+config USB_GADGET_S3C_HSUDC
+	boolean "S3C2416, S3C2443 and S3C2450 USB Device Controller"
+	depends on ARCH_S3C2410
+	select USB_GADGET_DUALSPEED
+	help
+	  Samsung's S3C2416, S3C2443 and S3C2450 is an ARM9 based SoC
+	  integrated with dual speed USB 2.0 device controller. It has
+	  8 endpoints, as well as endpoint zero.
+
+	  This driver has been tested on S3C2416 and S3C2450 processors.
+
+config USB_S3C_HSUDC
+	tristate
+	depends on USB_GADGET_S3C_HSUDC
+	default USB_GADGET
+	select USB_GADGET_SELECTED
+
 config USB_GADGET_PXA_U2O
 	boolean "PXA9xx Processor USB2.0 controller"
 	select USB_GADGET_DUALSPEED
diff --git a/drivers/usb/gadget/Makefile b/drivers/usb/gadget/Makefile
index 55f5e8a..540783c 100644
--- a/drivers/usb/gadget/Makefile
+++ b/drivers/usb/gadget/Makefile
@@ -23,6 +23,7 @@ obj-$(CONFIG_USB_R8A66597)	+= r8a66597-udc.o
 obj-$(CONFIG_USB_FSL_QE)	+= fsl_qe_udc.o
 obj-$(CONFIG_USB_CI13XXX_PCI)	+= ci13xxx_pci.o
 obj-$(CONFIG_USB_S3C_HSOTG)	+= s3c-hsotg.o
+obj-$(CONFIG_USB_S3C_HSUDC)	+= s3c-hsudc.o
 obj-$(CONFIG_USB_LANGWELL)	+= langwell_udc.o
 obj-$(CONFIG_USB_EG20T)		+= pch_udc.o
 obj-$(CONFIG_USB_PXA_U2O)	+= mv_udc.o
diff --git a/drivers/usb/gadget/gadget_chips.h b/drivers/usb/gadget/gadget_chips.h
index 5c2720d..836a08e 100644
--- a/drivers/usb/gadget/gadget_chips.h
+++ b/drivers/usb/gadget/gadget_chips.h
@@ -142,6 +142,12 @@
 #define gadget_is_s3c_hsotg(g)    0
 #endif
 
+#ifdef CONFIG_USB_S3C_HSUDC
+#define gadget_is_s3c_hsudc(g) (!strcmp("s3c-hsudc", (g)->name))
+#else
+#define gadget_is_s3c_hsudc(g) 0
+#endif
+
 #ifdef CONFIG_USB_GADGET_EG20T
 #define	gadget_is_pch(g)	(!strcmp("pch_udc", (g)->name))
 #else
@@ -215,6 +221,8 @@ static inline int usb_gadget_controller_number(struct usb_gadget *gadget)
 		return 0x27;
 	else if (gadget_is_ci13xxx_msm(gadget))
 		return 0x28;
+	else if (gadget_is_s3c_hsudc(gadget))
+		return 0x29;
 	return -ENOENT;
 }
 
diff --git a/drivers/usb/gadget/s3c-hsudc.c b/drivers/usb/gadget/s3c-hsudc.c
new file mode 100644
index 0000000..b98d8a1
--- /dev/null
+++ b/drivers/usb/gadget/s3c-hsudc.c
@@ -0,0 +1,1329 @@
+/* linux/drivers/usb/gadget/s3c-hsudc.c
+ *
+ * Copyright (c) 2010 Samsung Electronics Co., Ltd.
+ *		http://www.samsung.com/
+ *
+ * S3C24XX USB 2.0 High-speed USB controller gadget driver
+ *
+ * The S3C24XX USB 2.0 high-speed USB controller supports upto 9 endpoints.
+ * Each endpoint can be configured as either in or out endpoint. Endpoints
+ * can be configured for Bulk or Interrupt transfer mode.
+ *
+ * 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.
+*/
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/spinlock.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <linux/dma-mapping.h>
+#include <linux/delay.h>
+#include <linux/io.h>
+#include <linux/slab.h>
+#include <linux/clk.h>
+#include <linux/usb/ch9.h>
+#include <linux/usb/gadget.h>
+
+#include <mach/regs-s3c2443-clock.h>
+#include <plat/udc.h>
+
+#define S3C_HSUDC_REG(x)	(x)
+
+/* Non-Indexed Registers */
+#define S3C_IR				S3C_HSUDC_REG(0x00) /* Index Register */
+#define S3C_EIR				S3C_HSUDC_REG(0x04) /* EP Intr Status */
+#define S3C_EIR_EP0			(1<<0)
+#define S3C_EIER			S3C_HSUDC_REG(0x08) /* EP Intr Enable */
+#define S3C_FAR				S3C_HSUDC_REG(0x0c) /* Gadget Address */
+#define S3C_FNR				S3C_HSUDC_REG(0x10) /* Frame Number */
+#define S3C_EDR				S3C_HSUDC_REG(0x14) /* EP Direction */
+#define S3C_TR				S3C_HSUDC_REG(0x18) /* Test Register */
+#define S3C_SSR				S3C_HSUDC_REG(0x1c) /* System Status */
+#define S3C_SSR_DTZIEN_EN		(0xff8f)
+#define S3C_SSR_ERR			(0xff80)
+#define S3C_SSR_VBUSON			(1 << 8)
+#define S3C_SSR_HSP			(1 << 4)
+#define S3C_SSR_SDE			(1 << 3)
+#define S3C_SSR_RESUME			(1 << 2)
+#define S3C_SSR_SUSPEND			(1 << 1)
+#define S3C_SSR_RESET			(1 << 0)
+#define S3C_SCR				S3C_HSUDC_REG(0x20) /* System Control */
+#define S3C_SCR_DTZIEN_EN		(1 << 14)
+#define S3C_SCR_RRD_EN			(1 << 5)
+#define S3C_SCR_SUS_EN			(1 << 1)
+#define S3C_SCR_RST_EN			(1 << 0)
+#define S3C_EP0SR			S3C_HSUDC_REG(0x24) /* EP0 Status */
+#define S3C_EP0SR_EP0_LWO		(1 << 6)
+#define S3C_EP0SR_STALL			(1 << 4)
+#define S3C_EP0SR_TX_SUCCESS		(1 << 1)
+#define S3C_EP0SR_RX_SUCCESS		(1 << 0)
+#define S3C_EP0CR			S3C_HSUDC_REG(0x28) /* EP0 Control */
+#define S3C_BR(_x)			S3C_HSUDC_REG(0x60 + (_x * 4))
+
+/* Indexed Registers */
+#define S3C_ESR				S3C_HSUDC_REG(0x2c) /* EPn Status */
+#define S3C_ESR_FLUSH			(1 << 6)
+#define S3C_ESR_STALL			(1 << 5)
+#define S3C_ESR_LWO			(1 << 4)
+#define S3C_ESR_PSIF_ONE		(1 << 2)
+#define S3C_ESR_PSIF_TWO		(2 << 2)
+#define S3C_ESR_TX_SUCCESS		(1 << 1)
+#define S3C_ESR_RX_SUCCESS		(1 << 0)
+#define S3C_ECR				S3C_HSUDC_REG(0x30) /* EPn Control */
+#define S3C_ECR_DUEN			(1 << 7)
+#define S3C_ECR_FLUSH			(1 << 6)
+#define S3C_ECR_STALL			(1 << 1)
+#define S3C_ECR_IEMS			(1 << 0)
+#define S3C_BRCR			S3C_HSUDC_REG(0x34) /* Read Count */
+#define S3C_BWCR			S3C_HSUDC_REG(0x38) /* Write Count */
+#define S3C_MPR				S3C_HSUDC_REG(0x3c) /* Max Pkt Size */
+
+#define WAIT_FOR_SETUP			(0)
+#define DATA_STATE_XMIT			(1)
+#define DATA_STATE_RECV			(2)
+
+/**
+ * struct s3c_hsudc_ep - Endpoint representation used by driver.
+ * @ep: USB gadget layer representation of device endpoint.
+ * @name: Endpoint name (as required by ep autoconfiguration).
+ * @dev: Reference to the device controller to which this EP belongs.
+ * @desc: Endpoint descriptor obtained from the gadget driver.
+ * @queue: Transfer request queue for the endpoint.
+ * @stopped: Maintains state of endpoint, set if EP is halted.
+ * @bEndpointAddress: EP address (including direction bit).
+ * @fifo: Base address of EP FIFO.
+ */
+struct s3c_hsudc_ep {
+	struct usb_ep ep;
+	char name[20];
+	struct s3c_hsudc *dev;
+	const struct usb_endpoint_descriptor *desc;
+	struct list_head queue;
+	u8 stopped;
+	u8 bEndpointAddress;
+	void __iomem *fifo;
+};
+
+/**
+ * struct s3c_hsudc_req - Driver encapsulation of USB gadget transfer request.
+ * @req: Reference to USB gadget transfer request.
+ * @queue: Used for inserting this request to the endpoint request queue.
+ */
+struct s3c_hsudc_req {
+	struct usb_request req;
+	struct list_head queue;
+};
+
+/**
+ * struct s3c_hsudc - Driver's abstraction of the device controller.
+ * @gadget: Instance of usb_gadget which is referenced by gadget driver.
+ * @driver: Reference to currenty active gadget driver.
+ * @dev: The device reference used by probe function.
+ * @lock: Lock to synchronize the usage of Endpoints (EP's are indexed).
+ * @regs: Remapped base address of controller's register space.
+ * @mem_rsrc: Device memory resource used for remapping device register space.
+ * irq: IRQ number used by the controller.
+ * uclk: Reference to the controller clock.
+ * ep0state: Current state of EP0.
+ * ep: List of endpoints supported by the controller.
+ */
+struct s3c_hsudc {
+	struct usb_gadget gadget;
+	struct usb_gadget_driver *driver;
+	struct device *dev;
+	struct s3c24xx_hsudc_platdata *pd;
+	spinlock_t lock;
+	void __iomem *regs;
+	struct resource *mem_rsrc;
+	int irq;
+	struct clk *uclk;
+	int ep0state;
+	struct s3c_hsudc_ep ep[];
+};
+
+#define ep_maxpacket(_ep)	((_ep)->ep.maxpacket)
+#define ep_is_in(_ep)		((_ep)->bEndpointAddress & USB_DIR_IN)
+#define ep_index(_ep)		((_ep)->bEndpointAddress & \
+					USB_ENDPOINT_NUMBER_MASK)
+
+static struct s3c_hsudc *the_controller;
+static const char driver_name[] = "s3c-udc";
+static const char ep0name[] = "ep0-control";
+
+static inline struct s3c_hsudc_req *our_req(struct usb_request *req)
+{
+	return container_of(req, struct s3c_hsudc_req, req);
+}
+
+static inline struct s3c_hsudc_ep *our_ep(struct usb_ep *ep)
+{
+	return container_of(ep, struct s3c_hsudc_ep, ep);
+}
+
+static inline struct s3c_hsudc *to_hsudc(struct usb_gadget *gadget)
+{
+	return container_of(gadget, struct s3c_hsudc, gadget);
+}
+
+static inline void set_index(struct s3c_hsudc *hsudc, int ep_addr)
+{
+	ep_addr &= USB_ENDPOINT_NUMBER_MASK;
+	writel(ep_addr, hsudc->regs + S3C_IR);
+}
+
+static inline void __orr32(void __iomem *ptr, u32 val)
+{
+	writel(readl(ptr) | val, ptr);
+}
+
+static void s3c_hsudc_init_phy(void)
+{
+	u32 cfg;
+
+	cfg = readl(S3C2443_PWRCFG) | S3C2443_PWRCFG_USBPHY;
+	writel(cfg, S3C2443_PWRCFG);
+
+	cfg = readl(S3C2443_URSTCON);
+	cfg |= (S3C2443_URSTCON_FUNCRST | S3C2443_URSTCON_PHYRST);
+	writel(cfg, S3C2443_URSTCON);
+	mdelay(1);
+
+	cfg = readl(S3C2443_URSTCON);
+	cfg &= ~(S3C2443_URSTCON_FUNCRST | S3C2443_URSTCON_PHYRST);
+	writel(cfg, S3C2443_URSTCON);
+
+	cfg = readl(S3C2443_PHYCTRL);
+	cfg &= ~(S3C2443_PHYCTRL_CLKSEL | S3C2443_PHYCTRL_DSPORT);
+	cfg |= (S3C2443_PHYCTRL_EXTCLK | S3C2443_PHYCTRL_PLLSEL);
+	writel(cfg, S3C2443_PHYCTRL);
+
+	cfg = readl(S3C2443_PHYPWR);
+	cfg &= ~(S3C2443_PHYPWR_FSUSPEND | S3C2443_PHYPWR_PLL_PWRDN |
+		S3C2443_PHYPWR_XO_ON | S3C2443_PHYPWR_PLL_REFCLK |
+		S3C2443_PHYPWR_ANALOG_PD);
+	cfg |= S3C2443_PHYPWR_COMMON_ON;
+	writel(cfg, S3C2443_PHYPWR);
+
+	cfg = readl(S3C2443_UCLKCON);
+	cfg |= (S3C2443_UCLKCON_DETECT_VBUS | S3C2443_UCLKCON_FUNC_CLKEN |
+		S3C2443_UCLKCON_TCLKEN);
+	writel(cfg, S3C2443_UCLKCON);
+}
+
+static void s3c_hsudc_uninit_phy(void)
+{
+	u32 cfg;
+
+	cfg = readl(S3C2443_PWRCFG) & ~S3C2443_PWRCFG_USBPHY;
+	writel(cfg, S3C2443_PWRCFG);
+
+	writel(S3C2443_PHYPWR_FSUSPEND, S3C2443_PHYPWR);
+
+	cfg = readl(S3C2443_UCLKCON) & ~S3C2443_UCLKCON_FUNC_CLKEN;
+	writel(cfg, S3C2443_UCLKCON);
+}
+
+/**
+ * s3c_hsudc_complete_request - Complete a transfer request.
+ * @hsep: Endpoint to which the request belongs.
+ * @hsreq: Transfer request to be completed.
+ * @status: Transfer completion status for the transfer request.
+ */
+static void s3c_hsudc_complete_request(struct s3c_hsudc_ep *hsep,
+				struct s3c_hsudc_req *hsreq, int status)
+{
+	unsigned int stopped = hsep->stopped;
+	struct s3c_hsudc *hsudc = hsep->dev;
+
+	list_del_init(&hsreq->queue);
+	hsreq->req.status = status;
+
+	if (!ep_index(hsep)) {
+		hsudc->ep0state = WAIT_FOR_SETUP;
+		hsep->bEndpointAddress &= ~USB_DIR_IN;
+	}
+
+	hsep->stopped = 1;
+	spin_unlock(&hsudc->lock);
+	if (hsreq->req.complete != NULL)
+		hsreq->req.complete(&hsep->ep, &hsreq->req);
+	spin_lock(&hsudc->lock);
+	hsep->stopped = stopped;
+}
+
+/**
+ * s3c_hsudc_nuke_ep - Terminate all requests queued for a endpoint.
+ * @hsep: Endpoint for which queued requests have to be terminated.
+ * @status: Transfer completion status for the transfer request.
+ */
+static void s3c_hsudc_nuke_ep(struct s3c_hsudc_ep *hsep, int status)
+{
+	struct s3c_hsudc_req *hsreq;
+
+	while (!list_empty(&hsep->queue)) {
+		hsreq = list_entry(hsep->queue.next,
+				struct s3c_hsudc_req, queue);
+		s3c_hsudc_complete_request(hsep, hsreq, status);
+	}
+}
+
+/**
+ * s3c_hsudc_stop_activity - Stop activity on all endpoints.
+ * @hsudc: Device controller for which EP activity is to be stopped.
+ * @driver: Reference to the gadget driver which is currently active.
+ *
+ * All the endpoints are stopped and any pending transfer requests if any on
+ * the endpoint are terminated.
+ */
+static void s3c_hsudc_stop_activity(struct s3c_hsudc *hsudc,
+			  struct usb_gadget_driver *driver)
+{
+	struct s3c_hsudc_ep *hsep;
+	int epnum;
+
+	hsudc->gadget.speed = USB_SPEED_UNKNOWN;
+
+	for (epnum = 0; epnum < hsudc->pd->epnum; epnum++) {
+		hsep = &hsudc->ep[epnum];
+		hsep->stopped = 1;
+		s3c_hsudc_nuke_ep(hsep, -ESHUTDOWN);
+	}
+
+	spin_unlock(&hsudc->lock);
+	driver->disconnect(&hsudc->gadget);
+	spin_lock(&hsudc->lock);
+}
+
+/**
+ * s3c_hsudc_read_setup_pkt - Read the received setup packet from EP0 fifo.
+ * @hsudc: Device controller from which setup packet is to be read.
+ * @buf: The buffer into which the setup packet is read.
+ *
+ * The setup packet received in the EP0 fifo is read and stored into a
+ * given buffer address.
+ */
+
+static void s3c_hsudc_read_setup_pkt(struct s3c_hsudc *hsudc, u16 *buf)
+{
+	int count;
+
+	count = readl(hsudc->regs + S3C_BRCR);
+	while (count--)
+		*buf++ = (u16)readl(hsudc->regs + S3C_BR(0));
+
+	writel(S3C_EP0SR_RX_SUCCESS, hsudc->regs + S3C_EP0SR);
+}
+
+/**
+ * s3c_hsudc_write_fifo - Write next chunk of transfer data to EP fifo.
+ * @hsep: Endpoint to which the data is to be written.
+ * @hsreq: Transfer request from which the next chunk of data is written.
+ *
+ * Write the next chunk of data from a transfer request to the endpoint FIFO.
+ * If the transfer request completes, 1 is returned, otherwise 0 is returned.
+ */
+static int s3c_hsudc_write_fifo(struct s3c_hsudc_ep *hsep,
+				struct s3c_hsudc_req *hsreq)
+{
+	u16 *buf;
+	u32 max = ep_maxpacket(hsep);
+	u32 count, length;
+	bool is_last;
+	void __iomem *fifo = hsep->fifo;
+
+	buf = hsreq->req.buf + hsreq->req.actual;
+	prefetch(buf);
+
+	length = hsreq->req.length - hsreq->req.actual;
+	length = min(length, max);
+	hsreq->req.actual += length;
+
+	writel(length, hsep->dev->regs + S3C_BWCR);
+	for (count = 0; count < length; count += 2)
+		writel(*buf++, fifo);
+
+	if (count != max) {
+		is_last = true;
+	} else {
+		if (hsreq->req.length != hsreq->req.actual || hsreq->req.zero)
+			is_last = false;
+		else
+			is_last = true;
+	}
+
+	if (is_last) {
+		s3c_hsudc_complete_request(hsep, hsreq, 0);
+		return 1;
+	}
+
+	return 0;
+}
+
+/**
+ * s3c_hsudc_read_fifo - Read the next chunk of data from EP fifo.
+ * @hsep: Endpoint from which the data is to be read.
+ * @hsreq: Transfer request to which the next chunk of data read is written.
+ *
+ * Read the next chunk of data from the endpoint FIFO and a write it to the
+ * transfer request buffer. If the transfer request completes, 1 is returned,
+ * otherwise 0 is returned.
+ */
+static int s3c_hsudc_read_fifo(struct s3c_hsudc_ep *hsep,
+				struct s3c_hsudc_req *hsreq)
+{
+	struct s3c_hsudc *hsudc = hsep->dev;
+	u32 csr, offset;
+	u16 *buf, word;
+	u32 buflen, rcnt, rlen;
+	void __iomem *fifo = hsep->fifo;
+	u32 is_short = 0;
+
+	offset = (ep_index(hsep)) ? S3C_ESR : S3C_EP0SR;
+	csr = readl(hsudc->regs + offset);
+	if (!(csr & S3C_ESR_RX_SUCCESS))
+		return -EINVAL;
+
+	buf = hsreq->req.buf + hsreq->req.actual;
+	prefetchw(buf);
+	buflen = hsreq->req.length - hsreq->req.actual;
+
+	rcnt = readl(hsudc->regs + S3C_BRCR);
+	rlen = (csr & S3C_ESR_LWO) ? (rcnt * 2 - 1) : (rcnt * 2);
+
+	hsreq->req.actual += min(rlen, buflen);
+	is_short = (rlen < hsep->ep.maxpacket);
+
+	while (rcnt-- != 0) {
+		word = (u16)readl(fifo);
+		if (buflen) {
+			*buf++ = word;
+			buflen--;
+		} else {
+			hsreq->req.status = -EOVERFLOW;
+		}
+	}
+
+	writel(S3C_ESR_RX_SUCCESS, hsudc->regs + offset);
+
+	if (is_short || hsreq->req.actual == hsreq->req.length) {
+		s3c_hsudc_complete_request(hsep, hsreq, 0);
+		return 1;
+	}
+
+	return 0;
+}
+
+/**
+ * s3c_hsudc_epin_intr - Handle in-endpoint interrupt.
+ * @hsudc - Device controller for which the interrupt is to be handled.
+ * @ep_idx - Endpoint number on which an interrupt is pending.
+ *
+ * Handles interrupt for a in-endpoint. The interrupts that are handled are
+ * stall and data transmit complete interrupt.
+ */
+static void s3c_hsudc_epin_intr(struct s3c_hsudc *hsudc, u32 ep_idx)
+{
+	struct s3c_hsudc_ep *hsep = &hsudc->ep[ep_idx];
+	struct s3c_hsudc_req *hsreq;
+	u32 csr;
+
+	csr = readl((u32)hsudc->regs + S3C_ESR);
+	if (csr & S3C_ESR_STALL) {
+		writel(S3C_ESR_STALL, hsudc->regs + S3C_ESR);
+		return;
+	}
+
+	if (csr & S3C_ESR_TX_SUCCESS) {
+		writel(S3C_ESR_TX_SUCCESS, hsudc->regs + S3C_ESR);
+		if (list_empty(&hsep->queue))
+			return;
+
+		hsreq = list_entry(hsep->queue.next,
+				struct s3c_hsudc_req, queue);
+		if ((s3c_hsudc_write_fifo(hsep, hsreq) == 0) &&
+				(csr & S3C_ESR_PSIF_TWO))
+			s3c_hsudc_write_fifo(hsep, hsreq);
+	}
+}
+
+/**
+ * s3c_hsudc_epout_intr - Handle out-endpoint interrupt.
+ * @hsudc - Device controller for which the interrupt is to be handled.
+ * @ep_idx - Endpoint number on which an interrupt is pending.
+ *
+ * Handles interrupt for a out-endpoint. The interrupts that are handled are
+ * stall, flush and data ready interrupt.
+ */
+static void s3c_hsudc_epout_intr(struct s3c_hsudc *hsudc, u32 ep_idx)
+{
+	struct s3c_hsudc_ep *hsep = &hsudc->ep[ep_idx];
+	struct s3c_hsudc_req *hsreq;
+	u32 csr;
+
+	csr = readl((u32)hsudc->regs + S3C_ESR);
+	if (csr & S3C_ESR_STALL) {
+		writel(S3C_ESR_STALL, hsudc->regs + S3C_ESR);
+		return;
+	}
+
+	if (csr & S3C_ESR_FLUSH) {
+		__orr32(hsudc->regs + S3C_ECR, S3C_ECR_FLUSH);
+		return;
+	}
+
+	if (csr & S3C_ESR_RX_SUCCESS) {
+		if (list_empty(&hsep->queue))
+			return;
+
+		hsreq = list_entry(hsep->queue.next,
+				struct s3c_hsudc_req, queue);
+		if (((s3c_hsudc_read_fifo(hsep, hsreq)) == 0) &&
+				(csr & S3C_ESR_PSIF_TWO))
+			s3c_hsudc_read_fifo(hsep, hsreq);
+	}
+}
+
+/** s3c_hsudc_set_halt - Set or clear a endpoint halt.
+ * @_ep: Endpoint on which halt has to be set or cleared.
+ * @value: 1 for setting halt on endpoint, 0 to clear halt.
+ *
+ * Set or clear endpoint halt. If halt is set, the endpoint is stopped.
+ * If halt is cleared, for in-endpoints, if there are any pending
+ * transfer requests, transfers are started.
+ */
+static int s3c_hsudc_set_halt(struct usb_ep *_ep, int value)
+{
+	struct s3c_hsudc_ep *hsep = our_ep(_ep);
+	struct s3c_hsudc *hsudc = hsep->dev;
+	struct s3c_hsudc_req *hsreq;
+	unsigned long irqflags;
+	u32 ecr;
+	u32 offset;
+
+	if (value && ep_is_in(hsep) && !list_empty(&hsep->queue))
+		return -EAGAIN;
+
+	spin_lock_irqsave(&hsudc->lock, irqflags);
+	set_index(hsudc, ep_index(hsep));
+	offset = (ep_index(hsep)) ? S3C_ECR : S3C_EP0CR;
+	ecr = readl(hsudc->regs + offset);
+
+	if (value) {
+		ecr |= S3C_ECR_STALL;
+		if (ep_index(hsep))
+			ecr |= S3C_ECR_FLUSH;
+		hsep->stopped = 1;
+	} else {
+		ecr &= ~S3C_ECR_STALL;
+		hsep->stopped = 0;
+	}
+	writel(ecr, hsudc->regs + offset);
+
+	if (ep_is_in(hsep) && !list_empty(&hsep->queue) && !value) {
+		hsreq = list_entry(hsep->queue.next,
+			struct s3c_hsudc_req, queue);
+		if (hsreq)
+			s3c_hsudc_write_fifo(hsep, hsreq);
+	}
+
+	spin_unlock_irqrestore(&hsudc->lock, irqflags);
+	return 0;
+}
+
+/** s3c_hsudc_handle_reqfeat - Handle set feature or clear feature requests.
+ * @_ep: Device controller on which the set/clear feature needs to be handled.
+ * @ctrl: Control request as received on the endpoint 0.
+ *
+ * Handle set feature or clear feature control requests on the control endpoint.
+ */
+static int s3c_hsudc_handle_reqfeat(struct s3c_hsudc *hsudc,
+					struct usb_ctrlrequest *ctrl)
+{
+	struct s3c_hsudc_ep *hsep;
+	bool set = (ctrl->bRequest == USB_REQ_SET_FEATURE);
+	u8 ep_num = ctrl->wIndex & USB_ENDPOINT_NUMBER_MASK;
+
+	if (ctrl->bRequestType == USB_RECIP_ENDPOINT) {
+		hsep = &hsudc->ep[ep_num];
+		switch (le16_to_cpu(ctrl->wValue)) {
+		case USB_ENDPOINT_HALT:
+			s3c_hsudc_set_halt(&hsep->ep, set);
+			return 0;
+		}
+	}
+
+	return -ENOENT;
+}
+
+/**
+ * s3c_hsudc_process_req_status - Handle get status control request.
+ * @hsudc: Device controller on which get status request has be handled.
+ * @ctrl: Control request as received on the endpoint 0.
+ *
+ * Handle get status control request received on control endpoint.
+ */
+static void s3c_hsudc_process_req_status(struct s3c_hsudc *hsudc,
+					struct usb_ctrlrequest *ctrl)
+{
+	struct s3c_hsudc_ep *hsep0 = &hsudc->ep[0];
+	struct s3c_hsudc_req hsreq;
+	struct s3c_hsudc_ep *hsep;
+	__le16 reply;
+	u8 epnum;
+
+	switch (ctrl->bRequestType & USB_RECIP_MASK) {
+	case USB_RECIP_DEVICE:
+		reply = cpu_to_le16(0);
+		break;
+
+	case USB_RECIP_INTERFACE:
+		reply = cpu_to_le16(0);
+		break;
+
+	case USB_RECIP_ENDPOINT:
+		epnum = le16_to_cpu(ctrl->wIndex) & USB_ENDPOINT_NUMBER_MASK;
+		hsep = &hsudc->ep[epnum];
+		reply = cpu_to_le16(hsep->stopped ? 1 : 0);
+		break;
+	}
+
+	INIT_LIST_HEAD(&hsreq.queue);
+	hsreq.req.length = 2;
+	hsreq.req.buf = &reply;
+	hsreq.req.actual = 0;
+	hsreq.req.complete = NULL;
+	s3c_hsudc_write_fifo(hsep0, &hsreq);
+}
+
+/**
+ * s3c_hsudc_process_setup - Process control request received on endpoint 0.
+ * @hsudc: Device controller on which control request has been received.
+ *
+ * Read the control request received on endpoint 0, decode it and handle
+ * the request.
+ */
+static void s3c_hsudc_process_setup(struct s3c_hsudc *hsudc)
+{
+	struct s3c_hsudc_ep *hsep = &hsudc->ep[0];
+	struct usb_ctrlrequest ctrl = {0};
+	int ret;
+
+	s3c_hsudc_nuke_ep(hsep, -EPROTO);
+	s3c_hsudc_read_setup_pkt(hsudc, (u16 *)&ctrl);
+
+	if (ctrl.bRequestType & USB_DIR_IN) {
+		hsep->bEndpointAddress |= USB_DIR_IN;
+		hsudc->ep0state = DATA_STATE_XMIT;
+	} else {
+		hsep->bEndpointAddress &= ~USB_DIR_IN;
+		hsudc->ep0state = DATA_STATE_RECV;
+	}
+
+	switch (ctrl.bRequest) {
+	case USB_REQ_SET_ADDRESS:
+		if (ctrl.bRequestType != (USB_TYPE_STANDARD | USB_RECIP_DEVICE))
+			break;
+		hsudc->ep0state = WAIT_FOR_SETUP;
+		return;
+
+	case USB_REQ_GET_STATUS:
+		if ((ctrl.bRequestType & USB_TYPE_MASK) != USB_TYPE_STANDARD)
+			break;
+		s3c_hsudc_process_req_status(hsudc, &ctrl);
+		return;
+
+	case USB_REQ_SET_FEATURE:
+	case USB_REQ_CLEAR_FEATURE:
+		if ((ctrl.bRequestType & USB_TYPE_MASK) != USB_TYPE_STANDARD)
+			break;
+		s3c_hsudc_handle_reqfeat(hsudc, &ctrl);
+		hsudc->ep0state = WAIT_FOR_SETUP;
+		return;
+	}
+
+	if (hsudc->driver) {
+		spin_unlock(&hsudc->lock);
+		ret = hsudc->driver->setup(&hsudc->gadget, &ctrl);
+		spin_lock(&hsudc->lock);
+
+		if (ctrl.bRequest == USB_REQ_SET_CONFIGURATION) {
+			hsep->bEndpointAddress &= ~USB_DIR_IN;
+			hsudc->ep0state = WAIT_FOR_SETUP;
+		}
+
+		if (ret < 0) {
+			dev_err(hsudc->dev, "setup failed, returned %d\n",
+						ret);
+			s3c_hsudc_set_halt(&hsep->ep, 1);
+			hsudc->ep0state = WAIT_FOR_SETUP;
+			hsep->bEndpointAddress &= ~USB_DIR_IN;
+		}
+	}
+}
+
+/** s3c_hsudc_handle_ep0_intr - Handle endpoint 0 interrupt.
+ * @hsudc: Device controller on which endpoint 0 interrupt has occured.
+ *
+ * Handle endpoint 0 interrupt when it occurs. EP0 interrupt could occur
+ * when a stall handshake is sent to host or data is sent/received on
+ * endpoint 0.
+ */
+static void s3c_hsudc_handle_ep0_intr(struct s3c_hsudc *hsudc)
+{
+	struct s3c_hsudc_ep *hsep = &hsudc->ep[0];
+	struct s3c_hsudc_req *hsreq;
+	u32 csr = readl(hsudc->regs + S3C_EP0SR);
+	u32 ecr;
+
+	if (csr & S3C_EP0SR_STALL) {
+		ecr = readl(hsudc->regs + S3C_EP0CR);
+		ecr &= ~(S3C_ECR_STALL | S3C_ECR_FLUSH);
+		writel(ecr, hsudc->regs + S3C_EP0CR);
+
+		writel(S3C_EP0SR_STALL, hsudc->regs + S3C_EP0SR);
+		hsep->stopped = 0;
+
+		s3c_hsudc_nuke_ep(hsep, -ECONNABORTED);
+		hsudc->ep0state = WAIT_FOR_SETUP;
+		hsep->bEndpointAddress &= ~USB_DIR_IN;
+		return;
+	}
+
+	if (csr & S3C_EP0SR_TX_SUCCESS) {
+		writel(S3C_EP0SR_TX_SUCCESS, hsudc->regs + S3C_EP0SR);
+		if (ep_is_in(hsep)) {
+			if (list_empty(&hsep->queue))
+				return;
+
+			hsreq = list_entry(hsep->queue.next,
+					struct s3c_hsudc_req, queue);
+			s3c_hsudc_write_fifo(hsep, hsreq);
+		}
+	}
+
+	if (csr & S3C_EP0SR_RX_SUCCESS) {
+		if (hsudc->ep0state == WAIT_FOR_SETUP)
+			s3c_hsudc_process_setup(hsudc);
+		else {
+			if (!ep_is_in(hsep)) {
+				if (list_empty(&hsep->queue))
+					return;
+				hsreq = list_entry(hsep->queue.next,
+					struct s3c_hsudc_req, queue);
+				s3c_hsudc_read_fifo(hsep, hsreq);
+			}
+		}
+	}
+}
+
+/**
+ * s3c_hsudc_ep_enable - Enable a endpoint.
+ * @_ep: The endpoint to be enabled.
+ * @desc: Endpoint descriptor.
+ *
+ * Enables a endpoint when called from the gadget driver. Endpoint stall if
+ * any is cleared, transfer type is configured and endpoint interrupt is
+ * enabled.
+ */
+static int s3c_hsudc_ep_enable(struct usb_ep *_ep,
+				const struct usb_endpoint_descriptor *desc)
+{
+	struct s3c_hsudc_ep *hsep;
+	struct s3c_hsudc *hsudc;
+	unsigned long flags;
+	u32 ecr = 0;
+
+	hsep = container_of(_ep, struct s3c_hsudc_ep, ep);
+	if (!_ep || !desc || hsep->desc || _ep->name == ep0name
+		|| desc->bDescriptorType != USB_DT_ENDPOINT
+		|| hsep->bEndpointAddress != desc->bEndpointAddress
+		|| ep_maxpacket(hsep) < le16_to_cpu(desc->wMaxPacketSize))
+		return -EINVAL;
+
+	if ((desc->bmAttributes == USB_ENDPOINT_XFER_BULK
+		&& le16_to_cpu(desc->wMaxPacketSize) != ep_maxpacket(hsep))
+		|| !desc->wMaxPacketSize)
+		return -ERANGE;
+
+	hsudc = hsep->dev;
+	if (!hsudc->driver || hsudc->gadget.speed == USB_SPEED_UNKNOWN)
+		return -ESHUTDOWN;
+
+	spin_lock_irqsave(&hsudc->lock, flags);
+
+	set_index(hsudc, hsep->bEndpointAddress);
+	ecr |= ((usb_endpoint_xfer_int(desc)) ? S3C_ECR_IEMS : S3C_ECR_DUEN);
+	writel(ecr, hsudc->regs + S3C_ECR);
+
+	hsep->stopped = 0;
+	hsep->desc = desc;
+	hsep->ep.maxpacket = le16_to_cpu(desc->wMaxPacketSize);
+
+	s3c_hsudc_set_halt(_ep, 0);
+	__set_bit(ep_index(hsep), hsudc->regs + S3C_EIER);
+
+	spin_unlock_irqrestore(&hsudc->lock, flags);
+	return 0;
+}
+
+/**
+ * s3c_hsudc_ep_disable - Disable a endpoint.
+ * @_ep: The endpoint to be disabled.
+ * @desc: Endpoint descriptor.
+ *
+ * Disables a endpoint when called from the gadget driver.
+ */
+static int s3c_hsudc_ep_disable(struct usb_ep *_ep)
+{
+	struct s3c_hsudc_ep *hsep = our_ep(_ep);
+	struct s3c_hsudc *hsudc = hsep->dev;
+	unsigned long flags;
+
+	if (!_ep || !hsep->desc)
+		return -EINVAL;
+
+	spin_lock_irqsave(&hsudc->lock, flags);
+
+	set_index(hsudc, hsep->bEndpointAddress);
+	__clear_bit(ep_index(hsep), hsudc->regs + S3C_EIER);
+
+	s3c_hsudc_nuke_ep(hsep, -ESHUTDOWN);
+
+	hsep->desc = 0;
+	hsep->stopped = 1;
+
+	spin_unlock_irqrestore(&hsudc->lock, flags);
+	return 0;
+}
+
+/**
+ * s3c_hsudc_alloc_request - Allocate a new request.
+ * @_ep: Endpoint for which request is allocated (not used).
+ * @gfp_flags: Flags used for the allocation.
+ *
+ * Allocates a single transfer request structure when called from gadget driver.
+ */
+static struct usb_request *s3c_hsudc_alloc_request(struct usb_ep *_ep,
+						gfp_t gfp_flags)
+{
+	struct s3c_hsudc_req *hsreq;
+
+	hsreq = kzalloc(sizeof *hsreq, gfp_flags);
+	if (!hsreq)
+		return 0;
+
+	INIT_LIST_HEAD(&hsreq->queue);
+	return &hsreq->req;
+}
+
+/**
+ * s3c_hsudc_free_request - Deallocate a request.
+ * @ep: Endpoint for which request is deallocated (not used).
+ * @_req: Request to be deallocated.
+ *
+ * Allocates a single transfer request structure when called from gadget driver.
+ */
+static void s3c_hsudc_free_request(struct usb_ep *ep, struct usb_request *_req)
+{
+	struct s3c_hsudc_req *hsreq;
+
+	hsreq = container_of(_req, struct s3c_hsudc_req, req);
+	WARN_ON(!list_empty(&hsreq->queue));
+	kfree(hsreq);
+}
+
+/**
+ * s3c_hsudc_queue - Queue a transfer request for the endpoint.
+ * @_ep: Endpoint for which the request is queued.
+ * @_req: Request to be queued.
+ * @gfp_flags: Not used.
+ *
+ * Start or enqueue a request for a endpoint when called from gadget driver.
+ */
+static int s3c_hsudc_queue(struct usb_ep *_ep, struct usb_request *_req,
+			gfp_t gfp_flags)
+{
+	struct s3c_hsudc_req *hsreq;
+	struct s3c_hsudc_ep *hsep;
+	struct s3c_hsudc *hsudc;
+	unsigned long flags;
+	u32 offset;
+	u32 csr;
+
+	hsreq = container_of(_req, struct s3c_hsudc_req, req);
+	if ((!_req || !_req->complete || !_req->buf ||
+		!list_empty(&hsreq->queue)))
+		return -EINVAL;
+
+	hsep = container_of(_ep, struct s3c_hsudc_ep, ep);
+	hsudc = hsep->dev;
+	if (!hsudc->driver || hsudc->gadget.speed == USB_SPEED_UNKNOWN)
+		return -ESHUTDOWN;
+
+	spin_lock_irqsave(&hsudc->lock, flags);
+	set_index(hsudc, hsep->bEndpointAddress);
+
+	_req->status = -EINPROGRESS;
+	_req->actual = 0;
+
+	if (!ep_index(hsep) && _req->length == 0) {
+		hsudc->ep0state = WAIT_FOR_SETUP;
+		s3c_hsudc_complete_request(hsep, hsreq, 0);
+		spin_unlock_irqrestore(&hsudc->lock, flags);
+		return 0;
+	}
+
+	if (list_empty(&hsep->queue) && !hsep->stopped) {
+		offset = (ep_index(hsep)) ? S3C_ESR : S3C_EP0SR;
+		if (ep_is_in(hsep)) {
+			csr = readl((u32)hsudc->regs + offset);
+			if (!(csr & S3C_ESR_TX_SUCCESS) &&
+				(s3c_hsudc_write_fifo(hsep, hsreq) == 1))
+				hsreq = 0;
+		} else {
+			csr = readl((u32)hsudc->regs + offset);
+			if ((csr & S3C_ESR_RX_SUCCESS)
+				   && (s3c_hsudc_read_fifo(hsep, hsreq) == 1))
+				hsreq = 0;
+		}
+	}
+
+	if (hsreq != 0)
+		list_add_tail(&hsreq->queue, &hsep->queue);
+
+	spin_unlock_irqrestore(&hsudc->lock, flags);
+	return 0;
+}
+
+/**
+ * s3c_hsudc_dequeue - Dequeue a transfer request from an endpoint.
+ * @_ep: Endpoint from which the request is dequeued.
+ * @_req: Request to be dequeued.
+ *
+ * Dequeue a request from a endpoint when called from gadget driver.
+ */
+static int s3c_hsudc_dequeue(struct usb_ep *_ep, struct usb_request *_req)
+{
+	struct s3c_hsudc_ep *hsep = our_ep(_ep);
+	struct s3c_hsudc *hsudc = hsep->dev;
+	struct s3c_hsudc_req *hsreq;
+	unsigned long flags;
+
+	hsep = container_of(_ep, struct s3c_hsudc_ep, ep);
+	if (!_ep || hsep->ep.name == ep0name)
+		return -EINVAL;
+
+	spin_lock_irqsave(&hsudc->lock, flags);
+
+	list_for_each_entry(hsreq, &hsep->queue, queue) {
+		if (&hsreq->req == _req)
+			break;
+	}
+	if (&hsreq->req != _req) {
+		spin_unlock_irqrestore(&hsudc->lock, flags);
+		return -EINVAL;
+	}
+
+	set_index(hsudc, hsep->bEndpointAddress);
+	s3c_hsudc_complete_request(hsep, hsreq, -ECONNRESET);
+
+	spin_unlock_irqrestore(&hsudc->lock, flags);
+	return 0;
+}
+
+static struct usb_ep_ops s3c_hsudc_ep_ops = {
+	.enable = s3c_hsudc_ep_enable,
+	.disable = s3c_hsudc_ep_disable,
+	.alloc_request = s3c_hsudc_alloc_request,
+	.free_request = s3c_hsudc_free_request,
+	.queue = s3c_hsudc_queue,
+	.dequeue = s3c_hsudc_dequeue,
+	.set_halt = s3c_hsudc_set_halt,
+};
+
+/**
+ * s3c_hsudc_initep - Initialize a endpoint to default state.
+ * @hsudc - Reference to the device controller.
+ * @hsep - Endpoint to be initialized.
+ * @epnum - Address to be assigned to the endpoint.
+ *
+ * Initialize a endpoint with default configuration.
+ */
+static void s3c_hsudc_initep(struct s3c_hsudc *hsudc,
+				struct s3c_hsudc_ep *hsep, int epnum)
+{
+	char *dir;
+
+	if ((epnum % 2) == 0) {
+		dir = "out";
+	} else {
+		dir = "in";
+		hsep->bEndpointAddress = USB_DIR_IN;
+	}
+
+	hsep->bEndpointAddress |= epnum;
+	if (epnum)
+		snprintf(hsep->name, sizeof(hsep->name), "ep%d%s", epnum, dir);
+	else
+		snprintf(hsep->name, sizeof(hsep->name), "%s", ep0name);
+
+	INIT_LIST_HEAD(&hsep->queue);
+	INIT_LIST_HEAD(&hsep->ep.ep_list);
+	if (epnum)
+		list_add_tail(&hsep->ep.ep_list, &hsudc->gadget.ep_list);
+
+	hsep->dev = hsudc;
+	hsep->ep.name = hsep->name;
+	hsep->ep.maxpacket = epnum ? 512 : 64;
+	hsep->ep.ops = &s3c_hsudc_ep_ops;
+	hsep->fifo = hsudc->regs + S3C_BR(epnum);
+	hsep->desc = 0;
+	hsep->stopped = 0;
+
+	set_index(hsudc, epnum);
+	writel(hsep->ep.maxpacket, hsudc->regs + S3C_MPR);
+}
+
+/**
+ * s3c_hsudc_setup_ep - Configure all endpoints to default state.
+ * @hsudc: Reference to device controller.
+ *
+ * Configures all endpoints to default state.
+ */
+static void s3c_hsudc_setup_ep(struct s3c_hsudc *hsudc)
+{
+	int epnum;
+
+	hsudc->ep0state = WAIT_FOR_SETUP;
+	INIT_LIST_HEAD(&hsudc->gadget.ep_list);
+	for (epnum = 0; epnum < hsudc->pd->epnum; epnum++)
+		s3c_hsudc_initep(hsudc, &hsudc->ep[epnum], epnum);
+}
+
+/**
+ * s3c_hsudc_reconfig - Reconfigure the device controller to default state.
+ * @hsudc: Reference to device controller.
+ *
+ * Reconfigures the device controller registers to a default state.
+ */
+static void s3c_hsudc_reconfig(struct s3c_hsudc *hsudc)
+{
+	writel(0xAA, hsudc->regs + S3C_EDR);
+	writel(1, hsudc->regs + S3C_EIER);
+	writel(0, hsudc->regs + S3C_TR);
+	writel(S3C_SCR_DTZIEN_EN | S3C_SCR_RRD_EN | S3C_SCR_SUS_EN |
+			S3C_SCR_RST_EN, hsudc->regs + S3C_SCR);
+	writel(0, hsudc->regs + S3C_EP0CR);
+
+	s3c_hsudc_setup_ep(hsudc);
+}
+
+/**
+ * s3c_hsudc_irq - Interrupt handler for device controller.
+ * @irq: Not used.
+ * @_dev: Reference to the device controller.
+ *
+ * Interrupt handler for the device controller. This handler handles controller
+ * interrupts and endpoint interrupts.
+ */
+static irqreturn_t s3c_hsudc_irq(int irq, void *_dev)
+{
+	struct s3c_hsudc *hsudc = _dev;
+	struct s3c_hsudc_ep *hsep;
+	u32 ep_intr;
+	u32 sys_status;
+	u32 ep_idx;
+
+	spin_lock(&hsudc->lock);
+
+	sys_status = readl(hsudc->regs + S3C_SSR);
+	ep_intr = readl(hsudc->regs + S3C_EIR) & 0x3FF;
+
+	if (!ep_intr && !(sys_status & S3C_SSR_DTZIEN_EN)) {
+		spin_unlock(&hsudc->lock);
+		return IRQ_HANDLED;
+	}
+
+	if (sys_status) {
+		if (sys_status & S3C_SSR_VBUSON)
+			writel(S3C_SSR_VBUSON, hsudc->regs + S3C_SSR);
+
+		if (sys_status & S3C_SSR_ERR)
+			writel(S3C_SSR_ERR, hsudc->regs + S3C_SSR);
+
+		if (sys_status & S3C_SSR_SDE) {
+			writel(S3C_SSR_SDE, hsudc->regs + S3C_SSR);
+			hsudc->gadget.speed = (sys_status & S3C_SSR_HSP) ?
+				USB_SPEED_HIGH : USB_SPEED_FULL;
+		}
+
+		if (sys_status & S3C_SSR_SUSPEND) {
+			writel(S3C_SSR_SUSPEND, hsudc->regs + S3C_SSR);
+			if (hsudc->gadget.speed != USB_SPEED_UNKNOWN
+				&& hsudc->driver && hsudc->driver->suspend)
+				hsudc->driver->suspend(&hsudc->gadget);
+		}
+
+		if (sys_status & S3C_SSR_RESUME) {
+			writel(S3C_SSR_RESUME, hsudc->regs + S3C_SSR);
+			if (hsudc->gadget.speed != USB_SPEED_UNKNOWN
+				&& hsudc->driver && hsudc->driver->resume)
+				hsudc->driver->resume(&hsudc->gadget);
+		}
+
+		if (sys_status & S3C_SSR_RESET) {
+			writel(S3C_SSR_RESET, hsudc->regs + S3C_SSR);
+			for (ep_idx = 0; ep_idx < hsudc->pd->epnum; ep_idx++) {
+				hsep = &hsudc->ep[ep_idx];
+				hsep->stopped = 1;
+				s3c_hsudc_nuke_ep(hsep, -ECONNRESET);
+			}
+			s3c_hsudc_reconfig(hsudc);
+			hsudc->ep0state = WAIT_FOR_SETUP;
+		}
+	}
+
+	if (ep_intr & S3C_EIR_EP0) {
+		writel(S3C_EIR_EP0, hsudc->regs + S3C_EIR);
+		set_index(hsudc, 0);
+		s3c_hsudc_handle_ep0_intr(hsudc);
+	}
+
+	ep_intr >>= 1;
+	ep_idx = 1;
+	while (ep_intr) {
+		if (ep_intr & 1)  {
+			hsep = &hsudc->ep[ep_idx];
+			set_index(hsudc, ep_idx);
+			writel(1 << ep_idx, hsudc->regs + S3C_EIR);
+			if (ep_is_in(hsep))
+				s3c_hsudc_epin_intr(hsudc, ep_idx);
+			else
+				s3c_hsudc_epout_intr(hsudc, ep_idx);
+		}
+		ep_intr >>= 1;
+		ep_idx++;
+	}
+
+	spin_unlock(&hsudc->lock);
+	return IRQ_HANDLED;
+}
+
+int usb_gadget_probe_driver(struct usb_gadget_driver *driver,
+               int (*bind)(struct usb_gadget *))
+{
+	struct s3c_hsudc *hsudc = the_controller;
+	int ret;
+
+	if (!driver
+		|| (driver->speed != USB_SPEED_FULL &&
+			driver->speed != USB_SPEED_HIGH)
+		|| !bind
+		|| !driver->unbind || !driver->disconnect || !driver->setup)
+		return -EINVAL;
+
+	if (!hsudc)
+		return -ENODEV;
+
+	if (hsudc->driver)
+		return -EBUSY;
+
+	hsudc->driver = driver;
+	hsudc->gadget.dev.driver = &driver->driver;
+	hsudc->gadget.speed = USB_SPEED_UNKNOWN;
+	ret = device_add(&hsudc->gadget.dev);
+	if (ret) {
+		dev_err(hsudc->dev, "failed to probe gadget device");
+		return ret;
+	}
+
+	ret = bind(&hsudc->gadget);
+	if (ret) {
+		dev_err(hsudc->dev, "%s: bind failed\n", hsudc->gadget.name);
+		device_del(&hsudc->gadget.dev);
+
+		hsudc->driver = NULL;
+		hsudc->gadget.dev.driver = NULL;
+		return ret;
+	}
+
+	enable_irq(hsudc->irq);
+	dev_info(hsudc->dev, "bound driver %s\n", driver->driver.name);
+
+	s3c_hsudc_reconfig(hsudc);
+	s3c_hsudc_init_phy();
+	if (hsudc->pd->gpio_init)
+		hsudc->pd->gpio_init();
+
+	return 0;
+}
+EXPORT_SYMBOL(usb_gadget_probe_driver);
+
+int usb_gadget_unregister_driver(struct usb_gadget_driver *driver)
+{
+	struct s3c_hsudc *hsudc = the_controller;
+	unsigned long flags;
+
+	if (!hsudc)
+		return -ENODEV;
+
+	if (!driver || driver != hsudc->driver || !driver->unbind)
+		return -EINVAL;
+
+	spin_lock_irqsave(&hsudc->lock, flags);
+	hsudc->driver = 0;
+	s3c_hsudc_uninit_phy();
+	if (hsudc->pd->gpio_uninit)
+		hsudc->pd->gpio_uninit();
+	s3c_hsudc_stop_activity(hsudc, driver);
+	spin_unlock_irqrestore(&hsudc->lock, flags);
+
+	driver->unbind(&hsudc->gadget);
+	device_del(&hsudc->gadget.dev);
+	disable_irq(hsudc->irq);
+
+	dev_info(hsudc->dev, "unregistered gadget driver '%s'\n",
+			driver->driver.name);
+	return 0;
+}
+EXPORT_SYMBOL(usb_gadget_unregister_driver);
+
+static inline u32 s3c_hsudc_read_frameno(struct s3c_hsudc *hsudc)
+{
+	return readl(hsudc->regs + S3C_FNR) & 0x3FF;
+}
+
+static int s3c_hsudc_gadget_getframe(struct usb_gadget *gadget)
+{
+	return s3c_hsudc_read_frameno(to_hsudc(gadget));
+}
+
+static struct usb_gadget_ops s3c_hsudc_gadget_ops = {
+	.get_frame	= s3c_hsudc_gadget_getframe,
+};
+
+static int s3c_hsudc_probe(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	struct resource *res;
+	struct s3c_hsudc *hsudc;
+	struct s3c24xx_hsudc_platdata *pd = pdev->dev.platform_data;
+	int ret;
+
+	hsudc = kzalloc(sizeof(struct s3c_hsudc) +
+			sizeof(struct s3c_hsudc_ep) * pd->epnum,
+			GFP_KERNEL);
+	if (!hsudc) {
+		dev_err(dev, "cannot allocate memory\n");
+		return -ENOMEM;
+	}
+
+	the_controller = hsudc;
+	platform_set_drvdata(pdev, dev);
+	hsudc->dev = dev;
+	hsudc->pd = pdev->dev.platform_data;
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!res) {
+		dev_err(dev, "unable to obtain driver resource data\n");
+		ret = -ENODEV;
+		goto err_res;
+	}
+
+	hsudc->mem_rsrc = request_mem_region(res->start, resource_size(res),
+				dev_name(&pdev->dev));
+	if (!hsudc->mem_rsrc) {
+		dev_err(dev, "failed to reserve register area\n");
+		ret = -ENODEV;
+		goto err_res;
+	}
+
+	hsudc->regs = ioremap(res->start, resource_size(res));
+	if (!hsudc->regs) {
+		dev_err(dev, "error mapping device register area\n");
+		ret = -EBUSY;
+		goto err_remap;
+	}
+
+	ret = platform_get_irq(pdev, 0);
+	if (ret < 0) {
+		dev_err(dev, "unable to obtain IRQ number\n");
+		goto err_irq;
+	}
+	hsudc->irq = ret;
+
+	ret = request_irq(hsudc->irq, s3c_hsudc_irq, 0, driver_name, hsudc);
+	if (ret < 0) {
+		dev_err(dev, "irq request failed\n");
+		goto err_irq;
+	}
+
+	spin_lock_init(&hsudc->lock);
+
+	device_initialize(&hsudc->gadget.dev);
+	dev_set_name(&hsudc->gadget.dev, "gadget");
+
+	hsudc->gadget.is_dualspeed = 1;
+	hsudc->gadget.ops = &s3c_hsudc_gadget_ops;
+	hsudc->gadget.name = dev_name(dev);
+	hsudc->gadget.dev.parent = dev;
+	hsudc->gadget.dev.dma_mask = dev->dma_mask;
+	hsudc->gadget.ep0 = &hsudc->ep[0].ep;
+
+	hsudc->gadget.is_otg = 0;
+	hsudc->gadget.is_a_peripheral = 0;
+
+	s3c_hsudc_setup_ep(hsudc);
+
+	hsudc->uclk = clk_get(&pdev->dev, "usb-device");
+	if (hsudc->uclk == NULL) {
+		dev_err(dev, "failed to find usb-device clock source\n");
+		return -ENOENT;
+	}
+	clk_enable(hsudc->uclk);
+
+	local_irq_disable();
+
+	disable_irq(hsudc->irq);
+	local_irq_enable();
+	return 0;
+
+err_irq:
+	iounmap(hsudc->regs);
+
+err_remap:
+	release_resource(hsudc->mem_rsrc);
+	kfree(hsudc->mem_rsrc);
+
+err_res:
+	kfree(hsudc);
+	return ret;
+}
+
+static struct platform_driver s3c_hsudc_driver = {
+	.driver		= {
+		.owner	= THIS_MODULE,
+		.name	= "s3c-hsudc",
+	},
+	.probe		= s3c_hsudc_probe,
+};
+
+static int __init s3c_hsudc_modinit(void)
+{
+	return platform_driver_register(&s3c_hsudc_driver);
+}
+
+static void __exit s3c_hsudc_modexit(void)
+{
+	platform_driver_unregister(&s3c_hsudc_driver);
+}
+
+module_init(s3c_hsudc_modinit);
+module_exit(s3c_hsudc_modexit);
+
+MODULE_DESCRIPTION("Samsung S3C24XX USB high-speed controller driver");
+MODULE_AUTHOR("Thomas Abraham <thomas.ab@samsung.com>");
+MODULE_LICENSE("GPL");
-- 
1.7.2.3

^ permalink raw reply related	[flat|nested] 52+ messages in thread

* [PATCH RESEND v3 3/4] USB: Gadget: Add Samsung S3C24XX USB High-Speed controller driver
@ 2011-03-23 21:39   ` Heiko Stübner
  0 siblings, 0 replies; 52+ messages in thread
From: Heiko Stübner @ 2011-03-23 21:39 UTC (permalink / raw)
  To: linux-arm-kernel

From: Thomas Abraham <thomas.ab@samsung.com>

The Samsung's S3C2416, S3C2443 and S3C2450 includes a USB High-Speed
device controller module. This driver enables support for USB high-speed
gadget functionality for the Samsung S3C24xx SoC's that include this
controller.

Signed-off-by: Thomas Abraham <thomas.ab@samsung.com>
Signed-off-by: Sangbeom Kim <sbkim73@samsung.com>
Signed-off-by: Kukjin Kim <kgene.kim@samsung.com>
---
Changes since v1:
- Addressed comments from Ben Dooks

Changes since v2:
- Update the driver to match the data structure changes introduced by
  commit b0fca50f5a94a268ed02cfddf44448051ed9343f.
  Alexander Neumann <alexander@debian.org>
- Move s3c_hsudc to 0x29 in usb_gadget_controller_number in gadget_chips.h
  Heiko Stuebner <heiko@sntech.de>

 drivers/usb/gadget/Kconfig        |   17 +
 drivers/usb/gadget/Makefile       |    1 +
 drivers/usb/gadget/gadget_chips.h |    8 +
 drivers/usb/gadget/s3c-hsudc.c    | 1329 +++++++++++++++++++++++++++++++++++++
 4 files changed, 1355 insertions(+), 0 deletions(-)
 create mode 100644 drivers/usb/gadget/s3c-hsudc.c

diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig
index d500996..d836756 100644
--- a/drivers/usb/gadget/Kconfig
+++ b/drivers/usb/gadget/Kconfig
@@ -338,6 +338,23 @@ config USB_S3C2410_DEBUG
 	boolean "S3C2410 udc debug messages"
 	depends on USB_GADGET_S3C2410
 
+config USB_GADGET_S3C_HSUDC
+	boolean "S3C2416, S3C2443 and S3C2450 USB Device Controller"
+	depends on ARCH_S3C2410
+	select USB_GADGET_DUALSPEED
+	help
+	  Samsung's S3C2416, S3C2443 and S3C2450 is an ARM9 based SoC
+	  integrated with dual speed USB 2.0 device controller. It has
+	  8 endpoints, as well as endpoint zero.
+
+	  This driver has been tested on S3C2416 and S3C2450 processors.
+
+config USB_S3C_HSUDC
+	tristate
+	depends on USB_GADGET_S3C_HSUDC
+	default USB_GADGET
+	select USB_GADGET_SELECTED
+
 config USB_GADGET_PXA_U2O
 	boolean "PXA9xx Processor USB2.0 controller"
 	select USB_GADGET_DUALSPEED
diff --git a/drivers/usb/gadget/Makefile b/drivers/usb/gadget/Makefile
index 55f5e8a..540783c 100644
--- a/drivers/usb/gadget/Makefile
+++ b/drivers/usb/gadget/Makefile
@@ -23,6 +23,7 @@ obj-$(CONFIG_USB_R8A66597)	+= r8a66597-udc.o
 obj-$(CONFIG_USB_FSL_QE)	+= fsl_qe_udc.o
 obj-$(CONFIG_USB_CI13XXX_PCI)	+= ci13xxx_pci.o
 obj-$(CONFIG_USB_S3C_HSOTG)	+= s3c-hsotg.o
+obj-$(CONFIG_USB_S3C_HSUDC)	+= s3c-hsudc.o
 obj-$(CONFIG_USB_LANGWELL)	+= langwell_udc.o
 obj-$(CONFIG_USB_EG20T)		+= pch_udc.o
 obj-$(CONFIG_USB_PXA_U2O)	+= mv_udc.o
diff --git a/drivers/usb/gadget/gadget_chips.h b/drivers/usb/gadget/gadget_chips.h
index 5c2720d..836a08e 100644
--- a/drivers/usb/gadget/gadget_chips.h
+++ b/drivers/usb/gadget/gadget_chips.h
@@ -142,6 +142,12 @@
 #define gadget_is_s3c_hsotg(g)    0
 #endif
 
+#ifdef CONFIG_USB_S3C_HSUDC
+#define gadget_is_s3c_hsudc(g) (!strcmp("s3c-hsudc", (g)->name))
+#else
+#define gadget_is_s3c_hsudc(g) 0
+#endif
+
 #ifdef CONFIG_USB_GADGET_EG20T
 #define	gadget_is_pch(g)	(!strcmp("pch_udc", (g)->name))
 #else
@@ -215,6 +221,8 @@ static inline int usb_gadget_controller_number(struct usb_gadget *gadget)
 		return 0x27;
 	else if (gadget_is_ci13xxx_msm(gadget))
 		return 0x28;
+	else if (gadget_is_s3c_hsudc(gadget))
+		return 0x29;
 	return -ENOENT;
 }
 
diff --git a/drivers/usb/gadget/s3c-hsudc.c b/drivers/usb/gadget/s3c-hsudc.c
new file mode 100644
index 0000000..b98d8a1
--- /dev/null
+++ b/drivers/usb/gadget/s3c-hsudc.c
@@ -0,0 +1,1329 @@
+/* linux/drivers/usb/gadget/s3c-hsudc.c
+ *
+ * Copyright (c) 2010 Samsung Electronics Co., Ltd.
+ *		http://www.samsung.com/
+ *
+ * S3C24XX USB 2.0 High-speed USB controller gadget driver
+ *
+ * The S3C24XX USB 2.0 high-speed USB controller supports upto 9 endpoints.
+ * Each endpoint can be configured as either in or out endpoint. Endpoints
+ * can be configured for Bulk or Interrupt transfer mode.
+ *
+ * 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.
+*/
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/spinlock.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <linux/dma-mapping.h>
+#include <linux/delay.h>
+#include <linux/io.h>
+#include <linux/slab.h>
+#include <linux/clk.h>
+#include <linux/usb/ch9.h>
+#include <linux/usb/gadget.h>
+
+#include <mach/regs-s3c2443-clock.h>
+#include <plat/udc.h>
+
+#define S3C_HSUDC_REG(x)	(x)
+
+/* Non-Indexed Registers */
+#define S3C_IR				S3C_HSUDC_REG(0x00) /* Index Register */
+#define S3C_EIR				S3C_HSUDC_REG(0x04) /* EP Intr Status */
+#define S3C_EIR_EP0			(1<<0)
+#define S3C_EIER			S3C_HSUDC_REG(0x08) /* EP Intr Enable */
+#define S3C_FAR				S3C_HSUDC_REG(0x0c) /* Gadget Address */
+#define S3C_FNR				S3C_HSUDC_REG(0x10) /* Frame Number */
+#define S3C_EDR				S3C_HSUDC_REG(0x14) /* EP Direction */
+#define S3C_TR				S3C_HSUDC_REG(0x18) /* Test Register */
+#define S3C_SSR				S3C_HSUDC_REG(0x1c) /* System Status */
+#define S3C_SSR_DTZIEN_EN		(0xff8f)
+#define S3C_SSR_ERR			(0xff80)
+#define S3C_SSR_VBUSON			(1 << 8)
+#define S3C_SSR_HSP			(1 << 4)
+#define S3C_SSR_SDE			(1 << 3)
+#define S3C_SSR_RESUME			(1 << 2)
+#define S3C_SSR_SUSPEND			(1 << 1)
+#define S3C_SSR_RESET			(1 << 0)
+#define S3C_SCR				S3C_HSUDC_REG(0x20) /* System Control */
+#define S3C_SCR_DTZIEN_EN		(1 << 14)
+#define S3C_SCR_RRD_EN			(1 << 5)
+#define S3C_SCR_SUS_EN			(1 << 1)
+#define S3C_SCR_RST_EN			(1 << 0)
+#define S3C_EP0SR			S3C_HSUDC_REG(0x24) /* EP0 Status */
+#define S3C_EP0SR_EP0_LWO		(1 << 6)
+#define S3C_EP0SR_STALL			(1 << 4)
+#define S3C_EP0SR_TX_SUCCESS		(1 << 1)
+#define S3C_EP0SR_RX_SUCCESS		(1 << 0)
+#define S3C_EP0CR			S3C_HSUDC_REG(0x28) /* EP0 Control */
+#define S3C_BR(_x)			S3C_HSUDC_REG(0x60 + (_x * 4))
+
+/* Indexed Registers */
+#define S3C_ESR				S3C_HSUDC_REG(0x2c) /* EPn Status */
+#define S3C_ESR_FLUSH			(1 << 6)
+#define S3C_ESR_STALL			(1 << 5)
+#define S3C_ESR_LWO			(1 << 4)
+#define S3C_ESR_PSIF_ONE		(1 << 2)
+#define S3C_ESR_PSIF_TWO		(2 << 2)
+#define S3C_ESR_TX_SUCCESS		(1 << 1)
+#define S3C_ESR_RX_SUCCESS		(1 << 0)
+#define S3C_ECR				S3C_HSUDC_REG(0x30) /* EPn Control */
+#define S3C_ECR_DUEN			(1 << 7)
+#define S3C_ECR_FLUSH			(1 << 6)
+#define S3C_ECR_STALL			(1 << 1)
+#define S3C_ECR_IEMS			(1 << 0)
+#define S3C_BRCR			S3C_HSUDC_REG(0x34) /* Read Count */
+#define S3C_BWCR			S3C_HSUDC_REG(0x38) /* Write Count */
+#define S3C_MPR				S3C_HSUDC_REG(0x3c) /* Max Pkt Size */
+
+#define WAIT_FOR_SETUP			(0)
+#define DATA_STATE_XMIT			(1)
+#define DATA_STATE_RECV			(2)
+
+/**
+ * struct s3c_hsudc_ep - Endpoint representation used by driver.
+ * @ep: USB gadget layer representation of device endpoint.
+ * @name: Endpoint name (as required by ep autoconfiguration).
+ * @dev: Reference to the device controller to which this EP belongs.
+ * @desc: Endpoint descriptor obtained from the gadget driver.
+ * @queue: Transfer request queue for the endpoint.
+ * @stopped: Maintains state of endpoint, set if EP is halted.
+ * @bEndpointAddress: EP address (including direction bit).
+ * @fifo: Base address of EP FIFO.
+ */
+struct s3c_hsudc_ep {
+	struct usb_ep ep;
+	char name[20];
+	struct s3c_hsudc *dev;
+	const struct usb_endpoint_descriptor *desc;
+	struct list_head queue;
+	u8 stopped;
+	u8 bEndpointAddress;
+	void __iomem *fifo;
+};
+
+/**
+ * struct s3c_hsudc_req - Driver encapsulation of USB gadget transfer request.
+ * @req: Reference to USB gadget transfer request.
+ * @queue: Used for inserting this request to the endpoint request queue.
+ */
+struct s3c_hsudc_req {
+	struct usb_request req;
+	struct list_head queue;
+};
+
+/**
+ * struct s3c_hsudc - Driver's abstraction of the device controller.
+ * @gadget: Instance of usb_gadget which is referenced by gadget driver.
+ * @driver: Reference to currenty active gadget driver.
+ * @dev: The device reference used by probe function.
+ * @lock: Lock to synchronize the usage of Endpoints (EP's are indexed).
+ * @regs: Remapped base address of controller's register space.
+ * @mem_rsrc: Device memory resource used for remapping device register space.
+ * irq: IRQ number used by the controller.
+ * uclk: Reference to the controller clock.
+ * ep0state: Current state of EP0.
+ * ep: List of endpoints supported by the controller.
+ */
+struct s3c_hsudc {
+	struct usb_gadget gadget;
+	struct usb_gadget_driver *driver;
+	struct device *dev;
+	struct s3c24xx_hsudc_platdata *pd;
+	spinlock_t lock;
+	void __iomem *regs;
+	struct resource *mem_rsrc;
+	int irq;
+	struct clk *uclk;
+	int ep0state;
+	struct s3c_hsudc_ep ep[];
+};
+
+#define ep_maxpacket(_ep)	((_ep)->ep.maxpacket)
+#define ep_is_in(_ep)		((_ep)->bEndpointAddress & USB_DIR_IN)
+#define ep_index(_ep)		((_ep)->bEndpointAddress & \
+					USB_ENDPOINT_NUMBER_MASK)
+
+static struct s3c_hsudc *the_controller;
+static const char driver_name[] = "s3c-udc";
+static const char ep0name[] = "ep0-control";
+
+static inline struct s3c_hsudc_req *our_req(struct usb_request *req)
+{
+	return container_of(req, struct s3c_hsudc_req, req);
+}
+
+static inline struct s3c_hsudc_ep *our_ep(struct usb_ep *ep)
+{
+	return container_of(ep, struct s3c_hsudc_ep, ep);
+}
+
+static inline struct s3c_hsudc *to_hsudc(struct usb_gadget *gadget)
+{
+	return container_of(gadget, struct s3c_hsudc, gadget);
+}
+
+static inline void set_index(struct s3c_hsudc *hsudc, int ep_addr)
+{
+	ep_addr &= USB_ENDPOINT_NUMBER_MASK;
+	writel(ep_addr, hsudc->regs + S3C_IR);
+}
+
+static inline void __orr32(void __iomem *ptr, u32 val)
+{
+	writel(readl(ptr) | val, ptr);
+}
+
+static void s3c_hsudc_init_phy(void)
+{
+	u32 cfg;
+
+	cfg = readl(S3C2443_PWRCFG) | S3C2443_PWRCFG_USBPHY;
+	writel(cfg, S3C2443_PWRCFG);
+
+	cfg = readl(S3C2443_URSTCON);
+	cfg |= (S3C2443_URSTCON_FUNCRST | S3C2443_URSTCON_PHYRST);
+	writel(cfg, S3C2443_URSTCON);
+	mdelay(1);
+
+	cfg = readl(S3C2443_URSTCON);
+	cfg &= ~(S3C2443_URSTCON_FUNCRST | S3C2443_URSTCON_PHYRST);
+	writel(cfg, S3C2443_URSTCON);
+
+	cfg = readl(S3C2443_PHYCTRL);
+	cfg &= ~(S3C2443_PHYCTRL_CLKSEL | S3C2443_PHYCTRL_DSPORT);
+	cfg |= (S3C2443_PHYCTRL_EXTCLK | S3C2443_PHYCTRL_PLLSEL);
+	writel(cfg, S3C2443_PHYCTRL);
+
+	cfg = readl(S3C2443_PHYPWR);
+	cfg &= ~(S3C2443_PHYPWR_FSUSPEND | S3C2443_PHYPWR_PLL_PWRDN |
+		S3C2443_PHYPWR_XO_ON | S3C2443_PHYPWR_PLL_REFCLK |
+		S3C2443_PHYPWR_ANALOG_PD);
+	cfg |= S3C2443_PHYPWR_COMMON_ON;
+	writel(cfg, S3C2443_PHYPWR);
+
+	cfg = readl(S3C2443_UCLKCON);
+	cfg |= (S3C2443_UCLKCON_DETECT_VBUS | S3C2443_UCLKCON_FUNC_CLKEN |
+		S3C2443_UCLKCON_TCLKEN);
+	writel(cfg, S3C2443_UCLKCON);
+}
+
+static void s3c_hsudc_uninit_phy(void)
+{
+	u32 cfg;
+
+	cfg = readl(S3C2443_PWRCFG) & ~S3C2443_PWRCFG_USBPHY;
+	writel(cfg, S3C2443_PWRCFG);
+
+	writel(S3C2443_PHYPWR_FSUSPEND, S3C2443_PHYPWR);
+
+	cfg = readl(S3C2443_UCLKCON) & ~S3C2443_UCLKCON_FUNC_CLKEN;
+	writel(cfg, S3C2443_UCLKCON);
+}
+
+/**
+ * s3c_hsudc_complete_request - Complete a transfer request.
+ * @hsep: Endpoint to which the request belongs.
+ * @hsreq: Transfer request to be completed.
+ * @status: Transfer completion status for the transfer request.
+ */
+static void s3c_hsudc_complete_request(struct s3c_hsudc_ep *hsep,
+				struct s3c_hsudc_req *hsreq, int status)
+{
+	unsigned int stopped = hsep->stopped;
+	struct s3c_hsudc *hsudc = hsep->dev;
+
+	list_del_init(&hsreq->queue);
+	hsreq->req.status = status;
+
+	if (!ep_index(hsep)) {
+		hsudc->ep0state = WAIT_FOR_SETUP;
+		hsep->bEndpointAddress &= ~USB_DIR_IN;
+	}
+
+	hsep->stopped = 1;
+	spin_unlock(&hsudc->lock);
+	if (hsreq->req.complete != NULL)
+		hsreq->req.complete(&hsep->ep, &hsreq->req);
+	spin_lock(&hsudc->lock);
+	hsep->stopped = stopped;
+}
+
+/**
+ * s3c_hsudc_nuke_ep - Terminate all requests queued for a endpoint.
+ * @hsep: Endpoint for which queued requests have to be terminated.
+ * @status: Transfer completion status for the transfer request.
+ */
+static void s3c_hsudc_nuke_ep(struct s3c_hsudc_ep *hsep, int status)
+{
+	struct s3c_hsudc_req *hsreq;
+
+	while (!list_empty(&hsep->queue)) {
+		hsreq = list_entry(hsep->queue.next,
+				struct s3c_hsudc_req, queue);
+		s3c_hsudc_complete_request(hsep, hsreq, status);
+	}
+}
+
+/**
+ * s3c_hsudc_stop_activity - Stop activity on all endpoints.
+ * @hsudc: Device controller for which EP activity is to be stopped.
+ * @driver: Reference to the gadget driver which is currently active.
+ *
+ * All the endpoints are stopped and any pending transfer requests if any on
+ * the endpoint are terminated.
+ */
+static void s3c_hsudc_stop_activity(struct s3c_hsudc *hsudc,
+			  struct usb_gadget_driver *driver)
+{
+	struct s3c_hsudc_ep *hsep;
+	int epnum;
+
+	hsudc->gadget.speed = USB_SPEED_UNKNOWN;
+
+	for (epnum = 0; epnum < hsudc->pd->epnum; epnum++) {
+		hsep = &hsudc->ep[epnum];
+		hsep->stopped = 1;
+		s3c_hsudc_nuke_ep(hsep, -ESHUTDOWN);
+	}
+
+	spin_unlock(&hsudc->lock);
+	driver->disconnect(&hsudc->gadget);
+	spin_lock(&hsudc->lock);
+}
+
+/**
+ * s3c_hsudc_read_setup_pkt - Read the received setup packet from EP0 fifo.
+ * @hsudc: Device controller from which setup packet is to be read.
+ * @buf: The buffer into which the setup packet is read.
+ *
+ * The setup packet received in the EP0 fifo is read and stored into a
+ * given buffer address.
+ */
+
+static void s3c_hsudc_read_setup_pkt(struct s3c_hsudc *hsudc, u16 *buf)
+{
+	int count;
+
+	count = readl(hsudc->regs + S3C_BRCR);
+	while (count--)
+		*buf++ = (u16)readl(hsudc->regs + S3C_BR(0));
+
+	writel(S3C_EP0SR_RX_SUCCESS, hsudc->regs + S3C_EP0SR);
+}
+
+/**
+ * s3c_hsudc_write_fifo - Write next chunk of transfer data to EP fifo.
+ * @hsep: Endpoint to which the data is to be written.
+ * @hsreq: Transfer request from which the next chunk of data is written.
+ *
+ * Write the next chunk of data from a transfer request to the endpoint FIFO.
+ * If the transfer request completes, 1 is returned, otherwise 0 is returned.
+ */
+static int s3c_hsudc_write_fifo(struct s3c_hsudc_ep *hsep,
+				struct s3c_hsudc_req *hsreq)
+{
+	u16 *buf;
+	u32 max = ep_maxpacket(hsep);
+	u32 count, length;
+	bool is_last;
+	void __iomem *fifo = hsep->fifo;
+
+	buf = hsreq->req.buf + hsreq->req.actual;
+	prefetch(buf);
+
+	length = hsreq->req.length - hsreq->req.actual;
+	length = min(length, max);
+	hsreq->req.actual += length;
+
+	writel(length, hsep->dev->regs + S3C_BWCR);
+	for (count = 0; count < length; count += 2)
+		writel(*buf++, fifo);
+
+	if (count != max) {
+		is_last = true;
+	} else {
+		if (hsreq->req.length != hsreq->req.actual || hsreq->req.zero)
+			is_last = false;
+		else
+			is_last = true;
+	}
+
+	if (is_last) {
+		s3c_hsudc_complete_request(hsep, hsreq, 0);
+		return 1;
+	}
+
+	return 0;
+}
+
+/**
+ * s3c_hsudc_read_fifo - Read the next chunk of data from EP fifo.
+ * @hsep: Endpoint from which the data is to be read.
+ * @hsreq: Transfer request to which the next chunk of data read is written.
+ *
+ * Read the next chunk of data from the endpoint FIFO and a write it to the
+ * transfer request buffer. If the transfer request completes, 1 is returned,
+ * otherwise 0 is returned.
+ */
+static int s3c_hsudc_read_fifo(struct s3c_hsudc_ep *hsep,
+				struct s3c_hsudc_req *hsreq)
+{
+	struct s3c_hsudc *hsudc = hsep->dev;
+	u32 csr, offset;
+	u16 *buf, word;
+	u32 buflen, rcnt, rlen;
+	void __iomem *fifo = hsep->fifo;
+	u32 is_short = 0;
+
+	offset = (ep_index(hsep)) ? S3C_ESR : S3C_EP0SR;
+	csr = readl(hsudc->regs + offset);
+	if (!(csr & S3C_ESR_RX_SUCCESS))
+		return -EINVAL;
+
+	buf = hsreq->req.buf + hsreq->req.actual;
+	prefetchw(buf);
+	buflen = hsreq->req.length - hsreq->req.actual;
+
+	rcnt = readl(hsudc->regs + S3C_BRCR);
+	rlen = (csr & S3C_ESR_LWO) ? (rcnt * 2 - 1) : (rcnt * 2);
+
+	hsreq->req.actual += min(rlen, buflen);
+	is_short = (rlen < hsep->ep.maxpacket);
+
+	while (rcnt-- != 0) {
+		word = (u16)readl(fifo);
+		if (buflen) {
+			*buf++ = word;
+			buflen--;
+		} else {
+			hsreq->req.status = -EOVERFLOW;
+		}
+	}
+
+	writel(S3C_ESR_RX_SUCCESS, hsudc->regs + offset);
+
+	if (is_short || hsreq->req.actual == hsreq->req.length) {
+		s3c_hsudc_complete_request(hsep, hsreq, 0);
+		return 1;
+	}
+
+	return 0;
+}
+
+/**
+ * s3c_hsudc_epin_intr - Handle in-endpoint interrupt.
+ * @hsudc - Device controller for which the interrupt is to be handled.
+ * @ep_idx - Endpoint number on which an interrupt is pending.
+ *
+ * Handles interrupt for a in-endpoint. The interrupts that are handled are
+ * stall and data transmit complete interrupt.
+ */
+static void s3c_hsudc_epin_intr(struct s3c_hsudc *hsudc, u32 ep_idx)
+{
+	struct s3c_hsudc_ep *hsep = &hsudc->ep[ep_idx];
+	struct s3c_hsudc_req *hsreq;
+	u32 csr;
+
+	csr = readl((u32)hsudc->regs + S3C_ESR);
+	if (csr & S3C_ESR_STALL) {
+		writel(S3C_ESR_STALL, hsudc->regs + S3C_ESR);
+		return;
+	}
+
+	if (csr & S3C_ESR_TX_SUCCESS) {
+		writel(S3C_ESR_TX_SUCCESS, hsudc->regs + S3C_ESR);
+		if (list_empty(&hsep->queue))
+			return;
+
+		hsreq = list_entry(hsep->queue.next,
+				struct s3c_hsudc_req, queue);
+		if ((s3c_hsudc_write_fifo(hsep, hsreq) == 0) &&
+				(csr & S3C_ESR_PSIF_TWO))
+			s3c_hsudc_write_fifo(hsep, hsreq);
+	}
+}
+
+/**
+ * s3c_hsudc_epout_intr - Handle out-endpoint interrupt.
+ * @hsudc - Device controller for which the interrupt is to be handled.
+ * @ep_idx - Endpoint number on which an interrupt is pending.
+ *
+ * Handles interrupt for a out-endpoint. The interrupts that are handled are
+ * stall, flush and data ready interrupt.
+ */
+static void s3c_hsudc_epout_intr(struct s3c_hsudc *hsudc, u32 ep_idx)
+{
+	struct s3c_hsudc_ep *hsep = &hsudc->ep[ep_idx];
+	struct s3c_hsudc_req *hsreq;
+	u32 csr;
+
+	csr = readl((u32)hsudc->regs + S3C_ESR);
+	if (csr & S3C_ESR_STALL) {
+		writel(S3C_ESR_STALL, hsudc->regs + S3C_ESR);
+		return;
+	}
+
+	if (csr & S3C_ESR_FLUSH) {
+		__orr32(hsudc->regs + S3C_ECR, S3C_ECR_FLUSH);
+		return;
+	}
+
+	if (csr & S3C_ESR_RX_SUCCESS) {
+		if (list_empty(&hsep->queue))
+			return;
+
+		hsreq = list_entry(hsep->queue.next,
+				struct s3c_hsudc_req, queue);
+		if (((s3c_hsudc_read_fifo(hsep, hsreq)) == 0) &&
+				(csr & S3C_ESR_PSIF_TWO))
+			s3c_hsudc_read_fifo(hsep, hsreq);
+	}
+}
+
+/** s3c_hsudc_set_halt - Set or clear a endpoint halt.
+ * @_ep: Endpoint on which halt has to be set or cleared.
+ * @value: 1 for setting halt on endpoint, 0 to clear halt.
+ *
+ * Set or clear endpoint halt. If halt is set, the endpoint is stopped.
+ * If halt is cleared, for in-endpoints, if there are any pending
+ * transfer requests, transfers are started.
+ */
+static int s3c_hsudc_set_halt(struct usb_ep *_ep, int value)
+{
+	struct s3c_hsudc_ep *hsep = our_ep(_ep);
+	struct s3c_hsudc *hsudc = hsep->dev;
+	struct s3c_hsudc_req *hsreq;
+	unsigned long irqflags;
+	u32 ecr;
+	u32 offset;
+
+	if (value && ep_is_in(hsep) && !list_empty(&hsep->queue))
+		return -EAGAIN;
+
+	spin_lock_irqsave(&hsudc->lock, irqflags);
+	set_index(hsudc, ep_index(hsep));
+	offset = (ep_index(hsep)) ? S3C_ECR : S3C_EP0CR;
+	ecr = readl(hsudc->regs + offset);
+
+	if (value) {
+		ecr |= S3C_ECR_STALL;
+		if (ep_index(hsep))
+			ecr |= S3C_ECR_FLUSH;
+		hsep->stopped = 1;
+	} else {
+		ecr &= ~S3C_ECR_STALL;
+		hsep->stopped = 0;
+	}
+	writel(ecr, hsudc->regs + offset);
+
+	if (ep_is_in(hsep) && !list_empty(&hsep->queue) && !value) {
+		hsreq = list_entry(hsep->queue.next,
+			struct s3c_hsudc_req, queue);
+		if (hsreq)
+			s3c_hsudc_write_fifo(hsep, hsreq);
+	}
+
+	spin_unlock_irqrestore(&hsudc->lock, irqflags);
+	return 0;
+}
+
+/** s3c_hsudc_handle_reqfeat - Handle set feature or clear feature requests.
+ * @_ep: Device controller on which the set/clear feature needs to be handled.
+ * @ctrl: Control request as received on the endpoint 0.
+ *
+ * Handle set feature or clear feature control requests on the control endpoint.
+ */
+static int s3c_hsudc_handle_reqfeat(struct s3c_hsudc *hsudc,
+					struct usb_ctrlrequest *ctrl)
+{
+	struct s3c_hsudc_ep *hsep;
+	bool set = (ctrl->bRequest == USB_REQ_SET_FEATURE);
+	u8 ep_num = ctrl->wIndex & USB_ENDPOINT_NUMBER_MASK;
+
+	if (ctrl->bRequestType == USB_RECIP_ENDPOINT) {
+		hsep = &hsudc->ep[ep_num];
+		switch (le16_to_cpu(ctrl->wValue)) {
+		case USB_ENDPOINT_HALT:
+			s3c_hsudc_set_halt(&hsep->ep, set);
+			return 0;
+		}
+	}
+
+	return -ENOENT;
+}
+
+/**
+ * s3c_hsudc_process_req_status - Handle get status control request.
+ * @hsudc: Device controller on which get status request has be handled.
+ * @ctrl: Control request as received on the endpoint 0.
+ *
+ * Handle get status control request received on control endpoint.
+ */
+static void s3c_hsudc_process_req_status(struct s3c_hsudc *hsudc,
+					struct usb_ctrlrequest *ctrl)
+{
+	struct s3c_hsudc_ep *hsep0 = &hsudc->ep[0];
+	struct s3c_hsudc_req hsreq;
+	struct s3c_hsudc_ep *hsep;
+	__le16 reply;
+	u8 epnum;
+
+	switch (ctrl->bRequestType & USB_RECIP_MASK) {
+	case USB_RECIP_DEVICE:
+		reply = cpu_to_le16(0);
+		break;
+
+	case USB_RECIP_INTERFACE:
+		reply = cpu_to_le16(0);
+		break;
+
+	case USB_RECIP_ENDPOINT:
+		epnum = le16_to_cpu(ctrl->wIndex) & USB_ENDPOINT_NUMBER_MASK;
+		hsep = &hsudc->ep[epnum];
+		reply = cpu_to_le16(hsep->stopped ? 1 : 0);
+		break;
+	}
+
+	INIT_LIST_HEAD(&hsreq.queue);
+	hsreq.req.length = 2;
+	hsreq.req.buf = &reply;
+	hsreq.req.actual = 0;
+	hsreq.req.complete = NULL;
+	s3c_hsudc_write_fifo(hsep0, &hsreq);
+}
+
+/**
+ * s3c_hsudc_process_setup - Process control request received on endpoint 0.
+ * @hsudc: Device controller on which control request has been received.
+ *
+ * Read the control request received on endpoint 0, decode it and handle
+ * the request.
+ */
+static void s3c_hsudc_process_setup(struct s3c_hsudc *hsudc)
+{
+	struct s3c_hsudc_ep *hsep = &hsudc->ep[0];
+	struct usb_ctrlrequest ctrl = {0};
+	int ret;
+
+	s3c_hsudc_nuke_ep(hsep, -EPROTO);
+	s3c_hsudc_read_setup_pkt(hsudc, (u16 *)&ctrl);
+
+	if (ctrl.bRequestType & USB_DIR_IN) {
+		hsep->bEndpointAddress |= USB_DIR_IN;
+		hsudc->ep0state = DATA_STATE_XMIT;
+	} else {
+		hsep->bEndpointAddress &= ~USB_DIR_IN;
+		hsudc->ep0state = DATA_STATE_RECV;
+	}
+
+	switch (ctrl.bRequest) {
+	case USB_REQ_SET_ADDRESS:
+		if (ctrl.bRequestType != (USB_TYPE_STANDARD | USB_RECIP_DEVICE))
+			break;
+		hsudc->ep0state = WAIT_FOR_SETUP;
+		return;
+
+	case USB_REQ_GET_STATUS:
+		if ((ctrl.bRequestType & USB_TYPE_MASK) != USB_TYPE_STANDARD)
+			break;
+		s3c_hsudc_process_req_status(hsudc, &ctrl);
+		return;
+
+	case USB_REQ_SET_FEATURE:
+	case USB_REQ_CLEAR_FEATURE:
+		if ((ctrl.bRequestType & USB_TYPE_MASK) != USB_TYPE_STANDARD)
+			break;
+		s3c_hsudc_handle_reqfeat(hsudc, &ctrl);
+		hsudc->ep0state = WAIT_FOR_SETUP;
+		return;
+	}
+
+	if (hsudc->driver) {
+		spin_unlock(&hsudc->lock);
+		ret = hsudc->driver->setup(&hsudc->gadget, &ctrl);
+		spin_lock(&hsudc->lock);
+
+		if (ctrl.bRequest == USB_REQ_SET_CONFIGURATION) {
+			hsep->bEndpointAddress &= ~USB_DIR_IN;
+			hsudc->ep0state = WAIT_FOR_SETUP;
+		}
+
+		if (ret < 0) {
+			dev_err(hsudc->dev, "setup failed, returned %d\n",
+						ret);
+			s3c_hsudc_set_halt(&hsep->ep, 1);
+			hsudc->ep0state = WAIT_FOR_SETUP;
+			hsep->bEndpointAddress &= ~USB_DIR_IN;
+		}
+	}
+}
+
+/** s3c_hsudc_handle_ep0_intr - Handle endpoint 0 interrupt.
+ * @hsudc: Device controller on which endpoint 0 interrupt has occured.
+ *
+ * Handle endpoint 0 interrupt when it occurs. EP0 interrupt could occur
+ * when a stall handshake is sent to host or data is sent/received on
+ * endpoint 0.
+ */
+static void s3c_hsudc_handle_ep0_intr(struct s3c_hsudc *hsudc)
+{
+	struct s3c_hsudc_ep *hsep = &hsudc->ep[0];
+	struct s3c_hsudc_req *hsreq;
+	u32 csr = readl(hsudc->regs + S3C_EP0SR);
+	u32 ecr;
+
+	if (csr & S3C_EP0SR_STALL) {
+		ecr = readl(hsudc->regs + S3C_EP0CR);
+		ecr &= ~(S3C_ECR_STALL | S3C_ECR_FLUSH);
+		writel(ecr, hsudc->regs + S3C_EP0CR);
+
+		writel(S3C_EP0SR_STALL, hsudc->regs + S3C_EP0SR);
+		hsep->stopped = 0;
+
+		s3c_hsudc_nuke_ep(hsep, -ECONNABORTED);
+		hsudc->ep0state = WAIT_FOR_SETUP;
+		hsep->bEndpointAddress &= ~USB_DIR_IN;
+		return;
+	}
+
+	if (csr & S3C_EP0SR_TX_SUCCESS) {
+		writel(S3C_EP0SR_TX_SUCCESS, hsudc->regs + S3C_EP0SR);
+		if (ep_is_in(hsep)) {
+			if (list_empty(&hsep->queue))
+				return;
+
+			hsreq = list_entry(hsep->queue.next,
+					struct s3c_hsudc_req, queue);
+			s3c_hsudc_write_fifo(hsep, hsreq);
+		}
+	}
+
+	if (csr & S3C_EP0SR_RX_SUCCESS) {
+		if (hsudc->ep0state == WAIT_FOR_SETUP)
+			s3c_hsudc_process_setup(hsudc);
+		else {
+			if (!ep_is_in(hsep)) {
+				if (list_empty(&hsep->queue))
+					return;
+				hsreq = list_entry(hsep->queue.next,
+					struct s3c_hsudc_req, queue);
+				s3c_hsudc_read_fifo(hsep, hsreq);
+			}
+		}
+	}
+}
+
+/**
+ * s3c_hsudc_ep_enable - Enable a endpoint.
+ * @_ep: The endpoint to be enabled.
+ * @desc: Endpoint descriptor.
+ *
+ * Enables a endpoint when called from the gadget driver. Endpoint stall if
+ * any is cleared, transfer type is configured and endpoint interrupt is
+ * enabled.
+ */
+static int s3c_hsudc_ep_enable(struct usb_ep *_ep,
+				const struct usb_endpoint_descriptor *desc)
+{
+	struct s3c_hsudc_ep *hsep;
+	struct s3c_hsudc *hsudc;
+	unsigned long flags;
+	u32 ecr = 0;
+
+	hsep = container_of(_ep, struct s3c_hsudc_ep, ep);
+	if (!_ep || !desc || hsep->desc || _ep->name == ep0name
+		|| desc->bDescriptorType != USB_DT_ENDPOINT
+		|| hsep->bEndpointAddress != desc->bEndpointAddress
+		|| ep_maxpacket(hsep) < le16_to_cpu(desc->wMaxPacketSize))
+		return -EINVAL;
+
+	if ((desc->bmAttributes == USB_ENDPOINT_XFER_BULK
+		&& le16_to_cpu(desc->wMaxPacketSize) != ep_maxpacket(hsep))
+		|| !desc->wMaxPacketSize)
+		return -ERANGE;
+
+	hsudc = hsep->dev;
+	if (!hsudc->driver || hsudc->gadget.speed == USB_SPEED_UNKNOWN)
+		return -ESHUTDOWN;
+
+	spin_lock_irqsave(&hsudc->lock, flags);
+
+	set_index(hsudc, hsep->bEndpointAddress);
+	ecr |= ((usb_endpoint_xfer_int(desc)) ? S3C_ECR_IEMS : S3C_ECR_DUEN);
+	writel(ecr, hsudc->regs + S3C_ECR);
+
+	hsep->stopped = 0;
+	hsep->desc = desc;
+	hsep->ep.maxpacket = le16_to_cpu(desc->wMaxPacketSize);
+
+	s3c_hsudc_set_halt(_ep, 0);
+	__set_bit(ep_index(hsep), hsudc->regs + S3C_EIER);
+
+	spin_unlock_irqrestore(&hsudc->lock, flags);
+	return 0;
+}
+
+/**
+ * s3c_hsudc_ep_disable - Disable a endpoint.
+ * @_ep: The endpoint to be disabled.
+ * @desc: Endpoint descriptor.
+ *
+ * Disables a endpoint when called from the gadget driver.
+ */
+static int s3c_hsudc_ep_disable(struct usb_ep *_ep)
+{
+	struct s3c_hsudc_ep *hsep = our_ep(_ep);
+	struct s3c_hsudc *hsudc = hsep->dev;
+	unsigned long flags;
+
+	if (!_ep || !hsep->desc)
+		return -EINVAL;
+
+	spin_lock_irqsave(&hsudc->lock, flags);
+
+	set_index(hsudc, hsep->bEndpointAddress);
+	__clear_bit(ep_index(hsep), hsudc->regs + S3C_EIER);
+
+	s3c_hsudc_nuke_ep(hsep, -ESHUTDOWN);
+
+	hsep->desc = 0;
+	hsep->stopped = 1;
+
+	spin_unlock_irqrestore(&hsudc->lock, flags);
+	return 0;
+}
+
+/**
+ * s3c_hsudc_alloc_request - Allocate a new request.
+ * @_ep: Endpoint for which request is allocated (not used).
+ * @gfp_flags: Flags used for the allocation.
+ *
+ * Allocates a single transfer request structure when called from gadget driver.
+ */
+static struct usb_request *s3c_hsudc_alloc_request(struct usb_ep *_ep,
+						gfp_t gfp_flags)
+{
+	struct s3c_hsudc_req *hsreq;
+
+	hsreq = kzalloc(sizeof *hsreq, gfp_flags);
+	if (!hsreq)
+		return 0;
+
+	INIT_LIST_HEAD(&hsreq->queue);
+	return &hsreq->req;
+}
+
+/**
+ * s3c_hsudc_free_request - Deallocate a request.
+ * @ep: Endpoint for which request is deallocated (not used).
+ * @_req: Request to be deallocated.
+ *
+ * Allocates a single transfer request structure when called from gadget driver.
+ */
+static void s3c_hsudc_free_request(struct usb_ep *ep, struct usb_request *_req)
+{
+	struct s3c_hsudc_req *hsreq;
+
+	hsreq = container_of(_req, struct s3c_hsudc_req, req);
+	WARN_ON(!list_empty(&hsreq->queue));
+	kfree(hsreq);
+}
+
+/**
+ * s3c_hsudc_queue - Queue a transfer request for the endpoint.
+ * @_ep: Endpoint for which the request is queued.
+ * @_req: Request to be queued.
+ * @gfp_flags: Not used.
+ *
+ * Start or enqueue a request for a endpoint when called from gadget driver.
+ */
+static int s3c_hsudc_queue(struct usb_ep *_ep, struct usb_request *_req,
+			gfp_t gfp_flags)
+{
+	struct s3c_hsudc_req *hsreq;
+	struct s3c_hsudc_ep *hsep;
+	struct s3c_hsudc *hsudc;
+	unsigned long flags;
+	u32 offset;
+	u32 csr;
+
+	hsreq = container_of(_req, struct s3c_hsudc_req, req);
+	if ((!_req || !_req->complete || !_req->buf ||
+		!list_empty(&hsreq->queue)))
+		return -EINVAL;
+
+	hsep = container_of(_ep, struct s3c_hsudc_ep, ep);
+	hsudc = hsep->dev;
+	if (!hsudc->driver || hsudc->gadget.speed == USB_SPEED_UNKNOWN)
+		return -ESHUTDOWN;
+
+	spin_lock_irqsave(&hsudc->lock, flags);
+	set_index(hsudc, hsep->bEndpointAddress);
+
+	_req->status = -EINPROGRESS;
+	_req->actual = 0;
+
+	if (!ep_index(hsep) && _req->length == 0) {
+		hsudc->ep0state = WAIT_FOR_SETUP;
+		s3c_hsudc_complete_request(hsep, hsreq, 0);
+		spin_unlock_irqrestore(&hsudc->lock, flags);
+		return 0;
+	}
+
+	if (list_empty(&hsep->queue) && !hsep->stopped) {
+		offset = (ep_index(hsep)) ? S3C_ESR : S3C_EP0SR;
+		if (ep_is_in(hsep)) {
+			csr = readl((u32)hsudc->regs + offset);
+			if (!(csr & S3C_ESR_TX_SUCCESS) &&
+				(s3c_hsudc_write_fifo(hsep, hsreq) == 1))
+				hsreq = 0;
+		} else {
+			csr = readl((u32)hsudc->regs + offset);
+			if ((csr & S3C_ESR_RX_SUCCESS)
+				   && (s3c_hsudc_read_fifo(hsep, hsreq) == 1))
+				hsreq = 0;
+		}
+	}
+
+	if (hsreq != 0)
+		list_add_tail(&hsreq->queue, &hsep->queue);
+
+	spin_unlock_irqrestore(&hsudc->lock, flags);
+	return 0;
+}
+
+/**
+ * s3c_hsudc_dequeue - Dequeue a transfer request from an endpoint.
+ * @_ep: Endpoint from which the request is dequeued.
+ * @_req: Request to be dequeued.
+ *
+ * Dequeue a request from a endpoint when called from gadget driver.
+ */
+static int s3c_hsudc_dequeue(struct usb_ep *_ep, struct usb_request *_req)
+{
+	struct s3c_hsudc_ep *hsep = our_ep(_ep);
+	struct s3c_hsudc *hsudc = hsep->dev;
+	struct s3c_hsudc_req *hsreq;
+	unsigned long flags;
+
+	hsep = container_of(_ep, struct s3c_hsudc_ep, ep);
+	if (!_ep || hsep->ep.name == ep0name)
+		return -EINVAL;
+
+	spin_lock_irqsave(&hsudc->lock, flags);
+
+	list_for_each_entry(hsreq, &hsep->queue, queue) {
+		if (&hsreq->req == _req)
+			break;
+	}
+	if (&hsreq->req != _req) {
+		spin_unlock_irqrestore(&hsudc->lock, flags);
+		return -EINVAL;
+	}
+
+	set_index(hsudc, hsep->bEndpointAddress);
+	s3c_hsudc_complete_request(hsep, hsreq, -ECONNRESET);
+
+	spin_unlock_irqrestore(&hsudc->lock, flags);
+	return 0;
+}
+
+static struct usb_ep_ops s3c_hsudc_ep_ops = {
+	.enable = s3c_hsudc_ep_enable,
+	.disable = s3c_hsudc_ep_disable,
+	.alloc_request = s3c_hsudc_alloc_request,
+	.free_request = s3c_hsudc_free_request,
+	.queue = s3c_hsudc_queue,
+	.dequeue = s3c_hsudc_dequeue,
+	.set_halt = s3c_hsudc_set_halt,
+};
+
+/**
+ * s3c_hsudc_initep - Initialize a endpoint to default state.
+ * @hsudc - Reference to the device controller.
+ * @hsep - Endpoint to be initialized.
+ * @epnum - Address to be assigned to the endpoint.
+ *
+ * Initialize a endpoint with default configuration.
+ */
+static void s3c_hsudc_initep(struct s3c_hsudc *hsudc,
+				struct s3c_hsudc_ep *hsep, int epnum)
+{
+	char *dir;
+
+	if ((epnum % 2) == 0) {
+		dir = "out";
+	} else {
+		dir = "in";
+		hsep->bEndpointAddress = USB_DIR_IN;
+	}
+
+	hsep->bEndpointAddress |= epnum;
+	if (epnum)
+		snprintf(hsep->name, sizeof(hsep->name), "ep%d%s", epnum, dir);
+	else
+		snprintf(hsep->name, sizeof(hsep->name), "%s", ep0name);
+
+	INIT_LIST_HEAD(&hsep->queue);
+	INIT_LIST_HEAD(&hsep->ep.ep_list);
+	if (epnum)
+		list_add_tail(&hsep->ep.ep_list, &hsudc->gadget.ep_list);
+
+	hsep->dev = hsudc;
+	hsep->ep.name = hsep->name;
+	hsep->ep.maxpacket = epnum ? 512 : 64;
+	hsep->ep.ops = &s3c_hsudc_ep_ops;
+	hsep->fifo = hsudc->regs + S3C_BR(epnum);
+	hsep->desc = 0;
+	hsep->stopped = 0;
+
+	set_index(hsudc, epnum);
+	writel(hsep->ep.maxpacket, hsudc->regs + S3C_MPR);
+}
+
+/**
+ * s3c_hsudc_setup_ep - Configure all endpoints to default state.
+ * @hsudc: Reference to device controller.
+ *
+ * Configures all endpoints to default state.
+ */
+static void s3c_hsudc_setup_ep(struct s3c_hsudc *hsudc)
+{
+	int epnum;
+
+	hsudc->ep0state = WAIT_FOR_SETUP;
+	INIT_LIST_HEAD(&hsudc->gadget.ep_list);
+	for (epnum = 0; epnum < hsudc->pd->epnum; epnum++)
+		s3c_hsudc_initep(hsudc, &hsudc->ep[epnum], epnum);
+}
+
+/**
+ * s3c_hsudc_reconfig - Reconfigure the device controller to default state.
+ * @hsudc: Reference to device controller.
+ *
+ * Reconfigures the device controller registers to a default state.
+ */
+static void s3c_hsudc_reconfig(struct s3c_hsudc *hsudc)
+{
+	writel(0xAA, hsudc->regs + S3C_EDR);
+	writel(1, hsudc->regs + S3C_EIER);
+	writel(0, hsudc->regs + S3C_TR);
+	writel(S3C_SCR_DTZIEN_EN | S3C_SCR_RRD_EN | S3C_SCR_SUS_EN |
+			S3C_SCR_RST_EN, hsudc->regs + S3C_SCR);
+	writel(0, hsudc->regs + S3C_EP0CR);
+
+	s3c_hsudc_setup_ep(hsudc);
+}
+
+/**
+ * s3c_hsudc_irq - Interrupt handler for device controller.
+ * @irq: Not used.
+ * @_dev: Reference to the device controller.
+ *
+ * Interrupt handler for the device controller. This handler handles controller
+ * interrupts and endpoint interrupts.
+ */
+static irqreturn_t s3c_hsudc_irq(int irq, void *_dev)
+{
+	struct s3c_hsudc *hsudc = _dev;
+	struct s3c_hsudc_ep *hsep;
+	u32 ep_intr;
+	u32 sys_status;
+	u32 ep_idx;
+
+	spin_lock(&hsudc->lock);
+
+	sys_status = readl(hsudc->regs + S3C_SSR);
+	ep_intr = readl(hsudc->regs + S3C_EIR) & 0x3FF;
+
+	if (!ep_intr && !(sys_status & S3C_SSR_DTZIEN_EN)) {
+		spin_unlock(&hsudc->lock);
+		return IRQ_HANDLED;
+	}
+
+	if (sys_status) {
+		if (sys_status & S3C_SSR_VBUSON)
+			writel(S3C_SSR_VBUSON, hsudc->regs + S3C_SSR);
+
+		if (sys_status & S3C_SSR_ERR)
+			writel(S3C_SSR_ERR, hsudc->regs + S3C_SSR);
+
+		if (sys_status & S3C_SSR_SDE) {
+			writel(S3C_SSR_SDE, hsudc->regs + S3C_SSR);
+			hsudc->gadget.speed = (sys_status & S3C_SSR_HSP) ?
+				USB_SPEED_HIGH : USB_SPEED_FULL;
+		}
+
+		if (sys_status & S3C_SSR_SUSPEND) {
+			writel(S3C_SSR_SUSPEND, hsudc->regs + S3C_SSR);
+			if (hsudc->gadget.speed != USB_SPEED_UNKNOWN
+				&& hsudc->driver && hsudc->driver->suspend)
+				hsudc->driver->suspend(&hsudc->gadget);
+		}
+
+		if (sys_status & S3C_SSR_RESUME) {
+			writel(S3C_SSR_RESUME, hsudc->regs + S3C_SSR);
+			if (hsudc->gadget.speed != USB_SPEED_UNKNOWN
+				&& hsudc->driver && hsudc->driver->resume)
+				hsudc->driver->resume(&hsudc->gadget);
+		}
+
+		if (sys_status & S3C_SSR_RESET) {
+			writel(S3C_SSR_RESET, hsudc->regs + S3C_SSR);
+			for (ep_idx = 0; ep_idx < hsudc->pd->epnum; ep_idx++) {
+				hsep = &hsudc->ep[ep_idx];
+				hsep->stopped = 1;
+				s3c_hsudc_nuke_ep(hsep, -ECONNRESET);
+			}
+			s3c_hsudc_reconfig(hsudc);
+			hsudc->ep0state = WAIT_FOR_SETUP;
+		}
+	}
+
+	if (ep_intr & S3C_EIR_EP0) {
+		writel(S3C_EIR_EP0, hsudc->regs + S3C_EIR);
+		set_index(hsudc, 0);
+		s3c_hsudc_handle_ep0_intr(hsudc);
+	}
+
+	ep_intr >>= 1;
+	ep_idx = 1;
+	while (ep_intr) {
+		if (ep_intr & 1)  {
+			hsep = &hsudc->ep[ep_idx];
+			set_index(hsudc, ep_idx);
+			writel(1 << ep_idx, hsudc->regs + S3C_EIR);
+			if (ep_is_in(hsep))
+				s3c_hsudc_epin_intr(hsudc, ep_idx);
+			else
+				s3c_hsudc_epout_intr(hsudc, ep_idx);
+		}
+		ep_intr >>= 1;
+		ep_idx++;
+	}
+
+	spin_unlock(&hsudc->lock);
+	return IRQ_HANDLED;
+}
+
+int usb_gadget_probe_driver(struct usb_gadget_driver *driver,
+               int (*bind)(struct usb_gadget *))
+{
+	struct s3c_hsudc *hsudc = the_controller;
+	int ret;
+
+	if (!driver
+		|| (driver->speed != USB_SPEED_FULL &&
+			driver->speed != USB_SPEED_HIGH)
+		|| !bind
+		|| !driver->unbind || !driver->disconnect || !driver->setup)
+		return -EINVAL;
+
+	if (!hsudc)
+		return -ENODEV;
+
+	if (hsudc->driver)
+		return -EBUSY;
+
+	hsudc->driver = driver;
+	hsudc->gadget.dev.driver = &driver->driver;
+	hsudc->gadget.speed = USB_SPEED_UNKNOWN;
+	ret = device_add(&hsudc->gadget.dev);
+	if (ret) {
+		dev_err(hsudc->dev, "failed to probe gadget device");
+		return ret;
+	}
+
+	ret = bind(&hsudc->gadget);
+	if (ret) {
+		dev_err(hsudc->dev, "%s: bind failed\n", hsudc->gadget.name);
+		device_del(&hsudc->gadget.dev);
+
+		hsudc->driver = NULL;
+		hsudc->gadget.dev.driver = NULL;
+		return ret;
+	}
+
+	enable_irq(hsudc->irq);
+	dev_info(hsudc->dev, "bound driver %s\n", driver->driver.name);
+
+	s3c_hsudc_reconfig(hsudc);
+	s3c_hsudc_init_phy();
+	if (hsudc->pd->gpio_init)
+		hsudc->pd->gpio_init();
+
+	return 0;
+}
+EXPORT_SYMBOL(usb_gadget_probe_driver);
+
+int usb_gadget_unregister_driver(struct usb_gadget_driver *driver)
+{
+	struct s3c_hsudc *hsudc = the_controller;
+	unsigned long flags;
+
+	if (!hsudc)
+		return -ENODEV;
+
+	if (!driver || driver != hsudc->driver || !driver->unbind)
+		return -EINVAL;
+
+	spin_lock_irqsave(&hsudc->lock, flags);
+	hsudc->driver = 0;
+	s3c_hsudc_uninit_phy();
+	if (hsudc->pd->gpio_uninit)
+		hsudc->pd->gpio_uninit();
+	s3c_hsudc_stop_activity(hsudc, driver);
+	spin_unlock_irqrestore(&hsudc->lock, flags);
+
+	driver->unbind(&hsudc->gadget);
+	device_del(&hsudc->gadget.dev);
+	disable_irq(hsudc->irq);
+
+	dev_info(hsudc->dev, "unregistered gadget driver '%s'\n",
+			driver->driver.name);
+	return 0;
+}
+EXPORT_SYMBOL(usb_gadget_unregister_driver);
+
+static inline u32 s3c_hsudc_read_frameno(struct s3c_hsudc *hsudc)
+{
+	return readl(hsudc->regs + S3C_FNR) & 0x3FF;
+}
+
+static int s3c_hsudc_gadget_getframe(struct usb_gadget *gadget)
+{
+	return s3c_hsudc_read_frameno(to_hsudc(gadget));
+}
+
+static struct usb_gadget_ops s3c_hsudc_gadget_ops = {
+	.get_frame	= s3c_hsudc_gadget_getframe,
+};
+
+static int s3c_hsudc_probe(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	struct resource *res;
+	struct s3c_hsudc *hsudc;
+	struct s3c24xx_hsudc_platdata *pd = pdev->dev.platform_data;
+	int ret;
+
+	hsudc = kzalloc(sizeof(struct s3c_hsudc) +
+			sizeof(struct s3c_hsudc_ep) * pd->epnum,
+			GFP_KERNEL);
+	if (!hsudc) {
+		dev_err(dev, "cannot allocate memory\n");
+		return -ENOMEM;
+	}
+
+	the_controller = hsudc;
+	platform_set_drvdata(pdev, dev);
+	hsudc->dev = dev;
+	hsudc->pd = pdev->dev.platform_data;
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!res) {
+		dev_err(dev, "unable to obtain driver resource data\n");
+		ret = -ENODEV;
+		goto err_res;
+	}
+
+	hsudc->mem_rsrc = request_mem_region(res->start, resource_size(res),
+				dev_name(&pdev->dev));
+	if (!hsudc->mem_rsrc) {
+		dev_err(dev, "failed to reserve register area\n");
+		ret = -ENODEV;
+		goto err_res;
+	}
+
+	hsudc->regs = ioremap(res->start, resource_size(res));
+	if (!hsudc->regs) {
+		dev_err(dev, "error mapping device register area\n");
+		ret = -EBUSY;
+		goto err_remap;
+	}
+
+	ret = platform_get_irq(pdev, 0);
+	if (ret < 0) {
+		dev_err(dev, "unable to obtain IRQ number\n");
+		goto err_irq;
+	}
+	hsudc->irq = ret;
+
+	ret = request_irq(hsudc->irq, s3c_hsudc_irq, 0, driver_name, hsudc);
+	if (ret < 0) {
+		dev_err(dev, "irq request failed\n");
+		goto err_irq;
+	}
+
+	spin_lock_init(&hsudc->lock);
+
+	device_initialize(&hsudc->gadget.dev);
+	dev_set_name(&hsudc->gadget.dev, "gadget");
+
+	hsudc->gadget.is_dualspeed = 1;
+	hsudc->gadget.ops = &s3c_hsudc_gadget_ops;
+	hsudc->gadget.name = dev_name(dev);
+	hsudc->gadget.dev.parent = dev;
+	hsudc->gadget.dev.dma_mask = dev->dma_mask;
+	hsudc->gadget.ep0 = &hsudc->ep[0].ep;
+
+	hsudc->gadget.is_otg = 0;
+	hsudc->gadget.is_a_peripheral = 0;
+
+	s3c_hsudc_setup_ep(hsudc);
+
+	hsudc->uclk = clk_get(&pdev->dev, "usb-device");
+	if (hsudc->uclk == NULL) {
+		dev_err(dev, "failed to find usb-device clock source\n");
+		return -ENOENT;
+	}
+	clk_enable(hsudc->uclk);
+
+	local_irq_disable();
+
+	disable_irq(hsudc->irq);
+	local_irq_enable();
+	return 0;
+
+err_irq:
+	iounmap(hsudc->regs);
+
+err_remap:
+	release_resource(hsudc->mem_rsrc);
+	kfree(hsudc->mem_rsrc);
+
+err_res:
+	kfree(hsudc);
+	return ret;
+}
+
+static struct platform_driver s3c_hsudc_driver = {
+	.driver		= {
+		.owner	= THIS_MODULE,
+		.name	= "s3c-hsudc",
+	},
+	.probe		= s3c_hsudc_probe,
+};
+
+static int __init s3c_hsudc_modinit(void)
+{
+	return platform_driver_register(&s3c_hsudc_driver);
+}
+
+static void __exit s3c_hsudc_modexit(void)
+{
+	platform_driver_unregister(&s3c_hsudc_driver);
+}
+
+module_init(s3c_hsudc_modinit);
+module_exit(s3c_hsudc_modexit);
+
+MODULE_DESCRIPTION("Samsung S3C24XX USB high-speed controller driver");
+MODULE_AUTHOR("Thomas Abraham <thomas.ab@samsung.com>");
+MODULE_LICENSE("GPL");
-- 
1.7.2.3

^ permalink raw reply related	[flat|nested] 52+ messages in thread

* [PATCH RESEND 4/4] ARM: S3C2416: Add support for USB 2.0 High-Speed gadget controller
  2011-03-23 21:35 ` Heiko Stübner
@ 2011-03-23 21:40   ` Heiko Stübner
  -1 siblings, 0 replies; 52+ messages in thread
From: Heiko Stübner @ 2011-03-23 21:40 UTC (permalink / raw)
  To: Ben Dooks
  Cc: Thomas Abraham, Sangbeom Kim, Kukjin Kim, linux-arm-kernel,
	linux-samsung-soc, linux-usb, dbrownell, Alexander Neumann

From: Thomas Abraham <thomas.ab@samsung.com>

Add support for USB 2.0 High-Speed gadget controller driver for Samsung's
S3C2416 processor.

Signed-off-by: Thomas Abraham <thomas.ab@samsung.com>
Signed-off-by: Sangbeom Kim <sbkim73@samsung.com>
---
 arch/arm/mach-s3c2416/mach-smdk2416.c |   27 +++++++++++++++++++++++++++
 1 files changed, 27 insertions(+), 0 deletions(-)

diff --git a/arch/arm/mach-s3c2416/mach-smdk2416.c b/arch/arm/mach-s3c2416/mach-smdk2416.c
index 3f83177..ac27ebb 100644
--- a/arch/arm/mach-s3c2416/mach-smdk2416.c
+++ b/arch/arm/mach-s3c2416/mach-smdk2416.c
@@ -23,6 +23,7 @@
 #include <linux/mtd/partitions.h>
 #include <linux/gpio.h>
 #include <linux/fb.h>
+#include <linux/delay.h>
 
 #include <asm/mach/arch.h>
 #include <asm/mach/map.h>
@@ -35,6 +36,7 @@
 #include <plat/regs-serial.h>
 #include <mach/regs-gpio.h>
 #include <mach/regs-lcd.h>
+#include <mach/regs-s3c2443-clock.h>
 
 #include <mach/idle.h>
 #include <mach/leds-gpio.h>
@@ -47,6 +49,7 @@
 #include <plat/cpu.h>
 #include <plat/nand.h>
 #include <plat/sdhci.h>
+#include <plat/udc.h>
 
 #include <plat/regs-fb-v4.h>
 #include <plat/fb.h>
@@ -121,6 +124,27 @@ static struct s3c2410_uartcfg smdk2416_uartcfgs[] __initdata = {
 	}
 };
 
+void smdk2416_hsudc_gpio_init(void)
+{
+	s3c_gpio_setpull(S3C2410_GPH(14), S3C_GPIO_PULL_UP);
+	s3c_gpio_setpull(S3C2410_GPF(2), S3C_GPIO_PULL_NONE);
+	s3c_gpio_cfgpin(S3C2410_GPH(14), S3C_GPIO_SFN(1));
+	s3c2410_modify_misccr(S3C2416_MISCCR_SEL_SUSPND, 0);
+}
+
+void smdk2416_hsudc_gpio_uninit(void)
+{
+	s3c2410_modify_misccr(S3C2416_MISCCR_SEL_SUSPND, 1);
+	s3c_gpio_setpull(S3C2410_GPH(14), S3C_GPIO_PULL_NONE);
+	s3c_gpio_cfgpin(S3C2410_GPH(14), S3C_GPIO_SFN(0));
+}
+
+struct s3c24xx_hsudc_platdata smdk2416_hsudc_platdata = {
+	.epnum = 9,
+	.gpio_init = smdk2416_hsudc_gpio_init,
+	.gpio_uninit = smdk2416_hsudc_gpio_uninit,
+};
+
 struct s3c_fb_pd_win smdk2416_fb_win[] = {
 	[0] = {
 		/* think this is the same as the smdk6410 */
@@ -186,6 +210,7 @@ static struct platform_device *smdk2416_devices[] __initdata = {
 	&s3c_device_i2c0,
 	&s3c_device_hsmmc0,
 	&s3c_device_hsmmc1,
+	&s3c_device_usb_hsudc,
 };
 
 static void __init smdk2416_map_io(void)
@@ -203,6 +228,8 @@ static void __init smdk2416_machine_init(void)
 	s3c_sdhci0_set_platdata(&smdk2416_hsmmc0_pdata);
 	s3c_sdhci1_set_platdata(&smdk2416_hsmmc1_pdata);
 
+	s3c24xx_hsudc_set_platdata(&smdk2416_hsudc_platdata);
+
 	gpio_request(S3C2410_GPB(4), "USBHost Power");
 	gpio_direction_output(S3C2410_GPB(4), 1);
 
-- 
1.7.2.3

^ permalink raw reply related	[flat|nested] 52+ messages in thread

* [PATCH RESEND 4/4] ARM: S3C2416: Add support for USB 2.0 High-Speed gadget controller
@ 2011-03-23 21:40   ` Heiko Stübner
  0 siblings, 0 replies; 52+ messages in thread
From: Heiko Stübner @ 2011-03-23 21:40 UTC (permalink / raw)
  To: linux-arm-kernel

From: Thomas Abraham <thomas.ab@samsung.com>

Add support for USB 2.0 High-Speed gadget controller driver for Samsung's
S3C2416 processor.

Signed-off-by: Thomas Abraham <thomas.ab@samsung.com>
Signed-off-by: Sangbeom Kim <sbkim73@samsung.com>
---
 arch/arm/mach-s3c2416/mach-smdk2416.c |   27 +++++++++++++++++++++++++++
 1 files changed, 27 insertions(+), 0 deletions(-)

diff --git a/arch/arm/mach-s3c2416/mach-smdk2416.c b/arch/arm/mach-s3c2416/mach-smdk2416.c
index 3f83177..ac27ebb 100644
--- a/arch/arm/mach-s3c2416/mach-smdk2416.c
+++ b/arch/arm/mach-s3c2416/mach-smdk2416.c
@@ -23,6 +23,7 @@
 #include <linux/mtd/partitions.h>
 #include <linux/gpio.h>
 #include <linux/fb.h>
+#include <linux/delay.h>
 
 #include <asm/mach/arch.h>
 #include <asm/mach/map.h>
@@ -35,6 +36,7 @@
 #include <plat/regs-serial.h>
 #include <mach/regs-gpio.h>
 #include <mach/regs-lcd.h>
+#include <mach/regs-s3c2443-clock.h>
 
 #include <mach/idle.h>
 #include <mach/leds-gpio.h>
@@ -47,6 +49,7 @@
 #include <plat/cpu.h>
 #include <plat/nand.h>
 #include <plat/sdhci.h>
+#include <plat/udc.h>
 
 #include <plat/regs-fb-v4.h>
 #include <plat/fb.h>
@@ -121,6 +124,27 @@ static struct s3c2410_uartcfg smdk2416_uartcfgs[] __initdata = {
 	}
 };
 
+void smdk2416_hsudc_gpio_init(void)
+{
+	s3c_gpio_setpull(S3C2410_GPH(14), S3C_GPIO_PULL_UP);
+	s3c_gpio_setpull(S3C2410_GPF(2), S3C_GPIO_PULL_NONE);
+	s3c_gpio_cfgpin(S3C2410_GPH(14), S3C_GPIO_SFN(1));
+	s3c2410_modify_misccr(S3C2416_MISCCR_SEL_SUSPND, 0);
+}
+
+void smdk2416_hsudc_gpio_uninit(void)
+{
+	s3c2410_modify_misccr(S3C2416_MISCCR_SEL_SUSPND, 1);
+	s3c_gpio_setpull(S3C2410_GPH(14), S3C_GPIO_PULL_NONE);
+	s3c_gpio_cfgpin(S3C2410_GPH(14), S3C_GPIO_SFN(0));
+}
+
+struct s3c24xx_hsudc_platdata smdk2416_hsudc_platdata = {
+	.epnum = 9,
+	.gpio_init = smdk2416_hsudc_gpio_init,
+	.gpio_uninit = smdk2416_hsudc_gpio_uninit,
+};
+
 struct s3c_fb_pd_win smdk2416_fb_win[] = {
 	[0] = {
 		/* think this is the same as the smdk6410 */
@@ -186,6 +210,7 @@ static struct platform_device *smdk2416_devices[] __initdata = {
 	&s3c_device_i2c0,
 	&s3c_device_hsmmc0,
 	&s3c_device_hsmmc1,
+	&s3c_device_usb_hsudc,
 };
 
 static void __init smdk2416_map_io(void)
@@ -203,6 +228,8 @@ static void __init smdk2416_machine_init(void)
 	s3c_sdhci0_set_platdata(&smdk2416_hsmmc0_pdata);
 	s3c_sdhci1_set_platdata(&smdk2416_hsmmc1_pdata);
 
+	s3c24xx_hsudc_set_platdata(&smdk2416_hsudc_platdata);
+
 	gpio_request(S3C2410_GPB(4), "USBHost Power");
 	gpio_direction_output(S3C2410_GPB(4), 1);
 
-- 
1.7.2.3

^ permalink raw reply related	[flat|nested] 52+ messages in thread

* Re: [PATCH RESEND 0/4] USB: gadget: Add Samsung S3C24xx USB HS controller driver
  2011-03-23 21:35 ` Heiko Stübner
@ 2011-04-08 10:04   ` Heiko Stübner
  -1 siblings, 0 replies; 52+ messages in thread
From: Heiko Stübner @ 2011-04-08 10:04 UTC (permalink / raw)
  To: linux-arm-kernel, Ben Dooks, Kukjin Kim
  Cc: Thomas Abraham, Sangbeom Kim, linux-samsung-soc, linux-usb


Am Mittwoch 23 März 2011 schrieb Heiko Stübner:
> On 2010-10-13 Sangbeom Kim sent 4 patches to add support the USB High-Speed
> device controller module on Samsung's S3C2416, S3C2443 and S3C2450. [1]
> 
> bdooks worte on 2010-10-22:
> > patch 3/4 needs a bit of work, otherwise looks ok.
> > I'll look at applying the s3c24xx items in the next day or so.
> 
> On 2010-10-23 Kukjin Kim send a revised version of patch 3.
> It seems the patches were forgotten after this.
> 
> We resurrected them while working on a s3c2416 based device. Patch 3 needed
> an update to work with current kernels. The other 3 patches were not
> modified.
> 
> Tests, comments, etc very welcome.
anyone?

If someone could tell me if I'm doing something wrong I would be really 
grateful.

Heiko

^ permalink raw reply	[flat|nested] 52+ messages in thread

* [PATCH RESEND 0/4] USB: gadget: Add Samsung S3C24xx USB HS controller driver
@ 2011-04-08 10:04   ` Heiko Stübner
  0 siblings, 0 replies; 52+ messages in thread
From: Heiko Stübner @ 2011-04-08 10:04 UTC (permalink / raw)
  To: linux-arm-kernel


Am Mittwoch 23 M?rz 2011 schrieb Heiko St?bner:
> On 2010-10-13 Sangbeom Kim sent 4 patches to add support the USB High-Speed
> device controller module on Samsung's S3C2416, S3C2443 and S3C2450. [1]
> 
> bdooks worte on 2010-10-22:
> > patch 3/4 needs a bit of work, otherwise looks ok.
> > I'll look at applying the s3c24xx items in the next day or so.
> 
> On 2010-10-23 Kukjin Kim send a revised version of patch 3.
> It seems the patches were forgotten after this.
> 
> We resurrected them while working on a s3c2416 based device. Patch 3 needed
> an update to work with current kernels. The other 3 patches were not
> modified.
> 
> Tests, comments, etc very welcome.
anyone?

If someone could tell me if I'm doing something wrong I would be really 
grateful.

Heiko

^ permalink raw reply	[flat|nested] 52+ messages in thread

* Re: [PATCH RESEND 0/4] USB: gadget: Add Samsung S3C24xx USB HS controller driver
  2011-04-08 10:04   ` Heiko Stübner
@ 2011-04-08 20:52     ` Greg KH
  -1 siblings, 0 replies; 52+ messages in thread
From: Greg KH @ 2011-04-08 20:52 UTC (permalink / raw)
  To: Heiko Stübner
  Cc: linux-arm-kernel, Ben Dooks, Kukjin Kim, Thomas Abraham,
	Sangbeom Kim, linux-samsung-soc, linux-usb

On Fri, Apr 08, 2011 at 12:04:41PM +0200, Heiko Stübner wrote:
> 
> Am Mittwoch 23 März 2011 schrieb Heiko Stübner:
> > On 2010-10-13 Sangbeom Kim sent 4 patches to add support the USB High-Speed
> > device controller module on Samsung's S3C2416, S3C2443 and S3C2450. [1]
> > 
> > bdooks worte on 2010-10-22:
> > > patch 3/4 needs a bit of work, otherwise looks ok.
> > > I'll look at applying the s3c24xx items in the next day or so.
> > 
> > On 2010-10-23 Kukjin Kim send a revised version of patch 3.
> > It seems the patches were forgotten after this.
> > 
> > We resurrected them while working on a s3c2416 based device. Patch 3 needed
> > an update to work with current kernels. The other 3 patches were not
> > modified.
> > 
> > Tests, comments, etc very welcome.
> anyone?
> 
> If someone could tell me if I'm doing something wrong I would be really 
> grateful.

Sorry, been on vacation and at conferences, give me some time next week
to catch up on usb email and I will get back to this...

thanks,

greg k-h

^ permalink raw reply	[flat|nested] 52+ messages in thread

* [PATCH RESEND 0/4] USB: gadget: Add Samsung S3C24xx USB HS controller driver
@ 2011-04-08 20:52     ` Greg KH
  0 siblings, 0 replies; 52+ messages in thread
From: Greg KH @ 2011-04-08 20:52 UTC (permalink / raw)
  To: linux-arm-kernel

On Fri, Apr 08, 2011 at 12:04:41PM +0200, Heiko St?bner wrote:
> 
> Am Mittwoch 23 M?rz 2011 schrieb Heiko St?bner:
> > On 2010-10-13 Sangbeom Kim sent 4 patches to add support the USB High-Speed
> > device controller module on Samsung's S3C2416, S3C2443 and S3C2450. [1]
> > 
> > bdooks worte on 2010-10-22:
> > > patch 3/4 needs a bit of work, otherwise looks ok.
> > > I'll look at applying the s3c24xx items in the next day or so.
> > 
> > On 2010-10-23 Kukjin Kim send a revised version of patch 3.
> > It seems the patches were forgotten after this.
> > 
> > We resurrected them while working on a s3c2416 based device. Patch 3 needed
> > an update to work with current kernels. The other 3 patches were not
> > modified.
> > 
> > Tests, comments, etc very welcome.
> anyone?
> 
> If someone could tell me if I'm doing something wrong I would be really 
> grateful.

Sorry, been on vacation and at conferences, give me some time next week
to catch up on usb email and I will get back to this...

thanks,

greg k-h

^ permalink raw reply	[flat|nested] 52+ messages in thread

* Re: [PATCH RESEND v3 3/4] USB: Gadget: Add Samsung S3C24XX USB High-Speed controller driver
  2011-03-23 21:39   ` Heiko Stübner
@ 2011-04-13 23:28     ` Greg KH
  -1 siblings, 0 replies; 52+ messages in thread
From: Greg KH @ 2011-04-13 23:28 UTC (permalink / raw)
  To: Heiko Stübner
  Cc: Ben Dooks, Thomas Abraham, Sangbeom Kim, Kukjin Kim,
	linux-arm-kernel, linux-samsung-soc, linux-usb, dbrownell,
	Alexander Neumann

On Wed, Mar 23, 2011 at 10:39:28PM +0100, Heiko Stübner wrote:
> From: Thomas Abraham <thomas.ab@samsung.com>
> 
> The Samsung's S3C2416, S3C2443 and S3C2450 includes a USB High-Speed
> device controller module. This driver enables support for USB high-speed
> gadget functionality for the Samsung S3C24xx SoC's that include this
> controller.
> 
> Signed-off-by: Thomas Abraham <thomas.ab@samsung.com>
> Signed-off-by: Sangbeom Kim <sbkim73@samsung.com>
> Signed-off-by: Kukjin Kim <kgene.kim@samsung.com>
> ---
> Changes since v1:
> - Addressed comments from Ben Dooks
> 
> Changes since v2:
> - Update the driver to match the data structure changes introduced by
>   commit b0fca50f5a94a268ed02cfddf44448051ed9343f.
>   Alexander Neumann <alexander@debian.org>
> - Move s3c_hsudc to 0x29 in usb_gadget_controller_number in gadget_chips.h
>   Heiko Stuebner <heiko@sntech.de>

The Renesas driver got number 0x29, so this patch no longer applies :(

Care to respin this whole series against the next linux-next tree and
resend it to me so I can apply it?

thanks,

greg k-h

^ permalink raw reply	[flat|nested] 52+ messages in thread

* [PATCH RESEND v3 3/4] USB: Gadget: Add Samsung S3C24XX USB High-Speed controller driver
@ 2011-04-13 23:28     ` Greg KH
  0 siblings, 0 replies; 52+ messages in thread
From: Greg KH @ 2011-04-13 23:28 UTC (permalink / raw)
  To: linux-arm-kernel

On Wed, Mar 23, 2011 at 10:39:28PM +0100, Heiko St?bner wrote:
> From: Thomas Abraham <thomas.ab@samsung.com>
> 
> The Samsung's S3C2416, S3C2443 and S3C2450 includes a USB High-Speed
> device controller module. This driver enables support for USB high-speed
> gadget functionality for the Samsung S3C24xx SoC's that include this
> controller.
> 
> Signed-off-by: Thomas Abraham <thomas.ab@samsung.com>
> Signed-off-by: Sangbeom Kim <sbkim73@samsung.com>
> Signed-off-by: Kukjin Kim <kgene.kim@samsung.com>
> ---
> Changes since v1:
> - Addressed comments from Ben Dooks
> 
> Changes since v2:
> - Update the driver to match the data structure changes introduced by
>   commit b0fca50f5a94a268ed02cfddf44448051ed9343f.
>   Alexander Neumann <alexander@debian.org>
> - Move s3c_hsudc to 0x29 in usb_gadget_controller_number in gadget_chips.h
>   Heiko Stuebner <heiko@sntech.de>

The Renesas driver got number 0x29, so this patch no longer applies :(

Care to respin this whole series against the next linux-next tree and
resend it to me so I can apply it?

thanks,

greg k-h

^ permalink raw reply	[flat|nested] 52+ messages in thread

* [PATCH RESEND v2 0/4] USB: gadget: Add Samsung S3C24xx USB HS controller driver
  2011-04-13 23:28     ` Greg KH
@ 2011-04-14  9:06       ` Heiko Stübner
  -1 siblings, 0 replies; 52+ messages in thread
From: Heiko Stübner @ 2011-04-14  9:06 UTC (permalink / raw)
  To: Greg KH, Kukjin Kim
  Cc: Ben Dooks, Thomas Abraham, Sangbeom Kim, linux-arm-kernel,
	linux-samsung-soc, linux-usb, Alexander Neumann,
	Heiko Stübner

Am Donnerstag 14 April 2011 schrieb Greg KH:
> The Renesas driver got number 0x29, so this patch no longer applies :(
> 
> Care to respin this whole series against the next linux-next tree and
> resend it to me so I can apply it?
As requested by greg k-h, again the whole 4-patch series against most current 
linux-next.

Heiko

^ permalink raw reply	[flat|nested] 52+ messages in thread

* [PATCH RESEND v2 0/4] USB: gadget: Add Samsung S3C24xx USB HS controller driver
@ 2011-04-14  9:06       ` Heiko Stübner
  0 siblings, 0 replies; 52+ messages in thread
From: Heiko Stübner @ 2011-04-14  9:06 UTC (permalink / raw)
  To: linux-arm-kernel

Am Donnerstag 14 April 2011 schrieb Greg KH:
> The Renesas driver got number 0x29, so this patch no longer applies :(
> 
> Care to respin this whole series against the next linux-next tree and
> resend it to me so I can apply it?
As requested by greg k-h, again the whole 4-patch series against most current 
linux-next.

Heiko

^ permalink raw reply	[flat|nested] 52+ messages in thread

* [PATCH 1/4] ARM: S3C2416: Add USB Phy register definitions
  2011-04-14  9:06       ` Heiko Stübner
@ 2011-04-14  9:08         ` Heiko Stübner
  -1 siblings, 0 replies; 52+ messages in thread
From: Heiko Stübner @ 2011-04-14  9:08 UTC (permalink / raw)
  To: Greg KH, Kukjin Kim
  Cc: Ben Dooks, Thomas Abraham, Sangbeom Kim, linux-arm-kernel,
	linux-samsung-soc, linux-usb, Alexander Neumann

From: Thomas Abraham <thomas.ab@samsung.com>

Add register definitions required to configure the USB Phy. The definitions
for PHYCTRL, PHYPWR, URSTCON and UCLKCON registers and corresponding bit
field definitions are added.

Signed-off-by: Thomas Abraham <thomas.ab@samsung.com>
Signed-off-by: Sangbeom Kim <sbkim73@samsung.com>
---
 .../mach-s3c2410/include/mach/regs-s3c2443-clock.h |   25 ++++++++++++++++++++
 1 files changed, 25 insertions(+), 0 deletions(-)

diff --git a/arch/arm/mach-s3c2410/include/mach/regs-s3c2443-clock.h b/arch/arm/mach-s3c2410/include/mach/regs-s3c2443-clock.h
index 44494a5..5e06c72 100644
--- a/arch/arm/mach-s3c2410/include/mach/regs-s3c2443-clock.h
+++ b/arch/arm/mach-s3c2410/include/mach/regs-s3c2443-clock.h
@@ -37,6 +37,10 @@
 #define S3C2443_SYSID			S3C2443_CLKREG(0x5C)
 #define S3C2443_PWRCFG			S3C2443_CLKREG(0x60)
 #define S3C2443_RSTCON			S3C2443_CLKREG(0x64)
+#define S3C2443_PHYCTRL			S3C2443_CLKREG(0x80)
+#define S3C2443_PHYPWR			S3C2443_CLKREG(0x84)
+#define S3C2443_URSTCON			S3C2443_CLKREG(0x88)
+#define S3C2443_UCLKCON			S3C2443_CLKREG(0x8C)
 
 #define S3C2443_SWRST_RESET		(0x533c2443)
 
@@ -121,6 +125,27 @@
 
 #define S3C2443_PWRCFG_SLEEP		(1<<15)
 
+#define S3C2443_PWRCFG_USBPHY		(1 << 4)
+
+#define S3C2443_URSTCON_FUNCRST		(1 << 2)
+#define S3C2443_URSTCON_PHYRST		(1 << 0)
+
+#define S3C2443_PHYCTRL_CLKSEL		(1 << 3)
+#define S3C2443_PHYCTRL_EXTCLK		(1 << 2)
+#define S3C2443_PHYCTRL_PLLSEL		(1 << 1)
+#define S3C2443_PHYCTRL_DSPORT		(1 << 0)
+
+#define S3C2443_PHYPWR_COMMON_ON	(1 << 31)
+#define S3C2443_PHYPWR_ANALOG_PD	(1 << 4)
+#define S3C2443_PHYPWR_PLL_REFCLK	(1 << 3)
+#define S3C2443_PHYPWR_XO_ON		(1 << 2)
+#define S3C2443_PHYPWR_PLL_PWRDN	(1 << 1)
+#define S3C2443_PHYPWR_FSUSPEND		(1 << 0)
+
+#define S3C2443_UCLKCON_DETECT_VBUS	(1 << 31)
+#define S3C2443_UCLKCON_FUNC_CLKEN	(1 << 2)
+#define S3C2443_UCLKCON_TCLKEN		(1 << 0)
+
 #include <asm/div64.h>
 
 static inline unsigned int
-- 
1.7.1

^ permalink raw reply related	[flat|nested] 52+ messages in thread

* [PATCH 1/4] ARM: S3C2416: Add USB Phy register definitions
@ 2011-04-14  9:08         ` Heiko Stübner
  0 siblings, 0 replies; 52+ messages in thread
From: Heiko Stübner @ 2011-04-14  9:08 UTC (permalink / raw)
  To: linux-arm-kernel

From: Thomas Abraham <thomas.ab@samsung.com>

Add register definitions required to configure the USB Phy. The definitions
for PHYCTRL, PHYPWR, URSTCON and UCLKCON registers and corresponding bit
field definitions are added.

Signed-off-by: Thomas Abraham <thomas.ab@samsung.com>
Signed-off-by: Sangbeom Kim <sbkim73@samsung.com>
---
 .../mach-s3c2410/include/mach/regs-s3c2443-clock.h |   25 ++++++++++++++++++++
 1 files changed, 25 insertions(+), 0 deletions(-)

diff --git a/arch/arm/mach-s3c2410/include/mach/regs-s3c2443-clock.h b/arch/arm/mach-s3c2410/include/mach/regs-s3c2443-clock.h
index 44494a5..5e06c72 100644
--- a/arch/arm/mach-s3c2410/include/mach/regs-s3c2443-clock.h
+++ b/arch/arm/mach-s3c2410/include/mach/regs-s3c2443-clock.h
@@ -37,6 +37,10 @@
 #define S3C2443_SYSID			S3C2443_CLKREG(0x5C)
 #define S3C2443_PWRCFG			S3C2443_CLKREG(0x60)
 #define S3C2443_RSTCON			S3C2443_CLKREG(0x64)
+#define S3C2443_PHYCTRL			S3C2443_CLKREG(0x80)
+#define S3C2443_PHYPWR			S3C2443_CLKREG(0x84)
+#define S3C2443_URSTCON			S3C2443_CLKREG(0x88)
+#define S3C2443_UCLKCON			S3C2443_CLKREG(0x8C)
 
 #define S3C2443_SWRST_RESET		(0x533c2443)
 
@@ -121,6 +125,27 @@
 
 #define S3C2443_PWRCFG_SLEEP		(1<<15)
 
+#define S3C2443_PWRCFG_USBPHY		(1 << 4)
+
+#define S3C2443_URSTCON_FUNCRST		(1 << 2)
+#define S3C2443_URSTCON_PHYRST		(1 << 0)
+
+#define S3C2443_PHYCTRL_CLKSEL		(1 << 3)
+#define S3C2443_PHYCTRL_EXTCLK		(1 << 2)
+#define S3C2443_PHYCTRL_PLLSEL		(1 << 1)
+#define S3C2443_PHYCTRL_DSPORT		(1 << 0)
+
+#define S3C2443_PHYPWR_COMMON_ON	(1 << 31)
+#define S3C2443_PHYPWR_ANALOG_PD	(1 << 4)
+#define S3C2443_PHYPWR_PLL_REFCLK	(1 << 3)
+#define S3C2443_PHYPWR_XO_ON		(1 << 2)
+#define S3C2443_PHYPWR_PLL_PWRDN	(1 << 1)
+#define S3C2443_PHYPWR_FSUSPEND		(1 << 0)
+
+#define S3C2443_UCLKCON_DETECT_VBUS	(1 << 31)
+#define S3C2443_UCLKCON_FUNC_CLKEN	(1 << 2)
+#define S3C2443_UCLKCON_TCLKEN		(1 << 0)
+
 #include <asm/div64.h>
 
 static inline unsigned int
-- 
1.7.1

^ permalink raw reply related	[flat|nested] 52+ messages in thread

* [PATCH 2/4] ARM: S3C24XX: Add plaform device definition for USB High-Speed gadget controller.
  2011-04-14  9:06       ` Heiko Stübner
@ 2011-04-14  9:10         ` Heiko Stübner
  -1 siblings, 0 replies; 52+ messages in thread
From: Heiko Stübner @ 2011-04-14  9:10 UTC (permalink / raw)
  To: Greg KH, Kukjin Kim
  Cc: Ben Dooks, Thomas Abraham, Sangbeom Kim, linux-arm-kernel,
	linux-samsung-soc, linux-usb, Alexander Neumann

From: Thomas Abraham <thomas.ab@samsung.com>

S3C2416, S3C2443 and S3C2450 includes a USB High-Speed Gadget controller module.
This patch adds the following for supporting this controller.

1. Definition for USB High-Speed controller base address.
2. Platform device instantiation.
3. Declaration for platform data structure.
4. Functionality to setup platform data for the controller.

Signed-off-by: Thomas Abraham <thomas.ab@samsung.com>
Signed-off-by: Sangbeom Kim <sbkim73@samsung.com>
---
 arch/arm/mach-s3c2410/include/mach/map.h  |    4 +++
 arch/arm/plat-s3c24xx/devs.c              |   41 +++++++++++++++++++++++++++++
 arch/arm/plat-s3c24xx/include/plat/udc.h  |   17 ++++++++++++
 arch/arm/plat-samsung/include/plat/devs.h |    1 +
 4 files changed, 63 insertions(+), 0 deletions(-)

diff --git a/arch/arm/mach-s3c2410/include/mach/map.h b/arch/arm/mach-s3c2410/include/mach/map.h
index 25bbf5a..425552d 100644
--- a/arch/arm/mach-s3c2410/include/mach/map.h
+++ b/arch/arm/mach-s3c2410/include/mach/map.h
@@ -21,6 +21,10 @@
 /* USB host controller */
 #define S3C2410_PA_USBHOST (0x49000000)
 
+/* S3C2416/S3C2443/S3C2450 High-Speed USB Gadget */
+#define S3C2416_PA_HSUDC	(0x49800000)
+#define S3C2416_SZ_HSUDC	(SZ_4K)
+
 /* DMA controller */
 #define S3C2410_PA_DMA	   (0x4B000000)
 #define S3C24XX_SZ_DMA	   SZ_1M
diff --git a/arch/arm/plat-s3c24xx/devs.c b/arch/arm/plat-s3c24xx/devs.c
index 268f3ed..7366799 100644
--- a/arch/arm/plat-s3c24xx/devs.c
+++ b/arch/arm/plat-s3c24xx/devs.c
@@ -22,6 +22,7 @@
 #include <linux/io.h>
 #include <linux/slab.h>
 #include <linux/string.h>
+#include <linux/dma-mapping.h>
 
 #include <asm/mach/arch.h>
 #include <asm/mach/map.h>
@@ -233,6 +234,46 @@ void __init s3c24xx_udc_set_platdata(struct s3c2410_udc_mach_info *pd)
 	}
 }
 
+/* USB High Speed 2.0 Device (Gadget) */
+static struct resource s3c_hsudc_resource[] = {
+	[0] = {
+		.start	= S3C2416_PA_HSUDC,
+		.end	= S3C2416_PA_HSUDC + S3C2416_SZ_HSUDC - 1,
+		.flags	= IORESOURCE_MEM,
+	},
+	[1] = {
+		.start	= IRQ_USBD,
+		.end	= IRQ_USBD,
+		.flags	= IORESOURCE_IRQ,
+	}
+};
+
+static u64 s3c_hsudc_dmamask = DMA_BIT_MASK(32);
+
+struct platform_device s3c_device_usb_hsudc = {
+	.name		= "s3c-hsudc",
+	.id		= -1,
+	.num_resources	= ARRAY_SIZE(s3c_hsudc_resource),
+	.resource	= s3c_hsudc_resource,
+	.dev		= {
+		.dma_mask		= &s3c_hsudc_dmamask,
+		.coherent_dma_mask	= DMA_BIT_MASK(32),
+	},
+};
+
+void __init s3c24xx_hsudc_set_platdata(struct s3c24xx_hsudc_platdata *pd)
+{
+	struct s3c24xx_hsudc_platdata *npd;
+
+	npd = kmalloc(sizeof(*npd), GFP_KERNEL);
+	if (npd) {
+		memcpy(npd, pd, sizeof(*npd));
+		s3c_device_usb_hsudc.dev.platform_data = npd;
+	} else {
+		printk(KERN_ERR "no memory for udc platform data\n");
+	}
+}
+
 /* IIS */
 
 static struct resource s3c_iis_resource[] = {
diff --git a/arch/arm/plat-s3c24xx/include/plat/udc.h b/arch/arm/plat-s3c24xx/include/plat/udc.h
index 80457c6..f638842 100644
--- a/arch/arm/plat-s3c24xx/include/plat/udc.h
+++ b/arch/arm/plat-s3c24xx/include/plat/udc.h
@@ -37,4 +37,21 @@ struct s3c2410_udc_mach_info {
 
 extern void __init s3c24xx_udc_set_platdata(struct s3c2410_udc_mach_info *);
 
+/**
+ * s3c24xx_hsudc_platdata - Platform data for USB High-Speed gadget controller.
+ * @epnum: Number of endpoints to be instantiated by the controller driver.
+ * @gpio_init: Platform specific USB related GPIO initialization.
+ * @gpio_uninit: Platform specific USB releted GPIO uninitialzation.
+ *
+ * Representation of platform data for the S3C24XX USB 2.0 High Speed gadget
+ * controllers.
+ */
+struct s3c24xx_hsudc_platdata {
+	unsigned int	epnum;
+	void		(*gpio_init)(void);
+	void		(*gpio_uninit)(void);
+};
+
+extern void __init s3c24xx_hsudc_set_platdata(struct s3c24xx_hsudc_platdata *pd);
+
 #endif /* __ASM_ARM_ARCH_UDC_H */
diff --git a/arch/arm/plat-samsung/include/plat/devs.h b/arch/arm/plat-samsung/include/plat/devs.h
index 3f38deb..39818d8 100644
--- a/arch/arm/plat-samsung/include/plat/devs.h
+++ b/arch/arm/plat-samsung/include/plat/devs.h
@@ -88,6 +88,7 @@ extern struct platform_device s3c64xx_device_onenand1;
 extern struct platform_device s5p_device_onenand;
 
 extern struct platform_device s3c_device_usbgadget;
+extern struct platform_device s3c_device_usb_hsudc;
 extern struct platform_device s3c_device_usb_hsotg;
 
 extern struct platform_device s5pv210_device_ac97;
-- 
1.7.1

^ permalink raw reply related	[flat|nested] 52+ messages in thread

* [PATCH 2/4] ARM: S3C24XX: Add plaform device definition for USB High-Speed gadget controller.
@ 2011-04-14  9:10         ` Heiko Stübner
  0 siblings, 0 replies; 52+ messages in thread
From: Heiko Stübner @ 2011-04-14  9:10 UTC (permalink / raw)
  To: linux-arm-kernel

From: Thomas Abraham <thomas.ab@samsung.com>

S3C2416, S3C2443 and S3C2450 includes a USB High-Speed Gadget controller module.
This patch adds the following for supporting this controller.

1. Definition for USB High-Speed controller base address.
2. Platform device instantiation.
3. Declaration for platform data structure.
4. Functionality to setup platform data for the controller.

Signed-off-by: Thomas Abraham <thomas.ab@samsung.com>
Signed-off-by: Sangbeom Kim <sbkim73@samsung.com>
---
 arch/arm/mach-s3c2410/include/mach/map.h  |    4 +++
 arch/arm/plat-s3c24xx/devs.c              |   41 +++++++++++++++++++++++++++++
 arch/arm/plat-s3c24xx/include/plat/udc.h  |   17 ++++++++++++
 arch/arm/plat-samsung/include/plat/devs.h |    1 +
 4 files changed, 63 insertions(+), 0 deletions(-)

diff --git a/arch/arm/mach-s3c2410/include/mach/map.h b/arch/arm/mach-s3c2410/include/mach/map.h
index 25bbf5a..425552d 100644
--- a/arch/arm/mach-s3c2410/include/mach/map.h
+++ b/arch/arm/mach-s3c2410/include/mach/map.h
@@ -21,6 +21,10 @@
 /* USB host controller */
 #define S3C2410_PA_USBHOST (0x49000000)
 
+/* S3C2416/S3C2443/S3C2450 High-Speed USB Gadget */
+#define S3C2416_PA_HSUDC	(0x49800000)
+#define S3C2416_SZ_HSUDC	(SZ_4K)
+
 /* DMA controller */
 #define S3C2410_PA_DMA	   (0x4B000000)
 #define S3C24XX_SZ_DMA	   SZ_1M
diff --git a/arch/arm/plat-s3c24xx/devs.c b/arch/arm/plat-s3c24xx/devs.c
index 268f3ed..7366799 100644
--- a/arch/arm/plat-s3c24xx/devs.c
+++ b/arch/arm/plat-s3c24xx/devs.c
@@ -22,6 +22,7 @@
 #include <linux/io.h>
 #include <linux/slab.h>
 #include <linux/string.h>
+#include <linux/dma-mapping.h>
 
 #include <asm/mach/arch.h>
 #include <asm/mach/map.h>
@@ -233,6 +234,46 @@ void __init s3c24xx_udc_set_platdata(struct s3c2410_udc_mach_info *pd)
 	}
 }
 
+/* USB High Speed 2.0 Device (Gadget) */
+static struct resource s3c_hsudc_resource[] = {
+	[0] = {
+		.start	= S3C2416_PA_HSUDC,
+		.end	= S3C2416_PA_HSUDC + S3C2416_SZ_HSUDC - 1,
+		.flags	= IORESOURCE_MEM,
+	},
+	[1] = {
+		.start	= IRQ_USBD,
+		.end	= IRQ_USBD,
+		.flags	= IORESOURCE_IRQ,
+	}
+};
+
+static u64 s3c_hsudc_dmamask = DMA_BIT_MASK(32);
+
+struct platform_device s3c_device_usb_hsudc = {
+	.name		= "s3c-hsudc",
+	.id		= -1,
+	.num_resources	= ARRAY_SIZE(s3c_hsudc_resource),
+	.resource	= s3c_hsudc_resource,
+	.dev		= {
+		.dma_mask		= &s3c_hsudc_dmamask,
+		.coherent_dma_mask	= DMA_BIT_MASK(32),
+	},
+};
+
+void __init s3c24xx_hsudc_set_platdata(struct s3c24xx_hsudc_platdata *pd)
+{
+	struct s3c24xx_hsudc_platdata *npd;
+
+	npd = kmalloc(sizeof(*npd), GFP_KERNEL);
+	if (npd) {
+		memcpy(npd, pd, sizeof(*npd));
+		s3c_device_usb_hsudc.dev.platform_data = npd;
+	} else {
+		printk(KERN_ERR "no memory for udc platform data\n");
+	}
+}
+
 /* IIS */
 
 static struct resource s3c_iis_resource[] = {
diff --git a/arch/arm/plat-s3c24xx/include/plat/udc.h b/arch/arm/plat-s3c24xx/include/plat/udc.h
index 80457c6..f638842 100644
--- a/arch/arm/plat-s3c24xx/include/plat/udc.h
+++ b/arch/arm/plat-s3c24xx/include/plat/udc.h
@@ -37,4 +37,21 @@ struct s3c2410_udc_mach_info {
 
 extern void __init s3c24xx_udc_set_platdata(struct s3c2410_udc_mach_info *);
 
+/**
+ * s3c24xx_hsudc_platdata - Platform data for USB High-Speed gadget controller.
+ * @epnum: Number of endpoints to be instantiated by the controller driver.
+ * @gpio_init: Platform specific USB related GPIO initialization.
+ * @gpio_uninit: Platform specific USB releted GPIO uninitialzation.
+ *
+ * Representation of platform data for the S3C24XX USB 2.0 High Speed gadget
+ * controllers.
+ */
+struct s3c24xx_hsudc_platdata {
+	unsigned int	epnum;
+	void		(*gpio_init)(void);
+	void		(*gpio_uninit)(void);
+};
+
+extern void __init s3c24xx_hsudc_set_platdata(struct s3c24xx_hsudc_platdata *pd);
+
 #endif /* __ASM_ARM_ARCH_UDC_H */
diff --git a/arch/arm/plat-samsung/include/plat/devs.h b/arch/arm/plat-samsung/include/plat/devs.h
index 3f38deb..39818d8 100644
--- a/arch/arm/plat-samsung/include/plat/devs.h
+++ b/arch/arm/plat-samsung/include/plat/devs.h
@@ -88,6 +88,7 @@ extern struct platform_device s3c64xx_device_onenand1;
 extern struct platform_device s5p_device_onenand;
 
 extern struct platform_device s3c_device_usbgadget;
+extern struct platform_device s3c_device_usb_hsudc;
 extern struct platform_device s3c_device_usb_hsotg;
 
 extern struct platform_device s5pv210_device_ac97;
-- 
1.7.1

^ permalink raw reply related	[flat|nested] 52+ messages in thread

* [PATCH v4 3/4] USB: Gadget: Add Samsung S3C24XX USB High-Speed controller driver
  2011-04-14  9:06       ` Heiko Stübner
@ 2011-04-14  9:11         ` Heiko Stübner
  -1 siblings, 0 replies; 52+ messages in thread
From: Heiko Stübner @ 2011-04-14  9:11 UTC (permalink / raw)
  To: Greg KH, Kukjin Kim
  Cc: Ben Dooks, Thomas Abraham, Sangbeom Kim, linux-arm-kernel,
	linux-samsung-soc, linux-usb, Alexander Neumann

From: Thomas Abraham <thomas.ab@samsung.com>

The Samsung's S3C2416, S3C2443 and S3C2450 includes a USB High-Speed
device controller module. This driver enables support for USB high-speed
gadget functionality for the Samsung S3C24xx SoC's that include this
controller.

Signed-off-by: Thomas Abraham <thomas.ab@samsung.com>
Signed-off-by: Sangbeom Kim <sbkim73@samsung.com>
Signed-off-by: Kukjin Kim <kgene.kim@samsung.com>
Signed-off-by: Alexander Neumann <alexander@bumpern.de>
Signed-off-by: Heiko Stuebner <heiko@sntech.de>
---
Changes since v1:
- Addressed comments from Ben Dooks

Changes since v2:
- Update the driver to match the data structure changes introduced by
  commit b0fca50f5a94a268ed02cfddf44448051ed9343f.
  Alexander Neumann <alexander@debian.org>
- Move s3c_hsudc to 0x29 in usb_gadget_controller_number in gadget_chips.h
  Heiko Stuebner <heiko@sntech.de>

Changes since v3:
- Update gadget_chips.h-id again
  Heiko Stuebner <heiko@sntech.de>
 drivers/usb/gadget/Kconfig        |   17 +
 drivers/usb/gadget/Makefile       |    1 +
 drivers/usb/gadget/gadget_chips.h |    8 +
 drivers/usb/gadget/s3c-hsudc.c    | 1329 +++++++++++++++++++++++++++++++++++++
 4 files changed, 1355 insertions(+), 0 deletions(-)
 create mode 100644 drivers/usb/gadget/s3c-hsudc.c

diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig
index 4c02b9f..58456d1 100644
--- a/drivers/usb/gadget/Kconfig
+++ b/drivers/usb/gadget/Kconfig
@@ -356,6 +356,23 @@ config USB_S3C2410_DEBUG
 	boolean "S3C2410 udc debug messages"
 	depends on USB_GADGET_S3C2410
 
+config USB_GADGET_S3C_HSUDC
+	boolean "S3C2416, S3C2443 and S3C2450 USB Device Controller"
+	depends on ARCH_S3C2410
+	select USB_GADGET_DUALSPEED
+	help
+	  Samsung's S3C2416, S3C2443 and S3C2450 is an ARM9 based SoC
+	  integrated with dual speed USB 2.0 device controller. It has
+	  8 endpoints, as well as endpoint zero.
+
+	  This driver has been tested on S3C2416 and S3C2450 processors.
+
+config USB_S3C_HSUDC
+	tristate
+	depends on USB_GADGET_S3C_HSUDC
+	default USB_GADGET
+	select USB_GADGET_SELECTED
+
 config USB_GADGET_PXA_U2O
 	boolean "PXA9xx Processor USB2.0 controller"
 	select USB_GADGET_DUALSPEED
diff --git a/drivers/usb/gadget/Makefile b/drivers/usb/gadget/Makefile
index 1ea15ee..4fe92b1 100644
--- a/drivers/usb/gadget/Makefile
+++ b/drivers/usb/gadget/Makefile
@@ -22,6 +22,7 @@ obj-$(CONFIG_USB_R8A66597)	+= r8a66597-udc.o
 obj-$(CONFIG_USB_FSL_QE)	+= fsl_qe_udc.o
 obj-$(CONFIG_USB_CI13XXX_PCI)	+= ci13xxx_pci.o
 obj-$(CONFIG_USB_S3C_HSOTG)	+= s3c-hsotg.o
+obj-$(CONFIG_USB_S3C_HSUDC)	+= s3c-hsudc.o
 obj-$(CONFIG_USB_LANGWELL)	+= langwell_udc.o
 obj-$(CONFIG_USB_EG20T)		+= pch_udc.o
 obj-$(CONFIG_USB_PXA_U2O)	+= mv_udc.o
diff --git a/drivers/usb/gadget/gadget_chips.h b/drivers/usb/gadget/gadget_chips.h
index ec3fd97..bcdac7c 100644
--- a/drivers/usb/gadget/gadget_chips.h
+++ b/drivers/usb/gadget/gadget_chips.h
@@ -136,6 +136,12 @@
 #define gadget_is_s3c_hsotg(g)    0
 #endif
 
+#ifdef CONFIG_USB_S3C_HSUDC
+#define gadget_is_s3c_hsudc(g) (!strcmp("s3c-hsudc", (g)->name))
+#else
+#define gadget_is_s3c_hsudc(g) 0
+#endif
+
 #ifdef CONFIG_USB_GADGET_EG20T
 #define	gadget_is_pch(g)	(!strcmp("pch_udc", (g)->name))
 #else
@@ -215,6 +221,8 @@ static inline int usb_gadget_controller_number(struct usb_gadget *gadget)
 		return 0x28;
 	else if (gadget_is_renesas_usbhs(gadget))
 		return 0x29;
+	else if (gadget_is_s3c_hsudc(gadget))
+		return 0x30;
 
 	return -ENOENT;
 }
diff --git a/drivers/usb/gadget/s3c-hsudc.c b/drivers/usb/gadget/s3c-hsudc.c
new file mode 100644
index 0000000..b98d8a1
--- /dev/null
+++ b/drivers/usb/gadget/s3c-hsudc.c
@@ -0,0 +1,1329 @@
+/* linux/drivers/usb/gadget/s3c-hsudc.c
+ *
+ * Copyright (c) 2010 Samsung Electronics Co., Ltd.
+ *		http://www.samsung.com/
+ *
+ * S3C24XX USB 2.0 High-speed USB controller gadget driver
+ *
+ * The S3C24XX USB 2.0 high-speed USB controller supports upto 9 endpoints.
+ * Each endpoint can be configured as either in or out endpoint. Endpoints
+ * can be configured for Bulk or Interrupt transfer mode.
+ *
+ * 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.
+*/
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/spinlock.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <linux/dma-mapping.h>
+#include <linux/delay.h>
+#include <linux/io.h>
+#include <linux/slab.h>
+#include <linux/clk.h>
+#include <linux/usb/ch9.h>
+#include <linux/usb/gadget.h>
+
+#include <mach/regs-s3c2443-clock.h>
+#include <plat/udc.h>
+
+#define S3C_HSUDC_REG(x)	(x)
+
+/* Non-Indexed Registers */
+#define S3C_IR				S3C_HSUDC_REG(0x00) /* Index Register */
+#define S3C_EIR				S3C_HSUDC_REG(0x04) /* EP Intr Status */
+#define S3C_EIR_EP0			(1<<0)
+#define S3C_EIER			S3C_HSUDC_REG(0x08) /* EP Intr Enable */
+#define S3C_FAR				S3C_HSUDC_REG(0x0c) /* Gadget Address */
+#define S3C_FNR				S3C_HSUDC_REG(0x10) /* Frame Number */
+#define S3C_EDR				S3C_HSUDC_REG(0x14) /* EP Direction */
+#define S3C_TR				S3C_HSUDC_REG(0x18) /* Test Register */
+#define S3C_SSR				S3C_HSUDC_REG(0x1c) /* System Status */
+#define S3C_SSR_DTZIEN_EN		(0xff8f)
+#define S3C_SSR_ERR			(0xff80)
+#define S3C_SSR_VBUSON			(1 << 8)
+#define S3C_SSR_HSP			(1 << 4)
+#define S3C_SSR_SDE			(1 << 3)
+#define S3C_SSR_RESUME			(1 << 2)
+#define S3C_SSR_SUSPEND			(1 << 1)
+#define S3C_SSR_RESET			(1 << 0)
+#define S3C_SCR				S3C_HSUDC_REG(0x20) /* System Control */
+#define S3C_SCR_DTZIEN_EN		(1 << 14)
+#define S3C_SCR_RRD_EN			(1 << 5)
+#define S3C_SCR_SUS_EN			(1 << 1)
+#define S3C_SCR_RST_EN			(1 << 0)
+#define S3C_EP0SR			S3C_HSUDC_REG(0x24) /* EP0 Status */
+#define S3C_EP0SR_EP0_LWO		(1 << 6)
+#define S3C_EP0SR_STALL			(1 << 4)
+#define S3C_EP0SR_TX_SUCCESS		(1 << 1)
+#define S3C_EP0SR_RX_SUCCESS		(1 << 0)
+#define S3C_EP0CR			S3C_HSUDC_REG(0x28) /* EP0 Control */
+#define S3C_BR(_x)			S3C_HSUDC_REG(0x60 + (_x * 4))
+
+/* Indexed Registers */
+#define S3C_ESR				S3C_HSUDC_REG(0x2c) /* EPn Status */
+#define S3C_ESR_FLUSH			(1 << 6)
+#define S3C_ESR_STALL			(1 << 5)
+#define S3C_ESR_LWO			(1 << 4)
+#define S3C_ESR_PSIF_ONE		(1 << 2)
+#define S3C_ESR_PSIF_TWO		(2 << 2)
+#define S3C_ESR_TX_SUCCESS		(1 << 1)
+#define S3C_ESR_RX_SUCCESS		(1 << 0)
+#define S3C_ECR				S3C_HSUDC_REG(0x30) /* EPn Control */
+#define S3C_ECR_DUEN			(1 << 7)
+#define S3C_ECR_FLUSH			(1 << 6)
+#define S3C_ECR_STALL			(1 << 1)
+#define S3C_ECR_IEMS			(1 << 0)
+#define S3C_BRCR			S3C_HSUDC_REG(0x34) /* Read Count */
+#define S3C_BWCR			S3C_HSUDC_REG(0x38) /* Write Count */
+#define S3C_MPR				S3C_HSUDC_REG(0x3c) /* Max Pkt Size */
+
+#define WAIT_FOR_SETUP			(0)
+#define DATA_STATE_XMIT			(1)
+#define DATA_STATE_RECV			(2)
+
+/**
+ * struct s3c_hsudc_ep - Endpoint representation used by driver.
+ * @ep: USB gadget layer representation of device endpoint.
+ * @name: Endpoint name (as required by ep autoconfiguration).
+ * @dev: Reference to the device controller to which this EP belongs.
+ * @desc: Endpoint descriptor obtained from the gadget driver.
+ * @queue: Transfer request queue for the endpoint.
+ * @stopped: Maintains state of endpoint, set if EP is halted.
+ * @bEndpointAddress: EP address (including direction bit).
+ * @fifo: Base address of EP FIFO.
+ */
+struct s3c_hsudc_ep {
+	struct usb_ep ep;
+	char name[20];
+	struct s3c_hsudc *dev;
+	const struct usb_endpoint_descriptor *desc;
+	struct list_head queue;
+	u8 stopped;
+	u8 bEndpointAddress;
+	void __iomem *fifo;
+};
+
+/**
+ * struct s3c_hsudc_req - Driver encapsulation of USB gadget transfer request.
+ * @req: Reference to USB gadget transfer request.
+ * @queue: Used for inserting this request to the endpoint request queue.
+ */
+struct s3c_hsudc_req {
+	struct usb_request req;
+	struct list_head queue;
+};
+
+/**
+ * struct s3c_hsudc - Driver's abstraction of the device controller.
+ * @gadget: Instance of usb_gadget which is referenced by gadget driver.
+ * @driver: Reference to currenty active gadget driver.
+ * @dev: The device reference used by probe function.
+ * @lock: Lock to synchronize the usage of Endpoints (EP's are indexed).
+ * @regs: Remapped base address of controller's register space.
+ * @mem_rsrc: Device memory resource used for remapping device register space.
+ * irq: IRQ number used by the controller.
+ * uclk: Reference to the controller clock.
+ * ep0state: Current state of EP0.
+ * ep: List of endpoints supported by the controller.
+ */
+struct s3c_hsudc {
+	struct usb_gadget gadget;
+	struct usb_gadget_driver *driver;
+	struct device *dev;
+	struct s3c24xx_hsudc_platdata *pd;
+	spinlock_t lock;
+	void __iomem *regs;
+	struct resource *mem_rsrc;
+	int irq;
+	struct clk *uclk;
+	int ep0state;
+	struct s3c_hsudc_ep ep[];
+};
+
+#define ep_maxpacket(_ep)	((_ep)->ep.maxpacket)
+#define ep_is_in(_ep)		((_ep)->bEndpointAddress & USB_DIR_IN)
+#define ep_index(_ep)		((_ep)->bEndpointAddress & \
+					USB_ENDPOINT_NUMBER_MASK)
+
+static struct s3c_hsudc *the_controller;
+static const char driver_name[] = "s3c-udc";
+static const char ep0name[] = "ep0-control";
+
+static inline struct s3c_hsudc_req *our_req(struct usb_request *req)
+{
+	return container_of(req, struct s3c_hsudc_req, req);
+}
+
+static inline struct s3c_hsudc_ep *our_ep(struct usb_ep *ep)
+{
+	return container_of(ep, struct s3c_hsudc_ep, ep);
+}
+
+static inline struct s3c_hsudc *to_hsudc(struct usb_gadget *gadget)
+{
+	return container_of(gadget, struct s3c_hsudc, gadget);
+}
+
+static inline void set_index(struct s3c_hsudc *hsudc, int ep_addr)
+{
+	ep_addr &= USB_ENDPOINT_NUMBER_MASK;
+	writel(ep_addr, hsudc->regs + S3C_IR);
+}
+
+static inline void __orr32(void __iomem *ptr, u32 val)
+{
+	writel(readl(ptr) | val, ptr);
+}
+
+static void s3c_hsudc_init_phy(void)
+{
+	u32 cfg;
+
+	cfg = readl(S3C2443_PWRCFG) | S3C2443_PWRCFG_USBPHY;
+	writel(cfg, S3C2443_PWRCFG);
+
+	cfg = readl(S3C2443_URSTCON);
+	cfg |= (S3C2443_URSTCON_FUNCRST | S3C2443_URSTCON_PHYRST);
+	writel(cfg, S3C2443_URSTCON);
+	mdelay(1);
+
+	cfg = readl(S3C2443_URSTCON);
+	cfg &= ~(S3C2443_URSTCON_FUNCRST | S3C2443_URSTCON_PHYRST);
+	writel(cfg, S3C2443_URSTCON);
+
+	cfg = readl(S3C2443_PHYCTRL);
+	cfg &= ~(S3C2443_PHYCTRL_CLKSEL | S3C2443_PHYCTRL_DSPORT);
+	cfg |= (S3C2443_PHYCTRL_EXTCLK | S3C2443_PHYCTRL_PLLSEL);
+	writel(cfg, S3C2443_PHYCTRL);
+
+	cfg = readl(S3C2443_PHYPWR);
+	cfg &= ~(S3C2443_PHYPWR_FSUSPEND | S3C2443_PHYPWR_PLL_PWRDN |
+		S3C2443_PHYPWR_XO_ON | S3C2443_PHYPWR_PLL_REFCLK |
+		S3C2443_PHYPWR_ANALOG_PD);
+	cfg |= S3C2443_PHYPWR_COMMON_ON;
+	writel(cfg, S3C2443_PHYPWR);
+
+	cfg = readl(S3C2443_UCLKCON);
+	cfg |= (S3C2443_UCLKCON_DETECT_VBUS | S3C2443_UCLKCON_FUNC_CLKEN |
+		S3C2443_UCLKCON_TCLKEN);
+	writel(cfg, S3C2443_UCLKCON);
+}
+
+static void s3c_hsudc_uninit_phy(void)
+{
+	u32 cfg;
+
+	cfg = readl(S3C2443_PWRCFG) & ~S3C2443_PWRCFG_USBPHY;
+	writel(cfg, S3C2443_PWRCFG);
+
+	writel(S3C2443_PHYPWR_FSUSPEND, S3C2443_PHYPWR);
+
+	cfg = readl(S3C2443_UCLKCON) & ~S3C2443_UCLKCON_FUNC_CLKEN;
+	writel(cfg, S3C2443_UCLKCON);
+}
+
+/**
+ * s3c_hsudc_complete_request - Complete a transfer request.
+ * @hsep: Endpoint to which the request belongs.
+ * @hsreq: Transfer request to be completed.
+ * @status: Transfer completion status for the transfer request.
+ */
+static void s3c_hsudc_complete_request(struct s3c_hsudc_ep *hsep,
+				struct s3c_hsudc_req *hsreq, int status)
+{
+	unsigned int stopped = hsep->stopped;
+	struct s3c_hsudc *hsudc = hsep->dev;
+
+	list_del_init(&hsreq->queue);
+	hsreq->req.status = status;
+
+	if (!ep_index(hsep)) {
+		hsudc->ep0state = WAIT_FOR_SETUP;
+		hsep->bEndpointAddress &= ~USB_DIR_IN;
+	}
+
+	hsep->stopped = 1;
+	spin_unlock(&hsudc->lock);
+	if (hsreq->req.complete != NULL)
+		hsreq->req.complete(&hsep->ep, &hsreq->req);
+	spin_lock(&hsudc->lock);
+	hsep->stopped = stopped;
+}
+
+/**
+ * s3c_hsudc_nuke_ep - Terminate all requests queued for a endpoint.
+ * @hsep: Endpoint for which queued requests have to be terminated.
+ * @status: Transfer completion status for the transfer request.
+ */
+static void s3c_hsudc_nuke_ep(struct s3c_hsudc_ep *hsep, int status)
+{
+	struct s3c_hsudc_req *hsreq;
+
+	while (!list_empty(&hsep->queue)) {
+		hsreq = list_entry(hsep->queue.next,
+				struct s3c_hsudc_req, queue);
+		s3c_hsudc_complete_request(hsep, hsreq, status);
+	}
+}
+
+/**
+ * s3c_hsudc_stop_activity - Stop activity on all endpoints.
+ * @hsudc: Device controller for which EP activity is to be stopped.
+ * @driver: Reference to the gadget driver which is currently active.
+ *
+ * All the endpoints are stopped and any pending transfer requests if any on
+ * the endpoint are terminated.
+ */
+static void s3c_hsudc_stop_activity(struct s3c_hsudc *hsudc,
+			  struct usb_gadget_driver *driver)
+{
+	struct s3c_hsudc_ep *hsep;
+	int epnum;
+
+	hsudc->gadget.speed = USB_SPEED_UNKNOWN;
+
+	for (epnum = 0; epnum < hsudc->pd->epnum; epnum++) {
+		hsep = &hsudc->ep[epnum];
+		hsep->stopped = 1;
+		s3c_hsudc_nuke_ep(hsep, -ESHUTDOWN);
+	}
+
+	spin_unlock(&hsudc->lock);
+	driver->disconnect(&hsudc->gadget);
+	spin_lock(&hsudc->lock);
+}
+
+/**
+ * s3c_hsudc_read_setup_pkt - Read the received setup packet from EP0 fifo.
+ * @hsudc: Device controller from which setup packet is to be read.
+ * @buf: The buffer into which the setup packet is read.
+ *
+ * The setup packet received in the EP0 fifo is read and stored into a
+ * given buffer address.
+ */
+
+static void s3c_hsudc_read_setup_pkt(struct s3c_hsudc *hsudc, u16 *buf)
+{
+	int count;
+
+	count = readl(hsudc->regs + S3C_BRCR);
+	while (count--)
+		*buf++ = (u16)readl(hsudc->regs + S3C_BR(0));
+
+	writel(S3C_EP0SR_RX_SUCCESS, hsudc->regs + S3C_EP0SR);
+}
+
+/**
+ * s3c_hsudc_write_fifo - Write next chunk of transfer data to EP fifo.
+ * @hsep: Endpoint to which the data is to be written.
+ * @hsreq: Transfer request from which the next chunk of data is written.
+ *
+ * Write the next chunk of data from a transfer request to the endpoint FIFO.
+ * If the transfer request completes, 1 is returned, otherwise 0 is returned.
+ */
+static int s3c_hsudc_write_fifo(struct s3c_hsudc_ep *hsep,
+				struct s3c_hsudc_req *hsreq)
+{
+	u16 *buf;
+	u32 max = ep_maxpacket(hsep);
+	u32 count, length;
+	bool is_last;
+	void __iomem *fifo = hsep->fifo;
+
+	buf = hsreq->req.buf + hsreq->req.actual;
+	prefetch(buf);
+
+	length = hsreq->req.length - hsreq->req.actual;
+	length = min(length, max);
+	hsreq->req.actual += length;
+
+	writel(length, hsep->dev->regs + S3C_BWCR);
+	for (count = 0; count < length; count += 2)
+		writel(*buf++, fifo);
+
+	if (count != max) {
+		is_last = true;
+	} else {
+		if (hsreq->req.length != hsreq->req.actual || hsreq->req.zero)
+			is_last = false;
+		else
+			is_last = true;
+	}
+
+	if (is_last) {
+		s3c_hsudc_complete_request(hsep, hsreq, 0);
+		return 1;
+	}
+
+	return 0;
+}
+
+/**
+ * s3c_hsudc_read_fifo - Read the next chunk of data from EP fifo.
+ * @hsep: Endpoint from which the data is to be read.
+ * @hsreq: Transfer request to which the next chunk of data read is written.
+ *
+ * Read the next chunk of data from the endpoint FIFO and a write it to the
+ * transfer request buffer. If the transfer request completes, 1 is returned,
+ * otherwise 0 is returned.
+ */
+static int s3c_hsudc_read_fifo(struct s3c_hsudc_ep *hsep,
+				struct s3c_hsudc_req *hsreq)
+{
+	struct s3c_hsudc *hsudc = hsep->dev;
+	u32 csr, offset;
+	u16 *buf, word;
+	u32 buflen, rcnt, rlen;
+	void __iomem *fifo = hsep->fifo;
+	u32 is_short = 0;
+
+	offset = (ep_index(hsep)) ? S3C_ESR : S3C_EP0SR;
+	csr = readl(hsudc->regs + offset);
+	if (!(csr & S3C_ESR_RX_SUCCESS))
+		return -EINVAL;
+
+	buf = hsreq->req.buf + hsreq->req.actual;
+	prefetchw(buf);
+	buflen = hsreq->req.length - hsreq->req.actual;
+
+	rcnt = readl(hsudc->regs + S3C_BRCR);
+	rlen = (csr & S3C_ESR_LWO) ? (rcnt * 2 - 1) : (rcnt * 2);
+
+	hsreq->req.actual += min(rlen, buflen);
+	is_short = (rlen < hsep->ep.maxpacket);
+
+	while (rcnt-- != 0) {
+		word = (u16)readl(fifo);
+		if (buflen) {
+			*buf++ = word;
+			buflen--;
+		} else {
+			hsreq->req.status = -EOVERFLOW;
+		}
+	}
+
+	writel(S3C_ESR_RX_SUCCESS, hsudc->regs + offset);
+
+	if (is_short || hsreq->req.actual == hsreq->req.length) {
+		s3c_hsudc_complete_request(hsep, hsreq, 0);
+		return 1;
+	}
+
+	return 0;
+}
+
+/**
+ * s3c_hsudc_epin_intr - Handle in-endpoint interrupt.
+ * @hsudc - Device controller for which the interrupt is to be handled.
+ * @ep_idx - Endpoint number on which an interrupt is pending.
+ *
+ * Handles interrupt for a in-endpoint. The interrupts that are handled are
+ * stall and data transmit complete interrupt.
+ */
+static void s3c_hsudc_epin_intr(struct s3c_hsudc *hsudc, u32 ep_idx)
+{
+	struct s3c_hsudc_ep *hsep = &hsudc->ep[ep_idx];
+	struct s3c_hsudc_req *hsreq;
+	u32 csr;
+
+	csr = readl((u32)hsudc->regs + S3C_ESR);
+	if (csr & S3C_ESR_STALL) {
+		writel(S3C_ESR_STALL, hsudc->regs + S3C_ESR);
+		return;
+	}
+
+	if (csr & S3C_ESR_TX_SUCCESS) {
+		writel(S3C_ESR_TX_SUCCESS, hsudc->regs + S3C_ESR);
+		if (list_empty(&hsep->queue))
+			return;
+
+		hsreq = list_entry(hsep->queue.next,
+				struct s3c_hsudc_req, queue);
+		if ((s3c_hsudc_write_fifo(hsep, hsreq) == 0) &&
+				(csr & S3C_ESR_PSIF_TWO))
+			s3c_hsudc_write_fifo(hsep, hsreq);
+	}
+}
+
+/**
+ * s3c_hsudc_epout_intr - Handle out-endpoint interrupt.
+ * @hsudc - Device controller for which the interrupt is to be handled.
+ * @ep_idx - Endpoint number on which an interrupt is pending.
+ *
+ * Handles interrupt for a out-endpoint. The interrupts that are handled are
+ * stall, flush and data ready interrupt.
+ */
+static void s3c_hsudc_epout_intr(struct s3c_hsudc *hsudc, u32 ep_idx)
+{
+	struct s3c_hsudc_ep *hsep = &hsudc->ep[ep_idx];
+	struct s3c_hsudc_req *hsreq;
+	u32 csr;
+
+	csr = readl((u32)hsudc->regs + S3C_ESR);
+	if (csr & S3C_ESR_STALL) {
+		writel(S3C_ESR_STALL, hsudc->regs + S3C_ESR);
+		return;
+	}
+
+	if (csr & S3C_ESR_FLUSH) {
+		__orr32(hsudc->regs + S3C_ECR, S3C_ECR_FLUSH);
+		return;
+	}
+
+	if (csr & S3C_ESR_RX_SUCCESS) {
+		if (list_empty(&hsep->queue))
+			return;
+
+		hsreq = list_entry(hsep->queue.next,
+				struct s3c_hsudc_req, queue);
+		if (((s3c_hsudc_read_fifo(hsep, hsreq)) == 0) &&
+				(csr & S3C_ESR_PSIF_TWO))
+			s3c_hsudc_read_fifo(hsep, hsreq);
+	}
+}
+
+/** s3c_hsudc_set_halt - Set or clear a endpoint halt.
+ * @_ep: Endpoint on which halt has to be set or cleared.
+ * @value: 1 for setting halt on endpoint, 0 to clear halt.
+ *
+ * Set or clear endpoint halt. If halt is set, the endpoint is stopped.
+ * If halt is cleared, for in-endpoints, if there are any pending
+ * transfer requests, transfers are started.
+ */
+static int s3c_hsudc_set_halt(struct usb_ep *_ep, int value)
+{
+	struct s3c_hsudc_ep *hsep = our_ep(_ep);
+	struct s3c_hsudc *hsudc = hsep->dev;
+	struct s3c_hsudc_req *hsreq;
+	unsigned long irqflags;
+	u32 ecr;
+	u32 offset;
+
+	if (value && ep_is_in(hsep) && !list_empty(&hsep->queue))
+		return -EAGAIN;
+
+	spin_lock_irqsave(&hsudc->lock, irqflags);
+	set_index(hsudc, ep_index(hsep));
+	offset = (ep_index(hsep)) ? S3C_ECR : S3C_EP0CR;
+	ecr = readl(hsudc->regs + offset);
+
+	if (value) {
+		ecr |= S3C_ECR_STALL;
+		if (ep_index(hsep))
+			ecr |= S3C_ECR_FLUSH;
+		hsep->stopped = 1;
+	} else {
+		ecr &= ~S3C_ECR_STALL;
+		hsep->stopped = 0;
+	}
+	writel(ecr, hsudc->regs + offset);
+
+	if (ep_is_in(hsep) && !list_empty(&hsep->queue) && !value) {
+		hsreq = list_entry(hsep->queue.next,
+			struct s3c_hsudc_req, queue);
+		if (hsreq)
+			s3c_hsudc_write_fifo(hsep, hsreq);
+	}
+
+	spin_unlock_irqrestore(&hsudc->lock, irqflags);
+	return 0;
+}
+
+/** s3c_hsudc_handle_reqfeat - Handle set feature or clear feature requests.
+ * @_ep: Device controller on which the set/clear feature needs to be handled.
+ * @ctrl: Control request as received on the endpoint 0.
+ *
+ * Handle set feature or clear feature control requests on the control endpoint.
+ */
+static int s3c_hsudc_handle_reqfeat(struct s3c_hsudc *hsudc,
+					struct usb_ctrlrequest *ctrl)
+{
+	struct s3c_hsudc_ep *hsep;
+	bool set = (ctrl->bRequest == USB_REQ_SET_FEATURE);
+	u8 ep_num = ctrl->wIndex & USB_ENDPOINT_NUMBER_MASK;
+
+	if (ctrl->bRequestType == USB_RECIP_ENDPOINT) {
+		hsep = &hsudc->ep[ep_num];
+		switch (le16_to_cpu(ctrl->wValue)) {
+		case USB_ENDPOINT_HALT:
+			s3c_hsudc_set_halt(&hsep->ep, set);
+			return 0;
+		}
+	}
+
+	return -ENOENT;
+}
+
+/**
+ * s3c_hsudc_process_req_status - Handle get status control request.
+ * @hsudc: Device controller on which get status request has be handled.
+ * @ctrl: Control request as received on the endpoint 0.
+ *
+ * Handle get status control request received on control endpoint.
+ */
+static void s3c_hsudc_process_req_status(struct s3c_hsudc *hsudc,
+					struct usb_ctrlrequest *ctrl)
+{
+	struct s3c_hsudc_ep *hsep0 = &hsudc->ep[0];
+	struct s3c_hsudc_req hsreq;
+	struct s3c_hsudc_ep *hsep;
+	__le16 reply;
+	u8 epnum;
+
+	switch (ctrl->bRequestType & USB_RECIP_MASK) {
+	case USB_RECIP_DEVICE:
+		reply = cpu_to_le16(0);
+		break;
+
+	case USB_RECIP_INTERFACE:
+		reply = cpu_to_le16(0);
+		break;
+
+	case USB_RECIP_ENDPOINT:
+		epnum = le16_to_cpu(ctrl->wIndex) & USB_ENDPOINT_NUMBER_MASK;
+		hsep = &hsudc->ep[epnum];
+		reply = cpu_to_le16(hsep->stopped ? 1 : 0);
+		break;
+	}
+
+	INIT_LIST_HEAD(&hsreq.queue);
+	hsreq.req.length = 2;
+	hsreq.req.buf = &reply;
+	hsreq.req.actual = 0;
+	hsreq.req.complete = NULL;
+	s3c_hsudc_write_fifo(hsep0, &hsreq);
+}
+
+/**
+ * s3c_hsudc_process_setup - Process control request received on endpoint 0.
+ * @hsudc: Device controller on which control request has been received.
+ *
+ * Read the control request received on endpoint 0, decode it and handle
+ * the request.
+ */
+static void s3c_hsudc_process_setup(struct s3c_hsudc *hsudc)
+{
+	struct s3c_hsudc_ep *hsep = &hsudc->ep[0];
+	struct usb_ctrlrequest ctrl = {0};
+	int ret;
+
+	s3c_hsudc_nuke_ep(hsep, -EPROTO);
+	s3c_hsudc_read_setup_pkt(hsudc, (u16 *)&ctrl);
+
+	if (ctrl.bRequestType & USB_DIR_IN) {
+		hsep->bEndpointAddress |= USB_DIR_IN;
+		hsudc->ep0state = DATA_STATE_XMIT;
+	} else {
+		hsep->bEndpointAddress &= ~USB_DIR_IN;
+		hsudc->ep0state = DATA_STATE_RECV;
+	}
+
+	switch (ctrl.bRequest) {
+	case USB_REQ_SET_ADDRESS:
+		if (ctrl.bRequestType != (USB_TYPE_STANDARD | USB_RECIP_DEVICE))
+			break;
+		hsudc->ep0state = WAIT_FOR_SETUP;
+		return;
+
+	case USB_REQ_GET_STATUS:
+		if ((ctrl.bRequestType & USB_TYPE_MASK) != USB_TYPE_STANDARD)
+			break;
+		s3c_hsudc_process_req_status(hsudc, &ctrl);
+		return;
+
+	case USB_REQ_SET_FEATURE:
+	case USB_REQ_CLEAR_FEATURE:
+		if ((ctrl.bRequestType & USB_TYPE_MASK) != USB_TYPE_STANDARD)
+			break;
+		s3c_hsudc_handle_reqfeat(hsudc, &ctrl);
+		hsudc->ep0state = WAIT_FOR_SETUP;
+		return;
+	}
+
+	if (hsudc->driver) {
+		spin_unlock(&hsudc->lock);
+		ret = hsudc->driver->setup(&hsudc->gadget, &ctrl);
+		spin_lock(&hsudc->lock);
+
+		if (ctrl.bRequest == USB_REQ_SET_CONFIGURATION) {
+			hsep->bEndpointAddress &= ~USB_DIR_IN;
+			hsudc->ep0state = WAIT_FOR_SETUP;
+		}
+
+		if (ret < 0) {
+			dev_err(hsudc->dev, "setup failed, returned %d\n",
+						ret);
+			s3c_hsudc_set_halt(&hsep->ep, 1);
+			hsudc->ep0state = WAIT_FOR_SETUP;
+			hsep->bEndpointAddress &= ~USB_DIR_IN;
+		}
+	}
+}
+
+/** s3c_hsudc_handle_ep0_intr - Handle endpoint 0 interrupt.
+ * @hsudc: Device controller on which endpoint 0 interrupt has occured.
+ *
+ * Handle endpoint 0 interrupt when it occurs. EP0 interrupt could occur
+ * when a stall handshake is sent to host or data is sent/received on
+ * endpoint 0.
+ */
+static void s3c_hsudc_handle_ep0_intr(struct s3c_hsudc *hsudc)
+{
+	struct s3c_hsudc_ep *hsep = &hsudc->ep[0];
+	struct s3c_hsudc_req *hsreq;
+	u32 csr = readl(hsudc->regs + S3C_EP0SR);
+	u32 ecr;
+
+	if (csr & S3C_EP0SR_STALL) {
+		ecr = readl(hsudc->regs + S3C_EP0CR);
+		ecr &= ~(S3C_ECR_STALL | S3C_ECR_FLUSH);
+		writel(ecr, hsudc->regs + S3C_EP0CR);
+
+		writel(S3C_EP0SR_STALL, hsudc->regs + S3C_EP0SR);
+		hsep->stopped = 0;
+
+		s3c_hsudc_nuke_ep(hsep, -ECONNABORTED);
+		hsudc->ep0state = WAIT_FOR_SETUP;
+		hsep->bEndpointAddress &= ~USB_DIR_IN;
+		return;
+	}
+
+	if (csr & S3C_EP0SR_TX_SUCCESS) {
+		writel(S3C_EP0SR_TX_SUCCESS, hsudc->regs + S3C_EP0SR);
+		if (ep_is_in(hsep)) {
+			if (list_empty(&hsep->queue))
+				return;
+
+			hsreq = list_entry(hsep->queue.next,
+					struct s3c_hsudc_req, queue);
+			s3c_hsudc_write_fifo(hsep, hsreq);
+		}
+	}
+
+	if (csr & S3C_EP0SR_RX_SUCCESS) {
+		if (hsudc->ep0state == WAIT_FOR_SETUP)
+			s3c_hsudc_process_setup(hsudc);
+		else {
+			if (!ep_is_in(hsep)) {
+				if (list_empty(&hsep->queue))
+					return;
+				hsreq = list_entry(hsep->queue.next,
+					struct s3c_hsudc_req, queue);
+				s3c_hsudc_read_fifo(hsep, hsreq);
+			}
+		}
+	}
+}
+
+/**
+ * s3c_hsudc_ep_enable - Enable a endpoint.
+ * @_ep: The endpoint to be enabled.
+ * @desc: Endpoint descriptor.
+ *
+ * Enables a endpoint when called from the gadget driver. Endpoint stall if
+ * any is cleared, transfer type is configured and endpoint interrupt is
+ * enabled.
+ */
+static int s3c_hsudc_ep_enable(struct usb_ep *_ep,
+				const struct usb_endpoint_descriptor *desc)
+{
+	struct s3c_hsudc_ep *hsep;
+	struct s3c_hsudc *hsudc;
+	unsigned long flags;
+	u32 ecr = 0;
+
+	hsep = container_of(_ep, struct s3c_hsudc_ep, ep);
+	if (!_ep || !desc || hsep->desc || _ep->name == ep0name
+		|| desc->bDescriptorType != USB_DT_ENDPOINT
+		|| hsep->bEndpointAddress != desc->bEndpointAddress
+		|| ep_maxpacket(hsep) < le16_to_cpu(desc->wMaxPacketSize))
+		return -EINVAL;
+
+	if ((desc->bmAttributes == USB_ENDPOINT_XFER_BULK
+		&& le16_to_cpu(desc->wMaxPacketSize) != ep_maxpacket(hsep))
+		|| !desc->wMaxPacketSize)
+		return -ERANGE;
+
+	hsudc = hsep->dev;
+	if (!hsudc->driver || hsudc->gadget.speed == USB_SPEED_UNKNOWN)
+		return -ESHUTDOWN;
+
+	spin_lock_irqsave(&hsudc->lock, flags);
+
+	set_index(hsudc, hsep->bEndpointAddress);
+	ecr |= ((usb_endpoint_xfer_int(desc)) ? S3C_ECR_IEMS : S3C_ECR_DUEN);
+	writel(ecr, hsudc->regs + S3C_ECR);
+
+	hsep->stopped = 0;
+	hsep->desc = desc;
+	hsep->ep.maxpacket = le16_to_cpu(desc->wMaxPacketSize);
+
+	s3c_hsudc_set_halt(_ep, 0);
+	__set_bit(ep_index(hsep), hsudc->regs + S3C_EIER);
+
+	spin_unlock_irqrestore(&hsudc->lock, flags);
+	return 0;
+}
+
+/**
+ * s3c_hsudc_ep_disable - Disable a endpoint.
+ * @_ep: The endpoint to be disabled.
+ * @desc: Endpoint descriptor.
+ *
+ * Disables a endpoint when called from the gadget driver.
+ */
+static int s3c_hsudc_ep_disable(struct usb_ep *_ep)
+{
+	struct s3c_hsudc_ep *hsep = our_ep(_ep);
+	struct s3c_hsudc *hsudc = hsep->dev;
+	unsigned long flags;
+
+	if (!_ep || !hsep->desc)
+		return -EINVAL;
+
+	spin_lock_irqsave(&hsudc->lock, flags);
+
+	set_index(hsudc, hsep->bEndpointAddress);
+	__clear_bit(ep_index(hsep), hsudc->regs + S3C_EIER);
+
+	s3c_hsudc_nuke_ep(hsep, -ESHUTDOWN);
+
+	hsep->desc = 0;
+	hsep->stopped = 1;
+
+	spin_unlock_irqrestore(&hsudc->lock, flags);
+	return 0;
+}
+
+/**
+ * s3c_hsudc_alloc_request - Allocate a new request.
+ * @_ep: Endpoint for which request is allocated (not used).
+ * @gfp_flags: Flags used for the allocation.
+ *
+ * Allocates a single transfer request structure when called from gadget driver.
+ */
+static struct usb_request *s3c_hsudc_alloc_request(struct usb_ep *_ep,
+						gfp_t gfp_flags)
+{
+	struct s3c_hsudc_req *hsreq;
+
+	hsreq = kzalloc(sizeof *hsreq, gfp_flags);
+	if (!hsreq)
+		return 0;
+
+	INIT_LIST_HEAD(&hsreq->queue);
+	return &hsreq->req;
+}
+
+/**
+ * s3c_hsudc_free_request - Deallocate a request.
+ * @ep: Endpoint for which request is deallocated (not used).
+ * @_req: Request to be deallocated.
+ *
+ * Allocates a single transfer request structure when called from gadget driver.
+ */
+static void s3c_hsudc_free_request(struct usb_ep *ep, struct usb_request *_req)
+{
+	struct s3c_hsudc_req *hsreq;
+
+	hsreq = container_of(_req, struct s3c_hsudc_req, req);
+	WARN_ON(!list_empty(&hsreq->queue));
+	kfree(hsreq);
+}
+
+/**
+ * s3c_hsudc_queue - Queue a transfer request for the endpoint.
+ * @_ep: Endpoint for which the request is queued.
+ * @_req: Request to be queued.
+ * @gfp_flags: Not used.
+ *
+ * Start or enqueue a request for a endpoint when called from gadget driver.
+ */
+static int s3c_hsudc_queue(struct usb_ep *_ep, struct usb_request *_req,
+			gfp_t gfp_flags)
+{
+	struct s3c_hsudc_req *hsreq;
+	struct s3c_hsudc_ep *hsep;
+	struct s3c_hsudc *hsudc;
+	unsigned long flags;
+	u32 offset;
+	u32 csr;
+
+	hsreq = container_of(_req, struct s3c_hsudc_req, req);
+	if ((!_req || !_req->complete || !_req->buf ||
+		!list_empty(&hsreq->queue)))
+		return -EINVAL;
+
+	hsep = container_of(_ep, struct s3c_hsudc_ep, ep);
+	hsudc = hsep->dev;
+	if (!hsudc->driver || hsudc->gadget.speed == USB_SPEED_UNKNOWN)
+		return -ESHUTDOWN;
+
+	spin_lock_irqsave(&hsudc->lock, flags);
+	set_index(hsudc, hsep->bEndpointAddress);
+
+	_req->status = -EINPROGRESS;
+	_req->actual = 0;
+
+	if (!ep_index(hsep) && _req->length == 0) {
+		hsudc->ep0state = WAIT_FOR_SETUP;
+		s3c_hsudc_complete_request(hsep, hsreq, 0);
+		spin_unlock_irqrestore(&hsudc->lock, flags);
+		return 0;
+	}
+
+	if (list_empty(&hsep->queue) && !hsep->stopped) {
+		offset = (ep_index(hsep)) ? S3C_ESR : S3C_EP0SR;
+		if (ep_is_in(hsep)) {
+			csr = readl((u32)hsudc->regs + offset);
+			if (!(csr & S3C_ESR_TX_SUCCESS) &&
+				(s3c_hsudc_write_fifo(hsep, hsreq) == 1))
+				hsreq = 0;
+		} else {
+			csr = readl((u32)hsudc->regs + offset);
+			if ((csr & S3C_ESR_RX_SUCCESS)
+				   && (s3c_hsudc_read_fifo(hsep, hsreq) == 1))
+				hsreq = 0;
+		}
+	}
+
+	if (hsreq != 0)
+		list_add_tail(&hsreq->queue, &hsep->queue);
+
+	spin_unlock_irqrestore(&hsudc->lock, flags);
+	return 0;
+}
+
+/**
+ * s3c_hsudc_dequeue - Dequeue a transfer request from an endpoint.
+ * @_ep: Endpoint from which the request is dequeued.
+ * @_req: Request to be dequeued.
+ *
+ * Dequeue a request from a endpoint when called from gadget driver.
+ */
+static int s3c_hsudc_dequeue(struct usb_ep *_ep, struct usb_request *_req)
+{
+	struct s3c_hsudc_ep *hsep = our_ep(_ep);
+	struct s3c_hsudc *hsudc = hsep->dev;
+	struct s3c_hsudc_req *hsreq;
+	unsigned long flags;
+
+	hsep = container_of(_ep, struct s3c_hsudc_ep, ep);
+	if (!_ep || hsep->ep.name == ep0name)
+		return -EINVAL;
+
+	spin_lock_irqsave(&hsudc->lock, flags);
+
+	list_for_each_entry(hsreq, &hsep->queue, queue) {
+		if (&hsreq->req == _req)
+			break;
+	}
+	if (&hsreq->req != _req) {
+		spin_unlock_irqrestore(&hsudc->lock, flags);
+		return -EINVAL;
+	}
+
+	set_index(hsudc, hsep->bEndpointAddress);
+	s3c_hsudc_complete_request(hsep, hsreq, -ECONNRESET);
+
+	spin_unlock_irqrestore(&hsudc->lock, flags);
+	return 0;
+}
+
+static struct usb_ep_ops s3c_hsudc_ep_ops = {
+	.enable = s3c_hsudc_ep_enable,
+	.disable = s3c_hsudc_ep_disable,
+	.alloc_request = s3c_hsudc_alloc_request,
+	.free_request = s3c_hsudc_free_request,
+	.queue = s3c_hsudc_queue,
+	.dequeue = s3c_hsudc_dequeue,
+	.set_halt = s3c_hsudc_set_halt,
+};
+
+/**
+ * s3c_hsudc_initep - Initialize a endpoint to default state.
+ * @hsudc - Reference to the device controller.
+ * @hsep - Endpoint to be initialized.
+ * @epnum - Address to be assigned to the endpoint.
+ *
+ * Initialize a endpoint with default configuration.
+ */
+static void s3c_hsudc_initep(struct s3c_hsudc *hsudc,
+				struct s3c_hsudc_ep *hsep, int epnum)
+{
+	char *dir;
+
+	if ((epnum % 2) == 0) {
+		dir = "out";
+	} else {
+		dir = "in";
+		hsep->bEndpointAddress = USB_DIR_IN;
+	}
+
+	hsep->bEndpointAddress |= epnum;
+	if (epnum)
+		snprintf(hsep->name, sizeof(hsep->name), "ep%d%s", epnum, dir);
+	else
+		snprintf(hsep->name, sizeof(hsep->name), "%s", ep0name);
+
+	INIT_LIST_HEAD(&hsep->queue);
+	INIT_LIST_HEAD(&hsep->ep.ep_list);
+	if (epnum)
+		list_add_tail(&hsep->ep.ep_list, &hsudc->gadget.ep_list);
+
+	hsep->dev = hsudc;
+	hsep->ep.name = hsep->name;
+	hsep->ep.maxpacket = epnum ? 512 : 64;
+	hsep->ep.ops = &s3c_hsudc_ep_ops;
+	hsep->fifo = hsudc->regs + S3C_BR(epnum);
+	hsep->desc = 0;
+	hsep->stopped = 0;
+
+	set_index(hsudc, epnum);
+	writel(hsep->ep.maxpacket, hsudc->regs + S3C_MPR);
+}
+
+/**
+ * s3c_hsudc_setup_ep - Configure all endpoints to default state.
+ * @hsudc: Reference to device controller.
+ *
+ * Configures all endpoints to default state.
+ */
+static void s3c_hsudc_setup_ep(struct s3c_hsudc *hsudc)
+{
+	int epnum;
+
+	hsudc->ep0state = WAIT_FOR_SETUP;
+	INIT_LIST_HEAD(&hsudc->gadget.ep_list);
+	for (epnum = 0; epnum < hsudc->pd->epnum; epnum++)
+		s3c_hsudc_initep(hsudc, &hsudc->ep[epnum], epnum);
+}
+
+/**
+ * s3c_hsudc_reconfig - Reconfigure the device controller to default state.
+ * @hsudc: Reference to device controller.
+ *
+ * Reconfigures the device controller registers to a default state.
+ */
+static void s3c_hsudc_reconfig(struct s3c_hsudc *hsudc)
+{
+	writel(0xAA, hsudc->regs + S3C_EDR);
+	writel(1, hsudc->regs + S3C_EIER);
+	writel(0, hsudc->regs + S3C_TR);
+	writel(S3C_SCR_DTZIEN_EN | S3C_SCR_RRD_EN | S3C_SCR_SUS_EN |
+			S3C_SCR_RST_EN, hsudc->regs + S3C_SCR);
+	writel(0, hsudc->regs + S3C_EP0CR);
+
+	s3c_hsudc_setup_ep(hsudc);
+}
+
+/**
+ * s3c_hsudc_irq - Interrupt handler for device controller.
+ * @irq: Not used.
+ * @_dev: Reference to the device controller.
+ *
+ * Interrupt handler for the device controller. This handler handles controller
+ * interrupts and endpoint interrupts.
+ */
+static irqreturn_t s3c_hsudc_irq(int irq, void *_dev)
+{
+	struct s3c_hsudc *hsudc = _dev;
+	struct s3c_hsudc_ep *hsep;
+	u32 ep_intr;
+	u32 sys_status;
+	u32 ep_idx;
+
+	spin_lock(&hsudc->lock);
+
+	sys_status = readl(hsudc->regs + S3C_SSR);
+	ep_intr = readl(hsudc->regs + S3C_EIR) & 0x3FF;
+
+	if (!ep_intr && !(sys_status & S3C_SSR_DTZIEN_EN)) {
+		spin_unlock(&hsudc->lock);
+		return IRQ_HANDLED;
+	}
+
+	if (sys_status) {
+		if (sys_status & S3C_SSR_VBUSON)
+			writel(S3C_SSR_VBUSON, hsudc->regs + S3C_SSR);
+
+		if (sys_status & S3C_SSR_ERR)
+			writel(S3C_SSR_ERR, hsudc->regs + S3C_SSR);
+
+		if (sys_status & S3C_SSR_SDE) {
+			writel(S3C_SSR_SDE, hsudc->regs + S3C_SSR);
+			hsudc->gadget.speed = (sys_status & S3C_SSR_HSP) ?
+				USB_SPEED_HIGH : USB_SPEED_FULL;
+		}
+
+		if (sys_status & S3C_SSR_SUSPEND) {
+			writel(S3C_SSR_SUSPEND, hsudc->regs + S3C_SSR);
+			if (hsudc->gadget.speed != USB_SPEED_UNKNOWN
+				&& hsudc->driver && hsudc->driver->suspend)
+				hsudc->driver->suspend(&hsudc->gadget);
+		}
+
+		if (sys_status & S3C_SSR_RESUME) {
+			writel(S3C_SSR_RESUME, hsudc->regs + S3C_SSR);
+			if (hsudc->gadget.speed != USB_SPEED_UNKNOWN
+				&& hsudc->driver && hsudc->driver->resume)
+				hsudc->driver->resume(&hsudc->gadget);
+		}
+
+		if (sys_status & S3C_SSR_RESET) {
+			writel(S3C_SSR_RESET, hsudc->regs + S3C_SSR);
+			for (ep_idx = 0; ep_idx < hsudc->pd->epnum; ep_idx++) {
+				hsep = &hsudc->ep[ep_idx];
+				hsep->stopped = 1;
+				s3c_hsudc_nuke_ep(hsep, -ECONNRESET);
+			}
+			s3c_hsudc_reconfig(hsudc);
+			hsudc->ep0state = WAIT_FOR_SETUP;
+		}
+	}
+
+	if (ep_intr & S3C_EIR_EP0) {
+		writel(S3C_EIR_EP0, hsudc->regs + S3C_EIR);
+		set_index(hsudc, 0);
+		s3c_hsudc_handle_ep0_intr(hsudc);
+	}
+
+	ep_intr >>= 1;
+	ep_idx = 1;
+	while (ep_intr) {
+		if (ep_intr & 1)  {
+			hsep = &hsudc->ep[ep_idx];
+			set_index(hsudc, ep_idx);
+			writel(1 << ep_idx, hsudc->regs + S3C_EIR);
+			if (ep_is_in(hsep))
+				s3c_hsudc_epin_intr(hsudc, ep_idx);
+			else
+				s3c_hsudc_epout_intr(hsudc, ep_idx);
+		}
+		ep_intr >>= 1;
+		ep_idx++;
+	}
+
+	spin_unlock(&hsudc->lock);
+	return IRQ_HANDLED;
+}
+
+int usb_gadget_probe_driver(struct usb_gadget_driver *driver,
+               int (*bind)(struct usb_gadget *))
+{
+	struct s3c_hsudc *hsudc = the_controller;
+	int ret;
+
+	if (!driver
+		|| (driver->speed != USB_SPEED_FULL &&
+			driver->speed != USB_SPEED_HIGH)
+		|| !bind
+		|| !driver->unbind || !driver->disconnect || !driver->setup)
+		return -EINVAL;
+
+	if (!hsudc)
+		return -ENODEV;
+
+	if (hsudc->driver)
+		return -EBUSY;
+
+	hsudc->driver = driver;
+	hsudc->gadget.dev.driver = &driver->driver;
+	hsudc->gadget.speed = USB_SPEED_UNKNOWN;
+	ret = device_add(&hsudc->gadget.dev);
+	if (ret) {
+		dev_err(hsudc->dev, "failed to probe gadget device");
+		return ret;
+	}
+
+	ret = bind(&hsudc->gadget);
+	if (ret) {
+		dev_err(hsudc->dev, "%s: bind failed\n", hsudc->gadget.name);
+		device_del(&hsudc->gadget.dev);
+
+		hsudc->driver = NULL;
+		hsudc->gadget.dev.driver = NULL;
+		return ret;
+	}
+
+	enable_irq(hsudc->irq);
+	dev_info(hsudc->dev, "bound driver %s\n", driver->driver.name);
+
+	s3c_hsudc_reconfig(hsudc);
+	s3c_hsudc_init_phy();
+	if (hsudc->pd->gpio_init)
+		hsudc->pd->gpio_init();
+
+	return 0;
+}
+EXPORT_SYMBOL(usb_gadget_probe_driver);
+
+int usb_gadget_unregister_driver(struct usb_gadget_driver *driver)
+{
+	struct s3c_hsudc *hsudc = the_controller;
+	unsigned long flags;
+
+	if (!hsudc)
+		return -ENODEV;
+
+	if (!driver || driver != hsudc->driver || !driver->unbind)
+		return -EINVAL;
+
+	spin_lock_irqsave(&hsudc->lock, flags);
+	hsudc->driver = 0;
+	s3c_hsudc_uninit_phy();
+	if (hsudc->pd->gpio_uninit)
+		hsudc->pd->gpio_uninit();
+	s3c_hsudc_stop_activity(hsudc, driver);
+	spin_unlock_irqrestore(&hsudc->lock, flags);
+
+	driver->unbind(&hsudc->gadget);
+	device_del(&hsudc->gadget.dev);
+	disable_irq(hsudc->irq);
+
+	dev_info(hsudc->dev, "unregistered gadget driver '%s'\n",
+			driver->driver.name);
+	return 0;
+}
+EXPORT_SYMBOL(usb_gadget_unregister_driver);
+
+static inline u32 s3c_hsudc_read_frameno(struct s3c_hsudc *hsudc)
+{
+	return readl(hsudc->regs + S3C_FNR) & 0x3FF;
+}
+
+static int s3c_hsudc_gadget_getframe(struct usb_gadget *gadget)
+{
+	return s3c_hsudc_read_frameno(to_hsudc(gadget));
+}
+
+static struct usb_gadget_ops s3c_hsudc_gadget_ops = {
+	.get_frame	= s3c_hsudc_gadget_getframe,
+};
+
+static int s3c_hsudc_probe(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	struct resource *res;
+	struct s3c_hsudc *hsudc;
+	struct s3c24xx_hsudc_platdata *pd = pdev->dev.platform_data;
+	int ret;
+
+	hsudc = kzalloc(sizeof(struct s3c_hsudc) +
+			sizeof(struct s3c_hsudc_ep) * pd->epnum,
+			GFP_KERNEL);
+	if (!hsudc) {
+		dev_err(dev, "cannot allocate memory\n");
+		return -ENOMEM;
+	}
+
+	the_controller = hsudc;
+	platform_set_drvdata(pdev, dev);
+	hsudc->dev = dev;
+	hsudc->pd = pdev->dev.platform_data;
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!res) {
+		dev_err(dev, "unable to obtain driver resource data\n");
+		ret = -ENODEV;
+		goto err_res;
+	}
+
+	hsudc->mem_rsrc = request_mem_region(res->start, resource_size(res),
+				dev_name(&pdev->dev));
+	if (!hsudc->mem_rsrc) {
+		dev_err(dev, "failed to reserve register area\n");
+		ret = -ENODEV;
+		goto err_res;
+	}
+
+	hsudc->regs = ioremap(res->start, resource_size(res));
+	if (!hsudc->regs) {
+		dev_err(dev, "error mapping device register area\n");
+		ret = -EBUSY;
+		goto err_remap;
+	}
+
+	ret = platform_get_irq(pdev, 0);
+	if (ret < 0) {
+		dev_err(dev, "unable to obtain IRQ number\n");
+		goto err_irq;
+	}
+	hsudc->irq = ret;
+
+	ret = request_irq(hsudc->irq, s3c_hsudc_irq, 0, driver_name, hsudc);
+	if (ret < 0) {
+		dev_err(dev, "irq request failed\n");
+		goto err_irq;
+	}
+
+	spin_lock_init(&hsudc->lock);
+
+	device_initialize(&hsudc->gadget.dev);
+	dev_set_name(&hsudc->gadget.dev, "gadget");
+
+	hsudc->gadget.is_dualspeed = 1;
+	hsudc->gadget.ops = &s3c_hsudc_gadget_ops;
+	hsudc->gadget.name = dev_name(dev);
+	hsudc->gadget.dev.parent = dev;
+	hsudc->gadget.dev.dma_mask = dev->dma_mask;
+	hsudc->gadget.ep0 = &hsudc->ep[0].ep;
+
+	hsudc->gadget.is_otg = 0;
+	hsudc->gadget.is_a_peripheral = 0;
+
+	s3c_hsudc_setup_ep(hsudc);
+
+	hsudc->uclk = clk_get(&pdev->dev, "usb-device");
+	if (hsudc->uclk == NULL) {
+		dev_err(dev, "failed to find usb-device clock source\n");
+		return -ENOENT;
+	}
+	clk_enable(hsudc->uclk);
+
+	local_irq_disable();
+
+	disable_irq(hsudc->irq);
+	local_irq_enable();
+	return 0;
+
+err_irq:
+	iounmap(hsudc->regs);
+
+err_remap:
+	release_resource(hsudc->mem_rsrc);
+	kfree(hsudc->mem_rsrc);
+
+err_res:
+	kfree(hsudc);
+	return ret;
+}
+
+static struct platform_driver s3c_hsudc_driver = {
+	.driver		= {
+		.owner	= THIS_MODULE,
+		.name	= "s3c-hsudc",
+	},
+	.probe		= s3c_hsudc_probe,
+};
+
+static int __init s3c_hsudc_modinit(void)
+{
+	return platform_driver_register(&s3c_hsudc_driver);
+}
+
+static void __exit s3c_hsudc_modexit(void)
+{
+	platform_driver_unregister(&s3c_hsudc_driver);
+}
+
+module_init(s3c_hsudc_modinit);
+module_exit(s3c_hsudc_modexit);
+
+MODULE_DESCRIPTION("Samsung S3C24XX USB high-speed controller driver");
+MODULE_AUTHOR("Thomas Abraham <thomas.ab@samsung.com>");
+MODULE_LICENSE("GPL");
-- 
1.7.1

^ permalink raw reply related	[flat|nested] 52+ messages in thread

* [PATCH v4 3/4] USB: Gadget: Add Samsung S3C24XX USB High-Speed controller driver
@ 2011-04-14  9:11         ` Heiko Stübner
  0 siblings, 0 replies; 52+ messages in thread
From: Heiko Stübner @ 2011-04-14  9:11 UTC (permalink / raw)
  To: linux-arm-kernel

From: Thomas Abraham <thomas.ab@samsung.com>

The Samsung's S3C2416, S3C2443 and S3C2450 includes a USB High-Speed
device controller module. This driver enables support for USB high-speed
gadget functionality for the Samsung S3C24xx SoC's that include this
controller.

Signed-off-by: Thomas Abraham <thomas.ab@samsung.com>
Signed-off-by: Sangbeom Kim <sbkim73@samsung.com>
Signed-off-by: Kukjin Kim <kgene.kim@samsung.com>
Signed-off-by: Alexander Neumann <alexander@bumpern.de>
Signed-off-by: Heiko Stuebner <heiko@sntech.de>
---
Changes since v1:
- Addressed comments from Ben Dooks

Changes since v2:
- Update the driver to match the data structure changes introduced by
  commit b0fca50f5a94a268ed02cfddf44448051ed9343f.
  Alexander Neumann <alexander@debian.org>
- Move s3c_hsudc to 0x29 in usb_gadget_controller_number in gadget_chips.h
  Heiko Stuebner <heiko@sntech.de>

Changes since v3:
- Update gadget_chips.h-id again
  Heiko Stuebner <heiko@sntech.de>
 drivers/usb/gadget/Kconfig        |   17 +
 drivers/usb/gadget/Makefile       |    1 +
 drivers/usb/gadget/gadget_chips.h |    8 +
 drivers/usb/gadget/s3c-hsudc.c    | 1329 +++++++++++++++++++++++++++++++++++++
 4 files changed, 1355 insertions(+), 0 deletions(-)
 create mode 100644 drivers/usb/gadget/s3c-hsudc.c

diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig
index 4c02b9f..58456d1 100644
--- a/drivers/usb/gadget/Kconfig
+++ b/drivers/usb/gadget/Kconfig
@@ -356,6 +356,23 @@ config USB_S3C2410_DEBUG
 	boolean "S3C2410 udc debug messages"
 	depends on USB_GADGET_S3C2410
 
+config USB_GADGET_S3C_HSUDC
+	boolean "S3C2416, S3C2443 and S3C2450 USB Device Controller"
+	depends on ARCH_S3C2410
+	select USB_GADGET_DUALSPEED
+	help
+	  Samsung's S3C2416, S3C2443 and S3C2450 is an ARM9 based SoC
+	  integrated with dual speed USB 2.0 device controller. It has
+	  8 endpoints, as well as endpoint zero.
+
+	  This driver has been tested on S3C2416 and S3C2450 processors.
+
+config USB_S3C_HSUDC
+	tristate
+	depends on USB_GADGET_S3C_HSUDC
+	default USB_GADGET
+	select USB_GADGET_SELECTED
+
 config USB_GADGET_PXA_U2O
 	boolean "PXA9xx Processor USB2.0 controller"
 	select USB_GADGET_DUALSPEED
diff --git a/drivers/usb/gadget/Makefile b/drivers/usb/gadget/Makefile
index 1ea15ee..4fe92b1 100644
--- a/drivers/usb/gadget/Makefile
+++ b/drivers/usb/gadget/Makefile
@@ -22,6 +22,7 @@ obj-$(CONFIG_USB_R8A66597)	+= r8a66597-udc.o
 obj-$(CONFIG_USB_FSL_QE)	+= fsl_qe_udc.o
 obj-$(CONFIG_USB_CI13XXX_PCI)	+= ci13xxx_pci.o
 obj-$(CONFIG_USB_S3C_HSOTG)	+= s3c-hsotg.o
+obj-$(CONFIG_USB_S3C_HSUDC)	+= s3c-hsudc.o
 obj-$(CONFIG_USB_LANGWELL)	+= langwell_udc.o
 obj-$(CONFIG_USB_EG20T)		+= pch_udc.o
 obj-$(CONFIG_USB_PXA_U2O)	+= mv_udc.o
diff --git a/drivers/usb/gadget/gadget_chips.h b/drivers/usb/gadget/gadget_chips.h
index ec3fd97..bcdac7c 100644
--- a/drivers/usb/gadget/gadget_chips.h
+++ b/drivers/usb/gadget/gadget_chips.h
@@ -136,6 +136,12 @@
 #define gadget_is_s3c_hsotg(g)    0
 #endif
 
+#ifdef CONFIG_USB_S3C_HSUDC
+#define gadget_is_s3c_hsudc(g) (!strcmp("s3c-hsudc", (g)->name))
+#else
+#define gadget_is_s3c_hsudc(g) 0
+#endif
+
 #ifdef CONFIG_USB_GADGET_EG20T
 #define	gadget_is_pch(g)	(!strcmp("pch_udc", (g)->name))
 #else
@@ -215,6 +221,8 @@ static inline int usb_gadget_controller_number(struct usb_gadget *gadget)
 		return 0x28;
 	else if (gadget_is_renesas_usbhs(gadget))
 		return 0x29;
+	else if (gadget_is_s3c_hsudc(gadget))
+		return 0x30;
 
 	return -ENOENT;
 }
diff --git a/drivers/usb/gadget/s3c-hsudc.c b/drivers/usb/gadget/s3c-hsudc.c
new file mode 100644
index 0000000..b98d8a1
--- /dev/null
+++ b/drivers/usb/gadget/s3c-hsudc.c
@@ -0,0 +1,1329 @@
+/* linux/drivers/usb/gadget/s3c-hsudc.c
+ *
+ * Copyright (c) 2010 Samsung Electronics Co., Ltd.
+ *		http://www.samsung.com/
+ *
+ * S3C24XX USB 2.0 High-speed USB controller gadget driver
+ *
+ * The S3C24XX USB 2.0 high-speed USB controller supports upto 9 endpoints.
+ * Each endpoint can be configured as either in or out endpoint. Endpoints
+ * can be configured for Bulk or Interrupt transfer mode.
+ *
+ * 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.
+*/
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/spinlock.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <linux/dma-mapping.h>
+#include <linux/delay.h>
+#include <linux/io.h>
+#include <linux/slab.h>
+#include <linux/clk.h>
+#include <linux/usb/ch9.h>
+#include <linux/usb/gadget.h>
+
+#include <mach/regs-s3c2443-clock.h>
+#include <plat/udc.h>
+
+#define S3C_HSUDC_REG(x)	(x)
+
+/* Non-Indexed Registers */
+#define S3C_IR				S3C_HSUDC_REG(0x00) /* Index Register */
+#define S3C_EIR				S3C_HSUDC_REG(0x04) /* EP Intr Status */
+#define S3C_EIR_EP0			(1<<0)
+#define S3C_EIER			S3C_HSUDC_REG(0x08) /* EP Intr Enable */
+#define S3C_FAR				S3C_HSUDC_REG(0x0c) /* Gadget Address */
+#define S3C_FNR				S3C_HSUDC_REG(0x10) /* Frame Number */
+#define S3C_EDR				S3C_HSUDC_REG(0x14) /* EP Direction */
+#define S3C_TR				S3C_HSUDC_REG(0x18) /* Test Register */
+#define S3C_SSR				S3C_HSUDC_REG(0x1c) /* System Status */
+#define S3C_SSR_DTZIEN_EN		(0xff8f)
+#define S3C_SSR_ERR			(0xff80)
+#define S3C_SSR_VBUSON			(1 << 8)
+#define S3C_SSR_HSP			(1 << 4)
+#define S3C_SSR_SDE			(1 << 3)
+#define S3C_SSR_RESUME			(1 << 2)
+#define S3C_SSR_SUSPEND			(1 << 1)
+#define S3C_SSR_RESET			(1 << 0)
+#define S3C_SCR				S3C_HSUDC_REG(0x20) /* System Control */
+#define S3C_SCR_DTZIEN_EN		(1 << 14)
+#define S3C_SCR_RRD_EN			(1 << 5)
+#define S3C_SCR_SUS_EN			(1 << 1)
+#define S3C_SCR_RST_EN			(1 << 0)
+#define S3C_EP0SR			S3C_HSUDC_REG(0x24) /* EP0 Status */
+#define S3C_EP0SR_EP0_LWO		(1 << 6)
+#define S3C_EP0SR_STALL			(1 << 4)
+#define S3C_EP0SR_TX_SUCCESS		(1 << 1)
+#define S3C_EP0SR_RX_SUCCESS		(1 << 0)
+#define S3C_EP0CR			S3C_HSUDC_REG(0x28) /* EP0 Control */
+#define S3C_BR(_x)			S3C_HSUDC_REG(0x60 + (_x * 4))
+
+/* Indexed Registers */
+#define S3C_ESR				S3C_HSUDC_REG(0x2c) /* EPn Status */
+#define S3C_ESR_FLUSH			(1 << 6)
+#define S3C_ESR_STALL			(1 << 5)
+#define S3C_ESR_LWO			(1 << 4)
+#define S3C_ESR_PSIF_ONE		(1 << 2)
+#define S3C_ESR_PSIF_TWO		(2 << 2)
+#define S3C_ESR_TX_SUCCESS		(1 << 1)
+#define S3C_ESR_RX_SUCCESS		(1 << 0)
+#define S3C_ECR				S3C_HSUDC_REG(0x30) /* EPn Control */
+#define S3C_ECR_DUEN			(1 << 7)
+#define S3C_ECR_FLUSH			(1 << 6)
+#define S3C_ECR_STALL			(1 << 1)
+#define S3C_ECR_IEMS			(1 << 0)
+#define S3C_BRCR			S3C_HSUDC_REG(0x34) /* Read Count */
+#define S3C_BWCR			S3C_HSUDC_REG(0x38) /* Write Count */
+#define S3C_MPR				S3C_HSUDC_REG(0x3c) /* Max Pkt Size */
+
+#define WAIT_FOR_SETUP			(0)
+#define DATA_STATE_XMIT			(1)
+#define DATA_STATE_RECV			(2)
+
+/**
+ * struct s3c_hsudc_ep - Endpoint representation used by driver.
+ * @ep: USB gadget layer representation of device endpoint.
+ * @name: Endpoint name (as required by ep autoconfiguration).
+ * @dev: Reference to the device controller to which this EP belongs.
+ * @desc: Endpoint descriptor obtained from the gadget driver.
+ * @queue: Transfer request queue for the endpoint.
+ * @stopped: Maintains state of endpoint, set if EP is halted.
+ * @bEndpointAddress: EP address (including direction bit).
+ * @fifo: Base address of EP FIFO.
+ */
+struct s3c_hsudc_ep {
+	struct usb_ep ep;
+	char name[20];
+	struct s3c_hsudc *dev;
+	const struct usb_endpoint_descriptor *desc;
+	struct list_head queue;
+	u8 stopped;
+	u8 bEndpointAddress;
+	void __iomem *fifo;
+};
+
+/**
+ * struct s3c_hsudc_req - Driver encapsulation of USB gadget transfer request.
+ * @req: Reference to USB gadget transfer request.
+ * @queue: Used for inserting this request to the endpoint request queue.
+ */
+struct s3c_hsudc_req {
+	struct usb_request req;
+	struct list_head queue;
+};
+
+/**
+ * struct s3c_hsudc - Driver's abstraction of the device controller.
+ * @gadget: Instance of usb_gadget which is referenced by gadget driver.
+ * @driver: Reference to currenty active gadget driver.
+ * @dev: The device reference used by probe function.
+ * @lock: Lock to synchronize the usage of Endpoints (EP's are indexed).
+ * @regs: Remapped base address of controller's register space.
+ * @mem_rsrc: Device memory resource used for remapping device register space.
+ * irq: IRQ number used by the controller.
+ * uclk: Reference to the controller clock.
+ * ep0state: Current state of EP0.
+ * ep: List of endpoints supported by the controller.
+ */
+struct s3c_hsudc {
+	struct usb_gadget gadget;
+	struct usb_gadget_driver *driver;
+	struct device *dev;
+	struct s3c24xx_hsudc_platdata *pd;
+	spinlock_t lock;
+	void __iomem *regs;
+	struct resource *mem_rsrc;
+	int irq;
+	struct clk *uclk;
+	int ep0state;
+	struct s3c_hsudc_ep ep[];
+};
+
+#define ep_maxpacket(_ep)	((_ep)->ep.maxpacket)
+#define ep_is_in(_ep)		((_ep)->bEndpointAddress & USB_DIR_IN)
+#define ep_index(_ep)		((_ep)->bEndpointAddress & \
+					USB_ENDPOINT_NUMBER_MASK)
+
+static struct s3c_hsudc *the_controller;
+static const char driver_name[] = "s3c-udc";
+static const char ep0name[] = "ep0-control";
+
+static inline struct s3c_hsudc_req *our_req(struct usb_request *req)
+{
+	return container_of(req, struct s3c_hsudc_req, req);
+}
+
+static inline struct s3c_hsudc_ep *our_ep(struct usb_ep *ep)
+{
+	return container_of(ep, struct s3c_hsudc_ep, ep);
+}
+
+static inline struct s3c_hsudc *to_hsudc(struct usb_gadget *gadget)
+{
+	return container_of(gadget, struct s3c_hsudc, gadget);
+}
+
+static inline void set_index(struct s3c_hsudc *hsudc, int ep_addr)
+{
+	ep_addr &= USB_ENDPOINT_NUMBER_MASK;
+	writel(ep_addr, hsudc->regs + S3C_IR);
+}
+
+static inline void __orr32(void __iomem *ptr, u32 val)
+{
+	writel(readl(ptr) | val, ptr);
+}
+
+static void s3c_hsudc_init_phy(void)
+{
+	u32 cfg;
+
+	cfg = readl(S3C2443_PWRCFG) | S3C2443_PWRCFG_USBPHY;
+	writel(cfg, S3C2443_PWRCFG);
+
+	cfg = readl(S3C2443_URSTCON);
+	cfg |= (S3C2443_URSTCON_FUNCRST | S3C2443_URSTCON_PHYRST);
+	writel(cfg, S3C2443_URSTCON);
+	mdelay(1);
+
+	cfg = readl(S3C2443_URSTCON);
+	cfg &= ~(S3C2443_URSTCON_FUNCRST | S3C2443_URSTCON_PHYRST);
+	writel(cfg, S3C2443_URSTCON);
+
+	cfg = readl(S3C2443_PHYCTRL);
+	cfg &= ~(S3C2443_PHYCTRL_CLKSEL | S3C2443_PHYCTRL_DSPORT);
+	cfg |= (S3C2443_PHYCTRL_EXTCLK | S3C2443_PHYCTRL_PLLSEL);
+	writel(cfg, S3C2443_PHYCTRL);
+
+	cfg = readl(S3C2443_PHYPWR);
+	cfg &= ~(S3C2443_PHYPWR_FSUSPEND | S3C2443_PHYPWR_PLL_PWRDN |
+		S3C2443_PHYPWR_XO_ON | S3C2443_PHYPWR_PLL_REFCLK |
+		S3C2443_PHYPWR_ANALOG_PD);
+	cfg |= S3C2443_PHYPWR_COMMON_ON;
+	writel(cfg, S3C2443_PHYPWR);
+
+	cfg = readl(S3C2443_UCLKCON);
+	cfg |= (S3C2443_UCLKCON_DETECT_VBUS | S3C2443_UCLKCON_FUNC_CLKEN |
+		S3C2443_UCLKCON_TCLKEN);
+	writel(cfg, S3C2443_UCLKCON);
+}
+
+static void s3c_hsudc_uninit_phy(void)
+{
+	u32 cfg;
+
+	cfg = readl(S3C2443_PWRCFG) & ~S3C2443_PWRCFG_USBPHY;
+	writel(cfg, S3C2443_PWRCFG);
+
+	writel(S3C2443_PHYPWR_FSUSPEND, S3C2443_PHYPWR);
+
+	cfg = readl(S3C2443_UCLKCON) & ~S3C2443_UCLKCON_FUNC_CLKEN;
+	writel(cfg, S3C2443_UCLKCON);
+}
+
+/**
+ * s3c_hsudc_complete_request - Complete a transfer request.
+ * @hsep: Endpoint to which the request belongs.
+ * @hsreq: Transfer request to be completed.
+ * @status: Transfer completion status for the transfer request.
+ */
+static void s3c_hsudc_complete_request(struct s3c_hsudc_ep *hsep,
+				struct s3c_hsudc_req *hsreq, int status)
+{
+	unsigned int stopped = hsep->stopped;
+	struct s3c_hsudc *hsudc = hsep->dev;
+
+	list_del_init(&hsreq->queue);
+	hsreq->req.status = status;
+
+	if (!ep_index(hsep)) {
+		hsudc->ep0state = WAIT_FOR_SETUP;
+		hsep->bEndpointAddress &= ~USB_DIR_IN;
+	}
+
+	hsep->stopped = 1;
+	spin_unlock(&hsudc->lock);
+	if (hsreq->req.complete != NULL)
+		hsreq->req.complete(&hsep->ep, &hsreq->req);
+	spin_lock(&hsudc->lock);
+	hsep->stopped = stopped;
+}
+
+/**
+ * s3c_hsudc_nuke_ep - Terminate all requests queued for a endpoint.
+ * @hsep: Endpoint for which queued requests have to be terminated.
+ * @status: Transfer completion status for the transfer request.
+ */
+static void s3c_hsudc_nuke_ep(struct s3c_hsudc_ep *hsep, int status)
+{
+	struct s3c_hsudc_req *hsreq;
+
+	while (!list_empty(&hsep->queue)) {
+		hsreq = list_entry(hsep->queue.next,
+				struct s3c_hsudc_req, queue);
+		s3c_hsudc_complete_request(hsep, hsreq, status);
+	}
+}
+
+/**
+ * s3c_hsudc_stop_activity - Stop activity on all endpoints.
+ * @hsudc: Device controller for which EP activity is to be stopped.
+ * @driver: Reference to the gadget driver which is currently active.
+ *
+ * All the endpoints are stopped and any pending transfer requests if any on
+ * the endpoint are terminated.
+ */
+static void s3c_hsudc_stop_activity(struct s3c_hsudc *hsudc,
+			  struct usb_gadget_driver *driver)
+{
+	struct s3c_hsudc_ep *hsep;
+	int epnum;
+
+	hsudc->gadget.speed = USB_SPEED_UNKNOWN;
+
+	for (epnum = 0; epnum < hsudc->pd->epnum; epnum++) {
+		hsep = &hsudc->ep[epnum];
+		hsep->stopped = 1;
+		s3c_hsudc_nuke_ep(hsep, -ESHUTDOWN);
+	}
+
+	spin_unlock(&hsudc->lock);
+	driver->disconnect(&hsudc->gadget);
+	spin_lock(&hsudc->lock);
+}
+
+/**
+ * s3c_hsudc_read_setup_pkt - Read the received setup packet from EP0 fifo.
+ * @hsudc: Device controller from which setup packet is to be read.
+ * @buf: The buffer into which the setup packet is read.
+ *
+ * The setup packet received in the EP0 fifo is read and stored into a
+ * given buffer address.
+ */
+
+static void s3c_hsudc_read_setup_pkt(struct s3c_hsudc *hsudc, u16 *buf)
+{
+	int count;
+
+	count = readl(hsudc->regs + S3C_BRCR);
+	while (count--)
+		*buf++ = (u16)readl(hsudc->regs + S3C_BR(0));
+
+	writel(S3C_EP0SR_RX_SUCCESS, hsudc->regs + S3C_EP0SR);
+}
+
+/**
+ * s3c_hsudc_write_fifo - Write next chunk of transfer data to EP fifo.
+ * @hsep: Endpoint to which the data is to be written.
+ * @hsreq: Transfer request from which the next chunk of data is written.
+ *
+ * Write the next chunk of data from a transfer request to the endpoint FIFO.
+ * If the transfer request completes, 1 is returned, otherwise 0 is returned.
+ */
+static int s3c_hsudc_write_fifo(struct s3c_hsudc_ep *hsep,
+				struct s3c_hsudc_req *hsreq)
+{
+	u16 *buf;
+	u32 max = ep_maxpacket(hsep);
+	u32 count, length;
+	bool is_last;
+	void __iomem *fifo = hsep->fifo;
+
+	buf = hsreq->req.buf + hsreq->req.actual;
+	prefetch(buf);
+
+	length = hsreq->req.length - hsreq->req.actual;
+	length = min(length, max);
+	hsreq->req.actual += length;
+
+	writel(length, hsep->dev->regs + S3C_BWCR);
+	for (count = 0; count < length; count += 2)
+		writel(*buf++, fifo);
+
+	if (count != max) {
+		is_last = true;
+	} else {
+		if (hsreq->req.length != hsreq->req.actual || hsreq->req.zero)
+			is_last = false;
+		else
+			is_last = true;
+	}
+
+	if (is_last) {
+		s3c_hsudc_complete_request(hsep, hsreq, 0);
+		return 1;
+	}
+
+	return 0;
+}
+
+/**
+ * s3c_hsudc_read_fifo - Read the next chunk of data from EP fifo.
+ * @hsep: Endpoint from which the data is to be read.
+ * @hsreq: Transfer request to which the next chunk of data read is written.
+ *
+ * Read the next chunk of data from the endpoint FIFO and a write it to the
+ * transfer request buffer. If the transfer request completes, 1 is returned,
+ * otherwise 0 is returned.
+ */
+static int s3c_hsudc_read_fifo(struct s3c_hsudc_ep *hsep,
+				struct s3c_hsudc_req *hsreq)
+{
+	struct s3c_hsudc *hsudc = hsep->dev;
+	u32 csr, offset;
+	u16 *buf, word;
+	u32 buflen, rcnt, rlen;
+	void __iomem *fifo = hsep->fifo;
+	u32 is_short = 0;
+
+	offset = (ep_index(hsep)) ? S3C_ESR : S3C_EP0SR;
+	csr = readl(hsudc->regs + offset);
+	if (!(csr & S3C_ESR_RX_SUCCESS))
+		return -EINVAL;
+
+	buf = hsreq->req.buf + hsreq->req.actual;
+	prefetchw(buf);
+	buflen = hsreq->req.length - hsreq->req.actual;
+
+	rcnt = readl(hsudc->regs + S3C_BRCR);
+	rlen = (csr & S3C_ESR_LWO) ? (rcnt * 2 - 1) : (rcnt * 2);
+
+	hsreq->req.actual += min(rlen, buflen);
+	is_short = (rlen < hsep->ep.maxpacket);
+
+	while (rcnt-- != 0) {
+		word = (u16)readl(fifo);
+		if (buflen) {
+			*buf++ = word;
+			buflen--;
+		} else {
+			hsreq->req.status = -EOVERFLOW;
+		}
+	}
+
+	writel(S3C_ESR_RX_SUCCESS, hsudc->regs + offset);
+
+	if (is_short || hsreq->req.actual == hsreq->req.length) {
+		s3c_hsudc_complete_request(hsep, hsreq, 0);
+		return 1;
+	}
+
+	return 0;
+}
+
+/**
+ * s3c_hsudc_epin_intr - Handle in-endpoint interrupt.
+ * @hsudc - Device controller for which the interrupt is to be handled.
+ * @ep_idx - Endpoint number on which an interrupt is pending.
+ *
+ * Handles interrupt for a in-endpoint. The interrupts that are handled are
+ * stall and data transmit complete interrupt.
+ */
+static void s3c_hsudc_epin_intr(struct s3c_hsudc *hsudc, u32 ep_idx)
+{
+	struct s3c_hsudc_ep *hsep = &hsudc->ep[ep_idx];
+	struct s3c_hsudc_req *hsreq;
+	u32 csr;
+
+	csr = readl((u32)hsudc->regs + S3C_ESR);
+	if (csr & S3C_ESR_STALL) {
+		writel(S3C_ESR_STALL, hsudc->regs + S3C_ESR);
+		return;
+	}
+
+	if (csr & S3C_ESR_TX_SUCCESS) {
+		writel(S3C_ESR_TX_SUCCESS, hsudc->regs + S3C_ESR);
+		if (list_empty(&hsep->queue))
+			return;
+
+		hsreq = list_entry(hsep->queue.next,
+				struct s3c_hsudc_req, queue);
+		if ((s3c_hsudc_write_fifo(hsep, hsreq) == 0) &&
+				(csr & S3C_ESR_PSIF_TWO))
+			s3c_hsudc_write_fifo(hsep, hsreq);
+	}
+}
+
+/**
+ * s3c_hsudc_epout_intr - Handle out-endpoint interrupt.
+ * @hsudc - Device controller for which the interrupt is to be handled.
+ * @ep_idx - Endpoint number on which an interrupt is pending.
+ *
+ * Handles interrupt for a out-endpoint. The interrupts that are handled are
+ * stall, flush and data ready interrupt.
+ */
+static void s3c_hsudc_epout_intr(struct s3c_hsudc *hsudc, u32 ep_idx)
+{
+	struct s3c_hsudc_ep *hsep = &hsudc->ep[ep_idx];
+	struct s3c_hsudc_req *hsreq;
+	u32 csr;
+
+	csr = readl((u32)hsudc->regs + S3C_ESR);
+	if (csr & S3C_ESR_STALL) {
+		writel(S3C_ESR_STALL, hsudc->regs + S3C_ESR);
+		return;
+	}
+
+	if (csr & S3C_ESR_FLUSH) {
+		__orr32(hsudc->regs + S3C_ECR, S3C_ECR_FLUSH);
+		return;
+	}
+
+	if (csr & S3C_ESR_RX_SUCCESS) {
+		if (list_empty(&hsep->queue))
+			return;
+
+		hsreq = list_entry(hsep->queue.next,
+				struct s3c_hsudc_req, queue);
+		if (((s3c_hsudc_read_fifo(hsep, hsreq)) == 0) &&
+				(csr & S3C_ESR_PSIF_TWO))
+			s3c_hsudc_read_fifo(hsep, hsreq);
+	}
+}
+
+/** s3c_hsudc_set_halt - Set or clear a endpoint halt.
+ * @_ep: Endpoint on which halt has to be set or cleared.
+ * @value: 1 for setting halt on endpoint, 0 to clear halt.
+ *
+ * Set or clear endpoint halt. If halt is set, the endpoint is stopped.
+ * If halt is cleared, for in-endpoints, if there are any pending
+ * transfer requests, transfers are started.
+ */
+static int s3c_hsudc_set_halt(struct usb_ep *_ep, int value)
+{
+	struct s3c_hsudc_ep *hsep = our_ep(_ep);
+	struct s3c_hsudc *hsudc = hsep->dev;
+	struct s3c_hsudc_req *hsreq;
+	unsigned long irqflags;
+	u32 ecr;
+	u32 offset;
+
+	if (value && ep_is_in(hsep) && !list_empty(&hsep->queue))
+		return -EAGAIN;
+
+	spin_lock_irqsave(&hsudc->lock, irqflags);
+	set_index(hsudc, ep_index(hsep));
+	offset = (ep_index(hsep)) ? S3C_ECR : S3C_EP0CR;
+	ecr = readl(hsudc->regs + offset);
+
+	if (value) {
+		ecr |= S3C_ECR_STALL;
+		if (ep_index(hsep))
+			ecr |= S3C_ECR_FLUSH;
+		hsep->stopped = 1;
+	} else {
+		ecr &= ~S3C_ECR_STALL;
+		hsep->stopped = 0;
+	}
+	writel(ecr, hsudc->regs + offset);
+
+	if (ep_is_in(hsep) && !list_empty(&hsep->queue) && !value) {
+		hsreq = list_entry(hsep->queue.next,
+			struct s3c_hsudc_req, queue);
+		if (hsreq)
+			s3c_hsudc_write_fifo(hsep, hsreq);
+	}
+
+	spin_unlock_irqrestore(&hsudc->lock, irqflags);
+	return 0;
+}
+
+/** s3c_hsudc_handle_reqfeat - Handle set feature or clear feature requests.
+ * @_ep: Device controller on which the set/clear feature needs to be handled.
+ * @ctrl: Control request as received on the endpoint 0.
+ *
+ * Handle set feature or clear feature control requests on the control endpoint.
+ */
+static int s3c_hsudc_handle_reqfeat(struct s3c_hsudc *hsudc,
+					struct usb_ctrlrequest *ctrl)
+{
+	struct s3c_hsudc_ep *hsep;
+	bool set = (ctrl->bRequest == USB_REQ_SET_FEATURE);
+	u8 ep_num = ctrl->wIndex & USB_ENDPOINT_NUMBER_MASK;
+
+	if (ctrl->bRequestType == USB_RECIP_ENDPOINT) {
+		hsep = &hsudc->ep[ep_num];
+		switch (le16_to_cpu(ctrl->wValue)) {
+		case USB_ENDPOINT_HALT:
+			s3c_hsudc_set_halt(&hsep->ep, set);
+			return 0;
+		}
+	}
+
+	return -ENOENT;
+}
+
+/**
+ * s3c_hsudc_process_req_status - Handle get status control request.
+ * @hsudc: Device controller on which get status request has be handled.
+ * @ctrl: Control request as received on the endpoint 0.
+ *
+ * Handle get status control request received on control endpoint.
+ */
+static void s3c_hsudc_process_req_status(struct s3c_hsudc *hsudc,
+					struct usb_ctrlrequest *ctrl)
+{
+	struct s3c_hsudc_ep *hsep0 = &hsudc->ep[0];
+	struct s3c_hsudc_req hsreq;
+	struct s3c_hsudc_ep *hsep;
+	__le16 reply;
+	u8 epnum;
+
+	switch (ctrl->bRequestType & USB_RECIP_MASK) {
+	case USB_RECIP_DEVICE:
+		reply = cpu_to_le16(0);
+		break;
+
+	case USB_RECIP_INTERFACE:
+		reply = cpu_to_le16(0);
+		break;
+
+	case USB_RECIP_ENDPOINT:
+		epnum = le16_to_cpu(ctrl->wIndex) & USB_ENDPOINT_NUMBER_MASK;
+		hsep = &hsudc->ep[epnum];
+		reply = cpu_to_le16(hsep->stopped ? 1 : 0);
+		break;
+	}
+
+	INIT_LIST_HEAD(&hsreq.queue);
+	hsreq.req.length = 2;
+	hsreq.req.buf = &reply;
+	hsreq.req.actual = 0;
+	hsreq.req.complete = NULL;
+	s3c_hsudc_write_fifo(hsep0, &hsreq);
+}
+
+/**
+ * s3c_hsudc_process_setup - Process control request received on endpoint 0.
+ * @hsudc: Device controller on which control request has been received.
+ *
+ * Read the control request received on endpoint 0, decode it and handle
+ * the request.
+ */
+static void s3c_hsudc_process_setup(struct s3c_hsudc *hsudc)
+{
+	struct s3c_hsudc_ep *hsep = &hsudc->ep[0];
+	struct usb_ctrlrequest ctrl = {0};
+	int ret;
+
+	s3c_hsudc_nuke_ep(hsep, -EPROTO);
+	s3c_hsudc_read_setup_pkt(hsudc, (u16 *)&ctrl);
+
+	if (ctrl.bRequestType & USB_DIR_IN) {
+		hsep->bEndpointAddress |= USB_DIR_IN;
+		hsudc->ep0state = DATA_STATE_XMIT;
+	} else {
+		hsep->bEndpointAddress &= ~USB_DIR_IN;
+		hsudc->ep0state = DATA_STATE_RECV;
+	}
+
+	switch (ctrl.bRequest) {
+	case USB_REQ_SET_ADDRESS:
+		if (ctrl.bRequestType != (USB_TYPE_STANDARD | USB_RECIP_DEVICE))
+			break;
+		hsudc->ep0state = WAIT_FOR_SETUP;
+		return;
+
+	case USB_REQ_GET_STATUS:
+		if ((ctrl.bRequestType & USB_TYPE_MASK) != USB_TYPE_STANDARD)
+			break;
+		s3c_hsudc_process_req_status(hsudc, &ctrl);
+		return;
+
+	case USB_REQ_SET_FEATURE:
+	case USB_REQ_CLEAR_FEATURE:
+		if ((ctrl.bRequestType & USB_TYPE_MASK) != USB_TYPE_STANDARD)
+			break;
+		s3c_hsudc_handle_reqfeat(hsudc, &ctrl);
+		hsudc->ep0state = WAIT_FOR_SETUP;
+		return;
+	}
+
+	if (hsudc->driver) {
+		spin_unlock(&hsudc->lock);
+		ret = hsudc->driver->setup(&hsudc->gadget, &ctrl);
+		spin_lock(&hsudc->lock);
+
+		if (ctrl.bRequest == USB_REQ_SET_CONFIGURATION) {
+			hsep->bEndpointAddress &= ~USB_DIR_IN;
+			hsudc->ep0state = WAIT_FOR_SETUP;
+		}
+
+		if (ret < 0) {
+			dev_err(hsudc->dev, "setup failed, returned %d\n",
+						ret);
+			s3c_hsudc_set_halt(&hsep->ep, 1);
+			hsudc->ep0state = WAIT_FOR_SETUP;
+			hsep->bEndpointAddress &= ~USB_DIR_IN;
+		}
+	}
+}
+
+/** s3c_hsudc_handle_ep0_intr - Handle endpoint 0 interrupt.
+ * @hsudc: Device controller on which endpoint 0 interrupt has occured.
+ *
+ * Handle endpoint 0 interrupt when it occurs. EP0 interrupt could occur
+ * when a stall handshake is sent to host or data is sent/received on
+ * endpoint 0.
+ */
+static void s3c_hsudc_handle_ep0_intr(struct s3c_hsudc *hsudc)
+{
+	struct s3c_hsudc_ep *hsep = &hsudc->ep[0];
+	struct s3c_hsudc_req *hsreq;
+	u32 csr = readl(hsudc->regs + S3C_EP0SR);
+	u32 ecr;
+
+	if (csr & S3C_EP0SR_STALL) {
+		ecr = readl(hsudc->regs + S3C_EP0CR);
+		ecr &= ~(S3C_ECR_STALL | S3C_ECR_FLUSH);
+		writel(ecr, hsudc->regs + S3C_EP0CR);
+
+		writel(S3C_EP0SR_STALL, hsudc->regs + S3C_EP0SR);
+		hsep->stopped = 0;
+
+		s3c_hsudc_nuke_ep(hsep, -ECONNABORTED);
+		hsudc->ep0state = WAIT_FOR_SETUP;
+		hsep->bEndpointAddress &= ~USB_DIR_IN;
+		return;
+	}
+
+	if (csr & S3C_EP0SR_TX_SUCCESS) {
+		writel(S3C_EP0SR_TX_SUCCESS, hsudc->regs + S3C_EP0SR);
+		if (ep_is_in(hsep)) {
+			if (list_empty(&hsep->queue))
+				return;
+
+			hsreq = list_entry(hsep->queue.next,
+					struct s3c_hsudc_req, queue);
+			s3c_hsudc_write_fifo(hsep, hsreq);
+		}
+	}
+
+	if (csr & S3C_EP0SR_RX_SUCCESS) {
+		if (hsudc->ep0state == WAIT_FOR_SETUP)
+			s3c_hsudc_process_setup(hsudc);
+		else {
+			if (!ep_is_in(hsep)) {
+				if (list_empty(&hsep->queue))
+					return;
+				hsreq = list_entry(hsep->queue.next,
+					struct s3c_hsudc_req, queue);
+				s3c_hsudc_read_fifo(hsep, hsreq);
+			}
+		}
+	}
+}
+
+/**
+ * s3c_hsudc_ep_enable - Enable a endpoint.
+ * @_ep: The endpoint to be enabled.
+ * @desc: Endpoint descriptor.
+ *
+ * Enables a endpoint when called from the gadget driver. Endpoint stall if
+ * any is cleared, transfer type is configured and endpoint interrupt is
+ * enabled.
+ */
+static int s3c_hsudc_ep_enable(struct usb_ep *_ep,
+				const struct usb_endpoint_descriptor *desc)
+{
+	struct s3c_hsudc_ep *hsep;
+	struct s3c_hsudc *hsudc;
+	unsigned long flags;
+	u32 ecr = 0;
+
+	hsep = container_of(_ep, struct s3c_hsudc_ep, ep);
+	if (!_ep || !desc || hsep->desc || _ep->name == ep0name
+		|| desc->bDescriptorType != USB_DT_ENDPOINT
+		|| hsep->bEndpointAddress != desc->bEndpointAddress
+		|| ep_maxpacket(hsep) < le16_to_cpu(desc->wMaxPacketSize))
+		return -EINVAL;
+
+	if ((desc->bmAttributes == USB_ENDPOINT_XFER_BULK
+		&& le16_to_cpu(desc->wMaxPacketSize) != ep_maxpacket(hsep))
+		|| !desc->wMaxPacketSize)
+		return -ERANGE;
+
+	hsudc = hsep->dev;
+	if (!hsudc->driver || hsudc->gadget.speed == USB_SPEED_UNKNOWN)
+		return -ESHUTDOWN;
+
+	spin_lock_irqsave(&hsudc->lock, flags);
+
+	set_index(hsudc, hsep->bEndpointAddress);
+	ecr |= ((usb_endpoint_xfer_int(desc)) ? S3C_ECR_IEMS : S3C_ECR_DUEN);
+	writel(ecr, hsudc->regs + S3C_ECR);
+
+	hsep->stopped = 0;
+	hsep->desc = desc;
+	hsep->ep.maxpacket = le16_to_cpu(desc->wMaxPacketSize);
+
+	s3c_hsudc_set_halt(_ep, 0);
+	__set_bit(ep_index(hsep), hsudc->regs + S3C_EIER);
+
+	spin_unlock_irqrestore(&hsudc->lock, flags);
+	return 0;
+}
+
+/**
+ * s3c_hsudc_ep_disable - Disable a endpoint.
+ * @_ep: The endpoint to be disabled.
+ * @desc: Endpoint descriptor.
+ *
+ * Disables a endpoint when called from the gadget driver.
+ */
+static int s3c_hsudc_ep_disable(struct usb_ep *_ep)
+{
+	struct s3c_hsudc_ep *hsep = our_ep(_ep);
+	struct s3c_hsudc *hsudc = hsep->dev;
+	unsigned long flags;
+
+	if (!_ep || !hsep->desc)
+		return -EINVAL;
+
+	spin_lock_irqsave(&hsudc->lock, flags);
+
+	set_index(hsudc, hsep->bEndpointAddress);
+	__clear_bit(ep_index(hsep), hsudc->regs + S3C_EIER);
+
+	s3c_hsudc_nuke_ep(hsep, -ESHUTDOWN);
+
+	hsep->desc = 0;
+	hsep->stopped = 1;
+
+	spin_unlock_irqrestore(&hsudc->lock, flags);
+	return 0;
+}
+
+/**
+ * s3c_hsudc_alloc_request - Allocate a new request.
+ * @_ep: Endpoint for which request is allocated (not used).
+ * @gfp_flags: Flags used for the allocation.
+ *
+ * Allocates a single transfer request structure when called from gadget driver.
+ */
+static struct usb_request *s3c_hsudc_alloc_request(struct usb_ep *_ep,
+						gfp_t gfp_flags)
+{
+	struct s3c_hsudc_req *hsreq;
+
+	hsreq = kzalloc(sizeof *hsreq, gfp_flags);
+	if (!hsreq)
+		return 0;
+
+	INIT_LIST_HEAD(&hsreq->queue);
+	return &hsreq->req;
+}
+
+/**
+ * s3c_hsudc_free_request - Deallocate a request.
+ * @ep: Endpoint for which request is deallocated (not used).
+ * @_req: Request to be deallocated.
+ *
+ * Allocates a single transfer request structure when called from gadget driver.
+ */
+static void s3c_hsudc_free_request(struct usb_ep *ep, struct usb_request *_req)
+{
+	struct s3c_hsudc_req *hsreq;
+
+	hsreq = container_of(_req, struct s3c_hsudc_req, req);
+	WARN_ON(!list_empty(&hsreq->queue));
+	kfree(hsreq);
+}
+
+/**
+ * s3c_hsudc_queue - Queue a transfer request for the endpoint.
+ * @_ep: Endpoint for which the request is queued.
+ * @_req: Request to be queued.
+ * @gfp_flags: Not used.
+ *
+ * Start or enqueue a request for a endpoint when called from gadget driver.
+ */
+static int s3c_hsudc_queue(struct usb_ep *_ep, struct usb_request *_req,
+			gfp_t gfp_flags)
+{
+	struct s3c_hsudc_req *hsreq;
+	struct s3c_hsudc_ep *hsep;
+	struct s3c_hsudc *hsudc;
+	unsigned long flags;
+	u32 offset;
+	u32 csr;
+
+	hsreq = container_of(_req, struct s3c_hsudc_req, req);
+	if ((!_req || !_req->complete || !_req->buf ||
+		!list_empty(&hsreq->queue)))
+		return -EINVAL;
+
+	hsep = container_of(_ep, struct s3c_hsudc_ep, ep);
+	hsudc = hsep->dev;
+	if (!hsudc->driver || hsudc->gadget.speed == USB_SPEED_UNKNOWN)
+		return -ESHUTDOWN;
+
+	spin_lock_irqsave(&hsudc->lock, flags);
+	set_index(hsudc, hsep->bEndpointAddress);
+
+	_req->status = -EINPROGRESS;
+	_req->actual = 0;
+
+	if (!ep_index(hsep) && _req->length == 0) {
+		hsudc->ep0state = WAIT_FOR_SETUP;
+		s3c_hsudc_complete_request(hsep, hsreq, 0);
+		spin_unlock_irqrestore(&hsudc->lock, flags);
+		return 0;
+	}
+
+	if (list_empty(&hsep->queue) && !hsep->stopped) {
+		offset = (ep_index(hsep)) ? S3C_ESR : S3C_EP0SR;
+		if (ep_is_in(hsep)) {
+			csr = readl((u32)hsudc->regs + offset);
+			if (!(csr & S3C_ESR_TX_SUCCESS) &&
+				(s3c_hsudc_write_fifo(hsep, hsreq) == 1))
+				hsreq = 0;
+		} else {
+			csr = readl((u32)hsudc->regs + offset);
+			if ((csr & S3C_ESR_RX_SUCCESS)
+				   && (s3c_hsudc_read_fifo(hsep, hsreq) == 1))
+				hsreq = 0;
+		}
+	}
+
+	if (hsreq != 0)
+		list_add_tail(&hsreq->queue, &hsep->queue);
+
+	spin_unlock_irqrestore(&hsudc->lock, flags);
+	return 0;
+}
+
+/**
+ * s3c_hsudc_dequeue - Dequeue a transfer request from an endpoint.
+ * @_ep: Endpoint from which the request is dequeued.
+ * @_req: Request to be dequeued.
+ *
+ * Dequeue a request from a endpoint when called from gadget driver.
+ */
+static int s3c_hsudc_dequeue(struct usb_ep *_ep, struct usb_request *_req)
+{
+	struct s3c_hsudc_ep *hsep = our_ep(_ep);
+	struct s3c_hsudc *hsudc = hsep->dev;
+	struct s3c_hsudc_req *hsreq;
+	unsigned long flags;
+
+	hsep = container_of(_ep, struct s3c_hsudc_ep, ep);
+	if (!_ep || hsep->ep.name == ep0name)
+		return -EINVAL;
+
+	spin_lock_irqsave(&hsudc->lock, flags);
+
+	list_for_each_entry(hsreq, &hsep->queue, queue) {
+		if (&hsreq->req == _req)
+			break;
+	}
+	if (&hsreq->req != _req) {
+		spin_unlock_irqrestore(&hsudc->lock, flags);
+		return -EINVAL;
+	}
+
+	set_index(hsudc, hsep->bEndpointAddress);
+	s3c_hsudc_complete_request(hsep, hsreq, -ECONNRESET);
+
+	spin_unlock_irqrestore(&hsudc->lock, flags);
+	return 0;
+}
+
+static struct usb_ep_ops s3c_hsudc_ep_ops = {
+	.enable = s3c_hsudc_ep_enable,
+	.disable = s3c_hsudc_ep_disable,
+	.alloc_request = s3c_hsudc_alloc_request,
+	.free_request = s3c_hsudc_free_request,
+	.queue = s3c_hsudc_queue,
+	.dequeue = s3c_hsudc_dequeue,
+	.set_halt = s3c_hsudc_set_halt,
+};
+
+/**
+ * s3c_hsudc_initep - Initialize a endpoint to default state.
+ * @hsudc - Reference to the device controller.
+ * @hsep - Endpoint to be initialized.
+ * @epnum - Address to be assigned to the endpoint.
+ *
+ * Initialize a endpoint with default configuration.
+ */
+static void s3c_hsudc_initep(struct s3c_hsudc *hsudc,
+				struct s3c_hsudc_ep *hsep, int epnum)
+{
+	char *dir;
+
+	if ((epnum % 2) == 0) {
+		dir = "out";
+	} else {
+		dir = "in";
+		hsep->bEndpointAddress = USB_DIR_IN;
+	}
+
+	hsep->bEndpointAddress |= epnum;
+	if (epnum)
+		snprintf(hsep->name, sizeof(hsep->name), "ep%d%s", epnum, dir);
+	else
+		snprintf(hsep->name, sizeof(hsep->name), "%s", ep0name);
+
+	INIT_LIST_HEAD(&hsep->queue);
+	INIT_LIST_HEAD(&hsep->ep.ep_list);
+	if (epnum)
+		list_add_tail(&hsep->ep.ep_list, &hsudc->gadget.ep_list);
+
+	hsep->dev = hsudc;
+	hsep->ep.name = hsep->name;
+	hsep->ep.maxpacket = epnum ? 512 : 64;
+	hsep->ep.ops = &s3c_hsudc_ep_ops;
+	hsep->fifo = hsudc->regs + S3C_BR(epnum);
+	hsep->desc = 0;
+	hsep->stopped = 0;
+
+	set_index(hsudc, epnum);
+	writel(hsep->ep.maxpacket, hsudc->regs + S3C_MPR);
+}
+
+/**
+ * s3c_hsudc_setup_ep - Configure all endpoints to default state.
+ * @hsudc: Reference to device controller.
+ *
+ * Configures all endpoints to default state.
+ */
+static void s3c_hsudc_setup_ep(struct s3c_hsudc *hsudc)
+{
+	int epnum;
+
+	hsudc->ep0state = WAIT_FOR_SETUP;
+	INIT_LIST_HEAD(&hsudc->gadget.ep_list);
+	for (epnum = 0; epnum < hsudc->pd->epnum; epnum++)
+		s3c_hsudc_initep(hsudc, &hsudc->ep[epnum], epnum);
+}
+
+/**
+ * s3c_hsudc_reconfig - Reconfigure the device controller to default state.
+ * @hsudc: Reference to device controller.
+ *
+ * Reconfigures the device controller registers to a default state.
+ */
+static void s3c_hsudc_reconfig(struct s3c_hsudc *hsudc)
+{
+	writel(0xAA, hsudc->regs + S3C_EDR);
+	writel(1, hsudc->regs + S3C_EIER);
+	writel(0, hsudc->regs + S3C_TR);
+	writel(S3C_SCR_DTZIEN_EN | S3C_SCR_RRD_EN | S3C_SCR_SUS_EN |
+			S3C_SCR_RST_EN, hsudc->regs + S3C_SCR);
+	writel(0, hsudc->regs + S3C_EP0CR);
+
+	s3c_hsudc_setup_ep(hsudc);
+}
+
+/**
+ * s3c_hsudc_irq - Interrupt handler for device controller.
+ * @irq: Not used.
+ * @_dev: Reference to the device controller.
+ *
+ * Interrupt handler for the device controller. This handler handles controller
+ * interrupts and endpoint interrupts.
+ */
+static irqreturn_t s3c_hsudc_irq(int irq, void *_dev)
+{
+	struct s3c_hsudc *hsudc = _dev;
+	struct s3c_hsudc_ep *hsep;
+	u32 ep_intr;
+	u32 sys_status;
+	u32 ep_idx;
+
+	spin_lock(&hsudc->lock);
+
+	sys_status = readl(hsudc->regs + S3C_SSR);
+	ep_intr = readl(hsudc->regs + S3C_EIR) & 0x3FF;
+
+	if (!ep_intr && !(sys_status & S3C_SSR_DTZIEN_EN)) {
+		spin_unlock(&hsudc->lock);
+		return IRQ_HANDLED;
+	}
+
+	if (sys_status) {
+		if (sys_status & S3C_SSR_VBUSON)
+			writel(S3C_SSR_VBUSON, hsudc->regs + S3C_SSR);
+
+		if (sys_status & S3C_SSR_ERR)
+			writel(S3C_SSR_ERR, hsudc->regs + S3C_SSR);
+
+		if (sys_status & S3C_SSR_SDE) {
+			writel(S3C_SSR_SDE, hsudc->regs + S3C_SSR);
+			hsudc->gadget.speed = (sys_status & S3C_SSR_HSP) ?
+				USB_SPEED_HIGH : USB_SPEED_FULL;
+		}
+
+		if (sys_status & S3C_SSR_SUSPEND) {
+			writel(S3C_SSR_SUSPEND, hsudc->regs + S3C_SSR);
+			if (hsudc->gadget.speed != USB_SPEED_UNKNOWN
+				&& hsudc->driver && hsudc->driver->suspend)
+				hsudc->driver->suspend(&hsudc->gadget);
+		}
+
+		if (sys_status & S3C_SSR_RESUME) {
+			writel(S3C_SSR_RESUME, hsudc->regs + S3C_SSR);
+			if (hsudc->gadget.speed != USB_SPEED_UNKNOWN
+				&& hsudc->driver && hsudc->driver->resume)
+				hsudc->driver->resume(&hsudc->gadget);
+		}
+
+		if (sys_status & S3C_SSR_RESET) {
+			writel(S3C_SSR_RESET, hsudc->regs + S3C_SSR);
+			for (ep_idx = 0; ep_idx < hsudc->pd->epnum; ep_idx++) {
+				hsep = &hsudc->ep[ep_idx];
+				hsep->stopped = 1;
+				s3c_hsudc_nuke_ep(hsep, -ECONNRESET);
+			}
+			s3c_hsudc_reconfig(hsudc);
+			hsudc->ep0state = WAIT_FOR_SETUP;
+		}
+	}
+
+	if (ep_intr & S3C_EIR_EP0) {
+		writel(S3C_EIR_EP0, hsudc->regs + S3C_EIR);
+		set_index(hsudc, 0);
+		s3c_hsudc_handle_ep0_intr(hsudc);
+	}
+
+	ep_intr >>= 1;
+	ep_idx = 1;
+	while (ep_intr) {
+		if (ep_intr & 1)  {
+			hsep = &hsudc->ep[ep_idx];
+			set_index(hsudc, ep_idx);
+			writel(1 << ep_idx, hsudc->regs + S3C_EIR);
+			if (ep_is_in(hsep))
+				s3c_hsudc_epin_intr(hsudc, ep_idx);
+			else
+				s3c_hsudc_epout_intr(hsudc, ep_idx);
+		}
+		ep_intr >>= 1;
+		ep_idx++;
+	}
+
+	spin_unlock(&hsudc->lock);
+	return IRQ_HANDLED;
+}
+
+int usb_gadget_probe_driver(struct usb_gadget_driver *driver,
+               int (*bind)(struct usb_gadget *))
+{
+	struct s3c_hsudc *hsudc = the_controller;
+	int ret;
+
+	if (!driver
+		|| (driver->speed != USB_SPEED_FULL &&
+			driver->speed != USB_SPEED_HIGH)
+		|| !bind
+		|| !driver->unbind || !driver->disconnect || !driver->setup)
+		return -EINVAL;
+
+	if (!hsudc)
+		return -ENODEV;
+
+	if (hsudc->driver)
+		return -EBUSY;
+
+	hsudc->driver = driver;
+	hsudc->gadget.dev.driver = &driver->driver;
+	hsudc->gadget.speed = USB_SPEED_UNKNOWN;
+	ret = device_add(&hsudc->gadget.dev);
+	if (ret) {
+		dev_err(hsudc->dev, "failed to probe gadget device");
+		return ret;
+	}
+
+	ret = bind(&hsudc->gadget);
+	if (ret) {
+		dev_err(hsudc->dev, "%s: bind failed\n", hsudc->gadget.name);
+		device_del(&hsudc->gadget.dev);
+
+		hsudc->driver = NULL;
+		hsudc->gadget.dev.driver = NULL;
+		return ret;
+	}
+
+	enable_irq(hsudc->irq);
+	dev_info(hsudc->dev, "bound driver %s\n", driver->driver.name);
+
+	s3c_hsudc_reconfig(hsudc);
+	s3c_hsudc_init_phy();
+	if (hsudc->pd->gpio_init)
+		hsudc->pd->gpio_init();
+
+	return 0;
+}
+EXPORT_SYMBOL(usb_gadget_probe_driver);
+
+int usb_gadget_unregister_driver(struct usb_gadget_driver *driver)
+{
+	struct s3c_hsudc *hsudc = the_controller;
+	unsigned long flags;
+
+	if (!hsudc)
+		return -ENODEV;
+
+	if (!driver || driver != hsudc->driver || !driver->unbind)
+		return -EINVAL;
+
+	spin_lock_irqsave(&hsudc->lock, flags);
+	hsudc->driver = 0;
+	s3c_hsudc_uninit_phy();
+	if (hsudc->pd->gpio_uninit)
+		hsudc->pd->gpio_uninit();
+	s3c_hsudc_stop_activity(hsudc, driver);
+	spin_unlock_irqrestore(&hsudc->lock, flags);
+
+	driver->unbind(&hsudc->gadget);
+	device_del(&hsudc->gadget.dev);
+	disable_irq(hsudc->irq);
+
+	dev_info(hsudc->dev, "unregistered gadget driver '%s'\n",
+			driver->driver.name);
+	return 0;
+}
+EXPORT_SYMBOL(usb_gadget_unregister_driver);
+
+static inline u32 s3c_hsudc_read_frameno(struct s3c_hsudc *hsudc)
+{
+	return readl(hsudc->regs + S3C_FNR) & 0x3FF;
+}
+
+static int s3c_hsudc_gadget_getframe(struct usb_gadget *gadget)
+{
+	return s3c_hsudc_read_frameno(to_hsudc(gadget));
+}
+
+static struct usb_gadget_ops s3c_hsudc_gadget_ops = {
+	.get_frame	= s3c_hsudc_gadget_getframe,
+};
+
+static int s3c_hsudc_probe(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	struct resource *res;
+	struct s3c_hsudc *hsudc;
+	struct s3c24xx_hsudc_platdata *pd = pdev->dev.platform_data;
+	int ret;
+
+	hsudc = kzalloc(sizeof(struct s3c_hsudc) +
+			sizeof(struct s3c_hsudc_ep) * pd->epnum,
+			GFP_KERNEL);
+	if (!hsudc) {
+		dev_err(dev, "cannot allocate memory\n");
+		return -ENOMEM;
+	}
+
+	the_controller = hsudc;
+	platform_set_drvdata(pdev, dev);
+	hsudc->dev = dev;
+	hsudc->pd = pdev->dev.platform_data;
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!res) {
+		dev_err(dev, "unable to obtain driver resource data\n");
+		ret = -ENODEV;
+		goto err_res;
+	}
+
+	hsudc->mem_rsrc = request_mem_region(res->start, resource_size(res),
+				dev_name(&pdev->dev));
+	if (!hsudc->mem_rsrc) {
+		dev_err(dev, "failed to reserve register area\n");
+		ret = -ENODEV;
+		goto err_res;
+	}
+
+	hsudc->regs = ioremap(res->start, resource_size(res));
+	if (!hsudc->regs) {
+		dev_err(dev, "error mapping device register area\n");
+		ret = -EBUSY;
+		goto err_remap;
+	}
+
+	ret = platform_get_irq(pdev, 0);
+	if (ret < 0) {
+		dev_err(dev, "unable to obtain IRQ number\n");
+		goto err_irq;
+	}
+	hsudc->irq = ret;
+
+	ret = request_irq(hsudc->irq, s3c_hsudc_irq, 0, driver_name, hsudc);
+	if (ret < 0) {
+		dev_err(dev, "irq request failed\n");
+		goto err_irq;
+	}
+
+	spin_lock_init(&hsudc->lock);
+
+	device_initialize(&hsudc->gadget.dev);
+	dev_set_name(&hsudc->gadget.dev, "gadget");
+
+	hsudc->gadget.is_dualspeed = 1;
+	hsudc->gadget.ops = &s3c_hsudc_gadget_ops;
+	hsudc->gadget.name = dev_name(dev);
+	hsudc->gadget.dev.parent = dev;
+	hsudc->gadget.dev.dma_mask = dev->dma_mask;
+	hsudc->gadget.ep0 = &hsudc->ep[0].ep;
+
+	hsudc->gadget.is_otg = 0;
+	hsudc->gadget.is_a_peripheral = 0;
+
+	s3c_hsudc_setup_ep(hsudc);
+
+	hsudc->uclk = clk_get(&pdev->dev, "usb-device");
+	if (hsudc->uclk == NULL) {
+		dev_err(dev, "failed to find usb-device clock source\n");
+		return -ENOENT;
+	}
+	clk_enable(hsudc->uclk);
+
+	local_irq_disable();
+
+	disable_irq(hsudc->irq);
+	local_irq_enable();
+	return 0;
+
+err_irq:
+	iounmap(hsudc->regs);
+
+err_remap:
+	release_resource(hsudc->mem_rsrc);
+	kfree(hsudc->mem_rsrc);
+
+err_res:
+	kfree(hsudc);
+	return ret;
+}
+
+static struct platform_driver s3c_hsudc_driver = {
+	.driver		= {
+		.owner	= THIS_MODULE,
+		.name	= "s3c-hsudc",
+	},
+	.probe		= s3c_hsudc_probe,
+};
+
+static int __init s3c_hsudc_modinit(void)
+{
+	return platform_driver_register(&s3c_hsudc_driver);
+}
+
+static void __exit s3c_hsudc_modexit(void)
+{
+	platform_driver_unregister(&s3c_hsudc_driver);
+}
+
+module_init(s3c_hsudc_modinit);
+module_exit(s3c_hsudc_modexit);
+
+MODULE_DESCRIPTION("Samsung S3C24XX USB high-speed controller driver");
+MODULE_AUTHOR("Thomas Abraham <thomas.ab@samsung.com>");
+MODULE_LICENSE("GPL");
-- 
1.7.1

^ permalink raw reply related	[flat|nested] 52+ messages in thread

* [PATCH 4/4] ARM: S3C2416: Add support for USB 2.0 High-Speed gadget controller
  2011-04-14  9:06       ` Heiko Stübner
@ 2011-04-14  9:12         ` Heiko Stübner
  -1 siblings, 0 replies; 52+ messages in thread
From: Heiko Stübner @ 2011-04-14  9:12 UTC (permalink / raw)
  To: Greg KH, Kukjin Kim
  Cc: Ben Dooks, Thomas Abraham, Sangbeom Kim, linux-arm-kernel,
	linux-samsung-soc, linux-usb, Alexander Neumann

From: Thomas Abraham <thomas.ab@samsung.com>

Add support for USB 2.0 High-Speed gadget controller driver for Samsung's
S3C2416 processor.

Signed-off-by: Thomas Abraham <thomas.ab@samsung.com>
Signed-off-by: Sangbeom Kim <sbkim73@samsung.com>
---
 arch/arm/mach-s3c2416/mach-smdk2416.c |   27 +++++++++++++++++++++++++++
 1 files changed, 27 insertions(+), 0 deletions(-)

diff --git a/arch/arm/mach-s3c2416/mach-smdk2416.c b/arch/arm/mach-s3c2416/mach-smdk2416.c
index 3f83177..ac27ebb 100644
--- a/arch/arm/mach-s3c2416/mach-smdk2416.c
+++ b/arch/arm/mach-s3c2416/mach-smdk2416.c
@@ -23,6 +23,7 @@
 #include <linux/mtd/partitions.h>
 #include <linux/gpio.h>
 #include <linux/fb.h>
+#include <linux/delay.h>
 
 #include <asm/mach/arch.h>
 #include <asm/mach/map.h>
@@ -35,6 +36,7 @@
 #include <plat/regs-serial.h>
 #include <mach/regs-gpio.h>
 #include <mach/regs-lcd.h>
+#include <mach/regs-s3c2443-clock.h>
 
 #include <mach/idle.h>
 #include <mach/leds-gpio.h>
@@ -47,6 +49,7 @@
 #include <plat/cpu.h>
 #include <plat/nand.h>
 #include <plat/sdhci.h>
+#include <plat/udc.h>
 
 #include <plat/regs-fb-v4.h>
 #include <plat/fb.h>
@@ -121,6 +124,27 @@ static struct s3c2410_uartcfg smdk2416_uartcfgs[] __initdata = {
 	}
 };
 
+void smdk2416_hsudc_gpio_init(void)
+{
+	s3c_gpio_setpull(S3C2410_GPH(14), S3C_GPIO_PULL_UP);
+	s3c_gpio_setpull(S3C2410_GPF(2), S3C_GPIO_PULL_NONE);
+	s3c_gpio_cfgpin(S3C2410_GPH(14), S3C_GPIO_SFN(1));
+	s3c2410_modify_misccr(S3C2416_MISCCR_SEL_SUSPND, 0);
+}
+
+void smdk2416_hsudc_gpio_uninit(void)
+{
+	s3c2410_modify_misccr(S3C2416_MISCCR_SEL_SUSPND, 1);
+	s3c_gpio_setpull(S3C2410_GPH(14), S3C_GPIO_PULL_NONE);
+	s3c_gpio_cfgpin(S3C2410_GPH(14), S3C_GPIO_SFN(0));
+}
+
+struct s3c24xx_hsudc_platdata smdk2416_hsudc_platdata = {
+	.epnum = 9,
+	.gpio_init = smdk2416_hsudc_gpio_init,
+	.gpio_uninit = smdk2416_hsudc_gpio_uninit,
+};
+
 struct s3c_fb_pd_win smdk2416_fb_win[] = {
 	[0] = {
 		/* think this is the same as the smdk6410 */
@@ -186,6 +210,7 @@ static struct platform_device *smdk2416_devices[] __initdata = {
 	&s3c_device_i2c0,
 	&s3c_device_hsmmc0,
 	&s3c_device_hsmmc1,
+	&s3c_device_usb_hsudc,
 };
 
 static void __init smdk2416_map_io(void)
@@ -203,6 +228,8 @@ static void __init smdk2416_machine_init(void)
 	s3c_sdhci0_set_platdata(&smdk2416_hsmmc0_pdata);
 	s3c_sdhci1_set_platdata(&smdk2416_hsmmc1_pdata);
 
+	s3c24xx_hsudc_set_platdata(&smdk2416_hsudc_platdata);
+
 	gpio_request(S3C2410_GPB(4), "USBHost Power");
 	gpio_direction_output(S3C2410_GPB(4), 1);
 
-- 
1.7.1

^ permalink raw reply related	[flat|nested] 52+ messages in thread

* [PATCH 4/4] ARM: S3C2416: Add support for USB 2.0 High-Speed gadget controller
@ 2011-04-14  9:12         ` Heiko Stübner
  0 siblings, 0 replies; 52+ messages in thread
From: Heiko Stübner @ 2011-04-14  9:12 UTC (permalink / raw)
  To: linux-arm-kernel

From: Thomas Abraham <thomas.ab@samsung.com>

Add support for USB 2.0 High-Speed gadget controller driver for Samsung's
S3C2416 processor.

Signed-off-by: Thomas Abraham <thomas.ab@samsung.com>
Signed-off-by: Sangbeom Kim <sbkim73@samsung.com>
---
 arch/arm/mach-s3c2416/mach-smdk2416.c |   27 +++++++++++++++++++++++++++
 1 files changed, 27 insertions(+), 0 deletions(-)

diff --git a/arch/arm/mach-s3c2416/mach-smdk2416.c b/arch/arm/mach-s3c2416/mach-smdk2416.c
index 3f83177..ac27ebb 100644
--- a/arch/arm/mach-s3c2416/mach-smdk2416.c
+++ b/arch/arm/mach-s3c2416/mach-smdk2416.c
@@ -23,6 +23,7 @@
 #include <linux/mtd/partitions.h>
 #include <linux/gpio.h>
 #include <linux/fb.h>
+#include <linux/delay.h>
 
 #include <asm/mach/arch.h>
 #include <asm/mach/map.h>
@@ -35,6 +36,7 @@
 #include <plat/regs-serial.h>
 #include <mach/regs-gpio.h>
 #include <mach/regs-lcd.h>
+#include <mach/regs-s3c2443-clock.h>
 
 #include <mach/idle.h>
 #include <mach/leds-gpio.h>
@@ -47,6 +49,7 @@
 #include <plat/cpu.h>
 #include <plat/nand.h>
 #include <plat/sdhci.h>
+#include <plat/udc.h>
 
 #include <plat/regs-fb-v4.h>
 #include <plat/fb.h>
@@ -121,6 +124,27 @@ static struct s3c2410_uartcfg smdk2416_uartcfgs[] __initdata = {
 	}
 };
 
+void smdk2416_hsudc_gpio_init(void)
+{
+	s3c_gpio_setpull(S3C2410_GPH(14), S3C_GPIO_PULL_UP);
+	s3c_gpio_setpull(S3C2410_GPF(2), S3C_GPIO_PULL_NONE);
+	s3c_gpio_cfgpin(S3C2410_GPH(14), S3C_GPIO_SFN(1));
+	s3c2410_modify_misccr(S3C2416_MISCCR_SEL_SUSPND, 0);
+}
+
+void smdk2416_hsudc_gpio_uninit(void)
+{
+	s3c2410_modify_misccr(S3C2416_MISCCR_SEL_SUSPND, 1);
+	s3c_gpio_setpull(S3C2410_GPH(14), S3C_GPIO_PULL_NONE);
+	s3c_gpio_cfgpin(S3C2410_GPH(14), S3C_GPIO_SFN(0));
+}
+
+struct s3c24xx_hsudc_platdata smdk2416_hsudc_platdata = {
+	.epnum = 9,
+	.gpio_init = smdk2416_hsudc_gpio_init,
+	.gpio_uninit = smdk2416_hsudc_gpio_uninit,
+};
+
 struct s3c_fb_pd_win smdk2416_fb_win[] = {
 	[0] = {
 		/* think this is the same as the smdk6410 */
@@ -186,6 +210,7 @@ static struct platform_device *smdk2416_devices[] __initdata = {
 	&s3c_device_i2c0,
 	&s3c_device_hsmmc0,
 	&s3c_device_hsmmc1,
+	&s3c_device_usb_hsudc,
 };
 
 static void __init smdk2416_map_io(void)
@@ -203,6 +228,8 @@ static void __init smdk2416_machine_init(void)
 	s3c_sdhci0_set_platdata(&smdk2416_hsmmc0_pdata);
 	s3c_sdhci1_set_platdata(&smdk2416_hsmmc1_pdata);
 
+	s3c24xx_hsudc_set_platdata(&smdk2416_hsudc_platdata);
+
 	gpio_request(S3C2410_GPB(4), "USBHost Power");
 	gpio_direction_output(S3C2410_GPB(4), 1);
 
-- 
1.7.1

^ permalink raw reply related	[flat|nested] 52+ messages in thread

* Re: [PATCH v4 3/4] USB: Gadget: Add Samsung S3C24XX USB High-Speed controller driver
  2011-04-14  9:11         ` Heiko Stübner
@ 2011-04-14 15:35           ` Alan Stern
  -1 siblings, 0 replies; 52+ messages in thread
From: Alan Stern @ 2011-04-14 15:35 UTC (permalink / raw)
  To: Heiko Stübner
  Cc: Greg KH, Kukjin Kim, Ben Dooks, Thomas Abraham, Sangbeom Kim,
	linux-arm-kernel, linux-samsung-soc, linux-usb,
	Alexander Neumann

[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #1: Type: TEXT/PLAIN; charset=UTF-8, Size: 1042 bytes --]

On Thu, 14 Apr 2011, Heiko [iso-8859-1] Stübner wrote:

> From: Thomas Abraham <thomas.ab@samsung.com>
> 
> The Samsung's S3C2416, S3C2443 and S3C2450 includes a USB High-Speed
> device controller module. This driver enables support for USB high-speed
> gadget functionality for the Samsung S3C24xx SoC's that include this
> controller.
> 
> Signed-off-by: Thomas Abraham <thomas.ab@samsung.com>
> Signed-off-by: Sangbeom Kim <sbkim73@samsung.com>
> Signed-off-by: Kukjin Kim <kgene.kim@samsung.com>
> Signed-off-by: Alexander Neumann <alexander@bumpern.de>
> Signed-off-by: Heiko Stuebner <heiko@sntech.de>

...

> +static struct usb_ep_ops s3c_hsudc_ep_ops = {
> +	.enable = s3c_hsudc_ep_enable,
> +	.disable = s3c_hsudc_ep_disable,
> +	.alloc_request = s3c_hsudc_alloc_request,
> +	.free_request = s3c_hsudc_free_request,
> +	.queue = s3c_hsudc_queue,
> +	.dequeue = s3c_hsudc_dequeue,
> +	.set_halt = s3c_hsudc_set_halt,
> +};

There's no .set_wedge method.  Why do people always leave this out?

Alan Stern

^ permalink raw reply	[flat|nested] 52+ messages in thread

* [PATCH v4 3/4] USB: Gadget: Add Samsung S3C24XX USB High-Speed controller driver
@ 2011-04-14 15:35           ` Alan Stern
  0 siblings, 0 replies; 52+ messages in thread
From: Alan Stern @ 2011-04-14 15:35 UTC (permalink / raw)
  To: linux-arm-kernel

On Thu, 14 Apr 2011, Heiko [iso-8859-1] St?bner wrote:

> From: Thomas Abraham <thomas.ab@samsung.com>
> 
> The Samsung's S3C2416, S3C2443 and S3C2450 includes a USB High-Speed
> device controller module. This driver enables support for USB high-speed
> gadget functionality for the Samsung S3C24xx SoC's that include this
> controller.
> 
> Signed-off-by: Thomas Abraham <thomas.ab@samsung.com>
> Signed-off-by: Sangbeom Kim <sbkim73@samsung.com>
> Signed-off-by: Kukjin Kim <kgene.kim@samsung.com>
> Signed-off-by: Alexander Neumann <alexander@bumpern.de>
> Signed-off-by: Heiko Stuebner <heiko@sntech.de>

...

> +static struct usb_ep_ops s3c_hsudc_ep_ops = {
> +	.enable = s3c_hsudc_ep_enable,
> +	.disable = s3c_hsudc_ep_disable,
> +	.alloc_request = s3c_hsudc_alloc_request,
> +	.free_request = s3c_hsudc_free_request,
> +	.queue = s3c_hsudc_queue,
> +	.dequeue = s3c_hsudc_dequeue,
> +	.set_halt = s3c_hsudc_set_halt,
> +};

There's no .set_wedge method.  Why do people always leave this out?

Alan Stern

^ permalink raw reply	[flat|nested] 52+ messages in thread

* Re: [PATCH v4 3/4] USB: Gadget: Add Samsung S3C24XX USB High-Speed controller driver
  2011-04-14 15:35           ` Alan Stern
@ 2011-04-14 15:47             ` Greg KH
  -1 siblings, 0 replies; 52+ messages in thread
From: Greg KH @ 2011-04-14 15:47 UTC (permalink / raw)
  To: Alan Stern
  Cc: Heiko Stübner, Kukjin Kim, Ben Dooks, Thomas Abraham,
	Sangbeom Kim, linux-arm-kernel, linux-samsung-soc, linux-usb,
	Alexander Neumann

On Thu, Apr 14, 2011 at 11:35:43AM -0400, Alan Stern wrote:
> On Thu, 14 Apr 2011, Heiko [iso-8859-1] St?bner wrote:
> 
> > From: Thomas Abraham <thomas.ab@samsung.com>
> > 
> > The Samsung's S3C2416, S3C2443 and S3C2450 includes a USB High-Speed
> > device controller module. This driver enables support for USB high-speed
> > gadget functionality for the Samsung S3C24xx SoC's that include this
> > controller.
> > 
> > Signed-off-by: Thomas Abraham <thomas.ab@samsung.com>
> > Signed-off-by: Sangbeom Kim <sbkim73@samsung.com>
> > Signed-off-by: Kukjin Kim <kgene.kim@samsung.com>
> > Signed-off-by: Alexander Neumann <alexander@bumpern.de>
> > Signed-off-by: Heiko Stuebner <heiko@sntech.de>
> 
> ...
> 
> > +static struct usb_ep_ops s3c_hsudc_ep_ops = {
> > +	.enable = s3c_hsudc_ep_enable,
> > +	.disable = s3c_hsudc_ep_disable,
> > +	.alloc_request = s3c_hsudc_alloc_request,
> > +	.free_request = s3c_hsudc_free_request,
> > +	.queue = s3c_hsudc_queue,
> > +	.dequeue = s3c_hsudc_dequeue,
> > +	.set_halt = s3c_hsudc_set_halt,
> > +};
> 
> There's no .set_wedge method.  Why do people always leave this out?

Does the code spit out a nasty warning if this isn't set?  If not, I
would suggest adding it so that this doesn't keep happening.

Or just refuse to be able to register the structure, that would stop it
right away :)

thanks,

greg k-h

^ permalink raw reply	[flat|nested] 52+ messages in thread

* [PATCH v4 3/4] USB: Gadget: Add Samsung S3C24XX USB High-Speed controller driver
@ 2011-04-14 15:47             ` Greg KH
  0 siblings, 0 replies; 52+ messages in thread
From: Greg KH @ 2011-04-14 15:47 UTC (permalink / raw)
  To: linux-arm-kernel

On Thu, Apr 14, 2011 at 11:35:43AM -0400, Alan Stern wrote:
> On Thu, 14 Apr 2011, Heiko [iso-8859-1] St?bner wrote:
> 
> > From: Thomas Abraham <thomas.ab@samsung.com>
> > 
> > The Samsung's S3C2416, S3C2443 and S3C2450 includes a USB High-Speed
> > device controller module. This driver enables support for USB high-speed
> > gadget functionality for the Samsung S3C24xx SoC's that include this
> > controller.
> > 
> > Signed-off-by: Thomas Abraham <thomas.ab@samsung.com>
> > Signed-off-by: Sangbeom Kim <sbkim73@samsung.com>
> > Signed-off-by: Kukjin Kim <kgene.kim@samsung.com>
> > Signed-off-by: Alexander Neumann <alexander@bumpern.de>
> > Signed-off-by: Heiko Stuebner <heiko@sntech.de>
> 
> ...
> 
> > +static struct usb_ep_ops s3c_hsudc_ep_ops = {
> > +	.enable = s3c_hsudc_ep_enable,
> > +	.disable = s3c_hsudc_ep_disable,
> > +	.alloc_request = s3c_hsudc_alloc_request,
> > +	.free_request = s3c_hsudc_free_request,
> > +	.queue = s3c_hsudc_queue,
> > +	.dequeue = s3c_hsudc_dequeue,
> > +	.set_halt = s3c_hsudc_set_halt,
> > +};
> 
> There's no .set_wedge method.  Why do people always leave this out?

Does the code spit out a nasty warning if this isn't set?  If not, I
would suggest adding it so that this doesn't keep happening.

Or just refuse to be able to register the structure, that would stop it
right away :)

thanks,

greg k-h

^ permalink raw reply	[flat|nested] 52+ messages in thread

* Re: [PATCH v4 3/4] USB: Gadget: Add Samsung S3C24XX USB High-Speed controller driver
  2011-04-14 15:47             ` Greg KH
@ 2011-04-14 17:06               ` Heiko Stübner
  -1 siblings, 0 replies; 52+ messages in thread
From: Heiko Stübner @ 2011-04-14 17:06 UTC (permalink / raw)
  To: Greg KH, Alan Stern, Thomas Abraham
  Cc: Kukjin Kim, Ben Dooks, Sangbeom Kim, linux-arm-kernel,
	linux-samsung-soc, linux-usb, Alexander Neumann

Am Donnerstag 14 April 2011, 17:47:48 schrieb Greg KH:
> On Thu, Apr 14, 2011 at 11:35:43AM -0400, Alan Stern wrote:
> > On Thu, 14 Apr 2011, Heiko [iso-8859-1] St?bner wrote:
> > > From: Thomas Abraham <thomas.ab@samsung.com>
> > > 
> > > The Samsung's S3C2416, S3C2443 and S3C2450 includes a USB High-Speed
> > > device controller module. This driver enables support for USB
> > > high-speed gadget functionality for the Samsung S3C24xx SoC's that
> > > include this controller.
> > > 
> > > Signed-off-by: Thomas Abraham <thomas.ab@samsung.com>
> > > Signed-off-by: Sangbeom Kim <sbkim73@samsung.com>
> > > Signed-off-by: Kukjin Kim <kgene.kim@samsung.com>
> > > Signed-off-by: Alexander Neumann <alexander@bumpern.de>
> > > Signed-off-by: Heiko Stuebner <heiko@sntech.de>
> > 
> > ...
> > 
> > > +static struct usb_ep_ops s3c_hsudc_ep_ops = {
> > > +	.enable = s3c_hsudc_ep_enable,
> > > +	.disable = s3c_hsudc_ep_disable,
> > > +	.alloc_request = s3c_hsudc_alloc_request,
> > > +	.free_request = s3c_hsudc_free_request,
> > > +	.queue = s3c_hsudc_queue,
> > > +	.dequeue = s3c_hsudc_dequeue,
> > > +	.set_halt = s3c_hsudc_set_halt,
> > > +};
> > 
> > There's no .set_wedge method.  Why do people always leave this out?
> 
> Does the code spit out a nasty warning if this isn't set?  If not, I
> would suggest adding it so that this doesn't keep happening.

>From what I gathered from gadget.h the usb_ep_set_wedge function checks if 
set_wedge is defined and uses set_halt if it's missing. The comments 
surrounding usb_ep_set_wedge and usb_ep_ops also don't indicate that set_wedge 
is a must. 
As it has a fallback [through set_halt] it might therefore lead to the 
impression of being optional.

>From Alan's response I gather that it isn't optional and the fallback is only 
there for older drivers.

Hopefully Thomas [the original author] reads this. He would probably be able 
to implement the set_wedge function in a tiny fraction of the time I would 
need :-), as I don't really understand the inner workings of this.


Heiko

^ permalink raw reply	[flat|nested] 52+ messages in thread

* [PATCH v4 3/4] USB: Gadget: Add Samsung S3C24XX USB High-Speed controller driver
@ 2011-04-14 17:06               ` Heiko Stübner
  0 siblings, 0 replies; 52+ messages in thread
From: Heiko Stübner @ 2011-04-14 17:06 UTC (permalink / raw)
  To: linux-arm-kernel

Am Donnerstag 14 April 2011, 17:47:48 schrieb Greg KH:
> On Thu, Apr 14, 2011 at 11:35:43AM -0400, Alan Stern wrote:
> > On Thu, 14 Apr 2011, Heiko [iso-8859-1] St?bner wrote:
> > > From: Thomas Abraham <thomas.ab@samsung.com>
> > > 
> > > The Samsung's S3C2416, S3C2443 and S3C2450 includes a USB High-Speed
> > > device controller module. This driver enables support for USB
> > > high-speed gadget functionality for the Samsung S3C24xx SoC's that
> > > include this controller.
> > > 
> > > Signed-off-by: Thomas Abraham <thomas.ab@samsung.com>
> > > Signed-off-by: Sangbeom Kim <sbkim73@samsung.com>
> > > Signed-off-by: Kukjin Kim <kgene.kim@samsung.com>
> > > Signed-off-by: Alexander Neumann <alexander@bumpern.de>
> > > Signed-off-by: Heiko Stuebner <heiko@sntech.de>
> > 
> > ...
> > 
> > > +static struct usb_ep_ops s3c_hsudc_ep_ops = {
> > > +	.enable = s3c_hsudc_ep_enable,
> > > +	.disable = s3c_hsudc_ep_disable,
> > > +	.alloc_request = s3c_hsudc_alloc_request,
> > > +	.free_request = s3c_hsudc_free_request,
> > > +	.queue = s3c_hsudc_queue,
> > > +	.dequeue = s3c_hsudc_dequeue,
> > > +	.set_halt = s3c_hsudc_set_halt,
> > > +};
> > 
> > There's no .set_wedge method.  Why do people always leave this out?
> 
> Does the code spit out a nasty warning if this isn't set?  If not, I
> would suggest adding it so that this doesn't keep happening.

>From what I gathered from gadget.h the usb_ep_set_wedge function checks if 
set_wedge is defined and uses set_halt if it's missing. The comments 
surrounding usb_ep_set_wedge and usb_ep_ops also don't indicate that set_wedge 
is a must. 
As it has a fallback [through set_halt] it might therefore lead to the 
impression of being optional.

>From Alan's response I gather that it isn't optional and the fallback is only 
there for older drivers.

Hopefully Thomas [the original author] reads this. He would probably be able 
to implement the set_wedge function in a tiny fraction of the time I would 
need :-), as I don't really understand the inner workings of this.


Heiko

^ permalink raw reply	[flat|nested] 52+ messages in thread

* Re: [PATCH v4 3/4] USB: Gadget: Add Samsung S3C24XX USB High-Speed controller driver
  2011-04-14 15:47             ` Greg KH
@ 2011-04-14 17:15               ` Alan Stern
  -1 siblings, 0 replies; 52+ messages in thread
From: Alan Stern @ 2011-04-14 17:15 UTC (permalink / raw)
  To: Greg KH
  Cc: Heiko Stübner, Kukjin Kim, Ben Dooks, Thomas Abraham,
	Sangbeom Kim, linux-arm-kernel, linux-samsung-soc, linux-usb,
	Alexander Neumann

On Thu, 14 Apr 2011, Greg KH wrote:

> On Thu, Apr 14, 2011 at 11:35:43AM -0400, Alan Stern wrote:
> > On Thu, 14 Apr 2011, Heiko [iso-8859-1] St?bner wrote:
> > 
> > > From: Thomas Abraham <thomas.ab@samsung.com>
> > > 
> > > The Samsung's S3C2416, S3C2443 and S3C2450 includes a USB High-Speed
> > > device controller module. This driver enables support for USB high-speed
> > > gadget functionality for the Samsung S3C24xx SoC's that include this
> > > controller.
> > > 
> > > Signed-off-by: Thomas Abraham <thomas.ab@samsung.com>
> > > Signed-off-by: Sangbeom Kim <sbkim73@samsung.com>
> > > Signed-off-by: Kukjin Kim <kgene.kim@samsung.com>
> > > Signed-off-by: Alexander Neumann <alexander@bumpern.de>
> > > Signed-off-by: Heiko Stuebner <heiko@sntech.de>
> > 
> > ...
> > 
> > > +static struct usb_ep_ops s3c_hsudc_ep_ops = {
> > > +	.enable = s3c_hsudc_ep_enable,
> > > +	.disable = s3c_hsudc_ep_disable,
> > > +	.alloc_request = s3c_hsudc_alloc_request,
> > > +	.free_request = s3c_hsudc_free_request,
> > > +	.queue = s3c_hsudc_queue,
> > > +	.dequeue = s3c_hsudc_dequeue,
> > > +	.set_halt = s3c_hsudc_set_halt,
> > > +};
> > 
> > There's no .set_wedge method.  Why do people always leave this out?
> 
> Does the code spit out a nasty warning if this isn't set?  If not, I
> would suggest adding it so that this doesn't keep happening.
> 
> Or just refuse to be able to register the structure, that would stop it
> right away :)

In fact, set_wedge is optional.  But it's so easy to implement, there's 
no good reason for leaving it out.

I suspect what's happening is that people are just basing their new 
drivers on other drivers from very old kernel versions, before 
set_wedge was added.

Alan Stern

^ permalink raw reply	[flat|nested] 52+ messages in thread

* [PATCH v4 3/4] USB: Gadget: Add Samsung S3C24XX USB High-Speed controller driver
@ 2011-04-14 17:15               ` Alan Stern
  0 siblings, 0 replies; 52+ messages in thread
From: Alan Stern @ 2011-04-14 17:15 UTC (permalink / raw)
  To: linux-arm-kernel

On Thu, 14 Apr 2011, Greg KH wrote:

> On Thu, Apr 14, 2011 at 11:35:43AM -0400, Alan Stern wrote:
> > On Thu, 14 Apr 2011, Heiko [iso-8859-1] St?bner wrote:
> > 
> > > From: Thomas Abraham <thomas.ab@samsung.com>
> > > 
> > > The Samsung's S3C2416, S3C2443 and S3C2450 includes a USB High-Speed
> > > device controller module. This driver enables support for USB high-speed
> > > gadget functionality for the Samsung S3C24xx SoC's that include this
> > > controller.
> > > 
> > > Signed-off-by: Thomas Abraham <thomas.ab@samsung.com>
> > > Signed-off-by: Sangbeom Kim <sbkim73@samsung.com>
> > > Signed-off-by: Kukjin Kim <kgene.kim@samsung.com>
> > > Signed-off-by: Alexander Neumann <alexander@bumpern.de>
> > > Signed-off-by: Heiko Stuebner <heiko@sntech.de>
> > 
> > ...
> > 
> > > +static struct usb_ep_ops s3c_hsudc_ep_ops = {
> > > +	.enable = s3c_hsudc_ep_enable,
> > > +	.disable = s3c_hsudc_ep_disable,
> > > +	.alloc_request = s3c_hsudc_alloc_request,
> > > +	.free_request = s3c_hsudc_free_request,
> > > +	.queue = s3c_hsudc_queue,
> > > +	.dequeue = s3c_hsudc_dequeue,
> > > +	.set_halt = s3c_hsudc_set_halt,
> > > +};
> > 
> > There's no .set_wedge method.  Why do people always leave this out?
> 
> Does the code spit out a nasty warning if this isn't set?  If not, I
> would suggest adding it so that this doesn't keep happening.
> 
> Or just refuse to be able to register the structure, that would stop it
> right away :)

In fact, set_wedge is optional.  But it's so easy to implement, there's 
no good reason for leaving it out.

I suspect what's happening is that people are just basing their new 
drivers on other drivers from very old kernel versions, before 
set_wedge was added.

Alan Stern

^ permalink raw reply	[flat|nested] 52+ messages in thread

* Re: [PATCH v4 3/4] USB: Gadget: Add Samsung S3C24XX USB High-Speed controller driver
  2011-04-14 17:15               ` Alan Stern
@ 2011-04-16 10:00                 ` Heiko Stübner
  -1 siblings, 0 replies; 52+ messages in thread
From: Heiko Stübner @ 2011-04-16 10:00 UTC (permalink / raw)
  To: Alan Stern, Greg KH
  Cc: Kukjin Kim, Ben Dooks, Thomas Abraham, Sangbeom Kim,
	linux-arm-kernel, linux-samsung-soc, linux-usb,
	Alexander Neumann

Am Donnerstag 14 April 2011, 19:15:23 schrieb Alan Stern:
> On Thu, 14 Apr 2011, Greg KH wrote:
> > On Thu, Apr 14, 2011 at 11:35:43AM -0400, Alan Stern wrote:
> > > On Thu, 14 Apr 2011, Heiko [iso-8859-1] St?bner wrote:
> > > > From: Thomas Abraham <thomas.ab@samsung.com>
> > > > 
> > > > The Samsung's S3C2416, S3C2443 and S3C2450 includes a USB High-Speed
> > > > device controller module. This driver enables support for USB
> > > > high-speed gadget functionality for the Samsung S3C24xx SoC's that
> > > > include this controller.
> > > > 
> > > > Signed-off-by: Thomas Abraham <thomas.ab@samsung.com>
> > > > Signed-off-by: Sangbeom Kim <sbkim73@samsung.com>
> > > > Signed-off-by: Kukjin Kim <kgene.kim@samsung.com>
> > > > Signed-off-by: Alexander Neumann <alexander@bumpern.de>
> > > > Signed-off-by: Heiko Stuebner <heiko@sntech.de>
> > > 
> > > ...
> > > 
> > > > +static struct usb_ep_ops s3c_hsudc_ep_ops = {
> > > > +	.enable = s3c_hsudc_ep_enable,
> > > > +	.disable = s3c_hsudc_ep_disable,
> > > > +	.alloc_request = s3c_hsudc_alloc_request,
> > > > +	.free_request = s3c_hsudc_free_request,
> > > > +	.queue = s3c_hsudc_queue,
> > > > +	.dequeue = s3c_hsudc_dequeue,
> > > > +	.set_halt = s3c_hsudc_set_halt,
> > > > +};
> > > 
> > > There's no .set_wedge method.  Why do people always leave this out?
> > 
> > Does the code spit out a nasty warning if this isn't set?  If not, I
> > would suggest adding it so that this doesn't keep happening.
> > 
> > Or just refuse to be able to register the structure, that would stop it
> > right away :)
> 
> In fact, set_wedge is optional.  But it's so easy to implement, there's
> no good reason for leaving it out.

It seems Thomas [original author of the driver] will be able to implement said 
set_wedge function for it.
As he will need a bit of time for this, two possible ways for going forward 
come to mind:
(1) use current driver [as set_wedge is optional] and add it later via patch
(2) resubmit whole driver again when set_wedge is added to it

Obviously I would prefer option 1 :-), but in the end it's your decision.

Heiko

^ permalink raw reply	[flat|nested] 52+ messages in thread

* [PATCH v4 3/4] USB: Gadget: Add Samsung S3C24XX USB High-Speed controller driver
@ 2011-04-16 10:00                 ` Heiko Stübner
  0 siblings, 0 replies; 52+ messages in thread
From: Heiko Stübner @ 2011-04-16 10:00 UTC (permalink / raw)
  To: linux-arm-kernel

Am Donnerstag 14 April 2011, 19:15:23 schrieb Alan Stern:
> On Thu, 14 Apr 2011, Greg KH wrote:
> > On Thu, Apr 14, 2011 at 11:35:43AM -0400, Alan Stern wrote:
> > > On Thu, 14 Apr 2011, Heiko [iso-8859-1] St?bner wrote:
> > > > From: Thomas Abraham <thomas.ab@samsung.com>
> > > > 
> > > > The Samsung's S3C2416, S3C2443 and S3C2450 includes a USB High-Speed
> > > > device controller module. This driver enables support for USB
> > > > high-speed gadget functionality for the Samsung S3C24xx SoC's that
> > > > include this controller.
> > > > 
> > > > Signed-off-by: Thomas Abraham <thomas.ab@samsung.com>
> > > > Signed-off-by: Sangbeom Kim <sbkim73@samsung.com>
> > > > Signed-off-by: Kukjin Kim <kgene.kim@samsung.com>
> > > > Signed-off-by: Alexander Neumann <alexander@bumpern.de>
> > > > Signed-off-by: Heiko Stuebner <heiko@sntech.de>
> > > 
> > > ...
> > > 
> > > > +static struct usb_ep_ops s3c_hsudc_ep_ops = {
> > > > +	.enable = s3c_hsudc_ep_enable,
> > > > +	.disable = s3c_hsudc_ep_disable,
> > > > +	.alloc_request = s3c_hsudc_alloc_request,
> > > > +	.free_request = s3c_hsudc_free_request,
> > > > +	.queue = s3c_hsudc_queue,
> > > > +	.dequeue = s3c_hsudc_dequeue,
> > > > +	.set_halt = s3c_hsudc_set_halt,
> > > > +};
> > > 
> > > There's no .set_wedge method.  Why do people always leave this out?
> > 
> > Does the code spit out a nasty warning if this isn't set?  If not, I
> > would suggest adding it so that this doesn't keep happening.
> > 
> > Or just refuse to be able to register the structure, that would stop it
> > right away :)
> 
> In fact, set_wedge is optional.  But it's so easy to implement, there's
> no good reason for leaving it out.

It seems Thomas [original author of the driver] will be able to implement said 
set_wedge function for it.
As he will need a bit of time for this, two possible ways for going forward 
come to mind:
(1) use current driver [as set_wedge is optional] and add it later via patch
(2) resubmit whole driver again when set_wedge is added to it

Obviously I would prefer option 1 :-), but in the end it's your decision.

Heiko

^ permalink raw reply	[flat|nested] 52+ messages in thread

* Re: [PATCH v4 3/4] USB: Gadget: Add Samsung S3C24XX USB High-Speed controller driver
  2011-04-16 10:00                 ` Heiko Stübner
@ 2011-04-16 14:58                   ` Greg KH
  -1 siblings, 0 replies; 52+ messages in thread
From: Greg KH @ 2011-04-16 14:58 UTC (permalink / raw)
  To: Heiko Stübner
  Cc: Alan Stern, Kukjin Kim, Ben Dooks, Thomas Abraham, Sangbeom Kim,
	linux-arm-kernel, linux-samsung-soc, linux-usb,
	Alexander Neumann

On Sat, Apr 16, 2011 at 12:00:31PM +0200, Heiko Stübner wrote:
> Am Donnerstag 14 April 2011, 19:15:23 schrieb Alan Stern:
> > On Thu, 14 Apr 2011, Greg KH wrote:
> > > On Thu, Apr 14, 2011 at 11:35:43AM -0400, Alan Stern wrote:
> > > > On Thu, 14 Apr 2011, Heiko [iso-8859-1] St?bner wrote:
> > > > > From: Thomas Abraham <thomas.ab@samsung.com>
> > > > > 
> > > > > The Samsung's S3C2416, S3C2443 and S3C2450 includes a USB High-Speed
> > > > > device controller module. This driver enables support for USB
> > > > > high-speed gadget functionality for the Samsung S3C24xx SoC's that
> > > > > include this controller.
> > > > > 
> > > > > Signed-off-by: Thomas Abraham <thomas.ab@samsung.com>
> > > > > Signed-off-by: Sangbeom Kim <sbkim73@samsung.com>
> > > > > Signed-off-by: Kukjin Kim <kgene.kim@samsung.com>
> > > > > Signed-off-by: Alexander Neumann <alexander@bumpern.de>
> > > > > Signed-off-by: Heiko Stuebner <heiko@sntech.de>
> > > > 
> > > > ...
> > > > 
> > > > > +static struct usb_ep_ops s3c_hsudc_ep_ops = {
> > > > > +	.enable = s3c_hsudc_ep_enable,
> > > > > +	.disable = s3c_hsudc_ep_disable,
> > > > > +	.alloc_request = s3c_hsudc_alloc_request,
> > > > > +	.free_request = s3c_hsudc_free_request,
> > > > > +	.queue = s3c_hsudc_queue,
> > > > > +	.dequeue = s3c_hsudc_dequeue,
> > > > > +	.set_halt = s3c_hsudc_set_halt,
> > > > > +};
> > > > 
> > > > There's no .set_wedge method.  Why do people always leave this out?
> > > 
> > > Does the code spit out a nasty warning if this isn't set?  If not, I
> > > would suggest adding it so that this doesn't keep happening.
> > > 
> > > Or just refuse to be able to register the structure, that would stop it
> > > right away :)
> > 
> > In fact, set_wedge is optional.  But it's so easy to implement, there's
> > no good reason for leaving it out.
> 
> It seems Thomas [original author of the driver] will be able to implement said 
> set_wedge function for it.
> As he will need a bit of time for this, two possible ways for going forward 
> come to mind:
> (1) use current driver [as set_wedge is optional] and add it later via patch
> (2) resubmit whole driver again when set_wedge is added to it
> 
> Obviously I would prefer option 1 :-), but in the end it's your decision.

It shouldn't take that much time to do this, what is the delay?

I'd prefer to get the correct version implemented and would not like to
accept a patch that everyone knows is wrong.

thanks,

greg k-h

^ permalink raw reply	[flat|nested] 52+ messages in thread

* [PATCH v4 3/4] USB: Gadget: Add Samsung S3C24XX USB High-Speed controller driver
@ 2011-04-16 14:58                   ` Greg KH
  0 siblings, 0 replies; 52+ messages in thread
From: Greg KH @ 2011-04-16 14:58 UTC (permalink / raw)
  To: linux-arm-kernel

On Sat, Apr 16, 2011 at 12:00:31PM +0200, Heiko St?bner wrote:
> Am Donnerstag 14 April 2011, 19:15:23 schrieb Alan Stern:
> > On Thu, 14 Apr 2011, Greg KH wrote:
> > > On Thu, Apr 14, 2011 at 11:35:43AM -0400, Alan Stern wrote:
> > > > On Thu, 14 Apr 2011, Heiko [iso-8859-1] St?bner wrote:
> > > > > From: Thomas Abraham <thomas.ab@samsung.com>
> > > > > 
> > > > > The Samsung's S3C2416, S3C2443 and S3C2450 includes a USB High-Speed
> > > > > device controller module. This driver enables support for USB
> > > > > high-speed gadget functionality for the Samsung S3C24xx SoC's that
> > > > > include this controller.
> > > > > 
> > > > > Signed-off-by: Thomas Abraham <thomas.ab@samsung.com>
> > > > > Signed-off-by: Sangbeom Kim <sbkim73@samsung.com>
> > > > > Signed-off-by: Kukjin Kim <kgene.kim@samsung.com>
> > > > > Signed-off-by: Alexander Neumann <alexander@bumpern.de>
> > > > > Signed-off-by: Heiko Stuebner <heiko@sntech.de>
> > > > 
> > > > ...
> > > > 
> > > > > +static struct usb_ep_ops s3c_hsudc_ep_ops = {
> > > > > +	.enable = s3c_hsudc_ep_enable,
> > > > > +	.disable = s3c_hsudc_ep_disable,
> > > > > +	.alloc_request = s3c_hsudc_alloc_request,
> > > > > +	.free_request = s3c_hsudc_free_request,
> > > > > +	.queue = s3c_hsudc_queue,
> > > > > +	.dequeue = s3c_hsudc_dequeue,
> > > > > +	.set_halt = s3c_hsudc_set_halt,
> > > > > +};
> > > > 
> > > > There's no .set_wedge method.  Why do people always leave this out?
> > > 
> > > Does the code spit out a nasty warning if this isn't set?  If not, I
> > > would suggest adding it so that this doesn't keep happening.
> > > 
> > > Or just refuse to be able to register the structure, that would stop it
> > > right away :)
> > 
> > In fact, set_wedge is optional.  But it's so easy to implement, there's
> > no good reason for leaving it out.
> 
> It seems Thomas [original author of the driver] will be able to implement said 
> set_wedge function for it.
> As he will need a bit of time for this, two possible ways for going forward 
> come to mind:
> (1) use current driver [as set_wedge is optional] and add it later via patch
> (2) resubmit whole driver again when set_wedge is added to it
> 
> Obviously I would prefer option 1 :-), but in the end it's your decision.

It shouldn't take that much time to do this, what is the delay?

I'd prefer to get the correct version implemented and would not like to
accept a patch that everyone knows is wrong.

thanks,

greg k-h

^ permalink raw reply	[flat|nested] 52+ messages in thread

* Re: [PATCH v4 3/4] USB: Gadget: Add Samsung S3C24XX USB High-Speed controller driver
  2011-04-16 14:58                   ` Greg KH
@ 2011-04-16 18:47                     ` Heiko Stübner
  -1 siblings, 0 replies; 52+ messages in thread
From: Heiko Stübner @ 2011-04-16 18:47 UTC (permalink / raw)
  To: Greg KH, Alan Stern
  Cc: Kukjin Kim, Ben Dooks, Thomas Abraham, Sangbeom Kim,
	linux-arm-kernel, linux-samsung-soc, linux-usb,
	Alexander Neumann

Am Samstag 16 April 2011, 16:58:30 schrieb Greg KH:
> On Sat, Apr 16, 2011 at 12:00:31PM +0200, Heiko Stübner wrote:
> > Am Donnerstag 14 April 2011, 19:15:23 schrieb Alan Stern:
> > > On Thu, 14 Apr 2011, Greg KH wrote:
> > > > On Thu, Apr 14, 2011 at 11:35:43AM -0400, Alan Stern wrote:
> > > > > On Thu, 14 Apr 2011, Heiko [iso-8859-1] St?bner wrote:
> > > > > > From: Thomas Abraham <thomas.ab@samsung.com>
> > > > > > 
> > > > > > The Samsung's S3C2416, S3C2443 and S3C2450 includes a USB
> > > > > > High-Speed device controller module. This driver enables support
> > > > > > for USB high-speed gadget functionality for the Samsung S3C24xx
> > > > > > SoC's that include this controller.
> > > > > > 
> > > > > > Signed-off-by: Thomas Abraham <thomas.ab@samsung.com>
> > > > > > Signed-off-by: Sangbeom Kim <sbkim73@samsung.com>
> > > > > > Signed-off-by: Kukjin Kim <kgene.kim@samsung.com>
> > > > > > Signed-off-by: Alexander Neumann <alexander@bumpern.de>
> > > > > > Signed-off-by: Heiko Stuebner <heiko@sntech.de>
> > > > > 
> > > > > ...
> > > > > 
> > > > > > +static struct usb_ep_ops s3c_hsudc_ep_ops = {
> > > > > > +	.enable = s3c_hsudc_ep_enable,
> > > > > > +	.disable = s3c_hsudc_ep_disable,
> > > > > > +	.alloc_request = s3c_hsudc_alloc_request,
> > > > > > +	.free_request = s3c_hsudc_free_request,
> > > > > > +	.queue = s3c_hsudc_queue,
> > > > > > +	.dequeue = s3c_hsudc_dequeue,
> > > > > > +	.set_halt = s3c_hsudc_set_halt,
> > > > > > +};
> > > > > 
> > > > > There's no .set_wedge method.  Why do people always leave this out?
> > > > 
> > > > Does the code spit out a nasty warning if this isn't set?  If not, I
> > > > would suggest adding it so that this doesn't keep happening.
> > > > 
> > > > Or just refuse to be able to register the structure, that would stop
> > > > it right away :)
> > > 
> > > In fact, set_wedge is optional.  But it's so easy to implement, there's
> > > no good reason for leaving it out.
> > 
> > It seems Thomas [original author of the driver] will be able to implement
> > said set_wedge function for it.
> > As he will need a bit of time for this, two possible ways for going
> > forward come to mind:
> > (1) use current driver [as set_wedge is optional] and add it later via
> > patch (2) resubmit whole driver again when set_wedge is added to it
> > 
> > Obviously I would prefer option 1 :-), but in the end it's your decision.
>
> It shouldn't take that much time to do this, what is the delay?
from what I gathered, simply finding the necessary time slot for this.

> I'd prefer to get the correct version implemented and would not like to
> accept a patch that everyone knows is wrong.
ok, I will resubmit the driver when set_wedge is included

Heiko

^ permalink raw reply	[flat|nested] 52+ messages in thread

* [PATCH v4 3/4] USB: Gadget: Add Samsung S3C24XX USB High-Speed controller driver
@ 2011-04-16 18:47                     ` Heiko Stübner
  0 siblings, 0 replies; 52+ messages in thread
From: Heiko Stübner @ 2011-04-16 18:47 UTC (permalink / raw)
  To: linux-arm-kernel

Am Samstag 16 April 2011, 16:58:30 schrieb Greg KH:
> On Sat, Apr 16, 2011 at 12:00:31PM +0200, Heiko St?bner wrote:
> > Am Donnerstag 14 April 2011, 19:15:23 schrieb Alan Stern:
> > > On Thu, 14 Apr 2011, Greg KH wrote:
> > > > On Thu, Apr 14, 2011 at 11:35:43AM -0400, Alan Stern wrote:
> > > > > On Thu, 14 Apr 2011, Heiko [iso-8859-1] St?bner wrote:
> > > > > > From: Thomas Abraham <thomas.ab@samsung.com>
> > > > > > 
> > > > > > The Samsung's S3C2416, S3C2443 and S3C2450 includes a USB
> > > > > > High-Speed device controller module. This driver enables support
> > > > > > for USB high-speed gadget functionality for the Samsung S3C24xx
> > > > > > SoC's that include this controller.
> > > > > > 
> > > > > > Signed-off-by: Thomas Abraham <thomas.ab@samsung.com>
> > > > > > Signed-off-by: Sangbeom Kim <sbkim73@samsung.com>
> > > > > > Signed-off-by: Kukjin Kim <kgene.kim@samsung.com>
> > > > > > Signed-off-by: Alexander Neumann <alexander@bumpern.de>
> > > > > > Signed-off-by: Heiko Stuebner <heiko@sntech.de>
> > > > > 
> > > > > ...
> > > > > 
> > > > > > +static struct usb_ep_ops s3c_hsudc_ep_ops = {
> > > > > > +	.enable = s3c_hsudc_ep_enable,
> > > > > > +	.disable = s3c_hsudc_ep_disable,
> > > > > > +	.alloc_request = s3c_hsudc_alloc_request,
> > > > > > +	.free_request = s3c_hsudc_free_request,
> > > > > > +	.queue = s3c_hsudc_queue,
> > > > > > +	.dequeue = s3c_hsudc_dequeue,
> > > > > > +	.set_halt = s3c_hsudc_set_halt,
> > > > > > +};
> > > > > 
> > > > > There's no .set_wedge method.  Why do people always leave this out?
> > > > 
> > > > Does the code spit out a nasty warning if this isn't set?  If not, I
> > > > would suggest adding it so that this doesn't keep happening.
> > > > 
> > > > Or just refuse to be able to register the structure, that would stop
> > > > it right away :)
> > > 
> > > In fact, set_wedge is optional.  But it's so easy to implement, there's
> > > no good reason for leaving it out.
> > 
> > It seems Thomas [original author of the driver] will be able to implement
> > said set_wedge function for it.
> > As he will need a bit of time for this, two possible ways for going
> > forward come to mind:
> > (1) use current driver [as set_wedge is optional] and add it later via
> > patch (2) resubmit whole driver again when set_wedge is added to it
> > 
> > Obviously I would prefer option 1 :-), but in the end it's your decision.
>
> It shouldn't take that much time to do this, what is the delay?
from what I gathered, simply finding the necessary time slot for this.

> I'd prefer to get the correct version implemented and would not like to
> accept a patch that everyone knows is wrong.
ok, I will resubmit the driver when set_wedge is included

Heiko

^ permalink raw reply	[flat|nested] 52+ messages in thread

* [PATCH v3 0/4] USB: gadget: Add Samsung S3C24xx USB HS controller driver
  2011-04-16 14:58                   ` Greg KH
@ 2011-05-07 20:23                     ` Heiko Stübner
  -1 siblings, 0 replies; 52+ messages in thread
From: Heiko Stübner @ 2011-05-07 20:23 UTC (permalink / raw)
  To: Greg KH
  Cc: Alan Stern, Kukjin Kim, Ben Dooks, Thomas Abraham, Sangbeom Kim,
	linux-arm-kernel, linux-samsung-soc, linux-usb,
	Alexander Neumann, Heiko Stübner

Hi all,

Am Samstag 16 April 2011, 16:58:30 schrieb Greg KH:
> I'd prefer to get the correct version implemented and would not like to
> accept a patch that everyone knows is wrong.
again the whole 4-patch series. This time with added set_wedge function.

Thanks
Heiko

^ permalink raw reply	[flat|nested] 52+ messages in thread

* [PATCH v3 0/4] USB: gadget: Add Samsung S3C24xx USB HS controller driver
@ 2011-05-07 20:23                     ` Heiko Stübner
  0 siblings, 0 replies; 52+ messages in thread
From: Heiko Stübner @ 2011-05-07 20:23 UTC (permalink / raw)
  To: linux-arm-kernel

Hi all,

Am Samstag 16 April 2011, 16:58:30 schrieb Greg KH:
> I'd prefer to get the correct version implemented and would not like to
> accept a patch that everyone knows is wrong.
again the whole 4-patch series. This time with added set_wedge function.

Thanks
Heiko

^ permalink raw reply	[flat|nested] 52+ messages in thread

* [PATCH 1/4] ARM: S3C2416: Add USB Phy register definitions
  2011-05-07 20:23                     ` Heiko Stübner
@ 2011-05-07 20:24                       ` Heiko Stübner
  -1 siblings, 0 replies; 52+ messages in thread
From: Heiko Stübner @ 2011-05-07 20:24 UTC (permalink / raw)
  To: Greg KH
  Cc: Alan Stern, Kukjin Kim, Ben Dooks, Thomas Abraham, Sangbeom Kim,
	linux-arm-kernel, linux-samsung-soc, linux-usb,
	Alexander Neumann

From: Thomas Abraham <thomas.ab@samsung.com>

Add register definitions required to configure the USB Phy. The definitions
for PHYCTRL, PHYPWR, URSTCON and UCLKCON registers and corresponding bit
field definitions are added.

Signed-off-by: Thomas Abraham <thomas.ab@samsung.com>
Signed-off-by: Sangbeom Kim <sbkim73@samsung.com>
---
 .../mach-s3c2410/include/mach/regs-s3c2443-clock.h |   25 
++++++++++++++++++++
 1 files changed, 25 insertions(+), 0 deletions(-)

diff --git a/arch/arm/mach-s3c2410/include/mach/regs-s3c2443-clock.h 
b/arch/arm/mach-s3c2410/include/mach/regs-s3c2443-clock.h
index 44494a5..5e06c72 100644
--- a/arch/arm/mach-s3c2410/include/mach/regs-s3c2443-clock.h
+++ b/arch/arm/mach-s3c2410/include/mach/regs-s3c2443-clock.h
@@ -37,6 +37,10 @@
 #define S3C2443_SYSID			S3C2443_CLKREG(0x5C)
 #define S3C2443_PWRCFG			S3C2443_CLKREG(0x60)
 #define S3C2443_RSTCON			S3C2443_CLKREG(0x64)
+#define S3C2443_PHYCTRL			S3C2443_CLKREG(0x80)
+#define S3C2443_PHYPWR			S3C2443_CLKREG(0x84)
+#define S3C2443_URSTCON			S3C2443_CLKREG(0x88)
+#define S3C2443_UCLKCON			S3C2443_CLKREG(0x8C)
 
 #define S3C2443_SWRST_RESET		(0x533c2443)
 
@@ -121,6 +125,27 @@
 
 #define S3C2443_PWRCFG_SLEEP		(1<<15)
 
+#define S3C2443_PWRCFG_USBPHY		(1 << 4)
+
+#define S3C2443_URSTCON_FUNCRST		(1 << 2)
+#define S3C2443_URSTCON_PHYRST		(1 << 0)
+
+#define S3C2443_PHYCTRL_CLKSEL		(1 << 3)
+#define S3C2443_PHYCTRL_EXTCLK		(1 << 2)
+#define S3C2443_PHYCTRL_PLLSEL		(1 << 1)
+#define S3C2443_PHYCTRL_DSPORT		(1 << 0)
+
+#define S3C2443_PHYPWR_COMMON_ON	(1 << 31)
+#define S3C2443_PHYPWR_ANALOG_PD	(1 << 4)
+#define S3C2443_PHYPWR_PLL_REFCLK	(1 << 3)
+#define S3C2443_PHYPWR_XO_ON		(1 << 2)
+#define S3C2443_PHYPWR_PLL_PWRDN	(1 << 1)
+#define S3C2443_PHYPWR_FSUSPEND		(1 << 0)
+
+#define S3C2443_UCLKCON_DETECT_VBUS	(1 << 31)
+#define S3C2443_UCLKCON_FUNC_CLKEN	(1 << 2)
+#define S3C2443_UCLKCON_TCLKEN		(1 << 0)
+
 #include <asm/div64.h>
 
 static inline unsigned int
-- 
1.7.2.3

^ permalink raw reply related	[flat|nested] 52+ messages in thread

* [PATCH 1/4] ARM: S3C2416: Add USB Phy register definitions
@ 2011-05-07 20:24                       ` Heiko Stübner
  0 siblings, 0 replies; 52+ messages in thread
From: Heiko Stübner @ 2011-05-07 20:24 UTC (permalink / raw)
  To: linux-arm-kernel

From: Thomas Abraham <thomas.ab@samsung.com>

Add register definitions required to configure the USB Phy. The definitions
for PHYCTRL, PHYPWR, URSTCON and UCLKCON registers and corresponding bit
field definitions are added.

Signed-off-by: Thomas Abraham <thomas.ab@samsung.com>
Signed-off-by: Sangbeom Kim <sbkim73@samsung.com>
---
 .../mach-s3c2410/include/mach/regs-s3c2443-clock.h |   25 
++++++++++++++++++++
 1 files changed, 25 insertions(+), 0 deletions(-)

diff --git a/arch/arm/mach-s3c2410/include/mach/regs-s3c2443-clock.h 
b/arch/arm/mach-s3c2410/include/mach/regs-s3c2443-clock.h
index 44494a5..5e06c72 100644
--- a/arch/arm/mach-s3c2410/include/mach/regs-s3c2443-clock.h
+++ b/arch/arm/mach-s3c2410/include/mach/regs-s3c2443-clock.h
@@ -37,6 +37,10 @@
 #define S3C2443_SYSID			S3C2443_CLKREG(0x5C)
 #define S3C2443_PWRCFG			S3C2443_CLKREG(0x60)
 #define S3C2443_RSTCON			S3C2443_CLKREG(0x64)
+#define S3C2443_PHYCTRL			S3C2443_CLKREG(0x80)
+#define S3C2443_PHYPWR			S3C2443_CLKREG(0x84)
+#define S3C2443_URSTCON			S3C2443_CLKREG(0x88)
+#define S3C2443_UCLKCON			S3C2443_CLKREG(0x8C)
 
 #define S3C2443_SWRST_RESET		(0x533c2443)
 
@@ -121,6 +125,27 @@
 
 #define S3C2443_PWRCFG_SLEEP		(1<<15)
 
+#define S3C2443_PWRCFG_USBPHY		(1 << 4)
+
+#define S3C2443_URSTCON_FUNCRST		(1 << 2)
+#define S3C2443_URSTCON_PHYRST		(1 << 0)
+
+#define S3C2443_PHYCTRL_CLKSEL		(1 << 3)
+#define S3C2443_PHYCTRL_EXTCLK		(1 << 2)
+#define S3C2443_PHYCTRL_PLLSEL		(1 << 1)
+#define S3C2443_PHYCTRL_DSPORT		(1 << 0)
+
+#define S3C2443_PHYPWR_COMMON_ON	(1 << 31)
+#define S3C2443_PHYPWR_ANALOG_PD	(1 << 4)
+#define S3C2443_PHYPWR_PLL_REFCLK	(1 << 3)
+#define S3C2443_PHYPWR_XO_ON		(1 << 2)
+#define S3C2443_PHYPWR_PLL_PWRDN	(1 << 1)
+#define S3C2443_PHYPWR_FSUSPEND		(1 << 0)
+
+#define S3C2443_UCLKCON_DETECT_VBUS	(1 << 31)
+#define S3C2443_UCLKCON_FUNC_CLKEN	(1 << 2)
+#define S3C2443_UCLKCON_TCLKEN		(1 << 0)
+
 #include <asm/div64.h>
 
 static inline unsigned int
-- 
1.7.2.3

^ permalink raw reply related	[flat|nested] 52+ messages in thread

* [PATCH 2/4] ARM: S3C24XX: Add plaform device definition for USB High-Speed gadget controller.
  2011-05-07 20:23                     ` Heiko Stübner
@ 2011-05-07 20:26                         ` Heiko Stübner
  -1 siblings, 0 replies; 52+ messages in thread
From: Heiko Stübner @ 2011-05-07 20:26 UTC (permalink / raw)
  To: Greg KH
  Cc: Alan Stern, Kukjin Kim, Ben Dooks, Thomas Abraham, Sangbeom Kim,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	linux-samsung-soc-u79uwXL29TY76Z2rM5mHXA,
	linux-usb-u79uwXL29TY76Z2rM5mHXA, Alexander Neumann

From: Thomas Abraham <thomas.ab-Sze3O3UU22JBDgjK7y7TUQ@public.gmane.org>

S3C2416, S3C2443 and S3C2450 includes a USB High-Speed Gadget controller module.
This patch adds the following for supporting this controller.

1. Definition for USB High-Speed controller base address.
2. Platform device instantiation.
3. Declaration for platform data structure.
4. Functionality to setup platform data for the controller.

Signed-off-by: Thomas Abraham <thomas.ab-Sze3O3UU22JBDgjK7y7TUQ@public.gmane.org>
Signed-off-by: Sangbeom Kim <sbkim73-Sze3O3UU22JBDgjK7y7TUQ@public.gmane.org>
---
 arch/arm/mach-s3c2410/include/mach/map.h  |    4 +++
 arch/arm/plat-s3c24xx/devs.c              |   41 +++++++++++++++++++++++++++++
 arch/arm/plat-s3c24xx/include/plat/udc.h  |   17 ++++++++++++
 arch/arm/plat-samsung/include/plat/devs.h |    1 +
 4 files changed, 63 insertions(+), 0 deletions(-)

diff --git a/arch/arm/mach-s3c2410/include/mach/map.h b/arch/arm/mach-s3c2410/include/mach/map.h
index 25bbf5a..425552d 100644
--- a/arch/arm/mach-s3c2410/include/mach/map.h
+++ b/arch/arm/mach-s3c2410/include/mach/map.h
@@ -21,6 +21,10 @@
 /* USB host controller */
 #define S3C2410_PA_USBHOST (0x49000000)
 
+/* S3C2416/S3C2443/S3C2450 High-Speed USB Gadget */
+#define S3C2416_PA_HSUDC	(0x49800000)
+#define S3C2416_SZ_HSUDC	(SZ_4K)
+
 /* DMA controller */
 #define S3C2410_PA_DMA	   (0x4B000000)
 #define S3C24XX_SZ_DMA	   SZ_1M
diff --git a/arch/arm/plat-s3c24xx/devs.c b/arch/arm/plat-s3c24xx/devs.c
index 268f3ed..7366799 100644
--- a/arch/arm/plat-s3c24xx/devs.c
+++ b/arch/arm/plat-s3c24xx/devs.c
@@ -22,6 +22,7 @@
 #include <linux/io.h>
 #include <linux/slab.h>
 #include <linux/string.h>
+#include <linux/dma-mapping.h>
 
 #include <asm/mach/arch.h>
 #include <asm/mach/map.h>
@@ -233,6 +234,46 @@ void __init s3c24xx_udc_set_platdata(struct s3c2410_udc_mach_info *pd)
 	}
 }
 
+/* USB High Speed 2.0 Device (Gadget) */
+static struct resource s3c_hsudc_resource[] = {
+	[0] = {
+		.start	= S3C2416_PA_HSUDC,
+		.end	= S3C2416_PA_HSUDC + S3C2416_SZ_HSUDC - 1,
+		.flags	= IORESOURCE_MEM,
+	},
+	[1] = {
+		.start	= IRQ_USBD,
+		.end	= IRQ_USBD,
+		.flags	= IORESOURCE_IRQ,
+	}
+};
+
+static u64 s3c_hsudc_dmamask = DMA_BIT_MASK(32);
+
+struct platform_device s3c_device_usb_hsudc = {
+	.name		= "s3c-hsudc",
+	.id		= -1,
+	.num_resources	= ARRAY_SIZE(s3c_hsudc_resource),
+	.resource	= s3c_hsudc_resource,
+	.dev		= {
+		.dma_mask		= &s3c_hsudc_dmamask,
+		.coherent_dma_mask	= DMA_BIT_MASK(32),
+	},
+};
+
+void __init s3c24xx_hsudc_set_platdata(struct s3c24xx_hsudc_platdata *pd)
+{
+	struct s3c24xx_hsudc_platdata *npd;
+
+	npd = kmalloc(sizeof(*npd), GFP_KERNEL);
+	if (npd) {
+		memcpy(npd, pd, sizeof(*npd));
+		s3c_device_usb_hsudc.dev.platform_data = npd;
+	} else {
+		printk(KERN_ERR "no memory for udc platform data\n");
+	}
+}
+
 /* IIS */
 
 static struct resource s3c_iis_resource[] = {
diff --git a/arch/arm/plat-s3c24xx/include/plat/udc.h b/arch/arm/plat-s3c24xx/include/plat/udc.h
index 80457c6..f6388424 100644
--- a/arch/arm/plat-s3c24xx/include/plat/udc.h
+++ b/arch/arm/plat-s3c24xx/include/plat/udc.h
@@ -37,4 +37,21 @@ struct s3c2410_udc_mach_info {
 
 extern void __init s3c24xx_udc_set_platdata(struct s3c2410_udc_mach_info *);
 
+/**
+ * s3c24xx_hsudc_platdata - Platform data for USB High-Speed gadget controller.
+ * @epnum: Number of endpoints to be instantiated by the controller driver.
+ * @gpio_init: Platform specific USB related GPIO initialization.
+ * @gpio_uninit: Platform specific USB releted GPIO uninitialzation.
+ *
+ * Representation of platform data for the S3C24XX USB 2.0 High Speed gadget
+ * controllers.
+ */
+struct s3c24xx_hsudc_platdata {
+	unsigned int	epnum;
+	void		(*gpio_init)(void);
+	void		(*gpio_uninit)(void);
+};
+
+extern void __init s3c24xx_hsudc_set_platdata(struct s3c24xx_hsudc_platdata *pd);
+
 #endif /* __ASM_ARM_ARCH_UDC_H */
diff --git a/arch/arm/plat-samsung/include/plat/devs.h b/arch/arm/plat-samsung/include/plat/devs.h
index 3f38deb..39818d8 100644
--- a/arch/arm/plat-samsung/include/plat/devs.h
+++ b/arch/arm/plat-samsung/include/plat/devs.h
@@ -88,6 +88,7 @@ extern struct platform_device s3c64xx_device_onenand1;
 extern struct platform_device s5p_device_onenand;
 
 extern struct platform_device s3c_device_usbgadget;
+extern struct platform_device s3c_device_usb_hsudc;
 extern struct platform_device s3c_device_usb_hsotg;
 
 extern struct platform_device s5pv210_device_ac97;
-- 
1.7.2.3

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

^ permalink raw reply related	[flat|nested] 52+ messages in thread

* [PATCH 2/4] ARM: S3C24XX: Add plaform device definition for USB High-Speed gadget controller.
@ 2011-05-07 20:26                         ` Heiko Stübner
  0 siblings, 0 replies; 52+ messages in thread
From: Heiko Stübner @ 2011-05-07 20:26 UTC (permalink / raw)
  To: linux-arm-kernel

From: Thomas Abraham <thomas.ab@samsung.com>

S3C2416, S3C2443 and S3C2450 includes a USB High-Speed Gadget controller module.
This patch adds the following for supporting this controller.

1. Definition for USB High-Speed controller base address.
2. Platform device instantiation.
3. Declaration for platform data structure.
4. Functionality to setup platform data for the controller.

Signed-off-by: Thomas Abraham <thomas.ab@samsung.com>
Signed-off-by: Sangbeom Kim <sbkim73@samsung.com>
---
 arch/arm/mach-s3c2410/include/mach/map.h  |    4 +++
 arch/arm/plat-s3c24xx/devs.c              |   41 +++++++++++++++++++++++++++++
 arch/arm/plat-s3c24xx/include/plat/udc.h  |   17 ++++++++++++
 arch/arm/plat-samsung/include/plat/devs.h |    1 +
 4 files changed, 63 insertions(+), 0 deletions(-)

diff --git a/arch/arm/mach-s3c2410/include/mach/map.h b/arch/arm/mach-s3c2410/include/mach/map.h
index 25bbf5a..425552d 100644
--- a/arch/arm/mach-s3c2410/include/mach/map.h
+++ b/arch/arm/mach-s3c2410/include/mach/map.h
@@ -21,6 +21,10 @@
 /* USB host controller */
 #define S3C2410_PA_USBHOST (0x49000000)
 
+/* S3C2416/S3C2443/S3C2450 High-Speed USB Gadget */
+#define S3C2416_PA_HSUDC	(0x49800000)
+#define S3C2416_SZ_HSUDC	(SZ_4K)
+
 /* DMA controller */
 #define S3C2410_PA_DMA	   (0x4B000000)
 #define S3C24XX_SZ_DMA	   SZ_1M
diff --git a/arch/arm/plat-s3c24xx/devs.c b/arch/arm/plat-s3c24xx/devs.c
index 268f3ed..7366799 100644
--- a/arch/arm/plat-s3c24xx/devs.c
+++ b/arch/arm/plat-s3c24xx/devs.c
@@ -22,6 +22,7 @@
 #include <linux/io.h>
 #include <linux/slab.h>
 #include <linux/string.h>
+#include <linux/dma-mapping.h>
 
 #include <asm/mach/arch.h>
 #include <asm/mach/map.h>
@@ -233,6 +234,46 @@ void __init s3c24xx_udc_set_platdata(struct s3c2410_udc_mach_info *pd)
 	}
 }
 
+/* USB High Speed 2.0 Device (Gadget) */
+static struct resource s3c_hsudc_resource[] = {
+	[0] = {
+		.start	= S3C2416_PA_HSUDC,
+		.end	= S3C2416_PA_HSUDC + S3C2416_SZ_HSUDC - 1,
+		.flags	= IORESOURCE_MEM,
+	},
+	[1] = {
+		.start	= IRQ_USBD,
+		.end	= IRQ_USBD,
+		.flags	= IORESOURCE_IRQ,
+	}
+};
+
+static u64 s3c_hsudc_dmamask = DMA_BIT_MASK(32);
+
+struct platform_device s3c_device_usb_hsudc = {
+	.name		= "s3c-hsudc",
+	.id		= -1,
+	.num_resources	= ARRAY_SIZE(s3c_hsudc_resource),
+	.resource	= s3c_hsudc_resource,
+	.dev		= {
+		.dma_mask		= &s3c_hsudc_dmamask,
+		.coherent_dma_mask	= DMA_BIT_MASK(32),
+	},
+};
+
+void __init s3c24xx_hsudc_set_platdata(struct s3c24xx_hsudc_platdata *pd)
+{
+	struct s3c24xx_hsudc_platdata *npd;
+
+	npd = kmalloc(sizeof(*npd), GFP_KERNEL);
+	if (npd) {
+		memcpy(npd, pd, sizeof(*npd));
+		s3c_device_usb_hsudc.dev.platform_data = npd;
+	} else {
+		printk(KERN_ERR "no memory for udc platform data\n");
+	}
+}
+
 /* IIS */
 
 static struct resource s3c_iis_resource[] = {
diff --git a/arch/arm/plat-s3c24xx/include/plat/udc.h b/arch/arm/plat-s3c24xx/include/plat/udc.h
index 80457c6..f6388424 100644
--- a/arch/arm/plat-s3c24xx/include/plat/udc.h
+++ b/arch/arm/plat-s3c24xx/include/plat/udc.h
@@ -37,4 +37,21 @@ struct s3c2410_udc_mach_info {
 
 extern void __init s3c24xx_udc_set_platdata(struct s3c2410_udc_mach_info *);
 
+/**
+ * s3c24xx_hsudc_platdata - Platform data for USB High-Speed gadget controller.
+ * @epnum: Number of endpoints to be instantiated by the controller driver.
+ * @gpio_init: Platform specific USB related GPIO initialization.
+ * @gpio_uninit: Platform specific USB releted GPIO uninitialzation.
+ *
+ * Representation of platform data for the S3C24XX USB 2.0 High Speed gadget
+ * controllers.
+ */
+struct s3c24xx_hsudc_platdata {
+	unsigned int	epnum;
+	void		(*gpio_init)(void);
+	void		(*gpio_uninit)(void);
+};
+
+extern void __init s3c24xx_hsudc_set_platdata(struct s3c24xx_hsudc_platdata *pd);
+
 #endif /* __ASM_ARM_ARCH_UDC_H */
diff --git a/arch/arm/plat-samsung/include/plat/devs.h b/arch/arm/plat-samsung/include/plat/devs.h
index 3f38deb..39818d8 100644
--- a/arch/arm/plat-samsung/include/plat/devs.h
+++ b/arch/arm/plat-samsung/include/plat/devs.h
@@ -88,6 +88,7 @@ extern struct platform_device s3c64xx_device_onenand1;
 extern struct platform_device s5p_device_onenand;
 
 extern struct platform_device s3c_device_usbgadget;
+extern struct platform_device s3c_device_usb_hsudc;
 extern struct platform_device s3c_device_usb_hsotg;
 
 extern struct platform_device s5pv210_device_ac97;
-- 
1.7.2.3

^ permalink raw reply related	[flat|nested] 52+ messages in thread

* [PATCH v5 3/4] USB: Gadget: Add Samsung S3C24XX USB High-Speed controller driver
  2011-05-07 20:23                     ` Heiko Stübner
@ 2011-05-07 20:28                       ` Heiko Stübner
  -1 siblings, 0 replies; 52+ messages in thread
From: Heiko Stübner @ 2011-05-07 20:28 UTC (permalink / raw)
  To: Greg KH
  Cc: Alan Stern, Kukjin Kim, Ben Dooks, Thomas Abraham, Sangbeom Kim,
	linux-arm-kernel, linux-samsung-soc, linux-usb,
	Alexander Neumann

From: Thomas Abraham <thomas.ab@samsung.com>

The Samsung's S3C2416, S3C2443 and S3C2450 includes a USB High-Speed
device controller module. This driver enables support for USB high-speed
gadget functionality for the Samsung S3C24xx SoC's that include this
controller.

Signed-off-by: Thomas Abraham <thomas.ab@samsung.com>
Signed-off-by: Sangbeom Kim <sbkim73@samsung.com>
Signed-off-by: Kukjin Kim <kgene.kim@samsung.com>
Signed-off-by: Alexander Neumann <alexander@bumpern.de>
Signed-off-by: Heiko Stuebner <heiko@sntech.de>
---
Changes since v1:
- Addressed comments from Ben Dooks
  Thomas Abraham <thomas.ab@samsung.com>

Changes since v2:
- Update the driver to match the data structure changes introduced by
  commit b0fca50f5a94a268ed02cfddf44448051ed9343f.
  Alexander Neumann <alexander@debian.org>
- Move s3c_hsudc to 0x29 in usb_gadget_controller_number in gadget_chips.h
  Heiko Stuebner <heiko@sntech.de>

Changes since v3:
- Update gadget_chips.h-id again
  Heiko Stuebner <heiko@sntech.de>

Changes since v4:
- Add set_wedge function
  Thomas Abraham <thomas.ab@samsung.com>

 drivers/usb/gadget/Kconfig        |   17 +
 drivers/usb/gadget/Makefile       |    1 +
 drivers/usb/gadget/gadget_chips.h |    8 +
 drivers/usb/gadget/s3c-hsudc.c    | 1349 +++++++++++++++++++++++++++++++++++++
 4 files changed, 1375 insertions(+), 0 deletions(-)
 create mode 100644 drivers/usb/gadget/s3c-hsudc.c

diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig
index 4c02b9f..58456d1 100644
--- a/drivers/usb/gadget/Kconfig
+++ b/drivers/usb/gadget/Kconfig
@@ -356,6 +356,23 @@ config USB_S3C2410_DEBUG
 	boolean "S3C2410 udc debug messages"
 	depends on USB_GADGET_S3C2410
 
+config USB_GADGET_S3C_HSUDC
+	boolean "S3C2416, S3C2443 and S3C2450 USB Device Controller"
+	depends on ARCH_S3C2410
+	select USB_GADGET_DUALSPEED
+	help
+	  Samsung's S3C2416, S3C2443 and S3C2450 is an ARM9 based SoC
+	  integrated with dual speed USB 2.0 device controller. It has
+	  8 endpoints, as well as endpoint zero.
+
+	  This driver has been tested on S3C2416 and S3C2450 processors.
+
+config USB_S3C_HSUDC
+	tristate
+	depends on USB_GADGET_S3C_HSUDC
+	default USB_GADGET
+	select USB_GADGET_SELECTED
+
 config USB_GADGET_PXA_U2O
 	boolean "PXA9xx Processor USB2.0 controller"
 	select USB_GADGET_DUALSPEED
diff --git a/drivers/usb/gadget/Makefile b/drivers/usb/gadget/Makefile
index 1ea15ee..4fe92b1 100644
--- a/drivers/usb/gadget/Makefile
+++ b/drivers/usb/gadget/Makefile
@@ -22,6 +22,7 @@ obj-$(CONFIG_USB_R8A66597)	+= r8a66597-udc.o
 obj-$(CONFIG_USB_FSL_QE)	+= fsl_qe_udc.o
 obj-$(CONFIG_USB_CI13XXX_PCI)	+= ci13xxx_pci.o
 obj-$(CONFIG_USB_S3C_HSOTG)	+= s3c-hsotg.o
+obj-$(CONFIG_USB_S3C_HSUDC)	+= s3c-hsudc.o
 obj-$(CONFIG_USB_LANGWELL)	+= langwell_udc.o
 obj-$(CONFIG_USB_EG20T)		+= pch_udc.o
 obj-$(CONFIG_USB_PXA_U2O)	+= mv_udc.o
diff --git a/drivers/usb/gadget/gadget_chips.h b/drivers/usb/gadget/gadget_chips.h
index ec3fd97..bcdac7c 100644
--- a/drivers/usb/gadget/gadget_chips.h
+++ b/drivers/usb/gadget/gadget_chips.h
@@ -136,6 +136,12 @@
 #define gadget_is_s3c_hsotg(g)    0
 #endif
 
+#ifdef CONFIG_USB_S3C_HSUDC
+#define gadget_is_s3c_hsudc(g) (!strcmp("s3c-hsudc", (g)->name))
+#else
+#define gadget_is_s3c_hsudc(g) 0
+#endif
+
 #ifdef CONFIG_USB_GADGET_EG20T
 #define	gadget_is_pch(g)	(!strcmp("pch_udc", (g)->name))
 #else
@@ -215,6 +221,8 @@ static inline int usb_gadget_controller_number(struct usb_gadget *gadget)
 		return 0x28;
 	else if (gadget_is_renesas_usbhs(gadget))
 		return 0x29;
+	else if (gadget_is_s3c_hsudc(gadget))
+		return 0x30;
 
 	return -ENOENT;
 }
diff --git a/drivers/usb/gadget/s3c-hsudc.c b/drivers/usb/gadget/s3c-hsudc.c
new file mode 100644
index 0000000..c509487
--- /dev/null
+++ b/drivers/usb/gadget/s3c-hsudc.c
@@ -0,0 +1,1349 @@
+/* linux/drivers/usb/gadget/s3c-hsudc.c
+ *
+ * Copyright (c) 2010 Samsung Electronics Co., Ltd.
+ *		http://www.samsung.com/
+ *
+ * S3C24XX USB 2.0 High-speed USB controller gadget driver
+ *
+ * The S3C24XX USB 2.0 high-speed USB controller supports upto 9 endpoints.
+ * Each endpoint can be configured as either in or out endpoint. Endpoints
+ * can be configured for Bulk or Interrupt transfer mode.
+ *
+ * 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.
+*/
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/spinlock.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <linux/dma-mapping.h>
+#include <linux/delay.h>
+#include <linux/io.h>
+#include <linux/slab.h>
+#include <linux/clk.h>
+#include <linux/usb/ch9.h>
+#include <linux/usb/gadget.h>
+
+#include <mach/regs-s3c2443-clock.h>
+#include <plat/udc.h>
+
+#define S3C_HSUDC_REG(x)	(x)
+
+/* Non-Indexed Registers */
+#define S3C_IR				S3C_HSUDC_REG(0x00) /* Index Register */
+#define S3C_EIR				S3C_HSUDC_REG(0x04) /* EP Intr Status */
+#define S3C_EIR_EP0			(1<<0)
+#define S3C_EIER			S3C_HSUDC_REG(0x08) /* EP Intr Enable */
+#define S3C_FAR				S3C_HSUDC_REG(0x0c) /* Gadget Address */
+#define S3C_FNR				S3C_HSUDC_REG(0x10) /* Frame Number */
+#define S3C_EDR				S3C_HSUDC_REG(0x14) /* EP Direction */
+#define S3C_TR				S3C_HSUDC_REG(0x18) /* Test Register */
+#define S3C_SSR				S3C_HSUDC_REG(0x1c) /* System Status */
+#define S3C_SSR_DTZIEN_EN		(0xff8f)
+#define S3C_SSR_ERR			(0xff80)
+#define S3C_SSR_VBUSON			(1 << 8)
+#define S3C_SSR_HSP			(1 << 4)
+#define S3C_SSR_SDE			(1 << 3)
+#define S3C_SSR_RESUME			(1 << 2)
+#define S3C_SSR_SUSPEND			(1 << 1)
+#define S3C_SSR_RESET			(1 << 0)
+#define S3C_SCR				S3C_HSUDC_REG(0x20) /* System Control */
+#define S3C_SCR_DTZIEN_EN		(1 << 14)
+#define S3C_SCR_RRD_EN			(1 << 5)
+#define S3C_SCR_SUS_EN			(1 << 1)
+#define S3C_SCR_RST_EN			(1 << 0)
+#define S3C_EP0SR			S3C_HSUDC_REG(0x24) /* EP0 Status */
+#define S3C_EP0SR_EP0_LWO		(1 << 6)
+#define S3C_EP0SR_STALL			(1 << 4)
+#define S3C_EP0SR_TX_SUCCESS		(1 << 1)
+#define S3C_EP0SR_RX_SUCCESS		(1 << 0)
+#define S3C_EP0CR			S3C_HSUDC_REG(0x28) /* EP0 Control */
+#define S3C_BR(_x)			S3C_HSUDC_REG(0x60 + (_x * 4))
+
+/* Indexed Registers */
+#define S3C_ESR				S3C_HSUDC_REG(0x2c) /* EPn Status */
+#define S3C_ESR_FLUSH			(1 << 6)
+#define S3C_ESR_STALL			(1 << 5)
+#define S3C_ESR_LWO			(1 << 4)
+#define S3C_ESR_PSIF_ONE		(1 << 2)
+#define S3C_ESR_PSIF_TWO		(2 << 2)
+#define S3C_ESR_TX_SUCCESS		(1 << 1)
+#define S3C_ESR_RX_SUCCESS		(1 << 0)
+#define S3C_ECR				S3C_HSUDC_REG(0x30) /* EPn Control */
+#define S3C_ECR_DUEN			(1 << 7)
+#define S3C_ECR_FLUSH			(1 << 6)
+#define S3C_ECR_STALL			(1 << 1)
+#define S3C_ECR_IEMS			(1 << 0)
+#define S3C_BRCR			S3C_HSUDC_REG(0x34) /* Read Count */
+#define S3C_BWCR			S3C_HSUDC_REG(0x38) /* Write Count */
+#define S3C_MPR				S3C_HSUDC_REG(0x3c) /* Max Pkt Size */
+
+#define WAIT_FOR_SETUP			(0)
+#define DATA_STATE_XMIT			(1)
+#define DATA_STATE_RECV			(2)
+
+/**
+ * struct s3c_hsudc_ep - Endpoint representation used by driver.
+ * @ep: USB gadget layer representation of device endpoint.
+ * @name: Endpoint name (as required by ep autoconfiguration).
+ * @dev: Reference to the device controller to which this EP belongs.
+ * @desc: Endpoint descriptor obtained from the gadget driver.
+ * @queue: Transfer request queue for the endpoint.
+ * @stopped: Maintains state of endpoint, set if EP is halted.
+ * @bEndpointAddress: EP address (including direction bit).
+ * @fifo: Base address of EP FIFO.
+ */
+struct s3c_hsudc_ep {
+	struct usb_ep ep;
+	char name[20];
+	struct s3c_hsudc *dev;
+	const struct usb_endpoint_descriptor *desc;
+	struct list_head queue;
+	u8 stopped;
+	u8 wedge;
+	u8 bEndpointAddress;
+	void __iomem *fifo;
+};
+
+/**
+ * struct s3c_hsudc_req - Driver encapsulation of USB gadget transfer request.
+ * @req: Reference to USB gadget transfer request.
+ * @queue: Used for inserting this request to the endpoint request queue.
+ */
+struct s3c_hsudc_req {
+	struct usb_request req;
+	struct list_head queue;
+};
+
+/**
+ * struct s3c_hsudc - Driver's abstraction of the device controller.
+ * @gadget: Instance of usb_gadget which is referenced by gadget driver.
+ * @driver: Reference to currenty active gadget driver.
+ * @dev: The device reference used by probe function.
+ * @lock: Lock to synchronize the usage of Endpoints (EP's are indexed).
+ * @regs: Remapped base address of controller's register space.
+ * @mem_rsrc: Device memory resource used for remapping device register space.
+ * irq: IRQ number used by the controller.
+ * uclk: Reference to the controller clock.
+ * ep0state: Current state of EP0.
+ * ep: List of endpoints supported by the controller.
+ */
+struct s3c_hsudc {
+	struct usb_gadget gadget;
+	struct usb_gadget_driver *driver;
+	struct device *dev;
+	struct s3c24xx_hsudc_platdata *pd;
+	spinlock_t lock;
+	void __iomem *regs;
+	struct resource *mem_rsrc;
+	int irq;
+	struct clk *uclk;
+	int ep0state;
+	struct s3c_hsudc_ep ep[];
+};
+
+#define ep_maxpacket(_ep)	((_ep)->ep.maxpacket)
+#define ep_is_in(_ep)		((_ep)->bEndpointAddress & USB_DIR_IN)
+#define ep_index(_ep)		((_ep)->bEndpointAddress & \
+					USB_ENDPOINT_NUMBER_MASK)
+
+static struct s3c_hsudc *the_controller;
+static const char driver_name[] = "s3c-udc";
+static const char ep0name[] = "ep0-control";
+
+static inline struct s3c_hsudc_req *our_req(struct usb_request *req)
+{
+	return container_of(req, struct s3c_hsudc_req, req);
+}
+
+static inline struct s3c_hsudc_ep *our_ep(struct usb_ep *ep)
+{
+	return container_of(ep, struct s3c_hsudc_ep, ep);
+}
+
+static inline struct s3c_hsudc *to_hsudc(struct usb_gadget *gadget)
+{
+	return container_of(gadget, struct s3c_hsudc, gadget);
+}
+
+static inline void set_index(struct s3c_hsudc *hsudc, int ep_addr)
+{
+	ep_addr &= USB_ENDPOINT_NUMBER_MASK;
+	writel(ep_addr, hsudc->regs + S3C_IR);
+}
+
+static inline void __orr32(void __iomem *ptr, u32 val)
+{
+	writel(readl(ptr) | val, ptr);
+}
+
+static void s3c_hsudc_init_phy(void)
+{
+	u32 cfg;
+
+	cfg = readl(S3C2443_PWRCFG) | S3C2443_PWRCFG_USBPHY;
+	writel(cfg, S3C2443_PWRCFG);
+
+	cfg = readl(S3C2443_URSTCON);
+	cfg |= (S3C2443_URSTCON_FUNCRST | S3C2443_URSTCON_PHYRST);
+	writel(cfg, S3C2443_URSTCON);
+	mdelay(1);
+
+	cfg = readl(S3C2443_URSTCON);
+	cfg &= ~(S3C2443_URSTCON_FUNCRST | S3C2443_URSTCON_PHYRST);
+	writel(cfg, S3C2443_URSTCON);
+
+	cfg = readl(S3C2443_PHYCTRL);
+	cfg &= ~(S3C2443_PHYCTRL_CLKSEL | S3C2443_PHYCTRL_DSPORT);
+	cfg |= (S3C2443_PHYCTRL_EXTCLK | S3C2443_PHYCTRL_PLLSEL);
+	writel(cfg, S3C2443_PHYCTRL);
+
+	cfg = readl(S3C2443_PHYPWR);
+	cfg &= ~(S3C2443_PHYPWR_FSUSPEND | S3C2443_PHYPWR_PLL_PWRDN |
+		S3C2443_PHYPWR_XO_ON | S3C2443_PHYPWR_PLL_REFCLK |
+		S3C2443_PHYPWR_ANALOG_PD);
+	cfg |= S3C2443_PHYPWR_COMMON_ON;
+	writel(cfg, S3C2443_PHYPWR);
+
+	cfg = readl(S3C2443_UCLKCON);
+	cfg |= (S3C2443_UCLKCON_DETECT_VBUS | S3C2443_UCLKCON_FUNC_CLKEN |
+		S3C2443_UCLKCON_TCLKEN);
+	writel(cfg, S3C2443_UCLKCON);
+}
+
+static void s3c_hsudc_uninit_phy(void)
+{
+	u32 cfg;
+
+	cfg = readl(S3C2443_PWRCFG) & ~S3C2443_PWRCFG_USBPHY;
+	writel(cfg, S3C2443_PWRCFG);
+
+	writel(S3C2443_PHYPWR_FSUSPEND, S3C2443_PHYPWR);
+
+	cfg = readl(S3C2443_UCLKCON) & ~S3C2443_UCLKCON_FUNC_CLKEN;
+	writel(cfg, S3C2443_UCLKCON);
+}
+
+/**
+ * s3c_hsudc_complete_request - Complete a transfer request.
+ * @hsep: Endpoint to which the request belongs.
+ * @hsreq: Transfer request to be completed.
+ * @status: Transfer completion status for the transfer request.
+ */
+static void s3c_hsudc_complete_request(struct s3c_hsudc_ep *hsep,
+				struct s3c_hsudc_req *hsreq, int status)
+{
+	unsigned int stopped = hsep->stopped;
+	struct s3c_hsudc *hsudc = hsep->dev;
+
+	list_del_init(&hsreq->queue);
+	hsreq->req.status = status;
+
+	if (!ep_index(hsep)) {
+		hsudc->ep0state = WAIT_FOR_SETUP;
+		hsep->bEndpointAddress &= ~USB_DIR_IN;
+	}
+
+	hsep->stopped = 1;
+	spin_unlock(&hsudc->lock);
+	if (hsreq->req.complete != NULL)
+		hsreq->req.complete(&hsep->ep, &hsreq->req);
+	spin_lock(&hsudc->lock);
+	hsep->stopped = stopped;
+}
+
+/**
+ * s3c_hsudc_nuke_ep - Terminate all requests queued for a endpoint.
+ * @hsep: Endpoint for which queued requests have to be terminated.
+ * @status: Transfer completion status for the transfer request.
+ */
+static void s3c_hsudc_nuke_ep(struct s3c_hsudc_ep *hsep, int status)
+{
+	struct s3c_hsudc_req *hsreq;
+
+	while (!list_empty(&hsep->queue)) {
+		hsreq = list_entry(hsep->queue.next,
+				struct s3c_hsudc_req, queue);
+		s3c_hsudc_complete_request(hsep, hsreq, status);
+	}
+}
+
+/**
+ * s3c_hsudc_stop_activity - Stop activity on all endpoints.
+ * @hsudc: Device controller for which EP activity is to be stopped.
+ * @driver: Reference to the gadget driver which is currently active.
+ *
+ * All the endpoints are stopped and any pending transfer requests if any on
+ * the endpoint are terminated.
+ */
+static void s3c_hsudc_stop_activity(struct s3c_hsudc *hsudc,
+			  struct usb_gadget_driver *driver)
+{
+	struct s3c_hsudc_ep *hsep;
+	int epnum;
+
+	hsudc->gadget.speed = USB_SPEED_UNKNOWN;
+
+	for (epnum = 0; epnum < hsudc->pd->epnum; epnum++) {
+		hsep = &hsudc->ep[epnum];
+		hsep->stopped = 1;
+		s3c_hsudc_nuke_ep(hsep, -ESHUTDOWN);
+	}
+
+	spin_unlock(&hsudc->lock);
+	driver->disconnect(&hsudc->gadget);
+	spin_lock(&hsudc->lock);
+}
+
+/**
+ * s3c_hsudc_read_setup_pkt - Read the received setup packet from EP0 fifo.
+ * @hsudc: Device controller from which setup packet is to be read.
+ * @buf: The buffer into which the setup packet is read.
+ *
+ * The setup packet received in the EP0 fifo is read and stored into a
+ * given buffer address.
+ */
+
+static void s3c_hsudc_read_setup_pkt(struct s3c_hsudc *hsudc, u16 *buf)
+{
+	int count;
+
+	count = readl(hsudc->regs + S3C_BRCR);
+	while (count--)
+		*buf++ = (u16)readl(hsudc->regs + S3C_BR(0));
+
+	writel(S3C_EP0SR_RX_SUCCESS, hsudc->regs + S3C_EP0SR);
+}
+
+/**
+ * s3c_hsudc_write_fifo - Write next chunk of transfer data to EP fifo.
+ * @hsep: Endpoint to which the data is to be written.
+ * @hsreq: Transfer request from which the next chunk of data is written.
+ *
+ * Write the next chunk of data from a transfer request to the endpoint FIFO.
+ * If the transfer request completes, 1 is returned, otherwise 0 is returned.
+ */
+static int s3c_hsudc_write_fifo(struct s3c_hsudc_ep *hsep,
+				struct s3c_hsudc_req *hsreq)
+{
+	u16 *buf;
+	u32 max = ep_maxpacket(hsep);
+	u32 count, length;
+	bool is_last;
+	void __iomem *fifo = hsep->fifo;
+
+	buf = hsreq->req.buf + hsreq->req.actual;
+	prefetch(buf);
+
+	length = hsreq->req.length - hsreq->req.actual;
+	length = min(length, max);
+	hsreq->req.actual += length;
+
+	writel(length, hsep->dev->regs + S3C_BWCR);
+	for (count = 0; count < length; count += 2)
+		writel(*buf++, fifo);
+
+	if (count != max) {
+		is_last = true;
+	} else {
+		if (hsreq->req.length != hsreq->req.actual || hsreq->req.zero)
+			is_last = false;
+		else
+			is_last = true;
+	}
+
+	if (is_last) {
+		s3c_hsudc_complete_request(hsep, hsreq, 0);
+		return 1;
+	}
+
+	return 0;
+}
+
+/**
+ * s3c_hsudc_read_fifo - Read the next chunk of data from EP fifo.
+ * @hsep: Endpoint from which the data is to be read.
+ * @hsreq: Transfer request to which the next chunk of data read is written.
+ *
+ * Read the next chunk of data from the endpoint FIFO and a write it to the
+ * transfer request buffer. If the transfer request completes, 1 is returned,
+ * otherwise 0 is returned.
+ */
+static int s3c_hsudc_read_fifo(struct s3c_hsudc_ep *hsep,
+				struct s3c_hsudc_req *hsreq)
+{
+	struct s3c_hsudc *hsudc = hsep->dev;
+	u32 csr, offset;
+	u16 *buf, word;
+	u32 buflen, rcnt, rlen;
+	void __iomem *fifo = hsep->fifo;
+	u32 is_short = 0;
+
+	offset = (ep_index(hsep)) ? S3C_ESR : S3C_EP0SR;
+	csr = readl(hsudc->regs + offset);
+	if (!(csr & S3C_ESR_RX_SUCCESS))
+		return -EINVAL;
+
+	buf = hsreq->req.buf + hsreq->req.actual;
+	prefetchw(buf);
+	buflen = hsreq->req.length - hsreq->req.actual;
+
+	rcnt = readl(hsudc->regs + S3C_BRCR);
+	rlen = (csr & S3C_ESR_LWO) ? (rcnt * 2 - 1) : (rcnt * 2);
+
+	hsreq->req.actual += min(rlen, buflen);
+	is_short = (rlen < hsep->ep.maxpacket);
+
+	while (rcnt-- != 0) {
+		word = (u16)readl(fifo);
+		if (buflen) {
+			*buf++ = word;
+			buflen--;
+		} else {
+			hsreq->req.status = -EOVERFLOW;
+		}
+	}
+
+	writel(S3C_ESR_RX_SUCCESS, hsudc->regs + offset);
+
+	if (is_short || hsreq->req.actual == hsreq->req.length) {
+		s3c_hsudc_complete_request(hsep, hsreq, 0);
+		return 1;
+	}
+
+	return 0;
+}
+
+/**
+ * s3c_hsudc_epin_intr - Handle in-endpoint interrupt.
+ * @hsudc - Device controller for which the interrupt is to be handled.
+ * @ep_idx - Endpoint number on which an interrupt is pending.
+ *
+ * Handles interrupt for a in-endpoint. The interrupts that are handled are
+ * stall and data transmit complete interrupt.
+ */
+static void s3c_hsudc_epin_intr(struct s3c_hsudc *hsudc, u32 ep_idx)
+{
+	struct s3c_hsudc_ep *hsep = &hsudc->ep[ep_idx];
+	struct s3c_hsudc_req *hsreq;
+	u32 csr;
+
+	csr = readl((u32)hsudc->regs + S3C_ESR);
+	if (csr & S3C_ESR_STALL) {
+		writel(S3C_ESR_STALL, hsudc->regs + S3C_ESR);
+		return;
+	}
+
+	if (csr & S3C_ESR_TX_SUCCESS) {
+		writel(S3C_ESR_TX_SUCCESS, hsudc->regs + S3C_ESR);
+		if (list_empty(&hsep->queue))
+			return;
+
+		hsreq = list_entry(hsep->queue.next,
+				struct s3c_hsudc_req, queue);
+		if ((s3c_hsudc_write_fifo(hsep, hsreq) == 0) &&
+				(csr & S3C_ESR_PSIF_TWO))
+			s3c_hsudc_write_fifo(hsep, hsreq);
+	}
+}
+
+/**
+ * s3c_hsudc_epout_intr - Handle out-endpoint interrupt.
+ * @hsudc - Device controller for which the interrupt is to be handled.
+ * @ep_idx - Endpoint number on which an interrupt is pending.
+ *
+ * Handles interrupt for a out-endpoint. The interrupts that are handled are
+ * stall, flush and data ready interrupt.
+ */
+static void s3c_hsudc_epout_intr(struct s3c_hsudc *hsudc, u32 ep_idx)
+{
+	struct s3c_hsudc_ep *hsep = &hsudc->ep[ep_idx];
+	struct s3c_hsudc_req *hsreq;
+	u32 csr;
+
+	csr = readl((u32)hsudc->regs + S3C_ESR);
+	if (csr & S3C_ESR_STALL) {
+		writel(S3C_ESR_STALL, hsudc->regs + S3C_ESR);
+		return;
+	}
+
+	if (csr & S3C_ESR_FLUSH) {
+		__orr32(hsudc->regs + S3C_ECR, S3C_ECR_FLUSH);
+		return;
+	}
+
+	if (csr & S3C_ESR_RX_SUCCESS) {
+		if (list_empty(&hsep->queue))
+			return;
+
+		hsreq = list_entry(hsep->queue.next,
+				struct s3c_hsudc_req, queue);
+		if (((s3c_hsudc_read_fifo(hsep, hsreq)) == 0) &&
+				(csr & S3C_ESR_PSIF_TWO))
+			s3c_hsudc_read_fifo(hsep, hsreq);
+	}
+}
+
+/** s3c_hsudc_set_halt - Set or clear a endpoint halt.
+ * @_ep: Endpoint on which halt has to be set or cleared.
+ * @value: 1 for setting halt on endpoint, 0 to clear halt.
+ *
+ * Set or clear endpoint halt. If halt is set, the endpoint is stopped.
+ * If halt is cleared, for in-endpoints, if there are any pending
+ * transfer requests, transfers are started.
+ */
+static int s3c_hsudc_set_halt(struct usb_ep *_ep, int value)
+{
+	struct s3c_hsudc_ep *hsep = our_ep(_ep);
+	struct s3c_hsudc *hsudc = hsep->dev;
+	struct s3c_hsudc_req *hsreq;
+	unsigned long irqflags;
+	u32 ecr;
+	u32 offset;
+
+	if (value && ep_is_in(hsep) && !list_empty(&hsep->queue))
+		return -EAGAIN;
+
+	spin_lock_irqsave(&hsudc->lock, irqflags);
+	set_index(hsudc, ep_index(hsep));
+	offset = (ep_index(hsep)) ? S3C_ECR : S3C_EP0CR;
+	ecr = readl(hsudc->regs + offset);
+
+	if (value) {
+		ecr |= S3C_ECR_STALL;
+		if (ep_index(hsep))
+			ecr |= S3C_ECR_FLUSH;
+		hsep->stopped = 1;
+	} else {
+		ecr &= ~S3C_ECR_STALL;
+		hsep->stopped = hsep->wedge = 0;
+	}
+	writel(ecr, hsudc->regs + offset);
+
+	if (ep_is_in(hsep) && !list_empty(&hsep->queue) && !value) {
+		hsreq = list_entry(hsep->queue.next,
+			struct s3c_hsudc_req, queue);
+		if (hsreq)
+			s3c_hsudc_write_fifo(hsep, hsreq);
+	}
+
+	spin_unlock_irqrestore(&hsudc->lock, irqflags);
+	return 0;
+}
+
+/** s3c_hsudc_set_wedge - Sets the halt feature with the clear requests ignored
+ * @_ep: Endpoint on which wedge has to be set.
+ *
+ * Sets the halt feature with the clear requests ignored.
+ */
+static int s3c_hsudc_set_wedge(struct usb_ep *_ep)
+{
+	struct s3c_hsudc_ep *hsep = our_ep(_ep);
+
+	if (!hsep)
+		return -EINVAL;
+
+	hsep->wedge = 1;
+	return usb_ep_set_halt(_ep);
+}
+
+/** s3c_hsudc_handle_reqfeat - Handle set feature or clear feature requests.
+ * @_ep: Device controller on which the set/clear feature needs to be handled.
+ * @ctrl: Control request as received on the endpoint 0.
+ *
+ * Handle set feature or clear feature control requests on the control endpoint.
+ */
+static int s3c_hsudc_handle_reqfeat(struct s3c_hsudc *hsudc,
+					struct usb_ctrlrequest *ctrl)
+{
+	struct s3c_hsudc_ep *hsep;
+	bool set = (ctrl->bRequest == USB_REQ_SET_FEATURE);
+	u8 ep_num = ctrl->wIndex & USB_ENDPOINT_NUMBER_MASK;
+
+	if (ctrl->bRequestType == USB_RECIP_ENDPOINT) {
+		hsep = &hsudc->ep[ep_num];
+		switch (le16_to_cpu(ctrl->wValue)) {
+		case USB_ENDPOINT_HALT:
+			if (set || (!set && !hsep->wedge))
+				s3c_hsudc_set_halt(&hsep->ep, set);
+			return 0;
+		}
+	}
+
+	return -ENOENT;
+}
+
+/**
+ * s3c_hsudc_process_req_status - Handle get status control request.
+ * @hsudc: Device controller on which get status request has be handled.
+ * @ctrl: Control request as received on the endpoint 0.
+ *
+ * Handle get status control request received on control endpoint.
+ */
+static void s3c_hsudc_process_req_status(struct s3c_hsudc *hsudc,
+					struct usb_ctrlrequest *ctrl)
+{
+	struct s3c_hsudc_ep *hsep0 = &hsudc->ep[0];
+	struct s3c_hsudc_req hsreq;
+	struct s3c_hsudc_ep *hsep;
+	__le16 reply;
+	u8 epnum;
+
+	switch (ctrl->bRequestType & USB_RECIP_MASK) {
+	case USB_RECIP_DEVICE:
+		reply = cpu_to_le16(0);
+		break;
+
+	case USB_RECIP_INTERFACE:
+		reply = cpu_to_le16(0);
+		break;
+
+	case USB_RECIP_ENDPOINT:
+		epnum = le16_to_cpu(ctrl->wIndex) & USB_ENDPOINT_NUMBER_MASK;
+		hsep = &hsudc->ep[epnum];
+		reply = cpu_to_le16(hsep->stopped ? 1 : 0);
+		break;
+	}
+
+	INIT_LIST_HEAD(&hsreq.queue);
+	hsreq.req.length = 2;
+	hsreq.req.buf = &reply;
+	hsreq.req.actual = 0;
+	hsreq.req.complete = NULL;
+	s3c_hsudc_write_fifo(hsep0, &hsreq);
+}
+
+/**
+ * s3c_hsudc_process_setup - Process control request received on endpoint 0.
+ * @hsudc: Device controller on which control request has been received.
+ *
+ * Read the control request received on endpoint 0, decode it and handle
+ * the request.
+ */
+static void s3c_hsudc_process_setup(struct s3c_hsudc *hsudc)
+{
+	struct s3c_hsudc_ep *hsep = &hsudc->ep[0];
+	struct usb_ctrlrequest ctrl = {0};
+	int ret;
+
+	s3c_hsudc_nuke_ep(hsep, -EPROTO);
+	s3c_hsudc_read_setup_pkt(hsudc, (u16 *)&ctrl);
+
+	if (ctrl.bRequestType & USB_DIR_IN) {
+		hsep->bEndpointAddress |= USB_DIR_IN;
+		hsudc->ep0state = DATA_STATE_XMIT;
+	} else {
+		hsep->bEndpointAddress &= ~USB_DIR_IN;
+		hsudc->ep0state = DATA_STATE_RECV;
+	}
+
+	switch (ctrl.bRequest) {
+	case USB_REQ_SET_ADDRESS:
+		if (ctrl.bRequestType != (USB_TYPE_STANDARD | USB_RECIP_DEVICE))
+			break;
+		hsudc->ep0state = WAIT_FOR_SETUP;
+		return;
+
+	case USB_REQ_GET_STATUS:
+		if ((ctrl.bRequestType & USB_TYPE_MASK) != USB_TYPE_STANDARD)
+			break;
+		s3c_hsudc_process_req_status(hsudc, &ctrl);
+		return;
+
+	case USB_REQ_SET_FEATURE:
+	case USB_REQ_CLEAR_FEATURE:
+		if ((ctrl.bRequestType & USB_TYPE_MASK) != USB_TYPE_STANDARD)
+			break;
+		s3c_hsudc_handle_reqfeat(hsudc, &ctrl);
+		hsudc->ep0state = WAIT_FOR_SETUP;
+		return;
+	}
+
+	if (hsudc->driver) {
+		spin_unlock(&hsudc->lock);
+		ret = hsudc->driver->setup(&hsudc->gadget, &ctrl);
+		spin_lock(&hsudc->lock);
+
+		if (ctrl.bRequest == USB_REQ_SET_CONFIGURATION) {
+			hsep->bEndpointAddress &= ~USB_DIR_IN;
+			hsudc->ep0state = WAIT_FOR_SETUP;
+		}
+
+		if (ret < 0) {
+			dev_err(hsudc->dev, "setup failed, returned %d\n",
+						ret);
+			s3c_hsudc_set_halt(&hsep->ep, 1);
+			hsudc->ep0state = WAIT_FOR_SETUP;
+			hsep->bEndpointAddress &= ~USB_DIR_IN;
+		}
+	}
+}
+
+/** s3c_hsudc_handle_ep0_intr - Handle endpoint 0 interrupt.
+ * @hsudc: Device controller on which endpoint 0 interrupt has occured.
+ *
+ * Handle endpoint 0 interrupt when it occurs. EP0 interrupt could occur
+ * when a stall handshake is sent to host or data is sent/received on
+ * endpoint 0.
+ */
+static void s3c_hsudc_handle_ep0_intr(struct s3c_hsudc *hsudc)
+{
+	struct s3c_hsudc_ep *hsep = &hsudc->ep[0];
+	struct s3c_hsudc_req *hsreq;
+	u32 csr = readl(hsudc->regs + S3C_EP0SR);
+	u32 ecr;
+
+	if (csr & S3C_EP0SR_STALL) {
+		ecr = readl(hsudc->regs + S3C_EP0CR);
+		ecr &= ~(S3C_ECR_STALL | S3C_ECR_FLUSH);
+		writel(ecr, hsudc->regs + S3C_EP0CR);
+
+		writel(S3C_EP0SR_STALL, hsudc->regs + S3C_EP0SR);
+		hsep->stopped = 0;
+
+		s3c_hsudc_nuke_ep(hsep, -ECONNABORTED);
+		hsudc->ep0state = WAIT_FOR_SETUP;
+		hsep->bEndpointAddress &= ~USB_DIR_IN;
+		return;
+	}
+
+	if (csr & S3C_EP0SR_TX_SUCCESS) {
+		writel(S3C_EP0SR_TX_SUCCESS, hsudc->regs + S3C_EP0SR);
+		if (ep_is_in(hsep)) {
+			if (list_empty(&hsep->queue))
+				return;
+
+			hsreq = list_entry(hsep->queue.next,
+					struct s3c_hsudc_req, queue);
+			s3c_hsudc_write_fifo(hsep, hsreq);
+		}
+	}
+
+	if (csr & S3C_EP0SR_RX_SUCCESS) {
+		if (hsudc->ep0state == WAIT_FOR_SETUP)
+			s3c_hsudc_process_setup(hsudc);
+		else {
+			if (!ep_is_in(hsep)) {
+				if (list_empty(&hsep->queue))
+					return;
+				hsreq = list_entry(hsep->queue.next,
+					struct s3c_hsudc_req, queue);
+				s3c_hsudc_read_fifo(hsep, hsreq);
+			}
+		}
+	}
+}
+
+/**
+ * s3c_hsudc_ep_enable - Enable a endpoint.
+ * @_ep: The endpoint to be enabled.
+ * @desc: Endpoint descriptor.
+ *
+ * Enables a endpoint when called from the gadget driver. Endpoint stall if
+ * any is cleared, transfer type is configured and endpoint interrupt is
+ * enabled.
+ */
+static int s3c_hsudc_ep_enable(struct usb_ep *_ep,
+				const struct usb_endpoint_descriptor *desc)
+{
+	struct s3c_hsudc_ep *hsep;
+	struct s3c_hsudc *hsudc;
+	unsigned long flags;
+	u32 ecr = 0;
+
+	hsep = container_of(_ep, struct s3c_hsudc_ep, ep);
+	if (!_ep || !desc || hsep->desc || _ep->name == ep0name
+		|| desc->bDescriptorType != USB_DT_ENDPOINT
+		|| hsep->bEndpointAddress != desc->bEndpointAddress
+		|| ep_maxpacket(hsep) < le16_to_cpu(desc->wMaxPacketSize))
+		return -EINVAL;
+
+	if ((desc->bmAttributes == USB_ENDPOINT_XFER_BULK
+		&& le16_to_cpu(desc->wMaxPacketSize) != ep_maxpacket(hsep))
+		|| !desc->wMaxPacketSize)
+		return -ERANGE;
+
+	hsudc = hsep->dev;
+	if (!hsudc->driver || hsudc->gadget.speed == USB_SPEED_UNKNOWN)
+		return -ESHUTDOWN;
+
+	spin_lock_irqsave(&hsudc->lock, flags);
+
+	set_index(hsudc, hsep->bEndpointAddress);
+	ecr |= ((usb_endpoint_xfer_int(desc)) ? S3C_ECR_IEMS : S3C_ECR_DUEN);
+	writel(ecr, hsudc->regs + S3C_ECR);
+
+	hsep->stopped = hsep->wedge = 0;
+	hsep->desc = desc;
+	hsep->ep.maxpacket = le16_to_cpu(desc->wMaxPacketSize);
+
+	s3c_hsudc_set_halt(_ep, 0);
+	__set_bit(ep_index(hsep), hsudc->regs + S3C_EIER);
+
+	spin_unlock_irqrestore(&hsudc->lock, flags);
+	return 0;
+}
+
+/**
+ * s3c_hsudc_ep_disable - Disable a endpoint.
+ * @_ep: The endpoint to be disabled.
+ * @desc: Endpoint descriptor.
+ *
+ * Disables a endpoint when called from the gadget driver.
+ */
+static int s3c_hsudc_ep_disable(struct usb_ep *_ep)
+{
+	struct s3c_hsudc_ep *hsep = our_ep(_ep);
+	struct s3c_hsudc *hsudc = hsep->dev;
+	unsigned long flags;
+
+	if (!_ep || !hsep->desc)
+		return -EINVAL;
+
+	spin_lock_irqsave(&hsudc->lock, flags);
+
+	set_index(hsudc, hsep->bEndpointAddress);
+	__clear_bit(ep_index(hsep), hsudc->regs + S3C_EIER);
+
+	s3c_hsudc_nuke_ep(hsep, -ESHUTDOWN);
+
+	hsep->desc = 0;
+	hsep->stopped = 1;
+
+	spin_unlock_irqrestore(&hsudc->lock, flags);
+	return 0;
+}
+
+/**
+ * s3c_hsudc_alloc_request - Allocate a new request.
+ * @_ep: Endpoint for which request is allocated (not used).
+ * @gfp_flags: Flags used for the allocation.
+ *
+ * Allocates a single transfer request structure when called from gadget driver.
+ */
+static struct usb_request *s3c_hsudc_alloc_request(struct usb_ep *_ep,
+						gfp_t gfp_flags)
+{
+	struct s3c_hsudc_req *hsreq;
+
+	hsreq = kzalloc(sizeof *hsreq, gfp_flags);
+	if (!hsreq)
+		return 0;
+
+	INIT_LIST_HEAD(&hsreq->queue);
+	return &hsreq->req;
+}
+
+/**
+ * s3c_hsudc_free_request - Deallocate a request.
+ * @ep: Endpoint for which request is deallocated (not used).
+ * @_req: Request to be deallocated.
+ *
+ * Allocates a single transfer request structure when called from gadget driver.
+ */
+static void s3c_hsudc_free_request(struct usb_ep *ep, struct usb_request *_req)
+{
+	struct s3c_hsudc_req *hsreq;
+
+	hsreq = container_of(_req, struct s3c_hsudc_req, req);
+	WARN_ON(!list_empty(&hsreq->queue));
+	kfree(hsreq);
+}
+
+/**
+ * s3c_hsudc_queue - Queue a transfer request for the endpoint.
+ * @_ep: Endpoint for which the request is queued.
+ * @_req: Request to be queued.
+ * @gfp_flags: Not used.
+ *
+ * Start or enqueue a request for a endpoint when called from gadget driver.
+ */
+static int s3c_hsudc_queue(struct usb_ep *_ep, struct usb_request *_req,
+			gfp_t gfp_flags)
+{
+	struct s3c_hsudc_req *hsreq;
+	struct s3c_hsudc_ep *hsep;
+	struct s3c_hsudc *hsudc;
+	unsigned long flags;
+	u32 offset;
+	u32 csr;
+
+	hsreq = container_of(_req, struct s3c_hsudc_req, req);
+	if ((!_req || !_req->complete || !_req->buf ||
+		!list_empty(&hsreq->queue)))
+		return -EINVAL;
+
+	hsep = container_of(_ep, struct s3c_hsudc_ep, ep);
+	hsudc = hsep->dev;
+	if (!hsudc->driver || hsudc->gadget.speed == USB_SPEED_UNKNOWN)
+		return -ESHUTDOWN;
+
+	spin_lock_irqsave(&hsudc->lock, flags);
+	set_index(hsudc, hsep->bEndpointAddress);
+
+	_req->status = -EINPROGRESS;
+	_req->actual = 0;
+
+	if (!ep_index(hsep) && _req->length == 0) {
+		hsudc->ep0state = WAIT_FOR_SETUP;
+		s3c_hsudc_complete_request(hsep, hsreq, 0);
+		spin_unlock_irqrestore(&hsudc->lock, flags);
+		return 0;
+	}
+
+	if (list_empty(&hsep->queue) && !hsep->stopped) {
+		offset = (ep_index(hsep)) ? S3C_ESR : S3C_EP0SR;
+		if (ep_is_in(hsep)) {
+			csr = readl((u32)hsudc->regs + offset);
+			if (!(csr & S3C_ESR_TX_SUCCESS) &&
+				(s3c_hsudc_write_fifo(hsep, hsreq) == 1))
+				hsreq = 0;
+		} else {
+			csr = readl((u32)hsudc->regs + offset);
+			if ((csr & S3C_ESR_RX_SUCCESS)
+				   && (s3c_hsudc_read_fifo(hsep, hsreq) == 1))
+				hsreq = 0;
+		}
+	}
+
+	if (hsreq != 0)
+		list_add_tail(&hsreq->queue, &hsep->queue);
+
+	spin_unlock_irqrestore(&hsudc->lock, flags);
+	return 0;
+}
+
+/**
+ * s3c_hsudc_dequeue - Dequeue a transfer request from an endpoint.
+ * @_ep: Endpoint from which the request is dequeued.
+ * @_req: Request to be dequeued.
+ *
+ * Dequeue a request from a endpoint when called from gadget driver.
+ */
+static int s3c_hsudc_dequeue(struct usb_ep *_ep, struct usb_request *_req)
+{
+	struct s3c_hsudc_ep *hsep = our_ep(_ep);
+	struct s3c_hsudc *hsudc = hsep->dev;
+	struct s3c_hsudc_req *hsreq;
+	unsigned long flags;
+
+	hsep = container_of(_ep, struct s3c_hsudc_ep, ep);
+	if (!_ep || hsep->ep.name == ep0name)
+		return -EINVAL;
+
+	spin_lock_irqsave(&hsudc->lock, flags);
+
+	list_for_each_entry(hsreq, &hsep->queue, queue) {
+		if (&hsreq->req == _req)
+			break;
+	}
+	if (&hsreq->req != _req) {
+		spin_unlock_irqrestore(&hsudc->lock, flags);
+		return -EINVAL;
+	}
+
+	set_index(hsudc, hsep->bEndpointAddress);
+	s3c_hsudc_complete_request(hsep, hsreq, -ECONNRESET);
+
+	spin_unlock_irqrestore(&hsudc->lock, flags);
+	return 0;
+}
+
+static struct usb_ep_ops s3c_hsudc_ep_ops = {
+	.enable = s3c_hsudc_ep_enable,
+	.disable = s3c_hsudc_ep_disable,
+	.alloc_request = s3c_hsudc_alloc_request,
+	.free_request = s3c_hsudc_free_request,
+	.queue = s3c_hsudc_queue,
+	.dequeue = s3c_hsudc_dequeue,
+	.set_halt = s3c_hsudc_set_halt,
+	.set_wedge = s3c_hsudc_set_wedge,
+};
+
+/**
+ * s3c_hsudc_initep - Initialize a endpoint to default state.
+ * @hsudc - Reference to the device controller.
+ * @hsep - Endpoint to be initialized.
+ * @epnum - Address to be assigned to the endpoint.
+ *
+ * Initialize a endpoint with default configuration.
+ */
+static void s3c_hsudc_initep(struct s3c_hsudc *hsudc,
+				struct s3c_hsudc_ep *hsep, int epnum)
+{
+	char *dir;
+
+	if ((epnum % 2) == 0) {
+		dir = "out";
+	} else {
+		dir = "in";
+		hsep->bEndpointAddress = USB_DIR_IN;
+	}
+
+	hsep->bEndpointAddress |= epnum;
+	if (epnum)
+		snprintf(hsep->name, sizeof(hsep->name), "ep%d%s", epnum, dir);
+	else
+		snprintf(hsep->name, sizeof(hsep->name), "%s", ep0name);
+
+	INIT_LIST_HEAD(&hsep->queue);
+	INIT_LIST_HEAD(&hsep->ep.ep_list);
+	if (epnum)
+		list_add_tail(&hsep->ep.ep_list, &hsudc->gadget.ep_list);
+
+	hsep->dev = hsudc;
+	hsep->ep.name = hsep->name;
+	hsep->ep.maxpacket = epnum ? 512 : 64;
+	hsep->ep.ops = &s3c_hsudc_ep_ops;
+	hsep->fifo = hsudc->regs + S3C_BR(epnum);
+	hsep->desc = 0;
+	hsep->stopped = 0;
+	hsep->wedge = 0;
+
+	set_index(hsudc, epnum);
+	writel(hsep->ep.maxpacket, hsudc->regs + S3C_MPR);
+}
+
+/**
+ * s3c_hsudc_setup_ep - Configure all endpoints to default state.
+ * @hsudc: Reference to device controller.
+ *
+ * Configures all endpoints to default state.
+ */
+static void s3c_hsudc_setup_ep(struct s3c_hsudc *hsudc)
+{
+	int epnum;
+
+	hsudc->ep0state = WAIT_FOR_SETUP;
+	INIT_LIST_HEAD(&hsudc->gadget.ep_list);
+	for (epnum = 0; epnum < hsudc->pd->epnum; epnum++)
+		s3c_hsudc_initep(hsudc, &hsudc->ep[epnum], epnum);
+}
+
+/**
+ * s3c_hsudc_reconfig - Reconfigure the device controller to default state.
+ * @hsudc: Reference to device controller.
+ *
+ * Reconfigures the device controller registers to a default state.
+ */
+static void s3c_hsudc_reconfig(struct s3c_hsudc *hsudc)
+{
+	writel(0xAA, hsudc->regs + S3C_EDR);
+	writel(1, hsudc->regs + S3C_EIER);
+	writel(0, hsudc->regs + S3C_TR);
+	writel(S3C_SCR_DTZIEN_EN | S3C_SCR_RRD_EN | S3C_SCR_SUS_EN |
+			S3C_SCR_RST_EN, hsudc->regs + S3C_SCR);
+	writel(0, hsudc->regs + S3C_EP0CR);
+
+	s3c_hsudc_setup_ep(hsudc);
+}
+
+/**
+ * s3c_hsudc_irq - Interrupt handler for device controller.
+ * @irq: Not used.
+ * @_dev: Reference to the device controller.
+ *
+ * Interrupt handler for the device controller. This handler handles controller
+ * interrupts and endpoint interrupts.
+ */
+static irqreturn_t s3c_hsudc_irq(int irq, void *_dev)
+{
+	struct s3c_hsudc *hsudc = _dev;
+	struct s3c_hsudc_ep *hsep;
+	u32 ep_intr;
+	u32 sys_status;
+	u32 ep_idx;
+
+	spin_lock(&hsudc->lock);
+
+	sys_status = readl(hsudc->regs + S3C_SSR);
+	ep_intr = readl(hsudc->regs + S3C_EIR) & 0x3FF;
+
+	if (!ep_intr && !(sys_status & S3C_SSR_DTZIEN_EN)) {
+		spin_unlock(&hsudc->lock);
+		return IRQ_HANDLED;
+	}
+
+	if (sys_status) {
+		if (sys_status & S3C_SSR_VBUSON)
+			writel(S3C_SSR_VBUSON, hsudc->regs + S3C_SSR);
+
+		if (sys_status & S3C_SSR_ERR)
+			writel(S3C_SSR_ERR, hsudc->regs + S3C_SSR);
+
+		if (sys_status & S3C_SSR_SDE) {
+			writel(S3C_SSR_SDE, hsudc->regs + S3C_SSR);
+			hsudc->gadget.speed = (sys_status & S3C_SSR_HSP) ?
+				USB_SPEED_HIGH : USB_SPEED_FULL;
+		}
+
+		if (sys_status & S3C_SSR_SUSPEND) {
+			writel(S3C_SSR_SUSPEND, hsudc->regs + S3C_SSR);
+			if (hsudc->gadget.speed != USB_SPEED_UNKNOWN
+				&& hsudc->driver && hsudc->driver->suspend)
+				hsudc->driver->suspend(&hsudc->gadget);
+		}
+
+		if (sys_status & S3C_SSR_RESUME) {
+			writel(S3C_SSR_RESUME, hsudc->regs + S3C_SSR);
+			if (hsudc->gadget.speed != USB_SPEED_UNKNOWN
+				&& hsudc->driver && hsudc->driver->resume)
+				hsudc->driver->resume(&hsudc->gadget);
+		}
+
+		if (sys_status & S3C_SSR_RESET) {
+			writel(S3C_SSR_RESET, hsudc->regs + S3C_SSR);
+			for (ep_idx = 0; ep_idx < hsudc->pd->epnum; ep_idx++) {
+				hsep = &hsudc->ep[ep_idx];
+				hsep->stopped = 1;
+				s3c_hsudc_nuke_ep(hsep, -ECONNRESET);
+			}
+			s3c_hsudc_reconfig(hsudc);
+			hsudc->ep0state = WAIT_FOR_SETUP;
+		}
+	}
+
+	if (ep_intr & S3C_EIR_EP0) {
+		writel(S3C_EIR_EP0, hsudc->regs + S3C_EIR);
+		set_index(hsudc, 0);
+		s3c_hsudc_handle_ep0_intr(hsudc);
+	}
+
+	ep_intr >>= 1;
+	ep_idx = 1;
+	while (ep_intr) {
+		if (ep_intr & 1)  {
+			hsep = &hsudc->ep[ep_idx];
+			set_index(hsudc, ep_idx);
+			writel(1 << ep_idx, hsudc->regs + S3C_EIR);
+			if (ep_is_in(hsep))
+				s3c_hsudc_epin_intr(hsudc, ep_idx);
+			else
+				s3c_hsudc_epout_intr(hsudc, ep_idx);
+		}
+		ep_intr >>= 1;
+		ep_idx++;
+	}
+
+	spin_unlock(&hsudc->lock);
+	return IRQ_HANDLED;
+}
+
+int usb_gadget_probe_driver(struct usb_gadget_driver *driver,
+               int (*bind)(struct usb_gadget *))
+{
+	struct s3c_hsudc *hsudc = the_controller;
+	int ret;
+
+	if (!driver
+		|| (driver->speed != USB_SPEED_FULL &&
+			driver->speed != USB_SPEED_HIGH)
+		|| !bind
+		|| !driver->unbind || !driver->disconnect || !driver->setup)
+		return -EINVAL;
+
+	if (!hsudc)
+		return -ENODEV;
+
+	if (hsudc->driver)
+		return -EBUSY;
+
+	hsudc->driver = driver;
+	hsudc->gadget.dev.driver = &driver->driver;
+	hsudc->gadget.speed = USB_SPEED_UNKNOWN;
+	ret = device_add(&hsudc->gadget.dev);
+	if (ret) {
+		dev_err(hsudc->dev, "failed to probe gadget device");
+		return ret;
+	}
+
+	ret = bind(&hsudc->gadget);
+	if (ret) {
+		dev_err(hsudc->dev, "%s: bind failed\n", hsudc->gadget.name);
+		device_del(&hsudc->gadget.dev);
+
+		hsudc->driver = NULL;
+		hsudc->gadget.dev.driver = NULL;
+		return ret;
+	}
+
+	enable_irq(hsudc->irq);
+	dev_info(hsudc->dev, "bound driver %s\n", driver->driver.name);
+
+	s3c_hsudc_reconfig(hsudc);
+	s3c_hsudc_init_phy();
+	if (hsudc->pd->gpio_init)
+		hsudc->pd->gpio_init();
+
+	return 0;
+}
+EXPORT_SYMBOL(usb_gadget_probe_driver);
+
+int usb_gadget_unregister_driver(struct usb_gadget_driver *driver)
+{
+	struct s3c_hsudc *hsudc = the_controller;
+	unsigned long flags;
+
+	if (!hsudc)
+		return -ENODEV;
+
+	if (!driver || driver != hsudc->driver || !driver->unbind)
+		return -EINVAL;
+
+	spin_lock_irqsave(&hsudc->lock, flags);
+	hsudc->driver = 0;
+	s3c_hsudc_uninit_phy();
+	if (hsudc->pd->gpio_uninit)
+		hsudc->pd->gpio_uninit();
+	s3c_hsudc_stop_activity(hsudc, driver);
+	spin_unlock_irqrestore(&hsudc->lock, flags);
+
+	driver->unbind(&hsudc->gadget);
+	device_del(&hsudc->gadget.dev);
+	disable_irq(hsudc->irq);
+
+	dev_info(hsudc->dev, "unregistered gadget driver '%s'\n",
+			driver->driver.name);
+	return 0;
+}
+EXPORT_SYMBOL(usb_gadget_unregister_driver);
+
+static inline u32 s3c_hsudc_read_frameno(struct s3c_hsudc *hsudc)
+{
+	return readl(hsudc->regs + S3C_FNR) & 0x3FF;
+}
+
+static int s3c_hsudc_gadget_getframe(struct usb_gadget *gadget)
+{
+	return s3c_hsudc_read_frameno(to_hsudc(gadget));
+}
+
+static struct usb_gadget_ops s3c_hsudc_gadget_ops = {
+	.get_frame	= s3c_hsudc_gadget_getframe,
+};
+
+static int s3c_hsudc_probe(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	struct resource *res;
+	struct s3c_hsudc *hsudc;
+	struct s3c24xx_hsudc_platdata *pd = pdev->dev.platform_data;
+	int ret;
+
+	hsudc = kzalloc(sizeof(struct s3c_hsudc) +
+			sizeof(struct s3c_hsudc_ep) * pd->epnum,
+			GFP_KERNEL);
+	if (!hsudc) {
+		dev_err(dev, "cannot allocate memory\n");
+		return -ENOMEM;
+	}
+
+	the_controller = hsudc;
+	platform_set_drvdata(pdev, dev);
+	hsudc->dev = dev;
+	hsudc->pd = pdev->dev.platform_data;
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!res) {
+		dev_err(dev, "unable to obtain driver resource data\n");
+		ret = -ENODEV;
+		goto err_res;
+	}
+
+	hsudc->mem_rsrc = request_mem_region(res->start, resource_size(res),
+				dev_name(&pdev->dev));
+	if (!hsudc->mem_rsrc) {
+		dev_err(dev, "failed to reserve register area\n");
+		ret = -ENODEV;
+		goto err_res;
+	}
+
+	hsudc->regs = ioremap(res->start, resource_size(res));
+	if (!hsudc->regs) {
+		dev_err(dev, "error mapping device register area\n");
+		ret = -EBUSY;
+		goto err_remap;
+	}
+
+	ret = platform_get_irq(pdev, 0);
+	if (ret < 0) {
+		dev_err(dev, "unable to obtain IRQ number\n");
+		goto err_irq;
+	}
+	hsudc->irq = ret;
+
+	ret = request_irq(hsudc->irq, s3c_hsudc_irq, 0, driver_name, hsudc);
+	if (ret < 0) {
+		dev_err(dev, "irq request failed\n");
+		goto err_irq;
+	}
+
+	spin_lock_init(&hsudc->lock);
+
+	device_initialize(&hsudc->gadget.dev);
+	dev_set_name(&hsudc->gadget.dev, "gadget");
+
+	hsudc->gadget.is_dualspeed = 1;
+	hsudc->gadget.ops = &s3c_hsudc_gadget_ops;
+	hsudc->gadget.name = dev_name(dev);
+	hsudc->gadget.dev.parent = dev;
+	hsudc->gadget.dev.dma_mask = dev->dma_mask;
+	hsudc->gadget.ep0 = &hsudc->ep[0].ep;
+
+	hsudc->gadget.is_otg = 0;
+	hsudc->gadget.is_a_peripheral = 0;
+
+	s3c_hsudc_setup_ep(hsudc);
+
+	hsudc->uclk = clk_get(&pdev->dev, "usb-device");
+	if (hsudc->uclk == NULL) {
+		dev_err(dev, "failed to find usb-device clock source\n");
+		return -ENOENT;
+	}
+	clk_enable(hsudc->uclk);
+
+	local_irq_disable();
+
+	disable_irq(hsudc->irq);
+	local_irq_enable();
+	return 0;
+
+err_irq:
+	iounmap(hsudc->regs);
+
+err_remap:
+	release_resource(hsudc->mem_rsrc);
+	kfree(hsudc->mem_rsrc);
+
+err_res:
+	kfree(hsudc);
+	return ret;
+}
+
+static struct platform_driver s3c_hsudc_driver = {
+	.driver		= {
+		.owner	= THIS_MODULE,
+		.name	= "s3c-hsudc",
+	},
+	.probe		= s3c_hsudc_probe,
+};
+
+static int __init s3c_hsudc_modinit(void)
+{
+	return platform_driver_register(&s3c_hsudc_driver);
+}
+
+static void __exit s3c_hsudc_modexit(void)
+{
+	platform_driver_unregister(&s3c_hsudc_driver);
+}
+
+module_init(s3c_hsudc_modinit);
+module_exit(s3c_hsudc_modexit);
+
+MODULE_DESCRIPTION("Samsung S3C24XX USB high-speed controller driver");
+MODULE_AUTHOR("Thomas Abraham <thomas.ab@samsung.com>");
+MODULE_LICENSE("GPL");
-- 
1.7.2.3

^ permalink raw reply related	[flat|nested] 52+ messages in thread

* [PATCH v5 3/4] USB: Gadget: Add Samsung S3C24XX USB High-Speed controller driver
@ 2011-05-07 20:28                       ` Heiko Stübner
  0 siblings, 0 replies; 52+ messages in thread
From: Heiko Stübner @ 2011-05-07 20:28 UTC (permalink / raw)
  To: linux-arm-kernel

From: Thomas Abraham <thomas.ab@samsung.com>

The Samsung's S3C2416, S3C2443 and S3C2450 includes a USB High-Speed
device controller module. This driver enables support for USB high-speed
gadget functionality for the Samsung S3C24xx SoC's that include this
controller.

Signed-off-by: Thomas Abraham <thomas.ab@samsung.com>
Signed-off-by: Sangbeom Kim <sbkim73@samsung.com>
Signed-off-by: Kukjin Kim <kgene.kim@samsung.com>
Signed-off-by: Alexander Neumann <alexander@bumpern.de>
Signed-off-by: Heiko Stuebner <heiko@sntech.de>
---
Changes since v1:
- Addressed comments from Ben Dooks
  Thomas Abraham <thomas.ab@samsung.com>

Changes since v2:
- Update the driver to match the data structure changes introduced by
  commit b0fca50f5a94a268ed02cfddf44448051ed9343f.
  Alexander Neumann <alexander@debian.org>
- Move s3c_hsudc to 0x29 in usb_gadget_controller_number in gadget_chips.h
  Heiko Stuebner <heiko@sntech.de>

Changes since v3:
- Update gadget_chips.h-id again
  Heiko Stuebner <heiko@sntech.de>

Changes since v4:
- Add set_wedge function
  Thomas Abraham <thomas.ab@samsung.com>

 drivers/usb/gadget/Kconfig        |   17 +
 drivers/usb/gadget/Makefile       |    1 +
 drivers/usb/gadget/gadget_chips.h |    8 +
 drivers/usb/gadget/s3c-hsudc.c    | 1349 +++++++++++++++++++++++++++++++++++++
 4 files changed, 1375 insertions(+), 0 deletions(-)
 create mode 100644 drivers/usb/gadget/s3c-hsudc.c

diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig
index 4c02b9f..58456d1 100644
--- a/drivers/usb/gadget/Kconfig
+++ b/drivers/usb/gadget/Kconfig
@@ -356,6 +356,23 @@ config USB_S3C2410_DEBUG
 	boolean "S3C2410 udc debug messages"
 	depends on USB_GADGET_S3C2410
 
+config USB_GADGET_S3C_HSUDC
+	boolean "S3C2416, S3C2443 and S3C2450 USB Device Controller"
+	depends on ARCH_S3C2410
+	select USB_GADGET_DUALSPEED
+	help
+	  Samsung's S3C2416, S3C2443 and S3C2450 is an ARM9 based SoC
+	  integrated with dual speed USB 2.0 device controller. It has
+	  8 endpoints, as well as endpoint zero.
+
+	  This driver has been tested on S3C2416 and S3C2450 processors.
+
+config USB_S3C_HSUDC
+	tristate
+	depends on USB_GADGET_S3C_HSUDC
+	default USB_GADGET
+	select USB_GADGET_SELECTED
+
 config USB_GADGET_PXA_U2O
 	boolean "PXA9xx Processor USB2.0 controller"
 	select USB_GADGET_DUALSPEED
diff --git a/drivers/usb/gadget/Makefile b/drivers/usb/gadget/Makefile
index 1ea15ee..4fe92b1 100644
--- a/drivers/usb/gadget/Makefile
+++ b/drivers/usb/gadget/Makefile
@@ -22,6 +22,7 @@ obj-$(CONFIG_USB_R8A66597)	+= r8a66597-udc.o
 obj-$(CONFIG_USB_FSL_QE)	+= fsl_qe_udc.o
 obj-$(CONFIG_USB_CI13XXX_PCI)	+= ci13xxx_pci.o
 obj-$(CONFIG_USB_S3C_HSOTG)	+= s3c-hsotg.o
+obj-$(CONFIG_USB_S3C_HSUDC)	+= s3c-hsudc.o
 obj-$(CONFIG_USB_LANGWELL)	+= langwell_udc.o
 obj-$(CONFIG_USB_EG20T)		+= pch_udc.o
 obj-$(CONFIG_USB_PXA_U2O)	+= mv_udc.o
diff --git a/drivers/usb/gadget/gadget_chips.h b/drivers/usb/gadget/gadget_chips.h
index ec3fd97..bcdac7c 100644
--- a/drivers/usb/gadget/gadget_chips.h
+++ b/drivers/usb/gadget/gadget_chips.h
@@ -136,6 +136,12 @@
 #define gadget_is_s3c_hsotg(g)    0
 #endif
 
+#ifdef CONFIG_USB_S3C_HSUDC
+#define gadget_is_s3c_hsudc(g) (!strcmp("s3c-hsudc", (g)->name))
+#else
+#define gadget_is_s3c_hsudc(g) 0
+#endif
+
 #ifdef CONFIG_USB_GADGET_EG20T
 #define	gadget_is_pch(g)	(!strcmp("pch_udc", (g)->name))
 #else
@@ -215,6 +221,8 @@ static inline int usb_gadget_controller_number(struct usb_gadget *gadget)
 		return 0x28;
 	else if (gadget_is_renesas_usbhs(gadget))
 		return 0x29;
+	else if (gadget_is_s3c_hsudc(gadget))
+		return 0x30;
 
 	return -ENOENT;
 }
diff --git a/drivers/usb/gadget/s3c-hsudc.c b/drivers/usb/gadget/s3c-hsudc.c
new file mode 100644
index 0000000..c509487
--- /dev/null
+++ b/drivers/usb/gadget/s3c-hsudc.c
@@ -0,0 +1,1349 @@
+/* linux/drivers/usb/gadget/s3c-hsudc.c
+ *
+ * Copyright (c) 2010 Samsung Electronics Co., Ltd.
+ *		http://www.samsung.com/
+ *
+ * S3C24XX USB 2.0 High-speed USB controller gadget driver
+ *
+ * The S3C24XX USB 2.0 high-speed USB controller supports upto 9 endpoints.
+ * Each endpoint can be configured as either in or out endpoint. Endpoints
+ * can be configured for Bulk or Interrupt transfer mode.
+ *
+ * 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.
+*/
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/spinlock.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <linux/dma-mapping.h>
+#include <linux/delay.h>
+#include <linux/io.h>
+#include <linux/slab.h>
+#include <linux/clk.h>
+#include <linux/usb/ch9.h>
+#include <linux/usb/gadget.h>
+
+#include <mach/regs-s3c2443-clock.h>
+#include <plat/udc.h>
+
+#define S3C_HSUDC_REG(x)	(x)
+
+/* Non-Indexed Registers */
+#define S3C_IR				S3C_HSUDC_REG(0x00) /* Index Register */
+#define S3C_EIR				S3C_HSUDC_REG(0x04) /* EP Intr Status */
+#define S3C_EIR_EP0			(1<<0)
+#define S3C_EIER			S3C_HSUDC_REG(0x08) /* EP Intr Enable */
+#define S3C_FAR				S3C_HSUDC_REG(0x0c) /* Gadget Address */
+#define S3C_FNR				S3C_HSUDC_REG(0x10) /* Frame Number */
+#define S3C_EDR				S3C_HSUDC_REG(0x14) /* EP Direction */
+#define S3C_TR				S3C_HSUDC_REG(0x18) /* Test Register */
+#define S3C_SSR				S3C_HSUDC_REG(0x1c) /* System Status */
+#define S3C_SSR_DTZIEN_EN		(0xff8f)
+#define S3C_SSR_ERR			(0xff80)
+#define S3C_SSR_VBUSON			(1 << 8)
+#define S3C_SSR_HSP			(1 << 4)
+#define S3C_SSR_SDE			(1 << 3)
+#define S3C_SSR_RESUME			(1 << 2)
+#define S3C_SSR_SUSPEND			(1 << 1)
+#define S3C_SSR_RESET			(1 << 0)
+#define S3C_SCR				S3C_HSUDC_REG(0x20) /* System Control */
+#define S3C_SCR_DTZIEN_EN		(1 << 14)
+#define S3C_SCR_RRD_EN			(1 << 5)
+#define S3C_SCR_SUS_EN			(1 << 1)
+#define S3C_SCR_RST_EN			(1 << 0)
+#define S3C_EP0SR			S3C_HSUDC_REG(0x24) /* EP0 Status */
+#define S3C_EP0SR_EP0_LWO		(1 << 6)
+#define S3C_EP0SR_STALL			(1 << 4)
+#define S3C_EP0SR_TX_SUCCESS		(1 << 1)
+#define S3C_EP0SR_RX_SUCCESS		(1 << 0)
+#define S3C_EP0CR			S3C_HSUDC_REG(0x28) /* EP0 Control */
+#define S3C_BR(_x)			S3C_HSUDC_REG(0x60 + (_x * 4))
+
+/* Indexed Registers */
+#define S3C_ESR				S3C_HSUDC_REG(0x2c) /* EPn Status */
+#define S3C_ESR_FLUSH			(1 << 6)
+#define S3C_ESR_STALL			(1 << 5)
+#define S3C_ESR_LWO			(1 << 4)
+#define S3C_ESR_PSIF_ONE		(1 << 2)
+#define S3C_ESR_PSIF_TWO		(2 << 2)
+#define S3C_ESR_TX_SUCCESS		(1 << 1)
+#define S3C_ESR_RX_SUCCESS		(1 << 0)
+#define S3C_ECR				S3C_HSUDC_REG(0x30) /* EPn Control */
+#define S3C_ECR_DUEN			(1 << 7)
+#define S3C_ECR_FLUSH			(1 << 6)
+#define S3C_ECR_STALL			(1 << 1)
+#define S3C_ECR_IEMS			(1 << 0)
+#define S3C_BRCR			S3C_HSUDC_REG(0x34) /* Read Count */
+#define S3C_BWCR			S3C_HSUDC_REG(0x38) /* Write Count */
+#define S3C_MPR				S3C_HSUDC_REG(0x3c) /* Max Pkt Size */
+
+#define WAIT_FOR_SETUP			(0)
+#define DATA_STATE_XMIT			(1)
+#define DATA_STATE_RECV			(2)
+
+/**
+ * struct s3c_hsudc_ep - Endpoint representation used by driver.
+ * @ep: USB gadget layer representation of device endpoint.
+ * @name: Endpoint name (as required by ep autoconfiguration).
+ * @dev: Reference to the device controller to which this EP belongs.
+ * @desc: Endpoint descriptor obtained from the gadget driver.
+ * @queue: Transfer request queue for the endpoint.
+ * @stopped: Maintains state of endpoint, set if EP is halted.
+ * @bEndpointAddress: EP address (including direction bit).
+ * @fifo: Base address of EP FIFO.
+ */
+struct s3c_hsudc_ep {
+	struct usb_ep ep;
+	char name[20];
+	struct s3c_hsudc *dev;
+	const struct usb_endpoint_descriptor *desc;
+	struct list_head queue;
+	u8 stopped;
+	u8 wedge;
+	u8 bEndpointAddress;
+	void __iomem *fifo;
+};
+
+/**
+ * struct s3c_hsudc_req - Driver encapsulation of USB gadget transfer request.
+ * @req: Reference to USB gadget transfer request.
+ * @queue: Used for inserting this request to the endpoint request queue.
+ */
+struct s3c_hsudc_req {
+	struct usb_request req;
+	struct list_head queue;
+};
+
+/**
+ * struct s3c_hsudc - Driver's abstraction of the device controller.
+ * @gadget: Instance of usb_gadget which is referenced by gadget driver.
+ * @driver: Reference to currenty active gadget driver.
+ * @dev: The device reference used by probe function.
+ * @lock: Lock to synchronize the usage of Endpoints (EP's are indexed).
+ * @regs: Remapped base address of controller's register space.
+ * @mem_rsrc: Device memory resource used for remapping device register space.
+ * irq: IRQ number used by the controller.
+ * uclk: Reference to the controller clock.
+ * ep0state: Current state of EP0.
+ * ep: List of endpoints supported by the controller.
+ */
+struct s3c_hsudc {
+	struct usb_gadget gadget;
+	struct usb_gadget_driver *driver;
+	struct device *dev;
+	struct s3c24xx_hsudc_platdata *pd;
+	spinlock_t lock;
+	void __iomem *regs;
+	struct resource *mem_rsrc;
+	int irq;
+	struct clk *uclk;
+	int ep0state;
+	struct s3c_hsudc_ep ep[];
+};
+
+#define ep_maxpacket(_ep)	((_ep)->ep.maxpacket)
+#define ep_is_in(_ep)		((_ep)->bEndpointAddress & USB_DIR_IN)
+#define ep_index(_ep)		((_ep)->bEndpointAddress & \
+					USB_ENDPOINT_NUMBER_MASK)
+
+static struct s3c_hsudc *the_controller;
+static const char driver_name[] = "s3c-udc";
+static const char ep0name[] = "ep0-control";
+
+static inline struct s3c_hsudc_req *our_req(struct usb_request *req)
+{
+	return container_of(req, struct s3c_hsudc_req, req);
+}
+
+static inline struct s3c_hsudc_ep *our_ep(struct usb_ep *ep)
+{
+	return container_of(ep, struct s3c_hsudc_ep, ep);
+}
+
+static inline struct s3c_hsudc *to_hsudc(struct usb_gadget *gadget)
+{
+	return container_of(gadget, struct s3c_hsudc, gadget);
+}
+
+static inline void set_index(struct s3c_hsudc *hsudc, int ep_addr)
+{
+	ep_addr &= USB_ENDPOINT_NUMBER_MASK;
+	writel(ep_addr, hsudc->regs + S3C_IR);
+}
+
+static inline void __orr32(void __iomem *ptr, u32 val)
+{
+	writel(readl(ptr) | val, ptr);
+}
+
+static void s3c_hsudc_init_phy(void)
+{
+	u32 cfg;
+
+	cfg = readl(S3C2443_PWRCFG) | S3C2443_PWRCFG_USBPHY;
+	writel(cfg, S3C2443_PWRCFG);
+
+	cfg = readl(S3C2443_URSTCON);
+	cfg |= (S3C2443_URSTCON_FUNCRST | S3C2443_URSTCON_PHYRST);
+	writel(cfg, S3C2443_URSTCON);
+	mdelay(1);
+
+	cfg = readl(S3C2443_URSTCON);
+	cfg &= ~(S3C2443_URSTCON_FUNCRST | S3C2443_URSTCON_PHYRST);
+	writel(cfg, S3C2443_URSTCON);
+
+	cfg = readl(S3C2443_PHYCTRL);
+	cfg &= ~(S3C2443_PHYCTRL_CLKSEL | S3C2443_PHYCTRL_DSPORT);
+	cfg |= (S3C2443_PHYCTRL_EXTCLK | S3C2443_PHYCTRL_PLLSEL);
+	writel(cfg, S3C2443_PHYCTRL);
+
+	cfg = readl(S3C2443_PHYPWR);
+	cfg &= ~(S3C2443_PHYPWR_FSUSPEND | S3C2443_PHYPWR_PLL_PWRDN |
+		S3C2443_PHYPWR_XO_ON | S3C2443_PHYPWR_PLL_REFCLK |
+		S3C2443_PHYPWR_ANALOG_PD);
+	cfg |= S3C2443_PHYPWR_COMMON_ON;
+	writel(cfg, S3C2443_PHYPWR);
+
+	cfg = readl(S3C2443_UCLKCON);
+	cfg |= (S3C2443_UCLKCON_DETECT_VBUS | S3C2443_UCLKCON_FUNC_CLKEN |
+		S3C2443_UCLKCON_TCLKEN);
+	writel(cfg, S3C2443_UCLKCON);
+}
+
+static void s3c_hsudc_uninit_phy(void)
+{
+	u32 cfg;
+
+	cfg = readl(S3C2443_PWRCFG) & ~S3C2443_PWRCFG_USBPHY;
+	writel(cfg, S3C2443_PWRCFG);
+
+	writel(S3C2443_PHYPWR_FSUSPEND, S3C2443_PHYPWR);
+
+	cfg = readl(S3C2443_UCLKCON) & ~S3C2443_UCLKCON_FUNC_CLKEN;
+	writel(cfg, S3C2443_UCLKCON);
+}
+
+/**
+ * s3c_hsudc_complete_request - Complete a transfer request.
+ * @hsep: Endpoint to which the request belongs.
+ * @hsreq: Transfer request to be completed.
+ * @status: Transfer completion status for the transfer request.
+ */
+static void s3c_hsudc_complete_request(struct s3c_hsudc_ep *hsep,
+				struct s3c_hsudc_req *hsreq, int status)
+{
+	unsigned int stopped = hsep->stopped;
+	struct s3c_hsudc *hsudc = hsep->dev;
+
+	list_del_init(&hsreq->queue);
+	hsreq->req.status = status;
+
+	if (!ep_index(hsep)) {
+		hsudc->ep0state = WAIT_FOR_SETUP;
+		hsep->bEndpointAddress &= ~USB_DIR_IN;
+	}
+
+	hsep->stopped = 1;
+	spin_unlock(&hsudc->lock);
+	if (hsreq->req.complete != NULL)
+		hsreq->req.complete(&hsep->ep, &hsreq->req);
+	spin_lock(&hsudc->lock);
+	hsep->stopped = stopped;
+}
+
+/**
+ * s3c_hsudc_nuke_ep - Terminate all requests queued for a endpoint.
+ * @hsep: Endpoint for which queued requests have to be terminated.
+ * @status: Transfer completion status for the transfer request.
+ */
+static void s3c_hsudc_nuke_ep(struct s3c_hsudc_ep *hsep, int status)
+{
+	struct s3c_hsudc_req *hsreq;
+
+	while (!list_empty(&hsep->queue)) {
+		hsreq = list_entry(hsep->queue.next,
+				struct s3c_hsudc_req, queue);
+		s3c_hsudc_complete_request(hsep, hsreq, status);
+	}
+}
+
+/**
+ * s3c_hsudc_stop_activity - Stop activity on all endpoints.
+ * @hsudc: Device controller for which EP activity is to be stopped.
+ * @driver: Reference to the gadget driver which is currently active.
+ *
+ * All the endpoints are stopped and any pending transfer requests if any on
+ * the endpoint are terminated.
+ */
+static void s3c_hsudc_stop_activity(struct s3c_hsudc *hsudc,
+			  struct usb_gadget_driver *driver)
+{
+	struct s3c_hsudc_ep *hsep;
+	int epnum;
+
+	hsudc->gadget.speed = USB_SPEED_UNKNOWN;
+
+	for (epnum = 0; epnum < hsudc->pd->epnum; epnum++) {
+		hsep = &hsudc->ep[epnum];
+		hsep->stopped = 1;
+		s3c_hsudc_nuke_ep(hsep, -ESHUTDOWN);
+	}
+
+	spin_unlock(&hsudc->lock);
+	driver->disconnect(&hsudc->gadget);
+	spin_lock(&hsudc->lock);
+}
+
+/**
+ * s3c_hsudc_read_setup_pkt - Read the received setup packet from EP0 fifo.
+ * @hsudc: Device controller from which setup packet is to be read.
+ * @buf: The buffer into which the setup packet is read.
+ *
+ * The setup packet received in the EP0 fifo is read and stored into a
+ * given buffer address.
+ */
+
+static void s3c_hsudc_read_setup_pkt(struct s3c_hsudc *hsudc, u16 *buf)
+{
+	int count;
+
+	count = readl(hsudc->regs + S3C_BRCR);
+	while (count--)
+		*buf++ = (u16)readl(hsudc->regs + S3C_BR(0));
+
+	writel(S3C_EP0SR_RX_SUCCESS, hsudc->regs + S3C_EP0SR);
+}
+
+/**
+ * s3c_hsudc_write_fifo - Write next chunk of transfer data to EP fifo.
+ * @hsep: Endpoint to which the data is to be written.
+ * @hsreq: Transfer request from which the next chunk of data is written.
+ *
+ * Write the next chunk of data from a transfer request to the endpoint FIFO.
+ * If the transfer request completes, 1 is returned, otherwise 0 is returned.
+ */
+static int s3c_hsudc_write_fifo(struct s3c_hsudc_ep *hsep,
+				struct s3c_hsudc_req *hsreq)
+{
+	u16 *buf;
+	u32 max = ep_maxpacket(hsep);
+	u32 count, length;
+	bool is_last;
+	void __iomem *fifo = hsep->fifo;
+
+	buf = hsreq->req.buf + hsreq->req.actual;
+	prefetch(buf);
+
+	length = hsreq->req.length - hsreq->req.actual;
+	length = min(length, max);
+	hsreq->req.actual += length;
+
+	writel(length, hsep->dev->regs + S3C_BWCR);
+	for (count = 0; count < length; count += 2)
+		writel(*buf++, fifo);
+
+	if (count != max) {
+		is_last = true;
+	} else {
+		if (hsreq->req.length != hsreq->req.actual || hsreq->req.zero)
+			is_last = false;
+		else
+			is_last = true;
+	}
+
+	if (is_last) {
+		s3c_hsudc_complete_request(hsep, hsreq, 0);
+		return 1;
+	}
+
+	return 0;
+}
+
+/**
+ * s3c_hsudc_read_fifo - Read the next chunk of data from EP fifo.
+ * @hsep: Endpoint from which the data is to be read.
+ * @hsreq: Transfer request to which the next chunk of data read is written.
+ *
+ * Read the next chunk of data from the endpoint FIFO and a write it to the
+ * transfer request buffer. If the transfer request completes, 1 is returned,
+ * otherwise 0 is returned.
+ */
+static int s3c_hsudc_read_fifo(struct s3c_hsudc_ep *hsep,
+				struct s3c_hsudc_req *hsreq)
+{
+	struct s3c_hsudc *hsudc = hsep->dev;
+	u32 csr, offset;
+	u16 *buf, word;
+	u32 buflen, rcnt, rlen;
+	void __iomem *fifo = hsep->fifo;
+	u32 is_short = 0;
+
+	offset = (ep_index(hsep)) ? S3C_ESR : S3C_EP0SR;
+	csr = readl(hsudc->regs + offset);
+	if (!(csr & S3C_ESR_RX_SUCCESS))
+		return -EINVAL;
+
+	buf = hsreq->req.buf + hsreq->req.actual;
+	prefetchw(buf);
+	buflen = hsreq->req.length - hsreq->req.actual;
+
+	rcnt = readl(hsudc->regs + S3C_BRCR);
+	rlen = (csr & S3C_ESR_LWO) ? (rcnt * 2 - 1) : (rcnt * 2);
+
+	hsreq->req.actual += min(rlen, buflen);
+	is_short = (rlen < hsep->ep.maxpacket);
+
+	while (rcnt-- != 0) {
+		word = (u16)readl(fifo);
+		if (buflen) {
+			*buf++ = word;
+			buflen--;
+		} else {
+			hsreq->req.status = -EOVERFLOW;
+		}
+	}
+
+	writel(S3C_ESR_RX_SUCCESS, hsudc->regs + offset);
+
+	if (is_short || hsreq->req.actual == hsreq->req.length) {
+		s3c_hsudc_complete_request(hsep, hsreq, 0);
+		return 1;
+	}
+
+	return 0;
+}
+
+/**
+ * s3c_hsudc_epin_intr - Handle in-endpoint interrupt.
+ * @hsudc - Device controller for which the interrupt is to be handled.
+ * @ep_idx - Endpoint number on which an interrupt is pending.
+ *
+ * Handles interrupt for a in-endpoint. The interrupts that are handled are
+ * stall and data transmit complete interrupt.
+ */
+static void s3c_hsudc_epin_intr(struct s3c_hsudc *hsudc, u32 ep_idx)
+{
+	struct s3c_hsudc_ep *hsep = &hsudc->ep[ep_idx];
+	struct s3c_hsudc_req *hsreq;
+	u32 csr;
+
+	csr = readl((u32)hsudc->regs + S3C_ESR);
+	if (csr & S3C_ESR_STALL) {
+		writel(S3C_ESR_STALL, hsudc->regs + S3C_ESR);
+		return;
+	}
+
+	if (csr & S3C_ESR_TX_SUCCESS) {
+		writel(S3C_ESR_TX_SUCCESS, hsudc->regs + S3C_ESR);
+		if (list_empty(&hsep->queue))
+			return;
+
+		hsreq = list_entry(hsep->queue.next,
+				struct s3c_hsudc_req, queue);
+		if ((s3c_hsudc_write_fifo(hsep, hsreq) == 0) &&
+				(csr & S3C_ESR_PSIF_TWO))
+			s3c_hsudc_write_fifo(hsep, hsreq);
+	}
+}
+
+/**
+ * s3c_hsudc_epout_intr - Handle out-endpoint interrupt.
+ * @hsudc - Device controller for which the interrupt is to be handled.
+ * @ep_idx - Endpoint number on which an interrupt is pending.
+ *
+ * Handles interrupt for a out-endpoint. The interrupts that are handled are
+ * stall, flush and data ready interrupt.
+ */
+static void s3c_hsudc_epout_intr(struct s3c_hsudc *hsudc, u32 ep_idx)
+{
+	struct s3c_hsudc_ep *hsep = &hsudc->ep[ep_idx];
+	struct s3c_hsudc_req *hsreq;
+	u32 csr;
+
+	csr = readl((u32)hsudc->regs + S3C_ESR);
+	if (csr & S3C_ESR_STALL) {
+		writel(S3C_ESR_STALL, hsudc->regs + S3C_ESR);
+		return;
+	}
+
+	if (csr & S3C_ESR_FLUSH) {
+		__orr32(hsudc->regs + S3C_ECR, S3C_ECR_FLUSH);
+		return;
+	}
+
+	if (csr & S3C_ESR_RX_SUCCESS) {
+		if (list_empty(&hsep->queue))
+			return;
+
+		hsreq = list_entry(hsep->queue.next,
+				struct s3c_hsudc_req, queue);
+		if (((s3c_hsudc_read_fifo(hsep, hsreq)) == 0) &&
+				(csr & S3C_ESR_PSIF_TWO))
+			s3c_hsudc_read_fifo(hsep, hsreq);
+	}
+}
+
+/** s3c_hsudc_set_halt - Set or clear a endpoint halt.
+ * @_ep: Endpoint on which halt has to be set or cleared.
+ * @value: 1 for setting halt on endpoint, 0 to clear halt.
+ *
+ * Set or clear endpoint halt. If halt is set, the endpoint is stopped.
+ * If halt is cleared, for in-endpoints, if there are any pending
+ * transfer requests, transfers are started.
+ */
+static int s3c_hsudc_set_halt(struct usb_ep *_ep, int value)
+{
+	struct s3c_hsudc_ep *hsep = our_ep(_ep);
+	struct s3c_hsudc *hsudc = hsep->dev;
+	struct s3c_hsudc_req *hsreq;
+	unsigned long irqflags;
+	u32 ecr;
+	u32 offset;
+
+	if (value && ep_is_in(hsep) && !list_empty(&hsep->queue))
+		return -EAGAIN;
+
+	spin_lock_irqsave(&hsudc->lock, irqflags);
+	set_index(hsudc, ep_index(hsep));
+	offset = (ep_index(hsep)) ? S3C_ECR : S3C_EP0CR;
+	ecr = readl(hsudc->regs + offset);
+
+	if (value) {
+		ecr |= S3C_ECR_STALL;
+		if (ep_index(hsep))
+			ecr |= S3C_ECR_FLUSH;
+		hsep->stopped = 1;
+	} else {
+		ecr &= ~S3C_ECR_STALL;
+		hsep->stopped = hsep->wedge = 0;
+	}
+	writel(ecr, hsudc->regs + offset);
+
+	if (ep_is_in(hsep) && !list_empty(&hsep->queue) && !value) {
+		hsreq = list_entry(hsep->queue.next,
+			struct s3c_hsudc_req, queue);
+		if (hsreq)
+			s3c_hsudc_write_fifo(hsep, hsreq);
+	}
+
+	spin_unlock_irqrestore(&hsudc->lock, irqflags);
+	return 0;
+}
+
+/** s3c_hsudc_set_wedge - Sets the halt feature with the clear requests ignored
+ * @_ep: Endpoint on which wedge has to be set.
+ *
+ * Sets the halt feature with the clear requests ignored.
+ */
+static int s3c_hsudc_set_wedge(struct usb_ep *_ep)
+{
+	struct s3c_hsudc_ep *hsep = our_ep(_ep);
+
+	if (!hsep)
+		return -EINVAL;
+
+	hsep->wedge = 1;
+	return usb_ep_set_halt(_ep);
+}
+
+/** s3c_hsudc_handle_reqfeat - Handle set feature or clear feature requests.
+ * @_ep: Device controller on which the set/clear feature needs to be handled.
+ * @ctrl: Control request as received on the endpoint 0.
+ *
+ * Handle set feature or clear feature control requests on the control endpoint.
+ */
+static int s3c_hsudc_handle_reqfeat(struct s3c_hsudc *hsudc,
+					struct usb_ctrlrequest *ctrl)
+{
+	struct s3c_hsudc_ep *hsep;
+	bool set = (ctrl->bRequest == USB_REQ_SET_FEATURE);
+	u8 ep_num = ctrl->wIndex & USB_ENDPOINT_NUMBER_MASK;
+
+	if (ctrl->bRequestType == USB_RECIP_ENDPOINT) {
+		hsep = &hsudc->ep[ep_num];
+		switch (le16_to_cpu(ctrl->wValue)) {
+		case USB_ENDPOINT_HALT:
+			if (set || (!set && !hsep->wedge))
+				s3c_hsudc_set_halt(&hsep->ep, set);
+			return 0;
+		}
+	}
+
+	return -ENOENT;
+}
+
+/**
+ * s3c_hsudc_process_req_status - Handle get status control request.
+ * @hsudc: Device controller on which get status request has be handled.
+ * @ctrl: Control request as received on the endpoint 0.
+ *
+ * Handle get status control request received on control endpoint.
+ */
+static void s3c_hsudc_process_req_status(struct s3c_hsudc *hsudc,
+					struct usb_ctrlrequest *ctrl)
+{
+	struct s3c_hsudc_ep *hsep0 = &hsudc->ep[0];
+	struct s3c_hsudc_req hsreq;
+	struct s3c_hsudc_ep *hsep;
+	__le16 reply;
+	u8 epnum;
+
+	switch (ctrl->bRequestType & USB_RECIP_MASK) {
+	case USB_RECIP_DEVICE:
+		reply = cpu_to_le16(0);
+		break;
+
+	case USB_RECIP_INTERFACE:
+		reply = cpu_to_le16(0);
+		break;
+
+	case USB_RECIP_ENDPOINT:
+		epnum = le16_to_cpu(ctrl->wIndex) & USB_ENDPOINT_NUMBER_MASK;
+		hsep = &hsudc->ep[epnum];
+		reply = cpu_to_le16(hsep->stopped ? 1 : 0);
+		break;
+	}
+
+	INIT_LIST_HEAD(&hsreq.queue);
+	hsreq.req.length = 2;
+	hsreq.req.buf = &reply;
+	hsreq.req.actual = 0;
+	hsreq.req.complete = NULL;
+	s3c_hsudc_write_fifo(hsep0, &hsreq);
+}
+
+/**
+ * s3c_hsudc_process_setup - Process control request received on endpoint 0.
+ * @hsudc: Device controller on which control request has been received.
+ *
+ * Read the control request received on endpoint 0, decode it and handle
+ * the request.
+ */
+static void s3c_hsudc_process_setup(struct s3c_hsudc *hsudc)
+{
+	struct s3c_hsudc_ep *hsep = &hsudc->ep[0];
+	struct usb_ctrlrequest ctrl = {0};
+	int ret;
+
+	s3c_hsudc_nuke_ep(hsep, -EPROTO);
+	s3c_hsudc_read_setup_pkt(hsudc, (u16 *)&ctrl);
+
+	if (ctrl.bRequestType & USB_DIR_IN) {
+		hsep->bEndpointAddress |= USB_DIR_IN;
+		hsudc->ep0state = DATA_STATE_XMIT;
+	} else {
+		hsep->bEndpointAddress &= ~USB_DIR_IN;
+		hsudc->ep0state = DATA_STATE_RECV;
+	}
+
+	switch (ctrl.bRequest) {
+	case USB_REQ_SET_ADDRESS:
+		if (ctrl.bRequestType != (USB_TYPE_STANDARD | USB_RECIP_DEVICE))
+			break;
+		hsudc->ep0state = WAIT_FOR_SETUP;
+		return;
+
+	case USB_REQ_GET_STATUS:
+		if ((ctrl.bRequestType & USB_TYPE_MASK) != USB_TYPE_STANDARD)
+			break;
+		s3c_hsudc_process_req_status(hsudc, &ctrl);
+		return;
+
+	case USB_REQ_SET_FEATURE:
+	case USB_REQ_CLEAR_FEATURE:
+		if ((ctrl.bRequestType & USB_TYPE_MASK) != USB_TYPE_STANDARD)
+			break;
+		s3c_hsudc_handle_reqfeat(hsudc, &ctrl);
+		hsudc->ep0state = WAIT_FOR_SETUP;
+		return;
+	}
+
+	if (hsudc->driver) {
+		spin_unlock(&hsudc->lock);
+		ret = hsudc->driver->setup(&hsudc->gadget, &ctrl);
+		spin_lock(&hsudc->lock);
+
+		if (ctrl.bRequest == USB_REQ_SET_CONFIGURATION) {
+			hsep->bEndpointAddress &= ~USB_DIR_IN;
+			hsudc->ep0state = WAIT_FOR_SETUP;
+		}
+
+		if (ret < 0) {
+			dev_err(hsudc->dev, "setup failed, returned %d\n",
+						ret);
+			s3c_hsudc_set_halt(&hsep->ep, 1);
+			hsudc->ep0state = WAIT_FOR_SETUP;
+			hsep->bEndpointAddress &= ~USB_DIR_IN;
+		}
+	}
+}
+
+/** s3c_hsudc_handle_ep0_intr - Handle endpoint 0 interrupt.
+ * @hsudc: Device controller on which endpoint 0 interrupt has occured.
+ *
+ * Handle endpoint 0 interrupt when it occurs. EP0 interrupt could occur
+ * when a stall handshake is sent to host or data is sent/received on
+ * endpoint 0.
+ */
+static void s3c_hsudc_handle_ep0_intr(struct s3c_hsudc *hsudc)
+{
+	struct s3c_hsudc_ep *hsep = &hsudc->ep[0];
+	struct s3c_hsudc_req *hsreq;
+	u32 csr = readl(hsudc->regs + S3C_EP0SR);
+	u32 ecr;
+
+	if (csr & S3C_EP0SR_STALL) {
+		ecr = readl(hsudc->regs + S3C_EP0CR);
+		ecr &= ~(S3C_ECR_STALL | S3C_ECR_FLUSH);
+		writel(ecr, hsudc->regs + S3C_EP0CR);
+
+		writel(S3C_EP0SR_STALL, hsudc->regs + S3C_EP0SR);
+		hsep->stopped = 0;
+
+		s3c_hsudc_nuke_ep(hsep, -ECONNABORTED);
+		hsudc->ep0state = WAIT_FOR_SETUP;
+		hsep->bEndpointAddress &= ~USB_DIR_IN;
+		return;
+	}
+
+	if (csr & S3C_EP0SR_TX_SUCCESS) {
+		writel(S3C_EP0SR_TX_SUCCESS, hsudc->regs + S3C_EP0SR);
+		if (ep_is_in(hsep)) {
+			if (list_empty(&hsep->queue))
+				return;
+
+			hsreq = list_entry(hsep->queue.next,
+					struct s3c_hsudc_req, queue);
+			s3c_hsudc_write_fifo(hsep, hsreq);
+		}
+	}
+
+	if (csr & S3C_EP0SR_RX_SUCCESS) {
+		if (hsudc->ep0state == WAIT_FOR_SETUP)
+			s3c_hsudc_process_setup(hsudc);
+		else {
+			if (!ep_is_in(hsep)) {
+				if (list_empty(&hsep->queue))
+					return;
+				hsreq = list_entry(hsep->queue.next,
+					struct s3c_hsudc_req, queue);
+				s3c_hsudc_read_fifo(hsep, hsreq);
+			}
+		}
+	}
+}
+
+/**
+ * s3c_hsudc_ep_enable - Enable a endpoint.
+ * @_ep: The endpoint to be enabled.
+ * @desc: Endpoint descriptor.
+ *
+ * Enables a endpoint when called from the gadget driver. Endpoint stall if
+ * any is cleared, transfer type is configured and endpoint interrupt is
+ * enabled.
+ */
+static int s3c_hsudc_ep_enable(struct usb_ep *_ep,
+				const struct usb_endpoint_descriptor *desc)
+{
+	struct s3c_hsudc_ep *hsep;
+	struct s3c_hsudc *hsudc;
+	unsigned long flags;
+	u32 ecr = 0;
+
+	hsep = container_of(_ep, struct s3c_hsudc_ep, ep);
+	if (!_ep || !desc || hsep->desc || _ep->name == ep0name
+		|| desc->bDescriptorType != USB_DT_ENDPOINT
+		|| hsep->bEndpointAddress != desc->bEndpointAddress
+		|| ep_maxpacket(hsep) < le16_to_cpu(desc->wMaxPacketSize))
+		return -EINVAL;
+
+	if ((desc->bmAttributes == USB_ENDPOINT_XFER_BULK
+		&& le16_to_cpu(desc->wMaxPacketSize) != ep_maxpacket(hsep))
+		|| !desc->wMaxPacketSize)
+		return -ERANGE;
+
+	hsudc = hsep->dev;
+	if (!hsudc->driver || hsudc->gadget.speed == USB_SPEED_UNKNOWN)
+		return -ESHUTDOWN;
+
+	spin_lock_irqsave(&hsudc->lock, flags);
+
+	set_index(hsudc, hsep->bEndpointAddress);
+	ecr |= ((usb_endpoint_xfer_int(desc)) ? S3C_ECR_IEMS : S3C_ECR_DUEN);
+	writel(ecr, hsudc->regs + S3C_ECR);
+
+	hsep->stopped = hsep->wedge = 0;
+	hsep->desc = desc;
+	hsep->ep.maxpacket = le16_to_cpu(desc->wMaxPacketSize);
+
+	s3c_hsudc_set_halt(_ep, 0);
+	__set_bit(ep_index(hsep), hsudc->regs + S3C_EIER);
+
+	spin_unlock_irqrestore(&hsudc->lock, flags);
+	return 0;
+}
+
+/**
+ * s3c_hsudc_ep_disable - Disable a endpoint.
+ * @_ep: The endpoint to be disabled.
+ * @desc: Endpoint descriptor.
+ *
+ * Disables a endpoint when called from the gadget driver.
+ */
+static int s3c_hsudc_ep_disable(struct usb_ep *_ep)
+{
+	struct s3c_hsudc_ep *hsep = our_ep(_ep);
+	struct s3c_hsudc *hsudc = hsep->dev;
+	unsigned long flags;
+
+	if (!_ep || !hsep->desc)
+		return -EINVAL;
+
+	spin_lock_irqsave(&hsudc->lock, flags);
+
+	set_index(hsudc, hsep->bEndpointAddress);
+	__clear_bit(ep_index(hsep), hsudc->regs + S3C_EIER);
+
+	s3c_hsudc_nuke_ep(hsep, -ESHUTDOWN);
+
+	hsep->desc = 0;
+	hsep->stopped = 1;
+
+	spin_unlock_irqrestore(&hsudc->lock, flags);
+	return 0;
+}
+
+/**
+ * s3c_hsudc_alloc_request - Allocate a new request.
+ * @_ep: Endpoint for which request is allocated (not used).
+ * @gfp_flags: Flags used for the allocation.
+ *
+ * Allocates a single transfer request structure when called from gadget driver.
+ */
+static struct usb_request *s3c_hsudc_alloc_request(struct usb_ep *_ep,
+						gfp_t gfp_flags)
+{
+	struct s3c_hsudc_req *hsreq;
+
+	hsreq = kzalloc(sizeof *hsreq, gfp_flags);
+	if (!hsreq)
+		return 0;
+
+	INIT_LIST_HEAD(&hsreq->queue);
+	return &hsreq->req;
+}
+
+/**
+ * s3c_hsudc_free_request - Deallocate a request.
+ * @ep: Endpoint for which request is deallocated (not used).
+ * @_req: Request to be deallocated.
+ *
+ * Allocates a single transfer request structure when called from gadget driver.
+ */
+static void s3c_hsudc_free_request(struct usb_ep *ep, struct usb_request *_req)
+{
+	struct s3c_hsudc_req *hsreq;
+
+	hsreq = container_of(_req, struct s3c_hsudc_req, req);
+	WARN_ON(!list_empty(&hsreq->queue));
+	kfree(hsreq);
+}
+
+/**
+ * s3c_hsudc_queue - Queue a transfer request for the endpoint.
+ * @_ep: Endpoint for which the request is queued.
+ * @_req: Request to be queued.
+ * @gfp_flags: Not used.
+ *
+ * Start or enqueue a request for a endpoint when called from gadget driver.
+ */
+static int s3c_hsudc_queue(struct usb_ep *_ep, struct usb_request *_req,
+			gfp_t gfp_flags)
+{
+	struct s3c_hsudc_req *hsreq;
+	struct s3c_hsudc_ep *hsep;
+	struct s3c_hsudc *hsudc;
+	unsigned long flags;
+	u32 offset;
+	u32 csr;
+
+	hsreq = container_of(_req, struct s3c_hsudc_req, req);
+	if ((!_req || !_req->complete || !_req->buf ||
+		!list_empty(&hsreq->queue)))
+		return -EINVAL;
+
+	hsep = container_of(_ep, struct s3c_hsudc_ep, ep);
+	hsudc = hsep->dev;
+	if (!hsudc->driver || hsudc->gadget.speed == USB_SPEED_UNKNOWN)
+		return -ESHUTDOWN;
+
+	spin_lock_irqsave(&hsudc->lock, flags);
+	set_index(hsudc, hsep->bEndpointAddress);
+
+	_req->status = -EINPROGRESS;
+	_req->actual = 0;
+
+	if (!ep_index(hsep) && _req->length == 0) {
+		hsudc->ep0state = WAIT_FOR_SETUP;
+		s3c_hsudc_complete_request(hsep, hsreq, 0);
+		spin_unlock_irqrestore(&hsudc->lock, flags);
+		return 0;
+	}
+
+	if (list_empty(&hsep->queue) && !hsep->stopped) {
+		offset = (ep_index(hsep)) ? S3C_ESR : S3C_EP0SR;
+		if (ep_is_in(hsep)) {
+			csr = readl((u32)hsudc->regs + offset);
+			if (!(csr & S3C_ESR_TX_SUCCESS) &&
+				(s3c_hsudc_write_fifo(hsep, hsreq) == 1))
+				hsreq = 0;
+		} else {
+			csr = readl((u32)hsudc->regs + offset);
+			if ((csr & S3C_ESR_RX_SUCCESS)
+				   && (s3c_hsudc_read_fifo(hsep, hsreq) == 1))
+				hsreq = 0;
+		}
+	}
+
+	if (hsreq != 0)
+		list_add_tail(&hsreq->queue, &hsep->queue);
+
+	spin_unlock_irqrestore(&hsudc->lock, flags);
+	return 0;
+}
+
+/**
+ * s3c_hsudc_dequeue - Dequeue a transfer request from an endpoint.
+ * @_ep: Endpoint from which the request is dequeued.
+ * @_req: Request to be dequeued.
+ *
+ * Dequeue a request from a endpoint when called from gadget driver.
+ */
+static int s3c_hsudc_dequeue(struct usb_ep *_ep, struct usb_request *_req)
+{
+	struct s3c_hsudc_ep *hsep = our_ep(_ep);
+	struct s3c_hsudc *hsudc = hsep->dev;
+	struct s3c_hsudc_req *hsreq;
+	unsigned long flags;
+
+	hsep = container_of(_ep, struct s3c_hsudc_ep, ep);
+	if (!_ep || hsep->ep.name == ep0name)
+		return -EINVAL;
+
+	spin_lock_irqsave(&hsudc->lock, flags);
+
+	list_for_each_entry(hsreq, &hsep->queue, queue) {
+		if (&hsreq->req == _req)
+			break;
+	}
+	if (&hsreq->req != _req) {
+		spin_unlock_irqrestore(&hsudc->lock, flags);
+		return -EINVAL;
+	}
+
+	set_index(hsudc, hsep->bEndpointAddress);
+	s3c_hsudc_complete_request(hsep, hsreq, -ECONNRESET);
+
+	spin_unlock_irqrestore(&hsudc->lock, flags);
+	return 0;
+}
+
+static struct usb_ep_ops s3c_hsudc_ep_ops = {
+	.enable = s3c_hsudc_ep_enable,
+	.disable = s3c_hsudc_ep_disable,
+	.alloc_request = s3c_hsudc_alloc_request,
+	.free_request = s3c_hsudc_free_request,
+	.queue = s3c_hsudc_queue,
+	.dequeue = s3c_hsudc_dequeue,
+	.set_halt = s3c_hsudc_set_halt,
+	.set_wedge = s3c_hsudc_set_wedge,
+};
+
+/**
+ * s3c_hsudc_initep - Initialize a endpoint to default state.
+ * @hsudc - Reference to the device controller.
+ * @hsep - Endpoint to be initialized.
+ * @epnum - Address to be assigned to the endpoint.
+ *
+ * Initialize a endpoint with default configuration.
+ */
+static void s3c_hsudc_initep(struct s3c_hsudc *hsudc,
+				struct s3c_hsudc_ep *hsep, int epnum)
+{
+	char *dir;
+
+	if ((epnum % 2) == 0) {
+		dir = "out";
+	} else {
+		dir = "in";
+		hsep->bEndpointAddress = USB_DIR_IN;
+	}
+
+	hsep->bEndpointAddress |= epnum;
+	if (epnum)
+		snprintf(hsep->name, sizeof(hsep->name), "ep%d%s", epnum, dir);
+	else
+		snprintf(hsep->name, sizeof(hsep->name), "%s", ep0name);
+
+	INIT_LIST_HEAD(&hsep->queue);
+	INIT_LIST_HEAD(&hsep->ep.ep_list);
+	if (epnum)
+		list_add_tail(&hsep->ep.ep_list, &hsudc->gadget.ep_list);
+
+	hsep->dev = hsudc;
+	hsep->ep.name = hsep->name;
+	hsep->ep.maxpacket = epnum ? 512 : 64;
+	hsep->ep.ops = &s3c_hsudc_ep_ops;
+	hsep->fifo = hsudc->regs + S3C_BR(epnum);
+	hsep->desc = 0;
+	hsep->stopped = 0;
+	hsep->wedge = 0;
+
+	set_index(hsudc, epnum);
+	writel(hsep->ep.maxpacket, hsudc->regs + S3C_MPR);
+}
+
+/**
+ * s3c_hsudc_setup_ep - Configure all endpoints to default state.
+ * @hsudc: Reference to device controller.
+ *
+ * Configures all endpoints to default state.
+ */
+static void s3c_hsudc_setup_ep(struct s3c_hsudc *hsudc)
+{
+	int epnum;
+
+	hsudc->ep0state = WAIT_FOR_SETUP;
+	INIT_LIST_HEAD(&hsudc->gadget.ep_list);
+	for (epnum = 0; epnum < hsudc->pd->epnum; epnum++)
+		s3c_hsudc_initep(hsudc, &hsudc->ep[epnum], epnum);
+}
+
+/**
+ * s3c_hsudc_reconfig - Reconfigure the device controller to default state.
+ * @hsudc: Reference to device controller.
+ *
+ * Reconfigures the device controller registers to a default state.
+ */
+static void s3c_hsudc_reconfig(struct s3c_hsudc *hsudc)
+{
+	writel(0xAA, hsudc->regs + S3C_EDR);
+	writel(1, hsudc->regs + S3C_EIER);
+	writel(0, hsudc->regs + S3C_TR);
+	writel(S3C_SCR_DTZIEN_EN | S3C_SCR_RRD_EN | S3C_SCR_SUS_EN |
+			S3C_SCR_RST_EN, hsudc->regs + S3C_SCR);
+	writel(0, hsudc->regs + S3C_EP0CR);
+
+	s3c_hsudc_setup_ep(hsudc);
+}
+
+/**
+ * s3c_hsudc_irq - Interrupt handler for device controller.
+ * @irq: Not used.
+ * @_dev: Reference to the device controller.
+ *
+ * Interrupt handler for the device controller. This handler handles controller
+ * interrupts and endpoint interrupts.
+ */
+static irqreturn_t s3c_hsudc_irq(int irq, void *_dev)
+{
+	struct s3c_hsudc *hsudc = _dev;
+	struct s3c_hsudc_ep *hsep;
+	u32 ep_intr;
+	u32 sys_status;
+	u32 ep_idx;
+
+	spin_lock(&hsudc->lock);
+
+	sys_status = readl(hsudc->regs + S3C_SSR);
+	ep_intr = readl(hsudc->regs + S3C_EIR) & 0x3FF;
+
+	if (!ep_intr && !(sys_status & S3C_SSR_DTZIEN_EN)) {
+		spin_unlock(&hsudc->lock);
+		return IRQ_HANDLED;
+	}
+
+	if (sys_status) {
+		if (sys_status & S3C_SSR_VBUSON)
+			writel(S3C_SSR_VBUSON, hsudc->regs + S3C_SSR);
+
+		if (sys_status & S3C_SSR_ERR)
+			writel(S3C_SSR_ERR, hsudc->regs + S3C_SSR);
+
+		if (sys_status & S3C_SSR_SDE) {
+			writel(S3C_SSR_SDE, hsudc->regs + S3C_SSR);
+			hsudc->gadget.speed = (sys_status & S3C_SSR_HSP) ?
+				USB_SPEED_HIGH : USB_SPEED_FULL;
+		}
+
+		if (sys_status & S3C_SSR_SUSPEND) {
+			writel(S3C_SSR_SUSPEND, hsudc->regs + S3C_SSR);
+			if (hsudc->gadget.speed != USB_SPEED_UNKNOWN
+				&& hsudc->driver && hsudc->driver->suspend)
+				hsudc->driver->suspend(&hsudc->gadget);
+		}
+
+		if (sys_status & S3C_SSR_RESUME) {
+			writel(S3C_SSR_RESUME, hsudc->regs + S3C_SSR);
+			if (hsudc->gadget.speed != USB_SPEED_UNKNOWN
+				&& hsudc->driver && hsudc->driver->resume)
+				hsudc->driver->resume(&hsudc->gadget);
+		}
+
+		if (sys_status & S3C_SSR_RESET) {
+			writel(S3C_SSR_RESET, hsudc->regs + S3C_SSR);
+			for (ep_idx = 0; ep_idx < hsudc->pd->epnum; ep_idx++) {
+				hsep = &hsudc->ep[ep_idx];
+				hsep->stopped = 1;
+				s3c_hsudc_nuke_ep(hsep, -ECONNRESET);
+			}
+			s3c_hsudc_reconfig(hsudc);
+			hsudc->ep0state = WAIT_FOR_SETUP;
+		}
+	}
+
+	if (ep_intr & S3C_EIR_EP0) {
+		writel(S3C_EIR_EP0, hsudc->regs + S3C_EIR);
+		set_index(hsudc, 0);
+		s3c_hsudc_handle_ep0_intr(hsudc);
+	}
+
+	ep_intr >>= 1;
+	ep_idx = 1;
+	while (ep_intr) {
+		if (ep_intr & 1)  {
+			hsep = &hsudc->ep[ep_idx];
+			set_index(hsudc, ep_idx);
+			writel(1 << ep_idx, hsudc->regs + S3C_EIR);
+			if (ep_is_in(hsep))
+				s3c_hsudc_epin_intr(hsudc, ep_idx);
+			else
+				s3c_hsudc_epout_intr(hsudc, ep_idx);
+		}
+		ep_intr >>= 1;
+		ep_idx++;
+	}
+
+	spin_unlock(&hsudc->lock);
+	return IRQ_HANDLED;
+}
+
+int usb_gadget_probe_driver(struct usb_gadget_driver *driver,
+               int (*bind)(struct usb_gadget *))
+{
+	struct s3c_hsudc *hsudc = the_controller;
+	int ret;
+
+	if (!driver
+		|| (driver->speed != USB_SPEED_FULL &&
+			driver->speed != USB_SPEED_HIGH)
+		|| !bind
+		|| !driver->unbind || !driver->disconnect || !driver->setup)
+		return -EINVAL;
+
+	if (!hsudc)
+		return -ENODEV;
+
+	if (hsudc->driver)
+		return -EBUSY;
+
+	hsudc->driver = driver;
+	hsudc->gadget.dev.driver = &driver->driver;
+	hsudc->gadget.speed = USB_SPEED_UNKNOWN;
+	ret = device_add(&hsudc->gadget.dev);
+	if (ret) {
+		dev_err(hsudc->dev, "failed to probe gadget device");
+		return ret;
+	}
+
+	ret = bind(&hsudc->gadget);
+	if (ret) {
+		dev_err(hsudc->dev, "%s: bind failed\n", hsudc->gadget.name);
+		device_del(&hsudc->gadget.dev);
+
+		hsudc->driver = NULL;
+		hsudc->gadget.dev.driver = NULL;
+		return ret;
+	}
+
+	enable_irq(hsudc->irq);
+	dev_info(hsudc->dev, "bound driver %s\n", driver->driver.name);
+
+	s3c_hsudc_reconfig(hsudc);
+	s3c_hsudc_init_phy();
+	if (hsudc->pd->gpio_init)
+		hsudc->pd->gpio_init();
+
+	return 0;
+}
+EXPORT_SYMBOL(usb_gadget_probe_driver);
+
+int usb_gadget_unregister_driver(struct usb_gadget_driver *driver)
+{
+	struct s3c_hsudc *hsudc = the_controller;
+	unsigned long flags;
+
+	if (!hsudc)
+		return -ENODEV;
+
+	if (!driver || driver != hsudc->driver || !driver->unbind)
+		return -EINVAL;
+
+	spin_lock_irqsave(&hsudc->lock, flags);
+	hsudc->driver = 0;
+	s3c_hsudc_uninit_phy();
+	if (hsudc->pd->gpio_uninit)
+		hsudc->pd->gpio_uninit();
+	s3c_hsudc_stop_activity(hsudc, driver);
+	spin_unlock_irqrestore(&hsudc->lock, flags);
+
+	driver->unbind(&hsudc->gadget);
+	device_del(&hsudc->gadget.dev);
+	disable_irq(hsudc->irq);
+
+	dev_info(hsudc->dev, "unregistered gadget driver '%s'\n",
+			driver->driver.name);
+	return 0;
+}
+EXPORT_SYMBOL(usb_gadget_unregister_driver);
+
+static inline u32 s3c_hsudc_read_frameno(struct s3c_hsudc *hsudc)
+{
+	return readl(hsudc->regs + S3C_FNR) & 0x3FF;
+}
+
+static int s3c_hsudc_gadget_getframe(struct usb_gadget *gadget)
+{
+	return s3c_hsudc_read_frameno(to_hsudc(gadget));
+}
+
+static struct usb_gadget_ops s3c_hsudc_gadget_ops = {
+	.get_frame	= s3c_hsudc_gadget_getframe,
+};
+
+static int s3c_hsudc_probe(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	struct resource *res;
+	struct s3c_hsudc *hsudc;
+	struct s3c24xx_hsudc_platdata *pd = pdev->dev.platform_data;
+	int ret;
+
+	hsudc = kzalloc(sizeof(struct s3c_hsudc) +
+			sizeof(struct s3c_hsudc_ep) * pd->epnum,
+			GFP_KERNEL);
+	if (!hsudc) {
+		dev_err(dev, "cannot allocate memory\n");
+		return -ENOMEM;
+	}
+
+	the_controller = hsudc;
+	platform_set_drvdata(pdev, dev);
+	hsudc->dev = dev;
+	hsudc->pd = pdev->dev.platform_data;
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!res) {
+		dev_err(dev, "unable to obtain driver resource data\n");
+		ret = -ENODEV;
+		goto err_res;
+	}
+
+	hsudc->mem_rsrc = request_mem_region(res->start, resource_size(res),
+				dev_name(&pdev->dev));
+	if (!hsudc->mem_rsrc) {
+		dev_err(dev, "failed to reserve register area\n");
+		ret = -ENODEV;
+		goto err_res;
+	}
+
+	hsudc->regs = ioremap(res->start, resource_size(res));
+	if (!hsudc->regs) {
+		dev_err(dev, "error mapping device register area\n");
+		ret = -EBUSY;
+		goto err_remap;
+	}
+
+	ret = platform_get_irq(pdev, 0);
+	if (ret < 0) {
+		dev_err(dev, "unable to obtain IRQ number\n");
+		goto err_irq;
+	}
+	hsudc->irq = ret;
+
+	ret = request_irq(hsudc->irq, s3c_hsudc_irq, 0, driver_name, hsudc);
+	if (ret < 0) {
+		dev_err(dev, "irq request failed\n");
+		goto err_irq;
+	}
+
+	spin_lock_init(&hsudc->lock);
+
+	device_initialize(&hsudc->gadget.dev);
+	dev_set_name(&hsudc->gadget.dev, "gadget");
+
+	hsudc->gadget.is_dualspeed = 1;
+	hsudc->gadget.ops = &s3c_hsudc_gadget_ops;
+	hsudc->gadget.name = dev_name(dev);
+	hsudc->gadget.dev.parent = dev;
+	hsudc->gadget.dev.dma_mask = dev->dma_mask;
+	hsudc->gadget.ep0 = &hsudc->ep[0].ep;
+
+	hsudc->gadget.is_otg = 0;
+	hsudc->gadget.is_a_peripheral = 0;
+
+	s3c_hsudc_setup_ep(hsudc);
+
+	hsudc->uclk = clk_get(&pdev->dev, "usb-device");
+	if (hsudc->uclk == NULL) {
+		dev_err(dev, "failed to find usb-device clock source\n");
+		return -ENOENT;
+	}
+	clk_enable(hsudc->uclk);
+
+	local_irq_disable();
+
+	disable_irq(hsudc->irq);
+	local_irq_enable();
+	return 0;
+
+err_irq:
+	iounmap(hsudc->regs);
+
+err_remap:
+	release_resource(hsudc->mem_rsrc);
+	kfree(hsudc->mem_rsrc);
+
+err_res:
+	kfree(hsudc);
+	return ret;
+}
+
+static struct platform_driver s3c_hsudc_driver = {
+	.driver		= {
+		.owner	= THIS_MODULE,
+		.name	= "s3c-hsudc",
+	},
+	.probe		= s3c_hsudc_probe,
+};
+
+static int __init s3c_hsudc_modinit(void)
+{
+	return platform_driver_register(&s3c_hsudc_driver);
+}
+
+static void __exit s3c_hsudc_modexit(void)
+{
+	platform_driver_unregister(&s3c_hsudc_driver);
+}
+
+module_init(s3c_hsudc_modinit);
+module_exit(s3c_hsudc_modexit);
+
+MODULE_DESCRIPTION("Samsung S3C24XX USB high-speed controller driver");
+MODULE_AUTHOR("Thomas Abraham <thomas.ab@samsung.com>");
+MODULE_LICENSE("GPL");
-- 
1.7.2.3

^ permalink raw reply related	[flat|nested] 52+ messages in thread

* [PATCH 4/4] ARM: S3C2416: Add support for USB 2.0 High-Speed gadget controller
  2011-05-07 20:23                     ` Heiko Stübner
@ 2011-05-07 20:29                       ` Heiko Stübner
  -1 siblings, 0 replies; 52+ messages in thread
From: Heiko Stübner @ 2011-05-07 20:29 UTC (permalink / raw)
  To: Greg KH
  Cc: Alan Stern, Kukjin Kim, Ben Dooks, Thomas Abraham, Sangbeom Kim,
	linux-arm-kernel, linux-samsung-soc, linux-usb,
	Alexander Neumann

From: Thomas Abraham <thomas.ab@samsung.com>

Add support for USB 2.0 High-Speed gadget controller driver for Samsung's
S3C2416 processor.

Signed-off-by: Thomas Abraham <thomas.ab@samsung.com>
Signed-off-by: Sangbeom Kim <sbkim73@samsung.com>
---
 arch/arm/mach-s3c2416/mach-smdk2416.c |   27 +++++++++++++++++++++++++++
 1 files changed, 27 insertions(+), 0 deletions(-)

diff --git a/arch/arm/mach-s3c2416/mach-smdk2416.c b/arch/arm/mach-s3c2416/mach-smdk2416.c
index 3f83177..ac27ebb 100644
--- a/arch/arm/mach-s3c2416/mach-smdk2416.c
+++ b/arch/arm/mach-s3c2416/mach-smdk2416.c
@@ -23,6 +23,7 @@
 #include <linux/mtd/partitions.h>
 #include <linux/gpio.h>
 #include <linux/fb.h>
+#include <linux/delay.h>
 
 #include <asm/mach/arch.h>
 #include <asm/mach/map.h>
@@ -35,6 +36,7 @@
 #include <plat/regs-serial.h>
 #include <mach/regs-gpio.h>
 #include <mach/regs-lcd.h>
+#include <mach/regs-s3c2443-clock.h>
 
 #include <mach/idle.h>
 #include <mach/leds-gpio.h>
@@ -47,6 +49,7 @@
 #include <plat/cpu.h>
 #include <plat/nand.h>
 #include <plat/sdhci.h>
+#include <plat/udc.h>
 
 #include <plat/regs-fb-v4.h>
 #include <plat/fb.h>
@@ -121,6 +124,27 @@ static struct s3c2410_uartcfg smdk2416_uartcfgs[] __initdata = {
 	}
 };
 
+void smdk2416_hsudc_gpio_init(void)
+{
+	s3c_gpio_setpull(S3C2410_GPH(14), S3C_GPIO_PULL_UP);
+	s3c_gpio_setpull(S3C2410_GPF(2), S3C_GPIO_PULL_NONE);
+	s3c_gpio_cfgpin(S3C2410_GPH(14), S3C_GPIO_SFN(1));
+	s3c2410_modify_misccr(S3C2416_MISCCR_SEL_SUSPND, 0);
+}
+
+void smdk2416_hsudc_gpio_uninit(void)
+{
+	s3c2410_modify_misccr(S3C2416_MISCCR_SEL_SUSPND, 1);
+	s3c_gpio_setpull(S3C2410_GPH(14), S3C_GPIO_PULL_NONE);
+	s3c_gpio_cfgpin(S3C2410_GPH(14), S3C_GPIO_SFN(0));
+}
+
+struct s3c24xx_hsudc_platdata smdk2416_hsudc_platdata = {
+	.epnum = 9,
+	.gpio_init = smdk2416_hsudc_gpio_init,
+	.gpio_uninit = smdk2416_hsudc_gpio_uninit,
+};
+
 struct s3c_fb_pd_win smdk2416_fb_win[] = {
 	[0] = {
 		/* think this is the same as the smdk6410 */
@@ -186,6 +210,7 @@ static struct platform_device *smdk2416_devices[] __initdata = {
 	&s3c_device_i2c0,
 	&s3c_device_hsmmc0,
 	&s3c_device_hsmmc1,
+	&s3c_device_usb_hsudc,
 };
 
 static void __init smdk2416_map_io(void)
@@ -203,6 +228,8 @@ static void __init smdk2416_machine_init(void)
 	s3c_sdhci0_set_platdata(&smdk2416_hsmmc0_pdata);
 	s3c_sdhci1_set_platdata(&smdk2416_hsmmc1_pdata);
 
+	s3c24xx_hsudc_set_platdata(&smdk2416_hsudc_platdata);
+
 	gpio_request(S3C2410_GPB(4), "USBHost Power");
 	gpio_direction_output(S3C2410_GPB(4), 1);
 
-- 
1.7.2.3

^ permalink raw reply related	[flat|nested] 52+ messages in thread

* [PATCH 4/4] ARM: S3C2416: Add support for USB 2.0 High-Speed gadget controller
@ 2011-05-07 20:29                       ` Heiko Stübner
  0 siblings, 0 replies; 52+ messages in thread
From: Heiko Stübner @ 2011-05-07 20:29 UTC (permalink / raw)
  To: linux-arm-kernel

From: Thomas Abraham <thomas.ab@samsung.com>

Add support for USB 2.0 High-Speed gadget controller driver for Samsung's
S3C2416 processor.

Signed-off-by: Thomas Abraham <thomas.ab@samsung.com>
Signed-off-by: Sangbeom Kim <sbkim73@samsung.com>
---
 arch/arm/mach-s3c2416/mach-smdk2416.c |   27 +++++++++++++++++++++++++++
 1 files changed, 27 insertions(+), 0 deletions(-)

diff --git a/arch/arm/mach-s3c2416/mach-smdk2416.c b/arch/arm/mach-s3c2416/mach-smdk2416.c
index 3f83177..ac27ebb 100644
--- a/arch/arm/mach-s3c2416/mach-smdk2416.c
+++ b/arch/arm/mach-s3c2416/mach-smdk2416.c
@@ -23,6 +23,7 @@
 #include <linux/mtd/partitions.h>
 #include <linux/gpio.h>
 #include <linux/fb.h>
+#include <linux/delay.h>
 
 #include <asm/mach/arch.h>
 #include <asm/mach/map.h>
@@ -35,6 +36,7 @@
 #include <plat/regs-serial.h>
 #include <mach/regs-gpio.h>
 #include <mach/regs-lcd.h>
+#include <mach/regs-s3c2443-clock.h>
 
 #include <mach/idle.h>
 #include <mach/leds-gpio.h>
@@ -47,6 +49,7 @@
 #include <plat/cpu.h>
 #include <plat/nand.h>
 #include <plat/sdhci.h>
+#include <plat/udc.h>
 
 #include <plat/regs-fb-v4.h>
 #include <plat/fb.h>
@@ -121,6 +124,27 @@ static struct s3c2410_uartcfg smdk2416_uartcfgs[] __initdata = {
 	}
 };
 
+void smdk2416_hsudc_gpio_init(void)
+{
+	s3c_gpio_setpull(S3C2410_GPH(14), S3C_GPIO_PULL_UP);
+	s3c_gpio_setpull(S3C2410_GPF(2), S3C_GPIO_PULL_NONE);
+	s3c_gpio_cfgpin(S3C2410_GPH(14), S3C_GPIO_SFN(1));
+	s3c2410_modify_misccr(S3C2416_MISCCR_SEL_SUSPND, 0);
+}
+
+void smdk2416_hsudc_gpio_uninit(void)
+{
+	s3c2410_modify_misccr(S3C2416_MISCCR_SEL_SUSPND, 1);
+	s3c_gpio_setpull(S3C2410_GPH(14), S3C_GPIO_PULL_NONE);
+	s3c_gpio_cfgpin(S3C2410_GPH(14), S3C_GPIO_SFN(0));
+}
+
+struct s3c24xx_hsudc_platdata smdk2416_hsudc_platdata = {
+	.epnum = 9,
+	.gpio_init = smdk2416_hsudc_gpio_init,
+	.gpio_uninit = smdk2416_hsudc_gpio_uninit,
+};
+
 struct s3c_fb_pd_win smdk2416_fb_win[] = {
 	[0] = {
 		/* think this is the same as the smdk6410 */
@@ -186,6 +210,7 @@ static struct platform_device *smdk2416_devices[] __initdata = {
 	&s3c_device_i2c0,
 	&s3c_device_hsmmc0,
 	&s3c_device_hsmmc1,
+	&s3c_device_usb_hsudc,
 };
 
 static void __init smdk2416_map_io(void)
@@ -203,6 +228,8 @@ static void __init smdk2416_machine_init(void)
 	s3c_sdhci0_set_platdata(&smdk2416_hsmmc0_pdata);
 	s3c_sdhci1_set_platdata(&smdk2416_hsmmc1_pdata);
 
+	s3c24xx_hsudc_set_platdata(&smdk2416_hsudc_platdata);
+
 	gpio_request(S3C2410_GPB(4), "USBHost Power");
 	gpio_direction_output(S3C2410_GPB(4), 1);
 
-- 
1.7.2.3

^ permalink raw reply related	[flat|nested] 52+ messages in thread

* Re: [PATCH 2/4] ARM: S3C24XX: Add plaform device definition for USB High-Speed gadget controller.
  2011-05-07 20:26                         ` Heiko Stübner
@ 2011-05-08 11:17                           ` Sergei Shtylyov
  -1 siblings, 0 replies; 52+ messages in thread
From: Sergei Shtylyov @ 2011-05-08 11:17 UTC (permalink / raw)
  To: Heiko Stübner
  Cc: Greg KH, Alan Stern, Kukjin Kim, Ben Dooks, Thomas Abraham,
	Sangbeom Kim, linux-arm-kernel, linux-samsung-soc, linux-usb,
	Alexander Neumann

Hello.

On 08-05-2011 0:26, Heiko Stübner wrote:

> From: Thomas Abraham<thomas.ab@samsung.com>

> S3C2416, S3C2443 and S3C2450 includes a USB High-Speed Gadget controller module.
> This patch adds the following for supporting this controller.

> 1. Definition for USB High-Speed controller base address.
> 2. Platform device instantiation.
> 3. Declaration for platform data structure.
> 4. Functionality to setup platform data for the controller.

> Signed-off-by: Thomas Abraham<thomas.ab@samsung.com>
> Signed-off-by: Sangbeom Kim<sbkim73@samsung.com>
[...]

> diff --git a/arch/arm/plat-s3c24xx/devs.c b/arch/arm/plat-s3c24xx/devs.c
> index 268f3ed..7366799 100644
> --- a/arch/arm/plat-s3c24xx/devs.c
> +++ b/arch/arm/plat-s3c24xx/devs.c
[...]
> @@ -233,6 +234,46 @@ void __init s3c24xx_udc_set_platdata(struct s3c2410_udc_mach_info *pd)
[...]
> +void __init s3c24xx_hsudc_set_platdata(struct s3c24xx_hsudc_platdata *pd)
> +{
> +	struct s3c24xx_hsudc_platdata *npd;
> +
> +	npd = kmalloc(sizeof(*npd), GFP_KERNEL);
> +	if (npd) {
> +		memcpy(npd, pd, sizeof(*npd));

    You could use kmemdup() (from mm/util.c) instead...

WBR, Sergei

^ permalink raw reply	[flat|nested] 52+ messages in thread

* [PATCH 2/4] ARM: S3C24XX: Add plaform device definition for USB High-Speed gadget controller.
@ 2011-05-08 11:17                           ` Sergei Shtylyov
  0 siblings, 0 replies; 52+ messages in thread
From: Sergei Shtylyov @ 2011-05-08 11:17 UTC (permalink / raw)
  To: linux-arm-kernel

Hello.

On 08-05-2011 0:26, Heiko St?bner wrote:

> From: Thomas Abraham<thomas.ab@samsung.com>

> S3C2416, S3C2443 and S3C2450 includes a USB High-Speed Gadget controller module.
> This patch adds the following for supporting this controller.

> 1. Definition for USB High-Speed controller base address.
> 2. Platform device instantiation.
> 3. Declaration for platform data structure.
> 4. Functionality to setup platform data for the controller.

> Signed-off-by: Thomas Abraham<thomas.ab@samsung.com>
> Signed-off-by: Sangbeom Kim<sbkim73@samsung.com>
[...]

> diff --git a/arch/arm/plat-s3c24xx/devs.c b/arch/arm/plat-s3c24xx/devs.c
> index 268f3ed..7366799 100644
> --- a/arch/arm/plat-s3c24xx/devs.c
> +++ b/arch/arm/plat-s3c24xx/devs.c
[...]
> @@ -233,6 +234,46 @@ void __init s3c24xx_udc_set_platdata(struct s3c2410_udc_mach_info *pd)
[...]
> +void __init s3c24xx_hsudc_set_platdata(struct s3c24xx_hsudc_platdata *pd)
> +{
> +	struct s3c24xx_hsudc_platdata *npd;
> +
> +	npd = kmalloc(sizeof(*npd), GFP_KERNEL);
> +	if (npd) {
> +		memcpy(npd, pd, sizeof(*npd));

    You could use kmemdup() (from mm/util.c) instead...

WBR, Sergei

^ permalink raw reply	[flat|nested] 52+ messages in thread

end of thread, other threads:[~2011-05-08 11:19 UTC | newest]

Thread overview: 52+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2011-03-23 21:35 [PATCH RESEND 0/4] USB: gadget: Add Samsung S3C24xx USB HS controller driver Heiko Stübner
2011-03-23 21:35 ` Heiko Stübner
2011-03-23 21:37 ` [PATCH RESEND 1/4] ARM: S3C2416: Add USB Phy register definitions Heiko Stübner
2011-03-23 21:37   ` Heiko Stübner
2011-03-23 21:38 ` [PATCH RESEND 2/4] ARM: S3C24XX: Add plaform device definition for USB High-Speed gadget controller Heiko Stübner
2011-03-23 21:38   ` Heiko Stübner
2011-03-23 21:39 ` [PATCH RESEND v3 3/4] USB: Gadget: Add Samsung S3C24XX USB High-Speed controller driver Heiko Stübner
2011-03-23 21:39   ` Heiko Stübner
2011-04-13 23:28   ` Greg KH
2011-04-13 23:28     ` Greg KH
2011-04-14  9:06     ` [PATCH RESEND v2 0/4] USB: gadget: Add Samsung S3C24xx USB HS " Heiko Stübner
2011-04-14  9:06       ` Heiko Stübner
2011-04-14  9:08       ` [PATCH 1/4] ARM: S3C2416: Add USB Phy register definitions Heiko Stübner
2011-04-14  9:08         ` Heiko Stübner
2011-04-14  9:10       ` [PATCH 2/4] ARM: S3C24XX: Add plaform device definition for USB High-Speed gadget controller Heiko Stübner
2011-04-14  9:10         ` Heiko Stübner
2011-04-14  9:11       ` [PATCH v4 3/4] USB: Gadget: Add Samsung S3C24XX USB High-Speed controller driver Heiko Stübner
2011-04-14  9:11         ` Heiko Stübner
2011-04-14 15:35         ` Alan Stern
2011-04-14 15:35           ` Alan Stern
2011-04-14 15:47           ` Greg KH
2011-04-14 15:47             ` Greg KH
2011-04-14 17:06             ` Heiko Stübner
2011-04-14 17:06               ` Heiko Stübner
2011-04-14 17:15             ` Alan Stern
2011-04-14 17:15               ` Alan Stern
2011-04-16 10:00               ` Heiko Stübner
2011-04-16 10:00                 ` Heiko Stübner
2011-04-16 14:58                 ` Greg KH
2011-04-16 14:58                   ` Greg KH
2011-04-16 18:47                   ` Heiko Stübner
2011-04-16 18:47                     ` Heiko Stübner
2011-05-07 20:23                   ` [PATCH v3 0/4] USB: gadget: Add Samsung S3C24xx USB HS " Heiko Stübner
2011-05-07 20:23                     ` Heiko Stübner
2011-05-07 20:24                     ` [PATCH 1/4] ARM: S3C2416: Add USB Phy register definitions Heiko Stübner
2011-05-07 20:24                       ` Heiko Stübner
     [not found]                     ` <201105072223.44163.heiko-4mtYJXux2i+zQB+pC5nmwQ@public.gmane.org>
2011-05-07 20:26                       ` [PATCH 2/4] ARM: S3C24XX: Add plaform device definition for USB High-Speed gadget controller Heiko Stübner
2011-05-07 20:26                         ` Heiko Stübner
2011-05-08 11:17                         ` Sergei Shtylyov
2011-05-08 11:17                           ` Sergei Shtylyov
2011-05-07 20:28                     ` [PATCH v5 3/4] USB: Gadget: Add Samsung S3C24XX USB High-Speed controller driver Heiko Stübner
2011-05-07 20:28                       ` Heiko Stübner
2011-05-07 20:29                     ` [PATCH 4/4] ARM: S3C2416: Add support for USB 2.0 High-Speed gadget controller Heiko Stübner
2011-05-07 20:29                       ` Heiko Stübner
2011-04-14  9:12       ` Heiko Stübner
2011-04-14  9:12         ` Heiko Stübner
2011-03-23 21:40 ` [PATCH RESEND " Heiko Stübner
2011-03-23 21:40   ` Heiko Stübner
2011-04-08 10:04 ` [PATCH RESEND 0/4] USB: gadget: Add Samsung S3C24xx USB HS controller driver Heiko Stübner
2011-04-08 10:04   ` Heiko Stübner
2011-04-08 20:52   ` Greg KH
2011-04-08 20:52     ` Greg KH

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.