lots of usb patches
diff --git a/series b/series
index d97ef42..b143842 100644
--- a/series
+++ b/series
@@ -222,6 +222,25 @@
usb/usb-usbnet-reports-minidriver-name-through-ethtool.patch
usb/usb-add-an-ohci-board-specific-quirk.patch
usb/usb-sierra-add-more-checks-on-shutdown.patch
+usb/usb-elan-ftdi-check-for-driver-registration-status.patch
+usb/usb-gadget-rndis-fix-struct-rndis_packet_msg_type-unaligned-bug.patch
+usb/usb-bandrich-bandluxe-hsdpa-data-card-driver.patch
+usb/usb-remove-duplicate-define-of-ohci_quirk_zfmicro.patch
+usb/usbatm-detect-usb-device-shutdown-and-ignore-failed-urbs.patch
+usb/usb-cxacru-adsl-state-management.patch
+usb/usb-add-busnum-attribute-for-usb-devices.patch
+usb/usb-ethernet-gadget-workaround-network-stack-api-glitch.patch
+usb/usb-remove-ancient-broken-cris-hcd.patch
+usb/usbfs-micro-optimitation.patch
+usb/usb-add-picdem-device-to-ldusb.patch
+usb/usb-cp2101-new-device-ids.patch
+usb/usb-remove-huawei-unusual_devs-entry.patch
+usb/usb-dell-device-id-for-option.c.patch
+usb/usb-iowarrior.c-timeouts-too-small-in-usb_control_msg-calls.patch
+usb/usb-quirk-for-broken-suspend-of-it8152f-g.patch
+usb/usb-add-freescale-high-speed-usb-soc-device-controller-driver.patch
+usb/usb-update-gadget-files-for-fsl_usb2_udc-driver.patch
+usb/usb-update-mainainers-and-credits-for-freescale-usb-driver.patch
# my ols tutorial driver, never in mainline
usb/usb-gotemp.patch
diff --git a/usb/usb-add-busnum-attribute-for-usb-devices.patch b/usb/usb-add-busnum-attribute-for-usb-devices.patch
new file mode 100644
index 0000000..010c1de
--- /dev/null
+++ b/usb/usb-add-busnum-attribute-for-usb-devices.patch
@@ -0,0 +1,44 @@
+From stern@rowland.harvard.edu Wed Apr 25 12:15:49 2007
+From: Alan Stern <stern@rowland.harvard.edu>
+Date: Wed, 25 Apr 2007 15:15:43 -0400 (EDT)
+Subject: USB: add "busnum" attribute for USB devices
+To: Greg KH <greg@kroah.com>
+Message-ID: <Pine.LNX.4.44L0.0704251514580.2650-100000@iolanthe.rowland.org>
+
+
+This patch (as903) adds a "busnum" sysfs attribute for USB devices.
+
+Signed-off-by: Alan Stern <stern@rowland.harvard.edu>
+Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
+
+---
+ drivers/usb/core/sysfs.c | 11 +++++++++++
+ 1 file changed, 11 insertions(+)
+
+--- a/drivers/usb/core/sysfs.c
++++ b/drivers/usb/core/sysfs.c
+@@ -118,6 +118,16 @@ show_speed(struct device *dev, struct de
+ static DEVICE_ATTR(speed, S_IRUGO, show_speed, NULL);
+
+ static ssize_t
++show_busnum(struct device *dev, struct device_attribute *attr, char *buf)
++{
++ struct usb_device *udev;
++
++ udev = to_usb_device(dev);
++ return sprintf(buf, "%d\n", udev->bus->busnum);
++}
++static DEVICE_ATTR(busnum, S_IRUGO, show_busnum, NULL);
++
++static ssize_t
+ show_devnum(struct device *dev, struct device_attribute *attr, char *buf)
+ {
+ struct usb_device *udev;
+@@ -347,6 +357,7 @@ static struct attribute *dev_attrs[] = {
+ &dev_attr_bNumConfigurations.attr,
+ &dev_attr_bMaxPacketSize0.attr,
+ &dev_attr_speed.attr,
++ &dev_attr_busnum.attr,
+ &dev_attr_devnum.attr,
+ &dev_attr_version.attr,
+ &dev_attr_maxchild.attr,
diff --git a/usb/usb-add-freescale-high-speed-usb-soc-device-controller-driver.patch b/usb/usb-add-freescale-high-speed-usb-soc-device-controller-driver.patch
new file mode 100644
index 0000000..148fa7c
--- /dev/null
+++ b/usb/usb-add-freescale-high-speed-usb-soc-device-controller-driver.patch
@@ -0,0 +1,3158 @@
+From david-b@pacbell.net Mon Apr 23 10:54:37 2007
+From: Li Yang <leoli@freescale.com>
+Date: Mon, 23 Apr 2007 10:54:25 -0700
+Subject: USB: add Freescale high-speed USB SOC device controller driver
+To: Greg KH <greg@kroah.com>
+Cc: Li Yang <leoli@freescale.com>
+Message-ID: <200704231054.27400.david-b@pacbell.net>
+Content-Disposition: inline
+
+
+From: Li Yang <leoli@freescale.com>
+
+Freescale high-speed USB SOC can be found on some Freescale processors
+among different architectures. It supports both host and device functions.
+This driver adds its device support for Linux USB Gadget layer.
+It is tested on MPC8349 and MPC8313, but should work on other platforms
+with minor tweaks. The driver passed USBCV 1.3 compliance tests. Note
+that this driver doesn't yet include OTG support.
+
+Signed-off-by: Li Yang <leoli@freescale.com>
+Signed-off-by: Jiang Bo <tanya.jiang@freescale.com>
+Signed-off-by: Bruce Schmid <duck@freescale.com>
+Signed-off-by: David Brownell <dbrownell@users.sourceforge.net>
+Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
+
+
+---
+ drivers/usb/gadget/Kconfig | 21
+ drivers/usb/gadget/Makefile | 1
+ drivers/usb/gadget/fsl_usb2_udc.c | 2500 ++++++++++++++++++++++++++++++++++++++
+ drivers/usb/gadget/fsl_usb2_udc.h | 579 ++++++++
+ 4 files changed, 3101 insertions(+)
+
+--- a/drivers/usb/gadget/Kconfig
++++ b/drivers/usb/gadget/Kconfig
+@@ -68,6 +68,27 @@ choice
+ Many controller drivers are platform-specific; these
+ often need board-specific hooks.
+
++config USB_GADGET_FSL_USB2
++ boolean "Freescale Highspeed USB DR Peripheral Controller"
++ depends on MPC834x || PPC_MPC831x
++ select USB_GADGET_DUALSPEED
++ help
++ Some of Freescale PowerPC processors have a High Speed
++ Dual-Role(DR) USB controller, which supports device mode.
++
++ The number of programmable endpoints is different through
++ SOC revisions.
++
++ Say "y" to link the driver statically, or "m" to build a
++ dynamically linked module called "fsl_usb2_udc" and force
++ all gadget drivers to also be dynamically linked.
++
++config USB_FSL_USB2
++ tristate
++ depends on USB_GADGET_FSL_USB2
++ default USB_GADGET
++ select USB_GADGET_SELECTED
++
+ config USB_GADGET_NET2280
+ boolean "NetChip 228x"
+ depends on PCI
+--- a/drivers/usb/gadget/Makefile
++++ b/drivers/usb/gadget/Makefile
+@@ -8,6 +8,7 @@ obj-$(CONFIG_USB_GOKU) += goku_udc.o
+ obj-$(CONFIG_USB_OMAP) += omap_udc.o
+ obj-$(CONFIG_USB_LH7A40X) += lh7a40x_udc.o
+ obj-$(CONFIG_USB_AT91) += at91_udc.o
++obj-$(CONFIG_USB_FSL_USB2) += fsl_usb2_udc.o
+
+ #
+ # USB gadget drivers
+--- /dev/null
++++ b/drivers/usb/gadget/fsl_usb2_udc.c
+@@ -0,0 +1,2500 @@
++/*
++ * Copyright (C) 2004-2007 Freescale Semicondutor, Inc. All rights reserved.
++ *
++ * Author: Li Yang <leoli@freescale.com>
++ * Jiang Bo <tanya.jiang@freescale.com>
++ *
++ * Description:
++ * Freescale high-speed USB SOC DR module device controller driver.
++ * This can be found on MPC8349E/MPC8313E cpus.
++ * The driver is previously named as mpc_udc. Based on bare board
++ * code from Dave Liu and Shlomi Gridish.
++ *
++ * This program is free software; you can redistribute it and/or modify it
++ * under the terms of the GNU General Public License as published by the
++ * Free Software Foundation; either version 2 of the License, or (at your
++ * option) any later version.
++ */
++
++#undef VERBOSE
++
++#include <linux/module.h>
++#include <linux/kernel.h>
++#include <linux/ioport.h>
++#include <linux/types.h>
++#include <linux/errno.h>
++#include <linux/delay.h>
++#include <linux/sched.h>
++#include <linux/slab.h>
++#include <linux/init.h>
++#include <linux/timer.h>
++#include <linux/list.h>
++#include <linux/interrupt.h>
++#include <linux/proc_fs.h>
++#include <linux/mm.h>
++#include <linux/moduleparam.h>
++#include <linux/device.h>
++#include <linux/usb/ch9.h>
++#include <linux/usb_gadget.h>
++#include <linux/usb/otg.h>
++#include <linux/dma-mapping.h>
++#include <linux/platform_device.h>
++#include <linux/fsl_devices.h>
++#include <linux/dmapool.h>
++
++#include <asm/byteorder.h>
++#include <asm/io.h>
++#include <asm/irq.h>
++#include <asm/system.h>
++#include <asm/unaligned.h>
++#include <asm/dma.h>
++#include <asm/cacheflush.h>
++
++#include "fsl_usb2_udc.h"
++
++#define DRIVER_DESC "Freescale High-Speed USB SOC Device Controller driver"
++#define DRIVER_AUTHOR "Li Yang/Jiang Bo"
++#define DRIVER_VERSION "Apr 20, 2007"
++
++#define DMA_ADDR_INVALID (~(dma_addr_t)0)
++
++static const char driver_name[] = "fsl-usb2-udc";
++static const char driver_desc[] = DRIVER_DESC;
++
++volatile static struct usb_dr_device *dr_regs = NULL;
++volatile static struct usb_sys_interface *usb_sys_regs = NULL;
++
++/* it is initialized in probe() */
++static struct fsl_udc *udc_controller = NULL;
++
++static const struct usb_endpoint_descriptor
++fsl_ep0_desc = {
++ .bLength = USB_DT_ENDPOINT_SIZE,
++ .bDescriptorType = USB_DT_ENDPOINT,
++ .bEndpointAddress = 0,
++ .bmAttributes = USB_ENDPOINT_XFER_CONTROL,
++ .wMaxPacketSize = USB_MAX_CTRL_PAYLOAD,
++};
++
++static int fsl_udc_suspend(struct platform_device *pdev, pm_message_t state);
++static int fsl_udc_resume(struct platform_device *pdev);
++static void fsl_ep_fifo_flush(struct usb_ep *_ep);
++
++#ifdef CONFIG_PPC32
++#define fsl_readl(addr) in_le32(addr)
++#define fsl_writel(addr, val32) out_le32(val32, addr)
++#else
++#define fsl_readl(addr) readl(addr)
++#define fsl_writel(addr, val32) writel(addr, val32)
++#endif
++
++/********************************************************************
++ * Internal Used Function
++********************************************************************/
++/*-----------------------------------------------------------------
++ * done() - retire a request; caller blocked irqs
++ * @status : request status to be set, only works when
++ * request is still in progress.
++ *--------------------------------------------------------------*/
++static void done(struct fsl_ep *ep, struct fsl_req *req, int status)
++{
++ struct fsl_udc *udc = NULL;
++ unsigned char stopped = ep->stopped;
++ struct ep_td_struct *curr_td, *next_td;
++ int j;
++
++ udc = (struct fsl_udc *)ep->udc;
++ /* Removed the req from fsl_ep->queue */
++ list_del_init(&req->queue);
++
++ /* req.status should be set as -EINPROGRESS in ep_queue() */
++ if (req->req.status == -EINPROGRESS)
++ req->req.status = status;
++ else
++ status = req->req.status;
++
++ /* Free dtd for the request */
++ next_td = req->head;
++ for (j = 0; j < req->dtd_count; j++) {
++ curr_td = next_td;
++ if (j != req->dtd_count - 1) {
++ next_td = curr_td->next_td_virt;
++ }
++ dma_pool_free(udc->td_pool, curr_td, curr_td->td_dma);
++ }
++
++ if (req->mapped) {
++ dma_unmap_single(ep->udc->gadget.dev.parent,
++ req->req.dma, req->req.length,
++ ep_is_in(ep)
++ ? DMA_TO_DEVICE
++ : DMA_FROM_DEVICE);
++ req->req.dma = DMA_ADDR_INVALID;
++ req->mapped = 0;
++ } else
++ dma_sync_single_for_cpu(ep->udc->gadget.dev.parent,
++ req->req.dma, req->req.length,
++ ep_is_in(ep)
++ ? DMA_TO_DEVICE
++ : DMA_FROM_DEVICE);
++
++ if (status && (status != -ESHUTDOWN))
++ VDBG("complete %s req %p stat %d len %u/%u",
++ ep->ep.name, &req->req, status,
++ req->req.actual, req->req.length);
++
++ ep->stopped = 1;
++
++ spin_unlock(&ep->udc->lock);
++ /* complete() is from gadget layer,
++ * eg fsg->bulk_in_complete() */
++ if (req->req.complete)
++ req->req.complete(&ep->ep, &req->req);
++
++ spin_lock(&ep->udc->lock);
++ ep->stopped = stopped;
++}
++
++/*-----------------------------------------------------------------
++ * nuke(): delete all requests related to this ep
++ * called with spinlock held
++ *--------------------------------------------------------------*/
++static void nuke(struct fsl_ep *ep, int status)
++{
++ ep->stopped = 1;
++
++ /* Flush fifo */
++ fsl_ep_fifo_flush(&ep->ep);
++
++ /* Whether this eq has request linked */
++ while (!list_empty(&ep->queue)) {
++ struct fsl_req *req = NULL;
++
++ req = list_entry(ep->queue.next, struct fsl_req, queue);
++ done(ep, req, status);
++ }
++}
++
++/*------------------------------------------------------------------
++ Internal Hardware related function
++ ------------------------------------------------------------------*/
++
++static int dr_controller_setup(struct fsl_udc *udc)
++{
++ unsigned int tmp = 0, portctrl = 0, ctrl = 0;
++ unsigned long timeout;
++#define FSL_UDC_RESET_TIMEOUT 1000
++
++ /* before here, make sure dr_regs has been initialized */
++ if (!udc)
++ return -EINVAL;
++
++ /* Stop and reset the usb controller */
++ tmp = fsl_readl(&dr_regs->usbcmd);
++ tmp &= ~USB_CMD_RUN_STOP;
++ fsl_writel(tmp, &dr_regs->usbcmd);
++
++ tmp = fsl_readl(&dr_regs->usbcmd);
++ tmp |= USB_CMD_CTRL_RESET;
++ fsl_writel(tmp, &dr_regs->usbcmd);
++
++ /* Wait for reset to complete */
++ timeout = jiffies + FSL_UDC_RESET_TIMEOUT;
++ while (fsl_readl(&dr_regs->usbcmd) & USB_CMD_CTRL_RESET) {
++ if (time_after(jiffies, timeout)) {
++ ERR("udc reset timeout! \n");
++ return -ETIMEDOUT;
++ }
++ cpu_relax();
++ }
++
++ /* Set the controller as device mode */
++ tmp = fsl_readl(&dr_regs->usbmode);
++ tmp |= USB_MODE_CTRL_MODE_DEVICE;
++ /* Disable Setup Lockout */
++ tmp |= USB_MODE_SETUP_LOCK_OFF;
++ fsl_writel(tmp, &dr_regs->usbmode);
++
++ /* Clear the setup status */
++ fsl_writel(0, &dr_regs->usbsts);
++
++ tmp = udc->ep_qh_dma;
++ tmp &= USB_EP_LIST_ADDRESS_MASK;
++ fsl_writel(tmp, &dr_regs->endpointlistaddr);
++
++ VDBG("vir[qh_base] is %p phy[qh_base] is 0x%8x reg is 0x%8x",
++ (int)udc->ep_qh, (int)tmp,
++ fsl_readl(&dr_regs->endpointlistaddr));
++
++ /* Config PHY interface */
++ portctrl = fsl_readl(&dr_regs->portsc1);
++ portctrl &= ~PORTSCX_PHY_TYPE_SEL;
++ switch (udc->phy_mode) {
++ case FSL_USB2_PHY_ULPI:
++ portctrl |= PORTSCX_PTS_ULPI;
++ break;
++ case FSL_USB2_PHY_UTMI:
++ case FSL_USB2_PHY_UTMI_WIDE:
++ portctrl |= PORTSCX_PTS_UTMI;
++ break;
++ case FSL_USB2_PHY_SERIAL:
++ portctrl |= PORTSCX_PTS_FSLS;
++ break;
++ default:
++ return -EINVAL;
++ }
++ fsl_writel(portctrl, &dr_regs->portsc1);
++
++ /* Config control enable i/o output, cpu endian register */
++ ctrl = __raw_readl(&usb_sys_regs->control);
++ ctrl |= USB_CTRL_IOENB;
++ __raw_writel(ctrl, &usb_sys_regs->control);
++
++#if defined(CONFIG_PPC32) && !defined(CONFIG_NOT_COHERENT_CACHE)
++ /* Turn on cache snooping hardware, since some PowerPC platforms
++ * wholly rely on hardware to deal with cache coherent. */
++
++ /* Setup Snooping for all the 4GB space */
++ tmp = SNOOP_SIZE_2GB; /* starts from 0x0, size 2G */
++ __raw_writel(tmp, &usb_sys_regs->snoop1);
++ tmp |= 0x80000000; /* starts from 0x8000000, size 2G */
++ __raw_writel(tmp, &usb_sys_regs->snoop2);
++#endif
++
++ return 0;
++}
++
++/* Enable DR irq and set controller to run state */
++static void dr_controller_run(struct fsl_udc *udc)
++{
++ u32 temp;
++
++ /* Enable DR irq reg */
++ temp = USB_INTR_INT_EN | USB_INTR_ERR_INT_EN
++ | USB_INTR_PTC_DETECT_EN | USB_INTR_RESET_EN
++ | USB_INTR_DEVICE_SUSPEND | USB_INTR_SYS_ERR_EN;
++
++ fsl_writel(temp, &dr_regs->usbintr);
++
++ /* Clear stopped bit */
++ udc->stopped = 0;
++
++ /* Set the controller as device mode */
++ temp = fsl_readl(&dr_regs->usbmode);
++ temp |= USB_MODE_CTRL_MODE_DEVICE;
++ fsl_writel(temp, &dr_regs->usbmode);
++
++ /* Set controller to Run */
++ temp = fsl_readl(&dr_regs->usbcmd);
++ temp |= USB_CMD_RUN_STOP;
++ fsl_writel(temp, &dr_regs->usbcmd);
++
++ return;
++}
++
++static void dr_controller_stop(struct fsl_udc *udc)
++{
++ unsigned int tmp;
++
++ /* disable all INTR */
++ fsl_writel(0, &dr_regs->usbintr);
++
++ /* Set stopped bit for isr */
++ udc->stopped = 1;
++
++ /* disable IO output */
++/* usb_sys_regs->control = 0; */
++
++ /* set controller to Stop */
++ tmp = fsl_readl(&dr_regs->usbcmd);
++ tmp &= ~USB_CMD_RUN_STOP;
++ fsl_writel(tmp, &dr_regs->usbcmd);
++
++ return;
++}
++
++void dr_ep_setup(unsigned char ep_num, unsigned char dir, unsigned char ep_type)
++{
++ unsigned int tmp_epctrl = 0;
++
++ tmp_epctrl = fsl_readl(&dr_regs->endptctrl[ep_num]);
++ if (dir) {
++ if (ep_num)
++ tmp_epctrl |= EPCTRL_TX_DATA_TOGGLE_RST;
++ tmp_epctrl |= EPCTRL_TX_ENABLE;
++ tmp_epctrl |= ((unsigned int)(ep_type)
++ << EPCTRL_TX_EP_TYPE_SHIFT);
++ } else {
++ if (ep_num)
++ tmp_epctrl |= EPCTRL_RX_DATA_TOGGLE_RST;
++ tmp_epctrl |= EPCTRL_RX_ENABLE;
++ tmp_epctrl |= ((unsigned int)(ep_type)
++ << EPCTRL_RX_EP_TYPE_SHIFT);
++ }
++
++ fsl_writel(tmp_epctrl, &dr_regs->endptctrl[ep_num]);
++}
++
++static void
++dr_ep_change_stall(unsigned char ep_num, unsigned char dir, int value)
++{
++ u32 tmp_epctrl = 0;
++
++ tmp_epctrl = fsl_readl(&dr_regs->endptctrl[ep_num]);
++
++ if (value) {
++ /* set the stall bit */
++ if (dir)
++ tmp_epctrl |= EPCTRL_TX_EP_STALL;
++ else
++ tmp_epctrl |= EPCTRL_RX_EP_STALL;
++ } else {
++ /* clear the stall bit and reset data toggle */
++ if (dir) {
++ tmp_epctrl &= ~EPCTRL_TX_EP_STALL;
++ tmp_epctrl |= EPCTRL_TX_DATA_TOGGLE_RST;
++ } else {
++ tmp_epctrl &= ~EPCTRL_RX_EP_STALL;
++ tmp_epctrl |= EPCTRL_RX_DATA_TOGGLE_RST;
++ }
++ }
++ fsl_writel(tmp_epctrl, &dr_regs->endptctrl[ep_num]);
++}
++
++/* Get stall status of a specific ep
++ Return: 0: not stalled; 1:stalled */
++static int dr_ep_get_stall(unsigned char ep_num, unsigned char dir)
++{
++ u32 epctrl;
++
++ epctrl = fsl_readl(&dr_regs->endptctrl[ep_num]);
++ if (dir)
++ return (epctrl & EPCTRL_TX_EP_STALL) ? 1 : 0;
++ else
++ return (epctrl & EPCTRL_RX_EP_STALL) ? 1 : 0;
++}
++
++/********************************************************************
++ Internal Structure Build up functions
++********************************************************************/
++
++/*------------------------------------------------------------------
++* struct_ep_qh_setup(): set the Endpoint Capabilites field of QH
++ * @zlt: Zero Length Termination Select (1: disable; 0: enable)
++ * @mult: Mult field
++ ------------------------------------------------------------------*/
++static void struct_ep_qh_setup(struct fsl_udc *udc, unsigned char ep_num,
++ unsigned char dir, unsigned char ep_type,
++ unsigned int max_pkt_len,
++ unsigned int zlt, unsigned char mult)
++{
++ struct ep_queue_head *p_QH = &udc->ep_qh[2 * ep_num + dir];
++ unsigned int tmp = 0;
++
++ /* set the Endpoint Capabilites in QH */
++ switch (ep_type) {
++ case USB_ENDPOINT_XFER_CONTROL:
++ /* Interrupt On Setup (IOS). for control ep */
++ tmp = (max_pkt_len << EP_QUEUE_HEAD_MAX_PKT_LEN_POS)
++ | EP_QUEUE_HEAD_IOS;
++ break;
++ case USB_ENDPOINT_XFER_ISOC:
++ tmp = (max_pkt_len << EP_QUEUE_HEAD_MAX_PKT_LEN_POS)
++ | (mult << EP_QUEUE_HEAD_MULT_POS);
++ break;
++ case USB_ENDPOINT_XFER_BULK:
++ case USB_ENDPOINT_XFER_INT:
++ tmp = max_pkt_len << EP_QUEUE_HEAD_MAX_PKT_LEN_POS;
++ break;
++ default:
++ VDBG("error ep type is %d", ep_type);
++ return;
++ }
++ if (zlt)
++ tmp |= EP_QUEUE_HEAD_ZLT_SEL;
++ p_QH->max_pkt_length = cpu_to_le32(tmp);
++
++ return;
++}
++
++/* Setup qh structure and ep register for ep0. */
++static void ep0_setup(struct fsl_udc *udc)
++{
++ /* the intialization of an ep includes: fields in QH, Regs,
++ * fsl_ep struct */
++ struct_ep_qh_setup(udc, 0, USB_RECV, USB_ENDPOINT_XFER_CONTROL,
++ USB_MAX_CTRL_PAYLOAD, 0, 0);
++ struct_ep_qh_setup(udc, 0, USB_SEND, USB_ENDPOINT_XFER_CONTROL,
++ USB_MAX_CTRL_PAYLOAD, 0, 0);
++ dr_ep_setup(0, USB_RECV, USB_ENDPOINT_XFER_CONTROL);
++ dr_ep_setup(0, USB_SEND, USB_ENDPOINT_XFER_CONTROL);
++
++ return;
++
++}
++
++/***********************************************************************
++ Endpoint Management Functions
++***********************************************************************/
++
++/*-------------------------------------------------------------------------
++ * when configurations are set, or when interface settings change
++ * for example the do_set_interface() in gadget layer,
++ * the driver will enable or disable the relevant endpoints
++ * ep0 doesn't use this routine. It is always enabled.
++-------------------------------------------------------------------------*/
++static int fsl_ep_enable(struct usb_ep *_ep,
++ const struct usb_endpoint_descriptor *desc)
++{
++ struct fsl_udc *udc = NULL;
++ struct fsl_ep *ep = NULL;
++ unsigned short max = 0;
++ unsigned char mult = 0, zlt;
++ int retval = -EINVAL;
++ unsigned long flags = 0;
++
++ ep = container_of(_ep, struct fsl_ep, ep);
++
++ /* catch various bogus parameters */
++ if (!_ep || !desc || ep->desc
++ || (desc->bDescriptorType != USB_DT_ENDPOINT))
++ return -EINVAL;
++
++ udc = ep->udc;
++
++ if (!udc->driver || (udc->gadget.speed == USB_SPEED_UNKNOWN))
++ return -ESHUTDOWN;
++
++ max = le16_to_cpu(desc->wMaxPacketSize);
++
++ /* Disable automatic zlp generation. Driver is reponsible to indicate
++ * explicitly through req->req.zero. This is needed to enable multi-td
++ * request. */
++ zlt = 1;
++
++ /* Assume the max packet size from gadget is always correct */
++ switch (desc->bmAttributes & 0x03) {
++ case USB_ENDPOINT_XFER_CONTROL:
++ case USB_ENDPOINT_XFER_BULK:
++ case USB_ENDPOINT_XFER_INT:
++ /* mult = 0. Execute N Transactions as demonstrated by
++ * the USB variable length packet protocol where N is
++ * computed using the Maximum Packet Length (dQH) and
++ * the Total Bytes field (dTD) */
++ mult = 0;
++ break;
++ case USB_ENDPOINT_XFER_ISOC:
++ /* Calculate transactions needed for high bandwidth iso */
++ mult = (unsigned char)(1 + ((max >> 11) & 0x03));
++ max = max & 0x8ff; /* bit 0~10 */
++ /* 3 transactions at most */
++ if (mult > 3)
++ goto en_done;
++ break;
++ default:
++ goto en_done;
++ }
++
++ spin_lock_irqsave(&udc->lock, flags);
++ ep->ep.maxpacket = max;
++ ep->desc = desc;
++ ep->stopped = 0;
++
++ /* Controller related setup */
++ /* Init EPx Queue Head (Ep Capabilites field in QH
++ * according to max, zlt, mult) */
++ struct_ep_qh_setup(udc, (unsigned char) ep_index(ep),
++ (unsigned char) ((desc->bEndpointAddress & USB_DIR_IN)
++ ? USB_SEND : USB_RECV),
++ (unsigned char) (desc->bmAttributes
++ & USB_ENDPOINT_XFERTYPE_MASK),
++ max, zlt, mult);
++
++ /* Init endpoint ctrl register */
++ dr_ep_setup((unsigned char) ep_index(ep),
++ (unsigned char) ((desc->bEndpointAddress & USB_DIR_IN)
++ ? USB_SEND : USB_RECV),
++ (unsigned char) (desc->bmAttributes
++ & USB_ENDPOINT_XFERTYPE_MASK));
++
++ spin_unlock_irqrestore(&udc->lock, flags);
++ retval = 0;
++
++ VDBG("enabled %s (ep%d%s) maxpacket %d",ep->ep.name,
++ ep->desc->bEndpointAddress & 0x0f,
++ (desc->bEndpointAddress & USB_DIR_IN)
++ ? "in" : "out", max);
++en_done:
++ return retval;
++}
++
++/*---------------------------------------------------------------------
++ * @ep : the ep being unconfigured. May not be ep0
++ * Any pending and uncomplete req will complete with status (-ESHUTDOWN)
++*---------------------------------------------------------------------*/
++static int fsl_ep_disable(struct usb_ep *_ep)
++{
++ struct fsl_udc *udc = NULL;
++ struct fsl_ep *ep = NULL;
++ unsigned long flags = 0;
++ u32 epctrl;
++ int ep_num;
++
++ ep = container_of(_ep, struct fsl_ep, ep);
++ if (!_ep || !ep->desc) {
++ VDBG("%s not enabled", _ep ? ep->ep.name : NULL);
++ return -EINVAL;
++ }
++
++ /* disable ep on controller */
++ ep_num = ep_index(ep);
++ epctrl = fsl_readl(&dr_regs->endptctrl[ep_num]);
++ if (ep_is_in(ep))
++ epctrl &= ~EPCTRL_TX_ENABLE;
++ else
++ epctrl &= ~EPCTRL_RX_ENABLE;
++ fsl_writel(epctrl, &dr_regs->endptctrl[ep_num]);
++
++ udc = (struct fsl_udc *)ep->udc;
++ spin_lock_irqsave(&udc->lock, flags);
++
++ /* nuke all pending requests (does flush) */
++ nuke(ep, -ESHUTDOWN);
++
++ ep->desc = 0;
++ ep->stopped = 1;
++ spin_unlock_irqrestore(&udc->lock, flags);
++
++ VDBG("disabled %s OK", _ep->name);
++ return 0;
++}
++
++/*---------------------------------------------------------------------
++ * allocate a request object used by this endpoint
++ * the main operation is to insert the req->queue to the eq->queue
++ * Returns the request, or null if one could not be allocated
++*---------------------------------------------------------------------*/
++static struct usb_request *
++fsl_alloc_request(struct usb_ep *_ep, gfp_t gfp_flags)
++{
++ struct fsl_req *req = NULL;
++
++ req = kzalloc(sizeof *req, gfp_flags);
++ if (!req)
++ return NULL;
++
++ req->req.dma = DMA_ADDR_INVALID;
++ INIT_LIST_HEAD(&req->queue);
++
++ return &req->req;
++}
++
++static void fsl_free_request(struct usb_ep *_ep, struct usb_request *_req)
++{
++ struct fsl_req *req = NULL;
++
++ req = container_of(_req, struct fsl_req, req);
++
++ if (_req)
++ kfree(req);
++}
++
++/*------------------------------------------------------------------
++ * Allocate an I/O buffer
++*---------------------------------------------------------------------*/
++static void *fsl_alloc_buffer(struct usb_ep *_ep, unsigned bytes,
++ dma_addr_t *dma, gfp_t gfp_flags)
++{
++ struct fsl_ep *ep;
++
++ if (!_ep)
++ return NULL;
++
++ ep = container_of(_ep, struct fsl_ep, ep);
++
++ return dma_alloc_coherent(ep->udc->gadget.dev.parent,
++ bytes, dma, gfp_flags);
++}
++
++/*------------------------------------------------------------------
++ * frees an i/o buffer
++*---------------------------------------------------------------------*/
++static void fsl_free_buffer(struct usb_ep *_ep, void *buf,
++ dma_addr_t dma, unsigned bytes)
++{
++ struct fsl_ep *ep;
++
++ if (!_ep)
++ return NULL;
++
++ ep = container_of(_ep, struct fsl_ep, ep);
++
++ dma_free_coherent(ep->udc->gadget.dev.parent, bytes, buf, dma);
++}
++
++/*-------------------------------------------------------------------------*/
++static int fsl_queue_td(struct fsl_ep *ep, struct fsl_req *req)
++{
++ int i = ep_index(ep) * 2 + ep_is_in(ep);
++ u32 temp, bitmask, tmp_stat;
++ struct ep_queue_head *dQH = &ep->udc->ep_qh[i];
++
++ /* VDBG("QH addr Register 0x%8x", dr_regs->endpointlistaddr);
++ VDBG("ep_qh[%d] addr is 0x%8x", i, (u32)&(ep->udc->ep_qh[i])); */
++
++ bitmask = ep_is_in(ep)
++ ? (1 << (ep_index(ep) + 16))
++ : (1 << (ep_index(ep)));
++
++ /* check if the pipe is empty */
++ if (!(list_empty(&ep->queue))) {
++ /* Add td to the end */
++ struct fsl_req *lastreq;
++ lastreq = list_entry(ep->queue.prev, struct fsl_req, queue);
++ lastreq->tail->next_td_ptr =
++ cpu_to_le32(req->head->td_dma & DTD_ADDR_MASK);
++ /* Read prime bit, if 1 goto done */
++ if (fsl_readl(&dr_regs->endpointprime) & bitmask)
++ goto out;
++
++ do {
++ /* Set ATDTW bit in USBCMD */
++ temp = fsl_readl(&dr_regs->usbcmd);
++ fsl_writel(temp | USB_CMD_ATDTW, &dr_regs->usbcmd);
++
++ /* Read correct status bit */
++ tmp_stat = fsl_readl(&dr_regs->endptstatus) & bitmask;
++
++ } while (!(fsl_readl(&dr_regs->usbcmd) & USB_CMD_ATDTW));
++
++ /* Write ATDTW bit to 0 */
++ temp = fsl_readl(&dr_regs->usbcmd);
++ fsl_writel(temp & ~USB_CMD_ATDTW, &dr_regs->usbcmd);
++
++ if (tmp_stat)
++ goto out;
++ }
++
++ /* Write dQH next pointer and terminate bit to 0 */
++ temp = req->head->td_dma & EP_QUEUE_HEAD_NEXT_POINTER_MASK;
++ dQH->next_dtd_ptr = cpu_to_le32(temp);
++
++ /* Clear active and halt bit */
++ temp = cpu_to_le32(~(EP_QUEUE_HEAD_STATUS_ACTIVE
++ | EP_QUEUE_HEAD_STATUS_HALT));
++ dQH->size_ioc_int_sts &= temp;
++
++ /* Prime endpoint by writing 1 to ENDPTPRIME */
++ temp = ep_is_in(ep)
++ ? (1 << (ep_index(ep) + 16))
++ : (1 << (ep_index(ep)));
++ fsl_writel(temp, &dr_regs->endpointprime);
++out:
++ return 0;
++}
++
++/* Fill in the dTD structure
++ * @req: request that the transfer belongs to
++ * @length: return actually data length of the dTD
++ * @dma: return dma address of the dTD
++ * @is_last: return flag if it is the last dTD of the request
++ * return: pointer to the built dTD */
++static struct ep_td_struct *fsl_build_dtd(struct fsl_req *req, unsigned *length,
++ dma_addr_t *dma, int *is_last)
++{
++ u32 swap_temp;
++ struct ep_td_struct *dtd;
++
++ /* how big will this transfer be? */
++ *length = min(req->req.length - req->req.actual,
++ (unsigned)EP_MAX_LENGTH_TRANSFER);
++
++ dtd = dma_pool_alloc(udc_controller->td_pool, GFP_KERNEL, dma);
++ if (dtd == NULL)
++ return dtd;
++
++ dtd->td_dma = *dma;
++ /* Clear reserved field */
++ swap_temp = cpu_to_le32(dtd->size_ioc_sts);
++ swap_temp &= ~DTD_RESERVED_FIELDS;
++ dtd->size_ioc_sts = cpu_to_le32(swap_temp);
++
++ /* Init all of buffer page pointers */
++ swap_temp = (u32) (req->req.dma + req->req.actual);
++ dtd->buff_ptr0 = cpu_to_le32(swap_temp);
++ dtd->buff_ptr1 = cpu_to_le32(swap_temp + 0x1000);
++ dtd->buff_ptr2 = cpu_to_le32(swap_temp + 0x2000);
++ dtd->buff_ptr3 = cpu_to_le32(swap_temp + 0x3000);
++ dtd->buff_ptr4 = cpu_to_le32(swap_temp + 0x4000);
++
++ req->req.actual += *length;
++
++ /* zlp is needed if req->req.zero is set */
++ if (req->req.zero) {
++ if (*length == 0 || (*length % req->ep->ep.maxpacket) != 0)
++ *is_last = 1;
++ else
++ *is_last = 0;
++ } else if (req->req.length == req->req.actual)
++ *is_last = 1;
++ else
++ *is_last = 0;
++
++ if ((*is_last) == 0)
++ VDBG("multi-dtd request!\n");
++ /* Fill in the transfer size; set active bit */
++ swap_temp = ((*length << DTD_LENGTH_BIT_POS) | DTD_STATUS_ACTIVE);
++
++ /* Enable interrupt for the last dtd of a request */
++ if (*is_last && !req->req.no_interrupt)
++ swap_temp |= DTD_IOC;
++
++ dtd->size_ioc_sts = cpu_to_le32(swap_temp);
++
++ mb();
++
++ VDBG("length = %d address= 0x%x", *length, (int)*dma);
++
++ return dtd;
++}
++
++/* Generate dtd chain for a request */
++static int fsl_req_to_dtd(struct fsl_req *req)
++{
++ unsigned count;
++ int is_last;
++ int is_first =1;
++ struct ep_td_struct *last_dtd = NULL, *dtd;
++ dma_addr_t dma;
++
++ do {
++ dtd = fsl_build_dtd(req, &count, &dma, &is_last);
++ if (dtd == NULL)
++ return -ENOMEM;
++
++ if (is_first) {
++ is_first = 0;
++ req->head = dtd;
++ } else {
++ last_dtd->next_td_ptr = cpu_to_le32(dma);
++ last_dtd->next_td_virt = dtd;
++ }
++ last_dtd = dtd;
++
++ req->dtd_count++;
++ } while (!is_last);
++
++ dtd->next_td_ptr = cpu_to_le32(DTD_NEXT_TERMINATE);
++
++ req->tail = dtd;
++
++ return 0;
++}
++
++/* queues (submits) an I/O request to an endpoint */
++static int
++fsl_ep_queue(struct usb_ep *_ep, struct usb_request *_req, gfp_t gfp_flags)
++{
++ struct fsl_ep *ep = container_of(_ep, struct fsl_ep, ep);
++ struct fsl_req *req = container_of(_req, struct fsl_req, req);
++ struct fsl_udc *udc;
++ unsigned long flags;
++ int is_iso = 0;
++
++ /* catch various bogus parameters */
++ if (!_req || !req->req.complete || !req->req.buf
++ || !list_empty(&req->queue)) {
++ VDBG("%s, bad params\n", __FUNCTION__);
++ return -EINVAL;
++ }
++ if (!_ep || (!ep->desc && ep_index(ep))) {
++ VDBG("%s, bad ep\n", __FUNCTION__);
++ return -EINVAL;
++ }
++ if (ep->desc->bmAttributes == USB_ENDPOINT_XFER_ISOC) {
++ if (req->req.length > ep->ep.maxpacket)
++ return -EMSGSIZE;
++ is_iso = 1;
++ }
++
++ udc = ep->udc;
++ if (!udc->driver || udc->gadget.speed == USB_SPEED_UNKNOWN)
++ return -ESHUTDOWN;
++
++ req->ep = ep;
++
++ /* map virtual address to hardware */
++ if (req->req.dma == DMA_ADDR_INVALID) {
++ req->req.dma = dma_map_single(ep->udc->gadget.dev.parent,
++ req->req.buf,
++ req->req.length, ep_is_in(ep)
++ ? DMA_TO_DEVICE
++ : DMA_FROM_DEVICE);
++ req->mapped = 1;
++ } else {
++ dma_sync_single_for_device(ep->udc->gadget.dev.parent,
++ req->req.dma, req->req.length,
++ ep_is_in(ep)
++ ? DMA_TO_DEVICE
++ : DMA_FROM_DEVICE);
++ req->mapped = 0;
++ }
++
++ req->req.status = -EINPROGRESS;
++ req->req.actual = 0;
++ req->dtd_count = 0;
++
++ spin_lock_irqsave(&udc->lock, flags);
++
++ /* build dtds and push them to device queue */
++ if (!fsl_req_to_dtd(req)) {
++ fsl_queue_td(ep, req);
++ } else {
++ spin_unlock_irqrestore(&udc->lock, flags);
++ return -ENOMEM;
++ }
++
++ /* Update ep0 state */
++ if ((ep_index(ep) == 0))
++ udc->ep0_state = DATA_STATE_XMIT;
++
++ /* irq handler advances the queue */
++ if (req != NULL)
++ list_add_tail(&req->queue, &ep->queue);
++ spin_unlock_irqrestore(&udc->lock, flags);
++
++ return 0;
++}
++
++/* dequeues (cancels, unlinks) an I/O request from an endpoint */
++static int fsl_ep_dequeue(struct usb_ep *_ep, struct usb_request *_req)
++{
++ struct fsl_ep *ep = container_of(_ep, struct fsl_ep, ep);
++ struct fsl_req *req;
++ unsigned long flags;
++ int ep_num, stopped, ret = 0;
++ u32 epctrl;
++
++ if (!_ep || !_req)
++ return -EINVAL;
++
++ spin_lock_irqsave(&ep->udc->lock, flags);
++ stopped = ep->stopped;
++
++ /* Stop the ep before we deal with the queue */
++ ep->stopped = 1;
++ ep_num = ep_index(ep);
++ epctrl = fsl_readl(&dr_regs->endptctrl[ep_num]);
++ if (ep_is_in(ep))
++ epctrl &= ~EPCTRL_TX_ENABLE;
++ else
++ epctrl &= ~EPCTRL_RX_ENABLE;
++ fsl_writel(epctrl, &dr_regs->endptctrl[ep_num]);
++
++ /* make sure it's actually queued on this endpoint */
++ list_for_each_entry(req, &ep->queue, queue) {
++ if (&req->req == _req)
++ break;
++ }
++ if (&req->req != _req) {
++ ret = -EINVAL;
++ goto out;
++ }
++
++ /* The request is in progress, or completed but not dequeued */
++ if (ep->queue.next == &req->queue) {
++ _req->status = -ECONNRESET;
++ fsl_ep_fifo_flush(_ep); /* flush current transfer */
++
++ /* The request isn't the last request in this ep queue */
++ if (req->queue.next != &ep->queue) {
++ struct ep_queue_head *qh;
++ struct fsl_req *next_req;
++
++ qh = ep->qh;
++ next_req = list_entry(req->queue.next, struct fsl_req,
++ queue);
++
++ /* Point the QH to the first TD of next request */
++ fsl_writel((u32) next_req->head, &qh->curr_dtd_ptr);
++ }
++
++ /* The request hasn't been processed, patch up the TD chain */
++ } else {
++ struct fsl_req *prev_req;
++
++ prev_req = list_entry(req->queue.prev, struct fsl_req, queue);
++ fsl_writel(fsl_readl(&req->tail->next_td_ptr),
++ &prev_req->tail->next_td_ptr);
++
++ }
++
++ done(ep, req, -ECONNRESET);
++
++ /* Enable EP */
++out: epctrl = fsl_readl(&dr_regs->endptctrl[ep_num]);
++ if (ep_is_in(ep))
++ epctrl |= EPCTRL_TX_ENABLE;
++ else
++ epctrl |= EPCTRL_RX_ENABLE;
++ fsl_writel(epctrl, &dr_regs->endptctrl[ep_num]);
++ ep->stopped = stopped;
++
++ spin_unlock_irqrestore(&ep->udc->lock, flags);
++ return ret;
++}
++
++/*-------------------------------------------------------------------------*/
++
++/*-----------------------------------------------------------------
++ * modify the endpoint halt feature
++ * @ep: the non-isochronous endpoint being stalled
++ * @value: 1--set halt 0--clear halt
++ * Returns zero, or a negative error code.
++*----------------------------------------------------------------*/
++static int fsl_ep_set_halt(struct usb_ep *_ep, int value)
++{
++ struct fsl_ep *ep = NULL;
++ unsigned long flags = 0;
++ int status = -EOPNOTSUPP; /* operation not supported */
++ unsigned char ep_dir = 0, ep_num = 0;
++ struct fsl_udc *udc = NULL;
++
++ ep = container_of(_ep, struct fsl_ep, ep);
++ udc = ep->udc;
++ if (!_ep || !ep->desc) {
++ status = -EINVAL;
++ goto out;
++ }
++
++ if (ep->desc->bmAttributes == USB_ENDPOINT_XFER_ISOC) {
++ status = -EOPNOTSUPP;
++ goto out;
++ }
++
++ /* Attempt to halt IN ep will fail if any transfer requests
++ * are still queue */
++ if (value && ep_is_in(ep) && !list_empty(&ep->queue)) {
++ status = -EAGAIN;
++ goto out;
++ }
++
++ status = 0;
++ ep_dir = ep_is_in(ep) ? USB_SEND : USB_RECV;
++ ep_num = (unsigned char)(ep_index(ep));
++ spin_lock_irqsave(&ep->udc->lock, flags);
++ dr_ep_change_stall(ep_num, ep_dir, value);
++ spin_unlock_irqrestore(&ep->udc->lock, flags);
++
++ if (ep_index(ep) == 0) {
++ udc->ep0_state = WAIT_FOR_SETUP;
++ udc->ep0_dir = 0;
++ }
++out:
++ VDBG(" %s %s halt stat %d", ep->ep.name,
++ value ? "set" : "clear", status);
++
++ return status;
++}
++
++static void fsl_ep_fifo_flush(struct usb_ep *_ep)
++{
++ struct fsl_ep *ep;
++ int ep_num, ep_dir;
++ u32 bits;
++ unsigned long timeout;
++#define FSL_UDC_FLUSH_TIMEOUT 1000
++
++ if (!_ep) {
++ return;
++ } else {
++ ep = container_of(_ep, struct fsl_ep, ep);
++ if (!ep->desc)
++ return;
++ }
++ ep_num = ep_index(ep);
++ ep_dir = ep_is_in(ep) ? USB_SEND : USB_RECV;
++
++ if (ep_num == 0)
++ bits = (1 << 16) | 1;
++ else if (ep_dir == USB_SEND)
++ bits = 1 << (16 + ep_num);
++ else
++ bits = 1 << ep_num;
++
++ timeout = jiffies + FSL_UDC_FLUSH_TIMEOUT;
++ do {
++ fsl_writel(bits, &dr_regs->endptflush);
++
++ /* Wait until flush complete */
++ while (fsl_readl(&dr_regs->endptflush)) {
++ if (time_after(jiffies, timeout)) {
++ ERR("ep flush timeout\n");
++ return;
++ }
++ cpu_relax();
++ }
++ /* See if we need to flush again */
++ } while (fsl_readl(&dr_regs->endptstatus) & bits);
++}
++
++static struct usb_ep_ops fsl_ep_ops = {
++ .enable = fsl_ep_enable,
++ .disable = fsl_ep_disable,
++
++ .alloc_request = fsl_alloc_request,
++ .free_request = fsl_free_request,
++
++ .alloc_buffer = fsl_alloc_buffer,
++ .free_buffer = fsl_free_buffer,
++
++ .queue = fsl_ep_queue,
++ .dequeue = fsl_ep_dequeue,
++
++ .set_halt = fsl_ep_set_halt,
++ .fifo_flush = fsl_ep_fifo_flush, /* flush fifo */
++};
++
++/*-------------------------------------------------------------------------
++ Gadget Driver Layer Operations
++-------------------------------------------------------------------------*/
++
++/*----------------------------------------------------------------------
++ * Get the current frame number (from DR frame_index Reg )
++ *----------------------------------------------------------------------*/
++static int fsl_get_frame(struct usb_gadget *gadget)
++{
++ return (int)(fsl_readl(&dr_regs->frindex) & USB_FRINDEX_MASKS);
++}
++
++/*-----------------------------------------------------------------------
++ * Tries to wake up the host connected to this gadget
++ -----------------------------------------------------------------------*/
++static int fsl_wakeup(struct usb_gadget *gadget)
++{
++ struct fsl_udc *udc = container_of(gadget, struct fsl_udc, gadget);
++ u32 portsc;
++
++ /* Remote wakeup feature not enabled by host */
++ if (!udc->remote_wakeup)
++ return -ENOTSUPP;
++
++ portsc = fsl_readl(&dr_regs->portsc1);
++ /* not suspended? */
++ if (!(portsc & PORTSCX_PORT_SUSPEND))
++ return 0;
++ /* trigger force resume */
++ portsc |= PORTSCX_PORT_FORCE_RESUME;
++ fsl_writel(portsc, &dr_regs->portsc1);
++ return 0;
++}
++
++static int can_pullup(struct fsl_udc *udc)
++{
++ return udc->driver && udc->softconnect && udc->vbus_active;
++}
++
++/* Notify controller that VBUS is powered, Called by whatever
++ detects VBUS sessions */
++static int fsl_vbus_session(struct usb_gadget *gadget, int is_active)
++{
++ struct fsl_udc *udc;
++ unsigned long flags;
++
++ udc = container_of(gadget, struct fsl_udc, gadget);
++ spin_lock_irqsave(&udc->lock, flags);
++ VDBG("VBUS %s\n", is_active ? "on" : "off");
++ udc->vbus_active = (is_active != 0);
++ if (can_pullup(udc))
++ fsl_writel((fsl_readl(&dr_regs->usbcmd) | USB_CMD_RUN_STOP),
++ &dr_regs->usbcmd);
++ else
++ fsl_writel((fsl_readl(&dr_regs->usbcmd) & ~USB_CMD_RUN_STOP),
++ &dr_regs->usbcmd);
++ spin_unlock_irqrestore(&udc->lock, flags);
++ return 0;
++}
++
++/* constrain controller's VBUS power usage
++ * This call is used by gadget drivers during SET_CONFIGURATION calls,
++ * reporting how much power the device may consume. For example, this
++ * could affect how quickly batteries are recharged.
++ *
++ * Returns zero on success, else negative errno.
++ */
++static int fsl_vbus_draw(struct usb_gadget *gadget, unsigned mA)
++{
++#ifdef CONFIG_USB_OTG
++ struct fsl_udc *udc;
++
++ udc = container_of(gadget, struct fsl_udc, gadget);
++
++ if (udc->transceiver)
++ return otg_set_power(udc->transceiver, mA);
++#endif
++ return -ENOTSUPP;
++}
++
++/* Change Data+ pullup status
++ * this func is used by usb_gadget_connect/disconnet
++ */
++static int fsl_pullup(struct usb_gadget *gadget, int is_on)
++{
++ struct fsl_udc *udc;
++
++ udc = container_of(gadget, struct fsl_udc, gadget);
++ udc->softconnect = (is_on != 0);
++ if (can_pullup(udc))
++ fsl_writel((fsl_readl(&dr_regs->usbcmd) | USB_CMD_RUN_STOP),
++ &dr_regs->usbcmd);
++ else
++ fsl_writel((fsl_readl(&dr_regs->usbcmd) & ~USB_CMD_RUN_STOP),
++ &dr_regs->usbcmd);
++
++ return 0;
++}
++
++/* defined in usb_gadget.h */
++static struct usb_gadget_ops fsl_gadget_ops = {
++ .get_frame = fsl_get_frame,
++ .wakeup = fsl_wakeup,
++/* .set_selfpowered = fsl_set_selfpowered, */ /* Always selfpowered */
++ .vbus_session = fsl_vbus_session,
++ .vbus_draw = fsl_vbus_draw,
++ .pullup = fsl_pullup,
++};
++
++/* Set protocol stall on ep0, protocol stall will automatically be cleared
++ on new transaction */
++static void ep0stall(struct fsl_udc *udc)
++{
++ u32 tmp;
++
++ /* must set tx and rx to stall at the same time */
++ tmp = fsl_readl(&dr_regs->endptctrl[0]);
++ tmp |= EPCTRL_TX_EP_STALL | EPCTRL_RX_EP_STALL;
++ fsl_writel(tmp, &dr_regs->endptctrl[0]);
++ udc->ep0_state = WAIT_FOR_SETUP;
++ udc->ep0_dir = 0;
++}
++
++/* Prime a status phase for ep0 */
++static int ep0_prime_status(struct fsl_udc *udc, int direction)
++{
++ struct fsl_req *req = udc->status_req;
++ struct fsl_ep *ep;
++ int status = 0;
++
++ if (direction == EP_DIR_IN)
++ udc->ep0_dir = USB_DIR_IN;
++ else
++ udc->ep0_dir = USB_DIR_OUT;
++
++ ep = &udc->eps[0];
++ udc->ep0_state = WAIT_FOR_OUT_STATUS;
++
++ req->ep = ep;
++ req->req.length = 0;
++ req->req.status = -EINPROGRESS;
++ req->req.actual = 0;
++ req->req.complete = NULL;
++ req->dtd_count = 0;
++
++ if (fsl_req_to_dtd(req) == 0)
++ status = fsl_queue_td(ep, req);
++ else
++ return -ENOMEM;
++
++ if (status)
++ ERR("Can't queue ep0 status request \n");
++ list_add_tail(&req->queue, &ep->queue);
++
++ return status;
++}
++
++static inline int udc_reset_ep_queue(struct fsl_udc *udc, u8 pipe)
++{
++ struct fsl_ep *ep = get_ep_by_pipe(udc, pipe);
++
++ if (!ep->name)
++ return 0;
++
++ nuke(ep, -ESHUTDOWN);
++
++ return 0;
++}
++
++/*
++ * ch9 Set address
++ */
++static void ch9setaddress(struct fsl_udc *udc, u16 value, u16 index, u16 length)
++{
++ /* Save the new address to device struct */
++ udc->device_address = (u8) value;
++ /* Update usb state */
++ udc->usb_state = USB_STATE_ADDRESS;
++ /* Status phase */
++ if (ep0_prime_status(udc, EP_DIR_IN))
++ ep0stall(udc);
++}
++
++/*
++ * ch9 Get status
++ */
++static void ch9getstatus(struct fsl_udc *udc, u8 request_type, u16 value,
++ u16 index, u16 length)
++{
++ u16 tmp = 0; /* Status, cpu endian */
++
++ struct fsl_req *req;
++ struct fsl_ep *ep;
++ int status = 0;
++
++ ep = &udc->eps[0];
++
++ if ((request_type & USB_RECIP_MASK) == USB_RECIP_DEVICE) {
++ /* Get device status */
++ tmp = 1 << USB_DEVICE_SELF_POWERED;
++ tmp |= udc->remote_wakeup << USB_DEVICE_REMOTE_WAKEUP;
++ } else if ((request_type & USB_RECIP_MASK) == USB_RECIP_INTERFACE) {
++ /* Get interface status */
++ /* We don't have interface information in udc driver */
++ tmp = 0;
++ } else if ((request_type & USB_RECIP_MASK) == USB_RECIP_ENDPOINT) {
++ /* Get endpoint status */
++ struct fsl_ep *target_ep;
++
++ target_ep = get_ep_by_pipe(udc, get_pipe_by_windex(index));
++
++ /* stall if endpoint doesn't exist */
++ if (!target_ep->desc)
++ goto stall;
++ tmp = dr_ep_get_stall(ep_index(target_ep), ep_is_in(target_ep))
++ << USB_ENDPOINT_HALT;
++ }
++
++ udc->ep0_dir = USB_DIR_IN;
++ /* Borrow the per device status_req */
++ req = udc->status_req;
++ /* Fill in the reqest structure */
++ *((u16 *) req->req.buf) = cpu_to_le16(tmp);
++ req->ep = ep;
++ req->req.length = 2;
++ req->req.status = -EINPROGRESS;
++ req->req.actual = 0;
++ req->req.complete = NULL;
++ req->dtd_count = 0;
++
++ /* prime the data phase */
++ if ((fsl_req_to_dtd(req) == 0))
++ status = fsl_queue_td(ep, req);
++ else /* no mem */
++ goto stall;
++
++ if (status) {
++ ERR("Can't respond to getstatus request \n");
++ goto stall;
++ }
++ list_add_tail(&req->queue, &ep->queue);
++ udc->ep0_state = DATA_STATE_XMIT;
++ return;
++stall:
++ ep0stall(udc);
++}
++
++static void setup_received_irq(struct fsl_udc *udc,
++ struct usb_ctrlrequest *setup)
++{
++ u16 wValue = le16_to_cpu(setup->wValue);
++ u16 wIndex = le16_to_cpu(setup->wIndex);
++ u16 wLength = le16_to_cpu(setup->wLength);
++
++ udc_reset_ep_queue(udc, 0);
++
++ switch (setup->bRequest) {
++ /* Request that need Data+Status phase from udc */
++ case USB_REQ_GET_STATUS:
++ if ((setup->bRequestType & (USB_DIR_IN | USB_TYPE_STANDARD))
++ != (USB_DIR_IN | USB_TYPE_STANDARD))
++ break;
++ ch9getstatus(udc, setup->bRequestType, wValue, wIndex, wLength);
++ break;
++
++ /* Requests that need Status phase from udc */
++ case USB_REQ_SET_ADDRESS:
++ if (setup->bRequestType != (USB_DIR_OUT | USB_TYPE_STANDARD
++ | USB_RECIP_DEVICE))
++ break;
++ ch9setaddress(udc, wValue, wIndex, wLength);
++ break;
++
++ /* Handled by udc, no data, status by udc */
++ case USB_REQ_CLEAR_FEATURE:
++ case USB_REQ_SET_FEATURE:
++ { /* status transaction */
++ int rc = -EOPNOTSUPP;
++
++ if ((setup->bRequestType & USB_RECIP_MASK)
++ == USB_RECIP_ENDPOINT) {
++ int pipe = get_pipe_by_windex(wIndex);
++ struct fsl_ep *ep;
++
++ if (wValue != 0 || wLength != 0 || pipe > udc->max_ep)
++ break;
++ ep = get_ep_by_pipe(udc, pipe);
++
++ spin_unlock(&udc->lock);
++ rc = fsl_ep_set_halt(&ep->ep,
++ (setup->bRequest == USB_REQ_SET_FEATURE)
++ ? 1 : 0);
++ spin_lock(&udc->lock);
++
++ } else if ((setup->bRequestType & USB_RECIP_MASK)
++ == USB_RECIP_DEVICE) {
++ /* Note: The driver has not include OTG support yet.
++ * This will be set when OTG support is added */
++ if (!udc->gadget.is_otg)
++ break;
++ else if (setup->bRequest == USB_DEVICE_B_HNP_ENABLE)
++ udc->gadget.b_hnp_enable = 1;
++ else if (setup->bRequest == USB_DEVICE_A_HNP_SUPPORT)
++ udc->gadget.a_hnp_support = 1;
++ else if (setup->bRequest ==
++ USB_DEVICE_A_ALT_HNP_SUPPORT)
++ udc->gadget.a_alt_hnp_support = 1;
++ rc = 0;
++ }
++ if (rc == 0) {
++ if (ep0_prime_status(udc, EP_DIR_IN))
++ ep0stall(udc);
++ }
++ break;
++ }
++ /* Requests handled by gadget */
++ default:
++ if (wLength) {
++ /* Data phase from gadget, status phase from udc */
++ udc->ep0_dir = (setup->bRequestType & USB_DIR_IN)
++ ? USB_DIR_IN : USB_DIR_OUT;
++ spin_unlock(&udc->lock);
++ if (udc->driver->setup(&udc->gadget,
++ &udc->local_setup_buff) < 0)
++ ep0stall(udc);
++ spin_lock(&udc->lock);
++ udc->ep0_state = (setup->bRequestType & USB_DIR_IN)
++ ? DATA_STATE_XMIT : DATA_STATE_RECV;
++
++ } else {
++ /* No data phase, IN status from gadget */
++ udc->ep0_dir = USB_DIR_IN;
++ spin_unlock(&udc->lock);
++ if (udc->driver->setup(&udc->gadget,
++ &udc->local_setup_buff) < 0)
++ ep0stall(udc);
++ spin_lock(&udc->lock);
++ udc->ep0_state = WAIT_FOR_OUT_STATUS;
++ }
++ break;
++ }
++}
++
++/* Process request for Data or Status phase of ep0
++ * prime status phase if needed */
++static void ep0_req_complete(struct fsl_udc *udc, struct fsl_ep *ep0,
++ struct fsl_req *req)
++{
++ if (udc->usb_state == USB_STATE_ADDRESS) {
++ /* Set the new address */
++ u32 new_address = (u32) udc->device_address;
++ fsl_writel(new_address << USB_DEVICE_ADDRESS_BIT_POS,
++ &dr_regs->deviceaddr);
++ }
++
++ done(ep0, req, 0);
++
++ switch (udc->ep0_state) {
++ case DATA_STATE_XMIT:
++ /* receive status phase */
++ if (ep0_prime_status(udc, EP_DIR_OUT))
++ ep0stall(udc);
++ break;
++ case DATA_STATE_RECV:
++ /* send status phase */
++ if (ep0_prime_status(udc, EP_DIR_IN))
++ ep0stall(udc);
++ break;
++ case WAIT_FOR_OUT_STATUS:
++ udc->ep0_state = WAIT_FOR_SETUP;
++ break;
++ case WAIT_FOR_SETUP:
++ ERR("Unexpect ep0 packets \n");
++ break;
++ default:
++ ep0stall(udc);
++ break;
++ }
++}
++
++/* Tripwire mechanism to ensure a setup packet payload is extracted without
++ * being corrupted by another incoming setup packet */
++static void tripwire_handler(struct fsl_udc *udc, u8 ep_num, u8 *buffer_ptr)
++{
++ u32 temp;
++ struct ep_queue_head *qh;
++
++ qh = &udc->ep_qh[ep_num * 2 + EP_DIR_OUT];
++
++ /* Clear bit in ENDPTSETUPSTAT */
++ temp = fsl_readl(&dr_regs->endptsetupstat);
++ fsl_writel(temp | (1 << ep_num), &dr_regs->endptsetupstat);
++
++ /* while a hazard exists when setup package arrives */
++ do {
++ /* Set Setup Tripwire */
++ temp = fsl_readl(&dr_regs->usbcmd);
++ fsl_writel(temp | USB_CMD_SUTW, &dr_regs->usbcmd);
++
++ /* Copy the setup packet to local buffer */
++ memcpy(buffer_ptr, (u8 *) qh->setup_buffer, 8);
++ } while (!(fsl_readl(&dr_regs->usbcmd) & USB_CMD_SUTW));
++
++ /* Clear Setup Tripwire */
++ temp = fsl_readl(&dr_regs->usbcmd);
++ fsl_writel(temp & ~USB_CMD_SUTW, &dr_regs->usbcmd);
++}
++
++/* process-ep_req(): free the completed Tds for this req */
++static int process_ep_req(struct fsl_udc *udc, int pipe,
++ struct fsl_req *curr_req)
++{
++ struct ep_td_struct *curr_td;
++ int td_complete, actual, remaining_length, j, tmp;
++ int status = 0;
++ int errors = 0;
++ struct ep_queue_head *curr_qh = &udc->ep_qh[pipe];
++ int direction = pipe % 2;
++
++ curr_td = curr_req->head;
++ td_complete = 0;
++ actual = curr_req->req.length;
++
++ for (j = 0; j < curr_req->dtd_count; j++) {
++ remaining_length = (le32_to_cpu(curr_td->size_ioc_sts)
++ & DTD_PACKET_SIZE)
++ >> DTD_LENGTH_BIT_POS;
++ actual -= remaining_length;
++
++ if ((errors = le32_to_cpu(curr_td->size_ioc_sts) &
++ DTD_ERROR_MASK)) {
++ if (errors & DTD_STATUS_HALTED) {
++ ERR("dTD error %08x QH=%d\n", errors, pipe);
++ /* Clear the errors and Halt condition */
++ tmp = le32_to_cpu(curr_qh->size_ioc_int_sts);
++ tmp &= ~errors;
++ curr_qh->size_ioc_int_sts = cpu_to_le32(tmp);
++ status = -EPIPE;
++ /* FIXME: continue with next queued TD? */
++
++ break;
++ }
++ if (errors & DTD_STATUS_DATA_BUFF_ERR) {
++ VDBG("Transfer overflow");
++ status = -EPROTO;
++ break;
++ } else if (errors & DTD_STATUS_TRANSACTION_ERR) {
++ VDBG("ISO error");
++ status = -EILSEQ;
++ break;
++ } else
++ ERR("Unknown error has occured (0x%x)!\r\n",
++ errors);
++
++ } else if (le32_to_cpu(curr_td->size_ioc_sts)
++ & DTD_STATUS_ACTIVE) {
++ VDBG("Request not complete");
++ status = REQ_UNCOMPLETE;
++ return status;
++ } else if (remaining_length) {
++ if (direction) {
++ VDBG("Transmit dTD remaining length not zero");
++ status = -EPROTO;
++ break;
++ } else {
++ td_complete++;
++ break;
++ }
++ } else {
++ td_complete++;
++ VDBG("dTD transmitted successful ");
++ }
++
++ if (j != curr_req->dtd_count - 1)
++ curr_td = (struct ep_td_struct *)curr_td->next_td_virt;
++ }
++
++ if (status)
++ return status;
++
++ curr_req->req.actual = actual;
++
++ return 0;
++}
++
++/* Process a DTD completion interrupt */
++static void dtd_complete_irq(struct fsl_udc *udc)
++{
++ u32 bit_pos;
++ int i, ep_num, direction, bit_mask, status;
++ struct fsl_ep *curr_ep;
++ struct fsl_req *curr_req, *temp_req;
++
++ /* Clear the bits in the register */
++ bit_pos = fsl_readl(&dr_regs->endptcomplete);
++ fsl_writel(bit_pos, &dr_regs->endptcomplete);
++
++ if (!bit_pos)
++ return;
++
++ for (i = 0; i < udc->max_ep * 2; i++) {
++ ep_num = i >> 1;
++ direction = i % 2;
++
++ bit_mask = 1 << (ep_num + 16 * direction);
++
++ if (!(bit_pos & bit_mask))
++ continue;
++
++ curr_ep = get_ep_by_pipe(udc, i);
++
++ /* If the ep is configured */
++ if (curr_ep->name == NULL) {
++ WARN("Invalid EP?");
++ continue;
++ }
++
++ /* process the req queue until an uncomplete request */
++ list_for_each_entry_safe(curr_req, temp_req, &curr_ep->queue,
++ queue) {
++ status = process_ep_req(udc, i, curr_req);
++
++ VDBG("status of process_ep_req= %d, ep = %d",
++ status, ep_num);
++ if (status == REQ_UNCOMPLETE)
++ break;
++ /* write back status to req */
++ curr_req->req.status = status;
++
++ if (ep_num == 0) {
++ ep0_req_complete(udc, curr_ep, curr_req);
++ break;
++ } else
++ done(curr_ep, curr_req, status);
++ }
++ }
++}
++
++/* Process a port change interrupt */
++static void port_change_irq(struct fsl_udc *udc)
++{
++ u32 speed;
++
++ if (udc->bus_reset)
++ udc->bus_reset = 0;
++
++ /* Bus resetting is finished */
++ if (!(fsl_readl(&dr_regs->portsc1) & PORTSCX_PORT_RESET)) {
++ /* Get the speed */
++ speed = (fsl_readl(&dr_regs->portsc1)
++ & PORTSCX_PORT_SPEED_MASK);
++ switch (speed) {
++ case PORTSCX_PORT_SPEED_HIGH:
++ udc->gadget.speed = USB_SPEED_HIGH;
++ break;
++ case PORTSCX_PORT_SPEED_FULL:
++ udc->gadget.speed = USB_SPEED_FULL;
++ break;
++ case PORTSCX_PORT_SPEED_LOW:
++ udc->gadget.speed = USB_SPEED_LOW;
++ break;
++ default:
++ udc->gadget.speed = USB_SPEED_UNKNOWN;
++ break;
++ }
++ }
++
++ /* Update USB state */
++ if (!udc->resume_state)
++ udc->usb_state = USB_STATE_DEFAULT;
++}
++
++/* Process suspend interrupt */
++static void suspend_irq(struct fsl_udc *udc)
++{
++ udc->resume_state = udc->usb_state;
++ udc->usb_state = USB_STATE_SUSPENDED;
++
++ /* report suspend to the driver, serial.c does not support this */
++ if (udc->driver->suspend)
++ udc->driver->suspend(&udc->gadget);
++}
++
++static void bus_resume(struct fsl_udc *udc)
++{
++ udc->usb_state = udc->resume_state;
++ udc->resume_state = 0;
++
++ /* report resume to the driver, serial.c does not support this */
++ if (udc->driver->resume)
++ udc->driver->resume(&udc->gadget);
++}
++
++/* Clear up all ep queues */
++static int reset_queues(struct fsl_udc *udc)
++{
++ u8 pipe;
++
++ for (pipe = 0; pipe < udc->max_pipes; pipe++)
++ udc_reset_ep_queue(udc, pipe);
++
++ /* report disconnect; the driver is already quiesced */
++ udc->driver->disconnect(&udc->gadget);
++
++ return 0;
++}
++
++/* Process reset interrupt */
++static void reset_irq(struct fsl_udc *udc)
++{
++ u32 temp;
++ unsigned long timeout;
++
++ /* Clear the device address */
++ temp = fsl_readl(&dr_regs->deviceaddr);
++ fsl_writel(temp & ~USB_DEVICE_ADDRESS_MASK, &dr_regs->deviceaddr);
++
++ udc->device_address = 0;
++
++ /* Clear usb state */
++ udc->resume_state = 0;
++ udc->ep0_dir = 0;
++ udc->ep0_state = WAIT_FOR_SETUP;
++ udc->remote_wakeup = 0; /* default to 0 on reset */
++ udc->gadget.b_hnp_enable = 0;
++ udc->gadget.a_hnp_support = 0;
++ udc->gadget.a_alt_hnp_support = 0;
++
++ /* Clear all the setup token semaphores */
++ temp = fsl_readl(&dr_regs->endptsetupstat);
++ fsl_writel(temp, &dr_regs->endptsetupstat);
++
++ /* Clear all the endpoint complete status bits */
++ temp = fsl_readl(&dr_regs->endptcomplete);
++ fsl_writel(temp, &dr_regs->endptcomplete);
++
++ timeout = jiffies + 100;
++ while (fsl_readl(&dr_regs->endpointprime)) {
++ /* Wait until all endptprime bits cleared */
++ if (time_after(jiffies, timeout)) {
++ ERR("Timeout for reset\n");
++ break;
++ }
++ cpu_relax();
++ }
++
++ /* Write 1s to the flush register */
++ fsl_writel(0xffffffff, &dr_regs->endptflush);
++
++ if (fsl_readl(&dr_regs->portsc1) & PORTSCX_PORT_RESET) {
++ VDBG("Bus reset");
++ /* Bus is reseting */
++ udc->bus_reset = 1;
++ /* Reset all the queues, include XD, dTD, EP queue
++ * head and TR Queue */
++ reset_queues(udc);
++ udc->usb_state = USB_STATE_DEFAULT;
++ } else {
++ VDBG("Controller reset");
++ /* initialize usb hw reg except for regs for EP, not
++ * touch usbintr reg */
++ dr_controller_setup(udc);
++
++ /* Reset all internal used Queues */
++ reset_queues(udc);
++
++ ep0_setup(udc);
++
++ /* Enable DR IRQ reg, Set Run bit, change udc state */
++ dr_controller_run(udc);
++ udc->usb_state = USB_STATE_ATTACHED;
++ }
++}
++
++/*
++ * USB device controller interrupt handler
++ */
++static irqreturn_t fsl_udc_irq(int irq, void *_udc)
++{
++ struct fsl_udc *udc = _udc;
++ u32 irq_src;
++ irqreturn_t status = IRQ_NONE;
++ unsigned long flags;
++
++ /* Disable ISR for OTG host mode */
++ if (udc->stopped)
++ return IRQ_NONE;
++ spin_lock_irqsave(&udc->lock, flags);
++ irq_src = fsl_readl(&dr_regs->usbsts) & fsl_readl(&dr_regs->usbintr);
++ /* Clear notification bits */
++ fsl_writel(irq_src, &dr_regs->usbsts);
++
++ /* VDBG("irq_src [0x%8x]", irq_src); */
++
++ /* Need to resume? */
++ if (udc->usb_state == USB_STATE_SUSPENDED)
++ if ((fsl_readl(&dr_regs->portsc1) & PORTSCX_PORT_SUSPEND) == 0)
++ bus_resume(udc);
++
++ /* USB Interrupt */
++ if (irq_src & USB_STS_INT) {
++ VDBG("Packet int");
++ /* Setup package, we only support ep0 as control ep */
++ if (fsl_readl(&dr_regs->endptsetupstat) & EP_SETUP_STATUS_EP0) {
++ tripwire_handler(udc, 0,
++ (u8 *) (&udc->local_setup_buff));
++ setup_received_irq(udc, &udc->local_setup_buff);
++ status = IRQ_HANDLED;
++ }
++
++ /* completion of dtd */
++ if (fsl_readl(&dr_regs->endptcomplete)) {
++ dtd_complete_irq(udc);
++ status = IRQ_HANDLED;
++ }
++ }
++
++ /* SOF (for ISO transfer) */
++ if (irq_src & USB_STS_SOF) {
++ status = IRQ_HANDLED;
++ }
++
++ /* Port Change */
++ if (irq_src & USB_STS_PORT_CHANGE) {
++ port_change_irq(udc);
++ status = IRQ_HANDLED;
++ }
++
++ /* Reset Received */
++ if (irq_src & USB_STS_RESET) {
++ reset_irq(udc);
++ status = IRQ_HANDLED;
++ }
++
++ /* Sleep Enable (Suspend) */
++ if (irq_src & USB_STS_SUSPEND) {
++ suspend_irq(udc);
++ status = IRQ_HANDLED;
++ }
++
++ if (irq_src & (USB_STS_ERR | USB_STS_SYS_ERR)) {
++ VDBG("Error IRQ %x ", irq_src);
++ }
++
++ spin_unlock_irqrestore(&udc->lock, flags);
++ return status;
++}
++
++/*----------------------------------------------------------------*
++ * Hook to gadget drivers
++ * Called by initialization code of gadget drivers
++*----------------------------------------------------------------*/
++int usb_gadget_register_driver(struct usb_gadget_driver *driver)
++{
++ int retval = -ENODEV;
++ unsigned long flags = 0;
++
++ if (!udc_controller)
++ return -ENODEV;
++
++ if (!driver || (driver->speed != USB_SPEED_FULL
++ && driver->speed != USB_SPEED_HIGH)
++ || !driver->bind || !driver->disconnect
++ || !driver->setup)
++ return -EINVAL;
++
++ if (udc_controller->driver)
++ return -EBUSY;
++
++ /* lock is needed but whether should use this lock or another */
++ spin_lock_irqsave(&udc_controller->lock, flags);
++
++ driver->driver.bus = 0;
++ /* hook up the driver */
++ udc_controller->driver = driver;
++ udc_controller->gadget.dev.driver = &driver->driver;
++ spin_unlock_irqrestore(&udc_controller->lock, flags);
++
++ /* bind udc driver to gadget driver */
++ retval = driver->bind(&udc_controller->gadget);
++ if (retval) {
++ VDBG("bind to %s --> %d", driver->driver.name, retval);
++ udc_controller->gadget.dev.driver = 0;
++ udc_controller->driver = 0;
++ goto out;
++ }
++
++ /* Enable DR IRQ reg and Set usbcmd reg Run bit */
++ dr_controller_run(udc_controller);
++ udc_controller->usb_state = USB_STATE_ATTACHED;
++ udc_controller->ep0_state = WAIT_FOR_SETUP;
++ udc_controller->ep0_dir = 0;
++ printk(KERN_INFO "%s: bind to driver %s \n",
++ udc_controller->gadget.name, driver->driver.name);
++
++out:
++ if (retval)
++ printk("retval %d \n", retval);
++ return retval;
++}
++EXPORT_SYMBOL(usb_gadget_register_driver);
++
++/* Disconnect from gadget driver */
++int usb_gadget_unregister_driver(struct usb_gadget_driver *driver)
++{
++ struct fsl_ep *loop_ep;
++ unsigned long flags;
++
++ if (!udc_controller)
++ return -ENODEV;
++
++ if (!driver || driver != udc_controller->driver || !driver->unbind)
++ return -EINVAL;
++
++#ifdef CONFIG_USB_OTG
++ if (udc_controller->transceiver)
++ (void)otg_set_peripheral(udc_controller->transceiver, 0);
++#endif
++
++ /* stop DR, disable intr */
++ dr_controller_stop(udc_controller);
++
++ /* in fact, no needed */
++ udc_controller->usb_state = USB_STATE_ATTACHED;
++ udc_controller->ep0_state = WAIT_FOR_SETUP;
++ udc_controller->ep0_dir = 0;
++
++ /* stand operation */
++ spin_lock_irqsave(&udc_controller->lock, flags);
++ udc_controller->gadget.speed = USB_SPEED_UNKNOWN;
++ nuke(&udc_controller->eps[0], -ESHUTDOWN);
++ list_for_each_entry(loop_ep, &udc_controller->gadget.ep_list,
++ ep.ep_list)
++ nuke(loop_ep, -ESHUTDOWN);
++ spin_unlock_irqrestore(&udc_controller->lock, flags);
++
++ /* unbind gadget and unhook driver. */
++ driver->unbind(&udc_controller->gadget);
++ udc_controller->gadget.dev.driver = 0;
++ udc_controller->driver = 0;
++
++ printk("unregistered gadget driver '%s'\r\n", driver->driver.name);
++ return 0;
++}
++EXPORT_SYMBOL(usb_gadget_unregister_driver);
++
++/*-------------------------------------------------------------------------
++ PROC File System Support
++-------------------------------------------------------------------------*/
++#ifdef CONFIG_USB_GADGET_DEBUG_FILES
++
++#include <linux/seq_file.h>
++
++static const char proc_filename[] = "driver/fsl_usb2_udc";
++
++static int fsl_proc_read(char *page, char **start, off_t off, int count,
++ int *eof, void *_dev)
++{
++ char *buf = page;
++ char *next = buf;
++ unsigned size = count;
++ unsigned long flags;
++ int t, i;
++ u32 tmp_reg;
++ struct fsl_ep *ep = NULL;
++ struct fsl_req *req;
++
++ struct fsl_udc *udc = udc_controller;
++ if (off != 0)
++ return 0;
++
++ spin_lock_irqsave(&udc->lock, flags);
++
++ /* ------basic driver infomation ---- */
++ t = scnprintf(next, size,
++ DRIVER_DESC "\n"
++ "%s version: %s\n"
++ "Gadget driver: %s\n\n",
++ driver_name, DRIVER_VERSION,
++ udc->driver ? udc->driver->driver.name : "(none)");
++ size -= t;
++ next += t;
++
++ /* ------ DR Registers ----- */
++ tmp_reg = fsl_readl(&dr_regs->usbcmd);
++ t = scnprintf(next, size,
++ "USBCMD reg:\n"
++ "SetupTW: %d\n"
++ "Run/Stop: %s\n\n",
++ (tmp_reg & USB_CMD_SUTW) ? 1 : 0,
++ (tmp_reg & USB_CMD_RUN_STOP) ? "Run" : "Stop");
++ size -= t;
++ next += t;
++
++ tmp_reg = fsl_readl(&dr_regs->usbsts);
++ t = scnprintf(next, size,
++ "USB Status Reg:\n"
++ "Dr Suspend: %d" "Reset Received: %d" "System Error: %s"
++ "USB Error Interrupt: %s\n\n",
++ (tmp_reg & USB_STS_SUSPEND) ? 1 : 0,
++ (tmp_reg & USB_STS_RESET) ? 1 : 0,
++ (tmp_reg & USB_STS_SYS_ERR) ? "Err" : "Normal",
++ (tmp_reg & USB_STS_ERR) ? "Err detected" : "No err");
++ size -= t;
++ next += t;
++
++ tmp_reg = fsl_readl(&dr_regs->usbintr);
++ t = scnprintf(next, size,
++ "USB Intrrupt Enable Reg:\n"
++ "Sleep Enable: %d" "SOF Received Enable: %d"
++ "Reset Enable: %d\n"
++ "System Error Enable: %d"
++ "Port Change Dectected Enable: %d\n"
++ "USB Error Intr Enable: %d" "USB Intr Enable: %d\n\n",
++ (tmp_reg & USB_INTR_DEVICE_SUSPEND) ? 1 : 0,
++ (tmp_reg & USB_INTR_SOF_EN) ? 1 : 0,
++ (tmp_reg & USB_INTR_RESET_EN) ? 1 : 0,
++ (tmp_reg & USB_INTR_SYS_ERR_EN) ? 1 : 0,
++ (tmp_reg & USB_INTR_PTC_DETECT_EN) ? 1 : 0,
++ (tmp_reg & USB_INTR_ERR_INT_EN) ? 1 : 0,
++ (tmp_reg & USB_INTR_INT_EN) ? 1 : 0);
++ size -= t;
++ next += t;
++
++ tmp_reg = fsl_readl(&dr_regs->frindex);
++ t = scnprintf(next, size,
++ "USB Frame Index Reg:" "Frame Number is 0x%x\n\n",
++ (tmp_reg & USB_FRINDEX_MASKS));
++ size -= t;
++ next += t;
++
++ tmp_reg = fsl_readl(&dr_regs->deviceaddr);
++ t = scnprintf(next, size,
++ "USB Device Address Reg:" "Device Addr is 0x%x\n\n",
++ (tmp_reg & USB_DEVICE_ADDRESS_MASK));
++ size -= t;
++ next += t;
++
++ tmp_reg = fsl_readl(&dr_regs->endpointlistaddr);
++ t = scnprintf(next, size,
++ "USB Endpoint List Address Reg:"
++ "Device Addr is 0x%x\n\n",
++ (tmp_reg & USB_EP_LIST_ADDRESS_MASK));
++ size -= t;
++ next += t;
++
++ tmp_reg = fsl_readl(&dr_regs->portsc1);
++ t = scnprintf(next, size,
++ "USB Port Status&Control Reg:\n"
++ "Port Transceiver Type : %s" "Port Speed: %s \n"
++ "PHY Low Power Suspend: %s" "Port Reset: %s"
++ "Port Suspend Mode: %s \n" "Over-current Change: %s"
++ "Port Enable/Disable Change: %s\n"
++ "Port Enabled/Disabled: %s"
++ "Current Connect Status: %s\n\n", ( {
++ char *s;
++ switch (tmp_reg & PORTSCX_PTS_FSLS) {
++ case PORTSCX_PTS_UTMI:
++ s = "UTMI"; break;
++ case PORTSCX_PTS_ULPI:
++ s = "ULPI "; break;
++ case PORTSCX_PTS_FSLS:
++ s = "FS/LS Serial"; break;
++ default:
++ s = "None"; break;
++ }
++ s;} ), ( {
++ char *s;
++ switch (tmp_reg & PORTSCX_PORT_SPEED_UNDEF) {
++ case PORTSCX_PORT_SPEED_FULL:
++ s = "Full Speed"; break;
++ case PORTSCX_PORT_SPEED_LOW:
++ s = "Low Speed"; break;
++ case PORTSCX_PORT_SPEED_HIGH:
++ s = "High Speed"; break;
++ default:
++ s = "Undefined"; break;
++ }
++ s;
++ } ),
++ (tmp_reg & PORTSCX_PHY_LOW_POWER_SPD) ?
++ "Normal PHY mode" : "Low power mode",
++ (tmp_reg & PORTSCX_PORT_RESET) ? "In Reset" :
++ "Not in Reset",
++ (tmp_reg & PORTSCX_PORT_SUSPEND) ? "In " : "Not in",
++ (tmp_reg & PORTSCX_OVER_CURRENT_CHG) ? "Dected" :
++ "No",
++ (tmp_reg & PORTSCX_PORT_EN_DIS_CHANGE) ? "Disable" :
++ "Not change",
++ (tmp_reg & PORTSCX_PORT_ENABLE) ? "Enable" :
++ "Not correct",
++ (tmp_reg & PORTSCX_CURRENT_CONNECT_STATUS) ?
++ "Attached" : "Not-Att");
++ size -= t;
++ next += t;
++
++ tmp_reg = fsl_readl(&dr_regs->usbmode);
++ t = scnprintf(next, size,
++ "USB Mode Reg:" "Controller Mode is : %s\n\n", ( {
++ char *s;
++ switch (tmp_reg & USB_MODE_CTRL_MODE_HOST) {
++ case USB_MODE_CTRL_MODE_IDLE:
++ s = "Idle"; break;
++ case USB_MODE_CTRL_MODE_DEVICE:
++ s = "Device Controller"; break;
++ case USB_MODE_CTRL_MODE_HOST:
++ s = "Host Controller"; break;
++ default:
++ s = "None"; break;
++ }
++ s;
++ } ));
++ size -= t;
++ next += t;
++
++ tmp_reg = fsl_readl(&dr_regs->endptsetupstat);
++ t = scnprintf(next, size,
++ "Endpoint Setup Status Reg:" "SETUP on ep 0x%x\n\n",
++ (tmp_reg & EP_SETUP_STATUS_MASK));
++ size -= t;
++ next += t;
++
++ for (i = 0; i < udc->max_ep / 2; i++) {
++ tmp_reg = fsl_readl(&dr_regs->endptctrl[i]);
++ t = scnprintf(next, size, "EP Ctrl Reg [0x%x]: = [0x%x]\n",
++ i, tmp_reg);
++ size -= t;
++ next += t;
++ }
++ tmp_reg = fsl_readl(&dr_regs->endpointprime);
++ t = scnprintf(next, size, "EP Prime Reg = [0x%x]\n", tmp_reg);
++ size -= t;
++ next += t;
++
++ tmp_reg = usb_sys_regs->snoop1;
++ t = scnprintf(next, size, "\nSnoop1 Reg : = [0x%x]\n\n", tmp_reg);
++ size -= t;
++ next += t;
++
++ tmp_reg = usb_sys_regs->control;
++ t = scnprintf(next, size, "General Control Reg : = [0x%x]\n\n",
++ tmp_reg);
++ size -= t;
++ next += t;
++
++ /* ------fsl_udc, fsl_ep, fsl_request structure information ----- */
++ ep = &udc->eps[0];
++ t = scnprintf(next, size, "For %s Maxpkt is 0x%x index is 0x%x\n",
++ ep->ep.name, ep_maxpacket(ep), ep_index(ep));
++ size -= t;
++ next += t;
++
++ if (list_empty(&ep->queue)) {
++ t = scnprintf(next, size, "its req queue is empty\n\n");
++ size -= t;
++ next += t;
++ } else {
++ list_for_each_entry(req, &ep->queue, queue) {
++ t = scnprintf(next, size,
++ "req %p actual 0x%x length 0x%x buf %p\n",
++ &req->req, req->req.actual,
++ req->req.length, req->req.buf);
++ size -= t;
++ next += t;
++ }
++ }
++ /* other gadget->eplist ep */
++ list_for_each_entry(ep, &udc->gadget.ep_list, ep.ep_list) {
++ if (ep->desc) {
++ t = scnprintf(next, size,
++ "\nFor %s Maxpkt is 0x%x "
++ "index is 0x%x\n",
++ ep->ep.name, ep_maxpacket(ep),
++ ep_index(ep));
++ size -= t;
++ next += t;
++
++ if (list_empty(&ep->queue)) {
++ t = scnprintf(next, size,
++ "its req queue is empty\n\n");
++ size -= t;
++ next += t;
++ } else {
++ list_for_each_entry(req, &ep->queue, queue) {
++ t = scnprintf(next, size,
++ "req %p actual 0x%x length"
++ "0x%x buf %p\n",
++ &req->req, req->req.actual,
++ req->req.length, req->req.buf);
++ size -= t;
++ next += t;
++ } /* end for each_entry of ep req */
++ } /* end for else */
++ } /* end for if(ep->queue) */
++ } /* end (ep->desc) */
++
++ spin_unlock_irqrestore(&udc->lock, flags);
++
++ *eof = 1;
++ return count - size;
++}
++
++#define create_proc_file() create_proc_read_entry(proc_filename, \
++ 0, NULL, fsl_proc_read, NULL)
++
++#define remove_proc_file() remove_proc_entry(proc_filename, NULL)
++
++#else /* !CONFIG_USB_GADGET_DEBUG_FILES */
++
++#define create_proc_file() do {} while (0)
++#define remove_proc_file() do {} while (0)
++
++#endif /* CONFIG_USB_GADGET_DEBUG_FILES */
++
++/*-------------------------------------------------------------------------*/
++
++/* Release udc structures */
++static void fsl_udc_release(struct device *dev)
++{
++ complete(udc_controller->done);
++ dma_free_coherent(dev, udc_controller->ep_qh_size,
++ udc_controller->ep_qh, udc_controller->ep_qh_dma);
++ kfree(udc_controller);
++}
++
++/******************************************************************
++ Internal structure setup functions
++*******************************************************************/
++/*------------------------------------------------------------------
++ * init resource for globle controller
++ * Return the udc handle on success or NULL on failure
++ ------------------------------------------------------------------*/
++static struct fsl_udc *__init struct_udc_setup(struct platform_device *pdev)
++{
++ struct fsl_udc *udc;
++ struct fsl_usb2_platform_data *pdata;
++ size_t size;
++
++ udc = kzalloc(sizeof(struct fsl_udc), GFP_KERNEL);
++ if (udc == NULL) {
++ ERR("malloc udc failed\n");
++ return NULL;
++ }
++
++ pdata = pdev->dev.platform_data;
++ udc->phy_mode = pdata->phy_mode;
++ /* max_ep_nr is bidirectional ep number, max_ep doubles the number */
++ udc->max_ep = pdata->max_ep_nr * 2;
++
++ udc->eps = kzalloc(sizeof(struct fsl_ep) * udc->max_ep, GFP_KERNEL);
++ if (!udc->eps) {
++ ERR("malloc fsl_ep failed\n");
++ goto cleanup;
++ }
++
++ /* initialized QHs, take care of alignment */
++ size = udc->max_ep * sizeof(struct ep_queue_head);
++ if (size < QH_ALIGNMENT)
++ size = QH_ALIGNMENT;
++ else if ((size % QH_ALIGNMENT) != 0) {
++ size += QH_ALIGNMENT + 1;
++ size &= ~(QH_ALIGNMENT - 1);
++ }
++ udc->ep_qh = dma_alloc_coherent(&pdev->dev, size,
++ &udc->ep_qh_dma, GFP_KERNEL);
++ if (!udc->ep_qh) {
++ ERR("malloc QHs for udc failed\n");
++ kfree(udc->eps);
++ goto cleanup;
++ }
++
++ udc->ep_qh_size = size;
++
++ /* Initialize ep0 status request structure */
++ /* FIXME: fsl_alloc_request() ignores ep argument */
++ udc->status_req = container_of(fsl_alloc_request(NULL, GFP_KERNEL),
++ struct fsl_req, req);
++ /* allocate a small amount of memory to get valid address */
++ udc->status_req->req.buf = kmalloc(8, GFP_KERNEL);
++ udc->status_req->req.dma = virt_to_phys(udc->status_req->req.buf);
++
++ udc->resume_state = USB_STATE_NOTATTACHED;
++ udc->usb_state = USB_STATE_POWERED;
++ udc->ep0_dir = 0;
++ udc->remote_wakeup = 0; /* default to 0 on reset */
++ spin_lock_init(&udc->lock);
++
++ return udc;
++
++cleanup:
++ kfree(udc);
++ return NULL;
++}
++
++/*----------------------------------------------------------------
++ * Setup the fsl_ep struct for eps
++ * Link fsl_ep->ep to gadget->ep_list
++ * ep0out is not used so do nothing here
++ * ep0in should be taken care
++ *--------------------------------------------------------------*/
++static int __init struct_ep_setup(struct fsl_udc *udc, unsigned char index,
++ char *name, int link)
++{
++ struct fsl_ep *ep = &udc->eps[index];
++
++ ep->udc = udc;
++ strcpy(ep->name, name);
++ ep->ep.name = ep->name;
++
++ ep->ep.ops = &fsl_ep_ops;
++ ep->stopped = 0;
++
++ /* for ep0: maxP defined in desc
++ * for other eps, maxP is set by epautoconfig() called by gadget layer
++ */
++ ep->ep.maxpacket = (unsigned short) ~0;
++
++ /* the queue lists any req for this ep */
++ INIT_LIST_HEAD(&ep->queue);
++
++ /* gagdet.ep_list used for ep_autoconfig so no ep0 */
++ if (link)
++ list_add_tail(&ep->ep.ep_list, &udc->gadget.ep_list);
++ ep->gadget = &udc->gadget;
++ ep->qh = &udc->ep_qh[index];
++
++ return 0;
++}
++
++/* Driver probe function
++ * all intialize operations implemented here except enabling usb_intr reg
++ */
++static int __init fsl_udc_probe(struct platform_device *pdev)
++{
++ struct resource *res;
++ int ret = -ENODEV;
++ unsigned int i;
++
++ if (strcmp(pdev->name, driver_name)) {
++ VDBG("Wrong device\n");
++ return -ENODEV;
++ }
++
++ /* board setup should have been done in the platform code */
++
++ /* Initialize the udc structure including QH member and other member */
++ udc_controller = struct_udc_setup(pdev);
++ if (!udc_controller) {
++ VDBG("udc_controller is NULL \n");
++ return -ENOMEM;
++ }
++
++ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
++ if (!res)
++ return -ENXIO;
++
++ if (!request_mem_region(res->start, res->end - res->start + 1,
++ driver_name)) {
++ ERR("request mem region for %s failed \n", pdev->name);
++ return -EBUSY;
++ }
++
++ dr_regs = ioremap(res->start, res->end - res->start + 1);
++ if (!dr_regs) {
++ ret = -ENOMEM;
++ goto err1;
++ }
++
++ usb_sys_regs = (struct usb_sys_interface *)
++ ((u32)dr_regs + USB_DR_SYS_OFFSET);
++
++ udc_controller->irq = platform_get_irq(pdev, 0);
++ if (!udc_controller->irq) {
++ ret = -ENODEV;
++ goto err2;
++ }
++
++ ret = request_irq(udc_controller->irq, fsl_udc_irq, SA_SHIRQ,
++ driver_name, udc_controller);
++ if (ret != 0) {
++ ERR("cannot request irq %d err %d \n",
++ udc_controller->irq, ret);
++ goto err2;
++ }
++
++ /* initialize usb hw reg except for regs for EP,
++ * leave usbintr reg untouched */
++ dr_controller_setup(udc_controller);
++
++ /* Setup gadget structure */
++ udc_controller->gadget.ops = &fsl_gadget_ops;
++ udc_controller->gadget.is_dualspeed = 1;
++ udc_controller->gadget.ep0 = &udc_controller->eps[0].ep;
++ INIT_LIST_HEAD(&udc_controller->gadget.ep_list);
++ udc_controller->gadget.speed = USB_SPEED_UNKNOWN;
++ udc_controller->gadget.name = driver_name;
++
++ /* Setup gadget.dev and register with kernel */
++ strcpy(udc_controller->gadget.dev.bus_id, "gadget");
++ udc_controller->gadget.dev.release = fsl_udc_release;
++ udc_controller->gadget.dev.parent = &pdev->dev;
++ ret = device_register(&udc_controller->gadget.dev);
++ if (ret < 0)
++ goto err3;
++
++ /* setup QH and epctrl for ep0 */
++ ep0_setup(udc_controller);
++
++ /* setup udc->eps[] for ep0 */
++ struct_ep_setup(udc_controller, 0, "ep0", 0);
++ /* for ep0: the desc defined here;
++ * for other eps, gadget layer called ep_enable with defined desc
++ */
++ udc_controller->eps[0].desc = &fsl_ep0_desc;
++ udc_controller->eps[0].ep.maxpacket = USB_MAX_CTRL_PAYLOAD;
++
++ /* setup the udc->eps[] for non-control endpoints and link
++ * to gadget.ep_list */
++ for (i = 1; i < (int)(udc_controller->max_ep / 2); i++) {
++ char name[14];
++
++ sprintf(name, "ep%dout", i);
++ struct_ep_setup(udc_controller, i * 2, name, 1);
++ sprintf(name, "ep%din", i);
++ struct_ep_setup(udc_controller, i * 2 + 1, name, 1);
++ }
++
++ /* use dma_pool for TD management */
++ udc_controller->td_pool = dma_pool_create("udc_td", &pdev->dev,
++ sizeof(struct ep_td_struct),
++ DTD_ALIGNMENT, UDC_DMA_BOUNDARY);
++ if (udc_controller->td_pool == NULL) {
++ ret = -ENOMEM;
++ goto err4;
++ }
++ create_proc_file();
++ return 0;
++
++err4:
++ device_unregister(&udc_controller->gadget.dev);
++err3:
++ free_irq(udc_controller->irq, udc_controller);
++err2:
++ iounmap(dr_regs);
++err1:
++ release_mem_region(res->start, res->end - res->start + 1);
++ return ret;
++}
++
++/* Driver removal function
++ * Free resources and finish pending transactions
++ */
++static int __exit fsl_udc_remove(struct platform_device *pdev)
++{
++ struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
++
++ DECLARE_COMPLETION(done);
++
++ if (!udc_controller)
++ return -ENODEV;
++ udc_controller->done = &done;
++
++ /* DR has been stopped in usb_gadget_unregister_driver() */
++ remove_proc_file();
++
++ /* Free allocated memory */
++ kfree(udc_controller->status_req->req.buf);
++ kfree(udc_controller->status_req);
++ kfree(udc_controller->eps);
++
++ dma_pool_destroy(udc_controller->td_pool);
++ free_irq(udc_controller->irq, udc_controller);
++ iounmap(dr_regs);
++ release_mem_region(res->start, res->end - res->start + 1);
++
++ device_unregister(&udc_controller->gadget.dev);
++ /* free udc --wait for the release() finished */
++ wait_for_completion(&done);
++
++ return 0;
++}
++
++/*-----------------------------------------------------------------
++ * Modify Power management attributes
++ * Used by OTG statemachine to disable gadget temporarily
++ -----------------------------------------------------------------*/
++static int fsl_udc_suspend(struct platform_device *pdev, pm_message_t state)
++{
++ dr_controller_stop(udc_controller);
++ return 0;
++}
++
++/*-----------------------------------------------------------------
++ * Invoked on USB resume. May be called in_interrupt.
++ * Here we start the DR controller and enable the irq
++ *-----------------------------------------------------------------*/
++static int fsl_udc_resume(struct platform_device *pdev)
++{
++ /* Enable DR irq reg and set controller Run */
++ if (udc_controller->stopped) {
++ dr_controller_setup(udc_controller);
++ dr_controller_run(udc_controller);
++ }
++ udc_controller->usb_state = USB_STATE_ATTACHED;
++ udc_controller->ep0_state = WAIT_FOR_SETUP;
++ udc_controller->ep0_dir = 0;
++ return 0;
++}
++
++/*-------------------------------------------------------------------------
++ Register entry point for the peripheral controller driver
++--------------------------------------------------------------------------*/
++
++static struct platform_driver udc_driver = {
++ .remove = __exit_p(fsl_udc_remove),
++ /* these suspend and resume are not usb suspend and resume */
++ .suspend = fsl_udc_suspend,
++ .resume = fsl_udc_resume,
++ .driver = {
++ .name = (char *)driver_name,
++ .owner = THIS_MODULE,
++ },
++};
++
++static int __init udc_init(void)
++{
++ printk(KERN_INFO "%s (%s)\n", driver_desc, DRIVER_VERSION);
++ return platform_driver_probe(&udc_driver, fsl_udc_probe);
++}
++
++module_init(udc_init);
++
++static void __exit udc_exit(void)
++{
++ platform_driver_unregister(&udc_driver);
++ printk("%s unregistered \n", driver_desc);
++}
++
++module_exit(udc_exit);
++
++MODULE_DESCRIPTION(DRIVER_DESC);
++MODULE_AUTHOR(DRIVER_AUTHOR);
++MODULE_LICENSE("GPL");
+--- /dev/null
++++ b/drivers/usb/gadget/fsl_usb2_udc.h
+@@ -0,0 +1,579 @@
++/*
++ * Freescale USB device/endpoint management registers
++ */
++#ifndef __FSL_USB2_UDC_H
++#define __FSL_USB2_UDC_H
++
++/* ### define USB registers here
++ */
++#define USB_MAX_CTRL_PAYLOAD 64
++#define USB_DR_SYS_OFFSET 0x400
++
++ /* USB DR device mode registers (Little Endian) */
++struct usb_dr_device {
++ /* Capability register */
++ u8 res1[256];
++ u16 caplength; /* Capability Register Length */
++ u16 hciversion; /* Host Controller Interface Version */
++ u32 hcsparams; /* Host Controller Structual Parameters */
++ u32 hccparams; /* Host Controller Capability Parameters */
++ u8 res2[20];
++ u32 dciversion; /* Device Controller Interface Version */
++ u32 dccparams; /* Device Controller Capability Parameters */
++ u8 res3[24];
++ /* Operation register */
++ u32 usbcmd; /* USB Command Register */
++ u32 usbsts; /* USB Status Register */
++ u32 usbintr; /* USB Interrupt Enable Register */
++ u32 frindex; /* Frame Index Register */
++ u8 res4[4];
++ u32 deviceaddr; /* Device Address */
++ u32 endpointlistaddr; /* Endpoint List Address Register */
++ u8 res5[4];
++ u32 burstsize; /* Master Interface Data Burst Size Register */
++ u32 txttfilltuning; /* Transmit FIFO Tuning Controls Register */
++ u8 res6[24];
++ u32 configflag; /* Configure Flag Register */
++ u32 portsc1; /* Port 1 Status and Control Register */
++ u8 res7[28];
++ u32 otgsc; /* On-The-Go Status and Control */
++ u32 usbmode; /* USB Mode Register */
++ u32 endptsetupstat; /* Endpoint Setup Status Register */
++ u32 endpointprime; /* Endpoint Initialization Register */
++ u32 endptflush; /* Endpoint Flush Register */
++ u32 endptstatus; /* Endpoint Status Register */
++ u32 endptcomplete; /* Endpoint Complete Register */
++ u32 endptctrl[6]; /* Endpoint Control Registers */
++};
++
++ /* USB DR host mode registers (Little Endian) */
++struct usb_dr_host {
++ /* Capability register */
++ u8 res1[256];
++ u16 caplength; /* Capability Register Length */
++ u16 hciversion; /* Host Controller Interface Version */
++ u32 hcsparams; /* Host Controller Structual Parameters */
++ u32 hccparams; /* Host Controller Capability Parameters */
++ u8 res2[20];
++ u32 dciversion; /* Device Controller Interface Version */
++ u32 dccparams; /* Device Controller Capability Parameters */
++ u8 res3[24];
++ /* Operation register */
++ u32 usbcmd; /* USB Command Register */
++ u32 usbsts; /* USB Status Register */
++ u32 usbintr; /* USB Interrupt Enable Register */
++ u32 frindex; /* Frame Index Register */
++ u8 res4[4];
++ u32 periodiclistbase; /* Periodic Frame List Base Address Register */
++ u32 asynclistaddr; /* Current Asynchronous List Address Register */
++ u8 res5[4];
++ u32 burstsize; /* Master Interface Data Burst Size Register */
++ u32 txttfilltuning; /* Transmit FIFO Tuning Controls Register */
++ u8 res6[24];
++ u32 configflag; /* Configure Flag Register */
++ u32 portsc1; /* Port 1 Status and Control Register */
++ u8 res7[28];
++ u32 otgsc; /* On-The-Go Status and Control */
++ u32 usbmode; /* USB Mode Register */
++ u32 endptsetupstat; /* Endpoint Setup Status Register */
++ u32 endpointprime; /* Endpoint Initialization Register */
++ u32 endptflush; /* Endpoint Flush Register */
++ u32 endptstatus; /* Endpoint Status Register */
++ u32 endptcomplete; /* Endpoint Complete Register */
++ u32 endptctrl[6]; /* Endpoint Control Registers */
++};
++
++ /* non-EHCI USB system interface registers (Big Endian) */
++struct usb_sys_interface {
++ u32 snoop1;
++ u32 snoop2;
++ u32 age_cnt_thresh; /* Age Count Threshold Register */
++ u32 pri_ctrl; /* Priority Control Register */
++ u32 si_ctrl; /* System Interface Control Register */
++ u8 res[236];
++ u32 control; /* General Purpose Control Register */
++};
++
++/* ep0 transfer state */
++#define WAIT_FOR_SETUP 0
++#define DATA_STATE_XMIT 1
++#define DATA_STATE_NEED_ZLP 2
++#define WAIT_FOR_OUT_STATUS 3
++#define DATA_STATE_RECV 4
++
++/* Frame Index Register Bit Masks */
++#define USB_FRINDEX_MASKS 0x3fff
++/* USB CMD Register Bit Masks */
++#define USB_CMD_RUN_STOP 0x00000001
++#define USB_CMD_CTRL_RESET 0x00000002
++#define USB_CMD_PERIODIC_SCHEDULE_EN 0x00000010
++#define USB_CMD_ASYNC_SCHEDULE_EN 0x00000020
++#define USB_CMD_INT_AA_DOORBELL 0x00000040
++#define USB_CMD_ASP 0x00000300
++#define USB_CMD_ASYNC_SCH_PARK_EN 0x00000800
++#define USB_CMD_SUTW 0x00002000
++#define USB_CMD_ATDTW 0x00004000
++#define USB_CMD_ITC 0x00FF0000
++
++/* bit 15,3,2 are frame list size */
++#define USB_CMD_FRAME_SIZE_1024 0x00000000
++#define USB_CMD_FRAME_SIZE_512 0x00000004
++#define USB_CMD_FRAME_SIZE_256 0x00000008
++#define USB_CMD_FRAME_SIZE_128 0x0000000C
++#define USB_CMD_FRAME_SIZE_64 0x00008000
++#define USB_CMD_FRAME_SIZE_32 0x00008004
++#define USB_CMD_FRAME_SIZE_16 0x00008008
++#define USB_CMD_FRAME_SIZE_8 0x0000800C
++
++/* bit 9-8 are async schedule park mode count */
++#define USB_CMD_ASP_00 0x00000000
++#define USB_CMD_ASP_01 0x00000100
++#define USB_CMD_ASP_10 0x00000200
++#define USB_CMD_ASP_11 0x00000300
++#define USB_CMD_ASP_BIT_POS 8
++
++/* bit 23-16 are interrupt threshold control */
++#define USB_CMD_ITC_NO_THRESHOLD 0x00000000
++#define USB_CMD_ITC_1_MICRO_FRM 0x00010000
++#define USB_CMD_ITC_2_MICRO_FRM 0x00020000
++#define USB_CMD_ITC_4_MICRO_FRM 0x00040000
++#define USB_CMD_ITC_8_MICRO_FRM 0x00080000
++#define USB_CMD_ITC_16_MICRO_FRM 0x00100000
++#define USB_CMD_ITC_32_MICRO_FRM 0x00200000
++#define USB_CMD_ITC_64_MICRO_FRM 0x00400000
++#define USB_CMD_ITC_BIT_POS 16
++
++/* USB STS Register Bit Masks */
++#define USB_STS_INT 0x00000001
++#define USB_STS_ERR 0x00000002
++#define USB_STS_PORT_CHANGE 0x00000004
++#define USB_STS_FRM_LST_ROLL 0x00000008
++#define USB_STS_SYS_ERR 0x00000010
++#define USB_STS_IAA 0x00000020
++#define USB_STS_RESET 0x00000040
++#define USB_STS_SOF 0x00000080
++#define USB_STS_SUSPEND 0x00000100
++#define USB_STS_HC_HALTED 0x00001000
++#define USB_STS_RCL 0x00002000
++#define USB_STS_PERIODIC_SCHEDULE 0x00004000
++#define USB_STS_ASYNC_SCHEDULE 0x00008000
++
++/* USB INTR Register Bit Masks */
++#define USB_INTR_INT_EN 0x00000001
++#define USB_INTR_ERR_INT_EN 0x00000002
++#define USB_INTR_PTC_DETECT_EN 0x00000004
++#define USB_INTR_FRM_LST_ROLL_EN 0x00000008
++#define USB_INTR_SYS_ERR_EN 0x00000010
++#define USB_INTR_ASYN_ADV_EN 0x00000020
++#define USB_INTR_RESET_EN 0x00000040
++#define USB_INTR_SOF_EN 0x00000080
++#define USB_INTR_DEVICE_SUSPEND 0x00000100
++
++/* Device Address bit masks */
++#define USB_DEVICE_ADDRESS_MASK 0xFE000000
++#define USB_DEVICE_ADDRESS_BIT_POS 25
++
++/* endpoint list address bit masks */
++#define USB_EP_LIST_ADDRESS_MASK 0xfffff800
++
++/* PORTSCX Register Bit Masks */
++#define PORTSCX_CURRENT_CONNECT_STATUS 0x00000001
++#define PORTSCX_CONNECT_STATUS_CHANGE 0x00000002
++#define PORTSCX_PORT_ENABLE 0x00000004
++#define PORTSCX_PORT_EN_DIS_CHANGE 0x00000008
++#define PORTSCX_OVER_CURRENT_ACT 0x00000010
++#define PORTSCX_OVER_CURRENT_CHG 0x00000020
++#define PORTSCX_PORT_FORCE_RESUME 0x00000040
++#define PORTSCX_PORT_SUSPEND 0x00000080
++#define PORTSCX_PORT_RESET 0x00000100
++#define PORTSCX_LINE_STATUS_BITS 0x00000C00
++#define PORTSCX_PORT_POWER 0x00001000
++#define PORTSCX_PORT_INDICTOR_CTRL 0x0000C000
++#define PORTSCX_PORT_TEST_CTRL 0x000F0000
++#define PORTSCX_WAKE_ON_CONNECT_EN 0x00100000
++#define PORTSCX_WAKE_ON_CONNECT_DIS 0x00200000
++#define PORTSCX_WAKE_ON_OVER_CURRENT 0x00400000
++#define PORTSCX_PHY_LOW_POWER_SPD 0x00800000
++#define PORTSCX_PORT_FORCE_FULL_SPEED 0x01000000
++#define PORTSCX_PORT_SPEED_MASK 0x0C000000
++#define PORTSCX_PORT_WIDTH 0x10000000
++#define PORTSCX_PHY_TYPE_SEL 0xC0000000
++
++/* bit 11-10 are line status */
++#define PORTSCX_LINE_STATUS_SE0 0x00000000
++#define PORTSCX_LINE_STATUS_JSTATE 0x00000400
++#define PORTSCX_LINE_STATUS_KSTATE 0x00000800
++#define PORTSCX_LINE_STATUS_UNDEF 0x00000C00
++#define PORTSCX_LINE_STATUS_BIT_POS 10
++
++/* bit 15-14 are port indicator control */
++#define PORTSCX_PIC_OFF 0x00000000
++#define PORTSCX_PIC_AMBER 0x00004000
++#define PORTSCX_PIC_GREEN 0x00008000
++#define PORTSCX_PIC_UNDEF 0x0000C000
++#define PORTSCX_PIC_BIT_POS 14
++
++/* bit 19-16 are port test control */
++#define PORTSCX_PTC_DISABLE 0x00000000
++#define PORTSCX_PTC_JSTATE 0x00010000
++#define PORTSCX_PTC_KSTATE 0x00020000
++#define PORTSCX_PTC_SEQNAK 0x00030000
++#define PORTSCX_PTC_PACKET 0x00040000
++#define PORTSCX_PTC_FORCE_EN 0x00050000
++#define PORTSCX_PTC_BIT_POS 16
++
++/* bit 27-26 are port speed */
++#define PORTSCX_PORT_SPEED_FULL 0x00000000
++#define PORTSCX_PORT_SPEED_LOW 0x04000000
++#define PORTSCX_PORT_SPEED_HIGH 0x08000000
++#define PORTSCX_PORT_SPEED_UNDEF 0x0C000000
++#define PORTSCX_SPEED_BIT_POS 26
++
++/* bit 28 is parallel transceiver width for UTMI interface */
++#define PORTSCX_PTW 0x10000000
++#define PORTSCX_PTW_8BIT 0x00000000
++#define PORTSCX_PTW_16BIT 0x10000000
++
++/* bit 31-30 are port transceiver select */
++#define PORTSCX_PTS_UTMI 0x00000000
++#define PORTSCX_PTS_ULPI 0x80000000
++#define PORTSCX_PTS_FSLS 0xC0000000
++#define PORTSCX_PTS_BIT_POS 30
++
++/* otgsc Register Bit Masks */
++#define OTGSC_CTRL_VUSB_DISCHARGE 0x00000001
++#define OTGSC_CTRL_VUSB_CHARGE 0x00000002
++#define OTGSC_CTRL_OTG_TERM 0x00000008
++#define OTGSC_CTRL_DATA_PULSING 0x00000010
++#define OTGSC_STS_USB_ID 0x00000100
++#define OTGSC_STS_A_VBUS_VALID 0x00000200
++#define OTGSC_STS_A_SESSION_VALID 0x00000400
++#define OTGSC_STS_B_SESSION_VALID 0x00000800
++#define OTGSC_STS_B_SESSION_END 0x00001000
++#define OTGSC_STS_1MS_TOGGLE 0x00002000
++#define OTGSC_STS_DATA_PULSING 0x00004000
++#define OTGSC_INTSTS_USB_ID 0x00010000
++#define OTGSC_INTSTS_A_VBUS_VALID 0x00020000
++#define OTGSC_INTSTS_A_SESSION_VALID 0x00040000
++#define OTGSC_INTSTS_B_SESSION_VALID 0x00080000
++#define OTGSC_INTSTS_B_SESSION_END 0x00100000
++#define OTGSC_INTSTS_1MS 0x00200000
++#define OTGSC_INTSTS_DATA_PULSING 0x00400000
++#define OTGSC_INTR_USB_ID 0x01000000
++#define OTGSC_INTR_A_VBUS_VALID 0x02000000
++#define OTGSC_INTR_A_SESSION_VALID 0x04000000
++#define OTGSC_INTR_B_SESSION_VALID 0x08000000
++#define OTGSC_INTR_B_SESSION_END 0x10000000
++#define OTGSC_INTR_1MS_TIMER 0x20000000
++#define OTGSC_INTR_DATA_PULSING 0x40000000
++
++/* USB MODE Register Bit Masks */
++#define USB_MODE_CTRL_MODE_IDLE 0x00000000
++#define USB_MODE_CTRL_MODE_DEVICE 0x00000002
++#define USB_MODE_CTRL_MODE_HOST 0x00000003
++#define USB_MODE_CTRL_MODE_RSV 0x00000001
++#define USB_MODE_SETUP_LOCK_OFF 0x00000008
++#define USB_MODE_STREAM_DISABLE 0x00000010
++/* Endpoint Flush Register */
++#define EPFLUSH_TX_OFFSET 0x00010000
++#define EPFLUSH_RX_OFFSET 0x00000000
++
++/* Endpoint Setup Status bit masks */
++#define EP_SETUP_STATUS_MASK 0x0000003F
++#define EP_SETUP_STATUS_EP0 0x00000001
++
++/* ENDPOINTCTRLx Register Bit Masks */
++#define EPCTRL_TX_ENABLE 0x00800000
++#define EPCTRL_TX_DATA_TOGGLE_RST 0x00400000 /* Not EP0 */
++#define EPCTRL_TX_DATA_TOGGLE_INH 0x00200000 /* Not EP0 */
++#define EPCTRL_TX_TYPE 0x000C0000
++#define EPCTRL_TX_DATA_SOURCE 0x00020000 /* Not EP0 */
++#define EPCTRL_TX_EP_STALL 0x00010000
++#define EPCTRL_RX_ENABLE 0x00000080
++#define EPCTRL_RX_DATA_TOGGLE_RST 0x00000040 /* Not EP0 */
++#define EPCTRL_RX_DATA_TOGGLE_INH 0x00000020 /* Not EP0 */
++#define EPCTRL_RX_TYPE 0x0000000C
++#define EPCTRL_RX_DATA_SINK 0x00000002 /* Not EP0 */
++#define EPCTRL_RX_EP_STALL 0x00000001
++
++/* bit 19-18 and 3-2 are endpoint type */
++#define EPCTRL_EP_TYPE_CONTROL 0
++#define EPCTRL_EP_TYPE_ISO 1
++#define EPCTRL_EP_TYPE_BULK 2
++#define EPCTRL_EP_TYPE_INTERRUPT 3
++#define EPCTRL_TX_EP_TYPE_SHIFT 18
++#define EPCTRL_RX_EP_TYPE_SHIFT 2
++
++/* SNOOPn Register Bit Masks */
++#define SNOOP_ADDRESS_MASK 0xFFFFF000
++#define SNOOP_SIZE_ZERO 0x00 /* snooping disable */
++#define SNOOP_SIZE_4KB 0x0B /* 4KB snoop size */
++#define SNOOP_SIZE_8KB 0x0C
++#define SNOOP_SIZE_16KB 0x0D
++#define SNOOP_SIZE_32KB 0x0E
++#define SNOOP_SIZE_64KB 0x0F
++#define SNOOP_SIZE_128KB 0x10
++#define SNOOP_SIZE_256KB 0x11
++#define SNOOP_SIZE_512KB 0x12
++#define SNOOP_SIZE_1MB 0x13
++#define SNOOP_SIZE_2MB 0x14
++#define SNOOP_SIZE_4MB 0x15
++#define SNOOP_SIZE_8MB 0x16
++#define SNOOP_SIZE_16MB 0x17
++#define SNOOP_SIZE_32MB 0x18
++#define SNOOP_SIZE_64MB 0x19
++#define SNOOP_SIZE_128MB 0x1A
++#define SNOOP_SIZE_256MB 0x1B
++#define SNOOP_SIZE_512MB 0x1C
++#define SNOOP_SIZE_1GB 0x1D
++#define SNOOP_SIZE_2GB 0x1E /* 2GB snoop size */
++
++/* pri_ctrl Register Bit Masks */
++#define PRI_CTRL_PRI_LVL1 0x0000000C
++#define PRI_CTRL_PRI_LVL0 0x00000003
++
++/* si_ctrl Register Bit Masks */
++#define SI_CTRL_ERR_DISABLE 0x00000010
++#define SI_CTRL_IDRC_DISABLE 0x00000008
++#define SI_CTRL_RD_SAFE_EN 0x00000004
++#define SI_CTRL_RD_PREFETCH_DISABLE 0x00000002
++#define SI_CTRL_RD_PREFEFETCH_VAL 0x00000001
++
++/* control Register Bit Masks */
++#define USB_CTRL_IOENB 0x00000004
++#define USB_CTRL_ULPI_INT0EN 0x00000001
++
++/* Endpoint Queue Head data struct
++ * Rem: all the variables of qh are LittleEndian Mode
++ * and NEXT_POINTER_MASK should operate on a LittleEndian, Phy Addr
++ */
++struct ep_queue_head {
++ u32 max_pkt_length; /* Mult(31-30) , Zlt(29) , Max Pkt len
++ and IOS(15) */
++ u32 curr_dtd_ptr; /* Current dTD Pointer(31-5) */
++ u32 next_dtd_ptr; /* Next dTD Pointer(31-5), T(0) */
++ u32 size_ioc_int_sts; /* Total bytes (30-16), IOC (15),
++ MultO(11-10), STS (7-0) */
++ u32 buff_ptr0; /* Buffer pointer Page 0 (31-12) */
++ u32 buff_ptr1; /* Buffer pointer Page 1 (31-12) */
++ u32 buff_ptr2; /* Buffer pointer Page 2 (31-12) */
++ u32 buff_ptr3; /* Buffer pointer Page 3 (31-12) */
++ u32 buff_ptr4; /* Buffer pointer Page 4 (31-12) */
++ u32 res1;
++ u8 setup_buffer[8]; /* Setup data 8 bytes */
++ u32 res2[4];
++};
++
++/* Endpoint Queue Head Bit Masks */
++#define EP_QUEUE_HEAD_MULT_POS 30
++#define EP_QUEUE_HEAD_ZLT_SEL 0x20000000
++#define EP_QUEUE_HEAD_MAX_PKT_LEN_POS 16
++#define EP_QUEUE_HEAD_MAX_PKT_LEN(ep_info) (((ep_info)>>16)&0x07ff)
++#define EP_QUEUE_HEAD_IOS 0x00008000
++#define EP_QUEUE_HEAD_NEXT_TERMINATE 0x00000001
++#define EP_QUEUE_HEAD_IOC 0x00008000
++#define EP_QUEUE_HEAD_MULTO 0x00000C00
++#define EP_QUEUE_HEAD_STATUS_HALT 0x00000040
++#define EP_QUEUE_HEAD_STATUS_ACTIVE 0x00000080
++#define EP_QUEUE_CURRENT_OFFSET_MASK 0x00000FFF
++#define EP_QUEUE_HEAD_NEXT_POINTER_MASK 0xFFFFFFE0
++#define EP_QUEUE_FRINDEX_MASK 0x000007FF
++#define EP_MAX_LENGTH_TRANSFER 0x4000
++
++/* Endpoint Transfer Descriptor data struct */
++/* Rem: all the variables of td are LittleEndian Mode */
++struct ep_td_struct {
++ u32 next_td_ptr; /* Next TD pointer(31-5), T(0) set
++ indicate invalid */
++ u32 size_ioc_sts; /* Total bytes (30-16), IOC (15),
++ MultO(11-10), STS (7-0) */
++ u32 buff_ptr0; /* Buffer pointer Page 0 */
++ u32 buff_ptr1; /* Buffer pointer Page 1 */
++ u32 buff_ptr2; /* Buffer pointer Page 2 */
++ u32 buff_ptr3; /* Buffer pointer Page 3 */
++ u32 buff_ptr4; /* Buffer pointer Page 4 */
++ u32 res;
++ /* 32 bytes */
++ dma_addr_t td_dma; /* dma address for this td */
++ /* virtual address of next td specified in next_td_ptr */
++ struct ep_td_struct *next_td_virt;
++};
++
++/* Endpoint Transfer Descriptor bit Masks */
++#define DTD_NEXT_TERMINATE 0x00000001
++#define DTD_IOC 0x00008000
++#define DTD_STATUS_ACTIVE 0x00000080
++#define DTD_STATUS_HALTED 0x00000040
++#define DTD_STATUS_DATA_BUFF_ERR 0x00000020
++#define DTD_STATUS_TRANSACTION_ERR 0x00000008
++#define DTD_RESERVED_FIELDS 0x80007300
++#define DTD_ADDR_MASK 0xFFFFFFE0
++#define DTD_PACKET_SIZE 0x7FFF0000
++#define DTD_LENGTH_BIT_POS 16
++#define DTD_ERROR_MASK (DTD_STATUS_HALTED | \
++ DTD_STATUS_DATA_BUFF_ERR | \
++ DTD_STATUS_TRANSACTION_ERR)
++/* Alignment requirements; must be a power of two */
++#define DTD_ALIGNMENT 0x20
++#define QH_ALIGNMENT 2048
++
++/* Controller dma boundary */
++#define UDC_DMA_BOUNDARY 0x1000
++
++/* -----------------------------------------------------------------------*/
++/* ##### enum data
++*/
++typedef enum {
++ e_ULPI,
++ e_UTMI_8BIT,
++ e_UTMI_16BIT,
++ e_SERIAL
++} e_PhyInterface;
++
++/*-------------------------------------------------------------------------*/
++
++/* ### driver private data
++ */
++struct fsl_req {
++ struct usb_request req;
++ struct list_head queue;
++ /* ep_queue() func will add
++ a request->queue into a udc_ep->queue 'd tail */
++ struct fsl_ep *ep;
++ unsigned mapped:1;
++
++ struct ep_td_struct *head, *tail; /* For dTD List
++ cpu endian Virtual addr */
++ unsigned int dtd_count;
++};
++
++#define REQ_UNCOMPLETE 1
++
++struct fsl_ep {
++ struct usb_ep ep;
++ struct list_head queue;
++ struct fsl_udc *udc;
++ struct ep_queue_head *qh;
++ const struct usb_endpoint_descriptor *desc;
++ struct usb_gadget *gadget;
++
++ char name[14];
++ unsigned stopped:1;
++};
++
++#define EP_DIR_IN 1
++#define EP_DIR_OUT 0
++
++struct fsl_udc {
++
++ struct usb_gadget gadget;
++ struct usb_gadget_driver *driver;
++ struct fsl_ep *eps;
++ unsigned int max_ep;
++ unsigned int irq;
++
++ struct usb_ctrlrequest local_setup_buff;
++ spinlock_t lock;
++ struct otg_transceiver *transceiver;
++ unsigned softconnect:1;
++ unsigned vbus_active:1;
++ unsigned stopped:1;
++ unsigned remote_wakeup:1;
++
++ struct ep_queue_head *ep_qh; /* Endpoints Queue-Head */
++ struct fsl_req *status_req; /* ep0 status request */
++ struct dma_pool *td_pool; /* dma pool for DTD */
++ enum fsl_usb2_phy_modes phy_mode;
++
++ size_t ep_qh_size; /* size after alignment adjustment*/
++ dma_addr_t ep_qh_dma; /* dma address of QH */
++
++ u32 max_pipes; /* Device max pipes */
++ u32 max_use_endpts; /* Max endpointes to be used */
++ u32 bus_reset; /* Device is bus reseting */
++ u32 resume_state; /* USB state to resume */
++ u32 usb_state; /* USB current state */
++ u32 usb_next_state; /* USB next state */
++ u32 ep0_state; /* Endpoint zero state */
++ u32 ep0_dir; /* Endpoint zero direction: can be
++ USB_DIR_IN or USB_DIR_OUT */
++ u32 usb_sof_count; /* SOF count */
++ u32 errors; /* USB ERRORs count */
++ u8 device_address; /* Device USB address */
++
++ struct completion *done; /* to make sure release() is done */
++};
++
++/*-------------------------------------------------------------------------*/
++
++#ifdef DEBUG
++#define DBG(fmt, args...) printk(KERN_DEBUG "[%s] " fmt "\n", \
++ __FUNCTION__, ## args)
++#else
++#define DBG(fmt, args...) do{}while(0)
++#endif
++
++#if 0
++static void dump_msg(const char *label, const u8 * buf, unsigned int length)
++{
++ unsigned int start, num, i;
++ char line[52], *p;
++
++ if (length >= 512)
++ return;
++ DBG("%s, length %u:\n", label, length);
++ start = 0;
++ while (length > 0) {
++ num = min(length, 16u);
++ p = line;
++ for (i = 0; i < num; ++i) {
++ if (i == 8)
++ *p++ = ' ';
++ sprintf(p, " %02x", buf[i]);
++ p += 3;
++ }
++ *p = 0;
++ printk(KERN_DEBUG "%6x: %s\n", start, line);
++ buf += num;
++ start += num;
++ length -= num;
++ }
++}
++#endif
++
++#ifdef VERBOSE
++#define VDBG DBG
++#else
++#define VDBG(stuff...) do{}while(0)
++#endif
++
++#define ERR(stuff...) printk(KERN_ERR "udc: " stuff)
++#define WARN(stuff...) printk(KERN_WARNING "udc: " stuff)
++#define INFO(stuff...) printk(KERN_INFO "udc: " stuff)
++
++/*-------------------------------------------------------------------------*/
++
++/* ### Add board specific defines here
++ */
++
++/*
++ * ### pipe direction macro from device view
++ */
++#define USB_RECV 0 /* OUT EP */
++#define USB_SEND 1 /* IN EP */
++
++/*
++ * ### internal used help routines.
++ */
++#define ep_index(EP) ((EP)->desc->bEndpointAddress&0xF)
++#define ep_maxpacket(EP) ((EP)->ep.maxpacket)
++#define ep_is_in(EP) ( (ep_index(EP) == 0) ? (EP->udc->ep0_dir == \
++ USB_DIR_IN ):((EP)->desc->bEndpointAddress \
++ & USB_DIR_IN)==USB_DIR_IN)
++#define get_ep_by_pipe(udc, pipe) ((pipe == 1)? &udc->eps[0]: \
++ &udc->eps[pipe])
++#define get_pipe_by_windex(windex) ((windex & USB_ENDPOINT_NUMBER_MASK) \
++ * 2 + ((windex & USB_DIR_IN) ? 1 : 0))
++#define get_pipe_by_ep(EP) (ep_index(EP) * 2 + ep_is_in(EP))
++
++#endif
diff --git a/usb/usb-add-picdem-device-to-ldusb.patch b/usb/usb-add-picdem-device-to-ldusb.patch
new file mode 100644
index 0000000..275e36b
--- /dev/null
+++ b/usb/usb-add-picdem-device-to-ldusb.patch
@@ -0,0 +1,43 @@
+From jgoncalves@peragrin.com Fri Apr 20 11:06:05 2007
+From: Joey Goncalves <jgoncalves@peragrin.com>
+Date: Fri, 20 Apr 2007 11:05:54 -0700
+Subject: USB: add picdem device to ldusb
+To: gregkh@suse.de
+Cc: "Hund, Michael" <MHund@LD-Didactic.de>
+Message-ID: <46290102.4080506@peragrin.com>
+
+
+Hi Greg:
+
+I have found that /drivers/usb/misc/ldusb.c works with the "PICDEM Full
+Speed USB"
+http://www.microchip.com/stellent/idcplg?IdcService=SS_GET_PAGE&nodeId=1406&dDocName=en021940
+
+
+Signed-off-by: Joey S Goncalves <jgoncalves@peragrin.com>
+Cc: Michael Hund <MHund@LD-Didactic.de>
+Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
+
+---
+ drivers/usb/misc/ldusb.c | 3 +++
+ 1 file changed, 3 insertions(+)
+
+--- a/drivers/usb/misc/ldusb.c
++++ b/drivers/usb/misc/ldusb.c
+@@ -62,6 +62,8 @@
+ #define USB_DEVICE_ID_VERNIER_SKIP 0x0003
+ #define USB_DEVICE_ID_VERNIER_CYCLOPS 0x0004
+
++#define USB_VENDOR_ID_MICROCHIP 0x04d8
++#define USB_DEVICE_ID_PICDEM 0x000c
+
+ #ifdef CONFIG_USB_DYNAMIC_MINORS
+ #define USB_LD_MINOR_BASE 0
+@@ -89,6 +91,7 @@ static struct usb_device_id ld_usb_table
+ { USB_DEVICE(USB_VENDOR_ID_VERNIER, USB_DEVICE_ID_VERNIER_GOTEMP) },
+ { USB_DEVICE(USB_VENDOR_ID_VERNIER, USB_DEVICE_ID_VERNIER_SKIP) },
+ { USB_DEVICE(USB_VENDOR_ID_VERNIER, USB_DEVICE_ID_VERNIER_CYCLOPS) },
++ { USB_DEVICE(USB_VENDOR_ID_MICROCHIP, USB_DEVICE_ID_PICDEM) },
+ { } /* Terminating entry */
+ };
+ MODULE_DEVICE_TABLE(usb, ld_usb_table);
diff --git a/usb/usb-bandrich-bandluxe-hsdpa-data-card-driver.patch b/usb/usb-bandrich-bandluxe-hsdpa-data-card-driver.patch
new file mode 100644
index 0000000..e891daa
--- /dev/null
+++ b/usb/usb-bandrich-bandluxe-hsdpa-data-card-driver.patch
@@ -0,0 +1,45 @@
+From akpm@linux-foundation.org Thu Apr 26 00:38:09 2007
+From: Leon Leong <upleong@bandrich.com>
+Date: Thu, 26 Apr 2007 00:38:02 -0700
+Subject: USB: BandRich BandLuxe HSDPA Data Card Driver
+To: greg@kroah.com
+Cc: linux-usb-devel@lists.sourceforge.net, akpm@linux-foundation.org, upleong@bandrich.com
+Message-ID: <200704260738.l3Q7c2sO024372@shell0.pdx.osdl.net>
+
+
+From: Leon Leong <upleong@bandrich.com>
+
+Add the detection for the BandRich BandLuxe C100/C100S/C120 HSDPA Data
+Card. With the vendor and product IDs are set properly, the data card can
+be detected and works fine.
+
+Signed-off-by: Leon Leong <upleong@bandrich.com>
+Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
+
+---
+ drivers/usb/serial/option.c | 6 ++++++
+ 1 file changed, 6 insertions(+)
+
+--- a/drivers/usb/serial/option.c
++++ b/drivers/usb/serial/option.c
+@@ -113,6 +113,10 @@ static int option_send_setup(struct usb
+ #define ANYDATA_VENDOR_ID 0x16d5
+ #define ANYDATA_PRODUCT_ID 0x6501
+
++#define BANDRICH_VENDOR_ID 0x1A8D
++#define BANDRICH_PRODUCT_C100_1 0x1002
++#define BANDRICH_PRODUCT_C100_2 0x1003
++
+ static struct usb_device_id option_ids[] = {
+ { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_COLT) },
+ { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_RICOLA) },
+@@ -165,6 +169,8 @@ static struct usb_device_id option_ids[]
+ { USB_DEVICE(NOVATELWIRELESS_VENDOR_ID, 0x2130) }, /* Novatel Merlin ES620 SM Bus */
+ { USB_DEVICE(NOVATELWIRELESS_VENDOR_ID, 0x2410) }, /* Novatel EU740 */
+ { USB_DEVICE(ANYDATA_VENDOR_ID, ANYDATA_PRODUCT_ID) },
++ { USB_DEVICE(BANDRICH_VENDOR_ID, BANDRICH_PRODUCT_C100_1) },
++ { USB_DEVICE(BANDRICH_VENDOR_ID, BANDRICH_PRODUCT_C100_2) },
+ { } /* Terminating entry */
+ };
+ MODULE_DEVICE_TABLE(usb, option_ids);
diff --git a/usb/usb-cp2101-new-device-ids.patch b/usb/usb-cp2101-new-device-ids.patch
new file mode 100644
index 0000000..9b2c575
--- /dev/null
+++ b/usb/usb-cp2101-new-device-ids.patch
@@ -0,0 +1,33 @@
+From craig@microtron.org.uk Fri Apr 20 06:37:33 2007
+From: Craig Shelley <craig@microtron.org.uk>
+Date: Fri, 20 Apr 2007 14:37:17 +0100
+Subject: USB: CP2101 New Device IDs
+To: Greg KH <greg@kroah.com>
+Cc: linux-usb-devel@lists.sourceforge.net
+Message-ID: <1177076237.3749.4.camel@localhost.localdomain>
+
+
+Two new device IDs for CP2101 driver.
+
+Signed-off-by: Craig Shelley <craig@microtron.org.uk>
+Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
+
+
+---
+ drivers/usb/serial/cp2101.c | 2 ++
+ 1 file changed, 2 insertions(+)
+
+--- a/drivers/usb/serial/cp2101.c
++++ b/drivers/usb/serial/cp2101.c
+@@ -58,9 +58,11 @@ static struct usb_device_id id_table []
+ { USB_DEVICE(0x10AB, 0x10C5) }, /* Siemens MC60 Cable */
+ { USB_DEVICE(0x10B5, 0xAC70) }, /* Nokia CA-42 USB */
+ { USB_DEVICE(0x10C4, 0x803B) }, /* Pololu USB-serial converter */
++ { USB_DEVICE(0x10C4, 0x8053) }, /* Enfora EDG1228 */
+ { USB_DEVICE(0x10C4, 0x8066) }, /* Argussoft In-System Programmer */
+ { USB_DEVICE(0x10C4, 0x807A) }, /* Crumb128 board */
+ { USB_DEVICE(0x10C4, 0x80CA) }, /* Degree Controls Inc */
++ { USB_DEVICE(0x10C4, 0x80DD) }, /* Tracient RFID */
+ { USB_DEVICE(0x10C4, 0x80F6) }, /* Suunto sports instrument */
+ { USB_DEVICE(0x10C4, 0x813D) }, /* Burnside Telecom Deskmobile */
+ { USB_DEVICE(0x10C4, 0x814A) }, /* West Mountain Radio RIGblaster P&P */
diff --git a/usb/usb-cxacru-adsl-state-management.patch b/usb/usb-cxacru-adsl-state-management.patch
new file mode 100644
index 0000000..42d9a1e
--- /dev/null
+++ b/usb/usb-cxacru-adsl-state-management.patch
@@ -0,0 +1,397 @@
+From akpm@linux-foundation.org Thu Apr 26 00:38:30 2007
+From: Simon Arlott <simon@fire.lp0.eu>
+Date: Thu, 26 Apr 2007 00:38:05 -0700
+Subject: USB: cxacru: ADSL state management
+To: greg@kroah.com
+Cc: linux-usb-devel@lists.sourceforge.net, akpm@linux-foundation.org, simon@fire.lp0.eu, duncan.sands@math.u-psud.fr, gregkh@suse.de
+Message-ID: <200704260738.l3Q7c5gY024386@shell0.pdx.osdl.net>
+
+
+From: Simon Arlott <simon@fire.lp0.eu>
+
+The device has commands to start/stop the ADSL function, so this adds a
+sysfs attribute to allow it to be started/stopped/restarted. It also stops
+polling the device for status when the ADSL function is disabled.
+
+There are no problems with sending multiple start or stop commands, even
+with a fast loop of them the device still works. There is no need to
+protect the restart process from further user actions while it's waiting
+for 1.5s.
+
+Signed-off-by: Simon Arlott <simon@fire.lp0.eu>
+Cc: Duncan Sands <duncan.sands@math.u-psud.fr>
+Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
+
+---
+ drivers/usb/atm/cxacru.c | 236 +++++++++++++++++++++++++++++++++++++++++++++--
+ 1 file changed, 227 insertions(+), 9 deletions(-)
+
+--- a/drivers/usb/atm/cxacru.c
++++ b/drivers/usb/atm/cxacru.c
+@@ -4,6 +4,7 @@
+ *
+ * Copyright (C) 2004 David Woodhouse, Duncan Sands, Roman Kagan
+ * Copyright (C) 2005 Duncan Sands, Roman Kagan (rkagan % mail ! ru)
++ * Copyright (C) 2007 Simon Arlott
+ *
+ * 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
+@@ -146,6 +147,13 @@ enum cxacru_info_idx {
+ CXINF_MAX = 0x1c,
+ };
+
++enum cxacru_poll_state {
++ CXPOLL_STOPPING,
++ CXPOLL_STOPPED,
++ CXPOLL_POLLING,
++ CXPOLL_SHUTDOWN
++};
++
+ struct cxacru_modem_type {
+ u32 pll_f_clk;
+ u32 pll_b_clk;
+@@ -158,8 +166,12 @@ struct cxacru_data {
+ const struct cxacru_modem_type *modem_type;
+
+ int line_status;
++ struct mutex adsl_state_serialize;
++ int adsl_status;
+ struct delayed_work poll_work;
+ u32 card_info[CXINF_MAX];
++ struct mutex poll_state_serialize;
++ int poll_state;
+
+ /* contol handles */
+ struct mutex cm_serialize;
+@@ -171,10 +183,18 @@ struct cxacru_data {
+ struct completion snd_done;
+ };
+
++static int cxacru_cm(struct cxacru_data *instance, enum cxacru_cm_request cm,
++ u8 *wdata, int wsize, u8 *rdata, int rsize);
++static void cxacru_poll_status(struct work_struct *work);
++
+ /* Card info exported through sysfs */
+ #define CXACRU__ATTR_INIT(_name) \
+ static DEVICE_ATTR(_name, S_IRUGO, cxacru_sysfs_show_##_name, NULL)
+
++#define CXACRU_CMD_INIT(_name) \
++static DEVICE_ATTR(_name, S_IWUSR | S_IRUGO, \
++ cxacru_sysfs_show_##_name, cxacru_sysfs_store_##_name)
++
+ #define CXACRU_ATTR_INIT(_value, _type, _name) \
+ static ssize_t cxacru_sysfs_show_##_name(struct device *dev, \
+ struct device_attribute *attr, char *buf) \
+@@ -187,9 +207,11 @@ static ssize_t cxacru_sysfs_show_##_name
+ CXACRU__ATTR_INIT(_name)
+
+ #define CXACRU_ATTR_CREATE(_v, _t, _name) CXACRU_DEVICE_CREATE_FILE(_name)
++#define CXACRU_CMD_CREATE(_name) CXACRU_DEVICE_CREATE_FILE(_name)
+ #define CXACRU__ATTR_CREATE(_name) CXACRU_DEVICE_CREATE_FILE(_name)
+
+ #define CXACRU_ATTR_REMOVE(_v, _t, _name) CXACRU_DEVICE_REMOVE_FILE(_name)
++#define CXACRU_CMD_REMOVE(_name) CXACRU_DEVICE_REMOVE_FILE(_name)
+ #define CXACRU__ATTR_REMOVE(_name) CXACRU_DEVICE_REMOVE_FILE(_name)
+
+ static ssize_t cxacru_sysfs_showattr_u32(u32 value, char *buf)
+@@ -278,6 +300,119 @@ static ssize_t cxacru_sysfs_show_mac_add
+ atm_dev->esi[3], atm_dev->esi[4], atm_dev->esi[5]);
+ }
+
++static ssize_t cxacru_sysfs_show_adsl_state(struct device *dev,
++ struct device_attribute *attr, char *buf)
++{
++ struct usb_interface *intf = to_usb_interface(dev);
++ struct usbatm_data *usbatm_instance = usb_get_intfdata(intf);
++ struct cxacru_data *instance = usbatm_instance->driver_data;
++ u32 value = instance->card_info[CXINF_LINE_STARTABLE];
++
++ switch (value) {
++ case 0: return snprintf(buf, PAGE_SIZE, "running\n");
++ case 1: return snprintf(buf, PAGE_SIZE, "stopped\n");
++ default: return snprintf(buf, PAGE_SIZE, "unknown (%u)\n", value);
++ }
++}
++
++static ssize_t cxacru_sysfs_store_adsl_state(struct device *dev,
++ struct device_attribute *attr, const char *buf, size_t count)
++{
++ struct usb_interface *intf = to_usb_interface(dev);
++ struct usbatm_data *usbatm_instance = usb_get_intfdata(intf);
++ struct cxacru_data *instance = usbatm_instance->driver_data;
++ int ret;
++ int poll = -1;
++ char str_cmd[8];
++ int len = strlen(buf);
++
++ if (!capable(CAP_NET_ADMIN))
++ return -EACCES;
++
++ ret = sscanf(buf, "%7s", str_cmd);
++ if (ret != 1)
++ return -EINVAL;
++ ret = 0;
++
++ if (mutex_lock_interruptible(&instance->adsl_state_serialize))
++ return -ERESTARTSYS;
++
++ if (!strcmp(str_cmd, "stop") || !strcmp(str_cmd, "restart")) {
++ ret = cxacru_cm(instance, CM_REQUEST_CHIP_ADSL_LINE_STOP, NULL, 0, NULL, 0);
++ if (ret < 0) {
++ atm_err(usbatm_instance, "change adsl state:"
++ " CHIP_ADSL_LINE_STOP returned %d\n", ret);
++
++ ret = -EIO;
++ } else {
++ ret = len;
++ poll = CXPOLL_STOPPED;
++ }
++ }
++
++ /* Line status is only updated every second
++ * and the device appears to only react to
++ * START/STOP every second too. Wait 1.5s to
++ * be sure that restart will have an effect. */
++ if (!strcmp(str_cmd, "restart"))
++ msleep(1500);
++
++ if (!strcmp(str_cmd, "start") || !strcmp(str_cmd, "restart")) {
++ ret = cxacru_cm(instance, CM_REQUEST_CHIP_ADSL_LINE_START, NULL, 0, NULL, 0);
++ if (ret < 0) {
++ atm_err(usbatm_instance, "change adsl state:"
++ " CHIP_ADSL_LINE_START returned %d\n", ret);
++
++ ret = -EIO;
++ } else {
++ ret = len;
++ poll = CXPOLL_POLLING;
++ }
++ }
++
++ if (!strcmp(str_cmd, "poll")) {
++ ret = len;
++ poll = CXPOLL_POLLING;
++ }
++
++ if (ret == 0) {
++ ret = -EINVAL;
++ poll = -1;
++ }
++
++ if (poll == CXPOLL_POLLING) {
++ mutex_lock(&instance->poll_state_serialize);
++ switch (instance->poll_state) {
++ case CXPOLL_STOPPED:
++ /* start polling */
++ instance->poll_state = CXPOLL_POLLING;
++ break;
++
++ case CXPOLL_STOPPING:
++ /* abort stop request */
++ instance->poll_state = CXPOLL_POLLING;
++ case CXPOLL_POLLING:
++ case CXPOLL_SHUTDOWN:
++ /* don't start polling */
++ poll = -1;
++ }
++ mutex_unlock(&instance->poll_state_serialize);
++ } else if (poll == CXPOLL_STOPPED) {
++ mutex_lock(&instance->poll_state_serialize);
++ /* request stop */
++ if (instance->poll_state == CXPOLL_POLLING)
++ instance->poll_state = CXPOLL_STOPPING;
++ mutex_unlock(&instance->poll_state_serialize);
++ }
++
++ mutex_unlock(&instance->adsl_state_serialize);
++
++ if (poll == CXPOLL_POLLING)
++ cxacru_poll_status(&instance->poll_work.work);
++
++ return ret;
++}
++
+ /*
+ * All device attributes are included in CXACRU_ALL_FILES
+ * so that the same list can be used multiple times:
+@@ -312,7 +447,8 @@ CXACRU_ATTR_##_action(CXINF_LINE_STARTAB
+ CXACRU_ATTR_##_action(CXINF_MODULATION, MODU, modulation); \
+ CXACRU_ATTR_##_action(CXINF_ADSL_HEADEND, u32, adsl_headend); \
+ CXACRU_ATTR_##_action(CXINF_ADSL_HEADEND_ENVIRONMENT, u32, adsl_headend_environment); \
+-CXACRU_ATTR_##_action(CXINF_CONTROLLER_VERSION, u32, adsl_controller_version);
++CXACRU_ATTR_##_action(CXINF_CONTROLLER_VERSION, u32, adsl_controller_version); \
++CXACRU_CMD_##_action( adsl_state);
+
+ CXACRU_ALL_FILES(INIT);
+
+@@ -493,8 +629,6 @@ static int cxacru_card_status(struct cxa
+ return 0;
+ }
+
+-static void cxacru_poll_status(struct work_struct *work);
+-
+ static int cxacru_atm_start(struct usbatm_data *usbatm_instance,
+ struct atm_dev *atm_dev)
+ {
+@@ -503,6 +637,7 @@ static int cxacru_atm_start(struct usbat
+ struct atm_dev *atm_dev = usbatm_instance->atm_dev;
+ */
+ int ret;
++ int start_polling = 1;
+
+ dbg("cxacru_atm_start");
+
+@@ -515,14 +650,35 @@ static int cxacru_atm_start(struct usbat
+ }
+
+ /* start ADSL */
++ mutex_lock(&instance->adsl_state_serialize);
+ ret = cxacru_cm(instance, CM_REQUEST_CHIP_ADSL_LINE_START, NULL, 0, NULL, 0);
+ if (ret < 0) {
+ atm_err(usbatm_instance, "cxacru_atm_start: CHIP_ADSL_LINE_START returned %d\n", ret);
++ mutex_unlock(&instance->adsl_state_serialize);
+ return ret;
+ }
+
+ /* Start status polling */
+- cxacru_poll_status(&instance->poll_work.work);
++ mutex_lock(&instance->poll_state_serialize);
++ switch (instance->poll_state) {
++ case CXPOLL_STOPPED:
++ /* start polling */
++ instance->poll_state = CXPOLL_POLLING;
++ break;
++
++ case CXPOLL_STOPPING:
++ /* abort stop request */
++ instance->poll_state = CXPOLL_POLLING;
++ case CXPOLL_POLLING:
++ case CXPOLL_SHUTDOWN:
++ /* don't start polling */
++ start_polling = 0;
++ }
++ mutex_unlock(&instance->poll_state_serialize);
++ mutex_unlock(&instance->adsl_state_serialize);
++
++ if (start_polling)
++ cxacru_poll_status(&instance->poll_work.work);
+ return 0;
+ }
+
+@@ -533,16 +689,46 @@ static void cxacru_poll_status(struct wo
+ u32 buf[CXINF_MAX] = {};
+ struct usbatm_data *usbatm = instance->usbatm;
+ struct atm_dev *atm_dev = usbatm->atm_dev;
++ int keep_polling = 1;
+ int ret;
+
+ ret = cxacru_cm_get_array(instance, CM_REQUEST_CARD_INFO_GET, buf, CXINF_MAX);
+ if (ret < 0) {
+- atm_warn(usbatm, "poll status: error %d\n", ret);
++ if (ret != -ESHUTDOWN)
++ atm_warn(usbatm, "poll status: error %d\n", ret);
++
++ mutex_lock(&instance->poll_state_serialize);
++ if (instance->poll_state != CXPOLL_SHUTDOWN) {
++ instance->poll_state = CXPOLL_STOPPED;
++
++ if (ret != -ESHUTDOWN)
++ atm_warn(usbatm, "polling disabled, set adsl_state"
++ " to 'start' or 'poll' to resume\n");
++ }
++ mutex_unlock(&instance->poll_state_serialize);
+ goto reschedule;
+ }
+
+ memcpy(instance->card_info, buf, sizeof(instance->card_info));
+
++ if (instance->adsl_status != buf[CXINF_LINE_STARTABLE]) {
++ instance->adsl_status = buf[CXINF_LINE_STARTABLE];
++
++ switch (instance->adsl_status) {
++ case 0:
++ atm_printk(KERN_INFO, usbatm, "ADSL state: running\n");
++ break;
++
++ case 1:
++ atm_printk(KERN_INFO, usbatm, "ADSL state: stopped\n");
++ break;
++
++ default:
++ atm_printk(KERN_INFO, usbatm, "Unknown adsl status %02x\n", instance->adsl_status);
++ break;
++ }
++ }
++
+ if (instance->line_status == buf[CXINF_LINE_STATUS])
+ goto reschedule;
+
+@@ -597,8 +783,20 @@ static void cxacru_poll_status(struct wo
+ break;
+ }
+ reschedule:
+- schedule_delayed_work(&instance->poll_work,
+- round_jiffies_relative(msecs_to_jiffies(POLL_INTERVAL*1000)));
++
++ mutex_lock(&instance->poll_state_serialize);
++ if (instance->poll_state == CXPOLL_STOPPING &&
++ instance->adsl_status == 1 && /* stopped */
++ instance->line_status == 0) /* down */
++ instance->poll_state = CXPOLL_STOPPED;
++
++ if (instance->poll_state == CXPOLL_STOPPED)
++ keep_polling = 0;
++ mutex_unlock(&instance->poll_state_serialize);
++
++ if (keep_polling)
++ schedule_delayed_work(&instance->poll_work,
++ round_jiffies_relative(POLL_INTERVAL*HZ));
+ }
+
+ static int cxacru_fw(struct usb_device *usb_dev, enum cxacru_fw_request fw,
+@@ -835,6 +1033,13 @@ static int cxacru_bind(struct usbatm_dat
+ instance->modem_type = (struct cxacru_modem_type *) id->driver_info;
+ memset(instance->card_info, 0, sizeof(instance->card_info));
+
++ mutex_init(&instance->poll_state_serialize);
++ instance->poll_state = CXPOLL_STOPPED;
++ instance->line_status = -1;
++ instance->adsl_status = -1;
++
++ mutex_init(&instance->adsl_state_serialize);
++
+ instance->rcv_buf = (u8 *) __get_free_page(GFP_KERNEL);
+ if (!instance->rcv_buf) {
+ dbg("cxacru_bind: no memory for rcv_buf");
+@@ -909,6 +1114,7 @@ static void cxacru_unbind(struct usbatm_
+ struct usb_interface *intf)
+ {
+ struct cxacru_data *instance = usbatm_instance->driver_data;
++ int is_polling = 1;
+
+ dbg("cxacru_unbind entered");
+
+@@ -917,8 +1123,20 @@ static void cxacru_unbind(struct usbatm_
+ return;
+ }
+
+- while (!cancel_delayed_work(&instance->poll_work))
+- flush_scheduled_work();
++ mutex_lock(&instance->poll_state_serialize);
++ BUG_ON(instance->poll_state == CXPOLL_SHUTDOWN);
++
++ /* ensure that status polling continues unless
++ * it has already stopped */
++ if (instance->poll_state == CXPOLL_STOPPED)
++ is_polling = 0;
++
++ /* stop polling from being stopped or started */
++ instance->poll_state = CXPOLL_SHUTDOWN;
++ mutex_unlock(&instance->poll_state_serialize);
++
++ if (is_polling)
++ cancel_rearming_delayed_work(&instance->poll_work);
+
+ usb_kill_urb(instance->snd_urb);
+ usb_kill_urb(instance->rcv_urb);
diff --git a/usb/usb-dell-device-id-for-option.c.patch b/usb/usb-dell-device-id-for-option.c.patch
new file mode 100644
index 0000000..90aab7d
--- /dev/null
+++ b/usb/usb-dell-device-id-for-option.c.patch
@@ -0,0 +1,35 @@
+From linux-usb-devel-bounces@lists.sourceforge.net Thu Apr 12 06:16:22 2007
+From: Hans Engelen <engelenh@gmail.com>
+Date: Thu, 12 Apr 2007 14:40:26 +0200
+Subject: USB: dell device id for option.c
+To: linux-usb-devel@lists.sourceforge.net
+Message-ID: <5383c62b0704120540j68e7c1ddtd2682a02ce8cacaf@mail.gmail.com>
+Content-Disposition: inline
+
+From: Hans Engelen <engelenh@gmail.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
+
+
+---
+ drivers/usb/serial/option.c | 3 +++
+ 1 file changed, 3 insertions(+)
+
+--- a/drivers/usb/serial/option.c
++++ b/drivers/usb/serial/option.c
+@@ -117,6 +117,8 @@ static int option_send_setup(struct usb
+ #define BANDRICH_PRODUCT_C100_1 0x1002
+ #define BANDRICH_PRODUCT_C100_2 0x1003
+
++#define DELL_VENDOR_ID 0x413C
++
+ static struct usb_device_id option_ids[] = {
+ { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_COLT) },
+ { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_RICOLA) },
+@@ -171,6 +173,7 @@ static struct usb_device_id option_ids[]
+ { USB_DEVICE(ANYDATA_VENDOR_ID, ANYDATA_PRODUCT_ID) },
+ { USB_DEVICE(BANDRICH_VENDOR_ID, BANDRICH_PRODUCT_C100_1) },
+ { USB_DEVICE(BANDRICH_VENDOR_ID, BANDRICH_PRODUCT_C100_2) },
++ { USB_DEVICE(DELL_VENDOR_ID, 0x8118) }, /* Dell Wireless 5510 Mobile Broadband HSDPA ExpressCard */
+ { } /* Terminating entry */
+ };
+ MODULE_DEVICE_TABLE(usb, option_ids);
diff --git a/usb/usb-elan-ftdi-check-for-driver-registration-status.patch b/usb/usb-elan-ftdi-check-for-driver-registration-status.patch
new file mode 100644
index 0000000..3430c50
--- /dev/null
+++ b/usb/usb-elan-ftdi-check-for-driver-registration-status.patch
@@ -0,0 +1,62 @@
+From akpm@linux-foundation.org Thu Apr 26 00:38:09 2007
+From: Cyrill Gorcunov <gorcunov@gmail.com>
+Date: Thu, 26 Apr 2007 00:38:00 -0700
+Subject: USB Elan FTDI: check for driver registration status
+To: greg@kroah.com
+Cc: linux-usb-devel@lists.sourceforge.net, akpm@linux-foundation.org, gorcunov@gmail.com, lcapitulino@mandriva.com.br, zaitcev@redhat.com
+Message-ID: <200704260738.l3Q7c1i9024364@shell0.pdx.osdl.net>
+
+
+From: Cyrill Gorcunov <gorcunov@gmail.com>
+
+Add checking of driver registration status and release allocated resources
+if it failed.
+
+Signed-off-by: Cyrill Gorcunov <gorcunov@gmail.com>
+Cc: Pete Zaitcev <zaitcev@redhat.com>
+Cc: "Luiz Fernando N. Capitulino" <lcapitulino@mandriva.com.br>
+Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
+
+---
+ drivers/usb/misc/ftdi-elan.c | 18 +++++++++++-------
+ 1 file changed, 11 insertions(+), 7 deletions(-)
+
+--- a/drivers/usb/misc/ftdi-elan.c
++++ b/drivers/usb/misc/ftdi-elan.c
+@@ -2910,24 +2910,28 @@ static int __init ftdi_elan_init(void)
+ INIT_LIST_HEAD(&ftdi_static_list);
+ status_queue = create_singlethread_workqueue("ftdi-status-control");
+ if (!status_queue)
+- goto err1;
++ goto err_status_queue;
+ command_queue = create_singlethread_workqueue("ftdi-command-engine");
+ if (!command_queue)
+- goto err2;
++ goto err_command_queue;
+ respond_queue = create_singlethread_workqueue("ftdi-respond-engine");
+ if (!respond_queue)
+- goto err3;
++ goto err_respond_queue;
+ result = usb_register(&ftdi_elan_driver);
+- if (result)
++ if (result) {
++ destroy_workqueue(status_queue);
++ destroy_workqueue(command_queue);
++ destroy_workqueue(respond_queue);
+ printk(KERN_ERR "usb_register failed. Error number %d\n",
+ result);
++ }
+ return result;
+
+- err3:
++ err_respond_queue:
+ destroy_workqueue(command_queue);
+- err2:
++ err_command_queue:
+ destroy_workqueue(status_queue);
+- err1:
++ err_status_queue:
+ printk(KERN_ERR "%s couldn't create workqueue\n", ftdi_elan_driver.name);
+ return -ENOMEM;
+ }
diff --git a/usb/usb-ethernet-gadget-workaround-network-stack-api-glitch.patch b/usb/usb-ethernet-gadget-workaround-network-stack-api-glitch.patch
new file mode 100644
index 0000000..aa2b0f3
--- /dev/null
+++ b/usb/usb-ethernet-gadget-workaround-network-stack-api-glitch.patch
@@ -0,0 +1,38 @@
+From david-b@pacbell.net Mon Apr 23 10:50:22 2007
+From: Erik Hovland <erik@hovland.org>
+Date: Mon, 23 Apr 2007 10:50:15 -0700
+Subject: usb ethernet gadget, workaround network stack API glitch
+To: Greg KH <greg@kroah.com>
+Cc: Erik Hovland <erik@hovland.org>
+Message-ID: <200704231050.15344.david-b@pacbell.net>
+Content-Disposition: inline
+
+
+From: Erik Hovland <erik@hovland.org>
+
+Another workaround for the glitch in the network layer, whereby one call
+ignores the (otherwise kernel-wide) convention that free() calls should
+not oops when passed nulls. This code already handles that API glitch in
+most other paths.
+
+From: Erik Hovland <erik@hovland.org>
+Signed-off-by: David Brownell <dbrownell@users.sourceforge.net>
+Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
+
+
+---
+ drivers/usb/gadget/ether.c | 3 ++-
+ 1 file changed, 2 insertions(+), 1 deletion(-)
+
+--- a/drivers/usb/gadget/ether.c
++++ b/drivers/usb/gadget/ether.c
+@@ -1735,7 +1735,8 @@ enomem:
+ defer_kevent (dev, WORK_RX_MEMORY);
+ if (retval) {
+ DEBUG (dev, "rx submit --> %d\n", retval);
+- dev_kfree_skb_any (skb);
++ if (skb)
++ dev_kfree_skb_any(skb);
+ spin_lock(&dev->req_lock);
+ list_add (&req->list, &dev->rx_reqs);
+ spin_unlock(&dev->req_lock);
diff --git a/usb/usb-gadget-rndis-fix-struct-rndis_packet_msg_type-unaligned-bug.patch b/usb/usb-gadget-rndis-fix-struct-rndis_packet_msg_type-unaligned-bug.patch
new file mode 100644
index 0000000..9b4a8b7
--- /dev/null
+++ b/usb/usb-gadget-rndis-fix-struct-rndis_packet_msg_type-unaligned-bug.patch
@@ -0,0 +1,36 @@
+From akpm@linux-foundation.org Thu Apr 26 00:38:09 2007
+From: "Wu, Bryan" <bryan.wu@analog.com>
+Date: Thu, 26 Apr 2007 00:38:01 -0700
+Subject: USB gadget rndis: fix struct rndis_packet_msg_type unaligned bug
+To: greg@kroah.com
+Cc: linux-usb-devel@lists.sourceforge.net, akpm@linux-foundation.org, bryan.wu@analog.com, david-b@pacbell.net, jie.zhang@analog.com, roy.huang@analog.com
+Message-ID: <200704260738.l3Q7c22A024368@shell0.pdx.osdl.net>
+
+
+From: "Wu, Bryan" <bryan.wu@analog.com>
+
+skb_push function may return a pointer which is not aligned as required
+by struct rndis_packet_msg_type. Using attribute trick to fix this bug.
+
+Signed-off-by: Roy Huang <roy.huang@analog.com>
+Signed-off-by: Jie Zhang <jie.zhang@analog.com>
+Signed-off-by: Bryan Wu <bryan.wu@analog.com>
+Cc: David Brownell <david-b@pacbell.net>
+Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
+
+---
+ drivers/usb/gadget/rndis.h | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+--- a/drivers/usb/gadget/rndis.h
++++ b/drivers/usb/gadget/rndis.h
+@@ -195,7 +195,7 @@ struct rndis_packet_msg_type
+ __le32 PerPacketInfoLength;
+ __le32 VcHandle;
+ __le32 Reserved;
+-};
++} __attribute__ ((packed));
+
+ struct rndis_config_parameter
+ {
diff --git a/usb/usb-gotemp.patch b/usb/usb-gotemp.patch
index 480b100..93124d7 100644
--- a/usb/usb-gotemp.patch
+++ b/usb/usb-gotemp.patch
@@ -21,7 +21,7 @@
--- a/drivers/usb/Makefile
+++ b/drivers/usb/Makefile
-@@ -57,6 +57,7 @@ obj-$(CONFIG_USB_CYTHERM) += misc/
+@@ -56,6 +56,7 @@ obj-$(CONFIG_USB_CYTHERM) += misc/
obj-$(CONFIG_USB_EMI26) += misc/
obj-$(CONFIG_USB_EMI62) += misc/
obj-$(CONFIG_USB_FTDI_ELAN) += misc/
diff --git a/usb/usb-iowarrior.c-timeouts-too-small-in-usb_control_msg-calls.patch b/usb/usb-iowarrior.c-timeouts-too-small-in-usb_control_msg-calls.patch
new file mode 100644
index 0000000..2388a14
--- /dev/null
+++ b/usb/usb-iowarrior.c-timeouts-too-small-in-usb_control_msg-calls.patch
@@ -0,0 +1,74 @@
+From e.fahle@wayoda.org Thu Apr 5 02:13:33 2007
+From: Eberhard Fahle <e.fahle@wayoda.org>
+Date: Thu, 5 Apr 2007 11:13:21 +0200
+Subject: USB: iowarrior.c: timeouts too small in usb_control_msg calls
+To: linux-usb-devel@lists.sourceforge.net
+Cc: "Greg Kroah-Hartman" <gregkh@suse.de>, Robert Marquardt <marquardt@codemercs.com>
+Message-ID: <200704051113.21968.e.fahle@wayoda.org>
+
+
+The driver uses usb_control_msg() for exchanging data with the device.
+When the driver lived freeley _outside_ the kernel tree (pre 2.6.21) the
+timeouts for these calls where set to 5*HZ for reading, 1HZ for writing.
+(These timeouts seemed to work fine for all users of the driver, at
+least nobody complained in the last 2 years.
+
+The current code (2.6.21-rc5) removed the 'HZ' from the timeouts and
+left the driver with 5 jiffies for reading and 1 jiffy for writing. My
+new machine is fast, but not that fast.
+
+The patch also removes a useless debug statement, which was left over
+from testing a broken firmware version
+
+From: Eberhard Fahle <e.fahle@wayoda.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
+
+
+---
+ drivers/usb/misc/iowarrior.c | 14 ++++++--------
+ 1 file changed, 6 insertions(+), 8 deletions(-)
+
+--- a/drivers/usb/misc/iowarrior.c
++++ b/drivers/usb/misc/iowarrior.c
+@@ -118,7 +118,7 @@ static int usb_get_report(struct usb_dev
+ USB_DIR_IN | USB_TYPE_CLASS |
+ USB_RECIP_INTERFACE, (type << 8) + id,
+ inter->desc.bInterfaceNumber, buf, size,
+- GET_TIMEOUT);
++ GET_TIMEOUT*HZ);
+ }
+ //#endif
+
+@@ -133,7 +133,7 @@ static int usb_set_report(struct usb_int
+ USB_TYPE_CLASS | USB_RECIP_INTERFACE,
+ (type << 8) + id,
+ intf->cur_altsetting->desc.bInterfaceNumber, buf,
+- size, 1);
++ size, HZ);
+ }
+
+ /*---------------------*/
+@@ -750,7 +750,6 @@ static int iowarrior_probe(struct usb_in
+ struct usb_endpoint_descriptor *endpoint;
+ int i;
+ int retval = -ENOMEM;
+- int idele = 0;
+
+ /* allocate memory for our device state and intialize it */
+ dev = kzalloc(sizeof(struct iowarrior), GFP_KERNEL);
+@@ -826,11 +825,10 @@ static int iowarrior_probe(struct usb_in
+
+ /* Set the idle timeout to 0, if this is interface 0 */
+ if (dev->interface->cur_altsetting->desc.bInterfaceNumber == 0) {
+- idele = usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
+- 0x0A,
+- USB_TYPE_CLASS | USB_RECIP_INTERFACE, 0,
+- 0, NULL, 0, USB_CTRL_SET_TIMEOUT);
+- dbg("idele = %d", idele);
++ usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
++ 0x0A,
++ USB_TYPE_CLASS | USB_RECIP_INTERFACE, 0,
++ 0, NULL, 0, USB_CTRL_SET_TIMEOUT);
+ }
+ /* allow device read and ioctl */
+ dev->present = 1;
diff --git a/usb/usb-quirk-for-broken-suspend-of-it8152f-g.patch b/usb/usb-quirk-for-broken-suspend-of-it8152f-g.patch
new file mode 100644
index 0000000..c56ac54
--- /dev/null
+++ b/usb/usb-quirk-for-broken-suspend-of-it8152f-g.patch
@@ -0,0 +1,37 @@
+From david-b@pacbell.net Tue Apr 17 13:09:33 2007
+From: Raphael Assenat <raph@8d.com>
+Date: Tue, 17 Apr 2007 13:09:18 -0700
+Subject: USB: quirk for broken suspend of IT8152F/G
+To: Greg KH <greg@kroah.com>
+Cc: Raphael Assenat <raph@8d.com>
+Message-ID: <200704171309.19008.david-b@pacbell.net>
+
+
+From: Raphael Assenat <raph@8d.com>
+
+Here's a patch which adds my device to the list.
+
+This patch enables the broken suspend quirk for the PCI OHCI controller
+present in the IT8152F/G RISC-to-PCI Companion Chip.
+
+Signed-off-by: Raphael Assenat <raph@8d.com>
+Signed-off-by: David Brownell <dbrownell@users.sourceforge.net>
+Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
+
+---
+ drivers/usb/host/ohci-pci.c | 4 ++++
+ 1 file changed, 4 insertions(+)
+
+--- a/drivers/usb/host/ohci-pci.c
++++ b/drivers/usb/host/ohci-pci.c
+@@ -141,6 +141,10 @@ static const struct pci_device_id ohci_p
+ .subdevice = 0x0004,
+ .driver_data = (unsigned long) broken_suspend,
+ },
++ {
++ PCI_DEVICE(PCI_VENDOR_ID_ITE, 0x8152),
++ .driver_data = (unsigned long) broken_suspend,
++ },
+ /* FIXME for some of the early AMD 760 southbridges, OHCI
+ * won't work at all. blacklist them.
+ */
diff --git a/usb/usb-remove-ancient-broken-cris-hcd.patch b/usb/usb-remove-ancient-broken-cris-hcd.patch
new file mode 100644
index 0000000..7f7a82d
--- /dev/null
+++ b/usb/usb-remove-ancient-broken-cris-hcd.patch
@@ -0,0 +1,4891 @@
+From david-b@pacbell.net Sun Apr 22 11:06:02 2007
+From: David Brownell <david-b@pacbell.net>
+Date: Sun, 22 Apr 2007 11:05:52 -0700
+Subject: USB: remove ancient/broken CRIS hcd
+To: linux-usb-devel@lists.sourceforge.net
+Cc: Greg KH <greg@kroah.com>, "Mikael Starvik" <mikael.starvik@axis.com>
+Message-ID: <200704221105.53754.david-b@pacbell.net>
+Content-Disposition: inline
+
+
+Remove the old crisv10 HCD ... it can't have built for some time,
+doesn't even have a Kconfig entry, was the last driver not to have
+been converted to the "hcd" framework, and considering the usbcore
+changes since its last patch was merged, has just got to buggy as
+all get-out.
+
+I'm told Axis has a new driver, and will be submitting it soon.
+
+Signed-off-by: David Brownell <dbrownell@users.sourceforge.net>
+Cc: Mikael Starvik <mikael.starvik@axis.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
+
+---
+ drivers/usb/Makefile | 1
+ drivers/usb/host/Makefile | 1
+ drivers/usb/host/hc_crisv10.c | 4550 ------------------------------------------
+ drivers/usb/host/hc_crisv10.h | 289 --
+ 4 files changed, 4841 deletions(-)
+
+--- a/drivers/usb/Makefile
++++ b/drivers/usb/Makefile
+@@ -15,7 +15,6 @@ obj-$(CONFIG_USB_OHCI_HCD) += host/
+ obj-$(CONFIG_USB_UHCI_HCD) += host/
+ obj-$(CONFIG_USB_SL811_HCD) += host/
+ obj-$(CONFIG_USB_U132_HCD) += host/
+-obj-$(CONFIG_ETRAX_USB_HOST) += host/
+ obj-$(CONFIG_USB_OHCI_AT91) += host/
+
+ obj-$(CONFIG_USB_ACM) += class/
+--- a/drivers/usb/host/Makefile
++++ b/drivers/usb/host/Makefile
+@@ -15,4 +15,3 @@ obj-$(CONFIG_USB_UHCI_HCD) += uhci-hcd.o
+ obj-$(CONFIG_USB_SL811_HCD) += sl811-hcd.o
+ obj-$(CONFIG_USB_SL811_CS) += sl811_cs.o
+ obj-$(CONFIG_USB_U132_HCD) += u132-hcd.o
+-obj-$(CONFIG_ETRAX_ARCH_V10) += hc_crisv10.o
+--- a/drivers/usb/host/hc_crisv10.c
++++ /dev/null
+@@ -1,4550 +0,0 @@
+-/*
+- * usb-host.c: ETRAX 100LX USB Host Controller Driver (HCD)
+- *
+- * Copyright (c) 2002, 2003 Axis Communications AB.
+- */
+-
+-#include <linux/kernel.h>
+-#include <linux/delay.h>
+-#include <linux/ioport.h>
+-#include <linux/slab.h>
+-#include <linux/errno.h>
+-#include <linux/unistd.h>
+-#include <linux/interrupt.h>
+-#include <linux/init.h>
+-#include <linux/list.h>
+-#include <linux/spinlock.h>
+-
+-#include <asm/uaccess.h>
+-#include <asm/io.h>
+-#include <asm/irq.h>
+-#include <asm/dma.h>
+-#include <asm/system.h>
+-#include <asm/arch/svinto.h>
+-
+-#include <linux/usb.h>
+-/* Ugly include because we don't live with the other host drivers. */
+-#include <../drivers/usb/core/hcd.h>
+-#include <../drivers/usb/core/usb.h>
+-
+-#include "hc_crisv10.h"
+-
+-#define ETRAX_USB_HC_IRQ USB_HC_IRQ_NBR
+-#define ETRAX_USB_RX_IRQ USB_DMA_RX_IRQ_NBR
+-#define ETRAX_USB_TX_IRQ USB_DMA_TX_IRQ_NBR
+-
+-static const char *usb_hcd_version = "$Revision: 1.2 $";
+-
+-#undef KERN_DEBUG
+-#define KERN_DEBUG ""
+-
+-
+-#undef USB_DEBUG_RH
+-#undef USB_DEBUG_EPID
+-#undef USB_DEBUG_SB
+-#undef USB_DEBUG_DESC
+-#undef USB_DEBUG_URB
+-#undef USB_DEBUG_TRACE
+-#undef USB_DEBUG_BULK
+-#undef USB_DEBUG_CTRL
+-#undef USB_DEBUG_INTR
+-#undef USB_DEBUG_ISOC
+-
+-#ifdef USB_DEBUG_RH
+-#define dbg_rh(format, arg...) printk(KERN_DEBUG __FILE__ ": (RH) " format "\n" , ## arg)
+-#else
+-#define dbg_rh(format, arg...) do {} while (0)
+-#endif
+-
+-#ifdef USB_DEBUG_EPID
+-#define dbg_epid(format, arg...) printk(KERN_DEBUG __FILE__ ": (EPID) " format "\n" , ## arg)
+-#else
+-#define dbg_epid(format, arg...) do {} while (0)
+-#endif
+-
+-#ifdef USB_DEBUG_SB
+-#define dbg_sb(format, arg...) printk(KERN_DEBUG __FILE__ ": (SB) " format "\n" , ## arg)
+-#else
+-#define dbg_sb(format, arg...) do {} while (0)
+-#endif
+-
+-#ifdef USB_DEBUG_CTRL
+-#define dbg_ctrl(format, arg...) printk(KERN_DEBUG __FILE__ ": (CTRL) " format "\n" , ## arg)
+-#else
+-#define dbg_ctrl(format, arg...) do {} while (0)
+-#endif
+-
+-#ifdef USB_DEBUG_BULK
+-#define dbg_bulk(format, arg...) printk(KERN_DEBUG __FILE__ ": (BULK) " format "\n" , ## arg)
+-#else
+-#define dbg_bulk(format, arg...) do {} while (0)
+-#endif
+-
+-#ifdef USB_DEBUG_INTR
+-#define dbg_intr(format, arg...) printk(KERN_DEBUG __FILE__ ": (INTR) " format "\n" , ## arg)
+-#else
+-#define dbg_intr(format, arg...) do {} while (0)
+-#endif
+-
+-#ifdef USB_DEBUG_ISOC
+-#define dbg_isoc(format, arg...) printk(KERN_DEBUG __FILE__ ": (ISOC) " format "\n" , ## arg)
+-#else
+-#define dbg_isoc(format, arg...) do {} while (0)
+-#endif
+-
+-#ifdef USB_DEBUG_TRACE
+-#define DBFENTER (printk(": Entering: %s\n", __FUNCTION__))
+-#define DBFEXIT (printk(": Exiting: %s\n", __FUNCTION__))
+-#else
+-#define DBFENTER do {} while (0)
+-#define DBFEXIT do {} while (0)
+-#endif
+-
+-#define usb_pipeslow(pipe) (((pipe) >> 26) & 1)
+-
+-/*-------------------------------------------------------------------
+- Virtual Root Hub
+- -------------------------------------------------------------------*/
+-
+-static __u8 root_hub_dev_des[] =
+-{
+- 0x12, /* __u8 bLength; */
+- 0x01, /* __u8 bDescriptorType; Device */
+- 0x00, /* __le16 bcdUSB; v1.0 */
+- 0x01,
+- 0x09, /* __u8 bDeviceClass; HUB_CLASSCODE */
+- 0x00, /* __u8 bDeviceSubClass; */
+- 0x00, /* __u8 bDeviceProtocol; */
+- 0x08, /* __u8 bMaxPacketSize0; 8 Bytes */
+- 0x00, /* __le16 idVendor; */
+- 0x00,
+- 0x00, /* __le16 idProduct; */
+- 0x00,
+- 0x00, /* __le16 bcdDevice; */
+- 0x00,
+- 0x00, /* __u8 iManufacturer; */
+- 0x02, /* __u8 iProduct; */
+- 0x01, /* __u8 iSerialNumber; */
+- 0x01 /* __u8 bNumConfigurations; */
+-};
+-
+-/* Configuration descriptor */
+-static __u8 root_hub_config_des[] =
+-{
+- 0x09, /* __u8 bLength; */
+- 0x02, /* __u8 bDescriptorType; Configuration */
+- 0x19, /* __le16 wTotalLength; */
+- 0x00,
+- 0x01, /* __u8 bNumInterfaces; */
+- 0x01, /* __u8 bConfigurationValue; */
+- 0x00, /* __u8 iConfiguration; */
+- 0x40, /* __u8 bmAttributes; Bit 7: Bus-powered */
+- 0x00, /* __u8 MaxPower; */
+-
+- /* interface */
+- 0x09, /* __u8 if_bLength; */
+- 0x04, /* __u8 if_bDescriptorType; Interface */
+- 0x00, /* __u8 if_bInterfaceNumber; */
+- 0x00, /* __u8 if_bAlternateSetting; */
+- 0x01, /* __u8 if_bNumEndpoints; */
+- 0x09, /* __u8 if_bInterfaceClass; HUB_CLASSCODE */
+- 0x00, /* __u8 if_bInterfaceSubClass; */
+- 0x00, /* __u8 if_bInterfaceProtocol; */
+- 0x00, /* __u8 if_iInterface; */
+-
+- /* endpoint */
+- 0x07, /* __u8 ep_bLength; */
+- 0x05, /* __u8 ep_bDescriptorType; Endpoint */
+- 0x81, /* __u8 ep_bEndpointAddress; IN Endpoint 1 */
+- 0x03, /* __u8 ep_bmAttributes; Interrupt */
+- 0x08, /* __le16 ep_wMaxPacketSize; 8 Bytes */
+- 0x00,
+- 0xff /* __u8 ep_bInterval; 255 ms */
+-};
+-
+-static __u8 root_hub_hub_des[] =
+-{
+- 0x09, /* __u8 bLength; */
+- 0x29, /* __u8 bDescriptorType; Hub-descriptor */
+- 0x02, /* __u8 bNbrPorts; */
+- 0x00, /* __u16 wHubCharacteristics; */
+- 0x00,
+- 0x01, /* __u8 bPwrOn2pwrGood; 2ms */
+- 0x00, /* __u8 bHubContrCurrent; 0 mA */
+- 0x00, /* __u8 DeviceRemovable; *** 7 Ports max *** */
+- 0xff /* __u8 PortPwrCtrlMask; *** 7 ports max *** */
+-};
+-
+-static DEFINE_TIMER(bulk_start_timer, NULL, 0, 0);
+-static DEFINE_TIMER(bulk_eot_timer, NULL, 0, 0);
+-
+-/* We want the start timer to expire before the eot timer, because the former might start
+- traffic, thus making it unnecessary for the latter to time out. */
+-#define BULK_START_TIMER_INTERVAL (HZ/10) /* 100 ms */
+-#define BULK_EOT_TIMER_INTERVAL (HZ/10+2) /* 120 ms */
+-
+-#define OK(x) len = (x); dbg_rh("OK(%d): line: %d", x, __LINE__); break
+-#define CHECK_ALIGN(x) if (((__u32)(x)) & 0x00000003) \
+-{panic("Alignment check (DWORD) failed at %s:%s:%d\n", __FILE__, __FUNCTION__, __LINE__);}
+-
+-#define SLAB_FLAG (in_interrupt() ? GFP_ATOMIC : GFP_KERNEL)
+-#define KMALLOC_FLAG (in_interrupt() ? GFP_ATOMIC : GFP_KERNEL)
+-
+-/* Most helpful debugging aid */
+-#define assert(expr) ((void) ((expr) ? 0 : (err("assert failed at line %d",__LINE__))))
+-
+-/* Alternative assert define which stops after a failed assert. */
+-/*
+-#define assert(expr) \
+-{ \
+- if (!(expr)) { \
+- err("assert failed at line %d",__LINE__); \
+- while (1); \
+- } \
+-}
+-*/
+-
+-
+-/* FIXME: Should RX_BUF_SIZE be a config option, or maybe we should adjust it dynamically?
+- To adjust it dynamically we would have to get an interrupt when we reach the end
+- of the rx descriptor list, or when we get close to the end, and then allocate more
+- descriptors. */
+-
+-#define NBR_OF_RX_DESC 512
+-#define RX_DESC_BUF_SIZE 1024
+-#define RX_BUF_SIZE (NBR_OF_RX_DESC * RX_DESC_BUF_SIZE)
+-
+-/* The number of epids is, among other things, used for pre-allocating
+- ctrl, bulk and isoc EP descriptors (one for each epid).
+- Assumed to be > 1 when initiating the DMA lists. */
+-#define NBR_OF_EPIDS 32
+-
+-/* Support interrupt traffic intervals up to 128 ms. */
+-#define MAX_INTR_INTERVAL 128
+-
+-/* If periodic traffic (intr or isoc) is to be used, then one entry in the EP table
+- must be "invalid". By this we mean that we shouldn't care about epid attentions
+- for this epid, or at least handle them differently from epid attentions for "valid"
+- epids. This define determines which one to use (don't change it). */
+-#define INVALID_EPID 31
+-/* A special epid for the bulk dummys. */
+-#define DUMMY_EPID 30
+-
+-/* This is just a software cache for the valid entries in R_USB_EPT_DATA. */
+-static __u32 epid_usage_bitmask;
+-
+-/* A bitfield to keep information on in/out traffic is needed to uniquely identify
+- an endpoint on a device, since the most significant bit which indicates traffic
+- direction is lacking in the ep_id field (ETRAX epids can handle both in and
+- out traffic on endpoints that are otherwise identical). The USB framework, however,
+- relies on them to be handled separately. For example, bulk IN and OUT urbs cannot
+- be queued in the same list, since they would block each other. */
+-static __u32 epid_out_traffic;
+-
+-/* DMA IN cache bug. Align the DMA IN buffers to 32 bytes, i.e. a cache line.
+- Since RX_DESC_BUF_SIZE is 1024 is a multiple of 32, all rx buffers will be cache aligned. */
+-static volatile unsigned char RxBuf[RX_BUF_SIZE] __attribute__ ((aligned (32)));
+-static volatile USB_IN_Desc_t RxDescList[NBR_OF_RX_DESC] __attribute__ ((aligned (4)));
+-
+-/* Pointers into RxDescList. */
+-static volatile USB_IN_Desc_t *myNextRxDesc;
+-static volatile USB_IN_Desc_t *myLastRxDesc;
+-static volatile USB_IN_Desc_t *myPrevRxDesc;
+-
+-/* EP descriptors must be 32-bit aligned. */
+-static volatile USB_EP_Desc_t TxCtrlEPList[NBR_OF_EPIDS] __attribute__ ((aligned (4)));
+-static volatile USB_EP_Desc_t TxBulkEPList[NBR_OF_EPIDS] __attribute__ ((aligned (4)));
+-/* After each enabled bulk EP (IN or OUT) we put two disabled EP descriptors with the eol flag set,
+- causing the DMA to stop the DMA channel. The first of these two has the intr flag set, which
+- gives us a dma8_sub0_descr interrupt. When we receive this, we advance the DMA one step in the
+- EP list and then restart the bulk channel, thus forcing a switch between bulk EP descriptors
+- in each frame. */
+-static volatile USB_EP_Desc_t TxBulkDummyEPList[NBR_OF_EPIDS][2] __attribute__ ((aligned (4)));
+-
+-static volatile USB_EP_Desc_t TxIsocEPList[NBR_OF_EPIDS] __attribute__ ((aligned (4)));
+-static volatile USB_SB_Desc_t TxIsocSB_zout __attribute__ ((aligned (4)));
+-
+-static volatile USB_EP_Desc_t TxIntrEPList[MAX_INTR_INTERVAL] __attribute__ ((aligned (4)));
+-static volatile USB_SB_Desc_t TxIntrSB_zout __attribute__ ((aligned (4)));
+-
+-/* A zout transfer makes a memory access at the address of its buf pointer, which means that setting
+- this buf pointer to 0 will cause an access to the flash. In addition to this, setting sw_len to 0
+- results in a 16/32 bytes (depending on DMA burst size) transfer. Instead, we set it to 1, and point
+- it to this buffer. */
+-static int zout_buffer[4] __attribute__ ((aligned (4)));
+-
+-/* Cache for allocating new EP and SB descriptors. */
+-static struct kmem_cache *usb_desc_cache;
+-
+-/* Cache for the registers allocated in the top half. */
+-static struct kmem_cache *top_half_reg_cache;
+-
+-/* Cache for the data allocated in the isoc descr top half. */
+-static struct kmem_cache *isoc_compl_cache;
+-
+-static struct usb_bus *etrax_usb_bus;
+-
+-/* This is a circular (double-linked) list of the active urbs for each epid.
+- The head is never removed, and new urbs are linked onto the list as
+- urb_entry_t elements. Don't reference urb_list directly; use the wrapper
+- functions instead. Note that working with these lists might require spinlock
+- protection. */
+-static struct list_head urb_list[NBR_OF_EPIDS];
+-
+-/* Read about the need and usage of this lock in submit_ctrl_urb. */
+-static spinlock_t urb_list_lock;
+-
+-/* Used when unlinking asynchronously. */
+-static struct list_head urb_unlink_list;
+-
+-/* for returning string descriptors in UTF-16LE */
+-static int ascii2utf (char *ascii, __u8 *utf, int utfmax)
+-{
+- int retval;
+-
+- for (retval = 0; *ascii && utfmax > 1; utfmax -= 2, retval += 2) {
+- *utf++ = *ascii++ & 0x7f;
+- *utf++ = 0;
+- }
+- return retval;
+-}
+-
+-static int usb_root_hub_string (int id, int serial, char *type, __u8 *data, int len)
+-{
+- char buf [30];
+-
+- // assert (len > (2 * (sizeof (buf) + 1)));
+- // assert (strlen (type) <= 8);
+-
+- // language ids
+- if (id == 0) {
+- *data++ = 4; *data++ = 3; /* 4 bytes data */
+- *data++ = 0; *data++ = 0; /* some language id */
+- return 4;
+-
+- // serial number
+- } else if (id == 1) {
+- sprintf (buf, "%x", serial);
+-
+- // product description
+- } else if (id == 2) {
+- sprintf (buf, "USB %s Root Hub", type);
+-
+- // id 3 == vendor description
+-
+- // unsupported IDs --> "stall"
+- } else
+- return 0;
+-
+- data [0] = 2 + ascii2utf (buf, data + 2, len - 2);
+- data [1] = 3;
+- return data [0];
+-}
+-
+-/* Wrappers around the list functions (include/linux/list.h). */
+-
+-static inline int urb_list_empty(int epid)
+-{
+- return list_empty(&urb_list[epid]);
+-}
+-
+-/* Returns first urb for this epid, or NULL if list is empty. */
+-static inline struct urb *urb_list_first(int epid)
+-{
+- struct urb *first_urb = 0;
+-
+- if (!urb_list_empty(epid)) {
+- /* Get the first urb (i.e. head->next). */
+- urb_entry_t *urb_entry = list_entry((&urb_list[epid])->next, urb_entry_t, list);
+- first_urb = urb_entry->urb;
+- }
+- return first_urb;
+-}
+-
+-/* Adds an urb_entry last in the list for this epid. */
+-static inline void urb_list_add(struct urb *urb, int epid)
+-{
+- urb_entry_t *urb_entry = kmalloc(sizeof(urb_entry_t), KMALLOC_FLAG);
+- assert(urb_entry);
+-
+- urb_entry->urb = urb;
+- list_add_tail(&urb_entry->list, &urb_list[epid]);
+-}
+-
+-/* Search through the list for an element that contains this urb. (The list
+- is expected to be short and the one we are about to delete will often be
+- the first in the list.) */
+-static inline urb_entry_t *__urb_list_entry(struct urb *urb, int epid)
+-{
+- struct list_head *entry;
+- struct list_head *tmp;
+- urb_entry_t *urb_entry;
+-
+- list_for_each_safe(entry, tmp, &urb_list[epid]) {
+- urb_entry = list_entry(entry, urb_entry_t, list);
+- assert(urb_entry);
+- assert(urb_entry->urb);
+-
+- if (urb_entry->urb == urb) {
+- return urb_entry;
+- }
+- }
+- return 0;
+-}
+-
+-/* Delete an urb from the list. */
+-static inline void urb_list_del(struct urb *urb, int epid)
+-{
+- urb_entry_t *urb_entry = __urb_list_entry(urb, epid);
+- assert(urb_entry);
+-
+- /* Delete entry and free. */
+- list_del(&urb_entry->list);
+- kfree(urb_entry);
+-}
+-
+-/* Move an urb to the end of the list. */
+-static inline void urb_list_move_last(struct urb *urb, int epid)
+-{
+- urb_entry_t *urb_entry = __urb_list_entry(urb, epid);
+- assert(urb_entry);
+-
+- list_move_tail(&urb_entry->list, &urb_list[epid]);
+-}
+-
+-/* Get the next urb in the list. */
+-static inline struct urb *urb_list_next(struct urb *urb, int epid)
+-{
+- urb_entry_t *urb_entry = __urb_list_entry(urb, epid);
+-
+- assert(urb_entry);
+-
+- if (urb_entry->list.next != &urb_list[epid]) {
+- struct list_head *elem = urb_entry->list.next;
+- urb_entry = list_entry(elem, urb_entry_t, list);
+- return urb_entry->urb;
+- } else {
+- return NULL;
+- }
+-}
+-
+-
+-
+-/* For debug purposes only. */
+-static inline void urb_list_dump(int epid)
+-{
+- struct list_head *entry;
+- struct list_head *tmp;
+- urb_entry_t *urb_entry;
+- int i = 0;
+-
+- info("Dumping urb list for epid %d", epid);
+-
+- list_for_each_safe(entry, tmp, &urb_list[epid]) {
+- urb_entry = list_entry(entry, urb_entry_t, list);
+- info(" entry %d, urb = 0x%lx", i, (unsigned long)urb_entry->urb);
+- }
+-}
+-
+-static void init_rx_buffers(void);
+-static int etrax_rh_unlink_urb(struct urb *urb);
+-static void etrax_rh_send_irq(struct urb *urb);
+-static void etrax_rh_init_int_timer(struct urb *urb);
+-static void etrax_rh_int_timer_do(unsigned long ptr);
+-
+-static int etrax_usb_setup_epid(struct urb *urb);
+-static int etrax_usb_lookup_epid(struct urb *urb);
+-static int etrax_usb_allocate_epid(void);
+-static void etrax_usb_free_epid(int epid);
+-
+-static int etrax_remove_from_sb_list(struct urb *urb);
+-
+-static void* etrax_usb_buffer_alloc(struct usb_bus* bus, size_t size,
+- unsigned mem_flags, dma_addr_t *dma);
+-static void etrax_usb_buffer_free(struct usb_bus *bus, size_t size, void *addr, dma_addr_t dma);
+-
+-static void etrax_usb_add_to_bulk_sb_list(struct urb *urb, int epid);
+-static void etrax_usb_add_to_ctrl_sb_list(struct urb *urb, int epid);
+-static void etrax_usb_add_to_intr_sb_list(struct urb *urb, int epid);
+-static void etrax_usb_add_to_isoc_sb_list(struct urb *urb, int epid);
+-
+-static int etrax_usb_submit_bulk_urb(struct urb *urb);
+-static int etrax_usb_submit_ctrl_urb(struct urb *urb);
+-static int etrax_usb_submit_intr_urb(struct urb *urb);
+-static int etrax_usb_submit_isoc_urb(struct urb *urb);
+-
+-static int etrax_usb_submit_urb(struct urb *urb, unsigned mem_flags);
+-static int etrax_usb_unlink_urb(struct urb *urb, int status);
+-static int etrax_usb_get_frame_number(struct usb_device *usb_dev);
+-
+-static irqreturn_t etrax_usb_tx_interrupt(int irq, void *vhc);
+-static irqreturn_t etrax_usb_rx_interrupt(int irq, void *vhc);
+-static irqreturn_t etrax_usb_hc_interrupt_top_half(int irq, void *vhc);
+-static void etrax_usb_hc_interrupt_bottom_half(void *data);
+-
+-static void etrax_usb_isoc_descr_interrupt_bottom_half(void *data);
+-
+-
+-/* The following is a list of interrupt handlers for the host controller interrupts we use.
+- They are called from etrax_usb_hc_interrupt_bottom_half. */
+-static void etrax_usb_hc_isoc_eof_interrupt(void);
+-static void etrax_usb_hc_bulk_eot_interrupt(int timer_induced);
+-static void etrax_usb_hc_epid_attn_interrupt(usb_interrupt_registers_t *reg);
+-static void etrax_usb_hc_port_status_interrupt(usb_interrupt_registers_t *reg);
+-static void etrax_usb_hc_ctl_status_interrupt(usb_interrupt_registers_t *reg);
+-
+-static int etrax_rh_submit_urb (struct urb *urb);
+-
+-/* Forward declaration needed because they are used in the rx interrupt routine. */
+-static void etrax_usb_complete_urb(struct urb *urb, int status);
+-static void etrax_usb_complete_bulk_urb(struct urb *urb, int status);
+-static void etrax_usb_complete_ctrl_urb(struct urb *urb, int status);
+-static void etrax_usb_complete_intr_urb(struct urb *urb, int status);
+-static void etrax_usb_complete_isoc_urb(struct urb *urb, int status);
+-
+-static int etrax_usb_hc_init(void);
+-static void etrax_usb_hc_cleanup(void);
+-
+-static struct usb_operations etrax_usb_device_operations =
+-{
+- .get_frame_number = etrax_usb_get_frame_number,
+- .submit_urb = etrax_usb_submit_urb,
+- .unlink_urb = etrax_usb_unlink_urb,
+- .buffer_alloc = etrax_usb_buffer_alloc,
+- .buffer_free = etrax_usb_buffer_free
+-};
+-
+-/* Note that these functions are always available in their "__" variants, for use in
+- error situations. The "__" missing variants are controlled by the USB_DEBUG_DESC/
+- USB_DEBUG_URB macros. */
+-static void __dump_urb(struct urb* purb)
+-{
+- printk("\nurb :0x%08lx\n", (unsigned long)purb);
+- printk("dev :0x%08lx\n", (unsigned long)purb->dev);
+- printk("pipe :0x%08x\n", purb->pipe);
+- printk("status :%d\n", purb->status);
+- printk("transfer_flags :0x%08x\n", purb->transfer_flags);
+- printk("transfer_buffer :0x%08lx\n", (unsigned long)purb->transfer_buffer);
+- printk("transfer_buffer_length:%d\n", purb->transfer_buffer_length);
+- printk("actual_length :%d\n", purb->actual_length);
+- printk("setup_packet :0x%08lx\n", (unsigned long)purb->setup_packet);
+- printk("start_frame :%d\n", purb->start_frame);
+- printk("number_of_packets :%d\n", purb->number_of_packets);
+- printk("interval :%d\n", purb->interval);
+- printk("error_count :%d\n", purb->error_count);
+- printk("context :0x%08lx\n", (unsigned long)purb->context);
+- printk("complete :0x%08lx\n\n", (unsigned long)purb->complete);
+-}
+-
+-static void __dump_in_desc(volatile USB_IN_Desc_t *in)
+-{
+- printk("\nUSB_IN_Desc at 0x%08lx\n", (unsigned long)in);
+- printk(" sw_len : 0x%04x (%d)\n", in->sw_len, in->sw_len);
+- printk(" command : 0x%04x\n", in->command);
+- printk(" next : 0x%08lx\n", in->next);
+- printk(" buf : 0x%08lx\n", in->buf);
+- printk(" hw_len : 0x%04x (%d)\n", in->hw_len, in->hw_len);
+- printk(" status : 0x%04x\n\n", in->status);
+-}
+-
+-static void __dump_sb_desc(volatile USB_SB_Desc_t *sb)
+-{
+- char tt = (sb->command & 0x30) >> 4;
+- char *tt_string;
+-
+- switch (tt) {
+- case 0:
+- tt_string = "zout";
+- break;
+- case 1:
+- tt_string = "in";
+- break;
+- case 2:
+- tt_string = "out";
+- break;
+- case 3:
+- tt_string = "setup";
+- break;
+- default:
+- tt_string = "unknown (weird)";
+- }
+-
+- printk("\n USB_SB_Desc at 0x%08lx\n", (unsigned long)sb);
+- printk(" command : 0x%04x\n", sb->command);
+- printk(" rem : %d\n", (sb->command & 0x3f00) >> 8);
+- printk(" full : %d\n", (sb->command & 0x40) >> 6);
+- printk(" tt : %d (%s)\n", tt, tt_string);
+- printk(" intr : %d\n", (sb->command & 0x8) >> 3);
+- printk(" eot : %d\n", (sb->command & 0x2) >> 1);
+- printk(" eol : %d\n", sb->command & 0x1);
+- printk(" sw_len : 0x%04x (%d)\n", sb->sw_len, sb->sw_len);
+- printk(" next : 0x%08lx\n", sb->next);
+- printk(" buf : 0x%08lx\n\n", sb->buf);
+-}
+-
+-
+-static void __dump_ep_desc(volatile USB_EP_Desc_t *ep)
+-{
+- printk("\nUSB_EP_Desc at 0x%08lx\n", (unsigned long)ep);
+- printk(" command : 0x%04x\n", ep->command);
+- printk(" ep_id : %d\n", (ep->command & 0x1f00) >> 8);
+- printk(" enable : %d\n", (ep->command & 0x10) >> 4);
+- printk(" intr : %d\n", (ep->command & 0x8) >> 3);
+- printk(" eof : %d\n", (ep->command & 0x2) >> 1);
+- printk(" eol : %d\n", ep->command & 0x1);
+- printk(" hw_len : 0x%04x (%d)\n", ep->hw_len, ep->hw_len);
+- printk(" next : 0x%08lx\n", ep->next);
+- printk(" sub : 0x%08lx\n\n", ep->sub);
+-}
+-
+-static inline void __dump_ep_list(int pipe_type)
+-{
+- volatile USB_EP_Desc_t *ep;
+- volatile USB_EP_Desc_t *first_ep;
+- volatile USB_SB_Desc_t *sb;
+-
+- switch (pipe_type)
+- {
+- case PIPE_BULK:
+- first_ep = &TxBulkEPList[0];
+- break;
+- case PIPE_CONTROL:
+- first_ep = &TxCtrlEPList[0];
+- break;
+- case PIPE_INTERRUPT:
+- first_ep = &TxIntrEPList[0];
+- break;
+- case PIPE_ISOCHRONOUS:
+- first_ep = &TxIsocEPList[0];
+- break;
+- default:
+- warn("Cannot dump unknown traffic type");
+- return;
+- }
+- ep = first_ep;
+-
+- printk("\n\nDumping EP list...\n\n");
+-
+- do {
+- __dump_ep_desc(ep);
+- /* Cannot phys_to_virt on 0 as it turns into 80000000, which is != 0. */
+- sb = ep->sub ? phys_to_virt(ep->sub) : 0;
+- while (sb) {
+- __dump_sb_desc(sb);
+- sb = sb->next ? phys_to_virt(sb->next) : 0;
+- }
+- ep = (volatile USB_EP_Desc_t *)(phys_to_virt(ep->next));
+-
+- } while (ep != first_ep);
+-}
+-
+-static inline void __dump_ept_data(int epid)
+-{
+- unsigned long flags;
+- __u32 r_usb_ept_data;
+-
+- if (epid < 0 || epid > 31) {
+- printk("Cannot dump ept data for invalid epid %d\n", epid);
+- return;
+- }
+-
+- save_flags(flags);
+- cli();
+- *R_USB_EPT_INDEX = IO_FIELD(R_USB_EPT_INDEX, value, epid);
+- nop();
+- r_usb_ept_data = *R_USB_EPT_DATA;
+- restore_flags(flags);
+-
+- printk("\nR_USB_EPT_DATA = 0x%x for epid %d :\n", r_usb_ept_data, epid);
+- if (r_usb_ept_data == 0) {
+- /* No need for more detailed printing. */
+- return;
+- }
+- printk(" valid : %d\n", (r_usb_ept_data & 0x80000000) >> 31);
+- printk(" hold : %d\n", (r_usb_ept_data & 0x40000000) >> 30);
+- printk(" error_count_in : %d\n", (r_usb_ept_data & 0x30000000) >> 28);
+- printk(" t_in : %d\n", (r_usb_ept_data & 0x08000000) >> 27);
+- printk(" low_speed : %d\n", (r_usb_ept_data & 0x04000000) >> 26);
+- printk(" port : %d\n", (r_usb_ept_data & 0x03000000) >> 24);
+- printk(" error_code : %d\n", (r_usb_ept_data & 0x00c00000) >> 22);
+- printk(" t_out : %d\n", (r_usb_ept_data & 0x00200000) >> 21);
+- printk(" error_count_out : %d\n", (r_usb_ept_data & 0x00180000) >> 19);
+- printk(" max_len : %d\n", (r_usb_ept_data & 0x0003f800) >> 11);
+- printk(" ep : %d\n", (r_usb_ept_data & 0x00000780) >> 7);
+- printk(" dev : %d\n", (r_usb_ept_data & 0x0000003f));
+-}
+-
+-static inline void __dump_ept_data_list(void)
+-{
+- int i;
+-
+- printk("Dumping the whole R_USB_EPT_DATA list\n");
+-
+- for (i = 0; i < 32; i++) {
+- __dump_ept_data(i);
+- }
+-}
+-#ifdef USB_DEBUG_DESC
+-#define dump_in_desc(...) __dump_in_desc(...)
+-#define dump_sb_desc(...) __dump_sb_desc(...)
+-#define dump_ep_desc(...) __dump_ep_desc(...)
+-#else
+-#define dump_in_desc(...) do {} while (0)
+-#define dump_sb_desc(...) do {} while (0)
+-#define dump_ep_desc(...) do {} while (0)
+-#endif
+-
+-#ifdef USB_DEBUG_URB
+-#define dump_urb(x) __dump_urb(x)
+-#else
+-#define dump_urb(x) do {} while (0)
+-#endif
+-
+-static void init_rx_buffers(void)
+-{
+- int i;
+-
+- DBFENTER;
+-
+- for (i = 0; i < (NBR_OF_RX_DESC - 1); i++) {
+- RxDescList[i].sw_len = RX_DESC_BUF_SIZE;
+- RxDescList[i].command = 0;
+- RxDescList[i].next = virt_to_phys(&RxDescList[i + 1]);
+- RxDescList[i].buf = virt_to_phys(RxBuf + (i * RX_DESC_BUF_SIZE));
+- RxDescList[i].hw_len = 0;
+- RxDescList[i].status = 0;
+-
+- /* DMA IN cache bug. (struct etrax_dma_descr has the same layout as USB_IN_Desc
+- for the relevant fields.) */
+- prepare_rx_descriptor((struct etrax_dma_descr*)&RxDescList[i]);
+-
+- }
+-
+- RxDescList[i].sw_len = RX_DESC_BUF_SIZE;
+- RxDescList[i].command = IO_STATE(USB_IN_command, eol, yes);
+- RxDescList[i].next = virt_to_phys(&RxDescList[0]);
+- RxDescList[i].buf = virt_to_phys(RxBuf + (i * RX_DESC_BUF_SIZE));
+- RxDescList[i].hw_len = 0;
+- RxDescList[i].status = 0;
+-
+- myNextRxDesc = &RxDescList[0];
+- myLastRxDesc = &RxDescList[NBR_OF_RX_DESC - 1];
+- myPrevRxDesc = &RxDescList[NBR_OF_RX_DESC - 1];
+-
+- *R_DMA_CH9_FIRST = virt_to_phys(myNextRxDesc);
+- *R_DMA_CH9_CMD = IO_STATE(R_DMA_CH9_CMD, cmd, start);
+-
+- DBFEXIT;
+-}
+-
+-static void init_tx_bulk_ep(void)
+-{
+- int i;
+-
+- DBFENTER;
+-
+- for (i = 0; i < (NBR_OF_EPIDS - 1); i++) {
+- CHECK_ALIGN(&TxBulkEPList[i]);
+- TxBulkEPList[i].hw_len = 0;
+- TxBulkEPList[i].command = IO_FIELD(USB_EP_command, epid, i);
+- TxBulkEPList[i].sub = 0;
+- TxBulkEPList[i].next = virt_to_phys(&TxBulkEPList[i + 1]);
+-
+- /* Initiate two EPs, disabled and with the eol flag set. No need for any
+- preserved epid. */
+-
+- /* The first one has the intr flag set so we get an interrupt when the DMA
+- channel is about to become disabled. */
+- CHECK_ALIGN(&TxBulkDummyEPList[i][0]);
+- TxBulkDummyEPList[i][0].hw_len = 0;
+- TxBulkDummyEPList[i][0].command = (IO_FIELD(USB_EP_command, epid, DUMMY_EPID) |
+- IO_STATE(USB_EP_command, eol, yes) |
+- IO_STATE(USB_EP_command, intr, yes));
+- TxBulkDummyEPList[i][0].sub = 0;
+- TxBulkDummyEPList[i][0].next = virt_to_phys(&TxBulkDummyEPList[i][1]);
+-
+- /* The second one. */
+- CHECK_ALIGN(&TxBulkDummyEPList[i][1]);
+- TxBulkDummyEPList[i][1].hw_len = 0;
+- TxBulkDummyEPList[i][1].command = (IO_FIELD(USB_EP_command, epid, DUMMY_EPID) |
+- IO_STATE(USB_EP_command, eol, yes));
+- TxBulkDummyEPList[i][1].sub = 0;
+- /* The last dummy's next pointer is the same as the current EP's next pointer. */
+- TxBulkDummyEPList[i][1].next = virt_to_phys(&TxBulkEPList[i + 1]);
+- }
+-
+- /* Configure the last one. */
+- CHECK_ALIGN(&TxBulkEPList[i]);
+- TxBulkEPList[i].hw_len = 0;
+- TxBulkEPList[i].command = (IO_STATE(USB_EP_command, eol, yes) |
+- IO_FIELD(USB_EP_command, epid, i));
+- TxBulkEPList[i].sub = 0;
+- TxBulkEPList[i].next = virt_to_phys(&TxBulkEPList[0]);
+-
+- /* No need configuring dummy EPs for the last one as it will never be used for
+- bulk traffic (i == INVALD_EPID at this point). */
+-
+- /* Set up to start on the last EP so we will enable it when inserting traffic
+- for the first time (imitating the situation where the DMA has stopped
+- because there was no more traffic). */
+- *R_DMA_CH8_SUB0_EP = virt_to_phys(&TxBulkEPList[i]);
+- /* No point in starting the bulk channel yet.
+- *R_DMA_CH8_SUB0_CMD = IO_STATE(R_DMA_CH8_SUB0_CMD, cmd, start); */
+- DBFEXIT;
+-}
+-
+-static void init_tx_ctrl_ep(void)
+-{
+- int i;
+-
+- DBFENTER;
+-
+- for (i = 0; i < (NBR_OF_EPIDS - 1); i++) {
+- CHECK_ALIGN(&TxCtrlEPList[i]);
+- TxCtrlEPList[i].hw_len = 0;
+- TxCtrlEPList[i].command = IO_FIELD(USB_EP_command, epid, i);
+- TxCtrlEPList[i].sub = 0;
+- TxCtrlEPList[i].next = virt_to_phys(&TxCtrlEPList[i + 1]);
+- }
+-
+- CHECK_ALIGN(&TxCtrlEPList[i]);
+- TxCtrlEPList[i].hw_len = 0;
+- TxCtrlEPList[i].command = (IO_STATE(USB_EP_command, eol, yes) |
+- IO_FIELD(USB_EP_command, epid, i));
+-
+- TxCtrlEPList[i].sub = 0;
+- TxCtrlEPList[i].next = virt_to_phys(&TxCtrlEPList[0]);
+-
+- *R_DMA_CH8_SUB1_EP = virt_to_phys(&TxCtrlEPList[0]);
+- *R_DMA_CH8_SUB1_CMD = IO_STATE(R_DMA_CH8_SUB1_CMD, cmd, start);
+-
+- DBFEXIT;
+-}
+-
+-
+-static void init_tx_intr_ep(void)
+-{
+- int i;
+-
+- DBFENTER;
+-
+- /* Read comment at zout_buffer declaration for an explanation to this. */
+- TxIntrSB_zout.sw_len = 1;
+- TxIntrSB_zout.next = 0;
+- TxIntrSB_zout.buf = virt_to_phys(&zout_buffer[0]);
+- TxIntrSB_zout.command = (IO_FIELD(USB_SB_command, rem, 0) |
+- IO_STATE(USB_SB_command, tt, zout) |
+- IO_STATE(USB_SB_command, full, yes) |
+- IO_STATE(USB_SB_command, eot, yes) |
+- IO_STATE(USB_SB_command, eol, yes));
+-
+- for (i = 0; i < (MAX_INTR_INTERVAL - 1); i++) {
+- CHECK_ALIGN(&TxIntrEPList[i]);
+- TxIntrEPList[i].hw_len = 0;
+- TxIntrEPList[i].command =
+- (IO_STATE(USB_EP_command, eof, yes) |
+- IO_STATE(USB_EP_command, enable, yes) |
+- IO_FIELD(USB_EP_command, epid, INVALID_EPID));
+- TxIntrEPList[i].sub = virt_to_phys(&TxIntrSB_zout);
+- TxIntrEPList[i].next = virt_to_phys(&TxIntrEPList[i + 1]);
+- }
+-
+- CHECK_ALIGN(&TxIntrEPList[i]);
+- TxIntrEPList[i].hw_len = 0;
+- TxIntrEPList[i].command =
+- (IO_STATE(USB_EP_command, eof, yes) |
+- IO_STATE(USB_EP_command, eol, yes) |
+- IO_STATE(USB_EP_command, enable, yes) |
+- IO_FIELD(USB_EP_command, epid, INVALID_EPID));
+- TxIntrEPList[i].sub = virt_to_phys(&TxIntrSB_zout);
+- TxIntrEPList[i].next = virt_to_phys(&TxIntrEPList[0]);
+-
+- *R_DMA_CH8_SUB2_EP = virt_to_phys(&TxIntrEPList[0]);
+- *R_DMA_CH8_SUB2_CMD = IO_STATE(R_DMA_CH8_SUB2_CMD, cmd, start);
+- DBFEXIT;
+-}
+-
+-static void init_tx_isoc_ep(void)
+-{
+- int i;
+-
+- DBFENTER;
+-
+- /* Read comment at zout_buffer declaration for an explanation to this. */
+- TxIsocSB_zout.sw_len = 1;
+- TxIsocSB_zout.next = 0;
+- TxIsocSB_zout.buf = virt_to_phys(&zout_buffer[0]);
+- TxIsocSB_zout.command = (IO_FIELD(USB_SB_command, rem, 0) |
+- IO_STATE(USB_SB_command, tt, zout) |
+- IO_STATE(USB_SB_command, full, yes) |
+- IO_STATE(USB_SB_command, eot, yes) |
+- IO_STATE(USB_SB_command, eol, yes));
+-
+- /* The last isochronous EP descriptor is a dummy. */
+-
+- for (i = 0; i < (NBR_OF_EPIDS - 1); i++) {
+- CHECK_ALIGN(&TxIsocEPList[i]);
+- TxIsocEPList[i].hw_len = 0;
+- TxIsocEPList[i].command = IO_FIELD(USB_EP_command, epid, i);
+- TxIsocEPList[i].sub = 0;
+- TxIsocEPList[i].next = virt_to_phys(&TxIsocEPList[i + 1]);
+- }
+-
+- CHECK_ALIGN(&TxIsocEPList[i]);
+- TxIsocEPList[i].hw_len = 0;
+-
+- /* Must enable the last EP descr to get eof interrupt. */
+- TxIsocEPList[i].command = (IO_STATE(USB_EP_command, enable, yes) |
+- IO_STATE(USB_EP_command, eof, yes) |
+- IO_STATE(USB_EP_command, eol, yes) |
+- IO_FIELD(USB_EP_command, epid, INVALID_EPID));
+- TxIsocEPList[i].sub = virt_to_phys(&TxIsocSB_zout);
+- TxIsocEPList[i].next = virt_to_phys(&TxIsocEPList[0]);
+-
+- *R_DMA_CH8_SUB3_EP = virt_to_phys(&TxIsocEPList[0]);
+- *R_DMA_CH8_SUB3_CMD = IO_STATE(R_DMA_CH8_SUB3_CMD, cmd, start);
+-
+- DBFEXIT;
+-}
+-
+-static void etrax_usb_unlink_intr_urb(struct urb *urb)
+-{
+- volatile USB_EP_Desc_t *first_ep; /* First EP in the list. */
+- volatile USB_EP_Desc_t *curr_ep; /* Current EP, the iterator. */
+- volatile USB_EP_Desc_t *next_ep; /* The EP after current. */
+- volatile USB_EP_Desc_t *unlink_ep; /* The one we should remove from the list. */
+-
+- int epid;
+-
+- /* Read 8.8.4 in Designer's Reference, "Removing an EP Descriptor from the List". */
+-
+- DBFENTER;
+-
+- epid = ((etrax_urb_priv_t *)urb->hcpriv)->epid;
+-
+- first_ep = &TxIntrEPList[0];
+- curr_ep = first_ep;
+-
+-
+- /* Note that this loop removes all EP descriptors with this epid. This assumes
+- that all EP descriptors belong to the one and only urb for this epid. */
+-
+- do {
+- next_ep = (USB_EP_Desc_t *)phys_to_virt(curr_ep->next);
+-
+- if (IO_EXTRACT(USB_EP_command, epid, next_ep->command) == epid) {
+-
+- dbg_intr("Found EP to unlink for epid %d", epid);
+-
+- /* This is the one we should unlink. */
+- unlink_ep = next_ep;
+-
+- /* Actually unlink the EP from the DMA list. */
+- curr_ep->next = unlink_ep->next;
+-
+- /* Wait until the DMA is no longer at this descriptor. */
+- while (*R_DMA_CH8_SUB2_EP == virt_to_phys(unlink_ep));
+-
+- /* Now we are free to remove it and its SB descriptor.
+- Note that it is assumed here that there is only one sb in the
+- sb list for this ep. */
+- kmem_cache_free(usb_desc_cache, phys_to_virt(unlink_ep->sub));
+- kmem_cache_free(usb_desc_cache, (USB_EP_Desc_t *)unlink_ep);
+- }
+-
+- curr_ep = phys_to_virt(curr_ep->next);
+-
+- } while (curr_ep != first_ep);
+- urb->hcpriv = NULL;
+-}
+-
+-void etrax_usb_do_intr_recover(int epid)
+-{
+- USB_EP_Desc_t *first_ep, *tmp_ep;
+-
+- DBFENTER;
+-
+- first_ep = (USB_EP_Desc_t *)phys_to_virt(*R_DMA_CH8_SUB2_EP);
+- tmp_ep = first_ep;
+-
+- /* What this does is simply to walk the list of interrupt
+- ep descriptors and enable those that are disabled. */
+-
+- do {
+- if (IO_EXTRACT(USB_EP_command, epid, tmp_ep->command) == epid &&
+- !(tmp_ep->command & IO_MASK(USB_EP_command, enable))) {
+- tmp_ep->command |= IO_STATE(USB_EP_command, enable, yes);
+- }
+-
+- tmp_ep = (USB_EP_Desc_t *)phys_to_virt(tmp_ep->next);
+-
+- } while (tmp_ep != first_ep);
+-
+-
+- DBFEXIT;
+-}
+-
+-static int etrax_rh_unlink_urb (struct urb *urb)
+-{
+- etrax_hc_t *hc;
+-
+- DBFENTER;
+-
+- hc = urb->dev->bus->hcpriv;
+-
+- if (hc->rh.urb == urb) {
+- hc->rh.send = 0;
+- del_timer(&hc->rh.rh_int_timer);
+- }
+-
+- DBFEXIT;
+- return 0;
+-}
+-
+-static void etrax_rh_send_irq(struct urb *urb)
+-{
+- __u16 data = 0;
+- etrax_hc_t *hc = urb->dev->bus->hcpriv;
+- DBFENTER;
+-
+-/*
+- dbg_rh("R_USB_FM_NUMBER : 0x%08X", *R_USB_FM_NUMBER);
+- dbg_rh("R_USB_FM_REMAINING: 0x%08X", *R_USB_FM_REMAINING);
+-*/
+-
+- data |= (hc->rh.wPortChange_1) ? (1 << 1) : 0;
+- data |= (hc->rh.wPortChange_2) ? (1 << 2) : 0;
+-
+- *((__u16 *)urb->transfer_buffer) = cpu_to_le16(data);
+- /* FIXME: Why is actual_length set to 1 when data is 2 bytes?
+- Since only 1 byte is used, why not declare data as __u8? */
+- urb->actual_length = 1;
+- urb->status = 0;
+-
+- if (hc->rh.send && urb->complete) {
+- dbg_rh("wPortChange_1: 0x%04X", hc->rh.wPortChange_1);
+- dbg_rh("wPortChange_2: 0x%04X", hc->rh.wPortChange_2);
+-
+- urb->complete(urb, NULL);
+- }
+-
+- DBFEXIT;
+-}
+-
+-static void etrax_rh_init_int_timer(struct urb *urb)
+-{
+- etrax_hc_t *hc;
+-
+- DBFENTER;
+-
+- hc = urb->dev->bus->hcpriv;
+- hc->rh.interval = urb->interval;
+- init_timer(&hc->rh.rh_int_timer);
+- hc->rh.rh_int_timer.function = etrax_rh_int_timer_do;
+- hc->rh.rh_int_timer.data = (unsigned long)urb;
+- /* FIXME: Is the jiffies resolution enough? All intervals < 10 ms will be mapped
+- to 0, and the rest to the nearest lower 10 ms. */
+- hc->rh.rh_int_timer.expires = jiffies + ((HZ * hc->rh.interval) / 1000);
+- add_timer(&hc->rh.rh_int_timer);
+-
+- DBFEXIT;
+-}
+-
+-static void etrax_rh_int_timer_do(unsigned long ptr)
+-{
+- struct urb *urb;
+- etrax_hc_t *hc;
+-
+- DBFENTER;
+-
+- urb = (struct urb*)ptr;
+- hc = urb->dev->bus->hcpriv;
+-
+- if (hc->rh.send) {
+- etrax_rh_send_irq(urb);
+- }
+-
+- DBFEXIT;
+-}
+-
+-static int etrax_usb_setup_epid(struct urb *urb)
+-{
+- int epid;
+- char devnum, endpoint, out_traffic, slow;
+- int maxlen;
+- unsigned long flags;
+-
+- DBFENTER;
+-
+- epid = etrax_usb_lookup_epid(urb);
+- if ((epid != -1)){
+- /* An epid that fits this urb has been found. */
+- DBFEXIT;
+- return epid;
+- }
+-
+- /* We must find and initiate a new epid for this urb. */
+- epid = etrax_usb_allocate_epid();
+-
+- if (epid == -1) {
+- /* Failed to allocate a new epid. */
+- DBFEXIT;
+- return epid;
+- }
+-
+- /* We now have a new epid to use. Initiate it. */
+- set_bit(epid, (void *)&epid_usage_bitmask);
+-
+- devnum = usb_pipedevice(urb->pipe);
+- endpoint = usb_pipeendpoint(urb->pipe);
+- slow = usb_pipeslow(urb->pipe);
+- maxlen = usb_maxpacket(urb->dev, urb->pipe, usb_pipeout(urb->pipe));
+- if (usb_pipetype(urb->pipe) == PIPE_CONTROL) {
+- /* We want both IN and OUT control traffic to be put on the same EP/SB list. */
+- out_traffic = 1;
+- } else {
+- out_traffic = usb_pipeout(urb->pipe);
+- }
+-
+- save_flags(flags);
+- cli();
+-
+- *R_USB_EPT_INDEX = IO_FIELD(R_USB_EPT_INDEX, value, epid);
+- nop();
+-
+- if (usb_pipetype(urb->pipe) == PIPE_ISOCHRONOUS) {
+- *R_USB_EPT_DATA_ISO = IO_STATE(R_USB_EPT_DATA_ISO, valid, yes) |
+- /* FIXME: Change any to the actual port? */
+- IO_STATE(R_USB_EPT_DATA_ISO, port, any) |
+- IO_FIELD(R_USB_EPT_DATA_ISO, max_len, maxlen) |
+- IO_FIELD(R_USB_EPT_DATA_ISO, ep, endpoint) |
+- IO_FIELD(R_USB_EPT_DATA_ISO, dev, devnum);
+- } else {
+- *R_USB_EPT_DATA = IO_STATE(R_USB_EPT_DATA, valid, yes) |
+- IO_FIELD(R_USB_EPT_DATA, low_speed, slow) |
+- /* FIXME: Change any to the actual port? */
+- IO_STATE(R_USB_EPT_DATA, port, any) |
+- IO_FIELD(R_USB_EPT_DATA, max_len, maxlen) |
+- IO_FIELD(R_USB_EPT_DATA, ep, endpoint) |
+- IO_FIELD(R_USB_EPT_DATA, dev, devnum);
+- }
+-
+- restore_flags(flags);
+-
+- if (out_traffic) {
+- set_bit(epid, (void *)&epid_out_traffic);
+- } else {
+- clear_bit(epid, (void *)&epid_out_traffic);
+- }
+-
+- dbg_epid("Setting up epid %d with devnum %d, endpoint %d and max_len %d (%s)",
+- epid, devnum, endpoint, maxlen, out_traffic ? "OUT" : "IN");
+-
+- DBFEXIT;
+- return epid;
+-}
+-
+-static void etrax_usb_free_epid(int epid)
+-{
+- unsigned long flags;
+-
+- DBFENTER;
+-
+- if (!test_bit(epid, (void *)&epid_usage_bitmask)) {
+- warn("Trying to free unused epid %d", epid);
+- DBFEXIT;
+- return;
+- }
+-
+- save_flags(flags);
+- cli();
+-
+- *R_USB_EPT_INDEX = IO_FIELD(R_USB_EPT_INDEX, value, epid);
+- nop();
+- while (*R_USB_EPT_DATA & IO_MASK(R_USB_EPT_DATA, hold));
+- /* This will, among other things, set the valid field to 0. */
+- *R_USB_EPT_DATA = 0;
+- restore_flags(flags);
+-
+- clear_bit(epid, (void *)&epid_usage_bitmask);
+-
+-
+- dbg_epid("Freed epid %d", epid);
+-
+- DBFEXIT;
+-}
+-
+-static int etrax_usb_lookup_epid(struct urb *urb)
+-{
+- int i;
+- __u32 data;
+- char devnum, endpoint, slow, out_traffic;
+- int maxlen;
+- unsigned long flags;
+-
+- DBFENTER;
+-
+- devnum = usb_pipedevice(urb->pipe);
+- endpoint = usb_pipeendpoint(urb->pipe);
+- slow = usb_pipeslow(urb->pipe);
+- maxlen = usb_maxpacket(urb->dev, urb->pipe, usb_pipeout(urb->pipe));
+- if (usb_pipetype(urb->pipe) == PIPE_CONTROL) {
+- /* We want both IN and OUT control traffic to be put on the same EP/SB list. */
+- out_traffic = 1;
+- } else {
+- out_traffic = usb_pipeout(urb->pipe);
+- }
+-
+- /* Step through att epids. */
+- for (i = 0; i < NBR_OF_EPIDS; i++) {
+- if (test_bit(i, (void *)&epid_usage_bitmask) &&
+- test_bit(i, (void *)&epid_out_traffic) == out_traffic) {
+-
+- save_flags(flags);
+- cli();
+- *R_USB_EPT_INDEX = IO_FIELD(R_USB_EPT_INDEX, value, i);
+- nop();
+-
+- if (usb_pipetype(urb->pipe) == PIPE_ISOCHRONOUS) {
+- data = *R_USB_EPT_DATA_ISO;
+- restore_flags(flags);
+-
+- if ((IO_MASK(R_USB_EPT_DATA_ISO, valid) & data) &&
+- (IO_EXTRACT(R_USB_EPT_DATA_ISO, dev, data) == devnum) &&
+- (IO_EXTRACT(R_USB_EPT_DATA_ISO, ep, data) == endpoint) &&
+- (IO_EXTRACT(R_USB_EPT_DATA_ISO, max_len, data) == maxlen)) {
+- dbg_epid("Found epid %d for devnum %d, endpoint %d (%s)",
+- i, devnum, endpoint, out_traffic ? "OUT" : "IN");
+- DBFEXIT;
+- return i;
+- }
+- } else {
+- data = *R_USB_EPT_DATA;
+- restore_flags(flags);
+-
+- if ((IO_MASK(R_USB_EPT_DATA, valid) & data) &&
+- (IO_EXTRACT(R_USB_EPT_DATA, dev, data) == devnum) &&
+- (IO_EXTRACT(R_USB_EPT_DATA, ep, data) == endpoint) &&
+- (IO_EXTRACT(R_USB_EPT_DATA, low_speed, data) == slow) &&
+- (IO_EXTRACT(R_USB_EPT_DATA, max_len, data) == maxlen)) {
+- dbg_epid("Found epid %d for devnum %d, endpoint %d (%s)",
+- i, devnum, endpoint, out_traffic ? "OUT" : "IN");
+- DBFEXIT;
+- return i;
+- }
+- }
+- }
+- }
+-
+- DBFEXIT;
+- return -1;
+-}
+-
+-static int etrax_usb_allocate_epid(void)
+-{
+- int i;
+-
+- DBFENTER;
+-
+- for (i = 0; i < NBR_OF_EPIDS; i++) {
+- if (!test_bit(i, (void *)&epid_usage_bitmask)) {
+- dbg_epid("Found free epid %d", i);
+- DBFEXIT;
+- return i;
+- }
+- }
+-
+- dbg_epid("Found no free epids");
+- DBFEXIT;
+- return -1;
+-}
+-
+-static int etrax_usb_submit_urb(struct urb *urb, unsigned mem_flags)
+-{
+- etrax_hc_t *hc;
+- int ret = -EINVAL;
+-
+- DBFENTER;
+-
+- if (!urb->dev || !urb->dev->bus) {
+- return -ENODEV;
+- }
+- if (usb_maxpacket(urb->dev, urb->pipe, usb_pipeout(urb->pipe)) <= 0) {
+- info("Submit urb to pipe with maxpacketlen 0, pipe 0x%X\n", urb->pipe);
+- return -EMSGSIZE;
+- }
+-
+- if (urb->timeout) {
+- /* FIXME. */
+- warn("urb->timeout specified, ignoring.");
+- }
+-
+- hc = (etrax_hc_t*)urb->dev->bus->hcpriv;
+-
+- if (usb_pipedevice(urb->pipe) == hc->rh.devnum) {
+- /* This request is for the Virtual Root Hub. */
+- ret = etrax_rh_submit_urb(urb);
+-
+- } else if (usb_pipetype(urb->pipe) == PIPE_BULK) {
+-
+- ret = etrax_usb_submit_bulk_urb(urb);
+-
+- } else if (usb_pipetype(urb->pipe) == PIPE_CONTROL) {
+-
+- ret = etrax_usb_submit_ctrl_urb(urb);
+-
+- } else if (usb_pipetype(urb->pipe) == PIPE_INTERRUPT) {
+- int bustime;
+-
+- if (urb->bandwidth == 0) {
+- bustime = usb_check_bandwidth(urb->dev, urb);
+- if (bustime < 0) {
+- ret = bustime;
+- } else {
+- ret = etrax_usb_submit_intr_urb(urb);
+- if (ret == 0)
+- usb_claim_bandwidth(urb->dev, urb, bustime, 0);
+- }
+- } else {
+- /* Bandwidth already set. */
+- ret = etrax_usb_submit_intr_urb(urb);
+- }
+-
+- } else if (usb_pipetype(urb->pipe) == PIPE_ISOCHRONOUS) {
+- int bustime;
+-
+- if (urb->bandwidth == 0) {
+- bustime = usb_check_bandwidth(urb->dev, urb);
+- if (bustime < 0) {
+- ret = bustime;
+- } else {
+- ret = etrax_usb_submit_isoc_urb(urb);
+- if (ret == 0)
+- usb_claim_bandwidth(urb->dev, urb, bustime, 0);
+- }
+- } else {
+- /* Bandwidth already set. */
+- ret = etrax_usb_submit_isoc_urb(urb);
+- }
+- }
+-
+- DBFEXIT;
+-
+- if (ret != 0)
+- printk("Submit URB error %d\n", ret);
+-
+- return ret;
+-}
+-
+-static int etrax_usb_unlink_urb(struct urb *urb, int status)
+-{
+- etrax_hc_t *hc;
+- etrax_urb_priv_t *urb_priv;
+- int epid;
+- unsigned int flags;
+-
+- DBFENTER;
+-
+- if (!urb) {
+- return -EINVAL;
+- }
+-
+- /* Disable interrupts here since a descriptor interrupt for the isoc epid
+- will modify the sb list. This could possibly be done more granular, but
+- unlink_urb should not be used frequently anyway.
+- */
+-
+- save_flags(flags);
+- cli();
+-
+- if (!urb->dev || !urb->dev->bus) {
+- restore_flags(flags);
+- return -ENODEV;
+- }
+- if (!urb->hcpriv) {
+- /* This happens if a device driver calls unlink on an urb that
+- was never submitted (lazy driver) or if the urb was completed
+- while unlink was being called. */
+- restore_flags(flags);
+- return 0;
+- }
+- if (urb->transfer_flags & URB_ASYNC_UNLINK) {
+- /* FIXME. */
+- /* If URB_ASYNC_UNLINK is set:
+- unlink
+- move to a separate urb list
+- call complete at next sof with ECONNRESET
+-
+- If not:
+- wait 1 ms
+- unlink
+- call complete with ENOENT
+- */
+- warn("URB_ASYNC_UNLINK set, ignoring.");
+- }
+-
+- /* One might think that urb->status = -EINPROGRESS would be a requirement for unlinking,
+- but that doesn't work for interrupt and isochronous traffic since they are completed
+- repeatedly, and urb->status is set then. That may in itself be a bug though. */
+-
+- hc = urb->dev->bus->hcpriv;
+- urb_priv = (etrax_urb_priv_t *)urb->hcpriv;
+- epid = urb_priv->epid;
+-
+- /* Set the urb status (synchronous unlink). */
+- urb->status = -ENOENT;
+- urb_priv->urb_state = UNLINK;
+-
+- if (usb_pipedevice(urb->pipe) == hc->rh.devnum) {
+- int ret;
+- ret = etrax_rh_unlink_urb(urb);
+- DBFEXIT;
+- restore_flags(flags);
+- return ret;
+-
+- } else if (usb_pipetype(urb->pipe) == PIPE_BULK) {
+-
+- dbg_bulk("Unlink of bulk urb (0x%lx)", (unsigned long)urb);
+-
+- if (TxBulkEPList[epid].command & IO_MASK(USB_EP_command, enable)) {
+- /* The EP was enabled, disable it and wait. */
+- TxBulkEPList[epid].command &= ~IO_MASK(USB_EP_command, enable);
+-
+- /* Ah, the luxury of busy-wait. */
+- while (*R_DMA_CH8_SUB0_EP == virt_to_phys(&TxBulkEPList[epid]));
+- }
+- /* Kicking dummy list out of the party. */
+- TxBulkEPList[epid].next = virt_to_phys(&TxBulkEPList[(epid + 1) % NBR_OF_EPIDS]);
+-
+- } else if (usb_pipetype(urb->pipe) == PIPE_CONTROL) {
+-
+- dbg_ctrl("Unlink of ctrl urb (0x%lx)", (unsigned long)urb);
+-
+- if (TxCtrlEPList[epid].command & IO_MASK(USB_EP_command, enable)) {
+- /* The EP was enabled, disable it and wait. */
+- TxCtrlEPList[epid].command &= ~IO_MASK(USB_EP_command, enable);
+-
+- /* Ah, the luxury of busy-wait. */
+- while (*R_DMA_CH8_SUB1_EP == virt_to_phys(&TxCtrlEPList[epid]));
+- }
+-
+- } else if (usb_pipetype(urb->pipe) == PIPE_INTERRUPT) {
+-
+- dbg_intr("Unlink of intr urb (0x%lx)", (unsigned long)urb);
+-
+- /* Separate function because it's a tad more complicated. */
+- etrax_usb_unlink_intr_urb(urb);
+-
+- } else if (usb_pipetype(urb->pipe) == PIPE_ISOCHRONOUS) {
+-
+- dbg_isoc("Unlink of isoc urb (0x%lx)", (unsigned long)urb);
+-
+- if (TxIsocEPList[epid].command & IO_MASK(USB_EP_command, enable)) {
+- /* The EP was enabled, disable it and wait. */
+- TxIsocEPList[epid].command &= ~IO_MASK(USB_EP_command, enable);
+-
+- /* Ah, the luxury of busy-wait. */
+- while (*R_DMA_CH8_SUB3_EP == virt_to_phys(&TxIsocEPList[epid]));
+- }
+- }
+-
+- /* Note that we need to remove the urb from the urb list *before* removing its SB
+- descriptors. (This means that the isoc eof handler might get a null urb when we
+- are unlinking the last urb.) */
+-
+- if (usb_pipetype(urb->pipe) == PIPE_BULK) {
+-
+- urb_list_del(urb, epid);
+- TxBulkEPList[epid].sub = 0;
+- etrax_remove_from_sb_list(urb);
+-
+- } else if (usb_pipetype(urb->pipe) == PIPE_CONTROL) {
+-
+- urb_list_del(urb, epid);
+- TxCtrlEPList[epid].sub = 0;
+- etrax_remove_from_sb_list(urb);
+-
+- } else if (usb_pipetype(urb->pipe) == PIPE_INTERRUPT) {
+-
+- urb_list_del(urb, epid);
+- /* Sanity check (should never happen). */
+- assert(urb_list_empty(epid));
+-
+- /* Release allocated bandwidth. */
+- usb_release_bandwidth(urb->dev, urb, 0);
+-
+- } else if (usb_pipetype(urb->pipe) == PIPE_ISOCHRONOUS) {
+-
+- if (usb_pipeout(urb->pipe)) {
+-
+- USB_SB_Desc_t *iter_sb, *prev_sb, *next_sb;
+-
+- if (__urb_list_entry(urb, epid)) {
+-
+- urb_list_del(urb, epid);
+- iter_sb = TxIsocEPList[epid].sub ? phys_to_virt(TxIsocEPList[epid].sub) : 0;
+- prev_sb = 0;
+- while (iter_sb && (iter_sb != urb_priv->first_sb)) {
+- prev_sb = iter_sb;
+- iter_sb = iter_sb->next ? phys_to_virt(iter_sb->next) : 0;
+- }
+-
+- if (iter_sb == 0) {
+- /* Unlink of the URB currently being transmitted. */
+- prev_sb = 0;
+- iter_sb = TxIsocEPList[epid].sub ? phys_to_virt(TxIsocEPList[epid].sub) : 0;
+- }
+-
+- while (iter_sb && (iter_sb != urb_priv->last_sb)) {
+- iter_sb = iter_sb->next ? phys_to_virt(iter_sb->next) : 0;
+- }
+- if (iter_sb) {
+- next_sb = iter_sb->next ? phys_to_virt(iter_sb->next) : 0;
+- } else {
+- /* This should only happen if the DMA has completed
+- processing the SB list for this EP while interrupts
+- are disabled. */
+- dbg_isoc("Isoc urb not found, already sent?");
+- next_sb = 0;
+- }
+- if (prev_sb) {
+- prev_sb->next = next_sb ? virt_to_phys(next_sb) : 0;
+- } else {
+- TxIsocEPList[epid].sub = next_sb ? virt_to_phys(next_sb) : 0;
+- }
+-
+- etrax_remove_from_sb_list(urb);
+- if (urb_list_empty(epid)) {
+- TxIsocEPList[epid].sub = 0;
+- dbg_isoc("Last isoc out urb epid %d", epid);
+- } else if (next_sb || prev_sb) {
+- dbg_isoc("Re-enable isoc out epid %d", epid);
+-
+- TxIsocEPList[epid].hw_len = 0;
+- TxIsocEPList[epid].command |= IO_STATE(USB_EP_command, enable, yes);
+- } else {
+- TxIsocEPList[epid].sub = 0;
+- dbg_isoc("URB list non-empty and no SB list, EP disabled");
+- }
+- } else {
+- dbg_isoc("Urb 0x%p not found, completed already?", urb);
+- }
+- } else {
+-
+- urb_list_del(urb, epid);
+-
+- /* For in traffic there is only one SB descriptor for each EP even
+- though there may be several urbs (all urbs point at the same SB). */
+- if (urb_list_empty(epid)) {
+- /* No more urbs, remove the SB. */
+- TxIsocEPList[epid].sub = 0;
+- etrax_remove_from_sb_list(urb);
+- } else {
+- TxIsocEPList[epid].hw_len = 0;
+- TxIsocEPList[epid].command |= IO_STATE(USB_EP_command, enable, yes);
+- }
+- }
+- /* Release allocated bandwidth. */
+- usb_release_bandwidth(urb->dev, urb, 1);
+- }
+- /* Free the epid if urb list is empty. */
+- if (urb_list_empty(epid)) {
+- etrax_usb_free_epid(epid);
+- }
+- restore_flags(flags);
+-
+- /* Must be done before calling completion handler. */
+- kfree(urb_priv);
+- urb->hcpriv = 0;
+-
+- if (urb->complete) {
+- urb->complete(urb, NULL);
+- }
+-
+- DBFEXIT;
+- return 0;
+-}
+-
+-static int etrax_usb_get_frame_number(struct usb_device *usb_dev)
+-{
+- DBFENTER;
+- DBFEXIT;
+- return (*R_USB_FM_NUMBER & 0x7ff);
+-}
+-
+-static irqreturn_t etrax_usb_tx_interrupt(int irq, void *vhc)
+-{
+- DBFENTER;
+-
+- /* This interrupt handler could be used when unlinking EP descriptors. */
+-
+- if (*R_IRQ_READ2 & IO_MASK(R_IRQ_READ2, dma8_sub0_descr)) {
+- USB_EP_Desc_t *ep;
+-
+- //dbg_bulk("dma8_sub0_descr (BULK) intr.");
+-
+- /* It should be safe clearing the interrupt here, since we don't expect to get a new
+- one until we restart the bulk channel. */
+- *R_DMA_CH8_SUB0_CLR_INTR = IO_STATE(R_DMA_CH8_SUB0_CLR_INTR, clr_descr, do);
+-
+- /* Wait while the DMA is running (though we don't expect it to be). */
+- while (*R_DMA_CH8_SUB0_CMD & IO_MASK(R_DMA_CH8_SUB0_CMD, cmd));
+-
+- /* Advance the DMA to the next EP descriptor. */
+- ep = (USB_EP_Desc_t *)phys_to_virt(*R_DMA_CH8_SUB0_EP);
+-
+- //dbg_bulk("descr intr: DMA is at 0x%lx", (unsigned long)ep);
+-
+- /* ep->next is already a physical address; no need for a virt_to_phys. */
+- *R_DMA_CH8_SUB0_EP = ep->next;
+-
+- /* Start the DMA bulk channel again. */
+- *R_DMA_CH8_SUB0_CMD = IO_STATE(R_DMA_CH8_SUB0_CMD, cmd, start);
+- }
+- if (*R_IRQ_READ2 & IO_MASK(R_IRQ_READ2, dma8_sub1_descr)) {
+- struct urb *urb;
+- int epid;
+- etrax_urb_priv_t *urb_priv;
+- unsigned long int flags;
+-
+- dbg_ctrl("dma8_sub1_descr (CTRL) intr.");
+- *R_DMA_CH8_SUB1_CLR_INTR = IO_STATE(R_DMA_CH8_SUB1_CLR_INTR, clr_descr, do);
+-
+- /* The complete callback gets called so we cli. */
+- save_flags(flags);
+- cli();
+-
+- for (epid = 0; epid < NBR_OF_EPIDS - 1; epid++) {
+- if ((TxCtrlEPList[epid].sub == 0) ||
+- (epid == DUMMY_EPID) ||
+- (epid == INVALID_EPID)) {
+- /* Nothing here to see. */
+- continue;
+- }
+-
+- /* Get the first urb (if any). */
+- urb = urb_list_first(epid);
+-
+- if (urb) {
+-
+- /* Sanity check. */
+- assert(usb_pipetype(urb->pipe) == PIPE_CONTROL);
+-
+- urb_priv = (etrax_urb_priv_t *)urb->hcpriv;
+- assert(urb_priv);
+-
+- if (urb_priv->urb_state == WAITING_FOR_DESCR_INTR) {
+- assert(!(TxCtrlEPList[urb_priv->epid].command & IO_MASK(USB_EP_command, enable)));
+-
+- etrax_usb_complete_urb(urb, 0);
+- }
+- }
+- }
+- restore_flags(flags);
+- }
+- if (*R_IRQ_READ2 & IO_MASK(R_IRQ_READ2, dma8_sub2_descr)) {
+- dbg_intr("dma8_sub2_descr (INTR) intr.");
+- *R_DMA_CH8_SUB2_CLR_INTR = IO_STATE(R_DMA_CH8_SUB2_CLR_INTR, clr_descr, do);
+- }
+- if (*R_IRQ_READ2 & IO_MASK(R_IRQ_READ2, dma8_sub3_descr)) {
+- struct urb *urb;
+- int epid;
+- int epid_done;
+- etrax_urb_priv_t *urb_priv;
+- USB_SB_Desc_t *sb_desc;
+-
+- usb_isoc_complete_data_t *comp_data = NULL;
+-
+- /* One or more isoc out transfers are done. */
+- dbg_isoc("dma8_sub3_descr (ISOC) intr.");
+-
+- /* For each isoc out EP search for the first sb_desc with the intr flag
+- set. This descriptor must be the last packet from an URB. Then
+- traverse the URB list for the EP until the URB with urb_priv->last_sb
+- matching the intr-marked sb_desc is found. All URBs before this have
+- been sent.
+- */
+-
+- for (epid = 0; epid < NBR_OF_EPIDS - 1; epid++) {
+- /* Skip past epids with no SB lists, epids used for in traffic,
+- and special (dummy, invalid) epids. */
+- if ((TxIsocEPList[epid].sub == 0) ||
+- (test_bit(epid, (void *)&epid_out_traffic) == 0) ||
+- (epid == DUMMY_EPID) ||
+- (epid == INVALID_EPID)) {
+- /* Nothing here to see. */
+- continue;
+- }
+- sb_desc = phys_to_virt(TxIsocEPList[epid].sub);
+-
+- /* Find the last descriptor of the currently active URB for this ep.
+- This is the first descriptor in the sub list marked for a descriptor
+- interrupt. */
+- while (sb_desc && !IO_EXTRACT(USB_SB_command, intr, sb_desc->command)) {
+- sb_desc = sb_desc->next ? phys_to_virt(sb_desc->next) : 0;
+- }
+- assert(sb_desc);
+-
+- dbg_isoc("Check epid %d, sub 0x%p, SB 0x%p",
+- epid,
+- phys_to_virt(TxIsocEPList[epid].sub),
+- sb_desc);
+-
+- epid_done = 0;
+-
+- /* Get the first urb (if any). */
+- urb = urb_list_first(epid);
+- assert(urb);
+-
+- while (urb && !epid_done) {
+-
+- /* Sanity check. */
+- assert(usb_pipetype(urb->pipe) == PIPE_ISOCHRONOUS);
+-
+- if (!usb_pipeout(urb->pipe)) {
+- /* descr interrupts are generated only for out pipes. */
+- epid_done = 1;
+- continue;
+- }
+-
+- urb_priv = (etrax_urb_priv_t *)urb->hcpriv;
+- assert(urb_priv);
+-
+- if (sb_desc != urb_priv->last_sb) {
+-
+- /* This urb has been sent. */
+- dbg_isoc("out URB 0x%p sent", urb);
+-
+- urb_priv->urb_state = TRANSFER_DONE;
+-
+- } else if ((sb_desc == urb_priv->last_sb) &&
+- !(TxIsocEPList[epid].command & IO_MASK(USB_EP_command, enable))) {
+-
+- assert((sb_desc->command & IO_MASK(USB_SB_command, eol)) == IO_STATE(USB_SB_command, eol, yes));
+- assert(sb_desc->next == 0);
+-
+- dbg_isoc("out URB 0x%p last in list, epid disabled", urb);
+- TxIsocEPList[epid].sub = 0;
+- TxIsocEPList[epid].hw_len = 0;
+- urb_priv->urb_state = TRANSFER_DONE;
+-
+- epid_done = 1;
+-
+- } else {
+- epid_done = 1;
+- }
+- if (!epid_done) {
+- urb = urb_list_next(urb, epid);
+- }
+- }
+-
+- }
+-
+- *R_DMA_CH8_SUB3_CLR_INTR = IO_STATE(R_DMA_CH8_SUB3_CLR_INTR, clr_descr, do);
+-
+- comp_data = (usb_isoc_complete_data_t*)kmem_cache_alloc(isoc_compl_cache, GFP_ATOMIC);
+- assert(comp_data != NULL);
+-
+- INIT_WORK(&comp_data->usb_bh, etrax_usb_isoc_descr_interrupt_bottom_half, comp_data);
+- schedule_work(&comp_data->usb_bh);
+- }
+-
+- DBFEXIT;
+- return IRQ_HANDLED;
+-}
+-
+-static void etrax_usb_isoc_descr_interrupt_bottom_half(void *data)
+-{
+- usb_isoc_complete_data_t *comp_data = (usb_isoc_complete_data_t*)data;
+-
+- struct urb *urb;
+- int epid;
+- int epid_done;
+- etrax_urb_priv_t *urb_priv;
+-
+- DBFENTER;
+-
+- dbg_isoc("dma8_sub3_descr (ISOC) bottom half.");
+-
+- for (epid = 0; epid < NBR_OF_EPIDS - 1; epid++) {
+- unsigned long flags;
+-
+- save_flags(flags);
+- cli();
+-
+- epid_done = 0;
+-
+- /* The descriptor interrupt handler has marked all transmitted isoch. out
+- URBs with TRANSFER_DONE. Now we traverse all epids and for all that
+- have isoch. out traffic traverse its URB list and complete the
+- transmitted URB.
+- */
+-
+- while (!epid_done) {
+-
+- /* Get the first urb (if any). */
+- urb = urb_list_first(epid);
+- if (urb == 0) {
+- epid_done = 1;
+- continue;
+- }
+-
+- if (usb_pipetype(urb->pipe) != PIPE_ISOCHRONOUS) {
+- epid_done = 1;
+- continue;
+- }
+-
+- if (!usb_pipeout(urb->pipe)) {
+- /* descr interrupts are generated only for out pipes. */
+- epid_done = 1;
+- continue;
+- }
+-
+- dbg_isoc("Check epid %d, SB 0x%p", epid, (char*)TxIsocEPList[epid].sub);
+-
+- urb_priv = (etrax_urb_priv_t *)urb->hcpriv;
+- assert(urb_priv);
+-
+- if (urb_priv->urb_state == TRANSFER_DONE) {
+- int i;
+- struct usb_iso_packet_descriptor *packet;
+-
+- /* This urb has been sent. */
+- dbg_isoc("Completing isoc out URB 0x%p", urb);
+-
+- for (i = 0; i < urb->number_of_packets; i++) {
+- packet = &urb->iso_frame_desc[i];
+- packet->status = 0;
+- packet->actual_length = packet->length;
+- }
+-
+- etrax_usb_complete_isoc_urb(urb, 0);
+-
+- if (urb_list_empty(epid)) {
+- etrax_usb_free_epid(epid);
+- epid_done = 1;
+- }
+- } else {
+- epid_done = 1;
+- }
+- }
+- restore_flags(flags);
+-
+- }
+- kmem_cache_free(isoc_compl_cache, comp_data);
+-
+- DBFEXIT;
+-}
+-
+-
+-
+-static irqreturn_t etrax_usb_rx_interrupt(int irq, void *vhc)
+-{
+- struct urb *urb;
+- etrax_urb_priv_t *urb_priv;
+- int epid = 0;
+- unsigned long flags;
+-
+- /* Isoc diagnostics. */
+- static int curr_fm = 0;
+- static int prev_fm = 0;
+-
+- DBFENTER;
+-
+- /* Clear this interrupt. */
+- *R_DMA_CH9_CLR_INTR = IO_STATE(R_DMA_CH9_CLR_INTR, clr_eop, do);
+-
+- /* Note that this while loop assumes that all packets span only
+- one rx descriptor. */
+-
+- /* The reason we cli here is that we call the driver's callback functions. */
+- save_flags(flags);
+- cli();
+-
+- while (myNextRxDesc->status & IO_MASK(USB_IN_status, eop)) {
+-
+- epid = IO_EXTRACT(USB_IN_status, epid, myNextRxDesc->status);
+- urb = urb_list_first(epid);
+-
+- //printk("eop for epid %d, first urb 0x%lx\n", epid, (unsigned long)urb);
+-
+- if (!urb) {
+- err("No urb for epid %d in rx interrupt", epid);
+- __dump_ept_data(epid);
+- goto skip_out;
+- }
+-
+- /* Note that we cannot indescriminately assert(usb_pipein(urb->pipe)) since
+- ctrl pipes are not. */
+-
+- if (myNextRxDesc->status & IO_MASK(USB_IN_status, error)) {
+- __u32 r_usb_ept_data;
+- int no_error = 0;
+-
+- assert(test_bit(epid, (void *)&epid_usage_bitmask));
+-
+- *R_USB_EPT_INDEX = IO_FIELD(R_USB_EPT_INDEX, value, epid);
+- nop();
+- if (usb_pipetype(urb->pipe) == PIPE_ISOCHRONOUS) {
+- r_usb_ept_data = *R_USB_EPT_DATA_ISO;
+-
+- if ((r_usb_ept_data & IO_MASK(R_USB_EPT_DATA_ISO, valid)) &&
+- (IO_EXTRACT(R_USB_EPT_DATA_ISO, error_code, r_usb_ept_data) == 0) &&
+- (myNextRxDesc->status & IO_MASK(USB_IN_status, nodata))) {
+- /* Not an error, just a failure to receive an expected iso
+- in packet in this frame. This is not documented
+- in the designers reference.
+- */
+- no_error++;
+- } else {
+- warn("R_USB_EPT_DATA_ISO for epid %d = 0x%x", epid, r_usb_ept_data);
+- }
+- } else {
+- r_usb_ept_data = *R_USB_EPT_DATA;
+- warn("R_USB_EPT_DATA for epid %d = 0x%x", epid, r_usb_ept_data);
+- }
+-
+- if (!no_error){
+- warn("error in rx desc->status, epid %d, first urb = 0x%lx",
+- epid, (unsigned long)urb);
+- __dump_in_desc(myNextRxDesc);
+-
+- warn("R_USB_STATUS = 0x%x", *R_USB_STATUS);
+-
+- /* Check that ept was disabled when error occurred. */
+- switch (usb_pipetype(urb->pipe)) {
+- case PIPE_BULK:
+- assert(!(TxBulkEPList[epid].command & IO_MASK(USB_EP_command, enable)));
+- break;
+- case PIPE_CONTROL:
+- assert(!(TxCtrlEPList[epid].command & IO_MASK(USB_EP_command, enable)));
+- break;
+- case PIPE_INTERRUPT:
+- assert(!(TxIntrEPList[epid].command & IO_MASK(USB_EP_command, enable)));
+- break;
+- case PIPE_ISOCHRONOUS:
+- assert(!(TxIsocEPList[epid].command & IO_MASK(USB_EP_command, enable)));
+- break;
+- default:
+- warn("etrax_usb_rx_interrupt: bad pipetype %d in urb 0x%p",
+- usb_pipetype(urb->pipe),
+- urb);
+- }
+- etrax_usb_complete_urb(urb, -EPROTO);
+- goto skip_out;
+- }
+- }
+-
+- urb_priv = (etrax_urb_priv_t *)urb->hcpriv;
+- assert(urb_priv);
+-
+- if ((usb_pipetype(urb->pipe) == PIPE_BULK) ||
+- (usb_pipetype(urb->pipe) == PIPE_CONTROL) ||
+- (usb_pipetype(urb->pipe) == PIPE_INTERRUPT)) {
+-
+- if (myNextRxDesc->status & IO_MASK(USB_IN_status, nodata)) {
+- /* We get nodata for empty data transactions, and the rx descriptor's
+- hw_len field is not valid in that case. No data to copy in other
+- words. */
+- } else {
+- /* Make sure the data fits in the buffer. */
+- assert(urb_priv->rx_offset + myNextRxDesc->hw_len
+- <= urb->transfer_buffer_length);
+-
+- memcpy(urb->transfer_buffer + urb_priv->rx_offset,
+- phys_to_virt(myNextRxDesc->buf), myNextRxDesc->hw_len);
+- urb_priv->rx_offset += myNextRxDesc->hw_len;
+- }
+-
+- if (myNextRxDesc->status & IO_MASK(USB_IN_status, eot)) {
+- if ((usb_pipetype(urb->pipe) == PIPE_CONTROL) &&
+- ((TxCtrlEPList[urb_priv->epid].command & IO_MASK(USB_EP_command, enable)) ==
+- IO_STATE(USB_EP_command, enable, yes))) {
+- /* The EP is still enabled, so the OUT packet used to ack
+- the in data is probably not processed yet. If the EP
+- sub pointer has not moved beyond urb_priv->last_sb mark
+- it for a descriptor interrupt and complete the urb in
+- the descriptor interrupt handler.
+- */
+- USB_SB_Desc_t *sub = TxCtrlEPList[urb_priv->epid].sub ? phys_to_virt(TxCtrlEPList[urb_priv->epid].sub) : 0;
+-
+- while ((sub != NULL) && (sub != urb_priv->last_sb)) {
+- sub = sub->next ? phys_to_virt(sub->next) : 0;
+- }
+- if (sub != NULL) {
+- /* The urb has not been fully processed. */
+- urb_priv->urb_state = WAITING_FOR_DESCR_INTR;
+- } else {
+- warn("(CTRL) epid enabled and urb (0x%p) processed, ep->sub=0x%p", urb, (char*)TxCtrlEPList[urb_priv->epid].sub);
+- etrax_usb_complete_urb(urb, 0);
+- }
+- } else {
+- etrax_usb_complete_urb(urb, 0);
+- }
+- }
+-
+- } else if (usb_pipetype(urb->pipe) == PIPE_ISOCHRONOUS) {
+-
+- struct usb_iso_packet_descriptor *packet;
+-
+- if (urb_priv->urb_state == UNLINK) {
+- info("Ignoring rx data for urb being unlinked.");
+- goto skip_out;
+- } else if (urb_priv->urb_state == NOT_STARTED) {
+- info("What? Got rx data for urb that isn't started?");
+- goto skip_out;
+- }
+-
+- packet = &urb->iso_frame_desc[urb_priv->isoc_packet_counter];
+- packet->status = 0;
+-
+- if (myNextRxDesc->status & IO_MASK(USB_IN_status, nodata)) {
+- /* We get nodata for empty data transactions, and the rx descriptor's
+- hw_len field is not valid in that case. We copy 0 bytes however to
+- stay in synch. */
+- packet->actual_length = 0;
+- } else {
+- packet->actual_length = myNextRxDesc->hw_len;
+- /* Make sure the data fits in the buffer. */
+- assert(packet->actual_length <= packet->length);
+- memcpy(urb->transfer_buffer + packet->offset,
+- phys_to_virt(myNextRxDesc->buf), packet->actual_length);
+- }
+-
+- /* Increment the packet counter. */
+- urb_priv->isoc_packet_counter++;
+-
+- /* Note that we don't care about the eot field in the rx descriptor's status.
+- It will always be set for isoc traffic. */
+- if (urb->number_of_packets == urb_priv->isoc_packet_counter) {
+-
+- /* Out-of-synch diagnostics. */
+- curr_fm = (*R_USB_FM_NUMBER & 0x7ff);
+- if (((prev_fm + urb_priv->isoc_packet_counter) % (0x7ff + 1)) != curr_fm) {
+- /* This test is wrong, if there is more than one isoc
+- in endpoint active it will always calculate wrong
+- since prev_fm is shared by all endpoints.
+-
+- FIXME Make this check per URB using urb->start_frame.
+- */
+- dbg_isoc("Out of synch? Previous frame = %d, current frame = %d",
+- prev_fm, curr_fm);
+-
+- }
+- prev_fm = curr_fm;
+-
+- /* Complete the urb with status OK. */
+- etrax_usb_complete_isoc_urb(urb, 0);
+- }
+- }
+-
+- skip_out:
+-
+- /* DMA IN cache bug. Flush the DMA IN buffer from the cache. (struct etrax_dma_descr
+- has the same layout as USB_IN_Desc for the relevant fields.) */
+- prepare_rx_descriptor((struct etrax_dma_descr*)myNextRxDesc);
+-
+- myPrevRxDesc = myNextRxDesc;
+- myPrevRxDesc->command |= IO_MASK(USB_IN_command, eol);
+- myLastRxDesc->command &= ~IO_MASK(USB_IN_command, eol);
+- myLastRxDesc = myPrevRxDesc;
+-
+- myNextRxDesc->status = 0;
+- myNextRxDesc = phys_to_virt(myNextRxDesc->next);
+- }
+-
+- restore_flags(flags);
+-
+- DBFEXIT;
+-
+- return IRQ_HANDLED;
+-}
+-
+-
+-/* This function will unlink the SB descriptors associated with this urb. */
+-static int etrax_remove_from_sb_list(struct urb *urb)
+-{
+- USB_SB_Desc_t *next_sb, *first_sb, *last_sb;
+- etrax_urb_priv_t *urb_priv;
+- int i = 0;
+-
+- DBFENTER;
+-
+- urb_priv = (etrax_urb_priv_t *)urb->hcpriv;
+- assert(urb_priv);
+-
+- /* Just a sanity check. Since we don't fiddle with the DMA list the EP descriptor
+- doesn't really need to be disabled, it's just that we expect it to be. */
+- if (usb_pipetype(urb->pipe) == PIPE_BULK) {
+- assert(!(TxBulkEPList[urb_priv->epid].command & IO_MASK(USB_EP_command, enable)));
+- } else if (usb_pipetype(urb->pipe) == PIPE_CONTROL) {
+- assert(!(TxCtrlEPList[urb_priv->epid].command & IO_MASK(USB_EP_command, enable)));
+- }
+-
+- first_sb = urb_priv->first_sb;
+- last_sb = urb_priv->last_sb;
+-
+- assert(first_sb);
+- assert(last_sb);
+-
+- while (first_sb != last_sb) {
+- next_sb = (USB_SB_Desc_t *)phys_to_virt(first_sb->next);
+- kmem_cache_free(usb_desc_cache, first_sb);
+- first_sb = next_sb;
+- i++;
+- }
+- kmem_cache_free(usb_desc_cache, last_sb);
+- i++;
+- dbg_sb("%d SB descriptors freed", i);
+- /* Compare i with urb->number_of_packets for Isoc traffic.
+- Should be same when calling unlink_urb */
+-
+- DBFEXIT;
+-
+- return i;
+-}
+-
+-static int etrax_usb_submit_bulk_urb(struct urb *urb)
+-{
+- int epid;
+- int empty;
+- unsigned long flags;
+- etrax_urb_priv_t *urb_priv;
+-
+- DBFENTER;
+-
+- /* Epid allocation, empty check and list add must be protected.
+- Read about this in etrax_usb_submit_ctrl_urb. */
+-
+- spin_lock_irqsave(&urb_list_lock, flags);
+- epid = etrax_usb_setup_epid(urb);
+- if (epid == -1) {
+- DBFEXIT;
+- spin_unlock_irqrestore(&urb_list_lock, flags);
+- return -ENOMEM;
+- }
+- empty = urb_list_empty(epid);
+- urb_list_add(urb, epid);
+- spin_unlock_irqrestore(&urb_list_lock, flags);
+-
+- dbg_bulk("Adding bulk %s urb 0x%lx to %s list, epid %d",
+- usb_pipein(urb->pipe) ? "IN" : "OUT", (unsigned long)urb, empty ? "empty" : "", epid);
+-
+- /* Mark the urb as being in progress. */
+- urb->status = -EINPROGRESS;
+-
+- /* Setup the hcpriv data. */
+- urb_priv = kzalloc(sizeof(etrax_urb_priv_t), KMALLOC_FLAG);
+- assert(urb_priv != NULL);
+- /* This sets rx_offset to 0. */
+- urb_priv->urb_state = NOT_STARTED;
+- urb->hcpriv = urb_priv;
+-
+- if (empty) {
+- etrax_usb_add_to_bulk_sb_list(urb, epid);
+- }
+-
+- DBFEXIT;
+-
+- return 0;
+-}
+-
+-static void etrax_usb_add_to_bulk_sb_list(struct urb *urb, int epid)
+-{
+- USB_SB_Desc_t *sb_desc;
+- etrax_urb_priv_t *urb_priv = (etrax_urb_priv_t *)urb->hcpriv;
+- unsigned long flags;
+- char maxlen;
+-
+- DBFENTER;
+-
+- dbg_bulk("etrax_usb_add_to_bulk_sb_list, urb 0x%lx", (unsigned long)urb);
+-
+- maxlen = usb_maxpacket(urb->dev, urb->pipe, usb_pipeout(urb->pipe));
+-
+- sb_desc = kmem_cache_zalloc(usb_desc_cache, SLAB_FLAG);
+- assert(sb_desc != NULL);
+-
+-
+- if (usb_pipeout(urb->pipe)) {
+-
+- dbg_bulk("Grabbing bulk OUT, urb 0x%lx, epid %d", (unsigned long)urb, epid);
+-
+- /* This is probably a sanity check of the bulk transaction length
+- not being larger than 64 kB. */
+- if (urb->transfer_buffer_length > 0xffff) {
+- panic("urb->transfer_buffer_length > 0xffff");
+- }
+-
+- sb_desc->sw_len = urb->transfer_buffer_length;
+-
+- /* The rem field is don't care if it's not a full-length transfer, so setting
+- it shouldn't hurt. Also, rem isn't used for OUT traffic. */
+- sb_desc->command = (IO_FIELD(USB_SB_command, rem, 0) |
+- IO_STATE(USB_SB_command, tt, out) |
+- IO_STATE(USB_SB_command, eot, yes) |
+- IO_STATE(USB_SB_command, eol, yes));
+-
+- /* The full field is set to yes, even if we don't actually check that this is
+- a full-length transfer (i.e., that transfer_buffer_length % maxlen = 0).
+- Setting full prevents the USB controller from sending an empty packet in
+- that case. However, if URB_ZERO_PACKET was set we want that. */
+- if (!(urb->transfer_flags & URB_ZERO_PACKET)) {
+- sb_desc->command |= IO_STATE(USB_SB_command, full, yes);
+- }
+-
+- sb_desc->buf = virt_to_phys(urb->transfer_buffer);
+- sb_desc->next = 0;
+-
+- } else if (usb_pipein(urb->pipe)) {
+-
+- dbg_bulk("Grabbing bulk IN, urb 0x%lx, epid %d", (unsigned long)urb, epid);
+-
+- sb_desc->sw_len = urb->transfer_buffer_length ?
+- (urb->transfer_buffer_length - 1) / maxlen + 1 : 0;
+-
+- /* The rem field is don't care if it's not a full-length transfer, so setting
+- it shouldn't hurt. */
+- sb_desc->command =
+- (IO_FIELD(USB_SB_command, rem,
+- urb->transfer_buffer_length % maxlen) |
+- IO_STATE(USB_SB_command, tt, in) |
+- IO_STATE(USB_SB_command, eot, yes) |
+- IO_STATE(USB_SB_command, eol, yes));
+-
+- sb_desc->buf = 0;
+- sb_desc->next = 0;
+- }
+-
+- urb_priv->first_sb = sb_desc;
+- urb_priv->last_sb = sb_desc;
+- urb_priv->epid = epid;
+-
+- urb->hcpriv = urb_priv;
+-
+- /* Reset toggle bits and reset error count. */
+- save_flags(flags);
+- cli();
+-
+- *R_USB_EPT_INDEX = IO_FIELD(R_USB_EPT_INDEX, value, epid);
+- nop();
+-
+- /* FIXME: Is this a special case since the hold field is checked,
+- or should we check hold in a lot of other cases as well? */
+- if (*R_USB_EPT_DATA & IO_MASK(R_USB_EPT_DATA, hold)) {
+- panic("Hold was set in %s", __FUNCTION__);
+- }
+-
+- /* Reset error counters (regardless of which direction this traffic is). */
+- *R_USB_EPT_DATA &=
+- ~(IO_MASK(R_USB_EPT_DATA, error_count_in) |
+- IO_MASK(R_USB_EPT_DATA, error_count_out));
+-
+- /* Software must preset the toggle bits. */
+- if (usb_pipeout(urb->pipe)) {
+- char toggle =
+- usb_gettoggle(urb->dev, usb_pipeendpoint(urb->pipe), usb_pipeout(urb->pipe));
+- *R_USB_EPT_DATA &= ~IO_MASK(R_USB_EPT_DATA, t_out);
+- *R_USB_EPT_DATA |= IO_FIELD(R_USB_EPT_DATA, t_out, toggle);
+- } else {
+- char toggle =
+- usb_gettoggle(urb->dev, usb_pipeendpoint(urb->pipe), usb_pipeout(urb->pipe));
+- *R_USB_EPT_DATA &= ~IO_MASK(R_USB_EPT_DATA, t_in);
+- *R_USB_EPT_DATA |= IO_FIELD(R_USB_EPT_DATA, t_in, toggle);
+- }
+-
+- /* Assert that the EP descriptor is disabled. */
+- assert(!(TxBulkEPList[epid].command & IO_MASK(USB_EP_command, enable)));
+-
+- /* The reason we set the EP's sub pointer directly instead of
+- walking the SB list and linking it last in the list is that we only
+- have one active urb at a time (the rest are queued). */
+-
+- /* Note that we cannot have interrupts running when we have set the SB descriptor
+- but the EP is not yet enabled. If a bulk eot happens for another EP, we will
+- find this EP disabled and with a SB != 0, which will make us think that it's done. */
+- TxBulkEPList[epid].sub = virt_to_phys(sb_desc);
+- TxBulkEPList[epid].hw_len = 0;
+- /* Note that we don't have to fill in the ep_id field since this
+- was done when we allocated the EP descriptors in init_tx_bulk_ep. */
+-
+- /* Check if the dummy list is already with us (if several urbs were queued). */
+- if (TxBulkEPList[epid].next != virt_to_phys(&TxBulkDummyEPList[epid][0])) {
+-
+- dbg_bulk("Inviting dummy list to the party for urb 0x%lx, epid %d",
+- (unsigned long)urb, epid);
+-
+- /* The last EP in the dummy list already has its next pointer set to
+- TxBulkEPList[epid].next. */
+-
+- /* We don't need to check if the DMA is at this EP or not before changing the
+- next pointer, since we will do it in one 32-bit write (EP descriptors are
+- 32-bit aligned). */
+- TxBulkEPList[epid].next = virt_to_phys(&TxBulkDummyEPList[epid][0]);
+- }
+- /* Enable the EP descr. */
+- dbg_bulk("Enabling bulk EP for urb 0x%lx, epid %d", (unsigned long)urb, epid);
+- TxBulkEPList[epid].command |= IO_STATE(USB_EP_command, enable, yes);
+-
+- /* Everything is set up, safe to enable interrupts again. */
+- restore_flags(flags);
+-
+- /* If the DMA bulk channel isn't running, we need to restart it if it
+- has stopped at the last EP descriptor (DMA stopped because there was
+- no more traffic) or if it has stopped at a dummy EP with the intr flag
+- set (DMA stopped because we were too slow in inserting new traffic). */
+- if (!(*R_DMA_CH8_SUB0_CMD & IO_MASK(R_DMA_CH8_SUB0_CMD, cmd))) {
+-
+- USB_EP_Desc_t *ep;
+- ep = (USB_EP_Desc_t *)phys_to_virt(*R_DMA_CH8_SUB0_EP);
+- dbg_bulk("DMA channel not running in add");
+- dbg_bulk("DMA is at 0x%lx", (unsigned long)ep);
+-
+- if (*R_DMA_CH8_SUB0_EP == virt_to_phys(&TxBulkEPList[NBR_OF_EPIDS - 1]) ||
+- (ep->command & 0x8) >> 3) {
+- *R_DMA_CH8_SUB0_CMD = IO_STATE(R_DMA_CH8_SUB0_CMD, cmd, start);
+- /* Update/restart the bulk start timer since we just started the channel. */
+- mod_timer(&bulk_start_timer, jiffies + BULK_START_TIMER_INTERVAL);
+- /* Update/restart the bulk eot timer since we just inserted traffic. */
+- mod_timer(&bulk_eot_timer, jiffies + BULK_EOT_TIMER_INTERVAL);
+- }
+- }
+-
+- DBFEXIT;
+-}
+-
+-static void etrax_usb_complete_bulk_urb(struct urb *urb, int status)
+-{
+- etrax_urb_priv_t *urb_priv = (etrax_urb_priv_t *)urb->hcpriv;
+- int epid = urb_priv->epid;
+- unsigned long flags;
+-
+- DBFENTER;
+-
+- if (status)
+- warn("Completing bulk urb with status %d.", status);
+-
+- dbg_bulk("Completing bulk urb 0x%lx for epid %d", (unsigned long)urb, epid);
+-
+- /* Update the urb list. */
+- urb_list_del(urb, epid);
+-
+- /* For an IN pipe, we always set the actual length, regardless of whether there was
+- an error or not (which means the device driver can use the data if it wants to). */
+- if (usb_pipein(urb->pipe)) {
+- urb->actual_length = urb_priv->rx_offset;
+- } else {
+- /* Set actual_length for OUT urbs also; the USB mass storage driver seems
+- to want that. We wouldn't know of any partial writes if there was an error. */
+- if (status == 0) {
+- urb->actual_length = urb->transfer_buffer_length;
+- } else {
+- urb->actual_length = 0;
+- }
+- }
+-
+- /* FIXME: Is there something of the things below we shouldn't do if there was an error?
+- Like, maybe we shouldn't toggle the toggle bits, or maybe we shouldn't insert more traffic. */
+-
+- save_flags(flags);
+- cli();
+-
+- *R_USB_EPT_INDEX = IO_FIELD(R_USB_EPT_INDEX, value, epid);
+- nop();
+-
+- /* We need to fiddle with the toggle bits because the hardware doesn't do it for us. */
+- if (usb_pipeout(urb->pipe)) {
+- char toggle =
+- IO_EXTRACT(R_USB_EPT_DATA, t_out, *R_USB_EPT_DATA);
+- usb_settoggle(urb->dev, usb_pipeendpoint(urb->pipe),
+- usb_pipeout(urb->pipe), toggle);
+- } else {
+- char toggle =
+- IO_EXTRACT(R_USB_EPT_DATA, t_in, *R_USB_EPT_DATA);
+- usb_settoggle(urb->dev, usb_pipeendpoint(urb->pipe),
+- usb_pipeout(urb->pipe), toggle);
+- }
+- restore_flags(flags);
+-
+- /* Remember to free the SBs. */
+- etrax_remove_from_sb_list(urb);
+- kfree(urb_priv);
+- urb->hcpriv = 0;
+-
+- /* If there are any more urb's in the list we'd better start sending */
+- if (!urb_list_empty(epid)) {
+-
+- struct urb *new_urb;
+-
+- /* Get the first urb. */
+- new_urb = urb_list_first(epid);
+- assert(new_urb);
+-
+- dbg_bulk("More bulk for epid %d", epid);
+-
+- etrax_usb_add_to_bulk_sb_list(new_urb, epid);
+- }
+-
+- urb->status = status;
+-
+- /* We let any non-zero status from the layer above have precedence. */
+- if (status == 0) {
+- /* URB_SHORT_NOT_OK means that short reads (shorter than the endpoint's max length)
+- is to be treated as an error. */
+- if (urb->transfer_flags & URB_SHORT_NOT_OK) {
+- if (usb_pipein(urb->pipe) &&
+- (urb->actual_length !=
+- usb_maxpacket(urb->dev, urb->pipe, usb_pipeout(urb->pipe)))) {
+- urb->status = -EREMOTEIO;
+- }
+- }
+- }
+-
+- if (urb->complete) {
+- urb->complete(urb, NULL);
+- }
+-
+- if (urb_list_empty(epid)) {
+- /* This means that this EP is now free, deconfigure it. */
+- etrax_usb_free_epid(epid);
+-
+- /* No more traffic; time to clean up.
+- Must set sub pointer to 0, since we look at the sub pointer when handling
+- the bulk eot interrupt. */
+-
+- dbg_bulk("No bulk for epid %d", epid);
+-
+- TxBulkEPList[epid].sub = 0;
+-
+- /* Unlink the dummy list. */
+-
+- dbg_bulk("Kicking dummy list out of party for urb 0x%lx, epid %d",
+- (unsigned long)urb, epid);
+-
+- /* No need to wait for the DMA before changing the next pointer.
+- The modulo NBR_OF_EPIDS isn't actually necessary, since we will never use
+- the last one (INVALID_EPID) for actual traffic. */
+- TxBulkEPList[epid].next =
+- virt_to_phys(&TxBulkEPList[(epid + 1) % NBR_OF_EPIDS]);
+- }
+-
+- DBFEXIT;
+-}
+-
+-static int etrax_usb_submit_ctrl_urb(struct urb *urb)
+-{
+- int epid;
+- int empty;
+- unsigned long flags;
+- etrax_urb_priv_t *urb_priv;
+-
+- DBFENTER;
+-
+- /* FIXME: Return -ENXIO if there is already a queued urb for this endpoint? */
+-
+- /* Epid allocation, empty check and list add must be protected.
+-
+- Epid allocation because if we find an existing epid for this endpoint an urb might be
+- completed (emptying the list) before we add the new urb to the list, causing the epid
+- to be de-allocated. We would then start the transfer with an invalid epid -> epid attn.
+-
+- Empty check and add because otherwise we might conclude that the list is not empty,
+- after which it becomes empty before we add the new urb to the list, causing us not to
+- insert the new traffic into the SB list. */
+-
+- spin_lock_irqsave(&urb_list_lock, flags);
+- epid = etrax_usb_setup_epid(urb);
+- if (epid == -1) {
+- spin_unlock_irqrestore(&urb_list_lock, flags);
+- DBFEXIT;
+- return -ENOMEM;
+- }
+- empty = urb_list_empty(epid);
+- urb_list_add(urb, epid);
+- spin_unlock_irqrestore(&urb_list_lock, flags);
+-
+- dbg_ctrl("Adding ctrl urb 0x%lx to %s list, epid %d",
+- (unsigned long)urb, empty ? "empty" : "", epid);
+-
+- /* Mark the urb as being in progress. */
+- urb->status = -EINPROGRESS;
+-
+- /* Setup the hcpriv data. */
+- urb_priv = kzalloc(sizeof(etrax_urb_priv_t), KMALLOC_FLAG);
+- assert(urb_priv != NULL);
+- /* This sets rx_offset to 0. */
+- urb_priv->urb_state = NOT_STARTED;
+- urb->hcpriv = urb_priv;
+-
+- if (empty) {
+- etrax_usb_add_to_ctrl_sb_list(urb, epid);
+- }
+-
+- DBFEXIT;
+-
+- return 0;
+-}
+-
+-static void etrax_usb_add_to_ctrl_sb_list(struct urb *urb, int epid)
+-{
+- USB_SB_Desc_t *sb_desc_setup;
+- USB_SB_Desc_t *sb_desc_data;
+- USB_SB_Desc_t *sb_desc_status;
+-
+- etrax_urb_priv_t *urb_priv = (etrax_urb_priv_t *)urb->hcpriv;
+-
+- unsigned long flags;
+- char maxlen;
+-
+- DBFENTER;
+-
+- maxlen = usb_maxpacket(urb->dev, urb->pipe, usb_pipeout(urb->pipe));
+-
+- sb_desc_setup = (USB_SB_Desc_t*)kmem_cache_alloc(usb_desc_cache, SLAB_FLAG);
+- assert(sb_desc_setup != NULL);
+- sb_desc_status = (USB_SB_Desc_t*)kmem_cache_alloc(usb_desc_cache, SLAB_FLAG);
+- assert(sb_desc_status != NULL);
+-
+- /* Initialize the mandatory setup SB descriptor (used only in control transfers) */
+- sb_desc_setup->sw_len = 8;
+- sb_desc_setup->command = (IO_FIELD(USB_SB_command, rem, 0) |
+- IO_STATE(USB_SB_command, tt, setup) |
+- IO_STATE(USB_SB_command, full, yes) |
+- IO_STATE(USB_SB_command, eot, yes));
+-
+- sb_desc_setup->buf = virt_to_phys(urb->setup_packet);
+-
+- if (usb_pipeout(urb->pipe)) {
+- dbg_ctrl("Transfer for epid %d is OUT", epid);
+-
+- /* If this Control OUT transfer has an optional data stage we add an OUT token
+- before the mandatory IN (status) token, hence the reordered SB list */
+-
+- sb_desc_setup->next = virt_to_phys(sb_desc_status);
+- if (urb->transfer_buffer) {
+-
+- dbg_ctrl("This OUT transfer has an extra data stage");
+-
+- sb_desc_data = (USB_SB_Desc_t*)kmem_cache_alloc(usb_desc_cache, SLAB_FLAG);
+- assert(sb_desc_data != NULL);
+-
+- sb_desc_setup->next = virt_to_phys(sb_desc_data);
+-
+- sb_desc_data->sw_len = urb->transfer_buffer_length;
+- sb_desc_data->command = (IO_STATE(USB_SB_command, tt, out) |
+- IO_STATE(USB_SB_command, full, yes) |
+- IO_STATE(USB_SB_command, eot, yes));
+- sb_desc_data->buf = virt_to_phys(urb->transfer_buffer);
+- sb_desc_data->next = virt_to_phys(sb_desc_status);
+- }
+-
+- sb_desc_status->sw_len = 1;
+- sb_desc_status->command = (IO_FIELD(USB_SB_command, rem, 0) |
+- IO_STATE(USB_SB_command, tt, in) |
+- IO_STATE(USB_SB_command, eot, yes) |
+- IO_STATE(USB_SB_command, intr, yes) |
+- IO_STATE(USB_SB_command, eol, yes));
+-
+- sb_desc_status->buf = 0;
+- sb_desc_status->next = 0;
+-
+- } else if (usb_pipein(urb->pipe)) {
+-
+- dbg_ctrl("Transfer for epid %d is IN", epid);
+- dbg_ctrl("transfer_buffer_length = %d", urb->transfer_buffer_length);
+- dbg_ctrl("rem is calculated to %d", urb->transfer_buffer_length % maxlen);
+-
+- sb_desc_data = (USB_SB_Desc_t*)kmem_cache_alloc(usb_desc_cache, SLAB_FLAG);
+- assert(sb_desc_data != NULL);
+-
+- sb_desc_setup->next = virt_to_phys(sb_desc_data);
+-
+- sb_desc_data->sw_len = urb->transfer_buffer_length ?
+- (urb->transfer_buffer_length - 1) / maxlen + 1 : 0;
+- dbg_ctrl("sw_len got %d", sb_desc_data->sw_len);
+-
+- sb_desc_data->command =
+- (IO_FIELD(USB_SB_command, rem,
+- urb->transfer_buffer_length % maxlen) |
+- IO_STATE(USB_SB_command, tt, in) |
+- IO_STATE(USB_SB_command, eot, yes));
+-
+- sb_desc_data->buf = 0;
+- sb_desc_data->next = virt_to_phys(sb_desc_status);
+-
+- /* Read comment at zout_buffer declaration for an explanation to this. */
+- sb_desc_status->sw_len = 1;
+- sb_desc_status->command = (IO_FIELD(USB_SB_command, rem, 0) |
+- IO_STATE(USB_SB_command, tt, zout) |
+- IO_STATE(USB_SB_command, full, yes) |
+- IO_STATE(USB_SB_command, eot, yes) |
+- IO_STATE(USB_SB_command, intr, yes) |
+- IO_STATE(USB_SB_command, eol, yes));
+-
+- sb_desc_status->buf = virt_to_phys(&zout_buffer[0]);
+- sb_desc_status->next = 0;
+- }
+-
+- urb_priv->first_sb = sb_desc_setup;
+- urb_priv->last_sb = sb_desc_status;
+- urb_priv->epid = epid;
+-
+- urb_priv->urb_state = STARTED;
+-
+- /* Reset toggle bits and reset error count, remember to di and ei */
+- /* Warning: it is possible that this locking doesn't work with bottom-halves */
+-
+- save_flags(flags);
+- cli();
+-
+- *R_USB_EPT_INDEX = IO_FIELD(R_USB_EPT_INDEX, value, epid);
+- nop();
+- if (*R_USB_EPT_DATA & IO_MASK(R_USB_EPT_DATA, hold)) {
+- panic("Hold was set in %s", __FUNCTION__);
+- }
+-
+-
+- /* FIXME: Compare with etrax_usb_add_to_bulk_sb_list where the toggle bits
+- are set to a specific value. Why the difference? Read "Transfer and Toggle Bits
+- in Designer's Reference, p. 8 - 11. */
+- *R_USB_EPT_DATA &=
+- ~(IO_MASK(R_USB_EPT_DATA, error_count_in) |
+- IO_MASK(R_USB_EPT_DATA, error_count_out) |
+- IO_MASK(R_USB_EPT_DATA, t_in) |
+- IO_MASK(R_USB_EPT_DATA, t_out));
+-
+- /* Since we use the rx interrupt to complete ctrl urbs, we can enable interrupts now
+- (i.e. we don't check the sub pointer on an eot interrupt like we do for bulk traffic). */
+- restore_flags(flags);
+-
+- /* Assert that the EP descriptor is disabled. */
+- assert(!(TxCtrlEPList[epid].command & IO_MASK(USB_EP_command, enable)));
+-
+- /* Set up and enable the EP descriptor. */
+- TxCtrlEPList[epid].sub = virt_to_phys(sb_desc_setup);
+- TxCtrlEPList[epid].hw_len = 0;
+- TxCtrlEPList[epid].command |= IO_STATE(USB_EP_command, enable, yes);
+-
+- /* We start the DMA sub channel without checking if it's running or not, because:
+- 1) If it's already running, issuing the start command is a nop.
+- 2) We avoid a test-and-set race condition. */
+- *R_DMA_CH8_SUB1_CMD = IO_STATE(R_DMA_CH8_SUB1_CMD, cmd, start);
+-
+- DBFEXIT;
+-}
+-
+-static void etrax_usb_complete_ctrl_urb(struct urb *urb, int status)
+-{
+- etrax_urb_priv_t *urb_priv = (etrax_urb_priv_t *)urb->hcpriv;
+- int epid = urb_priv->epid;
+-
+- DBFENTER;
+-
+- if (status)
+- warn("Completing ctrl urb with status %d.", status);
+-
+- dbg_ctrl("Completing ctrl epid %d, urb 0x%lx", epid, (unsigned long)urb);
+-
+- /* Remove this urb from the list. */
+- urb_list_del(urb, epid);
+-
+- /* For an IN pipe, we always set the actual length, regardless of whether there was
+- an error or not (which means the device driver can use the data if it wants to). */
+- if (usb_pipein(urb->pipe)) {
+- urb->actual_length = urb_priv->rx_offset;
+- }
+-
+- /* FIXME: Is there something of the things below we shouldn't do if there was an error?
+- Like, maybe we shouldn't insert more traffic. */
+-
+- /* Remember to free the SBs. */
+- etrax_remove_from_sb_list(urb);
+- kfree(urb_priv);
+- urb->hcpriv = 0;
+-
+- /* If there are any more urbs in the list we'd better start sending. */
+- if (!urb_list_empty(epid)) {
+- struct urb *new_urb;
+-
+- /* Get the first urb. */
+- new_urb = urb_list_first(epid);
+- assert(new_urb);
+-
+- dbg_ctrl("More ctrl for epid %d, first urb = 0x%lx", epid, (unsigned long)new_urb);
+-
+- etrax_usb_add_to_ctrl_sb_list(new_urb, epid);
+- }
+-
+- urb->status = status;
+-
+- /* We let any non-zero status from the layer above have precedence. */
+- if (status == 0) {
+- /* URB_SHORT_NOT_OK means that short reads (shorter than the endpoint's max length)
+- is to be treated as an error. */
+- if (urb->transfer_flags & URB_SHORT_NOT_OK) {
+- if (usb_pipein(urb->pipe) &&
+- (urb->actual_length !=
+- usb_maxpacket(urb->dev, urb->pipe, usb_pipeout(urb->pipe)))) {
+- urb->status = -EREMOTEIO;
+- }
+- }
+- }
+-
+- if (urb->complete) {
+- urb->complete(urb, NULL);
+- }
+-
+- if (urb_list_empty(epid)) {
+- /* No more traffic. Time to clean up. */
+- etrax_usb_free_epid(epid);
+- /* Must set sub pointer to 0. */
+- dbg_ctrl("No ctrl for epid %d", epid);
+- TxCtrlEPList[epid].sub = 0;
+- }
+-
+- DBFEXIT;
+-}
+-
+-static int etrax_usb_submit_intr_urb(struct urb *urb)
+-{
+-
+- int epid;
+-
+- DBFENTER;
+-
+- if (usb_pipeout(urb->pipe)) {
+- /* Unsupported transfer type.
+- We don't support interrupt out traffic. (If we do, we can't support
+- intervals for neither in or out traffic, but are forced to schedule all
+- interrupt traffic in one frame.) */
+- return -EINVAL;
+- }
+-
+- epid = etrax_usb_setup_epid(urb);
+- if (epid == -1) {
+- DBFEXIT;
+- return -ENOMEM;
+- }
+-
+- if (!urb_list_empty(epid)) {
+- /* There is already a queued urb for this endpoint. */
+- etrax_usb_free_epid(epid);
+- return -ENXIO;
+- }
+-
+- urb->status = -EINPROGRESS;
+-
+- dbg_intr("Add intr urb 0x%lx, to list, epid %d", (unsigned long)urb, epid);
+-
+- urb_list_add(urb, epid);
+- etrax_usb_add_to_intr_sb_list(urb, epid);
+-
+- return 0;
+-
+- DBFEXIT;
+-}
+-
+-static void etrax_usb_add_to_intr_sb_list(struct urb *urb, int epid)
+-{
+-
+- volatile USB_EP_Desc_t *tmp_ep;
+- volatile USB_EP_Desc_t *first_ep;
+-
+- char maxlen;
+- int interval;
+- int i;
+-
+- etrax_urb_priv_t *urb_priv;
+-
+- DBFENTER;
+-
+- maxlen = usb_maxpacket(urb->dev, urb->pipe, usb_pipeout(urb->pipe));
+- interval = urb->interval;
+-
+- urb_priv = kzalloc(sizeof(etrax_urb_priv_t), KMALLOC_FLAG);
+- assert(urb_priv != NULL);
+- urb->hcpriv = urb_priv;
+-
+- first_ep = &TxIntrEPList[0];
+-
+- /* Round of the interval to 2^n, it is obvious that this code favours
+- smaller numbers, but that is actually a good thing */
+- /* FIXME: The "rounding error" for larger intervals will be quite
+- large. For in traffic this shouldn't be a problem since it will only
+- mean that we "poll" more often. */
+- for (i = 0; interval; i++) {
+- interval = interval >> 1;
+- }
+- interval = 1 << (i - 1);
+-
+- dbg_intr("Interval rounded to %d", interval);
+-
+- tmp_ep = first_ep;
+- i = 0;
+- do {
+- if (tmp_ep->command & IO_MASK(USB_EP_command, eof)) {
+- if ((i % interval) == 0) {
+- /* Insert the traffic ep after tmp_ep */
+- USB_EP_Desc_t *ep_desc;
+- USB_SB_Desc_t *sb_desc;
+-
+- dbg_intr("Inserting EP for epid %d", epid);
+-
+- ep_desc = (USB_EP_Desc_t *)
+- kmem_cache_alloc(usb_desc_cache, SLAB_FLAG);
+- sb_desc = (USB_SB_Desc_t *)
+- kmem_cache_alloc(usb_desc_cache, SLAB_FLAG);
+- assert(ep_desc != NULL);
+- CHECK_ALIGN(ep_desc);
+- assert(sb_desc != NULL);
+-
+- ep_desc->sub = virt_to_phys(sb_desc);
+- ep_desc->hw_len = 0;
+- ep_desc->command = (IO_FIELD(USB_EP_command, epid, epid) |
+- IO_STATE(USB_EP_command, enable, yes));
+-
+-
+- /* Round upwards the number of packets of size maxlen
+- that this SB descriptor should receive. */
+- sb_desc->sw_len = urb->transfer_buffer_length ?
+- (urb->transfer_buffer_length - 1) / maxlen + 1 : 0;
+- sb_desc->next = 0;
+- sb_desc->buf = 0;
+- sb_desc->command =
+- (IO_FIELD(USB_SB_command, rem, urb->transfer_buffer_length % maxlen) |
+- IO_STATE(USB_SB_command, tt, in) |
+- IO_STATE(USB_SB_command, eot, yes) |
+- IO_STATE(USB_SB_command, eol, yes));
+-
+- ep_desc->next = tmp_ep->next;
+- tmp_ep->next = virt_to_phys(ep_desc);
+- }
+- i++;
+- }
+- tmp_ep = (USB_EP_Desc_t *)phys_to_virt(tmp_ep->next);
+- } while (tmp_ep != first_ep);
+-
+-
+- /* Note that first_sb/last_sb doesn't apply to interrupt traffic. */
+- urb_priv->epid = epid;
+-
+- /* We start the DMA sub channel without checking if it's running or not, because:
+- 1) If it's already running, issuing the start command is a nop.
+- 2) We avoid a test-and-set race condition. */
+- *R_DMA_CH8_SUB2_CMD = IO_STATE(R_DMA_CH8_SUB2_CMD, cmd, start);
+-
+- DBFEXIT;
+-}
+-
+-
+-
+-static void etrax_usb_complete_intr_urb(struct urb *urb, int status)
+-{
+- etrax_urb_priv_t *urb_priv = (etrax_urb_priv_t *)urb->hcpriv;
+- int epid = urb_priv->epid;
+-
+- DBFENTER;
+-
+- if (status)
+- warn("Completing intr urb with status %d.", status);
+-
+- dbg_intr("Completing intr epid %d, urb 0x%lx", epid, (unsigned long)urb);
+-
+- urb->status = status;
+- urb->actual_length = urb_priv->rx_offset;
+-
+- dbg_intr("interrupt urb->actual_length = %d", urb->actual_length);
+-
+- /* We let any non-zero status from the layer above have precedence. */
+- if (status == 0) {
+- /* URB_SHORT_NOT_OK means that short reads (shorter than the endpoint's max length)
+- is to be treated as an error. */
+- if (urb->transfer_flags & URB_SHORT_NOT_OK) {
+- if (urb->actual_length !=
+- usb_maxpacket(urb->dev, urb->pipe, usb_pipeout(urb->pipe))) {
+- urb->status = -EREMOTEIO;
+- }
+- }
+- }
+-
+- /* The driver will resubmit the URB so we need to remove it first */
+- etrax_usb_unlink_urb(urb, 0);
+- if (urb->complete) {
+- urb->complete(urb, NULL);
+- }
+-
+- DBFEXIT;
+-}
+-
+-
+-static int etrax_usb_submit_isoc_urb(struct urb *urb)
+-{
+- int epid;
+- unsigned long flags;
+-
+- DBFENTER;
+-
+- dbg_isoc("Submitting isoc urb = 0x%lx", (unsigned long)urb);
+-
+- /* Epid allocation, empty check and list add must be protected.
+- Read about this in etrax_usb_submit_ctrl_urb. */
+-
+- spin_lock_irqsave(&urb_list_lock, flags);
+- /* Is there an active epid for this urb ? */
+- epid = etrax_usb_setup_epid(urb);
+- if (epid == -1) {
+- DBFEXIT;
+- spin_unlock_irqrestore(&urb_list_lock, flags);
+- return -ENOMEM;
+- }
+-
+- /* Ok, now we got valid endpoint, lets insert some traffic */
+-
+- urb->status = -EINPROGRESS;
+-
+- /* Find the last urb in the URB_List and add this urb after that one.
+- Also add the traffic, that is do an etrax_usb_add_to_isoc_sb_list. This
+- is important to make this in "real time" since isochronous traffic is
+- time sensitive. */
+-
+- dbg_isoc("Adding isoc urb to (possibly empty) list");
+- urb_list_add(urb, epid);
+- etrax_usb_add_to_isoc_sb_list(urb, epid);
+- spin_unlock_irqrestore(&urb_list_lock, flags);
+-
+- DBFEXIT;
+-
+- return 0;
+-}
+-
+-static void etrax_usb_check_error_isoc_ep(const int epid)
+-{
+- unsigned long int flags;
+- int error_code;
+- __u32 r_usb_ept_data;
+-
+- /* We can't read R_USB_EPID_ATTN here since it would clear the iso_eof,
+- bulk_eot and epid_attn interrupts. So we just check the status of
+- the epid without testing if for it in R_USB_EPID_ATTN. */
+-
+-
+- save_flags(flags);
+- cli();
+- *R_USB_EPT_INDEX = IO_FIELD(R_USB_EPT_INDEX, value, epid);
+- nop();
+- /* Note that although there are separate R_USB_EPT_DATA and R_USB_EPT_DATA_ISO
+- registers, they are located at the same address and are of the same size.
+- In other words, this read should be ok for isoc also. */
+- r_usb_ept_data = *R_USB_EPT_DATA;
+- restore_flags(flags);
+-
+- error_code = IO_EXTRACT(R_USB_EPT_DATA_ISO, error_code, r_usb_ept_data);
+-
+- if (r_usb_ept_data & IO_MASK(R_USB_EPT_DATA, hold)) {
+- warn("Hold was set for epid %d.", epid);
+- return;
+- }
+-
+- if (error_code == IO_STATE_VALUE(R_USB_EPT_DATA_ISO, error_code, no_error)) {
+-
+- /* This indicates that the SB list of the ept was completed before
+- new data was appended to it. This is not an error, but indicates
+- large system or USB load and could possibly cause trouble for
+- very timing sensitive USB device drivers so we log it.
+- */
+- info("Isoc. epid %d disabled with no error", epid);
+- return;
+-
+- } else if (error_code == IO_STATE_VALUE(R_USB_EPT_DATA_ISO, error_code, stall)) {
+- /* Not really a protocol error, just says that the endpoint gave
+- a stall response. Note that error_code cannot be stall for isoc. */
+- panic("Isoc traffic cannot stall");
+-
+- } else if (error_code == IO_STATE_VALUE(R_USB_EPT_DATA_ISO, error_code, bus_error)) {
+- /* Two devices responded to a transaction request. Must be resolved
+- by software. FIXME: Reset ports? */
+- panic("Bus error for epid %d."
+- " Two devices responded to transaction request",
+- epid);
+-
+- } else if (error_code == IO_STATE_VALUE(R_USB_EPT_DATA, error_code, buffer_error)) {
+- /* DMA overrun or underrun. */
+- warn("Buffer overrun/underrun for epid %d. DMA too busy?", epid);
+-
+- /* It seems that error_code = buffer_error in
+- R_USB_EPT_DATA/R_USB_EPT_DATA_ISO and ourun = yes in R_USB_STATUS
+- are the same error. */
+- }
+-}
+-
+-
+-static void etrax_usb_add_to_isoc_sb_list(struct urb *urb, int epid)
+-{
+-
+- int i = 0;
+-
+- etrax_urb_priv_t *urb_priv;
+- USB_SB_Desc_t *prev_sb_desc, *next_sb_desc, *temp_sb_desc;
+-
+- DBFENTER;
+-
+- prev_sb_desc = next_sb_desc = temp_sb_desc = NULL;
+-
+- urb_priv = kzalloc(sizeof(etrax_urb_priv_t), GFP_ATOMIC);
+- assert(urb_priv != NULL);
+-
+- urb->hcpriv = urb_priv;
+- urb_priv->epid = epid;
+-
+- if (usb_pipeout(urb->pipe)) {
+-
+- if (urb->number_of_packets == 0) panic("etrax_usb_add_to_isoc_sb_list 0 packets\n");
+-
+- dbg_isoc("Transfer for epid %d is OUT", epid);
+- dbg_isoc("%d packets in URB", urb->number_of_packets);
+-
+- /* Create one SB descriptor for each packet and link them together. */
+- for (i = 0; i < urb->number_of_packets; i++) {
+- if (!urb->iso_frame_desc[i].length)
+- continue;
+-
+- next_sb_desc = (USB_SB_Desc_t*)kmem_cache_alloc(usb_desc_cache, GFP_ATOMIC);
+- assert(next_sb_desc != NULL);
+-
+- if (urb->iso_frame_desc[i].length > 0) {
+-
+- next_sb_desc->command = (IO_STATE(USB_SB_command, tt, out) |
+- IO_STATE(USB_SB_command, eot, yes));
+-
+- next_sb_desc->sw_len = urb->iso_frame_desc[i].length;
+- next_sb_desc->buf = virt_to_phys((char*)urb->transfer_buffer + urb->iso_frame_desc[i].offset);
+-
+- /* Check if full length transfer. */
+- if (urb->iso_frame_desc[i].length ==
+- usb_maxpacket(urb->dev, urb->pipe, usb_pipeout(urb->pipe))) {
+- next_sb_desc->command |= IO_STATE(USB_SB_command, full, yes);
+- }
+- } else {
+- dbg_isoc("zero len packet");
+- next_sb_desc->command = (IO_FIELD(USB_SB_command, rem, 0) |
+- IO_STATE(USB_SB_command, tt, zout) |
+- IO_STATE(USB_SB_command, eot, yes) |
+- IO_STATE(USB_SB_command, full, yes));
+-
+- next_sb_desc->sw_len = 1;
+- next_sb_desc->buf = virt_to_phys(&zout_buffer[0]);
+- }
+-
+- /* First SB descriptor that belongs to this urb */
+- if (i == 0)
+- urb_priv->first_sb = next_sb_desc;
+- else
+- prev_sb_desc->next = virt_to_phys(next_sb_desc);
+-
+- prev_sb_desc = next_sb_desc;
+- }
+-
+- next_sb_desc->command |= (IO_STATE(USB_SB_command, intr, yes) |
+- IO_STATE(USB_SB_command, eol, yes));
+- next_sb_desc->next = 0;
+- urb_priv->last_sb = next_sb_desc;
+-
+- } else if (usb_pipein(urb->pipe)) {
+-
+- dbg_isoc("Transfer for epid %d is IN", epid);
+- dbg_isoc("transfer_buffer_length = %d", urb->transfer_buffer_length);
+- dbg_isoc("rem is calculated to %d", urb->iso_frame_desc[urb->number_of_packets - 1].length);
+-
+- /* Note that in descriptors for periodic traffic are not consumed. This means that
+- the USB controller never propagates in the SB list. In other words, if there already
+- is an SB descriptor in the list for this EP we don't have to do anything. */
+- if (TxIsocEPList[epid].sub == 0) {
+- dbg_isoc("Isoc traffic not already running, allocating SB");
+-
+- next_sb_desc = (USB_SB_Desc_t*)kmem_cache_alloc(usb_desc_cache, GFP_ATOMIC);
+- assert(next_sb_desc != NULL);
+-
+- next_sb_desc->command = (IO_STATE(USB_SB_command, tt, in) |
+- IO_STATE(USB_SB_command, eot, yes) |
+- IO_STATE(USB_SB_command, eol, yes));
+-
+- next_sb_desc->next = 0;
+- next_sb_desc->sw_len = 1; /* Actual number of packets is not relevant
+- for periodic in traffic as long as it is more
+- than zero. Set to 1 always. */
+- next_sb_desc->buf = 0;
+-
+- /* The rem field is don't care for isoc traffic, so we don't set it. */
+-
+- /* Only one SB descriptor that belongs to this urb. */
+- urb_priv->first_sb = next_sb_desc;
+- urb_priv->last_sb = next_sb_desc;
+-
+- } else {
+-
+- dbg_isoc("Isoc traffic already running, just setting first/last_sb");
+-
+- /* Each EP for isoc in will have only one SB descriptor, setup when submitting the
+- already active urb. Note that even though we may have several first_sb/last_sb
+- pointing at the same SB descriptor, they are freed only once (when the list has
+- become empty). */
+- urb_priv->first_sb = phys_to_virt(TxIsocEPList[epid].sub);
+- urb_priv->last_sb = phys_to_virt(TxIsocEPList[epid].sub);
+- return;
+- }
+-
+- }
+-
+- /* Find the spot to insert this urb and add it. */
+- if (TxIsocEPList[epid].sub == 0) {
+- /* First SB descriptor inserted in this list (in or out). */
+- dbg_isoc("Inserting SB desc first in list");
+- TxIsocEPList[epid].hw_len = 0;
+- TxIsocEPList[epid].sub = virt_to_phys(urb_priv->first_sb);
+-
+- } else {
+- /* Isochronous traffic is already running, insert new traffic last (only out). */
+- dbg_isoc("Inserting SB desc last in list");
+- temp_sb_desc = phys_to_virt(TxIsocEPList[epid].sub);
+- while ((temp_sb_desc->command & IO_MASK(USB_SB_command, eol)) !=
+- IO_STATE(USB_SB_command, eol, yes)) {
+- assert(temp_sb_desc->next);
+- temp_sb_desc = phys_to_virt(temp_sb_desc->next);
+- }
+- dbg_isoc("Appending list on desc 0x%p", temp_sb_desc);
+-
+- /* Next pointer must be set before eol is removed. */
+- temp_sb_desc->next = virt_to_phys(urb_priv->first_sb);
+- /* Clear the previous end of list flag since there is a new in the
+- added SB descriptor list. */
+- temp_sb_desc->command &= ~IO_MASK(USB_SB_command, eol);
+-
+- if (!(TxIsocEPList[epid].command & IO_MASK(USB_EP_command, enable))) {
+- /* 8.8.5 in Designer's Reference says we should check for and correct
+- any errors in the EP here. That should not be necessary if epid_attn
+- is handled correctly, so we assume all is ok. */
+- dbg_isoc("EP disabled");
+- etrax_usb_check_error_isoc_ep(epid);
+-
+- /* The SB list was exhausted. */
+- if (virt_to_phys(urb_priv->last_sb) != TxIsocEPList[epid].sub) {
+- /* The new sublist did not get processed before the EP was
+- disabled. Setup the EP again. */
+- dbg_isoc("Set EP sub to new list");
+- TxIsocEPList[epid].hw_len = 0;
+- TxIsocEPList[epid].sub = virt_to_phys(urb_priv->first_sb);
+- }
+- }
+- }
+-
+- if (urb->transfer_flags & URB_ISO_ASAP) {
+- /* The isoc transfer should be started as soon as possible. The start_frame
+- field is a return value if URB_ISO_ASAP was set. Comparing R_USB_FM_NUMBER
+- with a USB Chief trace shows that the first isoc IN token is sent 2 frames
+- later. I'm not sure how this affects usage of the start_frame field by the
+- device driver, or how it affects things when USB_ISO_ASAP is not set, so
+- therefore there's no compensation for the 2 frame "lag" here. */
+- urb->start_frame = (*R_USB_FM_NUMBER & 0x7ff);
+- TxIsocEPList[epid].command |= IO_STATE(USB_EP_command, enable, yes);
+- urb_priv->urb_state = STARTED;
+- dbg_isoc("URB_ISO_ASAP set, urb->start_frame set to %d", urb->start_frame);
+- } else {
+- /* Not started yet. */
+- urb_priv->urb_state = NOT_STARTED;
+- dbg_isoc("urb_priv->urb_state set to NOT_STARTED");
+- }
+-
+- /* We start the DMA sub channel without checking if it's running or not, because:
+- 1) If it's already running, issuing the start command is a nop.
+- 2) We avoid a test-and-set race condition. */
+- *R_DMA_CH8_SUB3_CMD = IO_STATE(R_DMA_CH8_SUB3_CMD, cmd, start);
+-
+- DBFEXIT;
+-}
+-
+-static void etrax_usb_complete_isoc_urb(struct urb *urb, int status)
+-{
+- etrax_urb_priv_t *urb_priv = (etrax_urb_priv_t *)urb->hcpriv;
+- int epid = urb_priv->epid;
+- int auto_resubmit = 0;
+-
+- DBFENTER;
+- dbg_isoc("complete urb 0x%p, status %d", urb, status);
+-
+- if (status)
+- warn("Completing isoc urb with status %d.", status);
+-
+- if (usb_pipein(urb->pipe)) {
+- int i;
+-
+- /* Make that all isoc packets have status and length set before
+- completing the urb. */
+- for (i = urb_priv->isoc_packet_counter; i < urb->number_of_packets; i++) {
+- urb->iso_frame_desc[i].actual_length = 0;
+- urb->iso_frame_desc[i].status = -EPROTO;
+- }
+-
+- urb_list_del(urb, epid);
+-
+- if (!list_empty(&urb_list[epid])) {
+- ((etrax_urb_priv_t *)(urb_list_first(epid)->hcpriv))->urb_state = STARTED;
+- } else {
+- unsigned long int flags;
+- if (TxIsocEPList[epid].command & IO_MASK(USB_EP_command, enable)) {
+- /* The EP was enabled, disable it and wait. */
+- TxIsocEPList[epid].command &= ~IO_MASK(USB_EP_command, enable);
+-
+- /* Ah, the luxury of busy-wait. */
+- while (*R_DMA_CH8_SUB3_EP == virt_to_phys(&TxIsocEPList[epid]));
+- }
+-
+- etrax_remove_from_sb_list(urb);
+- TxIsocEPList[epid].sub = 0;
+- TxIsocEPList[epid].hw_len = 0;
+-
+- save_flags(flags);
+- cli();
+- etrax_usb_free_epid(epid);
+- restore_flags(flags);
+- }
+-
+- urb->hcpriv = 0;
+- kfree(urb_priv);
+-
+- /* Release allocated bandwidth. */
+- usb_release_bandwidth(urb->dev, urb, 0);
+- } else if (usb_pipeout(urb->pipe)) {
+- int freed_descr;
+-
+- dbg_isoc("Isoc out urb complete 0x%p", urb);
+-
+- /* Update the urb list. */
+- urb_list_del(urb, epid);
+-
+- freed_descr = etrax_remove_from_sb_list(urb);
+- dbg_isoc("freed %d descriptors of %d packets", freed_descr, urb->number_of_packets);
+- assert(freed_descr == urb->number_of_packets);
+- urb->hcpriv = 0;
+- kfree(urb_priv);
+-
+- /* Release allocated bandwidth. */
+- usb_release_bandwidth(urb->dev, urb, 0);
+- }
+-
+- urb->status = status;
+- if (urb->complete) {
+- urb->complete(urb, NULL);
+- }
+-
+- if (auto_resubmit) {
+- /* Check that urb was not unlinked by the complete callback. */
+- if (__urb_list_entry(urb, epid)) {
+- /* Move this one down the list. */
+- urb_list_move_last(urb, epid);
+-
+- /* Mark the now first urb as started (may already be). */
+- ((etrax_urb_priv_t *)(urb_list_first(epid)->hcpriv))->urb_state = STARTED;
+-
+- /* Must set this to 0 since this urb is still active after
+- completion. */
+- urb_priv->isoc_packet_counter = 0;
+- } else {
+- warn("(ISOC) automatic resubmit urb 0x%p removed by complete.", urb);
+- }
+- }
+-
+- DBFEXIT;
+-}
+-
+-static void etrax_usb_complete_urb(struct urb *urb, int status)
+-{
+- switch (usb_pipetype(urb->pipe)) {
+- case PIPE_BULK:
+- etrax_usb_complete_bulk_urb(urb, status);
+- break;
+- case PIPE_CONTROL:
+- etrax_usb_complete_ctrl_urb(urb, status);
+- break;
+- case PIPE_INTERRUPT:
+- etrax_usb_complete_intr_urb(urb, status);
+- break;
+- case PIPE_ISOCHRONOUS:
+- etrax_usb_complete_isoc_urb(urb, status);
+- break;
+- default:
+- err("Unknown pipetype");
+- }
+-}
+-
+-
+-
+-static irqreturn_t etrax_usb_hc_interrupt_top_half(int irq, void *vhc)
+-{
+- usb_interrupt_registers_t *reg;
+- unsigned long flags;
+- __u32 irq_mask;
+- __u8 status;
+- __u32 epid_attn;
+- __u16 port_status_1;
+- __u16 port_status_2;
+- __u32 fm_number;
+-
+- DBFENTER;
+-
+- /* Read critical registers into local variables, do kmalloc afterwards. */
+- save_flags(flags);
+- cli();
+-
+- irq_mask = *R_USB_IRQ_MASK_READ;
+- /* Reading R_USB_STATUS clears the ctl_status interrupt. Note that R_USB_STATUS
+- must be read before R_USB_EPID_ATTN since reading the latter clears the
+- ourun and perror fields of R_USB_STATUS. */
+- status = *R_USB_STATUS;
+-
+- /* Reading R_USB_EPID_ATTN clears the iso_eof, bulk_eot and epid_attn interrupts. */
+- epid_attn = *R_USB_EPID_ATTN;
+-
+- /* Reading R_USB_RH_PORT_STATUS_1 and R_USB_RH_PORT_STATUS_2 clears the
+- port_status interrupt. */
+- port_status_1 = *R_USB_RH_PORT_STATUS_1;
+- port_status_2 = *R_USB_RH_PORT_STATUS_2;
+-
+- /* Reading R_USB_FM_NUMBER clears the sof interrupt. */
+- /* Note: the lower 11 bits contain the actual frame number, sent with each sof. */
+- fm_number = *R_USB_FM_NUMBER;
+-
+- restore_flags(flags);
+-
+- reg = (usb_interrupt_registers_t *)kmem_cache_alloc(top_half_reg_cache, GFP_ATOMIC);
+-
+- assert(reg != NULL);
+-
+- reg->hc = (etrax_hc_t *)vhc;
+-
+- /* Now put register values into kmalloc'd area. */
+- reg->r_usb_irq_mask_read = irq_mask;
+- reg->r_usb_status = status;
+- reg->r_usb_epid_attn = epid_attn;
+- reg->r_usb_rh_port_status_1 = port_status_1;
+- reg->r_usb_rh_port_status_2 = port_status_2;
+- reg->r_usb_fm_number = fm_number;
+-
+- INIT_WORK(®->usb_bh, etrax_usb_hc_interrupt_bottom_half, reg);
+- schedule_work(®->usb_bh);
+-
+- DBFEXIT;
+-
+- return IRQ_HANDLED;
+-}
+-
+-static void etrax_usb_hc_interrupt_bottom_half(void *data)
+-{
+- usb_interrupt_registers_t *reg = (usb_interrupt_registers_t *)data;
+- __u32 irq_mask = reg->r_usb_irq_mask_read;
+-
+- DBFENTER;
+-
+- /* Interrupts are handled in order of priority. */
+- if (irq_mask & IO_MASK(R_USB_IRQ_MASK_READ, epid_attn)) {
+- etrax_usb_hc_epid_attn_interrupt(reg);
+- }
+- if (irq_mask & IO_MASK(R_USB_IRQ_MASK_READ, port_status)) {
+- etrax_usb_hc_port_status_interrupt(reg);
+- }
+- if (irq_mask & IO_MASK(R_USB_IRQ_MASK_READ, ctl_status)) {
+- etrax_usb_hc_ctl_status_interrupt(reg);
+- }
+- if (irq_mask & IO_MASK(R_USB_IRQ_MASK_READ, iso_eof)) {
+- etrax_usb_hc_isoc_eof_interrupt();
+- }
+- if (irq_mask & IO_MASK(R_USB_IRQ_MASK_READ, bulk_eot)) {
+- /* Update/restart the bulk start timer since obviously the channel is running. */
+- mod_timer(&bulk_start_timer, jiffies + BULK_START_TIMER_INTERVAL);
+- /* Update/restart the bulk eot timer since we just received an bulk eot interrupt. */
+- mod_timer(&bulk_eot_timer, jiffies + BULK_EOT_TIMER_INTERVAL);
+-
+- etrax_usb_hc_bulk_eot_interrupt(0);
+- }
+-
+- kmem_cache_free(top_half_reg_cache, reg);
+-
+- DBFEXIT;
+-}
+-
+-
+-void etrax_usb_hc_isoc_eof_interrupt(void)
+-{
+- struct urb *urb;
+- etrax_urb_priv_t *urb_priv;
+- int epid;
+- unsigned long flags;
+-
+- DBFENTER;
+-
+- /* Do not check the invalid epid (it has a valid sub pointer). */
+- for (epid = 0; epid < NBR_OF_EPIDS - 1; epid++) {
+-
+- /* Do not check the invalid epid (it has a valid sub pointer). */
+- if ((epid == DUMMY_EPID) || (epid == INVALID_EPID))
+- continue;
+-
+- /* Disable interrupts to block the isoc out descriptor interrupt handler
+- from being called while the isoc EPID list is being checked.
+- */
+- save_flags(flags);
+- cli();
+-
+- if (TxIsocEPList[epid].sub == 0) {
+- /* Nothing here to see. */
+- restore_flags(flags);
+- continue;
+- }
+-
+- /* Get the first urb (if any). */
+- urb = urb_list_first(epid);
+- if (urb == 0) {
+- warn("Ignoring NULL urb");
+- restore_flags(flags);
+- continue;
+- }
+- if (usb_pipein(urb->pipe)) {
+-
+- /* Sanity check. */
+- assert(usb_pipetype(urb->pipe) == PIPE_ISOCHRONOUS);
+-
+- urb_priv = (etrax_urb_priv_t *)urb->hcpriv;
+- assert(urb_priv);
+-
+- if (urb_priv->urb_state == NOT_STARTED) {
+-
+- /* If ASAP is not set and urb->start_frame is the current frame,
+- start the transfer. */
+- if (!(urb->transfer_flags & URB_ISO_ASAP) &&
+- (urb->start_frame == (*R_USB_FM_NUMBER & 0x7ff))) {
+-
+- dbg_isoc("Enabling isoc IN EP descr for epid %d", epid);
+- TxIsocEPList[epid].command |= IO_STATE(USB_EP_command, enable, yes);
+-
+- /* This urb is now active. */
+- urb_priv->urb_state = STARTED;
+- continue;
+- }
+- }
+- }
+- restore_flags(flags);
+- }
+-
+- DBFEXIT;
+-
+-}
+-
+-void etrax_usb_hc_bulk_eot_interrupt(int timer_induced)
+-{
+- int epid;
+-
+- /* The technique is to run one urb at a time, wait for the eot interrupt at which
+- point the EP descriptor has been disabled. */
+-
+- DBFENTER;
+- dbg_bulk("bulk eot%s", timer_induced ? ", called by timer" : "");
+-
+- for (epid = 0; epid < NBR_OF_EPIDS; epid++) {
+-
+- if (!(TxBulkEPList[epid].command & IO_MASK(USB_EP_command, enable)) &&
+- (TxBulkEPList[epid].sub != 0)) {
+-
+- struct urb *urb;
+- etrax_urb_priv_t *urb_priv;
+- unsigned long flags;
+- __u32 r_usb_ept_data;
+-
+- /* Found a disabled EP descriptor which has a non-null sub pointer.
+- Verify that this ctrl EP descriptor got disabled no errors.
+- FIXME: Necessary to check error_code? */
+- dbg_bulk("for epid %d?", epid);
+-
+- /* Get the first urb. */
+- urb = urb_list_first(epid);
+-
+- /* FIXME: Could this happen for valid reasons? Why did it disappear? Because of
+- wrong unlinking? */
+- if (!urb) {
+- warn("NULL urb for epid %d", epid);
+- continue;
+- }
+-
+- assert(urb);
+- urb_priv = (etrax_urb_priv_t *)urb->hcpriv;
+- assert(urb_priv);
+-
+- /* Sanity checks. */
+- assert(usb_pipetype(urb->pipe) == PIPE_BULK);
+- if (phys_to_virt(TxBulkEPList[epid].sub) != urb_priv->last_sb) {
+- err("bulk endpoint got disabled before reaching last sb");
+- }
+-
+- /* For bulk IN traffic, there seems to be a race condition between
+- between the bulk eot and eop interrupts, or rather an uncertainty regarding
+- the order in which they happen. Normally we expect the eop interrupt from
+- DMA channel 9 to happen before the eot interrupt.
+-
+- Therefore, we complete the bulk IN urb in the rx interrupt handler instead. */
+-
+- if (usb_pipein(urb->pipe)) {
+- dbg_bulk("in urb, continuing");
+- continue;
+- }
+-
+- save_flags(flags);
+- cli();
+- *R_USB_EPT_INDEX = IO_FIELD(R_USB_EPT_INDEX, value, epid);
+- nop();
+- r_usb_ept_data = *R_USB_EPT_DATA;
+- restore_flags(flags);
+-
+- if (IO_EXTRACT(R_USB_EPT_DATA, error_code, r_usb_ept_data) ==
+- IO_STATE_VALUE(R_USB_EPT_DATA, error_code, no_error)) {
+- /* This means that the endpoint has no error, is disabled
+- and had inserted traffic, i.e. transfer successfully completed. */
+- etrax_usb_complete_bulk_urb(urb, 0);
+- } else {
+- /* Shouldn't happen. We expect errors to be caught by epid attention. */
+- err("Found disabled bulk EP desc, error_code != no_error");
+- }
+- }
+- }
+-
+- /* Normally, we should find (at least) one disabled EP descriptor with a valid sub pointer.
+- However, because of the uncertainty in the deliverance of the eop/eot interrupts, we may
+- not. Also, we might find two disabled EPs when handling an eot interrupt, and then find
+- none the next time. */
+-
+- DBFEXIT;
+-
+-}
+-
+-void etrax_usb_hc_epid_attn_interrupt(usb_interrupt_registers_t *reg)
+-{
+- /* This function handles the epid attention interrupt. There are a variety of reasons
+- for this interrupt to happen (Designer's Reference, p. 8 - 22 for the details):
+-
+- invalid ep_id - Invalid epid in an EP (EP disabled).
+- stall - Not strictly an error condition (EP disabled).
+- 3rd error - Three successive transaction errors (EP disabled).
+- buffer ourun - Buffer overrun or underrun (EP disabled).
+- past eof1 - Intr or isoc transaction proceeds past EOF1.
+- near eof - Intr or isoc transaction would not fit inside the frame.
+- zout transfer - If zout transfer for a bulk endpoint (EP disabled).
+- setup transfer - If setup transfer for a non-ctrl endpoint (EP disabled). */
+-
+- int epid;
+-
+-
+- DBFENTER;
+-
+- assert(reg != NULL);
+-
+- /* Note that we loop through all epids. We still want to catch errors for
+- the invalid one, even though we might handle them differently. */
+- for (epid = 0; epid < NBR_OF_EPIDS; epid++) {
+-
+- if (test_bit(epid, (void *)®->r_usb_epid_attn)) {
+-
+- struct urb *urb;
+- __u32 r_usb_ept_data;
+- unsigned long flags;
+- int error_code;
+-
+- save_flags(flags);
+- cli();
+- *R_USB_EPT_INDEX = IO_FIELD(R_USB_EPT_INDEX, value, epid);
+- nop();
+- /* Note that although there are separate R_USB_EPT_DATA and R_USB_EPT_DATA_ISO
+- registers, they are located at the same address and are of the same size.
+- In other words, this read should be ok for isoc also. */
+- r_usb_ept_data = *R_USB_EPT_DATA;
+- restore_flags(flags);
+-
+- /* First some sanity checks. */
+- if (epid == INVALID_EPID) {
+- /* FIXME: What if it became disabled? Could seriously hurt interrupt
+- traffic. (Use do_intr_recover.) */
+- warn("Got epid_attn for INVALID_EPID (%d).", epid);
+- err("R_USB_EPT_DATA = 0x%x", r_usb_ept_data);
+- err("R_USB_STATUS = 0x%x", reg->r_usb_status);
+- continue;
+- } else if (epid == DUMMY_EPID) {
+- /* We definitely don't care about these ones. Besides, they are
+- always disabled, so any possible disabling caused by the
+- epid attention interrupt is irrelevant. */
+- warn("Got epid_attn for DUMMY_EPID (%d).", epid);
+- continue;
+- }
+-
+- /* Get the first urb in the urb list for this epid. We blatantly assume
+- that only the first urb could have caused the epid attention.
+- (For bulk and ctrl, only one urb is active at any one time. For intr
+- and isoc we remove them once they are completed.) */
+- urb = urb_list_first(epid);
+-
+- if (urb == NULL) {
+- err("Got epid_attn for epid %i with no urb.", epid);
+- err("R_USB_EPT_DATA = 0x%x", r_usb_ept_data);
+- err("R_USB_STATUS = 0x%x", reg->r_usb_status);
+- continue;
+- }
+-
+- switch (usb_pipetype(urb->pipe)) {
+- case PIPE_BULK:
+- warn("Got epid attn for bulk endpoint, epid %d", epid);
+- break;
+- case PIPE_CONTROL:
+- warn("Got epid attn for control endpoint, epid %d", epid);
+- break;
+- case PIPE_INTERRUPT:
+- warn("Got epid attn for interrupt endpoint, epid %d", epid);
+- break;
+- case PIPE_ISOCHRONOUS:
+- warn("Got epid attn for isochronous endpoint, epid %d", epid);
+- break;
+- }
+-
+- if (usb_pipetype(urb->pipe) != PIPE_ISOCHRONOUS) {
+- if (r_usb_ept_data & IO_MASK(R_USB_EPT_DATA, hold)) {
+- warn("Hold was set for epid %d.", epid);
+- continue;
+- }
+- }
+-
+- /* Even though error_code occupies bits 22 - 23 in both R_USB_EPT_DATA and
+- R_USB_EPT_DATA_ISOC, we separate them here so we don't forget in other places. */
+- if (usb_pipetype(urb->pipe) == PIPE_ISOCHRONOUS) {
+- error_code = IO_EXTRACT(R_USB_EPT_DATA_ISO, error_code, r_usb_ept_data);
+- } else {
+- error_code = IO_EXTRACT(R_USB_EPT_DATA, error_code, r_usb_ept_data);
+- }
+-
+- /* Using IO_STATE_VALUE on R_USB_EPT_DATA should be ok for isoc also. */
+- if (error_code == IO_STATE_VALUE(R_USB_EPT_DATA, error_code, no_error)) {
+-
+- /* Isoc traffic doesn't have error_count_in/error_count_out. */
+- if ((usb_pipetype(urb->pipe) != PIPE_ISOCHRONOUS) &&
+- (IO_EXTRACT(R_USB_EPT_DATA, error_count_in, r_usb_ept_data) == 3 ||
+- IO_EXTRACT(R_USB_EPT_DATA, error_count_out, r_usb_ept_data) == 3)) {
+- /* 3rd error. */
+- warn("3rd error for epid %i", epid);
+- etrax_usb_complete_urb(urb, -EPROTO);
+-
+- } else if (reg->r_usb_status & IO_MASK(R_USB_STATUS, perror)) {
+-
+- warn("Perror for epid %d", epid);
+-
+- if (!(r_usb_ept_data & IO_MASK(R_USB_EPT_DATA, valid))) {
+- /* invalid ep_id */
+- panic("Perror because of invalid epid."
+- " Deconfigured too early?");
+- } else {
+- /* past eof1, near eof, zout transfer, setup transfer */
+-
+- /* Dump the urb and the relevant EP descriptor list. */
+-
+- __dump_urb(urb);
+- __dump_ept_data(epid);
+- __dump_ep_list(usb_pipetype(urb->pipe));
+-
+- panic("Something wrong with DMA descriptor contents."
+- " Too much traffic inserted?");
+- }
+- } else if (reg->r_usb_status & IO_MASK(R_USB_STATUS, ourun)) {
+- /* buffer ourun */
+- panic("Buffer overrun/underrun for epid %d. DMA too busy?", epid);
+- }
+-
+- } else if (error_code == IO_STATE_VALUE(R_USB_EPT_DATA, error_code, stall)) {
+- /* Not really a protocol error, just says that the endpoint gave
+- a stall response. Note that error_code cannot be stall for isoc. */
+- if (usb_pipetype(urb->pipe) == PIPE_ISOCHRONOUS) {
+- panic("Isoc traffic cannot stall");
+- }
+-
+- warn("Stall for epid %d", epid);
+- etrax_usb_complete_urb(urb, -EPIPE);
+-
+- } else if (error_code == IO_STATE_VALUE(R_USB_EPT_DATA, error_code, bus_error)) {
+- /* Two devices responded to a transaction request. Must be resolved
+- by software. FIXME: Reset ports? */
+- panic("Bus error for epid %d."
+- " Two devices responded to transaction request",
+- epid);
+-
+- } else if (error_code == IO_STATE_VALUE(R_USB_EPT_DATA, error_code, buffer_error)) {
+- /* DMA overrun or underrun. */
+- warn("Buffer overrun/underrun for epid %d. DMA too busy?", epid);
+-
+- /* It seems that error_code = buffer_error in
+- R_USB_EPT_DATA/R_USB_EPT_DATA_ISO and ourun = yes in R_USB_STATUS
+- are the same error. */
+- etrax_usb_complete_urb(urb, -EPROTO);
+- }
+- }
+- }
+-
+- DBFEXIT;
+-
+-}
+-
+-void etrax_usb_bulk_start_timer_func(unsigned long dummy)
+-{
+-
+- /* We might enable an EP descriptor behind the current DMA position when it's about
+- to decide that there are no more bulk traffic and it should stop the bulk channel.
+- Therefore we periodically check if the bulk channel is stopped and there is an
+- enabled bulk EP descriptor, in which case we start the bulk channel. */
+- dbg_bulk("bulk_start_timer timed out.");
+-
+- if (!(*R_DMA_CH8_SUB0_CMD & IO_MASK(R_DMA_CH8_SUB0_CMD, cmd))) {
+- int epid;
+-
+- dbg_bulk("Bulk DMA channel not running.");
+-
+- for (epid = 0; epid < NBR_OF_EPIDS; epid++) {
+- if (TxBulkEPList[epid].command & IO_MASK(USB_EP_command, enable)) {
+- dbg_bulk("Found enabled EP for epid %d, starting bulk channel.\n",
+- epid);
+- *R_DMA_CH8_SUB0_CMD = IO_STATE(R_DMA_CH8_SUB0_CMD, cmd, start);
+-
+- /* Restart the bulk eot timer since we just started the bulk channel. */
+- mod_timer(&bulk_eot_timer, jiffies + BULK_EOT_TIMER_INTERVAL);
+-
+- /* No need to search any further. */
+- break;
+- }
+- }
+- } else {
+- dbg_bulk("Bulk DMA channel running.");
+- }
+-}
+-
+-void etrax_usb_hc_port_status_interrupt(usb_interrupt_registers_t *reg)
+-{
+- etrax_hc_t *hc = reg->hc;
+- __u16 r_usb_rh_port_status_1 = reg->r_usb_rh_port_status_1;
+- __u16 r_usb_rh_port_status_2 = reg->r_usb_rh_port_status_2;
+-
+- DBFENTER;
+-
+- /* The Etrax RH does not include a wPortChange register, so this has to be handled in software
+- (by saving the old port status value for comparison when the port status interrupt happens).
+- See section 11.16.2.6.2 in the USB 1.1 spec for details. */
+-
+- dbg_rh("hc->rh.prev_wPortStatus_1 = 0x%x", hc->rh.prev_wPortStatus_1);
+- dbg_rh("hc->rh.prev_wPortStatus_2 = 0x%x", hc->rh.prev_wPortStatus_2);
+- dbg_rh("r_usb_rh_port_status_1 = 0x%x", r_usb_rh_port_status_1);
+- dbg_rh("r_usb_rh_port_status_2 = 0x%x", r_usb_rh_port_status_2);
+-
+- /* C_PORT_CONNECTION is set on any transition. */
+- hc->rh.wPortChange_1 |=
+- ((r_usb_rh_port_status_1 & (1 << RH_PORT_CONNECTION)) !=
+- (hc->rh.prev_wPortStatus_1 & (1 << RH_PORT_CONNECTION))) ?
+- (1 << RH_PORT_CONNECTION) : 0;
+-
+- hc->rh.wPortChange_2 |=
+- ((r_usb_rh_port_status_2 & (1 << RH_PORT_CONNECTION)) !=
+- (hc->rh.prev_wPortStatus_2 & (1 << RH_PORT_CONNECTION))) ?
+- (1 << RH_PORT_CONNECTION) : 0;
+-
+- /* C_PORT_ENABLE is _only_ set on a one to zero transition, i.e. when
+- the port is disabled, not when it's enabled. */
+- hc->rh.wPortChange_1 |=
+- ((hc->rh.prev_wPortStatus_1 & (1 << RH_PORT_ENABLE))
+- && !(r_usb_rh_port_status_1 & (1 << RH_PORT_ENABLE))) ?
+- (1 << RH_PORT_ENABLE) : 0;
+-
+- hc->rh.wPortChange_2 |=
+- ((hc->rh.prev_wPortStatus_2 & (1 << RH_PORT_ENABLE))
+- && !(r_usb_rh_port_status_2 & (1 << RH_PORT_ENABLE))) ?
+- (1 << RH_PORT_ENABLE) : 0;
+-
+- /* C_PORT_SUSPEND is set to one when the device has transitioned out
+- of the suspended state, i.e. when suspend goes from one to zero. */
+- hc->rh.wPortChange_1 |=
+- ((hc->rh.prev_wPortStatus_1 & (1 << RH_PORT_SUSPEND))
+- && !(r_usb_rh_port_status_1 & (1 << RH_PORT_SUSPEND))) ?
+- (1 << RH_PORT_SUSPEND) : 0;
+-
+- hc->rh.wPortChange_2 |=
+- ((hc->rh.prev_wPortStatus_2 & (1 << RH_PORT_SUSPEND))
+- && !(r_usb_rh_port_status_2 & (1 << RH_PORT_SUSPEND))) ?
+- (1 << RH_PORT_SUSPEND) : 0;
+-
+-
+- /* C_PORT_RESET is set when reset processing on this port is complete. */
+- hc->rh.wPortChange_1 |=
+- ((hc->rh.prev_wPortStatus_1 & (1 << RH_PORT_RESET))
+- && !(r_usb_rh_port_status_1 & (1 << RH_PORT_RESET))) ?
+- (1 << RH_PORT_RESET) : 0;
+-
+- hc->rh.wPortChange_2 |=
+- ((hc->rh.prev_wPortStatus_2 & (1 << RH_PORT_RESET))
+- && !(r_usb_rh_port_status_2 & (1 << RH_PORT_RESET))) ?
+- (1 << RH_PORT_RESET) : 0;
+-
+- /* Save the new values for next port status change. */
+- hc->rh.prev_wPortStatus_1 = r_usb_rh_port_status_1;
+- hc->rh.prev_wPortStatus_2 = r_usb_rh_port_status_2;
+-
+- dbg_rh("hc->rh.wPortChange_1 set to 0x%x", hc->rh.wPortChange_1);
+- dbg_rh("hc->rh.wPortChange_2 set to 0x%x", hc->rh.wPortChange_2);
+-
+- DBFEXIT;
+-
+-}
+-
+-void etrax_usb_hc_ctl_status_interrupt(usb_interrupt_registers_t *reg)
+-{
+- DBFENTER;
+-
+- /* FIXME: What should we do if we get ourun or perror? Dump the EP and SB
+- list for the corresponding epid? */
+- if (reg->r_usb_status & IO_MASK(R_USB_STATUS, ourun)) {
+- panic("USB controller got ourun.");
+- }
+- if (reg->r_usb_status & IO_MASK(R_USB_STATUS, perror)) {
+-
+- /* Before, etrax_usb_do_intr_recover was called on this epid if it was
+- an interrupt pipe. I don't see how re-enabling all EP descriptors
+- will help if there was a programming error. */
+- panic("USB controller got perror.");
+- }
+-
+- if (reg->r_usb_status & IO_MASK(R_USB_STATUS, device_mode)) {
+- /* We should never operate in device mode. */
+- panic("USB controller in device mode.");
+- }
+-
+- /* These if-statements could probably be nested. */
+- if (reg->r_usb_status & IO_MASK(R_USB_STATUS, host_mode)) {
+- info("USB controller in host mode.");
+- }
+- if (reg->r_usb_status & IO_MASK(R_USB_STATUS, started)) {
+- info("USB controller started.");
+- }
+- if (reg->r_usb_status & IO_MASK(R_USB_STATUS, running)) {
+- info("USB controller running.");
+- }
+-
+- DBFEXIT;
+-
+-}
+-
+-
+-static int etrax_rh_submit_urb(struct urb *urb)
+-{
+- struct usb_device *usb_dev = urb->dev;
+- etrax_hc_t *hc = usb_dev->bus->hcpriv;
+- unsigned int pipe = urb->pipe;
+- struct usb_ctrlrequest *cmd = (struct usb_ctrlrequest *) urb->setup_packet;
+- void *data = urb->transfer_buffer;
+- int leni = urb->transfer_buffer_length;
+- int len = 0;
+- int stat = 0;
+-
+- __u16 bmRType_bReq;
+- __u16 wValue;
+- __u16 wIndex;
+- __u16 wLength;
+-
+- DBFENTER;
+-
+- /* FIXME: What is this interrupt urb that is sent to the root hub? */
+- if (usb_pipetype (pipe) == PIPE_INTERRUPT) {
+- dbg_rh("Root-Hub submit IRQ: every %d ms", urb->interval);
+- hc->rh.urb = urb;
+- hc->rh.send = 1;
+- /* FIXME: We could probably remove this line since it's done
+- in etrax_rh_init_int_timer. (Don't remove it from
+- etrax_rh_init_int_timer though.) */
+- hc->rh.interval = urb->interval;
+- etrax_rh_init_int_timer(urb);
+- DBFEXIT;
+-
+- return 0;
+- }
+-
+- bmRType_bReq = cmd->bRequestType | (cmd->bRequest << 8);
+- wValue = le16_to_cpu(cmd->wValue);
+- wIndex = le16_to_cpu(cmd->wIndex);
+- wLength = le16_to_cpu(cmd->wLength);
+-
+- dbg_rh("bmRType_bReq : 0x%04x (%d)", bmRType_bReq, bmRType_bReq);
+- dbg_rh("wValue : 0x%04x (%d)", wValue, wValue);
+- dbg_rh("wIndex : 0x%04x (%d)", wIndex, wIndex);
+- dbg_rh("wLength : 0x%04x (%d)", wLength, wLength);
+-
+- switch (bmRType_bReq) {
+-
+- /* Request Destination:
+- without flags: Device,
+- RH_INTERFACE: interface,
+- RH_ENDPOINT: endpoint,
+- RH_CLASS means HUB here,
+- RH_OTHER | RH_CLASS almost ever means HUB_PORT here
+- */
+-
+- case RH_GET_STATUS:
+- *(__u16 *) data = cpu_to_le16 (1);
+- OK (2);
+-
+- case RH_GET_STATUS | RH_INTERFACE:
+- *(__u16 *) data = cpu_to_le16 (0);
+- OK (2);
+-
+- case RH_GET_STATUS | RH_ENDPOINT:
+- *(__u16 *) data = cpu_to_le16 (0);
+- OK (2);
+-
+- case RH_GET_STATUS | RH_CLASS:
+- *(__u32 *) data = cpu_to_le32 (0);
+- OK (4); /* hub power ** */
+-
+- case RH_GET_STATUS | RH_OTHER | RH_CLASS:
+- if (wIndex == 1) {
+- *((__u16*)data) = cpu_to_le16(hc->rh.prev_wPortStatus_1);
+- *((__u16*)data + 1) = cpu_to_le16(hc->rh.wPortChange_1);
+- } else if (wIndex == 2) {
+- *((__u16*)data) = cpu_to_le16(hc->rh.prev_wPortStatus_2);
+- *((__u16*)data + 1) = cpu_to_le16(hc->rh.wPortChange_2);
+- } else {
+- dbg_rh("RH_GET_STATUS whith invalid wIndex!");
+- OK(0);
+- }
+-
+- OK(4);
+-
+- case RH_CLEAR_FEATURE | RH_ENDPOINT:
+- switch (wValue) {
+- case (RH_ENDPOINT_STALL):
+- OK (0);
+- }
+- break;
+-
+- case RH_CLEAR_FEATURE | RH_CLASS:
+- switch (wValue) {
+- case (RH_C_HUB_OVER_CURRENT):
+- OK (0); /* hub power over current ** */
+- }
+- break;
+-
+- case RH_CLEAR_FEATURE | RH_OTHER | RH_CLASS:
+- switch (wValue) {
+- case (RH_PORT_ENABLE):
+- if (wIndex == 1) {
+-
+- dbg_rh("trying to do disable port 1");
+-
+- *R_USB_PORT1_DISABLE = IO_STATE(R_USB_PORT1_DISABLE, disable, yes);
+-
+- while (hc->rh.prev_wPortStatus_1 &
+- IO_STATE(R_USB_RH_PORT_STATUS_1, enabled, yes));
+- *R_USB_PORT1_DISABLE = IO_STATE(R_USB_PORT1_DISABLE, disable, no);
+- dbg_rh("Port 1 is disabled");
+-
+- } else if (wIndex == 2) {
+-
+- dbg_rh("trying to do disable port 2");
+-
+- *R_USB_PORT2_DISABLE = IO_STATE(R_USB_PORT2_DISABLE, disable, yes);
+-
+- while (hc->rh.prev_wPortStatus_2 &
+- IO_STATE(R_USB_RH_PORT_STATUS_2, enabled, yes));
+- *R_USB_PORT2_DISABLE = IO_STATE(R_USB_PORT2_DISABLE, disable, no);
+- dbg_rh("Port 2 is disabled");
+-
+- } else {
+- dbg_rh("RH_CLEAR_FEATURE->RH_PORT_ENABLE "
+- "with invalid wIndex == %d!", wIndex);
+- }
+-
+- OK (0);
+- case (RH_PORT_SUSPEND):
+- /* Opposite to suspend should be resume, so we'll do a resume. */
+- /* FIXME: USB 1.1, 11.16.2.2 says:
+- "Clearing the PORT_SUSPEND feature causes a host-initiated resume
+- on the specified port. If the port is not in the Suspended state,
+- the hub should treat this request as a functional no-operation."
+- Shouldn't we check if the port is in a suspended state before
+- resuming? */
+-
+- /* Make sure the controller isn't busy. */
+- while (*R_USB_COMMAND & IO_MASK(R_USB_COMMAND, busy));
+-
+- if (wIndex == 1) {
+- *R_USB_COMMAND =
+- IO_STATE(R_USB_COMMAND, port_sel, port1) |
+- IO_STATE(R_USB_COMMAND, port_cmd, resume) |
+- IO_STATE(R_USB_COMMAND, ctrl_cmd, nop);
+- } else if (wIndex == 2) {
+- *R_USB_COMMAND =
+- IO_STATE(R_USB_COMMAND, port_sel, port2) |
+- IO_STATE(R_USB_COMMAND, port_cmd, resume) |
+- IO_STATE(R_USB_COMMAND, ctrl_cmd, nop);
+- } else {
+- dbg_rh("RH_CLEAR_FEATURE->RH_PORT_SUSPEND "
+- "with invalid wIndex == %d!", wIndex);
+- }
+-
+- OK (0);
+- case (RH_PORT_POWER):
+- OK (0); /* port power ** */
+- case (RH_C_PORT_CONNECTION):
+- if (wIndex == 1) {
+- hc->rh.wPortChange_1 &= ~(1 << RH_PORT_CONNECTION);
+- } else if (wIndex == 2) {
+- hc->rh.wPortChange_2 &= ~(1 << RH_PORT_CONNECTION);
+- } else {
+- dbg_rh("RH_CLEAR_FEATURE->RH_C_PORT_CONNECTION "
+- "with invalid wIndex == %d!", wIndex);
+- }
+-
+- OK (0);
+- case (RH_C_PORT_ENABLE):
+- if (wIndex == 1) {
+- hc->rh.wPortChange_1 &= ~(1 << RH_PORT_ENABLE);
+- } else if (wIndex == 2) {
+- hc->rh.wPortChange_2 &= ~(1 << RH_PORT_ENABLE);
+- } else {
+- dbg_rh("RH_CLEAR_FEATURE->RH_C_PORT_ENABLE "
+- "with invalid wIndex == %d!", wIndex);
+- }
+- OK (0);
+- case (RH_C_PORT_SUSPEND):
+-/*** WR_RH_PORTSTAT(RH_PS_PSSC); */
+- OK (0);
+- case (RH_C_PORT_OVER_CURRENT):
+- OK (0); /* port power over current ** */
+- case (RH_C_PORT_RESET):
+- if (wIndex == 1) {
+- hc->rh.wPortChange_1 &= ~(1 << RH_PORT_RESET);
+- } else if (wIndex == 2) {
+- hc->rh.wPortChange_2 &= ~(1 << RH_PORT_RESET);
+- } else {
+- dbg_rh("RH_CLEAR_FEATURE->RH_C_PORT_RESET "
+- "with invalid index == %d!", wIndex);
+- }
+-
+- OK (0);
+-
+- }
+- break;
+-
+- case RH_SET_FEATURE | RH_OTHER | RH_CLASS:
+- switch (wValue) {
+- case (RH_PORT_SUSPEND):
+-
+- /* Make sure the controller isn't busy. */
+- while (*R_USB_COMMAND & IO_MASK(R_USB_COMMAND, busy));
+-
+- if (wIndex == 1) {
+- *R_USB_COMMAND =
+- IO_STATE(R_USB_COMMAND, port_sel, port1) |
+- IO_STATE(R_USB_COMMAND, port_cmd, suspend) |
+- IO_STATE(R_USB_COMMAND, ctrl_cmd, nop);
+- } else if (wIndex == 2) {
+- *R_USB_COMMAND =
+- IO_STATE(R_USB_COMMAND, port_sel, port2) |
+- IO_STATE(R_USB_COMMAND, port_cmd, suspend) |
+- IO_STATE(R_USB_COMMAND, ctrl_cmd, nop);
+- } else {
+- dbg_rh("RH_SET_FEATURE->RH_PORT_SUSPEND "
+- "with invalid wIndex == %d!", wIndex);
+- }
+-
+- OK (0);
+- case (RH_PORT_RESET):
+- if (wIndex == 1) {
+-
+- port_1_reset:
+- dbg_rh("Doing reset of port 1");
+-
+- /* Make sure the controller isn't busy. */
+- while (*R_USB_COMMAND & IO_MASK(R_USB_COMMAND, busy));
+-
+- *R_USB_COMMAND =
+- IO_STATE(R_USB_COMMAND, port_sel, port1) |
+- IO_STATE(R_USB_COMMAND, port_cmd, reset) |
+- IO_STATE(R_USB_COMMAND, ctrl_cmd, nop);
+-
+- /* We must wait at least 10 ms for the device to recover.
+- 15 ms should be enough. */
+- udelay(15000);
+-
+- /* Wait for reset bit to go low (should be done by now). */
+- while (hc->rh.prev_wPortStatus_1 &
+- IO_STATE(R_USB_RH_PORT_STATUS_1, reset, yes));
+-
+- /* If the port status is
+- 1) connected and enabled then there is a device and everything is fine
+- 2) neither connected nor enabled then there is no device, also fine
+- 3) connected and not enabled then we try again
+- (Yes, there are other port status combinations besides these.) */
+-
+- if ((hc->rh.prev_wPortStatus_1 &
+- IO_STATE(R_USB_RH_PORT_STATUS_1, connected, yes)) &&
+- (hc->rh.prev_wPortStatus_1 &
+- IO_STATE(R_USB_RH_PORT_STATUS_1, enabled, no))) {
+- dbg_rh("Connected device on port 1, but port not enabled?"
+- " Trying reset again.");
+- goto port_2_reset;
+- }
+-
+- /* Diagnostic printouts. */
+- if ((hc->rh.prev_wPortStatus_1 &
+- IO_STATE(R_USB_RH_PORT_STATUS_1, connected, no)) &&
+- (hc->rh.prev_wPortStatus_1 &
+- IO_STATE(R_USB_RH_PORT_STATUS_1, enabled, no))) {
+- dbg_rh("No connected device on port 1");
+- } else if ((hc->rh.prev_wPortStatus_1 &
+- IO_STATE(R_USB_RH_PORT_STATUS_1, connected, yes)) &&
+- (hc->rh.prev_wPortStatus_1 &
+- IO_STATE(R_USB_RH_PORT_STATUS_1, enabled, yes))) {
+- dbg_rh("Connected device on port 1, port 1 enabled");
+- }
+-
+- } else if (wIndex == 2) {
+-
+- port_2_reset:
+- dbg_rh("Doing reset of port 2");
+-
+- /* Make sure the controller isn't busy. */
+- while (*R_USB_COMMAND & IO_MASK(R_USB_COMMAND, busy));
+-
+- /* Issue the reset command. */
+- *R_USB_COMMAND =
+- IO_STATE(R_USB_COMMAND, port_sel, port2) |
+- IO_STATE(R_USB_COMMAND, port_cmd, reset) |
+- IO_STATE(R_USB_COMMAND, ctrl_cmd, nop);
+-
+- /* We must wait at least 10 ms for the device to recover.
+- 15 ms should be enough. */
+- udelay(15000);
+-
+- /* Wait for reset bit to go low (should be done by now). */
+- while (hc->rh.prev_wPortStatus_2 &
+- IO_STATE(R_USB_RH_PORT_STATUS_2, reset, yes));
+-
+- /* If the port status is
+- 1) connected and enabled then there is a device and everything is fine
+- 2) neither connected nor enabled then there is no device, also fine
+- 3) connected and not enabled then we try again
+- (Yes, there are other port status combinations besides these.) */
+-
+- if ((hc->rh.prev_wPortStatus_2 &
+- IO_STATE(R_USB_RH_PORT_STATUS_2, connected, yes)) &&
+- (hc->rh.prev_wPortStatus_2 &
+- IO_STATE(R_USB_RH_PORT_STATUS_2, enabled, no))) {
+- dbg_rh("Connected device on port 2, but port not enabled?"
+- " Trying reset again.");
+- goto port_2_reset;
+- }
+-
+- /* Diagnostic printouts. */
+- if ((hc->rh.prev_wPortStatus_2 &
+- IO_STATE(R_USB_RH_PORT_STATUS_2, connected, no)) &&
+- (hc->rh.prev_wPortStatus_2 &
+- IO_STATE(R_USB_RH_PORT_STATUS_2, enabled, no))) {
+- dbg_rh("No connected device on port 2");
+- } else if ((hc->rh.prev_wPortStatus_2 &
+- IO_STATE(R_USB_RH_PORT_STATUS_2, connected, yes)) &&
+- (hc->rh.prev_wPortStatus_2 &
+- IO_STATE(R_USB_RH_PORT_STATUS_2, enabled, yes))) {
+- dbg_rh("Connected device on port 2, port 2 enabled");
+- }
+-
+- } else {
+- dbg_rh("RH_SET_FEATURE->RH_PORT_RESET with invalid wIndex = %d", wIndex);
+- }
+-
+- /* Make sure the controller isn't busy. */
+- while (*R_USB_COMMAND & IO_MASK(R_USB_COMMAND, busy));
+-
+- /* If all enabled ports were disabled the host controller goes down into
+- started mode, so we need to bring it back into the running state.
+- (This is safe even if it's already in the running state.) */
+- *R_USB_COMMAND =
+- IO_STATE(R_USB_COMMAND, port_sel, nop) |
+- IO_STATE(R_USB_COMMAND, port_cmd, reset) |
+- IO_STATE(R_USB_COMMAND, ctrl_cmd, host_run);
+-
+- dbg_rh("...Done");
+- OK(0);
+-
+- case (RH_PORT_POWER):
+- OK (0); /* port power ** */
+- case (RH_PORT_ENABLE):
+- /* There is no port enable command in the host controller, so if the
+- port is already enabled, we do nothing. If not, we reset the port
+- (with an ugly goto). */
+-
+- if (wIndex == 1) {
+- if (hc->rh.prev_wPortStatus_1 &
+- IO_STATE(R_USB_RH_PORT_STATUS_1, enabled, no)) {
+- goto port_1_reset;
+- }
+- } else if (wIndex == 2) {
+- if (hc->rh.prev_wPortStatus_2 &
+- IO_STATE(R_USB_RH_PORT_STATUS_2, enabled, no)) {
+- goto port_2_reset;
+- }
+- } else {
+- dbg_rh("RH_SET_FEATURE->RH_GET_STATUS with invalid wIndex = %d", wIndex);
+- }
+- OK (0);
+- }
+- break;
+-
+- case RH_SET_ADDRESS:
+- hc->rh.devnum = wValue;
+- dbg_rh("RH address set to: %d", hc->rh.devnum);
+- OK (0);
+-
+- case RH_GET_DESCRIPTOR:
+- switch ((wValue & 0xff00) >> 8) {
+- case (0x01): /* device descriptor */
+- len = min_t(unsigned int, leni, min_t(unsigned int, sizeof (root_hub_dev_des), wLength));
+- memcpy (data, root_hub_dev_des, len);
+- OK (len);
+- case (0x02): /* configuration descriptor */
+- len = min_t(unsigned int, leni, min_t(unsigned int, sizeof (root_hub_config_des), wLength));
+- memcpy (data, root_hub_config_des, len);
+- OK (len);
+- case (0x03): /* string descriptors */
+- len = usb_root_hub_string (wValue & 0xff,
+- 0xff, "ETRAX 100LX",
+- data, wLength);
+- if (len > 0) {
+- OK(min(leni, len));
+- } else {
+- stat = -EPIPE;
+- }
+-
+- }
+- break;
+-
+- case RH_GET_DESCRIPTOR | RH_CLASS:
+- root_hub_hub_des[2] = hc->rh.numports;
+- len = min_t(unsigned int, leni, min_t(unsigned int, sizeof (root_hub_hub_des), wLength));
+- memcpy (data, root_hub_hub_des, len);
+- OK (len);
+-
+- case RH_GET_CONFIGURATION:
+- *(__u8 *) data = 0x01;
+- OK (1);
+-
+- case RH_SET_CONFIGURATION:
+- OK (0);
+-
+- default:
+- stat = -EPIPE;
+- }
+-
+- urb->actual_length = len;
+- urb->status = stat;
+- urb->dev = NULL;
+- if (urb->complete) {
+- urb->complete(urb, NULL);
+- }
+- DBFEXIT;
+-
+- return 0;
+-}
+-
+-static void
+-etrax_usb_bulk_eot_timer_func(unsigned long dummy)
+-{
+- /* Because of a race condition in the top half, we might miss a bulk eot.
+- This timer "simulates" a bulk eot if we don't get one for a while, hopefully
+- correcting the situation. */
+- dbg_bulk("bulk_eot_timer timed out.");
+- etrax_usb_hc_bulk_eot_interrupt(1);
+-}
+-
+-static void*
+-etrax_usb_buffer_alloc(struct usb_bus* bus, size_t size,
+- unsigned mem_flags, dma_addr_t *dma)
+-{
+- return kmalloc(size, mem_flags);
+-}
+-
+-static void
+-etrax_usb_buffer_free(struct usb_bus *bus, size_t size, void *addr, dma_addr_t dma)
+-{
+- kfree(addr);
+-}
+-
+-
+-static struct device fake_device;
+-
+-static int __init etrax_usb_hc_init(void)
+-{
+- static etrax_hc_t *hc;
+- struct usb_bus *bus;
+- struct usb_device *usb_rh;
+- int i;
+-
+- DBFENTER;
+-
+- info("ETRAX 100LX USB-HCD %s (c) 2001-2003 Axis Communications AB\n", usb_hcd_version);
+-
+- hc = kmalloc(sizeof(etrax_hc_t), GFP_KERNEL);
+- assert(hc != NULL);
+-
+- /* We use kmem_cache_* to make sure that all DMA desc. are dword aligned */
+- /* Note that we specify sizeof(USB_EP_Desc_t) as the size, but also allocate
+- SB descriptors from this cache. This is ok since sizeof(USB_EP_Desc_t) ==
+- sizeof(USB_SB_Desc_t). */
+-
+- usb_desc_cache = kmem_cache_create("usb_desc_cache", sizeof(USB_EP_Desc_t), 0,
+- SLAB_HWCACHE_ALIGN, 0, 0);
+- assert(usb_desc_cache != NULL);
+-
+- top_half_reg_cache = kmem_cache_create("top_half_reg_cache",
+- sizeof(usb_interrupt_registers_t),
+- 0, SLAB_HWCACHE_ALIGN, 0, 0);
+- assert(top_half_reg_cache != NULL);
+-
+- isoc_compl_cache = kmem_cache_create("isoc_compl_cache",
+- sizeof(usb_isoc_complete_data_t),
+- 0, SLAB_HWCACHE_ALIGN, 0, 0);
+- assert(isoc_compl_cache != NULL);
+-
+- etrax_usb_bus = bus = usb_alloc_bus(&etrax_usb_device_operations);
+- hc->bus = bus;
+- bus->bus_name="ETRAX 100LX";
+- bus->hcpriv = hc;
+-
+- /* Initialize RH to the default address.
+- And make sure that we have no status change indication */
+- hc->rh.numports = 2; /* The RH has two ports */
+- hc->rh.devnum = 1;
+- hc->rh.wPortChange_1 = 0;
+- hc->rh.wPortChange_2 = 0;
+-
+- /* Also initate the previous values to zero */
+- hc->rh.prev_wPortStatus_1 = 0;
+- hc->rh.prev_wPortStatus_2 = 0;
+-
+- /* Initialize the intr-traffic flags */
+- /* FIXME: This isn't used. (Besides, the error field isn't initialized.) */
+- hc->intr.sleeping = 0;
+- hc->intr.wq = NULL;
+-
+- epid_usage_bitmask = 0;
+- epid_out_traffic = 0;
+-
+- /* Mark the invalid epid as being used. */
+- set_bit(INVALID_EPID, (void *)&epid_usage_bitmask);
+- *R_USB_EPT_INDEX = IO_FIELD(R_USB_EPT_INDEX, value, INVALID_EPID);
+- nop();
+- /* The valid bit should still be set ('invalid' is in our world; not the hardware's). */
+- *R_USB_EPT_DATA = (IO_STATE(R_USB_EPT_DATA, valid, yes) |
+- IO_FIELD(R_USB_EPT_DATA, max_len, 1));
+-
+- /* Mark the dummy epid as being used. */
+- set_bit(DUMMY_EPID, (void *)&epid_usage_bitmask);
+- *R_USB_EPT_INDEX = IO_FIELD(R_USB_EPT_INDEX, value, DUMMY_EPID);
+- nop();
+- *R_USB_EPT_DATA = (IO_STATE(R_USB_EPT_DATA, valid, no) |
+- IO_FIELD(R_USB_EPT_DATA, max_len, 1));
+-
+- /* Initialize the urb list by initiating a head for each list. */
+- for (i = 0; i < NBR_OF_EPIDS; i++) {
+- INIT_LIST_HEAD(&urb_list[i]);
+- }
+- spin_lock_init(&urb_list_lock);
+-
+- INIT_LIST_HEAD(&urb_unlink_list);
+-
+-
+- /* Initiate the bulk start timer. */
+- init_timer(&bulk_start_timer);
+- bulk_start_timer.expires = jiffies + BULK_START_TIMER_INTERVAL;
+- bulk_start_timer.function = etrax_usb_bulk_start_timer_func;
+- add_timer(&bulk_start_timer);
+-
+-
+- /* Initiate the bulk eot timer. */
+- init_timer(&bulk_eot_timer);
+- bulk_eot_timer.expires = jiffies + BULK_EOT_TIMER_INTERVAL;
+- bulk_eot_timer.function = etrax_usb_bulk_eot_timer_func;
+- add_timer(&bulk_eot_timer);
+-
+- /* Set up the data structures for USB traffic. Note that this must be done before
+- any interrupt that relies on sane DMA list occurrs. */
+- init_rx_buffers();
+- init_tx_bulk_ep();
+- init_tx_ctrl_ep();
+- init_tx_intr_ep();
+- init_tx_isoc_ep();
+-
+- device_initialize(&fake_device);
+- kobject_set_name(&fake_device.kobj, "etrax_usb");
+- kobject_add(&fake_device.kobj);
+- kobject_uevent(&fake_device.kobj, KOBJ_ADD);
+- hc->bus->controller = &fake_device;
+- usb_register_bus(hc->bus);
+-
+- *R_IRQ_MASK2_SET =
+- /* Note that these interrupts are not used. */
+- IO_STATE(R_IRQ_MASK2_SET, dma8_sub0_descr, set) |
+- /* Sub channel 1 (ctrl) descr. interrupts are used. */
+- IO_STATE(R_IRQ_MASK2_SET, dma8_sub1_descr, set) |
+- IO_STATE(R_IRQ_MASK2_SET, dma8_sub2_descr, set) |
+- /* Sub channel 3 (isoc) descr. interrupts are used. */
+- IO_STATE(R_IRQ_MASK2_SET, dma8_sub3_descr, set);
+-
+- /* Note that the dma9_descr interrupt is not used. */
+- *R_IRQ_MASK2_SET =
+- IO_STATE(R_IRQ_MASK2_SET, dma9_eop, set) |
+- IO_STATE(R_IRQ_MASK2_SET, dma9_descr, set);
+-
+- /* FIXME: Enable iso_eof only when isoc traffic is running. */
+- *R_USB_IRQ_MASK_SET =
+- IO_STATE(R_USB_IRQ_MASK_SET, iso_eof, set) |
+- IO_STATE(R_USB_IRQ_MASK_SET, bulk_eot, set) |
+- IO_STATE(R_USB_IRQ_MASK_SET, epid_attn, set) |
+- IO_STATE(R_USB_IRQ_MASK_SET, port_status, set) |
+- IO_STATE(R_USB_IRQ_MASK_SET, ctl_status, set);
+-
+-
+- if (request_irq(ETRAX_USB_HC_IRQ, etrax_usb_hc_interrupt_top_half, 0,
+- "ETRAX 100LX built-in USB (HC)", hc)) {
+- err("Could not allocate IRQ %d for USB", ETRAX_USB_HC_IRQ);
+- etrax_usb_hc_cleanup();
+- DBFEXIT;
+- return -1;
+- }
+-
+- if (request_irq(ETRAX_USB_RX_IRQ, etrax_usb_rx_interrupt, 0,
+- "ETRAX 100LX built-in USB (Rx)", hc)) {
+- err("Could not allocate IRQ %d for USB", ETRAX_USB_RX_IRQ);
+- etrax_usb_hc_cleanup();
+- DBFEXIT;
+- return -1;
+- }
+-
+- if (request_irq(ETRAX_USB_TX_IRQ, etrax_usb_tx_interrupt, 0,
+- "ETRAX 100LX built-in USB (Tx)", hc)) {
+- err("Could not allocate IRQ %d for USB", ETRAX_USB_TX_IRQ);
+- etrax_usb_hc_cleanup();
+- DBFEXIT;
+- return -1;
+- }
+-
+- /* R_USB_COMMAND:
+- USB commands in host mode. The fields in this register should all be
+- written to in one write. Do not read-modify-write one field at a time. A
+- write to this register will trigger events in the USB controller and an
+- incomplete command may lead to unpredictable results, and in worst case
+- even to a deadlock in the controller.
+- (Note however that the busy field is read-only, so no need to write to it.) */
+-
+- /* Check the busy bit before writing to R_USB_COMMAND. */
+-
+- while (*R_USB_COMMAND & IO_MASK(R_USB_COMMAND, busy));
+-
+- /* Reset the USB interface. */
+- *R_USB_COMMAND =
+- IO_STATE(R_USB_COMMAND, port_sel, nop) |
+- IO_STATE(R_USB_COMMAND, port_cmd, reset) |
+- IO_STATE(R_USB_COMMAND, ctrl_cmd, reset);
+-
+- /* Designer's Reference, p. 8 - 10 says we should Initate R_USB_FM_PSTART to 0x2A30 (10800),
+- to guarantee that control traffic gets 10% of the bandwidth, and periodic transfer may
+- allocate the rest (90%). This doesn't work though. Read on for a lenghty explanation.
+-
+- While there is a difference between rev. 2 and rev. 3 of the ETRAX 100LX regarding the NAK
+- behaviour, it doesn't solve this problem. What happens is that a control transfer will not
+- be interrupted in its data stage when PSTART happens (the point at which periodic traffic
+- is started). Thus, if PSTART is set to 10800 and its IN or OUT token is NAKed until just before
+- PSTART happens, it will continue the IN/OUT transfer as long as it's ACKed. After it's done,
+- there may be too little time left for an isochronous transfer, causing an epid attention
+- interrupt due to perror. The work-around for this is to let the control transfers run at the
+- end of the frame instead of at the beginning, and will be interrupted just fine if it doesn't
+- fit into the frame. However, since there will *always* be a control transfer at the beginning
+- of the frame, regardless of what we set PSTART to, that transfer might be a 64-byte transfer
+- which consumes up to 15% of the frame, leaving only 85% for periodic traffic. The solution to
+- this would be to 'dummy allocate' 5% of the frame with the usb_claim_bandwidth function to make
+- sure that the periodic transfers that are inserted will always fit in the frame.
+-
+- The idea was suggested that a control transfer could be split up into several 8 byte transfers,
+- so that it would be interrupted by PSTART, but since this can't be done for an IN transfer this
+- hasn't been implemented.
+-
+- The value 11960 is chosen to be just after the SOF token, with a couple of bit times extra
+- for possible bit stuffing. */
+-
+- *R_USB_FM_PSTART = IO_FIELD(R_USB_FM_PSTART, value, 11960);
+-
+-#ifdef CONFIG_ETRAX_USB_HOST_PORT1
+- *R_USB_PORT1_DISABLE = IO_STATE(R_USB_PORT1_DISABLE, disable, no);
+-#endif
+-
+-#ifdef CONFIG_ETRAX_USB_HOST_PORT2
+- *R_USB_PORT2_DISABLE = IO_STATE(R_USB_PORT2_DISABLE, disable, no);
+-#endif
+-
+- while (*R_USB_COMMAND & IO_MASK(R_USB_COMMAND, busy));
+-
+- /* Configure the USB interface as a host controller. */
+- *R_USB_COMMAND =
+- IO_STATE(R_USB_COMMAND, port_sel, nop) |
+- IO_STATE(R_USB_COMMAND, port_cmd, reset) |
+- IO_STATE(R_USB_COMMAND, ctrl_cmd, host_config);
+-
+- /* Note: Do not reset any ports here. Await the port status interrupts, to have a controlled
+- sequence of resetting the ports. If we reset both ports now, and there are devices
+- on both ports, we will get a bus error because both devices will answer the set address
+- request. */
+-
+- while (*R_USB_COMMAND & IO_MASK(R_USB_COMMAND, busy));
+-
+- /* Start processing of USB traffic. */
+- *R_USB_COMMAND =
+- IO_STATE(R_USB_COMMAND, port_sel, nop) |
+- IO_STATE(R_USB_COMMAND, port_cmd, reset) |
+- IO_STATE(R_USB_COMMAND, ctrl_cmd, host_run);
+-
+- while (*R_USB_COMMAND & IO_MASK(R_USB_COMMAND, busy));
+-
+- usb_rh = usb_alloc_dev(NULL, hc->bus, 0);
+- hc->bus->root_hub = usb_rh;
+- usb_rh->state = USB_STATE_ADDRESS;
+- usb_rh->speed = USB_SPEED_FULL;
+- usb_rh->devnum = 1;
+- hc->bus->devnum_next = 2;
+- usb_rh->ep0.desc.wMaxPacketSize = __const_cpu_to_le16(64);
+- usb_get_device_descriptor(usb_rh, USB_DT_DEVICE_SIZE);
+- usb_new_device(usb_rh);
+-
+- DBFEXIT;
+-
+- return 0;
+-}
+-
+-static void etrax_usb_hc_cleanup(void)
+-{
+- DBFENTER;
+-
+- free_irq(ETRAX_USB_HC_IRQ, NULL);
+- free_irq(ETRAX_USB_RX_IRQ, NULL);
+- free_irq(ETRAX_USB_TX_IRQ, NULL);
+-
+- usb_deregister_bus(etrax_usb_bus);
+-
+- /* FIXME: call kmem_cache_destroy here? */
+-
+- DBFEXIT;
+-}
+-
+-module_init(etrax_usb_hc_init);
+-module_exit(etrax_usb_hc_cleanup);
+--- a/drivers/usb/host/hc_crisv10.h
++++ /dev/null
+@@ -1,289 +0,0 @@
+-#ifndef __LINUX_ETRAX_USB_H
+-#define __LINUX_ETRAX_USB_H
+-
+-#include <linux/types.h>
+-#include <linux/list.h>
+-
+-typedef struct USB_IN_Desc {
+- volatile __u16 sw_len;
+- volatile __u16 command;
+- volatile unsigned long next;
+- volatile unsigned long buf;
+- volatile __u16 hw_len;
+- volatile __u16 status;
+-} USB_IN_Desc_t;
+-
+-typedef struct USB_SB_Desc {
+- volatile __u16 sw_len;
+- volatile __u16 command;
+- volatile unsigned long next;
+- volatile unsigned long buf;
+- __u32 dummy;
+-} USB_SB_Desc_t;
+-
+-typedef struct USB_EP_Desc {
+- volatile __u16 hw_len;
+- volatile __u16 command;
+- volatile unsigned long sub;
+- volatile unsigned long next;
+- __u32 dummy;
+-} USB_EP_Desc_t;
+-
+-struct virt_root_hub {
+- int devnum;
+- void *urb;
+- void *int_addr;
+- int send;
+- int interval;
+- int numports;
+- struct timer_list rh_int_timer;
+- volatile __u16 wPortChange_1;
+- volatile __u16 wPortChange_2;
+- volatile __u16 prev_wPortStatus_1;
+- volatile __u16 prev_wPortStatus_2;
+-};
+-
+-struct etrax_usb_intr_traffic {
+- int sleeping;
+- int error;
+- struct wait_queue *wq;
+-};
+-
+-typedef struct etrax_usb_hc {
+- struct usb_bus *bus;
+- struct virt_root_hub rh;
+- struct etrax_usb_intr_traffic intr;
+-} etrax_hc_t;
+-
+-typedef enum {
+- STARTED,
+- NOT_STARTED,
+- UNLINK,
+- TRANSFER_DONE,
+- WAITING_FOR_DESCR_INTR
+-} etrax_usb_urb_state_t;
+-
+-
+-
+-typedef struct etrax_usb_urb_priv {
+- /* The first_sb field is used for freeing all SB descriptors belonging
+- to an urb. The corresponding ep descriptor's sub pointer cannot be
+- used for this since the DMA advances the sub pointer as it processes
+- the sb list. */
+- USB_SB_Desc_t *first_sb;
+- /* The last_sb field referes to the last SB descriptor that belongs to
+- this urb. This is important to know so we can free the SB descriptors
+- that ranges between first_sb and last_sb. */
+- USB_SB_Desc_t *last_sb;
+-
+- /* The rx_offset field is used in ctrl and bulk traffic to keep track
+- of the offset in the urb's transfer_buffer where incoming data should be
+- copied to. */
+- __u32 rx_offset;
+-
+- /* Counter used in isochronous transfers to keep track of the
+- number of packets received/transmitted. */
+- __u32 isoc_packet_counter;
+-
+- /* This field is used to pass information about the urb's current state between
+- the various interrupt handlers (thus marked volatile). */
+- volatile etrax_usb_urb_state_t urb_state;
+-
+- /* Connection between the submitted urb and ETRAX epid number */
+- __u8 epid;
+-
+- /* The rx_data_list field is used for periodic traffic, to hold
+- received data for later processing in the the complete_urb functions,
+- where the data us copied to the urb's transfer_buffer. Basically, we
+- use this intermediate storage because we don't know when it's safe to
+- reuse the transfer_buffer (FIXME?). */
+- struct list_head rx_data_list;
+-} etrax_urb_priv_t;
+-
+-/* This struct is for passing data from the top half to the bottom half. */
+-typedef struct usb_interrupt_registers
+-{
+- etrax_hc_t *hc;
+- __u32 r_usb_epid_attn;
+- __u8 r_usb_status;
+- __u16 r_usb_rh_port_status_1;
+- __u16 r_usb_rh_port_status_2;
+- __u32 r_usb_irq_mask_read;
+- __u32 r_usb_fm_number;
+- struct work_struct usb_bh;
+-} usb_interrupt_registers_t;
+-
+-/* This struct is for passing data from the isoc top half to the isoc bottom half. */
+-typedef struct usb_isoc_complete_data
+-{
+- struct urb *urb;
+- struct work_struct usb_bh;
+-} usb_isoc_complete_data_t;
+-
+-/* This struct holds data we get from the rx descriptors for DMA channel 9
+- for periodic traffic (intr and isoc). */
+-typedef struct rx_data
+-{
+- void *data;
+- int length;
+- struct list_head list;
+-} rx_data_t;
+-
+-typedef struct urb_entry
+-{
+- struct urb *urb;
+- struct list_head list;
+-} urb_entry_t;
+-
+-/* ---------------------------------------------------------------------------
+- Virtual Root HUB
+- ------------------------------------------------------------------------- */
+-/* destination of request */
+-#define RH_INTERFACE 0x01
+-#define RH_ENDPOINT 0x02
+-#define RH_OTHER 0x03
+-
+-#define RH_CLASS 0x20
+-#define RH_VENDOR 0x40
+-
+-/* Requests: bRequest << 8 | bmRequestType */
+-#define RH_GET_STATUS 0x0080
+-#define RH_CLEAR_FEATURE 0x0100
+-#define RH_SET_FEATURE 0x0300
+-#define RH_SET_ADDRESS 0x0500
+-#define RH_GET_DESCRIPTOR 0x0680
+-#define RH_SET_DESCRIPTOR 0x0700
+-#define RH_GET_CONFIGURATION 0x0880
+-#define RH_SET_CONFIGURATION 0x0900
+-#define RH_GET_STATE 0x0280
+-#define RH_GET_INTERFACE 0x0A80
+-#define RH_SET_INTERFACE 0x0B00
+-#define RH_SYNC_FRAME 0x0C80
+-/* Our Vendor Specific Request */
+-#define RH_SET_EP 0x2000
+-
+-
+-/* Hub port features */
+-#define RH_PORT_CONNECTION 0x00
+-#define RH_PORT_ENABLE 0x01
+-#define RH_PORT_SUSPEND 0x02
+-#define RH_PORT_OVER_CURRENT 0x03
+-#define RH_PORT_RESET 0x04
+-#define RH_PORT_POWER 0x08
+-#define RH_PORT_LOW_SPEED 0x09
+-#define RH_C_PORT_CONNECTION 0x10
+-#define RH_C_PORT_ENABLE 0x11
+-#define RH_C_PORT_SUSPEND 0x12
+-#define RH_C_PORT_OVER_CURRENT 0x13
+-#define RH_C_PORT_RESET 0x14
+-
+-/* Hub features */
+-#define RH_C_HUB_LOCAL_POWER 0x00
+-#define RH_C_HUB_OVER_CURRENT 0x01
+-
+-#define RH_DEVICE_REMOTE_WAKEUP 0x00
+-#define RH_ENDPOINT_STALL 0x01
+-
+-/* Our Vendor Specific feature */
+-#define RH_REMOVE_EP 0x00
+-
+-
+-#define RH_ACK 0x01
+-#define RH_REQ_ERR -1
+-#define RH_NACK 0x00
+-
+-/* Field definitions for */
+-
+-#define USB_IN_command__eol__BITNR 0 /* command macros */
+-#define USB_IN_command__eol__WIDTH 1
+-#define USB_IN_command__eol__no 0
+-#define USB_IN_command__eol__yes 1
+-
+-#define USB_IN_command__intr__BITNR 3
+-#define USB_IN_command__intr__WIDTH 1
+-#define USB_IN_command__intr__no 0
+-#define USB_IN_command__intr__yes 1
+-
+-#define USB_IN_status__eop__BITNR 1 /* status macros. */
+-#define USB_IN_status__eop__WIDTH 1
+-#define USB_IN_status__eop__no 0
+-#define USB_IN_status__eop__yes 1
+-
+-#define USB_IN_status__eot__BITNR 5
+-#define USB_IN_status__eot__WIDTH 1
+-#define USB_IN_status__eot__no 0
+-#define USB_IN_status__eot__yes 1
+-
+-#define USB_IN_status__error__BITNR 6
+-#define USB_IN_status__error__WIDTH 1
+-#define USB_IN_status__error__no 0
+-#define USB_IN_status__error__yes 1
+-
+-#define USB_IN_status__nodata__BITNR 7
+-#define USB_IN_status__nodata__WIDTH 1
+-#define USB_IN_status__nodata__no 0
+-#define USB_IN_status__nodata__yes 1
+-
+-#define USB_IN_status__epid__BITNR 8
+-#define USB_IN_status__epid__WIDTH 5
+-
+-#define USB_EP_command__eol__BITNR 0
+-#define USB_EP_command__eol__WIDTH 1
+-#define USB_EP_command__eol__no 0
+-#define USB_EP_command__eol__yes 1
+-
+-#define USB_EP_command__eof__BITNR 1
+-#define USB_EP_command__eof__WIDTH 1
+-#define USB_EP_command__eof__no 0
+-#define USB_EP_command__eof__yes 1
+-
+-#define USB_EP_command__intr__BITNR 3
+-#define USB_EP_command__intr__WIDTH 1
+-#define USB_EP_command__intr__no 0
+-#define USB_EP_command__intr__yes 1
+-
+-#define USB_EP_command__enable__BITNR 4
+-#define USB_EP_command__enable__WIDTH 1
+-#define USB_EP_command__enable__no 0
+-#define USB_EP_command__enable__yes 1
+-
+-#define USB_EP_command__hw_valid__BITNR 5
+-#define USB_EP_command__hw_valid__WIDTH 1
+-#define USB_EP_command__hw_valid__no 0
+-#define USB_EP_command__hw_valid__yes 1
+-
+-#define USB_EP_command__epid__BITNR 8
+-#define USB_EP_command__epid__WIDTH 5
+-
+-#define USB_SB_command__eol__BITNR 0 /* command macros. */
+-#define USB_SB_command__eol__WIDTH 1
+-#define USB_SB_command__eol__no 0
+-#define USB_SB_command__eol__yes 1
+-
+-#define USB_SB_command__eot__BITNR 1
+-#define USB_SB_command__eot__WIDTH 1
+-#define USB_SB_command__eot__no 0
+-#define USB_SB_command__eot__yes 1
+-
+-#define USB_SB_command__intr__BITNR 3
+-#define USB_SB_command__intr__WIDTH 1
+-#define USB_SB_command__intr__no 0
+-#define USB_SB_command__intr__yes 1
+-
+-#define USB_SB_command__tt__BITNR 4
+-#define USB_SB_command__tt__WIDTH 2
+-#define USB_SB_command__tt__zout 0
+-#define USB_SB_command__tt__in 1
+-#define USB_SB_command__tt__out 2
+-#define USB_SB_command__tt__setup 3
+-
+-
+-#define USB_SB_command__rem__BITNR 8
+-#define USB_SB_command__rem__WIDTH 6
+-
+-#define USB_SB_command__full__BITNR 6
+-#define USB_SB_command__full__WIDTH 1
+-#define USB_SB_command__full__no 0
+-#define USB_SB_command__full__yes 1
+-
+-#endif
diff --git a/usb/usb-remove-duplicate-define-of-ohci_quirk_zfmicro.patch b/usb/usb-remove-duplicate-define-of-ohci_quirk_zfmicro.patch
new file mode 100644
index 0000000..015b24f
--- /dev/null
+++ b/usb/usb-remove-duplicate-define-of-ohci_quirk_zfmicro.patch
@@ -0,0 +1,33 @@
+From akpm@linux-foundation.org Fri Apr 27 11:05:06 2007
+From: "S.Caglar Onur" <caglar@pardus.org.tr>
+Date: Thu, 26 Apr 2007 00:38:03 -0700
+Subject: USB: Remove duplicate define of OHCI_QUIRK_ZFMICRO
+To: greg@kroah.com
+Cc: linux-usb-devel@lists.sourceforge.net, akpm@linux-foundation.org, caglar@pardus.org.tr, tony.olech@elandigitalsystems.com
+Message-ID: <200704260738.l3Q7c3RZ024376@shell0.pdx.osdl.net>
+
+
+From: "S.Caglar Onur" <caglar@pardus.org.tr>
+
+Remove duplicate define of OHCI_QUIRK_ZFMICRO from ftdi-elan.c, its already
+defined in drivers/ush/host/ohci.c
+
+Signed-off-by: "S.Caglar Onur" <caglar@pardus.org.tr>
+Cc: <tony.olech@elandigitalsystems.com>
+Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
+
+---
+ drivers/usb/misc/ftdi-elan.c | 1 -
+ 1 file changed, 1 deletion(-)
+
+--- a/drivers/usb/misc/ftdi-elan.c
++++ b/drivers/usb/misc/ftdi-elan.c
+@@ -2304,7 +2304,6 @@ static int ftdi_elan_checkingPCI(struct
+ #define OHCI_QUIRK_SUPERIO 0x02
+ #define OHCI_QUIRK_INITRESET 0x04
+ #define OHCI_BIG_ENDIAN 0x08
+-#define OHCI_QUIRK_ZFMICRO 0x10
+ #define OHCI_CONTROL_INIT OHCI_CTRL_CBSR
+ #define OHCI_INTR_INIT (OHCI_INTR_MIE | OHCI_INTR_UE | OHCI_INTR_RD | \
+ OHCI_INTR_WDH)
diff --git a/usb/usb-remove-huawei-unusual_devs-entry.patch b/usb/usb-remove-huawei-unusual_devs-entry.patch
new file mode 100644
index 0000000..2f7de06
--- /dev/null
+++ b/usb/usb-remove-huawei-unusual_devs-entry.patch
@@ -0,0 +1,44 @@
+From phil@ipom.com Fri Apr 27 11:11:32 2007
+From: Phil Dibowitz <phil@ipom.com>
+Date: Sun, 15 Apr 2007 23:42:40 -0700
+Subject: USB: Remove Huawei unusual_devs entry
+To: Greg KH <greg@kroah.com>
+Cc: USB development list <linux-usb-devel@lists.sourceforge.net>, USB Storage list <usb-storage@lists.one-eyed-alien.net>, Rui Santos <rsantos@grupopie.com>, johann.wilhelm@student.tugraz.at, zihan@huawei.com, wanganyu1983@huawei.com, dingjianjian@huawei.com
+Message-ID: <46231AE0.1060203@ipom.com>
+
+
+Per the Rui Santos and the hardware manufacturers, this actually inhibits
+useful parts of the hardware. The correct way to use this hardware is with the
+software at http://www.kanoistika.sk/bobovsky/archiv/umts/ and the manufacturers
+are also planning on including Linux drivers/material in future revisions.
+
+CC: Rui Santos <rsantos@grupopie.com>
+CC: <johann.wilhelm@student.tugraz.at>
+CC: <zihan@huawei.com>
+CC: <wanganyu1983@huawei.com>
+CC: <dingjianjian@huawei.com>
+Signed-off-by: Phil Dibowitz <phil@ipom.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
+
+---
+ drivers/usb/storage/unusual_devs.h | 9 ---------
+ 1 file changed, 9 deletions(-)
+
+--- a/drivers/usb/storage/unusual_devs.h
++++ b/drivers/usb/storage/unusual_devs.h
+@@ -1371,15 +1371,6 @@ UNUSUAL_DEV( 0x1210, 0x0003, 0x0100, 0x
+ US_SC_DEVICE, US_PR_DEVICE, NULL,
+ US_FL_IGNORE_RESIDUE ),
+
+-/* This prevents the kernel from detecting the virtual cd-drive with the
+- * Windows drivers. <johann.wilhelm@student.tugraz.at>
+-*/
+-UNUSUAL_DEV( 0x12d1, 0x1003, 0x0000, 0xffff,
+- "HUAWEI",
+- "E220 USB-UMTS Install",
+- US_SC_DEVICE, US_PR_DEVICE, NULL,
+- US_FL_IGNORE_DEVICE),
+-
+ /* Reported by Vilius Bilinkevicius <vilisas AT xxx DOT lt) */
+ UNUSUAL_DEV( 0x132b, 0x000b, 0x0001, 0x0001,
+ "Minolta",
diff --git a/usb/usb-update-gadget-files-for-fsl_usb2_udc-driver.patch b/usb/usb-update-gadget-files-for-fsl_usb2_udc-driver.patch
new file mode 100644
index 0000000..b6900a7
--- /dev/null
+++ b/usb/usb-update-gadget-files-for-fsl_usb2_udc-driver.patch
@@ -0,0 +1,59 @@
+From david-b@pacbell.net Mon Apr 23 10:37:44 2007
+From: Li Yang <leoli@freescale.com>
+Date: Mon, 23 Apr 2007 10:37:36 -0700
+Subject: USB: update gadget files for fsl_usb2_udc driver
+To: Greg KH <greg@kroah.com>, Li Yang <leoli@freescale.com>
+Message-ID: <200704231037.36528.david-b@pacbell.net>
+Content-Disposition: inline
+
+
+From: Li Yang <leoli@freescale.com>
+
+Update gadget_chip.c, ether.c for newly added Freescale Highspeed USB
+device driver.
+
+Signed-off-by: Li Yang <leoli@freescale.com>
+Signed-off-by: David Brownell <dbrownell@users.sourceforge.net>
+Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
+
+
+---
+ drivers/usb/gadget/ether.c | 3 +++
+ drivers/usb/gadget/gadget_chips.h | 8 ++++++++
+ 2 files changed, 11 insertions(+)
+
+--- a/drivers/usb/gadget/ether.c
++++ b/drivers/usb/gadget/ether.c
+@@ -282,6 +282,9 @@ MODULE_PARM_DESC(host_addr, "Host Ethern
+ #define DEV_CONFIG_CDC
+ #endif
+
++#ifdef CONFIG_USB_GADGET_FSL_USB2
++#define DEV_CONFIG_CDC
++#endif
+
+ /* For CDC-incapable hardware, choose the simple cdc subset.
+ * Anything that talks bulk (without notable bugs) can do this.
+--- a/drivers/usb/gadget/gadget_chips.h
++++ b/drivers/usb/gadget/gadget_chips.h
+@@ -99,6 +99,12 @@
+ #define gadget_is_imx(g) 0
+ #endif
+
++#ifdef CONFIG_USB_GADGET_FSL_USB2
++#define gadget_is_fsl_usb2(g) !strcmp("fsl-usb2-udc", (g)->name)
++#else
++#define gadget_is_fsl_usb2(g) 0
++#endif
++
+ /* Mentor high speed function controller */
+ #ifdef CONFIG_USB_GADGET_MUSBHSFC
+ #define gadget_is_musbhsfc(g) !strcmp("musbhsfc_udc", (g)->name)
+@@ -177,5 +183,7 @@ static inline int usb_gadget_controller_
+ return 0x17;
+ else if (gadget_is_husb2dev(gadget))
+ return 0x18;
++ else if (gadget_is_fsl_usb2(gadget))
++ return 0x19;
+ return -ENOENT;
+ }
diff --git a/usb/usb-update-mainainers-and-credits-for-freescale-usb-driver.patch b/usb/usb-update-mainainers-and-credits-for-freescale-usb-driver.patch
new file mode 100644
index 0000000..50d8f56
--- /dev/null
+++ b/usb/usb-update-mainainers-and-credits-for-freescale-usb-driver.patch
@@ -0,0 +1,57 @@
+From david-b@pacbell.net Mon Apr 23 10:38:29 2007
+From: Li Yang <leoli@freescale.com>
+Date: Mon, 23 Apr 2007 10:38:18 -0700
+Subject: USB: update MAINAINERS and CREDITS for Freescale USB driver
+To: Greg KH <greg@kroah.com>
+Cc: Li Yang <leoli@freescale.com>
+Message-ID: <200704231038.18792.david-b@pacbell.net>
+Content-Disposition: inline
+
+From: Li Yang <leoli@freescale.com>
+
+Add MAINAINERS and CREDITS entry for Freescale Highspeed USB device
+driver.
+
+Signed-off-by: Li Yang <leoli@freescale.com>
+Signed-off-by: David Brownell <dbrownell@users.sourceforge.net>
+Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
+
+
+---
+ CREDITS | 8 ++++++++
+ MAINTAINERS | 7 +++++++
+ 2 files changed, 15 insertions(+)
+
+--- a/CREDITS
++++ b/CREDITS
+@@ -3295,6 +3295,14 @@ S: 12725 SW Millikan Way, Suite 400
+ S: Beaverton, Oregon 97005
+ S: USA
+
++N: Li Yang
++E: leoli@freescale.com
++D: Freescale Highspeed USB device driver
++D: Freescale QE SoC support and Ethernet driver
++S: B-1206 Jingmao Guojigongyu
++S: 16 Baliqiao Nanjie, Beijing 101100
++S: People's Repulic of China
++
+ N: Marcelo Tosatti
+ E: marcelo@kvack.org
+ D: v2.4 kernel maintainer
+--- a/MAINTAINERS
++++ b/MAINTAINERS
+@@ -1381,6 +1381,13 @@ L: linuxppc-embedded@ozlabs.org
+ L: netdev@vger.kernel.org
+ S: Maintained
+
++FREESCALE HIGHSPEED USB DEVICE DRIVER
++P: Li Yang
++M: leoli@freescale.com
++L: linux-usb-devel@lists.sourceforge.net
++L: linuxppc-embedded@ozlabs.org
++S: Maintained
++
+ FILE LOCKING (flock() and fcntl()/lockf())
+ P: Matthew Wilcox
+ M: matthew@wil.cx
diff --git a/usb/usbatm-detect-usb-device-shutdown-and-ignore-failed-urbs.patch b/usb/usbatm-detect-usb-device-shutdown-and-ignore-failed-urbs.patch
new file mode 100644
index 0000000..7f7edd7
--- /dev/null
+++ b/usb/usbatm-detect-usb-device-shutdown-and-ignore-failed-urbs.patch
@@ -0,0 +1,39 @@
+From akpm@linux-foundation.org Thu Apr 26 00:38:30 2007
+From: Simon Arlott <simon@fire.lp0.eu>
+Date: Thu, 26 Apr 2007 00:38:04 -0700
+Subject: usbatm: Detect usb device shutdown and ignore failed urbs
+To: greg@kroah.com
+Cc: linux-usb-devel@lists.sourceforge.net, akpm@linux-foundation.org, simon@fire.lp0.eu, duncan.sands@math.u-psud.fr
+Message-ID: <200704260738.l3Q7c5l5024381@shell0.pdx.osdl.net>
+
+
+From: Simon Arlott <simon@fire.lp0.eu>
+
+Detect usb device shutdown and ignore failed urbs. This happens when the
+driver is unloaded or the device is unplugged.
+
+I'm not sure what other urb statuses should be ignored, and the warning
+message doesn't need to be shown when the module is unloaded or the device
+is removed.
+
+Signed-off-by: Simon Arlott <simon@fire.lp0.eu>
+Cc: Duncan Sands <duncan.sands@math.u-psud.fr>
+Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
+
+---
+ drivers/usb/atm/usbatm.c | 3 +++
+ 1 file changed, 3 insertions(+)
+
+--- a/drivers/usb/atm/usbatm.c
++++ b/drivers/usb/atm/usbatm.c
+@@ -274,6 +274,9 @@ static void usbatm_complete(struct urb *
+ (!(channel->usbatm->flags & UDSL_IGNORE_EILSEQ) ||
+ urb->status != -EILSEQ ))
+ {
++ if (urb->status == -ESHUTDOWN)
++ return;
++
+ if (printk_ratelimit())
+ atm_warn(channel->usbatm, "%s: urb 0x%p failed (%d)!\n",
+ __func__, urb, urb->status);
diff --git a/usb/usbfs-micro-optimitation.patch b/usb/usbfs-micro-optimitation.patch
new file mode 100644
index 0000000..01274fc
--- /dev/null
+++ b/usb/usbfs-micro-optimitation.patch
@@ -0,0 +1,29 @@
+From oliver@neukum.org Fri Apr 20 11:51:02 2007
+From: Oliver Neukum <oliver@neukum.org>
+Date: Fri, 20 Apr 2007 20:50:48 +0200
+Subject: usbfs micro optimitation
+To: gregkh@suse.de, linux-usb-devel@lists.sourceforge.net
+Message-ID: <200704202050.49246.oliver@neukum.org>
+Content-Disposition: inline
+
+
+the memory barrier is needed only with smp.
+
+Signed-off-by: Oliver Neukum <oneukum@suse.de>
+Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
+
+---
+ drivers/usb/core/devio.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+--- a/drivers/usb/core/devio.c
++++ b/drivers/usb/core/devio.c
+@@ -580,7 +580,7 @@ static int usbdev_open(struct inode *ino
+ ps->disccontext = NULL;
+ ps->ifclaimed = 0;
+ security_task_getsecid(current, &ps->secid);
+- wmb();
++ smp_wmb();
+ list_add_tail(&ps->list, &dev->filelist);
+ file->private_data = ps;
+ out: