ChangeSet 1.1276.1.13, 2004/01/27 10:45:28-08:00, david-b@pacbell.net [PATCH] USB gadget: net2280 controller driver updates [7/7] A variety of fixes: - Resolves some problems with DMA chaining. It should stream a lot better now; but not all the funky cases are handled yet. - Now "use_dma_chaining" is a module parameter not a #define. It also defaults to false. Some gadget drivers will work fine with this enabled, getting some IRQ reduction and increased I/O parallelism (given deep I/O queues); others won't. - Handle isochronous transfers (from Mark Huang at Broadcom) - Some of the chiprev 0100 workarounds weren't quite right. Neither were buffer allocations on dma-incoherent systems. - Handle bulk endpoint halts better, for file_storage gadget driver. - Handle the hardware device status bits better: * selfpowered by default, clearable with the API; * remote wakeup disabled by default, host must enable it. This basically synchs this version with the latest 2.6 code. drivers/usb/gadget/net2280.c | 541 +++++++++++++++++++++++++++---------------- drivers/usb/gadget/net2280.h | 6 2 files changed, 350 insertions(+), 197 deletions(-) diff -Nru a/drivers/usb/gadget/net2280.c b/drivers/usb/gadget/net2280.c --- a/drivers/usb/gadget/net2280.c Wed Jan 28 13:36:30 2004 +++ b/drivers/usb/gadget/net2280.c Wed Jan 28 13:36:30 2004 @@ -25,9 +25,6 @@ * rev1 chips. Rev1a silicon (0110) fixes almost all of them. */ -#define USE_DMA_CHAINING - - /* * Copyright (C) 2003 David Brownell * Copyright (C) 2003 NetChip Technologies @@ -47,14 +44,13 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#define DEBUG 1 -// #define VERBOSE /* extra debug messages (success too) */ +#undef DEBUG /* messages on error and most fault paths */ +#undef VERBOSE /* extra debug messages (success too) */ #include #include #include #include -#include #include #include #include @@ -77,7 +73,7 @@ #define DRIVER_DESC "NetChip 2280 USB Peripheral Controller" -#define DRIVER_VERSION "Bastille Day 2003" +#define DRIVER_VERSION "2004 Jan 14" #define DMA_ADDR_INVALID (~(dma_addr_t)0) #define EP_DONTUSE 13 /* nonzero */ @@ -95,7 +91,23 @@ "ep-e", "ep-f", }; +/* use_dma -- general goodness, fewer interrupts, less cpu load (vs PIO) + * use_dma_chaining -- dma descriptor queueing gives even more irq reduction + * + * The net2280 DMA engines are not tightly integrated with their FIFOs; + * not all cases are (yet) handled well in this driver or the silicon. + * Some gadget drivers work better with the dma support here than others. + * These two parameters let you use PIO or more aggressive DMA. + */ static int use_dma = 1; +static int use_dma_chaining = 0; + +MODULE_PARM (use_dma, "i"); +MODULE_PARM_DESC (use_dma, "true to use dma controllers"); + +MODULE_PARM (use_dma_chaining, "i"); +MODULE_PARM_DESC (use_dma_chaining, "true to use dma descriptor queues"); + /* mode 0 == ep-{a,b,c,d} 1K fifo each * mode 1 == ep-{a,b} 2K fifo each, ep-{c,d} unavailable @@ -103,9 +115,6 @@ */ static ushort fifo_mode = 0; -MODULE_PARM (use_dma, "i"); -MODULE_PARM_DESC (use_dma, "true to use dma controllers"); - MODULE_PARM (fifo_mode, "h"); MODULE_PARM_DESC (fifo_mode, "net2280 fifo mode"); @@ -162,6 +171,7 @@ /* ep_reset() has already been called */ ep->stopped = 0; + ep->out_overflow = 0; /* set speed-dependent max packet; may kick in high bandwidth */ set_idx_reg (dev->regs, REG_EP_MAXPKT (dev, ep->num), max); @@ -169,8 +179,8 @@ /* FIFO lines can't go to different packets. PIO is ok, so * use it instead of troublesome (non-bulk) multi-packet DMA. */ - if (ep->is_in && ep->dma && (max % 4) != 0) { - DEBUG (ep->dev, "%s, no IN dma for maxpacket %d\n", + if (ep->dma && (max % 4) != 0 && use_dma_chaining) { + DEBUG (ep->dev, "%s, no dma for maxpacket %d\n", ep->ep.name, ep->ep.maxpacket); ep->dma = 0; } @@ -179,17 +189,21 @@ writel ((1 << FIFO_FLUSH), &ep->regs->ep_stat); tmp = (desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK); if (tmp == USB_ENDPOINT_XFER_INT) { - /* not just because of erratum 0105; avoid ever - * kicking in the "toggle-irrelevant" mode. - */ - tmp = USB_ENDPOINT_XFER_BULK; + /* erratum 0105 workaround prevents hs NYET */ + if (dev->chiprev == 0100 + && dev->gadget.speed == USB_SPEED_HIGH + && !(desc->bEndpointAddress & USB_DIR_IN)) + writel ((1 << CLEAR_NAK_OUT_PACKETS_MODE), + &ep->regs->ep_rsp); } else if (tmp == USB_ENDPOINT_XFER_BULK) { /* catch some particularly blatant driver bugs */ if ((dev->gadget.speed == USB_SPEED_HIGH && max != 512) || (dev->gadget.speed == USB_SPEED_FULL - && max > 64)) + && max > 64)) { + spin_unlock_irqrestore (&dev->lock, flags); return -ERANGE; + } } ep->is_iso = (tmp == USB_ENDPOINT_XFER_ISOC) ? 1 : 0; tmp <<= ENDPOINT_TYPE; @@ -205,11 +219,6 @@ writel (tmp, &ep->regs->ep_cfg); -#ifdef NET2280_DMA_OUT_WORKAROUND - if (!ep->is_in) - ep->dma = 0; -#endif - /* enable irqs */ if (!ep->dma) { /* pio, per-packet */ tmp = (1 << ep->num) | readl (&dev->regs->pciirqenb0); @@ -388,6 +397,7 @@ } td->dmacount = 0; /* not VALID */ td->dmaaddr = __constant_cpu_to_le32 (DMA_ADDR_INVALID); + td->dmadesc = td->dmaaddr; req->td = td; } return &req->req; @@ -430,6 +440,9 @@ #elif defined(CONFIG_PPC) && !defined(CONFIG_NOT_COHERENT_CACHE) #define USE_KMALLOC +#elif defined(CONFIG_MIPS) && !defined(CONFIG_NONCOHERENT_IO) +#define USE_KMALLOC + /* FIXME there are other cases, including an x86-64 one ... */ #endif @@ -451,31 +464,23 @@ ep = container_of (_ep, struct net2280_ep, ep); if (!_ep) return 0; - *dma = DMA_ADDR_INVALID; - if (ep->dma) { + #if defined(USE_KMALLOC) - retval = kmalloc (bytes, gfp_flags); - if (retval) - *dma = virt_to_phys (retval); - -#elif LINUX_VERSION_CODE > KERNEL_VERSION(2,5,58) -#warning Using dma_alloc_consistent even with sub-page allocations - /* the main problem with this call is that it wastes memory - * on typical 1/N page allocations: it allocates 1-N pages. - */ - retval = dma_alloc_coherent (&ep->dev->pdev->dev, - bytes, dma, gfp_flags); + retval = kmalloc(bytes, gfp_flags); + if (retval) + *dma = virt_to_phys(retval); #else -#error No dma-coherent memory allocator is available - /* pci_alloc_consistent works, but pci_free_consistent() - * isn't safe in_interrupt(). plus, in addition to the - * 1/Nth page weakness, it doesn't understand gfp_flags. + if (ep->dma) { + /* one problem with this call is that it wastes memory on + * typical 1/N page allocations: it allocates 1..N pages. + * another is that it always uses GFP_ATOMIC. */ -#endif +#warning Using pci_alloc_consistent even with buffers smaller than a page. + retval = pci_alloc_consistent(ep->dev->pdev, bytes, dma); } else - retval = kmalloc (bytes, gfp_flags); - + retval = kmalloc(bytes, gfp_flags); +#endif return retval; } @@ -488,9 +493,17 @@ ) { /* free memory into the right allocator */ #ifndef USE_KMALLOC - if (dma != DMA_ADDR_INVALID) - dma_free_coherent (ep->dev->pdev, bytes, dma); - else + if (dma != DMA_ADDR_INVALID) { + struct net2280_ep *ep; + + ep = container_of(_ep, struct net2280_ep, ep); + if (!_ep) + return; + /* one problem with this call is that some platforms + * don't allow it to be used in_irq(). + */ + pci_free_consistent(ep->dev->pdev, bytes, buf, dma); + } else #endif kfree (buf); } @@ -541,8 +554,11 @@ count -= 4; } - /* last fifo entry is "short" unless we wrote a full packet */ - if (total < ep->ep.maxpacket) { + /* last fifo entry is "short" unless we wrote a full packet. + * also explicitly validate last word in (periodic) transfers + * when maxpacket is not a multiple of 4 bytes. + */ + if (count || total < ep->ep.maxpacket) { tmp = count ? get_unaligned ((u32 *)buf) : count; cpu_to_le32s (&tmp); set_fifo_bytecount (ep, count & 0x03); @@ -555,6 +571,9 @@ /* work around erratum 0106: PCI and USB race over the OUT fifo. * caller guarantees chiprev 0100, out endpoint is NAKing, and * there's no real data in the fifo. + * + * NOTE: also used in cases where that erratum doesn't apply: + * where the host wrote "too much" data to us. */ static void out_flush (struct net2280_ep *ep) { @@ -599,13 +618,13 @@ /* erratum 0106 ... packets coming in during fifo reads might * be incompletely rejected. not all cases have workarounds. */ - if (ep->dev->chiprev == 0x0100) { + if (ep->dev->chiprev == 0x0100 + && ep->dev->gadget.speed == USB_SPEED_FULL) { + udelay (1); tmp = readl (&ep->regs->ep_stat); if ((tmp & (1 << NAK_OUT_PACKETS))) - /* cleanup = 1 */; - else if ((tmp & (1 << FIFO_FULL)) - /* don't break hs PING protocol ... */ - || ep->dev->gadget.speed == USB_SPEED_FULL) { + cleanup = 1; + else if ((tmp & (1 << FIFO_FULL))) { start_out_naking (ep); prevent = 1; } @@ -617,6 +636,15 @@ */ prefetchw (buf); count = readl (®s->ep_avail); + if (unlikely (count == 0)) { + udelay (1); + tmp = readl (&ep->regs->ep_stat); + count = readl (®s->ep_avail); + /* handled that data already? */ + if (count == 0 && (tmp & (1 << NAK_OUT_PACKETS)) == 0) + return 0; + } + tmp = req->req.length - req->req.actual; if (count > tmp) { /* as with DMA, data overflow gets flushed */ @@ -626,7 +654,10 @@ ep->ep.name, count, tmp); req->req.status = -EOVERFLOW; cleanup = 1; - } + /* NAK_OUT_PACKETS will be set, so flushing is safe; + * the next read will start with the next packet + */ + } /* else it's a ZLP, no worries */ count = tmp; } req->req.actual += count; @@ -665,7 +696,7 @@ } /* fill out dma descriptor to match a given request */ -static inline void +static void fill_dma_desc (struct net2280_ep *ep, struct net2280_request *req, int valid) { struct net2280_dma *td = req->td; @@ -678,15 +709,13 @@ */ if (ep->is_in) dmacount |= (1 << DMA_DIRECTION); - else + else if ((dmacount % ep->ep.maxpacket) != 0) dmacount |= (1 << END_OF_CHAIN); req->valid = valid; if (valid) dmacount |= (1 << VALID_BIT); -#ifdef USE_DMA_CHAINING - if (!req->req.no_interrupt) -#endif + if (likely(!req->req.no_interrupt || !use_dma_chaining)) dmacount |= (1 << DMA_DONE_INTERRUPT_ENABLE); /* td->dmadesc = previously set by caller */ @@ -698,7 +727,8 @@ } static const u32 dmactl_default = - (1 << DMA_CLEAR_COUNT_ENABLE) + (1 << DMA_SCATTER_GATHER_DONE_INTERRUPT) + | (1 << DMA_CLEAR_COUNT_ENABLE) /* erratum 0116 workaround part 1 (use POLLING) */ | (POLL_100_USEC << DESCRIPTOR_POLLING_RATE) | (1 << DMA_VALID_BIT_POLLING_ENABLE) @@ -714,18 +744,41 @@ static inline void stop_dma (struct net2280_dma_regs *dma) { - writel (dmactl_default & ~(1 << DMA_ENABLE), &dma->dmactl); + writel (readl (&dma->dmactl) & ~(1 << DMA_ENABLE), &dma->dmactl); spin_stop_dma (dma); } +static void start_queue (struct net2280_ep *ep, u32 dmactl, u32 td_dma) +{ + struct net2280_dma_regs *dma = ep->dma; + + writel ((1 << VALID_BIT) | (ep->is_in << DMA_DIRECTION), + &dma->dmacount); + writel (readl (&dma->dmastat), &dma->dmastat); + + writel (td_dma, &dma->dmadesc); + writel (dmactl, &dma->dmactl); + + /* erratum 0116 workaround part 3: pci arbiter away from net2280 */ + (void) readl (&ep->dev->pci->pcimstctl); + + writel ((1 << DMA_START), &dma->dmastat); + + if (!ep->is_in) + stop_out_naking (ep); +} + static void start_dma (struct net2280_ep *ep, struct net2280_request *req) { u32 tmp; - int clear_nak = 0; struct net2280_dma_regs *dma = ep->dma; /* FIXME can't use DMA for ZLPs */ + /* on this path we "know" there's no dma active (yet) */ + WARN_ON (readl (&dma->dmactl) & (1 << DMA_ENABLE)); + writel (0, &ep->dma->dmactl); + /* previous OUT packet might have been short */ if (!ep->is_in && ((tmp = readl (&ep->regs->ep_stat)) & (1 << NAK_OUT_PACKETS)) != 0) { @@ -733,9 +786,9 @@ &ep->regs->ep_stat); tmp = readl (&ep->regs->ep_avail); - if (tmp == 0) - clear_nak = 1; - else { + if (tmp) { + writel (readl (&dma->dmastat), &dma->dmastat); + /* transfer all/some fifo data */ writel (req->req.dma, &dma->dmaaddr); tmp = min (tmp, req->req.length); @@ -744,6 +797,8 @@ req->td->dmacount = cpu_to_le32 (req->req.length - tmp); writel ((1 << DMA_DONE_INTERRUPT_ENABLE) | tmp, &dma->dmacount); + req->td->dmadesc = 0; + req->valid = 1; writel ((1 << DMA_ENABLE), &dma->dmactl); writel ((1 << DMA_START), &dma->dmastat); @@ -751,8 +806,6 @@ } } - /* on this path we know there's no dma queue (yet) */ - WARN_ON (readl (&dma->dmactl) & (1 << DMA_ENABLE)); tmp = dmactl_default; /* force packet boundaries between dma requests, but prevent the @@ -772,25 +825,10 @@ req->td->dmadesc = cpu_to_le32 (ep->td_dma); fill_dma_desc (ep, req, 1); -#ifdef USE_DMA_CHAINING - writel ( (1 << VALID_BIT) - | (ep->is_in << DMA_DIRECTION) - | 0, &dma->dmacount); -#else - req->td->dmacount |= __constant_cpu_to_le32 (1 << END_OF_CHAIN); -#endif - - writel (req->td_dma, &dma->dmadesc); - writel (tmp, &dma->dmactl); - - /* erratum 0116 workaround part 3: pci arbiter away from net2280 */ - (void) readl (&ep->dev->pci->pcimstctl); + if (!use_dma_chaining) + req->td->dmacount |= __constant_cpu_to_le32 (1 << END_OF_CHAIN); - writel ((1 << DMA_START), &dma->dmastat); - - /* recover from previous short read; erratum 0112 workaround #1 */ - if (clear_nak) - writel ((1 << CLEAR_NAK_OUT_PACKETS), &ep->regs->ep_rsp); + start_queue (ep, tmp, req->td_dma); } static inline void @@ -893,7 +931,6 @@ _req->status = -EINPROGRESS; _req->actual = 0; - req->dma_done = 0; /* kickstart this i/o queue? */ if (list_empty (&ep->queue) && !ep->stopped) { @@ -977,10 +1014,11 @@ ) { req->req.actual = req->req.length - (DMA_BYTE_COUNT_MASK & dmacount); - rmb (); done (ep, req, status); } +static void restart_dma (struct net2280_ep *ep); + static void scan_dma_completions (struct net2280_ep *ep) { /* only look at descriptors that were "naturally" retired, @@ -1000,14 +1038,37 @@ break; /* SHORT_PACKET_TRANSFERRED_INTERRUPT handles "usb-short" - * packets, including overruns, even when the transfer was - * exactly the length requested (dmacount now zero). - * FIXME there's an overrun case here too, where we expect - * a short packet but receive a max length one (won't NAK). + * cases where DMA must be aborted; this code handles + * all non-abort DMA completions. */ - if (!ep->is_in && (req->req.length % ep->ep.maxpacket) != 0) { - req->dma_done = 1; + if (unlikely (req->td->dmadesc == 0)) { + /* paranoia */ + tmp = readl (&ep->dma->dmacount); + if (tmp & DMA_BYTE_COUNT_MASK) + break; + /* single transfer mode */ + dma_done (ep, req, tmp, 0); break; + } else if (!ep->is_in + && (req->req.length % ep->ep.maxpacket) != 0) { + tmp = readl (&ep->regs->ep_stat); + + /* AVOID TROUBLE HERE by not issuing short reads from + * your gadget driver. That helps avoids errata 0121, + * 0122, and 0124; not all cases trigger the warning. + */ + if ((tmp & (1 << NAK_OUT_PACKETS)) == 0) { + WARN (ep->dev, "%s lost packet sync!\n", + ep->ep.name); + req->req.status = -EOVERFLOW; + } else if ((tmp = readl (&ep->regs->ep_avail)) != 0) { + /* fifo gets flushed later */ + ep->out_overflow = 1; + DEBUG (ep->dev, "%s dma, discard %d len %d\n", + ep->ep.name, tmp, + req->req.length); + req->req.status = -EOVERFLOW; + } } dma_done (ep, req, tmp, 0); } @@ -1016,41 +1077,50 @@ static void restart_dma (struct net2280_ep *ep) { struct net2280_request *req; + u32 dmactl = dmactl_default; if (ep->stopped) return; req = list_entry (ep->queue.next, struct net2280_request, queue); -#ifdef USE_DMA_CHAINING + if (!use_dma_chaining) { + start_dma (ep, req); + return; + } + /* the 2280 will be processing the queue unless queue hiccups after * the previous transfer: * IN: wanted automagic zlp, head doesn't (or vice versa) + * DMA_FIFO_VALIDATE doesn't init from dma descriptors. * OUT: was "usb-short", we must restart. */ - if (!req->valid) { + if (ep->is_in && !req->valid) { struct net2280_request *entry, *prev = 0; - int qmode, reqmode, done = 0; + int reqmode, done = 0; DEBUG (ep->dev, "%s dma hiccup td %p\n", ep->ep.name, req->td); - qmode = likely (req->req.zero + ep->in_fifo_validate = likely (req->req.zero || (req->req.length % ep->ep.maxpacket) != 0); + if (ep->in_fifo_validate) + dmactl |= (1 << DMA_FIFO_VALIDATE); list_for_each_entry (entry, &ep->queue, queue) { u32 dmacount; - if (entry != req) + if (entry == req) continue; dmacount = entry->td->dmacount; if (!done) { reqmode = likely (entry->req.zero || (entry->req.length % ep->ep.maxpacket) != 0); - if (reqmode == qmode) { + if (reqmode == ep->in_fifo_validate) { entry->valid = 1; dmacount |= valid_bit; entry->td->dmacount = dmacount; prev = entry; continue; } else { + /* force a hiccup */ prev->td->dmacount |= dma_done_ie; done = 1; } @@ -1062,22 +1132,21 @@ entry->td->dmacount = dmacount; prev = entry; } - start_dma (ep, req); - } else if (!ep->is_in - && (readl (&ep->regs->ep_stat) - & (1 << NAK_OUT_PACKETS)) != 0) - start_dma (ep, req); -#else - start_dma (ep, req); -#endif + } + + writel (0, &ep->dma->dmactl); + start_queue (ep, dmactl, req->td_dma); } -static inline void abort_dma (struct net2280_ep *ep) +static void abort_dma (struct net2280_ep *ep) { /* abort the current transfer */ - writel ((1 << DMA_ABORT), &ep->dma->dmastat); - - /* collect completed transfers (except the current one) */ + if (likely (!list_empty (&ep->queue))) { + /* FIXME work around errata 0121, 0122, 0124 */ + writel ((1 << DMA_ABORT), &ep->dma->dmastat); + spin_stop_dma (ep->dma); + } else + stop_dma (ep->dma); scan_dma_completions (ep); } @@ -1108,43 +1177,53 @@ int stopped; ep = container_of (_ep, struct net2280_ep, ep); - req = container_of (_req, struct net2280_request, req); if (!_ep || (!ep->desc && ep->num != 0) || !_req) return -EINVAL; spin_lock_irqsave (&ep->dev->lock, flags); stopped = ep->stopped; - /* pause dma while we scan the queue */ + /* quiesce dma while we patch the queue */ dmactl = 0; ep->stopped = 1; if (ep->dma) { dmactl = readl (&ep->dma->dmactl); - writel (dmactl & ~(1 << DMA_ENABLE), &ep->dma->dmactl); - /* force synch, clean any completed requests */ - spin_stop_dma (ep->dma); + /* WARNING erratum 0127 may kick in ... */ + stop_dma (ep->dma); scan_dma_completions (ep); } + /* make sure it's still queued on this endpoint */ + list_for_each_entry (req, &ep->queue, queue) { + if (&req->req == _req) + break; + } + if (&req->req != _req) { + spin_unlock_irqrestore (&ep->dev->lock, flags); + return -EINVAL; + } + /* queue head may be partially complete. */ if (ep->queue.next == &req->queue) { if (ep->dma) { DEBUG (ep->dev, "unlink (%s) dma\n", _ep->name); _req->status = -ECONNRESET; abort_dma (ep); - if (likely (ep->queue.next == &req->queue)) + if (likely (ep->queue.next == &req->queue)) { + // NOTE: misreports single-transfer mode + req->td->dmacount = 0; /* invalidate */ dma_done (ep, req, - le32_to_cpup (&req->td->dmacount), + readl (&ep->dma->dmacount), -ECONNRESET); + } } else { DEBUG (ep->dev, "unlink (%s) pio\n", _ep->name); done (ep, req, -ECONNRESET); } req = 0; -#ifdef USE_DMA_CHAINING /* patch up hardware chaining data */ - } else if (ep->dma) { + } else if (ep->dma && use_dma_chaining) { if (req->queue.prev == ep->queue.next) { writel (le32_to_cpu (req->td->dmadesc), &ep->dma->dmadesc); @@ -1161,7 +1240,6 @@ if (req->td->dmacount & dma_done_ie) prev->td->dmacount |= dma_done_ie; } -#endif } if (req) @@ -1188,10 +1266,14 @@ /*-------------------------------------------------------------------------*/ +static int net2280_fifo_status (struct usb_ep *_ep); + static int net2280_set_halt (struct usb_ep *_ep, int value) { struct net2280_ep *ep; + unsigned long flags; + int retval = 0; ep = container_of (_ep, struct net2280_ep, ep); if (!_ep || (!ep->desc && ep->num != 0)) @@ -1202,19 +1284,27 @@ == USB_ENDPOINT_XFER_ISOC) return -EINVAL; - VDEBUG (ep->dev, "%s %s halt\n", _ep->name, value ? "set" : "clear"); - - /* set/clear, then synch memory views with the device */ - if (value) { - if (ep->num == 0) - ep->dev->protocol_stall = 1; - else - set_halt (ep); - } else - clear_halt (ep); - (void) readl (&ep->regs->ep_rsp); + spin_lock_irqsave (&ep->dev->lock, flags); + if (!list_empty (&ep->queue)) + retval = -EAGAIN; + else if (ep->is_in && value && net2280_fifo_status (_ep) != 0) + retval = -EAGAIN; + else { + VDEBUG (ep->dev, "%s %s halt\n", _ep->name, + value ? "set" : "clear"); + /* set/clear, then synch memory views with the device */ + if (value) { + if (ep->num == 0) + ep->dev->protocol_stall = 1; + else + set_halt (ep); + } else + clear_halt (ep); + (void) readl (&ep->regs->ep_rsp); + } + spin_unlock_irqrestore (&ep->dev->lock, flags); - return 0; + return retval; } static int @@ -1290,21 +1380,49 @@ static int net2280_wakeup (struct usb_gadget *_gadget) { struct net2280 *dev; + u32 tmp; + unsigned long flags; if (!_gadget) return 0; dev = container_of (_gadget, struct net2280, gadget); - writel (1 << GENERATE_RESUME, &dev->usb->usbstat); + + spin_lock_irqsave (&dev->lock, flags); + tmp = readl (&dev->usb->usbctl); + if (tmp & (1 << DEVICE_REMOTE_WAKEUP_ENABLE)) + writel (1 << GENERATE_RESUME, &dev->usb->usbstat); + spin_unlock_irqrestore (&dev->lock, flags); /* pci writes may still be posted */ return 0; } +static int net2280_set_selfpowered (struct usb_gadget *_gadget, int value) +{ + struct net2280 *dev; + u32 tmp; + unsigned long flags; + + if (!_gadget) + return 0; + dev = container_of (_gadget, struct net2280, gadget); + + spin_lock_irqsave (&dev->lock, flags); + tmp = readl (&dev->usb->usbctl); + if (value) + tmp |= (1 << SELF_POWERED_STATUS); + else + tmp &= ~(1 << SELF_POWERED_STATUS); + writel (tmp, &dev->usb->usbctl); + spin_unlock_irqrestore (&dev->lock, flags); + + return 0; +} + static const struct usb_gadget_ops net2280_ops = { .get_frame = net2280_get_frame, .wakeup = net2280_wakeup, - - // .set_selfpowered = net2280_set_selfpowered, + .set_selfpowered = net2280_set_selfpowered, }; /*-------------------------------------------------------------------------*/ @@ -1348,11 +1466,14 @@ /* Main Control Registers */ t = snprintf (next, size, "%s version " DRIVER_VERSION - ", chiprev %04x\n" + ", chiprev %04x, dma %s\n\n" "devinit %03x fifoctl %08x gadget '%s'\n" "pci irqenb0 %02x irqenb1 %08x " "irqstat0 %04x irqstat1 %08x\n", driver_name, dev->chiprev, + use_dma + ? (use_dma_chaining ? "chaining" : "enabled") + : "disabled", readl (&dev->regs->devinit), readl (&dev->regs->fifoctl), s, @@ -1399,7 +1520,7 @@ t1 = readl (&ep->regs->ep_cfg); t2 = readl (&ep->regs->ep_rsp) & 0xff; t = snprintf (next, size, - "%s\tcfg %05x rsp (%02x) %s%s%s%s%s%s%s%s" + "\n%s\tcfg %05x rsp (%02x) %s%s%s%s%s%s%s%s" "irqenb %02x\n", ep->ep.name, t1, t2, (t2 & (1 << CLEAR_NAK_OUT_PACKETS)) @@ -1453,7 +1574,7 @@ // none yet /* Statistics */ - t = snprintf (next, size, "irqs: "); + t = snprintf (next, size, "\nirqs: "); size -= t; next += t; for (i = 0; i < 7; i++) { @@ -1462,7 +1583,7 @@ ep = &dev->ep [i]; if (i && !ep->irqs) continue; - t = snprintf (next, size, " %s/%ld", ep->ep.name, ep->irqs); + t = snprintf (next, size, " %s/%lu", ep->ep.name, ep->irqs); size -= t; next += t; @@ -1504,7 +1625,7 @@ continue; t = d->bEndpointAddress; t = snprintf (next, size, - "%s (ep%d%s-%s) max %04x %s\n", + "\n%s (ep%d%s-%s) max %04x %s fifo %d\n", ep->ep.name, t & USB_ENDPOINT_NUMBER_MASK, (t & USB_DIR_IN) ? "in" : "out", ({ char *val; @@ -1517,7 +1638,7 @@ val = "iso"; break; }; val; }), le16_to_cpu (d->wMaxPacketSize) & 0x1fff, - ep->dma ? "dma" : "pio" + ep->dma ? "dma" : "pio", ep->fifo_size ); } else /* ep0 should only have one transfer queued */ t = snprintf (next, size, "ep0 max 64 pio %s\n", @@ -1552,6 +1673,20 @@ goto done; size -= t; next += t; + + if (ep->dma) { + struct net2280_dma *td; + + td = req->td; + t = snprintf (next, size, "\t td %08x " + " count %08x buf %08x desc %08x\n", + req->td_dma, td->dmacount, + td->dmaaddr, td->dmadesc); + if (t <= 0 || t > size) + goto done; + size -= t; + next += t; + } } } @@ -1686,8 +1821,10 @@ /* clear old dma and irq state */ for (tmp = 0; tmp < 4; tmp++) { - writel ((1 << DMA_ABORT), &dev->dma [tmp].dmastat); - stop_dma (&dev->dma [tmp]); + struct net2280_ep *ep = &dev->ep [tmp + 1]; + + if (ep->dma) + abort_dma (ep); } writel (~0, &dev->regs->irqstat0), writel (~(1 << SUSPEND_REQUEST_INTERRUPT), &dev->regs->irqstat1), @@ -1767,7 +1904,7 @@ | (1 << SELF_POWERED_USB_DEVICE) | (1 << REMOTE_WAKEUP_SUPPORT) | (1 << USB_DETECT_ENABLE) - | (1 << DEVICE_REMOTE_WAKEUP_ENABLE) + | (1 << SELF_POWERED_STATUS) , &dev->usb->usbctl); /* enable irqs so we can see ep0 and general operation */ @@ -1939,6 +2076,8 @@ ep->stopped = 1; set_halt (ep); } + if (!req) + allow_status (ep); mode = 2; /* reply to extra IN data tokens with a zlp */ } else if (t & (1 << DATA_IN_TOKEN_INTERRUPT)) { @@ -1979,41 +2118,62 @@ if (likely (ep->dma != 0)) { if (t & (1 << SHORT_PACKET_TRANSFERRED_INTERRUPT)) { u32 count; + int stopped = ep->stopped; /* TRANSFERRED works around OUT_DONE erratum 0112. * we expect (N <= maxpacket) bytes; host wrote M. * iff (M < N) we won't ever see a DMA interrupt. */ - count = readl (&ep->dma->dmacount); - count &= DMA_BYTE_COUNT_MASK; - if (!req->dma_done) { - /* dma can finish with the FIFO non-empty, - * on (M > N) errors. + ep->stopped = 1; + for (count = 0; ; t = readl (&ep->regs->ep_stat)) { + + /* any preceding dma transfers must finish. + * dma handles (M >= N), may empty the queue + */ + scan_dma_completions (ep); + if (unlikely (list_empty (&ep->queue) + || ep->out_overflow)) { + req = 0; + break; + } + req = list_entry (ep->queue.next, + struct net2280_request, queue); + + /* here either (M < N), a "real" short rx; + * or (M == N) and the queue didn't empty */ - while (count && (t & (1 << FIFO_EMPTY)) == 0) { - cpu_relax (); - t = readl (&ep->regs->ep_stat); + if (likely (t & (1 << FIFO_EMPTY))) { count = readl (&ep->dma->dmacount); count &= DMA_BYTE_COUNT_MASK; + if (readl (&ep->dma->dmadesc) + != req->td_dma) + req = 0; + break; } + udelay(1); } /* stop DMA, leave ep NAKing */ writel ((1 << DMA_ABORT), &ep->dma->dmastat); spin_stop_dma (ep->dma); - /* buffer might have been too small */ - t = readl (&ep->regs->ep_avail); - if (t != 0) - DEBUG (ep->dev, "%s dma, discard %d len %d\n", - ep->ep.name, t, count); - dma_done (ep, req, count, t ? -EOVERFLOW : 0); + if (likely (req != 0)) { + req->td->dmacount = 0; + t = readl (&ep->regs->ep_avail); + dma_done (ep, req, count, t); + } /* also flush to prevent erratum 0106 trouble */ - if (t || ep->dev->chiprev == 0x0100) + if (unlikely (ep->out_overflow + || (ep->dev->chiprev == 0x0100 + && ep->dev->gadget.speed + == USB_SPEED_FULL))) { out_flush (ep); + ep->out_overflow = 0; + } - /* restart dma (still NAKing OUT!) if needed */ + /* (re)start dma if needed, stop NAKing */ + ep->stopped = stopped; if (!list_empty (&ep->queue)) restart_dma (ep); } else @@ -2184,11 +2344,12 @@ * that'll mean a lot less irqs for some drivers. */ ep->is_in = (u.r.bRequestType & USB_DIR_IN) != 0; - if (ep->is_in) + if (ep->is_in) { scratch = (1 << DATA_PACKET_TRANSMITTED_INTERRUPT) | (1 << DATA_OUT_PING_TOKEN_INTERRUPT) | (1 << DATA_IN_TOKEN_INTERRUPT); - else + stop_out_naking (ep); + } else scratch = (1 << DATA_PACKET_RECEIVED_INTERRUPT) | (1 << DATA_OUT_PING_TOKEN_INTERRUPT) | (1 << DATA_IN_TOKEN_INTERRUPT); @@ -2390,18 +2551,18 @@ tmp = readl (&dma->dmastat); writel (tmp, &dma->dmastat); -#ifdef USE_DMA_CHAINING - /* chaining should stop only on error (which?) + /* chaining should stop on abort, short OUT from fifo, * or (stat0 codepath) short OUT transfer. */ -#else - if ((tmp & (1 << DMA_TRANSACTION_DONE_INTERRUPT)) == 0) { - DEBUG (ep->dev, "%s no xact done? %08x\n", - ep->ep.name, tmp); - continue; + if (!use_dma_chaining) { + if ((tmp & (1 << DMA_TRANSACTION_DONE_INTERRUPT)) + == 0) { + DEBUG (ep->dev, "%s no xact done? %08x\n", + ep->ep.name, tmp); + continue; + } + stop_dma (ep->dma); } - stop_dma (ep->dma); -#endif /* OUT transfers terminate when the data from the * host is in our memory. Process whatever's done. @@ -2417,16 +2578,14 @@ /* disable dma on inactive queues; else maybe restart */ if (list_empty (&ep->queue)) { -#ifdef USE_DMA_CHAINING - stop_dma (ep->dma); -#endif + if (use_dma_chaining) + stop_dma (ep->dma); } else { tmp = readl (&dma->dmactl); - if ((tmp & (1 << DMA_SCATTER_GATHER_ENABLE)) == 0 + if (!use_dma_chaining || (tmp & (1 << DMA_ENABLE)) == 0) restart_dma (ep); -#ifdef USE_DMA_CHAINING - else if (ep->desc->bEndpointAddress & USB_DIR_IN) { + else if (ep->is_in && use_dma_chaining) { struct net2280_request *req; u32 dmacount; @@ -2441,12 +2600,9 @@ dmacount &= __constant_cpu_to_le32 ( (1 << VALID_BIT) | DMA_BYTE_COUNT_MASK); - if (dmacount && (dmacount & valid_bit) == 0) { - stop_dma (ep->dma); + if (dmacount && (dmacount & valid_bit) == 0) restart_dma (ep); - } } -#endif } ep->irqs++; } @@ -2646,20 +2802,20 @@ } td->dmacount = 0; /* not VALID */ td->dmaaddr = __constant_cpu_to_le32 (DMA_ADDR_INVALID); + td->dmadesc = td->dmaaddr; dev->ep [i].dummy = td; } /* enable lower-overhead pci memory bursts during DMA */ - writel ((1 << PCI_RETRY_ABORT_ENABLE) - | (1 << DMA_MEMORY_WRITE_AND_INVALIDATE_ENABLE) + writel ( (1 << DMA_MEMORY_WRITE_AND_INVALIDATE_ENABLE) + // 256 write retries may not be enough... + // | (1 << PCI_RETRY_ABORT_ENABLE) | (1 << DMA_READ_MULTIPLE_ENABLE) | (1 << DMA_READ_LINE_ENABLE) , &dev->pci->pcimstctl); /* erratum 0115 shouldn't appear: Linux inits PCI_LATENCY_TIMER */ pci_set_master (pdev); -#ifdef HAVE_PCI_SET_MWI pci_set_mwi (pdev); -#endif /* ... also flushes any posted pci writes */ dev->chiprev = get_idx_reg (dev->regs, REG_CHIPREV) & 0xffff; @@ -2669,15 +2825,10 @@ INFO (dev, "%s\n", driver_desc); INFO (dev, "irq %s, pci mem %p, chip rev %04x\n", bufp, base, dev->chiprev); - bufp = DRIVER_VERSION -#ifndef USE_DMA_CHAINING - " (no dma chain)" -#endif -#ifdef NET2280_DMA_OUT_WORKAROUND - " (no dma out)" -#endif - ; - INFO (dev, "version: %s\n", bufp); + INFO (dev, "version: " DRIVER_VERSION "; dma %s\n", + use_dma + ? (use_dma_chaining ? "chaining" : "enabled") + : "disabled"); the_controller = dev; return 0; @@ -2720,6 +2871,8 @@ static int __init init (void) { + if (!use_dma) + use_dma_chaining = 0; return pci_module_init (&net2280_pci_driver); } module_init (init); diff -Nru a/drivers/usb/gadget/net2280.h b/drivers/usb/gadget/net2280.h --- a/drivers/usb/gadget/net2280.h Wed Jan 28 13:36:30 2004 +++ b/drivers/usb/gadget/net2280.h Wed Jan 28 13:36:30 2004 @@ -520,6 +520,7 @@ unsigned num : 8, fifo_size : 12, in_fifo_validate : 1, + out_overflow : 1, stopped : 1, is_in : 1, is_iso : 1; @@ -529,6 +530,7 @@ { /* ep0 only */ writel ( (1 << CLEAR_CONTROL_STATUS_PHASE_HANDSHAKE) + | (1 << CLEAR_NAK_OUT_PACKETS) | (1 << CLEAR_NAK_OUT_PACKETS_MODE) , &ep->regs->ep_rsp); ep->stopped = 1; @@ -546,7 +548,6 @@ dma_addr_t td_dma; struct list_head queue; unsigned mapped : 1, - dma_done : 1, valid : 1; }; @@ -559,8 +560,7 @@ unsigned enabled : 1, protocol_stall : 1, got_irq : 1, - region : 1, - selfpowered : 1; + region : 1; u16 chiprev; /* pci state used to access those endpoints */