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

📄 lh7a40x_udc.c

📁 linux-2.6.15.6
💻 C
📖 第 1 页 / 共 4 页
字号:
/* * linux/drivers/usb/gadget/lh7a40x_udc.c * Sharp LH7A40x on-chip full speed USB device controllers * * Copyright (C) 2004 Mikko Lahteenmaki, Nordic ID * Copyright (C) 2004 Bo Henriksen, Nordic ID * * 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 * */#include <linux/platform_device.h>#include "lh7a40x_udc.h"//#define DEBUG printk//#define DEBUG_EP0 printk//#define DEBUG_SETUP printk#ifndef DEBUG_EP0# define DEBUG_EP0(fmt,args...)#endif#ifndef DEBUG_SETUP# define DEBUG_SETUP(fmt,args...)#endif#ifndef DEBUG# define NO_STATES# define DEBUG(fmt,args...)#endif#define	DRIVER_DESC			"LH7A40x USB Device Controller"#define	DRIVER_VERSION		__DATE__#ifndef _BIT			/* FIXME - what happended to _BIT in 2.6.7bk18? */#define _BIT(x) (1<<(x))#endifstruct lh7a40x_udc *the_controller;static const char driver_name[] = "lh7a40x_udc";static const char driver_desc[] = DRIVER_DESC;static const char ep0name[] = "ep0-control";/*  Local definintions.*/#ifndef NO_STATESstatic char *state_names[] = {	"WAIT_FOR_SETUP",	"DATA_STATE_XMIT",	"DATA_STATE_NEED_ZLP",	"WAIT_FOR_OUT_STATUS",	"DATA_STATE_RECV"};#endif/*  Local declarations.*/static int lh7a40x_ep_enable(struct usb_ep *ep,			     const struct usb_endpoint_descriptor *);static int lh7a40x_ep_disable(struct usb_ep *ep);static struct usb_request *lh7a40x_alloc_request(struct usb_ep *ep, gfp_t);static void lh7a40x_free_request(struct usb_ep *ep, struct usb_request *);static void *lh7a40x_alloc_buffer(struct usb_ep *ep, unsigned, dma_addr_t *,				  gfp_t);static void lh7a40x_free_buffer(struct usb_ep *ep, void *, dma_addr_t,				unsigned);static int lh7a40x_queue(struct usb_ep *ep, struct usb_request *, gfp_t);static int lh7a40x_dequeue(struct usb_ep *ep, struct usb_request *);static int lh7a40x_set_halt(struct usb_ep *ep, int);static int lh7a40x_fifo_status(struct usb_ep *ep);static int lh7a40x_fifo_status(struct usb_ep *ep);static void lh7a40x_fifo_flush(struct usb_ep *ep);static void lh7a40x_ep0_kick(struct lh7a40x_udc *dev, struct lh7a40x_ep *ep);static void lh7a40x_handle_ep0(struct lh7a40x_udc *dev, u32 intr);static void done(struct lh7a40x_ep *ep, struct lh7a40x_request *req,		 int status);static void pio_irq_enable(int bEndpointAddress);static void pio_irq_disable(int bEndpointAddress);static void stop_activity(struct lh7a40x_udc *dev,			  struct usb_gadget_driver *driver);static void flush(struct lh7a40x_ep *ep);static void udc_enable(struct lh7a40x_udc *dev);static void udc_set_address(struct lh7a40x_udc *dev, unsigned char address);static struct usb_ep_ops lh7a40x_ep_ops = {	.enable = lh7a40x_ep_enable,	.disable = lh7a40x_ep_disable,	.alloc_request = lh7a40x_alloc_request,	.free_request = lh7a40x_free_request,	.alloc_buffer = lh7a40x_alloc_buffer,	.free_buffer = lh7a40x_free_buffer,	.queue = lh7a40x_queue,	.dequeue = lh7a40x_dequeue,	.set_halt = lh7a40x_set_halt,	.fifo_status = lh7a40x_fifo_status,	.fifo_flush = lh7a40x_fifo_flush,};/* Inline code */static __inline__ int write_packet(struct lh7a40x_ep *ep,				   struct lh7a40x_request *req, int max){	u8 *buf;	int length, count;	volatile u32 *fifo = (volatile u32 *)ep->fifo;	buf = req->req.buf + req->req.actual;	prefetch(buf);	length = req->req.length - req->req.actual;	length = min(length, max);	req->req.actual += length;	DEBUG("Write %d (max %d), fifo %p\n", length, max, fifo);	count = length;	while (count--) {		*fifo = *buf++;	}	return length;}static __inline__ void usb_set_index(u32 ep){	*(volatile u32 *)io_p2v(USB_INDEX) = ep;}static __inline__ u32 usb_read(u32 port){	return *(volatile u32 *)io_p2v(port);}static __inline__ void usb_write(u32 val, u32 port){	*(volatile u32 *)io_p2v(port) = val;}static __inline__ void usb_set(u32 val, u32 port){	volatile u32 *ioport = (volatile u32 *)io_p2v(port);	u32 after = (*ioport) | val;	*ioport = after;}static __inline__ void usb_clear(u32 val, u32 port){	volatile u32 *ioport = (volatile u32 *)io_p2v(port);	u32 after = (*ioport) & ~val;	*ioport = after;}/*-------------------------------------------------------------------------*/#define GPIO_PORTC_DR 	(0x80000E08)#define GPIO_PORTC_DDR 	(0x80000E18)#define GPIO_PORTC_PDR 	(0x80000E70)/* get port C pin data register */#define get_portc_pdr(bit) 		((usb_read(GPIO_PORTC_PDR) & _BIT(bit)) != 0)/* get port C data direction register */#define get_portc_ddr(bit) 		((usb_read(GPIO_PORTC_DDR) & _BIT(bit)) != 0)/* set port C data register */#define set_portc_dr(bit, val) 	(val ? usb_set(_BIT(bit), GPIO_PORTC_DR) : usb_clear(_BIT(bit), GPIO_PORTC_DR))/* set port C data direction register */#define set_portc_ddr(bit, val) (val ? usb_set(_BIT(bit), GPIO_PORTC_DDR) : usb_clear(_BIT(bit), GPIO_PORTC_DDR))/* * LPD7A404 GPIO's: * Port C bit 1 = USB Port 1 Power Enable * Port C bit 2 = USB Port 1 Data Carrier Detect */#define is_usb_connected() 		get_portc_pdr(2)#ifdef CONFIG_USB_GADGET_DEBUG_FILESstatic const char proc_node_name[] = "driver/udc";static intudc_proc_read(char *page, char **start, off_t off, int count,	      int *eof, void *_dev){	char *buf = page;	struct lh7a40x_udc *dev = _dev;	char *next = buf;	unsigned size = count;	unsigned long flags;	int t;	if (off != 0)		return 0;	local_irq_save(flags);	/* basic device status */	t = scnprintf(next, size,		      DRIVER_DESC "\n"		      "%s version: %s\n"		      "Gadget driver: %s\n"		      "Host: %s\n\n",		      driver_name, DRIVER_VERSION,		      dev->driver ? dev->driver->driver.name : "(none)",		      is_usb_connected()? "full speed" : "disconnected");	size -= t;	next += t;	t = scnprintf(next, size,		      "GPIO:\n"		      " Port C bit 1: %d, dir %d\n"		      " Port C bit 2: %d, dir %d\n\n",		      get_portc_pdr(1), get_portc_ddr(1),		      get_portc_pdr(2), get_portc_ddr(2)	    );	size -= t;	next += t;	t = scnprintf(next, size,		      "DCP pullup: %d\n\n",		      (usb_read(USB_PM) & PM_USB_DCP) != 0);	size -= t;	next += t;	local_irq_restore(flags);	*eof = 1;	return count - size;}#define create_proc_files() 	create_proc_read_entry(proc_node_name, 0, NULL, udc_proc_read, dev)#define remove_proc_files() 	remove_proc_entry(proc_node_name, NULL)#else	/* !CONFIG_USB_GADGET_DEBUG_FILES */#define create_proc_files() do {} while (0)#define remove_proc_files() do {} while (0)#endif	/* CONFIG_USB_GADGET_DEBUG_FILES *//* * 	udc_disable - disable USB device controller */static void udc_disable(struct lh7a40x_udc *dev){	DEBUG("%s, %p\n", __FUNCTION__, dev);	udc_set_address(dev, 0);	/* Disable interrupts */	usb_write(0, USB_IN_INT_EN);	usb_write(0, USB_OUT_INT_EN);	usb_write(0, USB_INT_EN);	/* Disable the USB */	usb_write(0, USB_PM);#ifdef CONFIG_ARCH_LH7A404	/* Disable USB power */	set_portc_dr(1, 0);#endif	/* if hardware supports it, disconnect from usb */	/* make_usb_disappear(); */	dev->ep0state = WAIT_FOR_SETUP;	dev->gadget.speed = USB_SPEED_UNKNOWN;	dev->usb_address = 0;}/* * 	udc_reinit - initialize software state */static void udc_reinit(struct lh7a40x_udc *dev){	u32 i;	DEBUG("%s, %p\n", __FUNCTION__, dev);	/* device/ep0 records init */	INIT_LIST_HEAD(&dev->gadget.ep_list);	INIT_LIST_HEAD(&dev->gadget.ep0->ep_list);	dev->ep0state = WAIT_FOR_SETUP;	/* basic endpoint records init */	for (i = 0; i < UDC_MAX_ENDPOINTS; i++) {		struct lh7a40x_ep *ep = &dev->ep[i];		if (i != 0)			list_add_tail(&ep->ep.ep_list, &dev->gadget.ep_list);		ep->desc = 0;		ep->stopped = 0;		INIT_LIST_HEAD(&ep->queue);		ep->pio_irqs = 0;	}	/* the rest was statically initialized, and is read-only */}#define BYTES2MAXP(x)	(x / 8)#define MAXP2BYTES(x)	(x * 8)/* until it's enabled, this UDC should be completely invisible * to any USB host. */static void udc_enable(struct lh7a40x_udc *dev){	int ep;	DEBUG("%s, %p\n", __FUNCTION__, dev);	dev->gadget.speed = USB_SPEED_UNKNOWN;#ifdef CONFIG_ARCH_LH7A404	/* Set Port C bit 1 & 2 as output */	set_portc_ddr(1, 1);	set_portc_ddr(2, 1);	/* Enable USB power */	set_portc_dr(1, 0);#endif	/*	 * C.f Chapter 18.1.3.1 Initializing the USB	 */	/* Disable the USB */	usb_clear(PM_USB_ENABLE, USB_PM);	/* Reset APB & I/O sides of the USB */	usb_set(USB_RESET_APB | USB_RESET_IO, USB_RESET);	mdelay(5);	usb_clear(USB_RESET_APB | USB_RESET_IO, USB_RESET);	/* Set MAXP values for each */	for (ep = 0; ep < UDC_MAX_ENDPOINTS; ep++) {		struct lh7a40x_ep *ep_reg = &dev->ep[ep];		u32 csr;		usb_set_index(ep);		switch (ep_reg->ep_type) {		case ep_bulk_in:		case ep_interrupt:			usb_clear(USB_IN_CSR2_USB_DMA_EN | USB_IN_CSR2_AUTO_SET,				  ep_reg->csr2);			/* Fall through */		case ep_control:			usb_write(BYTES2MAXP(ep_maxpacket(ep_reg)),				  USB_IN_MAXP);			break;		case ep_bulk_out:			usb_clear(USB_OUT_CSR2_USB_DMA_EN |				  USB_OUT_CSR2_AUTO_CLR, ep_reg->csr2);			usb_write(BYTES2MAXP(ep_maxpacket(ep_reg)),				  USB_OUT_MAXP);			break;		}		/* Read & Write CSR1, just in case */		csr = usb_read(ep_reg->csr1);		usb_write(csr, ep_reg->csr1);		flush(ep_reg);	}	/* Disable interrupts */	usb_write(0, USB_IN_INT_EN);	usb_write(0, USB_OUT_INT_EN);	usb_write(0, USB_INT_EN);	/* Enable interrupts */	usb_set(USB_IN_INT_EP0, USB_IN_INT_EN);	usb_set(USB_INT_RESET_INT | USB_INT_RESUME_INT, USB_INT_EN);	/* Dont enable rest of the interrupts */	/* usb_set(USB_IN_INT_EP3 | USB_IN_INT_EP1 | USB_IN_INT_EP0, USB_IN_INT_EN);	   usb_set(USB_OUT_INT_EP2, USB_OUT_INT_EN); */	/* Enable SUSPEND */	usb_set(PM_ENABLE_SUSPEND, USB_PM);	/* Enable the USB */	usb_set(PM_USB_ENABLE, USB_PM);#ifdef CONFIG_ARCH_LH7A404	/* NOTE: DOES NOT WORK! */	/* Let host detect UDC:	 * Software must write a 0 to the PMR:DCP_CTRL bit to turn this	 * transistor on and pull the USBDP pin HIGH.	 */	/* usb_clear(PM_USB_DCP, USB_PM);	   usb_set(PM_USB_DCP, USB_PM); */#endif}/*  Register entry point for the peripheral controller driver.*/int usb_gadget_register_driver(struct usb_gadget_driver *driver){	struct lh7a40x_udc *dev = the_controller;	int retval;	DEBUG("%s: %s\n", __FUNCTION__, driver->driver.name);	if (!driver	    || driver->speed != USB_SPEED_FULL	    || !driver->bind	    || !driver->unbind || !driver->disconnect || !driver->setup)		return -EINVAL;	if (!dev)		return -ENODEV;	if (dev->driver)		return -EBUSY;	/* first hook up the driver ... */	dev->driver = driver;	dev->gadget.dev.driver = &driver->driver;	device_add(&dev->gadget.dev);	retval = driver->bind(&dev->gadget);	if (retval) {		printk("%s: bind to driver %s --> error %d\n", dev->gadget.name,		       driver->driver.name, retval);		device_del(&dev->gadget.dev);		dev->driver = 0;		dev->gadget.dev.driver = 0;		return retval;	}	/* ... then enable host detection and ep0; and we're ready	 * for set_configuration as well as eventual disconnect.	 * NOTE:  this shouldn't power up until later.	 */	printk("%s: registered gadget driver '%s'\n", dev->gadget.name,	       driver->driver.name);	udc_enable(dev);	return 0;}EXPORT_SYMBOL(usb_gadget_register_driver);/*  Unregister entry point for the peripheral controller driver.*/int usb_gadget_unregister_driver(struct usb_gadget_driver *driver){	struct lh7a40x_udc *dev = the_controller;	unsigned long flags;	if (!dev)		return -ENODEV;	if (!driver || driver != dev->driver)		return -EINVAL;	spin_lock_irqsave(&dev->lock, flags);	dev->driver = 0;	stop_activity(dev, driver);	spin_unlock_irqrestore(&dev->lock, flags);	driver->unbind(&dev->gadget);	device_del(&dev->gadget.dev);	udc_disable(dev);	DEBUG("unregistered gadget driver '%s'\n", driver->driver.name);	return 0;}EXPORT_SYMBOL(usb_gadget_unregister_driver);/*-------------------------------------------------------------------------*//** Write request to FIFO (max write == maxp size) *  Return:  0 = still running, 1 = completed, negative = errno *  NOTE: INDEX register must be set for EP */static int write_fifo(struct lh7a40x_ep *ep, struct lh7a40x_request *req){	u32 max;	u32 csr;	max = le16_to_cpu(ep->desc->wMaxPacketSize);	csr = usb_read(ep->csr1);	DEBUG("CSR: %x %d\n", csr, csr & USB_IN_CSR1_FIFO_NOT_EMPTY);	if (!(csr & USB_IN_CSR1_FIFO_NOT_EMPTY)) {		unsigned count;		int is_last, is_short;		count = write_packet(ep, req, max);		usb_set(USB_IN_CSR1_IN_PKT_RDY, ep->csr1);		/* last packet is usually short (or a zlp) */		if (unlikely(count != max))			is_last = is_short = 1;		else {			if (likely(req->req.length != req->req.actual)			    || req->req.zero)				is_last = 0;			else				is_last = 1;			/* interrupt/iso maxpacket may not fill the fifo */			is_short = unlikely(max < ep_maxpacket(ep));		}		DEBUG("%s: wrote %s %d bytes%s%s %d left %p\n", __FUNCTION__,		      ep->ep.name, count,		      is_last ? "/L" : "", is_short ? "/S" : "",		      req->req.length - req->req.actual, req);		/* requests complete when all IN data is in the FIFO */		if (is_last) {			done(ep, req, 0);			if (list_empty(&ep->queue)) {				pio_irq_disable(ep_index(ep));			}			return 1;		}	} else {

⌨️ 快捷键说明

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