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@xxxxxxxxxxx> --- 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@xxxxxxxxxxx) + * + * 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@xxxxxxxxxxx) + * + * 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@xxxxxxxxxxx) + * + * 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@xxxxxxxxxxx) + * + * 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@xxxxxxxxxxx) + * + * 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