aboutsummaryrefslogtreecommitdiffstats
path: root/usb
diff options
authorGreg Kroah-Hartman <gregkh@suse.de>2009-12-22 12:22:11 -0800
committerGreg Kroah-Hartman <gregkh@suse.de>2009-12-22 12:22:11 -0800
commit52a248c714fb17267c89520a4a854b0f65de998c (patch)
treeb08803dad70c8bfeba8a20e4c06b24ea6ce5cf9d /usb
parentae75e9fa49d0b2a2a776794dad9e420e7bc269f2 (diff)
downloadpatches-52a248c714fb17267c89520a4a854b0f65de998c.tar.gz
huge catchup on pending patches
Diffstat (limited to 'usb')
-rw-r--r--usb/cdc_acm-add-reset_resume-method.patch53
-rw-r--r--usb/usb-add-call-to-notify-xhc-of-a-device-reset.patch98
-rw-r--r--usb/usb-at91_udc.c-use-resource_size.patch58
-rw-r--r--usb/usb-atmel_usba_udc.c-use-resource_size.patch37
-rw-r--r--usb/usb-cdc-acm-update-to-new-autopm-api.patch125
-rw-r--r--usb/usb-check-the-endpoint-type-against-the-pipe-type.patch111
-rw-r--r--usb/usb-cxacru-add-write-only-sysfs-attribute-for-modem-configuration.patch164
-rw-r--r--usb/usb-cxacru-check-data-length-is-not-negative.patch34
-rw-r--r--usb/usb-cxacru-check-device-isn-t-being-removed-during-sysfs-calls.patch166
-rw-r--r--usb/usb-cxacru-document-how-to-interact-with-the-flash-memory.patch51
-rw-r--r--usb/usb-cxacru-firmware-writes-on-ohci-are-slow-log-progress.patch70
-rw-r--r--usb/usb-cxacru-increment-driver-version.patch42
-rw-r--r--usb/usb-cxacru-remove-cxacru-cf.bin-loader.patch174
-rw-r--r--usb/usb-cxacru-return-an-empty-value-for-modulation-if-there-is-no-connection.patch46
-rw-r--r--usb/usb-cypress_m8-allow-unstable-baud-rates.patch49
-rw-r--r--usb/usb-cypress_m8-stop-using-usb-debug-driver-config.patch34
-rw-r--r--usb/usb-cypress_m8-unify-confusing-new-baudrate-check.patch41
-rw-r--r--usb/usb-ehci-add-call-of-free_cached_itd_list-function-in-disable_periodic.patch36
-rw-r--r--usb/usb-ehci-atmel.c-use-resource_size.patch29
-rw-r--r--usb/usb-ehci-au1xxx.c-use-platform_get_resource-and-resource_size.patch41
-rw-r--r--usb/usb-ehci-fix-audio-record-functionality-for-some-full-speed-sound-blaster-devices.patch45
-rw-r--r--usb/usb-ehci-fsl-add-power-management-support.patch155
-rw-r--r--usb/usb-ehci-fsl-fix-sparse-warnings.patch45
-rw-r--r--usb/usb-ehci-orion.c-use-resource_size.patch55
-rw-r--r--usb/usb-export-usb_quirk_reset_morphs-through-sysfs.patch67
-rw-r--r--usb/usb-fix-occasional-ulpi-timeouts-with-ehci-mxc.patch59
-rw-r--r--usb/usb-ftdi_sio-fix-error-message-on-close.patch29
-rw-r--r--usb/usb-ftdi_sio-fix-initialisation-of-latency-timeout.patch52
-rw-r--r--usb/usb-ftdi_sio-remove-obsolete-comment.patch30
-rw-r--r--usb/usb-ftdi_sio-remove-support-for-5-and-6-data-bits.patch37
-rw-r--r--usb/usb-gadget-add-int-support-for-blackfin-musb.patch33
-rw-r--r--usb/usb-gadget-use-ep5-for-bulk-in-and-ep6-for-bulk-out-for-blackfin-musb.patch50
-rw-r--r--usb/usb-host-sl811-allow-the-hcd-on-blackfin-systems.patch30
-rw-r--r--usb/usb-host-sl811-fix-unaligned-accesses.patch45
-rw-r--r--usb/usb-isp1362-use-kzalloc-for-allocating-only-one-thing.patch42
-rw-r--r--usb/usb-mxc-add-i.mx21-specific-usb-host-controller-driver.patch2862
-rw-r--r--usb/usb-mxc-add-platform-resources-for-i.mx21-usb-host-controller.patch61
-rw-r--r--usb/usb-mxc-use-dma_bit_mask-macro-rather-than-hardcoded-constants.patch155
-rw-r--r--usb/usb-otg-add-notifier-support.patch82
-rw-r--r--usb/usb-otg-isp1301_omap-fix-compile-error.patch41
-rw-r--r--usb/usb-otg-twl4030-add-support-for-notifier.patch128
-rw-r--r--usb/usb-pxa27x_udc-avoid-compiler-warnings-and-misbehavior-on-buggy-hardware.patch61
-rw-r--r--usb/usb-pxa27x_udc.c-use-resource_size.patch30
-rw-r--r--usb/usb-serial-fix-typo-in-debug-message.patch28
-rw-r--r--usb/usb-serial-mct_usb232-add-drain-on-close.patch73
-rw-r--r--usb/usb-serial-mct_usb232-move-dma-buffers-to-heap.patch190
-rw-r--r--usb/usb-storage-never-reset-devices-that-will-morph-to-an-old-mode.patch64
-rw-r--r--usb/usb-wusb-check-chid-is-all-zeros-before-stopping-the-host.patch35
-rw-r--r--usb/usb-xhci-allow-allocation-of-commands-without-input-contexts.patch91
-rw-r--r--usb/usb-xhci-allow-roothub-ports-to-be-disabled.patch66
-rw-r--r--usb/usb-xhci-fix-error-path-when-configuring-endpoints.patch32
-rw-r--r--usb/usb-xhci-no-gfp_kernel-in-block-error-handling.patch32
-rw-r--r--usb/usb-xhci-notify-the-xhc-when-a-device-is-reset.patch257
-rw-r--r--usb/usb-xhci-refactor-code-to-clear-port-change-bits.patch100
-rw-r--r--usb/usb-xhci-refactor-code-to-free-or-cache-endpoint-rings.patch103
-rw-r--r--usb/usb-xhci-refactor-test-for-vendor-specific-completion-codes.patch70
56 files changed, 6794 insertions, 0 deletions
diff --git a/usb/cdc_acm-add-reset_resume-method.patch b/usb/cdc_acm-add-reset_resume-method.patch
new file mode 100644
index 00000000000000..a1322bfae021e6
--- /dev/null
+++ b/usb/cdc_acm-add-reset_resume-method.patch
@@ -0,0 +1,53 @@
+From francescolavra@interfree.it Tue Dec 22 11:32:56 2009
+From: Francesco Lavra <francescolavra@interfree.it>
+Date: Tue, 08 Dec 2009 09:54:11 +0100
+Subject: Re: [PATCH] cdc_acm: add reset_resume method
+To: Oliver Neukum <oliver@neukum.org>
+Cc: Alan Cox <alan@lxorguk.ukuu.org.uk>, linux-usb@vger.kernel.org
+Message-ID: <1260262451.23982.3.camel@localhost>
+
+
+Add reset resume logic to the cdc acm driver
+
+Signed-off-by: Francesco Lavra <francescolavra@interfree.it>
+Acked-by: Oliver Neukum <oliver@neukum.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
+
+---
+ drivers/usb/class/cdc-acm.c | 18 ++++++++++++++++++
+ 1 file changed, 18 insertions(+)
+
+--- a/drivers/usb/class/cdc-acm.c
++++ b/drivers/usb/class/cdc-acm.c
+@@ -1460,6 +1460,23 @@ err_out:
+ return rv;
+ }
+
++static int acm_reset_resume(struct usb_interface *intf)
++{
++ struct acm *acm = usb_get_intfdata(intf);
++ struct tty_struct *tty;
++
++ mutex_lock(&acm->mutex);
++ if (acm->port.count) {
++ tty = tty_port_tty_get(&acm->port);
++ if (tty) {
++ tty_hangup(tty);
++ tty_kref_put(tty);
++ }
++ }
++ mutex_unlock(&acm->mutex);
++ return acm_resume(intf);
++}
++
+ #endif /* CONFIG_PM */
+
+ #define NOKIA_PCSUITE_ACM_INFO(x) \
+@@ -1602,6 +1619,7 @@ static struct usb_driver acm_driver = {
+ #ifdef CONFIG_PM
+ .suspend = acm_suspend,
+ .resume = acm_resume,
++ .reset_resume = acm_reset_resume,
+ #endif
+ .id_table = acm_ids,
+ #ifdef CONFIG_PM
diff --git a/usb/usb-add-call-to-notify-xhc-of-a-device-reset.patch b/usb/usb-add-call-to-notify-xhc-of-a-device-reset.patch
new file mode 100644
index 00000000000000..879159d0fdfeb0
--- /dev/null
+++ b/usb/usb-add-call-to-notify-xhc-of-a-device-reset.patch
@@ -0,0 +1,98 @@
+From sarah.a.sharp@linux.intel.com Tue Dec 22 11:48:32 2009
+From: Sarah Sharp <sarah.a.sharp@linux.intel.com>
+Date: Wed, 9 Dec 2009 15:59:17 -0800
+Subject: USB: Add call to notify xHC of a device reset.
+To: Greg KH <gregkh@suse.de>
+Cc: linux-usb@vger.kernel.org
+Message-ID: <20091209235917.GA19912@xanatos>
+
+
+Add a new host controller driver method, reset_device(), that the USB core
+will use to notify the host of a successful device reset. The call may
+fail due to out-of-memory errors; attempt the port reset sequence again if
+that happens. Update hub_port_init() to allow resetting a configured
+device.
+
+Signed-off-by: Sarah Sharp <sarah.a.sharp@linux.intel.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
+
+---
+ drivers/usb/core/hcd.h | 1 +
+ drivers/usb/core/hub.c | 19 +++++++++++--------
+ drivers/usb/host/xhci-pci.c | 1 +
+ drivers/usb/host/xhci.h | 1 +
+ 4 files changed, 14 insertions(+), 8 deletions(-)
+
+--- a/drivers/usb/core/hcd.h
++++ b/drivers/usb/core/hcd.h
+@@ -286,6 +286,7 @@ struct hc_driver {
+ */
+ int (*update_hub_device)(struct usb_hcd *, struct usb_device *hdev,
+ struct usb_tt *tt, gfp_t mem_flags);
++ int (*reset_device)(struct usb_hcd *, struct usb_device *);
+ };
+
+ extern int usb_hcd_link_urb_to_ep(struct usb_hcd *hcd, struct urb *urb);
+--- a/drivers/usb/core/hub.c
++++ b/drivers/usb/core/hub.c
+@@ -2007,7 +2007,9 @@ static int hub_port_reset(struct usb_hub
+ struct usb_device *udev, unsigned int delay)
+ {
+ int i, status;
++ struct usb_hcd *hcd;
+
++ hcd = bus_to_hcd(udev->bus);
+ /* Block EHCI CF initialization during the port reset.
+ * Some companion controllers don't like it when they mix.
+ */
+@@ -2035,6 +2037,14 @@ static int hub_port_reset(struct usb_hub
+ /* TRSTRCY = 10 ms; plus some extra */
+ msleep(10 + 40);
+ update_address(udev, 0);
++ if (hcd->driver->reset_device) {
++ status = hcd->driver->reset_device(hcd, udev);
++ if (status < 0) {
++ dev_err(&udev->dev, "Cannot reset "
++ "HCD device state\n");
++ break;
++ }
++ }
+ /* FALL THROUGH */
+ case -ENOTCONN:
+ case -ENODEV:
+@@ -2644,14 +2654,7 @@ hub_port_init (struct usb_hub *hub, stru
+
+ mutex_lock(&usb_address0_mutex);
+
+- if ((hcd->driver->flags & HCD_USB3) && udev->config) {
+- /* FIXME this will need special handling by the xHCI driver. */
+- dev_dbg(&udev->dev,
+- "xHCI reset of configured device "
+- "not supported yet.\n");
+- retval = -EINVAL;
+- goto fail;
+- } else if (!udev->config && oldspeed == USB_SPEED_SUPER) {
++ if (!udev->config && oldspeed == USB_SPEED_SUPER) {
+ /* Don't reset USB 3.0 devices during an initial setup */
+ usb_set_device_state(udev, USB_STATE_DEFAULT);
+ } else {
+--- a/drivers/usb/host/xhci.h
++++ b/drivers/usb/host/xhci.h
+@@ -1270,6 +1270,7 @@ int xhci_urb_dequeue(struct usb_hcd *hcd
+ int xhci_add_endpoint(struct usb_hcd *hcd, struct usb_device *udev, struct usb_host_endpoint *ep);
+ int xhci_drop_endpoint(struct usb_hcd *hcd, struct usb_device *udev, struct usb_host_endpoint *ep);
+ void xhci_endpoint_reset(struct usb_hcd *hcd, struct usb_host_endpoint *ep);
++int xhci_reset_device(struct usb_hcd *hcd, struct usb_device *udev);
+ int xhci_check_bandwidth(struct usb_hcd *hcd, struct usb_device *udev);
+ void xhci_reset_bandwidth(struct usb_hcd *hcd, struct usb_device *udev);
+
+--- a/drivers/usb/host/xhci-pci.c
++++ b/drivers/usb/host/xhci-pci.c
+@@ -139,6 +139,7 @@ static const struct hc_driver xhci_pci_h
+ .reset_bandwidth = xhci_reset_bandwidth,
+ .address_device = xhci_address_device,
+ .update_hub_device = xhci_update_hub_device,
++ .reset_device = xhci_reset_device,
+
+ /*
+ * scheduling support
diff --git a/usb/usb-at91_udc.c-use-resource_size.patch b/usb/usb-at91_udc.c-use-resource_size.patch
new file mode 100644
index 00000000000000..e377f178b27aa4
--- /dev/null
+++ b/usb/usb-at91_udc.c-use-resource_size.patch
@@ -0,0 +1,58 @@
+From hartleys@visionengravers.com Tue Dec 22 11:54:05 2009
+From: "H Hartley Sweeten" <hartleys@visionengravers.com>
+Date: Mon, 14 Dec 2009 17:59:22 -0500
+Subject: USB: at91_udc.c: use resource_size()
+Cc: "David Brownell" <dbrownell@users.sourceforge.net>
+Message-ID: <BD79186B4FD85F4B8E60E381CAEE19090200EA9B@mi8nycmail19.Mi8.com>
+
+
+Use resource_size().
+
+Signed-off-by: H Hartley Sweeten <hsweeten@visionengravers.com>
+Cc: David Brownell <dbrownell@users.sourceforge.net>
+Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
+
+---
+ drivers/usb/gadget/at91_udc.c | 10 ++++------
+ 1 file changed, 4 insertions(+), 6 deletions(-)
+
+--- a/drivers/usb/gadget/at91_udc.c
++++ b/drivers/usb/gadget/at91_udc.c
+@@ -1656,9 +1656,7 @@ static int __init at91udc_probe(struct p
+ if (!res)
+ return -ENXIO;
+
+- if (!request_mem_region(res->start,
+- res->end - res->start + 1,
+- driver_name)) {
++ if (!request_mem_region(res->start, resource_size(res), driver_name)) {
+ DBG("someone's using UDC memory\n");
+ return -EBUSY;
+ }
+@@ -1699,7 +1697,7 @@ static int __init at91udc_probe(struct p
+ udc->ep[3].maxpacket = 64;
+ }
+
+- udc->udp_baseaddr = ioremap(res->start, res->end - res->start + 1);
++ udc->udp_baseaddr = ioremap(res->start, resource_size(res));
+ if (!udc->udp_baseaddr) {
+ retval = -ENOMEM;
+ goto fail0a;
+@@ -1781,7 +1779,7 @@ fail0a:
+ if (cpu_is_at91rm9200())
+ gpio_free(udc->board.pullup_pin);
+ fail0:
+- release_mem_region(res->start, res->end - res->start + 1);
++ release_mem_region(res->start, resource_size(res));
+ DBG("%s probe failed, %d\n", driver_name, retval);
+ return retval;
+ }
+@@ -1813,7 +1811,7 @@ static int __exit at91udc_remove(struct
+ gpio_free(udc->board.pullup_pin);
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+- release_mem_region(res->start, res->end - res->start + 1);
++ release_mem_region(res->start, resource_size(res));
+
+ clk_put(udc->iclk);
+ clk_put(udc->fclk);
diff --git a/usb/usb-atmel_usba_udc.c-use-resource_size.patch b/usb/usb-atmel_usba_udc.c-use-resource_size.patch
new file mode 100644
index 00000000000000..f9f4f88a7a3857
--- /dev/null
+++ b/usb/usb-atmel_usba_udc.c-use-resource_size.patch
@@ -0,0 +1,37 @@
+From hartleys@visionengravers.com Tue Dec 22 11:53:53 2009
+From: "H Hartley Sweeten" <hartleys@visionengravers.com>
+Date: Mon, 14 Dec 2009 18:04:49 -0500
+Subject: USB: atmel_usba_udc.c: use resource_size()
+Cc: <haavard.skinnemoen@atmel.com>, "David Brownell" <dbrownell@users.sourceforge.net>
+Message-ID: <BD79186B4FD85F4B8E60E381CAEE19090200EA9D@mi8nycmail19.Mi8.com>
+
+
+Use resource_size().
+
+Signed-off-by: H Hartley Sweeten <hsweeten@visionengravers.com>
+Cc: David Brownell <dbrownell@users.sourceforge.net>
+Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
+
+---
+ drivers/usb/gadget/atmel_usba_udc.c | 4 ++--
+ 1 file changed, 2 insertions(+), 2 deletions(-)
+
+--- a/drivers/usb/gadget/atmel_usba_udc.c
++++ b/drivers/usb/gadget/atmel_usba_udc.c
+@@ -1914,14 +1914,14 @@ static int __init usba_udc_probe(struct
+ udc->vbus_pin = -ENODEV;
+
+ ret = -ENOMEM;
+- udc->regs = ioremap(regs->start, regs->end - regs->start + 1);
++ udc->regs = ioremap(regs->start, resource_size(regs));
+ if (!udc->regs) {
+ dev_err(&pdev->dev, "Unable to map I/O memory, aborting.\n");
+ goto err_map_regs;
+ }
+ dev_info(&pdev->dev, "MMIO registers at 0x%08lx mapped at %p\n",
+ (unsigned long)regs->start, udc->regs);
+- udc->fifo = ioremap(fifo->start, fifo->end - fifo->start + 1);
++ udc->fifo = ioremap(fifo->start, resource_size(fifo));
+ if (!udc->fifo) {
+ dev_err(&pdev->dev, "Unable to map FIFO, aborting.\n");
+ goto err_map_fifo;
diff --git a/usb/usb-cdc-acm-update-to-new-autopm-api.patch b/usb/usb-cdc-acm-update-to-new-autopm-api.patch
new file mode 100644
index 00000000000000..75672cac8a4cb0
--- /dev/null
+++ b/usb/usb-cdc-acm-update-to-new-autopm-api.patch
@@ -0,0 +1,125 @@
+From oliver@neukum.org Tue Dec 22 11:56:05 2009
+From: Oliver Neukum <oliver@neukum.org>
+Date: Wed, 16 Dec 2009 17:05:57 +0100
+Subject: USB: cdc-acm: Update to new autopm API
+To: "Greg Kroah-Hartman" <gregkh@suse.de>, linux-usb@vger.kernel.org
+Message-ID: <200912161705.57231.oliver@neukum.org>
+
+
+From: Oliver Neukum <oliver@neukum.org>
+
+Update cdc-acm to the async methods eliminating the workqueue
+
+Signed-off-by: Oliver Neukum <oliver@neukum.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
+
+---
+ drivers/usb/class/cdc-acm.c | 43 ++++++++++++++++++++++---------------------
+ drivers/usb/class/cdc-acm.h | 1 -
+ 2 files changed, 22 insertions(+), 22 deletions(-)
+
+--- a/drivers/usb/class/cdc-acm.c
++++ b/drivers/usb/class/cdc-acm.c
+@@ -170,6 +170,7 @@ static void acm_write_done(struct acm *a
+ {
+ wb->use = 0;
+ acm->transmitting--;
++ usb_autopm_put_interface_async(acm->control);
+ }
+
+ /*
+@@ -211,9 +212,12 @@ static int acm_write_start(struct acm *a
+ }
+
+ dbg("%s susp_count: %d", __func__, acm->susp_count);
++ usb_autopm_get_interface_async(acm->control);
+ if (acm->susp_count) {
+- acm->delayed_wb = wb;
+- schedule_work(&acm->waker);
++ if (!acm->delayed_wb)
++ acm->delayed_wb = wb;
++ else
++ usb_autopm_put_interface_async(acm->control);
+ spin_unlock_irqrestore(&acm->write_lock, flags);
+ return 0; /* A white lie */
+ }
+@@ -534,23 +538,6 @@ static void acm_softint(struct work_stru
+ tty_kref_put(tty);
+ }
+
+-static void acm_waker(struct work_struct *waker)
+-{
+- struct acm *acm = container_of(waker, struct acm, waker);
+- int rv;
+-
+- rv = usb_autopm_get_interface(acm->control);
+- if (rv < 0) {
+- dev_err(&acm->dev->dev, "Autopm failure in %s\n", __func__);
+- return;
+- }
+- if (acm->delayed_wb) {
+- acm_start_wb(acm, acm->delayed_wb);
+- acm->delayed_wb = NULL;
+- }
+- usb_autopm_put_interface(acm->control);
+-}
+-
+ /*
+ * TTY handlers
+ */
+@@ -1178,7 +1165,6 @@ made_compressed_probe:
+ acm->urb_task.func = acm_rx_tasklet;
+ acm->urb_task.data = (unsigned long) acm;
+ INIT_WORK(&acm->work, acm_softint);
+- INIT_WORK(&acm->waker, acm_waker);
+ init_waitqueue_head(&acm->drain_wait);
+ spin_lock_init(&acm->throttle_lock);
+ spin_lock_init(&acm->write_lock);
+@@ -1343,7 +1329,6 @@ static void stop_data_traffic(struct acm
+ tasklet_enable(&acm->urb_task);
+
+ cancel_work_sync(&acm->work);
+- cancel_work_sync(&acm->waker);
+ }
+
+ static void acm_disconnect(struct usb_interface *intf)
+@@ -1435,6 +1420,7 @@ static int acm_suspend(struct usb_interf
+ static int acm_resume(struct usb_interface *intf)
+ {
+ struct acm *acm = usb_get_intfdata(intf);
++ struct acm_wb *wb;
+ int rv = 0;
+ int cnt;
+
+@@ -1449,6 +1435,21 @@ static int acm_resume(struct usb_interfa
+ mutex_lock(&acm->mutex);
+ if (acm->port.count) {
+ rv = usb_submit_urb(acm->ctrlurb, GFP_NOIO);
++
++ spin_lock_irq(&acm->write_lock);
++ if (acm->delayed_wb) {
++ wb = acm->delayed_wb;
++ acm->delayed_wb = NULL;
++ spin_unlock_irq(&acm->write_lock);
++ acm_start_wb(acm, acm->delayed_wb);
++ } else {
++ spin_unlock_irq(&acm->write_lock);
++ }
++
++ /*
++ * delayed error checking because we must
++ * do the write path at all cost
++ */
+ if (rv < 0)
+ goto err_out;
+
+--- a/drivers/usb/class/cdc-acm.h
++++ b/drivers/usb/class/cdc-acm.h
+@@ -112,7 +112,6 @@ struct acm {
+ struct mutex mutex;
+ struct usb_cdc_line_coding line; /* bits, stop, parity */
+ struct work_struct work; /* work queue entry for line discipline waking up */
+- struct work_struct waker;
+ wait_queue_head_t drain_wait; /* close processing */
+ struct tasklet_struct urb_task; /* rx processing */
+ spinlock_t throttle_lock; /* synchronize throtteling and read callback */
diff --git a/usb/usb-check-the-endpoint-type-against-the-pipe-type.patch b/usb/usb-check-the-endpoint-type-against-the-pipe-type.patch
new file mode 100644
index 00000000000000..3607b51ccf0b86
--- /dev/null
+++ b/usb/usb-check-the-endpoint-type-against-the-pipe-type.patch
@@ -0,0 +1,111 @@
+From stern@rowland.harvard.edu Tue Dec 22 11:35:17 2009
+From: Alan Stern <stern@rowland.harvard.edu>
+Date: Fri, 11 Dec 2009 16:20:20 -0500 (EST)
+Subject: USB: check the endpoint type against the pipe type
+To: Greg KH <greg@kroah.com>
+Cc: Ondrej Zary <linux@rainbow-software.org>, USB list <linux-usb@vger.kernel.org>
+Message-ID: <Pine.LNX.4.44L0.0912111617360.2777-100000@iolanthe.rowland.org>
+
+
+This patch (as1316) adds some error checking to usb_submit_urb().
+It's conditional on CONFIG_USB_DEBUG, so it won't affect normal users.
+The new check makes sure that the actual type of the endpoint
+described by urb->pipe agrees with the type encoded in the pipe value.
+
+The USB error code documentation is updated to include the code
+returned by the new check, and the usbfs SUBMITURB handler is updated
+to use the correct pipe type when legacy user code tries to submit a
+bulk transfer to an interrupt endpoint.
+
+Signed-off-by: Alan Stern <stern@rowland.harvard.edu>
+Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
+
+
+---
+ Documentation/usb/error-codes.txt | 6 ++++--
+ drivers/usb/core/devio.c | 22 +++++++++++++---------
+ drivers/usb/core/urb.c | 7 +++++++
+ 3 files changed, 24 insertions(+), 11 deletions(-)
+
+--- a/Documentation/usb/error-codes.txt
++++ b/Documentation/usb/error-codes.txt
+@@ -41,8 +41,8 @@ USB-specific:
+
+ -EFBIG Host controller driver can't schedule that many ISO frames.
+
+--EPIPE Specified endpoint is stalled. For non-control endpoints,
+- reset this status with usb_clear_halt().
++-EPIPE The pipe type specified in the URB doesn't match the
++ endpoint's actual type.
+
+ -EMSGSIZE (a) endpoint maxpacket size is zero; it is not usable
+ in the current interface altsetting.
+@@ -60,6 +60,8 @@ USB-specific:
+
+ -EHOSTUNREACH URB was rejected because the device is suspended.
+
++-ENOEXEC A control URB doesn't contain a Setup packet.
++
+
+ **************************************************************************
+ * Error codes returned by in urb->status *
+--- a/drivers/usb/core/devio.c
++++ b/drivers/usb/core/devio.c
+@@ -1104,13 +1104,25 @@ static int proc_do_submiturb(struct dev_
+ case USB_ENDPOINT_XFER_CONTROL:
+ case USB_ENDPOINT_XFER_ISOC:
+ return -EINVAL;
+- /* allow single-shot interrupt transfers, at bogus rates */
++ case USB_ENDPOINT_XFER_INT:
++ /* allow single-shot interrupt transfers */
++ uurb->type = USBDEVFS_URB_TYPE_INTERRUPT;
++ goto interrupt_urb;
+ }
+ uurb->number_of_packets = 0;
+ if (uurb->buffer_length > MAX_USBFS_BUFFER_SIZE)
+ return -EINVAL;
+ break;
+
++ case USBDEVFS_URB_TYPE_INTERRUPT:
++ if (!usb_endpoint_xfer_int(&ep->desc))
++ return -EINVAL;
++ interrupt_urb:
++ uurb->number_of_packets = 0;
++ if (uurb->buffer_length > MAX_USBFS_BUFFER_SIZE)
++ return -EINVAL;
++ break;
++
+ case USBDEVFS_URB_TYPE_ISO:
+ /* arbitrary limit */
+ if (uurb->number_of_packets < 1 ||
+@@ -1143,14 +1155,6 @@ static int proc_do_submiturb(struct dev_
+ uurb->buffer_length = totlen;
+ break;
+
+- case USBDEVFS_URB_TYPE_INTERRUPT:
+- uurb->number_of_packets = 0;
+- if (!usb_endpoint_xfer_int(&ep->desc))
+- return -EINVAL;
+- if (uurb->buffer_length > MAX_USBFS_BUFFER_SIZE)
+- return -EINVAL;
+- break;
+-
+ default:
+ return -EINVAL;
+ }
+--- a/drivers/usb/core/urb.c
++++ b/drivers/usb/core/urb.c
+@@ -387,6 +387,13 @@ int usb_submit_urb(struct urb *urb, gfp_
+ {
+ unsigned int orig_flags = urb->transfer_flags;
+ unsigned int allowed;
++ static int pipetypes[4] = {
++ PIPE_CONTROL, PIPE_ISOCHRONOUS, PIPE_BULK, PIPE_INTERRUPT
++ };
++
++ /* Check that the pipe's type matches the endpoint's type */
++ if (usb_pipetype(urb->pipe) != pipetypes[xfertype])
++ return -EPIPE; /* The most suitable error code :-) */
+
+ /* enforce simple/standard policy */
+ allowed = (URB_NO_TRANSFER_DMA_MAP | URB_NO_SETUP_DMA_MAP |
diff --git a/usb/usb-cxacru-add-write-only-sysfs-attribute-for-modem-configuration.patch b/usb/usb-cxacru-add-write-only-sysfs-attribute-for-modem-configuration.patch
new file mode 100644
index 00000000000000..48abb51b46802c
--- /dev/null
+++ b/usb/usb-cxacru-add-write-only-sysfs-attribute-for-modem-configuration.patch
@@ -0,0 +1,164 @@
+From simon@fire.lp0.eu Tue Dec 22 11:24:16 2009
+From: Simon Arlott <simon@fire.lp0.eu>
+Date: Sat, 21 Nov 2009 15:14:01 +0000
+Subject: USB: cxacru: add write-only sysfs attribute for modem configuration
+To: Greg Kroah-Hartman <gregkh@suse.de>
+Message-ID: <4B0803B9.2070700@simon.arlott.org.uk>
+
+
+The modem can be configured using CM_REQUEST_CARD_DATA_SET,
+although CM_REQUEST_CARD_DATA_GET does not return any data.
+
+Tested by setting the modulation (0x0a) option.
+
+There is a list of parameters in the following archive,
+but the meaning of many of them is not well documented:
+ http://sourceforge.net/project/shownotes.php?release_id=301825
+
+This source also indicates that the highest parameter set
+is 0x4a but this varies by model so an arbitrary limit of
+0x7f has been used (the index is a 32-bit integer).
+
+Signed-off-by: Simon Arlott <simon@fire.lp0.eu>
+Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
+
+---
+ Documentation/networking/cxacru.txt | 9 ++++
+ drivers/usb/atm/cxacru.c | 76 +++++++++++++++++++++++++++++++++++-
+ 2 files changed, 84 insertions(+), 1 deletion(-)
+
+--- a/Documentation/networking/cxacru.txt
++++ b/Documentation/networking/cxacru.txt
+@@ -15,6 +15,15 @@ several sysfs attribute files for retrie
+ * adsl_headend_environment
+ Information about the remote headend.
+
++* adsl_config
++ Configuration writing interface.
++ Write parameters in hexadecimal format <index>=<value>,
++ separated by whitespace, e.g.:
++ "1=0 a=5"
++ Up to 7 parameters at a time will be sent and the modem will restart
++ the ADSL connection when any value is set. These are logged for future
++ reference.
++
+ * downstream_attenuation (dB)
+ * downstream_bits_per_frame
+ * downstream_rate (kbps)
+--- a/drivers/usb/atm/cxacru.c
++++ b/drivers/usb/atm/cxacru.c
+@@ -52,6 +52,7 @@ static const char cxacru_driver_name[] =
+ #define CXACRU_EP_DATA 0x02 /* Bulk in/out */
+
+ #define CMD_PACKET_SIZE 64 /* Should be maxpacket(ep)? */
++#define CMD_MAX_CONFIG ((CMD_PACKET_SIZE / 4 - 1) / 2)
+
+ /* Addresses */
+ #define PLLFCLK_ADDR 0x00350068
+@@ -216,6 +217,10 @@ static DEVICE_ATTR(_name, S_IRUGO, cxacr
+ static DEVICE_ATTR(_name, S_IWUSR | S_IRUGO, \
+ cxacru_sysfs_show_##_name, cxacru_sysfs_store_##_name)
+
++#define CXACRU_SET_INIT(_name) \
++static DEVICE_ATTR(_name, S_IWUSR, \
++ NULL, 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) \
+@@ -232,10 +237,12 @@ 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_SET_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_SET_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)
+@@ -438,6 +445,72 @@ static ssize_t cxacru_sysfs_store_adsl_s
+ return ret;
+ }
+
++/* CM_REQUEST_CARD_DATA_GET times out, so no show attribute */
++
++static ssize_t cxacru_sysfs_store_adsl_config(struct device *dev,
++ struct device_attribute *attr, const char *buf, size_t count)
++{
++ struct cxacru_data *instance = to_usbatm_driver_data(
++ to_usb_interface(dev));
++ int len = strlen(buf);
++ int ret, pos, num;
++ __le32 data[CMD_PACKET_SIZE / 4];
++
++ if (!capable(CAP_NET_ADMIN))
++ return -EACCES;
++
++ if (instance == NULL)
++ return -ENODEV;
++
++ pos = 0;
++ num = 0;
++ while (pos < len) {
++ int tmp;
++ u32 index;
++ u32 value;
++
++ ret = sscanf(buf + pos, "%x=%x%n", &index, &value, &tmp);
++ if (ret < 2)
++ return -EINVAL;
++ if (index < 0 || index > 0x7f)
++ return -EINVAL;
++ pos += tmp;
++
++ /* skip trailing newline */
++ if (buf[pos] == '\n' && pos == len-1)
++ pos++;
++
++ data[num * 2 + 1] = cpu_to_le32(index);
++ data[num * 2 + 2] = cpu_to_le32(value);
++ num++;
++
++ /* send config values when data buffer is full
++ * or no more data
++ */
++ if (pos >= len || num >= CMD_MAX_CONFIG) {
++ char log[CMD_MAX_CONFIG * 12 + 1]; /* %02x=%08x */
++
++ data[0] = cpu_to_le32(num);
++ ret = cxacru_cm(instance, CM_REQUEST_CARD_DATA_SET,
++ (u8 *) data, 4 + num * 8, NULL, 0);
++ if (ret < 0) {
++ atm_err(instance->usbatm,
++ "set card data returned %d\n", ret);
++ return -EIO;
++ }
++
++ for (tmp = 0; tmp < num; tmp++)
++ snprintf(log + tmp*12, 13, " %02x=%08x",
++ le32_to_cpu(data[tmp * 2 + 1]),
++ le32_to_cpu(data[tmp * 2 + 2]));
++ atm_info(instance->usbatm, "config%s\n", log);
++ num = 0;
++ }
++ }
++
++ return len;
++}
++
+ /*
+ * All device attributes are included in CXACRU_ALL_FILES
+ * so that the same list can be used multiple times:
+@@ -473,7 +546,8 @@ CXACRU_ATTR_##_action(CXINF_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_CMD_##_action( adsl_state);
++CXACRU_CMD_##_action( adsl_state); \
++CXACRU_SET_##_action( adsl_config);
+
+ CXACRU_ALL_FILES(INIT);
+
diff --git a/usb/usb-cxacru-check-data-length-is-not-negative.patch b/usb/usb-cxacru-check-data-length-is-not-negative.patch
new file mode 100644
index 00000000000000..a7a0337c794080
--- /dev/null
+++ b/usb/usb-cxacru-check-data-length-is-not-negative.patch
@@ -0,0 +1,34 @@
+From simon@fire.lp0.eu Tue Dec 22 11:23:06 2009
+From: Simon Arlott <simon@fire.lp0.eu>
+Date: Sat, 21 Nov 2009 15:07:14 +0000
+Subject: USB: cxacru: check data length is not negative
+To: Greg Kroah-Hartman <gregkh@suse.de>
+Message-ID: <4B080222.50403@simon.arlott.org.uk>
+
+
+When attempting to read data that is not actually
+an array of values, the length may be negative
+which causes an Oops due to a likely access off
+the end of the data array.
+
+This bug should not occur under normal use unless
+the device returns an invalid response.
+
+Signed-off-by: Simon Arlott <simon@fire.lp0.eu>
+Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
+
+---
+ drivers/usb/atm/cxacru.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+--- a/drivers/usb/atm/cxacru.c
++++ b/drivers/usb/atm/cxacru.c
+@@ -596,7 +596,7 @@ static int cxacru_cm_get_array(struct cx
+ len = ret / 4;
+ for (offb = 0; offb < len; ) {
+ int l = le32_to_cpu(buf[offb++]);
+- if (l > stride || l > (len - offb) / 2) {
++ if (l < 0 || l > stride || l > (len - offb) / 2) {
+ if (printk_ratelimit())
+ usb_err(instance->usbatm, "invalid data length from cm %#x: %d\n",
+ cm, l);
diff --git a/usb/usb-cxacru-check-device-isn-t-being-removed-during-sysfs-calls.patch b/usb/usb-cxacru-check-device-isn-t-being-removed-during-sysfs-calls.patch
new file mode 100644
index 00000000000000..e32cf048c875fb
--- /dev/null
+++ b/usb/usb-cxacru-check-device-isn-t-being-removed-during-sysfs-calls.patch
@@ -0,0 +1,166 @@
+From simon@fire.lp0.eu Tue Dec 22 11:23:23 2009
+From: Simon Arlott <simon@fire.lp0.eu>
+Date: Sat, 21 Nov 2009 15:33:51 +0000
+Subject: USB: cxacru: check device isn't being removed during sysfs calls
+To: Greg Kroah-Hartman <gregkh@suse.de>
+Cc: Duncan Sands <duncan.sands@math.u-psud.fr>
+Message-ID: <4B08085F.5020506@simon.arlott.org.uk>
+
+
+It is possible for usb_get_intfdata() to return NULL if
+sysfs is accessed while the module is being unloaded or
+the device is being removed.
+
+Move the access code to an inline function in usbatm.h,
+and return -ENODEV if any of the pointers are NULL.
+
+It should not be possible for the instance data or atm
+device to be invalid until after unbind() completes and
+the sysfs attributes have been removed.
+
+Signed-off-by: Simon Arlott <simon@fire.lp0.eu>
+Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
+
+---
+ drivers/usb/atm/cxacru.c | 48 +++++++++++++++++++++++++++--------------------
+ drivers/usb/atm/usbatm.c | 1
+ drivers/usb/atm/usbatm.h | 15 ++++++++++++++
+ 3 files changed, 44 insertions(+), 20 deletions(-)
+
+--- a/drivers/usb/atm/cxacru.c
++++ b/drivers/usb/atm/cxacru.c
+@@ -200,9 +200,12 @@ static DEVICE_ATTR(_name, S_IWUSR | S_IR
+ static ssize_t cxacru_sysfs_show_##_name(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; \
++ struct cxacru_data *instance = to_usbatm_driver_data(\
++ to_usb_interface(dev)); \
++\
++ if (instance == NULL) \
++ return -ENODEV; \
++\
+ return cxacru_sysfs_showattr_##_type(instance->card_info[_value], buf); \
+ } \
+ CXACRU__ATTR_INIT(_name)
+@@ -288,22 +291,28 @@ static ssize_t cxacru_sysfs_showattr_MOD
+ static ssize_t cxacru_sysfs_show_mac_address(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 atm_dev *atm_dev = usbatm_instance->atm_dev;
++ struct cxacru_data *instance = to_usbatm_driver_data(
++ to_usb_interface(dev));
+
+- return snprintf(buf, PAGE_SIZE, "%pM\n", atm_dev->esi);
++ if (instance == NULL || instance->usbatm->atm_dev == NULL)
++ return -ENODEV;
++
++ return snprintf(buf, PAGE_SIZE, "%pM\n",
++ instance->usbatm->atm_dev->esi);
+ }
+
+ 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];
+-
+ static char *str[] = { "running", "stopped" };
++ struct cxacru_data *instance = to_usbatm_driver_data(
++ to_usb_interface(dev));
++ u32 value;
++
++ if (instance == NULL)
++ return -ENODEV;
++
++ value = instance->card_info[CXINF_LINE_STARTABLE];
+ if (unlikely(value >= ARRAY_SIZE(str)))
+ return snprintf(buf, PAGE_SIZE, "%u\n", value);
+ return snprintf(buf, PAGE_SIZE, "%s\n", str[value]);
+@@ -312,9 +321,8 @@ static ssize_t cxacru_sysfs_show_adsl_st
+ 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;
++ struct cxacru_data *instance = to_usbatm_driver_data(
++ to_usb_interface(dev));
+ int ret;
+ int poll = -1;
+ char str_cmd[8];
+@@ -328,13 +336,16 @@ static ssize_t cxacru_sysfs_store_adsl_s
+ return -EINVAL;
+ ret = 0;
+
++ if (instance == NULL)
++ return -ENODEV;
++
+ 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:"
++ atm_err(instance->usbatm, "change adsl state:"
+ " CHIP_ADSL_LINE_STOP returned %d\n", ret);
+
+ ret = -EIO;
+@@ -354,7 +365,7 @@ static ssize_t cxacru_sysfs_store_adsl_s
+ 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:"
++ atm_err(instance->usbatm, "change adsl state:"
+ " CHIP_ADSL_LINE_START returned %d\n", ret);
+
+ ret = -EIO;
+@@ -649,9 +660,6 @@ static int cxacru_atm_start(struct usbat
+ {
+ struct cxacru_data *instance = usbatm_instance->driver_data;
+ struct usb_interface *intf = usbatm_instance->usb_intf;
+- /*
+- struct atm_dev *atm_dev = usbatm_instance->atm_dev;
+- */
+ int ret;
+ int start_polling = 1;
+
+--- a/drivers/usb/atm/usbatm.c
++++ b/drivers/usb/atm/usbatm.c
+@@ -1333,6 +1333,7 @@ void usbatm_usb_disconnect(struct usb_in
+ if (instance->atm_dev) {
+ sysfs_remove_link(&instance->atm_dev->class_dev.kobj, "device");
+ atm_dev_deregister(instance->atm_dev);
++ instance->atm_dev = NULL;
+ }
+
+ usbatm_put_instance(instance); /* taken in usbatm_usb_probe */
+--- a/drivers/usb/atm/usbatm.h
++++ b/drivers/usb/atm/usbatm.h
+@@ -204,4 +204,19 @@ struct usbatm_data {
+ struct urb *urbs[0];
+ };
+
++static inline void *to_usbatm_driver_data(struct usb_interface *intf)
++{
++ struct usbatm_data *usbatm_instance;
++
++ if (intf == NULL)
++ return NULL;
++
++ usbatm_instance = usb_get_intfdata(intf);
++
++ if (usbatm_instance == NULL) /* set NULL before unbind() */
++ return NULL;
++
++ return usbatm_instance->driver_data; /* set NULL after unbind() */
++}
++
+ #endif /* _USBATM_H_ */
diff --git a/usb/usb-cxacru-document-how-to-interact-with-the-flash-memory.patch b/usb/usb-cxacru-document-how-to-interact-with-the-flash-memory.patch
new file mode 100644
index 00000000000000..84c59ef41ceb65
--- /dev/null
+++ b/usb/usb-cxacru-document-how-to-interact-with-the-flash-memory.patch
@@ -0,0 +1,51 @@
+From simon@fire.lp0.eu Tue Dec 22 11:23:41 2009
+From: Simon Arlott <simon@fire.lp0.eu>
+Date: Sat, 21 Nov 2009 15:12:21 +0000
+Subject: USB: cxacru: document how to interact with the flash memory
+To: Greg Kroah-Hartman <gregkh@suse.de>
+Message-ID: <4B080355.8000300@simon.arlott.org.uk>
+
+
+These commands were found by accident... fortunately
+it still works even if the flash memory is erased,
+despite having no USB device IDs.
+
+Some example sysfs code for raw command access:
+ http://simon.arlott.org/pub/cxacru/raw.c
+
+Signed-off-by: Simon Arlott <simon@fire.lp0.eu>
+Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
+
+---
+ drivers/usb/atm/cxacru.c | 20 ++++++++++++++++++++
+ 1 file changed, 20 insertions(+)
+
+--- a/drivers/usb/atm/cxacru.c
++++ b/drivers/usb/atm/cxacru.c
+@@ -105,6 +105,26 @@ enum cxacru_cm_request {
+ CM_REQUEST_MAX,
+ };
+
++/* commands for interaction with the flash memory
++ *
++ * read: response is the contents of the first 60 bytes of flash memory
++ * write: request contains the 60 bytes of data to write to flash memory
++ * response is the contents of the first 60 bytes of flash memory
++ *
++ * layout: PP PP VV VV MM MM MM MM MM MM ?? ?? SS SS SS SS SS SS SS SS
++ * SS SS SS SS SS SS SS SS 00 00 00 00 00 00 00 00 00 00 00 00
++ * 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
++ *
++ * P: le16 USB Product ID
++ * V: le16 USB Vendor ID
++ * M: be48 MAC Address
++ * S: le16 ASCII Serial Number
++ */
++enum cxacru_cm_flash {
++ CM_FLASH_READ = 0xa1,
++ CM_FLASH_WRITE = 0xa2
++};
++
+ /* reply codes to the commands above */
+ enum cxacru_cm_status {
+ CM_STATUS_UNDEFINED,
diff --git a/usb/usb-cxacru-firmware-writes-on-ohci-are-slow-log-progress.patch b/usb/usb-cxacru-firmware-writes-on-ohci-are-slow-log-progress.patch
new file mode 100644
index 00000000000000..9b0445b5f7c16c
--- /dev/null
+++ b/usb/usb-cxacru-firmware-writes-on-ohci-are-slow-log-progress.patch
@@ -0,0 +1,70 @@
+From simon@fire.lp0.eu Tue Dec 22 11:23:59 2009
+From: Simon Arlott <simon@fire.lp0.eu>
+Date: Sat, 21 Nov 2009 15:12:56 +0000
+Subject: USB: cxacru: firmware writes on OHCI are slow, log progress
+To: Greg Kroah-Hartman <gregkh@suse.de>
+Message-ID: <4B080378.20903@simon.arlott.org.uk>
+
+
+Firmware writing takes 256ms per 4KB with OHCI, which
+is very slow compared to 7ms per 4KB with UHCI.
+
+Until I have access to a hardware USB analyser it may
+not be possible to determine why this happens.
+
+Instead of appearing to do nothing, log progress when
+writing firmware and then log the ATM device information
+when finished. Remove an unnecessary 4 second delay.
+
+Signed-off-by: Simon Arlott <simon@fire.lp0.eu>
+Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
+
+---
+ drivers/usb/atm/cxacru.c | 8 ++++++--
+ 1 file changed, 6 insertions(+), 2 deletions(-)
+
+--- a/drivers/usb/atm/cxacru.c
++++ b/drivers/usb/atm/cxacru.c
+@@ -725,6 +725,9 @@ static int cxacru_atm_start(struct usbat
+ mutex_unlock(&instance->poll_state_serialize);
+ mutex_unlock(&instance->adsl_state_serialize);
+
++ printk(KERN_INFO "%s%d: %s %pM\n", atm_dev->type, atm_dev->number,
++ usbatm_instance->description, atm_dev->esi);
++
+ if (start_polling)
+ cxacru_poll_status(&instance->poll_work.work);
+ return 0;
+@@ -939,6 +942,7 @@ static void cxacru_upload_firmware(struc
+ }
+
+ /* Firmware */
++ usb_info(usbatm, "loading firmware\n");
+ ret = cxacru_fw(usb_dev, FW_WRITE_MEM, 0x2, 0x0, FW_ADDR, fw->data, fw->size);
+ if (ret) {
+ usb_err(usbatm, "Firmware upload failed: %d\n", ret);
+@@ -947,6 +951,7 @@ static void cxacru_upload_firmware(struc
+
+ /* Boot ROM patch */
+ if (instance->modem_type->boot_rom_patch) {
++ usb_info(usbatm, "loading boot ROM patch\n");
+ ret = cxacru_fw(usb_dev, FW_WRITE_MEM, 0x2, 0x0, BR_ADDR, bp->data, bp->size);
+ if (ret) {
+ usb_err(usbatm, "Boot ROM patching failed: %d\n", ret);
+@@ -961,6 +966,7 @@ static void cxacru_upload_firmware(struc
+ return;
+ }
+
++ usb_info(usbatm, "starting device\n");
+ if (instance->modem_type->boot_rom_patch) {
+ val = cpu_to_le32(BR_ADDR);
+ ret = cxacru_fw(usb_dev, FW_WRITE_MEM, 0x2, 0x0, BR_STACK_ADDR, (u8 *) &val, 4);
+@@ -1004,8 +1010,6 @@ static void cxacru_upload_firmware(struc
+ return;
+ }
+ }
+-
+- msleep_interruptible(4000);
+ }
+
+ static int cxacru_find_firmware(struct cxacru_data *instance,
diff --git a/usb/usb-cxacru-increment-driver-version.patch b/usb/usb-cxacru-increment-driver-version.patch
new file mode 100644
index 00000000000000..463308b51b2a08
--- /dev/null
+++ b/usb/usb-cxacru-increment-driver-version.patch
@@ -0,0 +1,42 @@
+From simon@fire.lp0.eu Tue Dec 22 11:24:56 2009
+From: Simon Arlott <simon@fire.lp0.eu>
+Date: Sat, 21 Nov 2009 15:16:38 +0000
+Subject: USB: cxacru: increment driver version
+To: Greg Kroah-Hartman <gregkh@suse.de>
+Message-ID: <4B080456.10905@simon.arlott.org.uk>
+
+
+Changes:
+ Return an empty string for modulation
+ when there is no connection
+ Fix sysfs unload race conditions
+ Log firmware load process, remove delay
+ Add new configuration interface
+ Remove cxacru-cf.bin
+
+Signed-off-by: Simon Arlott <simon@fire.lp0.eu>
+Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
+
+---
+ drivers/usb/atm/cxacru.c | 3 ++-
+ 1 file changed, 2 insertions(+), 1 deletion(-)
+
+--- a/drivers/usb/atm/cxacru.c
++++ b/drivers/usb/atm/cxacru.c
+@@ -5,6 +5,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
++ * Copyright (C) 2009 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
+@@ -43,7 +44,7 @@
+ #include "usbatm.h"
+
+ #define DRIVER_AUTHOR "Roman Kagan, David Woodhouse, Duncan Sands, Simon Arlott"
+-#define DRIVER_VERSION "0.3"
++#define DRIVER_VERSION "0.4"
+ #define DRIVER_DESC "Conexant AccessRunner ADSL USB modem driver"
+
+ static const char cxacru_driver_name[] = "cxacru";
diff --git a/usb/usb-cxacru-remove-cxacru-cf.bin-loader.patch b/usb/usb-cxacru-remove-cxacru-cf.bin-loader.patch
new file mode 100644
index 00000000000000..9dd8285377df61
--- /dev/null
+++ b/usb/usb-cxacru-remove-cxacru-cf.bin-loader.patch
@@ -0,0 +1,174 @@
+From simon@fire.lp0.eu Tue Dec 22 11:24:33 2009
+From: Simon Arlott <simon@fire.lp0.eu>
+Date: Sat, 21 Nov 2009 15:15:47 +0000
+Subject: USB: cxacru: remove cxacru-cf.bin loader
+To: Greg Kroah-Hartman <gregkh@suse.de>
+Message-ID: <4B080423.2080803@simon.arlott.org.uk>
+
+
+This has never worked properly because wsize passed to
+cxacru_cm() is incorrectly set to the number of values
+instead of the data bytes. The maximum number of values
+that can be set at once is 7 which means the device will
+not get enough data to work with and none of the
+configuration values will be used.
+
+At least one existing cxacru-cf.bin file contains invalid
+data which will prevent the modem from syncing properly.
+
+Fixing it is likely to break existing systems, and the
+new sysfs interface for setting configuration parameters
+can provide the same functionality. A script is provided
+to convert from the original format.
+
+Signed-off-by: Simon Arlott <simon@fire.lp0.eu>
+Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
+---
+ Documentation/networking/00-INDEX | 2 +
+ Documentation/networking/cxacru-cf.py | 48 ++++++++++++++++++++++++++++++++++
+ Documentation/networking/cxacru.txt | 6 ++++
+ drivers/usb/atm/cxacru.c | 31 ++-------------------
+ 4 files changed, 59 insertions(+), 28 deletions(-)
+ create mode 100644 Documentation/networking/cxacru-cf.py
+
+--- a/Documentation/networking/00-INDEX
++++ b/Documentation/networking/00-INDEX
+@@ -32,6 +32,8 @@ cs89x0.txt
+ - the Crystal LAN (CS8900/20-based) Ethernet ISA adapter driver
+ cxacru.txt
+ - Conexant AccessRunner USB ADSL Modem
++cxacru-cf.py
++ - Conexant AccessRunner USB ADSL Modem configuration file parser
+ de4x5.txt
+ - the Digital EtherWORKS DE4?? and DE5?? PCI Ethernet driver
+ decnet.txt
+--- /dev/null
++++ b/Documentation/networking/cxacru-cf.py
+@@ -0,0 +1,48 @@
++#!/usr/bin/env python
++# Copyright 2009 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
++# Software Foundation; either version 2 of the License, or (at your option)
++# any later version.
++#
++# This program is distributed in the hope that it will be useful, but WITHOUT
++# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
++# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
++# more details.
++#
++# You should have received a copy of the GNU General Public License along with
++# this program; if not, write to the Free Software Foundation, Inc., 59
++# Temple Place - Suite 330, Boston, MA 02111-1307, USA.
++#
++# Usage: cxacru-cf.py < cxacru-cf.bin
++# Output: values string suitable for the sysfs adsl_config attribute
++#
++# Warning: cxacru-cf.bin with MD5 hash cdbac2689969d5ed5d4850f117702110
++# contains mis-aligned values which will stop the modem from being able
++# to make a connection. If the first and last two bytes are removed then
++# the values become valid, but the modulation will be forced to ANSI
++# T1.413 only which may not be appropriate.
++#
++# The original binary format is a packed list of le32 values.
++
++import sys
++import struct
++
++i = 0
++while True:
++ buf = sys.stdin.read(4)
++
++ if len(buf) == 0:
++ break
++ elif len(buf) != 4:
++ sys.stdout.write("\n")
++ sys.stderr.write("Error: read {0} not 4 bytes\n".format(len(buf)))
++ sys.exit(1)
++
++ if i > 0:
++ sys.stdout.write(" ")
++ sys.stdout.write("{0:x}={1}".format(i, struct.unpack("<I", buf)[0]))
++ i += 1
++
++sys.stdout.write("\n")
+--- a/Documentation/networking/cxacru.txt
++++ b/Documentation/networking/cxacru.txt
+@@ -4,6 +4,12 @@ While it is capable of managing/maintain
+ module loaded, the device will sometimes stop responding after unloading the
+ driver and it is necessary to unplug/remove power to the device to fix this.
+
++Note: support for cxacru-cf.bin has been removed. It was not loaded correctly
++so it had no effect on the device configuration. Fixing it could have stopped
++existing devices working when an invalid configuration is supplied.
++
++There is a script cxacru-cf.py to convert an existing file to the sysfs form.
++
+ Detected devices will appear as ATM devices named "cxacru". In /sys/class/atm/
+ these are directories named cxacruN where N is the device number. A symlink
+ named device points to the USB interface device's directory which contains
+--- a/drivers/usb/atm/cxacru.c
++++ b/drivers/usb/atm/cxacru.c
+@@ -978,11 +978,9 @@ cleanup:
+
+ static void cxacru_upload_firmware(struct cxacru_data *instance,
+ const struct firmware *fw,
+- const struct firmware *bp,
+- const struct firmware *cf)
++ const struct firmware *bp)
+ {
+ int ret;
+- int off;
+ struct usbatm_data *usbatm = instance->usbatm;
+ struct usb_device *usb_dev = usbatm->usb_dev;
+ __le16 signature[] = { usb_dev->descriptor.idVendor,
+@@ -1066,24 +1064,6 @@ static void cxacru_upload_firmware(struc
+ usb_err(usbatm, "modem failed to initialize: %d\n", ret);
+ return;
+ }
+-
+- /* Load config data (le32), doing one packet at a time */
+- if (cf)
+- for (off = 0; off < cf->size / 4; ) {
+- __le32 buf[CMD_PACKET_SIZE / 4 - 1];
+- int i, len = min_t(int, cf->size / 4 - off, CMD_PACKET_SIZE / 4 / 2 - 1);
+- buf[0] = cpu_to_le32(len);
+- for (i = 0; i < len; i++, off++) {
+- buf[i * 2 + 1] = cpu_to_le32(off);
+- memcpy(buf + i * 2 + 2, cf->data + off * 4, 4);
+- }
+- ret = cxacru_cm(instance, CM_REQUEST_CARD_DATA_SET,
+- (u8 *) buf, len, NULL, 0);
+- if (ret < 0) {
+- usb_err(usbatm, "load config data failed: %d\n", ret);
+- return;
+- }
+- }
+ }
+
+ static int cxacru_find_firmware(struct cxacru_data *instance,
+@@ -1109,7 +1089,7 @@ static int cxacru_find_firmware(struct c
+ static int cxacru_heavy_init(struct usbatm_data *usbatm_instance,
+ struct usb_interface *usb_intf)
+ {
+- const struct firmware *fw, *bp, *cf;
++ const struct firmware *fw, *bp;
+ struct cxacru_data *instance = usbatm_instance->driver_data;
+
+ int ret = cxacru_find_firmware(instance, "fw", &fw);
+@@ -1127,13 +1107,8 @@ static int cxacru_heavy_init(struct usba
+ }
+ }
+
+- if (cxacru_find_firmware(instance, "cf", &cf)) /* optional */
+- cf = NULL;
+-
+- cxacru_upload_firmware(instance, fw, bp, cf);
++ cxacru_upload_firmware(instance, fw, bp);
+
+- if (cf)
+- release_firmware(cf);
+ if (instance->modem_type->boot_rom_patch)
+ release_firmware(bp);
+ release_firmware(fw);
diff --git a/usb/usb-cxacru-return-an-empty-value-for-modulation-if-there-is-no-connection.patch b/usb/usb-cxacru-return-an-empty-value-for-modulation-if-there-is-no-connection.patch
new file mode 100644
index 00000000000000..e11b3cdcb59c5a
--- /dev/null
+++ b/usb/usb-cxacru-return-an-empty-value-for-modulation-if-there-is-no-connection.patch
@@ -0,0 +1,46 @@
+From simon@fire.lp0.eu Tue Dec 22 11:22:48 2009
+From: Simon Arlott <simon@fire.lp0.eu>
+Date: Sat, 21 Nov 2009 15:03:23 +0000
+Subject: USB: cxacru: return an empty value for modulation if there is no connection
+To: Greg Kroah-Hartman <gregkh@suse.de>
+Message-ID: <4B08013B.90403@simon.arlott.org.uk>
+
+
+When there is no connection, return an empty string
+instead of "0" for the connection modulation.
+
+Signed-off-by: Simon Arlott <simon@fire.lp0.eu>
+Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
+
+---
+ Documentation/networking/cxacru.txt | 1 +
+ drivers/usb/atm/cxacru.c | 4 ++--
+ 2 files changed, 3 insertions(+), 2 deletions(-)
+
+--- a/Documentation/networking/cxacru.txt
++++ b/Documentation/networking/cxacru.txt
+@@ -61,6 +61,7 @@ several sysfs attribute files for retrie
+ * mac_address
+
+ * modulation
++ "" (when not connected)
+ "ANSI T1.413"
+ "ITU-T G.992.1 (G.DMT)"
+ "ITU-T G.992.2 (G.LITE)"
+--- a/drivers/usb/atm/cxacru.c
++++ b/drivers/usb/atm/cxacru.c
+@@ -267,12 +267,12 @@ static ssize_t cxacru_sysfs_showattr_LIN
+ static ssize_t cxacru_sysfs_showattr_MODU(u32 value, char *buf)
+ {
+ static char *str[] = {
+- NULL,
++ "",
+ "ANSI T1.413",
+ "ITU-T G.992.1 (G.DMT)",
+ "ITU-T G.992.2 (G.LITE)"
+ };
+- if (unlikely(value >= ARRAY_SIZE(str) || str[value] == NULL))
++ if (unlikely(value >= ARRAY_SIZE(str)))
+ return snprintf(buf, PAGE_SIZE, "%u\n", value);
+ return snprintf(buf, PAGE_SIZE, "%s\n", str[value]);
+ }
diff --git a/usb/usb-cypress_m8-allow-unstable-baud-rates.patch b/usb/usb-cypress_m8-allow-unstable-baud-rates.patch
new file mode 100644
index 00000000000000..2c4fa4bc3f837a
--- /dev/null
+++ b/usb/usb-cypress_m8-allow-unstable-baud-rates.patch
@@ -0,0 +1,49 @@
+From vapier@gentoo.org Tue Dec 22 12:01:32 2009
+From: Mike Frysinger <vapier@gentoo.org>
+Date: Fri, 18 Dec 2009 16:33:03 -0500
+Subject: USB: cypress_m8: allow unstable baud rates
+To: Lonnie Mendez <dignome@gmail.com>
+Cc: linux-usb@vger.kernel.org
+Message-ID: <1261171983-20131-3-git-send-email-vapier@gentoo.org>
+
+
+I've got a crappy cypress converter here, and while running at higher
+baud rates craps out on throughput, it works fine with lower ones.
+While it'd be nice to simply use a lower baud rate, not all devices
+can be configured this way, and it is possible to (slowly) interact
+at higher rates by sending a byte at a time. So let people force
+higher rates when they need it via a module parameter.
+
+Signed-off-by: Mike Frysinger <vapier@gentoo.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
+
+---
+ drivers/usb/serial/cypress_m8.c | 6 ++++++
+ 1 file changed, 6 insertions(+)
+
+--- a/drivers/usb/serial/cypress_m8.c
++++ b/drivers/usb/serial/cypress_m8.c
+@@ -73,6 +73,7 @@
+ static int debug;
+ static int stats;
+ static int interval;
++static int unstable_bauds;
+
+ /*
+ * Version Information
+@@ -291,6 +292,9 @@ static int analyze_baud_rate(struct usb_
+ struct cypress_private *priv;
+ priv = usb_get_serial_port_data(port);
+
++ if (unstable_bauds)
++ return new_rate;
++
+ /*
+ * The general purpose firmware for the Cypress M8 allows for
+ * a maximum speed of 57600bps (I have no idea whether DeLorme
+@@ -1643,3 +1647,5 @@ module_param(stats, bool, S_IRUGO | S_IW
+ MODULE_PARM_DESC(stats, "Enable statistics or not");
+ module_param(interval, int, S_IRUGO | S_IWUSR);
+ MODULE_PARM_DESC(interval, "Overrides interrupt interval");
++module_param(unstable_bauds, bool, S_IRUGO | S_IWUSR);
++MODULE_PARM_DESC(unstable_bauds, "Allow unstable baud rates");
diff --git a/usb/usb-cypress_m8-stop-using-usb-debug-driver-config.patch b/usb/usb-cypress_m8-stop-using-usb-debug-driver-config.patch
new file mode 100644
index 00000000000000..bb57bfe237bb70
--- /dev/null
+++ b/usb/usb-cypress_m8-stop-using-usb-debug-driver-config.patch
@@ -0,0 +1,34 @@
+From vapier@gentoo.org Tue Dec 22 12:00:41 2009
+From: Mike Frysinger <vapier@gentoo.org>
+Date: Fri, 18 Dec 2009 16:33:01 -0500
+Subject: USB: cypress_m8: stop using USB debug driver config
+To: Lonnie Mendez <dignome@gmail.com>
+Cc: linux-usb@vger.kernel.org
+Message-ID: <1261171983-20131-1-git-send-email-vapier@gentoo.org>
+
+
+The USB_SERIAL_DEBUG Kconfig is for the USB serial debug driver, not for
+generically enabling debug output in random USB serial drivers.
+
+Signed-off-by: Mike Frysinger <vapier@gentoo.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
+
+---
+ drivers/usb/serial/cypress_m8.c | 6 +-----
+ 1 file changed, 1 insertion(+), 5 deletions(-)
+
+--- a/drivers/usb/serial/cypress_m8.c
++++ b/drivers/usb/serial/cypress_m8.c
+@@ -70,11 +70,7 @@
+ #include "cypress_m8.h"
+
+
+-#ifdef CONFIG_USB_SERIAL_DEBUG
+- static int debug = 1;
+-#else
+- static int debug;
+-#endif
++static int debug;
+ static int stats;
+ static int interval;
+
diff --git a/usb/usb-cypress_m8-unify-confusing-new-baudrate-check.patch b/usb/usb-cypress_m8-unify-confusing-new-baudrate-check.patch
new file mode 100644
index 00000000000000..b781a1469a5baa
--- /dev/null
+++ b/usb/usb-cypress_m8-unify-confusing-new-baudrate-check.patch
@@ -0,0 +1,41 @@
+From vapier@gentoo.org Tue Dec 22 12:01:20 2009
+From: Mike Frysinger <vapier@gentoo.org>
+Date: Fri, 18 Dec 2009 16:33:02 -0500
+Subject: USB: cypress_m8: unify confusing new baudrate check
+To: Lonnie Mendez <dignome@gmail.com>
+Cc: linux-usb@vger.kernel.org
+Message-ID: <1261171983-20131-2-git-send-email-vapier@gentoo.org>
+
+
+The current code has a confusing duplicate new_baudrate init when setting
+the serial parameters. So just combine the if statement checks to avoid
+this.
+
+Signed-off-by: Mike Frysinger <vapier@gentoo.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
+
+---
+ drivers/usb/serial/cypress_m8.c | 9 +++------
+ 1 file changed, 3 insertions(+), 6 deletions(-)
+
+--- a/drivers/usb/serial/cypress_m8.c
++++ b/drivers/usb/serial/cypress_m8.c
+@@ -352,15 +352,12 @@ static int cypress_serial_control(struct
+
+ switch (cypress_request_type) {
+ case CYPRESS_SET_CONFIG:
+- new_baudrate = priv->baud_rate;
+ /* 0 means 'Hang up' so doesn't change the true bit rate */
+- if (baud_rate == 0)
+- new_baudrate = priv->baud_rate;
+- /* Change of speed ? */
+- else if (baud_rate != priv->baud_rate) {
++ new_baudrate = priv->baud_rate;
++ if (baud_rate && baud_rate != priv->baud_rate) {
+ dbg("%s - baud rate is changing", __func__);
+ retval = analyze_baud_rate(port, baud_rate);
+- if (retval >= 0) {
++ if (retval >= 0) {
+ new_baudrate = retval;
+ dbg("%s - New baud rate set to %d",
+ __func__, new_baudrate);
diff --git a/usb/usb-ehci-add-call-of-free_cached_itd_list-function-in-disable_periodic.patch b/usb/usb-ehci-add-call-of-free_cached_itd_list-function-in-disable_periodic.patch
new file mode 100644
index 00000000000000..f60d6caa58f837
--- /dev/null
+++ b/usb/usb-ehci-add-call-of-free_cached_itd_list-function-in-disable_periodic.patch
@@ -0,0 +1,36 @@
+From saeed@marvell.com Tue Dec 22 11:50:16 2009
+From: Saeed Bishara <saeed@marvell.com>
+Date: Mon, 14 Dec 2009 17:17:33 +0200
+Subject: USB: ehci: add call of free_cached_itd_list() function in disable_periodic()
+To: linux-usb@vger.kernel.org
+Cc: Dmitri Epshtein <dima@marvell.com>, Saeed Bishara <saeed@marvell.com>
+Message-ID: <1260803854-4606-1-git-send-email-saeed@marvell.com>
+
+
+From: Dmitri Epshtein <dima@marvell.com>
+
+Sometimes disable_periodic() stop scan_periodic before than free_cached_itd_list() was called.
+In such case USB Host stacked during disconnect operation
+Solution: add call of free_cached_itd_list() function in disable_periodic()
+
+Signed-off-by: Dimitry Epshtein <dima@marvell.com>
+Signed-off-by: Saeed Bishara <saeed@marvell.com>
+Cc: David Brownell <dbrownell@users.sourceforge.net>
+Cc: Alan Stern <stern@rowland.harvard.edu>
+Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
+
+---
+ drivers/usb/host/ehci-sched.c | 2 ++
+ 1 file changed, 2 insertions(+)
+
+--- a/drivers/usb/host/ehci-sched.c
++++ b/drivers/usb/host/ehci-sched.c
+@@ -510,6 +510,8 @@ static int disable_periodic (struct ehci
+ ehci_writel(ehci, cmd, &ehci->regs->command);
+ /* posted write ... */
+
++ free_cached_itd_list(ehci);
++
+ ehci->next_uframe = -1;
+ return 0;
+ }
diff --git a/usb/usb-ehci-atmel.c-use-resource_size.patch b/usb/usb-ehci-atmel.c-use-resource_size.patch
new file mode 100644
index 00000000000000..a4e7e0e6253cdf
--- /dev/null
+++ b/usb/usb-ehci-atmel.c-use-resource_size.patch
@@ -0,0 +1,29 @@
+From hartleys@visionengravers.com Tue Dec 22 11:54:33 2009
+From: "H Hartley Sweeten" <hartleys@visionengravers.com>
+Date: Mon, 14 Dec 2009 18:15:35 -0500
+Subject: USB: ehci-atmel.c: use resource_size()
+Cc: "David Brownell" <dbrownell@users.sourceforge.net>
+Message-ID: <BD79186B4FD85F4B8E60E381CAEE19090200EAA6@mi8nycmail19.Mi8.com>
+
+
+Use resource_size().
+
+Signed-off-by: H Hartley Sweeten <hsweeten@visionengravers.com>
+Cc: David Brownell <dbrownell@users.sourceforge.net>
+Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
+
+---
+ drivers/usb/host/ehci-atmel.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+--- a/drivers/usb/host/ehci-atmel.c
++++ b/drivers/usb/host/ehci-atmel.c
+@@ -149,7 +149,7 @@ static int __init ehci_atmel_drv_probe(s
+ goto fail_request_resource;
+ }
+ hcd->rsrc_start = res->start;
+- hcd->rsrc_len = res->end - res->start + 1;
++ hcd->rsrc_len = resource_size(res);
+
+ if (!request_mem_region(hcd->rsrc_start, hcd->rsrc_len,
+ driver->description)) {
diff --git a/usb/usb-ehci-au1xxx.c-use-platform_get_resource-and-resource_size.patch b/usb/usb-ehci-au1xxx.c-use-platform_get_resource-and-resource_size.patch
new file mode 100644
index 00000000000000..e0feb1e0a2fe38
--- /dev/null
+++ b/usb/usb-ehci-au1xxx.c-use-platform_get_resource-and-resource_size.patch
@@ -0,0 +1,41 @@
+From hartleys@visionengravers.com Tue Dec 22 11:54:47 2009
+From: "H Hartley Sweeten" <hartleys@visionengravers.com>
+Date: Mon, 14 Dec 2009 18:22:42 -0500
+Subject: USB: ehci-au1xxx.c: use platform_get_resource() and resource_size()
+Cc: "David Woodhouse" <dwmw2@infradead.org>
+Message-ID: <BD79186B4FD85F4B8E60E381CAEE19090200EAAA@mi8nycmail19.Mi8.com>
+
+
+Use platform_get_resource() to fetch the memory resource and
+resource_size() for calculate the length.
+
+Signed-off-by: H Hartley Sweeten <hsweeten@visionengravers.com>
+Cc: David Brownell <dbrownell@users.sourceforge.net>
+Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
+
+---
+ drivers/usb/host/ehci-au1xxx.c | 6 ++++--
+ 1 file changed, 4 insertions(+), 2 deletions(-)
+
+--- a/drivers/usb/host/ehci-au1xxx.c
++++ b/drivers/usb/host/ehci-au1xxx.c
+@@ -121,6 +121,7 @@ static int ehci_hcd_au1xxx_drv_probe(str
+ {
+ struct usb_hcd *hcd;
+ struct ehci_hcd *ehci;
++ struct resource *res;
+ int ret;
+
+ if (usb_disabled())
+@@ -144,8 +145,9 @@ static int ehci_hcd_au1xxx_drv_probe(str
+ if (!hcd)
+ return -ENOMEM;
+
+- hcd->rsrc_start = pdev->resource[0].start;
+- hcd->rsrc_len = pdev->resource[0].end - pdev->resource[0].start + 1;
++ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
++ hcd->rsrc_start = res->start;
++ hcd->rsrc_len = resource_size(res);
+
+ if (!request_mem_region(hcd->rsrc_start, hcd->rsrc_len, hcd_name)) {
+ pr_debug("request_mem_region failed");
diff --git a/usb/usb-ehci-fix-audio-record-functionality-for-some-full-speed-sound-blaster-devices.patch b/usb/usb-ehci-fix-audio-record-functionality-for-some-full-speed-sound-blaster-devices.patch
new file mode 100644
index 00000000000000..05190361aa23ec
--- /dev/null
+++ b/usb/usb-ehci-fix-audio-record-functionality-for-some-full-speed-sound-blaster-devices.patch
@@ -0,0 +1,45 @@
+From saeed@marvell.com Tue Dec 22 11:51:36 2009
+From: Saeed Bishara <saeed@marvell.com>
+Date: Mon, 14 Dec 2009 17:17:34 +0200
+Subject: USB: ehci: fix audio record functionality for some Full speed sound blaster devices
+To: linux-usb@vger.kernel.org
+Cc: Dmitri Epshtein <dima@marvell.com>, Saeed Bishara <saeed@marvell.com>
+Message-ID: <1260803854-4606-2-git-send-email-saeed@marvell.com>
+
+
+From: Dmitri Epshtein <dima@marvell.com>
+
+This patch fix audio record functionality for some Full speed sound blaster devices.
+Issue: Sometimes transaction complete indication is coming from HW one frame later.
+Solution: If scan_periodic process now frame or previous frame now-1 and sitd transaction
+is not finished yet, exit scan_periodic function and check the same transaction in the next frame.
+
+Signed-off-by: Dimitry Epshtein <dima@marvell.com>
+Signed-off-by: Saeed Bishara <saeed@marvell.com>
+Cc: David Brownell <dbrownell@users.sourceforge.net>
+Cc: Alan Stern <stern@rowland.harvard.edu>
+Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
+
+---
+ drivers/usb/host/ehci-sched.c | 10 +++++++---
+ 1 file changed, 7 insertions(+), 3 deletions(-)
+
+--- a/drivers/usb/host/ehci-sched.c
++++ b/drivers/usb/host/ehci-sched.c
+@@ -2324,9 +2324,13 @@ restart:
+ * No need to check for activity unless the
+ * frame is current.
+ */
+- if (frame == clock_frame && live &&
+- (q.sitd->hw_results &
+- SITD_ACTIVE(ehci))) {
++ if (((frame == clock_frame) ||
++ (((frame + 1) % ehci->periodic_size)
++ == clock_frame))
++ && live
++ && (q.sitd->hw_results &
++ SITD_ACTIVE(ehci))) {
++
+ incomplete = true;
+ q_p = &q.sitd->sitd_next;
+ hw_p = &q.sitd->hw_next;
diff --git a/usb/usb-ehci-fsl-add-power-management-support.patch b/usb/usb-ehci-fsl-add-power-management-support.patch
new file mode 100644
index 00000000000000..f83ea35ca58760
--- /dev/null
+++ b/usb/usb-ehci-fsl-add-power-management-support.patch
@@ -0,0 +1,155 @@
+From avorontsov@ru.mvista.com Tue Dec 22 11:52:39 2009
+From: Anton Vorontsov <avorontsov@ru.mvista.com>
+Date: Mon, 14 Dec 2009 18:41:12 +0300
+Subject: USB: ehci-fsl: Add power management support
+To: Greg Kroah-Hartman <gregkh@suse.de>
+Cc: Scott Wood <scottwood@freescale.com>, Dave Liu <daveliu@freescale.com>, Jerry Huang <Chang-Ming.Huang@freescale.com>, linux-usb@vger.kernel.org, linuxppc-dev@ozlabs.org
+Message-ID: <20091214154112.GB26805@oksana.dev.rtsoft.ru>
+Content-Disposition: inline
+
+
+EHCI FSL controller preserve its state during sleep mode, so nothing
+fancy needs to be done.
+
+Though, during 'deep sleep' mode (as found in MPC831x CPUs) the
+controller turns off and needs to be reinitialized upon resume.
+
+This patch adds support for hibernation and resuming after deep sleep.
+Based on Dave Liu and Jerry Huang's work[1].
+
+[1] http://www.bitshrine.org/gpp/linux-fsl-2.6.24.3-MPC8315ERDB-usb-power-mangement.patch
+
+Signed-off-by: Anton Vorontsov <avorontsov@ru.mvista.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
+
+---
+ drivers/usb/host/ehci-fsl.c | 90 ++++++++++++++++++++++++++++++++++++++++----
+ 1 file changed, 83 insertions(+), 7 deletions(-)
+
+--- a/drivers/usb/host/ehci-fsl.c
++++ b/drivers/usb/host/ehci-fsl.c
+@@ -1,5 +1,6 @@
+ /*
+- * Copyright (c) 2005 MontaVista Software
++ * Copyright 2005-2009 MontaVista Software, Inc.
++ * Copyright 2008 Freescale Semiconductor, Inc.
+ *
+ * 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
+@@ -17,17 +18,20 @@
+ *
+ * Ported to 834x by Randy Vinson <rvinson@mvista.com> using code provided
+ * by Hunter Wu.
++ * Power Management support by Dave Liu <daveliu@freescale.com>,
++ * Jerry Huang <Chang-Ming.Huang@freescale.com> and
++ * Anton Vorontsov <avorontsov@ru.mvista.com>.
+ */
+
++#include <linux/kernel.h>
++#include <linux/types.h>
++#include <linux/delay.h>
++#include <linux/pm.h>
+ #include <linux/platform_device.h>
+ #include <linux/fsl_devices.h>
+
+ #include "ehci-fsl.h"
+
+-/* FIXME: Power Management is un-ported so temporarily disable it */
+-#undef CONFIG_PM
+-
+-
+ /* configure so an HC device and id are always provided */
+ /* always called with process context; sleeping is OK */
+
+@@ -285,10 +289,81 @@ static int ehci_fsl_setup(struct usb_hcd
+ return retval;
+ }
+
++struct ehci_fsl {
++ struct ehci_hcd ehci;
++
++#ifdef CONFIG_PM
++ /* Saved USB PHY settings, need to restore after deep sleep. */
++ u32 usb_ctrl;
++#endif
++};
++
++#ifdef CONFIG_PM
++
++static struct ehci_fsl *hcd_to_ehci_fsl(struct usb_hcd *hcd)
++{
++ struct ehci_hcd *ehci = hcd_to_ehci(hcd);
++
++ return container_of(ehci, struct ehci_fsl, ehci);
++}
++
++static int ehci_fsl_drv_suspend(struct device *dev)
++{
++ struct usb_hcd *hcd = dev_get_drvdata(dev);
++ struct ehci_fsl *ehci_fsl = hcd_to_ehci_fsl(hcd);
++ void __iomem *non_ehci = hcd->regs;
++
++ if (!fsl_deep_sleep())
++ return 0;
++
++ ehci_fsl->usb_ctrl = in_be32(non_ehci + FSL_SOC_USB_CTRL);
++ return 0;
++}
++
++static int ehci_fsl_drv_resume(struct device *dev)
++{
++ struct usb_hcd *hcd = dev_get_drvdata(dev);
++ struct ehci_fsl *ehci_fsl = hcd_to_ehci_fsl(hcd);
++ struct ehci_hcd *ehci = hcd_to_ehci(hcd);
++ void __iomem *non_ehci = hcd->regs;
++
++ if (!fsl_deep_sleep())
++ return 0;
++
++ usb_root_hub_lost_power(hcd->self.root_hub);
++
++ /* Restore USB PHY settings and enable the controller. */
++ out_be32(non_ehci + FSL_SOC_USB_CTRL, ehci_fsl->usb_ctrl);
++
++ ehci_reset(ehci);
++ ehci_fsl_reinit(ehci);
++
++ return 0;
++}
++
++static int ehci_fsl_drv_restore(struct device *dev)
++{
++ struct usb_hcd *hcd = dev_get_drvdata(dev);
++
++ usb_root_hub_lost_power(hcd->self.root_hub);
++ return 0;
++}
++
++static struct dev_pm_ops ehci_fsl_pm_ops = {
++ .suspend = ehci_fsl_drv_suspend,
++ .resume = ehci_fsl_drv_resume,
++ .restore = ehci_fsl_drv_restore,
++};
++
++#define EHCI_FSL_PM_OPS (&ehci_fsl_pm_ops)
++#else
++#define EHCI_FSL_PM_OPS NULL
++#endif /* CONFIG_PM */
++
+ static const struct hc_driver ehci_fsl_hc_driver = {
+ .description = hcd_name,
+ .product_desc = "Freescale On-Chip EHCI Host Controller",
+- .hcd_priv_size = sizeof(struct ehci_hcd),
++ .hcd_priv_size = sizeof(struct ehci_fsl),
+
+ /*
+ * generic hardware linkage
+@@ -355,6 +430,7 @@ static struct platform_driver ehci_fsl_d
+ .remove = ehci_fsl_drv_remove,
+ .shutdown = usb_hcd_platform_shutdown,
+ .driver = {
+- .name = "fsl-ehci",
++ .name = "fsl-ehci",
++ .pm = EHCI_FSL_PM_OPS,
+ },
+ };
diff --git a/usb/usb-ehci-fsl-fix-sparse-warnings.patch b/usb/usb-ehci-fsl-fix-sparse-warnings.patch
new file mode 100644
index 00000000000000..d7d204bf6f474f
--- /dev/null
+++ b/usb/usb-ehci-fsl-fix-sparse-warnings.patch
@@ -0,0 +1,45 @@
+From avorontsov@ru.mvista.com Tue Dec 22 11:52:20 2009
+From: Anton Vorontsov <avorontsov@ru.mvista.com>
+Date: Mon, 14 Dec 2009 18:41:05 +0300
+Subject: USB: ehci-fsl: Fix sparse warnings
+To: Greg Kroah-Hartman <gregkh@suse.de>
+Cc: Scott Wood <scottwood@freescale.com>, Dave Liu <daveliu@freescale.com>, Jerry Huang <Chang-Ming.Huang@freescale.com>, linux-usb@vger.kernel.org, linuxppc-dev@ozlabs.org
+Message-ID: <20091214154105.GA26805@oksana.dev.rtsoft.ru>
+Content-Disposition: inline
+
+
+This patch fixes following warnings:
+
+ehci-fsl.c:43:5: warning: symbol 'usb_hcd_fsl_probe' was not declared. Should it be static?
+ehci-fsl.c:150:6: warning: symbol 'usb_hcd_fsl_remove' was not declared. Should it be static?
+
+Signed-off-by: Anton Vorontsov <avorontsov@ru.mvista.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
+
+---
+ drivers/usb/host/ehci-fsl.c | 7 ++++---
+ 1 file changed, 4 insertions(+), 3 deletions(-)
+
+--- a/drivers/usb/host/ehci-fsl.c
++++ b/drivers/usb/host/ehci-fsl.c
+@@ -40,8 +40,8 @@
+ * Allocates basic resources for this USB host controller.
+ *
+ */
+-int usb_hcd_fsl_probe(const struct hc_driver *driver,
+- struct platform_device *pdev)
++static int usb_hcd_fsl_probe(const struct hc_driver *driver,
++ struct platform_device *pdev)
+ {
+ struct fsl_usb2_platform_data *pdata;
+ struct usb_hcd *hcd;
+@@ -147,7 +147,8 @@ int usb_hcd_fsl_probe(const struct hc_dr
+ * Reverses the effect of usb_hcd_fsl_probe().
+ *
+ */
+-void usb_hcd_fsl_remove(struct usb_hcd *hcd, struct platform_device *pdev)
++static void usb_hcd_fsl_remove(struct usb_hcd *hcd,
++ struct platform_device *pdev)
+ {
+ usb_remove_hcd(hcd);
+ iounmap(hcd->regs);
diff --git a/usb/usb-ehci-orion.c-use-resource_size.patch b/usb/usb-ehci-orion.c-use-resource_size.patch
new file mode 100644
index 00000000000000..b4dffeab239203
--- /dev/null
+++ b/usb/usb-ehci-orion.c-use-resource_size.patch
@@ -0,0 +1,55 @@
+From hartleys@visionengravers.com Tue Dec 22 11:54:19 2009
+From: "H Hartley Sweeten" <hartleys@visionengravers.com>
+Date: Mon, 14 Dec 2009 18:13:00 -0500
+Subject: USB: ehci-orion.c: use resource_size()
+Cc: "David Brownell" <dbrownell@users.sourceforge.net>
+Message-ID: <BD79186B4FD85F4B8E60E381CAEE19090200EAA5@mi8nycmail19.Mi8.com>
+
+
+Use resource_size().
+
+Signed-off-by: H Hartley Sweeten <hsweeten@visionengravers.com>
+Cc: David Brownell <dbrownell@users.sourceforge.net>
+Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
+
+---
+ drivers/usb/host/ehci-orion.c | 8 ++++----
+ 1 file changed, 4 insertions(+), 4 deletions(-)
+
+--- a/drivers/usb/host/ehci-orion.c
++++ b/drivers/usb/host/ehci-orion.c
+@@ -222,14 +222,14 @@ static int __devinit ehci_orion_drv_prob
+ goto err1;
+ }
+
+- if (!request_mem_region(res->start, res->end - res->start + 1,
++ if (!request_mem_region(res->start, resource_size(res),
+ ehci_orion_hc_driver.description)) {
+ dev_dbg(&pdev->dev, "controller already in use\n");
+ err = -EBUSY;
+ goto err1;
+ }
+
+- regs = ioremap(res->start, res->end - res->start + 1);
++ regs = ioremap(res->start, resource_size(res));
+ if (regs == NULL) {
+ dev_dbg(&pdev->dev, "error mapping memory\n");
+ err = -EFAULT;
+@@ -244,7 +244,7 @@ static int __devinit ehci_orion_drv_prob
+ }
+
+ hcd->rsrc_start = res->start;
+- hcd->rsrc_len = res->end - res->start + 1;
++ hcd->rsrc_len = resource_size(res);
+ hcd->regs = regs;
+
+ ehci = hcd_to_ehci(hcd);
+@@ -287,7 +287,7 @@ err4:
+ err3:
+ iounmap(regs);
+ err2:
+- release_mem_region(res->start, res->end - res->start + 1);
++ release_mem_region(res->start, resource_size(res));
+ err1:
+ dev_err(&pdev->dev, "init %s fail, %d\n",
+ dev_name(&pdev->dev), err);
diff --git a/usb/usb-export-usb_quirk_reset_morphs-through-sysfs.patch b/usb/usb-export-usb_quirk_reset_morphs-through-sysfs.patch
new file mode 100644
index 00000000000000..ae9ef92afb66f1
--- /dev/null
+++ b/usb/usb-export-usb_quirk_reset_morphs-through-sysfs.patch
@@ -0,0 +1,67 @@
+From oliver@neukum.org Tue Dec 22 12:00:05 2009
+From: Oliver Neukum <oliver@neukum.org>
+Date: Fri, 18 Dec 2009 12:15:24 +0100
+Subject: USB: Export USB_QUIRK_RESET_MORPHS through sysfs
+To: Greg KH <greg@kroah.com>, Alan Stern <stern@rowland.harvard.edu>, Stefan Seyfried <stefan.seyfried@googlemail.com>, Matthew Dharm <mdharm-kernel@one-eyed-alien.net>, Matthew Garrett <mjg59@srcf.ucam.org>, Josua Dietze <digidietze@draisberghof.de>, linux-usb@vger.kernel.org
+Message-ID: <200912181215.24843.oliver@neukum.org>
+
+
+Some devices which use mode switching revert to their
+primary mode as they are reset. They must not be reset for
+error handling. As user spaces makes the switch it also
+has to tell the kernel that a device is quirky.
+
+Signed-off-by: Oliver Neukum <oliver@neukum.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
+
+---
+ drivers/usb/core/sysfs.c | 31 +++++++++++++++++++++++++++++++
+ 1 file changed, 31 insertions(+)
+
+--- a/drivers/usb/core/sysfs.c
++++ b/drivers/usb/core/sysfs.c
+@@ -185,6 +185,36 @@ show_quirks(struct device *dev, struct d
+ static DEVICE_ATTR(quirks, S_IRUGO, show_quirks, NULL);
+
+ static ssize_t
++show_reset_quirk(struct device *dev, struct device_attribute *attr, char *buf)
++{
++ struct usb_device *udev;
++
++ udev = to_usb_device(dev);
++ return sprintf(buf, "%d\n", !!(udev->quirks & USB_QUIRK_RESET_MORPHS));
++}
++
++static ssize_t
++set_reset_quirk(struct device *dev, struct device_attribute *attr,
++ const char *buf, size_t count)
++{
++ struct usb_device *udev = to_usb_device(dev);
++ int config, value;
++
++ if (sscanf(buf, "%d", &config) != 1 || config < 0 || config > 1)
++ return -EINVAL;
++ usb_lock_device(udev);
++ if (config)
++ udev->quirks |= USB_QUIRK_RESET_MORPHS;
++ else
++ udev->quirks &= ~USB_QUIRK_RESET_MORPHS;
++ usb_unlock_device(udev);
++ return count;
++}
++
++static DEVICE_ATTR(reset_quirk, S_IRUGO | S_IWUSR,
++ show_reset_quirk, set_reset_quirk);
++
++static ssize_t
+ show_urbnum(struct device *dev, struct device_attribute *attr, char *buf)
+ {
+ struct usb_device *udev;
+@@ -552,6 +582,7 @@ static struct attribute *dev_attrs[] = {
+ &dev_attr_version.attr,
+ &dev_attr_maxchild.attr,
+ &dev_attr_quirks.attr,
++ &dev_attr_reset_quirk.attr,
+ &dev_attr_authorized.attr,
+ &dev_attr_remove.attr,
+ NULL,
diff --git a/usb/usb-fix-occasional-ulpi-timeouts-with-ehci-mxc.patch b/usb/usb-fix-occasional-ulpi-timeouts-with-ehci-mxc.patch
new file mode 100644
index 00000000000000..aebee9a86bf0c2
--- /dev/null
+++ b/usb/usb-fix-occasional-ulpi-timeouts-with-ehci-mxc.patch
@@ -0,0 +1,59 @@
+From valentin.longchamp@epfl.ch Tue Dec 22 11:28:19 2009
+From: Valentin Longchamp <valentin.longchamp@epfl.ch>
+Date: Wed, 2 Dec 2009 17:13:01 +0100
+Subject: USB: fix occasional ULPI timeouts with ehci-mxc
+To: daniel@caiaq.de
+Cc: acassis@gmail.com, linux-arm-kernel@lists.infradead.org, linux-usb@vger.kernel.org, Valentin Longchamp <valentin.longchamp@epfl.ch>
+Message-ID: <1259770381-16987-1-git-send-email-valentin.longchamp@epfl.ch>
+
+
+On various mxc boards, the intial ULPI reads resulted in a timeout
+which prevented the transceiver to be identified and thus the ehci
+device to be probed.
+
+Initializing the hardware lines connected to the transceiver (through
+pdata->init call) before actually enabling clocks and configuring
+registers in the devices fixes this problem.
+
+Signed-off-by: Valentin Longchamp <valentin.longchamp@epfl.ch>
+Acked-by: Daniel Mack <daniel@caiaq.de>
+Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
+
+---
+ drivers/usb/host/ehci-mxc.c | 18 +++++++++---------
+ 1 file changed, 9 insertions(+), 9 deletions(-)
+
+--- a/drivers/usb/host/ehci-mxc.c
++++ b/drivers/usb/host/ehci-mxc.c
+@@ -162,6 +162,15 @@ static int ehci_mxc_drv_probe(struct pla
+ goto err_ioremap;
+ }
+
++ /* call platform specific init function */
++ if (pdata->init) {
++ ret = pdata->init(pdev);
++ if (ret) {
++ dev_err(dev, "platform init failed\n");
++ goto err_init;
++ }
++ }
++
+ /* enable clocks */
+ priv->usbclk = clk_get(dev, "usb");
+ if (IS_ERR(priv->usbclk)) {
+@@ -192,15 +201,6 @@ static int ehci_mxc_drv_probe(struct pla
+ if (ret < 0)
+ goto err_init;
+
+- /* call platform specific init function */
+- if (pdata->init) {
+- ret = pdata->init(pdev);
+- if (ret) {
+- dev_err(dev, "platform init failed\n");
+- goto err_init;
+- }
+- }
+-
+ /* most platforms need some time to settle changed IO settings */
+ mdelay(10);
+
diff --git a/usb/usb-ftdi_sio-fix-error-message-on-close.patch b/usb/usb-ftdi_sio-fix-error-message-on-close.patch
new file mode 100644
index 00000000000000..f6294ec6efd7f7
--- /dev/null
+++ b/usb/usb-ftdi_sio-fix-error-message-on-close.patch
@@ -0,0 +1,29 @@
+From jhovold@gmail.com Tue Dec 22 11:25:18 2009
+From: Johan Hovold <jhovold@gmail.com>
+Date: Sun, 22 Nov 2009 17:01:13 +0100
+Subject: USB: ftdi_sio: fix error message on close
+To: Greg Kroah-Hartman <gregkh@suse.de>
+Cc: Alan Cox <alan@linux.intel.com>, linux-usb@vger.kernel.org, linux-kernel@vger.kernel.org, Johan Hovold <jhovold@gmail.com>
+Message-ID: <1258905673-6885-1-git-send-email-jhovold@gmail.com>
+
+
+Resubmitting read urb fails with -EPERM if completion handler runs while
+urb is being killed on close. This should not be reported as an error.
+
+Signed-off-by: Johan Hovold <jhovold@gmail.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
+---
+ drivers/usb/serial/ftdi_sio.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+--- a/drivers/usb/serial/ftdi_sio.c
++++ b/drivers/usb/serial/ftdi_sio.c
+@@ -1700,7 +1700,7 @@ static int ftdi_submit_read_urb(struct u
+ urb->transfer_buffer_length,
+ ftdi_read_bulk_callback, port);
+ result = usb_submit_urb(urb, mem_flags);
+- if (result)
++ if (result && result != -EPERM)
+ dev_err(&port->dev,
+ "%s - failed submitting read urb, error %d\n",
+ __func__, result);
diff --git a/usb/usb-ftdi_sio-fix-initialisation-of-latency-timeout.patch b/usb/usb-ftdi_sio-fix-initialisation-of-latency-timeout.patch
new file mode 100644
index 00000000000000..459dfa199be012
--- /dev/null
+++ b/usb/usb-ftdi_sio-fix-initialisation-of-latency-timeout.patch
@@ -0,0 +1,52 @@
+From jhovold@gmail.com Tue Dec 22 11:25:51 2009
+From: Johan Hovold <jhovold@gmail.com>
+Date: Sun, 22 Nov 2009 21:25:20 +0100
+Subject: USB: ftdi_sio: fix initialisation of latency timeout
+To: Greg Kroah-Hartman <gregkh@suse.de>
+Cc: Alan Cox <alan@linux.intel.com>, linux-usb@vger.kernel.org, linux-kernel@vger.kernel.org, Johan Hovold <jhovold@gmail.com>
+Message-ID: <1258921520-22609-1-git-send-email-jhovold@gmail.com>
+
+
+Latency timeout was read but never stored on port probe. When
+ASYNC_LOW_LATENCY was cleared the device timeout would get set to 0
+rather than the default 16ms.
+
+Signed-off-by: Johan Hovold <jhovold@gmail.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
+
+---
+ drivers/usb/serial/ftdi_sio.c | 9 +++++----
+ 1 file changed, 5 insertions(+), 4 deletions(-)
+
+--- a/drivers/usb/serial/ftdi_sio.c
++++ b/drivers/usb/serial/ftdi_sio.c
+@@ -1164,7 +1164,6 @@ static int read_latency_timer(struct usb
+ unsigned short latency = 0;
+ int rv = 0;
+
+-
+ dbg("%s", __func__);
+
+ rv = usb_control_msg(udev,
+@@ -1177,8 +1176,9 @@ static int read_latency_timer(struct usb
+ if (rv < 0) {
+ dev_err(&port->dev, "Unable to read latency timer: %i\n", rv);
+ return -EIO;
+- }
+- return latency;
++ } else
++ priv->latency = latency;
++ return rv;
+ }
+
+ static int get_serial_info(struct usb_serial_port *port,
+@@ -1564,7 +1564,8 @@ static int ftdi_sio_port_probe(struct us
+
+ ftdi_determine_type(port);
+ ftdi_set_max_packet_size(port);
+- read_latency_timer(port);
++ if (read_latency_timer(port) < 0)
++ priv->latency = 16;
+ create_sysfs_attrs(port);
+ return 0;
+ }
diff --git a/usb/usb-ftdi_sio-remove-obsolete-comment.patch b/usb/usb-ftdi_sio-remove-obsolete-comment.patch
new file mode 100644
index 00000000000000..faf996a6ac3d7d
--- /dev/null
+++ b/usb/usb-ftdi_sio-remove-obsolete-comment.patch
@@ -0,0 +1,30 @@
+From jhovold@gmail.com Tue Dec 22 11:25:27 2009
+From: Johan Hovold <jhovold@gmail.com>
+Date: Sun, 22 Nov 2009 17:25:08 +0100
+Subject: USB: ftdi_sio: remove obsolete comment
+To: Greg Kroah-Hartman <gregkh@suse.de>
+Cc: Alan Cox <alan@linux.intel.com>, linux-usb@vger.kernel.org, linux-kernel@vger.kernel.org, Johan Hovold <jhovold@gmail.com>
+Message-ID: <1258907108-11949-1-git-send-email-jhovold@gmail.com>
+
+
+We always push characters to ldisc immediately regardless of
+ASYNC_LOW_LATENCY.
+
+Signed-off-by: Johan Hovold <jhovold@gmail.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
+---
+ drivers/usb/serial/ftdi_sio.c | 3 +--
+ 1 file changed, 1 insertion(+), 2 deletions(-)
+
+--- a/drivers/usb/serial/ftdi_sio.c
++++ b/drivers/usb/serial/ftdi_sio.c
+@@ -1532,8 +1532,7 @@ static int ftdi_sio_port_probe(struct us
+ kref_init(&priv->kref);
+ spin_lock_init(&priv->tx_lock);
+ init_waitqueue_head(&priv->delta_msr_wait);
+- /* This will push the characters through immediately rather
+- than queue a task to deliver them */
++
+ priv->flags = ASYNC_LOW_LATENCY;
+
+ if (quirk && quirk->port_probe)
diff --git a/usb/usb-ftdi_sio-remove-support-for-5-and-6-data-bits.patch b/usb/usb-ftdi_sio-remove-support-for-5-and-6-data-bits.patch
new file mode 100644
index 00000000000000..425264ce2e39b5
--- /dev/null
+++ b/usb/usb-ftdi_sio-remove-support-for-5-and-6-data-bits.patch
@@ -0,0 +1,37 @@
+From mark.adamson@ftdichip.com Tue Dec 22 11:26:30 2009
+From: Mark Adamson <mark.adamson@ftdichip.com>
+Date: Tue, 24 Nov 2009 09:39:10 +0000
+Subject: USB: ftdi_sio: remove support for 5 and 6 data bits
+To: "gregkh@suse.de" <gregkh@suse.de>
+Message-ID: <8D2B66FD8A5723429FEFCD40CC13CDD1F9776EFEAA@GLAEXSV2.ftdi.local>
+
+
+From: Mark Adamson <mark.adamson@ftdichip.com>
+
+Removed CS5 and CS6 from data bits since these are not supported
+in FTDI hardware.
+
+
+Signed-off-by: Mark J. Adamson <mark.adamson@ftdichip.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
+
+---
+ drivers/usb/serial/ftdi_sio.c | 4 +---
+ 1 file changed, 1 insertion(+), 3 deletions(-)
+
+--- a/drivers/usb/serial/ftdi_sio.c
++++ b/drivers/usb/serial/ftdi_sio.c
+@@ -2226,12 +2226,10 @@ static void ftdi_set_termios(struct tty_
+ }
+ if (cflag & CSIZE) {
+ switch (cflag & CSIZE) {
+- case CS5: urb_value |= 5; dbg("Setting CS5"); break;
+- case CS6: urb_value |= 6; dbg("Setting CS6"); break;
+ case CS7: urb_value |= 7; dbg("Setting CS7"); break;
+ case CS8: urb_value |= 8; dbg("Setting CS8"); break;
+ default:
+- dev_err(&port->dev, "CSIZE was set but not CS5-CS8\n");
++ dev_err(&port->dev, "CSIZE was set but not CS7-CS8\n");
+ }
+ }
+
diff --git a/usb/usb-gadget-add-int-support-for-blackfin-musb.patch b/usb/usb-gadget-add-int-support-for-blackfin-musb.patch
new file mode 100644
index 00000000000000..fb9c4d59132898
--- /dev/null
+++ b/usb/usb-gadget-add-int-support-for-blackfin-musb.patch
@@ -0,0 +1,33 @@
+From vapier@gentoo.org Tue Dec 22 12:05:31 2009
+From: Cliff Cai <cliff.cai@analog.com>
+Date: Mon, 21 Dec 2009 10:42:39 -0500
+Subject: USB: gadget: add INT support for Blackfin musb
+To: linux-usb@vger.kernel.org, Greg Kroah-Hartman <gregkh@suse.de>
+Cc: Felipe Balbi <felipe.balbi@nokia.com>, uclinux-dist-devel@blackfin.uclinux.org, Cliff Cai <cliff.cai@analog.com>
+Message-ID: <1261410159-17375-1-git-send-email-vapier@gentoo.org>
+
+
+From: Cliff Cai <cliff.cai@analog.com>
+
+Signed-off-by: Cliff Cai <cliff.cai@analog.com>
+Signed-off-by: Mike Frysinger <vapier@gentoo.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
+
+---
+ drivers/usb/gadget/epautoconf.c | 5 +++++
+ 1 file changed, 5 insertions(+)
+
+--- a/drivers/usb/gadget/epautoconf.c
++++ b/drivers/usb/gadget/epautoconf.c
+@@ -284,6 +284,11 @@ struct usb_ep * __init usb_ep_autoconfig
+ ep = find_ep (gadget, "ep5in");
+ else
+ ep = find_ep (gadget, "ep6out");
++ } else if (USB_ENDPOINT_XFER_INT == type) {
++ if (USB_DIR_IN & desc->bEndpointAddress)
++ ep = find_ep(gadget, "ep1in");
++ else
++ ep = find_ep(gadget, "ep2out");
+ } else
+ ep = NULL;
+ if (ep && ep_matches (gadget, ep, desc))
diff --git a/usb/usb-gadget-use-ep5-for-bulk-in-and-ep6-for-bulk-out-for-blackfin-musb.patch b/usb/usb-gadget-use-ep5-for-bulk-in-and-ep6-for-bulk-out-for-blackfin-musb.patch
new file mode 100644
index 00000000000000..9904fcb59c93f0
--- /dev/null
+++ b/usb/usb-gadget-use-ep5-for-bulk-in-and-ep6-for-bulk-out-for-blackfin-musb.patch
@@ -0,0 +1,50 @@
+From vapier@gentoo.org Tue Dec 22 12:05:03 2009
+From: Bryan Wu <cooloney@kernel.org>
+Date: Mon, 21 Dec 2009 10:43:06 -0500
+Subject: USB: gadget: use ep5 for bulk-in and ep6 for bulk-out for Blackfin MUSB
+To: linux-usb@vger.kernel.org, Greg Kroah-Hartman <gregkh@suse.de>
+Cc: Felipe Balbi <felipe.balbi@nokia.com>, uclinux-dist-devel@blackfin.uclinux.org, Bryan Wu <cooloney@kernel.org>, Cliff Cai <cliff.cai@analog.com>
+Message-ID: <1261410186-17415-1-git-send-email-vapier@gentoo.org>
+
+
+From: Bryan Wu <cooloney@kernel.org>
+
+Normally, the musb uses ep1 as the bidirectional bulk endpoint. This won't
+work on the Blackfin musb as all endpoints (except ep0) are unidirectional.
+Further, ep1-ep4 have a small 128 byte FIFO which makes them undesirable
+for bulk endpoints (which need more like a 512 byte FIFO). This leaves us
+with ep5-ep7 which have 1024 byte FIFOs and can be configured as either
+in/out and bulk/interrupt/iso on the fly.
+
+Signed-off-by: Bryan Wu <cooloney@kernel.org>
+Signed-off-by: Cliff Cai <cliff.cai@analog.com>
+Signed-off-by: Mike Frysinger <vapier@gentoo.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
+
+---
+ drivers/usb/gadget/epautoconf.c | 14 ++++++++++++++
+ 1 file changed, 14 insertions(+)
+
+--- a/drivers/usb/gadget/epautoconf.c
++++ b/drivers/usb/gadget/epautoconf.c
+@@ -275,6 +275,20 @@ struct usb_ep * __init usb_ep_autoconfig
+ ep = find_ep (gadget, "ep1-bulk");
+ if (ep && ep_matches (gadget, ep, desc))
+ return ep;
++
++#ifdef CONFIG_BLACKFIN
++ } else if (gadget_is_musbhsfc(gadget) || gadget_is_musbhdrc(gadget)) {
++ if ((USB_ENDPOINT_XFER_BULK == type) ||
++ (USB_ENDPOINT_XFER_ISOC == type)) {
++ if (USB_DIR_IN & desc->bEndpointAddress)
++ ep = find_ep (gadget, "ep5in");
++ else
++ ep = find_ep (gadget, "ep6out");
++ } else
++ ep = NULL;
++ if (ep && ep_matches (gadget, ep, desc))
++ return ep;
++#endif
+ }
+
+ /* Second, look at endpoints until an unclaimed one looks usable */
diff --git a/usb/usb-host-sl811-allow-the-hcd-on-blackfin-systems.patch b/usb/usb-host-sl811-allow-the-hcd-on-blackfin-systems.patch
new file mode 100644
index 00000000000000..dbeba6f277165c
--- /dev/null
+++ b/usb/usb-host-sl811-allow-the-hcd-on-blackfin-systems.patch
@@ -0,0 +1,30 @@
+From vapier@gentoo.org Tue Dec 22 12:03:17 2009
+From: Michael Hennerich <michael.hennerich@analog.com>
+Date: Mon, 21 Dec 2009 11:16:00 -0500
+Subject: USB: host: SL811: allow the hcd on Blackfin systems
+To: linux-usb@vger.kernel.org, Greg Kroah-Hartman <gregkh@suse.de>
+Cc: uclinux-dist-devel@blackfin.uclinux.org, Michael Hennerich <michael.hennerich@analog.com>, Bryan Wu <cooloney@kernel.org>
+Message-ID: <1261412160-20666-2-git-send-email-vapier@gentoo.org>
+
+
+From: Michael Hennerich <michael.hennerich@analog.com>
+
+Signed-off-by: Michael Hennerich <michael.hennerich@analog.com>
+Signed-off-by: Bryan Wu <cooloney@kernel.org>
+Signed-off-by: Mike Frysinger <vapier@gentoo.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
+
+---
+ drivers/usb/Kconfig | 1 +
+ 1 file changed, 1 insertion(+)
+
+--- a/drivers/usb/Kconfig
++++ b/drivers/usb/Kconfig
+@@ -21,6 +21,7 @@ config USB_ARCH_HAS_HCD
+ default y if USB_ARCH_HAS_EHCI
+ default y if PCMCIA && !M32R # sl811_cs
+ default y if ARM # SL-811
++ default y if BLACKFIN # SL-811
+ default y if SUPERH # r8a66597-hcd
+ default PCI
+
diff --git a/usb/usb-host-sl811-fix-unaligned-accesses.patch b/usb/usb-host-sl811-fix-unaligned-accesses.patch
new file mode 100644
index 00000000000000..eadc7879125d48
--- /dev/null
+++ b/usb/usb-host-sl811-fix-unaligned-accesses.patch
@@ -0,0 +1,45 @@
+From vapier@gentoo.org Tue Dec 22 12:02:59 2009
+From: Michael Hennerich <michael.hennerich@analog.com>
+Date: Mon, 21 Dec 2009 12:53:24 -0500
+Subject: USB: host: SL811: fix unaligned accesses
+To: linux-usb@vger.kernel.org, Greg Kroah-Hartman <gregkh@suse.de>
+Cc: uclinux-dist-devel@blackfin.uclinux.org, Michael Hennerich <michael.hennerich@analog.com>, Bryan Wu <cooloney@kernel.org>
+Message-ID: <1261418004-11787-1-git-send-email-vapier@gentoo.org>
+
+
+From: Michael Hennerich <michael.hennerich@analog.com>
+
+Signed-off-by: Michael Hennerich <michael.hennerich@analog.com>
+Signed-off-by: Bryan Wu <cooloney@kernel.org>
+Signed-off-by: Mike Frysinger <vapier@gentoo.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
+
+---
+ drivers/usb/host/sl811-hcd.c | 5 +++--
+ 1 file changed, 3 insertions(+), 2 deletions(-)
+
+--- a/drivers/usb/host/sl811-hcd.c
++++ b/drivers/usb/host/sl811-hcd.c
+@@ -51,6 +51,7 @@
+ #include <asm/irq.h>
+ #include <asm/system.h>
+ #include <asm/byteorder.h>
++#include <asm/unaligned.h>
+
+ #include "../core/hcd.h"
+ #include "sl811.h"
+@@ -1272,12 +1273,12 @@ sl811h_hub_control(
+ sl811h_hub_descriptor(sl811, (struct usb_hub_descriptor *) buf);
+ break;
+ case GetHubStatus:
+- *(__le32 *) buf = cpu_to_le32(0);
++ put_unaligned_le32(0, buf);
+ break;
+ case GetPortStatus:
+ if (wIndex != 1)
+ goto error;
+- *(__le32 *) buf = cpu_to_le32(sl811->port1);
++ put_unaligned_le32(sl811->port1, buf);
+
+ #ifndef VERBOSE
+ if (*(u16*)(buf+2)) /* only if wPortChange is interesting */
diff --git a/usb/usb-isp1362-use-kzalloc-for-allocating-only-one-thing.patch b/usb/usb-isp1362-use-kzalloc-for-allocating-only-one-thing.patch
new file mode 100644
index 00000000000000..a371b44615f567
--- /dev/null
+++ b/usb/usb-isp1362-use-kzalloc-for-allocating-only-one-thing.patch
@@ -0,0 +1,42 @@
+From julia@diku.dk Tue Dec 22 12:02:00 2009
+From: Julia Lawall <julia@diku.dk>
+Date: Sat, 19 Dec 2009 08:17:44 +0100 (CET)
+Subject: USB: isp1362: Use kzalloc for allocating only one thing
+To: Greg Kroah-Hartman <gregkh@suse.de>, linux-usb@vger.kernel.org, linux-kernel@vger.kernel.org, kernel-janitors@vger.kernel.org
+Message-ID: <Pine.LNX.4.64.0912190817280.30546@ask.diku.dk>
+
+
+From: Julia Lawall <julia@diku.dk>
+
+Use kzalloc rather than kcalloc(1,...)
+
+The semantic patch that makes this change is as follows:
+(http://coccinelle.lip6.fr/)
+
+// <smpl>
+@@
+@@
+
+- kcalloc(1,
++ kzalloc(
+ ...)
+// </smpl>
+
+Signed-off-by: Julia Lawall <julia@diku.dk>
+Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
+
+---
+ drivers/usb/host/isp1362-hcd.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+--- a/drivers/usb/host/isp1362-hcd.c
++++ b/drivers/usb/host/isp1362-hcd.c
+@@ -1257,7 +1257,7 @@ static int isp1362_urb_enqueue(struct us
+
+ /* avoid all allocations within spinlocks: request or endpoint */
+ if (!hep->hcpriv) {
+- ep = kcalloc(1, sizeof *ep, mem_flags);
++ ep = kzalloc(sizeof *ep, mem_flags);
+ if (!ep)
+ return -ENOMEM;
+ }
diff --git a/usb/usb-mxc-add-i.mx21-specific-usb-host-controller-driver.patch b/usb/usb-mxc-add-i.mx21-specific-usb-host-controller-driver.patch
new file mode 100644
index 00000000000000..d333de76e598ed
--- /dev/null
+++ b/usb/usb-mxc-add-i.mx21-specific-usb-host-controller-driver.patch
@@ -0,0 +1,2862 @@
+From mfuzzey@gmail.com Tue Dec 22 11:21:21 2009
+From: Martin Fuzzey <mfuzzey@gmail.com>
+Date: Sat, 21 Nov 2009 12:14:48 +0100
+Subject: USB: MXC: Add i.MX21 specific USB host controller driver.
+To: Greg KH <greg@kroah.com>, Sascha Hauer <s.hauer@pengutronix.de>
+Cc: linux-usb@vger.kernel.org, linux-arm-kernel@lists.infradead.org
+Message-ID: <20091121111448.7904.32862.stgit@srv002.fuzzey.net>
+
+
+This driver is a Full / Low speed only USB host for the i.MX21.
+
+Signed-off-by: Martin Fuzzey <mfuzzey@gmail.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
+
+
+---
+ arch/arm/plat-mxc/include/mach/mx21-usbhost.h | 38
+ drivers/usb/Makefile | 1
+ drivers/usb/host/Kconfig | 11
+ drivers/usb/host/Makefile | 2
+ drivers/usb/host/imx21-dbg.c | 527 +++++++
+ drivers/usb/host/imx21-hcd.c | 1789 ++++++++++++++++++++++++++
+ drivers/usb/host/imx21-hcd.h | 436 ++++++
+ 7 files changed, 2804 insertions(+)
+
+--- /dev/null
++++ b/arch/arm/plat-mxc/include/mach/mx21-usbhost.h
+@@ -0,0 +1,38 @@
++/*
++ * Copyright (C) 2009 Martin Fuzzey <mfuzzey@gmail.com>
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation; either version 2 of the License, or
++ * (at your option) any later version.
++ *
++ * This program is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
++ * GNU General Public License for more details.
++ */
++
++#ifndef __ASM_ARCH_MX21_USBH
++#define __ASM_ARCH_MX21_USBH
++
++enum mx21_usbh_xcvr {
++ /* Values below as used by hardware (HWMODE register) */
++ MX21_USBXCVR_TXDIF_RXDIF = 0,
++ MX21_USBXCVR_TXDIF_RXSE = 1,
++ MX21_USBXCVR_TXSE_RXDIF = 2,
++ MX21_USBXCVR_TXSE_RXSE = 3,
++};
++
++struct mx21_usbh_platform_data {
++ enum mx21_usbh_xcvr host_xcvr; /* tranceiver mode host 1,2 ports */
++ enum mx21_usbh_xcvr otg_xcvr; /* tranceiver mode otg (as host) port */
++ u16 enable_host1:1,
++ enable_host2:1,
++ enable_otg_host:1, /* enable "OTG" port (as host) */
++ host1_xcverless:1, /* traceiverless host1 port */
++ host1_txenoe:1, /* output enable host1 transmit enable */
++ otg_ext_xcvr:1, /* external tranceiver for OTG port */
++ unused:10;
++};
++
++#endif /* __ASM_ARCH_MX21_USBH */
+--- /dev/null
++++ b/drivers/usb/host/imx21-dbg.c
+@@ -0,0 +1,527 @@
++/*
++ * Copyright (c) 2009 by Martin Fuzzey
++ *
++ * This program is free software; you can redistribute it and/or modify it
++ * under the terms of the GNU General Public License as published by the
++ * Free Software Foundation; either version 2 of the License, or (at your
++ * option) any later version.
++ *
++ * This program is distributed in the hope that it will be useful, but
++ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
++ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
++ * for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software Foundation,
++ * Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
++ */
++
++/* this file is part of imx21-hcd.c */
++
++#ifndef DEBUG
++
++static inline void create_debug_files(struct imx21 *imx21) { }
++static inline void remove_debug_files(struct imx21 *imx21) { }
++static inline void debug_urb_submitted(struct imx21 *imx21, struct urb *urb) {}
++static inline void debug_urb_completed(struct imx21 *imx21, struct urb *urb,
++ int status) {}
++static inline void debug_urb_unlinked(struct imx21 *imx21, struct urb *urb) {}
++static inline void debug_urb_queued_for_etd(struct imx21 *imx21,
++ struct urb *urb) {}
++static inline void debug_urb_queued_for_dmem(struct imx21 *imx21,
++ struct urb *urb) {}
++static inline void debug_etd_allocated(struct imx21 *imx21) {}
++static inline void debug_etd_freed(struct imx21 *imx21) {}
++static inline void debug_dmem_allocated(struct imx21 *imx21, int size) {}
++static inline void debug_dmem_freed(struct imx21 *imx21, int size) {}
++static inline void debug_isoc_submitted(struct imx21 *imx21,
++ int frame, struct td *td) {}
++static inline void debug_isoc_completed(struct imx21 *imx21,
++ int frame, struct td *td, int cc, int len) {}
++
++#else
++
++#include <linux/debugfs.h>
++#include <linux/seq_file.h>
++
++static const char *dir_labels[] = {
++ "TD 0",
++ "OUT",
++ "IN",
++ "TD 1"
++};
++
++static const char *speed_labels[] = {
++ "Full",
++ "Low"
++};
++
++static const char *format_labels[] = {
++ "Control",
++ "ISO",
++ "Bulk",
++ "Interrupt"
++};
++
++static inline struct debug_stats *stats_for_urb(struct imx21 *imx21,
++ struct urb *urb)
++{
++ return usb_pipeisoc(urb->pipe) ?
++ &imx21->isoc_stats : &imx21->nonisoc_stats;
++}
++
++static void debug_urb_submitted(struct imx21 *imx21, struct urb *urb)
++{
++ stats_for_urb(imx21, urb)->submitted++;
++}
++
++static void debug_urb_completed(struct imx21 *imx21, struct urb *urb, int st)
++{
++ if (st)
++ stats_for_urb(imx21, urb)->completed_failed++;
++ else
++ stats_for_urb(imx21, urb)->completed_ok++;
++}
++
++static void debug_urb_unlinked(struct imx21 *imx21, struct urb *urb)
++{
++ stats_for_urb(imx21, urb)->unlinked++;
++}
++
++static void debug_urb_queued_for_etd(struct imx21 *imx21, struct urb *urb)
++{
++ stats_for_urb(imx21, urb)->queue_etd++;
++}
++
++static void debug_urb_queued_for_dmem(struct imx21 *imx21, struct urb *urb)
++{
++ stats_for_urb(imx21, urb)->queue_dmem++;
++}
++
++static inline void debug_etd_allocated(struct imx21 *imx21)
++{
++ imx21->etd_usage.maximum = max(
++ ++(imx21->etd_usage.value),
++ imx21->etd_usage.maximum);
++}
++
++static inline void debug_etd_freed(struct imx21 *imx21)
++{
++ imx21->etd_usage.value--;
++}
++
++static inline void debug_dmem_allocated(struct imx21 *imx21, int size)
++{
++ imx21->dmem_usage.value += size;
++ imx21->dmem_usage.maximum = max(
++ imx21->dmem_usage.value,
++ imx21->dmem_usage.maximum);
++}
++
++static inline void debug_dmem_freed(struct imx21 *imx21, int size)
++{
++ imx21->dmem_usage.value -= size;
++}
++
++
++static void debug_isoc_submitted(struct imx21 *imx21,
++ int frame, struct td *td)
++{
++ struct debug_isoc_trace *trace = &imx21->isoc_trace[
++ imx21->isoc_trace_index++];
++
++ imx21->isoc_trace_index %= ARRAY_SIZE(imx21->isoc_trace);
++ trace->schedule_frame = td->frame;
++ trace->submit_frame = frame;
++ trace->request_len = td->len;
++ trace->td = td;
++}
++
++static inline void debug_isoc_completed(struct imx21 *imx21,
++ int frame, struct td *td, int cc, int len)
++{
++ struct debug_isoc_trace *trace, *trace_failed;
++ int i;
++ int found = 0;
++
++ trace = imx21->isoc_trace;
++ for (i = 0; i < ARRAY_SIZE(imx21->isoc_trace); i++, trace++) {
++ if (trace->td == td) {
++ trace->done_frame = frame;
++ trace->done_len = len;
++ trace->cc = cc;
++ trace->td = NULL;
++ found = 1;
++ break;
++ }
++ }
++
++ if (found && cc) {
++ trace_failed = &imx21->isoc_trace_failed[
++ imx21->isoc_trace_index_failed++];
++
++ imx21->isoc_trace_index_failed %= ARRAY_SIZE(
++ imx21->isoc_trace_failed);
++ *trace_failed = *trace;
++ }
++}
++
++
++static char *format_ep(struct usb_host_endpoint *ep, char *buf, int bufsize)
++{
++ if (ep)
++ snprintf(buf, bufsize, "ep_%02x (type:%02X kaddr:%p)",
++ ep->desc.bEndpointAddress,
++ usb_endpoint_type(&ep->desc),
++ ep);
++ else
++ snprintf(buf, bufsize, "none");
++ return buf;
++}
++
++static char *format_etd_dword0(u32 value, char *buf, int bufsize)
++{
++ snprintf(buf, bufsize,
++ "addr=%d ep=%d dir=%s speed=%s format=%s halted=%d",
++ value & 0x7F,
++ (value >> DW0_ENDPNT) & 0x0F,
++ dir_labels[(value >> DW0_DIRECT) & 0x03],
++ speed_labels[(value >> DW0_SPEED) & 0x01],
++ format_labels[(value >> DW0_FORMAT) & 0x03],
++ (value >> DW0_HALTED) & 0x01);
++ return buf;
++}
++
++static int debug_status_show(struct seq_file *s, void *v)
++{
++ struct imx21 *imx21 = s->private;
++ int etds_allocated = 0;
++ int etds_sw_busy = 0;
++ int etds_hw_busy = 0;
++ int dmem_blocks = 0;
++ int queued_for_etd = 0;
++ int queued_for_dmem = 0;
++ unsigned int dmem_bytes = 0;
++ int i;
++ struct etd_priv *etd;
++ u32 etd_enable_mask;
++ unsigned long flags;
++ struct imx21_dmem_area *dmem;
++ struct ep_priv *ep_priv;
++
++ spin_lock_irqsave(&imx21->lock, flags);
++
++ etd_enable_mask = readl(imx21->regs + USBH_ETDENSET);
++ for (i = 0, etd = imx21->etd; i < USB_NUM_ETD; i++, etd++) {
++ if (etd->alloc)
++ etds_allocated++;
++ if (etd->urb)
++ etds_sw_busy++;
++ if (etd_enable_mask & (1<<i))
++ etds_hw_busy++;
++ }
++
++ list_for_each_entry(dmem, &imx21->dmem_list, list) {
++ dmem_bytes += dmem->size;
++ dmem_blocks++;
++ }
++
++ list_for_each_entry(ep_priv, &imx21->queue_for_etd, queue)
++ queued_for_etd++;
++
++ list_for_each_entry(etd, &imx21->queue_for_dmem, queue)
++ queued_for_dmem++;
++
++ spin_unlock_irqrestore(&imx21->lock, flags);
++
++ seq_printf(s,
++ "Frame: %d\n"
++ "ETDs allocated: %d/%d (max=%d)\n"
++ "ETDs in use sw: %d\n"
++ "ETDs in use hw: %d\n"
++ "DMEM alocated: %d/%d (max=%d)\n"
++ "DMEM blocks: %d\n"
++ "Queued waiting for ETD: %d\n"
++ "Queued waiting for DMEM: %d\n",
++ readl(imx21->regs + USBH_FRMNUB) & 0xFFFF,
++ etds_allocated, USB_NUM_ETD, imx21->etd_usage.maximum,
++ etds_sw_busy,
++ etds_hw_busy,
++ dmem_bytes, DMEM_SIZE, imx21->dmem_usage.maximum,
++ dmem_blocks,
++ queued_for_etd,
++ queued_for_dmem);
++
++ return 0;
++}
++
++static int debug_dmem_show(struct seq_file *s, void *v)
++{
++ struct imx21 *imx21 = s->private;
++ struct imx21_dmem_area *dmem;
++ unsigned long flags;
++ char ep_text[40];
++
++ spin_lock_irqsave(&imx21->lock, flags);
++
++ list_for_each_entry(dmem, &imx21->dmem_list, list)
++ seq_printf(s,
++ "%04X: size=0x%X "
++ "ep=%s\n",
++ dmem->offset, dmem->size,
++ format_ep(dmem->ep, ep_text, sizeof(ep_text)));
++
++ spin_unlock_irqrestore(&imx21->lock, flags);
++
++ return 0;
++}
++
++static int debug_etd_show(struct seq_file *s, void *v)
++{
++ struct imx21 *imx21 = s->private;
++ struct etd_priv *etd;
++ char buf[60];
++ u32 dword;
++ int i, j;
++ unsigned long flags;
++
++ spin_lock_irqsave(&imx21->lock, flags);
++
++ for (i = 0, etd = imx21->etd; i < USB_NUM_ETD; i++, etd++) {
++ int state = -1;
++ struct urb_priv *urb_priv;
++ if (etd->urb) {
++ urb_priv = etd->urb->hcpriv;
++ if (urb_priv)
++ state = urb_priv->state;
++ }
++
++ seq_printf(s,
++ "etd_num: %d\n"
++ "ep: %s\n"
++ "alloc: %d\n"
++ "len: %d\n"
++ "busy sw: %d\n"
++ "busy hw: %d\n"
++ "urb state: %d\n"
++ "current urb: %p\n",
++
++ i,
++ format_ep(etd->ep, buf, sizeof(buf)),
++ etd->alloc,
++ etd->len,
++ etd->urb != NULL,
++ (readl(imx21->regs + USBH_ETDENSET) & (1 << i)) > 0,
++ state,
++ etd->urb);
++
++ for (j = 0; j < 4; j++) {
++ dword = etd_readl(imx21, i, j);
++ switch (j) {
++ case 0:
++ format_etd_dword0(dword, buf, sizeof(buf));
++ break;
++ case 2:
++ snprintf(buf, sizeof(buf),
++ "cc=0X%02X", dword >> DW2_COMPCODE);
++ break;
++ default:
++ *buf = 0;
++ break;
++ }
++ seq_printf(s,
++ "dword %d: submitted=%08X cur=%08X [%s]\n",
++ j,
++ etd->submitted_dwords[j],
++ dword,
++ buf);
++ }
++ seq_printf(s, "\n");
++ }
++
++ spin_unlock_irqrestore(&imx21->lock, flags);
++
++ return 0;
++}
++
++static void debug_statistics_show_one(struct seq_file *s,
++ const char *name, struct debug_stats *stats)
++{
++ seq_printf(s, "%s:\n"
++ "submitted URBs: %lu\n"
++ "completed OK: %lu\n"
++ "completed failed: %lu\n"
++ "unlinked: %lu\n"
++ "queued for ETD: %lu\n"
++ "queued for DMEM: %lu\n\n",
++ name,
++ stats->submitted,
++ stats->completed_ok,
++ stats->completed_failed,
++ stats->unlinked,
++ stats->queue_etd,
++ stats->queue_dmem);
++}
++
++static int debug_statistics_show(struct seq_file *s, void *v)
++{
++ struct imx21 *imx21 = s->private;
++ unsigned long flags;
++
++ spin_lock_irqsave(&imx21->lock, flags);
++
++ debug_statistics_show_one(s, "nonisoc", &imx21->nonisoc_stats);
++ debug_statistics_show_one(s, "isoc", &imx21->isoc_stats);
++ seq_printf(s, "unblock kludge triggers: %lu\n", imx21->debug_unblocks);
++ spin_unlock_irqrestore(&imx21->lock, flags);
++
++ return 0;
++}
++
++static void debug_isoc_show_one(struct seq_file *s,
++ const char *name, int index, struct debug_isoc_trace *trace)
++{
++ seq_printf(s, "%s %d:\n"
++ "cc=0X%02X\n"
++ "scheduled frame %d (%d)\n"
++ "submittted frame %d (%d)\n"
++ "completed frame %d (%d)\n"
++ "requested length=%d\n"
++ "completed length=%d\n\n",
++ name, index,
++ trace->cc,
++ trace->schedule_frame, trace->schedule_frame & 0xFFFF,
++ trace->submit_frame, trace->submit_frame & 0xFFFF,
++ trace->done_frame, trace->done_frame & 0xFFFF,
++ trace->request_len,
++ trace->done_len);
++}
++
++static int debug_isoc_show(struct seq_file *s, void *v)
++{
++ struct imx21 *imx21 = s->private;
++ struct debug_isoc_trace *trace;
++ unsigned long flags;
++ int i;
++
++ spin_lock_irqsave(&imx21->lock, flags);
++
++ trace = imx21->isoc_trace_failed;
++ for (i = 0; i < ARRAY_SIZE(imx21->isoc_trace_failed); i++, trace++)
++ debug_isoc_show_one(s, "isoc failed", i, trace);
++
++ trace = imx21->isoc_trace;
++ for (i = 0; i < ARRAY_SIZE(imx21->isoc_trace); i++, trace++)
++ debug_isoc_show_one(s, "isoc", i, trace);
++
++ spin_unlock_irqrestore(&imx21->lock, flags);
++
++ return 0;
++}
++
++static int debug_status_open(struct inode *inode, struct file *file)
++{
++ return single_open(file, debug_status_show, inode->i_private);
++}
++
++static int debug_dmem_open(struct inode *inode, struct file *file)
++{
++ return single_open(file, debug_dmem_show, inode->i_private);
++}
++
++static int debug_etd_open(struct inode *inode, struct file *file)
++{
++ return single_open(file, debug_etd_show, inode->i_private);
++}
++
++static int debug_statistics_open(struct inode *inode, struct file *file)
++{
++ return single_open(file, debug_statistics_show, inode->i_private);
++}
++
++static int debug_isoc_open(struct inode *inode, struct file *file)
++{
++ return single_open(file, debug_isoc_show, inode->i_private);
++}
++
++static const struct file_operations debug_status_fops = {
++ .open = debug_status_open,
++ .read = seq_read,
++ .llseek = seq_lseek,
++ .release = single_release,
++};
++
++static const struct file_operations debug_dmem_fops = {
++ .open = debug_dmem_open,
++ .read = seq_read,
++ .llseek = seq_lseek,
++ .release = single_release,
++};
++
++static const struct file_operations debug_etd_fops = {
++ .open = debug_etd_open,
++ .read = seq_read,
++ .llseek = seq_lseek,
++ .release = single_release,
++};
++
++static const struct file_operations debug_statistics_fops = {
++ .open = debug_statistics_open,
++ .read = seq_read,
++ .llseek = seq_lseek,
++ .release = single_release,
++};
++
++static const struct file_operations debug_isoc_fops = {
++ .open = debug_isoc_open,
++ .read = seq_read,
++ .llseek = seq_lseek,
++ .release = single_release,
++};
++
++static void create_debug_files(struct imx21 *imx21)
++{
++ imx21->debug_root = debugfs_create_dir(dev_name(imx21->dev), NULL);
++ if (!imx21->debug_root)
++ goto failed_create_rootdir;
++
++ if (!debugfs_create_file("status", S_IRUGO,
++ imx21->debug_root, imx21, &debug_status_fops))
++ goto failed_create;
++
++ if (!debugfs_create_file("dmem", S_IRUGO,
++ imx21->debug_root, imx21, &debug_dmem_fops))
++ goto failed_create;
++
++ if (!debugfs_create_file("etd", S_IRUGO,
++ imx21->debug_root, imx21, &debug_etd_fops))
++ goto failed_create;
++
++ if (!debugfs_create_file("statistics", S_IRUGO,
++ imx21->debug_root, imx21, &debug_statistics_fops))
++ goto failed_create;
++
++ if (!debugfs_create_file("isoc", S_IRUGO,
++ imx21->debug_root, imx21, &debug_isoc_fops))
++ goto failed_create;
++
++ return;
++
++failed_create:
++ debugfs_remove_recursive(imx21->debug_root);
++
++failed_create_rootdir:
++ imx21->debug_root = NULL;
++}
++
++
++static void remove_debug_files(struct imx21 *imx21)
++{
++ if (imx21->debug_root) {
++ debugfs_remove_recursive(imx21->debug_root);
++ imx21->debug_root = NULL;
++ }
++}
++
++#endif
++
+--- /dev/null
++++ b/drivers/usb/host/imx21-hcd.c
+@@ -0,0 +1,1789 @@
++/*
++ * USB Host Controller Driver for IMX21
++ *
++ * Copyright (C) 2006 Loping Dog Embedded Systems
++ * Copyright (C) 2009 Martin Fuzzey
++ * Originally written by Jay Monkman <jtm@lopingdog.com>
++ * Ported to 2.6.30, debugged and enhanced by Martin Fuzzey
++ *
++ * This program is free software; you can redistribute it and/or modify it
++ * under the terms of the GNU General Public License as published by the
++ * Free Software Foundation; either version 2 of the License, or (at your
++ * option) any later version.
++ *
++ * This program is distributed in the hope that it will be useful, but
++ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
++ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
++ * for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software Foundation,
++ * Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
++ */
++
++
++ /*
++ * The i.MX21 USB hardware contains
++ * * 32 transfer descriptors (called ETDs)
++ * * 4Kb of Data memory
++ *
++ * The data memory is shared between the host and fuction controlers
++ * (but this driver only supports the host controler)
++ *
++ * So setting up a transfer involves:
++ * * Allocating a ETD
++ * * Fill in ETD with appropriate information
++ * * Allocating data memory (and putting the offset in the ETD)
++ * * Activate the ETD
++ * * Get interrupt when done.
++ *
++ * An ETD is assigned to each active endpoint.
++ *
++ * Low resource (ETD and Data memory) situations are handled differently for
++ * isochronous and non insosynchronous transactions :
++ *
++ * Non ISOC transfers are queued if either ETDs or Data memory are unavailable
++ *
++ * ISOC transfers use 2 ETDs per endpoint to achieve double buffering.
++ * They allocate both ETDs and Data memory during URB submission
++ * (and fail if unavailable).
++ */
++
++#include <linux/clk.h>
++#include <linux/io.h>
++#include <linux/kernel.h>
++#include <linux/list.h>
++#include <linux/platform_device.h>
++#include <linux/usb.h>
++
++#include "../core/hcd.h"
++#include "imx21-hcd.h"
++
++#ifdef DEBUG
++#define DEBUG_LOG_FRAME(imx21, etd, event) \
++ (etd)->event##_frame = readl((imx21)->regs + USBH_FRMNUB)
++#else
++#define DEBUG_LOG_FRAME(imx21, etd, event) do { } while (0)
++#endif
++
++static const char hcd_name[] = "imx21-hcd";
++
++static inline struct imx21 *hcd_to_imx21(struct usb_hcd *hcd)
++{
++ return (struct imx21 *)hcd->hcd_priv;
++}
++
++
++/* =========================================== */
++/* Hardware access helpers */
++/* =========================================== */
++
++static inline void set_register_bits(struct imx21 *imx21, u32 offset, u32 mask)
++{
++ void __iomem *reg = imx21->regs + offset;
++ writel(readl(reg) | mask, reg);
++}
++
++static inline void clear_register_bits(struct imx21 *imx21,
++ u32 offset, u32 mask)
++{
++ void __iomem *reg = imx21->regs + offset;
++ writel(readl(reg) & ~mask, reg);
++}
++
++static inline void clear_toggle_bit(struct imx21 *imx21, u32 offset, u32 mask)
++{
++ void __iomem *reg = imx21->regs + offset;
++
++ if (readl(reg) & mask)
++ writel(mask, reg);
++}
++
++static inline void set_toggle_bit(struct imx21 *imx21, u32 offset, u32 mask)
++{
++ void __iomem *reg = imx21->regs + offset;
++
++ if (!(readl(reg) & mask))
++ writel(mask, reg);
++}
++
++static void etd_writel(struct imx21 *imx21, int etd_num, int dword, u32 value)
++{
++ writel(value, imx21->regs + USB_ETD_DWORD(etd_num, dword));
++}
++
++static u32 etd_readl(struct imx21 *imx21, int etd_num, int dword)
++{
++ return readl(imx21->regs + USB_ETD_DWORD(etd_num, dword));
++}
++
++static inline int wrap_frame(int counter)
++{
++ return counter & 0xFFFF;
++}
++
++static inline int frame_after(int frame, int after)
++{
++ /* handle wrapping like jiffies time_afer */
++ return (s16)((s16)after - (s16)frame) < 0;
++}
++
++static int imx21_hc_get_frame(struct usb_hcd *hcd)
++{
++ struct imx21 *imx21 = hcd_to_imx21(hcd);
++
++ return wrap_frame(readl(imx21->regs + USBH_FRMNUB));
++}
++
++
++#include "imx21-dbg.c"
++
++/* =========================================== */
++/* ETD management */
++/* =========================================== */
++
++static int alloc_etd(struct imx21 *imx21)
++{
++ int i;
++ struct etd_priv *etd = imx21->etd;
++
++ for (i = 0; i < USB_NUM_ETD; i++, etd++) {
++ if (etd->alloc == 0) {
++ memset(etd, 0, sizeof(imx21->etd[0]));
++ etd->alloc = 1;
++ debug_etd_allocated(imx21);
++ return i;
++ }
++ }
++ return -1;
++}
++
++static void disactivate_etd(struct imx21 *imx21, int num)
++{
++ int etd_mask = (1 << num);
++ struct etd_priv *etd = &imx21->etd[num];
++
++ writel(etd_mask, imx21->regs + USBH_ETDENCLR);
++ clear_register_bits(imx21, USBH_ETDDONEEN, etd_mask);
++ writel(etd_mask, imx21->regs + USB_ETDDMACHANLCLR);
++ clear_toggle_bit(imx21, USBH_ETDDONESTAT, etd_mask);
++
++ etd->active_count = 0;
++
++ DEBUG_LOG_FRAME(imx21, etd, disactivated);
++}
++
++static void reset_etd(struct imx21 *imx21, int num)
++{
++ struct etd_priv *etd = imx21->etd + num;
++ int i;
++
++ disactivate_etd(imx21, num);
++
++ for (i = 0; i < 4; i++)
++ etd_writel(imx21, num, i, 0);
++ etd->urb = NULL;
++ etd->ep = NULL;
++ etd->td = NULL;;
++}
++
++static void free_etd(struct imx21 *imx21, int num)
++{
++ if (num < 0)
++ return;
++
++ if (num >= USB_NUM_ETD) {
++ dev_err(imx21->dev, "BAD etd=%d!\n", num);
++ return;
++ }
++ if (imx21->etd[num].alloc == 0) {
++ dev_err(imx21->dev, "ETD %d already free!\n", num);
++ return;
++ }
++
++ debug_etd_freed(imx21);
++ reset_etd(imx21, num);
++ memset(&imx21->etd[num], 0, sizeof(imx21->etd[0]));
++}
++
++
++static void setup_etd_dword0(struct imx21 *imx21,
++ int etd_num, struct urb *urb, u8 dir, u16 maxpacket)
++{
++ etd_writel(imx21, etd_num, 0,
++ ((u32) usb_pipedevice(urb->pipe)) << DW0_ADDRESS |
++ ((u32) usb_pipeendpoint(urb->pipe) << DW0_ENDPNT) |
++ ((u32) dir << DW0_DIRECT) |
++ ((u32) ((urb->dev->speed == USB_SPEED_LOW) ?
++ 1 : 0) << DW0_SPEED) |
++ ((u32) fmt_urb_to_etd[usb_pipetype(urb->pipe)] << DW0_FORMAT) |
++ ((u32) maxpacket << DW0_MAXPKTSIZ));
++}
++
++static void activate_etd(struct imx21 *imx21,
++ int etd_num, dma_addr_t dma, u8 dir)
++{
++ u32 etd_mask = 1 << etd_num;
++ struct etd_priv *etd = &imx21->etd[etd_num];
++
++ clear_toggle_bit(imx21, USBH_ETDDONESTAT, etd_mask);
++ set_register_bits(imx21, USBH_ETDDONEEN, etd_mask);
++ clear_toggle_bit(imx21, USBH_XFILLSTAT, etd_mask);
++ clear_toggle_bit(imx21, USBH_YFILLSTAT, etd_mask);
++
++ if (dma) {
++ set_register_bits(imx21, USB_ETDDMACHANLCLR, etd_mask);
++ clear_toggle_bit(imx21, USBH_XBUFSTAT, etd_mask);
++ clear_toggle_bit(imx21, USBH_YBUFSTAT, etd_mask);
++ writel(dma, imx21->regs + USB_ETDSMSA(etd_num));
++ set_register_bits(imx21, USB_ETDDMAEN, etd_mask);
++ } else {
++ if (dir != TD_DIR_IN) {
++ /* need to set for ZLP */
++ set_toggle_bit(imx21, USBH_XFILLSTAT, etd_mask);
++ set_toggle_bit(imx21, USBH_YFILLSTAT, etd_mask);
++ }
++ }
++
++ DEBUG_LOG_FRAME(imx21, etd, activated);
++
++#ifdef DEBUG
++ if (!etd->active_count) {
++ int i;
++ etd->activated_frame = readl(imx21->regs + USBH_FRMNUB);
++ etd->disactivated_frame = -1;
++ etd->last_int_frame = -1;
++ etd->last_req_frame = -1;
++
++ for (i = 0; i < 4; i++)
++ etd->submitted_dwords[i] = etd_readl(imx21, etd_num, i);
++ }
++#endif
++
++ etd->active_count = 1;
++ writel(etd_mask, imx21->regs + USBH_ETDENSET);
++}
++
++/* =========================================== */
++/* Data memory management */
++/* =========================================== */
++
++static int alloc_dmem(struct imx21 *imx21, unsigned int size,
++ struct usb_host_endpoint *ep)
++{
++ unsigned int offset = 0;
++ struct imx21_dmem_area *area;
++ struct imx21_dmem_area *tmp;
++
++ size += (~size + 1) & 0x3; /* Round to 4 byte multiple */
++
++ if (size > DMEM_SIZE) {
++ dev_err(imx21->dev, "size=%d > DMEM_SIZE(%d)\n",
++ size, DMEM_SIZE);
++ return -EINVAL;
++ }
++
++ list_for_each_entry(tmp, &imx21->dmem_list, list) {
++ if ((size + offset) < offset)
++ goto fail;
++ if ((size + offset) <= tmp->offset)
++ break;
++ offset = tmp->size + tmp->offset;
++ if ((offset + size) > DMEM_SIZE)
++ goto fail;
++ }
++
++ area = kmalloc(sizeof(struct imx21_dmem_area), GFP_ATOMIC);
++ if (area == NULL)
++ return -ENOMEM;
++
++ area->ep = ep;
++ area->offset = offset;
++ area->size = size;
++ list_add_tail(&area->list, &tmp->list);
++ debug_dmem_allocated(imx21, size);
++ return offset;
++
++fail:
++ return -ENOMEM;
++}
++
++/* Memory now available for a queued ETD - activate it */
++static void activate_queued_etd(struct imx21 *imx21,
++ struct etd_priv *etd, u32 dmem_offset)
++{
++ struct urb_priv *urb_priv = etd->urb->hcpriv;
++ int etd_num = etd - &imx21->etd[0];
++ u32 maxpacket = etd_readl(imx21, etd_num, 1) >> DW1_YBUFSRTAD;
++ u8 dir = (etd_readl(imx21, etd_num, 2) >> DW2_DIRPID) & 0x03;
++
++ dev_dbg(imx21->dev, "activating queued ETD %d now DMEM available\n",
++ etd_num);
++ etd_writel(imx21, etd_num, 1,
++ ((dmem_offset + maxpacket) << DW1_YBUFSRTAD) | dmem_offset);
++
++ urb_priv->active = 1;
++ activate_etd(imx21, etd_num, etd->dma_handle, dir);
++}
++
++static void free_dmem(struct imx21 *imx21, int offset)
++{
++ struct imx21_dmem_area *area;
++ struct etd_priv *etd, *tmp;
++ int found = 0;
++
++ list_for_each_entry(area, &imx21->dmem_list, list) {
++ if (area->offset == offset) {
++ debug_dmem_freed(imx21, area->size);
++ list_del(&area->list);
++ kfree(area);
++ found = 1;
++ break;
++ }
++ }
++
++ if (!found) {
++ dev_err(imx21->dev,
++ "Trying to free unallocated DMEM %d\n", offset);
++ return;
++ }
++
++ /* Try again to allocate memory for anything we've queued */
++ list_for_each_entry_safe(etd, tmp, &imx21->queue_for_dmem, queue) {
++ offset = alloc_dmem(imx21, etd->dmem_size, etd->ep);
++ if (offset >= 0) {
++ list_del(&etd->queue);
++ activate_queued_etd(imx21, etd, (u32)offset);
++ }
++ }
++}
++
++static void free_epdmem(struct imx21 *imx21, struct usb_host_endpoint *ep)
++{
++ struct imx21_dmem_area *area, *tmp;
++
++ list_for_each_entry_safe(area, tmp, &imx21->dmem_list, list) {
++ if (area->ep == ep) {
++ dev_err(imx21->dev,
++ "Active DMEM %d for disabled ep=%p\n",
++ area->offset, ep);
++ list_del(&area->list);
++ kfree(area);
++ }
++ }
++}
++
++
++/* =========================================== */
++/* End handling */
++/* =========================================== */
++static void schedule_nonisoc_etd(struct imx21 *imx21, struct urb *urb);
++
++/* Endpoint now idle - release it's ETD(s) or asssign to queued request */
++static void ep_idle(struct imx21 *imx21, struct ep_priv *ep_priv)
++{
++ int etd_num;
++ int i;
++
++ for (i = 0; i < NUM_ISO_ETDS; i++) {
++ etd_num = ep_priv->etd[i];
++ if (etd_num < 0)
++ continue;
++
++ ep_priv->etd[i] = -1;
++ if (list_empty(&imx21->queue_for_etd)) {
++ free_etd(imx21, etd_num);
++ continue;
++ }
++
++ dev_dbg(imx21->dev,
++ "assigning idle etd %d for queued request\n", etd_num);
++ ep_priv = list_first_entry(&imx21->queue_for_etd,
++ struct ep_priv, queue);
++ list_del(&ep_priv->queue);
++ reset_etd(imx21, etd_num);
++ ep_priv->waiting_etd = 0;
++ ep_priv->etd[i] = etd_num;
++
++ if (list_empty(&ep_priv->ep->urb_list)) {
++ dev_err(imx21->dev, "No urb for queued ep!\n");
++ continue;
++ }
++ schedule_nonisoc_etd(imx21, list_first_entry(
++ &ep_priv->ep->urb_list, struct urb, urb_list));
++ }
++}
++
++static void urb_done(struct usb_hcd *hcd, struct urb *urb, int status)
++__releases(imx21->lock)
++__acquires(imx21->lock)
++{
++ struct imx21 *imx21 = hcd_to_imx21(hcd);
++ struct ep_priv *ep_priv = urb->ep->hcpriv;
++ struct urb_priv *urb_priv = urb->hcpriv;
++
++ debug_urb_completed(imx21, urb, status);
++ dev_vdbg(imx21->dev, "urb %p done %d\n", urb, status);
++
++ kfree(urb_priv->isoc_td);
++ kfree(urb->hcpriv);
++ urb->hcpriv = NULL;
++ usb_hcd_unlink_urb_from_ep(hcd, urb);
++ spin_unlock(&imx21->lock);
++ usb_hcd_giveback_urb(hcd, urb, status);
++ spin_lock(&imx21->lock);
++ if (list_empty(&ep_priv->ep->urb_list))
++ ep_idle(imx21, ep_priv);
++}
++
++/* =========================================== */
++/* ISOC Handling ... */
++/* =========================================== */
++
++static void schedule_isoc_etds(struct usb_hcd *hcd,
++ struct usb_host_endpoint *ep)
++{
++ struct imx21 *imx21 = hcd_to_imx21(hcd);
++ struct ep_priv *ep_priv = ep->hcpriv;
++ struct etd_priv *etd;
++ struct urb_priv *urb_priv;
++ struct td *td;
++ int etd_num;
++ int i;
++ int cur_frame;
++ u8 dir;
++
++ for (i = 0; i < NUM_ISO_ETDS; i++) {
++too_late:
++ if (list_empty(&ep_priv->td_list))
++ break;
++
++ etd_num = ep_priv->etd[i];
++ if (etd_num < 0)
++ break;
++
++ etd = &imx21->etd[etd_num];
++ if (etd->urb)
++ continue;
++
++ td = list_entry(ep_priv->td_list.next, struct td, list);
++ list_del(&td->list);
++ urb_priv = td->urb->hcpriv;
++
++ cur_frame = imx21_hc_get_frame(hcd);
++ if (frame_after(cur_frame, td->frame)) {
++ dev_dbg(imx21->dev, "isoc too late frame %d > %d\n",
++ cur_frame, td->frame);
++ urb_priv->isoc_status = -EXDEV;
++ td->urb->iso_frame_desc[
++ td->isoc_index].actual_length = 0;
++ td->urb->iso_frame_desc[td->isoc_index].status = -EXDEV;
++ if (--urb_priv->isoc_remaining == 0)
++ urb_done(hcd, td->urb, urb_priv->isoc_status);
++ goto too_late;
++ }
++
++ urb_priv->active = 1;
++ etd->td = td;
++ etd->ep = td->ep;
++ etd->urb = td->urb;
++ etd->len = td->len;
++
++ debug_isoc_submitted(imx21, cur_frame, td);
++
++ dir = usb_pipeout(td->urb->pipe) ? TD_DIR_OUT : TD_DIR_IN;
++ setup_etd_dword0(imx21, etd_num, td->urb, dir, etd->dmem_size);
++ etd_writel(imx21, etd_num, 1, etd->dmem_offset);
++ etd_writel(imx21, etd_num, 2,
++ (TD_NOTACCESSED << DW2_COMPCODE) |
++ ((td->frame & 0xFFFF) << DW2_STARTFRM));
++ etd_writel(imx21, etd_num, 3,
++ (TD_NOTACCESSED << DW3_COMPCODE0) |
++ (td->len << DW3_PKTLEN0));
++
++ activate_etd(imx21, etd_num, td->data, dir);
++ }
++}
++
++static void isoc_etd_done(struct usb_hcd *hcd, struct urb *urb, int etd_num)
++{
++ struct imx21 *imx21 = hcd_to_imx21(hcd);
++ int etd_mask = 1 << etd_num;
++ struct urb_priv *urb_priv = urb->hcpriv;
++ struct etd_priv *etd = imx21->etd + etd_num;
++ struct td *td = etd->td;
++ struct usb_host_endpoint *ep = etd->ep;
++ int isoc_index = td->isoc_index;
++ unsigned int pipe = urb->pipe;
++ int dir_in = usb_pipein(pipe);
++ int cc;
++ int bytes_xfrd;
++
++ disactivate_etd(imx21, etd_num);
++
++ cc = (etd_readl(imx21, etd_num, 3) >> DW3_COMPCODE0) & 0xf;
++ bytes_xfrd = etd_readl(imx21, etd_num, 3) & 0x3ff;
++
++ /* Input doesn't always fill the buffer, don't generate an error
++ * when this happens.
++ */
++ if (dir_in && (cc == TD_DATAUNDERRUN))
++ cc = TD_CC_NOERROR;
++
++ if (cc == TD_NOTACCESSED)
++ bytes_xfrd = 0;
++
++ debug_isoc_completed(imx21,
++ imx21_hc_get_frame(hcd), td, cc, bytes_xfrd);
++ if (cc) {
++ urb_priv->isoc_status = -EXDEV;
++ dev_dbg(imx21->dev,
++ "bad iso cc=0x%X frame=%d sched frame=%d "
++ "cnt=%d len=%d urb=%p etd=%d index=%d\n",
++ cc, imx21_hc_get_frame(hcd), td->frame,
++ bytes_xfrd, td->len, urb, etd_num, isoc_index);
++ }
++
++ if (dir_in)
++ clear_toggle_bit(imx21, USBH_XFILLSTAT, etd_mask);
++
++ urb->actual_length += bytes_xfrd;
++ urb->iso_frame_desc[isoc_index].actual_length = bytes_xfrd;
++ urb->iso_frame_desc[isoc_index].status = cc_to_error[cc];
++
++ etd->td = NULL;
++ etd->urb = NULL;
++ etd->ep = NULL;
++
++ if (--urb_priv->isoc_remaining == 0)
++ urb_done(hcd, urb, urb_priv->isoc_status);
++
++ schedule_isoc_etds(hcd, ep);
++}
++
++static struct ep_priv *alloc_isoc_ep(
++ struct imx21 *imx21, struct usb_host_endpoint *ep)
++{
++ struct ep_priv *ep_priv;
++ int i;
++
++ ep_priv = kzalloc(sizeof(struct ep_priv), GFP_ATOMIC);
++ if (ep_priv == NULL)
++ return NULL;
++
++ /* Allocate the ETDs */
++ for (i = 0; i < NUM_ISO_ETDS; i++) {
++ ep_priv->etd[i] = alloc_etd(imx21);
++ if (ep_priv->etd[i] < 0) {
++ int j;
++ dev_err(imx21->dev, "isoc: Couldn't allocate etd\n");
++ for (j = 0; j < i; j++)
++ free_etd(imx21, ep_priv->etd[j]);
++ goto alloc_etd_failed;
++ }
++ imx21->etd[ep_priv->etd[i]].ep = ep;
++ }
++
++ INIT_LIST_HEAD(&ep_priv->td_list);
++ ep_priv->ep = ep;
++ ep->hcpriv = ep_priv;
++ return ep_priv;
++
++alloc_etd_failed:
++ kfree(ep_priv);
++ return NULL;
++}
++
++static int imx21_hc_urb_enqueue_isoc(struct usb_hcd *hcd,
++ struct usb_host_endpoint *ep,
++ struct urb *urb, gfp_t mem_flags)
++{
++ struct imx21 *imx21 = hcd_to_imx21(hcd);
++ struct urb_priv *urb_priv;
++ unsigned long flags;
++ struct ep_priv *ep_priv;
++ struct td *td = NULL;
++ int i;
++ int ret;
++ int cur_frame;
++ u16 maxpacket;
++
++ urb_priv = kzalloc(sizeof(struct urb_priv), mem_flags);
++ if (urb_priv == NULL)
++ return -ENOMEM;
++
++ urb_priv->isoc_td = kzalloc(
++ sizeof(struct td) * urb->number_of_packets, mem_flags);
++ if (urb_priv->isoc_td == NULL) {
++ ret = -ENOMEM;
++ goto alloc_td_failed;
++ }
++
++ spin_lock_irqsave(&imx21->lock, flags);
++
++ if (ep->hcpriv == NULL) {
++ ep_priv = alloc_isoc_ep(imx21, ep);
++ if (ep_priv == NULL) {
++ ret = -ENOMEM;
++ goto alloc_ep_failed;
++ }
++ } else {
++ ep_priv = ep->hcpriv;
++ }
++
++ ret = usb_hcd_link_urb_to_ep(hcd, urb);
++ if (ret)
++ goto link_failed;
++
++ urb->status = -EINPROGRESS;
++ urb->actual_length = 0;
++ urb->error_count = 0;
++ urb->hcpriv = urb_priv;
++ urb_priv->ep = ep;
++
++ /* allocate data memory for largest packets if not already done */
++ maxpacket = usb_maxpacket(urb->dev, urb->pipe, usb_pipeout(urb->pipe));
++ for (i = 0; i < NUM_ISO_ETDS; i++) {
++ struct etd_priv *etd = &imx21->etd[ep_priv->etd[i]];
++
++ if (etd->dmem_size > 0 && etd->dmem_size < maxpacket) {
++ /* not sure if this can really occur.... */
++ dev_err(imx21->dev, "increasing isoc buffer %d->%d\n",
++ etd->dmem_size, maxpacket);
++ ret = -EMSGSIZE;
++ goto alloc_dmem_failed;
++ }
++
++ if (etd->dmem_size == 0) {
++ etd->dmem_offset = alloc_dmem(imx21, maxpacket, ep);
++ if (etd->dmem_offset < 0) {
++ dev_dbg(imx21->dev, "failed alloc isoc dmem\n");
++ ret = -EAGAIN;
++ goto alloc_dmem_failed;
++ }
++ etd->dmem_size = maxpacket;
++ }
++ }
++
++ /* calculate frame */
++ cur_frame = imx21_hc_get_frame(hcd);
++ if (urb->transfer_flags & URB_ISO_ASAP) {
++ if (list_empty(&ep_priv->td_list))
++ urb->start_frame = cur_frame + 5;
++ else
++ urb->start_frame = list_entry(
++ ep_priv->td_list.prev,
++ struct td, list)->frame + urb->interval;
++ }
++ urb->start_frame = wrap_frame(urb->start_frame);
++ if (frame_after(cur_frame, urb->start_frame)) {
++ dev_dbg(imx21->dev,
++ "enqueue: adjusting iso start %d (cur=%d) asap=%d\n",
++ urb->start_frame, cur_frame,
++ (urb->transfer_flags & URB_ISO_ASAP) != 0);
++ urb->start_frame = wrap_frame(cur_frame + 1);
++ }
++
++ /* set up transfers */
++ td = urb_priv->isoc_td;
++ for (i = 0; i < urb->number_of_packets; i++, td++) {
++ td->ep = ep;
++ td->urb = urb;
++ td->len = urb->iso_frame_desc[i].length;
++ td->isoc_index = i;
++ td->frame = wrap_frame(urb->start_frame + urb->interval * i);
++ td->data = urb->transfer_dma + urb->iso_frame_desc[i].offset;
++ list_add_tail(&td->list, &ep_priv->td_list);
++ }
++
++ urb_priv->isoc_remaining = urb->number_of_packets;
++ dev_vdbg(imx21->dev, "setup %d packets for iso frame %d->%d\n",
++ urb->number_of_packets, urb->start_frame, td->frame);
++
++ debug_urb_submitted(imx21, urb);
++ schedule_isoc_etds(hcd, ep);
++
++ spin_unlock_irqrestore(&imx21->lock, flags);
++ return 0;
++
++alloc_dmem_failed:
++ usb_hcd_unlink_urb_from_ep(hcd, urb);
++
++link_failed:
++alloc_ep_failed:
++ spin_unlock_irqrestore(&imx21->lock, flags);
++ kfree(urb_priv->isoc_td);
++
++alloc_td_failed:
++ kfree(urb_priv);
++ return ret;
++}
++
++static void dequeue_isoc_urb(struct imx21 *imx21,
++ struct urb *urb, struct ep_priv *ep_priv)
++{
++ struct urb_priv *urb_priv = urb->hcpriv;
++ struct td *td, *tmp;
++ int i;
++
++ if (urb_priv->active) {
++ for (i = 0; i < NUM_ISO_ETDS; i++) {
++ int etd_num = ep_priv->etd[i];
++ if (etd_num != -1 && imx21->etd[etd_num].urb == urb) {
++ struct etd_priv *etd = imx21->etd + etd_num;
++
++ reset_etd(imx21, etd_num);
++ if (etd->dmem_size)
++ free_dmem(imx21, etd->dmem_offset);
++ etd->dmem_size = 0;
++ }
++ }
++ }
++
++ list_for_each_entry_safe(td, tmp, &ep_priv->td_list, list) {
++ if (td->urb == urb) {
++ dev_vdbg(imx21->dev, "removing td %p\n", td);
++ list_del(&td->list);
++ }
++ }
++}
++
++/* =========================================== */
++/* NON ISOC Handling ... */
++/* =========================================== */
++
++static void schedule_nonisoc_etd(struct imx21 *imx21, struct urb *urb)
++{
++ unsigned int pipe = urb->pipe;
++ struct urb_priv *urb_priv = urb->hcpriv;
++ struct ep_priv *ep_priv = urb_priv->ep->hcpriv;
++ int state = urb_priv->state;
++ int etd_num = ep_priv->etd[0];
++ struct etd_priv *etd;
++ int dmem_offset;
++ u32 count;
++ u16 etd_buf_size;
++ u16 maxpacket;
++ u8 dir;
++ u8 bufround;
++ u8 datatoggle;
++ u8 interval = 0;
++ u8 relpolpos = 0;
++
++ if (etd_num < 0) {
++ dev_err(imx21->dev, "No valid ETD\n");
++ return;
++ }
++ if (readl(imx21->regs + USBH_ETDENSET) & (1 << etd_num))
++ dev_err(imx21->dev, "submitting to active ETD %d\n", etd_num);
++
++ etd = &imx21->etd[etd_num];
++ maxpacket = usb_maxpacket(urb->dev, pipe, usb_pipeout(pipe));
++ if (!maxpacket)
++ maxpacket = 8;
++
++ if (usb_pipecontrol(pipe) && (state != US_CTRL_DATA)) {
++ if (state == US_CTRL_SETUP) {
++ dir = TD_DIR_SETUP;
++ etd->dma_handle = urb->setup_dma;
++ bufround = 0;
++ count = 8;
++ datatoggle = TD_TOGGLE_DATA0;
++ } else { /* US_CTRL_ACK */
++ dir = usb_pipeout(pipe) ? TD_DIR_IN : TD_DIR_OUT;
++ etd->dma_handle = urb->transfer_dma;
++ bufround = 0;
++ count = 0;
++ datatoggle = TD_TOGGLE_DATA1;
++ }
++ } else {
++ dir = usb_pipeout(pipe) ? TD_DIR_OUT : TD_DIR_IN;
++ bufround = (dir == TD_DIR_IN) ? 1 : 0;
++ etd->dma_handle = urb->transfer_dma;
++ if (usb_pipebulk(pipe) && (state == US_BULK0))
++ count = 0;
++ else
++ count = urb->transfer_buffer_length;
++
++ if (usb_pipecontrol(pipe)) {
++ datatoggle = TD_TOGGLE_DATA1;
++ } else {
++ if (usb_gettoggle(
++ urb->dev,
++ usb_pipeendpoint(urb->pipe),
++ usb_pipeout(urb->pipe)))
++ datatoggle = TD_TOGGLE_DATA1;
++ else
++ datatoggle = TD_TOGGLE_DATA0;
++ }
++ }
++
++ etd->urb = urb;
++ etd->ep = urb_priv->ep;
++ etd->len = count;
++
++ if (usb_pipeint(pipe)) {
++ interval = urb->interval;
++ relpolpos = (readl(imx21->regs + USBH_FRMNUB) + 1) & 0xff;
++ }
++
++ /* Write ETD to device memory */
++ setup_etd_dword0(imx21, etd_num, urb, dir, maxpacket);
++
++ etd_writel(imx21, etd_num, 2,
++ (u32) interval << DW2_POLINTERV |
++ ((u32) relpolpos << DW2_RELPOLPOS) |
++ ((u32) dir << DW2_DIRPID) |
++ ((u32) bufround << DW2_BUFROUND) |
++ ((u32) datatoggle << DW2_DATATOG) |
++ ((u32) TD_NOTACCESSED << DW2_COMPCODE));
++
++ /* DMA will always transfer buffer size even if TOBYCNT in DWORD3
++ is smaller. Make sure we don't overrun the buffer!
++ */
++ if (count && count < maxpacket)
++ etd_buf_size = count;
++ else
++ etd_buf_size = maxpacket;
++
++ etd_writel(imx21, etd_num, 3,
++ ((u32) (etd_buf_size - 1) << DW3_BUFSIZE) | (u32) count);
++
++ if (!count)
++ etd->dma_handle = 0;
++
++ /* allocate x and y buffer space at once */
++ etd->dmem_size = (count > maxpacket) ? maxpacket * 2 : maxpacket;
++ dmem_offset = alloc_dmem(imx21, etd->dmem_size, urb_priv->ep);
++ if (dmem_offset < 0) {
++ /* Setup everything we can in HW and update when we get DMEM */
++ etd_writel(imx21, etd_num, 1, (u32)maxpacket << 16);
++
++ dev_dbg(imx21->dev, "Queuing etd %d for DMEM\n", etd_num);
++ debug_urb_queued_for_dmem(imx21, urb);
++ list_add_tail(&etd->queue, &imx21->queue_for_dmem);
++ return;
++ }
++
++ etd_writel(imx21, etd_num, 1,
++ (((u32) dmem_offset + (u32) maxpacket) << DW1_YBUFSRTAD) |
++ (u32) dmem_offset);
++
++ urb_priv->active = 1;
++
++ /* enable the ETD to kick off transfer */
++ dev_vdbg(imx21->dev, "Activating etd %d for %d bytes %s\n",
++ etd_num, count, dir != TD_DIR_IN ? "out" : "in");
++ activate_etd(imx21, etd_num, etd->dma_handle, dir);
++
++}
++
++static void nonisoc_etd_done(struct usb_hcd *hcd, struct urb *urb, int etd_num)
++{
++ struct imx21 *imx21 = hcd_to_imx21(hcd);
++ struct etd_priv *etd = &imx21->etd[etd_num];
++ u32 etd_mask = 1 << etd_num;
++ struct urb_priv *urb_priv = urb->hcpriv;
++ int dir;
++ u16 xbufaddr;
++ int cc;
++ u32 bytes_xfrd;
++ int etd_done;
++
++ disactivate_etd(imx21, etd_num);
++
++ dir = (etd_readl(imx21, etd_num, 0) >> DW0_DIRECT) & 0x3;
++ xbufaddr = etd_readl(imx21, etd_num, 1) & 0xffff;
++ cc = (etd_readl(imx21, etd_num, 2) >> DW2_COMPCODE) & 0xf;
++ bytes_xfrd = etd->len - (etd_readl(imx21, etd_num, 3) & 0x1fffff);
++
++ /* save toggle carry */
++ usb_settoggle(urb->dev, usb_pipeendpoint(urb->pipe),
++ usb_pipeout(urb->pipe),
++ (etd_readl(imx21, etd_num, 0) >> DW0_TOGCRY) & 0x1);
++
++ if (dir == TD_DIR_IN) {
++ clear_toggle_bit(imx21, USBH_XFILLSTAT, etd_mask);
++ clear_toggle_bit(imx21, USBH_YFILLSTAT, etd_mask);
++ }
++ free_dmem(imx21, xbufaddr);
++
++ urb->error_count = 0;
++ if (!(urb->transfer_flags & URB_SHORT_NOT_OK)
++ && (cc == TD_DATAUNDERRUN))
++ cc = TD_CC_NOERROR;
++
++ if (cc != 0)
++ dev_vdbg(imx21->dev, "cc is 0x%x\n", cc);
++
++ etd_done = (cc_to_error[cc] != 0); /* stop if error */
++
++ switch (usb_pipetype(urb->pipe)) {
++ case PIPE_CONTROL:
++ switch (urb_priv->state) {
++ case US_CTRL_SETUP:
++ if (urb->transfer_buffer_length > 0)
++ urb_priv->state = US_CTRL_DATA;
++ else
++ urb_priv->state = US_CTRL_ACK;
++ break;
++ case US_CTRL_DATA:
++ urb->actual_length += bytes_xfrd;
++ urb_priv->state = US_CTRL_ACK;
++ break;
++ case US_CTRL_ACK:
++ etd_done = 1;
++ break;
++ default:
++ dev_err(imx21->dev,
++ "Invalid pipe state %d\n", urb_priv->state);
++ etd_done = 1;
++ break;
++ }
++ break;
++
++ case PIPE_BULK:
++ urb->actual_length += bytes_xfrd;
++ if ((urb_priv->state == US_BULK)
++ && (urb->transfer_flags & URB_ZERO_PACKET)
++ && urb->transfer_buffer_length > 0
++ && ((urb->transfer_buffer_length %
++ usb_maxpacket(urb->dev, urb->pipe,
++ usb_pipeout(urb->pipe))) == 0)) {
++ /* need a 0-packet */
++ urb_priv->state = US_BULK0;
++ } else {
++ etd_done = 1;
++ }
++ break;
++
++ case PIPE_INTERRUPT:
++ urb->actual_length += bytes_xfrd;
++ etd_done = 1;
++ break;
++ }
++
++ if (!etd_done) {
++ dev_vdbg(imx21->dev, "next state=%d\n", urb_priv->state);
++ schedule_nonisoc_etd(imx21, urb);
++ } else {
++ struct usb_host_endpoint *ep = urb->ep;
++
++ urb_done(hcd, urb, cc_to_error[cc]);
++ etd->urb = NULL;
++
++ if (!list_empty(&ep->urb_list)) {
++ urb = list_first_entry(&ep->urb_list,
++ struct urb, urb_list);
++ dev_vdbg(imx21->dev, "next URB %p\n", urb);
++ schedule_nonisoc_etd(imx21, urb);
++ }
++ }
++}
++
++static struct ep_priv *alloc_ep(void)
++{
++ int i;
++ struct ep_priv *ep_priv;
++
++ ep_priv = kzalloc(sizeof(struct ep_priv), GFP_ATOMIC);
++ if (!ep_priv)
++ return NULL;
++
++ for (i = 0; i < NUM_ISO_ETDS; ++i)
++ ep_priv->etd[i] = -1;
++
++ return ep_priv;
++}
++
++static int imx21_hc_urb_enqueue(struct usb_hcd *hcd,
++ struct urb *urb, gfp_t mem_flags)
++{
++ struct imx21 *imx21 = hcd_to_imx21(hcd);
++ struct usb_host_endpoint *ep = urb->ep;
++ struct urb_priv *urb_priv;
++ struct ep_priv *ep_priv;
++ struct etd_priv *etd;
++ int ret;
++ unsigned long flags;
++ int new_ep = 0;
++
++ dev_vdbg(imx21->dev,
++ "enqueue urb=%p ep=%p len=%d "
++ "buffer=%p dma=%08X setupBuf=%p setupDma=%08X\n",
++ urb, ep,
++ urb->transfer_buffer_length,
++ urb->transfer_buffer, urb->transfer_dma,
++ urb->setup_packet, urb->setup_dma);
++
++ if (usb_pipeisoc(urb->pipe))
++ return imx21_hc_urb_enqueue_isoc(hcd, ep, urb, mem_flags);
++
++ urb_priv = kzalloc(sizeof(struct urb_priv), mem_flags);
++ if (!urb_priv)
++ return -ENOMEM;
++
++ spin_lock_irqsave(&imx21->lock, flags);
++
++ ep_priv = ep->hcpriv;
++ if (ep_priv == NULL) {
++ ep_priv = alloc_ep();
++ if (!ep_priv) {
++ ret = -ENOMEM;
++ goto failed_alloc_ep;
++ }
++ ep->hcpriv = ep_priv;
++ ep_priv->ep = ep;
++ new_ep = 1;
++ }
++
++ ret = usb_hcd_link_urb_to_ep(hcd, urb);
++ if (ret)
++ goto failed_link;
++
++ urb->status = -EINPROGRESS;
++ urb->actual_length = 0;
++ urb->error_count = 0;
++ urb->hcpriv = urb_priv;
++ urb_priv->ep = ep;
++
++ switch (usb_pipetype(urb->pipe)) {
++ case PIPE_CONTROL:
++ urb_priv->state = US_CTRL_SETUP;
++ break;
++ case PIPE_BULK:
++ urb_priv->state = US_BULK;
++ break;
++ }
++
++ debug_urb_submitted(imx21, urb);
++ if (ep_priv->etd[0] < 0) {
++ if (ep_priv->waiting_etd) {
++ dev_dbg(imx21->dev,
++ "no ETD available already queued %p\n",
++ ep_priv);
++ debug_urb_queued_for_etd(imx21, urb);
++ goto out;
++ }
++ ep_priv->etd[0] = alloc_etd(imx21);
++ if (ep_priv->etd[0] < 0) {
++ dev_dbg(imx21->dev,
++ "no ETD available queueing %p\n", ep_priv);
++ debug_urb_queued_for_etd(imx21, urb);
++ list_add_tail(&ep_priv->queue, &imx21->queue_for_etd);
++ ep_priv->waiting_etd = 1;
++ goto out;
++ }
++ }
++
++ /* Schedule if no URB already active for this endpoint */
++ etd = &imx21->etd[ep_priv->etd[0]];
++ if (etd->urb == NULL) {
++ DEBUG_LOG_FRAME(imx21, etd, last_req);
++ schedule_nonisoc_etd(imx21, urb);
++ }
++
++out:
++ spin_unlock_irqrestore(&imx21->lock, flags);
++ return 0;
++
++failed_link:
++failed_alloc_ep:
++ spin_unlock_irqrestore(&imx21->lock, flags);
++ kfree(urb_priv);
++ return ret;
++}
++
++static int imx21_hc_urb_dequeue(struct usb_hcd *hcd, struct urb *urb,
++ int status)
++{
++ struct imx21 *imx21 = hcd_to_imx21(hcd);
++ unsigned long flags;
++ struct usb_host_endpoint *ep;
++ struct ep_priv *ep_priv;
++ struct urb_priv *urb_priv = urb->hcpriv;
++ int ret = -EINVAL;
++
++ dev_vdbg(imx21->dev, "dequeue urb=%p iso=%d status=%d\n",
++ urb, usb_pipeisoc(urb->pipe), status);
++
++ spin_lock_irqsave(&imx21->lock, flags);
++
++ ret = usb_hcd_check_unlink_urb(hcd, urb, status);
++ if (ret)
++ goto fail;
++ ep = urb_priv->ep;
++ ep_priv = ep->hcpriv;
++
++ debug_urb_unlinked(imx21, urb);
++
++ if (usb_pipeisoc(urb->pipe)) {
++ dequeue_isoc_urb(imx21, urb, ep_priv);
++ schedule_isoc_etds(hcd, ep);
++ } else if (urb_priv->active) {
++ int etd_num = ep_priv->etd[0];
++ if (etd_num != -1) {
++ disactivate_etd(imx21, etd_num);
++ free_dmem(imx21, etd_readl(imx21, etd_num, 1) & 0xffff);
++ imx21->etd[etd_num].urb = NULL;
++ }
++ }
++
++ urb_done(hcd, urb, status);
++
++ spin_unlock_irqrestore(&imx21->lock, flags);
++ return 0;
++
++fail:
++ spin_unlock_irqrestore(&imx21->lock, flags);
++ return ret;
++}
++
++/* =========================================== */
++/* Interrupt dispatch */
++/* =========================================== */
++
++static void process_etds(struct usb_hcd *hcd, struct imx21 *imx21, int sof)
++{
++ int etd_num;
++ int enable_sof_int = 0;
++ unsigned long flags;
++
++ spin_lock_irqsave(&imx21->lock, flags);
++
++ for (etd_num = 0; etd_num < USB_NUM_ETD; etd_num++) {
++ u32 etd_mask = 1 << etd_num;
++ u32 enabled = readl(imx21->regs + USBH_ETDENSET) & etd_mask;
++ u32 done = readl(imx21->regs + USBH_ETDDONESTAT) & etd_mask;
++ struct etd_priv *etd = &imx21->etd[etd_num];
++
++
++ if (done) {
++ DEBUG_LOG_FRAME(imx21, etd, last_int);
++ } else {
++/*
++ * Kludge warning!
++ *
++ * When multiple transfers are using the bus we sometimes get into a state
++ * where the transfer has completed (the CC field of the ETD is != 0x0F),
++ * the ETD has self disabled but the ETDDONESTAT flag is not set
++ * (and hence no interrupt occurs).
++ * This causes the transfer in question to hang.
++ * The kludge below checks for this condition at each SOF and processes any
++ * blocked ETDs (after an arbitary 10 frame wait)
++ *
++ * With a single active transfer the usbtest test suite will run for days
++ * without the kludge.
++ * With other bus activity (eg mass storage) even just test1 will hang without
++ * the kludge.
++ */
++ u32 dword0;
++ int cc;
++
++ if (etd->active_count && !enabled) /* suspicious... */
++ enable_sof_int = 1;
++
++ if (!sof || enabled || !etd->active_count)
++ continue;
++
++ cc = etd_readl(imx21, etd_num, 2) >> DW2_COMPCODE;
++ if (cc == TD_NOTACCESSED)
++ continue;
++
++ if (++etd->active_count < 10)
++ continue;
++
++ dword0 = etd_readl(imx21, etd_num, 0);
++ dev_dbg(imx21->dev,
++ "unblock ETD %d dev=0x%X ep=0x%X cc=0x%02X!\n",
++ etd_num, dword0 & 0x7F,
++ (dword0 >> DW0_ENDPNT) & 0x0F,
++ cc);
++
++#ifdef DEBUG
++ dev_dbg(imx21->dev,
++ "frame: act=%d disact=%d"
++ " int=%d req=%d cur=%d\n",
++ etd->activated_frame,
++ etd->disactivated_frame,
++ etd->last_int_frame,
++ etd->last_req_frame,
++ readl(imx21->regs + USBH_FRMNUB));
++ imx21->debug_unblocks++;
++#endif
++ etd->active_count = 0;
++/* End of kludge */
++ }
++
++ if (etd->ep == NULL || etd->urb == NULL) {
++ dev_dbg(imx21->dev,
++ "Interrupt for unexpected etd %d"
++ " ep=%p urb=%p\n",
++ etd_num, etd->ep, etd->urb);
++ disactivate_etd(imx21, etd_num);
++ continue;
++ }
++
++ if (usb_pipeisoc(etd->urb->pipe))
++ isoc_etd_done(hcd, etd->urb, etd_num);
++ else
++ nonisoc_etd_done(hcd, etd->urb, etd_num);
++ }
++
++ /* only enable SOF interrupt if it may be needed for the kludge */
++ if (enable_sof_int)
++ set_register_bits(imx21, USBH_SYSIEN, USBH_SYSIEN_SOFINT);
++ else
++ clear_register_bits(imx21, USBH_SYSIEN, USBH_SYSIEN_SOFINT);
++
++
++ spin_unlock_irqrestore(&imx21->lock, flags);
++}
++
++static irqreturn_t imx21_irq(struct usb_hcd *hcd)
++{
++ struct imx21 *imx21 = hcd_to_imx21(hcd);
++ u32 ints = readl(imx21->regs + USBH_SYSISR);
++
++ if (ints & USBH_SYSIEN_HERRINT)
++ dev_dbg(imx21->dev, "Scheduling error\n");
++
++ if (ints & USBH_SYSIEN_SORINT)
++ dev_dbg(imx21->dev, "Scheduling overrun\n");
++
++ if (ints & (USBH_SYSISR_DONEINT | USBH_SYSISR_SOFINT))
++ process_etds(hcd, imx21, ints & USBH_SYSISR_SOFINT);
++
++ writel(ints, imx21->regs + USBH_SYSISR);
++ return IRQ_HANDLED;
++}
++
++static void imx21_hc_endpoint_disable(struct usb_hcd *hcd,
++ struct usb_host_endpoint *ep)
++{
++ struct imx21 *imx21 = hcd_to_imx21(hcd);
++ unsigned long flags;
++ struct ep_priv *ep_priv;
++ int i;
++
++ if (ep == NULL)
++ return;
++
++ spin_lock_irqsave(&imx21->lock, flags);
++ ep_priv = ep->hcpriv;
++ dev_vdbg(imx21->dev, "disable ep=%p, ep->hcpriv=%p\n", ep, ep_priv);
++
++ if (!list_empty(&ep->urb_list))
++ dev_dbg(imx21->dev, "ep's URB list is not empty\n");
++
++ if (ep_priv != NULL) {
++ for (i = 0; i < NUM_ISO_ETDS; i++) {
++ if (ep_priv->etd[i] > -1)
++ dev_dbg(imx21->dev, "free etd %d for disable\n",
++ ep_priv->etd[i]);
++
++ free_etd(imx21, ep_priv->etd[i]);
++ }
++ kfree(ep_priv);
++ ep->hcpriv = NULL;
++ }
++
++ for (i = 0; i < USB_NUM_ETD; i++) {
++ if (imx21->etd[i].alloc && imx21->etd[i].ep == ep) {
++ dev_err(imx21->dev,
++ "Active etd %d for disabled ep=%p!\n", i, ep);
++ free_etd(imx21, i);
++ }
++ }
++ free_epdmem(imx21, ep);
++ spin_unlock_irqrestore(&imx21->lock, flags);
++}
++
++/* =========================================== */
++/* Hub handling */
++/* =========================================== */
++
++static int get_hub_descriptor(struct usb_hcd *hcd,
++ struct usb_hub_descriptor *desc)
++{
++ struct imx21 *imx21 = hcd_to_imx21(hcd);
++ desc->bDescriptorType = 0x29; /* HUB descriptor */
++ desc->bHubContrCurrent = 0;
++
++ desc->bNbrPorts = readl(imx21->regs + USBH_ROOTHUBA)
++ & USBH_ROOTHUBA_NDNSTMPRT_MASK;
++ desc->bDescLength = 9;
++ desc->bPwrOn2PwrGood = 0;
++ desc->wHubCharacteristics = (__force __u16) cpu_to_le16(
++ 0x0002 | /* No power switching */
++ 0x0010 | /* No over current protection */
++ 0);
++
++ desc->bitmap[0] = 1 << 1;
++ desc->bitmap[1] = ~0;
++ return 0;
++}
++
++static int imx21_hc_hub_status_data(struct usb_hcd *hcd, char *buf)
++{
++ struct imx21 *imx21 = hcd_to_imx21(hcd);
++ int ports;
++ int changed = 0;
++ int i;
++ unsigned long flags;
++
++ spin_lock_irqsave(&imx21->lock, flags);
++ ports = readl(imx21->regs + USBH_ROOTHUBA)
++ & USBH_ROOTHUBA_NDNSTMPRT_MASK;
++ if (ports > 7) {
++ ports = 7;
++ dev_err(imx21->dev, "ports %d > 7\n", ports);
++ }
++ for (i = 0; i < ports; i++) {
++ if (readl(imx21->regs + USBH_PORTSTAT(i)) &
++ (USBH_PORTSTAT_CONNECTSC |
++ USBH_PORTSTAT_PRTENBLSC |
++ USBH_PORTSTAT_PRTSTATSC |
++ USBH_PORTSTAT_OVRCURIC |
++ USBH_PORTSTAT_PRTRSTSC)) {
++
++ changed = 1;
++ buf[0] |= 1 << (i + 1);
++ }
++ }
++ spin_unlock_irqrestore(&imx21->lock, flags);
++
++ if (changed)
++ dev_info(imx21->dev, "Hub status changed\n");
++ return changed;
++}
++
++static int imx21_hc_hub_control(struct usb_hcd *hcd,
++ u16 typeReq,
++ u16 wValue, u16 wIndex, char *buf, u16 wLength)
++{
++ struct imx21 *imx21 = hcd_to_imx21(hcd);
++ int rc = 0;
++ u32 status_write = 0;
++
++ switch (typeReq) {
++ case ClearHubFeature:
++ dev_dbg(imx21->dev, "ClearHubFeature\n");
++ switch (wValue) {
++ case C_HUB_OVER_CURRENT:
++ dev_dbg(imx21->dev, " OVER_CURRENT\n");
++ break;
++ case C_HUB_LOCAL_POWER:
++ dev_dbg(imx21->dev, " LOCAL_POWER\n");
++ break;
++ default:
++ dev_dbg(imx21->dev, " unknown\n");
++ rc = -EINVAL;
++ break;
++ }
++ break;
++
++ case ClearPortFeature:
++ dev_dbg(imx21->dev, "ClearPortFeature\n");
++ switch (wValue) {
++ case USB_PORT_FEAT_ENABLE:
++ dev_dbg(imx21->dev, " ENABLE\n");
++ status_write = USBH_PORTSTAT_CURCONST;
++ break;
++ case USB_PORT_FEAT_SUSPEND:
++ dev_dbg(imx21->dev, " SUSPEND\n");
++ status_write = USBH_PORTSTAT_PRTOVRCURI;
++ break;
++ case USB_PORT_FEAT_POWER:
++ dev_dbg(imx21->dev, " POWER\n");
++ status_write = USBH_PORTSTAT_LSDEVCON;
++ break;
++ case USB_PORT_FEAT_C_ENABLE:
++ dev_dbg(imx21->dev, " C_ENABLE\n");
++ status_write = USBH_PORTSTAT_PRTENBLSC;
++ break;
++ case USB_PORT_FEAT_C_SUSPEND:
++ dev_dbg(imx21->dev, " C_SUSPEND\n");
++ status_write = USBH_PORTSTAT_PRTSTATSC;
++ break;
++ case USB_PORT_FEAT_C_CONNECTION:
++ dev_dbg(imx21->dev, " C_CONNECTION\n");
++ status_write = USBH_PORTSTAT_CONNECTSC;
++ break;
++ case USB_PORT_FEAT_C_OVER_CURRENT:
++ dev_dbg(imx21->dev, " C_OVER_CURRENT\n");
++ status_write = USBH_PORTSTAT_OVRCURIC;
++ break;
++ case USB_PORT_FEAT_C_RESET:
++ dev_dbg(imx21->dev, " C_RESET\n");
++ status_write = USBH_PORTSTAT_PRTRSTSC;
++ break;
++ default:
++ dev_dbg(imx21->dev, " unknown\n");
++ rc = -EINVAL;
++ break;
++ }
++
++ break;
++
++ case GetHubDescriptor:
++ dev_dbg(imx21->dev, "GetHubDescriptor\n");
++ rc = get_hub_descriptor(hcd, (void *)buf);
++ break;
++
++ case GetHubStatus:
++ dev_dbg(imx21->dev, " GetHubStatus\n");
++ *(__le32 *) buf = 0;
++ break;
++
++ case GetPortStatus:
++ dev_dbg(imx21->dev, "GetPortStatus: port: %d, 0x%x\n",
++ wIndex, USBH_PORTSTAT(wIndex - 1));
++ *(__le32 *) buf = readl(imx21->regs +
++ USBH_PORTSTAT(wIndex - 1));
++ break;
++
++ case SetHubFeature:
++ dev_dbg(imx21->dev, "SetHubFeature\n");
++ switch (wValue) {
++ case C_HUB_OVER_CURRENT:
++ dev_dbg(imx21->dev, " OVER_CURRENT\n");
++ break;
++
++ case C_HUB_LOCAL_POWER:
++ dev_dbg(imx21->dev, " LOCAL_POWER\n");
++ break;
++ default:
++ dev_dbg(imx21->dev, " unknown\n");
++ rc = -EINVAL;
++ break;
++ }
++
++ break;
++
++ case SetPortFeature:
++ dev_dbg(imx21->dev, "SetPortFeature\n");
++ switch (wValue) {
++ case USB_PORT_FEAT_SUSPEND:
++ dev_dbg(imx21->dev, " SUSPEND\n");
++ status_write = USBH_PORTSTAT_PRTSUSPST;
++ break;
++ case USB_PORT_FEAT_POWER:
++ dev_dbg(imx21->dev, " POWER\n");
++ status_write = USBH_PORTSTAT_PRTPWRST;
++ break;
++ case USB_PORT_FEAT_RESET:
++ dev_dbg(imx21->dev, " RESET\n");
++ status_write = USBH_PORTSTAT_PRTRSTST;
++ break;
++ default:
++ dev_dbg(imx21->dev, " unknown\n");
++ rc = -EINVAL;
++ break;
++ }
++ break;
++
++ default:
++ dev_dbg(imx21->dev, " unknown\n");
++ rc = -EINVAL;
++ break;
++ }
++
++ if (status_write)
++ writel(status_write, imx21->regs + USBH_PORTSTAT(wIndex - 1));
++ return rc;
++}
++
++/* =========================================== */
++/* Host controller management */
++/* =========================================== */
++
++static int imx21_hc_reset(struct usb_hcd *hcd)
++{
++ struct imx21 *imx21 = hcd_to_imx21(hcd);
++ unsigned long timeout;
++ unsigned long flags;
++
++ spin_lock_irqsave(&imx21->lock, flags);
++
++ /* Reset the Host controler modules */
++ writel(USBOTG_RST_RSTCTRL | USBOTG_RST_RSTRH |
++ USBOTG_RST_RSTHSIE | USBOTG_RST_RSTHC,
++ imx21->regs + USBOTG_RST_CTRL);
++
++ /* Wait for reset to finish */
++ timeout = jiffies + HZ;
++ while (readl(imx21->regs + USBOTG_RST_CTRL) != 0) {
++ if (time_after(jiffies, timeout)) {
++ spin_unlock_irqrestore(&imx21->lock, flags);
++ dev_err(imx21->dev, "timeout waiting for reset\n");
++ return -ETIMEDOUT;
++ }
++ spin_unlock_irq(&imx21->lock);
++ schedule_timeout(1);
++ spin_lock_irq(&imx21->lock);
++ }
++ spin_unlock_irqrestore(&imx21->lock, flags);
++ return 0;
++}
++
++static int __devinit imx21_hc_start(struct usb_hcd *hcd)
++{
++ struct imx21 *imx21 = hcd_to_imx21(hcd);
++ unsigned long flags;
++ int i, j;
++ u32 hw_mode = USBOTG_HWMODE_CRECFG_HOST;
++ u32 usb_control = 0;
++
++ hw_mode |= ((imx21->pdata->host_xcvr << USBOTG_HWMODE_HOSTXCVR_SHIFT) &
++ USBOTG_HWMODE_HOSTXCVR_MASK);
++ hw_mode |= ((imx21->pdata->otg_xcvr << USBOTG_HWMODE_OTGXCVR_SHIFT) &
++ USBOTG_HWMODE_OTGXCVR_MASK);
++
++ if (imx21->pdata->host1_txenoe)
++ usb_control |= USBCTRL_HOST1_TXEN_OE;
++
++ if (!imx21->pdata->host1_xcverless)
++ usb_control |= USBCTRL_HOST1_BYP_TLL;
++
++ if (imx21->pdata->otg_ext_xcvr)
++ usb_control |= USBCTRL_OTC_RCV_RXDP;
++
++
++ spin_lock_irqsave(&imx21->lock, flags);
++
++ writel((USBOTG_CLK_CTRL_HST | USBOTG_CLK_CTRL_MAIN),
++ imx21->regs + USBOTG_CLK_CTRL);
++ writel(hw_mode, imx21->regs + USBOTG_HWMODE);
++ writel(usb_control, imx21->regs + USBCTRL);
++ writel(USB_MISCCONTROL_SKPRTRY | USB_MISCCONTROL_ARBMODE,
++ imx21->regs + USB_MISCCONTROL);
++
++ /* Clear the ETDs */
++ for (i = 0; i < USB_NUM_ETD; i++)
++ for (j = 0; j < 4; j++)
++ etd_writel(imx21, i, j, 0);
++
++ /* Take the HC out of reset */
++ writel(USBH_HOST_CTRL_HCUSBSTE_OPERATIONAL | USBH_HOST_CTRL_CTLBLKSR_1,
++ imx21->regs + USBH_HOST_CTRL);
++
++ /* Enable ports */
++ if (imx21->pdata->enable_otg_host)
++ writel(USBH_PORTSTAT_PRTPWRST | USBH_PORTSTAT_PRTENABST,
++ imx21->regs + USBH_PORTSTAT(0));
++
++ if (imx21->pdata->enable_host1)
++ writel(USBH_PORTSTAT_PRTPWRST | USBH_PORTSTAT_PRTENABST,
++ imx21->regs + USBH_PORTSTAT(1));
++
++ if (imx21->pdata->enable_host2)
++ writel(USBH_PORTSTAT_PRTPWRST | USBH_PORTSTAT_PRTENABST,
++ imx21->regs + USBH_PORTSTAT(2));
++
++
++ hcd->state = HC_STATE_RUNNING;
++
++ /* Enable host controller interrupts */
++ set_register_bits(imx21, USBH_SYSIEN,
++ USBH_SYSIEN_HERRINT |
++ USBH_SYSIEN_DONEINT | USBH_SYSIEN_SORINT);
++ set_register_bits(imx21, USBOTG_CINT_STEN, USBOTG_HCINT);
++
++ spin_unlock_irqrestore(&imx21->lock, flags);
++
++ return 0;
++}
++
++static void imx21_hc_stop(struct usb_hcd *hcd)
++{
++ struct imx21 *imx21 = hcd_to_imx21(hcd);
++ unsigned long flags;
++
++ spin_lock_irqsave(&imx21->lock, flags);
++
++ writel(0, imx21->regs + USBH_SYSIEN);
++ clear_register_bits(imx21, USBOTG_CINT_STEN, USBOTG_HCINT);
++ clear_register_bits(imx21, USBOTG_CLK_CTRL_HST | USBOTG_CLK_CTRL_MAIN,
++ USBOTG_CLK_CTRL);
++ spin_unlock_irqrestore(&imx21->lock, flags);
++}
++
++/* =========================================== */
++/* Driver glue */
++/* =========================================== */
++
++static struct hc_driver imx21_hc_driver = {
++ .description = hcd_name,
++ .product_desc = "IMX21 USB Host Controller",
++ .hcd_priv_size = sizeof(struct imx21),
++
++ .flags = HCD_USB11,
++ .irq = imx21_irq,
++
++ .reset = imx21_hc_reset,
++ .start = imx21_hc_start,
++ .stop = imx21_hc_stop,
++
++ /* I/O requests */
++ .urb_enqueue = imx21_hc_urb_enqueue,
++ .urb_dequeue = imx21_hc_urb_dequeue,
++ .endpoint_disable = imx21_hc_endpoint_disable,
++
++ /* scheduling support */
++ .get_frame_number = imx21_hc_get_frame,
++
++ /* Root hub support */
++ .hub_status_data = imx21_hc_hub_status_data,
++ .hub_control = imx21_hc_hub_control,
++
++};
++
++static struct mx21_usbh_platform_data default_pdata = {
++ .host_xcvr = MX21_USBXCVR_TXDIF_RXDIF,
++ .otg_xcvr = MX21_USBXCVR_TXDIF_RXDIF,
++ .enable_host1 = 1,
++ .enable_host2 = 1,
++ .enable_otg_host = 1,
++
++};
++
++static int imx21_remove(struct platform_device *pdev)
++{
++ struct usb_hcd *hcd = platform_get_drvdata(pdev);
++ struct imx21 *imx21 = hcd_to_imx21(hcd);
++ struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
++
++ remove_debug_files(imx21);
++ usb_remove_hcd(hcd);
++
++ if (res != NULL) {
++ clk_disable(imx21->clk);
++ clk_put(imx21->clk);
++ iounmap(imx21->regs);
++ release_mem_region(res->start, resource_size(res));
++ }
++
++ kfree(hcd);
++ return 0;
++}
++
++
++static int imx21_probe(struct platform_device *pdev)
++{
++ struct usb_hcd *hcd;
++ struct imx21 *imx21;
++ struct resource *res;
++ int ret;
++ int irq;
++
++ printk(KERN_INFO "%s\n", imx21_hc_driver.product_desc);
++
++ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
++ if (!res)
++ return -ENODEV;
++ irq = platform_get_irq(pdev, 0);
++ if (irq < 0)
++ return -ENXIO;
++
++ hcd = usb_create_hcd(&imx21_hc_driver,
++ &pdev->dev, dev_name(&pdev->dev));
++ if (hcd == NULL) {
++ dev_err(&pdev->dev, "Cannot create hcd (%s)\n",
++ dev_name(&pdev->dev));
++ return -ENOMEM;
++ }
++
++ imx21 = hcd_to_imx21(hcd);
++ imx21->dev = &pdev->dev;
++ imx21->pdata = pdev->dev.platform_data;
++ if (!imx21->pdata)
++ imx21->pdata = &default_pdata;
++
++ spin_lock_init(&imx21->lock);
++ INIT_LIST_HEAD(&imx21->dmem_list);
++ INIT_LIST_HEAD(&imx21->queue_for_etd);
++ INIT_LIST_HEAD(&imx21->queue_for_dmem);
++ create_debug_files(imx21);
++
++ res = request_mem_region(res->start, resource_size(res), hcd_name);
++ if (!res) {
++ ret = -EBUSY;
++ goto failed_request_mem;
++ }
++
++ imx21->regs = ioremap(res->start, resource_size(res));
++ if (imx21->regs == NULL) {
++ dev_err(imx21->dev, "Cannot map registers\n");
++ ret = -ENOMEM;
++ goto failed_ioremap;
++ }
++
++ /* Enable clocks source */
++ imx21->clk = clk_get(imx21->dev, NULL);
++ if (IS_ERR(imx21->clk)) {
++ dev_err(imx21->dev, "no clock found\n");
++ ret = PTR_ERR(imx21->clk);
++ goto failed_clock_get;
++ }
++
++ ret = clk_set_rate(imx21->clk, clk_round_rate(imx21->clk, 48000000));
++ if (ret)
++ goto failed_clock_set;
++ ret = clk_enable(imx21->clk);
++ if (ret)
++ goto failed_clock_enable;
++
++ dev_info(imx21->dev, "Hardware HC revision: 0x%02X\n",
++ (readl(imx21->regs + USBOTG_HWMODE) >> 16) & 0xFF);
++
++ ret = usb_add_hcd(hcd, irq, IRQF_DISABLED);
++ if (ret != 0) {
++ dev_err(imx21->dev, "usb_add_hcd() returned %d\n", ret);
++ goto failed_add_hcd;
++ }
++
++ return 0;
++
++failed_add_hcd:
++ clk_disable(imx21->clk);
++failed_clock_enable:
++failed_clock_set:
++ clk_put(imx21->clk);
++failed_clock_get:
++ iounmap(imx21->regs);
++failed_ioremap:
++ release_mem_region(res->start, res->end - res->start);
++failed_request_mem:
++ remove_debug_files(imx21);
++ usb_put_hcd(hcd);
++ return ret;
++}
++
++static struct platform_driver imx21_hcd_driver = {
++ .driver = {
++ .name = (char *)hcd_name,
++ },
++ .probe = imx21_probe,
++ .remove = imx21_remove,
++ .suspend = NULL,
++ .resume = NULL,
++};
++
++static int __init imx21_hcd_init(void)
++{
++ return platform_driver_register(&imx21_hcd_driver);
++}
++
++static void __exit imx21_hcd_cleanup(void)
++{
++ platform_driver_unregister(&imx21_hcd_driver);
++}
++
++module_init(imx21_hcd_init);
++module_exit(imx21_hcd_cleanup);
++
++MODULE_DESCRIPTION("i.MX21 USB Host controller");
++MODULE_AUTHOR("Martin Fuzzey");
++MODULE_LICENSE("GPL");
++MODULE_ALIAS("platform:imx21-hcd");
+--- /dev/null
++++ b/drivers/usb/host/imx21-hcd.h
+@@ -0,0 +1,436 @@
++/*
++ * Macros and prototypes for i.MX21
++ *
++ * Copyright (C) 2006 Loping Dog Embedded Systems
++ * Copyright (C) 2009 Martin Fuzzey
++ * Originally written by Jay Monkman <jtm@lopingdog.com>
++ * Ported to 2.6.30, debugged and enhanced by Martin Fuzzey
++ *
++ * This program is free software; you can redistribute it and/or modify it
++ * under the terms of the GNU General Public License as published by the
++ * Free Software Foundation; either version 2 of the License, or (at your
++ * option) any later version.
++ *
++ * This program is distributed in the hope that it will be useful, but
++ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
++ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
++ * for more details.
++ *
++ * You should have received a copy of the GNU General Public License
++ * along with this program; if not, write to the Free Software Foundation,
++ * Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
++ */
++
++#ifndef __LINUX_IMX21_HCD_H__
++#define __LINUX_IMX21_HCD_H__
++
++#include <mach/mx21-usbhost.h>
++
++#define NUM_ISO_ETDS 2
++#define USB_NUM_ETD 32
++#define DMEM_SIZE 4096
++
++/* Register definitions */
++#define USBOTG_HWMODE 0x00
++#define USBOTG_HWMODE_ANASDBEN (1 << 14)
++#define USBOTG_HWMODE_OTGXCVR_SHIFT 6
++#define USBOTG_HWMODE_OTGXCVR_MASK (3 << 6)
++#define USBOTG_HWMODE_OTGXCVR_TD_RD (0 << 6)
++#define USBOTG_HWMODE_OTGXCVR_TS_RD (2 << 6)
++#define USBOTG_HWMODE_OTGXCVR_TD_RS (1 << 6)
++#define USBOTG_HWMODE_OTGXCVR_TS_RS (3 << 6)
++#define USBOTG_HWMODE_HOSTXCVR_SHIFT 4
++#define USBOTG_HWMODE_HOSTXCVR_MASK (3 << 4)
++#define USBOTG_HWMODE_HOSTXCVR_TD_RD (0 << 4)
++#define USBOTG_HWMODE_HOSTXCVR_TS_RD (2 << 4)
++#define USBOTG_HWMODE_HOSTXCVR_TD_RS (1 << 4)
++#define USBOTG_HWMODE_HOSTXCVR_TS_RS (3 << 4)
++#define USBOTG_HWMODE_CRECFG_MASK (3 << 0)
++#define USBOTG_HWMODE_CRECFG_HOST (1 << 0)
++#define USBOTG_HWMODE_CRECFG_FUNC (2 << 0)
++#define USBOTG_HWMODE_CRECFG_HNP (3 << 0)
++
++#define USBOTG_CINT_STAT 0x04
++#define USBOTG_CINT_STEN 0x08
++#define USBOTG_ASHNPINT (1 << 5)
++#define USBOTG_ASFCINT (1 << 4)
++#define USBOTG_ASHCINT (1 << 3)
++#define USBOTG_SHNPINT (1 << 2)
++#define USBOTG_FCINT (1 << 1)
++#define USBOTG_HCINT (1 << 0)
++
++#define USBOTG_CLK_CTRL 0x0c
++#define USBOTG_CLK_CTRL_FUNC (1 << 2)
++#define USBOTG_CLK_CTRL_HST (1 << 1)
++#define USBOTG_CLK_CTRL_MAIN (1 << 0)
++
++#define USBOTG_RST_CTRL 0x10
++#define USBOTG_RST_RSTI2C (1 << 15)
++#define USBOTG_RST_RSTCTRL (1 << 5)
++#define USBOTG_RST_RSTFC (1 << 4)
++#define USBOTG_RST_RSTFSKE (1 << 3)
++#define USBOTG_RST_RSTRH (1 << 2)
++#define USBOTG_RST_RSTHSIE (1 << 1)
++#define USBOTG_RST_RSTHC (1 << 0)
++
++#define USBOTG_FRM_INTVL 0x14
++#define USBOTG_FRM_REMAIN 0x18
++#define USBOTG_HNP_CSR 0x1c
++#define USBOTG_HNP_ISR 0x2c
++#define USBOTG_HNP_IEN 0x30
++
++#define USBOTG_I2C_TXCVR_REG(x) (0x100 + (x))
++#define USBOTG_I2C_XCVR_DEVAD 0x118
++#define USBOTG_I2C_SEQ_OP_REG 0x119
++#define USBOTG_I2C_SEQ_RD_STARTAD 0x11a
++#define USBOTG_I2C_OP_CTRL_REG 0x11b
++#define USBOTG_I2C_SCLK_TO_SCK_HPER 0x11e
++#define USBOTG_I2C_MASTER_INT_REG 0x11f
++
++#define USBH_HOST_CTRL 0x80
++#define USBH_HOST_CTRL_HCRESET (1 << 31)
++#define USBH_HOST_CTRL_SCHDOVR(x) ((x) << 16)
++#define USBH_HOST_CTRL_RMTWUEN (1 << 4)
++#define USBH_HOST_CTRL_HCUSBSTE_RESET (0 << 2)
++#define USBH_HOST_CTRL_HCUSBSTE_RESUME (1 << 2)
++#define USBH_HOST_CTRL_HCUSBSTE_OPERATIONAL (2 << 2)
++#define USBH_HOST_CTRL_HCUSBSTE_SUSPEND (3 << 2)
++#define USBH_HOST_CTRL_CTLBLKSR_1 (0 << 0)
++#define USBH_HOST_CTRL_CTLBLKSR_2 (1 << 0)
++#define USBH_HOST_CTRL_CTLBLKSR_3 (2 << 0)
++#define USBH_HOST_CTRL_CTLBLKSR_4 (3 << 0)
++
++#define USBH_SYSISR 0x88
++#define USBH_SYSISR_PSCINT (1 << 6)
++#define USBH_SYSISR_FMOFINT (1 << 5)
++#define USBH_SYSISR_HERRINT (1 << 4)
++#define USBH_SYSISR_RESDETINT (1 << 3)
++#define USBH_SYSISR_SOFINT (1 << 2)
++#define USBH_SYSISR_DONEINT (1 << 1)
++#define USBH_SYSISR_SORINT (1 << 0)
++
++#define USBH_SYSIEN 0x8c
++#define USBH_SYSIEN_PSCINT (1 << 6)
++#define USBH_SYSIEN_FMOFINT (1 << 5)
++#define USBH_SYSIEN_HERRINT (1 << 4)
++#define USBH_SYSIEN_RESDETINT (1 << 3)
++#define USBH_SYSIEN_SOFINT (1 << 2)
++#define USBH_SYSIEN_DONEINT (1 << 1)
++#define USBH_SYSIEN_SORINT (1 << 0)
++
++#define USBH_XBUFSTAT 0x98
++#define USBH_YBUFSTAT 0x9c
++#define USBH_XYINTEN 0xa0
++#define USBH_XFILLSTAT 0xa8
++#define USBH_YFILLSTAT 0xac
++#define USBH_ETDENSET 0xc0
++#define USBH_ETDENCLR 0xc4
++#define USBH_IMMEDINT 0xcc
++#define USBH_ETDDONESTAT 0xd0
++#define USBH_ETDDONEEN 0xd4
++#define USBH_FRMNUB 0xe0
++#define USBH_LSTHRESH 0xe4
++
++#define USBH_ROOTHUBA 0xe8
++#define USBH_ROOTHUBA_PWRTOGOOD_MASK (0xff)
++#define USBH_ROOTHUBA_PWRTOGOOD_SHIFT (24)
++#define USBH_ROOTHUBA_NOOVRCURP (1 << 12)
++#define USBH_ROOTHUBA_OVRCURPM (1 << 11)
++#define USBH_ROOTHUBA_DEVTYPE (1 << 10)
++#define USBH_ROOTHUBA_PWRSWTMD (1 << 9)
++#define USBH_ROOTHUBA_NOPWRSWT (1 << 8)
++#define USBH_ROOTHUBA_NDNSTMPRT_MASK (0xff)
++
++#define USBH_ROOTHUBB 0xec
++#define USBH_ROOTHUBB_PRTPWRCM(x) (1 << ((x) + 16))
++#define USBH_ROOTHUBB_DEVREMOVE(x) (1 << (x))
++
++#define USBH_ROOTSTAT 0xf0
++#define USBH_ROOTSTAT_CLRRMTWUE (1 << 31)
++#define USBH_ROOTSTAT_OVRCURCHG (1 << 17)
++#define USBH_ROOTSTAT_DEVCONWUE (1 << 15)
++#define USBH_ROOTSTAT_OVRCURI (1 << 1)
++#define USBH_ROOTSTAT_LOCPWRS (1 << 0)
++
++#define USBH_PORTSTAT(x) (0xf4 + ((x) * 4))
++#define USBH_PORTSTAT_PRTRSTSC (1 << 20)
++#define USBH_PORTSTAT_OVRCURIC (1 << 19)
++#define USBH_PORTSTAT_PRTSTATSC (1 << 18)
++#define USBH_PORTSTAT_PRTENBLSC (1 << 17)
++#define USBH_PORTSTAT_CONNECTSC (1 << 16)
++#define USBH_PORTSTAT_LSDEVCON (1 << 9)
++#define USBH_PORTSTAT_PRTPWRST (1 << 8)
++#define USBH_PORTSTAT_PRTRSTST (1 << 4)
++#define USBH_PORTSTAT_PRTOVRCURI (1 << 3)
++#define USBH_PORTSTAT_PRTSUSPST (1 << 2)
++#define USBH_PORTSTAT_PRTENABST (1 << 1)
++#define USBH_PORTSTAT_CURCONST (1 << 0)
++
++#define USB_DMAREV 0x800
++#define USB_DMAINTSTAT 0x804
++#define USB_DMAINTSTAT_EPERR (1 << 1)
++#define USB_DMAINTSTAT_ETDERR (1 << 0)
++
++#define USB_DMAINTEN 0x808
++#define USB_DMAINTEN_EPERRINTEN (1 << 1)
++#define USB_DMAINTEN_ETDERRINTEN (1 << 0)
++
++#define USB_ETDDMAERSTAT 0x80c
++#define USB_EPDMAERSTAT 0x810
++#define USB_ETDDMAEN 0x820
++#define USB_EPDMAEN 0x824
++#define USB_ETDDMAXTEN 0x828
++#define USB_EPDMAXTEN 0x82c
++#define USB_ETDDMAENXYT 0x830
++#define USB_EPDMAENXYT 0x834
++#define USB_ETDDMABST4EN 0x838
++#define USB_EPDMABST4EN 0x83c
++
++#define USB_MISCCONTROL 0x840
++#define USB_MISCCONTROL_ISOPREVFRM (1 << 3)
++#define USB_MISCCONTROL_SKPRTRY (1 << 2)
++#define USB_MISCCONTROL_ARBMODE (1 << 1)
++#define USB_MISCCONTROL_FILTCC (1 << 0)
++
++#define USB_ETDDMACHANLCLR 0x848
++#define USB_EPDMACHANLCLR 0x84c
++#define USB_ETDSMSA(x) (0x900 + ((x) * 4))
++#define USB_EPSMSA(x) (0x980 + ((x) * 4))
++#define USB_ETDDMABUFPTR(x) (0xa00 + ((x) * 4))
++#define USB_EPDMABUFPTR(x) (0xa80 + ((x) * 4))
++
++#define USB_ETD_DWORD(x, w) (0x200 + ((x) * 16) + ((w) * 4))
++#define DW0_ADDRESS 0
++#define DW0_ENDPNT 7
++#define DW0_DIRECT 11
++#define DW0_SPEED 13
++#define DW0_FORMAT 14
++#define DW0_MAXPKTSIZ 16
++#define DW0_HALTED 27
++#define DW0_TOGCRY 28
++#define DW0_SNDNAK 30
++
++#define DW1_XBUFSRTAD 0
++#define DW1_YBUFSRTAD 16
++
++#define DW2_RTRYDELAY 0
++#define DW2_POLINTERV 0
++#define DW2_STARTFRM 0
++#define DW2_RELPOLPOS 8
++#define DW2_DIRPID 16
++#define DW2_BUFROUND 18
++#define DW2_DELAYINT 19
++#define DW2_DATATOG 22
++#define DW2_ERRORCNT 24
++#define DW2_COMPCODE 28
++
++#define DW3_TOTBYECNT 0
++#define DW3_PKTLEN0 0
++#define DW3_COMPCODE0 12
++#define DW3_PKTLEN1 16
++#define DW3_BUFSIZE 21
++#define DW3_COMPCODE1 28
++
++#define USBCTRL 0x600
++#define USBCTRL_I2C_WU_INT_STAT (1 << 27)
++#define USBCTRL_OTG_WU_INT_STAT (1 << 26)
++#define USBCTRL_HOST_WU_INT_STAT (1 << 25)
++#define USBCTRL_FNT_WU_INT_STAT (1 << 24)
++#define USBCTRL_I2C_WU_INT_EN (1 << 19)
++#define USBCTRL_OTG_WU_INT_EN (1 << 18)
++#define USBCTRL_HOST_WU_INT_EN (1 << 17)
++#define USBCTRL_FNT_WU_INT_EN (1 << 16)
++#define USBCTRL_OTC_RCV_RXDP (1 << 13)
++#define USBCTRL_HOST1_BYP_TLL (1 << 12)
++#define USBCTRL_OTG_BYP_VAL(x) ((x) << 10)
++#define USBCTRL_HOST1_BYP_VAL(x) ((x) << 8)
++#define USBCTRL_OTG_PWR_MASK (1 << 6)
++#define USBCTRL_HOST1_PWR_MASK (1 << 5)
++#define USBCTRL_HOST2_PWR_MASK (1 << 4)
++#define USBCTRL_USB_BYP (1 << 2)
++#define USBCTRL_HOST1_TXEN_OE (1 << 1)
++
++
++/* Values in TD blocks */
++#define TD_DIR_SETUP 0
++#define TD_DIR_OUT 1
++#define TD_DIR_IN 2
++#define TD_FORMAT_CONTROL 0
++#define TD_FORMAT_ISO 1
++#define TD_FORMAT_BULK 2
++#define TD_FORMAT_INT 3
++#define TD_TOGGLE_CARRY 0
++#define TD_TOGGLE_DATA0 2
++#define TD_TOGGLE_DATA1 3
++
++/* control transfer states */
++#define US_CTRL_SETUP 2
++#define US_CTRL_DATA 1
++#define US_CTRL_ACK 0
++
++/* bulk transfer main state and 0-length packet */
++#define US_BULK 1
++#define US_BULK0 0
++
++/*ETD format description*/
++#define IMX_FMT_CTRL 0x0
++#define IMX_FMT_ISO 0x1
++#define IMX_FMT_BULK 0x2
++#define IMX_FMT_INT 0x3
++
++static char fmt_urb_to_etd[4] = {
++/*PIPE_ISOCHRONOUS*/ IMX_FMT_ISO,
++/*PIPE_INTERRUPT*/ IMX_FMT_INT,
++/*PIPE_CONTROL*/ IMX_FMT_CTRL,
++/*PIPE_BULK*/ IMX_FMT_BULK
++};
++
++/* condition (error) CC codes and mapping (OHCI like) */
++
++#define TD_CC_NOERROR 0x00
++#define TD_CC_CRC 0x01
++#define TD_CC_BITSTUFFING 0x02
++#define TD_CC_DATATOGGLEM 0x03
++#define TD_CC_STALL 0x04
++#define TD_DEVNOTRESP 0x05
++#define TD_PIDCHECKFAIL 0x06
++/*#define TD_UNEXPECTEDPID 0x07 - reserved, not active on MX2*/
++#define TD_DATAOVERRUN 0x08
++#define TD_DATAUNDERRUN 0x09
++#define TD_BUFFEROVERRUN 0x0C
++#define TD_BUFFERUNDERRUN 0x0D
++#define TD_SCHEDULEOVERRUN 0x0E
++#define TD_NOTACCESSED 0x0F
++
++static const int cc_to_error[16] = {
++ /* No Error */ 0,
++ /* CRC Error */ -EILSEQ,
++ /* Bit Stuff */ -EPROTO,
++ /* Data Togg */ -EILSEQ,
++ /* Stall */ -EPIPE,
++ /* DevNotResp */ -ETIMEDOUT,
++ /* PIDCheck */ -EPROTO,
++ /* UnExpPID */ -EPROTO,
++ /* DataOver */ -EOVERFLOW,
++ /* DataUnder */ -EREMOTEIO,
++ /* (for hw) */ -EIO,
++ /* (for hw) */ -EIO,
++ /* BufferOver */ -ECOMM,
++ /* BuffUnder */ -ENOSR,
++ /* (for HCD) */ -ENOSPC,
++ /* (for HCD) */ -EALREADY
++};
++
++/* HCD data associated with a usb core URB */
++struct urb_priv {
++ struct urb *urb;
++ struct usb_host_endpoint *ep;
++ int active;
++ int state;
++ struct td *isoc_td;
++ int isoc_remaining;
++ int isoc_status;
++};
++
++/* HCD data associated with a usb core endpoint */
++struct ep_priv {
++ struct usb_host_endpoint *ep;
++ struct list_head td_list;
++ struct list_head queue;
++ int etd[NUM_ISO_ETDS];
++ int waiting_etd;
++};
++
++/* isoc packet */
++struct td {
++ struct list_head list;
++ struct urb *urb;
++ struct usb_host_endpoint *ep;
++ dma_addr_t data;
++ unsigned long buf_addr;
++ int len;
++ int frame;
++ int isoc_index;
++};
++
++/* HCD data associated with a hardware ETD */
++struct etd_priv {
++ struct usb_host_endpoint *ep;
++ struct urb *urb;
++ struct td *td;
++ struct list_head queue;
++ dma_addr_t dma_handle;
++ int alloc;
++ int len;
++ int dmem_size;
++ int dmem_offset;
++ int active_count;
++#ifdef DEBUG
++ int activated_frame;
++ int disactivated_frame;
++ int last_int_frame;
++ int last_req_frame;
++ u32 submitted_dwords[4];
++#endif
++};
++
++/* Hardware data memory info */
++struct imx21_dmem_area {
++ struct usb_host_endpoint *ep;
++ unsigned int offset;
++ unsigned int size;
++ struct list_head list;
++};
++
++#ifdef DEBUG
++struct debug_usage_stats {
++ unsigned int value;
++ unsigned int maximum;
++};
++
++struct debug_stats {
++ unsigned long submitted;
++ unsigned long completed_ok;
++ unsigned long completed_failed;
++ unsigned long unlinked;
++ unsigned long queue_etd;
++ unsigned long queue_dmem;
++};
++
++struct debug_isoc_trace {
++ int schedule_frame;
++ int submit_frame;
++ int request_len;
++ int done_frame;
++ int done_len;
++ int cc;
++ struct td *td;
++};
++#endif
++
++/* HCD data structure */
++struct imx21 {
++ spinlock_t lock;
++ struct device *dev;
++ struct mx21_usbh_platform_data *pdata;
++ struct list_head dmem_list;
++ struct list_head queue_for_etd; /* eps queued due to etd shortage */
++ struct list_head queue_for_dmem; /* etds queued due to dmem shortage */
++ struct etd_priv etd[USB_NUM_ETD];
++ struct clk *clk;
++ void __iomem *regs;
++#ifdef DEBUG
++ struct dentry *debug_root;
++ struct debug_stats nonisoc_stats;
++ struct debug_stats isoc_stats;
++ struct debug_usage_stats etd_usage;
++ struct debug_usage_stats dmem_usage;
++ struct debug_isoc_trace isoc_trace[20];
++ struct debug_isoc_trace isoc_trace_failed[20];
++ unsigned long debug_unblocks;
++ int isoc_trace_index;
++ int isoc_trace_index_failed;
++#endif
++};
++
++#endif
+--- a/drivers/usb/host/Kconfig
++++ b/drivers/usb/host/Kconfig
+@@ -399,3 +399,14 @@ config USB_HWA_HCD
+
+ To compile this driver a module, choose M here: the module
+ will be called "hwa-hc".
++
++config USB_IMX21_HCD
++ tristate "iMX21 HCD support"
++ depends on USB && ARM && MACH_MX21
++ help
++ This driver enables support for the on-chip USB host in the
++ iMX21 processor.
++
++ To compile this driver as a module, choose M here: the
++ module will be called "imx21-hcd".
++
+--- a/drivers/usb/host/Makefile
++++ b/drivers/usb/host/Makefile
+@@ -32,3 +32,5 @@ obj-$(CONFIG_USB_U132_HCD) += u132-hcd.o
+ obj-$(CONFIG_USB_R8A66597_HCD) += r8a66597-hcd.o
+ obj-$(CONFIG_USB_ISP1760_HCD) += isp1760.o
+ obj-$(CONFIG_USB_HWA_HCD) += hwa-hc.o
++obj-$(CONFIG_USB_IMX21_HCD) += imx21-hcd.o
++
+--- a/drivers/usb/Makefile
++++ b/drivers/usb/Makefile
+@@ -21,6 +21,7 @@ obj-$(CONFIG_USB_U132_HCD) += host/
+ obj-$(CONFIG_USB_R8A66597_HCD) += host/
+ obj-$(CONFIG_USB_HWA_HCD) += host/
+ obj-$(CONFIG_USB_ISP1760_HCD) += host/
++obj-$(CONFIG_USB_IMX21_HCD) += host/
+
+ obj-$(CONFIG_USB_C67X00_HCD) += c67x00/
+
diff --git a/usb/usb-mxc-add-platform-resources-for-i.mx21-usb-host-controller.patch b/usb/usb-mxc-add-platform-resources-for-i.mx21-usb-host-controller.patch
new file mode 100644
index 00000000000000..4482e64d7f94e8
--- /dev/null
+++ b/usb/usb-mxc-add-platform-resources-for-i.mx21-usb-host-controller.patch
@@ -0,0 +1,61 @@
+From mfuzzey@gmail.com Tue Dec 22 11:21:47 2009
+From: Martin Fuzzey <mfuzzey@gmail.com>
+Date: Sat, 21 Nov 2009 12:14:58 +0100
+Subject: USB: MXC: add platform resources for i.MX21 USB host controller.
+To: Greg KH <greg@kroah.com>, Sascha Hauer <s.hauer@pengutronix.de>
+Cc: linux-usb@vger.kernel.org, linux-arm-kernel@lists.infradead.org
+Message-ID: <20091121111458.7904.79069.stgit@srv002.fuzzey.net>
+
+
+
+
+Signed-off-by: Martin Fuzzey <mfuzzey@gmail.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
+
+---
+ arch/arm/mach-mx2/devices.c | 27 +++++++++++++++++++++++++++
+ arch/arm/mach-mx2/devices.h | 1 +
+ 2 files changed, 28 insertions(+)
+
+--- a/arch/arm/mach-mx2/devices.c
++++ b/arch/arm/mach-mx2/devices.c
+@@ -643,3 +643,30 @@ int __init mxc_register_gpios(void)
+ {
+ return mxc_gpio_init(imx_gpio_ports, ARRAY_SIZE(imx_gpio_ports));
+ }
++
++#ifdef CONFIG_MACH_MX21
++static struct resource mx21_usbhc_resources[] = {
++ {
++ .start = USBOTG_BASE_ADDR,
++ .end = USBOTG_BASE_ADDR + 0x1FFF,
++ .flags = IORESOURCE_MEM,
++ },
++ {
++ .start = MXC_INT_USBHOST,
++ .end = MXC_INT_USBHOST,
++ .flags = IORESOURCE_IRQ,
++ },
++};
++
++struct platform_device mx21_usbhc_device = {
++ .name = "imx21-hcd",
++ .id = 0,
++ .dev = {
++ .dma_mask = &mx21_usbhc_device.dev.coherent_dma_mask,
++ .coherent_dma_mask = DMA_BIT_MASK(32),
++ },
++ .num_resources = ARRAY_SIZE(mx21_usbhc_resources),
++ .resource = mx21_usbhc_resources,
++};
++#endif
++
+--- a/arch/arm/mach-mx2/devices.h
++++ b/arch/arm/mach-mx2/devices.h
+@@ -26,5 +26,6 @@ extern struct platform_device mxc_usbh2;
+ extern struct platform_device mxc_spi_device0;
+ extern struct platform_device mxc_spi_device1;
+ extern struct platform_device mxc_spi_device2;
++extern struct platform_device mx21_usbhc_device;
+ extern struct platform_device imx_ssi_device0;
+ extern struct platform_device imx_ssi_device1;
diff --git a/usb/usb-mxc-use-dma_bit_mask-macro-rather-than-hardcoded-constants.patch b/usb/usb-mxc-use-dma_bit_mask-macro-rather-than-hardcoded-constants.patch
new file mode 100644
index 00000000000000..1f1c1d3e7b84a4
--- /dev/null
+++ b/usb/usb-mxc-use-dma_bit_mask-macro-rather-than-hardcoded-constants.patch
@@ -0,0 +1,155 @@
+From mfuzzey@gmail.com Tue Dec 22 11:21:35 2009
+From: Martin Fuzzey <mfuzzey@gmail.com>
+Date: Sat, 21 Nov 2009 12:14:54 +0100
+Subject: USB: MXC: use DMA_BIT_MASK macro rather than hardcoded constants.
+To: Greg KH <greg@kroah.com>, Sascha Hauer <s.hauer@pengutronix.de>
+Cc: linux-usb@vger.kernel.org, linux-arm-kernel@lists.infradead.org
+Message-ID: <20091121111453.7904.12103.stgit@srv002.fuzzey.net>
+
+
+Also fixes tab/space issue causing checkpatch to complain.
+
+Signed-off-by: Martin Fuzzey <mfuzzey@gmail.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
+
+
+---
+ arch/arm/mach-mx2/devices.c | 53 ++++++++++++++++++++++----------------------
+ 1 file changed, 27 insertions(+), 26 deletions(-)
+
+--- a/arch/arm/mach-mx2/devices.c
++++ b/arch/arm/mach-mx2/devices.c
+@@ -31,6 +31,7 @@
+ #include <linux/init.h>
+ #include <linux/platform_device.h>
+ #include <linux/gpio.h>
++#include <linux/dma-mapping.h>
+
+ #include <mach/irqs.h>
+ #include <mach/hardware.h>
+@@ -292,7 +293,7 @@ struct platform_device mxc_fb_device = {
+ .num_resources = ARRAY_SIZE(mxc_fb),
+ .resource = mxc_fb,
+ .dev = {
+- .coherent_dma_mask = 0xFFFFFFFF,
++ .coherent_dma_mask = DMA_BIT_MASK(32),
+ },
+ };
+
+@@ -395,17 +396,17 @@ static struct resource mxc_sdhc1_resourc
+ },
+ };
+
+-static u64 mxc_sdhc1_dmamask = 0xffffffffUL;
++static u64 mxc_sdhc1_dmamask = DMA_BIT_MASK(32);
+
+ struct platform_device mxc_sdhc_device0 = {
+- .name = "mxc-mmc",
+- .id = 0,
+- .dev = {
+- .dma_mask = &mxc_sdhc1_dmamask,
+- .coherent_dma_mask = 0xffffffff,
+- },
+- .num_resources = ARRAY_SIZE(mxc_sdhc1_resources),
+- .resource = mxc_sdhc1_resources,
++ .name = "mxc-mmc",
++ .id = 0,
++ .dev = {
++ .dma_mask = &mxc_sdhc1_dmamask,
++ .coherent_dma_mask = DMA_BIT_MASK(32),
++ },
++ .num_resources = ARRAY_SIZE(mxc_sdhc1_resources),
++ .resource = mxc_sdhc1_resources,
+ };
+
+ static struct resource mxc_sdhc2_resources[] = {
+@@ -424,17 +425,17 @@ static struct resource mxc_sdhc2_resourc
+ },
+ };
+
+-static u64 mxc_sdhc2_dmamask = 0xffffffffUL;
++static u64 mxc_sdhc2_dmamask = DMA_BIT_MASK(32);
+
+ struct platform_device mxc_sdhc_device1 = {
+- .name = "mxc-mmc",
+- .id = 1,
+- .dev = {
+- .dma_mask = &mxc_sdhc2_dmamask,
+- .coherent_dma_mask = 0xffffffff,
+- },
+- .num_resources = ARRAY_SIZE(mxc_sdhc2_resources),
+- .resource = mxc_sdhc2_resources,
++ .name = "mxc-mmc",
++ .id = 1,
++ .dev = {
++ .dma_mask = &mxc_sdhc2_dmamask,
++ .coherent_dma_mask = DMA_BIT_MASK(32),
++ },
++ .num_resources = ARRAY_SIZE(mxc_sdhc2_resources),
++ .resource = mxc_sdhc2_resources,
+ };
+
+ #ifdef CONFIG_MACH_MX27
+@@ -450,7 +451,7 @@ static struct resource otg_resources[] =
+ },
+ };
+
+-static u64 otg_dmamask = 0xffffffffUL;
++static u64 otg_dmamask = DMA_BIT_MASK(32);
+
+ /* OTG gadget device */
+ struct platform_device mxc_otg_udc_device = {
+@@ -458,7 +459,7 @@ struct platform_device mxc_otg_udc_devic
+ .id = -1,
+ .dev = {
+ .dma_mask = &otg_dmamask,
+- .coherent_dma_mask = 0xffffffffUL,
++ .coherent_dma_mask = DMA_BIT_MASK(32),
+ },
+ .resource = otg_resources,
+ .num_resources = ARRAY_SIZE(otg_resources),
+@@ -469,7 +470,7 @@ struct platform_device mxc_otg_host = {
+ .name = "mxc-ehci",
+ .id = 0,
+ .dev = {
+- .coherent_dma_mask = 0xffffffff,
++ .coherent_dma_mask = DMA_BIT_MASK(32),
+ .dma_mask = &otg_dmamask,
+ },
+ .resource = otg_resources,
+@@ -478,7 +479,7 @@ struct platform_device mxc_otg_host = {
+
+ /* USB host 1 */
+
+-static u64 usbh1_dmamask = 0xffffffffUL;
++static u64 usbh1_dmamask = DMA_BIT_MASK(32);
+
+ static struct resource mxc_usbh1_resources[] = {
+ {
+@@ -496,7 +497,7 @@ struct platform_device mxc_usbh1 = {
+ .name = "mxc-ehci",
+ .id = 1,
+ .dev = {
+- .coherent_dma_mask = 0xffffffff,
++ .coherent_dma_mask = DMA_BIT_MASK(32),
+ .dma_mask = &usbh1_dmamask,
+ },
+ .resource = mxc_usbh1_resources,
+@@ -504,7 +505,7 @@ struct platform_device mxc_usbh1 = {
+ };
+
+ /* USB host 2 */
+-static u64 usbh2_dmamask = 0xffffffffUL;
++static u64 usbh2_dmamask = DMA_BIT_MASK(32);
+
+ static struct resource mxc_usbh2_resources[] = {
+ {
+@@ -522,7 +523,7 @@ struct platform_device mxc_usbh2 = {
+ .name = "mxc-ehci",
+ .id = 2,
+ .dev = {
+- .coherent_dma_mask = 0xffffffff,
++ .coherent_dma_mask = DMA_BIT_MASK(32),
+ .dma_mask = &usbh2_dmamask,
+ },
+ .resource = mxc_usbh2_resources,
diff --git a/usb/usb-otg-add-notifier-support.patch b/usb/usb-otg-add-notifier-support.patch
new file mode 100644
index 00000000000000..fe76e6144640f2
--- /dev/null
+++ b/usb/usb-otg-add-notifier-support.patch
@@ -0,0 +1,82 @@
+From felipe.balbi@nokia.com Tue Dec 22 11:58:05 2009
+From: Felipe Balbi <felipe.balbi@nokia.com>
+Date: Thu, 17 Dec 2009 13:01:36 +0200
+Subject: USB: otg: add notifier support
+To: Greg KH <greg@kroah.com>
+Cc: linux-usb@vger.kernel.org, Felipe Balbi <felipe.balbi@nokia.com>
+Message-ID: <1261047697-15791-2-git-send-email-felipe.balbi@nokia.com>
+
+
+The notifier will be used to communicate usb events
+to other drivers like the charger chip.
+
+This can be used as source of information to kick
+usb charger detection as described by the USB
+Battery Charging Specification 1.1 and/or to
+pass bMaxPower field of selected usb_configuration
+to charger chip in order to use that information
+as input current on the charging profile
+setup.
+
+Signed-off-by: Felipe Balbi <felipe.balbi@nokia.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
+
+---
+ include/linux/usb/otg.h | 25 +++++++++++++++++++++++++
+ 1 file changed, 25 insertions(+)
+
+--- a/include/linux/usb/otg.h
++++ b/include/linux/usb/otg.h
+@@ -9,6 +9,8 @@
+ #ifndef __LINUX_USB_OTG_H
+ #define __LINUX_USB_OTG_H
+
++#include <linux/notifier.h>
++
+ /* OTG defines lots of enumeration states before device reset */
+ enum usb_otg_state {
+ OTG_STATE_UNDEFINED = 0,
+@@ -33,6 +35,14 @@ enum usb_otg_state {
+ OTG_STATE_A_VBUS_ERR,
+ };
+
++enum usb_xceiv_events {
++ USB_EVENT_NONE, /* no events or cable disconnected */
++ USB_EVENT_VBUS, /* vbus valid event */
++ USB_EVENT_ID, /* id was grounded */
++ USB_EVENT_CHARGER, /* usb dedicated charger */
++ USB_EVENT_ENUMERATED, /* gadget driver enumerated */
++};
++
+ #define USB_OTG_PULLUP_ID (1 << 0)
+ #define USB_OTG_PULLDOWN_DP (1 << 1)
+ #define USB_OTG_PULLDOWN_DM (1 << 2)
+@@ -70,6 +80,9 @@ struct otg_transceiver {
+ struct otg_io_access_ops *io_ops;
+ void __iomem *io_priv;
+
++ /* for notification of usb_xceiv_events */
++ struct blocking_notifier_head notifier;
++
+ /* to pass extra port status to the root hub */
+ u16 port_status;
+ u16 port_change;
+@@ -203,6 +216,18 @@ otg_start_srp(struct otg_transceiver *ot
+ return otg->start_srp(otg);
+ }
+
++/* notifiers */
++static inline int
++otg_register_notifier(struct otg_transceiver *otg, struct notifier_block *nb)
++{
++ return blocking_notifier_chain_register(&otg->notifier, nb);
++}
++
++static inline void
++otg_unregister_notifier(struct otg_transceiver *otg, struct notifier_block *nb)
++{
++ blocking_notifier_chain_unregister(&otg->notifier, nb);
++}
+
+ /* for OTG controller drivers (and maybe other stuff) */
+ extern int usb_bus_start_enum(struct usb_bus *bus, unsigned port_num);
diff --git a/usb/usb-otg-isp1301_omap-fix-compile-error.patch b/usb/usb-otg-isp1301_omap-fix-compile-error.patch
new file mode 100644
index 00000000000000..2a536072fc4d44
--- /dev/null
+++ b/usb/usb-otg-isp1301_omap-fix-compile-error.patch
@@ -0,0 +1,41 @@
+From felipe.balbi@nokia.com Tue Dec 22 11:55:26 2009
+From: Felipe Balbi <felipe.balbi@nokia.com>
+Date: Tue, 15 Dec 2009 23:19:52 +0200
+Subject: usb: otg: isp1301_omap: fix compile error
+To: Greg Kroah-Hartman <gregkh@suse.de>
+Cc: Tony Lindgren <tony@atomide.com>, linux-usb@vger.kernel.org, Felipe Balbi <felipe.balbi@nokia.com>
+Message-ID: <1260911992-27840-1-git-send-email-felipe.balbi@nokia.com>
+
+
+commit 91c8a5a9985d5bf9c55f6f82f183f57b050b2a3a broke
+compilation of this driver after it introduced
+otg_init() as a static inline in <linux/usb/otg.h>
+
+Reported-by: Tony Lindgren <tony@atomide.com>
+Signed-off-by: Felipe Balbi <felipe.balbi@nokia.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
+
+---
+ drivers/usb/otg/isp1301_omap.c | 4 ++--
+ 1 file changed, 2 insertions(+), 2 deletions(-)
+
+--- a/drivers/usb/otg/isp1301_omap.c
++++ b/drivers/usb/otg/isp1301_omap.c
+@@ -843,7 +843,7 @@ static irqreturn_t omap_otg_irq(int irq,
+
+ static struct platform_device *otg_dev;
+
+-static int otg_init(struct isp1301 *isp)
++static int isp1301_otg_init(struct isp1301 *isp)
+ {
+ u32 l;
+
+@@ -1275,7 +1275,7 @@ static int __exit isp1301_remove(struct
+ static int isp1301_otg_enable(struct isp1301 *isp)
+ {
+ power_up(isp);
+- otg_init(isp);
++ isp1301_otg_init(isp);
+
+ /* NOTE: since we don't change this, this provides
+ * a few more interrupts than are strictly needed.
diff --git a/usb/usb-otg-twl4030-add-support-for-notifier.patch b/usb/usb-otg-twl4030-add-support-for-notifier.patch
new file mode 100644
index 00000000000000..3be4c87ebbd195
--- /dev/null
+++ b/usb/usb-otg-twl4030-add-support-for-notifier.patch
@@ -0,0 +1,128 @@
+From felipe.balbi@nokia.com Tue Dec 22 11:58:17 2009
+From: Felipe Balbi <felipe.balbi@nokia.com>
+Date: Thu, 17 Dec 2009 13:01:37 +0200
+Subject: USB: otg: twl4030: add support for notifier
+To: Greg KH <greg@kroah.com>
+Cc: linux-usb@vger.kernel.org, Felipe Balbi <felipe.balbi@nokia.com>
+Message-ID: <1261047697-15791-3-git-send-email-felipe.balbi@nokia.com>
+
+
+it's expected that the transceiver driver will
+initialize and call the notifier chain when
+necessary. Implement that for twl4030-usb driver.
+
+Signed-off-by: Felipe Balbi <felipe.balbi@nokia.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
+
+---
+ drivers/usb/otg/twl4030-usb.c | 35 ++++++++++++++---------------------
+ 1 file changed, 14 insertions(+), 21 deletions(-)
+
+--- a/drivers/usb/otg/twl4030-usb.c
++++ b/drivers/usb/otg/twl4030-usb.c
+@@ -36,7 +36,7 @@
+ #include <linux/i2c/twl.h>
+ #include <linux/regulator/consumer.h>
+ #include <linux/err.h>
+-
++#include <linux/notifier.h>
+
+ /* Register defines */
+
+@@ -236,15 +236,6 @@
+ #define PMBR1 0x0D
+ #define GPIO_USB_4PIN_ULPI_2430C (3 << 0)
+
+-
+-
+-enum linkstat {
+- USB_LINK_UNKNOWN = 0,
+- USB_LINK_NONE,
+- USB_LINK_VBUS,
+- USB_LINK_ID,
+-};
+-
+ struct twl4030_usb {
+ struct otg_transceiver otg;
+ struct device *dev;
+@@ -347,10 +338,10 @@ twl4030_usb_clear_bits(struct twl4030_us
+
+ /*-------------------------------------------------------------------------*/
+
+-static enum linkstat twl4030_usb_linkstat(struct twl4030_usb *twl)
++static enum usb_xceiv_events twl4030_usb_linkstat(struct twl4030_usb *twl)
+ {
+ int status;
+- int linkstat = USB_LINK_UNKNOWN;
++ int linkstat = USB_EVENT_NONE;
+
+ /*
+ * For ID/VBUS sensing, see manual section 15.4.8 ...
+@@ -368,11 +359,11 @@ static enum linkstat twl4030_usb_linksta
+ dev_err(twl->dev, "USB link status err %d\n", status);
+ else if (status & (BIT(7) | BIT(2))) {
+ if (status & BIT(2))
+- linkstat = USB_LINK_ID;
++ linkstat = USB_EVENT_ID;
+ else
+- linkstat = USB_LINK_VBUS;
++ linkstat = USB_EVENT_VBUS;
+ } else
+- linkstat = USB_LINK_NONE;
++ linkstat = USB_EVENT_NONE;
+
+ dev_dbg(twl->dev, "HW_CONDITIONS 0x%02x/%d; link %d\n",
+ status, status, linkstat);
+@@ -383,7 +374,7 @@ static enum linkstat twl4030_usb_linksta
+
+ spin_lock_irq(&twl->lock);
+ twl->linkstat = linkstat;
+- if (linkstat == USB_LINK_ID) {
++ if (linkstat == USB_EVENT_ID) {
+ twl->otg.default_a = true;
+ twl->otg.state = OTG_STATE_A_IDLE;
+ } else {
+@@ -564,7 +555,7 @@ static ssize_t twl4030_usb_vbus_show(str
+
+ spin_lock_irqsave(&twl->lock, flags);
+ ret = sprintf(buf, "%s\n",
+- (twl->linkstat == USB_LINK_VBUS) ? "on" : "off");
++ (twl->linkstat == USB_EVENT_VBUS) ? "on" : "off");
+ spin_unlock_irqrestore(&twl->lock, flags);
+
+ return ret;
+@@ -585,8 +576,7 @@ static irqreturn_t twl4030_usb_irq(int i
+ #endif
+
+ status = twl4030_usb_linkstat(twl);
+- if (status != USB_LINK_UNKNOWN) {
+-
++ if (status >= 0) {
+ /* FIXME add a set_power() method so that B-devices can
+ * configure the charger appropriately. It's not always
+ * correct to consume VBUS power, and how much current to
+@@ -598,12 +588,13 @@ static irqreturn_t twl4030_usb_irq(int i
+ * USB_LINK_VBUS state. musb_hdrc won't care until it
+ * starts to handle softconnect right.
+ */
+- if (status == USB_LINK_NONE)
++ if (status == USB_EVENT_NONE)
+ twl4030_phy_suspend(twl, 0);
+ else
+ twl4030_phy_resume(twl);
+
+- twl4030charger_usb_en(status == USB_LINK_VBUS);
++ blocking_notifier_call_chain(&twl->otg.notifier, status,
++ twl->otg.gadget);
+ }
+ sysfs_notify(&twl->dev->kobj, NULL, "vbus");
+
+@@ -693,6 +684,8 @@ static int __devinit twl4030_usb_probe(s
+ if (device_create_file(&pdev->dev, &dev_attr_vbus))
+ dev_warn(&pdev->dev, "could not create sysfs file\n");
+
++ BLOCKING_INIT_NOTIFIER_HEAD(&twl->otg.notifier);
++
+ /* Our job is to use irqs and status from the power module
+ * to keep the transceiver disabled when nothing's connected.
+ *
diff --git a/usb/usb-pxa27x_udc-avoid-compiler-warnings-and-misbehavior-on-buggy-hardware.patch b/usb/usb-pxa27x_udc-avoid-compiler-warnings-and-misbehavior-on-buggy-hardware.patch
new file mode 100644
index 00000000000000..5f66cf5291c6e0
--- /dev/null
+++ b/usb/usb-pxa27x_udc-avoid-compiler-warnings-and-misbehavior-on-buggy-hardware.patch
@@ -0,0 +1,61 @@
+From robert.jarzmik@free.fr Tue Dec 22 11:30:59 2009
+From: Enrico Scholz <enrico.scholz@sigma-chemnitz.de>
+Date: Fri, 04 Dec 2009 17:50:08 +0100
+Subject: USB: pxa27x_udc: avoid compiler warnings and misbehavior on buggy hardware
+To: dbrownell@users.sourceforge.net, gregkh@suse.de, Enrico Scholz <enrico.scholz@sigma-chemnitz.de>
+Cc: linux-usb@vger.kernel.org, eric.y.miao@gmail.com, linux@arm.linux.org.uk, Jonathan Cameron <jic23@cam.ac.uk>
+Message-ID: <87einaiszj.fsf@free.fr>
+
+
+From: Enrico Scholz <enrico.scholz@sigma-chemnitz.de>
+Date: Sun, 11 Oct 2009 11:52:48 +0200
+
+hardware reports wrong interrupt. Although such a situation should not
+happen, the compiler complains about this access.
+
+This patch adds a sanity check and generates warning to detect such
+issues.
+
+Signed-off-by: Enrico Scholz <enrico.scholz@sigma-chemnitz.de>
+Acked-by: Robert Jarzmik <robert.jarzmik@free.fr>
+Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
+
+---
+ drivers/usb/gadget/pxa27x_udc.c | 19 +++++++++++++------
+ 1 file changed, 13 insertions(+), 6 deletions(-)
+
+--- a/drivers/usb/gadget/pxa27x_udc.c
++++ b/drivers/usb/gadget/pxa27x_udc.c
+@@ -2218,9 +2218,13 @@ static void irq_handle_data(int irq, str
+ continue;
+
+ udc_writel(udc, UDCISR0, UDCISR_INT(i, UDCISR_INT_MASK));
+- ep = &udc->pxa_ep[i];
+- ep->stats.irqs++;
+- handle_ep(ep);
++
++ WARN_ON(i >= ARRAY_SIZE(udc->pxa_ep));
++ if (i < ARRAY_SIZE(udc->pxa_ep)) {
++ ep = &udc->pxa_ep[i];
++ ep->stats.irqs++;
++ handle_ep(ep);
++ }
+ }
+
+ for (i = 16; udcisr1 != 0 && i < 24; udcisr1 >>= 2, i++) {
+@@ -2228,9 +2232,12 @@ static void irq_handle_data(int irq, str
+ if (!(udcisr1 & UDCISR_INT_MASK))
+ continue;
+
+- ep = &udc->pxa_ep[i];
+- ep->stats.irqs++;
+- handle_ep(ep);
++ WARN_ON(i >= ARRAY_SIZE(udc->pxa_ep));
++ if (i < ARRAY_SIZE(udc->pxa_ep)) {
++ ep = &udc->pxa_ep[i];
++ ep->stats.irqs++;
++ handle_ep(ep);
++ }
+ }
+
+ }
diff --git a/usb/usb-pxa27x_udc.c-use-resource_size.patch b/usb/usb-pxa27x_udc.c-use-resource_size.patch
new file mode 100644
index 00000000000000..7346c7942e7410
--- /dev/null
+++ b/usb/usb-pxa27x_udc.c-use-resource_size.patch
@@ -0,0 +1,30 @@
+From hartleys@visionengravers.com Tue Dec 22 11:53:28 2009
+From: "H Hartley Sweeten" <hartleys@visionengravers.com>
+Date: Mon, 14 Dec 2009 18:05:02 -0500
+Subject: USB: pxa27x_udc.c: use resource_size()
+Cc: "David Brownell" <dbrownell@users.sourceforge.net>, "Robert Jarzmik" <robert.jarzmik@free.fr>
+Message-ID: <BD79186B4FD85F4B8E60E381CAEE19090200EA9E@mi8nycmail19.Mi8.com>
+
+
+Use resource_size().
+
+Signed-off-by: H Hartley Sweeten <hsweeten@visionengravers.com>
+Cc: David Brownell <dbrownell@users.sourceforge.net>
+Acked-by: Robert Jarzmik <robert.jarzmik@free.fr>
+Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
+
+---
+ drivers/usb/gadget/pxa27x_udc.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+--- a/drivers/usb/gadget/pxa27x_udc.c
++++ b/drivers/usb/gadget/pxa27x_udc.c
+@@ -2446,7 +2446,7 @@ static int __init pxa_udc_probe(struct p
+ }
+
+ retval = -ENOMEM;
+- udc->regs = ioremap(regs->start, regs->end - regs->start + 1);
++ udc->regs = ioremap(regs->start, resource_size(regs));
+ if (!udc->regs) {
+ dev_err(&pdev->dev, "Unable to map UDC I/O memory\n");
+ goto err_map;
diff --git a/usb/usb-serial-fix-typo-in-debug-message.patch b/usb/usb-serial-fix-typo-in-debug-message.patch
new file mode 100644
index 00000000000000..fd4a5eb5902756
--- /dev/null
+++ b/usb/usb-serial-fix-typo-in-debug-message.patch
@@ -0,0 +1,28 @@
+From jhovold@gmail.com Tue Dec 22 11:25:43 2009
+From: Johan Hovold <jhovold@gmail.com>
+Date: Sun, 22 Nov 2009 17:42:34 +0100
+Subject: USB: serial: fix typo in debug message
+To: Greg Kroah-Hartman <gregkh@suse.de>
+Cc: Alan Cox <alan@linux.intel.com>, Alan Stern <stern@rowland.harvard.edu>, Oliver Neukum <oliver@neukum.org>, linux-usb@vger.kernel.org, Johan Hovold <jhovold@gmail.com>
+Message-ID: <1258908154-14861-1-git-send-email-jhovold@gmail.com>
+
+
+Fixes confusing "serial_chars_in_buffer = port 0" messages.
+
+Signed-off-by: Johan Hovold <jhovold@gmail.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
+---
+ drivers/usb/serial/usb-serial.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+--- a/drivers/usb/serial/usb-serial.c
++++ b/drivers/usb/serial/usb-serial.c
+@@ -381,7 +381,7 @@ static int serial_write_room(struct tty_
+ static int serial_chars_in_buffer(struct tty_struct *tty)
+ {
+ struct usb_serial_port *port = tty->driver_data;
+- dbg("%s = port %d", __func__, port->number);
++ dbg("%s - port %d", __func__, port->number);
+
+ /* if the device was unplugged then any remaining characters
+ fell out of the connector ;) */
diff --git a/usb/usb-serial-mct_usb232-add-drain-on-close.patch b/usb/usb-serial-mct_usb232-add-drain-on-close.patch
new file mode 100644
index 00000000000000..a330e804d046a3
--- /dev/null
+++ b/usb/usb-serial-mct_usb232-add-drain-on-close.patch
@@ -0,0 +1,73 @@
+From zaitcev@redhat.com Tue Dec 22 11:31:54 2009
+From: Pete Zaitcev <zaitcev@redhat.com>
+Date: Mon, 7 Dec 2009 20:28:46 -0700
+Subject: USB: serial: mct_usb232: add drain on close
+To: Greg KH <greg@kroah.com>
+Cc: linux-usb@vger.kernel.org, Alan Cox <alan@lxorguk.ukuu.org.uk>
+Message-ID: <20091207202846.0eca5dd2@redhat.com>
+
+
+I ran the following test:
+
+ while true; do echo -n "01234567890abcdefghij" >/dev/ttyUSB0; done
+
+The data is sent over the serial line with random loss. It occurs because
+1. when echo closes ttyUSB0, there's a chance that it cancels the write URB.
+2. MCT U232 buffers the last character and acks the USB data before the
+ serial bits are completely sent out.
+
+This was originally reported in Red Hat Linux bug #170490.
+
+The fix relies on modern facilities drain_delay (approximately the
+number of bytes outstanding, see case #2 above) and chars_in_buffer.
+The latter did not account for the last buffer that was removed
+from the kfifo but not completed yet. I considered it a bug and
+changed it.
+
+The test above works as expected with this patch applied.
+
+Signed-off-by: Pete Zaitcev <zaitcev@redhat.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
+
+
+---
+ drivers/usb/serial/generic.c | 11 +++++++----
+ drivers/usb/serial/mct_u232.c | 3 +++
+ 2 files changed, 10 insertions(+), 4 deletions(-)
+
+--- a/drivers/usb/serial/generic.c
++++ b/drivers/usb/serial/generic.c
+@@ -386,12 +386,15 @@ int usb_serial_generic_chars_in_buffer(s
+
+ dbg("%s - port %d", __func__, port->number);
+
++ spin_lock_irqsave(&port->lock, flags);
+ if (serial->type->max_in_flight_urbs) {
+- spin_lock_irqsave(&port->lock, flags);
+ chars = port->tx_bytes_flight;
+- spin_unlock_irqrestore(&port->lock, flags);
+- } else if (serial->num_bulk_out)
+- chars = kfifo_len(port->write_fifo);
++ } else if (serial->num_bulk_out) {
++ /* This overcounts badly, but is good enough for drain wait. */
++ chars = __kfifo_len(port->write_fifo);
++ chars += port->write_urb_busy * port->bulk_out_size;
++ }
++ spin_unlock_irqrestore(&port->lock, flags);
+
+ dbg("%s - returns %d", __func__, chars);
+ return chars;
+--- a/drivers/usb/serial/mct_u232.c
++++ b/drivers/usb/serial/mct_u232.c
+@@ -514,8 +514,11 @@ static void mct_u232_dtr_rts(struct usb_
+
+ static void mct_u232_close(struct usb_serial_port *port)
+ {
++
+ dbg("%s port %d", __func__, port->number);
+
++ port->port.drain_delay = 2;
++
+ if (port->serial->dev) {
+ /* shutdown our urbs */
+ usb_kill_urb(port->write_urb);
diff --git a/usb/usb-serial-mct_usb232-move-dma-buffers-to-heap.patch b/usb/usb-serial-mct_usb232-move-dma-buffers-to-heap.patch
new file mode 100644
index 00000000000000..ec9f1de428a6cb
--- /dev/null
+++ b/usb/usb-serial-mct_usb232-move-dma-buffers-to-heap.patch
@@ -0,0 +1,190 @@
+From zaitcev@redhat.com Tue Dec 22 11:32:16 2009
+From: Pete Zaitcev <zaitcev@redhat.com>
+Date: Mon, 7 Dec 2009 20:29:05 -0700
+Subject: USB: serial mct_usb232: move DMA buffers to heap
+To: Greg KH <greg@kroah.com>
+Cc: linux-usb@vger.kernel.org
+Message-ID: <20091207202905.4b3b0b08@redhat.com>
+
+
+My distro kernel (Fedora Rawhide) started throwing warnings from DMA API
+checker, so I have no choice but band-aid it quick. There's no attempt
+to reuse DMA buffers. Control messages are only sent rarely anyway.
+
+Signed-off-by: Pete Zaitcev <zaitcev@redhat.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
+
+
+---
+ drivers/usb/serial/mct_u232.c | 55 +++++++++++++++++++++++++++++++++---------
+ drivers/usb/serial/mct_u232.h | 2 +
+ 2 files changed, 46 insertions(+), 11 deletions(-)
+
+--- a/drivers/usb/serial/mct_u232.c
++++ b/drivers/usb/serial/mct_u232.c
+@@ -75,6 +75,7 @@
+ #include <linux/module.h>
+ #include <linux/spinlock.h>
+ #include <linux/uaccess.h>
++#include <asm/unaligned.h>
+ #include <linux/usb.h>
+ #include <linux/usb/serial.h>
+ #include "mct_u232.h"
+@@ -231,19 +232,22 @@ static int mct_u232_calculate_baud_rate(
+ static int mct_u232_set_baud_rate(struct tty_struct *tty,
+ struct usb_serial *serial, struct usb_serial_port *port, speed_t value)
+ {
+- __le32 divisor;
++ unsigned int divisor;
+ int rc;
+- unsigned char zero_byte = 0;
++ unsigned char *buf;
+ unsigned char cts_enable_byte = 0;
+ speed_t speed;
+
+- divisor = cpu_to_le32(mct_u232_calculate_baud_rate(serial, value,
+- &speed));
++ buf = kmalloc(MCT_U232_MAX_SIZE, GFP_KERNEL);
++ if (buf == NULL)
++ return -ENOMEM;
+
++ divisor = mct_u232_calculate_baud_rate(serial, value, &speed);
++ put_unaligned_le32(cpu_to_le32(divisor), buf);
+ rc = usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0),
+ MCT_U232_SET_BAUD_RATE_REQUEST,
+ MCT_U232_SET_REQUEST_TYPE,
+- 0, 0, &divisor, MCT_U232_SET_BAUD_RATE_SIZE,
++ 0, 0, buf, MCT_U232_SET_BAUD_RATE_SIZE,
+ WDR_TIMEOUT);
+ if (rc < 0) /*FIXME: What value speed results */
+ dev_err(&port->dev, "Set BAUD RATE %d failed (error = %d)\n",
+@@ -269,10 +273,11 @@ static int mct_u232_set_baud_rate(struct
+ a device which is not asserting 'CTS'.
+ */
+
++ buf[0] = 0;
+ rc = usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0),
+ MCT_U232_SET_UNKNOWN1_REQUEST,
+ MCT_U232_SET_REQUEST_TYPE,
+- 0, 0, &zero_byte, MCT_U232_SET_UNKNOWN1_SIZE,
++ 0, 0, buf, MCT_U232_SET_UNKNOWN1_SIZE,
+ WDR_TIMEOUT);
+ if (rc < 0)
+ dev_err(&port->dev, "Sending USB device request code %d "
+@@ -284,30 +289,40 @@ static int mct_u232_set_baud_rate(struct
+
+ dbg("set_baud_rate: send second control message, data = %02X",
+ cts_enable_byte);
++ buf[0] = cts_enable_byte;
+ rc = usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0),
+ MCT_U232_SET_CTS_REQUEST,
+ MCT_U232_SET_REQUEST_TYPE,
+- 0, 0, &cts_enable_byte, MCT_U232_SET_CTS_SIZE,
++ 0, 0, buf, MCT_U232_SET_CTS_SIZE,
+ WDR_TIMEOUT);
+ if (rc < 0)
+ dev_err(&port->dev, "Sending USB device request code %d "
+ "failed (error = %d)\n", MCT_U232_SET_CTS_REQUEST, rc);
+
++ kfree(buf);
+ return rc;
+ } /* mct_u232_set_baud_rate */
+
+ static int mct_u232_set_line_ctrl(struct usb_serial *serial, unsigned char lcr)
+ {
+ int rc;
++ unsigned char *buf;
++
++ buf = kmalloc(MCT_U232_MAX_SIZE, GFP_KERNEL);
++ if (buf == NULL)
++ return -ENOMEM;
++
++ buf[0] = lcr;
+ rc = usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0),
+ MCT_U232_SET_LINE_CTRL_REQUEST,
+ MCT_U232_SET_REQUEST_TYPE,
+- 0, 0, &lcr, MCT_U232_SET_LINE_CTRL_SIZE,
++ 0, 0, buf, MCT_U232_SET_LINE_CTRL_SIZE,
+ WDR_TIMEOUT);
+ if (rc < 0)
+ dev_err(&serial->dev->dev,
+ "Set LINE CTRL 0x%x failed (error = %d)\n", lcr, rc);
+ dbg("set_line_ctrl: 0x%x", lcr);
++ kfree(buf);
+ return rc;
+ } /* mct_u232_set_line_ctrl */
+
+@@ -315,23 +330,31 @@ static int mct_u232_set_modem_ctrl(struc
+ unsigned int control_state)
+ {
+ int rc;
+- unsigned char mcr = MCT_U232_MCR_NONE;
++ unsigned char mcr;
++ unsigned char *buf;
++
++ buf = kmalloc(MCT_U232_MAX_SIZE, GFP_KERNEL);
++ if (buf == NULL)
++ return -ENOMEM;
+
++ mcr = MCT_U232_MCR_NONE;
+ if (control_state & TIOCM_DTR)
+ mcr |= MCT_U232_MCR_DTR;
+ if (control_state & TIOCM_RTS)
+ mcr |= MCT_U232_MCR_RTS;
+
++ buf[0] = mcr;
+ rc = usb_control_msg(serial->dev, usb_sndctrlpipe(serial->dev, 0),
+ MCT_U232_SET_MODEM_CTRL_REQUEST,
+ MCT_U232_SET_REQUEST_TYPE,
+- 0, 0, &mcr, MCT_U232_SET_MODEM_CTRL_SIZE,
++ 0, 0, buf, MCT_U232_SET_MODEM_CTRL_SIZE,
+ WDR_TIMEOUT);
+ if (rc < 0)
+ dev_err(&serial->dev->dev,
+ "Set MODEM CTRL 0x%x failed (error = %d)\n", mcr, rc);
+ dbg("set_modem_ctrl: state=0x%x ==> mcr=0x%x", control_state, mcr);
+
++ kfree(buf);
+ return rc;
+ } /* mct_u232_set_modem_ctrl */
+
+@@ -339,17 +362,27 @@ static int mct_u232_get_modem_stat(struc
+ unsigned char *msr)
+ {
+ int rc;
++ unsigned char *buf;
++
++ buf = kmalloc(MCT_U232_MAX_SIZE, GFP_KERNEL);
++ if (buf == NULL) {
++ *msr = 0;
++ return -ENOMEM;
++ }
+ rc = usb_control_msg(serial->dev, usb_rcvctrlpipe(serial->dev, 0),
+ MCT_U232_GET_MODEM_STAT_REQUEST,
+ MCT_U232_GET_REQUEST_TYPE,
+- 0, 0, msr, MCT_U232_GET_MODEM_STAT_SIZE,
++ 0, 0, buf, MCT_U232_GET_MODEM_STAT_SIZE,
+ WDR_TIMEOUT);
+ if (rc < 0) {
+ dev_err(&serial->dev->dev,
+ "Get MODEM STATus failed (error = %d)\n", rc);
+ *msr = 0;
++ } else {
++ *msr = buf[0];
+ }
+ dbg("get_modem_stat: 0x%x", *msr);
++ kfree(buf);
+ return rc;
+ } /* mct_u232_get_modem_stat */
+
+--- a/drivers/usb/serial/mct_u232.h
++++ b/drivers/usb/serial/mct_u232.h
+@@ -73,6 +73,8 @@
+ #define MCT_U232_SET_CTS_REQUEST 12
+ #define MCT_U232_SET_CTS_SIZE 1
+
++#define MCT_U232_MAX_SIZE 4 /* of MCT_XXX_SIZE */
++
+ /*
+ * Baud rate (divisor)
+ * Actually, there are two of them, MCT website calls them "Philips solution"
diff --git a/usb/usb-storage-never-reset-devices-that-will-morph-to-an-old-mode.patch b/usb/usb-storage-never-reset-devices-that-will-morph-to-an-old-mode.patch
new file mode 100644
index 00000000000000..c1325f24a023e1
--- /dev/null
+++ b/usb/usb-storage-never-reset-devices-that-will-morph-to-an-old-mode.patch
@@ -0,0 +1,64 @@
+From oliver@neukum.org Tue Dec 22 11:59:43 2009
+From: Oliver Neukum <oliver@neukum.org>
+Date: Fri, 18 Dec 2009 12:14:21 +0100
+Subject: USB: storage: Never reset devices that will morph to an old mode
+To: Greg KH <greg@kroah.com>, Josua Dietze <digidietze@draisberghof.de>, Alan Stern <stern@rowland.harvard.edu>, Matthew Dharm <mdharm-kernel@one-eyed-alien.net>, Matthew Garrett <mjg59@srcf.ucam.org>, Stefan Seyfried <stefan.seyfried@googlemail.com>, linux-usb@vger.kernel.org
+Message-ID: <200912181214.21522.oliver@neukum.org>
+
+
+Some devices must be switched to a new mode to fully use them.
+A reset would make them revert to the old mode. Therefore a reset
+must not be used for error handling with such devices.
+
+Signed-off-by: Oliver Neukum <oliver@neukum.org>
+Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
+
+---
+ drivers/usb/core/quirks.c | 3 ++-
+ drivers/usb/storage/transport.c | 6 ++++++
+ include/linux/usb/quirks.h | 3 +++
+ 3 files changed, 11 insertions(+), 1 deletion(-)
+
+--- a/drivers/usb/core/quirks.c
++++ b/drivers/usb/core/quirks.c
+@@ -120,6 +120,7 @@ void usb_detect_quirks(struct usb_device
+ * for all devices. It will affect things like hub resets
+ * and EMF-related port disables.
+ */
+- udev->persist_enabled = 1;
++ if (!(udev->quirks & USB_QUIRK_RESET_MORPHS))
++ udev->persist_enabled = 1;
+ #endif /* CONFIG_PM */
+ }
+--- a/drivers/usb/storage/transport.c
++++ b/drivers/usb/storage/transport.c
+@@ -47,6 +47,8 @@
+ #include <linux/errno.h>
+ #include <linux/slab.h>
+
++#include <linux/usb/quirks.h>
++
+ #include <scsi/scsi.h>
+ #include <scsi/scsi_eh.h>
+ #include <scsi/scsi_device.h>
+@@ -1297,6 +1299,10 @@ int usb_stor_port_reset(struct us_data *
+ {
+ int result;
+
++ /*for these devices we must use the class specific method */
++ if (us->pusb_dev->quirks & USB_QUIRK_RESET_MORPHS)
++ return -EPERM;
++
+ result = usb_lock_device_for_reset(us->pusb_dev, us->pusb_intf);
+ if (result < 0)
+ US_DEBUGP("unable to lock device for reset: %d\n", result);
+--- a/include/linux/usb/quirks.h
++++ b/include/linux/usb/quirks.h
+@@ -19,4 +19,7 @@
+ /* device can't handle its Configuration or Interface strings */
+ #define USB_QUIRK_CONFIG_INTF_STRINGS 0x00000008
+
++/*device will morph if reset, don't use reset for handling errors */
++#define USB_QUIRK_RESET_MORPHS 0x00000010
++
+ #endif /* __LINUX_USB_QUIRKS_H */
diff --git a/usb/usb-wusb-check-chid-is-all-zeros-before-stopping-the-host.patch b/usb/usb-wusb-check-chid-is-all-zeros-before-stopping-the-host.patch
new file mode 100644
index 00000000000000..04ce943159204d
--- /dev/null
+++ b/usb/usb-wusb-check-chid-is-all-zeros-before-stopping-the-host.patch
@@ -0,0 +1,35 @@
+From david.vrabel@csr.com Tue Dec 22 11:57:27 2009
+From: Julia Lawall <julia@diku.dk>
+Date: Thu, 17 Dec 2009 13:57:33 +0000
+Subject: USB: wusb: check CHID is all zeros before stopping the host
+To: Greg Kroah-Hartman <gregkh@suse.de>
+Cc: linux-usb@vger.kernel.org, Julia Lawall <julia@diku.dk>, David Vrabel <david.vrabel@csr.com>
+Message-ID: <1261058253-28184-1-git-send-email-david.vrabel@csr.com>
+
+
+From: Julia Lawall <julia@diku.dk>
+
+An incorrect sizeof() resulted in only 4 (or 8) octets of the CHID being
+checked instead of all 16 octets. A randomly generated CHID had a
+probability of being unable to start a WUSB host of less than 1 in
+2 billion.
+
+Signed-off-by: Julia Lawall <julia@diku.dk>
+Signed-off-by: David Vrabel <david.vrabel@csr.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
+
+---
+ drivers/usb/wusbcore/mmc.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+--- a/drivers/usb/wusbcore/mmc.c
++++ b/drivers/usb/wusbcore/mmc.c
+@@ -263,7 +263,7 @@ int wusbhc_chid_set(struct wusbhc *wusbh
+ {
+ int result = 0;
+
+- if (memcmp(chid, &wusb_ckhdid_zero, sizeof(chid)) == 0)
++ if (memcmp(chid, &wusb_ckhdid_zero, sizeof(*chid)) == 0)
+ chid = NULL;
+
+ mutex_lock(&wusbhc->mutex);
diff --git a/usb/usb-xhci-allow-allocation-of-commands-without-input-contexts.patch b/usb/usb-xhci-allow-allocation-of-commands-without-input-contexts.patch
new file mode 100644
index 00000000000000..e86d214e182ca0
--- /dev/null
+++ b/usb/usb-xhci-allow-allocation-of-commands-without-input-contexts.patch
@@ -0,0 +1,91 @@
+From sarah.a.sharp@linux.intel.com Tue Dec 22 11:47:29 2009
+From: Sarah Sharp <sarah.a.sharp@linux.intel.com>
+Date: Wed, 9 Dec 2009 15:59:03 -0800
+Subject: USB: xhci: Allow allocation of commands without input contexts.
+To: Greg KH <gregkh@suse.de>
+Cc: linux-usb@vger.kernel.org
+Message-ID: <20091209235903.GA19877@xanatos>
+Content-Disposition: inline
+
+
+The xhci_command structure is the basic structure for issuing commands to
+the xHCI hardware. It contains a struct completion (so that the issuing
+function can wait on the command), command status, and a input context
+that is used to pass information to the hardware. Not all commands need
+the input context, so make it optional to allocate. Allow
+xhci_free_container_ctx() to be passed a NULL input context, to make
+freeing the xhci_command structure simple.
+
+Signed-off-by: Sarah Sharp <sarah.a.sharp@linux.intel.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
+
+---
+ drivers/usb/host/xhci-hcd.c | 2 +-
+ drivers/usb/host/xhci-mem.c | 18 ++++++++++++------
+ drivers/usb/host/xhci.h | 3 ++-
+ 3 files changed, 15 insertions(+), 8 deletions(-)
+
+--- a/drivers/usb/host/xhci.h
++++ b/drivers/usb/host/xhci.h
+@@ -1237,7 +1237,8 @@ void xhci_free_or_cache_endpoint_ring(st
+ struct xhci_virt_device *virt_dev,
+ unsigned int ep_index);
+ struct xhci_command *xhci_alloc_command(struct xhci_hcd *xhci,
+- bool allocate_completion, gfp_t mem_flags);
++ bool allocate_in_ctx, bool allocate_completion,
++ gfp_t mem_flags);
+ void xhci_free_command(struct xhci_hcd *xhci,
+ struct xhci_command *command);
+
+--- a/drivers/usb/host/xhci-hcd.c
++++ b/drivers/usb/host/xhci-hcd.c
+@@ -1679,7 +1679,7 @@ int xhci_update_hub_device(struct usb_hc
+ xhci_warn(xhci, "Cannot update hub desc for unknown device.\n");
+ return -EINVAL;
+ }
+- config_cmd = xhci_alloc_command(xhci, true, mem_flags);
++ config_cmd = xhci_alloc_command(xhci, true, true, mem_flags);
+ if (!config_cmd) {
+ xhci_dbg(xhci, "Could not allocate xHCI command structure.\n");
+ return -ENOMEM;
+--- a/drivers/usb/host/xhci-mem.c
++++ b/drivers/usb/host/xhci-mem.c
+@@ -267,6 +267,8 @@ struct xhci_container_ctx *xhci_alloc_co
+ void xhci_free_container_ctx(struct xhci_hcd *xhci,
+ struct xhci_container_ctx *ctx)
+ {
++ if (!ctx)
++ return;
+ dma_pool_free(xhci->device_pool, ctx->bytes, ctx->dma);
+ kfree(ctx);
+ }
+@@ -844,7 +846,8 @@ static void scratchpad_free(struct xhci_
+ }
+
+ struct xhci_command *xhci_alloc_command(struct xhci_hcd *xhci,
+- bool allocate_completion, gfp_t mem_flags)
++ bool allocate_in_ctx, bool allocate_completion,
++ gfp_t mem_flags)
+ {
+ struct xhci_command *command;
+
+@@ -852,11 +855,14 @@ struct xhci_command *xhci_alloc_command(
+ if (!command)
+ return NULL;
+
+- command->in_ctx =
+- xhci_alloc_container_ctx(xhci, XHCI_CTX_TYPE_INPUT, mem_flags);
+- if (!command->in_ctx) {
+- kfree(command);
+- return NULL;
++ if (allocate_in_ctx) {
++ command->in_ctx =
++ xhci_alloc_container_ctx(xhci, XHCI_CTX_TYPE_INPUT,
++ mem_flags);
++ if (!command->in_ctx) {
++ kfree(command);
++ return NULL;
++ }
+ }
+
+ if (allocate_completion) {
diff --git a/usb/usb-xhci-allow-roothub-ports-to-be-disabled.patch b/usb/usb-xhci-allow-roothub-ports-to-be-disabled.patch
new file mode 100644
index 00000000000000..960d620c0c78ce
--- /dev/null
+++ b/usb/usb-xhci-allow-roothub-ports-to-be-disabled.patch
@@ -0,0 +1,66 @@
+From sarah.a.sharp@linux.intel.com Tue Dec 22 11:48:02 2009
+From: Sarah Sharp <sarah.a.sharp@linux.intel.com>
+Date: Wed, 9 Dec 2009 15:59:11 -0800
+Subject: USB: xhci: Allow roothub ports to be disabled.
+To: Greg KH <gregkh@suse.de>
+Cc: linux-usb@vger.kernel.org
+Message-ID: <20091209235911.GA19898@xanatos>
+
+
+Add the hub emulation code to allow ports on an xHCI root hub to be
+disabled. Add the code to clear the port enabled/disabled bit, and clear
+the port enabled/disabled change bit. Like EHCI, the port cannot be
+enabled by setting the port enabled/disabled bit. Instead, a port is
+enabled by the host controller after a reset.
+
+Signed-off-by: Sarah Sharp <sarah.a.sharp@linux.intel.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
+
+---
+ drivers/usb/host/xhci-hub.c | 18 ++++++++++++++++++
+ 1 file changed, 18 insertions(+)
+
+--- a/drivers/usb/host/xhci-hub.c
++++ b/drivers/usb/host/xhci-hub.c
+@@ -129,6 +129,16 @@ static u32 xhci_port_state_to_neutral(u3
+ return (state & XHCI_PORT_RO) | (state & XHCI_PORT_RWS);
+ }
+
++static void xhci_disable_port(struct xhci_hcd *xhci, u16 wIndex,
++ u32 __iomem *addr, u32 port_status)
++{
++ /* Write 1 to disable the port */
++ xhci_writel(xhci, port_status | PORT_PE, addr);
++ port_status = xhci_readl(xhci, addr);
++ xhci_dbg(xhci, "disable port, actual port %d status = 0x%x\n",
++ wIndex, port_status);
++}
++
+ static void xhci_clear_port_change_bit(struct xhci_hcd *xhci, u16 wValue,
+ u16 wIndex, u32 __iomem *addr, u32 port_status)
+ {
+@@ -148,6 +158,10 @@ static void xhci_clear_port_change_bit(s
+ status = PORT_OCC;
+ port_change_bit = "over-current";
+ break;
++ case USB_PORT_FEAT_C_ENABLE:
++ status = PORT_PEC;
++ port_change_bit = "enable/disable";
++ break;
+ default:
+ /* Should never happen */
+ return;
+@@ -260,9 +274,13 @@ int xhci_hub_control(struct usb_hcd *hcd
+ case USB_PORT_FEAT_C_RESET:
+ case USB_PORT_FEAT_C_CONNECTION:
+ case USB_PORT_FEAT_C_OVER_CURRENT:
++ case USB_PORT_FEAT_C_ENABLE:
+ xhci_clear_port_change_bit(xhci, wValue, wIndex,
+ addr, temp);
+ break;
++ case USB_PORT_FEAT_ENABLE:
++ xhci_disable_port(xhci, wIndex, addr, temp);
++ break;
+ default:
+ goto error;
+ }
diff --git a/usb/usb-xhci-fix-error-path-when-configuring-endpoints.patch b/usb/usb-xhci-fix-error-path-when-configuring-endpoints.patch
new file mode 100644
index 00000000000000..6434e9efc2db4f
--- /dev/null
+++ b/usb/usb-xhci-fix-error-path-when-configuring-endpoints.patch
@@ -0,0 +1,32 @@
+From sarah.a.sharp@linux.intel.com Tue Dec 22 11:46:54 2009
+From: Sarah Sharp <sarah.a.sharp@linux.intel.com>
+Date: Wed, 9 Dec 2009 15:58:58 -0800
+Subject: USB: xhci: Fix error path when configuring endpoints.
+To: Greg KH <gregkh@suse.de>
+Cc: linux-usb@vger.kernel.org
+Message-ID: <20091209235858.GA19863@xanatos>
+
+
+If we fail to queue an evaluate context command or a configure endpoint
+command to the command ring in xhci_configure_endpoint(), we need to
+remove the xhci_command structure from the device's command list before
+returning. If the command is left on the command list, it will sit there
+indefinitely, blocking commands submitted after this fails.
+
+Signed-off-by: Sarah Sharp <sarah.a.sharp@linux.intel.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
+---
+ drivers/usb/host/xhci-hcd.c | 2 ++
+ 1 file changed, 2 insertions(+)
+
+--- a/drivers/usb/host/xhci-hcd.c
++++ b/drivers/usb/host/xhci-hcd.c
+@@ -1181,6 +1181,8 @@ static int xhci_configure_endpoint(struc
+ ret = xhci_queue_evaluate_context(xhci, in_ctx->dma,
+ udev->slot_id);
+ if (ret < 0) {
++ if (command)
++ list_del(&command->cmd_list);
+ spin_unlock_irqrestore(&xhci->lock, flags);
+ xhci_dbg(xhci, "FIXME allocate a new ring segment\n");
+ return -ENOMEM;
diff --git a/usb/usb-xhci-no-gfp_kernel-in-block-error-handling.patch b/usb/usb-xhci-no-gfp_kernel-in-block-error-handling.patch
new file mode 100644
index 00000000000000..9e4ba203f1ba50
--- /dev/null
+++ b/usb/usb-xhci-no-gfp_kernel-in-block-error-handling.patch
@@ -0,0 +1,32 @@
+From oliver@neukum.org Tue Dec 22 11:56:48 2009
+From: Oliver Neukum <oliver@neukum.org>
+Date: Wed, 16 Dec 2009 19:43:59 +0100
+Subject: USB: xhci: No GFP_KERNEL in block error handling
+To: "Greg Kroah-Hartman" <gregkh@suse.de>, Sarah Sharp <sarah.a.sharp@linux.intel.com>, linux-usb@vger.kernel.org
+Message-ID: <200912161943.59710.oliver@neukum.org>
+
+
+From: Oliver Neukum <oliver@neukum.org>
+
+xhci_add_endpoint() is used in the reset path. It must
+use GFP_NOIO to avoid a possible deadlock.
+
+Signed-off-by: Oliver Neukum <oliver@neukum.org>
+Acked-by: Sarah Sharp <sarah.a.sharp@intel.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
+
+---
+ drivers/usb/host/xhci-hcd.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+--- a/drivers/usb/host/xhci-hcd.c
++++ b/drivers/usb/host/xhci-hcd.c
+@@ -1007,7 +1007,7 @@ int xhci_add_endpoint(struct usb_hcd *hc
+ * for usb_set_interface() and usb_set_configuration() claim).
+ */
+ if (xhci_endpoint_init(xhci, xhci->devs[udev->slot_id],
+- udev, ep, GFP_KERNEL) < 0) {
++ udev, ep, GFP_NOIO) < 0) {
+ dev_dbg(&udev->dev, "%s - could not initialize ep %#x\n",
+ __func__, ep->desc.bEndpointAddress);
+ return -ENOMEM;
diff --git a/usb/usb-xhci-notify-the-xhc-when-a-device-is-reset.patch b/usb/usb-xhci-notify-the-xhc-when-a-device-is-reset.patch
new file mode 100644
index 00000000000000..22643dfc2c0c93
--- /dev/null
+++ b/usb/usb-xhci-notify-the-xhc-when-a-device-is-reset.patch
@@ -0,0 +1,257 @@
+From sarah.a.sharp@linux.intel.com Tue Dec 22 11:48:14 2009
+From: Sarah Sharp <sarah.a.sharp@linux.intel.com>
+Date: Wed, 9 Dec 2009 15:59:13 -0800
+Subject: USB: xhci: Notify the xHC when a device is reset.
+To: Greg KH <gregkh@suse.de>
+Cc: linux-usb@vger.kernel.org, Oliver Neukum <oliver@neukum.org>
+Message-ID: <20091209235913.GA19905@xanatos>
+
+
+When a USB device is reset, the xHCI hardware must know, in order to match
+the device state and disable all endpoints except control endpoint 0.
+Issue a Reset Device command after a USB device is successfully reset.
+Wait on the command to finish, and then cache or free the disabled
+endpoint rings.
+
+There are four different USB device states that the xHCI hardware tracks:
+ - disabled/enabled - device connection has just been detected,
+ - default - the device has been reset and has an address of 0,
+ - addressed - the device has a non-zero address but no configuration has
+ been set,
+ - configured - a set configuration succeeded.
+
+The USB core may issue a port reset when a device is in any state, but the
+Reset Device command will fail for a 0.96 xHC if the device is not in the
+addressed or configured state. Don't consider this failure as an error,
+but don't free any endpoint rings if this command fails.
+
+A storage driver may request that the USB device be reset during error
+handling, so use GPF_NOIO instead of GPF_KERNEL while allocating memory
+for the Reset Device command.
+
+Signed-off-by: Sarah Sharp <sarah.a.sharp@linux.intel.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
+
+---
+ drivers/usb/host/xhci-dbg.c | 19 ++++++
+ drivers/usb/host/xhci-hcd.c | 125 +++++++++++++++++++++++++++++++++++++++++++
+ drivers/usb/host/xhci-ring.c | 19 ++++++
+ drivers/usb/host/xhci.h | 3 +
+ 4 files changed, 166 insertions(+)
+
+--- a/drivers/usb/host/xhci-dbg.c
++++ b/drivers/usb/host/xhci-dbg.c
+@@ -406,6 +406,25 @@ static void dbg_rsvd64(struct xhci_hcd *
+ }
+ }
+
++inline char *xhci_get_slot_state(struct xhci_hcd *xhci,
++ struct xhci_container_ctx *ctx)
++{
++ struct xhci_slot_ctx *slot_ctx = xhci_get_slot_ctx(xhci, ctx);
++
++ switch (GET_SLOT_STATE(slot_ctx->dev_state)) {
++ case 0:
++ return "enabled/disabled";
++ case 1:
++ return "default";
++ case 2:
++ return "addressed";
++ case 3:
++ return "configured";
++ default:
++ return "reserved";
++ }
++}
++
+ void xhci_dbg_slot_ctx(struct xhci_hcd *xhci, struct xhci_container_ctx *ctx)
+ {
+ /* Fields are 32 bits wide, DMA addresses are in bytes */
+--- a/drivers/usb/host/xhci.h
++++ b/drivers/usb/host/xhci.h
+@@ -1210,6 +1210,8 @@ void xhci_dbg_erst(struct xhci_hcd *xhci
+ void xhci_dbg_cmd_ptrs(struct xhci_hcd *xhci);
+ void xhci_dbg_ring_ptrs(struct xhci_hcd *xhci, struct xhci_ring *ring);
+ void xhci_dbg_ctx(struct xhci_hcd *xhci, struct xhci_container_ctx *ctx, unsigned int last_ep);
++inline char *xhci_get_slot_state(struct xhci_hcd *xhci,
++ struct xhci_container_ctx *ctx);
+
+ /* xHCI memory management */
+ void xhci_mem_cleanup(struct xhci_hcd *xhci);
+@@ -1298,6 +1300,7 @@ int xhci_queue_evaluate_context(struct x
+ u32 slot_id);
+ int xhci_queue_reset_ep(struct xhci_hcd *xhci, int slot_id,
+ unsigned int ep_index);
++int xhci_queue_reset_device(struct xhci_hcd *xhci, u32 slot_id);
+ void xhci_find_new_dequeue_state(struct xhci_hcd *xhci,
+ unsigned int slot_id, unsigned int ep_index,
+ struct xhci_td *cur_td, struct xhci_dequeue_state *state);
+--- a/drivers/usb/host/xhci-hcd.c
++++ b/drivers/usb/host/xhci-hcd.c
+@@ -1443,6 +1443,131 @@ void xhci_endpoint_reset(struct usb_hcd
+ }
+
+ /*
++ * This submits a Reset Device Command, which will set the device state to 0,
++ * set the device address to 0, and disable all the endpoints except the default
++ * control endpoint. The USB core should come back and call
++ * xhci_address_device(), and then re-set up the configuration. If this is
++ * called because of a usb_reset_and_verify_device(), then the old alternate
++ * settings will be re-installed through the normal bandwidth allocation
++ * functions.
++ *
++ * Wait for the Reset Device command to finish. Remove all structures
++ * associated with the endpoints that were disabled. Clear the input device
++ * structure? Cache the rings? Reset the control endpoint 0 max packet size?
++ */
++int xhci_reset_device(struct usb_hcd *hcd, struct usb_device *udev)
++{
++ int ret, i;
++ unsigned long flags;
++ struct xhci_hcd *xhci;
++ unsigned int slot_id;
++ struct xhci_virt_device *virt_dev;
++ struct xhci_command *reset_device_cmd;
++ int timeleft;
++ int last_freed_endpoint;
++
++ ret = xhci_check_args(hcd, udev, NULL, 0, __func__);
++ if (ret <= 0)
++ return ret;
++ xhci = hcd_to_xhci(hcd);
++ slot_id = udev->slot_id;
++ virt_dev = xhci->devs[slot_id];
++ if (!virt_dev) {
++ xhci_dbg(xhci, "%s called with invalid slot ID %u\n",
++ __func__, slot_id);
++ return -EINVAL;
++ }
++
++ xhci_dbg(xhci, "Resetting device with slot ID %u\n", slot_id);
++ /* Allocate the command structure that holds the struct completion.
++ * Assume we're in process context, since the normal device reset
++ * process has to wait for the device anyway. Storage devices are
++ * reset as part of error handling, so use GFP_NOIO instead of
++ * GFP_KERNEL.
++ */
++ reset_device_cmd = xhci_alloc_command(xhci, false, true, GFP_NOIO);
++ if (!reset_device_cmd) {
++ xhci_dbg(xhci, "Couldn't allocate command structure.\n");
++ return -ENOMEM;
++ }
++
++ /* Attempt to submit the Reset Device command to the command ring */
++ spin_lock_irqsave(&xhci->lock, flags);
++ reset_device_cmd->command_trb = xhci->cmd_ring->enqueue;
++ list_add_tail(&reset_device_cmd->cmd_list, &virt_dev->cmd_list);
++ ret = xhci_queue_reset_device(xhci, slot_id);
++ if (ret) {
++ xhci_dbg(xhci, "FIXME: allocate a command ring segment\n");
++ list_del(&reset_device_cmd->cmd_list);
++ spin_unlock_irqrestore(&xhci->lock, flags);
++ goto command_cleanup;
++ }
++ xhci_ring_cmd_db(xhci);
++ spin_unlock_irqrestore(&xhci->lock, flags);
++
++ /* Wait for the Reset Device command to finish */
++ timeleft = wait_for_completion_interruptible_timeout(
++ reset_device_cmd->completion,
++ USB_CTRL_SET_TIMEOUT);
++ if (timeleft <= 0) {
++ xhci_warn(xhci, "%s while waiting for reset device command\n",
++ timeleft == 0 ? "Timeout" : "Signal");
++ spin_lock_irqsave(&xhci->lock, flags);
++ /* The timeout might have raced with the event ring handler, so
++ * only delete from the list if the item isn't poisoned.
++ */
++ if (reset_device_cmd->cmd_list.next != LIST_POISON1)
++ list_del(&reset_device_cmd->cmd_list);
++ spin_unlock_irqrestore(&xhci->lock, flags);
++ ret = -ETIME;
++ goto command_cleanup;
++ }
++
++ /* The Reset Device command can't fail, according to the 0.95/0.96 spec,
++ * unless we tried to reset a slot ID that wasn't enabled,
++ * or the device wasn't in the addressed or configured state.
++ */
++ ret = reset_device_cmd->status;
++ switch (ret) {
++ case COMP_EBADSLT: /* 0.95 completion code for bad slot ID */
++ case COMP_CTX_STATE: /* 0.96 completion code for same thing */
++ xhci_info(xhci, "Can't reset device (slot ID %u) in %s state\n",
++ slot_id,
++ xhci_get_slot_state(xhci, virt_dev->out_ctx));
++ xhci_info(xhci, "Not freeing device rings.\n");
++ /* Don't treat this as an error. May change my mind later. */
++ ret = 0;
++ goto command_cleanup;
++ case COMP_SUCCESS:
++ xhci_dbg(xhci, "Successful reset device command.\n");
++ break;
++ default:
++ if (xhci_is_vendor_info_code(xhci, ret))
++ break;
++ xhci_warn(xhci, "Unknown completion code %u for "
++ "reset device command.\n", ret);
++ ret = -EINVAL;
++ goto command_cleanup;
++ }
++
++ /* Everything but endpoint 0 is disabled, so free or cache the rings. */
++ last_freed_endpoint = 1;
++ for (i = 1; i < 31; ++i) {
++ if (!virt_dev->eps[i].ring)
++ continue;
++ xhci_free_or_cache_endpoint_ring(xhci, virt_dev, i);
++ last_freed_endpoint = i;
++ }
++ xhci_dbg(xhci, "Output context after successful reset device cmd:\n");
++ xhci_dbg_ctx(xhci, virt_dev->out_ctx, last_freed_endpoint);
++ ret = 0;
++
++command_cleanup:
++ xhci_free_command(xhci, reset_device_cmd);
++ return ret;
++}
++
++/*
+ * At this point, the struct usb_device is about to go away, the device has
+ * disconnected, and all traffic has been stopped and the endpoints have been
+ * disabled. Free any HC data structures associated with that device.
+--- a/drivers/usb/host/xhci-ring.c
++++ b/drivers/usb/host/xhci-ring.c
+@@ -953,6 +953,17 @@ bandwidth_change:
+ case TRB_TYPE(TRB_RESET_EP):
+ handle_reset_ep_completion(xhci, event, xhci->cmd_ring->dequeue);
+ break;
++ case TRB_TYPE(TRB_RESET_DEV):
++ xhci_dbg(xhci, "Completed reset device command.\n");
++ slot_id = TRB_TO_SLOT_ID(
++ xhci->cmd_ring->dequeue->generic.field[3]);
++ virt_dev = xhci->devs[slot_id];
++ if (virt_dev)
++ handle_cmd_in_cmd_wait_list(xhci, virt_dev, event);
++ else
++ xhci_warn(xhci, "Reset device command completion "
++ "for disabled slot %u\n", slot_id);
++ break;
+ default:
+ /* Skip over unknown commands on the event ring */
+ xhci->error_bitmask |= 1 << 6;
+@@ -2189,6 +2200,14 @@ int xhci_queue_address_device(struct xhc
+ false);
+ }
+
++/* Queue a reset device command TRB */
++int xhci_queue_reset_device(struct xhci_hcd *xhci, u32 slot_id)
++{
++ return queue_command(xhci, 0, 0, 0,
++ TRB_TYPE(TRB_RESET_DEV) | SLOT_ID_FOR_TRB(slot_id),
++ false);
++}
++
+ /* Queue a configure endpoint command TRB */
+ int xhci_queue_configure_endpoint(struct xhci_hcd *xhci, dma_addr_t in_ctx_ptr,
+ u32 slot_id, bool command_must_succeed)
diff --git a/usb/usb-xhci-refactor-code-to-clear-port-change-bits.patch b/usb/usb-xhci-refactor-code-to-clear-port-change-bits.patch
new file mode 100644
index 00000000000000..5ffb6c477c2a80
--- /dev/null
+++ b/usb/usb-xhci-refactor-code-to-clear-port-change-bits.patch
@@ -0,0 +1,100 @@
+From sarah.a.sharp@linux.intel.com Tue Dec 22 11:47:50 2009
+From: Sarah Sharp <sarah.a.sharp@linux.intel.com>
+Date: Wed, 9 Dec 2009 15:59:08 -0800
+Subject: USB: xhci: Refactor code to clear port change bits.
+To: Greg KH <gregkh@suse.de>
+Cc: linux-usb@vger.kernel.org
+Message-ID: <20091209235908.GA19891@xanatos>
+Content-Disposition: inline
+
+
+Refactor the code to clear the port change bits in the port status
+register. All port status change bits are write one to clear.
+
+Remove a redundant port status read that was supposed to unblock any
+posted writes. We read the port after the write to get the updated status
+for debugging, so the port read after that is unnecessary.
+
+Signed-off-by: Sarah Sharp <sarah.a.sharp@linux.intel.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
+
+---
+ drivers/usb/host/xhci-hub.c | 47 +++++++++++++++++++++++++++++---------------
+ 1 file changed, 32 insertions(+), 15 deletions(-)
+
+--- a/drivers/usb/host/xhci-hub.c
++++ b/drivers/usb/host/xhci-hub.c
+@@ -129,6 +129,36 @@ static u32 xhci_port_state_to_neutral(u3
+ return (state & XHCI_PORT_RO) | (state & XHCI_PORT_RWS);
+ }
+
++static void xhci_clear_port_change_bit(struct xhci_hcd *xhci, u16 wValue,
++ u16 wIndex, u32 __iomem *addr, u32 port_status)
++{
++ char *port_change_bit;
++ u32 status;
++
++ switch (wValue) {
++ case USB_PORT_FEAT_C_RESET:
++ status = PORT_RC;
++ port_change_bit = "reset";
++ break;
++ case USB_PORT_FEAT_C_CONNECTION:
++ status = PORT_CSC;
++ port_change_bit = "connect";
++ break;
++ case USB_PORT_FEAT_C_OVER_CURRENT:
++ status = PORT_OCC;
++ port_change_bit = "over-current";
++ break;
++ default:
++ /* Should never happen */
++ return;
++ }
++ /* Change bits are all write 1 to clear */
++ xhci_writel(xhci, port_status | status, addr);
++ port_status = xhci_readl(xhci, addr);
++ xhci_dbg(xhci, "clear port %s change, actual port %d status = 0x%x\n",
++ port_change_bit, wIndex, port_status);
++}
++
+ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
+ u16 wIndex, char *buf, u16 wLength)
+ {
+@@ -138,7 +168,6 @@ int xhci_hub_control(struct usb_hcd *hcd
+ u32 temp, status;
+ int retval = 0;
+ u32 __iomem *addr;
+- char *port_change_bit;
+
+ ports = HCS_MAX_PORTS(xhci->hcs_params1);
+
+@@ -229,26 +258,14 @@ int xhci_hub_control(struct usb_hcd *hcd
+ temp = xhci_port_state_to_neutral(temp);
+ switch (wValue) {
+ case USB_PORT_FEAT_C_RESET:
+- status = PORT_RC;
+- port_change_bit = "reset";
+- break;
+ case USB_PORT_FEAT_C_CONNECTION:
+- status = PORT_CSC;
+- port_change_bit = "connect";
+- break;
+ case USB_PORT_FEAT_C_OVER_CURRENT:
+- status = PORT_OCC;
+- port_change_bit = "over-current";
++ xhci_clear_port_change_bit(xhci, wValue, wIndex,
++ addr, temp);
+ break;
+ default:
+ goto error;
+ }
+- /* Change bits are all write 1 to clear */
+- xhci_writel(xhci, temp | status, addr);
+- temp = xhci_readl(xhci, addr);
+- xhci_dbg(xhci, "clear port %s change, actual port %d status = 0x%x\n",
+- port_change_bit, wIndex, temp);
+- temp = xhci_readl(xhci, addr); /* unblock any posted writes */
+ break;
+ default:
+ error:
diff --git a/usb/usb-xhci-refactor-code-to-free-or-cache-endpoint-rings.patch b/usb/usb-xhci-refactor-code-to-free-or-cache-endpoint-rings.patch
new file mode 100644
index 00000000000000..c9270247cc2fdd
--- /dev/null
+++ b/usb/usb-xhci-refactor-code-to-free-or-cache-endpoint-rings.patch
@@ -0,0 +1,103 @@
+From sarah.a.sharp@linux.intel.com Tue Dec 22 11:47:06 2009
+From: Sarah Sharp <sarah.a.sharp@linux.intel.com>
+Date: Wed, 9 Dec 2009 15:59:01 -0800
+Subject: USB: xhci: Refactor code to free or cache endpoint rings.
+To: Greg KH <gregkh@suse.de>
+Cc: linux-usb@vger.kernel.org
+Message-ID: <20091209235901.GA19870@xanatos>
+Content-Disposition: inline
+
+
+Refactor out the code to cache or free endpoint rings from recently
+dropped or disabled endpoints. This code will be used by a new function
+to reset a device and disable all endpoints except control endpoint 0.
+
+Signed-off-by: Sarah Sharp <sarah.a.sharp@linux.intel.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
+
+---
+ drivers/usb/host/xhci-hcd.c | 19 +------------------
+ drivers/usb/host/xhci-mem.c | 25 +++++++++++++++++++++++++
+ drivers/usb/host/xhci.h | 3 +++
+ 3 files changed, 29 insertions(+), 18 deletions(-)
+
+--- a/drivers/usb/host/xhci.h
++++ b/drivers/usb/host/xhci.h
+@@ -1233,6 +1233,9 @@ int xhci_endpoint_init(struct xhci_hcd *
+ struct usb_device *udev, struct usb_host_endpoint *ep,
+ gfp_t mem_flags);
+ void xhci_ring_free(struct xhci_hcd *xhci, struct xhci_ring *ring);
++void xhci_free_or_cache_endpoint_ring(struct xhci_hcd *xhci,
++ struct xhci_virt_device *virt_dev,
++ unsigned int ep_index);
+ struct xhci_command *xhci_alloc_command(struct xhci_hcd *xhci,
+ bool allocate_completion, gfp_t mem_flags);
+ void xhci_free_command(struct xhci_hcd *xhci,
+--- a/drivers/usb/host/xhci-hcd.c
++++ b/drivers/usb/host/xhci-hcd.c
+@@ -1266,30 +1266,13 @@ int xhci_check_bandwidth(struct usb_hcd
+ xhci_zero_in_ctx(xhci, virt_dev);
+ /* Install new rings and free or cache any old rings */
+ for (i = 1; i < 31; ++i) {
+- int rings_cached;
+-
+ if (!virt_dev->eps[i].new_ring)
+ continue;
+ /* Only cache or free the old ring if it exists.
+ * It may not if this is the first add of an endpoint.
+ */
+ if (virt_dev->eps[i].ring) {
+- rings_cached = virt_dev->num_rings_cached;
+- if (rings_cached < XHCI_MAX_RINGS_CACHED) {
+- virt_dev->num_rings_cached++;
+- rings_cached = virt_dev->num_rings_cached;
+- virt_dev->ring_cache[rings_cached] =
+- virt_dev->eps[i].ring;
+- xhci_dbg(xhci, "Cached old ring, "
+- "%d ring%s cached\n",
+- rings_cached,
+- (rings_cached > 1) ? "s" : "");
+- } else {
+- xhci_ring_free(xhci, virt_dev->eps[i].ring);
+- xhci_dbg(xhci, "Ring cache full (%d rings), "
+- "freeing ring\n",
+- virt_dev->num_rings_cached);
+- }
++ xhci_free_or_cache_endpoint_ring(xhci, virt_dev, i);
+ }
+ virt_dev->eps[i].ring = virt_dev->eps[i].new_ring;
+ virt_dev->eps[i].new_ring = NULL;
+--- a/drivers/usb/host/xhci-mem.c
++++ b/drivers/usb/host/xhci-mem.c
+@@ -198,6 +198,31 @@ fail:
+ return 0;
+ }
+
++void xhci_free_or_cache_endpoint_ring(struct xhci_hcd *xhci,
++ struct xhci_virt_device *virt_dev,
++ unsigned int ep_index)
++{
++ int rings_cached;
++
++ rings_cached = virt_dev->num_rings_cached;
++ if (rings_cached < XHCI_MAX_RINGS_CACHED) {
++ virt_dev->num_rings_cached++;
++ rings_cached = virt_dev->num_rings_cached;
++ virt_dev->ring_cache[rings_cached] =
++ virt_dev->eps[ep_index].ring;
++ xhci_dbg(xhci, "Cached old ring, "
++ "%d ring%s cached\n",
++ rings_cached,
++ (rings_cached > 1) ? "s" : "");
++ } else {
++ xhci_ring_free(xhci, virt_dev->eps[ep_index].ring);
++ xhci_dbg(xhci, "Ring cache full (%d rings), "
++ "freeing ring\n",
++ virt_dev->num_rings_cached);
++ }
++ virt_dev->eps[ep_index].ring = NULL;
++}
++
+ /* Zero an endpoint ring (except for link TRBs) and move the enqueue and dequeue
+ * pointers to the beginning of the ring.
+ */
diff --git a/usb/usb-xhci-refactor-test-for-vendor-specific-completion-codes.patch b/usb/usb-xhci-refactor-test-for-vendor-specific-completion-codes.patch
new file mode 100644
index 00000000000000..7e952e2f3a2bb6
--- /dev/null
+++ b/usb/usb-xhci-refactor-test-for-vendor-specific-completion-codes.patch
@@ -0,0 +1,70 @@
+From sarah.a.sharp@linux.intel.com Tue Dec 22 11:47:40 2009
+From: Sarah Sharp <sarah.a.sharp@linux.intel.com>
+Date: Wed, 9 Dec 2009 15:59:06 -0800
+Subject: USB: xhci: Refactor test for vendor-specific completion codes.
+To: Greg KH <gregkh@suse.de>
+Cc: linux-usb@vger.kernel.org
+Message-ID: <20091209235906.GA19884@xanatos>
+Content-Disposition: inline
+
+
+All commands that can be issued to the xHCI hardware can come back with
+vendor-specific "informational" completion codes. These are to be treated
+like a successful completion code. Refactor out the code to test for the
+range of these codes and print debugging messages.
+
+Signed-off-by: Sarah Sharp <sarah.a.sharp@linux.intel.com>
+Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
+---
+ drivers/usb/host/xhci-ring.c | 22 +++++++++++++++-------
+ drivers/usb/host/xhci.h | 1 +
+ 2 files changed, 16 insertions(+), 7 deletions(-)
+
+--- a/drivers/usb/host/xhci.h
++++ b/drivers/usb/host/xhci.h
+@@ -1276,6 +1276,7 @@ dma_addr_t xhci_trb_virt_to_dma(struct x
+ struct xhci_segment *trb_in_td(struct xhci_segment *start_seg,
+ union xhci_trb *start_trb, union xhci_trb *end_trb,
+ dma_addr_t suspect_dma);
++int xhci_is_vendor_info_code(struct xhci_hcd *xhci, unsigned int trb_comp_code);
+ void xhci_ring_cmd_db(struct xhci_hcd *xhci);
+ void *xhci_setup_one_noop(struct xhci_hcd *xhci);
+ void xhci_handle_event(struct xhci_hcd *xhci);
+--- a/drivers/usb/host/xhci-ring.c
++++ b/drivers/usb/host/xhci-ring.c
+@@ -1080,6 +1080,20 @@ static int xhci_requires_manual_halt_cle
+ return 0;
+ }
+
++int xhci_is_vendor_info_code(struct xhci_hcd *xhci, unsigned int trb_comp_code)
++{
++ if (trb_comp_code >= 224 && trb_comp_code <= 255) {
++ /* Vendor defined "informational" completion code,
++ * treat as not-an-error.
++ */
++ xhci_dbg(xhci, "Vendor defined info completion code %u\n",
++ trb_comp_code);
++ xhci_dbg(xhci, "Treating code as success.\n");
++ return 1;
++ }
++ return 0;
++}
++
+ /*
+ * If this function returns an error condition, it means it got a Transfer
+ * event with a corrupted Slot ID, Endpoint ID, or TRB DMA address.
+@@ -1196,13 +1210,7 @@ static int handle_tx_event(struct xhci_h
+ status = -ENOSR;
+ break;
+ default:
+- if (trb_comp_code >= 224 && trb_comp_code <= 255) {
+- /* Vendor defined "informational" completion code,
+- * treat as not-an-error.
+- */
+- xhci_dbg(xhci, "Vendor defined info completion code %u\n",
+- trb_comp_code);
+- xhci_dbg(xhci, "Treating code as success.\n");
++ if (xhci_is_vendor_info_code(xhci, trb_comp_code)) {
+ status = 0;
+ break;
+ }