From 0e3b1bffd1974e6852912865a7cea481617b1c39 Mon Sep 17 00:00:00 2001 From: John Crispin Date: Thu, 30 May 2013 16:06:35 +0200 Subject: [PATCH 162/164] USB: MIPS: ralink: add rt5350/mt7620 UDC Signed-off-by: John Crispin --- drivers/usb/gadget/Kconfig | 8 + drivers/usb/gadget/Makefile | 1 + drivers/usb/gadget/rt_udc.h | 417 +++++++ drivers/usb/gadget/rt_udc_pdma.c | 2547 ++++++++++++++++++++++++++++++++++++++ 4 files changed, 2973 insertions(+) create mode 100644 drivers/usb/gadget/rt_udc.h create mode 100644 drivers/usb/gadget/rt_udc_pdma.c --- a/drivers/usb/gadget/Kconfig +++ b/drivers/usb/gadget/Kconfig @@ -336,6 +336,14 @@ config USB_MV_U3D MARVELL PXA2128 Processor series include a super speed USB3.0 device controller, which support super speed USB peripheral. +config USB_RT_UDC + boolean "Ralink USB Device Port" + depends on SOC_MT7620 + help + Say "y" to link the driver statically, or "m" to build a + dynamically linked module called "rt_udc" and force all + gadget drivers to also be dynamically linked. + # # Controllers available in both integrated and discrete versions # --- a/drivers/usb/gadget/Makefile +++ b/drivers/usb/gadget/Makefile @@ -34,6 +34,7 @@ obj-$(CONFIG_USB_MV_UDC) += mv_udc.o mv_udc-y := mv_udc_core.o obj-$(CONFIG_USB_FUSB300) += fusb300_udc.o obj-$(CONFIG_USB_MV_U3D) += mv_u3d_core.o +obj-$(CONFIG_USB_RT_UDC) += rt_udc_pdma.o # USB Functions obj-$(CONFIG_USB_F_ACM) += f_acm.o --- /dev/null +++ b/drivers/usb/gadget/rt_udc.h @@ -0,0 +1,417 @@ +/* + * Copyright (C) 2009 Y.Y. Huang, Ralink Tech.(yy_huang@ralinktech.com) + * + * This udc driver is now under testing and code is based on pxa2xx_udc.h + * Please use it with your own risk! + * + * 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. + */ + +#ifndef __LINUX_USB_GADGET_RT_UDC_H +#define __LINUX_USB_GADGET_RT_UDC_H + +#define CONFIG_RALINK_MT7620 + +#include + +//#include "../host/ralink_usb.h" /* for port sharing setting and power saving purpose */ + +#if defined (CONFIG_RALINK_RT3883) || defined (CONFIG_RALINK_RT3352) || defined (CONFIG_RALINK_MT7620) +#define IN_EP_NUM 2 +#define OUT_EP_NUM 2 +#elif defined (CONFIG_RALINK_RT5350) +#define IN_EP_NUM 1 +#define OUT_EP_NUM 1 +#else +#error "Please define a platform." +#endif + +/* Helper macros */ +#define EP_IDX(ep) ((ep->bEndpointAddress & ~USB_DIR_IN)+(EP_DIR(ep)? 0:IN_EP_NUM)) /* IN:1, OUT:0 */ +#define EP_NO(ep) ((ep->bEndpointAddress & ~USB_DIR_IN)) /* IN:1, OUT:0 */ +#define EP_DIR(ep) ((ep->bEndpointAddress) & USB_DIR_IN ? 1 : 0) +#define EP_IN 1 +#define EP_OUT 0 +#define RT_USB_NB_EP (IN_EP_NUM + OUT_EP_NUM + 1) + +/* Driver structures */ +struct rt_request { + struct usb_request req; + struct list_head queue; + unsigned int in_use; + struct rt_ep_struct *rt_ep; // test for rx tasklet + int zlp_dma_done; // used for DMA ZLP packet. + int txd_count; +}; + +enum ep0_state { + EP0_IDLE, + EP0_IN_DATA_PHASE, + EP0_OUT_DATA_PHASE, + EP0_NO_DATA_PHASE, + EP0_STALL, +}; + +struct rt_ep_struct { + struct usb_ep ep; + struct rt_udc_struct *rt_usb; + struct list_head queue; + unsigned char stopped; + unsigned char bEndpointAddress; + unsigned char bmAttributes; + + unsigned char pending; + unsigned int rx_done_count; /* used by OUT EP only */ + unsigned int tx_done_count; /* used by OUT EP only */ +}; + +struct rt_udc_struct { + struct usb_gadget gadget; + struct usb_gadget_driver *driver; + struct device *dev; + struct rt_ep_struct rt_ep[RT_USB_NB_EP]; + /* struct clk *clk; */ + struct timer_list timer; + enum ep0_state ep0state; + struct resource *res; + void __iomem *base; + unsigned char set_config; + int cfg, + intf, + alt, + interrupt; +}; + +#define USB_BASE (0xB0120000) + +#define OUT0BC (0x000) +#define IN0BC (0x001) +#define EP0CS (0x002) + +#define OUT1CON (0x00A) +#define IN1CON (0x00E) +#define OUT2CON (0x012) +#define IN2CON (0x016) +#define OUT3CON (0x01A) +#define IN3CON (0x01E) +#define OUT4CON (0x022) +#define IN4CON (0x026) + + +#define EP0INDAT (0x100) +#define EP0OUTDAT (0x140) +#define SETUPDATA (0x180) + +#define IN07IRQ (0x188) +#define IN815IRQ (0x189) +#define OUT07IRQ (0x18A) +#define OUT815IRQ (0x18B) +#define USBIRQ (0x18C) +#define OUT07PNGIRQ (0x18E) +#define OUT815PNGIRQ (0x18F) + +#define IN07IEN (0x194) +#define OUT07IEN (0x196) +#define USBIEN (0x198) + +#define OUT07PNGIEN (0x19A) +#define OUT815PNGIEN (0x19B) + +#define ENDPRST (0x1A2) +#define ENDPRST_IO (0x1 << 4) +#define ENDPRST_TOGRST (0x1 << 5) +#define ENDPRST_FIFORST (0x1 << 6) + +#define FIFOCTRL (0x1A8) + +#define EP_CS_EP0_STALL (0x1 << 0) +#define EP_CS_EP0_HSNAK (0x1 << 1) +#define EP_CS_EP0_INBSY (0x1 << 2) +#define EP_CS_EP0_OUTBSY (0x1 << 3) +#define EP_CS_AUTO (0x1 << 4) +#define EP_CS_NPAK1 (0x1 << 3) +#define EP_CS_NPAK0 (0x1 << 2) +#define EP_CS_BSY (0x1 << 1) +#define EP_CS_ERR (0x1 << 0) + +#define EP0_OUT_BSY (0x1 << 3) +#define EP0_IN_BSY (0x1 << 2) + +#define USB_INTR_HSPEED (0x20) +#define USB_INTR_RESET (0x10) +#define USB_INTR_SUSPEND (0x08) +#define USB_INTR_SETUP_TOKEN (0x04) +#define USB_INTR_SOF (0x02) +#define USB_INTR_SETUP_TOKEN_VALID (0x01) + +/* UDMA */ +#define RTUSB_UDMA_CTRL (USB_BASE + 0x800) +#define RTUSB_UDMA_WRR (USB_BASE + 0x804) + +/* PDMA */ +#define RTUSB_TX_BASE_PTR0 (USB_BASE + 0x1000) +#define RTUSB_TX_MAX_CNT0 (USB_BASE + 0x1004) +#define RTUSB_TX_CTX_IDX0 (USB_BASE + 0x1008) +#define RTUSB_TX_DTX_IDX0 (USB_BASE + 0x100C) +#define RTUSB_TX_BASE_PTR1 (USB_BASE + 0x1010) +#define RTUSB_TX_MAX_CNT1 (USB_BASE + 0x1014) +#define RTUSB_TX_CTX_IDX1 (USB_BASE + 0x1018) +#define RTUSB_TX_DTX_IDX1 (USB_BASE + 0x101C) +#define RTUSB_RX_BASE_PTR0 (USB_BASE + 0x1100) +#define RTUSB_RX_MAX_CNT0 (USB_BASE + 0x1104) +#define RTUSB_RX_CALC_IDX0 (USB_BASE + 0x1108) +#define RTUSB_RX_DRX_IDX0 (USB_BASE + 0x110C) +#define RTUSB_PDMA_GLO_CFG (USB_BASE + 0x1204) + +#define RTUSB_TX_WB_DDONE (0x1 << 6) +#define RTUSB_RX_DMA_BUSY (0x1 << 3) +#define RTUSB_RX_DMA_EN (0x1 << 2) +#define RTUSB_TX_DMA_BUSY (0x1 << 1) +#define RTUSB_TX_DMA_EN (0x1 << 0) + +#define RTUSB_PDMA_RST_IDX (USB_BASE + 0x1208) + +#define RTUSB_RST_DRX_IDX1 (0x1 << 17) +#define RTUSB_RST_DRX_IDX0 (0x1 << 16) +#define RTUSB_RST_DTX_IDX3 (0x1 << 3) +#define RTUSB_RST_DTX_IDX2 (0x1 << 2) +#define RTUSB_RST_DTX_IDX1 (0x1 << 1) +#define RTUSB_RST_DTX_IDX0 (0x1 << 0) + +#define RTUSB_DELAY_INT_CFG (USB_BASE + 0x120C) +#define RTUSB_INT_STATUS (USB_BASE + 0x1220) +#define RTUSB_RX_DONE_INT1 (0x1 << 17) +#define RTUSB_RX_DONE_INT0 (0x1 << 16) +#define RTUSB_TX_DONE_INT3 (0x1 << 3) +#define RTUSB_TX_DONE_INT2 (0x1 << 2) +#define RTUSB_TX_DONE_INT1 (0x1 << 1) +#define RTUSB_TX_DONE_INT0 (0x1 << 0) + +#define RTUSB_INT_MASK (USB_BASE + 0x1228) +#define RTUSB_RX_DONE_INT_MSK1 (0x1 << 17) +#define RTUSB_RX_DONE_INT_MSK0 (0x1 << 16) +#define RTUSB_TX_DONE_INT_MSK3 (0x1 << 3) +#define RTUSB_TX_DONE_INT_MSK2 (0x1 << 2) +#define RTUSB_TX_DONE_INT_MSK1 (0x1 << 1) +#define RTUSB_TX_DONE_INT_MSK0 (0x1 << 0) + + +/*========================================= + PDMA RX Descriptor Format define +=========================================*/ +//------------------------------------------------- +typedef struct _PDMA_RXD_INFO1_ PDMA_RXD_INFO1_T; + +struct _PDMA_RXD_INFO1_ { + unsigned int PDP0; +}; +//------------------------------------------------- +typedef struct _PDMA_RXD_INFO2_ PDMA_RXD_INFO2_T; + +struct _PDMA_RXD_INFO2_ { + unsigned int PLEN1 : 14; + unsigned int LS1 : 1; + unsigned int UN_USED : 1; + unsigned int PLEN0 : 14; + unsigned int LS0 : 1; + unsigned int DDONE_bit : 1; +}; +//------------------------------------------------- +typedef struct _PDMA_RXD_INFO3_ PDMA_RXD_INFO3_T; + +struct _PDMA_RXD_INFO3_ { + unsigned int PDP1; +}; +//------------------------------------------------- +typedef struct _PDMA_RXD_INFO4_ PDMA_RXD_INFO4_T; + +struct _PDMA_RXD_INFO4_ { + unsigned int Rx_bcnt:16; + unsigned int Reserved1:8; + unsigned int Out_ep_addr:4; + unsigned int Reserved0:4; +}; +struct PDMA_rxdesc { + PDMA_RXD_INFO1_T rxd_info1; + PDMA_RXD_INFO2_T rxd_info2; + PDMA_RXD_INFO3_T rxd_info3; + PDMA_RXD_INFO4_T rxd_info4; +}; +/*========================================= + PDMA TX Descriptor Format define +=========================================*/ +//------------------------------------------------- +typedef struct _PDMA_TXD_INFO1_ PDMA_TXD_INFO1_T; + +struct _PDMA_TXD_INFO1_ { + unsigned int SDP0; +}; +//------------------------------------------------- +typedef struct _PDMA_TXD_INFO2_ PDMA_TXD_INFO2_T; + +struct _PDMA_TXD_INFO2_ { + unsigned int SDL1 : 14; + unsigned int LS1_bit : 1; + unsigned int BURST_bit : 1; + unsigned int SDL0 : 14; + unsigned int LS0_bit : 1; + unsigned int DDONE_bit : 1; +}; +//------------------------------------------------- +typedef struct _PDMA_TXD_INFO3_ PDMA_TXD_INFO3_T; + +struct _PDMA_TXD_INFO3_ { + unsigned int SDP1; +}; +//------------------------------------------------- +typedef struct _PDMA_TXD_INFO4_ PDMA_TXD_INFO4_T; +struct _PDMA_TXD_INFO4_ { + unsigned int reserved2:17; + unsigned int zlp_flag:1; + unsigned int reserved1:6; + unsigned int In_ep_addr:4; + unsigned int rsv:4; +}; + +struct PDMA_txdesc { + PDMA_TXD_INFO1_T txd_info1; + PDMA_TXD_INFO2_T txd_info2; + PDMA_TXD_INFO3_T txd_info3; + PDMA_TXD_INFO4_T txd_info4; +}; + + +#ifdef DEBUG +#define DBG do{ if(debuglevel) printk("%s()\n", __FUNCTION__); }while(0); +#define DD do{ printk("%s: %s %d\n", driver_name, __FUNCTION__, __LINE__); } while(0); +#define xprintk(fmt, args...) do{ if(debuglevel) printk(fmt, ## args); } while(0); +#else +#define DBG +#define DD +#define xprintk(fmt, args...) +#endif + +#define FATAL_ERROR(fmt, args...) do{ printk(fmt, ## args); printk("\n############### ERROR #####################\n %s %d\n############### ERROR #####################\n", __FUNCTION__, __LINE__); BUG(); } while(0) + +static void inline dump_usbirq(u32 irqreg) +{ + if(irqreg) + xprintk("U%s%s%s%s%s%s\n", + (irqreg & USB_INTR_SOF) ? "sof" : "", + (irqreg & USB_INTR_RESET) ? " rst" : "", + (irqreg & USB_INTR_SUSPEND) ? " sus" : "", + (irqreg & USB_INTR_SETUP_TOKEN) ? "st" : "", + (irqreg & USB_INTR_SETUP_TOKEN_VALID) ? "sv" : "", + (irqreg & USB_INTR_HSPEED) ? " HS" : ""); + +// if(irqreg & USB_INTR_SETUP_TOKEN) +// printk("ST\n"); +// if(irqreg & USB_INTR_SETUP_TOKEN_VALID) +// printk("SV\n"); + +} + +static void inline dump_epirq(u32 irqreg, u32 ienreg, int dir) +{ + if(irqreg) + xprintk("%s%x\n", dir? "I" : "O", irqreg); +} + +static __inline__ u32 usb_read(u32 addr) +{ + return ioread32( (void __iomem *)(USB_BASE + (addr << 2)) ); +} + +static __inline__ void usb_write(u32 addr, u32 value) +{ + iowrite32(value, (void __iomem *)(USB_BASE + (addr << 2)) ); +} + +static __inline__ void reg_write(u32 addr, u32 value) +{ + iowrite32(value, (void __iomem *)0x0 + addr); +} + +static __inline__ u32 reg_read(u32 addr) +{ + return ioread32( (void __iomem *)0x0 + addr); +} + + +static void handle_pending_epoutirq(struct rt_udc_struct *rt_usb, struct rt_ep_struct *rt_ep, struct rt_request *req); + +/* Debug macros */ +#ifdef DEBUG +#define DEBUG_REQ +#define DEBUG_TRX +#define DEBUG_INIT +#define DEBUG_EP0 +#define DEBUG_EPX +#define DEBUG_ERR + +#ifdef DEBUG_REQ + #define D_REQ(dev, args...) printk(args) +#else + #define D_REQ(dev, args...) do {} while (0) +#endif /* DEBUG_REQ */ + +#ifdef DEBUG_TRX + #define D_TRX(dev, args...) printk(args) +#else + #define D_TRX(dev, args...) do {} while (0) +#endif /* DEBUG_TRX */ + +#ifdef DEBUG_INIT + #define D_INI(dev, args...) printk(args) +#else + #define D_INI(dev, args...) do {} while (0) +#endif /* DEBUG_INIT */ + +#ifdef DEBUG_EP0 + static const char *state_name[] = { + "IDLE", + "IN", + "OUT", + "NODATA", + "STALL" + }; + #define D_EP0(dev, args...) printk(args) +#else + #define D_EP0(dev, args...) do {} while (0) +#endif /* DEBUG_EP0 */ + +#ifdef DEBUG_EPX + #define D_EPX(dev, args...) printk(args) +#else + #define D_EPX(dev, args...) do {} while (0) +#endif /* DEBUG_EP0 */ + +#ifdef DEBUG_ERR + #define D_ERR(dev, args...) printk(args) +#else + #define D_ERR(dev, args...) do {} while (0) +#endif + +#else + #define D_REQ(dev, args...) do {} while (0) + #define D_TRX(dev, args...) do {} while (0) + #define D_INI(dev, args...) do {} while (0) + #define D_EP0(dev, args...) do {} while (0) + #define D_EPX(dev, args...) do {} while (0) + #define dump_ep_intr(x, y, z, i) do {} while (0) + #define dump_intr(x, y, z) do {} while (0) + #define dump_ep_stat(x, y) do {} while (0) + #define dump_usb_stat(x, y) do {} while (0) + #define dump_req(x, y, z) do {} while (0) + #define D_ERR(dev, args...) do {} while (0) +#endif /* DEBUG */ + +#endif /* __LINUX_USB_GADGET_RT_UDC_H */ --- /dev/null +++ b/drivers/usb/gadget/rt_udc_pdma.c @@ -0,0 +1,2547 @@ +/* + * driver/usb/gadget/rt_udc.c + * + * Copyright (C) 2009 Ying Yuan Huang, Ralink Tech. + * + * 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. + */ + +/* + * 1) [ USB composite device ]. The USB PDMA architecture is not suitable for USB composite + * device support. A passive gadget driver(device) may slow down or block other gadget + * (device) because they are in the same ring. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,36) +#include +#else +#include +#endif + +static const char driver_name[] = "rt_udc"; +static const char ep0name[] = "ep0"; +static unsigned debuglevel = 0; +module_param (debuglevel, uint, S_IRUGO); + +#define DEBUG +#include "rt_udc.h" + +#define PROC_DIR driver_name +#define DEBUGLEVEL_PROCFILE "debuglevel" +static struct proc_dir_entry *pProcDir = NULL; +static struct proc_dir_entry *pProcDebugLevel = NULL; + +/* + * USB PDMA related + */ +#define NUM_RX_DESC 256 +#define NUM_TX_DESC 256 +#define RX_BUFF_SZ 1600 /* 1536 */ +#define RING_RESET_TIMEOUT 3000 /* 3 secs */ +#define RX_RESCHEDULE 64 +#define TX_RESCHEDULE 4 +static unsigned dma = 0; +module_param (dma, uint, S_IRUGO); +static unsigned sm = 0; +module_param (sm, uint, S_IRUGO); +static unsigned int TXMAXCAP = 512; +module_param (TXMAXCAP, uint, S_IRUGO); + +static struct PDMA_txdesc *tx_ring0_cache = NULL; +static struct PDMA_rxdesc *rx_ring0_cache = NULL; +static volatile struct PDMA_rxdesc *rx_ring0_noncache = NULL; +static volatile struct PDMA_txdesc *tx_ring0_noncache = NULL; +static dma_addr_t tx_ring_bus_addr; +static dma_addr_t rx_ring_bus_addr; + +static int rx_dma_owner_idx0; /* Point to the next RXD DMA wants to use in RXD Ring#0. */ +static int tx_cpu_owner_idx0; +static int tx_need_free_idx0; + +static volatile unsigned char *USBRxPackets[NUM_RX_DESC]; /* Receive packets */ +static unsigned char tx_zlp_dummy_buf[8]; +struct tasklet_struct rx_dma_tasklet; +struct tasklet_struct tx_dma_tasklet; + +static struct rt_udc_struct controller; +static struct rt_request *handle_outep(struct rt_ep_struct *rt_ep); + +static int debuglevel_read(char *page, char **start, off_t off,int count, int *eof, void *data) +{ + int len; + sprintf(page, "%d\n", debuglevel); + len = strlen(page) + 1; + *eof = 1; + return len; +} + +static int debuglevel_write(struct file *file, const char *buffer, unsigned long count, void *data) +{ + char tmp[32]; + count = (count > 32) ? 32 : count; + memset(tmp, 0, 32); + if (copy_from_user(tmp, buffer, count)) + return -EFAULT; + debuglevel = simple_strtol(tmp, 0, 10); + return count; +} + +static void ep0_chg_stat(const char *label, struct rt_udc_struct *rt_usb, enum ep0_state stat) +{ + xprintk("<0st>%s->%s\n", state_name[rt_usb->ep0state], state_name[stat]); + + if (rt_usb->ep0state == stat) + return; + rt_usb->ep0state = stat; +} + +static u8 read_epcs(struct rt_ep_struct *rt_ep) +{ + int idx = EP_NO(rt_ep); + int dir = EP_DIR(rt_ep); + + if(idx == 0) + return usb_read(EP0CS); + + return (dir == EP_IN ? usb_read(0x7 + idx*8) : usb_read(0x3 + idx*8) ); +} + +static void write_epcs(struct rt_ep_struct *rt_ep, u8 val) +{ + int idx = EP_NO(rt_ep); + int dir = EP_DIR(rt_ep); + + if(idx == 0) + usb_write(EP0CS, val); + else + (dir == EP_IN ? /*IN */ usb_write(0x7 + idx*8, val) : usb_write(0x3 + idx*8, val) ); +} + +static u16 read_inbc(int epnum) +{ + u16 low, high = 0; + if(epnum == 0){ /* EP0 */ + low = usb_read(IN0BC); + }else{ + low = usb_read(epnum * 8 + 4); + high = usb_read((epnum * 8 + 4)+1); + } + return (low | (high << 8)); +} + +static u16 read_outbc(int epnum) +{ + u16 low, high = 0; + if(epnum == 0){ /* EP0 */ + low = usb_read(OUT0BC); + }else{ + low = usb_read(epnum * 8); + high = usb_read((epnum * 8)+1); + } + return (low | (high << 8)); +} + + +static void rt_all_eps_reset(void) +{ + // reset(toggle & fifo) all 16 IN & 16 OUT endpoints + usb_write(ENDPRST, 0x10); + usb_write(ENDPRST, 0x70); + usb_write(ENDPRST, 0x00); + usb_write(ENDPRST, 0x60); +} + +static void rt_ep_rst(struct rt_ep_struct *rt_ep) +{ + u8 reg = 0; + u8 idx = EP_NO(rt_ep); + u8 dir = EP_DIR(rt_ep); + if(dir == EP_IN ) + reg |= ENDPRST_IO | idx; + usb_write(ENDPRST, reg); + + reg |= ENDPRST_TOGRST | ENDPRST_FIFORST; + usb_write(ENDPRST, reg); +} + +static void rt_ep_irq_enable(struct rt_ep_struct *rt_ep) +{ + u8 reg; + u8 idx = EP_NO(rt_ep); + u8 dir = EP_DIR(rt_ep); + + if(idx == 0 /* ep0 */){ + usb_write(IN07IEN, (usb_read(IN07IEN) | 0x1) ); + usb_write(OUT07IEN, (usb_read(OUT07IEN) | 0x1) ); + }else{ /* epX */ + reg = usb_read(dir ? IN07IEN : OUT07IEN); + reg = reg | (0x1 << idx); + usb_write(dir == EP_IN ? IN07IEN : OUT07IEN, reg); + reg = usb_read(dir ? IN07IEN : OUT07IEN); + } +} + +static void rt_udc_init_ep(struct rt_udc_struct *rt_usb) +{ + DBG; + if(dma){ +#if defined (CONFIG_RALINK_RT3883) || defined (CONFIG_RALINK_RT3352) || defined (CONFIG_RALINK_MT7620) + usb_write(IN1CON, 0x8D); // InEP1 : Int, 2 subfifos + usb_write(IN2CON, 0x89); // InEP2 : Bulk, 2 subfifos + usb_write(OUT1CON, 0x8D); // OutEP1 : Int, 2 subfifos + usb_write(OUT2CON, 0x89); // OutEP2 : Bulk, 2 subfifos + //usb_write(OUT3CON, 0x89); // OutEP3 : Bulk, 2 subfifos + //usb_write(OUT4CON, 0x89); // OutEP4 : Bulk, 2 subfifos +#elif defined (CONFIG_RALINK_RT5350) + usb_write(IN1CON, 0x89); // InEP1 : BULK, 2 subfifos + usb_write(OUT1CON, 0x89); // OutEP1 : BULK, 2 subfifos +#else +#error "define a platform" +#endif + }else{ +#if defined (CONFIG_RALINK_RT3883) || defined (CONFIG_RALINK_RT3352) || defined (CONFIG_RALINK_MT7620) + usb_write(IN1CON, 0x8C); // InEP1 : Int , 1 subfifos + usb_write(IN2CON, 0x88); // InEP2 : Bulk, 1 subfifo + usb_write(OUT1CON, 0x8C); // OutEP1 : Int, 1 subfifos + usb_write(OUT2CON, 0x88); // OutEP2 : Bulk, 1 subfifos + //usb_write(OUT3CON, 0x88); // OutEP3 : Bulk, 1 subfifo + //usb_write(OUT4CON, 0x88); // OutEP4 : Bulk. 1 subfifo + +#elif defined (CONFIG_RALINK_RT5350) + usb_write(IN1CON, 0x88); // InEP1 : BULK , 1 subfifos + usb_write(OUT1CON, 0x88); // OutEP1 : BULK, 1 subfifos +#else +#error "define a platform" +#endif + } + // clear all pending HW interrupts + usb_write(IN07IRQ, 0xFF); + usb_write(OUT07IRQ, 0xFF); + rt_all_eps_reset(); + rt_ep_irq_enable(&rt_usb->rt_ep[0]); +} + +static void rt_udc_init_fifo(struct rt_udc_struct *rt_usb) +{ + // fifo control + if(dma){ + usb_write(FIFOCTRL, 0x31); // INEP1, Autoin = 1 + usb_write(FIFOCTRL, 0x32); // INEP2, Autoin = 1 + usb_write(FIFOCTRL, 0x21); // OUTEP1, Autoin = 1 + usb_write(FIFOCTRL, 0x22); // OUTEP2, Autoin = 1 + //usb_write(FIFOCTRL, 0x23);// OUTEP3, Autoin = 1 + //usb_write(FIFOCTRL, 0x24);// OUTEP4, Autoin = 1 + + usb_write(FIFOCTRL, 0x00); // Access by DMA + }else{ + usb_write(FIFOCTRL, 0x11); // INEP1, Autoin = 0 + usb_write(FIFOCTRL, 0x12); // INEP2, Autoin = 0 + usb_write(FIFOCTRL, 0x01); // OUTEP1, Autoin = 0 + usb_write(FIFOCTRL, 0x02); // OUTEP2, Autoin = 0 + //usb_write(FIFOCTRL, 0x03);// OUTEP3, Autoin = 0 + //usb_write(FIFOCTRL, 0x04);// OUTEP4, Autoin = 0 + + usb_write(FIFOCTRL, 0x80); // Access by CPU + } +} + +static void rt_udc_init(struct rt_udc_struct *rt_usb) +{ + /* Setup & Init endpoints */ + rt_udc_init_ep(rt_usb); + + // Enable HS, reset, suspend, SETUP valid data interrupt + usb_write(USBIRQ, 0xff); // clear first + usb_write(USBIEN, 0x21); + + /* Setup ep fifos */ + rt_udc_init_fifo(rt_usb); +} + +static void rt_ep_irq_disable(struct rt_ep_struct *rt_ep) +{ + u8 reg; + u8 idx = EP_NO(rt_ep); + u8 dir = EP_DIR(rt_ep); + + if(idx == 0 /* ep0 */){ + usb_write(IN07IEN, (usb_read(IN07IEN) & ~(0x1)) ); + usb_write(OUT07IEN, (usb_read(OUT07IEN) & ~(0x1)) ); + }else{ + reg = usb_read(dir ? IN07IEN : OUT07IEN); + reg = reg & ~(0x1 << idx); + usb_write(dir == EP_IN ? IN07IEN : OUT07IEN, reg); + reg = usb_read(dir ? IN07IEN : OUT07IEN); + } +} + +static u32 rt_fifo_bcount(struct rt_ep_struct *rt_ep) +{ + u8 low, high; + u32 rc; + + int idx = EP_NO(rt_ep); + int dir = EP_DIR(rt_ep); + + if(idx == 0) + return 0; + + if(dir /* IN */){ + low = usb_read(0x004 + idx*8); + high = usb_read( (0x004 + idx*8)+1 ); + }else{ /* OUT */ + low = usb_read(0x000 + idx*8); + high = usb_read( (0x000 + idx*8)+1 ); + } + rc = high | low; + return rc; +} + +void rt_flush(struct rt_ep_struct *rt_ep) +{ + rt_ep_rst(rt_ep); +} + +void rt_ep_stall(struct rt_ep_struct *rt_ep, int value) +{ + u8 tmp; + u32 addr; + int idx = EP_NO(rt_ep); + int dir = EP_DIR(rt_ep); + + if(idx == 0){ + tmp = usb_read(EP0CS); + if(value) + tmp |= 0x1; + else + tmp &= ~(0x1); + usb_write(EP0CS, tmp); + }else{ + addr = (dir == EP_IN ? 0x006 : 0x002) + idx * 8; + tmp = usb_read(addr); + if(value) + tmp |= 0x40; + else + tmp &= ~(0x40); + usb_write(addr, tmp); + } + + return; +} + +static int rt_udc_get_frame(struct usb_gadget *_gadget) +{ + return 0; +} + +static int rt_udc_wakeup(struct usb_gadget *_gadget) +{ + DBG; + return 0; +} + + +/******************************************************************************* + * USB request control functions + ******************************************************************************* + */ +static inline void ep_add_request(struct rt_ep_struct *rt_ep, struct rt_request *req) +{ + if (unlikely(!req)) + return; + req->in_use = 1; + req->zlp_dma_done = 0; + req->rt_ep = rt_ep; + list_add_tail(&req->queue, &rt_ep->queue); +} + +static inline void ep_del_request(struct rt_ep_struct *rt_ep, struct rt_request *req) +{ + if (unlikely(!req)) + return; + list_del_init(&req->queue); + req->zlp_dma_done = 0; + req->in_use = 0; +} + +static void done(struct rt_ep_struct *rt_ep, struct rt_request *req, int status) +{ + ep_del_request(rt_ep, req); + + if (likely(req->req.status == -EINPROGRESS)) + req->req.status = status; + else + status = req->req.status; + + if (status && status != -ESHUTDOWN) + D_ERR(rt_ep->rt_usb->dev, "<%s> complete %s req %p stat %d len %u/%u\n", __func__, rt_ep->ep.name, &req->req, status,req->req.actual, req->req.length); + + req->req.complete(&rt_ep->ep, &req->req); +} + +#if 0 +/* for reference */ +struct tasklet_struct rx_tasklet_tmp; +static void rx_done_do_tasklet(unsigned long arg) +{ + struct rt_ep_struct *rt_ep; + struct rt_request *rt_req; + struct usb_request *usb_req; + struct usb_ep *ep; + int i, rx_count, status = 0; + struct rt_udc_struct *rt_usb = &controller; + + for (i = (IN_EP_NUM+1); i < RT_USB_NB_EP; i++) { + rt_ep = &rt_usb->rt_ep[i]; + ep = &rt_ep->ep; + + // shared by irq handler, protect it + spin_lock_irqsave(&rx_done_lock, rx_done_lock_flags); + rx_count = rt_ep->rx_done_count; + + //spin_unlock_irqrestore(&rx_done_lock, rx_done_lock_flags); + + for (;rx_count > 0; rx_count--) { + if(unlikely(list_empty(&rt_ep->queue))) + FATAL_ERROR("empty queue"); + + rt_req = list_entry(rt_ep->queue.next, struct rt_request, queue); + usb_req = &rt_req->req; + + ep_del_request(rt_ep, rt_req); + rt_ep->rx_done_count--; + + spin_unlock_irqrestore(&rx_done_lock, rx_done_lock_flags); + + if (unlikely(rt_req->req.status == -EINPROGRESS)) + rt_req->req.status = status; + else + status = rt_req->req.status; + + if (unlikely(status && status != -ESHUTDOWN)) + D_ERR(rt_ep->rt_usb->dev, "<%s> complete %s req %p stat %d len %u/%u\n", __func__, rt_ep->ep.name, &rt_req->req, status,rt_req->req.actual, rt_req->req.length); + + // indicate gadget driver. + usb_req->complete(ep, usb_req); + + spin_lock_irqsave(&rx_done_lock, rx_done_lock_flags); + } + spin_unlock_irqrestore(&rx_done_lock, rx_done_lock_flags); + } +} +#endif + +struct tasklet_struct tx_tasklet; +static void tx_do_tasklet(unsigned long arg) +{ + return; +} + +struct tasklet_struct rx_tasklet; +static void rx_do_tasklet(unsigned long arg) +{ + struct rt_ep_struct *rt_ep; + struct rt_request *req; + struct usb_ep *ep; + int i; + struct rt_udc_struct *rt_usb = &controller; + + for (i = (IN_EP_NUM+1/* EP0 */); i < RT_USB_NB_EP; i++){ + u8 epcs; + rt_ep = &rt_usb->rt_ep[i]; + ep = &rt_ep->ep; + + epcs = read_epcs(rt_ep); + while(!(epcs & EP_CS_BSY)){ + req = handle_outep(rt_ep); + if(!req){ + // No usb request found. + // Just set up the flag (pending) and clear int. + rt_ep->pending = 1; + break; + }else{ + if(req && ( (req->req.actual % rt_ep->ep.maxpacket) || (req->req.actual >= req->req.length))){ + xprintk("q.l=%d,q.a=%d\n", req->req.length, req->req.actual); + done(rt_ep, req, 0); + } + } + + epcs = read_epcs(rt_ep); + write_epcs(rt_ep, 0x0); + epcs = read_epcs(rt_ep); + } + } +} + +static void nuke(struct rt_ep_struct *rt_ep, int status) +{ + struct rt_request *req; + + DBG; + while (!list_empty(&rt_ep->queue)) { + req = list_entry(rt_ep->queue.next, struct rt_request, queue); + done(rt_ep, req, status); + } +} + +/* + ******************************************************************************* + * Data tansfer over USB functions + ******************************************************************************* + */ +static int read_ep0_fifo(struct rt_ep_struct *rt_ep, struct rt_request *req) +{ + u8 *buf; + int byte_count, req_bufferspace, count, i; + +DBG; + if(!in_irq()) + FATAL_ERROR("not irq context."); + + byte_count = read_outbc(EP_NO(rt_ep)); + req_bufferspace = req->req.length - req->req.actual; + + buf = req->req.buf + req->req.actual; + + if(!req_bufferspace) + FATAL_ERROR("zlp"); + + if(byte_count > req_bufferspace) + FATAL_ERROR("buffer overflow, byte_count=%d, req->req.length=%d, req->req.actual=%d\n", byte_count, req->req.length ,req->req.actual); + + count = min(byte_count, req_bufferspace); + + //test, Access by CPU + if(dma) + usb_write(FIFOCTRL, 0x80); + + for (i = 0; i < count; i++){ + *buf = usb_read(EP0OUTDAT+i); + buf++; + } + req->req.actual += count; + + //test, Access by DMA + if(dma) + usb_write(FIFOCTRL, 0x00); + + return count; +} + + +static int read_ep_fifo(struct rt_ep_struct *rt_ep, struct rt_request *req) +{ + u8 *buf, ep_no, ep_no_shift; + int byte_count, req_bufferspace, count, i; + +DBG; + ep_no = EP_NO(rt_ep); + + byte_count = read_outbc(ep_no); + if(unlikely(!byte_count)) + FATAL_ERROR("ep_no:%d bc = 0", ep_no); + + req_bufferspace = req->req.length - req->req.actual; + + buf = req->req.buf + req->req.actual; + + if(unlikely(!req_bufferspace)) + FATAL_ERROR("zlp"); + + xprintk("bc=%d,r.l=%d,r.a=%d\n", byte_count, req->req.length ,req->req.actual); + if(unlikely(byte_count > req_bufferspace)) + FATAL_ERROR("buffer overflow, byte_count=%d, req->req.length=%d, req->req.actual=%d\n", byte_count, req->req.length ,req->req.actual); + + count = min(byte_count, req_bufferspace); + + ep_no_shift = 0x80+ep_no * 4; + for (i = 0; i < count; i++){ + *buf = usb_read(ep_no_shift); + buf++; + } + + req->req.actual += count; + + // EP Out irq handler would arm another transaction. + return count; +} + +static int write_ep_fifo_zlp(struct rt_ep_struct *rt_ep) +{ + u8 epcs; + int ep_no = EP_NO(rt_ep); + +DBG; + xprintk("w%d ZLP\n", EP_NO(rt_ep)); + epcs = read_epcs(rt_ep); + if(epcs & EP_CS_BSY) + FATAL_ERROR("EP%d busy. cs=%x\n", ep_no, epcs); + + /* check INEP byte count is zero? */ + if(read_inbc(ep_no)) + FATAL_ERROR("EP%d bc zero. bc=%d\n", ep_no, read_inbc(ep_no)); + + epcs = read_epcs(rt_ep); + write_epcs(rt_ep, epcs); + return 0; +} + + +/* + * handle_epinirq() +*/ +static int write_ep_fifo(struct rt_ep_struct *rt_ep, struct rt_request *req) +{ + u8 *buf, epcs; + int length, i, ep_no = EP_NO(rt_ep); + +DBG; + xprintk("w ep%d req=%p,r.l=%d,r.a=%d\n",EP_NO(rt_ep),&req->req,req->req.length,req->req.actual); + epcs = read_epcs(rt_ep); + if(epcs & EP_CS_BSY) + FATAL_ERROR("EP%d busy. epcs=%x\n", ep_no, epcs); + + /* check INEP byte count is zero? */ + if(read_inbc(ep_no)) + FATAL_ERROR("EP%d bc=%d\n", ep_no, read_inbc(ep_no)); + + buf = req->req.buf + req->req.actual; + length = (req->req.length - req->req.actual) < rt_ep->ep.maxpacket ? (req->req.length - req->req.actual) : rt_ep->ep.maxpacket; + req->req.actual += length; + if (!length) { /* zlp */ + // for debug + xprintk("<%s> zero packet\n", __func__); + write_ep_fifo_zlp(rt_ep); + return 0; + } + + // write to ep in fifo + for (i=0; i< length; i++) + usb_write(0x80+ep_no*4, *buf++); + + epcs = read_epcs(rt_ep); + write_epcs(rt_ep, epcs); + + return length; +} + +/* + * + */ +static int write_ep0_fifo(struct rt_ep_struct *rt_ep, struct rt_request *req) +{ + u8 *buf; + int length, i; + u32 maxpacket; + +DBG; + xprintk("q.l=%d, q.a=%d, maxp=%d\n", req->req.length, req->req.actual, rt_ep->ep.maxpacket); + + buf = req->req.buf + req->req.actual; + maxpacket = (u32)(rt_ep->ep.maxpacket); + length = min(req->req.length - req->req.actual, maxpacket); + + req->req.actual += length; + + if (!length && req->req.zero) + FATAL_ERROR("zlp"); + + if(!in_irq()) + FATAL_ERROR("Not in irq context"); + + //test, Access by CPU + if(dma) + usb_write(FIFOCTRL, 0x80); + + //write to ep0in fifo + for (i=0; i< length; i++) + usb_write(EP0INDAT+i, *buf++); + + // arm ep0in + usb_write(IN0BC, length); + if(length != rt_ep->ep.maxpacket) + usb_write(EP0CS, 0x2); // clear NAK bit to ACK host. + + //test, Access by CPU + if(dma) + usb_write(FIFOCTRL, 0x00); + + return length; +} + +static struct rt_request *get_unhandled_req(struct rt_ep_struct *rt_ep) +{ + struct list_head *p; + struct rt_request *req = NULL; + + if(EP_DIR(rt_ep) == EP_OUT) + FATAL_ERROR("Out EP"); + + if(dma){ + list_for_each(p, &rt_ep->queue){ + req = list_entry(p, struct rt_request, queue); + if(req->req.length > req->req.actual ) + return req; + else if(unlikely(req->req.length == 0 && req->zlp_dma_done == 0)) + return req; + else + continue; + } + return NULL; + }else{ + if (!list_empty(&rt_ep->queue)){ + req = list_entry(rt_ep->queue.next, struct rt_request, queue); + }else { + FATAL_ERROR("%s No request", rt_ep->ep.name); + } + return req; + } +} + +#define PADDING_LENGTH 64 +static int write_dma_txring(struct rt_ep_struct *rt_ep,struct rt_request *req) +{ + u8 *buf; + int length; + int retry_times = 0; + u32 hw_current_idx; +DBG; + xprintk("w%dr=%p,r.l=%d,r.a=%d\n", EP_NO(rt_ep), &req->req,req->req.length,req->req.actual); + + length = req->req.length; + + while(length > 0 || (req->req.length == 0 && req->zlp_dma_done == 0)){ +retry: + /* wait for a free TXD */ + hw_current_idx = reg_read(RTUSB_TX_DTX_IDX0); + if ( tx_ring0_cache[tx_cpu_owner_idx0].txd_info2.DDONE_bit == 0 || + ((tx_cpu_owner_idx0+1) % NUM_TX_DESC == hw_current_idx) ) { + if(retry_times > 1000) + return -1; + mdelay(1); + retry_times++; + goto retry; + } + + if(length > TXMAXCAP) + length = TXMAXCAP; + + buf = req->req.buf + req->req.actual; + req->req.actual += length; + + /* deal with ZLP.*/ + if(req->req.length == 0 && req->zlp_dma_done == 0) + req->zlp_dma_done = 1; + + req->txd_count++; + +#define phys_to_bus(a) ((u32)a & 0x1FFFFFFF) + if(length){ + tx_ring0_cache[tx_cpu_owner_idx0].txd_info1.SDP0 = cpu_to_le32(phys_to_bus(buf)); + tx_ring0_cache[tx_cpu_owner_idx0].txd_info2.SDL0 = cpu_to_le32(length); + tx_ring0_cache[tx_cpu_owner_idx0].txd_info4.zlp_flag = 0; + dma_cache_sync(NULL, (void *)buf, length, DMA_TO_DEVICE); + }else{ + tx_ring0_cache[tx_cpu_owner_idx0].txd_info1.SDP0 = cpu_to_le32(phys_to_bus(tx_zlp_dummy_buf)); + tx_ring0_cache[tx_cpu_owner_idx0].txd_info2.SDL0 = cpu_to_le32(sizeof(tx_zlp_dummy_buf)); + tx_ring0_cache[tx_cpu_owner_idx0].txd_info4.zlp_flag = 1; + } + + tx_ring0_cache[tx_cpu_owner_idx0].txd_info4.In_ep_addr = cpu_to_le32(EP_NO(rt_ep)); + tx_ring0_cache[tx_cpu_owner_idx0].txd_info2.DDONE_bit = 0; + tx_cpu_owner_idx0 = (tx_cpu_owner_idx0 + 1) % NUM_TX_DESC; + + length = req->req.length - req->req.actual; + } + + reg_write(RTUSB_TX_CTX_IDX0, tx_cpu_owner_idx0); + + return 0; +} + + +/******************************************************************************* + * Endpoint handlers + ******************************************************************************* + */ + +/* + * Handle In Endpoint + * CPU(FIFO): + * Enqueue -> Write fifo -> TX_DONE -> Write fifo -> TX_DONE -> .. + * + * DMA + * Enqueue -> Kick off TxD. Enqueue -> Kick off TxD. Enqueue -> Kick off TxD. + */ +static int handle_inep(struct rt_ep_struct *rt_ep) +{ + struct rt_request *req; + +DBG; + if(!(req = get_unhandled_req(rt_ep))) + return -1; + + if(dma){ + write_dma_txring(rt_ep, req); + }else{ + write_ep_fifo(rt_ep, req); + rt_ep->tx_done_count = 1; + } + return 0; +} + +/* + * IRQ context. + */ +static struct rt_request *handle_outep(struct rt_ep_struct *rt_ep) +{ + struct rt_request *req; + struct list_head *p; + int count = 0; + +DBG; + if (list_empty(&rt_ep->queue)){ + return NULL; + } + + list_for_each(p, &rt_ep->queue){ + if(count != rt_ep->rx_done_count){ + count++; + continue; + } + req = list_entry(p, struct rt_request, queue); + read_ep_fifo(rt_ep, req); + return req; + } + + return NULL; +} + +static struct rt_request *handle_inep0(struct rt_ep_struct *rt_ep) +{ + struct rt_request *req = NULL; + +DBG; + if (list_empty(&rt_ep->queue)) { + D_ERR(rt_ep->rt_usb->dev, "<%s> no request on %s\n", __func__, rt_ep->ep.name); + return NULL; + } + + req = list_entry(rt_ep->queue.next, struct rt_request, queue); + switch (rt_ep->rt_usb->ep0state) { + case EP0_IN_DATA_PHASE: /* GET_DESCRIPTOR */ + write_ep0_fifo(rt_ep, req); + break; + + // Impossible: + //case EP0_OUT_DATA_PHASE: /* SET_DESCRIPTOR */ + //case EP0_NO_DATA_PHASE: /* for no data stage control transfer */ + + default: + D_ERR(rt_ep->rt_usb->dev, "<%s> ep0 i/o, odd state %d\n", __func__, rt_ep->rt_usb->ep0state); + ep_del_request(rt_ep, req); + req = NULL; + break; + } + + return req; +} + +static struct rt_request *handle_outep0(struct rt_ep_struct *rt_ep) +{ + struct rt_request *req = NULL; + +DBG; + if (list_empty(&rt_ep->queue)) { + D_ERR(rt_ep->rt_usb->dev, "<%s> no request on %s\n", __func__, rt_ep->ep.name); + return NULL; + } + + if(rt_ep->rt_usb->ep0state != EP0_OUT_DATA_PHASE){ + D_EP0(rt_ep->rt_usb->dev, "<%s> ep0 i/o, odd state %d\n", __func__, rt_ep->rt_usb->ep0state); + ep_del_request(rt_ep, req); + req = NULL; + } + + req = list_entry(rt_ep->queue.next, struct rt_request, queue); + + read_ep0_fifo(rt_ep, req); + + return req; +} + +/******************************************************************************* + * USB gadget callback functions + ******************************************************************************* + */ +static void handle_dma_rxdone(struct rt_udc_struct *rt_usb) +{ + DBG; + tasklet_schedule(&rx_dma_tasklet); +} + +static void handle_dma_txdone(struct rt_udc_struct *rt_usb) +{ + DBG; + tasklet_schedule(&tx_dma_tasklet); +} + +static void handle_dmairq(struct rt_udc_struct *rt_usb, u32 irq) +{ + if(irq & RTUSB_RX_DONE_INT0){ + handle_dma_rxdone(rt_usb); + } + + if(irq & RTUSB_TX_DONE_INT0){ + handle_dma_txdone(rt_usb); + } + + reg_write(RTUSB_INT_STATUS, irq); +} + +static inline int udc_dma_reset_txring(void) +{ + int count = 0; + u32 reg; + + while(count++< RING_RESET_TIMEOUT){ + reg = reg_read(RTUSB_PDMA_GLO_CFG); + if(reg & RTUSB_TX_DMA_BUSY){ + mdelay(1); + }else + break; + + } + if(count== RING_RESET_TIMEOUT) + return -1; + + reg = reg_read(RTUSB_PDMA_RST_IDX); + udelay(100); + reg |= (RTUSB_RST_DTX_IDX1 | RTUSB_RST_DTX_IDX0); + reg_write(RTUSB_PDMA_RST_IDX, reg); + udelay(100); + return 0; +} + +static inline int udc_dma_reset_rxring(void) +{ + int count = 0; + u32 reg; + + while(count++< RING_RESET_TIMEOUT){ + reg = reg_read(RTUSB_PDMA_GLO_CFG); + if(reg & RTUSB_RX_DMA_BUSY){ + mdelay(1); + }else + break; + } + if(count== RING_RESET_TIMEOUT) + return -1; + + reg = reg_read(RTUSB_PDMA_RST_IDX); + udelay(100); + reg |= (RTUSB_RST_DRX_IDX1 | RTUSB_RST_DRX_IDX0); + reg_write(RTUSB_PDMA_RST_IDX, reg); + udelay(100);\ + return 0; +} + +static int udc_dma_hw_reset(void) +{ + if(udc_dma_reset_rxring() == -1) + return -1; + if(udc_dma_reset_txring() == -1) + return -1; + return 0; +} + +static void udc_dma_enable(int enable) +{ + u32 reg; + reg = reg_read(RTUSB_PDMA_GLO_CFG); + udelay(100); + if(enable) + reg |= RTUSB_TX_WB_DDONE | RTUSB_RX_DMA_EN | RTUSB_TX_DMA_EN ; + else + reg &= ~(RTUSB_TX_WB_DDONE | RTUSB_RX_DMA_EN | RTUSB_TX_DMA_EN) ; + reg_write(RTUSB_PDMA_GLO_CFG, reg); + udelay(500); +} + +static void udc_dma_int_enable(int enable) +{ + u32 reg; + reg = reg_read(RTUSB_INT_MASK); + udelay(100); + if(enable) + reg |= RTUSB_RX_DONE_INT_MSK0 | RTUSB_TX_DONE_INT_MSK0 ; + else + reg &= ~(RTUSB_RX_DONE_INT_MSK0 | RTUSB_TX_DONE_INT_MSK0) ; + reg_write(RTUSB_INT_MASK, reg); + udelay(100); +} + +static inline void udc_dma_tx_int_clear(void) +{ + reg_write(RTUSB_INT_STATUS, 0x0000000F); +} + +static inline void udc_dma_rx_int_clear(void) +{ + reg_write(RTUSB_INT_STATUS, 0x00030000); +} + +static inline void udc_dma_all_int_clear(void) +{ + reg_write(RTUSB_INT_STATUS, 0xFFFFFFFF); +} + +static int copy_data_to_ep(void *src, int length, int ep_num) +{ + struct rt_ep_struct *rt_ep; + struct rt_udc_struct *rt_usb = &controller; + struct rt_request *req; + int req_bufferspace, count; + u8 *buf; + + DBG; + rt_ep = &rt_usb->rt_ep[ep_num+IN_EP_NUM]; + + if (list_empty(&rt_ep->queue)){ + /* It is safe to return 0 if no req queued. */ + return 0; + } + + req = list_entry(rt_ep->queue.next, struct rt_request, queue); + req_bufferspace = req->req.length - req->req.actual; + + if(unlikely(!req_bufferspace)){ + // for debug + FATAL_ERROR("zlp"); + return -1; + } + + if(length > req_bufferspace){ + FATAL_ERROR("buffer overflow"); + return -1; + } + + // sync with cache. + if(likely(length)) + dma_cache_sync(NULL, src, length, DMA_FROM_DEVICE); + + buf = req->req.buf + req->req.actual; + count = min(length, req_bufferspace); + memcpy(buf, src, count); + + req->req.actual += count; + + if((req->req.actual % rt_ep->ep.maxpacket) || (req->req.actual >= req->req.length)){ + done(rt_ep, req, 0); // short packet indicates transaction is done. + } + return count; +} + +static void rx_dma_done_do_tasklet(unsigned long arg) +{ + u32 *rxd_info; + u32 length; + int ep, rc; + int processed_count=0; + + DBG; + for (;;){ + if (rx_ring0_cache[rx_dma_owner_idx0].rxd_info2.DDONE_bit == 0) + break; + + if(processed_count++ > RX_RESCHEDULE){ + tasklet_schedule(&rx_dma_tasklet); + break; + } + + length = rx_ring0_cache[rx_dma_owner_idx0].rxd_info4.Rx_bcnt; + ep = rx_ring0_cache[rx_dma_owner_idx0].rxd_info4.Out_ep_addr; + + // copy data from RXD->buffer to ep queue. + rc = copy_data_to_ep((void *)USBRxPackets[rx_dma_owner_idx0], length, ep); + if(rc <= 0) + return; + + rxd_info = (u32 *)&rx_ring0_cache[rx_dma_owner_idx0].rxd_info4; + *rxd_info = 0; + + /* clear DDONE bit*/ + rxd_info = (u32 *)&rx_ring0_cache[rx_dma_owner_idx0].rxd_info2; + *rxd_info = 0; + //rx_ring0_cache[rx_dma_owner_idx0].rxd_info2.DDONE_bit = 0; + //rx_ring0_cache[i].rxd_info2.LS0= 0; + rx_ring0_cache[rx_dma_owner_idx0].rxd_info2.PLEN0= sizeof(u8) * RX_BUFF_SZ; + + /* Move point to next RXD which wants to alloc */ + //OUTL(cpu_to_le32((u32) rx_dma_owner_idx0), RTUSB_RX_CALC_IDX0); + reg_write(RTUSB_RX_CALC_IDX0, rx_dma_owner_idx0); + + /* Update to Next packet point that was received. + */ + rx_dma_owner_idx0 = (rx_dma_owner_idx0 + 1) % NUM_RX_DESC; + } +} + +/* + * Recycle reqs and call gadget complete callback function. + */ +static void tx_dma_done_do_tasklet(unsigned long arg) +{ + int ep_num; + u32 hw_current; + struct rt_ep_struct *rt_ep; + struct rt_request *rt_req; + struct rt_udc_struct *rt_usb = &controller; + + DBG; + while(tx_need_free_idx0 != (hw_current = reg_read(RTUSB_TX_DTX_IDX0))){ + int retry = 0; + while(tx_ring0_cache[tx_need_free_idx0].txd_info2.DDONE_bit != 1){ + mdelay(1); + retry++; + if(retry > 1000) + FATAL_ERROR("tx timeout"); + } + + // rt_ep = tx_ring0_req_mapping[tx_need_free_idx0]; + ep_num = tx_ring0_cache[tx_need_free_idx0].txd_info4.In_ep_addr; + if(!ep_num || ep_num > IN_EP_NUM) + FATAL_ERROR("Out of range"); + + rt_ep = &rt_usb->rt_ep[ep_num]; + if(list_empty(&rt_ep->queue)) + FATAL_ERROR("ep[%d] No request", ep_num); + + rt_req = list_entry(rt_ep->queue.next, struct rt_request, queue); + rt_req->txd_count--; + + + if(rt_req->txd_count == 0) + done(rt_ep, rt_req, 0); + + tx_need_free_idx0 = (tx_need_free_idx0 + 1) % NUM_TX_DESC; + + } +} + +static int udc_dma_rst(void) +{ + if( udc_dma_reset_txring() == -1) + return -1; + if( udc_dma_reset_rxring() == -1) + return -1; + + tx_cpu_owner_idx0 = 0; + tx_need_free_idx0 = 0; + rx_dma_owner_idx0 = 0; + reg_write(RTUSB_RX_CALC_IDX0, cpu_to_le32(NUM_RX_DESC - 1)); + return 0; +} + +static int rt_udc_dma_init(void) +{ + int i; + + if( udc_dma_hw_reset() == -1) + return -1; + + for(i=0; i=0; i--) + kfree((void *)USBRxPackets[i]); + printk("No mem."); + return -1; + } + } + + tx_ring0_cache = dma_alloc_coherent(NULL, sizeof(struct PDMA_txdesc) * NUM_TX_DESC, &tx_ring_bus_addr, GFP_KERNEL); + rx_ring0_cache = dma_alloc_coherent(NULL, sizeof(struct PDMA_rxdesc) * NUM_RX_DESC, &rx_ring_bus_addr, GFP_KERNEL); + + printk("USB PDMA mode enabled.\n"); + printk("tx_ring=%p\n", tx_ring0_cache); + printk("rx_ring=%p\n", rx_ring0_cache); + + tx_ring0_noncache = tx_ring0_cache; + rx_ring0_noncache = rx_ring0_cache; + + for(i=0; i < NUM_RX_DESC; i++){ + memset((void *)&rx_ring0_noncache[i], 0, 16 /* sizeof()*/); + rx_ring0_noncache[i].rxd_info2.DDONE_bit = 0; + rx_ring0_noncache[i].rxd_info2.LS0= 0; + rx_ring0_noncache[i].rxd_info2.PLEN0= sizeof(u8) * RX_BUFF_SZ; + rx_ring0_noncache[i].rxd_info1.PDP0 = dma_map_single(NULL, (void *)USBRxPackets[i], sizeof(u8) * RX_BUFF_SZ, DMA_FROM_DEVICE); + } + + for (i=0; i < NUM_TX_DESC; i++) { + memset((void *)&tx_ring0_noncache[i],0, 16 /* sizeof()*/); + tx_ring0_noncache[i].txd_info2.LS0_bit = 1; + tx_ring0_noncache[i].txd_info2.DDONE_bit = 1; + // we would map dma buffer dynamically in IRQ handler & ep_queue(); + } + + rx_dma_owner_idx0 = 0; + tx_cpu_owner_idx0 = 0; + tx_need_free_idx0 = 0; + + /* initial UDMA register */ + //OUTL(cpu_to_le32((u32) UDMA_Init_Setting), RTUSB_UDMA_CTRL); + + if(sm){ + printk("Storage mode enabled.\n"); + reg_write(RTUSB_UDMA_CTRL, 0x3F000063); /* enable storage mode */ + }else + reg_write(RTUSB_UDMA_CTRL, 0x3F000003); + + + /* Tell the adapter where the TX/RX rings are located. */ + //OUTL(phys_to_bus((u32) &rx_ring[0]), RTUSB_RX_BASE_PTR0); + reg_write(RTUSB_RX_BASE_PTR0, rx_ring_bus_addr); + + //OUTL(phys_to_bus((u32) &tx_ring0[0]), RTUSB_TX_BASE_PTR0); + reg_write(RTUSB_TX_BASE_PTR0, tx_ring_bus_addr); + + //OUTL(cpu_to_le32((u32) NUM_RX_DESC), RTUSB_RX_MAX_CNT0); + //OUTL(cpu_to_le32((u32) NUM_TX_DESC), RTUSB_TX_MAX_CNT0); + reg_write(RTUSB_RX_MAX_CNT0, cpu_to_le32(NUM_RX_DESC)); + reg_write(RTUSB_TX_MAX_CNT0, cpu_to_le32(NUM_TX_DESC)); + + //OUTL(cpu_to_le32((u32) tx_cpu_owner_idx0), RTUSB_TX_CTX_IDX0); + //OUTL(cpu_to_le32((u32) (NUM_RX_DESC - 1)), RTUSB_RX_CALC_IDX0); + reg_write(RTUSB_TX_CTX_IDX0, cpu_to_le32(tx_cpu_owner_idx0)); + reg_write(RTUSB_RX_CALC_IDX0, cpu_to_le32(NUM_RX_DESC - 1)); + + udelay(500); + return 0; +} + +static int udc_dma_fini(void) +{ + int i; + u32 len; + dma_addr_t addr; + + udc_dma_enable(false); + udc_dma_int_enable(false); + + /* restore UDMA register */ + reg_write(RTUSB_UDMA_CTRL, 0x0); + + // unmap & free RX buffer + for(i=0; irt_usb; + unsigned long flags; + + DBG; + + if (!usb_ep || !desc || !EP_NO(rt_ep) || desc->bDescriptorType != USB_DT_ENDPOINT || rt_ep->bEndpointAddress != desc->bEndpointAddress) { + D_ERR(rt_usb->dev, "<%s> bad ep or descriptor\n", __func__); + return -EINVAL; + } + if (rt_ep->bmAttributes != desc->bmAttributes) { + D_ERR(rt_usb->dev, "<%s> %s type mismatch, 0x%x, 0x%x\n", __func__, usb_ep->name, rt_ep->bmAttributes, desc->bmAttributes); + return -EINVAL; + } + if (!rt_usb->driver || rt_usb->gadget.speed == USB_SPEED_UNKNOWN) { + D_ERR(rt_usb->dev, "<%s> bogus device state\n", __func__); + return -ESHUTDOWN; + } + local_irq_save(flags); + rt_ep->stopped = 0; + if(dma){ + //rt_ep_irq_enable(rt_ep); + }else + rt_ep_irq_enable(rt_ep); + local_irq_restore(flags); + + xprintk("<%s> ENABLED %s\n", __func__, usb_ep->name); + return 0; +} + +static int rt_ep_disable(struct usb_ep *usb_ep) +{ + struct rt_ep_struct *rt_ep = container_of(usb_ep, struct rt_ep_struct, ep); + unsigned long flags; + +DBG; + if (!usb_ep || !EP_NO(rt_ep) /* || !list_empty(&rt_ep->queue) */) { + D_ERR(rt_ep->rt_usb->dev, "<%s> %s can not be disabled\n", __func__, usb_ep ? rt_ep->ep.name : NULL); + return -EINVAL; + } + + local_irq_save(flags); + rt_ep->stopped = 1; + nuke(rt_ep, -ESHUTDOWN); + rt_flush(rt_ep); + rt_ep_irq_disable(rt_ep); + + local_irq_restore(flags); + + xprintk("<%s> DISABLED %s\n", __func__, usb_ep->name); + return 0; +} + +static struct usb_request *rt_ep_alloc_request (struct usb_ep *usb_ep, gfp_t gfp_flags) +{ + struct rt_request *req; + + DBG; + req = kzalloc(sizeof *req, gfp_flags); + if (!req || !usb_ep) + return 0; + + INIT_LIST_HEAD(&req->queue); + req->in_use = 0; + return &req->req; +} + +static void rt_ep_free_request(struct usb_ep *usb_ep, struct usb_request *usb_req) +{ + struct rt_request *req; + + DBG; + req = container_of(usb_req, struct rt_request, req); + WARN_ON(!list_empty(&req->queue)); + kfree(req); +} + +/* + * Two cases : + * 1) UDC TX (IN EPs) + * enqueue req -> handle_ep() -> write fifo -> TX_DONE -> handle_ep() -> write next fifo -> TX_DONE... + * + * 2) UDC RX (OUT EPs) + * enqueue req -> RX_DONE -> handle_ep() -> read_fifo -> RX_DONE -> handle_ep() -> read fifo... + */ +static int rt_ep_queue(struct usb_ep *usb_ep, struct usb_request *req, gfp_t gfp_flags) +{ + struct rt_ep_struct *rt_ep; + struct rt_udc_struct *rt_usb; + struct rt_request *rt_req; + unsigned long flags; + int ret = 0; + int handle_right_now = 0; + + rt_ep = container_of(usb_ep, struct rt_ep_struct, ep); + rt_usb = rt_ep->rt_usb; + rt_req = container_of(req, struct rt_request, req); + rt_req->rt_ep = rt_ep; + + if (rt_usb->set_config && !EP_NO(rt_ep)) { + rt_usb->set_config = 0; + D_ERR(rt_usb->dev, "<%s> gadget reply set config\n", __func__); + return 0; + } + + if (unlikely(!req || !rt_req || !req->complete || !req->buf)) { + D_ERR(rt_usb->dev, "<%s> bad params\n", __func__); + return -EINVAL; + } + + if (unlikely(!usb_ep || !rt_ep)) { + D_ERR(rt_usb->dev, "<%s> bad ep\n", __func__); + return -EINVAL; + } + + if (!rt_usb->driver || rt_usb->gadget.speed == USB_SPEED_UNKNOWN) { + D_ERR(rt_usb->dev, "<%s> bogus device state\n", __func__); + return -ESHUTDOWN; + } + + /* debug */ + xprintk(" ep%d%s %p %dB\n", EP_NO(rt_ep), ((!EP_NO(rt_ep) && rt_ep->rt_usb->ep0state == EP0_IN_DATA_PHASE) || (EP_NO(rt_ep) && EP_DIR(rt_ep) == EP_IN )) ? "IN" : "OUT", &rt_req->req, req->length); + + if (rt_ep->stopped) { + printk("EP%d -> stopped.\n", EP_NO(rt_ep)); + req->status = -ESHUTDOWN; + return -ESHUTDOWN; + } + + if (rt_req->in_use) { + D_ERR(rt_usb->dev, "<%s> refusing to queue req %p (already queued)\n", __func__, req); + return -1; + } + + local_irq_save(flags); + /* + * handle No-data Ctrl transfer. + */ + if(!EP_NO(rt_ep)/* EP0 */ && EP_DIR(rt_ep) == EP_OUT && !req->length){ + done(rt_ep, rt_req, 0); + local_irq_restore(flags); + return ret; + } + + req->status = -EINPROGRESS; + req->actual = 0; + + if(dma || list_empty(&rt_ep->queue)) + handle_right_now = 1; + + ep_add_request(rt_ep, rt_req); + + if(handle_right_now){ + if(!EP_NO(rt_ep) && rt_ep->rt_usb->ep0state != EP0_OUT_DATA_PHASE){ /* ep0 && TX*/ + handle_inep0(rt_ep); + }else if( EP_DIR(rt_ep) == EP_IN){ /* epin[1-x] */ + handle_inep(rt_ep); + }else{ + // other reqs are waiting for TX_DONE int. + } + } + + if(dma){ + if(EP_NO(rt_ep) && (EP_DIR(rt_ep) == EP_OUT)) + tasklet_schedule(&rx_dma_tasklet); + }else{ + if( (EP_DIR(rt_ep) == EP_OUT)/* OUT EP */ && rt_ep->pending){ + rt_ep->pending = 0; + handle_pending_epoutirq(rt_usb, rt_ep, rt_req); + } + } + + local_irq_restore(flags); + return ret; +} + +static int rt_ep_dequeue(struct usb_ep *usb_ep, struct usb_request *usb_req) +{ + struct rt_ep_struct *rt_ep = container_of(usb_ep, struct rt_ep_struct, ep); + struct rt_request *req; + unsigned long flags; + + DBG; + if (unlikely(!usb_ep || !EP_NO(rt_ep))) { + D_ERR(rt_ep->rt_usb->dev, "<%s> bad ep\n", __func__); + return -EINVAL; + } + + local_irq_save(flags); + + /* make sure it's actually queued on this endpoint */ + list_for_each_entry(req, &rt_ep->queue, queue) { + if (&req->req == usb_req) + break; + } + if (&req->req != usb_req) { + local_irq_restore(flags); + return -EINVAL; + } + + done(rt_ep, req, -ECONNRESET); + + local_irq_restore(flags); + return 0; +} + +static int rt_ep_set_halt(struct usb_ep *usb_ep, int value) +{ + struct rt_ep_struct *rt_ep = container_of(usb_ep, struct rt_ep_struct, ep); + unsigned long flags; + + DBG; + if (unlikely(!usb_ep || !EP_NO(rt_ep))) { + D_ERR(rt_ep->rt_usb->dev, "<%s> bad ep\n", __func__); + return -EINVAL; + } + + local_irq_save(flags); + + if ((rt_ep->bEndpointAddress & USB_DIR_IN) && !list_empty(&rt_ep->queue)) { + local_irq_restore(flags); + return -EAGAIN; + } + + rt_ep_stall(rt_ep, 1); + + local_irq_restore(flags); + + D_EPX(rt_ep->rt_usb->dev, "<%s> %s halt\n", __func__, usb_ep->name); + return 0; +} + +static int rt_ep_fifo_status(struct usb_ep *usb_ep) +{ + struct rt_ep_struct *rt_ep = container_of(usb_ep, struct rt_ep_struct, ep); + + DBG; + if (!usb_ep) { + D_ERR(rt_ep->rt_usb->dev, "<%s> bad ep\n", __func__); + return -ENODEV; + } + + if (rt_ep->rt_usb->gadget.speed == USB_SPEED_UNKNOWN) + return 0; + else + return rt_fifo_bcount(rt_ep); +} + +static void rt_ep_fifo_flush(struct usb_ep *usb_ep) +{ + struct rt_ep_struct *rt_ep = container_of(usb_ep, struct rt_ep_struct, ep); + unsigned long flags; + + DBG; + local_irq_save(flags); + + if (!usb_ep || !EP_NO(rt_ep) || !list_empty(&rt_ep->queue)) { + D_ERR(rt_ep->rt_usb->dev, "<%s> bad ep\n", __func__); + local_irq_restore(flags); + return; + } + + /* toggle and halt bits stay unchanged */ + rt_flush(rt_ep); + + local_irq_restore(flags); +} + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,36) +static void *rt_ep_alloc_buffer(struct usb_ep *_ep, unsigned bytes, dma_addr_t *dma, gfp_t gfp_flags) +{ + char *retval; + + retval = kmalloc (bytes, gfp_flags & ~(__GFP_DMA|__GFP_HIGHMEM)); + if (retval) +// *dma = virt_to_bus (retval); + *dma = (dma_addr_t)~0; + return retval; +} + +static void rt_ep_free_buffer(struct usb_ep *_ep, void *buf, dma_addr_t dma, unsigned bytes) +{ + kfree (buf); +} +#endif + +static struct usb_ep_ops rt_ep_ops = { + .enable = rt_ep_enable, + .disable = rt_ep_disable, + + .alloc_request = rt_ep_alloc_request, + .free_request = rt_ep_free_request, +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,36) + .alloc_buffer = rt_ep_alloc_buffer, + .free_buffer = rt_ep_free_buffer, +#endif + .queue = rt_ep_queue, + .dequeue = rt_ep_dequeue, + + .set_halt = rt_ep_set_halt, + .fifo_status= rt_ep_fifo_status, + .fifo_flush = rt_ep_fifo_flush, +}; + +/******************************************************************************* + * USB endpoint control functions + ******************************************************************************* + */ +static void usb_init_data(struct rt_udc_struct *rt_usb) +{ + struct rt_ep_struct *rt_ep; + u8 i; + + DBG; + /* device/ep0 records init */ + INIT_LIST_HEAD(&rt_usb->gadget.ep_list); + INIT_LIST_HEAD(&rt_usb->gadget.ep0->ep_list); + ep0_chg_stat(__func__, rt_usb, EP0_IDLE); + + /* basic endpoint records init */ + for (i = 0; i < RT_USB_NB_EP; i++) { + rt_ep = &rt_usb->rt_ep[i]; + + if (i) { + list_add_tail(&rt_ep->ep.ep_list, &rt_usb->gadget.ep_list); + rt_ep->stopped = 1; + } else + rt_ep->stopped = 0; + + INIT_LIST_HEAD(&rt_ep->queue); + } +} + +static void udc_stop_activity(struct rt_udc_struct *rt_usb, struct usb_gadget_driver *driver) +{ + struct rt_ep_struct *rt_ep; + int i; + + if (rt_usb->gadget.speed == USB_SPEED_UNKNOWN) + driver = NULL; + + /* prevent new request submissions, kill any outstanding requests */ + for (i = 0; i < RT_USB_NB_EP; i++) { + rt_ep = &rt_usb->rt_ep[i]; + if(i != 0){ /* don't have to flush EP[0]. */ + rt_flush(rt_ep); + rt_ep->stopped = 1; + rt_ep_irq_disable(rt_ep); + } + nuke(rt_ep, -ESHUTDOWN); + } + + rt_usb->cfg = 0; + rt_usb->intf = 0; + rt_usb->alt = 0; + + if (driver) + driver->disconnect(&rt_usb->gadget); +} + +/* + * keep for reference. + */ +static void handle_config(unsigned long data) +{ + DBG; +#if 0 + struct imx_udc_struct *imx_usb = (void *)data; + struct usb_ctrlrequest u; + int temp, cfg, intf, alt; + + local_irq_disable(); + + temp = __raw_readl(imx_usb->base + USB_STAT); + cfg = (temp & STAT_CFG) >> 5; + intf = (temp & STAT_INTF) >> 3; + alt = temp & STAT_ALTSET; + + xprintk("<%s> orig config C=%d, I=%d, A=%d / req config C=%d, I=%d, A=%d\n", __func__, imx_usb->cfg, imx_usb->intf, imx_usb->alt, cfg, intf, alt); + + if (cfg == 1 || cfg == 2) { + + if (imx_usb->cfg != cfg) { + u.bRequest = USB_REQ_SET_CONFIGURATION; + u.bRequestType = USB_DIR_OUT | USB_TYPE_STANDARD | USB_RECIP_DEVICE; + u.wValue = cfg; + u.wIndex = 0; + u.wLength = 0; + imx_usb->cfg = cfg; + imx_usb->driver->setup(&imx_usb->gadget, &u); + + } + if (imx_usb->intf != intf || imx_usb->alt != alt) { + u.bRequest = USB_REQ_SET_INTERFACE; + u.bRequestType = USB_DIR_OUT | USB_TYPE_STANDARD | USB_RECIP_INTERFACE; + u.wValue = alt; + u.wIndex = intf; + u.wLength = 0; + imx_usb->intf = intf; + imx_usb->alt = alt; + imx_usb->driver->setup(&imx_usb->gadget, &u); + } + } + + imx_usb->set_config = 0; + + local_irq_enable(); +#endif +} + +static void handle_setup(struct rt_udc_struct *rt_usb) +{ + u8 epcs; + int i; + union { + struct usb_ctrlrequest r; + u8 raw[8]; + u32 word[2]; + } u; + struct rt_ep_struct *rt_ep = &rt_usb->rt_ep[0]; + + nuke(rt_ep, -EPROTO); + + // read setup packet + for (i = 0; i < 8; i++) + u.raw[i] = usb_read(SETUPDATA + i); + + le16_to_cpus(&u.r.wValue); + le16_to_cpus(&u.r.wIndex); + le16_to_cpus(&u.r.wLength); + + xprintk(" %02x.%02x v%04x\n", u.r.bRequestType, u.r.bRequest, u.r.wValue); + + switch(u.r.bRequest){ + /* HW(CUSB2) has handled it. */ + case USB_REQ_SET_ADDRESS: + if (u.r.bRequestType != (USB_TYPE_STANDARD | USB_RECIP_DEVICE)) + break; + return; + } + + if(!u.r.wLength){ + ep0_chg_stat(__func__, rt_usb, EP0_NO_DATA_PHASE); + }else if (u.r.bRequestType & USB_DIR_IN){ + ep0_chg_stat(__func__, rt_usb, EP0_IN_DATA_PHASE); + }else{ + // reload and clear out0bc + usb_write(OUT0BC, 0); + ep0_chg_stat(__func__, rt_usb, EP0_OUT_DATA_PHASE); + } + + if(!rt_usb->driver){ + printk("<%s> please insert gadget driver/module.\n", __func__); + goto stall; + } + + if(!rt_usb->driver->setup) + goto stall; + + i = rt_usb->driver->setup(&rt_usb->gadget, &u.r); // gadget would queue more usb req here + + if (i < 0) { + printk("<%s> device setup error %d\n", __func__, i); + goto stall; + } + + if(rt_usb->ep0state == EP0_NO_DATA_PHASE){ + epcs = read_epcs(rt_ep); + epcs |= EP_CS_EP0_HSNAK; // clear hsnak to let HW ack the status stage. + write_epcs(rt_ep, epcs); + } + + return; +stall: + printk("<%s> protocol STALL\n", __func__); + rt_ep_stall(rt_ep, 1); + ep0_chg_stat(__func__, rt_usb, EP0_STALL); + return; +} + +/* + * handle TX done interrupt + */ +static void handle_epinirq(struct rt_udc_struct *rt_usb, u8 epinirq) +{ + u8 irq = 0x0; + struct rt_request *req; + struct rt_ep_struct *rt_ep; + + rt_ep = &rt_usb->rt_ep[epinirq]; + + if (list_empty(&rt_ep->queue)) + FATAL_ERROR("empty queue"); + + // clear ep interrupt + if(epinirq < 8){ + irq |= 1 << epinirq; + usb_write(IN07IRQ, irq); + }else{ + irq |= 1 << (epinirq-8); + usb_write(IN815IRQ, irq); + } + + req = list_entry(rt_ep->queue.next, struct rt_request, queue); + xprintk("r.l=%d, r.a=%d\n", req->req.length, req->req.actual); + if(req->req.actual >= req->req.length ){ + if( req->req.actual && (!(req->req.actual % rt_ep->ep.maxpacket)) && req->req.zero){ + // deal with one more "zlp" + req->req.zero = 0; + write_ep_fifo_zlp(rt_ep); + return; + } + + // the first tx req in ep->queue is done. + rt_ep->tx_done_count = 0; + if(!epinirq /* EP0 */) + ep0_chg_stat(__func__, rt_usb, EP0_IDLE); + done(rt_ep, req, 0); +#if 1 + // more reqs there. + if (!list_empty(&rt_ep->queue) && !rt_ep->tx_done_count){ + if(!epinirq /* EP0 */){ + handle_inep0(rt_ep); + }else{ + handle_inep(rt_ep); + } + } +#endif + }else{ + if(!epinirq /* EP0 */){ + handle_inep0(rt_ep); + }else + handle_inep(rt_ep); + } +} + +static void handle_ep0outirq(struct rt_udc_struct *rt_usb, u8 epoutirq) +{ + u8 epcs, irq = 0x0; + struct rt_request *req = NULL; + struct rt_ep_struct *rt_ep = NULL; + +DBG; + rt_ep = &rt_usb->rt_ep[0]; + + if(rt_usb->ep0state == EP0_STALL){ + printk("<%s> protocol STALL\n", __func__); + rt_ep_stall(rt_ep, 1); + return; + } + + if(rt_usb->ep0state != EP0_OUT_DATA_PHASE) + FATAL_ERROR("Odd stage"); + + do{ + if(unlikely(!read_outbc(0x0))) + FATAL_ERROR("EP0 BC"); + + if (unlikely(list_empty(&rt_ep->queue))) + FATAL_ERROR("EP0 no req"); + + req = handle_outep0(rt_ep); + + //req = list_entry(rt_ep->queue.next, struct rt_request, queue); + xprintk("q.l=%d,q.a=%d\n", req->req.length, req->req.actual); + + // clear ep interrupt + irq |= 1; + usb_write(OUT07IRQ, irq); + + if(req && ((req->req.actual % rt_ep->ep.maxpacket) || (req->req.actual >= req->req.length))){ + ep0_chg_stat(__func__, rt_usb, EP0_IDLE); + done(rt_ep, req, 0); // short packet indicates transaction is done. + + epcs = read_epcs(rt_ep); + epcs |= EP_CS_EP0_HSNAK; // clear hsnak bit to let HW ack the status stage. + write_epcs(rt_ep, epcs); + break; + } + + // reload EP[0] + usb_write(OUT0BC /*out0bc*/, 0x0); + epcs = read_epcs(rt_ep); + }while(!(epcs & EP0_OUT_BSY)); +} + +static void handle_pending_epoutirq(struct rt_udc_struct *rt_usb, struct rt_ep_struct *rt_ep, struct rt_request *req) +{ + u8 epcs; + +DBG; + do{ + if(unlikely(!read_outbc(EP_NO(rt_ep)))) + FATAL_ERROR("No BC"); + + handle_outep(rt_ep); + if(req && ( (req->req.actual % rt_ep->ep.maxpacket) || (req->req.actual >= req->req.length))){ + xprintk("q.l=%d,q.a=%d\n", req->req.length, req->req.actual); + + //rx_done(rt_ep, req, 0); + done(rt_ep, req, 0); + } + + epcs = read_epcs(rt_ep); + write_epcs(rt_ep, 0x0); + epcs = read_epcs(rt_ep); + + }while(!(epcs & EP_CS_BSY)); +} + +static void handle_epoutirq(struct rt_udc_struct *rt_usb, u8 epoutirq) +{ + u8 irq = 0x0; + +DBG; + if(unlikely(epoutirq == 0x0)){ + handle_ep0outirq(rt_usb, 0x0); + return; + } + + tasklet_schedule(&rx_tasklet); + + // clear ep interrupt + irq |= 1 << epoutirq; + usb_write(OUT07IRQ, irq); + return; +} + +static void eps_change_to_hs(struct rt_udc_struct *rt_usb) +{ + int i; + struct rt_ep_struct *rt_ep; + for(i = 0; i < RT_USB_NB_EP; i++){ + rt_ep = &rt_usb->rt_ep[i]; + if(rt_ep->bmAttributes == USB_ENDPOINT_XFER_BULK){ + rt_ep->ep.maxpacket = 512; + } + } +} + +static void eps_change_to_fs(struct rt_udc_struct *rt_usb) +{ + int i; + struct rt_ep_struct *rt_ep; + for(i = 0; i < RT_USB_NB_EP; i++){ + rt_ep = &rt_usb->rt_ep[i]; + if(rt_ep->bmAttributes == USB_ENDPOINT_XFER_BULK){ + rt_ep->ep.maxpacket = 64; + } + } +} + +void handle_highspeed(struct rt_udc_struct *rt_usb) +{ + DBG; + + eps_change_to_hs(rt_usb); + + if(dma){ +#if defined (CONFIG_RALINK_RT3883) || defined (CONFIG_RALINK_RT3352) || defined (CONFIG_RALINK_MT7620) + usb_write(IN1CON, 0x8D); // InEP1 : Int, 2 subfifos + usb_write(IN2CON, 0x89); // InEP2 : Bulk, 2 subfifo + usb_write(OUT1CON, 0x8D); // OutEP1 : Int, 2 subfifos + usb_write(OUT2CON, 0x89); // OutEP2 : Bulk, 2 subfifos + //usb_write(OUT3CON, 0x89); // OutEP3 : Bulk, 2 subfifo + //usb_write(OUT4CON, 0x89); // OutEP4 : Bulk. 2 subfifo +#elif defined (CONFIG_RALINK_RT5350) + // Access by CPU + usb_write(IN1CON, 0x89); // InEP1 : Bulk, 2 subfifos + usb_write(OUT1CON, 0x89); // OutEP1 : Bulk, 2 subfifos +#else +#error "define a platform" +#endif + }else{ + // Access by CPU +#if defined (CONFIG_RALINK_RT3883) || defined (CONFIG_RALINK_RT3352) || defined (CONFIG_RALINK_MT7620) + usb_write(IN1CON, 0x8C); // InEP1 : Int , 1 subfifos + usb_write(IN2CON, 0x88); // InEP2 : Bulk, 1 subfifo + + usb_write(OUT1CON, 0x8C); // OutEP1 : Int, 1 subfifos + usb_write(OUT2CON, 0x88); // OutEP2 : Bulk, 1 subfifos + //usb_write(OUT3CON, 0x88); // OutEP3 : Bulk, 1 subfifo + //usb_write(OUT4CON, 0x88); // OutEP4 : Bulk. 1 subfifo +#elif defined (CONFIG_RALINK_RT5350) + // Access by CPU + usb_write(IN1CON, 0x88); // InEP1 : Bulk , 1 subfifos + usb_write(OUT1CON, 0x88); // OutEP1 : Bulk, 1 subfifos +#else +#error "define a platform" +#endif + + } + // clear all pending interrupts + usb_write(IN07IRQ, 0xFF); + usb_write(OUT07IRQ, 0xFF); + + rt_usb->gadget.speed = USB_SPEED_HIGH; + + // reset ALL endpoints + rt_all_eps_reset(); + + // Enable ep0 interrupt. + // (EPx interrupt is enabled in EPx_enable(). ) + rt_ep_irq_enable(&rt_usb->rt_ep[0]); +} + +static void handle_reset(struct rt_udc_struct *rt_usb) +{ + struct rt_ep_struct *rt_ep; + int i; + + eps_change_to_fs(rt_usb); + + // remove all EPs' usb request + for (i = 0; i < RT_USB_NB_EP; i++) { + rt_ep = &rt_usb->rt_ep[i]; + if(i != 0){ /* don't have to flush EP[0]. */ + rt_flush(rt_ep); + rt_ep->stopped = 1; + rt_ep_irq_disable(rt_ep); + } + nuke(rt_ep, -ESHUTDOWN); + } + + rt_usb->cfg = 0; + rt_usb->intf = 0; + rt_usb->alt = 0; + + if(dma){ + // clear all PDMA interrupts + udc_dma_all_int_clear(); + // reset PDMA + udc_dma_rst(); + } + + // clear all pending interrupts + usb_write(IN07IRQ, 0xFF); + usb_write(OUT07IRQ, 0xFF); + + // flush all EP's fifo + rt_all_eps_reset(); +} + +static void handle_usbirq(struct rt_udc_struct *rt_usb, u8 usbirq) +{ + if(usbirq & USB_INTR_SETUP_TOKEN_VALID){ + // Setup token is arrival. + // get setup data and pass it to gadget driver. + handle_setup(rt_usb); + } + + if(usbirq & USB_INTR_RESET) + handle_reset(rt_usb); + + if(usbirq & USB_INTR_HSPEED) + handle_highspeed(rt_usb); + + /* + * DO NOT try to clear SoF and token Interrupt! + */ + if( (usbirq & USB_INTR_SETUP_TOKEN_VALID) || + (usbirq & USB_INTR_HSPEED) || + (usbirq & USB_INTR_RESET)) + usb_write(USBIRQ, usbirq); +} + +static int irq_count = 100; /* for debug */ +/* + * Interrupt handler + */ +irqreturn_t rt_irq_handler(int irq, void *_dev) +{ + u32 usbirq,epin07irq,epin07ien,epout07irq,epout07ien; + struct rt_udc_struct *rt_usb = _dev; +#ifdef DEBUG + u32 count_tmp = irq_count; +#endif + + + DBG; + irq_count++; + + usbirq = usb_read(USBIRQ); + epin07irq = usb_read(IN07IRQ); + epin07ien = usb_read(IN07IEN); + epout07irq = usb_read(OUT07IRQ); + epout07ien = usb_read(OUT07IEN); + + //epin07irq = epin07irq & epin07ien; + //epout07irq = epout07irq & epout07ien; + + xprintk(">%x\n", count_tmp); + dump_usbirq(usbirq); + dump_epirq(epin07irq, epin07ien, 1); + dump_epirq(epout07irq, epout07ien, 0); + + if(dma){ + u32 dma_irq = reg_read(RTUSB_INT_STATUS); + if(epin07irq & 0x1) // INEP0 + handle_epinirq(rt_usb, 0); + + if(usbirq) // HS, Reset, SetupValid + handle_usbirq(rt_usb, usbirq); + + if(epout07irq & 0x1) // OUTEP0 + handle_epoutirq(rt_usb, 0); + + if(dma_irq) + handle_dmairq(rt_usb, dma_irq); + + }else{ + if(epin07irq & 0x1) // INEP0 + handle_epinirq(rt_usb, 0); + + if(usbirq) // HS, Reset, SetupValid + handle_usbirq(rt_usb, usbirq); + + if(epout07irq & 0x1) // OUTEP0 + handle_epoutirq(rt_usb, 0); + + if(epout07irq & 0x2) // OUTEP1 + handle_epoutirq(rt_usb, 1); + + if(epin07irq & 0x2) // INEP1 + handle_epinirq(rt_usb, 1); + + if(epout07irq & 0x4) // OUTEP2 + handle_epoutirq(rt_usb, 2); + + if(epin07irq & 0x4) // INEP2 + handle_epinirq(rt_usb, 2); + + //if(epout07irq & 0x8) // OUTEP3 + // handle_epoutirq(rt_usb, 3); + + //if(epout07irq & 0x10) // OUTEP4 + // handle_epoutirq(rt_usb, 4); + } + xprintk("<%x\n", count_tmp); + return IRQ_HANDLED; +} + +/* + ****************************************************************************** + * Static defined Ralink UDC structure + ******************************************************************************* + */ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,36) +static void nop_release(struct device *dev) +{ + return; +} +#endif + +static const struct usb_gadget_ops rt_udc_ops = { + .get_frame = rt_udc_get_frame, + .wakeup = rt_udc_wakeup, +}; + +static struct rt_udc_struct controller = { + .gadget = { + .ops = &rt_udc_ops, + .ep0 = &controller.rt_ep[0].ep, + .name = driver_name, + .dev = { +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,36) + .init_name = "gadget", + .release = nop_release, +#else + .bus_id = "gadget", +#endif + }, + }, + .rt_ep[0] = { + .ep = { + .name = ep0name, + .ops = &rt_ep_ops, + .maxpacket = 64, + }, + .rt_usb = &controller, + .bEndpointAddress = 0, + .bmAttributes = USB_ENDPOINT_XFER_CONTROL, + .pending = 0, + }, +#if defined (CONFIG_RALINK_RT3883) || defined (CONFIG_RALINK_RT3352) || defined (CONFIG_RALINK_MT7620) + .rt_ep[1] = { + .ep = { + .name = "ep1in-int", + .ops = &rt_ep_ops, + .maxpacket = 64, + }, + .rt_usb = &controller, + .bEndpointAddress = USB_DIR_IN | 1, + .bmAttributes = USB_ENDPOINT_XFER_INT, + .pending = 0, + }, + .rt_ep[2] = { + .ep = { + .name = "ep2in-bulk", + .ops = &rt_ep_ops, + .maxpacket = 64, + }, + .rt_usb = &controller, + .bEndpointAddress = USB_DIR_IN | 2, + .bmAttributes = USB_ENDPOINT_XFER_BULK, + .pending = 0, + }, + .rt_ep[3] = { + .ep = { + .name = "ep1out-int", + .ops = &rt_ep_ops, + .maxpacket = 64, + }, + .rt_usb = &controller, + .bEndpointAddress = USB_DIR_OUT | 1, + .bmAttributes = USB_ENDPOINT_XFER_INT, + .pending = 0, + }, + .rt_ep[4] = { + .ep = { + .name = "ep2out-bulk", + .ops = &rt_ep_ops, + .maxpacket = 64, + }, + .rt_usb = &controller, + .bEndpointAddress = USB_DIR_OUT | 2, + .bmAttributes = USB_ENDPOINT_XFER_BULK, + .pending = 0, + }, + /* + .rt_ep[5] = { + .ep = { + .name = "ep3out-bulk", + .ops = &rt_ep_ops, + .maxpacket = 64, + }, + .rt_usb = &controller, + .bEndpointAddress = USB_DIR_OUT | 3, + .bmAttributes = USB_ENDPOINT_XFER_BULK, + .pending = 0, + }, + .rt_ep[6] = { + .ep = { + .name = "ep4out-bulk", + .ops = &rt_ep_ops, + .maxpacket = 64, + }, + .rt_usb = &controller, + .bEndpointAddress = USB_DIR_OUT | 4, + .bmAttributes = USB_ENDPOINT_XFER_BULK, + .pending = 0, + }, + */ + +#elif defined (CONFIG_RALINK_RT5350) + .rt_ep[1] = { + .ep = { + .name = "ep1in-bulk", + .ops = &rt_ep_ops, + .maxpacket = 64, + }, + .rt_usb = &controller, + .bEndpointAddress = USB_DIR_IN | 1, + .bmAttributes = USB_ENDPOINT_XFER_BULK, + .pending = 0, + }, + .rt_ep[2] = { + .ep = { + .name = "ep1out-bulk", + .ops = &rt_ep_ops, + .maxpacket = 64, + }, + .rt_usb = &controller, + .bEndpointAddress = USB_DIR_OUT | 1, + .bmAttributes = USB_ENDPOINT_XFER_BULK, + .pending = 0, + }, +#else +#error "define a platform" +#endif +}; + +/* + ******************************************************************************* + * USB gadged driver functions + ******************************************************************************* + */ + +static void rt_udc_enable(struct rt_udc_struct *rt_usb) +{ + DBG; + rt_usb->gadget.speed = USB_SPEED_FULL; + if(dma){ + // enable dma interrupts + udc_dma_all_int_clear(); + udc_dma_int_enable(true); + + udc_dma_rst(); + + // enable dma + udc_dma_enable(true); + } +} + +static void rt_udc_disable(struct rt_udc_struct *rt_usb) +{ + DBG; + ep0_chg_stat(__func__, rt_usb, EP0_IDLE); + rt_usb->gadget.speed = USB_SPEED_UNKNOWN; + if(dma){ + // disable dma interrupts + udc_dma_all_int_clear(); + udc_dma_int_enable(false); + + udc_dma_rst(); + + // disable dma + udc_dma_enable(false); + } +} + +int usb_gadget_register_driver(struct usb_gadget_driver *driver) +{ + struct rt_udc_struct *rt_usb = &controller; + int retval; + + DBG; + if (!driver || driver->speed < USB_SPEED_FULL || !driver->bind || !driver->disconnect || !driver->setup) + return -EINVAL; + if (!rt_usb) + return -ENODEV; + if (rt_usb->driver) + return -EBUSY; + + /* first hook up the driver ... */ + rt_usb->driver = driver; + rt_usb->gadget.dev.driver = &driver->driver; + retval = device_add(&rt_usb->gadget.dev); + if (retval) + goto fail; + + retval = driver->bind(&rt_usb->gadget); + if (retval) { + D_ERR(rt_usb->dev, "<%s> bind to driver --> error %d\n", __func__, retval); + device_del(&rt_usb->gadget.dev); + goto fail; + } + + D_INI(rt_usb->dev, "<%s> registered gadget driver '%s'\n", __func__, driver->driver.name); + rt_udc_enable(rt_usb); + return 0; + +fail: + rt_usb->driver = NULL; + rt_usb->gadget.dev.driver = NULL; + return retval; +} +EXPORT_SYMBOL(usb_gadget_register_driver); + +int usb_gadget_unregister_driver(struct usb_gadget_driver *driver) +{ + struct rt_udc_struct *rt_usb = &controller; + +DBG; + if (!rt_usb) + return -ENODEV; + if (!driver || driver != rt_usb->driver || !driver->unbind) + return -EINVAL; + + udc_stop_activity(rt_usb, driver); + rt_udc_disable(rt_usb); + del_timer(&rt_usb->timer); + + driver->unbind(&rt_usb->gadget); + rt_usb->gadget.dev.driver = NULL; + rt_usb->driver = NULL; + + device_del(&rt_usb->gadget.dev); + + D_INI(rt_usb->dev, "<%s> unregistered gadget driver '%s'\n", __func__, driver->driver.name); + + return 0; +} +EXPORT_SYMBOL(usb_gadget_unregister_driver); + +/******************************************************************************* + * Module functions + ******************************************************************************* + */ +static int __init rt_udc_probe(struct platform_device *pdev) +{ + struct rt_udc_struct *rt_usb = &controller; + struct resource *res_mem, *res_irq; + void __iomem *base; + int ret = 0, res_mem_size; + +DBG; + res_mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res_mem) { + dev_err(&pdev->dev, "can't get device resources\n"); + return -ENODEV; + } + + res_mem_size = res_mem->end - res_mem->start + 1; + if (!request_mem_region(res_mem->start, res_mem_size, res_mem->name)) { + dev_err(&pdev->dev, "can't allocate %d bytes at %d address\n", res_mem_size, res_mem->start); + return -ENOMEM; + } + + base = ioremap(res_mem->start, res_mem_size); + if (!base) { + dev_err(&pdev->dev, "ioremap failed\n"); + ret = -EIO; + goto fail1; + } + + res_irq = platform_get_resource(pdev, IORESOURCE_IRQ, 0); + if (!res_irq) { + dev_err(&pdev->dev, "can't get irq number\n"); + ret = -ENODEV; + goto fail3; + } + rt_usb->interrupt = res_irq->start; + + ret = request_irq(rt_usb->interrupt, rt_irq_handler, IRQF_DISABLED, driver_name, rt_usb); + if (ret) { + dev_err(&pdev->dev, "can't get irq %i, err %d\n", rt_usb->interrupt, ret); + goto fail3; + } + + rt_usb->res = res_mem; + rt_usb->base = base; + rt_usb->dev = &pdev->dev; + + device_initialize(&rt_usb->gadget.dev); + + rt_usb->gadget.dev.parent = &pdev->dev; + rt_usb->gadget.dev.dma_mask = pdev->dev.dma_mask; + + platform_set_drvdata(pdev, rt_usb); + + usb_init_data(rt_usb); + + if(dma){ + if(rt_udc_dma_init()) + goto fail4; + } + + rt_udc_init(rt_usb); + + init_timer(&rt_usb->timer); + rt_usb->timer.function = handle_config; + rt_usb->timer.data = (unsigned long)rt_usb; + + return 0; +fail4: + free_irq(rt_usb->interrupt, rt_usb); +fail3: + iounmap(base); +fail1: + release_mem_region(res_mem->start, res_mem_size); + return ret; +} + +static int __exit rt_udc_remove(struct platform_device *pdev) +{ + struct rt_udc_struct *rt_usb = platform_get_drvdata(pdev); + + DBG; + rt_udc_disable(rt_usb); + del_timer(&rt_usb->timer); + + free_irq(rt_usb->interrupt, rt_usb); + + iounmap(rt_usb->base); + release_mem_region(rt_usb->res->start, rt_usb->res->end - rt_usb->res->start + 1); + + //if (pdata->exit) + // pdata->exit(&pdev->dev); + platform_set_drvdata(pdev, NULL); + + return 0; +} + +static void set_device_mode(void) +{ + u32 val; + val = le32_to_cpu(*(volatile u_long *)(SYSCFG1)); + val = val & ~(USB0_HOST_MODE); + *(volatile u_long *)(SYSCFG1) = cpu_to_le32(val); + udelay(10000); +} + +/*----------------------------------------------------------------------------*/ +static struct platform_driver udc_driver = { + + .driver = { + .name = driver_name, + .owner = THIS_MODULE, + }, + .probe = rt_udc_probe, + .remove = __exit_p(rt_udc_remove), + .suspend = NULL, + .resume = NULL, +}; + +static int udc_create_proc(void) +{ + pProcDir = proc_mkdir(PROC_DIR, NULL); + if ((pProcDebugLevel = create_proc_entry(DEBUGLEVEL_PROCFILE, 0, pProcDir))){ + pProcDebugLevel->read_proc = (read_proc_t*)&debuglevel_read; + pProcDebugLevel->write_proc = (write_proc_t*)&debuglevel_write; + } + return 0; +} + +static int udc_remove_proc(void) +{ + if (pProcDebugLevel) + remove_proc_entry(DEBUGLEVEL_PROCFILE, pProcDir); + if (pProcDir) + remove_proc_entry(PROC_DIR, 0); + + return 0; +} + +static int __init udc_init(void) +{ + int ret; + udc_create_proc(); + + try_wake_up(); + set_device_mode(); + + ret = platform_driver_register(&udc_driver); + + tasklet_init(&rx_tasklet, rx_do_tasklet, 0); + tasklet_init(&tx_tasklet, tx_do_tasklet, 0); + + if(dma){ + printk("DMA TXMAXCAP=%d\n", TXMAXCAP); + tasklet_init(&rx_dma_tasklet, rx_dma_done_do_tasklet, 0); + tasklet_init(&tx_dma_tasklet, tx_dma_done_do_tasklet, 0); + } + + return ret; //platform_driver_probe(&udc_driver, rt_udc_probe); +} +module_init(udc_init); + +static void __exit udc_exit(void) +{ + DBG; + udc_remove_proc(); + if(dma) + udc_dma_fini(); + platform_driver_unregister(&udc_driver); +} +module_exit(udc_exit); + +MODULE_DESCRIPTION("Ralink USB Device Controller driver"); +MODULE_AUTHOR("Ying Yuan Huang "); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:rt_udc"); +