diff options
| author | Greg Kroah-Hartman <gregkh@suse.de> | 2009-12-22 12:22:11 -0800 |
|---|---|---|
| committer | Greg Kroah-Hartman <gregkh@suse.de> | 2009-12-22 12:22:11 -0800 |
| commit | 52a248c714fb17267c89520a4a854b0f65de998c (patch) | |
| tree | b08803dad70c8bfeba8a20e4c06b24ea6ce5cf9d /usb | |
| parent | ae75e9fa49d0b2a2a776794dad9e420e7bc269f2 (diff) | |
| download | patches-52a248c714fb17267c89520a4a854b0f65de998c.tar.gz | |
huge catchup on pending patches
Diffstat (limited to 'usb')
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; + } |
