From c2f6595fbdb408d3d6850cfae590c8fa93e27399 Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Wed, 18 Nov 2009 11:37:15 -0500 Subject: USB: EHCI: don't send Clear-TT-Buffer following a STALL This patch (as1304) fixes a regression in ehci-hcd. Evidently some hubs don't handle Clear-TT-Buffer requests correctly, so we should avoid sending them when they don't appear to be absolutely necessary. The reported symptom is that output on a downstream audio device cuts out because the hub stops relaying isochronous packets. The patch prevents Clear-TT-Buffer requests from being sent following a STALL handshake. In theory a STALL indicates either that the downstream device sent a STALL or that no matching TT buffer could be found. In either case, the transfer is completed and the TT buffer does not remain busy, so it doesn't need to be cleared. Also, the patch fixes a minor flaw in the code that actually sends the Clear-TT-Buffer requests. Although the pipe direction isn't really used for control transfers, it should be a Send rather than a Receive. Signed-off-by: Alan Stern Reported-by: Javier Kohen CC: David Brownell Cc: stable Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/hub.c | 2 +- drivers/usb/host/ehci-q.c | 16 ++++++++++++++-- 2 files changed, 15 insertions(+), 3 deletions(-) diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c index 5ce839137ad..0f857e64505 100644 --- a/drivers/usb/core/hub.c +++ b/drivers/usb/core/hub.c @@ -444,7 +444,7 @@ resubmit: static inline int hub_clear_tt_buffer (struct usb_device *hdev, u16 devinfo, u16 tt) { - return usb_control_msg(hdev, usb_rcvctrlpipe(hdev, 0), + return usb_control_msg(hdev, usb_sndctrlpipe(hdev, 0), HUB_CLEAR_TT_BUFFER, USB_RT_PORT, devinfo, tt, NULL, 0, 1000); } diff --git a/drivers/usb/host/ehci-q.c b/drivers/usb/host/ehci-q.c index 00ad9ce392e..139a2cc3f64 100644 --- a/drivers/usb/host/ehci-q.c +++ b/drivers/usb/host/ehci-q.c @@ -487,8 +487,20 @@ halt: * we must clear the TT buffer (11.17.5). */ if (unlikely(last_status != -EINPROGRESS && - last_status != -EREMOTEIO)) - ehci_clear_tt_buffer(ehci, qh, urb, token); + last_status != -EREMOTEIO)) { + /* The TT's in some hubs malfunction when they + * receive this request following a STALL (they + * stop sending isochronous packets). Since a + * STALL can't leave the TT buffer in a busy + * state (if you believe Figures 11-48 - 11-51 + * in the USB 2.0 spec), we won't clear the TT + * buffer in this case. Strictly speaking this + * is a violation of the spec. + */ + if (last_status != -EPIPE) + ehci_clear_tt_buffer(ehci, qh, urb, + token); + } } /* if we're removing something not at the queue head, -- cgit v1.2.3 From cea83241b3a84499c4f9b12f8288f787e7aa6383 Mon Sep 17 00:00:00 2001 From: Sergei Shtylyov Date: Wed, 18 Nov 2009 22:51:18 +0300 Subject: USB: musb_gadget: fix STALL handling The driver incorrectly cancels the mass-storage device CSW request (which leads to device reset) due to giving back URB at the head of endpoint's queue after sending each STALL handshake; stop doing that and start checking for the queue being non-empty before stalling an endpoint and disallowing stall in such case in musb_gadget_set_halt() like the other gadget drivers do. Moreover, the driver starts Rx request despite of the endpoint being halted -- fix this by moving the SendStall bit check from musb_g_rx() to rxstate(). And we also sometimes get into rxstate() with DMA still active after clearing an endpoint's halt (not clear why), so bail out in this case, similarly to what txstate() does... While at it, also do the following changes : - in musb_gadget_set_halt(), remove pointless Tx FIFO flushing (the driver does not allow stalling with non-empty Tx FIFO anyway); - in rxstate(), stop pointlessly zeroing the 'csr' variable; - in musb_gadget_set_halt(), move the 'done' label to a more proper place; - in musb_g_rx(), eliminate the 'done' label completely... Signed-off-by: Sergei Shtylyov Cc: Signed-off-by: Greg Kroah-Hartman --- drivers/usb/musb/musb_gadget.c | 79 ++++++++++++++++++------------------------ 1 file changed, 34 insertions(+), 45 deletions(-) diff --git a/drivers/usb/musb/musb_gadget.c b/drivers/usb/musb/musb_gadget.c index 8b3c4e2ed7b..74073f9a43f 100644 --- a/drivers/usb/musb/musb_gadget.c +++ b/drivers/usb/musb/musb_gadget.c @@ -4,6 +4,7 @@ * Copyright 2005 Mentor Graphics Corporation * Copyright (C) 2005-2006 by Texas Instruments * Copyright (C) 2006-2007 Nokia Corporation + * Copyright (C) 2009 MontaVista Software, Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -436,14 +437,6 @@ void musb_g_tx(struct musb *musb, u8 epnum) csr |= MUSB_TXCSR_P_WZC_BITS; csr &= ~MUSB_TXCSR_P_SENTSTALL; musb_writew(epio, MUSB_TXCSR, csr); - if (dma_channel_status(dma) == MUSB_DMA_STATUS_BUSY) { - dma->status = MUSB_DMA_STATUS_CORE_ABORT; - musb->dma_controller->channel_abort(dma); - } - - if (request) - musb_g_giveback(musb_ep, request, -EPIPE); - break; } @@ -582,15 +575,25 @@ void musb_g_tx(struct musb *musb, u8 epnum) */ static void rxstate(struct musb *musb, struct musb_request *req) { - u16 csr = 0; const u8 epnum = req->epnum; struct usb_request *request = &req->request; struct musb_ep *musb_ep = &musb->endpoints[epnum].ep_out; void __iomem *epio = musb->endpoints[epnum].regs; unsigned fifo_count = 0; u16 len = musb_ep->packet_sz; + u16 csr = musb_readw(epio, MUSB_RXCSR); - csr = musb_readw(epio, MUSB_RXCSR); + /* We shouldn't get here while DMA is active, but we do... */ + if (dma_channel_status(musb_ep->dma) == MUSB_DMA_STATUS_BUSY) { + DBG(4, "DMA pending...\n"); + return; + } + + if (csr & MUSB_RXCSR_P_SENDSTALL) { + DBG(5, "%s stalling, RXCSR %04x\n", + musb_ep->end_point.name, csr); + return; + } if (is_cppi_enabled() && musb_ep->dma) { struct dma_controller *c = musb->dma_controller; @@ -761,19 +764,10 @@ void musb_g_rx(struct musb *musb, u8 epnum) csr, dma ? " (dma)" : "", request); if (csr & MUSB_RXCSR_P_SENTSTALL) { - if (dma_channel_status(dma) == MUSB_DMA_STATUS_BUSY) { - dma->status = MUSB_DMA_STATUS_CORE_ABORT; - (void) musb->dma_controller->channel_abort(dma); - request->actual += musb_ep->dma->actual_len; - } - csr |= MUSB_RXCSR_P_WZC_BITS; csr &= ~MUSB_RXCSR_P_SENTSTALL; musb_writew(epio, MUSB_RXCSR, csr); - - if (request) - musb_g_giveback(musb_ep, request, -EPIPE); - goto done; + return; } if (csr & MUSB_RXCSR_P_OVERRUN) { @@ -795,7 +789,7 @@ void musb_g_rx(struct musb *musb, u8 epnum) DBG((csr & MUSB_RXCSR_DMAENAB) ? 4 : 1, "%s busy, csr %04x\n", musb_ep->end_point.name, csr); - goto done; + return; } if (dma && (csr & MUSB_RXCSR_DMAENAB)) { @@ -826,22 +820,15 @@ void musb_g_rx(struct musb *musb, u8 epnum) if ((request->actual < request->length) && (musb_ep->dma->actual_len == musb_ep->packet_sz)) - goto done; + return; #endif musb_g_giveback(musb_ep, request, 0); request = next_request(musb_ep); if (!request) - goto done; - - /* don't start more i/o till the stall clears */ - musb_ep_select(mbase, epnum); - csr = musb_readw(epio, MUSB_RXCSR); - if (csr & MUSB_RXCSR_P_SENDSTALL) - goto done; + return; } - /* analyze request if the ep is hot */ if (request) rxstate(musb, to_musb_request(request)); @@ -849,8 +836,6 @@ void musb_g_rx(struct musb *musb, u8 epnum) DBG(3, "packet waiting for %s%s request\n", musb_ep->desc ? "" : "inactive ", musb_ep->end_point.name); - -done: return; } @@ -1244,7 +1229,7 @@ int musb_gadget_set_halt(struct usb_ep *ep, int value) void __iomem *mbase; unsigned long flags; u16 csr; - struct musb_request *request = NULL; + struct musb_request *request; int status = 0; if (!ep) @@ -1260,24 +1245,29 @@ int musb_gadget_set_halt(struct usb_ep *ep, int value) musb_ep_select(mbase, epnum); - /* cannot portably stall with non-empty FIFO */ request = to_musb_request(next_request(musb_ep)); - if (value && musb_ep->is_in) { - csr = musb_readw(epio, MUSB_TXCSR); - if (csr & MUSB_TXCSR_FIFONOTEMPTY) { - DBG(3, "%s fifo busy, cannot halt\n", ep->name); - spin_unlock_irqrestore(&musb->lock, flags); - return -EAGAIN; + if (value) { + if (request) { + DBG(3, "request in progress, cannot halt %s\n", + ep->name); + status = -EAGAIN; + goto done; + } + /* Cannot portably stall with non-empty FIFO */ + if (musb_ep->is_in) { + csr = musb_readw(epio, MUSB_TXCSR); + if (csr & MUSB_TXCSR_FIFONOTEMPTY) { + DBG(3, "FIFO busy, cannot halt %s\n", ep->name); + status = -EAGAIN; + goto done; + } } - } /* set/clear the stall and toggle bits */ DBG(2, "%s: %s stall\n", ep->name, value ? "set" : "clear"); if (musb_ep->is_in) { csr = musb_readw(epio, MUSB_TXCSR); - if (csr & MUSB_TXCSR_FIFONOTEMPTY) - csr |= MUSB_TXCSR_FLUSHFIFO; csr |= MUSB_TXCSR_P_WZC_BITS | MUSB_TXCSR_CLRDATATOG; if (value) @@ -1300,14 +1290,13 @@ int musb_gadget_set_halt(struct usb_ep *ep, int value) musb_writew(epio, MUSB_RXCSR, csr); } -done: - /* maybe start the first request in the queue */ if (!musb_ep->busy && !value && request) { DBG(3, "restarting the request\n"); musb_ep_restart(musb, request); } +done: spin_unlock_irqrestore(&musb->lock, flags); return status; } -- cgit v1.2.3 From 0de6ab8b91f2e1e8e7fc66a8b5c5e8ca82ea16b7 Mon Sep 17 00:00:00 2001 From: "Eric W. Biederman" Date: Tue, 17 Nov 2009 19:10:48 -0800 Subject: USB: ftdi_sio: Keep going when write errors are encountered. The use of urb->actual_length to update tx_outstanding_bytes implicitly assumes that the number of bytes actually written is the same as the number of bytes we tried to write. On error that assumption is violated so just use transfer_buffer_length the number of bytes we intended to write to the device. If an error occurs we need to fall through and call usb_serial_port_softint to wake up processes waiting in tty_wait_until_sent. Signed-off-by: Eric W. Biederman Cc: stable Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/ftdi_sio.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/usb/serial/ftdi_sio.c b/drivers/usb/serial/ftdi_sio.c index 9c60d6d4908..ebcc6d0e2e9 100644 --- a/drivers/usb/serial/ftdi_sio.c +++ b/drivers/usb/serial/ftdi_sio.c @@ -1937,7 +1937,7 @@ static void ftdi_write_bulk_callback(struct urb *urb) return; } /* account for transferred data */ - countback = urb->actual_length; + countback = urb->transfer_buffer_length; data_offset = priv->write_offset; if (data_offset > 0) { /* Subtract the control bytes */ @@ -1950,7 +1950,6 @@ static void ftdi_write_bulk_callback(struct urb *urb) if (status) { dbg("nonzero write bulk status received: %d", status); - return; } usb_serial_port_softint(port); -- cgit v1.2.3 From c5deb832d7a3f9618b09e6eeaa91a1a845c90c65 Mon Sep 17 00:00:00 2001 From: Thomas Dahlmann Date: Tue, 17 Nov 2009 14:18:27 -0800 Subject: usb: amd5536udc: fixed shared interrupt bug and warning oops - fixed shared interrupt bug reported by Vadim Lobanov - fixed possible warning oops on driver unload when connected - prevent interrupt flood in PIO mode ("modprobe amd5536udc use_dma=0") when using gadget ether Signed-off-by: Thomas Dahlmann Cc: Robert Richter Cc: David Brownell Cc: stable Signed-off-by: Andrew Morton Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/amd5536udc.c | 49 +++++++++++++++++++++++++---------------- 1 file changed, 30 insertions(+), 19 deletions(-) diff --git a/drivers/usb/gadget/amd5536udc.c b/drivers/usb/gadget/amd5536udc.c index d5b65962dd3..731150d4b1d 100644 --- a/drivers/usb/gadget/amd5536udc.c +++ b/drivers/usb/gadget/amd5536udc.c @@ -1213,7 +1213,12 @@ udc_queue(struct usb_ep *usbep, struct usb_request *usbreq, gfp_t gfp) tmp &= AMD_UNMASK_BIT(ep->num); writel(tmp, &dev->regs->ep_irqmsk); } - } + } else if (ep->in) { + /* enable ep irq */ + tmp = readl(&dev->regs->ep_irqmsk); + tmp &= AMD_UNMASK_BIT(ep->num); + writel(tmp, &dev->regs->ep_irqmsk); + } } else if (ep->dma) { @@ -2005,18 +2010,17 @@ __acquires(dev->lock) { int tmp; - /* empty queues and init hardware */ - udc_basic_init(dev); - for (tmp = 0; tmp < UDC_EP_NUM; tmp++) { - empty_req_queue(&dev->ep[tmp]); - } - if (dev->gadget.speed != USB_SPEED_UNKNOWN) { spin_unlock(&dev->lock); driver->disconnect(&dev->gadget); spin_lock(&dev->lock); } - /* init */ + + /* empty queues and init hardware */ + udc_basic_init(dev); + for (tmp = 0; tmp < UDC_EP_NUM; tmp++) + empty_req_queue(&dev->ep[tmp]); + udc_setup_endpoints(dev); } @@ -2472,6 +2476,13 @@ static irqreturn_t udc_data_in_isr(struct udc *dev, int ep_ix) } } + } else if (!use_dma && ep->in) { + /* disable interrupt */ + tmp = readl( + &dev->regs->ep_irqmsk); + tmp |= AMD_BIT(ep->num); + writel(tmp, + &dev->regs->ep_irqmsk); } } /* clear status bits */ @@ -3279,6 +3290,17 @@ static int udc_pci_probe( goto finished; } + spin_lock_init(&dev->lock); + /* udc csr registers base */ + dev->csr = dev->virt_addr + UDC_CSR_ADDR; + /* dev registers base */ + dev->regs = dev->virt_addr + UDC_DEVCFG_ADDR; + /* ep registers base */ + dev->ep_regs = dev->virt_addr + UDC_EPREGS_ADDR; + /* fifo's base */ + dev->rxfifo = (u32 __iomem *)(dev->virt_addr + UDC_RXFIFO_ADDR); + dev->txfifo = (u32 __iomem *)(dev->virt_addr + UDC_TXFIFO_ADDR); + if (request_irq(pdev->irq, udc_irq, IRQF_SHARED, name, dev) != 0) { dev_dbg(&dev->pdev->dev, "request_irq(%d) fail\n", pdev->irq); kfree(dev); @@ -3331,7 +3353,6 @@ static int udc_probe(struct udc *dev) udc_pollstall_timer.data = 0; /* device struct setup */ - spin_lock_init(&dev->lock); dev->gadget.ops = &udc_ops; dev_set_name(&dev->gadget.dev, "gadget"); @@ -3340,16 +3361,6 @@ static int udc_probe(struct udc *dev) dev->gadget.name = name; dev->gadget.is_dualspeed = 1; - /* udc csr registers base */ - dev->csr = dev->virt_addr + UDC_CSR_ADDR; - /* dev registers base */ - dev->regs = dev->virt_addr + UDC_DEVCFG_ADDR; - /* ep registers base */ - dev->ep_regs = dev->virt_addr + UDC_EPREGS_ADDR; - /* fifo's base */ - dev->rxfifo = (u32 __iomem *)(dev->virt_addr + UDC_RXFIFO_ADDR); - dev->txfifo = (u32 __iomem *)(dev->virt_addr + UDC_TXFIFO_ADDR); - /* init registers, interrupts, ... */ startup_registers(dev); -- cgit v1.2.3 From 1230435c258e34b47ab7adc3db572c88a284234a Mon Sep 17 00:00:00 2001 From: Ajay Kumar Gupta Date: Tue, 17 Nov 2009 15:22:54 +0530 Subject: USB: musb: Remove unwanted message in boot log Removes below unnecessary log of almost 28 lines during boot. musb_hdrc: hw_ep 0shared, max 64 musb_hdrc: hw_ep 1tx, max 512 musb_hdrc: hw_ep 1rx, max 512 ... ... musb_hdrc: hw_ep 13shared, max 4096 musb_hdrc: hw_ep 14shared, max 1024 musb_hdrc: hw_ep 15shared, max 1024 Signed-off-by: Ajay Kumar Gupta Acked-by: Felipe Balbi Signed-off-by: Greg Kroah-Hartman --- drivers/usb/musb/musb_core.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/usb/musb/musb_core.c b/drivers/usb/musb/musb_core.c index 3a61ddb62bd..547e0e39072 100644 --- a/drivers/usb/musb/musb_core.c +++ b/drivers/usb/musb/musb_core.c @@ -1450,7 +1450,7 @@ static int __init musb_core_init(u16 musb_type, struct musb *musb) #endif if (hw_ep->max_packet_sz_tx) { - printk(KERN_DEBUG + DBG(1, "%s: hw_ep %d%s, %smax %d\n", musb_driver_name, i, hw_ep->is_shared_fifo ? "shared" : "tx", @@ -1459,7 +1459,7 @@ static int __init musb_core_init(u16 musb_type, struct musb *musb) hw_ep->max_packet_sz_tx); } if (hw_ep->max_packet_sz_rx && !hw_ep->is_shared_fifo) { - printk(KERN_DEBUG + DBG(1, "%s: hw_ep %d%s, %smax %d\n", musb_driver_name, i, "rx", -- cgit v1.2.3 From dfeffa531ccf9c31f2f55df6d7ca86eec92142df Mon Sep 17 00:00:00 2001 From: Ajay Kumar Gupta Date: Tue, 17 Nov 2009 15:22:55 +0530 Subject: USB: musb: fix ISOC Tx programming for CPPI DMAs Isochronous Tx DMA is getting programmed but never getting started for CPPI and TUSB DMAs and thus Isochronous Tx doesn't work. Fixing it by starting DMAs using musb_h_tx_dma_start(). Signed-off-by: Swaminathan S Signed-off-by: Babu Ravi Signed-off-by: Ajay Kumar Gupta Signed-off-by: Greg Kroah-Hartman --- drivers/usb/musb/musb_host.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/usb/musb/musb_host.c b/drivers/usb/musb/musb_host.c index cf94511485f..e3ab40a966e 100644 --- a/drivers/usb/musb/musb_host.c +++ b/drivers/usb/musb/musb_host.c @@ -1301,8 +1301,11 @@ void musb_host_tx(struct musb *musb, u8 epnum) return; } else if (usb_pipeisoc(pipe) && dma) { if (musb_tx_dma_program(musb->dma_controller, hw_ep, qh, urb, - offset, length)) + offset, length)) { + if (is_cppi_enabled() || tusb_dma_omap()) + musb_h_tx_dma_start(hw_ep); return; + } } else if (tx_csr & MUSB_TXCSR_DMAENAB) { DBG(1, "not complete, but DMA enabled?\n"); return; -- cgit v1.2.3 From 5542bc2ac7b52c021fc9c7a96329955491b7e763 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Gl=C3=B6ckner?= Date: Tue, 17 Nov 2009 15:22:56 +0530 Subject: USB: musb: respect usb_request->zero in control requests MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In gadget mode the answer to a control request should be followed by a zero-length packet if the amount transferred is an exact multiple of the endpoint's packet size and the requests has its "zero" flag set. This patch prevents the request from being immediately removed from the queue when a control IN transfer ends on a full packet and "zero" is set. The next time ep0_txstate is entered, a zero-length packet is queued and the request is removed as fifo_count is 0. Signed-off-by: Daniel Glöckner Signed-off-by: Ajay Kumar Gupta Signed-off-by: Greg Kroah-Hartman --- drivers/usb/musb/musb_gadget_ep0.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/usb/musb/musb_gadget_ep0.c b/drivers/usb/musb/musb_gadget_ep0.c index 7a6778675ad..522efb31b56 100644 --- a/drivers/usb/musb/musb_gadget_ep0.c +++ b/drivers/usb/musb/musb_gadget_ep0.c @@ -511,7 +511,8 @@ static void ep0_txstate(struct musb *musb) /* update the flags */ if (fifo_count < MUSB_MAX_END0_PACKET - || request->actual == request->length) { + || (request->actual == request->length + && !request->zero)) { musb->ep0_state = MUSB_EP0_STAGE_STATUSOUT; csr |= MUSB_CSR0_P_DATAEND; } else -- cgit v1.2.3 From 8d6499e5bde91ad05dea4f666bdfe79e65e7cf96 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Gl=C3=B6ckner?= Date: Tue, 17 Nov 2009 15:22:57 +0530 Subject: USB: musb: Fix CPPI IRQs not being signaled MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit On tx channel abort a cppi interrupt is generated for a short time by setting the lowest bit of the TCPPICOMPPTR register. It is then reset immediately by clearing the bit. When the interrupt handler is run, it does not detect an interrupt in the TCPPIMSKSR or RCPPIMSKSR registers and thus exits early without writing the TCPPIEOIR register. It appears that this inhibits further cppi interrupts until the handler is called by chance, f.ex. from davinci_interrupt(). By moving the unmasking of the interrupt below the writes to TCPPICOMPPTR, no interrupt is generated and no write to TCPPIEOIR is necessary. Signed-off-by: Daniel Glöckner Signed-off-by: Ajay Kumar Gupta Signed-off-by: Greg Kroah-Hartman --- drivers/usb/musb/cppi_dma.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/usb/musb/cppi_dma.c b/drivers/usb/musb/cppi_dma.c index c3577bbbae6..ef2332a9941 100644 --- a/drivers/usb/musb/cppi_dma.c +++ b/drivers/usb/musb/cppi_dma.c @@ -1442,11 +1442,6 @@ static int cppi_channel_abort(struct dma_channel *channel) musb_writew(regs, MUSB_TXCSR, value); musb_writew(regs, MUSB_TXCSR, value); - /* re-enable interrupt */ - if (enabled) - musb_writel(tibase, DAVINCI_TXCPPI_INTENAB_REG, - (1 << cppi_ch->index)); - /* While we scrub the TX state RAM, ensure that we clean * up any interrupt that's currently asserted: * 1. Write to completion Ptr value 0x1(bit 0 set) @@ -1459,6 +1454,11 @@ static int cppi_channel_abort(struct dma_channel *channel) cppi_reset_tx(tx_ram, 1); musb_writel(&tx_ram->tx_complete, 0, 0); + /* re-enable interrupt */ + if (enabled) + musb_writel(tibase, DAVINCI_TXCPPI_INTENAB_REG, + (1 << cppi_ch->index)); + cppi_dump_tx(5, cppi_ch, " (done teardown)"); /* REVISIT tx side _should_ clean up the same way -- cgit v1.2.3 From ee4ecb8ac63a5792bec448037d4b82ec4144f94b Mon Sep 17 00:00:00 2001 From: Oliver Neukum Date: Fri, 27 Nov 2009 15:17:59 +0100 Subject: USB: work around for EHCI with quirky periodic schedules a quirky chipset needs periodic schedules to run for a minimum time before they can be disabled again. This enforces the requirement with a time stamp and a calculated delay Signed-off-by: Oliver Neukum Cc: stable Signed-off-by: Greg Kroah-Hartman --- drivers/usb/host/ehci-hcd.c | 2 ++ drivers/usb/host/ehci-pci.c | 4 ++++ drivers/usb/host/ehci-sched.c | 12 ++++++++++++ drivers/usb/host/ehci.h | 2 ++ 4 files changed, 20 insertions(+) diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c index 9835e071394..f5f5601701c 100644 --- a/drivers/usb/host/ehci-hcd.c +++ b/drivers/usb/host/ehci-hcd.c @@ -28,6 +28,7 @@ #include #include #include +#include #include #include #include @@ -676,6 +677,7 @@ static int ehci_run (struct usb_hcd *hcd) ehci_readl(ehci, &ehci->regs->command); /* unblock posted writes */ msleep(5); up_write(&ehci_cf_port_reset_rwsem); + ehci->last_periodic_enable = ktime_get_real(); temp = HC_VERSION(ehci_readl(ehci, &ehci->caps->hc_capbase)); ehci_info (ehci, diff --git a/drivers/usb/host/ehci-pci.c b/drivers/usb/host/ehci-pci.c index 378861b9d79..ead5f4f2aa5 100644 --- a/drivers/usb/host/ehci-pci.c +++ b/drivers/usb/host/ehci-pci.c @@ -111,6 +111,10 @@ static int ehci_pci_setup(struct usb_hcd *hcd) switch (pdev->vendor) { case PCI_VENDOR_ID_INTEL: ehci->need_io_watchdog = 0; + if (pdev->device == 0x27cc) { + ehci->broken_periodic = 1; + ehci_info(ehci, "using broken periodic workaround\n"); + } break; case PCI_VENDOR_ID_TDI: if (pdev->device == PCI_DEVICE_ID_TDI_EHCI) { diff --git a/drivers/usb/host/ehci-sched.c b/drivers/usb/host/ehci-sched.c index b25cdea93a1..a5535b5e3fe 100644 --- a/drivers/usb/host/ehci-sched.c +++ b/drivers/usb/host/ehci-sched.c @@ -475,6 +475,8 @@ static int enable_periodic (struct ehci_hcd *ehci) /* make sure ehci_work scans these */ ehci->next_uframe = ehci_readl(ehci, &ehci->regs->frame_index) % (ehci->periodic_size << 3); + if (unlikely(ehci->broken_periodic)) + ehci->last_periodic_enable = ktime_get_real(); return 0; } @@ -486,6 +488,16 @@ static int disable_periodic (struct ehci_hcd *ehci) if (--ehci->periodic_sched) return 0; + if (unlikely(ehci->broken_periodic)) { + /* delay experimentally determined */ + ktime_t safe = ktime_add_us(ehci->last_periodic_enable, 1000); + ktime_t now = ktime_get_real(); + s64 delay = ktime_us_delta(safe, now); + + if (unlikely(delay > 0)) + udelay(delay); + } + /* did setting PSE not take effect yet? * takes effect only at frame boundaries... */ diff --git a/drivers/usb/host/ehci.h b/drivers/usb/host/ehci.h index 064e76821ff..2d85e21ff28 100644 --- a/drivers/usb/host/ehci.h +++ b/drivers/usb/host/ehci.h @@ -118,6 +118,7 @@ struct ehci_hcd { /* one per controller */ unsigned stamp; unsigned random_frame; unsigned long next_statechange; + ktime_t last_periodic_enable; u32 command; /* SILICON QUIRKS */ @@ -127,6 +128,7 @@ struct ehci_hcd { /* one per controller */ unsigned big_endian_desc:1; unsigned has_amcc_usb23:1; unsigned need_io_watchdog:1; + unsigned broken_periodic:1; /* required for usb32 quirk */ #define OHCI_CTRL_HCFS (3 << 6) -- cgit v1.2.3 From 0ec8648379334f1e127ebd5e57a625890f116824 Mon Sep 17 00:00:00 2001 From: Gernot Hillier Date: Fri, 27 Nov 2009 13:49:23 +0100 Subject: USB: Add support for Mobilcom Debitel USB UMTS Surf-Stick to option driver This patch adds the vendor and device id for the Mobilcom Debitel UMTS surf stick (a.k.a. 4G Systems XSStick W14, MobiData MBD-200HU, ...). To see these ids, you need to switch the stick to modem operation first with the help of usb_modeswitch. This makes it switch from 1c9e:f000 to 1c9e:9603 and thus be recognized by the option driver. Signed-off-by: Gernot Hillier Signed-off-by: Greg Kroah-Hartman --- drivers/usb/serial/option.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/usb/serial/option.c b/drivers/usb/serial/option.c index 319aaf9725b..0577e4b6111 100644 --- a/drivers/usb/serial/option.c +++ b/drivers/usb/serial/option.c @@ -336,6 +336,10 @@ static int option_resume(struct usb_serial *serial); #define AIRPLUS_VENDOR_ID 0x1011 #define AIRPLUS_PRODUCT_MCD650 0x3198 +/* 4G Systems products */ +#define FOUR_G_SYSTEMS_VENDOR_ID 0x1c9e +#define FOUR_G_SYSTEMS_PRODUCT_W14 0x9603 + static struct usb_device_id option_ids[] = { { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_COLT) }, { USB_DEVICE(OPTION_VENDOR_ID, OPTION_PRODUCT_RICOLA) }, @@ -599,6 +603,7 @@ static struct usb_device_id option_ids[] = { { USB_DEVICE(ALCATEL_VENDOR_ID, ALCATEL_PRODUCT_X060S) }, { USB_DEVICE(AIRPLUS_VENDOR_ID, AIRPLUS_PRODUCT_MCD650) }, { USB_DEVICE(TLAYTECH_VENDOR_ID, TLAYTECH_PRODUCT_TEU800) }, + { USB_DEVICE(FOUR_G_SYSTEMS_VENDOR_ID, FOUR_G_SYSTEMS_PRODUCT_W14) }, { } /* Terminating entry */ }; MODULE_DEVICE_TABLE(usb, option_ids); -- cgit v1.2.3