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

📄 amd5536udc.c

📁 linux 内核源代码
💻 C
📖 第 1 页 / 共 5 页
字号:
/* * amd5536.c -- AMD 5536 UDC high/full speed USB device controller * * Copyright (C) 2005-2007 AMD (http://www.amd.com) * Author: Thomas Dahlmann * * 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 *//* * The AMD5536 UDC is part of the x86 southbridge AMD Geode CS5536. * It is a USB Highspeed DMA capable USB device controller. Beside ep0 it * provides 4 IN and 4 OUT endpoints (bulk or interrupt type). * * Make sure that UDC is assigned to port 4 by BIOS settings (port can also * be used as host port) and UOC bits PAD_EN and APU are set (should be done * by BIOS init). * * UDC DMA requires 32-bit aligned buffers so DMA with gadget ether does not * work without updating NET_IP_ALIGN. Or PIO mode (module param "use_dma=0") * can be used with gadget ether. *//* debug control *//* #define UDC_VERBOSE *//* Driver strings */#define UDC_MOD_DESCRIPTION		"AMD 5536 UDC - USB Device Controller"#define UDC_DRIVER_VERSION_STRING	"01.00.0206 - $Revision: #3 $"/* system */#include <linux/module.h>#include <linux/pci.h>#include <linux/kernel.h>#include <linux/version.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/ioctl.h>#include <linux/fs.h>#include <linux/dmapool.h>#include <linux/moduleparam.h>#include <linux/device.h>#include <linux/io.h>#include <linux/irq.h>#include <asm/byteorder.h>#include <asm/system.h>#include <asm/unaligned.h>/* gadget stack */#include <linux/usb/ch9.h>#include <linux/usb/gadget.h>/* udc specific */#include "amd5536udc.h"static void udc_tasklet_disconnect(unsigned long);static void empty_req_queue(struct udc_ep *);static int udc_probe(struct udc *dev);static void udc_basic_init(struct udc *dev);static void udc_setup_endpoints(struct udc *dev);static void udc_soft_reset(struct udc *dev);static struct udc_request *udc_alloc_bna_dummy(struct udc_ep *ep);static void udc_free_request(struct usb_ep *usbep, struct usb_request *usbreq);static int udc_free_dma_chain(struct udc *dev, struct udc_request *req);static int udc_create_dma_chain(struct udc_ep *ep, struct udc_request *req,				unsigned long buf_len, gfp_t gfp_flags);static int udc_remote_wakeup(struct udc *dev);static int udc_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id);static void udc_pci_remove(struct pci_dev *pdev);/* description */static const char mod_desc[] = UDC_MOD_DESCRIPTION;static const char name[] = "amd5536udc";/* structure to hold endpoint function pointers */static const struct usb_ep_ops udc_ep_ops;/* received setup data */static union udc_setup_data setup_data;/* pointer to device object */static struct udc *udc;/* irq spin lock for soft reset */static DEFINE_SPINLOCK(udc_irq_spinlock);/* stall spin lock */static DEFINE_SPINLOCK(udc_stall_spinlock);/** slave mode: pending bytes in rx fifo after nyet,* used if EPIN irq came but no req was available*/static unsigned int udc_rxfifo_pending;/* count soft resets after suspend to avoid loop */static int soft_reset_occured;static int soft_reset_after_usbreset_occured;/* timer */static struct timer_list udc_timer;static int stop_timer;/* set_rde -- Is used to control enabling of RX DMA. Problem is * that UDC has only one bit (RDE) to enable/disable RX DMA for * all OUT endpoints. So we have to handle race conditions like * when OUT data reaches the fifo but no request was queued yet. * This cannot be solved by letting the RX DMA disabled until a * request gets queued because there may be other OUT packets * in the FIFO (important for not blocking control traffic). * The value of set_rde controls the correspondig timer. * * set_rde -1 == not used, means it is alloed to be set to 0 or 1 * set_rde  0 == do not touch RDE, do no start the RDE timer * set_rde  1 == timer function will look whether FIFO has data * set_rde  2 == set by timer function to enable RX DMA on next call */static int set_rde = -1;static DECLARE_COMPLETION(on_exit);static struct timer_list udc_pollstall_timer;static int stop_pollstall_timer;static DECLARE_COMPLETION(on_pollstall_exit);/* tasklet for usb disconnect */static DECLARE_TASKLET(disconnect_tasklet, udc_tasklet_disconnect,		(unsigned long) &udc);/* endpoint names used for print */static const char ep0_string[] = "ep0in";static const char *ep_string[] = {	ep0_string,	"ep1in-int", "ep2in-bulk", "ep3in-bulk", "ep4in-bulk", "ep5in-bulk",	"ep6in-bulk", "ep7in-bulk", "ep8in-bulk", "ep9in-bulk", "ep10in-bulk",	"ep11in-bulk", "ep12in-bulk", "ep13in-bulk", "ep14in-bulk",	"ep15in-bulk", "ep0out", "ep1out-bulk", "ep2out-bulk", "ep3out-bulk",	"ep4out-bulk", "ep5out-bulk", "ep6out-bulk", "ep7out-bulk",	"ep8out-bulk", "ep9out-bulk", "ep10out-bulk", "ep11out-bulk",	"ep12out-bulk", "ep13out-bulk", "ep14out-bulk", "ep15out-bulk"};/* DMA usage flag */static int use_dma = 1;/* packet per buffer dma */static int use_dma_ppb = 1;/* with per descr. update */static int use_dma_ppb_du;/* buffer fill mode */static int use_dma_bufferfill_mode;/* full speed only mode */static int use_fullspeed;/* tx buffer size for high speed */static unsigned long hs_tx_buf = UDC_EPIN_BUFF_SIZE;/* module parameters */module_param(use_dma, bool, S_IRUGO);MODULE_PARM_DESC(use_dma, "true for DMA");module_param(use_dma_ppb, bool, S_IRUGO);MODULE_PARM_DESC(use_dma_ppb, "true for DMA in packet per buffer mode");module_param(use_dma_ppb_du, bool, S_IRUGO);MODULE_PARM_DESC(use_dma_ppb_du,	"true for DMA in packet per buffer mode with descriptor update");module_param(use_fullspeed, bool, S_IRUGO);MODULE_PARM_DESC(use_fullspeed, "true for fullspeed only");/*---------------------------------------------------------------------------*//* Prints UDC device registers and endpoint irq registers */static void print_regs(struct udc *dev){	DBG(dev, "------- Device registers -------\n");	DBG(dev, "dev config     = %08x\n", readl(&dev->regs->cfg));	DBG(dev, "dev control    = %08x\n", readl(&dev->regs->ctl));	DBG(dev, "dev status     = %08x\n", readl(&dev->regs->sts));	DBG(dev, "\n");	DBG(dev, "dev int's      = %08x\n", readl(&dev->regs->irqsts));	DBG(dev, "dev intmask    = %08x\n", readl(&dev->regs->irqmsk));	DBG(dev, "\n");	DBG(dev, "dev ep int's   = %08x\n", readl(&dev->regs->ep_irqsts));	DBG(dev, "dev ep intmask = %08x\n", readl(&dev->regs->ep_irqmsk));	DBG(dev, "\n");	DBG(dev, "USE DMA        = %d\n", use_dma);	if (use_dma && use_dma_ppb && !use_dma_ppb_du) {		DBG(dev, "DMA mode       = PPBNDU (packet per buffer "			"WITHOUT desc. update)\n");		dev_info(&dev->pdev->dev, "DMA mode (%s)\n", "PPBNDU");	} else if (use_dma && use_dma_ppb_du && use_dma_ppb_du) {		DBG(dev, "DMA mode       = PPBDU (packet per buffer "			"WITH desc. update)\n");		dev_info(&dev->pdev->dev, "DMA mode (%s)\n", "PPBDU");	}	if (use_dma && use_dma_bufferfill_mode) {		DBG(dev, "DMA mode       = BF (buffer fill mode)\n");		dev_info(&dev->pdev->dev, "DMA mode (%s)\n", "BF");	}	if (!use_dma) {		dev_info(&dev->pdev->dev, "FIFO mode\n");	}	DBG(dev, "-------------------------------------------------------\n");}/* Masks unused interrupts */static int udc_mask_unused_interrupts(struct udc *dev){	u32 tmp;	/* mask all dev interrupts */	tmp =	AMD_BIT(UDC_DEVINT_SVC) |		AMD_BIT(UDC_DEVINT_ENUM) |		AMD_BIT(UDC_DEVINT_US) |		AMD_BIT(UDC_DEVINT_UR) |		AMD_BIT(UDC_DEVINT_ES) |		AMD_BIT(UDC_DEVINT_SI) |		AMD_BIT(UDC_DEVINT_SOF)|		AMD_BIT(UDC_DEVINT_SC);	writel(tmp, &dev->regs->irqmsk);	/* mask all ep interrupts */	writel(UDC_EPINT_MSK_DISABLE_ALL, &dev->regs->ep_irqmsk);	return 0;}/* Enables endpoint 0 interrupts */static int udc_enable_ep0_interrupts(struct udc *dev){	u32 tmp;	DBG(dev, "udc_enable_ep0_interrupts()\n");	/* read irq mask */	tmp = readl(&dev->regs->ep_irqmsk);	/* enable ep0 irq's */	tmp &= AMD_UNMASK_BIT(UDC_EPINT_IN_EP0)		& AMD_UNMASK_BIT(UDC_EPINT_OUT_EP0);	writel(tmp, &dev->regs->ep_irqmsk);	return 0;}/* Enables device interrupts for SET_INTF and SET_CONFIG */static int udc_enable_dev_setup_interrupts(struct udc *dev){	u32 tmp;	DBG(dev, "enable device interrupts for setup data\n");	/* read irq mask */	tmp = readl(&dev->regs->irqmsk);	/* enable SET_INTERFACE, SET_CONFIG and other needed irq's */	tmp &= AMD_UNMASK_BIT(UDC_DEVINT_SI)		& AMD_UNMASK_BIT(UDC_DEVINT_SC)		& AMD_UNMASK_BIT(UDC_DEVINT_UR)		& AMD_UNMASK_BIT(UDC_DEVINT_SVC)		& AMD_UNMASK_BIT(UDC_DEVINT_ENUM);	writel(tmp, &dev->regs->irqmsk);	return 0;}/* Calculates fifo start of endpoint based on preceeding endpoints */static int udc_set_txfifo_addr(struct udc_ep *ep){	struct udc	*dev;	u32 tmp;	int i;	if (!ep || !(ep->in))		return -EINVAL;	dev = ep->dev;	ep->txfifo = dev->txfifo;	/* traverse ep's */	for (i = 0; i < ep->num; i++) {		if (dev->ep[i].regs) {			/* read fifo size */			tmp = readl(&dev->ep[i].regs->bufin_framenum);			tmp = AMD_GETBITS(tmp, UDC_EPIN_BUFF_SIZE);			ep->txfifo += tmp;		}	}	return 0;}/* CNAK pending field: bit0 = ep0in, bit16 = ep0out */static u32 cnak_pending;static void UDC_QUEUE_CNAK(struct udc_ep *ep, unsigned num){	if (readl(&ep->regs->ctl) & AMD_BIT(UDC_EPCTL_NAK)) {		DBG(ep->dev, "NAK could not be cleared for ep%d\n", num);		cnak_pending |= 1 << (num);		ep->naking = 1;	} else		cnak_pending = cnak_pending & (~(1 << (num)));}/* Enables endpoint, is called by gadget driver */static intudc_ep_enable(struct usb_ep *usbep, const struct usb_endpoint_descriptor *desc){	struct udc_ep		*ep;	struct udc		*dev;	u32			tmp;	unsigned long		iflags;	u8 udc_csr_epix;	if (!usbep			|| usbep->name == ep0_string			|| !desc			|| desc->bDescriptorType != USB_DT_ENDPOINT)		return -EINVAL;	ep = container_of(usbep, struct udc_ep, ep);	dev = ep->dev;	DBG(dev, "udc_ep_enable() ep %d\n", ep->num);	if (!dev->driver || dev->gadget.speed == USB_SPEED_UNKNOWN)		return -ESHUTDOWN;	spin_lock_irqsave(&dev->lock, iflags);	ep->desc = desc;	ep->halted = 0;	/* set traffic type */	tmp = readl(&dev->ep[ep->num].regs->ctl);	tmp = AMD_ADDBITS(tmp, desc->bmAttributes, UDC_EPCTL_ET);	writel(tmp, &dev->ep[ep->num].regs->ctl);	/* set max packet size */	tmp = readl(&dev->ep[ep->num].regs->bufout_maxpkt);	tmp = AMD_ADDBITS(tmp, desc->wMaxPacketSize, UDC_EP_MAX_PKT_SIZE);	ep->ep.maxpacket = desc->wMaxPacketSize;	writel(tmp, &dev->ep[ep->num].regs->bufout_maxpkt);	/* IN ep */	if (ep->in) {		/* ep ix in UDC CSR register space */		udc_csr_epix = ep->num;		/* set buffer size (tx fifo entries) */		tmp = readl(&dev->ep[ep->num].regs->bufin_framenum);		/* double buffering: fifo size = 2 x max packet size */		tmp = AMD_ADDBITS(				tmp,				desc->wMaxPacketSize * UDC_EPIN_BUFF_SIZE_MULT						/ UDC_DWORD_BYTES,				UDC_EPIN_BUFF_SIZE);		writel(tmp, &dev->ep[ep->num].regs->bufin_framenum);		/* calc. tx fifo base addr */		udc_set_txfifo_addr(ep);		/* flush fifo */		tmp = readl(&ep->regs->ctl);		tmp |= AMD_BIT(UDC_EPCTL_F);		writel(tmp, &ep->regs->ctl);	/* OUT ep */	} else {		/* ep ix in UDC CSR register space */		udc_csr_epix = ep->num - UDC_CSR_EP_OUT_IX_OFS;		/* set max packet size UDC CSR	*/		tmp = readl(&dev->csr->ne[ep->num - UDC_CSR_EP_OUT_IX_OFS]);		tmp = AMD_ADDBITS(tmp, desc->wMaxPacketSize,					UDC_CSR_NE_MAX_PKT);		writel(tmp, &dev->csr->ne[ep->num - UDC_CSR_EP_OUT_IX_OFS]);		if (use_dma && !ep->in) {			/* alloc and init BNA dummy request */			ep->bna_dummy_req = udc_alloc_bna_dummy(ep);			ep->bna_occurred = 0;		}		if (ep->num != UDC_EP0OUT_IX)			dev->data_ep_enabled = 1;	}	/* set ep values */	tmp = readl(&dev->csr->ne[udc_csr_epix]);	/* max packet */	tmp = AMD_ADDBITS(tmp, desc->wMaxPacketSize, UDC_CSR_NE_MAX_PKT);	/* ep number */	tmp = AMD_ADDBITS(tmp, desc->bEndpointAddress, UDC_CSR_NE_NUM);	/* ep direction */	tmp = AMD_ADDBITS(tmp, ep->in, UDC_CSR_NE_DIR);	/* ep type */	tmp = AMD_ADDBITS(tmp, desc->bmAttributes, UDC_CSR_NE_TYPE);	/* ep config */	tmp = AMD_ADDBITS(tmp, ep->dev->cur_config, UDC_CSR_NE_CFG);	/* ep interface */	tmp = AMD_ADDBITS(tmp, ep->dev->cur_intf, UDC_CSR_NE_INTF);	/* ep alt */	tmp = AMD_ADDBITS(tmp, ep->dev->cur_alt, UDC_CSR_NE_ALT);	/* write reg */	writel(tmp, &dev->csr->ne[udc_csr_epix]);	/* enable ep irq */	tmp = readl(&dev->regs->ep_irqmsk);	tmp &= AMD_UNMASK_BIT(ep->num);	writel(tmp, &dev->regs->ep_irqmsk);	/*	 * clear NAK by writing CNAK	 * avoid BNA for OUT DMA, don't clear NAK until DMA desc. written	 */	if (!use_dma || ep->in) {		tmp = readl(&ep->regs->ctl);		tmp |= AMD_BIT(UDC_EPCTL_CNAK);		writel(tmp, &ep->regs->ctl);		ep->naking = 0;		UDC_QUEUE_CNAK(ep, ep->num);	}	tmp = desc->bEndpointAddress;	DBG(dev, "%s enabled\n", usbep->name);	spin_unlock_irqrestore(&dev->lock, iflags);	return 0;}/* Resets endpoint */static void ep_init(struct udc_regs __iomem *regs, struct udc_ep *ep){	u32		tmp;	VDBG(ep->dev, "ep-%d reset\n", ep->num);	ep->desc = NULL;	ep->ep.ops = &udc_ep_ops;	INIT_LIST_HEAD(&ep->queue);	ep->ep.maxpacket = (u16) ~0;	/* set NAK */	tmp = readl(&ep->regs->ctl);	tmp |= AMD_BIT(UDC_EPCTL_SNAK);	writel(tmp, &ep->regs->ctl);	ep->naking = 1;	/* disable interrupt */	tmp = readl(&regs->ep_irqmsk);	tmp |= AMD_BIT(ep->num);	writel(tmp, &regs->ep_irqmsk);	if (ep->in) {		/* unset P and IN bit of potential former DMA */		tmp = readl(&ep->regs->ctl);		tmp &= AMD_UNMASK_BIT(UDC_EPCTL_P);		writel(tmp, &ep->regs->ctl);		tmp = readl(&ep->regs->sts);		tmp |= AMD_BIT(UDC_EPSTS_IN);		writel(tmp, &ep->regs->sts);		/* flush the fifo */		tmp = readl(&ep->regs->ctl);		tmp |= AMD_BIT(UDC_EPCTL_F);		writel(tmp, &ep->regs->ctl);	}	/* reset desc pointer */	writel(0, &ep->regs->desptr);}/* Disables endpoint, is called by gadget driver */static int udc_ep_disable(struct usb_ep *usbep){	struct udc_ep	*ep = NULL;	unsigned long	iflags;	if (!usbep)		return -EINVAL;	ep = container_of(usbep, struct udc_ep, ep);	if (usbep->name == ep0_string || !ep->desc)		return -EINVAL;	DBG(ep->dev, "Disable ep-%d\n", ep->num);	spin_lock_irqsave(&ep->dev->lock, iflags);	udc_free_request(&ep->ep, &ep->bna_dummy_req->req);	empty_req_queue(ep);	ep_init(ep->dev->regs, ep);	spin_unlock_irqrestore(&ep->dev->lock, iflags);	return 0;}/* Allocates request packet, called by gadget driver */static struct usb_request *udc_alloc_request(struct usb_ep *usbep, gfp_t gfp)

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -