summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorWolfgang Denk <wd@denx.de>2011-12-12 07:58:58 +0100
committerWolfgang Denk <wd@denx.de>2011-12-12 07:58:58 +0100
commitb2eb7d9bc6032e16b7dd898f7c0ea654f63b61db (patch)
treeb8b649483183d93dedfcb5512d0b9720aa91644f
parent4f1a2cd1637027f31de7796aedb1fa5fc0ec0f97 (diff)
parentd98d8bc1c913a5a1aea6b17365f90c430d1fc95a (diff)
downloadblackbird-obmc-uboot-b2eb7d9bc6032e16b7dd898f7c0ea654f63b61db.tar.gz
blackbird-obmc-uboot-b2eb7d9bc6032e16b7dd898f7c0ea654f63b61db.zip
Merge branch 'master' of git://git.denx.de/u-boot-usb
* 'master' of git://git.denx.de/u-boot-usb: USB: efikamx: Enable USB on EfikaMX and EfikaSB USB: Add generic ULPI layer and a viewport USB: EHCI: Allow EHCI post-powerup configuration in board files USB: mx51evk: add end enable USB host support on port 1 USB: mx53loco: add end enable USB host support on port 1 USB: MX5: Add MX5 usb post-init callback USB: MX5: Abstract out mx51 USB pixmux configuration USB: MX5: add generic USB EHCI support for mx51 and mx53 USB: MX5: add helper functions to enable USB clocks usb:gadget:s5p Enable the USB Gadget framework at GONI usb:gadget:s5p USB Device Controller (UDC) implementation ehci: speed up initialization usb: add help for missing start subcommand cosmetic: remove excess whitespace from usb command help usb: align usb_endpoint_descriptor to 16-bit boundary usbtty: init endpoints prior to startup events pxa: convert pxa27x_udc to use read and write functions pxa: activate the first usb host port on pxa27x by default pxa: fix usb host register mismatch ehci-fsl: correct size of ehci caplength USB: Add usb_event_poll() to get keyboards working with EHCI USB: gadaget: add Marvell controller support USB: Fix complaints about strict aliasing in OHCI-HCD USB: Drop dead code from usb_kbd.c USB: Rework usb_kbd.c USB: Add functionality to poll the USB keyboard via control EP
-rw-r--r--Makefile1
-rw-r--r--arch/arm/cpu/armv7/mx5/clock.c72
-rw-r--r--arch/arm/cpu/pxa/usb.c6
-rw-r--r--arch/arm/include/asm/arch-mx5/clock.h5
-rw-r--r--arch/arm/include/asm/arch-mx5/crm_regs.h3
-rw-r--r--arch/arm/include/asm/arch-pxa/pxa-regs.h10
-rw-r--r--arch/arm/include/asm/arch-s5pc1xx/cpu.h4
-rw-r--r--board/efikamx/Makefile4
-rw-r--r--board/efikamx/efikamx-usb.c210
-rw-r--r--board/efikamx/efikamx.c15
-rw-r--r--board/freescale/mx51evk/mx51evk.c62
-rw-r--r--board/freescale/mx53loco/mx53loco.c10
-rw-r--r--board/samsung/goni/goni.c48
-rw-r--r--common/cmd_usb.c16
-rw-r--r--common/usb_kbd.c928
-rw-r--r--drivers/serial/usbtty.c4
-rw-r--r--drivers/usb/gadget/Makefile5
-rw-r--r--drivers/usb/gadget/gadget_chips.h7
-rw-r--r--drivers/usb/gadget/mv_udc.c499
-rw-r--r--drivers/usb/gadget/pxa27x_udc.c183
-rw-r--r--drivers/usb/gadget/regs-otg.h271
-rw-r--r--drivers/usb/gadget/s3c_udc_otg.c892
-rw-r--r--drivers/usb/gadget/s3c_udc_otg_xfer_dma.c1444
-rw-r--r--drivers/usb/host/Makefile1
-rw-r--r--drivers/usb/host/ehci-hcd.c47
-rw-r--r--drivers/usb/host/ehci-mx5.c255
-rw-r--r--drivers/usb/host/ohci-hcd.c75
-rw-r--r--drivers/usb/ulpi/Makefile44
-rw-r--r--drivers/usb/ulpi/ulpi-viewport.c118
-rw-r--r--drivers/usb/ulpi/ulpi.c227
-rw-r--r--include/configs/efikamx.h47
-rw-r--r--include/configs/mx51evk.h13
-rw-r--r--include/configs/mx53loco.h13
-rw-r--r--include/configs/s5p_goni.h3
-rw-r--r--include/usb/ehci-fsl.h41
-rw-r--r--include/usb/lin_gadget_compat.h62
-rw-r--r--include/usb/mv_udc.h151
-rw-r--r--include/usb/s3c_udc.h175
-rw-r--r--include/usb/ulpi.h298
-rw-r--r--include/usbdescriptors.h2
40 files changed, 5512 insertions, 759 deletions
diff --git a/Makefile b/Makefile
index 29e3dbb24e..bf8524d105 100644
--- a/Makefile
+++ b/Makefile
@@ -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 933ce05b76..e92f10623a 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 83022e2e56..307fc6ceec 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 1f8a537a56..ea972a3985 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 fcc0e36fa5..bdeafbc0df 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 8527c68c81..b81b42c07c 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 e74959fe22..e699fc47c0 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 bd1056e00d..fdd188ecfc 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 0000000000..840bd9aaa3
--- /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 3d2cc1a7f1..1f6c457e99 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 e5b0929f65..13c59413ec 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 3cf4195b5e..57170ce045 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 e191bfbd28..e8fb1ea413 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 8c87265668..d4ec2a291c 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 69939f06ac..75107c9a45 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 */
-
-static char usb_kbd_buffer[USB_KBD_BUFFER_LEN];
-static int usb_in_pointer;
-static int usb_out_pointer;
-
-unsigned char new[8];
-unsigned char old[8];
-int repeat_delay;
+#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)
+
+/* Size of the keyboard buffer */
+#define USB_KBD_BUFFER_LEN 0x20
+
+/* 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;
+
+ /* 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);
- 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;
+ /* 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;
+
+ data->usb_in_pointer++;
+ }
-/* 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_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;
- }
- 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);
+ return usb_kbd_irq_worker(dev);
+}
- if (new[i] > 3 && memscan(&old[2], new[i], 6) == &old[8])
- res |= usb_kbd_translate(new[i], new[0], 1);
- }
+/* 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[2] > 3) && (old[2] == new[2])) /* still pressed */
- res |= usb_kbd_translate(new[2], new[0], 2);
- if (res == 1)
- usb_kbd_setled(dev);
+/* 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;
+
+ 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
+ struct stdio_dev usb_kbd_dev, *old_dev;
+ struct usb_device *dev;
+ char *stdinname = getenv("stdin");
+ int error, i;
-/*
- * HID report descriptor collection item types
- */
+ /* 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_COLLECTION_PHYSICAL 0
-#define HID_COLLECTION_APPLICATION 1
-#define HID_COLLECTION_LOGICAL 2
-/*
- * HID report descriptor global item tags
- */
+ if (dev->devnum == -1)
+ continue;
-#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
+ /* Try probing the keyboard */
+ if (usb_kbd_probe(dev, 0) != 1)
+ continue;
-/*
- * HID report descriptor local item tags
- */
-
-#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;
+
+ /* Check if this is the standard input device. */
+ if (strcmp(stdinname, DEVNAME))
+ return 1;
-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");
-}
+ /* Reassign the console */
+ if (overwrite_console())
+ return 1;
+ error = console_assign(stdin, DEVNAME);
+ if (error)
+ return error;
-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;
-
- 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 cffd5a2b25..e2e87fef69 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 5e7271352c..64b091f4a1 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 9bb7e2e77a..5d7b638b38 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 0000000000..cdbdcfa91f
--- /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 e6ccd49ac0..0d3a9886f9 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 0000000000..3737e45231
--- /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 0000000000..5a3ac78ff2
--- /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(&reg->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, &reg->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**/
+ &reg->gusbcfg);
+
+ /* 3. Put the OTG device core in the disconnected state.*/
+ uTemp = readl(&reg->dctl);
+ uTemp |= SOFT_DISCONNECT;
+ writel(uTemp, &reg->dctl);
+
+ udelay(20);
+
+ /* 4. Make the OTG device core exit from the disconnected state.*/
+ uTemp = readl(&reg->dctl);
+ uTemp = uTemp & ~SOFT_DISCONNECT;
+ writel(uTemp, &reg->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, &reg->dcfg);
+
+ mdelay(1);
+
+ /* 6. Unmask the core interrupts*/
+ writel(GINTMSK_INIT, &reg->gintmsk);
+
+ /* 7. Set NAK bit of EP0, EP1, EP2*/
+ writel(DEPCTL_EPDIS|DEPCTL_SNAK, &reg->out_endp[EP0_CON].doepctl);
+ writel(DEPCTL_EPDIS|DEPCTL_SNAK, &reg->in_endp[EP0_CON].diepctl);
+
+ for (i = 1; i < S3C_MAX_ENDPOINTS; i++) {
+ writel(DEPCTL_EPDIS|DEPCTL_SNAK, &reg->out_endp[i].doepctl);
+ writel(DEPCTL_EPDIS|DEPCTL_SNAK, &reg->in_endp[i].diepctl);
+ }
+
+ /* 8. Unmask EPO interrupts*/
+ writel(((1 << EP0_CON) << DAINT_OUT_BIT)
+ | (1 << EP0_CON), &reg->daintmsk);
+
+ /* 9. Unmask device OUT EP common interrupts*/
+ writel(DOEPMSK_INIT, &reg->doepmsk);
+
+ /* 10. Unmask device IN EP common interrupts*/
+ writel(DIEPMSK_INIT, &reg->diepmsk);
+
+ /* 11. Set Rx FIFO Size (in 32-bit words) */
+ writel(RX_FIFO_SIZE >> 2, &reg->grxfsiz);
+
+ /* 12. Set Non Periodic Tx FIFO Size */
+ writel((NPTX_FIFO_SIZE >> 2) << 16 | ((RX_FIFO_SIZE >> 2)) << 0,
+ &reg->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,
+ &reg->dieptxf[i-1]);
+
+ /* Flush the RX FIFO */
+ writel(RX_FIFO_FLUSH, &reg->grstctl);
+ while (readl(&reg->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, &reg->grstctl);
+ writel(TX_FIFO_FLUSH_ALL | TX_FIFO_FLUSH, &reg->grstctl);
+ while (readl(&reg->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,
+ &reg->out_endp[EP0_CON].doepctl);
+
+ /* 14. Initialize OTG Link Core.*/
+ writel(GAHBCFG_INIT, &reg->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(&reg->in_endp[EP0_CON].diepctl);
+ writel(ep_ctrl|(0<<0), &reg->in_endp[EP0_CON].diepctl);
+
+ /* EP0 - Control OUT (64 bytes)*/
+ ep_ctrl = readl(&reg->out_endp[EP0_CON].doepctl);
+ writel(ep_ctrl|(0<<0), &reg->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(&reg->gintsts);
+ u32 gintmsk = readl(&reg->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 0000000000..255385750e
--- /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, &reg->in_endp[EP0_CON].diepdma);
+ writel(DIEPT_SIZ_PKT_CNT(1), &reg->in_endp[EP0_CON].dieptsiz);
+
+ ep_ctrl = readl(&reg->in_endp[EP0_CON].diepctl);
+ writel(ep_ctrl|DEPCTL_EPENA|DEPCTL_CNAK,
+ &reg->in_endp[EP0_CON].diepctl);
+
+ DEBUG_EP0("%s:EP0 ZLP DIEPCTL0 = 0x%x\n",
+ __func__, readl(&reg->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),
+ &reg->out_endp[EP0_CON].doeptsiz);
+ writel(usb_ctrl_dma_addr, &reg->out_endp[EP0_CON].doepdma);
+
+ ep_ctrl = readl(&reg->out_endp[EP0_CON].doepctl);
+ writel(ep_ctrl|DEPCTL_EPENA, &reg->out_endp[EP0_CON].doepctl);
+
+ DEBUG_EP0("%s:EP0 ZLP DIEPCTL0 = 0x%x\n",
+ __func__, readl(&reg->in_endp[EP0_CON].diepctl));
+ DEBUG_EP0("%s:EP0 ZLP DOEPCTL0 = 0x%x\n",
+ __func__, readl(&reg->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(&reg->in_endp[EP0_CON].diepctl));
+ DEBUG_EP0("%s:EP0 ZLP DOEPCTL0 = 0x%x\n",
+ __func__, readl(&reg->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),
+ &reg->out_endp[EP0_CON].doeptsiz);
+ writel(usb_ctrl_dma_addr, &reg->out_endp[EP0_CON].doepdma);
+
+ ep_ctrl = readl(&reg->out_endp[EP0_CON].doepctl);
+ writel(ep_ctrl|DEPCTL_EPENA|DEPCTL_CNAK,
+ &reg->out_endp[EP0_CON].doepctl);
+
+ DEBUG_EP0("%s:EP0 ZLP DIEPCTL0 = 0x%x\n",
+ __func__, readl(&reg->in_endp[EP0_CON].diepctl));
+ DEBUG_EP0("%s:EP0 ZLP DOEPCTL0 = 0x%x\n",
+ __func__, readl(&reg->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(&reg->out_endp[ep_num].doepctl);
+
+ writel(the_controller->dma_addr[ep_index(ep)+1],
+ &reg->out_endp[ep_num].doepdma);
+ writel(DOEPT_SIZ_PKT_CNT(pktcnt) | DOEPT_SIZ_XFER_SIZE(length),
+ &reg->out_endp[ep_num].doeptsiz);
+ writel(DEPCTL_EPENA|DEPCTL_CNAK|ctrl, &reg->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(&reg->out_endp[ep_num].doepdma),
+ readl(&reg->out_endp[ep_num].doeptsiz),
+ readl(&reg->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), &reg->grstctl);
+ writel(TX_FIFO_NUMBER(ep->fifo_num) | TX_FIFO_FLUSH, &reg->grstctl);
+ while (readl(&reg->grstctl) & TX_FIFO_FLUSH)
+ ;
+
+ writel(the_controller->dma_addr[ep_index(ep)+1],
+ &reg->in_endp[ep_num].diepdma);
+ writel(DIEPT_SIZ_PKT_CNT(pktcnt) | DIEPT_SIZ_XFER_SIZE(length),
+ &reg->in_endp[ep_num].dieptsiz);
+
+ ctrl = readl(&reg->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, &reg->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(&reg->in_endp[ep_num].diepdma),
+ readl(&reg->in_endp[ep_num].dieptsiz),
+ readl(&reg->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(&reg->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(&reg->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(&reg->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(&reg->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, &reg->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(&reg->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(&reg->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, &reg->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(&reg->gintsts);
+ gintmsk = readl(&reg->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(&reg->daint), readl(&reg->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, &reg->gintsts);
+ usb_status = (readl(&reg->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, &reg->gintsts);
+ }
+
+ if (intr_status & INT_SUSPEND) {
+ usb_status = readl(&reg->dsts);
+ DEBUG_ISR("\tSuspend interrupt :(DSTS):0x%x\n", usb_status);
+ writel(INT_SUSPEND, &reg->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, &reg->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(&reg->gotgctl);
+ DEBUG_ISR("\tReset interrupt - (GOTGCTL):0x%x\n", usb_status);
+ writel(INT_RESET, &reg->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(&reg->gintsts);
+ DEBUG_IN_EP("%s: ep_is_in, S3C_UDC_OTG_GINTSTS=0x%x\n",
+ __func__, gintsts);
+
+ setdma_tx(ep, req);
+ } else {
+ gintsts = readl(&reg->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(&reg->dcfg);
+ writel(DEVICE_ADDRESS(address) | ctrl, &reg->dcfg);
+
+ s3c_udc_ep0_zlp(dev);
+
+ DEBUG_EP0("%s: USB OTG 2.0 Device address=%d, DCFG=0x%x\n",
+ __func__, address, readl(&reg->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(&reg->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, &reg->in_endp[EP0_CON].diepctl);
+
+ DEBUG_EP0("%s: set ep%d stall, DIEPCTL0 = 0x%x\n",
+ __func__, ep_index(ep), &reg->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], &reg->in_endp[EP0_CON].diepdma);
+ writel(DIEPT_SIZ_PKT_CNT(1) | DIEPT_SIZ_XFER_SIZE(2),
+ &reg->in_endp[EP0_CON].dieptsiz);
+
+ ep_ctrl = readl(&reg->in_endp[EP0_CON].diepctl);
+ writel(ep_ctrl|DEPCTL_EPENA|DEPCTL_CNAK,
+ &reg->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(&reg->in_endp[ep_num].diepctl);
+ ep_ctrl |= DEPCTL_SNAK;
+ writel(ep_ctrl, &reg->in_endp[ep_num].diepctl);
+ DEBUG("%s: set NAK, DIEPCTL%d = 0x%x\n",
+ __func__, ep_num, readl(&reg->in_endp[ep_num].diepctl));
+ } else {
+ ep_ctrl = readl(&reg->out_endp[ep_num].doepctl);
+ ep_ctrl |= DEPCTL_SNAK;
+ writel(ep_ctrl, &reg->out_endp[ep_num].doepctl);
+ DEBUG("%s: set NAK, DOEPCTL%d = 0x%x\n",
+ __func__, ep_num, readl(&reg->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(&reg->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, &reg->in_endp[ep_num].diepctl);
+ DEBUG("%s: set stall, DIEPCTL%d = 0x%x\n",
+ __func__, ep_num, readl(&reg->in_endp[ep_num].diepctl));
+
+ } else {
+ ep_ctrl = readl(&reg->out_endp[ep_num].doepctl);
+
+ /* set the stall bit */
+ ep_ctrl |= DEPCTL_STALL;
+
+ writel(ep_ctrl, &reg->out_endp[ep_num].doepctl);
+ DEBUG("%s: set stall, DOEPCTL%d = 0x%x\n",
+ __func__, ep_num, readl(&reg->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(&reg->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, &reg->in_endp[ep_num].diepctl);
+ DEBUG("%s: cleared stall, DIEPCTL%d = 0x%x\n",
+ __func__, ep_num, readl(&reg->in_endp[ep_num].diepctl));
+
+ } else {
+ ep_ctrl = readl(&reg->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, &reg->out_endp[ep_num].doepctl);
+ DEBUG("%s: cleared stall, DOEPCTL%d = 0x%x\n",
+ __func__, ep_num, readl(&reg->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(&reg->in_endp[ep_num].diepctl);
+ daintmsk = 1 << ep_num;
+ } else {
+ ep_ctrl = readl(&reg->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, &reg->in_endp[ep_num].diepctl);
+ DEBUG("%s: USB Ative EP%d, DIEPCTRL%d = 0x%x\n",
+ __func__, ep_num, ep_num,
+ readl(&reg->in_endp[ep_num].diepctl));
+ } else {
+ writel(ep_ctrl, &reg->out_endp[ep_num].doepctl);
+ DEBUG("%s: USB Ative EP%d, DOEPCTRL%d = 0x%x\n",
+ __func__, ep_num, ep_num,
+ readl(&reg->out_endp[ep_num].doepctl));
+ }
+ }
+
+ /* Unmask EP Interrtupt */
+ writel(readl(&reg->daintmsk)|daintmsk, &reg->daintmsk);
+ DEBUG("%s: DAINTMSK = 0x%x\n", __func__, readl(&reg->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 09abb754d4..77e217f34f 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 2197119cf7..3f7bc2cef6 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, &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 0000000000..68a673e721
--- /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 60c9595268..cf906b47ca 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 0000000000..d43b2293e1
--- /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 0000000000..fa2e004a6c
--- /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 0000000000..805e29d76c
--- /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 2b069d6b7a..522487b395 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 dd53f48b9a..3b18a18d3e 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 537649ee16..34a4edd41e 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 3434de7f77..56b5547fd2 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 67600ed522..28693020ec 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 0000000000..9b315118d5
--- /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 0000000000..51d36c3f34
--- /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 0000000000..14dadc8b05
--- /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 0000000000..d871290871
--- /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 2dec3b93d6..392fcf5378 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;
OpenPOWER on IntegriCloud