📄 s3c2410_udc.c
字号:
/*
* linux/drivers/usb/gadget/s3c2410_udc.c
*/
#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-clock.h>
#include <asm/arch/regs-gpio.h>
#include <asm/arch/regs-udc.h>
#include <asm/arch/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?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 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 0
static 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);
}
#else
static int dprintk(int level, const char *fmt, ...)
{
return 0;
#endif
static 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 */
if (fifo_count != avail)
req->req.status = -EOVERFLOW;
} else {
is_last = (req->req.length <= req->req.actual) ? 1 : 0;
}
udc_write(idx, S3C2410_UDC_INDEX_REG);
fifo_count = s3c2410_udc_fifo_count_out();
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -