⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 net2280.c

📁 linux-2.4.29操作系统的源码
💻 C
📖 第 1 页 / 共 5 页
字号:
/* * 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 (&regs->pciirqenb0);		tmp &= ~(1 << ep->num);		writel (tmp, &regs->pciirqenb0);	} else {		tmp = readl (&regs->pciirqenb1);		tmp &= ~(1 << (8 + ep->num));	/* completion */		writel (tmp, &regs->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 + -