📄 s3c2410_udc.c
字号:
/* * linux/drivers/usb/gadget/s3c2410_udc.c * Samsung on-chip full speed USB device controllers * * Copyright (C) 2004 Herbert Pötzl - Arnaud Patard * * 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/config.h>#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/version.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-clock.h>#include <asm/arch/regs-gpio.h>#include <asm/arch/regs-udc.h>#include <asm/hardware/clock.h>#include "s3c2410_udc.h"#define DRIVER_DESC "S3C2410 USB Device Controller Gadget"#define DRIVER_VERSION "29 Oct 2004"#define DRIVER_AUTHOR "Herbert Pötzl <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 s3c2410_udc memory;static struct clk *udc_clock;/*------------------------- I/O ----------------------------------*/static void nuke (struct s3c2410_udc *udc, struct s3c2410_ep *ep){ while (!list_empty (&ep->queue)) { struct s3c2410_request *req; req = list_entry (ep->queue.next, struct s3c2410_request, queue); list_del_init (&req->queue); req->req.status = -ESHUTDOWN; spin_unlock (&udc->lock); req->req.complete (&ep->ep, &req->req); spin_lock (&udc->lock); }}/* * done */static void done(struct s3c2410_ep *ep, struct s3c2410_request *req, int status){ list_del_init(&req->queue); if (likely (req->req.status == -EINPROGRESS)) req->req.status = status; else status = req->req.status; req->req.complete(&ep->ep, &req->req);}static inline void 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++) nuke(dev, &dev->ep[i]);}static inline int fifo_count_out(void){ int tmp; tmp = __raw_readl(S3C2410_UDC_OUT_FIFO_CNT2_REG) << 8; tmp |= __raw_readl(S3C2410_UDC_OUT_FIFO_CNT1_REG); return tmp & 0xffff;}/* * write_packet */static inline int write_packet(u32 fifo, struct s3c2410_request *req, unsigned max){ unsigned len; u8 *buf; buf = req->req.buf + req->req.actual; len = min(req->req.length - req->req.actual, max); dprintk("write_packet %d %d %d ",req->req.actual,req->req.length,len); req->req.actual += len; dprintk("%d\n",req->req.actual); max = len; while (max--) __raw_writel(*buf++,fifo); return len;}/* * write_fifo */// return: 0 = still running, 1 = completed, negative = errnostatic int write_fifo(struct s3c2410_ep *ep, struct s3c2410_request *req){ u8 *buf; unsigned count; int is_last; u32 idx,fifo_reg; u32 ep_csr; switch(ep->bEndpointAddress&0x7F) { default: case 0: idx = 0; fifo_reg = S3C2410_UDC_EP0_FIFO_REG; break; case 1: idx = 1; fifo_reg = S3C2410_UDC_EP1_FIFO_REG; break; case 2: idx = 2; fifo_reg = S3C2410_UDC_EP2_FIFO_REG; break; case 3: idx = 3; fifo_reg = S3C2410_UDC_EP3_FIFO_REG; break; case 4: idx = 4; fifo_reg = S3C2410_UDC_EP4_FIFO_REG; break; } buf = req->req.buf + req->req.actual; prefetch(buf); count = ep->ep.maxpacket; count = write_packet(fifo_reg, req, count); /* 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 = 2; else is_last = 0; dprintk("Written ep%d %d. %d of %d b [last %d]\n",idx,count,req->req.actual,req->req.length,is_last); if (is_last) { /* The order is important. It prevents to send 2 packet at the same time **/ if (!idx) { set_ep0_de_in(); ep->dev->ep0state=EP0_IDLE; } else { __raw_writel(idx, S3C2410_UDC_INDEX_REG); ep_csr=__raw_readl(S3C2410_UDC_IN_CSR1_REG); __raw_writel(idx, S3C2410_UDC_INDEX_REG); __raw_writel(ep_csr|S3C2410_UDC_ICSR1_PKTRDY,S3C2410_UDC_IN_CSR1_REG); } done(ep, req, 0); } else { if (!idx) { set_ep0_ipr(); } else { __raw_writel(idx, S3C2410_UDC_INDEX_REG); ep_csr=__raw_readl(S3C2410_UDC_IN_CSR1_REG); __raw_writel(idx, S3C2410_UDC_INDEX_REG); __raw_writel(ep_csr|S3C2410_UDC_ICSR1_PKTRDY,S3C2410_UDC_IN_CSR1_REG); } } return is_last;}static inline int read_packet(u32 fifo, u8 *buf, struct s3c2410_request *req, unsigned avail){ unsigned len; len = min(req->req.length - req->req.actual, avail); req->req.actual += len; avail = len; while (avail--) *buf++ = (unsigned char)__raw_readl(fifo); return len;}// return: 0 = still running, 1 = queue empty, negative = errnostatic int 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,fifo_reg; switch(ep->bEndpointAddress&0x7F) { default: case 0: idx = 0; fifo_reg = S3C2410_UDC_EP0_FIFO_REG; break; case 1: idx = 1; fifo_reg = S3C2410_UDC_EP1_FIFO_REG; break; case 2: idx = 2; fifo_reg = S3C2410_UDC_EP2_FIFO_REG; break; case 3: idx = 3; fifo_reg = S3C2410_UDC_EP3_FIFO_REG; break; case 4: idx = 4; fifo_reg = S3C2410_UDC_EP4_FIFO_REG; break; } buf = req->req.buf + req->req.actual; bufferspace = req->req.length - req->req.actual; if (!bufferspace) { dprintk("read_fifo: Buffer full !!\n"); return -1; } __raw_writel(idx, S3C2410_UDC_INDEX_REG); fifo_count = fifo_count_out(); dprintk("fifo_read fifo count : %d\n",fifo_count); if (fifo_count > ep->ep.maxpacket) avail = ep->ep.maxpacket; else avail = fifo_count; fifo_count=read_packet(fifo_reg,buf,req,avail); if (fifo_count < ep->ep.maxpacket) { is_last = 1; /* overflowed this request? flush extra data */ if (fifo_count != avail) { req->req.status = -EOVERFLOW; } } else { if (req->req.length == req->req.actual) is_last = 1; else is_last = 0; } __raw_writel(idx, S3C2410_UDC_INDEX_REG); fifo_count = fifo_count_out(); dprintk("fifo_read fifo count : %d [last %d]\n",fifo_count,is_last);/* if (idx) is_last=0;*/ if (is_last) { if (!idx) { set_ep0_de_out(); ep->dev->ep0state=EP0_IDLE; } else { __raw_writel(idx, S3C2410_UDC_INDEX_REG); ep_csr=__raw_readl(S3C2410_UDC_OUT_CSR1_REG); __raw_writel(idx, S3C2410_UDC_INDEX_REG); __raw_writel(ep_csr&~S3C2410_UDC_OCSR1_PKTRDY,S3C2410_UDC_OUT_CSR1_REG); } done(ep, req, 0); if (!list_empty(&ep->queue)) { is_last=0; req = container_of(ep->queue.next, struct s3c2410_request, queue); } else is_last=1;// is_last = 0; } else { if (!idx) { clear_ep0_opr(); } else { __raw_writel(idx, S3C2410_UDC_INDEX_REG); ep_csr=__raw_readl(S3C2410_UDC_OUT_CSR1_REG); __raw_writel(idx, S3C2410_UDC_INDEX_REG); __raw_writel(ep_csr&~S3C2410_UDC_OCSR1_PKTRDY,S3C2410_UDC_OUT_CSR1_REG); } } return is_last;}static intread_fifo_crq(struct usb_ctrlrequest *crq){ int bytes_read = 0; int fifo_count = 0; int i; unsigned char *pOut = (unsigned char*)crq; __raw_writel(0, S3C2410_UDC_INDEX_REG); fifo_count = fifo_count_out(); BUG_ON( fifo_count > 8 ); dprintk("read_fifo_crq(): fifo_count=%d\n", fifo_count ); while( fifo_count-- ) { i = 0; do { *pOut = (unsigned char)__raw_readl(S3C2410_UDC_EP0_FIFO_REG); udelay( 10 ); i++; } while((fifo_count_out() != fifo_count) && (i < 10)); if ( i == 10 ) { printk("read_fifo(): read failure\n"); } pOut++; bytes_read++; } dprintk("read_fifo_crq: len=%d %02x:%02x {%x,%x,%x}\n", bytes_read, crq->bRequest, crq->bRequestType, crq->wValue, crq->wIndex, crq->wLength); return bytes_read;}/*------------------------- usb state machine -------------------------------*/static void handle_ep0(struct s3c2410_udc *dev){ u32 ep0csr; struct s3c2410_ep *ep = &dev->ep [0]; struct s3c2410_request *req; struct usb_ctrlrequest crq; if (list_empty(&ep->queue)) req = 0; else req = list_entry(ep->queue.next, struct s3c2410_request, queue); __raw_writel(0, S3C2410_UDC_INDEX_REG); ep0csr = __raw_readl(S3C2410_UDC_IN_CSR1_REG); /* clear stall status */ if (ep0csr & S3C2410_UDC_EP0_CSR_SENTSTL) { /* FIXME */ nuke(dev, ep); dprintk("... clear SENT_STALL ...\n"); clear_ep0_sst(); ep0csr &= ~(S3C2410_UDC_EP0_CSR_SENTSTL|S3C2410_UDC_EP0_CSR_SENDSTL); __raw_writel(0, S3C2410_UDC_INDEX_REG); __raw_writel(ep0csr, S3C2410_UDC_IN_CSR1_REG); dev->ep0state = EP0_IDLE; return; } /* clear setup end */ if (ep0csr & S3C2410_UDC_EP0_CSR_SE /* && dev->ep0state != EP0_IDLE */) { dprintk("... serviced SETUP_END ...\n"); nuke(dev, ep); clear_ep0_se(); dev->ep0state = EP0_IDLE; return; } switch (dev->ep0state) { case EP0_IDLE: /* start control request? */ if (ep0csr & S3C2410_UDC_EP0_CSR_OPKRDY) { int len, ret, tmp;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -