Re: [PATCH] Add Usb Gadget driver for Samsung s3c2410 ARM SoC
Ben Dooks <[email protected]>
| Newsgroups | gmane.linux.usb.devel |
|---|---|
| Message-ID | <[email protected]> |
On Sat, May 05, 2007 at 05:05:13PM +0200, Arnaud Patard wrote: > Hi, > > This patch adds the support for the Usb Device Controller that can be > found on Samsung S3C24xx SoCs. > This driver pass all tests from testusb (including test 13) and has been > tested afaik on S3C2410 and S3C2440 SoCs. Also tested on S3C2412. > [Please keep me in CC: when replying, I'm not subscribed to the list ] > > Signed-off-by: Arnaud Patard <[email protected]> Signed-off-by: Ben Dooks <[email protected]> > --- > drivers/usb/gadget/Kconfig | 19 + > drivers/usb/gadget/Makefile | 1 + > drivers/usb/gadget/s3c2410_udc.c | 2077 ++++++++++++++++++++++++++++++++++++++ > drivers/usb/gadget/s3c2410_udc.h | 112 ++ > 4 files changed, 2209 insertions(+), 0 deletions(-) > > diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig > index 8065f2b..790c2d8 100644 > --- a/drivers/usb/gadget/Kconfig > +++ b/drivers/usb/gadget/Kconfig > @@ -208,6 +208,25 @@ config USB_OTG > > Select this only if your OMAP board has a Mini-AB connector. > > +config USB_GADGET_S3C2410 > + boolean "S3C2410" > + depends on ARCH_S3C2410 > + help > + Samsung's S3C2410 is an ARM-4 processor with an integrated > + full speed USB 1.1 device controller. > + It has 4 configurable endpoints, as well as endpoint > + zero (for control transfers). > + > +config USB_S3C2410 > + tristate > + depends on USB_GADGET_S3C2410 > + default USB_GADGET > + select USB_GADGET_SELECTED > + > +config USB_S3C2410_DEBUG > + boolean "S3C2410 udc debug messages" > + depends on USB_GADGET_S3C2410 > + > config USB_GADGET_AT91 > boolean "AT91 USB Device Port" > depends on ARCH_AT91 > diff --git a/drivers/usb/gadget/Makefile b/drivers/usb/gadget/Makefile > index 5db1939..8fed08c 100644 > --- a/drivers/usb/gadget/Makefile > +++ b/drivers/usb/gadget/Makefile > @@ -7,6 +7,7 @@ obj-$(CONFIG_USB_PXA2XX) += pxa2xx_udc.o > obj-$(CONFIG_USB_GOKU) += goku_udc.o > obj-$(CONFIG_USB_OMAP) += omap_udc.o > obj-$(CONFIG_USB_LH7A40X) += lh7a40x_udc.o > +obj-$(CONFIG_USB_S3C2410) += s3c2410_udc.o > obj-$(CONFIG_USB_AT91) += at91_udc.o > obj-$(CONFIG_USB_FSL_USB2) += fsl_usb2_udc.o > > diff --git a/drivers/usb/gadget/s3c2410_udc.c b/drivers/usb/gadget/s3c2410_udc.c > new file mode 100644 > index 0000000..520f5cd > --- /dev/null > +++ b/drivers/usb/gadget/s3c2410_udc.c > @@ -0,0 +1,2077 @@ > +/* > + * linux/drivers/usb/gadget/s3c2410_udc.c > + * > + * Samsung S3C24xx series on-chip full speed USB device controllers > + * > + * Copyright (C) 2004-2007 Herbert P??tzl - Arnaud Patard > + * Additional cleanups by Ben Dooks <[email protected]> > + * > + * 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-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 <[email protected]>, Arnaud Patard <[email protected]>" > + > +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(); > + > + /* Only ep0 debug messages are interesting */ > + if (idx == 0) > + dprintk(DEBUG_VERBOSE, "%s fifo count : %d [last %d]\n", > + __func__, fifo_count,is_last); > + > + if (is_last) { > + if (idx == 0) { > + s3c2410_udc_set_ep0_de_out(base_addr); > + ep->dev->ep0state = EP0_IDLE; > + } else { > + udc_write(idx, S3C2410_UDC_INDEX_REG); > + ep_csr = udc_read(S3C2410_UDC_OUT_CSR1_REG); > + udc_write(idx, S3C2410_UDC_INDEX_REG); > + udc_write(ep_csr & ~S3C2410_UDC_OCSR1_PKTRDY, > + S3C2410_UDC_OUT_CSR1_REG); > + } > + > + s3c2410_udc_done(ep, req, 0); > + } else { > + if (idx == 0) { > + s3c2410_udc_clear_ep0_opr(base_addr); > + } else { > + udc_write(idx, S3C2410_UDC_INDEX_REG); > + ep_csr = udc_read(S3C2410_UDC_OUT_CSR1_REG); > + udc_write(idx, S3C2410_UDC_INDEX_REG); > + udc_write(ep_csr & ~S3C2410_UDC_OCSR1_PKTRDY, > + S3C2410_UDC_OUT_CSR1_REG); > + } > + } > + > + return is_last; > +} > + > +static int s3c2410_udc_read_fifo_crq(struct usb_ctrlrequest *crq) > +{ > + unsigned char *outbuf = (unsigned char*)crq; > + int bytes_read = 0; > + > + udc_write(0, S3C2410_UDC_INDEX_REG); > + > + bytes_read = s3c2410_udc_fifo_count_out(); > + > + dprintk(DEBUG_NORMAL, "%s: fifo_count=%d\n", __func__, bytes_read); > + > + if (bytes_read > sizeof(struct usb_ctrlrequest)) > + bytes_read = sizeof(struct usb_ctrlrequest); > + > + readsb(S3C2410_UDC_EP0_FIFO_REG + base_addr, outbuf, bytes_read); > + > + dprintk(DEBUG_VERBOSE, "%s: len=%d %02x:%02x {%x,%x,%x}\n", __func__, > + bytes_read, crq->bRequest, crq->bRequestType, > + crq->wValue, crq->wIndex, crq->wLength); > + > + return bytes_read; > +} > + > +static int s3c2410_udc_get_status(struct s3c2410_udc *dev, > + struct usb_ctrlrequest *crq) > +{ > + u16 status = 0; > + u8 ep_num = crq->wIndex & 0x7F; > + u8 is_in = crq->wIndex & USB_DIR_IN; > + > + switch (crq->bRequestType & USB_RECIP_MASK) { > + case USB_RECIP_INTERFACE: > + break; > + > + case USB_RECIP_DEVICE: > + status = dev->devstatus; > + break; > + > + case USB_RECIP_ENDPOINT: > + if (ep_num > 4 || crq->wLength > 2) > + return 1; > + > + if (ep_num == 0) { > + udc_write(0, S3C2410_UDC_INDEX_REG); > + status = udc_read(S3C2410_UDC_IN_CSR1_REG); > + status = status & S3C2410_UDC_EP0_CSR_SENDSTL; > + } else { > + udc_write(ep_num, S3C2410_UDC_INDEX_REG); > + if (is_in) { > + status = udc_read(S3C2410_UDC_IN_CSR1_REG); > + status = status & S3C2410_UDC_ICSR1_SENDSTL; > + } else { > + status = udc_read(S3C2410_UDC_OUT_CSR1_REG); > + status = status & S3C2410_UDC_OCSR1_SENDSTL; > + } > + } > + > + status = status ? 1 : 0; > + break; > + > + default: > + return 1; > + } > + > + /* Seems to be needed to get it working. ouch :( */ > + udelay(5); > + udc_write(status & 0xFF, S3C2410_UDC_EP0_FIFO_REG); > + udc_write(status >> 8, S3C2410_UDC_EP0_FIFO_REG); > + s3c2410_udc_set_ep0_de_in(base_addr); > + > + return 0; > +} > +/*------------------------- usb state machine -------------------------------*/ > +static int s3c2410_udc_set_halt(struct usb_ep *_ep, int value); > + > +static void s3c2410_udc_handle_ep0_idle(struct s3c2410_udc *dev, > + struct s3c2410_ep *ep, > + struct usb_ctrlrequest *crq, > + u32 ep0csr) > +{ > + int len, ret, tmp; > + > + /* start control request? */ > + if (!(ep0csr & S3C2410_UDC_EP0_CSR_OPKRDY)) > + return; > + > + s3c2410_udc_nuke(dev, ep, -EPROTO); > + > + len = s3c2410_udc_read_fifo_crq(crq); > + if (len != sizeof(*crq)) { > + dprintk(DEBUG_NORMAL, "setup begin: fifo READ ERROR" > + " wanted %d bytes got %d. Stalling out...\n", > + sizeof(*crq), len); > + s3c2410_udc_set_ep0_ss(base_addr); > + return; > + } > + > + dprintk(DEBUG_NORMAL, "bRequest = %d bRequestType %d wLength = %d\n", > + crq->bRequest, crq->bRequestType, crq->wLength); > + > + /* cope with automagic for some standard requests. */ > + dev->req_std = (crq->bRequestType & USB_TYPE_MASK) > + == USB_TYPE_STANDARD; > + dev->req_config = 0; > + dev->req_pending = 1; > + > + switch (crq->bRequest) { > + case USB_REQ_SET_CONFIGURATION: > + dprintk(DEBUG_NORMAL, "USB_REQ_SET_CONFIGURATION ... \n"); > + > + if (crq->bRequestType == USB_RECIP_DEVICE) { > + dev->req_config = 1; > + s3c2410_udc_set_ep0_de_out(base_addr); > + } > + break; > + > + case USB_REQ_SET_INTERFACE: > + dprintk(DEBUG_NORMAL, "USB_REQ_SET_INTERFACE ... \n"); > + > + if (crq->bRequestType == USB_RECIP_INTERFACE) { > + dev->req_config = 1; > + s3c2410_udc_set_ep0_de_out(base_addr); > + } > + break; > + > + case USB_REQ_SET_ADDRESS: > + dprintk(DEBUG_NORMAL, "USB_REQ_SET_ADDRESS ... \n"); > + > + if (crq->bRequestType == USB_RECIP_DEVICE) { > + tmp = crq->wValue & 0x7F; > + dev->address = tmp; > + udc_write((tmp | S3C2410_UDC_FUNCADDR_UPDATE), > + S3C2410_UDC_FUNC_ADDR_REG); > + s3c2410_udc_set_ep0_de_out(base_addr); > + return; > + } > + break; > + > + case USB_REQ_GET_STATUS: > + dprintk(DEBUG_NORMAL, "USB_REQ_GET_STATUS ... \n"); > + s3c2410_udc_clear_ep0_opr(base_addr); > + > + if (dev->req_std) { > + if (!s3c2410_udc_get_status(dev, crq)) { > + return; > + } > + } > + break; > + > + case USB_REQ_CLEAR_FEATURE: > + s3c2410_udc_clear_ep0_opr(base_addr); > + > + if (crq->bRequestType != USB_RECIP_ENDPOINT) > + break; > + > + if (crq->wValue != USB_ENDPOINT_HALT || crq->wLength != 0) > + break; > + > + s3c2410_udc_set_halt(&dev->ep[crq->wIndex & 0x7f].ep, 0); > + s3c2410_udc_set_ep0_de_out(base_addr); > + return; > + > + case USB_REQ_SET_FEATURE: > + s3c2410_udc_clear_ep0_opr(base_addr); > + > + if (crq->bRequestType != USB_RECIP_ENDPOINT) > + break; > + > + if (crq->wValue != USB_ENDPOINT_HALT || crq->wLength != 0) > + break; > + > + s3c2410_udc_set_halt(&dev->ep[crq->wIndex & 0x7f].ep, 1); > + s3c2410_udc_set_ep0_de_out(base_addr); > + return; > + > + default: > + s3c2410_udc_clear_ep0_opr(base_addr); > + break; > + } > + > + if (crq->bRequestType & USB_DIR_IN) > + dev->ep0state = EP0_IN_DATA_PHASE; > + else > + dev->ep0state = EP0_OUT_DATA_PHASE; > + > + ret = dev->driver->setup(&dev->gadget, crq); > + if (ret < 0) { > + if (dev->req_config) { > + dprintk(DEBUG_NORMAL, "config change %02x fail %d?\n", > + crq->bRequest, ret); > + return; > + } > + > + if (ret == -EOPNOTSUPP) > + dprintk(DEBUG_NORMAL, "Operation not supported\n"); > + else > + dprintk(DEBUG_NORMAL, "dev->driver->setup failed. (%d)\n",ret); > + > + udelay(5); > + s3c2410_udc_set_ep0_ss(base_addr); > + s3c2410_udc_set_ep0_de_out(base_addr); > + dev->ep0state = EP0_IDLE; > + /* deferred i/o == no response yet */ > + } else if (dev->req_pending) { > + dprintk(DEBUG_VERBOSE, "dev->req_pending... what now?\n"); > + dev->req_pending=0; > + } > + > + dprintk(DEBUG_VERBOSE, "ep0state %s\n", ep0states[dev->ep0state]); > +} > + > +static void s3c2410_udc_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 = NULL; > + else > + req = list_entry(ep->queue.next, struct s3c2410_request, queue); > + > + /* We make the assumption that S3C2410_UDC_IN_CSR1_REG equal to > + S3C2410_UDC_EP0_CSR_REG when index is zero */ > + > + udc_write(0, S3C2410_UDC_INDEX_REG); > + ep0csr = udc_read(S3C2410_UDC_IN_CSR1_REG); > + > + dprintk(DEBUG_NORMAL, "ep0csr %x ep0state %s\n", > + ep0csr, ep0states[dev->ep0state]); > + > + /* clear stall status */ > + if (ep0csr & S3C2410_UDC_EP0_CSR_SENTSTL) { > + s3c2410_udc_nuke(dev, ep, -EPIPE); > + dprintk(DEBUG_NORMAL, "... clear SENT_STALL ...\n"); > + s3c2410_udc_clear_ep0_sst(base_addr); > + dev->ep0state = EP0_IDLE; > + return; > + } > + > + /* clear setup end */ > + if (ep0csr & S3C2410_UDC_EP0_CSR_SE) { > + dprintk(DEBUG_NORMAL, "... serviced SETUP_END ...\n"); > + s3c2410_udc_nuke(dev, ep, 0); > + s3c2410_udc_clear_ep0_se(base_addr); > + dev->ep0state = EP0_IDLE; > + } > + > + switch (dev->ep0state) { > + case EP0_IDLE: > + s3c2410_udc_handle_ep0_idle(dev, ep, &crq, ep0csr); > + break; > + > + case EP0_IN_DATA_PHASE: /* GET_DESCRIPTOR etc */ > + dprintk(DEBUG_NORMAL, "EP0_IN_DATA_PHASE ... what now?\n"); > + if (!(ep0csr & S3C2410_UDC_EP0_CSR_IPKRDY) && req) { > + s3c2410_udc_write_fifo(ep, req); > + } > + break; > + > + case EP0_OUT_DATA_PHASE: /* SET_DESCRIPTOR etc */ > + dprintk(DEBUG_NORMAL, "EP0_OUT_DATA_PHASE ... what now?\n"); > + if ((ep0csr & S3C2410_UDC_EP0_CSR_OPKRDY) && req ) { > + s3c2410_udc_read_fifo(ep,req); > + } > + break; > + > + case EP0_END_XFER: > + dprintk(DEBUG_NORMAL, "EP0_END_XFER ... what now?\n"); > + dev->ep0state = EP0_IDLE; > + break; > + > + case EP0_STALL: > + dprintk(DEBUG_NORMAL, "EP0_STALL ... what now?\n"); > + dev->ep0state = EP0_IDLE; > + break; > + } > +} > + > +/* > + * handle_ep - Manage I/O endpoints > + */ > + > +static void s3c2410_udc_handle_ep(struct s3c2410_ep *ep) > +{ > + struct s3c2410_request *req; > + int is_in = ep->bEndpointAddress & USB_DIR_IN; > + u32 ep_csr1; > + u32 idx; > + > + if (likely (!list_empty(&ep->queue))) > + req = list_entry(ep->queue.next, > + struct s3c2410_request, queue); > + else > + req = NULL; > + > + idx = ep->bEndpointAddress & 0x7F; > + > + if (is_in) { > + udc_write(idx, S3C2410_UDC_INDEX_REG); > + ep_csr1 = udc_read(S3C2410_UDC_IN_CSR1_REG); > + dprintk(DEBUG_VERBOSE, "ep%01d write csr:%02x %d\n", > + idx, ep_csr1, req ? 1 : 0); > + > + if (ep_csr1 & S3C2410_UDC_ICSR1_SENTSTL) { > + dprintk(DEBUG_VERBOSE, "st\n"); > + udc_write(idx, S3C2410_UDC_INDEX_REG); > + udc_write(ep_csr1 & ~S3C2410_UDC_ICSR1_SENTSTL, > + S3C2410_UDC_IN_CSR1_REG); > + return; > + } > + > + if (!(ep_csr1 & S3C2410_UDC_ICSR1_PKTRDY) && req) { > + s3c2410_udc_write_fifo(ep,req); > + } > + } else { > + udc_write(idx, S3C2410_UDC_INDEX_REG); > + ep_csr1 = udc_read(S3C2410_UDC_OUT_CSR1_REG); > + dprintk(DEBUG_VERBOSE, "ep%01d rd csr:%02x\n", idx, ep_csr1); > + > + if (ep_csr1 & S3C2410_UDC_OCSR1_SENTSTL) { > + udc_write(idx, S3C2410_UDC_INDEX_REG); > + udc_write(ep_csr1 & ~S3C2410_UDC_OCSR1_SENTSTL, > + S3C2410_UDC_OUT_CSR1_REG); > + return; > + } > + > + if ((ep_csr1 & S3C2410_UDC_OCSR1_PKTRDY) && req) { > + s3c2410_udc_read_fifo(ep,req); > + } > + } > +} > + > +#include <asm/arch/regs-irq.h> > + > +/* > + * s3c2410_udc_irq - interrupt handler > + */ > +static irqreturn_t s3c2410_udc_irq(int irq, void *_dev) > +{ > + struct s3c2410_udc *dev = _dev; > + int usb_status; > + int usbd_status; > + int pwr_reg; > + int ep0csr; > + int i; > + u32 idx; > + unsigned long flags; > + > + spin_lock_irqsave(&dev->lock, flags); > + > + /* Driver connected ? */ > + if (!dev->driver) { > + /* Clear interrupts */ > + udc_write(udc_read(S3C2410_UDC_USB_INT_REG), > + S3C2410_UDC_USB_INT_REG); > + udc_write(udc_read(S3C2410_UDC_EP_INT_REG), > + S3C2410_UDC_EP_INT_REG); > + } > + > + /* Save index */ > + idx = udc_read(S3C2410_UDC_INDEX_REG); > + > + /* Read status registers */ > + usb_status = udc_read(S3C2410_UDC_USB_INT_REG); > + usbd_status = udc_read(S3C2410_UDC_EP_INT_REG); > + pwr_reg = udc_read(S3C2410_UDC_PWR_REG); > + > + udc_writeb(base_addr, S3C2410_UDC_INDEX_EP0, S3C2410_UDC_INDEX_REG); > + ep0csr = udc_read(S3C2410_UDC_IN_CSR1_REG); > + > + dprintk(DEBUG_NORMAL, "usbs=%02x, usbds=%02x, pwr=%02x ep0csr=%02x\n", > + usb_status, usbd_status, pwr_reg, ep0csr); > + > + /* > + * Now, handle interrupts. There's two types : > + * - Reset, Resume, Suspend coming -> usb_int_reg > + * - EP -> ep_int_reg > + */ > + > + /* RESET */ > + if (usb_status & S3C2410_UDC_USBINT_RESET) { > + /* two kind of reset : > + * - reset start -> pwr reg = 8 > + * - reset end -> pwr reg = 0 > + **/ > + dprintk(DEBUG_NORMAL, "USB reset csr %x pwr %x\n", > + ep0csr, pwr_reg); > + > + dev->gadget.speed = USB_SPEED_UNKNOWN; > + udc_write(0x00, S3C2410_UDC_INDEX_REG); > + udc_write((dev->ep[0].ep.maxpacket & 0x7ff) >> 3, > + S3C2410_UDC_MAXP_REG); > + dev->address = 0; > + > + dev->ep0state = EP0_IDLE; > + dev->gadget.speed = USB_SPEED_FULL; > + > + /* clear interrupt */ > + udc_write(S3C2410_UDC_USBINT_RESET, > + S3C2410_UDC_USB_INT_REG); > + > + udc_write(idx, S3C2410_UDC_INDEX_REG); > + spin_unlock_irqrestore(&dev->lock, flags); > + return IRQ_HANDLED; > + } > + > + /* RESUME */ > + if (usb_status & S3C2410_UDC_USBINT_RESUME) { > + dprintk(DEBUG_NORMAL, "USB resume\n"); > + > + /* clear interrupt */ > + udc_write(S3C2410_UDC_USBINT_RESUME, > + S3C2410_UDC_USB_INT_REG); > + > + if (dev->gadget.speed != USB_SPEED_UNKNOWN && > + dev->driver && dev->driver->resume) > + dev->driver->resume(&dev->gadget); > + } > + > + /* SUSPEND */ > + if (usb_status & S3C2410_UDC_USBINT_SUSPEND) { > + dprintk(DEBUG_NORMAL, "USB suspend\n"); > + > + /* clear interrupt */ > + udc_write(S3C2410_UDC_USBINT_SUSPEND, > + S3C2410_UDC_USB_INT_REG); > + > + if (dev->gadget.speed != USB_SPEED_UNKNOWN && > + dev->driver && dev->driver->suspend) > + dev->driver->suspend(&dev->gadget); > + > + dev->ep0state = EP0_IDLE; > + } > + > + /* EP */ > + /* control traffic */ > + /* check on ep0csr != 0 is not a good idea as clearing in_pkt_ready > + * generate an interrupt > + */ > + if (usbd_status & S3C2410_UDC_INT_EP0) { > + dprintk(DEBUG_VERBOSE, "USB ep0 irq\n"); > + /* Clear the interrupt bit by setting it to 1 */ > + udc_write(S3C2410_UDC_INT_EP0, S3C2410_UDC_EP_INT_REG); > + s3c2410_udc_handle_ep0(dev); > + } > + > + /* endpoint data transfers */ > + for (i = 1; i < S3C2410_ENDPOINTS; i++) { > + u32 tmp = 1 << i; > + if (usbd_status & tmp) { > + dprintk(DEBUG_VERBOSE, "USB ep%d irq\n", i); > + > + /* Clear the interrupt bit by setting it to 1 */ > + udc_write(tmp, S3C2410_UDC_EP_INT_REG); > + s3c2410_udc_handle_ep(&dev->ep[i]); > + } > + } > + > + dprintk(DEBUG_VERBOSE, "irq: %d s3c2410_udc_done.\n", irq); > + > + /* Restore old index */ > + udc_write(idx, S3C2410_UDC_INDEX_REG); > + > + spin_unlock_irqrestore(&dev->lock, flags); > + > + return IRQ_HANDLED; > +} > +/*------------------------- s3c2410_ep_ops ----------------------------------*/ > + > +static inline struct s3c2410_ep *to_s3c2410_ep(struct usb_ep *ep) > +{ > + return container_of(ep, struct s3c2410_ep, ep); > +} > + > +static inline struct s3c2410_udc *to_s3c2410_udc(struct usb_gadget *gadget) > +{ > + return container_of(gadget, struct s3c2410_udc, gadget); > +} > + > +static inline struct s3c2410_request *to_s3c2410_req(struct usb_request *req) > +{ > + return container_of(req, struct s3c2410_request, req); > +} > + > +/* > + * s3c2410_udc_ep_enable > + */ > +static int s3c2410_udc_ep_enable(struct usb_ep *_ep, > + const struct usb_endpoint_descriptor *desc) > +{ > + struct s3c2410_udc *dev; > + struct s3c2410_ep *ep; > + u32 max, tmp; > + unsigned long flags; > + u32 csr1,csr2; > + u32 int_en_reg; > + > + ep = to_s3c2410_ep(_ep); > + > + if (!_ep || !desc || ep->desc || _ep->name == ep0name || > + desc->bDescriptorType != USB_DT_ENDPOINT) > + return -EINVAL; > + > + dev = ep->dev; > + if (!dev->driver || dev->gadget.speed == USB_SPEED_UNKNOWN) > + return -ESHUTDOWN; > + > + max = le16_to_cpu(desc->wMaxPacketSize) & 0x1fff; > + > + local_irq_save (flags); > + _ep->maxpacket = max & 0x7ff; > + ep->desc = desc; > + ep->halted = 0; > + ep->bEndpointAddress = desc->bEndpointAddress; > + > + /* set max packet */ > + udc_write(ep->num, S3C2410_UDC_INDEX_REG); > + udc_write(max >> 3, S3C2410_UDC_MAXP_REG); > + > + /* set type, direction, address; reset fifo counters */ > + if (desc->bEndpointAddress & USB_DIR_IN) { > + csr1 = S3C2410_UDC_ICSR1_FFLUSH|S3C2410_UDC_ICSR1_CLRDT; > + csr2 = S3C2410_UDC_ICSR2_MODEIN|S3C2410_UDC_ICSR2_DMAIEN; > + > + udc_write(ep->num, S3C2410_UDC_INDEX_REG); > + udc_write(csr1, S3C2410_UDC_IN_CSR1_REG); > + udc_write(ep->num, S3C2410_UDC_INDEX_REG); > + udc_write(csr2, S3C2410_UDC_IN_CSR2_REG); > + } else { > + /* don't flush in fifo or it will cause endpoint interrupt */ > + csr1 = S3C2410_UDC_ICSR1_CLRDT; > + csr2 = S3C2410_UDC_ICSR2_DMAIEN; > + > + udc_write(ep->num, S3C2410_UDC_INDEX_REG); > + udc_write(csr1, S3C2410_UDC_IN_CSR1_REG); > + udc_write(ep->num, S3C2410_UDC_INDEX_REG); > + udc_write(csr2, S3C2410_UDC_IN_CSR2_REG); > + > + csr1 = S3C2410_UDC_OCSR1_FFLUSH | S3C2410_UDC_OCSR1_CLRDT; > + csr2 = S3C2410_UDC_OCSR2_DMAIEN; > + > + udc_write(ep->num, S3C2410_UDC_INDEX_REG); > + udc_write(csr1, S3C2410_UDC_OUT_CSR1_REG); > + udc_write(ep->num, S3C2410_UDC_INDEX_REG); > + udc_write(csr2, S3C2410_UDC_OUT_CSR2_REG); > + } > + > + /* enable irqs */ > + int_en_reg = udc_read(S3C2410_UDC_EP_INT_EN_REG); > + udc_write(int_en_reg | (1 << ep->num), S3C2410_UDC_EP_INT_EN_REG); > + > + /* print some debug message */ > + tmp = desc->bEndpointAddress; > + dprintk (DEBUG_NORMAL, "enable %s(%d) ep%x%s-blk max %02x\n", > + _ep->name,ep->num, tmp, > + desc->bEndpointAddress & USB_DIR_IN ? "in" : "out", max); > + > + local_irq_restore (flags); > + s3c2410_udc_set_halt(_ep, 0); > + > + return 0; > +} > + > +/* > + * s3c2410_udc_ep_disable > + */ > +static int s3c2410_udc_ep_disable(struct usb_ep *_ep) > +{ > + struct s3c2410_ep *ep = to_s3c2410_ep(_ep); > + unsigned long flags; > + u32 int_en_reg; > + > + if (!_ep || !ep->desc) { > + dprintk(DEBUG_NORMAL, "%s not enabled\n", > + _ep ? ep->ep.name : NULL); > + return -EINVAL; > + } > + > + local_irq_save(flags); > + > + dprintk(DEBUG_NORMAL, "ep_disable: %s\n", _ep->name); > + > + ep->desc = NULL; > + ep->halted = 1; > + > + s3c2410_udc_nuke (ep->dev, ep, -ESHUTDOWN); > + > + /* disable irqs */ > + int_en_reg = udc_read(S3C2410_UDC_EP_INT_EN_REG); > + udc_write(int_en_reg & ~(1<<ep->num), S3C2410_UDC_EP_INT_EN_REG); > + > + local_irq_restore(flags); > + > + dprintk(DEBUG_NORMAL, "%s disabled\n", _ep->name); > + > + return 0; > +} > + > +/* > + * s3c2410_udc_alloc_request > + */ > +static struct usb_request * s3c2410_udc_alloc_request(struct usb_ep *_ep, > + gfp_t mem_flags) > +{ > + struct s3c2410_request *req; > + > + dprintk(DEBUG_VERBOSE,"%s(%p,%d)\n", __func__, _ep, mem_flags); > + > + if (!_ep) > + return NULL; > + > + req = kzalloc (sizeof(struct s3c2410_request), mem_flags); > + if (!req) > + return NULL; > + > + INIT_LIST_HEAD (&req->queue); > + return &req->req; > +} > + > +/* > + * s3c2410_udc_free_request > + */ > +static void s3c2410_udc_free_request(struct usb_ep *_ep, > + struct usb_request *_req) > +{ > + struct s3c2410_ep *ep = to_s3c2410_ep(_ep); > + struct s3c2410_request *req = to_s3c2410_req(_req); > + > + dprintk(DEBUG_VERBOSE, "%s(%p,%p)\n", __func__, _ep, _req); > + > + if (!ep || !_req || (!ep->desc && _ep->name != ep0name)) > + return; > + > + WARN_ON (!list_empty (&req->queue)); > + kfree(req); > +} > + > +/* > + * s3c2410_udc_alloc_buffer > + */ > +static void * s3c2410_udc_alloc_buffer(struct usb_ep *_ep, unsigned bytes, > + dma_addr_t *dma, gfp_t mem_flags) > +{ > + char *retval; > + > + dprintk(DEBUG_VERBOSE, "%s()\n", __func__); > + > + if (!the_controller->driver) > + return NULL; > + > + retval = kmalloc (bytes, mem_flags); > + *dma = (dma_addr_t) retval; > + return retval; > +} > + > +/* > + * s3c2410_udc_free_buffer > + */ > +static void s3c2410_udc_free_buffer (struct usb_ep *_ep, void *buf, > + dma_addr_t dma, unsigned bytes) > +{ > + dprintk(DEBUG_VERBOSE, "%s()\n", __func__); > + > + if (bytes) > + kfree (buf); > +} > + > +/* > + * s3c2410_udc_queue > + */ > +static int s3c2410_udc_queue(struct usb_ep *_ep, struct usb_request *_req, > + gfp_t gfp_flags) > +{ > + struct s3c2410_request *req = to_s3c2410_req(_req); > + struct s3c2410_ep *ep = to_s3c2410_ep(_ep); > + struct s3c2410_udc *dev; > + u32 ep_csr = 0; > + int fifo_count = 0; > + unsigned long flags; > + > + if (unlikely (!_ep || (!ep->desc && ep->ep.name != ep0name))) { > + dprintk(DEBUG_NORMAL, "%s: invalid args\n", __func__); > + return -EINVAL; > + } > + > + dev = ep->dev; > + if (unlikely (!dev->driver || > + dev->gadget.speed == USB_SPEED_UNKNOWN)) { > + return -ESHUTDOWN; > + } > + > + local_irq_save (flags); > + > + if (unlikely(!_req || !_req->complete || !_req->buf || > + !list_empty(&req->queue))) { > + > + if (!_req) > + dprintk(DEBUG_NORMAL, "%s: 1 X X X\n", __func__); > + else { > + dprintk(DEBUG_NORMAL, "%s: 0 %01d %01d %01d\n", > + __func__, !_req->complete,!_req->buf, > + !list_empty(&req->queue)); > + } > + > + local_irq_restore(flags); > + return -EINVAL; > + } > + > + _req->status = -EINPROGRESS; > + _req->actual = 0; > + > + dprintk(DEBUG_VERBOSE, "%s: ep%x len %d\n", > + __func__, ep->bEndpointAddress, _req->length); > + > + if (ep->bEndpointAddress) { > + udc_write(ep->bEndpointAddress & 0x7F, S3C2410_UDC_INDEX_REG); > + > + ep_csr = udc_read((ep->bEndpointAddress & USB_DIR_IN) ? > + S3C2410_UDC_IN_CSR1_REG : > + S3C2410_UDC_OUT_CSR1_REG); > + fifo_count = s3c2410_udc_fifo_count_out(); > + } else { > + udc_write(0, S3C2410_UDC_INDEX_REG); > + ep_csr = udc_read(S3C2410_UDC_IN_CSR1_REG); > + fifo_count = s3c2410_udc_fifo_count_out(); > + } > + > + /* kickstart this i/o queue? */ > + if (list_empty(&ep->queue) && !ep->halted) { > + if (ep->bEndpointAddress == 0 /* ep0 */) { > + switch (dev->ep0state) { > + case EP0_IN_DATA_PHASE: > + if (!(ep_csr&S3C2410_UDC_EP0_CSR_IPKRDY) && > + s3c2410_udc_write_fifo(ep, req)) { > + dev->ep0state = EP0_IDLE; > + req = NULL; > + } > + break; > + > + case EP0_OUT_DATA_PHASE: > + if ((!_req->length) || > + ((ep_csr & S3C2410_UDC_OCSR1_PKTRDY) && > + s3c2410_udc_read_fifo(ep,req))) { > + dev->ep0state = EP0_IDLE; > + req = NULL; > + } > + break; > + > + default: > + local_irq_restore(flags); > + return -EL2HLT; > + } > + } else if ((ep->bEndpointAddress & USB_DIR_IN) != 0 && > + (!(ep_csr&S3C2410_UDC_OCSR1_PKTRDY)) && > + s3c2410_udc_write_fifo(ep, req)) { > + req = NULL; > + } else if ((ep_csr & S3C2410_UDC_OCSR1_PKTRDY) && > + fifo_count && s3c2410_udc_read_fifo(ep, req)) { > + req = NULL; > + } > + } > + > + /* pio or dma irq handler advances the queue. */ > + if (likely (req != 0)) > + list_add_tail(&req->queue, &ep->queue); > + > + local_irq_restore(flags); > + > + dprintk(DEBUG_VERBOSE, "%s ok\n", __func__); > + return 0; > +} > + > +/* > + * s3c2410_udc_dequeue > + */ > +static int s3c2410_udc_dequeue(struct usb_ep *_ep, struct usb_request *_req) > +{ > + struct s3c2410_ep *ep = to_s3c2410_ep(_ep); > + struct s3c2410_udc *udc; > + int retval = -EINVAL; > + unsigned long flags; > + struct s3c2410_request *req = NULL; > + > + dprintk(DEBUG_VERBOSE, "%s(%p,%p)\n", __func__, _ep, _req); > + > + if (!the_controller->driver) > + return -ESHUTDOWN; > + > + if (!_ep || !_req) > + return retval; > + > + udc = to_s3c2410_udc(ep->gadget); > + > + local_irq_save (flags); > + > + list_for_each_entry (req, &ep->queue, queue) { > + if (&req->req == _req) { > + list_del_init (&req->queue); > + _req->status = -ECONNRESET; > + retval = 0; > + break; > + } > + } > + > + if (retval == 0) { > + dprintk(DEBUG_VERBOSE, > + "dequeued req %p from %s, len %d buf %p\n", > + req, _ep->name, _req->length, _req->buf); > + > + s3c2410_udc_done(ep, req, -ECONNRESET); > + } > + > + local_irq_restore (flags); > + return retval; > +} > + > +/* > + * s3c2410_udc_set_halt > + */ > +static int s3c2410_udc_set_halt(struct usb_ep *_ep, int value) > +{ > + struct s3c2410_ep *ep = to_s3c2410_ep(_ep); > + u32 ep_csr = 0; > + unsigned long flags; > + u32 idx; > + > + if (unlikely (!_ep || (!ep->desc && ep->ep.name != ep0name))) { > + dprintk(DEBUG_NORMAL, "%s: inval 2\n", __func__); > + return -EINVAL; > + } > + > + local_irq_save (flags); > + > + idx = ep->bEndpointAddress & 0x7F; > + > + if (idx == 0) { > + s3c2410_udc_set_ep0_ss(base_addr); > + s3c2410_udc_set_ep0_de_out(base_addr); > + } else { > + udc_write(idx, S3C2410_UDC_INDEX_REG); > + ep_csr = udc_read((ep->bEndpointAddress &USB_DIR_IN) ? > + S3C2410_UDC_IN_CSR1_REG : > + S3C2410_UDC_OUT_CSR1_REG); > + > + if ((ep->bEndpointAddress & USB_DIR_IN) != 0) { > + if (value) > + udc_write(ep_csr | S3C2410_UDC_ICSR1_SENDSTL, > + S3C2410_UDC_IN_CSR1_REG); > + else { > + ep_csr &= ~S3C2410_UDC_ICSR1_SENDSTL; > + udc_write(ep_csr, S3C2410_UDC_IN_CSR1_REG); > + ep_csr |= S3C2410_UDC_ICSR1_CLRDT; > + udc_write(ep_csr, S3C2410_UDC_IN_CSR1_REG); > + } > + } else { > + if (value) > + udc_write(ep_csr | S3C2410_UDC_OCSR1_SENDSTL, > + S3C2410_UDC_OUT_CSR1_REG); > + else { > + ep_csr &= ~S3C2410_UDC_OCSR1_SENDSTL; > + udc_write(ep_csr, S3C2410_UDC_OUT_CSR1_REG); > + ep_csr |= S3C2410_UDC_OCSR1_CLRDT; > + udc_write(ep_csr, S3C2410_UDC_OUT_CSR1_REG); > + } > + } > + } > + > + ep->halted = value ? 1 : 0; > + local_irq_restore (flags); > + > + return 0; > +} > + > +static const struct usb_ep_ops s3c2410_ep_ops = { > + .enable = s3c2410_udc_ep_enable, > + .disable = s3c2410_udc_ep_disable, > + > + .alloc_request = s3c2410_udc_alloc_request, > + .free_request = s3c2410_udc_free_request, > + > + .alloc_buffer = s3c2410_udc_alloc_buffer, > + .free_buffer = s3c2410_udc_free_buffer, > + > + .queue = s3c2410_udc_queue, > + .dequeue = s3c2410_udc_dequeue, > + > + .set_halt = s3c2410_udc_set_halt, > +}; > + > +/*------------------------- usb_gadget_ops ----------------------------------*/ > + > +/* > + * s3c2410_udc_get_frame > + */ > +static int s3c2410_udc_get_frame(struct usb_gadget *_gadget) > +{ > + int tmp; > + > + dprintk(DEBUG_VERBOSE, "%s()\n", __func__); > + > + tmp = udc_read(S3C2410_UDC_FRAME_NUM2_REG) << 8; > + tmp |= udc_read(S3C2410_UDC_FRAME_NUM1_REG); > + return tmp; > +} > + > +/* > + * s3c2410_udc_wakeup > + */ > +static int s3c2410_udc_wakeup(struct usb_gadget *_gadget) > +{ > + dprintk(DEBUG_NORMAL, "%s()\n", __func__); > + return 0; > +} > + > +/* > + * s3c2410_udc_set_selfpowered > + */ > +static int s3c2410_udc_set_selfpowered(struct usb_gadget *gadget, int value) > +{ > + struct s3c2410_udc *udc = to_s3c2410_udc(gadget); > + > + dprintk(DEBUG_NORMAL, "%s()\n", __func__); > + > + if (value) > + udc->devstatus |= (1 << USB_DEVICE_SELF_POWERED); > + else > + udc->devstatus &= ~(1 << USB_DEVICE_SELF_POWERED); > + > + return 0; > +} > + > +static void s3c2410_udc_disable(struct s3c2410_udc *dev); > +static void s3c2410_udc_enable(struct s3c2410_udc *dev); > + > +static int s3c2410_udc_set_pullup(struct s3c2410_udc *udc, int is_on) > +{ > + dprintk(DEBUG_NORMAL, "%s()\n", __func__); > + > + if (udc_info && udc_info->udc_command) { > + if (is_on) > + s3c2410_udc_enable(udc); > + else { > + if (udc->gadget.speed != USB_SPEED_UNKNOWN) { > + if (udc->driver && udc->driver->disconnect) > + udc->driver->disconnect(&udc->gadget); > + > + } > + s3c2410_udc_disable(udc); > + } > + } > + else > + return -EOPNOTSUPP; > + > + return 0; > +} > + > +static int s3c2410_udc_vbus_session(struct usb_gadget *gadget, int is_active) > +{ > + struct s3c2410_udc *udc = to_s3c2410_udc(gadget); > + > + dprintk(DEBUG_NORMAL, "%s()\n", __func__); > + > + udc->vbus = (is_active != 0); > + s3c2410_udc_set_pullup(udc, is_active); > + return 0; > +} > + > +static int s3c2410_udc_pullup(struct usb_gadget *gadget, int is_on) > +{ > + struct s3c2410_udc *udc = to_s3c2410_udc(gadget); > + > + dprintk(DEBUG_NORMAL, "%s()\n", __func__); > + > + s3c2410_udc_set_pullup(udc, is_on ? 0 : 1); > + return 0; > +} > + > +static irqreturn_t s3c2410_udc_vbus_irq(int irq, void *_dev) > +{ > + struct s3c2410_udc *dev = _dev; > + unsigned int value; > + > + dprintk(DEBUG_NORMAL, "%s()\n", __func__); > + value = s3c2410_gpio_getpin(udc_info->vbus_pin); > + > + if (udc_info->vbus_pin_inverted) > + value = !value; > + > + if (value != dev->vbus) > + s3c2410_udc_vbus_session(&dev->gadget, value); > + > + return IRQ_HANDLED; > +} > + > +static int s3c2410_vbus_draw(struct usb_gadget *_gadget, unsigned ma) > +{ > + dprintk(DEBUG_NORMAL, "%s()\n", __func__); > + > + if (udc_info && udc_info->vbus_draw) { > + udc_info->vbus_draw(ma); > + return 0; > + } > + > + return -ENOTSUPP; > +} > + > +static const struct usb_gadget_ops s3c2410_ops = { > + .get_frame = s3c2410_udc_get_frame, > + .wakeup = s3c2410_udc_wakeup, > + .set_selfpowered = s3c2410_udc_set_selfpowered, > + .pullup = s3c2410_udc_pullup, > + .vbus_session = s3c2410_udc_vbus_session, > + .vbus_draw = s3c2410_vbus_draw, > +}; > + > +/*------------------------- gadget driver handling---------------------------*/ > +/* > + * s3c2410_udc_disable > + */ > +static void s3c2410_udc_disable(struct s3c2410_udc *dev) > +{ > + dprintk(DEBUG_NORMAL, "%s()\n", __func__); > + > + /* Disable all interrupts */ > + udc_write(0x00, S3C2410_UDC_USB_INT_EN_REG); > + udc_write(0x00, S3C2410_UDC_EP_INT_EN_REG); > + > + /* Clear the interrupt registers */ > + udc_write(S3C2410_UDC_USBINT_RESET | > + S3C2410_UDC_USBINT_RESUME | > + S3C2410_UDC_USBINT_SUSPEND, > + S3C2410_UDC_USB_INT_REG); > + > + udc_write(0x1F, S3C2410_UDC_EP_INT_REG); > + > + /* Good bye, cruel world */ > + if (udc_info && udc_info->udc_command) > + udc_info->udc_command(S3C2410_UDC_P_DISABLE); > + > + /* Set speed to unknown */ > + dev->gadget.speed = USB_SPEED_UNKNOWN; > +} > + > +/* > + * s3c2410_udc_reinit > + */ > +static void s3c2410_udc_reinit(struct s3c2410_udc *dev) > +{ > + u32 i; > + > + /* device/ep0 records init */ > + INIT_LIST_HEAD (&dev->gadget.ep_list); > + INIT_LIST_HEAD (&dev->gadget.ep0->ep_list); > + dev->ep0state = EP0_IDLE; > + > + for (i = 0; i < S3C2410_ENDPOINTS; i++) { > + struct s3c2410_ep *ep = &dev->ep[i]; > + > + if (i != 0) > + list_add_tail (&ep->ep.ep_list, &dev->gadget.ep_list); > + > + ep->dev = dev; > + ep->desc = NULL; > + ep->halted = 0; > + INIT_LIST_HEAD (&ep->queue); > + } > +} > + > +/* > + * s3c2410_udc_enable > + */ > +static void s3c2410_udc_enable(struct s3c2410_udc *dev) > +{ > + int i; > + > + dprintk(DEBUG_NORMAL, "s3c2410_udc_enable called\n"); > + > + /* dev->gadget.speed = USB_SPEED_UNKNOWN; */ > + dev->gadget.speed = USB_SPEED_FULL; > + > + /* Set MAXP for all endpoints */ > + for (i = 0; i < S3C2410_ENDPOINTS; i++) { > + udc_write(i, S3C2410_UDC_INDEX_REG); > + udc_write((dev->ep[i].ep.maxpacket & 0x7ff) >> 3, > + S3C2410_UDC_MAXP_REG); > + } > + > + /* Set default power state */ > + udc_write(DEFAULT_POWER_STATE, S3C2410_UDC_PWR_REG); > + > + /* Enable reset and suspend interrupt interrupts */ > + udc_write(S3C2410_UDC_USBINT_RESET | S3C2410_UDC_USBINT_SUSPEND, > + S3C2410_UDC_USB_INT_EN_REG); > + > + /* Enable ep0 interrupt */ > + udc_write(S3C2410_UDC_INT_EP0, S3C2410_UDC_EP_INT_EN_REG); > + > + /* time to say "hello, world" */ > + if (udc_info && udc_info->udc_command) > + udc_info->udc_command(S3C2410_UDC_P_ENABLE); > +} > + > +/* > + * nop_release > + */ > +static void nop_release(struct device *dev) > +{ > + dprintk(DEBUG_NORMAL, "%s %s\n", __FUNCTION__, dev->bus_id); > +} > + > +/* > + * usb_gadget_register_driver > + */ > +int usb_gadget_register_driver(struct usb_gadget_driver *driver) > +{ > + struct s3c2410_udc *udc = the_controller; > + int retval; > + > + dprintk(DEBUG_NORMAL, "usb_gadget_register_driver() '%s'\n", > + driver->driver.name); > + > + /* Sanity checks */ > + if (!udc) > + return -ENODEV; > + > + if (udc->driver) > + return -EBUSY; > + > + if (!driver->bind || !driver->setup || > + driver->speed != USB_SPEED_FULL) { > + printk(KERN_ERR "Invalid driver: bind %p setup %p speed %d\n", > + driver->bind, driver->setup, driver->speed); > + return -EINVAL; > + } > +#if defined(MODULE) > + if (!driver->unbind) { > + printk(KERN_ERR "Invalid driver: no unbind method\n"); > + return -EINVAL; > + } > +#endif > + > + /* Hook the driver */ > + udc->driver = driver; > + udc->gadget.dev.driver = &driver->driver; > + > + /* Bind the driver */ > + if ((retval = device_add(&udc->gadget.dev)) != 0) { > + printk(KERN_ERR "Error in device_add() : %d\n",retval); > + goto register_error; > + } > + > + dprintk(DEBUG_NORMAL, "binding gadget driver '%s'\n", > + driver->driver.name); > + > + if ((retval = driver->bind (&udc->gadget)) != 0) { > + device_del(&udc->gadget.dev); > + goto register_error; > + } > + > + /* Enable udc */ > + s3c2410_udc_enable(udc); > + > + return 0; > + > +register_error: > + udc->driver = NULL; > + udc->gadget.dev.driver = NULL; > + return retval; > +} > + > +/* > + * usb_gadget_unregister_driver > + */ > +int usb_gadget_unregister_driver(struct usb_gadget_driver *driver) > +{ > + struct s3c2410_udc *udc = the_controller; > + > + if (!udc) > + return -ENODEV; > + > + if (!driver || driver != udc->driver) > + return -EINVAL; > + > + dprintk(DEBUG_NORMAL,"usb_gadget_register_driver() '%s'\n", > + driver->driver.name); > + > + if (driver->disconnect) > + driver->disconnect(&udc->gadget); > + > + if (driver->unbind) > + driver->unbind (&udc->gadget); > + > + device_del(&udc->gadget.dev); > + udc->driver = NULL; > + > + /* Disable udc */ > + s3c2410_udc_disable(udc); > + > + return 0; > +} > + > +/*---------------------------------------------------------------------------*/ > +static struct s3c2410_udc memory = { > + .gadget = { > + .ops = &s3c2410_ops, > + .ep0 = &memory.ep[0].ep, > + .name = gadget_name, > + .dev = { > + .bus_id = "gadget", > + .release = nop_release, > + }, > + }, > + > + /* control endpoint */ > + .ep[0] = { > + .num = 0, > + .ep = { > + .name = ep0name, > + .ops = &s3c2410_ep_ops, > + .maxpacket = EP0_FIFO_SIZE, > + }, > + .dev = &memory, > + }, > + > + /* first group of endpoints */ > + .ep[1] = { > + .num = 1, > + .ep = { > + .name = "ep1-bulk", > + .ops = &s3c2410_ep_ops, > + .maxpacket = EP_FIFO_SIZE, > + }, > + .dev = &memory, > + .fifo_size = EP_FIFO_SIZE, > + .bEndpointAddress = 1, > + .bmAttributes = USB_ENDPOINT_XFER_BULK, > + }, > + .ep[2] = { > + .num = 2, > + .ep = { > + .name = "ep2-bulk", > + .ops = &s3c2410_ep_ops, > + .maxpacket = EP_FIFO_SIZE, > + }, > + .dev = &memory, > + .fifo_size = EP_FIFO_SIZE, > + .bEndpointAddress = 2, > + .bmAttributes = USB_ENDPOINT_XFER_BULK, > + }, > + .ep[3] = { > + .num = 3, > + .ep = { > + .name = "ep3-bulk", > + .ops = &s3c2410_ep_ops, > + .maxpacket = EP_FIFO_SIZE, > + }, > + .dev = &memory, > + .fifo_size = EP_FIFO_SIZE, > + .bEndpointAddress = 3, > + .bmAttributes = USB_ENDPOINT_XFER_BULK, > + }, > + .ep[4] = { > + .num = 4, > + .ep = { > + .name = "ep4-bulk", > + .ops = &s3c2410_ep_ops, > + .maxpacket = EP_FIFO_SIZE, > + }, > + .dev = &memory, > + .fifo_size = EP_FIFO_SIZE, > + .bEndpointAddress = 4, > + .bmAttributes = USB_ENDPOINT_XFER_BULK, > + } > + > +}; > + > +/* > + * probe - binds to the platform device > + */ > +static int s3c2410_udc_probe(struct platform_device *pdev) > +{ > + struct s3c2410_udc *udc = &memory; > + struct device *dev = &pdev->dev; > + int retval; > + unsigned int irq; > + > + dev_dbg(dev, "%s()\n", __func__); > + > + usb_bus_clock = clk_get(NULL, "usb-bus-gadget"); > + if (IS_ERR(usb_bus_clock)) { > + dev_err(dev, "failed to get usb bus clock source\n"); > + return PTR_ERR(usb_bus_clock); > + } > + > + clk_enable(usb_bus_clock); > + > + udc_clock = clk_get(NULL, "usb-device"); > + if (IS_ERR(udc_clock)) { > + dev_err(dev, "failed to get udc clock source\n"); > + return PTR_ERR(udc_clock); > + } > + > + clk_enable(udc_clock); > + > + mdelay(10); > + > + dev_dbg(dev, "got and enabled clocks\n"); > + > + if (strncmp(pdev->name, "s3c2440", 7) == 0) { > + dev_info(dev, "S3C2440: increasing FIFO to 128 bytes\n"); > + memory.ep[1].fifo_size = S3C2440_EP_FIFO_SIZE; > + memory.ep[2].fifo_size = S3C2440_EP_FIFO_SIZE; > + memory.ep[3].fifo_size = S3C2440_EP_FIFO_SIZE; > + memory.ep[4].fifo_size = S3C2440_EP_FIFO_SIZE; > + } > + > + spin_lock_init (&udc->lock); > + udc_info = pdev->dev.platform_data; > + > + rsrc_start = S3C2410_PA_USBDEV; > + rsrc_len = S3C24XX_SZ_USBDEV; > + > + if (!request_mem_region(rsrc_start, rsrc_len, gadget_name)) > + return -EBUSY; > + > + base_addr = ioremap(rsrc_start, rsrc_len); > + if (!base_addr) { > + retval = -ENOMEM; > + goto err_mem; > + } > + > + device_initialize(&udc->gadget.dev); > + udc->gadget.dev.parent = &pdev->dev; > + udc->gadget.dev.dma_mask = pdev->dev.dma_mask; > + > + the_controller = udc; > + platform_set_drvdata(pdev, udc); > + > + s3c2410_udc_disable(udc); > + s3c2410_udc_reinit(udc); > + > + /* irq setup after old hardware state is cleaned up */ > + retval = request_irq(IRQ_USBD, s3c2410_udc_irq, > + IRQF_DISABLED, gadget_name, udc); > + > + if (retval != 0) { > + dev_err(dev, "cannot get irq %i, err %d\n", IRQ_USBD, retval); > + retval = -EBUSY; > + goto err_map; > + } > + > + dev_dbg(dev, "got irq %i\n", IRQ_USBD); > + > + if (udc_info && udc_info->vbus_pin > 0) { > + irq = s3c2410_gpio_getirq(udc_info->vbus_pin); > + retval = request_irq(irq, s3c2410_udc_vbus_irq, > + IRQF_DISABLED | IRQF_TRIGGER_RISING | > + IRQF_TRIGGER_FALLING, > + gadget_name, udc); > + > + if (retval != 0) { > + dev_err(dev, "can't get vbus irq %i, err %d\n", > + irq, retval); > + retval = -EBUSY; > + goto err_int; > + } > + > + dev_dbg(dev, "got irq %i\n", irq); > + } else { > + udc->vbus = 1; > + } > + > + if (s3c2410_udc_debugfs_root) { > + udc->regs_info = debugfs_create_file("registers", S_IRUGO, > + s3c2410_udc_debugfs_root, > + udc, &s3c2410_udc_debugfs_fops); > + if (IS_ERR(udc->regs_info)) { > + dev_warn(dev, "debugfs file creation failed %ld\n", > + PTR_ERR(udc->regs_info)); > + udc->regs_info = NULL; > + } > + } > + > + dev_dbg(dev, "probe ok\n"); > + > + return 0; > + > +err_int: > + free_irq(IRQ_USBD, udc); > +err_map: > + iounmap(base_addr); > +err_mem: > + release_mem_region(rsrc_start, rsrc_len); > + > + return retval; > +} > + > +/* > + * s3c2410_udc_remove > + */ > +static int s3c2410_udc_remove(struct platform_device *pdev) > +{ > + struct s3c2410_udc *udc = platform_get_drvdata(pdev); > + unsigned int irq; > + > + dev_dbg(&pdev->dev, "%s()\n", __func__); > + usb_gadget_unregister_driver(udc->driver); > + > + debugfs_remove(udc->regs_info); > + > + if (udc_info && udc_info->vbus_pin > 0) { > + irq = s3c2410_gpio_getirq(udc_info->vbus_pin); > + free_irq(irq, udc); > + } > + > + free_irq(IRQ_USBD, udc); > + > + iounmap(base_addr); > + release_mem_region(rsrc_start, rsrc_len); > + > + platform_set_drvdata(pdev, NULL); > + > + if (!IS_ERR(udc_clock) && udc_clock != NULL) { > + clk_disable(udc_clock); > + clk_put(udc_clock); > + udc_clock = NULL; > + } > + > + if (!IS_ERR(usb_bus_clock) && usb_bus_clock != NULL) { > + clk_disable(usb_bus_clock); > + clk_put(usb_bus_clock); > + usb_bus_clock = NULL; > + } > + > + dev_dbg(&pdev->dev, "%s: remove ok\n", __func__); > + return 0; > +} > + > +#ifdef CONFIG_PM > +static int s3c2410_udc_suspend(struct platform_device *pdev, pm_message_t message) > +{ > + if (udc_info && udc_info->udc_command) > + udc_info->udc_command(S3C2410_UDC_P_DISABLE); > + > + return 0; > +} > + > +static int s3c2410_udc_resume(struct platform_device *pdev) > +{ > + if (udc_info && udc_info->udc_command) > + udc_info->udc_command(S3C2410_UDC_P_ENABLE); > + > + return 0; > +} > +#else > +#define s3c2410_udc_suspend NULL > +#define s3c2410_udc_resume NULL > +#endif > + > +static struct platform_driver udc_driver_2410 = { > + .driver = { > + .name = "s3c2410-usbgadget", > + .owner = THIS_MODULE, > + }, > + .probe = s3c2410_udc_probe, > + .remove = s3c2410_udc_remove, > + .suspend = s3c2410_udc_suspend, > + .resume = s3c2410_udc_resume, > +}; > + > +static struct platform_driver udc_driver_2440 = { > + .driver = { > + .name = "s3c2440-usbgadget", > + .owner = THIS_MODULE, > + }, > + .probe = s3c2410_udc_probe, > + .remove = s3c2410_udc_remove, > + .suspend = s3c2410_udc_suspend, > + .resume = s3c2410_udc_resume, > +}; > + > +static int __init udc_init(void) > +{ > + int retval; > + > + dprintk(DEBUG_NORMAL, "%s: version %s\n", gadget_name, DRIVER_VERSION); > + > + s3c2410_udc_debugfs_root = debugfs_create_dir(gadget_name, NULL); > + if (IS_ERR(s3c2410_udc_debugfs_root)) { > + printk(KERN_ERR "%s: debugfs dir creation failed %ld\n", > + gadget_name, PTR_ERR(s3c2410_udc_debugfs_root)); > + s3c2410_udc_debugfs_root = NULL; > + } > + > + retval = platform_driver_register(&udc_driver_2410); > + if (retval) > + goto err; > + > + retval = platform_driver_register(&udc_driver_2440); > + if (retval) > + goto err; > + > + return 0; > + > +err: > + debugfs_remove(s3c2410_udc_debugfs_root); > + return retval; > +} > + > +static void __exit udc_exit(void) > +{ > + platform_driver_unregister(&udc_driver_2410); > + platform_driver_unregister(&udc_driver_2440); > + debugfs_remove(s3c2410_udc_debugfs_root); > +} > + > +EXPORT_SYMBOL(usb_gadget_unregister_driver); > +EXPORT_SYMBOL(usb_gadget_register_driver); > + > +module_init(udc_init); > +module_exit(udc_exit); > + > +MODULE_AUTHOR(DRIVER_AUTHOR); > +MODULE_DESCRIPTION(DRIVER_DESC); > +MODULE_VERSION(DRIVER_VERSION); > +MODULE_LICENSE("GPL"); > diff --git a/drivers/usb/gadget/s3c2410_udc.h b/drivers/usb/gadget/s3c2410_udc.h > new file mode 100644 > index 0000000..de56646 > --- /dev/null > +++ b/drivers/usb/gadget/s3c2410_udc.h > @@ -0,0 +1,112 @@ > +/* > + * linux/drivers/usb/gadget/s3c2410_udc.h > + * Samsung on-chip full speed USB device controllers > + * > + * Copyright (C) 2004-2007 Herbert P??tzl - Arnaud Patard > + * Additional cleanups by Ben Dooks <[email protected]> > + * > + * 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 > + * > + */ > + > +#ifndef _S3C2410_UDC_H > +#define _S3C2410_UDC_H > + > +struct s3c2410_ep { > + struct list_head queue; > + unsigned long last_io; /* jiffies timestamp */ > + struct usb_gadget *gadget; > + struct s3c2410_udc *dev; > + const struct usb_endpoint_descriptor *desc; > + struct usb_ep ep; > + u8 num; > + > + unsigned short fifo_size; > + u8 bEndpointAddress; > + u8 bmAttributes; > + > + unsigned halted : 1; > + unsigned already_seen : 1; > + unsigned setup_stage : 1; > +}; > + > + > +/* Warning : ep0 has a fifo of 16 bytes */ > +/* Don't try to set 32 or 64 */ > +/* also testusb 14 fails wit 16 but is */ > +/* fine with 8 */ > +#define EP0_FIFO_SIZE 8 > +#define EP_FIFO_SIZE 64 > +#define DEFAULT_POWER_STATE 0x00 > + > +#define S3C2440_EP_FIFO_SIZE 128 > + > +static const char ep0name [] = "ep0"; > + > +static const char *const ep_name[] = { > + ep0name, /* everyone has ep0 */ > + /* s3c2410 four bidirectional bulk endpoints */ > + "ep1-bulk", "ep2-bulk", "ep3-bulk", "ep4-bulk", > +}; > + > +#define S3C2410_ENDPOINTS ARRAY_SIZE(ep_name) > + > +struct s3c2410_request { > + struct list_head queue; /* ep's requests */ > + struct usb_request req; > +}; > + > +enum ep0_state { > + EP0_IDLE, > + EP0_IN_DATA_PHASE, > + EP0_OUT_DATA_PHASE, > + EP0_END_XFER, > + EP0_STALL, > +}; > + > +static const char *ep0states[]= { > + "EP0_IDLE", > + "EP0_IN_DATA_PHASE", > + "EP0_OUT_DATA_PHASE", > + "EP0_END_XFER", > + "EP0_STALL", > +}; > + > +struct s3c2410_udc { > + spinlock_t lock; > + > + struct s3c2410_ep ep[S3C2410_ENDPOINTS]; > + int address; > + struct usb_gadget gadget; > + struct usb_gadget_driver *driver; > + struct s3c2410_request fifo_req; > + u8 fifo_buf[EP_FIFO_SIZE]; > + u16 devstatus; > + > + u32 port_status; > + int ep0state; > + > + unsigned got_irq : 1; > + > + unsigned req_std : 1; > + unsigned req_config : 1; > + unsigned req_pending : 1; > + u8 vbus; > + struct dentry *regs_info; > +}; > + > +#endif > + > + -- -- Ben Q: What's a light-year? A: One-third less calories than a regular year. ------------------------------------------------------------------------- This SF.net email is sponsored by DB2 Express Download DB2 Express C - the FREE version of DB2 express and take control of your XML. No limits. Just data. Click to get it now. http://sourceforge.net/powerbar/db2/ _______________________________________________ [email protected] To unsubscribe, use the last form field at: https://lists.sourceforge.net/lists/listinfo/linux-usb-devel