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

📄 jz4740_udc.c

📁 linux下面gadget设备驱动
💻 C
📖 第 1 页 / 共 4 页
字号:
/* * linux/drivers/usb/gadget/jz4740_udc.c * * Ingenic JZ4740 on-chip high speed USB device controller * * Copyright (C) 2006 - 2008 Ingenic Semiconductor Inc. * Author: <jlwei@ingenic.cn> * * 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 device has ep0, two bulk-in/interrupt-in endpoints, and one bulk-out endpoint. * *  - Endpoint numbering is fixed: ep0, ep1in-int, ep2in-bulk, ep1out-bulk. *  - DMA works with bulk-in (channel 1) and bulk-out (channel 2) endpoints. */#include <linux/kernel.h>#include <linux/module.h>#include <linux/platform_device.h>#include <linux/delay.h>#include <linux/ioport.h>#include <linux/slab.h>#include <linux/errno.h>#include <linux/init.h>#include <linux/list.h>#include <linux/interrupt.h>#include <linux/proc_fs.h>#include <linux/usb.h>#include <linux/usb/gadget.h>#include <asm/byteorder.h>#include <asm/io.h>#include <asm/irq.h>#include <asm/system.h>#include <asm/jzsoc.h>#include "jz4740_udc.h"//#define DEBUG(fmt,args...) printk(KERN_DEBUG fmt , ## args)//#define DEBUG(fmt,args...) printk(fmt , ## args)//#define DEBUG_EP0(fmt,args...) printk(fmt , ## args)//#define DEBUG_SETUP(fmt,args...) printk(fmt , ## args)#ifndef DEBUG# define DEBUG(fmt,args...) do {} while(0)#endif#ifndef DEBUG_EP0# define NO_STATES# define DEBUG_EP0(fmt,args...) do {} while(0)#endif#ifndef DEBUG_SETUP# define DEBUG_SETUP(fmt,args...) do {} while(0)#endifstatic unsigned int udc_debug = 0; /* 0: normal mode, 1: test udc cable type mode */module_param(udc_debug, int, 0);MODULE_PARM_DESC(udc_debug, "test udc cable or power type");static unsigned int use_dma = 1;   /* 1: use DMA, 0: use PIO */module_param(use_dma, int, 0);MODULE_PARM_DESC(use_dma, "DMA mode enable flag");#ifdef CONFIG_JZ_UDC_HOTPLUGextern int jz_udc_active; /* 0: No actions; 1: Have actions */#endif/* *  Local definintions. */#define	DRIVER_VERSION		"13-Mar-2008"#define	DRIVER_DESC		"JZ4740 USB Device Controller"static const char	gadget_name [] = "jz4740_udc";struct jz4740_udc *the_controller;static const char driver_name [] = "jz4740_udc";static const char driver_desc [] = DRIVER_DESC;static const char ep0name[] = "ep0";#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 jz4740_ep_enable(struct usb_ep *_ep, 			    const struct usb_endpoint_descriptor *desc);static int jz4740_ep_disable(struct usb_ep *_ep);static struct usb_request *jz4740_alloc_request(struct usb_ep *_ep, gfp_t gfp_flags);static void jz4740_free_request(struct usb_ep *_ep, struct usb_request *_req);static int jz4740_queue(struct usb_ep *_ep, struct usb_request *_req, gfp_t gfp_flags);static int jz4740_dequeue(struct usb_ep *_ep, struct usb_request *_req);static int jz4740_set_halt(struct usb_ep *_ep, int value);static int jz4740_fifo_status(struct usb_ep *_ep);static void jz4740_fifo_flush(struct usb_ep *_ep);static void jz4740_ep0_kick(struct jz4740_udc *dev, struct jz4740_ep *ep);static void jz4740_handle_ep0(struct jz4740_udc *dev, u32 intr);static void done(struct jz4740_ep *ep, struct jz4740_request *req,		 int status);static void pio_irq_enable(struct jz4740_ep *ep);static void pio_irq_disable(struct jz4740_ep *ep);static void stop_activity(struct jz4740_udc *dev,			  struct usb_gadget_driver *driver);static void nuke(struct jz4740_ep *ep, int status);static void flush(struct jz4740_ep *ep);static void udc_enable(struct jz4740_udc *dev);static void udc_set_address(struct jz4740_udc *dev, unsigned char address);static void jz4740_udc_release (struct device *dev) {}extern void *dma_alloc_noncoherent(struct device *dev, size_t size,				   dma_addr_t *dma_handle, gfp_t flag);extern void dma_free_noncoherent(struct device *dev, size_t size,				 void *vaddr, dma_addr_t dma_handle);static struct usb_ep_ops jz4740_ep_ops = {	.enable		= jz4740_ep_enable,	.disable	= jz4740_ep_disable,	.alloc_request	= jz4740_alloc_request,	.free_request	= jz4740_free_request,	.queue		= jz4740_queue,	.dequeue	= jz4740_dequeue,	.set_halt	= jz4740_set_halt,	.fifo_status	= jz4740_fifo_status,	.fifo_flush	= jz4740_fifo_flush,};/*-------------------------------------------------------------------------*//* inline functions of register read/write/set/clear  */static __inline__ u8 usb_readb(u32 port){	return *(volatile u8 *)port;}static __inline__ u16 usb_readw(u32 port){	return *(volatile u16 *)port;}static __inline__ u32 usb_readl(u32 port){	return *(volatile u32 *)port;}static __inline__ void usb_writeb(u32 port, u8 val){	*(volatile u8 *)port = val;}static __inline__ void usb_writew(u32 port, u16 val){	*(volatile u16 *)port = val;}static __inline__ void usb_writel(u32 port, u32 val){	*(volatile u32 *)port = val;}static __inline__ void usb_setb(u32 port, u8 val){	volatile u8 *ioport = (volatile u8 *)(port);	*ioport = (*ioport) | val;}static __inline__ void usb_setw(u32 port, u16 val){	volatile u16 *ioport = (volatile u16 *)(port);	*ioport = (*ioport) | val;}static __inline__ void usb_setl(u32 port, u32 val){	volatile u32 *ioport = (volatile u32 *)(port);	*ioport = (*ioport) | val;}static __inline__ void usb_clearb(u32 port, u8 val){	volatile u8 *ioport = (volatile u8 *)(port);	*ioport = (*ioport) & ~val;}static __inline__ void usb_clearw(u32 port, u16 val){	volatile u16 *ioport = (volatile u16 *)(port);	*ioport = (*ioport) & ~val;}static __inline__ void usb_clearl(u32 port, u32 val){	volatile u32 *ioport = (volatile u32 *)(port);	*ioport = (*ioport) & ~val;}/*-------------------------------------------------------------------------*/static __inline__ int write_packet(struct jz4740_ep *ep,				   struct jz4740_request *req, int max){	u8 *buf;		int length, nlong, nbyte;	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);	nlong = length >> 2;	nbyte = length & 0x3;	while (nlong--) {		*fifo = *((u32 *)buf);		buf += 4;	}	while (nbyte--) {		*((volatile u8 *)fifo) = *buf++;	}	return length;}static __inline__ int read_packet(struct jz4740_ep *ep, 				  struct jz4740_request *req, int count){	u8 *buf;	int length, nlong, nbyte;	volatile u32 *fifo = (volatile u32 *)ep->fifo;	buf = req->req.buf + req->req.actual;	prefetchw(buf);	length = req->req.length - req->req.actual;	length = min(length, count);	req->req.actual += length;	DEBUG("Read %d, fifo %p\n", length, fifo);	nlong = length >> 2;	nbyte = length & 0x3;	while (nlong--) {		*((u32 *)buf) = *fifo;		buf += 4;	}	while (nbyte--) {		*buf++ = *((volatile u8 *)fifo);	}	return length;}/*-------------------------------------------------------------------------*//* * 	udc_disable - disable USB device controller */static void udc_disable(struct jz4740_udc *dev){	DEBUG("%s, %p\n", __FUNCTION__, dev);	udc_set_address(dev, 0);	/* Disable interrupts */	usb_writew(USB_REG_INTRINE, 0);	usb_writew(USB_REG_INTROUTE, 0);	usb_writeb(USB_REG_INTRUSBE, 0);	/* Disable DMA */	usb_writel(USB_REG_CNTL1, 0);	usb_writel(USB_REG_CNTL2, 0);	/* Disconnect from usb */	usb_clearb(USB_REG_POWER, USB_POWER_SOFTCONN);	/* Disable the USB PHY */	REG_CPM_SCR &= ~CPM_SCR_USBPHY_ENABLE;	dev->ep0state = WAIT_FOR_SETUP;	dev->gadget.speed = USB_SPEED_UNKNOWN;}/* * 	udc_reinit - initialize software state */static void udc_reinit(struct jz4740_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;	for (i = 0; i < UDC_MAX_ENDPOINTS; i++) {		struct jz4740_ep *ep = &dev->ep[i];		if (i != 0)			list_add_tail(&ep->ep.ep_list, &dev->gadget.ep_list);		INIT_LIST_HEAD(&ep->queue);		ep->desc = 0;		ep->stopped = 0;		ep->pio_irqs = 0;	}}/* until it's enabled, this UDC should be completely invisible * to any USB host. */static void udc_enable(struct jz4740_udc *dev){	int i;	DEBUG("%s, %p\n", __FUNCTION__, dev);	dev->gadget.speed = USB_SPEED_UNKNOWN;	/* Flush FIFO for each */	for (i = 0; i < UDC_MAX_ENDPOINTS; i++) {		struct jz4740_ep *ep = &dev->ep[i];		usb_set_index(ep_index(ep));		flush(ep);	}	/* Set this bit to allow the UDC entering low-power mode when	 * there are no actions on the USB bus.	 * UDC still works during this bit was set.	 */	__cpm_stop_udc();	/* Enable the USB PHY */	REG_CPM_SCR |= CPM_SCR_USBPHY_ENABLE;	/* Disable interrupts */	usb_writew(USB_REG_INTRINE, 0);	usb_writew(USB_REG_INTROUTE, 0);	usb_writeb(USB_REG_INTRUSBE, 0);	/* Enable interrupts */	usb_setw(USB_REG_INTRINE, USB_INTR_EP0);	usb_setb(USB_REG_INTRUSBE, USB_INTR_RESET);	/* Don't enable rest of the interrupts */	/* usb_setw(USB_REG_INTRINE, USB_INTR_INEP1 | USB_INTR_INEP2);	   usb_setw(USB_REG_INTROUTE, USB_INTR_OUTEP1); */	/* Enable SUSPEND */	/* usb_setb(USB_REG_POWER, USB_POWER_SUSPENDM); */	/* Enable HS Mode */	usb_setb(USB_REG_POWER, USB_POWER_HSENAB);	/* Let host detect UDC:	 * Software must write a 1 to the PMR:USB_POWER_SOFTCONN bit to turn this	 * transistor on and pull the USBDP pin HIGH.	 */	usb_setb(USB_REG_POWER, USB_POWER_SOFTCONN);}/*-------------------------------------------------------------------------*//* keeping it simple: * - one bus driver, initted first; * - one function driver, initted second *//* * Register entry point for the peripheral controller driver. */int usb_gadget_register_driver(struct usb_gadget_driver *driver){	struct jz4740_udc *dev = the_controller;	int retval;	if (!driver	    || !driver->bind	    || !driver->unbind || !driver->disconnect || !driver->setup)	{		printk("\n-EINVAL");		return -EINVAL;	}	if (!dev)	{		printk("\n-ENODEV");		return -ENODEV;	}	if (dev->driver)	{		printk("\n-ENODEV");		return -EBUSY;	}	/* hook up the driver */	dev->driver = driver;	retval = driver->bind(&dev->gadget);	if (retval) {		DEBUG("%s: bind to driver %s --> error %d\n", dev->gadget.name,		            driver->driver.name, retval);		dev->driver = 0;		return retval;	}	/* then enable host detection and ep0; and we're ready	 * for set_configuration as well as eventual disconnect.	 */	udc_enable(dev);	DEBUG("%s: registered gadget driver '%s'\n", dev->gadget.name, 	      driver->driver.name);	return 0;}EXPORT_SYMBOL(usb_gadget_register_driver);static void stop_activity(struct jz4740_udc *dev,			  struct usb_gadget_driver *driver){	int i;	DEBUG("%s\n", __FUNCTION__);	/* don't disconnect drivers more than once */	if (dev->gadget.speed == USB_SPEED_UNKNOWN)		driver = 0;	dev->gadget.speed = USB_SPEED_UNKNOWN;	/* prevent new request submissions, kill any outstanding requests  */	for (i = 0; i < UDC_MAX_ENDPOINTS; i++) {		struct jz4740_ep *ep = &dev->ep[i];		ep->stopped = 1;		usb_set_index(ep_index(ep));		nuke(ep, -ESHUTDOWN);	}	/* report disconnect; the driver is already quiesced */	if (driver) {		spin_unlock(&dev->lock);		driver->disconnect(&dev->gadget);		spin_lock(&dev->lock);	}	/* re-init driver-visible data structures */	udc_reinit(dev);}/* * Unregister entry point for the peripheral controller driver. */int usb_gadget_unregister_driver(struct usb_gadget_driver *driver){	struct jz4740_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);	udc_disable(dev);	DEBUG("unregistered driver '%s'\n", driver->driver.name);	return 0;}EXPORT_SYMBOL(usb_gadget_unregister_driver);/*-------------------------------------------------------------------------*//* * Starting DMA using mode 1 */static void kick_dma(struct jz4740_ep *ep, struct jz4740_request *req){	u32 count = req->req.length;	u32 physaddr = virt_to_phys((void *)req->req.buf);	usb_set_index(ep_index(ep));	if (ep_is_in(ep)) { /* Bulk-IN transfer using DMA channel 1 */		ep->reg_addr = USB_REG_ADDR1;		dma_cache_wback_inv((unsigned long)req->req.buf, count);		pio_irq_enable(ep);		usb_writeb(USB_REG_INCSRH,			   USB_INCSRH_DMAREQENAB | USB_INCSRH_AUTOSET | USB_INCSRH_DMAREQMODE);		usb_writel(USB_REG_ADDR1, physaddr);		usb_writel(USB_REG_COUNT1, count);		usb_writel(USB_REG_CNTL1, USB_CNTL_ENA | USB_CNTL_DIR_IN | USB_CNTL_MODE_1 | 			   USB_CNTL_INTR_EN | USB_CNTL_BURST_16 | USB_CNTL_EP(ep_index(ep)));	}	else { /* Bulk-OUT transfer using DMA channel 2 */		ep->reg_addr = USB_REG_ADDR2;		dma_cache_wback_inv((unsigned long)req->req.buf, count);		pio_irq_enable(ep);		usb_setb(USB_REG_OUTCSRH,			 USB_OUTCSRH_DMAREQENAB | USB_OUTCSRH_AUTOCLR | USB_OUTCSRH_DMAREQMODE);		usb_writel(USB_REG_ADDR2, physaddr);		usb_writel(USB_REG_COUNT2, count);		usb_writel(USB_REG_CNTL2, USB_CNTL_ENA | USB_CNTL_MODE_1 | 			   USB_CNTL_INTR_EN | USB_CNTL_BURST_16 | USB_CNTL_EP(ep_index(ep)));	}}/*-------------------------------------------------------------------------*//** 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 jz4740_ep *ep, struct jz4740_request *req){	u32 max, csr;

⌨️ 快捷键说明

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