diff options
40 files changed, 5508 insertions, 755 deletions
| @@ -286,6 +286,7 @@ LIBS += drivers/usb/gadget/libusb_gadget.o  LIBS += drivers/usb/host/libusb_host.o  LIBS += drivers/usb/musb/libusb_musb.o  LIBS += drivers/usb/phy/libusb_phy.o +LIBS += drivers/usb/ulpi/libusb_ulpi.o  LIBS += drivers/video/libvideo.o  LIBS += drivers/watchdog/libwatchdog.o  LIBS += common/libcommon.o diff --git a/arch/arm/cpu/armv7/mx5/clock.c b/arch/arm/cpu/armv7/mx5/clock.c index 933ce05b7..e92f10623 100644 --- a/arch/arm/cpu/armv7/mx5/clock.c +++ b/arch/arm/cpu/armv7/mx5/clock.c @@ -50,6 +50,78 @@ struct mxc_pll_reg *mxc_plls[PLL_CLOCKS] = {  struct mxc_ccm_reg *mxc_ccm = (struct mxc_ccm_reg *)MXC_CCM_BASE; +void set_usboh3_clk(void) +{ +	unsigned int reg; + +	reg = readl(&mxc_ccm->cscmr1) & +		 ~MXC_CCM_CSCMR1_USBOH3_CLK_SEL_MASK; +	reg |= 1 << MXC_CCM_CSCMR1_USBOH3_CLK_SEL_OFFSET; +	writel(reg, &mxc_ccm->cscmr1); + +	reg = readl(&mxc_ccm->cscdr1); +	reg &= ~MXC_CCM_CSCDR1_USBOH3_CLK_PODF_MASK; +	reg &= ~MXC_CCM_CSCDR1_USBOH3_CLK_PRED_MASK; +	reg |= 4 << MXC_CCM_CSCDR1_USBOH3_CLK_PRED_OFFSET; +	reg |= 1 << MXC_CCM_CSCDR1_USBOH3_CLK_PODF_OFFSET; + +	writel(reg, &mxc_ccm->cscdr1); +} + +void enable_usboh3_clk(unsigned char enable) +{ +	unsigned int reg; + +	reg = readl(&mxc_ccm->CCGR2); +	if (enable) +		reg |= 1 << MXC_CCM_CCGR2_CG14_OFFSET; +	else +		reg &= ~(1 << MXC_CCM_CCGR2_CG14_OFFSET); +	writel(reg, &mxc_ccm->CCGR2); +} + +void set_usb_phy1_clk(void) +{ +	unsigned int reg; + +	reg = readl(&mxc_ccm->cscmr1); +	reg &= ~MXC_CCM_CSCMR1_USB_PHY_CLK_SEL; +	writel(reg, &mxc_ccm->cscmr1); +} + +void enable_usb_phy1_clk(unsigned char enable) +{ +	unsigned int reg; + +	reg = readl(&mxc_ccm->CCGR4); +	if (enable) +		reg |= 1 << MXC_CCM_CCGR4_CG5_OFFSET; +	else +		reg &= ~(1 << MXC_CCM_CCGR4_CG5_OFFSET); +	writel(reg, &mxc_ccm->CCGR4); +} + +void set_usb_phy2_clk(void) +{ +	unsigned int reg; + +	reg = readl(&mxc_ccm->cscmr1); +	reg &= ~MXC_CCM_CSCMR1_USB_PHY_CLK_SEL; +	writel(reg, &mxc_ccm->cscmr1); +} + +void enable_usb_phy2_clk(unsigned char enable) +{ +	unsigned int reg; + +	reg = readl(&mxc_ccm->CCGR4); +	if (enable) +		reg |= 1 << MXC_CCM_CCGR4_CG6_OFFSET; +	else +		reg &= ~(1 << MXC_CCM_CCGR4_CG6_OFFSET); +	writel(reg, &mxc_ccm->CCGR4); +} +  /*   * Calculate the frequency of PLLn.   */ diff --git a/arch/arm/cpu/pxa/usb.c b/arch/arm/cpu/pxa/usb.c index 83022e2e5..307fc6cee 100644 --- a/arch/arm/cpu/pxa/usb.c +++ b/arch/arm/cpu/pxa/usb.c @@ -55,7 +55,7 @@ int usb_cpu_init(void)  	while (readl(UHCHR) & UHCHR_FSBIR)  		udelay(1); -#if defined(CONFIG_CPU_MONAHANS) +#if defined(CONFIG_CPU_MONAHANS) || defined(CONFIG_PXA27X)  	writel(readl(UHCHR) & ~UHCHR_SSEP0, UHCHR);  #endif  #if defined(CONFIG_CPU_PXA27X) @@ -72,10 +72,10 @@ int usb_cpu_stop(void)  	udelay(11);  	writel(readl(UHCHR) & ~UHCHR_FHR, UHCHR); -	writel(readl(UHCCOMS) | UHCHR_FHR, UHCCOMS); +	writel(readl(UHCCOMS) | UHCCOMS_HCR, UHCCOMS);  	udelay(10); -#if defined(CONFIG_CPU_MONAHANS) +#if defined(CONFIG_CPU_MONAHANS) || defined(CONFIG_PXA27X)  	writel(readl(UHCHR) | UHCHR_SSEP0, UHCHR);  #endif  #if defined(CONFIG_CPU_PXA27X) diff --git a/arch/arm/include/asm/arch-mx5/clock.h b/arch/arm/include/asm/arch-mx5/clock.h index 1f8a537a5..ea972a398 100644 --- a/arch/arm/include/asm/arch-mx5/clock.h +++ b/arch/arm/include/asm/arch-mx5/clock.h @@ -40,4 +40,9 @@ u32 imx_get_uartclk(void);  u32 imx_get_fecclk(void);  unsigned int mxc_get_clock(enum mxc_clock clk); +void set_usb_phy2_clk(void); +void enable_usb_phy2_clk(unsigned char enable); +void set_usboh3_clk(void); +void enable_usboh3_clk(unsigned char enable); +  #endif /* __ASM_ARCH_CLOCK_H */ diff --git a/arch/arm/include/asm/arch-mx5/crm_regs.h b/arch/arm/include/asm/arch-mx5/crm_regs.h index fcc0e36fa..bdeafbc0d 100644 --- a/arch/arm/include/asm/arch-mx5/crm_regs.h +++ b/arch/arm/include/asm/arch-mx5/crm_regs.h @@ -195,7 +195,10 @@ struct mxc_ccm_reg {  /* Define the bits in register CCGRx */  #define MXC_CCM_CCGR_CG_MASK				0x3 +#define MXC_CCM_CCGR4_CG5_OFFSET			10 +#define MXC_CCM_CCGR4_CG6_OFFSET			12  #define MXC_CCM_CCGR5_CG5_OFFSET			10 +#define MXC_CCM_CCGR2_CG14_OFFSET			28  /* Define the bits in register CLPCR */  #define MXC_CCM_CLPCR_BYPASS_IPU_LPM_HS                 (0x1 << 18) diff --git a/arch/arm/include/asm/arch-pxa/pxa-regs.h b/arch/arm/include/asm/arch-pxa/pxa-regs.h index 8527c68c8..b81b42c07 100644 --- a/arch/arm/include/asm/arch-pxa/pxa-regs.h +++ b/arch/arm/include/asm/arch-pxa/pxa-regs.h @@ -645,7 +645,7 @@ typedef void		(*ExcpHndlr) (void) ;  #define UDCOTGICR_IEIDR		(1 << 1)	/* OTG ID Change Rising Edge Interrupt Enable */  #define UDCOTGICR_IEIDF		(1 << 0)	/* OTG ID Change Falling Edge Interrupt Enable */ -#define UDCCSN(x)	__REG2(0x40600100, (x) << 2) +#define UDCCSN(x)	(0x40600100 + ((x) << 2))  #define UDCCSR0		0x40600100 /* UDC Control/Status register - Endpoint 0 */  #define UDCCSR0_SA	(1 << 7)	/* Setup Active */ @@ -693,7 +693,7 @@ typedef void		(*ExcpHndlr) (void) ;  #define UDCCSR_PC	(1 << 1)	/* Packet Complete */  #define UDCCSR_FS	(1 << 0)	/* FIFO needs service */ -#define UDCBCN(x)	__REG2(0x40600200, (x)<<2) +#define UDCBCN(x)	(0x40600200 + ((x) << 2))  #define UDCBCR0         0x40600200 /* Byte Count Register - EP0 */  #define UDCBCRA         0x40600204 /* Byte Count Register - EPA */  #define UDCBCRB         0x40600208 /* Byte Count Register - EPB */ @@ -719,7 +719,7 @@ typedef void		(*ExcpHndlr) (void) ;  #define UDCBCRW         0x40600258 /* Byte Count Register - EPW */  #define UDCBCRX         0x4060025C /* Byte Count Register - EPX */ -#define UDCDN(x)	__REG2(0x40600300, (x)<<2) +#define UDCDN(x)	(0x40600300 + ((x) << 2))  #define UDCDR0          0x40600300 /* Data Register - EP0 */  #define UDCDRA          0x40600304 /* Data Register - EPA */  #define UDCDRB          0x40600308 /* Data Register - EPB */ @@ -745,7 +745,7 @@ typedef void		(*ExcpHndlr) (void) ;  #define UDCDRW          0x40600358 /* Data Register - EPW */  #define UDCDRX          0x4060035C /* Data Register - EPX */ -#define UDCCN(x)	__REG2(0x40600400, (x)<<2) +#define UDCCN(x)	(0x40600400 + ((x) << 2))  #define UDCCRA          0x40600404 /* Configuration register EPA */  #define UDCCRB          0x40600408 /* Configuration register EPB */  #define UDCCRC          0x4060040C /* Configuration register EPC */ @@ -835,6 +835,8 @@ typedef void		(*ExcpHndlr) (void) ;  #define UHCHIE		0x4C000068  #define UHCHIT		0x4C00006C +#define UHCCOMS_HCR	(1<<0) +  #define UHCHR_FSBIR	(1<<0)  #define UHCHR_FHR	(1<<1)  #define UHCHR_CGR	(1<<2) diff --git a/arch/arm/include/asm/arch-s5pc1xx/cpu.h b/arch/arm/include/asm/arch-s5pc1xx/cpu.h index e74959fe2..e699fc47c 100644 --- a/arch/arm/include/asm/arch-s5pc1xx/cpu.h +++ b/arch/arm/include/asm/arch-s5pc1xx/cpu.h @@ -55,6 +55,10 @@  #define S5PC110_VIC1_BASE	0xF2100000  #define S5PC110_VIC2_BASE	0xF2200000  #define S5PC110_VIC3_BASE	0xF2300000 +#define S5PC110_OTG_BASE	0xEC000000 +#define S5PC110_PHY_BASE	0xEC100000 +#define S5PC110_USB_PHY_CONTROL 0xE010E80C +  #ifndef __ASSEMBLY__  #include <asm/io.h> diff --git a/board/efikamx/Makefile b/board/efikamx/Makefile index bd1056e00..fdd188ecf 100644 --- a/board/efikamx/Makefile +++ b/board/efikamx/Makefile @@ -29,6 +29,10 @@ LIB	= $(obj)lib$(BOARD).o  COBJS	:= efikamx.o +ifdef	CONFIG_CMD_USB +COBJS	+= efikamx-usb.o +endif +  SRCS	:= $(SOBJS:.o=.S) $(COBJS:.o=.c)  OBJS	:= $(addprefix $(obj),$(COBJS))  SOBJS	:= $(addprefix $(obj),$(SOBJS)) diff --git a/board/efikamx/efikamx-usb.c b/board/efikamx/efikamx-usb.c new file mode 100644 index 000000000..840bd9aaa --- /dev/null +++ b/board/efikamx/efikamx-usb.c @@ -0,0 +1,210 @@ +/* + * Copyright (C) 2010 Marek Vasut <marek.vasut@gmail.com> + * + * (C) Copyright 2009 Freescale Semiconductor, Inc. + * + * See file CREDITS for list of people who contributed to this + * project. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ + +#include <common.h> +#include <usb.h> +#include <asm/io.h> +#include <asm/arch/imx-regs.h> +#include <asm/arch/mx5x_pins.h> +#include <asm/arch/iomux.h> +#include <asm/gpio.h> +#include <usb/ehci-fsl.h> +#include <usb/ulpi.h> +#include <errno.h> + +#include "../../drivers/usb/host/ehci.h" + +/* USB pin configuration */ +#define USB_PAD_CONFIG	(PAD_CTL_PKE_ENABLE | PAD_CTL_SRE_FAST | \ +			PAD_CTL_DRV_HIGH | PAD_CTL_100K_PU | \ +			PAD_CTL_HYS_ENABLE | PAD_CTL_PUE_PULL) + +/* + * Configure the USB H1 and USB H2 IOMUX + */ +void setup_iomux_usb(void) +{ +	setup_iomux_usb_h1(); + +	if (machine_is_efikasb()) +		setup_iomux_usb_h2(); + +	/* USB PHY reset */ +	mxc_request_iomux(MX51_PIN_EIM_D27, IOMUX_CONFIG_ALT1); +	mxc_iomux_set_pad(MX51_PIN_EIM_D27, PAD_CTL_PKE_ENABLE | +			PAD_CTL_SRE_FAST | PAD_CTL_DRV_HIGH); + +	/* USB HUB reset */ +	mxc_request_iomux(MX51_PIN_GPIO1_5, IOMUX_CONFIG_ALT0); +	mxc_iomux_set_pad(MX51_PIN_GPIO1_5, PAD_CTL_PKE_ENABLE | +			PAD_CTL_SRE_FAST | PAD_CTL_DRV_HIGH); + +	/* WIFI EN (act low) */ +	mxc_request_iomux(MX51_PIN_EIM_A22, IOMUX_CONFIG_GPIO); +	mxc_iomux_set_pad(MX51_PIN_EIM_A22, 0); +	/* WIFI RESET */ +	mxc_request_iomux(MX51_PIN_EIM_A16, IOMUX_CONFIG_GPIO); +	mxc_iomux_set_pad(MX51_PIN_EIM_A16, 0); +	/* BT EN (act low) */ +	mxc_request_iomux(MX51_PIN_EIM_A17, IOMUX_CONFIG_GPIO); +	mxc_iomux_set_pad(MX51_PIN_EIM_A17, 0); +} + +/* + * Enable devices connected to USB BUSes + */ +static void efika_usb_enable_devices(void) +{ +	/* Enable Bluetooth */ +	gpio_direction_output(IOMUX_TO_GPIO(MX51_PIN_EIM_A17), 0); +	udelay(10000); +	gpio_set_value(IOMUX_TO_GPIO(MX51_PIN_EIM_A17), 1); + +	/* Enable WiFi */ +	gpio_direction_output(IOMUX_TO_GPIO(MX51_PIN_EIM_A22), 1); +	udelay(10000); + +	/* Reset the WiFi chip */ +	gpio_direction_output(IOMUX_TO_GPIO(MX51_PIN_EIM_A16), 0); +	udelay(10000); +	gpio_set_value(IOMUX_TO_GPIO(MX51_PIN_EIM_A16), 1); +} + +/* + * Reset USB HUB (or HUBs on EfikaSB) + */ +static void efika_usb_hub_reset(void) +{ +	/* HUB reset */ +	gpio_direction_output(IOMUX_TO_GPIO(MX51_PIN_GPIO1_5), 1); +	udelay(1000); +	gpio_set_value(IOMUX_TO_GPIO(MX51_PIN_GPIO1_5), 0); +	udelay(1000); +	gpio_set_value(IOMUX_TO_GPIO(MX51_PIN_GPIO1_5), 1); +} + +/* + * Reset USB PHY (or PHYs on EfikaSB) + */ +static void efika_usb_phy_reset(void) +{ +	/* SMSC 3317 PHY reset */ +	gpio_direction_output(IOMUX_TO_GPIO(MX51_PIN_EIM_D27), 0); +	udelay(1000); +	gpio_set_value(IOMUX_TO_GPIO(MX51_PIN_EIM_D27), 1); +} + +static void efika_ehci_init(struct usb_ehci *ehci, uint32_t stp_gpio, +				uint32_t alt0, uint32_t alt1) +{ +	int ret; +	struct ulpi_regs *ulpi = (struct ulpi_regs *)0; + +	mxc_request_iomux(stp_gpio, alt0); +	mxc_iomux_set_pad(stp_gpio, PAD_CTL_DRV_HIGH | +				PAD_CTL_PKE_ENABLE | PAD_CTL_SRE_FAST); +	gpio_direction_output(IOMUX_TO_GPIO(stp_gpio), 0); +	udelay(1000); +	gpio_set_value(IOMUX_TO_GPIO(stp_gpio), 1); +	udelay(1000); + +	mxc_request_iomux(stp_gpio, alt1); +	mxc_iomux_set_pad(stp_gpio, USB_PAD_CONFIG); +	udelay(10000); + +	ret = ulpi_init((u32)&ehci->ulpi_viewpoint); +	if (ret) { +		printf("Efika USB ULPI initialization failed\n"); +		return; +	} + +	/* ULPI set flags */ +	ulpi_write((u32)&ehci->ulpi_viewpoint, &ulpi->otg_ctrl, +			ULPI_OTG_DP_PULLDOWN | ULPI_OTG_DM_PULLDOWN | +			ULPI_OTG_EXTVBUSIND); +	ulpi_write((u32)&ehci->ulpi_viewpoint, &ulpi->function_ctrl, +			ULPI_FC_FULL_SPEED | ULPI_FC_OPMODE_NORMAL | +			ULPI_FC_SUSPENDM); +	ulpi_write((u32)&ehci->ulpi_viewpoint, &ulpi->iface_ctrl, 0); + +	/* Set VBus */ +	ulpi_write((u32)&ehci->ulpi_viewpoint, &ulpi->otg_ctrl_set, +			ULPI_OTG_DRVVBUS | ULPI_OTG_DRVVBUS_EXT); + +	/* +	 * Set VBusChrg +	 * +	 * NOTE: This violates USB specification, but otherwise, USB on Efika +	 * doesn't work. +	 */ +	ulpi_write((u32)&ehci->ulpi_viewpoint, &ulpi->otg_ctrl_set, +			ULPI_OTG_CHRGVBUS); +} + +int board_ehci_hcd_init(int port) +{ +	/* Init iMX51 EHCI */ +	efika_usb_phy_reset(); +	efika_usb_hub_reset(); +	efika_usb_enable_devices(); + +	return 0; +} + +void ehci_powerup_fixup(uint32_t *status_reg, uint32_t *reg) +{ +	uint32_t port = OTG_BASE_ADDR + (0x200 * CONFIG_MXC_USB_PORT); +	struct usb_ehci *ehci = (struct usb_ehci *)port; +	struct ulpi_regs *ulpi = (struct ulpi_regs *)0; + +	ulpi_write((u32)&ehci->ulpi_viewpoint, &ulpi->otg_ctrl_set, +			ULPI_OTG_CHRGVBUS); + +	wait_ms(50); + +	/* terminate the reset */ +	*reg = ehci_readl(status_reg); +	*reg |= EHCI_PS_PE; +} + +void board_ehci_hcd_postinit(struct usb_ehci *ehci, int port) +{ +	uint32_t tmp; + +	if (port == 0) { +		/* Adjust UTMI PHY frequency to 24MHz */ +		tmp = readl(OTG_BASE_ADDR + 0x80c); +		tmp = (tmp & ~0x3) | 0x01; +		writel(tmp, OTG_BASE_ADDR + 0x80c); +	} else if (port == 1) { +		efika_ehci_init(ehci, MX51_PIN_USBH1_STP, +				IOMUX_CONFIG_ALT2, IOMUX_CONFIG_ALT0); +	} else if ((port == 2) && machine_is_efikasb()) { +		efika_ehci_init(ehci, MX51_PIN_EIM_A26, +				IOMUX_CONFIG_ALT1, IOMUX_CONFIG_ALT2); +	} + +	if (port) +		mdelay(10); +} diff --git a/board/efikamx/efikamx.c b/board/efikamx/efikamx.c index 3d2cc1a7f..1f6c457e9 100644 --- a/board/efikamx/efikamx.c +++ b/board/efikamx/efikamx.c @@ -540,6 +540,15 @@ static inline void setup_iomux_ata(void) { }  #endif  /* + * EHCI USB + */ +#ifdef	CONFIG_CMD_USB +extern void setup_iomux_usb(void); +#else +static inline void setup_iomux_usb(void) { } +#endif + +/*   * LED configuration   */  void setup_iomux_led(void) @@ -688,6 +697,12 @@ int board_late_init(void)  	setup_iomux_led();  	setup_iomux_ata(); +	setup_iomux_usb(); + +	if (machine_is_efikasb()) +		setenv("preboot", "usb reset ; setenv stdin usbkbd\0"); + +	setup_iomux_led();  	efikamx_toggle_led(EFIKAMX_LED_BLUE); diff --git a/board/freescale/mx51evk/mx51evk.c b/board/freescale/mx51evk/mx51evk.c index e5b0929f6..13c59413e 100644 --- a/board/freescale/mx51evk/mx51evk.c +++ b/board/freescale/mx51evk/mx51evk.c @@ -35,6 +35,7 @@  #include <pmic.h>  #include <fsl_pmic.h>  #include <mc13892.h> +#include <usb/ehci-fsl.h>  DECLARE_GLOBAL_DATA_PTR; @@ -172,6 +173,64 @@ static void setup_iomux_spi(void)  }  #endif +#ifdef CONFIG_USB_EHCI_MX5 +#define MX51EVK_USBH1_HUB_RST	IOMUX_TO_GPIO(MX51_PIN_GPIO1_7) /* GPIO1_7 */ +#define MX51EVK_USBH1_STP	IOMUX_TO_GPIO(MX51_PIN_USBH1_STP) /* GPIO1_27 */ +#define MX51EVK_USB_CLK_EN_B	IOMUX_TO_GPIO(MX51_PIN_EIM_D18) /* GPIO2_1 */ +#define MX51EVK_USB_PHY_RESET	IOMUX_TO_GPIO(MX51_PIN_EIM_D21) /* GPIO2_5 */ + +#define USBH1_PAD	(PAD_CTL_SRE_FAST | PAD_CTL_DRV_HIGH |		\ +			 PAD_CTL_100K_PU | PAD_CTL_PUE_PULL |		\ +			 PAD_CTL_PKE_ENABLE | PAD_CTL_HYS_ENABLE) +#define GPIO_PAD	(PAD_CTL_DRV_HIGH | PAD_CTL_PKE_ENABLE |	\ +			 PAD_CTL_SRE_FAST) +#define NO_PAD		(1 << 16) + +static void setup_usb_h1(void) +{ +	setup_iomux_usb_h1(); + +	/* GPIO_1_7 for USBH1 hub reset */ +	mxc_request_iomux(MX51_PIN_GPIO1_7, IOMUX_CONFIG_ALT0); +	mxc_iomux_set_pad(MX51_PIN_GPIO1_7, NO_PAD); + +	/* GPIO_2_1 */ +	mxc_request_iomux(MX51_PIN_EIM_D17, IOMUX_CONFIG_ALT1); +	mxc_iomux_set_pad(MX51_PIN_EIM_D17, GPIO_PAD); + +	/* GPIO_2_5 for USB PHY reset */ +	mxc_request_iomux(MX51_PIN_EIM_D21, IOMUX_CONFIG_ALT1); +	mxc_iomux_set_pad(MX51_PIN_EIM_D21, GPIO_PAD); +} + +void board_ehci_hcd_init(int port) +{ +	/* Set USBH1_STP to GPIO and toggle it */ +	mxc_request_iomux(MX51_PIN_USBH1_STP, IOMUX_CONFIG_GPIO); +	mxc_iomux_set_pad(MX51_PIN_USBH1_STP, USBH1_PAD); + +	gpio_direction_output(MX51EVK_USBH1_STP, 0); +	gpio_direction_output(MX51EVK_USB_PHY_RESET, 0); +	mdelay(10); +	gpio_set_value(MX51EVK_USBH1_STP, 1); + +	/* Set back USBH1_STP to be function */ +	mxc_request_iomux(MX51_PIN_USBH1_STP, IOMUX_CONFIG_ALT0); +	mxc_iomux_set_pad(MX51_PIN_USBH1_STP, USBH1_PAD); + +	/* De-assert USB PHY RESETB */ +	gpio_set_value(MX51EVK_USB_PHY_RESET, 1); + +	/* Drive USB_CLK_EN_B line low */ +	gpio_direction_output(MX51EVK_USB_CLK_EN_B, 0); + +	/* Reset USB hub */ +	gpio_direction_output(MX51EVK_USBH1_HUB_RST, 0); +	mdelay(2); +	gpio_set_value(MX51EVK_USBH1_HUB_RST, 1); +} +#endif +  static void power_init(void)  {  	unsigned int val; @@ -394,6 +453,9 @@ int board_early_init_f(void)  {  	setup_iomux_uart();  	setup_iomux_fec(); +#ifdef CONFIG_USB_EHCI_MX5 +	setup_usb_h1(); +#endif  	return 0;  } diff --git a/board/freescale/mx53loco/mx53loco.c b/board/freescale/mx53loco/mx53loco.c index 3cf4195b5..57170ce04 100644 --- a/board/freescale/mx53loco/mx53loco.c +++ b/board/freescale/mx53loco/mx53loco.c @@ -78,6 +78,16 @@ static void setup_iomux_uart(void)  				PAD_CTL_ODE_OPENDRAIN_ENABLE);  } +#ifdef CONFIG_USB_EHCI_MX5 +void board_ehci_hcd_init(int port) +{ +	/* request VBUS power enable pin, GPIO[8}, gpio7 */ +	mxc_request_iomux(MX53_PIN_ATA_DA_2, IOMUX_CONFIG_ALT1); +	gpio_direction_output(IOMUX_TO_GPIO(MX53_PIN_ATA_DA_2), 0); +	gpio_set_value(IOMUX_TO_GPIO(MX53_PIN_ATA_DA_2), 1); +} +#endif +  static void setup_iomux_fec(void)  {  	/*FEC_MDIO*/ diff --git a/board/samsung/goni/goni.c b/board/samsung/goni/goni.c index e191bfbd2..e8fb1ea41 100644 --- a/board/samsung/goni/goni.c +++ b/board/samsung/goni/goni.c @@ -26,7 +26,9 @@  #include <asm/arch/gpio.h>  #include <asm/arch/mmc.h>  #include <pmic.h> - +#include <usb/s3c_udc.h> +#include <asm/arch/cpu.h> +#include <max8998_pmic.h>  DECLARE_GLOBAL_DATA_PTR;  static struct s5pc110_gpio *s5pc110_gpio; @@ -100,3 +102,47 @@ int board_mmc_init(bd_t *bis)  	return s5p_mmc_init(0, 4);  }  #endif + +#ifdef CONFIG_USB_GADGET +static int s5pc1xx_phy_control(int on) +{ +	int ret; +	static int status; +	struct pmic *p = get_pmic(); + +	if (pmic_probe(p)) +		return -1; + +	if (on && !status) { +		ret = pmic_set_output(p, MAX8998_REG_ONOFF1, +				      MAX8998_LDO3, LDO_ON); +		ret = pmic_set_output(p, MAX8998_REG_ONOFF2, +				      MAX8998_LDO8, LDO_ON); +		if (ret) { +			puts("MAX8998 LDO setting error!\n"); +			return -1; +		} +		status = 1; +	} else if (!on && status) { +		ret = pmic_set_output(p, MAX8998_REG_ONOFF1, +				      MAX8998_LDO3, LDO_OFF); +		ret = pmic_set_output(p, MAX8998_REG_ONOFF2, +				      MAX8998_LDO8, LDO_OFF); +		if (ret) { +			puts("MAX8998 LDO setting error!\n"); +			return -1; +		} +		status = 0; +	} +	udelay(10000); + +	return 0; +} + +struct s3c_plat_otg_data s5pc110_otg_data = { +	.phy_control = s5pc1xx_phy_control, +	.regs_phy = S5PC110_PHY_BASE, +	.regs_otg = S5PC110_OTG_BASE, +	.usb_phy_ctrl = S5PC110_USB_PHY_CONTROL, +}; +#endif diff --git a/common/cmd_usb.c b/common/cmd_usb.c index 8c8726566..d4ec2a291 100644 --- a/common/cmd_usb.c +++ b/common/cmd_usb.c @@ -700,11 +700,12 @@ int do_usb(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])  U_BOOT_CMD(  	usb,	5,	1,	do_usb,  	"USB sub-system", -	"reset - reset (rescan) USB controller\n" -	"usb stop [f]  - stop USB [f]=force stop\n" -	"usb tree  - show USB device tree\n" +	"start - start (scan) USB controller\n" +	"usb reset - reset (rescan) USB controller\n" +	"usb stop [f] - stop USB [f]=force stop\n" +	"usb tree - show USB device tree\n"  	"usb info [dev] - show available USB devices\n" -	"usb storage  - show details of USB storage devices\n" +	"usb storage - show details of USB storage devices\n"  	"usb dev [dev] - show or set current USB storage device\n"  	"usb part [dev] - print partition table of one or all USB storage"  	" devices\n" @@ -725,8 +726,9 @@ U_BOOT_CMD(  U_BOOT_CMD(  	usb,	5,	1,	do_usb,  	"USB sub-system", -	"reset - reset (rescan) USB controller\n" -	"usb  tree  - show USB device tree\n" -	"usb  info [dev] - show available USB devices" +	"start - start (scan) USB controller\n" +	"usb reset - reset (rescan) USB controller\n" +	"usb tree - show USB device tree\n" +	"usb info [dev] - show available USB devices"  );  #endif diff --git a/common/usb_kbd.c b/common/usb_kbd.c index 69939f06a..75107c9a4 100644 --- a/common/usb_kbd.c +++ b/common/usb_kbd.c @@ -25,12 +25,18 @@   *   */  #include <common.h> +#include <malloc.h>  #include <stdio_dev.h>  #include <asm/byteorder.h>  #include <usb.h> -#undef USB_KBD_DEBUG +#ifdef	USB_KBD_DEBUG +#define USB_KBD_PRINTF(fmt, args...)	printf(fmt, ##args) +#else +#define USB_KBD_PRINTF(fmt, args...) +#endif +  /*   * If overwrite_console returns 1, the stdin, stderr and stdout   * are switched to the serial port, else the settings in the @@ -45,743 +51,457 @@ int overwrite_console(void)  }  #endif -#ifdef USB_KBD_DEBUG -#define USB_KBD_PRINTF(fmt, args...)	printf(fmt, ##args) -#else -#define USB_KBD_PRINTF(fmt, args...) -#endif - - -#define REPEAT_RATE	(40/4)	/* 40msec -> 25cps */ -#define REPEAT_DELAY	10	/* 10 x REPEAT_RATE = 400msec */ +/* Keyboard sampling rate */ +#define REPEAT_RATE	(40 / 4)	/* 40msec -> 25cps */ +#define REPEAT_DELAY	10		/* 10 x REPEAT_RATE = 400msec */  #define NUM_LOCK	0x53  #define CAPS_LOCK	0x39  #define SCROLL_LOCK	0x47 -  /* Modifier bits */ -#define LEFT_CNTR		0 -#define LEFT_SHIFT		1 -#define LEFT_ALT		2 -#define LEFT_GUI		3 -#define RIGHT_CNTR		4 -#define RIGHT_SHIFT		5 -#define RIGHT_ALT		6 -#define RIGHT_GUI		7 - -#define USB_KBD_BUFFER_LEN	0x20	/* size of the keyboardbuffer */ +#define LEFT_CNTR	(1 << 0) +#define LEFT_SHIFT	(1 << 1) +#define LEFT_ALT	(1 << 2) +#define LEFT_GUI	(1 << 3) +#define RIGHT_CNTR	(1 << 4) +#define RIGHT_SHIFT	(1 << 5) +#define RIGHT_ALT	(1 << 6) +#define RIGHT_GUI	(1 << 7) -static char usb_kbd_buffer[USB_KBD_BUFFER_LEN]; -static int usb_in_pointer; -static int usb_out_pointer; +/* Size of the keyboard buffer */ +#define USB_KBD_BUFFER_LEN	0x20 -unsigned char new[8]; -unsigned char old[8]; -int repeat_delay; +/* Device name */  #define DEVNAME			"usbkbd" -static unsigned char num_lock; -static unsigned char caps_lock; -static unsigned char scroll_lock; -static unsigned char ctrl; - -static unsigned char leds __attribute__((aligned(0x4))); -static unsigned char usb_kbd_numkey[] = { +/* Keyboard maps */ +static const unsigned char usb_kbd_numkey[] = {  	'1', '2', '3', '4', '5', '6', '7', '8', '9', '0',  	'\r', 0x1b, '\b', '\t', ' ', '-', '=', '[', ']',  	'\\', '#', ';', '\'', '`', ',', '.', '/'  }; -static unsigned char usb_kbd_numkey_shifted[] = { +static const unsigned char usb_kbd_numkey_shifted[] = {  	'!', '@', '#', '$', '%', '^', '&', '*', '(', ')',  	'\r', 0x1b, '\b', '\t', ' ', '_', '+', '{', '}',  	'|', '~', ':', '"', '~', '<', '>', '?'  }; -/****************************************************************** - * Queue handling - ******************************************************************/ -/* puts character in the queue and sets up the in and out pointer */ -static void usb_kbd_put_queue(char data) -{ -	if ((usb_in_pointer+1) == USB_KBD_BUFFER_LEN) { -		if (usb_out_pointer == 0) -			return; /* buffer full */ -		else -			usb_in_pointer = 0; -	} else { -		if ((usb_in_pointer+1) == usb_out_pointer) -			return; /* buffer full */ -		usb_in_pointer++; -	} -	usb_kbd_buffer[usb_in_pointer] = data; -	return; -} +/* + * NOTE: It's important for the NUM, CAPS, SCROLL-lock bits to be in this + *       order. See usb_kbd_setled() function! + */ +#define USB_KBD_NUMLOCK		(1 << 0) +#define USB_KBD_CAPSLOCK	(1 << 1) +#define USB_KBD_SCROLLLOCK	(1 << 2) +#define USB_KBD_CTRL		(1 << 3) -/* test if a character is in the queue */ -static int usb_kbd_testc(void) -{ -#ifdef CONFIG_SYS_USB_EVENT_POLL -	usb_event_poll(); -#endif -	if (usb_in_pointer == usb_out_pointer) -		return 0; /* no data */ -	else -		return 1; -} -/* gets the character from the queue */ -static int usb_kbd_getc(void) -{ -	char c; -	while (usb_in_pointer == usb_out_pointer) { -#ifdef CONFIG_SYS_USB_EVENT_POLL -		usb_event_poll(); -#endif -	} -	if ((usb_out_pointer+1) == USB_KBD_BUFFER_LEN) -		usb_out_pointer = 0; -	else -		usb_out_pointer++; -	c = usb_kbd_buffer[usb_out_pointer]; -	return (int)c; +#define USB_KBD_LEDMASK		\ +	(USB_KBD_NUMLOCK | USB_KBD_CAPSLOCK | USB_KBD_SCROLLLOCK) -} +struct usb_kbd_pdata { +	uint32_t	repeat_delay; -/* forward decleration */ -static int usb_kbd_probe(struct usb_device *dev, unsigned int ifnum); +	uint32_t	usb_in_pointer; +	uint32_t	usb_out_pointer; +	uint8_t		usb_kbd_buffer[USB_KBD_BUFFER_LEN]; -/* search for keyboard and register it if found */ -int drv_usb_kbd_init(void) +	uint8_t		new[8]; +	uint8_t		old[8]; + +	uint8_t		flags; +}; + +/* Generic keyboard event polling. */ +void usb_kbd_generic_poll(void)  { -	int error, i; -	struct stdio_dev usb_kbd_dev, *old_dev; -	struct usb_device *dev; -	char *stdinname = getenv("stdin"); +	struct stdio_dev *dev; +	struct usb_device *usb_kbd_dev; +	struct usb_kbd_pdata *data; +	struct usb_interface *iface; +	struct usb_endpoint_descriptor *ep; +	int pipe; +	int maxp; -	usb_in_pointer = 0; -	usb_out_pointer = 0; -	/* scan all USB Devices */ -	for (i = 0 ; i < USB_MAX_DEVICE ; i++) { -		dev = usb_get_dev_index(i); /* get device */ -		if (dev == NULL) -			return -1; -		if (dev->devnum != -1) { -			/* Ok, we found a keyboard */ -			if (usb_kbd_probe(dev, 0) == 1) { -				/* check, if it is already registered */ -				USB_KBD_PRINTF("USB KBD found set up " -						"device.\n"); -				old_dev = stdio_get_by_name(DEVNAME); -				if (old_dev) { -					/* already registered, just return ok */ -					USB_KBD_PRINTF("USB KBD is already " -							"registered.\n"); -					return 1; -				} -				/* register the keyboard */ -				USB_KBD_PRINTF("USB KBD register.\n"); -				memset(&usb_kbd_dev, 0, -						sizeof(struct stdio_dev)); -				strcpy(usb_kbd_dev.name, DEVNAME); -				usb_kbd_dev.flags =  DEV_FLAGS_INPUT | -							DEV_FLAGS_SYSTEM; -				usb_kbd_dev.putc = NULL; -				usb_kbd_dev.puts = NULL; -				usb_kbd_dev.getc = usb_kbd_getc; -				usb_kbd_dev.tstc = usb_kbd_testc; -				usb_kbd_dev.priv = (void *)dev; -				error = stdio_register(&usb_kbd_dev); -				if (error == 0) { -					/* -					 * check if this is the standard -					 * input device -					 */ -					if (strcmp(stdinname, DEVNAME) == 0) { -						/* reassign the console */ -						if (overwrite_console()) -							return 1; -						error = console_assign(stdin, -								DEVNAME); -						if (error == 0) -							return 1; -						else -							return error; -					} -					return 1; -				} -				return error; -			} -		} -	} -	/* no USB Keyboard found */ -	return -1; -} +	/* Get the pointer to USB Keyboard device pointer */ +	dev = stdio_get_by_name(DEVNAME); +	usb_kbd_dev = (struct usb_device *)dev->priv; +	data = usb_kbd_dev->privptr; +	iface = &usb_kbd_dev->config.if_desc[0]; +	ep = &iface->ep_desc[0]; +	pipe = usb_rcvintpipe(usb_kbd_dev, ep->bEndpointAddress); +	/* Submit a interrupt transfer request */ +	maxp = usb_maxpacket(usb_kbd_dev, pipe); +	usb_submit_int_msg(usb_kbd_dev, pipe, data->new, +			maxp > 8 ? 8 : maxp, ep->bInterval); +} -/* deregistering the keyboard */ -int usb_kbd_deregister(void) +/* Puts character in the queue and sets up the in and out pointer. */ +static void usb_kbd_put_queue(struct usb_kbd_pdata *data, char c)  { -#ifdef CONFIG_SYS_STDIO_DEREGISTER -	return stdio_deregister(DEVNAME); -#else -	return 1; -#endif -} +	if (data->usb_in_pointer == USB_KBD_BUFFER_LEN - 1) { +		/* Check for buffer full. */ +		if (data->usb_out_pointer == 0) +			return; -/************************************************************************** - * Low Level drivers - */ +		data->usb_in_pointer = 0; +	} else { +		/* Check for buffer full. */ +		if (data->usb_in_pointer == data->usb_out_pointer - 1) +			return; -/* set the LEDs. Since this is used in the irq routine, the control job -   is issued with a timeout of 0. This means, that the job is queued without -   waiting for job completion */ +		data->usb_in_pointer++; +	} + +	data->usb_kbd_buffer[data->usb_in_pointer] = c; +} +/* + * Set the LEDs. Since this is used in the irq routine, the control job is + * issued with a timeout of 0. This means, that the job is queued without + * waiting for job completion. + */  static void usb_kbd_setled(struct usb_device *dev)  { -	struct usb_interface *iface; -	iface = &dev->config.if_desc[0]; -	leds = 0; -	if (scroll_lock != 0) -		leds |= 1; -	leds <<= 1; -	if (caps_lock != 0) -		leds |= 1; -	leds <<= 1; -	if (num_lock != 0) -		leds |= 1; +	struct usb_interface *iface = &dev->config.if_desc[0]; +	struct usb_kbd_pdata *data = dev->privptr; +	uint32_t leds = data->flags & USB_KBD_LEDMASK; +  	usb_control_msg(dev, usb_sndctrlpipe(dev, 0),  		USB_REQ_SET_REPORT, USB_TYPE_CLASS | USB_RECIP_INTERFACE,  		0x200, iface->desc.bInterfaceNumber, (void *)&leds, 1, 0); -  } - -#define CAPITAL_MASK 0x20 +#define CAPITAL_MASK	0x20  /* Translate the scancode in ASCII */ -static int usb_kbd_translate(unsigned char scancode, unsigned char modifier, -				int pressed) +static int usb_kbd_translate(struct usb_kbd_pdata *data, unsigned char scancode, +				unsigned char modifier, int pressed)  { -	unsigned char keycode; +	uint8_t keycode = 0; +	/* Key released */  	if (pressed == 0) { -		/* key released */ -		repeat_delay = 0; +		data->repeat_delay = 0;  		return 0;  	} +  	if (pressed == 2) { -		repeat_delay++; -		if (repeat_delay < REPEAT_DELAY) +		data->repeat_delay++; +		if (data->repeat_delay < REPEAT_DELAY)  			return 0; -		repeat_delay = REPEAT_DELAY; + +		data->repeat_delay = REPEAT_DELAY;  	} -	keycode = 0; -	if ((scancode > 3) && (scancode <= 0x1d)) { /* alpha numeric values */ -		keycode = scancode - 4 + 0x61; -		if (caps_lock) -			/* switch to capital Letters */ + +	/* Alphanumeric values */ +	if ((scancode > 3) && (scancode <= 0x1d)) { +		keycode = scancode - 4 + 'a'; + +		if (data->flags & USB_KBD_CAPSLOCK)  			keycode &= ~CAPITAL_MASK; -		if (((modifier&(1 << LEFT_SHIFT)) != 0) || -			((modifier&(1 << RIGHT_SHIFT)) != 0)) { + +		if (modifier & (LEFT_SHIFT | RIGHT_SHIFT)) { +			/* Handle CAPSLock + Shift pressed simultaneously */  			if (keycode & CAPITAL_MASK) -				/* switch to capital Letters */  				keycode &= ~CAPITAL_MASK;  			else -				/* switch to non capital Letters */  				keycode |= CAPITAL_MASK;  		}  	} -	if ((scancode > 0x1d) && (scancode < 0x3A)) { -		if (((modifier&(1 << LEFT_SHIFT)) != 0) || -			((modifier&(1 << RIGHT_SHIFT)) != 0))  /* shifted */ -			keycode = usb_kbd_numkey_shifted[scancode-0x1e]; -		else /* non shifted */ -			keycode = usb_kbd_numkey[scancode-0x1e]; + +	if ((scancode > 0x1d) && (scancode < 0x3a)) { +		/* Shift pressed */ +		if (modifier & (LEFT_SHIFT | RIGHT_SHIFT)) +			keycode = usb_kbd_numkey_shifted[scancode - 0x1e]; +		else +			keycode = usb_kbd_numkey[scancode - 0x1e];  	} -	if (ctrl) +	if (data->flags & USB_KBD_CTRL)  		keycode = scancode - 0x3;  	if (pressed == 1) {  		if (scancode == NUM_LOCK) { -			num_lock = ~num_lock; +			data->flags ^= USB_KBD_NUMLOCK;  			return 1;  		} +  		if (scancode == CAPS_LOCK) { -			caps_lock = ~caps_lock; +			data->flags ^= USB_KBD_CAPSLOCK;  			return 1;  		}  		if (scancode == SCROLL_LOCK) { -			scroll_lock = ~scroll_lock; +			data->flags ^= USB_KBD_SCROLLLOCK;  			return 1;  		}  	} -	if (keycode != 0) { + +	/* Report keycode if any */ +	if (keycode) {  		USB_KBD_PRINTF("%c", keycode); -		usb_kbd_put_queue(keycode); +		usb_kbd_put_queue(data, keycode);  	} +  	return 0;  } +static uint32_t usb_kbd_service_key(struct usb_device *dev, int i, int up) +{ +	uint32_t res = 0; +	struct usb_kbd_pdata *data = dev->privptr; +	uint8_t *new; +	uint8_t *old; + +	if (up) { +		new = data->old; +		old = data->new; +	} else { +		new = data->new; +		old = data->old; +	} + +	if ((old[i] > 3) && (memscan(new + 2, old[i], 6) == new + 8)) +		res |= usb_kbd_translate(data, old[i], data->new[0], up); + +	return res; +} +  /* Interrupt service routine */ -static int usb_kbd_irq(struct usb_device *dev) +static int usb_kbd_irq_worker(struct usb_device *dev)  { -	int i, res; +	struct usb_kbd_pdata *data = dev->privptr; +	int i, res = 0; + +	/* No combo key pressed */ +	if (data->new[0] == 0x00) +		data->flags &= ~USB_KBD_CTRL; +	/* Left or Right Ctrl pressed */ +	else if ((data->new[0] == LEFT_CNTR) || (data->new[0] == RIGHT_CNTR)) +		data->flags |= USB_KBD_CTRL; + +	for (i = 2; i < 8; i++) { +		res |= usb_kbd_service_key(dev, i, 0); +		res |= usb_kbd_service_key(dev, i, 1); +	} + +	/* Key is still pressed */ +	if ((data->new[2] > 3) && (data->old[2] == data->new[2])) +		res |= usb_kbd_translate(data, data->new[2], data->new[0], 2); +	if (res == 1) +		usb_kbd_setled(dev); + +	memcpy(data->old, data->new, 8); + +	return 1; +} + +/* Keyboard interrupt handler */ +static int usb_kbd_irq(struct usb_device *dev) +{  	if ((dev->irq_status != 0) || (dev->irq_act_len != 8)) { -		USB_KBD_PRINTF("usb_keyboard Error %lX, len %d\n", +		USB_KBD_PRINTF("USB KBD: Error %lX, len %d\n",  				dev->irq_status, dev->irq_act_len);  		return 1;  	} -	res = 0; -	switch (new[0]) { -	case 0x0:	/* No combo key pressed */ -		ctrl = 0; -		break; -	case 0x01:	/* Left Ctrl pressed */ -	case 0x10:	/* Right Ctrl pressed */ -		ctrl = 1; -		break; -	} +	return usb_kbd_irq_worker(dev); +} -	for (i = 2; i < 8; i++) { -		if (old[i] > 3 && memscan(&new[2], old[i], 6) == &new[8]) -			res |= usb_kbd_translate(old[i], new[0], 0); +/* Interrupt polling */ +static inline void usb_kbd_poll_for_event(struct usb_device *dev) +{ +#if	defined(CONFIG_SYS_USB_EVENT_POLL) +	usb_event_poll(); +	usb_kbd_irq_worker(dev); +#elif	defined(CONFIG_SYS_USB_EVENT_POLL_VIA_CONTROL_EP) +	struct usb_interface *iface; +	struct usb_kbd_pdata *data = dev->privptr; +	iface = &dev->config.if_desc[0]; +	usb_get_report(dev, iface->desc.bInterfaceNumber, +			1, 1, data->new, sizeof(data->new)); +	if (memcmp(data->old, data->new, sizeof(data->new))) +		usb_kbd_irq_worker(dev); +#endif +} -		if (new[i] > 3 && memscan(&old[2], new[i], 6) == &old[8]) -			res |= usb_kbd_translate(new[i], new[0], 1); -	} +/* test if a character is in the queue */ +static int usb_kbd_testc(void) +{ +	struct stdio_dev *dev; +	struct usb_device *usb_kbd_dev; +	struct usb_kbd_pdata *data; -	if ((new[2] > 3) && (old[2] == new[2])) /* still pressed */ -		res |= usb_kbd_translate(new[2], new[0], 2); -	if (res == 1) -		usb_kbd_setled(dev); +	dev = stdio_get_by_name(DEVNAME); +	usb_kbd_dev = (struct usb_device *)dev->priv; +	data = usb_kbd_dev->privptr; -	memcpy(&old[0], &new[0], 8); +	usb_kbd_poll_for_event(usb_kbd_dev); -	return 1; /* install IRQ Handler again */ +	return !(data->usb_in_pointer == data->usb_out_pointer);  } -/* probes the USB device dev for keyboard type */ +/* gets the character from the queue */ +static int usb_kbd_getc(void) +{ +	struct stdio_dev *dev; +	struct usb_device *usb_kbd_dev; +	struct usb_kbd_pdata *data; + +	dev = stdio_get_by_name(DEVNAME); +	usb_kbd_dev = (struct usb_device *)dev->priv; +	data = usb_kbd_dev->privptr; + +	while (data->usb_in_pointer == data->usb_out_pointer) +		usb_kbd_poll_for_event(usb_kbd_dev); + +	if (data->usb_out_pointer == USB_KBD_BUFFER_LEN - 1) +		data->usb_out_pointer = 0; +	else +		data->usb_out_pointer++; + +	return data->usb_kbd_buffer[data->usb_out_pointer]; +} + +/* probes the USB device dev for keyboard type. */  static int usb_kbd_probe(struct usb_device *dev, unsigned int ifnum)  {  	struct usb_interface *iface;  	struct usb_endpoint_descriptor *ep; +	struct usb_kbd_pdata *data;  	int pipe, maxp;  	if (dev->descriptor.bNumConfigurations != 1)  		return 0; +  	iface = &dev->config.if_desc[ifnum];  	if (iface->desc.bInterfaceClass != 3)  		return 0; +  	if (iface->desc.bInterfaceSubClass != 1)  		return 0; +  	if (iface->desc.bInterfaceProtocol != 1)  		return 0; +  	if (iface->desc.bNumEndpoints != 1)  		return 0;  	ep = &iface->ep_desc[0]; +	/* Check if endpoint 1 is interrupt endpoint */  	if (!(ep->bEndpointAddress & 0x80))  		return 0; +  	if ((ep->bmAttributes & 3) != 3)  		return 0; -	USB_KBD_PRINTF("USB KBD found set protocol...\n"); -	/* ok, we found a USB Keyboard, install it */ -	/* usb_kbd_get_hid_desc(dev); */ -	usb_set_protocol(dev, iface->desc.bInterfaceNumber, 0); -	USB_KBD_PRINTF("USB KBD found set idle...\n"); -	usb_set_idle(dev, iface->desc.bInterfaceNumber, REPEAT_RATE, 0); -	memset(&new[0], 0, 8); -	memset(&old[0], 0, 8); -	repeat_delay = 0; -	pipe = usb_rcvintpipe(dev, ep->bEndpointAddress); -	maxp = usb_maxpacket(dev, pipe); -	dev->irq_handle = usb_kbd_irq; -	USB_KBD_PRINTF("USB KBD enable interrupt pipe...\n"); -	usb_submit_int_msg(dev, pipe, &new[0], maxp > 8 ? 8 : maxp, -				ep->bInterval); -	return 1; -} +	USB_KBD_PRINTF("USB KBD: found set protocol...\n"); -#if 0 -struct usb_hid_descriptor { -	unsigned char  bLength; -	unsigned char  bDescriptorType; /* 0x21 for HID */ -	unsigned short bcdHID; /* release number */ -	unsigned char  bCountryCode; -	unsigned char  bNumDescriptors; -	unsigned char  bReportDescriptorType; -	unsigned short wDescriptorLength; -} __packed; +	data = malloc(sizeof(struct usb_kbd_pdata)); +	if (!data) { +		printf("USB KBD: Error allocating private data\n"); +		return 0; +	} -/* - * We parse each description item into this structure. Short items data - * values are expanded to 32-bit signed int, long items contain a pointer - * into the data area. - */ +	/* Clear private data */ +	memset(data, 0, sizeof(struct usb_kbd_pdata)); -struct hid_item { -	unsigned char format; -	unsigned char size; -	unsigned char type; -	unsigned char tag; -	union { -	    unsigned char   u8; -	    char            s8; -	    unsigned short  u16; -	    short           s16; -	    unsigned long   u32; -	    long            s32; -	    unsigned char  *longdata; -	} data; -}; +	/* Insert private data into USB device structure */ +	dev->privptr = data; -/* - * HID report item format - */ - -#define HID_ITEM_FORMAT_SHORT	0 -#define HID_ITEM_FORMAT_LONG	1 +	/* Set IRQ handler */ +	dev->irq_handle = usb_kbd_irq; -/* - * Special tag indicating long items - */ +	pipe = usb_rcvintpipe(dev, ep->bEndpointAddress); +	maxp = usb_maxpacket(dev, pipe); -#define HID_ITEM_TAG_LONG	15 +	/* We found a USB Keyboard, install it. */ +	usb_set_protocol(dev, iface->desc.bInterfaceNumber, 0); +	USB_KBD_PRINTF("USB KBD: found set idle...\n"); +	usb_set_idle(dev, iface->desc.bInterfaceNumber, REPEAT_RATE, 0); -static struct usb_hid_descriptor usb_kbd_hid_desc; +	USB_KBD_PRINTF("USB KBD: enable interrupt pipe...\n"); +	usb_submit_int_msg(dev, pipe, data->new, maxp > 8 ? 8 : maxp, +				ep->bInterval); -void usb_kbd_display_hid(struct usb_hid_descriptor *hid) -{ -	printf("USB_HID_DESC:\n"); -	printf("  bLenght               0x%x\n", hid->bLength); -	printf("  bcdHID                0x%x\n", hid->bcdHID); -	printf("  bCountryCode          %d\n", hid->bCountryCode); -	printf("  bNumDescriptors       0x%x\n", hid->bNumDescriptors); -	printf("  bReportDescriptorType 0x%x\n", hid->bReportDescriptorType); -	printf("  wDescriptorLength     0x%x\n", hid->wDescriptorLength); +	/* Success. */ +	return 1;  } - -/* - * Fetch a report description item from the data stream. We support long - * items, though they are not used yet. - */ - -static int fetch_item(unsigned char *start, unsigned char *end, -			struct hid_item *item) +/* Search for keyboard and register it if found. */ +int drv_usb_kbd_init(void)  { -	if ((end - start) > 0) { -		unsigned char b = *start++; -		item->type = (b >> 2) & 3; -		item->tag  = (b >> 4) & 15; -		if (item->tag == HID_ITEM_TAG_LONG) { -			item->format = HID_ITEM_FORMAT_LONG; -			if ((end - start) >= 2) { -				item->size = *start++; -				item->tag  = *start++; -				if ((end - start) >= item->size) { -					item->data.longdata = start; -					start += item->size; -					return item->size; -				} -			} -		} else { -			item->format = HID_ITEM_FORMAT_SHORT; -			item->size = b & 3; -			switch (item->size) { -			case 0: -				return item->size; -			case 1: -				if ((end - start) >= 1) { -					item->data.u8 = *start++; -					return item->size; -				} -				break; -			case 2: -				if ((end - start) >= 2) { -					item->data.u16 = le16_to_cpu( -						(unsigned short *)start); -					start += 2; -					return item->size; -				} -			case 3: -				item->size++; -				if ((end - start) >= 4) { -					item->data.u32 = le32_to_cpu( -						(unsigned long *)start); -					start += 4; -					return item->size; -				} -			} -		} -	} -	return -1; -} - -/* - * HID report descriptor item type (prefix bit 2, 3) - */ - -#define HID_ITEM_TYPE_MAIN		0 -#define HID_ITEM_TYPE_GLOBAL		1 -#define HID_ITEM_TYPE_LOCAL		2 -#define HID_ITEM_TYPE_RESERVED		3 -/* - * HID report descriptor main item tags - */ - -#define HID_MAIN_ITEM_TAG_INPUT			8 -#define HID_MAIN_ITEM_TAG_OUTPUT		9 -#define HID_MAIN_ITEM_TAG_FEATURE		11 -#define HID_MAIN_ITEM_TAG_BEGIN_COLLECTION	10 -#define HID_MAIN_ITEM_TAG_END_COLLECTION	12 -/* - * HID report descriptor main item contents - */ - -#define HID_MAIN_ITEM_CONSTANT		0x001 -#define HID_MAIN_ITEM_VARIABLE		0x002 -#define HID_MAIN_ITEM_RELATIVE		0x004 -#define HID_MAIN_ITEM_WRAP		0x008 -#define HID_MAIN_ITEM_NONLINEAR		0x010 -#define HID_MAIN_ITEM_NO_PREFERRED	0x020 -#define HID_MAIN_ITEM_NULL_STATE	0x040 -#define HID_MAIN_ITEM_VOLATILE		0x080 -#define HID_MAIN_ITEM_BUFFERED_BYTE	0x100 - -/* - * HID report descriptor collection item types - */ +	struct stdio_dev usb_kbd_dev, *old_dev; +	struct usb_device *dev; +	char *stdinname = getenv("stdin"); +	int error, i; -#define HID_COLLECTION_PHYSICAL		0 -#define HID_COLLECTION_APPLICATION	1 -#define HID_COLLECTION_LOGICAL		2 -/* - * HID report descriptor global item tags - */ +	/* Scan all USB Devices */ +	for (i = 0; i < USB_MAX_DEVICE; i++) { +		/* Get USB device. */ +		dev = usb_get_dev_index(i); +		if (!dev) +			return -1; -#define HID_GLOBAL_ITEM_TAG_USAGE_PAGE		0 -#define HID_GLOBAL_ITEM_TAG_LOGICAL_MINIMUM	1 -#define HID_GLOBAL_ITEM_TAG_LOGICAL_MAXIMUM	2 -#define HID_GLOBAL_ITEM_TAG_PHYSICAL_MINIMUM	3 -#define HID_GLOBAL_ITEM_TAG_PHYSICAL_MAXIMUM	4 -#define HID_GLOBAL_ITEM_TAG_UNIT_EXPONENT	5 -#define HID_GLOBAL_ITEM_TAG_UNIT		6 -#define HID_GLOBAL_ITEM_TAG_REPORT_SIZE		7 -#define HID_GLOBAL_ITEM_TAG_REPORT_ID		8 -#define HID_GLOBAL_ITEM_TAG_REPORT_COUNT	9 -#define HID_GLOBAL_ITEM_TAG_PUSH		10 -#define HID_GLOBAL_ITEM_TAG_POP			11 +		if (dev->devnum == -1) +			continue; -/* - * HID report descriptor local item tags - */ +		/* Try probing the keyboard */ +		if (usb_kbd_probe(dev, 0) != 1) +			continue; -#define HID_LOCAL_ITEM_TAG_USAGE		0 -#define HID_LOCAL_ITEM_TAG_USAGE_MINIMUM	1 -#define HID_LOCAL_ITEM_TAG_USAGE_MAXIMUM	2 -#define HID_LOCAL_ITEM_TAG_DESIGNATOR_INDEX	3 -#define HID_LOCAL_ITEM_TAG_DESIGNATOR_MINIMUM	4 -#define HID_LOCAL_ITEM_TAG_DESIGNATOR_MAXIMUM	5 -#define HID_LOCAL_ITEM_TAG_STRING_INDEX		7 -#define HID_LOCAL_ITEM_TAG_STRING_MINIMUM	8 -#define HID_LOCAL_ITEM_TAG_STRING_MAXIMUM	9 -#define HID_LOCAL_ITEM_TAG_DELIMITER		10 +		/* We found a keyboard, check if it is already registered. */ +		USB_KBD_PRINTF("USB KBD: found set up device.\n"); +		old_dev = stdio_get_by_name(DEVNAME); +		if (old_dev) { +			/* Already registered, just return ok. */ +			USB_KBD_PRINTF("USB KBD: is already registered.\n"); +			return 1; +		} +		/* Register the keyboard */ +		USB_KBD_PRINTF("USB KBD: register.\n"); +		memset(&usb_kbd_dev, 0, sizeof(struct stdio_dev)); +		strcpy(usb_kbd_dev.name, DEVNAME); +		usb_kbd_dev.flags =  DEV_FLAGS_INPUT | DEV_FLAGS_SYSTEM; +		usb_kbd_dev.putc = NULL; +		usb_kbd_dev.puts = NULL; +		usb_kbd_dev.getc = usb_kbd_getc; +		usb_kbd_dev.tstc = usb_kbd_testc; +		usb_kbd_dev.priv = (void *)dev; +		error = stdio_register(&usb_kbd_dev); +		if (error) +			return error; -static void usb_kbd_show_item(struct hid_item *item) -{ -	switch (item->type) { -	case HID_ITEM_TYPE_MAIN: -		switch (item->tag) { -		case HID_MAIN_ITEM_TAG_INPUT: -			printf("Main Input"); -			break; -		case HID_MAIN_ITEM_TAG_OUTPUT: -			printf("Main Output"); -			break; -		case HID_MAIN_ITEM_TAG_FEATURE: -			printf("Main Feature"); -			break; -		case HID_MAIN_ITEM_TAG_BEGIN_COLLECTION: -			printf("Main Begin Collection"); -			break; -		case HID_MAIN_ITEM_TAG_END_COLLECTION: -			printf("Main End Collection"); -			break; -		default: -			printf("Main reserved %d", item->tag); -			break; -		} -		break; -	case HID_ITEM_TYPE_GLOBAL: -		switch (item->tag) { -		case HID_GLOBAL_ITEM_TAG_USAGE_PAGE: -			printf("- Global Usage Page"); -			break; -		case HID_GLOBAL_ITEM_TAG_LOGICAL_MINIMUM: -			printf("- Global Logical Minimum"); -			break; -		case HID_GLOBAL_ITEM_TAG_LOGICAL_MAXIMUM: -			printf("- Global Logical Maximum"); -			break; -		case HID_GLOBAL_ITEM_TAG_PHYSICAL_MINIMUM: -			printf("- Global physical Minimum"); -			break; -		case HID_GLOBAL_ITEM_TAG_PHYSICAL_MAXIMUM: -			printf("- Global physical Maximum"); -			break; -		case HID_GLOBAL_ITEM_TAG_UNIT_EXPONENT: -			printf("- Global Unit Exponent"); -			break; -		case HID_GLOBAL_ITEM_TAG_UNIT: -			printf("- Global Unit"); -			break; -		case HID_GLOBAL_ITEM_TAG_REPORT_SIZE: -			printf("- Global Report Size"); -			break; -		case HID_GLOBAL_ITEM_TAG_REPORT_ID: -			printf("- Global Report ID"); -			break; -		case HID_GLOBAL_ITEM_TAG_REPORT_COUNT: -			printf("- Global Report Count"); -			break; -		case HID_GLOBAL_ITEM_TAG_PUSH: -			printf("- Global Push"); -			break; -		case HID_GLOBAL_ITEM_TAG_POP: -			printf("- Global Pop"); -			break; -		default: -			printf("- Global reserved %d", item->tag); -			break; -		} -		break; -	case HID_ITEM_TYPE_LOCAL: -		switch (item->tag) { -		case HID_LOCAL_ITEM_TAG_USAGE: -			printf("-- Local Usage"); -			break; -		case HID_LOCAL_ITEM_TAG_USAGE_MINIMUM: -			printf("-- Local Usage Minimum"); -			break; -		case HID_LOCAL_ITEM_TAG_USAGE_MAXIMUM: -			printf("-- Local Usage Maximum"); -			break; -		case HID_LOCAL_ITEM_TAG_DESIGNATOR_INDEX: -			printf("-- Local Designator Index"); -			break; -		case HID_LOCAL_ITEM_TAG_DESIGNATOR_MINIMUM: -			printf("-- Local Designator Minimum"); -			break; -		case HID_LOCAL_ITEM_TAG_DESIGNATOR_MAXIMUM: -			printf("-- Local Designator Maximum"); -			break; -		case HID_LOCAL_ITEM_TAG_STRING_INDEX: -			printf("-- Local String Index"); -			break; -		case HID_LOCAL_ITEM_TAG_STRING_MINIMUM: -			printf("-- Local String Minimum"); -			break; -		case HID_LOCAL_ITEM_TAG_STRING_MAXIMUM: -			printf("-- Local String Maximum"); -			break; -		case HID_LOCAL_ITEM_TAG_DELIMITER: -			printf("-- Local Delimiter"); -			break; -		default: -			printf("-- Local reserved %d", item->tag); -			break; -		} -		break; -	default: -		printf("--- reserved %d", item->type); -		break; -	} -	switch (item->size) { -	case 1: -		printf("  %d", item->data.u8); -		break; -	case 2: -		printf("  %d", item->data.u16); -		break; -	case 4: -		printf("  %ld", item->data.u32); -		break; -	} -	printf("\n"); -} +		/* Check if this is the standard input device. */ +		if (strcmp(stdinname, DEVNAME)) +			return 1; +		/* Reassign the console */ +		if (overwrite_console()) +			return 1; -static int usb_kbd_get_hid_desc(struct usb_device *dev) -{ -	unsigned char buffer[256]; -	struct usb_descriptor_header *head; -	struct usb_config_descriptor *config; -	int index, len, i; -	unsigned char *start, *end; -	struct hid_item item; +		error = console_assign(stdin, DEVNAME); +		if (error) +			return error; -	if (usb_get_configuration_no(dev, &buffer[0], 0) == -1) -		return -1; -	head = (struct usb_descriptor_header *)&buffer[0]; -	if (head->bDescriptorType != USB_DT_CONFIG) { -		printf(" ERROR: NOT USB_CONFIG_DESC %x\n", -				head->bDescriptorType); -		return -1; -	} -	index = head->bLength; -	config = (struct usb_config_descriptor *)&buffer[0]; -	len = le16_to_cpu(config->wTotalLength); -	/* -	 * Ok the first entry must be a configuration entry, -	 * now process the others -	 */ -	head = (struct usb_descriptor_header *)&buffer[index]; -	while (index+1 < len) { -		if (head->bDescriptorType == USB_DT_HID) { -			printf("HID desc found\n"); -			memcpy(&usb_kbd_hid_desc, &buffer[index], -					buffer[index]); -			le16_to_cpus(&usb_kbd_hid_desc.bcdHID); -			le16_to_cpus(&usb_kbd_hid_desc.wDescriptorLength); -			usb_kbd_display_hid(&usb_kbd_hid_desc); -			len = 0; -			break; -		} -		index += head->bLength; -		head = (struct usb_descriptor_header *)&buffer[index]; -	} -	if (len > 0) -		return -1; -	len = usb_kbd_hid_desc.wDescriptorLength; -	index = usb_get_class_descriptor(dev, 0, USB_DT_REPORT, 0, &buffer[0], -						len); -	if (index < 0) { -		printf("reading report descriptor failed\n"); -		return -1; +		return 1;  	} -	printf(" report descriptor (size %u, read %d)\n", len, index); -	start = &buffer[0]; -	end = &buffer[len]; -	i = 0; -	do { -		index = fetch_item(start, end, &item); -		i += index; -		i++; -		if (index >= 0) -			usb_kbd_show_item(&item); - -		start += index; -		start++; -	} while (index >= 0); +	/* No USB Keyboard found */ +	return -1;  } +/* Deregister the keyboard. */ +int usb_kbd_deregister(void) +{ +#ifdef CONFIG_SYS_STDIO_DEREGISTER +	return stdio_deregister(DEVNAME); +#else +	return 1;  #endif +} diff --git a/drivers/serial/usbtty.c b/drivers/serial/usbtty.c index cffd5a2b2..e2e87fef6 100644 --- a/drivers/serial/usbtty.c +++ b/drivers/serial/usbtty.c @@ -554,11 +554,11 @@ int drv_usbtty_init (void)  	usbtty_init_strings ();  	usbtty_init_instances (); +	usbtty_init_endpoints (); +  	udc_startup_events (device_instance);/* Enable dev, init udc pointers */  	udc_connect ();		/* Enable pullup for host detection */ -	usbtty_init_endpoints (); -  	/* Device initialization */  	memset (&usbttydev, 0, sizeof (usbttydev)); diff --git a/drivers/usb/gadget/Makefile b/drivers/usb/gadget/Makefile index 5e7271352..64b091f4a 100644 --- a/drivers/usb/gadget/Makefile +++ b/drivers/usb/gadget/Makefile @@ -26,9 +26,14 @@ include $(TOPDIR)/config.mk  LIB	:= $(obj)libusb_gadget.o  # new USB gadget layer dependencies +ifdef CONFIG_USB_GADGET +COBJS-y += epautoconf.o config.o usbstring.o +COBJS-$(CONFIG_USB_GADGET_S3C_UDC_OTG) += s3c_udc_otg.o +endif  ifdef CONFIG_USB_ETHER  COBJS-y += ether.o epautoconf.o config.o usbstring.o  COBJS-$(CONFIG_USB_ETH_RNDIS) += rndis.o +COBJS-$(CONFIG_MV_UDC)	+= mv_udc.o  else  # Devices not related to the new gadget layer depend on CONFIG_USB_DEVICE  ifdef CONFIG_USB_DEVICE diff --git a/drivers/usb/gadget/gadget_chips.h b/drivers/usb/gadget/gadget_chips.h index 9bb7e2e77..5d7b638b3 100644 --- a/drivers/usb/gadget/gadget_chips.h +++ b/drivers/usb/gadget/gadget_chips.h @@ -150,6 +150,11 @@  #define	gadget_is_m66592(g)	0  #endif +#ifdef CONFIG_USB_GADGET_MV +#define gadget_is_mv(g)        (!strcmp("mv_udc", (g)->name)) +#else +#define gadget_is_mv(g)        0 +#endif  /*   * CONFIG_USB_GADGET_SX2 @@ -216,5 +221,7 @@ static inline int usb_gadget_controller_number(struct usb_gadget *gadget)  		return 0x20;  	else if (gadget_is_m66592(gadget))  		return 0x21; +	else if (gadget_is_mv(gadget)) +		return 0x22;  	return -ENOENT;  } diff --git a/drivers/usb/gadget/mv_udc.c b/drivers/usb/gadget/mv_udc.c new file mode 100644 index 000000000..cdbdcfa91 --- /dev/null +++ b/drivers/usb/gadget/mv_udc.c @@ -0,0 +1,499 @@ +/* + * Copyright 2011, Marvell Semiconductor Inc. + * Lei Wen <leiwen@marvell.com> + * + * See file CREDITS for list of people who contributed to this + * project. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + * + * Back ported to the 8xx platform (from the 8260 platform) by + * Murray.Jensen@cmst.csiro.au, 27-Jan-01. + */ + +#include <common.h> +#include <command.h> +#include <config.h> +#include <net.h> +#include <malloc.h> +#include <asm/io.h> +#include <linux/types.h> +#include <usb/mv_udc.h> + +#ifndef DEBUG +#define DBG(x...) do {} while (0) +#else +#define DBG(x...) printf(x) +static const char *reqname(unsigned r) +{ +	switch (r) { +	case USB_REQ_GET_STATUS: return "GET_STATUS"; +	case USB_REQ_CLEAR_FEATURE: return "CLEAR_FEATURE"; +	case USB_REQ_SET_FEATURE: return "SET_FEATURE"; +	case USB_REQ_SET_ADDRESS: return "SET_ADDRESS"; +	case USB_REQ_GET_DESCRIPTOR: return "GET_DESCRIPTOR"; +	case USB_REQ_SET_DESCRIPTOR: return "SET_DESCRIPTOR"; +	case USB_REQ_GET_CONFIGURATION: return "GET_CONFIGURATION"; +	case USB_REQ_SET_CONFIGURATION: return "SET_CONFIGURATION"; +	case USB_REQ_GET_INTERFACE: return "GET_INTERFACE"; +	case USB_REQ_SET_INTERFACE: return "SET_INTERFACE"; +	default: return "*UNKNOWN*"; +	} +} +#endif + +#define PAGE_SIZE	4096 +#define QH_MAXNUM	32 +static struct usb_endpoint_descriptor ep0_out_desc = { +	.bLength = sizeof(struct usb_endpoint_descriptor), +	.bDescriptorType = USB_DT_ENDPOINT, +	.bEndpointAddress = 0, +	.bmAttributes =	USB_ENDPOINT_XFER_CONTROL, +}; + +static struct usb_endpoint_descriptor ep0_in_desc = { +	.bLength = sizeof(struct usb_endpoint_descriptor), +	.bDescriptorType = USB_DT_ENDPOINT, +	.bEndpointAddress = USB_DIR_IN, +	.bmAttributes =	USB_ENDPOINT_XFER_CONTROL, +}; + +struct ept_queue_head *epts; +struct ept_queue_item *items[2 * NUM_ENDPOINTS]; +static int mv_pullup(struct usb_gadget *gadget, int is_on); +static int mv_ep_enable(struct usb_ep *ep, +		const struct usb_endpoint_descriptor *desc); +static int mv_ep_disable(struct usb_ep *ep); +static int mv_ep_queue(struct usb_ep *ep, +		struct usb_request *req, gfp_t gfp_flags); +static struct usb_request * +mv_ep_alloc_request(struct usb_ep *ep, unsigned int gfp_flags); +static void mv_ep_free_request(struct usb_ep *ep, struct usb_request *_req); + +static struct usb_gadget_ops mv_udc_ops = { +	.pullup = mv_pullup, +}; + +static struct usb_ep_ops mv_ep_ops = { +	.enable         = mv_ep_enable, +	.disable        = mv_ep_disable, +	.queue          = mv_ep_queue, +	.alloc_request  = mv_ep_alloc_request, +	.free_request   = mv_ep_free_request, +}; + +static struct mv_ep ep[2 * NUM_ENDPOINTS]; +static struct mv_drv controller = { +	.gadget = { +		.ep0 = &ep[0].ep, +		.name = "mv_udc", +	}, +}; + +static struct usb_request * +mv_ep_alloc_request(struct usb_ep *ep, unsigned int gfp_flags) +{ +	struct mv_ep *mv_ep = container_of(ep, struct mv_ep, ep); +	return &mv_ep->req; +} + +static void mv_ep_free_request(struct usb_ep *ep, struct usb_request *_req) +{ +	return; +} + +static void ep_enable(int num, int in) +{ +	struct ept_queue_head *head; +	struct mv_udc *udc = controller.udc; +	unsigned n; +	head = epts + 2*num + in; + +	n = readl(&udc->epctrl[num]); +	if (in) +		n |= (CTRL_TXE | CTRL_TXR | CTRL_TXT_BULK); +	else +		n |= (CTRL_RXE | CTRL_RXR | CTRL_RXT_BULK); + +	if (num != 0) +		head->config = CONFIG_MAX_PKT(EP_MAX_PACKET_SIZE) | CONFIG_ZLT; +	writel(n, &udc->epctrl[num]); +} + +static int mv_ep_enable(struct usb_ep *ep, +		const struct usb_endpoint_descriptor *desc) +{ +	struct mv_ep *mv_ep = container_of(ep, struct mv_ep, ep); +	int num, in; +	num = desc->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK; +	in = (desc->bEndpointAddress & USB_DIR_IN) != 0; +	ep_enable(num, in); +	mv_ep->desc = desc; +	return 0; +} + +static int mv_ep_disable(struct usb_ep *ep) +{ +	return 0; +} + +static int mv_ep_queue(struct usb_ep *ep, +		struct usb_request *req, gfp_t gfp_flags) +{ +	struct mv_ep *mv_ep = container_of(ep, struct mv_ep, ep); +	struct mv_udc *udc = controller.udc; +	struct ept_queue_item *item; +	struct ept_queue_head *head; +	unsigned phys; +	int bit, num, len, in; +	num = mv_ep->desc->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK; +	in = (mv_ep->desc->bEndpointAddress & USB_DIR_IN) != 0; +	item = items[2 * num + in]; +	head = epts + 2 * num + in; +	phys = (unsigned)req->buf; +	len = req->length; + +	item->next = TERMINATE; +	item->info = INFO_BYTES(len) | INFO_IOC | INFO_ACTIVE; +	item->page0 = phys; +	item->page1 = (phys & 0xfffff000) + 0x1000; + +	head->next = (unsigned) item; +	head->info = 0; + +	DBG("ept%d %s queue len %x, buffer %x\n", +			num, in ? "in" : "out", len, phys); + +	if (in) +		bit = EPT_TX(num); +	else +		bit = EPT_RX(num); + +	flush_cache(phys, len); +	flush_cache((unsigned long)item, sizeof(struct ept_queue_item)); +	writel(bit, &udc->epprime); + +	return 0; +} + +static void handle_ep_complete(struct mv_ep *ep) +{ +	struct ept_queue_item *item; +	int num, in, len; +	num = ep->desc->bEndpointAddress & USB_ENDPOINT_NUMBER_MASK; +	in = (ep->desc->bEndpointAddress & USB_DIR_IN) != 0; +	if (num == 0) +		ep->desc = &ep0_out_desc; +	item = items[2 * num + in]; + +	if (item->info & 0xff) +		printf("EP%d/%s FAIL nfo=%x pg0=%x\n", +			num, in ? "in" : "out", item->info, item->page0); + +	len = (item->info >> 16) & 0x7fff; +	ep->req.length -= len; +	DBG("ept%d %s complete %x\n", +			num, in ? "in" : "out", len); +	ep->req.complete(&ep->ep, &ep->req); +	if (num == 0) { +		ep->req.length = 0; +		usb_ep_queue(&ep->ep, &ep->req, 0); +		ep->desc = &ep0_in_desc; +	} +} + +#define SETUP(type, request) (((type) << 8) | (request)) + +static void handle_setup(void) +{ +	struct usb_request *req = &ep[0].req; +	struct mv_udc *udc = controller.udc; +	struct ept_queue_head *head; +	struct usb_ctrlrequest r; +	int status = 0; +	int num, in, _num, _in, i; +	char *buf; +	head = epts; + +	flush_cache((unsigned long)head, sizeof(struct ept_queue_head)); +	memcpy(&r, head->setup_data, sizeof(struct usb_ctrlrequest)); +	writel(EPT_RX(0), &udc->epstat); +	DBG("handle setup %s, %x, %x index %x value %x\n", reqname(r.bRequest), +	    r.bRequestType, r.bRequest, r.wIndex, r.wValue); + +	switch (SETUP(r.bRequestType, r.bRequest)) { +	case SETUP(USB_RECIP_ENDPOINT, USB_REQ_CLEAR_FEATURE): +		_num = r.wIndex & 15; +		_in = !!(r.wIndex & 0x80); + +		if ((r.wValue == 0) && (r.wLength == 0)) { +			req->length = 0; +			for (i = 0; i < NUM_ENDPOINTS; i++) { +				if (!ep[i].desc) +					continue; +				num = ep[i].desc->bEndpointAddress +					& USB_ENDPOINT_NUMBER_MASK; +				in = (ep[i].desc->bEndpointAddress +						& USB_DIR_IN) != 0; +				if ((num == _num) && (in == _in)) { +					ep_enable(num, in); +					usb_ep_queue(controller.gadget.ep0, +							req, 0); +					break; +				} +			} +		} +		return; + +	case SETUP(USB_RECIP_DEVICE, USB_REQ_SET_ADDRESS): +		/* +		 * write address delayed (will take effect +		 * after the next IN txn) +		 */ +		writel((r.wValue << 25) | (1 << 24), &udc->devaddr); +		req->length = 0; +		usb_ep_queue(controller.gadget.ep0, req, 0); +		return; + +	case SETUP(USB_DIR_IN | USB_RECIP_DEVICE, USB_REQ_GET_STATUS): +		req->length = 2; +		buf = (char *)req->buf; +		buf[0] = 1 << USB_DEVICE_SELF_POWERED; +		buf[1] = 0; +		usb_ep_queue(controller.gadget.ep0, req, 0); +		return; +	} +	/* pass request up to the gadget driver */ +	if (controller.driver) +		status = controller.driver->setup(&controller.gadget, &r); +	else +		status = -ENODEV; + +	if (!status) +		return; +	DBG("STALL reqname %s type %x value %x, index %x\n", +	    reqname(r.bRequest), r.bRequestType, r.wValue, r.wIndex); +	writel((1<<16) | (1 << 0), &udc->epctrl[0]); +} + +static void stop_activity(void) +{ +	int i, num, in; +	struct ept_queue_head *head; +	struct mv_udc *udc = controller.udc; +	writel(readl(&udc->epcomp), &udc->epcomp); +	writel(readl(&udc->epstat), &udc->epstat); +	writel(0xffffffff, &udc->epflush); + +	/* error out any pending reqs */ +	for (i = 0; i < NUM_ENDPOINTS; i++) { +		if (i != 0) +			writel(0, &udc->epctrl[i]); +		if (ep[i].desc) { +			num = ep[i].desc->bEndpointAddress +				& USB_ENDPOINT_NUMBER_MASK; +			in = (ep[i].desc->bEndpointAddress & USB_DIR_IN) != 0; +			head = epts + (num * 2) + (in); +			head->info = INFO_ACTIVE; +		} +	} +} + +void udc_irq(void) +{ +	struct mv_udc *udc = controller.udc; +	unsigned n = readl(&udc->usbsts); +	writel(n, &udc->usbsts); +	int bit, i, num, in; + +	n &= (STS_SLI | STS_URI | STS_PCI | STS_UI | STS_UEI); +	if (n == 0) +		return; + +	if (n & STS_URI) { +		DBG("-- reset --\n"); +		stop_activity(); +	} +	if (n & STS_SLI) +		DBG("-- suspend --\n"); + +	if (n & STS_PCI) { +		DBG("-- portchange --\n"); +		bit = (readl(&udc->portsc) >> 26) & 3; +		if (bit == 2) { +			controller.gadget.speed = USB_SPEED_HIGH; +			for (i = 1; i < NUM_ENDPOINTS && n; i++) +				if (ep[i].desc) +					ep[i].ep.maxpacket = 512; +		} else { +			controller.gadget.speed = USB_SPEED_FULL; +		} +	} + +	if (n & STS_UEI) +		printf("<UEI %x>\n", readl(&udc->epcomp)); + +	if ((n & STS_UI) || (n & STS_UEI)) { +		n = readl(&udc->epstat); +		if (n & EPT_RX(0)) +			handle_setup(); + +		n = readl(&udc->epcomp); +		if (n != 0) +			writel(n, &udc->epcomp); + +		for (i = 0; i < NUM_ENDPOINTS && n; i++) { +			if (ep[i].desc) { +				num = ep[i].desc->bEndpointAddress +					& USB_ENDPOINT_NUMBER_MASK; +				in = (ep[i].desc->bEndpointAddress +						& USB_DIR_IN) != 0; +				bit = (in) ? EPT_TX(num) : EPT_RX(num); +				if (n & bit) +					handle_ep_complete(&ep[i]); +			} +		} +	} +} + +int usb_gadget_handle_interrupts(void) +{ +	u32 value; +	struct mv_udc *udc = controller.udc; + +	value = readl(&udc->usbsts); +	if (value) +		udc_irq(); + +	return value; +} + +static int mv_pullup(struct usb_gadget *gadget, int is_on) +{ +	struct mv_udc *udc = controller.udc; +	if (is_on) { +		/* RESET */ +		writel(USBCMD_ITC(MICRO_8FRAME) | USBCMD_RST, &udc->usbcmd); +		udelay(200); + +		writel((unsigned) epts, &udc->epinitaddr); + +		/* select DEVICE mode */ +		writel(USBMODE_DEVICE, &udc->usbmode); + +		writel(0xffffffff, &udc->epflush); + +		/* Turn on the USB connection by enabling the pullup resistor */ +		writel(USBCMD_ITC(MICRO_8FRAME) | USBCMD_RUN, &udc->usbcmd); +	} else { +		stop_activity(); +		writel(USBCMD_FS2, &udc->usbcmd); +		udelay(800); +		if (controller.driver) +			controller.driver->disconnect(gadget); +	} + +	return 0; +} + +void udc_disconnect(void) +{ +	struct mv_udc *udc = controller.udc; +	/* disable pullup */ +	stop_activity(); +	writel(USBCMD_FS2, &udc->usbcmd); +	udelay(800); +	if (controller.driver) +		controller.driver->disconnect(&controller.gadget); +} + +static int mvudc_probe(void) +{ +	struct ept_queue_head *head; +	int i; + +	controller.gadget.ops = &mv_udc_ops; +	controller.udc = (struct mv_udc *)CONFIG_USB_REG_BASE; +	epts = memalign(PAGE_SIZE, QH_MAXNUM * sizeof(struct ept_queue_head)); +	memset(epts, 0, QH_MAXNUM * sizeof(struct ept_queue_head)); +	for (i = 0; i < 2 * NUM_ENDPOINTS; i++) { +		/* +		 * For item0 and item1, they are served as ep0 +		 * out&in seperately +		 */ +		head = epts + i; +		if (i < 2) +			head->config = CONFIG_MAX_PKT(EP0_MAX_PACKET_SIZE) +				| CONFIG_ZLT | CONFIG_IOS; +		else +			head->config = CONFIG_MAX_PKT(EP_MAX_PACKET_SIZE) +				| CONFIG_ZLT; +		head->next = TERMINATE; +		head->info = 0; + +		items[i] = memalign(PAGE_SIZE, sizeof(struct ept_queue_item)); +	} + +	INIT_LIST_HEAD(&controller.gadget.ep_list); +	ep[0].ep.maxpacket = 64; +	ep[0].ep.name = "ep0"; +	ep[0].desc = &ep0_in_desc; +	INIT_LIST_HEAD(&controller.gadget.ep0->ep_list); +	for (i = 0; i < 2 * NUM_ENDPOINTS; i++) { +		if (i != 0) { +			ep[i].ep.maxpacket = 512; +			ep[i].ep.name = "ep-"; +			list_add_tail(&ep[i].ep.ep_list, +				      &controller.gadget.ep_list); +			ep[i].desc = NULL; +		} +		ep[i].ep.ops = &mv_ep_ops; +	} +	return 0; +} + +int usb_gadget_register_driver(struct usb_gadget_driver *driver) +{ +	struct mv_udc *udc = controller.udc; +	int             retval; + +	if (!driver +			|| driver->speed < USB_SPEED_FULL +			|| !driver->bind +			|| !driver->setup) { +		DBG("bad parameter.\n"); +		return -EINVAL; +	} + +	if (!mvudc_probe()) { +		usb_lowlevel_init(); +		/* select ULPI phy */ +		writel(PTS(PTS_ENABLE) | PFSC, &udc->portsc); +	} +	retval = driver->bind(&controller.gadget); +	if (retval) { +		DBG("driver->bind() returned %d\n", retval); +		return retval; +	} +	controller.driver = driver; + +	return 0; +} + +int usb_gadget_unregister_driver(struct usb_gadget_driver *driver) +{ +	return 0; +} diff --git a/drivers/usb/gadget/pxa27x_udc.c b/drivers/usb/gadget/pxa27x_udc.c index e6ccd49ac..0d3a9886f 100644 --- a/drivers/usb/gadget/pxa27x_udc.c +++ b/drivers/usb/gadget/pxa27x_udc.c @@ -26,11 +26,13 @@  #include <common.h>  #include <config.h>  #include <asm/byteorder.h> -#include <usbdcore.h> -#include <usbdcore_ep0.h> +#include <usbdevice.h>  #include <asm/arch/hardware.h> +#include <asm/io.h>  #include <usb/pxa27x_udc.h> +#include "ep0.h" +  /* number of endpoints on this UDC */  #define UDC_MAX_ENDPOINTS	24 @@ -50,7 +52,7 @@ static void udc_dump_buffer(char *name, u8 *buf, int len)  static inline void udc_ack_int_UDCCR(int mask)  { -	USIR1	= mask | USIR1; +	writel(readl(USIR1) | mask, USIR1);  }  /* @@ -68,9 +70,7 @@ static int udc_write_urb(struct usb_endpoint_instance *endpoint)  {  	struct urb *urb = endpoint->tx_urb;  	int ep_num = endpoint->endpoint_address & USB_ENDPOINT_NUMBER_MASK; -	u32 *addr32 = (u32 *) &UDCDN(ep_num);  	u32 *data32 = (u32 *) urb->buffer; -	u8  *addr8 = (u8 *) &UDCDN(ep_num);  	u8  *data8 = (u8 *) urb->buffer;  	unsigned int i, n, w, b, is_short;  	int timeout = 2000;	/* 2ms */ @@ -98,26 +98,28 @@ static int udc_write_urb(struct usb_endpoint_instance *endpoint)  	/* Prepare for data send */  	if (ep_num) -		UDCCSN(ep_num) = UDCCSR_PC; +		writel(UDCCSR_PC ,UDCCSN(ep_num));  	for (i = 0; i < w; i++) -		*addr32 = data32[endpoint->sent/4 + i]; +		  writel(data32[endpoint->sent / 4 + i], UDCDN(ep_num)); +  	for (i = 0; i < b; i++) -		*addr8 = data8[endpoint->sent + w*4 + i]; +		  writeb(data8[endpoint->sent + w * 4 + i], UDCDN(ep_num));  	/* Set "Packet Complete" if less data then tx_packetSize */  	if (is_short) -		UDCCSN(ep_num) = ep_num ? UDCCSR_SP : UDCCSR0_IPR; +		writel(ep_num ? UDCCSR_SP : UDCCSR0_IPR, UDCCSN(ep_num));  	/* Wait for data sent */ -	while (!(UDCCSN(ep_num) & (ep_num ? UDCCSR_PC : UDCCSR0_IPR))) { -		if (ep_num) { +	if (ep_num) { +		while (!(readl(UDCCSN(ep_num)) & UDCCSR_PC)) {  			if (timeout-- == 0)  				return -1;  			else  				udelay(1); -		}; +		}  	} +  	endpoint->last = n;  	if (ep_num) { @@ -127,7 +129,7 @@ static int udc_write_urb(struct usb_endpoint_instance *endpoint)  		endpoint->last -= n;  	} -	if ((endpoint->tx_urb->actual_length - endpoint->sent) <= 0) { +	if (endpoint->sent >= urb->actual_length) {  		urb->actual_length = 0;  		endpoint->sent = 0;  		endpoint->last = 0; @@ -148,7 +150,6 @@ static int udc_read_urb(struct usb_endpoint_instance *endpoint)  {  	struct urb *urb = endpoint->rcv_urb;  	int ep_num = endpoint->endpoint_address & USB_ENDPOINT_NUMBER_MASK; -	u32 *addr32 = (u32 *) &UDCDN(ep_num);  	u32 *data32 = (u32 *) urb->buffer;  	unsigned int i, n, is_short ; @@ -160,15 +161,15 @@ static int udc_read_urb(struct usb_endpoint_instance *endpoint)  		endpoint->rcv_packetSize);  #endif -	if (UDCCSN(ep_num) & UDCCSR_BNE) -		n = UDCBCN(ep_num) & 0x3ff; +	if (readl(UDCCSN(ep_num)) & UDCCSR_BNE) +		n = readl(UDCBCN(ep_num)) & 0x3ff;  	else /* zlp */  		n = 0;  	is_short = n != endpoint->rcv_packetSize;  	usbdbg("n %d%s", n, is_short ? "-s" : "");  	for (i = 0; i < n; i += 4) -		data32[urb->actual_length/4 + i/4] = *addr32; +		data32[urb->actual_length / 4 + i / 4] = readl(UDCDN(ep_num));  	udc_dump_buffer("urb read", (u8 *) data32, urb->actual_length + n);  	usbd_rcv_complete(endpoint, n, 0); @@ -178,27 +179,35 @@ static int udc_read_urb(struct usb_endpoint_instance *endpoint)  static int udc_read_urb_ep0(void)  { -	u32 *addr32 = (u32 *) &UDCDN(0);  	u32 *data32 = (u32 *) ep0_urb->buffer; -	u8 *addr8 = (u8 *) &UDCDN(0);  	u8 *data8 = (u8 *) ep0_urb->buffer;  	unsigned int i, n, w, b; -	n = UDCBCR0; +	usbdbg("read urb on ep 0"); +#if defined(USBDDBG) && defined(USBDPARANOIA) +	usbdbg("urb: buf %p, buf_len %d, actual_len %d", +		ep0_urb->buffer, ep0_urb->buffer_length, ep0_urb->actual_length); +#endif + +	n = readl(UDCBCR0);  	w = n / 4;  	b = n % 4;  	for (i = 0; i < w; i++) { -		data32[ep0_urb->actual_length/4 + i] = *addr32; -		ep0_urb->actual_length += 4; +		data32[ep0_urb->actual_length / 4 + i] = readl(UDCDN(0)); +//		ep0_urb->actual_length += 4;  	}  	for (i = 0; i < b; i++) { -		data8[ep0_urb->actual_length + w*4 + i] = *addr8; -		ep0_urb->actual_length++; +		data8[ep0_urb->actual_length + w * 4 + i] = readb(UDCDN(0)); +//		ep0_urb->actual_length++;  	} -	UDCCSR0 = UDCCSR0_OPC | UDCCSR0_IPR; +	ep0_urb->actual_length += n; + +	udc_dump_buffer("urb read", (u8 *) data32, ep0_urb->actual_length); + +	writel(UDCCSR0_OPC | UDCCSR0_IPR, UDCCSR0);  	if (ep0_urb->actual_length == ep0_urb->device_request.wLength)  		return 1; @@ -207,7 +216,7 @@ static int udc_read_urb_ep0(void)  static void udc_handle_ep0(struct usb_endpoint_instance *endpoint)  { -	u32 udccsr0 = UDCCSR0; +	u32 udccsr0 = readl(UDCCSR0);  	u32 *data = (u32 *) &ep0_urb->device_request;  	int i; @@ -216,7 +225,7 @@ static void udc_handle_ep0(struct usb_endpoint_instance *endpoint)  	/* Clear stall status */  	if (udccsr0 & UDCCSR0_SST) {  		usberr("clear stall status"); -		UDCCSR0 = UDCCSR0_SST; +		writel(UDCCSR0_SST, UDCCSR0);  		ep0state = EP0_IDLE;  	} @@ -227,8 +236,7 @@ static void udc_handle_ep0(struct usb_endpoint_instance *endpoint)  	switch (ep0state) {  	case EP0_IDLE: - -		udccsr0 = UDCCSR0; +		udccsr0 = readl(UDCCSR0);  		/* Start control request? */  		if ((udccsr0 & (UDCCSR0_OPC | UDCCSR0_SA | UDCCSR0_RNE))  			== (UDCCSR0_OPC | UDCCSR0_SA | UDCCSR0_RNE)) { @@ -238,15 +246,15 @@ static void udc_handle_ep0(struct usb_endpoint_instance *endpoint)  			 */  			usbdbg("try reading SETUP packet");  			for (i = 0; i < 2; i++) { -				if ((UDCCSR0 & UDCCSR0_RNE) == 0) { +				if ((readl(UDCCSR0) & UDCCSR0_RNE) == 0) {  					usberr("setup packet too short:%d", i);  					goto stall;  				} -				data[i] = UDCDR0; +				data[i] = readl(UDCDR0);  			} -			UDCCSR0 |= (UDCCSR0_OPC | UDCCSR0_SA); -			if ((UDCCSR0 & UDCCSR0_RNE) != 0) { +			writel(readl(UDCCSR0) | UDCCSR0_OPC | UDCCSR0_SA, UDCCSR0); +			if ((readl(UDCCSR0) & UDCCSR0_RNE) != 0) {  				usberr("setup packet too long");  				goto stall;  			} @@ -261,7 +269,7 @@ static void udc_handle_ep0(struct usb_endpoint_instance *endpoint)  								(u8 *)data, 8);  					goto stall;  				} -				UDCCSR0 = UDCCSR0_IPR; +				writel(UDCCSR0_IPR, UDCCSR0);  				ep0state = EP0_IDLE;  			} else {  				/* Check direction */ @@ -274,7 +282,7 @@ static void udc_handle_ep0(struct usb_endpoint_instance *endpoint)  					ep0_urb->buffer_length =  						sizeof(ep0_urb->buffer_data);  					ep0_urb->actual_length = 0; -					UDCCSR0 = UDCCSR0_IPR; +					writel(UDCCSR0_IPR, UDCCSR0);  				} else {  					/* The ep0_recv_setup function has  					 * already placed our response packet @@ -289,9 +297,9 @@ stall:  							, (u8 *) data, 8);  						ep0state = EP0_IDLE; -						UDCCSR0 = UDCCSR0_SA | +						writel(UDCCSR0_SA |  						UDCCSR0_OPC | UDCCSR0_FST | -						UDCCS0_FTF; +						UDCCS0_FTF, UDCCSR0);  						return;  					} @@ -317,7 +325,7 @@ stall:  			 * - IPR cleared  			 * - OPC got set, without SA (likely status stage)  			 */ -			UDCCSR0 = udccsr0 & (UDCCSR0_SA | UDCCSR0_OPC); +			writel(udccsr0 & (UDCCSR0_SA | UDCCSR0_OPC), UDCCSR0);  		}  		break; @@ -351,7 +359,7 @@ read_complete:  	case EP0_IN_DATA:  		/* GET_DESCRIPTOR etc */  		if (udccsr0 & UDCCSR0_OPC) { -			UDCCSR0 = UDCCSR0_OPC | UDCCSR0_FTF; +			writel(UDCCSR0_OPC | UDCCSR0_FTF, UDCCSR0);  			usberr("ep0in premature status");  			ep0state = EP0_IDLE;  		} else { @@ -364,14 +372,14 @@ read_complete:  		break;  	case EP0_XFER_COMPLETE: -		UDCCSR0 = UDCCSR0_IPR; +		writel(UDCCSR0_IPR, UDCCSR0);  		ep0state = EP0_IDLE;  		break;  	default:  		usbdbg("Default\n");  	} -	USIR0 = USIR0_IR0; +	writel(USIR0_IR0, USIR0);  }  static void udc_handle_ep(struct usb_endpoint_instance *endpoint) @@ -380,33 +388,33 @@ static void udc_handle_ep(struct usb_endpoint_instance *endpoint)  	int ep_num = ep_addr & USB_ENDPOINT_NUMBER_MASK;  	int ep_isout = (ep_addr & USB_ENDPOINT_DIR_MASK) == USB_DIR_OUT; -	u32 flags = UDCCSN(ep_num) & (UDCCSR_SST | UDCCSR_TRN); +	u32 flags = readl(UDCCSN(ep_num)) & (UDCCSR_SST | UDCCSR_TRN);  	if (flags) -		UDCCSN(ep_num) = flags; +		writel(flags, UDCCSN(ep_num));  	if (ep_isout)  		udc_read_urb(endpoint);  	else  		udc_write_urb(endpoint); -	UDCCSN(ep_num) = UDCCSR_PC; +	writel(UDCCSR_PC, UDCCSN(ep_num));  }  static void udc_state_changed(void)  {  	int config, interface, alternate; -	UDCCR |= UDCCR_SMAC; +	writel(readl(UDCCR) | UDCCR_SMAC, UDCCR); -	config = (UDCCR & UDCCR_ACN) >> UDCCR_ACN_S; -	interface = (UDCCR & UDCCR_AIN) >> UDCCR_AIN_S; -	alternate = (UDCCR & UDCCR_AAISN) >> UDCCR_AAISN_S; +	config = (readl(UDCCR) & UDCCR_ACN) >> UDCCR_ACN_S; +	interface = (readl(UDCCR) & UDCCR_AIN) >> UDCCR_AIN_S; +	alternate = (readl(UDCCR) & UDCCR_AAISN) >> UDCCR_AAISN_S;  	usbdbg("New UDC settings are: conf %d - inter %d - alter %d",  		config, interface, alternate);  	usbd_device_event_irq(udc_device, DEVICE_CONFIGURED, 0); -	UDCISR1 = UDCISR1_IRCC; +	writel(UDCISR1_IRCC, UDCISR1);  }  void udc_irq(void) @@ -419,7 +427,7 @@ void udc_irq(void)  	do {  		handled = 0;  		/* Suspend Interrupt Request */ -		if (USIR1 & UDCCR_SUSIR) { +		if (readl(USIR1) & UDCCR_SUSIR) {  			usbdbg("Suspend\n");  			udc_ack_int_UDCCR(UDCCR_SUSIR);  			handled = 1; @@ -427,35 +435,35 @@ void udc_irq(void)  		}  		/* Resume Interrupt Request */ -		if (USIR1 & UDCCR_RESIR) { +		if (readl(USIR1) & UDCCR_RESIR) {  			udc_ack_int_UDCCR(UDCCR_RESIR);  			handled = 1;  			usbdbg("USB resume\n");  		} -		if (USIR1 & (1<<31)) { +		if (readl(USIR1) & (1<<31)) {  			handled = 1;  			udc_state_changed();  		}  		/* Reset Interrupt Request */ -		if (USIR1 & UDCCR_RSTIR) { +		if (readl(USIR1) & UDCCR_RSTIR) {  			udc_ack_int_UDCCR(UDCCR_RSTIR);  			handled = 1;  			usbdbg("Reset\n");  			usbd_device_event_irq(udc_device, DEVICE_RESET, 0);  		} else { -			if (USIR0) -				usbdbg("UISR0: %x \n", USIR0); +			if (readl(USIR0)) +				usbdbg("UISR0: %x \n", readl(USIR0)); -			if (USIR0 & 0x2) -				USIR0 = 0x2; +			if (readl(USIR0) & 0x2) +				writel(0x2, USIR0);  			/* Control traffic */ -			if (USIR0  & USIR0_IR0) { +			if (readl(USIR0)  & USIR0_IR0) {  				handled = 1; +				writel(USIR0_IR0, USIR0);  				udc_handle_ep0(udc_device->bus->endpoint_array); -				USIR0 = USIR0_IR0;  			}  			endpoint = udc_device->bus->endpoint_array; @@ -464,11 +472,11 @@ void udc_irq(void)  						USB_ENDPOINT_NUMBER_MASK;  				if (!ep_num)  					continue; -				udcisr0 = UDCISR0; +				udcisr0 = readl(UDCISR0);  				if (udcisr0 &  					UDCISR_INT(ep_num, UDC_INT_PACKETCMP)) { -					UDCISR0 = UDCISR_INT(ep_num, -							 UDC_INT_PACKETCMP); +					writel(UDCISR_INT(ep_num, UDC_INT_PACKETCMP), +					       UDCISR0);  					udc_handle_ep(&endpoint[i]);  				}  			} @@ -485,21 +493,21 @@ void udc_irq(void)  static inline void udc_set_mask_UDCCR(int mask)  { -    UDCCR = (UDCCR & UDCCR_MASK_BITS) | (mask & UDCCR_MASK_BITS); +    writel((readl(UDCCR) & UDCCR_MASK_BITS) | (mask & UDCCR_MASK_BITS), UDCCR);  }  static inline void udc_clear_mask_UDCCR(int mask)  { -    UDCCR = (UDCCR & UDCCR_MASK_BITS) & ~(mask & UDCCR_MASK_BITS); +    writel((readl(UDCCR) & UDCCR_MASK_BITS) & ~(mask & UDCCR_MASK_BITS), UDCCR);  }  static void pio_irq_enable(int ep_num)  {  	if (ep_num < 16) -		UDCICR0 |= 3 << (ep_num * 2); +		writel(readl(UDCICR0) | 3 << (ep_num * 2), UDCICR0);  	else {  		ep_num -= 16; -		UDCICR1 |= 3 << (ep_num * 2); +		writel(readl(UDCICR1) | 3 << (ep_num * 2), UDCICR1);  	}  } @@ -589,22 +597,26 @@ void udc_setup_ep(struct usb_device_instance *device, unsigned int id,  	tmp |= (ep_size << UDCCONR_MPS_S) & UDCCONR_MPS;  	tmp |= UDCCONR_EE; -	UDCCN(ep_num) = tmp; +	writel(tmp, UDCCN(ep_num)); -	usbdbg("UDCCR%c = %x", 'A' + ep_num-1, UDCCN(ep_num)); -	usbdbg("UDCCSR%c = %x", 'A' + ep_num-1, UDCCSN(ep_num)); +	//usbdbg +	usbdbg("UDCCR%c = %x", 'A' + ep_num-1, readl(UDCCN(ep_num))); +	usbdbg("UDCCSR%c = %x", 'A' + ep_num-1, readl(UDCCSN(ep_num)));  } -#define CONFIG_USB_DEV_PULLUP_GPIO 87 -  /* Connect the USB device to the bus */  void udc_connect(void)  {  	usbdbg("UDC connect"); +#ifdef CONFIG_USB_DEV_PULLUP_GPIO  	/* Turn on the USB connection by enabling the pullup resistor */  	set_GPIO_mode(CONFIG_USB_DEV_PULLUP_GPIO | GPIO_OUT); -	GPSR(CONFIG_USB_DEV_PULLUP_GPIO) = GPIO_bit(CONFIG_USB_DEV_PULLUP_GPIO); +	writel(GPIO_bit(CONFIG_USB_DEV_PULLUP_GPIO), GPSR(CONFIG_USB_DEV_PULLUP_GPIO)); +#else +	/* Host port 2 transceiver D+ pull up enable */ +	writel(readl(UP2OCR) | UP2OCR_DPPUE, UP2OCR); +#endif  }  /* Disconnect the USB device to the bus */ @@ -612,8 +624,13 @@ void udc_disconnect(void)  {  	usbdbg("UDC disconnect"); +#ifdef CONFIG_USB_DEV_PULLUP_GPIO  	/* Turn off the USB connection by disabling the pullup resistor */ -	GPCR(CONFIG_USB_DEV_PULLUP_GPIO) = GPIO_bit(CONFIG_USB_DEV_PULLUP_GPIO); +	writel(GPIO_bit(CONFIG_USB_DEV_PULLUP_GPIO), GPCR(CONFIG_USB_DEV_PULLUP_GPIO)); +#else +	/* Host port 2 transceiver D+ pull up disable */ +	writel(readl(UP2OCR) & ~UP2OCR_DPPUE, UP2OCR); +#endif  }  /* Switch on the UDC */ @@ -621,15 +638,14 @@ void udc_enable(struct usb_device_instance *device)  {  	ep0state = EP0_IDLE; -	CKEN |= CKEN11_USB;  	/* enable endpoint 0, A, B's Packet Complete Interrupt. */ -	UDCICR0 = 0x0000003f; -	UDCICR1 = 0xa8000000; +	writel(0xffffffff, UDCICR0); +	writel(0xa8000000, UDCICR1);  	/* clear the interrupt status/control registers */ -	UDCISR0 = 0xffffffff; -	UDCISR1 = 0xffffffff; +	writel(0xffffffff, UDCISR0); +	writel(0xffffffff, UDCISR1);  	/* set UDC-enable */  	udc_set_mask_UDCCR(UDCCR_UDE); @@ -652,7 +668,7 @@ void udc_disable(void)  	udc_clear_mask_UDCCR(UDCCR_UDE);  	/* Disable clock for USB device */ -	CKEN &= ~CKEN11_USB; +	writel(readl(CKEN) & ~CKEN11_USB, CKEN);  	/* Free ep0 URB */  	if (ep0_urb) { @@ -689,14 +705,15 @@ int udc_init(void)  	udc_device = NULL;  	usbdbg("PXA27x usbd start"); +	/* Enable clock for USB device */ +	writel(readl(CKEN) | CKEN11_USB, CKEN); +  	/* Disable the UDC */  	udc_clear_mask_UDCCR(UDCCR_UDE); -	/* Disable clock for USB device */ -	CKEN &= ~CKEN11_USB; -  	/* Disable IRQs: we don't use them */ -	UDCICR0 = UDCICR1 = 0; +	writel(0, UDCICR0); +	writel(0, UDCICR1);  	return 0;  } diff --git a/drivers/usb/gadget/regs-otg.h b/drivers/usb/gadget/regs-otg.h new file mode 100644 index 000000000..3737e4523 --- /dev/null +++ b/drivers/usb/gadget/regs-otg.h @@ -0,0 +1,271 @@ +/* linux/arch/arm/plat-s3c/include/plat/regs-otg.h + * + * Copyright (C) 2004 Herbert Poetzl <herbert@13thfloor.at> + * + * Registers remapping: + * Lukasz Majewski <l.majewski@samsumg.com> + * + * This include file is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + */ + +#ifndef __ASM_ARCH_REGS_USB_OTG_HS_H +#define __ASM_ARCH_REGS_USB_OTG_HS_H + +/* USB2.0 OTG Controller register */ +struct s3c_usbotg_phy { +	u32 phypwr; +	u32 phyclk; +	u32 rstcon; +}; + +/* Device Logical IN Endpoint-Specific Registers */ +struct s3c_dev_in_endp { +	u32 diepctl; +	u8  res1[4]; +	u32 diepint; +	u8  res2[4]; +	u32 dieptsiz; +	u32 diepdma; +	u8  res3[4]; +	u32 diepdmab; +}; + +/* Device Logical OUT Endpoint-Specific Registers */ +struct s3c_dev_out_endp { +	u32 doepctl; +	u8  res1[4]; +	u32 doepint; +	u8  res2[4]; +	u32 doeptsiz; +	u32 doepdma; +	u8  res3[4]; +	u32 doepdmab; +}; + +struct ep_fifo { +	u32 fifo; +	u8  res[4092]; +}; + +/* USB2.0 OTG Controller register */ +struct s3c_usbotg_reg { +	/* Core Global Registers */ +	u32 gotgctl; /* OTG Control & Status */ +	u32 gotgint; /* OTG Interrupt */ +	u32 gahbcfg; /* Core AHB Configuration */ +	u32 gusbcfg; /* Core USB Configuration */ +	u32 grstctl; /* Core Reset */ +	u32 gintsts; /* Core Interrupt */ +	u32 gintmsk; /* Core Interrupt Mask */ +	u32 grxstsr; /* Receive Status Debug Read/Status Read */ +	u32 grxstsp; /* Receive Status Debug Pop/Status Pop */ +	u32 grxfsiz; /* Receive FIFO Size */ +	u32 gnptxfsiz; /* Non-Periodic Transmit FIFO Size */ +	u8  res1[216]; +	u32 dieptxf[15]; /* Device Periodic Transmit FIFO size register */ +	u8  res2[1728]; +	/* Device Configuration */ +	u32 dcfg; /* Device Configuration Register */ +	u32 dctl; /* Device Control */ +	u32 dsts; /* Device Status */ +	u8  res3[4]; +	u32 diepmsk; /* Device IN Endpoint Common Interrupt Mask */ +	u32 doepmsk; /* Device OUT Endpoint Common Interrupt Mask */ +	u32 daint; /* Device All Endpoints Interrupt */ +	u32 daintmsk; /* Device All Endpoints Interrupt Mask */ +	u8  res4[224]; +	struct s3c_dev_in_endp in_endp[16]; +	struct s3c_dev_out_endp out_endp[16]; +	u8  res5[768]; +	struct ep_fifo ep[16]; +}; + +/*===================================================================== */ +/*definitions related to CSR setting */ + +/* S3C_UDC_OTG_GOTGCTL */ +#define B_SESSION_VALID		(0x1<<19) +#define A_SESSION_VALID		(0x1<<18) + +/* S3C_UDC_OTG_GAHBCFG */ +#define PTXFE_HALF			(0<<8) +#define PTXFE_ZERO			(1<<8) +#define NPTXFE_HALF			(0<<7) +#define NPTXFE_ZERO			(1<<7) +#define MODE_SLAVE			(0<<5) +#define MODE_DMA			(1<<5) +#define BURST_SINGLE			(0<<1) +#define BURST_INCR			(1<<1) +#define BURST_INCR4			(3<<1) +#define BURST_INCR8			(5<<1) +#define BURST_INCR16			(7<<1) +#define GBL_INT_UNMASK			(1<<0) +#define GBL_INT_MASK			(0<<0) + +/* S3C_UDC_OTG_GRSTCTL */ +#define AHB_MASTER_IDLE		(1u<<31) +#define CORE_SOFT_RESET		(0x1<<0) + +/* S3C_UDC_OTG_GINTSTS/S3C_UDC_OTG_GINTMSK core interrupt register */ +#define INT_RESUME			(1u<<31) +#define INT_DISCONN			(0x1<<29) +#define INT_CONN_ID_STS_CNG		(0x1<<28) +#define INT_OUT_EP			(0x1<<19) +#define INT_IN_EP			(0x1<<18) +#define INT_ENUMDONE			(0x1<<13) +#define INT_RESET			(0x1<<12) +#define INT_SUSPEND			(0x1<<11) +#define INT_EARLY_SUSPEND		(0x1<<10) +#define INT_NP_TX_FIFO_EMPTY		(0x1<<5) +#define INT_RX_FIFO_NOT_EMPTY		(0x1<<4) +#define INT_SOF			(0x1<<3) +#define INT_DEV_MODE			(0x0<<0) +#define INT_HOST_MODE			(0x1<<1) +#define INT_GOUTNakEff			(0x01<<7) +#define INT_GINNakEff			(0x01<<6) + +#define FULL_SPEED_CONTROL_PKT_SIZE	8 +#define FULL_SPEED_BULK_PKT_SIZE	64 + +#define HIGH_SPEED_CONTROL_PKT_SIZE	64 +#define HIGH_SPEED_BULK_PKT_SIZE	512 + +#define RX_FIFO_SIZE			(1024*4) +#define NPTX_FIFO_SIZE			(1024*4) +#define PTX_FIFO_SIZE			(1536*1) + +#define DEPCTL_TXFNUM_0		(0x0<<22) +#define DEPCTL_TXFNUM_1		(0x1<<22) +#define DEPCTL_TXFNUM_2		(0x2<<22) +#define DEPCTL_TXFNUM_3		(0x3<<22) +#define DEPCTL_TXFNUM_4		(0x4<<22) + +/* Enumeration speed */ +#define USB_HIGH_30_60MHZ		(0x0<<1) +#define USB_FULL_30_60MHZ		(0x1<<1) +#define USB_LOW_6MHZ			(0x2<<1) +#define USB_FULL_48MHZ			(0x3<<1) + +/* S3C_UDC_OTG_GRXSTSP STATUS */ +#define OUT_PKT_RECEIVED		(0x2<<17) +#define OUT_TRANSFER_COMPLELTED	(0x3<<17) +#define SETUP_TRANSACTION_COMPLETED	(0x4<<17) +#define SETUP_PKT_RECEIVED		(0x6<<17) +#define GLOBAL_OUT_NAK			(0x1<<17) + +/* S3C_UDC_OTG_DCTL device control register */ +#define NORMAL_OPERATION		(0x1<<0) +#define SOFT_DISCONNECT		(0x1<<1) + +/* S3C_UDC_OTG_DAINT device all endpoint interrupt register */ +#define DAINT_OUT_BIT			(16) +#define DAINT_MASK			(0xFFFF) + +/* S3C_UDC_OTG_DIEPCTL0/DOEPCTL0 device +   control IN/OUT endpoint 0 control register */ +#define DEPCTL_EPENA			(0x1<<31) +#define DEPCTL_EPDIS			(0x1<<30) +#define DEPCTL_SETD1PID		(0x1<<29) +#define DEPCTL_SETD0PID		(0x1<<28) +#define DEPCTL_SNAK			(0x1<<27) +#define DEPCTL_CNAK			(0x1<<26) +#define DEPCTL_STALL			(0x1<<21) +#define DEPCTL_TYPE_BIT		(18) +#define DEPCTL_TYPE_MASK		(0x3<<18) +#define DEPCTL_CTRL_TYPE		(0x0<<18) +#define DEPCTL_ISO_TYPE		(0x1<<18) +#define DEPCTL_BULK_TYPE		(0x2<<18) +#define DEPCTL_INTR_TYPE		(0x3<<18) +#define DEPCTL_USBACTEP		(0x1<<15) +#define DEPCTL_NEXT_EP_BIT		(11) +#define DEPCTL_MPS_BIT			(0) +#define DEPCTL_MPS_MASK		(0x7FF) + +#define DEPCTL0_MPS_64			(0x0<<0) +#define DEPCTL0_MPS_32			(0x1<<0) +#define DEPCTL0_MPS_16			(0x2<<0) +#define DEPCTL0_MPS_8			(0x3<<0) +#define DEPCTL_MPS_BULK_512		(512<<0) +#define DEPCTL_MPS_INT_MPS_16		(16<<0) + +#define DIEPCTL0_NEXT_EP_BIT		(11) + + +/* S3C_UDC_OTG_DIEPMSK/DOEPMSK device IN/OUT endpoint +   common interrupt mask register */ +/* S3C_UDC_OTG_DIEPINTn/DOEPINTn device IN/OUT endpoint interrupt register */ +#define BACK2BACK_SETUP_RECEIVED	(0x1<<6) +#define INTKNEPMIS			(0x1<<5) +#define INTKN_TXFEMP			(0x1<<4) +#define NON_ISO_IN_EP_TIMEOUT		(0x1<<3) +#define CTRL_OUT_EP_SETUP_PHASE_DONE	(0x1<<3) +#define AHB_ERROR			(0x1<<2) +#define EPDISBLD			(0x1<<1) +#define TRANSFER_DONE			(0x1<<0) + +#define USB_PHY_CTRL_EN0                (0x1 << 0) + +/* OPHYPWR */ +#define PHY_0_SLEEP                     (0x1 << 5) +#define OTG_DISABLE_0                   (0x1 << 4) +#define ANALOG_PWRDOWN                  (0x1 << 3) +#define FORCE_SUSPEND_0                 (0x1 << 0) + +/* URSTCON */ +#define HOST_SW_RST                     (0x1 << 4) +#define PHY_SW_RST1                     (0x1 << 3) +#define PHYLNK_SW_RST                   (0x1 << 2) +#define LINK_SW_RST                     (0x1 << 1) +#define PHY_SW_RST0                     (0x1 << 0) + +/* OPHYCLK */ +#define COMMON_ON_N1                    (0x1 << 7) +#define COMMON_ON_N0                    (0x1 << 4) +#define ID_PULLUP0                      (0x1 << 2) +#define CLK_SEL_24MHZ                   (0x3 << 0) +#define CLK_SEL_12MHZ                   (0x2 << 0) +#define CLK_SEL_48MHZ                   (0x0 << 0) + +/* Device Configuration Register DCFG */ +#define DEV_SPEED_HIGH_SPEED_20         (0x0 << 0) +#define DEV_SPEED_FULL_SPEED_20         (0x1 << 0) +#define DEV_SPEED_LOW_SPEED_11          (0x2 << 0) +#define DEV_SPEED_FULL_SPEED_11         (0x3 << 0) +#define EP_MISS_CNT(x)                  (x << 18) +#define DEVICE_ADDRESS(x)               (x << 4) + +/* Core Reset Register (GRSTCTL) */ +#define TX_FIFO_FLUSH                   (0x1 << 5) +#define RX_FIFO_FLUSH                   (0x1 << 4) +#define TX_FIFO_NUMBER(x)               (x << 6) +#define TX_FIFO_FLUSH_ALL               TX_FIFO_NUMBER(0x10) + +/* Masks definitions */ +#define GINTMSK_INIT	(INT_OUT_EP | INT_IN_EP | INT_RESUME | INT_ENUMDONE\ +			| INT_RESET | INT_SUSPEND) +#define DOEPMSK_INIT	(CTRL_OUT_EP_SETUP_PHASE_DONE | AHB_ERROR|TRANSFER_DONE) +#define DIEPMSK_INIT	(NON_ISO_IN_EP_TIMEOUT|AHB_ERROR|TRANSFER_DONE) +#define GAHBCFG_INIT	(PTXFE_HALF | NPTXFE_HALF | MODE_DMA | BURST_INCR4\ +			| GBL_INT_UNMASK) + +/* Device Endpoint X Transfer Size Register (DIEPTSIZX) */ +#define DIEPT_SIZ_PKT_CNT(x)                      (x << 19) +#define DIEPT_SIZ_XFER_SIZE(x)                    (x << 0) + +/* Device OUT Endpoint X Transfer Size Register (DOEPTSIZX) */ +#define DOEPT_SIZ_PKT_CNT(x)                      (x << 19) +#define DOEPT_SIZ_XFER_SIZE(x)                    (x << 0) +#define DOEPT_SIZ_XFER_SIZE_MAX_EP0               (0x7F << 0) +#define DOEPT_SIZ_XFER_SIZE_MAX_EP                (0x7FFF << 0) + +/* Device Endpoint-N Control Register (DIEPCTLn/DOEPCTLn) */ +#define DIEPCTL_TX_FIFO_NUM(x)                    (x << 22) +#define DIEPCTL_TX_FIFO_NUM_MASK                  (~DIEPCTL_TX_FIFO_NUM(0xF)) + +/* Device ALL Endpoints Interrupt Register (DAINT) */ +#define DAINT_IN_EP_INT(x)                        (x << 0) +#define DAINT_OUT_EP_INT(x)                       (x << 16) +#endif diff --git a/drivers/usb/gadget/s3c_udc_otg.c b/drivers/usb/gadget/s3c_udc_otg.c new file mode 100644 index 000000000..5a3ac78ff --- /dev/null +++ b/drivers/usb/gadget/s3c_udc_otg.c @@ -0,0 +1,892 @@ +/* + * drivers/usb/gadget/s3c_udc_otg.c + * Samsung S3C on-chip full/high speed USB OTG 2.0 device controllers + * + * Copyright (C) 2008 for Samsung Electronics + * + * BSP Support for Samsung's UDC driver + * available at: + * git://git.kernel.org/pub/scm/linux/kernel/git/kki_ap/linux-2.6-samsung.git + * + * State machine bugfixes: + * Marek Szyprowski <m.szyprowski@samsung.com> + * + * Ported to u-boot: + * Marek Szyprowski <m.szyprowski@samsung.com> + * Lukasz Majewski <l.majewski@samsumg.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA + * + */ + +#include <common.h> +#include <asm/errno.h> +#include <linux/list.h> +#include <malloc.h> + +#include <linux/usb/ch9.h> +#include <linux/usb/gadget.h> + +#include <asm/byteorder.h> +#include <asm/io.h> + +#include <asm/mach-types.h> +#include <asm/arch/gpio.h> + +#include "regs-otg.h" +#include <usb/s3c_udc.h> +#include <usb/lin_gadget_compat.h> + +/***********************************************************/ + +#define OTG_DMA_MODE		1 + +#undef DEBUG_S3C_UDC_SETUP +#undef DEBUG_S3C_UDC_EP0 +#undef DEBUG_S3C_UDC_ISR +#undef DEBUG_S3C_UDC_OUT_EP +#undef DEBUG_S3C_UDC_IN_EP +#undef DEBUG_S3C_UDC + +/* #define DEBUG_S3C_UDC_SETUP */ +/* #define DEBUG_S3C_UDC_EP0 */ +/* #define DEBUG_S3C_UDC_ISR */ +/* #define DEBUG_S3C_UDC_OUT_EP */ +/* #define DEBUG_S3C_UDC_IN_EP */ +/* #define DEBUG_S3C_UDC */ + +#include <usb/s3c_udc.h> + +#define EP0_CON		0 +#define EP_MASK		0xF + +#if defined(DEBUG_S3C_UDC_SETUP) || defined(DEBUG_S3C_UDC_ISR)	  \ +	|| defined(DEBUG_S3C_UDC_OUT_EP) +static char *state_names[] = { +	"WAIT_FOR_SETUP", +	"DATA_STATE_XMIT", +	"DATA_STATE_NEED_ZLP", +	"WAIT_FOR_OUT_STATUS", +	"DATA_STATE_RECV", +	"WAIT_FOR_COMPLETE", +	"WAIT_FOR_OUT_COMPLETE", +	"WAIT_FOR_IN_COMPLETE", +	"WAIT_FOR_NULL_COMPLETE", +}; +#endif + +#define DRIVER_DESC "S3C HS USB OTG Device Driver, (c) Samsung Electronics" +#define DRIVER_VERSION "15 March 2009" + +struct s3c_udc	*the_controller; + +static const char driver_name[] = "s3c-udc"; +static const char driver_desc[] = DRIVER_DESC; +static const char ep0name[] = "ep0-control"; + +/* Max packet size*/ +static unsigned int ep0_fifo_size = 64; +static unsigned int ep_fifo_size =  512; +static unsigned int ep_fifo_size2 = 1024; +static int reset_available = 1; + +static struct usb_ctrlrequest *usb_ctrl; +static dma_addr_t usb_ctrl_dma_addr; + +/* +  Local declarations. +*/ +static int s3c_ep_enable(struct usb_ep *ep, +			 const struct usb_endpoint_descriptor *); +static int s3c_ep_disable(struct usb_ep *ep); +static struct usb_request *s3c_alloc_request(struct usb_ep *ep, +					     gfp_t gfp_flags); +static void s3c_free_request(struct usb_ep *ep, struct usb_request *); + +static int s3c_queue(struct usb_ep *ep, struct usb_request *, gfp_t gfp_flags); +static int s3c_dequeue(struct usb_ep *ep, struct usb_request *); +static int s3c_fifo_status(struct usb_ep *ep); +static void s3c_fifo_flush(struct usb_ep *ep); +static void s3c_ep0_read(struct s3c_udc *dev); +static void s3c_ep0_kick(struct s3c_udc *dev, struct s3c_ep *ep); +static void s3c_handle_ep0(struct s3c_udc *dev); +static int s3c_ep0_write(struct s3c_udc *dev); +static int write_fifo_ep0(struct s3c_ep *ep, struct s3c_request *req); +static void done(struct s3c_ep *ep, struct s3c_request *req, int status); +static void stop_activity(struct s3c_udc *dev, +			  struct usb_gadget_driver *driver); +static int udc_enable(struct s3c_udc *dev); +static void udc_set_address(struct s3c_udc *dev, unsigned char address); +static void reconfig_usbd(void); +static void set_max_pktsize(struct s3c_udc *dev, enum usb_device_speed speed); +static void nuke(struct s3c_ep *ep, int status); +static int s3c_udc_set_halt(struct usb_ep *_ep, int value); +static void s3c_udc_set_nak(struct s3c_ep *ep); + +static struct usb_ep_ops s3c_ep_ops = { +	.enable = s3c_ep_enable, +	.disable = s3c_ep_disable, + +	.alloc_request = s3c_alloc_request, +	.free_request = s3c_free_request, + +	.queue = s3c_queue, +	.dequeue = s3c_dequeue, + +	.set_halt = s3c_udc_set_halt, +	.fifo_status = s3c_fifo_status, +	.fifo_flush = s3c_fifo_flush, +}; + +#define create_proc_files() do {} while (0) +#define remove_proc_files() do {} while (0) + +/***********************************************************/ + +void __iomem		*regs_otg; +struct s3c_usbotg_reg *reg; +struct s3c_usbotg_phy *phy; +static unsigned int usb_phy_ctrl; + +void otg_phy_init(struct s3c_udc *dev) +{ +	dev->pdata->phy_control(1); + +	/*USB PHY0 Enable */ +	printf("USB PHY0 Enable\n"); + +	/* Enable PHY */ +	writel(readl(usb_phy_ctrl) | USB_PHY_CTRL_EN0, usb_phy_ctrl); + +	if (dev->pdata->usb_flags == PHY0_SLEEP) /* C210 Universal */ +		writel((readl(&phy->phypwr) +			&~(PHY_0_SLEEP | OTG_DISABLE_0 | ANALOG_PWRDOWN) +			&~FORCE_SUSPEND_0), &phy->phypwr); +	else /* C110 GONI */ +		writel((readl(&phy->phypwr) &~(OTG_DISABLE_0 | ANALOG_PWRDOWN) +			&~FORCE_SUSPEND_0), &phy->phypwr); + +	writel((readl(&phy->phyclk) &~(ID_PULLUP0 | COMMON_ON_N0)) | +	       CLK_SEL_24MHZ, &phy->phyclk); /* PLL 24Mhz */ + +	writel((readl(&phy->rstcon) &~(LINK_SW_RST | PHYLNK_SW_RST)) +	       | PHY_SW_RST0, &phy->rstcon); +	udelay(10); +	writel(readl(&phy->rstcon) +	       &~(PHY_SW_RST0 | LINK_SW_RST | PHYLNK_SW_RST), &phy->rstcon); +	udelay(10); +} + +void otg_phy_off(struct s3c_udc *dev) +{ +	/* reset controller just in case */ +	writel(PHY_SW_RST0, &phy->rstcon); +	udelay(20); +	writel(readl(&phy->phypwr) &~PHY_SW_RST0, &phy->rstcon); +	udelay(20); + +	writel(readl(&phy->phypwr) | OTG_DISABLE_0 | ANALOG_PWRDOWN +	       | FORCE_SUSPEND_0, &phy->phypwr); + +	writel(readl(usb_phy_ctrl) &~USB_PHY_CTRL_EN0, usb_phy_ctrl); + +	writel((readl(&phy->phyclk) & ~(ID_PULLUP0 | COMMON_ON_N0)), +	      &phy->phyclk); + +	udelay(10000); + +	dev->pdata->phy_control(0); +} + +/***********************************************************/ + +#include "s3c_udc_otg_xfer_dma.c" + +/* + *	udc_disable - disable USB device controller + */ +static void udc_disable(struct s3c_udc *dev) +{ +	DEBUG_SETUP("%s: %p\n", __func__, dev); + +	udc_set_address(dev, 0); + +	dev->ep0state = WAIT_FOR_SETUP; +	dev->gadget.speed = USB_SPEED_UNKNOWN; +	dev->usb_address = 0; + +	otg_phy_off(dev); +} + +/* + *	udc_reinit - initialize software state + */ +static void udc_reinit(struct s3c_udc *dev) +{ +	unsigned int i; + +	DEBUG_SETUP("%s: %p\n", __func__, dev); + +	/* device/ep0 records init */ +	INIT_LIST_HEAD(&dev->gadget.ep_list); +	INIT_LIST_HEAD(&dev->gadget.ep0->ep_list); +	dev->ep0state = WAIT_FOR_SETUP; + +	/* basic endpoint records init */ +	for (i = 0; i < S3C_MAX_ENDPOINTS; i++) { +		struct s3c_ep *ep = &dev->ep[i]; + +		if (i != 0) +			list_add_tail(&ep->ep.ep_list, &dev->gadget.ep_list); + +		ep->desc = 0; +		ep->stopped = 0; +		INIT_LIST_HEAD(&ep->queue); +		ep->pio_irqs = 0; +	} + +	/* the rest was statically initialized, and is read-only */ +} + +#define BYTES2MAXP(x)	(x / 8) +#define MAXP2BYTES(x)	(x * 8) + +/* until it's enabled, this UDC should be completely invisible + * to any USB host. + */ +static int udc_enable(struct s3c_udc *dev) +{ +	DEBUG_SETUP("%s: %p\n", __func__, dev); + +	otg_phy_init(dev); +	reconfig_usbd(); + +	DEBUG_SETUP("S3C USB 2.0 OTG Controller Core Initialized : 0x%x\n", +		    readl(®->gintmsk)); + +	dev->gadget.speed = USB_SPEED_UNKNOWN; + +	return 0; +} + +/* +  Register entry point for the peripheral controller driver. +*/ +int usb_gadget_register_driver(struct usb_gadget_driver *driver) +{ +	struct s3c_udc *dev = the_controller; +	int retval = 0; +	unsigned long flags; + +	DEBUG_SETUP("%s: %s\n", __func__, "no name"); + +	if (!driver +	    || (driver->speed != USB_SPEED_FULL +		&& driver->speed != USB_SPEED_HIGH) +	    || !driver->bind || !driver->disconnect || !driver->setup) +		return -EINVAL; +	if (!dev) +		return -ENODEV; +	if (dev->driver) +		return -EBUSY; + +	spin_lock_irqsave(&dev->lock, flags); +	/* first hook up the driver ... */ +	dev->driver = driver; +	spin_unlock_irqrestore(&dev->lock, flags); + +	if (retval) { /* TODO */ +		printf("target device_add failed, error %d\n", retval); +		return retval; +	} + +	retval = driver->bind(&dev->gadget); +	if (retval) { +		DEBUG_SETUP("%s: bind to driver --> error %d\n", +			    dev->gadget.name, retval); +		dev->driver = 0; +		return retval; +	} + +	enable_irq(IRQ_OTG); + +	DEBUG_SETUP("Registered gadget driver %s\n", dev->gadget.name); +	udc_enable(dev); + +	return 0; +} + +/* + * Unregister entry point for the peripheral controller driver. + */ +int usb_gadget_unregister_driver(struct usb_gadget_driver *driver) +{ +	struct s3c_udc *dev = the_controller; +	unsigned long flags; + +	if (!dev) +		return -ENODEV; +	if (!driver || driver != dev->driver) +		return -EINVAL; + +	spin_lock_irqsave(&dev->lock, flags); +	dev->driver = 0; +	stop_activity(dev, driver); +	spin_unlock_irqrestore(&dev->lock, flags); + +	driver->unbind(&dev->gadget); + +	disable_irq(IRQ_OTG); + +	udc_disable(dev); +	return 0; +} + +/* + *	done - retire a request; caller blocked irqs + */ +static void done(struct s3c_ep *ep, struct s3c_request *req, int status) +{ +	unsigned int stopped = ep->stopped; + +	DEBUG("%s: %s %p, req = %p, stopped = %d\n", +	      __func__, ep->ep.name, ep, &req->req, stopped); + +	list_del_init(&req->queue); + +	if (likely(req->req.status == -EINPROGRESS)) +		req->req.status = status; +	else +		status = req->req.status; + +	if (status && status != -ESHUTDOWN) { +		DEBUG("complete %s req %p stat %d len %u/%u\n", +		      ep->ep.name, &req->req, status, +		      req->req.actual, req->req.length); +	} + +	/* don't modify queue heads during completion callback */ +	ep->stopped = 1; + +#ifdef DEBUG_S3C_UDC +	printf("calling complete callback\n"); +	{ +		int i, len = req->req.length; + +		printf("pkt[%d] = ", req->req.length); +		if (len > 64) +			len = 64; +		for (i = 0; i < len; i++) { +			printf("%02x", ((u8 *)req->req.buf)[i]); +			if ((i & 7) == 7) +				printf(" "); +		} +		printf("\n"); +	} +#endif +	spin_unlock(&ep->dev->lock); +	req->req.complete(&ep->ep, &req->req); +	spin_lock(&ep->dev->lock); + +	DEBUG("callback completed\n"); + +	ep->stopped = stopped; +} + +/* + *	nuke - dequeue ALL requests + */ +static void nuke(struct s3c_ep *ep, int status) +{ +	struct s3c_request *req; + +	DEBUG("%s: %s %p\n", __func__, ep->ep.name, ep); + +	/* called with irqs blocked */ +	while (!list_empty(&ep->queue)) { +		req = list_entry(ep->queue.next, struct s3c_request, queue); +		done(ep, req, status); +	} +} + +static void stop_activity(struct s3c_udc *dev, +			  struct usb_gadget_driver *driver) +{ +	int i; + +	/* don't disconnect drivers more than once */ +	if (dev->gadget.speed == USB_SPEED_UNKNOWN) +		driver = 0; +	dev->gadget.speed = USB_SPEED_UNKNOWN; + +	/* prevent new request submissions, kill any outstanding requests  */ +	for (i = 0; i < S3C_MAX_ENDPOINTS; i++) { +		struct s3c_ep *ep = &dev->ep[i]; +		ep->stopped = 1; +		nuke(ep, -ESHUTDOWN); +	} + +	/* report disconnect; the driver is already quiesced */ +	if (driver) { +		spin_unlock(&dev->lock); +		driver->disconnect(&dev->gadget); +		spin_lock(&dev->lock); +	} + +	/* re-init driver-visible data structures */ +	udc_reinit(dev); +} + +static void reconfig_usbd(void) +{ +	/* 2. Soft-reset OTG Core and then unreset again. */ +	int i; +	unsigned int uTemp = writel(CORE_SOFT_RESET, ®->grstctl); + +	DEBUG(2, "Reseting OTG controller\n"); + +	writel(0<<15		/* PHY Low Power Clock sel*/ +		|1<<14		/* Non-Periodic TxFIFO Rewind Enable*/ +		|0x5<<10	/* Turnaround time*/ +		|0<<9 | 0<<8	/* [0:HNP disable,1:HNP enable][ 0:SRP disable*/ +				/* 1:SRP enable] H1= 1,1*/ +		|0<<7		/* Ulpi DDR sel*/ +		|0<<6		/* 0: high speed utmi+, 1: full speed serial*/ +		|0<<4		/* 0: utmi+, 1:ulpi*/ +		|1<<3		/* phy i/f  0:8bit, 1:16bit*/ +		|0x7<<0,	/* HS/FS Timeout**/ +		®->gusbcfg); + +	/* 3. Put the OTG device core in the disconnected state.*/ +	uTemp = readl(®->dctl); +	uTemp |= SOFT_DISCONNECT; +	writel(uTemp, ®->dctl); + +	udelay(20); + +	/* 4. Make the OTG device core exit from the disconnected state.*/ +	uTemp = readl(®->dctl); +	uTemp = uTemp & ~SOFT_DISCONNECT; +	writel(uTemp, ®->dctl); + +	/* 5. Configure OTG Core to initial settings of device mode.*/ +	/* [][1: full speed(30Mhz) 0:high speed]*/ +	writel(EP_MISS_CNT(1) | DEV_SPEED_HIGH_SPEED_20, ®->dcfg); + +	mdelay(1); + +	/* 6. Unmask the core interrupts*/ +	writel(GINTMSK_INIT, ®->gintmsk); + +	/* 7. Set NAK bit of EP0, EP1, EP2*/ +	writel(DEPCTL_EPDIS|DEPCTL_SNAK, ®->out_endp[EP0_CON].doepctl); +	writel(DEPCTL_EPDIS|DEPCTL_SNAK, ®->in_endp[EP0_CON].diepctl); + +	for (i = 1; i < S3C_MAX_ENDPOINTS; i++) { +		writel(DEPCTL_EPDIS|DEPCTL_SNAK, ®->out_endp[i].doepctl); +		writel(DEPCTL_EPDIS|DEPCTL_SNAK, ®->in_endp[i].diepctl); +	} + +	/* 8. Unmask EPO interrupts*/ +	writel(((1 << EP0_CON) << DAINT_OUT_BIT) +	       | (1 << EP0_CON), ®->daintmsk); + +	/* 9. Unmask device OUT EP common interrupts*/ +	writel(DOEPMSK_INIT, ®->doepmsk); + +	/* 10. Unmask device IN EP common interrupts*/ +	writel(DIEPMSK_INIT, ®->diepmsk); + +	/* 11. Set Rx FIFO Size (in 32-bit words) */ +	writel(RX_FIFO_SIZE >> 2, ®->grxfsiz); + +	/* 12. Set Non Periodic Tx FIFO Size */ +	writel((NPTX_FIFO_SIZE >> 2) << 16 | ((RX_FIFO_SIZE >> 2)) << 0, +	       ®->gnptxfsiz); + +	for (i = 1; i < S3C_MAX_HW_ENDPOINTS; i++) +		writel((PTX_FIFO_SIZE >> 2) << 16 | +		       ((RX_FIFO_SIZE + NPTX_FIFO_SIZE + +			 PTX_FIFO_SIZE*(i-1)) >> 2) << 0, +		       ®->dieptxf[i-1]); + +	/* Flush the RX FIFO */ +	writel(RX_FIFO_FLUSH, ®->grstctl); +	while (readl(®->grstctl) & RX_FIFO_FLUSH) +		DEBUG("%s: waiting for S3C_UDC_OTG_GRSTCTL\n", __func__); + +	/* Flush all the Tx FIFO's */ +	writel(TX_FIFO_FLUSH_ALL, ®->grstctl); +	writel(TX_FIFO_FLUSH_ALL | TX_FIFO_FLUSH, ®->grstctl); +	while (readl(®->grstctl) & TX_FIFO_FLUSH) +		DEBUG("%s: waiting for S3C_UDC_OTG_GRSTCTL\n", __func__); + +	/* 13. Clear NAK bit of EP0, EP1, EP2*/ +	/* For Slave mode*/ +	/* EP0: Control OUT */ +	writel(DEPCTL_EPDIS | DEPCTL_CNAK, +	       ®->out_endp[EP0_CON].doepctl); + +	/* 14. Initialize OTG Link Core.*/ +	writel(GAHBCFG_INIT, ®->gahbcfg); +} + +static void set_max_pktsize(struct s3c_udc *dev, enum usb_device_speed speed) +{ +	unsigned int ep_ctrl; +	int i; + +	if (speed == USB_SPEED_HIGH) { +		ep0_fifo_size = 64; +		ep_fifo_size = 512; +		ep_fifo_size2 = 1024; +		dev->gadget.speed = USB_SPEED_HIGH; +	} else { +		ep0_fifo_size = 64; +		ep_fifo_size = 64; +		ep_fifo_size2 = 64; +		dev->gadget.speed = USB_SPEED_FULL; +	} + +	dev->ep[0].ep.maxpacket = ep0_fifo_size; +	for (i = 1; i < S3C_MAX_ENDPOINTS; i++) +		dev->ep[i].ep.maxpacket = ep_fifo_size; + +	/* EP0 - Control IN (64 bytes)*/ +	ep_ctrl = readl(®->in_endp[EP0_CON].diepctl); +	writel(ep_ctrl|(0<<0), ®->in_endp[EP0_CON].diepctl); + +	/* EP0 - Control OUT (64 bytes)*/ +	ep_ctrl = readl(®->out_endp[EP0_CON].doepctl); +	writel(ep_ctrl|(0<<0), ®->out_endp[EP0_CON].doepctl); +} + +static int s3c_ep_enable(struct usb_ep *_ep, +			 const struct usb_endpoint_descriptor *desc) +{ +	struct s3c_ep *ep; +	struct s3c_udc *dev; +	unsigned long flags; + +	DEBUG("%s: %p\n", __func__, _ep); + +	ep = container_of(_ep, struct s3c_ep, ep); +	if (!_ep || !desc || ep->desc || _ep->name == ep0name +	    || desc->bDescriptorType != USB_DT_ENDPOINT +	    || ep->bEndpointAddress != desc->bEndpointAddress +	    || ep_maxpacket(ep) < le16_to_cpu(desc->wMaxPacketSize)) { + +		DEBUG("%s: bad ep or descriptor\n", __func__); +		return -EINVAL; +	} + +	/* xfer types must match, except that interrupt ~= bulk */ +	if (ep->bmAttributes != desc->bmAttributes +	    && ep->bmAttributes != USB_ENDPOINT_XFER_BULK +	    && desc->bmAttributes != USB_ENDPOINT_XFER_INT) { + +		DEBUG("%s: %s type mismatch\n", __func__, _ep->name); +		return -EINVAL; +	} + +	/* hardware _could_ do smaller, but driver doesn't */ +	if ((desc->bmAttributes == USB_ENDPOINT_XFER_BULK +	     && le16_to_cpu(desc->wMaxPacketSize) != ep_maxpacket(ep)) +	    || !desc->wMaxPacketSize) { + +		DEBUG("%s: bad %s maxpacket\n", __func__, _ep->name); +		return -ERANGE; +	} + +	dev = ep->dev; +	if (!dev->driver || dev->gadget.speed == USB_SPEED_UNKNOWN) { + +		DEBUG("%s: bogus device state\n", __func__); +		return -ESHUTDOWN; +	} + +	ep->stopped = 0; +	ep->desc = desc; +	ep->pio_irqs = 0; +	ep->ep.maxpacket = le16_to_cpu(desc->wMaxPacketSize); + +	/* Reset halt state */ +	s3c_udc_set_nak(ep); +	s3c_udc_set_halt(_ep, 0); + +	spin_lock_irqsave(&ep->dev->lock, flags); +	s3c_udc_ep_activate(ep); +	spin_unlock_irqrestore(&ep->dev->lock, flags); + +	DEBUG("%s: enabled %s, stopped = %d, maxpacket = %d\n", +	      __func__, _ep->name, ep->stopped, ep->ep.maxpacket); +	return 0; +} + +/* + * Disable EP + */ +static int s3c_ep_disable(struct usb_ep *_ep) +{ +	struct s3c_ep *ep; +	unsigned long flags; + +	DEBUG("%s: %p\n", __func__, _ep); + +	ep = container_of(_ep, struct s3c_ep, ep); +	if (!_ep || !ep->desc) { +		DEBUG("%s: %s not enabled\n", __func__, +		      _ep ? ep->ep.name : NULL); +		return -EINVAL; +	} + +	spin_lock_irqsave(&ep->dev->lock, flags); + +	/* Nuke all pending requests */ +	nuke(ep, -ESHUTDOWN); + +	ep->desc = 0; +	ep->stopped = 1; + +	spin_unlock_irqrestore(&ep->dev->lock, flags); + +	DEBUG("%s: disabled %s\n", __func__, _ep->name); +	return 0; +} + +static struct usb_request *s3c_alloc_request(struct usb_ep *ep, +					     gfp_t gfp_flags) +{ +	struct s3c_request *req; + +	DEBUG("%s: %s %p\n", __func__, ep->name, ep); + +	req = kmalloc(sizeof *req, gfp_flags); +	if (!req) +		return 0; + +	memset(req, 0, sizeof *req); +	INIT_LIST_HEAD(&req->queue); + +	return &req->req; +} + +static void s3c_free_request(struct usb_ep *ep, struct usb_request *_req) +{ +	struct s3c_request *req; + +	DEBUG("%s: %p\n", __func__, ep); + +	req = container_of(_req, struct s3c_request, req); +	WARN_ON(!list_empty(&req->queue)); +	kfree(req); +} + +/* dequeue JUST ONE request */ +static int s3c_dequeue(struct usb_ep *_ep, struct usb_request *_req) +{ +	struct s3c_ep *ep; +	struct s3c_request *req; +	unsigned long flags; + +	DEBUG("%s: %p\n", __func__, _ep); + +	ep = container_of(_ep, struct s3c_ep, ep); +	if (!_ep || ep->ep.name == ep0name) +		return -EINVAL; + +	spin_lock_irqsave(&ep->dev->lock, flags); + +	/* make sure it's actually queued on this endpoint */ +	list_for_each_entry(req, &ep->queue, queue) { +		if (&req->req == _req) +			break; +	} +	if (&req->req != _req) { +		spin_unlock_irqrestore(&ep->dev->lock, flags); +		return -EINVAL; +	} + +	done(ep, req, -ECONNRESET); + +	spin_unlock_irqrestore(&ep->dev->lock, flags); +	return 0; +} + +/* + * Return bytes in EP FIFO + */ +static int s3c_fifo_status(struct usb_ep *_ep) +{ +	int count = 0; +	struct s3c_ep *ep; + +	ep = container_of(_ep, struct s3c_ep, ep); +	if (!_ep) { +		DEBUG("%s: bad ep\n", __func__); +		return -ENODEV; +	} + +	DEBUG("%s: %d\n", __func__, ep_index(ep)); + +	/* LPD can't report unclaimed bytes from IN fifos */ +	if (ep_is_in(ep)) +		return -EOPNOTSUPP; + +	return count; +} + +/* + * Flush EP FIFO + */ +static void s3c_fifo_flush(struct usb_ep *_ep) +{ +	struct s3c_ep *ep; + +	ep = container_of(_ep, struct s3c_ep, ep); +	if (unlikely(!_ep || (!ep->desc && ep->ep.name != ep0name))) { +		DEBUG("%s: bad ep\n", __func__); +		return; +	} + +	DEBUG("%s: %d\n", __func__, ep_index(ep)); +} + +static const struct usb_gadget_ops s3c_udc_ops = { +	/* current versions must always be self-powered */ +}; + +static struct s3c_udc memory = { +	.usb_address = 0, +	.gadget = { +		.ops = &s3c_udc_ops, +		.ep0 = &memory.ep[0].ep, +		.name = driver_name, +	}, + +	/* control endpoint */ +	.ep[0] = { +		.ep = { +			.name = ep0name, +			.ops = &s3c_ep_ops, +			.maxpacket = EP0_FIFO_SIZE, +		}, +		.dev = &memory, + +		.bEndpointAddress = 0, +		.bmAttributes = 0, + +		.ep_type = ep_control, +	}, + +	/* first group of endpoints */ +	.ep[1] = { +		.ep = { +			.name = "ep1in-bulk", +			.ops = &s3c_ep_ops, +			.maxpacket = EP_FIFO_SIZE, +		}, +		.dev = &memory, + +		.bEndpointAddress = USB_DIR_IN | 1, +		.bmAttributes = USB_ENDPOINT_XFER_BULK, + +		.ep_type = ep_bulk_out, +		.fifo_num = 1, +	}, + +	.ep[2] = { +		.ep = { +			.name = "ep2out-bulk", +			.ops = &s3c_ep_ops, +			.maxpacket = EP_FIFO_SIZE, +		}, +		.dev = &memory, + +		.bEndpointAddress = USB_DIR_OUT | 2, +		.bmAttributes = USB_ENDPOINT_XFER_BULK, + +		.ep_type = ep_bulk_in, +		.fifo_num = 2, +	}, + +	.ep[3] = { +		.ep = { +			.name = "ep3in-int", +			.ops = &s3c_ep_ops, +			.maxpacket = EP_FIFO_SIZE, +		}, +		.dev = &memory, + +		.bEndpointAddress = USB_DIR_IN | 3, +		.bmAttributes = USB_ENDPOINT_XFER_INT, + +		.ep_type = ep_interrupt, +		.fifo_num = 3, +	}, +}; + +/* + *	probe - binds to the platform device + */ + +int s3c_udc_probe(struct s3c_plat_otg_data *pdata) +{ +	struct s3c_udc *dev = &memory; +	int retval = 0, i; + +	DEBUG("%s: %p\n", __func__, pdata); + +	dev->pdata = pdata; + +	phy = (struct s3c_usbotg_phy *)pdata->regs_phy; +	reg = (struct s3c_usbotg_reg *)pdata->regs_otg; +	usb_phy_ctrl = pdata->usb_phy_ctrl; + +	/* regs_otg = (void *)pdata->regs_otg; */ + +	dev->gadget.is_dualspeed = 1;	/* Hack only*/ +	dev->gadget.is_otg = 0; +	dev->gadget.is_a_peripheral = 0; +	dev->gadget.b_hnp_enable = 0; +	dev->gadget.a_hnp_support = 0; +	dev->gadget.a_alt_hnp_support = 0; + +	the_controller = dev; + +	for (i = 0; i < S3C_MAX_ENDPOINTS+1; i++) { +		dev->dma_buf[i] = kmalloc(DMA_BUFFER_SIZE, GFP_KERNEL); +		dev->dma_addr[i] = (dma_addr_t) dev->dma_buf[i]; +		invalidate_dcache_range((unsigned long) dev->dma_buf[i], +					(unsigned long) (dev->dma_buf[i] +							 + DMA_BUFFER_SIZE)); +	} +	usb_ctrl = dev->dma_buf[0]; +	usb_ctrl_dma_addr = dev->dma_addr[0]; + +	udc_reinit(dev); + +	return retval; +} + +int usb_gadget_handle_interrupts() +{ +	u32 intr_status = readl(®->gintsts); +	u32 gintmsk = readl(®->gintmsk); + +	if (intr_status & gintmsk) +		return s3c_udc_irq(1, (void *)the_controller); +	return 0; +} diff --git a/drivers/usb/gadget/s3c_udc_otg_xfer_dma.c b/drivers/usb/gadget/s3c_udc_otg_xfer_dma.c new file mode 100644 index 000000000..255385750 --- /dev/null +++ b/drivers/usb/gadget/s3c_udc_otg_xfer_dma.c @@ -0,0 +1,1444 @@ +/* + * drivers/usb/gadget/s3c_udc_otg_xfer_dma.c + * Samsung S3C on-chip full/high speed USB OTG 2.0 device controllers + * + * Copyright (C) 2009 for Samsung Electronics + * + * BSP Support for Samsung's UDC driver + * available at: + * git://git.kernel.org/pub/scm/linux/kernel/git/kki_ap/linux-2.6-samsung.git + * + * State machine bugfixes: + * Marek Szyprowski <m.szyprowski@samsung.com> + * + * Ported to u-boot: + * Marek Szyprowski <m.szyprowski@samsung.com> + * Lukasz Majewski <l.majewski@samsumg.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA + * + */ + +static u8 clear_feature_num; +int clear_feature_flag; + +/* Bulk-Only Mass Storage Reset (class-specific request) */ +#define GET_MAX_LUN_REQUEST	0xFE +#define BOT_RESET_REQUEST	0xFF + +static inline void s3c_udc_ep0_zlp(struct s3c_udc *dev) +{ +	u32 ep_ctrl; + +	flush_dcache_range((unsigned long) usb_ctrl_dma_addr, +			   (unsigned long) usb_ctrl_dma_addr +			   + DMA_BUFFER_SIZE); + +	writel(usb_ctrl_dma_addr, ®->in_endp[EP0_CON].diepdma); +	writel(DIEPT_SIZ_PKT_CNT(1), ®->in_endp[EP0_CON].dieptsiz); + +	ep_ctrl = readl(®->in_endp[EP0_CON].diepctl); +	writel(ep_ctrl|DEPCTL_EPENA|DEPCTL_CNAK, +	       ®->in_endp[EP0_CON].diepctl); + +	DEBUG_EP0("%s:EP0 ZLP DIEPCTL0 = 0x%x\n", +		__func__, readl(®->in_endp[EP0_CON].diepctl)); +	dev->ep0state = WAIT_FOR_IN_COMPLETE; +} + +void s3c_udc_pre_setup(void) +{ +	u32 ep_ctrl; + +	DEBUG_IN_EP("%s : Prepare Setup packets.\n", __func__); + +	invalidate_dcache_range((unsigned long) usb_ctrl_dma_addr, +				(unsigned long) usb_ctrl_dma_addr +				+ DMA_BUFFER_SIZE); + +	writel(DOEPT_SIZ_PKT_CNT(1) | sizeof(struct usb_ctrlrequest), +	       ®->out_endp[EP0_CON].doeptsiz); +	writel(usb_ctrl_dma_addr, ®->out_endp[EP0_CON].doepdma); + +	ep_ctrl = readl(®->out_endp[EP0_CON].doepctl); +	writel(ep_ctrl|DEPCTL_EPENA, ®->out_endp[EP0_CON].doepctl); + +	DEBUG_EP0("%s:EP0 ZLP DIEPCTL0 = 0x%x\n", +		__func__, readl(®->in_endp[EP0_CON].diepctl)); +	DEBUG_EP0("%s:EP0 ZLP DOEPCTL0 = 0x%x\n", +		__func__, readl(®->out_endp[EP0_CON].doepctl)); + +} + +static inline void s3c_ep0_complete_out(void) +{ +	u32 ep_ctrl; + +	DEBUG_EP0("%s:EP0 ZLP DIEPCTL0 = 0x%x\n", +		__func__, readl(®->in_endp[EP0_CON].diepctl)); +	DEBUG_EP0("%s:EP0 ZLP DOEPCTL0 = 0x%x\n", +		__func__, readl(®->out_endp[EP0_CON].doepctl)); + +	DEBUG_IN_EP("%s : Prepare Complete Out packet.\n", __func__); + +	invalidate_dcache_range((unsigned long) usb_ctrl_dma_addr, +				(unsigned long) usb_ctrl_dma_addr +				+ DMA_BUFFER_SIZE); + +	writel(DOEPT_SIZ_PKT_CNT(1) | sizeof(struct usb_ctrlrequest), +	       ®->out_endp[EP0_CON].doeptsiz); +	writel(usb_ctrl_dma_addr, ®->out_endp[EP0_CON].doepdma); + +	ep_ctrl = readl(®->out_endp[EP0_CON].doepctl); +	writel(ep_ctrl|DEPCTL_EPENA|DEPCTL_CNAK, +	       ®->out_endp[EP0_CON].doepctl); + +	DEBUG_EP0("%s:EP0 ZLP DIEPCTL0 = 0x%x\n", +		__func__, readl(®->in_endp[EP0_CON].diepctl)); +	DEBUG_EP0("%s:EP0 ZLP DOEPCTL0 = 0x%x\n", +		__func__, readl(®->out_endp[EP0_CON].doepctl)); + +} + + +static int setdma_rx(struct s3c_ep *ep, struct s3c_request *req) +{ +	u32 *buf, ctrl; +	u32 length, pktcnt; +	u32 ep_num = ep_index(ep); + +	buf = req->req.buf + req->req.actual; + +	length = min(req->req.length - req->req.actual, (int)ep->ep.maxpacket); + +	ep->len = length; +	ep->dma_buf = buf; + +	invalidate_dcache_range((unsigned long) ep->dev->dma_buf[ep_num], +				(unsigned long) ep->dev->dma_buf[ep_num] +				+ DMA_BUFFER_SIZE); + +	if (length == 0) +		pktcnt = 1; +	else +		pktcnt = (length - 1)/(ep->ep.maxpacket) + 1; + +	pktcnt = 1; +	ctrl =  readl(®->out_endp[ep_num].doepctl); + +	writel(the_controller->dma_addr[ep_index(ep)+1], +	       ®->out_endp[ep_num].doepdma); +	writel(DOEPT_SIZ_PKT_CNT(pktcnt) | DOEPT_SIZ_XFER_SIZE(length), +	       ®->out_endp[ep_num].doeptsiz); +	writel(DEPCTL_EPENA|DEPCTL_CNAK|ctrl, ®->out_endp[ep_num].doepctl); + +	DEBUG_OUT_EP("%s: EP%d RX DMA start : DOEPDMA = 0x%x," +		     "DOEPTSIZ = 0x%x, DOEPCTL = 0x%x\n" +		     "\tbuf = 0x%p, pktcnt = %d, xfersize = %d\n", +		     __func__, ep_num, +		     readl(®->out_endp[ep_num].doepdma), +		     readl(®->out_endp[ep_num].doeptsiz), +		     readl(®->out_endp[ep_num].doepctl), +		     buf, pktcnt, length); +	return 0; + +} + +int setdma_tx(struct s3c_ep *ep, struct s3c_request *req) +{ +	u32 *buf, ctrl = 0; +	u32 length, pktcnt; +	u32 ep_num = ep_index(ep); +	u32 *p = the_controller->dma_buf[ep_index(ep)+1]; + +	buf = req->req.buf + req->req.actual; +	length = req->req.length - req->req.actual; + +	if (ep_num == EP0_CON) +		length = min_t(length, (u32)ep_maxpacket(ep)); + +	ep->len = length; +	ep->dma_buf = buf; +	memcpy(p, ep->dma_buf, length); + +	flush_dcache_range((unsigned long) p , +			   (unsigned long) p + DMA_BUFFER_SIZE); + +	if (length == 0) +		pktcnt = 1; +	else +		pktcnt = (length - 1)/(ep->ep.maxpacket) + 1; + +	/* Flush the endpoint's Tx FIFO */ +	writel(TX_FIFO_NUMBER(ep->fifo_num), ®->grstctl); +	writel(TX_FIFO_NUMBER(ep->fifo_num) | TX_FIFO_FLUSH, ®->grstctl); +	while (readl(®->grstctl) & TX_FIFO_FLUSH) +		; + +	writel(the_controller->dma_addr[ep_index(ep)+1], +	       ®->in_endp[ep_num].diepdma); +	writel(DIEPT_SIZ_PKT_CNT(pktcnt) | DIEPT_SIZ_XFER_SIZE(length), +	       ®->in_endp[ep_num].dieptsiz); + +	ctrl = readl(®->in_endp[ep_num].diepctl); + +	/* Write the FIFO number to be used for this endpoint */ +	ctrl &= DIEPCTL_TX_FIFO_NUM_MASK; +	ctrl |= DIEPCTL_TX_FIFO_NUM(ep->fifo_num); + +	/* Clear reserved (Next EP) bits */ +	ctrl = (ctrl&~(EP_MASK<<DEPCTL_NEXT_EP_BIT)); + +	writel(DEPCTL_EPENA|DEPCTL_CNAK|ctrl, ®->in_endp[ep_num].diepctl); + +	DEBUG_IN_EP("%s:EP%d TX DMA start : DIEPDMA0 = 0x%x," +		    "DIEPTSIZ0 = 0x%x, DIEPCTL0 = 0x%x\n" +		    "\tbuf = 0x%p, pktcnt = %d, xfersize = %d\n", +		    __func__, ep_num, +		    readl(®->in_endp[ep_num].diepdma), +		    readl(®->in_endp[ep_num].dieptsiz), +		    readl(®->in_endp[ep_num].diepctl), +		    buf, pktcnt, length); + +	return length; +} + +static void complete_rx(struct s3c_udc *dev, u8 ep_num) +{ +	struct s3c_ep *ep = &dev->ep[ep_num]; +	struct s3c_request *req = NULL; +	u32 ep_tsr = 0, xfer_size = 0, is_short = 0; +	u32 *p = the_controller->dma_buf[ep_index(ep)+1]; + +	if (list_empty(&ep->queue)) { +		DEBUG_OUT_EP("%s: RX DMA done : NULL REQ on OUT EP-%d\n", +					__func__, ep_num); +		return; + +	} + +	req = list_entry(ep->queue.next, struct s3c_request, queue); +	ep_tsr = readl(®->out_endp[ep_num].doeptsiz); + +	if (ep_num == EP0_CON) +		xfer_size = (ep_tsr & DOEPT_SIZ_XFER_SIZE_MAX_EP0); +	else +		xfer_size = (ep_tsr & DOEPT_SIZ_XFER_SIZE_MAX_EP); + +	xfer_size = ep->len - xfer_size; + +	invalidate_dcache_range((unsigned long) p, +				(unsigned long) p + DMA_BUFFER_SIZE); + +	memcpy(ep->dma_buf, p, ep->len); + +	req->req.actual += min(xfer_size, req->req.length - req->req.actual); +	is_short = (xfer_size < ep->ep.maxpacket); + +	DEBUG_OUT_EP("%s: RX DMA done : ep = %d, rx bytes = %d/%d, " +		     "is_short = %d, DOEPTSIZ = 0x%x, remained bytes = %d\n", +			__func__, ep_num, req->req.actual, req->req.length, +			is_short, ep_tsr, xfer_size); + +	if (is_short || req->req.actual == req->req.length) { +		if (ep_num == EP0_CON && dev->ep0state == DATA_STATE_RECV) { +			DEBUG_OUT_EP("	=> Send ZLP\n"); +			s3c_udc_ep0_zlp(dev); +			/* packet will be completed in complete_tx() */ +			dev->ep0state = WAIT_FOR_IN_COMPLETE; +		} else { +			done(ep, req, 0); + +			if (!list_empty(&ep->queue)) { +				req = list_entry(ep->queue.next, +					struct s3c_request, queue); +				DEBUG_OUT_EP("%s: Next Rx request start...\n", +					 __func__); +				setdma_rx(ep, req); +			} +		} +	} else +		setdma_rx(ep, req); +} + +static void complete_tx(struct s3c_udc *dev, u8 ep_num) +{ +	struct s3c_ep *ep = &dev->ep[ep_num]; +	struct s3c_request *req; +	u32 ep_tsr = 0, xfer_size = 0, is_short = 0; +	u32 last; + +	if (dev->ep0state == WAIT_FOR_NULL_COMPLETE) { +		dev->ep0state = WAIT_FOR_OUT_COMPLETE; +		s3c_ep0_complete_out(); +		return; +	} + +	if (list_empty(&ep->queue)) { +		DEBUG_IN_EP("%s: TX DMA done : NULL REQ on IN EP-%d\n", +					__func__, ep_num); +		return; + +	} + +	req = list_entry(ep->queue.next, struct s3c_request, queue); + +	ep_tsr = readl(®->in_endp[ep_num].dieptsiz); + +	xfer_size = ep->len; +	is_short = (xfer_size < ep->ep.maxpacket); +	req->req.actual += min(xfer_size, req->req.length - req->req.actual); + +	DEBUG_IN_EP("%s: TX DMA done : ep = %d, tx bytes = %d/%d, " +		     "is_short = %d, DIEPTSIZ = 0x%x, remained bytes = %d\n", +			__func__, ep_num, req->req.actual, req->req.length, +			is_short, ep_tsr, xfer_size); + +	if (ep_num == 0) { +		if (dev->ep0state == DATA_STATE_XMIT) { +			DEBUG_IN_EP("%s: ep_num = %d, ep0stat ==" +				    "DATA_STATE_XMIT\n", +				    __func__, ep_num); +			last = write_fifo_ep0(ep, req); +			if (last) +				dev->ep0state = WAIT_FOR_COMPLETE; +		} else if (dev->ep0state == WAIT_FOR_IN_COMPLETE) { +			DEBUG_IN_EP("%s: ep_num = %d, completing request\n", +				    __func__, ep_num); +			done(ep, req, 0); +			dev->ep0state = WAIT_FOR_SETUP; +		} else if (dev->ep0state == WAIT_FOR_COMPLETE) { +			DEBUG_IN_EP("%s: ep_num = %d, completing request\n", +				    __func__, ep_num); +			done(ep, req, 0); +			dev->ep0state = WAIT_FOR_OUT_COMPLETE; +			s3c_ep0_complete_out(); +		} else { +			DEBUG_IN_EP("%s: ep_num = %d, invalid ep state\n", +				    __func__, ep_num); +		} +		return; +	} + +	if (req->req.actual == req->req.length) +		done(ep, req, 0); + +	if (!list_empty(&ep->queue)) { +		req = list_entry(ep->queue.next, struct s3c_request, queue); +		DEBUG_IN_EP("%s: Next Tx request start...\n", __func__); +		setdma_tx(ep, req); +	} +} + +static inline void s3c_udc_check_tx_queue(struct s3c_udc *dev, u8 ep_num) +{ +	struct s3c_ep *ep = &dev->ep[ep_num]; +	struct s3c_request *req; + +	DEBUG_IN_EP("%s: Check queue, ep_num = %d\n", __func__, ep_num); + +	if (!list_empty(&ep->queue)) { +		req = list_entry(ep->queue.next, struct s3c_request, queue); +		DEBUG_IN_EP("%s: Next Tx request(0x%p) start...\n", +			    __func__, req); + +		if (ep_is_in(ep)) +			setdma_tx(ep, req); +		else +			setdma_rx(ep, req); +	} else { +		DEBUG_IN_EP("%s: NULL REQ on IN EP-%d\n", __func__, ep_num); + +		return; +	} + +} + +static void process_ep_in_intr(struct s3c_udc *dev) +{ +	u32 ep_intr, ep_intr_status; +	u8 ep_num = 0; + +	ep_intr = readl(®->daint); +	DEBUG_IN_EP("*** %s: EP In interrupt : DAINT = 0x%x\n", +				__func__, ep_intr); + +	ep_intr &= DAINT_MASK; + +	while (ep_intr) { +		if (ep_intr & DAINT_IN_EP_INT(1)) { +			ep_intr_status = readl(®->in_endp[ep_num].diepint); +			DEBUG_IN_EP("\tEP%d-IN : DIEPINT = 0x%x\n", +						ep_num, ep_intr_status); + +			/* Interrupt Clear */ +			writel(ep_intr_status, ®->in_endp[ep_num].diepint); + +			if (ep_intr_status & TRANSFER_DONE) { +				complete_tx(dev, ep_num); + +				if (ep_num == 0) { +					if (dev->ep0state == +					    WAIT_FOR_IN_COMPLETE) +						dev->ep0state = WAIT_FOR_SETUP; + +					if (dev->ep0state == WAIT_FOR_SETUP) +						s3c_udc_pre_setup(); + +					/* continue transfer after +					   set_clear_halt for DMA mode */ +					if (clear_feature_flag == 1) { +						s3c_udc_check_tx_queue(dev, +							clear_feature_num); +						clear_feature_flag = 0; +					} +				} +			} +		} +		ep_num++; +		ep_intr >>= 1; +	} +} + +static void process_ep_out_intr(struct s3c_udc *dev) +{ +	u32 ep_intr, ep_intr_status; +	u8 ep_num = 0; + +	ep_intr = readl(®->daint); +	DEBUG_OUT_EP("*** %s: EP OUT interrupt : DAINT = 0x%x\n", +				__func__, ep_intr); + +	ep_intr = (ep_intr >> DAINT_OUT_BIT) & DAINT_MASK; + +	while (ep_intr) { +		if (ep_intr & 0x1) { +			ep_intr_status = readl(®->out_endp[ep_num].doepint); +			DEBUG_OUT_EP("\tEP%d-OUT : DOEPINT = 0x%x\n", +						ep_num, ep_intr_status); + +			/* Interrupt Clear */ +			writel(ep_intr_status, ®->out_endp[ep_num].doepint); + +			if (ep_num == 0) { +				if (ep_intr_status & TRANSFER_DONE) { +					if (dev->ep0state != +					    WAIT_FOR_OUT_COMPLETE) +						complete_rx(dev, ep_num); +					else { +						dev->ep0state = WAIT_FOR_SETUP; +						s3c_udc_pre_setup(); +					} +				} + +				if (ep_intr_status & +				    CTRL_OUT_EP_SETUP_PHASE_DONE) { +					DEBUG_OUT_EP("SETUP packet arrived\n"); +					s3c_handle_ep0(dev); +				} +			} else { +				if (ep_intr_status & TRANSFER_DONE) +					complete_rx(dev, ep_num); +			} +		} +		ep_num++; +		ep_intr >>= 1; +	} +} + +/* + *	usb client interrupt handler. + */ +static int s3c_udc_irq(int irq, void *_dev) +{ +	struct s3c_udc *dev = _dev; +	u32 intr_status; +	u32 usb_status, gintmsk; +	unsigned long flags; + +	spin_lock_irqsave(&dev->lock, flags); + +	intr_status = readl(®->gintsts); +	gintmsk = readl(®->gintmsk); + +	DEBUG_ISR("\n*** %s : GINTSTS=0x%x(on state %s), GINTMSK : 0x%x," +		  "DAINT : 0x%x, DAINTMSK : 0x%x\n", +		  __func__, intr_status, state_names[dev->ep0state], gintmsk, +		  readl(®->daint), readl(®->daintmsk)); + +	if (!intr_status) { +		spin_unlock_irqrestore(&dev->lock, flags); +		return IRQ_HANDLED; +	} + +	if (intr_status & INT_ENUMDONE) { +		DEBUG_ISR("\tSpeed Detection interrupt\n"); + +		writel(INT_ENUMDONE, ®->gintsts); +		usb_status = (readl(®->dsts) & 0x6); + +		if (usb_status & (USB_FULL_30_60MHZ | USB_FULL_48MHZ)) { +			DEBUG_ISR("\t\tFull Speed Detection\n"); +			set_max_pktsize(dev, USB_SPEED_FULL); + +		} else { +			DEBUG_ISR("\t\tHigh Speed Detection : 0x%x\n", +				  usb_status); +			set_max_pktsize(dev, USB_SPEED_HIGH); +		} +	} + +	if (intr_status & INT_EARLY_SUSPEND) { +		DEBUG_ISR("\tEarly suspend interrupt\n"); +		writel(INT_EARLY_SUSPEND, ®->gintsts); +	} + +	if (intr_status & INT_SUSPEND) { +		usb_status = readl(®->dsts); +		DEBUG_ISR("\tSuspend interrupt :(DSTS):0x%x\n", usb_status); +		writel(INT_SUSPEND, ®->gintsts); + +		if (dev->gadget.speed != USB_SPEED_UNKNOWN +		    && dev->driver) { +			if (dev->driver->suspend) +				dev->driver->suspend(&dev->gadget); + +			/* HACK to let gadget detect disconnected state */ +			if (dev->driver->disconnect) { +				spin_unlock_irqrestore(&dev->lock, flags); +				dev->driver->disconnect(&dev->gadget); +				spin_lock_irqsave(&dev->lock, flags); +			} +		} +	} + +	if (intr_status & INT_RESUME) { +		DEBUG_ISR("\tResume interrupt\n"); +		writel(INT_RESUME, ®->gintsts); + +		if (dev->gadget.speed != USB_SPEED_UNKNOWN +		    && dev->driver +		    && dev->driver->resume) { + +			dev->driver->resume(&dev->gadget); +		} +	} + +	if (intr_status & INT_RESET) { +		usb_status = readl(®->gotgctl); +		DEBUG_ISR("\tReset interrupt - (GOTGCTL):0x%x\n", usb_status); +		writel(INT_RESET, ®->gintsts); + +		if ((usb_status & 0xc0000) == (0x3 << 18)) { +			if (reset_available) { +				DEBUG_ISR("\t\tOTG core got reset (%d)!!\n", +					  reset_available); +				reconfig_usbd(); +				dev->ep0state = WAIT_FOR_SETUP; +				reset_available = 0; +				s3c_udc_pre_setup(); +			} else +				reset_available = 1; + +		} else { +			reset_available = 1; +			DEBUG_ISR("\t\tRESET handling skipped\n"); +		} +	} + +	if (intr_status & INT_IN_EP) +		process_ep_in_intr(dev); + +	if (intr_status & INT_OUT_EP) +		process_ep_out_intr(dev); + +	spin_unlock_irqrestore(&dev->lock, flags); + +	return IRQ_HANDLED; +} + +/** Queue one request + *  Kickstart transfer if needed + */ +static int s3c_queue(struct usb_ep *_ep, struct usb_request *_req, +			 gfp_t gfp_flags) +{ +	struct s3c_request *req; +	struct s3c_ep *ep; +	struct s3c_udc *dev; +	unsigned long flags; +	u32 ep_num, gintsts; + +	req = container_of(_req, struct s3c_request, req); +	if (unlikely(!_req || !_req->complete || !_req->buf +		     || !list_empty(&req->queue))) { + +		DEBUG("%s: bad params\n", __func__); +		return -EINVAL; +	} + +	ep = container_of(_ep, struct s3c_ep, ep); + +	if (unlikely(!_ep || (!ep->desc && ep->ep.name != ep0name))) { + +		DEBUG("%s: bad ep: %s, %d, %x\n", __func__, +		      ep->ep.name, !ep->desc, _ep); +		return -EINVAL; +	} + +	ep_num = ep_index(ep); +	dev = ep->dev; +	if (unlikely(!dev->driver || dev->gadget.speed == USB_SPEED_UNKNOWN)) { + +		DEBUG("%s: bogus device state %p\n", __func__, dev->driver); +		return -ESHUTDOWN; +	} + +	spin_lock_irqsave(&dev->lock, flags); + +	_req->status = -EINPROGRESS; +	_req->actual = 0; + +	/* kickstart this i/o queue? */ +	DEBUG("\n*** %s: %s-%s req = %p, len = %d, buf = %p" +		"Q empty = %d, stopped = %d\n", +		__func__, _ep->name, ep_is_in(ep) ? "in" : "out", +		_req, _req->length, _req->buf, +		list_empty(&ep->queue), ep->stopped); + +#ifdef DEBUG_S3C_UDC +	{ +		int i, len = _req->length; + +		printf("pkt = "); +		if (len > 64) +			len = 64; +		for (i = 0; i < len; i++) { +			printf("%02x", ((u8 *)_req->buf)[i]); +			if ((i & 7) == 7) +				printf(" "); +		} +		printf("\n"); +	} +#endif + +	if (list_empty(&ep->queue) && !ep->stopped) { + +		if (ep_num == 0) { +			/* EP0 */ +			list_add_tail(&req->queue, &ep->queue); +			s3c_ep0_kick(dev, ep); +			req = 0; + +		} else if (ep_is_in(ep)) { +			gintsts = readl(®->gintsts); +			DEBUG_IN_EP("%s: ep_is_in, S3C_UDC_OTG_GINTSTS=0x%x\n", +						__func__, gintsts); + +			setdma_tx(ep, req); +		} else { +			gintsts = readl(®->gintsts); +			DEBUG_OUT_EP("%s:ep_is_out, S3C_UDC_OTG_GINTSTS=0x%x\n", +				__func__, gintsts); + +			setdma_rx(ep, req); +		} +	} + +	/* pio or dma irq handler advances the queue. */ +	if (likely(req != 0)) +		list_add_tail(&req->queue, &ep->queue); + +	spin_unlock_irqrestore(&dev->lock, flags); + +	return 0; +} + +/****************************************************************/ +/* End Point 0 related functions                                */ +/****************************************************************/ + +/* return:  0 = still running, 1 = completed, negative = errno */ +static int write_fifo_ep0(struct s3c_ep *ep, struct s3c_request *req) +{ +	u32 max; +	unsigned count; +	int is_last; + +	max = ep_maxpacket(ep); + +	DEBUG_EP0("%s: max = %d\n", __func__, max); + +	count = setdma_tx(ep, req); + +	/* last packet is usually short (or a zlp) */ +	if (likely(count != max)) +		is_last = 1; +	else { +		if (likely(req->req.length != req->req.actual + count) +		    || req->req.zero) +			is_last = 0; +		else +			is_last = 1; +	} + +	DEBUG_EP0("%s: wrote %s %d bytes%s %d left %p\n", __func__, +		  ep->ep.name, count, +		  is_last ? "/L" : "", +		  req->req.length - req->req.actual - count, req); + +	/* requests complete when all IN data is in the FIFO */ +	if (is_last) { +		ep->dev->ep0state = WAIT_FOR_SETUP; +		return 1; +	} + +	return 0; +} + +int s3c_fifo_read(struct s3c_ep *ep, u32 *cp, int max) +{ +	u32 bytes; + +	bytes = sizeof(struct usb_ctrlrequest); + +	invalidate_dcache_range((unsigned long) ep->dev->dma_buf[ep_index(ep)], +				(unsigned long) ep->dev->dma_buf[ep_index(ep)] +				+ DMA_BUFFER_SIZE); + +	DEBUG_EP0("%s: bytes=%d, ep_index=%d %p\n", __func__, +		  bytes, ep_index(ep), ep->dev->dma_buf[ep_index(ep)]); + +	return bytes; +} + +/** + * udc_set_address - set the USB address for this device + * @address: + * + * Called from control endpoint function + * after it decodes a set address setup packet. + */ +static void udc_set_address(struct s3c_udc *dev, unsigned char address) +{ +	u32 ctrl = readl(®->dcfg); +	writel(DEVICE_ADDRESS(address) | ctrl, ®->dcfg); + +	s3c_udc_ep0_zlp(dev); + +	DEBUG_EP0("%s: USB OTG 2.0 Device address=%d, DCFG=0x%x\n", +		__func__, address, readl(®->dcfg)); + +	dev->usb_address = address; +} + +static inline void s3c_udc_ep0_set_stall(struct s3c_ep *ep) +{ +	struct s3c_udc *dev; +	u32		ep_ctrl = 0; + +	dev = ep->dev; +	ep_ctrl = readl(®->in_endp[EP0_CON].diepctl); + +	/* set the disable and stall bits */ +	if (ep_ctrl & DEPCTL_EPENA) +		ep_ctrl |= DEPCTL_EPDIS; + +	ep_ctrl |= DEPCTL_STALL; + +	writel(ep_ctrl, ®->in_endp[EP0_CON].diepctl); + +	DEBUG_EP0("%s: set ep%d stall, DIEPCTL0 = 0x%x\n", +		__func__, ep_index(ep), ®->in_endp[EP0_CON].diepctl); +	/* +	 * The application can only set this bit, and the core clears it, +	 * when a SETUP token is received for this endpoint +	 */ +	dev->ep0state = WAIT_FOR_SETUP; + +	s3c_udc_pre_setup(); +} + +static void s3c_ep0_read(struct s3c_udc *dev) +{ +	struct s3c_request *req; +	struct s3c_ep *ep = &dev->ep[0]; +	int ret; + +	if (!list_empty(&ep->queue)) { +		req = list_entry(ep->queue.next, struct s3c_request, queue); + +	} else { +		DEBUG("%s: ---> BUG\n", __func__); +		BUG(); +		return; +	} + +	DEBUG_EP0("%s: req = %p, req.length = 0x%x, req.actual = 0x%x\n", +		__func__, req, req->req.length, req->req.actual); + +	if (req->req.length == 0) { +		/* zlp for Set_configuration, Set_interface, +		 * or Bulk-Only mass storge reset */ + +		ep->len = 0; +		s3c_udc_ep0_zlp(dev); + +		DEBUG_EP0("%s: req.length = 0, bRequest = %d\n", +			  __func__, usb_ctrl->bRequest); +		return; +	} + +	ret = setdma_rx(ep, req); +} + +/* + * DATA_STATE_XMIT + */ +static int s3c_ep0_write(struct s3c_udc *dev) +{ +	struct s3c_request *req; +	struct s3c_ep *ep = &dev->ep[0]; +	int ret, need_zlp = 0; + +	if (list_empty(&ep->queue)) +		req = 0; +	else +		req = list_entry(ep->queue.next, struct s3c_request, queue); + +	if (!req) { +		DEBUG_EP0("%s: NULL REQ\n", __func__); +		return 0; +	} + +	DEBUG_EP0("%s: req = %p, req.length = 0x%x, req.actual = 0x%x\n", +		__func__, req, req->req.length, req->req.actual); + +	if (req->req.length - req->req.actual == ep0_fifo_size) { +		/* Next write will end with the packet size, */ +		/* so we need Zero-length-packet */ +		need_zlp = 1; +	} + +	ret = write_fifo_ep0(ep, req); + +	if ((ret == 1) && !need_zlp) { +		/* Last packet */ +		dev->ep0state = WAIT_FOR_COMPLETE; +		DEBUG_EP0("%s: finished, waiting for status\n", __func__); + +	} else { +		dev->ep0state = DATA_STATE_XMIT; +		DEBUG_EP0("%s: not finished\n", __func__); +	} + +	return 1; +} + +u16	g_status; + +int s3c_udc_get_status(struct s3c_udc *dev, +		struct usb_ctrlrequest *crq) +{ +	u8 ep_num = crq->wIndex & 0x7F; +	u32 ep_ctrl; +	u32 *p = the_controller->dma_buf[1]; + +	DEBUG_SETUP("%s: *** USB_REQ_GET_STATUS\n", __func__); +	printf("crq->brequest:0x%x\n", crq->bRequestType & USB_RECIP_MASK); +	switch (crq->bRequestType & USB_RECIP_MASK) { +	case USB_RECIP_INTERFACE: +		g_status = 0; +		DEBUG_SETUP("\tGET_STATUS:USB_RECIP_INTERFACE, g_stauts = %d\n", +			    g_status); +		break; + +	case USB_RECIP_DEVICE: +		g_status = 0x1; /* Self powered */ +		DEBUG_SETUP("\tGET_STATUS: USB_RECIP_DEVICE, g_stauts = %d\n", +			    g_status); +		break; + +	case USB_RECIP_ENDPOINT: +		if (crq->wLength > 2) { +			DEBUG_SETUP("\tGET_STATUS:Not support EP or wLength\n"); +			return 1; +		} + +		g_status = dev->ep[ep_num].stopped; +		DEBUG_SETUP("\tGET_STATUS: USB_RECIP_ENDPOINT, g_stauts = %d\n", +			    g_status); + +		break; + +	default: +		return 1; +	} + +	memcpy(p, &g_status, sizeof(g_status)); + +	flush_dcache_range((unsigned long) p, +			   (unsigned long) p + DMA_BUFFER_SIZE); + +	writel(the_controller->dma_addr[1], ®->in_endp[EP0_CON].diepdma); +	writel(DIEPT_SIZ_PKT_CNT(1) | DIEPT_SIZ_XFER_SIZE(2), +	       ®->in_endp[EP0_CON].dieptsiz); + +	ep_ctrl = readl(®->in_endp[EP0_CON].diepctl); +	writel(ep_ctrl|DEPCTL_EPENA|DEPCTL_CNAK, +	       ®->in_endp[EP0_CON].diepctl); +	dev->ep0state = WAIT_FOR_NULL_COMPLETE; + +	return 0; +} + +static void s3c_udc_set_nak(struct s3c_ep *ep) +{ +	u8		ep_num; +	u32		ep_ctrl = 0; + +	ep_num = ep_index(ep); +	DEBUG("%s: ep_num = %d, ep_type = %d\n", __func__, ep_num, ep->ep_type); + +	if (ep_is_in(ep)) { +		ep_ctrl = readl(®->in_endp[ep_num].diepctl); +		ep_ctrl |= DEPCTL_SNAK; +		writel(ep_ctrl, ®->in_endp[ep_num].diepctl); +		DEBUG("%s: set NAK, DIEPCTL%d = 0x%x\n", +			__func__, ep_num, readl(®->in_endp[ep_num].diepctl)); +	} else { +		ep_ctrl = readl(®->out_endp[ep_num].doepctl); +		ep_ctrl |= DEPCTL_SNAK; +		writel(ep_ctrl, ®->out_endp[ep_num].doepctl); +		DEBUG("%s: set NAK, DOEPCTL%d = 0x%x\n", +		      __func__, ep_num, readl(®->out_endp[ep_num].doepctl)); +	} + +	return; +} + + +void s3c_udc_ep_set_stall(struct s3c_ep *ep) +{ +	u8		ep_num; +	u32		ep_ctrl = 0; + +	ep_num = ep_index(ep); +	DEBUG("%s: ep_num = %d, ep_type = %d\n", __func__, ep_num, ep->ep_type); + +	if (ep_is_in(ep)) { +		ep_ctrl = readl(®->in_endp[ep_num].diepctl); + +		/* set the disable and stall bits */ +		if (ep_ctrl & DEPCTL_EPENA) +			ep_ctrl |= DEPCTL_EPDIS; + +		ep_ctrl |= DEPCTL_STALL; + +		writel(ep_ctrl, ®->in_endp[ep_num].diepctl); +		DEBUG("%s: set stall, DIEPCTL%d = 0x%x\n", +		      __func__, ep_num, readl(®->in_endp[ep_num].diepctl)); + +	} else { +		ep_ctrl = readl(®->out_endp[ep_num].doepctl); + +		/* set the stall bit */ +		ep_ctrl |= DEPCTL_STALL; + +		writel(ep_ctrl, ®->out_endp[ep_num].doepctl); +		DEBUG("%s: set stall, DOEPCTL%d = 0x%x\n", +		      __func__, ep_num, readl(®->out_endp[ep_num].doepctl)); +	} + +	return; +} + +void s3c_udc_ep_clear_stall(struct s3c_ep *ep) +{ +	u8		ep_num; +	u32		ep_ctrl = 0; + +	ep_num = ep_index(ep); +	DEBUG("%s: ep_num = %d, ep_type = %d\n", __func__, ep_num, ep->ep_type); + +	if (ep_is_in(ep)) { +		ep_ctrl = readl(®->in_endp[ep_num].diepctl); + +		/* clear stall bit */ +		ep_ctrl &= ~DEPCTL_STALL; + +		/* +		 * USB Spec 9.4.5: For endpoints using data toggle, regardless +		 * of whether an endpoint has the Halt feature set, a +		 * ClearFeature(ENDPOINT_HALT) request always results in the +		 * data toggle being reinitialized to DATA0. +		 */ +		if (ep->bmAttributes == USB_ENDPOINT_XFER_INT +		    || ep->bmAttributes == USB_ENDPOINT_XFER_BULK) { +			ep_ctrl |= DEPCTL_SETD0PID; /* DATA0 */ +		} + +		writel(ep_ctrl, ®->in_endp[ep_num].diepctl); +		DEBUG("%s: cleared stall, DIEPCTL%d = 0x%x\n", +			__func__, ep_num, readl(®->in_endp[ep_num].diepctl)); + +	} else { +		ep_ctrl = readl(®->out_endp[ep_num].doepctl); + +		/* clear stall bit */ +		ep_ctrl &= ~DEPCTL_STALL; + +		if (ep->bmAttributes == USB_ENDPOINT_XFER_INT +		    || ep->bmAttributes == USB_ENDPOINT_XFER_BULK) { +			ep_ctrl |= DEPCTL_SETD0PID; /* DATA0 */ +		} + +		writel(ep_ctrl, ®->out_endp[ep_num].doepctl); +		DEBUG("%s: cleared stall, DOEPCTL%d = 0x%x\n", +		      __func__, ep_num, readl(®->out_endp[ep_num].doepctl)); +	} + +	return; +} + +static int s3c_udc_set_halt(struct usb_ep *_ep, int value) +{ +	struct s3c_ep	*ep; +	struct s3c_udc	*dev; +	unsigned long	flags; +	u8		ep_num; + +	ep = container_of(_ep, struct s3c_ep, ep); +	ep_num = ep_index(ep); + +	if (unlikely(!_ep || !ep->desc || ep_num == EP0_CON || +		     ep->desc->bmAttributes == USB_ENDPOINT_XFER_ISOC)) { +		DEBUG("%s: %s bad ep or descriptor\n", __func__, ep->ep.name); +		return -EINVAL; +	} + +	/* Attempt to halt IN ep will fail if any transfer requests +	 * are still queue */ +	if (value && ep_is_in(ep) && !list_empty(&ep->queue)) { +		DEBUG("%s: %s queue not empty, req = %p\n", +			__func__, ep->ep.name, +			list_entry(ep->queue.next, struct s3c_request, queue)); + +		return -EAGAIN; +	} + +	dev = ep->dev; +	DEBUG("%s: ep_num = %d, value = %d\n", __func__, ep_num, value); + +	spin_lock_irqsave(&dev->lock, flags); + +	if (value == 0) { +		ep->stopped = 0; +		s3c_udc_ep_clear_stall(ep); +	} else { +		if (ep_num == 0) +			dev->ep0state = WAIT_FOR_SETUP; + +		ep->stopped = 1; +		s3c_udc_ep_set_stall(ep); +	} + +	spin_unlock_irqrestore(&dev->lock, flags); + +	return 0; +} + +void s3c_udc_ep_activate(struct s3c_ep *ep) +{ +	u8 ep_num; +	u32 ep_ctrl = 0, daintmsk = 0; + +	ep_num = ep_index(ep); + +	/* Read DEPCTLn register */ +	if (ep_is_in(ep)) { +		ep_ctrl = readl(®->in_endp[ep_num].diepctl); +		daintmsk = 1 << ep_num; +	} else { +		ep_ctrl = readl(®->out_endp[ep_num].doepctl); +		daintmsk = (1 << ep_num) << DAINT_OUT_BIT; +	} + +	DEBUG("%s: EPCTRL%d = 0x%x, ep_is_in = %d\n", +		__func__, ep_num, ep_ctrl, ep_is_in(ep)); + +	/* If the EP is already active don't change the EP Control +	 * register. */ +	if (!(ep_ctrl & DEPCTL_USBACTEP)) { +		ep_ctrl = (ep_ctrl & ~DEPCTL_TYPE_MASK) | +			(ep->bmAttributes << DEPCTL_TYPE_BIT); +		ep_ctrl = (ep_ctrl & ~DEPCTL_MPS_MASK) | +			(ep->ep.maxpacket << DEPCTL_MPS_BIT); +		ep_ctrl |= (DEPCTL_SETD0PID | DEPCTL_USBACTEP | DEPCTL_SNAK); + +		if (ep_is_in(ep)) { +			writel(ep_ctrl, ®->in_endp[ep_num].diepctl); +			DEBUG("%s: USB Ative EP%d, DIEPCTRL%d = 0x%x\n", +			      __func__, ep_num, ep_num, +			      readl(®->in_endp[ep_num].diepctl)); +		} else { +			writel(ep_ctrl, ®->out_endp[ep_num].doepctl); +			DEBUG("%s: USB Ative EP%d, DOEPCTRL%d = 0x%x\n", +			      __func__, ep_num, ep_num, +			      readl(®->out_endp[ep_num].doepctl)); +		} +	} + +	/* Unmask EP Interrtupt */ +	writel(readl(®->daintmsk)|daintmsk, ®->daintmsk); +	DEBUG("%s: DAINTMSK = 0x%x\n", __func__, readl(®->daintmsk)); + +} + +static int s3c_udc_clear_feature(struct usb_ep *_ep) +{ +	struct s3c_udc	*dev; +	struct s3c_ep	*ep; +	u8		ep_num; + +	ep = container_of(_ep, struct s3c_ep, ep); +	ep_num = ep_index(ep); + +	dev = ep->dev; +	DEBUG_SETUP("%s: ep_num = %d, is_in = %d, clear_feature_flag = %d\n", +		__func__, ep_num, ep_is_in(ep), clear_feature_flag); + +	if (usb_ctrl->wLength != 0) { +		DEBUG_SETUP("\tCLEAR_FEATURE: wLength is not zero.....\n"); +		return 1; +	} + +	switch (usb_ctrl->bRequestType & USB_RECIP_MASK) { +	case USB_RECIP_DEVICE: +		switch (usb_ctrl->wValue) { +		case USB_DEVICE_REMOTE_WAKEUP: +			DEBUG_SETUP("\tOFF:USB_DEVICE_REMOTE_WAKEUP\n"); +			break; + +		case USB_DEVICE_TEST_MODE: +			DEBUG_SETUP("\tCLEAR_FEATURE: USB_DEVICE_TEST_MODE\n"); +			/** @todo Add CLEAR_FEATURE for TEST modes. */ +			break; +		} + +		s3c_udc_ep0_zlp(dev); +		break; + +	case USB_RECIP_ENDPOINT: +		DEBUG_SETUP("\tCLEAR_FEATURE:USB_RECIP_ENDPOINT, wValue = %d\n", +				usb_ctrl->wValue); + +		if (usb_ctrl->wValue == USB_ENDPOINT_HALT) { +			if (ep_num == 0) { +				s3c_udc_ep0_set_stall(ep); +				return 0; +			} + +			s3c_udc_ep0_zlp(dev); + +			s3c_udc_ep_clear_stall(ep); +			s3c_udc_ep_activate(ep); +			ep->stopped = 0; + +			clear_feature_num = ep_num; +			clear_feature_flag = 1; +		} +		break; +	} + +	return 0; +} + +static int s3c_udc_set_feature(struct usb_ep *_ep) +{ +	struct s3c_udc	*dev; +	struct s3c_ep	*ep; +	u8		ep_num; + +	ep = container_of(_ep, struct s3c_ep, ep); +	ep_num = ep_index(ep); +	dev = ep->dev; + +	DEBUG_SETUP("%s: *** USB_REQ_SET_FEATURE , ep_num = %d\n", +		    __func__, ep_num); + +	if (usb_ctrl->wLength != 0) { +		DEBUG_SETUP("\tSET_FEATURE: wLength is not zero.....\n"); +		return 1; +	} + +	switch (usb_ctrl->bRequestType & USB_RECIP_MASK) { +	case USB_RECIP_DEVICE: +		switch (usb_ctrl->wValue) { +		case USB_DEVICE_REMOTE_WAKEUP: +			DEBUG_SETUP("\tSET_FEATURE:USB_DEVICE_REMOTE_WAKEUP\n"); +			break; +		case USB_DEVICE_B_HNP_ENABLE: +			DEBUG_SETUP("\tSET_FEATURE: USB_DEVICE_B_HNP_ENABLE\n"); +			break; + +		case USB_DEVICE_A_HNP_SUPPORT: +			/* RH port supports HNP */ +			DEBUG_SETUP("\tSET_FEATURE:USB_DEVICE_A_HNP_SUPPORT\n"); +			break; + +		case USB_DEVICE_A_ALT_HNP_SUPPORT: +			/* other RH port does */ +			DEBUG_SETUP("\tSET: USB_DEVICE_A_ALT_HNP_SUPPORT\n"); +			break; +		} + +		s3c_udc_ep0_zlp(dev); +		return 0; + +	case USB_RECIP_INTERFACE: +		DEBUG_SETUP("\tSET_FEATURE: USB_RECIP_INTERFACE\n"); +		break; + +	case USB_RECIP_ENDPOINT: +		DEBUG_SETUP("\tSET_FEATURE: USB_RECIP_ENDPOINT\n"); +		if (usb_ctrl->wValue == USB_ENDPOINT_HALT) { +			if (ep_num == 0) { +				s3c_udc_ep0_set_stall(ep); +				return 0; +			} +			ep->stopped = 1; +			s3c_udc_ep_set_stall(ep); +		} + +		s3c_udc_ep0_zlp(dev); +		return 0; +	} + +	return 1; +} + +/* + * WAIT_FOR_SETUP (OUT_PKT_RDY) + */ +void s3c_ep0_setup(struct s3c_udc *dev) +{ +	struct s3c_ep *ep = &dev->ep[0]; +	int i, bytes, is_in; +	u8 ep_num; + +	/* Nuke all previous transfers */ +	nuke(ep, -EPROTO); + +	/* read control req from fifo (8 bytes) */ +	bytes = s3c_fifo_read(ep, (u32 *)usb_ctrl, 8); + +	DEBUG_SETUP("%s: bRequestType = 0x%x(%s), bRequest = 0x%x" +		    "\twLength = 0x%x, wValue = 0x%x, wIndex= 0x%x\n", +		    __func__, usb_ctrl->bRequestType, +		    (usb_ctrl->bRequestType & USB_DIR_IN) ? "IN" : "OUT", +		    usb_ctrl->bRequest, +		    usb_ctrl->wLength, usb_ctrl->wValue, usb_ctrl->wIndex); + +#ifdef DEBUG_S3C_UDC +	{ +		int i, len = sizeof(*usb_ctrl); +		char *p = usb_ctrl; + +		printf("pkt = "); +		for (i = 0; i < len; i++) { +			printf("%02x", ((u8 *)p)[i]); +			if ((i & 7) == 7) +				printf(" "); +		} +		printf("\n"); +	} +#endif + +	if (usb_ctrl->bRequest == GET_MAX_LUN_REQUEST && +	    usb_ctrl->wLength != 1) { +		DEBUG_SETUP("\t%s:GET_MAX_LUN_REQUEST:invalid", +			      __func__); +		DEBUG_SETUP("wLength = %d, setup returned\n", +			    usb_ctrl->wLength); + +		s3c_udc_ep0_set_stall(ep); +		dev->ep0state = WAIT_FOR_SETUP; + +		return; +	} else if (usb_ctrl->bRequest == BOT_RESET_REQUEST && +		 usb_ctrl->wLength != 0) { +		/* Bulk-Only *mass storge reset of class-specific request */ +		DEBUG_SETUP("%s:BOT Rest:invalid wLength =%d, setup returned\n", +			    __func__, usb_ctrl->wLength); + +		s3c_udc_ep0_set_stall(ep); +		dev->ep0state = WAIT_FOR_SETUP; + +		return; +	} + +	/* Set direction of EP0 */ +	if (likely(usb_ctrl->bRequestType & USB_DIR_IN)) { +		ep->bEndpointAddress |= USB_DIR_IN; +		is_in = 1; + +	} else { +		ep->bEndpointAddress &= ~USB_DIR_IN; +		is_in = 0; +	} +	/* cope with automagic for some standard requests. */ +	dev->req_std = (usb_ctrl->bRequestType & USB_TYPE_MASK) +		== USB_TYPE_STANDARD; +	dev->req_config = 0; +	dev->req_pending = 1; + +	/* Handle some SETUP packets ourselves */ +	if (dev->req_std) { +		switch (usb_ctrl->bRequest) { +		case USB_REQ_SET_ADDRESS: +		DEBUG_SETUP("%s: *** USB_REQ_SET_ADDRESS (%d)\n", +				__func__, usb_ctrl->wValue); +			if (usb_ctrl->bRequestType +				!= (USB_TYPE_STANDARD | USB_RECIP_DEVICE)) +				break; + +			udc_set_address(dev, usb_ctrl->wValue); +			return; + +		case USB_REQ_SET_CONFIGURATION: +			DEBUG_SETUP("=====================================\n"); +			DEBUG_SETUP("%s: USB_REQ_SET_CONFIGURATION (%d)\n", +					__func__, usb_ctrl->wValue); + +			if (usb_ctrl->bRequestType == USB_RECIP_DEVICE) { +				reset_available = 1; +				dev->req_config = 1; +			} +			break; + +		case USB_REQ_GET_DESCRIPTOR: +			DEBUG_SETUP("%s: *** USB_REQ_GET_DESCRIPTOR\n", +				    __func__); +			break; + +		case USB_REQ_SET_INTERFACE: +			DEBUG_SETUP("%s: *** USB_REQ_SET_INTERFACE (%d)\n", +					__func__, usb_ctrl->wValue); + +			if (usb_ctrl->bRequestType == USB_RECIP_INTERFACE) { +				reset_available = 1; +				dev->req_config = 1; +			} +			break; + +		case USB_REQ_GET_CONFIGURATION: +			DEBUG_SETUP("%s: *** USB_REQ_GET_CONFIGURATION\n", +				    __func__); +			break; + +		case USB_REQ_GET_STATUS: +			if (!s3c_udc_get_status(dev, usb_ctrl)) +				return; + +			break; + +		case USB_REQ_CLEAR_FEATURE: +			ep_num = usb_ctrl->wIndex & 0x7f; + +			if (!s3c_udc_clear_feature(&dev->ep[ep_num].ep)) +				return; + +			break; + +		case USB_REQ_SET_FEATURE: +			ep_num = usb_ctrl->wIndex & 0x7f; + +			if (!s3c_udc_set_feature(&dev->ep[ep_num].ep)) +				return; + +			break; + +		default: +			DEBUG_SETUP("%s: *** Default of usb_ctrl->bRequest=0x%x" +				"happened.\n", __func__, usb_ctrl->bRequest); +			break; +		} +	} + + +	if (likely(dev->driver)) { +		/* device-2-host (IN) or no data setup command, +		 * process immediately */ +		DEBUG_SETUP("%s:usb_ctrlreq will be passed to fsg_setup()\n", +			    __func__); + +		spin_unlock(&dev->lock); +		i = dev->driver->setup(&dev->gadget, usb_ctrl); +		spin_lock(&dev->lock); + +		if (i < 0) { +			if (dev->req_config) { +				DEBUG_SETUP("\tconfig change 0x%02x fail %d?\n", +					(u32)usb_ctrl->bRequest, i); +				return; +			} + +			/* setup processing failed, force stall */ +			s3c_udc_ep0_set_stall(ep); +			dev->ep0state = WAIT_FOR_SETUP; + +			DEBUG_SETUP("\tdev->driver->setup failed (%d)," +				    " bRequest = %d\n", +				i, usb_ctrl->bRequest); + + +		} else if (dev->req_pending) { +			dev->req_pending = 0; +			DEBUG_SETUP("\tdev->req_pending...\n"); +		} + +		DEBUG_SETUP("\tep0state = %s\n", state_names[dev->ep0state]); + +	} +} + +/* + * handle ep0 interrupt + */ +static void s3c_handle_ep0(struct s3c_udc *dev) +{ +	if (dev->ep0state == WAIT_FOR_SETUP) { +		DEBUG_OUT_EP("%s: WAIT_FOR_SETUP\n", __func__); +		s3c_ep0_setup(dev); + +	} else { +		DEBUG_OUT_EP("%s: strange state!!(state = %s)\n", +			__func__, state_names[dev->ep0state]); +	} +} + +static void s3c_ep0_kick(struct s3c_udc *dev, struct s3c_ep *ep) +{ +	DEBUG_EP0("%s: ep_is_in = %d\n", __func__, ep_is_in(ep)); +	if (ep_is_in(ep)) { +		dev->ep0state = DATA_STATE_XMIT; +		s3c_ep0_write(dev); + +	} else { +		dev->ep0state = DATA_STATE_RECV; +		s3c_ep0_read(dev); +	} +} diff --git a/drivers/usb/host/Makefile b/drivers/usb/host/Makefile index 09abb754d..77e217f34 100644 --- a/drivers/usb/host/Makefile +++ b/drivers/usb/host/Makefile @@ -42,6 +42,7 @@ COBJS-$(CONFIG_USB_EHCI_FSL) += ehci-fsl.o  endif  COBJS-$(CONFIG_USB_EHCI_MXC) += ehci-mxc.o  COBJS-$(CONFIG_USB_EHCI_MXS) += ehci-mxs.o +COBJS-$(CONFIG_USB_EHCI_MX5) += ehci-mx5.o  COBJS-$(CONFIG_USB_EHCI_PPC4XX) += ehci-ppc4xx.o  COBJS-$(CONFIG_USB_EHCI_IXP4XX) += ehci-ixp.o  COBJS-$(CONFIG_USB_EHCI_KIRKWOOD) += ehci-kirkwood.o diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c index 2197119cf..3f7bc2cef 100644 --- a/drivers/usb/host/ehci-hcd.c +++ b/drivers/usb/host/ehci-hcd.c @@ -26,6 +26,10 @@  #include <asm/io.h>  #include <malloc.h>  #include <watchdog.h> +#ifdef CONFIG_USB_KEYBOARD +#include <stdio_dev.h> +extern unsigned char new[]; +#endif  #include "ehci.h" @@ -48,7 +52,7 @@ static struct descriptor {  		0x29,		/* bDescriptorType: hub descriptor */  		2,		/* bNrPorts -- runtime modified */  		0,		/* wHubCharacteristics */ -		0xff,		/* bPwrOn2PwrGood */ +		10,		/* bPwrOn2PwrGood */  		0,		/* bHubCntrCurrent */  		{},		/* Device removable */  		{}		/* at most 7 ports! XXX */ @@ -201,6 +205,14 @@ static inline void ehci_invalidate_dcache(struct QH *qh)  }  #endif /* CONFIG_EHCI_DCACHE */ +void __ehci_powerup_fixup(uint32_t *status_reg, uint32_t *reg) +{ +	mdelay(50); +} + +void ehci_powerup_fixup(uint32_t *status_reg, uint32_t *reg) +	__attribute__((weak, alias("__ehci_powerup_fixup"))); +  static int handshake(uint32_t *ptr, uint32_t mask, uint32_t done, int usec)  {  	uint32_t result; @@ -709,8 +721,8 @@ ehci_submit_root(struct usb_device *dev, unsigned long pipe, void *buffer,  				 * usb 2.0 specification say 50 ms resets on  				 * root  				 */ -				wait_ms(50); -				/* terminate the reset */ +				ehci_powerup_fixup(status_reg, ®); +  				ehci_writel(status_reg, reg & ~EHCI_PS_PR);  				/*  				 * A host controller must terminate the reset @@ -895,5 +907,32 @@ submit_int_msg(struct usb_device *dev, unsigned long pipe, void *buffer,  	debug("dev=%p, pipe=%lu, buffer=%p, length=%d, interval=%d",  	      dev, pipe, buffer, length, interval); -	return -1; +	return ehci_submit_async(dev, pipe, buffer, length, NULL); +} + +#ifdef CONFIG_SYS_USB_EVENT_POLL +/* + * This function polls for USB keyboard data. + */ +void usb_event_poll() +{ +	struct stdio_dev *dev; +	struct usb_device *usb_kbd_dev; +	struct usb_interface *iface; +	struct usb_endpoint_descriptor *ep; +	int pipe; +	int maxp; + +	/* Get the pointer to USB Keyboard device pointer */ +	dev = stdio_get_by_name("usbkbd"); +	usb_kbd_dev = (struct usb_device *)dev->priv; +	iface = &usb_kbd_dev->config.if_desc[0]; +	ep = &iface->ep_desc[0]; +	pipe = usb_rcvintpipe(usb_kbd_dev, ep->bEndpointAddress); + +	/* Submit a interrupt transfer request */ +	maxp = usb_maxpacket(usb_kbd_dev, pipe); +	usb_submit_int_msg(usb_kbd_dev, pipe, &new[0], +			maxp > 8 ? 8 : maxp, ep->bInterval);  } +#endif /* CONFIG_SYS_USB_EVENT_POLL */ diff --git a/drivers/usb/host/ehci-mx5.c b/drivers/usb/host/ehci-mx5.c new file mode 100644 index 000000000..68a673e72 --- /dev/null +++ b/drivers/usb/host/ehci-mx5.c @@ -0,0 +1,255 @@ +/* + * Copyright (c) 2009 Daniel Mack <daniel@caiaq.de> + * Copyright (C) 2010 Freescale Semiconductor, Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#include <common.h> +#include <usb.h> +#include <errno.h> +#include <linux/compiler.h> +#include <usb/ehci-fsl.h> +#include <asm/io.h> +#include <asm/arch/imx-regs.h> +#include <asm/arch/clock.h> +#include <asm/arch/mx5x_pins.h> +#include <asm/arch/iomux.h> + +#include "ehci.h" +#include "ehci-core.h" + +#define MX5_USBOTHER_REGS_OFFSET 0x800 + + +#define MXC_OTG_OFFSET		0 +#define MXC_H1_OFFSET		0x200 +#define MXC_H2_OFFSET		0x400 + +#define MXC_USBCTRL_OFFSET		0 +#define MXC_USB_PHY_CTR_FUNC_OFFSET	0x8 +#define MXC_USB_PHY_CTR_FUNC2_OFFSET	0xc +#define MXC_USB_CTRL_1_OFFSET		0x10 +#define MXC_USBH2CTRL_OFFSET		0x14 + +/* USB_CTRL */ +#define MXC_OTG_UCTRL_OWIE_BIT	(1 << 27) /* OTG wakeup intr enable */ +#define MXC_OTG_UCTRL_OPM_BIT	(1 << 24) /* OTG power mask */ +#define MXC_H1_UCTRL_H1UIE_BIT	(1 << 12) /* Host1 ULPI interrupt enable */ +#define MXC_H1_UCTRL_H1WIE_BIT	(1 << 11) /* HOST1 wakeup intr enable */ +#define MXC_H1_UCTRL_H1PM_BIT	(1 << 8) /* HOST1 power mask */ + +/* USB_PHY_CTRL_FUNC */ +#define MXC_OTG_PHYCTRL_OC_DIS_BIT (1 << 8) /* OTG Disable Overcurrent Event */ +#define MXC_H1_OC_DIS_BIT	(1 << 5) /* UH1 Disable Overcurrent Event */ + +/* USBH2CTRL */ +#define MXC_H2_UCTRL_H2UIE_BIT	(1 << 8) +#define MXC_H2_UCTRL_H2WIE_BIT	(1 << 7) +#define MXC_H2_UCTRL_H2PM_BIT	(1 << 4) + +/* USB_CTRL_1 */ +#define MXC_USB_CTRL_UH1_EXT_CLK_EN		(1 << 25) + +/* USB pin configuration */ +#define USB_PAD_CONFIG	(PAD_CTL_PKE_ENABLE | PAD_CTL_SRE_FAST | \ +			PAD_CTL_DRV_HIGH | PAD_CTL_100K_PU | \ +			PAD_CTL_HYS_ENABLE | PAD_CTL_PUE_PULL) + +#ifdef CONFIG_MX51 +/* + * Configure the MX51 USB H1 IOMUX + */ +void setup_iomux_usb_h1(void) +{ +	mxc_request_iomux(MX51_PIN_USBH1_STP, IOMUX_CONFIG_ALT0); +	mxc_iomux_set_pad(MX51_PIN_USBH1_STP, USB_PAD_CONFIG); +	mxc_request_iomux(MX51_PIN_USBH1_CLK, IOMUX_CONFIG_ALT0); +	mxc_iomux_set_pad(MX51_PIN_USBH1_CLK, USB_PAD_CONFIG); +	mxc_request_iomux(MX51_PIN_USBH1_DIR, IOMUX_CONFIG_ALT0); +	mxc_iomux_set_pad(MX51_PIN_USBH1_DIR, USB_PAD_CONFIG); +	mxc_request_iomux(MX51_PIN_USBH1_NXT, IOMUX_CONFIG_ALT0); +	mxc_iomux_set_pad(MX51_PIN_USBH1_NXT, USB_PAD_CONFIG); + +	mxc_request_iomux(MX51_PIN_USBH1_DATA0, IOMUX_CONFIG_ALT0); +	mxc_iomux_set_pad(MX51_PIN_USBH1_DATA0, USB_PAD_CONFIG); +	mxc_request_iomux(MX51_PIN_USBH1_DATA1, IOMUX_CONFIG_ALT0); +	mxc_iomux_set_pad(MX51_PIN_USBH1_DATA1, USB_PAD_CONFIG); +	mxc_request_iomux(MX51_PIN_USBH1_DATA2, IOMUX_CONFIG_ALT0); +	mxc_iomux_set_pad(MX51_PIN_USBH1_DATA2, USB_PAD_CONFIG); +	mxc_request_iomux(MX51_PIN_USBH1_DATA3, IOMUX_CONFIG_ALT0); +	mxc_iomux_set_pad(MX51_PIN_USBH1_DATA3, USB_PAD_CONFIG); +	mxc_request_iomux(MX51_PIN_USBH1_DATA4, IOMUX_CONFIG_ALT0); +	mxc_iomux_set_pad(MX51_PIN_USBH1_DATA4, USB_PAD_CONFIG); +	mxc_request_iomux(MX51_PIN_USBH1_DATA5, IOMUX_CONFIG_ALT0); +	mxc_iomux_set_pad(MX51_PIN_USBH1_DATA5, USB_PAD_CONFIG); +	mxc_request_iomux(MX51_PIN_USBH1_DATA6, IOMUX_CONFIG_ALT0); +	mxc_iomux_set_pad(MX51_PIN_USBH1_DATA6, USB_PAD_CONFIG); +	mxc_request_iomux(MX51_PIN_USBH1_DATA7, IOMUX_CONFIG_ALT0); +	mxc_iomux_set_pad(MX51_PIN_USBH1_DATA7, USB_PAD_CONFIG); +} + +/* + * Configure the MX51 USB H2 IOMUX + */ +void setup_iomux_usb_h2(void) +{ +	mxc_request_iomux(MX51_PIN_EIM_A24, IOMUX_CONFIG_ALT2); +	mxc_iomux_set_pad(MX51_PIN_EIM_A24, USB_PAD_CONFIG); +	mxc_request_iomux(MX51_PIN_EIM_A25, IOMUX_CONFIG_ALT2); +	mxc_iomux_set_pad(MX51_PIN_EIM_A25, USB_PAD_CONFIG); +	mxc_request_iomux(MX51_PIN_EIM_A26, IOMUX_CONFIG_ALT2); +	mxc_iomux_set_pad(MX51_PIN_EIM_A26, USB_PAD_CONFIG); +	mxc_request_iomux(MX51_PIN_EIM_A27, IOMUX_CONFIG_ALT2); +	mxc_iomux_set_pad(MX51_PIN_EIM_A27, USB_PAD_CONFIG); + +	mxc_request_iomux(MX51_PIN_EIM_D16, IOMUX_CONFIG_ALT2); +	mxc_iomux_set_pad(MX51_PIN_EIM_D16, USB_PAD_CONFIG); +	mxc_request_iomux(MX51_PIN_EIM_D17, IOMUX_CONFIG_ALT2); +	mxc_iomux_set_pad(MX51_PIN_EIM_D17, USB_PAD_CONFIG); +	mxc_request_iomux(MX51_PIN_EIM_D18, IOMUX_CONFIG_ALT2); +	mxc_iomux_set_pad(MX51_PIN_EIM_D18, USB_PAD_CONFIG); +	mxc_request_iomux(MX51_PIN_EIM_D19, IOMUX_CONFIG_ALT2); +	mxc_iomux_set_pad(MX51_PIN_EIM_D19, USB_PAD_CONFIG); +	mxc_request_iomux(MX51_PIN_EIM_D20, IOMUX_CONFIG_ALT2); +	mxc_iomux_set_pad(MX51_PIN_EIM_D20, USB_PAD_CONFIG); +	mxc_request_iomux(MX51_PIN_EIM_D21, IOMUX_CONFIG_ALT2); +	mxc_iomux_set_pad(MX51_PIN_EIM_D21, USB_PAD_CONFIG); +	mxc_request_iomux(MX51_PIN_EIM_D22, IOMUX_CONFIG_ALT2); +	mxc_iomux_set_pad(MX51_PIN_EIM_D22, USB_PAD_CONFIG); +	mxc_request_iomux(MX51_PIN_EIM_D23, IOMUX_CONFIG_ALT2); +	mxc_iomux_set_pad(MX51_PIN_EIM_D23, USB_PAD_CONFIG); +} +#endif + +int mxc_set_usbcontrol(int port, unsigned int flags) +{ +	unsigned int v; +	void __iomem *usb_base = (void __iomem *)OTG_BASE_ADDR; +	void __iomem *usbother_base; +	int ret = 0; + +	usbother_base = usb_base + MX5_USBOTHER_REGS_OFFSET; + +	switch (port) { +	case 0:	/* OTG port */ +		if (flags & MXC_EHCI_INTERNAL_PHY) { +			v = __raw_readl(usbother_base + +					MXC_USB_PHY_CTR_FUNC_OFFSET); +			if (flags & MXC_EHCI_POWER_PINS_ENABLED) +				/* OC/USBPWR is not used */ +				v |= MXC_OTG_PHYCTRL_OC_DIS_BIT; +			else +				/* OC/USBPWR is used */ +				v &= ~MXC_OTG_PHYCTRL_OC_DIS_BIT; +			__raw_writel(v, usbother_base + +					MXC_USB_PHY_CTR_FUNC_OFFSET); + +			v = __raw_readl(usbother_base + MXC_USBCTRL_OFFSET); +			if (flags & MXC_EHCI_POWER_PINS_ENABLED) +				v |= MXC_OTG_UCTRL_OPM_BIT; +			else +				v &= ~MXC_OTG_UCTRL_OPM_BIT; +			__raw_writel(v, usbother_base + MXC_USBCTRL_OFFSET); +		} +		break; +	case 1:	/* Host 1 Host ULPI */ +#ifdef CONFIG_MX51 +		/* The clock for the USBH1 ULPI port will come externally +		   from the PHY. */ +		v = __raw_readl(usbother_base + MXC_USB_CTRL_1_OFFSET); +		__raw_writel(v | MXC_USB_CTRL_UH1_EXT_CLK_EN, usbother_base + +				MXC_USB_CTRL_1_OFFSET); +#endif + +		v = __raw_readl(usbother_base + MXC_USBCTRL_OFFSET); +		if (flags & MXC_EHCI_POWER_PINS_ENABLED) +			v &= ~MXC_H1_UCTRL_H1PM_BIT; /* HOST1 power mask used */ +		else +			v |= MXC_H1_UCTRL_H1PM_BIT; /* HOST1 power mask used */ +		__raw_writel(v, usbother_base + MXC_USBCTRL_OFFSET); + +		v = __raw_readl(usbother_base + MXC_USB_PHY_CTR_FUNC_OFFSET); +		if (flags & MXC_EHCI_POWER_PINS_ENABLED) +			v &= ~MXC_H1_OC_DIS_BIT; /* OC is used */ +		else +			v |= MXC_H1_OC_DIS_BIT; /* OC is not used */ +		__raw_writel(v, usbother_base + MXC_USB_PHY_CTR_FUNC_OFFSET); + +		break; +	case 2: /* Host 2 ULPI */ +		v = __raw_readl(usbother_base + MXC_USBH2CTRL_OFFSET); +		if (flags & MXC_EHCI_POWER_PINS_ENABLED) +			v &= ~MXC_H2_UCTRL_H2PM_BIT; /* HOST2 power mask used */ +		else +			v |= MXC_H2_UCTRL_H2PM_BIT; /* HOST2 power mask used */ + +		__raw_writel(v, usbother_base + MXC_USBH2CTRL_OFFSET); +		break; +	} + +	return ret; +} + +void __board_ehci_hcd_postinit(struct usb_ehci *ehci, int port) +{ +} + +void board_ehci_hcd_postinit(struct usb_ehci *ehci, int port) +	__attribute((weak, alias("__board_ehci_hcd_postinit"))); + +int ehci_hcd_init(void) +{ +	struct usb_ehci *ehci; +#ifdef CONFIG_MX53 +	struct clkctl *sc_regs = (struct clkctl *)CCM_BASE_ADDR; +	u32 reg; + +	reg = __raw_readl(&sc_regs->cscmr1) & ~(1 << 26); +	/* derive USB PHY clock multiplexer from PLL3 */ +	reg |= 1 << 26; +	__raw_writel(reg, &sc_regs->cscmr1); +#endif + +	set_usboh3_clk(); +	enable_usboh3_clk(1); +	set_usb_phy2_clk(); +	enable_usb_phy2_clk(1); +	mdelay(1); + +	/* Do board specific initialization */ +	board_ehci_hcd_init(CONFIG_MXC_USB_PORT); + +	ehci = (struct usb_ehci *)(OTG_BASE_ADDR + +		(0x200 * CONFIG_MXC_USB_PORT)); +	hccr = (struct ehci_hccr *)((uint32_t)&ehci->caplength); +	hcor = (struct ehci_hcor *)((uint32_t)hccr + +			HC_LENGTH(ehci_readl(&hccr->cr_capbase))); +	setbits_le32(&ehci->usbmode, CM_HOST); + +	__raw_writel(CONFIG_MXC_USB_PORTSC, &ehci->portsc); +	setbits_le32(&ehci->portsc, USB_EN); + +	mxc_set_usbcontrol(CONFIG_MXC_USB_PORT, CONFIG_MXC_USB_FLAGS); +	mdelay(10); + +	/* Do board specific post-initialization */ +	board_ehci_hcd_postinit(ehci, CONFIG_MXC_USB_PORT); + +	return 0; +} + +int ehci_hcd_stop(void) +{ +	return 0; +} + + diff --git a/drivers/usb/host/ohci-hcd.c b/drivers/usb/host/ohci-hcd.c index 60c959526..cf906b47c 100644 --- a/drivers/usb/host/ohci-hcd.c +++ b/drivers/usb/host/ohci-hcd.c @@ -1262,12 +1262,19 @@ static int ohci_submit_rh_msg(struct usb_device *dev, unsigned long pipe,  	int len = 0;  	int stat = 0;  	__u32 datab[4]; -	__u8 *data_buf = (__u8 *)datab; +	union { +		void *ptr; +		__u8 *u8; +		__u16 *u16; +		__u32 *u32; +	} databuf;  	__u16 bmRType_bReq;  	__u16 wValue;  	__u16 wIndex;  	__u16 wLength; +	databuf.u32 = (__u32 *)datab; +  #ifdef DEBUG  pkt_print(NULL, dev, pipe, buffer, transfer_len,  	  cmd, "SUB(rh)", usb_pipein(pipe)); @@ -1297,20 +1304,20 @@ pkt_print(NULL, dev, pipe, buffer, transfer_len,  	*/  	case RH_GET_STATUS: -		*(__u16 *) data_buf = cpu_to_le16(1); +		databuf.u16[0] = cpu_to_le16(1);  		OK(2);  	case RH_GET_STATUS | RH_INTERFACE: -		*(__u16 *) data_buf = cpu_to_le16(0); +		databuf.u16[0] = cpu_to_le16(0);  		OK(2);  	case RH_GET_STATUS | RH_ENDPOINT: -		*(__u16 *) data_buf = cpu_to_le16(0); +		databuf.u16[0] = cpu_to_le16(0);  		OK(2);  	case RH_GET_STATUS | RH_CLASS: -		*(__u32 *) data_buf = cpu_to_le32( +		databuf.u32[0] = cpu_to_le32(  				RD_RH_STAT & ~(RH_HS_CRWE | RH_HS_DRWE));  		OK(4);  	case RH_GET_STATUS | RH_OTHER | RH_CLASS: -		*(__u32 *) data_buf = cpu_to_le32(RD_RH_PORTSTAT); +		databuf.u32[0] = cpu_to_le32(RD_RH_PORTSTAT);  		OK(4);  	case RH_CLEAR_FEATURE | RH_ENDPOINT: @@ -1374,14 +1381,14 @@ pkt_print(NULL, dev, pipe, buffer, transfer_len,  					min_t(unsigned int,  					sizeof(root_hub_dev_des),  					wLength)); -			data_buf = root_hub_dev_des; OK(len); +			databuf.ptr = root_hub_dev_des; OK(len);  		case (0x02): /* configuration descriptor */  			len = min_t(unsigned int,  					leni,  					min_t(unsigned int,  					sizeof(root_hub_config_des),  					wLength)); -			data_buf = root_hub_config_des; OK(len); +			databuf.ptr = root_hub_config_des; OK(len);  		case (0x03): /* string descriptors */  			if (wValue == 0x0300) {  				len = min_t(unsigned int, @@ -1389,7 +1396,7 @@ pkt_print(NULL, dev, pipe, buffer, transfer_len,  						min_t(unsigned int,  						sizeof(root_hub_str_index0),  						wLength)); -				data_buf = root_hub_str_index0; +				databuf.ptr = root_hub_str_index0;  				OK(len);  			}  			if (wValue == 0x0301) { @@ -1398,7 +1405,7 @@ pkt_print(NULL, dev, pipe, buffer, transfer_len,  						min_t(unsigned int,  						sizeof(root_hub_str_index1),  						wLength)); -				data_buf = root_hub_str_index1; +				databuf.ptr = root_hub_str_index1;  				OK(len);  		}  		default: @@ -1410,41 +1417,45 @@ pkt_print(NULL, dev, pipe, buffer, transfer_len,  	{  		__u32 temp = roothub_a(&gohci); -		data_buf [0] = 9;		/* min length; */ -		data_buf [1] = 0x29; -		data_buf [2] = temp & RH_A_NDP; +		databuf.u8[0] = 9;		/* min length; */ +		databuf.u8[1] = 0x29; +		databuf.u8[2] = temp & RH_A_NDP;  #ifdef CONFIG_AT91C_PQFP_UHPBUG -		data_buf [2] = (data_buf [2] == 2) ? 1:0; +		databuf.u8[2] = (databuf.u8[2] == 2) ? 1 : 0;  #endif -		data_buf [3] = 0; +		databuf.u8[3] = 0;  		if (temp & RH_A_PSM)	/* per-port power switching? */ -			data_buf [3] |= 0x1; +			databuf.u8[3] |= 0x1;  		if (temp & RH_A_NOCP)	/* no overcurrent reporting? */ -			data_buf [3] |= 0x10; +			databuf.u8[3] |= 0x10;  		else if (temp & RH_A_OCPM)/* per-port overcurrent reporting? */ -			data_buf [3] |= 0x8; +			databuf.u8[3] |= 0x8; -		/* corresponds to data_buf[4-7] */ -		datab [1] = 0; -		data_buf [5] = (temp & RH_A_POTPGT) >> 24; +		/* corresponds to databuf.u8[4-7] */ +		databuf.u8[1] = 0; +		databuf.u8[5] = (temp & RH_A_POTPGT) >> 24;  		temp = roothub_b(&gohci); -		data_buf [7] = temp & RH_B_DR; -		if (data_buf [2] < 7) { -			data_buf [8] = 0xff; +		databuf.u8[7] = temp & RH_B_DR; +		if (databuf.u8[2] < 7) { +			databuf.u8[8] = 0xff;  		} else { -			data_buf [0] += 2; -			data_buf [8] = (temp & RH_B_DR) >> 8; -			data_buf [10] = data_buf [9] = 0xff; +			databuf.u8[0] += 2; +			databuf.u8[8] = (temp & RH_B_DR) >> 8; +			databuf.u8[10] = databuf.u8[9] = 0xff;  		}  		len = min_t(unsigned int, leni, -			    min_t(unsigned int, data_buf [0], wLength)); +			    min_t(unsigned int, databuf.u8[0], wLength));  		OK(len);  	} -	case RH_GET_CONFIGURATION:	*(__u8 *) data_buf = 0x01; OK(1); +	case RH_GET_CONFIGURATION: +		databuf.u8[0] = 0x01; +		OK(1); -	case RH_SET_CONFIGURATION:	WR_RH_STAT(0x10000); OK(0); +	case RH_SET_CONFIGURATION: +		WR_RH_STAT(0x10000); +		OK(0);  	default:  		dbg("unsupported root hub command"); @@ -1458,8 +1469,8 @@ pkt_print(NULL, dev, pipe, buffer, transfer_len,  #endif  	len = min_t(int, len, leni); -	if (data != data_buf) -	    memcpy(data, data_buf, len); +	if (data != databuf.ptr) +		memcpy(data, databuf.ptr, len);  	dev->act_len = len;  	dev->status = stat; diff --git a/drivers/usb/ulpi/Makefile b/drivers/usb/ulpi/Makefile new file mode 100644 index 000000000..d43b2293e --- /dev/null +++ b/drivers/usb/ulpi/Makefile @@ -0,0 +1,44 @@ +# +# Copyright (C) 2011 Jana Rapava <fermata7@gmail.com> +# See file CREDITS for list of people who contributed to this +# project. +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License as +# published by the Free Software Foundation; either version 2 of +# the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc. +# + +include $(TOPDIR)/config.mk + +LIB	:= $(obj)libusb_ulpi.o + +COBJS-$(CONFIG_USB_ULPI)		+= ulpi.o +COBJS-$(CONFIG_USB_ULPI_VIEWPORT)	+= ulpi-viewport.o + +COBJS	:= $(COBJS-y) +SRCS	:= $(COBJS:.o=.c) +OBJS	:= $(addprefix $(obj),$(COBJS)) + +all:	$(LIB) + +$(LIB):	$(obj).depend $(OBJS) +	$(call cmd_link_o_target, $(OBJS)) + +######################################################################### + +# defines $(obj).depend target +include $(SRCTREE)/rules.mk + +sinclude $(obj).depend + +######################################################################### diff --git a/drivers/usb/ulpi/ulpi-viewport.c b/drivers/usb/ulpi/ulpi-viewport.c new file mode 100644 index 000000000..fa2e004a6 --- /dev/null +++ b/drivers/usb/ulpi/ulpi-viewport.c @@ -0,0 +1,118 @@ +/* + * Copyright (C) 2011 Jana Rapava <fermata7@gmail.com> + * Copyright (C) 2011 CompuLab, Ltd. <www.compulab.co.il> + * + * Authors: Jana Rapava <fermata7@gmail.com> + *	    Igor Grinberg <grinberg@compulab.co.il> + * + * Based on: + * linux/drivers/usb/otg/ulpi_viewport.c + * + * Original Copyright follow: + * Copyright (C) 2011 Google, Inc. + * + * This software is licensed under the terms of the GNU General Public + * License version 2, as published by the Free Software Foundation, and + * may be copied, distributed, and modified under those terms. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + */ + +#include <common.h> +#include <asm/io.h> +#include <usb/ulpi.h> + +/* ULPI viewport control bits */ +#define ULPI_SS		(1 << 27) +#define ULPI_RWCTRL	(1 << 29) +#define ULPI_RWRUN	(1 << 30) +#define ULPI_WU		(1 << 31) + +/* + * Wait for the ULPI request to complete + * + * @ulpi_viewport	- the address of the viewport + * @mask		- expected value to wait for + * + * returns 0 on mask match, ULPI_ERROR on time out. + */ +static int ulpi_wait(u32 ulpi_viewport, u32 mask) +{ +	int timeout = CONFIG_USB_ULPI_TIMEOUT; + +	/* Wait for the bits in mask to become zero. */ +	while (--timeout) { +		if ((readl(ulpi_viewport) & mask) == 0) +			return 0; + +		udelay(1); +	} + +	return ULPI_ERROR; +} + +/* + * Wake the ULPI PHY up for communication + * + * returns 0 on success. + */ +static int ulpi_wakeup(u32 ulpi_viewport) +{ +	int err; + +	if (readl(ulpi_viewport) & ULPI_SS) +		return 0; /* already awake */ + +	writel(ULPI_WU, ulpi_viewport); + +	err = ulpi_wait(ulpi_viewport, ULPI_WU); +	if (err) +		printf("ULPI wakeup timed out\n"); + +	return err; +} + +/* + * Issue a ULPI read/write request + * + * @value - the ULPI request + */ +static int ulpi_request(u32 ulpi_viewport, u32 value) +{ +	int err; + +	err = ulpi_wakeup(ulpi_viewport); +	if (err) +		return err; + +	writel(value, ulpi_viewport); + +	err = ulpi_wait(ulpi_viewport, ULPI_RWRUN); +	if (err) +		printf("ULPI request timed out\n"); + +	return err; +} + +u32 ulpi_write(u32 ulpi_viewport, u8 *reg, u32 value) +{ +	u32 val = ULPI_RWRUN | ULPI_RWCTRL | ((u32)reg << 16) | (value & 0xff); + +	return ulpi_request(ulpi_viewport, val); +} + +u32 ulpi_read(u32 ulpi_viewport, u8 *reg) +{ +	u32 err; +	u32 val = ULPI_RWRUN | ((u32)reg << 16); + +	err = ulpi_request(ulpi_viewport, val); +	if (err) +		return err; + +	return (readl(ulpi_viewport) >> 8) & 0xff; +} diff --git a/drivers/usb/ulpi/ulpi.c b/drivers/usb/ulpi/ulpi.c new file mode 100644 index 000000000..805e29d76 --- /dev/null +++ b/drivers/usb/ulpi/ulpi.c @@ -0,0 +1,227 @@ +/* + * Copyright (C) 2011 Jana Rapava <fermata7@gmail.com> + * Copyright (C) 2011 CompuLab, Ltd. <www.compulab.co.il> + * + * Authors: Jana Rapava <fermata7@gmail.com> + *	    Igor Grinberg <grinberg@compulab.co.il> + * + * Based on: + * linux/drivers/usb/otg/ulpi.c + * Generic ULPI USB transceiver support + * + * Original Copyright follow: + * Copyright (C) 2009 Daniel Mack <daniel@caiaq.de> + * + * Based on sources from + * + *   Sascha Hauer <s.hauer@pengutronix.de> + *   Freescale Semiconductors + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + */ + +#include <common.h> +#include <exports.h> +#include <usb/ulpi.h> + +#define ULPI_ID_REGS_COUNT	4 +#define ULPI_TEST_VALUE		0x55	/* 0x55 == 0b01010101 */ + +static struct ulpi_regs *ulpi = (struct ulpi_regs *)0; + +static int ulpi_integrity_check(u32 ulpi_viewport) +{ +	u32 err, val, tval = ULPI_TEST_VALUE; +	int i; + +	/* Use the 'special' test value to check all bits */ +	for (i = 0; i < 2; i++, tval <<= 1) { +		err = ulpi_write(ulpi_viewport, &ulpi->scratch, tval); +		if (err) +			return err; + +		val = ulpi_read(ulpi_viewport, &ulpi->scratch); +		if (val != tval) { +			printf("ULPI integrity check failed\n"); +			return val; +		} +	} + +	return 0; +} + +int ulpi_init(u32 ulpi_viewport) +{ +	u32 val, id = 0; +	u8 *reg = &ulpi->product_id_high; +	int i; + +	/* Assemble ID from four ULPI ID registers (8 bits each). */ +	for (i = 0; i < ULPI_ID_REGS_COUNT; i++) { +		val = ulpi_read(ulpi_viewport, reg - i); +		if (val == ULPI_ERROR) +			return val; + +		id = (id << 8) | val; +	} + +	/* Split ID into vendor and product ID. */ +	debug("ULPI transceiver ID 0x%04x:0x%04x\n", id >> 16, id & 0xffff); + +	return ulpi_integrity_check(ulpi_viewport); +} + +int ulpi_select_transceiver(u32 ulpi_viewport, u8 speed) +{ +	u8 tspeed = ULPI_FC_FULL_SPEED; +	u32 val; + +	switch (speed) { +	case ULPI_FC_HIGH_SPEED: +	case ULPI_FC_FULL_SPEED: +	case ULPI_FC_LOW_SPEED: +	case ULPI_FC_FS4LS: +		tspeed = speed; +		break; +	default: +		printf("ULPI: %s: wrong transceiver speed specified, " +			"falling back to full speed\n", __func__); +	} + +	val = ulpi_read(ulpi_viewport, &ulpi->function_ctrl); +	if (val == ULPI_ERROR) +		return val; + +	/* clear the previous speed setting */ +	val = (val & ~ULPI_FC_XCVRSEL_MASK) | tspeed; + +	return ulpi_write(ulpi_viewport, &ulpi->function_ctrl, val); +} + +int ulpi_set_vbus(u32 ulpi_viewport, int on, int ext_power, int ext_ind) +{ +	u32 flags = ULPI_OTG_DRVVBUS; +	u8 *reg = on ? &ulpi->otg_ctrl_set : &ulpi->otg_ctrl_clear; + +	if (ext_power) +		flags |= ULPI_OTG_DRVVBUS_EXT; +	if (ext_ind) +		flags |= ULPI_OTG_EXTVBUSIND; + +	return ulpi_write(ulpi_viewport, reg, flags); +} + +int ulpi_set_pd(u32 ulpi_viewport, int enable) +{ +	u32 val = ULPI_OTG_DP_PULLDOWN | ULPI_OTG_DM_PULLDOWN; +	u8 *reg = enable ? &ulpi->otg_ctrl_set : &ulpi->otg_ctrl_clear; + +	return ulpi_write(ulpi_viewport, reg, val); +} + +int ulpi_opmode_sel(u32 ulpi_viewport, u8 opmode) +{ +	u8 topmode = ULPI_FC_OPMODE_NORMAL; +	u32 val; + +	switch (opmode) { +	case ULPI_FC_OPMODE_NORMAL: +	case ULPI_FC_OPMODE_NONDRIVING: +	case ULPI_FC_OPMODE_DISABLE_NRZI: +	case ULPI_FC_OPMODE_NOSYNC_NOEOP: +		topmode = opmode; +		break; +	default: +		printf("ULPI: %s: wrong OpMode specified, " +			"falling back to OpMode Normal\n", __func__); +	} + +	val = ulpi_read(ulpi_viewport, &ulpi->function_ctrl); +	if (val == ULPI_ERROR) +		return val; + +	/* clear the previous opmode setting */ +	val = (val & ~ULPI_FC_OPMODE_MASK) | topmode; + +	return ulpi_write(ulpi_viewport, &ulpi->function_ctrl, val); +} + +int ulpi_serial_mode_enable(u32 ulpi_viewport, u8 smode) +{ +	switch (smode) { +	case ULPI_IFACE_6_PIN_SERIAL_MODE: +	case ULPI_IFACE_3_PIN_SERIAL_MODE: +		break; +	default: +		printf("ULPI: %s: unrecognized Serial Mode specified\n", +			__func__); +		return ULPI_ERROR; +	} + +	return ulpi_write(ulpi_viewport, &ulpi->iface_ctrl_set, smode); +} + +int ulpi_suspend(u32 ulpi_viewport) +{ +	u32 err; + +	err = ulpi_write(ulpi_viewport, &ulpi->function_ctrl_clear, +			ULPI_FC_SUSPENDM); +	if (err) +		printf("ULPI: %s: failed writing the suspend bit\n", __func__); + +	return err; +} + +/* + * Wait for ULPI PHY reset to complete. + * Actual wait for reset must be done in a view port specific way, + * because it involves checking the DIR line. + */ +static int __ulpi_reset_wait(u32 ulpi_viewport) +{ +	u32 val; +	int timeout = CONFIG_USB_ULPI_TIMEOUT; + +	/* Wait for the RESET bit to become zero */ +	while (--timeout) { +		/* +		 * This function is generic and suppose to work +		 * with any viewport, so we cheat here and don't check +		 * for the error of ulpi_read(), if there is one, then +		 * there will be a timeout. +		 */ +		val = ulpi_read(ulpi_viewport, &ulpi->function_ctrl); +		if (!(val & ULPI_FC_RESET)) +			return 0; + +		udelay(1); +	} + +	printf("ULPI: %s: reset timed out\n", __func__); + +	return ULPI_ERROR; +} +int ulpi_reset_wait(u32) __attribute__((weak, alias("__ulpi_reset_wait"))); + +int ulpi_reset(u32 ulpi_viewport) +{ +	u32 err; + +	err = ulpi_write(ulpi_viewport, +			&ulpi->function_ctrl_set, ULPI_FC_RESET); +	if (err) { +		printf("ULPI: %s: failed writing reset bit\n", __func__); +		return err; +	} + +	return ulpi_reset_wait(ulpi_viewport); +} diff --git a/include/configs/efikamx.h b/include/configs/efikamx.h index 2b069d6b7..522487b39 100644 --- a/include/configs/efikamx.h +++ b/include/configs/efikamx.h @@ -44,6 +44,10 @@  #define CONFIG_SYS_TEXT_BASE		0x97800000 +#define	CONFIG_L2_OFF +#define	CONFIG_SYS_ICACHE_OFF +#define	CONFIG_SYS_DCACHE_OFF +  /*   * Bootloader Components Configuration   */ @@ -53,6 +57,8 @@  #define CONFIG_CMD_FAT  #define CONFIG_CMD_EXT2  #define CONFIG_CMD_IDE +#define CONFIG_CMD_NET +#define CONFIG_CMD_DATE  #undef CONFIG_CMD_IMLS  /* @@ -175,17 +181,46 @@  #endif  /* + * USB + */ +#define	CONFIG_CMD_USB +#ifdef	CONFIG_CMD_USB +#define	CONFIG_USB_EHCI			/* Enable EHCI USB support */ +#define	CONFIG_USB_EHCI_MX5 +#define	CONFIG_USB_ULPI +#define	CONFIG_USB_ULPI_VIEWPORT +#define	CONFIG_MXC_USB_PORT	1 +#if	(CONFIG_MXC_USB_PORT == 0) +#define	CONFIG_MXC_USB_PORTSC	(1 << 28) +#define	CONFIG_MXC_USB_FLAGS	MXC_EHCI_INTERNAL_PHY +#else +#define	CONFIG_MXC_USB_PORTSC	(2 << 30) +#define	CONFIG_MXC_USB_FLAGS	0 +#endif +#define	CONFIG_EHCI_IS_TDI +#define	CONFIG_USB_STORAGE +#define	CONFIG_USB_HOST_ETHER +#define	CONFIG_USB_KEYBOARD +#define	CONFIG_SYS_USB_EVENT_POLL_VIA_CONTROL_EP +#define CONFIG_PREBOOT +/* USB NET */ +#ifdef	CONFIG_CMD_NET +#define	CONFIG_USB_ETHER_ASIX +#define	CONFIG_NET_MULTI +#define	CONFIG_CMD_PING +#define	CONFIG_CMD_DHCP +#endif +#endif /* CONFIG_CMD_USB */ + +/*   * Filesystems   */  #ifdef CONFIG_CMD_FAT  #define CONFIG_DOS_PARTITION +#ifdef	CONFIG_CMD_NET +#define	CONFIG_CMD_NFS +#endif  #endif - -#undef CONFIG_CMD_PING -#undef CONFIG_CMD_DHCP -#undef CONFIG_CMD_NET -#undef CONFIG_CMD_NFS -#define CONFIG_CMD_DATE  /*   * Miscellaneous configurable options diff --git a/include/configs/mx51evk.h b/include/configs/mx51evk.h index dd53f48b9..3b18a18d3 100644 --- a/include/configs/mx51evk.h +++ b/include/configs/mx51evk.h @@ -110,6 +110,19 @@  #define CONFIG_CMD_MII  #define CONFIG_CMD_NET +/* USB Configs */ +#define CONFIG_CMD_USB +#define CONFIG_CMD_FAT +#define CONFIG_USB_EHCI +#define CONFIG_USB_EHCI_MX5 +#define CONFIG_USB_STORAGE +#define CONFIG_USB_HOST_ETHER +#define CONFIG_USB_ETHER_ASIX +#define CONFIG_USB_ETHER_SMSC95XX +#define CONFIG_MXC_USB_PORT	1 +#define CONFIG_MXC_USB_PORTSC	PORT_PTS_ULPI +#define CONFIG_MXC_USB_FLAGS	MXC_EHCI_POWER_PINS_ENABLED +  /* allow to overwrite serial and ethaddr */  #define CONFIG_ENV_OVERWRITE  #define CONFIG_CONS_INDEX		1 diff --git a/include/configs/mx53loco.h b/include/configs/mx53loco.h index 537649ee1..34a4edd41 100644 --- a/include/configs/mx53loco.h +++ b/include/configs/mx53loco.h @@ -72,6 +72,19 @@  #define CONFIG_CMD_MII  #define CONFIG_CMD_NET +/* USB Configs */ +#define CONFIG_CMD_USB +#define CONFIG_CMD_FAT +#define CONFIG_USB_EHCI +#define CONFIG_USB_EHCI_MX5 +#define CONFIG_USB_STORAGE +#define CONFIG_USB_HOST_ETHER +#define CONFIG_USB_ETHER_ASIX +#define CONFIG_USB_ETHER_SMSC95XX +#define CONFIG_MXC_USB_PORT	1 +#define CONFIG_MXC_USB_PORTSC	(PORT_PTS_UTMI | PORT_PTS_PTW) +#define CONFIG_MXC_USB_FLAGS	0 +  /* allow to overwrite serial and ethaddr */  #define CONFIG_ENV_OVERWRITE  #define CONFIG_CONS_INDEX		1 diff --git a/include/configs/s5p_goni.h b/include/configs/s5p_goni.h index 3434de7f7..56b5547fd 100644 --- a/include/configs/s5p_goni.h +++ b/include/configs/s5p_goni.h @@ -237,5 +237,8 @@  #define CONFIG_SYS_I2C_SPEED	50000  #define CONFIG_I2C_MULTI_BUS  #define CONFIG_SYS_MAX_I2C_BUS	7 +#define CONFIG_USB_GADGET +#define CONFIG_USB_GADGET_S3C_UDC_OTG +#define CONFIG_USB_GADGET_DUALSPEED  #endif	/* __CONFIG_H */ diff --git a/include/usb/ehci-fsl.h b/include/usb/ehci-fsl.h index 67600ed52..28693020e 100644 --- a/include/usb/ehci-fsl.h +++ b/include/usb/ehci-fsl.h @@ -186,35 +186,36 @@ struct usb_ehci {  	u32     gptimer1_ctrl;	/* 0x08C - General Purpose Timer 1 control */  	u32	sbuscfg;	/* 0x090 - System Bus Interface Control */  	u8	res2[0x6C]; -	u16	caplength;	/* 0x100 - Capability Register Length */ +	u8	caplength;	/* 0x100 - Capability Register Length */ +	u8	res3[0x1];  	u16	hciversion;	/* 0x102 - Host Interface Version */  	u32	hcsparams;	/* 0x104 - Host Structural Parameters */  	u32	hccparams;	/* 0x108 - Host Capability Parameters */ -	u8	res3[0x14]; +	u8	res4[0x14];  	u32	dciversion;	/* 0x120 - Device Interface Version */  	u32	dciparams;	/* 0x124 - Device Controller Params */ -	u8	res4[0x18]; +	u8	res5[0x18];  	u32	usbcmd;		/* 0x140 - USB Command */  	u32	usbsts;		/* 0x144 - USB Status */  	u32	usbintr;	/* 0x148 - USB Interrupt Enable */  	u32	frindex;	/* 0x14C - USB Frame Index */ -	u8	res5[0x4]; +	u8	res6[0x4];  	u32	perlistbase;	/* 0x154 - Periodic List Base  					 - USB Device Address */  	u32	ep_list_addr;	/* 0x158 - Next Asynchronous List  					 - End Point Address */ -	u8	res6[0x4]; +	u8	res7[0x4];  	u32	burstsize;	/* 0x160 - Programmable Burst Size */  #define FSL_EHCI_TXPBURST(X)	((X) << 8)  #define FSL_EHCI_RXPBURST(X)	(X)  	u32	txfilltuning;	/* 0x164 - Host TT Transmit  					   pre-buffer packet tuning */ -	u8	res7[0x8]; +	u8	res8[0x8];  	u32	ulpi_viewpoint;	/* 0x170 - ULPI Reister Access */ -	u8	res8[0xc]; +	u8	res9[0xc];  	u32	config_flag;	/* 0x180 - Configured Flag Register */  	u32	portsc;		/* 0x184 - Port status/control */ -	u8	res9[0x1C]; +	u8	res10[0x1C];  	u32	otgsc;		/* 0x1a4 - Oo-The-Go status and control */  	u32	usbmode;	/* 0x1a8 - USB Device Mode */  	u32	epsetupstat;	/* 0x1ac - End Point Setup Status */ @@ -228,18 +229,34 @@ struct usb_ehci {  	u32	epctrl3;	/* 0x1cc - End Point Control 3 */  	u32	epctrl4;	/* 0x1d0 - End Point Control 4 */  	u32	epctrl5;	/* 0x1d4 - End Point Control 5 */ -	u8	res10[0x28]; +	u8	res11[0x28];  	u32	usbgenctrl;	/* 0x200 - USB General Control */  	u32	isiphyctrl;	/* 0x204 - On-Chip PHY Control */ -	u8	res11[0x1F8]; +	u8	res12[0x1F8];  	u32	snoop1;		/* 0x400 - Snoop 1 */  	u32	snoop2;		/* 0x404 - Snoop 2 */  	u32	age_cnt_limit;	/* 0x408 - Age Count Threshold */  	u32	prictrl;	/* 0x40c - Priority Control */  	u32	sictrl;		/* 0x410 - System Interface Control */ -	u8	res12[0xEC]; +	u8	res13[0xEC];  	u32	control;	/* 0x500 - Control */ -	u8	res13[0xafc]; +	u8	res14[0xafc];  }; +/* + * For MXC SOCs + */ +#define MXC_EHCI_POWER_PINS_ENABLED	(1 << 5) +#define MXC_EHCI_TTL_ENABLED		(1 << 6) +#define MXC_EHCI_INTERNAL_PHY		(1 << 7) + +/* Board-specific initialization */ +int board_ehci_hcd_init(int port); + +/* CPU-specific abstracted-out IOMUX init */ +#ifdef CONFIG_MX51 +void setup_iomux_usb_h1(void); +void setup_iomux_usb_h2(void); +#endif +  #endif /* _EHCI_FSL_H */ diff --git a/include/usb/lin_gadget_compat.h b/include/usb/lin_gadget_compat.h new file mode 100644 index 000000000..9b315118d --- /dev/null +++ b/include/usb/lin_gadget_compat.h @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2011 Samsung Electronics + * Lukasz Majewski <l.majewski@samsung.com> + * + * This is a Linux kernel compatibility layer for USB Gadget + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ + +#ifndef __LIN_COMPAT_H__ +#define __LIN_COMPAT_H__ + +/* common */ +#define spin_lock_init(...) +#define spin_lock(...) +#define spin_lock_irqsave(lock, flags) do {flags = 1; } while (0) +#define spin_unlock(...) +#define spin_unlock_irqrestore(lock, flags) do {flags = 0; } while (0) +#define disable_irq(...) +#define enable_irq(...) + +#define mutex_init(...) +#define mutex_lock(...) +#define mutex_unlock(...) + +#define WARN_ON(x) if (x) {printf("WARNING in %s line %d\n" \ +				  , __FILE__, __LINE__); } + +#define KERN_WARNING +#define KERN_ERR +#define KERN_NOTICE +#define KERN_DEBUG + +#define GFP_KERNEL	0 + +#define IRQ_HANDLED	1 + +#define ENOTSUPP	524	/* Operation is not supported */ + +#define kmalloc(size, type) memalign(CONFIG_SYS_CACHELINE_SIZE, size) +#define kfree(addr) free(addr) +#define mdelay(n) ({unsigned long msec = (n); while (msec--) udelay(1000); }) + +#define __iomem +#define min_t min +#define dma_cache_maint(addr, size, mode) cache_flush() +void cache_flush(void); + +#endif /* __LIN_COMPAT_H__ */ diff --git a/include/usb/mv_udc.h b/include/usb/mv_udc.h new file mode 100644 index 000000000..51d36c3f3 --- /dev/null +++ b/include/usb/mv_udc.h @@ -0,0 +1,151 @@ +/* + * Copyright 2011, Marvell Semiconductor Inc. + * Lei Wen <leiwen@marvell.com> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of + * the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, + * MA 02111-1307 USA + */ + + +#ifndef __MV_UDC_H__ +#define __MV_UDC_H__ + +#include <asm/byteorder.h> +#include <asm/errno.h> +#include <linux/usb/ch9.h> +#include <linux/usb/gadget.h> + +/* Endpoint 0 states */ +#define EP0_IDLE		0 +#define EP0_IN_DATA		1 +#define EP0_OUT_DATA		2 +#define EP0_XFER_COMPLETE	3 + + +/* Endpoint parameters */ +#define MAX_ENDPOINTS		4 +#define EP_MAX_PACKET_SIZE	0x200 + +#define EP0_MAX_PACKET_SIZE	64 +#define UDC_OUT_ENDPOINT        0x02 +#define UDC_OUT_PACKET_SIZE	EP_MAX_PACKET_SIZE +#define UDC_IN_ENDPOINT         0x01 +#define UDC_IN_PACKET_SIZE      EP_MAX_PACKET_SIZE +#define UDC_INT_ENDPOINT        0x05 +#define UDC_INT_PACKET_SIZE     EP_MAX_PACKET_SIZE +#define UDC_BULK_PACKET_SIZE    EP_MAX_PACKET_SIZE + +#define        NUM_ENDPOINTS   6 +#define		REQ_COUNT	12 +struct mv_ep { +	struct usb_ep ep; +	struct usb_request req; +	struct list_head queue; +	const struct usb_endpoint_descriptor *desc; +}; + +struct mv_udc { +	u32 pad0[80]; +#define MICRO_8FRAME	0x8 +#define USBCMD_ITC(x)	(((x > 0xff) ? 0xff : x) << 16) +#define USBCMD_FS2	(1 << 15) +#define USBCMD_RST	(1 << 1) +#define USBCMD_RUN	(1) +	u32 usbcmd;		/* 0x140 */ +#define STS_SLI		(1 << 8) +#define STS_URI		(1 << 6) +#define STS_PCI		(1 << 2) +#define STS_UEI		(1 << 1) +#define STS_UI		(1 << 0) +	u32 usbsts;		/* 0x144 */ +	u32 pad1[3]; +	u32 devaddr;		/* 0x154 */ +	u32 epinitaddr;		/* 0x158 */ +	u32 pad2[10]; +#define PTS_ENABLE	2 +#define PTS(x)		((x & 0x3) << 30) +#define PFSC		(1 << 24) +	u32 portsc;		/* 0x184 */ +	u32 pad3[8]; +#define USBMODE_DEVICE	2 +	u32 usbmode;		/* 0x1a8 */ +	u32 epstat;		/* 0x1ac */ +#define EPT_TX(x)	(1 << ((x & 0xffff) + 16)) +#define EPT_RX(x)	(1 << (x & 0xffff)) +	u32 epprime;		/* 0x1b0 */ +	u32 epflush;		/* 0x1b4 */ +	u32 pad4; +	u32 epcomp;		/* 0x1bc */ +#define CTRL_TXE              (1 << 23) +#define CTRL_TXR              (1 << 22) +#define CTRL_RXE              (1 << 7) +#define CTRL_RXR              (1 << 6) +#define CTRL_TXT_BULK         (2 << 18) +#define CTRL_RXT_BULK         (2 << 2) +	u32 epctrl[16];		/* 0x1c0 */ +}; + +struct mv_drv { +	struct usb_gadget		gadget; +	struct usb_gadget_driver		*driver; +	struct mv_udc			*udc; +}; + +struct ept_queue_head { +	unsigned config; +	unsigned current; /* read-only */ + +	unsigned next; +	unsigned info; +	unsigned page0; +	unsigned page1; +	unsigned page2; +	unsigned page3; +	unsigned page4; +	unsigned reserved_0; + +	unsigned char setup_data[8]; + +	unsigned reserved_1; +	unsigned reserved_2; +	unsigned reserved_3; +	unsigned reserved_4; +}; + +#define CONFIG_MAX_PKT(n)     ((n) << 16) +#define CONFIG_ZLT            (1 << 29)    /* stop on zero-len xfer */ +#define CONFIG_IOS            (1 << 15)    /* IRQ on setup */ + +struct ept_queue_item { +	unsigned next; +	unsigned info; +	unsigned page0; +	unsigned page1; +	unsigned page2; +	unsigned page3; +	unsigned page4; +	unsigned reserved; +}; + +#define TERMINATE 1 +#define INFO_BYTES(n)         ((n) << 16) +#define INFO_IOC              (1 << 15) +#define INFO_ACTIVE           (1 << 7) +#define INFO_HALTED           (1 << 6) +#define INFO_BUFFER_ERROR     (1 << 5) +#define INFO_TX_ERROR         (1 << 3) + +extern int usb_lowlevel_init(void); +#endif /* __MV_UDC_H__ */ diff --git a/include/usb/s3c_udc.h b/include/usb/s3c_udc.h new file mode 100644 index 000000000..14dadc8b0 --- /dev/null +++ b/include/usb/s3c_udc.h @@ -0,0 +1,175 @@ +/* + * drivers/usb/gadget/s3c_udc.h + * Samsung S3C on-chip full/high speed USB device controllers + * Copyright (C) 2005 for Samsung Electronics + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA + * + */ + +#ifndef __S3C_USB_GADGET +#define __S3C_USB_GADGET + +#include <asm/errno.h> +#include <linux/usb/ch9.h> +#include <linux/usb/gadget.h> +#include <linux/list.h> +#include <usb/lin_gadget_compat.h> + +#define PHY0_SLEEP              (1 << 5) + +/*-------------------------------------------------------------------------*/ +/* DMA bounce buffer size, 16K is enough even for mass storage */ +#define DMA_BUFFER_SIZE	(4096*4) + +#define EP0_FIFO_SIZE		64 +#define EP_FIFO_SIZE		512 +#define EP_FIFO_SIZE2		1024 +/* ep0-control, ep1in-bulk, ep2out-bulk, ep3in-int */ +#define S3C_MAX_ENDPOINTS	4 +#define S3C_MAX_HW_ENDPOINTS	16 + +#define WAIT_FOR_SETUP          0 +#define DATA_STATE_XMIT         1 +#define DATA_STATE_NEED_ZLP     2 +#define WAIT_FOR_OUT_STATUS     3 +#define DATA_STATE_RECV         4 +#define WAIT_FOR_COMPLETE	5 +#define WAIT_FOR_OUT_COMPLETE	6 +#define WAIT_FOR_IN_COMPLETE	7 +#define WAIT_FOR_NULL_COMPLETE	8 + +#define TEST_J_SEL		0x1 +#define TEST_K_SEL		0x2 +#define TEST_SE0_NAK_SEL	0x3 +#define TEST_PACKET_SEL		0x4 +#define TEST_FORCE_ENABLE_SEL	0x5 + +/* ************************************************************************* */ +/* IO + */ + +enum ep_type { +	ep_control, ep_bulk_in, ep_bulk_out, ep_interrupt +}; + +struct s3c_ep { +	struct usb_ep ep; +	struct s3c_udc *dev; + +	const struct usb_endpoint_descriptor *desc; +	struct list_head queue; +	unsigned long pio_irqs; +	int len; +	void *dma_buf; + +	u8 stopped; +	u8 bEndpointAddress; +	u8 bmAttributes; + +	enum ep_type ep_type; +	int fifo_num; +}; + +struct s3c_request { +	struct usb_request req; +	struct list_head queue; +}; + +struct s3c_udc { +	struct usb_gadget gadget; +	struct usb_gadget_driver *driver; + +	struct s3c_plat_otg_data *pdata; + +	void *dma_buf[S3C_MAX_ENDPOINTS+1]; +	dma_addr_t dma_addr[S3C_MAX_ENDPOINTS+1]; + +	int ep0state; +	struct s3c_ep ep[S3C_MAX_ENDPOINTS]; + +	unsigned char usb_address; + +	unsigned req_pending:1, req_std:1, req_config:1; +}; + +extern struct s3c_udc *the_controller; + +#define ep_is_in(EP) (((EP)->bEndpointAddress&USB_DIR_IN) == USB_DIR_IN) +#define ep_index(EP) ((EP)->bEndpointAddress&0xF) +#define ep_maxpacket(EP) ((EP)->ep.maxpacket) + +/*-------------------------------------------------------------------------*/ +/* #define DEBUG_UDC */ +#ifdef DEBUG_UDC +#define DBG(stuff...)		printf("udc: " stuff) +#else +#define DBG(stuff...)		do {} while (0) +#endif + +#ifdef DEBUG_S3C_UDC_SETUP +#define DEBUG_SETUP(fmt, args...) printk(fmt, ##args) +#else +#define DEBUG_SETUP(fmt, args...) do {} while (0) +#endif + +#ifdef DEBUG_S3C_UDC_EP0 +#define DEBUG_EP0(fmt, args...) printk(fmt, ##args) +#else +#define DEBUG_EP0(fmt, args...) do {} while (0) +#endif + +#ifdef DEBUG_S3C_UDC +#define DEBUG(fmt, args...) printk(fmt, ##args) +#else +#define DEBUG(fmt, args...) do {} while (0) +#endif + +#ifdef DEBUG_S3C_UDC_ISR +#define DEBUG_ISR(fmt, args...) printk(fmt, ##args) +#else +#define DEBUG_ISR(fmt, args...) do {} while (0) +#endif + +#ifdef DEBUG_S3C_UDC_OUT_EP +#define DEBUG_OUT_EP(fmt, args...) printk(fmt, ##args) +#else +#define DEBUG_OUT_EP(fmt, args...) do {} while (0) +#endif + +#ifdef DEBUG_S3C_UDC_IN_EP +#define DEBUG_IN_EP(fmt, args...) printk(fmt, ##args) +#else +#define DEBUG_IN_EP(fmt, args...) do {} while (0) +#endif + +#define ERR(stuff...)		printf("ERR udc: " stuff) +#define WARN(stuff...)		printf("WARNING udc: " stuff) +#define INFO(stuff...)		printf("INFO udc: " stuff) + +extern void otg_phy_init(struct s3c_udc *dev); +extern void otg_phy_off(struct s3c_udc *dev); + +extern void s3c_udc_ep_set_stall(struct s3c_ep *ep); +extern int s3c_udc_probe(struct s3c_plat_otg_data *pdata); + +struct s3c_plat_otg_data { +	int		(*phy_control)(int on); +	unsigned int	regs_phy; +	unsigned int	regs_otg; +	unsigned int    usb_phy_ctrl; +	unsigned int    usb_flags; +}; +#endif diff --git a/include/usb/ulpi.h b/include/usb/ulpi.h new file mode 100644 index 000000000..d87129087 --- /dev/null +++ b/include/usb/ulpi.h @@ -0,0 +1,298 @@ +/* + * Generic ULPI interface. + * + * Copyright (C) 2011 Jana Rapava <fermata7@gmail.com> + * Copyright (C) 2011 CompuLab, Ltd. <www.compulab.co.il> + * + * Authors: Jana Rapava <fermata7@gmail.com> + *	    Igor Grinberg <grinberg@compulab.co.il> + * + * Register offsets taken from: + * linux/include/linux/usb/ulpi.h + * + * Original Copyrights follow: + * Copyright (C) 2010 Nokia Corporation + * + * This software is distributed under the terms of the GNU General + * Public License ("GPL") as published by the Free Software Foundation, + * version 2 of that License. + */ + +#ifndef __USB_ULPI_H__ +#define __USB_ULPI_H__ + +#define ULPI_ERROR	(1 << 8) /* overflow from any register value */ + +#ifndef CONFIG_USB_ULPI_TIMEOUT +#define CONFIG_USB_ULPI_TIMEOUT 1000	/* timeout in us */ +#endif + +/* + * Initialize the ULPI transciever and check the interface integrity. + * @ulpi_viewport -  the address of the ULPI viewport register. + * + * returns 0 on success, ULPI_ERROR on failure. + */ +int ulpi_init(u32 ulpi_viewport); + +/* + * Select transceiver speed. + * @speed	- ULPI_FC_HIGH_SPEED, ULPI_FC_FULL_SPEED (default), + *                ULPI_FC_LOW_SPEED,  ULPI_FC_FS4LS + * returns 0 on success, ULPI_ERROR on failure. + */ +int ulpi_select_transceiver(u32 ulpi_viewport, u8 speed); + +/* + * Enable/disable VBUS. + * @ext_power		- external VBUS supply is used (default is false) + * @ext_indicator	- external VBUS over-current indicator is used + * + * returns 0 on success, ULPI_ERROR on failure. + */ +int ulpi_enable_vbus(u32 ulpi_viewport, int on, int ext_power, int ext_ind); + +/* + * Enable/disable pull-down resistors on D+ and D- USB lines. + * + * returns 0 on success, ULPI_ERROR on failure. + */ +int ulpi_set_pd(u32 ulpi_viewport, int enable); + +/* + * Select OpMode. + * @opmode	- ULPI_FC_OPMODE_NORMAL (default), ULPI_FC_OPMODE_NONDRIVING, + *		  ULPI_FC_OPMODE_DISABLE_NRZI,	   ULPI_FC_OPMODE_NOSYNC_NOEOP + * + * returns 0 on success, ULPI_ERROR on failure. + */ +int ulpi_opmode_sel(u32 ulpi_viewport, u8 opmode); + +/* + * Switch to Serial Mode. + * @smode	- ULPI_IFACE_6_PIN_SERIAL_MODE or ULPI_IFACE_3_PIN_SERIAL_MODE + * + * returns 0 on success, ULPI_ERROR on failure. + * + * Notes: + * Switches immediately to Serial Mode. + * To return from Serial Mode, STP line needs to be asserted. + */ +int ulpi_serial_mode_enable(u32 ulpi_viewport, u8 smode); + +/* + * Put PHY into low power mode. + * + * returns 0 on success, ULPI_ERROR on failure. + * + * Notes: + * STP line must be driven low to keep the PHY in suspend. + * To resume the PHY, STP line needs to be asserted. + */ +int ulpi_suspend(u32 ulpi_viewport); + +/* + * Reset the transceiver. ULPI interface and registers are not affected. + * + * returns 0 on success, ULPI_ERROR on failure. + */ +int ulpi_reset(u32 ulpi_viewport); + + +/* ULPI access methods below must be implemented for each ULPI viewport. */ + +/* + * Write to the ULPI PHY register via the viewport. + * @reg		- the ULPI register (one of the fields in struct ulpi_regs). + * @value	- the value - only 8 lower bits are used, others ignored. + * + * returns 0 on success, ULPI_ERROR on failure. + */ +u32 ulpi_write(u32 ulpi_viewport, u8 *reg, u32 value); + +/* + * Read the ULPI PHY register content via the viewport. + * @reg		- the ULPI register (one of the fields in struct ulpi_regs). + * + * returns register content on success, ULPI_ERROR on failure. + */ +u32 ulpi_read(u32 ulpi_viewport, u8 *reg); + +/* + * Wait for the reset to complete. + * The Link must not attempt to access the PHY until the reset has + * completed and DIR line is de-asserted. + */ +int ulpi_reset_wait(u32 ulpi_viewport); + +/* Access Extended Register Set (indicator) */ +#define ACCESS_EXT_REGS_OFFSET	0x2f	/* read-write */ +/* Vendor-specific */ +#define VENDOR_SPEC_OFFSET	0x30 + +/* + * Extended Register Set + * + * Addresses 0x00-0x3F map directly to Immediate Register Set. + * Addresses 0x40-0x7F are reserved. + * Addresses 0x80-0xff are vendor-specific. + */ +#define EXT_VENDOR_SPEC_OFFSET	0x80 + +/* ULPI registers, bits and offsets definitions */ +struct ulpi_regs { +	/* Vendor ID and Product ID: 0x00 - 0x03 Read-only */ +	u8	vendor_id_low; +	u8	vendor_id_high; +	u8	product_id_low; +	u8	product_id_high; +	/* Function Control: 0x04 - 0x06 Read */ +	u8	function_ctrl;		/* 0x04 Write */ +	u8	function_ctrl_set;	/* 0x05 Set */ +	u8	function_ctrl_clear;	/* 0x06 Clear */ +	/* Interface Control: 0x07 - 0x09 Read */ +	u8	iface_ctrl;		/* 0x07 Write */ +	u8	iface_ctrl_set;		/* 0x08 Set */ +	u8	iface_ctrl_clear;	/* 0x09 Clear */ +	/* OTG Control: 0x0A - 0x0C Read */ +	u8	otg_ctrl;		/* 0x0A Write */ +	u8	otg_ctrl_set;		/* 0x0B Set */ +	u8	otg_ctrl_clear;		/* 0x0C Clear */ +	/* USB Interrupt Enable Rising: 0x0D - 0x0F Read */ +	u8	usb_ie_rising;		/* 0x0D Write */ +	u8	usb_ie_rising_set;	/* 0x0E Set */ +	u8	usb_ie_rising_clear;	/* 0x0F Clear */ +	/* USB Interrupt Enable Falling: 0x10 - 0x12 Read */ +	u8	usb_ie_falling;		/* 0x10 Write */ +	u8	usb_ie_falling_set;	/* 0x11 Set */ +	u8	usb_ie_falling_clear;	/* 0x12 Clear */ +	/* USB Interrupt Status: 0x13 Read-only */ +	u8	usb_int_status; +	/* USB Interrupt Latch: 0x14 Read-only with auto-clear */ +	u8	usb_int_latch; +	/* Debug: 0x15 Read-only */ +	u8	debug; +	/* Scratch Register: 0x16 - 0x18 Read */ +	u8	scratch;		/* 0x16 Write */ +	u8	scratch_set;		/* 0x17 Set */ +	u8	scratch_clear;		/* 0x18 Clear */ +	/* +	 * Optional Carkit registers: +	 * Carkit Control: 0x19 - 0x1B Read +	 */ +	u8	carkit_ctrl;		/* 0x19 Write */ +	u8	carkit_ctrl_set;	/* 0x1A Set */ +	u8	carkit_ctrl_clear;	/* 0x1B Clear */ +	/* Carkit Interrupt Delay: 0x1C Read, Write */ +	u8	carkit_int_delay; +	/* Carkit Interrupt Enable: 0x1D - 0x1F Read */ +	u8	carkit_ie;		/* 0x1D Write */ +	u8	carkit_ie_set;		/* 0x1E Set */ +	u8	carkit_ie_clear;	/* 0x1F Clear */ +	/* Carkit Interrupt Status: 0x20 Read-only */ +	u8	carkit_int_status; +	/* Carkit Interrupt Latch: 0x21 Read-only with auto-clear */ +	u8	carkit_int_latch; +	/* Carkit Pulse Control: 0x22 - 0x24 Read */ +	u8	carkit_pulse_ctrl;		/* 0x22 Write */ +	u8	carkit_pulse_ctrl_set;		/* 0x23 Set */ +	u8	carkit_pulse_ctrl_clear;	/* 0x24 Clear */ +	/* +	 * Other optional registers: +	 * Transmit Positive Width: 0x25 Read, Write +	 */ +	u8	transmit_pos_width; +	/* Transmit Negative Width: 0x26 Read, Write */ +	u8	transmit_neg_width; +	/* Receive Polarity Recovery: 0x27 Read, Write */ +	u8	recv_pol_recovery; +	/* +	 * Addresses 0x28 - 0x2E are reserved, so we use offsets +	 * for immediate registers with higher addresses +	 */ +}; + +/* + * Register Bits + */ + +/* Function Control */ +#define ULPI_FC_XCVRSEL_MASK		(3 << 0) +#define ULPI_FC_HIGH_SPEED		(0 << 0) +#define ULPI_FC_FULL_SPEED		(1 << 0) +#define ULPI_FC_LOW_SPEED		(2 << 0) +#define ULPI_FC_FS4LS			(3 << 0) +#define ULPI_FC_TERMSELECT		(1 << 2) +#define ULPI_FC_OPMODE_MASK		(3 << 3) +#define ULPI_FC_OPMODE_NORMAL		(0 << 3) +#define ULPI_FC_OPMODE_NONDRIVING	(1 << 3) +#define ULPI_FC_OPMODE_DISABLE_NRZI	(2 << 3) +#define ULPI_FC_OPMODE_NOSYNC_NOEOP	(3 << 3) +#define ULPI_FC_RESET			(1 << 5) +#define ULPI_FC_SUSPENDM		(1 << 6) + +/* Interface Control */ +#define ULPI_IFACE_6_PIN_SERIAL_MODE	(1 << 0) +#define ULPI_IFACE_3_PIN_SERIAL_MODE	(1 << 1) +#define ULPI_IFACE_CARKITMODE		(1 << 2) +#define ULPI_IFACE_CLOCKSUSPENDM	(1 << 3) +#define ULPI_IFACE_AUTORESUME		(1 << 4) +#define ULPI_IFACE_EXTVBUS_COMPLEMENT	(1 << 5) +#define ULPI_IFACE_PASSTHRU		(1 << 6) +#define ULPI_IFACE_PROTECT_IFC_DISABLE	(1 << 7) + +/* OTG Control */ +#define ULPI_OTG_ID_PULLUP		(1 << 0) +#define ULPI_OTG_DP_PULLDOWN		(1 << 1) +#define ULPI_OTG_DM_PULLDOWN		(1 << 2) +#define ULPI_OTG_DISCHRGVBUS		(1 << 3) +#define ULPI_OTG_CHRGVBUS		(1 << 4) +#define ULPI_OTG_DRVVBUS		(1 << 5) +#define ULPI_OTG_DRVVBUS_EXT		(1 << 6) +#define ULPI_OTG_EXTVBUSIND		(1 << 7) + +/* + * USB Interrupt Enable Rising, + * USB Interrupt Enable Falling, + * USB Interrupt Status and + * USB Interrupt Latch + */ +#define ULPI_INT_HOST_DISCONNECT	(1 << 0) +#define ULPI_INT_VBUS_VALID		(1 << 1) +#define ULPI_INT_SESS_VALID		(1 << 2) +#define ULPI_INT_SESS_END		(1 << 3) +#define ULPI_INT_IDGRD			(1 << 4) + +/* Debug */ +#define ULPI_DEBUG_LINESTATE0		(1 << 0) +#define ULPI_DEBUG_LINESTATE1		(1 << 1) + +/* Carkit Control */ +#define ULPI_CARKIT_CTRL_CARKITPWR		(1 << 0) +#define ULPI_CARKIT_CTRL_IDGNDDRV		(1 << 1) +#define ULPI_CARKIT_CTRL_TXDEN			(1 << 2) +#define ULPI_CARKIT_CTRL_RXDEN			(1 << 3) +#define ULPI_CARKIT_CTRL_SPKLEFTEN		(1 << 4) +#define ULPI_CARKIT_CTRL_SPKRIGHTEN		(1 << 5) +#define ULPI_CARKIT_CTRL_MICEN			(1 << 6) + +/* Carkit Interrupt Enable */ +#define ULPI_CARKIT_INT_EN_IDFLOAT_RISE		(1 << 0) +#define ULPI_CARKIT_INT_EN_IDFLOAT_FALL		(1 << 1) +#define ULPI_CARKIT_INT_EN_CARINTDET		(1 << 2) +#define ULPI_CARKIT_INT_EN_DP_RISE		(1 << 3) +#define ULPI_CARKIT_INT_EN_DP_FALL		(1 << 4) + +/* Carkit Interrupt Status and Latch */ +#define ULPI_CARKIT_INT_IDFLOAT			(1 << 0) +#define ULPI_CARKIT_INT_CARINTDET		(1 << 1) +#define ULPI_CARKIT_INT_DP			(1 << 2) + +/* Carkit Pulse Control*/ +#define ULPI_CARKIT_PLS_CTRL_TXPLSEN		(1 << 0) +#define ULPI_CARKIT_PLS_CTRL_RXPLSEN		(1 << 1) +#define ULPI_CARKIT_PLS_CTRL_SPKRLEFT_BIASEN	(1 << 2) +#define ULPI_CARKIT_PLS_CTRL_SPKRRIGHT_BIASEN	(1 << 3) + + +#endif /* __USB_ULPI_H__ */ diff --git a/include/usbdescriptors.h b/include/usbdescriptors.h index 2dec3b93d..392fcf537 100644 --- a/include/usbdescriptors.h +++ b/include/usbdescriptors.h @@ -199,7 +199,7 @@ struct usb_endpoint_descriptor {  	u8 bmAttributes;  	u16 wMaxPacketSize;  	u8 bInterval; -} __attribute__ ((packed)); +} __attribute__ ((packed)) __attribute__ ((aligned(2)));  struct usb_interface_descriptor {  	u8 bLength; |