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

📄 s3c2410_udc.c

📁 linux下面gadget设备驱动
💻 C
📖 第 1 页 / 共 4 页
字号:
/* * linux/drivers/usb/gadget/s3c2410_udc.c * * Samsung S3C24xx series on-chip full speed USB device controllers * * Copyright (C) 2004-2007 Herbert P鰐zl - Arnaud Patard *	Additional cleanups by Ben Dooks <ben-linux@fluff.org> * * 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/module.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/platform_device.h>#include <linux/version.h>#include <linux/clk.h>#include <linux/debugfs.h>#include <linux/seq_file.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/unaligned.h>#include <asm/arch/irqs.h>#include <asm/arch/hardware.h>#include <asm/arch/regs-gpio.h>#include <asm/plat-s3c24xx/regs-udc.h>#include <asm/plat-s3c24xx/udc.h>#include <asm/mach-types.h>#include "s3c2410_udc.h"#define DRIVER_DESC	"S3C2410 USB Device Controller Gadget"#define DRIVER_VERSION	"29 Apr 2007"#define DRIVER_AUTHOR	"Herbert P鰐zl <herbert@13thfloor.at>, " \			"Arnaud Patard <arnaud.patard@rtp-net.org>"static const char		gadget_name[] = "s3c2410_udc";static const char		driver_desc[] = DRIVER_DESC;static struct s3c2410_udc	*the_controller;static struct clk		*udc_clock;static struct clk		*usb_bus_clock;static void __iomem		*base_addr;static u64			rsrc_start;static u64			rsrc_len;static struct dentry		*s3c2410_udc_debugfs_root;static inline u32 udc_read(u32 reg){	return readb(base_addr + reg);}static inline void udc_write(u32 value, u32 reg){	writeb(value, base_addr + reg);}static inline void udc_writeb(void __iomem *base, u32 value, u32 reg){	writeb(value, base + reg);}static struct s3c2410_udc_mach_info *udc_info;/*************************** DEBUG FUNCTION ***************************/#define DEBUG_NORMAL	1#define DEBUG_VERBOSE	2#ifdef CONFIG_USB_S3C2410_DEBUG#define USB_S3C2410_DEBUG_LEVEL 0static uint32_t s3c2410_ticks = 0;static int dprintk(int level, const char *fmt, ...){	static char printk_buf[1024];	static long prevticks;	static int invocation;	va_list args;	int len;	if (level > USB_S3C2410_DEBUG_LEVEL)		return 0;	if (s3c2410_ticks != prevticks) {		prevticks = s3c2410_ticks;		invocation = 0;	}	len = scnprintf(printk_buf,			sizeof(printk_buf), "%1lu.%02d USB: ",			prevticks, invocation++);	va_start(args, fmt);	len = vscnprintf(printk_buf+len,			sizeof(printk_buf)-len, fmt, args);	va_end(args);	return printk(KERN_DEBUG "%s", printk_buf);}#elsestatic int dprintk(int level, const char *fmt, ...){	return 0;}#endifstatic int s3c2410_udc_debugfs_seq_show(struct seq_file *m, void *p){	u32 addr_reg,pwr_reg,ep_int_reg,usb_int_reg;	u32 ep_int_en_reg, usb_int_en_reg, ep0_csr;	u32 ep1_i_csr1,ep1_i_csr2,ep1_o_csr1,ep1_o_csr2;	u32 ep2_i_csr1,ep2_i_csr2,ep2_o_csr1,ep2_o_csr2;	addr_reg       = udc_read(S3C2410_UDC_FUNC_ADDR_REG);	pwr_reg        = udc_read(S3C2410_UDC_PWR_REG);	ep_int_reg     = udc_read(S3C2410_UDC_EP_INT_REG);	usb_int_reg    = udc_read(S3C2410_UDC_USB_INT_REG);	ep_int_en_reg  = udc_read(S3C2410_UDC_EP_INT_EN_REG);	usb_int_en_reg = udc_read(S3C2410_UDC_USB_INT_EN_REG);	udc_write(0, S3C2410_UDC_INDEX_REG);	ep0_csr        = udc_read(S3C2410_UDC_IN_CSR1_REG);	udc_write(1, S3C2410_UDC_INDEX_REG);	ep1_i_csr1     = udc_read(S3C2410_UDC_IN_CSR1_REG);	ep1_i_csr2     = udc_read(S3C2410_UDC_IN_CSR2_REG);	ep1_o_csr1     = udc_read(S3C2410_UDC_IN_CSR1_REG);	ep1_o_csr2     = udc_read(S3C2410_UDC_IN_CSR2_REG);	udc_write(2, S3C2410_UDC_INDEX_REG);	ep2_i_csr1     = udc_read(S3C2410_UDC_IN_CSR1_REG);	ep2_i_csr2     = udc_read(S3C2410_UDC_IN_CSR2_REG);	ep2_o_csr1     = udc_read(S3C2410_UDC_IN_CSR1_REG);	ep2_o_csr2     = udc_read(S3C2410_UDC_IN_CSR2_REG);	seq_printf(m, "FUNC_ADDR_REG  : 0x%04X\n"		 "PWR_REG        : 0x%04X\n"		 "EP_INT_REG     : 0x%04X\n"		 "USB_INT_REG    : 0x%04X\n"		 "EP_INT_EN_REG  : 0x%04X\n"		 "USB_INT_EN_REG : 0x%04X\n"		 "EP0_CSR        : 0x%04X\n"		 "EP1_I_CSR1     : 0x%04X\n"		 "EP1_I_CSR2     : 0x%04X\n"		 "EP1_O_CSR1     : 0x%04X\n"		 "EP1_O_CSR2     : 0x%04X\n"		 "EP2_I_CSR1     : 0x%04X\n"		 "EP2_I_CSR2     : 0x%04X\n"		 "EP2_O_CSR1     : 0x%04X\n"		 "EP2_O_CSR2     : 0x%04X\n",			addr_reg,pwr_reg,ep_int_reg,usb_int_reg,			ep_int_en_reg, usb_int_en_reg, ep0_csr,			ep1_i_csr1,ep1_i_csr2,ep1_o_csr1,ep1_o_csr2,			ep2_i_csr1,ep2_i_csr2,ep2_o_csr1,ep2_o_csr2		);	return 0;}static int s3c2410_udc_debugfs_fops_open(struct inode *inode,					 struct file *file){	return single_open(file, s3c2410_udc_debugfs_seq_show, NULL);}static const struct file_operations s3c2410_udc_debugfs_fops = {	.open		= s3c2410_udc_debugfs_fops_open,	.read		= seq_read,	.llseek		= seq_lseek,	.release	= single_release,	.owner		= THIS_MODULE,};/* io macros */static inline void s3c2410_udc_clear_ep0_opr(void __iomem *base){	udc_writeb(base, S3C2410_UDC_INDEX_EP0, S3C2410_UDC_INDEX_REG);	udc_writeb(base, S3C2410_UDC_EP0_CSR_SOPKTRDY,			S3C2410_UDC_EP0_CSR_REG);}static inline void s3c2410_udc_clear_ep0_sst(void __iomem *base){	udc_writeb(base, S3C2410_UDC_INDEX_EP0, S3C2410_UDC_INDEX_REG);	writeb(0x00, base + S3C2410_UDC_EP0_CSR_REG);}static inline void s3c2410_udc_clear_ep0_se(void __iomem *base){	udc_writeb(base, S3C2410_UDC_INDEX_EP0, S3C2410_UDC_INDEX_REG);	udc_writeb(base, S3C2410_UDC_EP0_CSR_SSE, S3C2410_UDC_EP0_CSR_REG);}static inline void s3c2410_udc_set_ep0_ipr(void __iomem *base){	udc_writeb(base, S3C2410_UDC_INDEX_EP0, S3C2410_UDC_INDEX_REG);	udc_writeb(base, S3C2410_UDC_EP0_CSR_IPKRDY, S3C2410_UDC_EP0_CSR_REG);}static inline void s3c2410_udc_set_ep0_de(void __iomem *base){	udc_writeb(base, S3C2410_UDC_INDEX_EP0, S3C2410_UDC_INDEX_REG);	udc_writeb(base, S3C2410_UDC_EP0_CSR_DE, S3C2410_UDC_EP0_CSR_REG);}inline void s3c2410_udc_set_ep0_ss(void __iomem *b){	udc_writeb(b, S3C2410_UDC_INDEX_EP0, S3C2410_UDC_INDEX_REG);	udc_writeb(b, S3C2410_UDC_EP0_CSR_SENDSTL, S3C2410_UDC_EP0_CSR_REG);}static inline void s3c2410_udc_set_ep0_de_out(void __iomem *base){	udc_writeb(base, S3C2410_UDC_INDEX_EP0, S3C2410_UDC_INDEX_REG);	udc_writeb(base,(S3C2410_UDC_EP0_CSR_SOPKTRDY				| S3C2410_UDC_EP0_CSR_DE),			S3C2410_UDC_EP0_CSR_REG);}static inline void s3c2410_udc_set_ep0_sse_out(void __iomem *base){	udc_writeb(base, S3C2410_UDC_INDEX_EP0, S3C2410_UDC_INDEX_REG);	udc_writeb(base, (S3C2410_UDC_EP0_CSR_SOPKTRDY				| S3C2410_UDC_EP0_CSR_SSE),			S3C2410_UDC_EP0_CSR_REG);}static inline void s3c2410_udc_set_ep0_de_in(void __iomem *base){	udc_writeb(base, S3C2410_UDC_INDEX_EP0, S3C2410_UDC_INDEX_REG);	udc_writeb(base, (S3C2410_UDC_EP0_CSR_IPKRDY			| S3C2410_UDC_EP0_CSR_DE),		S3C2410_UDC_EP0_CSR_REG);}/*------------------------- I/O ----------------------------------*//* *	s3c2410_udc_done */static void s3c2410_udc_done(struct s3c2410_ep *ep,		struct s3c2410_request *req, int status){	unsigned halted = ep->halted;	list_del_init(&req->queue);	if (likely (req->req.status == -EINPROGRESS))		req->req.status = status;	else		status = req->req.status;	ep->halted = 1;	req->req.complete(&ep->ep, &req->req);	ep->halted = halted;}static void s3c2410_udc_nuke(struct s3c2410_udc *udc,		struct s3c2410_ep *ep, int status){	/* Sanity check */	if (&ep->queue == NULL)		return;	while (!list_empty (&ep->queue)) {		struct s3c2410_request *req;		req = list_entry (ep->queue.next, struct s3c2410_request,				queue);		s3c2410_udc_done(ep, req, status);	}}static inline void s3c2410_udc_clear_ep_state(struct s3c2410_udc *dev){	unsigned i;	/* hardware SET_{CONFIGURATION,INTERFACE} automagic resets endpoint	 * fifos, and pending transactions mustn't be continued in any case.	 */	for (i = 1; i < S3C2410_ENDPOINTS; i++)		s3c2410_udc_nuke(dev, &dev->ep[i], -ECONNABORTED);}static inline int s3c2410_udc_fifo_count_out(void){	int tmp;	tmp = udc_read(S3C2410_UDC_OUT_FIFO_CNT2_REG) << 8;	tmp |= udc_read(S3C2410_UDC_OUT_FIFO_CNT1_REG);	return tmp;}/* *	s3c2410_udc_write_packet */static inline int s3c2410_udc_write_packet(int fifo,		struct s3c2410_request *req,		unsigned max){	unsigned len = min(req->req.length - req->req.actual, max);	u8 *buf = req->req.buf + req->req.actual;	prefetch(buf);	dprintk(DEBUG_VERBOSE, "%s %d %d %d %d\n", __func__,		req->req.actual, req->req.length, len, req->req.actual + len);	req->req.actual += len;	udelay(5);	writesb(base_addr + fifo, buf, len);	return len;}/* *	s3c2410_udc_write_fifo * * return:  0 = still running, 1 = completed, negative = errno */static int s3c2410_udc_write_fifo(struct s3c2410_ep *ep,		struct s3c2410_request *req){	unsigned	count;	int		is_last;	u32		idx;	int		fifo_reg;	u32		ep_csr;	idx = ep->bEndpointAddress & 0x7F;	switch (idx) {	default:		idx = 0;	case 0:		fifo_reg = S3C2410_UDC_EP0_FIFO_REG;		break;	case 1:		fifo_reg = S3C2410_UDC_EP1_FIFO_REG;		break;	case 2:		fifo_reg = S3C2410_UDC_EP2_FIFO_REG;		break;	case 3:		fifo_reg = S3C2410_UDC_EP3_FIFO_REG;		break;	case 4:		fifo_reg = S3C2410_UDC_EP4_FIFO_REG;		break;	}	count = s3c2410_udc_write_packet(fifo_reg, req, ep->ep.maxpacket);	/* last packet is often short (sometimes a zlp) */	if (count != ep->ep.maxpacket)		is_last = 1;	else if (req->req.length != req->req.actual || req->req.zero)		is_last = 0;	else		is_last = 2;	/* Only ep0 debug messages are interesting */	if (idx == 0)		dprintk(DEBUG_NORMAL,			"Written ep%d %d.%d of %d b [last %d,z %d]\n",			idx, count, req->req.actual, req->req.length,			is_last, req->req.zero);	if (is_last) {		/* The order is important. It prevents sending 2 packets		 * at the same time */		if (idx == 0) {			/* Reset signal => no need to say 'data sent' */			if (! (udc_read(S3C2410_UDC_USB_INT_REG)					& S3C2410_UDC_USBINT_RESET))				s3c2410_udc_set_ep0_de_in(base_addr);			ep->dev->ep0state=EP0_IDLE;		} else {			udc_write(idx, S3C2410_UDC_INDEX_REG);			ep_csr = udc_read(S3C2410_UDC_IN_CSR1_REG);			udc_write(idx, S3C2410_UDC_INDEX_REG);			udc_write(ep_csr | S3C2410_UDC_ICSR1_PKTRDY,					S3C2410_UDC_IN_CSR1_REG);		}		s3c2410_udc_done(ep, req, 0);		is_last = 1;	} else {		if (idx == 0) {			/* Reset signal => no need to say 'data sent' */			if (! (udc_read(S3C2410_UDC_USB_INT_REG)					& S3C2410_UDC_USBINT_RESET))				s3c2410_udc_set_ep0_ipr(base_addr);		} else {			udc_write(idx, S3C2410_UDC_INDEX_REG);			ep_csr = udc_read(S3C2410_UDC_IN_CSR1_REG);			udc_write(idx, S3C2410_UDC_INDEX_REG);			udc_write(ep_csr | S3C2410_UDC_ICSR1_PKTRDY,					S3C2410_UDC_IN_CSR1_REG);		}	}	return is_last;}static inline int s3c2410_udc_read_packet(int fifo, u8 *buf,		struct s3c2410_request *req, unsigned avail){	unsigned len;	len = min(req->req.length - req->req.actual, avail);	req->req.actual += len;	readsb(fifo + base_addr, buf, len);	return len;}/* * return:  0 = still running, 1 = queue empty, negative = errno */static int s3c2410_udc_read_fifo(struct s3c2410_ep *ep,				 struct s3c2410_request *req){	u8		*buf;	u32		ep_csr;	unsigned	bufferspace;	int		is_last=1;	unsigned	avail;	int		fifo_count = 0;	u32		idx;	int		fifo_reg;	idx = ep->bEndpointAddress & 0x7F;	switch (idx) {	default:		idx = 0;	case 0:		fifo_reg = S3C2410_UDC_EP0_FIFO_REG;		break;	case 1:		fifo_reg = S3C2410_UDC_EP1_FIFO_REG;		break;	case 2:		fifo_reg = S3C2410_UDC_EP2_FIFO_REG;		break;	case 3:		fifo_reg = S3C2410_UDC_EP3_FIFO_REG;		break;	case 4:		fifo_reg = S3C2410_UDC_EP4_FIFO_REG;		break;	}	if (!req->req.length)		return 1;	buf = req->req.buf + req->req.actual;	bufferspace = req->req.length - req->req.actual;	if (!bufferspace) {		dprintk(DEBUG_NORMAL, "%s: buffer full!\n", __func__);		return -1;	}	udc_write(idx, S3C2410_UDC_INDEX_REG);	fifo_count = s3c2410_udc_fifo_count_out();	dprintk(DEBUG_NORMAL, "%s fifo count : %d\n", __func__, fifo_count);	if (fifo_count > ep->ep.maxpacket)		avail = ep->ep.maxpacket;	else		avail = fifo_count;	fifo_count = s3c2410_udc_read_packet(fifo_reg, buf, req, avail);	/* checking this with ep0 is not accurate as we already	 * read a control request	 **/	if (idx != 0 && fifo_count < ep->ep.maxpacket) {		is_last = 1;		/* overflowed this request?  flush extra data */

⌨️ 快捷键说明

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