[Armadeus-commitlog] armadeus branch, master, updated. release-3.2-210-gbfc9119
Brought to you by:
sszy
|
From: Julien B a. A. <ar...@us...> - 2010-02-22 14:27:18
|
This is an automated email from the git hooks/post-receive script. It was
generated because a ref change was pushed to the repository containing
the project "armadeus".
The branch, master has been updated
via bfc9119170dad9ba1bed11670101be21e47517d3 (commit)
via 261a630daa5615b4212bfeb4b6198c5140e5e70a (commit)
via b888e0ff353290ac0fad5af8624369c8c85fc610 (commit)
via b6751f2cb5ed7e398fd6fcf330e00fae589ae54e (commit)
from 7466ac2121ad8cb5b150a0c3298568f848e90ec9 (commit)
Those revisions listed above that are new to this repository have
not appeared on any other notification email; so we list those
revisions in full, below.
- Log -----------------------------------------------------------------
commit bfc9119170dad9ba1bed11670101be21e47517d3
Merge: 261a630daa5615b4212bfeb4b6198c5140e5e70a 7466ac2121ad8cb5b150a0c3298568f848e90ec9
Author: Julien Boibessot <jul...@ar...>
Date: Mon Feb 22 15:26:19 2010 +0100
Merge branch 'master' of ssh://artemys@armadeus.git.sourceforge.net/gitroot/armadeus/armadeus
commit 261a630daa5615b4212bfeb4b6198c5140e5e70a
Author: Julien Boibessot <jul...@ar...>
Date: Mon Feb 22 15:26:02 2010 +0100
[LINUX] Add (preliminary) support for USB device functionnality of APF27 module
commit b888e0ff353290ac0fad5af8624369c8c85fc610
Merge: b6751f2cb5ed7e398fd6fcf330e00fae589ae54e 663489491607550291d1d6f795b74f6177786c56
Author: Julien Boibessot <jul...@ar...>
Date: Thu Feb 18 11:36:16 2010 +0100
Merge branch 'master' of ssh://artemys@armadeus.git.sourceforge.net/gitroot/armadeus/armadeus
commit b6751f2cb5ed7e398fd6fcf330e00fae589ae54e
Author: Julien Boibessot <jul...@ar...>
Date: Tue Feb 16 12:05:45 2010 +0100
[LINUX] Comment some 2.6.29 patches
-----------------------------------------------------------------------
Summary of changes:
buildroot/local/apf27_test/linux-2.6.29.4.config | 36 +-
.../armadeus/apf27/apf27-linux-2.6.29.config | 36 +-
.../2.6.29/281-apf27-armadeus-add_apf27.c.patch | 130 +-
.../287-apf27-armadeus-fix_usb_otg_int.patch | 2 +
.../296-apf27-armadeus-imx27-usb-host1.patch | 5 +
..._iMX3x_support_to_the_fsl_usb2_udc_driver.patch | 5154 ++++++++++++++++++++
...dc_core-fix_kernel_oops_on_module_removal.patch | 52 +
.../355c-armadeus-mx2-devices-add_usb_device.patch | 84 +
...rmadeus-fsl_udc-add_platform_init_support.patch | 67 +
9 files changed, 5528 insertions(+), 38 deletions(-)
create mode 100644 buildroot/target/device/armadeus/linux/kernel-patches/2.6.29/355a-denx-fsl_udc-Add_iMX3x_support_to_the_fsl_usb2_udc_driver.patch
create mode 100644 buildroot/target/device/armadeus/linux/kernel-patches/2.6.29/355b-montavista-fsl_udc_core-fix_kernel_oops_on_module_removal.patch
create mode 100644 buildroot/target/device/armadeus/linux/kernel-patches/2.6.29/355c-armadeus-mx2-devices-add_usb_device.patch
create mode 100644 buildroot/target/device/armadeus/linux/kernel-patches/2.6.29/355d-armadeus-fsl_udc-add_platform_init_support.patch
diff --git a/buildroot/local/apf27_test/linux-2.6.29.4.config b/buildroot/local/apf27_test/linux-2.6.29.4.config
index bfe8814..58fd748 100644
--- a/buildroot/local/apf27_test/linux-2.6.29.4.config
+++ b/buildroot/local/apf27_test/linux-2.6.29.4.config
@@ -1,7 +1,7 @@
#
# Automatically generated make config: don't edit
# Linux kernel version: 2.6.29.6
-# Mon Dec 14 18:25:57 2009
+# Mon Feb 22 13:40:04 2010
#
CONFIG_ARM=y
CONFIG_SYS_SUPPORTS_APM_EMULATION=y
@@ -1323,6 +1323,7 @@ CONFIG_USB_EHCI_MXC=y
# CONFIG_USB_SL811_HCD is not set
# CONFIG_USB_R8A66597_HCD is not set
# CONFIG_USB_HWA_HCD is not set
+# CONFIG_USB_GADGET_MUSB_HDRC is not set
#
# USB Device Class drivers
@@ -1390,7 +1391,38 @@ CONFIG_USB_STORAGE=y
# CONFIG_USB_TEST is not set
# CONFIG_USB_ISIGHTFW is not set
# CONFIG_USB_VST is not set
-# CONFIG_USB_GADGET is not set
+CONFIG_USB_GADGET=y
+CONFIG_USB_GADGET_DEBUG_FILES=y
+# CONFIG_USB_GADGET_DEBUG_FS is not set
+CONFIG_USB_GADGET_VBUS_DRAW=2
+CONFIG_USB_GADGET_SELECTED=y
+# CONFIG_USB_GADGET_AT91 is not set
+# CONFIG_USB_GADGET_ATMEL_USBA is not set
+CONFIG_USB_GADGET_FSL_USB2=y
+CONFIG_USB_FSL_USB2=y
+# CONFIG_USB_GADGET_LH7A40X is not set
+# CONFIG_USB_GADGET_OMAP is not set
+# CONFIG_USB_GADGET_PXA25X is not set
+# CONFIG_USB_GADGET_PXA27X is not set
+# CONFIG_USB_GADGET_S3C2410 is not set
+# CONFIG_USB_GADGET_IMX is not set
+# CONFIG_USB_GADGET_M66592 is not set
+# CONFIG_USB_GADGET_AMD5536UDC is not set
+# CONFIG_USB_GADGET_FSL_QE is not set
+# CONFIG_USB_GADGET_CI13XXX is not set
+# CONFIG_USB_GADGET_NET2280 is not set
+# CONFIG_USB_GADGET_GOKU is not set
+# CONFIG_USB_GADGET_DUMMY_HCD is not set
+CONFIG_USB_GADGET_DUALSPEED=y
+# CONFIG_USB_ZERO is not set
+CONFIG_USB_ETH=m
+CONFIG_USB_ETH_RNDIS=y
+CONFIG_USB_GADGETFS=m
+# CONFIG_USB_FILE_STORAGE is not set
+CONFIG_USB_G_SERIAL=m
+# CONFIG_USB_MIDI_GADGET is not set
+# CONFIG_USB_G_PRINTER is not set
+# CONFIG_USB_CDC_COMPOSITE is not set
#
# OTG and related infrastructure
diff --git a/buildroot/target/device/armadeus/apf27/apf27-linux-2.6.29.config b/buildroot/target/device/armadeus/apf27/apf27-linux-2.6.29.config
index dc5c9ad..a2239f2 100644
--- a/buildroot/target/device/armadeus/apf27/apf27-linux-2.6.29.config
+++ b/buildroot/target/device/armadeus/apf27/apf27-linux-2.6.29.config
@@ -1,7 +1,7 @@
#
# Automatically generated make config: don't edit
# Linux kernel version: 2.6.29.6
-# Thu Dec 17 12:58:12 2009
+# Mon Feb 22 13:40:04 2010
#
CONFIG_ARM=y
CONFIG_SYS_SUPPORTS_APM_EMULATION=y
@@ -1320,6 +1320,7 @@ CONFIG_USB_EHCI_MXC=y
# CONFIG_USB_SL811_HCD is not set
# CONFIG_USB_R8A66597_HCD is not set
# CONFIG_USB_HWA_HCD is not set
+# CONFIG_USB_GADGET_MUSB_HDRC is not set
#
# USB Device Class drivers
@@ -1387,7 +1388,38 @@ CONFIG_USB_STORAGE=y
# CONFIG_USB_TEST is not set
# CONFIG_USB_ISIGHTFW is not set
# CONFIG_USB_VST is not set
-# CONFIG_USB_GADGET is not set
+CONFIG_USB_GADGET=y
+CONFIG_USB_GADGET_DEBUG_FILES=y
+# CONFIG_USB_GADGET_DEBUG_FS is not set
+CONFIG_USB_GADGET_VBUS_DRAW=2
+CONFIG_USB_GADGET_SELECTED=y
+# CONFIG_USB_GADGET_AT91 is not set
+# CONFIG_USB_GADGET_ATMEL_USBA is not set
+CONFIG_USB_GADGET_FSL_USB2=y
+CONFIG_USB_FSL_USB2=y
+# CONFIG_USB_GADGET_LH7A40X is not set
+# CONFIG_USB_GADGET_OMAP is not set
+# CONFIG_USB_GADGET_PXA25X is not set
+# CONFIG_USB_GADGET_PXA27X is not set
+# CONFIG_USB_GADGET_S3C2410 is not set
+# CONFIG_USB_GADGET_IMX is not set
+# CONFIG_USB_GADGET_M66592 is not set
+# CONFIG_USB_GADGET_AMD5536UDC is not set
+# CONFIG_USB_GADGET_FSL_QE is not set
+# CONFIG_USB_GADGET_CI13XXX is not set
+# CONFIG_USB_GADGET_NET2280 is not set
+# CONFIG_USB_GADGET_GOKU is not set
+# CONFIG_USB_GADGET_DUMMY_HCD is not set
+CONFIG_USB_GADGET_DUALSPEED=y
+# CONFIG_USB_ZERO is not set
+CONFIG_USB_ETH=m
+CONFIG_USB_ETH_RNDIS=y
+CONFIG_USB_GADGETFS=m
+# CONFIG_USB_FILE_STORAGE is not set
+CONFIG_USB_G_SERIAL=m
+# CONFIG_USB_MIDI_GADGET is not set
+# CONFIG_USB_G_PRINTER is not set
+# CONFIG_USB_CDC_COMPOSITE is not set
#
# OTG and related infrastructure
diff --git a/buildroot/target/device/armadeus/linux/kernel-patches/2.6.29/281-apf27-armadeus-add_apf27.c.patch b/buildroot/target/device/armadeus/linux/kernel-patches/2.6.29/281-apf27-armadeus-add_apf27.c.patch
index 782a065..c88a75b 100644
--- a/buildroot/target/device/armadeus/linux/kernel-patches/2.6.29/281-apf27-armadeus-add_apf27.c.patch
+++ b/buildroot/target/device/armadeus/linux/kernel-patches/2.6.29/281-apf27-armadeus-add_apf27.c.patch
@@ -9,8 +9,8 @@ Signed-off-by: Eric Jarrige <eri...@ar...>
Index: linux-2.6.29.6/arch/arm/mach-mx2/apf27.c
===================================================================
--- /dev/null 1970-01-01 00:00:00.000000000 +0000
-+++ linux-2.6.29.6/arch/arm/mach-mx2/apf27.c 2010-01-21 11:06:41.000000000 +0100
-@@ -0,0 +1,727 @@
++++ linux-2.6.29.6/arch/arm/mach-mx2/apf27.c 2010-02-22 14:38:52.000000000 +0100
+@@ -0,0 +1,789 @@
+ /*
+ * apf27.c
+ *
@@ -62,6 +62,7 @@ Index: linux-2.6.29.6/arch/arm/mach-mx2/apf27.c
+#include <mach/mxc_nand.h>
+#include <mach/mxc_ehci.h>
+#include <mach/ulpi.h>
++#include <linux/fsl_devices.h> /* USB device */
+#include "../../../drivers/armadeus/pwm/pwm.h"
+#if defined(CONFIG_CPU_FREQ_IMX27) || defined(CONFIG_CPU_FREQ_IMX27_MODULE)
+#include "cpufreq_imx.h"
@@ -242,8 +243,7 @@ Index: linux-2.6.29.6/arch/arm/mach-mx2/apf27.c
+#endif /* CONFIG_FEC_OLD */
+
+
-+/* APF27 has an onboard PHY for OTG USB port */
-+#ifdef CONFIG_USB_EHCI_MXC
++/* APF27 has an onboard PHY (ISP1504) on OTG USB port */
+static int mxc_usbotg_pins[] = {
+ PE0_PF_USBOTG_NXT,
+ PE1_PF_USBOTG_STP,
@@ -259,9 +259,9 @@ Index: linux-2.6.29.6/arch/arm/mach-mx2/apf27.c
+ PC13_PF_USBOTG_DATA3,
+};
+
-+static int isp1504_set_vbus_power(void __iomem *view, int on)
++static int isp1504_check_presence(void __iomem *view)
+{
-+ int vid, pid, ret = 0;
++ int vid, pid;
+
+ vid = (ulpi_read(ISP1504_VID_HIGH, view) << 8) |
+ ulpi_read(ISP1504_VID_LOW, view);
@@ -274,6 +274,17 @@ Index: linux-2.6.29.6/arch/arm/mach-mx2/apf27.c
+ return -1;
+ }
+
++ return 0;
++}
++
++static int isp1504_set_vbus_power(void __iomem *view, int on)
++{
++ int ret = 0;
++
++ ret = isp1504_check_presence(view);
++ if (ret)
++ return ret;
++
+ if (on) {
+ /* force disconnection of the DM/DP signals. This
+ avoid powering the connected device through these lines */
@@ -300,6 +311,19 @@ Index: linux-2.6.29.6/arch/arm/mach-mx2/apf27.c
+ return ret;
+}
+
++static int isp1504_set_device(void __iomem *view)
++{
++ int ret = 0;
++
++ ret = isp1504_check_presence(view);
++ if (ret)
++ return ret;
++
++ ret = ulpi_clear(0x06, ISP1504_OTGCTL, view);
++
++ return ret;
++}
++
+static void isp1504_suspend(void __iomem *view)
+{
+ /* isp1504_set_vbus_power(view, 0); needed ? */
@@ -308,61 +332,97 @@ Index: linux-2.6.29.6/arch/arm/mach-mx2/apf27.c
+
+static void isp1504_resume(void __iomem *view)
+{
-+ /* uggly but the STP line must be set to 1 before the first PHY access
++ /* uggly but the STP line must be set to 1 before the first PHY access
+ in order to wake up the ULPI clock of the PHY. */
+ gpio_set_value(GPIO_PORTE | 1, 1);
+ /* deassert STP (gpio mode) */
+ mxc_gpio_mode(GPIO_PORTE | 1 | GPIO_OUT | GPIO_GPIO);
+ mdelay(1);
-+ /* STP now managed by the USB host ctrl */
++ /* STP now managed by the USB host ctrl */
+ mxc_gpio_mode(PE1_PF_USBOTG_STP);
+}
+
++#define UOG_USBCMD 0x140
++#define UOG_ULPIVIEW 0x170
++#define UOG_PORTSC1 0x184
++#define USB_CTRL 0x600
++#define PORTSC_PTS_MASK (3 << 30)
++#define PORTSC_PTS_ULPI (2 << 30)
++
++static void apf27_usbotg_reset(void)
++{
++ uint32_t temp;
++
++ temp = readl(IO_ADDRESS(OTG_BASE_ADDR) + UOG_USBCMD);
++ temp |= 0x02;
++ writel(temp, IO_ADDRESS(OTG_BASE_ADDR) + UOG_USBCMD);
++ pr_debug("reset controller: 0x%08x\n", temp);
++ mdelay(50);
++}
++
++static int otg_mode_host = 1;
++
++static int __init apf27_otg_mode(char *options)
++{
++ if (!strcmp(options, "host"))
++ otg_mode_host = 1;
++ else if (!strcmp(options, "device"))
++ otg_mode_host = 0;
++ else {
++ pr_info("otg_mode neither \"host\" nor \"device\". "
++ "Defaulting to host\n");
++ }
++
++ return 0;
++}
++__setup("otg_mode=", apf27_otg_mode);
++
+static int apf27_usbotg_init(struct platform_device *pdev)
+{
+ int ret;
+ uint32_t temp;
+
+ ret = mxc_gpio_setup_multiple_pins(mxc_usbotg_pins,
-+ ARRAY_SIZE(mxc_usbotg_pins), "usbotg");
++ ARRAY_SIZE(mxc_usbotg_pins), "USB OTG");
+ if (ret)
+ return ret;
+
-+ temp = readl(IO_ADDRESS(OTG_BASE_ADDR) + 0x600);
-+ pr_debug("USB_CTRL before: 0x%08x\n", temp);
++ /* Tweak power/wake up handling */
++ temp = readl(IO_ADDRESS(OTG_BASE_ADDR) + USB_CTRL);
+ temp &= ~( (3 << 29) | 1 );
+ temp |= (1 << 24) | (1 << 27) | (1 << 28);
-+ writel(temp, IO_ADDRESS(OTG_BASE_ADDR) + 0x600);
-+ pr_debug("USB_CTRL after: 0x%08x\n", temp);
-+
-+ temp = readl(IO_ADDRESS(OTG_BASE_ADDR) + 0x184);
-+ pr_debug("PORTSC1 before: 0x%08x\n", temp);
-+ temp &= ~(3 << 30);
-+ temp |= 2 << 30;
-+ writel(temp, IO_ADDRESS(OTG_BASE_ADDR) + 0x184);
-+ pr_debug("PORTSC1 after: 0x%08x\n", temp);
++ writel(temp, IO_ADDRESS(OTG_BASE_ADDR) + USB_CTRL);
+
++ /* Select ULPI PHY */
++ temp = readl(IO_ADDRESS(OTG_BASE_ADDR) + UOG_PORTSC1);
++ temp = (temp & ~PORTSC_PTS_MASK) | PORTSC_PTS_ULPI;
++ writel(temp, IO_ADDRESS(OTG_BASE_ADDR) + UOG_PORTSC1);
+ mdelay(10);
+
-+ temp = readl(IO_ADDRESS(OTG_BASE_ADDR) + 0x140);
-+ temp |= 0x02; /* reset controller */
-+ writel(temp, IO_ADDRESS(OTG_BASE_ADDR) + 0x140);
-+ pr_debug("reset controller: 0x%08x\n", temp);
-+ mdelay(50);
++ apf27_usbotg_reset();
++
++ if (otg_mode_host) {
++ ret = isp1504_set_vbus_power((void __iomem *)(IO_ADDRESS(OTG_BASE_ADDR + UOG_ULPIVIEW)), 1);
++ } else {
++ ret = isp1504_set_device((void __iomem *)(IO_ADDRESS(OTG_BASE_ADDR + UOG_ULPIVIEW)));
++ }
+
+ if (ret)
+ mxc_gpio_release_multiple_pins(mxc_usbotg_pins,
+ ARRAY_SIZE(mxc_usbotg_pins));
+
-+ ret = isp1504_set_vbus_power((void __iomem *)(IO_ADDRESS(OTG_BASE_ADDR + 0x170)), 1);
-+
+ return ret;
+}
+
+struct mxc_usb2_platform_data ehci0_pdata = {
+ .init = apf27_usbotg_init,
+};
-+#endif /* CONFIG_USB_EHCI_MXC */
++
++static struct fsl_usb2_platform_data otg_device_pdata = {
++ .init = apf27_usbotg_init,
++ .operating_mode = FSL_USB2_DR_DEVICE,
++ .phy_mode = FSL_USB2_PHY_ULPI,
++};
+
+
+/* APF27 frequency scaling stuff */
@@ -684,9 +744,11 @@ Index: linux-2.6.29.6/arch/arm/mach-mx2/apf27.c
+#ifdef CONFIG_WATCHDOG
+ mxc_register_device(&mxc_wdt, NULL);
+#endif
-+#ifdef CONFIG_USB_EHCI_MXC
-+ mxc_register_device(&mxc_otg, &ehci0_pdata);
-+#endif
++ if (otg_mode_host) {
++ mxc_register_device(&mxc_otg_host, &ehci0_pdata);
++ } else {
++ mxc_register_device(&mxc_otg_udc_device, &otg_device_pdata);
++ }
+#ifdef CONFIG_I2C
+ mxc_register_device(&imx_i2c_device1, &apf27_i2c_1_data);
+ i2c_register_board_info(1, apf27_i2c_devices_bus1,
@@ -741,7 +803,7 @@ Index: linux-2.6.29.6/arch/arm/mach-mx2/apf27.c
Index: linux-2.6.29.6/arch/arm/mach-mx2/apf27-dev.c
===================================================================
--- /dev/null 1970-01-01 00:00:00.000000000 +0000
-+++ linux-2.6.29.6/arch/arm/mach-mx2/apf27-dev.c 2010-01-26 22:31:08.000000000 +0100
++++ linux-2.6.29.6/arch/arm/mach-mx2/apf27-dev.c 2010-02-22 14:38:13.000000000 +0100
@@ -0,0 +1,1019 @@
+ /*
+ * apf27-dev.c
@@ -1765,7 +1827,7 @@ Index: linux-2.6.29.6/arch/arm/mach-mx2/apf27-dev.c
Index: linux-2.6.29.6/arch/arm/mach-mx2/apf27-lcd.c
===================================================================
--- /dev/null 1970-01-01 00:00:00.000000000 +0000
-+++ linux-2.6.29.6/arch/arm/mach-mx2/apf27-lcd.c 2010-01-21 11:06:41.000000000 +0100
++++ linux-2.6.29.6/arch/arm/mach-mx2/apf27-lcd.c 2010-02-16 13:17:29.000000000 +0100
@@ -0,0 +1,220 @@
+/*
+ * apf27-lcd.c
@@ -1990,7 +2052,7 @@ Index: linux-2.6.29.6/arch/arm/mach-mx2/apf27-lcd.c
Index: linux-2.6.29.6/arch/arm/mach-mx2/apf27-dev-ext-apw.c
===================================================================
--- /dev/null 1970-01-01 00:00:00.000000000 +0000
-+++ linux-2.6.29.6/arch/arm/mach-mx2/apf27-dev-ext-apw.c 2010-01-26 19:01:12.000000000 +0100
++++ linux-2.6.29.6/arch/arm/mach-mx2/apf27-dev-ext-apw.c 2010-02-16 13:17:29.000000000 +0100
@@ -0,0 +1,201 @@
+ /*
+ * apf27-dev-ext-apw.c
diff --git a/buildroot/target/device/armadeus/linux/kernel-patches/2.6.29/287-apf27-armadeus-fix_usb_otg_int.patch b/buildroot/target/device/armadeus/linux/kernel-patches/2.6.29/287-apf27-armadeus-fix_usb_otg_int.patch
index ff2ffd3..0ed7e65 100644
--- a/buildroot/target/device/armadeus/linux/kernel-patches/2.6.29/287-apf27-armadeus-fix_usb_otg_int.patch
+++ b/buildroot/target/device/armadeus/linux/kernel-patches/2.6.29/287-apf27-armadeus-fix_usb_otg_int.patch
@@ -1,5 +1,7 @@
Interrupt number of USB OTG module is not correct on MX2.
+Integrated in latest kernels.
+
Signed-off-by: Eric Jarrige <eri...@ar...>
Index: linux-2.6.29/arch/arm/mach-mx2/devices.c
diff --git a/buildroot/target/device/armadeus/linux/kernel-patches/2.6.29/296-apf27-armadeus-imx27-usb-host1.patch b/buildroot/target/device/armadeus/linux/kernel-patches/2.6.29/296-apf27-armadeus-imx27-usb-host1.patch
index b17a46a..f561cb7 100644
--- a/buildroot/target/device/armadeus/linux/kernel-patches/2.6.29/296-apf27-armadeus-imx27-usb-host1.patch
+++ b/buildroot/target/device/armadeus/linux/kernel-patches/2.6.29/296-apf27-armadeus-imx27-usb-host1.patch
@@ -1,3 +1,8 @@
+Add USB Host 1 ressources + correct ID of USB Host 2
+
+Integrated in latest kernels with different names for platform devices.
+Ex: mxc_ehci1 -> mxc_usbh1
+
Index: linux-2.6.29.4/arch/arm/mach-mx2/devices.c
===================================================================
--- linux-2.6.29.4.orig/arch/arm/mach-mx2/devices.c 2009-06-04 11:30:50.000000000 +0200
diff --git a/buildroot/target/device/armadeus/linux/kernel-patches/2.6.29/355a-denx-fsl_udc-Add_iMX3x_support_to_the_fsl_usb2_udc_driver.patch b/buildroot/target/device/armadeus/linux/kernel-patches/2.6.29/355a-denx-fsl_udc-Add_iMX3x_support_to_the_fsl_usb2_udc_driver.patch
new file mode 100644
index 0000000..447dde9
--- /dev/null
+++ b/buildroot/target/device/armadeus/linux/kernel-patches/2.6.29/355a-denx-fsl_udc-Add_iMX3x_support_to_the_fsl_usb2_udc_driver.patch
@@ -0,0 +1,5154 @@
+From 54e4026b64a970303349b952866641a7804ef594 Mon Sep 17 00:00:00 2001
+From: Guennadi Liakhovetski <lg...@de...>
+Date: Wed, 15 Apr 2009 14:25:33 +0200
+Subject: [PATCH] USB: gadget: Add i.MX3x support to the fsl_usb2_udc driver
+
+This patch adds support for i.MX3x (only tested with i.MX31 so far) ARM
+SoCs to the fsl_usb2_udc driver. It also moves PHY configuration before
+controller reset, because otherwise an ULPI PHY doesn't get a reset and
+doesn't function after a reboot. The problem with longer control transfers
+is still not fixed. The patch renames the fsl_usb2_udc.c file to
+fsl_udc_core.c to preserve the same module name for user-space
+backwards compatibility.
+
+Signed-off-by: Guennadi Liakhovetski <lg...@de...>
+Signed-off-by: Greg Kroah-Hartman <gr...@su...>
+---
+ drivers/usb/gadget/Kconfig | 2 +-
+ drivers/usb/gadget/Makefile | 4 +
+ drivers/usb/gadget/fsl_mx3_udc.c | 95 ++
+ drivers/usb/gadget/fsl_udc_core.c | 2491 +++++++++++++++++++++++++++++++++++++
+ drivers/usb/gadget/fsl_usb2_udc.c | 2468 ------------------------------------
+ drivers/usb/gadget/fsl_usb2_udc.h | 18 +
+ 6 files changed, 2609 insertions(+), 2469 deletions(-)
+ create mode 100644 drivers/usb/gadget/fsl_mx3_udc.c
+ create mode 100644 drivers/usb/gadget/fsl_udc_core.c
+ delete mode 100644 drivers/usb/gadget/fsl_usb2_udc.c
+
+diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig
+index 772dd3b..5de9b4f 100644
+--- a/drivers/usb/gadget/Kconfig
++++ b/drivers/usb/gadget/Kconfig
+@@ -156,7 +156,7 @@ config USB_ATMEL_USBA
+
+ config USB_GADGET_FSL_USB2
+ boolean "Freescale Highspeed USB DR Peripheral Controller"
+- depends on FSL_SOC
++ depends on FSL_SOC || ARCH_MXC
+ select USB_GADGET_DUALSPEED
+ help
+ Some of Freescale PowerPC processors have a High Speed
+diff --git a/drivers/usb/gadget/Makefile b/drivers/usb/gadget/Makefile
+index 39a51d7..c3fe063 100644
+--- a/drivers/usb/gadget/Makefile
++++ b/drivers/usb/gadget/Makefile
+@@ -18,6 +18,10 @@ obj-$(CONFIG_USB_S3C2410) += s3c2410_udc.o
+ obj-$(CONFIG_USB_AT91) += at91_udc.o
+ obj-$(CONFIG_USB_ATMEL_USBA) += atmel_usba_udc.o
+ obj-$(CONFIG_USB_FSL_USB2) += fsl_usb2_udc.o
++fsl_usb2_udc-objs := fsl_udc_core.o
++ifeq ($(CONFIG_ARCH_MXC),y)
++fsl_usb2_udc-objs += fsl_mx3_udc.o
++endif
+ obj-$(CONFIG_USB_M66592) += m66592-udc.o
+ obj-$(CONFIG_USB_FSL_QE) += fsl_qe_udc.o
+ obj-$(CONFIG_USB_CI13XXX) += ci13xxx_udc.o
+diff --git a/drivers/usb/gadget/fsl_mx3_udc.c b/drivers/usb/gadget/fsl_mx3_udc.c
+new file mode 100644
+index 0000000..4bc2bf3
+--- /dev/null
++++ b/drivers/usb/gadget/fsl_mx3_udc.c
+@@ -0,0 +1,95 @@
++/*
++ * Copyright (C) 2009
++ * Guennadi Liakhovetski, DENX Software Engineering, <lg...@de...>
++ *
++ * Description:
++ * Helper routines for i.MX3x SoCs from Freescale, needed by the fsl_usb2_udc.c
++ * driver to function correctly on these systems.
++ *
++ * 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.
++ */
++#include <linux/clk.h>
++#include <linux/delay.h>
++#include <linux/err.h>
++#include <linux/fsl_devices.h>
++#include <linux/platform_device.h>
++
++static struct clk *mxc_ahb_clk;
++static struct clk *mxc_usb_clk;
++
++int fsl_udc_clk_init(struct platform_device *pdev)
++{
++ struct fsl_usb2_platform_data *pdata;
++ unsigned long freq;
++ int ret;
++
++ pdata = pdev->dev.platform_data;
++
++ mxc_ahb_clk = clk_get(&pdev->dev, "usb_ahb");
++ if (IS_ERR(mxc_ahb_clk))
++ return PTR_ERR(mxc_ahb_clk);
++
++ ret = clk_enable(mxc_ahb_clk);
++ if (ret < 0) {
++ dev_err(&pdev->dev, "clk_enable(\"usb_ahb\") failed\n");
++ goto eenahb;
++ }
++
++ /* make sure USB_CLK is running at 60 MHz +/- 1000 Hz */
++ mxc_usb_clk = clk_get(&pdev->dev, "usb");
++ if (IS_ERR(mxc_usb_clk)) {
++ dev_err(&pdev->dev, "clk_get(\"usb\") failed\n");
++ ret = PTR_ERR(mxc_usb_clk);
++ goto egusb;
++ }
++
++ freq = clk_get_rate(mxc_usb_clk);
++ if (pdata->phy_mode != FSL_USB2_PHY_ULPI &&
++ (freq < 59999000 || freq > 60001000)) {
++ dev_err(&pdev->dev, "USB_CLK=%lu, should be 60MHz\n", freq);
++ goto eclkrate;
++ }
++
++ ret = clk_enable(mxc_usb_clk);
++ if (ret < 0) {
++ dev_err(&pdev->dev, "clk_enable(\"usb_clk\") failed\n");
++ goto eenusb;
++ }
++
++ return 0;
++
++eenusb:
++eclkrate:
++ clk_put(mxc_usb_clk);
++ mxc_usb_clk = NULL;
++egusb:
++ clk_disable(mxc_ahb_clk);
++eenahb:
++ clk_put(mxc_ahb_clk);
++ return ret;
++}
++
++void fsl_udc_clk_finalize(struct platform_device *pdev)
++{
++ struct fsl_usb2_platform_data *pdata = pdev->dev.platform_data;
++
++ /* ULPI transceivers don't need usbpll */
++ if (pdata->phy_mode == FSL_USB2_PHY_ULPI) {
++ clk_disable(mxc_usb_clk);
++ clk_put(mxc_usb_clk);
++ mxc_usb_clk = NULL;
++ }
++}
++
++void fsl_udc_clk_release(void)
++{
++ if (mxc_usb_clk) {
++ clk_disable(mxc_usb_clk);
++ clk_put(mxc_usb_clk);
++ }
++ clk_disable(mxc_ahb_clk);
++ clk_put(mxc_ahb_clk);
++}
+diff --git a/drivers/usb/gadget/fsl_udc_core.c b/drivers/usb/gadget/fsl_udc_core.c
+new file mode 100644
+index 0000000..42a74b8
+--- /dev/null
++++ b/drivers/usb/gadget/fsl_udc_core.c
+@@ -0,0 +1,2491 @@
++/*
++ * Copyright (C) 2004-2007 Freescale Semicondutor, Inc. All rights reserved.
++ *
++ * Author: Li Yang <le...@fr...>
++ * Jiang Bo <tan...@fr...>
++ *
++ * Description:
++ * Freescale high-speed USB SOC DR module device controller driver.
++ * This can be found on MPC8349E/MPC8313E cpus.
++ * The driver is previously named as mpc_udc. Based on bare board
++ * code from Dave Liu and Shlomi Gridish.
++ *
++ * 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.
++ */
++
++#undef VERBOSE
++
++#include <linux/module.h>
++#include <linux/kernel.h>
++#include <linux/ioport.h>
++#include <linux/types.h>
++#include <linux/errno.h>
++#include <linux/slab.h>
++#include <linux/init.h>
++#include <linux/list.h>
++#include <linux/interrupt.h>
++#include <linux/proc_fs.h>
++#include <linux/mm.h>
++#include <linux/moduleparam.h>
++#include <linux/device.h>
++#include <linux/usb/ch9.h>
++#include <linux/usb/gadget.h>
++#include <linux/usb/otg.h>
++#include <linux/dma-mapping.h>
++#include <linux/platform_device.h>
++#include <linux/fsl_devices.h>
++#include <linux/dmapool.h>
++#include <linux/delay.h>
++
++#include <asm/byteorder.h>
++#include <asm/io.h>
++#include <asm/system.h>
++#include <asm/unaligned.h>
++#include <asm/dma.h>
++
++#include "fsl_usb2_udc.h"
++
++#define DRIVER_DESC "Freescale High-Speed USB SOC Device Controller driver"
++#define DRIVER_AUTHOR "Li Yang/Jiang Bo"
++#define DRIVER_VERSION "Apr 20, 2007"
++
++#define DMA_ADDR_INVALID (~(dma_addr_t)0)
++
++static const char driver_name[] = "fsl-usb2-udc";
++static const char driver_desc[] = DRIVER_DESC;
++
++static struct usb_dr_device *dr_regs;
++#ifndef CONFIG_ARCH_MXC
++static struct usb_sys_interface *usb_sys_regs;
++#endif
++
++/* it is initialized in probe() */
++static struct fsl_udc *udc_controller = NULL;
++
++static const struct usb_endpoint_descriptor
++fsl_ep0_desc = {
++ .bLength = USB_DT_ENDPOINT_SIZE,
++ .bDescriptorType = USB_DT_ENDPOINT,
++ .bEndpointAddress = 0,
++ .bmAttributes = USB_ENDPOINT_XFER_CONTROL,
++ .wMaxPacketSize = USB_MAX_CTRL_PAYLOAD,
++};
++
++static void fsl_ep_fifo_flush(struct usb_ep *_ep);
++
++#ifdef CONFIG_PPC32
++#define fsl_readl(addr) in_le32(addr)
++#define fsl_writel(val32, addr) out_le32(addr, val32)
++#else
++#define fsl_readl(addr) readl(addr)
++#define fsl_writel(val32, addr) writel(val32, addr)
++#endif
++
++/********************************************************************
++ * Internal Used Function
++********************************************************************/
++/*-----------------------------------------------------------------
++ * done() - retire a request; caller blocked irqs
++ * @status : request status to be set, only works when
++ * request is still in progress.
++ *--------------------------------------------------------------*/
++static void done(struct fsl_ep *ep, struct fsl_req *req, int status)
++{
++ struct fsl_udc *udc = NULL;
++ unsigned char stopped = ep->stopped;
++ struct ep_td_struct *curr_td, *next_td;
++ int j;
++
++ udc = (struct fsl_udc *)ep->udc;
++ /* Removed the req from fsl_ep->queue */
++ list_del_init(&req->queue);
++
++ /* req.status should be set as -EINPROGRESS in ep_queue() */
++ if (req->req.status == -EINPROGRESS)
++ req->req.status = status;
++ else
++ status = req->req.status;
++
++ /* Free dtd for the request */
++ next_td = req->head;
++ for (j = 0; j < req->dtd_count; j++) {
++ curr_td = next_td;
++ if (j != req->dtd_count - 1) {
++ next_td = curr_td->next_td_virt;
++ }
++ dma_pool_free(udc->td_pool, curr_td, curr_td->td_dma);
++ }
++
++ if (req->mapped) {
++ dma_unmap_single(ep->udc->gadget.dev.parent,
++ req->req.dma, req->req.length,
++ ep_is_in(ep)
++ ? DMA_TO_DEVICE
++ : DMA_FROM_DEVICE);
++ req->req.dma = DMA_ADDR_INVALID;
++ req->mapped = 0;
++ } else
++ dma_sync_single_for_cpu(ep->udc->gadget.dev.parent,
++ req->req.dma, req->req.length,
++ ep_is_in(ep)
++ ? DMA_TO_DEVICE
++ : DMA_FROM_DEVICE);
++
++ if (status && (status != -ESHUTDOWN))
++ VDBG("complete %s req %p stat %d len %u/%u",
++ ep->ep.name, &req->req, status,
++ req->req.actual, req->req.length);
++
++ ep->stopped = 1;
++
++ spin_unlock(&ep->udc->lock);
++ /* complete() is from gadget layer,
++ * eg fsg->bulk_in_complete() */
++ if (req->req.complete)
++ req->req.complete(&ep->ep, &req->req);
++
++ spin_lock(&ep->udc->lock);
++ ep->stopped = stopped;
++}
++
++/*-----------------------------------------------------------------
++ * nuke(): delete all requests related to this ep
++ * called with spinlock held
++ *--------------------------------------------------------------*/
++static void nuke(struct fsl_ep *ep, int status)
++{
++ ep->stopped = 1;
++
++ /* Flush fifo */
++ fsl_ep_fifo_flush(&ep->ep);
++
++ /* Whether this eq has request linked */
++ while (!list_empty(&ep->queue)) {
++ struct fsl_req *req = NULL;
++
++ req = list_entry(ep->queue.next, struct fsl_req, queue);
++ done(ep, req, status);
++ }
++}
++
++/*------------------------------------------------------------------
++ Internal Hardware related function
++ ------------------------------------------------------------------*/
++
++static int dr_controller_setup(struct fsl_udc *udc)
++{
++ unsigned int tmp, portctrl;
++#ifndef CONFIG_ARCH_MXC
++ unsigned int ctrl;
++#endif
++ unsigned long timeout;
++#define FSL_UDC_RESET_TIMEOUT 1000
++
++ /* Config PHY interface */
++ portctrl = fsl_readl(&dr_regs->portsc1);
++ portctrl &= ~(PORTSCX_PHY_TYPE_SEL | PORTSCX_PORT_WIDTH);
++ switch (udc->phy_mode) {
++ case FSL_USB2_PHY_ULPI:
++ portctrl |= PORTSCX_PTS_ULPI;
++ break;
++ case FSL_USB2_PHY_UTMI_WIDE:
++ portctrl |= PORTSCX_PTW_16BIT;
++ /* fall through */
++ case FSL_USB2_PHY_UTMI:
++ portctrl |= PORTSCX_PTS_UTMI;
++ break;
++ case FSL_USB2_PHY_SERIAL:
++ portctrl |= PORTSCX_PTS_FSLS;
++ break;
++ default:
++ return -EINVAL;
++ }
++ fsl_writel(portctrl, &dr_regs->portsc1);
++
++ /* Stop and reset the usb controller */
++ tmp = fsl_readl(&dr_regs->usbcmd);
++ tmp &= ~USB_CMD_RUN_STOP;
++ fsl_writel(tmp, &dr_regs->usbcmd);
++
++ tmp = fsl_readl(&dr_regs->usbcmd);
++ tmp |= USB_CMD_CTRL_RESET;
++ fsl_writel(tmp, &dr_regs->usbcmd);
++
++ /* Wait for reset to complete */
++ timeout = jiffies + FSL_UDC_RESET_TIMEOUT;
++ while (fsl_readl(&dr_regs->usbcmd) & USB_CMD_CTRL_RESET) {
++ if (time_after(jiffies, timeout)) {
++ ERR("udc reset timeout!\n");
++ return -ETIMEDOUT;
++ }
++ cpu_relax();
++ }
++
++ /* Set the controller as device mode */
++ tmp = fsl_readl(&dr_regs->usbmode);
++ tmp |= USB_MODE_CTRL_MODE_DEVICE;
++ /* Disable Setup Lockout */
++ tmp |= USB_MODE_SETUP_LOCK_OFF;
++ fsl_writel(tmp, &dr_regs->usbmode);
++
++ /* Clear the setup status */
++ fsl_writel(0, &dr_regs->usbsts);
++
++ tmp = udc->ep_qh_dma;
++ tmp &= USB_EP_LIST_ADDRESS_MASK;
++ fsl_writel(tmp, &dr_regs->endpointlistaddr);
++
++ VDBG("vir[qh_base] is %p phy[qh_base] is 0x%8x reg is 0x%8x",
++ udc->ep_qh, (int)tmp,
++ fsl_readl(&dr_regs->endpointlistaddr));
++
++ /* Config control enable i/o output, cpu endian register */
++#ifndef CONFIG_ARCH_MXC
++ ctrl = __raw_readl(&usb_sys_regs->control);
++ ctrl |= USB_CTRL_IOENB;
++ __raw_writel(ctrl, &usb_sys_regs->control);
++#endif
++
++#if defined(CONFIG_PPC32) && !defined(CONFIG_NOT_COHERENT_CACHE)
++ /* Turn on cache snooping hardware, since some PowerPC platforms
++ * wholly rely on hardware to deal with cache coherent. */
++
++ /* Setup Snooping for all the 4GB space */
++ tmp = SNOOP_SIZE_2GB; /* starts from 0x0, size 2G */
++ __raw_writel(tmp, &usb_sys_regs->snoop1);
++ tmp |= 0x80000000; /* starts from 0x8000000, size 2G */
++ __raw_writel(tmp, &usb_sys_regs->snoop2);
++#endif
++
++ return 0;
++}
++
++/* Enable DR irq and set controller to run state */
++static void dr_controller_run(struct fsl_udc *udc)
++{
++ u32 temp;
++
++ /* Enable DR irq reg */
++ temp = USB_INTR_INT_EN | USB_INTR_ERR_INT_EN
++ | USB_INTR_PTC_DETECT_EN | USB_INTR_RESET_EN
++ | USB_INTR_DEVICE_SUSPEND | USB_INTR_SYS_ERR_EN;
++
++ fsl_writel(temp, &dr_regs->usbintr);
++
++ /* Clear stopped bit */
++ udc->stopped = 0;
++
++ /* Set the controller as device mode */
++ temp = fsl_readl(&dr_regs->usbmode);
++ temp |= USB_MODE_CTRL_MODE_DEVICE;
++ fsl_writel(temp, &dr_regs->usbmode);
++
++ /* Set controller to Run */
++ temp = fsl_readl(&dr_regs->usbcmd);
++ temp |= USB_CMD_RUN_STOP;
++ fsl_writel(temp, &dr_regs->usbcmd);
++
++ return;
++}
++
++static void dr_controller_stop(struct fsl_udc *udc)
++{
++ unsigned int tmp;
++
++ /* disable all INTR */
++ fsl_writel(0, &dr_regs->usbintr);
++
++ /* Set stopped bit for isr */
++ udc->stopped = 1;
++
++ /* disable IO output */
++/* usb_sys_regs->control = 0; */
++
++ /* set controller to Stop */
++ tmp = fsl_readl(&dr_regs->usbcmd);
++ tmp &= ~USB_CMD_RUN_STOP;
++ fsl_writel(tmp, &dr_regs->usbcmd);
++
++ return;
++}
++
++static void dr_ep_setup(unsigned char ep_num, unsigned char dir,
++ unsigned char ep_type)
++{
++ unsigned int tmp_epctrl = 0;
++
++ tmp_epctrl = fsl_readl(&dr_regs->endptctrl[ep_num]);
++ if (dir) {
++ if (ep_num)
++ tmp_epctrl |= EPCTRL_TX_DATA_TOGGLE_RST;
++ tmp_epctrl |= EPCTRL_TX_ENABLE;
++ tmp_epctrl |= ((unsigned int)(ep_type)
++ << EPCTRL_TX_EP_TYPE_SHIFT);
++ } else {
++ if (ep_num)
++ tmp_epctrl |= EPCTRL_RX_DATA_TOGGLE_RST;
++ tmp_epctrl |= EPCTRL_RX_ENABLE;
++ tmp_epctrl |= ((unsigned int)(ep_type)
++ << EPCTRL_RX_EP_TYPE_SHIFT);
++ }
++
++ fsl_writel(tmp_epctrl, &dr_regs->endptctrl[ep_num]);
++}
++
++static void
++dr_ep_change_stall(unsigned char ep_num, unsigned char dir, int value)
++{
++ u32 tmp_epctrl = 0;
++
++ tmp_epctrl = fsl_readl(&dr_regs->endptctrl[ep_num]);
++
++ if (value) {
++ /* set the stall bit */
++ if (dir)
++ tmp_epctrl |= EPCTRL_TX_EP_STALL;
++ else
++ tmp_epctrl |= EPCTRL_RX_EP_STALL;
++ } else {
++ /* clear the stall bit and reset data toggle */
++ if (dir) {
++ tmp_epctrl &= ~EPCTRL_TX_EP_STALL;
++ tmp_epctrl |= EPCTRL_TX_DATA_TOGGLE_RST;
++ } else {
++ tmp_epctrl &= ~EPCTRL_RX_EP_STALL;
++ tmp_epctrl |= EPCTRL_RX_DATA_TOGGLE_RST;
++ }
++ }
++ fsl_writel(tmp_epctrl, &dr_regs->endptctrl[ep_num]);
++}
++
++/* Get stall status of a specific ep
++ Return: 0: not stalled; 1:stalled */
++static int dr_ep_get_stall(unsigned char ep_num, unsigned char dir)
++{
++ u32 epctrl;
++
++ epctrl = fsl_readl(&dr_regs->endptctrl[ep_num]);
++ if (dir)
++ return (epctrl & EPCTRL_TX_EP_STALL) ? 1 : 0;
++ else
++ return (epctrl & EPCTRL_RX_EP_STALL) ? 1 : 0;
++}
++
++/********************************************************************
++ Internal Structure Build up functions
++********************************************************************/
++
++/*------------------------------------------------------------------
++* struct_ep_qh_setup(): set the Endpoint Capabilites field of QH
++ * @zlt: Zero Length Termination Select (1: disable; 0: enable)
++ * @mult: Mult field
++ ------------------------------------------------------------------*/
++static void struct_ep_qh_setup(struct fsl_udc *udc, unsigned char ep_num,
++ unsigned char dir, unsigned char ep_type,
++ unsigned int max_pkt_len,
++ unsigned int zlt, unsigned char mult)
++{
++ struct ep_queue_head *p_QH = &udc->ep_qh[2 * ep_num + dir];
++ unsigned int tmp = 0;
++
++ /* set the Endpoint Capabilites in QH */
++ switch (ep_type) {
++ case USB_ENDPOINT_XFER_CONTROL:
++ /* Interrupt On Setup (IOS). for control ep */
++ tmp = (max_pkt_len << EP_QUEUE_HEAD_MAX_PKT_LEN_POS)
++ | EP_QUEUE_HEAD_IOS;
++ break;
++ case USB_ENDPOINT_XFER_ISOC:
++ tmp = (max_pkt_len << EP_QUEUE_HEAD_MAX_PKT_LEN_POS)
++ | (mult << EP_QUEUE_HEAD_MULT_POS);
++ break;
++ case USB_ENDPOINT_XFER_BULK:
++ case USB_ENDPOINT_XFER_INT:
++ tmp = max_pkt_len << EP_QUEUE_HEAD_MAX_PKT_LEN_POS;
++ break;
++ default:
++ VDBG("error ep type is %d", ep_type);
++ return;
++ }
++ if (zlt)
++ tmp |= EP_QUEUE_HEAD_ZLT_SEL;
++
++ p_QH->max_pkt_length = cpu_to_le32(tmp);
++ p_QH->next_dtd_ptr = 1;
++ p_QH->size_ioc_int_sts = 0;
++
++ return;
++}
++
++/* Setup qh structure and ep register for ep0. */
++static void ep0_setup(struct fsl_udc *udc)
++{
++ /* the intialization of an ep includes: fields in QH, Regs,
++ * fsl_ep struct */
++ struct_ep_qh_setup(udc, 0, USB_RECV, USB_ENDPOINT_XFER_CONTROL,
++ USB_MAX_CTRL_PAYLOAD, 0, 0);
++ struct_ep_qh_setup(udc, 0, USB_SEND, USB_ENDPOINT_XFER_CONTROL,
++ USB_MAX_CTRL_PAYLOAD, 0, 0);
++ dr_ep_setup(0, USB_RECV, USB_ENDPOINT_XFER_CONTROL);
++ dr_ep_setup(0, USB_SEND, USB_ENDPOINT_XFER_CONTROL);
++
++ return;
++
++}
++
++/***********************************************************************
++ Endpoint Management Functions
++***********************************************************************/
++
++/*-------------------------------------------------------------------------
++ * when configurations are set, or when interface settings change
++ * for example the do_set_interface() in gadget layer,
++ * the driver will enable or disable the relevant endpoints
++ * ep0 doesn't use this routine. It is always enabled.
++-------------------------------------------------------------------------*/
++static int fsl_ep_enable(struct usb_ep *_ep,
++ const struct usb_endpoint_descriptor *desc)
++{
++ struct fsl_udc *udc = NULL;
++ struct fsl_ep *ep = NULL;
++ unsigned short max = 0;
++ unsigned char mult = 0, zlt;
++ int retval = -EINVAL;
++ unsigned long flags = 0;
++
++ ep = container_of(_ep, struct fsl_ep, ep);
++
++ /* catch various bogus parameters */
++ if (!_ep || !desc || ep->desc
++ || (desc->bDescriptorType != USB_DT_ENDPOINT))
++ return -EINVAL;
++
++ udc = ep->udc;
++
++ if (!udc->driver || (udc->gadget.speed == USB_SPEED_UNKNOWN))
++ return -ESHUTDOWN;
++
++ max = le16_to_cpu(desc->wMaxPacketSize);
++
++ /* Disable automatic zlp generation. Driver is reponsible to indicate
++ * explicitly through req->req.zero. This is needed to enable multi-td
++ * request. */
++ zlt = 1;
++
++ /* Assume the max packet size from gadget is always correct */
++ switch (desc->bmAttributes & 0x03) {
++ case USB_ENDPOINT_XFER_CONTROL:
++ case USB_ENDPOINT_XFER_BULK:
++ case USB_ENDPOINT_XFER_INT:
++ /* mult = 0. Execute N Transactions as demonstrated by
++ * the USB variable length packet protocol where N is
++ * computed using the Maximum Packet Length (dQH) and
++ * the Total Bytes field (dTD) */
++ mult = 0;
++ break;
++ case USB_ENDPOINT_XFER_ISOC:
++ /* Calculate transactions needed for high bandwidth iso */
++ mult = (unsigned char)(1 + ((max >> 11) & 0x03));
++ max = max & 0x8ff; /* bit 0~10 */
++ /* 3 transactions at most */
++ if (mult > 3)
++ goto en_done;
++ break;
++ default:
++ goto en_done;
++ }
++
++ spin_lock_irqsave(&udc->lock, flags);
++ ep->ep.maxpacket = max;
++ ep->desc = desc;
++ ep->stopped = 0;
++
++ /* Controller related setup */
++ /* Init EPx Queue Head (Ep Capabilites field in QH
++ * according to max, zlt, mult) */
++ struct_ep_qh_setup(udc, (unsigned char) ep_index(ep),
++ (unsigned char) ((desc->bEndpointAddress & USB_DIR_IN)
++ ? USB_SEND : USB_RECV),
++ (unsigned char) (desc->bmAttributes
++ & USB_ENDPOINT_XFERTYPE_MASK),
++ max, zlt, mult);
++
++ /* Init endpoint ctrl register */
++ dr_ep_setup((unsigned char) ep_index(ep),
++ (unsigned char) ((desc->bEndpointAddress & USB_DIR_IN)
++ ? USB_SEND : USB_RECV),
++ (unsigned char) (desc->bmAttributes
++ & USB_ENDPOINT_XFERTYPE_MASK));
++
++ spin_unlock_irqrestore(&udc->lock, flags);
++ retval = 0;
++
++ VDBG("enabled %s (ep%d%s) maxpacket %d",ep->ep.name,
++ ep->desc->bEndpointAddress & 0x0f,
++ (desc->bEndpointAddress & USB_DIR_IN)
++ ? "in" : "out", max);
++en_done:
++ return retval;
++}
++
++/*---------------------------------------------------------------------
++ * @ep : the ep being unconfigured. May not be ep0
++ * Any pending and uncomplete req will complete with status (-ESHUTDOWN)
++*---------------------------------------------------------------------*/
++static int fsl_ep_disable(struct usb_ep *_ep)
++{
++ struct fsl_udc *udc = NULL;
++ struct fsl_ep *ep = NULL;
++ unsigned long flags = 0;
++ u32 epctrl;
++ int ep_num;
++
++ ep = container_of(_ep, struct fsl_ep, ep);
++ if (!_ep || !ep->desc) {
++ VDBG("%s not enabled", _ep ? ep->ep.name : NULL);
++ return -EINVAL;
++ }
++
++ /* disable ep on controller */
++ ep_num = ep_index(ep);
++ epctrl = fsl_readl(&dr_regs->endptctrl[ep_num]);
++ if (ep_is_in(ep))
++ epctrl &= ~EPCTRL_TX_ENABLE;
++ else
++ epctrl &= ~EPCTRL_RX_ENABLE;
++ fsl_writel(epctrl, &dr_regs->endptctrl[ep_num]);
++
++ udc = (struct fsl_udc *)ep->udc;
++ spin_lock_irqsave(&udc->lock, flags);
++
++ /* nuke all pending requests (does flush) */
++ nuke(ep, -ESHUTDOWN);
++
++ ep->desc = NULL;
++ ep->stopped = 1;
++ spin_unlock_irqrestore(&udc->lock, flags);
++
++ VDBG("disabled %s OK", _ep->name);
++ return 0;
++}
++
++/*---------------------------------------------------------------------
++ * allocate a request object used by this endpoint
++ * the main operation is to insert the req->queue to the eq->queue
++ * Returns the request, or null if one could not be allocated
++*---------------------------------------------------------------------*/
++static struct usb_request *
++fsl_alloc_request(struct usb_ep *_ep, gfp_t gfp_flags)
++{
++ struct fsl_req *req = NULL;
++
++ req = kzalloc(sizeof *req, gfp_flags);
++ if (!req)
++ return NULL;
++
++ req->req.dma = DMA_ADDR_INVALID;
++ INIT_LIST_HEAD(&req->queue);
++
++ return &req->req;
++}
++
++static void fsl_free_request(struct usb_ep *_ep, struct usb_request *_req)
++{
++ struct fsl_req *req = NULL;
++
++ req = container_of(_req, struct fsl_req, req);
++
++ if (_req)
++ kfree(req);
++}
++
++/*-------------------------------------------------------------------------*/
++static void fsl_queue_td(struct fsl_ep *ep, struct fsl_req *req)
++{
++ int i = ep_index(ep) * 2 + ep_is_in(ep);
++ u32 temp, bitmask, tmp_stat;
++ struct ep_queue_head *dQH = &ep->udc->ep_qh[i];
++
++ /* VDBG("QH addr Register 0x%8x", dr_regs->endpointlistaddr);
++ VDBG("ep_qh[%d] addr is 0x%8x", i, (u32)&(ep->udc->ep_qh[i])); */
++
++ bitmask = ep_is_in(ep)
++ ? (1 << (ep_index(ep) + 16))
++ : (1 << (ep_index(ep)));
++
++ /* check if the pipe is empty */
++ if (!(list_empty(&ep->queue))) {
++ /* Add td to the end */
++ struct fsl_req *lastreq;
++ lastreq = list_entry(ep->queue.prev, struct fsl_req, queue);
++ lastreq->tail->next_td_ptr =
++ cpu_to_le32(req->head->td_dma & DTD_ADDR_MASK);
++ /* Read prime bit, if 1 goto done */
++ if (fsl_readl(&dr_regs->endpointprime) & bitmask)
++ goto out;
++
++ do {
++ /* Set ATDTW bit in USBCMD */
++ temp = fsl_readl(&dr_regs->usbcmd);
++ fsl_writel(temp | USB_CMD_ATDTW, &dr_regs->usbcmd);
++
++ /* Read correct status bit */
++ tmp_stat = fsl_readl(&dr_regs->endptstatus) & bitmask;
++
++ } while (!(fsl_readl(&dr_regs->usbcmd) & USB_CMD_ATDTW));
++
++ /* Write ATDTW bit to 0 */
++ temp = fsl_readl(&dr_regs->usbcmd);
++ fsl_writel(temp & ~USB_CMD_ATDTW, &dr_regs->usbcmd);
++
++ if (tmp_stat)
++ goto out;
++ }
++
++ /* Write dQH next pointer and terminate bit to 0 */
++ temp = req->head->td_dma & EP_QUEUE_HEAD_NEXT_POINTER_MASK;
++ dQH->next_dtd_ptr = cpu_to_le32(temp);
++
++ /* Clear active and halt bit */
++ temp = cpu_to_le32(~(EP_QUEUE_HEAD_STATUS_ACTIVE
++ | EP_QUEUE_HEAD_STATUS_HALT));
++ dQH->size_ioc_int_sts &= temp;
++
++ /* Ensure that updates to the QH will occure before priming. */
++ wmb();
++
++ /* Prime endpoint by writing 1 to ENDPTPRIME */
++ temp = ep_is_in(ep)
++ ? (1 << (ep_index(ep) + 16))
++ : (1 << (ep_index(ep)));
++ fsl_writel(temp, &dr_regs->endpointprime);
++out:
++ return;
++}
++
++/* Fill in the dTD structure
++ * @req: request that the transfer belongs to
++ * @length: return actually data length of the dTD
++ * @dma: return dma address of the dTD
++ * @is_last: return flag if it is the last dTD of the request
++ * return: pointer to the built dTD */
++static struct ep_td_struct *fsl_build_dtd(struct fsl_req *req, unsigned *length,
++ dma_addr_t *dma, int *is_last)
++{
++ u32 swap_temp;
++ struct ep_td_struct *dtd;
++
++ /* how big will this transfer be? */
++ *length = min(req->req.length - req->req.actual,
++ (unsigned)EP_MAX_LENGTH_TRANSFER);
++
++ dtd = dma_pool_alloc(udc_controller->td_pool, GFP_KERNEL, dma);
++ if (dtd == NULL)
++ return dtd;
++
++ dtd->td_dma = *dma;
++ /* Clear reserved field */
++ swap_temp = cpu_to_le32(dtd->size_ioc_sts);
++ swap_temp &= ~DTD_RESERVED_FIELDS;
++ dtd->size_ioc_sts = cpu_to_le32(swap_temp);
++
++ /* Init all of buffer page pointers */
++ swap_temp = (u32) (req->req.dma + req->req.actual);
++ dtd->buff_ptr0 = cpu_to_le32(swap_temp);
++ dtd->buff_ptr1 = cpu_to_le32(swap_temp + 0x1000);
++ dtd->buff_ptr2 = cpu_to_le32(swap_temp + 0x2000);
++ dtd->buff_ptr3 = cpu_to_le32(swap_temp + 0x3000);
++ dtd->buff_ptr4 = cpu_to_le32(swap_temp + 0x4000);
++
++ req->req.actual += *length;
++
++ /* zlp is needed if req->req.zero is set */
++ if (req->req.zero) {
++ if (*length == 0 || (*length % req->ep->ep.maxpacket) != 0)
++ *is_last = 1;
++ else
++ *is_last = 0;
++ } else if (req->req.length == req->req.actual)
++ *is_last = 1;
++ else
++ *is_last = 0;
++
++ if ((*is_last) == 0)
++ VDBG("multi-dtd request!");
++ /* Fill in the transfer size; set active bit */
++ swap_temp = ((*length << DTD_LENGTH_BIT_POS) | DTD_STATUS_ACTIVE);
++
++ /* Enable interrupt for the last dtd of a request */
++ if (*is_last && !req->req.no_interrupt)
++ swap_temp |= DTD_IOC;
++
++ dtd->size_ioc_sts = cpu_to_le32(swap_temp);
++
++ mb();
++
++ VDBG("length = %d address= 0x%x", *length, (int)*dma);
++
++ return dtd;
++}
++
++/* Generate dtd chain for a request */
++static int fsl_req_to_dtd(struct fsl_req *req)
++{
++ unsigned count;
++ int is_last;
++ int is_first =1;
++ struct ep_td_struct *last_dtd = NULL, *dtd;
++ dma_addr_t dma;
++
++ do {
++ dtd = fsl_build_dtd(req, &count, &dma, &is_last);
++ if (dtd == NULL)
++ return -ENOMEM;
++
++ if (is_first) {
++ is_first = 0;
++ req->head = dtd;
++ } else {
++ last_dtd->next_td_ptr = cpu_to_le32(dma);
++ last_dtd->next_td_virt = dtd;
++ }
++ last_dtd = dtd;
++
++ req->dtd_count++;
++ } while (!is_last);
++
++ dtd->next_td_ptr = cpu_to_le32(DTD_NEXT_TERMINATE);
++
++ req->tail = dtd;
++
++ return 0;
++}
++
++/* queues (submits) an I/O request to an endpoint */
++static int
++fsl_ep_queue(struct usb_ep *_ep, struct usb_request *_req, gfp_t gfp_flags)
++{
++ struct fsl_ep *ep = container_of(_ep, struct fsl_ep, ep);
++ struct fsl_req *req = container_of(_req, struct fsl_req, req);
++ struct fsl_udc *udc;
++ unsigned long flags;
++ int is_iso = 0;
++
++ /* catch various bogus parameters */
++ if (!_req || !req->req.complete || !req->req.buf
++ || !list_empty(&req->queue)) {
++ VDBG("%s, bad params", __func__);
++ return -EINVAL;
++ }
++ if (unlikely(!_ep || !ep->desc)) {
++ VDBG("%s, bad ep", __func__);
++ return -EINVAL;
++ }
++ if (ep->desc->bmAttributes == USB_ENDPOINT_XFER_ISOC) {
++ if (req->req.length > ep->ep.maxpacket)
++ return -EMSGSIZE;
++ is_iso = 1;
++ }
++
++ udc = ep->udc;
++ if (!udc->driver || udc->gadget.speed == USB_SPEED_UNKNOWN)
++ return -ESHUTDOWN;
++
++ req->ep = ep;
++
++ /* map virtual address to hardware */
++ if (req->req.dma == DMA_ADDR_INVALID) {
++ req->req.dma = dma_map_single(ep->udc->gadget.dev.parent,
++ req->req.buf,
++ req->req.length, ep_is_in(ep)
++ ? DMA_TO_DEVICE
++ : DMA_FROM_DEVICE);
++ req->mapped = 1;
++ } else {
++ dma_sync_single_for_device(ep->udc->gadget.dev.parent,
++ req->req.dma, req->req.length,
++ ep_is_in(ep)
++ ? DMA_TO_DEVICE
++ : DMA_FROM_DEVICE);
++ req->mapped = 0;
++ }
++
++ req->req.status = -EINPROGRESS;
++ req->req.actual = 0;
++ req->dtd_count = 0;
++
++ spin_lock_irqsave(&udc->lock, flags);
++
++ /* build dtds and push them to device queue */
++ if (!fsl_req_to_dtd(req)) {
++ fsl_queue_td(ep, req);
++ } else {
++ spin_unlock_irqrestore(&udc->lock, flags);
++ return -ENOMEM;
++ }
++
++ /* Update ep0 state */
++ if ((ep_index(ep) == 0))
++ udc->ep0_state = DATA_STATE_XMIT;
++
++ /* irq handler advances the queue */
++ if (req != NULL)
++ list_add_tail(&req->queue, &ep->queue);
++ spin_unlock_irqrestore(&udc->lock, flags);
++
++ return 0;
++}
++
++/* dequeues (cancels, unlinks) an I/O request from an endpoint */
++static int fsl_ep_dequeue(struct usb_ep *_ep, struct usb_request *_req)
++{
++ struct fsl_ep *ep = container_of(_ep, struct fsl_ep, ep);
++ struct fsl_req *req;
++ unsigned long flags;
++ int ep_num, stopped, ret = 0;
++ u32 epctrl;
++
++ if (!_ep || !_req)
++ return -EINVAL;
++
++ spin_lock_irqsave(&ep->udc->lock, flags);
++ stopped = ep->stopped;
++
++ /* Stop the ep before we deal with the queue */
++ ep->stopped = 1;
++ ep_num = ep_index(ep);
++ epctrl = fsl_readl(&dr_regs->endptctrl[ep_num]);
++ if (ep_is_in(ep))
++ epctrl &= ~EPCTRL_TX_ENABLE;
++ else
++ epctrl &= ~EPCTRL_RX_ENABLE;
++ fsl_writel(epctrl, &dr_regs->endptctrl[ep_num]);
++
++ /* 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) {
++ ret = -EINVAL;
++ goto out;
++ }
++
++ /* The request is in progress, or completed but not dequeued */
++ if (ep->queue.next == &req->queue) {
++ _req->status = -ECONNRESET;
++ fsl_ep_fifo_flush(_ep); /* flush current transfer */
++
++ /* The request isn't the last request in this ep queue */
++ if (req->queue.next != &ep->queue) {
++ struct ep_queue_head *qh;
++ struct fsl_req *next_req;
++
++ qh = ep->qh;
++ next_req = list_entry(req->queue.next, struct fsl_req,
++ queue);
++
++ /* Point the QH to the first TD of next request */
++ fsl_writel((u32) next_req->head, &qh->curr_dtd_ptr);
++ }
++
++ /* The request hasn't been processed, patch up the TD chain */
++ } else {
++ struct fsl_req *prev_req;
++
++ prev_req = list_entry(req->queue.prev, struct fsl_req, queue);
++ fsl_writel(fsl_readl(&req->tail->next_td_ptr),
++ &prev_req->tail->next_td_ptr);
++
++ }
++
++ done(ep, req, -ECONNRESET);
++
++ /* Enable EP */
++out: epctrl = fsl_readl(&dr_regs->endptctrl[ep_num]);
++ if (ep_is_in(ep))
++ epctrl |= EPCTRL_TX_ENABLE;
++ else
++ epctrl |= EPCTRL_RX_ENABLE;
++ fsl_writel(epctrl, &dr_regs->endptctrl[ep_num]);
++ ep->stopped = stopped;
++
++ spin_unlock_irqrestore(&ep->udc->lock, flags);
++ return ret;
++}
++
++/*-------------------------------------------------------------------------*/
++
++/*-----------------------------------------------------------------
++ * modify the endpoint halt feature
++ * @ep: the non-isochronous endpoint being stalled
++ * @value: 1--set halt 0--clear halt
++ * Returns zero, or a negative error code.
++*----------------------------------------------------------------*/
++static int fsl_ep_set_halt(struct usb_ep *_ep, int value)
++{
++ struct fsl_ep *ep = NULL;
++ unsigned long flags = 0;
++ int status = -EOPNOTSUPP; /* operation not supported */
++ unsigned char ep_dir = 0, ep_num = 0;
++ struct fsl_udc *udc = NULL;
++
++ ep = container_of(_ep, struct fsl_ep, ep);
++ udc = ep->udc;
++ if (!_ep || !ep->desc) {
++ status = -EINVAL;
++ goto out;
++ }
++
++ if (ep->desc->bmAttributes == USB_ENDPOINT_XFER_ISOC) {
++ status = -EOPNOTSUPP;
++ goto out;
++ }
++
++ /* Attempt to halt IN ep will fail if any transfer requests
++ * are still queue */
++ if (value && ep_is_in(ep) && !list_empty(&ep->queue)) {
++ status = -EAGAIN;
++ goto out;
++ }
++
++ status = 0;
++ ep_dir = ep_is_in(ep) ? USB_SEND : USB_RECV;
++ ep_num = (unsigned char)(ep_index(ep));
++ spin_lock_irqsave(&ep->udc->lock, flags);
++ dr_ep_change_stall(ep_num, ep_dir, value);
++ spin_unlock_irqrestore(&ep->udc->lock, flags);
++
++ if (ep_index(ep) == 0) {
++ udc->ep0_state = WAIT_FOR_SETUP;
++ udc->ep0_dir = 0;
++ }
++out:
++ VDBG(" %s %s halt stat %d", ep->ep.name,
++ value ? "set" : "clear", status);
++
++ return status;
++}
++
++static void fsl_ep_fifo_flush(struct usb_ep *_ep)
++{
++ struct fsl_ep *ep;
++ int ep_num, ep_dir;
++ u32 bits;
++ unsigned long timeout;
++#define FSL_UDC_FLUSH_TIMEOUT 1000
++
++ if (!_ep) {
++ return;
++ } else {
++ ep = container_of(_ep, struct fsl_ep, ep);
++ if (!ep->desc)
++ return;
++ }
++ ep_num = ep_index(ep);
++ ep_dir = ep_is_in(ep) ? USB_SEND : USB_RECV;
++
++ if (ep_num == 0)
++ bits = (1 << 16) | 1;
++ else if (ep_dir == USB_SEND)
++ bits = 1 << (16 + ep_num);
++ else
++ bits = 1 << ep_num;
++
++ timeout = jiffies + FSL_UDC_FLUSH_TIMEOUT;
++ do {
++ fsl_writel(bits, &dr_regs->endptflush);
++
++ /* Wait until flush complete */
++ while (fsl_readl(&dr_regs->endptflush)) {
++ if (time_after(jiffies, timeout)) {
++ ERR("ep flush timeout\n");
++ return;
++ }
++ cpu_relax();
++ }
++ /* See if we need to flush again */
++ } while (fsl_readl(&dr_regs->endptstatus) & bits);
++}
++
++static struct usb_ep_ops fsl_ep_ops = {
++ .enable = fsl_ep_enable,
++ .disable = fsl_ep_disable,
++
++ .alloc_request = fsl_alloc_request,
++ .free_request = fsl_free_request,
++
++ .queue = fsl_ep_queue,
++ .dequeue = fsl_ep_dequeue,
++
++ .set_halt = fsl_ep_set_halt,
++ .fifo_flush = fsl_ep_fifo_flush, /* flush fifo */
++};
++
++/*-------------------------------------------------------------------------
++ Gadget Driver Layer Operations
++-------------------------------------------------------------------------*/
++
++/*----------------------------------------------------------------------
++ * Get the current frame number (from DR frame_index Reg )
++ *----------------------------------------------------------------------*/
++static int fsl_get_frame(struct usb_gadget *gadget)
++{
++ return (int)(fsl_readl(&dr_regs->frindex) & USB_FRINDEX_MASKS);
++}
++
++/*-----------------------------------------------------------------------
++ * Tries to wake up the host connected to this gadget
++ -----------------------------------------------------------------------*/
++static int fsl_wakeup(struct usb_gadget *gadget)
++{
++ struct fsl_udc *udc = container_of(gadget, struct fsl_udc, gadget);
++ u32 portsc;
++
++ /* Remote wakeup feature not enabled by host */
++ if (!udc->remote_wakeup)
++ return -ENOTSUPP;
++
++ portsc = fsl_readl(&dr_regs->portsc1);
++ /* not suspended? */
++ if (!(portsc & PORTSCX_PORT_SUSPEND))
++ return 0;
++ /* trigger force resume */
++ portsc |= PORTSCX_PORT_FORCE_RESUME;
++ fsl_writel(portsc, &dr_regs->portsc1);
++ return 0;
++}
++
++static int can_pullup(struct fsl_udc *udc)
++{
++ return udc->driver && udc->softconnect && udc->vbus_active;
++}
++
++/* Notify controller that VBUS is powered, Called by whatever
++ detects VBUS sessions */
++static int fsl_vbus_session(struct usb_gadget *gadget, int is_active)
++{
++ struct fsl_udc *udc;
++ unsigned long flags;
++
++ udc = container_of(gadget, struct fsl_udc, gadget);
++ spin_lock_irqsave(&udc->lock, flags);
++ VDBG("VBUS %s", is_active ? "on" : "off");
++ udc->vbus_active = (is_active != 0);
++ if (can_pullup(udc))
++ fsl_writel((fsl_readl(&dr_regs->usbcmd) | USB_CMD_RUN_STOP),
++ &dr_regs->usbcmd);
++ else
++ fsl_writel((fsl_readl(&dr_regs->usbcmd) & ~USB_CMD_RUN_STOP),
++ &dr_regs->usbcmd);
++ spin_unlock_irqrestore(&udc->lock, flags);
++ return 0;
++}
++
++/* constrain controller's VBUS power usage
++ * This call is used by gadget drivers during SET_CONFIGURATION calls,
++ * reporting how much power the device may consume. For example, this
++ * could affect how quickly batteries are recharged.
++ *
++ * Returns zero on success, else negative errno.
++ */
++static int fsl_vbus_draw(struct usb_gadget *gadget, unsigned mA)
++{
++ struct fsl_udc *udc;
++
++ udc = container_of(gadget, struct fsl_udc, gadget);
++ if (udc->transceiver)
++ return otg_set_power(udc->transceiver, mA);
++ return -ENOTSUPP;
++}
++
++/* Change Data+ pullup status
++ * this func is used by usb_gadget_connect/disconnet
++ */
++static int fsl_pullup(struct usb_gadget *gadget, int is_on)
++{
++ struct fsl_udc *udc;
++
++ udc = container_of(gadget, struct fsl_udc, gadget);
++ udc->softconnect = (is_on != 0);
++ if (can_pullup(udc))
++ fsl_writel((fsl_readl(&dr_regs->usbcmd) | USB_CMD_RUN_STOP),
++ &dr_regs->usbcmd);
++ else
++ fsl_writel((fsl_readl(&dr_regs->usbcmd) & ~USB_CMD_RUN_STOP),
++ &dr_regs->usbcmd);
++
++ return 0;
++}
++
++/* defined in gadget.h */
++static struct usb_gadget_ops fsl_gadget_ops = {
++ .get_frame = fsl_get_frame,
++ .wakeup = fsl_wakeup,
++/* .set_selfpowered = fsl_set_selfpowered, */ /* Always selfpowered */
++ .vbus_session = fsl_vbus_session,
++ .vbus_draw = fsl_vbus_draw,
++ .pullup = fsl_pullup,
++};
++
++/* Set protocol stall on ep0, protocol stall will automatically be cleared
++ on new transaction */
++static void ep0stall(struct fsl_udc *udc)
++{
++ u32 tmp;
++
++ /* must set tx and rx to stall at the same time */
++ tmp = fsl_readl(&dr_regs->endptctrl[0]);
++ tmp |= EPCTRL_TX_EP_STALL | EPCTRL_RX_EP_STALL;
++ fsl_writel(tmp, &dr_regs->endptctrl[0]);
++ udc->ep0_state = WAIT_FOR_SETUP;
++ udc->ep0_dir = 0;
++}
++
++/* Prime a status phase for ep0 */
++static int ep0_prime_status(struct fsl_udc *udc, int direction)
++{
++ struct fsl_req *req = udc->status_req;
++ struct fsl_ep *ep;
++
++ if (direction == EP_DIR_IN)
++ udc->ep0_dir = USB_DIR_IN;
++ else
++ udc->ep0_dir = USB_DIR_OUT;
++
++ ep = &udc->eps[0];
++ udc->ep0_state = WAIT_FOR_OUT_STATUS;
++
++ req->ep = ep;
++ req->req.length = 0;
++ req->req.status = -EINPROGRESS;
++ req->req.actual = 0;
++ req->req.complete = NULL;
++ req->dtd_count = 0;
++
++ if (fsl_req_to_dtd(req) == 0)
++ fsl_queue_td(ep, req);
++ else
++ return -ENOMEM;
++
++ list_add_tail(&req->queue, &ep->queue);
++
++ return 0;
++}
++
++static void udc_reset_ep_queue(struct fsl_udc *udc, u8 pipe)
++{
++ struct fsl_ep *ep = get_ep_by_pipe(udc, pipe);
++
++ if (ep->name)
++ nuke(ep, -ESHUTDOWN);
++}
++
++/*
++ * ch9 Set address
++ */
++static void ch9setaddress(struct fsl_udc *udc, u16 value, u16 index, u16 length)
++{
++ /* Save the new address to device struct */
++ udc->device_address = (u8) value;
++ /* Update usb state */
++ udc->usb_state = USB_STATE_ADDRESS;
++ /* Status phase */
++ if (ep0_prime_status(udc, EP_DIR_IN))
++ ep0stall(udc);
++}
++
++/*
++ * ch9 Get status
++ */
++static void ch9getstatus(struct fsl_udc *udc, u8 request_type, u16 value,
++ u16 index, u16 length)
++{
++ u16 tmp = 0; /* Status, cpu endian */
++ struct fsl_req *req;
++ struct fsl_ep *ep;
++
++ ep = &udc->eps[0];
++
++ if ((request_type & USB_RECIP_MASK) == USB_RECIP_DEVICE) {
++ /* Get device status */
++ tmp = 1 << USB_DEVICE_SELF_POWERED;
++ tmp |= udc->remote_wakeup << USB_DEVICE_REMOTE_WAKEUP;
++ } else if ((request_type & USB_RECIP_MASK) == USB_RECIP_INTERFACE) {
++ /* Get interface status */
++ /* We don't have interface information in udc driver */
++ tmp = 0;
++ } else if ((request_type & USB_RECIP_MASK) == USB_RECIP_ENDPOINT) {
++ /* Get endpoint status */
++ struct fsl_ep *target_ep;
++
++ target_ep = get_ep_by_pipe(udc, get_pipe_by_windex(index));
++
++ /* stall if endpoint doesn't exist */
++ if (!target_ep->desc)
++ goto stall;
++ tmp = dr_ep_get_stall(ep_index(target_ep), ep_is_in(target_ep))
++ << USB_ENDPOINT_HALT;
++ }
++
++ udc->ep0_dir = USB_DIR_IN;
++ /* Borrow the per device status_req */
++ req = udc->status_req;
++ /* Fill in the reqest structure */
++ *((u16 *) req->req.buf) = cpu_to_le16(tmp);
++ req->ep = ep;
++ req->req.length = 2;
++ req->req.status = -EINPROGRESS;
++ req->req.actual = 0;
++ req->req.complete = NULL;
++ req->dtd_count = 0;
++
++ /* prime the data phase */
++ if ((fsl_req_to_dtd(req) == 0))
++ fsl_queue_td(ep, req);
++ else /* no mem */
++ goto stall;
++
++ list_add_tail(&req->queue, &ep->queue);
++ udc->ep0_state = DATA_STATE_XMIT;
++ return;
++stall:
++ ep0stall(udc);
++}
++
++static void setup_received_irq(struct fsl_udc *udc,
++ struct usb_ctrlrequest *setup)
++{
++ u16 wValue = le16_to_cpu(setup->wValue);
++ u16 wIndex = le16_to_cpu(setup->wIndex);
++ u16 wLength = le16_to_cpu(setup->wLength);
++
++ udc_reset_ep_queue(udc, 0);
++
++ /* We process some stardard setup requests here */
++ switch (setup->bRequest) {
++ case USB_REQ_GET_STATUS:
++ /* Data+Status phase from udc */
++ if ((setup->bRequestType & (USB_DIR_IN | USB_TYPE_MASK))
++ != (USB_DIR_IN | USB_TYPE_STANDARD))
++ break;
++ ch9getstatus(udc, setup->bRequestType, wValue, wIndex, wLength);
++ return;
++
++ case USB_REQ_SET_ADDRESS:
++ /* Status phase from udc */
++ if (setup->bRequestType != (USB_DIR_OUT | USB_TYPE_STANDARD
++ | USB_RECIP_DEVICE))
++ break;
++ ch9setaddress(udc, wValue, wIndex, wLength);
++ return;
++
++ case USB_REQ_CLEAR_FEATURE:
++ case USB_REQ_SET_FEATURE:
++ /* Status phase from udc */
++ {
++ int rc = -EOPNOTSUPP;
++
++ if ((setup->bRequestType & (USB_RECIP_MASK | USB_TYPE_MASK))
++ == (USB_RECIP_ENDPOINT | USB_TYPE_STANDARD)) {
++ int pipe = get_pipe_by_windex(wIndex);
++ struct fsl_ep *ep;
++
++ if (wValue != 0 || wLength != 0 || pipe > udc->max_ep)
++ break;
++ ep = get_ep_by_pipe(udc, pipe);
++
++ spin_unlock(&udc->lock);
++ rc = fsl_ep_set_halt(&ep->ep,
++ (setup->bRequest == USB_REQ_SET_FEATURE)
++ ? 1 : 0);
++ spin_lock(&udc->lock);
++
++ } else if ((setup->bRequestType & (USB_RECIP_MASK
++ | USB_TYPE_MASK)) == (USB_RECIP_DEVICE
++ | USB_TYPE_STANDARD)) {
++ /* Note: The driver has not include OTG support yet.
++ * This will be set when OTG supp...
[truncated message content] |