This patch adds support for the USB Device Controller on the Au1200 SOC as
well as basic device-only OTG (On-The-Go) support. There are some defines and
hooks for future expansion to full OTG support.
This has been tested with the g_zero gadget as well as the g_file_storage
gadget on a DB1250 board.
Signed-off-by: Kevin Hickey <khickey@rmicorp.com>
---
arch/mips/configs/db1200_defconfig | 21 +
drivers/usb/core/Kconfig | 2 -
drivers/usb/gadget/Kconfig | 49 +-
drivers/usb/gadget/Makefile | 5 +
drivers/usb/gadget/au1200_otg.c | 854 +++++++++++
drivers/usb/gadget/au1200_otg.h | 138 ++
drivers/usb/gadget/au1200_udc.c | 2862 ++++++++++++++++++++++++++++++++++++
drivers/usb/gadget/au1200_udc.h | 816 ++++++++++
drivers/usb/gadget/au1200_uoc.h | 1021 +++++++++++++
drivers/usb/gadget/gadget_chips.h | 6 +
include/linux/usb/otg.h | 7 +-
11 files changed, 5770 insertions(+), 11 deletions(-)
create mode 100644 drivers/usb/gadget/au1200_otg.c
create mode 100644 drivers/usb/gadget/au1200_otg.h
create mode 100644 drivers/usb/gadget/au1200_udc.c
create mode 100644 drivers/usb/gadget/au1200_udc.h
create mode 100644 drivers/usb/gadget/au1200_uoc.h
diff --git a/arch/mips/configs/db1200_defconfig
b/arch/mips/configs/db1200_defconfig
index ab17973..b69fec3 100644
--- a/arch/mips/configs/db1200_defconfig
+++ b/arch/mips/configs/db1200_defconfig
@@ -914,13 +914,34 @@ CONFIG_USB_ARCH_HAS_EHCI=y
#
CONFIG_USB_GADGET=m
# CONFIG_USB_GADGET_DEBUG_FILES is not set
+CONFIG_USB_GADGET_SELECTED=y
+# CONFIG_USB_GADGET_AMD5536UDC is not set
+# CONFIG_USB_GADGET_ATMEL_USBA is not set
+# CONFIG_USB_GADGET_FSL_USB2 is not set
# CONFIG_USB_GADGET_NET2280 is not set
# CONFIG_USB_GADGET_PXA2XX is not set
+# CONFIG_USB_GADGET_M66592 is not set
# CONFIG_USB_GADGET_GOKU is not set
# CONFIG_USB_GADGET_LH7A40X is not set
# CONFIG_USB_GADGET_OMAP is not set
+CONFIG_USB_GADGET_AU1200=y
+CONFIG_USB_AU1200=m
+# CONFIG_USB_GADGET_S3C2410 is not set
# CONFIG_USB_GADGET_AT91 is not set
# CONFIG_USB_GADGET_DUMMY_HCD is not set
+CONFIG_USB_GADGET_DUALSPEED=y
+CONFIG_USB_PORT_AU1200OTG=y
+CONFIG_USB_AU1200OTG=m
+CONFIG_USB_ZERO=m
+# CONFIG_USB_ETH is not set
+CONFIG_USB_GADGETFS=m
+CONFIG_USB_FILE_STORAGE=m
+# CONFIG_USB_FILE_STORAGE_TEST is not set
+# CONFIG_USB_G_SERIAL is not set
+# CONFIG_USB_MIDI_GADGET is not set
+CONFIG_MMC=y
+# CONFIG_MMC_DEBUG is not set
+# CONFIG_MMC_UNSAFE_RESUME is not set
# CONFIG_USB_GADGET_DUALSPEED is not set
#
diff --git a/drivers/usb/core/Kconfig b/drivers/usb/core/Kconfig
index cc9f397..cc54c55 100644
--- a/drivers/usb/core/Kconfig
+++ b/drivers/usb/core/Kconfig
@@ -106,8 +106,6 @@ config USB_OTG
bool
depends on USB && EXPERIMENTAL
select USB_SUSPEND
- default n
-
config USB_OTG_WHITELIST
bool "Rely on OTG Targeted Peripherals List"
diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig
index acc95b2..ceb8a17 100644
--- a/drivers/usb/gadget/Kconfig
+++ b/drivers/usb/gadget/Kconfig
@@ -315,16 +315,28 @@ config USB_OMAP
default USB_GADGET
select USB_GADGET_SELECTED
-config USB_OTG
- boolean "OTG Support"
- depends on USB_GADGET_OMAP && ARCH_OMAP_OTG && USB_OHCI_HCD
+config USB_GADGET_AU1200
+ boolean "AU1200 USB Device Controller"
+ depends on SOC_AU1200
+ select USB_GADGET_DUALSPEED
help
- The most notable feature of USB OTG is support for a
- "Dual-Role" device, which can act as either a device
- or a host. The initial role choice can be changed
- later, when two dual-role devices talk to each other.
+ The RMI Alchemy Au1200 and Au1250 SOCs include a full On-The-Go port
+ with USB 1.1 and USB 2.0 support. The device port supports 4
+ bidirectional endpoints plus the default endpoint ep0.
+
+ This driver provides the device mode for the On-The-Go port. The
+ port will not be active unless the au1200_otg driver is loaded or
+ built statically.
+
+ Say "y" to link the driver statically, or "m" to build a
+ dynamically linked module called "au1200_udc" and force all
+ gadget drivers to also be dynamically linked.
- Select this only if your OMAP board has a Mini-AB connector.
+config USB_AU1200
+ tristate
+ depends on USB_GADGET_AU1200
+ default USB_GADGET
+ select USB_GADGET_SELECTED
config USB_GADGET_S3C2410
boolean "S3C2410 USB Device Controller"
@@ -407,6 +419,27 @@ config USB_GADGET_DUALSPEED
Means that gadget drivers should include extra descriptors
and code to handle dual-speed controllers.
+config USB_PORT_AU1200OTG
+ boolean "AU1200 USB portmux control (On-The-Go support)"
+ depends on USB_GADGET_AU1200 || USB_EHCI_HCD || USB_OHCI_HCD
+ default n
+ help
+ The AU1200 and Au1200 USB device port can be used as either a host
+ port or a device port. This driver configures the port based on
+ hardware or software set criteria. It is required to be loaded for
+ au1200_udc to be useful.
+
+ NOTE: Currently, only device-port mode is supported. Host-port and
+ other On The Go modes will be supported in a future release.
+
+ Say "y" to link this driver statically or "m" to build a dynamically
+ linked module called "au1200_otg".
+
+config USB_AU1200OTG
+ tristate
+ depends on USB_PORT_AU1200OTG
+ default USB_AU1200
+
#
# USB Gadget Drivers
#
diff --git a/drivers/usb/gadget/Makefile b/drivers/usb/gadget/Makefile
index fcb5cb9..d2af7cd 100644
--- a/drivers/usb/gadget/Makefile
+++ b/drivers/usb/gadget/Makefile
@@ -12,6 +12,7 @@ obj-$(CONFIG_USB_PXA25X) += pxa25x_udc.o
obj-$(CONFIG_USB_PXA27X) += pxa27x_udc.o
obj-$(CONFIG_USB_GOKU) += goku_udc.o
obj-$(CONFIG_USB_OMAP) += omap_udc.o
+obj-$(CONFIG_USB_AU1200) += au1200_udc.o
obj-$(CONFIG_USB_LH7A40X) += lh7a40x_udc.o
obj-$(CONFIG_USB_S3C2410) += s3c2410_udc.o
obj-$(CONFIG_USB_AT91) += at91_udc.o
@@ -49,3 +50,7 @@ obj-$(CONFIG_USB_G_PRINTER) += g_printer.o
obj-$(CONFIG_USB_MIDI_GADGET) += g_midi.o
obj-$(CONFIG_USB_CDC_COMPOSITE) += g_cdc.o
+#
+# AU1200 USB OTG options
+#
+obj-$(CONFIG_USB_AU1200OTG) += au1200_otg.o
diff --git a/drivers/usb/gadget/au1200_otg.c b/drivers/usb/gadget/au1200_otg.c
new file mode 100644
index 0000000..a110aff
--- /dev/null
+++ b/drivers/usb/gadget/au1200_otg.c
@@ -0,0 +1,854 @@
+/*
+ * Au1200 On The Go port driver.
+ */
+
+/*
+ * Copyright (C) 2008 RMI Corporation (http://www.rmicorp.com)
+ * Author: Kevin Hickey (khickey@rmicorp.com)
+ *
+ * THIS SOFTWARE IS PROVIDED BY RMI Corporation 'AS IS' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL RMI OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
+ * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * 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.
+ *
+ * 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
+ */
+
+/*****************************************************************************
+ * Includes
+ *****************************************************************************/
+
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/kernel.h>
+#include <linux/version.h>
+#include <linux/delay.h>
+#include <linux/ioport.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/smp_lock.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/timer.h>
+#include <linux/list.h>
+#include <linux/interrupt.h>
+
+#include <asm/byteorder.h>
+#include <linux/io.h>
+#include <asm/irq.h>
+#include <asm/system.h>
+#include <asm/unaligned.h>
+
+#include <linux/platform_device.h>
+
+#include <asm/mach-au1x00/au1000.h>
+#include <asm/mach-au1x00/au1000_gpio.h>
+
+#include <linux/usb.h>
+#include <linux/usb/gadget.h>
+#include <linux/usb/otg.h>
+
+#define DRIVER_DESC "Au1200 USB OTG Controller"
+#define DRIVER_NAME_FOR_PRINT OTG_DRIVER_NAME
+
+#include "au1200_otg.h"
+#include "au1200_uoc.h"
+
+
+/*****************************************************************************
+ * Function Declarations
+ *****************************************************************************/
+
+static u32 otg_app_query(int);
+static int au1200otg_set_peripheral(struct otg_transceiver *,
+ struct usb_gadget *);
+
+
+/*****************************************************************************
+ * Data
+ *****************************************************************************/
+
+static const char driver_name[] = OTG_DRIVER_NAME;
+static const char driver_desc[] = DRIVER_DESC;
+
+static struct otg *the_controller;
+static const char *transceiver_label = "au1200_otg";
+
+static u32 init_state;
+
+struct usb_otg_gadget_extension otg_gadget_extension = {
+ .request = NULL,
+ .query = otg_app_query,
+ .notify = NULL
+};
+
+static u32 state_mask;
+u32 otg_tmr_high_count;
+
+/*****************************************************************************
+ * Function Definitions
+ *****************************************************************************/
+
+/**
+ * \brief
+ * fill OTG transceiver struct
+ *
+ * \param transceiver OTG transceiver
+ *
+ * \return void
+ */
+static inline void otg_init_transceiver(struct otg_transceiver *transceiver)
+{
+ transceiver->dev = NULL;
+ transceiver->label = transceiver_label;
+ transceiver->default_a = 0;
+ transceiver->state = OTG_STATE_UNDEFINED;
+ transceiver->prv_state = OTG_STATE_UNDEFINED;
+ transceiver->params = 0;
+ transceiver->otg_priv = (void *) &otg_gadget_extension;
+ transceiver->host = NULL;
+ transceiver->gadget = NULL;
+ transceiver->port_status = 0;
+ transceiver->port_change = 0;
+ transceiver->set_host = NULL;
+ transceiver->set_peripheral = au1200otg_set_peripheral;
+ transceiver->set_power = NULL;
+ transceiver->start_srp = NULL;
+ transceiver->start_hnp = NULL;
+}
+
+/**
+ * \brief
+ * OTG state change
+ *
+ * subset of OTG states to support the gadget only or
+ * ID pin configuration
+ *
+ * \param otg OTG controller info
+ * \param event_code event that requested a state change
+ * \param pEvt_mask
+ *
+ * \return events that were not handled here
+ */
+u32 otg_change_state(struct otg *otg, u32 _event, u32 *pEvt_mask)
+{
+ u32 event_code = _event;
+ u32 uoc_status = get_status(otg);
+
+ if (GOT_EVENT(OTG_GADGET_READY, event_code) &&
+ ((otg->transceiver.state & OTG_STATE_MASK) !=
+ OTG_STATE_UNDEFINED)) {
+
+ /* Switch from "NO_B_DEVICE" states to normal operation or */
+ /* deactivate operations in case gadget was unloaded */
+
+ CHANGE_STATE(otg, OTG_STATE_UNDEFINED, pEvt_mask);
+ }
+ if ((OTG_INT_TMX & event_code) && otg_tmr_high_count) {
+
+ /* a long timer is running : decrement the high part */
+
+ restart_timer(otg);
+ otg_tmr_high_count--;
+ RES_EVENT(OTG_INT_TMX, event_code);
+ }
+
+ do {
+ switch (otg->transceiver.state & OTG_STATE_MASK) {
+ /* NOT_ASSIGNED (yet): init state, 1st time after loading */
+ /* ====================================================== */
+
+ case OTG_STATE_UNDEFINED:
+
+ CHECK_STATE(otg, OTG_STATE_UNDEFINED, pEvt_mask);
+
+ if (IS_FLAG_RES(otg, OTG_FLAGS_ACTIV)) {
+
+ /* seems to be the first time: let it run ! */
+
+ SET_FLAG(otg, OTG_FLAGS_ACTIV);
+ }
+
+ /* muxer is still neutral */
+
+ RES_EVENT((OTG_INT_IDC | OTG_INT_TMX), event_code);
+
+ if (IS_FLAG_RES(otg, OTG_GADGET_READY)) {
+ if (IS_BIT_RES(OTG_STS_ID, uoc_status)) {
+
+ /* ID pin connected: A-device (host) */
+
+ if (OTG_STATE_NO_B_DEVICE_A !=
+ otg->transceiver.state)
+ CHANGE_STATE(otg,
+ OTG_STATE_NO_B_DEVICE_A,
+ pEvt_mask);
+ } else if (IS_BIT_SET(OTG_STS_ID, uoc_status)) {
+
+ /* ID pin not connected:
+ * disable(neutral)*/
+
+ if (OTG_STATE_NO_B_DEVICE_B !=
+ otg->transceiver.state)
+ CHANGE_STATE(otg,
+ OTG_STATE_NO_B_DEVICE_B,
+ pEvt_mask);
+ }
+ } else if ((OTG_STATE_NO_B_DEVICE_A ==
+ otg->transceiver.state) ||
+ (OTG_STATE_NO_B_DEVICE_B ==
+ otg->transceiver.state)) {
+
+ /* Exit "not ready" state */
+ /* ---------------------- */
+
+ RES_EVENT(OTG_GADGET_READY, event_code);
+ CHANGE_STATE(otg, OTG_STATE_UNDEFINED,
+ pEvt_mask);
+ }
+
+ /* ================================================== */
+
+ else {
+
+ /* ID pin is not connected: B-device
+ * (peripheral)*/
+
+ CHANGE_STATE(otg, OTG_STATE_B_IDLE, pEvt_mask);
+ }
+ break;
+
+ case OTG_STATE_B_IDLE:
+ /* B_IDLE: init state for B-devices */
+ /* monitor VBus, no connection, no activity */
+
+ CHECK_STATE(otg, OTG_STATE_B_IDLE, pEvt_mask);
+
+ if (IS_BIT_SET(OTG_STS_SESSVLD, uoc_status)) {
+ /* Session valid => B_PERIPHERAL */
+
+ RES_EVENT(OTG_INT_SVC, event_code);
+ CHANGE_STATE(otg, OTG_STATE_B_PERIPHERAL,
+ pEvt_mask);
+ if (otg_gadget_extension.notify) {
+ otg_gadget_extension.notify(
+ OTG_GADGET_EVT_SVALID);
+ }
+ }
+ break;
+
+ case OTG_STATE_B_PERIPHERAL:
+ /* B_PERIPHERAL: connected to A-host, responding */
+ /* VBus driven by A, remote activity */
+
+ CHECK_STATE(otg, OTG_STATE_B_PERIPHERAL, pEvt_mask);
+
+ if (IS_BIT_RES(OTG_STS_SESSVLD, uoc_status)) {
+ /* ID pin changed | ~Session valid => B_IDLE */
+
+ RES_EVENT((OTG_INT_IDC | OTG_INT_SVC),
+ event_code);
+ CHANGE_STATE(otg, OTG_STATE_B_IDLE, pEvt_mask);
+ if (otg_gadget_extension.notify) {
+ otg_gadget_extension.notify(
+ OTG_GADGET_EVT_SVDROP);
+ }
+ }
+ break;
+
+ default:
+ /* something went wrong */
+ BUG();
+ break;
+ }
+ } while ((otg->transceiver.state ^ otg->transceiver.prv_state) &
+ (OTG_STATE_MASK));
+
+
+ DBG("OTG-state change done\n");
+
+ return event_code;
+}
+
+/**
+ * \brief
+ * OTG state change request
+ *
+ * \param dev OTG device info
+ * \param params
+ *
+ * \return void
+ */
+static inline void otg_req_state_chg(struct otg *otg, u32 params)
+{
+ u32 temp, tmp2;
+ unsigned long flags;
+
+ local_irq_save(flags);
+
+ /* disable global OTG interrupt, clear int status: */
+ temp = ~((u32) OTG_INT_GLOBAL) & readl(&otg->regs->inten);
+ writel(temp, &otg->regs->inten);
+ tmp2 = readl(&otg->regs->intr);
+ writel(tmp2, &otg->regs->intr);
+ temp &= tmp2;
+
+ /* update OTG state: */
+ otg_change_state(otg, (params | temp), &temp);
+
+ /* enable global OTG interrupt: */
+ state_mask = ~temp & OTG_INT_ADDS;
+ writel((OTG_INT_ADDS | temp | OTG_INT_GLOBAL), &otg->regs->inten);
+ local_irq_restore(flags);
+}
+
+/**
+ * \brief
+ * OTG set transceiver:
+ *
+ * \param pointer to transceiver struct
+ *
+ * \return void
+ */
+int otg_set_transceiver(struct otg_transceiver *transceiver)
+{
+ struct otg *otg = the_controller;
+
+ if (unlikely(transceiver != otg_to_transceiver(otg))) {
+ ERR("USB OTG: unknown transceiver\n");
+ return -EINVAL;
+ } else
+ return 0;
+}
+
+/**
+ * \brief
+ * OTG get transceiver: provide info to others
+ *
+ * \param void
+ *
+ * \return pointer to transceiver struct
+ */
+struct otg_transceiver *otg_get_transceiver(void)
+{
+ return otg_to_transceiver(the_controller);
+}
+
+/**
+ * \brief
+ * Bind/unbind the OTG controller to/from usb gadget
+ *
+ * \param transceiver this transceiver
+ * \param gadget usb gadget info
+ *
+ * \return error code
+ */
+static int au1200otg_set_peripheral(struct otg_transceiver *transceiver,
+ struct usb_gadget *gadget)
+{
+ struct otg *otg = the_controller;
+ int flag = 0;
+
+ if (unlikely(transceiver != otg_to_transceiver(otg))) {
+ ERR("USB OTG: unknown transceiver\n");
+ return -EINVAL;
+ }
+ if (gadget) {
+ if (transceiver->gadget) {
+ ERR("USB gadget: OTG driver already registered\n");
+ return -EBUSY;
+ }
+ DBG("bind OTG driver to USB gadget\n");
+ transceiver->gadget = gadget;
+ SET_FLAG(otg, OTG_GADGET_READY | OTG_B_BUS_REQ);
+
+ /* Now checking consistence ... */
+ /* Depending on the driver loading sequence is possible */
+ /* that the "Load state defaults" function was already */
+ /* called so the state could be inconsistent. */
+ if (transceiver->host && !transceiver->host->is_b_host)
+ flag |= 1;
+ transceiver->gadget->is_a_peripheral = flag;
+
+ if (IS_FLAG_SET(otg, OTG_FLAGS_ACTIV))
+ otg_req_state_chg(otg, OTG_GADGET_READY);
+
+ if (IS_BIT_SET(OTG_STS_SESSVLD, readl(&otg->regs->sts)) &&
+ (otg->transceiver.state & OTG_STATE_MASK)
+ == OTG_STATE_B_PERIPHERAL) {
+ VDBG("calling gadget: connect\n");
+ otg_gadget_extension.notify(OTG_GADGET_EVT_SVALID);
+ }
+ return 0;
+ } else {
+ DBG("unbind OTG driver from USB gadget\n");
+ RES_FLAG(otg, OTG_GADGET_READY | OTG_B_BUS_REQ);
+ if (IS_FLAG_SET(otg, OTG_FLAGS_ACTIV))
+ otg_req_state_chg(otg, OTG_GADGET_READY);
+
+ transceiver->gadget = NULL;
+ return 0;
+ }
+}
+
+/**
+ * \brief
+ * OTG application query
+ *
+ * \param index select status info data
+ *
+ * \return status
+ */
+static u32 otg_app_query(int index)
+{
+ struct otg *otg = the_controller;
+ u32 temp = 0;
+
+ if (index == 0) {
+ temp = otg->transceiver.params |
+ readl(&otg->regs->sts);
+
+ if (((readl(&otg->regs->ctl) & OTG_CTL_MUX_MASK) ==
+ OTG_CTL_ENABLE_UDC) &&
+ ((temp & OTG_STS_PSUS) ||
+ (~temp & OTG_STS_VBUSVLD)))
+ temp |= OTG_FLAGS_UDC_SUSP;
+ } else if (index == 1)
+ temp = otg->transceiver.state;
+
+ return temp;
+}
+
+/**
+ * \brief
+ * OTG ISR calling the main state machine
+ *
+ * \param irq IRQ number
+ * \param _dev
+ * \param r
+ *
+ * \return IRQ_HANDLED(system code)
+ */
+static irqreturn_t otg_isr(int irq, void *dev)
+{
+ struct otg *otg = (struct otg *) dev;
+ u32 interrupts, int_mask, temp;
+
+ int_mask = readl(&otg->regs->inten);
+ if ((OTG_INT_GLOBAL & int_mask) &&
+ (int_mask &= ~((u32) OTG_INT_GLOBAL)) &&
+ (interrupts = int_mask &
+ (temp = readl(&otg->regs->intr)))) {
+
+ writel(int_mask, &otg->regs->inten);
+ /* clear interrupt status */
+ writel(temp, &otg->regs->intr);
+ /* filter out additional WA interrupts, they're done */
+ /* don't want to see them in the state machine */
+
+ if (interrupts & ~state_mask) {
+
+ /* events pending for the state machine */
+
+ otg_change_state(otg, (interrupts & ~state_mask),
+ &int_mask);
+ }
+
+ /* enable interrupts and keep information about WA ints: */
+
+ state_mask = OTG_INT_ADDS & ~int_mask;
+ writel((OTG_INT_ADDS | int_mask | OTG_INT_GLOBAL),
+ &otg->regs->inten);
+ }
+ return IRQ_HANDLED;
+}
+
+/**
+ * \brief
+ * OTG probe: init hardware, register the driver
+ *
+ * \param otg otg controller info
+ *
+ * \return success
+ */
+static inline int __init otg_probe(struct otg *otg)
+{
+ int retval;
+ u32 temp;
+ int i;
+
+ /* initialize the OTG controller */
+
+ VDBG("OTG init ...\n");
+
+#ifdef VERBOSE
+ /* print regs */
+ print_regs(otg);
+#endif
+ /* Make sure we'll remember the initial state */
+ init_state = readl(&otg->regs->ctl);
+ VDBG(" OTG init state was %08x\n", init_state);
+
+ /* turn on the OTG controller */
+ writel((init_state | OTG_CTL_PADEN), &otg->regs->ctl);
+
+ /* initialize flags */
+ otg->transceiver.params = 0;
+
+ /* make sure all interrupts are disabled */
+ writel(OTG_INT_DISALL, &otg->regs->inten);
+ writel(OTG_INT_ENALL, &otg->regs->intr);
+
+ /* Set multiplexer to neutral, get power control, drop VBus */
+
+ if (((init_state & OTG_CTL_MUX_MASK) == OTG_CTL_ENABLE_UHC) &&
+ ((((init_state & OTG_CTL_PPO) &&
+ (init_state & OTG_CTL_PPWR))) ||
+ ((~init_state & OTG_CTL_PPO) &&
+ (readl(&otg->regs->sts) & OTG_STS_SESSVLD)))) {
+
+ /* VBus still powered try to discharge VBus and set timer */
+
+ DBG("Setting init state, trying to discharge VBus ...\n");
+
+ for (i = 0; i < 4; i++) {
+ writel(TIMER_PERIOD, &otg->regs->tmr);
+ writel((OTG_CTL_PADEN | OTG_CTL_IDSNSEN |
+ OTG_CTL_PPO | OTG_CTL_DISCHRG |
+ OTG_CTL_TMR_UNCOND),
+ &otg->regs->ctl);
+ while (!(readl(&otg->regs->sts) & OTG_STS_TMH))
+ udelay(1);
+ }
+ writel((OTG_CTL_PADEN | OTG_CTL_IDSNSEN | OTG_CTL_PPO),
+ &otg->regs->ctl);
+ writel(OTG_INT_ENALL, &otg->regs->intr);
+#ifdef DEBUG
+ if (readl(&otg->regs->sts) & OTG_STS_SESSVLD)
+ DBG(" VBus still high, external host connected\n");
+ else
+ DBG(" VBus discharged\n");
+#endif
+ } else {
+ DBG("Setting init state\n");
+
+ writel((OTG_CTL_PADEN | OTG_CTL_IDSNSEN | OTG_CTL_PPO),
+ &otg->regs->ctl);
+ }
+
+ VDBG("OTG init done\n");
+
+ /* registering to the device driver */
+ if (usb_gadget_register_otg(otg_get_transceiver)) {
+ ERR("gadget driver registration failed\n");
+ retval = -ENODEV;
+ goto err1;
+ }
+
+ /* finally activate OTG functionality */
+ /* Enable timer interrupt, start timer, set state */
+
+ SET_OTG_TIMER(otg, IDSNS_WAIT);
+ CHANGE_STATE(otg, OTG_STATE_UNDEFINED, &temp);
+ CHECK_STATE(otg, OTG_STATE_UNDEFINED, &temp);
+
+ /* clear all interrupts before enable */
+ writel(readl(&otg->regs->intr), &otg->regs->intr);
+
+ state_mask = ~temp & OTG_INT_ADDS;
+ writel((OTG_INT_ADDS | temp | OTG_INT_GLOBAL), &otg->regs->inten);
+
+ DBG("OTG-HW initialized, now checking ID ...\n");
+
+ return 0;
+
+ usb_gadget_unregister_otg();
+err1:
+ return retval;
+}
+
+/**
+ * \brief
+ * OTG remove: deregister the driver, clean-up hardware
+ *
+ * \param otg otg controller info
+ *
+ * \return void
+ */
+static inline void __exit otg_remove(struct otg *otg)
+{
+ int muxer;
+
+ /* unregistering from the usb gadget */
+ usb_gadget_unregister_otg();
+
+ /* clean up the OTG controller */
+
+ /* Disable all interrupts */
+ writel(OTG_INT_DISALL, &otg->regs->inten);
+ writel(OTG_INT_ENALL, &otg->regs->intr);
+
+ /* reset state, terminate all connections */
+ CHANGE_STATE(otg, OTG_STATE_UNDEFINED, &state_mask);
+ CHECK_STATE(otg, OTG_STATE_UNDEFINED, &state_mask);
+ otg->transceiver.params = 0;
+
+ muxer = init_state & (OTG_CTL_ENABLE_UHC | OTG_CTL_ENABLE_UDC);
+
+
+ /* Don't assign the port to the device controller */
+
+ if (!(muxer ^ OTG_CTL_ENABLE_UDC)) {
+
+ init_state &= ~((u32)(OTG_CTL_MUX_MASK | OTG_CTL_PUEN));
+ muxer = OTG_CTL_DISABLE_ALL;
+ }
+ VDBG("OTG writing back corrected init state: %08x\n", init_state);
+
+ /* Now, that's the moment to remember */
+ /* Set dev muxer and pull up bits, turn off the OTG controller */
+
+ /* Turn off VBus */
+ writel(init_state, &otg->regs->ctl);
+
+ if (!(muxer ^ OTG_CTL_ENABLE_UHC))
+ INFO("disabling OTG-HW, port is assigned to host\n");
+ else if (!(muxer ^ OTG_CTL_ENABLE_UDC))
+ INFO("disabling OTG-HW, port is assigned to device\n");
+ else
+ INFO("disabling OTG-HW, port is not assigned\n");
+
+ VDBG("OTG exit: OTG-HW disabled\n");
+
+ if (!muxer)
+ INFO("OTG HW disabled, port is not assigned\n");
+}
+
+/**
+ * \brief
+ * OTG dev probe: enable, init controller hardware
+ *
+ * \param dev platform device info
+ *
+ * \return success
+ */
+static int __init otg_drv_probe(struct device *dev)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct otg *otg;
+ u32 resource, len, irq;
+ void *base;
+ int retval;
+ char buf[8] = {0, 0, 0, 0, 0, 0, 0, 0}, *bufp;
+
+ /* alloc, and start init */
+ otg = kmalloc(sizeof(struct otg), GFP_KERNEL);
+ if (!otg) {
+ ERR("couldn't allocate memory for OTG driver\n");
+ retval = -ENOMEM;
+ goto err1;
+ }
+ DBG("kmalloc: OTG driver: %p\n", otg);
+
+ /* hold global device pointer */
+ the_controller = otg;
+
+ memset(otg, 0, sizeof(struct otg));
+ spin_lock_init(&otg->lock);
+
+ if (pdev->resource[0].flags != IORESOURCE_MEM) {
+ ERR("resource is not IORESOURCE_MEM\n");
+ retval = -ENOMEM;
+ goto err2;
+ }
+ resource = pdev->resource[0].start;
+ len = pdev->resource[0].end + 1 - pdev->resource[0].start;
+ if (pdev->resource[1].flags != IORESOURCE_IRQ) {
+ ERR("resource is not IORESOURCE_IRQ\n");
+ retval = -ENOMEM;
+ goto err2;
+ }
+ irq = pdev->resource[1].start;
+
+ otg->pdev = pdev;
+
+ au_writel((au_readl(USB_MSR_BASE + USB_MSR_MCFG) |
+ (1 << USBMSRMCFG_GMEMEN)),
+ (USB_MSR_BASE + USB_MSR_MCFG));
+ au_readl(USB_MSR_BASE + USB_MSR_MCFG);
+ au_sync();
+
+ otg->enabled = 1;
+
+ if (!request_mem_region(resource, len, driver_name)) {
+ ERR("controller already in use\n");
+ retval = -EBUSY;
+ goto err3;
+ }
+ otg->region = 1;
+
+ base = ioremap_nocache(resource, len);
+ if (!base) {
+ ERR("couldn't map memory\n");
+ retval = -EFAULT;
+ goto err4;
+ }
+ otg->regs = (struct otg_regs *) base;
+ bufp = buf;
+
+ otg->chiprev = (u16) read_c0_prid() & 0xff;
+
+ /* OTG transceiver info */
+ otg->transceiver.dev = dev;
+ otg_init_transceiver(otg_to_transceiver(otg));
+
+ /* make sure all interrupts are disabled */
+ writel(OTG_INT_DISALL, &otg->regs->inten);
+ writel(OTG_INT_ENALL, &otg->regs->intr);
+ readl(&otg->regs->inten);
+
+ /* irq setup after old hardware is cleaned up */
+ if (!irq) {
+ ERR("No IRQ. Check system setup!\n");
+ retval = -ENODEV;
+ goto err5;
+ }
+ snprintf(buf, sizeof buf, "%d", irq);
+ bufp = buf;
+ if (request_irq(irq, otg_isr, IRQF_SHARED,
+ driver_name, otg) != 0) {
+ ERR("request interrupt %s failed\n", bufp);
+ retval = -EBUSY;
+ goto err5;
+ }
+ otg->got_irq = 1;
+
+ /* done */
+ INFO("%s\n", driver_desc);
+ INFO("irq %s, mem %08x, chip rev %02x (Au1200 %s)\n",
+ bufp, resource, otg->chiprev,
+ (otg->chiprev ? "AC" : "AB"));
+
+ retval = otg_probe(otg);
+ if (retval == 0) {
+ dev_set_drvdata(dev, otg);
+ return 0;
+ }
+
+ otg->got_irq = 0;
+ free_irq(irq, otg);
+err5:
+
+ otg->regs = NULL;
+ iounmap(base);
+err4:
+ otg->region = 0;
+ release_mem_region(resource, len);
+err3:
+ otg->enabled = 0;
+ au_writel((au_readl(USB_MSR_BASE + USB_MSR_MCFG) &
+ ~((u32)(1 << USBMSRMCFG_GMEMEN))),
+ (USB_MSR_BASE + USB_MSR_MCFG));
+ au_readl(USB_MSR_BASE + USB_MSR_MCFG);
+ /* au_sync(); */
+ udelay(1000);
+err2:
+ otg->pdev = NULL;
+ the_controller = NULL;
+ DBG("kfree: OTG driver: %p\n", otg);
+ kfree(otg);
+err1:
+ otg = NULL;
+
+ return retval;
+}
+
+/**
+ * \brief
+ * OTG dev remove: clean-up, disable controller hardware
+ *
+ * \param dev platform device info
+ *
+ * \return void
+ */
+static int __exit otg_drv_remove(struct device *dev)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct otg *otg = dev_get_drvdata(dev);
+
+ otg_remove(otg);
+
+ otg->got_irq = 0;
+ free_irq(pdev->resource[1].start, otg);
+ iounmap(otg->regs);
+ otg->regs = NULL;
+ otg->region = 0;
+ release_mem_region(pdev->resource[0].start,
+ pdev->resource[0].end + 1
+ - pdev->resource[0].start);
+ otg->enabled = 0;
+
+ au_readl(USB_MSR_BASE + USB_MSR_MCFG);
+ au_sync();
+
+ otg->pdev = NULL;
+ the_controller = NULL;
+ DBG("kfree: OTG driver: %p\n", otg);
+ kfree(otg);
+ otg = NULL;
+ dev_set_drvdata(dev, NULL);
+ return 0;
+}
+
+
+/*****************************************************************************
+ * More data
+ *****************************************************************************/
+
+/**
+ * \brief
+ * driver struct to be used for driver registration
+ *
+ */
+static struct device_driver otg_device_driver = {
+ .name = "au1xxx-uoc",
+ .bus = &platform_bus_type,
+ .probe = otg_drv_probe,
+ .remove = otg_drv_remove,
+ /* .suspend = otg_drv_suspend, */
+ /* .resume = otg_drv_resume, */
+};
+
+/* This comment closes the module definition from above. There can be multiple
+ definitions of this kind in a file. See the doxygen documentation for more
+ information. */
+/** \}*/
+
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_AUTHOR("Kevin Hickey");
+MODULE_LICENSE("GPL");
+
+static int __init init(void)
+{
+ return driver_register(&otg_device_driver);
+}
+static void __exit cleanup(void)
+{
+ driver_unregister(&otg_device_driver);
+}
+
+module_init(init);
+module_exit(cleanup);
diff --git a/drivers/usb/gadget/au1200_otg.h b/drivers/usb/gadget/au1200_otg.h
new file mode 100644
index 0000000..8c2e3a5
--- /dev/null
+++ b/drivers/usb/gadget/au1200_otg.h
@@ -0,0 +1,138 @@
+/*
+ * Declarations for the Au1200 On The Go port driver.
+ */
+
+/*
+ * Copyright (C) 2008 RMI Corporation (http://www.rmicorp.com)
+ * Author: Kevin Hickey (khickey@rmicorp.com)
+ *
+ * THIS SOFTWARE IS PROVIDED BY RMI Corporation 'AS IS' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL RMI OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
+ * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * 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.
+ *
+ * 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 AU1200_OTG_H
+#define AU1200_OTG_H
+
+/**********************************
+ * OTG sub-state definitions
+ ***********************************/
+
+#define OTG_STATE_MASK 0x0F
+
+#define OTG_STATE_NO_B_DEVICE_A (0x60 | OTG_STATE_UNDEFINED)
+#define OTG_STATE_NO_B_DEVICE_B (0x40 | OTG_STATE_UNDEFINED)
+
+#define OTG_STATE_B_HOST_WT (0x10 | OTG_STATE_B_HOST)
+#define OTG_STATE_B_PERIPHERAL_WT (0x10 | OTG_STATE_B_PERIPHERAL)
+#define OTG_STATE_B_PERIPHERAL_DC (0x20 | OTG_STATE_B_PERIPHERAL)
+#define OTG_STATE_B_SRP_WAIT_SE0 (0x10 | OTG_STATE_B_SRP_INIT)
+#define OTG_STATE_B_SRP_D_PULSE (0x20 | OTG_STATE_B_SRP_INIT)
+#define OTG_STATE_B_SRP_V_PULSE (0x30 | OTG_STATE_B_SRP_INIT)
+#define OTG_STATE_B_SRP_V_DCHRG (0x40 | OTG_STATE_B_SRP_INIT)
+#define OTG_STATE_B_SRP_WAIT_VBUS (0x50 | OTG_STATE_B_SRP_INIT)
+
+#define OTG_STATE_A_IDLE_WAIT_DP (0x10 | OTG_STATE_A_IDLE)
+#define OTG_STATE_A_IDLE_WAIT_VP (0x20 | OTG_STATE_A_IDLE)
+#define OTG_STATE_A_IDLE_WAIT_MP (0x30 | OTG_STATE_A_IDLE)
+#define OTG_STATE_A_IDLE_WAIT_DV (0x40 | OTG_STATE_A_IDLE)
+#define OTG_STATE_A_WAIT_BCON_VB (0x10 | OTG_STATE_A_WAIT_BCON)
+#define OTG_STATE_A_WAIT_VFALL_DN (0x10 | OTG_STATE_A_WAIT_VFALL)
+
+
+/**********************************
+ * typical timer values
+ **********************************/
+
+#define OTG_TMR_WAIT_VFALL 10 /* ( ) A waits for VBus */
+#define OTG_TMR_A_WAIT_VRISE 100 /* ( ) A waits for VBus */
+#define OTG_TMR_A_WAIT_BCON 200 /* ( ) A waits for B-connect (1.. s) */
+#define OTG_TMR_A_IDLE_BDIS 250 /* (ms) A waits for B-disc (200.. ms) */
+#define OTG_TMR_B_WAIT_ADISCON 600 /* (us) B waits for A to disconnect <1ms */
+#define OTG_TMR_B_ACON_BRST 200 /* (us) B waits before starting reset */
+#define OTG_TMR_B_ASE0_BRST 5 /* (ms) B waits for A-conn (3.125.. ms) */
+#define OTG_TMR_B_AIDL_BDIS 50 /* (ms) B waits before dc (5..150ms) */
+#define OTG_TMR_SRP_WAIT_SE0 2 /* ( ) B SRP idle wait */
+#define OTG_TMR_SRP_WAIT_DP 7 /* (ms) B SRP D_PULSE (5..10ms) */
+#define OTG_TMR_SRP_WAIT_VP 80 /* (ms) B SRP V_PULSE (5..100ms) */
+#define OTG_TMR_SRP_DCHRG_V 30 /* ( ) B SRP VBus discharge */
+#define OTG_TMR_SRP_WAIT_VRS 5800 /* (ms) B SRP waits for VBus (5..6s) */
+#define OTG_TMR_ASRP_WAIT_MP 4 /* ( ) A SRP min. pulse */
+#define OTG_TMR_ASRP_WAIT_DP 10 /* (ms) A SRP D_PULSE TO */
+#define OTG_TMR_ASRP_WAIT_VP 200 /* (ms) A SRP V_PULSE TO */
+#define OTG_TMR_ASRP_WAIT_DV 200 /* ( ) A SRP waits for V_PULSE */
+#define OTG_TMR_A_BCON_VB 50 /* ( ) A waits for VBus after connect */
+
+#define OTG_TMR_IDSNS_WAIT 10 /* (ms) ID sense wait */
+#define TIMER_PERIOD 1000 /* 10 ms, if longer than 10ms */
+
+/**********************************
+ * OTG state parameters
+ **********************************/
+
+#define OTG_HOST_READY (1<<20) /* indicates a USB host driver is */
+/* running */
+#define OTG_GADGET_READY (1<<21) /* indicates a USB gadget driver is */
+/* running */
+#define OTG_A_BUS_REQ (1<<22) /* used by appl-SW to request a */
+/* VBus rise, auto-reset by driver */
+#define OTG_A_BUS_DROP (1<<23) /* used by appl-SW to request a */
+/* VBus drop, auto-reset by driver */
+#define OTG_A_CLR_ERR (1<<24) /* used by appl-SW to request VBerr */
+/* clean-up, auto-reset by driver */
+#define OTG_AB_HNP_REQ (1<<25) /* used by appl-SW to initiate */
+/* HNP, auto-reset by driver */
+#define OTG_B_BUS_REQ (1<<26) /* used by appl-SW to request */
+/* B-device functionality, ... */
+#define OTG_B_BUS_DIS (1<<27) /* used by appl-SW to request */
+/* disable B-device functionality */
+#define OTG_B_aSSN_REQ (1<<28) /* used by appl-SW to initiate SRP, */
+/* auto-reset by the driver */
+#define OTG_B_SRP_ERROR (1<<29) /* indicates invalid HW conditions */
+/* during SRP, reset by writing "1" */
+#define OTG_A_VBUS_FAILED (1<<30) /* indicates a VBus error, reset by */
+/* writing "1", when setting */
+/* CLR_ERR or when leaving A-states */
+#define OTG_UDC_RWK_REQ (1<<31) /* call UDC function to force a */
+/* remote wake-up */
+
+#define SW_REQUEST_MASK (OTG_A_BUS_REQ | OTG_A_BUS_DROP | \
+ OTG_A_CLR_ERR | OTG_B_aSSN_REQ | \
+ OTG_B_BUS_REQ | OTG_B_BUS_DIS | \
+ OTG_UDC_RWK_REQ)
+
+/*********************************************************************/
+
+/*
+ * gadget events for notify function
+ */
+#define OTG_GADGET_EVT_SVDROP (1<<0) /* Session valid drop */
+#define OTG_GADGET_EVT_SVALID (1<<1) /* Session valid */
+#define OTG_GADGET_REQ_WAKE (1<<2) /* Request remote wake-up */
+#define OTG_FLAGS_UDC_SUSP (1<<17) /* gadget phy suspended */
+
+/*****************************************************************************
+ * Data
+ *****************************************************************************/
+struct usb_otg_gadget_extension {
+ int (*request) (u32); /* function call for state change requests */
+ u32 (*query) (int); /* function call to query state */
+ void (*notify) (u32); /* filled in by gadget for notification */
+};
+
+#endif /* AU1200_OTG_H */
diff --git a/drivers/usb/gadget/au1200_udc.c b/drivers/usb/gadget/au1200_udc.c
new file mode 100644
index 0000000..5289181
--- /dev/null
+++ b/drivers/usb/gadget/au1200_udc.c
@@ -0,0 +1,2862 @@
+/*
+ * RMI Au1200 UDC high/full speed USB device controller.
+ */
+
+/*
+ * Copyright (C) 2008 RMI Corporation (http://www.rmicorp.com)
+ * Author: Kevin Hickey (khickey@rmicorp.com)
+ *
+ * Adapted from the AMD5536 UDC module.
+ *
+ * THIS SOFTWARE IS PROVIDED BY RMI Corporation 'AS IS' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL RMI OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
+ * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * 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.
+ *
+ * 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
+ */
+
+/*****************************************************************************
+ * Defines
+ *****************************************************************************/
+
+/* debug control */
+/* #define UDC_DEBUG */
+/* #define UDC_VERBOSE */
+/* #define UDC_REGISTER_DUMP */
+
+/* Driver strings */
+#define UDC_MOD_DESCRIPTION "RMI Au1200 UDC - USB Device Controller"
+
+/*****************************************************************************
+ * Includes
+ *****************************************************************************/
+/* system */
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/version.h>
+#include <linux/delay.h>
+#include <linux/ioport.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/smp_lock.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/timer.h>
+#include <linux/list.h>
+#include <linux/interrupt.h>
+#include <linux/ioctl.h>
+#include <linux/fs.h>
+#include <linux/dma-mapping.h>
+#include <linux/dmapool.h>
+#include <linux/moduleparam.h>
+#include <linux/device.h>
+#include <linux/platform_device.h>
+#include <linux/io.h>
+#include <linux/irq.h>
+
+#include <asm/byteorder.h>
+#include <asm/system.h>
+#include <asm/unaligned.h>
+
+/* MIPS config */
+#include <asm/mach-au1x00/au1xxx.h>
+
+/* gadget stack */
+#include <linux/usb/ch9.h>
+#include <linux/usb/gadget.h>
+#include <linux/usb/otg.h>
+
+/* udc specific */
+#include "au1200_udc.h"
+
+/*****************************************************************************
+ * Static Function Declarations
+ *****************************************************************************/
+static void udc_tasklet_disconnect(unsigned long);
+static void empty_req_queue(struct udc_ep *);
+static int udc_probe(struct udc *dev);
+static void udc_basic_init(struct udc *dev);
+static void udc_setup_endpoints(struct udc *dev);
+static void udc_soft_reset(struct udc *dev);
+static void udc_free_request(struct usb_ep *usbep, struct usb_request *usbreq);
+static struct udc_data_dma *udc_get_last_dma_desc(struct udc_request *req);
+static int udc_free_dma_chain(struct udc *dev, struct udc_request *req);
+static inline int startup_registers(struct udc *dev);
+static int udc_remote_wakeup(struct udc *dev);
+static int udc_suspend(struct udc *dev);
+static int udc_resume(struct udc *dev);
+static void udc_clear_NAK(struct udc_ep *ep);
+
+static int execute_bulk_request_with_dma(struct usb_ep *usbep,
+ struct usb_request *usbreq, gfp_t gfp);
+
+static void udc_tasklet_execute_request(unsigned long);
+
+/*****************************************************************************
+ * Data
+ *****************************************************************************/
+/* description */
+static const char mod_desc[] = UDC_MOD_DESCRIPTION;
+static const char name[] = DRIVER_NAME_FOR_PRINT;
+
+/* structure to hold endpoint function pointers */
+static struct usb_ep_ops udc_ep_ops;
+
+/* received setup data */
+static union udc_setup_data setup_data;
+
+/* pointer to device object */
+static struct udc *udc;
+
+/* irq spin lock for soft reset */
+spinlock_t udc_irq_spinlock;
+/* stall spin lock */
+spinlock_t udc_stall_spinlock;
+
+/* this is used for dma chaining */
+static int udc_gfp_flags;
+
+/* count soft resets after suspend to avoid loop */
+static int soft_reset_occured;
+static int soft_reset_after_usbreset_occured;
+
+#ifdef UDC_USE_TIMER
+/* timer */
+static struct timer_list udc_timer;
+static int stop_timer;
+int set_rde = -1;
+DECLARE_COMPLETION(on_exit);
+static struct timer_list udc_pollstall_timer;
+static int stop_pollstall_timer;
+DECLARE_COMPLETION(on_pollstall_exit);
+#endif
+
+/* tasklet for usb disconnect */
+DECLARE_TASKLET(disconnect_tasklet, udc_tasklet_disconnect,
+ (unsigned long)&udc);
+
+/* otg registering count */
+static u32 otg_reg_count;
+
+/* gadget registering count */
+static u32 gadget_bind_count;
+
+/* endpoint names used for print */
+static const char ep0_string[] = "ep0in";
+static const char *ep_string[] = {
+ ep0_string,
+ "ep1in-int", "ep2in-bulk", "ep3in-bulk", "ep4in-bulk", "ep5in-bulk",
+ "ep6in-bulk", "ep7in-bulk", "ep8in-bulk", "ep9in-bulk", "ep10in-bulk",
+ "ep11in-bulk", "ep12in-bulk", "ep13in-bulk", "ep14in-bulk",
+ "ep15in-bulk", "ep0out", "ep1out-bulk", "ep2out-bulk", "ep3out-bulk",
+ "ep4out-bulk", "ep5out-bulk", "ep6out-bulk", "ep7out-bulk",
+ "ep8out-bulk", "ep9out-bulk", "ep10out-bulk", "ep11out-bulk",
+ "ep12out-bulk", "ep13out-bulk", "ep14out-bulk", "ep15out-bulk"
+};
+
+
+#ifdef UDC_DEBUG
+/* data for debuging only */
+static unsigned long no_pref_req;
+static unsigned long no_req;
+static u32 same_cfg;
+static u32 num_enums;
+#endif
+
+/****** following flags can be set by module parameters */
+/* DMA usage flag */
+static int use_dma = 1;
+/* packet per buffer dma */
+static int use_dma_ppb = 1;
+/* with per descr. update */
+static int use_dma_ppb_du;
+/* buffer fill mode */
+static int use_dma_bufferfill_mode;
+/* full speed only mode */
+static int use_fullspeed;
+/* tx buffer size for high speed */
+static unsigned long hs_tx_buf = UDC_EPIN_BUFF_SIZE;
+
+/* module parameters */
+module_param(use_dma, bool, S_IRUGO);
+MODULE_PARM_DESC(use_dma, "true for DMA");
+module_param(use_dma_ppb, bool, S_IRUGO);
+MODULE_PARM_DESC(use_dma_ppb, "true for DMA in packet per buffer mode");
+module_param(use_dma_ppb_du, bool, S_IRUGO);
+MODULE_PARM_DESC(use_dma_ppb_du,
+ "true for DMA in packet per buffer mode with descriptor update");
+module_param(use_fullspeed, bool, S_IRUGO);
+MODULE_PARM_DESC(use_fullspeed, "true for fullspeed only");
+module_param(hs_tx_buf, long, S_IRUGO);
+MODULE_PARM_DESC(hs_tx_buf,
+ "high speed tx buffer size for data endpoints in dwords");
+
+MODULE_DESCRIPTION(UDC_MOD_DESCRIPTION);
+MODULE_AUTHOR("Kevin Hickey");
+MODULE_LICENSE("GPL");
+
+/*****************************************************************************
+ * Function Definitions
+ *****************************************************************************/
+/* printing registers
--------------------------------------------------------*/
+/**
+ * Prints UDC device registers and endpoint irq registers
+ *
+ * \param dev pointer to device struct
+ */
+static void print_regs(struct udc *dev)
+{
+ DBG("------- Device registers -------\n");
+ DBG("dev config = %08lx\n", (unsigned long) dev->regs->cfg);
+ DBG("dev control = %08lx\n", (unsigned long) dev->regs->ctl);
+ DBG("dev status = %08lx\n", (unsigned long) dev->regs->sts);
+ DBG("\n");
+ DBG("dev int's = %08lx\n", (unsigned long) dev->regs->irqsts);
+ DBG("dev intmask = %08lx\n", (unsigned long) dev->regs->irqmsk);
+ DBG("\n");
+ DBG("dev ep int's = %08lx\n", (unsigned long) dev->regs->ep_irqsts);
+ DBG("dev ep intmask = %08lx\n", (unsigned long) dev->regs->ep_irqmsk);
+ DBG("\n");
+ DBG("USE DMA = %d\n", use_dma);
+ if (use_dma) {
+ DBG("DMA mode = ");
+ if (use_dma_ppb && !use_dma_ppb_du)
+ DBG("PPBNDU (packet per buffer w/o desc. update)\n");
+ else if (use_dma_ppb_du && use_dma_ppb_du)
+ DBG("PPBDU (packet per buffer with desc. update)\n");
+ if (use_dma_bufferfill_mode)
+ DBG("BF (buffer fill mode)\n");
+ }
+
+ if (!use_dma)
+ INFO("FIFO mode\n");
+#ifdef UDC_USE_TIMER
+ INFO("RDE timer is used\n");
+#endif
+ DBG("-------------------------------------------------------\n");
+}
+
+#ifdef UDC_DEBUG
+/**
+ * Prints misc information, to be removed
+ *
+ * \param dev pointer to device struct
+ */
+static void print_misc(struct udc *dev)
+{
+ print_regs(dev);
+
+ if (use_dma)
+ INFO("no_req=%ld no_pref_req=%ld\n", no_req, no_pref_req);
+}
+#endif
+
+/**
+ * Masks unused interrupts
+ *
+ * \param dev pointer to device struct
+ * \return 0 if success
+ */
+static int udc_mask_unused_interrupts(struct udc *dev)
+{
+ u32 tmp;
+
+ /* mask all dev interrupts */
+ tmp = UDC_BIT(UDC_DEVINT_ENUM) |
+ UDC_BIT(UDC_DEVINT_US) |
+ UDC_BIT(UDC_DEVINT_UR) |
+ UDC_BIT(UDC_DEVINT_ES) |
+ UDC_BIT(UDC_DEVINT_SI) |
+ UDC_BIT(UDC_DEVINT_SOF)|
+ UDC_BIT(UDC_DEVINT_SC);
+ iowrite32(tmp, &dev->regs->irqmsk);
+
+ /* mask all ep interrupts */
+ iowrite32(UDC_EPINT_MSK_DISABLE_ALL, &dev->regs->ep_irqmsk);
+
+ return 0;
+}
+
+/**
+ * Enables endpoint 0 interrupts
+ *
+ * \param dev pointer to device struct
+ * \return 0 if success
+ */
+static int udc_enable_ep0_interrupts(struct udc *dev)
+{
+ u32 tmp;
+
+ tmp = ioread32(&dev->regs->ep_irqmsk);
+ tmp &= ~(UDC_BIT(UDC_EPINT_IN_EP0) | UDC_BIT(UDC_EPINT_OUT_EP0));
+
+ iowrite32(tmp, &dev->regs->ep_irqmsk);
+
+ return 0;
+}
+
+/**
+ * Calculates fifo start of endpoint based on preceeding endpoints
+ *
+ * \param ep pointer to ep struct
+ * \return 0 if success
+ */
+static u32 *udc_calc_txfifo_addr(const struct udc *dev, unsigned ep_num)
+{
+ u32 tmp;
+ int i;
+ u32 *retval = dev->txfifo;
+
+ /* traverse ep's */
+ for (i = 0; i < ep_num; ++i) {
+ if (dev->ep[i].regs) {
+ /* read fifo size */
+ tmp = ioread32(&dev->ep[i].regs->bufin_framenum);
+ tmp = UDC_GETBITS(tmp, UDC_EPIN_BUFF_SIZE);
+ retval += tmp;
+ }
+ }
+ return retval;
+}
+
+/**
+ * Enables endpoint, is called by gadget driver
+ *
+ * \param usbep pointer to ep struct
+ * \param desc pointer to endpoint descriptor
+ * \return 0 if success
+ */
+static int udc_ep_enable(struct usb_ep *usbep,
+ const struct usb_endpoint_descriptor *desc)
+{
+ struct udc_ep *ep;
+ struct udc *dev;
+ u32 tmp;
+ unsigned long iflags;
+ u8 udc_csr_epix;
+
+ VDBG("udc_enable()\n");
+
+ ep = container_of(usbep, struct udc_ep, ep);
+ if (!usbep
+ || usbep->name == ep0_string
+ || !desc
+ || desc->bDescriptorType != USB_DT_ENDPOINT) {
+ ERR("udc_enable: !usbep=%d !desc=%d ep->desc!=NULL=%d \
+ usbep->name==ep0_string=%d \
+ desc->bDescriptorType!=USB_DT_ENDPOINT=%d\n",
+ !usbep, !desc, ep->desc != NULL,
+ usbep->name == ep0_string,
+ desc->bDescriptorType != USB_DT_ENDPOINT);
+ return -EINVAL;
+ }
+
+ dev = ep->dev;
+
+ /* exit on suspend */
+ if (dev->sys_suspended)
+ return -ESHUTDOWN;
+
+ if (!dev->driver || dev->gadget.speed == USB_SPEED_UNKNOWN)
+ return -ESHUTDOWN;
+
+ spin_lock_irqsave(&dev->lock, iflags);
+ ep->desc = desc;
+
+ ep->halted = 0;
+
+ /* set traffic type */
+ tmp = ioread32(&ep->regs->ctl);
+ tmp = UDC_ADDBITS(tmp, desc->bmAttributes, UDC_EPCTL_ET);
+ iowrite32(tmp, &ep->regs->ctl);
+
+ /* set max packet size */
+ tmp = ioread32(&ep->regs->bufout_maxpkt);
+ tmp = UDC_ADDBITS(tmp, desc->wMaxPacketSize, UDC_EP_MAX_PKT_SIZE);
+ ep->ep.maxpacket = desc->wMaxPacketSize;
+ iowrite32(tmp, &ep->regs->bufout_maxpkt);
+
+ /* IN ep */
+ if (ep->in) {
+ /* ep ix in UDC CSR register space */
+ udc_csr_epix = ep->num;
+
+ /* set buffer size (tx fifo entries) */
+ tmp = ioread32(&ep->regs->bufin_framenum);
+ /* double buffering: fifo size = 2 x max packet size */
+ tmp = UDC_ADDBITS(
+ tmp,
+ desc->wMaxPacketSize * UDC_EPIN_BUFF_SIZE_MULT /
+ UDC_DWORD_BYTES,
+ UDC_EPIN_BUFF_SIZE);
+ iowrite32(tmp, &ep->regs->bufin_framenum);
+
+ /* calc. tx fifo base addr */
+ ep->txfifo = udc_calc_txfifo_addr(dev, ep->num);
+
+ /* flush fifo */
+ tmp = ioread32(&ep->regs->ctl);
+ tmp |= UDC_BIT(UDC_EPCTL_F);
+ iowrite32(tmp, &ep->regs->ctl);
+
+ } /* OUT ep */
+ else {
+ /* ep ix in UDC CSR register space */
+ udc_csr_epix = ep->num - UDC_CSR_EP_OUT_IX_OFS;
+
+ if (ep->num != UDC_EP0OUT_IX)
+ dev->data_ep_enabled = 1;
+ }
+
+ /***** UDC CSR reg ****************************/
+ /* set ep values */
+ tmp = ioread32(&dev->csr->ne[udc_csr_epix]);
+ /* max packet */
+ tmp = UDC_ADDBITS(tmp, desc->wMaxPacketSize, UDC_CSR_NE_MAX_PKT);
+ /* ep number */
+ tmp = UDC_ADDBITS(tmp, desc->bEndpointAddress, UDC_CSR_NE_NUM);
+ /* ep direction */
+ tmp = UDC_ADDBITS(tmp, ep->in, UDC_CSR_NE_DIR);
+ /* ep type */
+ tmp = UDC_ADDBITS(tmp, desc->bmAttributes, UDC_CSR_NE_TYPE);
+ /* ep config */
+ tmp = UDC_ADDBITS(tmp, dev->cur_config, UDC_CSR_NE_CFG);
+ /* ep interface */
+ tmp = UDC_ADDBITS(tmp, dev->cur_intf, UDC_CSR_NE_INTF);
+ /* ep alt */
+ tmp = UDC_ADDBITS(tmp, dev->cur_alt, UDC_CSR_NE_ALT);
+ /* write reg */
+ iowrite32(tmp, &dev->csr->ne[udc_csr_epix]);
+
+ /* enable ep irq */
+ tmp = ioread32(&dev->regs->ep_irqmsk);
+ tmp &= UDC_UNMASK_BIT(ep->num);
+ iowrite32(tmp, &dev->regs->ep_irqmsk);
+
+ /* clear NAK by writing CNAK */
+ /* avoid BNA for OUT DMA, dont clear NAK until DMA desc. written */
+ if (ep->in)
+ udc_clear_NAK(ep);
+
+ DBG("%s enabled\n", usbep->name);
+
+ spin_unlock_irqrestore(&dev->lock, iflags);
+ return 0;
+}
+
+/**
+ * Enables device interrupts for SET_INTF and SET_CONFIG
+ *
+ * \param dev pointer to device struct
+ * \return 0 if success
+ */
+static int udc_enable_dev_setup_interrupts(struct udc *dev)
+{
+ u32 tmp;
+
+ /* read irq mask */
+ tmp = ioread32(&dev->regs->irqmsk);
+
+ /* enable SET_INTERFACE, SET_CONFIG and other needed irq's */
+ tmp &= UDC_UNMASK_BIT(UDC_DEVINT_SI)
+ & UDC_UNMASK_BIT(UDC_DEVINT_SC)
+ & UDC_UNMASK_BIT(UDC_DEVINT_UR)
+ & UDC_UNMASK_BIT(UDC_DEVINT_ENUM);
+ iowrite32(tmp, &dev->regs->irqmsk);
+
+ return 0;
+}
+
+/**
+ * Resets endpoint
+ *
+ * \param regs pointer to device register struct
+ * \param ep pointer to endpoint
+ */
+static void ep_init(struct udc_regs *regs, struct udc_ep *ep)
+{
+ u32 tmp;
+
+ VDBG("ep-%d reset\n", ep->num);
+ ep->desc = 0;
+ ep->ep.ops = &udc_ep_ops;
+ INIT_LIST_HEAD(&ep->queue);
+
+ ep->ep.maxpacket = (u16) ~0;
+ if (!(ep->dev->sys_suspended)) {
+ /* set NAK */
+ tmp = ioread32(&ep->regs->ctl);
+ tmp |= UDC_BIT(UDC_EPCTL_SNAK);
+ iowrite32(tmp, &ep->regs->ctl);
+ ep->naking = 1;
+
+ /* disable interrupt */
+ tmp = ioread32(®s->ep_irqmsk);
+ tmp |= UDC_BIT(ep->num);
+ iowrite32(tmp, ®s->ep_irqmsk);
+
+ if (ep->in) {
+ /* unset P and IN bit of potential former DMA */
+ tmp = ioread32(&ep->regs->ctl);
+ tmp &= UDC_UNMASK_BIT(UDC_EPCTL_P);
+ iowrite32(tmp, &ep->regs->ctl);
+
+ tmp = ioread32(&ep->regs->sts);
+ tmp |= UDC_BIT(UDC_EPSTS_IN);
+ iowrite32(tmp, &ep->regs->sts);
+
+ /* flush the fifo */
+ tmp = ioread32(&ep->regs->ctl);
+ tmp |= UDC_BIT(UDC_EPCTL_F);
+ iowrite32(tmp, &ep->regs->ctl);
+
+ }
+ /* reset desc pointer */
+ iowrite32(0, &ep->regs->desptr);
+ }
+
+
+}
+
+/**
+ * Disables endpoint, is called by gadget driver
+ *
+ * \param usbep pointer to ep struct
+ * \return 0 if success
+ */
+static int udc_disable(struct usb_ep *usbep)
+{
+ struct udc_ep *ep;
+ struct udc *dev;
+ unsigned long iflags;
+
+ if (!usbep)
+ return -EINVAL;
+
+ ep = container_of(usbep, struct udc_ep, ep);
+ dev = ep->dev;
+
+ if (usbep->name == ep0_string || !ep->desc)
+ return -EINVAL;
+
+ DBG("Disable %s\n", usbep->name);
+
+ spin_lock_irqsave(&dev->lock, iflags);
+ empty_req_queue(ep);
+ ep_init(dev->regs, ep);
+ spin_unlock_irqrestore(&dev->lock, iflags);
+
+ return 0;
+}
+
+/**
+ * Allocates request packet, called by gadget driver
+ */
+static struct usb_request *
+udc_alloc_request(struct usb_ep *usbep, gfp_t gfp)
+{
+ struct udc_request *req;
+ struct udc_data_dma *dma_desc;
+ struct udc_ep *ep;
+ struct udc *dev;
+
+ static int serial;
+
+ VDBG("udc_alloc_req()\n");
+ if (!usbep)
+ return 0;
+
+ ep = container_of(usbep, struct udc_ep, ep);
+ dev = ep->dev;
+ udc_gfp_flags = gfp;
+
+ req = kmalloc(sizeof(struct udc_request), gfp);
+ if (!req)
+ return 0;
+
+ memset(req, 0, sizeof *req);
+ req->req.dma = DMA_DONT_USE;
+ INIT_LIST_HEAD(&req->queue);
+ req->ready_for_p_bit = false;
+
+ req->serial_number = serial++;
+
+ /* KH TODO: Extract to function to be used by this
+ * and prepare_dma_chain. */
+ gfp = GFP_ATOMIC | GFP_DMA;
+ /* ep0 in requests are allocated from data pool here */
+ dma_desc = dma_pool_alloc(dev->data_requests, gfp,
+ &req->td_phys);
+ if (!dma_desc) {
+ kfree(req);
+ return 0;
+ }
+
+ VDBG("udc_alloc_req: req = %lx dma_desc = %lx, \
+ req->td_phys = %lx\n",
+ (unsigned long)req, (unsigned long) dma_desc,
+ (unsigned long)req->td_phys);
+ /* prevent from using desc. - set HOST BUSY */
+ dma_desc->status = UDC_ADDBITS(dma_desc->status,
+ UDC_DMA_STP_STS_BS_HOST_BUSY,
+ UDC_DMA_STP_STS_BS);
+ dma_desc->bufptr = __constant_cpu_to_le32(DMA_DONT_USE);
+ dma_desc->next = req->td_phys;
+ req->td_data = dma_desc;
+ req->chain_len = 1;
+
+ return &req->req;
+}
+
+/**
+ * Frees request packet, called by gadget driver
+ */
+static void udc_free_request(struct usb_ep *usbep, struct usb_request *usbreq)
+{
+ struct udc_ep *ep;
+ struct udc_request *req;
+
+ if (!usbep || !usbreq)
+ return;
+
+ ep = container_of(usbep, struct udc_ep, ep);
+ req = container_of(usbreq, struct udc_request, req);
+
+ WARN_ON(!list_empty(&req->queue));
+ if (req->td_data) {
+ if (req->chain_len > 1)
+ udc_free_dma_chain(ep->dev, req);
+
+ /* Free the first entry, not done by udc_free_dma_chain */
+ dma_pool_free(ep->dev->data_requests, req->td_data,
+ req->td_phys);
+ }
+ kfree(req);
+}
+
+/**
+ * Completes request packet
+ */
+static void
+complete_req(struct udc_ep *ep, struct udc_request *req, int sts)
+{
+ unsigned halted;
+
+ /* unmap DMA */
+ if (req->req.dma != DMA_DONT_USE) {
+ dma_unmap_single(0,
+ req->req.dma,
+ req->req.length,
+ ep->in ? DMA_TO_DEVICE : DMA_FROM_DEVICE);
+
+ req->req.dma = DMA_DONT_USE;
+ }
+
+ halted = ep->halted;
+ ep->halted = 1;
+
+ /* set new status if pending */
+ if (req->req.status == -EINPROGRESS)
+ req->req.status = sts;
+
+ list_del_init(&req->queue);
+
+ req->req.complete(&ep->ep, &req->req);
+
+ up(&ep->in_use);
+ ep->halted = halted;
+}
+
+/**
+ * Frees pci pool descriptors of a DMA chain
+ */
+static int udc_free_dma_chain(struct udc *dev, struct udc_request *req)
+{
+
+ int ret_val = 0;
+ struct udc_data_dma *td;
+ struct udc_data_dma *td_last = NULL;
+ unsigned int i;
+
+ /* Do not free first desc., will be done by free for request */
+ td_last = req->td_data;
+ td = phys_to_virt(td_last->next);
+
+ for (i = 1; i < req->chain_len; i++) {
+
+ dma_pool_free(dev->data_requests, td,
+ (dma_addr_t) td_last->next);
+ td_last = td;
+ td = phys_to_virt(td_last->next);
+ }
+
+ return ret_val;
+}
+
+/**
+ * Iterates to the end of a DMA chain and returns last descriptor
+ */
+static struct udc_data_dma *udc_get_last_dma_desc(struct udc_request *req)
+{
+ struct udc_data_dma *td;
+
+ td = req->td_data;
+ while (td && !(td->status & UDC_BIT(UDC_DMA_IN_STS_L)))
+ td = phys_to_virt(td->next);
+
+ return td;
+
+}
+
+static inline void udc_set_rde(struct udc *dev)
+{
+ UDC_SET_BIT(UDC_DEVCTL_RDE, &dev->regs->ctl);
+}
+
+/*
+ * Print a single DMA descriptor. Used by print_descriptor_chain.
+ */
+static void print_dma_descriptor(struct udc_data_dma *desc)
+{
+ INFO("DMA Descriptor:\n");
+ INFO("Address:0x%8.8x\n", (u32)desc);
+ INFO("Status: 0x%8.8x\n", desc->status);
+ INFO("Buffer: 0x%8.8x\n", desc->bufptr);
+ INFO("Next: 0x%8.8x\n", desc->next);
+}
+
+/*
+ * Walk and print a descriptor chain. Useful for debugging and error output
+ */
+static void print_descriptor_chain(struct udc_ep *ep)
+{
+ struct udc_data_dma *desc = phys_to_virt(ep->regs->desptr);
+
+ INFO("DMA Descriptor Chain (%s: 0x%8.8X)\n", ep->ep.name,
+ ep->regs->desptr);
+ print_dma_descriptor(desc);
+ while (desc && !(desc->status & UDC_BIT(UDC_DMA_IN_STS_L)) &&
+ (desc->next != ep->regs->desptr)) {
+ desc = (struct udc_data_dma *)(phys_to_virt(desc->next));
+ print_dma_descriptor(desc);
+ }
+
+}
+
+struct udc_data_dma *prepare_dma_chain(struct udc_ep *ep,
+ struct udc_request *req, gfp_t gfp)
+{
+ int i;
+ struct usb_ep *usbep = &ep->ep;
+ struct usb_request *usbreq = &req->req;
+ struct udc_data_dma *td = req->td_data;
+ struct udc_data_dma *next = NULL;
+ dma_addr_t dma_addr;
+
+ td->bufptr = usbreq->dma;
+ td->status = 0;
+ if (ep->in)
+ td->status = UDC_ADDBITS(td->status,
+ ep->ep.maxpacket,
+ UDC_DMA_IN_STS_TXBYTES);
+
+ for (i = usbep->maxpacket; i < usbreq->length; i += usbep->maxpacket) {
+ if (td->next == (u32)req->td_phys) {
+ next = dma_pool_alloc(ep->dev->data_requests,
+ gfp, &dma_addr);
+ if (next == NULL) {
+ ERR("%s allocation failed!\n", __func__);
+ return NULL;
+ }
+
+ ++req->chain_len;
+ /* Last points to first */
+ next->next = (u32)req->td_phys;
+ td->next = dma_addr;
+ } else {
+ next = (struct udc_data_dma *)phys_to_virt(td->next);
+ }
+
+ next->bufptr = usbreq->dma + i;
+ next->status = 0;
+ if (ep->in)
+ next->status = UDC_ADDBITS(next->status,
+ ep->ep.maxpacket,
+ UDC_DMA_IN_STS_TXBYTES);
+ td = next;
+ }
+
+ return td;
+}
+
+static void udc_clear_NAK(struct udc_ep *ep)
+{
+ u32 tmp = ioread32(&ep->regs->ctl);
+ int i = 0;
+
+ while (tmp & UDC_BIT(UDC_EPCTL_NAK)) {
+ tmp |= UDC_BIT(UDC_EPCTL_CNAK);
+ iowrite32(tmp, &ep->regs->ctl);
+ au_sync();
+
+ udelay(100);
+ tmp = ioread32(&ep->regs->ctl);
+ ++i;
+ if (i % 100 == 0)
+ INFO("Tried to CNAK %d times.\n", i);
+ }
+
+ ep->naking = 0;
+}
+
+static int execute_bulk_request_with_dma(struct usb_ep *usbep,
+ struct usb_request *usbreq, gfp_t gfp)
+{
+ int retval;
+ struct udc *dev;
+ struct udc_ep *ep;
+ struct udc_request *req;
+ unsigned long iflags;
+ struct udc_data_dma *td;
+
+ ep = container_of(usbep, struct udc_ep, ep);
+ req = container_of(usbreq, struct udc_request, req);
+ dev = ep->dev;
+ td = req->td_data; /* For notational convenience */
+
+ /* We don't want the isr to start acting on this request while we're
+ * setting it up. */
+ spin_lock_irqsave(&dev->lock, iflags);
+
+ usbreq->actual = 0;
+ usbreq->status = -EINPROGRESS;
+
+ /* Map the buffer allocated for the request into DMA space.
+ * Remember that the CPU should not access this memory until it is
+ * unmapped. */
+ if (usbreq->length > 0 && usbreq->dma == DMA_DONT_USE) {
+ usbreq->dma = dma_map_single(dev->pdev,
+ usbreq->buf,
+ usbreq->length,
+ ep->in ? DMA_TO_DEVICE : DMA_FROM_DEVICE);
+ }
+
+ td = prepare_dma_chain(ep, req, gfp);
+ if (td == NULL) {
+ retval = -ENOMEM;
+ goto finish;
+ }
+
+ if (ep->in && usbreq->length % usbep->maxpacket != 0)
+ td->status = UDC_ADDBITS(td->status,
+ usbreq->length % usbep->maxpacket,
+ UDC_DMA_IN_STS_TXBYTES);
+
+ td->status |= UDC_BIT(UDC_DMA_OUT_STS_L);
+
+ /* Set the endpoint descriptor register to start the transfer */
+ iowrite32((u32)req->td_phys, &ep->regs->desptr);
+ au_sync();
+
+ udc_clear_NAK(ep);
+
+finish:
+ spin_unlock_irqrestore(&dev->lock, iflags);
+ return retval;
+}
+
+static void udc_tasklet_execute_request(unsigned long ep_as_ul)
+{
+ struct udc_ep *ep = (struct udc_ep *)ep_as_ul;
+ struct udc_request *req;
+
+ down(&ep->in_et);
+
+ if (!list_empty(&ep->queue) && !down_trylock(&ep->in_use)) {
+ req = list_entry(ep->queue.next, struct udc_request, queue);
+
+ execute_bulk_request_with_dma(&ep->ep, &req->req,
+ udc_gfp_flags);
+ if (ep->in)
+ UDC_UNSET_BIT(ep->num, &ep->dev->regs->ep_irqmsk);
+ else
+ udc_set_rde(ep->dev);
+
+ /* Harmless to do on out EPs and saves a branch */
+ req->ready_for_p_bit = true;
+ }
+
+ up(&ep->in_et);
+}
+
+/**
+ * Queues a request packet, called by gadget driver
+ */
+static int udc_queue(struct usb_ep *usbep, struct usb_request *usbreq,
+ gfp_t gfp)
+{
+ int retval = 0;
+ unsigned long iflags = 0;
+ struct udc_ep *ep;
+ struct udc_request *req;
+ struct udc *dev;
+
+ /* check the inputs */
+
+ if (!usbep || !usbreq || !usbreq->complete || !usbreq->buf)
+ return -EINVAL;
+
+ req = container_of(usbreq, struct udc_request, req);
+ ep = container_of(usbep, struct udc_ep, ep);
+ dev = ep->dev;
+
+ if (!ep->desc && (ep->num != 0 && ep->num != UDC_EP0OUT_IX))
+ return -EINVAL;
+
+ /* exit on suspend */
+ if (dev->sys_suspended)
+ return -ESHUTDOWN;
+
+ if (!dev->driver || dev->gadget.speed == USB_SPEED_UNKNOWN)
+ return -ESHUTDOWN;
+
+ spin_lock_irqsave(&dev->lock, iflags);
+ if (ep->num != UDC_EP0OUT_IX && ep->num != UDC_EP0IN_IX) {
+ list_add_tail(&req->queue, &ep->queue);
+ tasklet_schedule(&ep->execute_tasklet);
+ goto finished;
+ } else {
+ if (usbreq->length > 0) {
+ list_add_tail(&req->queue, &ep->queue);
+ execute_bulk_request_with_dma(usbep, usbreq, gfp);
+ UDC_UNSET_BIT(ep->num, &dev->regs->ep_irqmsk);
+ goto finished;
+ } else {
+ /* IN zlp's are handled by hardware */
+ complete_req(ep, req, 0);
+ if (dev->set_cfg_not_acked) {
+ UDC_SET_BIT(UDC_DEVCTL_CSR_DONE,
+ &dev->regs->ctl);
+ dev->set_cfg_not_acked = 0;
+ }
+ goto finished;
+ }
+ }
+
+finished:
+ spin_unlock_irqrestore(&dev->lock, iflags);
+ return retval;
+}
+
+/**
+ * Empty request queue of an endpoint
+ */
+static void empty_req_queue(struct udc_ep *ep)
+{
+ struct udc_request *req;
+
+ ep->halted = 1;
+ while (!list_empty(&ep->queue)) {
+ req = list_entry(ep->queue.next,
+ struct udc_request,
+ queue);
+ complete_req(ep, req, -ESHUTDOWN);
+ }
+}
+
+/**
+ * Dequeues a request packet, called by gadget driver
+ */
+static int udc_dequeue(struct usb_ep *usbep, struct usb_request *usbreq)
+{
+ struct udc_ep *ep;
+ struct udc_request *req;
+ unsigned long iflags;
+
+ if (!usbep || !usbreq)
+ return -EINVAL;
+
+ ep = container_of(usbep, struct udc_ep, ep);
+ if (!ep->desc && (ep->num != 0 && ep->num != UDC_EP0OUT_IX))
+ return -EINVAL;
+
+ req = container_of(usbreq, struct udc_request, req);
+
+ spin_lock_irqsave(&ep->dev->lock, iflags);
+ complete_req(ep, req, -ECONNRESET);
+ spin_unlock_irqrestore(&ep->dev->lock, iflags);
+
+ return 0;
+}
+
+/**
+ * Halt or clear halt of endpoint, called by gadget driver
+ */
+static int udc_set_halt(struct usb_ep *usbep, int halt)
+{
+ struct udc_ep *ep;
+ unsigned long iflags;
+ int retval = 0;
+
+ if (!usbep)
+ return -EINVAL;
+
+ DBG("set_halt %s: halt=%d\n", usbep->name, halt);
+
+ /* TODO: DRY */
+ ep = container_of(usbep, struct udc_ep, ep);
+ if (!ep->desc && (ep->num != 0 && ep->num != UDC_EP0OUT_IX))
+ return -EINVAL;
+ if (!ep->dev->driver || ep->dev->gadget.speed == USB_SPEED_UNKNOWN)
+ return -ESHUTDOWN;
+ if (ep->dev->sys_suspended)
+ return -ESHUTDOWN;
+
+ spin_lock_irqsave(&udc_stall_spinlock, iflags);
+ /* halt or clear halt */
+ if (halt) {
+ if (ep->num != 0) {
+ UDC_SET_BIT(UDC_EPCTL_S, &ep->regs->ctl);
+ ep->halted = 1;
+ }
+ } else {
+ if (ep->halted) {
+ UDC_UNSET_BIT(UDC_EPCTL_S, &ep->regs->ctl);
+ udc_clear_NAK(ep);
+ ep->halted = 0;
+ }
+ }
+ spin_unlock_irqrestore(&udc_stall_spinlock, iflags);
+ return retval;
+}
+
+/**
+ * Return fifo fill state, called by gadget driver
+ * This is equivalent to unimplemented
+ */
+static int udc_fifo_status(struct usb_ep *usbep)
+{
+ return 0;
+}
+
+/**
+ * Flush the endpoint fifo, called by gadget driver
+ * This is equivalent to unimplemented
+ */
+static void udc_fifo_flush(struct usb_ep *usbep)
+{
+ return;
+}
+
+static struct usb_ep_ops udc_ep_ops = {
+ .enable = udc_ep_enable,
+ .disable = udc_disable,
+
+ .queue = udc_queue,
+ .dequeue = udc_dequeue,
+
+ .alloc_request = udc_alloc_request,
+ .free_request = udc_free_request,
+
+ .set_halt = udc_set_halt,
+ .fifo_status = udc_fifo_status,
+ .fifo_flush = udc_fifo_flush,
+};
+
+/*-------------------------------------------------------------------------*/
+
+/**
+ * Get frame count fifo, called by gadget driver
+ * This is equivalent to unimplemented
+ */
+static int udc_get_frame(struct usb_gadget *gadget)
+{
+ return 0;
+}
+
+/**
+ * Remote wakeup gadget interface
+ */
+static int udc_wakeup(struct usb_gadget *gadget)
+{
+ struct udc *dev;
+
+ if (!gadget)
+ return -EINVAL;
+ dev = container_of(gadget, struct udc, gadget);
+ udc_remote_wakeup(dev);
+
+ return 0;
+}
+
+/**
+ * gadget ioctl, used for OTG support notification
+ * \return 1 if OTG supported, else 0
+ */
+static int udc_gadget_ioctl(struct usb_gadget *gadget, unsigned cmd,
+ unsigned long par)
+{
+ struct udc *dev;
+ int retval = 0;
+ unsigned long iflags;
+ u32 tmp;
+
+ if (!gadget)
+ return -ENODEV;
+ dev = container_of(gadget, struct udc, gadget);
+ spin_lock_irqsave(&dev->lock, iflags);
+ tmp = ioread32(&dev->regs->cfg);
+
+ if (tmp & UDC_BIT(UDC_DEVCFG_HNPSFEN))
+ retval = 1;
+ else
+ retval = 0;
+ spin_unlock_irqrestore(&dev->lock, iflags);
+ return retval;
+}
+
+static const struct usb_gadget_ops udc_ops = {
+ .wakeup = udc_wakeup,
+ .get_frame = udc_get_frame,
+ .ioctl = udc_gadget_ioctl,
+};
+
+/**
+ * Setups endpoint parameters, adds endpoints to linked list
+ */
+static void make_ep_lists(struct udc *dev)
+{
+ /* make gadget ep lists */
+ INIT_LIST_HEAD(&dev->gadget.ep_list);
+ list_add_tail(&dev->ep[UDC_EPIN_STATUS_IX].ep.ep_list,
+ &dev->gadget.ep_list);
+ list_add_tail(&dev->ep[UDC_EPIN_IX].ep.ep_list, &dev->gadget.ep_list);
+ list_add_tail(&dev->ep[UDC_EPOUT_IX].ep.ep_list,
+ &dev->gadget.ep_list);
+
+ /* fifo config */
+ dev->ep[UDC_EPIN_STATUS_IX].fifo_depth = UDC_EPIN_SMALLINT_BUFF_SIZE;
+ if (dev->gadget.speed == USB_SPEED_FULL)
+ dev->ep[UDC_EPIN_IX].fifo_depth = UDC_FS_EPIN_BUFF_SIZE;
+ else if (dev->gadget.speed == USB_SPEED_HIGH)
+ dev->ep[UDC_EPIN_IX].fifo_depth = hs_tx_buf;
+ dev->ep[UDC_EPOUT_IX].fifo_depth = UDC_RXFIFO_SIZE;
+}
+
+/**
+ * Init registers at driver load time
+ */
+static int startup_registers(struct udc *dev)
+{
+ u32 tmp;
+ DBG("In startup_registers()\n");
+
+ /* init controller by soft reset */
+ udc_soft_reset(dev);
+
+ /* mask not needed interrupts */
+ udc_mask_unused_interrupts(dev);
+
+ /* put into initial config */
+ udc_basic_init(dev);
+ /* link up all endpoints */
+ udc_setup_endpoints(dev);
+
+ /* program speed */
+ tmp = ioread32(&dev->regs->cfg);
+ if (use_fullspeed)
+ tmp = UDC_ADDBITS(tmp, UDC_DEVCFG_SPD_FS, UDC_DEVCFG_SPD);
+ else
+ tmp = UDC_ADDBITS(tmp, UDC_DEVCFG_SPD_HS, UDC_DEVCFG_SPD);
+ iowrite32(tmp, &dev->regs->cfg);
+
+ DBG("After speed program\n");
+
+ return 0;
+}
+
+/**
+ * Inits UDC context
+ */
+static void udc_basic_init(struct udc *dev)
+{
+ dev->gadget.speed = USB_SPEED_UNKNOWN;
+
+ /* disable DMA */
+ UDC_UNSET_BIT(UDC_DEVCTL_RDE, &dev->regs->ctl);
+ UDC_UNSET_BIT(UDC_DEVCTL_TDE, &dev->regs->ctl);
+
+ /* enable dynamic CSR programming */
+ UDC_SET_BITS((UDC_BIT(UDC_DEVCFG_CSR_PRG) |
+ UDC_BIT(UDC_DEVCFG_SP) |
+ UDC_BIT(UDC_DEVCFG_RWKP)),
+ &dev->regs->cfg);
+
+ make_ep_lists(dev);
+
+ dev->data_ep_enabled = 0;
+ dev->data_ep_queued = 0;
+}
+
+/**
+ * Sets initial endpoint parameters
+ *
+ * \param dev pointer to device struct
+ */
+static void udc_setup_endpoints(struct udc *dev)
+{
+ struct udc_ep *ep;
+ u32 tmp;
+
+ DBG("udc_setup_endpoints()\n");
+
+ /* read enum speed */
+ tmp = ioread32(&dev->regs->sts);
+ tmp = UDC_GETBITS(tmp, UDC_DEVSTS_ENUM_SPEED);
+ if (tmp == UDC_DEVSTS_ENUM_SPEED_HIGH)
+ dev->gadget.speed = USB_SPEED_HIGH;
+ else if (tmp == UDC_DEVSTS_ENUM_SPEED_FULL)
+ dev->gadget.speed = USB_SPEED_FULL;
+
+ /* set basic ep parameters */
+ for (tmp = 0; tmp < UDC_EP_NUM; tmp++) {
+ ep = &dev->ep[tmp];
+ ep->dev = dev;
+ ep->ep.name = ep_string[tmp];
+ ep->num = tmp;
+ /* txfifo size is calculated at enable time */
+ ep->txfifo = dev->txfifo;
+
+ /* fifo size */
+ if (tmp < UDC_EPIN_NUM) {
+ ep->fifo_depth = UDC_TXFIFO_SIZE;
+ ep->in = 1;
+ } else {
+ ep->fifo_depth = UDC_RXFIFO_SIZE;
+ ep->in = 0;
+
+ }
+ ep->regs = &dev->ep_regs[tmp];
+ /* ep will be reset only if ep was not enabled before to avoid
+ disabling ep interrupts when ENUM interrupt occurs but ep is
+ not enabled by gadget driver */
+ if (!ep->desc)
+ ep_init(dev->regs, ep);
+
+ /* nak OUT endpoints until enable - not for ep0*/
+ if (tmp > UDC_EPIN_NUM) {
+ UDC_SET_BIT(UDC_EPCTL_SNAK, &ep->regs->ctl);
+ ep->naking = 1;
+ }
+ }
+
+ DBG("Done setting up ep params\n");
+
+ /* EP0 max packet */
+ if (dev->gadget.speed == USB_SPEED_FULL) {
+ dev->ep[UDC_EP0IN_IX].ep.maxpacket = UDC_FS_EP0IN_MAX_PKT_SIZE;
+ dev->ep[UDC_EP0OUT_IX].ep.maxpacket =
+ UDC_FS_EP0OUT_MAX_PKT_SIZE;
+ } else if (dev->gadget.speed == USB_SPEED_HIGH) {
+ dev->ep[UDC_EP0IN_IX].ep.maxpacket = UDC_EP0IN_MAX_PKT_SIZE;
+ dev->ep[UDC_EP0OUT_IX].ep.maxpacket = UDC_EP0OUT_MAX_PKT_SIZE;
+ }
+
+ DBG("Done setting up EP0 max packet\n");
+
+ /* with suspend bug workaround, ep0 params for gadget driver
+ are set at gadget driver bind() call */
+ dev->gadget.ep0 = &dev->ep[UDC_EP0IN_IX].ep;
+ dev->ep[UDC_EP0IN_IX].halted = 0;
+ INIT_LIST_HEAD(&dev->gadget.ep0->ep_list);
+
+ /* init cfg/alt/int */
+ dev->cur_config = 0;
+ dev->cur_intf = 0;
+ dev->cur_alt = 0;
+
+ DBG("udc_setup_endpoints done\n");
+}
+
+/**
+ * Bringup after Connect event,
+ * initial bringup to be ready for ep0 events
+ */
+static void usb_connect(struct udc *dev)
+{
+ INFO("USB Connect\n");
+
+ dev->connected = 1;
+
+ /* put into initial config */
+ udc_basic_init(dev);
+
+ /* enable device setup interrupts */
+ udc_enable_dev_setup_interrupts(dev);
+}
+
+/**
+ * Calls gadget with disconnect event and resets the UDC and makes
+ * initial bringup to be ready for ep0 events
+ */
+static void usb_disconnect(struct udc *dev)
+{
+ INFO("USB Disconnect\n");
+
+ dev->connected = 0;
+
+ /* mask interrupts */
+ udc_mask_unused_interrupts(dev);
+
+ tasklet_schedule(&disconnect_tasklet);
+}
+
+/**
+ * Tasklet for disconnect to be outside of interrupt
+ * context
+ */
+static void udc_tasklet_disconnect(unsigned long par)
+{
+ struct udc *dev = (struct udc *)(*((struct udc **) par));
+ u32 tmp;
+
+ DBG("Tasklet disconnect\n");
+ if (dev->driver) {
+ /* call gadget to reset configs etc. */
+ if (spin_is_locked(&dev->lock)) {
+ spin_unlock(&dev->lock);
+ dev->driver->disconnect(&dev->gadget);
+ spin_lock(&dev->lock);
+ } else
+ dev->driver->disconnect(&dev->gadget);
+
+ /* empty queues */
+ for (tmp = 0; tmp < UDC_EP_NUM; tmp++)
+ empty_req_queue(&dev->ep[tmp]);
+ }
+
+ /* disable ep0 */
+ ep_init(dev->regs, &dev->ep[UDC_EP0IN_IX]);
+
+ if (!soft_reset_occured) {
+ /* init controller by soft reset */
+ udc_soft_reset(dev);
+ soft_reset_occured++;
+ }
+ /* re-enable dev interrupts */
+ udc_enable_dev_setup_interrupts(dev);
+ /* back to full speed ? */
+ if (use_fullspeed) {
+ tmp = ioread32(&dev->regs->cfg);
+ tmp = UDC_ADDBITS(tmp, UDC_DEVCFG_SPD_FS, UDC_DEVCFG_SPD);
+ iowrite32(tmp, &dev->regs->cfg);
+ }
+}
+
+/**
+ * Reset the UDC core
+ */
+static void udc_soft_reset(struct udc *dev)
+{
+ DBG("Soft reset\n");
+ /* reset possible waiting interrupts, because int.
+ status is lost after soft reset */
+ /* ep int. status reset */
+ iowrite32(UDC_EPINT_MSK_DISABLE_ALL, &dev->regs->ep_irqsts);
+ /* device int. status reset */
+ iowrite32(UDC_DEV_MSK_DISABLE, &dev->regs->irqsts);
+
+ spin_lock_irq(&udc_irq_spinlock);
+ iowrite32(UDC_BIT(UDC_DEVCFG_SOFTRESET), &dev->regs->cfg);
+ ioread32(&dev->regs->cfg);
+ spin_unlock_irq(&udc_irq_spinlock);
+
+}
+
+/**
+ * Called by OTG driver to notify us regarding an OTG event
+ *
+ * \param code notify code
+ */
+void otg_notify(unsigned int code)
+{
+ DBG("OTG notify code=%d\n", code);
+ switch (code) {
+ case OTG_GADGET_EVT_SVDROP:
+ /* disconnect event */
+ usb_disconnect(udc);
+ break;
+ case OTG_GADGET_EVT_SVALID:
+ /* connect event */
+ usb_connect(udc);
+ break;
+ case OTG_GADGET_REQ_WAKE:
+ /* remote wakeup event */
+ udc_remote_wakeup(udc);
+ break;
+ }
+}
+
+/**
+ * Inits endpoint 0 so that SETUP packets are processed
+ *
+ * \param dev pointer to device struct
+ */
+static void activate_control_endpoints(struct udc *dev)
+{
+ u32 tmp;
+ struct udc_ep *ep0in = &dev->ep[UDC_EP0IN_IX];
+ struct udc_ep *ep0out = &dev->ep[UDC_EP0OUT_IX];
+
+ DBG("activate_control_endpoints\n");
+
+ /* flush fifo */
+ UDC_SET_BIT(UDC_EPCTL_F, &ep0in->regs->ctl);
+
+ /* set ep0 directions */
+ ep0in->in = 1;
+ ep0out->in = 0;
+
+ /* set buffer size (tx fifo entries) of EP0_IN */
+ tmp = ioread32(&ep0in->regs->bufin_framenum);
+ if (dev->gadget.speed == USB_SPEED_FULL)
+ tmp = UDC_ADDBITS(tmp, UDC_FS_EPIN0_BUFF_SIZE,
+ UDC_EPIN_BUFF_SIZE);
+ else if (dev->gadget.speed == USB_SPEED_HIGH)
+ tmp = UDC_ADDBITS(tmp, UDC_EPIN0_BUFF_SIZE, UDC_EPIN_BUFF_SIZE);
+ iowrite32(tmp, &ep0in->regs->bufin_framenum);
+
+ /* set max packet size of EP0_IN */
+ tmp = ioread32(&ep0in->regs->bufout_maxpkt);
+ if (dev->gadget.speed == USB_SPEED_FULL)
+ tmp = UDC_ADDBITS(tmp, UDC_FS_EP0IN_MAX_PKT_SIZE,
+ UDC_EP_MAX_PKT_SIZE);
+ else if (dev->gadget.speed == USB_SPEED_HIGH)
+ tmp = UDC_ADDBITS(tmp, UDC_EP0IN_MAX_PKT_SIZE,
+ UDC_EP_MAX_PKT_SIZE);
+ iowrite32(tmp, &ep0in->regs->bufout_maxpkt);
+
+ /* set max packet size of EP0_OUT */
+ tmp = ioread32(&ep0out->regs->bufout_maxpkt);
+ if (dev->gadget.speed == USB_SPEED_FULL)
+ tmp = UDC_ADDBITS(tmp, UDC_FS_EP0OUT_MAX_PKT_SIZE,
+ UDC_EP_MAX_PKT_SIZE);
+ else if (dev->gadget.speed == USB_SPEED_HIGH)
+ tmp = UDC_ADDBITS(tmp, UDC_EP0OUT_MAX_PKT_SIZE,
+ UDC_EP_MAX_PKT_SIZE);
+ iowrite32(tmp, &ep0out->regs->bufout_maxpkt);
+
+ /* set max packet size of EP0 in UDC CSR */
+ tmp = ioread32(&dev->csr->ne[0]);
+ if (dev->gadget.speed == USB_SPEED_FULL)
+ tmp = UDC_ADDBITS(tmp, UDC_FS_EP0OUT_MAX_PKT_SIZE,
+ UDC_CSR_NE_MAX_PKT);
+ else if (dev->gadget.speed == USB_SPEED_HIGH)
+ tmp = UDC_ADDBITS(tmp, UDC_EP0OUT_MAX_PKT_SIZE,
+ UDC_CSR_NE_MAX_PKT);
+ iowrite32(tmp, &dev->csr->ne[0]);
+
+ ep0out->td->status |= UDC_BIT(UDC_DMA_OUT_STS_L);
+ /* write dma desc address */
+ iowrite32(ep0out->td_stp_dma, &ep0out->regs->subptr);
+ iowrite32(ep0out->td_phys, &ep0out->regs->desptr);
+ /* enable DMA */
+ UDC_SET_BITS((UDC_BIT(UDC_DEVCTL_MODE)
+ | UDC_BIT(UDC_DEVCTL_RDE)
+ | UDC_BIT(UDC_DEVCTL_TDE)),
+ &dev->regs->ctl);
+
+ if (use_dma_bufferfill_mode)
+ UDC_SET_BIT(UDC_DEVCTL_BF, &dev->regs->ctl);
+ else if (use_dma_ppb_du)
+ UDC_SET_BIT(UDC_DEVCTL_DU, &dev->regs->ctl);
+
+ /* clear NAK by writing CNAK for EP0IN */
+ udc_clear_NAK(ep0in);
+ udc_clear_NAK(ep0out);
+}
+
+/**
+ * \brief
+ * Make endpoint 0 ready for control traffic
+ */
+static int setup_ep0(struct udc *dev)
+{
+ activate_control_endpoints(dev);
+ /* enable ep0 interrupts */
+ udc_enable_ep0_interrupts(dev);
+ /* enable device setup interrupts */
+ udc_enable_dev_setup_interrupts(dev);
+
+ return 0;
+}
+
+/**
+ * Called by gadget driver to register itself
+ */
+int usb_gadget_register_driver(struct usb_gadget_driver *driver)
+{
+ struct udc *dev = udc;
+ int retval;
+ u32 tmp;
+
+ DBG("In usb_gadget_register_driver\n");
+
+ DBG("Driver speed is %d\n", driver->speed);
+ if (!driver || !driver->bind
+ || !driver->unbind
+ || !driver->setup
+ || driver->speed != USB_SPEED_HIGH)
+ return -EINVAL;
+ if (!dev)
+ return -ENODEV;
+ if (dev->driver)
+ return -EBUSY;
+
+ driver->driver.bus = 0;
+ dev->driver = driver;
+ dev->gadget.dev.driver = &driver->driver;
+
+ device_create_file(&dev->pdev->dev, &dev_attr_function);
+ device_create_file(&dev->pdev->dev, &dev_attr_queues);
+
+#ifdef CONFIG_USB_OTG
+ DBG("Gadget is OTG\n");
+ dev->gadget.is_otg = 1;
+#endif
+ retval = driver->bind(&dev->gadget);
+ /* e.g. ether gadget needs driver_data on both ep0 endpoints */
+ dev->ep[UDC_EP0OUT_IX].ep.driver_data =
+ dev->ep[UDC_EP0IN_IX].ep.driver_data;
+
+ gadget_bind_count++;
+ if (retval) {
+ DBG("binding to %s returning %d\n",
+ driver->driver.name, retval);
+ dev->driver = 0;
+ dev->gadget.dev.driver = 0;
+ return retval;
+ } else {
+ DBG("Binding successful\n");
+ }
+
+ /* if otg driver already registered */
+ /* call otg bind() to mux udc to phy */
+ if (dev->otg_transceiver) {
+ dev->otg_transceiver->set_peripheral(
+ dev->otg_transceiver, &dev->gadget);
+ /* clear SD */
+ tmp = ioread32(&dev->regs->ctl);
+ tmp = tmp & UDC_CLEAR_BIT(UDC_DEVCTL_SD);
+ iowrite32(tmp, &dev->regs->ctl);
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL(usb_gadget_register_driver);
+
+/**
+ * Called by OTG driver to register itself
+ */
+int usb_gadget_register_otg(struct otg_transceiver *(*get_transceiver)(void))
+{
+ struct udc *dev = udc;
+ int retval;
+ u32 tmp;
+
+ if (!get_transceiver)
+ return -EINVAL;
+ if (!dev)
+ return -ENODEV;
+ if (dev->otg_transceiver)
+ return -EBUSY;
+
+ dev->otg_transceiver = get_transceiver();
+
+ if (!dev->otg_transceiver->otg_priv)
+ return -EINVAL;
+ dev->otg_driver = (struct usb_otg_gadget_extension *)
+ dev->otg_transceiver->otg_priv;
+
+ /* init registers here first with suspend bug */
+ if (!otg_reg_count) {
+ startup_registers(dev);
+ otg_reg_count++;
+ }
+
+ /* set notify function */
+ dev->otg_driver->notify = otg_notify;
+ DBG("otg_driver->notify set.\n");
+ /* if gadget driver already registered */
+ /* call gadget bind() to switch to mux udc to phy */
+ if (dev->driver) {
+ /* otg driver bind() */
+ retval = dev->otg_transceiver->set_peripheral(
+ dev->otg_transceiver, &dev->gadget);
+ if (retval) {
+ DBG("error bind to uoc driver\n");
+ dev->otg_driver = NULL;
+ dev->otg_transceiver = NULL;
+ return retval;
+ }
+ /* get ready for ep0 traffic */
+ setup_ep0(dev);
+
+ /* clear SD */
+ tmp = ioread32(&dev->regs->ctl);
+ tmp = tmp & UDC_CLEAR_BIT(UDC_DEVCTL_SD);
+ iowrite32(tmp, &dev->regs->ctl);
+ }
+
+ INFO("registered uoc driver\n");
+
+ return 0;
+}
+EXPORT_SYMBOL(usb_gadget_register_otg);
+
+/**
+ * Called by OTG driver to unregister itself
+ */
+int usb_gadget_unregister_otg(void)
+{
+ struct udc *dev = udc;
+ unsigned long flags;
+
+ if (!dev)
+ return -ENODEV;
+
+ spin_lock_irqsave(&dev->lock, flags);
+
+ /* mask not needed interrupts */
+ udc_mask_unused_interrupts(dev);
+
+ spin_unlock_irqrestore(&dev->lock, flags);
+
+ dev->otg_supported = 0;
+ if (dev->otg_transceiver) {
+ dev->otg_transceiver->set_peripheral(dev->otg_transceiver,
+ NULL);
+ dev->otg_transceiver = NULL;
+ }
+ if (dev->otg_driver) {
+ dev->otg_driver->notify = NULL;
+ dev->otg_driver = NULL;
+ }
+
+ /* set SD */
+ UDC_SET_BIT(UDC_DEVCTL_SD, &dev->regs->ctl);
+
+ DBG("unregistered uoc driver\n");
+ return 0;
+}
+EXPORT_SYMBOL(usb_gadget_unregister_otg);
+
+/**
+ * shutdown requests and disconnect from gadget
+ */
+static void shutdown(struct udc *dev, struct usb_gadget_driver *driver)
+{
+ int tmp;
+
+ /* empty queues and init hardware */
+ udc_basic_init(dev);
+ for (tmp = 0; tmp < UDC_EP_NUM; tmp++)
+ empty_req_queue(&dev->ep[tmp]);
+
+ if (dev->gadget.speed != USB_SPEED_UNKNOWN)
+ driver->disconnect(&dev->gadget);
+ udc_setup_endpoints(dev);
+}
+
+/**
+ * Called by gadget driver to unregister itself
+ */
+int usb_gadget_unregister_driver(struct usb_gadget_driver *driver)
+{
+ struct udc *dev = udc;
+ unsigned long iflags;
+
+ if (!dev)
+ return -ENODEV;
+ if (!driver || driver != dev->driver)
+ return -EINVAL;
+ if (gadget_bind_count) {
+ spin_lock_irqsave(&dev->lock, iflags);
+ shutdown(dev, driver);
+ spin_unlock_irqrestore(&dev->lock, iflags);
+ }
+
+ /* unbind from otg driver first */
+ if (dev->otg_transceiver) {
+ dev->otg_transceiver->set_peripheral(
+ dev->otg_transceiver, NULL);
+ }
+
+ if (gadget_bind_count)
+ driver->unbind(&dev->gadget);
+
+ gadget_bind_count = 0;
+ dev->driver = 0;
+
+ /* set SD */
+ UDC_SET_BIT(UDC_DEVCTL_SD, &dev->regs->ctl);
+ DBG("%s: unregistered\n", driver->driver.name);
+
+ return 0;
+}
+EXPORT_SYMBOL(usb_gadget_unregister_driver);
+
+void update_req_count(struct udc_request *req)
+{
+ struct udc_data_dma *last_desc;
+ unsigned int count;
+ unsigned int tmp;
+
+ last_desc = udc_get_last_dma_desc(req);
+ count = UDC_GETBITS(last_desc->status, UDC_DMA_OUT_STS_RXBYTES);
+ if (count == 0) {
+ /* on 64k packets the RXBYTES field is zero */
+ if (req->req.length == UDC_DMA_MAXPACKET)
+ count = UDC_DMA_MAXPACKET;
+ }
+
+ VDBG("Received %lx bytes\n", (unsigned long) count);
+
+ tmp = req->req.length - req->req.actual;
+ if (count > tmp) {
+ ERR("Buffer overrun!\n");
+ req->req.status = -EOVERFLOW;
+ count = tmp;
+ }
+
+ req->req.actual += count;
+}
+
+/**
+ * Check for and clear BNA and Hardware errors
+ * returns nonzero if any errors were found
+ */
+static inline int check_and_clear_errors(struct udc_ep *ep)
+{
+ u32 epsts;
+
+ epsts = ioread32(&ep->regs->sts);
+ /* BNA event */
+ if (epsts & UDC_BIT(UDC_EPSTS_BNA)) {
+ ERR("BNA occurred - %s: desptr = 0x%8.8x\n", ep->ep.name,
+ ep->regs->desptr);
+ UDC_SET_BIT(UDC_EPSTS_BNA, &ep->regs->sts);
+ return 1;
+ }
+
+ /* HE event */
+ if (epsts & UDC_BIT(UDC_EPSTS_HE)) {
+ ERR("HE occured on %s\n", ep->ep.name);
+ UDC_SET_BIT(UDC_EPSTS_HE, &ep->regs->sts);
+ return 1;
+ }
+
+ return 0;
+}
+
+/**
+ * Interrupt handler for data OUT traffic
+ */
+static inline int udc_data_out_isr(struct udc *dev, int ep_ix)
+{
+ int ret_val = 0;
+ u32 tmp;
+ struct udc_ep *ep;
+ struct udc_request *req;
+ unsigned long iflags;
+ struct udc_data_dma *last_desc;
+ unsigned dma_done;
+
+ VDBG("ep%d irq\n", ep_ix);
+ ep = &dev->ep[ep_ix];
+
+ spin_lock_irqsave(&dev->lock, iflags);
+
+ tmp = ioread32(&ep->regs->sts);
+ /* BNA event ? */
+ if (tmp & UDC_BIT(UDC_EPSTS_BNA)) {
+ ERR("BNA occurred - %s: desptr = 0x%8.8x\n", ep->ep.name,
+ ep->regs->desptr);
+ /* clear BNA */
+ ep->regs->sts = UDC_BIT(UDC_EPSTS_BNA);
+ au_sync();
+ goto finished;
+ }
+
+ /* HE event ? */
+ if (tmp & UDC_BIT(UDC_EPSTS_HE)) {
+ ERR("HE occured on %s\n", ep->ep.name);
+
+ /* clear HE */
+ ep->regs->sts = UDC_BIT(UDC_EPSTS_HE);
+ au_sync();
+
+ ret_val = 1;
+ goto finished;
+ }
+
+ /*
+ epsts = ioread32(&ep->regs->sts);
+ if (check_and_clear_errors(ep))
+ goto finished;
+ */
+
+ if (!list_empty(&ep->queue))
+ req = list_entry(ep->queue.next, struct udc_request, queue);
+ else {
+ INFO("In %s but there is no queued request.\n", __func__);
+ goto finished;
+ }
+
+ /* check for DMA done */
+ last_desc = udc_get_last_dma_desc(req);
+ dma_done = UDC_GETBITS(last_desc->status, UDC_DMA_OUT_STS_BS);
+
+ if (dma_done == UDC_DMA_OUT_STS_BS_DMA_DONE) {
+ update_req_count(req);
+ complete_req(ep, req, 0);
+ tasklet_schedule(&ep->execute_tasklet);
+ } else {
+ INFO("Got a DMA done interrupt but DMA is not done.\n");
+ print_descriptor_chain(ep);
+ }
+
+finished:
+ /* clear OUT bits in ep status */
+ ep->regs->sts = UDC_EPSTS_OUT_CLEAR;
+ au_sync();
+
+ spin_unlock_irqrestore(&dev->lock, iflags);
+ return ret_val;
+}
+
+/**
+ * Interrupt handler for data IN traffic
+ */
+static inline int udc_data_in_isr(struct udc *dev, int ep_ix)
+{
+ int ret_val = 0;
+ u32 epsts;
+ struct udc_ep *ep;
+ unsigned long iflags;
+ struct udc_request *req;
+
+ spin_lock_irqsave(&dev->lock, iflags);
+
+ ep = &dev->ep[ep_ix];
+ epsts = ioread32(&ep->regs->sts);
+
+ /* BNA */
+ if (epsts & UDC_BIT(UDC_EPSTS_BNA)) {
+ ERR("BNA ep%din occured - DESPTR = %08lx \n", ep->num,
+ (long unsigned int)ep->regs->desptr);
+
+ /* clear BNA */
+ ep->regs->sts = UDC_BIT(UDC_EPSTS_BNA);
+ au_sync();
+
+ goto finished;
+ }
+
+ /* HE event */
+ if (epsts & UDC_BIT(UDC_EPSTS_HE)) {
+ ERR("HE occured on %s\n", ep->ep.name);
+
+ /* clear HE */
+ ep->regs->sts = UDC_BIT(UDC_EPSTS_HE);
+ au_sync();
+
+ ret_val = 1;
+ goto finished;
+ }
+ /*
+ epsts = ioread32(&ep->regs->sts);
+ if (check_and_clear_errors(ep))
+ goto finished;
+ */
+
+ if (!list_empty(&ep->queue)) {
+ req = list_entry(ep->queue.next, struct udc_request, queue);
+ } else {
+ /* This is not all that unusual - the host can be greedy when
+ * it wants IN data and might beat the gadget to queueing a
+ * request.
+ */
+ goto finished;
+ }
+
+ /* DMA completion */
+ if (epsts & UDC_BIT(UDC_EPSTS_TDC)) {
+ /* Disable this IRQ to prevent flooding */
+ dev->regs->ep_irqmsk |= UDC_BIT(ep->num);
+ au_sync();
+
+ complete_req(ep, req, 0);
+ tasklet_schedule(&ep->execute_tasklet);
+ }
+
+ if (epsts & UDC_BIT(UDC_EPSTS_IN)) {
+ /* set poll demand bit */
+ if (req->ready_for_p_bit) {
+ req->ready_for_p_bit = false;
+ ep->regs->ctl |= UDC_BIT(UDC_EPCTL_P);
+ au_sync();
+ }
+ }
+
+finished:
+ /* clear status bits */
+ UDC_SET_BITS(epsts, &ep->regs->sts);
+ spin_unlock_irqrestore(&dev->lock, iflags);
+ return ret_val;
+}
+
+/**
+ * Interrupt handler for Control OUT traffic
+ *
+ * \param dev pointer to UDC device object
+ * \return 0 if success
+ */
+static inline int udc_control_out_isr(struct udc *dev)
+{
+ int ret_val = 0;
+ u32 tmp;
+ int setup_supported;
+ struct udc_ep *ep0in;
+ struct udc_ep *ep0out;
+
+ ep0out = &dev->ep[UDC_EP0OUT_IX];
+ ep0in = &dev->ep[UDC_EP0IN_IX];
+
+ /* clear irq */
+ UDC_SET_BIT(UDC_EPINT_OUT_EP0, &dev->regs->ep_irqsts);
+ if (check_and_clear_errors(ep0out))
+ goto finished;
+
+ tmp = ep0out->regs->sts;
+
+ /* type of data: SETUP or DATA 0 bytes */
+ tmp = UDC_GETBITS(tmp, UDC_EPSTS_OUT);
+ /* setup data */
+ if (tmp == UDC_EPSTS_OUT_SETUP) {
+ dev->waiting_zlp_ack_ep0in = 0;
+
+ ep0out->regs->sts |= UDC_EPSTS_OUT_CLEAR;
+
+ setup_data.data[0] = dev->ep[UDC_EP0OUT_IX].td_stp->data12;
+ setup_data.data[1] = dev->ep[UDC_EP0OUT_IX].td_stp->data34;
+ ep0out->td_stp->status = UDC_DMA_STP_STS_BS_HOST_READY;
+
+ /* determine direction of control data */
+ if ((setup_data.request.bRequestType & USB_DIR_IN) == 0) {
+ dev->gadget.ep0 = &dev->ep[UDC_EP0OUT_IX].ep;
+ } else {
+ dev->gadget.ep0 = &dev->ep[UDC_EP0IN_IX].ep;
+ udc_set_rde(dev);
+ }
+
+ setup_supported = dev->driver->setup(&dev->gadget,
+ &setup_data.request);
+
+ tmp = ioread32(&dev->ep[UDC_EP0IN_IX].regs->ctl);
+
+ if (setup_supported >= 0 &&
+ setup_supported < UDC_EP0IN_MAXPACKET) {
+ ep0in->regs->ctl |= UDC_BIT(UDC_EPCTL_CNAK);
+ } else if (setup_supported < 0) {
+ /* if unsupported request then stall */
+ ep0in->regs->ctl |= UDC_BIT(UDC_EPCTL_S);
+ au_sync();
+ }
+
+ ep0out->regs->ctl |= UDC_BIT(UDC_EPCTL_CNAK);
+ au_sync();
+ } else if (tmp == UDC_EPSTS_OUT_DATA) {
+ /* no req if 0 packet, just reactivate */
+ if (list_empty(&ep0out->queue)) {
+ ep0out->td->status =
+ UDC_ADDBITS(ep0out->td->status,
+ UDC_DMA_OUT_STS_BS_HOST_READY,
+ UDC_DMA_OUT_STS_BS);
+ } else {
+ udc_data_out_isr(dev, UDC_EP0OUT_IX);
+ ep0out->regs->desptr = ep0out->td_phys;
+ }
+ udc_set_rde(dev);
+ }
+
+finished:
+ ep0out->regs->sts = UDC_EPSTS_OUT_CLEAR;
+ return ret_val;
+}
+
+/**
+ * Interrupt handler for Control IN traffic
+ *
+ * \param dev pointer to UDC device object
+ * \return 0 if success
+ */
+static inline int udc_control_in_isr(struct udc *dev)
+{
+ int ret_val = 0;
+ u32 tmp;
+ struct udc_ep *ep;
+ struct udc_request *req;
+
+ ep = &dev->ep[UDC_EP0IN_IX];
+
+ UDC_SET_BIT(UDC_EPINT_IN_EP0, &dev->regs->ep_irqsts);
+
+ tmp = ep->regs->sts;
+ if (!list_empty(&ep->queue)) {
+ req = list_entry(ep->queue.next, struct udc_request, queue);
+
+ /* DMA completion */
+ if (tmp & UDC_BIT(UDC_EPSTS_TDC)) {
+ ep->regs->ctl |= UDC_BIT(UDC_EPCTL_CNAK);
+ } else if (tmp & UDC_BIT(UDC_EPSTS_IN)) {
+ ep->regs->desptr = (u32)req->td_phys;
+ req->td_data->status = UDC_ADDBITS(req->td_data->status,
+ UDC_DMA_STP_STS_BS_HOST_READY,
+ UDC_DMA_STP_STS_BS);
+ au_sync();
+
+ ep->regs->ctl |= UDC_BIT(UDC_EPCTL_P);
+
+ /* All bytes are always transferred */
+ req->req.actual = req->req.length;
+ complete_req(ep, req, 0);
+ au_sync();
+ }
+ }
+ ep->regs->sts = ep->regs->sts;
+ au_sync();
+
+ return ret_val;
+}
+
+/**
+ * Interrupt handler for global device events
+ *
+ * \param dev pointer to UDC device object
+ * \param dev_irq device interrupt bit of DEVINT register
+ * \return 0 if success
+ */
+static inline int udc_dev_isr(struct udc *dev, u32 dev_irq)
+{
+ int ret_val = 0;
+ u32 tmp;
+ u32 cfg;
+ struct udc_ep *ep;
+ u16 i;
+ u8 udc_csr_epix;
+
+ DBG("Got interrupt. dev_irq is %8.8X\n", dev_irq);
+
+ /* SET_CONFIG irq ? */
+ if (dev_irq & UDC_BIT(UDC_DEVINT_SC)) {
+
+ /* read config value */
+ tmp = ioread32(&dev->regs->sts);
+ cfg = UDC_GETBITS(tmp, UDC_DEVSTS_CFG);
+#ifdef UDC_DEBUG
+ /* this is needed for debug only */
+ if (cfg == dev->cur_config)
+ same_cfg = 1;
+ else
+ same_cfg = 0;
+ VDBG("same_cfg=%d\n", same_cfg);
+#endif
+ DBG("SET_CONFIG interrupt: config=%d\n", cfg);
+ dev->cur_config = cfg;
+ dev->set_cfg_not_acked = 1;
+
+ /* make usb request for gadget driver */
+ memset(&setup_data, 0 , sizeof(union udc_setup_data));
+ setup_data.request.bRequest = USB_REQ_SET_CONFIGURATION;
+ setup_data.request.wValue = dev->cur_config;
+
+ /* programm the NE registers */
+ for (i = 0; i < UDC_EP_NUM; i++) {
+ ep = &dev->ep[i];
+ if (ep->in) {
+
+ /* ep ix in UDC CSR register space */
+ udc_csr_epix = ep->num;
+
+
+ } /* OUT ep */
+ else {
+ /* ep ix in UDC CSR register space */
+ udc_csr_epix = ep->num - UDC_CSR_EP_OUT_IX_OFS;
+ }
+
+ tmp = ioread32(&dev->csr->ne[udc_csr_epix]);
+ /* ep cfg */
+ tmp = UDC_ADDBITS(tmp, ep->dev->cur_config,
+ UDC_CSR_NE_CFG);
+ /* write reg */
+ iowrite32(tmp, &dev->csr->ne[udc_csr_epix]);
+
+ /* clear stall bits */
+ ep->halted = 0;
+ tmp = ioread32(&ep->regs->ctl);
+ tmp = tmp & UDC_CLEAR_BIT(UDC_EPCTL_S);
+ iowrite32(tmp, &ep->regs->ctl);
+ }
+ /* call gadget zero with setup data received */
+ spin_unlock(&dev->lock);
+ tmp = dev->driver->setup(&dev->gadget, &setup_data.request);
+ spin_lock(&dev->lock);
+
+ } /* SET_INTERFACE ? */
+ if (dev_irq & UDC_BIT(UDC_DEVINT_SI)) {
+ dev->set_cfg_not_acked = 1;
+ /* read interface and alt setting values */
+ tmp = ioread32(&dev->regs->sts);
+ dev->cur_alt = UDC_GETBITS(tmp, UDC_DEVSTS_ALT);
+ dev->cur_intf = UDC_GETBITS(tmp, UDC_DEVSTS_INTF);
+
+ /* make usb request for gadget driver */
+ memset(&setup_data, 0 , sizeof(union udc_setup_data));
+ setup_data.request.bRequest = USB_REQ_SET_INTERFACE;
+ setup_data.request.bRequestType = USB_RECIP_INTERFACE;
+ setup_data.request.wValue = dev->cur_alt;
+ setup_data.request.wIndex = dev->cur_intf;
+
+ DBG("SET_INTERFACE interrupt: alt=%d intf=%d\n",
+ dev->cur_alt, dev->cur_intf);
+
+ for (i = 0; i < UDC_EP_NUM; i++) {
+ ep = &dev->ep[i];
+ if (ep->in) {
+
+ /* ep ix in UDC CSR register space */
+ udc_csr_epix = ep->num;
+
+
+ } /* OUT ep */
+ else {
+ /* ep ix in UDC CSR register space */
+ udc_csr_epix = ep->num - UDC_CSR_EP_OUT_IX_OFS;
+ }
+
+ /***** UDC CSR reg ****************************/
+ /* set ep values */
+ tmp = ioread32(&dev->csr->ne[udc_csr_epix]);
+ /* ep interface */
+ tmp = UDC_ADDBITS(tmp, ep->dev->cur_intf,
+ UDC_CSR_NE_INTF);
+ /* ep alt */
+ tmp = UDC_ADDBITS(tmp, ep->dev->cur_alt,
+ UDC_CSR_NE_ALT);
+ /* write reg */
+ iowrite32(tmp, &dev->csr->ne[udc_csr_epix]);
+
+ /* clear stall bits */
+ ep->halted = 0;
+ tmp = ioread32(&ep->regs->ctl);
+ tmp = tmp & UDC_CLEAR_BIT(UDC_EPCTL_S);
+ iowrite32(tmp, &ep->regs->ctl);
+ }
+
+ /* call gadget zero with setup data received */
+ spin_unlock(&dev->lock);
+ tmp = dev->driver->setup(&dev->gadget, &setup_data.request);
+ spin_lock(&dev->lock);
+
+ } /* USB reset */
+ if (dev_irq & UDC_BIT(UDC_DEVINT_UR)) {
+ DBG("USB Reset interrupt\n");
+
+ /* allow soft reset when suspend occurs */
+ soft_reset_occured = 0;
+
+ dev->waiting_zlp_ack_ep0in = 0;
+ dev->set_cfg_not_acked = 0;
+
+ /* mask not needed interrupts */
+ udc_mask_unused_interrupts(dev);
+
+ /* call gadget to reset configs etc. */
+ spin_unlock(&dev->lock);
+ dev->driver->disconnect(&dev->gadget);
+ spin_lock(&dev->lock);
+
+ /* disable ep0 to empty req queue */
+ empty_req_queue(&dev->ep[UDC_EP0IN_IX]);
+ ep_init(dev->regs,
+ &dev->ep[UDC_EP0IN_IX]);
+
+ /* soft reset when rxfifo not empty */
+ tmp = ioread32(&dev->regs->sts);
+ if (!(tmp & UDC_BIT(UDC_DEVSTS_RXFIFO_EMPTY)) &&
+ !soft_reset_after_usbreset_occured) {
+ udc_soft_reset(dev);
+ soft_reset_after_usbreset_occured++;
+ }
+
+ /* put into initial config */
+ udc_basic_init(dev);
+
+ /* enable device setup interrupts */
+ udc_enable_dev_setup_interrupts(dev);
+
+ } /* USB suspend */
+
+ if (dev_irq & UDC_BIT(UDC_DEVINT_ENUM)) {
+ DBG("ENUM interrupt\n");
+#ifdef UDC_DEBUG
+ num_enums++;
+ DBG("%d enumerations !\n", num_enums);
+#endif
+ soft_reset_after_usbreset_occured = 0;
+
+ /* disable ep0 to empty req queue */
+ empty_req_queue(&dev->ep[UDC_EP0IN_IX]);
+ ep_init(dev->regs, &dev->ep[UDC_EP0IN_IX]);
+
+ /* link up all endpoints */
+ udc_setup_endpoints(dev);
+ if (dev->gadget.speed == USB_SPEED_HIGH)
+ INFO("Connect: Speed = HIGH_SPEED\n");
+ else if (dev->gadget.speed == USB_SPEED_FULL)
+ INFO("Connect: Speed = FULL_SPEED\n");
+
+ /* init ep 0 */
+ activate_control_endpoints(dev);
+
+ /* enable ep0 interrupts */
+ udc_enable_ep0_interrupts(dev);
+ }
+
+ return ret_val;
+}
+
+static irqreturn_t udc_irq(int irq, void *pdev)
+{
+ struct udc *dev = pdev;
+ u32 reg;
+ u16 i;
+ u32 ep_irq;
+
+ /* If UDC is suspended, then don't touch any register, otherwise
+ system hangs in endless retry => possibly hang !!! */
+ if (dev->otg_driver && dev->otg_driver->query) {
+ if (dev->otg_driver->query(0) & OTG_FLAGS_UDC_SUSP)
+ return IRQ_HANDLED;
+ } else {
+ return IRQ_HANDLED;
+ }
+
+ if (dev->sys_suspended)
+ return IRQ_HANDLED;
+
+ spin_lock(&dev->lock);
+
+ /* check for ep irq */
+ reg = ioread32(&dev->regs->ep_irqsts);
+ if (reg) {
+ /* EP0 OUT */
+ if (reg & UDC_BIT(UDC_EPINT_OUT_EP0))
+ udc_control_out_isr(dev);
+
+ if (reg & UDC_BIT(UDC_EPINT_IN_EP0))
+ udc_control_in_isr(dev);
+
+ /* data endpoint */
+ /* iterate ep's */
+ for (i = 1; i < UDC_EP_NUM; i++) {
+ ep_irq = 1 << i;
+ /* irq for out ep ? */
+ if ((reg & ep_irq) && i > UDC_EPIN_NUM) {
+ /* clear irq */
+ iowrite32(ep_irq, &dev->regs->ep_irqsts);
+ udc_data_out_isr(dev, i);
+ } /* irq for in ep ? */
+ if ((reg & ep_irq) && i < UDC_EPIN_NUM && i > 0) {
+ /* clear irq */
+ iowrite32(ep_irq, &dev->regs->ep_irqsts);
+ udc_data_in_isr(dev, i);
+ }
+
+ }
+
+ }
+
+ /* check for dev irq */
+ reg = ioread32(&dev->regs->irqsts);
+ if (reg) {
+ /* clear irq */
+ iowrite32(reg, &dev->regs->irqsts);
+ udc_dev_isr(dev, reg);
+ }
+
+
+ spin_unlock(&dev->lock);
+ return IRQ_HANDLED;
+}
+
+/**
+ * Tears down device
+ *
+ * \param pdev pointer to device struct
+ */
+static void gadget_release(struct device *pdev)
+{
+ struct udc *dev = dev_get_drvdata(pdev);
+ kfree(dev);
+}
+
+static void udc_remove(struct udc *dev)
+{
+ u32 tmp;
+ /* disable UDC memory, DMA and clock */
+ tmp = ioread32((u32 *) (USB_MSR_BASE + USB_MSR_MCFG));
+ tmp &= UDC_CLEAR_BIT(USBMSRMCFG_DMEMEN)
+ & UDC_CLEAR_BIT(USBMSRMCFG_DBMEN)
+ & UDC_CLEAR_BIT(USBMSRMCFG_UDCCLKEN);
+ iowrite32(tmp, (u32 *)(USB_MSR_BASE + USB_MSR_MCFG));
+
+ device_unregister(&udc->gadget.dev);
+ udc = 0;
+}
+
+static void udc_drv_remove(struct device *_dev)
+{
+ struct platform_device *pdev = to_platform_device(_dev);
+ struct udc *dev = dev_get_drvdata(_dev);
+
+#ifdef UDC_DEBUG
+ print_misc(dev);
+#endif
+ /* gadget driver registered ? */
+ if (dev->driver) {
+ WARN("unregistering %s on driver remove\n",
+ dev->driver->driver.name);
+ usb_gadget_unregister_driver(dev->driver);
+ }
+ /* otg driver registered ? */
+ if (dev->otg_transceiver) {
+ /* should have been done already by driver model core */
+ WARN("uoc driver is still registered\n");
+ }
+ /* dma pool cleanup */
+ if (dev->data_requests)
+ dma_pool_destroy(dev->data_requests);
+ if (dev->stp_requests) {
+ /* cleanup DMA desc's for ep0in */
+ dma_pool_free(dev->stp_requests,
+ dev->ep[UDC_EP0OUT_IX].td_stp,
+ dev->ep[UDC_EP0OUT_IX].td_stp_dma);
+ dma_pool_free(dev->stp_requests,
+ dev->ep[UDC_EP0OUT_IX].td,
+ dev->ep[UDC_EP0OUT_IX].td_phys);
+
+ dma_pool_destroy(dev->stp_requests);
+ }
+
+ /* init controller by soft reset */
+ iowrite32(UDC_BIT(UDC_DEVCFG_SOFTRESET), &dev->regs->cfg);
+
+ if (dev->irq_registered)
+ free_irq(pdev->resource[1].start, dev);
+ if (dev->regs)
+ iounmap(dev->regs);
+ if (dev->mem_region)
+ release_mem_region(pdev->resource[0].start,
+ pdev->resource[0].end + 1
+ - pdev->resource[0].start);
+
+ device_remove_file(&pdev->dev, &dev_attr_registers);
+ dev_set_drvdata(_dev, 0);
+ udc_remove(dev);
+}
+
+__init static int init_dma_pools(struct udc *dev)
+{
+ struct udc_stp_dma *td_stp;
+ struct udc_data_dma *td_data;
+ int retval;
+
+ /* consistent DMA mode setting ? */
+ if (use_dma_ppb) {
+ use_dma_bufferfill_mode = 0;
+ } else {
+ use_dma_ppb_du = 0;
+ use_dma_bufferfill_mode = 1;
+ }
+
+ /* DMA setup */
+ dev->data_requests = dma_pool_create("data_requests", NULL,
+ sizeof(struct udc_data_dma),
+ UDC_POOL_ALIGN,
+ UDC_POOL_CROSS);
+ if (!dev->data_requests) {
+ DBG("can't get request data pool\n");
+ retval = -ENOMEM;
+ goto finished;
+ }
+
+ /* EP0 in dma regs = dev control regs */
+ dev->ep[UDC_EP0IN_IX].dma = &dev->regs->ctl;
+
+ /* dma desc for setup data */
+ dev->stp_requests = dma_pool_create("setup requests", NULL,
+ sizeof(struct udc_stp_dma),
+ UDC_POOL_ALIGN,
+ UDC_POOL_CROSS);
+ if (!dev->stp_requests) {
+ DBG("can't get stp request pool\n");
+ retval = -ENOMEM;
+ goto finished;
+ }
+ /* setup */
+ td_stp = dma_pool_alloc(dev->stp_requests, UDC_POOL_GFP_STP,
+ &dev->ep[UDC_EP0OUT_IX].td_stp_dma);
+ if (td_stp == NULL) {
+ retval = -ENOMEM;
+ goto finished;
+ }
+ dev->ep[UDC_EP0OUT_IX].td_stp = td_stp;
+ /* data: 0 packets !? */
+ td_data = dma_pool_alloc(dev->stp_requests, UDC_POOL_GFP_STP,
+ &dev->ep[UDC_EP0OUT_IX].td_phys);
+ if (td_data == NULL) {
+ retval = -ENOMEM;
+ goto finished;
+ }
+ dev->ep[UDC_EP0OUT_IX].td = td_data;
+ /* point to itself */
+ dev->ep[UDC_EP0OUT_IX].td->next = dev->ep[UDC_EP0OUT_IX].td_phys;
+ return 0;
+
+finished:
+ return retval;
+}
+
+/**
+ * Called by kernel init device context
+ */
+static int udc_drv_probe(struct device *_dev)
+{
+ char tmp[8];
+ struct udc *dev;
+ struct platform_device *pdev = to_platform_device(_dev);
+ u32 resource;
+ u32 len;
+ u32 irq;
+ int retval = 0;
+ u32 reg;
+
+ /* basic init */
+ reg = ioread32((u32 *) (USB_MSR_BASE + USB_MSR_MCFG));
+ if (reg == 0) {
+ /* default value */
+ reg = USBMSRMCFG_DEFAULT;
+ iowrite32(reg, (u32 *)(USB_MSR_BASE + USB_MSR_MCFG));
+ ioread32((u32 *)(USB_MSR_BASE + USB_MSR_MCFG));
+ udelay(1000);
+ }
+ /* enable UDC memory, DMA, clock, cacheable memory,
+ * read combining and prefetch enable */
+ reg |= UDC_BIT(USBMSRMCFG_DMEMEN) | UDC_BIT(USBMSRMCFG_DBMEN)
+ | UDC_BIT(USBMSRMCFG_UDCCLKEN)
+ | UDC_BIT(USBMSRMCFG_PHYPLLEN)
+#ifdef CONFIG_DMA_COHERENT
+ | UDC_BIT(USBMSRMCFG_UCAM)
+#endif
+ | UDC_BIT(USBMSRMCFG_RDCOMB)
+ | UDC_BIT(USBMSRMCFG_PFEN);
+ iowrite32(reg, (u32 *)(USB_MSR_BASE + USB_MSR_MCFG));
+
+ /* one udc only */
+ if (udc) {
+ WARN("already probed.\n");
+ return -EBUSY;
+ }
+
+ /* init */
+ dev = kmalloc(sizeof(struct udc), GFP_KERNEL);
+ if (!dev) {
+ retval = -ENOMEM;
+ goto finished;
+ }
+ memset(dev, 0, sizeof(struct udc));
+
+ dev->pdev = _dev;
+
+ /* check platform resources */
+ if (pdev->resource[0].flags != IORESOURCE_MEM) {
+ ERR("resource[0] must be IORESOURCE_MEM\n");
+ retval = -ENOMEM;
+ goto finished;
+ }
+ resource = pdev->resource[0].start;
+ len = pdev->resource[0].end + 1 - pdev->resource[0].start;
+ if (pdev->resource[1].flags != IORESOURCE_IRQ) {
+ ERR("resource[1] must be IORESOURCE_IRQ\n");
+ retval = -ENOMEM;
+ goto finished;
+ }
+ irq = pdev->resource[1].start;
+
+ /* platform device resource allocation */
+ /* mem */
+ if (!request_mem_region(resource, len, name)) {
+ ERR("controller already in use\n");
+ retval = -EBUSY;
+ goto finished;
+ }
+ dev->mem_region = 1;
+
+ dev->virt_addr = ioremap_nocache(resource, len);
+ if (dev->virt_addr == NULL) {
+ DBG("start address cannot be mapped\n");
+ retval = -EFAULT;
+ goto finished;
+ }
+
+ /* irq */
+ if (!irq) {
+ ERR("irq not set\n");
+ retval = -ENODEV;
+ goto finished;
+ }
+ snprintf(tmp, sizeof tmp, "%d", irq);
+ if (request_irq(irq, udc_irq, IRQF_SHARED, name, dev) != 0) {
+ ERR("error on request_irq() with %s\n", tmp);
+ retval = -EBUSY;
+ goto finished;
+ }
+ dev->irq_registered = 1;
+
+ dev_set_drvdata(_dev, dev);
+
+ /* chip revision */
+ dev->chiprev = 0;
+
+ /* chip rev for Au1200 */
+ dev->chiprev = (u16) read_c0_prid() & 0xff;
+
+ /* init dma pools */
+ if (use_dma) {
+ retval = init_dma_pools(dev);
+ if (retval != 0)
+ goto finished;
+ }
+
+ dev->phys_addr = resource;
+ dev->irq = irq;
+ dev->pdev = _dev;
+ dev->gadget.dev.parent = _dev;
+ dev->gadget.dev.dma_mask = _dev->dma_mask;
+ /* general probing */
+ if (udc_probe(dev) != 0)
+ goto finished;
+ return retval;
+
+finished:
+ if (dev)
+ udc_drv_remove(_dev);
+ return retval;
+}
+
+/**
+ * general probe
+ */
+__init int udc_probe(struct udc *dev)
+{
+ char tmp[128];
+ u32 reg;
+ int retval;
+
+ /* device struct setup */
+ spin_lock_init(&dev->lock);
+ spin_lock_init(&udc_irq_spinlock);
+ spin_lock_init(&udc_stall_spinlock);
+ dev->gadget.ops = &udc_ops;
+
+ strcpy(dev->gadget.dev.bus_id, "gadget");
+ dev->gadget.dev.release = gadget_release;
+ dev->gadget.name = name;
+ dev->gadget.is_dualspeed = 1;
+
+ /* udc csr registers base */
+ dev->csr = (struct udc_csrs *)(dev->virt_addr + UDC_CSR_ADDR);
+ /* dev registers base */
+ dev->regs = (struct udc_regs *)(dev->virt_addr + UDC_DEVCFG_ADDR);
+ /* ep registers base */
+ dev->ep_regs = (struct udc_ep_regs *)(dev->virt_addr + UDC_EPREGS_ADDR);
+ /* fifo's base */
+ dev->rxfifo = (u32 *) (dev->virt_addr + UDC_RXFIFO_ADDR);
+ dev->txfifo = (u32 *) (dev->virt_addr + UDC_TXFIFO_ADDR);
+
+ /* init registers, interrupts, ... */
+ {
+ u32 tmp;
+
+ dev->gadget.ep0 = &dev->ep[UDC_EP0IN_IX].ep;
+ dev->ep[UDC_EP0IN_IX].halted = 0;
+ INIT_LIST_HEAD(&dev->gadget.ep0->ep_list);
+ dev->gadget.speed = USB_SPEED_HIGH;
+ make_ep_lists(dev);
+ /* basic endpoint init */
+ for (tmp = 0; tmp < UDC_EP_NUM; tmp++) {
+ struct udc_ep *ep = &dev->ep[tmp];
+
+ ep->ep.name = ep_string[tmp];
+ ep->dev = dev;
+ ep->num = tmp;
+ /* txfifo size is calculated at enable time */
+ ep->txfifo = dev->txfifo;
+
+ /* fifo size */
+ if (tmp < UDC_EPIN_NUM) {
+ ep->fifo_depth = UDC_TXFIFO_SIZE;
+ ep->in = 1;
+ } else {
+ ep->fifo_depth = UDC_RXFIFO_SIZE;
+ ep->in = 0;
+
+ }
+
+ ep->regs = &dev->ep_regs[tmp];
+ if (!ep->desc) {
+ ep->desc = 0;
+ INIT_LIST_HEAD(&ep->queue);
+
+ ep->ep.maxpacket = ~0;
+ ep->ep.ops = &udc_ep_ops;
+ }
+
+ tasklet_init(&ep->execute_tasklet,
+ udc_tasklet_execute_request,
+ (unsigned long)ep);
+ init_MUTEX(&ep->in_use);
+ init_MUTEX(&ep->in_et);
+ }
+ dev->ep[UDC_EP0IN_IX].ep.maxpacket = UDC_EP0IN_MAX_PKT_SIZE;
+ dev->ep[UDC_EP0OUT_IX].ep.maxpacket = UDC_EP0OUT_MAX_PKT_SIZE;
+ }
+
+
+ INFO("%s\n", mod_desc);
+
+ snprintf(tmp, sizeof tmp, "%d", dev->irq);
+ INFO("irq %s, mem %08lx, chip rev %02x (Au1200 %s)\n",
+ tmp, dev->phys_addr, dev->chiprev,
+ (dev->chiprev == 0) ? "AB" : "AC");
+#ifdef CONFIG_DMA_COHERENT
+ /* coherent DMA not possible with AB silicon */
+ if (dev->chiprev == UDC_AUAB_REV) {
+ ERR("Your chip revision is %s, it must be at least %s to use"
+ " coherent DMA. \nPlease change DMA_COHERENT to"
+ " DMA_NONCOHERENT in arch/mips/Kconfig and"
+ " re-compile .\n", "AB", "AC");
+ retval = -ENODEV;
+ goto finished;
+ }
+#endif
+
+#ifdef CONFIG_DMA_COHERENT
+ INFO("Compiled for coherent memory.\n");
+#endif
+#ifdef CONFIG_DMA_NONCOHERENT
+ INFO("Compiled for non-coherent memory.\n");
+#endif
+ udc = dev;
+
+ retval = device_register(&dev->gadget.dev);
+ if (retval) {
+ ERR("Failed to register gadget device\n");
+ goto finished;
+ }
+ device_create_file(&pdev->dev, &dev_attr_registers);
+
+ /* set SD */
+ reg = ioread32(&dev->regs->ctl);
+ reg |= UDC_BIT(UDC_DEVCTL_SD);
+ iowrite32(reg, &dev->regs->ctl);
+ /* print dev register info */
+ print_regs(dev);
+ return 0;
+
+finished:
+ return retval;
+}
+
+
+/**
+ * Initiates a remote wakeup
+ *
+ * \return 0 if success
+ */
+/* initiate remote wakeup */
+static int udc_remote_wakeup(struct udc *dev)
+{
+ INFO("UDC initiates remote wakeup\n");
+
+ UDC_SET_BIT(UDC_DEVCTL_RES, &dev->regs->ctl);
+ UDC_UNSET_BIT(UDC_DEVCTL_RES, &dev->regs->ctl);
+
+ return 0;
+}
+
+/**
+ * Suspends UDC
+ */
+static int udc_suspend(struct udc *dev)
+{
+ int retval = 0;
+
+ u32 tmp;
+ INFO("UDC suspend\n");
+ /* mask interrupts */
+ udc_mask_unused_interrupts(dev);
+
+ if (dev->driver && dev->driver->disconnect) {
+ /* call gadget to reset context */
+ if (spin_is_locked(&dev->lock)) {
+ spin_unlock(&dev->lock);
+ dev->driver->disconnect(&dev->gadget);
+ spin_lock(&dev->lock);
+ } else
+ dev->driver->disconnect(&dev->gadget);
+
+ /* disable ep0 to empty req queue */
+ empty_req_queue(&dev->ep[UDC_EP0IN_IX]);
+ ep_init(dev->regs,
+ &dev->ep[UDC_EP0IN_IX]);
+
+ /* init controller by soft reset */
+ udc_soft_reset(dev);
+
+ }
+ if (dev->otg_driver && dev->otg_transceiver
+ && dev->otg_transceiver->set_peripheral) {
+ /* if UDC is supended by Host or already disconnected then
+ don't force disconnect by unbind() */
+ if (dev->otg_driver->query) {
+ if (!(dev->otg_driver->query(0) & OTG_FLAGS_UDC_SUSP)) {
+ /* unbind from otg driver -> host disconnect */
+ dev->otg_transceiver->set_peripheral(
+ dev->otg_transceiver, NULL);
+ dev->connected = 0;
+ }
+ } else {
+ /* unbind from otg driver -> host disconnect */
+ dev->otg_transceiver->set_peripheral(
+ dev->otg_transceiver, NULL);
+ dev->connected = 0;
+ }
+ }
+
+ dev->sys_suspended = 1;
+
+ /* switch off UDC clock */
+ tmp = ioread32((u32 *)(USB_MSR_BASE + USB_MSR_MCFG));
+ tmp &= UDC_CLEAR_BIT(USBMSRMCFG_UDCCLKEN);
+ iowrite32(tmp, (u32 *)(USB_MSR_BASE + USB_MSR_MCFG));
+
+ return retval;
+}
+
+static int udc_resume(struct udc *dev)
+{
+ int retval = 0;
+
+ u32 tmp;
+ INFO("UDC resume\n");
+ /* switch on UDC clock */
+ tmp = ioread32((u32 *)(USB_MSR_BASE + USB_MSR_MCFG));
+ tmp |= UDC_BIT(USBMSRMCFG_UDCCLKEN);
+ iowrite32(tmp, (u32 *)(USB_MSR_BASE + USB_MSR_MCFG));
+
+ dev->sys_suspended = 0;
+
+ usb_connect(dev);
+ if (dev->otg_transceiver && dev->otg_transceiver->set_peripheral) {
+ /* bind to otg driver */
+ dev->otg_transceiver->set_peripheral(dev->otg_transceiver,
+ &dev->gadget);
+ }
+ return retval;
+}
+
+static int udc_au1xxx_drv_probe(struct device *dev)
+{
+ int retval;
+
+ DBG("udc_au1xxx_drv_probe()\n");
+ retval = udc_drv_probe(dev);
+ return retval;
+}
+
+static int udc_au1xxx_drv_remove(struct device *dev)
+{
+ DBG("udc_au1xxx_drv_remove()\n");
+ udc_drv_remove(dev);
+ return 0;
+}
+
+static int udc_au1xxx_drv_suspend(struct device *dev, pm_message_t state)
+{
+ struct udc *udc_dev = dev_get_drvdata(dev);
+ return udc_suspend(udc_dev);
+}
+
+static int udc_au1xxx_drv_resume(struct device *dev)
+{
+ struct udc *udc_dev = dev_get_drvdata(dev);
+ return udc_resume(udc_dev);
+}
+
+static struct device_driver udc_au1xxx_driver = {
+ .name = "au1xxx-udc",
+ .bus = &platform_bus_type,
+ .probe = udc_au1xxx_drv_probe,
+ .remove = udc_au1xxx_drv_remove,
+ .suspend = udc_au1xxx_drv_suspend,
+ .resume = udc_au1xxx_drv_resume,
+};
+
+
+static int __init init(void)
+{
+ int rc;
+
+ /* probe by device system */
+ rc = driver_register(&udc_au1xxx_driver);
+
+ return rc;
+}
+module_init(init);
+
+static void __exit cleanup(void)
+{
+ /* unregister at device system */
+ driver_unregister(&udc_au1xxx_driver);
+
+}
+module_exit(cleanup);
+
diff --git a/drivers/usb/gadget/au1200_udc.h b/drivers/usb/gadget/au1200_udc.h
new file mode 100644
index 0000000..2a4f7bf
--- /dev/null
+++ b/drivers/usb/gadget/au1200_udc.h
@@ -0,0 +1,816 @@
+/*
+ * Header for driver for RMI Au1200 UDC high/full speed USB device controller
+ */
+/*
+ * Copyright (C) 2008 RMI Corporation (http://www.rmicorp.com)
+ * Author: Kevin Hickey (khickey@rmicorp.com)
+ *
+ * Adapted from the AMD5536 UDC module.
+ *
+ * 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 AU1200UDC_H
+#define AU1200UDC_H
+
+/*****************************************************************************
+ * Constants
+ *****************************************************************************/
+
+/* Driver constants -------------------------------------------------------*/
+#define DRIVER_NAME_FOR_PRINT "au1200_udc"
+
+/* Platform specific -------------------------------------------------------*/
+#define UDC_POOL_ALIGN 32
+#define UDC_POOL_CROSS 4096
+#define UDC_POOL_GFP_STP (GFP_ATOMIC | GFP_DMA)
+
+#ifndef USBMSRMCFG_UCAM
+#define USBMSRMCFG_UCAM 7
+#endif
+#define USBMSRMCFG_DEFAULT 0x00d02000
+
+/* Au1200 rev. */
+#define UDC_AUAB_REV 0
+#define UDC_AUAC_REV 1
+#define UDC_AUA0 0
+
+/* Global CSR's -------------------------------------------------------------*/
+/* UDC CSR's */
+#define UDC_CSR_ADDR 0x500
+
+/* EP NE bits */
+/* EP number */
+#define UDC_CSR_NE_NUM_MASK 0x0000000f
+#define UDC_CSR_NE_NUM_OFS 0
+/* EP direction */
+#define UDC_CSR_NE_DIR_MASK 0x00000010
+#define UDC_CSR_NE_DIR_OFS 4
+/* EP type */
+#define UDC_CSR_NE_TYPE_MASK 0x00000060
+#define UDC_CSR_NE_TYPE_OFS 5
+/* EP config number */
+#define UDC_CSR_NE_CFG_MASK 0x00000780
+#define UDC_CSR_NE_CFG_OFS 7
+/* EP interface number */
+#define UDC_CSR_NE_INTF_MASK 0x00007800
+#define UDC_CSR_NE_INTF_OFS 11
+/* EP alt setting */
+#define UDC_CSR_NE_ALT_MASK 0x00078000
+#define UDC_CSR_NE_ALT_OFS 15
+
+/* max pkt */
+#define UDC_CSR_NE_MAX_PKT_MASK 0x3ff80000
+#define UDC_CSR_NE_MAX_PKT_OFS 19
+
+/* Device Config Register ---------------------------------------------------*/
+#define UDC_DEVCFG_ADDR 0x400
+
+#define UDC_DEVCFG_SOFTRESET 31
+#define UDC_DEVCFG_HNPSFEN 30
+#define UDC_DEVCFG_DMARST 29
+#define UDC_DEVCFG_SET_DESC 18
+#define UDC_DEVCFG_CSR_PRG 17
+#define UDC_DEVCFG_STATUS 7
+#define UDC_DEVCFG_DIR 6
+#define UDC_DEVCFG_PI 5
+#define UDC_DEVCFG_SS 4
+#define UDC_DEVCFG_SP 3
+#define UDC_DEVCFG_RWKP 2
+
+#define UDC_DEVCFG_SPD_MASK 0x3
+#define UDC_DEVCFG_SPD_OFS 0
+#define UDC_DEVCFG_SPD_HS 0x0
+#define UDC_DEVCFG_SPD_FS 0x1
+#define UDC_DEVCFG_SPD_LS 0x2
+/*#define UDC_DEVCFG_SPD_FS 0x3*/
+
+
+/* Device Control Register --------------------------------------------------*/
+#define UDC_DEVCTL_ADDR 0x404
+
+#define UDC_DEVCTL_THLEN_MASK 0xff000000
+#define UDC_DEVCTL_THLEN_OFS 24
+
+#define UDC_DEVCTL_BRLEN_MASK 0x00ff0000
+#define UDC_DEVCTL_BRLEN_OFS 16
+
+#define UDC_DEVCTL_CSR_DONE 13
+#define UDC_DEVCTL_DEVNAK 12
+#define UDC_DEVCTL_SD 10
+#define UDC_DEVCTL_MODE 9
+#define UDC_DEVCTL_BREN 8
+#define UDC_DEVCTL_THE 7
+#define UDC_DEVCTL_BF 6
+#define UDC_DEVCTL_BE 5
+#define UDC_DEVCTL_DU 4
+#define UDC_DEVCTL_TDE 3
+#define UDC_DEVCTL_RDE 2
+#define UDC_DEVCTL_RES 0
+
+
+/* Device Status Register ---------------------------------------------------*/
+#define UDC_DEVSTS_ADDR 0x408
+
+#define UDC_DEVSTS_TS_MASK 0xfffc0000
+#define UDC_DEVSTS_TS_OFS 18
+
+#define UDC_DEVSTS_SESSVLD 17
+#define UDC_DEVSTS_PHY_ERROR 16
+#define UDC_DEVSTS_RXFIFO_EMPTY 15
+
+#define UDC_DEVSTS_ENUM_SPEED_MASK 0x00006000
+#define UDC_DEVSTS_ENUM_SPEED_OFS 13
+#define UDC_DEVSTS_ENUM_SPEED_FULL 1
+#define UDC_DEVSTS_ENUM_SPEED_HIGH 0
+
+#define UDC_DEVSTS_SUSP 12
+
+#define UDC_DEVSTS_ALT_MASK 0x00000f00
+#define UDC_DEVSTS_ALT_OFS 8
+
+#define UDC_DEVSTS_INTF_MASK 0x000000f0
+#define UDC_DEVSTS_INTF_OFS 4
+
+#define UDC_DEVSTS_CFG_MASK 0x0000000f
+#define UDC_DEVSTS_CFG_OFS 0
+
+
+/* Device Interrupt Register ------------------------------------------------*/
+#define UDC_DEVINT_ADDR 0x40c
+
+#define UDC_DEVINT_ENUM 6
+#define UDC_DEVINT_SOF 5
+#define UDC_DEVINT_US 4
+#define UDC_DEVINT_UR 3
+#define UDC_DEVINT_ES 2
+#define UDC_DEVINT_SI 1
+#define UDC_DEVINT_SC 0
+
+/* Device Interrupt Mask Register -------------------------------------------*/
+#define UDC_DEVINT_MSK_ADDR 0x410
+
+#define UDC_DEVINT_MSK 0x3f
+
+/* Endpoint Interrupt Register ----------------------------------------------*/
+#define UDC_EPINT_ADDR 0x414
+
+#define UDC_EPINT_OUT_MASK 0xffff0000
+#define UDC_EPINT_OUT_OFS 16
+#define UDC_EPINT_IN_MASK 0x0000ffff
+#define UDC_EPINT_IN_OFS 0
+
+#define UDC_EPINT_IN_EP0 0
+#define UDC_EPINT_IN_EP1 1
+#define UDC_EPINT_IN_EP2 2
+#define UDC_EPINT_IN_EP3 3
+#define UDC_EPINT_OUT_EP0 16
+#define UDC_EPINT_OUT_EP1 17
+#define UDC_EPINT_OUT_EP2 18
+#define UDC_EPINT_OUT_EP3 19
+
+#define UDC_EPINT_EP0_ENABLE_MSK 0x001e001e
+
+/* Endpoint Interrupt Mask Register -----------------------------------------*/
+#define UDC_EPINT_MSK_ADDR 0x418
+
+#define UDC_EPINT_OUT_MSK_MASK 0xffff0000
+#define UDC_EPINT_OUT_MSK_OFS 16
+#define UDC_EPINT_IN_MSK_MASK 0x0000ffff
+#define UDC_EPINT_IN_MSK_OFS 0
+
+#define UDC_EPINT_MSK_DISABLE_ALL (UDC_EPINT_OUT_MASK |\
+ UDC_EPINT_IN_MASK)
+/* mask non-EP0 endpoints */
+#define UDC_EPDATAINT_MSK_DISABLE 0xfffefffe
+/* mask all dev interrupts */
+#define UDC_DEV_MSK_DISABLE 0x7f
+
+/* Endpoint-specific CSR's --------------------------------------------------*/
+/* Endpoint Control Registers -----------------------------------------------*/
+#define UDC_EPREGS_ADDR 0x0
+#define UDC_EPIN_REGS_ADDR 0x0
+#define UDC_EPOUT_REGS_ADDR 0x200
+
+#define UDC_EPCTL_ADDR 0x0
+
+#define UDC_EPCTL_RRDY 9
+#define UDC_EPCTL_CNAK 8
+#define UDC_EPCTL_SNAK 7
+#define UDC_EPCTL_NAK 6
+
+#define UDC_EPCTL_ET_MASK 0x00000030
+#define UDC_EPCTL_ET_OFS 4
+#define UDC_EPCTL_ET_CONTROL 0
+#define UDC_EPCTL_ET_ISO 1
+#define UDC_EPCTL_ET_BULK 2
+#define UDC_EPCTL_ET_INTERRUPT 3
+
+#define UDC_EPCTL_P 3
+#define UDC_EPCTL_SN 2
+#define UDC_EPCTL_F 1
+#define UDC_EPCTL_S 0
+
+/* Endpoint Status Registers ------------------------------------------------*/
+#define UDC_EPSTS_ADDR 0x4
+
+#define UDC_EPSTS_RX_PKT_SIZE_MASK 0x007ff800
+#define UDC_EPSTS_RX_PKT_SIZE_OFS 11
+
+#define UDC_EPSTS_TDC 10
+#define UDC_EPSTS_HE 9
+#define UDC_EPSTS_BNA 7
+#define UDC_EPSTS_IN 6
+
+#define UDC_EPSTS_OUT_MASK 0x00000030
+#define UDC_EPSTS_OUT_OFS 4
+#define UDC_EPSTS_OUT_DATA 1
+#define UDC_EPSTS_OUT_DATA_CLEAR 0x10
+#define UDC_EPSTS_OUT_SETUP 2
+#define UDC_EPSTS_OUT_SETUP_CLEAR 0x20
+#define UDC_EPSTS_OUT_CLEAR 0x30
+
+/* Endpoint Buffer Size IN/ Receive Packet Frame Number OUT Registers ------*/
+#define UDC_EPIN_BUFF_SIZE_ADDR 0x8
+#define UDC_EPOUT_FRAME_NUMBER_ADDR 0x8
+
+#define UDC_EPIN_BUFF_SIZE_MASK 0x0000ffff
+#define UDC_EPIN_BUFF_SIZE_OFS 0
+/* EP0in txfifo = 128 bytes*/
+#define UDC_EPIN0_BUFF_SIZE 32
+/* EP0in fullspeed txfifo = 128 bytes*/
+#define UDC_FS_EPIN0_BUFF_SIZE 32
+
+/* fifo size mult = fifo size / max packet */
+#define UDC_EPIN_BUFF_SIZE_MULT 2
+
+/* EPin data fifo size = 1024 bytes DOUBLE BUFFERING */
+#define UDC_EPIN_BUFF_SIZE 256
+/* EPin small INT data fifo size = 128 bytes */
+#define UDC_EPIN_SMALLINT_BUFF_SIZE 32
+
+/* EPin fullspeed data fifo size = 128 bytes DOUBLE BUFFERING */
+#define UDC_FS_EPIN_BUFF_SIZE 32
+
+#define UDC_EPOUT_FRAME_NUMBER_MASK 0x0000ffff
+#define UDC_EPOUT_FRAME_NUMBER_OFS 0
+
+/* Endpoint Buffer Size OUT/Max Packet Size Registers -----------------------*/
+#define UDC_EPOUT_BUFF_SIZE_ADDR 0x0c
+#define UDC_EP_MAX_PKT_SIZE_ADDR 0x0c
+
+#define UDC_EPOUT_BUFF_SIZE_MASK 0xffff0000
+#define UDC_EPOUT_BUFF_SIZE_OFS 16
+#define UDC_EP_MAX_PKT_SIZE_MASK 0x0000ffff
+#define UDC_EP_MAX_PKT_SIZE_OFS 0
+/* EP0in max packet size = 64 bytes */
+#define UDC_EP0IN_MAX_PKT_SIZE 64
+/* EP0out max packet size = 64 bytes */
+#define UDC_EP0OUT_MAX_PKT_SIZE 64
+/* EP0in fullspeed max packet size = 64 bytes */
+#define UDC_FS_EP0IN_MAX_PKT_SIZE 64
+/* EP0out fullspeed max packet size = 64 bytes */
+#define UDC_FS_EP0OUT_MAX_PKT_SIZE 64
+
+/* Endpoint dma descriptors ------------------------------------------------*/
+/* Setup data */
+/* Status dword */
+#define UDC_DMA_STP_STS_CFG_MASK 0x0fff0000
+#define UDC_DMA_STP_STS_CFG_OFS 16
+#define UDC_DMA_STP_STS_CFG_ALT_MASK 0x000f0000
+#define UDC_DMA_STP_STS_CFG_ALT_OFS 16
+#define UDC_DMA_STP_STS_CFG_INTF_MASK 0x00f00000
+#define UDC_DMA_STP_STS_CFG_INTF_OFS 20
+#define UDC_DMA_STP_STS_CFG_NUM_MASK 0x0f000000
+#define UDC_DMA_STP_STS_CFG_NUM_OFS 24
+#define UDC_DMA_STP_STS_RX_MASK 0x30000000
+#define UDC_DMA_STP_STS_RX_OFS 28
+#define UDC_DMA_STP_STS_BS_MASK 0xc0000000
+#define UDC_DMA_STP_STS_BS_OFS 30
+#define UDC_DMA_STP_STS_BS_HOST_READY 0
+#define UDC_DMA_STP_STS_BS_DMA_BUSY 1
+#define UDC_DMA_STP_STS_BS_DMA_DONE 2
+#define UDC_DMA_STP_STS_BS_HOST_BUSY 3
+/* IN data */
+/* Status dword */
+#define UDC_DMA_IN_STS_TXBYTES_MASK 0x0000ffff
+#define UDC_DMA_IN_STS_TXBYTES_OFS 0
+#define UDC_DMA_IN_STS_FRAMENUM_MASK 0x07ff0000
+#define UDC_DMA_IN_STS_FRAMENUM_OFS 0
+#define UDC_DMA_IN_STS_L 27
+#define UDC_DMA_IN_STS_TX_MASK 0x30000000
+#define UDC_DMA_IN_STS_TX_OFS 28
+#define UDC_DMA_IN_STS_BS_MASK 0xc0000000
+#define UDC_DMA_IN_STS_BS_OFS 30
+#define UDC_DMA_IN_STS_BS_HOST_READY 0
+#define UDC_DMA_IN_STS_BS_DMA_BUSY 1
+#define UDC_DMA_IN_STS_BS_DMA_DONE 2
+#define UDC_DMA_IN_STS_BS_HOST_BUSY 3
+/* OUT data */
+/* Status dword */
+#define UDC_DMA_OUT_STS_RXBYTES_MASK 0x0000ffff
+#define UDC_DMA_OUT_STS_RXBYTES_OFS 0
+#define UDC_DMA_OUT_STS_FRAMENUM_MASK 0x07ff0000
+#define UDC_DMA_OUT_STS_FRAMENUM_OFS 0
+#define UDC_DMA_OUT_STS_L 27
+#define UDC_DMA_OUT_STS_RX_MASK 0x30000000
+#define UDC_DMA_OUT_STS_RX_OFS 28
+#define UDC_DMA_OUT_STS_BS_MASK 0xc0000000
+#define UDC_DMA_OUT_STS_BS_OFS 30
+#define UDC_DMA_OUT_STS_BS_HOST_READY 0
+#define UDC_DMA_OUT_STS_BS_DMA_BUSY 1
+#define UDC_DMA_OUT_STS_BS_DMA_DONE 2
+#define UDC_DMA_OUT_STS_BS_HOST_BUSY 3
+/* other constants */
+/* max ep0in packet */
+#define UDC_EP0IN_MAXPACKET 1000
+/* max dma packet */
+#define UDC_DMA_MAXPACKET 65536
+/* DMA buffer len for temp request, should be the same as the upper
+ layer gadget is using */
+#define UDC_DMA_TEMP_BUFFER_LEN 4096
+/* un-usable DMA address */
+#define DMA_DONT_USE (~(dma_addr_t)0)
+
+/* other Endpoint register addresses and values-----------------------------*/
+#define UDC_EP_SUBPTR_ADDR 0x10
+#define UDC_EP_DESPTR_ADDR 0x14
+#define UDC_EP_WRITE_CONFIRM_ADDR 0x1c
+
+/* EP number as layouted in AHB space */
+#define UDC_EP_NUM 32
+#define UDC_EPIN_NUM 16
+#define UDC_EPIN_NUM_USED 5
+#define UDC_EPOUT_NUM 16
+/* EP number of EP's really used = EP0 + 8 data EP's */
+#define UDC_USED_EP_NUM 9
+/* UDC CSR regs are aligned but AHB regs not - offset for OUT EP's */
+#define UDC_CSR_EP_OUT_IX_OFS 12
+
+#define UDC_EP0OUT_IX 16
+#define UDC_EP0IN_IX 0
+
+/* max packet */
+#define UDC_HS_BULK_MAXPKT 512
+
+/* Rx fifo address and size = 1k -------------------------------------------*/
+#define UDC_RXFIFO_ADDR 0x800
+#define UDC_RXFIFO_SIZE 0x400
+
+/* Tx fifo address and size = 1.5k -----------------------------------------*/
+#define UDC_TXFIFO_ADDR 0xc00
+#define UDC_TXFIFO_SIZE 0x600
+
+/* default data endpoints --------------------------------------------------*/
+#define UDC_EPIN_STATUS_IX 1
+#define UDC_EPIN_IX 2
+#define UDC_EPOUT_IX 18
+
+/* general constants -------------------------------------------------------*/
+#define UDC_DWORD_BYTES 4
+#define UDC_BITS_PER_BYTE_SHIFT 3
+#define UDC_BYTE_MASK 0xff
+#define UDC_BITS_PER_BYTE 8
+
+/* char device constants ---------------------------------------------------*/
+/* names */
+#ifdef UDC_DEBUG
+#ifdef UDC_DRIVER_NAME
+#define UDC_DEVICE_NAME UDC_DRIVER_NAME
+#else
+#define UDC_DEVICE_NAME "amd5536udc"
+#endif
+#define UDC_DEVICE_FILE_NAME "amd5536udc_dev"
+#define UDC_DEVICE_FILE_INODE "/dev/amd5536udc_dev"
+/* major number */
+#define UDC_MAJOR_NUM 240
+#endif
+
+#ifdef __KERNEL__
+/* kernel wrappers */
+#define device_create_file(x, y) do {} while (0)
+#define device_remove_file device_create_file
+
+#ifndef WARN_ON
+#define WARN_ON(a) do {} while (0)
+#endif
+
+#ifndef BUG_ON
+#define BUG_ON(cond)do {if (unlikely((cond) != 0)) BUG(); } while (0)
+#endif
+
+#ifndef likely
+#define likely(a) (a)
+#define unlikely(a) (a)
+#endif
+
+#ifndef container_of
+#define container_of list_entry
+#endif
+
+#endif
+
+/* MIPS specific -----------------------------------------------------------*/
+
+/*****************************************************************************
+ * Includes
+ *****************************************************************************/
+#include "au1200_otg.h"
+
+/*****************************************************************************
+ * Types
+ *****************************************************************************/
+
+/* UDC CSR's */
+struct udc_csrs {
+
+ /* sca - setup command address */
+ u32 sca;
+
+ /* ep ne's */
+ u32 ne[UDC_USED_EP_NUM];
+} __attribute__ ((packed));
+
+/* AHB subsystem CSR registers */
+struct udc_regs {
+
+ /* device configuration */
+ u32 cfg;
+
+ /* device control */
+ u32 ctl;
+
+ /* device status */
+ u32 sts;
+
+ /* device interrupt */
+ u32 irqsts;
+
+ /* device interrupt mask */
+ u32 irqmsk;
+
+ /* endpoint interrupt */
+ u32 ep_irqsts;
+
+ /* endpoint interrupt mask */
+ u32 ep_irqmsk;
+} __attribute__ ((packed));
+
+/* endpoint specific registers */
+struct udc_ep_regs {
+
+ /* endpoint control */
+ u32 ctl;
+
+ /* endpoint status */
+ u32 sts;
+
+ /* endpoint buffer size in/ receive packet frame number out */
+ u32 bufin_framenum;
+
+ /* endpoint buffer size out/max packet size */
+ u32 bufout_maxpkt;
+
+ /* endpoint setup buffer pointer */
+ u32 subptr;
+
+ /* endpoint data descriptor pointer */
+ u32 desptr;
+
+ /* reserverd */
+ u32 reserved;
+
+ /* write/read confirmation */
+ u32 confirm;
+
+} __attribute__ ((packed));
+
+#ifdef __KERNEL__
+/* control data DMA desc */
+struct udc_stp_dma {
+ /* status quadlet */
+ u32 status;
+ /* reserved */
+ u32 _reserved;
+ /* first setup word */
+ u32 data12;
+ /* second setup word */
+ u32 data34;
+} __attribute__((aligned(16)));
+
+/* normal data DMA desc */
+struct udc_data_dma {
+ /* status quadlet */
+ u32 status;
+ /* reserved */
+ u32 _reserved;
+ /* buffer pointer */
+ u32 bufptr;
+ /* next descriptor pointer */
+ u32 next;
+} __attribute__((aligned(16)));
+
+/* request packet */
+struct udc_request {
+ /* embedded gadget ep */
+ struct usb_request req;
+
+ /* flags */
+ unsigned dma_going:1,
+ dma_done : 1;
+ /* phys. address */
+ dma_addr_t td_phys;
+ /* first dma desc. of chain */
+ struct udc_data_dma *td_data;
+
+ struct list_head queue;
+
+ /* chain length */
+ unsigned chain_len;
+ int serial_number;
+ bool ready_for_p_bit;
+
+};
+
+/* UDC specific endpoint parameters */
+struct udc_ep {
+ struct usb_ep ep;
+ struct udc_ep_regs *regs;
+ u32 *txfifo;
+ u32 *dma;
+ dma_addr_t td_phys;
+ dma_addr_t td_stp_dma;
+ struct udc_stp_dma *td_stp;
+ struct udc_data_dma *td;
+ /* temp request */
+ struct udc_request *req;
+ unsigned req_used;
+ unsigned req_completed;
+
+ /* NAK state */
+ unsigned naking;
+ struct tasklet_struct execute_tasklet;
+ struct semaphore in_use;
+ struct semaphore in_et;
+
+ struct udc *dev;
+
+ /* queue for requests */
+ struct list_head queue;
+ const struct usb_endpoint_descriptor *desc;
+ unsigned halted;
+ unsigned num:5,
+ fifo_depth : 14,
+ in : 1;
+};
+
+/* device struct */
+struct udc {
+ struct usb_gadget gadget;
+ spinlock_t lock;
+ /* all endpoints */
+ struct udc_ep ep[UDC_EP_NUM];
+ struct usb_gadget_driver *driver;
+ struct otg_transceiver *otg_transceiver;
+ struct usb_otg_gadget_extension *otg_driver;
+ /* operational flags */
+ unsigned active:1,
+ waiting_zlp_ack_ep0in : 1,
+ set_cfg_not_acked : 1,
+ irq_registered : 1,
+ otg_supported : 1,
+ data_ep_enabled : 1,
+ data_ep_queued : 1,
+ mem_region : 1,
+ selfpowered : 1,
+ sys_suspended : 1,
+ connected;
+
+ u16 chiprev;
+
+ /* registers */
+ struct device *pdev;
+ struct udc_csrs *csr;
+ struct udc_regs *regs;
+ struct udc_ep_regs *ep_regs;
+ u32 *rxfifo;
+ u32 *txfifo;
+
+ /* DMA desc pools */
+ struct dma_pool *data_requests;
+ struct dma_pool *stp_requests;
+
+ /* device data */
+ unsigned long phys_addr;
+ void *virt_addr;
+ unsigned irq;
+
+ /* states */
+ u16 cur_config;
+ u16 cur_intf;
+ u16 cur_alt;
+};
+
+/* setup request data */
+union udc_setup_data {
+ u32 data[2];
+ struct usb_ctrlrequest request;
+};
+#endif /*__KERNEL__*/
+
+/*****************************************************************************
+ * Macros
+ *****************************************************************************/
+
+/***************************************
+ * SET and GET bitfields in u32 values
+ * via constants for mask/offset:
+ * <bit_field_stub_name> is the text between
+ * UDC_ and _MASK|_OFS of appropiate
+ * constant
+ ****************************************/
+/* set bitfield value in u32 u32Val */
+#define UDC_ADDBITS(u32Val, bitfield_val, bitfield_stub_name)\
+ (((u32Val) & (((u32) ~((u32) bitfield_stub_name##_MASK))))\
+ |(((bitfield_val) << ((u32) bitfield_stub_name##_OFS))\
+ & ((u32) bitfield_stub_name##_MASK)))
+
+/* set bitfield value in zero-initialized u32 u32Val */
+/* => bitfield bits in u32Val are all zero */
+#define UDC_INIT_SETBITS(u32Val, bitfield_val, bitfield_stub_name)\
+ ((u32Val)\
+ |(((bitfield_val) << ((u32) bitfield_stub_name##_OFS))\
+ &((u32) bitfield_stub_name##_MASK)))
+
+/* get bitfield value from u32 u32Val */
+#define UDC_GETBITS(u32Val, bitfield_stub_name)\
+ ((u32Val & ((u32) bitfield_stub_name##_MASK))\
+ >> ((u32) bitfield_stub_name##_OFS))
+
+/* SET and GET bits in u32 values ------------------------------------------*/
+#define UDC_BIT(bit_stub_name) (1 << bit_stub_name)
+#define UDC_UNMASK_BIT(bit_stub_name) (~UDC_BIT(bit_stub_name))
+#define UDC_CLEAR_BIT(bit_stub_name) (~UDC_BIT(bit_stub_name))
+
+#define UDC_SET_BIT(bit_number, register_address) \
+ do { \
+ iowrite32(ioread32((register_address)) | (1 << bit_number),\
+ (register_address)); \
+ au_sync(); \
+ } while (0)
+
+/* Note that this takes a set of bits and does not shift them */
+#define UDC_SET_BITS(bits_to_set, register_address)
\
+ do { \
+ iowrite32(ioread32((register_address)) | (bits_to_set), \
+ (register_address)); \
+ au_sync(); \
+ } while (0)
+
+#define UDC_UNSET_BIT(bit_number, register_address) \
+ do { \
+ iowrite32(ioread32((register_address)) & ~(1 << bit_number),\
+ (register_address)); \
+ au_sync(); \
+ } while (0)
+
+/* misc --------------------------------------------------------------------*/
+#define DIR_STRING(bAddress) (((bAddress) & USB_DIR_IN) ? "in" : "out")
+
+/* print macros ------------------------------------------------------------*/
+
+#ifdef UDC_VERBOSE
+#ifndef UDC_DEBUG
+#define UDC_DEBUG
+#endif
+#endif
+
+/**
+ * \brief
+ * Macro for printing information in drivers
+ *
+ * This macro is used for printing kernel messages in driver source code.
+ * It should be used for printing useful information about states and called
+ * functions for normal operation (not for errors and warnings).
+ *
+ * \param fmt is format string for printk
+ * \param args... are arguments given to printk (number depends on <fmt>)
+ * \return code from printk
+ */
+#define INFO(args...) \
+ printk(KERN_INFO DRIVER_NAME_FOR_PRINT ": " args)
+
+/**
+ * \brief
+ * Macro for printing warnings in drivers
+ *
+ * This macro is used for printing kernel messages in driver source code.
+ * It should be used for printing warnings.
+ *
+ * \param fmt is format string for printk
+ * \param args... are arguments given to printk (number depends on <fmt>)
+ * \return code from printk
+ */
+#ifdef WARN
+#undef WARN
+#endif
+#define WARN(args...) \
+ printk(KERN_WARNING DRIVER_NAME_FOR_PRINT " warning: " args)
+
+/**
+ * \brief
+ * Macro for printing errors in drivers
+ *
+ * This macro is used for printing kernel messages in driver source code.
+ * It should be used for printing errors.
+ *
+ * \param fmt is format string for printk
+ * \param args... are arguments given to printk (number depends on <fmt>)
+ * \return code from printk
+ */
+#define ERR(args...) \
+ printk(KERN_ERR DRIVER_NAME_FOR_PRINT " error: " args)
+
+/**
+ * \brief
+ * Macro for printing debug messages in drivers
+ *
+ * This macro is used for printing kernel messages in driver source code
+ * when UDC_DEBUG is defined
+ * It should be used for printing debug messages.
+ *
+ * \param fmt is format string for printk
+ * \param args... are arguments given to printk (number depends on <fmt>)
+ * \return code from printk
+ */
+#ifdef UDC_DEBUG
+#define DBG(args...) \
+ printk(KERN_DEBUG DRIVER_NAME_FOR_PRINT " debug: " args)
+#else
+
+#define DBG(args...) \
+ do {} while (0)
+#endif
+
+/**
+ * \brief
+ * Macro for printing verbose debug messages in drivers
+ *
+ * This macro is used for printing kernel messages in driver source code
+ * when UDC_DEBUG and UDC_VERBOSE is defined
+ * It should be used for printing debug messages.
+ *
+ * \param fmt is format string for printk
+ * \param args... are arguments given to printk (number depends on <fmt>)
+ * \return code from printk
+ */
+#ifdef UDC_VERBOSE
+#define VDBG DBG
+#else
+#define VDBG(args...) \
+ do {} while (0)
+#endif
+
+/*****************************************************************************
+ * Data
+ *****************************************************************************/
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif
+
+/*****************************************************************************
+ * Functions
+ *****************************************************************************/
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif
+
+/*****************************************************************************
+ * Inline Functions
+ *****************************************************************************/
+
+#endif /* #ifdef AU1200UDC_H */
diff --git a/drivers/usb/gadget/au1200_uoc.h b/drivers/usb/gadget/au1200_uoc.h
new file mode 100644
index 0000000..569668a
--- /dev/null
+++ b/drivers/usb/gadget/au1200_uoc.h
@@ -0,0 +1,1021 @@
+/*
+ * Declarations and macros for the Au1200 On The Go port driver.
+ */
+
+/*
+ * Copyright (C) 2008 RMI Corporation (http://www.rmicorp.com)
+ * Author: Kevin Hickey (khickey@rmicorp.com)
+ *
+ * Adapted from earlier work by Karsten Boge
+ *
+ * THIS SOFTWARE IS PROVIDED BY RMI Corporation 'AS IS' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+ * EVENT SHALL RMI OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
+ * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * 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.
+ *
+ * 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 AU1200_UOC_H
+#define AU1200_UOC_H
+
+
+/*****************************************************************************
+* Config options
+*****************************************************************************/
+#ifdef VERBOSE
+#ifndef DEBUG
+#define DEBUG
+#endif
+#endif
+
+
+/*****************************************************************************
+* Constants
+*****************************************************************************/
+
+#define OTG_DRIVER_NAME "au1200_otg"
+#define OTG_FLAGS_ACTIV (1<<19) /* full OTG functionality is activ */
+
+/**********************************
+* Register definitions
+**********************************/
+/* capabilities */
+#define OTG_CAP_APU (1<<15) /* automatic pull-up enable */
+
+/* multiplexer */
+#define OTG_MUX_DISABLE_ALL 0 /* not assigned */
+#define OTG_MUX_ENABLE_UHC (2<<0) /* assigned to host */
+#define OTG_MUX_ENABLE_UDC (3<<0) /* assigned to device */
+#define OTG_MUX_PUEN (1<<2) /* pull-up enable */
+#define OTG_MUX_VBUSVLD (1<<8) /* VBus valid */
+
+/* status */
+#define OTG_STS_ID (1<<0) /* ID pin status */
+#define OTG_STS_VBUSVLD (1<<1) /* VBus valid */
+#define OTG_STS_SESSVLD (1<<2) /* Session valid */
+#define OTG_STS_SESSEND (1<<3) /* Session end */
+#define OTG_STS_LST (3<<4) /* Line state */
+#define OTG_STS_LST_J (1<<4) /* Line state */
+#define OTG_STS_LST_K (2<<4) /* Line state */
+#define OTG_STS_PSPD (3<<6) /* Port speed */
+#define OTG_STS_PSPD_LS (1<<7) /* Port speed */
+#define OTG_STS_PSPD_FS (2<<7) /* Port speed */
+#define OTG_STS_FSOE (1<<8) /* FS output enable (OHC) */
+#define OTG_STS_PCON (1<<9) /* Port connected */
+#define OTG_STS_PSUS (1<<10) /* Port suspended */
+#define OTG_STS_TMH (1<<11) /* Timer halted */
+#define OTG_STS_HNP_EN (1<<12) /* HNP enabled for B-dev */
+#define OTG_STS_HNP_SUPP (1<<13) /* A-host supports HNP */
+#define OTG_STS_HNP_ALTSUPP (1<<14) /* A-host supports alt. HNP */
+#define OTG_STS_HNPSTS (OTG_STS_HNP_EN | OTG_STS_HNP_SUPP | \
+ OTG_STS_HNP_ALTSUPP)
+#define OTG_STS_OC (1<<15) /* over-current */
+#define OTG_STS_DPR (1<<16) /* Downstream port reset */
+
+/* control */
+#define OTG_CTL_DISABLE_ALL 0 /* not assigned */
+#define OTG_CTL_ENABLE_UHC (2<<0) /* assigned to host */
+#define OTG_CTL_ENABLE_UDC (3<<0) /* assigned to device */
+#define OTG_CTL_MUX_MASK (3<<0) /* port mux mask */
+#define OTG_CTL_PPWR (1<<2) /* port power switch */
+#define OTG_CTL_PPO (1<<3) /* port power override */
+#define OTG_CTL_CHRG (1<<4) /* charge VBus */
+#define OTG_CTL_DISCHRG (1<<5) /* discharge VBus */
+#define OTG_CTL_IDSNSEN (1<<6) /* ID sense enable, ID-PU */
+#define OTG_CTL_PADEN (1<<7)
+#define OTG_CTL_PUEN (1<<8) /* pull-up enable */
+#define OTG_CTL_DMPDEN (1<<9) /* pull-down enable */
+#define OTG_CTL_HNPSFEN (1<<10) /* HNP SET_FEATURE enable */
+#define OTG_CTL_WPCS_DEAS (2<<16) /* deassert port connect */
+#define OTG_CTL_WPCS_ASRT (3<<16) /* assert port connect */
+#define OTG_CTL_WPSS_DEAS (2<<18) /* deassert port suspend */
+#define OTG_CTL_WPSS_ASRT (3<<18) /* assert port suspend */
+/* timer conditions */
+#define OTG_CTL_TMR_RLP (1<<28) /* timer reload policy */
+#define OTG_CTL_TMR_ALL (0xf<<24) /* stop timer */
+#define OTG_CTL_TMR_STOP 0 /* timer disabled */
+#define OTG_CTL_TMR_UNCOND (1<<24) /* count unconditionally */
+#define OTG_CTL_TMR_SE0 (2<<24) /* count if LSt = FS-SE0 */
+#define OTG_CTL_TMR_FSJ (3<<24) /* count if LSt = FS-J */
+#define OTG_CTL_TMR_FSK (4<<24) /* count if LSt = FS-K */
+#define OTG_CTL_TMR_NOSE0 (5<<24) /* count if LSt <> FS-SE0 */
+#define OTG_CTL_TMR_NORX (6<<24) /* count if Rx inactiv */
+#define OTG_CTL_TMR_ID (7<<24) /* count if ID = 0 */
+
+/* interrupts */
+#define OTG_INT_GLOBAL (1<<31) /* global interrupt enable */
+#define OTG_INT_ENALL 0x7fff /* enable all */
+#define OTG_INT_DISALL 0 /* disable all */
+#define OTG_INT_IDC (1<<0) /* ID pin change */
+#define OTG_INT_VBVC (1<<1) /* VBUS valid change */
+#define OTG_INT_SVC (1<<2) /* Session valid change */
+#define OTG_INT_SEC (1<<3) /* Session end change */
+#define OTG_INT_LSTC (1<<4) /* Line state change */
+#define OTG_INT_PSPDC (1<<5) /* Port speed change */
+#define OTG_INT_FSOEC (1<<6) /* FS/LS OE change */
+#define OTG_INT_HSDD (1<<7) /* HS disconnect detected */
+#define OTG_INT_RXACT (1<<8) /* Rx activity detected */
+#define OTG_INT_PCC (1<<9) /* Port connect change */
+#define OTG_INT_PSC (1<<10) /* Port suspend change */
+#define OTG_INT_TMX (1<<11) /* Timer expired */
+#define OTG_INT_HNPFC (1<<12) /* HNP feature change */
+#define OTG_INT_OCD (1<<13) /* over current detected */
+#define OTG_INT_DPRC (1<<14) /* Downstream port reset change */
+
+#define OTG_INT_ADDS OTG_INT_SVC
+
+/**********************************
+ * OTG state dependend data
+ **********************************/
+
+/*
+ * generic
+ */
+#define OTG_CTL_DEFAULT (OTG_CTL_PADEN | \
+ OTG_CTL_IDSNSEN)
+#define OTG_CTL_HOST_DEFAULT (OTG_CTL_DEFAULT | \
+ OTG_CTL_ENABLE_UHC)
+#ifdef CONFIG_USB_OTG
+#define OTG_CTL_PERIPHERAL_DEFAULT (OTG_CTL_DEFAULT | \
+ OTG_CTL_HNPSFEN | \
+ OTG_CTL_ENABLE_UDC | \
+ OTG_CTL_PPO | OTG_CTL_PUEN)
+#else
+#define OTG_CTL_PERIPHERAL_DEFAULT (OTG_CTL_DEFAULT | \
+ OTG_CTL_ENABLE_UDC | \
+ OTG_CTL_PPO | OTG_CTL_PUEN)
+#endif
+
+#define OTG_INT_DEFAULT OTG_INT_IDC
+
+/*
+ * OTG_STATE_UNDEFINED
+ */
+#define OTG_STATE_UNDEFINED_CONTROL (OTG_CTL_DEFAULT | OTG_CTL_PPO | \
+ OTG_CTL_TMR_UNCOND)
+#define OTG_STATE_UNDEFINED_STATUS 0
+#define OTG_STATE_UNDEFINED_STATUS_MASK 0
+#define OTG_STATE_UNDEFINED_INTERRUPTS OTG_INT_TMX
+
+/*
+ * OTG_STATE_NO_B_DEVICE_A
+ */
+#define OTG_STATE_NO_B_DEVICE_A_CONTROL OTG_CTL_HOST_DEFAULT
+#define OTG_STATE_NO_B_DEVICE_A_STATUS 0
+#define OTG_STATE_NO_B_DEVICE_A_STATUS_MASK 0
+#define OTG_STATE_NO_B_DEVICE_A_INTERRUPTS OTG_INT_DEFAULT
+
+/*
+ * OTG_STATE_NO_B_DEVICE_B
+ */
+#define OTG_STATE_NO_B_DEVICE_B_CONTROL (OTG_CTL_DEFAULT | OTG_CTL_PPO)
+#define OTG_STATE_NO_B_DEVICE_B_STATUS 0
+#define OTG_STATE_NO_B_DEVICE_B_STATUS_MASK 0
+#define OTG_STATE_NO_B_DEVICE_B_INTERRUPTS OTG_INT_DEFAULT
+
+/*
+ * OTG_STATE_A_IDLE
+ */
+#define OTG_STATE_A_IDLE_CONTROL (OTG_CTL_DEFAULT | OTG_CTL_PPO)
+#define OTG_STATE_A_IDLE_STATUS 0
+#define OTG_STATE_A_IDLE_STATUS_MASK 0
+#define OTG_STATE_A_IDLE_INTERRUPTS (OTG_INT_DEFAULT | OTG_INT_SVC | \
+ OTG_INT_LSTC)
+
+/*
+ * OTG_STATE_A_IDLE_WAIT_DP
+ */
+#define OTG_STATE_A_IDLE_WAIT_DP_CONTROL (OTG_STATE_A_IDLE_CONTROL | \
+ OTG_CTL_TMR_UNCOND)
+#define OTG_STATE_A_IDLE_WAIT_DP_STATUS 0
+#define OTG_STATE_A_IDLE_WAIT_DP_STATUS_MASK 0
+#define OTG_STATE_A_IDLE_WAIT_DP_INTERRUPTS (OTG_STATE_A_IDLE_INTERRUPTS | \
+ OTG_INT_TMX)
+
+/*
+ * OTG_STATE_A_IDLE_WAIT_VP
+ */
+#define OTG_STATE_A_IDLE_WAIT_VP_CONTROL (OTG_STATE_A_IDLE_CONTROL | \
+ OTG_CTL_TMR_UNCOND)
+#define OTG_STATE_A_IDLE_WAIT_VP_STATUS 0
+#define OTG_STATE_A_IDLE_WAIT_VP_STATUS_MASK 0
+#define OTG_STATE_A_IDLE_WAIT_VP_INTERRUPTS (OTG_STATE_A_IDLE_INTERRUPTS | \
+ OTG_INT_TMX)
+
+/*
+ * OTG_STATE_A_IDLE_WAIT_MP
+ */
+#define OTG_STATE_A_IDLE_WAIT_MP_CONTROL (OTG_STATE_A_IDLE_CONTROL | \
+ OTG_CTL_TMR_UNCOND)
+#define OTG_STATE_A_IDLE_WAIT_MP_STATUS 0
+#define OTG_STATE_A_IDLE_WAIT_MP_STATUS_MASK 0
+#define OTG_STATE_A_IDLE_WAIT_MP_INTERRUPTS (OTG_STATE_A_IDLE_INTERRUPTS | \
+ OTG_INT_TMX)
+
+/*
+ * OTG_STATE_A_IDLE_WAIT_DV
+ */
+#define OTG_STATE_A_IDLE_WAIT_DV_CONTROL (OTG_STATE_A_IDLE_CONTROL | \
+ OTG_CTL_TMR_UNCOND)
+#define OTG_STATE_A_IDLE_WAIT_DV_STATUS 0
+#define OTG_STATE_A_IDLE_WAIT_DV_STATUS_MASK 0
+#define OTG_STATE_A_IDLE_WAIT_DV_INTERRUPTS (OTG_STATE_A_IDLE_INTERRUPTS | \
+ OTG_INT_TMX)
+
+/*
+ * OTG_STATE_A_WAIT_VRISE
+ */
+#define OTG_STATE_A_WAIT_VRISE_CONTROL (OTG_CTL_HOST_DEFAULT | \
+ OTG_CTL_TMR_UNCOND)
+#define OTG_STATE_A_WAIT_VRISE_STATUS 0
+#define OTG_STATE_A_WAIT_VRISE_STATUS_MASK 0
+#define OTG_STATE_A_WAIT_VRISE_INTERRUPTS (OTG_INT_DEFAULT | OTG_INT_TMX | \
+ OTG_INT_VBVC)
+
+/*
+ * OTG_STATE_A_WAIT_BCON
+ */
+#define OTG_STATE_A_WAIT_BCON_CONTROL OTG_CTL_HOST_DEFAULT
+#define OTG_STATE_A_WAIT_BCON_STATUS 0
+#define OTG_STATE_A_WAIT_BCON_STATUS_MASK 0
+#define OTG_STATE_A_WAIT_BCON_INTERRUPTS (OTG_INT_DEFAULT | OTG_INT_TMX | \
+ OTG_INT_VBVC | OTG_INT_PCC)
+
+/*
+ * OTG_STATE_A_WAIT_BCON_VB
+ */
+#define OTG_STATE_A_WAIT_BCON_VB_CONTROL (OTG_STATE_A_WAIT_BCON_CONTROL | \
+ OTG_CTL_TMR_UNCOND)
+#define OTG_STATE_A_WAIT_BCON_VB_STATUS 0
+#define OTG_STATE_A_WAIT_BCON_VB_STATUS_MASK 0
+#define OTG_STATE_A_WAIT_BCON_VB_INTERRUPTS OTG_STATE_A_WAIT_BCON_INTERRUPTS
+
+/*
+ * OTG_STATE_A_HOST
+ */
+#define OTG_STATE_A_HOST_CONTROL OTG_CTL_HOST_DEFAULT
+#define OTG_STATE_A_HOST_STATUS 0
+#define OTG_STATE_A_HOST_STATUS_MASK 0
+#ifdef CONFIG_USB_OTG
+#ifndef VERBOSE
+#define OTG_STATE_A_HOST_INTERRUPTS (OTG_INT_DEFAULT | \
+ OTG_INT_VBVC | OTG_INT_DPRC | \
+ OTG_INT_PCC | OTG_INT_PSC)
+#else
+#define OTG_STATE_A_HOST_INTERRUPTS (OTG_INT_DEFAULT | \
+ OTG_INT_VBVC | OTG_INT_DPRC | \
+ OTG_INT_PCC | OTG_INT_PSC | \
+ OTG_INT_PSPDC)
+/* OTG_INT_LSTC */
+#endif
+#else
+/* IDPIN mode only */
+#define OTG_STATE_A_HOST_INTERRUPTS OTG_INT_IDC
+#endif
+
+/*
+ * OTG_STATE_A_SUSPEND
+ */
+#define OTG_STATE_A_SUSPEND_CONTROL (OTG_CTL_HOST_DEFAULT | \
+ OTG_CTL_TMR_UNCOND)
+#define OTG_STATE_A_SUSPEND_STATUS 0
+#define OTG_STATE_A_SUSPEND_STATUS_MASK 0
+#define OTG_STATE_A_SUSPEND_INTERRUPTS (OTG_INT_DEFAULT | OTG_INT_TMX | \
+ OTG_INT_VBVC | OTG_INT_DPRC | \
+ OTG_INT_PCC | OTG_INT_PSC)
+
+/*
+ * OTG_STATE_A_PERIPHERAL
+ */
+#define OTG_STATE_A_PERIPHERAL_CONTROL (OTG_CTL_PERIPHERAL_DEFAULT | \
+ OTG_CTL_PPWR | OTG_CTL_DMPDEN)
+#define OTG_STATE_A_PERIPHERAL_STATUS 0
+#define OTG_STATE_A_PERIPHERAL_STATUS_MASK 0
+#ifndef VERBOSE
+#define OTG_STATE_A_PERIPHERAL_INTERRUPTS (OTG_INT_DEFAULT | \
+ OTG_INT_VBVC | OTG_INT_OCD | \
+ OTG_INT_PCC | OTG_INT_PSC)
+#else
+#define OTG_STATE_A_PERIPHERAL_INTERRUPTS (OTG_INT_DEFAULT | \
+ OTG_INT_VBVC | OTG_INT_OCD | \
+ OTG_INT_PCC | OTG_INT_PSC | \
+ OTG_INT_PSPDC)
+/* OTG_INT_LSTC */
+#endif
+
+/*
+ * OTG_STATE_A_VBUS_ERR
+ */
+#define OTG_STATE_A_VBUS_ERR_CONTROL (OTG_CTL_HOST_DEFAULT | \
+ OTG_CTL_PPO | OTG_CTL_DISCHRG)
+#define OTG_STATE_A_VBUS_ERR_STATUS 0
+#define OTG_STATE_A_VBUS_ERR_STATUS_MASK 0
+#define OTG_STATE_A_VBUS_ERR_INTERRUPTS OTG_INT_DEFAULT
+
+/*
+ * OTG_STATE_A_WAIT_VFALL
+ */
+#define OTG_STATE_A_WAIT_VFALL_CONTROL (OTG_CTL_HOST_DEFAULT | \
+ OTG_CTL_PPO)
+#define OTG_STATE_A_WAIT_VFALL_STATUS 0
+#define OTG_STATE_A_WAIT_VFALL_STATUS_MASK 0
+#define OTG_STATE_A_WAIT_VFALL_INTERRUPTS (OTG_INT_DEFAULT | OTG_INT_SEC)
+
+/*
+ * OTG_STATE_A_WAIT_VFALL_DN
+ */
+#define OTG_STATE_A_WAIT_VFALL_DN_CONTROL (OTG_STATE_A_WAIT_VFALL_CONTROL |
\
+ OTG_CTL_DISCHRG)
+#define OTG_STATE_A_WAIT_VFALL_DN_STATUS 0
+#define OTG_STATE_A_WAIT_VFALL_DN_STATUS_MASK 0
+#define OTG_STATE_A_WAIT_VFALL_DN_INTERRUPTS OTG_STATE_A_WAIT_VFALL_INTERRUPTS
+
+/*
+ * OTG_STATE_A_WAIT_BDISCON
+ */
+#define OTG_STATE_A_WAIT_BDISCON_CONTROL (OTG_CTL_DEFAULT | \
+ OTG_CTL_PPO | OTG_CTL_PPWR | \
+ OTG_CTL_TMR_UNCOND)
+#define OTG_STATE_A_WAIT_BDISCON_STATUS 0
+#define OTG_STATE_A_WAIT_BDISCON_STATUS_MASK 0
+#define OTG_STATE_A_WAIT_BDISCON_INTERRUPTS (OTG_INT_DEFAULT | OTG_INT_TMX | \
+ OTG_INT_VBVC | OTG_INT_OCD | \
+ OTG_INT_PSPDC | OTG_INT_LSTC)
+
+/*
+ * OTG_STATE_B_IDLE
+ */
+/*** HS-A0 WA: BUG-3885: VB_SESS_VLD value too high ***/
+/*** HS-A0 WA: BUG-3943: gadget suspend issue ***/
+#define OTG_STATE_B_IDLE_CONTROL (OTG_CTL_PERIPHERAL_DEFAULT & \
+ ~((u32) (OTG_CTL_PUEN | \
+ OTG_CTL_ENABLE_UDC)))
+#define OTG_STATE_B_IDLE_STATUS 0
+#define OTG_STATE_B_IDLE_STATUS_MASK 0
+#ifdef CONFIG_USB_OTG
+#define OTG_STATE_B_IDLE_INTERRUPTS (OTG_INT_DEFAULT | OTG_INT_SVC)
+#else
+#ifdef CONFIG_USB_OTGMUX_IDPIN
+/* IDPIN mode */
+#define OTG_STATE_B_IDLE_INTERRUPTS (OTG_INT_IDC | OTG_INT_SVC)
+#else
+/* gadget mode */
+#define OTG_STATE_B_IDLE_INTERRUPTS OTG_INT_SVC
+#endif
+#endif
+
+/*
+ * OTG_STATE_B_PERIPHERAL
+ */
+#define OTG_STATE_B_PERIPHERAL_CONTROL (OTG_CTL_PERIPHERAL_DEFAULT | \
+ OTG_CTL_DMPDEN)
+#define OTG_STATE_B_PERIPHERAL_STATUS 0
+#define OTG_STATE_B_PERIPHERAL_STATUS_MASK 0
+#ifdef CONFIG_USB_OTG
+#ifndef VERBOSE
+#define OTG_STATE_B_PERIPHERAL_INTERRUPTS (OTG_INT_DEFAULT | OTG_INT_SVC | \
+ OTG_INT_PCC | OTG_INT_PSC | \
+ OTG_INT_HNPFC)
+#else
+#define OTG_STATE_B_PERIPHERAL_INTERRUPTS (OTG_INT_DEFAULT | OTG_INT_SVC | \
+ OTG_INT_PCC | OTG_INT_PSC | \
+ OTG_INT_HNPFC | OTG_INT_PSPDC)
+/* OTG_INT_LSTC */
+#endif
+#else
+#ifdef CONFIG_USB_OTGMUX_IDPIN
+/* IDPIN mode */
+#define OTG_STATE_B_PERIPHERAL_INTERRUPTS (OTG_INT_IDC | OTG_INT_SVC)
+#else
+/* gadget mode */
+#define OTG_STATE_B_PERIPHERAL_INTERRUPTS OTG_INT_SVC
+#endif
+#endif
+
+/*
+ * OTG_STATE_B_PERIPHERAL_WT
+ */
+#define OTG_STATE_B_PERIPHERAL_WT_CONTROL (OTG_STATE_B_PERIPHERAL_CONTROL |
\
+ OTG_CTL_PPO | OTG_CTL_TMR_UNCOND)
+#define OTG_STATE_B_PERIPHERAL_WT_STATUS 0
+#define OTG_STATE_B_PERIPHERAL_WT_STATUS_MASK 0
+#define OTG_STATE_B_PERIPHERAL_WT_INTERRUPTS
(OTG_STATE_B_PERIPHERAL_INTERRUPTS\
+ | OTG_INT_TMX)
+
+/*
+ * OTG_STATE_B_PERIPHERAL_DC
+ */
+#define OTG_STATE_B_PERIPHERAL_DC_CONTROL (OTG_CTL_HOST_DEFAULT | \
+ OTG_CTL_PPO | OTG_CTL_DMPDEN | \
+ OTG_CTL_TMR_UNCOND)
+#define OTG_STATE_B_PERIPHERAL_DC_STATUS 0
+#define OTG_STATE_B_PERIPHERAL_DC_STATUS_MASK 0
+#define OTG_STATE_B_PERIPHERAL_DC_INTERRUPTS
(OTG_STATE_B_PERIPHERAL_INTERRUPTS\
+ | OTG_INT_TMX | OTG_INT_LSTC)
+
+/*
+ * OTG_STATE_B_WAIT_ACON
+ */
+#define OTG_STATE_B_WAIT_ACON_CONTROL (OTG_CTL_HOST_DEFAULT | \
+ OTG_CTL_PPO | OTG_CTL_TMR_UNCOND)
+#define OTG_STATE_B_WAIT_ACON_STATUS 0
+#define OTG_STATE_B_WAIT_ACON_STATUS_MASK 0
+#define OTG_STATE_B_WAIT_ACON_INTERRUPTS (OTG_INT_DEFAULT | OTG_INT_SVC | \
+ OTG_INT_PCC | OTG_INT_PSC | \
+ OTG_INT_HNPFC | OTG_INT_TMX)
+
+/*
+ * OTG_STATE_B_HOST
+ */
+#define OTG_STATE_B_HOST_CONTROL (OTG_CTL_HOST_DEFAULT | \
+ OTG_CTL_PPO)
+#define OTG_STATE_B_HOST_STATUS 0
+#define OTG_STATE_B_HOST_STATUS_MASK 0
+#define OTG_STATE_B_HOST_INTERRUPTS (OTG_INT_DEFAULT | OTG_INT_SVC | \
+ OTG_INT_PCC | OTG_INT_SVC | \
+ OTG_INT_PSPDC)
+
+/*
+ * OTG_STATE_B_HOST_WT
+ */
+#define OTG_STATE_B_HOST_WT_CONTROL (OTG_STATE_B_HOST_CONTROL | \
+ OTG_CTL_TMR_UNCOND)
+#define OTG_STATE_B_HOST_WT_STATUS 0
+#define OTG_STATE_B_HOST_WT_STATUS_MASK 0
+#define OTG_STATE_B_HOST_WT_INTERRUPTS (OTG_INT_DEFAULT | OTG_INT_SVC | \
+ OTG_INT_PCC | OTG_INT_TMX)
+
+/*
+ * OTG_STATE_B_SRP_INIT
+ */
+#define OTG_STATE_B_SRP_INIT_CONTROL OTG_STATE_B_IDLE_CONTROL
+#define OTG_STATE_B_SRP_INIT_STATUS 0
+#define OTG_STATE_B_SRP_INIT_STATUS_MASK 0
+#define OTG_STATE_B_SRP_INIT_INTERRUPTS OTG_INT_DEFAULT
+
+/*
+ * OTG_STATE_B_SRP_WTSE0
+ */
+#define OTG_STATE_B_SRP_WAIT_SE0_CONTROL (OTG_STATE_B_SRP_INIT_CONTROL | \
+ OTG_CTL_TMR_UNCOND)
+#define OTG_STATE_B_SRP_WAIT_SE0_STATUS 0
+#define OTG_STATE_B_SRP_WAIT_SE0_STATUS_MASK 0
+#define OTG_STATE_B_SRP_WAIT_SE0_INTERRUPTS (OTG_STATE_B_SRP_INIT_INTERRUPTS \
+ | OTG_INT_TMX | OTG_INT_LSTC)
+
+/*
+ * OTG_STATE_B_SRP_D_PLS
+ *
+ * note: changing to this state requires an additional call:
+ * set_srp_conditions (dev);
+ * reset_srp_conditions (dev) is required for the next state
+ */
+#define OTG_STATE_B_SRP_D_PULSE_CONTROL (OTG_CTL_PERIPHERAL_DEFAULT | \
+ OTG_CTL_PUEN | \
+ OTG_CTL_TMR_UNCOND)
+#define OTG_STATE_B_SRP_D_PULSE_STATUS 0
+#define OTG_STATE_B_SRP_D_PULSE_STATUS_MASK 0
+#define OTG_STATE_B_SRP_D_PULSE_INTERRUPTS (OTG_STATE_B_SRP_INIT_INTERRUPTS \
+ | OTG_INT_SEC | OTG_INT_TMX)
+
+/*
+ * OTG_STATE_B_SRP_V_PLS
+ */
+#define OTG_STATE_B_SRP_V_PULSE_CONTROL (OTG_STATE_B_SRP_INIT_CONTROL | \
+ OTG_CTL_CHRG | \
+ OTG_CTL_TMR_UNCOND)
+#define OTG_STATE_B_SRP_V_PULSE_STATUS 0
+#define OTG_STATE_B_SRP_V_PULSE_STATUS_MASK 0
+#define OTG_STATE_B_SRP_V_PULSE_INTERRUPTS (OTG_STATE_B_SRP_INIT_INTERRUPTS \
+ | OTG_INT_TMX)
+
+/*
+ * OTG_STATE_B_SRP_V_DCG
+ */
+#define OTG_STATE_B_SRP_V_DCHRG_CONTROL (OTG_STATE_B_SRP_INIT_CONTROL | \
+ OTG_CTL_DISCHRG | \
+ OTG_CTL_TMR_UNCOND)
+#define OTG_STATE_B_SRP_V_DCHRG_STATUS 0
+#define OTG_STATE_B_SRP_V_DCHRG_STATUS_MASK 0
+#define OTG_STATE_B_SRP_V_DCHRG_INTERRUPTS (OTG_STATE_B_SRP_INIT_INTERRUPTS \
+ | OTG_INT_TMX)
+
+/*
+ * OTG_STATE_B_SRP_WTVB
+ */
+#define OTG_STATE_B_SRP_WAIT_VBUS_CONTROL (OTG_STATE_B_SRP_INIT_CONTROL | \
+ OTG_CTL_TMR_UNCOND)
+#define OTG_STATE_B_SRP_WAIT_VBUS_STATUS 0
+#define OTG_STATE_B_SRP_WAIT_VBUS_STATUS_MASK 0
+#define OTG_STATE_B_SRP_WAIT_VBUS_INTERRUPTS (OTG_STATE_B_SRP_INIT_INTERRUPTS \
+ | OTG_INT_SVC | OTG_INT_TMX)
+
+/*********************************/
+
+/* other */
+
+#define OTG_APP_REQ_ACK 0
+
+
+/*****************************************************************************
+* Types
+*****************************************************************************/
+
+
+/*****************************************************************************
+* Macros
+*****************************************************************************/
+
+/* printing messages */
+
+#define INFO(args...) \
+ printk(KERN_INFO DRIVER_NAME_FOR_PRINT ": " args)
+
+#ifdef WARN
+#undef WARN
+#endif
+
+#define WARN(args...) \
+ printk(KERN_WARNING DRIVER_NAME_FOR_PRINT " warning: " args)
+
+#define ERR(args...) \
+ printk(KERN_ERR DRIVER_NAME_FOR_PRINT " error: " args)
+
+#ifdef DEBUG
+#define DBG(args...) \
+ printk(KERN_DEBUG DRIVER_NAME_FOR_PRINT " debug: " args)
+#else
+#define DBG(args...) \
+ do {} while (0)
+#endif
+
+#ifdef VERBOSE
+#define VDBG DBG
+#else
+#define VDBG(args...) \
+ do { } while (0)
+#endif
+
+/****************************************************************************/
+
+/* this should always return "1" and print something in verbose mode */
+#ifdef VERBOSE
+#define VDBG_SPC(fmt, args...) \
+ (VDBG(fmt, args) ? 1 : 1)
+#else
+#define VDBG_SPC(fmt, args...) 1
+#endif
+
+/* query bit(s) (long: 32-bit access) */
+#define IS_BIT_RES(data, code) \
+ (!((data) & (code)) ? \
+ (VDBG_SPC(" OTG HW status: %s is reset\n", #data)) : 0)
+
+#define IS_BIT_SET(data, code) \
+ (((data) & (code)) ? \
+ (VDBG_SPC(" OTG HW status: %s is set\n", #data)) : 0)
+
+/* query SW flag(s) */
+#define IS_FLAG_RES(dev, data) \
+ (!((data) & (dev)->transceiver.params) ? \
+ (VDBG_SPC(" OTG SW status: %s is reset\n", #data)) : 0)
+
+#define IS_FLAG_SET(dev, data) \
+ (((data) & (dev)->transceiver.params) ? \
+ (VDBG_SPC(" OTG SW status: %s is set\n", #data)) : 0)
+
+/* query event bit(s) */
+#define GOT_EVENT(data, code) \
+ (((data) & (code)) ? \
+ (VDBG_SPC(" OTG event: %s\n", #data)) : 0)
+
+/* set SW flag */
+#ifdef VERBOSE
+#define SET_FLAG(dev, data) \
+do { \
+ if (!((data) & (dev)->transceiver.params)) \
+ DBG(" OTG SW status change: set flag %s\n", #data); \
+ (dev)->transceiver.params |= (data) \
+} while (0);
+#else
+#define SET_FLAG(dev, data) \
+ ((dev)->transceiver.params |= (data))
+#endif
+
+/* reset SW flag */
+#ifdef VERBOSE
+#define RES_FLAG(dev, data) \
+do { \
+ if ((data) & (dev)->transceiver.params) \
+ DBG(" OTG SW status change: reset flag %s\n", #data); \
+ (dev)->transceiver.params &= ~((u32) (data)) \
+} while (0);
+#else
+#define RES_FLAG(dev, data) \
+ ((dev)->transceiver.params &= ~((u32) (data)))
+#endif
+
+/* reset event bit */
+#define RES_EVENT(data, code) \
+ ((code) &= ~((u32) (data)))
+/* NOTE: this is not really needed so far, might be replaced with */
+/* #define RES_EVENT(data, code) \ */
+/* do {} while (0) */
+
+/* change OTG state */
+#ifdef CONFIG_USB_OTG
+#define PREPARE_STATE_CHANGE(dev, new_state) \
+ switch ((new_state) & OTG_STATE_MASK) { \
+ case OTG_STATE_UNDEFINED: \
+ set_undef_state_defaults((dev)); \
+ break; \
+ case OTG_STATE_A_IDLE: \
+ set_a_state_defaults((dev)); \
+ break; \
+ case OTG_STATE_B_IDLE: \
+ set_b_state_defaults((dev)); \
+ break; \
+ default: \
+ break; \
+ } \
+ do {} while (0)
+#else
+#define PREPARE_STATE_CHANGE(dev, new_state) \
+ do {} while (0)
+#endif
+
+#define CHANGE_STATE(dev, new_state, pMask) \
+do { \
+ PREPARE_STATE_CHANGE(dev, new_state); \
+ iowrite32((new_state##_CONTROL), &(dev)->regs->ctl); \
+ *(pMask) = (new_state##_INTERRUPTS); \
+ (dev)->transceiver.state = (new_state); \
+ DBG("OTG new state: %s\n", #new_state); \
+} while (0);
+
+/* verify OTG state */
+#ifndef CONFIG_OTG_TEST_MODE
+
+#define CHECK_STATE(dev, act_state, pMask) \
+do { \
+ *(pMask) = (act_state##_INTERRUPTS); \
+ (dev)->transceiver.prv_state = (act_state); \
+ VDBG("OTG state: %s\n", #act_state); \
+} while (0);
+#else
+#define CHECK_STATE(dev, act_state, pMask) \
+do {\
+ *(pMask) = (act_state##_INTERRUPTS); \
+ (dev)->transceiver.prv_state = (act_state); \
+ if (((ioread32(&(dev)->regs->sts) ^ (act_state##_STATUS))) & \
+ act_state##_STATUS_MASK) \
+ WARN("OTG warning: incorrect status\n"); \
+ VDBG("OTG state: %s\n", #act_state); \
+} while (0);
+#endif
+
+/* set timer */
+#define SET_OTG_TIMER(dev, val) \
+ set_timer((dev), ((OTG_TMR_##val) * 100))
+
+/* set timer (<1ms) */
+#define SET_OTG_TIMER_SHORT(dev, val) \
+ set_timer((dev), ((OTG_TMR_##val) / 10))
+
+/* set timer (>10ms) */
+#define SET_OTG_TIMER_LONG(dev, val) \
+ set_timer_long ((dev), ((OTG_TMR_##val) / 10))
+
+#ifdef VERBOSE
+#define HS_DISCON_WARNING() \
+ if (!(OTG_CTL_ENABLE_UHC ^ \
+ (OTG_CTL_MUX_MASK & ioread32(&dev->regs->ctl))) && \
+ !(OTG_STS_PSPD & ioread32(&dev->regs->sts))) \
+ DBG(" OTG warning: disable UHC from HS-mode\n")
+#else
+#define HS_DISCON_WARNING() \
+ do { } while (0)
+#endif
+
+
+/*****************************************************************************
+* Data
+*****************************************************************************/
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+struct otg_regs {
+ u32 cap; /* capabilities */
+ u32 mux; /* mux */
+ u32 sts; /* status */
+ u32 ctl; /* control */
+ u32 tmr; /* timer */
+ u32 intr; /* interrupt request */
+ u32 inten; /* interrupt enable */
+} __attribute__ ((packed));
+
+
+struct otg {
+ spinlock_t lock;
+ unsigned enabled:1,
+ got_irq : 1,
+ region : 1;
+ u16 chiprev;
+
+ struct platform_device *pdev;
+ struct otg_regs *regs;
+ struct otg_transceiver transceiver;
+};
+#define otg_transceiver_to_otg(pTransceiver) \
+ container_of(otg, struct otg, pTransceiver)
+#define otg_to_transceiver(pOtg) \
+ (&pOtg->transceiver)
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif
+
+/*****************************************************************************
+* Functions
+*****************************************************************************/
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+extern int usb_gadget_register_otg(struct otg_transceiver * (
+ *get_transceiver)(void));
+extern int usb_gadget_unregister_otg(void);
+
+void otg_init_state(struct otg *);
+int otg_exit_state(struct otg *);
+
+#ifdef DEBUG
+static void print_regs(struct otg *);
+#endif /* DEBUG */
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif
+
+
+/*****************************************************************************
+* Inline Functions
+*****************************************************************************/
+
+extern u32 otg_tmr_high_count;
+extern struct otg_ctl *otg_ctl;
+
+#ifdef CONFIG_USB_OTG
+/**
+ * \brief
+ * set neutral state information
+ *
+ * \param dev OTG controller info
+ *
+ * \return void
+ */
+static inline void set_undef_state_defaults(struct otg *dev)
+{
+ dev->transceiver.default_a = 0;
+ if (dev->transceiver.host)
+ dev->transceiver.host->is_b_host = 0;
+ if (dev->transceiver.gadget)
+ dev->transceiver.gadget->is_a_peripheral = 0;
+}
+
+/**
+ * \brief
+ * set A state information
+ *
+ * \param dev OTG controller info
+ *
+ * \return void
+ */
+static inline void set_a_state_defaults(struct otg *dev)
+{
+ dev->transceiver.default_a = 1;
+ if (dev->transceiver.host)
+ dev->transceiver.host->is_b_host = 0;
+ if (dev->transceiver.gadget)
+ dev->transceiver.gadget->is_a_peripheral = 1;
+}
+
+/**
+ * \brief
+ * set B state information
+ *
+ * \param dev OTG controller info
+ *
+ * \return void
+ */
+static inline void set_b_state_defaults(struct otg *dev)
+{
+ dev->transceiver.default_a = 0;
+ if (dev->transceiver.host)
+ dev->transceiver.host->is_b_host = 1;
+ if (dev->transceiver.gadget)
+ dev->transceiver.gadget->is_a_peripheral = 0;
+}
+
+/**
+ * \brief
+ * set B state information
+ *
+ * \param dev OTG controller info
+ *
+ * \return void
+ */
+static inline void reset_b_hnp_enable(struct otg *dev)
+{
+ if (dev->transceiver.host)
+ dev->transceiver.host->b_hnp_enable = 0;
+ VDBG(" OTG action: HNP disabled in B-device\n");
+}
+
+/**
+ * \brief
+ * set B state information
+ *
+ * \param dev OTG controller info
+ *
+ * \return void
+ */
+static inline int is_b_hnp_enabled(struct otg *dev)
+{
+ int retVal = 0;
+
+ if (dev->transceiver.host &&
+ dev->transceiver.host->b_hnp_enable) {
+ VDBG(" OTG status: HNP is enabled in HS-B-device\n");
+ retVal = 1;
+ }
+#ifdef VERBOSE
+ else
+ DBG(" OTG status: HNP is disabled in B-device\n");
+#endif
+ return retVal;
+}
+#endif
+
+/**
+ * \brief
+ * Read the status register
+ *
+ * \param dev OTG controller info
+ *
+ * \return status
+ */
+static inline u32 get_status(struct otg *dev)
+{
+ return ioread32(&dev->regs->sts);
+}
+
+/**
+ * \brief
+ * Load and start the timer for an unconditional run
+ *
+ * \param dev OTG controller info
+ * \param val Value to load
+ *
+ * \return void
+ */
+static inline void set_timer(struct otg *dev, u32 val)
+{
+ otg_tmr_high_count = 0;
+
+ iowrite32((val), &dev->regs->tmr);
+ VDBG(" OTG action: start timer: %d0 us\n", val);
+}
+
+/**
+ * \brief
+ * Load and start the timer for an unconditional run
+ *
+ * \param dev OTG controller info
+ * \param val Value to load
+ *
+ * \return void
+ */
+static inline void set_timer_long(struct otg *dev, u32 val)
+{
+ otg_tmr_high_count = val - 1;
+
+ iowrite32(TIMER_PERIOD, &dev->regs->tmr);
+ VDBG(" OTG action: start timer: %d0 ms\n", val);
+}
+
+/**
+ * \brief
+ * Re-start the timer (value already loaded)
+ *
+ * \param dev OTG controller info
+ *
+ * \return void
+ */
+static inline void restart_timer(struct otg *dev)
+{
+ iowrite32((ioread32(&dev->regs->ctl) | OTG_CTL_TMR_UNCOND),
+ &dev->regs->ctl);
+}
+
+/**
+ * \brief
+ * Reset the timer while running (value already loaded)
+ *
+ * \param dev OTG controller info
+ *
+ * \return void
+ */
+static inline void reset_timer(struct otg *dev)
+{
+ u32 temp;
+
+ temp = ioread32(&dev->regs->ctl);
+ iowrite32((temp & ~((u32) OTG_CTL_TMR_ALL)), &dev->regs->ctl);
+ iowrite32(temp, &dev->regs->ctl);
+ VDBG(" OTG action: re-start timer\n");
+}
+
+/**
+ * \brief
+ * Prepare the D-pulse
+ *
+ * \param dev OTG controller info
+ *
+ * \return void
+ */
+static inline void set_srp_conditions(struct otg *dev)
+{
+ VDBG(" OTG action: SRP init: no action needed due to A0 WAs\n");
+}
+
+/**
+ * \brief
+ * Reset conditions after SRP
+ *
+ * activates the auto-pull-up feature so after SRP the host
+ * will detect a device connect after calling this function
+ *
+ * \param dev OTG controller info
+ *
+ * \return void
+ */
+static inline void reset_srp_conditions(struct otg *dev)
+{
+ VDBG(" OTG action: SRP done: no action needed due to A0 WAs\n");
+}
+
+/**
+ * \brief
+ * enable HNP for both devices
+ *
+ * \param dev OTG controller info
+ *
+ * \return success
+ */
+static inline int otg_enable_hnp(struct otg *dev)
+{
+ int retVal = 0;
+ return retVal;
+}
+
+#ifdef DEBUG
+/**
+ * \brief
+ * Print OTG controller registers (debug mode only)
+ *
+ * \param dev OTG controller info
+ *
+ * \return void
+ */
+static inline void print_regs(struct otg *dev)
+{
+ DBG("-- UOC registers ---\n");
+ DBG("otg cap = %08x\n", ioread32(&dev->regs->cap));
+ DBG("otg mux = %08x\n", ioread32(&dev->regs->mux));
+ DBG("otg sts = %08x\n", ioread32(&dev->regs->sts));
+ DBG("otg ctl = %08x\n", ioread32(&dev->regs->ctl));
+ DBG("otg tmr = %08x\n", ioread32(&dev->regs->tmr));
+ DBG("otg intr = %08x\n", ioread32(&dev->regs->intr));
+ DBG("otg inten = %08x\n", ioread32(&dev->regs->inten));
+ DBG("--------------------\n");
+}
+#endif /* DEBUG */
+
+#endif /* AU1200_UOC_H */
diff --git a/drivers/usb/gadget/gadget_chips.h
b/drivers/usb/gadget/gadget_chips.h
index 17d9905..8151d74 100644
--- a/drivers/usb/gadget/gadget_chips.h
+++ b/drivers/usb/gadget/gadget_chips.h
@@ -78,6 +78,12 @@
#define gadget_is_omap(g) 0
#endif
+#ifdef CONFIG_USB_GADGET_AU1200
+#define gadget_is_au1200(g) !strcmp("au1200_udc", (g)->name)
+#else
+#define gadget_is_au1200(g) 0
+#endif
+
/* not yet ported 2.4 --> 2.6 */
#ifdef CONFIG_USB_GADGET_N9604
#define gadget_is_n9604(g) !strcmp("n9604_udc", (g)->name)
diff --git a/include/linux/usb/otg.h b/include/linux/usb/otg.h
index 1db25d1..5b88c43 100644
--- a/include/linux/usb/otg.h
+++ b/include/linux/usb/otg.h
@@ -45,7 +45,12 @@ struct otg_transceiver {
u8 default_a;
enum usb_otg_state state;
-
+#ifdef CONFIG_USB_PORT_AU1200OTG
+ u8 prv_state;
+ u32 params;
+ void *otg_priv;
+ u8 hostcount;
+#endif
struct usb_bus *host;
struct usb_gadget *gadget;
--
1.5.4.3
|