* [PATCH 0/4] USB: gadget: Add Samsung S3C24xx USB HS controller driver @ 2010-10-13 0:10 ` Sangbeom Kim 0 siblings, 0 replies; 16+ messages in thread From: Sangbeom Kim @ 2010-10-13 0:10 UTC (permalink / raw) To: linux-usb, linux-arm-kernel, linux-samsung-soc; +Cc: kgene.kim, ben-linux 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 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] 16+ messages in thread
* [PATCH 0/4] USB: gadget: Add Samsung S3C24xx USB HS controller driver @ 2010-10-13 0:10 ` Sangbeom Kim 0 siblings, 0 replies; 16+ messages in thread From: Sangbeom Kim @ 2010-10-13 0:10 UTC (permalink / raw) To: linux-arm-kernel 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 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] 16+ messages in thread
[parent not found: <1286928627-22824-1-git-send-email-sbkim73-Sze3O3UU22JBDgjK7y7TUQ@public.gmane.org>]
* [PATCH 1/4] ARM: S3C2416: Add USB Phy register definitions 2010-10-13 0:10 ` Sangbeom Kim @ 2010-10-13 0:10 ` Sangbeom Kim -1 siblings, 0 replies; 16+ messages in thread From: Sangbeom Kim @ 2010-10-13 0:10 UTC (permalink / raw) To: linux-usb-u79uwXL29TY76Z2rM5mHXA, linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r, linux-samsung-soc-u79uwXL29TY76Z2rM5mHXA Cc: kgene.kim-Sze3O3UU22JBDgjK7y7TUQ, ben-linux-elnMNo+KYs3YtjvyW6yDsg, Thomas Abraham, Sangbeom Kim From: Thomas Abraham <thomas.ab-Sze3O3UU22JBDgjK7y7TUQ@public.gmane.org> 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-Sze3O3UU22JBDgjK7y7TUQ@public.gmane.org> Signed-off-by: Sangbeom Kim <sbkim73-Sze3O3UU22JBDgjK7y7TUQ@public.gmane.org> --- .../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 08ab9df..2c93d83 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) @@ -118,6 +122,27 @@ #define S3C2443_SCLKCON_UARTCLK (1<<8) #define S3C2443_SCLKCON_USBHOST (1<<1) +#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.6.6.rc2 -- 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] 16+ messages in thread
* [PATCH 1/4] ARM: S3C2416: Add USB Phy register definitions @ 2010-10-13 0:10 ` Sangbeom Kim 0 siblings, 0 replies; 16+ messages in thread From: Sangbeom Kim @ 2010-10-13 0:10 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 08ab9df..2c93d83 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) @@ -118,6 +122,27 @@ #define S3C2443_SCLKCON_UARTCLK (1<<8) #define S3C2443_SCLKCON_USBHOST (1<<1) +#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.6.6.rc2 ^ permalink raw reply related [flat|nested] 16+ messages in thread
* [PATCH 2/4] ARM: S3C24XX: Add plaform device definition for USB High-Speed gadget controller. 2010-10-13 0:10 ` Sangbeom Kim @ 2010-10-13 0:10 ` Sangbeom Kim -1 siblings, 0 replies; 16+ messages in thread From: Sangbeom Kim @ 2010-10-13 0:10 UTC (permalink / raw) To: linux-usb-u79uwXL29TY76Z2rM5mHXA, linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r, linux-samsung-soc-u79uwXL29TY76Z2rM5mHXA Cc: kgene.kim-Sze3O3UU22JBDgjK7y7TUQ, ben-linux-elnMNo+KYs3YtjvyW6yDsg, Thomas Abraham, Sangbeom Kim 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 cd3983a..0c6667f 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 452e184..481d9c9 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> @@ -234,6 +235,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 7d448e1..8221859 100644 --- a/arch/arm/plat-samsung/include/plat/devs.h +++ b/arch/arm/plat-samsung/include/plat/devs.h @@ -78,6 +78,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.6.6.rc2 -- 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] 16+ messages in thread
* [PATCH 2/4] ARM: S3C24XX: Add plaform device definition for USB High-Speed gadget controller. @ 2010-10-13 0:10 ` Sangbeom Kim 0 siblings, 0 replies; 16+ messages in thread From: Sangbeom Kim @ 2010-10-13 0: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 cd3983a..0c6667f 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 452e184..481d9c9 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> @@ -234,6 +235,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 7d448e1..8221859 100644 --- a/arch/arm/plat-samsung/include/plat/devs.h +++ b/arch/arm/plat-samsung/include/plat/devs.h @@ -78,6 +78,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.6.6.rc2 ^ permalink raw reply related [flat|nested] 16+ messages in thread
* [PATCH 3/4] USB: Gadget: Add Samsung S3C24XX USB High-Speed controller driver 2010-10-13 0:10 ` Sangbeom Kim @ 2010-10-13 0:10 ` Sangbeom Kim -1 siblings, 0 replies; 16+ messages in thread From: Sangbeom Kim @ 2010-10-13 0:10 UTC (permalink / raw) To: linux-usb-u79uwXL29TY76Z2rM5mHXA, linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r, linux-samsung-soc-u79uwXL29TY76Z2rM5mHXA Cc: kgene.kim-Sze3O3UU22JBDgjK7y7TUQ, ben-linux-elnMNo+KYs3YtjvyW6yDsg, Thomas Abraham, Sangbeom Kim From: Thomas Abraham <thomas.ab-Sze3O3UU22JBDgjK7y7TUQ@public.gmane.org> 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-Sze3O3UU22JBDgjK7y7TUQ@public.gmane.org> Signed-off-by: Sangbeom Kim <sbkim73-Sze3O3UU22JBDgjK7y7TUQ@public.gmane.org> --- drivers/usb/gadget/Kconfig | 17 + drivers/usb/gadget/Makefile | 1 + drivers/usb/gadget/gadget_chips.h | 7 + drivers/usb/gadget/s3c-hsudc.c | 1329 +++++++++++++++++++++++++++++++++++++ 4 files changed, 1354 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 cd27f9b..235d4db 100644 --- a/drivers/usb/gadget/Kconfig +++ b/drivers/usb/gadget/Kconfig @@ -348,6 +348,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 + # # Controllers available in both integrated and discrete versions # diff --git a/drivers/usb/gadget/Makefile b/drivers/usb/gadget/Makefile index 27283df..76e3d67 100644 --- a/drivers/usb/gadget/Makefile +++ b/drivers/usb/gadget/Makefile @@ -27,6 +27,7 @@ obj-$(CONFIG_USB_R8A66597) += r8a66597-udc.o obj-$(CONFIG_USB_FSL_QE) += fsl_qe_udc.o obj-$(CONFIG_USB_CI13XXX) += ci13xxx_udc.o obj-$(CONFIG_USB_S3C_HSOTG) += s3c-hsotg.o +obj-$(CONFIG_USB_S3C_HSUDC) += s3c-hsudc.o obj-$(CONFIG_USB_LANGWELL) += langwell_udc.o # diff --git a/drivers/usb/gadget/gadget_chips.h b/drivers/usb/gadget/gadget_chips.h index e511fec..0b27f6f 100644 --- a/drivers/usb/gadget/gadget_chips.h +++ b/drivers/usb/gadget/gadget_chips.h @@ -142,6 +142,11 @@ #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 /** * usb_gadget_controller_number - support bcdDevice id convention @@ -200,6 +205,8 @@ static inline int usb_gadget_controller_number(struct usb_gadget *gadget) return 0x25; else if (gadget_is_s3c_hsotg(gadget)) return 0x26; + else if (gadget_is_s3c_hsudc(gadget)) + return 0x27; return -ENOENT; } diff --git a/drivers/usb/gadget/s3c-hsudc.c b/drivers/usb/gadget/s3c-hsudc.c new file mode 100644 index 0000000..ebc4a10 --- /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; + u32 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; + __raw_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 = __raw_readl(S3C2443_PWRCFG) | S3C2443_PWRCFG_USBPHY; + __raw_writel(cfg, S3C2443_PWRCFG); + + cfg = __raw_readl(S3C2443_URSTCON); + cfg |= (S3C2443_URSTCON_FUNCRST | S3C2443_URSTCON_PHYRST); + __raw_writel(cfg, S3C2443_URSTCON); + mdelay(1); + + cfg = __raw_readl(S3C2443_URSTCON); + cfg &= ~(S3C2443_URSTCON_FUNCRST | S3C2443_URSTCON_PHYRST); + __raw_writel(cfg, S3C2443_URSTCON); + + cfg = __raw_readl(S3C2443_PHYCTRL); + cfg &= ~(S3C2443_PHYCTRL_CLKSEL | S3C2443_PHYCTRL_DSPORT); + cfg |= (S3C2443_PHYCTRL_EXTCLK | S3C2443_PHYCTRL_PLLSEL); + __raw_writel(cfg, S3C2443_PHYCTRL); + + cfg = __raw_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; + __raw_writel(cfg, S3C2443_PHYPWR); + + cfg = __raw_readl(S3C2443_UCLKCON); + cfg |= (S3C2443_UCLKCON_DETECT_VBUS | S3C2443_UCLKCON_FUNC_CLKEN | + S3C2443_UCLKCON_TCLKEN); + __raw_writel(cfg, S3C2443_UCLKCON); +} + +static void s3c_hsudc_uninit_phy(void) +{ + u32 cfg; + + cfg = __raw_readl(S3C2443_PWRCFG) & ~S3C2443_PWRCFG_USBPHY; + __raw_writel(cfg, S3C2443_PWRCFG); + + __raw_writel(S3C2443_PHYPWR_FSUSPEND, S3C2443_PHYPWR); + + cfg = __raw_readl(S3C2443_UCLKCON) & ~S3C2443_UCLKCON_FUNC_CLKEN; + __raw_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 = __raw_readl(hsudc->regs + S3C_BRCR); + while (count--) + *buf++ = (u16)__raw_readl(hsudc->regs + S3C_BR(0)); + + __raw_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; + int is_last; + u32 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; + + __raw_writel(length, hsep->dev->regs + S3C_BWCR); + for (count = 0; count < length; count += 2) + __raw_writel(*buf++, fifo); + + if (count != max) { + is_last = 1; + } else { + if (hsreq->req.length != hsreq->req.actual || hsreq->req.zero) + is_last = 0; + else + is_last = 1; + } + + 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; + u32 fifo = hsep->fifo; + u32 is_short = 0; + + offset = (ep_index(hsep)) ? S3C_ESR : S3C_EP0SR; + csr = __raw_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 = __raw_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)__raw_readl(fifo); + if (buflen) { + *buf++ = word; + buflen--; + } else { + hsreq->req.status = -EOVERFLOW; + } + } + + __raw_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 = __raw_readl((u32)hsudc->regs + S3C_ESR); + if (csr & S3C_ESR_STALL) { + __raw_writel(S3C_ESR_STALL, hsudc->regs + S3C_ESR); + return; + } + + if (csr & S3C_ESR_TX_SUCCESS) { + __raw_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 = __raw_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 = __raw_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; + } + __raw_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 = __raw_readl(hsudc->regs + S3C_EP0SR); + u32 ecr; + + if (csr & S3C_EP0SR_STALL) { + ecr = __raw_readl(hsudc->regs + S3C_EP0CR); + ecr &= ~(S3C_ECR_STALL | S3C_ECR_FLUSH); + __raw_writel(ecr, hsudc->regs + S3C_EP0CR); + + __raw_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) { + __raw_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; + + hsep = container_of(_ep, struct s3c_hsudc_ep, ep); + 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 = __raw_readl((u32)hsudc->regs + offset); + if (!(csr & S3C_ESR_TX_SUCCESS) && + (s3c_hsudc_write_fifo(hsep, hsreq) == 1)) + hsreq = 0; + } else { + csr = __raw_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 = (u32)(hsudc->regs + S3C_BR(epnum)); + hsep->desc = 0; + hsep->stopped = 0; + + set_index(hsudc, epnum); + __raw_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) +{ + __raw_writel(0xAA, hsudc->regs + S3C_EDR); + __raw_writel(1, hsudc->regs + S3C_EIER); + __raw_writel(0, hsudc->regs + S3C_TR); + __raw_writel(S3C_SCR_DTZIEN_EN | S3C_SCR_RRD_EN | S3C_SCR_SUS_EN | + S3C_SCR_RST_EN, hsudc->regs + S3C_SCR); + __raw_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 = __raw_readl(hsudc->regs + S3C_SSR); + ep_intr = __raw_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) + __raw_writel(S3C_SSR_VBUSON, hsudc->regs + S3C_SSR); + + if (sys_status & S3C_SSR_ERR) + __raw_writel(S3C_SSR_ERR, hsudc->regs + S3C_SSR); + + if (sys_status & S3C_SSR_SDE) { + __raw_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) { + __raw_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) { + __raw_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) { + __raw_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) { + __raw_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); + __raw_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_register_driver(struct usb_gadget_driver *driver) +{ + struct s3c_hsudc *hsudc = the_controller; + int ret; + + if (!driver + || (driver->speed != USB_SPEED_FULL && + driver->speed != USB_SPEED_HIGH) + || !driver->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 register gadget device"); + return ret; + } + + ret = driver->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_register_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-Sze3O3UU22JBDgjK7y7TUQ@public.gmane.org>"); +MODULE_LICENSE("GPL"); -- 1.6.6.rc2 -- 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] 16+ messages in thread
* [PATCH 3/4] USB: Gadget: Add Samsung S3C24XX USB High-Speed controller driver @ 2010-10-13 0:10 ` Sangbeom Kim 0 siblings, 0 replies; 16+ messages in thread From: Sangbeom Kim @ 2010-10-13 0:10 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> --- drivers/usb/gadget/Kconfig | 17 + drivers/usb/gadget/Makefile | 1 + drivers/usb/gadget/gadget_chips.h | 7 + drivers/usb/gadget/s3c-hsudc.c | 1329 +++++++++++++++++++++++++++++++++++++ 4 files changed, 1354 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 cd27f9b..235d4db 100644 --- a/drivers/usb/gadget/Kconfig +++ b/drivers/usb/gadget/Kconfig @@ -348,6 +348,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 + # # Controllers available in both integrated and discrete versions # diff --git a/drivers/usb/gadget/Makefile b/drivers/usb/gadget/Makefile index 27283df..76e3d67 100644 --- a/drivers/usb/gadget/Makefile +++ b/drivers/usb/gadget/Makefile @@ -27,6 +27,7 @@ obj-$(CONFIG_USB_R8A66597) += r8a66597-udc.o obj-$(CONFIG_USB_FSL_QE) += fsl_qe_udc.o obj-$(CONFIG_USB_CI13XXX) += ci13xxx_udc.o obj-$(CONFIG_USB_S3C_HSOTG) += s3c-hsotg.o +obj-$(CONFIG_USB_S3C_HSUDC) += s3c-hsudc.o obj-$(CONFIG_USB_LANGWELL) += langwell_udc.o # diff --git a/drivers/usb/gadget/gadget_chips.h b/drivers/usb/gadget/gadget_chips.h index e511fec..0b27f6f 100644 --- a/drivers/usb/gadget/gadget_chips.h +++ b/drivers/usb/gadget/gadget_chips.h @@ -142,6 +142,11 @@ #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 /** * usb_gadget_controller_number - support bcdDevice id convention @@ -200,6 +205,8 @@ static inline int usb_gadget_controller_number(struct usb_gadget *gadget) return 0x25; else if (gadget_is_s3c_hsotg(gadget)) return 0x26; + else if (gadget_is_s3c_hsudc(gadget)) + return 0x27; return -ENOENT; } diff --git a/drivers/usb/gadget/s3c-hsudc.c b/drivers/usb/gadget/s3c-hsudc.c new file mode 100644 index 0000000..ebc4a10 --- /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; + u32 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; + __raw_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 = __raw_readl(S3C2443_PWRCFG) | S3C2443_PWRCFG_USBPHY; + __raw_writel(cfg, S3C2443_PWRCFG); + + cfg = __raw_readl(S3C2443_URSTCON); + cfg |= (S3C2443_URSTCON_FUNCRST | S3C2443_URSTCON_PHYRST); + __raw_writel(cfg, S3C2443_URSTCON); + mdelay(1); + + cfg = __raw_readl(S3C2443_URSTCON); + cfg &= ~(S3C2443_URSTCON_FUNCRST | S3C2443_URSTCON_PHYRST); + __raw_writel(cfg, S3C2443_URSTCON); + + cfg = __raw_readl(S3C2443_PHYCTRL); + cfg &= ~(S3C2443_PHYCTRL_CLKSEL | S3C2443_PHYCTRL_DSPORT); + cfg |= (S3C2443_PHYCTRL_EXTCLK | S3C2443_PHYCTRL_PLLSEL); + __raw_writel(cfg, S3C2443_PHYCTRL); + + cfg = __raw_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; + __raw_writel(cfg, S3C2443_PHYPWR); + + cfg = __raw_readl(S3C2443_UCLKCON); + cfg |= (S3C2443_UCLKCON_DETECT_VBUS | S3C2443_UCLKCON_FUNC_CLKEN | + S3C2443_UCLKCON_TCLKEN); + __raw_writel(cfg, S3C2443_UCLKCON); +} + +static void s3c_hsudc_uninit_phy(void) +{ + u32 cfg; + + cfg = __raw_readl(S3C2443_PWRCFG) & ~S3C2443_PWRCFG_USBPHY; + __raw_writel(cfg, S3C2443_PWRCFG); + + __raw_writel(S3C2443_PHYPWR_FSUSPEND, S3C2443_PHYPWR); + + cfg = __raw_readl(S3C2443_UCLKCON) & ~S3C2443_UCLKCON_FUNC_CLKEN; + __raw_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 = __raw_readl(hsudc->regs + S3C_BRCR); + while (count--) + *buf++ = (u16)__raw_readl(hsudc->regs + S3C_BR(0)); + + __raw_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; + int is_last; + u32 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; + + __raw_writel(length, hsep->dev->regs + S3C_BWCR); + for (count = 0; count < length; count += 2) + __raw_writel(*buf++, fifo); + + if (count != max) { + is_last = 1; + } else { + if (hsreq->req.length != hsreq->req.actual || hsreq->req.zero) + is_last = 0; + else + is_last = 1; + } + + 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; + u32 fifo = hsep->fifo; + u32 is_short = 0; + + offset = (ep_index(hsep)) ? S3C_ESR : S3C_EP0SR; + csr = __raw_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 = __raw_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)__raw_readl(fifo); + if (buflen) { + *buf++ = word; + buflen--; + } else { + hsreq->req.status = -EOVERFLOW; + } + } + + __raw_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 = __raw_readl((u32)hsudc->regs + S3C_ESR); + if (csr & S3C_ESR_STALL) { + __raw_writel(S3C_ESR_STALL, hsudc->regs + S3C_ESR); + return; + } + + if (csr & S3C_ESR_TX_SUCCESS) { + __raw_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 = __raw_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 = __raw_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; + } + __raw_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 = __raw_readl(hsudc->regs + S3C_EP0SR); + u32 ecr; + + if (csr & S3C_EP0SR_STALL) { + ecr = __raw_readl(hsudc->regs + S3C_EP0CR); + ecr &= ~(S3C_ECR_STALL | S3C_ECR_FLUSH); + __raw_writel(ecr, hsudc->regs + S3C_EP0CR); + + __raw_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) { + __raw_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; + + hsep = container_of(_ep, struct s3c_hsudc_ep, ep); + 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 = __raw_readl((u32)hsudc->regs + offset); + if (!(csr & S3C_ESR_TX_SUCCESS) && + (s3c_hsudc_write_fifo(hsep, hsreq) == 1)) + hsreq = 0; + } else { + csr = __raw_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 = (u32)(hsudc->regs + S3C_BR(epnum)); + hsep->desc = 0; + hsep->stopped = 0; + + set_index(hsudc, epnum); + __raw_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) +{ + __raw_writel(0xAA, hsudc->regs + S3C_EDR); + __raw_writel(1, hsudc->regs + S3C_EIER); + __raw_writel(0, hsudc->regs + S3C_TR); + __raw_writel(S3C_SCR_DTZIEN_EN | S3C_SCR_RRD_EN | S3C_SCR_SUS_EN | + S3C_SCR_RST_EN, hsudc->regs + S3C_SCR); + __raw_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 = __raw_readl(hsudc->regs + S3C_SSR); + ep_intr = __raw_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) + __raw_writel(S3C_SSR_VBUSON, hsudc->regs + S3C_SSR); + + if (sys_status & S3C_SSR_ERR) + __raw_writel(S3C_SSR_ERR, hsudc->regs + S3C_SSR); + + if (sys_status & S3C_SSR_SDE) { + __raw_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) { + __raw_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) { + __raw_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) { + __raw_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) { + __raw_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); + __raw_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_register_driver(struct usb_gadget_driver *driver) +{ + struct s3c_hsudc *hsudc = the_controller; + int ret; + + if (!driver + || (driver->speed != USB_SPEED_FULL && + driver->speed != USB_SPEED_HIGH) + || !driver->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 register gadget device"); + return ret; + } + + ret = driver->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_register_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.6.6.rc2 ^ permalink raw reply related [flat|nested] 16+ messages in thread
* Re: [PATCH 3/4] USB: Gadget: Add Samsung S3C24XX USB High-Speed controller driver 2010-10-13 0:10 ` Sangbeom Kim @ 2010-10-21 22:36 ` Ben Dooks -1 siblings, 0 replies; 16+ messages in thread From: Ben Dooks @ 2010-10-21 22:36 UTC (permalink / raw) To: Sangbeom Kim Cc: linux-usb, linux-arm-kernel, linux-samsung-soc, kgene.kim, Thomas Abraham, ben-linux On 13/10/10 01:10, Sangbeom Kim 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> > --- > drivers/usb/gadget/Kconfig | 17 + > drivers/usb/gadget/Makefile | 1 + > drivers/usb/gadget/gadget_chips.h | 7 + > drivers/usb/gadget/s3c-hsudc.c | 1329 +++++++++++++++++++++++++++++++++++++ > 4 files changed, 1354 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 cd27f9b..235d4db 100644 > --- a/drivers/usb/gadget/Kconfig > +++ b/drivers/usb/gadget/Kconfig > @@ -348,6 +348,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 > + > # > # Controllers available in both integrated and discrete versions > # > diff --git a/drivers/usb/gadget/Makefile b/drivers/usb/gadget/Makefile > index 27283df..76e3d67 100644 > --- a/drivers/usb/gadget/Makefile > +++ b/drivers/usb/gadget/Makefile > @@ -27,6 +27,7 @@ obj-$(CONFIG_USB_R8A66597) += r8a66597-udc.o > obj-$(CONFIG_USB_FSL_QE) += fsl_qe_udc.o > obj-$(CONFIG_USB_CI13XXX) += ci13xxx_udc.o > obj-$(CONFIG_USB_S3C_HSOTG) += s3c-hsotg.o > +obj-$(CONFIG_USB_S3C_HSUDC) += s3c-hsudc.o > obj-$(CONFIG_USB_LANGWELL) += langwell_udc.o > > # > diff --git a/drivers/usb/gadget/gadget_chips.h b/drivers/usb/gadget/gadget_chips.h > index e511fec..0b27f6f 100644 > --- a/drivers/usb/gadget/gadget_chips.h > +++ b/drivers/usb/gadget/gadget_chips.h > @@ -142,6 +142,11 @@ > #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 > > /** > * usb_gadget_controller_number - support bcdDevice id convention > @@ -200,6 +205,8 @@ static inline int usb_gadget_controller_number(struct usb_gadget *gadget) > return 0x25; > else if (gadget_is_s3c_hsotg(gadget)) > return 0x26; > + else if (gadget_is_s3c_hsudc(gadget)) > + return 0x27; > return -ENOENT; > } > > diff --git a/drivers/usb/gadget/s3c-hsudc.c b/drivers/usb/gadget/s3c-hsudc.c > new file mode 100644 > index 0000000..ebc4a10 > --- /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> hmm, do you really need to include the clock registers? surely the clk_ api should be doing the work for you. > +#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 */ hmm, the formatting is a bit non-standard. > + > +static inline void __orr32(void __iomem *ptr, u32 val) > +{ > + writel(readl(ptr) | val, ptr); > +} > + > +static void s3c_hsudc_init_phy(void) > +{ > + u32 cfg; > + > + cfg = __raw_readl(S3C2443_PWRCFG) | S3C2443_PWRCFG_USBPHY; > + __raw_writel(cfg, S3C2443_PWRCFG); > + > + cfg = __raw_readl(S3C2443_URSTCON); > + cfg |= (S3C2443_URSTCON_FUNCRST | S3C2443_URSTCON_PHYRST); > + __raw_writel(cfg, S3C2443_URSTCON); > + mdelay(1); > + > + cfg = __raw_readl(S3C2443_URSTCON); > + cfg &= ~(S3C2443_URSTCON_FUNCRST | S3C2443_URSTCON_PHYRST); > + __raw_writel(cfg, S3C2443_URSTCON); > + > + cfg = __raw_readl(S3C2443_PHYCTRL); > + cfg &= ~(S3C2443_PHYCTRL_CLKSEL | S3C2443_PHYCTRL_DSPORT); > + cfg |= (S3C2443_PHYCTRL_EXTCLK | S3C2443_PHYCTRL_PLLSEL); > + __raw_writel(cfg, S3C2443_PHYCTRL); > + > + cfg = __raw_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; > + __raw_writel(cfg, S3C2443_PHYPWR); > + > + cfg = __raw_readl(S3C2443_UCLKCON); > + cfg |= (S3C2443_UCLKCON_DETECT_VBUS | S3C2443_UCLKCON_FUNC_CLKEN | > + S3C2443_UCLKCON_TCLKEN); > + __raw_writel(cfg, S3C2443_UCLKCON); > +} > + > +static void s3c_hsudc_uninit_phy(void) > +{ > + u32 cfg; > + > + cfg = __raw_readl(S3C2443_PWRCFG) & ~S3C2443_PWRCFG_USBPHY; > + __raw_writel(cfg, S3C2443_PWRCFG); > + > + __raw_writel(S3C2443_PHYPWR_FSUSPEND, S3C2443_PHYPWR); > + > + cfg = __raw_readl(S3C2443_UCLKCON) & ~S3C2443_UCLKCON_FUNC_CLKEN; > + __raw_writel(cfg, S3C2443_UCLKCON); > +} I suppose this has to be here. > +/** > + * 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 = __raw_readl(hsudc->regs + S3C_BRCR); > + while (count--) > + *buf++ = (u16)__raw_readl(hsudc->regs + S3C_BR(0)); hmm, __raw_readl() shouldn't be being used for ioremaped addresses? > + __raw_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; > + int is_last; this could be a bool. > + u32 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; > + > + __raw_writel(length, hsep->dev->regs + S3C_BWCR); > + for (count = 0; count < length; count += 2) > + __raw_writel(*buf++, fifo); > + > + if (count != max) { > + is_last = 1; > + } else { > + if (hsreq->req.length != hsreq->req.actual || hsreq->req.zero) > + is_last = 0; > + else > + is_last = 1; > + } > + > + 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; > + u32 fifo = hsep->fifo; if this is an ioaddr, it should be void __iomem *. > + u32 is_short = 0; > + > + offset = (ep_index(hsep)) ? S3C_ESR : S3C_EP0SR; > + csr = __raw_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 = __raw_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)__raw_readl(fifo); > + if (buflen) { > + *buf++ = word; > + buflen--; > + } else { > + hsreq->req.status = -EOVERFLOW; > + } > + } > + > + __raw_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_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; > + > + hsep = container_of(_ep, struct s3c_hsudc_ep, ep); hmm, you've got hsep assigned twice? > + 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; > +} > + local_irq_disable(); > + > + disable_irq(hsudc->irq); > + local_irq_enable(); hmm, why are you disabling the irqs around this call? > + return 0; ^ permalink raw reply [flat|nested] 16+ messages in thread
* [PATCH 3/4] USB: Gadget: Add Samsung S3C24XX USB High-Speed controller driver @ 2010-10-21 22:36 ` Ben Dooks 0 siblings, 0 replies; 16+ messages in thread From: Ben Dooks @ 2010-10-21 22:36 UTC (permalink / raw) To: linux-arm-kernel On 13/10/10 01:10, Sangbeom Kim 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> > --- > drivers/usb/gadget/Kconfig | 17 + > drivers/usb/gadget/Makefile | 1 + > drivers/usb/gadget/gadget_chips.h | 7 + > drivers/usb/gadget/s3c-hsudc.c | 1329 +++++++++++++++++++++++++++++++++++++ > 4 files changed, 1354 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 cd27f9b..235d4db 100644 > --- a/drivers/usb/gadget/Kconfig > +++ b/drivers/usb/gadget/Kconfig > @@ -348,6 +348,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 > + > # > # Controllers available in both integrated and discrete versions > # > diff --git a/drivers/usb/gadget/Makefile b/drivers/usb/gadget/Makefile > index 27283df..76e3d67 100644 > --- a/drivers/usb/gadget/Makefile > +++ b/drivers/usb/gadget/Makefile > @@ -27,6 +27,7 @@ obj-$(CONFIG_USB_R8A66597) += r8a66597-udc.o > obj-$(CONFIG_USB_FSL_QE) += fsl_qe_udc.o > obj-$(CONFIG_USB_CI13XXX) += ci13xxx_udc.o > obj-$(CONFIG_USB_S3C_HSOTG) += s3c-hsotg.o > +obj-$(CONFIG_USB_S3C_HSUDC) += s3c-hsudc.o > obj-$(CONFIG_USB_LANGWELL) += langwell_udc.o > > # > diff --git a/drivers/usb/gadget/gadget_chips.h b/drivers/usb/gadget/gadget_chips.h > index e511fec..0b27f6f 100644 > --- a/drivers/usb/gadget/gadget_chips.h > +++ b/drivers/usb/gadget/gadget_chips.h > @@ -142,6 +142,11 @@ > #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 > > /** > * usb_gadget_controller_number - support bcdDevice id convention > @@ -200,6 +205,8 @@ static inline int usb_gadget_controller_number(struct usb_gadget *gadget) > return 0x25; > else if (gadget_is_s3c_hsotg(gadget)) > return 0x26; > + else if (gadget_is_s3c_hsudc(gadget)) > + return 0x27; > return -ENOENT; > } > > diff --git a/drivers/usb/gadget/s3c-hsudc.c b/drivers/usb/gadget/s3c-hsudc.c > new file mode 100644 > index 0000000..ebc4a10 > --- /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> hmm, do you really need to include the clock registers? surely the clk_ api should be doing the work for you. > +#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 */ hmm, the formatting is a bit non-standard. > + > +static inline void __orr32(void __iomem *ptr, u32 val) > +{ > + writel(readl(ptr) | val, ptr); > +} > + > +static void s3c_hsudc_init_phy(void) > +{ > + u32 cfg; > + > + cfg = __raw_readl(S3C2443_PWRCFG) | S3C2443_PWRCFG_USBPHY; > + __raw_writel(cfg, S3C2443_PWRCFG); > + > + cfg = __raw_readl(S3C2443_URSTCON); > + cfg |= (S3C2443_URSTCON_FUNCRST | S3C2443_URSTCON_PHYRST); > + __raw_writel(cfg, S3C2443_URSTCON); > + mdelay(1); > + > + cfg = __raw_readl(S3C2443_URSTCON); > + cfg &= ~(S3C2443_URSTCON_FUNCRST | S3C2443_URSTCON_PHYRST); > + __raw_writel(cfg, S3C2443_URSTCON); > + > + cfg = __raw_readl(S3C2443_PHYCTRL); > + cfg &= ~(S3C2443_PHYCTRL_CLKSEL | S3C2443_PHYCTRL_DSPORT); > + cfg |= (S3C2443_PHYCTRL_EXTCLK | S3C2443_PHYCTRL_PLLSEL); > + __raw_writel(cfg, S3C2443_PHYCTRL); > + > + cfg = __raw_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; > + __raw_writel(cfg, S3C2443_PHYPWR); > + > + cfg = __raw_readl(S3C2443_UCLKCON); > + cfg |= (S3C2443_UCLKCON_DETECT_VBUS | S3C2443_UCLKCON_FUNC_CLKEN | > + S3C2443_UCLKCON_TCLKEN); > + __raw_writel(cfg, S3C2443_UCLKCON); > +} > + > +static void s3c_hsudc_uninit_phy(void) > +{ > + u32 cfg; > + > + cfg = __raw_readl(S3C2443_PWRCFG) & ~S3C2443_PWRCFG_USBPHY; > + __raw_writel(cfg, S3C2443_PWRCFG); > + > + __raw_writel(S3C2443_PHYPWR_FSUSPEND, S3C2443_PHYPWR); > + > + cfg = __raw_readl(S3C2443_UCLKCON) & ~S3C2443_UCLKCON_FUNC_CLKEN; > + __raw_writel(cfg, S3C2443_UCLKCON); > +} I suppose this has to be here. > +/** > + * 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 = __raw_readl(hsudc->regs + S3C_BRCR); > + while (count--) > + *buf++ = (u16)__raw_readl(hsudc->regs + S3C_BR(0)); hmm, __raw_readl() shouldn't be being used for ioremaped addresses? > + __raw_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; > + int is_last; this could be a bool. > + u32 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; > + > + __raw_writel(length, hsep->dev->regs + S3C_BWCR); > + for (count = 0; count < length; count += 2) > + __raw_writel(*buf++, fifo); > + > + if (count != max) { > + is_last = 1; > + } else { > + if (hsreq->req.length != hsreq->req.actual || hsreq->req.zero) > + is_last = 0; > + else > + is_last = 1; > + } > + > + 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; > + u32 fifo = hsep->fifo; if this is an ioaddr, it should be void __iomem *. > + u32 is_short = 0; > + > + offset = (ep_index(hsep)) ? S3C_ESR : S3C_EP0SR; > + csr = __raw_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 = __raw_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)__raw_readl(fifo); > + if (buflen) { > + *buf++ = word; > + buflen--; > + } else { > + hsreq->req.status = -EOVERFLOW; > + } > + } > + > + __raw_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_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; > + > + hsep = container_of(_ep, struct s3c_hsudc_ep, ep); hmm, you've got hsep assigned twice? > + 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; > +} > + local_irq_disable(); > + > + disable_irq(hsudc->irq); > + local_irq_enable(); hmm, why are you disabling the irqs around this call? > + return 0; ^ permalink raw reply [flat|nested] 16+ messages in thread
* [PATCH 4/4] ARM: S3C2416: Add support for USB 2.0 High-Speed gadget controller 2010-10-13 0:10 ` Sangbeom Kim @ 2010-10-13 0:10 ` Sangbeom Kim -1 siblings, 0 replies; 16+ messages in thread From: Sangbeom Kim @ 2010-10-13 0:10 UTC (permalink / raw) To: linux-usb-u79uwXL29TY76Z2rM5mHXA, linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r, linux-samsung-soc-u79uwXL29TY76Z2rM5mHXA Cc: kgene.kim-Sze3O3UU22JBDgjK7y7TUQ, ben-linux-elnMNo+KYs3YtjvyW6yDsg, Thomas Abraham, Sangbeom Kim From: Thomas Abraham <thomas.ab-Sze3O3UU22JBDgjK7y7TUQ@public.gmane.org> Add support for USB 2.0 High-Speed gadget controller driver for Samsung's S3C2416 processor. Signed-off-by: Thomas Abraham <thomas.ab-Sze3O3UU22JBDgjK7y7TUQ@public.gmane.org> Signed-off-by: Sangbeom Kim <sbkim73-Sze3O3UU22JBDgjK7y7TUQ@public.gmane.org> --- 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 5fc3f67..429e720 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> @@ -46,6 +48,7 @@ #include <plat/devs.h> #include <plat/cpu.h> #include <plat/nand.h> +#include <plat/udc.h> #include <plat/regs-fb-v4.h> #include <plat/fb.h> @@ -113,6 +116,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 */ @@ -166,6 +190,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) @@ -180,6 +205,8 @@ static void __init smdk2416_machine_init(void) s3c_i2c0_set_platdata(NULL); s3c_fb_set_platdata(&smdk2416_fb_platdata); + s3c24xx_hsudc_set_platdata(&smdk2416_hsudc_platdata); + gpio_request(S3C2410_GPB(4), "USBHost Power"); gpio_direction_output(S3C2410_GPB(4), 1); -- 1.6.6.rc2 -- 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] 16+ messages in thread
* [PATCH 4/4] ARM: S3C2416: Add support for USB 2.0 High-Speed gadget controller @ 2010-10-13 0:10 ` Sangbeom Kim 0 siblings, 0 replies; 16+ messages in thread From: Sangbeom Kim @ 2010-10-13 0:10 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 5fc3f67..429e720 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> @@ -46,6 +48,7 @@ #include <plat/devs.h> #include <plat/cpu.h> #include <plat/nand.h> +#include <plat/udc.h> #include <plat/regs-fb-v4.h> #include <plat/fb.h> @@ -113,6 +116,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 */ @@ -166,6 +190,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) @@ -180,6 +205,8 @@ static void __init smdk2416_machine_init(void) s3c_i2c0_set_platdata(NULL); s3c_fb_set_platdata(&smdk2416_fb_platdata); + s3c24xx_hsudc_set_platdata(&smdk2416_hsudc_platdata); + gpio_request(S3C2410_GPB(4), "USBHost Power"); gpio_direction_output(S3C2410_GPB(4), 1); -- 1.6.6.rc2 ^ permalink raw reply related [flat|nested] 16+ messages in thread
* Re: [PATCH 0/4] USB: gadget: Add Samsung S3C24xx USB HS controller driver 2010-10-13 0:10 ` Sangbeom Kim @ 2010-10-21 22:37 ` Ben Dooks -1 siblings, 0 replies; 16+ messages in thread From: Ben Dooks @ 2010-10-21 22:37 UTC (permalink / raw) To: Sangbeom Kim Cc: linux-usb, linux-arm-kernel, linux-samsung-soc, kgene.kim, ben-linux On 13/10/10 01:10, Sangbeom Kim wrote: > 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 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 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. -- Ben ^ permalink raw reply [flat|nested] 16+ messages in thread
* [PATCH 0/4] USB: gadget: Add Samsung S3C24xx USB HS controller driver @ 2010-10-21 22:37 ` Ben Dooks 0 siblings, 0 replies; 16+ messages in thread From: Ben Dooks @ 2010-10-21 22:37 UTC (permalink / raw) To: linux-arm-kernel On 13/10/10 01:10, Sangbeom Kim wrote: > 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 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 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. -- Ben ^ permalink raw reply [flat|nested] 16+ messages in thread
* Re: [PATCH 0/4] USB: gadget: Add Samsung S3C24xx USB HS controller driver 2010-10-21 22:37 ` Ben Dooks @ 2010-10-22 6:15 ` Thomas Abraham -1 siblings, 0 replies; 16+ messages in thread From: Thomas Abraham @ 2010-10-22 6:15 UTC (permalink / raw) To: Ben Dooks Cc: Sangbeom Kim, linux-samsung-soc, linux-usb, kgene.kim, linux-arm-kernel On Fri, Oct 22, 2010 at 4:07 AM, Ben Dooks <ben-linux@fluff.org> wrote: > On 13/10/10 01:10, Sangbeom Kim wrote: >> 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 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 > > 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. Hi Ben, Thanks for your review. I will update patch 3/4 as per your comments. Regards, Thomas. > > -- > Ben > > _______________________________________________ > linux-arm-kernel mailing list > linux-arm-kernel@lists.infradead.org > http://lists.infradead.org/mailman/listinfo/linux-arm-kernel > ^ permalink raw reply [flat|nested] 16+ messages in thread
* [PATCH 0/4] USB: gadget: Add Samsung S3C24xx USB HS controller driver @ 2010-10-22 6:15 ` Thomas Abraham 0 siblings, 0 replies; 16+ messages in thread From: Thomas Abraham @ 2010-10-22 6:15 UTC (permalink / raw) To: linux-arm-kernel On Fri, Oct 22, 2010 at 4:07 AM, Ben Dooks <ben-linux@fluff.org> wrote: > On 13/10/10 01:10, Sangbeom Kim wrote: >> 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 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 > > 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. Hi Ben, Thanks for your review. I will update patch 3/4 as per your comments. Regards, Thomas. > > -- > Ben > > _______________________________________________ > linux-arm-kernel mailing list > linux-arm-kernel at lists.infradead.org > http://lists.infradead.org/mailman/listinfo/linux-arm-kernel > ^ permalink raw reply [flat|nested] 16+ messages in thread
end of thread, other threads:[~2010-10-22 6:15 UTC | newest]
Thread overview: 16+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2010-10-13 0:10 [PATCH 0/4] USB: gadget: Add Samsung S3C24xx USB HS controller driver Sangbeom Kim
2010-10-13 0:10 ` Sangbeom Kim
[not found] ` <1286928627-22824-1-git-send-email-sbkim73-Sze3O3UU22JBDgjK7y7TUQ@public.gmane.org>
2010-10-13 0:10 ` [PATCH 1/4] ARM: S3C2416: Add USB Phy register definitions Sangbeom Kim
2010-10-13 0:10 ` Sangbeom Kim
2010-10-13 0:10 ` [PATCH 2/4] ARM: S3C24XX: Add plaform device definition for USB High-Speed gadget controller Sangbeom Kim
2010-10-13 0:10 ` Sangbeom Kim
2010-10-13 0:10 ` [PATCH 3/4] USB: Gadget: Add Samsung S3C24XX USB High-Speed controller driver Sangbeom Kim
2010-10-13 0:10 ` Sangbeom Kim
2010-10-21 22:36 ` Ben Dooks
2010-10-21 22:36 ` Ben Dooks
2010-10-13 0:10 ` [PATCH 4/4] ARM: S3C2416: Add support for USB 2.0 High-Speed gadget controller Sangbeom Kim
2010-10-13 0:10 ` Sangbeom Kim
2010-10-21 22:37 ` [PATCH 0/4] USB: gadget: Add Samsung S3C24xx USB HS controller driver Ben Dooks
2010-10-21 22:37 ` Ben Dooks
2010-10-22 6:15 ` Thomas Abraham
2010-10-22 6:15 ` Thomas Abraham
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.