3026 lines
78 KiB
Diff
3026 lines
78 KiB
Diff
|
From 0e3b1bffd1974e6852912865a7cea481617b1c39 Mon Sep 17 00:00:00 2001
|
||
|
From: John Crispin <blogic@openwrt.org>
|
||
|
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 <blogic@openwrt.org>
|
||
|
---
|
||
|
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
|
||
|
|
||
|
diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig
|
||
|
index c7525b1..6f0e293 100644
|
||
|
--- 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
|
||
|
#
|
||
|
diff --git a/drivers/usb/gadget/Makefile b/drivers/usb/gadget/Makefile
|
||
|
index 82fb225..f78a3b2 100644
|
||
|
--- 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
|
||
|
diff --git a/drivers/usb/gadget/rt_udc.h b/drivers/usb/gadget/rt_udc.h
|
||
|
new file mode 100644
|
||
|
index 0000000..088e0d9
|
||
|
--- /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 <linux/types.h>
|
||
|
+
|
||
|
+//#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 */
|
||
|
diff --git a/drivers/usb/gadget/rt_udc_pdma.c b/drivers/usb/gadget/rt_udc_pdma.c
|
||
|
new file mode 100644
|
||
|
index 0000000..d5b89a2
|
||
|
--- /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. <yyhuang@ralink_tech.com>
|
||
|
+ *
|
||
|
+ * 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 <linux/init.h>
|
||
|
+#include <linux/kernel.h>
|
||
|
+#include <linux/platform_device.h>
|
||
|
+#include <linux/module.h>
|
||
|
+#include <linux/errno.h>
|
||
|
+#include <linux/list.h>
|
||
|
+#include <linux/interrupt.h>
|
||
|
+#include <linux/io.h>
|
||
|
+#include <linux/irq.h>
|
||
|
+#include <linux/device.h>
|
||
|
+#include <linux/dma-mapping.h>
|
||
|
+#include <linux/clk.h>
|
||
|
+#include <linux/delay.h>
|
||
|
+#include <linux/timer.h>
|
||
|
+#include <linux/proc_fs.h>
|
||
|
+#include <linux/usb/ch9.h>
|
||
|
+#include <linux/version.h>
|
||
|
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,36)
|
||
|
+#include <linux/usb_gadget.h>
|
||
|
+#else
|
||
|
+#include <linux/usb/gadget.h>
|
||
|
+#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<NUM_RX_DESC; i++){
|
||
|
+ USBRxPackets[i] = kmalloc(sizeof(u8) * RX_BUFF_SZ, GFP_ATOMIC | GFP_DMA); // todo: use GFP_KERNEL instead.
|
||
|
+ if(!USBRxPackets[i]){
|
||
|
+ for(i=i-1; 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; i<NUM_RX_DESC; i++){
|
||
|
+ addr = rx_ring0_noncache[i].rxd_info1.PDP0;
|
||
|
+ if(addr)
|
||
|
+ dma_unmap_single(NULL, addr, sizeof(u8) * RX_BUFF_SZ, DMA_FROM_DEVICE);
|
||
|
+ kfree((void *)USBRxPackets[i]);
|
||
|
+ }
|
||
|
+
|
||
|
+ // unmap Tx buffer only(but not free it)
|
||
|
+ for(i=0; i<NUM_TX_DESC; i++){
|
||
|
+ addr = tx_ring0_noncache[i].txd_info1.SDP0;
|
||
|
+ if(addr){
|
||
|
+ len = tx_ring0_noncache[i].txd_info2.SDL0;
|
||
|
+ dma_unmap_single(NULL, addr, sizeof(u8) * len, DMA_TO_DEVICE);
|
||
|
+ }
|
||
|
+ }
|
||
|
+
|
||
|
+ dma_free_coherent(NULL, sizeof(struct PDMA_txdesc) * NUM_TX_DESC, tx_ring0_cache, tx_ring_bus_addr);
|
||
|
+ dma_free_coherent(NULL, sizeof(struct PDMA_rxdesc) * NUM_RX_DESC, rx_ring0_cache, rx_ring_bus_addr);
|
||
|
+
|
||
|
+ return 0;
|
||
|
+}
|
||
|
+
|
||
|
+static int rt_ep_enable(struct usb_ep *usb_ep, const struct usb_endpoint_descriptor *desc)
|
||
|
+{
|
||
|
+ struct rt_ep_struct *rt_ep = container_of(usb_ep, struct rt_ep_struct, ep);
|
||
|
+ struct rt_udc_struct *rt_usb = rt_ep->rt_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("<eq> 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("<SETUP> %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 <yy_huang@ralinktech.com>");
|
||
|
+MODULE_LICENSE("GPL");
|
||
|
+MODULE_ALIAS("platform:rt_udc");
|
||
|
+
|
||
|
--
|
||
|
1.7.10.4
|
||
|
|