📄 net2280.c
字号:
/* * Driver for the NetChip 2280 USB device controller. * Specs and errata are available from <http://www.netchip.com>. * * NetChip Technology Inc. supported the development of this driver. * * * CODE STATUS HIGHLIGHTS * * Used with a gadget driver like "zero.c" this enumerates fine to Windows * or Linux hosts; handles disconnect, reconnect, and reset, for full or * high speed operation; and passes USB-IF "chapter 9" tests. * * Handles standard stress loads from the Linux "usbtest" driver, with * either DMA (default) or PIO (use_dma=n) used for ep-{a,b,c,d}. Testing * with "ttcp" (and the "ether.c" driver) behaves nicely too. * * DMA is enabled by default. Drivers using transfer queues might use * DMA chaining to remove IRQ latencies between transfers. (Except when * short OUT transfers happen.) Drivers can use the req->no_interrupt * hint to completely eliminate some IRQs, if a later IRQ is guaranteed * and DMA chaining is enabled. * * Note that almost all the errata workarounds here are only needed for * rev1 chips. Rev1a silicon (0110) fixes almost all of them. *//* * Copyright (C) 2003 David Brownell * Copyright (C) 2003 NetChip Technologies * * 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 */#undef DEBUG /* messages on error and most fault paths */#undef VERBOSE /* extra debug messages (success too) */#include <linux/config.h>#include <linux/module.h>#include <linux/pci.h>#include <linux/kernel.h>#include <linux/delay.h>#include <linux/ioport.h>#include <linux/sched.h>#include <linux/slab.h>#include <linux/smp_lock.h>#include <linux/errno.h>#include <linux/init.h>#include <linux/timer.h>#include <linux/list.h>#include <linux/interrupt.h>#include <linux/usb_ch9.h>#include <linux/usb_gadget.h>#include <asm/byteorder.h>#include <asm/io.h>#include <asm/irq.h>#include <asm/system.h>#include <asm/unaligned.h>#define DRIVER_DESC "NetChip 2280 USB Peripheral Controller"#define DRIVER_VERSION "2004 Jan 14"#define DMA_ADDR_INVALID (~(dma_addr_t)0)#define EP_DONTUSE 13 /* nonzero */#define USE_RDK_LEDS /* GPIO pins control three LEDs */static const char driver_name [] = "net2280";static const char driver_desc [] = DRIVER_DESC;static const char ep0name [] = "ep0";static const char *ep_name [] = { ep0name, "ep-a", "ep-b", "ep-c", "ep-d", "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 * mode 2 == ep-a 2K fifo, ep-{b,c} 1K each, ep-d unavailable */static ushort fifo_mode = 0;MODULE_PARM (fifo_mode, "h");MODULE_PARM_DESC (fifo_mode, "net2280 fifo mode");#define DIR_STRING(bAddress) (((bAddress) & USB_DIR_IN) ? "in" : "out")#if defined(USE_SYSFS_DEBUG_FILES) || defined (DEBUG)static char *type_string (u8 bmAttributes){ switch ((bmAttributes) & USB_ENDPOINT_XFERTYPE_MASK) { case USB_ENDPOINT_XFER_BULK: return "bulk"; case USB_ENDPOINT_XFER_ISOC: return "iso"; case USB_ENDPOINT_XFER_INT: return "intr"; }; return "control";}#endif#include "net2280.h"#define valid_bit __constant_cpu_to_le32 (1 << VALID_BIT)#define dma_done_ie __constant_cpu_to_le32 (1 << DMA_DONE_INTERRUPT_ENABLE)/*-------------------------------------------------------------------------*/static intnet2280_enable (struct usb_ep *_ep, const struct usb_endpoint_descriptor *desc){ struct net2280 *dev; struct net2280_ep *ep; u32 max, tmp; unsigned long flags; ep = container_of (_ep, struct net2280_ep, ep); if (!_ep || !desc || ep->desc || _ep->name == ep0name || desc->bDescriptorType != USB_DT_ENDPOINT) return -EINVAL; dev = ep->dev; if (!dev->driver || dev->gadget.speed == USB_SPEED_UNKNOWN) return -ESHUTDOWN; /* erratum 0119 workaround ties up an endpoint number */ if ((desc->bEndpointAddress & 0x0f) == EP_DONTUSE) return -EDOM; /* sanity check ep-e/ep-f since their fifos are small */ max = le16_to_cpu (desc->wMaxPacketSize) & 0x1fff; if (ep->num > 4 && max > 64) return -ERANGE; spin_lock_irqsave (&dev->lock, flags); _ep->maxpacket = max & 0x7ff; ep->desc = desc; /* 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); /* FIFO lines can't go to different packets. PIO is ok, so * use it instead of troublesome (non-bulk) multi-packet DMA. */ 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; } /* set type, direction, address; reset fifo counters */ writel ((1 << FIFO_FLUSH), &ep->regs->ep_stat); tmp = (desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK); if (tmp == USB_ENDPOINT_XFER_INT) { /* 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)) { spin_unlock_irqrestore (&dev->lock, flags); return -ERANGE; } } ep->is_iso = (tmp == USB_ENDPOINT_XFER_ISOC) ? 1 : 0; tmp <<= ENDPOINT_TYPE; tmp |= desc->bEndpointAddress; tmp |= (4 << ENDPOINT_BYTE_COUNT); /* default full fifo lines */ tmp |= 1 << ENDPOINT_ENABLE; wmb (); /* for OUT transfers, block the rx fifo until a read is posted */ ep->is_in = (tmp & USB_DIR_IN) != 0; if (!ep->is_in) writel ((1 << SET_NAK_OUT_PACKETS), &ep->regs->ep_rsp); writel (tmp, &ep->regs->ep_cfg); /* enable irqs */ if (!ep->dma) { /* pio, per-packet */ tmp = (1 << ep->num) | readl (&dev->regs->pciirqenb0); writel (tmp, &dev->regs->pciirqenb0); tmp = (1 << DATA_PACKET_RECEIVED_INTERRUPT_ENABLE) | (1 << DATA_PACKET_TRANSMITTED_INTERRUPT_ENABLE) | readl (&ep->regs->ep_irqenb); writel (tmp, &ep->regs->ep_irqenb); } else { /* dma, per-request */ tmp = (1 << (8 + ep->num)); /* completion */ tmp |= readl (&dev->regs->pciirqenb1); writel (tmp, &dev->regs->pciirqenb1); /* for short OUT transfers, dma completions can't * advance the queue; do it pio-style, by hand. * NOTE erratum 0112 workaround #2 */ if ((desc->bEndpointAddress & USB_DIR_IN) == 0) { tmp = (1 << SHORT_PACKET_TRANSFERRED_INTERRUPT_ENABLE); writel (tmp, &ep->regs->ep_irqenb); tmp = (1 << ep->num) | readl (&dev->regs->pciirqenb0); writel (tmp, &dev->regs->pciirqenb0); } } tmp = desc->bEndpointAddress; DEBUG (dev, "enabled %s (ep%d%s-%s) %s max %04x\n", _ep->name, tmp & 0x0f, DIR_STRING (tmp), type_string (desc->bmAttributes), ep->dma ? "dma" : "pio", max); /* pci writes may still be posted */ spin_unlock_irqrestore (&dev->lock, flags); return 0;}static int handshake (u32 *ptr, u32 mask, u32 done, int usec){ u32 result; do { result = readl (ptr); if (result == ~(u32)0) /* "device unplugged" */ return -ENODEV; result &= mask; if (result == done) return 0; udelay (1); usec--; } while (usec > 0); return -ETIMEDOUT;}static struct usb_ep_ops net2280_ep_ops;static void ep_reset (struct net2280_regs *regs, struct net2280_ep *ep){ u32 tmp; ep->desc = 0; INIT_LIST_HEAD (&ep->queue); ep->ep.maxpacket = ~0; ep->ep.ops = &net2280_ep_ops; /* disable the dma, irqs, endpoint... */ if (ep->dma) { writel (0, &ep->dma->dmactl); writel ( (1 << DMA_SCATTER_GATHER_DONE_INTERRUPT) | (1 << DMA_TRANSACTION_DONE_INTERRUPT) | (1 << DMA_ABORT) , &ep->dma->dmastat); tmp = readl (®s->pciirqenb0); tmp &= ~(1 << ep->num); writel (tmp, ®s->pciirqenb0); } else { tmp = readl (®s->pciirqenb1); tmp &= ~(1 << (8 + ep->num)); /* completion */ writel (tmp, ®s->pciirqenb1); } writel (0, &ep->regs->ep_irqenb); /* init to our chosen defaults, notably so that we NAK OUT * packets until the driver queues a read (+note erratum 0112) */ writel ( (1 << SET_NAK_OUT_PACKETS_MODE) | (1 << SET_NAK_OUT_PACKETS) | (1 << CLEAR_EP_HIDE_STATUS_PHASE) | (1 << CLEAR_INTERRUPT_MODE) | (1 << CLEAR_CONTROL_STATUS_PHASE_HANDSHAKE) | (1 << CLEAR_ENDPOINT_TOGGLE) | (1 << CLEAR_ENDPOINT_HALT) , &ep->regs->ep_rsp); /* scrub most status bits, and flush any fifo state */ writel ( (1 << TIMEOUT) | (1 << USB_STALL_SENT) | (1 << USB_IN_NAK_SENT) | (1 << USB_IN_ACK_RCVD) | (1 << USB_OUT_PING_NAK_SENT) | (1 << USB_OUT_ACK_SENT) | (1 << FIFO_OVERFLOW) | (1 << FIFO_UNDERFLOW) | (1 << FIFO_FLUSH) | (1 << SHORT_PACKET_OUT_DONE_INTERRUPT) | (1 << SHORT_PACKET_TRANSFERRED_INTERRUPT) | (1 << DATA_PACKET_RECEIVED_INTERRUPT) | (1 << DATA_PACKET_TRANSMITTED_INTERRUPT) | (1 << DATA_OUT_PING_TOKEN_INTERRUPT) | (1 << DATA_IN_TOKEN_INTERRUPT) , &ep->regs->ep_stat); /* fifo size is handled separately */}static void nuke (struct net2280_ep *);static int net2280_disable (struct usb_ep *_ep){ struct net2280_ep *ep; unsigned long flags; ep = container_of (_ep, struct net2280_ep, ep); if (!_ep || !ep->desc || _ep->name == ep0name) return -EINVAL; spin_lock_irqsave (&ep->dev->lock, flags); nuke (ep); ep_reset (ep->dev->regs, ep); VDEBUG (ep->dev, "disabled %s %s\n", ep->dma ? "dma" : "pio", _ep->name); /* synch memory views with the device */ (void) readl (&ep->regs->ep_cfg); if (use_dma && !ep->dma && ep->num >= 1 && ep->num <= 4) ep->dma = &ep->dev->dma [ep->num - 1]; spin_unlock_irqrestore (&ep->dev->lock, flags); return 0;}/*-------------------------------------------------------------------------*/static struct usb_request *net2280_alloc_request (struct usb_ep *_ep, int gfp_flags){ struct net2280_ep *ep; struct net2280_request *req; if (!_ep) return 0; ep = container_of (_ep, struct net2280_ep, ep); req = kmalloc (sizeof *req, gfp_flags); if (!req) return 0; memset (req, 0, sizeof *req); req->req.dma = DMA_ADDR_INVALID; INIT_LIST_HEAD (&req->queue); /* this dma descriptor may be swapped with the previous dummy */ if (ep->dma) { struct net2280_dma *td; td = pci_pool_alloc (ep->dev->requests, gfp_flags, &req->td_dma); if (!td) { kfree (req); return 0; } td->dmacount = 0; /* not VALID */ td->dmaaddr = __constant_cpu_to_le32 (DMA_ADDR_INVALID); td->dmadesc = td->dmaaddr; req->td = td; } return &req->req;}static voidnet2280_free_request (struct usb_ep *_ep, struct usb_request *_req){ struct net2280_ep *ep; struct net2280_request *req; ep = container_of (_ep, struct net2280_ep, ep); if (!_ep || !_req) return; req = container_of (_req, struct net2280_request, req); WARN_ON (!list_empty (&req->queue)); if (req->td) pci_pool_free (ep->dev->requests, req->td, req->td_dma); kfree (req);}/*-------------------------------------------------------------------------*/#undef USE_KMALLOC/* many common platforms have dma-coherent caches, which means that it's * safe to use kmalloc() memory for all i/o buffers without using any * cache flushing calls. (unless you're trying to share cache lines * between dma and non-dma activities, which is a slow idea in any case.) * * other platforms need more care, with 2.5 having a moderately general * solution (which falls down for allocations smaller than one page) * that improves significantly on the 2.4 PCI allocators by removing * the restriction that memory never be freed in_interrupt(). */#if defined(CONFIG_X86)#define USE_KMALLOC#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/* allocating buffers this way eliminates dma mapping overhead, which * on some platforms will mean eliminating a per-io buffer copy. with * some kinds of system caches, further tweaks may still be needed. */static void *net2280_alloc_buffer ( struct usb_ep *_ep, unsigned bytes, dma_addr_t *dma, int gfp_flags){ void *retval; struct net2280_ep *ep; ep = container_of (_ep, struct net2280_ep, ep); if (!_ep) return 0; *dma = DMA_ADDR_INVALID;#if defined(USE_KMALLOC) retval = kmalloc(bytes, gfp_flags); if (retval) *dma = virt_to_phys(retval);#else 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. */#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);#endif return retval;}static voidnet2280_free_buffer ( struct usb_ep *_ep, void *buf,
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -