📄 s3c2410_udc.c
字号:
/* * 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 + -