* [PATCH] Au1200 USB Device Controller and device-only OTG
@ 2008-09-11 17:59 Kevin Hickey
2008-09-15 19:16 ` David Brownell
0 siblings, 1 reply; 5+ messages in thread
From: Kevin Hickey @ 2008-09-11 17:59 UTC (permalink / raw)
To: linux-mips, ralf, linux-usb, mano; +Cc: Kevin Hickey
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
^ permalink raw reply related [flat|nested] 5+ messages in thread
* Re: [PATCH] Au1200 USB Device Controller and device-only OTG
2008-09-11 17:59 [PATCH] Au1200 USB Device Controller and device-only OTG Kevin Hickey
@ 2008-09-15 19:16 ` David Brownell
2008-09-15 19:26 ` Kevin Hickey
0 siblings, 1 reply; 5+ messages in thread
From: David Brownell @ 2008-09-15 19:16 UTC (permalink / raw)
To: Kevin Hickey; +Cc: linux-mips, ralf, linux-usb, mano
On Thursday 11 September 2008, Kevin Hickey wrote:
> basic device-only OTG (On-The-Go) support
That does't look like it's done right. For starters, it abuses
Kconfig to handle a board-specific config option. Put that data
in platform_data instead ...
Second, it breaks some previously-working code.
Third, it misbehaves even on an x86 config. Needs something like
the appended patch.
- Dave
--- g26.orig/drivers/usb/gadget/Kconfig 2008-09-15 12:10:22.000000000 -0700
+++ g26/drivers/usb/gadget/Kconfig 2008-09-15 12:10:06.000000000 -0700
@@ -490,7 +490,7 @@ config USB_GADGET_DUALSPEED
config USB_PORT_AU1200OTG
boolean "AU1200 USB portmux control (On-The-Go support)"
- depends on USB_GADGET_AU1200 || USB_EHCI_HCD || USB_OHCI_HCD
+ 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
^ permalink raw reply [flat|nested] 5+ messages in thread
* Re: [PATCH] Au1200 USB Device Controller and device-only OTG
2008-09-15 19:16 ` David Brownell
@ 2008-09-15 19:26 ` Kevin Hickey
2008-09-15 19:26 ` Kevin Hickey
2008-09-16 6:53 ` David Brownell
0 siblings, 2 replies; 5+ messages in thread
From: Kevin Hickey @ 2008-09-15 19:26 UTC (permalink / raw)
To: David Brownell; +Cc: linux-mips, ralf, linux-usb, mano
[-- Attachment #1: Type: text/plain, Size: 1348 bytes --]
On Mon, 2008-09-15 at 12:16 -0700, David Brownell wrote:
> On Thursday 11 September 2008, Kevin Hickey wrote:
> > basic device-only OTG (On-The-Go) support
>
> That does't look like it's done right. For starters, it abuses
> Kconfig to handle a board-specific config option. Put that data
> in platform_data instead ...
I don't understand what you mean by this. Can you be more specific?
>
> Second, it breaks some previously-working code.
Can you be more specific?
>
> Third, it misbehaves even on an x86 config. Needs something like
> the appended patch.
Does it only misbehave on an x86 config or also on a MIPS config? I
have no problems when building for DB1200.
>
> - Dave
>
>
> --- g26.orig/drivers/usb/gadget/Kconfig 2008-09-15 12:10:22.000000000 -0700
> +++ g26/drivers/usb/gadget/Kconfig 2008-09-15 12:10:06.000000000 -0700
> @@ -490,7 +490,7 @@ config USB_GADGET_DUALSPEED
>
> config USB_PORT_AU1200OTG
> boolean "AU1200 USB portmux control (On-The-Go support)"
> - depends on USB_GADGET_AU1200 || USB_EHCI_HCD || USB_OHCI_HCD
> + 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
>
--
Kevin Hickey
Alchemy Solutions
RMI Corporation
khickey@rmicorp.com
P: 512.691.8044
[-- Attachment #2: Type: text/html, Size: 2263 bytes --]
^ permalink raw reply [flat|nested] 5+ messages in thread
* Re: [PATCH] Au1200 USB Device Controller and device-only OTG
2008-09-15 19:26 ` Kevin Hickey
@ 2008-09-15 19:26 ` Kevin Hickey
2008-09-16 6:53 ` David Brownell
1 sibling, 0 replies; 5+ messages in thread
From: Kevin Hickey @ 2008-09-15 19:26 UTC (permalink / raw)
To: David Brownell; +Cc: linux-mips, ralf, linux-usb, mano
[-- Attachment #1: Type: text/plain, Size: 1348 bytes --]
On Mon, 2008-09-15 at 12:16 -0700, David Brownell wrote:
> On Thursday 11 September 2008, Kevin Hickey wrote:
> > basic device-only OTG (On-The-Go) support
>
> That does't look like it's done right. For starters, it abuses
> Kconfig to handle a board-specific config option. Put that data
> in platform_data instead ...
I don't understand what you mean by this. Can you be more specific?
>
> Second, it breaks some previously-working code.
Can you be more specific?
>
> Third, it misbehaves even on an x86 config. Needs something like
> the appended patch.
Does it only misbehave on an x86 config or also on a MIPS config? I
have no problems when building for DB1200.
>
> - Dave
>
>
> --- g26.orig/drivers/usb/gadget/Kconfig 2008-09-15 12:10:22.000000000 -0700
> +++ g26/drivers/usb/gadget/Kconfig 2008-09-15 12:10:06.000000000 -0700
> @@ -490,7 +490,7 @@ config USB_GADGET_DUALSPEED
>
> config USB_PORT_AU1200OTG
> boolean "AU1200 USB portmux control (On-The-Go support)"
> - depends on USB_GADGET_AU1200 || USB_EHCI_HCD || USB_OHCI_HCD
> + 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
>
--
Kevin Hickey
Alchemy Solutions
RMI Corporation
khickey@rmicorp.com
P: 512.691.8044
[-- Attachment #2: Type: text/html, Size: 2263 bytes --]
^ permalink raw reply [flat|nested] 5+ messages in thread
* Re: [PATCH] Au1200 USB Device Controller and device-only OTG
2008-09-15 19:26 ` Kevin Hickey
2008-09-15 19:26 ` Kevin Hickey
@ 2008-09-16 6:53 ` David Brownell
1 sibling, 0 replies; 5+ messages in thread
From: David Brownell @ 2008-09-16 6:53 UTC (permalink / raw)
To: Kevin Hickey; +Cc: linux-mips, ralf, linux-usb, mano
On Monday 15 September 2008, Kevin Hickey wrote:
> On Mon, 2008-09-15 at 12:16 -0700, David Brownell wrote:
> > On Thursday 11 September 2008, Kevin Hickey wrote:
> > > basic device-only OTG (On-The-Go) support
> >
> > That does't look like it's done right. For starters, it abuses
> > Kconfig to handle a board-specific config option. Put that data
> > in platform_data instead ...
>
> I don't understand what you mean by this. Can you be more specific?
The need for CONFIG_USB_AU1200OTG is board-specific,
and doesn't belong in Kconfig.
Also, the au1200_otg code should live with platform code ...
plan for it to become "real OTG support" (at least for cable
based role switching), and then it becomes clear that it does
not belong in the drivers/usb host or gadget directories (since
it affects both). At this point I have a preference for such
stuff to live in arch/... directories
> > Second, it breaks some previously-working code.
>
> Can you be more specific?
Breaks the orignal OMAP OTG support:
> > -config USB_OTG
> > - boolean "OTG Support"
> > - depends on USB_GADGET_OMAP && ARCH_OMAP_OTG && USB_OHCI_HCD
... by removing that stuff.
> > Third, it misbehaves even on an x86 config. Needs something like
> > the appended patch.
>
> Does it only misbehave on an x86 config or also on a MIPS config? I
> have no problems when building for DB1200.
Read the patch and you'll see what's going on. Any non-MIPS config
gets broken.
- Dave
> >
> > - Dave
> >
> >
> > --- g26.orig/drivers/usb/gadget/Kconfig 2008-09-15 12:10:22.000000000 -0700
> > +++ g26/drivers/usb/gadget/Kconfig 2008-09-15 12:10:06.000000000 -0700
> > @@ -490,7 +490,7 @@ config USB_GADGET_DUALSPEED
> >
> > config USB_PORT_AU1200OTG
> > boolean "AU1200 USB portmux control (On-The-Go support)"
> > - depends on USB_GADGET_AU1200 || USB_EHCI_HCD || USB_OHCI_HCD
> > + 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
> >
>
^ permalink raw reply [flat|nested] 5+ messages in thread
end of thread, other threads:[~2008-09-16 6:54 UTC | newest]
Thread overview: 5+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2008-09-11 17:59 [PATCH] Au1200 USB Device Controller and device-only OTG Kevin Hickey
2008-09-15 19:16 ` David Brownell
2008-09-15 19:26 ` Kevin Hickey
2008-09-15 19:26 ` Kevin Hickey
2008-09-16 6:53 ` David Brownell
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox